summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-usb7
-rw-r--r--Documentation/ABI/testing/sysfs-class-extcon22
-rw-r--r--Documentation/ABI/testing/sysfs-class-regulator21
-rw-r--r--Documentation/ABI/testing/sysfs-driver-wacom13
-rw-r--r--Documentation/RCU/checklist.txt6
-rw-r--r--Documentation/RCU/stallwarn.txt16
-rw-r--r--Documentation/RCU/trace.txt43
-rw-r--r--Documentation/RCU/whatisRCU.txt9
-rw-r--r--Documentation/accounting/getdelays.c5
-rw-r--r--Documentation/arm/Marvell/README232
-rw-r--r--Documentation/arm64/booting.txt152
-rw-r--r--Documentation/arm64/memory.txt73
-rw-r--r--Documentation/devicetree/bindings/arm/vt8500.txt14
-rw-r--r--Documentation/devicetree/bindings/arm/vt8500/via,vt8500-intc.txt16
-rw-r--r--Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.txt13
-rw-r--r--Documentation/devicetree/bindings/arm/vt8500/via,vt8500-timer.txt15
-rw-r--r--Documentation/devicetree/bindings/bus/omap-ocp2scp.txt10
-rw-r--r--Documentation/devicetree/bindings/clock/vt8500.txt72
-rw-r--r--Documentation/devicetree/bindings/dma/mmp-dma.txt74
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-vt8500.txt24
-rw-r--r--Documentation/devicetree/bindings/media/exynos5-gsc.txt30
-rw-r--r--Documentation/devicetree/bindings/mfd/ab8500.txt15
-rw-r--r--Documentation/devicetree/bindings/misc/at25.txt34
-rw-r--r--Documentation/devicetree/bindings/misc/lis302.txt112
-rw-r--r--Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt196
-rw-r--r--Documentation/devicetree/bindings/regulator/regulator.txt5
-rw-r--r--Documentation/devicetree/bindings/regulator/tps65217.txt31
-rw-r--r--Documentation/devicetree/bindings/regulator/tps6586x.txt66
-rw-r--r--Documentation/devicetree/bindings/rtc/via,vt8500-rtc.txt15
-rw-r--r--Documentation/devicetree/bindings/sound/ux500-mop500.txt39
-rw-r--r--Documentation/devicetree/bindings/sound/ux500-msp.txt43
-rw-r--r--Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt15
-rw-r--r--Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt41
-rw-r--r--Documentation/devicetree/bindings/tty/serial/via,vt8500-uart.txt17
-rw-r--r--Documentation/devicetree/bindings/usb/am33xx-usb.txt14
-rw-r--r--Documentation/devicetree/bindings/usb/ci13xxx-imx.txt5
-rw-r--r--Documentation/devicetree/bindings/usb/omap-usb.txt33
-rw-r--r--Documentation/devicetree/bindings/usb/platform-uhci.txt15
-rw-r--r--Documentation/devicetree/bindings/usb/pxa-usb.txt31
-rw-r--r--Documentation/devicetree/bindings/usb/twlxxxx-usb.txt40
-rw-r--r--Documentation/devicetree/bindings/usb/usb-phy.txt17
-rw-r--r--Documentation/devicetree/bindings/usb/usbmisc-imx.txt14
-rw-r--r--Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt15
-rw-r--r--Documentation/devicetree/bindings/usb/vt8500-ehci.txt12
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.txt2
-rw-r--r--Documentation/devicetree/bindings/video/via,vt8500-fb.txt62
-rw-r--r--Documentation/devicetree/bindings/video/wm,prizm-ge-rops.txt13
-rw-r--r--Documentation/devicetree/bindings/video/wm,wm8505-fb.txt23
-rw-r--r--Documentation/devicetree/bindings/w1/w1-gpio.txt22
-rw-r--r--Documentation/dontdiff1
-rw-r--r--Documentation/feature-removal-schedule.txt639
-rw-r--r--Documentation/filesystems/debugfs.txt4
-rwxr-xr-xDocumentation/hid/hid-sensor.txt140
-rw-r--r--Documentation/hwmon/adt741051
-rw-r--r--Documentation/hwmon/ina2xx18
-rw-r--r--Documentation/hwmon/lm7012
-rw-r--r--Documentation/hwmon/max19760
-rw-r--r--Documentation/hwmon/mcp302123
-rw-r--r--Documentation/ia64/aliasing-test.c1
-rw-r--r--Documentation/kernel-parameters.txt26
-rw-r--r--Documentation/kobject.txt6
-rw-r--r--Documentation/misc-devices/lis3lv02d3
-rw-r--r--Documentation/power/swsusp.txt2
-rw-r--r--Documentation/scheduler/sched-arch.txt10
-rw-r--r--Documentation/trace/kprobetrace.txt2
-rw-r--r--Documentation/usb/persist.txt3
-rw-r--r--Documentation/vfio.txt2
-rw-r--r--Documentation/x86/x86_64/boot-options.txt7
-rw-r--r--Documentation/zh_CN/arm/Booting175
-rw-r--r--Documentation/zh_CN/basic_profiling.txt71
-rw-r--r--Documentation/zh_CN/filesystems/sysfs.txt372
-rw-r--r--Documentation/zh_CN/gpio.txt658
-rw-r--r--Documentation/zh_CN/video4linux/omap3isp.txt277
-rw-r--r--Documentation/zh_CN/video4linux/v4l2-framework.txt983
-rw-r--r--MAINTAINERS40
-rw-r--r--Makefile10
-rw-r--r--arch/Kconfig32
-rw-r--r--arch/alpha/kernel/pci.c6
-rw-r--r--arch/alpha/kernel/process.c6
-rw-r--r--arch/alpha/kernel/smp.c1
-rw-r--r--arch/arm/Kconfig66
-rw-r--r--arch/arm/Kconfig.debug14
-rw-r--r--arch/arm/boot/compressed/head.S1
-rw-r--r--arch/arm/boot/dts/Makefile1
-rw-r--r--arch/arm/boot/dts/at91sam9260.dtsi3
-rw-r--r--arch/arm/boot/dts/at91sam9263.dtsi5
-rw-r--r--arch/arm/boot/dts/at91sam9g45.dtsi5
-rw-r--r--arch/arm/boot/dts/at91sam9n12.dtsi4
-rw-r--r--arch/arm/boot/dts/at91sam9x5.dtsi4
-rw-r--r--arch/arm/boot/dts/dbx5x0.dtsi (renamed from arch/arm/boot/dts/db8500.dtsi)71
-rw-r--r--arch/arm/boot/dts/exynos4.dtsi248
-rw-r--r--arch/arm/boot/dts/exynos4210-origen.dts77
-rw-r--r--arch/arm/boot/dts/exynos4210-pinctrl.dtsi457
-rw-r--r--arch/arm/boot/dts/exynos4210-smdkv310.dts54
-rw-r--r--arch/arm/boot/dts/exynos4210-trats.dts237
-rw-r--r--arch/arm/boot/dts/exynos4210.dtsi201
-rw-r--r--arch/arm/boot/dts/exynos5250-smdk5250.dts59
-rw-r--r--arch/arm/boot/dts/exynos5250.dtsi60
-rw-r--r--arch/arm/boot/dts/hrefv60plus.dts95
-rw-r--r--arch/arm/boot/dts/imx23.dtsi5
-rw-r--r--arch/arm/boot/dts/imx28.dtsi5
-rw-r--r--arch/arm/boot/dts/omap4.dtsi8
-rw-r--r--arch/arm/boot/dts/omap5.dtsi12
-rw-r--r--arch/arm/boot/dts/phy3250.dts12
-rw-r--r--arch/arm/boot/dts/snowball.dts19
-rw-r--r--arch/arm/boot/dts/vt8500-bv07.dts36
-rw-r--r--arch/arm/boot/dts/vt8500.dtsi116
-rw-r--r--arch/arm/boot/dts/wm8505-ref.dts36
-rw-r--r--arch/arm/boot/dts/wm8505.dtsi143
-rw-r--r--arch/arm/boot/dts/wm8650-mid.dts36
-rw-r--r--arch/arm/boot/dts/wm8650.dtsi147
-rw-r--r--arch/arm/configs/afeb9260_defconfig1
-rw-r--r--arch/arm/configs/armadillo800eva_defconfig3
-rw-r--r--arch/arm/configs/at91rm9200_defconfig2
-rw-r--r--arch/arm/configs/at91sam9261_defconfig2
-rw-r--r--arch/arm/configs/at91sam9263_defconfig3
-rw-r--r--arch/arm/configs/at91sam9g20_defconfig2
-rw-r--r--arch/arm/configs/at91sam9rl_defconfig2
-rw-r--r--arch/arm/configs/cpu9260_defconfig2
-rw-r--r--arch/arm/configs/cpu9g20_defconfig2
-rw-r--r--arch/arm/configs/marzen_defconfig2
-rw-r--r--arch/arm/configs/mmp2_defconfig5
-rw-r--r--arch/arm/configs/pxa910_defconfig4
-rw-r--r--arch/arm/configs/qil-a9260_defconfig3
-rw-r--r--arch/arm/configs/stamp9g20_defconfig1
-rw-r--r--arch/arm/configs/usb-a9260_defconfig1
-rw-r--r--arch/arm/include/asm/leds.h50
-rw-r--r--arch/arm/include/asm/unistd.h2
-rw-r--r--arch/arm/kernel/Makefile1
-rw-r--r--arch/arm/kernel/bios32.c9
-rw-r--r--arch/arm/kernel/calls.S1
-rw-r--r--arch/arm/kernel/leds.c121
-rw-r--r--arch/arm/kernel/process.c6
-rw-r--r--arch/arm/kernel/smp_twd.c48
-rw-r--r--arch/arm/kernel/time.c17
-rw-r--r--arch/arm/mach-at91/at91rm9200_devices.c92
-rw-r--r--arch/arm/mach-at91/at91sam9260_devices.c84
-rw-r--r--arch/arm/mach-at91/at91sam9261_devices.c60
-rw-r--r--arch/arm/mach-at91/at91sam9263.c4
-rw-r--r--arch/arm/mach-at91/at91sam9263_devices.c161
-rw-r--r--arch/arm/mach-at91/at91sam9rl_devices.c60
-rw-r--r--arch/arm/mach-at91/at91x40.c2
-rw-r--r--arch/arm/mach-at91/at91x40_time.c4
-rw-r--r--arch/arm/mach-at91/board-afeb-9260v1.c14
-rw-r--r--arch/arm/mach-at91/board-carmeva.c14
-rw-r--r--arch/arm/mach-at91/board-cpu9krea.c17
-rw-r--r--arch/arm/mach-at91/board-cpuat91.c13
-rw-r--r--arch/arm/mach-at91/board-csb337.c16
-rw-r--r--arch/arm/mach-at91/board-eb9200.c14
-rw-r--r--arch/arm/mach-at91/board-ecbat91.c32
-rw-r--r--arch/arm/mach-at91/board-eco920.c36
-rw-r--r--arch/arm/mach-at91/board-flexibity.c14
-rw-r--r--arch/arm/mach-at91/board-foxg20.c16
-rw-r--r--arch/arm/mach-at91/board-kafa.c17
-rw-r--r--arch/arm/mach-at91/board-kb9202.c37
-rw-r--r--arch/arm/mach-at91/board-neocore926.c13
-rw-r--r--arch/arm/mach-at91/board-picotux200.c14
-rw-r--r--arch/arm/mach-at91/board-qil-a9260.c14
-rw-r--r--arch/arm/mach-at91/board-rm9200dk.c17
-rw-r--r--arch/arm/mach-at91/board-rm9200ek.c17
-rw-r--r--arch/arm/mach-at91/board-rsi-ews.c16
-rw-r--r--arch/arm/mach-at91/board-sam9-l9260.c39
-rw-r--r--arch/arm/mach-at91/board-sam9260ek.c16
-rw-r--r--arch/arm/mach-at91/board-sam9261ek.c16
-rw-r--r--arch/arm/mach-at91/board-sam9263ek.c13
-rw-r--r--arch/arm/mach-at91/board-sam9g20ek.c16
-rw-r--r--arch/arm/mach-at91/board-sam9rlek.c13
-rw-r--r--arch/arm/mach-at91/board-stamp9g20.c14
-rw-r--r--arch/arm/mach-at91/board-usb-a926x.c2
-rw-r--r--arch/arm/mach-at91/board-yl-9200.c16
-rw-r--r--arch/arm/mach-at91/include/mach/board.h1
-rw-r--r--arch/arm/mach-at91/include/mach/hardware.h4
-rw-r--r--arch/arm/mach-at91/include/mach/uncompress.h6
-rw-r--r--arch/arm/mach-at91/leds.c105
-rw-r--r--arch/arm/mach-at91/setup.c4
-rw-r--r--arch/arm/mach-clps711x/Kconfig6
-rw-r--r--arch/arm/mach-clps711x/Makefile3
-rw-r--r--arch/arm/mach-clps711x/ceiva.c64
-rw-r--r--arch/arm/mach-clps711x/common.c89
-rw-r--r--arch/arm/mach-clps711x/include/mach/clps711x.h9
-rw-r--r--arch/arm/mach-clps711x/include/mach/debug-macro.S8
-rw-r--r--arch/arm/mach-clps711x/include/mach/hardware.h48
-rw-r--r--arch/arm/mach-clps711x/include/mach/timex.h23
-rw-r--r--arch/arm/mach-clps711x/p720t-leds.c63
-rw-r--r--arch/arm/mach-clps711x/p720t.c62
-rw-r--r--arch/arm/mach-ebsa110/Makefile4
-rw-r--r--arch/arm/mach-ebsa110/core.c8
-rw-r--r--arch/arm/mach-ebsa110/core.h12
-rw-r--r--arch/arm/mach-ebsa110/leds.c81
-rw-r--r--arch/arm/mach-ep93xx/adssphere.c1
-rw-r--r--arch/arm/mach-ep93xx/gesbc9312.c1
-rw-r--r--arch/arm/mach-ep93xx/ts72xx.c12
-rw-r--r--arch/arm/mach-ep93xx/ts72xx.h (renamed from arch/arm/mach-ep93xx/include/mach/ts72xx.h)10
-rw-r--r--arch/arm/mach-exynos/Kconfig9
-rw-r--r--arch/arm/mach-exynos/clock-exynos4.c31
-rw-r--r--arch/arm/mach-exynos/clock-exynos5.c163
-rw-r--r--arch/arm/mach-exynos/common.c26
-rw-r--r--arch/arm/mach-exynos/include/mach/map.h11
-rw-r--r--arch/arm/mach-exynos/include/mach/sysmmu.h2
-rw-r--r--arch/arm/mach-exynos/mach-exynos4-dt.c32
-rw-r--r--arch/arm/mach-exynos/mach-exynos5-dt.c16
-rw-r--r--arch/arm/mach-exynos/mach-origen.c6
-rw-r--r--arch/arm/mach-exynos/mach-smdk4x12.c6
-rw-r--r--arch/arm/mach-exynos/mach-smdkv310.c7
-rw-r--r--arch/arm/mach-footbridge/Makefile4
-rw-r--r--arch/arm/mach-footbridge/ebsa285-leds.c138
-rw-r--r--arch/arm/mach-footbridge/ebsa285.c81
-rw-r--r--arch/arm/mach-footbridge/netwinder-hw.c112
-rw-r--r--arch/arm/mach-footbridge/netwinder-leds.c138
-rw-r--r--arch/arm/mach-imx/Kconfig1
-rw-r--r--arch/arm/mach-imx/clk-imx25.c2
-rw-r--r--arch/arm/mach-imx/mach-armadillo5x0.c3
-rw-r--r--arch/arm/mach-imx/mach-kzm_arm11_01.c4
-rw-r--r--arch/arm/mach-imx/mach-mx27_3ds.c21
-rw-r--r--arch/arm/mach-imx/mach-mx31ads.c2
-rw-r--r--arch/arm/mach-imx/mach-mx31lite.c2
-rw-r--r--arch/arm/mach-integrator/Makefile3
-rw-r--r--arch/arm/mach-integrator/core.c7
-rw-r--r--arch/arm/mach-integrator/cpu.c8
-rw-r--r--arch/arm/mach-integrator/include/mach/cm.h2
-rw-r--r--arch/arm/mach-integrator/integrator_ap.c12
-rw-r--r--arch/arm/mach-integrator/integrator_cp.c6
-rw-r--r--arch/arm/mach-integrator/leds.c161
-rw-r--r--arch/arm/mach-integrator/pci_v3.c12
-rw-r--r--arch/arm/mach-iop13xx/include/mach/iop13xx.h20
-rw-r--r--arch/arm/mach-iop13xx/include/mach/memory.h14
-rw-r--r--arch/arm/mach-iop13xx/io.c8
-rw-r--r--arch/arm/mach-iop13xx/pci.c16
-rw-r--r--arch/arm/mach-iop13xx/pci.h4
-rw-r--r--arch/arm/mach-iop13xx/setup.c10
-rw-r--r--arch/arm/mach-iop32x/glantank.c2
-rw-r--r--arch/arm/mach-ixp4xx/common.c8
-rw-r--r--arch/arm/mach-ixp4xx/include/mach/cpu.h5
-rw-r--r--arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h10
-rw-r--r--arch/arm/mach-ks8695/Kconfig61
-rw-r--r--arch/arm/mach-ks8695/Makefile11
-rw-r--r--arch/arm/mach-ks8695/board-og.c199
-rw-r--r--arch/arm/mach-ks8695/board-sg.c121
-rw-r--r--arch/arm/mach-ks8695/cpu.c2
-rw-r--r--arch/arm/mach-ks8695/devices.c21
-rw-r--r--arch/arm/mach-ks8695/include/mach/devices.h5
-rw-r--r--arch/arm/mach-ks8695/include/mach/hardware.h2
-rw-r--r--arch/arm/mach-ks8695/include/mach/uncompress.h6
-rw-r--r--arch/arm/mach-ks8695/leds.c92
-rw-r--r--arch/arm/mach-lpc32xx/common.c8
-rw-r--r--arch/arm/mach-lpc32xx/include/mach/gpio-lpc32xx.h2
-rw-r--r--arch/arm/mach-lpc32xx/include/mach/hardware.h2
-rw-r--r--arch/arm/mach-lpc32xx/phy3250.c22
-rw-r--r--arch/arm/mach-mmp/include/mach/debug-macro.S12
-rw-r--r--arch/arm/mach-mmp/irq.c4
-rw-r--r--arch/arm/mach-mmp/ttc_dkb.c10
-rw-r--r--arch/arm/mach-msm/Kconfig8
-rw-r--r--arch/arm/mach-msm/include/mach/msm_iomap-8x60.h11
-rw-r--r--arch/arm/mach-msm/include/mach/msm_iomap.h3
-rw-r--r--arch/arm/mach-msm/io.c2
-rw-r--r--arch/arm/mach-msm/smd.c19
-rw-r--r--arch/arm/mach-mxs/icoll.c63
-rw-r--r--arch/arm/mach-mxs/include/mach/common.h3
-rw-r--r--arch/arm/mach-mxs/include/mach/entry-macro.S35
-rw-r--r--arch/arm/mach-mxs/include/mach/irqs.h32
-rw-r--r--arch/arm/mach-mxs/mach-mxs.c41
-rw-r--r--arch/arm/mach-mxs/timer.c13
-rw-r--r--arch/arm/mach-nomadik/board-nhk8815.c3
-rw-r--r--arch/arm/mach-nomadik/include/mach/hardware.h2
-rw-r--r--arch/arm/mach-nomadik/include/mach/uncompress.h8
-rw-r--r--arch/arm/mach-omap1/Makefile8
-rw-r--r--arch/arm/mach-omap1/board-h2.c32
-rw-r--r--arch/arm/mach-omap1/board-h3.c32
-rw-r--r--arch/arm/mach-omap1/board-osk.c33
-rw-r--r--arch/arm/mach-omap1/leds-h2p2-debug.c169
-rw-r--r--arch/arm/mach-omap1/leds-innovator.c98
-rw-r--r--arch/arm/mach-omap1/leds-osk.c113
-rw-r--r--arch/arm/mach-omap1/leds.c70
-rw-r--r--arch/arm/mach-omap1/leds.h3
-rw-r--r--arch/arm/mach-omap1/time.c1
-rw-r--r--arch/arm/mach-omap1/timer32k.c1
-rw-r--r--arch/arm/mach-omap2/Kconfig7
-rw-r--r--arch/arm/mach-omap2/Makefile5
-rw-r--r--arch/arm/mach-omap2/board-4430sdp.c12
-rw-r--r--arch/arm/mach-omap2/board-omap3evm.c1
-rw-r--r--arch/arm/mach-omap2/board-omap4panda.c12
-rw-r--r--arch/arm/mach-omap2/gpio.c1
-rw-r--r--arch/arm/mach-omap2/omap-wakeupgen.c8
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_44xx_data.c6
-rw-r--r--arch/arm/mach-omap2/omap_phy_internal.c138
-rw-r--r--arch/arm/mach-omap2/timer.c97
-rw-r--r--arch/arm/mach-omap2/twl-common.c42
-rw-r--r--arch/arm/mach-omap2/twl-common.h3
-rw-r--r--arch/arm/mach-omap2/usb-musb.c3
-rw-r--r--arch/arm/mach-orion5x/common.c7
-rw-r--r--arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c1
-rw-r--r--arch/arm/mach-orion5x/rd88f5181l-ge-setup.c1
-rw-r--r--arch/arm/mach-orion5x/rd88f5182-setup.c72
-rw-r--r--arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c1
-rw-r--r--arch/arm/mach-orion5x/ts78xx-setup.c12
-rw-r--r--arch/arm/mach-prima2/include/mach/uncompress.h4
-rw-r--r--arch/arm/mach-pxa/Makefile8
-rw-r--r--arch/arm/mach-pxa/idp.c81
-rw-r--r--arch/arm/mach-pxa/leds-idp.c115
-rw-r--r--arch/arm/mach-pxa/leds-lubbock.c124
-rw-r--r--arch/arm/mach-pxa/leds-mainstone.c119
-rw-r--r--arch/arm/mach-pxa/leds.c32
-rw-r--r--arch/arm/mach-pxa/leds.h13
-rw-r--r--arch/arm/mach-pxa/lubbock.c95
-rw-r--r--arch/arm/mach-pxa/mainstone.c94
-rw-r--r--arch/arm/mach-pxa/stargate2.c2
-rw-r--r--arch/arm/mach-realview/core.c39
-rw-r--r--arch/arm/mach-realview/core.h2
-rw-r--r--arch/arm/mach-realview/realview_eb.c5
-rw-r--r--arch/arm/mach-realview/realview_pb1176.c5
-rw-r--r--arch/arm/mach-realview/realview_pb11mp.c5
-rw-r--r--arch/arm/mach-realview/realview_pba8.c5
-rw-r--r--arch/arm/mach-realview/realview_pbx.c5
-rw-r--r--arch/arm/mach-s3c24xx/clock-s3c2440.c14
-rw-r--r--arch/arm/mach-s3c24xx/mach-h1940.c4
-rw-r--r--arch/arm/mach-s3c64xx/mach-crag6410-module.c36
-rw-r--r--arch/arm/mach-s3c64xx/mach-crag6410.c12
-rw-r--r--arch/arm/mach-sa1100/Makefile9
-rw-r--r--arch/arm/mach-sa1100/assabet.c85
-rw-r--r--arch/arm/mach-sa1100/badge4.c30
-rw-r--r--arch/arm/mach-sa1100/cerf.c42
-rw-r--r--arch/arm/mach-sa1100/hackkit.c32
-rw-r--r--arch/arm/mach-sa1100/include/mach/simpad.h2
-rw-r--r--arch/arm/mach-sa1100/lart.c26
-rw-r--r--arch/arm/mach-sa1100/leds-assabet.c113
-rw-r--r--arch/arm/mach-sa1100/leds-badge4.c110
-rw-r--r--arch/arm/mach-sa1100/leds-cerf.c110
-rw-r--r--arch/arm/mach-sa1100/leds-hackkit.c112
-rw-r--r--arch/arm/mach-sa1100/leds-lart.c101
-rw-r--r--arch/arm/mach-sa1100/leds.c50
-rw-r--r--arch/arm/mach-sa1100/leds.h13
-rw-r--r--arch/arm/mach-sa1100/simpad.c2
-rw-r--r--arch/arm/mach-shark/Makefile4
-rw-r--r--arch/arm/mach-shark/core.c1
-rw-r--r--arch/arm/mach-shark/leds.c220
-rw-r--r--arch/arm/mach-shmobile/board-ap4evb.c12
-rw-r--r--arch/arm/mach-shmobile/board-armadillo800eva.c31
-rw-r--r--arch/arm/mach-shmobile/board-bonito.c8
-rw-r--r--arch/arm/mach-shmobile/board-g3evm.c12
-rw-r--r--arch/arm/mach-shmobile/board-g4evm.c30
-rw-r--r--arch/arm/mach-shmobile/board-kzm9g.c14
-rw-r--r--arch/arm/mach-shmobile/board-mackerel.c22
-rw-r--r--arch/arm/mach-shmobile/board-marzen.c68
-rw-r--r--arch/arm/mach-shmobile/clock-r8a7740.c46
-rw-r--r--arch/arm/mach-shmobile/clock-sh7367.c44
-rw-r--r--arch/arm/mach-shmobile/clock-sh7372.c60
-rw-r--r--arch/arm/mach-shmobile/clock-sh7377.c50
-rw-r--r--arch/arm/mach-shmobile/clock-sh73a0.c70
-rw-r--r--arch/arm/mach-shmobile/intc-r8a7779.c14
-rw-r--r--arch/arm/mach-shmobile/intc-sh7372.c27
-rw-r--r--arch/arm/mach-shmobile/intc-sh73a0.c20
-rw-r--r--arch/arm/mach-shmobile/pm-rmobile.c6
-rw-r--r--arch/arm/mach-shmobile/pm-sh7372.c57
-rw-r--r--arch/arm/mach-shmobile/setup-sh7367.c2
-rw-r--r--arch/arm/mach-shmobile/setup-sh7377.c2
-rw-r--r--arch/arm/mach-shmobile/setup-sh73a0.c2
-rw-r--r--arch/arm/mach-shmobile/sh-gpio.h6
-rw-r--r--arch/arm/mach-spear13xx/include/mach/spear.h14
-rw-r--r--arch/arm/mach-spear13xx/spear13xx.c6
-rw-r--r--arch/arm/mach-tegra/Makefile3
-rw-r--r--arch/arm/mach-tegra/board-dt-tegra20.c27
-rw-r--r--arch/arm/mach-tegra/board-pinmux.c87
-rw-r--r--arch/arm/mach-tegra/board-pinmux.h54
-rw-r--r--arch/arm/mach-tegra/devices.c701
-rw-r--r--arch/arm/mach-tegra/devices.h60
-rw-r--r--arch/arm/mach-tegra/include/mach/pinconf-tegra.h63
-rw-r--r--arch/arm/mach-tegra/include/mach/suspend.h38
-rw-r--r--arch/arm/mach-tegra/pcie.c12
-rw-r--r--arch/arm/mach-tegra/powergate.c43
-rw-r--r--arch/arm/mach-tegra/tegra20_clocks.c1
-rw-r--r--arch/arm/mach-tegra/tegra20_clocks_data.c21
-rw-r--r--arch/arm/mach-tegra/tegra30_clocks_data.c15
-rw-r--r--arch/arm/mach-tegra/timer.c1
-rw-r--r--arch/arm/mach-ux500/Kconfig1
-rw-r--r--arch/arm/mach-ux500/Makefile2
-rw-r--r--arch/arm/mach-ux500/board-mop500-audio.c (renamed from arch/arm/mach-ux500/board-mop500-msp.c)102
-rw-r--r--arch/arm/mach-ux500/board-mop500-msp.h14
-rw-r--r--arch/arm/mach-ux500/board-mop500-regulators.c15
-rw-r--r--arch/arm/mach-ux500/board-mop500-regulators.h1
-rw-r--r--arch/arm/mach-ux500/board-mop500-sdi.c4
-rw-r--r--arch/arm/mach-ux500/board-mop500.c58
-rw-r--r--arch/arm/mach-ux500/board-mop500.h11
-rw-r--r--arch/arm/mach-ux500/cpu-db8500.c11
-rw-r--r--arch/arm/mach-ux500/cpu.c2
-rw-r--r--arch/arm/mach-ux500/include/mach/msp.h2
-rw-r--r--arch/arm/mach-ux500/include/mach/uncompress.h4
-rw-r--r--arch/arm/mach-versatile/core.c5
-rw-r--r--arch/arm/mach-vt8500/Kconfig73
-rw-r--r--arch/arm/mach-vt8500/Makefile8
-rw-r--r--arch/arm/mach-vt8500/bv07.c80
-rw-r--r--arch/arm/mach-vt8500/common.h (renamed from arch/arm/mach-tegra/include/mach/gpio-tegra.h)22
-rw-r--r--arch/arm/mach-vt8500/devices-vt8500.c91
-rw-r--r--arch/arm/mach-vt8500/devices-wm8505.c99
-rw-r--r--arch/arm/mach-vt8500/devices.c270
-rw-r--r--arch/arm/mach-vt8500/devices.h88
-rw-r--r--arch/arm/mach-vt8500/gpio.c240
-rw-r--r--arch/arm/mach-vt8500/include/mach/restart.h4
-rw-r--r--arch/arm/mach-vt8500/include/mach/vt8500_irqs.h88
-rw-r--r--arch/arm/mach-vt8500/include/mach/vt8500_regs.h79
-rw-r--r--arch/arm/mach-vt8500/include/mach/wm8505_irqs.h115
-rw-r--r--arch/arm/mach-vt8500/include/mach/wm8505_regs.h78
-rw-r--r--arch/arm/mach-vt8500/irq.c209
-rw-r--r--arch/arm/mach-vt8500/restart.c54
-rw-r--r--arch/arm/mach-vt8500/timer.c67
-rw-r--r--arch/arm/mach-vt8500/vt8500.c195
-rw-r--r--arch/arm/mach-vt8500/wm8505_7in.c79
-rw-r--r--arch/arm/mm/cache-l2x0.c2
-rw-r--r--arch/arm/mm/dma-mapping.c2
-rw-r--r--arch/arm/plat-mxc/include/mach/mx25.h1
-rw-r--r--arch/arm/plat-mxc/include/mach/mx31.h6
-rw-r--r--arch/arm/plat-omap/Kconfig3
-rw-r--r--arch/arm/plat-omap/debug-leds.c293
-rw-r--r--arch/arm/plat-omap/include/plat/omap-serial.h2
-rw-r--r--arch/arm/plat-samsung/clock.c18
-rw-r--r--arch/arm/plat-samsung/devs.c5
-rw-r--r--arch/arm/plat-samsung/s5p-irq-gpioint.c4
-rw-r--r--arch/arm/plat-samsung/time.c1
-rw-r--r--arch/arm/plat-versatile/Kconfig4
-rw-r--r--arch/arm/plat-versatile/leds.c8
-rw-r--r--arch/arm/tools/mach-types1
-rw-r--r--arch/arm64/Kconfig222
-rw-r--r--arch/arm64/Kconfig.debug27
-rw-r--r--arch/arm64/Makefile71
-rw-r--r--arch/arm64/boot/.gitignore2
-rw-r--r--arch/arm64/boot/Makefile36
-rw-r--r--arch/arm64/boot/install.sh46
-rw-r--r--arch/arm64/configs/defconfig85
-rw-r--r--arch/arm64/include/asm/Kbuild51
-rw-r--r--arch/arm64/include/asm/arm_generic.h100
-rw-r--r--arch/arm64/include/asm/asm-offsets.h1
-rw-r--r--arch/arm64/include/asm/assembler.h109
-rw-r--r--arch/arm64/include/asm/atomic.h305
-rw-r--r--arch/arm64/include/asm/auxvec.h22
-rw-r--r--arch/arm64/include/asm/barrier.h52
-rw-r--r--arch/arm64/include/asm/bitops.h53
-rw-r--r--arch/arm64/include/asm/bitsperlong.h23
-rw-r--r--arch/arm64/include/asm/byteorder.h21
-rw-r--r--arch/arm64/include/asm/cache.h32
-rw-r--r--arch/arm64/include/asm/cacheflush.h148
-rw-r--r--arch/arm64/include/asm/cachetype.h48
-rw-r--r--arch/arm64/include/asm/cmpxchg.h173
-rw-r--r--arch/arm64/include/asm/compat.h242
-rw-r--r--arch/arm64/include/asm/compiler.h30
-rw-r--r--arch/arm64/include/asm/cputable.h30
-rw-r--r--arch/arm64/include/asm/cputype.h49
-rw-r--r--arch/arm64/include/asm/debug-monitors.h88
-rw-r--r--arch/arm64/include/asm/device.h26
-rw-r--r--arch/arm64/include/asm/dma-mapping.h124
-rw-r--r--arch/arm64/include/asm/elf.h179
-rw-r--r--arch/arm64/include/asm/exception.h23
-rw-r--r--arch/arm64/include/asm/exec.h23
-rw-r--r--arch/arm64/include/asm/fb.h34
-rw-r--r--arch/arm64/include/asm/fcntl.h29
-rw-r--r--arch/arm64/include/asm/fpsimd.h64
-rw-r--r--arch/arm64/include/asm/futex.h136
-rw-r--r--arch/arm64/include/asm/hardirq.h52
-rw-r--r--arch/arm64/include/asm/hw_breakpoint.h137
-rw-r--r--arch/arm64/include/asm/hwcap.h53
-rw-r--r--arch/arm64/include/asm/io.h258
-rw-r--r--arch/arm64/include/asm/irq.h8
-rw-r--r--arch/arm64/include/asm/irqflags.h91
-rw-r--r--arch/arm64/include/asm/memblock.h21
-rw-r--r--arch/arm64/include/asm/memory.h144
-rw-r--r--arch/arm64/include/asm/mmu.h30
-rw-r--r--arch/arm64/include/asm/mmu_context.h152
-rw-r--r--arch/arm64/include/asm/module.h23
-rw-r--r--arch/arm64/include/asm/page.h67
-rw-r--r--arch/arm64/include/asm/param.h23
-rw-r--r--arch/arm64/include/asm/perf_event.h22
-rw-r--r--arch/arm64/include/asm/pgalloc.h113
-rw-r--r--arch/arm64/include/asm/pgtable-2level-hwdef.h43
-rw-r--r--arch/arm64/include/asm/pgtable-2level-types.h60
-rw-r--r--arch/arm64/include/asm/pgtable-3level-hwdef.h50
-rw-r--r--arch/arm64/include/asm/pgtable-3level-types.h66
-rw-r--r--arch/arm64/include/asm/pgtable-hwdef.h94
-rw-r--r--arch/arm64/include/asm/pgtable.h328
-rw-r--r--arch/arm64/include/asm/pmu.h82
-rw-r--r--arch/arm64/include/asm/proc-fns.h50
-rw-r--r--arch/arm64/include/asm/processor.h175
-rw-r--r--arch/arm64/include/asm/prom.h1
-rw-r--r--arch/arm64/include/asm/ptrace.h207
-rw-r--r--arch/arm64/include/asm/setup.h26
-rw-r--r--arch/arm64/include/asm/shmparam.h28
-rw-r--r--arch/arm64/include/asm/sigcontext.h69
-rw-r--r--arch/arm64/include/asm/siginfo.h23
-rw-r--r--arch/arm64/include/asm/signal.h24
-rw-r--r--arch/arm64/include/asm/signal32.h53
-rw-r--r--arch/arm64/include/asm/smp.h69
-rw-r--r--arch/arm64/include/asm/sparsemem.h24
-rw-r--r--arch/arm64/include/asm/spinlock.h202
-rw-r--r--arch/arm64/include/asm/spinlock_types.h38
-rw-r--r--arch/arm64/include/asm/stacktrace.h29
-rw-r--r--arch/arm64/include/asm/stat.h62
-rw-r--r--arch/arm64/include/asm/statfs.h23
-rw-r--r--arch/arm64/include/asm/syscall.h101
-rw-r--r--arch/arm64/include/asm/syscalls.h40
-rw-r--r--arch/arm64/include/asm/system_misc.h54
-rw-r--r--arch/arm64/include/asm/thread_info.h127
-rw-r--r--arch/arm64/include/asm/timex.h29
-rw-r--r--arch/arm64/include/asm/tlb.h190
-rw-r--r--arch/arm64/include/asm/tlbflush.h122
-rw-r--r--arch/arm64/include/asm/traps.h30
-rw-r--r--arch/arm64/include/asm/uaccess.h297
-rw-r--r--arch/arm64/include/asm/ucontext.h30
-rw-r--r--arch/arm64/include/asm/unistd.h27
-rw-r--r--arch/arm64/include/asm/unistd32.h758
-rw-r--r--arch/arm64/include/asm/vdso.h41
-rw-r--r--arch/arm64/include/asm/vdso_datapage.h43
-rw-r--r--arch/arm64/kernel/.gitignore1
-rw-r--r--arch/arm64/kernel/Makefile27
-rw-r--r--arch/arm64/kernel/arm64ksyms.c46
-rw-r--r--arch/arm64/kernel/asm-offsets.c108
-rw-r--r--arch/arm64/kernel/cputable.c33
-rw-r--r--arch/arm64/kernel/debug-monitors.c288
-rw-r--r--arch/arm64/kernel/entry-fpsimd.S80
-rw-r--r--arch/arm64/kernel/entry.S695
-rw-r--r--arch/arm64/kernel/fpsimd.c106
-rw-r--r--arch/arm64/kernel/head.S510
-rw-r--r--arch/arm64/kernel/hw_breakpoint.c880
-rw-r--r--arch/arm64/kernel/io.c64
-rw-r--r--arch/arm64/kernel/irq.c84
-rw-r--r--arch/arm64/kernel/kuser32.S77
-rw-r--r--arch/arm64/kernel/module.c456
-rw-r--r--arch/arm64/kernel/perf_event.c1368
-rw-r--r--arch/arm64/kernel/process.c408
-rw-r--r--arch/arm64/kernel/ptrace.c1126
-rw-r--r--arch/arm64/kernel/setup.c347
-rw-r--r--arch/arm64/kernel/signal.c437
-rw-r--r--arch/arm64/kernel/signal32.c876
-rw-r--r--arch/arm64/kernel/smp.c469
-rw-r--r--arch/arm64/kernel/stacktrace.c127
-rw-r--r--arch/arm64/kernel/sys.c138
-rw-r--r--arch/arm64/kernel/sys32.S282
-rw-r--r--arch/arm64/kernel/sys_compat.c164
-rw-r--r--arch/arm64/kernel/time.c65
-rw-r--r--arch/arm64/kernel/traps.c348
-rw-r--r--arch/arm64/kernel/vdso.c261
-rw-r--r--arch/arm64/kernel/vdso/.gitignore2
-rw-r--r--arch/arm64/kernel/vdso/Makefile63
-rwxr-xr-xarch/arm64/kernel/vdso/gen_vdso_offsets.sh15
-rw-r--r--arch/arm64/kernel/vdso/gettimeofday.S242
-rw-r--r--arch/arm64/kernel/vdso/note.S28
-rw-r--r--arch/arm64/kernel/vdso/sigreturn.S37
-rw-r--r--arch/arm64/kernel/vdso/vdso.S33
-rw-r--r--arch/arm64/kernel/vdso/vdso.lds.S100
-rw-r--r--arch/arm64/kernel/vmlinux.lds.S126
-rw-r--r--arch/arm64/lib/Makefile4
-rw-r--r--arch/arm64/lib/bitops.c25
-rw-r--r--arch/arm64/lib/clear_page.S39
-rw-r--r--arch/arm64/lib/clear_user.S58
-rw-r--r--arch/arm64/lib/copy_from_user.S66
-rw-r--r--arch/arm64/lib/copy_in_user.S63
-rw-r--r--arch/arm64/lib/copy_page.S46
-rw-r--r--arch/arm64/lib/copy_to_user.S61
-rw-r--r--arch/arm64/lib/delay.c55
-rw-r--r--arch/arm64/lib/strncpy_from_user.S50
-rw-r--r--arch/arm64/lib/strnlen_user.S47
-rw-r--r--arch/arm64/mm/Makefile4
-rw-r--r--arch/arm64/mm/cache.S168
-rw-r--r--arch/arm64/mm/context.c159
-rw-r--r--arch/arm64/mm/copypage.c34
-rw-r--r--arch/arm64/mm/dma-mapping.c79
-rw-r--r--arch/arm64/mm/extable.c17
-rw-r--r--arch/arm64/mm/fault.c534
-rw-r--r--arch/arm64/mm/flush.c135
-rw-r--r--arch/arm64/mm/init.c437
-rw-r--r--arch/arm64/mm/ioremap.c84
-rw-r--r--arch/arm64/mm/mm.h2
-rw-r--r--arch/arm64/mm/mmap.c144
-rw-r--r--arch/arm64/mm/mmu.c395
-rw-r--r--arch/arm64/mm/pgd.c54
-rw-r--r--arch/arm64/mm/proc-macros.S55
-rw-r--r--arch/arm64/mm/proc.S175
-rw-r--r--arch/arm64/mm/tlb.S71
-rw-r--r--arch/c6x/include/asm/Kbuild1
-rw-r--r--arch/c6x/include/asm/barrier.h27
-rw-r--r--arch/cris/kernel/process.c3
-rw-r--r--arch/frv/kernel/process.c3
-rw-r--r--arch/frv/mb93090-mb00/pci-vdk.c4
-rw-r--r--arch/h8300/kernel/process.c3
-rw-r--r--arch/ia64/Kconfig12
-rw-r--r--arch/ia64/include/asm/numa.h2
-rw-r--r--arch/ia64/include/asm/switch_to.h8
-rw-r--r--arch/ia64/kernel/machine_kexec.c3
-rw-r--r--arch/ia64/kernel/process.c7
-rw-r--r--arch/ia64/kernel/time.c66
-rw-r--r--arch/ia64/pci/pci.c11
-rw-r--r--arch/ia64/sn/kernel/io_common.c4
-rw-r--r--arch/ia64/xen/xencomm.c3
-rw-r--r--arch/m32r/kernel/process.c3
-rw-r--r--arch/m68k/amiga/platform.c5
-rw-r--r--arch/m68k/include/asm/apollohw.h12
-rw-r--r--arch/m68k/kernel/pcibios.c5
-rw-r--r--arch/m68k/kernel/process.c3
-rw-r--r--arch/m68k/kernel/time.c5
-rw-r--r--arch/m68k/platform/coldfire/clk.c6
-rw-r--r--arch/m68k/q40/config.c5
-rw-r--r--arch/mips/kernel/smp-cmp.c2
-rw-r--r--arch/mips/mm/gup.c2
-rw-r--r--arch/mips/mti-malta/malta-int.c9
-rw-r--r--arch/mips/mti-malta/malta-platform.c5
-rw-r--r--arch/mips/pci/pci-octeon.c15
-rw-r--r--arch/mips/pci/pci.c6
-rw-r--r--arch/mn10300/kernel/process.c3
-rw-r--r--arch/parisc/kernel/process.c3
-rw-r--r--arch/powerpc/boot/.gitignore4
-rw-r--r--arch/powerpc/include/asm/machdep.h3
-rw-r--r--arch/powerpc/include/asm/time.h6
-rw-r--r--arch/powerpc/kernel/pci-common.c20
-rw-r--r--arch/powerpc/kernel/process.c3
-rw-r--r--arch/powerpc/kernel/time.c55
-rw-r--r--arch/powerpc/platforms/83xx/suspend.c2
-rw-r--r--arch/powerpc/platforms/Kconfig.cputype16
-rw-r--r--arch/powerpc/platforms/cell/celleb_pci.c2
-rw-r--r--arch/powerpc/platforms/powernv/pci-ioda.c41
-rw-r--r--arch/powerpc/sysdev/fsl_msi.c8
-rw-r--r--arch/s390/Kbuild1
-rw-r--r--arch/s390/Kconfig421
-rw-r--r--arch/s390/boot/compressed/Makefile1
-rw-r--r--arch/s390/boot/compressed/misc.c45
-rw-r--r--arch/s390/defconfig14
-rw-r--r--arch/s390/include/asm/appldata.h2
-rw-r--r--arch/s390/include/asm/chsc.h28
-rw-r--r--arch/s390/include/asm/cio.h28
-rw-r--r--arch/s390/include/asm/cmpxchg.h61
-rw-r--r--arch/s390/include/asm/cpu_mf.h4
-rw-r--r--arch/s390/include/asm/cputime.h3
-rw-r--r--arch/s390/include/asm/css_chars.h39
-rw-r--r--arch/s390/include/asm/eadm.h124
-rw-r--r--arch/s390/include/asm/elf.h3
-rw-r--r--arch/s390/include/asm/etr.h8
-rw-r--r--arch/s390/include/asm/hugetlb.h24
-rw-r--r--arch/s390/include/asm/irq.h2
-rw-r--r--arch/s390/include/asm/isc.h1
-rw-r--r--arch/s390/include/asm/lowcore.h6
-rw-r--r--arch/s390/include/asm/mmu_context.h2
-rw-r--r--arch/s390/include/asm/percpu.h50
-rw-r--r--arch/s390/include/asm/processor.h59
-rw-r--r--arch/s390/include/asm/ptrace.h12
-rw-r--r--arch/s390/include/asm/runtime_instr.h98
-rw-r--r--arch/s390/include/asm/scsw.h38
-rw-r--r--arch/s390/include/asm/setup.h5
-rw-r--r--arch/s390/include/asm/smp.h4
-rw-r--r--arch/s390/include/asm/string.h8
-rw-r--r--arch/s390/include/asm/switch_to.h6
-rw-r--r--arch/s390/include/asm/sysinfo.h39
-rw-r--r--arch/s390/include/asm/tlbflush.h2
-rw-r--r--arch/s390/include/asm/topology.h20
-rw-r--r--arch/s390/include/asm/uaccess.h15
-rw-r--r--arch/s390/include/asm/unistd.h4
-rw-r--r--arch/s390/kernel/Makefile12
-rw-r--r--arch/s390/kernel/asm-offsets.c2
-rw-r--r--arch/s390/kernel/cache.c385
-rw-r--r--arch/s390/kernel/compat_wrapper.S13
-rw-r--r--arch/s390/kernel/crash.c14
-rw-r--r--arch/s390/kernel/crash_dump.c3
-rw-r--r--arch/s390/kernel/dis.c58
-rw-r--r--arch/s390/kernel/early.c38
-rw-r--r--arch/s390/kernel/entry64.S17
-rw-r--r--arch/s390/kernel/irq.c56
-rw-r--r--arch/s390/kernel/kprobes.c2
-rw-r--r--arch/s390/kernel/lgr.c29
-rw-r--r--arch/s390/kernel/machine_kexec.c9
-rw-r--r--arch/s390/kernel/process.c8
-rw-r--r--arch/s390/kernel/processor.c7
-rw-r--r--arch/s390/kernel/ptrace.c70
-rw-r--r--arch/s390/kernel/runtime_instr.c150
-rw-r--r--arch/s390/kernel/s390_ksyms.c2
-rw-r--r--arch/s390/kernel/setup.c56
-rw-r--r--arch/s390/kernel/smp.c46
-rw-r--r--arch/s390/kernel/syscalls.S2
-rw-r--r--arch/s390/kernel/sysinfo.c351
-rw-r--r--arch/s390/kernel/time.c4
-rw-r--r--arch/s390/kernel/topology.c27
-rw-r--r--arch/s390/kernel/traps.c41
-rw-r--r--arch/s390/kernel/vdso.c8
-rw-r--r--arch/s390/kernel/vtime.c11
-rw-r--r--arch/s390/kvm/Kconfig2
-rw-r--r--arch/s390/kvm/priv.c4
-rw-r--r--arch/s390/lib/Makefile3
-rw-r--r--arch/s390/lib/mem32.S92
-rw-r--r--arch/s390/lib/mem64.S88
-rw-r--r--arch/s390/lib/string.c56
-rw-r--r--arch/s390/lib/uaccess_pt.c142
-rw-r--r--arch/s390/mm/Makefile2
-rw-r--r--arch/s390/mm/extable.c81
-rw-r--r--arch/s390/mm/fault.c7
-rw-r--r--arch/s390/mm/gup.c37
-rw-r--r--arch/s390/mm/init.c4
-rw-r--r--arch/s390/mm/pgtable.c6
-rw-r--r--arch/s390/mm/vmem.c2
-rw-r--r--arch/s390/net/Makefile4
-rw-r--r--arch/s390/net/bpf_jit.S130
-rw-r--r--arch/s390/net/bpf_jit_comp.c776
-rw-r--r--arch/score/kernel/process.c4
-rw-r--r--arch/sh/drivers/pci/pci.c5
-rw-r--r--arch/sh/kernel/cpu/sh5/entry.S2
-rw-r--r--arch/sh/kernel/entry-common.S2
-rw-r--r--arch/sparc/kernel/leon_pci.c9
-rw-r--r--arch/sparc/kernel/module.c13
-rw-r--r--arch/sparc/kernel/pci.c4
-rw-r--r--arch/tile/Kconfig8
-rw-r--r--arch/tile/include/asm/topology.h1
-rw-r--r--arch/tile/include/gxio/iorpc_trio.h24
-rw-r--r--arch/tile/kernel/pci.c34
-rw-r--r--arch/tile/kernel/pci_gx.c8
-rw-r--r--arch/um/drivers/mconsole_kern.c1
-rw-r--r--arch/um/include/asm/processor-generic.h9
-rw-r--r--arch/um/include/shared/common-offsets.h10
-rw-r--r--arch/um/include/shared/user.h11
-rw-r--r--arch/um/kernel/exec.c25
-rw-r--r--arch/um/kernel/process.c8
-rw-r--r--arch/um/kernel/signal.c6
-rw-r--r--arch/um/kernel/syscall.c24
-rw-r--r--arch/um/scripts/Makefile.rules2
-rw-r--r--arch/unicore32/kernel/pci.c8
-rw-r--r--arch/x86/Kconfig106
-rw-r--r--arch/x86/Kconfig.cpu5
-rw-r--r--arch/x86/Makefile2
-rw-r--r--arch/x86/boot/compressed/Makefile3
-rw-r--r--arch/x86/boot/compressed/eboot.c34
-rw-r--r--arch/x86/boot/compressed/eboot.h4
-rw-r--r--arch/x86/boot/header.S4
-rw-r--r--arch/x86/configs/i386_defconfig23
-rw-r--r--arch/x86/configs/x86_64_defconfig23
-rw-r--r--arch/x86/ia32/ia32_signal.c31
-rw-r--r--arch/x86/ia32/ia32entry.S6
-rw-r--r--arch/x86/ia32/sys_ia32.c2
-rw-r--r--arch/x86/include/asm/alternative-asm.h9
-rw-r--r--arch/x86/include/asm/alternative.h36
-rw-r--r--arch/x86/include/asm/bitops.h14
-rw-r--r--arch/x86/include/asm/calling.h48
-rw-r--r--arch/x86/include/asm/cpufeature.h4
-rw-r--r--arch/x86/include/asm/fpu-internal.h422
-rw-r--r--arch/x86/include/asm/ftrace.h56
-rw-r--r--arch/x86/include/asm/futex.h19
-rw-r--r--arch/x86/include/asm/hardirq.h4
-rw-r--r--arch/x86/include/asm/hpet.h2
-rw-r--r--arch/x86/include/asm/i387.h29
-rw-r--r--arch/x86/include/asm/iommu_table.h6
-rw-r--r--arch/x86/include/asm/kprobes.h1
-rw-r--r--arch/x86/include/asm/kvm.h16
-rw-r--r--arch/x86/include/asm/kvm_host.h16
-rw-r--r--arch/x86/include/asm/mce.h13
-rw-r--r--arch/x86/include/asm/microcode.h10
-rw-r--r--arch/x86/include/asm/perf_event.h2
-rw-r--r--arch/x86/include/asm/perf_regs.h33
-rw-r--r--arch/x86/include/asm/pgtable_types.h6
-rw-r--r--arch/x86/include/asm/processor-flags.h1
-rw-r--r--arch/x86/include/asm/processor.h3
-rw-r--r--arch/x86/include/asm/rcu.h32
-rw-r--r--arch/x86/include/asm/signal.h4
-rw-r--r--arch/x86/include/asm/smap.h91
-rw-r--r--arch/x86/include/asm/svm.h205
-rw-r--r--arch/x86/include/asm/sys_ia32.h2
-rw-r--r--arch/x86/include/asm/thread_info.h10
-rw-r--r--arch/x86/include/asm/uaccess.h28
-rw-r--r--arch/x86/include/asm/uaccess_32.h3
-rw-r--r--arch/x86/include/asm/uaccess_64.h3
-rw-r--r--arch/x86/include/asm/uprobes.h3
-rw-r--r--arch/x86/include/asm/vdso.h3
-rw-r--r--arch/x86/include/asm/vmx.h127
-rw-r--r--arch/x86/include/asm/x86_init.h9
-rw-r--r--arch/x86/include/asm/xen/page.h3
-rw-r--r--arch/x86/include/asm/xor_32.h56
-rw-r--r--arch/x86/include/asm/xor_64.h61
-rw-r--r--arch/x86/include/asm/xor_avx.h54
-rw-r--r--arch/x86/include/asm/xsave.h23
-rw-r--r--arch/x86/kernel/Makefile2
-rw-r--r--arch/x86/kernel/acpi/boot.c2
-rw-r--r--arch/x86/kernel/acpi/sleep.c15
-rw-r--r--arch/x86/kernel/alternative.c111
-rw-r--r--arch/x86/kernel/apic/apic.c2
-rw-r--r--arch/x86/kernel/cpu/amd.c67
-rw-r--r--arch/x86/kernel/cpu/bugs.c7
-rw-r--r--arch/x86/kernel/cpu/common.c63
-rw-r--r--arch/x86/kernel/cpu/intel.c4
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce-inject.c8
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce-internal.h12
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce.c94
-rw-r--r--arch/x86/kernel/cpu/mcheck/mce_intel.c168
-rw-r--r--arch/x86/kernel/cpu/perf_event.h2
-rw-r--r--arch/x86/kernel/cpu/perf_event_amd_ibs.c12
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel.c24
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_ds.c14
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_uncore.c36
-rw-r--r--arch/x86/kernel/cpu/perf_event_intel_uncore.h6
-rw-r--r--arch/x86/kernel/cpu/proc.c5
-rw-r--r--arch/x86/kernel/cpuid.c5
-rw-r--r--arch/x86/kernel/devicetree.c51
-rw-r--r--arch/x86/kernel/entry_32.S100
-rw-r--r--arch/x86/kernel/entry_64.S172
-rw-r--r--arch/x86/kernel/ftrace.c73
-rw-r--r--arch/x86/kernel/head_32.S31
-rw-r--r--arch/x86/kernel/i387.c292
-rw-r--r--arch/x86/kernel/i8259.c2
-rw-r--r--arch/x86/kernel/irq.c4
-rw-r--r--arch/x86/kernel/kprobes.c67
-rw-r--r--arch/x86/kernel/microcode_amd.c357
-rw-r--r--arch/x86/kernel/microcode_core.c67
-rw-r--r--arch/x86/kernel/microcode_intel.c3
-rw-r--r--arch/x86/kernel/msr.c5
-rw-r--r--arch/x86/kernel/perf_regs.c105
-rw-r--r--arch/x86/kernel/probe_roms.c2
-rw-r--r--arch/x86/kernel/process.c22
-rw-r--r--arch/x86/kernel/process_32.c4
-rw-r--r--arch/x86/kernel/process_64.c4
-rw-r--r--arch/x86/kernel/ptrace.c8
-rw-r--r--arch/x86/kernel/setup.c4
-rw-r--r--arch/x86/kernel/signal.c231
-rw-r--r--arch/x86/kernel/smpboot.c20
-rw-r--r--arch/x86/kernel/step.c53
-rw-r--r--arch/x86/kernel/traps.c174
-rw-r--r--arch/x86/kernel/uprobes.c52
-rw-r--r--arch/x86/kernel/x8664_ksyms_64.c6
-rw-r--r--arch/x86/kernel/x86_init.c4
-rw-r--r--arch/x86/kernel/xsave.c517
-rw-r--r--arch/x86/kvm/trace.h89
-rw-r--r--arch/x86/kvm/vmx.c12
-rw-r--r--arch/x86/kvm/x86.c3
-rw-r--r--arch/x86/lib/copy_user_64.S7
-rw-r--r--arch/x86/lib/copy_user_nocache_64.S3
-rw-r--r--arch/x86/lib/getuser.S10
-rw-r--r--arch/x86/lib/putuser.S8
-rw-r--r--arch/x86/lib/usercopy_32.c13
-rw-r--r--arch/x86/lib/usercopy_64.c3
-rw-r--r--arch/x86/mm/fault.c31
-rw-r--r--arch/x86/mm/init.c2
-rw-r--r--arch/x86/mm/init_32.c13
-rw-r--r--arch/x86/mm/tlb.c4
-rw-r--r--arch/x86/pci/acpi.c3
-rw-r--r--arch/x86/pci/mmconfig-shared.c2
-rw-r--r--arch/x86/pci/visws.c5
-rw-r--r--arch/x86/platform/efi/Makefile1
-rw-r--r--arch/x86/platform/efi/efi-bgrt.c76
-rw-r--r--arch/x86/platform/efi/efi.c66
-rw-r--r--arch/x86/realmode/rm/wakeup.h2
-rw-r--r--arch/x86/realmode/rm/wakeup_asm.S29
-rw-r--r--arch/x86/um/Kconfig1
-rw-r--r--arch/x86/um/shared/sysdep/kernel-offsets.h3
-rw-r--r--arch/x86/um/shared/sysdep/syscalls.h2
-rw-r--r--arch/x86/um/signal.c6
-rw-r--r--arch/x86/um/sys_call_table_32.c2
-rw-r--r--arch/x86/um/syscalls_32.c27
-rw-r--r--arch/x86/um/syscalls_64.c23
-rw-r--r--arch/x86/xen/enlighten.c4
-rw-r--r--arch/x86/xen/mmu.c18
-rw-r--r--arch/x86/xen/p2m.c27
-rw-r--r--arch/x86/xen/setup.c4
-rw-r--r--arch/x86/xen/smp.c6
-rw-r--r--arch/xtensa/Kconfig18
-rw-r--r--arch/xtensa/kernel/pci.c8
-rw-r--r--arch/xtensa/kernel/process.c3
-rw-r--r--block/blk-core.c8
-rw-r--r--block/ioctl.c2
-rw-r--r--block/partitions/ibm.c455
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/acpi/Kconfig4
-rw-r--r--drivers/acpi/bgrt.c76
-rw-r--r--drivers/acpi/pci_root.c101
-rw-r--r--drivers/acpi/pci_slot.c44
-rw-r--r--drivers/base/core.c105
-rw-r--r--drivers/base/devres.c42
-rw-r--r--drivers/base/firmware_class.c838
-rw-r--r--drivers/base/platform.c41
-rw-r--r--drivers/base/power/main.c22
-rw-r--r--drivers/base/regmap/regmap-irq.c92
-rw-r--r--drivers/base/regmap/regmap.c13
-rw-r--r--drivers/block/Kconfig12
-rw-r--r--drivers/block/Makefile1
-rw-r--r--drivers/block/aoe/aoecmd.c1
-rw-r--r--drivers/block/cciss_scsi.c1
-rw-r--r--drivers/block/mtip32xx/mtip32xx.c38
-rw-r--r--drivers/block/mtip32xx/mtip32xx.h10
-rw-r--r--drivers/block/nbd.c9
-rw-r--r--drivers/block/nvme.c155
-rw-r--r--drivers/block/rbd.c7
-rw-r--r--drivers/block/ub.c2474
-rw-r--r--drivers/block/xen-blkback/blkback.c2
-rw-r--r--drivers/bus/Kconfig21
-rw-r--r--drivers/bus/Makefile8
-rw-r--r--drivers/bus/omap-ocp2scp.c88
-rw-r--r--drivers/bus/omap_l3_noc.c (renamed from arch/arm/mach-omap2/omap_l3_noc.c)4
-rw-r--r--drivers/bus/omap_l3_noc.h (renamed from arch/arm/mach-omap2/omap_l3_noc.h)0
-rw-r--r--drivers/bus/omap_l3_smx.c (renamed from arch/arm/mach-omap2/omap_l3_smx.c)0
-rw-r--r--drivers/bus/omap_l3_smx.h (renamed from arch/arm/mach-omap2/omap_l3_smx.h)0
-rw-r--r--drivers/char/Kconfig6
-rw-r--r--drivers/char/agp/sgi-agp.c5
-rw-r--r--drivers/char/mmtimer.c3
-rw-r--r--drivers/char/nwbutton.c4
-rw-r--r--drivers/char/nwflash.c34
-rw-r--r--drivers/char/pcmcia/synclink_cs.c20
-rw-r--r--drivers/char/ppdev.c3
-rw-r--r--drivers/char/rtc.c2
-rw-r--r--drivers/char/tlclk.c4
-rw-r--r--drivers/char/virtio_console.c12
-rw-r--r--drivers/clk/Makefile2
-rw-r--r--drivers/clk/clk-devres.c55
-rw-r--r--drivers/clk/clk-vt8500.c510
-rw-r--r--drivers/clk/clkdev.c45
-rw-r--r--drivers/clk/mxs/clk-imx23.c2
-rw-r--r--drivers/clk/mxs/clk-imx28.c2
-rw-r--r--drivers/clocksource/Kconfig5
-rw-r--r--drivers/clocksource/Makefile2
-rw-r--r--drivers/clocksource/arm_generic.c232
-rw-r--r--drivers/cpufreq/powernow-k8.c63
-rw-r--r--drivers/dma/at_hdmac.c21
-rw-r--r--drivers/dma/ep93xx_dma.c2
-rw-r--r--drivers/dma/fsldma.c2
-rw-r--r--drivers/dma/imx-dma.c4
-rw-r--r--drivers/dma/intel_mid_dma.c2
-rw-r--r--drivers/dma/intel_mid_dma_regs.h6
-rw-r--r--drivers/dma/ioat/hw.h4
-rw-r--r--drivers/dma/pl330.c23
-rw-r--r--drivers/dma/ppc4xx/adma.c2
-rw-r--r--drivers/dma/ste_dma40_ll.h2
-rw-r--r--drivers/dma/tegra20-apb-dma.c8
-rw-r--r--drivers/edac/edac_mc.c57
-rw-r--r--drivers/edac/i3200_edac.c2
-rw-r--r--drivers/edac/i5000_edac.c4
-rw-r--r--drivers/edac/sb_edac.c7
-rw-r--r--drivers/extcon/Kconfig8
-rw-r--r--drivers/extcon/Makefile5
-rw-r--r--drivers/extcon/extcon-adc-jack.c198
-rw-r--r--drivers/extcon/extcon-arizona.c83
-rw-r--r--drivers/extcon/extcon-class.c (renamed from drivers/extcon/extcon_class.c)12
-rw-r--r--drivers/extcon/extcon-gpio.c (renamed from drivers/extcon/extcon_gpio.c)0
-rw-r--r--drivers/extcon/extcon-max77693.c8
-rw-r--r--drivers/firmware/efivars.c17
-rw-r--r--drivers/gpio/Kconfig6
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-lpc32xx.c5
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c2
-rw-r--r--drivers/gpio/gpio-mxs.c48
-rw-r--r--drivers/gpio/gpio-omap.c8
-rw-r--r--drivers/gpio/gpio-samsung.c21
-rw-r--r--drivers/gpio/gpio-vt8500.c316
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c3
-rw-r--r--drivers/gpu/drm/i915/intel_display.c12
-rw-r--r--drivers/gpu/drm/i915/intel_hdmi.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_abi16.c2
-rw-r--r--drivers/gpu/drm/nouveau/nv50_gpio.c15
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fb.c1
-rw-r--r--drivers/gpu/drm/nouveau/nvc0_fifo.c3
-rw-r--r--drivers/gpu/drm/nouveau/nve0_fifo.c3
-rw-r--r--drivers/gpu/drm/radeon/atombios_crtc.c163
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c10
-rw-r--r--drivers/gpu/drm/radeon/r100.c3
-rw-r--r--drivers/gpu/drm/udl/udl_connector.c7
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_fence.c2
-rw-r--r--drivers/gpu/vga/vgaarb.c29
-rw-r--r--drivers/hid/Kconfig36
-rw-r--r--drivers/hid/Makefile22
-rw-r--r--drivers/hid/hid-a4tech.c1
-rw-r--r--drivers/hid/hid-apple.c1
-rw-r--r--drivers/hid/hid-aureal.c1
-rw-r--r--drivers/hid/hid-belkin.c1
-rw-r--r--drivers/hid/hid-cherry.c1
-rw-r--r--drivers/hid/hid-core.c43
-rw-r--r--drivers/hid/hid-cypress.c1
-rw-r--r--drivers/hid/hid-debug.c12
-rw-r--r--drivers/hid/hid-ezkey.c1
-rw-r--r--drivers/hid/hid-gyration.c1
-rw-r--r--drivers/hid/hid-holtekff.c3
-rw-r--r--drivers/hid/hid-ids.h22
-rw-r--r--drivers/hid/hid-input.c11
-rw-r--r--drivers/hid/hid-lcpower.c2
-rw-r--r--drivers/hid/hid-lenovo-tpkbd.c149
-rw-r--r--drivers/hid/hid-lg.c20
-rw-r--r--drivers/hid/hid-lg.h4
-rw-r--r--drivers/hid/hid-lg4ff.c198
-rw-r--r--drivers/hid/hid-logitech-dj.c45
-rw-r--r--drivers/hid/hid-logitech-dj.h1
-rw-r--r--drivers/hid/hid-magicmouse.c2
-rw-r--r--drivers/hid/hid-microsoft.c1
-rw-r--r--drivers/hid/hid-monterey.c1
-rw-r--r--drivers/hid/hid-multitouch.c252
-rw-r--r--drivers/hid/hid-ntrig.c8
-rw-r--r--drivers/hid/hid-petalynx.c1
-rw-r--r--drivers/hid/hid-picolcd.c2748
-rw-r--r--drivers/hid/hid-picolcd.h309
-rw-r--r--drivers/hid/hid-picolcd_backlight.c122
-rw-r--r--drivers/hid/hid-picolcd_cir.c152
-rw-r--r--drivers/hid/hid-picolcd_core.c689
-rw-r--r--drivers/hid/hid-picolcd_debugfs.c899
-rw-r--r--drivers/hid/hid-picolcd_fb.c615
-rw-r--r--drivers/hid/hid-picolcd_lcd.c107
-rw-r--r--drivers/hid/hid-picolcd_leds.c175
-rw-r--r--drivers/hid/hid-primax.c25
-rw-r--r--drivers/hid/hid-prodikeys.c18
-rw-r--r--drivers/hid/hid-ps3remote.c215
-rw-r--r--drivers/hid/hid-samsung.c1
-rw-r--r--drivers/hid/hid-sensor-hub.c680
-rw-r--r--drivers/hid/hid-sony.c1
-rw-r--r--drivers/hid/hid-sunplus.c1
-rw-r--r--drivers/hid/hid-uclogic.c98
-rw-r--r--drivers/hid/hid-wacom.c170
-rw-r--r--drivers/hid/hid-waltop.c29
-rw-r--r--drivers/hid/hid-wiimote-ext.c97
-rw-r--r--drivers/hid/hidraw.c84
-rw-r--r--drivers/hid/usbhid/hid-core.c6
-rw-r--r--drivers/hid/usbhid/hid-quirks.c3
-rw-r--r--drivers/hv/hv.c34
-rw-r--r--drivers/hv/hv_kvp.c253
-rw-r--r--drivers/hv/hv_util.c4
-rw-r--r--drivers/hv/hyperv_vmbus.h47
-rw-r--r--drivers/hv/vmbus_drv.c42
-rw-r--r--drivers/hwmon/Kconfig126
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/abituguru.c7
-rw-r--r--drivers/hwmon/abituguru3.c6
-rw-r--r--drivers/hwmon/ad7314.c24
-rw-r--r--drivers/hwmon/ad7414.c21
-rw-r--r--drivers/hwmon/ad7418.c21
-rw-r--r--drivers/hwmon/adcxx.c9
-rw-r--r--drivers/hwmon/adm1029.c21
-rw-r--r--drivers/hwmon/adm9240.c26
-rw-r--r--drivers/hwmon/ads1015.c12
-rw-r--r--drivers/hwmon/ads7828.c15
-rw-r--r--drivers/hwmon/ads7871.c30
-rw-r--r--drivers/hwmon/adt7410.c464
-rw-r--r--drivers/hwmon/adt7411.c9
-rw-r--r--drivers/hwmon/adt7462.c15
-rw-r--r--drivers/hwmon/adt7470.c15
-rw-r--r--drivers/hwmon/amc6821.c16
-rw-r--r--drivers/hwmon/applesmc.c4
-rw-r--r--drivers/hwmon/asb100.c17
-rw-r--r--drivers/hwmon/asus_atk0110.c1
-rw-r--r--drivers/hwmon/coretemp.c5
-rw-r--r--drivers/hwmon/dme1737.c45
-rw-r--r--drivers/hwmon/ds620.c16
-rw-r--r--drivers/hwmon/emc1403.c18
-rw-r--r--drivers/hwmon/emc2103.c10
-rw-r--r--drivers/hwmon/f71882fg.c48
-rw-r--r--drivers/hwmon/f75375s.c8
-rw-r--r--drivers/hwmon/fam15h_power.c40
-rw-r--r--drivers/hwmon/g760a.c10
-rw-r--r--drivers/hwmon/ina2xx.c169
-rw-r--r--drivers/hwmon/jz4740-hwmon.c53
-rw-r--r--drivers/hwmon/lm70.c42
-rw-r--r--drivers/hwmon/lm93.c2
-rw-r--r--drivers/hwmon/lm95241.c15
-rw-r--r--drivers/hwmon/lm95245.c15
-rw-r--r--drivers/hwmon/ltc4151.c15
-rw-r--r--drivers/hwmon/ltc4215.c15
-rw-r--r--drivers/hwmon/ltc4245.c14
-rw-r--r--drivers/hwmon/max1111.c74
-rw-r--r--drivers/hwmon/max1668.c8
-rw-r--r--drivers/hwmon/max197.c349
-rw-r--r--drivers/hwmon/mcp3021.c67
-rw-r--r--drivers/hwmon/sch5627.c5
-rw-r--r--drivers/hwmon/sch5636.c6
-rw-r--r--drivers/hwmon/sch56xx-common.c28
-rw-r--r--drivers/hwmon/sht15.c154
-rw-r--r--drivers/hwmon/sht21.c13
-rw-r--r--drivers/hwmon/via-cputemp.c28
-rw-r--r--drivers/hwmon/vt8231.c24
-rw-r--r--drivers/hwmon/w83791d.c7
-rw-r--r--drivers/hwmon/w83792d.c58
-rw-r--r--drivers/hwmon/w83793.c23
-rw-r--r--drivers/hwmon/w83l786ng.c13
-rw-r--r--drivers/hwspinlock/hwspinlock_core.c3
-rw-r--r--drivers/i2c/busses/i2c-mpc.c12
-rw-r--r--drivers/i2c/busses/i2c-omap.c3
-rw-r--r--drivers/i2c/busses/i2c-tegra.c130
-rw-r--r--drivers/iio/Kconfig7
-rw-r--r--drivers/iio/Makefile5
-rw-r--r--drivers/iio/accel/Kconfig16
-rw-r--r--drivers/iio/accel/Makefile5
-rw-r--r--drivers/iio/accel/hid-sensor-accel-3d.c418
-rw-r--r--drivers/iio/adc/Kconfig38
-rw-r--r--drivers/iio/adc/Makefile4
-rw-r--r--drivers/iio/adc/ad7266.c2
-rw-r--r--drivers/iio/adc/ad7476.c (renamed from drivers/staging/iio/adc/ad7476_core.c)210
-rw-r--r--drivers/iio/adc/ad7791.c460
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c558
-rw-r--r--drivers/iio/adc/at91_adc.c77
-rw-r--r--drivers/iio/adc/lp8788_adc.c264
-rw-r--r--drivers/iio/common/Kconfig5
-rw-r--r--drivers/iio/common/Makefile9
-rw-r--r--drivers/iio/common/hid-sensors/Kconfig26
-rw-r--r--drivers/iio/common/hid-sensors/Makefile6
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.c250
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.h57
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.c103
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.h26
-rw-r--r--drivers/iio/dac/Kconfig20
-rw-r--r--drivers/iio/dac/Makefile1
-rw-r--r--drivers/iio/dac/ad5446.c450
-rw-r--r--drivers/iio/dac/ad5446.h91
-rw-r--r--drivers/iio/dac/ad5755.c650
-rw-r--r--drivers/iio/gyro/Kconfig16
-rw-r--r--drivers/iio/gyro/Makefile5
-rw-r--r--drivers/iio/gyro/hid-sensor-gyro-3d.c418
-rw-r--r--drivers/iio/industrialio-buffer.c15
-rw-r--r--drivers/iio/industrialio-core.c13
-rw-r--r--drivers/iio/inkern.c139
-rw-r--r--drivers/iio/kfifo_buf.c31
-rw-r--r--drivers/iio/light/Kconfig10
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/adjd_s311.c2
-rw-r--r--drivers/iio/light/hid-sensor-als.c385
-rw-r--r--drivers/iio/magnetometer/Kconfig16
-rw-r--r--drivers/iio/magnetometer/Makefile5
-rw-r--r--drivers/iio/magnetometer/hid-sensor-magn-3d.c419
-rw-r--r--drivers/infiniband/hw/cxgb4/cm.c2
-rw-r--r--drivers/infiniband/hw/ehca/ehca_irq.c250
-rw-r--r--drivers/infiniband/hw/ehca/ehca_irq.h6
-rw-r--r--drivers/infiniband/hw/mthca/mthca_reset.c8
-rw-r--r--drivers/infiniband/hw/ocrdma/ocrdma_verbs.c8
-rw-r--r--drivers/infiniband/hw/qib/qib.h2
-rw-r--r--drivers/infiniband/hw/qib/qib_mad.c3
-rw-r--r--drivers/infiniband/hw/qib/qib_pcie.c40
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib.h5
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c93
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c2
-rw-r--r--drivers/input/evdev.c78
-rw-r--r--drivers/input/input-mt.c305
-rw-r--r--drivers/input/input.c254
-rw-r--r--drivers/input/keyboard/imx_keypad.c4
-rw-r--r--drivers/input/misc/ab8500-ponkey.c4
-rw-r--r--drivers/input/misc/uinput.c2
-rw-r--r--drivers/input/mouse/alps.c2
-rw-r--r--drivers/input/mouse/bcm5974.c348
-rw-r--r--drivers/input/mouse/elantech.c4
-rw-r--r--drivers/input/mouse/rpcmouse.c2
-rw-r--r--drivers/input/mouse/sentelic.c13
-rw-r--r--drivers/input/mouse/synaptics.c4
-rw-r--r--drivers/input/serio/ambakmi.c6
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h6
-rw-r--r--drivers/input/tablet/wacom_wac.c6
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c2
-rw-r--r--drivers/input/touchscreen/cyttsp_core.c2
-rw-r--r--drivers/input/touchscreen/edt-ft5x06.c11
-rw-r--r--drivers/input/touchscreen/egalax_ts.c2
-rw-r--r--drivers/input/touchscreen/ili210x.c2
-rw-r--r--drivers/input/touchscreen/mms114.c2
-rw-r--r--drivers/input/touchscreen/penmount.c2
-rw-r--r--drivers/input/touchscreen/usbtouchscreen.c40
-rw-r--r--drivers/input/touchscreen/wacom_w8001.c2
-rw-r--r--drivers/iommu/amd_iommu.c6
-rw-r--r--drivers/iommu/intel-iommu.c6
-rw-r--r--drivers/irqchip/Kconfig1
-rw-r--r--drivers/leds/Kconfig10
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/ledtrig-cpu.c163
-rw-r--r--drivers/lguest/x86/core.c10
-rw-r--r--drivers/macintosh/mediabay.c8
-rw-r--r--drivers/md/dm-mpath.c11
-rw-r--r--drivers/md/dm-table.c61
-rw-r--r--drivers/md/dm-thin.c135
-rw-r--r--drivers/md/dm-verity.c8
-rw-r--r--drivers/md/dm.c71
-rw-r--r--drivers/md/dm.h1
-rw-r--r--drivers/md/md.c6
-rw-r--r--drivers/md/raid10.c8
-rw-r--r--drivers/md/raid5.c7
-rw-r--r--drivers/media/dvb/ngene/ngene-cards.c2
-rw-r--r--drivers/memory/Makefile3
-rw-r--r--drivers/memory/emif.c205
-rw-r--r--drivers/memory/of_memory.c153
-rw-r--r--drivers/memory/of_memory.h36
-rw-r--r--drivers/memory/tegra20-mc.c10
-rw-r--r--drivers/memory/tegra30-mc.c22
-rw-r--r--drivers/mfd/ab8500-gpadc.c2
-rw-r--r--drivers/mfd/da9052-i2c.c4
-rw-r--r--drivers/mfd/rc5t583.c2
-rw-r--r--drivers/mfd/rdc321x-southbridge.c2
-rw-r--r--drivers/mfd/tps65911-comparator.c2
-rw-r--r--drivers/mfd/wm8994-irq.c1
-rw-r--r--drivers/misc/Kconfig14
-rw-r--r--drivers/misc/bmp085-i2c.c7
-rw-r--r--drivers/misc/bmp085-spi.c13
-rw-r--r--drivers/misc/c2port/Kconfig5
-rw-r--r--drivers/misc/carma/carma-fpga-program.c1
-rw-r--r--drivers/misc/carma/carma-fpga.c2
-rw-r--r--drivers/misc/eeprom/Kconfig2
-rw-r--r--drivers/misc/eeprom/at25.c83
-rw-r--r--drivers/misc/hpilo.c11
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.c192
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d.h49
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_i2c.c32
-rw-r--r--drivers/misc/lis3lv02d/lis3lv02d_spi.c21
-rw-r--r--drivers/misc/mei/Kconfig2
-rw-r--r--drivers/misc/mei/hw.h83
-rw-r--r--drivers/misc/mei/init.c93
-rw-r--r--drivers/misc/mei/interface.h10
-rw-r--r--drivers/misc/mei/interrupt.c44
-rw-r--r--drivers/misc/mei/iorw.c59
-rw-r--r--drivers/misc/mei/main.c107
-rw-r--r--drivers/misc/mei/mei_dev.h78
-rw-r--r--drivers/misc/mei/wd.c91
-rw-r--r--drivers/misc/pch_phub.c15
-rw-r--r--drivers/misc/ti-st/st_core.c12
-rw-r--r--drivers/misc/ti-st/st_kim.c109
-rw-r--r--drivers/misc/tifm_7xx1.c14
-rw-r--r--drivers/mmc/core/sdio.c2
-rw-r--r--drivers/mmc/host/at91_mci.c2
-rw-r--r--drivers/mmc/host/atmel-mci.c2
-rw-r--r--drivers/mmc/host/omap_hsmmc.c4
-rw-r--r--drivers/mmc/host/sdhci-esdhc-imx.c2
-rw-r--r--drivers/mmc/host/vub300.c2
-rw-r--r--drivers/mtd/mtdchar.c48
-rw-r--r--drivers/mtd/nand/Kconfig40
-rw-r--r--drivers/net/can/janz-ican3.c4
-rw-r--r--drivers/net/can/mscan/mpc5xxx_can.c4
-rw-r--r--drivers/net/can/ti_hecc.c2
-rw-r--r--drivers/net/ethernet/3com/typhoon.c2
-rw-r--r--drivers/net/ethernet/atheros/atl1c/atl1c_main.c4
-rw-r--r--drivers/net/ethernet/atheros/atl1e/atl1e_main.c2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2.c4
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x.h2
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c12
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c31
-rw-r--r--drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h2
-rw-r--r--drivers/net/ethernet/broadcom/tg3.c52
-rw-r--r--drivers/net/ethernet/cadence/at91_ether.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c2
-rw-r--r--drivers/net/ethernet/chelsio/cxgb3/t3_hw.c22
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c12
-rw-r--r--drivers/net/ethernet/chelsio/cxgb4/t4_hw.c6
-rw-r--r--drivers/net/ethernet/emulex/benet/be_main.c2
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ethtool.c1
-rw-r--r--drivers/net/ethernet/freescale/gianfar_ptp.c4
-rw-r--r--drivers/net/ethernet/intel/e100.c2
-rw-r--r--drivers/net/ethernet/intel/e1000/e1000_main.c13
-rw-r--r--drivers/net/ethernet/intel/e1000e/netdev.c29
-rw-r--r--drivers/net/ethernet/intel/igb/igb_main.c14
-rw-r--r--drivers/net/ethernet/intel/igbvf/netdev.c2
-rw-r--r--drivers/net/ethernet/intel/ixgb/ixgb_main.c2
-rw-r--r--drivers/net/ethernet/intel/ixgbe/ixgbe_main.c4
-rw-r--r--drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.c30
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/icm.h10
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/main.c2
-rw-r--r--drivers/net/ethernet/mellanox/mlx4/reset.c8
-rw-r--r--drivers/net/ethernet/myricom/myri10ge/myri10ge.c31
-rw-r--r--drivers/net/ethernet/neterion/s2io.c2
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-config.c4
-rw-r--r--drivers/net/ethernet/neterion/vxge/vxge-main.c2
-rw-r--r--drivers/net/ethernet/octeon/octeon_mgmt.c4
-rw-r--r--drivers/net/ethernet/oki-semi/pch_gbe/pch_gbe_main.c2
-rw-r--r--drivers/net/ethernet/pasemi/pasemi_mac.c4
-rw-r--r--drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c8
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c4
-rw-r--r--drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c2
-rw-r--r--drivers/net/ethernet/qlogic/qlge/qlge_main.c2
-rw-r--r--drivers/net/ethernet/realtek/r8169.c44
-rw-r--r--drivers/net/ethernet/seeq/ether3.c4
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_main.c10
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c8
-rw-r--r--drivers/net/ethernet/sun/niu.c19
-rw-r--r--drivers/net/irda/sh_sir.c2
-rw-r--r--drivers/net/phy/bcm87xx.c2
-rw-r--r--drivers/net/phy/micrel.c45
-rw-r--r--drivers/net/phy/smsc.c28
-rw-r--r--drivers/net/ppp/pppoe.c2
-rw-r--r--drivers/net/team/team.c44
-rw-r--r--drivers/net/usb/asix_devices.c4
-rw-r--r--drivers/net/usb/qmi_wwan.c11
-rw-r--r--drivers/net/usb/smsc75xx.c1
-rw-r--r--drivers/net/wireless/ath/ath9k/ar9003_eeprom.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/debug.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c4
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.h3
-rw-r--r--drivers/net/wireless/ath/ath9k/link.c2
-rw-r--r--drivers/net/wireless/ath/ath9k/pci.c21
-rw-r--r--drivers/net/wireless/b43/Kconfig4
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c2
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c26
-rw-r--r--drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c6
-rw-r--r--drivers/net/wireless/brcm80211/brcmsmac/channel.c2
-rw-r--r--drivers/net/wireless/iwlegacy/common.h4
-rw-r--r--drivers/net/wireless/iwlwifi/pcie/trans.c8
-rw-r--r--drivers/net/wireless/rtlwifi/pci.c8
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c6
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/def.h1
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/hw.c12
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192ce/sw.c6
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192de/fw.c6
-rw-r--r--drivers/oprofile/cpu_buffer.c11
-rw-r--r--drivers/parisc/dino.c10
-rw-r--r--drivers/parisc/lba_pci.c7
-rw-r--r--drivers/pci/.gitignore4
-rw-r--r--drivers/pci/Kconfig1
-rw-r--r--drivers/pci/access.c202
-rw-r--r--drivers/pci/bus.c6
-rw-r--r--drivers/pci/hotplug/Kconfig24
-rw-r--r--drivers/pci/hotplug/Makefile3
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c99
-rw-r--r--drivers/pci/hotplug/cpcihp_generic.c8
-rw-r--r--drivers/pci/hotplug/cpqphp_ctrl.c21
-rw-r--r--drivers/pci/hotplug/fakephp.c164
-rw-r--r--drivers/pci/hotplug/pciehp_acpi.c6
-rw-r--r--drivers/pci/hotplug/pciehp_core.c28
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c12
-rw-r--r--drivers/pci/hotplug/pcihp_slot.c20
-rw-r--r--drivers/pci/iov.c6
-rw-r--r--drivers/pci/pci-driver.c57
-rw-r--r--drivers/pci/pci.c358
-rw-r--r--drivers/pci/pcie/aer/aer_inject.c2
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c26
-rw-r--r--drivers/pci/pcie/aer/aerdrv_acpi.c2
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c67
-rw-r--r--drivers/pci/pcie/aspm.c119
-rw-r--r--drivers/pci/pcie/pme.c29
-rw-r--r--drivers/pci/pcie/portdrv_bus.c2
-rw-r--r--drivers/pci/pcie/portdrv_core.c34
-rw-r--r--drivers/pci/pcie/portdrv_pci.c27
-rw-r--r--drivers/pci/probe.c39
-rw-r--r--drivers/pci/proc.c19
-rw-r--r--drivers/pci/quirks.c31
-rw-r--r--drivers/pci/remove.c156
-rw-r--r--drivers/pci/rom.c59
-rw-r--r--drivers/pci/search.c63
-rw-r--r--drivers/pci/setup-bus.c81
-rw-r--r--drivers/pci/setup-irq.c9
-rw-r--r--drivers/pci/xen-pcifront.c10
-rw-r--r--drivers/pcmcia/cardbus.c15
-rw-r--r--drivers/pcmcia/pxa2xx_base.c2
-rw-r--r--drivers/pinctrl/Kconfig9
-rw-r--r--drivers/pinctrl/Makefile2
-rw-r--r--drivers/pinctrl/pinctrl-exynos.c579
-rw-r--r--drivers/pinctrl/pinctrl-exynos.h218
-rw-r--r--drivers/pinctrl/pinctrl-samsung.c888
-rw-r--r--drivers/pinctrl/pinctrl-samsung.h239
-rw-r--r--drivers/pinctrl/pinctrl-tegra.c2
-rw-r--r--drivers/pinctrl/pinctrl-tegra.h44
-rw-r--r--drivers/power/Kconfig7
-rw-r--r--drivers/power/Makefile1
-rw-r--r--drivers/power/generic-adc-battery.c422
-rw-r--r--drivers/rapidio/devices/tsi721.c18
-rw-r--r--drivers/regulator/Kconfig38
-rw-r--r--drivers/regulator/Makefile2
-rw-r--r--drivers/regulator/aat2870-regulator.c2
-rw-r--r--drivers/regulator/ab3100.c6
-rw-r--r--drivers/regulator/ab8500.c36
-rw-r--r--drivers/regulator/arizona-ldo1.c6
-rw-r--r--drivers/regulator/arizona-micsupp.c5
-rw-r--r--drivers/regulator/core.c153
-rw-r--r--drivers/regulator/da9052-regulator.c4
-rw-r--r--drivers/regulator/dummy.c2
-rw-r--r--drivers/regulator/fan53555.c322
-rw-r--r--drivers/regulator/isl6271a-regulator.c6
-rw-r--r--drivers/regulator/lp872x.c88
-rw-r--r--drivers/regulator/lp8788-buck.c80
-rw-r--r--drivers/regulator/lp8788-ldo.c8
-rw-r--r--drivers/regulator/max77686.c30
-rw-r--r--drivers/regulator/max8907-regulator.c408
-rw-r--r--drivers/regulator/mc13783-regulator.c89
-rw-r--r--drivers/regulator/mc13892-regulator.c77
-rw-r--r--drivers/regulator/mc13xxx-regulator-core.c17
-rw-r--r--drivers/regulator/mc13xxx.h1
-rw-r--r--drivers/regulator/of_regulator.c25
-rw-r--r--drivers/regulator/palmas-regulator.c45
-rw-r--r--drivers/regulator/s2mps11.c27
-rw-r--r--drivers/regulator/tps6524x-regulator.c10
-rw-r--r--drivers/regulator/tps6586x-regulator.c76
-rw-r--r--drivers/regulator/twl-regulator.c110
-rw-r--r--drivers/regulator/wm831x-dcdc.c11
-rw-r--r--drivers/regulator/wm831x-ldo.c12
-rw-r--r--drivers/regulator/wm8400-regulator.c7
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c6
-rw-r--r--drivers/rtc/rtc-ab8500.c6
-rw-r--r--drivers/rtc/rtc-twl.c5
-rw-r--r--drivers/rtc/rtc-vt8500.c9
-rw-r--r--drivers/s390/block/Kconfig18
-rw-r--r--drivers/s390/block/Makefile6
-rw-r--r--drivers/s390/block/dasd.c17
-rw-r--r--drivers/s390/block/dasd_alias.c27
-rw-r--r--drivers/s390/block/dasd_eckd.c53
-rw-r--r--drivers/s390/block/dasd_ioctl.c4
-rw-r--r--drivers/s390/block/scm_blk.c445
-rw-r--r--drivers/s390/block/scm_blk.h117
-rw-r--r--drivers/s390/block/scm_blk_cluster.c228
-rw-r--r--drivers/s390/block/scm_drv.c81
-rw-r--r--drivers/s390/char/con3270.c1
-rw-r--r--drivers/s390/char/monreader.c5
-rw-r--r--drivers/s390/char/sclp.c2
-rw-r--r--drivers/s390/char/sclp_rw.c2
-rw-r--r--drivers/s390/char/tape.h1
-rw-r--r--drivers/s390/char/tape_std.h4
-rw-r--r--drivers/s390/char/vmlogrdr.c2
-rw-r--r--drivers/s390/cio/Makefile2
-rw-r--r--drivers/s390/cio/chsc.c52
-rw-r--r--drivers/s390/cio/chsc.h43
-rw-r--r--drivers/s390/cio/cio.c2
-rw-r--r--drivers/s390/cio/css.c1
-rw-r--r--drivers/s390/cio/device.c7
-rw-r--r--drivers/s390/cio/eadm_sch.c401
-rw-r--r--drivers/s390/cio/eadm_sch.h20
-rw-r--r--drivers/s390/cio/orb.h24
-rw-r--r--drivers/s390/cio/qdio_debug.h38
-rw-r--r--drivers/s390/cio/scm.c317
-rw-r--r--drivers/s390/crypto/Makefile3
-rw-r--r--drivers/s390/crypto/ap_bus.c209
-rw-r--r--drivers/s390/crypto/ap_bus.h35
-rw-r--r--drivers/s390/crypto/zcrypt_api.c187
-rw-r--r--drivers/s390/crypto/zcrypt_api.h19
-rw-r--r--drivers/s390/crypto/zcrypt_cex2a.c371
-rw-r--r--drivers/s390/crypto/zcrypt_cex4.c149
-rw-r--r--drivers/s390/crypto/zcrypt_cex4.h12
-rw-r--r--drivers/s390/crypto/zcrypt_debug.h59
-rw-r--r--drivers/s390/crypto/zcrypt_error.h13
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.c531
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype50.h39
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.c856
-rw-r--r--drivers/s390/crypto/zcrypt_msgtype6.h169
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.c781
-rw-r--r--drivers/s390/crypto/zcrypt_pcixcc.h3
-rw-r--r--drivers/s390/net/qeth_core_main.c15
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_core.c2
-rw-r--r--drivers/scsi/arm/eesox.c2
-rw-r--r--drivers/scsi/bfa/bfa_ioc.c2
-rw-r--r--drivers/scsi/bfa/bfa_ioc.h2
-rw-r--r--drivers/scsi/bnx2fc/bnx2fc_fcoe.c4
-rw-r--r--drivers/scsi/bnx2i/bnx2i_hwi.c3
-rw-r--r--drivers/scsi/gdth.h9
-rw-r--r--drivers/scsi/hpsa.c3
-rw-r--r--drivers/scsi/ipr.c68
-rw-r--r--drivers/scsi/isci/host.c2
-rw-r--r--drivers/scsi/isci/init.c2
-rw-r--r--drivers/scsi/isci/port.c2
-rw-r--r--drivers/scsi/isci/request.c2
-rw-r--r--drivers/scsi/isci/task.c2
-rw-r--r--drivers/scsi/lpfc/lpfc_init.c4
-rw-r--r--drivers/scsi/lpfc/lpfc_sli.c4
-rw-r--r--drivers/scsi/megaraid.c5
-rw-r--r--drivers/scsi/megaraid.h35
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c7
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c2
-rw-r--r--drivers/scsi/mvumi.c6
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c8
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c2
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.c4
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c8
-rw-r--r--drivers/scsi/sym53c8xx_2/sym_glue.c2
-rw-r--r--drivers/scsi/virtio_scsi.c2
-rw-r--r--drivers/scsi/vmw_pvscsi.c4
-rw-r--r--drivers/sh/intc/core.c2
-rw-r--r--drivers/sh/pfc/pinctrl.c5
-rw-r--r--drivers/spi/spi-au1550.c2
-rw-r--r--drivers/spi/spi-bfin-sport.c2
-rw-r--r--drivers/spi/spi-imx.c2
-rw-r--r--drivers/spi/spi-oc-tiny.c2
-rw-r--r--drivers/spi/spi-omap2-mcspi.c2
-rw-r--r--drivers/spi/spi-ppc4xx.c4
-rw-r--r--drivers/spi/spi-topcliff-pch.c2
-rw-r--r--drivers/staging/Kconfig12
-rw-r--r--drivers/staging/Makefile6
-rw-r--r--drivers/staging/android/alarm-dev.c17
-rw-r--r--drivers/staging/android/ashmem.c32
-rw-r--r--drivers/staging/android/binder.c6
-rw-r--r--drivers/staging/android/logger.c40
-rw-r--r--drivers/staging/android/logger.h24
-rw-r--r--drivers/staging/android/timed_gpio.c13
-rw-r--r--drivers/staging/asus_oled/asus_oled.c56
-rw-r--r--drivers/staging/bcm/Bcmchar.c2
-rw-r--r--drivers/staging/bcm/CmHost.c150
-rw-r--r--drivers/staging/bcm/CmHost.h54
-rw-r--r--drivers/staging/bcm/InterfaceInit.c17
-rw-r--r--drivers/staging/bcm/InterfaceInit.h33
-rw-r--r--drivers/staging/bcm/Kconfig2
-rw-r--r--drivers/staging/bcm/Misc.c5
-rw-r--r--drivers/staging/bcm/PHSModule.c4
-rw-r--r--drivers/staging/bcm/Prototypes.h2
-rw-r--r--drivers/staging/bcm/Transmit.c234
-rw-r--r--drivers/staging/bcm/cntrl_SignalingInterface.h704
-rw-r--r--drivers/staging/bcm/hostmibs.c2
-rw-r--r--drivers/staging/bcm/nvm.c4
-rw-r--r--drivers/staging/bcm/target_params.h2
-rw-r--r--drivers/staging/ccg/Kconfig7
-rw-r--r--drivers/staging/ccg/Makefile2
-rw-r--r--drivers/staging/ccg/ccg.c43
-rw-r--r--drivers/staging/ccg/composite.c1688
-rw-r--r--drivers/staging/ccg/composite.h395
-rw-r--r--drivers/staging/ccg/config.c158
-rw-r--r--drivers/staging/ccg/epautoconf.c393
-rw-r--r--drivers/staging/ccg/f_acm.c814
-rw-r--r--drivers/staging/ccg/f_fs.c2455
-rw-r--r--drivers/staging/ccg/f_mass_storage.c3135
-rw-r--r--drivers/staging/ccg/f_rndis.c918
-rw-r--r--drivers/staging/ccg/gadget_chips.h150
-rw-r--r--drivers/staging/ccg/ndis.h47
-rw-r--r--drivers/staging/ccg/rndis.c1175
-rw-r--r--drivers/staging/ccg/rndis.h222
-rw-r--r--drivers/staging/ccg/storage_common.c893
-rw-r--r--drivers/staging/ccg/u_ether.c986
-rw-r--r--drivers/staging/ccg/u_ether.h154
-rw-r--r--drivers/staging/ccg/u_serial.c1341
-rw-r--r--drivers/staging/ccg/u_serial.h65
-rw-r--r--drivers/staging/ccg/usbstring.c71
-rw-r--r--drivers/staging/ced1401/Kconfig6
-rw-r--r--drivers/staging/ced1401/Makefile3
-rw-r--r--drivers/staging/ced1401/TODO10
-rw-r--r--drivers/staging/ced1401/ced_ioc.c1515
-rw-r--r--drivers/staging/ced1401/ced_ioctl.h345
-rw-r--r--drivers/staging/ced1401/machine.h127
-rw-r--r--drivers/staging/ced1401/usb1401.c1637
-rw-r--r--drivers/staging/ced1401/usb1401.h249
-rw-r--r--drivers/staging/ced1401/use1401.h287
-rw-r--r--drivers/staging/ced1401/use14_ioc.h301
-rw-r--r--drivers/staging/ced1401/userspace/use1401.c3035
-rw-r--r--drivers/staging/comedi/Kconfig66
-rw-r--r--drivers/staging/comedi/comedi.h1109
-rw-r--r--drivers/staging/comedi/comedi_fops.c152
-rw-r--r--drivers/staging/comedi/comedidev.h2
-rw-r--r--drivers/staging/comedi/drivers.c82
-rw-r--r--drivers/staging/comedi/drivers/8253.h15
-rw-r--r--drivers/staging/comedi/drivers/8255.c41
-rw-r--r--drivers/staging/comedi/drivers/8255_pci.c353
-rw-r--r--drivers/staging/comedi/drivers/Makefile6
-rw-r--r--drivers/staging/comedi/drivers/acl7225b.c6
-rw-r--r--drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.c195
-rw-r--r--drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.h27
-rw-r--r--drivers/staging/comedi/drivers/addi-data/addi_common.c14
-rw-r--r--drivers/staging/comedi/drivers/addi-data/amcc_s5933_58.h453
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.c18
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c61
-rw-r--r--drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.c81
-rw-r--r--drivers/staging/comedi/drivers/addi_apci_all.c18
-rw-r--r--drivers/staging/comedi/drivers/adl_pci6208.c183
-rw-r--r--drivers/staging/comedi/drivers/adl_pci7230.c190
-rw-r--r--drivers/staging/comedi/drivers/adl_pci7296.c183
-rw-r--r--drivers/staging/comedi/drivers/adl_pci7432.c200
-rw-r--r--drivers/staging/comedi/drivers/adl_pci7x3x.c332
-rw-r--r--drivers/staging/comedi/drivers/adl_pci8164.c73
-rw-r--r--drivers/staging/comedi/drivers/adl_pci9111.c973
-rw-r--r--drivers/staging/comedi/drivers/adl_pci9118.c961
-rw-r--r--drivers/staging/comedi/drivers/adq12b.c29
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1710.c766
-rw-r--r--drivers/staging/comedi/drivers/adv_pci1723.c222
-rw-r--r--drivers/staging/comedi/drivers/adv_pci_dio.c473
-rw-r--r--drivers/staging/comedi/drivers/aio_aio12_8.c218
-rw-r--r--drivers/staging/comedi/drivers/aio_iiro_16.c4
-rw-r--r--drivers/staging/comedi/drivers/amplc_dio200.c114
-rw-r--r--drivers/staging/comedi/drivers/amplc_pc236.c108
-rw-r--r--drivers/staging/comedi/drivers/amplc_pc263.c54
-rw-r--r--drivers/staging/comedi/drivers/amplc_pci224.c64
-rw-r--r--drivers/staging/comedi/drivers/amplc_pci230.c154
-rw-r--r--drivers/staging/comedi/drivers/c6xdigio.c6
-rw-r--r--drivers/staging/comedi/drivers/cb_das16_cs.c56
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidas.c202
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidas64.c128
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidda.c441
-rw-r--r--drivers/staging/comedi/drivers/cb_pcidio.c211
-rw-r--r--drivers/staging/comedi/drivers/cb_pcimdas.c279
-rw-r--r--drivers/staging/comedi/drivers/cb_pcimdda.c393
-rw-r--r--drivers/staging/comedi/drivers/comedi_bond.c110
-rw-r--r--drivers/staging/comedi/drivers/comedi_fc.h32
-rw-r--r--drivers/staging/comedi/drivers/comedi_parport.c86
-rw-r--r--drivers/staging/comedi/drivers/comedi_test.c108
-rw-r--r--drivers/staging/comedi/drivers/contec_pci_dio.c146
-rw-r--r--drivers/staging/comedi/drivers/daqboard2000.c466
-rw-r--r--drivers/staging/comedi/drivers/das08.c482
-rw-r--r--drivers/staging/comedi/drivers/das08.h15
-rw-r--r--drivers/staging/comedi/drivers/das08_cs.c26
-rw-r--r--drivers/staging/comedi/drivers/das16.c82
-rw-r--r--drivers/staging/comedi/drivers/das16m1.c54
-rw-r--r--drivers/staging/comedi/drivers/das1800.c145
-rw-r--r--drivers/staging/comedi/drivers/das6402.c4
-rw-r--r--drivers/staging/comedi/drivers/das800.c52
-rw-r--r--drivers/staging/comedi/drivers/dmm32at.c57
-rw-r--r--drivers/staging/comedi/drivers/dt2801.c12
-rw-r--r--drivers/staging/comedi/drivers/dt2811.c8
-rw-r--r--drivers/staging/comedi/drivers/dt2814.c46
-rw-r--r--drivers/staging/comedi/drivers/dt2815.c2
-rw-r--r--drivers/staging/comedi/drivers/dt2817.c2
-rw-r--r--drivers/staging/comedi/drivers/dt282x.c103
-rw-r--r--drivers/staging/comedi/drivers/dt3000.c48
-rw-r--r--drivers/staging/comedi/drivers/dt9812.c8
-rw-r--r--drivers/staging/comedi/drivers/dyna_pci10xx.c161
-rw-r--r--drivers/staging/comedi/drivers/fl512.c4
-rw-r--r--drivers/staging/comedi/drivers/gsc_hpdi.c43
-rw-r--r--drivers/staging/comedi/drivers/icp_multi.c692
-rw-r--r--drivers/staging/comedi/drivers/icp_multi.h297
-rw-r--r--drivers/staging/comedi/drivers/ii_pci20kc.c4
-rw-r--r--drivers/staging/comedi/drivers/jr3_pci.c9
-rw-r--r--drivers/staging/comedi/drivers/jr3_pci.h4
-rw-r--r--drivers/staging/comedi/drivers/ke_counter.c103
-rw-r--r--drivers/staging/comedi/drivers/me4000.c1636
-rw-r--r--drivers/staging/comedi/drivers/me4000.h409
-rw-r--r--drivers/staging/comedi/drivers/me_daq.c221
-rw-r--r--drivers/staging/comedi/drivers/mite.c285
-rw-r--r--drivers/staging/comedi/drivers/mite.h66
-rw-r--r--drivers/staging/comedi/drivers/mpc624.c2
-rw-r--r--drivers/staging/comedi/drivers/mpc8260cpm.c2
-rw-r--r--drivers/staging/comedi/drivers/multiq3.c14
-rw-r--r--drivers/staging/comedi/drivers/ni_6527.c171
-rw-r--r--drivers/staging/comedi/drivers/ni_65xx.c181
-rw-r--r--drivers/staging/comedi/drivers/ni_660x.c133
-rw-r--r--drivers/staging/comedi/drivers/ni_670x.c62
-rw-r--r--drivers/staging/comedi/drivers/ni_at_a2150.c46
-rw-r--r--drivers/staging/comedi/drivers/ni_at_ao.c8
-rw-r--r--drivers/staging/comedi/drivers/ni_atmio.c2
-rw-r--r--drivers/staging/comedi/drivers/ni_atmio16d.c67
-rw-r--r--drivers/staging/comedi/drivers/ni_daq_700.c212
-rw-r--r--drivers/staging/comedi/drivers/ni_daq_dio24.c12
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc.c467
-rw-r--r--drivers/staging/comedi/drivers/ni_labpc_cs.c6
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_common.c240
-rw-r--r--drivers/staging/comedi/drivers/ni_mio_cs.c4
-rw-r--r--drivers/staging/comedi/drivers/ni_pcidio.c472
-rw-r--r--drivers/staging/comedi/drivers/ni_pcimio.c70
-rw-r--r--drivers/staging/comedi/drivers/ni_tiocmd.c61
-rw-r--r--drivers/staging/comedi/drivers/pcl711.c53
-rw-r--r--drivers/staging/comedi/drivers/pcl724.c15
-rw-r--r--drivers/staging/comedi/drivers/pcl725.c4
-rw-r--r--drivers/staging/comedi/drivers/pcl726.c6
-rw-r--r--drivers/staging/comedi/drivers/pcl730.c8
-rw-r--r--drivers/staging/comedi/drivers/pcl812.c57
-rw-r--r--drivers/staging/comedi/drivers/pcl816.c52
-rw-r--r--drivers/staging/comedi/drivers/pcl818.c59
-rw-r--r--drivers/staging/comedi/drivers/pcm3724.c26
-rw-r--r--drivers/staging/comedi/drivers/pcm3730.c12
-rw-r--r--drivers/staging/comedi/drivers/pcm_common.c56
-rw-r--r--drivers/staging/comedi/drivers/pcmad.c2
-rw-r--r--drivers/staging/comedi/drivers/pcmda12.c2
-rw-r--r--drivers/staging/comedi/drivers/pcmmio.c32
-rw-r--r--drivers/staging/comedi/drivers/pcmuio.c14
-rw-r--r--drivers/staging/comedi/drivers/poc.c2
-rw-r--r--drivers/staging/comedi/drivers/quatech_daqp_cs.c63
-rw-r--r--drivers/staging/comedi/drivers/rtd520.c67
-rw-r--r--drivers/staging/comedi/drivers/rti800.c10
-rw-r--r--drivers/staging/comedi/drivers/rti802.c2
-rw-r--r--drivers/staging/comedi/drivers/s526.c602
-rw-r--r--drivers/staging/comedi/drivers/s626.c755
-rw-r--r--drivers/staging/comedi/drivers/s626.h1
-rw-r--r--drivers/staging/comedi/drivers/serial2002.c14
-rw-r--r--drivers/staging/comedi/drivers/skel.c74
-rw-r--r--drivers/staging/comedi/drivers/ssv_dnp.c7
-rw-r--r--drivers/staging/comedi/drivers/usbdux.c201
-rw-r--r--drivers/staging/comedi/drivers/usbduxfast.c120
-rw-r--r--drivers/staging/comedi/drivers/usbduxsigma.c192
-rw-r--r--drivers/staging/comedi/drivers/vmk80xx.c14
-rw-r--r--drivers/staging/comedi/kcomedilib/kcomedilib_main.c23
-rw-r--r--drivers/staging/comedi/range.c18
-rw-r--r--drivers/staging/cptm1217/clearpad_tm1217.c13
-rw-r--r--drivers/staging/crystalhd/crystalhd_lnx.c6
-rw-r--r--drivers/staging/csr/Makefile1
-rw-r--r--drivers/staging/csr/bh.c173
-rw-r--r--drivers/staging/csr/csr_formatted_io.c27
-rw-r--r--drivers/staging/csr/csr_formatted_io.h25
-rw-r--r--drivers/staging/csr/csr_framework_ext.c13
-rw-r--r--drivers/staging/csr/csr_panic.c1
-rw-r--r--drivers/staging/csr/csr_time.c27
-rw-r--r--drivers/staging/csr/csr_wifi_hip_card_sdio.c8
-rw-r--r--drivers/staging/csr/csr_wifi_hip_download.c2
-rw-r--r--drivers/staging/csr/csr_wifi_hip_send.c9
-rw-r--r--drivers/staging/csr/csr_wifi_hip_udi.c94
-rw-r--r--drivers/staging/csr/csr_wifi_hip_unifi.h1
-rw-r--r--drivers/staging/csr/drv.c46
-rw-r--r--drivers/staging/csr/firmware.c6
-rw-r--r--drivers/staging/csr/io.c53
-rw-r--r--drivers/staging/csr/monitor.c7
-rw-r--r--drivers/staging/csr/netdev.c630
-rw-r--r--drivers/staging/csr/sdio_events.c4
-rw-r--r--drivers/staging/csr/sdio_mmc.c65
-rw-r--r--drivers/staging/csr/sme_blocking.c4
-rw-r--r--drivers/staging/csr/sme_native.c6
-rw-r--r--drivers/staging/csr/sme_sys.c22
-rw-r--r--drivers/staging/csr/sme_wext.c8
-rw-r--r--drivers/staging/csr/ul_int.c5
-rw-r--r--drivers/staging/csr/unifi_pdu_processing.c5
-rw-r--r--drivers/staging/csr/unifi_priv.h40
-rw-r--r--drivers/staging/csr/unifi_wext.h27
-rw-r--r--drivers/staging/csr/wext_events.c8
-rw-r--r--drivers/staging/cxt1e1/linux.c12
-rw-r--r--drivers/staging/cxt1e1/musycc.c10
-rw-r--r--drivers/staging/dgrp/Kconfig9
-rw-r--r--drivers/staging/dgrp/Makefile12
-rw-r--r--drivers/staging/dgrp/README2
-rw-r--r--drivers/staging/dgrp/TODO13
-rw-r--r--drivers/staging/dgrp/dgrp_common.c200
-rw-r--r--drivers/staging/dgrp/dgrp_common.h208
-rw-r--r--drivers/staging/dgrp/dgrp_dpa_ops.c556
-rw-r--r--drivers/staging/dgrp/dgrp_driver.c110
-rw-r--r--drivers/staging/dgrp/dgrp_mon_ops.c346
-rw-r--r--drivers/staging/dgrp/dgrp_net_ops.c3737
-rw-r--r--drivers/staging/dgrp/dgrp_ports_ops.c170
-rw-r--r--drivers/staging/dgrp/dgrp_specproc.c822
-rw-r--r--drivers/staging/dgrp/dgrp_sysfs.c555
-rw-r--r--drivers/staging/dgrp/dgrp_tty.c3331
-rw-r--r--drivers/staging/dgrp/digirp.h129
-rw-r--r--drivers/staging/dgrp/drp.h693
-rw-r--r--drivers/staging/et131x/et131x.c26
-rw-r--r--drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c34
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_hw.c14
-rw-r--r--drivers/staging/ft1000/ft1000-usb/ft1000_usb.h2
-rw-r--r--drivers/staging/gdm72xx/gdm_qos.c118
-rw-r--r--drivers/staging/gdm72xx/gdm_qos.h58
-rw-r--r--drivers/staging/gdm72xx/gdm_sdio.c37
-rw-r--r--drivers/staging/gdm72xx/gdm_usb.c62
-rw-r--r--drivers/staging/gdm72xx/gdm_usb.h16
-rw-r--r--drivers/staging/gdm72xx/gdm_wimax.c16
-rw-r--r--drivers/staging/gdm72xx/usb_boot.c220
-rw-r--r--drivers/staging/iio/Documentation/generic_buffer.c10
-rw-r--r--drivers/staging/iio/Documentation/inkernel.txt6
-rw-r--r--drivers/staging/iio/Kconfig2
-rw-r--r--drivers/staging/iio/TODO2
-rw-r--r--drivers/staging/iio/accel/adis16201_core.c4
-rw-r--r--drivers/staging/iio/accel/adis16201_ring.c3
-rw-r--r--drivers/staging/iio/accel/adis16203_core.c4
-rw-r--r--drivers/staging/iio/accel/adis16203_ring.c5
-rw-r--r--drivers/staging/iio/accel/adis16204_core.c4
-rw-r--r--drivers/staging/iio/accel/adis16204_ring.c3
-rw-r--r--drivers/staging/iio/accel/adis16209_core.c4
-rw-r--r--drivers/staging/iio/accel/adis16209_ring.c3
-rw-r--r--drivers/staging/iio/accel/adis16220_core.c11
-rw-r--r--drivers/staging/iio/accel/adis16240_core.c4
-rw-r--r--drivers/staging/iio/accel/adis16240_ring.c3
-rw-r--r--drivers/staging/iio/accel/kxsd9.c6
-rw-r--r--drivers/staging/iio/accel/lis3l02dq.h14
-rw-r--r--drivers/staging/iio/accel/lis3l02dq_core.c26
-rw-r--r--drivers/staging/iio/accel/lis3l02dq_ring.c31
-rw-r--r--drivers/staging/iio/accel/sca3000.h2
-rw-r--r--drivers/staging/iio/accel/sca3000_core.c14
-rw-r--r--drivers/staging/iio/accel/sca3000_ring.c2
-rw-r--r--drivers/staging/iio/adc/Kconfig43
-rw-r--r--drivers/staging/iio/adc/Makefile5
-rw-r--r--drivers/staging/iio/adc/ad7192.c523
-rw-r--r--drivers/staging/iio/adc/ad7298_core.c2
-rw-r--r--drivers/staging/iio/adc/ad7298_ring.c3
-rw-r--r--drivers/staging/iio/adc/ad7476.h66
-rw-r--r--drivers/staging/iio/adc/ad7476_ring.c64
-rw-r--r--drivers/staging/iio/adc/ad7606.h2
-rw-r--r--drivers/staging/iio/adc/ad7606_core.c6
-rw-r--r--drivers/staging/iio/adc/ad7606_ring.c3
-rw-r--r--drivers/staging/iio/adc/ad7780.c237
-rw-r--r--drivers/staging/iio/adc/ad7793.c688
-rw-r--r--drivers/staging/iio/adc/ad7793.h10
-rw-r--r--drivers/staging/iio/adc/ad7887_core.c2
-rw-r--r--drivers/staging/iio/adc/ad7887_ring.c2
-rw-r--r--drivers/staging/iio/adc/ad799x_ring.c3
-rw-r--r--drivers/staging/iio/adc/lpc32xx_adc.c2
-rw-r--r--drivers/staging/iio/adc/max1363.h2
-rw-r--r--drivers/staging/iio/adc/max1363_core.c32
-rw-r--r--drivers/staging/iio/adc/max1363_ring.c2
-rw-r--r--drivers/staging/iio/adc/mxs-lradc.c590
-rw-r--r--drivers/staging/iio/adc/spear_adc.c27
-rw-r--r--drivers/staging/iio/gyro/adis16060_core.c4
-rw-r--r--drivers/staging/iio/gyro/adis16080_core.c2
-rw-r--r--drivers/staging/iio/gyro/adis16130_core.c2
-rw-r--r--drivers/staging/iio/gyro/adis16260_core.c12
-rw-r--r--drivers/staging/iio/gyro/adis16260_ring.c3
-rw-r--r--drivers/staging/iio/gyro/adxrs450_core.c2
-rw-r--r--drivers/staging/iio/iio_dummy_evgen.c2
-rw-r--r--drivers/staging/iio/iio_hwmon.c47
-rw-r--r--drivers/staging/iio/iio_simple_dummy.c24
-rw-r--r--drivers/staging/iio/iio_simple_dummy.h6
-rw-r--r--drivers/staging/iio/iio_simple_dummy_buffer.c13
-rw-r--r--drivers/staging/iio/impedance-analyzer/ad5933.c4
-rw-r--r--drivers/staging/iio/imu/adis16400.h2
-rw-r--r--drivers/staging/iio/imu/adis16400_core.c18
-rw-r--r--drivers/staging/iio/imu/adis16400_ring.c2
-rw-r--r--drivers/staging/iio/light/isl29018.c17
-rw-r--r--drivers/staging/iio/light/tsl2563.c2
-rw-r--r--drivers/staging/iio/magnetometer/hmc5843.c2
-rw-r--r--drivers/staging/iio/meter/ade7753.c13
-rw-r--r--drivers/staging/iio/meter/ade7754.c12
-rw-r--r--drivers/staging/iio/meter/ade7758.h2
-rw-r--r--drivers/staging/iio/meter/ade7758_core.c13
-rw-r--r--drivers/staging/iio/meter/ade7758_ring.c3
-rw-r--r--drivers/staging/iio/meter/ade7759.c11
-rw-r--r--drivers/staging/iio/meter/ade7854-spi.c2
-rw-r--r--drivers/staging/iio/resolver/ad2s1210.c2
-rw-r--r--drivers/staging/iio/ring_hw.h2
-rw-r--r--drivers/staging/iio/ring_sw.c7
-rw-r--r--drivers/staging/iio/trigger/Kconfig2
-rw-r--r--drivers/staging/iio/trigger/iio-trig-bfin-timer.c109
-rw-r--r--drivers/staging/iio/trigger/iio-trig-bfin-timer.h24
-rw-r--r--drivers/staging/iio/trigger/iio-trig-gpio.c8
-rw-r--r--drivers/staging/iio/trigger/iio-trig-periodic-rtc.c8
-rw-r--r--drivers/staging/iio/trigger/iio-trig-sysfs.c17
-rw-r--r--drivers/staging/imx-drm/Kconfig35
-rw-r--r--drivers/staging/imx-drm/Makefile9
-rw-r--r--drivers/staging/imx-drm/TODO22
-rw-r--r--drivers/staging/imx-drm/imx-drm-core.c884
-rw-r--r--drivers/staging/imx-drm/imx-drm.h58
-rw-r--r--drivers/staging/imx-drm/imx-fb.c47
-rw-r--r--drivers/staging/imx-drm/imx-fbdev.c74
-rw-r--r--drivers/staging/imx-drm/ipu-v3/Makefile3
-rw-r--r--drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h318
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-common.c1143
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-dc.c372
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-di.c700
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c408
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-dp.c336
-rw-r--r--drivers/staging/imx-drm/ipu-v3/ipu-prv.h206
-rw-r--r--drivers/staging/imx-drm/ipuv3-crtc.c579
-rw-r--r--drivers/staging/imx-drm/parallel-display.c261
-rw-r--r--drivers/staging/ipack/Kconfig1
-rw-r--r--drivers/staging/ipack/TODO25
-rw-r--r--drivers/staging/ipack/bridges/tpci200.c735
-rw-r--r--drivers/staging/ipack/bridges/tpci200.h39
-rw-r--r--drivers/staging/ipack/devices/ipoctal.c738
-rw-r--r--drivers/staging/ipack/devices/scc2698.h117
-rw-r--r--drivers/staging/ipack/ipack.c372
-rw-r--r--drivers/staging/ipack/ipack.h88
-rw-r--r--drivers/staging/ipack/ipack_ids.h32
-rw-r--r--drivers/staging/keucr/smcommon.h2
-rw-r--r--drivers/staging/keucr/usb.c2
-rw-r--r--drivers/staging/line6/pcm.c8
-rw-r--r--drivers/staging/line6/variax.c8
-rw-r--r--drivers/staging/nvec/Kconfig2
-rw-r--r--drivers/staging/nvec/nvec.c25
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon.c53
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon_xo_1.c15
-rw-r--r--drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c6
-rw-r--r--drivers/staging/omap-thermal/omap-bandgap.c23
-rw-r--r--drivers/staging/omap-thermal/omap-thermal-common.c42
-rw-r--r--drivers/staging/omap-thermal/omap4-thermal.c54
-rw-r--r--drivers/staging/omap-thermal/omap5-thermal.c38
-rw-r--r--drivers/staging/omapdrm/omap_crtc.c33
-rw-r--r--drivers/staging/omapdrm/omap_dmm_tiler.c51
-rw-r--r--drivers/staging/omapdrm/omap_dmm_tiler.h17
-rw-r--r--drivers/staging/omapdrm/omap_drv.c21
-rw-r--r--drivers/staging/omapdrm/omap_drv.h33
-rw-r--r--drivers/staging/omapdrm/omap_fb.c99
-rw-r--r--drivers/staging/omapdrm/omap_gem.c60
-rw-r--r--drivers/staging/omapdrm/omap_plane.c111
-rw-r--r--drivers/staging/ozwpan/ozcdev.c14
-rw-r--r--drivers/staging/ozwpan/ozevent.c6
-rw-r--r--drivers/staging/ozwpan/ozhcd.c104
-rw-r--r--drivers/staging/ozwpan/ozmain.c2
-rw-r--r--drivers/staging/ozwpan/ozpd.c11
-rw-r--r--drivers/staging/ozwpan/ozpd.h1
-rw-r--r--drivers/staging/ozwpan/ozproto.c13
-rw-r--r--drivers/staging/ozwpan/ozproto.h8
-rw-r--r--drivers/staging/ozwpan/ozprotocol.h6
-rw-r--r--drivers/staging/panel/panel.c52
-rw-r--r--drivers/staging/ramster/Kconfig25
-rw-r--r--drivers/staging/ramster/Makefile7
-rw-r--r--drivers/staging/ramster/TODO13
-rw-r--r--drivers/staging/ramster/cluster/Makefile3
-rw-r--r--drivers/staging/ramster/ramster.h113
-rw-r--r--drivers/staging/ramster/ramster/heartbeat.c (renamed from drivers/staging/ramster/cluster/heartbeat.c)6
-rw-r--r--drivers/staging/ramster/ramster/heartbeat.h (renamed from drivers/staging/ramster/cluster/heartbeat.h)0
-rw-r--r--drivers/staging/ramster/ramster/masklog.c (renamed from drivers/staging/ramster/cluster/masklog.c)0
-rw-r--r--drivers/staging/ramster/ramster/masklog.h (renamed from drivers/staging/ramster/cluster/masklog.h)0
-rw-r--r--drivers/staging/ramster/ramster/nodemanager.c (renamed from drivers/staging/ramster/cluster/nodemanager.c)15
-rw-r--r--drivers/staging/ramster/ramster/nodemanager.h (renamed from drivers/staging/ramster/cluster/nodemanager.h)0
-rw-r--r--drivers/staging/ramster/ramster/r2net.c (renamed from drivers/staging/ramster/r2net.c)79
-rw-r--r--drivers/staging/ramster/ramster/ramster.c985
-rw-r--r--drivers/staging/ramster/ramster/ramster.h161
-rw-r--r--drivers/staging/ramster/ramster/ramster_nodemanager.h (renamed from drivers/staging/ramster/cluster/ramster_nodemanager.h)0
-rw-r--r--drivers/staging/ramster/ramster/tcp.c (renamed from drivers/staging/ramster/cluster/tcp.c)53
-rw-r--r--drivers/staging/ramster/ramster/tcp.h (renamed from drivers/staging/ramster/cluster/tcp.h)0
-rw-r--r--drivers/staging/ramster/ramster/tcp_internal.h (renamed from drivers/staging/ramster/cluster/tcp_internal.h)0
-rw-r--r--drivers/staging/ramster/tmem.c313
-rw-r--r--drivers/staging/ramster/tmem.h109
-rw-r--r--drivers/staging/ramster/xvmalloc.c509
-rw-r--r--drivers/staging/ramster/xvmalloc.h30
-rw-r--r--drivers/staging/ramster/xvmalloc_int.h95
-rw-r--r--drivers/staging/ramster/zbud.c1060
-rw-r--r--drivers/staging/ramster/zbud.h33
-rw-r--r--drivers/staging/ramster/zcache-main.c3544
-rw-r--r--drivers/staging/ramster/zcache.h55
-rw-r--r--drivers/staging/rtl8187se/ieee80211/ieee80211.h2
-rw-r--r--drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c13
-rw-r--r--drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c5
-rw-r--r--drivers/staging/rtl8187se/r8180_core.c4
-rw-r--r--drivers/staging/rtl8187se/r8180_hw.h9
-rw-r--r--drivers/staging/rtl8187se/r8185b_init.c2
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c9
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h4
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_core.c6
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_dm.c2
-rw-r--r--drivers/staging/rtl8192e/rtl8192e/rtl_pci.c8
-rw-r--r--drivers/staging/rtl8192e/rtllib.h8
-rw-r--r--drivers/staging/rtl8192e/rtllib_softmac.c24
-rw-r--r--drivers/staging/rtl8192e/rtllib_softmac_wx.c7
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211.h4
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c20
-rw-r--r--drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c5
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c2
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h4
-rw-r--r--drivers/staging/rtl8192u/ieee80211/rtl819x_HTProc.c2
-rw-r--r--drivers/staging/rtl8192u/r8192U_core.c27
-rw-r--r--drivers/staging/rtl8192u/r819xU_HTType.h2
-rw-r--r--drivers/staging/rtl8192u/r819xU_phyreg.h2
-rw-r--r--drivers/staging/rtl8712/drv_types.h9
-rw-r--r--drivers/staging/rtl8712/ethernet.h8
-rw-r--r--drivers/staging/rtl8712/os_intfs.c2
-rw-r--r--drivers/staging/rtl8712/rtl8712_recv.c9
-rw-r--r--drivers/staging/rtl8712/rtl8712_recv.h2
-rw-r--r--drivers/staging/rtl8712/rtl8712_xmit.c2
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_linux.c4
-rw-r--r--drivers/staging/rtl8712/rtl871x_ioctl_set.c5
-rw-r--r--drivers/staging/rtl8712/rtl871x_mlme.c5
-rw-r--r--drivers/staging/rtl8712/rtl871x_pwrctrl.h20
-rw-r--r--drivers/staging/rtl8712/rtl871x_recv.c9
-rw-r--r--drivers/staging/rtl8712/rtl871x_security.c8
-rw-r--r--drivers/staging/rtl8712/usb_intf.c1
-rw-r--r--drivers/staging/rtl8712/usb_osintf.h3
-rw-r--r--drivers/staging/rtl8712/usb_vendor_req.h58
-rw-r--r--drivers/staging/rts5139/rts51x_fop.c2
-rw-r--r--drivers/staging/rts5139/trace.h32
-rw-r--r--drivers/staging/rts_pstor/ms.c872
-rw-r--r--drivers/staging/rts_pstor/rtsx.c112
-rw-r--r--drivers/staging/rts_pstor/rtsx_card.c186
-rw-r--r--drivers/staging/rts_pstor/rtsx_chip.c563
-rw-r--r--drivers/staging/rts_pstor/rtsx_scsi.c405
-rw-r--r--drivers/staging/rts_pstor/rtsx_transport.c100
-rw-r--r--drivers/staging/rts_pstor/sd.c1055
-rw-r--r--drivers/staging/rts_pstor/trace.h30
-rw-r--r--drivers/staging/sbe-2t3e3/2t3e3.h1
-rw-r--r--drivers/staging/sbe-2t3e3/dc.c10
-rw-r--r--drivers/staging/sbe-2t3e3/module.c15
-rw-r--r--drivers/staging/sbe-2t3e3/netdev.c8
-rw-r--r--drivers/staging/sep/sep_main.c2
-rw-r--r--drivers/staging/serqt_usb2/serqt_usb2.c163
-rw-r--r--drivers/staging/silicom/Kconfig46
-rw-r--r--drivers/staging/silicom/Makefile9
-rw-r--r--drivers/staging/silicom/README14
-rw-r--r--drivers/staging/silicom/TODO8
-rw-r--r--drivers/staging/silicom/bits.h56
-rw-r--r--drivers/staging/silicom/bp_ioctl.h140
-rw-r--r--drivers/staging/silicom/bp_mod.c8931
-rw-r--r--drivers/staging/silicom/bp_mod.h704
-rw-r--r--drivers/staging/silicom/bp_proc.c1350
-rw-r--r--drivers/staging/silicom/bypass.h202
-rw-r--r--drivers/staging/silicom/bypasslib/Makefile6
-rw-r--r--drivers/staging/silicom/bypasslib/bp_ioctl.h198
-rw-r--r--drivers/staging/silicom/bypasslib/bplibk.h47
-rw-r--r--drivers/staging/silicom/bypasslib/bypass.c529
-rw-r--r--drivers/staging/silicom/bypasslib/libbp_sd.h509
-rw-r--r--drivers/staging/silicom/libbp_sd.h550
-rw-r--r--drivers/staging/slicoss/slicoss.c40
-rw-r--r--drivers/staging/sm7xxfb/sm7xx.h2
-rw-r--r--drivers/staging/sm7xxfb/sm7xxfb.c119
-rw-r--r--drivers/staging/speakup/i18n.c1
-rw-r--r--drivers/staging/speakup/speakup_soft.c15
-rw-r--r--drivers/staging/telephony/ixj.c4
-rw-r--r--drivers/staging/tidspbridge/Documentation/error-codes2
-rw-r--r--drivers/staging/tidspbridge/core/_tiomap.h2
-rw-r--r--drivers/staging/tidspbridge/core/chnl_sm.c9
-rw-r--r--drivers/staging/tidspbridge/core/dsp-clock.c2
-rw-r--r--drivers/staging/tidspbridge/core/io_sm.c10
-rw-r--r--drivers/staging/tidspbridge/core/sync.c2
-rw-r--r--drivers/staging/tidspbridge/core/tiomap3430.c8
-rw-r--r--drivers/staging/tidspbridge/core/tiomap3430_pwr.c2
-rw-r--r--drivers/staging/tidspbridge/core/wdt.c5
-rw-r--r--drivers/staging/tidspbridge/dynload/tramp.c8
-rw-r--r--drivers/staging/tidspbridge/gen/uuidutil.c21
-rw-r--r--drivers/staging/tidspbridge/hw/hw_mmu.c6
-rw-r--r--drivers/staging/tidspbridge/include/dspbridge/dspioctl.h2
-rw-r--r--drivers/staging/tidspbridge/include/dspbridge/mbx_sh.h2
-rw-r--r--drivers/staging/tidspbridge/include/dspbridge/node.h2
-rw-r--r--drivers/staging/tidspbridge/include/dspbridge/ntfy.h2
-rw-r--r--drivers/staging/tidspbridge/include/dspbridge/proc.h2
-rw-r--r--drivers/staging/tidspbridge/include/dspbridge/strm.h2
-rw-r--r--drivers/staging/tidspbridge/include/dspbridge/sync.h4
-rw-r--r--drivers/staging/tidspbridge/include/dspbridge/uuidutil.h20
-rw-r--r--drivers/staging/tidspbridge/rmgr/dbdcd.c29
-rw-r--r--drivers/staging/tidspbridge/rmgr/drv_interface.c14
-rw-r--r--drivers/staging/tidspbridge/rmgr/dspdrv.c4
-rw-r--r--drivers/staging/tidspbridge/rmgr/mgr.c4
-rw-r--r--drivers/staging/tidspbridge/rmgr/nldr.c2
-rw-r--r--drivers/staging/tidspbridge/rmgr/node.c5
-rw-r--r--drivers/staging/tidspbridge/rmgr/proc.c2
-rw-r--r--drivers/staging/usbip/Kconfig4
-rw-r--r--drivers/staging/usbip/stub_dev.c8
-rw-r--r--drivers/staging/usbip/stub_rx.c2
-rw-r--r--drivers/staging/usbip/usbip_common.c7
-rw-r--r--drivers/staging/usbip/userspace/configure.ac12
-rw-r--r--drivers/staging/usbip/userspace/doc/usbip.866
-rw-r--r--drivers/staging/usbip/userspace/doc/usbip_bind_driver.842
-rw-r--r--drivers/staging/usbip/userspace/doc/usbipd.816
-rw-r--r--drivers/staging/usbip/vhci_hcd.c31
-rw-r--r--drivers/staging/vme/devices/vme_pio2_core.c10
-rw-r--r--drivers/staging/vme/devices/vme_user.c10
-rw-r--r--drivers/staging/vt6655/80211mgr.h2
-rw-r--r--drivers/staging/vt6655/baseband.c172
-rw-r--r--drivers/staging/vt6655/baseband.h10
-rw-r--r--drivers/staging/vt6655/bssdb.c4
-rw-r--r--drivers/staging/vt6655/card.c101
-rw-r--r--drivers/staging/vt6655/datarate.c2
-rw-r--r--drivers/staging/vt6655/device.h4
-rw-r--r--drivers/staging/vt6655/device_main.c104
-rw-r--r--drivers/staging/vt6655/dpc.c2
-rw-r--r--drivers/staging/vt6655/hostap.c8
-rw-r--r--drivers/staging/vt6655/ioctl.c9
-rw-r--r--drivers/staging/vt6655/iwctl.c6
-rw-r--r--drivers/staging/vt6655/key.c2
-rw-r--r--drivers/staging/vt6655/mac.c12
-rw-r--r--drivers/staging/vt6655/mac.h2
-rw-r--r--drivers/staging/vt6655/mib.c2
-rw-r--r--drivers/staging/vt6655/power.c2
-rw-r--r--drivers/staging/vt6655/rf.c82
-rw-r--r--drivers/staging/vt6655/rf.h2
-rw-r--r--drivers/staging/vt6655/rxtx.c42
-rw-r--r--drivers/staging/vt6655/tcrc.c2
-rw-r--r--drivers/staging/vt6655/tcrc.h2
-rw-r--r--drivers/staging/vt6655/tether.c4
-rw-r--r--drivers/staging/vt6655/tkip.c2
-rw-r--r--drivers/staging/vt6655/vntwifi.c2
-rw-r--r--drivers/staging/vt6655/wcmd.c6
-rw-r--r--drivers/staging/vt6655/wmgr.c56
-rw-r--r--drivers/staging/vt6655/wmgr.h4
-rw-r--r--drivers/staging/vt6655/wpa.c2
-rw-r--r--drivers/staging/vt6655/wpa2.c8
-rw-r--r--drivers/staging/vt6655/wpactl.c6
-rw-r--r--drivers/staging/vt6655/wroute.c2
-rw-r--r--drivers/staging/vt6656/80211mgr.h4
-rw-r--r--drivers/staging/vt6656/baseband.c6
-rw-r--r--drivers/staging/vt6656/baseband.h2
-rw-r--r--drivers/staging/vt6656/bssdb.c18
-rw-r--r--drivers/staging/vt6656/card.c38
-rw-r--r--drivers/staging/vt6656/device.h2
-rw-r--r--drivers/staging/vt6656/dpc.c2
-rw-r--r--drivers/staging/vt6656/hostap.c30
-rw-r--r--drivers/staging/vt6656/int.c2
-rw-r--r--drivers/staging/vt6656/ioctl.c11
-rw-r--r--drivers/staging/vt6656/iwctl.c2
-rw-r--r--drivers/staging/vt6656/key.c4
-rw-r--r--drivers/staging/vt6656/main_usb.c20
-rw-r--r--drivers/staging/vt6656/power.c2
-rw-r--r--drivers/staging/vt6656/rf.c58
-rw-r--r--drivers/staging/vt6656/rf.h2
-rw-r--r--drivers/staging/vt6656/rxtx.c52
-rw-r--r--drivers/staging/vt6656/tcrc.c2
-rw-r--r--drivers/staging/vt6656/tcrc.h2
-rw-r--r--drivers/staging/vt6656/tether.c4
-rw-r--r--drivers/staging/vt6656/tkip.c2
-rw-r--r--drivers/staging/vt6656/wcmd.c8
-rw-r--r--drivers/staging/vt6656/wctl.c2
-rw-r--r--drivers/staging/vt6656/wmgr.c26
-rw-r--r--drivers/staging/vt6656/wpa.c2
-rw-r--r--drivers/staging/vt6656/wpa2.c6
-rw-r--r--drivers/staging/vt6656/wpactl.c6
-rw-r--r--drivers/staging/winbond/Kconfig2
-rw-r--r--drivers/staging/winbond/localpara.h4
-rw-r--r--drivers/staging/winbond/mds.c2
-rw-r--r--drivers/staging/winbond/mto.c2
-rw-r--r--drivers/staging/winbond/phy_calibration.c14
-rw-r--r--drivers/staging/winbond/reg.c2
-rw-r--r--drivers/staging/winbond/sme_api.h11
-rw-r--r--drivers/staging/winbond/wb35reg.c4
-rw-r--r--drivers/staging/winbond/wb35tx.c154
-rw-r--r--drivers/staging/winbond/wb35tx_s.h46
-rw-r--r--drivers/staging/wlags49_h2/README.ubuntu12
-rw-r--r--drivers/staging/wlags49_h2/TODO10
-rw-r--r--drivers/staging/wlags49_h2/hcf.c20
-rw-r--r--drivers/staging/wlags49_h2/hcfcfg.h2
-rw-r--r--drivers/staging/wlags49_h2/hcfdef.h6
-rw-r--r--drivers/staging/wlags49_h2/mdd.h8
-rw-r--r--drivers/staging/wlags49_h2/sta_h2.c2
-rw-r--r--drivers/staging/wlags49_h2/sta_h25.c2
-rw-r--r--drivers/staging/wlags49_h2/wl_enc.h2
-rw-r--r--drivers/staging/wlags49_h2/wl_if.h2
-rw-r--r--drivers/staging/wlags49_h2/wl_internal.h4
-rw-r--r--drivers/staging/wlags49_h2/wl_main.c18
-rw-r--r--drivers/staging/wlags49_h2/wl_netdev.c59
-rw-r--r--drivers/staging/wlags49_h2/wl_pci.c8
-rw-r--r--drivers/staging/wlags49_h2/wl_priv.c2
-rw-r--r--drivers/staging/wlags49_h2/wl_profile.c2
-rw-r--r--drivers/staging/wlags49_h2/wl_version.h2
-rw-r--r--drivers/staging/wlags49_h2/wl_wext.c27
-rw-r--r--drivers/staging/wlags49_h25/Kconfig2
-rw-r--r--drivers/staging/wlags49_h25/TODO8
-rw-r--r--drivers/staging/wlan-ng/cfg80211.c11
-rw-r--r--drivers/staging/wlan-ng/hfa384x_usb.c37
-rw-r--r--drivers/staging/wlan-ng/p80211conv.c9
-rw-r--r--drivers/staging/wlan-ng/p80211netdev.c21
-rw-r--r--drivers/staging/wlan-ng/p80211netdev.h2
-rw-r--r--drivers/staging/wlan-ng/p80211req.c152
-rw-r--r--drivers/staging/wlan-ng/p80211types.h2
-rw-r--r--drivers/staging/wlan-ng/p80211wep.c18
-rw-r--r--drivers/staging/wlan-ng/prism2fw.c56
-rw-r--r--drivers/staging/wlan-ng/prism2sta.c53
-rw-r--r--drivers/staging/xgifb/XGI_main_26.c45
-rw-r--r--drivers/staging/xgifb/vb_def.h21
-rw-r--r--drivers/staging/xgifb/vb_init.c5
-rw-r--r--drivers/staging/xgifb/vb_setmode.c601
-rw-r--r--drivers/staging/xgifb/vb_struct.h49
-rw-r--r--drivers/staging/xgifb/vb_table.h194
-rw-r--r--drivers/staging/zcache/tmem.c2
-rw-r--r--drivers/staging/zsmalloc/zsmalloc-main.c322
-rw-r--r--drivers/staging/zsmalloc/zsmalloc_int.h155
-rw-r--r--drivers/target/iscsi/iscsi_target_parameters.c4
-rw-r--r--drivers/tty/Kconfig9
-rw-r--r--drivers/tty/hvc/Kconfig2
-rw-r--r--drivers/tty/hvc/hvc_console.c2
-rw-r--r--drivers/tty/ipwireless/network.c7
-rw-r--r--drivers/tty/n_gsm.c2
-rw-r--r--drivers/tty/pty.c2
-rw-r--r--drivers/tty/serial/8250/8250.c72
-rw-r--r--drivers/tty/serial/8250/8250.h9
-rw-r--r--drivers/tty/serial/8250/8250_pci.c11
-rw-r--r--drivers/tty/serial/8250/8250_pnp.c31
-rw-r--r--drivers/tty/serial/8250/Kconfig16
-rw-r--r--drivers/tty/serial/8250/Makefile5
-rw-r--r--drivers/tty/serial/Kconfig23
-rw-r--r--drivers/tty/serial/Makefile1
-rw-r--r--drivers/tty/serial/amba-pl011.c88
-rw-r--r--drivers/tty/serial/cpm_uart/cpm_uart_core.c23
-rw-r--r--drivers/tty/serial/imx.c8
-rw-r--r--drivers/tty/serial/jsm/jsm_driver.c2
-rw-r--r--drivers/tty/serial/kgdb_nmi.c402
-rw-r--r--drivers/tty/serial/kgdboc.c9
-rw-r--r--drivers/tty/serial/max3100.c14
-rw-r--r--drivers/tty/serial/mpc52xx_uart.c2
-rw-r--r--drivers/tty/serial/mxs-auart.c1
-rw-r--r--drivers/tty/serial/omap-serial.c27
-rw-r--r--drivers/tty/serial/samsung.c49
-rw-r--r--drivers/tty/serial/sccnxp.c17
-rw-r--r--drivers/tty/serial/serial_core.c28
-rw-r--r--drivers/tty/serial/serial_ks8695.c4
-rw-r--r--drivers/tty/serial/vt8500_serial.c58
-rw-r--r--drivers/usb/Kconfig1
-rw-r--r--drivers/usb/atm/ueagle-atm.c79
-rw-r--r--drivers/usb/atm/usbatm.c46
-rw-r--r--drivers/usb/chipidea/Kconfig1
-rw-r--r--drivers/usb/chipidea/Makefile4
-rw-r--r--drivers/usb/chipidea/ci.h1
-rw-r--r--drivers/usb/chipidea/ci13xxx_imx.c71
-rw-r--r--drivers/usb/chipidea/ci13xxx_imx.h28
-rw-r--r--drivers/usb/chipidea/core.c24
-rw-r--r--drivers/usb/chipidea/udc.c39
-rw-r--r--drivers/usb/chipidea/usbmisc_imx6q.c162
-rw-r--r--drivers/usb/class/cdc-acm.c1
-rw-r--r--drivers/usb/core/Kconfig2
-rw-r--r--drivers/usb/core/config.c2
-rw-r--r--drivers/usb/core/devices.c9
-rw-r--r--drivers/usb/core/devio.c35
-rw-r--r--drivers/usb/core/driver.c11
-rw-r--r--drivers/usb/core/endpoint.c10
-rw-r--r--drivers/usb/core/hcd.c28
-rw-r--r--drivers/usb/core/hub.c214
-rw-r--r--drivers/usb/core/message.c2
-rw-r--r--drivers/usb/core/quirks.c2
-rw-r--r--drivers/usb/core/sysfs.c12
-rw-r--r--drivers/usb/core/usb-acpi.c205
-rw-r--r--drivers/usb/core/usb.h13
-rw-r--r--drivers/usb/dwc3/Kconfig2
-rw-r--r--drivers/usb/dwc3/core.c18
-rw-r--r--drivers/usb/dwc3/core.h7
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c66
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c66
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c67
-rw-r--r--drivers/usb/dwc3/ep0.c217
-rw-r--r--drivers/usb/dwc3/gadget.c101
-rw-r--r--drivers/usb/early/ehci-dbgp.c17
-rw-r--r--drivers/usb/gadget/Kconfig78
-rw-r--r--drivers/usb/gadget/Makefile4
-rw-r--r--drivers/usb/gadget/acm_ms.c55
-rw-r--r--drivers/usb/gadget/amd5536udc.c6
-rw-r--r--drivers/usb/gadget/at91_udc.c5
-rw-r--r--drivers/usb/gadget/audio.c62
-rw-r--r--drivers/usb/gadget/bcm63xx_udc.c2464
-rw-r--r--drivers/usb/gadget/cdc2.c56
-rw-r--r--drivers/usb/gadget/composite.c290
-rw-r--r--drivers/usb/gadget/config.c6
-rw-r--r--drivers/usb/gadget/dbgp.c11
-rw-r--r--drivers/usb/gadget/dummy_hcd.c2
-rw-r--r--drivers/usb/gadget/epautoconf.c33
-rw-r--r--drivers/usb/gadget/ether.c61
-rw-r--r--drivers/usb/gadget/f_ecm.c5
-rw-r--r--drivers/usb/gadget/f_hid.c2
-rw-r--r--drivers/usb/gadget/f_mass_storage.c16
-rw-r--r--drivers/usb/gadget/f_midi.c1
-rw-r--r--drivers/usb/gadget/f_ncm.c5
-rw-r--r--drivers/usb/gadget/f_sourcesink.c2
-rw-r--r--drivers/usb/gadget/f_subset.c5
-rw-r--r--drivers/usb/gadget/f_uac2.c2
-rw-r--r--drivers/usb/gadget/file_storage.c38
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c6
-rw-r--r--drivers/usb/gadget/fusb300_udc.c4
-rw-r--r--drivers/usb/gadget/g_ffs.c26
-rw-r--r--drivers/usb/gadget/gadget_chips.h96
-rw-r--r--drivers/usb/gadget/gmidi.c61
-rw-r--r--drivers/usb/gadget/goku_udc.c6
-rw-r--r--drivers/usb/gadget/hid.c52
-rw-r--r--drivers/usb/gadget/inode.c29
-rw-r--r--drivers/usb/gadget/lpc32xx_udc.c129
-rw-r--r--drivers/usb/gadget/m66592-udc.c4
-rw-r--r--drivers/usb/gadget/mass_storage.c39
-rw-r--r--drivers/usb/gadget/multi.c42
-rw-r--r--drivers/usb/gadget/mv_udc_core.c91
-rw-r--r--drivers/usb/gadget/ncm.c62
-rw-r--r--drivers/usb/gadget/nokia.c58
-rw-r--r--drivers/usb/gadget/omap_udc.c6
-rw-r--r--drivers/usb/gadget/pch_udc.c6
-rw-r--r--drivers/usb/gadget/printer.c173
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c7
-rw-r--r--drivers/usb/gadget/pxa25x_udc.h2
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c6
-rw-r--r--drivers/usb/gadget/rndis.c22
-rw-r--r--drivers/usb/gadget/s3c-hsotg.c8
-rw-r--r--drivers/usb/gadget/s3c-hsudc.c51
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c148
-rw-r--r--drivers/usb/gadget/serial.c72
-rw-r--r--drivers/usb/gadget/tcm_usb_gadget.c49
-rw-r--r--drivers/usb/gadget/tcm_usb_gadget.h11
-rw-r--r--drivers/usb/gadget/u_ether.c8
-rw-r--r--drivers/usb/gadget/udc-core.c13
-rw-r--r--drivers/usb/gadget/usbstring.c3
-rw-r--r--drivers/usb/gadget/webcam.c42
-rw-r--r--drivers/usb/gadget/zero.c73
-rw-r--r--drivers/usb/host/Kconfig40
-rw-r--r--drivers/usb/host/ehci-atmel.c30
-rw-r--r--drivers/usb/host/ehci-au1xxx.c20
-rw-r--r--drivers/usb/host/ehci-cns3xxx.c16
-rw-r--r--drivers/usb/host/ehci-dbg.c6
-rw-r--r--drivers/usb/host/ehci-fsl.c57
-rw-r--r--drivers/usb/host/ehci-fsl.h1
-rw-r--r--drivers/usb/host/ehci-grlib.c18
-rw-r--r--drivers/usb/host/ehci-hcd.c4
-rw-r--r--drivers/usb/host/ehci-hub.c4
-rw-r--r--drivers/usb/host/ehci-ixp4xx.c19
-rw-r--r--drivers/usb/host/ehci-ls1x.c20
-rw-r--r--drivers/usb/host/ehci-msm.c13
-rw-r--r--drivers/usb/host/ehci-mv.c51
-rw-r--r--drivers/usb/host/ehci-mxc.c45
-rw-r--r--drivers/usb/host/ehci-platform.c50
-rw-r--r--drivers/usb/host/ehci-ppc-of.c28
-rw-r--r--drivers/usb/host/ehci-s5p.c7
-rw-r--r--drivers/usb/host/ehci-sead3.c15
-rw-r--r--drivers/usb/host/ehci-sh.c35
-rw-r--r--drivers/usb/host/ehci-tegra.c57
-rw-r--r--drivers/usb/host/ehci-timer.c6
-rw-r--r--drivers/usb/host/ehci-vt8500.c24
-rw-r--r--drivers/usb/host/ehci-xilinx-of.c20
-rw-r--r--drivers/usb/host/fhci-sched.c3
-rw-r--r--drivers/usb/host/fsl-mph-dr-of.c2
-rw-r--r--drivers/usb/host/ohci-at91.c3
-rw-r--r--drivers/usb/host/ohci-nxp.c2
-rw-r--r--drivers/usb/host/ohci-platform.c46
-rw-r--r--drivers/usb/host/ohci-pxa27x.c68
-rw-r--r--drivers/usb/host/ohci-xls.c2
-rw-r--r--drivers/usb/host/r8a66597-hcd.c5
-rw-r--r--drivers/usb/host/sl811-hcd.c2
-rw-r--r--drivers/usb/host/uhci-hcd.c5
-rw-r--r--drivers/usb/host/uhci-platform.c157
-rw-r--r--drivers/usb/host/whci/hcd.c8
-rw-r--r--drivers/usb/host/whci/qset.c11
-rw-r--r--drivers/usb/host/xhci-hub.c20
-rw-r--r--drivers/usb/host/xhci-mem.c7
-rw-r--r--drivers/usb/host/xhci-pci.c1
-rw-r--r--drivers/usb/host/xhci-ring.c290
-rw-r--r--drivers/usb/host/xhci.c43
-rw-r--r--drivers/usb/host/xhci.h20
-rw-r--r--drivers/usb/misc/Kconfig5
-rw-r--r--drivers/usb/misc/Makefile1
-rw-r--r--drivers/usb/misc/ezusb.c160
-rw-r--r--drivers/usb/misc/legousbtower.c3
-rw-r--r--drivers/usb/misc/rio500.c2
-rw-r--r--drivers/usb/musb/Kconfig3
-rw-r--r--drivers/usb/musb/am35x.c67
-rw-r--r--drivers/usb/musb/blackfin.c64
-rw-r--r--drivers/usb/musb/cppi_dma.c2
-rw-r--r--drivers/usb/musb/da8xx.c59
-rw-r--r--drivers/usb/musb/davinci.c67
-rw-r--r--drivers/usb/musb/musb_core.c331
-rw-r--r--drivers/usb/musb/musb_core.h17
-rw-r--r--drivers/usb/musb/musb_debugfs.c8
-rw-r--r--drivers/usb/musb/musb_dma.h2
-rw-r--r--drivers/usb/musb/musb_dsps.c194
-rw-r--r--drivers/usb/musb/musb_gadget.c172
-rw-r--r--drivers/usb/musb/musb_host.c264
-rw-r--r--drivers/usb/musb/musb_host.h3
-rw-r--r--drivers/usb/musb/musb_virthub.c13
-rw-r--r--drivers/usb/musb/musbhsdma.c2
-rw-r--r--drivers/usb/musb/omap2430.c156
-rw-r--r--drivers/usb/musb/omap2430.h9
-rw-r--r--drivers/usb/musb/tusb6010.c57
-rw-r--r--drivers/usb/musb/tusb6010_omap.c2
-rw-r--r--drivers/usb/musb/ux500.c33
-rw-r--r--drivers/usb/musb/ux500_dma.c2
-rw-r--r--drivers/usb/otg/Kconfig2
-rw-r--r--drivers/usb/otg/fsl_otg.c34
-rw-r--r--drivers/usb/otg/mxs-phy.c38
-rw-r--r--drivers/usb/otg/nop-usb-xceiv.c8
-rw-r--r--drivers/usb/otg/otg.c2
-rw-r--r--drivers/usb/otg/twl4030-usb.c26
-rw-r--r--drivers/usb/otg/twl6030-usb.c157
-rw-r--r--drivers/usb/phy/Kconfig17
-rw-r--r--drivers/usb/phy/Makefile3
-rw-r--r--drivers/usb/phy/isp1301.c6
-rw-r--r--drivers/usb/phy/mv_u3d_phy.c345
-rw-r--r--drivers/usb/phy/mv_u3d_phy.h105
-rw-r--r--drivers/usb/phy/omap-usb2.c271
-rw-r--r--drivers/usb/phy/tegra_usb_phy.c (renamed from arch/arm/mach-tegra/usb_phy.c)153
-rw-r--r--drivers/usb/renesas_usbhs/common.c15
-rw-r--r--drivers/usb/renesas_usbhs/mod.c25
-rw-r--r--drivers/usb/serial/Kconfig21
-rw-r--r--drivers/usb/serial/Makefile2
-rw-r--r--drivers/usb/serial/aircable.c5
-rw-r--r--drivers/usb/serial/ark3116.c7
-rw-r--r--drivers/usb/serial/belkin_sa.c8
-rw-r--r--drivers/usb/serial/ch341.c49
-rw-r--r--drivers/usb/serial/console.c23
-rw-r--r--drivers/usb/serial/cp210x.c141
-rw-r--r--drivers/usb/serial/cyberjack.c77
-rw-r--r--drivers/usb/serial/cypress_m8.c157
-rw-r--r--drivers/usb/serial/digi_acceleport.c50
-rw-r--r--drivers/usb/serial/empeg.c5
-rw-r--r--drivers/usb/serial/ezusb.c59
-rw-r--r--drivers/usb/serial/f81232.c8
-rw-r--r--drivers/usb/serial/ftdi_sio.c138
-rw-r--r--drivers/usb/serial/ftdi_sio_ids.h5
-rw-r--r--drivers/usb/serial/funsoft.c5
-rw-r--r--drivers/usb/serial/garmin_gps.c129
-rw-r--r--drivers/usb/serial/generic.c11
-rw-r--r--drivers/usb/serial/io_edgeport.c616
-rw-r--r--drivers/usb/serial/io_ti.c408
-rw-r--r--drivers/usb/serial/ipaq.c4
-rw-r--r--drivers/usb/serial/ipw.c66
-rw-r--r--drivers/usb/serial/ir-usb.c16
-rw-r--r--drivers/usb/serial/iuu_phoenix.c134
-rw-r--r--drivers/usb/serial/keyspan.c338
-rw-r--r--drivers/usb/serial/keyspan.h17
-rw-r--r--drivers/usb/serial/keyspan_pda.c73
-rw-r--r--drivers/usb/serial/kl5kusb105.c41
-rw-r--r--drivers/usb/serial/kobil_sct.c98
-rw-r--r--drivers/usb/serial/mct_u232.c104
-rw-r--r--drivers/usb/serial/metro-usb.c7
-rw-r--r--drivers/usb/serial/mos7720.c231
-rw-r--r--drivers/usb/serial/mos7840.c478
-rw-r--r--drivers/usb/serial/navman.c8
-rw-r--r--drivers/usb/serial/omninet.c43
-rw-r--r--drivers/usb/serial/opticon.c30
-rw-r--r--drivers/usb/serial/option.c23
-rw-r--r--drivers/usb/serial/oti6858.c77
-rw-r--r--drivers/usb/serial/pl2303.c8
-rw-r--r--drivers/usb/serial/qcaux.c10
-rw-r--r--drivers/usb/serial/qcserial.c5
-rw-r--r--drivers/usb/serial/quatech2.c7
-rw-r--r--drivers/usb/serial/safe_serial.c12
-rw-r--r--drivers/usb/serial/sierra.c11
-rw-r--r--drivers/usb/serial/spcp8x5.c5
-rw-r--r--drivers/usb/serial/ssu100.c37
-rw-r--r--drivers/usb/serial/symbolserial.c8
-rw-r--r--drivers/usb/serial/ti_usb_3410_5052.c150
-rw-r--r--drivers/usb/serial/usb-serial.c176
-rw-r--r--drivers/usb/serial/usb_wwan.c56
-rw-r--r--drivers/usb/serial/visor.c13
-rw-r--r--drivers/usb/serial/whiteheat.c135
-rw-r--r--drivers/usb/serial/zte_ev.c307
-rw-r--r--drivers/usb/storage/Kconfig14
-rw-r--r--drivers/usb/storage/Makefile9
-rw-r--r--drivers/usb/storage/alauda.c2
-rw-r--r--drivers/usb/storage/cypress_atacb.c2
-rw-r--r--drivers/usb/storage/datafab.c2
-rw-r--r--drivers/usb/storage/ene_ub6250.c30
-rw-r--r--drivers/usb/storage/freecom.c2
-rw-r--r--drivers/usb/storage/isd200.c5
-rw-r--r--drivers/usb/storage/jumpshot.c2
-rw-r--r--drivers/usb/storage/karma.c2
-rw-r--r--drivers/usb/storage/libusual.c243
-rw-r--r--drivers/usb/storage/onetouch.c2
-rw-r--r--drivers/usb/storage/realtek_cr.c2
-rw-r--r--drivers/usb/storage/sddr09.c2
-rw-r--r--drivers/usb/storage/sddr55.c2
-rw-r--r--drivers/usb/storage/shuttle_usbat.c2
-rw-r--r--drivers/usb/storage/sierra_ms.c3
-rw-r--r--drivers/usb/storage/transport.c2
-rw-r--r--drivers/usb/storage/uas.c107
-rw-r--r--drivers/usb/storage/unusual_devs.h36
-rw-r--r--drivers/usb/storage/usb.c17
-rw-r--r--drivers/usb/storage/usual-tables.c18
-rw-r--r--drivers/usb/wusbcore/Kconfig3
-rw-r--r--drivers/usb/wusbcore/security.c7
-rw-r--r--drivers/usb/wusbcore/wa-hc.c4
-rw-r--r--drivers/vfio/pci/vfio_pci_intrs.c76
-rw-r--r--drivers/video/Kconfig18
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/backlight/88pm860x_bl.c1
-rw-r--r--drivers/video/da8xx-fb.c8
-rw-r--r--drivers/video/efifb.c4
-rw-r--r--drivers/video/epson1355fb.c749
-rw-r--r--drivers/video/exynos/exynos_mipi_dsi.c2
-rw-r--r--drivers/video/tmiofb.c4
-rw-r--r--drivers/video/vt8500lcdfb.c79
-rw-r--r--drivers/video/wm8505fb.c97
-rw-r--r--drivers/video/wmt_ge_rops.c9
-rw-r--r--drivers/vme/bridges/vme_ca91cx42.c20
-rw-r--r--drivers/vme/bridges/vme_tsi148.c10
-rw-r--r--drivers/w1/masters/Kconfig1
-rw-r--r--drivers/w1/masters/ds1wm.c2
-rw-r--r--drivers/w1/masters/omap_hdq.c56
-rw-r--r--drivers/w1/masters/w1-gpio.c66
-rw-r--r--drivers/watchdog/hpwdt.c3
-rw-r--r--drivers/watchdog/mpc8xxx_wdt.c2
-rw-r--r--drivers/watchdog/watchdog_core.c3
-rw-r--r--drivers/xen/Makefile2
-rw-r--r--drivers/xen/dbgp.c48
-rw-r--r--drivers/xen/gntdev.c5
-rw-r--r--drivers/xen/grant-table.c6
-rw-r--r--drivers/xen/xen-pciback/pci_stub.c2
-rw-r--r--fs/btrfs/ctree.h2
-rw-r--r--fs/btrfs/delayed-ref.h2
-rw-r--r--fs/btrfs/inode.c4
-rw-r--r--fs/cifs/Kconfig38
-rw-r--r--fs/cifs/Makefile2
-rw-r--r--fs/cifs/cifs_unicode.c2
-rw-r--r--fs/cifs/cifsacl.c2
-rw-r--r--fs/cifs/cifsencrypt.c67
-rw-r--r--fs/cifs/cifsfs.c63
-rw-r--r--fs/cifs/cifsfs.h2
-rw-r--r--fs/cifs/cifsglob.h268
-rw-r--r--fs/cifs/cifspdu.h2
-rw-r--r--fs/cifs/cifsproto.h136
-rw-r--r--fs/cifs/cifssmb.c289
-rw-r--r--fs/cifs/connect.c198
-rw-r--r--fs/cifs/dir.c124
-rw-r--r--fs/cifs/file.c1055
-rw-r--r--fs/cifs/inode.c441
-rw-r--r--fs/cifs/ioctl.c32
-rw-r--r--fs/cifs/link.c74
-rw-r--r--fs/cifs/misc.c32
-rw-r--r--fs/cifs/netmisc.c9
-rw-r--r--fs/cifs/readdir.c167
-rw-r--r--fs/cifs/sess.c3
-rw-r--r--fs/cifs/smb1ops.c328
-rw-r--r--fs/cifs/smb2file.c302
-rw-r--r--fs/cifs/smb2glob.h14
-rw-r--r--fs/cifs/smb2inode.c98
-rw-r--r--fs/cifs/smb2maperror.c6
-rw-r--r--fs/cifs/smb2misc.c240
-rw-r--r--fs/cifs/smb2ops.c346
-rw-r--r--fs/cifs/smb2pdu.c1235
-rw-r--r--fs/cifs/smb2pdu.h284
-rw-r--r--fs/cifs/smb2proto.h87
-rw-r--r--fs/cifs/smb2transport.c204
-rw-r--r--fs/cifs/transport.c283
-rw-r--r--fs/compat_ioctl.c6
-rw-r--r--fs/dcache.c12
-rw-r--r--fs/debugfs/file.c76
-rw-r--r--fs/debugfs/inode.c8
-rw-r--r--fs/dlm/ast.c4
-rw-r--r--fs/dlm/config.c79
-rw-r--r--fs/dlm/config.h2
-rw-r--r--fs/dlm/dlm_internal.h46
-rw-r--r--fs/dlm/lockspace.c15
-rw-r--r--fs/dlm/lowcomms.c215
-rw-r--r--fs/dlm/lowcomms.h2
-rw-r--r--fs/dlm/main.c2
-rw-r--r--fs/dlm/member.c17
-rw-r--r--fs/dlm/rcom.c2
-rw-r--r--fs/dlm/recoverd.c27
-rw-r--r--fs/dlm/recoverd.h1
-rw-r--r--fs/dlm/user.c7
-rw-r--r--fs/ext2/balloc.c2
-rw-r--r--fs/ext3/balloc.c2
-rw-r--r--fs/ext3/inode.c2
-rw-r--r--fs/ext4/inode.c14
-rw-r--r--fs/ext4/mballoc.c2
-rw-r--r--fs/fs-writeback.c4
-rw-r--r--fs/gfs2/aops.c11
-rw-r--r--fs/gfs2/bmap.c2
-rw-r--r--fs/gfs2/file.c4
-rw-r--r--fs/gfs2/glock.c60
-rw-r--r--fs/gfs2/glops.c1
-rw-r--r--fs/gfs2/incore.h30
-rw-r--r--fs/gfs2/inode.c20
-rw-r--r--fs/gfs2/ops_fstype.c8
-rw-r--r--fs/gfs2/quota.c11
-rw-r--r--fs/gfs2/rgrp.c1203
-rw-r--r--fs/gfs2/rgrp.h28
-rw-r--r--fs/gfs2/super.c9
-rw-r--r--fs/gfs2/trace_gfs2.h20
-rw-r--r--fs/gfs2/trans.h7
-rw-r--r--fs/gfs2/xattr.c96
-rw-r--r--fs/libfs.c2
-rw-r--r--fs/lockd/svclock.c3
-rw-r--r--fs/namespace.c10
-rw-r--r--fs/nfs/super.c2
-rw-r--r--fs/proc/proc_sysctl.c5
-rw-r--r--fs/stat.c4
-rw-r--r--fs/sysfs/symlink.c2
-rw-r--r--fs/xfs/xfs_buf.c5
-rw-r--r--fs/xfs/xfs_buf.h41
-rw-r--r--fs/xfs/xfs_super.c1
-rw-r--r--include/asm-generic/bitops/builtin-__ffs.h15
-rw-r--r--include/asm-generic/bitops/builtin-__fls.h15
-rw-r--r--include/asm-generic/bitops/builtin-ffs.h17
-rw-r--r--include/asm-generic/bitops/builtin-fls.h16
-rw-r--r--include/asm-generic/unistd.h4
-rw-r--r--include/clocksource/arm_generic.h21
-rw-r--r--include/linux/acpi.h6
-rw-r--r--include/linux/atmel_tc.h2
-rw-r--r--include/linux/bcd.h17
-rw-r--r--include/linux/compiler-gcc4.h7
-rw-r--r--include/linux/compiler.h4
-rw-r--r--include/linux/dcache.h2
-rw-r--r--include/linux/device.h85
-rw-r--r--include/linux/efi-bgrt.h21
-rw-r--r--include/linux/efi.h8
-rw-r--r--include/linux/elf.h4
-rw-r--r--include/linux/extcon.h18
-rw-r--r--include/linux/extcon/extcon-adc-jack.h71
-rw-r--r--include/linux/firmware.h15
-rw-r--r--include/linux/fsl_devices.h2
-rw-r--r--include/linux/ftrace.h158
-rw-r--r--include/linux/hardirq.h8
-rw-r--r--include/linux/hid-sensor-hub.h160
-rw-r--r--include/linux/hid-sensor-ids.h112
-rw-r--r--include/linux/hid.h5
-rw-r--r--include/linux/hyperv.h101
-rw-r--r--include/linux/iio/adc/ad_sigma_delta.h173
-rw-r--r--include/linux/iio/buffer.h6
-rw-r--r--include/linux/iio/consumer.h44
-rw-r--r--include/linux/iio/iio.h65
-rw-r--r--include/linux/iio/kfifo_buf.h3
-rw-r--r--include/linux/iio/machine.h5
-rw-r--r--include/linux/iio/trigger.h13
-rw-r--r--include/linux/iio/trigger_consumer.h11
-rw-r--r--include/linux/iio/types.h1
-rw-r--r--include/linux/input.h35
-rw-r--r--include/linux/input/mt.h57
-rw-r--r--include/linux/interrupt.h2
-rw-r--r--include/linux/iommu.h42
-rw-r--r--include/linux/irqdesc.h2
-rw-r--r--include/linux/kdb.h29
-rw-r--r--include/linux/kernel.h2
-rw-r--r--include/linux/kernel_stat.h8
-rw-r--r--include/linux/kgdb.h13
-rw-r--r--include/linux/kprobes.h27
-rw-r--r--include/linux/kthread.h11
-rw-r--r--include/linux/kvm_host.h4
-rw-r--r--include/linux/leds.h16
-rw-r--r--include/linux/memory.h2
-rw-r--r--include/linux/mfd/abx500/ab8500-codec.h6
-rw-r--r--include/linux/mfd/max77686.h1
-rw-r--r--include/linux/mfd/max8998.h2
-rw-r--r--include/linux/micrel_phy.h19
-rw-r--r--include/linux/mod_devicetable.h10
-rw-r--r--include/linux/netdevice.h3
-rw-r--r--include/linux/nvme.h2
-rw-r--r--include/linux/of_irq.h5
-rw-r--r--include/linux/pci-acpi.h17
-rw-r--r--include/linux/pci.h55
-rw-r--r--include/linux/pci_ids.h1
-rw-r--r--include/linux/pci_regs.h7
-rw-r--r--include/linux/pcieport_if.h2
-rw-r--r--include/linux/perf_event.h62
-rw-r--r--include/linux/perf_regs.h25
-rw-r--r--include/linux/platform_data/ad5755.h103
-rw-r--r--include/linux/platform_data/ad7791.h17
-rw-r--r--include/linux/platform_data/max197.h21
-rw-r--r--include/linux/platform_data/sht15.h (renamed from include/linux/sht15.h)1
-rw-r--r--include/linux/platform_device.h4
-rw-r--r--include/linux/pm.h5
-rw-r--r--include/linux/power/generic-adc-battery.h29
-rw-r--r--include/linux/rcupdate.h21
-rw-r--r--include/linux/regmap.h3
-rw-r--r--include/linux/regulator/consumer.h15
-rw-r--r--include/linux/regulator/driver.h18
-rw-r--r--include/linux/regulator/fan53555.h60
-rw-r--r--include/linux/regulator/machine.h2
-rw-r--r--include/linux/sched.h21
-rw-r--r--include/linux/screen_info.h2
-rw-r--r--include/linux/security.h1
-rw-r--r--include/linux/serial_core.h4
-rw-r--r--include/linux/smpboot.h43
-rw-r--r--include/linux/task_work.h3
-rw-r--r--include/linux/ti_wilink_st.h3
-rw-r--r--include/linux/timer.h165
-rw-r--r--include/linux/topology.h2
-rw-r--r--include/linux/tracepoint.h28
-rw-r--r--include/linux/uprobes.h15
-rw-r--r--include/linux/usb.h32
-rw-r--r--include/linux/usb/ch11.h4
-rw-r--r--include/linux/usb/composite.h97
-rw-r--r--include/linux/usb/ehci_def.h35
-rw-r--r--include/linux/usb/ehci_pdriver.h8
-rw-r--r--include/linux/usb/ezusb.h16
-rw-r--r--include/linux/usb/gadget.h33
-rw-r--r--include/linux/usb/hcd.h4
-rw-r--r--include/linux/usb/nop-usb-xceiv.h24
-rw-r--r--include/linux/usb/ohci_pdriver.h8
-rw-r--r--include/linux/usb/omap_usb.h46
-rw-r--r--include/linux/usb/otg.h236
-rw-r--r--include/linux/usb/phy.h233
-rw-r--r--include/linux/usb/phy_companion.h34
-rw-r--r--include/linux/usb/quirks.h4
-rw-r--r--include/linux/usb/serial.h32
-rw-r--r--include/linux/usb/tegra_usb_phy.h (renamed from arch/arm/mach-tegra/include/mach/usb_phy.h)16
-rw-r--r--include/linux/usb_usual.h24
-rw-r--r--include/linux/usbdevice_fs.h14
-rw-r--r--include/linux/w1-gpio.h1
-rw-r--r--include/linux/xfrm.h2
-rw-r--r--include/net/ip6_fib.h5
-rw-r--r--include/net/net_namespace.h10
-rw-r--r--include/net/netns/ipv4.h1
-rw-r--r--include/net/route.h2
-rw-r--r--include/net/sock.h2
-rw-r--r--include/trace/define_trace.h2
-rw-r--r--include/trace/events/kmem.h4
-rw-r--r--include/video/epson1355.h64
-rw-r--r--include/xen/grant_table.h3
-rw-r--r--include/xen/interface/physdev.h16
-rw-r--r--init/Kconfig186
-rw-r--r--init/main.c5
-rw-r--r--kernel/Kconfig.locks103
-rw-r--r--kernel/Makefile5
-rw-r--r--kernel/cpu.c21
-rw-r--r--kernel/debug/debug_core.c14
-rw-r--r--kernel/debug/kdb/kdb_main.c31
-rw-r--r--kernel/events/callchain.c38
-rw-r--r--kernel/events/core.c214
-rw-r--r--kernel/events/internal.h82
-rw-r--r--kernel/events/ring_buffer.c10
-rw-r--r--kernel/events/uprobes.c248
-rw-r--r--kernel/fork.c10
-rw-r--r--kernel/irq/chip.c1
-rw-r--r--kernel/irq/dummychip.c2
-rw-r--r--kernel/kprobes.c247
-rw-r--r--kernel/kthread.c185
-rw-r--r--kernel/lockdep.c39
-rw-r--r--kernel/pid_namespace.c6
-rw-r--r--kernel/rcupdate.c4
-rw-r--r--kernel/rcutiny.c33
-rw-r--r--kernel/rcutiny_plugin.h10
-rw-r--r--kernel/rcutorture.c159
-rw-r--r--kernel/rcutree.c916
-rw-r--r--kernel/rcutree.h50
-rw-r--r--kernel/rcutree_plugin.h597
-rw-r--r--kernel/rcutree_trace.c22
-rw-r--r--kernel/sched/Makefile2
-rw-r--r--kernel/sched/core.c694
-rw-r--r--kernel/sched/cputime.c530
-rw-r--r--kernel/sched/fair.c81
-rw-r--r--kernel/sched/features.h10
-rw-r--r--kernel/sched/rt.c5
-rw-r--r--kernel/sched/sched.h69
-rw-r--r--kernel/signal.c18
-rw-r--r--kernel/smpboot.c233
-rw-r--r--kernel/smpboot.h4
-rw-r--r--kernel/softirq.c117
-rw-r--r--kernel/sysctl.c8
-rw-r--r--kernel/task_work.c111
-rw-r--r--kernel/time/tick-sched.c6
-rw-r--r--kernel/time/timekeeping.c19
-rw-r--r--kernel/timer.c108
-rw-r--r--kernel/trace/Kconfig10
-rw-r--r--kernel/trace/Makefile8
-rw-r--r--kernel/trace/ftrace.c322
-rw-r--r--kernel/trace/ring_buffer.c4
-rw-r--r--kernel/trace/trace.c12
-rw-r--r--kernel/trace/trace.h3
-rw-r--r--kernel/trace/trace_event_perf.c3
-rw-r--r--kernel/trace/trace_events.c116
-rw-r--r--kernel/trace/trace_events_filter.c2
-rw-r--r--kernel/trace/trace_functions.c14
-rw-r--r--kernel/trace/trace_functions_graph.c5
-rw-r--r--kernel/trace/trace_irqsoff.c5
-rw-r--r--kernel/trace/trace_sched_wakeup.c5
-rw-r--r--kernel/trace/trace_selftest.c304
-rw-r--r--kernel/trace/trace_stack.c4
-rw-r--r--kernel/trace/trace_syscalls.c2
-rw-r--r--kernel/watchdog.c263
-rw-r--r--kernel/workqueue.c37
-rw-r--r--lib/Kconfig.debug20
-rw-r--r--lib/bcd.c8
-rw-r--r--lib/dynamic_debug.c56
-rw-r--r--lib/flex_proportions.c2
-rw-r--r--mm/bootmem.c2
-rw-r--r--mm/huge_memory.c1
-rw-r--r--mm/kmemleak.c6
-rw-r--r--mm/memory_hotplug.c16
-rw-r--r--mm/page_alloc.c2
-rw-r--r--mm/slab.c6
-rw-r--r--mm/slub.c15
-rw-r--r--mm/vmscan.c1
-rw-r--r--net/8021q/vlanproc.c2
-rw-r--r--net/batman-adv/bat_iv_ogm.c13
-rw-r--r--net/batman-adv/bitarray.h6
-rw-r--r--net/batman-adv/soft-interface.c7
-rw-r--r--net/bluetooth/bnep/sock.c4
-rw-r--r--net/bluetooth/cmtp/sock.c4
-rw-r--r--net/bluetooth/hci_core.c2
-rw-r--r--net/bluetooth/hci_sock.c16
-rw-r--r--net/bluetooth/hidp/sock.c4
-rw-r--r--net/bluetooth/l2cap_core.c2
-rw-r--r--net/bluetooth/mgmt.c16
-rw-r--r--net/ceph/messenger.c5
-rw-r--r--net/core/dev.c25
-rw-r--r--net/core/skbuff.c4
-rw-r--r--net/core/sock.c3
-rw-r--r--net/ipv4/arp.c2
-rw-r--r--net/ipv4/devinet.c10
-rw-r--r--net/ipv4/fib_frontend.c20
-rw-r--r--net/ipv4/fib_rules.c2
-rw-r--r--net/ipv4/fib_trie.c6
-rw-r--r--net/ipv4/inetpeer.c5
-rw-r--r--net/ipv4/raw.c14
-rw-r--r--net/ipv4/route.c43
-rw-r--r--net/ipv4/tcp.c23
-rw-r--r--net/ipv4/tcp_input.c5
-rw-r--r--net/ipv6/inet6_connection_sock.c23
-rw-r--r--net/ipv6/ip6_fib.c4
-rw-r--r--net/ipv6/mip6.c20
-rw-r--r--net/ipv6/raw.c21
-rw-r--r--net/ipv6/route.c19
-rw-r--r--net/l2tp/l2tp_netlink.c12
-rw-r--r--net/netfilter/xt_limit.c8
-rw-r--r--net/netrom/af_netrom.c2
-rw-r--r--net/sched/sch_qfq.c5
-rw-r--r--net/wireless/reg.c12
-rw-r--r--net/xfrm/xfrm_policy.c3
-rw-r--r--net/xfrm/xfrm_user.c57
-rw-r--r--scripts/Makefile.fwinst4
-rwxr-xr-xscripts/checksyscalls.sh2
-rw-r--r--scripts/coccinelle/api/memdup_user.cocci4
-rw-r--r--scripts/kconfig/streamline_config.pl50
-rw-r--r--scripts/mod/file2alias.c15
-rw-r--r--scripts/recordmcount.h4
-rw-r--r--scripts/sortextable.c10
-rw-r--r--security/apparmor/.gitignore1
-rw-r--r--security/keys/keyctl.c2
-rw-r--r--security/selinux/include/xfrm.h1
-rw-r--r--sound/oss/.gitignore1
-rw-r--r--sound/soc/codecs/ab8500-codec.c81
-rw-r--r--sound/soc/codecs/wm2000.c2
-rw-r--r--sound/soc/ux500/mop500.c47
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c6
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c89
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.h8
-rw-r--r--sound/usb/endpoint.c8
-rwxr-xr-xtools/hv/hv_get_dhcp_info.sh28
-rwxr-xr-xtools/hv/hv_get_dns_info.sh13
-rw-r--r--tools/hv/hv_kvp_daemon.c1036
-rwxr-xr-xtools/hv/hv_set_ifconfig.sh68
-rw-r--r--tools/lib/traceevent/Makefile2
-rw-r--r--tools/lib/traceevent/event-parse.c754
-rw-r--r--tools/lib/traceevent/event-parse.h46
-rw-r--r--tools/lib/traceevent/event-utils.h6
-rw-r--r--tools/perf/.gitignore2
-rw-r--r--tools/perf/Documentation/Makefile6
-rw-r--r--tools/perf/Documentation/jit-interface.txt15
-rw-r--r--tools/perf/Documentation/perf-annotate.txt3
-rw-r--r--tools/perf/Documentation/perf-diff.txt3
-rw-r--r--tools/perf/Documentation/perf-kvm.txt30
-rw-r--r--tools/perf/Documentation/perf-list.txt48
-rw-r--r--tools/perf/Documentation/perf-report.txt3
-rw-r--r--tools/perf/Documentation/perf-script-perl.txt4
-rw-r--r--tools/perf/Documentation/perf-script-python.txt10
-rw-r--r--tools/perf/Documentation/perf-trace.txt53
-rw-r--r--tools/perf/MANIFEST4
-rw-r--r--tools/perf/Makefile173
-rw-r--r--tools/perf/arch/x86/Makefile3
-rw-r--r--tools/perf/arch/x86/include/perf_regs.h80
-rw-r--r--tools/perf/arch/x86/util/unwind.c111
-rw-r--r--tools/perf/bash_completion26
-rw-r--r--tools/perf/bench/bench.h3
-rw-r--r--tools/perf/bench/mem-memcpy.c2
-rw-r--r--tools/perf/bench/mem-memset.c2
-rw-r--r--tools/perf/bench/sched-messaging.c2
-rw-r--r--tools/perf/bench/sched-pipe.c10
-rw-r--r--tools/perf/builtin-annotate.c4
-rw-r--r--tools/perf/builtin-bench.c2
-rw-r--r--tools/perf/builtin-buildid-cache.c10
-rw-r--r--tools/perf/builtin-buildid-list.c7
-rw-r--r--tools/perf/builtin-diff.c96
-rw-r--r--tools/perf/builtin-evlist.c2
-rw-r--r--tools/perf/builtin-help.c50
-rw-r--r--tools/perf/builtin-inject.c29
-rw-r--r--tools/perf/builtin-kmem.c234
-rw-r--r--tools/perf/builtin-kvm.c838
-rw-r--r--tools/perf/builtin-list.c16
-rw-r--r--tools/perf/builtin-lock.c414
-rw-r--r--tools/perf/builtin-probe.c24
-rw-r--r--tools/perf/builtin-record.c305
-rw-r--r--tools/perf/builtin-report.c47
-rw-r--r--tools/perf/builtin-sched.c1522
-rw-r--r--tools/perf/builtin-script.c229
-rw-r--r--tools/perf/builtin-stat.c136
-rw-r--r--tools/perf/builtin-test.c353
-rw-r--r--tools/perf/builtin-timechart.c70
-rw-r--r--tools/perf/builtin-top.c33
-rw-r--r--tools/perf/builtin-trace.c310
-rw-r--r--tools/perf/builtin.h2
-rw-r--r--tools/perf/command-list.txt3
-rw-r--r--tools/perf/config/feature-tests.mak50
-rw-r--r--tools/perf/perf-archive.sh6
-rw-r--r--tools/perf/perf.c75
-rw-r--r--tools/perf/perf.h15
-rwxr-xr-xtools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py94
-rw-r--r--tools/perf/scripts/python/bin/event_analyzing_sample-record8
-rw-r--r--tools/perf/scripts/python/bin/event_analyzing_sample-report3
-rw-r--r--tools/perf/scripts/python/event_analyzing_sample.py189
-rw-r--r--tools/perf/ui/browser.c7
-rw-r--r--tools/perf/ui/browsers/annotate.c6
-rw-r--r--tools/perf/ui/browsers/hists.c133
-rw-r--r--tools/perf/ui/gtk/browser.c111
-rw-r--r--tools/perf/ui/gtk/gtk.h3
-rw-r--r--tools/perf/ui/gtk/helpline.c56
-rw-r--r--tools/perf/ui/gtk/setup.c6
-rw-r--r--tools/perf/ui/gtk/util.c9
-rw-r--r--tools/perf/ui/helpline.c56
-rw-r--r--tools/perf/ui/helpline.h33
-rw-r--r--tools/perf/ui/hist.c390
-rw-r--r--tools/perf/ui/setup.c10
-rw-r--r--tools/perf/ui/stdio/hist.c498
-rw-r--r--tools/perf/ui/tui/helpline.c57
-rw-r--r--tools/perf/ui/tui/setup.c10
-rw-r--r--tools/perf/util/alias.c3
-rw-r--r--tools/perf/util/annotate.c19
-rw-r--r--tools/perf/util/annotate.h15
-rw-r--r--tools/perf/util/build-id.c11
-rw-r--r--tools/perf/util/cache.h6
-rw-r--r--tools/perf/util/callchain.c6
-rw-r--r--tools/perf/util/callchain.h2
-rw-r--r--tools/perf/util/cgroup.c4
-rw-r--r--tools/perf/util/config.c6
-rw-r--r--tools/perf/util/cpumap.c22
-rw-r--r--tools/perf/util/cpumap.h13
-rw-r--r--tools/perf/util/debug.c4
-rw-r--r--tools/perf/util/debug.h17
-rw-r--r--tools/perf/util/dso-test-data.c2
-rw-r--r--tools/perf/util/dwarf-aux.c2
-rw-r--r--tools/perf/util/event.c71
-rw-r--r--tools/perf/util/event.h14
-rw-r--r--tools/perf/util/evlist.c143
-rw-r--r--tools/perf/util/evlist.h35
-rw-r--r--tools/perf/util/evsel.c276
-rw-r--r--tools/perf/util/evsel.h63
-rwxr-xr-xtools/perf/util/generate-cmdlist.sh15
-rw-r--r--tools/perf/util/header.c1063
-rw-r--r--tools/perf/util/header.h29
-rw-r--r--tools/perf/util/help.c4
-rw-r--r--tools/perf/util/hist.c721
-rw-r--r--tools/perf/util/hist.h75
-rw-r--r--tools/perf/util/include/linux/bitops.h4
-rw-r--r--tools/perf/util/include/linux/compiler.h9
-rw-r--r--tools/perf/util/include/linux/kernel.h17
-rw-r--r--tools/perf/util/include/linux/magic.h12
-rw-r--r--tools/perf/util/include/linux/rbtree.h1
-rw-r--r--tools/perf/util/include/linux/string.h2
-rw-r--r--tools/perf/util/include/linux/types.h8
-rw-r--r--tools/perf/util/intlist.c8
-rw-r--r--tools/perf/util/map.c47
-rw-r--r--tools/perf/util/map.h9
-rw-r--r--tools/perf/util/parse-events-test.c426
-rw-r--r--tools/perf/util/parse-events.c254
-rw-r--r--tools/perf/util/parse-events.h18
-rw-r--r--tools/perf/util/parse-events.l56
-rw-r--r--tools/perf/util/parse-events.y125
-rw-r--r--tools/perf/util/parse-options.c3
-rw-r--r--tools/perf/util/perf_regs.h14
-rw-r--r--tools/perf/util/pmu.c80
-rw-r--r--tools/perf/util/pmu.h3
-rw-r--r--tools/perf/util/pmu.y6
-rw-r--r--tools/perf/util/probe-event.c69
-rw-r--r--tools/perf/util/probe-finder.c28
-rw-r--r--tools/perf/util/python-ext-sources2
-rw-r--r--tools/perf/util/python.c17
-rw-r--r--tools/perf/util/scripting-engines/trace-event-perl.c50
-rw-r--r--tools/perf/util/scripting-engines/trace-event-python.c113
-rw-r--r--tools/perf/util/session.c198
-rw-r--r--tools/perf/util/session.h10
-rw-r--r--tools/perf/util/sort.c25
-rw-r--r--tools/perf/util/sort.h2
-rw-r--r--tools/perf/util/stat.c57
-rw-r--r--tools/perf/util/stat.h16
-rw-r--r--tools/perf/util/string.c18
-rw-r--r--tools/perf/util/strlist.c2
-rw-r--r--tools/perf/util/symbol-elf.c841
-rw-r--r--tools/perf/util/symbol-minimal.c307
-rw-r--r--tools/perf/util/symbol.c942
-rw-r--r--tools/perf/util/symbol.h67
-rw-r--r--tools/perf/util/target.c4
-rw-r--r--tools/perf/util/thread.h2
-rw-r--r--tools/perf/util/top.c3
-rw-r--r--tools/perf/util/top.h1
-rw-r--r--tools/perf/util/trace-event-parse.c54
-rw-r--r--tools/perf/util/trace-event-scripting.c34
-rw-r--r--tools/perf/util/trace-event.h12
-rw-r--r--tools/perf/util/unwind.c571
-rw-r--r--tools/perf/util/unwind.h35
-rw-r--r--tools/perf/util/util.c25
-rw-r--r--tools/perf/util/util.h9
-rw-r--r--tools/perf/util/vdso.c111
-rw-r--r--tools/perf/util/vdso.h18
-rw-r--r--tools/perf/util/wrapper.c3
-rw-r--r--tools/scripts/Makefile.include6
-rw-r--r--tools/testing/ktest/examples/include/defaults.conf2
-rw-r--r--tools/testing/ktest/examples/include/tests.conf2
-rwxr-xr-xtools/testing/ktest/ktest.pl4
-rw-r--r--tools/testing/selftests/vm/run_vmtests6
-rw-r--r--tools/usb/testusb.c25
2885 files changed, 170865 insertions, 68267 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 5f75f8f7df34..b6fbe514a869 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -220,3 +220,10 @@ Description:
If the device doesn't support LTM, the file will read "no".
The file will be present for all speeds of USB devices, and will
always read "no" for USB 1.1 and USB 2.0 devices.
+
+What: /sys/bus/usb/devices/.../(hub interface)/portX
+Date: August 2012
+Contact: Lan Tianyu <tianyu.lan@intel.com>
+Description:
+ The /sys/bus/usb/devices/.../(hub interface)/portX
+ is usb port device's sysfs directory.
diff --git a/Documentation/ABI/testing/sysfs-class-extcon b/Documentation/ABI/testing/sysfs-class-extcon
index 20ab361bd8c6..57a726232912 100644
--- a/Documentation/ABI/testing/sysfs-class-extcon
+++ b/Documentation/ABI/testing/sysfs-class-extcon
@@ -13,7 +13,7 @@ Description:
accessory cables have such capability. For example,
the 30-pin port of Nuri board (/arch/arm/mach-exynos)
may have both HDMI and Charger attached, or analog audio,
- video, and USB cables attached simulteneously.
+ video, and USB cables attached simultaneously.
If there are cables mutually exclusive with each other,
such binary relations may be expressed with extcon_dev's
@@ -35,7 +35,7 @@ Description:
The /sys/class/extcon/.../state shows and stores the cable
attach/detach information of the corresponding extcon object.
If the extcon object has an optional callback "show_state"
- defined, the showing function is overriden with the optional
+ defined, the showing function is overridden with the optional
callback.
If the default callback for showing function is used, the
@@ -46,19 +46,19 @@ Description:
TA=1
EAR_JACK=0
#
- In this example, the extcon device have USB_OTG and TA
+ In this example, the extcon device has USB_OTG and TA
cables attached and HDMI and EAR_JACK cables detached.
In order to update the state of an extcon device, enter a hex
- state number starting with 0x.
- echo 0xHEX > state
+ state number starting with 0x:
+ # echo 0xHEX > state
- This updates the whole state of the extcon dev.
+ This updates the whole state of the extcon device.
Inputs of all the methods are required to meet the
- mutually_exclusive contidions if they exist.
+ mutually_exclusive conditions if they exist.
It is recommended to use this "global" state interface if
- you need to enter the value atomically. The later state
+ you need to set the value atomically. The later state
interface associated with each cable cannot update
multiple cable states of an extcon device simultaneously.
@@ -73,7 +73,7 @@ What: /sys/class/extcon/.../cable.x/state
Date: February 2012
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
- The /sys/class/extcon/.../cable.x/name shows and stores the
+ The /sys/class/extcon/.../cable.x/state shows and stores the
state of cable "x" (integer between 0 and 31) of an extcon
device. The state value is either 0 (detached) or 1
(attached).
@@ -83,8 +83,8 @@ Date: December 2011
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
Shows the relations of mutually exclusiveness. For example,
- if the mutually_exclusive array of extcon_dev is
- {0x3, 0x5, 0xC, 0x0}, the, the output is:
+ if the mutually_exclusive array of extcon device is
+ {0x3, 0x5, 0xC, 0x0}, then the output is:
# ls mutually_exclusive/
0x3
0x5
diff --git a/Documentation/ABI/testing/sysfs-class-regulator b/Documentation/ABI/testing/sysfs-class-regulator
index e091fa873792..bc578bc60628 100644
--- a/Documentation/ABI/testing/sysfs-class-regulator
+++ b/Documentation/ABI/testing/sysfs-class-regulator
@@ -349,3 +349,24 @@ Description:
This will be one of the same strings reported by
the "state" attribute.
+
+What: /sys/class/regulator/.../bypass
+Date: September 2012
+KernelVersion: 3.7
+Contact: Mark Brown <broonie@opensource.wolfsonmicro.com>
+Description:
+ Some regulator directories will contain a field called
+ bypass. This indicates if the device is in bypass mode.
+
+ This will be one of the following strings:
+
+ 'enabled'
+ 'disabled'
+ 'unknown'
+
+ 'enabled' means the regulator is in bypass mode.
+
+ 'disabled' means that the regulator is regulating.
+
+ 'unknown' means software cannot determine the state, or
+ the reported state is invalid.
diff --git a/Documentation/ABI/testing/sysfs-driver-wacom b/Documentation/ABI/testing/sysfs-driver-wacom
index 8d55a83d6921..7fc781048b79 100644
--- a/Documentation/ABI/testing/sysfs-driver-wacom
+++ b/Documentation/ABI/testing/sysfs-driver-wacom
@@ -1,3 +1,16 @@
+WWhat: /sys/class/hidraw/hidraw*/device/oled*_img
+Date: June 2012
+Contact: linux-bluetooth@vger.kernel.org
+Description:
+ The /sys/class/hidraw/hidraw*/device/oled*_img files control
+ OLED mocro displays on Intuos4 Wireless tablet. Accepted image
+ has to contain 256 bytes (64x32 px 1 bit colour). The format
+ is the same as PBM image 62x32px without header (64 bits per
+ horizontal line, 32 lines). An example of setting OLED No. 0:
+ dd bs=256 count=1 if=img_file of=[path to oled0_img]/oled0_img
+ The attribute is read only and no local copy of the image is
+ stored.
+
What: /sys/class/hidraw/hidraw*/device/speed
Date: April 2010
Kernel Version: 2.6.35
diff --git a/Documentation/RCU/checklist.txt b/Documentation/RCU/checklist.txt
index fc103d7a0474..cdb20d41a44a 100644
--- a/Documentation/RCU/checklist.txt
+++ b/Documentation/RCU/checklist.txt
@@ -310,6 +310,12 @@ over a rather long period of time, but improvements are always welcome!
code under the influence of preempt_disable(), you instead
need to use synchronize_irq() or synchronize_sched().
+ This same limitation also applies to synchronize_rcu_bh()
+ and synchronize_srcu(), as well as to the asynchronous and
+ expedited forms of the three primitives, namely call_rcu(),
+ call_rcu_bh(), call_srcu(), synchronize_rcu_expedited(),
+ synchronize_rcu_bh_expedited(), and synchronize_srcu_expedited().
+
12. Any lock acquired by an RCU callback must be acquired elsewhere
with softirq disabled, e.g., via spin_lock_irqsave(),
spin_lock_bh(), etc. Failing to disable irq on a given
diff --git a/Documentation/RCU/stallwarn.txt b/Documentation/RCU/stallwarn.txt
index 523364e4e1f1..1927151b386b 100644
--- a/Documentation/RCU/stallwarn.txt
+++ b/Documentation/RCU/stallwarn.txt
@@ -99,7 +99,7 @@ In kernels with CONFIG_RCU_FAST_NO_HZ, even more information is
printed:
INFO: rcu_preempt detected stall on CPU
- 0: (64628 ticks this GP) idle=dd5/3fffffffffffffff/0 drain=0 . timer=-1
+ 0: (64628 ticks this GP) idle=dd5/3fffffffffffffff/0 drain=0 . timer not pending
(t=65000 jiffies)
The "(64628 ticks this GP)" indicates that this CPU has taken more
@@ -116,13 +116,13 @@ number between the two "/"s is the value of the nesting, which will
be a small positive number if in the idle loop and a very large positive
number (as shown above) otherwise.
-For CONFIG_RCU_FAST_NO_HZ kernels, the "drain=0" indicates that the
-CPU is not in the process of trying to force itself into dyntick-idle
-state, the "." indicates that the CPU has not given up forcing RCU
-into dyntick-idle mode (it would be "H" otherwise), and the "timer=-1"
-indicates that the CPU has not recented forced RCU into dyntick-idle
-mode (it would otherwise indicate the number of microseconds remaining
-in this forced state).
+For CONFIG_RCU_FAST_NO_HZ kernels, the "drain=0" indicates that the CPU is
+not in the process of trying to force itself into dyntick-idle state, the
+"." indicates that the CPU has not given up forcing RCU into dyntick-idle
+mode (it would be "H" otherwise), and the "timer not pending" indicates
+that the CPU has not recently forced RCU into dyntick-idle mode (it
+would otherwise indicate the number of microseconds remaining in this
+forced state).
Multiple Warnings From One Stall
diff --git a/Documentation/RCU/trace.txt b/Documentation/RCU/trace.txt
index f6f15ce39903..672d19083252 100644
--- a/Documentation/RCU/trace.txt
+++ b/Documentation/RCU/trace.txt
@@ -333,23 +333,23 @@ o Each element of the form "1/1 0:127 ^0" represents one struct
The output of "cat rcu/rcu_pending" looks as follows:
rcu_sched:
- 0 np=255892 qsp=53936 rpq=85 cbr=0 cng=14417 gpc=10033 gps=24320 nf=6445 nn=146741
- 1 np=261224 qsp=54638 rpq=33 cbr=0 cng=25723 gpc=16310 gps=2849 nf=5912 nn=155792
- 2 np=237496 qsp=49664 rpq=23 cbr=0 cng=2762 gpc=45478 gps=1762 nf=1201 nn=136629
- 3 np=236249 qsp=48766 rpq=98 cbr=0 cng=286 gpc=48049 gps=1218 nf=207 nn=137723
- 4 np=221310 qsp=46850 rpq=7 cbr=0 cng=26 gpc=43161 gps=4634 nf=3529 nn=123110
- 5 np=237332 qsp=48449 rpq=9 cbr=0 cng=54 gpc=47920 gps=3252 nf=201 nn=137456
- 6 np=219995 qsp=46718 rpq=12 cbr=0 cng=50 gpc=42098 gps=6093 nf=4202 nn=120834
- 7 np=249893 qsp=49390 rpq=42 cbr=0 cng=72 gpc=38400 gps=17102 nf=41 nn=144888
+ 0 np=255892 qsp=53936 rpq=85 cbr=0 cng=14417 gpc=10033 gps=24320 nn=146741
+ 1 np=261224 qsp=54638 rpq=33 cbr=0 cng=25723 gpc=16310 gps=2849 nn=155792
+ 2 np=237496 qsp=49664 rpq=23 cbr=0 cng=2762 gpc=45478 gps=1762 nn=136629
+ 3 np=236249 qsp=48766 rpq=98 cbr=0 cng=286 gpc=48049 gps=1218 nn=137723
+ 4 np=221310 qsp=46850 rpq=7 cbr=0 cng=26 gpc=43161 gps=4634 nn=123110
+ 5 np=237332 qsp=48449 rpq=9 cbr=0 cng=54 gpc=47920 gps=3252 nn=137456
+ 6 np=219995 qsp=46718 rpq=12 cbr=0 cng=50 gpc=42098 gps=6093 nn=120834
+ 7 np=249893 qsp=49390 rpq=42 cbr=0 cng=72 gpc=38400 gps=17102 nn=144888
rcu_bh:
- 0 np=146741 qsp=1419 rpq=6 cbr=0 cng=6 gpc=0 gps=0 nf=2 nn=145314
- 1 np=155792 qsp=12597 rpq=3 cbr=0 cng=0 gpc=4 gps=8 nf=3 nn=143180
- 2 np=136629 qsp=18680 rpq=1 cbr=0 cng=0 gpc=7 gps=6 nf=0 nn=117936
- 3 np=137723 qsp=2843 rpq=0 cbr=0 cng=0 gpc=10 gps=7 nf=0 nn=134863
- 4 np=123110 qsp=12433 rpq=0 cbr=0 cng=0 gpc=4 gps=2 nf=0 nn=110671
- 5 np=137456 qsp=4210 rpq=1 cbr=0 cng=0 gpc=6 gps=5 nf=0 nn=133235
- 6 np=120834 qsp=9902 rpq=2 cbr=0 cng=0 gpc=6 gps=3 nf=2 nn=110921
- 7 np=144888 qsp=26336 rpq=0 cbr=0 cng=0 gpc=8 gps=2 nf=0 nn=118542
+ 0 np=146741 qsp=1419 rpq=6 cbr=0 cng=6 gpc=0 gps=0 nn=145314
+ 1 np=155792 qsp=12597 rpq=3 cbr=0 cng=0 gpc=4 gps=8 nn=143180
+ 2 np=136629 qsp=18680 rpq=1 cbr=0 cng=0 gpc=7 gps=6 nn=117936
+ 3 np=137723 qsp=2843 rpq=0 cbr=0 cng=0 gpc=10 gps=7 nn=134863
+ 4 np=123110 qsp=12433 rpq=0 cbr=0 cng=0 gpc=4 gps=2 nn=110671
+ 5 np=137456 qsp=4210 rpq=1 cbr=0 cng=0 gpc=6 gps=5 nn=133235
+ 6 np=120834 qsp=9902 rpq=2 cbr=0 cng=0 gpc=6 gps=3 nn=110921
+ 7 np=144888 qsp=26336 rpq=0 cbr=0 cng=0 gpc=8 gps=2 nn=118542
As always, this is once again split into "rcu_sched" and "rcu_bh"
portions, with CONFIG_TREE_PREEMPT_RCU kernels having an additional
@@ -377,17 +377,6 @@ o "gpc" is the number of times that an old grace period had
o "gps" is the number of times that a new grace period had started,
but this CPU was not yet aware of it.
-o "nf" is the number of times that this CPU suspected that the
- current grace period had run for too long, and thus needed to
- be forced.
-
- Please note that "forcing" consists of sending resched IPIs
- to holdout CPUs. If that CPU really still is in an old RCU
- read-side critical section, then we really do have to wait for it.
- The assumption behing "forcing" is that the CPU is not still in
- an old RCU read-side critical section, but has not yet responded
- for some other reason.
-
o "nn" is the number of times that this CPU needed nothing. Alert
readers will note that the rcu "nn" number for a given CPU very
closely matches the rcu_bh "np" number for that same CPU. This
diff --git a/Documentation/RCU/whatisRCU.txt b/Documentation/RCU/whatisRCU.txt
index 69ee188515e7..bf0f6de2aa00 100644
--- a/Documentation/RCU/whatisRCU.txt
+++ b/Documentation/RCU/whatisRCU.txt
@@ -873,7 +873,7 @@ d. Do you need to treat NMI handlers, hardirq handlers,
and code segments with preemption disabled (whether
via preempt_disable(), local_irq_save(), local_bh_disable(),
or some other mechanism) as if they were explicit RCU readers?
- If so, you need RCU-sched.
+ If so, RCU-sched is the only choice that will work for you.
e. Do you need RCU grace periods to complete even in the face
of softirq monopolization of one or more of the CPUs? For
@@ -884,7 +884,12 @@ f. Is your workload too update-intensive for normal use of
RCU, but inappropriate for other synchronization mechanisms?
If so, consider SLAB_DESTROY_BY_RCU. But please be careful!
-g. Otherwise, use RCU.
+g. Do you need read-side critical sections that are respected
+ even though they are in the middle of the idle loop, during
+ user-mode execution, or on an offlined CPU? If so, SRCU is the
+ only choice that will work for you.
+
+h. Otherwise, use RCU.
Of course, this all assumes that you have determined that RCU is in fact
the right tool for your job.
diff --git a/Documentation/accounting/getdelays.c b/Documentation/accounting/getdelays.c
index f6318f6d7baf..6f706aca2049 100644
--- a/Documentation/accounting/getdelays.c
+++ b/Documentation/accounting/getdelays.c
@@ -98,10 +98,9 @@ static int create_nl_socket(int protocol)
if (rcvbufsz)
if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
&rcvbufsz, sizeof(rcvbufsz)) < 0) {
- fprintf(stderr, "Unable to set socket rcv buf size "
- "to %d\n",
+ fprintf(stderr, "Unable to set socket rcv buf size to %d\n",
rcvbufsz);
- return -1;
+ goto error;
}
memset(&local, 0, sizeof(local));
diff --git a/Documentation/arm/Marvell/README b/Documentation/arm/Marvell/README
new file mode 100644
index 000000000000..8f08a86e03b7
--- /dev/null
+++ b/Documentation/arm/Marvell/README
@@ -0,0 +1,232 @@
+ARM Marvell SoCs
+================
+
+This document lists all the ARM Marvell SoCs that are currently
+supported in mainline by the Linux kernel. As the Marvell families of
+SoCs are large and complex, it is hard to understand where the support
+for a particular SoC is available in the Linux kernel. This document
+tries to help in understanding where those SoCs are supported, and to
+match them with their corresponding public datasheet, when available.
+
+Orion family
+------------
+
+ Flavors:
+ 88F5082
+ 88F5181
+ 88F5181L
+ 88F5182
+ Datasheet : http://www.embeddedarm.com/documentation/third-party/MV88F5182-datasheet.pdf
+ Programmer's User Guide : http://www.embeddedarm.com/documentation/third-party/MV88F5182-opensource-manual.pdf
+ User Manual : http://www.embeddedarm.com/documentation/third-party/MV88F5182-usermanual.pdf
+ 88F5281
+ Datasheet : http://www.ocmodshop.com/images/reviews/networking/qnap_ts409u/marvel_88f5281_data_sheet.pdf
+ 88F6183
+ Core: Feroceon ARMv5 compatible
+ Linux kernel mach directory: arch/arm/mach-orion5x
+ Linux kernel plat directory: arch/arm/plat-orion
+
+Kirkwood family
+---------------
+
+ Flavors:
+ 88F6282 a.k.a Armada 300
+ Product Brief : http://www.marvell.com/embedded-processors/armada-300/assets/armada_310.pdf
+ 88F6283 a.k.a Armada 310
+ Product Brief : http://www.marvell.com/embedded-processors/armada-300/assets/armada_310.pdf
+ 88F6190
+ Product Brief : http://www.marvell.com/embedded-processors/kirkwood/assets/88F6190-003_WEB.pdf
+ Hardware Spec : http://www.marvell.com/embedded-processors/kirkwood/assets/HW_88F619x_OpenSource.pdf
+ Functional Spec: http://www.marvell.com/embedded-processors/kirkwood/assets/FS_88F6180_9x_6281_OpenSource.pdf
+ 88F6192
+ Product Brief : http://www.marvell.com/embedded-processors/kirkwood/assets/88F6192-003_ver1.pdf
+ Hardware Spec : http://www.marvell.com/embedded-processors/kirkwood/assets/HW_88F619x_OpenSource.pdf
+ Functional Spec: http://www.marvell.com/embedded-processors/kirkwood/assets/FS_88F6180_9x_6281_OpenSource.pdf
+ 88F6182
+ 88F6180
+ Product Brief : http://www.marvell.com/embedded-processors/kirkwood/assets/88F6180-003_ver1.pdf
+ Hardware Spec : http://www.marvell.com/embedded-processors/kirkwood/assets/HW_88F6180_OpenSource.pdf
+ Functional Spec: http://www.marvell.com/embedded-processors/kirkwood/assets/FS_88F6180_9x_6281_OpenSource.pdf
+ 88F6281
+ Product Brief : http://www.marvell.com/embedded-processors/kirkwood/assets/88F6281-004_ver1.pdf
+ Hardware Spec : http://www.marvell.com/embedded-processors/kirkwood/assets/HW_88F6281_OpenSource.pdf
+ Functional Spec: http://www.marvell.com/embedded-processors/kirkwood/assets/FS_88F6180_9x_6281_OpenSource.pdf
+ Homepage: http://www.marvell.com/embedded-processors/kirkwood/
+ Core: Feroceon ARMv5 compatible
+ Linux kernel mach directory: arch/arm/mach-kirkwood
+ Linux kernel plat directory: arch/arm/plat-orion
+
+Discovery family
+----------------
+
+ Flavors:
+ MV78100
+ Product Brief : http://www.marvell.com/embedded-processors/discovery-innovation/assets/MV78100-003_WEB.pdf
+ Hardware Spec : http://www.marvell.com/embedded-processors/discovery-innovation/assets/HW_MV78100_OpenSource.pdf
+ Functional Spec: http://www.marvell.com/embedded-processors/discovery-innovation/assets/FS_MV76100_78100_78200_OpenSource.pdf
+ MV78200
+ Product Brief : http://www.marvell.com/embedded-processors/discovery-innovation/assets/MV78200-002_WEB.pdf
+ Hardware Spec : http://www.marvell.com/embedded-processors/discovery-innovation/assets/HW_MV78200_OpenSource.pdf
+ Functional Spec: http://www.marvell.com/embedded-processors/discovery-innovation/assets/FS_MV76100_78100_78200_OpenSource.pdf
+ MV76100
+ Not supported by the Linux kernel.
+
+ Core: Feroceon ARMv5 compatible
+
+ Linux kernel mach directory: arch/arm/mach-mv78xx0
+ Linux kernel plat directory: arch/arm/plat-orion
+
+EBU Armada family
+-----------------
+
+ Armada 370 Flavors:
+ 88F6710
+ 88F6707
+ 88F6W11
+
+ Armada XP Flavors:
+ MV78230
+ MV78260
+ MV78460
+
+ 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
+
+ Linux kernel mach directory: arch/arm/mach-mvebu
+ Linux kernel plat directory: none
+
+Avanta family
+-------------
+
+ Flavors:
+ 88F6510
+ 88F6530P
+ 88F6550
+ 88F6560
+ Homepage : http://www.marvell.com/broadband/
+ Product Brief: http://www.marvell.com/broadband/assets/Marvell_Avanta_88F6510_305_060-001_product_brief.pdf
+ No public datasheet available.
+
+ Core: ARMv5 compatible
+
+ Linux kernel mach directory: no code in mainline yet, planned for the future
+ Linux kernel plat directory: no code in mainline yet, planned for the future
+
+Dove family (application processor)
+-----------------------------------
+
+ Flavors:
+ 88AP510 a.k.a Armada 510
+ Product Brief : http://www.marvell.com/application-processors/armada-500/assets/Marvell_Armada510_SoC.pdf
+ Hardware Spec : http://www.marvell.com/application-processors/armada-500/assets/Armada-510-Hardware-Spec.pdf
+ Functional Spec : http://www.marvell.com/application-processors/armada-500/assets/Armada-510-Functional-Spec.pdf
+ Homepage: http://www.marvell.com/application-processors/armada-500/
+ Core: ARMv7 compatible
+ Directory: arch/arm/mach-dove
+
+PXA 2xx/3xx/93x/95x family
+--------------------------
+
+ Flavors:
+ PXA21x, PXA25x, PXA26x
+ Application processor only
+ Core: ARMv5 XScale core
+ PXA270, PXA271, PXA272
+ Product Brief : http://www.marvell.com/application-processors/pxa-family/assets/pxa_27x_pb.pdf
+ Design guide : http://www.marvell.com/application-processors/pxa-family/assets/pxa_27x_design_guide.pdf
+ Developers manual : http://www.marvell.com/application-processors/pxa-family/assets/pxa_27x_dev_man.pdf
+ Specification : http://www.marvell.com/application-processors/pxa-family/assets/pxa_27x_emts.pdf
+ Specification update : http://www.marvell.com/application-processors/pxa-family/assets/pxa_27x_spec_update.pdf
+ Application processor only
+ Core: ARMv5 XScale core
+ PXA300, PXA310, PXA320
+ PXA 300 Product Brief : http://www.marvell.com/application-processors/pxa-family/assets/PXA300_PB_R4.pdf
+ PXA 310 Product Brief : http://www.marvell.com/application-processors/pxa-family/assets/PXA310_PB_R4.pdf
+ PXA 320 Product Brief : http://www.marvell.com/application-processors/pxa-family/assets/PXA320_PB_R4.pdf
+ Design guide : http://www.marvell.com/application-processors/pxa-family/assets/PXA3xx_Design_Guide.pdf
+ Developers manual : http://www.marvell.com/application-processors/pxa-family/assets/PXA3xx_Developers_Manual.zip
+ Specifications : http://www.marvell.com/application-processors/pxa-family/assets/PXA3xx_EMTS.pdf
+ Specification Update : http://www.marvell.com/application-processors/pxa-family/assets/PXA3xx_Spec_Update.zip
+ Reference Manual : http://www.marvell.com/application-processors/pxa-family/assets/PXA3xx_TavorP_BootROM_Ref_Manual.pdf
+ Application processor only
+ Core: ARMv5 XScale core
+ PXA930, PXA935
+ Application processor with Communication processor
+ Core: ARMv5 XScale core
+ PXA955
+ Application processor with Communication processor
+ Core: ARMv7 compatible Sheeva PJ4 core
+
+ Comments:
+
+ * This line of SoCs originates from the XScale family developed by
+ Intel and acquired by Marvell in ~2006. The PXA21x, PXA25x,
+ PXA26x, PXA27x, PXA3xx and PXA93x were developed by Intel, while
+ the later PXA95x were developed by Marvell.
+
+ * Due to their XScale origin, these SoCs have virtually nothing in
+ common with the other (Kirkwood, Dove, etc.) families of Marvell
+ SoCs, except with the MMP/MMP2 family of SoCs.
+
+ Linux kernel mach directory: arch/arm/mach-pxa
+ Linux kernel plat directory: arch/arm/plat-pxa
+
+MMP/MMP2 family (communication processor)
+-----------------------------------------
+
+ Flavors:
+ PXA168, a.k.a Armada 168
+ Homepage : http://www.marvell.com/application-processors/armada-100/armada-168.jsp
+ Product brief : http://www.marvell.com/application-processors/armada-100/assets/pxa_168_pb.pdf
+ Hardware manual : http://www.marvell.com/application-processors/armada-100/assets/armada_16x_datasheet.pdf
+ Software manual : http://www.marvell.com/application-processors/armada-100/assets/armada_16x_software_manual.pdf
+ Specification update : http://www.marvell.com/application-processors/armada-100/assets/ARMADA16x_Spec_update.pdf
+ Boot ROM manual : http://www.marvell.com/application-processors/armada-100/assets/armada_16x_ref_manual.pdf
+ App node package : http://www.marvell.com/application-processors/armada-100/assets/armada_16x_app_note_package.pdf
+ Application processor only
+ Core: ARMv5 compatible Marvell PJ1 (Mohawk)
+ PXA910
+ Homepage : http://www.marvell.com/communication-processors/pxa910/
+ Product Brief : http://www.marvell.com/communication-processors/pxa910/assets/Marvell_PXA910_Platform-001_PB_final.pdf
+ Application processor with Communication processor
+ Core: ARMv5 compatible Marvell PJ1 (Mohawk)
+ MMP2, a.k.a Armada 610
+ Product Brief : http://www.marvell.com/application-processors/armada-600/assets/armada610_pb.pdf
+ Application processor only
+ Core: ARMv7 compatible Sheeva PJ4 core
+
+ Comments:
+
+ * This line of SoCs originates from the XScale family developed by
+ Intel and acquired by Marvell in ~2006. All the processors of
+ this MMP/MMP2 family were developed by Marvell.
+
+ * Due to their XScale origin, these SoCs have virtually nothing in
+ common with the other (Kirkwood, Dove, etc.) families of Marvell
+ SoCs, except with the PXA family of SoCs listed above.
+
+ Linux kernel mach directory: arch/arm/mach-mmp
+ Linux kernel plat directory: arch/arm/plat-pxa
+
+Long-term plans
+---------------
+
+ * Unify the mach-dove/, mach-mv78xx0/, mach-orion5x/ and
+ mach-kirkwood/ into the mach-mvebu/ to support all SoCs from the
+ Marvell EBU (Engineering Business Unit) in a single mach-<foo>
+ directory. The plat-orion/ would therefore disappear.
+
+ * Unify the mach-mmp/ and mach-pxa/ into the same mach-pxa
+ directory. The plat-pxa/ would therefore disappear.
+
+Credits
+-------
+
+ Maen Suleiman <maen@marvell.com>
+ Lior Amsalem <alior@marvell.com>
+ Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ Andrew Lunn <andrew@lunn.ch>
+ Nicolas Pitre <nico@fluxnic.net>
+ Eric Miao <eric.y.miao@gmail.com>
diff --git a/Documentation/arm64/booting.txt b/Documentation/arm64/booting.txt
new file mode 100644
index 000000000000..9c4d388daddc
--- /dev/null
+++ b/Documentation/arm64/booting.txt
@@ -0,0 +1,152 @@
+ Booting AArch64 Linux
+ =====================
+
+Author: Will Deacon <will.deacon@arm.com>
+Date : 07 September 2012
+
+This document is based on the ARM booting document by Russell King and
+is relevant to all public releases of the AArch64 Linux kernel.
+
+The AArch64 exception model is made up of a number of exception levels
+(EL0 - EL3), with EL0 and EL1 having a secure and a non-secure
+counterpart. EL2 is the hypervisor level and exists only in non-secure
+mode. EL3 is the highest priority level and exists only in secure mode.
+
+For the purposes of this document, we will use the term `boot loader'
+simply to define all software that executes on the CPU(s) before control
+is passed to the Linux kernel. This may include secure monitor and
+hypervisor code, or it may just be a handful of instructions for
+preparing a minimal boot environment.
+
+Essentially, the boot loader should provide (as a minimum) the
+following:
+
+1. Setup and initialise the RAM
+2. Setup the device tree
+3. Decompress the kernel image
+4. Call the kernel image
+
+
+1. Setup and initialise RAM
+---------------------------
+
+Requirement: MANDATORY
+
+The boot loader is expected to find and initialise all RAM that the
+kernel will use for volatile data storage in the system. It performs
+this in a machine dependent manner. (It may use internal algorithms
+to automatically locate and size all RAM, or it may use knowledge of
+the RAM in the machine, or any other method the boot loader designer
+sees fit.)
+
+
+2. Setup the device tree
+-------------------------
+
+Requirement: MANDATORY
+
+The device tree blob (dtb) must be no bigger than 2 megabytes in size
+and placed at a 2-megabyte boundary within the first 512 megabytes from
+the start of the kernel image. This is to allow the kernel to map the
+blob using a single section mapping in the initial page tables.
+
+
+3. Decompress the kernel image
+------------------------------
+
+Requirement: OPTIONAL
+
+The AArch64 kernel does not currently provide a decompressor and
+therefore requires decompression (gzip etc.) to be performed by the boot
+loader if a compressed Image target (e.g. Image.gz) is used. For
+bootloaders that do not implement this requirement, the uncompressed
+Image target is available instead.
+
+
+4. Call the kernel image
+------------------------
+
+Requirement: MANDATORY
+
+The decompressed kernel image contains a 32-byte header as follows:
+
+ u32 magic = 0x14000008; /* branch to stext, little-endian */
+ u32 res0 = 0; /* reserved */
+ u64 text_offset; /* Image load offset */
+ u64 res1 = 0; /* reserved */
+ u64 res2 = 0; /* reserved */
+
+The image must be placed at the specified offset (currently 0x80000)
+from the start of the system RAM and called there. The start of the
+system RAM must be aligned to 2MB.
+
+Before jumping into the kernel, the following conditions must be met:
+
+- Quiesce all DMA capable devices so that memory does not get
+ corrupted by bogus network packets or disk data. This will save
+ you many hours of debug.
+
+- Primary CPU general-purpose register settings
+ x0 = physical address of device tree blob (dtb) in system RAM.
+ x1 = 0 (reserved for future use)
+ x2 = 0 (reserved for future use)
+ x3 = 0 (reserved for future use)
+
+- CPU mode
+ All forms of interrupts must be masked in PSTATE.DAIF (Debug, SError,
+ IRQ and FIQ).
+ The CPU must be in either EL2 (RECOMMENDED in order to have access to
+ the virtualisation extensions) or non-secure EL1.
+
+- Caches, MMUs
+ The MMU must be off.
+ Instruction cache may be on or off.
+ Data cache must be off and invalidated.
+ External caches (if present) must be configured and disabled.
+
+- Architected timers
+ CNTFRQ must be programmed with the timer frequency.
+ If entering the kernel at EL1, CNTHCTL_EL2 must have EL1PCTEN (bit 0)
+ set where available.
+
+- Coherency
+ All CPUs to be booted by the kernel must be part of the same coherency
+ domain on entry to the kernel. This may require IMPLEMENTATION DEFINED
+ initialisation to enable the receiving of maintenance operations on
+ each CPU.
+
+- System registers
+ All writable architected system registers at the exception level where
+ the kernel image will be entered must be initialised by software at a
+ higher exception level to prevent execution in an UNKNOWN state.
+
+The boot loader is expected to enter the kernel on each CPU in the
+following manner:
+
+- The primary CPU must jump directly to the first instruction of the
+ kernel image. The device tree blob passed by this CPU must contain
+ for each CPU node:
+
+ 1. An 'enable-method' property. Currently, the only supported value
+ for this field is the string "spin-table".
+
+ 2. A 'cpu-release-addr' property identifying a 64-bit,
+ zero-initialised memory location.
+
+ It is expected that the bootloader will generate these device tree
+ properties and insert them into the blob prior to kernel entry.
+
+- Any secondary CPUs must spin outside of the kernel in a reserved area
+ of memory (communicated to the kernel by a /memreserve/ region in the
+ device tree) polling their cpu-release-addr location, which must be
+ contained in the reserved region. A wfe instruction may be inserted
+ to reduce the overhead of the busy-loop and a sev will be issued by
+ the primary CPU. When a read of the location pointed to by the
+ cpu-release-addr returns a non-zero value, the CPU must jump directly
+ to this value.
+
+- Secondary CPU general-purpose register settings
+ x0 = 0 (reserved for future use)
+ x1 = 0 (reserved for future use)
+ x2 = 0 (reserved for future use)
+ x3 = 0 (reserved for future use)
diff --git a/Documentation/arm64/memory.txt b/Documentation/arm64/memory.txt
new file mode 100644
index 000000000000..dbbdcbba75a3
--- /dev/null
+++ b/Documentation/arm64/memory.txt
@@ -0,0 +1,73 @@
+ Memory Layout on AArch64 Linux
+ ==============================
+
+Author: Catalin Marinas <catalin.marinas@arm.com>
+Date : 20 February 2012
+
+This document describes the virtual memory layout used by the AArch64
+Linux kernel. The architecture allows up to 4 levels of translation
+tables with a 4KB page size and up to 3 levels with a 64KB page size.
+
+AArch64 Linux uses 3 levels of translation tables with the 4KB page
+configuration, allowing 39-bit (512GB) virtual addresses for both user
+and kernel. With 64KB pages, only 2 levels of translation tables are
+used but the memory layout is the same.
+
+User addresses have bits 63:39 set to 0 while the kernel addresses have
+the same bits set to 1. TTBRx selection is given by bit 63 of the
+virtual address. The swapper_pg_dir contains only kernel (global)
+mappings while the user pgd contains only user (non-global) mappings.
+The swapper_pgd_dir address is written to TTBR1 and never written to
+TTBR0.
+
+
+AArch64 Linux memory layout:
+
+Start End Size Use
+-----------------------------------------------------------------------
+0000000000000000 0000007fffffffff 512GB user
+
+ffffff8000000000 ffffffbbfffcffff ~240GB vmalloc
+
+ffffffbbfffd0000 ffffffbcfffdffff 64KB [guard page]
+
+ffffffbbfffe0000 ffffffbcfffeffff 64KB PCI I/O space
+
+ffffffbbffff0000 ffffffbcffffffff 64KB [guard page]
+
+ffffffbc00000000 ffffffbdffffffff 8GB vmemmap
+
+ffffffbe00000000 ffffffbffbffffff ~8GB [guard, future vmmemap]
+
+ffffffbffc000000 ffffffbfffffffff 64MB modules
+
+ffffffc000000000 ffffffffffffffff 256GB memory
+
+
+Translation table lookup with 4KB pages:
+
++--------+--------+--------+--------+--------+--------+--------+--------+
+|63 56|55 48|47 40|39 32|31 24|23 16|15 8|7 0|
++--------+--------+--------+--------+--------+--------+--------+--------+
+ | | | | | |
+ | | | | | v
+ | | | | | [11:0] in-page offset
+ | | | | +-> [20:12] L3 index
+ | | | +-----------> [29:21] L2 index
+ | | +---------------------> [38:30] L1 index
+ | +-------------------------------> [47:39] L0 index (not used)
+ +-------------------------------------------------> [63] TTBR0/1
+
+
+Translation table lookup with 64KB pages:
+
++--------+--------+--------+--------+--------+--------+--------+--------+
+|63 56|55 48|47 40|39 32|31 24|23 16|15 8|7 0|
++--------+--------+--------+--------+--------+--------+--------+--------+
+ | | | | |
+ | | | | v
+ | | | | [15:0] in-page offset
+ | | | +----------> [28:16] L3 index
+ | | +--------------------------> [41:29] L2 index (only 38:29 used)
+ | +-------------------------------> [47:42] L1 index (not used)
+ +-------------------------------------------------> [63] TTBR0/1
diff --git a/Documentation/devicetree/bindings/arm/vt8500.txt b/Documentation/devicetree/bindings/arm/vt8500.txt
new file mode 100644
index 000000000000..d657832c6819
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/vt8500.txt
@@ -0,0 +1,14 @@
+VIA/Wondermedia VT8500 Platforms Device Tree Bindings
+---------------------------------------
+
+Boards with the VIA VT8500 SoC shall have the following properties:
+Required root node property:
+compatible = "via,vt8500";
+
+Boards with the Wondermedia WM8505 SoC shall have the following properties:
+Required root node property:
+compatible = "wm,wm8505";
+
+Boards with the Wondermedia WM8650 SoC shall have the following properties:
+Required root node property:
+compatible = "wm,wm8650";
diff --git a/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-intc.txt b/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-intc.txt
new file mode 100644
index 000000000000..0a4ce1051b02
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-intc.txt
@@ -0,0 +1,16 @@
+VIA/Wondermedia VT8500 Interrupt Controller
+-----------------------------------------------------
+
+Required properties:
+- compatible : "via,vt8500-intc"
+- reg : Should contain 1 register ranges(address and length)
+- #interrupt-cells : should be <1>
+
+Example:
+
+ intc: interrupt-controller@d8140000 {
+ compatible = "via,vt8500-intc";
+ interrupt-controller;
+ reg = <0xd8140000 0x10000>;
+ #interrupt-cells = <1>;
+ };
diff --git a/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.txt b/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.txt
new file mode 100644
index 000000000000..521b9c7de933
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-pmc.txt
@@ -0,0 +1,13 @@
+VIA/Wondermedia VT8500 Power Management Controller
+-----------------------------------------------------
+
+Required properties:
+- compatible : "via,vt8500-pmc"
+- reg : Should contain 1 register ranges(address and length)
+
+Example:
+
+ pmc@d8130000 {
+ compatible = "via,vt8500-pmc";
+ reg = <0xd8130000 0x1000>;
+ };
diff --git a/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-timer.txt b/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-timer.txt
new file mode 100644
index 000000000000..901c73f0d8ef
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/vt8500/via,vt8500-timer.txt
@@ -0,0 +1,15 @@
+VIA/Wondermedia VT8500 Timer
+-----------------------------------------------------
+
+Required properties:
+- compatible : "via,vt8500-timer"
+- reg : Should contain 1 register ranges(address and length)
+- interrupts : interrupt for the timer
+
+Example:
+
+ timer@d8130100 {
+ compatible = "via,vt8500-timer";
+ reg = <0xd8130100 0x28>;
+ interrupts = <36>;
+ };
diff --git a/Documentation/devicetree/bindings/bus/omap-ocp2scp.txt b/Documentation/devicetree/bindings/bus/omap-ocp2scp.txt
new file mode 100644
index 000000000000..d2fe064a828b
--- /dev/null
+++ b/Documentation/devicetree/bindings/bus/omap-ocp2scp.txt
@@ -0,0 +1,10 @@
+* OMAP OCP2SCP - ocp interface to scp interface
+
+properties:
+- compatible : Should be "ti,omap-ocp2scp"
+- #address-cells, #size-cells : Must be present if the device has sub-nodes
+- ranges : the child address space are mapped 1:1 onto the parent address space
+- ti,hwmods : must be "ocp2scp_usb_phy"
+
+Sub-nodes:
+All the devices connected to ocp2scp are described using sub-node to ocp2scp
diff --git a/Documentation/devicetree/bindings/clock/vt8500.txt b/Documentation/devicetree/bindings/clock/vt8500.txt
new file mode 100644
index 000000000000..a880c70d0047
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/vt8500.txt
@@ -0,0 +1,72 @@
+Device Tree Clock bindings for arch-vt8500
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Required properties:
+- compatible : shall be one of the following:
+ "via,vt8500-pll-clock" - for a VT8500/WM8505 PLL clock
+ "wm,wm8650-pll-clock" - for a WM8650 PLL clock
+ "via,vt8500-device-clock" - for a VT/WM device clock
+
+Required properties for PLL clocks:
+- reg : shall be the control register offset from PMC base for the pll clock.
+- clocks : shall be the input parent clock phandle for the clock. This should
+ be the reference clock.
+- #clock-cells : from common clock binding; shall be set to 0.
+
+Required properties for device clocks:
+- clocks : shall be the input parent clock phandle for the clock. This should
+ be a pll output.
+- #clock-cells : from common clock binding; shall be set to 0.
+
+
+Device Clocks
+
+Device clocks are required to have one or both of the following sets of
+properties:
+
+
+Gated device clocks:
+
+Required properties:
+- enable-reg : shall be the register offset from PMC base for the enable
+ register.
+- enable-bit : shall be the bit within enable-reg to enable/disable the clock.
+
+
+Divisor device clocks:
+
+Required property:
+- divisor-reg : shall be the register offset from PMC base for the divisor
+ register.
+Optional property:
+- divisor-mask : shall be the mask for the divisor register. Defaults to 0x1f
+ if not specified.
+
+
+For example:
+
+ref25: ref25M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <25000000>;
+};
+
+plla: plla {
+ #clock-cells = <0>;
+ compatible = "wm,wm8650-pll-clock";
+ clocks = <&ref25>;
+ reg = <0x200>;
+};
+
+sdhc: sdhc {
+ #clock-cells = <0>;
+ compatible = "via,vt8500-device-clock";
+ clocks = <&pllb>;
+ divisor-reg = <0x328>;
+ divisor-mask = <0x3f>;
+ enable-reg = <0x254>;
+ enable-bit = <18>;
+};
diff --git a/Documentation/devicetree/bindings/dma/mmp-dma.txt b/Documentation/devicetree/bindings/dma/mmp-dma.txt
new file mode 100644
index 000000000000..a4fa4efa1d83
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/mmp-dma.txt
@@ -0,0 +1,74 @@
+* MARVELL MMP DMA controller
+
+Marvell Peripheral DMA Controller
+Used platfroms: pxa688, pxa910, pxa3xx, etc
+
+Required properties:
+- compatible: Should be "marvell,pdma-1.0"
+- reg: Should contain DMA registers location and length.
+- interrupts: Either contain all of the per-channel DMA interrupts
+ or one irq for pdma device
+- #dma-channels: Number of DMA channels supported by the controller.
+
+"marvell,pdma-1.0"
+Used platfroms: pxa25x, pxa27x, pxa3xx, pxa93x, pxa168, pxa910, pxa688.
+
+Examples:
+
+/*
+ * Each channel has specific irq
+ * ICU parse out irq channel from ICU register,
+ * while DMA controller may not able to distinguish the irq channel
+ * Using this method, interrupt-parent is required as demuxer
+ * For example, pxa688 icu register 0x128, bit 0~15 is PDMA channel irq,
+ * 18~21 is ADMA irq
+ */
+pdma: dma-controller@d4000000 {
+ compatible = "marvell,pdma-1.0";
+ reg = <0xd4000000 0x10000>;
+ interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15>;
+ interrupt-parent = <&intcmux32>;
+ #dma-channels = <16>;
+ };
+
+/*
+ * One irq for all channels
+ * Dmaengine driver (DMA controller) distinguish irq channel via
+ * parsing internal register
+ */
+pdma: dma-controller@d4000000 {
+ compatible = "marvell,pdma-1.0";
+ reg = <0xd4000000 0x10000>;
+ interrupts = <47>;
+ #dma-channels = <16>;
+ };
+
+
+Marvell Two Channel DMA Controller used specifically for audio
+Used platfroms: pxa688, pxa910
+
+Required properties:
+- compatible: Should be "marvell,adma-1.0" or "marvell,pxa910-squ"
+- reg: Should contain DMA registers location and length.
+- interrupts: Either contain all of the per-channel DMA interrupts
+ or one irq for dma device
+
+"marvell,adma-1.0" used on pxa688
+"marvell,pxa910-squ" used on pxa910
+
+Examples:
+
+/* each channel has specific irq */
+adma0: dma-controller@d42a0800 {
+ compatible = "marvell,adma-1.0";
+ reg = <0xd42a0800 0x100>;
+ interrupts = <18 19>;
+ interrupt-parent = <&intcmux32>;
+ };
+
+/* One irq for all channels */
+squ: dma-controller@d42a0800 {
+ compatible = "marvell,pxa910-squ";
+ reg = <0xd42a0800 0x100>;
+ interrupts = <46>;
+ };
diff --git a/Documentation/devicetree/bindings/gpio/gpio-vt8500.txt b/Documentation/devicetree/bindings/gpio/gpio-vt8500.txt
new file mode 100644
index 000000000000..f4dc5233167e
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-vt8500.txt
@@ -0,0 +1,24 @@
+VIA/Wondermedia VT8500 GPIO Controller
+-----------------------------------------------------
+
+Required properties:
+- compatible : "via,vt8500-gpio", "wm,wm8505-gpio"
+ or "wm,wm8650-gpio" depending on your SoC
+- reg : Should contain 1 register range (address and length)
+- #gpio-cells : should be <3>.
+ 1) bank
+ 2) pin number
+ 3) flags - should be 0
+
+Example:
+
+ gpio: gpio-controller@d8110000 {
+ compatible = "via,vt8500-gpio";
+ gpio-controller;
+ reg = <0xd8110000 0x10000>;
+ #gpio-cells = <3>;
+ };
+
+ vibrate {
+ gpios = <&gpio 0 1 0>; /* Bank 0, Pin 1, No flags */
+ };
diff --git a/Documentation/devicetree/bindings/media/exynos5-gsc.txt b/Documentation/devicetree/bindings/media/exynos5-gsc.txt
new file mode 100644
index 000000000000..0604d42f38d1
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/exynos5-gsc.txt
@@ -0,0 +1,30 @@
+* Samsung Exynos5 G-Scaler device
+
+G-Scaler is used for scaling and color space conversion on EXYNOS5 SoCs.
+
+Required properties:
+- compatible: should be "samsung,exynos5-gsc"
+- reg: should contain G-Scaler physical address location and length.
+- interrupts: should contain G-Scaler interrupt number
+
+Example:
+
+gsc_0: gsc@0x13e00000 {
+ compatible = "samsung,exynos5-gsc";
+ reg = <0x13e00000 0x1000>;
+ interrupts = <0 85 0>;
+};
+
+Aliases:
+Each G-Scaler node should have a numbered alias in the aliases node,
+in the form of gscN, N = 0...3. G-Scaler driver uses these aliases
+to retrieve the device IDs using "of_alias_get_id()" call.
+
+Example:
+
+aliases {
+ gsc0 =&gsc_0;
+ gsc1 =&gsc_1;
+ gsc2 =&gsc_2;
+ gsc3 =&gsc_3;
+};
diff --git a/Documentation/devicetree/bindings/mfd/ab8500.txt b/Documentation/devicetree/bindings/mfd/ab8500.txt
index 69e757a657a0..ce83c8d3c00e 100644
--- a/Documentation/devicetree/bindings/mfd/ab8500.txt
+++ b/Documentation/devicetree/bindings/mfd/ab8500.txt
@@ -23,6 +23,7 @@ Device IRQ Names Supply Names Description
ab8500-bm : : : Battery Manager
ab8500-btemp : : : Battery Temperature
ab8500-charger : : : Battery Charger
+ab8500-codec : : : Audio Codec
ab8500-fg : : : Fuel Gauge
ab8500-gpadc : HW_CONV_END : vddadc : Analogue to Digital Converter
SW_CONV_END : :
@@ -52,6 +53,14 @@ Optional child device properties:
supplied in the interrupts property
- <supply_name>-supply : contains a phandle to the regulator supply node in Device Tree
+Non-standard child device properties:
+ - Audio CODEC:
+ - stericsson,amic[1|2]-type-single-ended : Single-ended Analoge Mic (default: differential)
+ - stericsson,amic1a-bias-vamic2 : Analoge Mic wishes to use a non-standard Vamic
+ - stericsson,amic1b-bias-vamic2 : Analoge Mic wishes to use a non-standard Vamic
+ - stericsson,amic2-bias-vamic1 : Analoge Mic wishes to use a non-standard Vamic
+ - stericsson,earpeice-cmv : Earpeice voltage (only: 950 | 1100 | 1270 | 1580)
+
ab8500@5 {
compatible = "stericsson,ab8500";
reg = <5>; /* mailbox 5 is i2c */
@@ -110,6 +119,12 @@ ab8500@5 {
compatible = "stericsson,ab8500-pwm";
};
+ codec: ab8500-codec {
+ compatible = "stericsson,ab8500-codec";
+
+ stericsson,earpeice-cmv = <950>; /* Units in mV. */
+ };
+
ab8500-regulators {
compatible = "stericsson,ab8500-regulator";
diff --git a/Documentation/devicetree/bindings/misc/at25.txt b/Documentation/devicetree/bindings/misc/at25.txt
index ab3c327929dd..1d3447165c37 100644
--- a/Documentation/devicetree/bindings/misc/at25.txt
+++ b/Documentation/devicetree/bindings/misc/at25.txt
@@ -1,21 +1,35 @@
-Atmel AT25 eeprom
+EEPROMs (SPI) compatible with Atmel at25.
Required properties:
- compatible : "atmel,at25".
- reg : chip select number
- spi-max-frequency : max spi frequency to use
+- pagesize : size of the eeprom page
+- size : total eeprom size in bytes
+- address-width : number of address bits (one of 8, 16, or 24)
+Optional properties:
+- spi-cpha : SPI shifted clock phase, as per spi-bus bindings.
+- spi-cpol : SPI inverse clock polarity, as per spi-bus bindings.
+- read-only : this parameter-less property disables writes to the eeprom
+
+Obsolete legacy properties are can be used in place of "size", "pagesize",
+"address-width", and "read-only":
- at25,byte-len : total eeprom size in bytes
- at25,addr-mode : addr-mode flags, as defined in include/linux/spi/eeprom.h
- at25,page-size : size of the eeprom page
-Examples:
-at25@0 {
- compatible = "atmel,at25";
- reg = <0>
- spi-max-frequency = <5000000>;
+Additional compatible properties are also allowed.
+
+Example:
+ at25@0 {
+ compatible = "atmel,at25", "st,m95256";
+ reg = <0>
+ spi-max-frequency = <5000000>;
+ spi-cpha;
+ spi-cpol;
- at25,byte-len = <0x8000>;
- at25,addr-mode = <2>;
- at25,page-size = <64>;
-};
+ pagesize = <64>;
+ size = <32768>;
+ address-width = <16>;
+ };
diff --git a/Documentation/devicetree/bindings/misc/lis302.txt b/Documentation/devicetree/bindings/misc/lis302.txt
new file mode 100644
index 000000000000..6def86f6b053
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/lis302.txt
@@ -0,0 +1,112 @@
+LIS302 accelerometer devicetree bindings
+
+This device is matched via its bus drivers, and has a number of properties
+that apply in on the generic device (independent from the bus).
+
+
+Required properties for the SPI bindings:
+ - compatible: should be set to "st,lis3lv02d_spi"
+ - reg: the chipselect index
+ - spi-max-frequency: maximal bus speed, should be set to 1000000 unless
+ constrained by external circuitry
+ - interrupts: the interrupt generated by the device
+
+Required properties for the I2C bindings:
+ - compatible: should be set to "st,lis3lv02d"
+ - reg: i2c slave address
+ - Vdd-supply: The input supply for Vdd
+ - Vdd_IO-supply: The input supply for Vdd_IO
+
+
+Optional properties for all bus drivers:
+
+ - st,click-single-{x,y,z}: if present, tells the device to issue an
+ interrupt on single click events on the
+ x/y/z axis.
+ - st,click-double-{x,y,z}: if present, tells the device to issue an
+ interrupt on double click events on the
+ x/y/z axis.
+ - st,click-thresh-{x,y,z}: set the x/y/z axis threshold
+ - st,click-click-time-limit: click time limit, from 0 to 127.5msec
+ with step of 0.5 msec
+ - st,click-latency: click latency, from 0 to 255 msec with
+ step of 1 msec.
+ - st,click-window: click window, from 0 to 255 msec with
+ step of 1 msec.
+ - st,irq{1,2}-disable: disable IRQ 1/2
+ - st,irq{1,2}-ff-wu-1: raise IRQ 1/2 on FF_WU_1 condition
+ - st,irq{1,2}-ff-wu-2: raise IRQ 1/2 on FF_WU_2 condition
+ - st,irq{1,2}-data-ready: raise IRQ 1/2 on data ready contition
+ - st,irq{1,2}-click: raise IRQ 1/2 on click condition
+ - st,irq-open-drain: consider IRQ lines open-drain
+ - st,irq-active-low: make IRQ lines active low
+ - st,wu-duration-1: duration register for Free-Fall/Wake-Up
+ interrupt 1
+ - st,wu-duration-2: duration register for Free-Fall/Wake-Up
+ interrupt 2
+ - st,wakeup-{x,y,z}-{lo,hi}: set wakeup condition on x/y/z axis for
+ upper/lower limit
+ - st,highpass-cutoff-hz=: 1, 2, 4 or 8 for 1Hz, 2Hz, 4Hz or 8Hz of
+ highpass cut-off frequency
+ - st,hipass{1,2}-disable: disable highpass 1/2.
+ - st,default-rate=: set the default rate
+ - st,axis-{x,y,z}=: set the axis to map to the three coordinates
+ - st,{min,max}-limit-{x,y,z} set the min/max limits for x/y/z axis
+ (used by self-test)
+
+
+Example for a SPI device node:
+
+ lis302@0 {
+ compatible = "st,lis302dl-spi";
+ reg = <0>;
+ spi-max-frequency = <1000000>;
+ interrupt-parent = <&gpio>;
+ interrupts = <104 0>;
+
+ 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;
+ };
+
+Example for a I2C device node:
+
+ lis331dlh: lis331dlh@18 {
+ compatible = "st,lis331dlh", "st,lis3lv02d";
+ reg = <0x18>;
+ Vdd-supply = <&lis3_reg>;
+ Vdd_IO-supply = <&lis3_reg>;
+
+ 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>;
+ };
+
diff --git a/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
new file mode 100644
index 000000000000..03dee50532f5
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt
@@ -0,0 +1,196 @@
+Samsung GPIO and Pin Mux/Config controller
+
+Samsung's ARM based SoC's integrates a GPIO and Pin mux/config hardware
+controller. It controls the input/output settings on the available pads/pins
+and also provides ability to multiplex and configure the output of various
+on-chip controllers onto these pads.
+
+Required Properties:
+- compatible: should be one of the following.
+ - "samsung,pinctrl-exynos4210": for Exynos4210 compatible pin-controller.
+ - "samsung,pinctrl-exynos5250": for Exynos5250 compatible pin-controller.
+
+- reg: Base address of the pin controller hardware module and length of
+ the address space it occupies.
+
+- interrupts: interrupt specifier for the controller. The format and value of
+ the interrupt specifier depends on the interrupt parent for the controller.
+
+- Pin mux/config groups as child nodes: The pin mux (selecting pin function
+ mode) and pin config (pull up/down, driver strength) settings are represented
+ as child nodes of the pin-controller node. There should be atleast one
+ child node and there is no limit on the count of these child nodes.
+
+ The child node should contain a list of pin(s) on which a particular pin
+ function selection or pin configuration (or both) have to applied. This
+ list of pins is specified using the property name "samsung,pins". There
+ should be atleast one pin specfied for this property and there is no upper
+ limit on the count of pins that can be specified. The pins are specified
+ using pin names which are derived from the hardware manual of the SoC. As
+ an example, the pins in GPA0 bank of the pin controller can be represented
+ as "gpa0-0", "gpa0-1", "gpa0-2" and so on. The names should be in lower case.
+ The format of the pin names should be (as per the hardware manual)
+ "[pin bank name]-[pin number within the bank]".
+
+ The pin function selection that should be applied on the pins listed in the
+ child node is specified using the "samsung,pin-function" property. The value
+ of this property that should be applied to each of the pins listed in the
+ "samsung,pins" property should be picked from the hardware manual of the SoC
+ for the specified pin group. This property is optional in the child node if
+ no specific function selection is desired for the pins listed in the child
+ node. The value of this property is used as-is to program the pin-controller
+ function selector register of the pin-bank.
+
+ The child node can also optionally specify one or more of the pin
+ configuration that should be applied on all the pins listed in the
+ "samsung,pins" property of the child node. The following pin configuration
+ properties are supported.
+
+ - samsung,pin-pud: Pull up/down configuration.
+ - samsung,pin-drv: Drive strength configuration.
+ - samsung,pin-pud-pdn: Pull up/down configuration in power down mode.
+ - samsung,pin-drv-pdn: Drive strength configuration in power down mode.
+
+ The values specified by these config properties should be derived from the
+ hardware manual and these values are programmed as-is into the pin
+ pull up/down and driver strength register of the pin-controller.
+
+ Note: A child should include atleast a pin function selection property or
+ pin configuration property (one or more) or both.
+
+ The client nodes that require a particular pin function selection and/or
+ pin configuration should use the bindings listed in the "pinctrl-bindings.txt"
+ file.
+
+External GPIO and Wakeup Interrupts:
+
+The controller supports two types of external interrupts over gpio. The first
+is the external gpio interrupt and second is the external wakeup interrupts.
+The difference between the two is that the external wakeup interrupts can be
+used as system wakeup events.
+
+A. External GPIO Interrupts: For supporting external gpio interrupts, the
+ following properties should be specified in the pin-controller device node.
+
+- interrupt-controller: identifies the controller node as interrupt-parent.
+- #interrupt-cells: the value of this property should be 2.
+ - First Cell: represents the external gpio interrupt number local to the
+ external gpio interrupt space of the controller.
+ - Second Cell: flags to identify the type of the interrupt
+ - 1 = rising edge triggered
+ - 2 = falling edge triggered
+ - 3 = rising and falling edge triggered
+ - 4 = high level triggered
+ - 8 = low level triggered
+
+B. External Wakeup Interrupts: For supporting external wakeup interrupts, a
+ child node representing the external wakeup interrupt controller should be
+ included in the pin-controller device node. This child node should include
+ the following properties.
+
+ - compatible: identifies the type of the external wakeup interrupt controller
+ The possible values are:
+ - samsung,exynos4210-wakeup-eint: represents wakeup interrupt controller
+ found on Samsung Exynos4210 SoC.
+ - interrupt-parent: phandle of the interrupt parent to which the external
+ wakeup interrupts are forwarded to.
+ - interrupt-controller: identifies the node as interrupt-parent.
+ - #interrupt-cells: the value of this property should be 2
+ - First Cell: represents the external wakeup interrupt number local to
+ the external wakeup interrupt space of the controller.
+ - Second Cell: flags to identify the type of the interrupt
+ - 1 = rising edge triggered
+ - 2 = falling edge triggered
+ - 3 = rising and falling edge triggered
+ - 4 = high level triggered
+ - 8 = low level triggered
+
+Aliases:
+
+All the pin controller nodes should be represented in the aliases node using
+the following format 'pinctrl{n}' where n is a unique number for the alias.
+
+Example 1: A pin-controller node with pin groups.
+
+ pinctrl_0: pinctrl@11400000 {
+ compatible = "samsung,pinctrl-exynos4210";
+ reg = <0x11400000 0x1000>;
+ interrupts = <0 47 0>;
+
+ uart0_data: uart0-data {
+ samsung,pins = "gpa0-0", "gpa0-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart0_fctl: uart0-fctl {
+ samsung,pins = "gpa0-2", "gpa0-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart1_data: uart1-data {
+ samsung,pins = "gpa0-4", "gpa0-5";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart1_fctl: uart1-fctl {
+ samsung,pins = "gpa0-6", "gpa0-7";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c2_bus: i2c2-bus {
+ samsung,pins = "gpa0-6", "gpa0-7";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+ };
+
+Example 2: A pin-controller node with external wakeup interrupt controller node.
+
+ pinctrl_1: pinctrl@11000000 {
+ compatible = "samsung,pinctrl-exynos4210";
+ reg = <0x11000000 0x1000>;
+ interrupts = <0 46 0>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ wakup_eint: wakeup-interrupt-controller {
+ compatible = "samsung,exynos4210-wakeup-eint";
+ interrupt-parent = <&gic>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>,
+ <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>,
+ <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
+ <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>,
+ <0 32 0>;
+ };
+ };
+
+Example 3: A uart client node that supports 'default' and 'flow-control' states.
+
+ uart@13800000 {
+ compatible = "samsung,exynos4210-uart";
+ reg = <0x13800000 0x100>;
+ interrupts = <0 52 0>;
+ pinctrl-names = "default", "flow-control;
+ pinctrl-0 = <&uart0_data>;
+ pinctrl-1 = <&uart0_data &uart0_fctl>;
+ };
+
+Example 4: Set up the default pin state for uart controller.
+
+ static int s3c24xx_serial_probe(struct platform_device *pdev) {
+ struct pinctrl *pinctrl;
+ ...
+ ...
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ }
diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt
index 66ece3f87bbc..ecfc6ccd67ef 100644
--- a/Documentation/devicetree/bindings/regulator/regulator.txt
+++ b/Documentation/devicetree/bindings/regulator/regulator.txt
@@ -11,10 +11,13 @@ Optional properties:
- regulator-boot-on: bootloader/firmware enabled regulator
- <name>-supply: phandle to the parent supply/regulator node
- regulator-ramp-delay: ramp delay for regulator(in uV/uS)
+
+Deprecated properties:
- regulator-compatible: If a regulator chip contains multiple
regulators, and if the chip's binding contains a child node that
describes each regulator, then this property indicates which regulator
- this child node is intended to configure.
+ this child node is intended to configure. If this property is missing,
+ the node's name will be used instead.
Example:
diff --git a/Documentation/devicetree/bindings/regulator/tps65217.txt b/Documentation/devicetree/bindings/regulator/tps65217.txt
index 0487e9675ba0..d316fb895daf 100644
--- a/Documentation/devicetree/bindings/regulator/tps65217.txt
+++ b/Documentation/devicetree/bindings/regulator/tps65217.txt
@@ -22,66 +22,49 @@ Example:
compatible = "ti,tps65217";
regulators {
- #address-cells = <1>;
- #size-cells = <0>;
-
- dcdc1_reg: regulator@0 {
- reg = <0>;
- regulator-compatible = "dcdc1";
+ dcdc1_reg: dcdc1 {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <1800000>;
regulator-boot-on;
regulator-always-on;
};
- dcdc2_reg: regulator@1 {
- reg = <1>;
- regulator-compatible = "dcdc2";
+ dcdc2_reg: dcdc2 {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
- dcdc3_reg: regulator@2 {
- reg = <2>;
- regulator-compatible = "dcdc3";
+ dcdc3_reg: dcc3 {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
- ldo1_reg: regulator@3 {
- reg = <3>;
- regulator-compatible = "ldo1";
+ ldo1_reg: ldo1 {
regulator-min-microvolt = <1000000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
- ldo2_reg: regulator@4 {
- reg = <4>;
- regulator-compatible = "ldo2";
+ ldo2_reg: ldo2 {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
- ldo3_reg: regulator@5 {
- reg = <5>;
- regulator-compatible = "ldo3";
+ ldo3_reg: ldo3 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
};
- ldo4_reg: regulator@6 {
- reg = <6>;
- regulator-compatible = "ldo4";
+ ldo4_reg: ldo4 {
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
diff --git a/Documentation/devicetree/bindings/regulator/tps6586x.txt b/Documentation/devicetree/bindings/regulator/tps6586x.txt
index a2436e1edfc1..07b9ef6e49d5 100644
--- a/Documentation/devicetree/bindings/regulator/tps6586x.txt
+++ b/Documentation/devicetree/bindings/regulator/tps6586x.txt
@@ -6,8 +6,11 @@ Required properties:
- interrupts: the interrupt outputs of the controller
- #gpio-cells: number of cells to describe a GPIO
- gpio-controller: mark the device as a GPIO controller
-- regulators: list of regulators provided by this controller, must have
- property "regulator-compatible" to match their hardware counterparts:
+- regulators: A node that houses a sub-node for each regulator within the
+ device. Each sub-node is identified using the node's name (or the deprecated
+ regulator-compatible property if present), with valid values listed below.
+ The content of each sub-node is defined by the standard binding for
+ regulators; see regulator.txt.
sys, sm[0-2], ldo[0-9] and ldo_rtc
- sys-supply: The input supply for SYS.
- vin-sm0-supply: The input supply for the SM0.
@@ -45,111 +48,80 @@ Example:
vinldo9-supply = <...>;
regulators {
- #address-cells = <1>;
- #size-cells = <0>;
-
- sys_reg: regulator@0 {
- reg = <0>;
- regulator-compatible = "sys";
+ sys_reg: sys {
regulator-name = "vdd_sys";
regulator-boot-on;
regulator-always-on;
};
- sm0_reg: regulator@1 {
- reg = <1>;
- regulator-compatible = "sm0";
+ sm0_reg: sm0 {
regulator-min-microvolt = < 725000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
- sm1_reg: regulator@2 {
- reg = <2>;
- regulator-compatible = "sm1";
+ sm1_reg: sm1 {
regulator-min-microvolt = < 725000>;
regulator-max-microvolt = <1500000>;
regulator-boot-on;
regulator-always-on;
};
- sm2_reg: regulator@3 {
- reg = <3>;
- regulator-compatible = "sm2";
+ sm2_reg: sm2 {
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <4550000>;
regulator-boot-on;
regulator-always-on;
};
- ldo0_reg: regulator@4 {
- reg = <4>;
- regulator-compatible = "ldo0";
+ ldo0_reg: ldo0 {
regulator-name = "PCIE CLK";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
};
- ldo1_reg: regulator@5 {
- reg = <5>;
- regulator-compatible = "ldo1";
+ ldo1_reg: ldo1 {
regulator-min-microvolt = < 725000>;
regulator-max-microvolt = <1500000>;
};
- ldo2_reg: regulator@6 {
- reg = <6>;
- regulator-compatible = "ldo2";
+ ldo2_reg: ldo2 {
regulator-min-microvolt = < 725000>;
regulator-max-microvolt = <1500000>;
};
- ldo3_reg: regulator@7 {
- reg = <7>;
- regulator-compatible = "ldo3";
+ ldo3_reg: ldo3 {
regulator-min-microvolt = <1250000>;
regulator-max-microvolt = <3300000>;
};
- ldo4_reg: regulator@8 {
- reg = <8>;
- regulator-compatible = "ldo4";
+ ldo4_reg: ldo4 {
regulator-min-microvolt = <1700000>;
regulator-max-microvolt = <2475000>;
};
- ldo5_reg: regulator@9 {
- reg = <9>;
- regulator-compatible = "ldo5";
+ ldo5_reg: ldo5 {
regulator-min-microvolt = <1250000>;
regulator-max-microvolt = <3300000>;
};
- ldo6_reg: regulator@10 {
- reg = <10>;
- regulator-compatible = "ldo6";
+ ldo6_reg: ldo6 {
regulator-min-microvolt = <1250000>;
regulator-max-microvolt = <3300000>;
};
- ldo7_reg: regulator@11 {
- reg = <11>;
- regulator-compatible = "ldo7";
+ ldo7_reg: ldo7 {
regulator-min-microvolt = <1250000>;
regulator-max-microvolt = <3300000>;
};
- ldo8_reg: regulator@12 {
- reg = <12>;
- regulator-compatible = "ldo8";
+ ldo8_reg: ldo8 {
regulator-min-microvolt = <1250000>;
regulator-max-microvolt = <3300000>;
};
- ldo9_reg: regulator@13 {
- reg = <13>;
- regulator-compatible = "ldo9";
+ ldo9_reg: ldo9 {
regulator-min-microvolt = <1250000>;
regulator-max-microvolt = <3300000>;
};
diff --git a/Documentation/devicetree/bindings/rtc/via,vt8500-rtc.txt b/Documentation/devicetree/bindings/rtc/via,vt8500-rtc.txt
new file mode 100644
index 000000000000..3c0484c49582
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/via,vt8500-rtc.txt
@@ -0,0 +1,15 @@
+VIA/Wondermedia VT8500 Realtime Clock Controller
+-----------------------------------------------------
+
+Required properties:
+- compatible : "via,vt8500-rtc"
+- reg : Should contain 1 register ranges(address and length)
+- interrupts : alarm interrupt
+
+Example:
+
+ rtc@d8100000 {
+ compatible = "via,vt8500-rtc";
+ reg = <0xd8100000 0x10000>;
+ interrupts = <48>;
+ };
diff --git a/Documentation/devicetree/bindings/sound/ux500-mop500.txt b/Documentation/devicetree/bindings/sound/ux500-mop500.txt
new file mode 100644
index 000000000000..48e071c96b46
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ux500-mop500.txt
@@ -0,0 +1,39 @@
+* MOP500 Audio Machine Driver
+
+This node is responsible for linking together all ux500 Audio Driver components.
+
+Required properties:
+ - compatible : "stericsson,snd-soc-mop500"
+
+Non-standard properties:
+ - stericsson,cpu-dai : Phandle to the CPU-side DAI
+ - stericsson,audio-codec : Phandle to the Audio CODEC
+ - stericsson,card-name : Over-ride default card name
+
+Example:
+
+ sound {
+ compatible = "stericsson,snd-soc-mop500";
+
+ stericsson,cpu-dai = <&msp1 &msp3>;
+ stericsson,audio-codec = <&codec>;
+ };
+
+ msp1: msp@80124000 {
+ compatible = "stericsson,ux500-msp-i2s";
+ reg = <0x80124000 0x1000>;
+ interrupts = <0 62 0x4>;
+ v-ape-supply = <&db8500_vape_reg>;
+ };
+
+ msp3: msp@80125000 {
+ compatible = "stericsson,ux500-msp-i2s";
+ reg = <0x80125000 0x1000>;
+ interrupts = <0 62 0x4>;
+ v-ape-supply = <&db8500_vape_reg>;
+ };
+
+ codec: ab8500-codec {
+ compatible = "stericsson,ab8500-codec";
+ stericsson,earpeice-cmv = <950>; /* Units in mV. */
+ };
diff --git a/Documentation/devicetree/bindings/sound/ux500-msp.txt b/Documentation/devicetree/bindings/sound/ux500-msp.txt
new file mode 100644
index 000000000000..99acd9c774e1
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ux500-msp.txt
@@ -0,0 +1,43 @@
+* ux500 MSP (CPU-side Digital Audio Interface)
+
+Required properties:
+ - compatible :"stericsson,ux500-msp-i2s"
+ - reg : Physical base address and length of the device's registers.
+
+Optional properties:
+ - interrupts : The interrupt output from the device.
+ - interrupt-parent : The parent interrupt controller.
+ - <name>-supply : Phandle to the regulator <name> supply
+
+Example:
+
+ sound {
+ compatible = "stericsson,snd-soc-mop500";
+
+ stericsson,platform-pcm-dma = <&pcm>;
+ stericsson,cpu-dai = <&msp1 &msp3>;
+ stericsson,audio-codec = <&codec>;
+ };
+
+ pcm: ux500-pcm {
+ compatible = "stericsson,ux500-pcm";
+ };
+
+ msp1: msp@80124000 {
+ compatible = "stericsson,ux500-msp-i2s";
+ reg = <0x80124000 0x1000>;
+ interrupts = <0 62 0x4>;
+ v-ape-supply = <&db8500_vape_reg>;
+ };
+
+ msp3: msp@80125000 {
+ compatible = "stericsson,ux500-msp-i2s";
+ reg = <0x80125000 0x1000>;
+ interrupts = <0 62 0x4>;
+ v-ape-supply = <&db8500_vape_reg>;
+ };
+
+ codec: ab8500-codec {
+ compatible = "stericsson,ab8500-codec";
+ stericsson,earpeice-cmv = <950>; /* Units in mV. */
+ };
diff --git a/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
new file mode 100644
index 000000000000..801d58cb6d4d
--- /dev/null
+++ b/Documentation/devicetree/bindings/staging/iio/adc/mxs-lradc.txt
@@ -0,0 +1,15 @@
+* Freescale i.MX28 LRADC device driver
+
+Required properties:
+- compatible: Should be "fsl,imx28-lradc"
+- reg: Address and length of the register set for the device
+- interrupts: Should contain the LRADC interrupts
+
+Examples:
+
+ lradc@80050000 {
+ compatible = "fsl,imx28-lradc";
+ reg = <0x80050000 0x2000>;
+ interrupts = <10 14 15 16 17 18 19
+ 20 21 22 23 24 25>;
+ };
diff --git a/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt b/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt
new file mode 100644
index 000000000000..07654f0338b6
--- /dev/null
+++ b/Documentation/devicetree/bindings/staging/imx-drm/fsl-imx-drm.txt
@@ -0,0 +1,41 @@
+Freescale i.MX IPUv3
+====================
+
+Required properties:
+- compatible: Should be "fsl,<chip>-ipu"
+- reg: should be register base and length as documented in the
+ datasheet
+- interrupts: Should contain sync interrupt and error interrupt,
+ in this order.
+- #crtc-cells: 1, See below
+
+example:
+
+ipu: ipu@18000000 {
+ #crtc-cells = <1>;
+ compatible = "fsl,imx53-ipu";
+ reg = <0x18000000 0x080000000>;
+ interrupts = <11 10>;
+};
+
+Parallel display support
+========================
+
+Required properties:
+- compatible: Should be "fsl,imx-parallel-display"
+- crtc: the crtc this display is connected to, see below
+Optional properties:
+- interface_pix_fmt: How this display is connected to the
+ crtc. Currently supported types: "rgb24", "rgb565"
+- edid: verbatim EDID data block describing attached display.
+- ddc: phandle describing the i2c bus handling the display data
+ channel
+
+example:
+
+display@di0 {
+ compatible = "fsl,imx-parallel-display";
+ edid = [edid-data];
+ crtc = <&ipu 0>;
+ interface-pix-fmt = "rgb24";
+};
diff --git a/Documentation/devicetree/bindings/tty/serial/via,vt8500-uart.txt b/Documentation/devicetree/bindings/tty/serial/via,vt8500-uart.txt
new file mode 100644
index 000000000000..5feef1ef167d
--- /dev/null
+++ b/Documentation/devicetree/bindings/tty/serial/via,vt8500-uart.txt
@@ -0,0 +1,17 @@
+VIA/Wondermedia VT8500 UART Controller
+-----------------------------------------------------
+
+Required properties:
+- compatible : "via,vt8500-uart"
+- reg : Should contain 1 register ranges(address and length)
+- interrupts : UART interrupt
+- clocks : phandle to the uart source clock (usually a 24Mhz fixed clock)
+
+Example:
+
+ uart@d8210000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd8210000 0x1040>;
+ interrupts = <47>;
+ clocks = <&ref24>;
+ };
diff --git a/Documentation/devicetree/bindings/usb/am33xx-usb.txt b/Documentation/devicetree/bindings/usb/am33xx-usb.txt
new file mode 100644
index 000000000000..ca8fa56e9f03
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/am33xx-usb.txt
@@ -0,0 +1,14 @@
+AM33XX MUSB GLUE
+ - compatible : Should be "ti,musb-am33xx"
+ - ti,hwmods : must be "usb_otg_hs"
+ - multipoint : Should be "1" indicating the musb controller supports
+ multipoint. This is a MUSB configuration-specific setting.
+ - num_eps : Specifies the number of endpoints. This is also a
+ MUSB configuration-specific setting. Should be set to "16"
+ - ram_bits : Specifies the ram address size. Should be set to "12"
+ - port0_mode : Should be "3" to represent OTG. "1" signifies HOST and "2"
+ represents PERIPHERAL.
+ - port1_mode : Should be "1" to represent HOST. "3" signifies OTG and "2"
+ represents PERIPHERAL.
+ - power : Should be "250". This signifies the controller can supply upto
+ 500mA when operating in host mode.
diff --git a/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt b/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt
index 2c290418bb2d..5778b9c83bd8 100644
--- a/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt
+++ b/Documentation/devicetree/bindings/usb/ci13xxx-imx.txt
@@ -7,7 +7,10 @@ Required properties:
Optional properties:
- fsl,usbphy: phandler of usb phy that connects to the only one port
+- fsl,usbmisc: phandler of non-core register device, with one argument
+ that indicate usb controller index
- vbus-supply: regulator for vbus
+- disable-over-current: disable over current detect
Examples:
usb@02184000 { /* USB OTG */
@@ -15,4 +18,6 @@ usb@02184000 { /* USB OTG */
reg = <0x02184000 0x200>;
interrupts = <0 43 0x04>;
fsl,usbphy = <&usbphy1>;
+ fsl,usbmisc = <&usbmisc 0>;
+ disable-over-current;
};
diff --git a/Documentation/devicetree/bindings/usb/omap-usb.txt b/Documentation/devicetree/bindings/usb/omap-usb.txt
new file mode 100644
index 000000000000..29a043ecda52
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/omap-usb.txt
@@ -0,0 +1,33 @@
+OMAP GLUE
+
+OMAP MUSB GLUE
+ - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
+ - ti,hwmods : must be "usb_otg_hs"
+ - multipoint : Should be "1" indicating the musb controller supports
+ multipoint. This is a MUSB configuration-specific setting.
+ - num_eps : Specifies the number of endpoints. This is also a
+ MUSB configuration-specific setting. Should be set to "16"
+ - ram_bits : Specifies the ram address size. Should be set to "12"
+ - interface_type : This is a board specific setting to describe the type of
+ interface between the controller and the phy. It should be "0" or "1"
+ specifying ULPI and UTMI respectively.
+ - mode : Should be "3" to represent OTG. "1" signifies HOST and "2"
+ represents PERIPHERAL.
+ - power : Should be "50". This signifies the controller can supply upto
+ 100mA when operating in host mode.
+
+SOC specific device node entry
+usb_otg_hs: usb_otg_hs@4a0ab000 {
+ compatible = "ti,omap4-musb";
+ ti,hwmods = "usb_otg_hs";
+ multipoint = <1>;
+ num_eps = <16>;
+ ram_bits = <12>;
+};
+
+Board specific device node entry
+&usb_otg_hs {
+ interface_type = <1>;
+ mode = <3>;
+ power = <50>;
+};
diff --git a/Documentation/devicetree/bindings/usb/platform-uhci.txt b/Documentation/devicetree/bindings/usb/platform-uhci.txt
new file mode 100644
index 000000000000..a4fb0719d157
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/platform-uhci.txt
@@ -0,0 +1,15 @@
+Generic Platform UHCI Controller
+-----------------------------------------------------
+
+Required properties:
+- compatible : "platform-uhci"
+- reg : Should contain 1 register ranges(address and length)
+- interrupts : UHCI controller interrupt
+
+Example:
+
+ uhci@d8007b00 {
+ compatible = "platform-uhci";
+ reg = <0xd8007b00 0x200>;
+ interrupts = <43>;
+ };
diff --git a/Documentation/devicetree/bindings/usb/pxa-usb.txt b/Documentation/devicetree/bindings/usb/pxa-usb.txt
new file mode 100644
index 000000000000..79729a948d5a
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/pxa-usb.txt
@@ -0,0 +1,31 @@
+PXA USB controllers
+
+OHCI
+
+Required properties:
+ - compatible: Should be "marvell,pxa-ohci" for USB controllers
+ used in host mode.
+
+Optional properties:
+ - "marvell,enable-port1", "marvell,enable-port2", "marvell,enable-port3"
+ If present, enables the appropriate USB port of the controller.
+ - "marvell,port-mode" selects the mode of the ports:
+ 1 = PMM_NPS_MODE
+ 2 = PMM_GLOBAL_MODE
+ 3 = PMM_PERPORT_MODE
+ - "marvell,power-sense-low" - power sense pin is low-active.
+ - "marvell,power-control-low" - power control pin is low-active.
+ - "marvell,no-oc-protection" - disable over-current protection.
+ - "marvell,oc-mode-perport" - enable per-port over-current protection.
+ - "marvell,power_on_delay" Power On to Power Good time - in ms.
+
+Example:
+
+ usb0: ohci@4c000000 {
+ compatible = "marvell,pxa-ohci", "usb-ohci";
+ reg = <0x4c000000 0x100000>;
+ interrupts = <18>;
+ marvell,enable-port1;
+ marvell,port-mode = <2>; /* PMM_GLOBAL_MODE */
+ };
+
diff --git a/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt b/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt
new file mode 100644
index 000000000000..36b9aede3f40
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/twlxxxx-usb.txt
@@ -0,0 +1,40 @@
+USB COMPARATOR OF TWL CHIPS
+
+TWL6030 USB COMPARATOR
+ - compatible : Should be "ti,twl6030-usb"
+ - interrupts : Two interrupt numbers to the cpu should be specified. First
+ interrupt number is the otg interrupt number that raises ID interrupts when
+ the controller has to act as host and the second interrupt number is the
+ usb interrupt number that raises VBUS interrupts when the controller has to
+ act as device
+ - usb-supply : phandle to the regulator device tree node. It should be vusb
+ if it is twl6030 or ldousb if it is twl6025 subclass.
+
+twl6030-usb {
+ compatible = "ti,twl6030-usb";
+ interrupts = < 4 10 >;
+};
+
+Board specific device node entry
+&twl6030-usb {
+ usb-supply = <&vusb>;
+};
+
+TWL4030 USB PHY AND COMPARATOR
+ - compatible : Should be "ti,twl4030-usb"
+ - interrupts : The interrupt numbers to the cpu should be specified. First
+ interrupt number is the otg interrupt number that raises ID interrupts
+ and VBUS interrupts. The second interrupt number is optional.
+ - <supply-name>-supply : phandle to the regulator device tree node.
+ <supply-name> should be vusb1v5, vusb1v8 and vusb3v1
+ - usb_mode : The mode used by the phy to connect to the controller. "1"
+ specifies "ULPI" mode and "2" specifies "CEA2011_3PIN" mode.
+
+twl4030-usb {
+ compatible = "ti,twl4030-usb";
+ interrupts = < 10 4 >;
+ usb1v5-supply = <&vusb1v5>;
+ usb1v8-supply = <&vusb1v8>;
+ usb3v1-supply = <&vusb3v1>;
+ usb_mode = <1>;
+};
diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt
new file mode 100644
index 000000000000..80d4148cb661
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/usb-phy.txt
@@ -0,0 +1,17 @@
+USB PHY
+
+OMAP USB2 PHY
+
+Required properties:
+ - compatible: Should be "ti,omap-usb2"
+ - reg : Address and length of the register set for the device. Also
+add the address of control module dev conf register until a driver for
+control module is added
+
+This is usually a subnode of ocp2scp to which it is connected.
+
+usb2phy@4a0ad080 {
+ compatible = "ti,omap-usb2";
+ reg = <0x4a0ad080 0x58>,
+ <0x4a002300 0x4>;
+};
diff --git a/Documentation/devicetree/bindings/usb/usbmisc-imx.txt b/Documentation/devicetree/bindings/usb/usbmisc-imx.txt
new file mode 100644
index 000000000000..97ce94e1a6cc
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/usbmisc-imx.txt
@@ -0,0 +1,14 @@
+* Freescale i.MX non-core registers
+
+Required properties:
+- #index-cells: Cells used to descibe usb controller index. Should be <1>
+- compatible: Should be one of below:
+ "fsl,imx6q-usbmisc" for imx6q
+- reg: Should contain registers location and length
+
+Examples:
+usbmisc@02184800 {
+ #index-cells = <1>;
+ compatible = "fsl,imx6q-usbmisc";
+ reg = <0x02184800 0x200>;
+};
diff --git a/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt b/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt
new file mode 100644
index 000000000000..17b3ad1d97e7
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt
@@ -0,0 +1,15 @@
+VIA/Wondermedia VT8500 EHCI Controller
+-----------------------------------------------------
+
+Required properties:
+- compatible : "via,vt8500-ehci"
+- reg : Should contain 1 register ranges(address and length)
+- interrupts : ehci controller interrupt
+
+Example:
+
+ ehci@d8007900 {
+ compatible = "via,vt8500-ehci";
+ reg = <0xd8007900 0x200>;
+ interrupts = <43>;
+ };
diff --git a/Documentation/devicetree/bindings/usb/vt8500-ehci.txt b/Documentation/devicetree/bindings/usb/vt8500-ehci.txt
new file mode 100644
index 000000000000..5fb8fd6e250c
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/vt8500-ehci.txt
@@ -0,0 +1,12 @@
+VIA VT8500 and Wondermedia WM8xxx SoC USB controllers.
+
+Required properties:
+ - compatible: Should be "via,vt8500-ehci" or "wm,prizm-ehci".
+ - reg: Address range of the ehci registers. size should be 0x200
+ - interrupts: Should contain the ehci interrupt.
+
+usb: ehci@D8007100 {
+ compatible = "wm,prizm-ehci", "usb-ehci";
+ reg = <0xD8007100 0x200>;
+ interrupts = <1>;
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 4f293e5571f0..9de2b9ff9d6e 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -48,5 +48,7 @@ sirf SiRF Technology, Inc.
st STMicroelectronics
stericsson ST-Ericsson
ti Texas Instruments
+via VIA Technologies, Inc.
wlf Wolfson Microelectronics
+wm Wondermedia Technologies, Inc.
xlnx Xilinx
diff --git a/Documentation/devicetree/bindings/video/via,vt8500-fb.txt b/Documentation/devicetree/bindings/video/via,vt8500-fb.txt
new file mode 100644
index 000000000000..c870b6478ec8
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/via,vt8500-fb.txt
@@ -0,0 +1,62 @@
+VIA VT8500 Framebuffer
+-----------------------------------------------------
+
+Required properties:
+- compatible : "via,vt8500-fb"
+- reg : Should contain 1 register ranges(address and length)
+- interrupts : framebuffer controller interrupt
+- display: a phandle pointing to the display node
+
+Required nodes:
+- display: a display node is required to initialize the lcd panel
+ This should be in the board dts.
+- default-mode: a videomode within the display with timing parameters
+ as specified below.
+
+Example:
+
+ fb@d800e400 {
+ compatible = "via,vt8500-fb";
+ reg = <0xd800e400 0x400>;
+ interrupts = <12>;
+ display = <&display>;
+ default-mode = <&mode0>;
+ };
+
+VIA VT8500 Display
+-----------------------------------------------------
+Required properties (as per of_videomode_helper):
+
+ - hactive, vactive: Display resolution
+ - hfront-porch, hback-porch, hsync-len: Horizontal Display timing parameters
+ in pixels
+ vfront-porch, vback-porch, vsync-len: Vertical display timing parameters in
+ lines
+ - clock: displayclock in Hz
+ - bpp: lcd panel bit-depth.
+ <16> for RGB565, <32> for RGB888
+
+Optional properties (as per of_videomode_helper):
+ - width-mm, height-mm: Display dimensions in mm
+ - hsync-active-high (bool): Hsync pulse is active high
+ - vsync-active-high (bool): Vsync pulse is active high
+ - interlaced (bool): This is an interlaced mode
+ - doublescan (bool): This is a doublescan mode
+
+Example:
+ display: display@0 {
+ modes {
+ mode0: mode@0 {
+ hactive = <800>;
+ vactive = <480>;
+ hback-porch = <88>;
+ hfront-porch = <40>;
+ hsync-len = <0>;
+ vback-porch = <32>;
+ vfront-porch = <11>;
+ vsync-len = <1>;
+ clock = <0>; /* unused but required */
+ bpp = <16>; /* non-standard but required */
+ };
+ };
+ };
diff --git a/Documentation/devicetree/bindings/video/wm,prizm-ge-rops.txt b/Documentation/devicetree/bindings/video/wm,prizm-ge-rops.txt
new file mode 100644
index 000000000000..a850fa011f02
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/wm,prizm-ge-rops.txt
@@ -0,0 +1,13 @@
+VIA/Wondermedia Graphics Engine Controller
+-----------------------------------------------------
+
+Required properties:
+- compatible : "wm,prizm-ge-rops"
+- reg : Should contain 1 register ranges(address and length)
+
+Example:
+
+ ge_rops@d8050400 {
+ compatible = "wm,prizm-ge-rops";
+ reg = <0xd8050400 0x100>;
+ };
diff --git a/Documentation/devicetree/bindings/video/wm,wm8505-fb.txt b/Documentation/devicetree/bindings/video/wm,wm8505-fb.txt
new file mode 100644
index 000000000000..3d325e1d11ee
--- /dev/null
+++ b/Documentation/devicetree/bindings/video/wm,wm8505-fb.txt
@@ -0,0 +1,23 @@
+Wondermedia WM8505 Framebuffer
+-----------------------------------------------------
+
+Required properties:
+- compatible : "wm,wm8505-fb"
+- reg : Should contain 1 register ranges(address and length)
+- via,display: a phandle pointing to the display node
+
+Required nodes:
+- display: a display node is required to initialize the lcd panel
+ This should be in the board dts. See definition in
+ Documentation/devicetree/bindings/video/via,vt8500-fb.txt
+- default-mode: a videomode node as specified in
+ Documentation/devicetree/bindings/video/via,vt8500-fb.txt
+
+Example:
+
+ fb@d8050800 {
+ compatible = "wm,wm8505-fb";
+ reg = <0xd8050800 0x200>;
+ display = <&display>;
+ default-mode = <&mode0>;
+ };
diff --git a/Documentation/devicetree/bindings/w1/w1-gpio.txt b/Documentation/devicetree/bindings/w1/w1-gpio.txt
new file mode 100644
index 000000000000..6e09c35d9f1a
--- /dev/null
+++ b/Documentation/devicetree/bindings/w1/w1-gpio.txt
@@ -0,0 +1,22 @@
+w1-gpio devicetree bindings
+
+Required properties:
+
+ - compatible: "w1-gpio"
+ - gpios: one or two GPIO specs:
+ - the first one is used as data I/O pin
+ - the second one is optional. If specified, it is used as
+ enable pin for an external pin pullup.
+
+Optional properties:
+
+ - linux,open-drain: if specified, the data pin is considered in
+ open-drain mode.
+
+Examples:
+
+ onewire@0 {
+ compatible = "w1-gpio";
+ gpios = <&gpio 126 0>, <&gpio 105 0>;
+ };
+
diff --git a/Documentation/dontdiff b/Documentation/dontdiff
index 39462cf35cd4..74c25c8d8884 100644
--- a/Documentation/dontdiff
+++ b/Documentation/dontdiff
@@ -162,7 +162,6 @@ mach-types.h
machtypes.h
map
map_hugetlb
-maui_boot.h
media
mconf
miboot*
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
deleted file mode 100644
index f4d8c7105fcd..000000000000
--- a/Documentation/feature-removal-schedule.txt
+++ /dev/null
@@ -1,639 +0,0 @@
-The following is a list of files and features that are going to be
-removed in the kernel source tree. Every entry should contain what
-exactly is going away, why it is happening, and who is going to be doing
-the work. When the feature is removed from the kernel, it should also
-be removed from this file. The suggested deprecation period is 3 releases.
-
----------------------------
-
-What: ddebug_query="query" boot cmdline param
-When: v3.8
-Why: obsoleted by dyndbg="query" and module.dyndbg="query"
-Who: Jim Cromie <jim.cromie@gmail.com>, Jason Baron <jbaron@redhat.com>
-
----------------------------
-
-What: /proc/sys/vm/nr_pdflush_threads
-When: 2012
-Why: Since pdflush is deprecated, the interface exported in /proc/sys/vm/
- should be removed.
-Who: Wanpeng Li <liwp@linux.vnet.ibm.com>
-
----------------------------
-
-What: CONFIG_APM_CPU_IDLE, and its ability to call APM BIOS in idle
-When: 2012
-Why: This optional sub-feature of APM is of dubious reliability,
- and ancient APM laptops are likely better served by calling HLT.
- Deleting CONFIG_APM_CPU_IDLE allows x86 to stop exporting
- the pm_idle function pointer to modules.
-Who: Len Brown <len.brown@intel.com>
-
-----------------------------
-
-What: x86_32 "no-hlt" cmdline param
-When: 2012
-Why: remove a branch from idle path, simplify code used by everybody.
- This option disabled the use of HLT in idle and machine_halt()
- for hardware that was flakey 15-years ago. Today we have
- "idle=poll" that removed HLT from idle, and so if such a machine
- is still running the upstream kernel, "idle=poll" is likely sufficient.
-Who: Len Brown <len.brown@intel.com>
-
-----------------------------
-
-What: x86 "idle=mwait" cmdline param
-When: 2012
-Why: simplify x86 idle code
-Who: Len Brown <len.brown@intel.com>
-
-----------------------------
-
-What: PRISM54
-When: 2.6.34
-
-Why: prism54 FullMAC PCI / Cardbus devices used to be supported only by the
- prism54 wireless driver. After Intersil stopped selling these
- devices in preference for the newer more flexible SoftMAC devices
- a SoftMAC device driver was required and prism54 did not support
- them. The p54pci driver now exists and has been present in the kernel for
- a while. This driver supports both SoftMAC devices and FullMAC devices.
- The main difference between these devices was the amount of memory which
- could be used for the firmware. The SoftMAC devices support a smaller
- amount of memory. Because of this the SoftMAC firmware fits into FullMAC
- devices's memory. p54pci supports not only PCI / Cardbus but also USB
- and SPI. Since p54pci supports all devices prism54 supports
- you will have a conflict. I'm not quite sure how distributions are
- handling this conflict right now. prism54 was kept around due to
- claims users may experience issues when using the SoftMAC driver.
- Time has passed users have not reported issues. If you use prism54
- and for whatever reason you cannot use p54pci please let us know!
- E-mail us at: linux-wireless@vger.kernel.org
-
- For more information see the p54 wiki page:
-
- http://wireless.kernel.org/en/users/Drivers/p54
-
-Who: Luis R. Rodriguez <lrodriguez@atheros.com>
-
----------------------------
-
-What: The ieee80211_regdom module parameter
-When: March 2010 / desktop catchup
-
-Why: This was inherited by the CONFIG_WIRELESS_OLD_REGULATORY code,
- and currently serves as an option for users to define an
- ISO / IEC 3166 alpha2 code for the country they are currently
- present in. Although there are userspace API replacements for this
- through nl80211 distributions haven't yet caught up with implementing
- decent alternatives through standard GUIs. Although available as an
- option through iw or wpa_supplicant its just a matter of time before
- distributions pick up good GUI options for this. The ideal solution
- would actually consist of intelligent designs which would do this for
- the user automatically even when travelling through different countries.
- Until then we leave this module parameter as a compromise.
-
- When userspace improves with reasonable widely-available alternatives for
- this we will no longer need this module parameter. This entry hopes that
- by the super-futuristically looking date of "March 2010" we will have
- such replacements widely available.
-
-Who: Luis R. Rodriguez <lrodriguez@atheros.com>
-
----------------------------
-
-What: dev->power.power_state
-When: July 2007
-Why: Broken design for runtime control over driver power states, confusing
- driver-internal runtime power management with: mechanisms to support
- system-wide sleep state transitions; event codes that distinguish
- different phases of swsusp "sleep" transitions; and userspace policy
- inputs. This framework was never widely used, and most attempts to
- use it were broken. Drivers should instead be exposing domain-specific
- interfaces either to kernel or to userspace.
-Who: Pavel Machek <pavel@ucw.cz>
-
----------------------------
-
-What: /proc/<pid>/oom_adj
-When: August 2012
-Why: /proc/<pid>/oom_adj allows userspace to influence the oom killer's
- badness heuristic used to determine which task to kill when the kernel
- is out of memory.
-
- The badness heuristic has since been rewritten since the introduction of
- this tunable such that its meaning is deprecated. The value was
- implemented as a bitshift on a score generated by the badness()
- function that did not have any precise units of measure. With the
- rewrite, the score is given as a proportion of available memory to the
- task allocating pages, so using a bitshift which grows the score
- exponentially is, thus, impossible to tune with fine granularity.
-
- A much more powerful interface, /proc/<pid>/oom_score_adj, was
- introduced with the oom killer rewrite that allows users to increase or
- decrease the badness score linearly. This interface will replace
- /proc/<pid>/oom_adj.
-
- A warning will be emitted to the kernel log if an application uses this
- deprecated interface. After it is printed once, future warnings will be
- suppressed until the kernel is rebooted.
-
----------------------------
-
-What: remove EXPORT_SYMBOL(kernel_thread)
-When: August 2006
-Files: arch/*/kernel/*_ksyms.c
-Check: kernel_thread
-Why: kernel_thread is a low-level implementation detail. Drivers should
- use the <linux/kthread.h> API instead which shields them from
- implementation details and provides a higherlevel interface that
- prevents bugs and code duplication
-Who: Christoph Hellwig <hch@lst.de>
-
----------------------------
-
-What: Unused EXPORT_SYMBOL/EXPORT_SYMBOL_GPL exports
- (temporary transition config option provided until then)
- The transition config option will also be removed at the same time.
-When: before 2.6.19
-Why: Unused symbols are both increasing the size of the kernel binary
- and are often a sign of "wrong API"
-Who: Arjan van de Ven <arjan@linux.intel.com>
-
----------------------------
-
-What: PHYSDEVPATH, PHYSDEVBUS, PHYSDEVDRIVER in the uevent environment
-When: October 2008
-Why: The stacking of class devices makes these values misleading and
- inconsistent.
- Class devices should not carry any of these properties, and bus
- devices have SUBSYTEM and DRIVER as a replacement.
-Who: Kay Sievers <kay.sievers@suse.de>
-
----------------------------
-
-What: ACPI procfs interface
-When: July 2008
-Why: ACPI sysfs conversion should be finished by January 2008.
- ACPI procfs interface will be removed in July 2008 so that
- there is enough time for the user space to catch up.
-Who: Zhang Rui <rui.zhang@intel.com>
-
----------------------------
-
-What: CONFIG_ACPI_PROCFS_POWER
-When: 2.6.39
-Why: sysfs I/F for ACPI power devices, including AC and Battery,
- has been working in upstream kernel since 2.6.24, Sep 2007.
- In 2.6.37, we make the sysfs I/F always built in and this option
- disabled by default.
- Remove this option and the ACPI power procfs interface in 2.6.39.
-Who: Zhang Rui <rui.zhang@intel.com>
-
----------------------------
-
-What: /proc/acpi/event
-When: February 2008
-Why: /proc/acpi/event has been replaced by events via the input layer
- and netlink since 2.6.23.
-Who: Len Brown <len.brown@intel.com>
-
----------------------------
-
-What: i386/x86_64 bzImage symlinks
-When: April 2010
-
-Why: The i386/x86_64 merge provides a symlink to the old bzImage
- location so not yet updated user space tools, e.g. package
- scripts, do not break.
-Who: Thomas Gleixner <tglx@linutronix.de>
-
----------------------------
-
-What: GPIO autorequest on gpio_direction_{input,output}() in gpiolib
-When: February 2010
-Why: All callers should use explicit gpio_request()/gpio_free().
- The autorequest mechanism in gpiolib was provided mostly as a
- migration aid for legacy GPIO interfaces (for SOC based GPIOs).
- Those users have now largely migrated. Platforms implementing
- the GPIO interfaces without using gpiolib will see no changes.
-Who: David Brownell <dbrownell@users.sourceforge.net>
----------------------------
-
-What: b43 support for firmware revision < 410
-When: The schedule was July 2008, but it was decided that we are going to keep the
- code as long as there are no major maintanance headaches.
- So it _could_ be removed _any_ time now, if it conflicts with something new.
-Why: The support code for the old firmware hurts code readability/maintainability
- and slightly hurts runtime performance. Bugfixes for the old firmware
- are not provided by Broadcom anymore.
-Who: Michael Buesch <m@bues.ch>
-
----------------------------
-
-What: Ability for non root users to shm_get hugetlb pages based on mlock
- resource limits
-When: 2.6.31
-Why: Non root users need to be part of /proc/sys/vm/hugetlb_shm_group or
- have CAP_IPC_LOCK to be able to allocate shm segments backed by
- huge pages. The mlock based rlimit check to allow shm hugetlb is
- inconsistent with mmap based allocations. Hence it is being
- deprecated.
-Who: Ravikiran Thirumalai <kiran@scalex86.org>
-
----------------------------
-
-What: sysfs ui for changing p4-clockmod parameters
-When: September 2009
-Why: See commits 129f8ae9b1b5be94517da76009ea956e89104ce8 and
- e088e4c9cdb618675874becb91b2fd581ee707e6.
- Removal is subject to fixing any remaining bugs in ACPI which may
- cause the thermal throttling not to happen at the right time.
-Who: Dave Jones <davej@redhat.com>, Matthew Garrett <mjg@redhat.com>
-
------------------------------
-
-What: fakephp and associated sysfs files in /sys/bus/pci/slots/
-When: 2011
-Why: In 2.6.27, the semantics of /sys/bus/pci/slots was redefined to
- represent a machine's physical PCI slots. The change in semantics
- had userspace implications, as the hotplug core no longer allowed
- drivers to create multiple sysfs files per physical slot (required
- for multi-function devices, e.g.). fakephp was seen as a developer's
- tool only, and its interface changed. Too late, we learned that
- there were some users of the fakephp interface.
-
- In 2.6.30, the original fakephp interface was restored. At the same
- time, the PCI core gained the ability that fakephp provided, namely
- function-level hot-remove and hot-add.
-
- Since the PCI core now provides the same functionality, exposed in:
-
- /sys/bus/pci/rescan
- /sys/bus/pci/devices/.../remove
- /sys/bus/pci/devices/.../rescan
-
- there is no functional reason to maintain fakephp as well.
-
- We will keep the existing module so that 'modprobe fakephp' will
- present the old /sys/bus/pci/slots/... interface for compatibility,
- but users are urged to migrate their applications to the API above.
-
- After a reasonable transition period, we will remove the legacy
- fakephp interface.
-Who: Alex Chiang <achiang@hp.com>
-
----------------------------
-
-What: CONFIG_RFKILL_INPUT
-When: 2.6.33
-Why: Should be implemented in userspace, policy daemon.
-Who: Johannes Berg <johannes@sipsolutions.net>
-
-----------------------------
-
-What: sound-slot/service-* module aliases and related clutters in
- sound/sound_core.c
-When: August 2010
-Why: OSS sound_core grabs all legacy minors (0-255) of SOUND_MAJOR
- (14) and requests modules using custom sound-slot/service-*
- module aliases. The only benefit of doing this is allowing
- use of custom module aliases which might as well be considered
- a bug at this point. This preemptive claiming prevents
- alternative OSS implementations.
-
- Till the feature is removed, the kernel will be requesting
- both sound-slot/service-* and the standard char-major-* module
- aliases and allow turning off the pre-claiming selectively via
- CONFIG_SOUND_OSS_CORE_PRECLAIM and soundcore.preclaim_oss
- kernel parameter.
-
- After the transition phase is complete, both the custom module
- aliases and switches to disable it will go away. This removal
- will also allow making ALSA OSS emulation independent of
- sound_core. The dependency will be broken then too.
-Who: Tejun Heo <tj@kernel.org>
-
-----------------------------
-
-What: sysfs-class-rfkill state file
-When: Feb 2014
-Files: net/rfkill/core.c
-Why: Documented as obsolete since Feb 2010. This file is limited to 3
- states while the rfkill drivers can have 4 states.
-Who: anybody or Florian Mickler <florian@mickler.org>
-
-----------------------------
-
-What: sysfs-class-rfkill claim file
-When: Feb 2012
-Files: net/rfkill/core.c
-Why: It is not possible to claim an rfkill driver since 2007. This is
- Documented as obsolete since Feb 2010.
-Who: anybody or Florian Mickler <florian@mickler.org>
-
-----------------------------
-
-What: iwlwifi 50XX module parameters
-When: 3.0
-Why: The "..50" modules parameters were used to configure 5000 series and
- up devices; different set of module parameters also available for 4965
- with same functionalities. Consolidate both set into single place
- in drivers/net/wireless/iwlwifi/iwl-agn.c
-
-Who: Wey-Yi Guy <wey-yi.w.guy@intel.com>
-
-----------------------------
-
-What: iwl4965 alias support
-When: 3.0
-Why: Internal alias support has been present in module-init-tools for some
- time, the MODULE_ALIAS("iwl4965") boilerplate aliases can be removed
- with no impact.
-
-Who: Wey-Yi Guy <wey-yi.w.guy@intel.com>
-
----------------------------
-
-What: xt_NOTRACK
-Files: net/netfilter/xt_NOTRACK.c
-When: April 2011
-Why: Superseded by xt_CT
-Who: Netfilter developer team <netfilter-devel@vger.kernel.org>
-
-----------------------------
-
-What: IRQF_DISABLED
-When: 2.6.36
-Why: The flag is a NOOP as we run interrupt handlers with interrupts disabled
-Who: Thomas Gleixner <tglx@linutronix.de>
-
-----------------------------
-
-What: PCI DMA unmap state API
-When: August 2012
-Why: PCI DMA unmap state API (include/linux/pci-dma.h) was replaced
- with DMA unmap state API (DMA unmap state API can be used for
- any bus).
-Who: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
-
-----------------------------
-
-What: iwlwifi disable_hw_scan module parameters
-When: 3.0
-Why: Hareware scan is the prefer method for iwlwifi devices for
- scanning operation. Remove software scan support for all the
- iwlwifi devices.
-
-Who: Wey-Yi Guy <wey-yi.w.guy@intel.com>
-
-----------------------------
-
-What: Legacy, non-standard chassis intrusion detection interface.
-When: June 2011
-Why: The adm9240, w83792d and w83793 hardware monitoring drivers have
- legacy interfaces for chassis intrusion detection. A standard
- interface has been added to each driver, so the legacy interface
- can be removed.
-Who: Jean Delvare <khali@linux-fr.org>
-
-----------------------------
-
-What: i2c_driver.attach_adapter
- i2c_driver.detach_adapter
-When: September 2011
-Why: These legacy callbacks should no longer be used as i2c-core offers
- a variety of preferable alternative ways to instantiate I2C devices.
-Who: Jean Delvare <khali@linux-fr.org>
-
-----------------------------
-
-What: Opening a radio device node will no longer automatically switch the
- tuner mode from tv to radio.
-When: 3.3
-Why: Just opening a V4L device should not change the state of the hardware
- like that. It's very unexpected and against the V4L spec. Instead, you
- switch to radio mode by calling VIDIOC_S_FREQUENCY. This is the second
- and last step of the move to consistent handling of tv and radio tuners.
-Who: Hans Verkuil <hans.verkuil@cisco.com>
-
-----------------------------
-
-What: CONFIG_CFG80211_WEXT
-When: as soon as distributions ship new wireless tools, ie. wpa_supplicant 1.0
- and NetworkManager/connman/etc. that are able to use nl80211
-Why: Wireless extensions are deprecated, and userland tools are moving to
- using nl80211. New drivers are no longer using wireless extensions,
- and while there might still be old drivers, both new drivers and new
- userland no longer needs them and they can't be used for an feature
- developed in the past couple of years. As such, compatibility with
- wireless extensions in new drivers will be removed.
-Who: Johannes Berg <johannes@sipsolutions.net>
-
-----------------------------
-
-What: g_file_storage driver
-When: 3.8
-Why: This driver has been superseded by g_mass_storage.
-Who: Alan Stern <stern@rowland.harvard.edu>
-
-----------------------------
-
-What: threeg and interface sysfs files in /sys/devices/platform/acer-wmi
-When: 2012
-Why: In 3.0, we can now autodetect internal 3G device and already have
- the threeg rfkill device. So, we plan to remove threeg sysfs support
- for it's no longer necessary.
-
- We also plan to remove interface sysfs file that exposed which ACPI-WMI
- interface that was used by acer-wmi driver. It will replaced by
- information log when acer-wmi initial.
-Who: Lee, Chun-Yi <jlee@novell.com>
-
----------------------------
-
-What: /sys/devices/platform/_UDC_/udc/_UDC_/is_dualspeed file and
- is_dualspeed line in /sys/devices/platform/ci13xxx_*/udc/device file.
-When: 3.8
-Why: The is_dualspeed file is superseded by maximum_speed in the same
- directory and is_dualspeed line in device file is superseded by
- max_speed line in the same file.
-
- The maximum_speed/max_speed specifies maximum speed supported by UDC.
- To check if dualspeeed is supported, check if the value is >= 3.
- Various possible speeds are defined in <linux/usb/ch9.h>.
-Who: Michal Nazarewicz <mina86@mina86.com>
-
-----------------------------
-
-What: The XFS nodelaylog mount option
-When: 3.3
-Why: The delaylog mode that has been the default since 2.6.39 has proven
- stable, and the old code is in the way of additional improvements in
- the log code.
-Who: Christoph Hellwig <hch@lst.de>
-
-----------------------------
-
-What: iwlagn alias support
-When: 3.5
-Why: The iwlagn module has been renamed iwlwifi. The alias will be around
- for backward compatibility for several cycles and then dropped.
-Who: Don Fry <donald.h.fry@intel.com>
-
-----------------------------
-
-What: pci_scan_bus_parented()
-When: 3.5
-Why: The pci_scan_bus_parented() interface creates a new root bus. The
- bus is created with default resources (ioport_resource and
- iomem_resource) that are always wrong, so we rely on arch code to
- correct them later. Callers of pci_scan_bus_parented() should
- convert to using pci_scan_root_bus() so they can supply a list of
- bus resources when the bus is created.
-Who: Bjorn Helgaas <bhelgaas@google.com>
-
-----------------------------
-
-What: Low Performance USB Block driver ("CONFIG_BLK_DEV_UB")
-When: 3.6
-Why: This driver provides support for USB storage devices like "USB
- sticks". As of now, it is deactivated in Debian, Fedora and
- Ubuntu. All current users can switch over to usb-storage
- (CONFIG_USB_STORAGE) which only drawback is the additional SCSI
- stack.
-Who: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
-
-----------------------------
-
-What: get_robust_list syscall
-When: 2013
-Why: There appear to be no production users of the get_robust_list syscall,
- and it runs the risk of leaking address locations, allowing the bypass
- of ASLR. It was only ever intended for debugging, so it should be
- removed.
-Who: Kees Cook <keescook@chromium.org>
-
-----------------------------
-
-What: Removing the pn544 raw driver.
-When: 3.6
-Why: With the introduction of the NFC HCI and SHDL kernel layers, pn544.c
- is being replaced by pn544_hci.c which is accessible through the netlink
- and socket NFC APIs. Moreover, pn544.c is outdated and does not seem to
- work properly with the latest Android stacks.
- Having 2 drivers for the same hardware is confusing and as such we
- should only keep the one following the kernel NFC APIs.
-Who: Samuel Ortiz <sameo@linux.intel.com>
-
-----------------------------
-
-What: setitimer accepts user NULL pointer (value)
-When: 3.6
-Why: setitimer is not returning -EFAULT if user pointer is NULL. This
- violates the spec.
-Who: Sasikantha Babu <sasikanth.v19@gmail.com>
-
-----------------------------
-
-What: remove bogus DV presets V4L2_DV_1080I29_97, V4L2_DV_1080I30 and
- V4L2_DV_1080I25
-When: 3.6
-Why: These HDTV formats do not exist and were added by a confused mind
- (that was me, to be precise...)
-Who: Hans Verkuil <hans.verkuil@cisco.com>
-
-----------------------------
-
-What: V4L2_CID_HCENTER, V4L2_CID_VCENTER V4L2 controls
-When: 3.7
-Why: The V4L2_CID_VCENTER, V4L2_CID_HCENTER controls have been deprecated
- for about 4 years and they are not used by any mainline driver.
- There are newer controls (V4L2_CID_PAN*, V4L2_CID_TILT*) that provide
- similar functionality.
-Who: Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
-
-----------------------------
-
-What: cgroup option updates via remount
-When: March 2013
-Why: Remount currently allows changing bound subsystems and
- release_agent. Rebinding is hardly useful as it only works
- when the hierarchy is empty and release_agent itself should be
- replaced with conventional fsnotify.
-
-----------------------------
-
-What: xt_recent rev 0
-When: 2013
-Who: Pablo Neira Ayuso <pablo@netfilter.org>
-Files: net/netfilter/xt_recent.c
-
-----------------------------
-
-What: KVM debugfs statistics
-When: 2013
-Why: KVM tracepoints provide mostly equivalent information in a much more
- flexible fashion.
-
-----------------------------
-
-What: at91-mci driver ("CONFIG_MMC_AT91")
-When: 3.8
-Why: There are two mci drivers: at91-mci and atmel-mci. The PDC support
- was added to atmel-mci as a first step to support more chips.
- Then at91-mci was kept only for old IP versions (on at91rm9200 and
- at91sam9261). The support of these IP versions has just been added
- to atmel-mci, so atmel-mci can be used for all chips.
-Who: Ludovic Desroches <ludovic.desroches@atmel.com>
-
-----------------------------
-
-What: net/wanrouter/
-When: June 2013
-Why: Unsupported/unmaintained/unused since 2.6
-
-----------------------------
-
-What: V4L2 selections API target rectangle and flags unification, the
- following definitions will be removed: V4L2_SEL_TGT_CROP_ACTIVE,
- V4L2_SEL_TGT_COMPOSE_ACTIVE, V4L2_SUBDEV_SEL_*, V4L2_SUBDEV_SEL_FLAG_*
- in favor of common V4L2_SEL_TGT_* and V4L2_SEL_FLAG_* definitions.
- For more details see include/linux/v4l2-common.h.
-When: 3.8
-Why: The regular V4L2 selections and the subdev selection API originally
- defined distinct names for the target rectangles and flags - V4L2_SEL_*
- and V4L2_SUBDEV_SEL_*. Although, it turned out that the meaning of these
- target rectangles is virtually identical and the APIs were consolidated
- to use single set of names - V4L2_SEL_*. This didn't involve any ABI
- changes. Alias definitions were created for the original ones to avoid
- any instabilities in the user space interface. After few cycles these
- backward compatibility definitions will be removed.
-Who: Sylwester Nawrocki <sylvester.nawrocki@gmail.com>
-
-----------------------------
-
-What: Using V4L2_CAP_VIDEO_CAPTURE and V4L2_CAP_VIDEO_OUTPUT flags
- to indicate a V4L2 memory-to-memory device capability
-When: 3.8
-Why: New drivers should use new V4L2_CAP_VIDEO_M2M capability flag
- to indicate a V4L2 video memory-to-memory (M2M) device and
- applications can now identify a M2M video device by checking
- for V4L2_CAP_VIDEO_M2M, with VIDIOC_QUERYCAP ioctl. Using ORed
- V4L2_CAP_VIDEO_CAPTURE and V4L2_CAP_VIDEO_OUTPUT flags for M2M
- devices is ambiguous and may lead, for example, to identifying
- a M2M device as a video capture or output device.
-Who: Sylwester Nawrocki <s.nawrocki@samsung.com>
-
-----------------------------
-
-What: OMAP private DMA implementation
-When: 2013
-Why: We have a DMA engine implementation; all users should be updated
- to use this rather than persisting with the old APIs. The old APIs
- block merging the old DMA engine implementation into the DMA
- engine driver.
-Who: Russell King <linux@arm.linux.org.uk>,
- Santosh Shilimkar <santosh.shilimkar@ti.com>
-
-----------------------------
diff --git a/Documentation/filesystems/debugfs.txt b/Documentation/filesystems/debugfs.txt
index 7a34f827989c..3a863f692728 100644
--- a/Documentation/filesystems/debugfs.txt
+++ b/Documentation/filesystems/debugfs.txt
@@ -15,8 +15,8 @@ Debugfs is typically mounted with a command like:
mount -t debugfs none /sys/kernel/debug
(Or an equivalent /etc/fstab line).
-The debugfs root directory is accessible by anyone by default. To
-restrict access to the tree the "uid", "gid" and "mode" mount
+The debugfs root directory is accessible only to the root user by
+default. To change access to the tree the "uid", "gid" and "mode" mount
options can be used.
Note that the debugfs API is exported GPL-only to modules.
diff --git a/Documentation/hid/hid-sensor.txt b/Documentation/hid/hid-sensor.txt
new file mode 100755
index 000000000000..948b0989c433
--- /dev/null
+++ b/Documentation/hid/hid-sensor.txt
@@ -0,0 +1,140 @@
+
+HID Sensors Framework
+======================
+HID sensor framework provides necessary interfaces to implement sensor drivers,
+which are connected to a sensor hub. The sensor hub is a HID device and it provides
+a report descriptor conforming to HID 1.12 sensor usage tables.
+
+Description from the HID 1.12 "HID Sensor Usages" specification:
+"Standardization of HID usages for sensors would allow (but not require) sensor
+hardware vendors to provide a consistent Plug And Play interface at the USB boundary,
+thereby enabling some operating systems to incorporate common device drivers that
+could be reused between vendors, alleviating any need for the vendors to provide
+the drivers themselves."
+
+This specification describes many usage IDs, which describe the type of sensor
+and also the individual data fields. Each sensor can have variable number of
+data fields. The length and order is specified in the report descriptor. For
+example a part of report descriptor can look like:
+
+ INPUT(1)[INPUT]
+ ..
+ Field(2)
+ Physical(0020.0073)
+ Usage(1)
+ 0020.045f
+ Logical Minimum(-32767)
+ Logical Maximum(32767)
+ Report Size(8)
+ Report Count(1)
+ Report Offset(16)
+ Flags(Variable Absolute)
+..
+..
+
+The report is indicating "sensor page (0x20)" contains an accelerometer-3D (0x73).
+This accelerometer-3D has some fields. Here for example field 2 is motion intensity
+(0x045f) with a logical minimum value of -32767 and logical maximum of 32767. The
+order of fields and length of each field is important as the input event raw
+data will use this format.
+
+
+Implementation
+=================
+
+This specification defines many different types of sensors with different sets of
+data fields. It is difficult to have a common input event to user space applications,
+for different sensors. For example an accelerometer can send X,Y and Z data, whereas
+an ambient light sensor can send illumination data.
+So the implementation has two parts:
+- Core hid driver
+- Individual sensor processing part (sensor drivers)
+
+Core driver
+-----------
+The core driver registers (hid-sensor-hub) registers as a HID driver. It parses
+report descriptors and identifies all the sensors present. It adds an MFD device
+with name HID-SENSOR-xxxx (where xxxx is usage id from the specification).
+For example
+HID-SENSOR-200073 is registered for an Accelerometer 3D driver.
+So if any driver with this name is inserted, then the probe routine for that
+function will be called. So an accelerometer processing driver can register
+with this name and will be probed if there is an accelerometer-3D detected.
+
+The core driver provides a set of APIs which can be used by the processing
+drivers to register and get events for that usage id. Also it provides parsing
+functions, which get and set each input/feature/output report.
+
+Individual sensor processing part (sensor drivers)
+-----------
+The processing driver will use an interface provided by the core driver to parse
+the report and get the indexes of the fields and also can get events. This driver
+can use IIO interface to use the standard ABI defined for a type of sensor.
+
+
+Core driver Interface
+=====================
+
+Callback structure:
+Each processing driver can use this structure to set some callbacks.
+ int (*suspend)(..): Callback when HID suspend is received
+ int (*resume)(..): Callback when HID resume is received
+ int (*capture_sample)(..): Capture a sample for one of its data fields
+ int (*send_event)(..): One complete event is received which can have
+ multiple data fields.
+
+Registration functions:
+int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ struct hid_sensor_hub_callbacks *usage_callback):
+
+Registers callbacks for an usage id. The callback functions are not allowed
+to sleep.
+
+
+int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id):
+
+Removes callbacks for an usage id.
+
+
+Parsing function:
+int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
+ u8 type,
+ u32 usage_id, u32 attr_usage_id,
+ struct hid_sensor_hub_attribute_info *info);
+
+A processing driver can look for some field of interest and check if it exists
+in a report descriptor. If it exists it will store necessary information
+so that fields can be set or get individually.
+These indexes avoid searching every time and getting field index to get or set.
+
+
+Set Feature report
+int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+ u32 field_index, s32 value);
+
+This interface is used to set a value for a field in feature report. For example
+if there is a field report_interval, which is parsed by a call to
+sensor_hub_input_get_attribute_info before, then it can directly set that individual
+field.
+
+
+int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+ u32 field_index, s32 *value);
+
+This interface is used to get a value for a field in input report. For example
+if there is a field report_interval, which is parsed by a call to
+sensor_hub_input_get_attribute_info before, then it can directly get that individual
+field value.
+
+
+int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ u32 attr_usage_id, u32 report_id);
+
+This is used to get a particular field value through input reports. For example
+accelerometer wants to poll X axis value, then it can call this function with
+the usage id of X axis. HID sensors can provide events, so this is not necessary
+to poll for any field. If there is some new sample, the core driver will call
+registered callback function to process the sample.
diff --git a/Documentation/hwmon/adt7410 b/Documentation/hwmon/adt7410
new file mode 100644
index 000000000000..96004000dc2a
--- /dev/null
+++ b/Documentation/hwmon/adt7410
@@ -0,0 +1,51 @@
+Kernel driver adt7410
+=====================
+
+Supported chips:
+ * Analog Devices ADT7410
+ Prefix: 'adt7410'
+ Addresses scanned: I2C 0x48 - 0x4B
+ Datasheet: Publicly available at the Analog Devices website
+ http://www.analog.com/static/imported-files/data_sheets/ADT7410.pdf
+
+Author: Hartmut Knaack <knaack.h@gmx.de>
+
+Description
+-----------
+
+The ADT7410 is a temperature sensor with rated temperature range of -55°C to
++150°C. It has a high accuracy of +/-0.5°C and can be operated at a resolution
+of 13 bits (0.0625°C) or 16 bits (0.0078°C). The sensor provides an INT pin to
+indicate that a minimum or maximum temperature set point has been exceeded, as
+well as a critical temperature (CT) pin to indicate that the critical
+temperature set point has been exceeded. Both pins can be set up with a common
+hysteresis of 0°C - 15°C and a fault queue, ranging from 1 to 4 events. Both
+pins can individually set to be active-low or active-high, while the whole
+device can either run in comparator mode or interrupt mode. The ADT7410
+supports continous temperature sampling, as well as sampling one temperature
+value per second or even justget one sample on demand for power saving.
+Besides, it can completely power down its ADC, if power management is
+required.
+
+Configuration Notes
+-------------------
+
+Since the device uses one hysteresis value, which is an offset to minimum,
+maximum and critical temperature, it can only be set for temp#_max_hyst.
+However, temp#_min_hyst and temp#_crit_hyst show their corresponding
+hysteresis.
+The device is set to 16 bit resolution and comparator mode.
+
+sysfs-Interface
+---------------
+
+temp#_input - temperature input
+temp#_min - temperature minimum setpoint
+temp#_max - temperature maximum setpoint
+temp#_crit - critical temperature setpoint
+temp#_min_hyst - hysteresis for temperature minimum (read-only)
+temp#_max_hyst - hysteresis for temperature maximum (read/write)
+temp#_crit_hyst - hysteresis for critical temperature (read-only)
+temp#_min_alarm - temperature minimum alarm flag
+temp#_max_alarm - temperature maximum alarm flag
+temp#_crit_alarm - critical temperature alarm flag
diff --git a/Documentation/hwmon/ina2xx b/Documentation/hwmon/ina2xx
index f50a6cc27616..03444f9d833f 100644
--- a/Documentation/hwmon/ina2xx
+++ b/Documentation/hwmon/ina2xx
@@ -8,12 +8,24 @@ Supported chips:
Datasheet: Publicly available at the Texas Instruments website
http://www.ti.com/
+ * Texas Instruments INA220
+ Prefix: 'ina220'
+ Addresses: I2C 0x40 - 0x4f
+ Datasheet: Publicly available at the Texas Instruments website
+ http://www.ti.com/
+
* Texas Instruments INA226
Prefix: 'ina226'
Addresses: I2C 0x40 - 0x4f
Datasheet: Publicly available at the Texas Instruments website
http://www.ti.com/
+ * Texas Instruments INA230
+ Prefix: 'ina230'
+ Addresses: I2C 0x40 - 0x4f
+ Datasheet: Publicly available at the Texas Instruments website
+ http://www.ti.com/
+
Author: Lothar Felten <l-felten@ti.com>
Description
@@ -23,7 +35,13 @@ The INA219 is a high-side current shunt and power monitor with an I2C
interface. The INA219 monitors both shunt drop and supply voltage, with
programmable conversion times and filtering.
+The INA220 is a high or low side current shunt and power monitor with an I2C
+interface. The INA220 monitors both shunt drop and supply voltage.
+
The INA226 is a current shunt and power monitor with an I2C interface.
The INA226 monitors both a shunt voltage drop and bus supply voltage.
+The INA230 is a high or low side current shunt and power monitor with an I2C
+interface. The INA230 monitors both a shunt voltage drop and bus supply voltage.
+
The shunt value in micro-ohms can be set via platform data.
diff --git a/Documentation/hwmon/lm70 b/Documentation/hwmon/lm70
index 0d240291e3cc..86d182942c51 100644
--- a/Documentation/hwmon/lm70
+++ b/Documentation/hwmon/lm70
@@ -6,6 +6,10 @@ Supported chips:
Datasheet: http://www.national.com/pf/LM/LM70.html
* Texas Instruments TMP121/TMP123
Information: http://focus.ti.com/docs/prod/folders/print/tmp121.html
+ * National Semiconductor LM71
+ Datasheet: http://www.ti.com/product/LM71
+ * National Semiconductor LM74
+ Datasheet: http://www.ti.com/product/LM74
Author:
Kaiwan N Billimoria <kaiwan@designergraphix.com>
@@ -31,9 +35,11 @@ As a real (in-tree) example of this "SPI protocol driver" interfacing
with a "SPI master controller driver", see drivers/spi/spi_lm70llp.c
and its associated documentation.
-The TMP121/TMP123 are very similar; main differences are 4 wire SPI inter-
-face (read only) and 13-bit temperature data (0.0625 degrees celsius reso-
-lution).
+The LM74 and TMP121/TMP123 are very similar; main difference is 13-bit
+temperature data (0.0625 degrees celsius resolution).
+
+The LM71 is also very similar; main difference is 14-bit temperature
+data (0.03125 degrees celsius resolution).
Thanks to
---------
diff --git a/Documentation/hwmon/max197 b/Documentation/hwmon/max197
new file mode 100644
index 000000000000..8d89b9009df8
--- /dev/null
+++ b/Documentation/hwmon/max197
@@ -0,0 +1,60 @@
+Maxim MAX197 driver
+===================
+
+Author:
+ * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
+
+Supported chips:
+ * Maxim MAX197
+ Prefix: 'max197'
+ Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX197.pdf
+
+ * Maxim MAX199
+ Prefix: 'max199'
+ Datasheet: http://datasheets.maxim-ic.com/en/ds/MAX199.pdf
+
+Description
+-----------
+
+The A/D converters MAX197, and MAX199 are both 8-Channel, Multi-Range, 5V,
+12-Bit DAS with 8+4 Bus Interface and Fault Protection.
+
+The available ranges for the MAX197 are {0,-5V} to 5V, and {0,-10V} to 10V,
+while they are {0,-2V} to 2V, and {0,-4V} to 4V on the MAX199.
+
+Platform data
+-------------
+
+The MAX197 platform data (defined in linux/platform_data/max197.h) should be
+filled with a pointer to a conversion function, defined like:
+
+ int convert(u8 ctrl);
+
+ctrl is the control byte to write to start a new conversion.
+On success, the function must return the 12-bit raw value read from the chip,
+or a negative error code otherwise.
+
+Control byte format:
+
+Bit Name Description
+7,6 PD1,PD0 Clock and Power-Down modes
+5 ACQMOD Internal or External Controlled Acquisition
+4 RNG Full-scale voltage magnitude at the input
+3 BIP Unipolar or Bipolar conversion mode
+2,1,0 A2,A1,A0 Channel
+
+Sysfs interface
+---------------
+
+* in[0-7]_input: The conversion value for the corresponding channel.
+ RO
+
+* in[0-7]_min: The lower limit (in mV) for the corresponding channel.
+ For the MAX197, it will be adjusted to -10000, -5000, or 0.
+ For the MAX199, it will be adjusted to -4000, -2000, or 0.
+ RW
+
+* in[0-7]_max: The higher limit (in mV) for the corresponding channel.
+ For the MAX197, it will be adjusted to 0, 5000, or 10000.
+ For the MAX199, it will be adjusted to 0, 2000, or 4000.
+ RW
diff --git a/Documentation/hwmon/mcp3021 b/Documentation/hwmon/mcp3021
index 325fd87e81b2..74a6b72adf5f 100644
--- a/Documentation/hwmon/mcp3021
+++ b/Documentation/hwmon/mcp3021
@@ -5,18 +5,25 @@ Supported chips:
* Microchip Technology MCP3021
Prefix: 'mcp3021'
Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21805a.pdf
+ * Microchip Technology MCP3221
+ Prefix: 'mcp3221'
+ Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/21732c.pdf
-Author: Mingkai Hu
+Authors:
+ Mingkai Hu
+ Sven Schuchmann <schuchmann@schleissheimer.de>
Description
-----------
-This driver implements support for the Microchip Technology MCP3021 chip.
+This driver implements support for the Microchip Technology MCP3021 and
+MCP3221 chip.
The Microchip Technology Inc. MCP3021 is a successive approximation A/D
-converter (ADC) with 10-bit resolution.
-This device provides one single-ended input with very low power consumption.
-Communication to the MCP3021 is performed using a 2-wire I2C compatible
-interface. Standard (100 kHz) and Fast (400 kHz) I2C modes are available.
-The default I2C device address is 0x4d (contact the Microchip factory for
-additional address options).
+converter (ADC) with 10-bit resolution. The MCP3221 has 12-bit resolution.
+
+These devices provide one single-ended input with very low power consumption.
+Communication to the MCP3021/MCP3221 is performed using a 2-wire I2C
+compatible interface. Standard (100 kHz) and Fast (400 kHz) I2C modes are
+available. The default I2C device address is 0x4d (contact the Microchip
+factory for additional address options).
diff --git a/Documentation/ia64/aliasing-test.c b/Documentation/ia64/aliasing-test.c
index 5caa2af33207..62a190d45f38 100644
--- a/Documentation/ia64/aliasing-test.c
+++ b/Documentation/ia64/aliasing-test.c
@@ -132,6 +132,7 @@ static int read_rom(char *path)
rc = write(fd, "1", 2);
if (rc <= 0) {
+ close(fd);
perror("write");
return -1;
}
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index ad7e2e5088c1..df43807bb5da 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1812,8 +1812,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
noexec=on: enable non-executable mappings (default)
noexec=off: disable non-executable mappings
+ nosmap [X86]
+ Disable SMAP (Supervisor Mode Access Prevention)
+ even if it is supported by processor.
+
nosmep [X86]
- Disable SMEP (Supervisor Mode Execution Protection)
+ Disable SMEP (Supervisor Mode Execution Prevention)
even if it is supported by processor.
noexec32 [X86-64]
@@ -1833,6 +1837,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
and restore using xsave. The kernel will fallback to
enabling legacy floating-point and sse state.
+ eagerfpu= [X86]
+ on enable eager fpu restore
+ off disable eager fpu restore
+ auto selects the default scheme, which automatically
+ enables eagerfpu restore for xsaveopt.
+
nohlt [BUGS=ARM,SH] Tells the kernel that the sleep(SH) or
wfi(ARM) instruction doesn't work correctly and not to
use it. This is also useful when using JTAG debugger.
@@ -2385,6 +2395,17 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
rcutree.rcu_cpu_stall_timeout= [KNL,BOOT]
Set timeout for RCU CPU stall warning messages.
+ rcutree.jiffies_till_first_fqs= [KNL,BOOT]
+ Set delay from grace-period initialization to
+ first attempt to force quiescent states.
+ Units are jiffies, minimum value is zero,
+ and maximum value is HZ.
+
+ rcutree.jiffies_till_next_fqs= [KNL,BOOT]
+ Set delay between subsequent attempts to force
+ quiescent states. Units are jiffies, minimum
+ value is one, and maximum value is HZ.
+
rcutorture.fqs_duration= [KNL,BOOT]
Set duration of force_quiescent_state bursts.
@@ -2638,9 +2659,6 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
smart2= [HW]
Format: <io1>[,<io2>[,...,<io8>]]
- smp-alt-once [X86-32,SMP] On a hotplug CPU system, only
- attempt to substitute SMP alternatives once at boot.
-
smsc-ircc2.nopnp [HW] Don't use PNP to discover SMC devices
smsc-ircc2.ircc_cfg= [HW] Device configuration I/O port
smsc-ircc2.ircc_sir= [HW] SIR base I/O port
diff --git a/Documentation/kobject.txt b/Documentation/kobject.txt
index 49578cf1aea5..c5182bb2c16c 100644
--- a/Documentation/kobject.txt
+++ b/Documentation/kobject.txt
@@ -284,9 +284,11 @@ instead, it is associated with the ktype. So let us introduce struct
kobj_type:
struct kobj_type {
- void (*release)(struct kobject *);
+ void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops;
- struct attribute **default_attrs;
+ struct attribute **default_attrs;
+ const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
+ const void *(*namespace)(struct kobject *kobj);
};
This structure is used to describe a particular type of kobject (or, more
diff --git a/Documentation/misc-devices/lis3lv02d b/Documentation/misc-devices/lis3lv02d
index f1a4ec840f86..af815b9ba413 100644
--- a/Documentation/misc-devices/lis3lv02d
+++ b/Documentation/misc-devices/lis3lv02d
@@ -4,7 +4,8 @@ Kernel driver lis3lv02d
Supported chips:
* STMicroelectronics LIS3LV02DL, LIS3LV02DQ (12 bits precision)
- * STMicroelectronics LIS302DL, LIS3L02DQ, LIS331DL (8 bits)
+ * STMicroelectronics LIS302DL, LIS3L02DQ, LIS331DL (8 bits) and
+ LIS331DLH (16 bits)
Authors:
Yan Burman <burman.yan@gmail.com>
diff --git a/Documentation/power/swsusp.txt b/Documentation/power/swsusp.txt
index 92341b84250d..0b4b63e7e9b6 100644
--- a/Documentation/power/swsusp.txt
+++ b/Documentation/power/swsusp.txt
@@ -53,7 +53,7 @@ before suspend (it is limited to 500 MB by default).
Article about goals and implementation of Software Suspend for Linux
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Author: G‚ábor Kuti
+Author: Gábor Kuti
Last revised: 2003-10-20 by Pavel Machek
Idea and goals to achieve
diff --git a/Documentation/scheduler/sched-arch.txt b/Documentation/scheduler/sched-arch.txt
index 28aa1075e291..b1b8587b86f0 100644
--- a/Documentation/scheduler/sched-arch.txt
+++ b/Documentation/scheduler/sched-arch.txt
@@ -17,16 +17,6 @@ you must `#define __ARCH_WANT_UNLOCKED_CTXSW` in a header file
Unlocked context switches introduce only a very minor performance
penalty to the core scheduler implementation in the CONFIG_SMP case.
-2. Interrupt status
-By default, the switch_to arch function is called with interrupts
-disabled. Interrupts may be enabled over the call if it is likely to
-introduce a significant interrupt latency by adding the line
-`#define __ARCH_WANT_INTERRUPTS_ON_CTXSW` in the same place as for
-unlocked context switches. This define also implies
-`__ARCH_WANT_UNLOCKED_CTXSW`. See arch/arm/include/asm/system.h for an
-example.
-
-
CPU idle
========
Your cpu_idle routines need to obey the following rules:
diff --git a/Documentation/trace/kprobetrace.txt b/Documentation/trace/kprobetrace.txt
index d0d0bb9e3e25..d68ea5fc812b 100644
--- a/Documentation/trace/kprobetrace.txt
+++ b/Documentation/trace/kprobetrace.txt
@@ -12,7 +12,7 @@ kprobes can probe (this means, all functions body except for __kprobes
functions). Unlike the Tracepoint based event, this can be added and removed
dynamically, on the fly.
-To enable this feature, build your kernel with CONFIG_KPROBE_TRACING=y.
+To enable this feature, build your kernel with CONFIG_KPROBE_EVENT=y.
Similar to the events tracer, this doesn't need to be activated via
current_tracer. Instead of that, add probe points via
diff --git a/Documentation/usb/persist.txt b/Documentation/usb/persist.txt
index 074b159b77c2..35d70eda9ad6 100644
--- a/Documentation/usb/persist.txt
+++ b/Documentation/usb/persist.txt
@@ -155,6 +155,9 @@ If the kernel gets fooled in this way, it's almost certain to cause
data corruption and to crash your system. You'll have no one to blame
but yourself.
+For those devices with avoid_reset_quirk attribute being set, persist
+maybe fail because they may morph after reset.
+
YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK!
That having been said, most of the time there shouldn't be any trouble
diff --git a/Documentation/vfio.txt b/Documentation/vfio.txt
index 0cb6685c8029..8eda3635a17d 100644
--- a/Documentation/vfio.txt
+++ b/Documentation/vfio.txt
@@ -133,7 +133,7 @@ character devices for this group:
$ lspci -n -s 0000:06:0d.0
06:0d.0 0401: 1102:0002 (rev 08)
# echo 0000:06:0d.0 > /sys/bus/pci/devices/0000:06:0d.0/driver/unbind
-# echo 1102 0002 > /sys/bus/pci/drivers/vfio/new_id
+# echo 1102 0002 > /sys/bus/pci/drivers/vfio-pci/new_id
Now we need to look at what other devices are in the group to free
it for use by VFIO:
diff --git a/Documentation/x86/x86_64/boot-options.txt b/Documentation/x86/x86_64/boot-options.txt
index c54b4f503e2a..de38429beb71 100644
--- a/Documentation/x86/x86_64/boot-options.txt
+++ b/Documentation/x86/x86_64/boot-options.txt
@@ -50,6 +50,13 @@ Machine check
monarchtimeout:
Sets the time in us to wait for other CPUs on machine checks. 0
to disable.
+ mce=bios_cmci_threshold
+ Don't overwrite the bios-set CMCI threshold. This boot option
+ prevents Linux from overwriting the CMCI threshold set by the
+ bios. Without this option, Linux always sets the CMCI
+ threshold to 1. Enabling this may make memory predictive failure
+ analysis less effective if the bios sets thresholds for memory
+ errors since we will not see details for all errors.
nomce (for compatibility with i386): same as mce=off
diff --git a/Documentation/zh_CN/arm/Booting b/Documentation/zh_CN/arm/Booting
new file mode 100644
index 000000000000..6158a64df80c
--- /dev/null
+++ b/Documentation/zh_CN/arm/Booting
@@ -0,0 +1,175 @@
+Chinese translated version of Documentation/arm/Booting
+
+If you have any comment or update to the content, please contact the
+original document maintainer directly. However, if you have a problem
+communicating in English you can also ask the Chinese maintainer for
+help. Contact the Chinese maintainer if this translation is outdated
+or if there is a problem with the translation.
+
+Maintainer: Russell King <linux@arm.linux.org.uk>
+Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
+---------------------------------------------------------------------
+Documentation/arm/Booting 的中文翻译
+
+如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
+交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
+译存在问题,请联系中文版维护者。
+
+英文版维护者: Russell King <linux@arm.linux.org.uk>
+中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+
+以下为正文
+---------------------------------------------------------------------
+
+ 启动 ARM Linux
+ ==============
+
+作者:Russell King
+日期:2002年5月18日
+
+以下文档适用于 2.4.18-rmk6 及以上版本。
+
+为了启动 ARM Linux,你需要一个引导装载程序(boot loader),
+它是一个在主内核启动前运行的一个小程序。引导装载程序需要初始化各种
+设备,并最终调用 Linux 内核,将信息传递给内核。
+
+从本质上讲,引导装载程序应提供(至少)以下功能:
+
+1、设置和初始化 RAM。
+2、初始化一个串口。
+3、检测机器的类型(machine type)。
+4、设置内核标签列表(tagged list)。
+5、调用内核映像。
+
+
+1、设置和初始化 RAM
+-------------------
+
+现有的引导加载程序: 强制
+新开发的引导加载程序: 强制
+
+引导装载程序应该找到并初始化系统中所有内核用于保持系统变量数据的 RAM。
+这个操作的执行是设备依赖的。(它可能使用内部算法来自动定位和计算所有
+RAM,或可能使用对这个设备已知的 RAM 信息,还可能使用任何引导装载程序
+设计者想到的匹配方法。)
+
+
+2、初始化一个串口
+-----------------------------
+
+现有的引导加载程序: 可选、建议
+新开发的引导加载程序: 可选、建议
+
+引导加载程序应该初始化并使能一个目标板上的串口。这允许内核串口驱动
+自动检测哪个串口用于内核控制台。(一般用于调试或与目标板通信。)
+
+作为替代方案,引导加载程序也可以通过标签列表传递相关的'console='
+选项给内核以指定某个串口,而串口数据格式的选项在以下文档中描述:
+
+ Documentation/kernel-parameters.txt。
+
+
+3、检测机器类型
+--------------------------
+
+现有的引导加载程序: 可选
+新开发的引导加载程序: 强制
+
+引导加载程序应该通过某些方式检测自身所处的机器类型。这是一个硬件
+代码或通过查看所连接的硬件用某些算法得到,这些超出了本文档的范围。
+引导加载程序最终必须能提供一个 MACH_TYPE_xxx 值给内核。
+(详见 linux/arch/arm/tools/mach-types )。
+
+4、设置启动数据
+------------------
+
+现有的引导加载程序: 可选、强烈建议
+新开发的引导加载程序: 强制
+
+引导加载程序必须提供标签列表或者 dtb 映像以传递配置数据给内核。启动
+数据的物理地址通过寄存器 r2 传递给内核。
+
+4a、设置内核标签列表
+--------------------------------
+
+bootloader 必须创建和初始化内核标签列表。一个有效的标签列表以
+ATAG_CORE 标签开始,并以 ATAG_NONE 标签结束。ATAG_CORE 标签可以是
+空的,也可以是非空。一个空 ATAG_CORE 标签其 size 域设置为
+‘2’(0x00000002)。ATAG_NONE 标签的 size 域必须设置为零。
+
+在列表中可以保存任意数量的标签。对于一个重复的标签是追加到之前标签
+所携带的信息之后,还是会覆盖原来的信息,是未定义的。某些标签的行为
+是前者,其他是后者。
+
+bootloader 必须传递一个系统内存的位置和最小值,以及根文件系统位置。
+因此,最小的标签列表如下所示:
+
+ +-----------+
+基地址 -> | ATAG_CORE | |
+ +-----------+ |
+ | ATAG_MEM | | 地址增长方向
+ +-----------+ |
+ | ATAG_NONE | |
+ +-----------+ v
+
+标签列表应该保存在系统的 RAM 中。
+
+标签列表必须置于内核自解压和 initrd'bootp' 程序都不会覆盖的内存区。
+建议放在 RAM 的头 16KiB 中。
+
+4b、设置设备树
+-------------------------
+
+bootloader 必须以 64bit 地址对齐的形式加载一个设备树映像(dtb)到系统
+RAM 中,并用启动数据初始化它。dtb 格式在文档
+Documentation/devicetree/booting-without-of.txt 中。内核将会在
+dtb 物理地址处查找 dtb 魔数值(0xd00dfeed),以确定 dtb 是否已经代替
+标签列表被传递进来。
+
+bootloader 必须传递一个系统内存的位置和最小值,以及根文件系统位置。
+dtb 必须置于内核自解压不会覆盖的内存区。建议将其放置于 RAM 的头 16KiB
+中。但是不可将其放置于“0”物理地址处,因为内核认为:r2 中为 0,意味着
+没有标签列表和 dtb 传递过来。
+
+5、调用内核映像
+---------------------------
+
+现有的引导加载程序: 强制
+新开发的引导加载程序: 强制
+
+调用内核映像 zImage 有两个选择。如果 zImge 保存在 flash 中,且是为了
+在 flash 中直接运行而被正确链接的。这样引导加载程序就可以在 flash 中
+直接调用 zImage。
+
+zImage 也可以被放在系统 RAM(任意位置)中被调用。注意:内核使用映像
+基地址的前 16KB RAM 空间来保存页表。建议将映像置于 RAM 的 32KB 处。
+
+对于以上任意一种情况,都必须符合以下启动状态:
+
+- 停止所有 DMA 设备,这样内存数据就不会因为虚假网络包或磁盘数据而被破坏。
+ 这可能可以节省你许多的调试时间。
+
+- CPU 寄存器配置
+ r0 = 0,
+ r1 = (在上面 3 中获取的)机器类型码。
+ r2 = 标签列表在系统 RAM 中的物理地址,或
+ 设备树块(dtb)在系统 RAM 中的物理地址
+
+- CPU 模式
+ 所有形式的中断必须被禁止 (IRQs 和 FIQs)
+ CPU 必须处于 SVC 模式。(对于 Angel 调试有特例存在)
+
+- 缓存,MMUs
+ MMU 必须关闭。
+ 指令缓存开启或关闭都可以。
+ 数据缓存必须关闭。
+
+- 引导加载程序应该通过直接跳转到内核映像的第一条指令来调用内核映像。
+
+ 对于支持 ARM 指令集的 CPU,跳入内核入口时必须处在 ARM 状态,即使
+ 对于 Thumb-2 内核也是如此。
+
+ 对于仅支持 Thumb 指令集的 CPU,比如 Cortex-M 系列的 CPU,跳入
+ 内核入口时必须处于 Thumb 状态。
diff --git a/Documentation/zh_CN/basic_profiling.txt b/Documentation/zh_CN/basic_profiling.txt
new file mode 100644
index 000000000000..1e6bf0bdf8f5
--- /dev/null
+++ b/Documentation/zh_CN/basic_profiling.txt
@@ -0,0 +1,71 @@
+Chinese translated version of Documentation/basic_profiling
+
+If you have any comment or update to the content, please post to LKML directly.
+However, if you have problem communicating in English you can also ask the
+Chinese maintainer for help. Contact the Chinese maintainer, if this
+translation is outdated or there is problem with translation.
+
+Chinese maintainer: Liang Xie <xieliang@xiaomi.com>
+---------------------------------------------------------------------
+Documentation/basic_profiling的中文翻译
+
+如果想评论或更新本文的内容,请直接发信到LKML。如果你使用英文交流有困难的话,也可
+以向中文版维护者求助。如果本翻译更新不及时或者翻译存在问题,请联系中文版维护者。
+
+中文版维护者: 谢良 Liang Xie <xieliang007@gmail.com>
+中文版翻译者: 谢良 Liang Xie <xieliang007@gmail.com>
+中文版校译者:
+以下为正文
+---------------------------------------------------------------------
+
+下面这些说明指令都是非常基础的,如果你想进一步了解请阅读相关专业文档:)
+请不要再在本文档增加新的内容,但可以修复文档中的错误:)(mbligh@aracnet.com)
+感谢John Levon,Dave Hansen等在撰写时的帮助
+
+<test> 用于表示要测量的目标
+请先确保您已经有正确的System.map / vmlinux配置!
+
+对于linux系统来说,配置vmlinuz最容易的方法可能就是使用“make install”,然后修改
+/sbin/installkernel将vmlinux拷贝到/boot目录,而System.map通常是默认安装好的
+
+Readprofile
+-----------
+2.6系列内核需要版本相对较新的readprofile,比如util-linux 2.12a中包含的,可以从:
+
+http://www.kernel.org/pub/linux/utils/util-linux/ 下载
+
+大部分linux发行版已经包含了.
+
+启用readprofile需要在kernel启动命令行增加”profile=2“
+
+clear readprofile -r
+ <test>
+dump output readprofile -m /boot/System.map > captured_profile
+
+Oprofile
+--------
+
+从http://oprofile.sourceforge.net/获取源代码(请参考Changes以获取匹配的版本)
+在kernel启动命令行增加“idle=poll”
+
+配置CONFIG_PROFILING=y和CONFIG_OPROFILE=y然后重启进入新kernel
+
+./configure --with-kernel-support
+make install
+
+想得到好的测量结果,请确保启用了本地APIC特性。如果opreport显示有0Hz CPU,
+说明APIC特性没有开启。另外注意idle=poll选项可能有损性能。
+
+One time setup:
+ opcontrol --setup --vmlinux=/boot/vmlinux
+
+clear opcontrol --reset
+start opcontrol --start
+ <test>
+stop opcontrol --stop
+dump output opreport > output_file
+
+如果只看kernel相关的报告结果,请运行命令 opreport -l /boot/vmlinux > output_file
+
+通过reset选项可以清理过期统计数据,相当于重启的效果。
+
diff --git a/Documentation/zh_CN/filesystems/sysfs.txt b/Documentation/zh_CN/filesystems/sysfs.txt
new file mode 100644
index 000000000000..e230eaa33122
--- /dev/null
+++ b/Documentation/zh_CN/filesystems/sysfs.txt
@@ -0,0 +1,372 @@
+Chinese translated version of Documentation/filesystems/sysfs.txt
+
+If you have any comment or update to the content, please contact the
+original document maintainer directly. However, if you have a problem
+communicating in English you can also ask the Chinese maintainer for
+help. Contact the Chinese maintainer if this translation is outdated
+or if there is a problem with the translation.
+
+Maintainer: Patrick Mochel <mochel@osdl.org>
+ Mike Murphy <mamurph@cs.clemson.edu>
+Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
+---------------------------------------------------------------------
+Documentation/filesystems/sysfs.txt 的中文翻译
+
+如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
+交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
+译存在问题,请联系中文版维护者。
+英文版维护者: Patrick Mochel <mochel@osdl.org>
+ Mike Murphy <mamurph@cs.clemson.edu>
+中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+
+
+以下为正文
+---------------------------------------------------------------------
+sysfs - 用于导出内核对象(kobject)的文件系统
+
+Patrick Mochel <mochel@osdl.org>
+Mike Murphy <mamurph@cs.clemson.edu>
+
+修订: 16 August 2011
+原始版本: 10 January 2003
+
+
+sysfs 简介:
+~~~~~~~~~~
+
+sysfs 是一个最初基于 ramfs 且位于内存的文件系统。它提供导出内核
+数据结构及其属性,以及它们之间的关联到用户空间的方法。
+
+sysfs 始终与 kobject 的底层结构紧密相关。请阅读
+Documentation/kobject.txt 文档以获得更多关于 kobject 接口的
+信息。
+
+
+使用 sysfs
+~~~~~~~~~~~
+
+只要内核配置中定义了 CONFIG_SYSFS ,sysfs 总是被编译进内核。你可
+通过以下命令挂载它:
+
+ mount -t sysfs sysfs /sys
+
+
+创建目录
+~~~~~~~~
+
+任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建。这个
+目录是作为该 kobject 的父对象所在目录的子目录创建的,以准确地传递
+内核的对象层次到用户空间。sysfs 中的顶层目录代表着内核对象层次的
+共同祖先;例如:某些对象属于某个子系统。
+
+Sysfs 在与其目录关联的 sysfs_dirent 对象中内部保存一个指向实现
+目录的 kobject 的指针。以前,这个 kobject 指针被 sysfs 直接用于
+kobject 文件打开和关闭的引用计数。而现在的 sysfs 实现中,kobject
+引用计数只能通过 sysfs_schedule_callback() 函数直接修改。
+
+
+属性
+~~~~
+
+kobject 的属性可在文件系统中以普通文件的形式导出。Sysfs 为属性定义
+了面向文件 I/O 操作的方法,以提供对内核属性的读写。
+
+
+属性应为 ASCII 码文本文件。以一个文件只存储一个属性值为宜。但一个
+文件只包含一个属性值可能影响效率,所以一个包含相同数据类型的属性值
+数组也被广泛地接受。
+
+混合类型、表达多行数据以及一些怪异的数据格式会遭到强烈反对。这样做是
+很丢脸的,而且其代码会在未通知作者的情况下被重写。
+
+
+一个简单的属性结构定义如下:
+
+struct attribute {
+ char * name;
+ struct module *owner;
+ umode_t mode;
+};
+
+
+int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
+void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);
+
+
+一个单独的属性结构并不包含读写其属性值的方法。子系统最好为增删特定
+对象类型的属性定义自己的属性结构体和封装函数。
+
+例如:驱动程序模型定义的 device_attribute 结构体如下:
+
+struct device_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct device *dev, struct device_attribute *attr,
+ char *buf);
+ ssize_t (*store)(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+};
+
+int device_create_file(struct device *, const struct device_attribute *);
+void device_remove_file(struct device *, const struct device_attribute *);
+
+为了定义设备属性,同时定义了一下辅助宏:
+
+#define DEVICE_ATTR(_name, _mode, _show, _store) \
+struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
+
+例如:声明
+
+static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo);
+
+等同于如下代码:
+
+static struct device_attribute dev_attr_foo = {
+ .attr = {
+ .name = "foo",
+ .mode = S_IWUSR | S_IRUGO,
+ .show = show_foo,
+ .store = store_foo,
+ },
+};
+
+
+子系统特有的回调函数
+~~~~~~~~~~~~~~~~~~~
+
+当一个子系统定义一个新的属性类型时,必须实现一系列的 sysfs 操作,
+以帮助读写调用实现属性所有者的显示和储存方法。
+
+struct sysfs_ops {
+ ssize_t (*show)(struct kobject *, struct attribute *, char *);
+ ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
+};
+
+[子系统应已经定义了一个 struct kobj_type 结构体作为这个类型的
+描述符,并在此保存 sysfs_ops 的指针。更多的信息参见 kobject 的
+文档]
+
+sysfs 会为这个类型调用适当的方法。当一个文件被读写时,这个方法会
+将一般的kobject 和 attribute 结构体指针转换为适当的指针类型后
+调用相关联的函数。
+
+
+示例:
+
+#define to_dev(obj) container_of(obj, struct device, kobj)
+#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
+
+static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
+ char *buf)
+{
+ struct device_attribute *dev_attr = to_dev_attr(attr);
+ struct device *dev = to_dev(kobj);
+ ssize_t ret = -EIO;
+
+ if (dev_attr->show)
+ ret = dev_attr->show(dev, dev_attr, buf);
+ if (ret >= (ssize_t)PAGE_SIZE) {
+ print_symbol("dev_attr_show: %s returned bad count\n",
+ (unsigned long)dev_attr->show);
+ }
+ return ret;
+}
+
+
+
+读写属性数据
+~~~~~~~~~~~~
+
+在声明属性时,必须指定 show() 或 store() 方法,以实现属性的
+读或写。这些方法的类型应该和以下的设备属性定义一样简单。
+
+ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
+ssize_t (*store)(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+
+也就是说,他们应只以一个处理对象、一个属性和一个缓冲指针作为参数。
+
+sysfs 会分配一个大小为 (PAGE_SIZE) 的缓冲区并传递给这个方法。
+Sysfs 将会为每次读写操作调用一次这个方法。这使得这些方法在执行时
+会出现以下的行为:
+
+- 在读方面(read(2)),show() 方法应该填充整个缓冲区。回想属性
+ 应只导出了一个属性值或是一个同类型属性值的数组,所以这个代价将
+ 不会不太高。
+
+ 这使得用户空间可以局部地读和任意的向前搜索整个文件。如果用户空间
+ 向后搜索到零或使用‘0’偏移执行一个pread(2)操作,show()方法将
+ 再次被调用,以重新填充缓存。
+
+- 在写方面(write(2)),sysfs 希望在第一次写操作时得到整个缓冲区。
+ 之后 Sysfs 传递整个缓冲区给 store() 方法。
+
+ 当要写 sysfs 文件时,用户空间进程应首先读取整个文件,修该想要
+ 改变的值,然后回写整个缓冲区。
+
+ 在读写属性值时,属性方法的执行应操作相同的缓冲区。
+
+注记:
+
+- 写操作导致的 show() 方法重载,会忽略当前文件位置。
+
+- 缓冲区应总是 PAGE_SIZE 大小。对于i386,这个值为4096。
+
+- show() 方法应该返回写入缓冲区的字节数,也就是 snprintf()的
+ 返回值。
+
+- show() 应始终使用 snprintf()。
+
+- store() 应返回缓冲区的已用字节数。如果整个缓存都已填满,只需返回
+ count 参数。
+
+- show() 或 store() 可以返回错误值。当得到一个非法值,必须返回一个
+ 错误值。
+
+- 一个传递给方法的对象将会通过 sysfs 调用对象内嵌的引用计数固定在
+ 内存中。尽管如此,对象代表的物理实体(如设备)可能已不存在。如有必要,
+ 应该实现一个检测机制。
+
+一个简单的(未经实验证实的)设备属性实现如下:
+
+static ssize_t show_name(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ return scnprintf(buf, PAGE_SIZE, "%s\n", dev->name);
+}
+
+static ssize_t store_name(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ snprintf(dev->name, sizeof(dev->name), "%.*s",
+ (int)min(count, sizeof(dev->name) - 1), buf);
+ return count;
+}
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, store_name);
+
+
+(注意:真正的实现不允许用户空间设置设备名。)
+
+顶层目录布局
+~~~~~~~~~~~~
+
+sysfs 目录的安排显示了内核数据结构之间的关系。
+
+顶层 sysfs 目录如下:
+
+block/
+bus/
+class/
+dev/
+devices/
+firmware/
+net/
+fs/
+
+devices/ 包含了一个设备树的文件系统表示。他直接映射了内部的内核
+设备树,反映了设备的层次结构。
+
+bus/ 包含了内核中各种总线类型的平面目录布局。每个总线目录包含两个
+子目录:
+
+ devices/
+ drivers/
+
+devices/ 包含了系统中出现的每个设备的符号链接,他们指向 root/ 下的
+设备目录。
+
+drivers/ 包含了每个已为特定总线上的设备而挂载的驱动程序的目录(这里
+假定驱动没有跨越多个总线类型)。
+
+fs/ 包含了一个为文件系统设立的目录。现在每个想要导出属性的文件系统必须
+在 fs/ 下创建自己的层次结构(参见Documentation/filesystems/fuse.txt)。
+
+dev/ 包含两个子目录: char/ 和 block/。在这两个子目录中,有以
+<major>:<minor> 格式命名的符号链接。这些符号链接指向 sysfs 目录
+中相应的设备。/sys/dev 提供一个通过一个 stat(2) 操作结果,查找
+设备 sysfs 接口快捷的方法。
+
+更多有关 driver-model 的特性信息可以在 Documentation/driver-model/
+中找到。
+
+
+TODO: 完成这一节。
+
+
+当前接口
+~~~~~~~~
+
+以下的接口层普遍存在于当前的sysfs中:
+
+- 设备 (include/linux/device.h)
+----------------------------------
+结构体:
+
+struct device_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct device *dev, struct device_attribute *attr,
+ char *buf);
+ ssize_t (*store)(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count);
+};
+
+声明:
+
+DEVICE_ATTR(_name, _mode, _show, _store);
+
+增/删属性:
+
+int device_create_file(struct device *dev, const struct device_attribute * attr);
+void device_remove_file(struct device *dev, const struct device_attribute * attr);
+
+
+- 总线驱动程序 (include/linux/device.h)
+--------------------------------------
+结构体:
+
+struct bus_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct bus_type *, char * buf);
+ ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
+};
+
+声明:
+
+BUS_ATTR(_name, _mode, _show, _store)
+
+增/删属性:
+
+int bus_create_file(struct bus_type *, struct bus_attribute *);
+void bus_remove_file(struct bus_type *, struct bus_attribute *);
+
+
+- 设备驱动程序 (include/linux/device.h)
+-----------------------------------------
+
+结构体:
+
+struct driver_attribute {
+ struct attribute attr;
+ ssize_t (*show)(struct device_driver *, char * buf);
+ ssize_t (*store)(struct device_driver *, const char * buf,
+ size_t count);
+};
+
+声明:
+
+DRIVER_ATTR(_name, _mode, _show, _store)
+
+增/删属性:
+
+int driver_create_file(struct device_driver *, const struct driver_attribute *);
+void driver_remove_file(struct device_driver *, const struct driver_attribute *);
+
+
+文档
+~~~~
+
+sysfs 目录结构以及其中包含的属性定义了一个内核与用户空间之间的 ABI。
+对于任何 ABI,其自身的稳定和适当的文档是非常重要的。所有新的 sysfs
+属性必须在 Documentation/ABI 中有文档。详见 Documentation/ABI/README。
diff --git a/Documentation/zh_CN/gpio.txt b/Documentation/zh_CN/gpio.txt
new file mode 100644
index 000000000000..4fa7b4e6f856
--- /dev/null
+++ b/Documentation/zh_CN/gpio.txt
@@ -0,0 +1,658 @@
+Chinese translated version of Documentation/gpio.txt
+
+If you have any comment or update to the content, please contact the
+original document maintainer directly. However, if you have a problem
+communicating in English you can also ask the Chinese maintainer for
+help. Contact the Chinese maintainer if this translation is outdated
+or if there is a problem with the translation.
+
+Maintainer: Grant Likely <grant.likely@secretlab.ca>
+ Linus Walleij <linus.walleij@linaro.org>
+Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
+---------------------------------------------------------------------
+Documentation/gpio.txt 的中文翻译
+
+如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
+交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
+译存在问题,请联系中文版维护者。
+英文版维护者: Grant Likely <grant.likely@secretlab.ca>
+ Linus Walleij <linus.walleij@linaro.org>
+中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+
+
+以下为正文
+---------------------------------------------------------------------
+GPIO 接口
+
+本文档提供了一个在Linux下访问GPIO的公约概述。
+
+这些函数以 gpio_* 作为前缀。其他的函数不允许使用这样的前缀或相关的
+__gpio_* 前缀。
+
+
+什么是GPIO?
+==========
+"通用输入/输出口"(GPIO)是一个灵活的由软件控制的数字信号。他们可
+由多种芯片提供,且对于从事嵌入式和定制硬件的 Linux 开发者来说是
+比较熟悉。每个GPIO 都代表一个连接到特定引脚或球栅阵列(BGA)封装中
+“球珠”的一个位。电路板原理图显示了 GPIO 与外部硬件的连接关系。
+驱动可以编写成通用代码,以使板级启动代码可传递引脚配置数据给驱动。
+
+片上系统 (SOC) 处理器对 GPIO 有很大的依赖。在某些情况下,每个
+非专用引脚都可配置为 GPIO,且大多数芯片都最少有一些 GPIO。
+可编程逻辑器件(类似 FPGA) 可以方便地提供 GPIO。像电源管理和
+音频编解码器这样的多功能芯片经常留有一些这样的引脚来帮助那些引脚
+匮乏的 SOC。同时还有通过 I2C 或 SPI 串行总线连接的“GPIO扩展器”
+芯片。大多数 PC 的南桥有一些拥有 GPIO 能力的引脚 (只有BIOS
+固件才知道如何使用他们)。
+
+GPIO 的实际功能因系统而异。通常用法有:
+
+ - 输出值可写 (高电平=1,低电平=0)。一些芯片也有如何驱动这些值的选项,
+ 例如只允许输出一个值、支持“线与”及其他取值类似的模式(值得注意的是
+ “开漏”信号)
+
+ - 输入值可读(1、0)。一些芯片支持引脚在配置为“输出”时回读,这对于类似
+ “线与”的情况(以支持双向信号)是非常有用的。GPIO 控制器可能有输入
+ 去毛刺/消抖逻辑,这有时需要软件控制。
+
+ - 输入通常可作为 IRQ 信号,一般是沿触发,但有时是电平触发。这样的 IRQ
+ 可能配置为系统唤醒事件,以将系统从低功耗状态下唤醒。
+
+ - 通常一个 GPIO 根据不同产品电路板的需求,可以配置为输入或输出,也有仅
+ 支持单向的。
+
+ - 大部分 GPIO 可以在持有自旋锁时访问,但是通常由串行总线扩展的 GPIO
+ 不允许持有自旋锁。但某些系统也支持这种类型。
+
+对于给定的电路板,每个 GPIO 都用于某个特定的目的,如监控 MMC/SD 卡的
+插入/移除、检测卡的写保护状态、驱动 LED、配置收发器、模拟串行总线、
+复位硬件看门狗、感知开关状态等等。
+
+
+GPIO 公约
+=========
+注意,这个叫做“公约”,因为这不是强制性的,不遵循这个公约是无伤大雅的,
+因为此时可移植性并不重要。GPIO 常用于板级特定的电路逻辑,甚至可能
+随着电路板的版本而改变,且不可能在不同走线的电路板上使用。仅有在少数
+功能上才具有可移植性,其他功能是平台特定。这也是由于“胶合”的逻辑造成的。
+
+此外,这不需要任何的执行框架,只是一个接口。某个平台可能通过一个简单地
+访问芯片寄存器的内联函数来实现它,其他平台可能通过委托一系列不同的GPIO
+控制器的抽象函数来实现它。(有一些可选的代码能支持这种策略的实现,本文档
+后面会介绍,但作为 GPIO 接口的客户端驱动程序必须与它的实现无关。)
+
+也就是说,如果在他们的平台上支持这个公约,驱动应尽可能的使用它。平台
+必须在 Kconfig 中声明对 GENERIC_GPIO的支持 (布尔型 true),并提供
+一个 <asm/gpio.h> 文件。那些调用标准 GPIO 函数的驱动应该在 Kconfig
+入口中声明依赖GENERIC_GPIO。当驱动包含文件:
+
+ #include <linux/gpio.h>
+
+则 GPIO 函数是可用,无论是“真实代码”还是经优化过的语句。如果你遵守
+这个公约,当你的代码完成后,对其他的开发者来说会更容易看懂和维护。
+
+注意,这些操作包含所用平台的 I/O 屏障代码,驱动无须显式地调用他们。
+
+
+标识 GPIO
+---------
+GPIO 是通过无符号整型来标识的,范围是 0 到 MAX_INT。保留“负”数
+用于其他目的,例如标识信号“在这个板子上不可用”或指示错误。未接触底层
+硬件的代码会忽略这些整数。
+
+平台会定义这些整数的用法,且通常使用 #define 来定义 GPIO,这样
+板级特定的启动代码可以直接关联相应的原理图。相对来说,驱动应该仅使用
+启动代码传递过来的 GPIO 编号,使用 platform_data 保存板级特定
+引脚配置数据 (同时还有其他须要的板级特定数据),避免可能出现的问题。
+
+例如一个平台使用编号 32-159 来标识 GPIO,而在另一个平台使用编号0-63
+标识一组 GPIO 控制器,64-79标识另一类 GPIO 控制器,且在一个含有
+FPGA 的特定板子上使用 80-95。编号不一定要连续,那些平台中,也可以
+使用编号2000-2063来标识一个 I2C 接口的 GPIO 扩展器中的 GPIO。
+
+如果你要初始化一个带有无效 GPIO 编号的结构体,可以使用一些负编码
+(如"-EINVAL"),那将使其永远不会是有效。来测试这样一个结构体中的编号
+是否关联一个 GPIO,你可使用以下断言:
+
+ int gpio_is_valid(int number);
+
+如果编号不存在,则请求和释放 GPIO 的函数将拒绝执行相关操作(见下文)。
+其他编号也可能被拒绝,比如一个编号可能存在,但暂时在给定的电路上不可用。
+
+一个平台是否支持多个 GPIO 控制器为平台特定的实现问题,就像是否可以
+在 GPIO 编号空间中有“空洞”和是否可以在运行时添加新的控制器一样。
+这些问题会影响其他事情,包括相邻的 GPIO 编号是否存在等。
+
+使用 GPIO
+---------
+对于一个 GPIO,系统应该做的第一件事情就是通过 gpio_request()
+函数分配它,见下文。
+
+接下来是设置I/O方向,这通常是在板级启动代码中为所使用的 GPIO 设置
+platform_device 时完成。
+
+ /* 设置为输入或输出, 返回 0 或负的错误代码 */
+ int gpio_direction_input(unsigned gpio);
+ int gpio_direction_output(unsigned gpio, int value);
+
+返回值为零代表成功,否则返回一个负的错误代码。这个返回值需要检查,因为
+get/set(获取/设置)函数调用没法返回错误,且有可能是配置错误。通常,
+你应该在进程上下文中调用这些函数。然而,对于自旋锁安全的 GPIO,在板子
+启动的早期、进程启动前使用他们也是可以的。
+
+对于作为输出的 GPIO,为其提供初始输出值,对于避免在系统启动期间出现
+信号毛刺是很有帮助的。
+
+为了与传统的 GPIO 接口兼容, 在设置一个 GPIO 方向时,如果它还未被申请,
+则隐含了申请那个 GPIO 的操作(见下文)。这种兼容性正在从可选的 gpiolib
+框架中移除。
+
+如果这个 GPIO 编码不存在,或者特定的 GPIO 不能用于那种模式,则方向
+设置可能失败。依赖启动固件来正确地设置方向通常是一个坏主意,因为它可能
+除了启动Linux,并没有做更多的验证工作。(同理, 板子的启动代码可能需要
+将这个复用的引脚设置为 GPIO,并正确地配置上拉/下拉电阻。)
+
+
+访问自旋锁安全的 GPIO
+-------------------
+大多数 GPIO 控制器可以通过内存读/写指令来访问。这些指令不会休眠,可以
+安全地在硬(非线程)中断例程和类似的上下文中完成。
+
+对于那些用 gpio_cansleep()测试总是返回失败的 GPIO(见下文),使用
+以下的函数访问:
+
+ /* GPIO 输入:返回零或非零 */
+ int gpio_get_value(unsigned gpio);
+
+ /* GPIO 输出 */
+ void gpio_set_value(unsigned gpio, int value);
+
+GPIO值是布尔值,零表示低电平,非零表示高电平。当读取一个输出引脚的值时,
+返回值应该是引脚上的值。这个值不总是和输出值相符,因为存在开漏输出信号和
+输出延迟问题。
+
+以上的 get/set 函数无错误返回值,因为之前 gpio_direction_*()应已检查过
+其是否为“无效GPIO”。此外,还需要注意的是并不是所有平台都可以从输出引脚
+中读取数据,对于不能读取的引脚应总返回零。另外,对那些在原子上下文中无法
+安全访问的 GPIO (译者注:因为访问可能导致休眠)使用这些函数是不合适的
+(见下文)。
+
+在 GPIO 编号(还有输出、值)为常数的情况下,鼓励通过平台特定的实现来优化
+这两个函数来访问 GPIO 值。这种情况(读写一个硬件寄存器)下只需要几条指令
+是很正常的,且无须自旋锁。这种优化函数比起那些在子程序上花费许多指令的
+函数可以使得模拟接口(译者注:例如 GPIO 模拟 I2C、1-wire 或 SPI)的
+应用(在空间和时间上都)更具效率。
+
+
+访问可能休眠的 GPIO
+-----------------
+某些 GPIO 控制器必须通过基于总线(如 I2C 或 SPI)的消息访问。读或写这些
+GPIO 值的命令需要等待其信息排到队首才发送命令,再获得其反馈。期间需要
+休眠,这不能在 IRQ 例程(中断上下文)中执行。
+
+支持此类 GPIO 的平台通过以下函数返回非零值来区分出这种 GPIO。(此函数需要
+一个之前通过 gpio_request 分配到的有效 GPIO 编号):
+
+ int gpio_cansleep(unsigned gpio);
+
+为了访问这种 GPIO,内核定义了一套不同的函数:
+
+ /* GPIO 输入:返回零或非零 ,可能会休眠 */
+ int gpio_get_value_cansleep(unsigned gpio);
+
+ /* GPIO 输出,可能会休眠 */
+ void gpio_set_value_cansleep(unsigned gpio, int value);
+
+
+访问这样的 GPIO 需要一个允许休眠的上下文,例如线程 IRQ 处理例程,并用以上的
+访问函数替换那些没有 cansleep()后缀的自旋锁安全访问函数。
+
+除了这些访问函数可能休眠,且它们操作的 GPIO 不能在硬件 IRQ 处理例程中访问的
+事实,这些处理例程实际上和自旋锁安全的函数是一样的。
+
+** 除此之外 ** 调用设置和配置此类 GPIO 的函数也必须在允许休眠的上下文中,
+因为它们可能也需要访问 GPIO 控制器芯片: (这些设置函数通常在板级启动代码或者
+驱动探测/断开代码中,所以这是一个容易满足的约束条件。)
+
+ gpio_direction_input()
+ gpio_direction_output()
+ gpio_request()
+
+## gpio_request_one()
+## gpio_request_array()
+## gpio_free_array()
+
+ gpio_free()
+ gpio_set_debounce()
+
+
+
+声明和释放 GPIO
+----------------------------
+为了有助于捕获系统配置错误,定义了两个函数。
+
+ /* 申请 GPIO, 返回 0 或负的错误代码.
+ * 非空标签可能有助于诊断.
+ */
+ int gpio_request(unsigned gpio, const char *label);
+
+ /* 释放之前声明的 GPIO */
+ void gpio_free(unsigned gpio);
+
+将无效的 GPIO 编码传递给 gpio_request()会导致失败,申请一个已使用这个
+函数声明过的 GPIO 也会失败。gpio_request()的返回值必须检查。你应该在
+进程上下文中调用这些函数。然而,对于自旋锁安全的 GPIO,在板子启动的早期、
+进入进程之前是可以申请的。
+
+这个函数完成两个基本的目标。一是标识那些实际上已作为 GPIO 使用的信号线,
+这样便于更好地诊断;系统可能需要服务几百个可用的 GPIO,但是对于任何一个
+给定的电路板通常只有一些被使用。另一个目的是捕获冲突,查明错误:如两个或
+更多驱动错误地认为他们已经独占了某个信号线,或是错误地认为移除一个管理着
+某个已激活信号的驱动是安全的。也就是说,申请 GPIO 的作用类似一种锁机制。
+
+某些平台可能也使用 GPIO 作为电源管理激活信号(例如通过关闭未使用芯片区和
+简单地关闭未使用时钟)。
+
+对于 GPIO 使用 pinctrl 子系统已知的引脚,子系统应该被告知其使用情况;
+一个 gpiolib 驱动的 .request()操作应调用 pinctrl_request_gpio(),
+而 gpiolib 驱动的 .free()操作应调用 pinctrl_free_gpio()。pinctrl
+子系统允许 pinctrl_request_gpio()在某个引脚或引脚组以复用形式“属于”
+一个设备时都成功返回。
+
+任何须将 GPIO 信号导向适当引脚的引脚复用硬件的编程应该发生在 GPIO
+驱动的 .direction_input()或 .direction_output()函数中,以及
+任何输出 GPIO 值的设置之后。这样可使从引脚特殊功能到 GPIO 的转换
+不会在引脚产生毛刺波形。有时当用一个 GPIO 实现其信号驱动一个非 GPIO
+硬件模块的解决方案时,就需要这种机制。
+
+某些平台允许部分或所有 GPIO 信号使用不同的引脚。类似的,GPIO 或引脚的
+其他方面也需要配置,如上拉/下拉。平台软件应该在对这些 GPIO 调用
+gpio_request()前将这类细节配置好,例如使用 pinctrl 子系统的映射表,
+使得 GPIO 的用户无须关注这些细节。
+
+还有一个值得注意的是在释放 GPIO 前,你必须停止使用它。
+
+
+注意:申请一个 GPIO 并没有以任何方式配置它,只不过标识那个 GPIO 处于使用
+状态。必须有另外的代码来处理引脚配置(如控制 GPIO 使用的引脚、上拉/下拉)。
+考虑到大多数情况下声明 GPIO 之后就会立即配置它们,所以定义了以下三个辅助函数:
+
+ /* 申请一个 GPIO 信号, 同时通过特定的'flags'初始化配置,
+ * 其他和 gpio_request()的参数和返回值相同
+ *
+ */
+ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
+
+ /* 在单个函数中申请多个 GPIO
+ */
+ int gpio_request_array(struct gpio *array, size_t num);
+
+ /* 在单个函数中释放多个 GPIO
+ */
+ void gpio_free_array(struct gpio *array, size_t num);
+
+这里 'flags' 当前定义可指定以下属性:
+
+ * GPIOF_DIR_IN - 配置方向为输入
+ * GPIOF_DIR_OUT - 配置方向为输出
+
+ * GPIOF_INIT_LOW - 在作为输出时,初始值为低电平
+ * GPIOF_INIT_HIGH - 在作为输出时,初始值为高电平
+ * GPIOF_OPEN_DRAIN - gpio引脚为开漏信号
+ * GPIOF_OPEN_SOURCE - gpio引脚为源极开路信号
+
+ * GPIOF_EXPORT_DIR_FIXED - 将 gpio 导出到 sysfs,并保持方向
+ * GPIOF_EXPORT_DIR_CHANGEABLE - 同样是导出, 但允许改变方向
+
+因为 GPIOF_INIT_* 仅有在配置为输出的时候才存在,所以有效的组合为:
+
+ * GPIOF_IN - 配置为输入
+ * GPIOF_OUT_INIT_LOW - 配置为输出,并初始化为低电平
+ * GPIOF_OUT_INIT_HIGH - 配置为输出,并初始化为高电平
+
+当设置 flag 为 GPIOF_OPEN_DRAIN 时,则假设引脚是开漏信号。这样的引脚
+将不会在输出模式下置1。这样的引脚需要连接上拉电阻。通过使能这个标志,gpio库
+将会在被要求输出模式下置1时将引脚变为输入状态来使引脚置高。引脚在输出模式下
+通过置0使其输出低电平。
+
+当设置 flag 为 GPIOF_OPEN_SOURCE 时,则假设引脚为源极开路信号。这样的引脚
+将不会在输出模式下置0。这样的引脚需要连接下拉电阻。通过使能这个标志,gpio库
+将会在被要求输出模式下置0时将引脚变为输入状态来使引脚置低。引脚在输出模式下
+通过置1使其输出高电平。
+
+将来这些标志可能扩展到支持更多的属性。
+
+更进一步,为了更简单地声明/释放多个 GPIO,'struct gpio'被引进来封装所有
+这三个领域:
+
+ struct gpio {
+ unsigned gpio;
+ unsigned long flags;
+ const char *label;
+ };
+
+一个典型的用例:
+
+ static struct gpio leds_gpios[] = {
+ { 32, GPIOF_OUT_INIT_HIGH, "Power LED" }, /* 默认开启 */
+ { 33, GPIOF_OUT_INIT_LOW, "Green LED" }, /* 默认关闭 */
+ { 34, GPIOF_OUT_INIT_LOW, "Red LED" }, /* 默认关闭 */
+ { 35, GPIOF_OUT_INIT_LOW, "Blue LED" }, /* 默认关闭 */
+ { ... },
+ };
+
+ err = gpio_request_one(31, GPIOF_IN, "Reset Button");
+ if (err)
+ ...
+
+ err = gpio_request_array(leds_gpios, ARRAY_SIZE(leds_gpios));
+ if (err)
+ ...
+
+ gpio_free_array(leds_gpios, ARRAY_SIZE(leds_gpios));
+
+
+GPIO 映射到 IRQ
+--------------------
+GPIO 编号是无符号整数;IRQ 编号也是。这些构成了两个逻辑上不同的命名空间
+(GPIO 0 不一定使用 IRQ 0)。你可以通过以下函数在它们之间实现映射:
+
+ /* 映射 GPIO 编号到 IRQ 编号 */
+ int gpio_to_irq(unsigned gpio);
+
+ /* 映射 IRQ 编号到 GPIO 编号 (尽量避免使用) */
+ int irq_to_gpio(unsigned irq);
+
+它们的返回值为对应命名空间的相关编号,或是负的错误代码(如果无法映射)。
+(例如,某些 GPIO 无法做为 IRQ 使用。)以下的编号错误是未经检测的:使用一个
+未通过 gpio_direction_input()配置为输入的 GPIO 编号,或者使用一个
+并非来源于gpio_to_irq()的 IRQ 编号。
+
+这两个映射函数可能会在信号编号的加减计算过程上花些时间。它们不可休眠。
+
+gpio_to_irq()返回的非错误值可以传递给 request_irq()或者 free_irq()。
+它们通常通过板级特定的初始化代码存放到平台设备的 IRQ 资源中。注意:IRQ
+触发选项是 IRQ 接口的一部分,如 IRQF_TRIGGER_FALLING,系统唤醒能力
+也是如此。
+
+irq_to_gpio()返回的非错误值大多数通常可以被 gpio_get_value()所使用,
+比如在 IRQ 是沿触发时初始化或更新驱动状态。注意某些平台不支持反映射,所以
+你应该尽量避免使用它。
+
+
+模拟开漏信号
+----------------------------
+有时在只有低电平信号作为实际驱动结果(译者注:多个输出连接于一点,逻辑电平
+结果为所有输出的逻辑与)的时候,共享的信号线需要使用“开漏”信号。(该术语
+适用于 CMOS 管;而 TTL 用“集电极开路”。)一个上拉电阻使信号为高电平。这
+有时被称为“线与”。实际上,从负逻辑(低电平为真)的角度来看,这是一个“线或”。
+
+一个开漏信号的常见例子是共享的低电平使能 IRQ 信号线。此外,有时双向数据总线
+信号也使用漏极开路信号。
+
+某些 GPIO 控制器直接支持开漏输出,还有许多不支持。当你需要开漏信号,但
+硬件又不直接支持的时候,一个常用的方法是用任何即可作输入也可作输出的 GPIO
+引脚来模拟:
+
+ LOW: gpio_direction_output(gpio, 0) ... 这代码驱动信号并覆盖
+ 上拉配置。
+
+ HIGH: gpio_direction_input(gpio) ... 这代码关闭输出,所以上拉电阻
+ (或其他的一些器件)控制了信号。
+
+如果你将信号线“驱动”为高电平,但是 gpio_get_value(gpio)报告了一个
+低电平(在适当的上升时间后),你就可以知道是其他的一些组件将共享信号线拉低了。
+这不一定是错误的。一个常见的例子就是 I2C 时钟的延长:一个需要较慢时钟的
+从设备延迟 SCK 的上升沿,而 I2C 主设备相应地调整其信号传输速率。
+
+
+这些公约忽略了什么?
+================
+这些公约忽略的最大一件事就是引脚复用,因为这属于高度芯片特定的属性且
+没有可移植性。某个平台可能不需要明确的复用信息;有的对于任意给定的引脚
+可能只有两个功能选项;有的可能每个引脚有八个功能选项;有的可能可以将
+几个引脚中的任何一个作为给定的 GPIO。(是的,这些例子都来自于当前运行
+Linux 的系统。)
+
+在某些系统中,与引脚复用相关的是配置和使能集成的上、下拉模式。并不是所有
+平台都支持这种模式,或者不会以相同的方式来支持这种模式;且任何给定的电路板
+可能使用外置的上拉(或下拉)电阻,这时芯片上的就不应该使用。(当一个电路需要
+5kOhm 的拉动电阻,芯片上的 100 kOhm 电阻就不能做到。)同样的,驱动能力
+(2 mA vs 20 mA)和电压(1.8V vs 3.3V)是平台特定问题,就像模型一样在
+可配置引脚和 GPIO 之间(没)有一一对应的关系。
+
+还有其他一些系统特定的机制没有在这里指出,例如上述的输入去毛刺和线与输出
+选项。硬件可能支持批量读或写 GPIO,但是那一般是配置相关的:对于处于同一
+块区(bank)的GPIO。(GPIO 通常以 16 或 32 个组成一个区块,一个给定的
+片上系统一般有几个这样的区块。)某些系统可以通过输出 GPIO 触发 IRQ,
+或者从并非以 GPIO 管理的引脚取值。这些机制的相关代码没有必要具有可移植性。
+
+当前,动态定义 GPIO 并不是标准的,例如作为配置一个带有某些 GPIO 扩展器的
+附加电路板的副作用。
+
+GPIO 实现者的框架 (可选)
+=====================
+前面提到了,有一个可选的实现框架,让平台使用相同的编程接口,更加简单地支持
+不同种类的 GPIO 控制器。这个框架称为"gpiolib"。
+
+作为一个辅助调试功能,如果 debugfs 可用,就会有一个 /sys/kernel/debug/gpio
+文件。通过这个框架,它可以列出所有注册的控制器,以及当前正在使用中的 GPIO
+的状态。
+
+
+控制器驱动: gpio_chip
+-------------------
+在框架中每个 GPIO 控制器都包装为一个 "struct gpio_chip",他包含了
+该类型的每个控制器的常用信息:
+
+ - 设置 GPIO 方向的方法
+ - 用于访问 GPIO 值的方法
+ - 告知调用其方法是否可能休眠的标志
+ - 可选的 debugfs 信息导出方法 (显示类似上拉配置一样的额外状态)
+ - 诊断标签
+
+也包含了来自 device.platform_data 的每个实例的数据:它第一个 GPIO 的
+编号和它可用的 GPIO 的数量。
+
+实现 gpio_chip 的代码应支持多控制器实例,这可能使用驱动模型。那些代码要
+配置每个 gpio_chip,并发起gpiochip_add()。卸载一个 GPIO 控制器很少见,
+但在必要的时候可以使用 gpiochip_remove()。
+
+大部分 gpio_chip 是一个实例特定结构体的一部分,而并不将 GPIO 接口单独
+暴露出来,比如编址、电源管理等。类似编解码器这样的芯片会有复杂的非 GPIO
+状态。
+
+任何一个 debugfs 信息导出方法通常应该忽略还未申请作为 GPIO 的信号线。
+他们可以使用 gpiochip_is_requested()测试,当这个 GPIO 已经申请过了
+就返回相关的标签,否则返回 NULL。
+
+
+平台支持
+-------
+为了支持这个框架,一个平台的 Kconfig 文件将会 "select"(选择)
+ARCH_REQUIRE_GPIOLIB 或 ARCH_WANT_OPTIONAL_GPIOLIB,并让它的
+<asm/gpio.h> 包含 <asm-generic/gpio.h>,同时定义三个方法:
+gpio_get_value()、gpio_set_value()和 gpio_cansleep()。
+
+它也应提供一个 ARCH_NR_GPIOS 的定义值,这样可以更好地反映该平台 GPIO
+的实际数量,节省静态表的空间。(这个定义值应该包含片上系统内建 GPIO 和
+GPIO 扩展器中的数据。)
+
+ARCH_REQUIRE_GPIOLIB 意味着 gpiolib 核心在这个构架中将总是编译进内核。
+
+ARCH_WANT_OPTIONAL_GPIOLIB 意味着 gpiolib 核心默认关闭,且用户可以
+使能它,并将其编译进内核(可选)。
+
+如果这些选项都没被选择,该平台就不通过 GPIO-lib 支持 GPIO,且代码不可以
+被用户使能。
+
+以下这些方法的实现可以直接使用框架代码,并总是通过 gpio_chip 调度:
+
+ #define gpio_get_value __gpio_get_value
+ #define gpio_set_value __gpio_set_value
+ #define gpio_cansleep __gpio_cansleep
+
+这些定义可以用更理想的实现方法替代,那就是使用经过逻辑优化的内联函数来访问
+基于特定片上系统的 GPIO。例如,若引用的 GPIO (寄存器位偏移)是常量“12”,
+读取或设置它可能只需少则两或三个指令,且不会休眠。当这样的优化无法实现时,
+那些函数必须使用框架提供的代码,那就至少要几十条指令才可以实现。对于用 GPIO
+模拟的 I/O 接口, 如此精简指令是很有意义的。
+
+对于片上系统,平台特定代码为片上 GPIO 每个区(bank)定义并注册 gpio_chip
+实例。那些 GPIO 应该根据芯片厂商的文档进行编码/标签,并直接和电路板原理图
+对应。他们应该开始于零并终止于平台特定的限制。这些 GPIO(代码)通常从
+arch_initcall()或者更早的地方集成进平台初始化代码,使这些 GPIO 总是可用,
+且他们通常可以作为 IRQ 使用。
+
+板级支持
+-------
+对于外部 GPIO 控制器(例如 I2C 或 SPI 扩展器、专用芯片、多功能器件、FPGA
+或 CPLD),大多数常用板级特定代码都可以注册控制器设备,并保证他们的驱动知道
+gpiochip_add()所使用的 GPIO 编号。他们的起始编号通常跟在平台特定的 GPIO
+编号之后。
+
+例如板级启动代码应该创建结构体指明芯片公开的 GPIO 范围,并使用 platform_data
+将其传递给每个 GPIO 扩展器芯片。然后芯片驱动中的 probe()例程可以将这个
+数据传递给 gpiochip_add()。
+
+初始化顺序很重要。例如,如果一个设备依赖基于 I2C 的(扩展)GPIO,那么它的
+probe()例程就应该在那个 GPIO 有效以后才可以被调用。这意味着设备应该在
+GPIO 可以工作之后才可被注册。解决这类依赖的的一种方法是让这种 gpio_chip
+控制器向板级特定代码提供 setup()和 teardown()回调函数。一旦所有必须的
+资源可用之后,这些板级特定的回调函数将会注册设备,并可以在这些 GPIO 控制器
+设备变成无效时移除它们。
+
+
+用户空间的 Sysfs 接口(可选)
+========================
+使用“gpiolib”实现框架的平台可以选择配置一个 GPIO 的 sysfs 用户接口。
+这不同于 debugfs 接口,因为它提供的是对 GPIO方向和值的控制,而不只显示
+一个GPIO 的状态摘要。此外,它可以出现在没有调试支持的产品级系统中。
+
+例如,通过适当的系统硬件文档,用户空间可以知道 GIOP #23 控制 Flash
+存储器的写保护(用于保护其中 Bootloader 分区)。产品的系统升级可能需要
+临时解除这个保护:首先导入一个 GPIO,改变其输出状态,然后在重新使能写保护
+前升级代码。通常情况下,GPIO #23 是不会被触及的,并且内核也不需要知道他。
+
+根据适当的硬件文档,某些系统的用户空间 GPIO 可以用于确定系统配置数据,
+这些数据是标准内核不知道的。在某些任务中,简单的用户空间 GPIO 驱动可能是
+系统真正需要的。
+
+注意:标准内核驱动中已经存在通用的“LED 和按键”GPIO 任务,分别是:
+"leds-gpio" 和 "gpio_keys"。请使用这些来替代直接访问 GPIO,因为集成在
+内核框架中的这类驱动比你在用户空间的代码更好。
+
+
+Sysfs 中的路径
+--------------
+在/sys/class/gpio 中有 3 类入口:
+
+ - 用于在用户空间控制 GPIO 的控制接口;
+
+ - GPIOs 本身;以及
+
+ - GPIO 控制器 ("gpio_chip" 实例)。
+
+除了这些标准的文件,还包含“device”符号链接。
+
+控制接口是只写的:
+
+ /sys/class/gpio/
+
+ "export" ... 用户空间可以通过写其编号到这个文件,要求内核导出
+ 一个 GPIO 的控制到用户空间。
+
+ 例如: 如果内核代码没有申请 GPIO #19,"echo 19 > export"
+ 将会为 GPIO #19 创建一个 "gpio19" 节点。
+
+ "unexport" ... 导出到用户空间的逆操作。
+
+ 例如: "echo 19 > unexport" 将会移除使用"export"文件导出的
+ "gpio19" 节点。
+
+GPIO 信号的路径类似 /sys/class/gpio/gpio42/ (对于 GPIO #42 来说),
+并有如下的读/写属性:
+
+ /sys/class/gpio/gpioN/
+
+ "direction" ... 读取得到 "in" 或 "out"。这个值通常运行写入。
+ 写入"out" 时,其引脚的默认输出为低电平。为了确保无故障运行,
+ "low" 或 "high" 的电平值应该写入 GPIO 的配置,作为初始输出值。
+
+ 注意:如果内核不支持改变 GPIO 的方向,或者在导出时内核代码没有
+ 明确允许用户空间可以重新配置 GPIO 方向,那么这个属性将不存在。
+
+ "value" ... 读取得到 0 (低电平) 或 1 (高电平)。如果 GPIO 配置为
+ 输出,这个值允许写操作。任何非零值都以高电平看待。
+
+ 如果引脚可以配置为中断信号,且如果已经配置了产生中断的模式
+ (见"edge"的描述),你可以对这个文件使用轮询操作(poll(2)),
+ 且轮询操作会在任何中断触发时返回。如果你使用轮询操作(poll(2)),
+ 请在 events 中设置 POLLPRI 和 POLLERR。如果你使用轮询操作
+ (select(2)),请在 exceptfds 设置你期望的文件描述符。在
+ 轮询操作(poll(2))返回之后,既可以通过 lseek(2)操作读取
+ sysfs 文件的开始部分,也可以关闭这个文件并重新打开它来读取数据。
+
+ "edge" ... 读取得到“none”、“rising”、“falling”或者“both”。
+ 将这些字符串写入这个文件可以选择沿触发模式,会使得轮询操作
+ (select(2))在"value"文件中返回。
+
+ 这个文件仅有在这个引脚可以配置为可产生中断输入引脚时,才存在。
+
+ "active_low" ... 读取得到 0 (假) 或 1 (真)。写入任何非零值可以
+ 翻转这个属性的(读写)值。已存在或之后通过"edge"属性设置了"rising"
+ 和 "falling" 沿触发模式的轮询操作(poll(2))将会遵循这个设置。
+
+GPIO 控制器的路径类似 /sys/class/gpio/gpiochip42/ (对于从#42 GPIO
+开始实现控制的控制器),并有着以下只读属性:
+
+ /sys/class/gpio/gpiochipN/
+
+ "base" ... 与以上的 N 相同,代表此芯片管理的第一个 GPIO 的编号
+
+ "label" ... 用于诊断 (并不总是只有唯一值)
+
+ "ngpio" ... 此控制器所管理的 GPIO 数量(而 GPIO 编号从 N 到
+ N + ngpio - 1)
+
+大多数情况下,电路板的文档应当标明每个 GPIO 的使用目的。但是那些编号并不总是
+固定的,例如在扩展卡上的 GPIO会根据所使用的主板或所在堆叠架构中其他的板子而
+有所不同。在这种情况下,你可能需要使用 gpiochip 节点(尽可能地结合电路图)来
+确定给定信号所用的 GPIO 编号。
+
+
+从内核代码中导出
+-------------
+内核代码可以明确地管理那些已通过 gpio_request()申请的 GPIO 的导出:
+
+ /* 导出 GPIO 到用户空间 */
+ int gpio_export(unsigned gpio, bool direction_may_change);
+
+ /* gpio_export()的逆操作 */
+ void gpio_unexport();
+
+ /* 创建一个 sysfs 连接到已导出的 GPIO 节点 */
+ int gpio_export_link(struct device *dev, const char *name,
+ unsigned gpio)
+
+ /* 改变 sysfs 中的一个 GPIO 节点的极性 */
+ int gpio_sysfs_set_active_low(unsigned gpio, int value);
+
+在一个内核驱动申请一个 GPIO 之后,它可以通过 gpio_export()使其在 sysfs
+接口中可见。该驱动可以控制信号方向是否可修改。这有助于防止用户空间代码无意间
+破坏重要的系统状态。
+
+这个明确的导出有助于(通过使某些实验更容易来)调试,也可以提供一个始终存在的接口,
+与文档配合作为板级支持包的一部分。
+
+在 GPIO 被导出之后,gpio_export_link()允许在 sysfs 文件系统的任何地方
+创建一个到这个 GPIO sysfs 节点的符号链接。这样驱动就可以通过一个描述性的
+名字,在 sysfs 中他们所拥有的设备下提供一个(到这个 GPIO sysfs 节点的)接口。
+
+驱动可以使用 gpio_sysfs_set_active_low() 来在用户空间隐藏电路板之间
+GPIO 线的极性差异。这个仅对 sysfs 接口起作用。极性的改变可以在 gpio_export()
+前后进行,且之前使能的轮询操作(poll(2))支持(上升或下降沿)将会被重新配置来遵循
+这个设置。
diff --git a/Documentation/zh_CN/video4linux/omap3isp.txt b/Documentation/zh_CN/video4linux/omap3isp.txt
new file mode 100644
index 000000000000..67ffbf352ae0
--- /dev/null
+++ b/Documentation/zh_CN/video4linux/omap3isp.txt
@@ -0,0 +1,277 @@
+Chinese translated version of Documentation/video4linux/omap3isp.txt
+
+If you have any comment or update to the content, please contact the
+original document maintainer directly. However, if you have a problem
+communicating in English you can also ask the Chinese maintainer for
+help. Contact the Chinese maintainer if this translation is outdated
+or if there is a problem with the translation.
+
+Maintainer: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ Sakari Ailus <sakari.ailus@iki.fi>
+ David Cohen <dacohen@gmail.com>
+Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
+---------------------------------------------------------------------
+Documentation/video4linux/omap3isp.txt 的中文翻译
+
+如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
+交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
+译存在问题,请联系中文版维护者。
+英文版维护者: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ Sakari Ailus <sakari.ailus@iki.fi>
+ David Cohen <dacohen@gmail.com>
+中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+
+
+以下为正文
+---------------------------------------------------------------------
+OMAP 3 图像信号处理器 (ISP) 驱动
+
+Copyright (C) 2010 Nokia Corporation
+Copyright (C) 2009 Texas Instruments, Inc.
+
+联系人: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ Sakari Ailus <sakari.ailus@iki.fi>
+ David Cohen <dacohen@gmail.com>
+
+
+介绍
+===
+
+本文档介绍了由 drivers/media/video/omap3isp 加载的德州仪器
+(TI)OMAP 3 图像信号处理器 (ISP) 驱动。原始驱动由德州仪器(TI)
+编写,但此后由诺基亚重写了两次。
+
+驱动已在以下 OMAP 3 系列的芯片中成功使用:
+
+ 3430
+ 3530
+ 3630
+
+驱动实现了 V4L2、媒体控制器和 v4l2_subdev 接口。支持内核中使用
+v4l2_subdev 接口的传感器、镜头和闪光灯驱动。
+
+
+拆分为子设备
+==========
+
+OMAP 3 ISP 被拆分为 V4L2 子设备,ISP中的每个模块都由一个子设备
+来表示。每个子设备向用户空间提供一个 V4L2 子设备接口。
+
+ OMAP3 ISP CCP2
+ OMAP3 ISP CSI2a
+ OMAP3 ISP CCDC
+ OMAP3 ISP preview
+ OMAP3 ISP resizer
+ OMAP3 ISP AEWB
+ OMAP3 ISP AF
+ OMAP3 ISP histogram
+
+ISP 中每个可能的连接都通过一个链接嵌入到媒体控制器接口中。详见例程 [2]。
+
+
+控制 OMAP 3 ISP
+==============
+
+通常,对 OMAP 3 ISP 的配置会在下一帧起始时生效。在传感器垂直消隐期间,
+模块变为空闲时完成配置。在内存到内存的操作中,视频管道一次处理一帧。
+应用配置应在帧间完成。
+
+ISP 中的所有模块,除 CSI-2 和 (可能存在的)CCP2 接收器外,都必须
+接收完整的帧数据。因此,传感器必须保证从不发送部分帧数据给ISP。
+
+Autoidle(自动空闲)功能至少在 3430 的 ISP 模块中确实存在一些问题。
+当 omap3isp 模块参数 autoidle 非零时,autoidle(自动空闲)功能
+仅在 3630 中启用了。
+
+
+事件机制
+======
+
+OMAP 3 ISP 驱动在 CCDC 和统计(AEWB、AF 和 直方图)子设备中支持
+V4L2 事件机制接口。
+
+CCDC 子设备通过 HS_VS 中断,处理 V4L2_EVENT_FRAME_SYNC 类型
+事件,用于告知帧起始。早期版本的驱动则使用 V4L2_EVENT_OMAP3ISP_HS_VS。
+当在 CCDC 模块中接收到起始帧的第一行时,会准确地触发事件。这个事件
+可以在 CCDC 子设备中“订阅”。
+
+(当使用并行接口时,必须注意正确地配置 VS 信号极性。而当使用串行接收时
+这个会自动校正。)
+
+每个统计子设备都可以产生事件。每当一个统计缓冲区可由用户空间应用程序
+通过 VIDIOC_OMAP3ISP_STAT_REQ IOCTL 操作获取时,就会产生一个
+事件。当前存在以下事件:
+
+ V4L2_EVENT_OMAP3ISP_AEWB
+ V4L2_EVENT_OMAP3ISP_AF
+ V4L2_EVENT_OMAP3ISP_HIST
+
+这些 ioctl 的事件数据类型为 struct omap3isp_stat_event_status
+结构体。如果出现计算错误的统计,也同样会产生一个事件,但没有相关的统计
+数据缓冲区。这种情况下 omap3isp_stat_event_status.buf_err 会被
+设置为非零值。
+
+
+私有 IOCTL
+==========
+
+OMAP 3 ISP 驱动支持标准的 V4L2 IOCTL 以及可能存在且实用的控制。但
+ISP 提供的许多功能都不在标准 IOCTL 之列,例如 gamma(伽马)表和统计
+数据采集配置等。
+
+通常,会有一个私有 ioctl 用于配置每个包含硬件依赖功能的模块。
+
+支持以下私有 IOCTL:
+
+ VIDIOC_OMAP3ISP_CCDC_CFG
+ VIDIOC_OMAP3ISP_PRV_CFG
+ VIDIOC_OMAP3ISP_AEWB_CFG
+ VIDIOC_OMAP3ISP_HIST_CFG
+ VIDIOC_OMAP3ISP_AF_CFG
+ VIDIOC_OMAP3ISP_STAT_REQ
+ VIDIOC_OMAP3ISP_STAT_EN
+
+在 include/linux/omap3isp.h 中描述了这些 ioctl 使用的参数结构体。
+与特定 ISP 模块相关的 ISP 自身的详细功能在技术参考手册 (TRMs)中有
+描述,详见文档结尾。
+
+虽然在不使用任何私有 IOCTL 的情况下使用 ISP 驱动是可能的,但这样无法
+获得最佳的图像质量。AEWB、AF 和 直方图(译者注:一般用于自动曝光和增益
+控制,以及图像均衡等)模块无法在未使用适当的私有 IOCTL 配置的情况下使用。
+
+
+CCDC 和 preview(预览)模块 IOCTL
+===============================
+
+VIDIOC_OMAP3ISP_CCDC_CFG 和 VIDIOC_OMAP3ISP_PRV_CFG IOCTL
+被分别用于配置、启用和禁用 CCDC 和 preview(预览)模块的功能。在它们
+所控制的模块中,两个 IOCTL 控制多种功能。VIDIOC_OMAP3ISP_CCDC_CFG IOCTL
+接受一个指向 omap3isp_ccdc_update_config 结构体的指针作为它的参数。
+同样的,VIDIOC_OMAP3ISP_PRV_CFG 接受一个指向 omap3isp_prev_update_config
+结构体的指针。以上两个结构体定义位于 [1]。
+
+这些结构体中的 update 域标识是否针对指定的功能更新配置,而 flag 域
+则标识是启用还是禁用此功能。
+
+update 和 flag 位接受以下掩码值。CCDC 和 preview(预览)模块的
+每个单独功能都与一个 flag 关联(禁用或启用;在结构体中 flag 域的
+一部分)和一个指向功能配置数据的指针。
+
+对于 VIDIOC_OMAP3ISP_CCDC_CFG,下面列出了 update 和 flag 域
+中的有效值。 这些值可能会在同一个 IOCTL 调用中配置多个功能。
+
+ OMAP3ISP_CCDC_ALAW
+ OMAP3ISP_CCDC_LPF
+ OMAP3ISP_CCDC_BLCLAMP
+ OMAP3ISP_CCDC_BCOMP
+ OMAP3ISP_CCDC_FPC
+ OMAP3ISP_CCDC_CULL
+ OMAP3ISP_CCDC_CONFIG_LSC
+ OMAP3ISP_CCDC_TBL_LSC
+
+针对 VIDIOC_OMAP3ISP_PRV_CFG 的相应值如下:
+
+ OMAP3ISP_PREV_LUMAENH
+ OMAP3ISP_PREV_INVALAW
+ OMAP3ISP_PREV_HRZ_MED
+ OMAP3ISP_PREV_CFA
+ OMAP3ISP_PREV_CHROMA_SUPP
+ OMAP3ISP_PREV_WB
+ OMAP3ISP_PREV_BLKADJ
+ OMAP3ISP_PREV_RGB2RGB
+ OMAP3ISP_PREV_COLOR_CONV
+ OMAP3ISP_PREV_YC_LIMIT
+ OMAP3ISP_PREV_DEFECT_COR
+ OMAP3ISP_PREV_GAMMABYPASS
+ OMAP3ISP_PREV_DRK_FRM_CAPTURE
+ OMAP3ISP_PREV_DRK_FRM_SUBTRACT
+ OMAP3ISP_PREV_LENS_SHADING
+ OMAP3ISP_PREV_NF
+ OMAP3ISP_PREV_GAMMA
+
+在启用某个功能的时候,相关的配置数据指针不可为 NULL。在禁用某个功能时,
+配置数据指针会被忽略。
+
+
+统计模块 IOCTL
+=============
+
+统计子设备相较于其他子设备提供了更多动态配置选项。在图像处理流水线处于
+工作状态时,它们可以被启用、禁用和重配。
+
+统计模块总是从 CCDC 中获取输入的图像数据(由于直方图内存读取未实现)。
+统计数据可由用户通过统计子设备节点使用私有 IOCTL 获取。
+
+AEWB、AF 和 直方图子设备提供的私有 IOCTL 极大程度上反应了 ISP 硬件
+提供的寄存器级接口。有些方面纯粹和驱动程序的实现相关,这些将在下面讨论。
+
+VIDIOC_OMAP3ISP_STAT_EN
+-----------------------
+
+这个私有 IOCTL 启用/禁用 一个统计模块。如果这个申请在视频流启动前完成,
+它将在视频流水线开始工作时生效。如果视频流水线已经处于工作状态了,它将在
+CCDC 变为空闲时生效。
+
+VIDIOC_OMAP3ISP_AEWB_CFG, VIDIOC_OMAP3ISP_HIST_CFG and VIDIOC_OMAP3ISP_AF_CFG
+-----------------------------------------------------------------------------
+
+这些 IOCTL 用于配置模块。它们要求用户应用程序对硬件有深入的认识。对
+大多数域的解释可以在 OMAP 的 TRM 中找到。以下两个域对于以上所有的
+私有 IOCTL 配置都很常见,由于他们没有在 TRM 中提及,故需要对其有
+更好的认识。
+
+omap3isp_[h3a_af/h3a_aewb/hist]_config.buf_size:
+
+模块在内部处理自身缓冲。对模块数据输出所必需的缓存大小依赖于已申请的配置。
+虽然驱动支持在视频流工作时重新配置,但对于所需缓存量大于模块启用时内部
+所分配数量的情况,则不支持重新配置。在这种情况下将返回 -EBUSY。为了避免
+此类状况,无论是禁用/重配/启用模块,还是第一次配置时申请必须的缓存大小,
+都应在模块禁用的情况下进行。
+
+内部缓冲分配的大小需综合考虑所申请配置的最小缓存量以及 buf_size 域中
+所设的值。如果 buf_size 域在[minimum(最小值), maximum(最大值)]
+缓冲大小范围之外,则应该将其调整到其范围中。驱动则会选择最大值。正确的
+buf_size 值将回写到用户应用程序中。
+
+omap3isp_[h3a_af/h3a_aewb/hist]_config.config_counter:
+
+由于配置并未在申请之后同步生效,驱动必须提供一个跟踪这类信息的方法,
+以提供更准确的数据。在一个配置被申请之后,返回到用户空间应用程序的
+config_counter 是一个与其配置相关的唯一值。当用户应用程序接收到
+一个缓冲可用或一个新的缓冲申请事件时,这个 config_counter 用于
+一个缓冲数据和一个配置的匹配。
+
+VIDIOC_OMAP3ISP_STAT_REQ
+------------------------
+
+将内部缓冲队列中最早的数据发送到用户空间,然后丢弃此缓冲区。
+omap3isp_stat_data.frame_number 域与视频缓冲的 field_count
+域相匹配。
+
+
+技术参考手册 (TRMs) 和其他文档
+==========================
+
+OMAP 3430 TRM:
+<URL:http://focus.ti.com/pdfs/wtbu/OMAP34xx_ES3.1.x_PUBLIC_TRM_vZM.zip>
+参考于 2011-03-05.
+
+OMAP 35xx TRM:
+<URL:http://www.ti.com/litv/pdf/spruf98o> 参考于 2011-03-05.
+
+OMAP 3630 TRM:
+<URL:http://focus.ti.com/pdfs/wtbu/OMAP36xx_ES1.x_PUBLIC_TRM_vQ.zip>
+参考于 2011-03-05.
+
+DM 3730 TRM:
+<URL:http://www.ti.com/litv/pdf/sprugn4h> 参考于 2011-03-06.
+
+
+参考资料
+=======
+
+[1] include/linux/omap3isp.h
+
+[2] http://git.ideasonboard.org/?p=media-ctl.git;a=summary
diff --git a/Documentation/zh_CN/video4linux/v4l2-framework.txt b/Documentation/zh_CN/video4linux/v4l2-framework.txt
new file mode 100644
index 000000000000..3e74f13af426
--- /dev/null
+++ b/Documentation/zh_CN/video4linux/v4l2-framework.txt
@@ -0,0 +1,983 @@
+Chinese translated version of Documentation/video4linux/v4l2-framework.txt
+
+If you have any comment or update to the content, please contact the
+original document maintainer directly. However, if you have a problem
+communicating in English you can also ask the Chinese maintainer for
+help. Contact the Chinese maintainer if this translation is outdated
+or if there is a problem with the translation.
+
+Maintainer: Mauro Carvalho Chehab <mchehab@infradead.org>
+Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
+---------------------------------------------------------------------
+Documentation/video4linux/v4l2-framework.txt 的中文翻译
+
+如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
+交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
+译存在问题,请联系中文版维护者。
+英文版维护者: Mauro Carvalho Chehab <mchehab@infradead.org>
+中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
+
+
+以下为正文
+---------------------------------------------------------------------
+V4L2 驱动框架概览
+==============
+
+本文档描述 V4L2 框架所提供的各种结构和它们之间的关系。
+
+
+介绍
+----
+
+大部分现代 V4L2 设备由多个 IC 组成,在 /dev 下导出多个设备节点,
+并同时创建非 V4L2 设备(如 DVB、ALSA、FB、I2C 和红外输入设备)。
+由于这种硬件的复杂性,V4L2 驱动也变得非常复杂。
+
+尤其是 V4L2 必须支持 IC 实现音视频的多路复用和编解码,这就更增加了其
+复杂性。通常这些 IC 通过一个或多个 I2C 总线连接到主桥驱动器,但也可
+使用其他总线。这些设备称为“子设备”。
+
+长期以来,这个框架仅限于通过 video_device 结构体创建 V4L 设备节点,
+并使用 video_buf 处理视频缓冲(注:本文不讨论 video_buf 框架)。
+
+这意味着所有驱动必须自己设置设备实例并连接到子设备。其中一部分要正确地
+完成是比较复杂的,使得许多驱动都没有正确地实现。
+
+由于框架的缺失,有很多通用代码都不可重复利用。
+
+因此,这个框架构建所有驱动都需要的基本结构块,而统一的框架将使通用代码
+创建成实用函数并在所有驱动中共享变得更加容易。
+
+
+驱动结构
+-------
+
+所有 V4L2 驱动都有如下结构:
+
+1) 每个设备实例的结构体--包含其设备状态。
+
+2) 初始化和控制子设备的方法(如果有)。
+
+3) 创建 V4L2 设备节点 (/dev/videoX、/dev/vbiX 和 /dev/radioX)
+ 并跟踪设备节点的特定数据。
+
+4) 特定文件句柄结构体--包含每个文件句柄的数据。
+
+5) 视频缓冲处理。
+
+以下是它们的初略关系图:
+
+ device instances(设备实例)
+ |
+ +-sub-device instances(子设备实例)
+ |
+ \-V4L2 device nodes(V4L2 设备节点)
+ |
+ \-filehandle instances(文件句柄实例)
+
+
+框架结构
+-------
+
+该框架非常类似驱动结构:它有一个 v4l2_device 结构用于保存设备
+实例的数据;一个 v4l2_subdev 结构体代表子设备实例;video_device
+结构体保存 V4L2 设备节点的数据;将来 v4l2_fh 结构体将跟踪文件句柄
+实例(暂未尚未实现)。
+
+V4L2 框架也可与媒体框架整合(可选的)。如果驱动设置了 v4l2_device
+结构体的 mdev 域,子设备和视频节点的入口将自动出现在媒体框架中。
+
+
+v4l2_device 结构体
+----------------
+
+每个设备实例都通过 v4l2_device (v4l2-device.h)结构体来表示。
+简单设备可以仅分配这个结构体,但在大多数情况下,都会将这个结构体
+嵌入到一个更大的结构体中。
+
+你必须注册这个设备实例:
+
+ v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
+
+注册操作将会初始化 v4l2_device 结构体。如果 dev->driver_data 域
+为 NULL,就将其指向 v4l2_dev。
+
+需要与媒体框架整合的驱动必须手动设置 dev->driver_data,指向包含
+v4l2_device 结构体实例的驱动特定设备结构体。这可以在注册 V4L2 设备
+实例前通过 dev_set_drvdata() 函数完成。同时必须设置 v4l2_device
+结构体的 mdev 域,指向适当的初始化并注册过的 media_device 实例。
+
+如果 v4l2_dev->name 为空,则它将被设置为从 dev 中衍生出的值(为了
+更加精确,形式为驱动名后跟 bus_id)。如果你在调用 v4l2_device_register
+前已经设置好了,则不会被修改。如果 dev 为 NULL,则你*必须*在调用
+v4l2_device_register 前设置 v4l2_dev->name。
+
+你可以基于驱动名和驱动的全局 atomic_t 类型的实例编号,通过
+v4l2_device_set_name() 设置 name。这样会生成类似 ivtv0、ivtv1 等
+名字。若驱动名以数字结尾,则会在编号和驱动名间插入一个破折号,如:
+cx18-0、cx18-1 等。此函数返回实例编号。
+
+第一个 “dev” 参数通常是一个指向 pci_dev、usb_interface 或
+platform_device 的指针。很少使其为 NULL,除非是一个ISA设备或者
+当一个设备创建了多个 PCI 设备,使得 v4l2_dev 无法与一个特定的父设备
+关联。
+
+你也可以提供一个 notify() 回调,使子设备可以调用它实现事件通知。
+但这个设置与子设备相关。子设备支持的任何通知必须在
+include/media/<subdevice>.h 中定义一个消息头。
+
+注销 v4l2_device 使用如下函数:
+
+ v4l2_device_unregister(struct v4l2_device *v4l2_dev);
+
+如果 dev->driver_data 域指向 v4l2_dev,将会被重置为 NULL。注销同时
+会自动从设备中注销所有子设备。
+
+如果你有一个热插拔设备(如USB设备),则当断开发生时,父设备将无效。
+由于 v4l2_device 有一个指向父设备的指针必须被清除,同时标志父设备
+已消失,所以必须调用以下函数:
+
+ v4l2_device_disconnect(struct v4l2_device *v4l2_dev);
+
+这个函数并*不*注销子设备,因此你依然要调用 v4l2_device_unregister()
+函数。如果你的驱动器并非热插拔的,就没有必要调用 v4l2_device_disconnect()。
+
+有时你需要遍历所有被特定驱动注册的设备。这通常发生在多个设备驱动使用
+同一个硬件的情况下。如:ivtvfb 驱动是一个使用 ivtv 硬件的帧缓冲驱动,
+同时 alsa 驱动也使用此硬件。
+
+你可以使用如下例程遍历所有注册的设备:
+
+static int callback(struct device *dev, void *p)
+{
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+
+ /* 测试这个设备是否已经初始化 */
+ if (v4l2_dev == NULL)
+ return 0;
+ ...
+ return 0;
+}
+
+int iterate(void *p)
+{
+ struct device_driver *drv;
+ int err;
+
+ /* 在PCI 总线上查找ivtv驱动。
+ pci_bus_type是全局的. 对于USB总线使用usb_bus_type。 */
+ drv = driver_find("ivtv", &pci_bus_type);
+ /* 遍历所有的ivtv设备实例 */
+ err = driver_for_each_device(drv, NULL, p, callback);
+ put_driver(drv);
+ return err;
+}
+
+有时你需要一个设备实例的运行计数。这个通常用于映射一个设备实例到一个
+模块选择数组的索引。
+
+推荐方法如下:
+
+static atomic_t drv_instance = ATOMIC_INIT(0);
+
+static int __devinit drv_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ ...
+ state->instance = atomic_inc_return(&drv_instance) - 1;
+}
+
+如果你有多个设备节点,对于热插拔设备,知道何时注销 v4l2_device 结构体
+就比较困难。为此 v4l2_device 有引用计数支持。当调用 video_register_device
+时增加引用计数,而设备节点释放时减小引用计数。当引用计数为零,则
+v4l2_device 的release() 回调将被执行。你就可以在此时做最后的清理工作。
+
+如果创建了其他设备节点(比如 ALSA),则你可以通过以下函数手动增减
+引用计数:
+
+void v4l2_device_get(struct v4l2_device *v4l2_dev);
+
+或:
+
+int v4l2_device_put(struct v4l2_device *v4l2_dev);
+
+由于引用技术初始化为 1 ,你也需要在 disconnect() 回调(对于 USB 设备)中
+调用 v4l2_device_put,或者 remove() 回调(例如对于 PCI 设备),否则
+引用计数将永远不会为 0 。
+
+v4l2_subdev结构体
+------------------
+
+许多驱动需要与子设备通信。这些设备可以完成各种任务,但通常他们负责
+音视频复用和编解码。如网络摄像头的子设备通常是传感器和摄像头控制器。
+
+这些一般为 I2C 接口设备,但并不一定都是。为了给驱动提供调用子设备的
+统一接口,v4l2_subdev 结构体(v4l2-subdev.h)产生了。
+
+每个子设备驱动都必须有一个 v4l2_subdev 结构体。这个结构体可以单独
+代表一个简单的子设备,也可以嵌入到一个更大的结构体中,与更多设备状态
+信息保存在一起。通常有一个下级设备结构体(比如:i2c_client)包含了
+内核创建的设备数据。建议使用 v4l2_set_subdevdata() 将这个结构体的
+指针保存在 v4l2_subdev 的私有数据域(dev_priv)中。这使得通过 v4l2_subdev
+找到实际的低层总线特定设备数据变得容易。
+
+你同时需要一个从低层结构体获取 v4l2_subdev 指针的方法。对于常用的
+i2c_client 结构体,i2c_set_clientdata() 函数可用于保存一个 v4l2_subdev
+指针;对于其他总线你可能需要使用其他相关函数。
+
+桥驱动中也应保存每个子设备的私有数据,比如一个指向特定桥的各设备私有
+数据的指针。为此 v4l2_subdev 结构体提供主机私有数据域(host_priv),
+并可通过 v4l2_get_subdev_hostdata() 和 v4l2_set_subdev_hostdata()
+访问。
+
+从总线桥驱动的视角,驱动加载子设备模块并以某种方式获得 v4l2_subdev
+结构体指针。对于 i2c 总线设备相对简单:调用 i2c_get_clientdata()。
+对于其他总线也需要做类似的操作。针对 I2C 总线上的子设备辅助函数帮你
+完成了大部分复杂的工作。
+
+每个 v4l2_subdev 都包含子设备驱动需要实现的函数指针(如果对此设备
+不适用,可为NULL)。由于子设备可完成许多不同的工作,而在一个庞大的
+函数指针结构体中通常仅有少数有用的函数实现其功能肯定不合适。所以,
+函数指针根据其实现的功能被分类,每一类都有自己的函数指针结构体。
+
+顶层函数指针结构体包含了指向各类函数指针结构体的指针,如果子设备驱动
+不支持该类函数中的任何一个功能,则指向该类结构体的指针为NULL。
+
+这些结构体定义如下:
+
+struct v4l2_subdev_core_ops {
+ int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip);
+ int (*log_status)(struct v4l2_subdev *sd);
+ int (*init)(struct v4l2_subdev *sd, u32 val);
+ ...
+};
+
+struct v4l2_subdev_tuner_ops {
+ ...
+};
+
+struct v4l2_subdev_audio_ops {
+ ...
+};
+
+struct v4l2_subdev_video_ops {
+ ...
+};
+
+struct v4l2_subdev_pad_ops {
+ ...
+};
+
+struct v4l2_subdev_ops {
+ const struct v4l2_subdev_core_ops *core;
+ const struct v4l2_subdev_tuner_ops *tuner;
+ const struct v4l2_subdev_audio_ops *audio;
+ const struct v4l2_subdev_video_ops *video;
+ const struct v4l2_subdev_pad_ops *video;
+};
+
+其中 core(核心)函数集通常可用于所有子设备,其他类别的实现依赖于
+子设备。如视频设备可能不支持音频操作函数,反之亦然。
+
+这样的设置在限制了函数指针数量的同时,还使增加新的操作函数和分类
+变得较为容易。
+
+子设备驱动可使用如下函数初始化 v4l2_subdev 结构体:
+
+ v4l2_subdev_init(sd, &ops);
+
+然后,你必须用一个唯一的名字初始化 subdev->name,并初始化模块的
+owner 域。若使用 i2c 辅助函数,这些都会帮你处理好。
+
+若需同媒体框架整合,你必须调用 media_entity_init() 初始化 v4l2_subdev
+结构体中的 media_entity 结构体(entity 域):
+
+ struct media_pad *pads = &my_sd->pads;
+ int err;
+
+ err = media_entity_init(&sd->entity, npads, pads, 0);
+
+pads 数组必须预先初始化。无须手动设置 media_entity 的 type 和
+name 域,但如有必要,revision 域必须初始化。
+
+当(任何)子设备节点被打开/关闭,对 entity 的引用将被自动获取/释放。
+
+在子设备被注销之后,不要忘记清理 media_entity 结构体:
+
+ media_entity_cleanup(&sd->entity);
+
+如果子设备驱动趋向于处理视频并整合进了媒体框架,必须使用 v4l2_subdev_pad_ops
+替代 v4l2_subdev_video_ops 实现格式相关的功能。
+
+这种情况下,子设备驱动应该设置 link_validate 域,以提供它自身的链接
+验证函数。链接验证函数应对管道(两端链接的都是 V4L2 子设备)中的每个
+链接调用。驱动还要负责验证子设备和视频节点间格式配置的正确性。
+
+如果 link_validate 操作没有设置,默认的 v4l2_subdev_link_validate_default()
+函数将会被调用。这个函数保证宽、高和媒体总线像素格式在链接的收发两端
+都一致。子设备驱动除了它们自己的检测外,也可以自由使用这个函数以执行
+上面提到的检查。
+
+设备(桥)驱动程序必须向 v4l2_device 注册 v4l2_subdev:
+
+ int err = v4l2_device_register_subdev(v4l2_dev, sd);
+
+如果子设备模块在它注册前消失,这个操作可能失败。在这个函数成功返回后,
+subdev->dev 域就指向了 v4l2_device。
+
+如果 v4l2_device 父设备的 mdev 域为非 NULL 值,则子设备实体将被自动
+注册为媒体设备。
+
+注销子设备则可用如下函数:
+
+ v4l2_device_unregister_subdev(sd);
+
+此后,子设备模块就可卸载,且 sd->dev == NULL。
+
+注册之设备后,可通过以下方式直接调用其操作函数:
+
+ err = sd->ops->core->g_chip_ident(sd, &chip);
+
+但使用如下宏会比较容易且合适:
+
+ err = v4l2_subdev_call(sd, core, g_chip_ident, &chip);
+
+这个宏将会做 NULL 指针检查,如果 subdev 为 NULL,则返回-ENODEV;如果
+subdev->core 或 subdev->core->g_chip_ident 为 NULL,则返回 -ENOIOCTLCMD;
+否则将返回 subdev->ops->core->g_chip_ident ops 调用的实际结果。
+
+有时也可能同时调用所有或一系列子设备的某个操作函数:
+
+ v4l2_device_call_all(v4l2_dev, 0, core, g_chip_ident, &chip);
+
+任何不支持此操作的子设备都会被跳过,并忽略错误返回值。但如果你需要
+检查出错码,则可使用如下函数:
+
+ err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_chip_ident, &chip);
+
+除 -ENOIOCTLCMD 外的任何错误都会跳出循环并返回错误值。如果(除 -ENOIOCTLCMD
+外)没有错误发生,则返回 0。
+
+对于以上两个函数的第二个参数为组 ID。如果为 0,则所有子设备都会执行
+这个操作。如果为非 0 值,则只有那些组 ID 匹配的子设备才会执行此操作。
+在桥驱动注册一个子设备前,可以设置 sd->grp_id 为任何期望值(默认值为
+0)。这个值属于桥驱动,且子设备驱动将不会修改和使用它。
+
+组 ID 赋予了桥驱动更多对于如何调用回调的控制。例如,电路板上有多个
+音频芯片,每个都有改变音量的能力。但当用户想要改变音量的时候,通常
+只有一个会被实际使用。你可以对这样的子设备设置组 ID 为(例如 AUDIO_CONTROLLER)
+并在调用 v4l2_device_call_all() 时指定它为组 ID 值。这就保证了只有
+需要的子设备才会执行这个回调。
+
+如果子设备需要通知它的 v4l2_device 父设备一个事件,可以调用
+v4l2_subdev_notify(sd, notification, arg)。这个宏检查是否有一个
+notify() 回调被注册,如果没有,返回 -ENODEV。否则返回 notify() 调用
+结果。
+
+使用 v4l2_subdev 的好处在于它是一个通用结构体,且不包含任何底层硬件
+信息。所有驱动可以包含多个 I2C 总线的子设备,但也有子设备是通过 GPIO
+控制。这个区别仅在配置设备时有关系,一旦子设备注册完成,对于 v4l2
+子系统来说就完全透明了。
+
+
+V4L2 子设备用户空间API
+--------------------
+
+除了通过 v4l2_subdev_ops 结构导出的内核 API,V4L2 子设备也可以直接
+通过用户空间应用程序来控制。
+
+可以在 /dev 中创建名为 v4l-subdevX 设备节点,以通过其直接访问子设备。
+如果子设备支持用户空间直接配置,必须在注册前设置 V4L2_SUBDEV_FL_HAS_DEVNODE
+标志。
+
+注册子设备之后, v4l2_device 驱动会通过调用 v4l2_device_register_subdev_nodes()
+函数为所有已注册并设置了 V4L2_SUBDEV_FL_HAS_DEVNODE 的子设备创建
+设备节点。这些设备节点会在子设备注销时自动删除。
+
+这些设备节点处理 V4L2 API 的一个子集。
+
+VIDIOC_QUERYCTRL
+VIDIOC_QUERYMENU
+VIDIOC_G_CTRL
+VIDIOC_S_CTRL
+VIDIOC_G_EXT_CTRLS
+VIDIOC_S_EXT_CTRLS
+VIDIOC_TRY_EXT_CTRLS
+
+ 这些 ioctls 控制与 V4L2 中定义的一致。他们行为相同,唯一的
+ 不同是他们只处理子设备的控制实现。根据驱动程序,这些控制也
+ 可以通过一个(或多个) V4L2 设备节点访问。
+
+VIDIOC_DQEVENT
+VIDIOC_SUBSCRIBE_EVENT
+VIDIOC_UNSUBSCRIBE_EVENT
+
+ 这些 ioctls 事件与 V4L2 中定义的一致。他们行为相同,唯一的
+ 不同是他们只处理子设备产生的事件。根据驱动程序,这些事件也
+ 可以通过一个(或多个) V4L2 设备节点上报。
+
+ 要使用事件通知的子设备驱动,在注册子设备前必须在 v4l2_subdev::flags
+ 中设置 V4L2_SUBDEV_USES_EVENTS 并在 v4l2_subdev::nevents
+ 中初始化事件队列深度。注册完成后,事件会在 v4l2_subdev::devnode
+ 设备节点中像通常一样被排队。
+
+ 为正确支持事件机制,poll() 文件操作也应被实现。
+
+私有 ioctls
+
+ 不在以上列表中的所有 ioctls 会通过 core::ioctl 操作直接传递
+ 给子设备驱动。
+
+
+I2C 子设备驱动
+-------------
+
+由于这些驱动很常见,所以内特提供了特定的辅助函数(v4l2-common.h)让这些
+设备的使用更加容易。
+
+添加 v4l2_subdev 支持的推荐方法是让 I2C 驱动将 v4l2_subdev 结构体
+嵌入到为每个 I2C 设备实例创建的状态结构体中。而最简单的设备没有状态
+结构体,此时可以直接创建一个 v4l2_subdev 结构体。
+
+一个典型的状态结构体如下所示(‘chipname’用芯片名代替):
+
+struct chipname_state {
+ struct v4l2_subdev sd;
+ ... /* 附加的状态域*/
+};
+
+初始化 v4l2_subdev 结构体的方法如下:
+
+ v4l2_i2c_subdev_init(&state->sd, client, subdev_ops);
+
+这个函数将填充 v4l2_subdev 结构体中的所有域,并保证 v4l2_subdev 和
+i2c_client 都指向彼此。
+
+同时,你也应该为从 v4l2_subdev 指针找到 chipname_state 结构体指针
+添加一个辅助内联函数。
+
+static inline struct chipname_state *to_state(struct v4l2_subdev *sd)
+{
+ return container_of(sd, struct chipname_state, sd);
+}
+
+使用以下函数可以通过 v4l2_subdev 结构体指针获得 i2c_client 结构体
+指针:
+
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+而以下函数则相反,通过 i2c_client 结构体指针获得 v4l2_subdev 结构体
+指针:
+
+ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+当 remove()函数被调用前,必须保证先调用 v4l2_device_unregister_subdev(sd)。
+此操作将会从桥驱动中注销子设备。即使子设备没有注册,调用此函数也是
+安全的。
+
+必须这样做的原因是:当桥驱动注销 i2c 适配器时,remove()回调函数
+会被那个适配器上的 i2c 设备调用。此后,相应的 v4l2_subdev 结构体
+就不存在了,所有它们必须先被注销。在 remove()回调函数中调用
+v4l2_device_unregister_subdev(sd),可以保证执行总是正确的。
+
+
+桥驱动也有一些辅组函数可用:
+
+struct v4l2_subdev *sd = v4l2_i2c_new_subdev(v4l2_dev, adapter,
+ "module_foo", "chipid", 0x36, NULL);
+
+这个函数会加载给定的模块(如果没有模块需要加载,可以为 NULL),
+并用给定的 i2c 适配器结构体指针(i2c_adapter)和 器件地址(chip/address)
+作为参数调用 i2c_new_device()。如果一切顺利,则就在 v4l2_device
+中注册了子设备。
+
+你也可以利用 v4l2_i2c_new_subdev()的最后一个参数,传递一个可能的
+I2C 地址数组,让函数自动探测。这些探测地址只有在前一个参数为 0 的
+情况下使用。非零参数意味着你知道准确的 i2c 地址,所以此时无须进行
+探测。
+
+如果出错,两个函数都返回 NULL。
+
+注意:传递给 v4l2_i2c_new_subdev()的 chipid 通常与模块名一致。
+它允许你指定一个芯片的变体,比如“saa7114”或“saa7115”。一般通过
+i2c 驱动自动探测。chipid 的使用是在今后需要深入了解的事情。这个与
+i2c 驱动不同,较容易混淆。要知道支持哪些芯片变体,你可以查阅 i2c
+驱动代码的 i2c_device_id 表,上面列出了所有可能支持的芯片。
+
+还有两个辅助函数:
+
+v4l2_i2c_new_subdev_cfg:这个函数添加新的 irq 和 platform_data
+参数,并有‘addr’和‘probed_addrs’参数:如果 addr 非零,则被使用
+(不探测变体),否则 probed_addrs 中的地址将用于自动探测。
+
+例如:以下代码将会探测地址(0x10):
+
+struct v4l2_subdev *sd = v4l2_i2c_new_subdev_cfg(v4l2_dev, adapter,
+ "module_foo", "chipid", 0, NULL, 0, I2C_ADDRS(0x10));
+
+v4l2_i2c_new_subdev_board 使用一个 i2c_board_info 结构体,将其
+替代 irq、platform_data 和 add r参数传递给 i2c 驱动。
+
+如果子设备支持 s_config 核心操作,这个操作会在子设备配置好之后以 irq 和
+platform_data 为参数调用。早期的 v4l2_i2c_new_(probed_)subdev 函数
+同样也会调用 s_config,但仅在 irq 为 0 且 platform_data 为 NULL 时。
+
+video_device结构体
+-----------------
+
+在 /dev 目录下的实际设备节点根据 video_device 结构体(v4l2-dev.h)
+创建。此结构体既可以动态分配也可以嵌入到一个更大的结构体中。
+
+动态分配方法如下:
+
+ struct video_device *vdev = video_device_alloc();
+
+ if (vdev == NULL)
+ return -ENOMEM;
+
+ vdev->release = video_device_release;
+
+如果将其嵌入到一个大结构体中,则必须自己实现 release()回调。
+
+ struct video_device *vdev = &my_vdev->vdev;
+
+ vdev->release = my_vdev_release;
+
+release()回调必须被设置,且在最后一个 video_device 用户退出之后
+被调用。
+
+默认的 video_device_release()回调只是调用 kfree 来释放之前分配的
+内存。
+
+你应该设置这些域:
+
+- v4l2_dev: 设置为 v4l2_device 父设备。
+
+- name: 设置为唯一的描述性设备名。
+
+- fops: 设置为已有的 v4l2_file_operations 结构体。
+
+- ioctl_ops: 如果你使用v4l2_ioctl_ops 来简化 ioctl 的维护
+ (强烈建议使用,且将来可能变为强制性的!),然后设置你自己的
+ v4l2_ioctl_ops 结构体.
+
+- lock: 如果你要在驱动中实现所有的锁操作,则设为 NULL 。否则
+ 就要设置一个指向 struct mutex_lock 结构体的指针,这个锁将
+ 在 unlocked_ioctl 文件操作被调用前由内核获得,并在调用返回后
+ 释放。详见下一节。
+
+- prio: 保持对优先级的跟踪。用于实现 VIDIOC_G/S_PRIORITY。如果
+ 设置为 NULL,则会使用 v4l2_device 中的 v4l2_prio_state 结构体。
+ 如果要对每个设备节点(组)实现独立的优先级,可以将其指向自己
+ 实现的 v4l2_prio_state 结构体。
+
+- parent: 仅在使用 NULL 作为父设备结构体参数注册 v4l2_device 时
+ 设置此参数。只有在一个硬件设备包含多一个 PCI 设备,共享同一个
+ v4l2_device 核心时才会发生。
+
+ cx88 驱动就是一个例子:一个 v4l2_device 结构体核心,被一个裸的
+ 视频 PCI 设备(cx8800)和一个 MPEG PCI 设备(cx8802)共用。由于
+ v4l2_device 无法与特定的 PCI 设备关联,所有没有设置父设备。但当
+ video_device 配置后,就知道使用哪个父 PCI 设备了。
+
+- flags:可选。如果你要让框架处理设置 VIDIOC_G/S_PRIORITY ioctls,
+ 请设置 V4L2_FL_USE_FH_PRIO。这要求你使用 v4l2_fh 结构体。
+ 一旦所有驱动使用了核心的优先级处理,最终这个标志将消失。但现在它
+ 必须被显式设置。
+
+如果你使用 v4l2_ioctl_ops,则应该在 v4l2_file_operations 结构体中
+设置 .unlocked_ioctl 指向 video_ioctl2。
+
+请勿使用 .ioctl!它已被废弃,今后将消失。
+
+某些情况下你要告诉核心:你在 v4l2_ioctl_ops 指定的某个函数应被忽略。
+你可以在 video_device_register 被调用前通过以下函数标记这个 ioctls。
+
+void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd);
+
+基于外部因素(例如某个板卡已被使用),在不创建新结构体的情况下,你想
+要关闭 v4l2_ioctl_ops 中某个特性往往需要这个机制。
+
+v4l2_file_operations 结构体是 file_operations 的一个子集。其主要
+区别在于:因 inode 参数从未被使用,它将被忽略。
+
+如果需要与媒体框架整合,你必须通过调用 media_entity_init() 初始化
+嵌入在 video_device 结构体中的 media_entity(entity 域)结构体:
+
+ struct media_pad *pad = &my_vdev->pad;
+ int err;
+
+ err = media_entity_init(&vdev->entity, 1, pad, 0);
+
+pads 数组必须预先初始化。没有必要手动设置 media_entity 的 type 和
+name 域。
+
+当(任何)子设备节点被打开/关闭,对 entity 的引用将被自动获取/释放。
+
+v4l2_file_operations 与锁
+--------------------------
+
+你可以在 video_device 结构体中设置一个指向 mutex_lock 的指针。通常
+这既可是一个顶层互斥锁也可为设备节点自身的互斥锁。默认情况下,此锁
+用于 unlocked_ioctl,但为了使用 ioctls 你通过以下函数可禁用锁定:
+
+ void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd);
+
+例如: v4l2_disable_ioctl_locking(vdev, VIDIOC_DQBUF);
+
+你必须在注册 video_device 前调用这个函数。
+
+特别是对于 USB 驱动程序,某些命令(如设置控制)需要很长的时间,可能
+需要自行为缓冲区队列的 ioctls 实现锁定。
+
+如果你需要更细粒度的锁,你必须设置 mutex_lock 为 NULL,并完全自己实现
+锁机制。
+
+这完全由驱动开发者决定使用何种方法。然而,如果你的驱动存在长延时操作
+(例如,改变 USB 摄像头的曝光时间可能需要较长时间),而你又想让用户
+在等待长延时操作完成期间做其他的事,则你最好自己实现锁机制。
+
+如果指定一个锁,则所有 ioctl 操作将在这个锁的作用下串行执行。如果你
+使用 videobuf,则必须将同一个锁传递给 videobuf 队列初始化函数;如
+videobuf 必须等待一帧的到达,则可临时解锁并在这之后重新上锁。如果驱动
+也在代码执行期间等待,则可做同样的工作(临时解锁,再上锁)让其他进程
+可以在第一个进程阻塞时访问设备节点。
+
+在使用 videobuf2 的情况下,必须实现 wait_prepare 和 wait_finish 回调
+在适当的时候解锁/加锁。进一步来说,如果你在 video_device 结构体中使用
+锁,则必须在 wait_prepare 和 wait_finish 中对这个互斥锁进行解锁/加锁。
+
+热插拔的断开实现也必须在调用 v4l2_device_disconnect 前获得锁。
+
+video_device注册
+---------------
+
+接下来你需要注册视频设备:这会为你创建一个字符设备。
+
+ err = video_register_device(vdev, VFL_TYPE_GRABBER, -1);
+ if (err) {
+ video_device_release(vdev); /* or kfree(my_vdev); */
+ return err;
+ }
+
+如果 v4l2_device 父设备的 mdev 域为非 NULL 值,视频设备实体将自动
+注册为媒体设备。
+
+注册哪种设备是根据类型(type)参数。存在以下类型:
+
+VFL_TYPE_GRABBER: 用于视频输入/输出设备的 videoX
+VFL_TYPE_VBI: 用于垂直消隐数据的 vbiX (例如,隐藏式字幕,图文电视)
+VFL_TYPE_RADIO: 用于广播调谐器的 radioX
+
+最后一个参数让你确定一个所控制设备的设备节点号数量(例如 videoX 中的 X)。
+通常你可以传入-1,让 v4l2 框架自己选择第一个空闲的编号。但是有时用户
+需要选择一个特定的节点号。驱动允许用户通过驱动模块参数选择一个特定的
+设备节点号是很普遍的。这个编号将会传递给这个函数,且 video_register_device
+将会试图选择这个设备节点号。如果这个编号被占用,下一个空闲的设备节点
+编号将被选中,并向内核日志中发送一个警告信息。
+
+另一个使用场景是当驱动创建多个设备时。这种情况下,对不同的视频设备在
+编号上使用不同的范围是很有用的。例如,视频捕获设备从 0 开始,视频
+输出设备从 16 开始。所以你可以使用最后一个参数来指定设备节点号最小值,
+而 v4l2 框架会试图选择第一个的空闲编号(等于或大于你提供的编号)。
+如果失败,则它会就选择第一个空闲的编号。
+
+由于这种情况下,你会忽略无法选择特定设备节点号的警告,则可调用
+video_register_device_no_warn() 函数避免警告信息的产生。
+
+只要设备节点被创建,一些属性也会同时创建。在 /sys/class/video4linux
+目录中你会找到这些设备。例如进入其中的 video0 目录,你会看到‘name’和
+‘index’属性。‘name’属性值就是 video_device 结构体中的‘name’域。
+
+‘index’属性值就是设备节点的索引值:每次调用 video_register_device(),
+索引值都递增 1 。第一个视频设备节点总是从索引值 0 开始。
+
+用户可以设置 udev 规则,利用索引属性生成花哨的设备名(例如:用‘mpegX’
+代表 MPEG 视频捕获设备节点)。
+
+在设备成功注册后,就可以使用这些域:
+
+- vfl_type: 传递给 video_register_device 的设备类型。
+- minor: 已指派的次设备号。
+- num: 设备节点编号 (例如 videoX 中的 X)。
+- index: 设备索引号。
+
+如果注册失败,你必须调用 video_device_release() 来释放已分配的
+video_device 结构体;如果 video_device 是嵌入在自己创建的结构体中,
+你也必须释放它。vdev->release() 回调不会在注册失败之后被调用,
+你也不应试图在注册失败后注销设备。
+
+
+video_device 注销
+----------------
+
+当视频设备节点已被移除,不论是卸载驱动还是USB设备断开,你都应注销
+它们:
+
+ video_unregister_device(vdev);
+
+这个操作将从 sysfs 中移除设备节点(导致 udev 将其从 /dev 中移除)。
+
+video_unregister_device() 返回之后,就无法完成打开操作。尽管如此,
+USB 设备的情况则不同,某些应用程序可能依然打开着其中一个已注销设备
+节点。所以在注销之后,所有文件操作(当然除了 release )也应返回错误值。
+
+当最后一个视频设备节点的用户退出,则 vdev->release() 回调会被调用,
+并且你可以做最后的清理操作。
+
+不要忘记清理与视频设备相关的媒体入口(如果被初始化过):
+
+ media_entity_cleanup(&vdev->entity);
+
+这可以在 release 回调中完成。
+
+
+video_device 辅助函数
+---------------------
+
+一些有用的辅助函数如下:
+
+- file/video_device 私有数据
+
+你可以用以下函数在 video_device 结构体中设置/获取驱动私有数据:
+
+void *video_get_drvdata(struct video_device *vdev);
+void video_set_drvdata(struct video_device *vdev, void *data);
+
+注意:在调用 video_register_device() 前执行 video_set_drvdata()
+是安全的。
+
+而以下函数:
+
+struct video_device *video_devdata(struct file *file);
+
+返回 file 结构体中拥有的的 video_device 指针。
+
+video_drvdata 辅助函数结合了 video_get_drvdata 和 video_devdata
+的功能:
+
+void *video_drvdata(struct file *file);
+
+你可以使用如下代码从 video_device 结构体中获取 v4l2_device 结构体
+指针:
+
+struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
+
+- 设备节点名
+
+video_device 设备节点在内核中的名称可以通过以下函数获得
+
+const char *video_device_node_name(struct video_device *vdev);
+
+这个名字被用户空间工具(例如 udev)作为提示信息使用。应尽可能使用
+此功能,而非访问 video_device::num 和 video_device::minor 域。
+
+
+视频缓冲辅助函数
+---------------
+
+v4l2 核心 API 提供了一个处理视频缓冲的标准方法(称为“videobuf”)。
+这些方法使驱动可以通过统一的方式实现 read()、mmap() 和 overlay()。
+目前在设备上支持视频缓冲的方法有分散/聚集 DMA(videobuf-dma-sg)、
+线性 DMA(videobuf-dma-contig)以及大多用于 USB 设备的用 vmalloc
+分配的缓冲(videobuf-vmalloc)。
+
+请参阅 Documentation/video4linux/videobuf,以获得更多关于 videobuf
+层的使用信息。
+
+v4l2_fh 结构体
+-------------
+
+v4l2_fh 结构体提供一个保存用于 V4L2 框架的文件句柄特定数据的简单方法。
+如果 video_device 的 flag 设置了 V4L2_FL_USE_FH_PRIO 标志,新驱动
+必须使用 v4l2_fh 结构体,因为它也用于实现优先级处理(VIDIOC_G/S_PRIORITY)。
+
+v4l2_fh 的用户(位于 V4l2 框架中,并非驱动)可通过测试
+video_device->flags 中的 V4L2_FL_USES_V4L2_FH 位得知驱动是否使用
+v4l2_fh 作为他的 file->private_data 指针。这个位会在调用 v4l2_fh_init()
+时被设置。
+
+v4l2_fh 结构体作为驱动自身文件句柄结构体的一部分被分配,且驱动在
+其打开函数中将 file->private_data 指向它。
+
+在许多情况下,v4l2_fh 结构体会嵌入到一个更大的结构体中。这钟情况下,
+应该在 open() 中调用 v4l2_fh_init+v4l2_fh_add,并在 release() 中
+调用 v4l2_fh_del+v4l2_fh_exit。
+
+驱动可以通过使用 container_of 宏提取他们自己的文件句柄结构体。例如:
+
+struct my_fh {
+ int blah;
+ struct v4l2_fh fh;
+};
+
+...
+
+int my_open(struct file *file)
+{
+ struct my_fh *my_fh;
+ struct video_device *vfd;
+ int ret;
+
+ ...
+
+ my_fh = kzalloc(sizeof(*my_fh), GFP_KERNEL);
+
+ ...
+
+ v4l2_fh_init(&my_fh->fh, vfd);
+
+ ...
+
+ file->private_data = &my_fh->fh;
+ v4l2_fh_add(&my_fh->fh);
+ return 0;
+}
+
+int my_release(struct file *file)
+{
+ struct v4l2_fh *fh = file->private_data;
+ struct my_fh *my_fh = container_of(fh, struct my_fh, fh);
+
+ ...
+ v4l2_fh_del(&my_fh->fh);
+ v4l2_fh_exit(&my_fh->fh);
+ kfree(my_fh);
+ return 0;
+}
+
+以下是 v4l2_fh 函数使用的简介:
+
+void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
+
+ 初始化文件句柄。这*必须*在驱动的 v4l2_file_operations->open()
+ 函数中执行。
+
+void v4l2_fh_add(struct v4l2_fh *fh)
+
+ 添加一个 v4l2_fh 到 video_device 文件句柄列表。一旦文件句柄
+ 初始化完成就必须调用。
+
+void v4l2_fh_del(struct v4l2_fh *fh)
+
+ 从 video_device() 中解除文件句柄的关联。文件句柄的退出函数也
+ 将被调用。
+
+void v4l2_fh_exit(struct v4l2_fh *fh)
+
+ 清理文件句柄。在清理完 v4l2_fh 后,相关内存会被释放。
+
+
+如果 v4l2_fh 不是嵌入在其他结构体中的,则可以用这些辅助函数:
+
+int v4l2_fh_open(struct file *filp)
+
+ 分配一个 v4l2_fh 结构体空间,初始化并将其添加到 file 结构体相关的
+ video_device 结构体中。
+
+int v4l2_fh_release(struct file *filp)
+
+ 从 file 结构体相关的 video_device 结构体中删除 v4l2_fh ,清理
+ v4l2_fh 并释放空间。
+
+这两个函数可以插入到 v4l2_file_operation 的 open() 和 release()
+操作中。
+
+
+某些驱动需要在第一个文件句柄打开和最后一个文件句柄关闭的时候做些
+工作。所以加入了两个辅助函数以检查 v4l2_fh 结构体是否是相关设备
+节点打开的唯一文件句柄。
+
+int v4l2_fh_is_singular(struct v4l2_fh *fh)
+
+ 如果此文件句柄是唯一打开的文件句柄,则返回 1 ,否则返回 0 。
+
+int v4l2_fh_is_singular_file(struct file *filp)
+
+ 功能相同,但通过 filp->private_data 调用 v4l2_fh_is_singular。
+
+
+V4L2 事件机制
+-----------
+
+V4L2 事件机制提供了一个通用的方法将事件传递到用户空间。驱动必须使用
+v4l2_fh 才能支持 V4L2 事件机制。
+
+
+事件通过一个类型和选择 ID 来定义。ID 对应一个 V4L2 对象,例如
+一个控制 ID。如果未使用,则 ID 为 0。
+
+当用户订阅一个事件,驱动会为此分配一些 kevent 结构体。所以每个
+事件组(类型、ID)都会有自己的一套 kevent 结构体。这保证了如果
+一个驱动短时间内产生了许多同类事件,不会覆盖其他类型的事件。
+
+但如果你收到的事件数量大于同类事件 kevent 的保存数量,则最早的
+事件将被丢弃,并加入新事件。
+
+此外,v4l2_subscribed_event 结构体内部有可供驱动设置的 merge() 和
+replace() 回调,这些回调会在新事件产生且没有多余空间的时候被调用。
+replace() 回调让你可以将早期事件的净荷替换为新事件的净荷,将早期
+净荷的相关数据合并到替换进来的新净荷中。当该类型的事件仅分配了一个
+kevent 结构体时,它将被调用。merge() 回调让你可以合并最早的事件净荷
+到在它之后的那个事件净荷中。当该类型的事件分配了两个或更多 kevent
+结构体时,它将被调用。
+
+这种方法不会有状态信息丢失,只会导致中间步骤信息丢失。
+
+
+关于 replace/merge 回调的一个不错的例子在 v4l2-event.c 中:用于
+控制事件的 ctrls_replace() 和 ctrls_merge() 回调。
+
+注意:这些回调可以在中断上下文中调用,所以它们必须尽快完成并退出。
+
+有用的函数:
+
+void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
+
+ 将事件加入视频设备的队列。驱动仅负责填充 type 和 data 域。
+ 其他域由 V4L2 填充。
+
+int v4l2_event_subscribe(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub, unsigned elems,
+ const struct v4l2_subscribed_event_ops *ops)
+
+ video_device->ioctl_ops->vidioc_subscribe_event 必须检测驱动能
+ 产生特定 id 的事件。然后调用 v4l2_event_subscribe() 来订阅该事件。
+
+ elems 参数是该事件的队列大小。若为 0,V4L2 框架将会(根据事件类型)
+ 填充默认值。
+
+ ops 参数允许驱动指定一系列回调:
+ * add: 当添加一个新监听者时调用(重复订阅同一个事件,此回调
+ 仅被执行一次)。
+ * del: 当一个监听者停止监听时调用。
+ * replace: 用‘新’事件替换‘早期‘事件。
+ * merge: 将‘早期‘事件合并到‘新’事件中。
+ 这四个调用都是可选的,如果不想指定任何回调,则 ops 可为 NULL。
+
+int v4l2_event_unsubscribe(struct v4l2_fh *fh,
+ struct v4l2_event_subscription *sub)
+
+ v4l2_ioctl_ops 结构体中的 vidioc_unsubscribe_event 回调函数。
+ 驱动程序可以直接使用 v4l2_event_unsubscribe() 实现退订事件过程。
+
+ 特殊的 V4L2_EVENT_ALL 类型,可用于退订所有事件。驱动可能在特殊
+ 情况下需要做此操作。
+
+int v4l2_event_pending(struct v4l2_fh *fh)
+
+ 返回未决事件的数量。有助于实现轮询(poll)操作。
+
+事件通过 poll 系统调用传递到用户空间。驱动可用
+v4l2_fh->wait (wait_queue_head_t 类型)作为参数调用 poll_wait()。
+
+事件分为标准事件和私有事件。新的标准事件必须使用可用的最小事件类型
+编号。驱动必须从他们本类型的编号起始处分配事件。类型的编号起始为
+V4L2_EVENT_PRIVATE_START + n * 1000 ,其中 n 为可用最小编号。每个
+类型中的第一个事件类型编号是为以后的使用保留的,所以第一个可用事件
+类型编号是‘class base + 1’。
+
+V4L2 事件机制的使用实例可以在 OMAP3 ISP 的驱动
+(drivers/media/video/omap3isp)中找到。
diff --git a/MAINTAINERS b/MAINTAINERS
index e8e4fa49b5ae..8c22b7f6f41a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -896,6 +896,12 @@ W: http://www.digriz.org.uk/ts78xx/kernel
S: Maintained
F: arch/arm/mach-orion5x/ts78xx-*
+ARM/MICREL KS8695 ARCHITECTURE
+M: Greg Ungerer <gerg@uclinux.org>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+F: arch/arm/mach-ks8695
+S: Odd Fixes
+
ARM/MIOA701 MACHINE SUPPORT
M: Robert Jarzmik <robert.jarzmik@free.fr>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -1192,6 +1198,12 @@ S: Maintained
F: arch/arm/mach-pxa/z2.c
F: arch/arm/mach-pxa/include/mach/z2.h
+ARM64 PORT (AARCH64 ARCHITECTURE)
+M: Catalin Marinas <catalin.marinas@arm.com>
+L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S: Maintained
+F: arch/arm64/
+
ASC7621 HARDWARE MONITOR DRIVER
M: George Joseph <george.joseph@fairview5.com>
L: lm-sensors@lm-sensors.org
@@ -2852,7 +2864,9 @@ F: include/linux/firewire*.h
F: tools/firewire/
FIRMWARE LOADER (request_firmware)
-S: Orphan
+M: Ming Lei <ming.lei@canonical.com>
+L: linux-kernel@vger.kernel.org
+S: Maintained
F: Documentation/firmware_class/
F: drivers/base/firmware*.c
F: include/linux/firmware.h
@@ -3545,11 +3559,12 @@ K: \b(ABS|SYN)_MT_
INTEL C600 SERIES SAS CONTROLLER DRIVER
M: Intel SCU Linux support <intel-linux-scu@intel.com>
+M: Lukasz Dorau <lukasz.dorau@intel.com>
+M: Maciej Patelczyk <maciej.patelczyk@intel.com>
M: Dave Jiang <dave.jiang@intel.com>
-M: Ed Nadolski <edmund.nadolski@intel.com>
L: linux-scsi@vger.kernel.org
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/djbw/isci.git
-S: Maintained
+T: git git://git.code.sf.net/p/intel-sas/isci
+S: Supported
F: drivers/scsi/isci/
F: firmware/isci/
@@ -3659,11 +3674,12 @@ F: Documentation/networking/README.ipw2200
F: drivers/net/wireless/ipw2x00/
INTEL(R) TRUSTED EXECUTION TECHNOLOGY (TXT)
-M: Joseph Cihula <joseph.cihula@intel.com>
+M: Richard L Maliszewski <richard.l.maliszewski@intel.com>
+M: Gang Wei <gang.wei@intel.com>
M: Shane Wang <shane.wang@intel.com>
L: tboot-devel@lists.sourceforge.net
W: http://tboot.sourceforge.net
-T: Mercurial http://www.bughost.org/repos.hg/tboot.hg
+T: hg http://tboot.hg.sourceforge.net:8000/hgroot/tboot/tboot
S: Supported
F: Documentation/intel_txt.txt
F: include/linux/tboot.h
@@ -5313,6 +5329,12 @@ L: linux-mtd@lists.infradead.org
S: Maintained
F: drivers/mtd/devices/phram.c
+PICOLCD HID DRIVER
+M: Bruno Prémont <bonbons@linux-vserver.org>
+L: linux-input@vger.kernel.org
+S: Maintained
+F: drivers/hid/hid-picolcd*
+
PICOXCELL SUPPORT
M: Jamie Iles <jamie@jamieiles.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -5536,6 +5558,8 @@ F: Documentation/devicetree/bindings/pwm/
F: include/linux/pwm.h
F: include/linux/of_pwm.h
F: drivers/pwm/
+F: drivers/video/backlight/pwm_bl.c
+F: include/linux/pwm_backlight.h
PXA2xx/PXA3xx SUPPORT
M: Eric Miao <eric.y.miao@gmail.com>
@@ -6769,14 +6793,14 @@ F: drivers/net/team/
F: include/linux/if_team.h
TEGRA SUPPORT
-M: Colin Cross <ccross@android.com>
-M: Olof Johansson <olof@lixom.net>
M: Stephen Warren <swarren@wwwdotorg.org>
L: linux-tegra@vger.kernel.org
Q: http://patchwork.ozlabs.org/project/linux-tegra/list/
T: git git://git.kernel.org/pub/scm/linux/kernel/git/swarren/linux-tegra.git
S: Supported
F: arch/arm/mach-tegra
+F: arch/arm/boot/dts/tegra*
+F: arch/arm/configs/tegra_defconfig
TEHUTI ETHERNET DRIVER
M: Andy Gospodarek <andy@greyhouse.net>
diff --git a/Makefile b/Makefile
index ae6928cc59d3..846dd7607854 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,8 @@
VERSION = 3
PATCHLEVEL = 6
SUBLEVEL = 0
-EXTRAVERSION = -rc6
-NAME = Saber-toothed Squirrel
+EXTRAVERSION =
+NAME = Terrified Chipmunk
# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
@@ -609,7 +609,11 @@ KBUILD_CFLAGS += $(call cc-option, -femit-struct-debug-baseonly)
endif
ifdef CONFIG_FUNCTION_TRACER
-KBUILD_CFLAGS += -pg
+ifdef CONFIG_HAVE_FENTRY
+CC_USING_FENTRY := $(call cc-option, -mfentry -DCC_USING_FENTRY)
+endif
+KBUILD_CFLAGS += -pg $(CC_USING_FENTRY)
+KBUILD_AFLAGS += $(CC_USING_FENTRY)
ifdef CONFIG_DYNAMIC_FTRACE
ifdef CONFIG_HAVE_C_RECORDMCOUNT
BUILD_C_RECORDMCOUNT := y
diff --git a/arch/Kconfig b/arch/Kconfig
index 72f2fa189cc5..a62965d057f6 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -222,6 +222,19 @@ config HAVE_PERF_EVENTS_NMI
subsystem. Also has support for calculating CPU cycle events
to determine how many clock cycles in a given period.
+config HAVE_PERF_REGS
+ bool
+ help
+ Support selective register dumps for perf events. This includes
+ bit-mapping of each registers and a unique architecture id.
+
+config HAVE_PERF_USER_STACK_DUMP
+ bool
+ help
+ Support user stack dumps for perf event samples. This needs
+ access to the user stack pointer which is not unified across
+ architectures.
+
config HAVE_ARCH_JUMP_LABEL
bool
@@ -281,4 +294,23 @@ config SECCOMP_FILTER
See Documentation/prctl/seccomp_filter.txt for details.
+config HAVE_RCU_USER_QS
+ bool
+ help
+ Provide kernel entry/exit hooks necessary for userspace
+ RCU extended quiescent state. Syscalls need to be wrapped inside
+ rcu_user_exit()-rcu_user_enter() through the slow path using
+ TIF_NOHZ flag. Exceptions handlers must be wrapped as well. Irqs
+ are already protected inside rcu_irq_enter/rcu_irq_exit() but
+ preemption or signal handling on irq exit still need to be protected.
+
+config HAVE_VIRT_CPU_ACCOUNTING
+ bool
+
+config HAVE_IRQ_TIME_ACCOUNTING
+ bool
+ help
+ Archs need to ensure they use a high enough resolution clock to
+ support irq time accounting and then call enable_sched_clock_irqtime().
+
source "kernel/gcov/Kconfig"
diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c
index 9816d5a4d176..ef757147cbf9 100644
--- a/arch/alpha/kernel/pci.c
+++ b/arch/alpha/kernel/pci.c
@@ -256,12 +256,6 @@ pcibios_fixup_bus(struct pci_bus *bus)
}
}
-void __init
-pcibios_update_irq(struct pci_dev *dev, int irq)
-{
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-}
-
int
pcibios_enable_device(struct pci_dev *dev, int mask)
{
diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c
index d6fde98b74b3..83638aa096d5 100644
--- a/arch/alpha/kernel/process.c
+++ b/arch/alpha/kernel/process.c
@@ -28,6 +28,7 @@
#include <linux/tty.h>
#include <linux/console.h>
#include <linux/slab.h>
+#include <linux/rcupdate.h>
#include <asm/reg.h>
#include <asm/uaccess.h>
@@ -54,9 +55,12 @@ cpu_idle(void)
/* FIXME -- EV6 and LCA45 know how to power down
the CPU. */
+ rcu_idle_enter();
while (!need_resched())
cpu_relax();
- schedule();
+
+ rcu_idle_exit();
+ schedule_preempt_disabled();
}
}
diff --git a/arch/alpha/kernel/smp.c b/arch/alpha/kernel/smp.c
index 35ddc02bfa4a..a41ad90a97a6 100644
--- a/arch/alpha/kernel/smp.c
+++ b/arch/alpha/kernel/smp.c
@@ -166,6 +166,7 @@ smp_callin(void)
DBGS(("smp_callin: commencing CPU %d current %p active_mm %p\n",
cpuid, current, current->active_mm));
+ preempt_disable();
/* Do nothing. */
cpu_idle();
}
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index f7673b4e56bc..9d72ed67e432 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -349,6 +349,8 @@ config ARCH_CLPS711X
bool "Cirrus Logic CLPS711x/EP721x/EP731x-based"
select CPU_ARM720T
select ARCH_USES_GETTIMEOFFSET
+ select COMMON_CLK
+ select CLKDEV_LOOKUP
select NEED_MACH_MEMORY_H
help
Support for Cirrus Logic 711x/721x/731x based boards.
@@ -446,7 +448,9 @@ config ARCH_MXS
select CLKSRC_MMIO
select COMMON_CLK
select HAVE_CLK_PREPARE
+ select MULTI_IRQ_HANDLER
select PINCTRL
+ select SPARSE_IRQ
select USE_OF
help
Support for Freescale MXS-based family of processors
@@ -938,6 +942,10 @@ config ARCH_VT8500
select ARCH_HAS_CPUFREQ
select GENERIC_CLOCKEVENTS
select ARCH_REQUIRE_GPIOLIB
+ select USE_OF
+ select COMMON_CLK
+ select HAVE_CLK
+ select CLKDEV_LOOKUP
help
Support for VIA/WonderMedia VT8500/WM85xx System-on-Chip.
@@ -1112,8 +1120,6 @@ source "arch/arm/mach-versatile/Kconfig"
source "arch/arm/mach-vexpress/Kconfig"
source "arch/arm/plat-versatile/Kconfig"
-source "arch/arm/mach-vt8500/Kconfig"
-
source "arch/arm/mach-w90x900/Kconfig"
# Definitions to make life easier
@@ -1600,6 +1606,7 @@ config ARCH_NR_GPIO
default 355 if ARCH_U8500
default 264 if MACH_H4700
default 512 if SOC_OMAP5
+ default 288 if ARCH_VT8500
default 0
help
Maximum number of GPIOs in the system.
@@ -1758,59 +1765,6 @@ config FORCE_MAX_ZONEORDER
This config option is actually maximum order plus one. For example,
a value of 11 means that the largest free memory block is 2^10 pages.
-config LEDS
- bool "Timer and CPU usage LEDs"
- depends on ARCH_CDB89712 || ARCH_EBSA110 || \
- ARCH_EBSA285 || ARCH_INTEGRATOR || \
- ARCH_LUBBOCK || MACH_MAINSTONE || ARCH_NETWINDER || \
- ARCH_OMAP || ARCH_P720T || ARCH_PXA_IDP || \
- ARCH_SA1100 || ARCH_SHARK || ARCH_VERSATILE || \
- ARCH_AT91 || ARCH_DAVINCI || \
- ARCH_KS8695 || MACH_RD88F5182 || ARCH_REALVIEW
- help
- If you say Y here, the LEDs on your machine will be used
- to provide useful information about your current system status.
-
- If you are compiling a kernel for a NetWinder or EBSA-285, you will
- be able to select which LEDs are active using the options below. If
- you are compiling a kernel for the EBSA-110 or the LART however, the
- red LED will simply flash regularly to indicate that the system is
- still functional. It is safe to say Y here if you have a CATS
- system, but the driver will do nothing.
-
-config LEDS_TIMER
- bool "Timer LED" if (!ARCH_CDB89712 && !ARCH_OMAP) || \
- OMAP_OSK_MISTRAL || MACH_OMAP_H2 \
- || MACH_OMAP_PERSEUS2
- depends on LEDS
- depends on !GENERIC_CLOCKEVENTS
- default y if ARCH_EBSA110
- help
- If you say Y here, one of the system LEDs (the green one on the
- NetWinder, the amber one on the EBSA285, or the red one on the LART)
- will flash regularly to indicate that the system is still
- operational. This is mainly useful to kernel hackers who are
- debugging unstable kernels.
-
- The LART uses the same LED for both Timer LED and CPU usage LED
- functions. You may choose to use both, but the Timer LED function
- will overrule the CPU usage LED.
-
-config LEDS_CPU
- bool "CPU usage LED" if (!ARCH_CDB89712 && !ARCH_EBSA110 && \
- !ARCH_OMAP) \
- || OMAP_OSK_MISTRAL || MACH_OMAP_H2 \
- || MACH_OMAP_PERSEUS2
- depends on LEDS
- help
- If you say Y here, the red LED will be used to give a good real
- time indication of CPU usage, by lighting whenever the idle task
- is not currently executing.
-
- The LART uses the same LED for both Timer LED and CPU usage LED
- functions. You may choose to use both, but the Timer LED function
- will overrule the CPU usage LED.
-
config ALIGNMENT_TRAP
bool
depends on CPU_CP15_MMU
@@ -2290,7 +2244,7 @@ menu "Power management options"
source "kernel/power/Kconfig"
config ARCH_SUSPEND_POSSIBLE
- depends on !ARCH_S5PC100 && !ARCH_TEGRA
+ depends on !ARCH_S5PC100
depends on CPU_ARM920T || CPU_ARM926T || CPU_SA1100 || \
CPU_V6 || CPU_V6K || CPU_V7 || CPU_XSC3 || CPU_XSCALE || CPU_MOHAWK
def_bool y
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index a7eb28260b2e..b0f3857b3a4c 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -224,6 +224,20 @@ choice
Say Y here if you want kernel low-level debugging support
on i.MX6Q UART4.
+ config DEBUG_MMP_UART2
+ bool "Kernel low-level debugging message via MMP UART2"
+ depends on ARCH_MMP
+ help
+ Say Y here if you want kernel low-level debugging support
+ on MMP UART2.
+
+ config DEBUG_MMP_UART3
+ bool "Kernel low-level debugging message via MMP UART3"
+ depends on ARCH_MMP
+ help
+ Say Y here if you want kernel low-level debugging support
+ on MMP UART3.
+
config DEBUG_MSM_UART1
bool "Kernel low-level debugging messages via MSM UART1"
depends on ARCH_MSM7X00A || ARCH_MSM7X30 || ARCH_QSD8X50
diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S
index 81769c1341fa..bc67cbff3944 100644
--- a/arch/arm/boot/compressed/head.S
+++ b/arch/arm/boot/compressed/head.S
@@ -653,6 +653,7 @@ __armv7_mmu_cache_on:
mcrne p15, 0, r0, c8, c7, 0 @ flush I,D TLBs
#endif
mrc p15, 0, r0, c1, c0, 0 @ read control reg
+ bic r0, r0, #1 << 28 @ clear SCTLR.TRE
orr r0, r0, #0x5000 @ I-cache enable, RR cache replacement
orr r0, r0, #0x003c @ write buffer
#ifdef CONFIG_MMU
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index d302e6655e65..43c084c2cd66 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -19,6 +19,7 @@ dtb-$(CONFIG_ARCH_AT91) += aks-cdu.dtb \
dtb-$(CONFIG_ARCH_BCM2835) += bcm2835-rpi-b.dtb
dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \
exynos4210-smdkv310.dtb \
+ exynos4210-trats.dtb \
exynos5250-smdk5250.dtb
dtb-$(CONFIG_ARCH_HIGHBANK) += highbank.dtb
dtb-$(CONFIG_ARCH_IMX5) += imx51-babbage.dtb \
diff --git a/arch/arm/boot/dts/at91sam9260.dtsi b/arch/arm/boot/dts/at91sam9260.dtsi
index 66389c1c6f62..7c95f76398de 100644
--- a/arch/arm/boot/dts/at91sam9260.dtsi
+++ b/arch/arm/boot/dts/at91sam9260.dtsi
@@ -104,6 +104,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioB: gpio@fffff600 {
@@ -113,6 +114,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioC: gpio@fffff800 {
@@ -122,6 +124,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
dbgu: serial@fffff200 {
diff --git a/arch/arm/boot/dts/at91sam9263.dtsi b/arch/arm/boot/dts/at91sam9263.dtsi
index b460d6ce9eb5..195019b7ca0e 100644
--- a/arch/arm/boot/dts/at91sam9263.dtsi
+++ b/arch/arm/boot/dts/at91sam9263.dtsi
@@ -95,6 +95,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioB: gpio@fffff400 {
@@ -104,6 +105,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioC: gpio@fffff600 {
@@ -113,6 +115,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioD: gpio@fffff800 {
@@ -122,6 +125,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioE: gpio@fffffa00 {
@@ -131,6 +135,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
dbgu: serial@ffffee00 {
diff --git a/arch/arm/boot/dts/at91sam9g45.dtsi b/arch/arm/boot/dts/at91sam9g45.dtsi
index bafa8806fc17..63751b1e744b 100644
--- a/arch/arm/boot/dts/at91sam9g45.dtsi
+++ b/arch/arm/boot/dts/at91sam9g45.dtsi
@@ -113,6 +113,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioB: gpio@fffff400 {
@@ -122,6 +123,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioC: gpio@fffff600 {
@@ -131,6 +133,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioD: gpio@fffff800 {
@@ -140,6 +143,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioE: gpio@fffffa00 {
@@ -149,6 +153,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
dbgu: serial@ffffee00 {
diff --git a/arch/arm/boot/dts/at91sam9n12.dtsi b/arch/arm/boot/dts/at91sam9n12.dtsi
index bfac0dfc332c..ef9336ae9614 100644
--- a/arch/arm/boot/dts/at91sam9n12.dtsi
+++ b/arch/arm/boot/dts/at91sam9n12.dtsi
@@ -107,6 +107,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioB: gpio@fffff600 {
@@ -116,6 +117,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioC: gpio@fffff800 {
@@ -125,6 +127,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioD: gpio@fffffa00 {
@@ -134,6 +137,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
dbgu: serial@fffff200 {
diff --git a/arch/arm/boot/dts/at91sam9x5.dtsi b/arch/arm/boot/dts/at91sam9x5.dtsi
index 4a18c393b136..8a387a8d61b7 100644
--- a/arch/arm/boot/dts/at91sam9x5.dtsi
+++ b/arch/arm/boot/dts/at91sam9x5.dtsi
@@ -115,6 +115,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioB: gpio@fffff600 {
@@ -124,6 +125,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioC: gpio@fffff800 {
@@ -133,6 +135,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
pioD: gpio@fffffa00 {
@@ -142,6 +145,7 @@
#gpio-cells = <2>;
gpio-controller;
interrupt-controller;
+ #interrupt-cells = <2>;
};
dbgu: serial@fffff200 {
diff --git a/arch/arm/boot/dts/db8500.dtsi b/arch/arm/boot/dts/dbx5x0.dtsi
index 3180a9c588b9..748ba7aa746c 100644
--- a/arch/arm/boot/dts/db8500.dtsi
+++ b/arch/arm/boot/dts/dbx5x0.dtsi
@@ -194,6 +194,8 @@
interrupts = <0 47 0x4>;
#address-cells = <1>;
#size-cells = <1>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
ranges;
prcmu-timer-4@80157450 {
@@ -330,6 +332,7 @@
ab8500@5 {
compatible = "stericsson,ab8500";
reg = <5>; /* mailbox 5 is i2c */
+ interrupt-parent = <&intc>;
interrupts = <0 40 0x4>;
interrupt-controller;
#interrupt-cells = <2>;
@@ -371,7 +374,7 @@
};
ab8500-ponkey {
- compatible = "stericsson,ab8500-ponkey";
+ compatible = "stericsson,ab8500-poweron-key";
interrupts = <6 0x4
7 0x4>;
interrupt-names = "ONKEY_DBF", "ONKEY_DBR";
@@ -389,6 +392,12 @@
compatible = "stericsson,ab8500-debug";
};
+ codec: ab8500-codec {
+ compatible = "stericsson,ab8500-codec";
+
+ stericsson,earpeice-cmv = <950>; /* Units in mV. */
+ };
+
ab8500-regulators {
compatible = "stericsson,ab8500-regulator";
@@ -471,48 +480,63 @@
};
i2c@80004000 {
- compatible = "stericsson,db8500-i2c", "st,nomadik-i2c";
+ compatible = "stericsson,db8500-i2c", "st,nomadik-i2c", "arm,primecell";
reg = <0x80004000 0x1000>;
interrupts = <0 21 0x4>;
#address-cells = <1>;
#size-cells = <0>;
+ v-i2c-supply = <&db8500_vape_reg>;
+
+ clock-frequency = <400000>;
};
i2c@80122000 {
- compatible = "stericsson,db8500-i2c", "st,nomadik-i2c";
+ compatible = "stericsson,db8500-i2c", "st,nomadik-i2c", "arm,primecell";
reg = <0x80122000 0x1000>;
interrupts = <0 22 0x4>;
#address-cells = <1>;
#size-cells = <0>;
+ v-i2c-supply = <&db8500_vape_reg>;
+
+ clock-frequency = <400000>;
};
i2c@80128000 {
- compatible = "stericsson,db8500-i2c", "st,nomadik-i2c";
+ compatible = "stericsson,db8500-i2c", "st,nomadik-i2c", "arm,primecell";
reg = <0x80128000 0x1000>;
interrupts = <0 55 0x4>;
#address-cells = <1>;
#size-cells = <0>;
+ v-i2c-supply = <&db8500_vape_reg>;
+
+ clock-frequency = <400000>;
};
i2c@80110000 {
- compatible = "stericsson,db8500-i2c", "st,nomadik-i2c";
+ compatible = "stericsson,db8500-i2c", "st,nomadik-i2c", "arm,primecell";
reg = <0x80110000 0x1000>;
interrupts = <0 12 0x4>;
#address-cells = <1>;
#size-cells = <0>;
+ v-i2c-supply = <&db8500_vape_reg>;
+
+ clock-frequency = <400000>;
};
i2c@8012a000 {
- compatible = "stericsson,db8500-i2c", "st,nomadik-i2c";
+ compatible = "stericsson,db8500-i2c", "st,nomadik-i2c", "arm,primecell";
reg = <0x8012a000 0x1000>;
interrupts = <0 51 0x4>;
#address-cells = <1>;
#size-cells = <0>;
+ v-i2c-supply = <&db8500_vape_reg>;
+
+ clock-frequency = <400000>;
};
ssp@80002000 {
compatible = "arm,pl022", "arm,primecell";
- reg = <80002000 0x1000>;
+ reg = <0x80002000 0x1000>;
interrupts = <0 14 0x4>;
#address-cells = <1>;
#size-cells = <0>;
@@ -580,6 +604,39 @@
status = "disabled";
};
+ msp0: msp@80123000 {
+ compatible = "stericsson,ux500-msp-i2s";
+ reg = <0x80123000 0x1000>;
+ interrupts = <0 31 0x4>;
+ v-ape-supply = <&db8500_vape_reg>;
+ status = "disabled";
+ };
+
+ msp1: msp@80124000 {
+ compatible = "stericsson,ux500-msp-i2s";
+ reg = <0x80124000 0x1000>;
+ interrupts = <0 62 0x4>;
+ v-ape-supply = <&db8500_vape_reg>;
+ status = "disabled";
+ };
+
+ // HDMI sound
+ msp2: msp@80117000 {
+ compatible = "stericsson,ux500-msp-i2s";
+ reg = <0x80117000 0x1000>;
+ interrupts = <0 98 0x4>;
+ v-ape-supply = <&db8500_vape_reg>;
+ status = "disabled";
+ };
+
+ msp3: msp@80125000 {
+ compatible = "stericsson,ux500-msp-i2s";
+ reg = <0x80125000 0x1000>;
+ interrupts = <0 62 0x4>;
+ v-ape-supply = <&db8500_vape_reg>;
+ status = "disabled";
+ };
+
external-bus@50000000 {
compatible = "simple-bus";
reg = <0x50000000 0x4000000>;
diff --git a/arch/arm/boot/dts/exynos4.dtsi b/arch/arm/boot/dts/exynos4.dtsi
new file mode 100644
index 000000000000..a26c3dd58269
--- /dev/null
+++ b/arch/arm/boot/dts/exynos4.dtsi
@@ -0,0 +1,248 @@
+/*
+ * Samsung's Exynos4 SoC series common device tree source
+ *
+ * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Copyright (c) 2010-2011 Linaro Ltd.
+ * www.linaro.org
+ *
+ * Samsung's Exynos4 SoC series device nodes are listed in this file. Particular
+ * SoCs from Exynos4 series can include this file and provide values for SoCs
+ * specfic bindings.
+ *
+ * Note: This file does not include device nodes for all the controllers in
+ * Exynos4 SoCs. As device tree coverage for Exynos4 increases, additional
+ * nodes can be added to this file.
+ *
+ * This 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"
+
+/ {
+ interrupt-parent = <&gic>;
+
+ aliases {
+ spi0 = &spi_0;
+ spi1 = &spi_1;
+ spi2 = &spi_2;
+ };
+
+ gic:interrupt-controller@10490000 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ interrupt-controller;
+ reg = <0x10490000 0x1000>, <0x10480000 0x100>;
+ };
+
+ combiner:interrupt-controller@10440000 {
+ compatible = "samsung,exynos4210-combiner";
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ reg = <0x10440000 0x1000>;
+ };
+
+ watchdog@10060000 {
+ compatible = "samsung,s3c2410-wdt";
+ reg = <0x10060000 0x100>;
+ interrupts = <0 43 0>;
+ status = "disabled";
+ };
+
+ rtc@10070000 {
+ compatible = "samsung,s3c6410-rtc";
+ reg = <0x10070000 0x100>;
+ interrupts = <0 44 0>, <0 45 0>;
+ status = "disabled";
+ };
+
+ keypad@100A0000 {
+ compatible = "samsung,s5pv210-keypad";
+ reg = <0x100A0000 0x100>;
+ interrupts = <0 109 0>;
+ status = "disabled";
+ };
+
+ sdhci@12510000 {
+ compatible = "samsung,exynos4210-sdhci";
+ reg = <0x12510000 0x100>;
+ interrupts = <0 73 0>;
+ status = "disabled";
+ };
+
+ sdhci@12520000 {
+ compatible = "samsung,exynos4210-sdhci";
+ reg = <0x12520000 0x100>;
+ interrupts = <0 74 0>;
+ status = "disabled";
+ };
+
+ sdhci@12530000 {
+ compatible = "samsung,exynos4210-sdhci";
+ reg = <0x12530000 0x100>;
+ interrupts = <0 75 0>;
+ status = "disabled";
+ };
+
+ sdhci@12540000 {
+ compatible = "samsung,exynos4210-sdhci";
+ reg = <0x12540000 0x100>;
+ interrupts = <0 76 0>;
+ status = "disabled";
+ };
+
+ serial@13800000 {
+ compatible = "samsung,exynos4210-uart";
+ reg = <0x13800000 0x100>;
+ interrupts = <0 52 0>;
+ status = "disabled";
+ };
+
+ serial@13810000 {
+ compatible = "samsung,exynos4210-uart";
+ reg = <0x13810000 0x100>;
+ interrupts = <0 53 0>;
+ status = "disabled";
+ };
+
+ serial@13820000 {
+ compatible = "samsung,exynos4210-uart";
+ reg = <0x13820000 0x100>;
+ interrupts = <0 54 0>;
+ status = "disabled";
+ };
+
+ serial@13830000 {
+ compatible = "samsung,exynos4210-uart";
+ reg = <0x13830000 0x100>;
+ interrupts = <0 55 0>;
+ status = "disabled";
+ };
+
+ i2c@13860000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "samsung,s3c2440-i2c";
+ reg = <0x13860000 0x100>;
+ interrupts = <0 58 0>;
+ status = "disabled";
+ };
+
+ i2c@13870000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "samsung,s3c2440-i2c";
+ reg = <0x13870000 0x100>;
+ interrupts = <0 59 0>;
+ status = "disabled";
+ };
+
+ i2c@13880000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "samsung,s3c2440-i2c";
+ reg = <0x13880000 0x100>;
+ interrupts = <0 60 0>;
+ status = "disabled";
+ };
+
+ i2c@13890000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "samsung,s3c2440-i2c";
+ reg = <0x13890000 0x100>;
+ interrupts = <0 61 0>;
+ status = "disabled";
+ };
+
+ i2c@138A0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "samsung,s3c2440-i2c";
+ reg = <0x138A0000 0x100>;
+ interrupts = <0 62 0>;
+ status = "disabled";
+ };
+
+ i2c@138B0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "samsung,s3c2440-i2c";
+ reg = <0x138B0000 0x100>;
+ interrupts = <0 63 0>;
+ status = "disabled";
+ };
+
+ i2c@138C0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "samsung,s3c2440-i2c";
+ reg = <0x138C0000 0x100>;
+ interrupts = <0 64 0>;
+ status = "disabled";
+ };
+
+ i2c@138D0000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "samsung,s3c2440-i2c";
+ reg = <0x138D0000 0x100>;
+ interrupts = <0 65 0>;
+ status = "disabled";
+ };
+
+ spi_0: spi@13920000 {
+ compatible = "samsung,exynos4210-spi";
+ reg = <0x13920000 0x100>;
+ interrupts = <0 66 0>;
+ tx-dma-channel = <&pdma0 7>; /* preliminary */
+ rx-dma-channel = <&pdma0 6>; /* preliminary */
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ spi_1: spi@13930000 {
+ compatible = "samsung,exynos4210-spi";
+ reg = <0x13930000 0x100>;
+ interrupts = <0 67 0>;
+ tx-dma-channel = <&pdma1 7>; /* preliminary */
+ rx-dma-channel = <&pdma1 6>; /* preliminary */
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ spi_2: spi@13940000 {
+ compatible = "samsung,exynos4210-spi";
+ reg = <0x13940000 0x100>;
+ interrupts = <0 68 0>;
+ tx-dma-channel = <&pdma0 9>; /* preliminary */
+ rx-dma-channel = <&pdma0 8>; /* preliminary */
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ amba {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "arm,amba-bus";
+ interrupt-parent = <&gic>;
+ ranges;
+
+ pdma0: pdma@12680000 {
+ compatible = "arm,pl330", "arm,primecell";
+ reg = <0x12680000 0x1000>;
+ interrupts = <0 35 0>;
+ };
+
+ pdma1: pdma@12690000 {
+ compatible = "arm,pl330", "arm,primecell";
+ reg = <0x12690000 0x1000>;
+ interrupts = <0 36 0>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/exynos4210-origen.dts b/arch/arm/boot/dts/exynos4210-origen.dts
index 0c49caa09978..3e68f52e8454 100644
--- a/arch/arm/boot/dts/exynos4210-origen.dts
+++ b/arch/arm/boot/dts/exynos4210-origen.dts
@@ -40,6 +40,7 @@
<&gpk2 4 2 3 3>,
<&gpk2 5 2 3 3>,
<&gpk2 6 2 3 3>;
+ status = "okay";
};
sdhci@12510000 {
@@ -53,6 +54,7 @@
<&gpk0 4 2 3 3>,
<&gpk0 5 2 3 3>,
<&gpk0 6 2 3 3>;
+ status = "okay";
};
gpio_keys {
@@ -62,88 +64,45 @@
up {
label = "Up";
- gpios = <&gpx2 0 0 0 2>;
+ gpios = <&gpx2 0 0 0x10000 2>;
linux,code = <103>;
+ gpio-key,wakeup;
};
down {
label = "Down";
- gpios = <&gpx2 1 0 0 2>;
+ gpios = <&gpx2 1 0 0x10000 2>;
linux,code = <108>;
+ gpio-key,wakeup;
};
back {
label = "Back";
- gpios = <&gpx1 7 0 0 2>;
+ gpios = <&gpx1 7 0 0x10000 2>;
linux,code = <158>;
+ gpio-key,wakeup;
};
home {
label = "Home";
- gpios = <&gpx1 6 0 0 2>;
+ gpios = <&gpx1 6 0 0x10000 2>;
linux,code = <102>;
+ gpio-key,wakeup;
};
menu {
label = "Menu";
- gpios = <&gpx1 5 0 0 2>;
+ gpios = <&gpx1 5 0 0x10000 2>;
linux,code = <139>;
+ gpio-key,wakeup;
};
};
- keypad@100A0000 {
- status = "disabled";
- };
-
- sdhci@12520000 {
- status = "disabled";
- };
-
- sdhci@12540000 {
- status = "disabled";
- };
-
- i2c@13860000 {
- status = "disabled";
- };
-
- i2c@13870000 {
- status = "disabled";
- };
-
- i2c@13880000 {
- status = "disabled";
- };
-
- i2c@13890000 {
- status = "disabled";
- };
-
- i2c@138A0000 {
- status = "disabled";
- };
-
- i2c@138B0000 {
- status = "disabled";
- };
-
- i2c@138C0000 {
- status = "disabled";
- };
-
- i2c@138D0000 {
- status = "disabled";
- };
-
- spi_0: spi@13920000 {
- status = "disabled";
- };
-
- spi_1: spi@13930000 {
- status = "disabled";
- };
-
- spi_2: spi@13940000 {
- status = "disabled";
+ leds {
+ compatible = "gpio-leds";
+ status {
+ gpios = <&gpx1 3 0 0x10000 2>;
+ linux,default-trigger = "heartbeat";
+ };
};
};
diff --git a/arch/arm/boot/dts/exynos4210-pinctrl.dtsi b/arch/arm/boot/dts/exynos4210-pinctrl.dtsi
new file mode 100644
index 000000000000..b12cf272ad0d
--- /dev/null
+++ b/arch/arm/boot/dts/exynos4210-pinctrl.dtsi
@@ -0,0 +1,457 @@
+/*
+ * Samsung's Exynos4210 SoC pin-mux and pin-config device tree source
+ *
+ * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Copyright (c) 2011-2012 Linaro Ltd.
+ * www.linaro.org
+ *
+ * Samsung's Exynos4210 SoC pin-mux and pin-config optiosn are listed as device
+ * tree nodes are listed in this file.
+ *
+ * This 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.
+*/
+
+/ {
+ pinctrl@11400000 {
+ uart0_data: uart0-data {
+ samsung,pins = "gpa0-0", "gpa0-1";
+ samsung,pin-function = <0x2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart0_fctl: uart0-fctl {
+ samsung,pins = "gpa0-2", "gpa0-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart1_data: uart1-data {
+ samsung,pins = "gpa0-4", "gpa0-5";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart1_fctl: uart1-fctl {
+ samsung,pins = "gpa0-6", "gpa0-7";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c2_bus: i2c2-bus {
+ samsung,pins = "gpa0-6", "gpa0-7";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart2_data: uart2-data {
+ samsung,pins = "gpa1-0", "gpa1-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart2_fctl: uart2-fctl {
+ samsung,pins = "gpa1-2", "gpa1-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart_audio_a: uart-audio-a {
+ samsung,pins = "gpa1-0", "gpa1-1";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c3_bus: i2c3-bus {
+ samsung,pins = "gpa1-2", "gpa1-3";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart3_data: uart3-data {
+ samsung,pins = "gpa1-4", "gpa1-5";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ uart_audio_b: uart-audio-b {
+ samsung,pins = "gpa1-4", "gpa1-5";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ spi0_bus: spi0-bus {
+ samsung,pins = "gpb-0", "gpb-2", "gpb-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c4_bus: i2c4-bus {
+ samsung,pins = "gpb-2", "gpb-3";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ spi1_bus: spi1-bus {
+ samsung,pins = "gpb-4", "gpb-6", "gpb-7";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c5_bus: i2c5-bus {
+ samsung,pins = "gpb-6", "gpb-7";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2s1_bus: i2s1-bus {
+ samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3",
+ "gpc0-4";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ pcm1_bus: pcm1-bus {
+ samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3",
+ "gpc0-4";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ ac97_bus: ac97-bus {
+ samsung,pins = "gpc0-0", "gpc0-1", "gpc0-2", "gpc0-3",
+ "gpc0-4";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2s2_bus: i2s2-bus {
+ samsung,pins = "gpc1-0", "gpc1-1", "gpc1-2", "gpc1-3",
+ "gpc1-4";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ pcm2_bus: pcm2-bus {
+ samsung,pins = "gpc1-0", "gpc1-1", "gpc1-2", "gpc1-3",
+ "gpc1-4";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ spdif_bus: spdif-bus {
+ samsung,pins = "gpc1-0", "gpc1-1";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c6_bus: i2c6-bus {
+ samsung,pins = "gpc1-3", "gpc1-4";
+ samsung,pin-function = <4>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ spi2_bus: spi2-bus {
+ samsung,pins = "gpc1-1", "gpc1-2", "gpc1-3", "gpc1-4";
+ samsung,pin-function = <5>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c7_bus: i2c7-bus {
+ samsung,pins = "gpd0-2", "gpd0-3";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c0_bus: i2c0-bus {
+ samsung,pins = "gpd1-0", "gpd1-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ i2c1_bus: i2c1-bus {
+ samsung,pins = "gpd1-2", "gpd1-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+ };
+
+ pinctrl@11000000 {
+ sd0_clk: sd0-clk {
+ samsung,pins = "gpk0-0";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd0_cmd: sd0-cmd {
+ samsung,pins = "gpk0-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd0_cd: sd0-cd {
+ samsung,pins = "gpk0-2";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd0_bus1: sd0-bus-width1 {
+ samsung,pins = "gpk0-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd0_bus4: sd0-bus-width4 {
+ samsung,pins = "gpk0-3", "gpk0-4", "gpk0-5", "gpk0-6";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd0_bus8: sd0-bus-width8 {
+ samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd4_clk: sd4-clk {
+ samsung,pins = "gpk0-0";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd4_cmd: sd4-cmd {
+ samsung,pins = "gpk0-1";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd4_cd: sd4-cd {
+ samsung,pins = "gpk0-2";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd4_bus1: sd4-bus-width1 {
+ samsung,pins = "gpk0-3";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd4_bus4: sd4-bus-width4 {
+ samsung,pins = "gpk0-3", "gpk0-4", "gpk0-5", "gpk0-6";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd4_bus8: sd4-bus-width8 {
+ samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <4>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd1_clk: sd1-clk {
+ samsung,pins = "gpk1-0";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd1_cmd: sd1-cmd {
+ samsung,pins = "gpk1-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd1_cd: sd1-cd {
+ samsung,pins = "gpk1-2";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd1_bus1: sd1-bus-width1 {
+ samsung,pins = "gpk1-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd1_bus4: sd1-bus-width4 {
+ samsung,pins = "gpk1-3", "gpk1-4", "gpk1-5", "gpk1-6";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd2_clk: sd2-clk {
+ samsung,pins = "gpk2-0";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd2_cmd: sd2-cmd {
+ samsung,pins = "gpk2-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd2_cd: sd2-cd {
+ samsung,pins = "gpk2-2";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd2_bus1: sd2-bus-width1 {
+ samsung,pins = "gpk2-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd2_bus4: sd2-bus-width4 {
+ samsung,pins = "gpk2-3", "gpk2-4", "gpk2-5", "gpk2-6";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd2_bus8: sd2-bus-width8 {
+ samsung,pins = "gpk3-3", "gpk3-4", "gpk3-5", "gpk3-6";
+ samsung,pin-function = <3>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd3_clk: sd3-clk {
+ samsung,pins = "gpk3-0";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd3_cmd: sd3-cmd {
+ samsung,pins = "gpk3-1";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd3_cd: sd3-cd {
+ samsung,pins = "gpk3-2";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd3_bus1: sd3-bus-width1 {
+ samsung,pins = "gpk3-3";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ sd3_bus4: sd3-bus-width4 {
+ samsung,pins = "gpk3-3", "gpk3-4", "gpk3-5", "gpk3-6";
+ samsung,pin-function = <2>;
+ samsung,pin-pud = <3>;
+ samsung,pin-drv = <0>;
+ };
+
+ eint0: ext-int0 {
+ samsung,pins = "gpx0-0";
+ samsung,pin-function = <0xf>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ eint8: ext-int8 {
+ samsung,pins = "gpx1-0";
+ samsung,pin-function = <0xf>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ eint15: ext-int15 {
+ samsung,pins = "gpx1-7";
+ samsung,pin-function = <0xf>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ eint16: ext-int16 {
+ samsung,pins = "gpx2-0";
+ samsung,pin-function = <0xf>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ eint31: ext-int31 {
+ samsung,pins = "gpx3-7";
+ samsung,pin-function = <0xf>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+ };
+
+ pinctrl@03860000 {
+ i2s0_bus: i2s0-bus {
+ samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3",
+ "gpz-4", "gpz-5", "gpz-6";
+ samsung,pin-function = <0x2>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+
+ pcm0_bus: pcm0-bus {
+ samsung,pins = "gpz-0", "gpz-1", "gpz-2", "gpz-3",
+ "gpz-4";
+ samsung,pin-function = <0x3>;
+ samsung,pin-pud = <0>;
+ samsung,pin-drv = <0>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/exynos4210-smdkv310.dts b/arch/arm/boot/dts/exynos4210-smdkv310.dts
index 1beccc8f14ff..63610c3ba3af 100644
--- a/arch/arm/boot/dts/exynos4210-smdkv310.dts
+++ b/arch/arm/boot/dts/exynos4210-smdkv310.dts
@@ -26,7 +26,7 @@
};
chosen {
- bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC1,115200 init=/linuxrc";
+ bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc";
};
sdhci@12530000 {
@@ -40,6 +40,7 @@
<&gpk2 4 2 3 3>,
<&gpk2 5 2 3 3>,
<&gpk2 6 2 3 3>;
+ status = "okay";
};
keypad@100A0000 {
@@ -47,6 +48,7 @@
samsung,keypad-num-columns = <8>;
linux,keypad-no-autorepeat;
linux,keypad-wakeup;
+ status = "okay";
row-gpios = <&gpx2 0 3 3 0>,
<&gpx2 1 3 3 0>;
@@ -128,6 +130,7 @@
samsung,i2c-max-bus-freq = <20000>;
gpios = <&gpd1 0 2 3 0>,
<&gpd1 1 2 3 0>;
+ status = "okay";
eeprom@50 {
compatible = "samsung,24ad0xd1";
@@ -140,58 +143,11 @@
};
};
- sdhci@12510000 {
- status = "disabled";
- };
-
- sdhci@12520000 {
- status = "disabled";
- };
-
- sdhci@12540000 {
- status = "disabled";
- };
-
- i2c@13870000 {
- status = "disabled";
- };
-
- i2c@13880000 {
- status = "disabled";
- };
-
- i2c@13890000 {
- status = "disabled";
- };
-
- i2c@138A0000 {
- status = "disabled";
- };
-
- i2c@138B0000 {
- status = "disabled";
- };
-
- i2c@138C0000 {
- status = "disabled";
- };
-
- i2c@138D0000 {
- status = "disabled";
- };
-
- spi_0: spi@13920000 {
- status = "disabled";
- };
-
- spi_1: spi@13930000 {
- status = "disabled";
- };
-
spi_2: spi@13940000 {
gpios = <&gpc1 1 5 3 0>,
<&gpc1 3 5 3 0>,
<&gpc1 4 5 3 0>;
+ status = "okay";
w25x80@0 {
#address-cells = <1>;
diff --git a/arch/arm/boot/dts/exynos4210-trats.dts b/arch/arm/boot/dts/exynos4210-trats.dts
new file mode 100644
index 000000000000..73567b843e72
--- /dev/null
+++ b/arch/arm/boot/dts/exynos4210-trats.dts
@@ -0,0 +1,237 @@
+/*
+ * Samsung's Exynos4210 based Trats board device tree source
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Device tree source file for Samsung's Trats board which is based on
+ * Samsung's Exynos4210 SoC.
+ *
+ * This 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/ "exynos4210.dtsi"
+
+/ {
+ model = "Samsung Trats based on Exynos4210";
+ compatible = "samsung,trats", "samsung,exynos4210";
+
+ memory {
+ reg = <0x40000000 0x20000000
+ 0x60000000 0x20000000>;
+ };
+
+ chosen {
+ bootargs = "console=ttySAC2,115200N8 root=/dev/mmcblk0p5 rootwait earlyprintk panic=5";
+ };
+
+ vemmc_reg: voltage-regulator@0 {
+ compatible = "regulator-fixed";
+ regulator-name = "VMEM_VDD_2.8V";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ gpio = <&gpk0 2 1 0 0>;
+ enable-active-high;
+ };
+
+ sdhci_emmc: sdhci@12510000 {
+ bus-width = <8>;
+ non-removable;
+ broken-voltage;
+ gpios = <&gpk0 0 2 0 3>,
+ <&gpk0 1 2 0 3>,
+ <&gpk0 3 2 2 3>,
+ <&gpk0 4 2 2 3>,
+ <&gpk0 5 2 2 3>,
+ <&gpk0 6 2 2 3>,
+ <&gpk1 3 3 3 3>,
+ <&gpk1 4 3 3 3>,
+ <&gpk1 5 3 3 3>,
+ <&gpk1 6 3 3 3>;
+ vmmc-supply = <&vemmc_reg>;
+ status = "okay";
+ };
+
+ serial@13800000 {
+ status = "okay";
+ };
+
+ serial@13810000 {
+ status = "okay";
+ };
+
+ serial@13820000 {
+ status = "okay";
+ };
+
+ serial@13830000 {
+ status = "okay";
+ };
+
+ i2c@138B0000 {
+ samsung,i2c-sda-delay = <100>;
+ samsung,i2c-slave-addr = <0x10>;
+ samsung,i2c-max-bus-freq = <100000>;
+ gpios = <&gpb 6 3 3 0>,
+ <&gpb 7 3 3 0>;
+ status = "okay";
+
+ max8997_pmic@66 {
+ compatible = "maxim,max8997-pmic";
+
+ reg = <0x66>;
+
+ max8997,pmic-buck1-uses-gpio-dvs;
+ max8997,pmic-buck2-uses-gpio-dvs;
+ max8997,pmic-buck5-uses-gpio-dvs;
+
+ max8997,pmic-ignore-gpiodvs-side-effect;
+ max8997,pmic-buck125-default-dvs-idx = <0>;
+
+ max8997,pmic-buck125-dvs-gpios = <&gpx0 5 1 0 0>,
+ <&gpx0 6 1 0 0>,
+ <&gpl0 0 1 0 0>;
+
+ max8997,pmic-buck1-dvs-voltage = <1350000>, <1300000>,
+ <1250000>, <1200000>,
+ <1150000>, <1100000>,
+ <1000000>, <950000>;
+
+ max8997,pmic-buck2-dvs-voltage = <1100000>, <1000000>,
+ <950000>, <900000>,
+ <1100000>, <1000000>,
+ <950000>, <900000>;
+
+ max8997,pmic-buck5-dvs-voltage = <1200000>, <1200000>,
+ <1200000>, <1200000>,
+ <1200000>, <1200000>,
+ <1200000>, <1200000>;
+
+ regulators {
+ valive_reg: LDO2 {
+ regulator-name = "VALIVE_1.1V_C210";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ };
+
+ vusb_reg: LDO3 {
+ regulator-name = "VUSB_1.1V_C210";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ };
+
+ vmipi_reg: LDO4 {
+ regulator-name = "VMIPI_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ vpda_reg: LDO6 {
+ regulator-name = "VCC_1.8V_PDA";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-always-on;
+ };
+
+ vcam_reg: LDO7 {
+ regulator-name = "CAM_ISP_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ vusbdac_reg: LDO8 {
+ regulator-name = "VUSB/VDAC_3.3V_C210";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ vccpda_reg: LDO9 {
+ regulator-name = "VCC_2.8V_PDA";
+ regulator-min-microvolt = <2800000>;
+ regulator-max-microvolt = <2800000>;
+ regulator-always-on;
+ };
+
+ vpll_reg: LDO10 {
+ regulator-name = "VPLL_1.1V_C210";
+ regulator-min-microvolt = <1100000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ };
+
+ vcclcd_reg: LDO13 {
+ regulator-name = "VCC_3.3V_LCD";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ vlcd_reg: LDO15 {
+ regulator-name = "VLCD_2.2V";
+ regulator-min-microvolt = <2200000>;
+ regulator-max-microvolt = <2200000>;
+ };
+
+ camsensor_reg: LDO16 {
+ regulator-name = "CAM_SENSOR_IO_1.8V";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ };
+
+ vddq_reg: LDO21 {
+ regulator-name = "VDDQ_M1M2_1.2V";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ };
+
+ varm_breg: BUCK1 {
+ regulator-name = "VARM_1.2V_C210";
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1350000>;
+ regulator-always-on;
+ };
+
+ vint_breg: BUCK2 {
+ regulator-name = "VINT_1.1V_C210";
+ regulator-min-microvolt = <900000>;
+ regulator-max-microvolt = <1100000>;
+ regulator-always-on;
+ };
+
+ camisp_breg: BUCK4 {
+ regulator-name = "CAM_ISP_CORE_1.2V";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ };
+
+ vmem_breg: BUCK5 {
+ regulator-name = "VMEM_1.2V_C210";
+ regulator-min-microvolt = <1200000>;
+ regulator-max-microvolt = <1200000>;
+ regulator-always-on;
+ };
+
+ vccsub_breg: BUCK7 {
+ regulator-name = "VCC_SUB_2.0V";
+ regulator-min-microvolt = <2000000>;
+ regulator-max-microvolt = <2000000>;
+ regulator-always-on;
+ };
+
+ safe1_sreg: ESAFEOUT1 {
+ regulator-name = "SAFEOUT1";
+ regulator-always-on;
+ };
+
+ safe2_sreg: ESAFEOUT2 {
+ regulator-name = "SAFEOUT2";
+ regulator-boot-on;
+ };
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi
index 02891fe876e4..214c557eda7f 100644
--- a/arch/arm/boot/dts/exynos4210.dtsi
+++ b/arch/arm/boot/dts/exynos4210.dtsi
@@ -19,199 +19,60 @@
* published by the Free Software Foundation.
*/
-/include/ "skeleton.dtsi"
+/include/ "exynos4.dtsi"
+/include/ "exynos4210-pinctrl.dtsi"
/ {
compatible = "samsung,exynos4210";
- interrupt-parent = <&gic>;
aliases {
- spi0 = &spi_0;
- spi1 = &spi_1;
- spi2 = &spi_2;
+ pinctrl0 = &pinctrl_0;
+ pinctrl1 = &pinctrl_1;
+ pinctrl2 = &pinctrl_2;
};
gic:interrupt-controller@10490000 {
- compatible = "arm,cortex-a9-gic";
- #interrupt-cells = <3>;
- interrupt-controller;
cpu-offset = <0x8000>;
- reg = <0x10490000 0x1000>, <0x10480000 0x100>;
};
combiner:interrupt-controller@10440000 {
- compatible = "samsung,exynos4210-combiner";
- #interrupt-cells = <2>;
- interrupt-controller;
- reg = <0x10440000 0x1000>;
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>;
};
- watchdog@10060000 {
- compatible = "samsung,s3c2410-wdt";
- reg = <0x10060000 0x100>;
- interrupts = <0 43 0>;
- };
-
- rtc@10070000 {
- compatible = "samsung,s3c6410-rtc";
- reg = <0x10070000 0x100>;
- interrupts = <0 44 0>, <0 45 0>;
- };
-
- keypad@100A0000 {
- compatible = "samsung,s5pv210-keypad";
- reg = <0x100A0000 0x100>;
- interrupts = <0 109 0>;
- };
-
- sdhci@12510000 {
- compatible = "samsung,exynos4210-sdhci";
- reg = <0x12510000 0x100>;
- interrupts = <0 73 0>;
- };
-
- sdhci@12520000 {
- compatible = "samsung,exynos4210-sdhci";
- reg = <0x12520000 0x100>;
- interrupts = <0 74 0>;
- };
-
- sdhci@12530000 {
- compatible = "samsung,exynos4210-sdhci";
- reg = <0x12530000 0x100>;
- interrupts = <0 75 0>;
- };
-
- sdhci@12540000 {
- compatible = "samsung,exynos4210-sdhci";
- reg = <0x12540000 0x100>;
- interrupts = <0 76 0>;
- };
-
- serial@13800000 {
- compatible = "samsung,exynos4210-uart";
- reg = <0x13800000 0x100>;
- interrupts = <0 52 0>;
- };
-
- serial@13810000 {
- compatible = "samsung,exynos4210-uart";
- reg = <0x13810000 0x100>;
- interrupts = <0 53 0>;
- };
-
- serial@13820000 {
- compatible = "samsung,exynos4210-uart";
- reg = <0x13820000 0x100>;
- interrupts = <0 54 0>;
- };
-
- serial@13830000 {
- compatible = "samsung,exynos4210-uart";
- reg = <0x13830000 0x100>;
- interrupts = <0 55 0>;
- };
-
- i2c@13860000 {
- compatible = "samsung,s3c2440-i2c";
- reg = <0x13860000 0x100>;
- interrupts = <0 58 0>;
- };
-
- i2c@13870000 {
- compatible = "samsung,s3c2440-i2c";
- reg = <0x13870000 0x100>;
- interrupts = <0 59 0>;
- };
-
- i2c@13880000 {
- compatible = "samsung,s3c2440-i2c";
- reg = <0x13880000 0x100>;
- interrupts = <0 60 0>;
- };
-
- i2c@13890000 {
- compatible = "samsung,s3c2440-i2c";
- reg = <0x13890000 0x100>;
- interrupts = <0 61 0>;
- };
-
- i2c@138A0000 {
- compatible = "samsung,s3c2440-i2c";
- reg = <0x138A0000 0x100>;
- interrupts = <0 62 0>;
- };
-
- i2c@138B0000 {
- compatible = "samsung,s3c2440-i2c";
- reg = <0x138B0000 0x100>;
- interrupts = <0 63 0>;
- };
-
- i2c@138C0000 {
- compatible = "samsung,s3c2440-i2c";
- reg = <0x138C0000 0x100>;
- interrupts = <0 64 0>;
- };
-
- i2c@138D0000 {
- compatible = "samsung,s3c2440-i2c";
- reg = <0x138D0000 0x100>;
- interrupts = <0 65 0>;
- };
-
- spi_0: spi@13920000 {
- compatible = "samsung,exynos4210-spi";
- reg = <0x13920000 0x100>;
- interrupts = <0 66 0>;
- tx-dma-channel = <&pdma0 7>; /* preliminary */
- rx-dma-channel = <&pdma0 6>; /* preliminary */
- #address-cells = <1>;
- #size-cells = <0>;
- };
-
- spi_1: spi@13930000 {
- compatible = "samsung,exynos4210-spi";
- reg = <0x13930000 0x100>;
- interrupts = <0 67 0>;
- tx-dma-channel = <&pdma1 7>; /* preliminary */
- rx-dma-channel = <&pdma1 6>; /* preliminary */
- #address-cells = <1>;
- #size-cells = <0>;
- };
-
- spi_2: spi@13940000 {
- compatible = "samsung,exynos4210-spi";
- reg = <0x13940000 0x100>;
- interrupts = <0 68 0>;
- tx-dma-channel = <&pdma0 9>; /* preliminary */
- rx-dma-channel = <&pdma0 8>; /* preliminary */
- #address-cells = <1>;
- #size-cells = <0>;
+ pinctrl_0: pinctrl@11400000 {
+ compatible = "samsung,pinctrl-exynos4210";
+ reg = <0x11400000 0x1000>;
+ interrupts = <0 47 0>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
};
- amba {
- #address-cells = <1>;
- #size-cells = <1>;
- compatible = "arm,amba-bus";
- interrupt-parent = <&gic>;
- ranges;
+ pinctrl_1: pinctrl@11000000 {
+ compatible = "samsung,pinctrl-exynos4210";
+ reg = <0x11000000 0x1000>;
+ interrupts = <0 46 0>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
- pdma0: pdma@12680000 {
- compatible = "arm,pl330", "arm,primecell";
- reg = <0x12680000 0x1000>;
- interrupts = <0 35 0>;
+ wakup_eint: wakeup-interrupt-controller {
+ compatible = "samsung,exynos4210-wakeup-eint";
+ interrupt-parent = <&gic>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ interrupts = <0 16 0>, <0 17 0>, <0 18 0>, <0 19 0>,
+ <0 20 0>, <0 21 0>, <0 22 0>, <0 23 0>,
+ <0 24 0>, <0 25 0>, <0 26 0>, <0 27 0>,
+ <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>,
+ <0 32 0>;
};
+ };
- pdma1: pdma@12690000 {
- compatible = "arm,pl330", "arm,primecell";
- reg = <0x12690000 0x1000>;
- interrupts = <0 36 0>;
- };
+ pinctrl_2: pinctrl@03860000 {
+ compatible = "samsung,pinctrl-exynos4210";
+ reg = <0x03860000 0x1000>;
};
gpio-controllers {
diff --git a/arch/arm/boot/dts/exynos5250-smdk5250.dts b/arch/arm/boot/dts/exynos5250-smdk5250.dts
index 8a5e348793c7..a352df403b7a 100644
--- a/arch/arm/boot/dts/exynos5250-smdk5250.dts
+++ b/arch/arm/boot/dts/exynos5250-smdk5250.dts
@@ -16,12 +16,19 @@
model = "SAMSUNG SMDK5250 board based on EXYNOS5250";
compatible = "samsung,smdk5250", "samsung,exynos5250";
+ aliases {
+ mshc0 = &dwmmc_0;
+ mshc1 = &dwmmc_1;
+ mshc2 = &dwmmc_2;
+ mshc3 = &dwmmc_3;
+ };
+
memory {
reg = <0x40000000 0x80000000>;
};
chosen {
- bootargs = "root=/dev/ram0 rw ramdisk=8192 console=ttySAC1,115200";
+ bootargs = "root=/dev/ram0 rw ramdisk=8192 initrd=0x41000000,8M console=ttySAC2,115200 init=/linuxrc";
};
i2c@12C60000 {
@@ -72,6 +79,56 @@
status = "disabled";
};
+ dwmmc_0: dwmmc0@12200000 {
+ num-slots = <1>;
+ supports-highspeed;
+ broken-cd;
+ fifo-depth = <0x80>;
+ card-detect-delay = <200>;
+ samsung,dw-mshc-ciu-div = <3>;
+ samsung,dw-mshc-sdr-timing = <2 3 3>;
+ samsung,dw-mshc-ddr-timing = <1 2 3>;
+
+ slot@0 {
+ reg = <0>;
+ bus-width = <8>;
+ gpios = <&gpc0 0 2 0 3>, <&gpc0 1 2 0 3>,
+ <&gpc1 0 2 3 3>, <&gpc1 1 2 3 3>,
+ <&gpc1 2 2 3 3>, <&gpc1 3 2 3 3>,
+ <&gpc0 3 2 3 3>, <&gpc0 4 2 3 3>,
+ <&gpc0 5 2 3 3>, <&gpc0 6 2 3 3>;
+ };
+ };
+
+ dwmmc_1: dwmmc1@12210000 {
+ status = "disabled";
+ };
+
+ dwmmc_2: dwmmc2@12220000 {
+ num-slots = <1>;
+ supports-highspeed;
+ fifo-depth = <0x80>;
+ card-detect-delay = <200>;
+ samsung,dw-mshc-ciu-div = <3>;
+ samsung,dw-mshc-sdr-timing = <2 3 3>;
+ samsung,dw-mshc-ddr-timing = <1 2 3>;
+
+ slot@0 {
+ reg = <0>;
+ bus-width = <4>;
+ samsung,cd-pinmux-gpio = <&gpc3 2 2 3 3>;
+ gpios = <&gpc3 0 2 0 3>, <&gpc3 1 2 0 3>,
+ <&gpc3 3 2 3 3>, <&gpc3 4 2 3 3>,
+ <&gpc3 5 2 3 3>, <&gpc3 6 2 3 3>,
+ <&gpc4 3 3 3 3>, <&gpc4 3 3 3 3>,
+ <&gpc4 5 3 3 3>, <&gpc4 6 3 3 3>;
+ };
+ };
+
+ dwmmc_3: dwmmc3@12230000 {
+ status = "disabled";
+ };
+
spi_0: spi@12d20000 {
status = "disabled";
};
diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi
index 004aaa8d123c..dddfd6e444dc 100644
--- a/arch/arm/boot/dts/exynos5250.dtsi
+++ b/arch/arm/boot/dts/exynos5250.dtsi
@@ -27,6 +27,10 @@
spi0 = &spi_0;
spi1 = &spi_1;
spi2 = &spi_2;
+ gsc0 = &gsc_0;
+ gsc1 = &gsc_1;
+ gsc2 = &gsc_2;
+ gsc3 = &gsc_3;
};
gic:interrupt-controller@10481000 {
@@ -182,6 +186,38 @@
#size-cells = <0>;
};
+ dwmmc0@12200000 {
+ compatible = "samsung,exynos5250-dw-mshc";
+ reg = <0x12200000 0x1000>;
+ interrupts = <0 75 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ dwmmc1@12210000 {
+ compatible = "samsung,exynos5250-dw-mshc";
+ reg = <0x12210000 0x1000>;
+ interrupts = <0 76 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ dwmmc2@12220000 {
+ compatible = "samsung,exynos5250-dw-mshc";
+ reg = <0x12220000 0x1000>;
+ interrupts = <0 77 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ dwmmc3@12230000 {
+ compatible = "samsung,exynos5250-dw-mshc";
+ reg = <0x12230000 0x1000>;
+ interrupts = <0 78 0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
amba {
#address-cells = <1>;
#size-cells = <1>;
@@ -460,4 +496,28 @@
#gpio-cells = <4>;
};
};
+
+ gsc_0: gsc@0x13e00000 {
+ compatible = "samsung,exynos5-gsc";
+ reg = <0x13e00000 0x1000>;
+ interrupts = <0 85 0>;
+ };
+
+ gsc_1: gsc@0x13e10000 {
+ compatible = "samsung,exynos5-gsc";
+ reg = <0x13e10000 0x1000>;
+ interrupts = <0 86 0>;
+ };
+
+ gsc_2: gsc@0x13e20000 {
+ compatible = "samsung,exynos5-gsc";
+ reg = <0x13e20000 0x1000>;
+ interrupts = <0 87 0>;
+ };
+
+ gsc_3: gsc@0x13e30000 {
+ compatible = "samsung,exynos5-gsc";
+ reg = <0x13e30000 0x1000>;
+ interrupts = <0 88 0>;
+ };
};
diff --git a/arch/arm/boot/dts/hrefv60plus.dts b/arch/arm/boot/dts/hrefv60plus.dts
new file mode 100644
index 000000000000..2131d77dc9c9
--- /dev/null
+++ b/arch/arm/boot/dts/hrefv60plus.dts
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2012 ST-Ericsson AB
+ *
+ * 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/ "dbx5x0.dtsi"
+
+/ {
+ model = "ST-Ericsson HREF platform with Device Tree";
+ compatible = "st-ericsson,hrefv60+";
+
+ memory {
+ reg = <0x00000000 0x20000000>;
+ };
+
+ soc-u9500 {
+ uart@80120000 {
+ status = "okay";
+ };
+
+ uart@80121000 {
+ status = "okay";
+ };
+
+ uart@80007000 {
+ status = "okay";
+ };
+
+ i2c@80004000 {
+ tc3589x@42 {
+ compatible = "tc3589x";
+ reg = <0x42>;
+ interrupt-parent = <&gpio6>;
+ interrupts = <25 0x1>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+
+ tc3589x_gpio: tc3589x_gpio {
+ compatible = "tc3589x-gpio";
+ interrupts = <0 0x1>;
+
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ };
+ };
+
+ tps61052@33 {
+ compatible = "tps61052";
+ reg = <0x33>;
+ };
+ };
+
+ i2c@80128000 {
+ lp5521@0x33 {
+ compatible = "lp5521";
+ reg = <0x33>;
+ };
+
+ lp5521@0x34 {
+ compatible = "lp5521";
+ reg = <0x34>;
+ };
+
+ bh1780@0x29 {
+ compatible = "rohm,bh1780gli";
+ reg = <0x33>;
+ };
+ };
+
+ sound {
+ compatible = "stericsson,snd-soc-mop500";
+
+ stericsson,cpu-dai = <&msp1 &msp3>;
+ stericsson,audio-codec = <&codec>;
+ };
+
+ msp1: msp@80124000 {
+ status = "okay";
+ };
+
+ msp3: msp@80125000 {
+ status = "okay";
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/imx23.dtsi b/arch/arm/boot/dts/imx23.dtsi
index 3f3b6fc229b3..9ca4ca70c1bc 100644
--- a/arch/arm/boot/dts/imx23.dtsi
+++ b/arch/arm/boot/dts/imx23.dtsi
@@ -43,7 +43,7 @@
ranges;
icoll: interrupt-controller@80000000 {
- compatible = "fsl,imx23-icoll", "fsl,mxs-icoll";
+ compatible = "fsl,imx23-icoll", "fsl,icoll";
interrupt-controller;
#interrupt-cells = <1>;
reg = <0x80000000 0x2000>;
@@ -407,8 +407,9 @@
};
timrot@80068000 {
+ compatible = "fsl,imx23-timrot", "fsl,timrot";
reg = <0x80068000 0x2000>;
- status = "disabled";
+ interrupts = <28 29 30 31>;
};
auart0: serial@8006c000 {
diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi
index 724147eab84b..59fbfba23df8 100644
--- a/arch/arm/boot/dts/imx28.dtsi
+++ b/arch/arm/boot/dts/imx28.dtsi
@@ -52,7 +52,7 @@
ranges;
icoll: interrupt-controller@80000000 {
- compatible = "fsl,imx28-icoll", "fsl,mxs-icoll";
+ compatible = "fsl,imx28-icoll", "fsl,icoll";
interrupt-controller;
#interrupt-cells = <1>;
reg = <0x80000000 0x2000>;
@@ -787,8 +787,9 @@
};
timrot@80068000 {
+ compatible = "fsl,imx28-timrot", "fsl,timrot";
reg = <0x80068000 0x2000>;
- status = "disabled";
+ interrupts = <48 49 50 51>;
};
auart0: serial@8006a000 {
diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi
index 5d1c48459e6e..3883f94fdbd0 100644
--- a/arch/arm/boot/dts/omap4.dtsi
+++ b/arch/arm/boot/dts/omap4.dtsi
@@ -430,5 +430,13 @@
hw-caps-ll-interface;
hw-caps-temp-alert;
};
+
+ ocp2scp {
+ compatible = "ti,omap-ocp2scp";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+ ti,hwmods = "ocp2scp_usb_phy";
+ };
};
};
diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi
index 9ac75b37c992..5db33f481a33 100644
--- a/arch/arm/boot/dts/omap5.dtsi
+++ b/arch/arm/boot/dts/omap5.dtsi
@@ -33,9 +33,21 @@
cpus {
cpu@0 {
compatible = "arm,cortex-a15";
+ timer {
+ compatible = "arm,armv7-timer";
+ /* 14th PPI IRQ, active low level-sensitive */
+ interrupts = <1 14 0x308>;
+ clock-frequency = <6144000>;
+ };
};
cpu@1 {
compatible = "arm,cortex-a15";
+ timer {
+ compatible = "arm,armv7-timer";
+ /* 14th PPI IRQ, active low level-sensitive */
+ interrupts = <1 14 0x308>;
+ clock-frequency = <6144000>;
+ };
};
};
diff --git a/arch/arm/boot/dts/phy3250.dts b/arch/arm/boot/dts/phy3250.dts
index a7ad85e4b8f9..90fdbd77f274 100644
--- a/arch/arm/boot/dts/phy3250.dts
+++ b/arch/arm/boot/dts/phy3250.dts
@@ -189,16 +189,14 @@
leds {
compatible = "gpio-leds";
- led0 {
- gpios = <&gpio 5 1 1>; /* GPO_P3 1, GPIO 80, active low */
- linux,default-trigger = "heartbeat";
+ led0 { /* red */
+ gpios = <&gpio 5 1 0>; /* GPO_P3 1, GPIO 80, active high */
default-state = "off";
};
- led1 {
- gpios = <&gpio 5 14 1>; /* GPO_P3 14, GPIO 93, active low */
- linux,default-trigger = "timer";
- default-state = "off";
+ led1 { /* green */
+ gpios = <&gpio 5 14 0>; /* GPO_P3 14, GPIO 93, active high */
+ linux,default-trigger = "heartbeat";
};
};
};
diff --git a/arch/arm/boot/dts/snowball.dts b/arch/arm/boot/dts/snowball.dts
index 7e334d4cae21..702c0baa6004 100644
--- a/arch/arm/boot/dts/snowball.dts
+++ b/arch/arm/boot/dts/snowball.dts
@@ -10,7 +10,7 @@
*/
/dts-v1/;
-/include/ "db8500.dtsi"
+/include/ "dbx5x0.dtsi"
/ {
model = "Calao Systems Snowball platform with device tree";
@@ -83,6 +83,22 @@
};
soc-u9500 {
+
+ sound {
+ compatible = "stericsson,snd-soc-mop500";
+
+ stericsson,cpu-dai = <&msp1 &msp3>;
+ stericsson,audio-codec = <&codec>;
+ };
+
+ msp1: msp@80124000 {
+ status = "okay";
+ };
+
+ msp3: msp@80125000 {
+ status = "okay";
+ };
+
external-bus@50000000 {
status = "okay";
@@ -111,7 +127,6 @@
mmc-cap-mmc-highspeed;
vmmc-supply = <&ab8500_ldo_aux3_reg>;
- #gpio-cells = <1>;
cd-gpios = <&gpio6 26 0x4>; // 218
cd-inverted;
diff --git a/arch/arm/boot/dts/vt8500-bv07.dts b/arch/arm/boot/dts/vt8500-bv07.dts
new file mode 100644
index 000000000000..567cf4e8ab84
--- /dev/null
+++ b/arch/arm/boot/dts/vt8500-bv07.dts
@@ -0,0 +1,36 @@
+/*
+ * vt8500-bv07.dts - Device tree file for Benign BV07 Netbook
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * Licensed under GPLv2 or later
+ */
+
+/dts-v1/;
+/include/ "vt8500.dtsi"
+
+/ {
+ model = "Benign BV07 Netbook";
+
+ /*
+ * Display node is based on Sascha Hauer's patch on dri-devel.
+ * Added a bpp property to calculate the size of the framebuffer
+ * until the binding is formalized.
+ */
+ display: display@0 {
+ modes {
+ mode0: mode@0 {
+ hactive = <800>;
+ vactive = <480>;
+ hback-porch = <88>;
+ hfront-porch = <40>;
+ hsync-len = <0>;
+ vback-porch = <32>;
+ vfront-porch = <11>;
+ vsync-len = <1>;
+ clock = <0>; /* unused but required */
+ bpp = <16>; /* non-standard but required */
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/vt8500.dtsi b/arch/arm/boot/dts/vt8500.dtsi
new file mode 100644
index 000000000000..d8645e990b21
--- /dev/null
+++ b/arch/arm/boot/dts/vt8500.dtsi
@@ -0,0 +1,116 @@
+/*
+ * vt8500.dtsi - Device tree file for VIA VT8500 SoC
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * Licensed under GPLv2 or later
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+ compatible = "via,vt8500";
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges;
+ interrupt-parent = <&intc>;
+
+ intc: interrupt-controller@d8140000 {
+ compatible = "via,vt8500-intc";
+ interrupt-controller;
+ reg = <0xd8140000 0x10000>;
+ #interrupt-cells = <1>;
+ };
+
+ gpio: gpio-controller@d8110000 {
+ compatible = "via,vt8500-gpio";
+ gpio-controller;
+ reg = <0xd8110000 0x10000>;
+ #gpio-cells = <3>;
+ };
+
+ pmc@d8130000 {
+ compatible = "via,vt8500-pmc";
+ reg = <0xd8130000 0x1000>;
+
+ clocks {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ref24: ref24M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ };
+ };
+ };
+
+ timer@d8130100 {
+ compatible = "via,vt8500-timer";
+ reg = <0xd8130100 0x28>;
+ interrupts = <36>;
+ };
+
+ ehci@d8007900 {
+ compatible = "via,vt8500-ehci";
+ reg = <0xd8007900 0x200>;
+ interrupts = <43>;
+ };
+
+ uhci@d8007b00 {
+ compatible = "platform-uhci";
+ reg = <0xd8007b00 0x200>;
+ interrupts = <43>;
+ };
+
+ fb@d800e400 {
+ compatible = "via,vt8500-fb";
+ reg = <0xd800e400 0x400>;
+ interrupts = <12>;
+ display = <&display>;
+ default-mode = <&mode0>;
+ };
+
+ ge_rops@d8050400 {
+ compatible = "wm,prizm-ge-rops";
+ reg = <0xd8050400 0x100>;
+ };
+
+ uart@d8200000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd8200000 0x1040>;
+ interrupts = <32>;
+ clocks = <&ref24>;
+ };
+
+ uart@d82b0000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd82b0000 0x1040>;
+ interrupts = <33>;
+ clocks = <&ref24>;
+ };
+
+ uart@d8210000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd8210000 0x1040>;
+ interrupts = <47>;
+ clocks = <&ref24>;
+ };
+
+ uart@d82c0000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd82c0000 0x1040>;
+ interrupts = <50>;
+ clocks = <&ref24>;
+ };
+
+ rtc@d8100000 {
+ compatible = "via,vt8500-rtc";
+ reg = <0xd8100000 0x10000>;
+ interrupts = <48>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/wm8505-ref.dts b/arch/arm/boot/dts/wm8505-ref.dts
new file mode 100644
index 000000000000..fd4e248074c6
--- /dev/null
+++ b/arch/arm/boot/dts/wm8505-ref.dts
@@ -0,0 +1,36 @@
+/*
+ * wm8505-ref.dts - Device tree file for Wondermedia WM8505 reference netbook
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * Licensed under GPLv2 or later
+ */
+
+/dts-v1/;
+/include/ "wm8505.dtsi"
+
+/ {
+ model = "Wondermedia WM8505 Netbook";
+
+ /*
+ * Display node is based on Sascha Hauer's patch on dri-devel.
+ * Added a bpp property to calculate the size of the framebuffer
+ * until the binding is formalized.
+ */
+ display: display@0 {
+ modes {
+ mode0: mode@0 {
+ hactive = <800>;
+ vactive = <480>;
+ hback-porch = <88>;
+ hfront-porch = <40>;
+ hsync-len = <0>;
+ vback-porch = <32>;
+ vfront-porch = <11>;
+ vsync-len = <1>;
+ clock = <0>; /* unused but required */
+ bpp = <32>; /* non-standard but required */
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/wm8505.dtsi b/arch/arm/boot/dts/wm8505.dtsi
new file mode 100644
index 000000000000..b459691655ab
--- /dev/null
+++ b/arch/arm/boot/dts/wm8505.dtsi
@@ -0,0 +1,143 @@
+/*
+ * wm8505.dtsi - Device tree file for Wondermedia WM8505 SoC
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * Licensed under GPLv2 or later
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+ compatible = "wm,wm8505";
+
+ cpus {
+ cpu@0 {
+ compatible = "arm,arm926ejs";
+ };
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges;
+ interrupt-parent = <&intc0>;
+
+ intc0: interrupt-controller@d8140000 {
+ compatible = "via,vt8500-intc";
+ interrupt-controller;
+ reg = <0xd8140000 0x10000>;
+ #interrupt-cells = <1>;
+ };
+
+ /* Secondary IC cascaded to intc0 */
+ intc1: interrupt-controller@d8150000 {
+ compatible = "via,vt8500-intc";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0xD8150000 0x10000>;
+ interrupts = <56 57 58 59 60 61 62 63>;
+ };
+
+ gpio: gpio-controller@d8110000 {
+ compatible = "wm,wm8505-gpio";
+ gpio-controller;
+ reg = <0xd8110000 0x10000>;
+ #gpio-cells = <3>;
+ };
+
+ pmc@d8130000 {
+ compatible = "via,vt8500-pmc";
+ reg = <0xd8130000 0x1000>;
+ clocks {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ref24: ref24M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ };
+ };
+ };
+
+ timer@d8130100 {
+ compatible = "via,vt8500-timer";
+ reg = <0xd8130100 0x28>;
+ interrupts = <36>;
+ };
+
+ ehci@d8007100 {
+ compatible = "via,vt8500-ehci";
+ reg = <0xd8007100 0x200>;
+ interrupts = <43>;
+ };
+
+ uhci@d8007300 {
+ compatible = "platform-uhci";
+ reg = <0xd8007300 0x200>;
+ interrupts = <43>;
+ };
+
+ fb@d8050800 {
+ compatible = "wm,wm8505-fb";
+ reg = <0xd8050800 0x200>;
+ display = <&display>;
+ default-mode = <&mode0>;
+ };
+
+ ge_rops@d8050400 {
+ compatible = "wm,prizm-ge-rops";
+ reg = <0xd8050400 0x100>;
+ };
+
+ uart@d8200000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd8200000 0x1040>;
+ interrupts = <32>;
+ clocks = <&ref24>;
+ };
+
+ uart@d82b0000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd82b0000 0x1040>;
+ interrupts = <33>;
+ clocks = <&ref24>;
+ };
+
+ uart@d8210000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd8210000 0x1040>;
+ interrupts = <47>;
+ clocks = <&ref24>;
+ };
+
+ uart@d82c0000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd82c0000 0x1040>;
+ interrupts = <50>;
+ clocks = <&ref24>;
+ };
+
+ uart@d8370000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd8370000 0x1040>;
+ interrupts = <31>;
+ clocks = <&ref24>;
+ };
+
+ uart@d8380000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd8380000 0x1040>;
+ interrupts = <30>;
+ clocks = <&ref24>;
+ };
+
+ rtc@d8100000 {
+ compatible = "via,vt8500-rtc";
+ reg = <0xd8100000 0x10000>;
+ interrupts = <48>;
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/wm8650-mid.dts b/arch/arm/boot/dts/wm8650-mid.dts
new file mode 100644
index 000000000000..cefd938f842f
--- /dev/null
+++ b/arch/arm/boot/dts/wm8650-mid.dts
@@ -0,0 +1,36 @@
+/*
+ * wm8650-mid.dts - Device tree file for Wondermedia WM8650-MID Tablet
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * Licensed under GPLv2 or later
+ */
+
+/dts-v1/;
+/include/ "wm8650.dtsi"
+
+/ {
+ model = "Wondermedia WM8650-MID Tablet";
+
+ /*
+ * Display node is based on Sascha Hauer's patch on dri-devel.
+ * Added a bpp property to calculate the size of the framebuffer
+ * until the binding is formalized.
+ */
+ display: display@0 {
+ modes {
+ mode0: mode@0 {
+ hactive = <800>;
+ vactive = <480>;
+ hback-porch = <88>;
+ hfront-porch = <40>;
+ hsync-len = <0>;
+ vback-porch = <32>;
+ vfront-porch = <11>;
+ vsync-len = <1>;
+ clock = <0>; /* unused but required */
+ bpp = <16>; /* non-standard but required */
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/wm8650.dtsi b/arch/arm/boot/dts/wm8650.dtsi
new file mode 100644
index 000000000000..83b9467559bb
--- /dev/null
+++ b/arch/arm/boot/dts/wm8650.dtsi
@@ -0,0 +1,147 @@
+/*
+ * wm8650.dtsi - Device tree file for Wondermedia WM8650 SoC
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * Licensed under GPLv2 or later
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+ compatible = "wm,wm8650";
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ ranges;
+ interrupt-parent = <&intc0>;
+
+ intc0: interrupt-controller@d8140000 {
+ compatible = "via,vt8500-intc";
+ interrupt-controller;
+ reg = <0xd8140000 0x10000>;
+ #interrupt-cells = <1>;
+ };
+
+ /* Secondary IC cascaded to intc0 */
+ intc1: interrupt-controller@d8150000 {
+ compatible = "via,vt8500-intc";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0xD8150000 0x10000>;
+ interrupts = <56 57 58 59 60 61 62 63>;
+ };
+
+ gpio: gpio-controller@d8110000 {
+ compatible = "wm,wm8650-gpio";
+ gpio-controller;
+ reg = <0xd8110000 0x10000>;
+ #gpio-cells = <3>;
+ };
+
+ pmc@d8130000 {
+ compatible = "via,vt8500-pmc";
+ reg = <0xd8130000 0x1000>;
+
+ clocks {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ref25: ref25M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <25000000>;
+ };
+
+ ref24: ref24M {
+ #clock-cells = <0>;
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ };
+
+ plla: plla {
+ #clock-cells = <0>;
+ compatible = "wm,wm8650-pll-clock";
+ clocks = <&ref25>;
+ reg = <0x200>;
+ };
+
+ pllb: pllb {
+ #clock-cells = <0>;
+ compatible = "wm,wm8650-pll-clock";
+ clocks = <&ref25>;
+ reg = <0x204>;
+ };
+
+ arm: arm {
+ #clock-cells = <0>;
+ compatible = "via,vt8500-device-clock";
+ clocks = <&plla>;
+ divisor-reg = <0x300>;
+ };
+
+ sdhc: sdhc {
+ #clock-cells = <0>;
+ compatible = "via,vt8500-device-clock";
+ clocks = <&pllb>;
+ divisor-reg = <0x328>;
+ divisor-mask = <0x3f>;
+ enable-reg = <0x254>;
+ enable-bit = <18>;
+ };
+ };
+ };
+
+ timer@d8130100 {
+ compatible = "via,vt8500-timer";
+ reg = <0xd8130100 0x28>;
+ interrupts = <36>;
+ };
+
+ ehci@d8007900 {
+ compatible = "via,vt8500-ehci";
+ reg = <0xd8007900 0x200>;
+ interrupts = <43>;
+ };
+
+ uhci@d8007b00 {
+ compatible = "platform-uhci";
+ reg = <0xd8007b00 0x200>;
+ interrupts = <43>;
+ };
+
+ fb@d8050800 {
+ compatible = "wm,wm8505-fb";
+ reg = <0xd8050800 0x200>;
+ display = <&display>;
+ default-mode = <&mode0>;
+ };
+
+ ge_rops@d8050400 {
+ compatible = "wm,prizm-ge-rops";
+ reg = <0xd8050400 0x100>;
+ };
+
+ uart@d8200000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd8200000 0x1040>;
+ interrupts = <32>;
+ clocks = <&ref24>;
+ };
+
+ uart@d82b0000 {
+ compatible = "via,vt8500-uart";
+ reg = <0xd82b0000 0x1040>;
+ interrupts = <33>;
+ clocks = <&ref24>;
+ };
+
+ rtc@d8100000 {
+ compatible = "via,vt8500-rtc";
+ reg = <0xd8100000 0x10000>;
+ interrupts = <48>;
+ };
+ };
+};
diff --git a/arch/arm/configs/afeb9260_defconfig b/arch/arm/configs/afeb9260_defconfig
index 2afdf67c2127..c285a9d777d9 100644
--- a/arch/arm/configs/afeb9260_defconfig
+++ b/arch/arm/configs/afeb9260_defconfig
@@ -39,7 +39,6 @@ CONFIG_MTD_BLOCK=y
CONFIG_MTD_DATAFLASH=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ATMEL=y
-CONFIG_MTD_NAND_ATMEL_ECC_SOFT=y
CONFIG_BLK_DEV_RAM=y
CONFIG_BLK_DEV_RAM_SIZE=8192
CONFIG_ATMEL_SSC=y
diff --git a/arch/arm/configs/armadillo800eva_defconfig b/arch/arm/configs/armadillo800eva_defconfig
index 90610c7030f7..f78d259f8d23 100644
--- a/arch/arm/configs/armadillo800eva_defconfig
+++ b/arch/arm/configs/armadillo800eva_defconfig
@@ -85,6 +85,7 @@ CONFIG_SERIAL_SH_SCI_NR_UARTS=8
CONFIG_SERIAL_SH_SCI_CONSOLE=y
# CONFIG_HW_RANDOM is not set
CONFIG_I2C=y
+CONFIG_I2C_GPIO=y
CONFIG_I2C_SH_MOBILE=y
# CONFIG_HWMON is not set
CONFIG_MEDIA_SUPPORT=y
@@ -120,6 +121,8 @@ CONFIG_USB_ETH=m
CONFIG_MMC=y
CONFIG_MMC_SDHI=y
CONFIG_MMC_SH_MMCIF=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_S35390A=y
CONFIG_DMADEVICES=y
CONFIG_SH_DMAE=y
CONFIG_UIO=y
diff --git a/arch/arm/configs/at91rm9200_defconfig b/arch/arm/configs/at91rm9200_defconfig
index d54e2acd3ab1..4ae57a34a582 100644
--- a/arch/arm/configs/at91rm9200_defconfig
+++ b/arch/arm/configs/at91rm9200_defconfig
@@ -232,7 +232,7 @@ CONFIG_USB_GADGET=y
CONFIG_USB_ETH=m
CONFIG_USB_MASS_STORAGE=m
CONFIG_MMC=y
-CONFIG_MMC_AT91=y
+CONFIG_MMC_ATMELMCI=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
diff --git a/arch/arm/configs/at91sam9261_defconfig b/arch/arm/configs/at91sam9261_defconfig
index ade6b2f23116..1e8712ef062e 100644
--- a/arch/arm/configs/at91sam9261_defconfig
+++ b/arch/arm/configs/at91sam9261_defconfig
@@ -128,7 +128,7 @@ CONFIG_USB_GADGETFS=m
CONFIG_USB_FILE_STORAGE=m
CONFIG_USB_G_SERIAL=m
CONFIG_MMC=y
-CONFIG_MMC_AT91=m
+CONFIG_MMC_ATMELMCI=m
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
diff --git a/arch/arm/configs/at91sam9263_defconfig b/arch/arm/configs/at91sam9263_defconfig
index 1cf96264cba1..d2050cada82d 100644
--- a/arch/arm/configs/at91sam9263_defconfig
+++ b/arch/arm/configs/at91sam9263_defconfig
@@ -61,7 +61,6 @@ CONFIG_MTD_DATAFLASH=y
CONFIG_MTD_BLOCK2MTD=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ATMEL=y
-CONFIG_MTD_NAND_ATMEL_ECC_SOFT=y
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_GLUEBI=y
CONFIG_BLK_DEV_LOOP=y
@@ -138,7 +137,7 @@ CONFIG_USB_FILE_STORAGE=m
CONFIG_USB_G_SERIAL=m
CONFIG_MMC=y
CONFIG_SDIO_UART=m
-CONFIG_MMC_AT91=m
+CONFIG_MMC_ATMELMCI=m
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_ATMEL_PWM=y
diff --git a/arch/arm/configs/at91sam9g20_defconfig b/arch/arm/configs/at91sam9g20_defconfig
index 994d331b2319..e1b0e80b54a5 100644
--- a/arch/arm/configs/at91sam9g20_defconfig
+++ b/arch/arm/configs/at91sam9g20_defconfig
@@ -99,7 +99,7 @@ CONFIG_USB_GADGETFS=m
CONFIG_USB_FILE_STORAGE=m
CONFIG_USB_G_SERIAL=m
CONFIG_MMC=y
-CONFIG_MMC_AT91=m
+CONFIG_MMC_ATMELMCI=m
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
diff --git a/arch/arm/configs/at91sam9rl_defconfig b/arch/arm/configs/at91sam9rl_defconfig
index ad562ee64209..7cf87856d63c 100644
--- a/arch/arm/configs/at91sam9rl_defconfig
+++ b/arch/arm/configs/at91sam9rl_defconfig
@@ -60,7 +60,7 @@ CONFIG_AT91SAM9X_WATCHDOG=y
CONFIG_FB=y
CONFIG_FB_ATMEL=y
CONFIG_MMC=y
-CONFIG_MMC_AT91=m
+CONFIG_MMC_ATMELMCI=m
CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_AT91SAM9=y
CONFIG_EXT2_FS=y
diff --git a/arch/arm/configs/cpu9260_defconfig b/arch/arm/configs/cpu9260_defconfig
index bbf729e2fb6f..921480c23b98 100644
--- a/arch/arm/configs/cpu9260_defconfig
+++ b/arch/arm/configs/cpu9260_defconfig
@@ -82,7 +82,7 @@ CONFIG_USB_STORAGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_ETH=m
CONFIG_MMC=y
-CONFIG_MMC_AT91=m
+CONFIG_MMC_ATMELMCI=m
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
diff --git a/arch/arm/configs/cpu9g20_defconfig b/arch/arm/configs/cpu9g20_defconfig
index e7d7942927f3..ea116cbdffa1 100644
--- a/arch/arm/configs/cpu9g20_defconfig
+++ b/arch/arm/configs/cpu9g20_defconfig
@@ -82,7 +82,7 @@ CONFIG_USB_STORAGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_ETH=m
CONFIG_MMC=y
-CONFIG_MMC_AT91=m
+CONFIG_MMC_ATMELMCI=m
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
diff --git a/arch/arm/configs/marzen_defconfig b/arch/arm/configs/marzen_defconfig
index 864f9a5c39dd..f513acedc10a 100644
--- a/arch/arm/configs/marzen_defconfig
+++ b/arch/arm/configs/marzen_defconfig
@@ -68,6 +68,8 @@ CONFIG_SERIAL_SH_SCI_CONSOLE=y
# CONFIG_HW_RANDOM is not set
CONFIG_GPIO_SYSFS=y
# CONFIG_HWMON is not set
+CONFIG_THERMAL=y
+CONFIG_RCAR_THERMAL=y
CONFIG_SSB=y
# CONFIG_HID_SUPPORT is not set
# CONFIG_USB_SUPPORT is not set
diff --git a/arch/arm/configs/mmp2_defconfig b/arch/arm/configs/mmp2_defconfig
index 5a584520db2f..f1cb95e58af0 100644
--- a/arch/arm/configs/mmp2_defconfig
+++ b/arch/arm/configs/mmp2_defconfig
@@ -16,7 +16,7 @@ CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CMDLINE="root=/dev/nfs rootfstype=nfs nfsroot=192.168.1.100:/nfsroot/ ip=192.168.1.101:192.168.1.100::255.255.255.0::eth0:on console=ttyS2,38400 mem=128M user_debug=255"
+CONFIG_CMDLINE="root=/dev/nfs rootfstype=nfs nfsroot=192.168.1.100:/nfsroot/ ip=192.168.1.101:192.168.1.100::255.255.255.0::eth0:on console=ttyS2,38400 mem=128M user_debug=255 earlyprintk"
CONFIG_VFP=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -90,6 +90,9 @@ CONFIG_DEBUG_INFO=y
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
# CONFIG_DYNAMIC_DEBUG is not set
CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_MMP_UART3=y
+CONFIG_EARLY_PRINTK=y
CONFIG_DEBUG_ERRORS=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/pxa910_defconfig b/arch/arm/configs/pxa910_defconfig
index 1cd381e1d47d..191118caa5c0 100644
--- a/arch/arm/configs/pxa910_defconfig
+++ b/arch/arm/configs/pxa910_defconfig
@@ -17,7 +17,7 @@ CONFIG_PREEMPT=y
CONFIG_AEABI=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
-CONFIG_CMDLINE="root=/dev/nfs rootfstype=nfs nfsroot=192.168.2.100:/nfsroot/ ip=192.168.2.101:192.168.2.100::255.255.255.0::eth0:on console=ttyS0,115200 mem=128M"
+CONFIG_CMDLINE="root=/dev/nfs rootfstype=nfs nfsroot=192.168.2.100:/nfsroot/ ip=192.168.2.101:192.168.2.100::255.255.255.0::eth0:on console=ttyS0,115200 mem=128M earlyprintk"
CONFIG_FPE_NWFPE=y
CONFIG_NET=y
CONFIG_PACKET=y
@@ -66,5 +66,7 @@ CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_USER=y
CONFIG_DEBUG_ERRORS=y
CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_MMP_UART2=y
+CONFIG_EARLY_PRINTK=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/qil-a9260_defconfig b/arch/arm/configs/qil-a9260_defconfig
index 9160f3b7751f..42d5db1876ab 100644
--- a/arch/arm/configs/qil-a9260_defconfig
+++ b/arch/arm/configs/qil-a9260_defconfig
@@ -50,7 +50,6 @@ CONFIG_MTD_BLOCK=y
CONFIG_MTD_DATAFLASH=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ATMEL=y
-CONFIG_MTD_NAND_ATMEL_ECC_SOFT=y
CONFIG_BLK_DEV_LOOP=y
# CONFIG_MISC_DEVICES is not set
CONFIG_SCSI=y
@@ -87,7 +86,7 @@ CONFIG_USB_STORAGE=y
CONFIG_USB_GADGET=y
CONFIG_USB_ETH=m
CONFIG_MMC=y
-CONFIG_MMC_AT91=m
+CONFIG_MMC_ATMELMCI=m
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
CONFIG_LEDS_GPIO=y
diff --git a/arch/arm/configs/stamp9g20_defconfig b/arch/arm/configs/stamp9g20_defconfig
index d5e260b8b160..52f1488591c7 100644
--- a/arch/arm/configs/stamp9g20_defconfig
+++ b/arch/arm/configs/stamp9g20_defconfig
@@ -100,7 +100,6 @@ CONFIG_USB_ETH=m
CONFIG_USB_FILE_STORAGE=m
CONFIG_USB_G_SERIAL=m
CONFIG_MMC=y
-# CONFIG_MMC_AT91 is not set
CONFIG_MMC_ATMELMCI=y
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
diff --git a/arch/arm/configs/usb-a9260_defconfig b/arch/arm/configs/usb-a9260_defconfig
index 2e39f38b9627..a1501e1e1a90 100644
--- a/arch/arm/configs/usb-a9260_defconfig
+++ b/arch/arm/configs/usb-a9260_defconfig
@@ -49,7 +49,6 @@ CONFIG_MTD_BLOCK=y
CONFIG_MTD_DATAFLASH=y
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ATMEL=y
-CONFIG_MTD_NAND_ATMEL_ECC_SOFT=y
CONFIG_BLK_DEV_LOOP=y
# CONFIG_MISC_DEVICES is not set
CONFIG_SCSI=y
diff --git a/arch/arm/include/asm/leds.h b/arch/arm/include/asm/leds.h
deleted file mode 100644
index c545739f39b7..000000000000
--- a/arch/arm/include/asm/leds.h
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * arch/arm/include/asm/leds.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.
- *
- * Event-driven interface for LEDs on machines
- * Added led_start and led_stop- Alex Holden, 28th Dec 1998.
- */
-#ifndef ASM_ARM_LEDS_H
-#define ASM_ARM_LEDS_H
-
-
-typedef enum {
- led_idle_start,
- led_idle_end,
- led_timer,
- led_start,
- led_stop,
- led_claim, /* override idle & timer leds */
- led_release, /* restore idle & timer leds */
- led_start_timer_mode,
- led_stop_timer_mode,
- led_green_on,
- led_green_off,
- led_amber_on,
- led_amber_off,
- led_red_on,
- led_red_off,
- led_blue_on,
- led_blue_off,
- /*
- * I want this between led_timer and led_start, but
- * someone has decided to export this to user space
- */
- led_halted
-} led_event_t;
-
-/* Use this routine to handle LEDs */
-
-#ifdef CONFIG_LEDS
-extern void (*leds_event)(led_event_t);
-#else
-#define leds_event(e)
-#endif
-
-#endif
diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h
index 0cab47d4a83f..2fde5fd1acce 100644
--- a/arch/arm/include/asm/unistd.h
+++ b/arch/arm/include/asm/unistd.h
@@ -404,6 +404,7 @@
#define __NR_setns (__NR_SYSCALL_BASE+375)
#define __NR_process_vm_readv (__NR_SYSCALL_BASE+376)
#define __NR_process_vm_writev (__NR_SYSCALL_BASE+377)
+ /* 378 for kcmp */
/*
* The following SWIs are ARM private.
@@ -483,6 +484,7 @@
*/
#define __IGNORE_fadvise64_64
#define __IGNORE_migrate_pages
+#define __IGNORE_kcmp
#endif /* __KERNEL__ */
#endif /* __ASM_ARM_UNISTD_H */
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 1c4321430737..d81f3a6d9ad8 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -21,7 +21,6 @@ obj-y := elf.o entry-armv.o entry-common.o irq.o opcodes.o \
obj-$(CONFIG_DEPRECATED_PARAM_STRUCT) += compat.o
-obj-$(CONFIG_LEDS) += leds.o
obj-$(CONFIG_OC_ETM) += etm.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_ISA_DMA_API) += dma.o
diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c
index b244696de1a3..9b722612553d 100644
--- a/arch/arm/kernel/bios32.c
+++ b/arch/arm/kernel/bios32.c
@@ -271,15 +271,6 @@ static void __devinit pci_fixup_it8152(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_8152, pci_fixup_it8152);
-
-
-void __devinit pcibios_update_irq(struct pci_dev *dev, int irq)
-{
- if (debug_pci)
- printk("PCI: Assigning IRQ %02d to %s\n", irq, pci_name(dev));
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-}
-
/*
* If the bus contains any of these devices, then we must not turn on
* parity checking of any kind. Currently this is CyberPro 20x0 only.
diff --git a/arch/arm/kernel/calls.S b/arch/arm/kernel/calls.S
index 463ff4a0ec8a..e337879595e5 100644
--- a/arch/arm/kernel/calls.S
+++ b/arch/arm/kernel/calls.S
@@ -387,6 +387,7 @@
/* 375 */ CALL(sys_setns)
CALL(sys_process_vm_readv)
CALL(sys_process_vm_writev)
+ CALL(sys_ni_syscall) /* reserved for sys_kcmp */
#ifndef syscalls_counted
.equ syscalls_padding, ((NR_syscalls + 3) & ~3) - NR_syscalls
#define syscalls_counted
diff --git a/arch/arm/kernel/leds.c b/arch/arm/kernel/leds.c
deleted file mode 100644
index 1911dae19e4f..000000000000
--- a/arch/arm/kernel/leds.c
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * LED support code, ripped out of arch/arm/kernel/time.c
- *
- * Copyright (C) 1994-2001 Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#include <linux/export.h>
-#include <linux/init.h>
-#include <linux/device.h>
-#include <linux/syscore_ops.h>
-#include <linux/string.h>
-
-#include <asm/leds.h>
-
-static void dummy_leds_event(led_event_t evt)
-{
-}
-
-void (*leds_event)(led_event_t) = dummy_leds_event;
-
-struct leds_evt_name {
- const char name[8];
- int on;
- int off;
-};
-
-static const struct leds_evt_name evt_names[] = {
- { "amber", led_amber_on, led_amber_off },
- { "blue", led_blue_on, led_blue_off },
- { "green", led_green_on, led_green_off },
- { "red", led_red_on, led_red_off },
-};
-
-static ssize_t leds_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t size)
-{
- int ret = -EINVAL, len = strcspn(buf, " ");
-
- if (len > 0 && buf[len] == '\0')
- len--;
-
- if (strncmp(buf, "claim", len) == 0) {
- leds_event(led_claim);
- ret = size;
- } else if (strncmp(buf, "release", len) == 0) {
- leds_event(led_release);
- ret = size;
- } else {
- int i;
-
- for (i = 0; i < ARRAY_SIZE(evt_names); i++) {
- if (strlen(evt_names[i].name) != len ||
- strncmp(buf, evt_names[i].name, len) != 0)
- continue;
- if (strncmp(buf+len, " on", 3) == 0) {
- leds_event(evt_names[i].on);
- ret = size;
- } else if (strncmp(buf+len, " off", 4) == 0) {
- leds_event(evt_names[i].off);
- ret = size;
- }
- break;
- }
- }
- return ret;
-}
-
-static DEVICE_ATTR(event, 0200, NULL, leds_store);
-
-static struct bus_type leds_subsys = {
- .name = "leds",
- .dev_name = "leds",
-};
-
-static struct device leds_device = {
- .id = 0,
- .bus = &leds_subsys,
-};
-
-static int leds_suspend(void)
-{
- leds_event(led_stop);
- return 0;
-}
-
-static void leds_resume(void)
-{
- leds_event(led_start);
-}
-
-static void leds_shutdown(void)
-{
- leds_event(led_halted);
-}
-
-static struct syscore_ops leds_syscore_ops = {
- .shutdown = leds_shutdown,
- .suspend = leds_suspend,
- .resume = leds_resume,
-};
-
-static int __init leds_init(void)
-{
- int ret;
- ret = subsys_system_register(&leds_subsys, NULL);
- if (ret == 0)
- ret = device_register(&leds_device);
- if (ret == 0)
- ret = device_create_file(&leds_device, &dev_attr_event);
- if (ret == 0)
- register_syscore_ops(&leds_syscore_ops);
- return ret;
-}
-
-device_initcall(leds_init);
-
-EXPORT_SYMBOL(leds_event);
diff --git a/arch/arm/kernel/process.c b/arch/arm/kernel/process.c
index 693b744fd572..04eea22d7958 100644
--- a/arch/arm/kernel/process.c
+++ b/arch/arm/kernel/process.c
@@ -31,9 +31,9 @@
#include <linux/random.h>
#include <linux/hw_breakpoint.h>
#include <linux/cpuidle.h>
+#include <linux/leds.h>
#include <asm/cacheflush.h>
-#include <asm/leds.h>
#include <asm/processor.h>
#include <asm/thread_notify.h>
#include <asm/stacktrace.h>
@@ -189,7 +189,7 @@ void cpu_idle(void)
while (1) {
tick_nohz_idle_enter();
rcu_idle_enter();
- leds_event(led_idle_start);
+ ledtrig_cpu(CPU_LED_IDLE_START);
while (!need_resched()) {
#ifdef CONFIG_HOTPLUG_CPU
if (cpu_is_offline(smp_processor_id()))
@@ -220,7 +220,7 @@ void cpu_idle(void)
} else
local_irq_enable();
}
- leds_event(led_idle_end);
+ ledtrig_cpu(CPU_LED_IDLE_END);
rcu_idle_exit();
tick_nohz_idle_exit();
schedule_preempt_disabled();
diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c
index fef42b21cecb..e1f906989bb8 100644
--- a/arch/arm/kernel/smp_twd.c
+++ b/arch/arm/kernel/smp_twd.c
@@ -11,7 +11,6 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/clk.h>
-#include <linux/cpufreq.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
@@ -96,7 +95,52 @@ static void twd_timer_stop(struct clock_event_device *clk)
disable_percpu_irq(clk->irq);
}
-#ifdef CONFIG_CPU_FREQ
+#ifdef CONFIG_COMMON_CLK
+
+/*
+ * Updates clockevent frequency when the cpu frequency changes.
+ * Called on the cpu that is changing frequency with interrupts disabled.
+ */
+static void twd_update_frequency(void *new_rate)
+{
+ twd_timer_rate = *((unsigned long *) new_rate);
+
+ clockevents_update_freq(*__this_cpu_ptr(twd_evt), twd_timer_rate);
+}
+
+static int twd_rate_change(struct notifier_block *nb,
+ unsigned long flags, void *data)
+{
+ struct clk_notifier_data *cnd = data;
+
+ /*
+ * The twd clock events must be reprogrammed to account for the new
+ * frequency. The timer is local to a cpu, so cross-call to the
+ * changing cpu.
+ */
+ if (flags == POST_RATE_CHANGE)
+ smp_call_function(twd_update_frequency,
+ (void *)&cnd->new_rate, 1);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block twd_clk_nb = {
+ .notifier_call = twd_rate_change,
+};
+
+static int twd_clk_init(void)
+{
+ if (twd_evt && *__this_cpu_ptr(twd_evt) && !IS_ERR(twd_clk))
+ return clk_notifier_register(twd_clk, &twd_clk_nb);
+
+ return 0;
+}
+core_initcall(twd_clk_init);
+
+#elif defined (CONFIG_CPU_FREQ)
+
+#include <linux/cpufreq.h>
/*
* Updates clockevent frequency when the cpu frequency changes.
diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c
index af2afb019672..09be0c3c9069 100644
--- a/arch/arm/kernel/time.c
+++ b/arch/arm/kernel/time.c
@@ -25,7 +25,6 @@
#include <linux/timer.h>
#include <linux/irq.h>
-#include <asm/leds.h>
#include <asm/thread_info.h>
#include <asm/sched_clock.h>
#include <asm/stacktrace.h>
@@ -80,21 +79,6 @@ u32 arch_gettimeoffset(void)
}
#endif /* CONFIG_ARCH_USES_GETTIMEOFFSET */
-#ifdef CONFIG_LEDS_TIMER
-static inline void do_leds(void)
-{
- static unsigned int count = HZ/2;
-
- if (--count == 0) {
- count = HZ/2;
- leds_event(led_timer);
- }
-}
-#else
-#define do_leds()
-#endif
-
-
#ifndef CONFIG_GENERIC_CLOCKEVENTS
/*
* Kernel system timer support.
@@ -102,7 +86,6 @@ static inline void do_leds(void)
void timer_tick(void)
{
profile_tick(CPU_PROFILING);
- do_leds();
xtime_update(1);
#ifndef CONFIG_SMP
update_process_times(user_mode(get_irq_regs()));
diff --git a/arch/arm/mach-at91/at91rm9200_devices.c b/arch/arm/mach-at91/at91rm9200_devices.c
index 01fb7325fecc..9ac427a702da 100644
--- a/arch/arm/mach-at91/at91rm9200_devices.c
+++ b/arch/arm/mach-at91/at91rm9200_devices.c
@@ -294,9 +294,9 @@ void __init at91_add_device_cf(struct at91_cf_data *data) {}
* MMC / SD
* -------------------------------------------------------------------- */
-#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE)
+#if IS_ENABLED(CONFIG_MMC_ATMELMCI)
static u64 mmc_dmamask = DMA_BIT_MASK(32);
-static struct at91_mmc_data mmc_data;
+static struct mci_platform_data mmc_data;
static struct resource mmc_resources[] = {
[0] = {
@@ -312,7 +312,7 @@ static struct resource mmc_resources[] = {
};
static struct platform_device at91rm9200_mmc_device = {
- .name = "at91_mci",
+ .name = "atmel_mci",
.id = -1,
.dev = {
.dma_mask = &mmc_dmamask,
@@ -323,53 +323,69 @@ static struct platform_device at91rm9200_mmc_device = {
.num_resources = ARRAY_SIZE(mmc_resources),
};
-void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data)
+void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data)
{
+ unsigned int i;
+ unsigned int slot_count = 0;
+
if (!data)
return;
- /* input/irq */
- if (gpio_is_valid(data->det_pin)) {
- at91_set_gpio_input(data->det_pin, 1);
- at91_set_deglitch(data->det_pin, 1);
- }
- if (gpio_is_valid(data->wp_pin))
- at91_set_gpio_input(data->wp_pin, 1);
- if (gpio_is_valid(data->vcc_pin))
- at91_set_gpio_output(data->vcc_pin, 0);
-
- /* CLK */
- at91_set_A_periph(AT91_PIN_PA27, 0);
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
- if (data->slot_b) {
- /* CMD */
- at91_set_B_periph(AT91_PIN_PA8, 1);
+ if (!data->slot[i].bus_width)
+ continue;
- /* DAT0, maybe DAT1..DAT3 */
- at91_set_B_periph(AT91_PIN_PA9, 1);
- if (data->wire4) {
- at91_set_B_periph(AT91_PIN_PA10, 1);
- at91_set_B_periph(AT91_PIN_PA11, 1);
- at91_set_B_periph(AT91_PIN_PA12, 1);
+ /* input/irq */
+ if (gpio_is_valid(data->slot[i].detect_pin)) {
+ at91_set_gpio_input(data->slot[i].detect_pin, 1);
+ at91_set_deglitch(data->slot[i].detect_pin, 1);
}
- } else {
- /* CMD */
- at91_set_A_periph(AT91_PIN_PA28, 1);
-
- /* DAT0, maybe DAT1..DAT3 */
- at91_set_A_periph(AT91_PIN_PA29, 1);
- if (data->wire4) {
- at91_set_B_periph(AT91_PIN_PB3, 1);
- at91_set_B_periph(AT91_PIN_PB4, 1);
- at91_set_B_periph(AT91_PIN_PB5, 1);
+ if (gpio_is_valid(data->slot[i].wp_pin))
+ at91_set_gpio_input(data->slot[i].wp_pin, 1);
+
+ switch (i) {
+ case 0: /* slot A */
+ /* CMD */
+ at91_set_A_periph(AT91_PIN_PA28, 1);
+ /* DAT0, maybe DAT1..DAT3 */
+ at91_set_A_periph(AT91_PIN_PA29, 1);
+ if (data->slot[i].bus_width == 4) {
+ at91_set_B_periph(AT91_PIN_PB3, 1);
+ at91_set_B_periph(AT91_PIN_PB4, 1);
+ at91_set_B_periph(AT91_PIN_PB5, 1);
+ }
+ slot_count++;
+ break;
+ case 1: /* slot B */
+ /* CMD */
+ at91_set_B_periph(AT91_PIN_PA8, 1);
+ /* DAT0, maybe DAT1..DAT3 */
+ at91_set_B_periph(AT91_PIN_PA9, 1);
+ if (data->slot[i].bus_width == 4) {
+ at91_set_B_periph(AT91_PIN_PA10, 1);
+ at91_set_B_periph(AT91_PIN_PA11, 1);
+ at91_set_B_periph(AT91_PIN_PA12, 1);
+ }
+ slot_count++;
+ break;
+ default:
+ printk(KERN_ERR
+ "AT91: SD/MMC slot %d not available\n", i);
+ break;
+ }
+ if (slot_count) {
+ /* CLK */
+ at91_set_A_periph(AT91_PIN_PA27, 0);
+
+ mmc_data = *data;
+ platform_device_register(&at91rm9200_mmc_device);
}
}
- mmc_data = *data;
- platform_device_register(&at91rm9200_mmc_device);
}
#else
-void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
+void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data) {}
#endif
diff --git a/arch/arm/mach-at91/at91sam9260_devices.c b/arch/arm/mach-at91/at91sam9260_devices.c
index bce572a530ef..af50ff3281c7 100644
--- a/arch/arm/mach-at91/at91sam9260_devices.c
+++ b/arch/arm/mach-at91/at91sam9260_devices.c
@@ -209,92 +209,10 @@ void __init at91_add_device_eth(struct macb_platform_data *data) {}
/* --------------------------------------------------------------------
- * MMC / SD
- * -------------------------------------------------------------------- */
-
-#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE)
-static u64 mmc_dmamask = DMA_BIT_MASK(32);
-static struct at91_mmc_data mmc_data;
-
-static struct resource mmc_resources[] = {
- [0] = {
- .start = AT91SAM9260_BASE_MCI,
- .end = AT91SAM9260_BASE_MCI + SZ_16K - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = NR_IRQS_LEGACY + AT91SAM9260_ID_MCI,
- .end = NR_IRQS_LEGACY + AT91SAM9260_ID_MCI,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct platform_device at91sam9260_mmc_device = {
- .name = "at91_mci",
- .id = -1,
- .dev = {
- .dma_mask = &mmc_dmamask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- .platform_data = &mmc_data,
- },
- .resource = mmc_resources,
- .num_resources = ARRAY_SIZE(mmc_resources),
-};
-
-void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data)
-{
- if (!data)
- return;
-
- /* input/irq */
- if (gpio_is_valid(data->det_pin)) {
- at91_set_gpio_input(data->det_pin, 1);
- at91_set_deglitch(data->det_pin, 1);
- }
- if (gpio_is_valid(data->wp_pin))
- at91_set_gpio_input(data->wp_pin, 1);
- if (gpio_is_valid(data->vcc_pin))
- at91_set_gpio_output(data->vcc_pin, 0);
-
- /* CLK */
- at91_set_A_periph(AT91_PIN_PA8, 0);
-
- if (data->slot_b) {
- /* CMD */
- at91_set_B_periph(AT91_PIN_PA1, 1);
-
- /* DAT0, maybe DAT1..DAT3 */
- at91_set_B_periph(AT91_PIN_PA0, 1);
- if (data->wire4) {
- at91_set_B_periph(AT91_PIN_PA5, 1);
- at91_set_B_periph(AT91_PIN_PA4, 1);
- at91_set_B_periph(AT91_PIN_PA3, 1);
- }
- } else {
- /* CMD */
- at91_set_A_periph(AT91_PIN_PA7, 1);
-
- /* DAT0, maybe DAT1..DAT3 */
- at91_set_A_periph(AT91_PIN_PA6, 1);
- if (data->wire4) {
- at91_set_A_periph(AT91_PIN_PA9, 1);
- at91_set_A_periph(AT91_PIN_PA10, 1);
- at91_set_A_periph(AT91_PIN_PA11, 1);
- }
- }
-
- mmc_data = *data;
- platform_device_register(&at91sam9260_mmc_device);
-}
-#else
-void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
-#endif
-
-/* --------------------------------------------------------------------
* MMC / SD Slot for Atmel MCI Driver
* -------------------------------------------------------------------- */
-#if defined(CONFIG_MMC_ATMELMCI) || defined(CONFIG_MMC_ATMELMCI_MODULE)
+#if IS_ENABLED(CONFIG_MMC_ATMELMCI)
static u64 mmc_dmamask = DMA_BIT_MASK(32);
static struct mci_platform_data mmc_data;
diff --git a/arch/arm/mach-at91/at91sam9261_devices.c b/arch/arm/mach-at91/at91sam9261_devices.c
index bc2590d712d0..11e9fa835cde 100644
--- a/arch/arm/mach-at91/at91sam9261_devices.c
+++ b/arch/arm/mach-at91/at91sam9261_devices.c
@@ -137,9 +137,9 @@ void __init at91_add_device_udc(struct at91_udc_data *data) {}
* MMC / SD
* -------------------------------------------------------------------- */
-#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE)
+#if IS_ENABLED(CONFIG_MMC_ATMELMCI)
static u64 mmc_dmamask = DMA_BIT_MASK(32);
-static struct at91_mmc_data mmc_data;
+static struct mci_platform_data mmc_data;
static struct resource mmc_resources[] = {
[0] = {
@@ -155,7 +155,7 @@ static struct resource mmc_resources[] = {
};
static struct platform_device at91sam9261_mmc_device = {
- .name = "at91_mci",
+ .name = "atmel_mci",
.id = -1,
.dev = {
.dma_mask = &mmc_dmamask,
@@ -166,40 +166,40 @@ static struct platform_device at91sam9261_mmc_device = {
.num_resources = ARRAY_SIZE(mmc_resources),
};
-void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data)
+void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data)
{
if (!data)
return;
- /* input/irq */
- if (gpio_is_valid(data->det_pin)) {
- at91_set_gpio_input(data->det_pin, 1);
- at91_set_deglitch(data->det_pin, 1);
- }
- if (gpio_is_valid(data->wp_pin))
- at91_set_gpio_input(data->wp_pin, 1);
- if (gpio_is_valid(data->vcc_pin))
- at91_set_gpio_output(data->vcc_pin, 0);
-
- /* CLK */
- at91_set_B_periph(AT91_PIN_PA2, 0);
-
- /* CMD */
- at91_set_B_periph(AT91_PIN_PA1, 1);
-
- /* DAT0, maybe DAT1..DAT3 */
- at91_set_B_periph(AT91_PIN_PA0, 1);
- if (data->wire4) {
- at91_set_B_periph(AT91_PIN_PA4, 1);
- at91_set_B_periph(AT91_PIN_PA5, 1);
- at91_set_B_periph(AT91_PIN_PA6, 1);
- }
+ if (data->slot[0].bus_width) {
+ /* input/irq */
+ if (gpio_is_valid(data->slot[0].detect_pin)) {
+ at91_set_gpio_input(data->slot[0].detect_pin, 1);
+ at91_set_deglitch(data->slot[0].detect_pin, 1);
+ }
+ if (gpio_is_valid(data->slot[0].wp_pin))
+ at91_set_gpio_input(data->slot[0].wp_pin, 1);
+
+ /* CLK */
+ at91_set_B_periph(AT91_PIN_PA2, 0);
- mmc_data = *data;
- platform_device_register(&at91sam9261_mmc_device);
+ /* CMD */
+ at91_set_B_periph(AT91_PIN_PA1, 1);
+
+ /* DAT0, maybe DAT1..DAT3 */
+ at91_set_B_periph(AT91_PIN_PA0, 1);
+ if (data->slot[0].bus_width == 4) {
+ at91_set_B_periph(AT91_PIN_PA4, 1);
+ at91_set_B_periph(AT91_PIN_PA5, 1);
+ at91_set_B_periph(AT91_PIN_PA6, 1);
+ }
+
+ mmc_data = *data;
+ platform_device_register(&at91sam9261_mmc_device);
+ }
}
#else
-void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
+void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data) {}
#endif
diff --git a/arch/arm/mach-at91/at91sam9263.c b/arch/arm/mach-at91/at91sam9263.c
index 84b38105231e..144ef5de51b6 100644
--- a/arch/arm/mach-at91/at91sam9263.c
+++ b/arch/arm/mach-at91/at91sam9263.c
@@ -188,8 +188,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
CLKDEV_CON_ID("hclk", &macb_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.0", &ssc0_clk),
CLKDEV_CON_DEV_ID("pclk", "ssc.1", &ssc1_clk),
- CLKDEV_CON_DEV_ID("mci_clk", "at91_mci.0", &mmc0_clk),
- CLKDEV_CON_DEV_ID("mci_clk", "at91_mci.1", &mmc1_clk),
+ CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.0", &mmc0_clk),
+ CLKDEV_CON_DEV_ID("mci_clk", "atmel_mci.1", &mmc1_clk),
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.0", &spi0_clk),
CLKDEV_CON_DEV_ID("spi_clk", "atmel_spi.1", &spi1_clk),
CLKDEV_CON_DEV_ID("t0_clk", "atmel_tcb.0", &tcb_clk),
diff --git a/arch/arm/mach-at91/at91sam9263_devices.c b/arch/arm/mach-at91/at91sam9263_devices.c
index 9b6ca734f1a9..7c0898fe20fa 100644
--- a/arch/arm/mach-at91/at91sam9263_devices.c
+++ b/arch/arm/mach-at91/at91sam9263_devices.c
@@ -218,9 +218,9 @@ void __init at91_add_device_eth(struct macb_platform_data *data) {}
* MMC / SD
* -------------------------------------------------------------------- */
-#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE)
+#if IS_ENABLED(CONFIG_MMC_ATMELMCI)
static u64 mmc_dmamask = DMA_BIT_MASK(32);
-static struct at91_mmc_data mmc0_data, mmc1_data;
+static struct mci_platform_data mmc0_data, mmc1_data;
static struct resource mmc0_resources[] = {
[0] = {
@@ -236,7 +236,7 @@ static struct resource mmc0_resources[] = {
};
static struct platform_device at91sam9263_mmc0_device = {
- .name = "at91_mci",
+ .name = "atmel_mci",
.id = 0,
.dev = {
.dma_mask = &mmc_dmamask,
@@ -261,7 +261,7 @@ static struct resource mmc1_resources[] = {
};
static struct platform_device at91sam9263_mmc1_device = {
- .name = "at91_mci",
+ .name = "atmel_mci",
.id = 1,
.dev = {
.dma_mask = &mmc_dmamask,
@@ -272,85 +272,110 @@ static struct platform_device at91sam9263_mmc1_device = {
.num_resources = ARRAY_SIZE(mmc1_resources),
};
-void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data)
+void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data)
{
+ unsigned int i;
+ unsigned int slot_count = 0;
+
if (!data)
return;
- /* input/irq */
- if (gpio_is_valid(data->det_pin)) {
- at91_set_gpio_input(data->det_pin, 1);
- at91_set_deglitch(data->det_pin, 1);
- }
- if (gpio_is_valid(data->wp_pin))
- at91_set_gpio_input(data->wp_pin, 1);
- if (gpio_is_valid(data->vcc_pin))
- at91_set_gpio_output(data->vcc_pin, 0);
+ for (i = 0; i < ATMCI_MAX_NR_SLOTS; i++) {
- if (mmc_id == 0) { /* MCI0 */
- /* CLK */
- at91_set_A_periph(AT91_PIN_PA12, 0);
+ if (!data->slot[i].bus_width)
+ continue;
- if (data->slot_b) {
- /* CMD */
- at91_set_A_periph(AT91_PIN_PA16, 1);
+ /* input/irq */
+ if (gpio_is_valid(data->slot[i].detect_pin)) {
+ at91_set_gpio_input(data->slot[i].detect_pin,
+ 1);
+ at91_set_deglitch(data->slot[i].detect_pin,
+ 1);
+ }
+ if (gpio_is_valid(data->slot[i].wp_pin))
+ at91_set_gpio_input(data->slot[i].wp_pin, 1);
+
+ if (mmc_id == 0) { /* MCI0 */
+ switch (i) {
+ case 0: /* slot A */
+ /* CMD */
+ at91_set_A_periph(AT91_PIN_PA1, 1);
+ /* DAT0, maybe DAT1..DAT3 */
+ at91_set_A_periph(AT91_PIN_PA0, 1);
+ if (data->slot[i].bus_width == 4) {
+ at91_set_A_periph(AT91_PIN_PA3, 1);
+ at91_set_A_periph(AT91_PIN_PA4, 1);
+ at91_set_A_periph(AT91_PIN_PA5, 1);
+ }
+ slot_count++;
+ break;
+ case 1: /* slot B */
+ /* CMD */
+ at91_set_A_periph(AT91_PIN_PA16, 1);
+ /* DAT0, maybe DAT1..DAT3 */
+ at91_set_A_periph(AT91_PIN_PA17, 1);
+ if (data->slot[i].bus_width == 4) {
+ at91_set_A_periph(AT91_PIN_PA18, 1);
+ at91_set_A_periph(AT91_PIN_PA19, 1);
+ at91_set_A_periph(AT91_PIN_PA20, 1);
+ }
+ slot_count++;
+ break;
+ default:
+ printk(KERN_ERR
+ "AT91: SD/MMC slot %d not available\n", i);
+ break;
+ }
+ if (slot_count) {
+ /* CLK */
+ at91_set_A_periph(AT91_PIN_PA12, 0);
- /* DAT0, maybe DAT1..DAT3 */
- at91_set_A_periph(AT91_PIN_PA17, 1);
- if (data->wire4) {
- at91_set_A_periph(AT91_PIN_PA18, 1);
- at91_set_A_periph(AT91_PIN_PA19, 1);
- at91_set_A_periph(AT91_PIN_PA20, 1);
+ mmc0_data = *data;
+ platform_device_register(&at91sam9263_mmc0_device);
}
- } else {
- /* CMD */
- at91_set_A_periph(AT91_PIN_PA1, 1);
-
- /* DAT0, maybe DAT1..DAT3 */
- at91_set_A_periph(AT91_PIN_PA0, 1);
- if (data->wire4) {
- at91_set_A_periph(AT91_PIN_PA3, 1);
- at91_set_A_periph(AT91_PIN_PA4, 1);
- at91_set_A_periph(AT91_PIN_PA5, 1);
+ } else if (mmc_id == 1) { /* MCI1 */
+ switch (i) {
+ case 0: /* slot A */
+ /* CMD */
+ at91_set_A_periph(AT91_PIN_PA7, 1);
+ /* DAT0, maybe DAT1..DAT3 */
+ at91_set_A_periph(AT91_PIN_PA8, 1);
+ if (data->slot[i].bus_width == 4) {
+ at91_set_A_periph(AT91_PIN_PA9, 1);
+ at91_set_A_periph(AT91_PIN_PA10, 1);
+ at91_set_A_periph(AT91_PIN_PA11, 1);
+ }
+ slot_count++;
+ break;
+ case 1: /* slot B */
+ /* CMD */
+ at91_set_A_periph(AT91_PIN_PA21, 1);
+ /* DAT0, maybe DAT1..DAT3 */
+ at91_set_A_periph(AT91_PIN_PA22, 1);
+ if (data->slot[i].bus_width == 4) {
+ at91_set_A_periph(AT91_PIN_PA23, 1);
+ at91_set_A_periph(AT91_PIN_PA24, 1);
+ at91_set_A_periph(AT91_PIN_PA25, 1);
+ }
+ slot_count++;
+ break;
+ default:
+ printk(KERN_ERR
+ "AT91: SD/MMC slot %d not available\n", i);
+ break;
}
- }
+ if (slot_count) {
+ /* CLK */
+ at91_set_A_periph(AT91_PIN_PA6, 0);
- mmc0_data = *data;
- platform_device_register(&at91sam9263_mmc0_device);
- } else { /* MCI1 */
- /* CLK */
- at91_set_A_periph(AT91_PIN_PA6, 0);
-
- if (data->slot_b) {
- /* CMD */
- at91_set_A_periph(AT91_PIN_PA21, 1);
-
- /* DAT0, maybe DAT1..DAT3 */
- at91_set_A_periph(AT91_PIN_PA22, 1);
- if (data->wire4) {
- at91_set_A_periph(AT91_PIN_PA23, 1);
- at91_set_A_periph(AT91_PIN_PA24, 1);
- at91_set_A_periph(AT91_PIN_PA25, 1);
- }
- } else {
- /* CMD */
- at91_set_A_periph(AT91_PIN_PA7, 1);
-
- /* DAT0, maybe DAT1..DAT3 */
- at91_set_A_periph(AT91_PIN_PA8, 1);
- if (data->wire4) {
- at91_set_A_periph(AT91_PIN_PA9, 1);
- at91_set_A_periph(AT91_PIN_PA10, 1);
- at91_set_A_periph(AT91_PIN_PA11, 1);
+ mmc1_data = *data;
+ platform_device_register(&at91sam9263_mmc1_device);
}
}
-
- mmc1_data = *data;
- platform_device_register(&at91sam9263_mmc1_device);
}
}
#else
-void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
+void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data) {}
#endif
/* --------------------------------------------------------------------
diff --git a/arch/arm/mach-at91/at91sam9rl_devices.c b/arch/arm/mach-at91/at91sam9rl_devices.c
index dcda24838b5a..deafea0e493d 100644
--- a/arch/arm/mach-at91/at91sam9rl_devices.c
+++ b/arch/arm/mach-at91/at91sam9rl_devices.c
@@ -161,9 +161,9 @@ void __init at91_add_device_usba(struct usba_platform_data *data) {}
* MMC / SD
* -------------------------------------------------------------------- */
-#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE)
+#if IS_ENABLED(CONFIG_MMC_ATMELMCI)
static u64 mmc_dmamask = DMA_BIT_MASK(32);
-static struct at91_mmc_data mmc_data;
+static struct mci_platform_data mmc_data;
static struct resource mmc_resources[] = {
[0] = {
@@ -179,7 +179,7 @@ static struct resource mmc_resources[] = {
};
static struct platform_device at91sam9rl_mmc_device = {
- .name = "at91_mci",
+ .name = "atmel_mci",
.id = -1,
.dev = {
.dma_mask = &mmc_dmamask,
@@ -190,40 +190,40 @@ static struct platform_device at91sam9rl_mmc_device = {
.num_resources = ARRAY_SIZE(mmc_resources),
};
-void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data)
+void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data)
{
if (!data)
return;
- /* input/irq */
- if (gpio_is_valid(data->det_pin)) {
- at91_set_gpio_input(data->det_pin, 1);
- at91_set_deglitch(data->det_pin, 1);
- }
- if (gpio_is_valid(data->wp_pin))
- at91_set_gpio_input(data->wp_pin, 1);
- if (gpio_is_valid(data->vcc_pin))
- at91_set_gpio_output(data->vcc_pin, 0);
-
- /* CLK */
- at91_set_A_periph(AT91_PIN_PA2, 0);
-
- /* CMD */
- at91_set_A_periph(AT91_PIN_PA1, 1);
-
- /* DAT0, maybe DAT1..DAT3 */
- at91_set_A_periph(AT91_PIN_PA0, 1);
- if (data->wire4) {
- at91_set_A_periph(AT91_PIN_PA3, 1);
- at91_set_A_periph(AT91_PIN_PA4, 1);
- at91_set_A_periph(AT91_PIN_PA5, 1);
+ if (data->slot[0].bus_width) {
+ /* input/irq */
+ if (gpio_is_valid(data->slot[0].detect_pin)) {
+ at91_set_gpio_input(data->slot[0].detect_pin, 1);
+ at91_set_deglitch(data->slot[0].detect_pin, 1);
+ }
+ if (gpio_is_valid(data->slot[0].wp_pin))
+ at91_set_gpio_input(data->slot[0].wp_pin, 1);
+
+ /* CLK */
+ at91_set_A_periph(AT91_PIN_PA2, 0);
+
+ /* CMD */
+ at91_set_A_periph(AT91_PIN_PA1, 1);
+
+ /* DAT0, maybe DAT1..DAT3 */
+ at91_set_A_periph(AT91_PIN_PA0, 1);
+ if (data->slot[0].bus_width == 4) {
+ at91_set_A_periph(AT91_PIN_PA3, 1);
+ at91_set_A_periph(AT91_PIN_PA4, 1);
+ at91_set_A_periph(AT91_PIN_PA5, 1);
+ }
+
+ mmc_data = *data;
+ platform_device_register(&at91sam9rl_mmc_device);
}
-
- mmc_data = *data;
- platform_device_register(&at91sam9rl_mmc_device);
}
#else
-void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {}
+void __init at91_add_device_mci(short mmc_id, struct mci_platform_data *data) {}
#endif
diff --git a/arch/arm/mach-at91/at91x40.c b/arch/arm/mach-at91/at91x40.c
index 46090e642d8e..6bd7300a2bc5 100644
--- a/arch/arm/mach-at91/at91x40.c
+++ b/arch/arm/mach-at91/at91x40.c
@@ -47,7 +47,7 @@ static void at91x40_idle(void)
* Disable the processor clock. The processor will be automatically
* re-enabled by an interrupt or by a reset.
*/
- __raw_writel(AT91_PS_CR_CPU, AT91_PS_CR);
+ __raw_writel(AT91_PS_CR_CPU, AT91_IO_P2V(AT91_PS_CR));
cpu_do_idle();
}
diff --git a/arch/arm/mach-at91/at91x40_time.c b/arch/arm/mach-at91/at91x40_time.c
index 6ca680a1d5d1..ee06d7bcdf76 100644
--- a/arch/arm/mach-at91/at91x40_time.c
+++ b/arch/arm/mach-at91/at91x40_time.c
@@ -29,10 +29,10 @@
#include <mach/at91_tc.h>
#define at91_tc_read(field) \
- __raw_readl(AT91_TC + field)
+ __raw_readl(AT91_IO_P2V(AT91_TC) + field)
#define at91_tc_write(field, value) \
- __raw_writel(value, AT91_TC + field);
+ __raw_writel(value, AT91_IO_P2V(AT91_TC) + field);
/*
* 3 counter/timer units present.
diff --git a/arch/arm/mach-at91/board-afeb-9260v1.c b/arch/arm/mach-at91/board-afeb-9260v1.c
index de7be1931817..93a832f70232 100644
--- a/arch/arm/mach-at91/board-afeb-9260v1.c
+++ b/arch/arm/mach-at91/board-afeb-9260v1.c
@@ -133,12 +133,12 @@ static struct atmel_nand_data __initdata afeb9260_nand_data = {
/*
* MCI (SD/MMC)
*/
-static struct at91_mmc_data __initdata afeb9260_mmc_data = {
- .det_pin = AT91_PIN_PC9,
- .wp_pin = AT91_PIN_PC4,
- .slot_b = 1,
- .wire4 = 1,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata afeb9260_mci0_data = {
+ .slot[1] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PC9,
+ .wp_pin = AT91_PIN_PC4,
+ },
};
@@ -199,7 +199,7 @@ static void __init afeb9260_board_init(void)
at91_set_B_periph(AT91_PIN_PA10, 0); /* ETX2 */
at91_set_B_periph(AT91_PIN_PA11, 0); /* ETX3 */
/* MMC */
- at91_add_device_mmc(0, &afeb9260_mmc_data);
+ at91_add_device_mci(0, &afeb9260_mci0_data);
/* I2C */
at91_add_device_i2c(afeb9260_i2c_devices,
ARRAY_SIZE(afeb9260_i2c_devices));
diff --git a/arch/arm/mach-at91/board-carmeva.c b/arch/arm/mach-at91/board-carmeva.c
index a5b002f32a61..71d8f362a1d5 100644
--- a/arch/arm/mach-at91/board-carmeva.c
+++ b/arch/arm/mach-at91/board-carmeva.c
@@ -71,12 +71,12 @@ static struct at91_udc_data __initdata carmeva_udc_data = {
// .vcc_pin = -EINVAL,
// };
-static struct at91_mmc_data __initdata carmeva_mmc_data = {
- .slot_b = 0,
- .wire4 = 1,
- .det_pin = AT91_PIN_PB10,
- .wp_pin = AT91_PIN_PC14,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata carmeva_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PB10,
+ .wp_pin = AT91_PIN_PC14,
+ },
};
static struct spi_board_info carmeva_spi_devices[] = {
@@ -150,7 +150,7 @@ static void __init carmeva_board_init(void)
/* Compact Flash */
// at91_add_device_cf(&carmeva_cf_data);
/* MMC */
- at91_add_device_mmc(0, &carmeva_mmc_data);
+ at91_add_device_mci(0, &carmeva_mci0_data);
/* LEDs */
at91_gpio_leds(carmeva_leds, ARRAY_SIZE(carmeva_leds));
}
diff --git a/arch/arm/mach-at91/board-cpu9krea.c b/arch/arm/mach-at91/board-cpu9krea.c
index ecbc13b594de..e71c473316e3 100644
--- a/arch/arm/mach-at91/board-cpu9krea.c
+++ b/arch/arm/mach-at91/board-cpu9krea.c
@@ -254,8 +254,7 @@ static struct gpio_led cpu9krea_leds[] = {
static struct i2c_board_info __initdata cpu9krea_i2c_devices[] = {
{
- I2C_BOARD_INFO("rtc-ds1307", 0x68),
- .type = "ds1339",
+ I2C_BOARD_INFO("ds1339", 0x68),
},
};
@@ -312,12 +311,12 @@ static void __init cpu9krea_add_device_buttons(void)
/*
* MCI (SD/MMC)
*/
-static struct at91_mmc_data __initdata cpu9krea_mmc_data = {
- .slot_b = 0,
- .wire4 = 1,
- .det_pin = AT91_PIN_PA29,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata cpu9krea_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PA29,
+ .wp_pin = -EINVAL,
+ },
};
static void __init cpu9krea_board_init(void)
@@ -359,7 +358,7 @@ static void __init cpu9krea_board_init(void)
/* Ethernet */
at91_add_device_eth(&cpu9krea_macb_data);
/* MMC */
- at91_add_device_mmc(0, &cpu9krea_mmc_data);
+ at91_add_device_mci(0, &cpu9krea_mci0_data);
/* I2C */
at91_add_device_i2c(cpu9krea_i2c_devices,
ARRAY_SIZE(cpu9krea_i2c_devices));
diff --git a/arch/arm/mach-at91/board-cpuat91.c b/arch/arm/mach-at91/board-cpuat91.c
index 2e6d043c82f2..2cbd1a2b6c35 100644
--- a/arch/arm/mach-at91/board-cpuat91.c
+++ b/arch/arm/mach-at91/board-cpuat91.c
@@ -78,11 +78,12 @@ static struct at91_udc_data __initdata cpuat91_udc_data = {
.pullup_pin = AT91_PIN_PC14,
};
-static struct at91_mmc_data __initdata cpuat91_mmc_data = {
- .det_pin = AT91_PIN_PC2,
- .wire4 = 1,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata cpuat91_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PC2,
+ .wp_pin = -EINVAL,
+ },
};
static struct physmap_flash_data cpuat91_flash_data = {
@@ -168,7 +169,7 @@ static void __init cpuat91_board_init(void)
/* USB Device */
at91_add_device_udc(&cpuat91_udc_data);
/* MMC */
- at91_add_device_mmc(0, &cpuat91_mmc_data);
+ at91_add_device_mci(0, &cpuat91_mci0_data);
/* I2C */
at91_add_device_i2c(NULL, 0);
/* Platform devices */
diff --git a/arch/arm/mach-at91/board-csb337.c b/arch/arm/mach-at91/board-csb337.c
index 462bc319cbc5..3e37437a7a61 100644
--- a/arch/arm/mach-at91/board-csb337.c
+++ b/arch/arm/mach-at91/board-csb337.c
@@ -87,12 +87,12 @@ static struct at91_cf_data __initdata csb337_cf_data = {
.rst_pin = AT91_PIN_PD2,
};
-static struct at91_mmc_data __initdata csb337_mmc_data = {
- .det_pin = AT91_PIN_PD5,
- .slot_b = 0,
- .wire4 = 1,
- .wp_pin = AT91_PIN_PD6,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata csb337_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PD5,
+ .wp_pin = AT91_PIN_PD6,
+ },
};
static struct spi_board_info csb337_spi_devices[] = {
@@ -220,8 +220,6 @@ static struct gpio_led csb_leds[] = {
static void __init csb337_board_init(void)
{
- /* Setup the LEDs */
- at91_init_leds(AT91_PIN_PB0, AT91_PIN_PB1);
/* Serial */
/* DBGU on ttyS0 */
at91_register_uart(0, 0, 0);
@@ -240,7 +238,7 @@ static void __init csb337_board_init(void)
/* SPI */
at91_add_device_spi(csb337_spi_devices, ARRAY_SIZE(csb337_spi_devices));
/* MMC */
- at91_add_device_mmc(0, &csb337_mmc_data);
+ at91_add_device_mci(0, &csb337_mci0_data);
/* NOR flash */
platform_device_register(&csb_flash);
/* LEDs */
diff --git a/arch/arm/mach-at91/board-eb9200.c b/arch/arm/mach-at91/board-eb9200.c
index d1e1f3fc0a47..0cfac16ee9d5 100644
--- a/arch/arm/mach-at91/board-eb9200.c
+++ b/arch/arm/mach-at91/board-eb9200.c
@@ -70,12 +70,12 @@ static struct at91_cf_data __initdata eb9200_cf_data = {
.rst_pin = AT91_PIN_PC5,
};
-static struct at91_mmc_data __initdata eb9200_mmc_data = {
- .slot_b = 0,
- .wire4 = 1,
- .det_pin = -EINVAL,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata eb9200_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = -EINVAL,
+ .wp_pin = -EINVAL,
+ },
};
static struct i2c_board_info __initdata eb9200_i2c_devices[] = {
@@ -113,7 +113,7 @@ static void __init eb9200_board_init(void)
at91_add_device_spi(NULL, 0);
/* MMC */
/* only supports 1 or 4 bit interface, not wired through to SPI */
- at91_add_device_mmc(0, &eb9200_mmc_data);
+ at91_add_device_mci(0, &eb9200_mci0_data);
}
MACHINE_START(ATEB9200, "Embest ATEB9200")
diff --git a/arch/arm/mach-at91/board-ecbat91.c b/arch/arm/mach-at91/board-ecbat91.c
index 9c24cb25707c..3d931ffac4bf 100644
--- a/arch/arm/mach-at91/board-ecbat91.c
+++ b/arch/arm/mach-at91/board-ecbat91.c
@@ -64,12 +64,12 @@ static struct at91_usbh_data __initdata ecb_at91usbh_data = {
.overcurrent_pin= {-EINVAL, -EINVAL},
};
-static struct at91_mmc_data __initdata ecb_at91mmc_data = {
- .slot_b = 0,
- .wire4 = 1,
- .det_pin = -EINVAL,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata ecbat91_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = -EINVAL,
+ .wp_pin = -EINVAL,
+ },
};
@@ -138,11 +138,20 @@ static struct spi_board_info __initdata ecb_at91spi_devices[] = {
},
};
+/*
+ * LEDs
+ */
+static struct gpio_led ecb_leds[] = {
+ { /* D1 */
+ .name = "led1",
+ .gpio = AT91_PIN_PC7,
+ .active_low = 1,
+ .default_trigger = "heartbeat",
+ }
+};
+
static void __init ecb_at91board_init(void)
{
- /* Setup the LEDs */
- at91_init_leds(AT91_PIN_PC7, AT91_PIN_PC7);
-
/* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0);
@@ -161,10 +170,13 @@ static void __init ecb_at91board_init(void)
at91_add_device_i2c(NULL, 0);
/* MMC */
- at91_add_device_mmc(0, &ecb_at91mmc_data);
+ at91_add_device_mci(0, &ecbat91_mci0_data);
/* SPI */
at91_add_device_spi(ecb_at91spi_devices, ARRAY_SIZE(ecb_at91spi_devices));
+
+ /* LEDs */
+ at91_gpio_leds(ecb_leds, ARRAY_SIZE(ecb_leds));
}
MACHINE_START(ECBAT91, "emQbit's ECB_AT91")
diff --git a/arch/arm/mach-at91/board-eco920.c b/arch/arm/mach-at91/board-eco920.c
index 82bdfde3405f..d93658a2b128 100644
--- a/arch/arm/mach-at91/board-eco920.c
+++ b/arch/arm/mach-at91/board-eco920.c
@@ -56,12 +56,12 @@ static struct at91_udc_data __initdata eco920_udc_data = {
.pullup_pin = AT91_PIN_PB13,
};
-static struct at91_mmc_data __initdata eco920_mmc_data = {
- .slot_b = 0,
- .wire4 = 0,
- .det_pin = -EINVAL,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata eco920_mci0_data = {
+ .slot[0] = {
+ .bus_width = 1,
+ .detect_pin = -EINVAL,
+ .wp_pin = -EINVAL,
+ },
};
static struct physmap_flash_data eco920_flash_data = {
@@ -93,10 +93,26 @@ static struct spi_board_info eco920_spi_devices[] = {
},
};
+/*
+ * LEDs
+ */
+static struct gpio_led eco920_leds[] = {
+ { /* D1 */
+ .name = "led1",
+ .gpio = AT91_PIN_PB0,
+ .active_low = 1,
+ .default_trigger = "heartbeat",
+ },
+ { /* D2 */
+ .name = "led2",
+ .gpio = AT91_PIN_PB1,
+ .active_low = 1,
+ .default_trigger = "timer",
+ }
+};
+
static void __init eco920_board_init(void)
{
- /* Setup the LEDs */
- at91_init_leds(AT91_PIN_PB0, AT91_PIN_PB1);
/* DBGU on ttyS0. (Rx & Tx only */
at91_register_uart(0, 0, 0);
at91_add_device_serial();
@@ -104,7 +120,7 @@ static void __init eco920_board_init(void)
at91_add_device_usbh(&eco920_usbh_data);
at91_add_device_udc(&eco920_udc_data);
- at91_add_device_mmc(0, &eco920_mmc_data);
+ at91_add_device_mci(0, &eco920_mci0_data);
platform_device_register(&eco920_flash);
at91_ramc_write(0, AT91_SMC_CSR(7), AT91_SMC_RWHOLD_(1)
@@ -127,6 +143,8 @@ static void __init eco920_board_init(void)
);
at91_add_device_spi(eco920_spi_devices, ARRAY_SIZE(eco920_spi_devices));
+ /* LEDs */
+ at91_gpio_leds(eco920_leds, ARRAY_SIZE(eco920_leds));
}
MACHINE_START(ECO920, "eco920")
diff --git a/arch/arm/mach-at91/board-flexibity.c b/arch/arm/mach-at91/board-flexibity.c
index 6cc83a87d77c..fa98abacb1ba 100644
--- a/arch/arm/mach-at91/board-flexibity.c
+++ b/arch/arm/mach-at91/board-flexibity.c
@@ -75,12 +75,12 @@ static struct spi_board_info flexibity_spi_devices[] = {
};
/* MCI (SD/MMC) */
-static struct at91_mmc_data __initdata flexibity_mmc_data = {
- .slot_b = 0,
- .wire4 = 1,
- .det_pin = AT91_PIN_PC9,
- .wp_pin = AT91_PIN_PC4,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata flexibity_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PC9,
+ .wp_pin = AT91_PIN_PC4,
+ },
};
/* LEDs */
@@ -152,7 +152,7 @@ static void __init flexibity_board_init(void)
at91_add_device_spi(flexibity_spi_devices,
ARRAY_SIZE(flexibity_spi_devices));
/* MMC */
- at91_add_device_mmc(0, &flexibity_mmc_data);
+ at91_add_device_mci(0, &flexibity_mci0_data);
/* LEDs */
at91_gpio_leds(flexibity_leds, ARRAY_SIZE(flexibity_leds));
}
diff --git a/arch/arm/mach-at91/board-foxg20.c b/arch/arm/mach-at91/board-foxg20.c
index 69ab1247ef81..6e47071d8206 100644
--- a/arch/arm/mach-at91/board-foxg20.c
+++ b/arch/arm/mach-at91/board-foxg20.c
@@ -86,7 +86,7 @@ static struct at91_udc_data __initdata foxg20_udc_data = {
* SPI devices.
*/
static struct spi_board_info foxg20_spi_devices[] = {
-#if !defined(CONFIG_MMC_AT91)
+#if !IS_ENABLED(CONFIG_MMC_ATMELMCI)
{
.modalias = "mtd_dataflash",
.chip_select = 1,
@@ -109,12 +109,12 @@ static struct macb_platform_data __initdata foxg20_macb_data = {
* MCI (SD/MMC)
* det_pin, wp_pin and vcc_pin are not connected
*/
-static struct at91_mmc_data __initdata foxg20_mmc_data = {
- .slot_b = 1,
- .wire4 = 1,
- .det_pin = -EINVAL,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata foxg20_mci0_data = {
+ .slot[1] = {
+ .bus_width = 4,
+ .detect_pin = -EINVAL,
+ .wp_pin = -EINVAL,
+ },
};
@@ -247,7 +247,7 @@ static void __init foxg20_board_init(void)
/* Ethernet */
at91_add_device_eth(&foxg20_macb_data);
/* MMC */
- at91_add_device_mmc(0, &foxg20_mmc_data);
+ at91_add_device_mci(0, &foxg20_mci0_data);
/* I2C */
at91_add_device_i2c(foxg20_i2c_devices, ARRAY_SIZE(foxg20_i2c_devices));
/* LEDs */
diff --git a/arch/arm/mach-at91/board-kafa.c b/arch/arm/mach-at91/board-kafa.c
index 64c1dbf88a07..86050da3ba53 100644
--- a/arch/arm/mach-at91/board-kafa.c
+++ b/arch/arm/mach-at91/board-kafa.c
@@ -66,11 +66,20 @@ static struct at91_udc_data __initdata kafa_udc_data = {
.pullup_pin = AT91_PIN_PB7,
};
+/*
+ * LEDs
+ */
+static struct gpio_led kafa_leds[] = {
+ { /* D1 */
+ .name = "led1",
+ .gpio = AT91_PIN_PB4,
+ .active_low = 1,
+ .default_trigger = "heartbeat",
+ },
+};
+
static void __init kafa_board_init(void)
{
- /* Set up the LEDs */
- at91_init_leds(AT91_PIN_PB4, AT91_PIN_PB4);
-
/* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0);
@@ -88,6 +97,8 @@ static void __init kafa_board_init(void)
at91_add_device_i2c(NULL, 0);
/* SPI */
at91_add_device_spi(NULL, 0);
+ /* LEDs */
+ at91_gpio_leds(kafa_leds, ARRAY_SIZE(kafa_leds));
}
MACHINE_START(KAFA, "Sperry-Sun KAFA")
diff --git a/arch/arm/mach-at91/board-kb9202.c b/arch/arm/mach-at91/board-kb9202.c
index 5d96cb85175f..abe9fed7a3e0 100644
--- a/arch/arm/mach-at91/board-kb9202.c
+++ b/arch/arm/mach-at91/board-kb9202.c
@@ -69,12 +69,12 @@ static struct at91_udc_data __initdata kb9202_udc_data = {
.pullup_pin = AT91_PIN_PB22,
};
-static struct at91_mmc_data __initdata kb9202_mmc_data = {
- .det_pin = AT91_PIN_PB2,
- .slot_b = 0,
- .wire4 = 1,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata kb9202_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PB2,
+ .wp_pin = -EINVAL,
+ },
};
static struct mtd_partition __initdata kb9202_nand_partition[] = {
@@ -96,11 +96,26 @@ static struct atmel_nand_data __initdata kb9202_nand_data = {
.num_parts = ARRAY_SIZE(kb9202_nand_partition),
};
+/*
+ * LEDs
+ */
+static struct gpio_led kb9202_leds[] = {
+ { /* D1 */
+ .name = "led1",
+ .gpio = AT91_PIN_PC19,
+ .active_low = 1,
+ .default_trigger = "heartbeat",
+ },
+ { /* D2 */
+ .name = "led2",
+ .gpio = AT91_PIN_PC18,
+ .active_low = 1,
+ .default_trigger = "timer",
+ }
+};
+
static void __init kb9202_board_init(void)
{
- /* Set up the LEDs */
- at91_init_leds(AT91_PIN_PC19, AT91_PIN_PC18);
-
/* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0);
@@ -121,13 +136,15 @@ static void __init kb9202_board_init(void)
/* USB Device */
at91_add_device_udc(&kb9202_udc_data);
/* MMC */
- at91_add_device_mmc(0, &kb9202_mmc_data);
+ at91_add_device_mci(0, &kb9202_mci0_data);
/* I2C */
at91_add_device_i2c(NULL, 0);
/* SPI */
at91_add_device_spi(NULL, 0);
/* NAND */
at91_add_device_nand(&kb9202_nand_data);
+ /* LEDs */
+ at91_gpio_leds(kb9202_leds, ARRAY_SIZE(kb9202_leds));
}
MACHINE_START(KB9200, "KB920x")
diff --git a/arch/arm/mach-at91/board-neocore926.c b/arch/arm/mach-at91/board-neocore926.c
index 18103c5d993c..9cda3fd346ae 100644
--- a/arch/arm/mach-at91/board-neocore926.c
+++ b/arch/arm/mach-at91/board-neocore926.c
@@ -138,11 +138,12 @@ static struct spi_board_info neocore926_spi_devices[] = {
/*
* MCI (SD/MMC)
*/
-static struct at91_mmc_data __initdata neocore926_mmc_data = {
- .wire4 = 1,
- .det_pin = AT91_PIN_PE18,
- .wp_pin = AT91_PIN_PE19,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata neocore926_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PE18,
+ .wp_pin = AT91_PIN_PE19,
+ },
};
@@ -354,7 +355,7 @@ static void __init neocore926_board_init(void)
neocore926_add_device_ts();
/* MMC */
- at91_add_device_mmc(1, &neocore926_mmc_data);
+ at91_add_device_mci(0, &neocore926_mci0_data);
/* Ethernet */
at91_add_device_eth(&neocore926_macb_data);
diff --git a/arch/arm/mach-at91/board-picotux200.c b/arch/arm/mach-at91/board-picotux200.c
index 127065504508..f83e1de699e6 100644
--- a/arch/arm/mach-at91/board-picotux200.c
+++ b/arch/arm/mach-at91/board-picotux200.c
@@ -62,12 +62,12 @@ static struct at91_usbh_data __initdata picotux200_usbh_data = {
.overcurrent_pin= {-EINVAL, -EINVAL},
};
-static struct at91_mmc_data __initdata picotux200_mmc_data = {
- .det_pin = AT91_PIN_PB27,
- .slot_b = 0,
- .wire4 = 1,
- .wp_pin = AT91_PIN_PA17,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata picotux200_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PB27,
+ .wp_pin = AT91_PIN_PA17,
+ },
};
#define PICOTUX200_FLASH_BASE AT91_CHIPSELECT_0
@@ -112,7 +112,7 @@ static void __init picotux200_board_init(void)
at91_add_device_i2c(NULL, 0);
/* MMC */
at91_set_gpio_output(AT91_PIN_PB22, 1); /* this MMC card slot can optionally use SPI signaling (CS3). */
- at91_add_device_mmc(0, &picotux200_mmc_data);
+ at91_add_device_mci(0, &picotux200_mci0_data);
/* NOR Flash */
platform_device_register(&picotux200_flash);
}
diff --git a/arch/arm/mach-at91/board-qil-a9260.c b/arch/arm/mach-at91/board-qil-a9260.c
index bf351e285422..799f214edebe 100644
--- a/arch/arm/mach-at91/board-qil-a9260.c
+++ b/arch/arm/mach-at91/board-qil-a9260.c
@@ -156,12 +156,12 @@ static void __init ek_add_device_nand(void)
/*
* MCI (SD/MMC)
*/
-static struct at91_mmc_data __initdata ek_mmc_data = {
- .slot_b = 0,
- .wire4 = 1,
- .det_pin = -EINVAL,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata ek_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = -EINVAL,
+ .wp_pin = -EINVAL,
+ },
};
/*
@@ -245,7 +245,7 @@ static void __init ek_board_init(void)
/* Ethernet */
at91_add_device_eth(&ek_macb_data);
/* MMC */
- at91_add_device_mmc(0, &ek_mmc_data);
+ at91_add_device_mci(0, &ek_mci0_data);
/* Push Buttons */
ek_add_device_buttons();
/* LEDs */
diff --git a/arch/arm/mach-at91/board-rm9200dk.c b/arch/arm/mach-at91/board-rm9200dk.c
index cc2bf9796073..66338e7ebfba 100644
--- a/arch/arm/mach-at91/board-rm9200dk.c
+++ b/arch/arm/mach-at91/board-rm9200dk.c
@@ -77,12 +77,12 @@ static struct at91_cf_data __initdata dk_cf_data = {
};
#ifndef CONFIG_MTD_AT91_DATAFLASH_CARD
-static struct at91_mmc_data __initdata dk_mmc_data = {
- .slot_b = 0,
- .wire4 = 1,
- .det_pin = -EINVAL,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata dk_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = -EINVAL,
+ .wp_pin = -EINVAL,
+ },
};
#endif
@@ -177,9 +177,6 @@ static struct gpio_led dk_leds[] = {
static void __init dk_board_init(void)
{
- /* Setup the LEDs */
- at91_init_leds(AT91_PIN_PB2, AT91_PIN_PB2);
-
/* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0);
@@ -208,7 +205,7 @@ static void __init dk_board_init(void)
#else
/* MMC */
at91_set_gpio_output(AT91_PIN_PB7, 1); /* this MMC card slot can optionally use SPI signaling (CS3). */
- at91_add_device_mmc(0, &dk_mmc_data);
+ at91_add_device_mci(0, &dk_mci0_data);
#endif
/* NAND */
at91_add_device_nand(&dk_nand_data);
diff --git a/arch/arm/mach-at91/board-rm9200ek.c b/arch/arm/mach-at91/board-rm9200ek.c
index 62e19e64c9d3..5d1b5729dc69 100644
--- a/arch/arm/mach-at91/board-rm9200ek.c
+++ b/arch/arm/mach-at91/board-rm9200ek.c
@@ -70,12 +70,12 @@ static struct at91_udc_data __initdata ek_udc_data = {
};
#ifndef CONFIG_MTD_AT91_DATAFLASH_CARD
-static struct at91_mmc_data __initdata ek_mmc_data = {
- .det_pin = AT91_PIN_PB27,
- .slot_b = 0,
- .wire4 = 1,
- .wp_pin = AT91_PIN_PA17,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata ek_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PB27,
+ .wp_pin = AT91_PIN_PA17,
+ }
};
#endif
@@ -148,9 +148,6 @@ static struct gpio_led ek_leds[] = {
static void __init ek_board_init(void)
{
- /* Setup the LEDs */
- at91_init_leds(AT91_PIN_PB1, AT91_PIN_PB2);
-
/* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0);
@@ -177,7 +174,7 @@ static void __init ek_board_init(void)
#else
/* MMC */
at91_set_gpio_output(AT91_PIN_PB22, 1); /* this MMC card slot can optionally use SPI signaling (CS3). */
- at91_add_device_mmc(0, &ek_mmc_data);
+ at91_add_device_mci(0, &ek_mci0_data);
#endif
/* NOR Flash */
platform_device_register(&ek_flash);
diff --git a/arch/arm/mach-at91/board-rsi-ews.c b/arch/arm/mach-at91/board-rsi-ews.c
index c3b43aefdb75..a0ecf04e9ae3 100644
--- a/arch/arm/mach-at91/board-rsi-ews.c
+++ b/arch/arm/mach-at91/board-rsi-ews.c
@@ -58,11 +58,12 @@ static struct at91_usbh_data rsi_ews_usbh_data __initdata = {
/*
* SD/MC
*/
-static struct at91_mmc_data rsi_ews_mmc_data __initdata = {
- .slot_b = 0,
- .wire4 = 1,
- .det_pin = AT91_PIN_PB27,
- .wp_pin = AT91_PIN_PB29,
+static struct mci_platform_data __initdata rsi_ews_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PB27,
+ .wp_pin = AT91_PIN_PB29,
+ },
};
/*
@@ -185,9 +186,6 @@ static struct platform_device rsiews_nor_flash = {
*/
static void __init rsi_ews_board_init(void)
{
- /* Setup the LEDs */
- at91_init_leds(AT91_PIN_PB6, AT91_PIN_PB9);
-
/* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */
/* This one is for debugging */
@@ -215,7 +213,7 @@ static void __init rsi_ews_board_init(void)
at91_add_device_spi(rsi_ews_spi_devices,
ARRAY_SIZE(rsi_ews_spi_devices));
/* MMC */
- at91_add_device_mmc(0, &rsi_ews_mmc_data);
+ at91_add_device_mci(0, &rsi_ews_mci0_data);
/* NOR Flash */
platform_device_register(&rsiews_nor_flash);
/* LEDs */
diff --git a/arch/arm/mach-at91/board-sam9-l9260.c b/arch/arm/mach-at91/board-sam9-l9260.c
index 7bf6da70d7d5..c5f01acce3c0 100644
--- a/arch/arm/mach-at91/board-sam9-l9260.c
+++ b/arch/arm/mach-at91/board-sam9-l9260.c
@@ -73,7 +73,7 @@ static struct at91_udc_data __initdata ek_udc_data = {
* SPI devices.
*/
static struct spi_board_info ek_spi_devices[] = {
-#if !defined(CONFIG_MMC_AT91)
+#if !IS_ENABLED(CONFIG_MMC_ATMELMCI)
{ /* DataFlash chip */
.modalias = "mtd_dataflash",
.chip_select = 1,
@@ -158,19 +158,34 @@ static void __init ek_add_device_nand(void)
/*
* MCI (SD/MMC)
*/
-static struct at91_mmc_data __initdata ek_mmc_data = {
- .slot_b = 1,
- .wire4 = 1,
- .det_pin = AT91_PIN_PC8,
- .wp_pin = AT91_PIN_PC4,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata ek_mci0_data = {
+ .slot[1] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PC8,
+ .wp_pin = AT91_PIN_PC4,
+ },
+};
+
+/*
+ * LEDs
+ */
+static struct gpio_led ek_leds[] = {
+ { /* D1 */
+ .name = "led1",
+ .gpio = AT91_PIN_PA9,
+ .active_low = 1,
+ .default_trigger = "heartbeat",
+ },
+ { /* D2 */
+ .name = "led2",
+ .gpio = AT91_PIN_PA6,
+ .active_low = 1,
+ .default_trigger = "timer",
+ }
};
static void __init ek_board_init(void)
{
- /* Setup the LEDs */
- at91_init_leds(AT91_PIN_PA9, AT91_PIN_PA6);
-
/* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0);
@@ -194,9 +209,11 @@ static void __init ek_board_init(void)
/* Ethernet */
at91_add_device_eth(&ek_macb_data);
/* MMC */
- at91_add_device_mmc(0, &ek_mmc_data);
+ at91_add_device_mci(0, &ek_mci0_data);
/* I2C */
at91_add_device_i2c(NULL, 0);
+ /* LEDs */
+ at91_gpio_leds(ek_leds, ARRAY_SIZE(ek_leds));
}
MACHINE_START(SAM9_L9260, "Olimex SAM9-L9260")
diff --git a/arch/arm/mach-at91/board-sam9260ek.c b/arch/arm/mach-at91/board-sam9260ek.c
index 889c1bf71eb5..8cd6e679fbe0 100644
--- a/arch/arm/mach-at91/board-sam9260ek.c
+++ b/arch/arm/mach-at91/board-sam9260ek.c
@@ -108,7 +108,7 @@ static void __init at73c213_set_clk(struct at73c213_board_info *info) {}
* SPI devices.
*/
static struct spi_board_info ek_spi_devices[] = {
-#if !defined(CONFIG_MMC_AT91)
+#if !IS_ENABLED(CONFIG_MMC_ATMELMCI)
{ /* DataFlash chip */
.modalias = "mtd_dataflash",
.chip_select = 1,
@@ -211,12 +211,12 @@ static void __init ek_add_device_nand(void)
/*
* MCI (SD/MMC)
*/
-static struct at91_mmc_data __initdata ek_mmc_data = {
- .slot_b = 1,
- .wire4 = 1,
- .det_pin = -EINVAL,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata ek_mci0_data = {
+ .slot[1] = {
+ .bus_width = 4,
+ .detect_pin = -EINVAL,
+ .wp_pin = -EINVAL,
+ },
};
@@ -329,7 +329,7 @@ static void __init ek_board_init(void)
/* Ethernet */
at91_add_device_eth(&ek_macb_data);
/* MMC */
- at91_add_device_mmc(0, &ek_mmc_data);
+ at91_add_device_mci(0, &ek_mci0_data);
/* I2C */
at91_add_device_i2c(ek_i2c_devices, ARRAY_SIZE(ek_i2c_devices));
/* SSC (to AT73C213) */
diff --git a/arch/arm/mach-at91/board-sam9261ek.c b/arch/arm/mach-at91/board-sam9261ek.c
index 2269be5fa384..27b3af1a3047 100644
--- a/arch/arm/mach-at91/board-sam9261ek.c
+++ b/arch/arm/mach-at91/board-sam9261ek.c
@@ -340,11 +340,12 @@ static struct spi_board_info ek_spi_devices[] = {
* MCI (SD/MMC)
* det_pin, wp_pin and vcc_pin are not connected
*/
-static struct at91_mmc_data __initdata ek_mmc_data = {
- .wire4 = 1,
- .det_pin = -EINVAL,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = -EINVAL,
+ .wp_pin = -EINVAL,
+ },
};
#endif /* CONFIG_SPI_ATMEL_* */
@@ -569,9 +570,6 @@ static struct gpio_led ek_leds[] = {
static void __init ek_board_init(void)
{
- /* Setup the LEDs */
- at91_init_leds(AT91_PIN_PA13, AT91_PIN_PA14);
-
/* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0);
@@ -598,7 +596,7 @@ static void __init ek_board_init(void)
at91_add_device_ssc(AT91SAM9261_ID_SSC1, ATMEL_SSC_TX);
#else
/* MMC */
- at91_add_device_mmc(0, &ek_mmc_data);
+ at91_add_device_mci(0, &mci0_data);
#endif
/* LCD Controller */
at91_add_device_lcdc(&ek_lcdc_data);
diff --git a/arch/arm/mach-at91/board-sam9263ek.c b/arch/arm/mach-at91/board-sam9263ek.c
index 82adf581afc2..073e17403d98 100644
--- a/arch/arm/mach-at91/board-sam9263ek.c
+++ b/arch/arm/mach-at91/board-sam9263ek.c
@@ -141,11 +141,12 @@ static struct spi_board_info ek_spi_devices[] = {
/*
* MCI (SD/MMC)
*/
-static struct at91_mmc_data __initdata ek_mmc_data = {
- .wire4 = 1,
- .det_pin = AT91_PIN_PE18,
- .wp_pin = AT91_PIN_PE19,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata mci1_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PE18,
+ .wp_pin = AT91_PIN_PE19,
+ },
};
@@ -420,7 +421,7 @@ static void __init ek_board_init(void)
/* Touchscreen */
ek_add_device_ts();
/* MMC */
- at91_add_device_mmc(1, &ek_mmc_data);
+ at91_add_device_mci(1, &mci1_data);
/* Ethernet */
at91_add_device_eth(&ek_macb_data);
/* NAND */
diff --git a/arch/arm/mach-at91/board-sam9g20ek.c b/arch/arm/mach-at91/board-sam9g20ek.c
index 4ea4ee00364b..3ab2b86a3762 100644
--- a/arch/arm/mach-at91/board-sam9g20ek.c
+++ b/arch/arm/mach-at91/board-sam9g20ek.c
@@ -92,7 +92,7 @@ static struct at91_udc_data __initdata ek_udc_data = {
* SPI devices.
*/
static struct spi_board_info ek_spi_devices[] = {
-#if !(defined(CONFIG_MMC_ATMELMCI) || defined(CONFIG_MMC_AT91))
+#if !IS_ENABLED(CONFIG_MMC_ATMELMCI)
{ /* DataFlash chip */
.modalias = "mtd_dataflash",
.chip_select = 1,
@@ -199,7 +199,6 @@ static void __init ek_add_device_nand(void)
* MCI (SD/MMC)
* wp_pin and vcc_pin are not connected
*/
-#if defined(CONFIG_MMC_ATMELMCI) || defined(CONFIG_MMC_ATMELMCI_MODULE)
static struct mci_platform_data __initdata ek_mmc_data = {
.slot[1] = {
.bus_width = 4,
@@ -208,28 +207,15 @@ static struct mci_platform_data __initdata ek_mmc_data = {
},
};
-#else
-static struct at91_mmc_data __initdata ek_mmc_data = {
- .slot_b = 1, /* Only one slot so use slot B */
- .wire4 = 1,
- .det_pin = AT91_PIN_PC9,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
-};
-#endif
static void __init ek_add_device_mmc(void)
{
-#if defined(CONFIG_MMC_ATMELMCI) || defined(CONFIG_MMC_ATMELMCI_MODULE)
if (ek_have_2mmc()) {
ek_mmc_data.slot[0].bus_width = 4;
ek_mmc_data.slot[0].detect_pin = AT91_PIN_PC2;
ek_mmc_data.slot[0].wp_pin = -1;
}
at91_add_device_mci(0, &ek_mmc_data);
-#else
- at91_add_device_mmc(0, &ek_mmc_data);
-#endif
}
/*
diff --git a/arch/arm/mach-at91/board-sam9rlek.c b/arch/arm/mach-at91/board-sam9rlek.c
index e7dc3ead7045..fb89ea92e3f2 100644
--- a/arch/arm/mach-at91/board-sam9rlek.c
+++ b/arch/arm/mach-at91/board-sam9rlek.c
@@ -56,11 +56,12 @@ static struct usba_platform_data __initdata ek_usba_udc_data = {
/*
* MCI (SD/MMC)
*/
-static struct at91_mmc_data __initdata ek_mmc_data = {
- .wire4 = 1,
- .det_pin = AT91_PIN_PA15,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PA15,
+ .wp_pin = -EINVAL,
+ },
};
@@ -303,7 +304,7 @@ static void __init ek_board_init(void)
/* SPI */
at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices));
/* MMC */
- at91_add_device_mmc(0, &ek_mmc_data);
+ at91_add_device_mci(0, &mci0_data);
/* LCD Controller */
at91_add_device_lcdc(&ek_lcdc_data);
/* AC97 */
diff --git a/arch/arm/mach-at91/board-stamp9g20.c b/arch/arm/mach-at91/board-stamp9g20.c
index 29eae1626bf7..c3fb31d5116e 100644
--- a/arch/arm/mach-at91/board-stamp9g20.c
+++ b/arch/arm/mach-at91/board-stamp9g20.c
@@ -83,7 +83,6 @@ static void __init add_device_nand(void)
* MCI (SD/MMC)
* det_pin, wp_pin and vcc_pin are not connected
*/
-#if defined(CONFIG_MMC_ATMELMCI) || defined(CONFIG_MMC_ATMELMCI_MODULE)
static struct mci_platform_data __initdata mmc_data = {
.slot[0] = {
.bus_width = 4,
@@ -91,15 +90,6 @@ static struct mci_platform_data __initdata mmc_data = {
.wp_pin = -1,
},
};
-#else
-static struct at91_mmc_data __initdata mmc_data = {
- .slot_b = 0,
- .wire4 = 1,
- .det_pin = -EINVAL,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
-};
-#endif
/*
@@ -223,11 +213,7 @@ void __init stamp9g20_board_init(void)
/* NAND */
add_device_nand();
/* MMC */
-#if defined(CONFIG_MMC_ATMELMCI) || defined(CONFIG_MMC_ATMELMCI_MODULE)
at91_add_device_mci(0, &mmc_data);
-#else
- at91_add_device_mmc(0, &mmc_data);
-#endif
/* W1 */
add_w1();
}
diff --git a/arch/arm/mach-at91/board-usb-a926x.c b/arch/arm/mach-at91/board-usb-a926x.c
index c1476b9fe7b9..6ea069b57335 100644
--- a/arch/arm/mach-at91/board-usb-a926x.c
+++ b/arch/arm/mach-at91/board-usb-a926x.c
@@ -109,14 +109,12 @@ static struct mmc_spi_platform_data at91_mmc_spi_pdata = {
* SPI devices.
*/
static struct spi_board_info usb_a9263_spi_devices[] = {
-#if !defined(CONFIG_MMC_AT91)
{ /* DataFlash chip */
.modalias = "mtd_dataflash",
.chip_select = 0,
.max_speed_hz = 15 * 1000 * 1000,
.bus_num = 0,
}
-#endif
};
static struct spi_board_info usb_a9g20_spi_devices[] = {
diff --git a/arch/arm/mach-at91/board-yl-9200.c b/arch/arm/mach-at91/board-yl-9200.c
index 516d340549d8..f162fdfd66eb 100644
--- a/arch/arm/mach-at91/board-yl-9200.c
+++ b/arch/arm/mach-at91/board-yl-9200.c
@@ -119,11 +119,12 @@ static struct at91_udc_data __initdata yl9200_udc_data = {
/*
* MMC
*/
-static struct at91_mmc_data __initdata yl9200_mmc_data = {
- .det_pin = AT91_PIN_PB9,
- .wire4 = 1,
- .wp_pin = -EINVAL,
- .vcc_pin = -EINVAL,
+static struct mci_platform_data __initdata yl9200_mci0_data = {
+ .slot[0] = {
+ .bus_width = 4,
+ .detect_pin = AT91_PIN_PB9,
+ .wp_pin = -EINVAL,
+ },
};
/*
@@ -541,9 +542,6 @@ void __init yl9200_add_device_video(void) {}
static void __init yl9200_board_init(void)
{
- /* Setup the LEDs D2=PB17 (timer), D3=PB16 (cpu) */
- at91_init_leds(AT91_PIN_PB16, AT91_PIN_PB17);
-
/* Serial */
/* DBGU on ttyS0. (Rx & Tx only) */
at91_register_uart(0, 0, 0);
@@ -568,7 +566,7 @@ static void __init yl9200_board_init(void)
/* I2C */
at91_add_device_i2c(yl9200_i2c_devices, ARRAY_SIZE(yl9200_i2c_devices));
/* MMC */
- at91_add_device_mmc(0, &yl9200_mmc_data);
+ at91_add_device_mci(0, &yl9200_mci0_data);
/* NAND */
at91_add_device_nand(&yl9200_nand_data);
/* NOR Flash */
diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index 369afc2ffc5b..c55a4364ffb4 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -187,7 +187,6 @@ struct at91_can_data {
extern void __init at91_add_device_can(struct at91_can_data *data);
/* LEDs */
-extern void __init at91_init_leds(u8 cpu_led, u8 timer_led);
extern void __init at91_gpio_leds(struct gpio_led *leds, int nr);
extern void __init at91_pwm_leds(struct gpio_led *leds, int nr);
diff --git a/arch/arm/mach-at91/include/mach/hardware.h b/arch/arm/mach-at91/include/mach/hardware.h
index 09242b67d277..711a7892d331 100644
--- a/arch/arm/mach-at91/include/mach/hardware.h
+++ b/arch/arm/mach-at91/include/mach/hardware.h
@@ -67,13 +67,13 @@
* to 0xFEF78000 .. 0xFF000000. (544Kb)
*/
#define AT91_IO_PHYS_BASE 0xFFF78000
-#define AT91_IO_VIRT_BASE (0xFF000000 - AT91_IO_SIZE)
+#define AT91_IO_VIRT_BASE IOMEM(0xFF000000 - AT91_IO_SIZE)
#else
/*
* Identity mapping for the non MMU case.
*/
#define AT91_IO_PHYS_BASE AT91_BASE_SYS
-#define AT91_IO_VIRT_BASE AT91_IO_PHYS_BASE
+#define AT91_IO_VIRT_BASE IOMEM(AT91_IO_PHYS_BASE)
#endif
#define AT91_IO_SIZE (0xFFFFFFFF - AT91_IO_PHYS_BASE + 1)
diff --git a/arch/arm/mach-at91/include/mach/uncompress.h b/arch/arm/mach-at91/include/mach/uncompress.h
index 6f6118d1576a..97ad68a826f8 100644
--- a/arch/arm/mach-at91/include/mach/uncompress.h
+++ b/arch/arm/mach-at91/include/mach/uncompress.h
@@ -94,7 +94,7 @@ static const u32 uarts_sam9x5[] = {
0,
};
-static inline const u32* decomp_soc_detect(u32 dbgu_base)
+static inline const u32* decomp_soc_detect(void __iomem *dbgu_base)
{
u32 cidr, socid;
@@ -142,10 +142,10 @@ static inline void arch_decomp_setup(void)
int i = 0;
const u32* usarts;
- usarts = decomp_soc_detect(AT91_BASE_DBGU0);
+ usarts = decomp_soc_detect((void __iomem *)AT91_BASE_DBGU0);
if (!usarts)
- usarts = decomp_soc_detect(AT91_BASE_DBGU1);
+ usarts = decomp_soc_detect((void __iomem *)AT91_BASE_DBGU1);
if (!usarts) {
at91_uart = NULL;
return;
diff --git a/arch/arm/mach-at91/leds.c b/arch/arm/mach-at91/leds.c
index 8dfafe76ffe6..1b1e62b5f41b 100644
--- a/arch/arm/mach-at91/leds.c
+++ b/arch/arm/mach-at91/leds.c
@@ -90,108 +90,3 @@ void __init at91_pwm_leds(struct gpio_led *leds, int nr)
#else
void __init at91_pwm_leds(struct gpio_led *leds, int nr){}
#endif
-
-
-/* ------------------------------------------------------------------------- */
-
-#if defined(CONFIG_LEDS)
-
-#include <asm/leds.h>
-
-/*
- * Old ARM-specific LED framework; not fully functional when generic time is
- * in use.
- */
-
-static u8 at91_leds_cpu;
-static u8 at91_leds_timer;
-
-static inline void at91_led_on(unsigned int led)
-{
- at91_set_gpio_value(led, 0);
-}
-
-static inline void at91_led_off(unsigned int led)
-{
- at91_set_gpio_value(led, 1);
-}
-
-static inline void at91_led_toggle(unsigned int led)
-{
- unsigned long is_off = at91_get_gpio_value(led);
- if (is_off)
- at91_led_on(led);
- else
- at91_led_off(led);
-}
-
-
-/*
- * Handle LED events.
- */
-static void at91_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- switch(evt) {
- case led_start: /* System startup */
- at91_led_on(at91_leds_cpu);
- break;
-
- case led_stop: /* System stop / suspend */
- at91_led_off(at91_leds_cpu);
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer: /* Every 50 timer ticks */
- at91_led_toggle(at91_leds_timer);
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start: /* Entering idle state */
- at91_led_off(at91_leds_cpu);
- break;
-
- case led_idle_end: /* Exit idle state */
- at91_led_on(at91_leds_cpu);
- break;
-#endif
-
- default:
- break;
- }
-
- local_irq_restore(flags);
-}
-
-
-static int __init leds_init(void)
-{
- if (!at91_leds_timer || !at91_leds_cpu)
- return -ENODEV;
-
- leds_event = at91_leds_event;
-
- leds_event(led_start);
- return 0;
-}
-
-__initcall(leds_init);
-
-
-void __init at91_init_leds(u8 cpu_led, u8 timer_led)
-{
- /* Enable GPIO to access the LEDs */
- at91_set_gpio_output(cpu_led, 1);
- at91_set_gpio_output(timer_led, 1);
-
- at91_leds_cpu = cpu_led;
- at91_leds_timer = timer_led;
-}
-
-#else
-void __init at91_init_leds(u8 cpu_led, u8 timer_led) {}
-#endif
diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c
index 944bffb08991..e6f52de1062f 100644
--- a/arch/arm/mach-at91/setup.c
+++ b/arch/arm/mach-at91/setup.c
@@ -73,7 +73,7 @@ void __init at91_init_sram(int bank, unsigned long base, unsigned int length)
{
struct map_desc *desc = &sram_desc[bank];
- desc->virtual = AT91_IO_VIRT_BASE - length;
+ desc->virtual = (unsigned long)AT91_IO_VIRT_BASE - length;
if (bank > 0)
desc->virtual -= sram_desc[bank - 1].length;
@@ -88,7 +88,7 @@ void __init at91_init_sram(int bank, unsigned long base, unsigned int length)
}
static struct map_desc at91_io_desc __initdata = {
- .virtual = AT91_VA_BASE_SYS,
+ .virtual = (unsigned long)AT91_VA_BASE_SYS,
.pfn = __phys_to_pfn(AT91_BASE_SYS),
.length = SZ_16K,
.type = MT_DEVICE,
diff --git a/arch/arm/mach-clps711x/Kconfig b/arch/arm/mach-clps711x/Kconfig
index ea036d621581..e6135363765a 100644
--- a/arch/arm/mach-clps711x/Kconfig
+++ b/arch/arm/mach-clps711x/Kconfig
@@ -16,12 +16,6 @@ config ARCH_CDB89712
The board includes 2 serial ports, Ethernet, IRDA, and expansion
headers. It comes with 16 MB SDRAM and 8 MB flash ROM.
-config ARCH_CEIVA
- bool "CEIVA"
- help
- Say Y here if you intend to run this kernel on the Ceiva/Polaroid
- PhotoMax Digital Picture Frame.
-
config ARCH_CLEP7312
bool "CLEP7312"
help
diff --git a/arch/arm/mach-clps711x/Makefile b/arch/arm/mach-clps711x/Makefile
index f2f0256232e3..6da6940b3656 100644
--- a/arch/arm/mach-clps711x/Makefile
+++ b/arch/arm/mach-clps711x/Makefile
@@ -9,12 +9,9 @@ obj-m :=
obj-n :=
obj- :=
-obj-$(CONFIG_ARCH_CEIVA) += ceiva.o
obj-$(CONFIG_ARCH_AUTCPU12) += autcpu12.o
obj-$(CONFIG_ARCH_CDB89712) += cdb89712.o
obj-$(CONFIG_ARCH_CLEP7312) += clep7312.o
obj-$(CONFIG_ARCH_EDB7211) += edb7211-arch.o edb7211-mm.o
obj-$(CONFIG_ARCH_FORTUNET) += fortunet.o
obj-$(CONFIG_ARCH_P720T) += p720t.o
-leds-$(CONFIG_ARCH_P720T) += p720t-leds.o
-obj-$(CONFIG_LEDS) += $(leds-y)
diff --git a/arch/arm/mach-clps711x/ceiva.c b/arch/arm/mach-clps711x/ceiva.c
deleted file mode 100644
index a70147e347ac..000000000000
--- a/arch/arm/mach-clps711x/ceiva.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * linux/arch/arm/mach-clps711x/arch-ceiva.c
- *
- * Copyright (C) 2002, Rob Scott <rscott@mtrob.fdns.net>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <linux/init.h>
-#include <linux/types.h>
-#include <linux/string.h>
-
-#include <asm/setup.h>
-#include <asm/mach-types.h>
-#include <asm/mach/arch.h>
-
-#include <linux/kernel.h>
-
-#include <mach/hardware.h>
-#include <asm/page.h>
-#include <asm/pgtable.h>
-#include <asm/sizes.h>
-
-#include <asm/mach/map.h>
-
-#include "common.h"
-
-static struct map_desc ceiva_io_desc[] __initdata = {
- /* SED1355 controlled video RAM & registers */
- {
- .virtual = CEIVA_VIRT_SED1355,
- .pfn = __phys_to_pfn(CEIVA_PHYS_SED1355),
- .length = SZ_2M,
- .type = MT_DEVICE
- }
-};
-
-
-static void __init ceiva_map_io(void)
-{
- clps711x_map_io();
- iotable_init(ceiva_io_desc, ARRAY_SIZE(ceiva_io_desc));
-}
-
-
-MACHINE_START(CEIVA, "CEIVA/Polaroid Photo MAX Digital Picture Frame")
- /* Maintainer: Rob Scott */
- .atag_offset = 0x100,
- .map_io = ceiva_map_io,
- .init_irq = clps711x_init_irq,
- .timer = &clps711x_timer,
- .restart = clps711x_restart,
-MACHINE_END
diff --git a/arch/arm/mach-clps711x/common.c b/arch/arm/mach-clps711x/common.c
index f15293bd7974..509243d89a32 100644
--- a/arch/arm/mach-clps711x/common.c
+++ b/arch/arm/mach-clps711x/common.c
@@ -19,24 +19,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <linux/kernel.h>
-#include <linux/mm.h>
+#include <linux/io.h>
#include <linux/init.h>
#include <linux/interrupt.h>
-#include <linux/io.h>
#include <linux/irq.h>
-#include <linux/sched.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
#include <asm/sizes.h>
-#include <mach/hardware.h>
-#include <asm/irq.h>
-#include <asm/leds.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
#include <asm/mach/map.h>
#include <asm/mach/time.h>
#include <asm/system_misc.h>
+#include <mach/hardware.h>
+
+static struct clk *clk_pll, *clk_bus, *clk_uart, *clk_timerl, *clk_timerh,
+ *clk_tint, *clk_spi;
+static unsigned long latch;
+
/*
* This maps the generic CLPS711x registers
*/
@@ -166,8 +167,8 @@ void __init clps711x_init_irq(void)
static unsigned long clps711x_gettimeoffset(void)
{
unsigned long hwticks;
- hwticks = LATCH - (clps_readl(TC2D) & 0xffff); /* since last underflow */
- return (hwticks * (tick_nsec / 1000)) / LATCH;
+ hwticks = latch - (clps_readl(TC2D) & 0xffff);
+ return (hwticks * (tick_nsec / 1000)) / latch;
}
/*
@@ -185,15 +186,71 @@ static struct irqaction clps711x_timer_irq = {
.handler = p720t_timer_interrupt,
};
+static void add_fixed_clk(struct clk *clk, const char *name, int rate)
+{
+ clk = clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate);
+ clk_register_clkdev(clk, name, NULL);
+}
+
static void __init clps711x_timer_init(void)
{
- unsigned int syscon;
+ int osc, ext, pll, cpu, bus, timl, timh, uart, spi;
+ u32 tmp;
+
+ osc = 3686400;
+ ext = 13000000;
+
+ tmp = clps_readl(PLLR) >> 24;
+ if (tmp)
+ pll = (osc * tmp) / 2;
+ else
+ pll = 73728000; /* Default value */
+
+ tmp = clps_readl(SYSFLG2);
+ if (tmp & SYSFLG2_CKMODE) {
+ cpu = ext;
+ bus = cpu;
+ spi = 135400;
+ } else {
+ cpu = pll;
+ if (cpu >= 36864000)
+ bus = cpu / 2;
+ else
+ bus = 36864000 / 2;
+ spi = cpu / 576;
+ }
+
+ uart = bus / 10;
+
+ if (tmp & SYSFLG2_CKMODE) {
+ tmp = clps_readl(SYSCON2);
+ if (tmp & SYSCON2_OSTB)
+ timh = ext / 26;
+ else
+ timh = 541440;
+ } else
+ timh = cpu / 144;
+
+ timl = timh / 256;
+
+ /* All clocks are fixed */
+ add_fixed_clk(clk_pll, "pll", pll);
+ add_fixed_clk(clk_bus, "bus", bus);
+ add_fixed_clk(clk_uart, "uart", uart);
+ add_fixed_clk(clk_timerl, "timer_lf", timl);
+ add_fixed_clk(clk_timerh, "timer_hf", timh);
+ add_fixed_clk(clk_tint, "tint", 64);
+ add_fixed_clk(clk_spi, "spi", spi);
+
+ pr_info("CPU frequency set at %i Hz.\n", cpu);
+
+ latch = (timh + HZ / 2) / HZ;
- syscon = clps_readl(SYSCON1);
- syscon |= SYSCON1_TC2S | SYSCON1_TC2M;
- clps_writel(syscon, SYSCON1);
+ tmp = clps_readl(SYSCON1);
+ tmp |= SYSCON1_TC2S | SYSCON1_TC2M;
+ clps_writel(tmp, SYSCON1);
- clps_writel(LATCH-1, TC2D); /* 512kHz / 100Hz - 1 */
+ clps_writel(latch - 1, TC2D);
setup_irq(IRQ_TC2OI, &clps711x_timer_irq);
}
diff --git a/arch/arm/mach-clps711x/include/mach/clps711x.h b/arch/arm/mach-clps711x/include/mach/clps711x.h
index 1dd806f2847e..c82e21ca49c7 100644
--- a/arch/arm/mach-clps711x/include/mach/clps711x.h
+++ b/arch/arm/mach-clps711x/include/mach/clps711x.h
@@ -31,8 +31,8 @@
#define PBDDR (0x0041)
#define PCDDR (0x0042)
#define PDDDR (0x0043)
-#define PEDR (0x0080)
-#define PEDDR (0x00c0)
+#define PEDR (0x0083)
+#define PEDDR (0x00c3)
#define SYSCON1 (0x0100)
#define SYSFLG1 (0x0140)
#define MEMCFG1 (0x0180)
@@ -77,7 +77,7 @@
#define KBDEOI (0x1700)
#define DAIR (0x2000)
-#define DAIR0 (0x2040)
+#define DAIDR0 (0x2040)
#define DAIDR1 (0x2080)
#define DAIDR2 (0x20c0)
#define DAISR (0x2100)
@@ -191,8 +191,7 @@
#define UBRLCR_WRDLEN8 (3 << 17)
#define UBRLCR_WRDLEN_MASK (3 << 17)
-#define SYNCIO_FRMLEN(x) (((x) & 0x3f) << 7)
-#define SYNCIO_CFGLEN(x) ((x) & 0x7f)
+#define SYNCIO_FRMLEN(x) (((x) & 0x1f) << 8)
#define SYNCIO_SMCKEN (1 << 13)
#define SYNCIO_TXFRMEN (1 << 14)
diff --git a/arch/arm/mach-clps711x/include/mach/debug-macro.S b/arch/arm/mach-clps711x/include/mach/debug-macro.S
index 118b3d930573..cb3684f8dae0 100644
--- a/arch/arm/mach-clps711x/include/mach/debug-macro.S
+++ b/arch/arm/mach-clps711x/include/mach/debug-macro.S
@@ -28,17 +28,11 @@
.endm
.macro waituart,rd,rx
-1001: ldr \rd, [\rx, #0x0140] @ SYSFLGx
- tst \rd, #1 << 11 @ UBUSYx
- bne 1001b
.endm
.macro busyuart,rd,rx
- tst \rx, #0x1000 @ UART2 does not have CTS here
- bne 1002f
1001: ldr \rd, [\rx, #0x0140] @ SYSFLGx
- tst \rd, #1 << 8 @ CTS
+ tst \rd, #1 << 11 @ UBUSYx
bne 1001b
-1002:
.endm
diff --git a/arch/arm/mach-clps711x/include/mach/hardware.h b/arch/arm/mach-clps711x/include/mach/hardware.h
index 13a64fcd7dd1..8497775d6ee5 100644
--- a/arch/arm/mach-clps711x/include/mach/hardware.h
+++ b/arch/arm/mach-clps711x/include/mach/hardware.h
@@ -116,7 +116,6 @@
#endif /* CONFIG_ARCH_EDB7211 */
-
/*
* Relevant bits in port D, which controls power to the various parts of
* the LCD on the EDB7211.
@@ -125,51 +124,4 @@
#define EDB_PD2_LCDEN (1<<2)
#define EDB_PD3_LCDBL (1<<3)
-
-#if defined (CONFIG_ARCH_CEIVA)
-
-/*
- * The two flash banks are wired to chip selects 0 and 1. This is the mapping
- * for them.
- *
- * nCS0 and nCS1 are at 0x70000000 and 0x60000000, respectively, when running
- * in jumpered boot mode.
- */
-#define CEIVA_PHYS_FLASH1 CS0_PHYS_BASE /* physical */
-#define CEIVA_PHYS_FLASH2 CS1_PHYS_BASE /* physical */
-
-#define CEIVA_VIRT_FLASH1 (0xfa000000) /* virtual */
-#define CEIVA_VIRT_FLASH2 (0xfb000000) /* virtual */
-
-#define CEIVA_FLASH_SIZE 0x100000
-#define CEIVA_FLASH_WIDTH 2
-
-/*
- * SED1355 LCD controller
- */
-#define CEIVA_PHYS_SED1355 CS2_PHYS_BASE
-#define CEIVA_VIRT_SED1355 (0xfc000000)
-
-/*
- * Relevant bits in port D, which controls power to the various parts of
- * the LCD on the Ceiva Photo Max, and reset to the LCD controller.
- */
-
-// Reset line to SED1355 (must be high to operate)
-#define CEIVA_PD1_LCDRST (1<<1)
-// LCD panel enable (set to one, to enable LCD)
-#define CEIVA_PD4_LCDEN (1<<4)
-// Backlight (set to one, to turn on backlight
-#define CEIVA_PD5_LCDBL (1<<5)
-
-/*
- * Relevant bits in port B, which report the status of the buttons.
- */
-
-// White button
-#define CEIVA_PB4_WHT_BTN (1<<4)
-// Black button
-#define CEIVA_PB0_BLK_BTN (1<<0)
-#endif // #if defined (CONFIG_ARCH_CEIVA)
-
#endif
diff --git a/arch/arm/mach-clps711x/include/mach/timex.h b/arch/arm/mach-clps711x/include/mach/timex.h
index ac8823ccff93..de6fd192d1c3 100644
--- a/arch/arm/mach-clps711x/include/mach/timex.h
+++ b/arch/arm/mach-clps711x/include/mach/timex.h
@@ -1,23 +1,2 @@
-/*
- * arch/arm/mach-clps711x/include/mach/timex.h
- *
- * Prospector 720T architecture timex specifications
- *
- * Copyright (C) 2000 Deep Blue Solutions Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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
- */
-
+/* Bogus value */
#define CLOCK_TICK_RATE 512000
diff --git a/arch/arm/mach-clps711x/p720t-leds.c b/arch/arm/mach-clps711x/p720t-leds.c
deleted file mode 100644
index bbc449fbe14a..000000000000
--- a/arch/arm/mach-clps711x/p720t-leds.c
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * linux/arch/arm/mach-clps711x/leds.c
- *
- * Integrator LED control routines
- *
- * Copyright (C) 2000 Deep Blue Solutions Ltd
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/io.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-#include <asm/mach-types.h>
-
-static void p720t_leds_event(led_event_t ledevt)
-{
- unsigned long flags;
- u32 pddr;
-
- local_irq_save(flags);
- switch(ledevt) {
- case led_idle_start:
- break;
-
- case led_idle_end:
- break;
-
- case led_timer:
- pddr = clps_readb(PDDR);
- clps_writeb(pddr ^ 1, PDDR);
- break;
-
- default:
- break;
- }
-
- local_irq_restore(flags);
-}
-
-static int __init leds_init(void)
-{
- if (machine_is_p720t())
- leds_event = p720t_leds_event;
-
- return 0;
-}
-
-arch_initcall(leds_init);
diff --git a/arch/arm/mach-clps711x/p720t.c b/arch/arm/mach-clps711x/p720t.c
index f266d90b9efc..b752b586fc2f 100644
--- a/arch/arm/mach-clps711x/p720t.c
+++ b/arch/arm/mach-clps711x/p720t.c
@@ -23,6 +23,8 @@
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
#include <mach/hardware.h>
#include <asm/pgtable.h>
@@ -34,6 +36,8 @@
#include <asm/mach/map.h>
#include <mach/syspld.h>
+#include <asm/hardware/clps7111.h>
+
#include "common.h"
/*
@@ -107,6 +111,64 @@ static void __init p720t_init_early(void)
}
}
+/*
+ * LED controled by CPLD
+ */
+#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
+static void p720t_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
+{
+ u8 reg = clps_readb(PDDR);
+
+ if (b != LED_OFF)
+ reg |= 0x1;
+ else
+ reg &= ~0x1;
+
+ clps_writeb(reg, PDDR);
+}
+
+static enum led_brightness p720t_led_get(struct led_classdev *cdev)
+{
+ u8 reg = clps_readb(PDDR);
+
+ return (reg & 0x1) ? LED_FULL : LED_OFF;
+}
+
+static int __init p720t_leds_init(void)
+{
+
+ struct led_classdev *cdev;
+ int ret;
+
+ if (!machine_is_p720t())
+ return -ENODEV;
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
+
+ cdev->name = "p720t:0";
+ cdev->brightness_set = p720t_led_set;
+ cdev->brightness_get = p720t_led_get;
+ cdev->default_trigger = "heartbeat";
+
+ ret = led_classdev_register(NULL, cdev);
+ if (ret < 0) {
+ kfree(cdev);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Since we may have triggers on any subsystem, defer registration
+ * until after subsystem_init.
+ */
+fs_initcall(p720t_leds_init);
+#endif
+
MACHINE_START(P720T, "ARM-Prospector720T")
/* Maintainer: ARM Ltd/Deep Blue Solutions Ltd */
.atag_offset = 0x100,
diff --git a/arch/arm/mach-ebsa110/Makefile b/arch/arm/mach-ebsa110/Makefile
index 6520ac835802..935e4af01a27 100644
--- a/arch/arm/mach-ebsa110/Makefile
+++ b/arch/arm/mach-ebsa110/Makefile
@@ -4,9 +4,7 @@
# Object file lists.
-obj-y := core.o io.o
+obj-y := core.o io.o leds.o
obj-m :=
obj-n :=
obj- :=
-
-obj-$(CONFIG_LEDS) += leds.o
diff --git a/arch/arm/mach-ebsa110/core.c b/arch/arm/mach-ebsa110/core.c
index 6f8068692edf..f0fe6b5350e2 100644
--- a/arch/arm/mach-ebsa110/core.c
+++ b/arch/arm/mach-ebsa110/core.c
@@ -74,22 +74,22 @@ static struct map_desc ebsa110_io_desc[] __initdata = {
* sparse external-decode ISAIO space
*/
{ /* IRQ_STAT/IRQ_MCLR */
- .virtual = IRQ_STAT,
+ .virtual = (unsigned long)IRQ_STAT,
.pfn = __phys_to_pfn(TRICK4_PHYS),
.length = TRICK4_SIZE,
.type = MT_DEVICE
}, { /* IRQ_MASK/IRQ_MSET */
- .virtual = IRQ_MASK,
+ .virtual = (unsigned long)IRQ_MASK,
.pfn = __phys_to_pfn(TRICK3_PHYS),
.length = TRICK3_SIZE,
.type = MT_DEVICE
}, { /* SOFT_BASE */
- .virtual = SOFT_BASE,
+ .virtual = (unsigned long)SOFT_BASE,
.pfn = __phys_to_pfn(TRICK1_PHYS),
.length = TRICK1_SIZE,
.type = MT_DEVICE
}, { /* PIT_BASE */
- .virtual = PIT_BASE,
+ .virtual = (unsigned long)PIT_BASE,
.pfn = __phys_to_pfn(TRICK0_PHYS),
.length = TRICK0_SIZE,
.type = MT_DEVICE
diff --git a/arch/arm/mach-ebsa110/core.h b/arch/arm/mach-ebsa110/core.h
index c93c9e43012d..afe137ee172e 100644
--- a/arch/arm/mach-ebsa110/core.h
+++ b/arch/arm/mach-ebsa110/core.h
@@ -31,11 +31,11 @@
#define TRICK7_PHYS 0xf3c00000
/* Virtual addresses */
-#define PIT_BASE 0xfc000000 /* trick 0 */
-#define SOFT_BASE 0xfd000000 /* trick 1 */
-#define IRQ_MASK 0xfe000000 /* trick 3 - read */
-#define IRQ_MSET 0xfe000000 /* trick 3 - write */
-#define IRQ_STAT 0xff000000 /* trick 4 - read */
-#define IRQ_MCLR 0xff000000 /* trick 4 - write */
+#define PIT_BASE IOMEM(0xfc000000) /* trick 0 */
+#define SOFT_BASE IOMEM(0xfd000000) /* trick 1 */
+#define IRQ_MASK IOMEM(0xfe000000) /* trick 3 - read */
+#define IRQ_MSET IOMEM(0xfe000000) /* trick 3 - write */
+#define IRQ_STAT IOMEM(0xff000000) /* trick 4 - read */
+#define IRQ_MCLR IOMEM(0xff000000) /* trick 4 - write */
#endif
diff --git a/arch/arm/mach-ebsa110/leds.c b/arch/arm/mach-ebsa110/leds.c
index 99e14e362500..0398258c20cd 100644
--- a/arch/arm/mach-ebsa110/leds.c
+++ b/arch/arm/mach-ebsa110/leds.c
@@ -1,52 +1,71 @@
/*
- * linux/arch/arm/mach-ebsa110/leds.c
+ * Driver for the LED found on the EBSA110 machine
+ * Based on Versatile and RealView machine LED code
*
- * 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.
- *
- * EBSA-110 LED control routines. We use the led as follows:
- *
- * - Red - toggles state every 50 timer interrupts
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Bryan Wu <bryan.wu@canonical.com>
*/
-#include <linux/module.h>
-#include <linux/spinlock.h>
+#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
-#include <mach/hardware.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include "core.h"
-static spinlock_t leds_lock;
-
-static void ebsa110_leds_event(led_event_t ledevt)
+#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
+static void ebsa110_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
{
- unsigned long flags;
+ u8 reg = __raw_readb(SOFT_BASE);
- spin_lock_irqsave(&leds_lock, flags);
+ if (b != LED_OFF)
+ reg |= 0x80;
+ else
+ reg &= ~0x80;
- switch(ledevt) {
- case led_timer:
- *(volatile unsigned char *)SOFT_BASE ^= 128;
- break;
+ __raw_writeb(reg, SOFT_BASE);
+}
- default:
- break;
- }
+static enum led_brightness ebsa110_led_get(struct led_classdev *cdev)
+{
+ u8 reg = __raw_readb(SOFT_BASE);
- spin_unlock_irqrestore(&leds_lock, flags);
+ return (reg & 0x80) ? LED_FULL : LED_OFF;
}
-static int __init leds_init(void)
+static int __init ebsa110_leds_init(void)
{
- if (machine_is_ebsa110())
- leds_event = ebsa110_leds_event;
+
+ struct led_classdev *cdev;
+ int ret;
+
+ if (!machine_is_ebsa110())
+ return -ENODEV;
+
+ cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+ if (!cdev)
+ return -ENOMEM;
+
+ cdev->name = "ebsa110:0";
+ cdev->brightness_set = ebsa110_led_set;
+ cdev->brightness_get = ebsa110_led_get;
+ cdev->default_trigger = "heartbeat";
+
+ ret = led_classdev_register(NULL, cdev);
+ if (ret < 0) {
+ kfree(cdev);
+ return ret;
+ }
return 0;
}
-__initcall(leds_init);
+/*
+ * Since we may have triggers on any subsystem, defer registration
+ * until after subsystem_init.
+ */
+fs_initcall(ebsa110_leds_init);
+#endif
diff --git a/arch/arm/mach-ep93xx/adssphere.c b/arch/arm/mach-ep93xx/adssphere.c
index a472777e9eba..41383bf03d4b 100644
--- a/arch/arm/mach-ep93xx/adssphere.c
+++ b/arch/arm/mach-ep93xx/adssphere.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/sizes.h>
#include <mach/hardware.h>
diff --git a/arch/arm/mach-ep93xx/gesbc9312.c b/arch/arm/mach-ep93xx/gesbc9312.c
index 437c34111155..7fd705b5efe4 100644
--- a/arch/arm/mach-ep93xx/gesbc9312.c
+++ b/arch/arm/mach-ep93xx/gesbc9312.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
+#include <linux/sizes.h>
#include <mach/hardware.h>
diff --git a/arch/arm/mach-ep93xx/ts72xx.c b/arch/arm/mach-ep93xx/ts72xx.c
index 75cab2d7ec73..3c4c233391dc 100644
--- a/arch/arm/mach-ep93xx/ts72xx.c
+++ b/arch/arm/mach-ep93xx/ts72xx.c
@@ -21,7 +21,6 @@
#include <linux/mtd/partitions.h>
#include <mach/hardware.h>
-#include <mach/ts72xx.h>
#include <asm/hardware/vic.h>
#include <asm/mach-types.h>
@@ -29,30 +28,31 @@
#include <asm/mach/arch.h>
#include "soc.h"
+#include "ts72xx.h"
static struct map_desc ts72xx_io_desc[] __initdata = {
{
- .virtual = TS72XX_MODEL_VIRT_BASE,
+ .virtual = (unsigned long)TS72XX_MODEL_VIRT_BASE,
.pfn = __phys_to_pfn(TS72XX_MODEL_PHYS_BASE),
.length = TS72XX_MODEL_SIZE,
.type = MT_DEVICE,
}, {
- .virtual = TS72XX_OPTIONS_VIRT_BASE,
+ .virtual = (unsigned long)TS72XX_OPTIONS_VIRT_BASE,
.pfn = __phys_to_pfn(TS72XX_OPTIONS_PHYS_BASE),
.length = TS72XX_OPTIONS_SIZE,
.type = MT_DEVICE,
}, {
- .virtual = TS72XX_OPTIONS2_VIRT_BASE,
+ .virtual = (unsigned long)TS72XX_OPTIONS2_VIRT_BASE,
.pfn = __phys_to_pfn(TS72XX_OPTIONS2_PHYS_BASE),
.length = TS72XX_OPTIONS2_SIZE,
.type = MT_DEVICE,
}, {
- .virtual = TS72XX_RTC_INDEX_VIRT_BASE,
+ .virtual = (unsigned long)TS72XX_RTC_INDEX_VIRT_BASE,
.pfn = __phys_to_pfn(TS72XX_RTC_INDEX_PHYS_BASE),
.length = TS72XX_RTC_INDEX_SIZE,
.type = MT_DEVICE,
}, {
- .virtual = TS72XX_RTC_DATA_VIRT_BASE,
+ .virtual = (unsigned long)TS72XX_RTC_DATA_VIRT_BASE,
.pfn = __phys_to_pfn(TS72XX_RTC_DATA_PHYS_BASE),
.length = TS72XX_RTC_DATA_SIZE,
.type = MT_DEVICE,
diff --git a/arch/arm/mach-ep93xx/include/mach/ts72xx.h b/arch/arm/mach-ep93xx/ts72xx.h
index f1397a13e76b..071feaa30adc 100644
--- a/arch/arm/mach-ep93xx/include/mach/ts72xx.h
+++ b/arch/arm/mach-ep93xx/ts72xx.h
@@ -14,7 +14,7 @@
*/
#define TS72XX_MODEL_PHYS_BASE 0x22000000
-#define TS72XX_MODEL_VIRT_BASE 0xfebff000
+#define TS72XX_MODEL_VIRT_BASE IOMEM(0xfebff000)
#define TS72XX_MODEL_SIZE 0x00001000
#define TS72XX_MODEL_TS7200 0x00
@@ -26,7 +26,7 @@
#define TS72XX_OPTIONS_PHYS_BASE 0x22400000
-#define TS72XX_OPTIONS_VIRT_BASE 0xfebfe000
+#define TS72XX_OPTIONS_VIRT_BASE IOMEM(0xfebfe000)
#define TS72XX_OPTIONS_SIZE 0x00001000
#define TS72XX_OPTIONS_COM2_RS485 0x02
@@ -34,18 +34,18 @@
#define TS72XX_OPTIONS2_PHYS_BASE 0x22800000
-#define TS72XX_OPTIONS2_VIRT_BASE 0xfebfd000
+#define TS72XX_OPTIONS2_VIRT_BASE IOMEM(0xfebfd000)
#define TS72XX_OPTIONS2_SIZE 0x00001000
#define TS72XX_OPTIONS2_TS9420 0x04
#define TS72XX_OPTIONS2_TS9420_BOOT 0x02
-#define TS72XX_RTC_INDEX_VIRT_BASE 0xfebf9000
+#define TS72XX_RTC_INDEX_VIRT_BASE IOMEM(0xfebf9000)
#define TS72XX_RTC_INDEX_PHYS_BASE 0x10800000
#define TS72XX_RTC_INDEX_SIZE 0x00001000
-#define TS72XX_RTC_DATA_VIRT_BASE 0xfebf8000
+#define TS72XX_RTC_DATA_VIRT_BASE IOMEM(0xfebf8000)
#define TS72XX_RTC_DATA_PHYS_BASE 0x11700000
#define TS72XX_RTC_DATA_SIZE 0x00001000
diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig
index b5b4c8c9db11..4372075c551f 100644
--- a/arch/arm/mach-exynos/Kconfig
+++ b/arch/arm/mach-exynos/Kconfig
@@ -221,6 +221,7 @@ config MACH_SMDKV310
select EXYNOS4_SETUP_KEYPAD
select EXYNOS4_SETUP_SDHCI
select EXYNOS4_SETUP_USB_PHY
+ select S3C24XX_PWM
help
Machine support for Samsung SMDKV310
@@ -348,6 +349,7 @@ config MACH_ORIGEN
select EXYNOS4_SETUP_FIMD0
select EXYNOS4_SETUP_SDHCI
select EXYNOS4_SETUP_USB_PHY
+ select S3C24XX_PWM
help
Machine support for ORIGEN based on Samsung EXYNOS4210
@@ -383,6 +385,7 @@ config MACH_SMDK4212
select EXYNOS4_SETUP_KEYPAD
select EXYNOS4_SETUP_SDHCI
select EXYNOS4_SETUP_USB_PHY
+ select S3C24XX_PWM
help
Machine support for Samsung SMDK4212
@@ -405,6 +408,8 @@ config MACH_EXYNOS4_DT
select USE_OF
select ARM_AMBA
select HAVE_SAMSUNG_KEYPAD if INPUT_KEYBOARD
+ select PINCTRL
+ select PINCTRL_EXYNOS4
help
Machine support for Samsung Exynos4 machine with device tree enabled.
Select this if a fdt blob is available for the Exynos4 SoC based board.
@@ -418,8 +423,8 @@ config MACH_EXYNOS5_DT
select USE_OF
select ARM_AMBA
help
- Machine support for Samsung Exynos4 machine with device tree enabled.
- Select this if a fdt blob is available for the EXYNOS4 SoC based board.
+ Machine support for Samsung EXYNOS5 machine with device tree enabled.
+ Select this if a fdt blob is available for the EXYNOS5 SoC based board.
if ARCH_EXYNOS4
diff --git a/arch/arm/mach-exynos/clock-exynos4.c b/arch/arm/mach-exynos/clock-exynos4.c
index 2f51293c1875..6a45c9a9abe9 100644
--- a/arch/arm/mach-exynos/clock-exynos4.c
+++ b/arch/arm/mach-exynos/clock-exynos4.c
@@ -501,6 +501,10 @@ static struct clk exynos4_init_clocks_off[] = {
.enable = exynos4_clk_ip_cam_ctrl,
.ctrlbit = (1 << 3),
}, {
+ .name = "tsi",
+ .enable = exynos4_clk_ip_fsys_ctrl,
+ .ctrlbit = (1 << 4),
+ }, {
.name = "hsmmc",
.devname = "exynos4-sdhci.0",
.parent = &exynos4_clk_aclk_133.clk,
@@ -530,6 +534,14 @@ static struct clk exynos4_init_clocks_off[] = {
.enable = exynos4_clk_ip_fsys_ctrl,
.ctrlbit = (1 << 9),
}, {
+ .name = "onenand",
+ .enable = exynos4_clk_ip_fsys_ctrl,
+ .ctrlbit = (1 << 15),
+ }, {
+ .name = "nfcon",
+ .enable = exynos4_clk_ip_fsys_ctrl,
+ .ctrlbit = (1 << 16),
+ }, {
.name = "dac",
.devname = "s5p-sdo",
.enable = exynos4_clk_ip_tv_ctrl,
@@ -615,6 +627,25 @@ static struct clk exynos4_init_clocks_off[] = {
.enable = exynos4_clk_ip_peril_ctrl,
.ctrlbit = (1 << 21),
}, {
+ .name = "pcm",
+ .devname = "samsung-pcm.1",
+ .enable = exynos4_clk_ip_peril_ctrl,
+ .ctrlbit = (1 << 22),
+ }, {
+ .name = "pcm",
+ .devname = "samsung-pcm.2",
+ .enable = exynos4_clk_ip_peril_ctrl,
+ .ctrlbit = (1 << 23),
+ }, {
+ .name = "slimbus",
+ .enable = exynos4_clk_ip_peril_ctrl,
+ .ctrlbit = (1 << 25),
+ }, {
+ .name = "spdif",
+ .devname = "samsung-spdif",
+ .enable = exynos4_clk_ip_peril_ctrl,
+ .ctrlbit = (1 << 26),
+ }, {
.name = "ac97",
.devname = "samsung-ac97",
.enable = exynos4_clk_ip_peril_ctrl,
diff --git a/arch/arm/mach-exynos/clock-exynos5.c b/arch/arm/mach-exynos/clock-exynos5.c
index 3b00e299b624..c44ca1ee1b8d 100644
--- a/arch/arm/mach-exynos/clock-exynos5.c
+++ b/arch/arm/mach-exynos/clock-exynos5.c
@@ -547,6 +547,68 @@ static struct clksrc_clk exynos5_clk_aclk_66 = {
.reg_div = { .reg = EXYNOS5_CLKDIV_TOP0, .shift = 0, .size = 3 },
};
+static struct clksrc_clk exynos5_clk_mout_aclk_300_gscl_mid = {
+ .clk = {
+ .name = "mout_aclk_300_gscl_mid",
+ },
+ .sources = &exynos5_clkset_aclk,
+ .reg_src = { .reg = EXYNOS5_CLKSRC_TOP0, .shift = 24, .size = 1 },
+};
+
+static struct clk *exynos5_clkset_aclk_300_mid1_list[] = {
+ [0] = &exynos5_clk_sclk_vpll.clk,
+ [1] = &exynos5_clk_mout_cpll.clk,
+};
+
+static struct clksrc_sources exynos5_clkset_aclk_300_gscl_mid1 = {
+ .sources = exynos5_clkset_aclk_300_mid1_list,
+ .nr_sources = ARRAY_SIZE(exynos5_clkset_aclk_300_mid1_list),
+};
+
+static struct clksrc_clk exynos5_clk_mout_aclk_300_gscl_mid1 = {
+ .clk = {
+ .name = "mout_aclk_300_gscl_mid1",
+ },
+ .sources = &exynos5_clkset_aclk_300_gscl_mid1,
+ .reg_src = { .reg = EXYNOS5_CLKSRC_TOP1, .shift = 12, .size = 1 },
+};
+
+static struct clk *exynos5_clkset_aclk_300_gscl_list[] = {
+ [0] = &exynos5_clk_mout_aclk_300_gscl_mid.clk,
+ [1] = &exynos5_clk_mout_aclk_300_gscl_mid1.clk,
+};
+
+static struct clksrc_sources exynos5_clkset_aclk_300_gscl = {
+ .sources = exynos5_clkset_aclk_300_gscl_list,
+ .nr_sources = ARRAY_SIZE(exynos5_clkset_aclk_300_gscl_list),
+};
+
+static struct clksrc_clk exynos5_clk_mout_aclk_300_gscl = {
+ .clk = {
+ .name = "mout_aclk_300_gscl",
+ },
+ .sources = &exynos5_clkset_aclk_300_gscl,
+ .reg_src = { .reg = EXYNOS5_CLKSRC_TOP0, .shift = 25, .size = 1 },
+};
+
+static struct clk *exynos5_clk_src_gscl_300_list[] = {
+ [0] = &clk_ext_xtal_mux,
+ [1] = &exynos5_clk_mout_aclk_300_gscl.clk,
+};
+
+static struct clksrc_sources exynos5_clk_src_gscl_300 = {
+ .sources = exynos5_clk_src_gscl_300_list,
+ .nr_sources = ARRAY_SIZE(exynos5_clk_src_gscl_300_list),
+};
+
+static struct clksrc_clk exynos5_clk_aclk_300_gscl = {
+ .clk = {
+ .name = "aclk_300_gscl",
+ },
+ .sources = &exynos5_clk_src_gscl_300,
+ .reg_src = { .reg = EXYNOS5_CLKSRC_TOP3, .shift = 10, .size = 1 },
+};
+
static struct clk exynos5_init_clocks_off[] = {
{
.name = "timers",
@@ -564,35 +626,30 @@ static struct clk exynos5_init_clocks_off[] = {
.enable = exynos5_clk_ip_peris_ctrl,
.ctrlbit = (1 << 19),
}, {
- .name = "hsmmc",
- .devname = "exynos4-sdhci.0",
+ .name = "biu", /* bus interface unit clock */
+ .devname = "dw_mmc.0",
.parent = &exynos5_clk_aclk_200.clk,
.enable = exynos5_clk_ip_fsys_ctrl,
.ctrlbit = (1 << 12),
}, {
- .name = "hsmmc",
- .devname = "exynos4-sdhci.1",
+ .name = "biu",
+ .devname = "dw_mmc.1",
.parent = &exynos5_clk_aclk_200.clk,
.enable = exynos5_clk_ip_fsys_ctrl,
.ctrlbit = (1 << 13),
}, {
- .name = "hsmmc",
- .devname = "exynos4-sdhci.2",
+ .name = "biu",
+ .devname = "dw_mmc.2",
.parent = &exynos5_clk_aclk_200.clk,
.enable = exynos5_clk_ip_fsys_ctrl,
.ctrlbit = (1 << 14),
}, {
- .name = "hsmmc",
- .devname = "exynos4-sdhci.3",
+ .name = "biu",
+ .devname = "dw_mmc.3",
.parent = &exynos5_clk_aclk_200.clk,
.enable = exynos5_clk_ip_fsys_ctrl,
.ctrlbit = (1 << 15),
}, {
- .name = "dwmci",
- .parent = &exynos5_clk_aclk_200.clk,
- .enable = exynos5_clk_ip_fsys_ctrl,
- .ctrlbit = (1 << 16),
- }, {
.name = "sata",
.devname = "ahci",
.enable = exynos5_clk_ip_fsys_ctrl,
@@ -755,6 +812,26 @@ static struct clk exynos5_init_clocks_off[] = {
.enable = exynos5_clk_ip_peric_ctrl,
.ctrlbit = (1 << 18),
}, {
+ .name = "gscl",
+ .devname = "exynos-gsc.0",
+ .enable = exynos5_clk_ip_gscl_ctrl,
+ .ctrlbit = (1 << 0),
+ }, {
+ .name = "gscl",
+ .devname = "exynos-gsc.1",
+ .enable = exynos5_clk_ip_gscl_ctrl,
+ .ctrlbit = (1 << 1),
+ }, {
+ .name = "gscl",
+ .devname = "exynos-gsc.2",
+ .enable = exynos5_clk_ip_gscl_ctrl,
+ .ctrlbit = (1 << 2),
+ }, {
+ .name = "gscl",
+ .devname = "exynos-gsc.3",
+ .enable = exynos5_clk_ip_gscl_ctrl,
+ .ctrlbit = (1 << 3),
+ }, {
.name = SYSMMU_CLOCK_NAME,
.devname = SYSMMU_CLOCK_DEVNAME(mfc_l, 0),
.enable = &exynos5_clk_ip_mfc_ctrl,
@@ -882,6 +959,13 @@ static struct clk exynos5_clk_mdma1 = {
.ctrlbit = (1 << 4),
};
+static struct clk exynos5_clk_fimd1 = {
+ .name = "fimd",
+ .devname = "exynos5-fb.1",
+ .enable = exynos5_clk_ip_disp1_ctrl,
+ .ctrlbit = (1 << 0),
+};
+
struct clk *exynos5_clkset_group_list[] = {
[0] = &clk_ext_xtal_mux,
[1] = NULL,
@@ -1006,8 +1090,8 @@ static struct clksrc_clk exynos5_clk_sclk_uart3 = {
static struct clksrc_clk exynos5_clk_sclk_mmc0 = {
.clk = {
- .name = "sclk_mmc",
- .devname = "exynos4-sdhci.0",
+ .name = "ciu", /* card interface unit clock */
+ .devname = "dw_mmc.0",
.parent = &exynos5_clk_dout_mmc0.clk,
.enable = exynos5_clksrc_mask_fsys_ctrl,
.ctrlbit = (1 << 0),
@@ -1017,8 +1101,8 @@ static struct clksrc_clk exynos5_clk_sclk_mmc0 = {
static struct clksrc_clk exynos5_clk_sclk_mmc1 = {
.clk = {
- .name = "sclk_mmc",
- .devname = "exynos4-sdhci.1",
+ .name = "ciu",
+ .devname = "dw_mmc.1",
.parent = &exynos5_clk_dout_mmc1.clk,
.enable = exynos5_clksrc_mask_fsys_ctrl,
.ctrlbit = (1 << 4),
@@ -1028,8 +1112,8 @@ static struct clksrc_clk exynos5_clk_sclk_mmc1 = {
static struct clksrc_clk exynos5_clk_sclk_mmc2 = {
.clk = {
- .name = "sclk_mmc",
- .devname = "exynos4-sdhci.2",
+ .name = "ciu",
+ .devname = "dw_mmc.2",
.parent = &exynos5_clk_dout_mmc2.clk,
.enable = exynos5_clksrc_mask_fsys_ctrl,
.ctrlbit = (1 << 8),
@@ -1039,8 +1123,8 @@ static struct clksrc_clk exynos5_clk_sclk_mmc2 = {
static struct clksrc_clk exynos5_clk_sclk_mmc3 = {
.clk = {
- .name = "sclk_mmc",
- .devname = "exynos4-sdhci.3",
+ .name = "ciu",
+ .devname = "dw_mmc.3",
.parent = &exynos5_clk_dout_mmc3.clk,
.enable = exynos5_clksrc_mask_fsys_ctrl,
.ctrlbit = (1 << 12),
@@ -1111,27 +1195,21 @@ static struct clksrc_clk exynos5_clk_sclk_spi2 = {
.reg_div = { .reg = EXYNOS5_CLKDIV_PERIC2, .shift = 8, .size = 8 },
};
+struct clksrc_clk exynos5_clk_sclk_fimd1 = {
+ .clk = {
+ .name = "sclk_fimd",
+ .devname = "exynos5-fb.1",
+ .enable = exynos5_clksrc_mask_disp1_0_ctrl,
+ .ctrlbit = (1 << 0),
+ },
+ .sources = &exynos5_clkset_group,
+ .reg_src = { .reg = EXYNOS5_CLKSRC_DISP1_0, .shift = 0, .size = 4 },
+ .reg_div = { .reg = EXYNOS5_CLKDIV_DISP1_0, .shift = 0, .size = 4 },
+};
+
static struct clksrc_clk exynos5_clksrcs[] = {
{
.clk = {
- .name = "sclk_dwmci",
- .parent = &exynos5_clk_dout_mmc4.clk,
- .enable = exynos5_clksrc_mask_fsys_ctrl,
- .ctrlbit = (1 << 16),
- },
- .reg_div = { .reg = EXYNOS5_CLKDIV_FSYS3, .shift = 8, .size = 8 },
- }, {
- .clk = {
- .name = "sclk_fimd",
- .devname = "s3cfb.1",
- .enable = exynos5_clksrc_mask_disp1_0_ctrl,
- .ctrlbit = (1 << 0),
- },
- .sources = &exynos5_clkset_group,
- .reg_src = { .reg = EXYNOS5_CLKSRC_DISP1_0, .shift = 0, .size = 4 },
- .reg_div = { .reg = EXYNOS5_CLKDIV_DISP1_0, .shift = 0, .size = 4 },
- }, {
- .clk = {
.name = "aclk_266_gscl",
},
.sources = &clk_src_gscl_266,
@@ -1216,6 +1294,10 @@ static struct clksrc_clk *exynos5_sysclks[] = {
&exynos5_clk_aclk_266,
&exynos5_clk_aclk_200,
&exynos5_clk_aclk_166,
+ &exynos5_clk_aclk_300_gscl,
+ &exynos5_clk_mout_aclk_300_gscl,
+ &exynos5_clk_mout_aclk_300_gscl_mid,
+ &exynos5_clk_mout_aclk_300_gscl_mid1,
&exynos5_clk_aclk_66_pre,
&exynos5_clk_aclk_66,
&exynos5_clk_dout_mmc0,
@@ -1231,12 +1313,14 @@ static struct clksrc_clk *exynos5_sysclks[] = {
&exynos5_clk_mdout_spi0,
&exynos5_clk_mdout_spi1,
&exynos5_clk_mdout_spi2,
+ &exynos5_clk_sclk_fimd1,
};
static struct clk *exynos5_clk_cdev[] = {
&exynos5_clk_pdma0,
&exynos5_clk_pdma1,
&exynos5_clk_mdma1,
+ &exynos5_clk_fimd1,
};
static struct clksrc_clk *exynos5_clksrc_cdev[] = {
@@ -1265,6 +1349,7 @@ static struct clk_lookup exynos5_clk_lookup[] = {
CLKDEV_INIT("dma-pl330.0", "apb_pclk", &exynos5_clk_pdma0),
CLKDEV_INIT("dma-pl330.1", "apb_pclk", &exynos5_clk_pdma1),
CLKDEV_INIT("dma-pl330.2", "apb_pclk", &exynos5_clk_mdma1),
+ CLKDEV_INIT("exynos5-fb.1", "lcd", &exynos5_clk_fimd1),
};
static unsigned long exynos5_epll_get_rate(struct clk *clk)
diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c
index 4eb39cdf75ea..715b690e5009 100644
--- a/arch/arm/mach-exynos/common.c
+++ b/arch/arm/mach-exynos/common.c
@@ -980,6 +980,32 @@ static int __init exynos_init_irq_eint(void)
{
int irq;
+#ifdef CONFIG_PINCTRL_SAMSUNG
+ /*
+ * The Samsung pinctrl driver provides an integrated gpio/pinmux/pinconf
+ * functionality along with support for external gpio and wakeup
+ * interrupts. If the samsung pinctrl driver is enabled and includes
+ * the wakeup interrupt support, then the setting up external wakeup
+ * interrupts here can be skipped. This check here is temporary to
+ * allow exynos4 platforms that do not use Samsung pinctrl driver to
+ * co-exist with platforms that do. When all of the Samsung Exynos4
+ * platforms switch over to using the pinctrl driver, the wakeup
+ * interrupt support code here can be completely removed.
+ */
+ struct device_node *pctrl_np, *wkup_np;
+ const char *pctrl_compat = "samsung,pinctrl-exynos4210";
+ const char *wkup_compat = "samsung,exynos4210-wakeup-eint";
+
+ for_each_compatible_node(pctrl_np, NULL, pctrl_compat) {
+ if (of_device_is_available(pctrl_np)) {
+ wkup_np = of_find_compatible_node(pctrl_np, NULL,
+ wkup_compat);
+ if (wkup_np)
+ return -ENODEV;
+ }
+ }
+#endif
+
if (soc_is_exynos5250())
exynos_eint_base = ioremap(EXYNOS5_PA_GPIO1, SZ_4K);
else
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
index 9d1f3ac86db2..8480849affb9 100644
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ b/arch/arm/mach-exynos/include/mach/map.h
@@ -89,7 +89,7 @@
#define EXYNOS4_PA_L2CC 0x10502000
#define EXYNOS4_PA_MDMA0 0x10810000
-#define EXYNOS4_PA_MDMA1 0x12840000
+#define EXYNOS4_PA_MDMA1 0x12850000
#define EXYNOS4_PA_PDMA0 0x12680000
#define EXYNOS4_PA_PDMA1 0x12690000
#define EXYNOS5_PA_MDMA0 0x10800000
@@ -121,6 +121,11 @@
#define EXYNOS4_PA_SYSMMU_MFC_L 0x13620000
#define EXYNOS4_PA_SYSMMU_MFC_R 0x13630000
+#define EXYNOS5_PA_GSC0 0x13E00000
+#define EXYNOS5_PA_GSC1 0x13E10000
+#define EXYNOS5_PA_GSC2 0x13E20000
+#define EXYNOS5_PA_GSC3 0x13E30000
+
#define EXYNOS5_PA_SYSMMU_MDMA1 0x10A40000
#define EXYNOS5_PA_SYSMMU_SSS 0x10A50000
#define EXYNOS5_PA_SYSMMU_2D 0x10A60000
@@ -172,6 +177,10 @@
#define EXYNOS4_PA_HSMMC(x) (0x12510000 + ((x) * 0x10000))
#define EXYNOS4_PA_DWMCI 0x12550000
+#define EXYNOS5_PA_DWMCI0 0x12200000
+#define EXYNOS5_PA_DWMCI1 0x12210000
+#define EXYNOS5_PA_DWMCI2 0x12220000
+#define EXYNOS5_PA_DWMCI3 0x12230000
#define EXYNOS4_PA_HSOTG 0x12480000
#define EXYNOS4_PA_USB_HSPHY 0x125B0000
diff --git a/arch/arm/mach-exynos/include/mach/sysmmu.h b/arch/arm/mach-exynos/include/mach/sysmmu.h
index 998daf2add92..88a4543b0001 100644
--- a/arch/arm/mach-exynos/include/mach/sysmmu.h
+++ b/arch/arm/mach-exynos/include/mach/sysmmu.h
@@ -58,7 +58,7 @@ static inline void platform_set_sysmmu(
#endif
#else /* !CONFIG_EXYNOS_DEV_SYSMMU */
-#define platform_set_sysmmu(dev, sysmmu) do { } while (0)
+#define platform_set_sysmmu(sysmmu, dev) do { } while (0)
#endif
#define SYSMMU_CLOCK_DEVNAME(ipname, id) (SYSMMU_DEVNAME_BASE "." #id)
diff --git a/arch/arm/mach-exynos/mach-exynos4-dt.c b/arch/arm/mach-exynos/mach-exynos4-dt.c
index b2b5d5faa748..e58d786faf78 100644
--- a/arch/arm/mach-exynos/mach-exynos4-dt.c
+++ b/arch/arm/mach-exynos/mach-exynos4-dt.c
@@ -1,5 +1,5 @@
/*
- * Samsung's Exynos4210 flattened device tree enabled machine
+ * Samsung's EXYNOS4 flattened device tree enabled machine
*
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
* http://www.samsung.com
@@ -36,7 +36,7 @@
* at some point, the drivers should be capable of parsing all the platform
* data from the device tree.
*/
-static const struct of_dev_auxdata exynos4210_auxdata_lookup[] __initconst = {
+static const struct of_dev_auxdata exynos4_auxdata_lookup[] __initconst = {
OF_DEV_AUXDATA("samsung,exynos4210-uart", EXYNOS4_PA_UART0,
"exynos4210-uart.0", NULL),
OF_DEV_AUXDATA("samsung,exynos4210-uart", EXYNOS4_PA_UART1,
@@ -55,6 +55,20 @@ static const struct of_dev_auxdata exynos4210_auxdata_lookup[] __initconst = {
"exynos4-sdhci.3", NULL),
OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS4_PA_IIC(0),
"s3c2440-i2c.0", NULL),
+ OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS4_PA_IIC(1),
+ "s3c2440-i2c.1", NULL),
+ OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS4_PA_IIC(2),
+ "s3c2440-i2c.2", NULL),
+ OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS4_PA_IIC(3),
+ "s3c2440-i2c.3", NULL),
+ OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS4_PA_IIC(4),
+ "s3c2440-i2c.4", NULL),
+ OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS4_PA_IIC(5),
+ "s3c2440-i2c.5", NULL),
+ OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS4_PA_IIC(6),
+ "s3c2440-i2c.6", NULL),
+ OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS4_PA_IIC(7),
+ "s3c2440-i2c.7", NULL),
OF_DEV_AUXDATA("samsung,exynos4210-spi", EXYNOS4_PA_SPI0,
"exynos4210-spi.0", NULL),
OF_DEV_AUXDATA("samsung,exynos4210-spi", EXYNOS4_PA_SPI1,
@@ -66,19 +80,19 @@ static const struct of_dev_auxdata exynos4210_auxdata_lookup[] __initconst = {
{},
};
-static void __init exynos4210_dt_map_io(void)
+static void __init exynos4_dt_map_io(void)
{
exynos_init_io(NULL, 0);
s3c24xx_init_clocks(24000000);
}
-static void __init exynos4210_dt_machine_init(void)
+static void __init exynos4_dt_machine_init(void)
{
of_platform_populate(NULL, of_default_bus_match_table,
- exynos4210_auxdata_lookup, NULL);
+ exynos4_auxdata_lookup, NULL);
}
-static char const *exynos4210_dt_compat[] __initdata = {
+static char const *exynos4_dt_compat[] __initdata = {
"samsung,exynos4210",
NULL
};
@@ -86,11 +100,11 @@ static char const *exynos4210_dt_compat[] __initdata = {
DT_MACHINE_START(EXYNOS4210_DT, "Samsung Exynos4 (Flattened Device Tree)")
/* Maintainer: Thomas Abraham <thomas.abraham@linaro.org> */
.init_irq = exynos4_init_irq,
- .map_io = exynos4210_dt_map_io,
+ .map_io = exynos4_dt_map_io,
.handle_irq = gic_handle_irq,
- .init_machine = exynos4210_dt_machine_init,
+ .init_machine = exynos4_dt_machine_init,
.init_late = exynos_init_late,
.timer = &exynos4_timer,
- .dt_compat = exynos4210_dt_compat,
+ .dt_compat = exynos4_dt_compat,
.restart = exynos4_restart,
MACHINE_END
diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c
index 8833060f77e9..db1cd8eacf28 100644
--- a/arch/arm/mach-exynos/mach-exynos5-dt.c
+++ b/arch/arm/mach-exynos/mach-exynos5-dt.c
@@ -47,6 +47,14 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = {
"s3c2440-i2c.0", NULL),
OF_DEV_AUXDATA("samsung,s3c2440-i2c", EXYNOS5_PA_IIC(1),
"s3c2440-i2c.1", NULL),
+ OF_DEV_AUXDATA("samsung,exynos5250-dw-mshc", EXYNOS5_PA_DWMCI0,
+ "dw_mmc.0", NULL),
+ OF_DEV_AUXDATA("samsung,exynos5250-dw-mshc", EXYNOS5_PA_DWMCI1,
+ "dw_mmc.1", NULL),
+ OF_DEV_AUXDATA("samsung,exynos5250-dw-mshc", EXYNOS5_PA_DWMCI2,
+ "dw_mmc.2", NULL),
+ OF_DEV_AUXDATA("samsung,exynos5250-dw-mshc", EXYNOS5_PA_DWMCI3,
+ "dw_mmc.3", NULL),
OF_DEV_AUXDATA("samsung,exynos4210-spi", EXYNOS5_PA_SPI0,
"exynos4210-spi.0", NULL),
OF_DEV_AUXDATA("samsung,exynos4210-spi", EXYNOS5_PA_SPI1,
@@ -56,6 +64,14 @@ static const struct of_dev_auxdata exynos5250_auxdata_lookup[] __initconst = {
OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_PDMA0, "dma-pl330.0", NULL),
OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_PDMA1, "dma-pl330.1", NULL),
OF_DEV_AUXDATA("arm,pl330", EXYNOS5_PA_MDMA1, "dma-pl330.2", NULL),
+ OF_DEV_AUXDATA("samsung,exynos5-gsc", EXYNOS5_PA_GSC0,
+ "exynos-gsc.0", NULL),
+ OF_DEV_AUXDATA("samsung,exynos5-gsc", EXYNOS5_PA_GSC1,
+ "exynos-gsc.1", NULL),
+ OF_DEV_AUXDATA("samsung,exynos5-gsc", EXYNOS5_PA_GSC2,
+ "exynos-gsc.2", NULL),
+ OF_DEV_AUXDATA("samsung,exynos5-gsc", EXYNOS5_PA_GSC3,
+ "exynos-gsc.3", NULL),
{},
};
diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c
index fc23f74ade81..67b50bb89c0f 100644
--- a/arch/arm/mach-exynos/mach-origen.c
+++ b/arch/arm/mach-exynos/mach-origen.c
@@ -15,6 +15,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/input.h>
+#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/gpio_keys.h>
#include <linux/i2c.h>
@@ -614,6 +615,10 @@ static struct platform_device origen_lcd_hv070wsa = {
.dev.platform_data = &origen_lcd_hv070wsa_data,
};
+static struct pwm_lookup origen_pwm_lookup[] = {
+ PWM_LOOKUP("s3c24xx-pwm.0", 0, "pwm-backlight.0", NULL),
+};
+
#ifdef CONFIG_DRM_EXYNOS
static struct exynos_drm_fimd_pdata drm_fimd_pdata = {
.panel = {
@@ -798,6 +803,7 @@ static void __init origen_machine_init(void)
platform_add_devices(origen_devices, ARRAY_SIZE(origen_devices));
+ pwm_add_table(origen_pwm_lookup, ARRAY_SIZE(origen_pwm_lookup));
samsung_bl_set(&origen_bl_gpio_info, &origen_bl_data);
origen_bt_setup();
diff --git a/arch/arm/mach-exynos/mach-smdk4x12.c b/arch/arm/mach-exynos/mach-smdk4x12.c
index 589f1db140f0..7a265d1a82d3 100644
--- a/arch/arm/mach-exynos/mach-smdk4x12.c
+++ b/arch/arm/mach-exynos/mach-smdk4x12.c
@@ -17,6 +17,7 @@
#include <linux/mfd/max8997.h>
#include <linux/mmc/host.h>
#include <linux/platform_device.h>
+#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/regulator/machine.h>
#include <linux/serial_core.h>
@@ -222,6 +223,10 @@ static struct platform_pwm_backlight_data smdk4x12_bl_data = {
.pwm_period_ns = 1000,
};
+static struct pwm_lookup smdk4x12_pwm_lookup[] = {
+ PWM_LOOKUP("s3c24xx-pwm.1", 0, "pwm-backlight.0", NULL),
+};
+
static uint32_t smdk4x12_keymap[] __initdata = {
/* KEY(row, col, keycode) */
KEY(1, 3, KEY_1), KEY(1, 4, KEY_2), KEY(1, 5, KEY_3),
@@ -349,6 +354,7 @@ static void __init smdk4x12_machine_init(void)
ARRAY_SIZE(smdk4x12_i2c_devs7));
samsung_bl_set(&smdk4x12_bl_gpio_info, &smdk4x12_bl_data);
+ pwm_add_table(smdk4x12_pwm_lookup, ARRAY_SIZE(smdk4x12_pwm_lookup));
samsung_keypad_set_platdata(&smdk4x12_keypad_data);
diff --git a/arch/arm/mach-exynos/mach-smdkv310.c b/arch/arm/mach-exynos/mach-smdkv310.c
index 6e52cbd0b3e0..c15d2238ceb0 100644
--- a/arch/arm/mach-exynos/mach-smdkv310.c
+++ b/arch/arm/mach-exynos/mach-smdkv310.c
@@ -18,6 +18,7 @@
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/input.h>
+#include <linux/pwm.h>
#include <linux/pwm_backlight.h>
#include <linux/platform_data/s3c-hsotg.h>
@@ -360,6 +361,10 @@ static struct i2c_board_info hdmiphy_info = {
I2C_BOARD_INFO("hdmiphy-exynos4210", 0x38),
};
+static struct pwm_lookup smdkv310_pwm_lookup[] = {
+ PWM_LOOKUP("s3c24xx-pwm.1", 0, "pwm-backlight.0", NULL),
+};
+
static void s5p_tv_setup(void)
{
/* direct HPD to HDMI chip */
@@ -399,6 +404,8 @@ static void __init smdkv310_machine_init(void)
samsung_keypad_set_platdata(&smdkv310_keypad_data);
samsung_bl_set(&smdkv310_bl_gpio_info, &smdkv310_bl_data);
+ pwm_add_table(smdkv310_pwm_lookup, ARRAY_SIZE(smdkv310_pwm_lookup));
+
#ifdef CONFIG_DRM_EXYNOS
s5p_device_fimd0.dev.platform_data = &drm_fimd_pdata;
exynos4_fimd0_gpio_setup_24bpp();
diff --git a/arch/arm/mach-footbridge/Makefile b/arch/arm/mach-footbridge/Makefile
index 3afb1b25946f..0b64dd430d61 100644
--- a/arch/arm/mach-footbridge/Makefile
+++ b/arch/arm/mach-footbridge/Makefile
@@ -14,15 +14,11 @@ pci-$(CONFIG_ARCH_EBSA285_HOST) += ebsa285-pci.o
pci-$(CONFIG_ARCH_NETWINDER) += netwinder-pci.o
pci-$(CONFIG_ARCH_PERSONAL_SERVER) += personal-pci.o
-leds-$(CONFIG_ARCH_EBSA285) += ebsa285-leds.o
-leds-$(CONFIG_ARCH_NETWINDER) += netwinder-leds.o
-
obj-$(CONFIG_ARCH_CATS) += cats-hw.o isa-timer.o
obj-$(CONFIG_ARCH_EBSA285) += ebsa285.o dc21285-timer.o
obj-$(CONFIG_ARCH_NETWINDER) += netwinder-hw.o isa-timer.o
obj-$(CONFIG_ARCH_PERSONAL_SERVER) += personal.o dc21285-timer.o
obj-$(CONFIG_PCI) +=$(pci-y)
-obj-$(CONFIG_LEDS) +=$(leds-y)
obj-$(CONFIG_ISA) += isa.o isa-rtc.o
diff --git a/arch/arm/mach-footbridge/ebsa285-leds.c b/arch/arm/mach-footbridge/ebsa285-leds.c
deleted file mode 100644
index 5bd266754b95..000000000000
--- a/arch/arm/mach-footbridge/ebsa285-leds.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * linux/arch/arm/mach-footbridge/ebsa285-leds.c
- *
- * Copyright (C) 1998-1999 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.
- * EBSA-285 control routines.
- *
- * The EBSA-285 uses the leds as follows:
- * - Green - toggles state every 50 timer interrupts
- * - Amber - On if system is not idle
- * - Red - currently unused
- *
- * Changelog:
- * 02-05-1999 RMK Various cleanups
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-#include <asm/mach-types.h>
-
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
-static char led_state;
-static char hw_led_state;
-
-static DEFINE_SPINLOCK(leds_lock);
-
-static void ebsa285_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&leds_lock, flags);
-
- switch (evt) {
- case led_start:
- hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN;
-#ifndef CONFIG_LEDS_CPU
- hw_led_state |= XBUS_LED_AMBER;
-#endif
- led_state |= LED_STATE_ENABLED;
- break;
-
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN | XBUS_LED_AMBER;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = XBUS_LED_RED | XBUS_LED_GREEN | XBUS_LED_AMBER;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state ^= XBUS_LED_GREEN;
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= XBUS_LED_AMBER;
- break;
-
- case led_idle_end:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~XBUS_LED_AMBER;
- break;
-#endif
-
- case led_halted:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~XBUS_LED_RED;
- break;
-
- case led_green_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~XBUS_LED_GREEN;
- break;
-
- case led_green_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= XBUS_LED_GREEN;
- break;
-
- case led_amber_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~XBUS_LED_AMBER;
- break;
-
- case led_amber_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= XBUS_LED_AMBER;
- break;
-
- case led_red_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~XBUS_LED_RED;
- break;
-
- case led_red_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= XBUS_LED_RED;
- break;
-
- default:
- break;
- }
-
- if (led_state & LED_STATE_ENABLED)
- *XBUS_LEDS = hw_led_state;
-
- spin_unlock_irqrestore(&leds_lock, flags);
-}
-
-static int __init leds_init(void)
-{
- if (machine_is_ebsa285())
- leds_event = ebsa285_leds_event;
-
- leds_event(led_start);
-
- return 0;
-}
-
-__initcall(leds_init);
diff --git a/arch/arm/mach-footbridge/ebsa285.c b/arch/arm/mach-footbridge/ebsa285.c
index 27716a7e5fc1..b09551ef89ca 100644
--- a/arch/arm/mach-footbridge/ebsa285.c
+++ b/arch/arm/mach-footbridge/ebsa285.c
@@ -5,6 +5,8 @@
*/
#include <linux/init.h>
#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
#include <asm/hardware/dec21285.h>
#include <asm/mach-types.h>
@@ -13,6 +15,85 @@
#include "common.h"
+/* LEDs */
+#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
+struct ebsa285_led {
+ struct led_classdev cdev;
+ u8 mask;
+};
+
+/*
+ * The triggers lines up below will only be used if the
+ * LED triggers are compiled in.
+ */
+static const struct {
+ const char *name;
+ const char *trigger;
+} ebsa285_leds[] = {
+ { "ebsa285:amber", "heartbeat", },
+ { "ebsa285:green", "cpu0", },
+ { "ebsa285:red",},
+};
+
+static void ebsa285_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
+{
+ struct ebsa285_led *led = container_of(cdev,
+ struct ebsa285_led, cdev);
+
+ if (b != LED_OFF)
+ *XBUS_LEDS |= led->mask;
+ else
+ *XBUS_LEDS &= ~led->mask;
+}
+
+static enum led_brightness ebsa285_led_get(struct led_classdev *cdev)
+{
+ struct ebsa285_led *led = container_of(cdev,
+ struct ebsa285_led, cdev);
+
+ return (*XBUS_LEDS & led->mask) ? LED_FULL : LED_OFF;
+}
+
+static int __init ebsa285_leds_init(void)
+{
+ int i;
+
+ if (machine_is_ebsa285())
+ return -ENODEV;
+
+ /* 3 LEDS All ON */
+ *XBUS_LEDS |= XBUS_LED_AMBER | XBUS_LED_GREEN | XBUS_LED_RED;
+
+ for (i = 0; i < ARRAY_SIZE(ebsa285_leds); i++) {
+ struct ebsa285_led *led;
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ break;
+
+ led->cdev.name = ebsa285_leds[i].name;
+ led->cdev.brightness_set = ebsa285_led_set;
+ led->cdev.brightness_get = ebsa285_led_get;
+ led->cdev.default_trigger = ebsa285_leds[i].trigger;
+ led->mask = BIT(i);
+
+ if (led_classdev_register(NULL, &led->cdev) < 0) {
+ kfree(led);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Since we may have triggers on any subsystem, defer registration
+ * until after subsystem_init.
+ */
+fs_initcall(ebsa285_leds_init);
+#endif
+
MACHINE_START(EBSA285, "EBSA285")
/* Maintainer: Russell King */
.atag_offset = 0x100,
diff --git a/arch/arm/mach-footbridge/netwinder-hw.c b/arch/arm/mach-footbridge/netwinder-hw.c
index cac9f67e7da7..d2d14339c6c4 100644
--- a/arch/arm/mach-footbridge/netwinder-hw.c
+++ b/arch/arm/mach-footbridge/netwinder-hw.c
@@ -12,9 +12,10 @@
#include <linux/init.h>
#include <linux/io.h>
#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
#include <asm/hardware/dec21285.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include <asm/setup.h>
#include <asm/system_misc.h>
@@ -27,13 +28,6 @@
#define GP1_IO_BASE 0x338
#define GP2_IO_BASE 0x33a
-
-#ifdef CONFIG_LEDS
-#define DEFAULT_LEDS 0
-#else
-#define DEFAULT_LEDS GPIO_GREEN_LED
-#endif
-
/*
* Winbond WB83977F accessibility stuff
*/
@@ -611,15 +605,9 @@ static void __init rwa010_init(void)
static int __init nw_hw_init(void)
{
if (machine_is_netwinder()) {
- unsigned long flags;
-
wb977_init();
cpld_init();
rwa010_init();
-
- raw_spin_lock_irqsave(&nw_gpio_lock, flags);
- nw_gpio_modify_op(GPIO_RED_LED|GPIO_GREEN_LED, DEFAULT_LEDS);
- raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
}
return 0;
}
@@ -672,6 +660,102 @@ static void netwinder_restart(char mode, const char *cmd)
}
}
+/* LEDs */
+#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
+struct netwinder_led {
+ struct led_classdev cdev;
+ u8 mask;
+};
+
+/*
+ * The triggers lines up below will only be used if the
+ * LED triggers are compiled in.
+ */
+static const struct {
+ const char *name;
+ const char *trigger;
+} netwinder_leds[] = {
+ { "netwinder:green", "heartbeat", },
+ { "netwinder:red", "cpu0", },
+};
+
+/*
+ * The LED control in Netwinder is reversed:
+ * - setting bit means turn off LED
+ * - clearing bit means turn on LED
+ */
+static void netwinder_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
+{
+ struct netwinder_led *led = container_of(cdev,
+ struct netwinder_led, cdev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ reg = nw_gpio_read();
+ if (b != LED_OFF)
+ reg &= ~led->mask;
+ else
+ reg |= led->mask;
+ nw_gpio_modify_op(led->mask, reg);
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
+}
+
+static enum led_brightness netwinder_led_get(struct led_classdev *cdev)
+{
+ struct netwinder_led *led = container_of(cdev,
+ struct netwinder_led, cdev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&nw_gpio_lock, flags);
+ reg = nw_gpio_read();
+ spin_unlock_irqrestore(&nw_gpio_lock, flags);
+
+ return (reg & led->mask) ? LED_OFF : LED_FULL;
+}
+
+static int __init netwinder_leds_init(void)
+{
+ int i;
+
+ if (!machine_is_netwinder())
+ return -ENODEV;
+
+ for (i = 0; i < ARRAY_SIZE(netwinder_leds); i++) {
+ struct netwinder_led *led;
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ break;
+
+ led->cdev.name = netwinder_leds[i].name;
+ led->cdev.brightness_set = netwinder_led_set;
+ led->cdev.brightness_get = netwinder_led_get;
+ led->cdev.default_trigger = netwinder_leds[i].trigger;
+
+ if (i == 0)
+ led->mask = GPIO_GREEN_LED;
+ else
+ led->mask = GPIO_RED_LED;
+
+ if (led_classdev_register(NULL, &led->cdev) < 0) {
+ kfree(led);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Since we may have triggers on any subsystem, defer registration
+ * until after subsystem_init.
+ */
+fs_initcall(netwinder_leds_init);
+#endif
+
MACHINE_START(NETWINDER, "Rebel-NetWinder")
/* Maintainer: Russell King/Rebel.com */
.atag_offset = 0x100,
diff --git a/arch/arm/mach-footbridge/netwinder-leds.c b/arch/arm/mach-footbridge/netwinder-leds.c
deleted file mode 100644
index 5a2bd89cbdca..000000000000
--- a/arch/arm/mach-footbridge/netwinder-leds.c
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * linux/arch/arm/mach-footbridge/netwinder-leds.c
- *
- * Copyright (C) 1998-1999 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.
- *
- * NetWinder LED control routines.
- *
- * The Netwinder uses the leds as follows:
- * - Green - toggles state every 50 timer interrupts
- * - Red - On if the system is not idle
- *
- * Changelog:
- * 02-05-1999 RMK Various cleanups
- */
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-#include <asm/mach-types.h>
-
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
-static char led_state;
-static char hw_led_state;
-
-static DEFINE_RAW_SPINLOCK(leds_lock);
-
-static void netwinder_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&leds_lock, flags);
-
- switch (evt) {
- case led_start:
- led_state |= LED_STATE_ENABLED;
- hw_led_state = GPIO_GREEN_LED;
- break;
-
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = 0;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = 0;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state ^= GPIO_GREEN_LED;
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~GPIO_RED_LED;
- break;
-
- case led_idle_end:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= GPIO_RED_LED;
- break;
-#endif
-
- case led_halted:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= GPIO_RED_LED;
- break;
-
- case led_green_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= GPIO_GREEN_LED;
- break;
-
- case led_green_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~GPIO_GREEN_LED;
- break;
-
- case led_amber_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= GPIO_GREEN_LED | GPIO_RED_LED;
- break;
-
- case led_amber_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~(GPIO_GREEN_LED | GPIO_RED_LED);
- break;
-
- case led_red_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= GPIO_RED_LED;
- break;
-
- case led_red_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~GPIO_RED_LED;
- break;
-
- default:
- break;
- }
-
- raw_spin_unlock_irqrestore(&leds_lock, flags);
-
- if (led_state & LED_STATE_ENABLED) {
- raw_spin_lock_irqsave(&nw_gpio_lock, flags);
- nw_gpio_modify_op(GPIO_RED_LED | GPIO_GREEN_LED, hw_led_state);
- raw_spin_unlock_irqrestore(&nw_gpio_lock, flags);
- }
-}
-
-static int __init leds_init(void)
-{
- if (machine_is_netwinder())
- leds_event = netwinder_leds_event;
-
- leds_event(led_start);
-
- return 0;
-}
-
-__initcall(leds_init);
diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
index 7ca5fe45945f..3a2042fb9712 100644
--- a/arch/arm/mach-imx/Kconfig
+++ b/arch/arm/mach-imx/Kconfig
@@ -298,6 +298,7 @@ config MACH_MX27_3DS
select IMX_HAVE_PLATFORM_IMX_FB
select IMX_HAVE_PLATFORM_IMX_I2C
select IMX_HAVE_PLATFORM_IMX_KEYPAD
+ select IMX_HAVE_PLATFORM_IMX_SSI
select IMX_HAVE_PLATFORM_IMX_UART
select IMX_HAVE_PLATFORM_MX2_CAMERA
select IMX_HAVE_PLATFORM_MXC_EHCI
diff --git a/arch/arm/mach-imx/clk-imx25.c b/arch/arm/mach-imx/clk-imx25.c
index 4431a62fff5b..d20d4795f4ea 100644
--- a/arch/arm/mach-imx/clk-imx25.c
+++ b/arch/arm/mach-imx/clk-imx25.c
@@ -241,6 +241,6 @@ int __init mx25_clocks_init(void)
clk_register_clkdev(clk[sdma_ahb], "ahb", "imx35-sdma");
clk_register_clkdev(clk[iim_ipg], "iim", NULL);
- mxc_timer_init(MX25_IO_ADDRESS(MX25_GPT1_BASE_ADDR), 54);
+ mxc_timer_init(MX25_IO_ADDRESS(MX25_GPT1_BASE_ADDR), MX25_INT_GPT1);
return 0;
}
diff --git a/arch/arm/mach-imx/mach-armadillo5x0.c b/arch/arm/mach-imx/mach-armadillo5x0.c
index 2c6ab3273f9e..5985ed1b8c98 100644
--- a/arch/arm/mach-imx/mach-armadillo5x0.c
+++ b/arch/arm/mach-imx/mach-armadillo5x0.c
@@ -526,7 +526,8 @@ static void __init armadillo5x0_init(void)
imx31_add_mxc_nand(&armadillo5x0_nand_board_info);
/* set NAND page size to 2k if not configured via boot mode pins */
- __raw_writel(__raw_readl(MXC_CCM_RCSR) | (1 << 30), MXC_CCM_RCSR);
+ __raw_writel(__raw_readl(mx3_ccm_base + MXC_CCM_RCSR) |
+ (1 << 30), mx3_ccm_base + MXC_CCM_RCSR);
/* RTC */
/* Get RTC IRQ and register the chip */
diff --git a/arch/arm/mach-imx/mach-kzm_arm11_01.c b/arch/arm/mach-imx/mach-kzm_arm11_01.c
index 8dc9d3edf17a..0330078ff788 100644
--- a/arch/arm/mach-imx/mach-kzm_arm11_01.c
+++ b/arch/arm/mach-imx/mach-kzm_arm11_01.c
@@ -258,13 +258,13 @@ static void __init kzm_board_init(void)
*/
static struct map_desc kzm_io_desc[] __initdata = {
{
- .virtual = MX31_CS4_BASE_ADDR_VIRT,
+ .virtual = (unsigned long)MX31_CS4_BASE_ADDR_VIRT,
.pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR),
.length = MX31_CS4_SIZE,
.type = MT_DEVICE
},
{
- .virtual = MX31_CS5_BASE_ADDR_VIRT,
+ .virtual = (unsigned long)MX31_CS5_BASE_ADDR_VIRT,
.pfn = __phys_to_pfn(MX31_CS5_BASE_ADDR),
.length = MX31_CS5_SIZE,
.type = MT_DEVICE
diff --git a/arch/arm/mach-imx/mach-mx27_3ds.c b/arch/arm/mach-imx/mach-mx27_3ds.c
index 58c24c1a7ab7..05996f39005c 100644
--- a/arch/arm/mach-imx/mach-mx27_3ds.c
+++ b/arch/arm/mach-imx/mach-mx27_3ds.c
@@ -158,6 +158,11 @@ static const int mx27pdk_pins[] __initconst = {
PB21_PF_CSI_HSYNC,
CSI_PWRDWN | GPIO_GPIO | GPIO_OUT,
CSI_RESET | GPIO_GPIO | GPIO_OUT,
+ /* SSI4 */
+ PC16_PF_SSI4_FS,
+ PC17_PF_SSI4_RXD,
+ PC18_PF_SSI4_TXD,
+ PC19_PF_SSI4_CLK,
};
static struct gpio mx27_3ds_camera_gpios[] = {
@@ -329,13 +334,24 @@ static struct mc13xxx_regulator_init_data mx27_3ds_regulators[] = {
};
/* MC13783 */
+static struct mc13xxx_codec_platform_data mx27_3ds_codec = {
+ .dac_ssi_port = MC13783_SSI1_PORT,
+ .adc_ssi_port = MC13783_SSI1_PORT,
+};
+
static struct mc13xxx_platform_data mc13783_pdata = {
.regulators = {
.regulators = mx27_3ds_regulators,
.num_regulators = ARRAY_SIZE(mx27_3ds_regulators),
},
- .flags = MC13XXX_USE_TOUCHSCREEN | MC13XXX_USE_RTC,
+ .flags = MC13XXX_USE_TOUCHSCREEN | MC13XXX_USE_RTC |
+ MC13XXX_USE_CODEC,
+ .codec = &mx27_3ds_codec,
+};
+
+static struct imx_ssi_platform_data mx27_3ds_ssi_pdata = {
+ .flags = IMX_SSI_DMA | IMX_SSI_NET,
};
/* SPI */
@@ -512,6 +528,9 @@ static void __init mx27pdk_init(void)
}
imx27_add_mx2_camera(&mx27_3ds_cam_pdata);
+ imx27_add_imx_ssi(0, &mx27_3ds_ssi_pdata);
+
+ imx_add_platform_device("imx_mc13783", 0, NULL, 0, NULL, 0);
}
static void __init mx27pdk_timer_init(void)
diff --git a/arch/arm/mach-imx/mach-mx31ads.c b/arch/arm/mach-imx/mach-mx31ads.c
index d37f4809c556..e774b07f48d3 100644
--- a/arch/arm/mach-imx/mach-mx31ads.c
+++ b/arch/arm/mach-imx/mach-mx31ads.c
@@ -540,7 +540,7 @@ static void __init mxc_init_audio(void)
*/
static struct map_desc mx31ads_io_desc[] __initdata = {
{
- .virtual = MX31_CS4_BASE_ADDR_VIRT,
+ .virtual = (unsigned long)MX31_CS4_BASE_ADDR_VIRT,
.pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR),
.length = CS4_CS8900_MMIO_START,
.type = MT_DEVICE
diff --git a/arch/arm/mach-imx/mach-mx31lite.c b/arch/arm/mach-imx/mach-mx31lite.c
index c8785b39eaed..ef57cff5abfb 100644
--- a/arch/arm/mach-imx/mach-mx31lite.c
+++ b/arch/arm/mach-imx/mach-mx31lite.c
@@ -207,7 +207,7 @@ static struct platform_device physmap_flash_device = {
*/
static struct map_desc mx31lite_io_desc[] __initdata = {
{
- .virtual = MX31_CS4_BASE_ADDR_VIRT,
+ .virtual = (unsigned long)MX31_CS4_BASE_ADDR_VIRT,
.pfn = __phys_to_pfn(MX31_CS4_BASE_ADDR),
.length = MX31_CS4_SIZE,
.type = MT_DEVICE
diff --git a/arch/arm/mach-integrator/Makefile b/arch/arm/mach-integrator/Makefile
index ebeef966e1f5..5521d18bf19a 100644
--- a/arch/arm/mach-integrator/Makefile
+++ b/arch/arm/mach-integrator/Makefile
@@ -4,11 +4,10 @@
# Object file lists.
-obj-y := core.o lm.o
+obj-y := core.o lm.o leds.o
obj-$(CONFIG_ARCH_INTEGRATOR_AP) += integrator_ap.o
obj-$(CONFIG_ARCH_INTEGRATOR_CP) += integrator_cp.o
-obj-$(CONFIG_LEDS) += leds.o
obj-$(CONFIG_PCI) += pci_v3.o pci.o
obj-$(CONFIG_CPU_FREQ_INTEGRATOR) += cpu.o
obj-$(CONFIG_INTEGRATOR_IMPD1) += impd1.o
diff --git a/arch/arm/mach-integrator/core.c b/arch/arm/mach-integrator/core.c
index 3fa6c51390da..dad3cb74ed31 100644
--- a/arch/arm/mach-integrator/core.c
+++ b/arch/arm/mach-integrator/core.c
@@ -28,7 +28,6 @@
#include <mach/cm.h>
#include <mach/irqs.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include <asm/mach/time.h>
#include <asm/pgtable.h>
@@ -95,8 +94,8 @@ arch_initcall(integrator_init);
* UART0 7 6
* UART1 5 4
*/
-#define SC_CTRLC IO_ADDRESS(INTEGRATOR_SC_CTRLC)
-#define SC_CTRLS IO_ADDRESS(INTEGRATOR_SC_CTRLS)
+#define SC_CTRLC __io_address(INTEGRATOR_SC_CTRLC)
+#define SC_CTRLS __io_address(INTEGRATOR_SC_CTRLS)
static void integrator_uart_set_mctrl(struct amba_device *dev, void __iomem *base, unsigned int mctrl)
{
@@ -128,8 +127,6 @@ static struct amba_pl010_data integrator_uart_data = {
.set_mctrl = integrator_uart_set_mctrl,
};
-#define CM_CTRL IO_ADDRESS(INTEGRATOR_HDR_CTRL)
-
static DEFINE_RAW_SPINLOCK(cm_lock);
/**
diff --git a/arch/arm/mach-integrator/cpu.c b/arch/arm/mach-integrator/cpu.c
index fbb457779895..590c192cdf4d 100644
--- a/arch/arm/mach-integrator/cpu.c
+++ b/arch/arm/mach-integrator/cpu.c
@@ -25,10 +25,10 @@
static struct cpufreq_driver integrator_driver;
-#define CM_ID IO_ADDRESS(INTEGRATOR_HDR_ID)
-#define CM_OSC IO_ADDRESS(INTEGRATOR_HDR_OSC)
-#define CM_STAT IO_ADDRESS(INTEGRATOR_HDR_STAT)
-#define CM_LOCK IO_ADDRESS(INTEGRATOR_HDR_LOCK)
+#define CM_ID __io_address(INTEGRATOR_HDR_ID)
+#define CM_OSC __io_address(INTEGRATOR_HDR_OSC)
+#define CM_STAT __io_address(INTEGRATOR_HDR_STAT)
+#define CM_LOCK __io_address(INTEGRATOR_HDR_LOCK)
static const struct icst_params lclk_params = {
.ref = 24000000,
diff --git a/arch/arm/mach-integrator/include/mach/cm.h b/arch/arm/mach-integrator/include/mach/cm.h
index 445d57adb043..1a78692e32a4 100644
--- a/arch/arm/mach-integrator/include/mach/cm.h
+++ b/arch/arm/mach-integrator/include/mach/cm.h
@@ -3,6 +3,8 @@
*/
void cm_control(u32, u32);
+#define CM_CTRL IO_ADDRESS(INTEGRATOR_HDR_CTRL)
+
#define CM_CTRL_LED (1 << 0)
#define CM_CTRL_nMBDET (1 << 1)
#define CM_CTRL_REMAP (1 << 2)
diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c
index fd3ef28d2c1a..2215d96cd735 100644
--- a/arch/arm/mach-integrator/integrator_ap.c
+++ b/arch/arm/mach-integrator/integrator_ap.c
@@ -134,17 +134,17 @@ static struct map_desc ap_io_desc[] __initdata = {
.length = SZ_4K,
.type = MT_DEVICE
}, {
- .virtual = PCI_MEMORY_VADDR,
+ .virtual = (unsigned long)PCI_MEMORY_VADDR,
.pfn = __phys_to_pfn(PHYS_PCI_MEM_BASE),
.length = SZ_16M,
.type = MT_DEVICE
}, {
- .virtual = PCI_CONFIG_VADDR,
+ .virtual = (unsigned long)PCI_CONFIG_VADDR,
.pfn = __phys_to_pfn(PHYS_PCI_CONFIG_BASE),
.length = SZ_16M,
.type = MT_DEVICE
}, {
- .virtual = PCI_V3_VADDR,
+ .virtual = (unsigned long)PCI_V3_VADDR,
.pfn = __phys_to_pfn(PHYS_PCI_V3_BASE),
.length = SZ_64K,
.type = MT_DEVICE
@@ -314,9 +314,9 @@ static void __init ap_init(void)
/*
* Where is the timer (VA)?
*/
-#define TIMER0_VA_BASE IO_ADDRESS(INTEGRATOR_TIMER0_BASE)
-#define TIMER1_VA_BASE IO_ADDRESS(INTEGRATOR_TIMER1_BASE)
-#define TIMER2_VA_BASE IO_ADDRESS(INTEGRATOR_TIMER2_BASE)
+#define TIMER0_VA_BASE __io_address(INTEGRATOR_TIMER0_BASE)
+#define TIMER1_VA_BASE __io_address(INTEGRATOR_TIMER1_BASE)
+#define TIMER2_VA_BASE __io_address(INTEGRATOR_TIMER2_BASE)
static unsigned long timer_reload;
diff --git a/arch/arm/mach-integrator/integrator_cp.c b/arch/arm/mach-integrator/integrator_cp.c
index 82d5c837cc74..3df5fc369361 100644
--- a/arch/arm/mach-integrator/integrator_cp.c
+++ b/arch/arm/mach-integrator/integrator_cp.c
@@ -59,7 +59,7 @@
#define INTCP_ETH_SIZE 0x10
-#define INTCP_VA_CTRL_BASE IO_ADDRESS(INTEGRATOR_CP_CTL_BASE)
+#define INTCP_VA_CTRL_BASE __io_address(INTEGRATOR_CP_CTL_BASE)
#define INTCP_FLASHPROG 0x04
#define CINTEGRATOR_FLASHPROG_FLVPPEN (1 << 0)
#define CINTEGRATOR_FLASHPROG_FLWREN (1 << 1)
@@ -265,8 +265,8 @@ static struct platform_device *intcp_devs[] __initdata = {
*/
static unsigned int mmc_status(struct device *dev)
{
- unsigned int status = readl(IO_ADDRESS(0xca000000 + 4));
- writel(8, IO_ADDRESS(INTEGRATOR_CP_CTL_BASE + 8));
+ unsigned int status = readl(__io_address(0xca000000 + 4));
+ writel(8, __io_address(INTEGRATOR_CP_CTL_BASE + 8));
return status & 8;
}
diff --git a/arch/arm/mach-integrator/leds.c b/arch/arm/mach-integrator/leds.c
index 466defa97842..7a7f6d3273bf 100644
--- a/arch/arm/mach-integrator/leds.c
+++ b/arch/arm/mach-integrator/leds.c
@@ -1,90 +1,125 @@
/*
- * linux/arch/arm/mach-integrator/leds.c
+ * Driver for the 4 user LEDs found on the Integrator AP/CP baseboard
+ * Based on Versatile and RealView machine LED code
*
- * Integrator/AP and Integrator/CP LED control routines
- *
- * Copyright (C) 1999 ARM Limited
- * Copyright (C) 2000 Deep Blue Solutions Ltd
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Bryan Wu <bryan.wu@canonical.com>
*/
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/smp.h>
-#include <linux/spinlock.h>
#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <mach/cm.h>
#include <mach/hardware.h>
#include <mach/platform.h>
-#include <asm/leds.h>
-#include <asm/mach-types.h>
-#include <mach/cm.h>
-static int saved_leds;
+#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
+
+#define ALPHA_REG __io_address(INTEGRATOR_DBG_BASE)
+#define LEDREG (__io_address(INTEGRATOR_DBG_BASE) + INTEGRATOR_DBG_LEDS_OFFSET)
-static void integrator_leds_event(led_event_t ledevt)
+struct integrator_led {
+ struct led_classdev cdev;
+ u8 mask;
+};
+
+/*
+ * The triggers lines up below will only be used if the
+ * LED triggers are compiled in.
+ */
+static const struct {
+ const char *name;
+ const char *trigger;
+} integrator_leds[] = {
+ { "integrator:green0", "heartbeat", },
+ { "integrator:yellow", },
+ { "integrator:red", },
+ { "integrator:green1", },
+ { "integrator:core_module", "cpu0", },
+};
+
+static void integrator_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
{
- unsigned long flags;
- const unsigned int dbg_base = IO_ADDRESS(INTEGRATOR_DBG_BASE);
- unsigned int update_alpha_leds;
+ struct integrator_led *led = container_of(cdev,
+ struct integrator_led, cdev);
+ u32 reg = __raw_readl(LEDREG);
- // yup, change the LEDs
- local_irq_save(flags);
- update_alpha_leds = 0;
+ if (b != LED_OFF)
+ reg |= led->mask;
+ else
+ reg &= ~led->mask;
- switch(ledevt) {
- case led_idle_start:
- cm_control(CM_CTRL_LED, 0);
- break;
+ while (__raw_readl(ALPHA_REG) & 1)
+ cpu_relax();
- case led_idle_end:
- cm_control(CM_CTRL_LED, CM_CTRL_LED);
- break;
+ __raw_writel(reg, LEDREG);
+}
- case led_timer:
- saved_leds ^= GREEN_LED;
- update_alpha_leds = 1;
- break;
+static enum led_brightness integrator_led_get(struct led_classdev *cdev)
+{
+ struct integrator_led *led = container_of(cdev,
+ struct integrator_led, cdev);
+ u32 reg = __raw_readl(LEDREG);
- case led_red_on:
- saved_leds |= RED_LED;
- update_alpha_leds = 1;
- break;
+ return (reg & led->mask) ? LED_FULL : LED_OFF;
+}
- case led_red_off:
- saved_leds &= ~RED_LED;
- update_alpha_leds = 1;
- break;
+static void cm_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
+{
+ if (b != LED_OFF)
+ cm_control(CM_CTRL_LED, CM_CTRL_LED);
+ else
+ cm_control(CM_CTRL_LED, 0);
+}
- default:
- break;
- }
+static enum led_brightness cm_led_get(struct led_classdev *cdev)
+{
+ u32 reg = readl(CM_CTRL);
- if (update_alpha_leds) {
- while (__raw_readl(dbg_base + INTEGRATOR_DBG_ALPHA_OFFSET) & 1);
- __raw_writel(saved_leds, dbg_base + INTEGRATOR_DBG_LEDS_OFFSET);
- }
- local_irq_restore(flags);
+ return (reg & CM_CTRL_LED) ? LED_FULL : LED_OFF;
}
-static int __init leds_init(void)
+static int __init integrator_leds_init(void)
{
- if (machine_is_integrator() || machine_is_cintegrator())
- leds_event = integrator_leds_event;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(integrator_leds); i++) {
+ struct integrator_led *led;
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ break;
+
+
+ led->cdev.name = integrator_leds[i].name;
+
+ if (i == 4) { /* Setting for LED in core module */
+ led->cdev.brightness_set = cm_led_set;
+ led->cdev.brightness_get = cm_led_get;
+ } else {
+ led->cdev.brightness_set = integrator_led_set;
+ led->cdev.brightness_get = integrator_led_get;
+ }
+
+ led->cdev.default_trigger = integrator_leds[i].trigger;
+ led->mask = BIT(i);
+
+ if (led_classdev_register(NULL, &led->cdev) < 0) {
+ kfree(led);
+ break;
+ }
+ }
return 0;
}
-core_initcall(leds_init);
+/*
+ * Since we may have triggers on any subsystem, defer registration
+ * until after subsystem_init.
+ */
+fs_initcall(integrator_leds_init);
+#endif
diff --git a/arch/arm/mach-integrator/pci_v3.c b/arch/arm/mach-integrator/pci_v3.c
index 495f181fc937..bbeca59df66b 100644
--- a/arch/arm/mach-integrator/pci_v3.c
+++ b/arch/arm/mach-integrator/pci_v3.c
@@ -181,7 +181,7 @@ static DEFINE_RAW_SPINLOCK(v3_lock);
#undef V3_LB_BASE_PREFETCH
#define V3_LB_BASE_PREFETCH 0
-static unsigned long v3_open_config_window(struct pci_bus *bus,
+static void __iomem *v3_open_config_window(struct pci_bus *bus,
unsigned int devfn, int offset)
{
unsigned int address, mapaddress, busnr;
@@ -280,7 +280,7 @@ static void v3_close_config_window(void)
static int v3_read_config(struct pci_bus *bus, unsigned int devfn, int where,
int size, u32 *val)
{
- unsigned long addr;
+ void __iomem *addr;
unsigned long flags;
u32 v;
@@ -311,7 +311,7 @@ static int v3_read_config(struct pci_bus *bus, unsigned int devfn, int where,
static int v3_write_config(struct pci_bus *bus, unsigned int devfn, int where,
int size, u32 val)
{
- unsigned long addr;
+ void __iomem *addr;
unsigned long flags;
raw_spin_lock_irqsave(&v3_lock, flags);
@@ -388,9 +388,9 @@ static int __init pci_v3_setup_resources(struct pci_sys_data *sys)
* means I can't get additional information on the reason for the pm2fb
* problems. I suppose I'll just have to mind-meld with the machine. ;)
*/
-#define SC_PCI IO_ADDRESS(INTEGRATOR_SC_PCIENABLE)
-#define SC_LBFADDR IO_ADDRESS(INTEGRATOR_SC_BASE + 0x20)
-#define SC_LBFCODE IO_ADDRESS(INTEGRATOR_SC_BASE + 0x24)
+#define SC_PCI __io_address(INTEGRATOR_SC_PCIENABLE)
+#define SC_LBFADDR __io_address(INTEGRATOR_SC_BASE + 0x20)
+#define SC_LBFCODE __io_address(INTEGRATOR_SC_BASE + 0x24)
static int
v3_pci_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
diff --git a/arch/arm/mach-iop13xx/include/mach/iop13xx.h b/arch/arm/mach-iop13xx/include/mach/iop13xx.h
index e10e101645dd..7480f58267aa 100644
--- a/arch/arm/mach-iop13xx/include/mach/iop13xx.h
+++ b/arch/arm/mach-iop13xx/include/mach/iop13xx.h
@@ -126,18 +126,16 @@ extern unsigned long get_iop_tick_rate(void);
* IOP13XX chipset registers
*/
#define IOP13XX_PMMR_PHYS_MEM_BASE 0xffd80000UL /* PMMR phys. address */
-#define IOP13XX_PMMR_VIRT_MEM_BASE 0xfee80000UL /* PMMR phys. address */
+#define IOP13XX_PMMR_VIRT_MEM_BASE (void __iomem *)(0xfee80000UL) /* PMMR phys. address */
#define IOP13XX_PMMR_MEM_WINDOW_SIZE 0x80000
#define IOP13XX_PMMR_UPPER_MEM_VA (IOP13XX_PMMR_VIRT_MEM_BASE +\
IOP13XX_PMMR_MEM_WINDOW_SIZE - 1)
#define IOP13XX_PMMR_UPPER_MEM_PA (IOP13XX_PMMR_PHYS_MEM_BASE +\
IOP13XX_PMMR_MEM_WINDOW_SIZE - 1)
-#define IOP13XX_PMMR_VIRT_TO_PHYS(addr) (u32) ((u32) addr +\
- (IOP13XX_PMMR_PHYS_MEM_BASE\
- - IOP13XX_PMMR_VIRT_MEM_BASE))
-#define IOP13XX_PMMR_PHYS_TO_VIRT(addr) (u32) ((u32) addr -\
- (IOP13XX_PMMR_PHYS_MEM_BASE\
- - IOP13XX_PMMR_VIRT_MEM_BASE))
+#define IOP13XX_PMMR_VIRT_TO_PHYS(addr) (((addr) - IOP13XX_PMMR_VIRT_MEM_BASE)\
+ + IOP13XX_PMMR_PHYS_MEM_BASE)
+#define IOP13XX_PMMR_PHYS_TO_VIRT(addr) (((addr) - IOP13XX_PMMR_PHYS_MEM_BASE)\
+ + IOP13XX_PMMR_VIRT_MEM_BASE)
#define IOP13XX_REG_ADDR32(reg) (IOP13XX_PMMR_VIRT_MEM_BASE + (reg))
#define IOP13XX_REG_ADDR16(reg) (IOP13XX_PMMR_VIRT_MEM_BASE + (reg))
#define IOP13XX_REG_ADDR8(reg) (IOP13XX_PMMR_VIRT_MEM_BASE + (reg))
@@ -147,10 +145,10 @@ extern unsigned long get_iop_tick_rate(void);
#define IOP13XX_PMMR_SIZE 0x00080000
/*=================== Defines for Platform Devices =====================*/
-#define IOP13XX_UART0_PHYS (IOP13XX_PMMR_PHYS_MEM_BASE | 0x00002300)
-#define IOP13XX_UART1_PHYS (IOP13XX_PMMR_PHYS_MEM_BASE | 0x00002340)
-#define IOP13XX_UART0_VIRT (IOP13XX_PMMR_VIRT_MEM_BASE | 0x00002300)
-#define IOP13XX_UART1_VIRT (IOP13XX_PMMR_VIRT_MEM_BASE | 0x00002340)
+#define IOP13XX_UART0_PHYS (IOP13XX_PMMR_PHYS_MEM_BASE + 0x00002300)
+#define IOP13XX_UART1_PHYS (IOP13XX_PMMR_PHYS_MEM_BASE + 0x00002340)
+#define IOP13XX_UART0_VIRT (IOP13XX_PMMR_VIRT_MEM_BASE + 0x00002300)
+#define IOP13XX_UART1_VIRT (IOP13XX_PMMR_VIRT_MEM_BASE + 0x00002340)
#define IOP13XX_I2C0_PHYS (IOP13XX_PMMR_PHYS_MEM_BASE | 0x00002500)
#define IOP13XX_I2C1_PHYS (IOP13XX_PMMR_PHYS_MEM_BASE | 0x00002520)
diff --git a/arch/arm/mach-iop13xx/include/mach/memory.h b/arch/arm/mach-iop13xx/include/mach/memory.h
index 1afa99ef97fa..7c032d0ab24a 100644
--- a/arch/arm/mach-iop13xx/include/mach/memory.h
+++ b/arch/arm/mach-iop13xx/include/mach/memory.h
@@ -16,12 +16,12 @@
#define IOP13XX_PMMR_P_START (IOP13XX_PMMR_PHYS_MEM_BASE)
#define IOP13XX_PMMR_P_END (IOP13XX_PMMR_PHYS_MEM_BASE + IOP13XX_PMMR_SIZE)
-static inline dma_addr_t __virt_to_lbus(unsigned long x)
+static inline dma_addr_t __virt_to_lbus(void __iomem *x)
{
return x + IOP13XX_PMMR_PHYS_MEM_BASE - IOP13XX_PMMR_VIRT_MEM_BASE;
}
-static inline unsigned long __lbus_to_virt(dma_addr_t x)
+static inline void __iomem *__lbus_to_virt(dma_addr_t x)
{
return x + IOP13XX_PMMR_VIRT_MEM_BASE - IOP13XX_PMMR_PHYS_MEM_BASE;
}
@@ -38,23 +38,23 @@ static inline unsigned long __lbus_to_virt(dma_addr_t x)
#define __arch_dma_to_virt(dev, addr) \
({ \
- unsigned long __virt; \
+ void * __virt; \
dma_addr_t __dma = addr; \
if (is_lbus_device(dev) && __is_lbus_dma(__dma)) \
__virt = __lbus_to_virt(__dma); \
else \
- __virt = __phys_to_virt(__dma); \
- (void *)__virt; \
+ __virt = (void *)__phys_to_virt(__dma); \
+ __virt; \
})
#define __arch_virt_to_dma(dev, addr) \
({ \
- unsigned long __virt = (unsigned long)addr; \
+ void * __virt = addr; \
dma_addr_t __dma; \
if (is_lbus_device(dev) && __is_lbus_virt(__virt)) \
__dma = __virt_to_lbus(__virt); \
else \
- __dma = __virt_to_phys(__virt); \
+ __dma = __virt_to_phys((unsigned long)__virt); \
__dma; \
})
diff --git a/arch/arm/mach-iop13xx/io.c b/arch/arm/mach-iop13xx/io.c
index 851dc8f2b6b5..183dc8b5511b 100644
--- a/arch/arm/mach-iop13xx/io.c
+++ b/arch/arm/mach-iop13xx/io.c
@@ -33,14 +33,14 @@ static void __iomem *__iop13xx_ioremap_caller(unsigned long cookie,
if (unlikely(!iop13xx_atux_mem_base))
retval = NULL;
else
- retval = (void *)(iop13xx_atux_mem_base +
+ retval = (iop13xx_atux_mem_base +
(cookie - IOP13XX_PCIX_LOWER_MEM_RA));
break;
case IOP13XX_PCIE_LOWER_MEM_RA ... IOP13XX_PCIE_UPPER_MEM_RA:
if (unlikely(!iop13xx_atue_mem_base))
retval = NULL;
else
- retval = (void *)(iop13xx_atue_mem_base +
+ retval = (iop13xx_atue_mem_base +
(cookie - IOP13XX_PCIE_LOWER_MEM_RA));
break;
case IOP13XX_PBI_LOWER_MEM_RA ... IOP13XX_PBI_UPPER_MEM_RA:
@@ -49,7 +49,7 @@ static void __iomem *__iop13xx_ioremap_caller(unsigned long cookie,
size, mtype, __builtin_return_address(0));
break;
case IOP13XX_PMMR_PHYS_MEM_BASE ... IOP13XX_PMMR_UPPER_MEM_PA:
- retval = (void *) IOP13XX_PMMR_PHYS_TO_VIRT(cookie);
+ retval = IOP13XX_PMMR_PHYS_TO_VIRT(cookie);
break;
default:
retval = __arm_ioremap_caller(cookie, size, mtype,
@@ -74,7 +74,7 @@ static void __iop13xx_iounmap(volatile void __iomem *addr)
goto skip;
switch ((u32) addr) {
- case IOP13XX_PMMR_VIRT_MEM_BASE ... IOP13XX_PMMR_UPPER_MEM_VA:
+ case (u32)IOP13XX_PMMR_VIRT_MEM_BASE ... (u32)IOP13XX_PMMR_UPPER_MEM_VA:
goto skip;
}
__iounmap(addr);
diff --git a/arch/arm/mach-iop13xx/pci.c b/arch/arm/mach-iop13xx/pci.c
index 91f731a2957b..9082b84aeebb 100644
--- a/arch/arm/mach-iop13xx/pci.c
+++ b/arch/arm/mach-iop13xx/pci.c
@@ -36,8 +36,8 @@ u32 iop13xx_atux_pmmr_offset; /* This offset can change based on strapping */
u32 iop13xx_atue_pmmr_offset; /* This offset can change based on strapping */
static struct pci_bus *pci_bus_atux = 0;
static struct pci_bus *pci_bus_atue = 0;
-u32 iop13xx_atue_mem_base;
-u32 iop13xx_atux_mem_base;
+void __iomem *iop13xx_atue_mem_base;
+void __iomem *iop13xx_atux_mem_base;
size_t iop13xx_atue_mem_size;
size_t iop13xx_atux_mem_size;
@@ -88,8 +88,7 @@ void iop13xx_map_pci_memory(void)
}
if (end) {
- iop13xx_atux_mem_base =
- (u32) __arm_ioremap_pfn(
+ iop13xx_atux_mem_base = __arm_ioremap_pfn(
__phys_to_pfn(IOP13XX_PCIX_LOWER_MEM_PA)
, 0, iop13xx_atux_mem_size, MT_DEVICE);
if (!iop13xx_atux_mem_base) {
@@ -99,7 +98,7 @@ void iop13xx_map_pci_memory(void)
}
} else
iop13xx_atux_mem_size = 0;
- PRINTK("%s: atu: %d bus_size: %d mem_base: %x\n",
+ PRINTK("%s: atu: %d bus_size: %d mem_base: %p\n",
__func__, atu, iop13xx_atux_mem_size,
iop13xx_atux_mem_base);
break;
@@ -114,8 +113,7 @@ void iop13xx_map_pci_memory(void)
}
if (end) {
- iop13xx_atue_mem_base =
- (u32) __arm_ioremap_pfn(
+ iop13xx_atue_mem_base = __arm_ioremap_pfn(
__phys_to_pfn(IOP13XX_PCIE_LOWER_MEM_PA)
, 0, iop13xx_atue_mem_size, MT_DEVICE);
if (!iop13xx_atue_mem_base) {
@@ -125,13 +123,13 @@ void iop13xx_map_pci_memory(void)
}
} else
iop13xx_atue_mem_size = 0;
- PRINTK("%s: atu: %d bus_size: %d mem_base: %x\n",
+ PRINTK("%s: atu: %d bus_size: %d mem_base: %p\n",
__func__, atu, iop13xx_atue_mem_size,
iop13xx_atue_mem_base);
break;
}
- printk("%s: Initialized (%uM @ resource/virtual: %08lx/%08x)\n",
+ printk("%s: Initialized (%uM @ resource/virtual: %08lx/%p)\n",
atu ? "ATUE" : "ATUX",
(atu ? iop13xx_atue_mem_size : iop13xx_atux_mem_size) /
SZ_1M,
diff --git a/arch/arm/mach-iop13xx/pci.h b/arch/arm/mach-iop13xx/pci.h
index c70cf5b41e31..d45a80b3080e 100644
--- a/arch/arm/mach-iop13xx/pci.h
+++ b/arch/arm/mach-iop13xx/pci.h
@@ -1,6 +1,6 @@
#include <linux/types.h>
-extern u32 iop13xx_atue_mem_base;
-extern u32 iop13xx_atux_mem_base;
+extern void __iomem *iop13xx_atue_mem_base;
+extern void __iomem *iop13xx_atux_mem_base;
extern size_t iop13xx_atue_mem_size;
extern size_t iop13xx_atux_mem_size;
diff --git a/arch/arm/mach-iop13xx/setup.c b/arch/arm/mach-iop13xx/setup.c
index 4a7f20d7fb6e..3181f61ea63e 100644
--- a/arch/arm/mach-iop13xx/setup.c
+++ b/arch/arm/mach-iop13xx/setup.c
@@ -36,7 +36,7 @@
*/
static struct map_desc iop13xx_std_desc[] __initdata = {
{ /* mem mapped registers */
- .virtual = IOP13XX_PMMR_VIRT_MEM_BASE,
+ .virtual = (unsigned long)IOP13XX_PMMR_VIRT_MEM_BASE,
.pfn = __phys_to_pfn(IOP13XX_PMMR_PHYS_MEM_BASE),
.length = IOP13XX_PMMR_SIZE,
.type = MT_DEVICE,
@@ -71,8 +71,8 @@ static struct resource iop13xx_uart1_resources[] = {
static struct plat_serial8250_port iop13xx_uart0_data[] = {
{
- .membase = (char*)(IOP13XX_UART0_VIRT),
- .mapbase = (IOP13XX_UART0_PHYS),
+ .membase = IOP13XX_UART0_VIRT,
+ .mapbase = IOP13XX_UART0_PHYS,
.irq = IRQ_IOP13XX_UART0,
.uartclk = IOP13XX_UART_XTAL,
.regshift = 2,
@@ -84,8 +84,8 @@ static struct plat_serial8250_port iop13xx_uart0_data[] = {
static struct plat_serial8250_port iop13xx_uart1_data[] = {
{
- .membase = (char*)(IOP13XX_UART1_VIRT),
- .mapbase = (IOP13XX_UART1_PHYS),
+ .membase = IOP13XX_UART1_VIRT,
+ .mapbase = IOP13XX_UART1_PHYS,
.irq = IRQ_IOP13XX_UART1,
.uartclk = IOP13XX_UART_XTAL,
.regshift = 2,
diff --git a/arch/arm/mach-iop32x/glantank.c b/arch/arm/mach-iop32x/glantank.c
index c15a100ba779..02e20c3912ba 100644
--- a/arch/arm/mach-iop32x/glantank.c
+++ b/arch/arm/mach-iop32x/glantank.c
@@ -183,7 +183,7 @@ static struct i2c_board_info __initdata glantank_i2c_devices[] = {
static void glantank_power_off(void)
{
- __raw_writeb(0x01, 0xfe8d0004);
+ __raw_writeb(0x01, IOMEM(0xfe8d0004));
while (1)
;
diff --git a/arch/arm/mach-ixp4xx/common.c b/arch/arm/mach-ixp4xx/common.c
index a9f80943d01f..fdf91a160884 100644
--- a/arch/arm/mach-ixp4xx/common.c
+++ b/arch/arm/mach-ixp4xx/common.c
@@ -53,24 +53,24 @@ static struct clock_event_device clockevent_ixp4xx;
*************************************************************************/
static struct map_desc ixp4xx_io_desc[] __initdata = {
{ /* UART, Interrupt ctrl, GPIO, timers, NPEs, MACs, USB .... */
- .virtual = IXP4XX_PERIPHERAL_BASE_VIRT,
+ .virtual = (unsigned long)IXP4XX_PERIPHERAL_BASE_VIRT,
.pfn = __phys_to_pfn(IXP4XX_PERIPHERAL_BASE_PHYS),
.length = IXP4XX_PERIPHERAL_REGION_SIZE,
.type = MT_DEVICE
}, { /* Expansion Bus Config Registers */
- .virtual = IXP4XX_EXP_CFG_BASE_VIRT,
+ .virtual = (unsigned long)IXP4XX_EXP_CFG_BASE_VIRT,
.pfn = __phys_to_pfn(IXP4XX_EXP_CFG_BASE_PHYS),
.length = IXP4XX_EXP_CFG_REGION_SIZE,
.type = MT_DEVICE
}, { /* PCI Registers */
- .virtual = IXP4XX_PCI_CFG_BASE_VIRT,
+ .virtual = (unsigned long)IXP4XX_PCI_CFG_BASE_VIRT,
.pfn = __phys_to_pfn(IXP4XX_PCI_CFG_BASE_PHYS),
.length = IXP4XX_PCI_CFG_REGION_SIZE,
.type = MT_DEVICE
},
#ifdef CONFIG_DEBUG_LL
{ /* Debug UART mapping */
- .virtual = IXP4XX_DEBUG_UART_BASE_VIRT,
+ .virtual = (unsigned long)IXP4XX_DEBUG_UART_BASE_VIRT,
.pfn = __phys_to_pfn(IXP4XX_DEBUG_UART_BASE_PHYS),
.length = IXP4XX_DEBUG_UART_REGION_SIZE,
.type = MT_DEVICE
diff --git a/arch/arm/mach-ixp4xx/include/mach/cpu.h b/arch/arm/mach-ixp4xx/include/mach/cpu.h
index b2ef65db0e91..ebc0ba31ce85 100644
--- a/arch/arm/mach-ixp4xx/include/mach/cpu.h
+++ b/arch/arm/mach-ixp4xx/include/mach/cpu.h
@@ -14,6 +14,7 @@
#ifndef __ASM_ARCH_CPU_H__
#define __ASM_ARCH_CPU_H__
+#include <linux/io.h>
#include <asm/cputype.h>
/* Processor id value in CP15 Register 0 */
@@ -37,7 +38,7 @@
static inline u32 ixp4xx_read_feature_bits(void)
{
- u32 val = ~*IXP4XX_EXP_CFG2;
+ u32 val = ~__raw_readl(IXP4XX_EXP_CFG2);
if (cpu_is_ixp42x_rev_a0())
return IXP42X_FEATURE_MASK & ~(IXP4XX_FEATURE_RCOMP |
@@ -51,7 +52,7 @@ static inline u32 ixp4xx_read_feature_bits(void)
static inline void ixp4xx_write_feature_bits(u32 value)
{
- *IXP4XX_EXP_CFG2 = ~value;
+ __raw_writel(~value, IXP4XX_EXP_CFG2);
}
#endif /* _ASM_ARCH_CPU_H */
diff --git a/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h
index 97c530f66e78..eb68b61ce975 100644
--- a/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h
+++ b/arch/arm/mach-ixp4xx/include/mach/ixp4xx-regs.h
@@ -49,21 +49,21 @@
* Expansion BUS Configuration registers
*/
#define IXP4XX_EXP_CFG_BASE_PHYS (0xC4000000)
-#define IXP4XX_EXP_CFG_BASE_VIRT (0xFFBFE000)
+#define IXP4XX_EXP_CFG_BASE_VIRT IOMEM(0xFFBFE000)
#define IXP4XX_EXP_CFG_REGION_SIZE (0x00001000)
/*
* PCI Config registers
*/
#define IXP4XX_PCI_CFG_BASE_PHYS (0xC0000000)
-#define IXP4XX_PCI_CFG_BASE_VIRT (0xFFBFF000)
+#define IXP4XX_PCI_CFG_BASE_VIRT IOMEM(0xFFBFF000)
#define IXP4XX_PCI_CFG_REGION_SIZE (0x00001000)
/*
* Peripheral space
*/
#define IXP4XX_PERIPHERAL_BASE_PHYS (0xC8000000)
-#define IXP4XX_PERIPHERAL_BASE_VIRT (0xFFBEB000)
+#define IXP4XX_PERIPHERAL_BASE_VIRT IOMEM(0xFFBEB000)
#define IXP4XX_PERIPHERAL_REGION_SIZE (0x00013000)
/*
@@ -73,7 +73,7 @@
* aligned so that it * can be used with the low-level debug code.
*/
#define IXP4XX_DEBUG_UART_BASE_PHYS (0xC8000000)
-#define IXP4XX_DEBUG_UART_BASE_VIRT (0xffb00000)
+#define IXP4XX_DEBUG_UART_BASE_VIRT IOMEM(0xffb00000)
#define IXP4XX_DEBUG_UART_REGION_SIZE (0x00001000)
#define IXP4XX_EXP_CS0_OFFSET 0x00
@@ -92,7 +92,7 @@
/*
* Expansion Bus Controller registers.
*/
-#define IXP4XX_EXP_REG(x) ((volatile u32 *)(IXP4XX_EXP_CFG_BASE_VIRT+(x)))
+#define IXP4XX_EXP_REG(x) ((volatile u32 __iomem *)(IXP4XX_EXP_CFG_BASE_VIRT+(x)))
#define IXP4XX_EXP_CS0 IXP4XX_EXP_REG(IXP4XX_EXP_CS0_OFFSET)
#define IXP4XX_EXP_CS1 IXP4XX_EXP_REG(IXP4XX_EXP_CS1_OFFSET)
diff --git a/arch/arm/mach-ks8695/Kconfig b/arch/arm/mach-ks8695/Kconfig
index f5c39a8c2b00..a545976bdbd6 100644
--- a/arch/arm/mach-ks8695/Kconfig
+++ b/arch/arm/mach-ks8695/Kconfig
@@ -21,6 +21,67 @@ config MACH_ACS5K
say 'Y' here if you want your kernel to run on the Brivo
Systems LLC, ACS-5000 Master board.
+config MACH_LITE300
+ bool "SecureComputing SG300"
+ help
+ Say 'Y' here if you want your kernel to support the
+ SecureComputing / SnapGear SG300 VPN Internet Router.
+ See http://www.securecomputing.com for more details.
+
+config MACH_SG310
+ bool "McAfee SG310"
+ help
+ Say 'Y' here if you want your kernel to support the
+ McAfee / SnapGear SG310 VPN Internet Router.
+ See http://www.mcafee.com for more details.
+
+config MACH_SE4200
+ bool "SecureComputing SE4200"
+ help
+ Say 'Y' here if you want your kernel to support the
+ SecureComputing / SnapGear SE4200 Secure Wireless VPN
+ Internet Router.
+ See http://www.securecomputing.com for more details.
+
+config MACH_CM4002
+ bool "OpenGear CM4002"
+ help
+ Say 'Y' here if you want your kernel to support the OpenGear
+ CM4002 Secure Access Server. See http://www.opengear.com for
+ more details.
+
+config MACH_CM4008
+ bool "OpenGear CM4008"
+ select MIGHT_HAVE_PCI
+ help
+ Say 'Y' here if you want your kernel to support the OpenGear
+ CM4008 Console Server. See http://www.opengear.com for more
+ details.
+
+config MACH_CM41xx
+ bool "OpenGear CM41xx"
+ select MIGHT_HAVE_PCI
+ help
+ Say 'Y' here if you want your kernel to support the OpenGear
+ CM4016 or CM4048 Console Servers. See http://www.opengear.com for
+ more details.
+
+config MACH_IM4004
+ bool "OpenGear IM4004"
+ select MIGHT_HAVE_PCI
+ help
+ Say 'Y' here if you want your kernel to support the OpenGear
+ IM4004 Secure Access Server. See http://www.opengear.com for
+ more details.
+
+config MACH_IM42xx
+ bool "OpenGear IM42xx"
+ select MIGHT_HAVE_PCI
+ help
+ Say 'Y' here if you want your kernel to support the OpenGear
+ IM4216 or IM4248 Console Servers. See http://www.opengear.com for
+ more details.
+
endmenu
endif
diff --git a/arch/arm/mach-ks8695/Makefile b/arch/arm/mach-ks8695/Makefile
index 853efd9133c6..e370caf0c91b 100644
--- a/arch/arm/mach-ks8695/Makefile
+++ b/arch/arm/mach-ks8695/Makefile
@@ -11,10 +11,15 @@ obj- :=
# PCI support is optional
obj-$(CONFIG_PCI) += pci.o
-# LEDs
-obj-$(CONFIG_LEDS) += leds.o
-
# Board-specific support
obj-$(CONFIG_MACH_KS8695) += board-micrel.o
obj-$(CONFIG_MACH_DSM320) += board-dsm320.o
obj-$(CONFIG_MACH_ACS5K) += board-acs5k.o
+obj-$(CONFIG_MACH_LITE300) += board-sg.o
+obj-$(CONFIG_MACH_SG310) += board-sg.o
+obj-$(CONFIG_MACH_SE4200) += board-sg.o
+obj-$(CONFIG_MACH_CM4002) += board-og.o
+obj-$(CONFIG_MACH_CM4008) += board-og.o
+obj-$(CONFIG_MACH_CM41xx) += board-og.o
+obj-$(CONFIG_MACH_IM4004) += board-og.o
+obj-$(CONFIG_MACH_IM42xx) += board-og.o
diff --git a/arch/arm/mach-ks8695/board-og.c b/arch/arm/mach-ks8695/board-og.c
new file mode 100644
index 000000000000..1623ba461e47
--- /dev/null
+++ b/arch/arm/mach-ks8695/board-og.c
@@ -0,0 +1,199 @@
+/*
+ * board-og.c -- support for the OpenGear KS8695 based boards.
+ *
+ * This 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/interrupt.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/serial_8250.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <mach/devices.h>
+#include <mach/regs-gpio.h>
+#include <mach/gpio-ks8695.h>
+#include "generic.h"
+
+static int og_pci_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ if (machine_is_im4004() && (slot == 8))
+ return KS8695_IRQ_EXTERN1;
+ return KS8695_IRQ_EXTERN0;
+}
+
+static struct ks8695_pci_cfg __initdata og_pci = {
+ .mode = KS8695_MODE_PCI,
+ .map_irq = og_pci_map_irq,
+};
+
+static void __init og_register_pci(void)
+{
+ /* Initialize the GPIO lines for interrupt mode */
+ ks8695_gpio_interrupt(KS8695_GPIO_0, IRQ_TYPE_LEVEL_LOW);
+
+ /* Cardbus Slot */
+ if (machine_is_im4004())
+ ks8695_gpio_interrupt(KS8695_GPIO_1, IRQ_TYPE_LEVEL_LOW);
+
+ ks8695_init_pci(&og_pci);
+}
+
+/*
+ * The PCI bus reset is driven by a dedicated GPIO line. Toggle it here
+ * and bring the PCI bus out of reset.
+ */
+static void __init og_pci_bus_reset(void)
+{
+ unsigned int rstline = 1;
+
+ /* Some boards use a different GPIO as the PCI reset line */
+ if (machine_is_im4004())
+ rstline = 2;
+ else if (machine_is_im42xx())
+ rstline = 0;
+
+ gpio_request(rstline, "PCI reset");
+ gpio_direction_output(rstline, 0);
+
+ /* Drive a reset on the PCI reset line */
+ gpio_set_value(rstline, 1);
+ gpio_set_value(rstline, 0);
+ mdelay(100);
+ gpio_set_value(rstline, 1);
+ mdelay(100);
+}
+
+/*
+ * Direct connect serial ports (non-PCI that is).
+ */
+#define S8250_PHYS 0x03800000
+#define S8250_VIRT 0xf4000000
+#define S8250_SIZE 0x00100000
+
+static struct __initdata map_desc og_io_desc[] = {
+ {
+ .virtual = S8250_VIRT,
+ .pfn = __phys_to_pfn(S8250_PHYS),
+ .length = S8250_SIZE,
+ .type = MT_DEVICE,
+ }
+};
+
+static struct resource og_uart_resources[] = {
+ {
+ .start = S8250_VIRT,
+ .end = S8250_VIRT + S8250_SIZE,
+ .flags = IORESOURCE_MEM
+ },
+};
+
+static struct plat_serial8250_port og_uart_data[] = {
+ {
+ .mapbase = S8250_VIRT,
+ .membase = (char *) S8250_VIRT,
+ .irq = 3,
+ .flags = UPF_BOOT_AUTOCONF | UPF_SKIP_TEST,
+ .iotype = UPIO_MEM,
+ .regshift = 2,
+ .uartclk = 115200 * 16,
+ },
+ { },
+};
+
+static struct platform_device og_uart = {
+ .name = "serial8250",
+ .id = 0,
+ .dev.platform_data = og_uart_data,
+ .num_resources = 1,
+ .resource = og_uart_resources
+};
+
+static struct platform_device *og_devices[] __initdata = {
+ &og_uart
+};
+
+static void __init og_init(void)
+{
+ ks8695_register_gpios();
+
+ if (machine_is_cm4002()) {
+ ks8695_gpio_interrupt(KS8695_GPIO_1, IRQ_TYPE_LEVEL_HIGH);
+ iotable_init(og_io_desc, ARRAY_SIZE(og_io_desc));
+ platform_add_devices(og_devices, ARRAY_SIZE(og_devices));
+ } else {
+ og_pci_bus_reset();
+ og_register_pci();
+ }
+
+ ks8695_add_device_lan();
+ ks8695_add_device_wan();
+}
+
+#ifdef CONFIG_MACH_CM4002
+MACHINE_START(CM4002, "OpenGear/CM4002")
+ /* OpenGear Inc. */
+ .atag_offset = 0x100,
+ .map_io = ks8695_map_io,
+ .init_irq = ks8695_init_irq,
+ .init_machine = og_init,
+ .timer = &ks8695_timer,
+ .restart = ks8695_restart,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_CM4008
+MACHINE_START(CM4008, "OpenGear/CM4008")
+ /* OpenGear Inc. */
+ .atag_offset = 0x100,
+ .map_io = ks8695_map_io,
+ .init_irq = ks8695_init_irq,
+ .init_machine = og_init,
+ .timer = &ks8695_timer,
+ .restart = ks8695_restart,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_CM41xx
+MACHINE_START(CM41XX, "OpenGear/CM41xx")
+ /* OpenGear Inc. */
+ .atag_offset = 0x100,
+ .map_io = ks8695_map_io,
+ .init_irq = ks8695_init_irq,
+ .init_machine = og_init,
+ .timer = &ks8695_timer,
+ .restart = ks8695_restart,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_IM4004
+MACHINE_START(IM4004, "OpenGear/IM4004")
+ /* OpenGear Inc. */
+ .atag_offset = 0x100,
+ .map_io = ks8695_map_io,
+ .init_irq = ks8695_init_irq,
+ .init_machine = og_init,
+ .timer = &ks8695_timer,
+ .restart = ks8695_restart,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_IM42xx
+MACHINE_START(IM42XX, "OpenGear/IM42xx")
+ /* OpenGear Inc. */
+ .atag_offset = 0x100,
+ .map_io = ks8695_map_io,
+ .init_irq = ks8695_init_irq,
+ .init_machine = og_init,
+ .timer = &ks8695_timer,
+ .restart = ks8695_restart,
+MACHINE_END
+#endif
diff --git a/arch/arm/mach-ks8695/board-sg.c b/arch/arm/mach-ks8695/board-sg.c
new file mode 100644
index 000000000000..f35b98b5bf37
--- /dev/null
+++ b/arch/arm/mach-ks8695/board-sg.c
@@ -0,0 +1,121 @@
+/*
+ * board-sg.c -- support for the SnapGear KS8695 based boards
+ *
+ * This 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/init.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/physmap.h>
+#include <linux/mtd/partitions.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/devices.h>
+#include "generic.h"
+
+/*
+ * The SG310 machine type is fitted with a conventional 8MB Strataflash
+ * device. Define its partitioning.
+ */
+#define FL_BASE 0x02000000
+#define FL_SIZE SZ_8M
+
+static struct mtd_partition sg_mtd_partitions[] = {
+ [0] = {
+ .name = "SnapGear Boot Loader",
+ .size = SZ_128K,
+ },
+ [1] = {
+ .name = "SnapGear non-volatile configuration",
+ .size = SZ_512K,
+ .offset = SZ_256K,
+ },
+ [2] = {
+ .name = "SnapGear image",
+ .offset = SZ_512K + SZ_256K,
+ },
+ [3] = {
+ .name = "SnapGear StrataFlash",
+ },
+ [4] = {
+ .name = "SnapGear Boot Tags",
+ .size = SZ_128K,
+ .offset = SZ_128K,
+ },
+};
+
+static struct physmap_flash_data sg_mtd_pdata = {
+ .width = 1,
+ .nr_parts = ARRAY_SIZE(sg_mtd_partitions),
+ .parts = sg_mtd_partitions,
+};
+
+
+static struct resource sg_mtd_resource[] = {
+ [0] = {
+ .start = FL_BASE,
+ .end = FL_BASE + FL_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device sg_mtd_device = {
+ .name = "physmap-flash",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(sg_mtd_resource),
+ .resource = sg_mtd_resource,
+ .dev = {
+ .platform_data = &sg_mtd_pdata,
+ },
+};
+
+static void __init sg_init(void)
+{
+ ks8695_add_device_lan();
+ ks8695_add_device_wan();
+
+ if (machine_is_sg310())
+ platform_device_register(&sg_mtd_device);
+}
+
+#ifdef CONFIG_MACH_LITE300
+MACHINE_START(LITE300, "SecureComputing/SG300")
+ /* SnapGear */
+ .atag_offset = 0x100,
+ .map_io = ks8695_map_io,
+ .init_irq = ks8695_init_irq,
+ .init_machine = sg_init,
+ .timer = &ks8695_timer,
+ .restart = ks8695_restart,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_SG310
+MACHINE_START(SG310, "McAfee/SG310")
+ /* SnapGear */
+ .atag_offset = 0x100,
+ .map_io = ks8695_map_io,
+ .init_irq = ks8695_init_irq,
+ .init_machine = sg_init,
+ .timer = &ks8695_timer,
+ .restart = ks8695_restart,
+MACHINE_END
+#endif
+
+#ifdef CONFIG_MACH_SE4200
+MACHINE_START(SE4200, "SecureComputing/SE4200")
+ /* SnapGear */
+ .atag_offset = 0x100,
+ .map_io = ks8695_map_io,
+ .init_irq = ks8695_init_irq,
+ .init_machine = sg_init,
+ .timer = &ks8695_timer,
+ .restart = ks8695_restart,
+MACHINE_END
+#endif
diff --git a/arch/arm/mach-ks8695/cpu.c b/arch/arm/mach-ks8695/cpu.c
index 7f3f24053a00..ddb24222918e 100644
--- a/arch/arm/mach-ks8695/cpu.c
+++ b/arch/arm/mach-ks8695/cpu.c
@@ -36,7 +36,7 @@
static struct __initdata map_desc ks8695_io_desc[] = {
{
- .virtual = KS8695_IO_VA,
+ .virtual = (unsigned long)KS8695_IO_VA,
.pfn = __phys_to_pfn(KS8695_IO_PA),
.length = KS8695_IO_SIZE,
.type = MT_DEVICE,
diff --git a/arch/arm/mach-ks8695/devices.c b/arch/arm/mach-ks8695/devices.c
index 73bd63812878..47399bc3c024 100644
--- a/arch/arm/mach-ks8695/devices.c
+++ b/arch/arm/mach-ks8695/devices.c
@@ -182,27 +182,6 @@ static void __init ks8695_add_device_watchdog(void)
}
-/* --------------------------------------------------------------------
- * LEDs
- * -------------------------------------------------------------------- */
-
-#if defined(CONFIG_LEDS)
-short ks8695_leds_cpu = -1;
-short ks8695_leds_timer = -1;
-
-void __init ks8695_init_leds(u8 cpu_led, u8 timer_led)
-{
- /* Enable GPIO to access the LEDs */
- gpio_direction_output(cpu_led, 1);
- gpio_direction_output(timer_led, 1);
-
- ks8695_leds_cpu = cpu_led;
- ks8695_leds_timer = timer_led;
-}
-#else
-void __init ks8695_init_leds(u8 cpu_led, u8 timer_led) {}
-#endif
-
/* -------------------------------------------------------------------- */
/*
diff --git a/arch/arm/mach-ks8695/include/mach/devices.h b/arch/arm/mach-ks8695/include/mach/devices.h
index 85a3c9aa7d13..1e6594a0f297 100644
--- a/arch/arm/mach-ks8695/include/mach/devices.h
+++ b/arch/arm/mach-ks8695/include/mach/devices.h
@@ -18,11 +18,6 @@ extern void __init ks8695_add_device_wan(void);
extern void __init ks8695_add_device_lan(void);
extern void __init ks8695_add_device_hpna(void);
- /* LEDs */
-extern short ks8695_leds_cpu;
-extern short ks8695_leds_timer;
-extern void __init ks8695_init_leds(u8 cpu_led, u8 timer_led);
-
/* PCI */
#define KS8695_MODE_PCI 0
#define KS8695_MODE_MINIPCI 1
diff --git a/arch/arm/mach-ks8695/include/mach/hardware.h b/arch/arm/mach-ks8695/include/mach/hardware.h
index 5e0c388143da..5090338c0db2 100644
--- a/arch/arm/mach-ks8695/include/mach/hardware.h
+++ b/arch/arm/mach-ks8695/include/mach/hardware.h
@@ -33,7 +33,7 @@
* head debug code as the initial MMU setup only deals in L1 sections.
*/
#define KS8695_IO_PA 0x03F00000
-#define KS8695_IO_VA 0xF0000000
+#define KS8695_IO_VA IOMEM(0xF0000000)
#define KS8695_IO_SIZE SZ_1M
#define KS8695_PCIMEM_PA 0x60000000
diff --git a/arch/arm/mach-ks8695/include/mach/uncompress.h b/arch/arm/mach-ks8695/include/mach/uncompress.h
index 9495cb4d701a..8879d610308a 100644
--- a/arch/arm/mach-ks8695/include/mach/uncompress.h
+++ b/arch/arm/mach-ks8695/include/mach/uncompress.h
@@ -19,15 +19,15 @@
static void putc(char c)
{
- while (!(__raw_readl(KS8695_UART_PA + KS8695_URLS) & URLS_URTHRE))
+ while (!(__raw_readl((void __iomem*)KS8695_UART_PA + KS8695_URLS) & URLS_URTHRE))
barrier();
- __raw_writel(c, KS8695_UART_PA + KS8695_URTH);
+ __raw_writel(c, (void __iomem*)KS8695_UART_PA + KS8695_URTH);
}
static inline void flush(void)
{
- while (!(__raw_readl(KS8695_UART_PA + KS8695_URLS) & URLS_URTE))
+ while (!(__raw_readl((void __iomem*)KS8695_UART_PA + KS8695_URLS) & URLS_URTE))
barrier();
}
diff --git a/arch/arm/mach-ks8695/leds.c b/arch/arm/mach-ks8695/leds.c
deleted file mode 100644
index 4bd707547293..000000000000
--- a/arch/arm/mach-ks8695/leds.c
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * LED driver for KS8695-based boards.
- *
- * Copyright (C) Andrew Victor
- *
- * This 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/gpio.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-
-#include <asm/leds.h>
-#include <mach/devices.h>
-
-
-static inline void ks8695_led_on(unsigned int led)
-{
- gpio_set_value(led, 0);
-}
-
-static inline void ks8695_led_off(unsigned int led)
-{
- gpio_set_value(led, 1);
-}
-
-static inline void ks8695_led_toggle(unsigned int led)
-{
- unsigned long is_off = gpio_get_value(led);
- if (is_off)
- ks8695_led_on(led);
- else
- ks8695_led_off(led);
-}
-
-
-/*
- * Handle LED events.
- */
-static void ks8695_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- switch(evt) {
- case led_start: /* System startup */
- ks8695_led_on(ks8695_leds_cpu);
- break;
-
- case led_stop: /* System stop / suspend */
- ks8695_led_off(ks8695_leds_cpu);
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer: /* Every 50 timer ticks */
- ks8695_led_toggle(ks8695_leds_timer);
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start: /* Entering idle state */
- ks8695_led_off(ks8695_leds_cpu);
- break;
-
- case led_idle_end: /* Exit idle state */
- ks8695_led_on(ks8695_leds_cpu);
- break;
-#endif
-
- default:
- break;
- }
-
- local_irq_restore(flags);
-}
-
-
-static int __init leds_init(void)
-{
- if ((ks8695_leds_timer == -1) || (ks8695_leds_cpu == -1))
- return -ENODEV;
-
- leds_event = ks8695_leds_event;
-
- leds_event(led_start);
- return 0;
-}
-
-__initcall(leds_init);
diff --git a/arch/arm/mach-lpc32xx/common.c b/arch/arm/mach-lpc32xx/common.c
index a48dc2dec485..0d4db8c544b5 100644
--- a/arch/arm/mach-lpc32xx/common.c
+++ b/arch/arm/mach-lpc32xx/common.c
@@ -177,25 +177,25 @@ u32 clk_get_pclk_div(void)
static struct map_desc lpc32xx_io_desc[] __initdata = {
{
- .virtual = IO_ADDRESS(LPC32XX_AHB0_START),
+ .virtual = (unsigned long)IO_ADDRESS(LPC32XX_AHB0_START),
.pfn = __phys_to_pfn(LPC32XX_AHB0_START),
.length = LPC32XX_AHB0_SIZE,
.type = MT_DEVICE
},
{
- .virtual = IO_ADDRESS(LPC32XX_AHB1_START),
+ .virtual = (unsigned long)IO_ADDRESS(LPC32XX_AHB1_START),
.pfn = __phys_to_pfn(LPC32XX_AHB1_START),
.length = LPC32XX_AHB1_SIZE,
.type = MT_DEVICE
},
{
- .virtual = IO_ADDRESS(LPC32XX_FABAPB_START),
+ .virtual = (unsigned long)IO_ADDRESS(LPC32XX_FABAPB_START),
.pfn = __phys_to_pfn(LPC32XX_FABAPB_START),
.length = LPC32XX_FABAPB_SIZE,
.type = MT_DEVICE
},
{
- .virtual = IO_ADDRESS(LPC32XX_IRAM_BASE),
+ .virtual = (unsigned long)IO_ADDRESS(LPC32XX_IRAM_BASE),
.pfn = __phys_to_pfn(LPC32XX_IRAM_BASE),
.length = (LPC32XX_IRAM_BANK_SIZE * 2),
.type = MT_DEVICE
diff --git a/arch/arm/mach-lpc32xx/include/mach/gpio-lpc32xx.h b/arch/arm/mach-lpc32xx/include/mach/gpio-lpc32xx.h
index 1816e22a3479..a544e962a818 100644
--- a/arch/arm/mach-lpc32xx/include/mach/gpio-lpc32xx.h
+++ b/arch/arm/mach-lpc32xx/include/mach/gpio-lpc32xx.h
@@ -30,7 +30,7 @@
#define LPC32XX_GPIO_P1_MAX 24
#define LPC32XX_GPIO_P2_MAX 13
#define LPC32XX_GPIO_P3_MAX 6
-#define LPC32XX_GPI_P3_MAX 28
+#define LPC32XX_GPI_P3_MAX 29
#define LPC32XX_GPO_P3_MAX 24
#define LPC32XX_GPIO_P0_GRP 0
diff --git a/arch/arm/mach-lpc32xx/include/mach/hardware.h b/arch/arm/mach-lpc32xx/include/mach/hardware.h
index 33e1dde37bd9..69065de97a3d 100644
--- a/arch/arm/mach-lpc32xx/include/mach/hardware.h
+++ b/arch/arm/mach-lpc32xx/include/mach/hardware.h
@@ -25,7 +25,7 @@
/*
* This macro relies on fact that for all HW i/o addresses bits 20-23 are 0
*/
-#define IO_ADDRESS(x) (((((x) & 0xff000000) >> 4) | ((x) & 0xfffff)) |\
+#define IO_ADDRESS(x) IOMEM(((((x) & 0xff000000) >> 4) | ((x) & 0xfffff)) |\
IO_BASE)
#define io_p2v(x) ((void __iomem *) (unsigned long) IO_ADDRESS(x))
diff --git a/arch/arm/mach-lpc32xx/phy3250.c b/arch/arm/mach-lpc32xx/phy3250.c
index 8f2a2f8712d7..e8ff4c3f0566 100644
--- a/arch/arm/mach-lpc32xx/phy3250.c
+++ b/arch/arm/mach-lpc32xx/phy3250.c
@@ -24,12 +24,9 @@
#include <linux/irq.h>
#include <linux/dma-mapping.h>
#include <linux/device.h>
-#include <linux/spi/spi.h>
-#include <linux/spi/eeprom.h>
#include <linux/gpio.h>
#include <linux/amba/bus.h>
#include <linux/amba/clcd.h>
-#include <linux/amba/pl022.h>
#include <linux/amba/pl08x.h>
#include <linux/amba/mmci.h>
#include <linux/of.h>
@@ -158,21 +155,6 @@ static struct clcd_board lpc32xx_clcd_data = {
.remove = lpc32xx_clcd_remove,
};
-/*
- * AMBA SSP (SPI)
- */
-static struct pl022_ssp_controller lpc32xx_ssp0_data = {
- .bus_id = 0,
- .num_chipselect = 1,
- .enable_dma = 0,
-};
-
-static struct pl022_ssp_controller lpc32xx_ssp1_data = {
- .bus_id = 1,
- .num_chipselect = 1,
- .enable_dma = 0,
-};
-
static struct pl08x_channel_data pl08x_slave_channels[] = {
{
.bus_id = "nand-slc",
@@ -234,8 +216,8 @@ static struct lpc32xx_mlc_platform_data lpc32xx_mlc_data = {
};
static const struct of_dev_auxdata lpc32xx_auxdata_lookup[] __initconst = {
- OF_DEV_AUXDATA("arm,pl022", 0x20084000, "dev:ssp0", &lpc32xx_ssp0_data),
- OF_DEV_AUXDATA("arm,pl022", 0x2008C000, "dev:ssp1", &lpc32xx_ssp1_data),
+ OF_DEV_AUXDATA("arm,pl022", 0x20084000, "dev:ssp0", NULL),
+ OF_DEV_AUXDATA("arm,pl022", 0x2008C000, "dev:ssp1", NULL),
OF_DEV_AUXDATA("arm,pl110", 0x31040000, "dev:clcd", &lpc32xx_clcd_data),
OF_DEV_AUXDATA("arm,pl080", 0x31000000, "pl08xdmac", &pl08x_pd),
OF_DEV_AUXDATA("arm,pl18x", 0x20098000, "20098000.sd",
diff --git a/arch/arm/mach-mmp/include/mach/debug-macro.S b/arch/arm/mach-mmp/include/mach/debug-macro.S
index b6f14d203c25..5c3cc29688ab 100644
--- a/arch/arm/mach-mmp/include/mach/debug-macro.S
+++ b/arch/arm/mach-mmp/include/mach/debug-macro.S
@@ -9,13 +9,21 @@
* published by the Free Software Foundation.
*/
+#if defined(CONFIG_DEBUG_MMP_UART2)
+#define MMP_UART_OFFSET 0x00017000
+#elif defined(CONFIG_DEBUG_MMP_UART3)
+#define MMP_UART_OFFSET 0x00018000
+#else
+#error "Select uart for DEBUG_LL"
+#endif
+
#include <mach/addr-map.h>
.macro addruart, rp, rv, tmp
ldr \rp, =APB_PHYS_BASE @ physical
ldr \rv, =APB_VIRT_BASE @ virtual
- orr \rp, \rp, #0x00017000
- orr \rv, \rv, #0x00017000
+ orr \rp, \rp, #MMP_UART_OFFSET
+ orr \rv, \rv, #MMP_UART_OFFSET
.endm
#define UART_SHIFT 2
diff --git a/arch/arm/mach-mmp/irq.c b/arch/arm/mach-mmp/irq.c
index e60c7d98922b..3c71246cd994 100644
--- a/arch/arm/mach-mmp/irq.c
+++ b/arch/arm/mach-mmp/irq.c
@@ -153,10 +153,8 @@ static void icu_mux_irq_demux(unsigned int irq, struct irq_desc *desc)
status = readl_relaxed(data->reg_status) & ~mask;
if (status == 0)
break;
- n = find_first_bit(&status, BITS_PER_LONG);
- while (n < BITS_PER_LONG) {
+ for_each_set_bit(n, &status, BITS_PER_LONG) {
generic_handle_irq(icu_data[i].virq_base + n);
- n = find_next_bit(&status, BITS_PER_LONG, n + 1);
}
}
}
diff --git a/arch/arm/mach-mmp/ttc_dkb.c b/arch/arm/mach-mmp/ttc_dkb.c
index 7a7de2b12a62..ce55fd8821c4 100644
--- a/arch/arm/mach-mmp/ttc_dkb.c
+++ b/arch/arm/mach-mmp/ttc_dkb.c
@@ -177,12 +177,22 @@ static struct mv_usb_platform_data ttc_usb_pdata = {
#endif
#endif
+#ifdef CONFIG_MTD_NAND_PXA3xx
+static struct pxa3xx_nand_platform_data dkb_nand_info = {
+ .enable_arbiter = 1,
+ .num_cs = 1,
+};
+#endif
+
static void __init ttc_dkb_init(void)
{
mfp_config(ARRAY_AND_SIZE(ttc_dkb_pin_config));
/* on-chip devices */
pxa910_add_uart(1);
+#ifdef CONFIG_MTD_NAND_PXA3xx
+ pxa910_add_nand(&dkb_nand_info);
+#endif
/* off-chip devices */
pxa910_add_twsi(0, NULL, ARRAY_AND_SIZE(ttc_dkb_i2c_info));
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index b2740c800e8c..7902de151cc5 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -1,8 +1,12 @@
if ARCH_MSM
+comment "Qualcomm MSM SoC Type"
+ depends on (ARCH_MSM8X60 || ARCH_MSM8960)
+
choice
prompt "Qualcomm MSM SoC Type"
default ARCH_MSM7X00A
+ depends on !(ARCH_MSM8X60 || ARCH_MSM8960)
config ARCH_MSM7X00A
bool "MSM7x00A / MSM7x01A"
@@ -36,6 +40,8 @@ config ARCH_QSD8X50
select GPIO_MSM_V1
select MSM_PROC_COMM
+endchoice
+
config ARCH_MSM8X60
bool "MSM8X60"
select ARCH_MSM_SCORPIONMP
@@ -57,8 +63,6 @@ config ARCH_MSM8960
select MSM_SCM if SMP
select USE_OF
-endchoice
-
config MSM_HAS_DEBUG_UART_HS
bool
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h b/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h
index c6d38f1d0c98..199372e62def 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h
@@ -41,21 +41,10 @@
#define MSM8X60_QGIC_CPU_PHYS 0x02081000
#define MSM8X60_QGIC_CPU_SIZE SZ_4K
-#define MSM_ACC_BASE IOMEM(0xF0002000)
-#define MSM_ACC_PHYS 0x02001000
-#define MSM_ACC_SIZE SZ_4K
-
-#define MSM_GCC_BASE IOMEM(0xF0003000)
-#define MSM_GCC_PHYS 0x02082000
-#define MSM_GCC_SIZE SZ_4K
-
#define MSM_TLMM_BASE IOMEM(0xF0004000)
#define MSM_TLMM_PHYS 0x00800000
#define MSM_TLMM_SIZE SZ_16K
-#define MSM_SHARED_RAM_BASE IOMEM(0xF0100000)
-#define MSM_SHARED_RAM_SIZE SZ_1M
-
#define MSM8X60_TMR_PHYS 0x02000000
#define MSM8X60_TMR_SIZE SZ_4K
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap.h b/arch/arm/mach-msm/include/mach/msm_iomap.h
index 00afdfb8c38f..2ab7cf0919b3 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap.h
@@ -41,12 +41,11 @@
#include "msm_iomap-7x30.h"
#elif defined(CONFIG_ARCH_QSD8X50)
#include "msm_iomap-8x50.h"
-#elif defined(CONFIG_ARCH_MSM8X60)
-#include "msm_iomap-8x60.h"
#else
#include "msm_iomap-7x00.h"
#endif
+#include "msm_iomap-8x60.h"
#include "msm_iomap-8960.h"
#define MSM_DEBUG_UART_SIZE SZ_4K
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index 3854f6f20ce2..123ef9cbce1b 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -111,8 +111,6 @@ static struct map_desc msm8x60_io_desc[] __initdata = {
MSM_CHIP_DEVICE(QGIC_CPU, MSM8X60),
MSM_CHIP_DEVICE(TMR, MSM8X60),
MSM_CHIP_DEVICE(TMR0, MSM8X60),
- MSM_DEVICE(ACC),
- MSM_DEVICE(GCC),
#ifdef CONFIG_DEBUG_MSM8660_UART
MSM_DEVICE(DEBUG_UART),
#endif
diff --git a/arch/arm/mach-msm/smd.c b/arch/arm/mach-msm/smd.c
index 84183ed2ef79..c5a2eddc6cdc 100644
--- a/arch/arm/mach-msm/smd.c
+++ b/arch/arm/mach-msm/smd.c
@@ -49,13 +49,14 @@ static int msm_smd_debug_mask;
struct shared_info {
int ready;
- unsigned state;
+ void __iomem *state;
};
static unsigned dummy_state[SMSM_STATE_COUNT];
static struct shared_info smd_info = {
- .state = (unsigned) &dummy_state,
+ /* FIXME: not a real __iomem pointer */
+ .state = &dummy_state,
};
module_param_named(debug_mask, msm_smd_debug_mask,
@@ -789,22 +790,22 @@ void *smem_alloc(unsigned id, unsigned size)
return smem_find(id, size);
}
-void *smem_item(unsigned id, unsigned *size)
+void __iomem *smem_item(unsigned id, unsigned *size)
{
struct smem_shared *shared = (void *) MSM_SHARED_RAM_BASE;
struct smem_heap_entry *toc = shared->heap_toc;
if (id >= SMEM_NUM_ITEMS)
- return 0;
+ return NULL;
if (toc[id].allocated) {
*size = toc[id].size;
- return (void *) (MSM_SHARED_RAM_BASE + toc[id].offset);
+ return (MSM_SHARED_RAM_BASE + toc[id].offset);
} else {
*size = 0;
}
- return 0;
+ return NULL;
}
void *smem_find(unsigned id, unsigned size_in)
@@ -850,7 +851,7 @@ static irqreturn_t smsm_irq_handler(int irq, void *data)
int smsm_change_state(enum smsm_state_item item,
uint32_t clear_mask, uint32_t set_mask)
{
- unsigned long addr = smd_info.state + item * 4;
+ void __iomem *addr = smd_info.state + item * 4;
unsigned long flags;
unsigned state;
@@ -936,10 +937,10 @@ int smd_core_init(void)
/* wait for essential items to be initialized */
for (;;) {
unsigned size;
- void *state;
+ void __iomem *state;
state = smem_item(SMEM_SMSM_SHARED_STATE, &size);
if (size == SMSM_V1_SIZE || size == SMSM_V2_SIZE) {
- smd_info.state = (unsigned)state;
+ smd_info.state = state;
break;
}
}
diff --git a/arch/arm/mach-mxs/icoll.c b/arch/arm/mach-mxs/icoll.c
index 23ca9d083b2c..8fb23af154b3 100644
--- a/arch/arm/mach-mxs/icoll.c
+++ b/arch/arm/mach-mxs/icoll.c
@@ -19,20 +19,27 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/io.h>
-
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <asm/exception.h>
#include <mach/mxs.h>
#include <mach/common.h>
#define HW_ICOLL_VECTOR 0x0000
#define HW_ICOLL_LEVELACK 0x0010
#define HW_ICOLL_CTRL 0x0020
+#define HW_ICOLL_STAT_OFFSET 0x0070
#define HW_ICOLL_INTERRUPTn_SET(n) (0x0124 + (n) * 0x10)
#define HW_ICOLL_INTERRUPTn_CLR(n) (0x0128 + (n) * 0x10)
#define BM_ICOLL_INTERRUPTn_ENABLE 0x00000004
#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 0x1
+#define ICOLL_NUM_IRQS 128
+
static void __iomem *icoll_base = MXS_IO_ADDRESS(MXS_ICOLL_BASE_ADDR);
+static struct irq_domain *icoll_domain;
static void icoll_ack_irq(struct irq_data *d)
{
@@ -48,13 +55,13 @@ static void icoll_ack_irq(struct irq_data *d)
static void icoll_mask_irq(struct irq_data *d)
{
__raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
- icoll_base + HW_ICOLL_INTERRUPTn_CLR(d->irq));
+ icoll_base + HW_ICOLL_INTERRUPTn_CLR(d->hwirq));
}
static void icoll_unmask_irq(struct irq_data *d)
{
__raw_writel(BM_ICOLL_INTERRUPTn_ENABLE,
- icoll_base + HW_ICOLL_INTERRUPTn_SET(d->irq));
+ icoll_base + HW_ICOLL_INTERRUPTn_SET(d->hwirq));
}
static struct irq_chip mxs_icoll_chip = {
@@ -63,18 +70,56 @@ static struct irq_chip mxs_icoll_chip = {
.irq_unmask = icoll_unmask_irq,
};
-void __init icoll_init_irq(void)
+asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs)
{
- int i;
+ u32 irqnr;
+
+ do {
+ irqnr = __raw_readl(icoll_base + HW_ICOLL_STAT_OFFSET);
+ if (irqnr != 0x7f) {
+ __raw_writel(irqnr, icoll_base + HW_ICOLL_VECTOR);
+ irqnr = irq_find_mapping(icoll_domain, irqnr);
+ handle_IRQ(irqnr, regs);
+ continue;
+ }
+ break;
+ } while (1);
+}
+
+static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &mxs_icoll_chip, handle_level_irq);
+ set_irq_flags(virq, IRQF_VALID);
+
+ return 0;
+}
+static struct irq_domain_ops icoll_irq_domain_ops = {
+ .map = icoll_irq_domain_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+void __init icoll_of_init(struct device_node *np,
+ struct device_node *interrupt_parent)
+{
/*
* Interrupt Collector reset, which initializes the priority
* for each irq to level 0.
*/
mxs_reset_block(icoll_base + HW_ICOLL_CTRL);
- for (i = 0; i < MXS_INTERNAL_IRQS; i++) {
- irq_set_chip_and_handler(i, &mxs_icoll_chip, handle_level_irq);
- set_irq_flags(i, IRQF_VALID);
- }
+ icoll_domain = irq_domain_add_linear(np, ICOLL_NUM_IRQS,
+ &icoll_irq_domain_ops, NULL);
+ WARN_ON(!icoll_domain);
+}
+
+static const struct of_device_id icoll_of_match[] __initconst = {
+ {.compatible = "fsl,icoll", .data = icoll_of_init},
+ { /* sentinel */ }
+};
+
+void __init icoll_init_irq(void)
+{
+ of_irq_init(icoll_of_match);
}
diff --git a/arch/arm/mach-mxs/include/mach/common.h b/arch/arm/mach-mxs/include/mach/common.h
index 4dec79563f19..be5a9c93cb2a 100644
--- a/arch/arm/mach-mxs/include/mach/common.h
+++ b/arch/arm/mach-mxs/include/mach/common.h
@@ -13,7 +13,7 @@
extern const u32 *mxs_get_ocotp(void);
extern int mxs_reset_block(void __iomem *);
-extern void mxs_timer_init(int);
+extern void mxs_timer_init(void);
extern void mxs_restart(char, const char *);
extern int mxs_saif_clkmux_select(unsigned int clkmux);
@@ -24,5 +24,6 @@ extern int mx28_clocks_init(void);
extern void mx28_map_io(void);
extern void icoll_init_irq(void);
+extern void icoll_handle_irq(struct pt_regs *);
#endif /* __MACH_MXS_COMMON_H__ */
diff --git a/arch/arm/mach-mxs/include/mach/entry-macro.S b/arch/arm/mach-mxs/include/mach/entry-macro.S
deleted file mode 100644
index 0c14259705b9..000000000000
--- a/arch/arm/mach-mxs/include/mach/entry-macro.S
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Low-level IRQ helper macros for Freescale MXS-based
- *
- * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 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 <mach/mxs.h>
-
-#define MXS_ICOLL_VBASE MXS_IO_ADDRESS(MXS_ICOLL_BASE_ADDR)
-#define HW_ICOLL_STAT_OFFSET 0x70
-
- .macro get_irqnr_and_base, irqnr, irqstat, base, tmp
- ldr \irqnr, [\base, #HW_ICOLL_STAT_OFFSET]
- cmp \irqnr, #0x7F
- strne \irqnr, [\base]
- moveqs \irqnr, #0
- .endm
-
- .macro get_irqnr_preamble, base, tmp
- ldr \base, =MXS_ICOLL_VBASE
- .endm
diff --git a/arch/arm/mach-mxs/include/mach/irqs.h b/arch/arm/mach-mxs/include/mach/irqs.h
deleted file mode 100644
index f771039b814a..000000000000
--- a/arch/arm/mach-mxs/include/mach/irqs.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. 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.
- */
-
-#ifndef __MACH_MXS_IRQS_H__
-#define __MACH_MXS_IRQS_H__
-
-#define MXS_INTERNAL_IRQS 128
-
-#define MXS_GPIO_IRQ_START MXS_INTERNAL_IRQS
-
-/* the maximum for MXS-based */
-#define MXS_GPIO_IRQS (32 * 5)
-
-/*
- * The next 16 interrupts are for board specific purposes. Since
- * the kernel can only run on one machine at a time, we can re-use
- * these. If you need more, increase MXS_BOARD_IRQS, but keep it
- * within sensible limits.
- */
-#define MXS_BOARD_IRQ_START (MXS_GPIO_IRQ_START + MXS_GPIO_IRQS)
-#define MXS_BOARD_IRQS 16
-
-#define NR_IRQS (MXS_BOARD_IRQ_START + MXS_BOARD_IRQS)
-
-#endif /* __MACH_MXS_IRQS_H__ */
diff --git a/arch/arm/mach-mxs/mach-mxs.c b/arch/arm/mach-mxs/mach-mxs.c
index 433af893ad8a..4748ec551a68 100644
--- a/arch/arm/mach-mxs/mach-mxs.c
+++ b/arch/arm/mach-mxs/mach-mxs.c
@@ -17,10 +17,8 @@
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/init.h>
-#include <linux/irqdomain.h>
#include <linux/micrel_phy.h>
#include <linux/mxsfb.h>
-#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/phy.h>
#include <linux/pinctrl/consumer.h>
@@ -141,37 +139,6 @@ static struct of_dev_auxdata mxs_auxdata_lookup[] __initdata = {
{ /* sentinel */ }
};
-static int __init mxs_icoll_add_irq_domain(struct device_node *np,
- struct device_node *interrupt_parent)
-{
- irq_domain_add_legacy(np, 128, 0, 0, &irq_domain_simple_ops, NULL);
-
- return 0;
-}
-
-static int __init mxs_gpio_add_irq_domain(struct device_node *np,
- struct device_node *interrupt_parent)
-{
- static int gpio_irq_base = MXS_GPIO_IRQ_START;
-
- irq_domain_add_legacy(np, 32, gpio_irq_base, 0, &irq_domain_simple_ops, NULL);
- gpio_irq_base += 32;
-
- return 0;
-}
-
-static const struct of_device_id mxs_irq_match[] __initconst = {
- { .compatible = "fsl,mxs-icoll", .data = mxs_icoll_add_irq_domain, },
- { .compatible = "fsl,mxs-gpio", .data = mxs_gpio_add_irq_domain, },
- { /* sentinel */ }
-};
-
-static void __init mxs_dt_init_irq(void)
-{
- icoll_init_irq();
- of_irq_init(mxs_irq_match);
-}
-
static void __init imx23_timer_init(void)
{
mx23_clocks_init();
@@ -305,7 +272,7 @@ static void __init apx4devkit_init(void)
enable_clk_enet_out();
if (IS_BUILTIN(CONFIG_PHYLIB))
- phy_register_fixup_for_uid(PHY_ID_KS8051, MICREL_PHY_ID_MASK,
+ phy_register_fixup_for_uid(PHY_ID_KSZ8051, MICREL_PHY_ID_MASK,
apx4devkit_phy_fixup);
mxsfb_pdata.mode_list = apx4devkit_video_modes;
@@ -421,7 +388,8 @@ static const char *imx28_dt_compat[] __initdata = {
DT_MACHINE_START(IMX23, "Freescale i.MX23 (Device Tree)")
.map_io = mx23_map_io,
- .init_irq = mxs_dt_init_irq,
+ .init_irq = icoll_init_irq,
+ .handle_irq = icoll_handle_irq,
.timer = &imx23_timer,
.init_machine = mxs_machine_init,
.dt_compat = imx23_dt_compat,
@@ -430,7 +398,8 @@ MACHINE_END
DT_MACHINE_START(IMX28, "Freescale i.MX28 (Device Tree)")
.map_io = mx28_map_io,
- .init_irq = mxs_dt_init_irq,
+ .init_irq = icoll_init_irq,
+ .handle_irq = icoll_handle_irq,
.timer = &imx28_timer,
.init_machine = mxs_machine_init,
.dt_compat = imx28_dt_compat,
diff --git a/arch/arm/mach-mxs/timer.c b/arch/arm/mach-mxs/timer.c
index 02d36de9c4e8..7c3792613392 100644
--- a/arch/arm/mach-mxs/timer.c
+++ b/arch/arm/mach-mxs/timer.c
@@ -25,6 +25,8 @@
#include <linux/irq.h>
#include <linux/clockchips.h>
#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
#include <asm/mach/time.h>
#include <mach/mxs.h>
@@ -244,9 +246,17 @@ static int __init mxs_clocksource_init(struct clk *timer_clk)
return 0;
}
-void __init mxs_timer_init(int irq)
+void __init mxs_timer_init(void)
{
+ struct device_node *np;
struct clk *timer_clk;
+ int irq;
+
+ np = of_find_compatible_node(NULL, NULL, "fsl,timrot");
+ if (!np) {
+ pr_err("%s: failed find timrot node\n", __func__);
+ return;
+ }
timer_clk = clk_get_sys("timrot", NULL);
if (IS_ERR(timer_clk)) {
@@ -295,5 +305,6 @@ void __init mxs_timer_init(int irq)
mxs_clockevent_init(timer_clk);
/* Make irqs happen */
+ irq = irq_of_parse_and_map(np, 0);
setup_irq(irq, &mxs_timer_irq);
}
diff --git a/arch/arm/mach-nomadik/board-nhk8815.c b/arch/arm/mach-nomadik/board-nhk8815.c
index 381c08027df4..92a00260d074 100644
--- a/arch/arm/mach-nomadik/board-nhk8815.c
+++ b/arch/arm/mach-nomadik/board-nhk8815.c
@@ -112,8 +112,7 @@ static struct mtd_partition nhk8815_partitions[] = {
static struct nomadik_nand_platform_data nhk8815_nand_data = {
.parts = nhk8815_partitions,
.nparts = ARRAY_SIZE(nhk8815_partitions),
- .options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING \
- | NAND_NO_READRDY,
+ .options = NAND_COPYBACK | NAND_CACHEPRG | NAND_NO_PADDING,
.init = nhk8815_nand_init,
};
diff --git a/arch/arm/mach-nomadik/include/mach/hardware.h b/arch/arm/mach-nomadik/include/mach/hardware.h
index 6316dba3bfc8..02035e459f50 100644
--- a/arch/arm/mach-nomadik/include/mach/hardware.h
+++ b/arch/arm/mach-nomadik/include/mach/hardware.h
@@ -30,7 +30,7 @@
- NOMADIK_IO_VIRTUAL + NOMADIK_IO_PHYSICAL)
/* used in asm code, so no casts */
-#define IO_ADDRESS(x) ((x) - NOMADIK_IO_PHYSICAL + NOMADIK_IO_VIRTUAL)
+#define IO_ADDRESS(x) IOMEM((x) - NOMADIK_IO_PHYSICAL + NOMADIK_IO_VIRTUAL)
/*
* Base address defination for Nomadik Onchip Logic Block
diff --git a/arch/arm/mach-nomadik/include/mach/uncompress.h b/arch/arm/mach-nomadik/include/mach/uncompress.h
index 071003bc8456..7d4687e9cbdf 100644
--- a/arch/arm/mach-nomadik/include/mach/uncompress.h
+++ b/arch/arm/mach-nomadik/include/mach/uncompress.h
@@ -27,10 +27,10 @@
struct amba_device;
#include <linux/amba/serial.h>
-#define NOMADIK_UART_DR 0x101FB000
-#define NOMADIK_UART_LCRH 0x101FB02c
-#define NOMADIK_UART_CR 0x101FB030
-#define NOMADIK_UART_FR 0x101FB018
+#define NOMADIK_UART_DR (void __iomem *)0x101FB000
+#define NOMADIK_UART_LCRH (void __iomem *)0x101FB02c
+#define NOMADIK_UART_CR (void __iomem *)0x101FB030
+#define NOMADIK_UART_FR (void __iomem *)0x101FB018
static void putc(const char c)
{
diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile
index 398e9e53e189..cd169c386161 100644
--- a/arch/arm/mach-omap1/Makefile
+++ b/arch/arm/mach-omap1/Makefile
@@ -61,14 +61,6 @@ obj-$(CONFIG_ARCH_OMAP850) += gpio7xx.o
obj-$(CONFIG_ARCH_OMAP15XX) += gpio15xx.o
obj-$(CONFIG_ARCH_OMAP16XX) += gpio16xx.o
-# LEDs support
-led-$(CONFIG_MACH_OMAP_H2) += leds-h2p2-debug.o
-led-$(CONFIG_MACH_OMAP_H3) += leds-h2p2-debug.o
-led-$(CONFIG_MACH_OMAP_INNOVATOR) += leds-innovator.o
-led-$(CONFIG_MACH_OMAP_PERSEUS2) += leds-h2p2-debug.o
-led-$(CONFIG_MACH_OMAP_OSK) += leds-osk.o
-obj-$(CONFIG_LEDS) += $(led-y)
-
ifneq ($(CONFIG_FB_OMAP),)
obj-y += lcd_dma.o
endif
diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c
index af283a2bc7c7..376f7f29ef77 100644
--- a/arch/arm/mach-omap1/board-h2.c
+++ b/arch/arm/mach-omap1/board-h2.c
@@ -32,6 +32,7 @@
#include <linux/smc91x.h>
#include <linux/omapfb.h>
#include <linux/platform_data/gpio-omap.h>
+#include <linux/leds.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
@@ -307,12 +308,39 @@ static struct platform_device h2_irda_device = {
.resource = h2_irda_resources,
};
+static struct gpio_led h2_gpio_led_pins[] = {
+ {
+ .name = "h2:red",
+ .default_trigger = "heartbeat",
+ .gpio = 3,
+ },
+ {
+ .name = "h2:green",
+ .default_trigger = "cpu0",
+ .gpio = OMAP_MPUIO(4),
+ },
+};
+
+static struct gpio_led_platform_data h2_gpio_led_data = {
+ .leds = h2_gpio_led_pins,
+ .num_leds = ARRAY_SIZE(h2_gpio_led_pins),
+};
+
+static struct platform_device h2_gpio_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &h2_gpio_led_data,
+ },
+};
+
static struct platform_device *h2_devices[] __initdata = {
&h2_nor_device,
&h2_nand_device,
&h2_smc91x_device,
&h2_irda_device,
&h2_kp_device,
+ &h2_gpio_leds,
};
static void __init h2_init_smc91x(void)
@@ -407,6 +435,10 @@ static void __init h2_init(void)
omap_cfg_reg(E19_1610_KBR4);
omap_cfg_reg(N19_1610_KBR5);
+ /* GPIO based LEDs */
+ omap_cfg_reg(P18_1610_GPIO3);
+ omap_cfg_reg(MPUIO4);
+
h2_smc91x_resources[1].start = gpio_to_irq(0);
h2_smc91x_resources[1].end = gpio_to_irq(0);
platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices));
diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c
index 06d11b1ee9c6..ededdb7ef28c 100644
--- a/arch/arm/mach-omap1/board-h3.c
+++ b/arch/arm/mach-omap1/board-h3.c
@@ -32,6 +32,7 @@
#include <linux/smc91x.h>
#include <linux/omapfb.h>
#include <linux/platform_data/gpio-omap.h>
+#include <linux/leds.h>
#include <asm/setup.h>
#include <asm/page.h>
@@ -325,6 +326,32 @@ static struct spi_board_info h3_spi_board_info[] __initdata = {
},
};
+static struct gpio_led h3_gpio_led_pins[] = {
+ {
+ .name = "h3:red",
+ .default_trigger = "heartbeat",
+ .gpio = 3,
+ },
+ {
+ .name = "h3:green",
+ .default_trigger = "cpu0",
+ .gpio = OMAP_MPUIO(4),
+ },
+};
+
+static struct gpio_led_platform_data h3_gpio_led_data = {
+ .leds = h3_gpio_led_pins,
+ .num_leds = ARRAY_SIZE(h3_gpio_led_pins),
+};
+
+static struct platform_device h3_gpio_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &h3_gpio_led_data,
+ },
+};
+
static struct platform_device *devices[] __initdata = {
&nor_device,
&nand_device,
@@ -332,6 +359,7 @@ static struct platform_device *devices[] __initdata = {
&intlat_device,
&h3_kp_device,
&h3_lcd_device,
+ &h3_gpio_leds,
};
static struct omap_usb_config h3_usb_config __initdata = {
@@ -399,6 +427,10 @@ static void __init h3_init(void)
omap_cfg_reg(E19_1610_KBR4);
omap_cfg_reg(N19_1610_KBR5);
+ /* GPIO based LEDs */
+ omap_cfg_reg(P18_1610_GPIO3);
+ omap_cfg_reg(MPUIO4);
+
smc91x_resources[1].start = gpio_to_irq(40);
smc91x_resources[1].end = gpio_to_irq(40);
platform_add_devices(devices, ARRAY_SIZE(devices));
diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c
index 2f1f9b967576..5973945a8741 100644
--- a/arch/arm/mach-omap1/board-osk.c
+++ b/arch/arm/mach-omap1/board-osk.c
@@ -382,10 +382,37 @@ static struct platform_device osk5912_lcd_device = {
.id = -1,
};
+static struct gpio_led mistral_gpio_led_pins[] = {
+ {
+ .name = "mistral:red",
+ .default_trigger = "heartbeat",
+ .gpio = 3,
+ },
+ {
+ .name = "mistral:green",
+ .default_trigger = "cpu0",
+ .gpio = OMAP_MPUIO(4),
+ },
+};
+
+static struct gpio_led_platform_data mistral_gpio_led_data = {
+ .leds = mistral_gpio_led_pins,
+ .num_leds = ARRAY_SIZE(mistral_gpio_led_pins),
+};
+
+static struct platform_device mistral_gpio_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &mistral_gpio_led_data,
+ },
+};
+
static struct platform_device *mistral_devices[] __initdata = {
&osk5912_kp_device,
&mistral_bl_device,
&osk5912_lcd_device,
+ &mistral_gpio_leds,
};
static int mistral_get_pendown_state(void)
@@ -510,6 +537,12 @@ static void __init osk_mistral_init(void)
if (gpio_request(2, "lcd_pwr") == 0)
gpio_direction_output(2, 1);
+ /*
+ * GPIO based LEDs
+ */
+ omap_cfg_reg(P18_1610_GPIO3);
+ omap_cfg_reg(MPUIO4);
+
i2c_register_board_info(1, mistral_i2c_board_info,
ARRAY_SIZE(mistral_i2c_board_info));
diff --git a/arch/arm/mach-omap1/leds-h2p2-debug.c b/arch/arm/mach-omap1/leds-h2p2-debug.c
deleted file mode 100644
index 6f958aec9459..000000000000
--- a/arch/arm/mach-omap1/leds-h2p2-debug.c
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * linux/arch/arm/mach-omap1/leds-h2p2-debug.c
- *
- * Copyright 2003 by Texas Instruments Incorporated
- *
- * There are 16 LEDs on the debug board (all green); four may be used
- * for logical 'green', 'amber', 'red', and 'blue' (after "claiming").
- *
- * The "surfer" expansion board and H2 sample board also have two-color
- * green+red LEDs (in parallel), used here for timer and idle indicators.
- */
-#include <linux/gpio.h>
-#include <linux/init.h>
-#include <linux/kernel_stat.h>
-#include <linux/sched.h>
-#include <linux/io.h>
-#include <linux/platform_data/gpio-omap.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-#include <asm/mach-types.h>
-
-#include <plat/fpga.h>
-
-#include "leds.h"
-
-
-#define GPIO_LED_RED 3
-#define GPIO_LED_GREEN OMAP_MPUIO(4)
-
-
-#define LED_STATE_ENABLED 0x01
-#define LED_STATE_CLAIMED 0x02
-#define LED_TIMER_ON 0x04
-
-#define GPIO_IDLE GPIO_LED_GREEN
-#define GPIO_TIMER GPIO_LED_RED
-
-
-void h2p2_dbg_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- static struct h2p2_dbg_fpga __iomem *fpga;
- static u16 led_state, hw_led_state;
-
- local_irq_save(flags);
-
- if (!(led_state & LED_STATE_ENABLED) && evt != led_start)
- goto done;
-
- switch (evt) {
- case led_start:
- if (!fpga)
- fpga = ioremap(H2P2_DBG_FPGA_START,
- H2P2_DBG_FPGA_SIZE);
- if (fpga) {
- led_state |= LED_STATE_ENABLED;
- __raw_writew(~0, &fpga->leds);
- }
- break;
-
- case led_stop:
- case led_halted:
- /* all leds off during suspend or shutdown */
-
- if (! machine_is_omap_perseus2()) {
- gpio_set_value(GPIO_TIMER, 0);
- gpio_set_value(GPIO_IDLE, 0);
- }
-
- led_state &= ~LED_STATE_ENABLED;
- if (fpga) {
- __raw_writew(~0, &fpga->leds);
- if (evt == led_halted) {
- iounmap(fpga);
- fpga = NULL;
- }
- }
-
- goto done;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = 0;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- led_state ^= LED_TIMER_ON;
-
- if (machine_is_omap_perseus2())
- hw_led_state ^= H2P2_DBG_FPGA_P2_LED_TIMER;
- else {
- gpio_set_value(GPIO_TIMER, led_state & LED_TIMER_ON);
- goto done;
- }
-
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- if (machine_is_omap_perseus2())
- hw_led_state |= H2P2_DBG_FPGA_P2_LED_IDLE;
- else {
- gpio_set_value(GPIO_IDLE, 1);
- goto done;
- }
-
- break;
-
- case led_idle_end:
- if (machine_is_omap_perseus2())
- hw_led_state &= ~H2P2_DBG_FPGA_P2_LED_IDLE;
- else {
- gpio_set_value(GPIO_IDLE, 0);
- goto done;
- }
-
- break;
-#endif
-
- case led_green_on:
- hw_led_state |= H2P2_DBG_FPGA_LED_GREEN;
- break;
- case led_green_off:
- hw_led_state &= ~H2P2_DBG_FPGA_LED_GREEN;
- break;
-
- case led_amber_on:
- hw_led_state |= H2P2_DBG_FPGA_LED_AMBER;
- break;
- case led_amber_off:
- hw_led_state &= ~H2P2_DBG_FPGA_LED_AMBER;
- break;
-
- case led_red_on:
- hw_led_state |= H2P2_DBG_FPGA_LED_RED;
- break;
- case led_red_off:
- hw_led_state &= ~H2P2_DBG_FPGA_LED_RED;
- break;
-
- case led_blue_on:
- hw_led_state |= H2P2_DBG_FPGA_LED_BLUE;
- break;
- case led_blue_off:
- hw_led_state &= ~H2P2_DBG_FPGA_LED_BLUE;
- break;
-
- default:
- break;
- }
-
-
- /*
- * Actually burn the LEDs
- */
- if (led_state & LED_STATE_ENABLED && fpga)
- __raw_writew(~hw_led_state, &fpga->leds);
-
-done:
- local_irq_restore(flags);
-}
diff --git a/arch/arm/mach-omap1/leds-innovator.c b/arch/arm/mach-omap1/leds-innovator.c
deleted file mode 100644
index 3a066ee8d02c..000000000000
--- a/arch/arm/mach-omap1/leds-innovator.c
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * linux/arch/arm/mach-omap1/leds-innovator.c
- */
-#include <linux/init.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-
-#include "leds.h"
-
-
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
-
-static unsigned int led_state;
-static unsigned int hw_led_state;
-
-void innovator_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- switch (evt) {
- case led_start:
- hw_led_state = 0;
- led_state = LED_STATE_ENABLED;
- break;
-
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- hw_led_state = 0;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = 0;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = 0;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state ^= 0;
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= 0;
- break;
-
- case led_idle_end:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~0;
- break;
-#endif
-
- case led_halted:
- break;
-
- case led_green_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~0;
- break;
-
- case led_green_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= 0;
- break;
-
- case led_amber_on:
- break;
-
- case led_amber_off:
- break;
-
- case led_red_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~0;
- break;
-
- case led_red_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= 0;
- break;
-
- default:
- break;
- }
-
- local_irq_restore(flags);
-}
diff --git a/arch/arm/mach-omap1/leds-osk.c b/arch/arm/mach-omap1/leds-osk.c
deleted file mode 100644
index 936ed426b84f..000000000000
--- a/arch/arm/mach-omap1/leds-osk.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * linux/arch/arm/mach-omap1/leds-osk.c
- *
- * LED driver for OSK with optional Mistral QVGA board
- */
-#include <linux/gpio.h>
-#include <linux/init.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-
-#include "leds.h"
-
-
-#define LED_STATE_ENABLED (1 << 0)
-#define LED_STATE_CLAIMED (1 << 1)
-static u8 led_state;
-
-#define TIMER_LED (1 << 3) /* Mistral board */
-#define IDLE_LED (1 << 4) /* Mistral board */
-static u8 hw_led_state;
-
-
-#ifdef CONFIG_OMAP_OSK_MISTRAL
-
-/* For now, all system indicators require the Mistral board, since that
- * LED can be manipulated without a task context. This LED is either red,
- * or green, but not both; it can't give the full "disco led" effect.
- */
-
-#define GPIO_LED_RED 3
-#define GPIO_LED_GREEN OMAP_MPUIO(4)
-
-static void mistral_setled(void)
-{
- int red = 0;
- int green = 0;
-
- if (hw_led_state & TIMER_LED)
- red = 1;
- else if (hw_led_state & IDLE_LED)
- green = 1;
- /* else both sides are disabled */
-
- gpio_set_value(GPIO_LED_GREEN, green);
- gpio_set_value(GPIO_LED_RED, red);
-}
-
-#endif
-
-void osk_leds_event(led_event_t evt)
-{
- unsigned long flags;
- u16 leds;
-
- local_irq_save(flags);
-
- if (!(led_state & LED_STATE_ENABLED) && evt != led_start)
- goto done;
-
- leds = hw_led_state;
- switch (evt) {
- case led_start:
- led_state |= LED_STATE_ENABLED;
- hw_led_state = 0;
- leds = ~0;
- break;
-
- case led_halted:
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- hw_led_state = 0;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = 0;
- leds = ~0;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = 0;
- break;
-
-#ifdef CONFIG_OMAP_OSK_MISTRAL
-
- case led_timer:
- hw_led_state ^= TIMER_LED;
- mistral_setled();
- break;
-
- case led_idle_start: /* idle == off */
- hw_led_state &= ~IDLE_LED;
- mistral_setled();
- break;
-
- case led_idle_end:
- hw_led_state |= IDLE_LED;
- mistral_setled();
- break;
-
-#endif /* CONFIG_OMAP_OSK_MISTRAL */
-
- default:
- break;
- }
-
- leds ^= hw_led_state;
-
-done:
- local_irq_restore(flags);
-}
diff --git a/arch/arm/mach-omap1/leds.c b/arch/arm/mach-omap1/leds.c
deleted file mode 100644
index 4071479f7106..000000000000
--- a/arch/arm/mach-omap1/leds.c
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * linux/arch/arm/mach-omap1/leds.c
- *
- * OMAP LEDs dispatcher
- */
-#include <linux/gpio.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/platform_data/gpio-omap.h>
-
-#include <asm/leds.h>
-#include <asm/mach-types.h>
-
-#include <mach/mux.h>
-
-#include "leds.h"
-
-static int __init
-omap_leds_init(void)
-{
- if (!cpu_class_is_omap1())
- return -ENODEV;
-
- if (machine_is_omap_innovator())
- leds_event = innovator_leds_event;
-
- else if (machine_is_omap_h2()
- || machine_is_omap_h3()
- || machine_is_omap_perseus2())
- leds_event = h2p2_dbg_leds_event;
-
- else if (machine_is_omap_osk())
- leds_event = osk_leds_event;
-
- else
- return -1;
-
- if (machine_is_omap_h2()
- || machine_is_omap_h3()
-#ifdef CONFIG_OMAP_OSK_MISTRAL
- || machine_is_omap_osk()
-#endif
- ) {
-
- /* LED1/LED2 pins can be used as GPIO (as done here), or by
- * the LPG (works even in deep sleep!), to drive a bicolor
- * LED on the H2 sample board, and another on the H2/P2
- * "surfer" expansion board.
- *
- * The same pins drive a LED on the OSK Mistral board, but
- * that's a different kind of LED (just one color at a time).
- */
- omap_cfg_reg(P18_1610_GPIO3);
- if (gpio_request(3, "LED red") == 0)
- gpio_direction_output(3, 1);
- else
- printk(KERN_WARNING "LED: can't get GPIO3/red?\n");
-
- omap_cfg_reg(MPUIO4);
- if (gpio_request(OMAP_MPUIO(4), "LED green") == 0)
- gpio_direction_output(OMAP_MPUIO(4), 1);
- else
- printk(KERN_WARNING "LED: can't get MPUIO4/green?\n");
- }
-
- leds_event(led_start);
- return 0;
-}
-
-__initcall(omap_leds_init);
diff --git a/arch/arm/mach-omap1/leds.h b/arch/arm/mach-omap1/leds.h
deleted file mode 100644
index a1e9fedc376c..000000000000
--- a/arch/arm/mach-omap1/leds.h
+++ /dev/null
@@ -1,3 +0,0 @@
-extern void innovator_leds_event(led_event_t evt);
-extern void h2p2_dbg_leds_event(led_event_t evt);
-extern void osk_leds_event(led_event_t evt);
diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c
index 4062480bfec7..4d4816fd6fc9 100644
--- a/arch/arm/mach-omap1/time.c
+++ b/arch/arm/mach-omap1/time.c
@@ -44,7 +44,6 @@
#include <linux/clockchips.h>
#include <linux/io.h>
-#include <asm/leds.h>
#include <asm/irq.h>
#include <asm/sched_clock.h>
diff --git a/arch/arm/mach-omap1/timer32k.c b/arch/arm/mach-omap1/timer32k.c
index eae49c3980c9..74529549130c 100644
--- a/arch/arm/mach-omap1/timer32k.c
+++ b/arch/arm/mach-omap1/timer32k.c
@@ -46,7 +46,6 @@
#include <linux/clockchips.h>
#include <linux/io.h>
-#include <asm/leds.h>
#include <asm/irq.h>
#include <asm/mach/irq.h>
#include <asm/mach/time.h>
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index eef99b77c40b..a6219eaf1f68 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -25,6 +25,9 @@ config ARCH_OMAP2PLUS_TYPICAL
config SOC_HAS_OMAP2_SDRC
bool "OMAP2 SDRAM Controller support"
+config SOC_HAS_REALTIME_COUNTER
+ bool "Real time free running counter"
+
config ARCH_OMAP2
bool "TI OMAP2"
depends on ARCH_OMAP2PLUS
@@ -45,6 +48,7 @@ config ARCH_OMAP3
select ARM_CPU_SUSPEND if PM
select MULTI_IRQ_HANDLER
select SOC_HAS_OMAP2_SDRC
+ select OMAP_INTERCONNECT
config ARCH_OMAP4
bool "TI OMAP4"
@@ -64,6 +68,7 @@ config ARCH_OMAP4
select USB_ARCH_HAS_EHCI if USB_SUPPORT
select ARM_CPU_SUSPEND if PM
select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP
+ select OMAP_INTERCONNECT
config SOC_OMAP5
bool "TI OMAP5"
@@ -71,6 +76,8 @@ config SOC_OMAP5
select ARM_GIC
select HAVE_SMP
select ARM_CPU_SUSPEND if PM
+ select SOC_HAS_REALTIME_COUNTER
+ select ARM_ARCH_TIMER
comment "OMAP Core Type"
depends on ARCH_OMAP2
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 845202358ddc..7d6abda3b74e 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -180,11 +180,6 @@ obj-$(CONFIG_ARCH_OMAP4) += omap_hwmod_44xx_data.o
# EMU peripherals
obj-$(CONFIG_OMAP3_EMU) += emu.o
-# L3 interconnect
-obj-$(CONFIG_ARCH_OMAP3) += omap_l3_smx.o
-obj-$(CONFIG_ARCH_OMAP4) += omap_l3_noc.o
-obj-$(CONFIG_SOC_OMAP5) += omap_l3_noc.o
-
obj-$(CONFIG_OMAP_MBOX_FWK) += mailbox_mach.o
mailbox_mach-objs := mailbox.o
diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c
index 6fe90796d462..a88809a59ea9 100644
--- a/arch/arm/mach-omap2/board-4430sdp.c
+++ b/arch/arm/mach-omap2/board-4430sdp.c
@@ -545,6 +545,14 @@ static struct twl6040_platform_data twl6040_data = {
.audpwron_gpio = 127,
};
+static struct i2c_board_info __initdata sdp4430_i2c_1_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("twl6040", 0x4b),
+ .irq = 119 + OMAP44XX_IRQ_GIC_START,
+ .platform_data = &twl6040_data,
+ },
+};
+
static struct twl4030_platform_data sdp4430_twldata = {
/* Regulators */
.vusim = &sdp4430_vusim,
@@ -578,8 +586,8 @@ static int __init omap4_i2c_init(void)
TWL_COMMON_REGULATOR_CLK32KG |
TWL_COMMON_REGULATOR_V1V8 |
TWL_COMMON_REGULATOR_V2V1);
- omap4_pmic_init("twl6030", &sdp4430_twldata,
- &twl6040_data, 119 + OMAP44XX_IRQ_GIC_START);
+ omap4_pmic_init("twl6030", &sdp4430_twldata, sdp4430_i2c_1_boardinfo,
+ ARRAY_SIZE(sdp4430_i2c_1_boardinfo));
omap_register_i2c_bus(2, 400, NULL, 0);
omap_register_i2c_bus(3, 400, sdp4430_i2c_3_boardinfo,
ARRAY_SIZE(sdp4430_i2c_3_boardinfo));
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c
index 3fe5f0f69c73..c64e565bdef5 100644
--- a/arch/arm/mach-omap2/board-omap3evm.c
+++ b/arch/arm/mach-omap2/board-omap3evm.c
@@ -32,6 +32,7 @@
#include <linux/spi/ads7846.h>
#include <linux/i2c/twl.h>
#include <linux/usb/otg.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <linux/smsc911x.h>
#include <linux/wl12xx.h>
diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c
index 8ebb16c5182e..e0dd70b9d917 100644
--- a/arch/arm/mach-omap2/board-omap4panda.c
+++ b/arch/arm/mach-omap2/board-omap4panda.c
@@ -264,6 +264,14 @@ static struct twl6040_platform_data twl6040_data = {
.audpwron_gpio = 127,
};
+static struct i2c_board_info __initdata panda_i2c_1_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("twl6040", 0x4b),
+ .irq = 119 + OMAP44XX_IRQ_GIC_START,
+ .platform_data = &twl6040_data,
+ },
+};
+
/* Panda board uses the common PMIC configuration */
static struct twl4030_platform_data omap4_panda_twldata;
@@ -291,8 +299,8 @@ static int __init omap4_panda_i2c_init(void)
TWL_COMMON_REGULATOR_CLK32KG |
TWL_COMMON_REGULATOR_V1V8 |
TWL_COMMON_REGULATOR_V2V1);
- omap4_pmic_init("twl6030", &omap4_panda_twldata,
- &twl6040_data, 119 + OMAP44XX_IRQ_GIC_START);
+ omap4_pmic_init("twl6030", &omap4_panda_twldata, panda_i2c_1_boardinfo,
+ ARRAY_SIZE(panda_i2c_1_boardinfo));
omap_register_i2c_bus(2, 400, NULL, 0);
/*
* Bus 3 is attached to the DVI port where devices like the pico DLP
diff --git a/arch/arm/mach-omap2/gpio.c b/arch/arm/mach-omap2/gpio.c
index e7b246da02d0..d1058f16fb40 100644
--- a/arch/arm/mach-omap2/gpio.c
+++ b/arch/arm/mach-omap2/gpio.c
@@ -123,6 +123,7 @@ static int __init omap2_gpio_dev_init(struct omap_hwmod *oh, void *unused)
break;
default:
WARN(1, "Invalid gpio bank_type\n");
+ kfree(pdata->regs);
kfree(pdata);
return -EINVAL;
}
diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c
index b3275babf192..5d3b4f4f81ae 100644
--- a/arch/arm/mach-omap2/omap-wakeupgen.c
+++ b/arch/arm/mach-omap2/omap-wakeupgen.c
@@ -230,13 +230,7 @@ static inline void omap4_irq_save_context(void)
/* Save AuxBoot* registers */
val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
__raw_writel(val, sar_base + AUXCOREBOOT0_OFFSET);
- val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
- __raw_writel(val, sar_base + AUXCOREBOOT1_OFFSET);
-
- /* Save SyncReq generation logic */
- val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
- __raw_writel(val, sar_base + AUXCOREBOOT0_OFFSET);
- val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_0);
+ val = __raw_readl(wakeupgen_base + OMAP_AUX_CORE_BOOT_1);
__raw_writel(val, sar_base + AUXCOREBOOT1_OFFSET);
/* Save SyncReq generation logic */
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index f9bcb24cd515..c7dcb606cd0c 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -5889,6 +5889,12 @@ static struct omap_hwmod_addr_space omap44xx_usb_otg_hs_addrs[] = {
.pa_end = 0x4a0ab003,
.flags = ADDR_TYPE_RT
},
+ {
+ /* XXX: Remove this once control module driver is in place */
+ .pa_start = 0x4a00233c,
+ .pa_end = 0x4a00233f,
+ .flags = ADDR_TYPE_RT
+ },
{ }
};
diff --git a/arch/arm/mach-omap2/omap_phy_internal.c b/arch/arm/mach-omap2/omap_phy_internal.c
index 593eaea35cec..d992db8ff0b0 100644
--- a/arch/arm/mach-omap2/omap_phy_internal.c
+++ b/arch/arm/mach-omap2/omap_phy_internal.c
@@ -33,144 +33,6 @@
#include "soc.h"
#include "control.h"
-/* OMAP control module register for UTMI PHY */
-#define CONTROL_DEV_CONF 0x300
-#define PHY_PD 0x1
-
-#define USBOTGHS_CONTROL 0x33c
-#define AVALID BIT(0)
-#define BVALID BIT(1)
-#define VBUSVALID BIT(2)
-#define SESSEND BIT(3)
-#define IDDIG BIT(4)
-
-static struct clk *phyclk, *clk48m, *clk32k;
-static void __iomem *ctrl_base;
-static int usbotghs_control;
-
-int omap4430_phy_init(struct device *dev)
-{
- ctrl_base = ioremap(OMAP443X_SCM_BASE, SZ_1K);
- if (!ctrl_base) {
- pr_err("control module ioremap failed\n");
- return -ENOMEM;
- }
- /* Power down the phy */
- __raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
-
- if (!dev) {
- iounmap(ctrl_base);
- return 0;
- }
-
- phyclk = clk_get(dev, "ocp2scp_usb_phy_ick");
- if (IS_ERR(phyclk)) {
- dev_err(dev, "cannot clk_get ocp2scp_usb_phy_ick\n");
- iounmap(ctrl_base);
- return PTR_ERR(phyclk);
- }
-
- clk48m = clk_get(dev, "ocp2scp_usb_phy_phy_48m");
- if (IS_ERR(clk48m)) {
- dev_err(dev, "cannot clk_get ocp2scp_usb_phy_phy_48m\n");
- clk_put(phyclk);
- iounmap(ctrl_base);
- return PTR_ERR(clk48m);
- }
-
- clk32k = clk_get(dev, "usb_phy_cm_clk32k");
- if (IS_ERR(clk32k)) {
- dev_err(dev, "cannot clk_get usb_phy_cm_clk32k\n");
- clk_put(phyclk);
- clk_put(clk48m);
- iounmap(ctrl_base);
- return PTR_ERR(clk32k);
- }
- return 0;
-}
-
-int omap4430_phy_set_clk(struct device *dev, int on)
-{
- static int state;
-
- if (on && !state) {
- /* Enable the phy clocks */
- clk_enable(phyclk);
- clk_enable(clk48m);
- clk_enable(clk32k);
- state = 1;
- } else if (state) {
- /* Disable the phy clocks */
- clk_disable(phyclk);
- clk_disable(clk48m);
- clk_disable(clk32k);
- state = 0;
- }
- return 0;
-}
-
-int omap4430_phy_power(struct device *dev, int ID, int on)
-{
- if (on) {
- if (ID)
- /* enable VBUS valid, IDDIG groung */
- __raw_writel(AVALID | VBUSVALID, ctrl_base +
- USBOTGHS_CONTROL);
- else
- /*
- * Enable VBUS Valid, AValid and IDDIG
- * high impedance
- */
- __raw_writel(IDDIG | AVALID | VBUSVALID,
- ctrl_base + USBOTGHS_CONTROL);
- } else {
- /* Enable session END and IDIG to high impedance. */
- __raw_writel(SESSEND | IDDIG, ctrl_base +
- USBOTGHS_CONTROL);
- }
- return 0;
-}
-
-int omap4430_phy_suspend(struct device *dev, int suspend)
-{
- if (suspend) {
- /* Disable the clocks */
- omap4430_phy_set_clk(dev, 0);
- /* Power down the phy */
- __raw_writel(PHY_PD, ctrl_base + CONTROL_DEV_CONF);
-
- /* save the context */
- usbotghs_control = __raw_readl(ctrl_base + USBOTGHS_CONTROL);
- } else {
- /* Enable the internel phy clcoks */
- omap4430_phy_set_clk(dev, 1);
- /* power on the phy */
- if (__raw_readl(ctrl_base + CONTROL_DEV_CONF) & PHY_PD) {
- __raw_writel(~PHY_PD, ctrl_base + CONTROL_DEV_CONF);
- mdelay(200);
- }
-
- /* restore the context */
- __raw_writel(usbotghs_control, ctrl_base + USBOTGHS_CONTROL);
- }
-
- return 0;
-}
-
-int omap4430_phy_exit(struct device *dev)
-{
- if (ctrl_base)
- iounmap(ctrl_base);
- if (phyclk)
- clk_put(phyclk);
- if (clk48m)
- clk_put(clk48m);
- if (clk32k)
- clk_put(clk32k);
-
- return 0;
-}
-
void am35x_musb_reset(void)
{
u32 regval;
diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c
index 810aa1a332e1..8847d6eb2313 100644
--- a/arch/arm/mach-omap2/timer.c
+++ b/arch/arm/mach-omap2/timer.c
@@ -42,6 +42,7 @@
#include <asm/smp_twd.h>
#include <asm/sched_clock.h>
+#include <asm/arch_timer.h>
#include <plat/omap_hwmod.h>
#include <plat/omap_device.h>
#include <plat/dmtimer.h>
@@ -72,6 +73,11 @@
#define OMAP3_SECURE_TIMER 1
#endif
+#define REALTIME_COUNTER_BASE 0x48243200
+#define INCREMENTER_NUMERATOR_OFFSET 0x10
+#define INCREMENTER_DENUMERATOR_RELOAD_OFFSET 0x14
+#define NUMERATOR_DENUMERATOR_MASK 0xfffff000
+
/* Clockevent code */
static struct omap_dm_timer clkev;
@@ -349,6 +355,84 @@ static void __init omap2_clocksource_init(int gptimer_id,
omap2_gptimer_clocksource_init(gptimer_id, fck_source);
}
+#ifdef CONFIG_SOC_HAS_REALTIME_COUNTER
+/*
+ * The realtime counter also called master counter, is a free-running
+ * counter, which is related to real time. It produces the count used
+ * by the CPU local timer peripherals in the MPU cluster. The timer counts
+ * at a rate of 6.144 MHz. Because the device operates on different clocks
+ * in different power modes, the master counter shifts operation between
+ * clocks, adjusting the increment per clock in hardware accordingly to
+ * maintain a constant count rate.
+ */
+static void __init realtime_counter_init(void)
+{
+ void __iomem *base;
+ static struct clk *sys_clk;
+ unsigned long rate;
+ unsigned int reg, num, den;
+
+ base = ioremap(REALTIME_COUNTER_BASE, SZ_32);
+ if (!base) {
+ pr_err("%s: ioremap failed\n", __func__);
+ return;
+ }
+ sys_clk = clk_get(NULL, "sys_clkin_ck");
+ if (!sys_clk) {
+ pr_err("%s: failed to get system clock handle\n", __func__);
+ iounmap(base);
+ return;
+ }
+
+ rate = clk_get_rate(sys_clk);
+ /* Numerator/denumerator values refer TRM Realtime Counter section */
+ switch (rate) {
+ case 1200000:
+ num = 64;
+ den = 125;
+ break;
+ case 1300000:
+ num = 768;
+ den = 1625;
+ break;
+ case 19200000:
+ num = 8;
+ den = 25;
+ break;
+ case 2600000:
+ num = 384;
+ den = 1625;
+ break;
+ case 2700000:
+ num = 256;
+ den = 1125;
+ break;
+ case 38400000:
+ default:
+ /* Program it for 38.4 MHz */
+ num = 4;
+ den = 25;
+ break;
+ }
+
+ /* Program numerator and denumerator registers */
+ reg = __raw_readl(base + INCREMENTER_NUMERATOR_OFFSET) &
+ NUMERATOR_DENUMERATOR_MASK;
+ reg |= num;
+ __raw_writel(reg, base + INCREMENTER_NUMERATOR_OFFSET);
+
+ reg = __raw_readl(base + INCREMENTER_NUMERATOR_OFFSET) &
+ NUMERATOR_DENUMERATOR_MASK;
+ reg |= den;
+ __raw_writel(reg, base + INCREMENTER_DENUMERATOR_RELOAD_OFFSET);
+
+ iounmap(base);
+}
+#else
+static inline void __init realtime_counter_init(void)
+{}
+#endif
+
#define OMAP_SYS_TIMER_INIT(name, clkev_nr, clkev_src, \
clksrc_nr, clksrc_src) \
static void __init omap##name##_timer_init(void) \
@@ -410,7 +494,18 @@ OMAP_SYS_TIMER(4)
#endif
#ifdef CONFIG_SOC_OMAP5
-OMAP_SYS_TIMER_INIT(5, 1, OMAP4_CLKEV_SOURCE, 2, OMAP4_MPU_SOURCE)
+static void __init omap5_timer_init(void)
+{
+ int err;
+
+ omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE);
+ omap2_clocksource_init(2, OMAP4_MPU_SOURCE);
+ realtime_counter_init();
+
+ err = arch_timer_of_register();
+ if (err)
+ pr_err("%s: arch_timer_register failed %d\n", __func__, err);
+}
OMAP_SYS_TIMER(5)
#endif
diff --git a/arch/arm/mach-omap2/twl-common.c b/arch/arm/mach-omap2/twl-common.c
index 99be94e94547..45f77413c21d 100644
--- a/arch/arm/mach-omap2/twl-common.c
+++ b/arch/arm/mach-omap2/twl-common.c
@@ -40,16 +40,6 @@ static struct i2c_board_info __initdata pmic_i2c_board_info = {
.flags = I2C_CLIENT_WAKE,
};
-static struct i2c_board_info __initdata omap4_i2c1_board_info[] = {
- {
- .addr = 0x48,
- .flags = I2C_CLIENT_WAKE,
- },
- {
- I2C_BOARD_INFO("twl6040", 0x4b),
- },
-};
-
#if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP4)
static int twl_set_voltage(void *data, int target_uV)
{
@@ -79,30 +69,25 @@ void __init omap_pmic_init(int bus, u32 clkrate,
void __init omap4_pmic_init(const char *pmic_type,
struct twl4030_platform_data *pmic_data,
- struct twl6040_platform_data *twl6040_data, int twl6040_irq)
+ struct i2c_board_info *devices, int nr_devices)
{
/* PMIC part*/
omap_mux_init_signal("sys_nirq1", OMAP_PIN_INPUT_PULLUP | OMAP_PIN_OFF_WAKEUPENABLE);
- strncpy(omap4_i2c1_board_info[0].type, pmic_type,
- sizeof(omap4_i2c1_board_info[0].type));
- omap4_i2c1_board_info[0].irq = 7 + OMAP44XX_IRQ_GIC_START;
- omap4_i2c1_board_info[0].platform_data = pmic_data;
-
- /* TWL6040 audio IC part */
- omap4_i2c1_board_info[1].irq = twl6040_irq;
- omap4_i2c1_board_info[1].platform_data = twl6040_data;
-
- omap_register_i2c_bus(1, 400, omap4_i2c1_board_info, 2);
+ omap_pmic_init(1, 400, pmic_type, 7 + OMAP44XX_IRQ_GIC_START, pmic_data);
+ /* Register additional devices on i2c1 bus if needed */
+ if (devices)
+ i2c_register_board_info(1, devices, nr_devices);
}
void __init omap_pmic_late_init(void)
{
- /* Init the OMAP TWL parameters (if PMIC has been registered) */
- if (pmic_i2c_board_info.irq)
- omap3_twl_init();
- if (omap4_i2c1_board_info[0].irq)
- omap4_twl_init();
+ /* Init the OMAP TWL parameters (if PMIC has been registerd) */
+ if (!pmic_i2c_board_info.irq)
+ return;
+
+ omap3_twl_init();
+ omap4_twl_init();
}
#if defined(CONFIG_ARCH_OMAP3)
@@ -252,11 +237,6 @@ void __init omap3_pmic_get_config(struct twl4030_platform_data *pmic_data,
#if defined(CONFIG_ARCH_OMAP4)
static struct twl4030_usb_data omap4_usb_pdata = {
- .phy_init = omap4430_phy_init,
- .phy_exit = omap4430_phy_exit,
- .phy_power = omap4430_phy_power,
- .phy_set_clock = omap4430_phy_set_clk,
- .phy_suspend = omap4430_phy_suspend,
};
static struct regulator_init_data omap4_vdac_idata = {
diff --git a/arch/arm/mach-omap2/twl-common.h b/arch/arm/mach-omap2/twl-common.h
index d109c09ef34b..2256efe90cf1 100644
--- a/arch/arm/mach-omap2/twl-common.h
+++ b/arch/arm/mach-omap2/twl-common.h
@@ -32,6 +32,7 @@
struct twl4030_platform_data;
struct twl6040_platform_data;
+struct i2c_board_info;
void omap_pmic_init(int bus, u32 clkrate, const char *pmic_type, int pmic_irq,
struct twl4030_platform_data *pmic_data);
@@ -51,7 +52,7 @@ static inline void omap3_pmic_init(const char *pmic_type,
void omap4_pmic_init(const char *pmic_type,
struct twl4030_platform_data *pmic_data,
- struct twl6040_platform_data *audio_data, int twl6040_irq);
+ struct i2c_board_info *devices, int nr_devices);
void omap3_pmic_get_config(struct twl4030_platform_data *pmic_data,
u32 pdata_flags, u32 regulators_flags);
diff --git a/arch/arm/mach-omap2/usb-musb.c b/arch/arm/mach-omap2/usb-musb.c
index 136c64bc9028..51da21cb78f1 100644
--- a/arch/arm/mach-omap2/usb-musb.c
+++ b/arch/arm/mach-omap2/usb-musb.c
@@ -116,7 +116,4 @@ void __init usb_musb_init(struct omap_musb_board_data *musb_board_data)
dev->dma_mask = &musb_dmamask;
dev->coherent_dma_mask = musb_dmamask;
put_device(dev);
-
- if (cpu_is_omap44xx())
- omap4430_phy_init(dev);
}
diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c
index 5387fdfcaf3f..073c7d799068 100644
--- a/arch/arm/mach-orion5x/common.c
+++ b/arch/arm/mach-orion5x/common.c
@@ -194,6 +194,13 @@ void __init orion5x_wdt_init(void)
void __init orion5x_init_early(void)
{
orion_time_set_base(TIMER_VIRT_BASE);
+
+ /*
+ * Some Orion5x devices allocate their coherent buffers from atomic
+ * context. Increase size of atomic coherent pool to make sure such
+ * the allocations won't fail.
+ */
+ init_dma_coherent_pool_size(SZ_1M);
}
int orion5x_tclk;
diff --git a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c
index 78a6a11d8216..9b1c95310291 100644
--- a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c
+++ b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c
@@ -18,7 +18,6 @@
#include <linux/ethtool.h>
#include <net/dsa.h>
#include <asm/mach-types.h>
-#include <asm/leds.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include <mach/orion5x.h>
diff --git a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c
index 2f5dc54cd4cd..51ba2b81a10b 100644
--- a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c
+++ b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c
@@ -19,7 +19,6 @@
#include <linux/i2c.h>
#include <net/dsa.h>
#include <asm/mach-types.h>
-#include <asm/leds.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include <mach/orion5x.h>
diff --git a/arch/arm/mach-orion5x/rd88f5182-setup.c b/arch/arm/mach-orion5x/rd88f5182-setup.c
index 399130fac0b6..0a56b9444f1b 100644
--- a/arch/arm/mach-orion5x/rd88f5182-setup.c
+++ b/arch/arm/mach-orion5x/rd88f5182-setup.c
@@ -19,8 +19,8 @@
#include <linux/mv643xx_eth.h>
#include <linux/ata_platform.h>
#include <linux/i2c.h>
+#include <linux/leds.h>
#include <asm/mach-types.h>
-#include <asm/leds.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include <mach/orion5x.h>
@@ -53,12 +53,6 @@
#define RD88F5182_PCI_SLOT0_IRQ_A_PIN 7
#define RD88F5182_PCI_SLOT0_IRQ_B_PIN 6
-/*
- * GPIO Debug LED
- */
-
-#define RD88F5182_GPIO_DBG_LED 0
-
/*****************************************************************************
* 16M NOR Flash on Device bus CS1
****************************************************************************/
@@ -83,55 +77,32 @@ static struct platform_device rd88f5182_nor_flash = {
.resource = &rd88f5182_nor_flash_resource,
};
-#ifdef CONFIG_LEDS
-
/*****************************************************************************
- * Use GPIO debug led as CPU active indication
+ * Use GPIO LED as CPU active indication
****************************************************************************/
-static void rd88f5182_dbgled_event(led_event_t evt)
-{
- int val;
-
- if (evt == led_idle_end)
- val = 1;
- else if (evt == led_idle_start)
- val = 0;
- else
- return;
-
- gpio_set_value(RD88F5182_GPIO_DBG_LED, val);
-}
-
-static int __init rd88f5182_dbgled_init(void)
-{
- int pin;
-
- if (machine_is_rd88f5182()) {
- pin = RD88F5182_GPIO_DBG_LED;
+#define RD88F5182_GPIO_LED 0
- if (gpio_request(pin, "DBGLED") == 0) {
- if (gpio_direction_output(pin, 0) != 0) {
- printk(KERN_ERR "rd88f5182_dbgled_init failed "
- "to set output pin %d\n", pin);
- gpio_free(pin);
- return 0;
- }
- } else {
- printk(KERN_ERR "rd88f5182_dbgled_init failed "
- "to request gpio %d\n", pin);
- return 0;
- }
-
- leds_event = rd88f5182_dbgled_event;
- }
-
- return 0;
-}
+static struct gpio_led rd88f5182_gpio_led_pins[] = {
+ {
+ .name = "rd88f5182:cpu",
+ .default_trigger = "cpu0",
+ .gpio = RD88F5182_GPIO_LED,
+ },
+};
-__initcall(rd88f5182_dbgled_init);
+static struct gpio_led_platform_data rd88f5182_gpio_led_data = {
+ .leds = rd88f5182_gpio_led_pins,
+ .num_leds = ARRAY_SIZE(rd88f5182_gpio_led_pins),
+};
-#endif
+static struct platform_device rd88f5182_gpio_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &rd88f5182_gpio_led_data,
+ },
+};
/*****************************************************************************
* PCI
@@ -298,6 +269,7 @@ static void __init rd88f5182_init(void)
orion5x_setup_dev1_win(RD88F5182_NOR_BASE, RD88F5182_NOR_SIZE);
platform_device_register(&rd88f5182_nor_flash);
+ platform_device_register(&rd88f5182_gpio_leds);
i2c_register_board_info(0, &rd88f5182_i2c_rtc, 1);
}
diff --git a/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c b/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c
index 92df49c1b62a..ed50910b08a4 100644
--- a/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c
+++ b/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c
@@ -20,7 +20,6 @@
#include <linux/ethtool.h>
#include <net/dsa.h>
#include <asm/mach-types.h>
-#include <asm/leds.h>
#include <asm/mach/arch.h>
#include <asm/mach/pci.h>
#include <mach/orion5x.h>
diff --git a/arch/arm/mach-orion5x/ts78xx-setup.c b/arch/arm/mach-orion5x/ts78xx-setup.c
index b4203277f3cd..b0727dcd1ef9 100644
--- a/arch/arm/mach-orion5x/ts78xx-setup.c
+++ b/arch/arm/mach-orion5x/ts78xx-setup.c
@@ -36,7 +36,7 @@
* FPGA - lives where the PCI bus would be at ORION5X_PCI_MEM_PHYS_BASE
*/
#define TS78XX_FPGA_REGS_PHYS_BASE 0xe8000000
-#define TS78XX_FPGA_REGS_VIRT_BASE 0xff900000
+#define TS78XX_FPGA_REGS_VIRT_BASE IOMEM(0xff900000)
#define TS78XX_FPGA_REGS_SIZE SZ_1M
static struct ts78xx_fpga_data ts78xx_fpga = {
@@ -50,7 +50,7 @@ static struct ts78xx_fpga_data ts78xx_fpga = {
****************************************************************************/
static struct map_desc ts78xx_io_desc[] __initdata = {
{
- .virtual = TS78XX_FPGA_REGS_VIRT_BASE,
+ .virtual = (unsigned long)TS78XX_FPGA_REGS_VIRT_BASE,
.pfn = __phys_to_pfn(TS78XX_FPGA_REGS_PHYS_BASE),
.length = TS78XX_FPGA_REGS_SIZE,
.type = MT_DEVICE,
@@ -80,8 +80,8 @@ static struct mv_sata_platform_data ts78xx_sata_data = {
/*****************************************************************************
* RTC M48T86 - nicked^Wborrowed from arch/arm/mach-ep93xx/ts72xx.c
****************************************************************************/
-#define TS_RTC_CTRL (TS78XX_FPGA_REGS_VIRT_BASE | 0x808)
-#define TS_RTC_DATA (TS78XX_FPGA_REGS_VIRT_BASE | 0x80c)
+#define TS_RTC_CTRL (TS78XX_FPGA_REGS_VIRT_BASE + 0x808)
+#define TS_RTC_DATA (TS78XX_FPGA_REGS_VIRT_BASE + 0x80c)
static unsigned char ts78xx_ts_rtc_readbyte(unsigned long addr)
{
@@ -162,8 +162,8 @@ static void ts78xx_ts_rtc_unload(void)
/*****************************************************************************
* NAND Flash
****************************************************************************/
-#define TS_NAND_CTRL (TS78XX_FPGA_REGS_VIRT_BASE | 0x800) /* VIRT */
-#define TS_NAND_DATA (TS78XX_FPGA_REGS_PHYS_BASE | 0x804) /* PHYS */
+#define TS_NAND_CTRL (TS78XX_FPGA_REGS_VIRT_BASE + 0x800) /* VIRT */
+#define TS_NAND_DATA (TS78XX_FPGA_REGS_PHYS_BASE + 0x804) /* PHYS */
/*
* hardware specific access to control-lines
diff --git a/arch/arm/mach-prima2/include/mach/uncompress.h b/arch/arm/mach-prima2/include/mach/uncompress.h
index 83125c6a30b3..0c898fcf909c 100644
--- a/arch/arm/mach-prima2/include/mach/uncompress.h
+++ b/arch/arm/mach-prima2/include/mach/uncompress.h
@@ -25,11 +25,11 @@ static __inline__ void putc(char c)
* during kernel decompression, all mappings are flat:
* virt_addr == phys_addr
*/
- while (__raw_readl(SIRFSOC_UART1_PA_BASE + SIRFSOC_UART_TXFIFO_STATUS)
+ while (__raw_readl((void __iomem *)SIRFSOC_UART1_PA_BASE + SIRFSOC_UART_TXFIFO_STATUS)
& SIRFSOC_UART1_TXFIFO_FULL)
barrier();
- __raw_writel(c, SIRFSOC_UART1_PA_BASE + SIRFSOC_UART_TXFIFO_DATA);
+ __raw_writel(c, (void __iomem *)SIRFSOC_UART1_PA_BASE + SIRFSOC_UART_TXFIFO_DATA);
}
static inline void flush(void)
diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile
index 2bedc9ed076c..ee88d6eae648 100644
--- a/arch/arm/mach-pxa/Makefile
+++ b/arch/arm/mach-pxa/Makefile
@@ -98,12 +98,4 @@ obj-$(CONFIG_MACH_RAUMFELD_CONNECTOR) += raumfeld.o
obj-$(CONFIG_MACH_RAUMFELD_SPEAKER) += raumfeld.o
obj-$(CONFIG_MACH_ZIPIT2) += z2.o
-# Support for blinky lights
-led-y := leds.o
-led-$(CONFIG_ARCH_LUBBOCK) += leds-lubbock.o
-led-$(CONFIG_MACH_MAINSTONE) += leds-mainstone.o
-led-$(CONFIG_ARCH_PXA_IDP) += leds-idp.o
-
-obj-$(CONFIG_LEDS) += $(led-y)
-
obj-$(CONFIG_TOSA_BT) += tosa-bt.o
diff --git a/arch/arm/mach-pxa/idp.c b/arch/arm/mach-pxa/idp.c
index c36151940d17..64507cdd2e8f 100644
--- a/arch/arm/mach-pxa/idp.c
+++ b/arch/arm/mach-pxa/idp.c
@@ -191,6 +191,87 @@ static void __init idp_map_io(void)
iotable_init(idp_io_desc, ARRAY_SIZE(idp_io_desc));
}
+/* LEDs */
+#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
+struct idp_led {
+ struct led_classdev cdev;
+ u8 mask;
+};
+
+/*
+ * The triggers lines up below will only be used if the
+ * LED triggers are compiled in.
+ */
+static const struct {
+ const char *name;
+ const char *trigger;
+} idp_leds[] = {
+ { "idp:green", "heartbeat", },
+ { "idp:red", "cpu0", },
+};
+
+static void idp_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
+{
+ struct idp_led *led = container_of(cdev,
+ struct idp_led, cdev);
+ u32 reg = IDP_CPLD_LED_CONTROL;
+
+ if (b != LED_OFF)
+ reg &= ~led->mask;
+ else
+ reg |= led->mask;
+
+ IDP_CPLD_LED_CONTROL = reg;
+}
+
+static enum led_brightness idp_led_get(struct led_classdev *cdev)
+{
+ struct idp_led *led = container_of(cdev,
+ struct idp_led, cdev);
+
+ return (IDP_CPLD_LED_CONTROL & led->mask) ? LED_OFF : LED_FULL;
+}
+
+static int __init idp_leds_init(void)
+{
+ int i;
+
+ if (!machine_is_pxa_idp())
+ return -ENODEV;
+
+ for (i = 0; i < ARRAY_SIZE(idp_leds); i++) {
+ struct idp_led *led;
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ break;
+
+ led->cdev.name = idp_leds[i].name;
+ led->cdev.brightness_set = idp_led_set;
+ led->cdev.brightness_get = idp_led_get;
+ led->cdev.default_trigger = idp_leds[i].trigger;
+
+ if (i == 0)
+ led->mask = IDP_HB_LED;
+ else
+ led->mask = IDP_BUSY_LED;
+
+ if (led_classdev_register(NULL, &led->cdev) < 0) {
+ kfree(led);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Since we may have triggers on any subsystem, defer registration
+ * until after subsystem_init.
+ */
+fs_initcall(idp_leds_init);
+#endif
MACHINE_START(PXA_IDP, "Vibren PXA255 IDP")
/* Maintainer: Vibren Technologies */
diff --git a/arch/arm/mach-pxa/leds-idp.c b/arch/arm/mach-pxa/leds-idp.c
deleted file mode 100644
index 06b060025d11..000000000000
--- a/arch/arm/mach-pxa/leds-idp.c
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * linux/arch/arm/mach-pxa/leds-idp.c
- *
- * Copyright (C) 2000 John Dorsey <john+@cs.cmu.edu>
- *
- * Copyright (c) 2001 Jeff Sutherland <jeffs@accelent.com>
- *
- * Original (leds-footbridge.c) by Russell King
- *
- * Macros for actual LED manipulation should be in machine specific
- * files in this 'mach' directory.
- */
-
-
-#include <linux/init.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-
-#include <mach/pxa25x.h>
-#include <mach/idp.h>
-
-#include "leds.h"
-
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
-
-static unsigned int led_state;
-static unsigned int hw_led_state;
-
-void idp_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- switch (evt) {
- case led_start:
- hw_led_state = IDP_HB_LED | IDP_BUSY_LED;
- led_state = LED_STATE_ENABLED;
- break;
-
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = IDP_HB_LED | IDP_BUSY_LED;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = IDP_HB_LED | IDP_BUSY_LED;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state ^= IDP_HB_LED;
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~IDP_BUSY_LED;
- break;
-
- case led_idle_end:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= IDP_BUSY_LED;
- break;
-#endif
-
- case led_halted:
- break;
-
- case led_green_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= IDP_HB_LED;
- break;
-
- case led_green_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~IDP_HB_LED;
- break;
-
- case led_amber_on:
- break;
-
- case led_amber_off:
- break;
-
- case led_red_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= IDP_BUSY_LED;
- break;
-
- case led_red_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~IDP_BUSY_LED;
- break;
-
- default:
- break;
- }
-
- if (led_state & LED_STATE_ENABLED)
- IDP_CPLD_LED_CONTROL = ( (IDP_CPLD_LED_CONTROL | IDP_LEDS_MASK) & ~hw_led_state);
- else
- IDP_CPLD_LED_CONTROL |= IDP_LEDS_MASK;
-
- local_irq_restore(flags);
-}
diff --git a/arch/arm/mach-pxa/leds-lubbock.c b/arch/arm/mach-pxa/leds-lubbock.c
deleted file mode 100644
index 0bd85c884a7c..000000000000
--- a/arch/arm/mach-pxa/leds-lubbock.c
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * linux/arch/arm/mach-pxa/leds-lubbock.c
- *
- * Copyright (C) 2000 John Dorsey <john+@cs.cmu.edu>
- *
- * Copyright (c) 2001 Jeff Sutherland <jeffs@accelent.com>
- *
- * Original (leds-footbridge.c) by Russell King
- *
- * Major surgery on April 2004 by Nicolas Pitre for less global
- * namespace collision. Mostly adapted the Mainstone version.
- */
-
-#include <linux/init.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-#include <mach/pxa25x.h>
-#include <mach/lubbock.h>
-
-#include "leds.h"
-
-/*
- * 8 discrete leds available for general use:
- *
- * Note: bits [15-8] are used to enable/blank the 8 7 segment hex displays
- * so be sure to not monkey with them here.
- */
-
-#define D28 (1 << 0)
-#define D27 (1 << 1)
-#define D26 (1 << 2)
-#define D25 (1 << 3)
-#define D24 (1 << 4)
-#define D23 (1 << 5)
-#define D22 (1 << 6)
-#define D21 (1 << 7)
-
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
-
-static unsigned int led_state;
-static unsigned int hw_led_state;
-
-void lubbock_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- switch (evt) {
- case led_start:
- hw_led_state = 0;
- led_state = LED_STATE_ENABLED;
- break;
-
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = 0;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = 0;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- hw_led_state ^= D26;
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- hw_led_state &= ~D27;
- break;
-
- case led_idle_end:
- hw_led_state |= D27;
- break;
-#endif
-
- case led_halted:
- break;
-
- case led_green_on:
- hw_led_state |= D21;
- break;
-
- case led_green_off:
- hw_led_state &= ~D21;
- break;
-
- case led_amber_on:
- hw_led_state |= D22;
- break;
-
- case led_amber_off:
- hw_led_state &= ~D22;
- break;
-
- case led_red_on:
- hw_led_state |= D23;
- break;
-
- case led_red_off:
- hw_led_state &= ~D23;
- break;
-
- default:
- break;
- }
-
- if (led_state & LED_STATE_ENABLED)
- LUB_DISC_BLNK_LED = (LUB_DISC_BLNK_LED | 0xff) & ~hw_led_state;
- else
- LUB_DISC_BLNK_LED |= 0xff;
-
- local_irq_restore(flags);
-}
diff --git a/arch/arm/mach-pxa/leds-mainstone.c b/arch/arm/mach-pxa/leds-mainstone.c
deleted file mode 100644
index 4058ab340fe6..000000000000
--- a/arch/arm/mach-pxa/leds-mainstone.c
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * linux/arch/arm/mach-pxa/leds-mainstone.c
- *
- * Author: Nicolas Pitre
- * Created: Nov 05, 2002
- * 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.
- */
-
-#include <linux/init.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-
-#include <mach/pxa27x.h>
-#include <mach/mainstone.h>
-
-#include "leds.h"
-
-
-/* 8 discrete leds available for general use: */
-#define D28 (1 << 0)
-#define D27 (1 << 1)
-#define D26 (1 << 2)
-#define D25 (1 << 3)
-#define D24 (1 << 4)
-#define D23 (1 << 5)
-#define D22 (1 << 6)
-#define D21 (1 << 7)
-
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
-
-static unsigned int led_state;
-static unsigned int hw_led_state;
-
-void mainstone_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- switch (evt) {
- case led_start:
- hw_led_state = 0;
- led_state = LED_STATE_ENABLED;
- break;
-
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = 0;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = 0;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- hw_led_state ^= D26;
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- hw_led_state &= ~D27;
- break;
-
- case led_idle_end:
- hw_led_state |= D27;
- break;
-#endif
-
- case led_halted:
- break;
-
- case led_green_on:
- hw_led_state |= D21;
- break;
-
- case led_green_off:
- hw_led_state &= ~D21;
- break;
-
- case led_amber_on:
- hw_led_state |= D22;
- break;
-
- case led_amber_off:
- hw_led_state &= ~D22;
- break;
-
- case led_red_on:
- hw_led_state |= D23;
- break;
-
- case led_red_off:
- hw_led_state &= ~D23;
- break;
-
- default:
- break;
- }
-
- if (led_state & LED_STATE_ENABLED)
- MST_LEDCTRL = (MST_LEDCTRL | 0xff) & ~hw_led_state;
- else
- MST_LEDCTRL |= 0xff;
-
- local_irq_restore(flags);
-}
diff --git a/arch/arm/mach-pxa/leds.c b/arch/arm/mach-pxa/leds.c
deleted file mode 100644
index bbe4d5f6afaa..000000000000
--- a/arch/arm/mach-pxa/leds.c
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * linux/arch/arm/mach-pxa/leds.c
- *
- * xscale LEDs dispatcher
- *
- * Copyright (C) 2001 Nicolas Pitre
- *
- * Copyright (c) 2001 Jeff Sutherland, Accelent Systems Inc.
- */
-#include <linux/compiler.h>
-#include <linux/init.h>
-
-#include <asm/leds.h>
-#include <asm/mach-types.h>
-
-#include "leds.h"
-
-static int __init
-pxa_leds_init(void)
-{
- if (machine_is_lubbock())
- leds_event = lubbock_leds_event;
- if (machine_is_mainstone())
- leds_event = mainstone_leds_event;
- if (machine_is_pxa_idp())
- leds_event = idp_leds_event;
-
- leds_event(led_start);
- return 0;
-}
-
-core_initcall(pxa_leds_init);
diff --git a/arch/arm/mach-pxa/leds.h b/arch/arm/mach-pxa/leds.h
deleted file mode 100644
index 7f0dfe01345a..000000000000
--- a/arch/arm/mach-pxa/leds.h
+++ /dev/null
@@ -1,13 +0,0 @@
-/*
- * arch/arm/mach-pxa/leds.h
- *
- * Copyright (c) 2001 Jeff Sutherland, Accelent Systems Inc.
- *
- * blinky lights for various PXA-based systems:
- *
- */
-
-extern void idp_leds_event(led_event_t evt);
-extern void lubbock_leds_event(led_event_t evt);
-extern void mainstone_leds_event(led_event_t evt);
-extern void trizeps4_leds_event(led_event_t evt);
diff --git a/arch/arm/mach-pxa/lubbock.c b/arch/arm/mach-pxa/lubbock.c
index 44dd7565707e..553056d9a3c5 100644
--- a/arch/arm/mach-pxa/lubbock.c
+++ b/arch/arm/mach-pxa/lubbock.c
@@ -15,6 +15,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/syscore_ops.h>
#include <linux/major.h>
@@ -23,6 +24,8 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/smc91x.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
#include <linux/spi/spi.h>
#include <linux/spi/ads7846.h>
@@ -549,6 +552,98 @@ static void __init lubbock_map_io(void)
PCFR |= PCFR_OPDE;
}
+/*
+ * Driver for the 8 discrete LEDs available for general use:
+ * Note: bits [15-8] are used to enable/blank the 8 7 segment hex displays
+ * so be sure to not monkey with them here.
+ */
+
+#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
+struct lubbock_led {
+ struct led_classdev cdev;
+ u8 mask;
+};
+
+/*
+ * The triggers lines up below will only be used if the
+ * LED triggers are compiled in.
+ */
+static const struct {
+ const char *name;
+ const char *trigger;
+} lubbock_leds[] = {
+ { "lubbock:D28", "default-on", },
+ { "lubbock:D27", "cpu0", },
+ { "lubbock:D26", "heartbeat" },
+ { "lubbock:D25", },
+ { "lubbock:D24", },
+ { "lubbock:D23", },
+ { "lubbock:D22", },
+ { "lubbock:D21", },
+};
+
+static void lubbock_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
+{
+ struct lubbock_led *led = container_of(cdev,
+ struct lubbock_led, cdev);
+ u32 reg = LUB_DISC_BLNK_LED;
+
+ if (b != LED_OFF)
+ reg |= led->mask;
+ else
+ reg &= ~led->mask;
+
+ LUB_DISC_BLNK_LED = reg;
+}
+
+static enum led_brightness lubbock_led_get(struct led_classdev *cdev)
+{
+ struct lubbock_led *led = container_of(cdev,
+ struct lubbock_led, cdev);
+ u32 reg = LUB_DISC_BLNK_LED;
+
+ return (reg & led->mask) ? LED_FULL : LED_OFF;
+}
+
+static int __init lubbock_leds_init(void)
+{
+ int i;
+
+ if (!machine_is_lubbock())
+ return -ENODEV;
+
+ /* All ON */
+ LUB_DISC_BLNK_LED |= 0xff;
+ for (i = 0; i < ARRAY_SIZE(lubbock_leds); i++) {
+ struct lubbock_led *led;
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ break;
+
+ led->cdev.name = lubbock_leds[i].name;
+ led->cdev.brightness_set = lubbock_led_set;
+ led->cdev.brightness_get = lubbock_led_get;
+ led->cdev.default_trigger = lubbock_leds[i].trigger;
+ led->mask = BIT(i);
+
+ if (led_classdev_register(NULL, &led->cdev) < 0) {
+ kfree(led);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Since we may have triggers on any subsystem, defer registration
+ * until after subsystem_init.
+ */
+fs_initcall(lubbock_leds_init);
+#endif
+
MACHINE_START(LUBBOCK, "Intel DBPXA250 Development Platform (aka Lubbock)")
/* Maintainer: MontaVista Software Inc. */
.map_io = lubbock_map_io,
diff --git a/arch/arm/mach-pxa/mainstone.c b/arch/arm/mach-pxa/mainstone.c
index 5d9475730a3f..f27a61ee7ac7 100644
--- a/arch/arm/mach-pxa/mainstone.c
+++ b/arch/arm/mach-pxa/mainstone.c
@@ -28,6 +28,8 @@
#include <linux/pwm_backlight.h>
#include <linux/smc91x.h>
#include <linux/i2c/pxa-i2c.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
#include <asm/types.h>
#include <asm/setup.h>
@@ -613,6 +615,98 @@ static void __init mainstone_map_io(void)
PCFR = 0x66;
}
+/*
+ * Driver for the 8 discrete LEDs available for general use:
+ * Note: bits [15-8] are used to enable/blank the 8 7 segment hex displays
+ * so be sure to not monkey with them here.
+ */
+
+#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
+struct mainstone_led {
+ struct led_classdev cdev;
+ u8 mask;
+};
+
+/*
+ * The triggers lines up below will only be used if the
+ * LED triggers are compiled in.
+ */
+static const struct {
+ const char *name;
+ const char *trigger;
+} mainstone_leds[] = {
+ { "mainstone:D28", "default-on", },
+ { "mainstone:D27", "cpu0", },
+ { "mainstone:D26", "heartbeat" },
+ { "mainstone:D25", },
+ { "mainstone:D24", },
+ { "mainstone:D23", },
+ { "mainstone:D22", },
+ { "mainstone:D21", },
+};
+
+static void mainstone_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
+{
+ struct mainstone_led *led = container_of(cdev,
+ struct mainstone_led, cdev);
+ u32 reg = MST_LEDCTRL;
+
+ if (b != LED_OFF)
+ reg |= led->mask;
+ else
+ reg &= ~led->mask;
+
+ MST_LEDCTRL = reg;
+}
+
+static enum led_brightness mainstone_led_get(struct led_classdev *cdev)
+{
+ struct mainstone_led *led = container_of(cdev,
+ struct mainstone_led, cdev);
+ u32 reg = MST_LEDCTRL;
+
+ return (reg & led->mask) ? LED_FULL : LED_OFF;
+}
+
+static int __init mainstone_leds_init(void)
+{
+ int i;
+
+ if (!machine_is_mainstone())
+ return -ENODEV;
+
+ /* All ON */
+ MST_LEDCTRL |= 0xff;
+ for (i = 0; i < ARRAY_SIZE(mainstone_leds); i++) {
+ struct mainstone_led *led;
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ break;
+
+ led->cdev.name = mainstone_leds[i].name;
+ led->cdev.brightness_set = mainstone_led_set;
+ led->cdev.brightness_get = mainstone_led_get;
+ led->cdev.default_trigger = mainstone_leds[i].trigger;
+ led->mask = BIT(i);
+
+ if (led_classdev_register(NULL, &led->cdev) < 0) {
+ kfree(led);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Since we may have triggers on any subsystem, defer registration
+ * until after subsystem_init.
+ */
+fs_initcall(mainstone_leds_init);
+#endif
+
MACHINE_START(MAINSTONE, "Intel HCDDBBVA0 Development Platform (aka Mainstone)")
/* Maintainer: MontaVista Software Inc. */
.atag_offset = 0x100, /* BLOB boot parameter setting */
diff --git a/arch/arm/mach-pxa/stargate2.c b/arch/arm/mach-pxa/stargate2.c
index 08ea7a84a291..456560b5aad4 100644
--- a/arch/arm/mach-pxa/stargate2.c
+++ b/arch/arm/mach-pxa/stargate2.c
@@ -52,7 +52,7 @@
#include <linux/spi/spi.h>
#include <linux/spi/pxa2xx_spi.h>
#include <linux/mfd/da903x.h>
-#include <linux/sht15.h>
+#include <linux/platform_data/sht15.h>
#include "devices.h"
#include "generic.h"
diff --git a/arch/arm/mach-realview/core.c b/arch/arm/mach-realview/core.c
index ff007d15e0ec..682467480588 100644
--- a/arch/arm/mach-realview/core.c
+++ b/arch/arm/mach-realview/core.c
@@ -34,7 +34,6 @@
#include <mach/hardware.h>
#include <asm/irq.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include <asm/hardware/arm_timer.h>
#include <asm/hardware/icst.h>
@@ -330,44 +329,6 @@ struct clcd_board clcd_plat_data = {
.remove = versatile_clcd_remove_dma,
};
-#ifdef CONFIG_LEDS
-#define VA_LEDS_BASE (__io_address(REALVIEW_SYS_BASE) + REALVIEW_SYS_LED_OFFSET)
-
-void realview_leds_event(led_event_t ledevt)
-{
- unsigned long flags;
- u32 val;
- u32 led = 1 << smp_processor_id();
-
- local_irq_save(flags);
- val = readl(VA_LEDS_BASE);
-
- switch (ledevt) {
- case led_idle_start:
- val = val & ~led;
- break;
-
- case led_idle_end:
- val = val | led;
- break;
-
- case led_timer:
- val = val ^ REALVIEW_SYS_LED7;
- break;
-
- case led_halted:
- val = 0;
- break;
-
- default:
- break;
- }
-
- writel(val, VA_LEDS_BASE);
- local_irq_restore(flags);
-}
-#endif /* CONFIG_LEDS */
-
/*
* Where is the timer (VA)?
*/
diff --git a/arch/arm/mach-realview/core.h b/arch/arm/mach-realview/core.h
index 78cd970c80f2..602ca5ec52c5 100644
--- a/arch/arm/mach-realview/core.h
+++ b/arch/arm/mach-realview/core.h
@@ -26,7 +26,6 @@
#include <linux/io.h>
#include <asm/setup.h>
-#include <asm/leds.h>
#define APB_DEVICE(name, busid, base, plat) \
static AMBA_APB_DEVICE(name, busid, 0, REALVIEW_##base##_BASE, base##_IRQ, plat)
@@ -47,7 +46,6 @@ extern void __iomem *timer1_va_base;
extern void __iomem *timer2_va_base;
extern void __iomem *timer3_va_base;
-extern void realview_leds_event(led_event_t ledevt);
extern void realview_timer_init(unsigned int timer_irq);
extern int realview_flash_register(struct resource *res, u32 num);
extern int realview_eth_register(const char *name, struct resource *res);
diff --git a/arch/arm/mach-realview/realview_eb.c b/arch/arm/mach-realview/realview_eb.c
index ce7747692c8b..d3b3cd216d64 100644
--- a/arch/arm/mach-realview/realview_eb.c
+++ b/arch/arm/mach-realview/realview_eb.c
@@ -31,7 +31,6 @@
#include <mach/hardware.h>
#include <asm/irq.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include <asm/pgtable.h>
#include <asm/hardware/gic.h>
@@ -463,10 +462,6 @@ static void __init realview_eb_init(void)
struct amba_device *d = amba_devs[i];
amba_device_register(d, &iomem_resource);
}
-
-#ifdef CONFIG_LEDS
- leds_event = realview_leds_event;
-#endif
}
MACHINE_START(REALVIEW_EB, "ARM-RealView EB")
diff --git a/arch/arm/mach-realview/realview_pb1176.c b/arch/arm/mach-realview/realview_pb1176.c
index e21711d72ee2..07d6672ddae7 100644
--- a/arch/arm/mach-realview/realview_pb1176.c
+++ b/arch/arm/mach-realview/realview_pb1176.c
@@ -33,7 +33,6 @@
#include <mach/hardware.h>
#include <asm/irq.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include <asm/pgtable.h>
#include <asm/hardware/gic.h>
@@ -376,10 +375,6 @@ static void __init realview_pb1176_init(void)
struct amba_device *d = amba_devs[i];
amba_device_register(d, &iomem_resource);
}
-
-#ifdef CONFIG_LEDS
- leds_event = realview_leds_event;
-#endif
}
MACHINE_START(REALVIEW_PB1176, "ARM-RealView PB1176")
diff --git a/arch/arm/mach-realview/realview_pb11mp.c b/arch/arm/mach-realview/realview_pb11mp.c
index a80269981dd4..7ed53d75350f 100644
--- a/arch/arm/mach-realview/realview_pb11mp.c
+++ b/arch/arm/mach-realview/realview_pb11mp.c
@@ -31,7 +31,6 @@
#include <mach/hardware.h>
#include <asm/irq.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include <asm/pgtable.h>
#include <asm/hardware/gic.h>
@@ -358,10 +357,6 @@ static void __init realview_pb11mp_init(void)
struct amba_device *d = amba_devs[i];
amba_device_register(d, &iomem_resource);
}
-
-#ifdef CONFIG_LEDS
- leds_event = realview_leds_event;
-#endif
}
MACHINE_START(REALVIEW_PB11MP, "ARM-RealView PB11MPCore")
diff --git a/arch/arm/mach-realview/realview_pba8.c b/arch/arm/mach-realview/realview_pba8.c
index 1435cd863965..9992431b8a15 100644
--- a/arch/arm/mach-realview/realview_pba8.c
+++ b/arch/arm/mach-realview/realview_pba8.c
@@ -30,7 +30,6 @@
#include <linux/platform_data/clk-realview.h>
#include <asm/irq.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include <asm/pgtable.h>
#include <asm/hardware/gic.h>
@@ -300,10 +299,6 @@ static void __init realview_pba8_init(void)
struct amba_device *d = amba_devs[i];
amba_device_register(d, &iomem_resource);
}
-
-#ifdef CONFIG_LEDS
- leds_event = realview_leds_event;
-#endif
}
MACHINE_START(REALVIEW_PBA8, "ARM-RealView PB-A8")
diff --git a/arch/arm/mach-realview/realview_pbx.c b/arch/arm/mach-realview/realview_pbx.c
index a4b1aa93bb5a..4f486f05108a 100644
--- a/arch/arm/mach-realview/realview_pbx.c
+++ b/arch/arm/mach-realview/realview_pbx.c
@@ -29,7 +29,6 @@
#include <linux/platform_data/clk-realview.h>
#include <asm/irq.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include <asm/smp_twd.h>
#include <asm/pgtable.h>
@@ -395,10 +394,6 @@ static void __init realview_pbx_init(void)
struct amba_device *d = amba_devs[i];
amba_device_register(d, &iomem_resource);
}
-
-#ifdef CONFIG_LEDS
- leds_event = realview_leds_event;
-#endif
}
MACHINE_START(REALVIEW_PBX, "ARM-RealView PBX")
diff --git a/arch/arm/mach-s3c24xx/clock-s3c2440.c b/arch/arm/mach-s3c24xx/clock-s3c2440.c
index cb2883d553b5..749220f91a70 100644
--- a/arch/arm/mach-s3c24xx/clock-s3c2440.c
+++ b/arch/arm/mach-s3c24xx/clock-s3c2440.c
@@ -87,6 +87,19 @@ static int s3c2440_camif_upll_setrate(struct clk *clk, unsigned long rate)
return 0;
}
+static unsigned long s3c2440_camif_upll_getrate(struct clk *clk)
+{
+ unsigned long parent_rate = clk_get_rate(clk->parent);
+ unsigned long camdivn = __raw_readl(S3C2440_CAMDIVN);
+
+ if (!(camdivn & S3C2440_CAMDIVN_CAMCLK_SEL))
+ return parent_rate;
+
+ camdivn &= S3C2440_CAMDIVN_CAMCLK_MASK;
+
+ return parent_rate / (camdivn + 1) / 2;
+}
+
/* Extra S3C2440 clocks */
static struct clk s3c2440_clk_cam = {
@@ -99,6 +112,7 @@ static struct clk s3c2440_clk_cam_upll = {
.name = "camif-upll",
.ops = &(struct clk_ops) {
.set_rate = s3c2440_camif_upll_setrate,
+ .get_rate = s3c2440_camif_upll_getrate,
.round_rate = s3c2440_camif_upll_round,
},
};
diff --git a/arch/arm/mach-s3c24xx/mach-h1940.c b/arch/arm/mach-s3c24xx/mach-h1940.c
index 9638b337593c..63aaf076f611 100644
--- a/arch/arm/mach-s3c24xx/mach-h1940.c
+++ b/arch/arm/mach-s3c24xx/mach-h1940.c
@@ -380,7 +380,7 @@ int h1940_led_blink_set(unsigned gpio, int state,
default:
blink_gpio = S3C2410_GPA(3);
check_gpio1 = S3C2410_GPA(1);
- check_gpio1 = S3C2410_GPA(7);
+ check_gpio2 = S3C2410_GPA(7);
break;
}
@@ -460,7 +460,7 @@ static void h1940_set_mmc_power(unsigned char power_mode, unsigned short vdd)
break;
default:
break;
- };
+ }
}
static struct s3c24xx_mci_pdata h1940_mmc_cfg __initdata = {
diff --git a/arch/arm/mach-s3c64xx/mach-crag6410-module.c b/arch/arm/mach-s3c64xx/mach-crag6410-module.c
index 181aa99427fe..4e3fe57674c8 100644
--- a/arch/arm/mach-s3c64xx/mach-crag6410-module.c
+++ b/arch/arm/mach-s3c64xx/mach-crag6410-module.c
@@ -16,6 +16,7 @@
#include <linux/mfd/wm831x/irq.h>
#include <linux/mfd/wm831x/gpio.h>
#include <linux/mfd/wm8994/pdata.h>
+#include <linux/mfd/arizona/pdata.h>
#include <linux/regulator/machine.h>
@@ -181,9 +182,33 @@ static const struct i2c_board_info wm1277_devs[] = {
},
};
-static const struct i2c_board_info wm5102_devs[] = {
- { I2C_BOARD_INFO("wm5102", 0x1a),
- .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2, },
+static struct arizona_pdata wm5102_pdata = {
+ .ldoena = S3C64XX_GPN(7),
+ .gpio_base = CODEC_GPIO_BASE,
+ .irq_active_high = true,
+ .micd_pol_gpio = CODEC_GPIO_BASE + 4,
+ .gpio_defaults = {
+ [2] = 0x10000, /* AIF3TXLRCLK */
+ [3] = 0x4, /* OPCLK */
+ },
+};
+
+static struct s3c64xx_spi_csinfo wm5102_spi_csinfo = {
+ .line = S3C64XX_GPN(5),
+};
+
+static struct spi_board_info wm5102_spi_devs[] = {
+ [0] = {
+ .modalias = "wm5102",
+ .max_speed_hz = 10 * 1000 * 1000,
+ .bus_num = 0,
+ .chip_select = 0,
+ .mode = SPI_MODE_0,
+ .irq = GLENFARCLAS_PMIC_IRQ_BASE +
+ WM831X_IRQ_GPIO_2,
+ .controller_data = &wm5102_spi_csinfo,
+ .platform_data = &wm5102_pdata,
+ },
};
static const struct i2c_board_info wm6230_i2c_devs[] = {
@@ -223,8 +248,9 @@ static __devinitdata const struct {
{ .id = 0x3c, .name = "1273-EV1 Longmorn" },
{ .id = 0x3d, .name = "1277-EV1 Littlemill",
.i2c_devs = wm1277_devs, .num_i2c_devs = ARRAY_SIZE(wm1277_devs) },
- { .id = 0x3e, .name = "WM5102-6271-EV1-CS127",
- .i2c_devs = wm5102_devs, .num_i2c_devs = ARRAY_SIZE(wm5102_devs) },
+ { .id = 0x3e, .name = "WM5102-6271-EV1-CS127 Amrut",
+ .spi_devs = wm5102_spi_devs,
+ .num_spi_devs = ARRAY_SIZE(wm5102_spi_devs) },
};
static __devinit int wlf_gf_module_probe(struct i2c_client *i2c,
diff --git a/arch/arm/mach-s3c64xx/mach-crag6410.c b/arch/arm/mach-s3c64xx/mach-crag6410.c
index 717d675188d4..8b4d46706645 100644
--- a/arch/arm/mach-s3c64xx/mach-crag6410.c
+++ b/arch/arm/mach-s3c64xx/mach-crag6410.c
@@ -287,6 +287,16 @@ static struct platform_device littlemill_device = {
.id = -1,
};
+static struct platform_device bells_wm5102_device = {
+ .name = "bells",
+ .id = 0,
+};
+
+static struct platform_device bells_wm5110_device = {
+ .name = "bells",
+ .id = 1,
+};
+
static struct regulator_consumer_supply wallvdd_consumers[] = {
REGULATOR_SUPPLY("SPKVDD", "1-001a"),
REGULATOR_SUPPLY("SPKVDD1", "1-001a"),
@@ -359,6 +369,8 @@ static struct platform_device *crag6410_devices[] __initdata = {
&tobermory_device,
&littlemill_device,
&lowland_device,
+ &bells_wm5102_device,
+ &bells_wm5110_device,
&wallvdd_device,
};
diff --git a/arch/arm/mach-sa1100/Makefile b/arch/arm/mach-sa1100/Makefile
index 60b97ec01676..1aed9e70465d 100644
--- a/arch/arm/mach-sa1100/Makefile
+++ b/arch/arm/mach-sa1100/Makefile
@@ -7,21 +7,17 @@ obj-y := clock.o generic.o irq.o time.o #nmi-oopser.o
obj-m :=
obj-n :=
obj- :=
-led-y := leds.o
obj-$(CONFIG_CPU_FREQ_SA1100) += cpu-sa1100.o
obj-$(CONFIG_CPU_FREQ_SA1110) += cpu-sa1110.o
# Specific board support
obj-$(CONFIG_SA1100_ASSABET) += assabet.o
-led-$(CONFIG_SA1100_ASSABET) += leds-assabet.o
obj-$(CONFIG_ASSABET_NEPONSET) += neponset.o
obj-$(CONFIG_SA1100_BADGE4) += badge4.o
-led-$(CONFIG_SA1100_BADGE4) += leds-badge4.o
obj-$(CONFIG_SA1100_CERF) += cerf.o
-led-$(CONFIG_SA1100_CERF) += leds-cerf.o
obj-$(CONFIG_SA1100_COLLIE) += collie.o
@@ -29,13 +25,11 @@ obj-$(CONFIG_SA1100_H3100) += h3100.o h3xxx.o
obj-$(CONFIG_SA1100_H3600) += h3600.o h3xxx.o
obj-$(CONFIG_SA1100_HACKKIT) += hackkit.o
-led-$(CONFIG_SA1100_HACKKIT) += leds-hackkit.o
obj-$(CONFIG_SA1100_JORNADA720) += jornada720.o
obj-$(CONFIG_SA1100_JORNADA720_SSP) += jornada720_ssp.o
obj-$(CONFIG_SA1100_LART) += lart.o
-led-$(CONFIG_SA1100_LART) += leds-lart.o
obj-$(CONFIG_SA1100_NANOENGINE) += nanoengine.o
obj-$(CONFIG_PCI_NANOENGINE) += pci-nanoengine.o
@@ -46,9 +40,6 @@ obj-$(CONFIG_SA1100_SHANNON) += shannon.o
obj-$(CONFIG_SA1100_SIMPAD) += simpad.o
-# LEDs support
-obj-$(CONFIG_LEDS) += $(led-y)
-
# Miscellaneous functions
obj-$(CONFIG_PM) += pm.o sleep.o
obj-$(CONFIG_SA1100_SSP) += ssp.o
diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c
index ba49241b02f0..e1ccda6128eb 100644
--- a/arch/arm/mach-sa1100/assabet.c
+++ b/arch/arm/mach-sa1100/assabet.c
@@ -20,6 +20,8 @@
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
#include <linux/mm.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
#include <video/sa1100fb.h>
@@ -529,6 +531,89 @@ static void __init assabet_map_io(void)
sa1100_register_uart(2, 3);
}
+/* LEDs */
+#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
+struct assabet_led {
+ struct led_classdev cdev;
+ u32 mask;
+};
+
+/*
+ * The triggers lines up below will only be used if the
+ * LED triggers are compiled in.
+ */
+static const struct {
+ const char *name;
+ const char *trigger;
+} assabet_leds[] = {
+ { "assabet:red", "cpu0",},
+ { "assabet:green", "heartbeat", },
+};
+
+/*
+ * The LED control in Assabet is reversed:
+ * - setting bit means turn off LED
+ * - clearing bit means turn on LED
+ */
+static void assabet_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
+{
+ struct assabet_led *led = container_of(cdev,
+ struct assabet_led, cdev);
+
+ if (b != LED_OFF)
+ ASSABET_BCR_clear(led->mask);
+ else
+ ASSABET_BCR_set(led->mask);
+}
+
+static enum led_brightness assabet_led_get(struct led_classdev *cdev)
+{
+ struct assabet_led *led = container_of(cdev,
+ struct assabet_led, cdev);
+
+ return (ASSABET_BCR & led->mask) ? LED_OFF : LED_FULL;
+}
+
+static int __init assabet_leds_init(void)
+{
+ int i;
+
+ if (!machine_is_assabet())
+ return -ENODEV;
+
+ for (i = 0; i < ARRAY_SIZE(assabet_leds); i++) {
+ struct assabet_led *led;
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ break;
+
+ led->cdev.name = assabet_leds[i].name;
+ led->cdev.brightness_set = assabet_led_set;
+ led->cdev.brightness_get = assabet_led_get;
+ led->cdev.default_trigger = assabet_leds[i].trigger;
+
+ if (!i)
+ led->mask = ASSABET_BCR_LED_RED;
+ else
+ led->mask = ASSABET_BCR_LED_GREEN;
+
+ if (led_classdev_register(NULL, &led->cdev) < 0) {
+ kfree(led);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Since we may have triggers on any subsystem, defer registration
+ * until after subsystem_init.
+ */
+fs_initcall(assabet_leds_init);
+#endif
MACHINE_START(ASSABET, "Intel-Assabet")
.atag_offset = 0x100,
diff --git a/arch/arm/mach-sa1100/badge4.c b/arch/arm/mach-sa1100/badge4.c
index b30fb99b587c..038df4894b0f 100644
--- a/arch/arm/mach-sa1100/badge4.c
+++ b/arch/arm/mach-sa1100/badge4.c
@@ -22,6 +22,8 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/errno.h>
+#include <linux/gpio.h>
+#include <linux/leds.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
@@ -76,8 +78,36 @@ static struct platform_device sa1111_device = {
.resource = sa1111_resources,
};
+/* LEDs */
+struct gpio_led badge4_gpio_leds[] = {
+ {
+ .name = "badge4:red",
+ .default_trigger = "heartbeat",
+ .gpio = 7,
+ },
+ {
+ .name = "badge4:green",
+ .default_trigger = "cpu0",
+ .gpio = 9,
+ },
+};
+
+static struct gpio_led_platform_data badge4_gpio_led_info = {
+ .leds = badge4_gpio_leds,
+ .num_leds = ARRAY_SIZE(badge4_gpio_leds),
+};
+
+static struct platform_device badge4_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &badge4_gpio_led_info,
+ }
+};
+
static struct platform_device *devices[] __initdata = {
&sa1111_device,
+ &badge4_leds,
};
static int __init badge4_sa1111_init(void)
diff --git a/arch/arm/mach-sa1100/cerf.c b/arch/arm/mach-sa1100/cerf.c
index 985d0b584717..ad0eb08ea077 100644
--- a/arch/arm/mach-sa1100/cerf.c
+++ b/arch/arm/mach-sa1100/cerf.c
@@ -17,6 +17,8 @@
#include <linux/irq.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
+#include <linux/gpio.h>
+#include <linux/leds.h>
#include <mach/hardware.h>
#include <asm/setup.h>
@@ -43,8 +45,48 @@ static struct platform_device cerfuart2_device = {
.resource = cerfuart2_resources,
};
+/* LEDs */
+struct gpio_led cerf_gpio_leds[] = {
+ {
+ .name = "cerf:d0",
+ .default_trigger = "heartbeat",
+ .gpio = 0,
+ },
+ {
+ .name = "cerf:d1",
+ .default_trigger = "cpu0",
+ .gpio = 1,
+ },
+ {
+ .name = "cerf:d2",
+ .default_trigger = "default-on",
+ .gpio = 2,
+ },
+ {
+ .name = "cerf:d3",
+ .default_trigger = "default-on",
+ .gpio = 3,
+ },
+
+};
+
+static struct gpio_led_platform_data cerf_gpio_led_info = {
+ .leds = cerf_gpio_leds,
+ .num_leds = ARRAY_SIZE(cerf_gpio_leds),
+};
+
+static struct platform_device cerf_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &cerf_gpio_led_info,
+ }
+};
+
+
static struct platform_device *cerf_devices[] __initdata = {
&cerfuart2_device,
+ &cerf_leds,
};
#ifdef CONFIG_SA1100_CERF_FLASH_32MB
diff --git a/arch/arm/mach-sa1100/hackkit.c b/arch/arm/mach-sa1100/hackkit.c
index 7f86bd911826..fc106aab7c7e 100644
--- a/arch/arm/mach-sa1100/hackkit.c
+++ b/arch/arm/mach-sa1100/hackkit.c
@@ -21,6 +21,10 @@
#include <linux/serial_core.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
+#include <linux/tty.h>
+#include <linux/gpio.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
#include <asm/mach-types.h>
#include <asm/setup.h>
@@ -183,9 +187,37 @@ static struct flash_platform_data hackkit_flash_data = {
static struct resource hackkit_flash_resource =
DEFINE_RES_MEM(SA1100_CS0_PHYS, SZ_32M);
+/* LEDs */
+struct gpio_led hackkit_gpio_leds[] = {
+ {
+ .name = "hackkit:red",
+ .default_trigger = "cpu0",
+ .gpio = 22,
+ },
+ {
+ .name = "hackkit:green",
+ .default_trigger = "heartbeat",
+ .gpio = 23,
+ },
+};
+
+static struct gpio_led_platform_data hackkit_gpio_led_info = {
+ .leds = hackkit_gpio_leds,
+ .num_leds = ARRAY_SIZE(hackkit_gpio_leds),
+};
+
+static struct platform_device hackkit_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &hackkit_gpio_led_info,
+ }
+};
+
static void __init hackkit_init(void)
{
sa11x0_register_mtd(&hackkit_flash_data, &hackkit_flash_resource, 1);
+ platform_device_register(&hackkit_leds);
}
/**********************************************************************
diff --git a/arch/arm/mach-sa1100/include/mach/simpad.h b/arch/arm/mach-sa1100/include/mach/simpad.h
index cdea671e8931..ac2ea767215d 100644
--- a/arch/arm/mach-sa1100/include/mach/simpad.h
+++ b/arch/arm/mach-sa1100/include/mach/simpad.h
@@ -87,7 +87,7 @@
#define SIMPAD_CS3_PCMCIA_SHORT (SIMPAD_CS3_GPIO_BASE + 22)
#define SIMPAD_CS3_GPIO_23 (SIMPAD_CS3_GPIO_BASE + 23)
-#define CS3_BASE 0xf1000000
+#define CS3_BASE IOMEM(0xf1000000)
long simpad_get_cs3_ro(void);
long simpad_get_cs3_shadow(void);
diff --git a/arch/arm/mach-sa1100/lart.c b/arch/arm/mach-sa1100/lart.c
index 7dc1a89b1273..3048b17e84c5 100644
--- a/arch/arm/mach-sa1100/lart.c
+++ b/arch/arm/mach-sa1100/lart.c
@@ -5,6 +5,9 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/tty.h>
+#include <linux/gpio.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
#include <video/sa1100fb.h>
@@ -126,6 +129,27 @@ static struct map_desc lart_io_desc[] __initdata = {
}
};
+/* LEDs */
+struct gpio_led lart_gpio_leds[] = {
+ {
+ .name = "lart:red",
+ .default_trigger = "cpu0",
+ .gpio = 23,
+ },
+};
+
+static struct gpio_led_platform_data lart_gpio_led_info = {
+ .leds = lart_gpio_leds,
+ .num_leds = ARRAY_SIZE(lart_gpio_leds),
+};
+
+static struct platform_device lart_leds = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &lart_gpio_led_info,
+ }
+};
static void __init lart_map_io(void)
{
sa1100_map_io();
@@ -139,6 +163,8 @@ static void __init lart_map_io(void)
GPDR |= GPIO_UART_TXD;
GPDR &= ~GPIO_UART_RXD;
PPAR |= PPAR_UPR;
+
+ platform_device_register(&lart_leds);
}
MACHINE_START(LART, "LART")
diff --git a/arch/arm/mach-sa1100/leds-assabet.c b/arch/arm/mach-sa1100/leds-assabet.c
deleted file mode 100644
index 3699176bca94..000000000000
--- a/arch/arm/mach-sa1100/leds-assabet.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * linux/arch/arm/mach-sa1100/leds-assabet.c
- *
- * Copyright (C) 2000 John Dorsey <john+@cs.cmu.edu>
- *
- * Original (leds-footbridge.c) by Russell King
- *
- * Assabet uses the LEDs as follows:
- * - Green - toggles state every 50 timer interrupts
- * - Red - on if system is not idle
- */
-#include <linux/init.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-#include <mach/assabet.h>
-
-#include "leds.h"
-
-
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
-
-static unsigned int led_state;
-static unsigned int hw_led_state;
-
-#define ASSABET_BCR_LED_MASK (ASSABET_BCR_LED_GREEN | ASSABET_BCR_LED_RED)
-
-void assabet_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- switch (evt) {
- case led_start:
- hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN;
- led_state = LED_STATE_ENABLED;
- break;
-
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN;
- ASSABET_BCR_frob(ASSABET_BCR_LED_MASK, hw_led_state);
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = ASSABET_BCR_LED_RED | ASSABET_BCR_LED_GREEN;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state ^= ASSABET_BCR_LED_GREEN;
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= ASSABET_BCR_LED_RED;
- break;
-
- case led_idle_end:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~ASSABET_BCR_LED_RED;
- break;
-#endif
-
- case led_halted:
- break;
-
- case led_green_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~ASSABET_BCR_LED_GREEN;
- break;
-
- case led_green_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= ASSABET_BCR_LED_GREEN;
- break;
-
- case led_amber_on:
- break;
-
- case led_amber_off:
- break;
-
- case led_red_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~ASSABET_BCR_LED_RED;
- break;
-
- case led_red_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= ASSABET_BCR_LED_RED;
- break;
-
- default:
- break;
- }
-
- if (led_state & LED_STATE_ENABLED)
- ASSABET_BCR_frob(ASSABET_BCR_LED_MASK, hw_led_state);
-
- local_irq_restore(flags);
-}
diff --git a/arch/arm/mach-sa1100/leds-badge4.c b/arch/arm/mach-sa1100/leds-badge4.c
deleted file mode 100644
index f99fac3eedb6..000000000000
--- a/arch/arm/mach-sa1100/leds-badge4.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * linux/arch/arm/mach-sa1100/leds-badge4.c
- *
- * Author: Christopher Hoover <ch@hpl.hp.com>
- * Copyright (C) 2002 Hewlett-Packard Company
- *
- * This 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 <mach/hardware.h>
-#include <asm/leds.h>
-
-#include "leds.h"
-
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
-
-static unsigned int led_state;
-static unsigned int hw_led_state;
-
-#define LED_RED GPIO_GPIO(7)
-#define LED_GREEN GPIO_GPIO(9)
-#define LED_MASK (LED_RED|LED_GREEN)
-
-#define LED_IDLE LED_GREEN
-#define LED_TIMER LED_RED
-
-void badge4_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- switch (evt) {
- case led_start:
- GPDR |= LED_MASK;
- hw_led_state = LED_MASK;
- led_state = LED_STATE_ENABLED;
- break;
-
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = LED_MASK;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = LED_MASK;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state ^= LED_TIMER;
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- /* LED off when system is idle */
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~LED_IDLE;
- break;
-
- case led_idle_end:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= LED_IDLE;
- break;
-#endif
-
- case led_red_on:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~LED_RED;
- break;
-
- case led_red_off:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= LED_RED;
- break;
-
- case led_green_on:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~LED_GREEN;
- break;
-
- case led_green_off:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= LED_GREEN;
- break;
-
- default:
- break;
- }
-
- if (led_state & LED_STATE_ENABLED) {
- GPSR = hw_led_state;
- GPCR = hw_led_state ^ LED_MASK;
- }
-
- local_irq_restore(flags);
-}
diff --git a/arch/arm/mach-sa1100/leds-cerf.c b/arch/arm/mach-sa1100/leds-cerf.c
deleted file mode 100644
index 30fc3b2bf555..000000000000
--- a/arch/arm/mach-sa1100/leds-cerf.c
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * linux/arch/arm/mach-sa1100/leds-cerf.c
- *
- * Author: ???
- */
-#include <linux/init.h>
-#include <linux/io.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-
-#include "leds.h"
-
-
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
-
-static unsigned int led_state;
-static unsigned int hw_led_state;
-
-#define LED_D0 GPIO_GPIO(0)
-#define LED_D1 GPIO_GPIO(1)
-#define LED_D2 GPIO_GPIO(2)
-#define LED_D3 GPIO_GPIO(3)
-#define LED_MASK (LED_D0|LED_D1|LED_D2|LED_D3)
-
-void cerf_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- switch (evt) {
- case led_start:
- hw_led_state = LED_MASK;
- led_state = LED_STATE_ENABLED;
- break;
-
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = LED_MASK;
- break;
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = LED_MASK;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state ^= LED_D0;
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~LED_D1;
- break;
-
- case led_idle_end:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= LED_D1;
- break;
-#endif
- case led_green_on:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~LED_D2;
- break;
-
- case led_green_off:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= LED_D2;
- break;
-
- case led_amber_on:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~LED_D3;
- break;
-
- case led_amber_off:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= LED_D3;
- break;
-
- case led_red_on:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~LED_D1;
- break;
-
- case led_red_off:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= LED_D1;
- break;
-
- default:
- break;
- }
-
- if (led_state & LED_STATE_ENABLED) {
- GPSR = hw_led_state;
- GPCR = hw_led_state ^ LED_MASK;
- }
-
- local_irq_restore(flags);
-}
diff --git a/arch/arm/mach-sa1100/leds-hackkit.c b/arch/arm/mach-sa1100/leds-hackkit.c
deleted file mode 100644
index f8e47235babe..000000000000
--- a/arch/arm/mach-sa1100/leds-hackkit.c
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * linux/arch/arm/mach-sa1100/leds-hackkit.c
- *
- * based on leds-lart.c
- *
- * (C) Erik Mouw (J.A.K.Mouw@its.tudelft.nl), April 21, 2000
- * (C) Stefan Eletzhofer <stefan.eletzhofer@eletztrick.de>, 2002
- *
- * The HackKit has two leds (GPIO 22/23). The red led (gpio 22) is used
- * as cpu led, the green one is used as timer led.
- */
-#include <linux/init.h>
-#include <linux/io.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-
-#include "leds.h"
-
-
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
-
-static unsigned int led_state;
-static unsigned int hw_led_state;
-
-#define LED_GREEN GPIO_GPIO23
-#define LED_RED GPIO_GPIO22
-#define LED_MASK (LED_RED | LED_GREEN)
-
-void hackkit_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- switch(evt) {
- case led_start:
- /* pin 22/23 are outputs */
- GPDR |= LED_MASK;
- hw_led_state = LED_MASK;
- led_state = LED_STATE_ENABLED;
- break;
-
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = LED_MASK;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = LED_MASK;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state ^= LED_GREEN;
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- /* The LART people like the LED to be off when the
- system is idle... */
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~LED_RED;
- break;
-
- case led_idle_end:
- /* ... and on if the system is not idle */
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= LED_RED;
- break;
-#endif
-
- case led_red_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~LED_RED;
- break;
-
- case led_red_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= LED_RED;
- break;
-
- case led_green_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~LED_GREEN;
- break;
-
- case led_green_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= LED_GREEN;
- break;
-
- default:
- break;
- }
-
- /* Now set the GPIO state, or nothing will happen at all */
- if (led_state & LED_STATE_ENABLED) {
- GPSR = hw_led_state;
- GPCR = hw_led_state ^ LED_MASK;
- }
-
- local_irq_restore(flags);
-}
diff --git a/arch/arm/mach-sa1100/leds-lart.c b/arch/arm/mach-sa1100/leds-lart.c
deleted file mode 100644
index 50a5b143b460..000000000000
--- a/arch/arm/mach-sa1100/leds-lart.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * linux/arch/arm/mach-sa1100/leds-lart.c
- *
- * (C) Erik Mouw (J.A.K.Mouw@its.tudelft.nl), April 21, 2000
- *
- * LART uses the LED as follows:
- * - GPIO23 is the LED, on if system is not idle
- * You can use both CONFIG_LEDS_CPU and CONFIG_LEDS_TIMER at the same
- * time, but in that case the timer events will still dictate the
- * pace of the LED.
- */
-#include <linux/init.h>
-#include <linux/io.h>
-
-#include <mach/hardware.h>
-#include <asm/leds.h>
-
-#include "leds.h"
-
-
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
-
-static unsigned int led_state;
-static unsigned int hw_led_state;
-
-#define LED_23 GPIO_GPIO23
-#define LED_MASK (LED_23)
-
-void lart_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- local_irq_save(flags);
-
- switch(evt) {
- case led_start:
- /* pin 23 is output pin */
- GPDR |= LED_23;
- hw_led_state = LED_MASK;
- led_state = LED_STATE_ENABLED;
- break;
-
- case led_stop:
- led_state &= ~LED_STATE_ENABLED;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = LED_MASK;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = LED_MASK;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state ^= LED_23;
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- /* The LART people like the LED to be off when the
- system is idle... */
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~LED_23;
- break;
-
- case led_idle_end:
- /* ... and on if the system is not idle */
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= LED_23;
- break;
-#endif
-
- case led_red_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~LED_23;
- break;
-
- case led_red_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= LED_23;
- break;
-
- default:
- break;
- }
-
- /* Now set the GPIO state, or nothing will happen at all */
- if (led_state & LED_STATE_ENABLED) {
- GPSR = hw_led_state;
- GPCR = hw_led_state ^ LED_MASK;
- }
-
- local_irq_restore(flags);
-}
diff --git a/arch/arm/mach-sa1100/leds.c b/arch/arm/mach-sa1100/leds.c
deleted file mode 100644
index 5fe71a0f1053..000000000000
--- a/arch/arm/mach-sa1100/leds.c
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * linux/arch/arm/mach-sa1100/leds.c
- *
- * SA1100 LEDs dispatcher
- *
- * Copyright (C) 2001 Nicolas Pitre
- */
-#include <linux/compiler.h>
-#include <linux/init.h>
-
-#include <asm/leds.h>
-#include <asm/mach-types.h>
-
-#include "leds.h"
-
-static int __init
-sa1100_leds_init(void)
-{
- if (machine_is_assabet())
- leds_event = assabet_leds_event;
- if (machine_is_consus())
- leds_event = consus_leds_event;
- if (machine_is_badge4())
- leds_event = badge4_leds_event;
- if (machine_is_brutus())
- leds_event = brutus_leds_event;
- if (machine_is_cerf())
- leds_event = cerf_leds_event;
- if (machine_is_flexanet())
- leds_event = flexanet_leds_event;
- if (machine_is_graphicsclient())
- leds_event = graphicsclient_leds_event;
- if (machine_is_hackkit())
- leds_event = hackkit_leds_event;
- if (machine_is_lart())
- leds_event = lart_leds_event;
- if (machine_is_pfs168())
- leds_event = pfs168_leds_event;
- if (machine_is_graphicsmaster())
- leds_event = graphicsmaster_leds_event;
- if (machine_is_adsbitsy())
- leds_event = adsbitsy_leds_event;
- if (machine_is_pt_system3())
- leds_event = system3_leds_event;
-
- leds_event(led_start);
- return 0;
-}
-
-core_initcall(sa1100_leds_init);
diff --git a/arch/arm/mach-sa1100/leds.h b/arch/arm/mach-sa1100/leds.h
deleted file mode 100644
index 776b6020f550..000000000000
--- a/arch/arm/mach-sa1100/leds.h
+++ /dev/null
@@ -1,13 +0,0 @@
-extern void assabet_leds_event(led_event_t evt);
-extern void badge4_leds_event(led_event_t evt);
-extern void consus_leds_event(led_event_t evt);
-extern void brutus_leds_event(led_event_t evt);
-extern void cerf_leds_event(led_event_t evt);
-extern void flexanet_leds_event(led_event_t evt);
-extern void graphicsclient_leds_event(led_event_t evt);
-extern void hackkit_leds_event(led_event_t evt);
-extern void lart_leds_event(led_event_t evt);
-extern void pfs168_leds_event(led_event_t evt);
-extern void graphicsmaster_leds_event(led_event_t evt);
-extern void adsbitsy_leds_event(led_event_t evt);
-extern void system3_leds_event(led_event_t evt);
diff --git a/arch/arm/mach-sa1100/simpad.c b/arch/arm/mach-sa1100/simpad.c
index c73e1a3b430a..71790e581d93 100644
--- a/arch/arm/mach-sa1100/simpad.c
+++ b/arch/arm/mach-sa1100/simpad.c
@@ -124,7 +124,7 @@ static struct map_desc simpad_io_desc[] __initdata = {
.length = 0x00800000,
.type = MT_DEVICE
}, { /* Simpad CS3 */
- .virtual = CS3_BASE,
+ .virtual = (unsigned long)CS3_BASE,
.pfn = __phys_to_pfn(SA1100_CS3_PHYS),
.length = 0x00100000,
.type = MT_DEVICE
diff --git a/arch/arm/mach-shark/Makefile b/arch/arm/mach-shark/Makefile
index 45be9b04e7ba..29657183c452 100644
--- a/arch/arm/mach-shark/Makefile
+++ b/arch/arm/mach-shark/Makefile
@@ -4,9 +4,7 @@
# Object file lists.
-obj-y := core.o dma.o irq.o pci.o
+obj-y := core.o dma.o irq.o pci.o leds.o
obj-m :=
obj-n :=
obj- :=
-
-obj-$(CONFIG_LEDS) += leds.o
diff --git a/arch/arm/mach-shark/core.c b/arch/arm/mach-shark/core.c
index d35b94ef73b7..9ad2e9737fb5 100644
--- a/arch/arm/mach-shark/core.c
+++ b/arch/arm/mach-shark/core.c
@@ -13,7 +13,6 @@
#include <asm/setup.h>
#include <asm/mach-types.h>
-#include <asm/leds.h>
#include <asm/param.h>
#include <asm/system_misc.h>
diff --git a/arch/arm/mach-shark/leds.c b/arch/arm/mach-shark/leds.c
index 25609076921f..081c778a10ac 100644
--- a/arch/arm/mach-shark/leds.c
+++ b/arch/arm/mach-shark/leds.c
@@ -1,165 +1,117 @@
/*
- * arch/arm/mach-shark/leds.c
- * by Alexander Schulz
- *
- * derived from:
- * arch/arm/kernel/leds-footbridge.c
- * Copyright (C) 1998-1999 Russell King
- *
* DIGITAL Shark LED control routines.
*
- * The leds use is as follows:
- * - Green front - toggles state every 50 timer interrupts
- * - Amber front - Unused, this is a dual color led (Amber/Green)
- * - Amber back - On if system is not idle
+ * Driver for the 3 user LEDs found on the Shark
+ * Based on Versatile and RealView machine LED code
*
- * Changelog:
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Bryan Wu <bryan.wu@canonical.com>
*/
#include <linux/kernel.h>
-#include <linux/module.h>
#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/ioport.h>
#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
-#include <asm/leds.h>
+#include <asm/mach-types.h>
-#define LED_STATE_ENABLED 1
-#define LED_STATE_CLAIMED 2
+#if defined(CONFIG_NEW_LEDS) && defined(CONFIG_LEDS_CLASS)
+struct shark_led {
+ struct led_classdev cdev;
+ u8 mask;
+};
-#define SEQUOIA_LED_GREEN (1<<6)
-#define SEQUOIA_LED_AMBER (1<<5)
-#define SEQUOIA_LED_BACK (1<<7)
+/*
+ * The triggers lines up below will only be used if the
+ * LED triggers are compiled in.
+ */
+static const struct {
+ const char *name;
+ const char *trigger;
+} shark_leds[] = {
+ { "shark:amber0", "default-on", }, /* Bit 5 */
+ { "shark:green", "heartbeat", }, /* Bit 6 */
+ { "shark:amber1", "cpu0" }, /* Bit 7 */
+};
+
+static u16 led_reg_read(void)
+{
+ outw(0x09, 0x24);
+ return inw(0x26);
+}
-static char led_state;
-static short hw_led_state;
-static short saved_state;
+static void led_reg_write(u16 value)
+{
+ outw(0x09, 0x24);
+ outw(value, 0x26);
+}
-static DEFINE_RAW_SPINLOCK(leds_lock);
+static void shark_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
+{
+ struct shark_led *led = container_of(cdev,
+ struct shark_led, cdev);
+ u16 reg = led_reg_read();
-short sequoia_read(int addr) {
- outw(addr,0x24);
- return inw(0x26);
-}
+ if (b != LED_OFF)
+ reg |= led->mask;
+ else
+ reg &= ~led->mask;
-void sequoia_write(short value,short addr) {
- outw(addr,0x24);
- outw(value,0x26);
+ led_reg_write(reg);
}
-static void sequoia_leds_event(led_event_t evt)
+static enum led_brightness shark_led_get(struct led_classdev *cdev)
{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&leds_lock, flags);
+ struct shark_led *led = container_of(cdev,
+ struct shark_led, cdev);
+ u16 reg = led_reg_read();
- hw_led_state = sequoia_read(0x09);
+ return (reg & led->mask) ? LED_FULL : LED_OFF;
+}
- switch (evt) {
- case led_start:
- hw_led_state |= SEQUOIA_LED_GREEN;
- hw_led_state |= SEQUOIA_LED_AMBER;
-#ifdef CONFIG_LEDS_CPU
- hw_led_state |= SEQUOIA_LED_BACK;
-#else
- hw_led_state &= ~SEQUOIA_LED_BACK;
-#endif
- led_state |= LED_STATE_ENABLED;
- break;
-
- case led_stop:
- hw_led_state &= ~SEQUOIA_LED_BACK;
- hw_led_state |= SEQUOIA_LED_GREEN;
- hw_led_state |= SEQUOIA_LED_AMBER;
- led_state &= ~LED_STATE_ENABLED;
- break;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- saved_state = hw_led_state;
- hw_led_state &= ~SEQUOIA_LED_BACK;
- hw_led_state |= SEQUOIA_LED_GREEN;
- hw_led_state |= SEQUOIA_LED_AMBER;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- hw_led_state = saved_state;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state ^= SEQUOIA_LED_GREEN;
- break;
-#endif
+static int __init shark_leds_init(void)
+{
+ int i;
+ u16 reg;
-#ifdef CONFIG_LEDS_CPU
- case led_idle_start:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state &= ~SEQUOIA_LED_BACK;
- break;
+ if (!machine_is_shark())
+ return -ENODEV;
- case led_idle_end:
- if (!(led_state & LED_STATE_CLAIMED))
- hw_led_state |= SEQUOIA_LED_BACK;
- break;
-#endif
+ for (i = 0; i < ARRAY_SIZE(shark_leds); i++) {
+ struct shark_led *led;
- case led_green_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~SEQUOIA_LED_GREEN;
- break;
-
- case led_green_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= SEQUOIA_LED_GREEN;
- break;
-
- case led_amber_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~SEQUOIA_LED_AMBER;
- break;
-
- case led_amber_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= SEQUOIA_LED_AMBER;
- break;
-
- case led_red_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= SEQUOIA_LED_BACK;
- break;
-
- case led_red_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~SEQUOIA_LED_BACK;
- break;
-
- default:
- break;
- }
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ break;
- if (led_state & LED_STATE_ENABLED)
- sequoia_write(hw_led_state,0x09);
+ led->cdev.name = shark_leds[i].name;
+ led->cdev.brightness_set = shark_led_set;
+ led->cdev.brightness_get = shark_led_get;
+ led->cdev.default_trigger = shark_leds[i].trigger;
- raw_spin_unlock_irqrestore(&leds_lock, flags);
-}
+ /* Count in 5 bits offset */
+ led->mask = BIT(i + 5);
-static int __init leds_init(void)
-{
- extern void (*leds_event)(led_event_t);
- short temp;
-
- leds_event = sequoia_leds_event;
+ if (led_classdev_register(NULL, &led->cdev) < 0) {
+ kfree(led);
+ break;
+ }
+ }
/* Make LEDs independent of power-state */
- request_region(0x24,4,"sequoia");
- temp = sequoia_read(0x09);
- temp |= 1<<10;
- sequoia_write(temp,0x09);
- leds_event(led_start);
+ request_region(0x24, 4, "led_reg");
+ reg = led_reg_read();
+ reg |= 1 << 10;
+ led_reg_write(reg);
+
return 0;
}
-__initcall(leds_init);
+/*
+ * Since we may have triggers on any subsystem, defer registration
+ * until after subsystem_init.
+ */
+fs_initcall(shark_leds_init);
+#endif
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index c7f164e20661..bc3b5da59e25 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -434,7 +434,7 @@ static void usb1_host_port_power(int port, int power)
return;
/* set VBOUT/PWEN and EXTLP1 in DVSTCTR */
- __raw_writew(__raw_readw(0xE68B0008) | 0x600, 0xE68B0008);
+ __raw_writew(__raw_readw(IOMEM(0xE68B0008)) | 0x600, IOMEM(0xE68B0008));
}
static struct r8a66597_platdata usb1_host_data = {
@@ -1226,9 +1226,9 @@ static struct i2c_board_info i2c1_devices[] = {
};
-#define GPIO_PORT9CR 0xE6051009
-#define GPIO_PORT10CR 0xE605100A
-#define USCCR1 0xE6058144
+#define GPIO_PORT9CR IOMEM(0xE6051009)
+#define GPIO_PORT10CR IOMEM(0xE605100A)
+#define USCCR1 IOMEM(0xE6058144)
static void __init ap4evb_init(void)
{
u32 srcr4;
@@ -1306,7 +1306,7 @@ static void __init ap4evb_init(void)
gpio_request(GPIO_FN_OVCN2_1, NULL);
/* setup USB phy */
- __raw_writew(0x8a0a, 0xE6058130); /* USBCR4 */
+ __raw_writew(0x8a0a, IOMEM(0xE6058130)); /* USBCR4 */
/* enable FSI2 port A (ak4643) */
gpio_request(GPIO_FN_FSIAIBT, NULL);
@@ -1455,7 +1455,7 @@ static void __init ap4evb_init(void)
gpio_request(GPIO_FN_HDMI_CEC, NULL);
/* Reset HDMI, must be held at least one EXTALR (32768Hz) period */
-#define SRCR4 0xe61580bc
+#define SRCR4 IOMEM(0xe61580bc)
srcr4 = __raw_readl(SRCR4);
__raw_writel(srcr4 | (1 << 13), SRCR4);
udelay(50);
diff --git a/arch/arm/mach-shmobile/board-armadillo800eva.c b/arch/arm/mach-shmobile/board-armadillo800eva.c
index b573de073c2b..c6593d394273 100644
--- a/arch/arm/mach-shmobile/board-armadillo800eva.c
+++ b/arch/arm/mach-shmobile/board-armadillo800eva.c
@@ -37,6 +37,7 @@
#include <linux/mmc/host.h>
#include <linux/mmc/sh_mmcif.h>
#include <linux/mmc/sh_mobile_sdhi.h>
+#include <linux/i2c-gpio.h>
#include <mach/common.h>
#include <mach/irqs.h>
#include <mach/r8a7740.h>
@@ -137,7 +138,7 @@
* usbhsf_power_ctrl()
*/
#define IRQ7 evt2irq(0x02e0)
-#define USBCR1 0xe605810a
+#define USBCR1 IOMEM(0xe605810a)
#define USBH 0xC6700000
#define USBH_USBCTR 0x10834
@@ -879,6 +880,21 @@ static struct platform_device fsi_hdmi_device = {
},
};
+/* RTC: RTC connects i2c-gpio. */
+static struct i2c_gpio_platform_data i2c_gpio_data = {
+ .sda_pin = GPIO_PORT208,
+ .scl_pin = GPIO_PORT91,
+ .udelay = 5, /* 100 kHz */
+};
+
+static struct platform_device i2c_gpio_device = {
+ .name = "i2c-gpio",
+ .id = 2,
+ .dev = {
+ .platform_data = &i2c_gpio_data,
+ },
+};
+
/* I2C */
static struct i2c_board_info i2c0_devices[] = {
{
@@ -890,6 +906,13 @@ static struct i2c_board_info i2c0_devices[] = {
},
};
+static struct i2c_board_info i2c2_devices[] = {
+ {
+ I2C_BOARD_INFO("s35390a", 0x30),
+ .type = "s35390a",
+ },
+};
+
/*
* board devices
*/
@@ -906,6 +929,7 @@ static struct platform_device *eva_devices[] __initdata = {
&fsi_device,
&fsi_wm8978_device,
&fsi_hdmi_device,
+ &i2c_gpio_device,
};
static void __init eva_clock_init(void)
@@ -952,8 +976,8 @@ clock_error:
/*
* board init
*/
-#define GPIO_PORT7CR 0xe6050007
-#define GPIO_PORT8CR 0xe6050008
+#define GPIO_PORT7CR IOMEM(0xe6050007)
+#define GPIO_PORT8CR IOMEM(0xe6050008)
static void __init eva_init(void)
{
struct platform_device *usb = NULL;
@@ -1176,6 +1200,7 @@ static void __init eva_init(void)
#endif
i2c_register_board_info(0, i2c0_devices, ARRAY_SIZE(i2c0_devices));
+ i2c_register_board_info(2, i2c2_devices, ARRAY_SIZE(i2c2_devices));
r8a7740_add_standard_devices();
diff --git a/arch/arm/mach-shmobile/board-bonito.c b/arch/arm/mach-shmobile/board-bonito.c
index 4129008eae29..cb8c994e1430 100644
--- a/arch/arm/mach-shmobile/board-bonito.c
+++ b/arch/arm/mach-shmobile/board-bonito.c
@@ -108,12 +108,12 @@ static struct regulator_consumer_supply dummy_supplies[] = {
#define FPGA_ETH_IRQ (FPGA_IRQ0 + 15)
static u16 bonito_fpga_read(u32 offset)
{
- return __raw_readw(0xf0003000 + offset);
+ return __raw_readw(IOMEM(0xf0003000) + offset);
}
static void bonito_fpga_write(u32 offset, u16 val)
{
- __raw_writew(val, 0xf0003000 + offset);
+ __raw_writew(val, IOMEM(0xf0003000) + offset);
}
static void bonito_fpga_irq_disable(struct irq_data *data)
@@ -361,8 +361,8 @@ static void __init bonito_map_io(void)
#define BIT_ON(sw, bit) (sw & (1 << bit))
#define BIT_OFF(sw, bit) (!(sw & (1 << bit)))
-#define VCCQ1CR 0xE6058140
-#define VCCQ1LCDCR 0xE6058186
+#define VCCQ1CR IOMEM(0xE6058140)
+#define VCCQ1LCDCR IOMEM(0xE6058186)
static void __init bonito_init(void)
{
diff --git a/arch/arm/mach-shmobile/board-g3evm.c b/arch/arm/mach-shmobile/board-g3evm.c
index 796fa00ad3c4..b179d4c213bb 100644
--- a/arch/arm/mach-shmobile/board-g3evm.c
+++ b/arch/arm/mach-shmobile/board-g3evm.c
@@ -106,7 +106,7 @@ static void usb_host_port_power(int port, int power)
return;
/* set VBOUT/PWEN and EXTLP0 in DVSTCTR */
- __raw_writew(__raw_readw(0xe6890008) | 0x600, 0xe6890008);
+ __raw_writew(__raw_readw(IOMEM(0xe6890008)) | 0x600, IOMEM(0xe6890008));
}
static struct r8a66597_platdata usb_host_data = {
@@ -279,10 +279,10 @@ static void __init g3evm_init(void)
gpio_request(GPIO_FN_IDIN, NULL);
/* setup USB phy */
- __raw_writew(0x0300, 0xe605810a); /* USBCR1 */
- __raw_writew(0x00e0, 0xe60581c0); /* CPFCH */
- __raw_writew(0x6010, 0xe60581c6); /* CGPOSR */
- __raw_writew(0x8a0a, 0xe605810c); /* USBCR2 */
+ __raw_writew(0x0300, IOMEM(0xe605810a)); /* USBCR1 */
+ __raw_writew(0x00e0, IOMEM(0xe60581c0)); /* CPFCH */
+ __raw_writew(0x6010, IOMEM(0xe60581c6)); /* CGPOSR */
+ __raw_writew(0x8a0a, IOMEM(0xe605810c)); /* USBCR2 */
/* KEYSC @ CN7 */
gpio_request(GPIO_FN_PORT42_KEYOUT0, NULL);
@@ -320,7 +320,7 @@ static void __init g3evm_init(void)
gpio_request(GPIO_FN_WE0_XWR0_FWE, NULL);
gpio_request(GPIO_FN_FRB, NULL);
/* FOE, FCDE, FSC on dedicated pins */
- __raw_writel(__raw_readl(0xe6158048) & ~(1 << 15), 0xe6158048);
+ __raw_writel(__raw_readl(IOMEM(0xe6158048)) & ~(1 << 15), IOMEM(0xe6158048));
/* IrDA */
gpio_request(GPIO_FN_IRDA_OUT, NULL);
diff --git a/arch/arm/mach-shmobile/board-g4evm.c b/arch/arm/mach-shmobile/board-g4evm.c
index eeee12e4da62..35c126caa4d8 100644
--- a/arch/arm/mach-shmobile/board-g4evm.c
+++ b/arch/arm/mach-shmobile/board-g4evm.c
@@ -128,7 +128,7 @@ static void usb_host_port_power(int port, int power)
return;
/* set VBOUT/PWEN and EXTLP0 in DVSTCTR */
- __raw_writew(__raw_readw(0xe6890008) | 0x600, 0xe6890008);
+ __raw_writew(__raw_readw(IOMEM(0xe6890008)) | 0x600, IOMEM(0xe6890008));
}
static struct r8a66597_platdata usb_host_data = {
@@ -272,17 +272,17 @@ static struct platform_device *g4evm_devices[] __initdata = {
&sdhi1_device,
};
-#define GPIO_SDHID0_D0 0xe60520fc
-#define GPIO_SDHID0_D1 0xe60520fd
-#define GPIO_SDHID0_D2 0xe60520fe
-#define GPIO_SDHID0_D3 0xe60520ff
-#define GPIO_SDHICMD0 0xe6052100
+#define GPIO_SDHID0_D0 IOMEM(0xe60520fc)
+#define GPIO_SDHID0_D1 IOMEM(0xe60520fd)
+#define GPIO_SDHID0_D2 IOMEM(0xe60520fe)
+#define GPIO_SDHID0_D3 IOMEM(0xe60520ff)
+#define GPIO_SDHICMD0 IOMEM(0xe6052100)
-#define GPIO_SDHID1_D0 0xe6052103
-#define GPIO_SDHID1_D1 0xe6052104
-#define GPIO_SDHID1_D2 0xe6052105
-#define GPIO_SDHID1_D3 0xe6052106
-#define GPIO_SDHICMD1 0xe6052107
+#define GPIO_SDHID1_D0 IOMEM(0xe6052103)
+#define GPIO_SDHID1_D1 IOMEM(0xe6052104)
+#define GPIO_SDHID1_D2 IOMEM(0xe6052105)
+#define GPIO_SDHID1_D3 IOMEM(0xe6052106)
+#define GPIO_SDHICMD1 IOMEM(0xe6052107)
static void __init g4evm_init(void)
{
@@ -320,10 +320,10 @@ static void __init g4evm_init(void)
gpio_request(GPIO_FN_IDIN, NULL);
/* setup USB phy */
- __raw_writew(0x0200, 0xe605810a); /* USBCR1 */
- __raw_writew(0x00e0, 0xe60581c0); /* CPFCH */
- __raw_writew(0x6010, 0xe60581c6); /* CGPOSR */
- __raw_writew(0x8a0a, 0xe605810c); /* USBCR2 */
+ __raw_writew(0x0200, IOMEM(0xe605810a)); /* USBCR1 */
+ __raw_writew(0x00e0, IOMEM(0xe60581c0)); /* CPFCH */
+ __raw_writew(0x6010, IOMEM(0xe60581c6)); /* CGPOSR */
+ __raw_writew(0x8a0a, IOMEM(0xe605810c)); /* USBCR2 */
/* KEYSC @ CN31 */
gpio_request(GPIO_FN_PORT60_KEYOUT5, NULL);
diff --git a/arch/arm/mach-shmobile/board-kzm9g.c b/arch/arm/mach-shmobile/board-kzm9g.c
index 0a3d1f19077e..773a2b95a4e0 100644
--- a/arch/arm/mach-shmobile/board-kzm9g.c
+++ b/arch/arm/mach-shmobile/board-kzm9g.c
@@ -133,8 +133,8 @@ static struct platform_device usb_host_device = {
/* USB Func CN17 */
struct usbhs_private {
- unsigned int phy;
- unsigned int cr2;
+ void __iomem *phy;
+ void __iomem *cr2;
struct renesas_usbhs_platform_info info;
};
@@ -232,8 +232,8 @@ static u32 usbhs_pipe_cfg[] = {
};
static struct usbhs_private usbhs_private = {
- .phy = 0xe60781e0, /* USBPHYINT */
- .cr2 = 0xe605810c, /* USBCR2 */
+ .phy = IOMEM(0xe60781e0), /* USBPHYINT */
+ .cr2 = IOMEM(0xe605810c), /* USBCR2 */
.info = {
.platform_callback = {
.hardware_init = usbhs_hardware_init,
@@ -346,11 +346,11 @@ static struct resource sh_mmcif_resources[] = {
.flags = IORESOURCE_MEM,
},
[1] = {
- .start = gic_spi(141),
+ .start = gic_spi(140),
.flags = IORESOURCE_IRQ,
},
[2] = {
- .start = gic_spi(140),
+ .start = gic_spi(141),
.flags = IORESOURCE_IRQ,
},
};
@@ -765,7 +765,7 @@ static void __init kzm_init(void)
static void kzm9g_restart(char mode, const char *cmd)
{
-#define RESCNT2 0xe6188020
+#define RESCNT2 IOMEM(0xe6188020)
/* Do soft power on reset */
writel((1 << 31), RESCNT2);
}
diff --git a/arch/arm/mach-shmobile/board-mackerel.c b/arch/arm/mach-shmobile/board-mackerel.c
index 164fca0c1a42..62783b5d8813 100644
--- a/arch/arm/mach-shmobile/board-mackerel.c
+++ b/arch/arm/mach-shmobile/board-mackerel.c
@@ -585,8 +585,8 @@ out:
#define USBHS0_POLL_INTERVAL (HZ * 5)
struct usbhs_private {
- unsigned int usbphyaddr;
- unsigned int usbcrcaddr;
+ void __iomem *usbphyaddr;
+ void __iomem *usbcrcaddr;
struct renesas_usbhs_platform_info info;
struct delayed_work work;
struct platform_device *pdev;
@@ -644,7 +644,7 @@ static void usbhs0_hardware_exit(struct platform_device *pdev)
}
static struct usbhs_private usbhs0_private = {
- .usbcrcaddr = 0xe605810c, /* USBCR2 */
+ .usbcrcaddr = IOMEM(0xe605810c), /* USBCR2 */
.info = {
.platform_callback = {
.hardware_init = usbhs0_hardware_init,
@@ -778,8 +778,8 @@ static u32 usbhs1_pipe_cfg[] = {
};
static struct usbhs_private usbhs1_private = {
- .usbphyaddr = 0xe60581e2, /* USBPHY1INTAP */
- .usbcrcaddr = 0xe6058130, /* USBCR4 */
+ .usbphyaddr = IOMEM(0xe60581e2), /* USBPHY1INTAP */
+ .usbcrcaddr = IOMEM(0xe6058130), /* USBCR4 */
.info = {
.platform_callback = {
.hardware_init = usbhs1_hardware_init,
@@ -1404,12 +1404,12 @@ static struct i2c_board_info i2c1_devices[] = {
},
};
-#define GPIO_PORT9CR 0xE6051009
-#define GPIO_PORT10CR 0xE605100A
-#define GPIO_PORT167CR 0xE60520A7
-#define GPIO_PORT168CR 0xE60520A8
-#define SRCR4 0xe61580bc
-#define USCCR1 0xE6058144
+#define GPIO_PORT9CR IOMEM(0xE6051009)
+#define GPIO_PORT10CR IOMEM(0xE605100A)
+#define GPIO_PORT167CR IOMEM(0xE60520A7)
+#define GPIO_PORT168CR IOMEM(0xE60520A8)
+#define SRCR4 IOMEM(0xe61580bc)
+#define USCCR1 IOMEM(0xE6058144)
static void __init mackerel_init(void)
{
u32 srcr4;
diff --git a/arch/arm/mach-shmobile/board-marzen.c b/arch/arm/mach-shmobile/board-marzen.c
index 01ce3f15c6a3..b8a7525a4e2f 100644
--- a/arch/arm/mach-shmobile/board-marzen.c
+++ b/arch/arm/mach-shmobile/board-marzen.c
@@ -30,6 +30,8 @@
#include <linux/regulator/fixed.h>
#include <linux/regulator/machine.h>
#include <linux/smsc911x.h>
+#include <linux/mmc/sh_mobile_sdhi.h>
+#include <linux/mfd/tmio.h>
#include <mach/hardware.h>
#include <mach/r8a7779.h>
#include <mach/common.h>
@@ -39,6 +41,12 @@
#include <asm/hardware/gic.h>
#include <asm/traps.h>
+/* Fixed 3.3V regulator to be used by SDHI0 */
+static struct regulator_consumer_supply fixed3v3_power_consumers[] = {
+ REGULATOR_SUPPLY("vmmc", "sh_mobile_sdhi.0"),
+ REGULATOR_SUPPLY("vqmmc", "sh_mobile_sdhi.0"),
+};
+
/* Dummy supplies, where voltage doesn't matter */
static struct regulator_consumer_supply dummy_supplies[] = {
REGULATOR_SUPPLY("vddvario", "smsc911x"),
@@ -75,13 +83,61 @@ static struct platform_device eth_device = {
.num_resources = ARRAY_SIZE(smsc911x_resources),
};
+static struct resource sdhi0_resources[] = {
+ [0] = {
+ .name = "sdhi0",
+ .start = 0xffe4c000,
+ .end = 0xffe4c0ff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = gic_spi(104),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct sh_mobile_sdhi_info sdhi0_platform_data = {
+ .tmio_flags = TMIO_MMC_WRPROTECT_DISABLE | TMIO_MMC_HAS_IDLE_WAIT,
+ .tmio_caps = MMC_CAP_SD_HIGHSPEED,
+};
+
+static struct platform_device sdhi0_device = {
+ .name = "sh_mobile_sdhi",
+ .num_resources = ARRAY_SIZE(sdhi0_resources),
+ .resource = sdhi0_resources,
+ .id = 0,
+ .dev = {
+ .platform_data = &sdhi0_platform_data,
+ }
+};
+
+/* Thermal */
+static struct resource thermal_resources[] = {
+ [0] = {
+ .start = 0xFFC48000,
+ .end = 0xFFC48038 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device thermal_device = {
+ .name = "rcar_thermal",
+ .resource = thermal_resources,
+ .num_resources = ARRAY_SIZE(thermal_resources),
+};
+
static struct platform_device *marzen_devices[] __initdata = {
&eth_device,
+ &sdhi0_device,
+ &thermal_device,
};
static void __init marzen_init(void)
{
- regulator_register_fixed(0, dummy_supplies, ARRAY_SIZE(dummy_supplies));
+ regulator_register_always_on(0, "fixed-3.3V", fixed3v3_power_consumers,
+ ARRAY_SIZE(fixed3v3_power_consumers), 3300000);
+ regulator_register_fixed(1, dummy_supplies,
+ ARRAY_SIZE(dummy_supplies));
r8a7779_pinmux_init();
@@ -97,6 +153,16 @@ static void __init marzen_init(void)
gpio_request(GPIO_FN_EX_CS0, NULL); /* nCS */
gpio_request(GPIO_FN_IRQ1_B, NULL); /* IRQ + PME */
+ /* SD0 (CN20) */
+ gpio_request(GPIO_FN_SD0_CLK, NULL);
+ gpio_request(GPIO_FN_SD0_CMD, NULL);
+ gpio_request(GPIO_FN_SD0_DAT0, NULL);
+ gpio_request(GPIO_FN_SD0_DAT1, NULL);
+ gpio_request(GPIO_FN_SD0_DAT2, NULL);
+ gpio_request(GPIO_FN_SD0_DAT3, NULL);
+ gpio_request(GPIO_FN_SD0_CD, NULL);
+ gpio_request(GPIO_FN_SD0_WP, NULL);
+
r8a7779_add_standard_devices();
platform_add_devices(marzen_devices, ARRAY_SIZE(marzen_devices));
}
diff --git a/arch/arm/mach-shmobile/clock-r8a7740.c b/arch/arm/mach-shmobile/clock-r8a7740.c
index ad5fccc7b5e7..6729e0032180 100644
--- a/arch/arm/mach-shmobile/clock-r8a7740.c
+++ b/arch/arm/mach-shmobile/clock-r8a7740.c
@@ -41,29 +41,29 @@
*/
/* CPG registers */
-#define FRQCRA 0xe6150000
-#define FRQCRB 0xe6150004
-#define VCLKCR1 0xE6150008
-#define VCLKCR2 0xE615000c
-#define FRQCRC 0xe61500e0
-#define FSIACKCR 0xe6150018
-#define PLLC01CR 0xe6150028
-
-#define SUBCKCR 0xe6150080
-#define USBCKCR 0xe615008c
-
-#define MSTPSR0 0xe6150030
-#define MSTPSR1 0xe6150038
-#define MSTPSR2 0xe6150040
-#define MSTPSR3 0xe6150048
-#define MSTPSR4 0xe615004c
-#define FSIBCKCR 0xe6150090
-#define HDMICKCR 0xe6150094
-#define SMSTPCR0 0xe6150130
-#define SMSTPCR1 0xe6150134
-#define SMSTPCR2 0xe6150138
-#define SMSTPCR3 0xe615013c
-#define SMSTPCR4 0xe6150140
+#define FRQCRA IOMEM(0xe6150000)
+#define FRQCRB IOMEM(0xe6150004)
+#define VCLKCR1 IOMEM(0xE6150008)
+#define VCLKCR2 IOMEM(0xE615000c)
+#define FRQCRC IOMEM(0xe61500e0)
+#define FSIACKCR IOMEM(0xe6150018)
+#define PLLC01CR IOMEM(0xe6150028)
+
+#define SUBCKCR IOMEM(0xe6150080)
+#define USBCKCR IOMEM(0xe615008c)
+
+#define MSTPSR0 IOMEM(0xe6150030)
+#define MSTPSR1 IOMEM(0xe6150038)
+#define MSTPSR2 IOMEM(0xe6150040)
+#define MSTPSR3 IOMEM(0xe6150048)
+#define MSTPSR4 IOMEM(0xe615004c)
+#define FSIBCKCR IOMEM(0xe6150090)
+#define HDMICKCR IOMEM(0xe6150094)
+#define SMSTPCR0 IOMEM(0xe6150130)
+#define SMSTPCR1 IOMEM(0xe6150134)
+#define SMSTPCR2 IOMEM(0xe6150138)
+#define SMSTPCR3 IOMEM(0xe615013c)
+#define SMSTPCR4 IOMEM(0xe6150140)
/* Fixed 32 KHz root clock from EXTALR pin */
static struct clk extalr_clk = {
diff --git a/arch/arm/mach-shmobile/clock-sh7367.c b/arch/arm/mach-shmobile/clock-sh7367.c
index 162b791b8984..ef0a95e592c4 100644
--- a/arch/arm/mach-shmobile/clock-sh7367.c
+++ b/arch/arm/mach-shmobile/clock-sh7367.c
@@ -24,28 +24,28 @@
#include <mach/common.h>
/* SH7367 registers */
-#define RTFRQCR 0xe6150000
-#define SYFRQCR 0xe6150004
-#define CMFRQCR 0xe61500E0
-#define VCLKCR1 0xe6150008
-#define VCLKCR2 0xe615000C
-#define VCLKCR3 0xe615001C
-#define SCLKACR 0xe6150010
-#define SCLKBCR 0xe6150014
-#define SUBUSBCKCR 0xe6158080
-#define SPUCKCR 0xe6150084
-#define MSUCKCR 0xe6150088
-#define MVI3CKCR 0xe6150090
-#define VOUCKCR 0xe6150094
-#define MFCK1CR 0xe6150098
-#define MFCK2CR 0xe615009C
-#define PLLC1CR 0xe6150028
-#define PLLC2CR 0xe615002C
-#define RTMSTPCR0 0xe6158030
-#define RTMSTPCR2 0xe6158038
-#define SYMSTPCR0 0xe6158040
-#define SYMSTPCR2 0xe6158048
-#define CMMSTPCR0 0xe615804c
+#define RTFRQCR IOMEM(0xe6150000)
+#define SYFRQCR IOMEM(0xe6150004)
+#define CMFRQCR IOMEM(0xe61500E0)
+#define VCLKCR1 IOMEM(0xe6150008)
+#define VCLKCR2 IOMEM(0xe615000C)
+#define VCLKCR3 IOMEM(0xe615001C)
+#define SCLKACR IOMEM(0xe6150010)
+#define SCLKBCR IOMEM(0xe6150014)
+#define SUBUSBCKCR IOMEM(0xe6158080)
+#define SPUCKCR IOMEM(0xe6150084)
+#define MSUCKCR IOMEM(0xe6150088)
+#define MVI3CKCR IOMEM(0xe6150090)
+#define VOUCKCR IOMEM(0xe6150094)
+#define MFCK1CR IOMEM(0xe6150098)
+#define MFCK2CR IOMEM(0xe615009C)
+#define PLLC1CR IOMEM(0xe6150028)
+#define PLLC2CR IOMEM(0xe615002C)
+#define RTMSTPCR0 IOMEM(0xe6158030)
+#define RTMSTPCR2 IOMEM(0xe6158038)
+#define SYMSTPCR0 IOMEM(0xe6158040)
+#define SYMSTPCR2 IOMEM(0xe6158048)
+#define CMMSTPCR0 IOMEM(0xe615804c)
/* Fixed 32 KHz root clock from EXTALR pin */
static struct clk r_clk = {
diff --git a/arch/arm/mach-shmobile/clock-sh7372.c b/arch/arm/mach-shmobile/clock-sh7372.c
index 5a2894b1c965..430a90ffa120 100644
--- a/arch/arm/mach-shmobile/clock-sh7372.c
+++ b/arch/arm/mach-shmobile/clock-sh7372.c
@@ -24,36 +24,36 @@
#include <mach/common.h>
/* SH7372 registers */
-#define FRQCRA 0xe6150000
-#define FRQCRB 0xe6150004
-#define FRQCRC 0xe61500e0
-#define FRQCRD 0xe61500e4
-#define VCLKCR1 0xe6150008
-#define VCLKCR2 0xe615000c
-#define VCLKCR3 0xe615001c
-#define FMSICKCR 0xe6150010
-#define FMSOCKCR 0xe6150014
-#define FSIACKCR 0xe6150018
-#define FSIBCKCR 0xe6150090
-#define SUBCKCR 0xe6150080
-#define SPUCKCR 0xe6150084
-#define VOUCKCR 0xe6150088
-#define HDMICKCR 0xe6150094
-#define DSITCKCR 0xe6150060
-#define DSI0PCKCR 0xe6150064
-#define DSI1PCKCR 0xe6150098
-#define PLLC01CR 0xe6150028
-#define PLLC2CR 0xe615002c
-#define RMSTPCR0 0xe6150110
-#define RMSTPCR1 0xe6150114
-#define RMSTPCR2 0xe6150118
-#define RMSTPCR3 0xe615011c
-#define RMSTPCR4 0xe6150120
-#define SMSTPCR0 0xe6150130
-#define SMSTPCR1 0xe6150134
-#define SMSTPCR2 0xe6150138
-#define SMSTPCR3 0xe615013c
-#define SMSTPCR4 0xe6150140
+#define FRQCRA IOMEM(0xe6150000)
+#define FRQCRB IOMEM(0xe6150004)
+#define FRQCRC IOMEM(0xe61500e0)
+#define FRQCRD IOMEM(0xe61500e4)
+#define VCLKCR1 IOMEM(0xe6150008)
+#define VCLKCR2 IOMEM(0xe615000c)
+#define VCLKCR3 IOMEM(0xe615001c)
+#define FMSICKCR IOMEM(0xe6150010)
+#define FMSOCKCR IOMEM(0xe6150014)
+#define FSIACKCR IOMEM(0xe6150018)
+#define FSIBCKCR IOMEM(0xe6150090)
+#define SUBCKCR IOMEM(0xe6150080)
+#define SPUCKCR IOMEM(0xe6150084)
+#define VOUCKCR IOMEM(0xe6150088)
+#define HDMICKCR IOMEM(0xe6150094)
+#define DSITCKCR IOMEM(0xe6150060)
+#define DSI0PCKCR IOMEM(0xe6150064)
+#define DSI1PCKCR IOMEM(0xe6150098)
+#define PLLC01CR IOMEM(0xe6150028)
+#define PLLC2CR IOMEM(0xe615002c)
+#define RMSTPCR0 IOMEM(0xe6150110)
+#define RMSTPCR1 IOMEM(0xe6150114)
+#define RMSTPCR2 IOMEM(0xe6150118)
+#define RMSTPCR3 IOMEM(0xe615011c)
+#define RMSTPCR4 IOMEM(0xe6150120)
+#define SMSTPCR0 IOMEM(0xe6150130)
+#define SMSTPCR1 IOMEM(0xe6150134)
+#define SMSTPCR2 IOMEM(0xe6150138)
+#define SMSTPCR3 IOMEM(0xe615013c)
+#define SMSTPCR4 IOMEM(0xe6150140)
#define FSIDIVA 0xFE1F8000
#define FSIDIVB 0xFE1F8008
diff --git a/arch/arm/mach-shmobile/clock-sh7377.c b/arch/arm/mach-shmobile/clock-sh7377.c
index 85f2a3ec2c44..b8480d19e1c8 100644
--- a/arch/arm/mach-shmobile/clock-sh7377.c
+++ b/arch/arm/mach-shmobile/clock-sh7377.c
@@ -24,31 +24,31 @@
#include <mach/common.h>
/* SH7377 registers */
-#define RTFRQCR 0xe6150000
-#define SYFRQCR 0xe6150004
-#define CMFRQCR 0xe61500E0
-#define VCLKCR1 0xe6150008
-#define VCLKCR2 0xe615000C
-#define VCLKCR3 0xe615001C
-#define FMSICKCR 0xe6150010
-#define FMSOCKCR 0xe6150014
-#define FSICKCR 0xe6150018
-#define PLLC1CR 0xe6150028
-#define PLLC2CR 0xe615002C
-#define SUBUSBCKCR 0xe6150080
-#define SPUCKCR 0xe6150084
-#define MSUCKCR 0xe6150088
-#define MVI3CKCR 0xe6150090
-#define HDMICKCR 0xe6150094
-#define MFCK1CR 0xe6150098
-#define MFCK2CR 0xe615009C
-#define DSITCKCR 0xe6150060
-#define DSIPCKCR 0xe6150064
-#define SMSTPCR0 0xe6150130
-#define SMSTPCR1 0xe6150134
-#define SMSTPCR2 0xe6150138
-#define SMSTPCR3 0xe615013C
-#define SMSTPCR4 0xe6150140
+#define RTFRQCR IOMEM(0xe6150000)
+#define SYFRQCR IOMEM(0xe6150004)
+#define CMFRQCR IOMEM(0xe61500E0)
+#define VCLKCR1 IOMEM(0xe6150008)
+#define VCLKCR2 IOMEM(0xe615000C)
+#define VCLKCR3 IOMEM(0xe615001C)
+#define FMSICKCR IOMEM(0xe6150010)
+#define FMSOCKCR IOMEM(0xe6150014)
+#define FSICKCR IOMEM(0xe6150018)
+#define PLLC1CR IOMEM(0xe6150028)
+#define PLLC2CR IOMEM(0xe615002C)
+#define SUBUSBCKCR IOMEM(0xe6150080)
+#define SPUCKCR IOMEM(0xe6150084)
+#define MSUCKCR IOMEM(0xe6150088)
+#define MVI3CKCR IOMEM(0xe6150090)
+#define HDMICKCR IOMEM(0xe6150094)
+#define MFCK1CR IOMEM(0xe6150098)
+#define MFCK2CR IOMEM(0xe615009C)
+#define DSITCKCR IOMEM(0xe6150060)
+#define DSIPCKCR IOMEM(0xe6150064)
+#define SMSTPCR0 IOMEM(0xe6150130)
+#define SMSTPCR1 IOMEM(0xe6150134)
+#define SMSTPCR2 IOMEM(0xe6150138)
+#define SMSTPCR3 IOMEM(0xe615013C)
+#define SMSTPCR4 IOMEM(0xe6150140)
/* Fixed 32 KHz root clock from EXTALR pin */
static struct clk r_clk = {
diff --git a/arch/arm/mach-shmobile/clock-sh73a0.c b/arch/arm/mach-shmobile/clock-sh73a0.c
index 7f8da18a8580..516ff7f3e434 100644
--- a/arch/arm/mach-shmobile/clock-sh73a0.c
+++ b/arch/arm/mach-shmobile/clock-sh73a0.c
@@ -23,43 +23,43 @@
#include <linux/clkdev.h>
#include <mach/common.h>
-#define FRQCRA 0xe6150000
-#define FRQCRB 0xe6150004
-#define FRQCRD 0xe61500e4
-#define VCLKCR1 0xe6150008
-#define VCLKCR2 0xe615000C
-#define VCLKCR3 0xe615001C
-#define ZBCKCR 0xe6150010
-#define FLCKCR 0xe6150014
-#define SD0CKCR 0xe6150074
-#define SD1CKCR 0xe6150078
-#define SD2CKCR 0xe615007C
-#define FSIACKCR 0xe6150018
-#define FSIBCKCR 0xe6150090
-#define SUBCKCR 0xe6150080
-#define SPUACKCR 0xe6150084
-#define SPUVCKCR 0xe6150094
-#define MSUCKCR 0xe6150088
-#define HSICKCR 0xe615008C
-#define MFCK1CR 0xe6150098
-#define MFCK2CR 0xe615009C
-#define DSITCKCR 0xe6150060
-#define DSI0PCKCR 0xe6150064
-#define DSI1PCKCR 0xe6150068
+#define FRQCRA IOMEM(0xe6150000)
+#define FRQCRB IOMEM(0xe6150004)
+#define FRQCRD IOMEM(0xe61500e4)
+#define VCLKCR1 IOMEM(0xe6150008)
+#define VCLKCR2 IOMEM(0xe615000C)
+#define VCLKCR3 IOMEM(0xe615001C)
+#define ZBCKCR IOMEM(0xe6150010)
+#define FLCKCR IOMEM(0xe6150014)
+#define SD0CKCR IOMEM(0xe6150074)
+#define SD1CKCR IOMEM(0xe6150078)
+#define SD2CKCR IOMEM(0xe615007C)
+#define FSIACKCR IOMEM(0xe6150018)
+#define FSIBCKCR IOMEM(0xe6150090)
+#define SUBCKCR IOMEM(0xe6150080)
+#define SPUACKCR IOMEM(0xe6150084)
+#define SPUVCKCR IOMEM(0xe6150094)
+#define MSUCKCR IOMEM(0xe6150088)
+#define HSICKCR IOMEM(0xe615008C)
+#define MFCK1CR IOMEM(0xe6150098)
+#define MFCK2CR IOMEM(0xe615009C)
+#define DSITCKCR IOMEM(0xe6150060)
+#define DSI0PCKCR IOMEM(0xe6150064)
+#define DSI1PCKCR IOMEM(0xe6150068)
#define DSI0PHYCR 0xe615006C
#define DSI1PHYCR 0xe6150070
-#define PLLECR 0xe61500d0
-#define PLL0CR 0xe61500d8
-#define PLL1CR 0xe6150028
-#define PLL2CR 0xe615002c
-#define PLL3CR 0xe61500dc
-#define SMSTPCR0 0xe6150130
-#define SMSTPCR1 0xe6150134
-#define SMSTPCR2 0xe6150138
-#define SMSTPCR3 0xe615013c
-#define SMSTPCR4 0xe6150140
-#define SMSTPCR5 0xe6150144
-#define CKSCR 0xe61500c0
+#define PLLECR IOMEM(0xe61500d0)
+#define PLL0CR IOMEM(0xe61500d8)
+#define PLL1CR IOMEM(0xe6150028)
+#define PLL2CR IOMEM(0xe615002c)
+#define PLL3CR IOMEM(0xe61500dc)
+#define SMSTPCR0 IOMEM(0xe6150130)
+#define SMSTPCR1 IOMEM(0xe6150134)
+#define SMSTPCR2 IOMEM(0xe6150138)
+#define SMSTPCR3 IOMEM(0xe615013c)
+#define SMSTPCR4 IOMEM(0xe6150140)
+#define SMSTPCR5 IOMEM(0xe6150144)
+#define CKSCR IOMEM(0xe61500c0)
/* Fixed 32 KHz root clock from EXTALR pin */
static struct clk r_clk = {
diff --git a/arch/arm/mach-shmobile/intc-r8a7779.c b/arch/arm/mach-shmobile/intc-r8a7779.c
index f04fad4ec4fb..ef66f1a8aa2e 100644
--- a/arch/arm/mach-shmobile/intc-r8a7779.c
+++ b/arch/arm/mach-shmobile/intc-r8a7779.c
@@ -29,14 +29,14 @@
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
-#define INT2SMSKCR0 0xfe7822a0
-#define INT2SMSKCR1 0xfe7822a4
-#define INT2SMSKCR2 0xfe7822a8
-#define INT2SMSKCR3 0xfe7822ac
-#define INT2SMSKCR4 0xfe7822b0
+#define INT2SMSKCR0 IOMEM(0xfe7822a0)
+#define INT2SMSKCR1 IOMEM(0xfe7822a4)
+#define INT2SMSKCR2 IOMEM(0xfe7822a8)
+#define INT2SMSKCR3 IOMEM(0xfe7822ac)
+#define INT2SMSKCR4 IOMEM(0xfe7822b0)
-#define INT2NTSR0 0xfe700060
-#define INT2NTSR1 0xfe700064
+#define INT2NTSR0 IOMEM(0xfe700060)
+#define INT2NTSR1 IOMEM(0xfe700064)
static int r8a7779_set_wake(struct irq_data *data, unsigned int on)
{
diff --git a/arch/arm/mach-shmobile/intc-sh7372.c b/arch/arm/mach-shmobile/intc-sh7372.c
index 2587a22842f2..a91caad7db7c 100644
--- a/arch/arm/mach-shmobile/intc-sh7372.c
+++ b/arch/arm/mach-shmobile/intc-sh7372.c
@@ -624,6 +624,9 @@ void sh7372_intcs_resume(void)
__raw_writeb(ffd5[k], intcs_ffd5 + k);
}
+#define E694_BASE IOMEM(0xe6940000)
+#define E695_BASE IOMEM(0xe6950000)
+
static unsigned short e694[0x200];
static unsigned short e695[0x200];
@@ -632,22 +635,22 @@ void sh7372_intca_suspend(void)
int k;
for (k = 0x00; k <= 0x38; k += 4)
- e694[k] = __raw_readw(0xe6940000 + k);
+ e694[k] = __raw_readw(E694_BASE + k);
for (k = 0x80; k <= 0xb4; k += 4)
- e694[k] = __raw_readb(0xe6940000 + k);
+ e694[k] = __raw_readb(E694_BASE + k);
for (k = 0x180; k <= 0x1b4; k += 4)
- e694[k] = __raw_readb(0xe6940000 + k);
+ e694[k] = __raw_readb(E694_BASE + k);
for (k = 0x00; k <= 0x50; k += 4)
- e695[k] = __raw_readw(0xe6950000 + k);
+ e695[k] = __raw_readw(E695_BASE + k);
for (k = 0x80; k <= 0xa8; k += 4)
- e695[k] = __raw_readb(0xe6950000 + k);
+ e695[k] = __raw_readb(E695_BASE + k);
for (k = 0x180; k <= 0x1a8; k += 4)
- e695[k] = __raw_readb(0xe6950000 + k);
+ e695[k] = __raw_readb(E695_BASE + k);
}
void sh7372_intca_resume(void)
@@ -655,20 +658,20 @@ void sh7372_intca_resume(void)
int k;
for (k = 0x00; k <= 0x38; k += 4)
- __raw_writew(e694[k], 0xe6940000 + k);
+ __raw_writew(e694[k], E694_BASE + k);
for (k = 0x80; k <= 0xb4; k += 4)
- __raw_writeb(e694[k], 0xe6940000 + k);
+ __raw_writeb(e694[k], E694_BASE + k);
for (k = 0x180; k <= 0x1b4; k += 4)
- __raw_writeb(e694[k], 0xe6940000 + k);
+ __raw_writeb(e694[k], E694_BASE + k);
for (k = 0x00; k <= 0x50; k += 4)
- __raw_writew(e695[k], 0xe6950000 + k);
+ __raw_writew(e695[k], E695_BASE + k);
for (k = 0x80; k <= 0xa8; k += 4)
- __raw_writeb(e695[k], 0xe6950000 + k);
+ __raw_writeb(e695[k], E695_BASE + k);
for (k = 0x180; k <= 0x1a8; k += 4)
- __raw_writeb(e695[k], 0xe6950000 + k);
+ __raw_writeb(e695[k], E695_BASE + k);
}
diff --git a/arch/arm/mach-shmobile/intc-sh73a0.c b/arch/arm/mach-shmobile/intc-sh73a0.c
index 588555a67d9c..f0c5e5190601 100644
--- a/arch/arm/mach-shmobile/intc-sh73a0.c
+++ b/arch/arm/mach-shmobile/intc-sh73a0.c
@@ -366,10 +366,12 @@ static irqreturn_t sh73a0_irq_pin_demux(int irq, void *dev_id)
static struct irqaction sh73a0_irq_pin_cascade[32];
-#define PINTER0 0xe69000a0
-#define PINTER1 0xe69000a4
-#define PINTRR0 0xe69000d0
-#define PINTRR1 0xe69000d4
+#define PINTER0_PHYS 0xe69000a0
+#define PINTER1_PHYS 0xe69000a4
+#define PINTER0_VIRT IOMEM(0xe69000a0)
+#define PINTER1_VIRT IOMEM(0xe69000a4)
+#define PINTRR0 IOMEM(0xe69000d0)
+#define PINTRR1 IOMEM(0xe69000d4)
#define PINT0A_IRQ(n, irq) INTC_IRQ((n), SH73A0_PINT0_IRQ(irq))
#define PINT0B_IRQ(n, irq) INTC_IRQ((n), SH73A0_PINT0_IRQ(irq + 8))
@@ -377,14 +379,14 @@ static struct irqaction sh73a0_irq_pin_cascade[32];
#define PINT0D_IRQ(n, irq) INTC_IRQ((n), SH73A0_PINT0_IRQ(irq + 24))
#define PINT1E_IRQ(n, irq) INTC_IRQ((n), SH73A0_PINT1_IRQ(irq))
-INTC_PINT(intc_pint0, PINTER0, 0xe69000b0, "sh73a0-pint0", \
+INTC_PINT(intc_pint0, PINTER0_PHYS, 0xe69000b0, "sh73a0-pint0", \
INTC_PINT_E(A), INTC_PINT_E(B), INTC_PINT_E(C), INTC_PINT_E(D), \
INTC_PINT_V(A, PINT0A_IRQ), INTC_PINT_V(B, PINT0B_IRQ), \
INTC_PINT_V(C, PINT0C_IRQ), INTC_PINT_V(D, PINT0D_IRQ), \
INTC_PINT_E(A), INTC_PINT_E(B), INTC_PINT_E(C), INTC_PINT_E(D), \
INTC_PINT_E(A), INTC_PINT_E(B), INTC_PINT_E(C), INTC_PINT_E(D));
-INTC_PINT(intc_pint1, PINTER1, 0xe69000c0, "sh73a0-pint1", \
+INTC_PINT(intc_pint1, PINTER1_PHYS, 0xe69000c0, "sh73a0-pint1", \
INTC_PINT_E(E), INTC_PINT_E_EMPTY, INTC_PINT_E_EMPTY, INTC_PINT_E_EMPTY, \
INTC_PINT_V(E, PINT1E_IRQ), INTC_PINT_V_NONE, \
INTC_PINT_V_NONE, INTC_PINT_V_NONE, \
@@ -394,7 +396,7 @@ INTC_PINT(intc_pint1, PINTER1, 0xe69000c0, "sh73a0-pint1", \
static struct irqaction sh73a0_pint0_cascade;
static struct irqaction sh73a0_pint1_cascade;
-static void pint_demux(unsigned long rr, unsigned long er, int base_irq)
+static void pint_demux(void __iomem *rr, void __iomem *er, int base_irq)
{
unsigned long value = ioread32(rr) & ioread32(er);
int k;
@@ -409,13 +411,13 @@ static void pint_demux(unsigned long rr, unsigned long er, int base_irq)
static irqreturn_t sh73a0_pint0_demux(int irq, void *dev_id)
{
- pint_demux(PINTRR0, PINTER0, SH73A0_PINT0_IRQ(0));
+ pint_demux(PINTRR0, PINTER0_VIRT, SH73A0_PINT0_IRQ(0));
return IRQ_HANDLED;
}
static irqreturn_t sh73a0_pint1_demux(int irq, void *dev_id)
{
- pint_demux(PINTRR1, PINTER1, SH73A0_PINT1_IRQ(0));
+ pint_demux(PINTRR1, PINTER1_VIRT, SH73A0_PINT1_IRQ(0));
return IRQ_HANDLED;
}
diff --git a/arch/arm/mach-shmobile/pm-rmobile.c b/arch/arm/mach-shmobile/pm-rmobile.c
index a8562540f1d6..32e177275e47 100644
--- a/arch/arm/mach-shmobile/pm-rmobile.c
+++ b/arch/arm/mach-shmobile/pm-rmobile.c
@@ -20,9 +20,9 @@
#include <mach/pm-rmobile.h>
/* SYSC */
-#define SPDCR 0xe6180008
-#define SWUCR 0xe6180014
-#define PSTR 0xe6180080
+#define SPDCR IOMEM(0xe6180008)
+#define SWUCR IOMEM(0xe6180014)
+#define PSTR IOMEM(0xe6180080)
#define PSTR_RETRIES 100
#define PSTR_DELAY_US 10
diff --git a/arch/arm/mach-shmobile/pm-sh7372.c b/arch/arm/mach-shmobile/pm-sh7372.c
index 792037069226..162121842a2b 100644
--- a/arch/arm/mach-shmobile/pm-sh7372.c
+++ b/arch/arm/mach-shmobile/pm-sh7372.c
@@ -29,45 +29,46 @@
#include <mach/pm-rmobile.h>
/* DBG */
-#define DBGREG1 0xe6100020
-#define DBGREG9 0xe6100040
+#define DBGREG1 IOMEM(0xe6100020)
+#define DBGREG9 IOMEM(0xe6100040)
/* CPGA */
-#define SYSTBCR 0xe6150024
-#define MSTPSR0 0xe6150030
-#define MSTPSR1 0xe6150038
-#define MSTPSR2 0xe6150040
-#define MSTPSR3 0xe6150048
-#define MSTPSR4 0xe615004c
-#define PLLC01STPCR 0xe61500c8
+#define SYSTBCR IOMEM(0xe6150024)
+#define MSTPSR0 IOMEM(0xe6150030)
+#define MSTPSR1 IOMEM(0xe6150038)
+#define MSTPSR2 IOMEM(0xe6150040)
+#define MSTPSR3 IOMEM(0xe6150048)
+#define MSTPSR4 IOMEM(0xe615004c)
+#define PLLC01STPCR IOMEM(0xe61500c8)
/* SYSC */
-#define SBAR 0xe6180020
-#define WUPRMSK 0xe6180028
-#define WUPSMSK 0xe618002c
-#define WUPSMSK2 0xe6180048
-#define WUPSFAC 0xe6180098
-#define IRQCR 0xe618022c
-#define IRQCR2 0xe6180238
-#define IRQCR3 0xe6180244
-#define IRQCR4 0xe6180248
-#define PDNSEL 0xe6180254
+#define SBAR IOMEM(0xe6180020)
+#define WUPRMSK IOMEM(0xe6180028)
+#define WUPSMSK IOMEM(0xe618002c)
+#define WUPSMSK2 IOMEM(0xe6180048)
+#define WUPSFAC IOMEM(0xe6180098)
+#define IRQCR IOMEM(0xe618022c)
+#define IRQCR2 IOMEM(0xe6180238)
+#define IRQCR3 IOMEM(0xe6180244)
+#define IRQCR4 IOMEM(0xe6180248)
+#define PDNSEL IOMEM(0xe6180254)
/* INTC */
-#define ICR1A 0xe6900000
-#define ICR2A 0xe6900004
-#define ICR3A 0xe6900008
-#define ICR4A 0xe690000c
-#define INTMSK00A 0xe6900040
-#define INTMSK10A 0xe6900044
-#define INTMSK20A 0xe6900048
-#define INTMSK30A 0xe690004c
+#define ICR1A IOMEM(0xe6900000)
+#define ICR2A IOMEM(0xe6900004)
+#define ICR3A IOMEM(0xe6900008)
+#define ICR4A IOMEM(0xe690000c)
+#define INTMSK00A IOMEM(0xe6900040)
+#define INTMSK10A IOMEM(0xe6900044)
+#define INTMSK20A IOMEM(0xe6900048)
+#define INTMSK30A IOMEM(0xe690004c)
/* MFIS */
+/* FIXME: pointing where? */
#define SMFRAM 0xe6a70000
/* AP-System Core */
-#define APARMBAREA 0xe6f10020
+#define APARMBAREA IOMEM(0xe6f10020)
#ifdef CONFIG_PM
diff --git a/arch/arm/mach-shmobile/setup-sh7367.c b/arch/arm/mach-shmobile/setup-sh7367.c
index 2e3074ab75b3..e647f5410879 100644
--- a/arch/arm/mach-shmobile/setup-sh7367.c
+++ b/arch/arm/mach-shmobile/setup-sh7367.c
@@ -462,7 +462,7 @@ static void __init sh7367_earlytimer_init(void)
shmobile_earlytimer_init();
}
-#define SYMSTPCR2 0xe6158048
+#define SYMSTPCR2 IOMEM(0xe6158048)
#define SYMSTPCR2_CMT1 (1 << 29)
void __init sh7367_add_early_devices(void)
diff --git a/arch/arm/mach-shmobile/setup-sh7377.c b/arch/arm/mach-shmobile/setup-sh7377.c
index 855b1506caf8..edcf98bb7012 100644
--- a/arch/arm/mach-shmobile/setup-sh7377.c
+++ b/arch/arm/mach-shmobile/setup-sh7377.c
@@ -484,7 +484,7 @@ static void __init sh7377_earlytimer_init(void)
shmobile_earlytimer_init();
}
-#define SMSTPCR3 0xe615013c
+#define SMSTPCR3 IOMEM(0xe615013c)
#define SMSTPCR3_CMT1 (1 << 29)
void __init sh7377_add_early_devices(void)
diff --git a/arch/arm/mach-shmobile/setup-sh73a0.c b/arch/arm/mach-shmobile/setup-sh73a0.c
index 38ed2ddd3265..db99a4ade80c 100644
--- a/arch/arm/mach-shmobile/setup-sh73a0.c
+++ b/arch/arm/mach-shmobile/setup-sh73a0.c
@@ -780,7 +780,7 @@ static struct platform_device *sh73a0_late_devices[] __initdata = {
&pmu_device,
};
-#define SRCR2 0xe61580b0
+#define SRCR2 IOMEM(0xe61580b0)
void __init sh73a0_add_standard_devices(void)
{
diff --git a/arch/arm/mach-shmobile/sh-gpio.h b/arch/arm/mach-shmobile/sh-gpio.h
index 7ba1877fa97a..e834763ac2a5 100644
--- a/arch/arm/mach-shmobile/sh-gpio.h
+++ b/arch/arm/mach-shmobile/sh-gpio.h
@@ -21,12 +21,12 @@
* the method to control only pull up/down/free.
* this function should be replaced by correct gpio function
*/
-static inline void __init gpio_direction_none(u32 addr)
+static inline void __init gpio_direction_none(void __iomem * addr)
{
__raw_writeb(0x00, addr);
}
-static inline void __init gpio_request_pullup(u32 addr)
+static inline void __init gpio_request_pullup(void __iomem * addr)
{
u8 data = __raw_readb(addr);
@@ -35,7 +35,7 @@ static inline void __init gpio_request_pullup(u32 addr)
__raw_writeb(data, addr);
}
-static inline void __init gpio_request_pulldown(u32 addr)
+static inline void __init gpio_request_pulldown(void __iomem * addr)
{
u8 data = __raw_readb(addr);
diff --git a/arch/arm/mach-spear13xx/include/mach/spear.h b/arch/arm/mach-spear13xx/include/mach/spear.h
index 65f27def239b..07d90acc92c8 100644
--- a/arch/arm/mach-spear13xx/include/mach/spear.h
+++ b/arch/arm/mach-spear13xx/include/mach/spear.h
@@ -17,26 +17,26 @@
#include <asm/memory.h>
#define PERIP_GRP2_BASE UL(0xB3000000)
-#define VA_PERIP_GRP2_BASE UL(0xFE000000)
+#define VA_PERIP_GRP2_BASE IOMEM(0xFE000000)
#define MCIF_SDHCI_BASE UL(0xB3000000)
#define SYSRAM0_BASE UL(0xB3800000)
-#define VA_SYSRAM0_BASE UL(0xFE800000)
+#define VA_SYSRAM0_BASE IOMEM(0xFE800000)
#define SYS_LOCATION (VA_SYSRAM0_BASE + 0x600)
#define PERIP_GRP1_BASE UL(0xE0000000)
-#define VA_PERIP_GRP1_BASE UL(0xFD000000)
+#define VA_PERIP_GRP1_BASE IOMEM(0xFD000000)
#define UART_BASE UL(0xE0000000)
-#define VA_UART_BASE UL(0xFD000000)
+#define VA_UART_BASE IOMEM(0xFD000000)
#define SSP_BASE UL(0xE0100000)
#define MISC_BASE UL(0xE0700000)
-#define VA_MISC_BASE IOMEM(UL(0xFD700000))
+#define VA_MISC_BASE IOMEM(0xFD700000)
#define A9SM_AND_MPMC_BASE UL(0xEC000000)
-#define VA_A9SM_AND_MPMC_BASE UL(0xFC000000)
+#define VA_A9SM_AND_MPMC_BASE IOMEM(0xFC000000)
/* A9SM peripheral offsets */
#define A9SM_PERIP_BASE UL(0xEC800000)
-#define VA_A9SM_PERIP_BASE UL(0xFC800000)
+#define VA_A9SM_PERIP_BASE IOMEM(0xFC800000)
#define VA_SCU_BASE (VA_A9SM_PERIP_BASE + 0x00)
#define L2CC_BASE UL(0xED000000)
diff --git a/arch/arm/mach-spear13xx/spear13xx.c b/arch/arm/mach-spear13xx/spear13xx.c
index cf936b106e27..e10648801b2e 100644
--- a/arch/arm/mach-spear13xx/spear13xx.c
+++ b/arch/arm/mach-spear13xx/spear13xx.c
@@ -114,17 +114,17 @@ void __init spear13xx_l2x0_init(void)
*/
struct map_desc spear13xx_io_desc[] __initdata = {
{
- .virtual = VA_PERIP_GRP2_BASE,
+ .virtual = (unsigned long)VA_PERIP_GRP2_BASE,
.pfn = __phys_to_pfn(PERIP_GRP2_BASE),
.length = SZ_16M,
.type = MT_DEVICE
}, {
- .virtual = VA_PERIP_GRP1_BASE,
+ .virtual = (unsigned long)VA_PERIP_GRP1_BASE,
.pfn = __phys_to_pfn(PERIP_GRP1_BASE),
.length = SZ_16M,
.type = MT_DEVICE
}, {
- .virtual = VA_A9SM_AND_MPMC_BASE,
+ .virtual = (unsigned long)VA_A9SM_AND_MPMC_BASE,
.pfn = __phys_to_pfn(A9SM_AND_MPMC_BASE),
.length = SZ_16M,
.type = MT_DEVICE
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 04eb74e3f601..9aa653b3eb32 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -1,6 +1,4 @@
-obj-y += board-pinmux.o
obj-y += common.o
-obj-y += devices.o
obj-y += io.o
obj-y += irq.o
obj-y += clock.o
@@ -24,7 +22,6 @@ obj-$(CONFIG_SMP) += reset.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o
obj-$(CONFIG_TEGRA_PCI) += pcie.o
-obj-$(CONFIG_USB_SUPPORT) += usb_phy.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-dt-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o
diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/board-dt-tegra20.c
index 5d8c8fb060b0..57e235f4ac74 100644
--- a/arch/arm/mach-tegra/board-dt-tegra20.c
+++ b/arch/arm/mach-tegra/board-dt-tegra20.c
@@ -28,9 +28,11 @@
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/pda_power.h>
+#include <linux/platform_data/tegra_usb.h>
#include <linux/io.h>
#include <linux/i2c.h>
#include <linux/i2c-tegra.h>
+#include <linux/usb/tegra_usb_phy.h>
#include <asm/hardware/gic.h>
#include <asm/mach-types.h>
@@ -43,9 +45,32 @@
#include "board.h"
#include "clock.h"
-#include "devices.h"
#include "common.h"
+struct tegra_ehci_platform_data tegra_ehci1_pdata = {
+ .operating_mode = TEGRA_USB_OTG,
+ .power_down_on_bus_suspend = 1,
+ .vbus_gpio = -1,
+};
+
+struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = {
+ .reset_gpio = -1,
+ .clk = "cdev2",
+};
+
+struct tegra_ehci_platform_data tegra_ehci2_pdata = {
+ .phy_config = &tegra_ehci2_ulpi_phy_config,
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ .vbus_gpio = -1,
+};
+
+struct tegra_ehci_platform_data tegra_ehci3_pdata = {
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ .vbus_gpio = -1,
+};
+
struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC1_BASE, "sdhci-tegra.0", NULL),
OF_DEV_AUXDATA("nvidia,tegra20-sdhci", TEGRA_SDMMC2_BASE, "sdhci-tegra.1", NULL),
diff --git a/arch/arm/mach-tegra/board-pinmux.c b/arch/arm/mach-tegra/board-pinmux.c
deleted file mode 100644
index a5574c71b931..000000000000
--- a/arch/arm/mach-tegra/board-pinmux.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/notifier.h>
-#include <linux/string.h>
-
-#include "board-pinmux.h"
-#include "devices.h"
-
-unsigned long tegra_pincfg_pullnone_driven[2] = {
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_PULL, TEGRA_PINCONFIG_PULL_NONE),
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_TRISTATE, TEGRA_PINCONFIG_DRIVEN),
-};
-
-unsigned long tegra_pincfg_pullnone_tristate[2] = {
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_PULL, TEGRA_PINCONFIG_PULL_NONE),
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_TRISTATE, TEGRA_PINCONFIG_TRISTATE),
-};
-
-unsigned long tegra_pincfg_pullnone_na[1] = {
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_PULL, TEGRA_PINCONFIG_PULL_NONE),
-};
-
-unsigned long tegra_pincfg_pullup_driven[2] = {
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_PULL, TEGRA_PINCONFIG_PULL_UP),
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_TRISTATE, TEGRA_PINCONFIG_DRIVEN),
-};
-
-unsigned long tegra_pincfg_pullup_tristate[2] = {
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_PULL, TEGRA_PINCONFIG_PULL_UP),
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_TRISTATE, TEGRA_PINCONFIG_TRISTATE),
-};
-
-unsigned long tegra_pincfg_pullup_na[1] = {
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_PULL, TEGRA_PINCONFIG_PULL_UP),
-};
-
-unsigned long tegra_pincfg_pulldown_driven[2] = {
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_PULL, TEGRA_PINCONFIG_PULL_DOWN),
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_TRISTATE, TEGRA_PINCONFIG_DRIVEN),
-};
-
-unsigned long tegra_pincfg_pulldown_tristate[2] = {
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_PULL, TEGRA_PINCONFIG_PULL_DOWN),
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_TRISTATE, TEGRA_PINCONFIG_TRISTATE),
-};
-
-unsigned long tegra_pincfg_pulldown_na[1] = {
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_PULL, TEGRA_PINCONFIG_PULL_DOWN),
-};
-
-unsigned long tegra_pincfg_pullna_driven[1] = {
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_TRISTATE, TEGRA_PINCONFIG_DRIVEN),
-};
-
-unsigned long tegra_pincfg_pullna_tristate[1] = {
- TEGRA_PINCONF_PACK(TEGRA_PINCONF_PARAM_TRISTATE, TEGRA_PINCONFIG_TRISTATE),
-};
-
-static struct platform_device *devices[] = {
- &tegra_gpio_device,
- &tegra_pinmux_device,
-};
-
-void tegra_board_pinmux_init(struct tegra_board_pinmux_conf *conf_a,
- struct tegra_board_pinmux_conf *conf_b)
-{
- if (conf_a)
- pinctrl_register_mappings(conf_a->maps, conf_a->map_count);
- if (conf_b)
- pinctrl_register_mappings(conf_b->maps, conf_b->map_count);
-
- platform_add_devices(devices, ARRAY_SIZE(devices));
-}
diff --git a/arch/arm/mach-tegra/board-pinmux.h b/arch/arm/mach-tegra/board-pinmux.h
deleted file mode 100644
index c5f3f3381e86..000000000000
--- a/arch/arm/mach-tegra/board-pinmux.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2011,2012, NVIDIA CORPORATION. All rights reserved.
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __MACH_TEGRA_BOARD_PINMUX_H
-#define __MACH_TEGRA_BOARD_PINMUX_H
-
-#include <linux/pinctrl/machine.h>
-
-#include <mach/pinconf-tegra.h>
-
-#define PINMUX_DEV "tegra20-pinctrl"
-
-#define TEGRA_MAP_MUX(_group_, _function_) \
- PIN_MAP_MUX_GROUP_HOG_DEFAULT(PINMUX_DEV, _group_, _function_)
-
-#define TEGRA_MAP_CONF(_group_, _pull_, _drive_) \
- PIN_MAP_CONFIGS_GROUP_HOG_DEFAULT(PINMUX_DEV, _group_, tegra_pincfg_pull##_pull_##_##_drive_)
-
-#define TEGRA_MAP_MUXCONF(_group_, _function_, _pull_, _drive_) \
- TEGRA_MAP_MUX(_group_, _function_), \
- TEGRA_MAP_CONF(_group_, _pull_, _drive_)
-
-extern unsigned long tegra_pincfg_pullnone_driven[2];
-extern unsigned long tegra_pincfg_pullnone_tristate[2];
-extern unsigned long tegra_pincfg_pullnone_na[1];
-extern unsigned long tegra_pincfg_pullup_driven[2];
-extern unsigned long tegra_pincfg_pullup_tristate[2];
-extern unsigned long tegra_pincfg_pullup_na[1];
-extern unsigned long tegra_pincfg_pulldown_driven[2];
-extern unsigned long tegra_pincfg_pulldown_tristate[2];
-extern unsigned long tegra_pincfg_pulldown_na[1];
-extern unsigned long tegra_pincfg_pullna_driven[1];
-extern unsigned long tegra_pincfg_pullna_tristate[1];
-
-struct tegra_board_pinmux_conf {
- struct pinctrl_map *maps;
- int map_count;
-};
-
-void tegra_board_pinmux_init(struct tegra_board_pinmux_conf *conf_a,
- struct tegra_board_pinmux_conf *conf_b);
-
-#endif
diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c
deleted file mode 100644
index 61e9603744a7..000000000000
--- a/arch/arm/mach-tegra/devices.c
+++ /dev/null
@@ -1,701 +0,0 @@
-/*
- * Copyright (C) 2010,2011 Google, Inc.
- *
- * Author:
- * Colin Cross <ccross@android.com>
- * Erik Gilling <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/resource.h>
-#include <linux/platform_device.h>
-#include <linux/dma-mapping.h>
-#include <linux/fsl_devices.h>
-#include <linux/serial_8250.h>
-#include <linux/i2c-tegra.h>
-#include <mach/irqs.h>
-#include <mach/iomap.h>
-#include <mach/dma.h>
-#include <mach/usb_phy.h>
-
-#include "gpio-names.h"
-#include "devices.h"
-
-static struct resource gpio_resource[] = {
- [0] = {
- .start = TEGRA_GPIO_BASE,
- .end = TEGRA_GPIO_BASE + TEGRA_GPIO_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = INT_GPIO1,
- .end = INT_GPIO1,
- .flags = IORESOURCE_IRQ,
- },
- [2] = {
- .start = INT_GPIO2,
- .end = INT_GPIO2,
- .flags = IORESOURCE_IRQ,
- },
- [3] = {
- .start = INT_GPIO3,
- .end = INT_GPIO3,
- .flags = IORESOURCE_IRQ,
- },
- [4] = {
- .start = INT_GPIO4,
- .end = INT_GPIO4,
- .flags = IORESOURCE_IRQ,
- },
- [5] = {
- .start = INT_GPIO5,
- .end = INT_GPIO5,
- .flags = IORESOURCE_IRQ,
- },
- [6] = {
- .start = INT_GPIO6,
- .end = INT_GPIO6,
- .flags = IORESOURCE_IRQ,
- },
- [7] = {
- .start = INT_GPIO7,
- .end = INT_GPIO7,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device tegra_gpio_device = {
- .name = "tegra-gpio",
- .id = -1,
- .resource = gpio_resource,
- .num_resources = ARRAY_SIZE(gpio_resource),
-};
-
-static struct resource pinmux_resource[] = {
- [0] = {
- /* Tri-state registers */
- .start = TEGRA_APB_MISC_BASE + 0x14,
- .end = TEGRA_APB_MISC_BASE + 0x20 + 3,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- /* Mux registers */
- .start = TEGRA_APB_MISC_BASE + 0x80,
- .end = TEGRA_APB_MISC_BASE + 0x9c + 3,
- .flags = IORESOURCE_MEM,
- },
- [2] = {
- /* Pull-up/down registers */
- .start = TEGRA_APB_MISC_BASE + 0xa0,
- .end = TEGRA_APB_MISC_BASE + 0xb0 + 3,
- .flags = IORESOURCE_MEM,
- },
- [3] = {
- /* Pad control registers */
- .start = TEGRA_APB_MISC_BASE + 0x868,
- .end = TEGRA_APB_MISC_BASE + 0x90c + 3,
- .flags = IORESOURCE_MEM,
- },
-};
-
-struct platform_device tegra_pinmux_device = {
- .name = "tegra20-pinctrl",
- .id = -1,
- .resource = pinmux_resource,
- .num_resources = ARRAY_SIZE(pinmux_resource),
-};
-
-static struct resource i2c_resource1[] = {
- [0] = {
- .start = INT_I2C,
- .end = INT_I2C,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_I2C_BASE,
- .end = TEGRA_I2C_BASE + TEGRA_I2C_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct resource i2c_resource2[] = {
- [0] = {
- .start = INT_I2C2,
- .end = INT_I2C2,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_I2C2_BASE,
- .end = TEGRA_I2C2_BASE + TEGRA_I2C2_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct resource i2c_resource3[] = {
- [0] = {
- .start = INT_I2C3,
- .end = INT_I2C3,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_I2C3_BASE,
- .end = TEGRA_I2C3_BASE + TEGRA_I2C3_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct resource i2c_resource4[] = {
- [0] = {
- .start = INT_DVC,
- .end = INT_DVC,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_DVC_BASE,
- .end = TEGRA_DVC_BASE + TEGRA_DVC_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct tegra_i2c_platform_data tegra_i2c1_platform_data = {
- .bus_clk_rate = 400000,
-};
-
-static struct tegra_i2c_platform_data tegra_i2c2_platform_data = {
- .bus_clk_rate = 400000,
-};
-
-static struct tegra_i2c_platform_data tegra_i2c3_platform_data = {
- .bus_clk_rate = 400000,
-};
-
-static struct tegra_i2c_platform_data tegra_dvc_platform_data = {
- .bus_clk_rate = 400000,
-};
-
-struct platform_device tegra_i2c_device1 = {
- .name = "tegra-i2c",
- .id = 0,
- .resource = i2c_resource1,
- .num_resources = ARRAY_SIZE(i2c_resource1),
- .dev = {
- .platform_data = &tegra_i2c1_platform_data,
- },
-};
-
-struct platform_device tegra_i2c_device2 = {
- .name = "tegra-i2c",
- .id = 1,
- .resource = i2c_resource2,
- .num_resources = ARRAY_SIZE(i2c_resource2),
- .dev = {
- .platform_data = &tegra_i2c2_platform_data,
- },
-};
-
-struct platform_device tegra_i2c_device3 = {
- .name = "tegra-i2c",
- .id = 2,
- .resource = i2c_resource3,
- .num_resources = ARRAY_SIZE(i2c_resource3),
- .dev = {
- .platform_data = &tegra_i2c3_platform_data,
- },
-};
-
-struct platform_device tegra_i2c_device4 = {
- .name = "tegra-i2c",
- .id = 3,
- .resource = i2c_resource4,
- .num_resources = ARRAY_SIZE(i2c_resource4),
- .dev = {
- .platform_data = &tegra_dvc_platform_data,
- },
-};
-
-static struct resource spi_resource1[] = {
- [0] = {
- .start = INT_S_LINK1,
- .end = INT_S_LINK1,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_SPI1_BASE,
- .end = TEGRA_SPI1_BASE + TEGRA_SPI1_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct resource spi_resource2[] = {
- [0] = {
- .start = INT_SPI_2,
- .end = INT_SPI_2,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_SPI2_BASE,
- .end = TEGRA_SPI2_BASE + TEGRA_SPI2_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct resource spi_resource3[] = {
- [0] = {
- .start = INT_SPI_3,
- .end = INT_SPI_3,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_SPI3_BASE,
- .end = TEGRA_SPI3_BASE + TEGRA_SPI3_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct resource spi_resource4[] = {
- [0] = {
- .start = INT_SPI_4,
- .end = INT_SPI_4,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_SPI4_BASE,
- .end = TEGRA_SPI4_BASE + TEGRA_SPI4_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-struct platform_device tegra_spi_device1 = {
- .name = "spi_tegra",
- .id = 0,
- .resource = spi_resource1,
- .num_resources = ARRAY_SIZE(spi_resource1),
- .dev = {
- .coherent_dma_mask = 0xffffffff,
- },
-};
-
-struct platform_device tegra_spi_device2 = {
- .name = "spi_tegra",
- .id = 1,
- .resource = spi_resource2,
- .num_resources = ARRAY_SIZE(spi_resource2),
- .dev = {
- .coherent_dma_mask = 0xffffffff,
- },
-};
-
-struct platform_device tegra_spi_device3 = {
- .name = "spi_tegra",
- .id = 2,
- .resource = spi_resource3,
- .num_resources = ARRAY_SIZE(spi_resource3),
- .dev = {
- .coherent_dma_mask = 0xffffffff,
- },
-};
-
-struct platform_device tegra_spi_device4 = {
- .name = "spi_tegra",
- .id = 3,
- .resource = spi_resource4,
- .num_resources = ARRAY_SIZE(spi_resource4),
- .dev = {
- .coherent_dma_mask = 0xffffffff,
- },
-};
-
-
-static struct resource sdhci_resource1[] = {
- [0] = {
- .start = INT_SDMMC1,
- .end = INT_SDMMC1,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_SDMMC1_BASE,
- .end = TEGRA_SDMMC1_BASE + TEGRA_SDMMC1_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct resource sdhci_resource2[] = {
- [0] = {
- .start = INT_SDMMC2,
- .end = INT_SDMMC2,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_SDMMC2_BASE,
- .end = TEGRA_SDMMC2_BASE + TEGRA_SDMMC2_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct resource sdhci_resource3[] = {
- [0] = {
- .start = INT_SDMMC3,
- .end = INT_SDMMC3,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_SDMMC3_BASE,
- .end = TEGRA_SDMMC3_BASE + TEGRA_SDMMC3_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-static struct resource sdhci_resource4[] = {
- [0] = {
- .start = INT_SDMMC4,
- .end = INT_SDMMC4,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = TEGRA_SDMMC4_BASE,
- .end = TEGRA_SDMMC4_BASE + TEGRA_SDMMC4_SIZE-1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-/* board files should fill in platform_data register the devices themselvs.
- * See board-harmony.c for an example
- */
-struct platform_device tegra_sdhci_device1 = {
- .name = "sdhci-tegra",
- .id = 0,
- .resource = sdhci_resource1,
- .num_resources = ARRAY_SIZE(sdhci_resource1),
-};
-
-struct platform_device tegra_sdhci_device2 = {
- .name = "sdhci-tegra",
- .id = 1,
- .resource = sdhci_resource2,
- .num_resources = ARRAY_SIZE(sdhci_resource2),
-};
-
-struct platform_device tegra_sdhci_device3 = {
- .name = "sdhci-tegra",
- .id = 2,
- .resource = sdhci_resource3,
- .num_resources = ARRAY_SIZE(sdhci_resource3),
-};
-
-struct platform_device tegra_sdhci_device4 = {
- .name = "sdhci-tegra",
- .id = 3,
- .resource = sdhci_resource4,
- .num_resources = ARRAY_SIZE(sdhci_resource4),
-};
-
-static struct resource tegra_usb1_resources[] = {
- [0] = {
- .start = TEGRA_USB_BASE,
- .end = TEGRA_USB_BASE + TEGRA_USB_SIZE - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = INT_USB,
- .end = INT_USB,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource tegra_usb2_resources[] = {
- [0] = {
- .start = TEGRA_USB2_BASE,
- .end = TEGRA_USB2_BASE + TEGRA_USB2_SIZE - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = INT_USB2,
- .end = INT_USB2,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource tegra_usb3_resources[] = {
- [0] = {
- .start = TEGRA_USB3_BASE,
- .end = TEGRA_USB3_BASE + TEGRA_USB3_SIZE - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = INT_USB3,
- .end = INT_USB3,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config = {
- .reset_gpio = -1,
- .clk = "cdev2",
-};
-
-struct tegra_ehci_platform_data tegra_ehci1_pdata = {
- .operating_mode = TEGRA_USB_OTG,
- .power_down_on_bus_suspend = 1,
- .vbus_gpio = -1,
-};
-
-struct tegra_ehci_platform_data tegra_ehci2_pdata = {
- .phy_config = &tegra_ehci2_ulpi_phy_config,
- .operating_mode = TEGRA_USB_HOST,
- .power_down_on_bus_suspend = 1,
- .vbus_gpio = -1,
-};
-
-struct tegra_ehci_platform_data tegra_ehci3_pdata = {
- .operating_mode = TEGRA_USB_HOST,
- .power_down_on_bus_suspend = 1,
- .vbus_gpio = -1,
-};
-
-static u64 tegra_ehci_dmamask = DMA_BIT_MASK(32);
-
-struct platform_device tegra_ehci1_device = {
- .name = "tegra-ehci",
- .id = 0,
- .dev = {
- .dma_mask = &tegra_ehci_dmamask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- .platform_data = &tegra_ehci1_pdata,
- },
- .resource = tegra_usb1_resources,
- .num_resources = ARRAY_SIZE(tegra_usb1_resources),
-};
-
-struct platform_device tegra_ehci2_device = {
- .name = "tegra-ehci",
- .id = 1,
- .dev = {
- .dma_mask = &tegra_ehci_dmamask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- .platform_data = &tegra_ehci2_pdata,
- },
- .resource = tegra_usb2_resources,
- .num_resources = ARRAY_SIZE(tegra_usb2_resources),
-};
-
-struct platform_device tegra_ehci3_device = {
- .name = "tegra-ehci",
- .id = 2,
- .dev = {
- .dma_mask = &tegra_ehci_dmamask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- .platform_data = &tegra_ehci3_pdata,
- },
- .resource = tegra_usb3_resources,
- .num_resources = ARRAY_SIZE(tegra_usb3_resources),
-};
-
-static struct resource tegra_pmu_resources[] = {
- [0] = {
- .start = INT_CPU0_PMU_INTR,
- .end = INT_CPU0_PMU_INTR,
- .flags = IORESOURCE_IRQ,
- },
- [1] = {
- .start = INT_CPU1_PMU_INTR,
- .end = INT_CPU1_PMU_INTR,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device tegra_pmu_device = {
- .name = "arm-pmu",
- .id = -1,
- .num_resources = ARRAY_SIZE(tegra_pmu_resources),
- .resource = tegra_pmu_resources,
-};
-
-static struct resource tegra_uarta_resources[] = {
- [0] = {
- .start = TEGRA_UARTA_BASE,
- .end = TEGRA_UARTA_BASE + TEGRA_UARTA_SIZE - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = INT_UARTA,
- .end = INT_UARTA,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource tegra_uartb_resources[] = {
- [0] = {
- .start = TEGRA_UARTB_BASE,
- .end = TEGRA_UARTB_BASE + TEGRA_UARTB_SIZE - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = INT_UARTB,
- .end = INT_UARTB,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource tegra_uartc_resources[] = {
- [0] = {
- .start = TEGRA_UARTC_BASE,
- .end = TEGRA_UARTC_BASE + TEGRA_UARTC_SIZE - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = INT_UARTC,
- .end = INT_UARTC,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource tegra_uartd_resources[] = {
- [0] = {
- .start = TEGRA_UARTD_BASE,
- .end = TEGRA_UARTD_BASE + TEGRA_UARTD_SIZE - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = INT_UARTD,
- .end = INT_UARTD,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-static struct resource tegra_uarte_resources[] = {
- [0] = {
- .start = TEGRA_UARTE_BASE,
- .end = TEGRA_UARTE_BASE + TEGRA_UARTE_SIZE - 1,
- .flags = IORESOURCE_MEM,
- },
- [1] = {
- .start = INT_UARTE,
- .end = INT_UARTE,
- .flags = IORESOURCE_IRQ,
- },
-};
-
-struct platform_device tegra_uarta_device = {
- .name = "tegra_uart",
- .id = 0,
- .num_resources = ARRAY_SIZE(tegra_uarta_resources),
- .resource = tegra_uarta_resources,
- .dev = {
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
-};
-
-struct platform_device tegra_uartb_device = {
- .name = "tegra_uart",
- .id = 1,
- .num_resources = ARRAY_SIZE(tegra_uartb_resources),
- .resource = tegra_uartb_resources,
- .dev = {
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
-};
-
-struct platform_device tegra_uartc_device = {
- .name = "tegra_uart",
- .id = 2,
- .num_resources = ARRAY_SIZE(tegra_uartc_resources),
- .resource = tegra_uartc_resources,
- .dev = {
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
-};
-
-struct platform_device tegra_uartd_device = {
- .name = "tegra_uart",
- .id = 3,
- .num_resources = ARRAY_SIZE(tegra_uartd_resources),
- .resource = tegra_uartd_resources,
- .dev = {
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
-};
-
-struct platform_device tegra_uarte_device = {
- .name = "tegra_uart",
- .id = 4,
- .num_resources = ARRAY_SIZE(tegra_uarte_resources),
- .resource = tegra_uarte_resources,
- .dev = {
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
-};
-
-static struct resource i2s_resource1[] = {
- [0] = {
- .start = INT_I2S1,
- .end = INT_I2S1,
- .flags = IORESOURCE_IRQ
- },
- [1] = {
- .start = TEGRA_DMA_REQ_SEL_I2S_1,
- .end = TEGRA_DMA_REQ_SEL_I2S_1,
- .flags = IORESOURCE_DMA
- },
- [2] = {
- .start = TEGRA_I2S1_BASE,
- .end = TEGRA_I2S1_BASE + TEGRA_I2S1_SIZE - 1,
- .flags = IORESOURCE_MEM
- }
-};
-
-static struct resource i2s_resource2[] = {
- [0] = {
- .start = INT_I2S2,
- .end = INT_I2S2,
- .flags = IORESOURCE_IRQ
- },
- [1] = {
- .start = TEGRA_DMA_REQ_SEL_I2S2_1,
- .end = TEGRA_DMA_REQ_SEL_I2S2_1,
- .flags = IORESOURCE_DMA
- },
- [2] = {
- .start = TEGRA_I2S2_BASE,
- .end = TEGRA_I2S2_BASE + TEGRA_I2S2_SIZE - 1,
- .flags = IORESOURCE_MEM
- }
-};
-
-struct platform_device tegra_i2s_device1 = {
- .name = "tegra20-i2s",
- .id = 0,
- .resource = i2s_resource1,
- .num_resources = ARRAY_SIZE(i2s_resource1),
-};
-
-struct platform_device tegra_i2s_device2 = {
- .name = "tegra20-i2s",
- .id = 1,
- .resource = i2s_resource2,
- .num_resources = ARRAY_SIZE(i2s_resource2),
-};
-
-static struct resource tegra_das_resources[] = {
- [0] = {
- .start = TEGRA_APB_MISC_DAS_BASE,
- .end = TEGRA_APB_MISC_DAS_BASE + TEGRA_APB_MISC_DAS_SIZE - 1,
- .flags = IORESOURCE_MEM,
- },
-};
-
-struct platform_device tegra_das_device = {
- .name = "tegra20-das",
- .id = -1,
- .num_resources = ARRAY_SIZE(tegra_das_resources),
- .resource = tegra_das_resources,
-};
diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h
deleted file mode 100644
index 4f5052726495..000000000000
--- a/arch/arm/mach-tegra/devices.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2010,2011 Google, Inc.
- *
- * Author:
- * Colin Cross <ccross@android.com>
- * Erik Gilling <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_DEVICES_H
-#define __MACH_TEGRA_DEVICES_H
-
-#include <linux/platform_device.h>
-#include <linux/platform_data/tegra_usb.h>
-
-#include <mach/usb_phy.h>
-
-extern struct tegra_ulpi_config tegra_ehci2_ulpi_phy_config;
-
-extern struct tegra_ehci_platform_data tegra_ehci1_pdata;
-extern struct tegra_ehci_platform_data tegra_ehci2_pdata;
-extern struct tegra_ehci_platform_data tegra_ehci3_pdata;
-
-extern struct platform_device tegra_gpio_device;
-extern struct platform_device tegra_pinmux_device;
-extern struct platform_device tegra_sdhci_device1;
-extern struct platform_device tegra_sdhci_device2;
-extern struct platform_device tegra_sdhci_device3;
-extern struct platform_device tegra_sdhci_device4;
-extern struct platform_device tegra_i2c_device1;
-extern struct platform_device tegra_i2c_device2;
-extern struct platform_device tegra_i2c_device3;
-extern struct platform_device tegra_i2c_device4;
-extern struct platform_device tegra_spi_device1;
-extern struct platform_device tegra_spi_device2;
-extern struct platform_device tegra_spi_device3;
-extern struct platform_device tegra_spi_device4;
-extern struct platform_device tegra_ehci1_device;
-extern struct platform_device tegra_ehci2_device;
-extern struct platform_device tegra_ehci3_device;
-extern struct platform_device tegra_uarta_device;
-extern struct platform_device tegra_uartb_device;
-extern struct platform_device tegra_uartc_device;
-extern struct platform_device tegra_uartd_device;
-extern struct platform_device tegra_uarte_device;
-extern struct platform_device tegra_pmu_device;
-extern struct platform_device tegra_i2s_device1;
-extern struct platform_device tegra_i2s_device2;
-extern struct platform_device tegra_das_device;
-
-#endif
diff --git a/arch/arm/mach-tegra/include/mach/pinconf-tegra.h b/arch/arm/mach-tegra/include/mach/pinconf-tegra.h
deleted file mode 100644
index 1f24d304921e..000000000000
--- a/arch/arm/mach-tegra/include/mach/pinconf-tegra.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * pinctrl configuration definitions for the NVIDIA Tegra pinmux
- *
- * Copyright (c) 2011, NVIDIA 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.
- */
-
-#ifndef __PINCONF_TEGRA_H__
-#define __PINCONF_TEGRA_H__
-
-enum tegra_pinconf_param {
- /* argument: tegra_pinconf_pull */
- TEGRA_PINCONF_PARAM_PULL,
- /* argument: tegra_pinconf_tristate */
- TEGRA_PINCONF_PARAM_TRISTATE,
- /* argument: Boolean */
- TEGRA_PINCONF_PARAM_ENABLE_INPUT,
- /* argument: Boolean */
- TEGRA_PINCONF_PARAM_OPEN_DRAIN,
- /* argument: Boolean */
- TEGRA_PINCONF_PARAM_LOCK,
- /* argument: Boolean */
- TEGRA_PINCONF_PARAM_IORESET,
- /* argument: Boolean */
- TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE,
- /* argument: Boolean */
- TEGRA_PINCONF_PARAM_SCHMITT,
- /* argument: Boolean */
- TEGRA_PINCONF_PARAM_LOW_POWER_MODE,
- /* argument: Integer, range is HW-dependant */
- TEGRA_PINCONF_PARAM_DRIVE_DOWN_STRENGTH,
- /* argument: Integer, range is HW-dependant */
- TEGRA_PINCONF_PARAM_DRIVE_UP_STRENGTH,
- /* argument: Integer, range is HW-dependant */
- TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING,
- /* argument: Integer, range is HW-dependant */
- TEGRA_PINCONF_PARAM_SLEW_RATE_RISING,
-};
-
-enum tegra_pinconf_pull {
- TEGRA_PINCONFIG_PULL_NONE,
- TEGRA_PINCONFIG_PULL_DOWN,
- TEGRA_PINCONFIG_PULL_UP,
-};
-
-enum tegra_pinconf_tristate {
- TEGRA_PINCONFIG_DRIVEN,
- TEGRA_PINCONFIG_TRISTATE,
-};
-
-#define TEGRA_PINCONF_PACK(_param_, _arg_) ((_param_) << 16 | (_arg_))
-#define TEGRA_PINCONF_UNPACK_PARAM(_conf_) ((_conf_) >> 16)
-#define TEGRA_PINCONF_UNPACK_ARG(_conf_) ((_conf_) & 0xffff)
-
-#endif
diff --git a/arch/arm/mach-tegra/include/mach/suspend.h b/arch/arm/mach-tegra/include/mach/suspend.h
deleted file mode 100644
index 5af8715d2e1e..000000000000
--- a/arch/arm/mach-tegra/include/mach/suspend.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * arch/arm/mach-tegra/include/mach/suspend.h
- *
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- * Colin Cross <ccross@google.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_SUSPEND_H_
-#define _MACH_TEGRA_SUSPEND_H_
-
-void tegra_pinmux_suspend(void);
-void tegra_irq_suspend(void);
-void tegra_gpio_suspend(void);
-void tegra_clk_suspend(void);
-void tegra_dma_suspend(void);
-void tegra_timer_suspend(void);
-
-void tegra_pinmux_resume(void);
-void tegra_irq_resume(void);
-void tegra_gpio_resume(void);
-void tegra_clk_resume(void);
-void tegra_dma_resume(void);
-void tegra_timer_resume(void);
-
-#endif /* _MACH_TEGRA_SUSPEND_H_ */
diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c
index 3463fb5b79c7..a8dba6489c9b 100644
--- a/arch/arm/mach-tegra/pcie.c
+++ b/arch/arm/mach-tegra/pcie.c
@@ -352,17 +352,7 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class);
/* Tegra PCIE requires relaxed ordering */
static void __devinit tegra_pcie_relax_enable(struct pci_dev *dev)
{
- u16 val16;
- int pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
-
- if (pos <= 0) {
- dev_err(&dev->dev, "skipping relaxed ordering fixup\n");
- return;
- }
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &val16);
- val16 |= PCI_EXP_DEVCTL_RELAX_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, val16);
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN);
}
DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c
index 15d506501ccc..de0662de28a0 100644
--- a/arch/arm/mach-tegra/powergate.c
+++ b/arch/arm/mach-tegra/powergate.c
@@ -199,7 +199,9 @@ int __init tegra_powergate_init(void)
#ifdef CONFIG_DEBUG_FS
-static const char * const powergate_name[] = {
+static const char * const *powergate_name;
+
+static const char * const powergate_name_t20[] = {
[TEGRA_POWERGATE_CPU] = "cpu",
[TEGRA_POWERGATE_3D] = "3d",
[TEGRA_POWERGATE_VENC] = "venc",
@@ -209,6 +211,23 @@ static const char * const powergate_name[] = {
[TEGRA_POWERGATE_MPE] = "mpe",
};
+static const char * const powergate_name_t30[] = {
+ [TEGRA_POWERGATE_CPU] = "cpu0",
+ [TEGRA_POWERGATE_3D] = "3d0",
+ [TEGRA_POWERGATE_VENC] = "venc",
+ [TEGRA_POWERGATE_VDEC] = "vdec",
+ [TEGRA_POWERGATE_PCIE] = "pcie",
+ [TEGRA_POWERGATE_L2] = "l2",
+ [TEGRA_POWERGATE_MPE] = "mpe",
+ [TEGRA_POWERGATE_HEG] = "heg",
+ [TEGRA_POWERGATE_SATA] = "sata",
+ [TEGRA_POWERGATE_CPU1] = "cpu1",
+ [TEGRA_POWERGATE_CPU2] = "cpu2",
+ [TEGRA_POWERGATE_CPU3] = "cpu3",
+ [TEGRA_POWERGATE_CELP] = "celp",
+ [TEGRA_POWERGATE_3D1] = "3d1",
+};
+
static int powergate_show(struct seq_file *s, void *data)
{
int i;
@@ -237,14 +256,24 @@ static const struct file_operations powergate_fops = {
int __init tegra_powergate_debugfs_init(void)
{
struct dentry *d;
- int err = -ENOMEM;
- d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
- &powergate_fops);
- if (!d)
- return -ENOMEM;
+ switch (tegra_chip_id) {
+ case TEGRA20:
+ powergate_name = powergate_name_t20;
+ break;
+ case TEGRA30:
+ powergate_name = powergate_name_t30;
+ break;
+ }
+
+ if (powergate_name) {
+ d = debugfs_create_file("powergate", S_IRUGO, NULL, NULL,
+ &powergate_fops);
+ if (!d)
+ return -ENOMEM;
+ }
- return err;
+ return 0;
}
#endif
diff --git a/arch/arm/mach-tegra/tegra20_clocks.c b/arch/arm/mach-tegra/tegra20_clocks.c
index 9273b0dffc66..deb873fb12b6 100644
--- a/arch/arm/mach-tegra/tegra20_clocks.c
+++ b/arch/arm/mach-tegra/tegra20_clocks.c
@@ -28,7 +28,6 @@
#include <linux/clk.h>
#include <mach/iomap.h>
-#include <mach/suspend.h>
#include "clock.h"
#include "fuse.h"
diff --git a/arch/arm/mach-tegra/tegra20_clocks_data.c b/arch/arm/mach-tegra/tegra20_clocks_data.c
index e81dcd239c95..cc9b5fd8c3d3 100644
--- a/arch/arm/mach-tegra/tegra20_clocks_data.c
+++ b/arch/arm/mach-tegra/tegra20_clocks_data.c
@@ -28,7 +28,6 @@
#include <linux/clk.h>
#include <mach/iomap.h>
-#include <mach/suspend.h>
#include "clock.h"
#include "fuse.h"
@@ -918,14 +917,10 @@ PERIPH_CLK(la, "la", NULL, 76, 0x1f8, 26000000, mux_pllp_pllc_pllm_clkm, MUX
PERIPH_CLK(owr, "tegra_w1", NULL, 71, 0x1cc, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71);
PERIPH_CLK(nor, "nor", NULL, 42, 0x1d0, 92000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71); /* requires min voltage */
PERIPH_CLK(mipi, "mipi", NULL, 50, 0x174, 60000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71); /* scales with voltage */
-PERIPH_CLK(i2c1, "tegra-i2c.0", NULL, 12, 0x124, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16);
-PERIPH_CLK(i2c2, "tegra-i2c.1", NULL, 54, 0x198, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16);
-PERIPH_CLK(i2c3, "tegra-i2c.2", NULL, 67, 0x1b8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16);
-PERIPH_CLK(dvc, "tegra-i2c.3", NULL, 47, 0x128, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16);
-PERIPH_CLK(i2c1_i2c, "tegra-i2c.0", "i2c", 0, 0, 72000000, mux_pllp_out3, 0);
-PERIPH_CLK(i2c2_i2c, "tegra-i2c.1", "i2c", 0, 0, 72000000, mux_pllp_out3, 0);
-PERIPH_CLK(i2c3_i2c, "tegra-i2c.2", "i2c", 0, 0, 72000000, mux_pllp_out3, 0);
-PERIPH_CLK(dvc_i2c, "tegra-i2c.3", "i2c", 0, 0, 72000000, mux_pllp_out3, 0);
+PERIPH_CLK(i2c1, "tegra-i2c.0", "div-clk", 12, 0x124, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16);
+PERIPH_CLK(i2c2, "tegra-i2c.1", "div-clk", 54, 0x198, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16);
+PERIPH_CLK(i2c3, "tegra-i2c.2", "div-clk", 67, 0x1b8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16);
+PERIPH_CLK(dvc, "tegra-i2c.3", "div-clk", 47, 0x128, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16);
PERIPH_CLK(uarta, "tegra-uart.0", NULL, 6, 0x178, 600000000, mux_pllp_pllc_pllm_clkm, MUX);
PERIPH_CLK(uartb, "tegra-uart.1", NULL, 7, 0x17c, 600000000, mux_pllp_pllc_pllm_clkm, MUX);
PERIPH_CLK(uartc, "tegra-uart.2", NULL, 55, 0x1a0, 600000000, mux_pllp_pllc_pllm_clkm, MUX);
@@ -990,10 +985,6 @@ static struct clk *tegra_list_clks[] = {
&tegra_i2c2,
&tegra_i2c3,
&tegra_dvc,
- &tegra_i2c1_i2c,
- &tegra_i2c2_i2c,
- &tegra_i2c3_i2c,
- &tegra_dvc_i2c,
&tegra_uarta,
&tegra_uartb,
&tegra_uartc,
@@ -1057,6 +1048,10 @@ static struct clk_duplicate tegra_clk_duplicates[] = {
CLK_DUPLICATE("vde", "tegra-aes", "vde"),
CLK_DUPLICATE("cclk", NULL, "cpu"),
CLK_DUPLICATE("twd", "smp_twd", NULL),
+ CLK_DUPLICATE("pll_p_out3", "tegra-i2c.0", "fast-clk"),
+ CLK_DUPLICATE("pll_p_out3", "tegra-i2c.1", "fast-clk"),
+ CLK_DUPLICATE("pll_p_out3", "tegra-i2c.2", "fast-clk"),
+ CLK_DUPLICATE("pll_p_out3", "tegra-i2c.3", "fast-clk"),
};
#define CLK(dev, con, ck) \
diff --git a/arch/arm/mach-tegra/tegra30_clocks_data.c b/arch/arm/mach-tegra/tegra30_clocks_data.c
index c10449603df0..d92cb556ae35 100644
--- a/arch/arm/mach-tegra/tegra30_clocks_data.c
+++ b/arch/arm/mach-tegra/tegra30_clocks_data.c
@@ -1071,11 +1071,11 @@ PERIPH_CLK(la, "la", NULL, 76, 0x1f8, 26000000, mux_pllp_pllc_pllm_clkm, MUX
PERIPH_CLK(owr, "tegra_w1", NULL, 71, 0x1cc, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB);
PERIPH_CLK(nor, "nor", NULL, 42, 0x1d0, 127000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71); /* requires min voltage */
PERIPH_CLK(mipi, "mipi", NULL, 50, 0x174, 60000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB); /* scales with voltage */
-PERIPH_CLK(i2c1, "tegra-i2c.0", NULL, 12, 0x124, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB);
-PERIPH_CLK(i2c2, "tegra-i2c.1", NULL, 54, 0x198, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB);
-PERIPH_CLK(i2c3, "tegra-i2c.2", NULL, 67, 0x1b8, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB);
-PERIPH_CLK(i2c4, "tegra-i2c.3", NULL, 103, 0x3c4, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB);
-PERIPH_CLK(i2c5, "tegra-i2c.4", NULL, 47, 0x128, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB);
+PERIPH_CLK(i2c1, "tegra-i2c.0", "div-clk", 12, 0x124, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB);
+PERIPH_CLK(i2c2, "tegra-i2c.1", "div-clk", 54, 0x198, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB);
+PERIPH_CLK(i2c3, "tegra-i2c.2", "div-clk", 67, 0x1b8, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB);
+PERIPH_CLK(i2c4, "tegra-i2c.3", "div-clk", 103, 0x3c4, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB);
+PERIPH_CLK(i2c5, "tegra-i2c.4", "div-clk", 47, 0x128, 26000000, mux_pllp_clkm, MUX | DIV_U16 | PERIPH_ON_APB);
PERIPH_CLK(uarta, "tegra-uart.0", NULL, 6, 0x178, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB);
PERIPH_CLK(uartb, "tegra-uart.1", NULL, 7, 0x17c, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB);
PERIPH_CLK(uartc, "tegra-uart.2", NULL, 55, 0x1a0, 800000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | DIV_U71_UART | PERIPH_ON_APB);
@@ -1287,6 +1287,11 @@ struct clk_duplicate tegra_clk_duplicates[] = {
CLK_DUPLICATE("dam1", NULL, "dam1"),
CLK_DUPLICATE("dam2", NULL, "dam2"),
CLK_DUPLICATE("spdif_in", NULL, "spdif_in"),
+ CLK_DUPLICATE("pll_p_out3", "tegra-i2c.0", "fast-clk"),
+ CLK_DUPLICATE("pll_p_out3", "tegra-i2c.1", "fast-clk"),
+ CLK_DUPLICATE("pll_p_out3", "tegra-i2c.2", "fast-clk"),
+ CLK_DUPLICATE("pll_p_out3", "tegra-i2c.3", "fast-clk"),
+ CLK_DUPLICATE("pll_p_out3", "tegra-i2c.4", "fast-clk"),
};
struct clk *tegra_ptr_clks[] = {
diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c
index 57b5bdc13b9b..eccdce983043 100644
--- a/arch/arm/mach-tegra/timer.c
+++ b/arch/arm/mach-tegra/timer.c
@@ -33,7 +33,6 @@
#include <mach/iomap.h>
#include <mach/irqs.h>
-#include <mach/suspend.h>
#include "board.h"
#include "clock.h"
diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
index a258996d954b..c77c86c47369 100644
--- a/arch/arm/mach-ux500/Kconfig
+++ b/arch/arm/mach-ux500/Kconfig
@@ -29,6 +29,7 @@ config MACH_MOP500
select I2C
select I2C_NOMADIK
select SOC_BUS
+ select REGULATOR_FIXED_VOLTAGE
help
Include support for the MOP500 development platform.
diff --git a/arch/arm/mach-ux500/Makefile b/arch/arm/mach-ux500/Makefile
index 5691ef679d01..f24710dfc395 100644
--- a/arch/arm/mach-ux500/Makefile
+++ b/arch/arm/mach-ux500/Makefile
@@ -12,6 +12,6 @@ obj-$(CONFIG_MACH_MOP500) += board-mop500.o board-mop500-sdi.o \
board-mop500-uib.o board-mop500-stuib.o \
board-mop500-u8500uib.o \
board-mop500-pins.o \
- board-mop500-msp.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-msp.c b/arch/arm/mach-ux500/board-mop500-audio.c
index df15646036aa..070629a95625 100644
--- a/arch/arm/mach-ux500/board-mop500-msp.c
+++ b/arch/arm/mach-ux500/board-mop500-audio.c
@@ -7,7 +7,6 @@
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/gpio.h>
-#include <linux/pinctrl/consumer.h>
#include <plat/gpio-nomadik.h>
#include <plat/pincfg.h>
@@ -23,53 +22,6 @@
#include "devices-db8500.h"
#include "pins-db8500.h"
-/* MSP1/3 Tx/Rx usage protection */
-static DEFINE_SPINLOCK(msp_rxtx_lock);
-
-/* Reference Count */
-static int msp_rxtx_ref;
-
-/* Pin modes */
-struct pinctrl *msp1_p;
-struct pinctrl_state *msp1_def;
-struct pinctrl_state *msp1_sleep;
-
-int msp13_i2s_init(void)
-{
- int retval = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&msp_rxtx_lock, flags);
- if (msp_rxtx_ref == 0 && !(IS_ERR(msp1_p) || IS_ERR(msp1_def))) {
- retval = pinctrl_select_state(msp1_p, msp1_def);
- if (retval)
- pr_err("could not set MSP1 defstate\n");
- }
- if (!retval)
- msp_rxtx_ref++;
- spin_unlock_irqrestore(&msp_rxtx_lock, flags);
-
- return retval;
-}
-
-int msp13_i2s_exit(void)
-{
- int retval = 0;
- unsigned long flags;
-
- spin_lock_irqsave(&msp_rxtx_lock, flags);
- WARN_ON(!msp_rxtx_ref);
- msp_rxtx_ref--;
- if (msp_rxtx_ref == 0 && !(IS_ERR(msp1_p) || IS_ERR(msp1_sleep))) {
- retval = pinctrl_select_state(msp1_p, msp1_sleep);
- if (retval)
- pr_err("could not set MSP1 sleepstate\n");
- }
- spin_unlock_irqrestore(&msp_rxtx_lock, flags);
-
- return retval;
-}
-
static struct stedma40_chan_cfg msp0_dma_rx = {
.high_priority = true,
.dir = STEDMA40_PERIPH_TO_MEM,
@@ -96,7 +48,7 @@ static struct stedma40_chan_cfg msp0_dma_tx = {
/* data_width is set during configuration */
};
-static struct msp_i2s_platform_data msp0_platform_data = {
+struct msp_i2s_platform_data msp0_platform_data = {
.id = MSP_I2S_0,
.msp_i2s_dma_rx = &msp0_dma_rx,
.msp_i2s_dma_tx = &msp0_dma_tx,
@@ -128,12 +80,10 @@ static struct stedma40_chan_cfg msp1_dma_tx = {
/* data_width is set during configuration */
};
-static struct msp_i2s_platform_data msp1_platform_data = {
+struct msp_i2s_platform_data msp1_platform_data = {
.id = MSP_I2S_1,
.msp_i2s_dma_rx = NULL,
.msp_i2s_dma_tx = &msp1_dma_tx,
- .msp_i2s_init = msp13_i2s_init,
- .msp_i2s_exit = msp13_i2s_exit,
};
static struct stedma40_chan_cfg msp2_dma_rx = {
@@ -193,11 +143,11 @@ static struct platform_device *db8500_add_msp_i2s(struct device *parent,
/* Platform device for ASoC MOP500 machine */
static struct platform_device snd_soc_mop500 = {
- .name = "snd-soc-mop500",
- .id = 0,
- .dev = {
- .platform_data = NULL,
- },
+ .name = "snd-soc-mop500",
+ .id = 0,
+ .dev = {
+ .platform_data = NULL,
+ },
};
/* Platform device for Ux500-PCM */
@@ -209,59 +159,37 @@ static struct platform_device ux500_pcm = {
},
};
-static struct msp_i2s_platform_data msp2_platform_data = {
+struct msp_i2s_platform_data msp2_platform_data = {
.id = MSP_I2S_2,
.msp_i2s_dma_rx = &msp2_dma_rx,
.msp_i2s_dma_tx = &msp2_dma_tx,
};
-static struct msp_i2s_platform_data msp3_platform_data = {
+struct msp_i2s_platform_data msp3_platform_data = {
.id = MSP_I2S_3,
.msp_i2s_dma_rx = &msp1_dma_rx,
.msp_i2s_dma_tx = NULL,
- .msp_i2s_init = msp13_i2s_init,
- .msp_i2s_exit = msp13_i2s_exit,
};
-int mop500_msp_init(struct device *parent)
+void mop500_audio_init(struct device *parent)
{
- struct platform_device *msp1;
-
pr_info("%s: Register platform-device 'snd-soc-mop500'.\n", __func__);
platform_device_register(&snd_soc_mop500);
pr_info("Initialize MSP I2S-devices.\n");
db8500_add_msp_i2s(parent, 0, U8500_MSP0_BASE, IRQ_DB8500_MSP0,
&msp0_platform_data);
- msp1 = db8500_add_msp_i2s(parent, 1, U8500_MSP1_BASE, IRQ_DB8500_MSP1,
+ db8500_add_msp_i2s(parent, 1, U8500_MSP1_BASE, IRQ_DB8500_MSP1,
&msp1_platform_data);
db8500_add_msp_i2s(parent, 2, U8500_MSP2_BASE, IRQ_DB8500_MSP2,
&msp2_platform_data);
db8500_add_msp_i2s(parent, 3, U8500_MSP3_BASE, IRQ_DB8500_MSP1,
&msp3_platform_data);
+}
- /* Get the pinctrl handle for MSP1 */
- if (msp1) {
- msp1_p = pinctrl_get(&msp1->dev);
- if (IS_ERR(msp1_p))
- dev_err(&msp1->dev, "could not get MSP1 pinctrl\n");
- else {
- msp1_def = pinctrl_lookup_state(msp1_p,
- PINCTRL_STATE_DEFAULT);
- if (IS_ERR(msp1_def)) {
- dev_err(&msp1->dev,
- "could not get MSP1 defstate\n");
- }
- msp1_sleep = pinctrl_lookup_state(msp1_p,
- PINCTRL_STATE_SLEEP);
- if (IS_ERR(msp1_sleep))
- dev_err(&msp1->dev,
- "could not get MSP1 idlestate\n");
- }
- }
-
+/* Due for removal once the MSP driver has been fully DT:ed. */
+void mop500_of_audio_init(struct device *parent)
+{
pr_info("%s: Register platform-device 'ux500-pcm'\n", __func__);
platform_device_register(&ux500_pcm);
-
- return 0;
}
diff --git a/arch/arm/mach-ux500/board-mop500-msp.h b/arch/arm/mach-ux500/board-mop500-msp.h
deleted file mode 100644
index 6fcfb5e2cc94..000000000000
--- a/arch/arm/mach-ux500/board-mop500-msp.h
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright (C) ST-Ericsson SA 2012
- *
- * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
- * for ST-Ericsson.
- *
- * License terms:
- *
- * This 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.
- */
-
-void mop500_msp_init(struct device *parent);
diff --git a/arch/arm/mach-ux500/board-mop500-regulators.c b/arch/arm/mach-ux500/board-mop500-regulators.c
index 52426a425787..2a17bc506cff 100644
--- a/arch/arm/mach-ux500/board-mop500-regulators.c
+++ b/arch/arm/mach-ux500/board-mop500-regulators.c
@@ -13,6 +13,21 @@
#include <linux/regulator/ab8500.h>
#include "board-mop500-regulators.h"
+static struct regulator_consumer_supply gpio_en_3v3_consumers[] = {
+ REGULATOR_SUPPLY("vdd33a", "smsc911x.0"),
+};
+
+struct regulator_init_data gpio_en_3v3_regulator = {
+ .constraints = {
+ .name = "EN-3V3",
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(gpio_en_3v3_consumers),
+ .consumer_supplies = gpio_en_3v3_consumers,
+};
+
/*
* TPS61052 regulator
*/
diff --git a/arch/arm/mach-ux500/board-mop500-regulators.h b/arch/arm/mach-ux500/board-mop500-regulators.h
index 94992158d962..78a0642a2206 100644
--- a/arch/arm/mach-ux500/board-mop500-regulators.h
+++ b/arch/arm/mach-ux500/board-mop500-regulators.h
@@ -18,5 +18,6 @@ extern struct ab8500_regulator_reg_init
ab8500_regulator_reg_init[AB8500_NUM_REGULATOR_REGISTERS];
extern struct regulator_init_data ab8500_regulators[AB8500_NUM_REGULATORS];
extern struct regulator_init_data tps61052_regulator;
+extern struct regulator_init_data gpio_en_3v3_regulator;
#endif
diff --git a/arch/arm/mach-ux500/board-mop500-sdi.c b/arch/arm/mach-ux500/board-mop500-sdi.c
index 18ff781cfbe4..9c8e4a9e83ee 100644
--- a/arch/arm/mach-ux500/board-mop500-sdi.c
+++ b/arch/arm/mach-ux500/board-mop500-sdi.c
@@ -152,7 +152,7 @@ static struct stedma40_chan_cfg sdi1_dma_cfg_tx = {
};
#endif
-static struct mmci_platform_data mop500_sdi1_data = {
+struct mmci_platform_data mop500_sdi1_data = {
.ocr_mask = MMC_VDD_29_30,
.f_max = 50000000,
.capabilities = MMC_CAP_4_BIT_DATA,
@@ -189,7 +189,7 @@ static struct stedma40_chan_cfg mop500_sdi2_dma_cfg_tx = {
};
#endif
-static struct mmci_platform_data mop500_sdi2_data = {
+struct mmci_platform_data mop500_sdi2_data = {
.ocr_mask = MMC_VDD_165_195,
.f_max = 50000000,
.capabilities = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA |
diff --git a/arch/arm/mach-ux500/board-mop500.c b/arch/arm/mach-ux500/board-mop500.c
index 7ebfcc7d7515..416d436111f2 100644
--- a/arch/arm/mach-ux500/board-mop500.c
+++ b/arch/arm/mach-ux500/board-mop500.c
@@ -23,6 +23,7 @@
#include <linux/spi/spi.h>
#include <linux/mfd/abx500/ab8500.h>
#include <linux/regulator/ab8500.h>
+#include <linux/regulator/fixed.h>
#include <linux/mfd/tc3589x.h>
#include <linux/mfd/tps6105x.h>
#include <linux/mfd/abx500/ab8500-gpio.h>
@@ -54,7 +55,6 @@
#include "devices-db8500.h"
#include "board-mop500.h"
#include "board-mop500-regulators.h"
-#include "board-mop500-msp.h"
static struct gpio_led snowball_led_array[] = {
{
@@ -76,6 +76,23 @@ static struct platform_device snowball_led_dev = {
},
};
+static struct fixed_voltage_config snowball_gpio_en_3v3_data = {
+ .supply_name = "EN-3V3",
+ .gpio = SNOWBALL_EN_3V3_ETH_GPIO,
+ .microvolts = 3300000,
+ .enable_high = 1,
+ .init_data = &gpio_en_3v3_regulator,
+ .startup_delay = 5000, /* 1200us */
+};
+
+static struct platform_device snowball_gpio_en_3v3_regulator_dev = {
+ .name = "reg-fixed-voltage",
+ .id = 1,
+ .dev = {
+ .platform_data = &snowball_gpio_en_3v3_data,
+ },
+};
+
static struct ab8500_gpio_platform_data ab8500_gpio_pdata = {
.gpio_base = MOP500_AB8500_PIN_GPIO(1),
.irq_base = MOP500_AB8500_VIR_GPIO_IRQ_BASE,
@@ -565,6 +582,7 @@ static struct platform_device *snowball_platform_devs[] __initdata = {
&snowball_led_dev,
&snowball_key_dev,
&snowball_sbnet_dev,
+ &snowball_gpio_en_3v3_regulator_dev,
};
static void __init mop500_init_machine(void)
@@ -587,7 +605,7 @@ static void __init mop500_init_machine(void)
mop500_i2c_init(parent);
mop500_sdi_init(parent);
mop500_spi_init(parent);
- mop500_msp_init(parent);
+ mop500_audio_init(parent);
mop500_uart_init(parent);
u8500_cryp1_hash1_init(parent);
@@ -621,7 +639,7 @@ static void __init snowball_init_machine(void)
mop500_i2c_init(parent);
snowball_sdi_init(parent);
mop500_spi_init(parent);
- mop500_msp_init(parent);
+ mop500_audio_init(parent);
mop500_uart_init(parent);
/* This board has full regulator constraints */
@@ -653,7 +671,7 @@ static void __init hrefv60_init_machine(void)
mop500_i2c_init(parent);
hrefv60_sdi_init(parent);
mop500_spi_init(parent);
- mop500_msp_init(parent);
+ mop500_audio_init(parent);
mop500_uart_init(parent);
i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
@@ -708,12 +726,9 @@ MACHINE_END
#ifdef CONFIG_MACH_UX500_DT
-static struct platform_device *snowball_of_platform_devs[] __initdata = {
- &snowball_led_dev,
- &snowball_key_dev,
-};
-
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),
/* Requires DMA and call-back bindings. */
OF_DEV_AUXDATA("arm,pl011", 0x80120000, "uart0", &uart0_plat),
OF_DEV_AUXDATA("arm,pl011", 0x80121000, "uart1", &uart1_plat),
@@ -721,6 +736,8 @@ struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = {
/* Requires DMA bindings. */
OF_DEV_AUXDATA("arm,pl022", 0x80002000, "ssp0", &ssp0_plat),
OF_DEV_AUXDATA("arm,pl18x", 0x80126000, "sdi0", &mop500_sdi0_data),
+ OF_DEV_AUXDATA("arm,pl18x", 0x80118000, "sdi1", &mop500_sdi1_data),
+ OF_DEV_AUXDATA("arm,pl18x", 0x80005000, "sdi2", &mop500_sdi2_data),
OF_DEV_AUXDATA("arm,pl18x", 0x80114000, "sdi4", &mop500_sdi4_data),
/* Requires clock name bindings. */
OF_DEV_AUXDATA("st,nomadik-gpio", 0x8012e000, "gpio.0", NULL),
@@ -739,6 +756,15 @@ struct of_dev_auxdata u8500_auxdata_lookup[] __initdata = {
OF_DEV_AUXDATA("st,nomadik-i2c", 0x8012a000, "nmk-i2c.4", NULL),
/* Requires device name bindings. */
OF_DEV_AUXDATA("stericsson,nmk_pinctrl", 0, "pinctrl-db8500", NULL),
+ /* Requires clock name and DMA bindings. */
+ OF_DEV_AUXDATA("stericsson,ux500-msp-i2s", 0x80123000,
+ "ux500-msp-i2s.0", &msp0_platform_data),
+ OF_DEV_AUXDATA("stericsson,ux500-msp-i2s", 0x80124000,
+ "ux500-msp-i2s.1", &msp1_platform_data),
+ OF_DEV_AUXDATA("stericsson,ux500-msp-i2s", 0x80117000,
+ "ux500-msp-i2s.2", &msp2_platform_data),
+ OF_DEV_AUXDATA("stericsson,ux500-msp-i2s", 0x80125000,
+ "ux500-msp-i2s.3", &msp3_platform_data),
{},
};
@@ -779,7 +805,7 @@ static void __init u8500_init_machine(void)
ARRAY_SIZE(mop500_platform_devs));
mop500_sdi_init(parent);
- mop500_msp_init(parent);
+ mop500_audio_init(parent);
i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs);
i2c_register_board_info(2, mop500_i2c2_devices,
@@ -788,7 +814,7 @@ static void __init u8500_init_machine(void)
mop500_uib_init();
} else if (of_machine_is_compatible("calaosystems,snowball-a9500")) {
- mop500_msp_init(parent);
+ mop500_of_audio_init(parent);
} else if (of_machine_is_compatible("st-ericsson,hrefv60+")) {
/*
* The HREFv60 board removed a GPIO expander and routed
@@ -799,16 +825,6 @@ static void __init u8500_init_machine(void)
platform_add_devices(mop500_platform_devs,
ARRAY_SIZE(mop500_platform_devs));
- hrefv60_sdi_init(parent);
- mop500_msp_init(parent);
-
- i2c0_devs = ARRAY_SIZE(mop500_i2c0_devices);
- i2c0_devs -= NUM_PRE_V60_I2C0_DEVICES;
-
- i2c_register_board_info(0, mop500_i2c0_devices, i2c0_devs);
- i2c_register_board_info(2, mop500_i2c2_devices,
- ARRAY_SIZE(mop500_i2c2_devices));
-
mop500_uib_init();
}
diff --git a/arch/arm/mach-ux500/board-mop500.h b/arch/arm/mach-ux500/board-mop500.h
index b5bfc1a78b1a..aca39a68712a 100644
--- a/arch/arm/mach-ux500/board-mop500.h
+++ b/arch/arm/mach-ux500/board-mop500.h
@@ -9,6 +9,7 @@
/* For NOMADIK_NR_GPIO */
#include <mach/irqs.h>
+#include <mach/msp.h>
#include <linux/amba/mmci.h>
/* Snowball specific GPIO assignments, this board has no GPIO expander */
@@ -80,7 +81,14 @@
struct device;
struct i2c_board_info;
extern struct mmci_platform_data mop500_sdi0_data;
+extern struct mmci_platform_data mop500_sdi1_data;
+extern struct mmci_platform_data mop500_sdi2_data;
extern struct mmci_platform_data mop500_sdi4_data;
+extern struct msp_i2s_platform_data msp0_platform_data;
+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;
+extern struct arm_pmu_platdata db8500_pmu_platdata;
extern void mop500_sdi_init(struct device *parent);
extern void snowball_sdi_init(struct device *parent);
@@ -91,6 +99,9 @@ void __init mop500_stuib_init(void);
void __init mop500_pinmaps_init(void);
void __init snowball_pinmaps_init(void);
void __init hrefv60_pinmaps_init(void);
+void mop500_audio_init(struct device *parent);
+/* Due for removal once the MSP driver has been fully DT:ed. */
+void mop500_of_audio_init(struct device *parent);
int __init mop500_uib_init(void);
void mop500_uib_i2c_add(int busnum, struct i2c_board_info *info,
diff --git a/arch/arm/mach-ux500/cpu-db8500.c b/arch/arm/mach-ux500/cpu-db8500.c
index aef2c68e9ec1..bcdfe6b1d453 100644
--- a/arch/arm/mach-ux500/cpu-db8500.c
+++ b/arch/arm/mach-ux500/cpu-db8500.c
@@ -18,6 +18,7 @@
#include <linux/io.h>
#include <linux/mfd/abx500/ab8500.h>
+#include <asm/pmu.h>
#include <asm/mach/map.h>
#include <plat/gpio-nomadik.h>
#include <mach/hardware.h>
@@ -137,10 +138,6 @@ static struct platform_device *platform_devs[] __initdata = {
&db8500_prcmu_device,
};
-static struct platform_device *of_platform_devs[] __initdata = {
- &u8500_dma40_device,
-};
-
static resource_size_t __initdata db8500_gpio_base[] = {
U8500_GPIOBANK0_BASE,
U8500_GPIOBANK1_BASE,
@@ -234,7 +231,6 @@ struct device * __init u8500_init_devices(struct ab8500_platform_data *ab8500)
struct device * __init u8500_of_init_devices(void)
{
struct device *parent;
- int i;
parent = db8500_soc_device_init();
@@ -243,8 +239,7 @@ struct device * __init u8500_of_init_devices(void)
platform_device_register_data(parent,
"cpufreq-u8500", -1, NULL, 0);
- for (i = 0; i < ARRAY_SIZE(of_platform_devs); i++)
- of_platform_devs[i]->dev.parent = parent;
+ u8500_dma40_device.dev.parent = parent;
/*
* Devices to be DT:ed:
@@ -252,7 +247,7 @@ struct device * __init u8500_of_init_devices(void)
* db8500_pmu_device = done
* db8500_prcmu_device = done
*/
- platform_add_devices(of_platform_devs, ARRAY_SIZE(of_platform_devs));
+ platform_device_register(&u8500_dma40_device);
return parent;
}
diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c
index 3d62c64c84c4..2236cbd03cd7 100644
--- a/arch/arm/mach-ux500/cpu.c
+++ b/arch/arm/mach-ux500/cpu.c
@@ -49,6 +49,8 @@ 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);
diff --git a/arch/arm/mach-ux500/include/mach/msp.h b/arch/arm/mach-ux500/include/mach/msp.h
index 798be19129ef..3cc7142eee02 100644
--- a/arch/arm/mach-ux500/include/mach/msp.h
+++ b/arch/arm/mach-ux500/include/mach/msp.h
@@ -22,8 +22,6 @@ struct msp_i2s_platform_data {
enum msp_i2s_id id;
struct stedma40_chan_cfg *msp_i2s_dma_rx;
struct stedma40_chan_cfg *msp_i2s_dma_tx;
- int (*msp_i2s_init) (void);
- int (*msp_i2s_exit) (void);
};
#endif
diff --git a/arch/arm/mach-ux500/include/mach/uncompress.h b/arch/arm/mach-ux500/include/mach/uncompress.h
index 34775baadaea..d60ecd1753f0 100644
--- a/arch/arm/mach-ux500/include/mach/uncompress.h
+++ b/arch/arm/mach-ux500/include/mach/uncompress.h
@@ -24,7 +24,7 @@
#include <linux/amba/serial.h>
#include <mach/hardware.h>
-u32 ux500_uart_base;
+void __iomem *ux500_uart_base;
static void putc(const char c)
{
@@ -51,7 +51,7 @@ static void flush(void)
static inline void arch_decomp_setup(void)
{
/* Use machine_is_foo() macro if you need to switch base someday */
- ux500_uart_base = U8500_UART2_BASE;
+ ux500_uart_base = (void __iomem *)U8500_UART2_BASE;
}
#define arch_decomp_wdog() /* nothing to do here */
diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c
index ca7902c6ed18..5b5c1eeb5b5c 100644
--- a/arch/arm/mach-versatile/core.c
+++ b/arch/arm/mach-versatile/core.c
@@ -37,7 +37,6 @@
#include <linux/mtd/physmap.h>
#include <asm/irq.h>
-#include <asm/leds.h>
#include <asm/hardware/arm_timer.h>
#include <asm/hardware/icst.h>
#include <asm/hardware/vic.h>
@@ -758,10 +757,6 @@ void __init versatile_init(void)
struct amba_device *d = amba_devs[i];
amba_device_register(d, &iomem_resource);
}
-
-#ifdef CONFIG_LEDS
- leds_event = versatile_leds_event;
-#endif
}
/*
diff --git a/arch/arm/mach-vt8500/Kconfig b/arch/arm/mach-vt8500/Kconfig
deleted file mode 100644
index 2c20a341c11a..000000000000
--- a/arch/arm/mach-vt8500/Kconfig
+++ /dev/null
@@ -1,73 +0,0 @@
-if ARCH_VT8500
-
-config VTWM_VERSION_VT8500
- bool
-
-config VTWM_VERSION_WM8505
- bool
-
-config MACH_BV07
- bool "Benign BV07-8500 Mini Netbook"
- depends on ARCH_VT8500
- select VTWM_VERSION_VT8500
- help
- Add support for the inexpensive 7-inch netbooks sold by many
- Chinese distributors under various names. Note that there are
- many hardware implementations in identical exterior, make sure
- that yours is indeed based on a VIA VT8500 chip.
-
-config MACH_WM8505_7IN_NETBOOK
- bool "WM8505 7-inch generic netbook"
- depends on ARCH_VT8500
- select VTWM_VERSION_WM8505
- help
- Add support for the inexpensive 7-inch netbooks sold by many
- Chinese distributors under various names. Note that there are
- many hardware implementations in identical exterior, make sure
- that yours is indeed based on a WonderMedia WM8505 chip.
-
-comment "LCD panel size"
-
-config WMT_PANEL_800X480
- bool "7-inch with 800x480 resolution"
- depends on (FB_VT8500 || FB_WM8505)
- default y
- help
- These are found in most of the netbooks in generic cases, as
- well as in Eken M001 tablets and possibly elsewhere.
-
- To select this panel at runtime, say y here and append
- 'panel=800x480' to your kernel command line. Otherwise, the
- largest one available will be used.
-
-config WMT_PANEL_800X600
- bool "8-inch with 800x600 resolution"
- depends on (FB_VT8500 || FB_WM8505)
- help
- These are found in Eken M003 tablets and possibly elsewhere.
-
- To select this panel at runtime, say y here and append
- 'panel=800x600' to your kernel command line. Otherwise, the
- largest one available will be used.
-
-config WMT_PANEL_1024X576
- bool "10-inch with 1024x576 resolution"
- depends on (FB_VT8500 || FB_WM8505)
- help
- These are found in CherryPal netbooks and possibly elsewhere.
-
- To select this panel at runtime, say y here and append
- 'panel=1024x576' to your kernel command line. Otherwise, the
- largest one available will be used.
-
-config WMT_PANEL_1024X600
- bool "10-inch with 1024x600 resolution"
- depends on (FB_VT8500 || FB_WM8505)
- help
- These are found in Eken M006 tablets and possibly elsewhere.
-
- To select this panel at runtime, say y here and append
- 'panel=1024x600' to your kernel command line. Otherwise, the
- largest one available will be used.
-
-endif
diff --git a/arch/arm/mach-vt8500/Makefile b/arch/arm/mach-vt8500/Makefile
index 7ce51767c99c..e035251cda48 100644
--- a/arch/arm/mach-vt8500/Makefile
+++ b/arch/arm/mach-vt8500/Makefile
@@ -1,7 +1 @@
-obj-y += devices.o gpio.o irq.o timer.o restart.o
-
-obj-$(CONFIG_VTWM_VERSION_VT8500) += devices-vt8500.o
-obj-$(CONFIG_VTWM_VERSION_WM8505) += devices-wm8505.o
-
-obj-$(CONFIG_MACH_BV07) += bv07.o
-obj-$(CONFIG_MACH_WM8505_7IN_NETBOOK) += wm8505_7in.o
+obj-$(CONFIG_ARCH_VT8500) += irq.o timer.o vt8500.o
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
deleted file mode 100644
index f9fbeb2d10e9..000000000000
--- a/arch/arm/mach-vt8500/bv07.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * arch/arm/mach-vt8500/bv07.c
- *
- * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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/io.h>
-#include <linux/pm.h>
-
-#include <asm/mach-types.h>
-#include <asm/mach/arch.h>
-#include <mach/restart.h>
-
-#include "devices.h"
-
-static void __iomem *pmc_hiber;
-
-static struct platform_device *devices[] __initdata = {
- &vt8500_device_uart0,
- &vt8500_device_lcdc,
- &vt8500_device_ehci,
- &vt8500_device_ge_rops,
- &vt8500_device_pwm,
- &vt8500_device_pwmbl,
- &vt8500_device_rtc,
-};
-
-static void vt8500_power_off(void)
-{
- local_irq_disable();
- writew(5, pmc_hiber);
- asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
-}
-
-void __init bv07_init(void)
-{
-#ifdef CONFIG_FB_VT8500
- void __iomem *gpio_mux_reg = ioremap(wmt_gpio_base + 0x200, 4);
- if (gpio_mux_reg) {
- writel(readl(gpio_mux_reg) | 1, gpio_mux_reg);
- iounmap(gpio_mux_reg);
- } else {
- printk(KERN_ERR "Could not remap the GPIO mux register, display may not work properly!\n");
- }
-#endif
- pmc_hiber = ioremap(wmt_pmc_base + 0x12, 2);
- if (pmc_hiber)
- pm_power_off = &vt8500_power_off;
- else
- printk(KERN_ERR "PMC Hibernation register could not be remapped, not enabling power off!\n");
-
- wmt_setup_restart();
- vt8500_set_resources();
- platform_add_devices(devices, ARRAY_SIZE(devices));
- vt8500_gpio_init();
-}
-
-MACHINE_START(BV07, "Benign BV07 Mini Netbook")
- .atag_offset = 0x100,
- .restart = wmt_restart,
- .reserve = vt8500_reserve_mem,
- .map_io = vt8500_map_io,
- .init_irq = vt8500_init_irq,
- .timer = &vt8500_timer,
- .init_machine = bv07_init,
-MACHINE_END
diff --git a/arch/arm/mach-tegra/include/mach/gpio-tegra.h b/arch/arm/mach-vt8500/common.h
index a978b3cc3a8d..2b2419646e95 100644
--- a/arch/arm/mach-tegra/include/mach/gpio-tegra.h
+++ b/arch/arm/mach-vt8500/common.h
@@ -1,10 +1,6 @@
-/*
- * arch/arm/mach-tegra/include/mach/gpio.h
+/* linux/arch/arm/mach-vt8500/dt_common.h
*
- * Copyright (C) 2010 Google, Inc.
- *
- * Author:
- * Erik Gilling <konkers@google.com>
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -17,12 +13,16 @@
*
*/
-#ifndef __MACH_TEGRA_GPIO_TEGRA_H
-#define __MACH_TEGRA_GPIO_TEGRA_H
+#ifndef __ARCH_ARM_MACH_VT8500_DT_COMMON_H
+#define __ARCH_ARM_MACH_VT8500_DT_COMMON_H
+
+#include <linux/of.h>
-#include <linux/types.h>
-#include <mach/irqs.h>
+void __init vt8500_timer_init(void);
+int __init vt8500_irq_init(struct device_node *node,
+ struct device_node *parent);
-#define TEGRA_NR_GPIOS INT_GPIO_NR
+/* defined in drivers/clk/clk-vt8500.c */
+void __init vtwm_clk_init(void __iomem *pmc_base);
#endif
diff --git a/arch/arm/mach-vt8500/devices-vt8500.c b/arch/arm/mach-vt8500/devices-vt8500.c
deleted file mode 100644
index 19519aeecf37..000000000000
--- a/arch/arm/mach-vt8500/devices-vt8500.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/* linux/arch/arm/mach-vt8500/devices-vt8500.c
- *
- * Copyright (C) 2010 Alexey Charkov <alchark@gmail.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/platform_device.h>
-
-#include <mach/vt8500_regs.h>
-#include <mach/vt8500_irqs.h>
-#include <mach/i8042.h>
-#include "devices.h"
-
-void __init vt8500_set_resources(void)
-{
- struct resource tmp[3];
-
- tmp[0] = wmt_mmio_res(VT8500_LCDC_BASE, SZ_1K);
- tmp[1] = wmt_irq_res(IRQ_LCDC);
- wmt_res_add(&vt8500_device_lcdc, tmp, 2);
-
- tmp[0] = wmt_mmio_res(VT8500_UART0_BASE, 0x1040);
- tmp[1] = wmt_irq_res(IRQ_UART0);
- wmt_res_add(&vt8500_device_uart0, tmp, 2);
-
- tmp[0] = wmt_mmio_res(VT8500_UART1_BASE, 0x1040);
- tmp[1] = wmt_irq_res(IRQ_UART1);
- wmt_res_add(&vt8500_device_uart1, tmp, 2);
-
- tmp[0] = wmt_mmio_res(VT8500_UART2_BASE, 0x1040);
- tmp[1] = wmt_irq_res(IRQ_UART2);
- wmt_res_add(&vt8500_device_uart2, tmp, 2);
-
- tmp[0] = wmt_mmio_res(VT8500_UART3_BASE, 0x1040);
- tmp[1] = wmt_irq_res(IRQ_UART3);
- wmt_res_add(&vt8500_device_uart3, tmp, 2);
-
- tmp[0] = wmt_mmio_res(VT8500_EHCI_BASE, SZ_512);
- tmp[1] = wmt_irq_res(IRQ_EHCI);
- wmt_res_add(&vt8500_device_ehci, tmp, 2);
-
- tmp[0] = wmt_mmio_res(VT8500_GEGEA_BASE, SZ_256);
- wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
-
- tmp[0] = wmt_mmio_res(VT8500_PWM_BASE, 0x44);
- wmt_res_add(&vt8500_device_pwm, tmp, 1);
-
- tmp[0] = wmt_mmio_res(VT8500_RTC_BASE, 0x2c);
- tmp[1] = wmt_irq_res(IRQ_RTC);
- tmp[2] = wmt_irq_res(IRQ_RTCSM);
- wmt_res_add(&vt8500_device_rtc, tmp, 3);
-}
-
-static void __init vt8500_set_externs(void)
-{
- /* Non-resource-aware stuff */
- wmt_ic_base = VT8500_IC_BASE;
- wmt_gpio_base = VT8500_GPIO_BASE;
- wmt_pmc_base = VT8500_PMC_BASE;
- wmt_i8042_base = VT8500_PS2_BASE;
-
- wmt_nr_irqs = VT8500_NR_IRQS;
- wmt_timer_irq = IRQ_PMCOS0;
- wmt_gpio_ext_irq[0] = IRQ_EXT0;
- wmt_gpio_ext_irq[1] = IRQ_EXT1;
- wmt_gpio_ext_irq[2] = IRQ_EXT2;
- wmt_gpio_ext_irq[3] = IRQ_EXT3;
- wmt_gpio_ext_irq[4] = IRQ_EXT4;
- wmt_gpio_ext_irq[5] = IRQ_EXT5;
- wmt_gpio_ext_irq[6] = IRQ_EXT6;
- wmt_gpio_ext_irq[7] = IRQ_EXT7;
- wmt_i8042_kbd_irq = IRQ_PS2KBD;
- wmt_i8042_aux_irq = IRQ_PS2MOUSE;
-}
-
-void __init vt8500_map_io(void)
-{
- iotable_init(wmt_io_desc, ARRAY_SIZE(wmt_io_desc));
-
- /* Should be done before interrupts and timers are initialized */
- vt8500_set_externs();
-}
diff --git a/arch/arm/mach-vt8500/devices-wm8505.c b/arch/arm/mach-vt8500/devices-wm8505.c
deleted file mode 100644
index db4594e029f4..000000000000
--- a/arch/arm/mach-vt8500/devices-wm8505.c
+++ /dev/null
@@ -1,99 +0,0 @@
-/* linux/arch/arm/mach-vt8500/devices-wm8505.c
- *
- * Copyright (C) 2010 Alexey Charkov <alchark@gmail.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/platform_device.h>
-
-#include <mach/wm8505_regs.h>
-#include <mach/wm8505_irqs.h>
-#include <mach/i8042.h>
-#include "devices.h"
-
-void __init wm8505_set_resources(void)
-{
- struct resource tmp[3];
-
- tmp[0] = wmt_mmio_res(WM8505_GOVR_BASE, SZ_512);
- wmt_res_add(&vt8500_device_wm8505_fb, tmp, 1);
-
- tmp[0] = wmt_mmio_res(WM8505_UART0_BASE, 0x1040);
- tmp[1] = wmt_irq_res(IRQ_UART0);
- wmt_res_add(&vt8500_device_uart0, tmp, 2);
-
- tmp[0] = wmt_mmio_res(WM8505_UART1_BASE, 0x1040);
- tmp[1] = wmt_irq_res(IRQ_UART1);
- wmt_res_add(&vt8500_device_uart1, tmp, 2);
-
- tmp[0] = wmt_mmio_res(WM8505_UART2_BASE, 0x1040);
- tmp[1] = wmt_irq_res(IRQ_UART2);
- wmt_res_add(&vt8500_device_uart2, tmp, 2);
-
- tmp[0] = wmt_mmio_res(WM8505_UART3_BASE, 0x1040);
- tmp[1] = wmt_irq_res(IRQ_UART3);
- wmt_res_add(&vt8500_device_uart3, tmp, 2);
-
- tmp[0] = wmt_mmio_res(WM8505_UART4_BASE, 0x1040);
- tmp[1] = wmt_irq_res(IRQ_UART4);
- wmt_res_add(&vt8500_device_uart4, tmp, 2);
-
- tmp[0] = wmt_mmio_res(WM8505_UART5_BASE, 0x1040);
- tmp[1] = wmt_irq_res(IRQ_UART5);
- wmt_res_add(&vt8500_device_uart5, tmp, 2);
-
- tmp[0] = wmt_mmio_res(WM8505_EHCI_BASE, SZ_512);
- tmp[1] = wmt_irq_res(IRQ_EHCI);
- wmt_res_add(&vt8500_device_ehci, tmp, 2);
-
- tmp[0] = wmt_mmio_res(WM8505_GEGEA_BASE, SZ_256);
- wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
-
- tmp[0] = wmt_mmio_res(WM8505_PWM_BASE, 0x44);
- wmt_res_add(&vt8500_device_pwm, tmp, 1);
-
- tmp[0] = wmt_mmio_res(WM8505_RTC_BASE, 0x2c);
- tmp[1] = wmt_irq_res(IRQ_RTC);
- tmp[2] = wmt_irq_res(IRQ_RTCSM);
- wmt_res_add(&vt8500_device_rtc, tmp, 3);
-}
-
-static void __init wm8505_set_externs(void)
-{
- /* Non-resource-aware stuff */
- wmt_ic_base = WM8505_IC_BASE;
- wmt_sic_base = WM8505_SIC_BASE;
- wmt_gpio_base = WM8505_GPIO_BASE;
- wmt_pmc_base = WM8505_PMC_BASE;
- wmt_i8042_base = WM8505_PS2_BASE;
-
- wmt_nr_irqs = WM8505_NR_IRQS;
- wmt_timer_irq = IRQ_PMCOS0;
- wmt_gpio_ext_irq[0] = IRQ_EXT0;
- wmt_gpio_ext_irq[1] = IRQ_EXT1;
- wmt_gpio_ext_irq[2] = IRQ_EXT2;
- wmt_gpio_ext_irq[3] = IRQ_EXT3;
- wmt_gpio_ext_irq[4] = IRQ_EXT4;
- wmt_gpio_ext_irq[5] = IRQ_EXT5;
- wmt_gpio_ext_irq[6] = IRQ_EXT6;
- wmt_gpio_ext_irq[7] = IRQ_EXT7;
- wmt_i8042_kbd_irq = IRQ_PS2KBD;
- wmt_i8042_aux_irq = IRQ_PS2MOUSE;
-}
-
-void __init wm8505_map_io(void)
-{
- iotable_init(wmt_io_desc, ARRAY_SIZE(wmt_io_desc));
-
- /* Should be done before interrupts and timers are initialized */
- wm8505_set_externs();
-}
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
deleted file mode 100644
index 82b4bcedffba..000000000000
--- a/arch/arm/mach-vt8500/devices.c
+++ /dev/null
@@ -1,270 +0,0 @@
-/* linux/arch/arm/mach-vt8500/devices.c
- *
- * Copyright (C) 2010 Alexey Charkov <alchark@gmail.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/io.h>
-#include <linux/device.h>
-#include <linux/dma-mapping.h>
-#include <linux/platform_device.h>
-#include <linux/pwm_backlight.h>
-#include <linux/memblock.h>
-
-#include <asm/mach/arch.h>
-
-#include <linux/platform_data/video-vt8500lcdfb.h>
-#include <mach/i8042.h>
-#include "devices.h"
-
-/* These can't use resources currently */
-unsigned long wmt_ic_base __initdata;
-unsigned long wmt_sic_base __initdata;
-unsigned long wmt_gpio_base __initdata;
-unsigned long wmt_pmc_base __initdata;
-unsigned long wmt_i8042_base __initdata;
-
-int wmt_nr_irqs __initdata;
-int wmt_timer_irq __initdata;
-int wmt_gpio_ext_irq[8] __initdata;
-
-/* Should remain accessible after init.
- * i8042 driver desperately calls for attention...
- */
-int wmt_i8042_kbd_irq;
-int wmt_i8042_aux_irq;
-
-static u64 fb_dma_mask = DMA_BIT_MASK(32);
-
-struct platform_device vt8500_device_lcdc = {
- .name = "vt8500-lcd",
- .id = 0,
- .dev = {
- .dma_mask = &fb_dma_mask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
-};
-
-struct platform_device vt8500_device_wm8505_fb = {
- .name = "wm8505-fb",
- .id = 0,
-};
-
-/* Smallest to largest */
-static struct vt8500fb_platform_data panels[] = {
-#ifdef CONFIG_WMT_PANEL_800X480
-{
- .xres_virtual = 800,
- .yres_virtual = 480 * 2,
- .mode = {
- .name = "800x480",
- .xres = 800,
- .yres = 480,
- .left_margin = 88,
- .right_margin = 40,
- .upper_margin = 32,
- .lower_margin = 11,
- .hsync_len = 0,
- .vsync_len = 1,
- .vmode = FB_VMODE_NONINTERLACED,
- },
-},
-#endif
-#ifdef CONFIG_WMT_PANEL_800X600
-{
- .xres_virtual = 800,
- .yres_virtual = 600 * 2,
- .mode = {
- .name = "800x600",
- .xres = 800,
- .yres = 600,
- .left_margin = 88,
- .right_margin = 40,
- .upper_margin = 32,
- .lower_margin = 11,
- .hsync_len = 0,
- .vsync_len = 1,
- .vmode = FB_VMODE_NONINTERLACED,
- },
-},
-#endif
-#ifdef CONFIG_WMT_PANEL_1024X576
-{
- .xres_virtual = 1024,
- .yres_virtual = 576 * 2,
- .mode = {
- .name = "1024x576",
- .xres = 1024,
- .yres = 576,
- .left_margin = 40,
- .right_margin = 24,
- .upper_margin = 32,
- .lower_margin = 11,
- .hsync_len = 96,
- .vsync_len = 2,
- .vmode = FB_VMODE_NONINTERLACED,
- },
-},
-#endif
-#ifdef CONFIG_WMT_PANEL_1024X600
-{
- .xres_virtual = 1024,
- .yres_virtual = 600 * 2,
- .mode = {
- .name = "1024x600",
- .xres = 1024,
- .yres = 600,
- .left_margin = 66,
- .right_margin = 2,
- .upper_margin = 19,
- .lower_margin = 1,
- .hsync_len = 23,
- .vsync_len = 8,
- .vmode = FB_VMODE_NONINTERLACED,
- },
-},
-#endif
-};
-
-static int current_panel_idx __initdata = ARRAY_SIZE(panels) - 1;
-
-static int __init panel_setup(char *str)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(panels); i++) {
- if (strcmp(panels[i].mode.name, str) == 0) {
- current_panel_idx = i;
- break;
- }
- }
- return 0;
-}
-
-early_param("panel", panel_setup);
-
-static inline void preallocate_fb(struct vt8500fb_platform_data *p,
- unsigned long align) {
- p->video_mem_len = (p->xres_virtual * p->yres_virtual * 4) >>
- (p->bpp > 16 ? 0 : (p->bpp > 8 ? 1 :
- (8 / p->bpp) + 1));
- p->video_mem_phys = (unsigned long)memblock_alloc(p->video_mem_len,
- align);
- p->video_mem_virt = phys_to_virt(p->video_mem_phys);
-}
-
-struct platform_device vt8500_device_uart0 = {
- .name = "vt8500_serial",
- .id = 0,
-};
-
-struct platform_device vt8500_device_uart1 = {
- .name = "vt8500_serial",
- .id = 1,
-};
-
-struct platform_device vt8500_device_uart2 = {
- .name = "vt8500_serial",
- .id = 2,
-};
-
-struct platform_device vt8500_device_uart3 = {
- .name = "vt8500_serial",
- .id = 3,
-};
-
-struct platform_device vt8500_device_uart4 = {
- .name = "vt8500_serial",
- .id = 4,
-};
-
-struct platform_device vt8500_device_uart5 = {
- .name = "vt8500_serial",
- .id = 5,
-};
-
-static u64 ehci_dma_mask = DMA_BIT_MASK(32);
-
-struct platform_device vt8500_device_ehci = {
- .name = "vt8500-ehci",
- .id = 0,
- .dev = {
- .dma_mask = &ehci_dma_mask,
- .coherent_dma_mask = DMA_BIT_MASK(32),
- },
-};
-
-struct platform_device vt8500_device_ge_rops = {
- .name = "wmt_ge_rops",
- .id = -1,
-};
-
-struct platform_device vt8500_device_pwm = {
- .name = "vt8500-pwm",
- .id = 0,
-};
-
-static struct platform_pwm_backlight_data vt8500_pwmbl_data = {
- .pwm_id = 0,
- .max_brightness = 128,
- .dft_brightness = 70,
- .pwm_period_ns = 250000, /* revisit when clocks are implemented */
-};
-
-struct platform_device vt8500_device_pwmbl = {
- .name = "pwm-backlight",
- .id = 0,
- .dev = {
- .platform_data = &vt8500_pwmbl_data,
- },
-};
-
-struct platform_device vt8500_device_rtc = {
- .name = "vt8500-rtc",
- .id = 0,
-};
-
-struct map_desc wmt_io_desc[] __initdata = {
- /* SoC MMIO registers */
- [0] = {
- .virtual = 0xf8000000,
- .pfn = __phys_to_pfn(0xd8000000),
- .length = 0x00390000, /* max of all chip variants */
- .type = MT_DEVICE
- },
- /* PCI I/O space, numbers tied to those in <mach/io.h> */
- [1] = {
- .virtual = 0xf0000000,
- .pfn = __phys_to_pfn(0xc0000000),
- .length = SZ_64K,
- .type = MT_DEVICE
- },
-};
-
-void __init vt8500_reserve_mem(void)
-{
-#ifdef CONFIG_FB_VT8500
- panels[current_panel_idx].bpp = 16; /* Always use RGB565 */
- preallocate_fb(&panels[current_panel_idx], SZ_4M);
- vt8500_device_lcdc.dev.platform_data = &panels[current_panel_idx];
-#endif
-}
-
-void __init wm8505_reserve_mem(void)
-{
-#if defined CONFIG_FB_WM8505
- panels[current_panel_idx].bpp = 32; /* Always use RGB888 */
- preallocate_fb(&panels[current_panel_idx], 32);
- vt8500_device_wm8505_fb.dev.platform_data = &panels[current_panel_idx];
-#endif
-}
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
deleted file mode 100644
index 188d4e17f35c..000000000000
--- a/arch/arm/mach-vt8500/devices.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/* linux/arch/arm/mach-vt8500/devices.h
- *
- * Copyright (C) 2010 Alexey Charkov <alchark@gmail.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 __ARCH_ARM_MACH_VT8500_DEVICES_H
-#define __ARCH_ARM_MACH_VT8500_DEVICES_H
-
-#include <linux/platform_device.h>
-#include <asm/mach/map.h>
-
-void __init vt8500_init_irq(void);
-void __init wm8505_init_irq(void);
-void __init vt8500_map_io(void);
-void __init wm8505_map_io(void);
-void __init vt8500_reserve_mem(void);
-void __init wm8505_reserve_mem(void);
-void __init vt8500_gpio_init(void);
-void __init vt8500_set_resources(void);
-void __init wm8505_set_resources(void);
-
-extern unsigned long wmt_ic_base __initdata;
-extern unsigned long wmt_sic_base __initdata;
-extern unsigned long wmt_gpio_base __initdata;
-extern unsigned long wmt_pmc_base __initdata;
-
-extern int wmt_nr_irqs __initdata;
-extern int wmt_timer_irq __initdata;
-extern int wmt_gpio_ext_irq[8] __initdata;
-
-extern struct map_desc wmt_io_desc[2] __initdata;
-
-static inline struct resource wmt_mmio_res(u32 start, u32 size)
-{
- struct resource tmp = {
- .flags = IORESOURCE_MEM,
- .start = start,
- .end = start + size - 1,
- };
-
- return tmp;
-}
-
-static inline struct resource wmt_irq_res(int irq)
-{
- struct resource tmp = {
- .flags = IORESOURCE_IRQ,
- .start = irq,
- .end = irq,
- };
-
- return tmp;
-}
-
-static inline void wmt_res_add(struct platform_device *pdev,
- const struct resource *res, unsigned int num)
-{
- if (unlikely(platform_device_add_resources(pdev, res, num)))
- pr_err("Failed to assign resources\n");
-}
-
-extern struct sys_timer vt8500_timer;
-
-extern struct platform_device vt8500_device_uart0;
-extern struct platform_device vt8500_device_uart1;
-extern struct platform_device vt8500_device_uart2;
-extern struct platform_device vt8500_device_uart3;
-extern struct platform_device vt8500_device_uart4;
-extern struct platform_device vt8500_device_uart5;
-
-extern struct platform_device vt8500_device_lcdc;
-extern struct platform_device vt8500_device_wm8505_fb;
-extern struct platform_device vt8500_device_ehci;
-extern struct platform_device vt8500_device_ge_rops;
-extern struct platform_device vt8500_device_pwm;
-extern struct platform_device vt8500_device_pwmbl;
-extern struct platform_device vt8500_device_rtc;
-#endif
diff --git a/arch/arm/mach-vt8500/gpio.c b/arch/arm/mach-vt8500/gpio.c
deleted file mode 100644
index 2bcc0ec783df..000000000000
--- a/arch/arm/mach-vt8500/gpio.c
+++ /dev/null
@@ -1,240 +0,0 @@
-/* linux/arch/arm/mach-vt8500/gpio.c
- *
- * Copyright (C) 2010 Alexey Charkov <alchark@gmail.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/gpio.h>
-#include <linux/init.h>
-#include <linux/irq.h>
-#include <linux/io.h>
-
-#include "devices.h"
-
-#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
-
-#define ENABLE_REGS 0x0
-#define DIRECTION_REGS 0x20
-#define OUTVALUE_REGS 0x40
-#define INVALUE_REGS 0x60
-
-#define EXT_REGOFF 0x1c
-
-static void __iomem *regbase;
-
-struct vt8500_gpio_chip {
- struct gpio_chip chip;
- unsigned int shift;
- unsigned int regoff;
-};
-
-static int gpio_to_irq_map[8];
-
-static int vt8500_muxed_gpio_request(struct gpio_chip *chip,
- unsigned offset)
-{
- struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
- unsigned val = readl(regbase + ENABLE_REGS + vt8500_chip->regoff);
-
- val |= (1 << vt8500_chip->shift << offset);
- writel(val, regbase + ENABLE_REGS + vt8500_chip->regoff);
-
- return 0;
-}
-
-static void vt8500_muxed_gpio_free(struct gpio_chip *chip,
- unsigned offset)
-{
- struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
- unsigned val = readl(regbase + ENABLE_REGS + vt8500_chip->regoff);
-
- val &= ~(1 << vt8500_chip->shift << offset);
- writel(val, regbase + ENABLE_REGS + vt8500_chip->regoff);
-}
-
-static int vt8500_muxed_gpio_direction_input(struct gpio_chip *chip,
- unsigned offset)
-{
- struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
- unsigned val = readl(regbase + DIRECTION_REGS + vt8500_chip->regoff);
-
- val &= ~(1 << vt8500_chip->shift << offset);
- writel(val, regbase + DIRECTION_REGS + vt8500_chip->regoff);
-
- return 0;
-}
-
-static int vt8500_muxed_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
- unsigned val = readl(regbase + DIRECTION_REGS + vt8500_chip->regoff);
-
- val |= (1 << vt8500_chip->shift << offset);
- writel(val, regbase + DIRECTION_REGS + vt8500_chip->regoff);
-
- if (value) {
- val = readl(regbase + OUTVALUE_REGS + vt8500_chip->regoff);
- val |= (1 << vt8500_chip->shift << offset);
- writel(val, regbase + OUTVALUE_REGS + vt8500_chip->regoff);
- }
- return 0;
-}
-
-static int vt8500_muxed_gpio_get_value(struct gpio_chip *chip,
- unsigned offset)
-{
- struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
-
- return (readl(regbase + INVALUE_REGS + vt8500_chip->regoff)
- >> vt8500_chip->shift >> offset) & 1;
-}
-
-static void vt8500_muxed_gpio_set_value(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
- unsigned val = readl(regbase + INVALUE_REGS + vt8500_chip->regoff);
-
- if (value)
- val |= (1 << vt8500_chip->shift << offset);
- else
- val &= ~(1 << vt8500_chip->shift << offset);
-
- writel(val, regbase + INVALUE_REGS + vt8500_chip->regoff);
-}
-
-#define VT8500_GPIO_BANK(__name, __shift, __off, __base, __num) \
-{ \
- .chip = { \
- .label = __name, \
- .request = vt8500_muxed_gpio_request, \
- .free = vt8500_muxed_gpio_free, \
- .direction_input = vt8500_muxed_gpio_direction_input, \
- .direction_output = vt8500_muxed_gpio_direction_output, \
- .get = vt8500_muxed_gpio_get_value, \
- .set = vt8500_muxed_gpio_set_value, \
- .can_sleep = 0, \
- .base = __base, \
- .ngpio = __num, \
- }, \
- .shift = __shift, \
- .regoff = __off, \
-}
-
-static struct vt8500_gpio_chip vt8500_muxed_gpios[] = {
- VT8500_GPIO_BANK("uart0", 0, 0x0, 8, 4),
- VT8500_GPIO_BANK("uart1", 4, 0x0, 12, 4),
- VT8500_GPIO_BANK("spi0", 8, 0x0, 16, 4),
- VT8500_GPIO_BANK("spi1", 12, 0x0, 20, 4),
- VT8500_GPIO_BANK("spi2", 16, 0x0, 24, 4),
- VT8500_GPIO_BANK("pwmout", 24, 0x0, 28, 2),
-
- VT8500_GPIO_BANK("sdmmc", 0, 0x4, 30, 11),
- VT8500_GPIO_BANK("ms", 16, 0x4, 41, 7),
- VT8500_GPIO_BANK("i2c0", 24, 0x4, 48, 2),
- VT8500_GPIO_BANK("i2c1", 26, 0x4, 50, 2),
-
- VT8500_GPIO_BANK("mii", 0, 0x8, 52, 20),
- VT8500_GPIO_BANK("see", 20, 0x8, 72, 4),
- VT8500_GPIO_BANK("ide", 24, 0x8, 76, 7),
-
- VT8500_GPIO_BANK("ccir", 0, 0xc, 83, 19),
-
- VT8500_GPIO_BANK("ts", 8, 0x10, 102, 11),
-
- VT8500_GPIO_BANK("lcd", 0, 0x14, 113, 23),
-};
-
-static int vt8500_gpio_direction_input(struct gpio_chip *chip,
- unsigned offset)
-{
- unsigned val = readl(regbase + DIRECTION_REGS + EXT_REGOFF);
-
- val &= ~(1 << offset);
- writel(val, regbase + DIRECTION_REGS + EXT_REGOFF);
- return 0;
-}
-
-static int vt8500_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- unsigned val = readl(regbase + DIRECTION_REGS + EXT_REGOFF);
-
- val |= (1 << offset);
- writel(val, regbase + DIRECTION_REGS + EXT_REGOFF);
-
- if (value) {
- val = readl(regbase + OUTVALUE_REGS + EXT_REGOFF);
- val |= (1 << offset);
- writel(val, regbase + OUTVALUE_REGS + EXT_REGOFF);
- }
- return 0;
-}
-
-static int vt8500_gpio_get_value(struct gpio_chip *chip,
- unsigned offset)
-{
- return (readl(regbase + INVALUE_REGS + EXT_REGOFF) >> offset) & 1;
-}
-
-static void vt8500_gpio_set_value(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- unsigned val = readl(regbase + OUTVALUE_REGS + EXT_REGOFF);
-
- if (value)
- val |= (1 << offset);
- else
- val &= ~(1 << offset);
-
- writel(val, regbase + OUTVALUE_REGS + EXT_REGOFF);
-}
-
-static int vt8500_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
-{
- if (offset > 7)
- return -EINVAL;
-
- return gpio_to_irq_map[offset];
-}
-
-static struct gpio_chip vt8500_external_gpios = {
- .label = "extgpio",
- .direction_input = vt8500_gpio_direction_input,
- .direction_output = vt8500_gpio_direction_output,
- .get = vt8500_gpio_get_value,
- .set = vt8500_gpio_set_value,
- .to_irq = vt8500_gpio_to_irq,
- .can_sleep = 0,
- .base = 0,
- .ngpio = 8,
-};
-
-void __init vt8500_gpio_init(void)
-{
- int i;
-
- for (i = 0; i < 8; i++)
- gpio_to_irq_map[i] = wmt_gpio_ext_irq[i];
-
- regbase = ioremap(wmt_gpio_base, SZ_64K);
- if (!regbase) {
- printk(KERN_ERR "Failed to map MMIO registers for GPIO\n");
- return;
- }
-
- gpiochip_add(&vt8500_external_gpios);
-
- for (i = 0; i < ARRAY_SIZE(vt8500_muxed_gpios); i++)
- gpiochip_add(&vt8500_muxed_gpios[i].chip);
-}
diff --git a/arch/arm/mach-vt8500/include/mach/restart.h b/arch/arm/mach-vt8500/include/mach/restart.h
index 89f9b787d2a0..738979518acb 100644
--- a/arch/arm/mach-vt8500/include/mach/restart.h
+++ b/arch/arm/mach-vt8500/include/mach/restart.h
@@ -13,5 +13,5 @@
*
*/
-void wmt_setup_restart(void);
-void wmt_restart(char mode, const char *cmd);
+void vt8500_setup_restart(void);
+void vt8500_restart(char mode, const char *cmd);
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h b/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
deleted file mode 100644
index ecfee9124711..000000000000
--- a/arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * arch/arm/mach-vt8500/include/mach/vt8500_irqs.h
- *
- * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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
- */
-
-/* VT8500 Interrupt Sources */
-
-#define IRQ_JPEGENC 0 /* JPEG Encoder */
-#define IRQ_JPEGDEC 1 /* JPEG Decoder */
- /* Reserved */
-#define IRQ_PATA 3 /* PATA Controller */
- /* Reserved */
-#define IRQ_DMA 5 /* DMA Controller */
-#define IRQ_EXT0 6 /* External Interrupt 0 */
-#define IRQ_EXT1 7 /* External Interrupt 1 */
-#define IRQ_GE 8 /* Graphic Engine */
-#define IRQ_GOV 9 /* Graphic Overlay Engine */
-#define IRQ_ETHER 10 /* Ethernet MAC */
-#define IRQ_MPEGTS 11 /* Transport Stream Interface */
-#define IRQ_LCDC 12 /* LCD Controller */
-#define IRQ_EXT2 13 /* External Interrupt 2 */
-#define IRQ_EXT3 14 /* External Interrupt 3 */
-#define IRQ_EXT4 15 /* External Interrupt 4 */
-#define IRQ_CIPHER 16 /* Cipher */
-#define IRQ_VPP 17 /* Video Post-Processor */
-#define IRQ_I2C1 18 /* I2C 1 */
-#define IRQ_I2C0 19 /* I2C 0 */
-#define IRQ_SDMMC 20 /* SD/MMC Controller */
-#define IRQ_SDMMC_DMA 21 /* SD/MMC Controller DMA */
-#define IRQ_PMC_WU 22 /* Power Management Controller Wakeup */
- /* Reserved */
-#define IRQ_SPI0 24 /* SPI 0 */
-#define IRQ_SPI1 25 /* SPI 1 */
-#define IRQ_SPI2 26 /* SPI 2 */
-#define IRQ_LCDDF 27 /* LCD Data Formatter */
-#define IRQ_NAND 28 /* NAND Flash Controller */
-#define IRQ_NAND_DMA 29 /* NAND Flash Controller DMA */
-#define IRQ_MS 30 /* MemoryStick Controller */
-#define IRQ_MS_DMA 31 /* MemoryStick Controller DMA */
-#define IRQ_UART0 32 /* UART 0 */
-#define IRQ_UART1 33 /* UART 1 */
-#define IRQ_I2S 34 /* I2S */
-#define IRQ_PCM 35 /* PCM */
-#define IRQ_PMCOS0 36 /* PMC OS Timer 0 */
-#define IRQ_PMCOS1 37 /* PMC OS Timer 1 */
-#define IRQ_PMCOS2 38 /* PMC OS Timer 2 */
-#define IRQ_PMCOS3 39 /* PMC OS Timer 3 */
-#define IRQ_VPU 40 /* Video Processing Unit */
-#define IRQ_VID 41 /* Video Digital Input Interface */
-#define IRQ_AC97 42 /* AC97 Interface */
-#define IRQ_EHCI 43 /* USB */
-#define IRQ_NOR 44 /* NOR Flash Controller */
-#define IRQ_PS2MOUSE 45 /* PS/2 Mouse */
-#define IRQ_PS2KBD 46 /* PS/2 Keyboard */
-#define IRQ_UART2 47 /* UART 2 */
-#define IRQ_RTC 48 /* RTC Interrupt */
-#define IRQ_RTCSM 49 /* RTC Second/Minute Update Interrupt */
-#define IRQ_UART3 50 /* UART 3 */
-#define IRQ_ADC 51 /* ADC */
-#define IRQ_EXT5 52 /* External Interrupt 5 */
-#define IRQ_EXT6 53 /* External Interrupt 6 */
-#define IRQ_EXT7 54 /* External Interrupt 7 */
-#define IRQ_CIR 55 /* CIR */
-#define IRQ_DMA0 56 /* DMA Channel 0 */
-#define IRQ_DMA1 57 /* DMA Channel 1 */
-#define IRQ_DMA2 58 /* DMA Channel 2 */
-#define IRQ_DMA3 59 /* DMA Channel 3 */
-#define IRQ_DMA4 60 /* DMA Channel 4 */
-#define IRQ_DMA5 61 /* DMA Channel 5 */
-#define IRQ_DMA6 62 /* DMA Channel 6 */
-#define IRQ_DMA7 63 /* DMA Channel 7 */
-
-#define VT8500_NR_IRQS 64
diff --git a/arch/arm/mach-vt8500/include/mach/vt8500_regs.h b/arch/arm/mach-vt8500/include/mach/vt8500_regs.h
deleted file mode 100644
index 29c63ecb2383..000000000000
--- a/arch/arm/mach-vt8500/include/mach/vt8500_regs.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * arch/arm/mach-vt8500/include/mach/vt8500_regs.h
- *
- * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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_ARM_ARCH_VT8500_REGS_H
-#define __ASM_ARM_ARCH_VT8500_REGS_H
-
-/* VT8500 Registers Map */
-
-#define VT8500_REGS_START_PHYS 0xd8000000 /* Start of MMIO registers */
-#define VT8500_REGS_START_VIRT 0xf8000000 /* Virtual mapping start */
-
-#define VT8500_DDR_BASE 0xd8000000 /* 1k DDR/DDR2 Memory
- Controller */
-#define VT8500_DMA_BASE 0xd8001000 /* 1k DMA Controller */
-#define VT8500_SFLASH_BASE 0xd8002000 /* 1k Serial Flash Memory
- Controller */
-#define VT8500_ETHER_BASE 0xd8004000 /* 1k Ethernet MAC 0 */
-#define VT8500_CIPHER_BASE 0xd8006000 /* 4k Cipher */
-#define VT8500_USB_BASE 0xd8007800 /* 2k USB OTG */
-# define VT8500_EHCI_BASE 0xd8007900 /* EHCI */
-# define VT8500_UHCI_BASE 0xd8007b01 /* UHCI */
-#define VT8500_PATA_BASE 0xd8008000 /* 512 PATA */
-#define VT8500_PS2_BASE 0xd8008800 /* 1k PS/2 */
-#define VT8500_NAND_BASE 0xd8009000 /* 1k NAND Controller */
-#define VT8500_NOR_BASE 0xd8009400 /* 1k NOR Controller */
-#define VT8500_SDMMC_BASE 0xd800a000 /* 1k SD/MMC Controller */
-#define VT8500_MS_BASE 0xd800b000 /* 1k MS/MSPRO Controller */
-#define VT8500_LCDC_BASE 0xd800e400 /* 1k LCD Controller */
-#define VT8500_VPU_BASE 0xd8050000 /* 256 VPU */
-#define VT8500_GOV_BASE 0xd8050300 /* 256 GOV */
-#define VT8500_GEGEA_BASE 0xd8050400 /* 768 GE/GE Alpha Mixing */
-#define VT8500_LCDF_BASE 0xd8050900 /* 256 LCD Formatter */
-#define VT8500_VID_BASE 0xd8050a00 /* 256 VID */
-#define VT8500_VPP_BASE 0xd8050b00 /* 256 VPP */
-#define VT8500_TSBK_BASE 0xd80f4000 /* 4k TSBK */
-#define VT8500_JPEGDEC_BASE 0xd80fe000 /* 4k JPEG Decoder */
-#define VT8500_JPEGENC_BASE 0xd80ff000 /* 4k JPEG Encoder */
-#define VT8500_RTC_BASE 0xd8100000 /* 64k RTC */
-#define VT8500_GPIO_BASE 0xd8110000 /* 64k GPIO Configuration */
-#define VT8500_SCC_BASE 0xd8120000 /* 64k System Configuration*/
-#define VT8500_PMC_BASE 0xd8130000 /* 64k PMC Configuration */
-#define VT8500_IC_BASE 0xd8140000 /* 64k Interrupt Controller*/
-#define VT8500_UART0_BASE 0xd8200000 /* 64k UART 0 */
-#define VT8500_UART2_BASE 0xd8210000 /* 64k UART 2 */
-#define VT8500_PWM_BASE 0xd8220000 /* 64k PWM Configuration */
-#define VT8500_SPI0_BASE 0xd8240000 /* 64k SPI 0 */
-#define VT8500_SPI1_BASE 0xd8250000 /* 64k SPI 1 */
-#define VT8500_CIR_BASE 0xd8270000 /* 64k CIR */
-#define VT8500_I2C0_BASE 0xd8280000 /* 64k I2C 0 */
-#define VT8500_AC97_BASE 0xd8290000 /* 64k AC97 */
-#define VT8500_SPI2_BASE 0xd82a0000 /* 64k SPI 2 */
-#define VT8500_UART1_BASE 0xd82b0000 /* 64k UART 1 */
-#define VT8500_UART3_BASE 0xd82c0000 /* 64k UART 3 */
-#define VT8500_PCM_BASE 0xd82d0000 /* 64k PCM */
-#define VT8500_I2C1_BASE 0xd8320000 /* 64k I2C 1 */
-#define VT8500_I2S_BASE 0xd8330000 /* 64k I2S */
-#define VT8500_ADC_BASE 0xd8340000 /* 64k ADC */
-
-#define VT8500_REGS_END_PHYS 0xd834ffff /* End of MMIO registers */
-#define VT8500_REGS_LENGTH (VT8500_REGS_END_PHYS \
- - VT8500_REGS_START_PHYS + 1)
-
-#endif
diff --git a/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h b/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
deleted file mode 100644
index 6128627ac753..000000000000
--- a/arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * arch/arm/mach-vt8500/include/mach/wm8505_irqs.h
- *
- * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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
- */
-
-/* WM8505 Interrupt Sources */
-
-#define IRQ_UHCI 0 /* UHC FS (UHCI?) */
-#define IRQ_EHCI 1 /* UHC HS */
-#define IRQ_UDCDMA 2 /* UDC DMA */
- /* Reserved */
-#define IRQ_PS2MOUSE 4 /* PS/2 Mouse */
-#define IRQ_UDC 5 /* UDC */
-#define IRQ_EXT0 6 /* External Interrupt 0 */
-#define IRQ_EXT1 7 /* External Interrupt 1 */
-#define IRQ_KEYPAD 8 /* Keypad */
-#define IRQ_DMA 9 /* DMA Controller */
-#define IRQ_ETHER 10 /* Ethernet MAC */
- /* Reserved */
- /* Reserved */
-#define IRQ_EXT2 13 /* External Interrupt 2 */
-#define IRQ_EXT3 14 /* External Interrupt 3 */
-#define IRQ_EXT4 15 /* External Interrupt 4 */
-#define IRQ_APB 16 /* APB Bridge */
-#define IRQ_DMA0 17 /* DMA Channel 0 */
-#define IRQ_I2C1 18 /* I2C 1 */
-#define IRQ_I2C0 19 /* I2C 0 */
-#define IRQ_SDMMC 20 /* SD/MMC Controller */
-#define IRQ_SDMMC_DMA 21 /* SD/MMC Controller DMA */
-#define IRQ_PMC_WU 22 /* Power Management Controller Wakeup */
-#define IRQ_PS2KBD 23 /* PS/2 Keyboard */
-#define IRQ_SPI0 24 /* SPI 0 */
-#define IRQ_SPI1 25 /* SPI 1 */
-#define IRQ_SPI2 26 /* SPI 2 */
-#define IRQ_DMA1 27 /* DMA Channel 1 */
-#define IRQ_NAND 28 /* NAND Flash Controller */
-#define IRQ_NAND_DMA 29 /* NAND Flash Controller DMA */
-#define IRQ_UART5 30 /* UART 5 */
-#define IRQ_UART4 31 /* UART 4 */
-#define IRQ_UART0 32 /* UART 0 */
-#define IRQ_UART1 33 /* UART 1 */
-#define IRQ_DMA2 34 /* DMA Channel 2 */
-#define IRQ_I2S 35 /* I2S */
-#define IRQ_PMCOS0 36 /* PMC OS Timer 0 */
-#define IRQ_PMCOS1 37 /* PMC OS Timer 1 */
-#define IRQ_PMCOS2 38 /* PMC OS Timer 2 */
-#define IRQ_PMCOS3 39 /* PMC OS Timer 3 */
-#define IRQ_DMA3 40 /* DMA Channel 3 */
-#define IRQ_DMA4 41 /* DMA Channel 4 */
-#define IRQ_AC97 42 /* AC97 Interface */
- /* Reserved */
-#define IRQ_NOR 44 /* NOR Flash Controller */
-#define IRQ_DMA5 45 /* DMA Channel 5 */
-#define IRQ_DMA6 46 /* DMA Channel 6 */
-#define IRQ_UART2 47 /* UART 2 */
-#define IRQ_RTC 48 /* RTC Interrupt */
-#define IRQ_RTCSM 49 /* RTC Second/Minute Update Interrupt */
-#define IRQ_UART3 50 /* UART 3 */
-#define IRQ_DMA7 51 /* DMA Channel 7 */
-#define IRQ_EXT5 52 /* External Interrupt 5 */
-#define IRQ_EXT6 53 /* External Interrupt 6 */
-#define IRQ_EXT7 54 /* External Interrupt 7 */
-#define IRQ_CIR 55 /* CIR */
-#define IRQ_SIC0 56 /* SIC IRQ0 */
-#define IRQ_SIC1 57 /* SIC IRQ1 */
-#define IRQ_SIC2 58 /* SIC IRQ2 */
-#define IRQ_SIC3 59 /* SIC IRQ3 */
-#define IRQ_SIC4 60 /* SIC IRQ4 */
-#define IRQ_SIC5 61 /* SIC IRQ5 */
-#define IRQ_SIC6 62 /* SIC IRQ6 */
-#define IRQ_SIC7 63 /* SIC IRQ7 */
- /* Reserved */
-#define IRQ_JPEGDEC 65 /* JPEG Decoder */
-#define IRQ_SAE 66 /* SAE (?) */
- /* Reserved */
-#define IRQ_VPU 79 /* Video Processing Unit */
-#define IRQ_VPP 80 /* Video Post-Processor */
-#define IRQ_VID 81 /* Video Digital Input Interface */
-#define IRQ_SPU 82 /* SPU (?) */
-#define IRQ_PIP 83 /* PIP Error */
-#define IRQ_GE 84 /* Graphic Engine */
-#define IRQ_GOV 85 /* Graphic Overlay Engine */
-#define IRQ_DVO 86 /* Digital Video Output */
- /* Reserved */
-#define IRQ_DMA8 92 /* DMA Channel 8 */
-#define IRQ_DMA9 93 /* DMA Channel 9 */
-#define IRQ_DMA10 94 /* DMA Channel 10 */
-#define IRQ_DMA11 95 /* DMA Channel 11 */
-#define IRQ_DMA12 96 /* DMA Channel 12 */
-#define IRQ_DMA13 97 /* DMA Channel 13 */
-#define IRQ_DMA14 98 /* DMA Channel 14 */
-#define IRQ_DMA15 99 /* DMA Channel 15 */
- /* Reserved */
-#define IRQ_GOVW 111 /* GOVW (?) */
-#define IRQ_GOVRSDSCD 112 /* GOVR SDSCD (?) */
-#define IRQ_GOVRSDMIF 113 /* GOVR SDMIF (?) */
-#define IRQ_GOVRHDSCD 114 /* GOVR HDSCD (?) */
-#define IRQ_GOVRHDMIF 115 /* GOVR HDMIF (?) */
-
-#define WM8505_NR_IRQS 116
diff --git a/arch/arm/mach-vt8500/include/mach/wm8505_regs.h b/arch/arm/mach-vt8500/include/mach/wm8505_regs.h
deleted file mode 100644
index df1550941efb..000000000000
--- a/arch/arm/mach-vt8500/include/mach/wm8505_regs.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * arch/arm/mach-vt8500/include/mach/wm8505_regs.h
- *
- * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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_ARM_ARCH_WM8505_REGS_H
-#define __ASM_ARM_ARCH_WM8505_REGS_H
-
-/* WM8505 Registers Map */
-
-#define WM8505_REGS_START_PHYS 0xd8000000 /* Start of MMIO registers */
-#define WM8505_REGS_START_VIRT 0xf8000000 /* Virtual mapping start */
-
-#define WM8505_DDR_BASE 0xd8000400 /* 1k DDR/DDR2 Memory
- Controller */
-#define WM8505_DMA_BASE 0xd8001800 /* 1k DMA Controller */
-#define WM8505_VDMA_BASE 0xd8001c00 /* 1k VDMA */
-#define WM8505_SFLASH_BASE 0xd8002000 /* 1k Serial Flash Memory
- Controller */
-#define WM8505_ETHER_BASE 0xd8004000 /* 1k Ethernet MAC 0 */
-#define WM8505_CIPHER_BASE 0xd8006000 /* 4k Cipher */
-#define WM8505_USB_BASE 0xd8007000 /* 2k USB 2.0 Host */
-# define WM8505_EHCI_BASE 0xd8007100 /* EHCI */
-# define WM8505_UHCI_BASE 0xd8007301 /* UHCI */
-#define WM8505_PS2_BASE 0xd8008800 /* 1k PS/2 */
-#define WM8505_NAND_BASE 0xd8009000 /* 1k NAND Controller */
-#define WM8505_NOR_BASE 0xd8009400 /* 1k NOR Controller */
-#define WM8505_SDMMC_BASE 0xd800a000 /* 1k SD/MMC Controller */
-#define WM8505_VPU_BASE 0xd8050000 /* 256 VPU */
-#define WM8505_GOV_BASE 0xd8050300 /* 256 GOV */
-#define WM8505_GEGEA_BASE 0xd8050400 /* 768 GE/GE Alpha Mixing */
-#define WM8505_GOVR_BASE 0xd8050800 /* 512 GOVR (frambuffer) */
-#define WM8505_VID_BASE 0xd8050a00 /* 256 VID */
-#define WM8505_SCL_BASE 0xd8050d00 /* 256 SCL */
-#define WM8505_VPP_BASE 0xd8050f00 /* 256 VPP */
-#define WM8505_JPEGDEC_BASE 0xd80fe000 /* 4k JPEG Decoder */
-#define WM8505_RTC_BASE 0xd8100000 /* 64k RTC */
-#define WM8505_GPIO_BASE 0xd8110000 /* 64k GPIO Configuration */
-#define WM8505_SCC_BASE 0xd8120000 /* 64k System Configuration*/
-#define WM8505_PMC_BASE 0xd8130000 /* 64k PMC Configuration */
-#define WM8505_IC_BASE 0xd8140000 /* 64k Interrupt Controller*/
-#define WM8505_SIC_BASE 0xd8150000 /* 64k Secondary IC */
-#define WM8505_UART0_BASE 0xd8200000 /* 64k UART 0 */
-#define WM8505_UART2_BASE 0xd8210000 /* 64k UART 2 */
-#define WM8505_PWM_BASE 0xd8220000 /* 64k PWM Configuration */
-#define WM8505_SPI0_BASE 0xd8240000 /* 64k SPI 0 */
-#define WM8505_SPI1_BASE 0xd8250000 /* 64k SPI 1 */
-#define WM8505_KEYPAD_BASE 0xd8260000 /* 64k Keypad control */
-#define WM8505_CIR_BASE 0xd8270000 /* 64k CIR */
-#define WM8505_I2C0_BASE 0xd8280000 /* 64k I2C 0 */
-#define WM8505_AC97_BASE 0xd8290000 /* 64k AC97 */
-#define WM8505_SPI2_BASE 0xd82a0000 /* 64k SPI 2 */
-#define WM8505_UART1_BASE 0xd82b0000 /* 64k UART 1 */
-#define WM8505_UART3_BASE 0xd82c0000 /* 64k UART 3 */
-#define WM8505_I2C1_BASE 0xd8320000 /* 64k I2C 1 */
-#define WM8505_I2S_BASE 0xd8330000 /* 64k I2S */
-#define WM8505_UART4_BASE 0xd8370000 /* 64k UART 4 */
-#define WM8505_UART5_BASE 0xd8380000 /* 64k UART 5 */
-
-#define WM8505_REGS_END_PHYS 0xd838ffff /* End of MMIO registers */
-#define WM8505_REGS_LENGTH (WM8505_REGS_END_PHYS \
- - WM8505_REGS_START_PHYS + 1)
-
-#endif
diff --git a/arch/arm/mach-vt8500/irq.c b/arch/arm/mach-vt8500/irq.c
index 642de0408f25..f8f9ab9bc56e 100644
--- a/arch/arm/mach-vt8500/irq.c
+++ b/arch/arm/mach-vt8500/irq.c
@@ -1,6 +1,7 @@
/*
* arch/arm/mach-vt8500/irq.c
*
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
* Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -18,81 +19,102 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+/*
+ * This file is copied and modified from the original irq.c provided by
+ * Alexey Charkov. Minor changes have been made for Device Tree Support.
+ */
+
+#include <linux/slab.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/interrupt.h>
+#include <linux/bitops.h>
+
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
#include <asm/irq.h>
-#include "devices.h"
-#define VT8500_IC_DCTR 0x40 /* Destination control
- register, 64*u8 */
-#define VT8500_INT_ENABLE (1 << 3)
-#define VT8500_TRIGGER_HIGH (0 << 4)
-#define VT8500_TRIGGER_RISING (1 << 4)
-#define VT8500_TRIGGER_FALLING (2 << 4)
+#define VT8500_ICPC_IRQ 0x20
+#define VT8500_ICPC_FIQ 0x24
+#define VT8500_ICDC 0x40 /* Destination Control 64*u32 */
+#define VT8500_ICIS 0x80 /* Interrupt status, 16*u32 */
+
+/* ICPC */
+#define ICPC_MASK 0x3F
+#define ICPC_ROTATE BIT(6)
+
+/* IC_DCTR */
+#define ICDC_IRQ 0x00
+#define ICDC_FIQ 0x01
+#define ICDC_DSS0 0x02
+#define ICDC_DSS1 0x03
+#define ICDC_DSS2 0x04
+#define ICDC_DSS3 0x05
+#define ICDC_DSS4 0x06
+#define ICDC_DSS5 0x07
+
+#define VT8500_INT_DISABLE 0
+#define VT8500_INT_ENABLE BIT(3)
+
+#define VT8500_TRIGGER_HIGH 0
+#define VT8500_TRIGGER_RISING BIT(5)
+#define VT8500_TRIGGER_FALLING BIT(6)
#define VT8500_EDGE ( VT8500_TRIGGER_RISING \
| VT8500_TRIGGER_FALLING)
-#define VT8500_IC_STATUS 0x80 /* Interrupt status, 2*u32 */
-static void __iomem *ic_regbase;
-static void __iomem *sic_regbase;
+static int irq_cnt;
+
+struct vt8500_irq_priv {
+ void __iomem *base;
+};
static void vt8500_irq_mask(struct irq_data *d)
{
- void __iomem *base = ic_regbase;
- unsigned irq = d->irq;
+ struct vt8500_irq_priv *priv =
+ (struct vt8500_irq_priv *)(d->domain->host_data);
+ void __iomem *base = priv->base;
u8 edge;
- if (irq >= 64) {
- base = sic_regbase;
- irq -= 64;
- }
- edge = readb(base + VT8500_IC_DCTR + irq) & VT8500_EDGE;
+ edge = readb(base + VT8500_ICDC + d->hwirq) & VT8500_EDGE;
if (edge) {
- void __iomem *stat_reg = base + VT8500_IC_STATUS
- + (irq < 32 ? 0 : 4);
+ void __iomem *stat_reg = base + VT8500_ICIS
+ + (d->hwirq < 32 ? 0 : 4);
unsigned status = readl(stat_reg);
- status |= (1 << (irq & 0x1f));
+ status |= (1 << (d->hwirq & 0x1f));
writel(status, stat_reg);
} else {
- u8 dctr = readb(base + VT8500_IC_DCTR + irq);
+ u8 dctr = readb(base + VT8500_ICDC + d->hwirq);
dctr &= ~VT8500_INT_ENABLE;
- writeb(dctr, base + VT8500_IC_DCTR + irq);
+ writeb(dctr, base + VT8500_ICDC + d->hwirq);
}
}
static void vt8500_irq_unmask(struct irq_data *d)
{
- void __iomem *base = ic_regbase;
- unsigned irq = d->irq;
+ struct vt8500_irq_priv *priv =
+ (struct vt8500_irq_priv *)(d->domain->host_data);
+ void __iomem *base = priv->base;
u8 dctr;
- if (irq >= 64) {
- base = sic_regbase;
- irq -= 64;
- }
- dctr = readb(base + VT8500_IC_DCTR + irq);
+ dctr = readb(base + VT8500_ICDC + d->hwirq);
dctr |= VT8500_INT_ENABLE;
- writeb(dctr, base + VT8500_IC_DCTR + irq);
+ writeb(dctr, base + VT8500_ICDC + d->hwirq);
}
static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type)
{
- void __iomem *base = ic_regbase;
- unsigned irq = d->irq;
- unsigned orig_irq = irq;
+ struct vt8500_irq_priv *priv =
+ (struct vt8500_irq_priv *)(d->domain->host_data);
+ void __iomem *base = priv->base;
u8 dctr;
- if (irq >= 64) {
- base = sic_regbase;
- irq -= 64;
- }
-
- dctr = readb(base + VT8500_IC_DCTR + irq);
+ dctr = readb(base + VT8500_ICDC + d->hwirq);
dctr &= ~VT8500_EDGE;
switch (flow_type) {
@@ -100,18 +122,18 @@ static int vt8500_irq_set_type(struct irq_data *d, unsigned int flow_type)
return -EINVAL;
case IRQF_TRIGGER_HIGH:
dctr |= VT8500_TRIGGER_HIGH;
- __irq_set_handler_locked(orig_irq, handle_level_irq);
+ __irq_set_handler_locked(d->irq, handle_level_irq);
break;
case IRQF_TRIGGER_FALLING:
dctr |= VT8500_TRIGGER_FALLING;
- __irq_set_handler_locked(orig_irq, handle_edge_irq);
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
break;
case IRQF_TRIGGER_RISING:
dctr |= VT8500_TRIGGER_RISING;
- __irq_set_handler_locked(orig_irq, handle_edge_irq);
+ __irq_set_handler_locked(d->irq, handle_edge_irq);
break;
}
- writeb(dctr, base + VT8500_IC_DCTR + irq);
+ writeb(dctr, base + VT8500_ICDC + d->hwirq);
return 0;
}
@@ -124,57 +146,76 @@ static struct irq_chip vt8500_irq_chip = {
.irq_set_type = vt8500_irq_set_type,
};
-void __init vt8500_init_irq(void)
+static void __init vt8500_init_irq_hw(void __iomem *base)
{
unsigned int i;
- ic_regbase = ioremap(wmt_ic_base, SZ_64K);
+ /* Enable rotating priority for IRQ */
+ writel(ICPC_ROTATE, base + VT8500_ICPC_IRQ);
+ writel(0x00, base + VT8500_ICPC_FIQ);
- if (ic_regbase) {
- /* Enable rotating priority for IRQ */
- writel((1 << 6), ic_regbase + 0x20);
- writel(0, ic_regbase + 0x24);
+ for (i = 0; i < 64; i++) {
+ /* Disable all interrupts and route them to IRQ */
+ writeb(VT8500_INT_DISABLE | ICDC_IRQ,
+ base + VT8500_ICDC + i);
+ }
+}
- for (i = 0; i < wmt_nr_irqs; i++) {
- /* Disable all interrupts and route them to IRQ */
- writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
+static int vt8500_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &vt8500_irq_chip, handle_level_irq);
+ set_irq_flags(virq, IRQF_VALID);
- irq_set_chip_and_handler(i, &vt8500_irq_chip,
- handle_level_irq);
- set_irq_flags(i, IRQF_VALID);
- }
- } else {
- printk(KERN_ERR "Unable to remap the Interrupt Controller registers, not enabling IRQs!\n");
- }
+ return 0;
}
-void __init wm8505_init_irq(void)
+static struct irq_domain_ops vt8500_irq_domain_ops = {
+ .map = vt8500_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+int __init vt8500_irq_init(struct device_node *node, struct device_node *parent)
{
- unsigned int i;
+ struct irq_domain *vt8500_irq_domain;
+ struct vt8500_irq_priv *priv;
+ int irq, i;
+ struct device_node *np = node;
+
+ priv = kzalloc(sizeof(struct vt8500_irq_priv), GFP_KERNEL);
+ priv->base = of_iomap(np, 0);
+
+ vt8500_irq_domain = irq_domain_add_legacy(node, 64, irq_cnt, 0,
+ &vt8500_irq_domain_ops, priv);
+ if (!vt8500_irq_domain)
+ pr_err("%s: Unable to add wmt irq domain!\n", __func__);
+
+ irq_set_default_host(vt8500_irq_domain);
+
+ vt8500_init_irq_hw(priv->base);
- ic_regbase = ioremap(wmt_ic_base, SZ_64K);
- sic_regbase = ioremap(wmt_sic_base, SZ_64K);
-
- if (ic_regbase && sic_regbase) {
- /* Enable rotating priority for IRQ */
- writel((1 << 6), ic_regbase + 0x20);
- writel(0, ic_regbase + 0x24);
- writel((1 << 6), sic_regbase + 0x20);
- writel(0, sic_regbase + 0x24);
-
- for (i = 0; i < wmt_nr_irqs; i++) {
- /* Disable all interrupts and route them to IRQ */
- if (i < 64)
- writeb(0x00, ic_regbase + VT8500_IC_DCTR + i);
- else
- writeb(0x00, sic_regbase + VT8500_IC_DCTR
- + i - 64);
-
- irq_set_chip_and_handler(i, &vt8500_irq_chip,
- handle_level_irq);
- set_irq_flags(i, IRQF_VALID);
+ pr_info("Added IRQ Controller @ %x [virq_base = %d]\n",
+ (u32)(priv->base), irq_cnt);
+
+ /* check if this is a slaved controller */
+ if (of_irq_count(np) != 0) {
+ /* check that we have the correct number of interrupts */
+ if (of_irq_count(np) != 8) {
+ pr_err("%s: Incorrect IRQ map for slave controller\n",
+ __func__);
+ return -EINVAL;
}
- } else {
- printk(KERN_ERR "Unable to remap the Interrupt Controller registers, not enabling IRQs!\n");
+
+ for (i = 0; i < 8; i++) {
+ irq = irq_of_parse_and_map(np, i);
+ enable_irq(irq);
+ }
+
+ pr_info("vt8500-irq: Enabled slave->parent interrupts\n");
}
+
+ irq_cnt += 64;
+
+ return 0;
}
+
diff --git a/arch/arm/mach-vt8500/restart.c b/arch/arm/mach-vt8500/restart.c
deleted file mode 100644
index 497e89a5e130..000000000000
--- a/arch/arm/mach-vt8500/restart.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/* linux/arch/arm/mach-vt8500/restart.c
- *
- * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
- *
- * 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 <asm/io.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-
-#define LEGACY_PMC_BASE 0xD8130000
-#define WMT_PRIZM_PMSR_REG 0x60
-
-static void __iomem *pmc_base;
-
-void wmt_setup_restart(void)
-{
- struct device_node *np;
-
- /*
- * Check if Power Mgmt Controller node is present in device tree. If no
- * device tree node, use the legacy PMSR value (valid for all current
- * SoCs).
- */
- np = of_find_compatible_node(NULL, NULL, "wmt,prizm-pmc");
- if (np) {
- pmc_base = of_iomap(np, 0);
-
- if (!pmc_base)
- pr_err("%s:of_iomap(pmc) failed\n", __func__);
-
- of_node_put(np);
- } else {
- pmc_base = ioremap(LEGACY_PMC_BASE, 0x1000);
- if (!pmc_base) {
- pr_err("%s:ioremap(rstc) failed\n", __func__);
- return;
- }
- }
-}
-
-void wmt_restart(char mode, const char *cmd)
-{
- if (pmc_base)
- writel(1, pmc_base + WMT_PRIZM_PMSR_REG);
-}
diff --git a/arch/arm/mach-vt8500/timer.c b/arch/arm/mach-vt8500/timer.c
index d5376c592ab6..050e1833f2d0 100644
--- a/arch/arm/mach-vt8500/timer.c
+++ b/arch/arm/mach-vt8500/timer.c
@@ -1,6 +1,7 @@
/*
- * arch/arm/mach-vt8500/timer.c
+ * arch/arm/mach-vt8500/timer_dt.c
*
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
* Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -18,18 +19,25 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+/*
+ * This file is copied and modified from the original timer.c provided by
+ * Alexey Charkov. Minor changes have been made for Device Tree Support.
+ */
+
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/clocksource.h>
#include <linux/clockchips.h>
#include <linux/delay.h>
-
#include <asm/mach/time.h>
-#include "devices.h"
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
#define VT8500_TIMER_OFFSET 0x0100
+#define VT8500_TIMER_HZ 3000000
#define TIMER_MATCH_VAL 0x0000
#define TIMER_COUNT_VAL 0x0010
#define TIMER_STATUS_VAL 0x0014
@@ -39,7 +47,6 @@
#define TIMER_COUNT_R_ACTIVE (1 << 5) /* not ready for read */
#define TIMER_COUNT_W_ACTIVE (1 << 4) /* not ready for write */
#define TIMER_MATCH_W_ACTIVE (1 << 0) /* not ready for write */
-#define VT8500_TIMER_HZ 3000000
#define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
@@ -55,7 +62,7 @@ static cycle_t vt8500_timer_read(struct clocksource *cs)
return readl(regbase + TIMER_COUNT_VAL);
}
-struct clocksource clocksource = {
+static struct clocksource clocksource = {
.name = "vt8500_timer",
.rating = 200,
.read = vt8500_timer_read,
@@ -98,7 +105,7 @@ static void vt8500_timer_set_mode(enum clock_event_mode mode,
}
}
-struct clock_event_device clockevent = {
+static struct clock_event_device clockevent = {
.name = "vt8500_timer",
.features = CLOCK_EVT_FEAT_ONESHOT,
.rating = 200,
@@ -115,26 +122,51 @@ static irqreturn_t vt8500_timer_interrupt(int irq, void *dev_id)
return IRQ_HANDLED;
}
-struct irqaction irq = {
+static struct irqaction irq = {
.name = "vt8500_timer",
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
.handler = vt8500_timer_interrupt,
.dev_id = &clockevent,
};
-static void __init vt8500_timer_init(void)
+static struct of_device_id vt8500_timer_ids[] = {
+ { .compatible = "via,vt8500-timer" },
+ { }
+};
+
+void __init vt8500_timer_init(void)
{
- regbase = ioremap(wmt_pmc_base + VT8500_TIMER_OFFSET, 0x28);
- if (!regbase)
- printk(KERN_ERR "vt8500_timer_init: failed to map MMIO registers\n");
+ struct device_node *np;
+ int timer_irq;
+
+ np = of_find_matching_node(NULL, vt8500_timer_ids);
+ if (!np) {
+ pr_err("%s: Timer description missing from Device Tree\n",
+ __func__);
+ return;
+ }
+ regbase = of_iomap(np, 0);
+ if (!regbase) {
+ pr_err("%s: Missing iobase description in Device Tree\n",
+ __func__);
+ of_node_put(np);
+ return;
+ }
+ timer_irq = irq_of_parse_and_map(np, 0);
+ if (!timer_irq) {
+ pr_err("%s: Missing irq description in Device Tree\n",
+ __func__);
+ of_node_put(np);
+ return;
+ }
writel(1, regbase + TIMER_CTRL_VAL);
writel(0xf, regbase + TIMER_STATUS_VAL);
writel(~0, regbase + TIMER_MATCH_VAL);
if (clocksource_register_hz(&clocksource, VT8500_TIMER_HZ))
- printk(KERN_ERR "vt8500_timer_init: clocksource_register failed for %s\n",
- clocksource.name);
+ pr_err("%s: vt8500_timer_init: clocksource_register failed for %s\n",
+ __func__, clocksource.name);
clockevents_calc_mult_shift(&clockevent, VT8500_TIMER_HZ, 4);
@@ -144,12 +176,9 @@ static void __init vt8500_timer_init(void)
clockevent.min_delta_ns = clockevent_delta2ns(4, &clockevent);
clockevent.cpumask = cpumask_of(0);
- if (setup_irq(wmt_timer_irq, &irq))
- printk(KERN_ERR "vt8500_timer_init: setup_irq failed for %s\n",
- clockevent.name);
+ if (setup_irq(timer_irq, &irq))
+ pr_err("%s: setup_irq failed for %s\n", __func__,
+ clockevent.name);
clockevents_register_device(&clockevent);
}
-struct sys_timer vt8500_timer = {
- .init = vt8500_timer_init
-};
diff --git a/arch/arm/mach-vt8500/vt8500.c b/arch/arm/mach-vt8500/vt8500.c
new file mode 100644
index 000000000000..587ea950d08b
--- /dev/null
+++ b/arch/arm/mach-vt8500/vt8500.c
@@ -0,0 +1,195 @@
+/*
+ * arch/arm/mach-vt8500/vt8500.c
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This program is free software; 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/io.h>
+#include <linux/pm.h>
+
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/time.h>
+#include <asm/mach/map.h>
+
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#include <mach/restart.h>
+
+#include "common.h"
+
+#define LEGACY_GPIO_BASE 0xD8110000
+#define LEGACY_PMC_BASE 0xD8130000
+
+/* Registers in GPIO Controller */
+#define VT8500_GPIO_MUX_REG 0x200
+
+/* Registers in Power Management Controller */
+#define VT8500_HCR_REG 0x12
+#define VT8500_PMSR_REG 0x60
+
+static void __iomem *pmc_base;
+
+void vt8500_restart(char mode, const char *cmd)
+{
+ if (pmc_base)
+ writel(1, pmc_base + VT8500_PMSR_REG);
+}
+
+static struct map_desc vt8500_io_desc[] __initdata = {
+ /* SoC MMIO registers */
+ [0] = {
+ .virtual = 0xf8000000,
+ .pfn = __phys_to_pfn(0xd8000000),
+ .length = 0x00390000, /* max of all chip variants */
+ .type = MT_DEVICE
+ },
+};
+
+void __init vt8500_map_io(void)
+{
+ iotable_init(vt8500_io_desc, ARRAY_SIZE(vt8500_io_desc));
+}
+
+static void vt8500_power_off(void)
+{
+ local_irq_disable();
+ writew(5, pmc_base + VT8500_HCR_REG);
+ asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
+}
+
+void __init vt8500_init(void)
+{
+ struct device_node *np, *fb;
+ void __iomem *gpio_base;
+
+#ifdef CONFIG_FB_VT8500
+ fb = of_find_compatible_node(NULL, NULL, "via,vt8500-fb");
+ if (fb) {
+ np = of_find_compatible_node(NULL, NULL, "via,vt8500-gpio");
+ if (np) {
+ gpio_base = of_iomap(np, 0);
+
+ if (!gpio_base)
+ pr_err("%s: of_iomap(gpio_mux) failed\n",
+ __func__);
+
+ of_node_put(np);
+ } else {
+ gpio_base = ioremap(LEGACY_GPIO_BASE, 0x1000);
+ if (!gpio_base)
+ pr_err("%s: ioremap(legacy_gpio_mux) failed\n",
+ __func__);
+ }
+ if (gpio_base) {
+ writel(readl(gpio_base + VT8500_GPIO_MUX_REG) | 1,
+ gpio_base + VT8500_GPIO_MUX_REG);
+ iounmap(gpio_base);
+ } else
+ pr_err("%s: Could not remap GPIO mux\n", __func__);
+
+ of_node_put(fb);
+ }
+#endif
+
+#ifdef CONFIG_FB_WM8505
+ fb = of_find_compatible_node(NULL, NULL, "wm,wm8505-fb");
+ if (fb) {
+ np = of_find_compatible_node(NULL, NULL, "wm,wm8505-gpio");
+ if (!np)
+ np = of_find_compatible_node(NULL, NULL,
+ "wm,wm8650-gpio");
+ if (np) {
+ gpio_base = of_iomap(np, 0);
+
+ if (!gpio_base)
+ pr_err("%s: of_iomap(gpio_mux) failed\n",
+ __func__);
+
+ of_node_put(np);
+ } else {
+ gpio_base = ioremap(LEGACY_GPIO_BASE, 0x1000);
+ if (!gpio_base)
+ pr_err("%s: ioremap(legacy_gpio_mux) failed\n",
+ __func__);
+ }
+ if (gpio_base) {
+ writel(readl(gpio_base + VT8500_GPIO_MUX_REG) |
+ 0x80000000, gpio_base + VT8500_GPIO_MUX_REG);
+ iounmap(gpio_base);
+ } else
+ pr_err("%s: Could not remap GPIO mux\n", __func__);
+
+ of_node_put(fb);
+ }
+#endif
+
+ np = of_find_compatible_node(NULL, NULL, "via,vt8500-pmc");
+ if (np) {
+ pmc_base = of_iomap(np, 0);
+
+ if (!pmc_base)
+ pr_err("%s:of_iomap(pmc) failed\n", __func__);
+
+ of_node_put(np);
+ } else {
+ pmc_base = ioremap(LEGACY_PMC_BASE, 0x1000);
+ if (!pmc_base)
+ pr_err("%s:ioremap(power_off) failed\n", __func__);
+ }
+ if (pmc_base)
+ pm_power_off = &vt8500_power_off;
+ else
+ pr_err("%s: PMC Hibernation register could not be remapped, not enabling power off!\n", __func__);
+
+ vtwm_clk_init(pmc_base);
+
+ of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const struct of_device_id vt8500_irq_match[] __initconst = {
+ { .compatible = "via,vt8500-intc", .data = vt8500_irq_init, },
+ { /* sentinel */ },
+};
+
+static void __init vt8500_init_irq(void)
+{
+ of_irq_init(vt8500_irq_match);
+};
+
+static struct sys_timer vt8500_timer = {
+ .init = vt8500_timer_init,
+};
+
+static const char * const vt8500_dt_compat[] = {
+ "via,vt8500",
+ "wm,wm8650",
+ "wm,wm8505",
+};
+
+DT_MACHINE_START(WMT_DT, "VIA/Wondermedia SoC (Device Tree Support)")
+ .dt_compat = vt8500_dt_compat,
+ .map_io = vt8500_map_io,
+ .init_irq = vt8500_init_irq,
+ .timer = &vt8500_timer,
+ .init_machine = vt8500_init,
+ .restart = vt8500_restart,
+MACHINE_END
+
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
deleted file mode 100644
index db19886caf7c..000000000000
--- a/arch/arm/mach-vt8500/wm8505_7in.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * arch/arm/mach-vt8500/wm8505_7in.c
- *
- * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * 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/io.h>
-#include <linux/pm.h>
-
-#include <asm/mach-types.h>
-#include <asm/mach/arch.h>
-#include <mach/restart.h>
-
-#include "devices.h"
-
-static void __iomem *pmc_hiber;
-
-static struct platform_device *devices[] __initdata = {
- &vt8500_device_uart0,
- &vt8500_device_ehci,
- &vt8500_device_wm8505_fb,
- &vt8500_device_ge_rops,
- &vt8500_device_pwm,
- &vt8500_device_pwmbl,
- &vt8500_device_rtc,
-};
-
-static void vt8500_power_off(void)
-{
- local_irq_disable();
- writew(5, pmc_hiber);
- asm("mcr%? p15, 0, %0, c7, c0, 4" : : "r" (0));
-}
-
-void __init wm8505_7in_init(void)
-{
-#ifdef CONFIG_FB_WM8505
- void __iomem *gpio_mux_reg = ioremap(wmt_gpio_base + 0x200, 4);
- if (gpio_mux_reg) {
- writel(readl(gpio_mux_reg) | 0x80000000, gpio_mux_reg);
- iounmap(gpio_mux_reg);
- } else {
- printk(KERN_ERR "Could not remap the GPIO mux register, display may not work properly!\n");
- }
-#endif
- pmc_hiber = ioremap(wmt_pmc_base + 0x12, 2);
- if (pmc_hiber)
- pm_power_off = &vt8500_power_off;
- else
- printk(KERN_ERR "PMC Hibernation register could not be remapped, not enabling power off!\n");
- wmt_setup_restart();
- wm8505_set_resources();
- platform_add_devices(devices, ARRAY_SIZE(devices));
- vt8500_gpio_init();
-}
-
-MACHINE_START(WM8505_7IN_NETBOOK, "WM8505 7-inch generic netbook")
- .atag_offset = 0x100,
- .restart = wmt_restart,
- .reserve = wm8505_reserve_mem,
- .map_io = wm8505_map_io,
- .init_irq = wm8505_init_irq,
- .timer = &vt8500_timer,
- .init_machine = wm8505_7in_init,
-MACHINE_END
diff --git a/arch/arm/mm/cache-l2x0.c b/arch/arm/mm/cache-l2x0.c
index 2a8e380501e8..577baf7d0a8d 100644
--- a/arch/arm/mm/cache-l2x0.c
+++ b/arch/arm/mm/cache-l2x0.c
@@ -554,7 +554,7 @@ static const struct of_device_id l2x0_ids[] __initconst = {
int __init l2x0_of_init(u32 aux_val, u32 aux_mask)
{
struct device_node *np;
- struct l2x0_of_data *data;
+ const struct l2x0_of_data *data;
struct resource res;
np = of_find_matching_node(NULL, l2x0_ids);
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index e59c4ab71bcb..13f555d62491 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -346,6 +346,8 @@ static int __init atomic_pool_init(void)
(unsigned)pool->size / 1024);
return 0;
}
+
+ kfree(pages);
no_pages:
kfree(bitmap);
no_bitmap:
diff --git a/arch/arm/plat-mxc/include/mach/mx25.h b/arch/arm/plat-mxc/include/mach/mx25.h
index 627d94f1b010..ec466400a200 100644
--- a/arch/arm/plat-mxc/include/mach/mx25.h
+++ b/arch/arm/plat-mxc/include/mach/mx25.h
@@ -98,6 +98,7 @@
#define MX25_INT_UART1 (NR_IRQS_LEGACY + 45)
#define MX25_INT_GPIO2 (NR_IRQS_LEGACY + 51)
#define MX25_INT_GPIO1 (NR_IRQS_LEGACY + 52)
+#define MX25_INT_GPT1 (NR_IRQS_LEGACY + 54)
#define MX25_INT_FEC (NR_IRQS_LEGACY + 57)
#define MX25_DMA_REQ_SSI2_RX1 22
diff --git a/arch/arm/plat-mxc/include/mach/mx31.h b/arch/arm/plat-mxc/include/mach/mx31.h
index dbced61d9fda..ee9b1f9215df 100644
--- a/arch/arm/plat-mxc/include/mach/mx31.h
+++ b/arch/arm/plat-mxc/include/mach/mx31.h
@@ -76,7 +76,7 @@
#define MX31_RTIC_BASE_ADDR (MX31_AIPS2_BASE_ADDR + 0xec000)
#define MX31_ROMP_BASE_ADDR 0x60000000
-#define MX31_ROMP_BASE_ADDR_VIRT 0xfc500000
+#define MX31_ROMP_BASE_ADDR_VIRT IOMEM(0xfc500000)
#define MX31_ROMP_SIZE SZ_1M
#define MX31_AVIC_BASE_ADDR 0x68000000
@@ -92,11 +92,11 @@
#define MX31_CS3_BASE_ADDR 0xb2000000
#define MX31_CS4_BASE_ADDR 0xb4000000
-#define MX31_CS4_BASE_ADDR_VIRT 0xf6000000
+#define MX31_CS4_BASE_ADDR_VIRT IOMEM(0xf6000000)
#define MX31_CS4_SIZE SZ_32M
#define MX31_CS5_BASE_ADDR 0xb6000000
-#define MX31_CS5_BASE_ADDR_VIRT 0xf8000000
+#define MX31_CS5_BASE_ADDR_VIRT IOMEM(0xf8000000)
#define MX31_CS5_SIZE SZ_32M
#define MX31_X_MEMC_BASE_ADDR 0xb8000000
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index d15a4a6d6146..ca83a7659aef 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -42,9 +42,8 @@ config OMAP_DEBUG_DEVICES
For debug cards on TI reference boards.
config OMAP_DEBUG_LEDS
- bool
+ def_bool y if NEW_LEDS
depends on OMAP_DEBUG_DEVICES
- default y if LEDS_CLASS
config POWER_AVS_OMAP
bool "AVS(Adaptive Voltage Scaling) support for OMAP IP versions 1&2"
diff --git a/arch/arm/plat-omap/debug-leds.c b/arch/arm/plat-omap/debug-leds.c
index 195aaae65872..ea29bbe8e5cf 100644
--- a/arch/arm/plat-omap/debug-leds.c
+++ b/arch/arm/plat-omap/debug-leds.c
@@ -1,280 +1,119 @@
/*
* linux/arch/arm/plat-omap/debug-leds.c
*
+ * Copyright 2011 by Bryan Wu <bryan.wu@canonical.com>
* Copyright 2003 by Texas Instruments Incorporated
*
* This 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/gpio.h>
+
+#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/leds.h>
#include <linux/io.h>
#include <linux/platform_data/gpio-omap.h>
+#include <linux/slab.h>
#include <mach/hardware.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include <plat/fpga.h>
-
/* Many OMAP development platforms reuse the same "debug board"; these
* platforms include H2, H3, H4, and Perseus2. There are 16 LEDs on the
* debug board (all green), accessed through FPGA registers.
- *
- * The "surfer" expansion board and H2 sample board also have two-color
- * green+red LEDs (in parallel), used here for timer and idle indicators
- * in preference to the ones on the debug board, for a "Disco LED" effect.
- *
- * This driver exports either the original ARM LED API, the new generic
- * one, or both.
- */
-
-static spinlock_t lock;
-static struct h2p2_dbg_fpga __iomem *fpga;
-static u16 led_state, hw_led_state;
-
-
-#ifdef CONFIG_OMAP_DEBUG_LEDS
-#define new_led_api() 1
-#else
-#define new_led_api() 0
-#endif
-
-
-/*-------------------------------------------------------------------------*/
-
-/* original ARM debug LED API:
- * - timer and idle leds (some boards use non-FPGA leds here);
- * - up to 4 generic leds, easily accessed in-kernel (any context)
*/
-#define GPIO_LED_RED 3
-#define GPIO_LED_GREEN OMAP_MPUIO(4)
-
-#define LED_STATE_ENABLED 0x01
-#define LED_STATE_CLAIMED 0x02
-#define LED_TIMER_ON 0x04
-
-#define GPIO_IDLE GPIO_LED_GREEN
-#define GPIO_TIMER GPIO_LED_RED
-
-static void h2p2_dbg_leds_event(led_event_t evt)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&lock, flags);
-
- if (!(led_state & LED_STATE_ENABLED) && evt != led_start)
- goto done;
-
- switch (evt) {
- case led_start:
- if (fpga)
- led_state |= LED_STATE_ENABLED;
- break;
-
- case led_stop:
- case led_halted:
- /* all leds off during suspend or shutdown */
-
- if (!(machine_is_omap_perseus2() || machine_is_omap_h4())) {
- gpio_set_value(GPIO_TIMER, 0);
- gpio_set_value(GPIO_IDLE, 0);
- }
-
- __raw_writew(~0, &fpga->leds);
- led_state &= ~LED_STATE_ENABLED;
- goto done;
-
- case led_claim:
- led_state |= LED_STATE_CLAIMED;
- hw_led_state = 0;
- break;
-
- case led_release:
- led_state &= ~LED_STATE_CLAIMED;
- break;
-
-#ifdef CONFIG_LEDS_TIMER
- case led_timer:
- led_state ^= LED_TIMER_ON;
-
- if (machine_is_omap_perseus2() || machine_is_omap_h4())
- hw_led_state ^= H2P2_DBG_FPGA_P2_LED_TIMER;
- else {
- gpio_set_value(GPIO_TIMER,
- led_state & LED_TIMER_ON);
- goto done;
- }
-
- break;
-#endif
-
-#ifdef CONFIG_LEDS_CPU
- /* LED lit iff busy */
- case led_idle_start:
- if (machine_is_omap_perseus2() || machine_is_omap_h4())
- hw_led_state &= ~H2P2_DBG_FPGA_P2_LED_IDLE;
- else {
- gpio_set_value(GPIO_IDLE, 1);
- goto done;
- }
-
- break;
+static struct h2p2_dbg_fpga __iomem *fpga;
- case led_idle_end:
- if (machine_is_omap_perseus2() || machine_is_omap_h4())
- hw_led_state |= H2P2_DBG_FPGA_P2_LED_IDLE;
- else {
- gpio_set_value(GPIO_IDLE, 0);
- goto done;
- }
-
- break;
-#endif
-
- case led_green_on:
- hw_led_state |= H2P2_DBG_FPGA_LED_GREEN;
- break;
- case led_green_off:
- hw_led_state &= ~H2P2_DBG_FPGA_LED_GREEN;
- break;
-
- case led_amber_on:
- hw_led_state |= H2P2_DBG_FPGA_LED_AMBER;
- break;
- case led_amber_off:
- hw_led_state &= ~H2P2_DBG_FPGA_LED_AMBER;
- break;
-
- case led_red_on:
- hw_led_state |= H2P2_DBG_FPGA_LED_RED;
- break;
- case led_red_off:
- hw_led_state &= ~H2P2_DBG_FPGA_LED_RED;
- break;
-
- case led_blue_on:
- hw_led_state |= H2P2_DBG_FPGA_LED_BLUE;
- break;
- case led_blue_off:
- hw_led_state &= ~H2P2_DBG_FPGA_LED_BLUE;
- break;
-
- default:
- break;
- }
-
-
- /*
- * Actually burn the LEDs
- */
- if (led_state & LED_STATE_ENABLED)
- __raw_writew(~hw_led_state, &fpga->leds);
-
-done:
- spin_unlock_irqrestore(&lock, flags);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* "new" LED API
- * - with syfs access and generic triggering
- * - not readily accessible to in-kernel drivers
- */
+static u16 fpga_led_state;
struct dbg_led {
struct led_classdev cdev;
u16 mask;
};
-static struct dbg_led dbg_leds[] = {
- /* REVISIT at least H2 uses different timer & cpu leds... */
-#ifndef CONFIG_LEDS_TIMER
- { .mask = 1 << 0, .cdev.name = "d4:green",
- .cdev.default_trigger = "heartbeat", },
-#endif
-#ifndef CONFIG_LEDS_CPU
- { .mask = 1 << 1, .cdev.name = "d5:green", }, /* !idle */
-#endif
- { .mask = 1 << 2, .cdev.name = "d6:green", },
- { .mask = 1 << 3, .cdev.name = "d7:green", },
-
- { .mask = 1 << 4, .cdev.name = "d8:green", },
- { .mask = 1 << 5, .cdev.name = "d9:green", },
- { .mask = 1 << 6, .cdev.name = "d10:green", },
- { .mask = 1 << 7, .cdev.name = "d11:green", },
-
- { .mask = 1 << 8, .cdev.name = "d12:green", },
- { .mask = 1 << 9, .cdev.name = "d13:green", },
- { .mask = 1 << 10, .cdev.name = "d14:green", },
- { .mask = 1 << 11, .cdev.name = "d15:green", },
-
-#ifndef CONFIG_LEDS
- { .mask = 1 << 12, .cdev.name = "d16:green", },
- { .mask = 1 << 13, .cdev.name = "d17:green", },
- { .mask = 1 << 14, .cdev.name = "d18:green", },
- { .mask = 1 << 15, .cdev.name = "d19:green", },
-#endif
+static const struct {
+ const char *name;
+ const char *trigger;
+} dbg_leds[] = {
+ { "dbg:d4", "heartbeat", },
+ { "dbg:d5", "cpu0", },
+ { "dbg:d6", "default-on", },
+ { "dbg:d7", },
+ { "dbg:d8", },
+ { "dbg:d9", },
+ { "dbg:d10", },
+ { "dbg:d11", },
+ { "dbg:d12", },
+ { "dbg:d13", },
+ { "dbg:d14", },
+ { "dbg:d15", },
+ { "dbg:d16", },
+ { "dbg:d17", },
+ { "dbg:d18", },
+ { "dbg:d19", },
};
-static void
-fpga_led_set(struct led_classdev *cdev, enum led_brightness value)
+/*
+ * The triggers lines up below will only be used if the
+ * LED triggers are compiled in.
+ */
+static void dbg_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
{
- struct dbg_led *led = container_of(cdev, struct dbg_led, cdev);
- unsigned long flags;
+ struct dbg_led *led = container_of(cdev, struct dbg_led, cdev);
+ u16 reg;
- spin_lock_irqsave(&lock, flags);
- if (value == LED_OFF)
- hw_led_state &= ~led->mask;
+ reg = __raw_readw(&fpga->leds);
+ if (b != LED_OFF)
+ reg |= led->mask;
else
- hw_led_state |= led->mask;
- __raw_writew(~hw_led_state, &fpga->leds);
- spin_unlock_irqrestore(&lock, flags);
+ reg &= ~led->mask;
+ __raw_writew(reg, &fpga->leds);
}
-static void __init newled_init(struct device *dev)
+static enum led_brightness dbg_led_get(struct led_classdev *cdev)
{
- unsigned i;
- struct dbg_led *led;
- int status;
+ struct dbg_led *led = container_of(cdev, struct dbg_led, cdev);
+ u16 reg;
- for (i = 0, led = dbg_leds; i < ARRAY_SIZE(dbg_leds); i++, led++) {
- led->cdev.brightness_set = fpga_led_set;
- status = led_classdev_register(dev, &led->cdev);
- if (status < 0)
- break;
- }
- return;
+ reg = __raw_readw(&fpga->leds);
+ return (reg & led->mask) ? LED_FULL : LED_OFF;
}
-
-/*-------------------------------------------------------------------------*/
-
-static int /* __init */ fpga_probe(struct platform_device *pdev)
+static int fpga_probe(struct platform_device *pdev)
{
struct resource *iomem;
-
- spin_lock_init(&lock);
+ int i;
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!iomem)
return -ENODEV;
fpga = ioremap(iomem->start, H2P2_DBG_FPGA_SIZE);
- __raw_writew(~0, &fpga->leds);
+ __raw_writew(0xff, &fpga->leds);
+
+ for (i = 0; i < ARRAY_SIZE(dbg_leds); i++) {
+ struct dbg_led *led;
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ break;
-#ifdef CONFIG_LEDS
- leds_event = h2p2_dbg_leds_event;
- leds_event(led_start);
-#endif
+ led->cdev.name = dbg_leds[i].name;
+ led->cdev.brightness_set = dbg_led_set;
+ led->cdev.brightness_get = dbg_led_get;
+ led->cdev.default_trigger = dbg_leds[i].trigger;
+ led->mask = BIT(i);
- if (new_led_api()) {
- newled_init(&pdev->dev);
+ if (led_classdev_register(NULL, &led->cdev) < 0) {
+ kfree(led);
+ break;
+ }
}
return 0;
@@ -282,13 +121,15 @@ static int /* __init */ fpga_probe(struct platform_device *pdev)
static int fpga_suspend_noirq(struct device *dev)
{
- __raw_writew(~0, &fpga->leds);
+ fpga_led_state = __raw_readw(&fpga->leds);
+ __raw_writew(0xff, &fpga->leds);
+
return 0;
}
static int fpga_resume_noirq(struct device *dev)
{
- __raw_writew(~hw_led_state, &fpga->leds);
+ __raw_writew(~fpga_led_state, &fpga->leds);
return 0;
}
diff --git a/arch/arm/plat-omap/include/plat/omap-serial.h b/arch/arm/plat-omap/include/plat/omap-serial.h
index b2eac60b6904..f4a4cd014795 100644
--- a/arch/arm/plat-omap/include/plat/omap-serial.h
+++ b/arch/arm/plat-omap/include/plat/omap-serial.h
@@ -52,7 +52,7 @@
#define OMAP_UART_DMA_CH_FREE -1
-#define OMAP_MAX_HSUART_PORTS 4
+#define OMAP_MAX_HSUART_PORTS 6
#define MSR_SAVE_FLAGS UART_MSR_ANY_DELTA
diff --git a/arch/arm/plat-samsung/clock.c b/arch/arm/plat-samsung/clock.c
index 65c5eca475e7..012bbd0b8d81 100644
--- a/arch/arm/plat-samsung/clock.c
+++ b/arch/arm/plat-samsung/clock.c
@@ -119,7 +119,7 @@ void clk_disable(struct clk *clk)
unsigned long clk_get_rate(struct clk *clk)
{
- if (IS_ERR(clk))
+ if (IS_ERR_OR_NULL(clk))
return 0;
if (clk->rate != 0)
@@ -136,7 +136,7 @@ unsigned long clk_get_rate(struct clk *clk)
long clk_round_rate(struct clk *clk, unsigned long rate)
{
- if (!IS_ERR(clk) && clk->ops && clk->ops->round_rate)
+ if (!IS_ERR_OR_NULL(clk) && clk->ops && clk->ops->round_rate)
return (clk->ops->round_rate)(clk, rate);
return rate;
@@ -144,9 +144,10 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
int clk_set_rate(struct clk *clk, unsigned long rate)
{
+ unsigned long flags;
int ret;
- if (IS_ERR(clk))
+ if (IS_ERR_OR_NULL(clk))
return -EINVAL;
/* We do not default just do a clk->rate = rate as
@@ -159,9 +160,9 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
if (clk->ops == NULL || clk->ops->set_rate == NULL)
return -EINVAL;
- spin_lock(&clocks_lock);
+ spin_lock_irqsave(&clocks_lock, flags);
ret = (clk->ops->set_rate)(clk, rate);
- spin_unlock(&clocks_lock);
+ spin_unlock_irqrestore(&clocks_lock, flags);
return ret;
}
@@ -173,17 +174,18 @@ struct clk *clk_get_parent(struct clk *clk)
int clk_set_parent(struct clk *clk, struct clk *parent)
{
+ unsigned long flags;
int ret = 0;
- if (IS_ERR(clk))
+ if (IS_ERR_OR_NULL(clk) || IS_ERR_OR_NULL(parent))
return -EINVAL;
- spin_lock(&clocks_lock);
+ spin_lock_irqsave(&clocks_lock, flags);
if (clk->ops && clk->ops->set_parent)
ret = (clk->ops->set_parent)(clk, parent);
- spin_unlock(&clocks_lock);
+ spin_unlock_irqrestore(&clocks_lock, flags);
return ret;
}
diff --git a/arch/arm/plat-samsung/devs.c b/arch/arm/plat-samsung/devs.c
index b151d4932661..03f654d55eff 100644
--- a/arch/arm/plat-samsung/devs.c
+++ b/arch/arm/plat-samsung/devs.c
@@ -51,6 +51,7 @@
#include <linux/platform_data/usb-ehci-s5p.h>
#include <plat/fb.h>
#include <plat/fb-s3c2410.h>
+#include <plat/hdmi.h>
#include <linux/platform_data/hwmon-s3c.h>
#include <linux/platform_data/i2c-s3c2410.h>
#include <plat/keypad.h>
@@ -762,7 +763,7 @@ void __init s5p_i2c_hdmiphy_set_platdata(struct s3c2410_platform_i2c *pd)
&s5p_device_i2c_hdmiphy);
}
-struct s5p_hdmi_platform_data s5p_hdmi_def_platdata;
+static struct s5p_hdmi_platform_data s5p_hdmi_def_platdata;
void __init s5p_hdmi_set_platdata(struct i2c_board_info *hdmiphy_info,
struct i2c_board_info *mhl_info, int mhl_bus)
@@ -1590,6 +1591,8 @@ struct platform_device s3c64xx_device_spi1 = {
void __init s3c64xx_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
int num_cs)
{
+ struct s3c64xx_spi_info pd;
+
/* Reject invalid configuration */
if (!num_cs || src_clk_nr < 0) {
pr_err("%s: Invalid SPI configuration\n", __func__);
diff --git a/arch/arm/plat-samsung/s5p-irq-gpioint.c b/arch/arm/plat-samsung/s5p-irq-gpioint.c
index f9431fe5b06e..23557d30e44c 100644
--- a/arch/arm/plat-samsung/s5p-irq-gpioint.c
+++ b/arch/arm/plat-samsung/s5p-irq-gpioint.c
@@ -24,7 +24,7 @@
#include <asm/mach/irq.h>
-#define GPIO_BASE(chip) (((unsigned long)(chip)->base) & 0xFFFFF000u)
+#define GPIO_BASE(chip) ((void __iomem *)((unsigned long)((chip)->base) & 0xFFFFF000u))
#define CON_OFFSET 0x700
#define MASK_OFFSET 0x900
@@ -153,7 +153,7 @@ static __init int s5p_gpioint_add(struct samsung_gpio_chip *chip)
bank->chips[group - bank->start] = chip;
gc = irq_alloc_generic_chip("s5p_gpioint", 1, chip->irq_base,
- (void __iomem *)GPIO_BASE(chip),
+ GPIO_BASE(chip),
handle_level_irq);
if (!gc)
return -ENOMEM;
diff --git a/arch/arm/plat-samsung/time.c b/arch/arm/plat-samsung/time.c
index 4dcb11c3d894..60552e22f22e 100644
--- a/arch/arm/plat-samsung/time.c
+++ b/arch/arm/plat-samsung/time.c
@@ -28,7 +28,6 @@
#include <linux/io.h>
#include <linux/platform_device.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include <asm/irq.h>
diff --git a/arch/arm/plat-versatile/Kconfig b/arch/arm/plat-versatile/Kconfig
index 8d5c10a5084d..2a4ae8a6a081 100644
--- a/arch/arm/plat-versatile/Kconfig
+++ b/arch/arm/plat-versatile/Kconfig
@@ -16,8 +16,10 @@ config PLAT_VERSATILE_FPGA_IRQ_NR
depends on PLAT_VERSATILE_FPGA_IRQ
config PLAT_VERSATILE_LEDS
- def_bool y if LEDS_CLASS
+ def_bool y if NEW_LEDS
depends on ARCH_REALVIEW || ARCH_VERSATILE
+ select LEDS_CLASS
+ select LEDS_TRIGGER
config PLAT_VERSATILE_SCHED_CLOCK
def_bool y
diff --git a/arch/arm/plat-versatile/leds.c b/arch/arm/plat-versatile/leds.c
index 3169fa555ea6..d2490d00b46c 100644
--- a/arch/arm/plat-versatile/leds.c
+++ b/arch/arm/plat-versatile/leds.c
@@ -37,10 +37,10 @@ static const struct {
} versatile_leds[] = {
{ "versatile:0", "heartbeat", },
{ "versatile:1", "mmc0", },
- { "versatile:2", },
- { "versatile:3", },
- { "versatile:4", },
- { "versatile:5", },
+ { "versatile:2", "cpu0" },
+ { "versatile:3", "cpu1" },
+ { "versatile:4", "cpu2" },
+ { "versatile:5", "cpu3" },
{ "versatile:6", },
{ "versatile:7", },
};
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index 7bc7948c5432..831e1fdfdb2f 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -66,7 +66,6 @@ iq80321 ARCH_IQ80321 IQ80321 169
ks8695 ARCH_KS8695 KS8695 180
karo ARCH_KARO KARO 190
smdk2410 ARCH_SMDK2410 SMDK2410 193
-ceiva ARCH_CEIVA CEIVA 200
voiceblue MACH_VOICEBLUE VOICEBLUE 218
h5400 ARCH_H5400 H5400 220
omap_innovator MACH_OMAP_INNOVATOR OMAP_INNOVATOR 234
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
new file mode 100644
index 000000000000..767ba5685454
--- /dev/null
+++ b/arch/arm64/Kconfig
@@ -0,0 +1,222 @@
+config ARM64
+ def_bool y
+ select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
+ select GENERIC_CLOCKEVENTS
+ select GENERIC_HARDIRQS_NO_DEPRECATED
+ select GENERIC_IOMAP
+ select GENERIC_IRQ_PROBE
+ select GENERIC_IRQ_SHOW
+ select GENERIC_SMP_IDLE_THREAD
+ select GENERIC_TIME_VSYSCALL
+ select HARDIRQS_SW_RESEND
+ select HAVE_ARCH_TRACEHOOK
+ select HAVE_DMA_API_DEBUG
+ select HAVE_DMA_ATTRS
+ select HAVE_GENERIC_DMA_COHERENT
+ select HAVE_GENERIC_HARDIRQS
+ select HAVE_HW_BREAKPOINT if PERF_EVENTS
+ select HAVE_IRQ_WORK
+ select HAVE_MEMBLOCK
+ select HAVE_PERF_EVENTS
+ select HAVE_SPARSE_IRQ
+ select IRQ_DOMAIN
+ select NO_BOOTMEM
+ select OF
+ select OF_EARLY_FLATTREE
+ select PERF_USE_VMALLOC
+ select RTC_LIB
+ select SPARSE_IRQ
+ help
+ ARM 64-bit (AArch64) Linux support.
+
+config 64BIT
+ def_bool y
+
+config ARCH_PHYS_ADDR_T_64BIT
+ def_bool y
+
+config MMU
+ def_bool y
+
+config NO_IOPORT
+ def_bool y
+
+config STACKTRACE_SUPPORT
+ def_bool y
+
+config LOCKDEP_SUPPORT
+ def_bool y
+
+config TRACE_IRQFLAGS_SUPPORT
+ def_bool y
+
+config GENERIC_LOCKBREAK
+ def_bool y
+ depends on SMP && PREEMPT
+
+config RWSEM_GENERIC_SPINLOCK
+ def_bool y
+
+config GENERIC_HWEIGHT
+ def_bool y
+
+config GENERIC_CSUM
+ def_bool y
+
+config GENERIC_CALIBRATE_DELAY
+ def_bool y
+
+config ZONE_DMA32
+ def_bool y
+
+config ARCH_DMA_ADDR_T_64BIT
+ def_bool y
+
+config NEED_DMA_MAP_STATE
+ def_bool y
+
+config NEED_SG_DMA_LENGTH
+ def_bool y
+
+config SWIOTLB
+ def_bool y
+
+config IOMMU_HELPER
+ def_bool SWIOTLB
+
+source "init/Kconfig"
+
+source "kernel/Kconfig.freezer"
+
+menu "System Type"
+
+endmenu
+
+menu "Bus support"
+
+config ARM_AMBA
+ bool
+
+endmenu
+
+menu "Kernel Features"
+
+source "kernel/time/Kconfig"
+
+config ARM64_64K_PAGES
+ bool "Enable 64KB pages support"
+ help
+ This feature enables 64KB pages support (4KB by default)
+ allowing only two levels of page tables and faster TLB
+ look-up. AArch32 emulation is not available when this feature
+ is enabled.
+
+config SMP
+ bool "Symmetric Multi-Processing"
+ select USE_GENERIC_SMP_HELPERS
+ help
+ This enables support for systems with more than one CPU. If
+ you say N here, the kernel will run on single and
+ multiprocessor machines, but will use only one CPU of a
+ multiprocessor machine. If you say Y here, the kernel will run
+ on many, but not all, single processor machines. On a single
+ processor machine, the kernel will run faster if you say N
+ here.
+
+ If you don't know what to do here, say N.
+
+config NR_CPUS
+ int "Maximum number of CPUs (2-32)"
+ range 2 32
+ depends on SMP
+ default "4"
+
+source kernel/Kconfig.preempt
+
+config HZ
+ int
+ default 100
+
+config ARCH_HAS_HOLES_MEMORYMODEL
+ def_bool y if SPARSEMEM
+
+config ARCH_SPARSEMEM_ENABLE
+ def_bool y
+ select SPARSEMEM_VMEMMAP_ENABLE
+
+config ARCH_SPARSEMEM_DEFAULT
+ def_bool ARCH_SPARSEMEM_ENABLE
+
+config ARCH_SELECT_MEMORY_MODEL
+ def_bool ARCH_SPARSEMEM_ENABLE
+
+config HAVE_ARCH_PFN_VALID
+ def_bool ARCH_HAS_HOLES_MEMORYMODEL || !SPARSEMEM
+
+config HW_PERF_EVENTS
+ bool "Enable hardware performance counter support for perf events"
+ depends on PERF_EVENTS
+ default y
+ help
+ Enable hardware performance counter support for perf events. If
+ disabled, perf events will use software events only.
+
+source "mm/Kconfig"
+
+endmenu
+
+menu "Boot options"
+
+config CMDLINE
+ string "Default kernel command string"
+ default ""
+ help
+ Provide a set of default command-line options at build time by
+ entering them here. As a minimum, you should specify the the
+ root device (e.g. root=/dev/nfs).
+
+config CMDLINE_FORCE
+ bool "Always use the default kernel command string"
+ help
+ Always use the default kernel command string, even if the boot
+ loader passes other arguments to the kernel.
+ This is useful if you cannot or don't want to change the
+ command-line options your boot loader passes to the kernel.
+
+endmenu
+
+menu "Userspace binary formats"
+
+source "fs/Kconfig.binfmt"
+
+config COMPAT
+ bool "Kernel support for 32-bit EL0"
+ depends on !ARM64_64K_PAGES
+ select COMPAT_BINFMT_ELF
+ help
+ This option enables support for a 32-bit EL0 running under a 64-bit
+ kernel at EL1. AArch32-specific components such as system calls,
+ the user helper functions, VFP support and the ptrace interface are
+ handled appropriately by the kernel.
+
+ If you want to execute 32-bit userspace applications, say Y.
+
+config SYSVIPC_COMPAT
+ def_bool y
+ depends on COMPAT && SYSVIPC
+
+endmenu
+
+source "net/Kconfig"
+
+source "drivers/Kconfig"
+
+source "fs/Kconfig"
+
+source "arch/arm64/Kconfig.debug"
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
+
+source "lib/Kconfig"
diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug
new file mode 100644
index 000000000000..d7553f2bda66
--- /dev/null
+++ b/arch/arm64/Kconfig.debug
@@ -0,0 +1,27 @@
+menu "Kernel hacking"
+
+source "lib/Kconfig.debug"
+
+config FRAME_POINTER
+ bool
+ default y
+
+config DEBUG_ERRORS
+ bool "Verbose kernel error messages"
+ depends on DEBUG_KERNEL
+ help
+ This option controls verbose debugging information which can be
+ printed when the kernel detects an internal error. This debugging
+ information is useful to kernel hackers when tracking down problems,
+ but mostly meaningless to other people. It's safe to say Y unless
+ you are concerned with the code size or don't want to see these
+ messages.
+
+config DEBUG_STACK_USAGE
+ bool "Enable stack utilization instrumentation"
+ depends on DEBUG_KERNEL
+ help
+ Enables the display of the minimum amount of free stack which each
+ task has ever had available in the sysrq-T output.
+
+endmenu
diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
new file mode 100644
index 000000000000..364191f3be43
--- /dev/null
+++ b/arch/arm64/Makefile
@@ -0,0 +1,71 @@
+#
+# arch/arm64/Makefile
+#
+# This file is included by the global makefile so that you can add your own
+# architecture-specific flags and dependencies.
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1995-2001 by Russell King
+
+LDFLAGS_vmlinux :=-p --no-undefined -X
+CPPFLAGS_vmlinux.lds = -DTEXT_OFFSET=$(TEXT_OFFSET)
+OBJCOPYFLAGS :=-O binary -R .note -R .note.gnu.build-id -R .comment -S
+GZFLAGS :=-9
+
+LIBGCC := $(shell $(CC) $(KBUILD_CFLAGS) -print-libgcc-file-name)
+
+KBUILD_DEFCONFIG := defconfig
+
+KBUILD_CFLAGS += -mgeneral-regs-only
+KBUILD_CPPFLAGS += -mlittle-endian
+AS += -EL
+LD += -EL
+
+comma = ,
+
+CHECKFLAGS += -D__aarch64__
+
+# Default value
+head-y := arch/arm64/kernel/head.o
+
+# The byte offset of the kernel image in RAM from the start of RAM.
+TEXT_OFFSET := 0x00080000
+
+export TEXT_OFFSET GZFLAGS
+
+core-y += arch/arm64/kernel/ arch/arm64/mm/
+libs-y := arch/arm64/lib/ $(libs-y)
+libs-y += $(LIBGCC)
+
+# Default target when executing plain make
+KBUILD_IMAGE := Image.gz
+
+all: $(KBUILD_IMAGE)
+
+boot := arch/arm64/boot
+
+Image Image.gz: vmlinux
+ $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
+
+zinstall install: vmlinux
+ $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $@
+
+%.dtb:
+ $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
+
+# We use MRPROPER_FILES and CLEAN_FILES now
+archclean:
+ $(Q)$(MAKE) $(clean)=$(boot)
+
+define archhelp
+ echo '* Image.gz - Compressed kernel image (arch/$(ARCH)/boot/Image.gz)'
+ echo ' Image - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'
+ echo ' install - Install uncompressed kernel'
+ echo ' zinstall - Install compressed kernel'
+ echo ' Install using (your) ~/bin/installkernel or'
+ echo ' (distribution) /sbin/installkernel or'
+ echo ' install to $$(INSTALL_PATH) and run lilo'
+endef
diff --git a/arch/arm64/boot/.gitignore b/arch/arm64/boot/.gitignore
new file mode 100644
index 000000000000..8dab0bb6ae66
--- /dev/null
+++ b/arch/arm64/boot/.gitignore
@@ -0,0 +1,2 @@
+Image
+Image.gz
diff --git a/arch/arm64/boot/Makefile b/arch/arm64/boot/Makefile
new file mode 100644
index 000000000000..eca209b2b0bf
--- /dev/null
+++ b/arch/arm64/boot/Makefile
@@ -0,0 +1,36 @@
+#
+# arch/arm64/boot/Makefile
+#
+# This file is included by the global makefile so that you can add your own
+# architecture-specific flags and dependencies.
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2012, ARM Ltd.
+# Author: Will Deacon <will.deacon@arm.com>
+#
+# Based on the ia64 boot/Makefile.
+#
+
+targets := Image Image.gz
+
+$(obj)/Image: vmlinux FORCE
+ $(call if_changed,objcopy)
+
+$(obj)/Image.gz: $(obj)/Image FORCE
+ $(call if_changed,gzip)
+
+$(obj)/%.dtb: $(src)/dts/%.dts
+ $(call cmd,dtc)
+
+install: $(obj)/Image
+ $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \
+ $(obj)/Image System.map "$(INSTALL_PATH)"
+
+zinstall: $(obj)/Image.gz
+ $(CONFIG_SHELL) $(srctree)/$(src)/install.sh $(KERNELRELEASE) \
+ $(obj)/Image.gz System.map "$(INSTALL_PATH)"
+
+clean-files += *.dtb
diff --git a/arch/arm64/boot/install.sh b/arch/arm64/boot/install.sh
new file mode 100644
index 000000000000..12ed78aa6f0c
--- /dev/null
+++ b/arch/arm64/boot/install.sh
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# arch/arm64/boot/install.sh
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License. See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 1995 by Linus Torvalds
+#
+# Adapted from code in arch/i386/boot/Makefile by H. Peter Anvin
+# Adapted from code in arch/i386/boot/install.sh by Russell King
+#
+# "make install" script for the AArch64 Linux port
+#
+# Arguments:
+# $1 - kernel version
+# $2 - kernel image file
+# $3 - kernel map file
+# $4 - default install path (blank if root directory)
+#
+
+# User may have a custom install script
+if [ -x ~/bin/${INSTALLKERNEL} ]; then exec ~/bin/${INSTALLKERNEL} "$@"; fi
+if [ -x /sbin/${INSTALLKERNEL} ]; then exec /sbin/${INSTALLKERNEL} "$@"; fi
+
+if [ "$(basename $2)" = "Image.gz" ]; then
+# Compressed install
+ echo "Installing compressed kernel"
+ base=vmlinuz
+else
+# Normal install
+ echo "Installing normal kernel"
+ base=vmlinux
+fi
+
+if [ -f $4/$base-$1 ]; then
+ mv $4/$base-$1 $4/$base-$1.old
+fi
+cat $2 > $4/$base-$1
+
+# Install system map file
+if [ -f $4/System.map-$1 ]; then
+ mv $4/System.map-$1 $4/System.map-$1.old
+fi
+cp $3 $4/System.map-$1
diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
new file mode 100644
index 000000000000..9212c7880da7
--- /dev/null
+++ b/arch/arm64/configs/defconfig
@@ -0,0 +1,85 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_UTS_NS is not set
+# CONFIG_IPC_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_NET_NS is not set
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_KALLSYMS_ALL=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PROFILING=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_SMP=y
+CONFIG_PREEMPT_VOLUNTARY=y
+CONFIG_CMDLINE="console=ttyAMA0"
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_COMPAT=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_INET_LRO is not set
+# CONFIG_IPV6 is not set
+# CONFIG_WIRELESS is not set
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+# CONFIG_BLK_DEV is not set
+CONFIG_SCSI=y
+# CONFIG_SCSI_PROC_FS is not set
+CONFIG_BLK_DEV_SD=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_NETDEVICES=y
+CONFIG_MII=y
+# CONFIG_WLAN is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_SERIO_I8042 is not set
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_LEGACY_PTY_COUNT=16
+# CONFIG_HW_RANDOM is not set
+# CONFIG_HWMON is not set
+CONFIG_FB=y
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT2_FS=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+# CONFIG_EXT3_FS_XATTR is not set
+CONFIG_FUSE_FS=y
+CONFIG_CUSE=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_ROOT_NFS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_SCHED_DEBUG is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_FTRACE is not set
+CONFIG_ATOMIC64_SELFTEST=y
+CONFIG_DEBUG_ERRORS=y
diff --git a/arch/arm64/include/asm/Kbuild b/arch/arm64/include/asm/Kbuild
new file mode 100644
index 000000000000..35924a542d43
--- /dev/null
+++ b/arch/arm64/include/asm/Kbuild
@@ -0,0 +1,51 @@
+include include/asm-generic/Kbuild.asm
+
+header-y += hwcap.h
+
+generic-y += bug.h
+generic-y += bugs.h
+generic-y += checksum.h
+generic-y += cputime.h
+generic-y += current.h
+generic-y += delay.h
+generic-y += div64.h
+generic-y += dma.h
+generic-y += emergency-restart.h
+generic-y += errno.h
+generic-y += ftrace.h
+generic-y += hw_irq.h
+generic-y += ioctl.h
+generic-y += ioctls.h
+generic-y += ipcbuf.h
+generic-y += irq_regs.h
+generic-y += kdebug.h
+generic-y += kmap_types.h
+generic-y += linkage.h
+generic-y += local.h
+generic-y += local64.h
+generic-y += mman.h
+generic-y += msgbuf.h
+generic-y += mutex.h
+generic-y += pci.h
+generic-y += percpu.h
+generic-y += poll.h
+generic-y += posix_types.h
+generic-y += resource.h
+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 += sizes.h
+generic-y += socket.h
+generic-y += sockios.h
+generic-y += string.h
+generic-y += switch_to.h
+generic-y += swab.h
+generic-y += termbits.h
+generic-y += termios.h
+generic-y += topology.h
+generic-y += types.h
+generic-y += unaligned.h
+generic-y += user.h
diff --git a/arch/arm64/include/asm/arm_generic.h b/arch/arm64/include/asm/arm_generic.h
new file mode 100644
index 000000000000..e4cec9d30f27
--- /dev/null
+++ b/arch/arm64/include/asm/arm_generic.h
@@ -0,0 +1,100 @@
+/*
+ * arch/arm64/include/asm/arm_generic.h
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_ARM_GENERIC_H
+#define __ASM_ARM_GENERIC_H
+
+#include <linux/clocksource.h>
+
+#define ARCH_TIMER_CTRL_ENABLE (1 << 0)
+#define ARCH_TIMER_CTRL_IMASK (1 << 1)
+#define ARCH_TIMER_CTRL_ISTATUS (1 << 2)
+
+#define ARCH_TIMER_REG_CTRL 0
+#define ARCH_TIMER_REG_FREQ 1
+#define ARCH_TIMER_REG_TVAL 2
+
+static inline void arch_timer_reg_write(int reg, u32 val)
+{
+ switch (reg) {
+ case ARCH_TIMER_REG_CTRL:
+ asm volatile("msr cntp_ctl_el0, %0" : : "r" (val));
+ break;
+ case ARCH_TIMER_REG_TVAL:
+ asm volatile("msr cntp_tval_el0, %0" : : "r" (val));
+ break;
+ default:
+ BUILD_BUG();
+ }
+
+ isb();
+}
+
+static inline u32 arch_timer_reg_read(int reg)
+{
+ u32 val;
+
+ switch (reg) {
+ case ARCH_TIMER_REG_CTRL:
+ asm volatile("mrs %0, cntp_ctl_el0" : "=r" (val));
+ break;
+ case ARCH_TIMER_REG_FREQ:
+ asm volatile("mrs %0, cntfrq_el0" : "=r" (val));
+ break;
+ case ARCH_TIMER_REG_TVAL:
+ asm volatile("mrs %0, cntp_tval_el0" : "=r" (val));
+ break;
+ default:
+ BUILD_BUG();
+ }
+
+ return val;
+}
+
+static inline void __cpuinit arch_counter_enable_user_access(void)
+{
+ u32 cntkctl;
+
+ /* Disable user access to the timers and the virtual counter. */
+ asm volatile("mrs %0, cntkctl_el1" : "=r" (cntkctl));
+ cntkctl &= ~((3 << 8) | (1 << 1));
+
+ /* Enable user access to the physical counter and frequency. */
+ cntkctl |= 1;
+ asm volatile("msr cntkctl_el1, %0" : : "r" (cntkctl));
+}
+
+static inline cycle_t arch_counter_get_cntpct(void)
+{
+ cycle_t cval;
+
+ asm volatile("mrs %0, cntpct_el0" : "=r" (cval));
+
+ return cval;
+}
+
+static inline cycle_t arch_counter_get_cntvct(void)
+{
+ cycle_t cval;
+
+ asm volatile("mrs %0, cntvct_el0" : "=r" (cval));
+
+ return cval;
+}
+
+#endif
diff --git a/arch/arm64/include/asm/asm-offsets.h b/arch/arm64/include/asm/asm-offsets.h
new file mode 100644
index 000000000000..d370ee36a182
--- /dev/null
+++ b/arch/arm64/include/asm/asm-offsets.h
@@ -0,0 +1 @@
+#include <generated/asm-offsets.h>
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
new file mode 100644
index 000000000000..da2a13e8f1e6
--- /dev/null
+++ b/arch/arm64/include/asm/assembler.h
@@ -0,0 +1,109 @@
+/*
+ * Based on arch/arm/include/asm/assembler.h
+ *
+ * Copyright (C) 1996-2000 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASSEMBLY__
+#error "Only include this from assembly code"
+#endif
+
+#include <asm/ptrace.h>
+
+/*
+ * Stack pushing/popping (register pairs only). Equivalent to store decrement
+ * before, load increment after.
+ */
+ .macro push, xreg1, xreg2
+ stp \xreg1, \xreg2, [sp, #-16]!
+ .endm
+
+ .macro pop, xreg1, xreg2
+ ldp \xreg1, \xreg2, [sp], #16
+ .endm
+
+/*
+ * Enable and disable interrupts.
+ */
+ .macro disable_irq
+ msr daifset, #2
+ .endm
+
+ .macro enable_irq
+ msr daifclr, #2
+ .endm
+
+/*
+ * Save/disable and restore interrupts.
+ */
+ .macro save_and_disable_irqs, olddaif
+ mrs \olddaif, daif
+ disable_irq
+ .endm
+
+ .macro restore_irqs, olddaif
+ msr daif, \olddaif
+ .endm
+
+/*
+ * Enable and disable debug exceptions.
+ */
+ .macro disable_dbg
+ msr daifset, #8
+ .endm
+
+ .macro enable_dbg
+ msr daifclr, #8
+ .endm
+
+ .macro disable_step, tmp
+ mrs \tmp, mdscr_el1
+ bic \tmp, \tmp, #1
+ msr mdscr_el1, \tmp
+ .endm
+
+ .macro enable_step, tmp
+ mrs \tmp, mdscr_el1
+ orr \tmp, \tmp, #1
+ msr mdscr_el1, \tmp
+ .endm
+
+ .macro enable_dbg_if_not_stepping, tmp
+ mrs \tmp, mdscr_el1
+ tbnz \tmp, #1, 9990f
+ enable_dbg
+9990:
+ .endm
+
+/*
+ * SMP data memory barrier
+ */
+ .macro smp_dmb, opt
+#ifdef CONFIG_SMP
+ dmb \opt
+#endif
+ .endm
+
+#define USER(l, x...) \
+9999: x; \
+ .section __ex_table,"a"; \
+ .align 3; \
+ .quad 9999b,l; \
+ .previous
+
+/*
+ * Register aliases.
+ */
+lr .req x30 // link register
diff --git a/arch/arm64/include/asm/atomic.h b/arch/arm64/include/asm/atomic.h
new file mode 100644
index 000000000000..407717ba060e
--- /dev/null
+++ b/arch/arm64/include/asm/atomic.h
@@ -0,0 +1,305 @@
+/*
+ * Based on arch/arm/include/asm/atomic.h
+ *
+ * Copyright (C) 1996 Russell King.
+ * Copyright (C) 2002 Deep Blue Solutions Ltd.
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_ATOMIC_H
+#define __ASM_ATOMIC_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+#include <asm/barrier.h>
+#include <asm/cmpxchg.h>
+
+#define ATOMIC_INIT(i) { (i) }
+
+#ifdef __KERNEL__
+
+/*
+ * On ARM, ordinary assignment (str instruction) doesn't clear the local
+ * strex/ldrex monitor on some implementations. The reason we can use it for
+ * atomic_set() is the clrex or dummy strex done on every exception return.
+ */
+#define atomic_read(v) (*(volatile int *)&(v)->counter)
+#define atomic_set(v,i) (((v)->counter) = (i))
+
+/*
+ * AArch64 UP and SMP safe atomic ops. We use load exclusive and
+ * store exclusive to ensure that these are atomic. We may loop
+ * to ensure that the update happens.
+ */
+static inline void atomic_add(int i, atomic_t *v)
+{
+ unsigned long tmp;
+ int result;
+
+ asm volatile("// atomic_add\n"
+"1: ldxr %w0, [%3]\n"
+" add %w0, %w0, %w4\n"
+" stxr %w1, %w0, [%3]\n"
+" cbnz %w1, 1b"
+ : "=&r" (result), "=&r" (tmp), "+o" (v->counter)
+ : "r" (&v->counter), "Ir" (i)
+ : "cc");
+}
+
+static inline int atomic_add_return(int i, atomic_t *v)
+{
+ unsigned long tmp;
+ int result;
+
+ asm volatile("// atomic_add_return\n"
+"1: ldaxr %w0, [%3]\n"
+" add %w0, %w0, %w4\n"
+" stlxr %w1, %w0, [%3]\n"
+" cbnz %w1, 1b"
+ : "=&r" (result), "=&r" (tmp), "+o" (v->counter)
+ : "r" (&v->counter), "Ir" (i)
+ : "cc");
+
+ return result;
+}
+
+static inline void atomic_sub(int i, atomic_t *v)
+{
+ unsigned long tmp;
+ int result;
+
+ asm volatile("// atomic_sub\n"
+"1: ldxr %w0, [%3]\n"
+" sub %w0, %w0, %w4\n"
+" stxr %w1, %w0, [%3]\n"
+" cbnz %w1, 1b"
+ : "=&r" (result), "=&r" (tmp), "+o" (v->counter)
+ : "r" (&v->counter), "Ir" (i)
+ : "cc");
+}
+
+static inline int atomic_sub_return(int i, atomic_t *v)
+{
+ unsigned long tmp;
+ int result;
+
+ asm volatile("// atomic_sub_return\n"
+"1: ldaxr %w0, [%3]\n"
+" sub %w0, %w0, %w4\n"
+" stlxr %w1, %w0, [%3]\n"
+" cbnz %w1, 1b"
+ : "=&r" (result), "=&r" (tmp), "+o" (v->counter)
+ : "r" (&v->counter), "Ir" (i)
+ : "cc");
+
+ return result;
+}
+
+static inline int atomic_cmpxchg(atomic_t *ptr, int old, int new)
+{
+ unsigned long tmp;
+ int oldval;
+
+ asm volatile("// atomic_cmpxchg\n"
+"1: ldaxr %w1, [%3]\n"
+" cmp %w1, %w4\n"
+" b.ne 2f\n"
+" stlxr %w0, %w5, [%3]\n"
+" cbnz %w0, 1b\n"
+"2:"
+ : "=&r" (tmp), "=&r" (oldval), "+o" (ptr->counter)
+ : "r" (&ptr->counter), "Ir" (old), "r" (new)
+ : "cc");
+
+ return oldval;
+}
+
+static inline void atomic_clear_mask(unsigned long mask, unsigned long *addr)
+{
+ unsigned long tmp, tmp2;
+
+ asm volatile("// atomic_clear_mask\n"
+"1: ldxr %0, [%3]\n"
+" bic %0, %0, %4\n"
+" stxr %w1, %0, [%3]\n"
+" cbnz %w1, 1b"
+ : "=&r" (tmp), "=&r" (tmp2), "+o" (*addr)
+ : "r" (addr), "Ir" (mask)
+ : "cc");
+}
+
+#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;
+
+ c = atomic_read(v);
+ while (c != u && (old = atomic_cmpxchg((v), c, c + a)) != c)
+ c = old;
+ return c;
+}
+
+#define atomic_inc(v) atomic_add(1, v)
+#define atomic_dec(v) atomic_sub(1, v)
+
+#define atomic_inc_and_test(v) (atomic_add_return(1, v) == 0)
+#define atomic_dec_and_test(v) (atomic_sub_return(1, v) == 0)
+#define atomic_inc_return(v) (atomic_add_return(1, v))
+#define atomic_dec_return(v) (atomic_sub_return(1, v))
+#define atomic_sub_and_test(i, v) (atomic_sub_return(i, v) == 0)
+
+#define atomic_add_negative(i,v) (atomic_add_return(i, v) < 0)
+
+#define smp_mb__before_atomic_dec() smp_mb()
+#define smp_mb__after_atomic_dec() smp_mb()
+#define smp_mb__before_atomic_inc() smp_mb()
+#define smp_mb__after_atomic_inc() smp_mb()
+
+/*
+ * 64-bit atomic operations.
+ */
+#define ATOMIC64_INIT(i) { (i) }
+
+#define atomic64_read(v) (*(volatile long long *)&(v)->counter)
+#define atomic64_set(v,i) (((v)->counter) = (i))
+
+static inline void atomic64_add(u64 i, atomic64_t *v)
+{
+ long result;
+ unsigned long tmp;
+
+ asm volatile("// atomic64_add\n"
+"1: ldxr %0, [%3]\n"
+" add %0, %0, %4\n"
+" stxr %w1, %0, [%3]\n"
+" cbnz %w1, 1b"
+ : "=&r" (result), "=&r" (tmp), "+o" (v->counter)
+ : "r" (&v->counter), "Ir" (i)
+ : "cc");
+}
+
+static inline long atomic64_add_return(long i, atomic64_t *v)
+{
+ long result;
+ unsigned long tmp;
+
+ asm volatile("// atomic64_add_return\n"
+"1: ldaxr %0, [%3]\n"
+" add %0, %0, %4\n"
+" stlxr %w1, %0, [%3]\n"
+" cbnz %w1, 1b"
+ : "=&r" (result), "=&r" (tmp), "+o" (v->counter)
+ : "r" (&v->counter), "Ir" (i)
+ : "cc");
+
+ return result;
+}
+
+static inline void atomic64_sub(u64 i, atomic64_t *v)
+{
+ long result;
+ unsigned long tmp;
+
+ asm volatile("// atomic64_sub\n"
+"1: ldxr %0, [%3]\n"
+" sub %0, %0, %4\n"
+" stxr %w1, %0, [%3]\n"
+" cbnz %w1, 1b"
+ : "=&r" (result), "=&r" (tmp), "+o" (v->counter)
+ : "r" (&v->counter), "Ir" (i)
+ : "cc");
+}
+
+static inline long atomic64_sub_return(long i, atomic64_t *v)
+{
+ long result;
+ unsigned long tmp;
+
+ asm volatile("// atomic64_sub_return\n"
+"1: ldaxr %0, [%3]\n"
+" sub %0, %0, %4\n"
+" stlxr %w1, %0, [%3]\n"
+" cbnz %w1, 1b"
+ : "=&r" (result), "=&r" (tmp), "+o" (v->counter)
+ : "r" (&v->counter), "Ir" (i)
+ : "cc");
+
+ return result;
+}
+
+static inline long atomic64_cmpxchg(atomic64_t *ptr, long old, long new)
+{
+ long oldval;
+ unsigned long res;
+
+ asm volatile("// atomic64_cmpxchg\n"
+"1: ldaxr %1, [%3]\n"
+" cmp %1, %4\n"
+" b.ne 2f\n"
+" stlxr %w0, %5, [%3]\n"
+" cbnz %w0, 1b\n"
+"2:"
+ : "=&r" (res), "=&r" (oldval), "+o" (ptr->counter)
+ : "r" (&ptr->counter), "Ir" (old), "r" (new)
+ : "cc");
+
+ return oldval;
+}
+
+#define atomic64_xchg(v, new) (xchg(&((v)->counter), new))
+
+static inline long atomic64_dec_if_positive(atomic64_t *v)
+{
+ long result;
+ unsigned long tmp;
+
+ asm volatile("// atomic64_dec_if_positive\n"
+"1: ldaxr %0, [%3]\n"
+" subs %0, %0, #1\n"
+" b.mi 2f\n"
+" stlxr %w1, %0, [%3]\n"
+" cbnz %w1, 1b\n"
+"2:"
+ : "=&r" (result), "=&r" (tmp), "+o" (v->counter)
+ : "r" (&v->counter)
+ : "cc");
+
+ return result;
+}
+
+static inline int atomic64_add_unless(atomic64_t *v, long a, long u)
+{
+ long c, old;
+
+ c = atomic64_read(v);
+ while (c != u && (old = atomic64_cmpxchg((v), c, c + a)) != c)
+ c = old;
+
+ return c != u;
+}
+
+#define atomic64_add_negative(a, v) (atomic64_add_return((a), (v)) < 0)
+#define atomic64_inc(v) atomic64_add(1LL, (v))
+#define atomic64_inc_return(v) atomic64_add_return(1LL, (v))
+#define atomic64_inc_and_test(v) (atomic64_inc_return(v) == 0)
+#define atomic64_sub_and_test(a, v) (atomic64_sub_return((a), (v)) == 0)
+#define atomic64_dec(v) atomic64_sub(1LL, (v))
+#define atomic64_dec_return(v) atomic64_sub_return(1LL, (v))
+#define atomic64_dec_and_test(v) (atomic64_dec_return((v)) == 0)
+#define atomic64_inc_not_zero(v) atomic64_add_unless((v), 1LL, 0LL)
+
+#endif
+#endif
diff --git a/arch/arm64/include/asm/auxvec.h b/arch/arm64/include/asm/auxvec.h
new file mode 100644
index 000000000000..22d6d8885854
--- /dev/null
+++ b/arch/arm64/include/asm/auxvec.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_AUXVEC_H
+#define __ASM_AUXVEC_H
+
+/* vDSO location */
+#define AT_SYSINFO_EHDR 33
+
+#endif
diff --git a/arch/arm64/include/asm/barrier.h b/arch/arm64/include/asm/barrier.h
new file mode 100644
index 000000000000..d4a63338a53c
--- /dev/null
+++ b/arch/arm64/include/asm/barrier.h
@@ -0,0 +1,52 @@
+/*
+ * Based on arch/arm/include/asm/barrier.h
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_BARRIER_H
+#define __ASM_BARRIER_H
+
+#ifndef __ASSEMBLY__
+
+#define sev() asm volatile("sev" : : : "memory")
+#define wfe() asm volatile("wfe" : : : "memory")
+#define wfi() asm volatile("wfi" : : : "memory")
+
+#define isb() asm volatile("isb" : : : "memory")
+#define dsb() asm volatile("dsb sy" : : : "memory")
+
+#define mb() dsb()
+#define rmb() asm volatile("dsb ld" : : : "memory")
+#define wmb() asm volatile("dsb st" : : : "memory")
+
+#ifndef CONFIG_SMP
+#define smp_mb() barrier()
+#define smp_rmb() barrier()
+#define smp_wmb() barrier()
+#else
+#define smp_mb() asm volatile("dmb ish" : : : "memory")
+#define smp_rmb() asm volatile("dmb ishld" : : : "memory")
+#define smp_wmb() asm volatile("dmb ishst" : : : "memory")
+#endif
+
+#define read_barrier_depends() do { } while(0)
+#define smp_read_barrier_depends() do { } while(0)
+
+#define set_mb(var, value) do { var = value; smp_mb(); } while (0)
+#define nop() asm volatile("nop");
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_BARRIER_H */
diff --git a/arch/arm64/include/asm/bitops.h b/arch/arm64/include/asm/bitops.h
new file mode 100644
index 000000000000..5e693073b030
--- /dev/null
+++ b/arch/arm64/include/asm/bitops.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_BITOPS_H
+#define __ASM_BITOPS_H
+
+#include <linux/compiler.h>
+
+#include <asm/barrier.h>
+
+/*
+ * clear_bit may not imply a memory barrier
+ */
+#ifndef smp_mb__before_clear_bit
+#define smp_mb__before_clear_bit() smp_mb()
+#define smp_mb__after_clear_bit() smp_mb()
+#endif
+
+#ifndef _LINUX_BITOPS_H
+#error only <linux/bitops.h> can be included directly
+#endif
+
+#include <asm-generic/bitops/builtin-__ffs.h>
+#include <asm-generic/bitops/builtin-ffs.h>
+#include <asm-generic/bitops/builtin-__fls.h>
+#include <asm-generic/bitops/builtin-fls.h>
+
+#include <asm-generic/bitops/ffz.h>
+#include <asm-generic/bitops/fls64.h>
+#include <asm-generic/bitops/find.h>
+
+#include <asm-generic/bitops/sched.h>
+#include <asm-generic/bitops/hweight.h>
+#include <asm-generic/bitops/lock.h>
+
+#include <asm-generic/bitops/atomic.h>
+#include <asm-generic/bitops/non-atomic.h>
+#include <asm-generic/bitops/le.h>
+#include <asm-generic/bitops/ext2-atomic.h>
+
+#endif /* __ASM_BITOPS_H */
diff --git a/arch/arm64/include/asm/bitsperlong.h b/arch/arm64/include/asm/bitsperlong.h
new file mode 100644
index 000000000000..fce9c2924fa3
--- /dev/null
+++ b/arch/arm64/include/asm/bitsperlong.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_BITSPERLONG_H
+#define __ASM_BITSPERLONG_H
+
+#define __BITS_PER_LONG 64
+
+#include <asm-generic/bitsperlong.h>
+
+#endif /* __ASM_BITSPERLONG_H */
diff --git a/arch/arm64/include/asm/byteorder.h b/arch/arm64/include/asm/byteorder.h
new file mode 100644
index 000000000000..2b92046aafc5
--- /dev/null
+++ b/arch/arm64/include/asm/byteorder.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_BYTEORDER_H
+#define __ASM_BYTEORDER_H
+
+#include <linux/byteorder/little_endian.h>
+
+#endif /* __ASM_BYTEORDER_H */
diff --git a/arch/arm64/include/asm/cache.h b/arch/arm64/include/asm/cache.h
new file mode 100644
index 000000000000..390308a67f0d
--- /dev/null
+++ b/arch/arm64/include/asm/cache.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_CACHE_H
+#define __ASM_CACHE_H
+
+#define L1_CACHE_SHIFT 6
+#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)
+
+/*
+ * Memory returned by kmalloc() may be used for DMA, so we must make
+ * sure that all such allocations are cache aligned. Otherwise,
+ * unrelated code may cause parts of the buffer to be read into the
+ * cache before the transfer is done, causing old data to be seen by
+ * the CPU.
+ */
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
+#define ARCH_SLAB_MINALIGN 8
+
+#endif
diff --git a/arch/arm64/include/asm/cacheflush.h b/arch/arm64/include/asm/cacheflush.h
new file mode 100644
index 000000000000..aa3132ab7f29
--- /dev/null
+++ b/arch/arm64/include/asm/cacheflush.h
@@ -0,0 +1,148 @@
+/*
+ * Based on arch/arm/include/asm/cacheflush.h
+ *
+ * Copyright (C) 1999-2002 Russell King.
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_CACHEFLUSH_H
+#define __ASM_CACHEFLUSH_H
+
+#include <linux/mm.h>
+
+/*
+ * This flag is used to indicate that the page pointed to by a pte is clean
+ * and does not require cleaning before returning it to the user.
+ */
+#define PG_dcache_clean PG_arch_1
+
+/*
+ * MM Cache Management
+ * ===================
+ *
+ * The arch/arm64/mm/cache.S implements these methods.
+ *
+ * Start addresses are inclusive and end addresses are exclusive; start
+ * addresses should be rounded down, end addresses up.
+ *
+ * See Documentation/cachetlb.txt for more information. Please note that
+ * the implementation assumes non-aliasing VIPT D-cache and (aliasing)
+ * VIPT or ASID-tagged VIVT I-cache.
+ *
+ * flush_cache_all()
+ *
+ * Unconditionally clean and invalidate the entire cache.
+ *
+ * flush_cache_mm(mm)
+ *
+ * Clean and invalidate all user space cache entries
+ * before a change of page tables.
+ *
+ * flush_icache_range(start, end)
+ *
+ * Ensure coherency between the I-cache and the D-cache in the
+ * region described by start, end.
+ * - start - virtual start address
+ * - end - virtual end address
+ *
+ * __flush_cache_user_range(start, end)
+ *
+ * Ensure coherency between the I-cache and the D-cache in the
+ * region described by start, end.
+ * - start - virtual start address
+ * - end - virtual end address
+ *
+ * __flush_dcache_area(kaddr, size)
+ *
+ * Ensure that the data held in page is written back.
+ * - kaddr - page address
+ * - size - region size
+ */
+extern void flush_cache_all(void);
+extern void flush_cache_mm(struct mm_struct *mm);
+extern void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
+extern void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr, unsigned long pfn);
+extern void flush_icache_range(unsigned long start, unsigned long end);
+extern void __flush_dcache_area(void *addr, size_t len);
+extern void __flush_cache_user_range(unsigned long start, unsigned long end);
+
+/*
+ * Copy user data from/to a page which is mapped into a different
+ * processes address space. Really, we want to allow our "user
+ * space" model to handle this.
+ */
+extern void copy_to_user_page(struct vm_area_struct *, struct page *,
+ unsigned long, void *, const void *, unsigned long);
+#define copy_from_user_page(vma, page, vaddr, dst, src, len) \
+ do { \
+ memcpy(dst, src, len); \
+ } while (0)
+
+#define flush_cache_dup_mm(mm) flush_cache_mm(mm)
+
+/*
+ * flush_dcache_page is used when the kernel has written to the page
+ * cache page at virtual address page->virtual.
+ *
+ * If this page isn't mapped (ie, page_mapping == NULL), or it might
+ * have userspace mappings, then we _must_ always clean + invalidate
+ * the dcache entries associated with the kernel mapping.
+ *
+ * Otherwise we can defer the operation, and clean the cache when we are
+ * about to change to user space. This is the same method as used on SPARC64.
+ * See update_mmu_cache for the user space part.
+ */
+#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
+extern void flush_dcache_page(struct page *);
+
+static inline void __flush_icache_all(void)
+{
+ asm("ic ialluis");
+}
+
+#define flush_dcache_mmap_lock(mapping) \
+ spin_lock_irq(&(mapping)->tree_lock)
+#define flush_dcache_mmap_unlock(mapping) \
+ spin_unlock_irq(&(mapping)->tree_lock)
+
+#define flush_icache_user_range(vma,page,addr,len) \
+ flush_dcache_page(page)
+
+/*
+ * We don't appear to need to do anything here. In fact, if we did, we'd
+ * duplicate cache flushing elsewhere performed by flush_dcache_page().
+ */
+#define flush_icache_page(vma,page) do { } while (0)
+
+/*
+ * flush_cache_vmap() is used when creating mappings (eg, via vmap,
+ * vmalloc, ioremap etc) in kernel space for pages. On non-VIPT
+ * caches, since the direct-mappings of these pages may contain cached
+ * data, we need to do a full cache flush to ensure that writebacks
+ * don't corrupt data placed into these pages via the new mappings.
+ */
+static inline void flush_cache_vmap(unsigned long start, unsigned long end)
+{
+ /*
+ * set_pte_at() called from vmap_pte_range() does not
+ * have a DSB after cleaning the cache line.
+ */
+ dsb();
+}
+
+static inline void flush_cache_vunmap(unsigned long start, unsigned long end)
+{
+}
+
+#endif
diff --git a/arch/arm64/include/asm/cachetype.h b/arch/arm64/include/asm/cachetype.h
new file mode 100644
index 000000000000..85f5f511352a
--- /dev/null
+++ b/arch/arm64/include/asm/cachetype.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_CACHETYPE_H
+#define __ASM_CACHETYPE_H
+
+#include <asm/cputype.h>
+
+#define CTR_L1IP_SHIFT 14
+#define CTR_L1IP_MASK 3
+
+#define ICACHE_POLICY_RESERVED 0
+#define ICACHE_POLICY_AIVIVT 1
+#define ICACHE_POLICY_VIPT 2
+#define ICACHE_POLICY_PIPT 3
+
+static inline u32 icache_policy(void)
+{
+ return (read_cpuid_cachetype() >> CTR_L1IP_SHIFT) & CTR_L1IP_MASK;
+}
+
+/*
+ * Whilst the D-side always behaves as PIPT on AArch64, aliasing is
+ * permitted in the I-cache.
+ */
+static inline int icache_is_aliasing(void)
+{
+ return icache_policy() != ICACHE_POLICY_PIPT;
+}
+
+static inline int icache_is_aivivt(void)
+{
+ return icache_policy() == ICACHE_POLICY_AIVIVT;
+}
+
+#endif /* __ASM_CACHETYPE_H */
diff --git a/arch/arm64/include/asm/cmpxchg.h b/arch/arm64/include/asm/cmpxchg.h
new file mode 100644
index 000000000000..e0e65b069d9e
--- /dev/null
+++ b/arch/arm64/include/asm/cmpxchg.h
@@ -0,0 +1,173 @@
+/*
+ * Based on arch/arm/include/asm/cmpxchg.h
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_CMPXCHG_H
+#define __ASM_CMPXCHG_H
+
+#include <linux/bug.h>
+
+#include <asm/barrier.h>
+
+static inline unsigned long __xchg(unsigned long x, volatile void *ptr, int size)
+{
+ unsigned long ret, tmp;
+
+ switch (size) {
+ case 1:
+ asm volatile("// __xchg1\n"
+ "1: ldaxrb %w0, [%3]\n"
+ " stlxrb %w1, %w2, [%3]\n"
+ " cbnz %w1, 1b\n"
+ : "=&r" (ret), "=&r" (tmp)
+ : "r" (x), "r" (ptr)
+ : "memory", "cc");
+ break;
+ case 2:
+ asm volatile("// __xchg2\n"
+ "1: ldaxrh %w0, [%3]\n"
+ " stlxrh %w1, %w2, [%3]\n"
+ " cbnz %w1, 1b\n"
+ : "=&r" (ret), "=&r" (tmp)
+ : "r" (x), "r" (ptr)
+ : "memory", "cc");
+ break;
+ case 4:
+ asm volatile("// __xchg4\n"
+ "1: ldaxr %w0, [%3]\n"
+ " stlxr %w1, %w2, [%3]\n"
+ " cbnz %w1, 1b\n"
+ : "=&r" (ret), "=&r" (tmp)
+ : "r" (x), "r" (ptr)
+ : "memory", "cc");
+ break;
+ case 8:
+ asm volatile("// __xchg8\n"
+ "1: ldaxr %0, [%3]\n"
+ " stlxr %w1, %2, [%3]\n"
+ " cbnz %w1, 1b\n"
+ : "=&r" (ret), "=&r" (tmp)
+ : "r" (x), "r" (ptr)
+ : "memory", "cc");
+ break;
+ default:
+ BUILD_BUG();
+ }
+
+ return ret;
+}
+
+#define xchg(ptr,x) \
+ ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
+
+static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
+ unsigned long new, int size)
+{
+ unsigned long oldval = 0, res;
+
+ switch (size) {
+ case 1:
+ do {
+ asm volatile("// __cmpxchg1\n"
+ " ldxrb %w1, [%2]\n"
+ " mov %w0, #0\n"
+ " cmp %w1, %w3\n"
+ " b.ne 1f\n"
+ " stxrb %w0, %w4, [%2]\n"
+ "1:\n"
+ : "=&r" (res), "=&r" (oldval)
+ : "r" (ptr), "Ir" (old), "r" (new)
+ : "cc");
+ } while (res);
+ break;
+
+ case 2:
+ do {
+ asm volatile("// __cmpxchg2\n"
+ " ldxrh %w1, [%2]\n"
+ " mov %w0, #0\n"
+ " cmp %w1, %w3\n"
+ " b.ne 1f\n"
+ " stxrh %w0, %w4, [%2]\n"
+ "1:\n"
+ : "=&r" (res), "=&r" (oldval)
+ : "r" (ptr), "Ir" (old), "r" (new)
+ : "memory", "cc");
+ } while (res);
+ break;
+
+ case 4:
+ do {
+ asm volatile("// __cmpxchg4\n"
+ " ldxr %w1, [%2]\n"
+ " mov %w0, #0\n"
+ " cmp %w1, %w3\n"
+ " b.ne 1f\n"
+ " stxr %w0, %w4, [%2]\n"
+ "1:\n"
+ : "=&r" (res), "=&r" (oldval)
+ : "r" (ptr), "Ir" (old), "r" (new)
+ : "cc");
+ } while (res);
+ break;
+
+ case 8:
+ do {
+ asm volatile("// __cmpxchg8\n"
+ " ldxr %1, [%2]\n"
+ " mov %w0, #0\n"
+ " cmp %1, %3\n"
+ " b.ne 1f\n"
+ " stxr %w0, %4, [%2]\n"
+ "1:\n"
+ : "=&r" (res), "=&r" (oldval)
+ : "r" (ptr), "Ir" (old), "r" (new)
+ : "cc");
+ } while (res);
+ break;
+
+ default:
+ BUILD_BUG();
+ }
+
+ return oldval;
+}
+
+static inline unsigned long __cmpxchg_mb(volatile void *ptr, unsigned long old,
+ unsigned long new, int size)
+{
+ unsigned long ret;
+
+ smp_mb();
+ ret = __cmpxchg(ptr, old, new, size);
+ smp_mb();
+
+ return ret;
+}
+
+#define cmpxchg(ptr,o,n) \
+ ((__typeof__(*(ptr)))__cmpxchg_mb((ptr), \
+ (unsigned long)(o), \
+ (unsigned long)(n), \
+ sizeof(*(ptr))))
+
+#define cmpxchg_local(ptr,o,n) \
+ ((__typeof__(*(ptr)))__cmpxchg((ptr), \
+ (unsigned long)(o), \
+ (unsigned long)(n), \
+ sizeof(*(ptr))))
+
+#endif /* __ASM_CMPXCHG_H */
diff --git a/arch/arm64/include/asm/compat.h b/arch/arm64/include/asm/compat.h
new file mode 100644
index 000000000000..a670a33ad736
--- /dev/null
+++ b/arch/arm64/include/asm/compat.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_COMPAT_H
+#define __ASM_COMPAT_H
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+
+/*
+ * Architecture specific compatibility types
+ */
+#include <linux/types.h>
+#include <linux/sched.h>
+
+#define COMPAT_USER_HZ 100
+#define COMPAT_UTS_MACHINE "armv8l\0\0"
+
+typedef u32 compat_size_t;
+typedef s32 compat_ssize_t;
+typedef s32 compat_time_t;
+typedef s32 compat_clock_t;
+typedef s32 compat_pid_t;
+typedef u32 __compat_uid_t;
+typedef u32 __compat_gid_t;
+typedef u32 __compat_uid32_t;
+typedef u32 __compat_gid32_t;
+typedef u32 compat_mode_t;
+typedef u32 compat_ino_t;
+typedef u32 compat_dev_t;
+typedef s32 compat_off_t;
+typedef s64 compat_loff_t;
+typedef s16 compat_nlink_t;
+typedef u16 compat_ipc_pid_t;
+typedef s32 compat_daddr_t;
+typedef u32 compat_caddr_t;
+typedef __kernel_fsid_t compat_fsid_t;
+typedef s32 compat_key_t;
+typedef s32 compat_timer_t;
+
+typedef s32 compat_int_t;
+typedef s32 compat_long_t;
+typedef s64 compat_s64;
+typedef u32 compat_uint_t;
+typedef u32 compat_ulong_t;
+typedef u64 compat_u64;
+
+struct compat_timespec {
+ compat_time_t tv_sec;
+ s32 tv_nsec;
+};
+
+struct compat_timeval {
+ compat_time_t tv_sec;
+ s32 tv_usec;
+};
+
+struct compat_stat {
+ compat_dev_t st_dev;
+ compat_ino_t st_ino;
+ compat_mode_t st_mode;
+ compat_nlink_t st_nlink;
+ __compat_uid32_t st_uid;
+ __compat_gid32_t st_gid;
+ compat_dev_t st_rdev;
+ compat_off_t st_size;
+ compat_off_t st_blksize;
+ compat_off_t st_blocks;
+ compat_time_t st_atime;
+ u32 st_atime_nsec;
+ compat_time_t st_mtime;
+ u32 st_mtime_nsec;
+ compat_time_t st_ctime;
+ u32 st_ctime_nsec;
+ u32 __unused4[2];
+};
+
+struct compat_flock {
+ short l_type;
+ short l_whence;
+ compat_off_t l_start;
+ compat_off_t l_len;
+ compat_pid_t l_pid;
+};
+
+#define F_GETLK64 12 /* using 'struct flock64' */
+#define F_SETLK64 13
+#define F_SETLKW64 14
+
+struct compat_flock64 {
+ short l_type;
+ short l_whence;
+ compat_loff_t l_start;
+ compat_loff_t l_len;
+ compat_pid_t l_pid;
+};
+
+struct compat_statfs {
+ int f_type;
+ int f_bsize;
+ int f_blocks;
+ int f_bfree;
+ int f_bavail;
+ int f_files;
+ int f_ffree;
+ compat_fsid_t f_fsid;
+ int f_namelen; /* SunOS ignores this field. */
+ int f_frsize;
+ int f_flags;
+ int f_spare[4];
+};
+
+#define COMPAT_RLIM_INFINITY 0xffffffff
+
+typedef u32 compat_old_sigset_t;
+
+#define _COMPAT_NSIG 64
+#define _COMPAT_NSIG_BPW 32
+
+typedef u32 compat_sigset_word;
+
+#define COMPAT_OFF_T_MAX 0x7fffffff
+#define COMPAT_LOFF_T_MAX 0x7fffffffffffffffL
+
+/*
+ * A pointer passed in from user mode. This should not
+ * be used for syscall parameters, just declare them
+ * as pointers because the syscall entry code will have
+ * appropriately converted them already.
+ */
+typedef u32 compat_uptr_t;
+
+static inline void __user *compat_ptr(compat_uptr_t uptr)
+{
+ return (void __user *)(unsigned long)uptr;
+}
+
+static inline compat_uptr_t ptr_to_compat(void __user *uptr)
+{
+ return (u32)(unsigned long)uptr;
+}
+
+static inline void __user *arch_compat_alloc_user_space(long len)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+ return (void __user *)regs->compat_sp - len;
+}
+
+struct compat_ipc64_perm {
+ compat_key_t key;
+ __compat_uid32_t uid;
+ __compat_gid32_t gid;
+ __compat_uid32_t cuid;
+ __compat_gid32_t cgid;
+ unsigned short mode;
+ unsigned short __pad1;
+ unsigned short seq;
+ unsigned short __pad2;
+ compat_ulong_t unused1;
+ compat_ulong_t unused2;
+};
+
+struct compat_semid64_ds {
+ struct compat_ipc64_perm sem_perm;
+ compat_time_t sem_otime;
+ compat_ulong_t __unused1;
+ compat_time_t sem_ctime;
+ compat_ulong_t __unused2;
+ compat_ulong_t sem_nsems;
+ compat_ulong_t __unused3;
+ compat_ulong_t __unused4;
+};
+
+struct compat_msqid64_ds {
+ struct compat_ipc64_perm msg_perm;
+ compat_time_t msg_stime;
+ compat_ulong_t __unused1;
+ compat_time_t msg_rtime;
+ compat_ulong_t __unused2;
+ compat_time_t msg_ctime;
+ compat_ulong_t __unused3;
+ compat_ulong_t msg_cbytes;
+ compat_ulong_t msg_qnum;
+ compat_ulong_t msg_qbytes;
+ compat_pid_t msg_lspid;
+ compat_pid_t msg_lrpid;
+ compat_ulong_t __unused4;
+ compat_ulong_t __unused5;
+};
+
+struct compat_shmid64_ds {
+ struct compat_ipc64_perm shm_perm;
+ compat_size_t shm_segsz;
+ compat_time_t shm_atime;
+ compat_ulong_t __unused1;
+ compat_time_t shm_dtime;
+ compat_ulong_t __unused2;
+ compat_time_t shm_ctime;
+ compat_ulong_t __unused3;
+ compat_pid_t shm_cpid;
+ compat_pid_t shm_lpid;
+ compat_ulong_t shm_nattch;
+ compat_ulong_t __unused4;
+ compat_ulong_t __unused5;
+};
+
+static inline int is_compat_task(void)
+{
+ return test_thread_flag(TIF_32BIT);
+}
+
+static inline int is_compat_thread(struct thread_info *thread)
+{
+ return test_ti_thread_flag(thread, TIF_32BIT);
+}
+
+#else /* !CONFIG_COMPAT */
+
+static inline int is_compat_task(void)
+{
+ return 0;
+}
+
+static inline int is_compat_thread(struct thread_info *thread)
+{
+ return 0;
+}
+
+#endif /* CONFIG_COMPAT */
+#endif /* __KERNEL__ */
+#endif /* __ASM_COMPAT_H */
diff --git a/arch/arm64/include/asm/compiler.h b/arch/arm64/include/asm/compiler.h
new file mode 100644
index 000000000000..ee35fd0f2236
--- /dev/null
+++ b/arch/arm64/include/asm/compiler.h
@@ -0,0 +1,30 @@
+/*
+ * Based on arch/arm/include/asm/compiler.h
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_COMPILER_H
+#define __ASM_COMPILER_H
+
+/*
+ * This is used to ensure the compiler did actually allocate the register we
+ * asked it for some inline assembly sequences. Apparently we can't trust the
+ * compiler from one version to another so a bit of paranoia won't hurt. This
+ * string is meant to be concatenated with the inline asm string and will
+ * cause compilation to stop on mismatch. (for details, see gcc PR 15089)
+ */
+#define __asmeq(x, y) ".ifnc " x "," y " ; .err ; .endif\n\t"
+
+#endif /* __ASM_COMPILER_H */
diff --git a/arch/arm64/include/asm/cputable.h b/arch/arm64/include/asm/cputable.h
new file mode 100644
index 000000000000..e3bd983d3661
--- /dev/null
+++ b/arch/arm64/include/asm/cputable.h
@@ -0,0 +1,30 @@
+/*
+ * arch/arm64/include/asm/cputable.h
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_CPUTABLE_H
+#define __ASM_CPUTABLE_H
+
+struct cpu_info {
+ unsigned int cpu_id_val;
+ unsigned int cpu_id_mask;
+ const char *cpu_name;
+ unsigned long (*cpu_setup)(void);
+};
+
+extern struct cpu_info *lookup_processor_type(unsigned int);
+
+#endif
diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h
new file mode 100644
index 000000000000..ef54125e6c1e
--- /dev/null
+++ b/arch/arm64/include/asm/cputype.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_CPUTYPE_H
+#define __ASM_CPUTYPE_H
+
+#define ID_MIDR_EL1 "midr_el1"
+#define ID_CTR_EL0 "ctr_el0"
+
+#define ID_AA64PFR0_EL1 "id_aa64pfr0_el1"
+#define ID_AA64DFR0_EL1 "id_aa64dfr0_el1"
+#define ID_AA64AFR0_EL1 "id_aa64afr0_el1"
+#define ID_AA64ISAR0_EL1 "id_aa64isar0_el1"
+#define ID_AA64MMFR0_EL1 "id_aa64mmfr0_el1"
+
+#define read_cpuid(reg) ({ \
+ u64 __val; \
+ asm("mrs %0, " reg : "=r" (__val)); \
+ __val; \
+})
+
+/*
+ * The CPU ID never changes at run time, so we might as well tell the
+ * compiler that it's constant. Use this function to read the CPU ID
+ * rather than directly reading processor_id or read_cpuid() directly.
+ */
+static inline u32 __attribute_const__ read_cpuid_id(void)
+{
+ return read_cpuid(ID_MIDR_EL1);
+}
+
+static inline u32 __attribute_const__ read_cpuid_cachetype(void)
+{
+ return read_cpuid(ID_CTR_EL0);
+}
+
+#endif
diff --git a/arch/arm64/include/asm/debug-monitors.h b/arch/arm64/include/asm/debug-monitors.h
new file mode 100644
index 000000000000..7eaa0b302493
--- /dev/null
+++ b/arch/arm64/include/asm/debug-monitors.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_DEBUG_MONITORS_H
+#define __ASM_DEBUG_MONITORS_H
+
+#ifdef __KERNEL__
+
+#define DBG_ESR_EVT(x) (((x) >> 27) & 0x7)
+
+/* AArch64 */
+#define DBG_ESR_EVT_HWBP 0x0
+#define DBG_ESR_EVT_HWSS 0x1
+#define DBG_ESR_EVT_HWWP 0x2
+#define DBG_ESR_EVT_BRK 0x6
+
+enum debug_el {
+ DBG_ACTIVE_EL0 = 0,
+ DBG_ACTIVE_EL1,
+};
+
+/* AArch32 */
+#define DBG_ESR_EVT_BKPT 0x4
+#define DBG_ESR_EVT_VECC 0x5
+
+#define AARCH32_BREAK_ARM 0x07f001f0
+#define AARCH32_BREAK_THUMB 0xde01
+#define AARCH32_BREAK_THUMB2_LO 0xf7f0
+#define AARCH32_BREAK_THUMB2_HI 0xa000
+
+#ifndef __ASSEMBLY__
+struct task_struct;
+
+#define local_dbg_save(flags) \
+ do { \
+ typecheck(unsigned long, flags); \
+ asm volatile( \
+ "mrs %0, daif // local_dbg_save\n" \
+ "msr daifset, #8" \
+ : "=r" (flags) : : "memory"); \
+ } while (0)
+
+#define local_dbg_restore(flags) \
+ do { \
+ typecheck(unsigned long, flags); \
+ asm volatile( \
+ "msr daif, %0 // local_dbg_restore\n" \
+ : : "r" (flags) : "memory"); \
+ } while (0)
+
+#define DBG_ARCH_ID_RESERVED 0 /* In case of ptrace ABI updates. */
+
+u8 debug_monitors_arch(void);
+
+void enable_debug_monitors(enum debug_el el);
+void disable_debug_monitors(enum debug_el el);
+
+void user_rewind_single_step(struct task_struct *task);
+void user_fastforward_single_step(struct task_struct *task);
+
+void kernel_enable_single_step(struct pt_regs *regs);
+void kernel_disable_single_step(void);
+int kernel_active_single_step(void);
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+int reinstall_suspended_bps(struct pt_regs *regs);
+#else
+static inline int reinstall_suspended_bps(struct pt_regs *regs)
+{
+ return -ENODEV;
+}
+#endif
+
+#endif /* __ASSEMBLY */
+#endif /* __KERNEL__ */
+#endif /* __ASM_DEBUG_MONITORS_H */
diff --git a/arch/arm64/include/asm/device.h b/arch/arm64/include/asm/device.h
new file mode 100644
index 000000000000..0d8453c755a8
--- /dev/null
+++ b/arch/arm64/include/asm/device.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_DEVICE_H
+#define __ASM_DEVICE_H
+
+struct dev_archdata {
+ struct dma_map_ops *dma_ops;
+};
+
+struct pdev_archdata {
+};
+
+#endif
diff --git a/arch/arm64/include/asm/dma-mapping.h b/arch/arm64/include/asm/dma-mapping.h
new file mode 100644
index 000000000000..538f4b44db5d
--- /dev/null
+++ b/arch/arm64/include/asm/dma-mapping.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_DMA_MAPPING_H
+#define __ASM_DMA_MAPPING_H
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+
+#include <asm-generic/dma-coherent.h>
+
+#define ARCH_HAS_DMA_GET_REQUIRED_MASK
+
+extern struct dma_map_ops *dma_ops;
+
+static inline struct dma_map_ops *get_dma_ops(struct device *dev)
+{
+ if (unlikely(!dev) || !dev->archdata.dma_ops)
+ return dma_ops;
+ else
+ return dev->archdata.dma_ops;
+}
+
+#include <asm-generic/dma-mapping-common.h>
+
+static inline dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
+{
+ return (dma_addr_t)paddr;
+}
+
+static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dev_addr)
+{
+ return (phys_addr_t)dev_addr;
+}
+
+static inline int dma_mapping_error(struct device *dev, dma_addr_t dev_addr)
+{
+ struct dma_map_ops *ops = get_dma_ops(dev);
+ return ops->mapping_error(dev, dev_addr);
+}
+
+static inline int dma_supported(struct device *dev, u64 mask)
+{
+ struct dma_map_ops *ops = get_dma_ops(dev);
+ return ops->dma_supported(dev, mask);
+}
+
+static inline int dma_set_mask(struct device *dev, u64 mask)
+{
+ if (!dev->dma_mask || !dma_supported(dev, mask))
+ return -EIO;
+ *dev->dma_mask = mask;
+
+ return 0;
+}
+
+static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size)
+{
+ if (!dev->dma_mask)
+ return 0;
+
+ return addr + size - 1 <= *dev->dma_mask;
+}
+
+static inline void dma_mark_clean(void *addr, size_t size)
+{
+}
+
+static inline void *dma_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flags)
+{
+ struct dma_map_ops *ops = get_dma_ops(dev);
+ void *vaddr;
+
+ if (dma_alloc_from_coherent(dev, size, dma_handle, &vaddr))
+ return vaddr;
+
+ vaddr = ops->alloc(dev, size, dma_handle, flags, NULL);
+ debug_dma_alloc_coherent(dev, size, *dma_handle, vaddr);
+ return vaddr;
+}
+
+static inline void dma_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dev_addr)
+{
+ struct dma_map_ops *ops = get_dma_ops(dev);
+
+ if (dma_release_from_coherent(dev, get_order(size), vaddr))
+ return;
+
+ debug_dma_free_coherent(dev, size, vaddr, dev_addr);
+ ops->free(dev, size, vaddr, dev_addr, NULL);
+}
+
+/*
+ * There is no dma_cache_sync() implementation, so just return NULL here.
+ */
+static inline void *dma_alloc_noncoherent(struct device *dev, size_t size,
+ dma_addr_t *handle, gfp_t flags)
+{
+ return NULL;
+}
+
+static inline void dma_free_noncoherent(struct device *dev, size_t size,
+ void *cpu_addr, dma_addr_t handle)
+{
+}
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_DMA_MAPPING_H */
diff --git a/arch/arm64/include/asm/elf.h b/arch/arm64/include/asm/elf.h
new file mode 100644
index 000000000000..cf284649dfcb
--- /dev/null
+++ b/arch/arm64/include/asm/elf.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_ELF_H
+#define __ASM_ELF_H
+
+#include <asm/hwcap.h>
+
+/*
+ * ELF register definitions..
+ */
+#include <asm/ptrace.h>
+#include <asm/user.h>
+
+typedef unsigned long elf_greg_t;
+typedef unsigned long elf_freg_t[3];
+
+#define ELF_NGREG (sizeof (struct pt_regs) / sizeof(elf_greg_t))
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
+
+typedef struct user_fp elf_fpregset_t;
+
+#define EM_AARCH64 183
+
+/*
+ * AArch64 static relocation types.
+ */
+
+/* Miscellaneous. */
+#define R_ARM_NONE 0
+#define R_AARCH64_NONE 256
+
+/* Data. */
+#define R_AARCH64_ABS64 257
+#define R_AARCH64_ABS32 258
+#define R_AARCH64_ABS16 259
+#define R_AARCH64_PREL64 260
+#define R_AARCH64_PREL32 261
+#define R_AARCH64_PREL16 262
+
+/* Instructions. */
+#define R_AARCH64_MOVW_UABS_G0 263
+#define R_AARCH64_MOVW_UABS_G0_NC 264
+#define R_AARCH64_MOVW_UABS_G1 265
+#define R_AARCH64_MOVW_UABS_G1_NC 266
+#define R_AARCH64_MOVW_UABS_G2 267
+#define R_AARCH64_MOVW_UABS_G2_NC 268
+#define R_AARCH64_MOVW_UABS_G3 269
+
+#define R_AARCH64_MOVW_SABS_G0 270
+#define R_AARCH64_MOVW_SABS_G1 271
+#define R_AARCH64_MOVW_SABS_G2 272
+
+#define R_AARCH64_LD_PREL_LO19 273
+#define R_AARCH64_ADR_PREL_LO21 274
+#define R_AARCH64_ADR_PREL_PG_HI21 275
+#define R_AARCH64_ADR_PREL_PG_HI21_NC 276
+#define R_AARCH64_ADD_ABS_LO12_NC 277
+#define R_AARCH64_LDST8_ABS_LO12_NC 278
+
+#define R_AARCH64_TSTBR14 279
+#define R_AARCH64_CONDBR19 280
+#define R_AARCH64_JUMP26 282
+#define R_AARCH64_CALL26 283
+#define R_AARCH64_LDST16_ABS_LO12_NC 284
+#define R_AARCH64_LDST32_ABS_LO12_NC 285
+#define R_AARCH64_LDST64_ABS_LO12_NC 286
+#define R_AARCH64_LDST128_ABS_LO12_NC 299
+
+#define R_AARCH64_MOVW_PREL_G0 287
+#define R_AARCH64_MOVW_PREL_G0_NC 288
+#define R_AARCH64_MOVW_PREL_G1 289
+#define R_AARCH64_MOVW_PREL_G1_NC 290
+#define R_AARCH64_MOVW_PREL_G2 291
+#define R_AARCH64_MOVW_PREL_G2_NC 292
+#define R_AARCH64_MOVW_PREL_G3 293
+
+
+/*
+ * These are used to set parameters in the core dumps.
+ */
+#define ELF_CLASS ELFCLASS64
+#define ELF_DATA ELFDATA2LSB
+#define ELF_ARCH EM_AARCH64
+
+#define ELF_PLATFORM_SIZE 16
+#define ELF_PLATFORM ("aarch64")
+
+/*
+ * This is used to ensure we don't load something for the wrong architecture.
+ */
+#define elf_check_arch(x) ((x)->e_machine == EM_AARCH64)
+
+#define elf_read_implies_exec(ex,stk) (stk != EXSTACK_DISABLE_X)
+
+#define CORE_DUMP_USE_REGSET
+#define ELF_EXEC_PAGESIZE PAGE_SIZE
+
+/*
+ * This is the location that an ET_DYN program is loaded if exec'ed. Typical
+ * use of this is to invoke "./ld.so someprog" to test out a new version of
+ * the loader. We need to make sure that it is out of the way of the program
+ * that it will "exec", and that there is sufficient room for the brk.
+ */
+extern unsigned long randomize_et_dyn(unsigned long base);
+#define ELF_ET_DYN_BASE (randomize_et_dyn(2 * TASK_SIZE_64 / 3))
+
+/*
+ * When the program starts, a1 contains a pointer to a function to be
+ * registered with atexit, as per the SVR4 ABI. A value of 0 means we have no
+ * such handler.
+ */
+#define ELF_PLAT_INIT(_r, load_addr) (_r)->regs[0] = 0
+
+#define SET_PERSONALITY(ex) clear_thread_flag(TIF_32BIT);
+
+#define ARCH_DLINFO \
+do { \
+ NEW_AUX_ENT(AT_SYSINFO_EHDR, \
+ (elf_addr_t)current->mm->context.vdso); \
+} while (0)
+
+#define ARCH_HAS_SETUP_ADDITIONAL_PAGES
+struct linux_binprm;
+extern int arch_setup_additional_pages(struct linux_binprm *bprm,
+ int uses_interp);
+
+/* 1GB of VA */
+#ifdef CONFIG_COMPAT
+#define STACK_RND_MASK (test_thread_flag(TIF_32BIT) ? \
+ 0x7ff >> (PAGE_SHIFT - 12) : \
+ 0x3ffff >> (PAGE_SHIFT - 12))
+#else
+#define STACK_RND_MASK (0x3ffff >> (PAGE_SHIFT - 12))
+#endif
+
+struct mm_struct;
+extern unsigned long arch_randomize_brk(struct mm_struct *mm);
+#define arch_randomize_brk arch_randomize_brk
+
+#ifdef CONFIG_COMPAT
+#define EM_ARM 40
+#define COMPAT_ELF_PLATFORM ("v8l")
+
+#define COMPAT_ELF_ET_DYN_BASE (randomize_et_dyn(2 * TASK_SIZE_32 / 3))
+
+/* AArch32 registers. */
+#define COMPAT_ELF_NGREG 18
+typedef unsigned int compat_elf_greg_t;
+typedef compat_elf_greg_t compat_elf_gregset_t[COMPAT_ELF_NGREG];
+
+/* AArch32 EABI. */
+#define EF_ARM_EABI_MASK 0xff000000
+#define compat_elf_check_arch(x) (((x)->e_machine == EM_ARM) && \
+ ((x)->e_flags & EF_ARM_EABI_MASK))
+
+#define compat_start_thread compat_start_thread
+#define COMPAT_SET_PERSONALITY(ex) set_thread_flag(TIF_32BIT);
+#define COMPAT_ARCH_DLINFO
+extern int aarch32_setup_vectors_page(struct linux_binprm *bprm,
+ int uses_interp);
+#define compat_arch_setup_additional_pages \
+ aarch32_setup_vectors_page
+
+#endif /* CONFIG_COMPAT */
+
+#endif
diff --git a/arch/arm64/include/asm/exception.h b/arch/arm64/include/asm/exception.h
new file mode 100644
index 000000000000..ac63519b7b90
--- /dev/null
+++ b/arch/arm64/include/asm/exception.h
@@ -0,0 +1,23 @@
+/*
+ * Based on arch/arm/include/asm/exception.h
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_EXCEPTION_H
+#define __ASM_EXCEPTION_H
+
+#define __exception __attribute__((section(".exception.text")))
+
+#endif /* __ASM_EXCEPTION_H */
diff --git a/arch/arm64/include/asm/exec.h b/arch/arm64/include/asm/exec.h
new file mode 100644
index 000000000000..db0563c23482
--- /dev/null
+++ b/arch/arm64/include/asm/exec.h
@@ -0,0 +1,23 @@
+/*
+ * Based on arch/arm/include/asm/exec.h
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_EXEC_H
+#define __ASM_EXEC_H
+
+extern unsigned long arch_align_stack(unsigned long sp);
+
+#endif /* __ASM_EXEC_H */
diff --git a/arch/arm64/include/asm/fb.h b/arch/arm64/include/asm/fb.h
new file mode 100644
index 000000000000..adb88a64b2fe
--- /dev/null
+++ b/arch/arm64/include/asm/fb.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_FB_H_
+#define __ASM_FB_H_
+
+#include <linux/fb.h>
+#include <linux/fs.h>
+#include <asm/page.h>
+
+static inline void fb_pgprotect(struct file *file, struct vm_area_struct *vma,
+ unsigned long off)
+{
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+}
+
+static inline int fb_is_primary_device(struct fb_info *info)
+{
+ return 0;
+}
+
+#endif /* __ASM_FB_H_ */
diff --git a/arch/arm64/include/asm/fcntl.h b/arch/arm64/include/asm/fcntl.h
new file mode 100644
index 000000000000..cd2e630c235e
--- /dev/null
+++ b/arch/arm64/include/asm/fcntl.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_FCNTL_H
+#define __ASM_FCNTL_H
+
+/*
+ * Using our own definitions for AArch32 (compat) support.
+ */
+#define O_DIRECTORY 040000 /* must be a directory */
+#define O_NOFOLLOW 0100000 /* don't follow links */
+#define O_DIRECT 0200000 /* direct disk access hint - currently ignored */
+#define O_LARGEFILE 0400000
+
+#include <asm-generic/fcntl.h>
+
+#endif
diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h
new file mode 100644
index 000000000000..b42fab9f62a9
--- /dev/null
+++ b/arch/arm64/include/asm/fpsimd.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_FP_H
+#define __ASM_FP_H
+
+#include <asm/ptrace.h>
+
+#ifndef __ASSEMBLY__
+
+/*
+ * FP/SIMD storage area has:
+ * - FPSR and FPCR
+ * - 32 128-bit data registers
+ *
+ * Note that user_fp forms a prefix of this structure, which is relied
+ * upon in the ptrace FP/SIMD accessors. struct user_fpsimd_state must
+ * form a prefix of struct fpsimd_state.
+ */
+struct fpsimd_state {
+ union {
+ struct user_fpsimd_state user_fpsimd;
+ struct {
+ __uint128_t vregs[32];
+ u32 fpsr;
+ u32 fpcr;
+ };
+ };
+};
+
+#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
+/* Masks for extracting the FPSR and FPCR from the FPSCR */
+#define VFP_FPSCR_STAT_MASK 0xf800009f
+#define VFP_FPSCR_CTRL_MASK 0x07f79f00
+/*
+ * The VFP state has 32x64-bit registers and a single 32-bit
+ * control/status register.
+ */
+#define VFP_STATE_SIZE ((32 * 8) + 4)
+#endif
+
+struct task_struct;
+
+extern void fpsimd_save_state(struct fpsimd_state *state);
+extern void fpsimd_load_state(struct fpsimd_state *state);
+
+extern void fpsimd_thread_switch(struct task_struct *next);
+extern void fpsimd_flush_thread(void);
+
+#endif
+
+#endif
diff --git a/arch/arm64/include/asm/futex.h b/arch/arm64/include/asm/futex.h
new file mode 100644
index 000000000000..3468ae8439fa
--- /dev/null
+++ b/arch/arm64/include/asm/futex.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_FUTEX_H
+#define __ASM_FUTEX_H
+
+#ifdef __KERNEL__
+
+#include <linux/futex.h>
+#include <linux/uaccess.h>
+#include <asm/errno.h>
+
+#define __futex_atomic_op(insn, ret, oldval, uaddr, tmp, oparg) \
+ asm volatile( \
+"1: ldaxr %w1, %2\n" \
+ insn "\n" \
+"2: stlxr %w3, %w0, %2\n" \
+" cbnz %w3, 1b\n" \
+"3:\n" \
+" .pushsection .fixup,\"ax\"\n" \
+"4: mov %w0, %w5\n" \
+" b 3b\n" \
+" .popsection\n" \
+" .pushsection __ex_table,\"a\"\n" \
+" .align 3\n" \
+" .quad 1b, 4b, 2b, 4b\n" \
+" .popsection\n" \
+ : "=&r" (ret), "=&r" (oldval), "+Q" (*uaddr), "=&r" (tmp) \
+ : "r" (oparg), "Ir" (-EFAULT) \
+ : "cc")
+
+static inline int
+futex_atomic_op_inuser (int encoded_op, u32 __user *uaddr)
+{
+ int op = (encoded_op >> 28) & 7;
+ int cmp = (encoded_op >> 24) & 15;
+ int oparg = (encoded_op << 8) >> 20;
+ int cmparg = (encoded_op << 20) >> 20;
+ int oldval = 0, ret, tmp;
+
+ if (encoded_op & (FUTEX_OP_OPARG_SHIFT << 28))
+ oparg = 1 << oparg;
+
+ if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
+ return -EFAULT;
+
+ pagefault_disable(); /* implies preempt_disable() */
+
+ switch (op) {
+ case FUTEX_OP_SET:
+ __futex_atomic_op("mov %w0, %w4",
+ ret, oldval, uaddr, tmp, oparg);
+ break;
+ case FUTEX_OP_ADD:
+ __futex_atomic_op("add %w0, %w1, %w4",
+ ret, oldval, uaddr, tmp, oparg);
+ break;
+ case FUTEX_OP_OR:
+ __futex_atomic_op("orr %w0, %w1, %w4",
+ ret, oldval, uaddr, tmp, oparg);
+ break;
+ case FUTEX_OP_ANDN:
+ __futex_atomic_op("and %w0, %w1, %w4",
+ ret, oldval, uaddr, tmp, ~oparg);
+ break;
+ case FUTEX_OP_XOR:
+ __futex_atomic_op("eor %w0, %w1, %w4",
+ ret, oldval, uaddr, tmp, oparg);
+ break;
+ default:
+ ret = -ENOSYS;
+ }
+
+ pagefault_enable(); /* subsumes preempt_enable() */
+
+ if (!ret) {
+ switch (cmp) {
+ case FUTEX_OP_CMP_EQ: ret = (oldval == cmparg); break;
+ case FUTEX_OP_CMP_NE: ret = (oldval != cmparg); break;
+ case FUTEX_OP_CMP_LT: ret = (oldval < cmparg); break;
+ case FUTEX_OP_CMP_GE: ret = (oldval >= cmparg); break;
+ case FUTEX_OP_CMP_LE: ret = (oldval <= cmparg); break;
+ case FUTEX_OP_CMP_GT: ret = (oldval > cmparg); break;
+ default: ret = -ENOSYS;
+ }
+ }
+ return ret;
+}
+
+static inline int
+futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
+ u32 oldval, u32 newval)
+{
+ int ret = 0;
+ u32 val, tmp;
+
+ if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
+ return -EFAULT;
+
+ asm volatile("// futex_atomic_cmpxchg_inatomic\n"
+"1: ldaxr %w1, %2\n"
+" sub %w3, %w1, %w4\n"
+" cbnz %w3, 3f\n"
+"2: stlxr %w3, %w5, %2\n"
+" cbnz %w3, 1b\n"
+"3:\n"
+" .pushsection .fixup,\"ax\"\n"
+"4: mov %w0, %w6\n"
+" b 3b\n"
+" .popsection\n"
+" .pushsection __ex_table,\"a\"\n"
+" .align 3\n"
+" .quad 1b, 4b, 2b, 4b\n"
+" .popsection\n"
+ : "+r" (ret), "=&r" (val), "+Q" (*uaddr), "=&r" (tmp)
+ : "r" (oldval), "r" (newval), "Ir" (-EFAULT)
+ : "cc", "memory");
+
+ *uval = val;
+ return ret;
+}
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_FUTEX_H */
diff --git a/arch/arm64/include/asm/hardirq.h b/arch/arm64/include/asm/hardirq.h
new file mode 100644
index 000000000000..507546353d62
--- /dev/null
+++ b/arch/arm64/include/asm/hardirq.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_HARDIRQ_H
+#define __ASM_HARDIRQ_H
+
+#include <linux/cache.h>
+#include <linux/threads.h>
+#include <asm/irq.h>
+
+#define NR_IPI 4
+
+typedef struct {
+ unsigned int __softirq_pending;
+#ifdef CONFIG_SMP
+ unsigned int ipi_irqs[NR_IPI];
+#endif
+} ____cacheline_aligned irq_cpustat_t;
+
+#include <linux/irq_cpustat.h> /* Standard mappings for irq_cpustat_t above */
+
+#define __inc_irq_stat(cpu, member) __IRQ_STAT(cpu, member)++
+#define __get_irq_stat(cpu, member) __IRQ_STAT(cpu, member)
+
+#ifdef CONFIG_SMP
+u64 smp_irq_stat_cpu(unsigned int cpu);
+#define arch_irq_stat_cpu smp_irq_stat_cpu
+#endif
+
+#define __ARCH_IRQ_EXIT_IRQS_DISABLED 1
+
+static inline void ack_bad_irq(unsigned int irq)
+{
+ extern unsigned long irq_err_count;
+ irq_err_count++;
+}
+
+extern void handle_IRQ(unsigned int, struct pt_regs *);
+
+#endif /* __ASM_HARDIRQ_H */
diff --git a/arch/arm64/include/asm/hw_breakpoint.h b/arch/arm64/include/asm/hw_breakpoint.h
new file mode 100644
index 000000000000..d064047612b1
--- /dev/null
+++ b/arch/arm64/include/asm/hw_breakpoint.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_HW_BREAKPOINT_H
+#define __ASM_HW_BREAKPOINT_H
+
+#ifdef __KERNEL__
+
+struct arch_hw_breakpoint_ctrl {
+ u32 __reserved : 19,
+ len : 8,
+ type : 2,
+ privilege : 2,
+ enabled : 1;
+};
+
+struct arch_hw_breakpoint {
+ u64 address;
+ u64 trigger;
+ struct arch_hw_breakpoint_ctrl ctrl;
+};
+
+static inline u32 encode_ctrl_reg(struct arch_hw_breakpoint_ctrl ctrl)
+{
+ return (ctrl.len << 5) | (ctrl.type << 3) | (ctrl.privilege << 1) |
+ ctrl.enabled;
+}
+
+static inline void decode_ctrl_reg(u32 reg,
+ struct arch_hw_breakpoint_ctrl *ctrl)
+{
+ ctrl->enabled = reg & 0x1;
+ reg >>= 1;
+ ctrl->privilege = reg & 0x3;
+ reg >>= 2;
+ ctrl->type = reg & 0x3;
+ reg >>= 2;
+ ctrl->len = reg & 0xff;
+}
+
+/* Breakpoint */
+#define ARM_BREAKPOINT_EXECUTE 0
+
+/* Watchpoints */
+#define ARM_BREAKPOINT_LOAD 1
+#define ARM_BREAKPOINT_STORE 2
+#define AARCH64_ESR_ACCESS_MASK (1 << 6)
+
+/* Privilege Levels */
+#define AARCH64_BREAKPOINT_EL1 1
+#define AARCH64_BREAKPOINT_EL0 2
+
+/* Lengths */
+#define ARM_BREAKPOINT_LEN_1 0x1
+#define ARM_BREAKPOINT_LEN_2 0x3
+#define ARM_BREAKPOINT_LEN_4 0xf
+#define ARM_BREAKPOINT_LEN_8 0xff
+
+/* Kernel stepping */
+#define ARM_KERNEL_STEP_NONE 0
+#define ARM_KERNEL_STEP_ACTIVE 1
+#define ARM_KERNEL_STEP_SUSPEND 2
+
+/*
+ * Limits.
+ * Changing these will require modifications to the register accessors.
+ */
+#define ARM_MAX_BRP 16
+#define ARM_MAX_WRP 16
+#define ARM_MAX_HBP_SLOTS (ARM_MAX_BRP + ARM_MAX_WRP)
+
+/* Virtual debug register bases. */
+#define AARCH64_DBG_REG_BVR 0
+#define AARCH64_DBG_REG_BCR (AARCH64_DBG_REG_BVR + ARM_MAX_BRP)
+#define AARCH64_DBG_REG_WVR (AARCH64_DBG_REG_BCR + ARM_MAX_BRP)
+#define AARCH64_DBG_REG_WCR (AARCH64_DBG_REG_WVR + ARM_MAX_WRP)
+
+/* Debug register names. */
+#define AARCH64_DBG_REG_NAME_BVR "bvr"
+#define AARCH64_DBG_REG_NAME_BCR "bcr"
+#define AARCH64_DBG_REG_NAME_WVR "wvr"
+#define AARCH64_DBG_REG_NAME_WCR "wcr"
+
+/* Accessor macros for the debug registers. */
+#define AARCH64_DBG_READ(N, REG, VAL) do {\
+ asm volatile("mrs %0, dbg" REG #N "_el1" : "=r" (VAL));\
+} while (0)
+
+#define AARCH64_DBG_WRITE(N, REG, VAL) do {\
+ asm volatile("msr dbg" REG #N "_el1, %0" :: "r" (VAL));\
+} while (0)
+
+struct task_struct;
+struct notifier_block;
+struct perf_event;
+struct pmu;
+
+extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
+ int *gen_len, int *gen_type);
+extern int arch_check_bp_in_kernelspace(struct perf_event *bp);
+extern int arch_validate_hwbkpt_settings(struct perf_event *bp);
+extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
+ unsigned long val, void *data);
+
+extern int arch_install_hw_breakpoint(struct perf_event *bp);
+extern void arch_uninstall_hw_breakpoint(struct perf_event *bp);
+extern void hw_breakpoint_pmu_read(struct perf_event *bp);
+extern int hw_breakpoint_slots(int type);
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+extern void hw_breakpoint_thread_switch(struct task_struct *next);
+extern void ptrace_hw_copy_thread(struct task_struct *task);
+#else
+static inline void hw_breakpoint_thread_switch(struct task_struct *next)
+{
+}
+static inline void ptrace_hw_copy_thread(struct task_struct *task)
+{
+}
+#endif
+
+extern struct pmu perf_ops_bp;
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_BREAKPOINT_H */
diff --git a/arch/arm64/include/asm/hwcap.h b/arch/arm64/include/asm/hwcap.h
new file mode 100644
index 000000000000..f8190ba45a3e
--- /dev/null
+++ b/arch/arm64/include/asm/hwcap.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_HWCAP_H
+#define __ASM_HWCAP_H
+
+/*
+ * HWCAP flags - for elf_hwcap (in kernel) and AT_HWCAP
+ */
+#define HWCAP_FP (1 << 0)
+#define HWCAP_ASIMD (1 << 1)
+
+#define COMPAT_HWCAP_HALF (1 << 1)
+#define COMPAT_HWCAP_THUMB (1 << 2)
+#define COMPAT_HWCAP_FAST_MULT (1 << 4)
+#define COMPAT_HWCAP_VFP (1 << 6)
+#define COMPAT_HWCAP_EDSP (1 << 7)
+#define COMPAT_HWCAP_NEON (1 << 12)
+#define COMPAT_HWCAP_VFPv3 (1 << 13)
+#define COMPAT_HWCAP_TLS (1 << 15)
+#define COMPAT_HWCAP_VFPv4 (1 << 16)
+#define COMPAT_HWCAP_IDIVA (1 << 17)
+#define COMPAT_HWCAP_IDIVT (1 << 18)
+#define COMPAT_HWCAP_IDIV (COMPAT_HWCAP_IDIVA|COMPAT_HWCAP_IDIVT)
+
+#if defined(__KERNEL__) && !defined(__ASSEMBLY__)
+/*
+ * This yields a mask that user programs can use to figure out what
+ * instruction set this cpu supports.
+ */
+#define ELF_HWCAP (elf_hwcap)
+#define COMPAT_ELF_HWCAP (COMPAT_HWCAP_HALF|COMPAT_HWCAP_THUMB|\
+ COMPAT_HWCAP_FAST_MULT|COMPAT_HWCAP_EDSP|\
+ COMPAT_HWCAP_TLS|COMPAT_HWCAP_VFP|\
+ COMPAT_HWCAP_VFPv3|COMPAT_HWCAP_VFPv4|\
+ COMPAT_HWCAP_NEON|COMPAT_HWCAP_IDIV)
+
+extern unsigned int elf_hwcap;
+#endif
+
+#endif
diff --git a/arch/arm64/include/asm/io.h b/arch/arm64/include/asm/io.h
new file mode 100644
index 000000000000..74a2a7d304a9
--- /dev/null
+++ b/arch/arm64/include/asm/io.h
@@ -0,0 +1,258 @@
+/*
+ * Based on arch/arm/include/asm/io.h
+ *
+ * Copyright (C) 1996-2000 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_IO_H
+#define __ASM_IO_H
+
+#ifdef __KERNEL__
+
+#include <linux/types.h>
+
+#include <asm/byteorder.h>
+#include <asm/barrier.h>
+#include <asm/pgtable.h>
+
+/*
+ * Generic IO read/write. These perform native-endian accesses.
+ */
+static inline void __raw_writeb(u8 val, volatile void __iomem *addr)
+{
+ asm volatile("strb %w0, [%1]" : : "r" (val), "r" (addr));
+}
+
+static inline void __raw_writew(u16 val, volatile void __iomem *addr)
+{
+ asm volatile("strh %w0, [%1]" : : "r" (val), "r" (addr));
+}
+
+static inline void __raw_writel(u32 val, volatile void __iomem *addr)
+{
+ asm volatile("str %w0, [%1]" : : "r" (val), "r" (addr));
+}
+
+static inline void __raw_writeq(u64 val, volatile void __iomem *addr)
+{
+ asm volatile("str %0, [%1]" : : "r" (val), "r" (addr));
+}
+
+static inline u8 __raw_readb(const volatile void __iomem *addr)
+{
+ u8 val;
+ asm volatile("ldrb %w0, [%1]" : "=r" (val) : "r" (addr));
+ return val;
+}
+
+static inline u16 __raw_readw(const volatile void __iomem *addr)
+{
+ u16 val;
+ asm volatile("ldrh %w0, [%1]" : "=r" (val) : "r" (addr));
+ return val;
+}
+
+static inline u32 __raw_readl(const volatile void __iomem *addr)
+{
+ u32 val;
+ asm volatile("ldr %w0, [%1]" : "=r" (val) : "r" (addr));
+ return val;
+}
+
+static inline u64 __raw_readq(const volatile void __iomem *addr)
+{
+ u64 val;
+ asm volatile("ldr %0, [%1]" : "=r" (val) : "r" (addr));
+ return val;
+}
+
+/* IO barriers */
+#define __iormb() rmb()
+#define __iowmb() wmb()
+
+#define mmiowb() do { } while (0)
+
+/*
+ * Relaxed I/O memory access primitives. These follow the Device memory
+ * ordering rules but do not guarantee any ordering relative to Normal memory
+ * accesses.
+ */
+#define readb_relaxed(c) ({ u8 __v = __raw_readb(c); __v; })
+#define readw_relaxed(c) ({ u16 __v = le16_to_cpu((__force __le16)__raw_readw(c)); __v; })
+#define readl_relaxed(c) ({ u32 __v = le32_to_cpu((__force __le32)__raw_readl(c)); __v; })
+
+#define writeb_relaxed(v,c) ((void)__raw_writeb((v),(c)))
+#define writew_relaxed(v,c) ((void)__raw_writew((__force u16)cpu_to_le16(v),(c)))
+#define writel_relaxed(v,c) ((void)__raw_writel((__force u32)cpu_to_le32(v),(c)))
+
+/*
+ * I/O memory access primitives. Reads are ordered relative to any
+ * following Normal memory access. Writes are ordered relative to any prior
+ * Normal memory access.
+ */
+#define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; })
+#define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; })
+#define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })
+
+#define writeb(v,c) ({ __iowmb(); writeb_relaxed((v),(c)); })
+#define writew(v,c) ({ __iowmb(); writew_relaxed((v),(c)); })
+#define writel(v,c) ({ __iowmb(); writel_relaxed((v),(c)); })
+
+/*
+ * I/O port access primitives.
+ */
+#define IO_SPACE_LIMIT 0xffff
+#define PCI_IOBASE ((void __iomem *)0xffffffbbfffe0000UL)
+
+static inline u8 inb(unsigned long addr)
+{
+ return readb(addr + PCI_IOBASE);
+}
+
+static inline u16 inw(unsigned long addr)
+{
+ return readw(addr + PCI_IOBASE);
+}
+
+static inline u32 inl(unsigned long addr)
+{
+ return readl(addr + PCI_IOBASE);
+}
+
+static inline void outb(u8 b, unsigned long addr)
+{
+ writeb(b, addr + PCI_IOBASE);
+}
+
+static inline void outw(u16 b, unsigned long addr)
+{
+ writew(b, addr + PCI_IOBASE);
+}
+
+static inline void outl(u32 b, unsigned long addr)
+{
+ writel(b, addr + PCI_IOBASE);
+}
+
+#define inb_p(addr) inb(addr)
+#define inw_p(addr) inw(addr)
+#define inl_p(addr) inl(addr)
+
+#define outb_p(x, addr) outb((x), (addr))
+#define outw_p(x, addr) outw((x), (addr))
+#define outl_p(x, addr) outl((x), (addr))
+
+static inline void insb(unsigned long addr, void *buffer, int count)
+{
+ u8 *buf = buffer;
+ while (count--)
+ *buf++ = __raw_readb(addr + PCI_IOBASE);
+}
+
+static inline void insw(unsigned long addr, void *buffer, int count)
+{
+ u16 *buf = buffer;
+ while (count--)
+ *buf++ = __raw_readw(addr + PCI_IOBASE);
+}
+
+static inline void insl(unsigned long addr, void *buffer, int count)
+{
+ u32 *buf = buffer;
+ while (count--)
+ *buf++ = __raw_readl(addr + PCI_IOBASE);
+}
+
+static inline void outsb(unsigned long addr, const void *buffer, int count)
+{
+ const u8 *buf = buffer;
+ while (count--)
+ __raw_writeb(*buf++, addr + PCI_IOBASE);
+}
+
+static inline void outsw(unsigned long addr, const void *buffer, int count)
+{
+ const u16 *buf = buffer;
+ while (count--)
+ __raw_writew(*buf++, addr + PCI_IOBASE);
+}
+
+static inline void outsl(unsigned long addr, const void *buffer, int count)
+{
+ const u32 *buf = buffer;
+ while (count--)
+ __raw_writel(*buf++, addr + PCI_IOBASE);
+}
+
+#define insb_p(port,to,len) insb(port,to,len)
+#define insw_p(port,to,len) insw(port,to,len)
+#define insl_p(port,to,len) insl(port,to,len)
+
+#define outsb_p(port,from,len) outsb(port,from,len)
+#define outsw_p(port,from,len) outsw(port,from,len)
+#define outsl_p(port,from,len) outsl(port,from,len)
+
+/*
+ * String version of I/O memory access operations.
+ */
+extern void __memcpy_fromio(void *, const volatile void __iomem *, size_t);
+extern void __memcpy_toio(volatile void __iomem *, const void *, size_t);
+extern void __memset_io(volatile void __iomem *, int, size_t);
+
+#define memset_io(c,v,l) __memset_io((c),(v),(l))
+#define memcpy_fromio(a,c,l) __memcpy_fromio((a),(c),(l))
+#define memcpy_toio(c,a,l) __memcpy_toio((c),(a),(l))
+
+/*
+ * I/O memory mapping functions.
+ */
+extern void __iomem *__ioremap(phys_addr_t phys_addr, size_t size, pgprot_t prot);
+extern void __iounmap(volatile void __iomem *addr);
+
+#define PROT_DEFAULT (PTE_TYPE_PAGE | PTE_AF | PTE_DIRTY)
+#define PROT_DEVICE_nGnRE (PROT_DEFAULT | PTE_XN | PTE_ATTRINDX(MT_DEVICE_nGnRE))
+#define PROT_NORMAL_NC (PROT_DEFAULT | PTE_ATTRINDX(MT_NORMAL_NC))
+
+#define ioremap(addr, size) __ioremap((addr), (size), PROT_DEVICE_nGnRE)
+#define ioremap_nocache(addr, size) __ioremap((addr), (size), PROT_DEVICE_nGnRE)
+#define ioremap_wc(addr, size) __ioremap((addr), (size), PROT_NORMAL_NC)
+#define iounmap __iounmap
+
+#define ARCH_HAS_IOREMAP_WC
+#include <asm-generic/iomap.h>
+
+/*
+ * More restrictive address range checking than the default implementation
+ * (PHYS_OFFSET and PHYS_MASK taken into account).
+ */
+#define ARCH_HAS_VALID_PHYS_ADDR_RANGE
+extern int valid_phys_addr_range(unsigned long addr, size_t size);
+extern int valid_mmap_phys_addr_range(unsigned long pfn, size_t size);
+
+extern int devmem_is_allowed(unsigned long pfn);
+
+/*
+ * Convert a physical pointer to a virtual kernel pointer for /dev/mem
+ * access
+ */
+#define xlate_dev_mem_ptr(p) __va(p)
+
+/*
+ * Convert a virtual cached pointer to an uncached pointer
+ */
+#define xlate_dev_kmem_ptr(p) p
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_IO_H */
diff --git a/arch/arm64/include/asm/irq.h b/arch/arm64/include/asm/irq.h
new file mode 100644
index 000000000000..a4e1cad3202a
--- /dev/null
+++ b/arch/arm64/include/asm/irq.h
@@ -0,0 +1,8 @@
+#ifndef __ASM_IRQ_H
+#define __ASM_IRQ_H
+
+#include <asm-generic/irq.h>
+
+extern void (*handle_arch_irq)(struct pt_regs *);
+
+#endif
diff --git a/arch/arm64/include/asm/irqflags.h b/arch/arm64/include/asm/irqflags.h
new file mode 100644
index 000000000000..aa11943b8502
--- /dev/null
+++ b/arch/arm64/include/asm/irqflags.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_IRQFLAGS_H
+#define __ASM_IRQFLAGS_H
+
+#ifdef __KERNEL__
+
+#include <asm/ptrace.h>
+
+/*
+ * CPU interrupt mask handling.
+ */
+static inline unsigned long arch_local_irq_save(void)
+{
+ unsigned long flags;
+ asm volatile(
+ "mrs %0, daif // arch_local_irq_save\n"
+ "msr daifset, #2"
+ : "=r" (flags)
+ :
+ : "memory");
+ return flags;
+}
+
+static inline void arch_local_irq_enable(void)
+{
+ asm volatile(
+ "msr daifclr, #2 // arch_local_irq_enable"
+ :
+ :
+ : "memory");
+}
+
+static inline void arch_local_irq_disable(void)
+{
+ asm volatile(
+ "msr daifset, #2 // arch_local_irq_disable"
+ :
+ :
+ : "memory");
+}
+
+#define local_fiq_enable() asm("msr daifclr, #1" : : : "memory")
+#define local_fiq_disable() asm("msr daifset, #1" : : : "memory")
+
+/*
+ * Save the current interrupt enable state.
+ */
+static inline unsigned long arch_local_save_flags(void)
+{
+ unsigned long flags;
+ asm volatile(
+ "mrs %0, daif // arch_local_save_flags"
+ : "=r" (flags)
+ :
+ : "memory");
+ return flags;
+}
+
+/*
+ * restore saved IRQ state
+ */
+static inline void arch_local_irq_restore(unsigned long flags)
+{
+ asm volatile(
+ "msr daif, %0 // arch_local_irq_restore"
+ :
+ : "r" (flags)
+ : "memory");
+}
+
+static inline int arch_irqs_disabled_flags(unsigned long flags)
+{
+ return flags & PSR_I_BIT;
+}
+
+#endif
+#endif
diff --git a/arch/arm64/include/asm/memblock.h b/arch/arm64/include/asm/memblock.h
new file mode 100644
index 000000000000..6afeed2467f1
--- /dev/null
+++ b/arch/arm64/include/asm/memblock.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_MEMBLOCK_H
+#define __ASM_MEMBLOCK_H
+
+extern void arm64_memblock_init(void);
+
+#endif
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h
new file mode 100644
index 000000000000..1cac16a001cb
--- /dev/null
+++ b/arch/arm64/include/asm/memory.h
@@ -0,0 +1,144 @@
+/*
+ * Based on arch/arm/include/asm/memory.h
+ *
+ * Copyright (C) 2000-2002 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Note: this file should not be included by non-asm/.h files
+ */
+#ifndef __ASM_MEMORY_H
+#define __ASM_MEMORY_H
+
+#include <linux/compiler.h>
+#include <linux/const.h>
+#include <linux/types.h>
+#include <asm/sizes.h>
+
+/*
+ * Allow for constants defined here to be used from assembly code
+ * by prepending the UL suffix only with actual C code compilation.
+ */
+#define UL(x) _AC(x, UL)
+
+/*
+ * PAGE_OFFSET - the virtual address of the start of the kernel image.
+ * VA_BITS - the maximum number of bits for virtual addresses.
+ * TASK_SIZE - the maximum size of a user space task.
+ * TASK_UNMAPPED_BASE - the lower boundary of the mmap VM area.
+ * The module space lives between the addresses given by TASK_SIZE
+ * and PAGE_OFFSET - it must be within 128MB of the kernel text.
+ */
+#define PAGE_OFFSET UL(0xffffffc000000000)
+#define MODULES_END (PAGE_OFFSET)
+#define MODULES_VADDR (MODULES_END - SZ_64M)
+#define VA_BITS (39)
+#define TASK_SIZE_64 (UL(1) << VA_BITS)
+
+#ifdef CONFIG_COMPAT
+#define TASK_SIZE_32 UL(0x100000000)
+#define TASK_SIZE (test_thread_flag(TIF_32BIT) ? \
+ TASK_SIZE_32 : TASK_SIZE_64)
+#else
+#define TASK_SIZE TASK_SIZE_64
+#endif /* CONFIG_COMPAT */
+
+#define TASK_UNMAPPED_BASE (PAGE_ALIGN(TASK_SIZE / 4))
+
+#if TASK_SIZE_64 > MODULES_VADDR
+#error Top of 64-bit user space clashes with start of module space
+#endif
+
+/*
+ * 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.
+ */
+#define __virt_to_phys(x) (((phys_addr_t)(x) - PAGE_OFFSET + PHYS_OFFSET))
+#define __phys_to_virt(x) ((unsigned long)((x) - PHYS_OFFSET + PAGE_OFFSET))
+
+/*
+ * Convert a physical address to a Page Frame Number and back
+ */
+#define __phys_to_pfn(paddr) ((unsigned long)((paddr) >> PAGE_SHIFT))
+#define __pfn_to_phys(pfn) ((phys_addr_t)(pfn) << PAGE_SHIFT)
+
+/*
+ * Convert a page to/from a physical address
+ */
+#define page_to_phys(page) (__pfn_to_phys(page_to_pfn(page)))
+#define phys_to_page(phys) (pfn_to_page(__phys_to_pfn(phys)))
+
+/*
+ * Memory types available.
+ */
+#define MT_DEVICE_nGnRnE 0
+#define MT_DEVICE_nGnRE 1
+#define MT_DEVICE_GRE 2
+#define MT_NORMAL_NC 3
+#define MT_NORMAL 4
+
+#ifndef __ASSEMBLY__
+
+extern phys_addr_t memstart_addr;
+/* PHYS_OFFSET - the physical address of the start of memory. */
+#define PHYS_OFFSET ({ memstart_addr; })
+
+/*
+ * 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 (PHYS_OFFSET >> PAGE_SHIFT)
+
+/*
+ * Note: Drivers should NOT use these. They are the wrong
+ * translation for translating DMA addresses. Use the driver
+ * DMA support - see dma-mapping.h.
+ */
+static inline phys_addr_t virt_to_phys(const volatile void *x)
+{
+ return __virt_to_phys((unsigned long)(x));
+}
+
+static inline void *phys_to_virt(phys_addr_t x)
+{
+ return (void *)(__phys_to_virt(x));
+}
+
+/*
+ * Drivers should NOT use these either.
+ */
+#define __pa(x) __virt_to_phys((unsigned long)(x))
+#define __va(x) ((void *)__phys_to_virt((phys_addr_t)(x)))
+#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
+
+/*
+ * virt_to_page(k) convert a _valid_ virtual address to struct page *
+ * virt_addr_valid(k) indicates whether a virtual address is valid
+ */
+#define ARCH_PFN_OFFSET PHYS_PFN_OFFSET
+
+#define virt_to_page(kaddr) pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)
+#define virt_addr_valid(kaddr) (((void *)(kaddr) >= (void *)PAGE_OFFSET) && \
+ ((void *)(kaddr) < (void *)high_memory))
+
+#endif
+
+#include <asm-generic/memory_model.h>
+
+#endif
diff --git a/arch/arm64/include/asm/mmu.h b/arch/arm64/include/asm/mmu.h
new file mode 100644
index 000000000000..d4f7fd5b9e33
--- /dev/null
+++ b/arch/arm64/include/asm/mmu.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_MMU_H
+#define __ASM_MMU_H
+
+typedef struct {
+ unsigned int id;
+ raw_spinlock_t id_lock;
+ void *vdso;
+} mm_context_t;
+
+#define ASID(mm) ((mm)->context.id & 0xffff)
+
+extern void paging_init(void);
+extern void setup_mm_for_reboot(void);
+
+#endif
diff --git a/arch/arm64/include/asm/mmu_context.h b/arch/arm64/include/asm/mmu_context.h
new file mode 100644
index 000000000000..f68465dee026
--- /dev/null
+++ b/arch/arm64/include/asm/mmu_context.h
@@ -0,0 +1,152 @@
+/*
+ * Based on arch/arm/include/asm/mmu_context.h
+ *
+ * Copyright (C) 1996 Russell King.
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_MMU_CONTEXT_H
+#define __ASM_MMU_CONTEXT_H
+
+#include <linux/compiler.h>
+#include <linux/sched.h>
+
+#include <asm/cacheflush.h>
+#include <asm/proc-fns.h>
+#include <asm-generic/mm_hooks.h>
+#include <asm/cputype.h>
+#include <asm/pgtable.h>
+
+#define MAX_ASID_BITS 16
+
+extern unsigned int cpu_last_asid;
+
+void __init_new_context(struct task_struct *tsk, struct mm_struct *mm);
+void __new_context(struct mm_struct *mm);
+
+/*
+ * Set TTBR0 to empty_zero_page. No translations will be possible via TTBR0.
+ */
+static inline void cpu_set_reserved_ttbr0(void)
+{
+ unsigned long ttbr = page_to_phys(empty_zero_page);
+
+ asm(
+ " msr ttbr0_el1, %0 // set TTBR0\n"
+ " isb"
+ :
+ : "r" (ttbr));
+}
+
+static inline void switch_new_context(struct mm_struct *mm)
+{
+ unsigned long flags;
+
+ __new_context(mm);
+
+ local_irq_save(flags);
+ cpu_switch_mm(mm->pgd, mm);
+ local_irq_restore(flags);
+}
+
+static inline void check_and_switch_context(struct mm_struct *mm,
+ struct task_struct *tsk)
+{
+ /*
+ * Required during context switch to avoid speculative page table
+ * walking with the wrong TTBR.
+ */
+ cpu_set_reserved_ttbr0();
+
+ if (!((mm->context.id ^ cpu_last_asid) >> MAX_ASID_BITS))
+ /*
+ * The ASID is from the current generation, just switch to the
+ * new pgd. This condition is only true for calls from
+ * context_switch() and interrupts are already disabled.
+ */
+ cpu_switch_mm(mm->pgd, mm);
+ else if (irqs_disabled())
+ /*
+ * Defer the new ASID allocation until after the context
+ * switch critical region since __new_context() cannot be
+ * called with interrupts disabled.
+ */
+ set_ti_thread_flag(task_thread_info(tsk), TIF_SWITCH_MM);
+ else
+ /*
+ * That is a direct call to switch_mm() or activate_mm() with
+ * interrupts enabled and a new context.
+ */
+ switch_new_context(mm);
+}
+
+#define init_new_context(tsk,mm) (__init_new_context(tsk,mm),0)
+#define destroy_context(mm) do { } while(0)
+
+#define finish_arch_post_lock_switch \
+ finish_arch_post_lock_switch
+static inline void finish_arch_post_lock_switch(void)
+{
+ if (test_and_clear_thread_flag(TIF_SWITCH_MM)) {
+ struct mm_struct *mm = current->mm;
+ unsigned long flags;
+
+ __new_context(mm);
+
+ local_irq_save(flags);
+ cpu_switch_mm(mm->pgd, mm);
+ local_irq_restore(flags);
+ }
+}
+
+/*
+ * This is called when "tsk" is about to enter lazy TLB mode.
+ *
+ * mm: describes the currently active mm context
+ * tsk: task which is entering lazy tlb
+ * cpu: cpu number which is entering lazy tlb
+ *
+ * tsk->mm will be NULL
+ */
+static inline void
+enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
+{
+}
+
+/*
+ * This is the actual mm switch as far as the scheduler
+ * is concerned. No registers are touched. We avoid
+ * calling the CPU specific function when the mm hasn't
+ * actually changed.
+ */
+static inline void
+switch_mm(struct mm_struct *prev, struct mm_struct *next,
+ struct task_struct *tsk)
+{
+ unsigned int cpu = smp_processor_id();
+
+#ifdef CONFIG_SMP
+ /* check for possible thread migration */
+ if (!cpumask_empty(mm_cpumask(next)) &&
+ !cpumask_test_cpu(cpu, mm_cpumask(next)))
+ __flush_icache_all();
+#endif
+ if (!cpumask_test_and_set_cpu(cpu, mm_cpumask(next)) || prev != next)
+ check_and_switch_context(next, tsk);
+}
+
+#define deactivate_mm(tsk,mm) do { } while (0)
+#define activate_mm(prev,next) switch_mm(prev, next, NULL)
+
+#endif
diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h
new file mode 100644
index 000000000000..e80e232b730e
--- /dev/null
+++ b/arch/arm64/include/asm/module.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_MODULE_H
+#define __ASM_MODULE_H
+
+#include <asm-generic/module.h>
+
+#define MODULE_ARCH_VERMAGIC "aarch64"
+
+#endif /* __ASM_MODULE_H */
diff --git a/arch/arm64/include/asm/page.h b/arch/arm64/include/asm/page.h
new file mode 100644
index 000000000000..46bf66628b6a
--- /dev/null
+++ b/arch/arm64/include/asm/page.h
@@ -0,0 +1,67 @@
+/*
+ * Based on arch/arm/include/asm/page.h
+ *
+ * Copyright (C) 1995-2003 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PAGE_H
+#define __ASM_PAGE_H
+
+/* PAGE_SHIFT determines the page size */
+#ifdef CONFIG_ARM64_64K_PAGES
+#define PAGE_SHIFT 16
+#else
+#define PAGE_SHIFT 12
+#endif
+#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
+#define PAGE_MASK (~(PAGE_SIZE-1))
+
+/* We do define AT_SYSINFO_EHDR but don't use the gate mechanism */
+#define __HAVE_ARCH_GATE_AREA 1
+
+#ifndef __ASSEMBLY__
+
+#ifdef CONFIG_ARM64_64K_PAGES
+#include <asm/pgtable-2level-types.h>
+#else
+#include <asm/pgtable-3level-types.h>
+#endif
+
+extern void __cpu_clear_user_page(void *p, unsigned long user);
+extern void __cpu_copy_user_page(void *to, const void *from,
+ unsigned long user);
+extern void copy_page(void *to, const void *from);
+extern void clear_page(void *to);
+
+#define clear_user_page(addr,vaddr,pg) __cpu_clear_user_page(addr, vaddr)
+#define copy_user_page(to,from,vaddr,pg) __cpu_copy_user_page(to, from, vaddr)
+
+typedef struct page *pgtable_t;
+
+#ifdef CONFIG_HAVE_ARCH_PFN_VALID
+extern int pfn_valid(unsigned long);
+#endif
+
+#include <asm/memory.h>
+
+#endif /* !__ASSEMBLY__ */
+
+#define VM_DATA_DEFAULT_FLAGS \
+ (((current->personality & READ_IMPLIES_EXEC) ? VM_EXEC : 0) | \
+ VM_READ | VM_WRITE | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC)
+
+#include <asm-generic/getorder.h>
+
+#endif
diff --git a/arch/arm64/include/asm/param.h b/arch/arm64/include/asm/param.h
new file mode 100644
index 000000000000..8e3a281d448a
--- /dev/null
+++ b/arch/arm64/include/asm/param.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PARAM_H
+#define __ASM_PARAM_H
+
+#define EXEC_PAGESIZE 65536
+
+#include <asm-generic/param.h>
+
+#endif
diff --git a/arch/arm64/include/asm/perf_event.h b/arch/arm64/include/asm/perf_event.h
new file mode 100644
index 000000000000..a6fffd511c5e
--- /dev/null
+++ b/arch/arm64/include/asm/perf_event.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __ASM_PERF_EVENT_H
+#define __ASM_PERF_EVENT_H
+
+/* It's quiet around here... */
+
+#endif
diff --git a/arch/arm64/include/asm/pgalloc.h b/arch/arm64/include/asm/pgalloc.h
new file mode 100644
index 000000000000..f214069ec5d5
--- /dev/null
+++ b/arch/arm64/include/asm/pgalloc.h
@@ -0,0 +1,113 @@
+/*
+ * Based on arch/arm/include/asm/pgalloc.h
+ *
+ * Copyright (C) 2000-2001 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PGALLOC_H
+#define __ASM_PGALLOC_H
+
+#include <asm/pgtable-hwdef.h>
+#include <asm/processor.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+
+#define check_pgt_cache() do { } while (0)
+
+#ifndef CONFIG_ARM64_64K_PAGES
+
+static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long addr)
+{
+ return (pmd_t *)get_zeroed_page(GFP_KERNEL | __GFP_REPEAT);
+}
+
+static inline void pmd_free(struct mm_struct *mm, pmd_t *pmd)
+{
+ BUG_ON((unsigned long)pmd & (PAGE_SIZE-1));
+ free_page((unsigned long)pmd);
+}
+
+static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
+{
+ set_pud(pud, __pud(__pa(pmd) | PMD_TYPE_TABLE));
+}
+
+#endif /* CONFIG_ARM64_64K_PAGES */
+
+extern pgd_t *pgd_alloc(struct mm_struct *mm);
+extern void pgd_free(struct mm_struct *mm, pgd_t *pgd);
+
+#define PGALLOC_GFP (GFP_KERNEL | __GFP_NOTRACK | __GFP_REPEAT | __GFP_ZERO)
+
+static inline pte_t *
+pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr)
+{
+ return (pte_t *)__get_free_page(PGALLOC_GFP);
+}
+
+static inline pgtable_t
+pte_alloc_one(struct mm_struct *mm, unsigned long addr)
+{
+ struct page *pte;
+
+ pte = alloc_pages(PGALLOC_GFP, 0);
+ if (pte)
+ pgtable_page_ctor(pte);
+
+ return pte;
+}
+
+/*
+ * Free a PTE table.
+ */
+static inline void pte_free_kernel(struct mm_struct *mm, pte_t *pte)
+{
+ if (pte)
+ free_page((unsigned long)pte);
+}
+
+static inline void pte_free(struct mm_struct *mm, pgtable_t pte)
+{
+ pgtable_page_dtor(pte);
+ __free_page(pte);
+}
+
+static inline void __pmd_populate(pmd_t *pmdp, phys_addr_t pte,
+ pmdval_t prot)
+{
+ set_pmd(pmdp, __pmd(pte | prot));
+}
+
+/*
+ * Populate the pmdp entry with a pointer to the pte. This pmd is part
+ * of the mm address space.
+ */
+static inline void
+pmd_populate_kernel(struct mm_struct *mm, pmd_t *pmdp, pte_t *ptep)
+{
+ /*
+ * The pmd must be loaded with the physical address of the PTE table
+ */
+ __pmd_populate(pmdp, __pa(ptep), PMD_TYPE_TABLE);
+}
+
+static inline void
+pmd_populate(struct mm_struct *mm, pmd_t *pmdp, pgtable_t ptep)
+{
+ __pmd_populate(pmdp, page_to_phys(ptep), PMD_TYPE_TABLE);
+}
+#define pmd_pgtable(pmd) pmd_page(pmd)
+
+#endif
diff --git a/arch/arm64/include/asm/pgtable-2level-hwdef.h b/arch/arm64/include/asm/pgtable-2level-hwdef.h
new file mode 100644
index 000000000000..0a8ed3f94e93
--- /dev/null
+++ b/arch/arm64/include/asm/pgtable-2level-hwdef.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PGTABLE_2LEVEL_HWDEF_H
+#define __ASM_PGTABLE_2LEVEL_HWDEF_H
+
+/*
+ * With LPAE and 64KB pages, there are 2 levels of page tables. Each level has
+ * 8192 entries of 8 bytes each, occupying a 64KB page. Levels 0 and 1 are not
+ * used. The 2nd level table (PGD for Linux) can cover a range of 4TB, each
+ * entry representing 512MB. The user and kernel address spaces are limited to
+ * 512GB and therefore we only use 1024 entries in the PGD.
+ */
+#define PTRS_PER_PTE 8192
+#define PTRS_PER_PGD 1024
+
+/*
+ * PGDIR_SHIFT determines the size a top-level page table entry can map.
+ */
+#define PGDIR_SHIFT 29
+#define PGDIR_SIZE (_AC(1, UL) << PGDIR_SHIFT)
+#define PGDIR_MASK (~(PGDIR_SIZE-1))
+
+/*
+ * section address mask and size definitions.
+ */
+#define SECTION_SHIFT 29
+#define SECTION_SIZE (_AC(1, UL) << SECTION_SHIFT)
+#define SECTION_MASK (~(SECTION_SIZE-1))
+
+#endif
diff --git a/arch/arm64/include/asm/pgtable-2level-types.h b/arch/arm64/include/asm/pgtable-2level-types.h
new file mode 100644
index 000000000000..3c3ca7d361e4
--- /dev/null
+++ b/arch/arm64/include/asm/pgtable-2level-types.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PGTABLE_2LEVEL_TYPES_H
+#define __ASM_PGTABLE_2LEVEL_TYPES_H
+
+typedef u64 pteval_t;
+typedef u64 pgdval_t;
+typedef pgdval_t pmdval_t;
+
+#undef STRICT_MM_TYPECHECKS
+
+#ifdef STRICT_MM_TYPECHECKS
+
+/*
+ * These are used to make use of C type-checking..
+ */
+typedef struct { pteval_t pte; } pte_t;
+typedef struct { pgdval_t pgd; } pgd_t;
+typedef struct { pteval_t pgprot; } pgprot_t;
+
+#define pte_val(x) ((x).pte)
+#define pgd_val(x) ((x).pgd)
+#define pgprot_val(x) ((x).pgprot)
+
+#define __pte(x) ((pte_t) { (x) } )
+#define __pgd(x) ((pgd_t) { (x) } )
+#define __pgprot(x) ((pgprot_t) { (x) } )
+
+#else /* !STRICT_MM_TYPECHECKS */
+
+typedef pteval_t pte_t;
+typedef pgdval_t pgd_t;
+typedef pteval_t pgprot_t;
+
+#define pte_val(x) (x)
+#define pgd_val(x) (x)
+#define pgprot_val(x) (x)
+
+#define __pte(x) (x)
+#define __pgd(x) (x)
+#define __pgprot(x) (x)
+
+#endif /* STRICT_MM_TYPECHECKS */
+
+#include <asm-generic/pgtable-nopmd.h>
+
+#endif /* __ASM_PGTABLE_2LEVEL_TYPES_H */
diff --git a/arch/arm64/include/asm/pgtable-3level-hwdef.h b/arch/arm64/include/asm/pgtable-3level-hwdef.h
new file mode 100644
index 000000000000..3dbf941d7767
--- /dev/null
+++ b/arch/arm64/include/asm/pgtable-3level-hwdef.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PGTABLE_3LEVEL_HWDEF_H
+#define __ASM_PGTABLE_3LEVEL_HWDEF_H
+
+/*
+ * With LPAE and 4KB pages, there are 3 levels of page tables. Each level has
+ * 512 entries of 8 bytes each, occupying a 4K page. The first level table
+ * covers a range of 512GB, each entry representing 1GB. The user and kernel
+ * address spaces are limited to 512GB each.
+ */
+#define PTRS_PER_PTE 512
+#define PTRS_PER_PMD 512
+#define PTRS_PER_PGD 512
+
+/*
+ * PGDIR_SHIFT determines the size a top-level page table entry can map.
+ */
+#define PGDIR_SHIFT 30
+#define PGDIR_SIZE (_AC(1, UL) << PGDIR_SHIFT)
+#define PGDIR_MASK (~(PGDIR_SIZE-1))
+
+/*
+ * PMD_SHIFT determines the size a middle-level page table entry can map.
+ */
+#define PMD_SHIFT 21
+#define PMD_SIZE (_AC(1, UL) << PMD_SHIFT)
+#define PMD_MASK (~(PMD_SIZE-1))
+
+/*
+ * section address mask and size definitions.
+ */
+#define SECTION_SHIFT 21
+#define SECTION_SIZE (_AC(1, UL) << SECTION_SHIFT)
+#define SECTION_MASK (~(SECTION_SIZE-1))
+
+#endif
diff --git a/arch/arm64/include/asm/pgtable-3level-types.h b/arch/arm64/include/asm/pgtable-3level-types.h
new file mode 100644
index 000000000000..4489615f14a9
--- /dev/null
+++ b/arch/arm64/include/asm/pgtable-3level-types.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PGTABLE_3LEVEL_TYPES_H
+#define __ASM_PGTABLE_3LEVEL_TYPES_H
+
+typedef u64 pteval_t;
+typedef u64 pmdval_t;
+typedef u64 pgdval_t;
+
+#undef STRICT_MM_TYPECHECKS
+
+#ifdef STRICT_MM_TYPECHECKS
+
+/*
+ * These are used to make use of C type-checking..
+ */
+typedef struct { pteval_t pte; } pte_t;
+typedef struct { pmdval_t pmd; } pmd_t;
+typedef struct { pgdval_t pgd; } pgd_t;
+typedef struct { pteval_t pgprot; } pgprot_t;
+
+#define pte_val(x) ((x).pte)
+#define pmd_val(x) ((x).pmd)
+#define pgd_val(x) ((x).pgd)
+#define pgprot_val(x) ((x).pgprot)
+
+#define __pte(x) ((pte_t) { (x) } )
+#define __pmd(x) ((pmd_t) { (x) } )
+#define __pgd(x) ((pgd_t) { (x) } )
+#define __pgprot(x) ((pgprot_t) { (x) } )
+
+#else /* !STRICT_MM_TYPECHECKS */
+
+typedef pteval_t pte_t;
+typedef pmdval_t pmd_t;
+typedef pgdval_t pgd_t;
+typedef pteval_t pgprot_t;
+
+#define pte_val(x) (x)
+#define pmd_val(x) (x)
+#define pgd_val(x) (x)
+#define pgprot_val(x) (x)
+
+#define __pte(x) (x)
+#define __pmd(x) (x)
+#define __pgd(x) (x)
+#define __pgprot(x) (x)
+
+#endif /* STRICT_MM_TYPECHECKS */
+
+#include <asm-generic/pgtable-nopud.h>
+
+#endif /* __ASM_PGTABLE_3LEVEL_TYPES_H */
diff --git a/arch/arm64/include/asm/pgtable-hwdef.h b/arch/arm64/include/asm/pgtable-hwdef.h
new file mode 100644
index 000000000000..0f3b4581d925
--- /dev/null
+++ b/arch/arm64/include/asm/pgtable-hwdef.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PGTABLE_HWDEF_H
+#define __ASM_PGTABLE_HWDEF_H
+
+#ifdef CONFIG_ARM64_64K_PAGES
+#include <asm/pgtable-2level-hwdef.h>
+#else
+#include <asm/pgtable-3level-hwdef.h>
+#endif
+
+/*
+ * Hardware page table definitions.
+ *
+ * Level 2 descriptor (PMD).
+ */
+#define PMD_TYPE_MASK (_AT(pmdval_t, 3) << 0)
+#define PMD_TYPE_FAULT (_AT(pmdval_t, 0) << 0)
+#define PMD_TYPE_TABLE (_AT(pmdval_t, 3) << 0)
+#define PMD_TYPE_SECT (_AT(pmdval_t, 1) << 0)
+
+/*
+ * Section
+ */
+#define PMD_SECT_S (_AT(pmdval_t, 3) << 8)
+#define PMD_SECT_AF (_AT(pmdval_t, 1) << 10)
+#define PMD_SECT_NG (_AT(pmdval_t, 1) << 11)
+#define PMD_SECT_XN (_AT(pmdval_t, 1) << 54)
+
+/*
+ * AttrIndx[2:0] encoding (mapping attributes defined in the MAIR* registers).
+ */
+#define PMD_ATTRINDX(t) (_AT(pmdval_t, (t)) << 2)
+#define PMD_ATTRINDX_MASK (_AT(pmdval_t, 7) << 2)
+
+/*
+ * Level 3 descriptor (PTE).
+ */
+#define PTE_TYPE_MASK (_AT(pteval_t, 3) << 0)
+#define PTE_TYPE_FAULT (_AT(pteval_t, 0) << 0)
+#define PTE_TYPE_PAGE (_AT(pteval_t, 3) << 0)
+#define PTE_USER (_AT(pteval_t, 1) << 6) /* AP[1] */
+#define PTE_RDONLY (_AT(pteval_t, 1) << 7) /* AP[2] */
+#define PTE_SHARED (_AT(pteval_t, 3) << 8) /* SH[1:0], inner shareable */
+#define PTE_AF (_AT(pteval_t, 1) << 10) /* Access Flag */
+#define PTE_NG (_AT(pteval_t, 1) << 11) /* nG */
+#define PTE_XN (_AT(pteval_t, 1) << 54) /* XN */
+
+/*
+ * AttrIndx[2:0] encoding (mapping attributes defined in the MAIR* registers).
+ */
+#define PTE_ATTRINDX(t) (_AT(pteval_t, (t)) << 2)
+#define PTE_ATTRINDX_MASK (_AT(pteval_t, 7) << 2)
+
+/*
+ * 40-bit physical address supported.
+ */
+#define PHYS_MASK_SHIFT (40)
+#define PHYS_MASK ((UL(1) << PHYS_MASK_SHIFT) - 1)
+
+/*
+ * TCR flags.
+ */
+#define TCR_TxSZ(x) (((UL(64) - (x)) << 16) | ((UL(64) - (x)) << 0))
+#define TCR_IRGN_NC ((UL(0) << 8) | (UL(0) << 24))
+#define TCR_IRGN_WBWA ((UL(1) << 8) | (UL(1) << 24))
+#define TCR_IRGN_WT ((UL(2) << 8) | (UL(2) << 24))
+#define TCR_IRGN_WBnWA ((UL(3) << 8) | (UL(3) << 24))
+#define TCR_IRGN_MASK ((UL(3) << 8) | (UL(3) << 24))
+#define TCR_ORGN_NC ((UL(0) << 10) | (UL(0) << 26))
+#define TCR_ORGN_WBWA ((UL(1) << 10) | (UL(1) << 26))
+#define TCR_ORGN_WT ((UL(2) << 10) | (UL(2) << 26))
+#define TCR_ORGN_WBnWA ((UL(3) << 10) | (UL(3) << 26))
+#define TCR_ORGN_MASK ((UL(3) << 10) | (UL(3) << 26))
+#define TCR_SHARED ((UL(3) << 12) | (UL(3) << 28))
+#define TCR_TG0_64K (UL(1) << 14)
+#define TCR_TG1_64K (UL(1) << 30)
+#define TCR_IPS_40BIT (UL(2) << 32)
+#define TCR_ASID16 (UL(1) << 36)
+
+#endif
diff --git a/arch/arm64/include/asm/pgtable.h b/arch/arm64/include/asm/pgtable.h
new file mode 100644
index 000000000000..8960239be722
--- /dev/null
+++ b/arch/arm64/include/asm/pgtable.h
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PGTABLE_H
+#define __ASM_PGTABLE_H
+
+#include <asm/proc-fns.h>
+
+#include <asm/memory.h>
+#include <asm/pgtable-hwdef.h>
+
+/*
+ * Software defined PTE bits definition.
+ */
+#define PTE_VALID (_AT(pteval_t, 1) << 0) /* pte_present() check */
+#define PTE_FILE (_AT(pteval_t, 1) << 2) /* only when !pte_present() */
+#define PTE_DIRTY (_AT(pteval_t, 1) << 55)
+#define PTE_SPECIAL (_AT(pteval_t, 1) << 56)
+
+/*
+ * VMALLOC and SPARSEMEM_VMEMMAP ranges.
+ */
+#define VMALLOC_START UL(0xffffff8000000000)
+#define VMALLOC_END (PAGE_OFFSET - UL(0x400000000) - SZ_64K)
+
+#define vmemmap ((struct page *)(VMALLOC_END + SZ_64K))
+
+#define FIRST_USER_ADDRESS 0
+
+#ifndef __ASSEMBLY__
+extern void __pte_error(const char *file, int line, unsigned long val);
+extern void __pmd_error(const char *file, int line, unsigned long val);
+extern void __pgd_error(const char *file, int line, unsigned long val);
+
+#define pte_ERROR(pte) __pte_error(__FILE__, __LINE__, pte_val(pte))
+#ifndef CONFIG_ARM64_64K_PAGES
+#define pmd_ERROR(pmd) __pmd_error(__FILE__, __LINE__, pmd_val(pmd))
+#endif
+#define pgd_ERROR(pgd) __pgd_error(__FILE__, __LINE__, pgd_val(pgd))
+
+/*
+ * The pgprot_* and protection_map entries will be fixed up at runtime to
+ * include the cachable and bufferable bits based on memory policy, as well as
+ * any architecture dependent bits like global/ASID and SMP shared mapping
+ * bits.
+ */
+#define _PAGE_DEFAULT PTE_TYPE_PAGE | PTE_AF
+
+extern pgprot_t pgprot_default;
+
+#define _MOD_PROT(p, b) __pgprot(pgprot_val(p) | (b))
+
+#define PAGE_NONE _MOD_PROT(pgprot_default, PTE_NG | PTE_XN | PTE_RDONLY)
+#define PAGE_SHARED _MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_XN)
+#define PAGE_SHARED_EXEC _MOD_PROT(pgprot_default, PTE_USER | PTE_NG)
+#define PAGE_COPY _MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_XN | PTE_RDONLY)
+#define PAGE_COPY_EXEC _MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_RDONLY)
+#define PAGE_READONLY _MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_XN | PTE_RDONLY)
+#define PAGE_READONLY_EXEC _MOD_PROT(pgprot_default, PTE_USER | PTE_NG | PTE_RDONLY)
+#define PAGE_KERNEL _MOD_PROT(pgprot_default, PTE_XN | PTE_DIRTY)
+#define PAGE_KERNEL_EXEC _MOD_PROT(pgprot_default, PTE_DIRTY)
+
+#define __PAGE_NONE __pgprot(_PAGE_DEFAULT | PTE_NG | PTE_XN | PTE_RDONLY)
+#define __PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_XN)
+#define __PAGE_SHARED_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG)
+#define __PAGE_COPY __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_XN | PTE_RDONLY)
+#define __PAGE_COPY_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_RDONLY)
+#define __PAGE_READONLY __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_XN | PTE_RDONLY)
+#define __PAGE_READONLY_EXEC __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_RDONLY)
+
+#endif /* __ASSEMBLY__ */
+
+#define __P000 __PAGE_NONE
+#define __P001 __PAGE_READONLY
+#define __P010 __PAGE_COPY
+#define __P011 __PAGE_COPY
+#define __P100 __PAGE_READONLY_EXEC
+#define __P101 __PAGE_READONLY_EXEC
+#define __P110 __PAGE_COPY_EXEC
+#define __P111 __PAGE_COPY_EXEC
+
+#define __S000 __PAGE_NONE
+#define __S001 __PAGE_READONLY
+#define __S010 __PAGE_SHARED
+#define __S011 __PAGE_SHARED
+#define __S100 __PAGE_READONLY_EXEC
+#define __S101 __PAGE_READONLY_EXEC
+#define __S110 __PAGE_SHARED_EXEC
+#define __S111 __PAGE_SHARED_EXEC
+
+#ifndef __ASSEMBLY__
+/*
+ * ZERO_PAGE is a global shared page that is always zero: used
+ * for zero-mapped memory areas etc..
+ */
+extern struct page *empty_zero_page;
+#define ZERO_PAGE(vaddr) (empty_zero_page)
+
+#define pte_pfn(pte) ((pte_val(pte) & PHYS_MASK) >> PAGE_SHIFT)
+
+#define pfn_pte(pfn,prot) (__pte(((phys_addr_t)(pfn) << PAGE_SHIFT) | pgprot_val(prot)))
+
+#define pte_none(pte) (!pte_val(pte))
+#define pte_clear(mm,addr,ptep) set_pte(ptep, __pte(0))
+#define pte_page(pte) (pfn_to_page(pte_pfn(pte)))
+#define pte_offset_kernel(dir,addr) (pmd_page_vaddr(*(dir)) + __pte_index(addr))
+
+#define pte_offset_map(dir,addr) pte_offset_kernel((dir), (addr))
+#define pte_offset_map_nested(dir,addr) pte_offset_kernel((dir), (addr))
+#define pte_unmap(pte) do { } while (0)
+#define pte_unmap_nested(pte) do { } while (0)
+
+/*
+ * The following only work if pte_present(). Undefined behaviour otherwise.
+ */
+#define pte_present(pte) (pte_val(pte) & PTE_VALID)
+#define pte_dirty(pte) (pte_val(pte) & PTE_DIRTY)
+#define pte_young(pte) (pte_val(pte) & PTE_AF)
+#define pte_special(pte) (pte_val(pte) & PTE_SPECIAL)
+#define pte_write(pte) (!(pte_val(pte) & PTE_RDONLY))
+#define pte_exec(pte) (!(pte_val(pte) & PTE_XN))
+
+#define pte_present_exec_user(pte) \
+ ((pte_val(pte) & (PTE_VALID | PTE_USER | PTE_XN)) == \
+ (PTE_VALID | PTE_USER))
+
+#define PTE_BIT_FUNC(fn,op) \
+static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; }
+
+PTE_BIT_FUNC(wrprotect, |= PTE_RDONLY);
+PTE_BIT_FUNC(mkwrite, &= ~PTE_RDONLY);
+PTE_BIT_FUNC(mkclean, &= ~PTE_DIRTY);
+PTE_BIT_FUNC(mkdirty, |= PTE_DIRTY);
+PTE_BIT_FUNC(mkold, &= ~PTE_AF);
+PTE_BIT_FUNC(mkyoung, |= PTE_AF);
+PTE_BIT_FUNC(mkspecial, |= PTE_SPECIAL);
+
+static inline void set_pte(pte_t *ptep, pte_t pte)
+{
+ *ptep = pte;
+}
+
+extern void __sync_icache_dcache(pte_t pteval, unsigned long addr);
+
+static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
+ pte_t *ptep, pte_t pte)
+{
+ if (pte_present_exec_user(pte))
+ __sync_icache_dcache(pte, addr);
+ set_pte(ptep, pte);
+}
+
+/*
+ * Huge pte definitions.
+ */
+#define pte_huge(pte) ((pte_val(pte) & PTE_TYPE_MASK) == PTE_TYPE_HUGEPAGE)
+#define pte_mkhuge(pte) (__pte((pte_val(pte) & ~PTE_TYPE_MASK) | PTE_TYPE_HUGEPAGE))
+
+#define __pgprot_modify(prot,mask,bits) \
+ __pgprot((pgprot_val(prot) & ~(mask)) | (bits))
+
+#define __HAVE_ARCH_PTE_SPECIAL
+
+/*
+ * Mark the prot value as uncacheable and unbufferable.
+ */
+#define pgprot_noncached(prot) \
+ __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_nGnRnE))
+#define pgprot_writecombine(prot) \
+ __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_DEVICE_GRE))
+#define pgprot_dmacoherent(prot) \
+ __pgprot_modify(prot, PTE_ATTRINDX_MASK, PTE_ATTRINDX(MT_NORMAL_NC))
+#define __HAVE_PHYS_MEM_ACCESS_PROT
+struct file;
+extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
+ unsigned long size, pgprot_t vma_prot);
+
+#define pmd_none(pmd) (!pmd_val(pmd))
+#define pmd_present(pmd) (pmd_val(pmd))
+
+#define pmd_bad(pmd) (!(pmd_val(pmd) & 2))
+
+static inline void set_pmd(pmd_t *pmdp, pmd_t pmd)
+{
+ *pmdp = pmd;
+ dsb();
+}
+
+static inline void pmd_clear(pmd_t *pmdp)
+{
+ set_pmd(pmdp, __pmd(0));
+}
+
+static inline pte_t *pmd_page_vaddr(pmd_t pmd)
+{
+ return __va(pmd_val(pmd) & PHYS_MASK & (s32)PAGE_MASK);
+}
+
+#define pmd_page(pmd) pfn_to_page(__phys_to_pfn(pmd_val(pmd) & PHYS_MASK))
+
+/*
+ * Conversion functions: convert a page and protection to a page entry,
+ * and a page entry and page directory to the page they refer to.
+ */
+#define mk_pte(page,prot) pfn_pte(page_to_pfn(page),prot)
+
+#ifndef CONFIG_ARM64_64K_PAGES
+
+#define pud_none(pud) (!pud_val(pud))
+#define pud_bad(pud) (!(pud_val(pud) & 2))
+#define pud_present(pud) (pud_val(pud))
+
+static inline void set_pud(pud_t *pudp, pud_t pud)
+{
+ *pudp = pud;
+ dsb();
+}
+
+static inline void pud_clear(pud_t *pudp)
+{
+ set_pud(pudp, __pud(0));
+}
+
+static inline pmd_t *pud_page_vaddr(pud_t pud)
+{
+ return __va(pud_val(pud) & PHYS_MASK & (s32)PAGE_MASK);
+}
+
+#endif /* CONFIG_ARM64_64K_PAGES */
+
+/* to find an entry in a page-table-directory */
+#define pgd_index(addr) (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))
+
+#define pgd_offset(mm, addr) ((mm)->pgd+pgd_index(addr))
+
+/* to find an entry in a kernel page-table-directory */
+#define pgd_offset_k(addr) pgd_offset(&init_mm, addr)
+
+/* Find an entry in the second-level page table.. */
+#ifndef CONFIG_ARM64_64K_PAGES
+#define pmd_index(addr) (((addr) >> PMD_SHIFT) & (PTRS_PER_PMD - 1))
+static inline pmd_t *pmd_offset(pud_t *pud, unsigned long addr)
+{
+ return (pmd_t *)pud_page_vaddr(*pud) + pmd_index(addr);
+}
+#endif
+
+/* Find an entry in the third-level page table.. */
+#define __pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1))
+
+static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
+{
+ const pteval_t mask = PTE_USER | PTE_XN | PTE_RDONLY;
+ pte_val(pte) = (pte_val(pte) & ~mask) | (pgprot_val(newprot) & mask);
+ return pte;
+}
+
+extern pgd_t swapper_pg_dir[PTRS_PER_PGD];
+extern pgd_t idmap_pg_dir[PTRS_PER_PGD];
+
+#define SWAPPER_DIR_SIZE (3 * PAGE_SIZE)
+#define IDMAP_DIR_SIZE (2 * PAGE_SIZE)
+
+/*
+ * Encode and decode a swap entry:
+ * bits 0-1: present (must be zero)
+ * bit 2: PTE_FILE
+ * bits 3-8: swap type
+ * bits 9-63: swap offset
+ */
+#define __SWP_TYPE_SHIFT 3
+#define __SWP_TYPE_BITS 6
+#define __SWP_TYPE_MASK ((1 << __SWP_TYPE_BITS) - 1)
+#define __SWP_OFFSET_SHIFT (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
+
+#define __swp_type(x) (((x).val >> __SWP_TYPE_SHIFT) & __SWP_TYPE_MASK)
+#define __swp_offset(x) ((x).val >> __SWP_OFFSET_SHIFT)
+#define __swp_entry(type,offset) ((swp_entry_t) { ((type) << __SWP_TYPE_SHIFT) | ((offset) << __SWP_OFFSET_SHIFT) })
+
+#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
+#define __swp_entry_to_pte(swp) ((pte_t) { (swp).val })
+
+/*
+ * Ensure that there are not more swap files than can be encoded in the kernel
+ * the PTEs.
+ */
+#define MAX_SWAPFILES_CHECK() BUILD_BUG_ON(MAX_SWAPFILES_SHIFT > __SWP_TYPE_BITS)
+
+/*
+ * Encode and decode a file entry:
+ * bits 0-1: present (must be zero)
+ * bit 2: PTE_FILE
+ * bits 3-63: file offset / PAGE_SIZE
+ */
+#define pte_file(pte) (pte_val(pte) & PTE_FILE)
+#define pte_to_pgoff(x) (pte_val(x) >> 3)
+#define pgoff_to_pte(x) __pte(((x) << 3) | PTE_FILE)
+
+#define PTE_FILE_MAX_BITS 61
+
+extern int kern_addr_valid(unsigned long addr);
+
+#include <asm-generic/pgtable.h>
+
+/*
+ * remap a physical page `pfn' of size `size' with page protection `prot'
+ * into virtual address `from'
+ */
+#define io_remap_pfn_range(vma,from,pfn,size,prot) \
+ remap_pfn_range(vma, from, pfn, size, prot)
+
+#define pgtable_cache_init() do { } while (0)
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __ASM_PGTABLE_H */
diff --git a/arch/arm64/include/asm/pmu.h b/arch/arm64/include/asm/pmu.h
new file mode 100644
index 000000000000..e6f087806aaf
--- /dev/null
+++ b/arch/arm64/include/asm/pmu.h
@@ -0,0 +1,82 @@
+/*
+ * Based on arch/arm/include/asm/pmu.h
+ *
+ * Copyright (C) 2009 picoChip Designs Ltd, Jamie Iles
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PMU_H
+#define __ASM_PMU_H
+
+#ifdef CONFIG_HW_PERF_EVENTS
+
+/* The events for a given PMU register set. */
+struct pmu_hw_events {
+ /*
+ * The events that are active on the PMU for the given index.
+ */
+ struct perf_event **events;
+
+ /*
+ * A 1 bit for an index indicates that the counter is being used for
+ * an event. A 0 means that the counter can be used.
+ */
+ unsigned long *used_mask;
+
+ /*
+ * Hardware lock to serialize accesses to PMU registers. Needed for the
+ * read/modify/write sequences.
+ */
+ raw_spinlock_t pmu_lock;
+};
+
+struct arm_pmu {
+ struct pmu pmu;
+ cpumask_t active_irqs;
+ const char *name;
+ irqreturn_t (*handle_irq)(int irq_num, void *dev);
+ void (*enable)(struct hw_perf_event *evt, int idx);
+ void (*disable)(struct hw_perf_event *evt, int idx);
+ int (*get_event_idx)(struct pmu_hw_events *hw_events,
+ struct hw_perf_event *hwc);
+ int (*set_event_filter)(struct hw_perf_event *evt,
+ struct perf_event_attr *attr);
+ u32 (*read_counter)(int idx);
+ void (*write_counter)(int idx, u32 val);
+ void (*start)(void);
+ void (*stop)(void);
+ void (*reset)(void *);
+ int (*map_event)(struct perf_event *event);
+ int num_events;
+ atomic_t active_events;
+ struct mutex reserve_mutex;
+ u64 max_period;
+ struct platform_device *plat_device;
+ struct pmu_hw_events *(*get_hw_events)(void);
+};
+
+#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
+
+int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type);
+
+u64 armpmu_event_update(struct perf_event *event,
+ struct hw_perf_event *hwc,
+ int idx);
+
+int armpmu_event_set_period(struct perf_event *event,
+ struct hw_perf_event *hwc,
+ int idx);
+
+#endif /* CONFIG_HW_PERF_EVENTS */
+#endif /* __ASM_PMU_H */
diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h
new file mode 100644
index 000000000000..7cdf466fd0c5
--- /dev/null
+++ b/arch/arm64/include/asm/proc-fns.h
@@ -0,0 +1,50 @@
+/*
+ * Based on arch/arm/include/asm/proc-fns.h
+ *
+ * Copyright (C) 1997-1999 Russell King
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PROCFNS_H
+#define __ASM_PROCFNS_H
+
+#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
+
+#include <asm/page.h>
+
+struct mm_struct;
+
+extern void cpu_cache_off(void);
+extern void cpu_do_idle(void);
+extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
+extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
+
+#include <asm/memory.h>
+
+#define cpu_switch_mm(pgd,mm) cpu_do_switch_mm(virt_to_phys(pgd),mm)
+
+#define cpu_get_pgd() \
+({ \
+ unsigned long pg; \
+ asm("mrs %0, ttbr0_el1\n" \
+ : "=r" (pg)); \
+ pg &= ~0xffff000000003ffful; \
+ (pgd_t *)phys_to_virt(pg); \
+})
+
+#endif /* __ASSEMBLY__ */
+#endif /* __KERNEL__ */
+#endif /* __ASM_PROCFNS_H */
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
new file mode 100644
index 000000000000..39a208a392f7
--- /dev/null
+++ b/arch/arm64/include/asm/processor.h
@@ -0,0 +1,175 @@
+/*
+ * Based on arch/arm/include/asm/processor.h
+ *
+ * Copyright (C) 1995-1999 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PROCESSOR_H
+#define __ASM_PROCESSOR_H
+
+/*
+ * Default implementation of macro that returns current
+ * instruction pointer ("program counter").
+ */
+#define current_text_addr() ({ __label__ _l; _l: &&_l;})
+
+#ifdef __KERNEL__
+
+#include <linux/string.h>
+
+#include <asm/fpsimd.h>
+#include <asm/hw_breakpoint.h>
+#include <asm/ptrace.h>
+#include <asm/types.h>
+
+#ifdef __KERNEL__
+#define STACK_TOP_MAX TASK_SIZE_64
+#ifdef CONFIG_COMPAT
+#define AARCH32_VECTORS_BASE 0xffff0000
+#define STACK_TOP (test_thread_flag(TIF_32BIT) ? \
+ AARCH32_VECTORS_BASE : STACK_TOP_MAX)
+#else
+#define STACK_TOP STACK_TOP_MAX
+#endif /* CONFIG_COMPAT */
+#endif /* __KERNEL__ */
+
+struct debug_info {
+ /* Have we suspended stepping by a debugger? */
+ int suspended_step;
+ /* Allow breakpoints and watchpoints to be disabled for this thread. */
+ int bps_disabled;
+ int wps_disabled;
+ /* Hardware breakpoints pinned to this task. */
+ struct perf_event *hbp_break[ARM_MAX_BRP];
+ struct perf_event *hbp_watch[ARM_MAX_WRP];
+};
+
+struct cpu_context {
+ unsigned long x19;
+ unsigned long x20;
+ unsigned long x21;
+ unsigned long x22;
+ unsigned long x23;
+ unsigned long x24;
+ unsigned long x25;
+ unsigned long x26;
+ unsigned long x27;
+ unsigned long x28;
+ unsigned long fp;
+ unsigned long sp;
+ unsigned long pc;
+};
+
+struct thread_struct {
+ struct cpu_context cpu_context; /* cpu context */
+ unsigned long tp_value;
+ struct fpsimd_state fpsimd_state;
+ unsigned long fault_address; /* fault info */
+ struct debug_info debug; /* debugging */
+};
+
+#define INIT_THREAD { }
+
+static inline void start_thread_common(struct pt_regs *regs, unsigned long pc)
+{
+ memset(regs, 0, sizeof(*regs));
+ regs->syscallno = ~0UL;
+ regs->pc = pc;
+}
+
+static inline void start_thread(struct pt_regs *regs, unsigned long pc,
+ unsigned long sp)
+{
+ unsigned long *stack = (unsigned long *)sp;
+
+ start_thread_common(regs, pc);
+ regs->pstate = PSR_MODE_EL0t;
+ regs->sp = sp;
+ regs->regs[2] = stack[2]; /* x2 (envp) */
+ regs->regs[1] = stack[1]; /* x1 (argv) */
+ regs->regs[0] = stack[0]; /* x0 (argc) */
+}
+
+#ifdef CONFIG_COMPAT
+static inline void compat_start_thread(struct pt_regs *regs, unsigned long pc,
+ unsigned long sp)
+{
+ unsigned int *stack = (unsigned int *)sp;
+
+ start_thread_common(regs, pc);
+ regs->pstate = COMPAT_PSR_MODE_USR;
+ if (pc & 1)
+ regs->pstate |= COMPAT_PSR_T_BIT;
+ regs->compat_sp = sp;
+ regs->regs[2] = stack[2]; /* x2 (envp) */
+ regs->regs[1] = stack[1]; /* x1 (argv) */
+ regs->regs[0] = stack[0]; /* x0 (argc) */
+}
+#endif
+
+/* Forward declaration, a strange C thing */
+struct task_struct;
+
+/* Free all resources held by a thread. */
+extern void release_thread(struct task_struct *);
+
+/* Prepare to copy thread state - unlazy all lazy status */
+#define prepare_to_copy(tsk) do { } while (0)
+
+unsigned long get_wchan(struct task_struct *p);
+
+#define cpu_relax() barrier()
+
+/* Thread switching */
+extern struct task_struct *cpu_switch_to(struct task_struct *prev,
+ struct task_struct *next);
+
+/*
+ * Create a new kernel thread
+ */
+extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
+
+#define task_pt_regs(p) \
+ ((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1)
+
+#define KSTK_EIP(tsk) task_pt_regs(tsk)->pc
+#define KSTK_ESP(tsk) task_pt_regs(tsk)->sp
+
+/*
+ * Prefetching support
+ */
+#define ARCH_HAS_PREFETCH
+static inline void prefetch(const void *ptr)
+{
+ asm volatile("prfm pldl1keep, %a0\n" : : "p" (ptr));
+}
+
+#define ARCH_HAS_PREFETCHW
+static inline void prefetchw(const void *ptr)
+{
+ asm volatile("prfm pstl1keep, %a0\n" : : "p" (ptr));
+}
+
+#define ARCH_HAS_SPINLOCK_PREFETCH
+static inline void spin_lock_prefetch(const void *x)
+{
+ prefetchw(x);
+}
+
+#define HAVE_ARCH_PICK_MMAP_LAYOUT
+
+#endif
+
+#endif /* __ASM_PROCESSOR_H */
diff --git a/arch/arm64/include/asm/prom.h b/arch/arm64/include/asm/prom.h
new file mode 100644
index 000000000000..68b90e682957
--- /dev/null
+++ b/arch/arm64/include/asm/prom.h
@@ -0,0 +1 @@
+/* Empty for now */
diff --git a/arch/arm64/include/asm/ptrace.h b/arch/arm64/include/asm/ptrace.h
new file mode 100644
index 000000000000..0fa5d6c9ef76
--- /dev/null
+++ b/arch/arm64/include/asm/ptrace.h
@@ -0,0 +1,207 @@
+/*
+ * Based on arch/arm/include/asm/ptrace.h
+ *
+ * Copyright (C) 1996-2003 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_PTRACE_H
+#define __ASM_PTRACE_H
+
+#include <linux/types.h>
+
+#include <asm/hwcap.h>
+
+/* AArch32-specific ptrace requests */
+#define COMPAT_PTRACE_GETREGS 12
+#define COMPAT_PTRACE_SETREGS 13
+#define COMPAT_PTRACE_GET_THREAD_AREA 22
+#define COMPAT_PTRACE_SET_SYSCALL 23
+#define COMPAT_PTRACE_GETVFPREGS 27
+#define COMPAT_PTRACE_SETVFPREGS 28
+#define COMPAT_PTRACE_GETHBPREGS 29
+#define COMPAT_PTRACE_SETHBPREGS 30
+
+/*
+ * PSR bits
+ */
+#define PSR_MODE_EL0t 0x00000000
+#define PSR_MODE_EL1t 0x00000004
+#define PSR_MODE_EL1h 0x00000005
+#define PSR_MODE_EL2t 0x00000008
+#define PSR_MODE_EL2h 0x00000009
+#define PSR_MODE_EL3t 0x0000000c
+#define PSR_MODE_EL3h 0x0000000d
+#define PSR_MODE_MASK 0x0000000f
+
+/* AArch32 CPSR bits */
+#define PSR_MODE32_BIT 0x00000010
+#define COMPAT_PSR_MODE_USR 0x00000010
+#define COMPAT_PSR_T_BIT 0x00000020
+#define COMPAT_PSR_IT_MASK 0x0600fc00 /* If-Then execution state mask */
+
+/* AArch64 SPSR bits */
+#define PSR_F_BIT 0x00000040
+#define PSR_I_BIT 0x00000080
+#define PSR_A_BIT 0x00000100
+#define PSR_D_BIT 0x00000200
+#define PSR_Q_BIT 0x08000000
+#define PSR_V_BIT 0x10000000
+#define PSR_C_BIT 0x20000000
+#define PSR_Z_BIT 0x40000000
+#define PSR_N_BIT 0x80000000
+
+/*
+ * Groups of PSR bits
+ */
+#define PSR_f 0xff000000 /* Flags */
+#define PSR_s 0x00ff0000 /* Status */
+#define PSR_x 0x0000ff00 /* Extension */
+#define PSR_c 0x000000ff /* Control */
+
+/*
+ * These are 'magic' values for PTRACE_PEEKUSR that return info about where a
+ * process is located in memory.
+ */
+#define PT_TEXT_ADDR 0x10000
+#define PT_DATA_ADDR 0x10004
+#define PT_TEXT_END_ADDR 0x10008
+
+#ifndef __ASSEMBLY__
+
+/*
+ * User structures for general purpose, floating point and debug registers.
+ */
+struct user_pt_regs {
+ __u64 regs[31];
+ __u64 sp;
+ __u64 pc;
+ __u64 pstate;
+};
+
+struct user_fpsimd_state {
+ __uint128_t vregs[32];
+ __u32 fpsr;
+ __u32 fpcr;
+};
+
+struct user_hwdebug_state {
+ __u32 dbg_info;
+ struct {
+ __u64 addr;
+ __u32 ctrl;
+ } dbg_regs[16];
+};
+
+#ifdef __KERNEL__
+
+/* sizeof(struct user) for AArch32 */
+#define COMPAT_USER_SZ 296
+/* AArch32 uses x13 as the stack pointer... */
+#define compat_sp regs[13]
+/* ... and x14 as the link register. */
+#define compat_lr regs[14]
+
+/*
+ * This struct defines the way the registers are stored on the stack during an
+ * exception. Note that sizeof(struct pt_regs) has to be a multiple of 16 (for
+ * stack alignment). struct user_pt_regs must form a prefix of struct pt_regs.
+ */
+struct pt_regs {
+ union {
+ struct user_pt_regs user_regs;
+ struct {
+ u64 regs[31];
+ u64 sp;
+ u64 pc;
+ u64 pstate;
+ };
+ };
+ u64 orig_x0;
+ u64 syscallno;
+};
+
+#define arch_has_single_step() (1)
+
+#ifdef CONFIG_COMPAT
+#define compat_thumb_mode(regs) \
+ (((regs)->pstate & COMPAT_PSR_T_BIT))
+#else
+#define compat_thumb_mode(regs) (0)
+#endif
+
+#define user_mode(regs) \
+ (((regs)->pstate & PSR_MODE_MASK) == PSR_MODE_EL0t)
+
+#define compat_user_mode(regs) \
+ (((regs)->pstate & (PSR_MODE32_BIT | PSR_MODE_MASK)) == \
+ (PSR_MODE32_BIT | PSR_MODE_EL0t))
+
+#define processor_mode(regs) \
+ ((regs)->pstate & PSR_MODE_MASK)
+
+#define interrupts_enabled(regs) \
+ (!((regs)->pstate & PSR_I_BIT))
+
+#define fast_interrupts_enabled(regs) \
+ (!((regs)->pstate & PSR_F_BIT))
+
+#define user_stack_pointer(regs) \
+ ((regs)->sp)
+
+/*
+ * Are the current registers suitable for user mode? (used to maintain
+ * security in signal handlers)
+ */
+static inline int valid_user_regs(struct user_pt_regs *regs)
+{
+ if (user_mode(regs) && (regs->pstate & PSR_I_BIT) == 0) {
+ regs->pstate &= ~(PSR_F_BIT | PSR_A_BIT);
+
+ /* The T bit is reserved for AArch64 */
+ if (!(regs->pstate & PSR_MODE32_BIT))
+ regs->pstate &= ~COMPAT_PSR_T_BIT;
+
+ return 1;
+ }
+
+ /*
+ * Force PSR to something logical...
+ */
+ regs->pstate &= PSR_f | PSR_s | (PSR_x & ~PSR_A_BIT) | \
+ COMPAT_PSR_T_BIT | PSR_MODE32_BIT;
+
+ if (!(regs->pstate & PSR_MODE32_BIT)) {
+ regs->pstate &= ~COMPAT_PSR_T_BIT;
+ regs->pstate |= PSR_MODE_EL0t;
+ }
+
+ return 0;
+}
+
+#define instruction_pointer(regs) (regs)->pc
+
+#ifdef CONFIG_SMP
+extern unsigned long profile_pc(struct pt_regs *regs);
+#else
+#define profile_pc(regs) instruction_pointer(regs)
+#endif
+
+extern int aarch32_break_trap(struct pt_regs *regs);
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASSEMBLY__ */
+
+#endif
diff --git a/arch/arm64/include/asm/setup.h b/arch/arm64/include/asm/setup.h
new file mode 100644
index 000000000000..9cf2e46fbbdf
--- /dev/null
+++ b/arch/arm64/include/asm/setup.h
@@ -0,0 +1,26 @@
+/*
+ * Based on arch/arm/include/asm/setup.h
+ *
+ * Copyright (C) 1997-1999 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SETUP_H
+#define __ASM_SETUP_H
+
+#include <linux/types.h>
+
+#define COMMAND_LINE_SIZE 2048
+
+#endif
diff --git a/arch/arm64/include/asm/shmparam.h b/arch/arm64/include/asm/shmparam.h
new file mode 100644
index 000000000000..4df608a8459e
--- /dev/null
+++ b/arch/arm64/include/asm/shmparam.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SHMPARAM_H
+#define __ASM_SHMPARAM_H
+
+/*
+ * For IPC syscalls from compat tasks, we need to use the legacy 16k
+ * alignment value. Since we don't have aliasing D-caches, the rest of
+ * the time we can safely use PAGE_SIZE.
+ */
+#define COMPAT_SHMLBA 0x4000
+
+#include <asm-generic/shmparam.h>
+
+#endif /* __ASM_SHMPARAM_H */
diff --git a/arch/arm64/include/asm/sigcontext.h b/arch/arm64/include/asm/sigcontext.h
new file mode 100644
index 000000000000..573cec778819
--- /dev/null
+++ b/arch/arm64/include/asm/sigcontext.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SIGCONTEXT_H
+#define __ASM_SIGCONTEXT_H
+
+#include <linux/types.h>
+
+/*
+ * Signal context structure - contains all info to do with the state
+ * before the signal handler was invoked.
+ */
+struct sigcontext {
+ __u64 fault_address;
+ /* AArch64 registers */
+ __u64 regs[31];
+ __u64 sp;
+ __u64 pc;
+ __u64 pstate;
+ /* 4K reserved for FP/SIMD state and future expansion */
+ __u8 __reserved[4096] __attribute__((__aligned__(16)));
+};
+
+/*
+ * Header to be used at the beginning of structures extending the user
+ * context. Such structures must be placed after the rt_sigframe on the stack
+ * and be 16-byte aligned. The last structure must be a dummy one with the
+ * magic and size set to 0.
+ */
+struct _aarch64_ctx {
+ __u32 magic;
+ __u32 size;
+};
+
+#define FPSIMD_MAGIC 0x46508001
+
+struct fpsimd_context {
+ struct _aarch64_ctx head;
+ __u32 fpsr;
+ __u32 fpcr;
+ __uint128_t vregs[32];
+};
+
+#ifdef __KERNEL__
+/*
+ * Auxiliary context saved in the sigcontext.__reserved array. Not exported to
+ * user space as it will change with the addition of new context. User space
+ * should check the magic/size information.
+ */
+struct aux_context {
+ struct fpsimd_context fpsimd;
+ /* additional context to be added before "end" */
+ struct _aarch64_ctx end;
+};
+#endif
+
+#endif
diff --git a/arch/arm64/include/asm/siginfo.h b/arch/arm64/include/asm/siginfo.h
new file mode 100644
index 000000000000..5a74a0853db0
--- /dev/null
+++ b/arch/arm64/include/asm/siginfo.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SIGINFO_H
+#define __ASM_SIGINFO_H
+
+#define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int))
+
+#include <asm-generic/siginfo.h>
+
+#endif
diff --git a/arch/arm64/include/asm/signal.h b/arch/arm64/include/asm/signal.h
new file mode 100644
index 000000000000..8d1e7236431b
--- /dev/null
+++ b/arch/arm64/include/asm/signal.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SIGNAL_H
+#define __ASM_SIGNAL_H
+
+/* Required for AArch32 compatibility. */
+#define SA_RESTORER 0x04000000
+
+#include <asm-generic/signal.h>
+
+#endif
diff --git a/arch/arm64/include/asm/signal32.h b/arch/arm64/include/asm/signal32.h
new file mode 100644
index 000000000000..7c275e3b640f
--- /dev/null
+++ b/arch/arm64/include/asm/signal32.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SIGNAL32_H
+#define __ASM_SIGNAL32_H
+
+#ifdef __KERNEL__
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+
+#define AARCH32_KERN_SIGRET_CODE_OFFSET 0x500
+
+extern const compat_ulong_t aarch32_sigret_code[6];
+
+int compat_setup_frame(int usig, struct k_sigaction *ka, sigset_t *set,
+ struct pt_regs *regs);
+int compat_setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs);
+
+void compat_setup_restart_syscall(struct pt_regs *regs);
+#else
+
+static inline int compat_setup_frame(int usid, struct k_sigaction *ka,
+ sigset_t *set, struct pt_regs *regs)
+{
+ return -ENOSYS;
+}
+
+static inline int compat_setup_rt_frame(int usig, struct k_sigaction *ka,
+ siginfo_t *info, sigset_t *set,
+ struct pt_regs *regs)
+{
+ return -ENOSYS;
+}
+
+static inline void compat_setup_restart_syscall(struct pt_regs *regs)
+{
+}
+#endif /* CONFIG_COMPAT */
+#endif /* __KERNEL__ */
+#endif /* __ASM_SIGNAL32_H */
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h
new file mode 100644
index 000000000000..7e34295f78e3
--- /dev/null
+++ b/arch/arm64/include/asm/smp.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SMP_H
+#define __ASM_SMP_H
+
+#include <linux/threads.h>
+#include <linux/cpumask.h>
+#include <linux/thread_info.h>
+
+#ifndef CONFIG_SMP
+# error "<asm/smp.h> included in non-SMP build"
+#endif
+
+#define raw_smp_processor_id() (current_thread_info()->cpu)
+
+struct seq_file;
+
+/*
+ * generate IPI list text
+ */
+extern void show_ipi_list(struct seq_file *p, int prec);
+
+/*
+ * Called from C code, this handles an IPI.
+ */
+extern void handle_IPI(int ipinr, struct pt_regs *regs);
+
+/*
+ * Setup the set of possible CPUs (via set_cpu_possible)
+ */
+extern void smp_init_cpus(void);
+
+/*
+ * Provide a function to raise an IPI cross call on CPUs in callmap.
+ */
+extern void set_smp_cross_call(void (*)(const struct cpumask *, unsigned int));
+
+/*
+ * Called from the secondary holding pen, this is the secondary CPU entry point.
+ */
+asmlinkage void secondary_start_kernel(void);
+
+/*
+ * Initial data for bringing up a secondary CPU.
+ */
+struct secondary_data {
+ void *stack;
+};
+extern struct secondary_data secondary_data;
+extern void secondary_holding_pen(void);
+extern volatile unsigned long secondary_holding_pen_release;
+
+extern void arch_send_call_function_single_ipi(int cpu);
+extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
+
+#endif /* ifndef __ASM_SMP_H */
diff --git a/arch/arm64/include/asm/sparsemem.h b/arch/arm64/include/asm/sparsemem.h
new file mode 100644
index 000000000000..1be62bcb9d47
--- /dev/null
+++ b/arch/arm64/include/asm/sparsemem.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SPARSEMEM_H
+#define __ASM_SPARSEMEM_H
+
+#ifdef CONFIG_SPARSEMEM
+#define MAX_PHYSMEM_BITS 40
+#define SECTION_SIZE_BITS 30
+#endif
+
+#endif
diff --git a/arch/arm64/include/asm/spinlock.h b/arch/arm64/include/asm/spinlock.h
new file mode 100644
index 000000000000..41112fe2f8b1
--- /dev/null
+++ b/arch/arm64/include/asm/spinlock.h
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SPINLOCK_H
+#define __ASM_SPINLOCK_H
+
+#include <asm/spinlock_types.h>
+#include <asm/processor.h>
+
+/*
+ * Spinlock implementation.
+ *
+ * The old value is read exclusively and the new one, if unlocked, is written
+ * exclusively. In case of failure, the loop is restarted.
+ *
+ * The memory barriers are implicit with the load-acquire and store-release
+ * instructions.
+ *
+ * Unlocked value: 0
+ * Locked value: 1
+ */
+
+#define arch_spin_is_locked(x) ((x)->lock != 0)
+#define arch_spin_unlock_wait(lock) \
+ do { while (arch_spin_is_locked(lock)) cpu_relax(); } while (0)
+
+#define arch_spin_lock_flags(lock, flags) arch_spin_lock(lock)
+
+static inline void arch_spin_lock(arch_spinlock_t *lock)
+{
+ unsigned int tmp;
+
+ asm volatile(
+ " sevl\n"
+ "1: wfe\n"
+ "2: ldaxr %w0, [%1]\n"
+ " cbnz %w0, 1b\n"
+ " stxr %w0, %w2, [%1]\n"
+ " cbnz %w0, 2b\n"
+ : "=&r" (tmp)
+ : "r" (&lock->lock), "r" (1)
+ : "memory");
+}
+
+static inline int arch_spin_trylock(arch_spinlock_t *lock)
+{
+ unsigned int tmp;
+
+ asm volatile(
+ " ldaxr %w0, [%1]\n"
+ " cbnz %w0, 1f\n"
+ " stxr %w0, %w2, [%1]\n"
+ "1:\n"
+ : "=&r" (tmp)
+ : "r" (&lock->lock), "r" (1)
+ : "memory");
+
+ return !tmp;
+}
+
+static inline void arch_spin_unlock(arch_spinlock_t *lock)
+{
+ asm volatile(
+ " stlr %w1, [%0]\n"
+ : : "r" (&lock->lock), "r" (0) : "memory");
+}
+
+/*
+ * Write lock implementation.
+ *
+ * Write locks set bit 31. Unlocking, is done by writing 0 since the lock is
+ * exclusively held.
+ *
+ * The memory barriers are implicit with the load-acquire and store-release
+ * instructions.
+ */
+
+static inline void arch_write_lock(arch_rwlock_t *rw)
+{
+ unsigned int tmp;
+
+ asm volatile(
+ " sevl\n"
+ "1: wfe\n"
+ "2: ldaxr %w0, [%1]\n"
+ " cbnz %w0, 1b\n"
+ " stxr %w0, %w2, [%1]\n"
+ " cbnz %w0, 2b\n"
+ : "=&r" (tmp)
+ : "r" (&rw->lock), "r" (0x80000000)
+ : "memory");
+}
+
+static inline int arch_write_trylock(arch_rwlock_t *rw)
+{
+ unsigned int tmp;
+
+ asm volatile(
+ " ldaxr %w0, [%1]\n"
+ " cbnz %w0, 1f\n"
+ " stxr %w0, %w2, [%1]\n"
+ "1:\n"
+ : "=&r" (tmp)
+ : "r" (&rw->lock), "r" (0x80000000)
+ : "memory");
+
+ return !tmp;
+}
+
+static inline void arch_write_unlock(arch_rwlock_t *rw)
+{
+ asm volatile(
+ " stlr %w1, [%0]\n"
+ : : "r" (&rw->lock), "r" (0) : "memory");
+}
+
+/* write_can_lock - would write_trylock() succeed? */
+#define arch_write_can_lock(x) ((x)->lock == 0)
+
+/*
+ * Read lock implementation.
+ *
+ * It exclusively loads the lock value, increments it and stores the new value
+ * back if positive and the CPU still exclusively owns the location. If the
+ * value is negative, the lock is already held.
+ *
+ * During unlocking there may be multiple active read locks but no write lock.
+ *
+ * The memory barriers are implicit with the load-acquire and store-release
+ * instructions.
+ */
+static inline void arch_read_lock(arch_rwlock_t *rw)
+{
+ unsigned int tmp, tmp2;
+
+ asm volatile(
+ " sevl\n"
+ "1: wfe\n"
+ "2: ldaxr %w0, [%2]\n"
+ " add %w0, %w0, #1\n"
+ " tbnz %w0, #31, 1b\n"
+ " stxr %w1, %w0, [%2]\n"
+ " cbnz %w1, 2b\n"
+ : "=&r" (tmp), "=&r" (tmp2)
+ : "r" (&rw->lock)
+ : "memory");
+}
+
+static inline void arch_read_unlock(arch_rwlock_t *rw)
+{
+ unsigned int tmp, tmp2;
+
+ asm volatile(
+ "1: ldxr %w0, [%2]\n"
+ " sub %w0, %w0, #1\n"
+ " stlxr %w1, %w0, [%2]\n"
+ " cbnz %w1, 1b\n"
+ : "=&r" (tmp), "=&r" (tmp2)
+ : "r" (&rw->lock)
+ : "memory");
+}
+
+static inline int arch_read_trylock(arch_rwlock_t *rw)
+{
+ unsigned int tmp, tmp2 = 1;
+
+ asm volatile(
+ " ldaxr %w0, [%2]\n"
+ " add %w0, %w0, #1\n"
+ " tbnz %w0, #31, 1f\n"
+ " stxr %w1, %w0, [%2]\n"
+ "1:\n"
+ : "=&r" (tmp), "+r" (tmp2)
+ : "r" (&rw->lock)
+ : "memory");
+
+ return !tmp2;
+}
+
+/* read_can_lock - would read_trylock() succeed? */
+#define arch_read_can_lock(x) ((x)->lock < 0x80000000)
+
+#define arch_read_lock_flags(lock, flags) arch_read_lock(lock)
+#define arch_write_lock_flags(lock, flags) arch_write_lock(lock)
+
+#define arch_spin_relax(lock) cpu_relax()
+#define arch_read_relax(lock) cpu_relax()
+#define arch_write_relax(lock) cpu_relax()
+
+#endif /* __ASM_SPINLOCK_H */
diff --git a/arch/arm64/include/asm/spinlock_types.h b/arch/arm64/include/asm/spinlock_types.h
new file mode 100644
index 000000000000..9a494346efed
--- /dev/null
+++ b/arch/arm64/include/asm/spinlock_types.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SPINLOCK_TYPES_H
+#define __ASM_SPINLOCK_TYPES_H
+
+#if !defined(__LINUX_SPINLOCK_TYPES_H) && !defined(__ASM_SPINLOCK_H)
+# error "please don't include this file directly"
+#endif
+
+/* We only require natural alignment for exclusive accesses. */
+#define __lock_aligned
+
+typedef struct {
+ volatile unsigned int lock;
+} arch_spinlock_t;
+
+#define __ARCH_SPIN_LOCK_UNLOCKED { 0 }
+
+typedef struct {
+ volatile unsigned int lock;
+} arch_rwlock_t;
+
+#define __ARCH_RW_LOCK_UNLOCKED { 0 }
+
+#endif
diff --git a/arch/arm64/include/asm/stacktrace.h b/arch/arm64/include/asm/stacktrace.h
new file mode 100644
index 000000000000..7318f6d54aa9
--- /dev/null
+++ b/arch/arm64/include/asm/stacktrace.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_STACKTRACE_H
+#define __ASM_STACKTRACE_H
+
+struct stackframe {
+ unsigned long fp;
+ unsigned long sp;
+ unsigned long pc;
+};
+
+extern int unwind_frame(struct stackframe *frame);
+extern void walk_stackframe(struct stackframe *frame,
+ int (*fn)(struct stackframe *, void *), void *data);
+
+#endif /* __ASM_STACKTRACE_H */
diff --git a/arch/arm64/include/asm/stat.h b/arch/arm64/include/asm/stat.h
new file mode 100644
index 000000000000..d87225cbead8
--- /dev/null
+++ b/arch/arm64/include/asm/stat.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_STAT_H
+#define __ASM_STAT_H
+
+#include <asm-generic/stat.h>
+
+#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
+
+#include <asm/compat.h>
+
+/*
+ * struct stat64 is needed for compat tasks only. Its definition is different
+ * from the generic struct stat64.
+ */
+struct stat64 {
+ compat_u64 st_dev;
+ unsigned char __pad0[4];
+
+#define STAT64_HAS_BROKEN_ST_INO 1
+ compat_ulong_t __st_ino;
+ compat_uint_t st_mode;
+ compat_uint_t st_nlink;
+
+ compat_ulong_t st_uid;
+ compat_ulong_t st_gid;
+
+ compat_u64 st_rdev;
+ unsigned char __pad3[4];
+
+ compat_s64 st_size;
+ compat_ulong_t st_blksize;
+ compat_u64 st_blocks; /* Number of 512-byte blocks allocated. */
+
+ compat_ulong_t st_atime;
+ compat_ulong_t st_atime_nsec;
+
+ compat_ulong_t st_mtime;
+ compat_ulong_t st_mtime_nsec;
+
+ compat_ulong_t st_ctime;
+ compat_ulong_t st_ctime_nsec;
+
+ compat_u64 st_ino;
+};
+
+#endif
+
+#endif
diff --git a/arch/arm64/include/asm/statfs.h b/arch/arm64/include/asm/statfs.h
new file mode 100644
index 000000000000..6f6219050978
--- /dev/null
+++ b/arch/arm64/include/asm/statfs.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_STATFS_H
+#define __ASM_STATFS_H
+
+#define ARCH_PACK_COMPAT_STATFS64 __attribute__((packed,aligned(4)))
+
+#include <asm-generic/statfs.h>
+
+#endif
diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h
new file mode 100644
index 000000000000..89c047f9a971
--- /dev/null
+++ b/arch/arm64/include/asm/syscall.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SYSCALL_H
+#define __ASM_SYSCALL_H
+
+#include <linux/err.h>
+
+
+static inline int syscall_get_nr(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ return regs->syscallno;
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ regs->regs[0] = regs->orig_x0;
+}
+
+
+static inline long syscall_get_error(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ unsigned long error = regs->regs[0];
+ return IS_ERR_VALUE(error) ? error : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+ struct pt_regs *regs)
+{
+ return regs->regs[0];
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+ struct pt_regs *regs,
+ int error, long val)
+{
+ regs->regs[0] = (long) error ? error : val;
+}
+
+#define SYSCALL_MAX_ARGS 6
+
+static inline void syscall_get_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ unsigned long *args)
+{
+ if (i + n > SYSCALL_MAX_ARGS) {
+ unsigned long *args_bad = args + SYSCALL_MAX_ARGS - i;
+ unsigned int n_bad = n + i - SYSCALL_MAX_ARGS;
+ pr_warning("%s called with max args %d, handling only %d\n",
+ __func__, i + n, SYSCALL_MAX_ARGS);
+ memset(args_bad, 0, n_bad * sizeof(args[0]));
+ }
+
+ if (i == 0) {
+ args[0] = regs->orig_x0;
+ args++;
+ i++;
+ n--;
+ }
+
+ memcpy(args, &regs->regs[i], n * sizeof(args[0]));
+}
+
+static inline void syscall_set_arguments(struct task_struct *task,
+ struct pt_regs *regs,
+ unsigned int i, unsigned int n,
+ const unsigned long *args)
+{
+ if (i + n > SYSCALL_MAX_ARGS) {
+ pr_warning("%s called with max args %d, handling only %d\n",
+ __func__, i + n, SYSCALL_MAX_ARGS);
+ n = SYSCALL_MAX_ARGS - i;
+ }
+
+ if (i == 0) {
+ regs->orig_x0 = args[0];
+ args++;
+ i++;
+ n--;
+ }
+
+ memcpy(&regs->regs[i], args, n * sizeof(args[0]));
+}
+
+#endif /* __ASM_SYSCALL_H */
diff --git a/arch/arm64/include/asm/syscalls.h b/arch/arm64/include/asm/syscalls.h
new file mode 100644
index 000000000000..09ff33572aab
--- /dev/null
+++ b/arch/arm64/include/asm/syscalls.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SYSCALLS_H
+#define __ASM_SYSCALLS_H
+
+#include <linux/linkage.h>
+#include <linux/compiler.h>
+#include <linux/signal.h>
+
+/*
+ * System call wrappers implemented in kernel/entry.S.
+ */
+asmlinkage long sys_execve_wrapper(const char __user *filename,
+ const char __user *const __user *argv,
+ const char __user *const __user *envp);
+asmlinkage long sys_clone_wrapper(unsigned long clone_flags,
+ unsigned long newsp,
+ void __user *parent_tid,
+ unsigned long tls_val,
+ void __user *child_tid);
+asmlinkage long sys_rt_sigreturn_wrapper(void);
+asmlinkage long sys_sigaltstack_wrapper(const stack_t __user *uss,
+ stack_t __user *uoss);
+
+#include <asm-generic/syscalls.h>
+
+#endif /* __ASM_SYSCALLS_H */
diff --git a/arch/arm64/include/asm/system_misc.h b/arch/arm64/include/asm/system_misc.h
new file mode 100644
index 000000000000..95e407255347
--- /dev/null
+++ b/arch/arm64/include/asm/system_misc.h
@@ -0,0 +1,54 @@
+/*
+ * Based on arch/arm/include/asm/system_misc.h
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_SYSTEM_MISC_H
+#define __ASM_SYSTEM_MISC_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/compiler.h>
+#include <linux/linkage.h>
+#include <linux/irqflags.h>
+
+struct pt_regs;
+
+void die(const char *msg, struct pt_regs *regs, int err);
+
+struct siginfo;
+void arm64_notify_die(const char *str, struct pt_regs *regs,
+ struct siginfo *info, int err);
+
+void hook_debug_fault_code(int nr, int (*fn)(unsigned long, unsigned int,
+ struct pt_regs *),
+ int sig, int code, const char *name);
+
+struct mm_struct;
+extern void show_pte(struct mm_struct *mm, unsigned long addr);
+extern void __show_regs(struct pt_regs *);
+
+void soft_restart(unsigned long);
+extern void (*pm_restart)(const char *cmd);
+
+#define UDBG_UNDEFINED (1 << 0)
+#define UDBG_SYSCALL (1 << 1)
+#define UDBG_BADABORT (1 << 2)
+#define UDBG_SEGV (1 << 3)
+#define UDBG_BUS (1 << 4)
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_SYSTEM_MISC_H */
diff --git a/arch/arm64/include/asm/thread_info.h b/arch/arm64/include/asm/thread_info.h
new file mode 100644
index 000000000000..3659e460071d
--- /dev/null
+++ b/arch/arm64/include/asm/thread_info.h
@@ -0,0 +1,127 @@
+/*
+ * Based on arch/arm/include/asm/thread_info.h
+ *
+ * Copyright (C) 2002 Russell King.
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_THREAD_INFO_H
+#define __ASM_THREAD_INFO_H
+
+#ifdef __KERNEL__
+
+#include <linux/compiler.h>
+
+#ifndef CONFIG_ARM64_64K_PAGES
+#define THREAD_SIZE_ORDER 1
+#endif
+
+#define THREAD_SIZE 8192
+#define THREAD_START_SP (THREAD_SIZE - 16)
+
+#ifndef __ASSEMBLY__
+
+struct task_struct;
+struct exec_domain;
+
+#include <asm/types.h>
+
+typedef unsigned long mm_segment_t;
+
+/*
+ * low level task data that entry.S needs immediate access to.
+ * __switch_to() assumes cpu_context follows immediately after cpu_domain.
+ */
+struct thread_info {
+ unsigned long flags; /* low level flags */
+ mm_segment_t addr_limit; /* address limit */
+ struct task_struct *task; /* main task structure */
+ struct exec_domain *exec_domain; /* execution domain */
+ struct restart_block restart_block;
+ int preempt_count; /* 0 => preemptable, <0 => bug */
+ int cpu; /* cpu */
+};
+
+#define INIT_THREAD_INFO(tsk) \
+{ \
+ .task = &tsk, \
+ .exec_domain = &default_exec_domain, \
+ .flags = 0, \
+ .preempt_count = INIT_PREEMPT_COUNT, \
+ .addr_limit = KERNEL_DS, \
+ .restart_block = { \
+ .fn = do_no_restart_syscall, \
+ }, \
+}
+
+#define init_thread_info (init_thread_union.thread_info)
+#define init_stack (init_thread_union.stack)
+
+/*
+ * how to get the thread information struct from C
+ */
+static inline struct thread_info *current_thread_info(void) __attribute_const__;
+
+static inline struct thread_info *current_thread_info(void)
+{
+ register unsigned long sp asm ("sp");
+ return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
+}
+
+#define thread_saved_pc(tsk) \
+ ((unsigned long)(tsk->thread.cpu_context.pc))
+#define thread_saved_sp(tsk) \
+ ((unsigned long)(tsk->thread.cpu_context.sp))
+#define thread_saved_fp(tsk) \
+ ((unsigned long)(tsk->thread.cpu_context.fp))
+
+#endif
+
+/*
+ * We use bit 30 of the preempt_count to indicate that kernel
+ * preemption is occurring. See <asm/hardirq.h>.
+ */
+#define PREEMPT_ACTIVE 0x40000000
+
+/*
+ * thread information flags:
+ * TIF_SYSCALL_TRACE - syscall trace active
+ * TIF_SIGPENDING - signal pending
+ * TIF_NEED_RESCHED - rescheduling necessary
+ * TIF_NOTIFY_RESUME - callback before returning to user
+ * TIF_USEDFPU - FPU was used by this task this quantum (SMP)
+ * TIF_POLLING_NRFLAG - true if poll_idle() is polling TIF_NEED_RESCHED
+ */
+#define TIF_SIGPENDING 0
+#define TIF_NEED_RESCHED 1
+#define TIF_NOTIFY_RESUME 2 /* callback before returning to user */
+#define TIF_SYSCALL_TRACE 8
+#define TIF_POLLING_NRFLAG 16
+#define TIF_MEMDIE 18 /* is terminating due to OOM killer */
+#define TIF_FREEZE 19
+#define TIF_RESTORE_SIGMASK 20
+#define TIF_SINGLESTEP 21
+#define TIF_32BIT 22 /* 32bit process */
+#define TIF_SWITCH_MM 23 /* deferred switch_mm */
+
+#define _TIF_SIGPENDING (1 << TIF_SIGPENDING)
+#define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED)
+#define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME)
+#define _TIF_32BIT (1 << TIF_32BIT)
+
+#define _TIF_WORK_MASK (_TIF_NEED_RESCHED | _TIF_SIGPENDING | \
+ _TIF_NOTIFY_RESUME)
+
+#endif /* __KERNEL__ */
+#endif /* __ASM_THREAD_INFO_H */
diff --git a/arch/arm64/include/asm/timex.h b/arch/arm64/include/asm/timex.h
new file mode 100644
index 000000000000..b24a31a7e2c9
--- /dev/null
+++ b/arch/arm64/include/asm/timex.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_TIMEX_H
+#define __ASM_TIMEX_H
+
+/*
+ * Use the current timer as a cycle counter since this is what we use for
+ * the delay loop.
+ */
+#define get_cycles() ({ cycles_t c; read_current_timer(&c); c; })
+
+#include <asm-generic/timex.h>
+
+#define ARCH_HAS_READ_CURRENT_TIMER
+
+#endif
diff --git a/arch/arm64/include/asm/tlb.h b/arch/arm64/include/asm/tlb.h
new file mode 100644
index 000000000000..654f0968030b
--- /dev/null
+++ b/arch/arm64/include/asm/tlb.h
@@ -0,0 +1,190 @@
+/*
+ * Based on arch/arm/include/asm/tlb.h
+ *
+ * Copyright (C) 2002 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_TLB_H
+#define __ASM_TLB_H
+
+#include <linux/pagemap.h>
+#include <linux/swap.h>
+
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+
+#define MMU_GATHER_BUNDLE 8
+
+/*
+ * TLB handling. This allows us to remove pages from the page
+ * tables, and efficiently handle the TLB issues.
+ */
+struct mmu_gather {
+ struct mm_struct *mm;
+ unsigned int fullmm;
+ struct vm_area_struct *vma;
+ unsigned long range_start;
+ unsigned long range_end;
+ unsigned int nr;
+ unsigned int max;
+ struct page **pages;
+ struct page *local[MMU_GATHER_BUNDLE];
+};
+
+/*
+ * This is unnecessarily complex. There's three ways the TLB shootdown
+ * code is used:
+ * 1. Unmapping a range of vmas. See zap_page_range(), unmap_region().
+ * tlb->fullmm = 0, and tlb_start_vma/tlb_end_vma will be called.
+ * tlb->vma will be non-NULL.
+ * 2. Unmapping all vmas. See exit_mmap().
+ * tlb->fullmm = 1, and tlb_start_vma/tlb_end_vma will be called.
+ * tlb->vma will be non-NULL. Additionally, page tables will be freed.
+ * 3. Unmapping argument pages. See shift_arg_pages().
+ * tlb->fullmm = 0, but tlb_start_vma/tlb_end_vma will not be called.
+ * tlb->vma will be NULL.
+ */
+static inline void tlb_flush(struct mmu_gather *tlb)
+{
+ if (tlb->fullmm || !tlb->vma)
+ flush_tlb_mm(tlb->mm);
+ else if (tlb->range_end > 0) {
+ flush_tlb_range(tlb->vma, tlb->range_start, tlb->range_end);
+ tlb->range_start = TASK_SIZE;
+ tlb->range_end = 0;
+ }
+}
+
+static inline void tlb_add_flush(struct mmu_gather *tlb, unsigned long addr)
+{
+ if (!tlb->fullmm) {
+ if (addr < tlb->range_start)
+ tlb->range_start = addr;
+ if (addr + PAGE_SIZE > tlb->range_end)
+ tlb->range_end = addr + PAGE_SIZE;
+ }
+}
+
+static inline void __tlb_alloc_page(struct mmu_gather *tlb)
+{
+ unsigned long addr = __get_free_pages(GFP_NOWAIT | __GFP_NOWARN, 0);
+
+ if (addr) {
+ tlb->pages = (void *)addr;
+ tlb->max = PAGE_SIZE / sizeof(struct page *);
+ }
+}
+
+static inline void tlb_flush_mmu(struct mmu_gather *tlb)
+{
+ tlb_flush(tlb);
+ free_pages_and_swap_cache(tlb->pages, tlb->nr);
+ tlb->nr = 0;
+ if (tlb->pages == tlb->local)
+ __tlb_alloc_page(tlb);
+}
+
+static inline void
+tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm, unsigned int fullmm)
+{
+ tlb->mm = mm;
+ tlb->fullmm = fullmm;
+ tlb->vma = NULL;
+ tlb->max = ARRAY_SIZE(tlb->local);
+ tlb->pages = tlb->local;
+ tlb->nr = 0;
+ __tlb_alloc_page(tlb);
+}
+
+static inline void
+tlb_finish_mmu(struct mmu_gather *tlb, unsigned long start, unsigned long end)
+{
+ tlb_flush_mmu(tlb);
+
+ /* keep the page table cache within bounds */
+ check_pgt_cache();
+
+ if (tlb->pages != tlb->local)
+ free_pages((unsigned long)tlb->pages, 0);
+}
+
+/*
+ * Memorize the range for the TLB flush.
+ */
+static inline void
+tlb_remove_tlb_entry(struct mmu_gather *tlb, pte_t *ptep, unsigned long addr)
+{
+ tlb_add_flush(tlb, addr);
+}
+
+/*
+ * In the case of tlb vma handling, we can optimise these away in the
+ * case where we're doing a full MM flush. When we're doing a munmap,
+ * the vmas are adjusted to only cover the region to be torn down.
+ */
+static inline void
+tlb_start_vma(struct mmu_gather *tlb, struct vm_area_struct *vma)
+{
+ if (!tlb->fullmm) {
+ tlb->vma = vma;
+ tlb->range_start = TASK_SIZE;
+ tlb->range_end = 0;
+ }
+}
+
+static inline void
+tlb_end_vma(struct mmu_gather *tlb, struct vm_area_struct *vma)
+{
+ if (!tlb->fullmm)
+ tlb_flush(tlb);
+}
+
+static inline int __tlb_remove_page(struct mmu_gather *tlb, struct page *page)
+{
+ tlb->pages[tlb->nr++] = page;
+ VM_BUG_ON(tlb->nr > tlb->max);
+ return tlb->max - tlb->nr;
+}
+
+static inline void tlb_remove_page(struct mmu_gather *tlb, struct page *page)
+{
+ if (!__tlb_remove_page(tlb, page))
+ tlb_flush_mmu(tlb);
+}
+
+static inline void __pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
+ unsigned long addr)
+{
+ pgtable_page_dtor(pte);
+ tlb_add_flush(tlb, addr);
+ tlb_remove_page(tlb, pte);
+}
+
+#ifndef CONFIG_ARM64_64K_PAGES
+static inline void __pmd_free_tlb(struct mmu_gather *tlb, pmd_t *pmdp,
+ unsigned long addr)
+{
+ tlb_add_flush(tlb, addr);
+ tlb_remove_page(tlb, virt_to_page(pmdp));
+}
+#endif
+
+#define pte_free_tlb(tlb, ptep, addr) __pte_free_tlb(tlb, ptep, addr)
+#define pmd_free_tlb(tlb, pmdp, addr) __pmd_free_tlb(tlb, pmdp, addr)
+#define pud_free_tlb(tlb, pudp, addr) pud_free((tlb)->mm, pudp)
+
+#define tlb_migrate_finish(mm) do { } while (0)
+
+#endif
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
new file mode 100644
index 000000000000..122d6320f745
--- /dev/null
+++ b/arch/arm64/include/asm/tlbflush.h
@@ -0,0 +1,122 @@
+/*
+ * Based on arch/arm/include/asm/tlbflush.h
+ *
+ * Copyright (C) 1999-2003 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_TLBFLUSH_H
+#define __ASM_TLBFLUSH_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/sched.h>
+#include <asm/cputype.h>
+
+extern void __cpu_flush_user_tlb_range(unsigned long, unsigned long, struct vm_area_struct *);
+extern void __cpu_flush_kern_tlb_range(unsigned long, unsigned long);
+
+extern struct cpu_tlb_fns cpu_tlb;
+
+/*
+ * TLB Management
+ * ==============
+ *
+ * The arch/arm64/mm/tlb.S files implement these methods.
+ *
+ * The TLB specific code is expected to perform whatever tests it needs
+ * to determine if it should invalidate the TLB for each call. Start
+ * addresses are inclusive and end addresses are exclusive; it is safe to
+ * round these addresses down.
+ *
+ * flush_tlb_all()
+ *
+ * Invalidate the entire TLB.
+ *
+ * flush_tlb_mm(mm)
+ *
+ * Invalidate all TLB entries in a particular address space.
+ * - mm - mm_struct describing address space
+ *
+ * flush_tlb_range(mm,start,end)
+ *
+ * Invalidate a range of TLB entries in the specified address
+ * space.
+ * - mm - mm_struct describing address space
+ * - start - start address (may not be aligned)
+ * - end - end address (exclusive, may not be aligned)
+ *
+ * flush_tlb_page(vaddr,vma)
+ *
+ * Invalidate the specified page in the specified address range.
+ * - vaddr - virtual address (may not be aligned)
+ * - vma - vma_struct describing address range
+ *
+ * flush_kern_tlb_page(kaddr)
+ *
+ * Invalidate the TLB entry for the specified page. The address
+ * will be in the kernels virtual memory space. Current uses
+ * only require the D-TLB to be invalidated.
+ * - kaddr - Kernel virtual memory address
+ */
+static inline void flush_tlb_all(void)
+{
+ dsb();
+ asm("tlbi vmalle1is");
+ dsb();
+ isb();
+}
+
+static inline void flush_tlb_mm(struct mm_struct *mm)
+{
+ unsigned long asid = (unsigned long)ASID(mm) << 48;
+
+ dsb();
+ asm("tlbi aside1is, %0" : : "r" (asid));
+ dsb();
+}
+
+static inline void flush_tlb_page(struct vm_area_struct *vma,
+ unsigned long uaddr)
+{
+ unsigned long addr = uaddr >> 12 |
+ ((unsigned long)ASID(vma->vm_mm) << 48);
+
+ dsb();
+ asm("tlbi vae1is, %0" : : "r" (addr));
+ dsb();
+}
+
+/*
+ * Convert calls to our calling convention.
+ */
+#define flush_tlb_range(vma,start,end) __cpu_flush_user_tlb_range(start,end,vma)
+#define flush_tlb_kernel_range(s,e) __cpu_flush_kern_tlb_range(s,e)
+
+/*
+ * On AArch64, the cache coherency is handled via the set_pte_at() function.
+ */
+static inline void update_mmu_cache(struct vm_area_struct *vma,
+ unsigned long addr, pte_t *ptep)
+{
+ /*
+ * set_pte() does not have a DSB, so make sure that the page table
+ * write is visible.
+ */
+ dsb();
+}
+
+#endif
+
+#endif
diff --git a/arch/arm64/include/asm/traps.h b/arch/arm64/include/asm/traps.h
new file mode 100644
index 000000000000..10ca8ff93cc2
--- /dev/null
+++ b/arch/arm64/include/asm/traps.h
@@ -0,0 +1,30 @@
+/*
+ * Based on arch/arm/include/asm/traps.h
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_TRAP_H
+#define __ASM_TRAP_H
+
+static inline int in_exception_text(unsigned long ptr)
+{
+ extern char __exception_text_start[];
+ extern char __exception_text_end[];
+
+ return ptr >= (unsigned long)&__exception_text_start &&
+ ptr < (unsigned long)&__exception_text_end;
+}
+
+#endif
diff --git a/arch/arm64/include/asm/uaccess.h b/arch/arm64/include/asm/uaccess.h
new file mode 100644
index 000000000000..008f8481da65
--- /dev/null
+++ b/arch/arm64/include/asm/uaccess.h
@@ -0,0 +1,297 @@
+/*
+ * Based on arch/arm/include/asm/uaccess.h
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_UACCESS_H
+#define __ASM_UACCESS_H
+
+/*
+ * User space memory access functions
+ */
+#include <linux/string.h>
+#include <linux/thread_info.h>
+
+#include <asm/ptrace.h>
+#include <asm/errno.h>
+#include <asm/memory.h>
+#include <asm/compiler.h>
+
+#define VERIFY_READ 0
+#define VERIFY_WRITE 1
+
+/*
+ * The exception table consists of pairs of addresses: the first is the
+ * address of an instruction that is allowed to fault, and the second is
+ * the address at which the program should continue. No registers are
+ * modified, so it is entirely up to the continuation code to figure out
+ * what to do.
+ *
+ * All the routines below use bits of fixup code that are out of line
+ * with the main instruction path. This means when everything is well,
+ * we don't even have to jump over them. Further, they do not intrude
+ * on our cache or tlb entries.
+ */
+
+struct exception_table_entry
+{
+ unsigned long insn, fixup;
+};
+
+extern int fixup_exception(struct pt_regs *regs);
+
+#define KERNEL_DS (-1UL)
+#define get_ds() (KERNEL_DS)
+
+#define USER_DS TASK_SIZE_64
+#define get_fs() (current_thread_info()->addr_limit)
+
+static inline void set_fs(mm_segment_t fs)
+{
+ current_thread_info()->addr_limit = fs;
+}
+
+#define segment_eq(a,b) ((a) == (b))
+
+/*
+ * Return 1 if addr < current->addr_limit, 0 otherwise.
+ */
+#define __addr_ok(addr) \
+({ \
+ unsigned long flag; \
+ asm("cmp %1, %0; cset %0, lo" \
+ : "=&r" (flag) \
+ : "r" (addr), "0" (current_thread_info()->addr_limit) \
+ : "cc"); \
+ flag; \
+})
+
+/*
+ * Test whether a block of memory is a valid user space address.
+ * Returns 1 if the range is valid, 0 otherwise.
+ *
+ * This is equivalent to the following test:
+ * (u65)addr + (u65)size < (u65)current->addr_limit
+ *
+ * This needs 65-bit arithmetic.
+ */
+#define __range_ok(addr, size) \
+({ \
+ unsigned long flag, roksum; \
+ __chk_user_ptr(addr); \
+ asm("adds %1, %1, %3; ccmp %1, %4, #2, cc; cset %0, cc" \
+ : "=&r" (flag), "=&r" (roksum) \
+ : "1" (addr), "Ir" (size), \
+ "r" (current_thread_info()->addr_limit) \
+ : "cc"); \
+ flag; \
+})
+
+#define access_ok(type, addr, size) __range_ok(addr, size)
+
+/*
+ * The "__xxx" versions of the user access functions do not verify the address
+ * space - it must have been done previously with a separate "access_ok()"
+ * call.
+ *
+ * The "__xxx_error" versions set the third argument to -EFAULT if an error
+ * occurs, and leave it unchanged on success.
+ */
+#define __get_user_asm(instr, reg, x, addr, err) \
+ asm volatile( \
+ "1: " instr " " reg "1, [%2]\n" \
+ "2:\n" \
+ " .section .fixup, \"ax\"\n" \
+ " .align 2\n" \
+ "3: mov %w0, %3\n" \
+ " mov %1, #0\n" \
+ " b 2b\n" \
+ " .previous\n" \
+ " .section __ex_table,\"a\"\n" \
+ " .align 3\n" \
+ " .quad 1b, 3b\n" \
+ " .previous" \
+ : "+r" (err), "=&r" (x) \
+ : "r" (addr), "i" (-EFAULT))
+
+#define __get_user_err(x, ptr, err) \
+do { \
+ unsigned long __gu_val; \
+ __chk_user_ptr(ptr); \
+ switch (sizeof(*(ptr))) { \
+ case 1: \
+ __get_user_asm("ldrb", "%w", __gu_val, (ptr), (err)); \
+ break; \
+ case 2: \
+ __get_user_asm("ldrh", "%w", __gu_val, (ptr), (err)); \
+ break; \
+ case 4: \
+ __get_user_asm("ldr", "%w", __gu_val, (ptr), (err)); \
+ break; \
+ case 8: \
+ __get_user_asm("ldr", "%", __gu_val, (ptr), (err)); \
+ break; \
+ default: \
+ BUILD_BUG(); \
+ } \
+ (x) = (__typeof__(*(ptr)))__gu_val; \
+} while (0)
+
+#define __get_user(x, ptr) \
+({ \
+ int __gu_err = 0; \
+ __get_user_err((x), (ptr), __gu_err); \
+ __gu_err; \
+})
+
+#define __get_user_error(x, ptr, err) \
+({ \
+ __get_user_err((x), (ptr), (err)); \
+ (void)0; \
+})
+
+#define __get_user_unaligned __get_user
+
+#define get_user(x, ptr) \
+({ \
+ might_sleep(); \
+ access_ok(VERIFY_READ, (ptr), sizeof(*(ptr))) ? \
+ __get_user((x), (ptr)) : \
+ ((x) = 0, -EFAULT); \
+})
+
+#define __put_user_asm(instr, reg, x, addr, err) \
+ asm volatile( \
+ "1: " instr " " reg "1, [%2]\n" \
+ "2:\n" \
+ " .section .fixup,\"ax\"\n" \
+ " .align 2\n" \
+ "3: mov %w0, %3\n" \
+ " b 2b\n" \
+ " .previous\n" \
+ " .section __ex_table,\"a\"\n" \
+ " .align 3\n" \
+ " .quad 1b, 3b\n" \
+ " .previous" \
+ : "+r" (err) \
+ : "r" (x), "r" (addr), "i" (-EFAULT))
+
+#define __put_user_err(x, ptr, err) \
+do { \
+ __typeof__(*(ptr)) __pu_val = (x); \
+ __chk_user_ptr(ptr); \
+ switch (sizeof(*(ptr))) { \
+ case 1: \
+ __put_user_asm("strb", "%w", __pu_val, (ptr), (err)); \
+ break; \
+ case 2: \
+ __put_user_asm("strh", "%w", __pu_val, (ptr), (err)); \
+ break; \
+ case 4: \
+ __put_user_asm("str", "%w", __pu_val, (ptr), (err)); \
+ break; \
+ case 8: \
+ __put_user_asm("str", "%", __pu_val, (ptr), (err)); \
+ break; \
+ default: \
+ BUILD_BUG(); \
+ } \
+} while (0)
+
+#define __put_user(x, ptr) \
+({ \
+ int __pu_err = 0; \
+ __put_user_err((x), (ptr), __pu_err); \
+ __pu_err; \
+})
+
+#define __put_user_error(x, ptr, err) \
+({ \
+ __put_user_err((x), (ptr), (err)); \
+ (void)0; \
+})
+
+#define __put_user_unaligned __put_user
+
+#define put_user(x, ptr) \
+({ \
+ might_sleep(); \
+ access_ok(VERIFY_WRITE, (ptr), sizeof(*(ptr))) ? \
+ __put_user((x), (ptr)) : \
+ -EFAULT; \
+})
+
+extern unsigned long __must_check __copy_from_user(void *to, const void __user *from, unsigned long n);
+extern unsigned long __must_check __copy_to_user(void __user *to, const void *from, unsigned long n);
+extern unsigned long __must_check __copy_in_user(void __user *to, const void __user *from, unsigned long n);
+extern unsigned long __must_check __clear_user(void __user *addr, unsigned long n);
+
+extern unsigned long __must_check __strncpy_from_user(char *to, const char __user *from, unsigned long count);
+extern unsigned long __must_check __strnlen_user(const char __user *s, long n);
+
+static inline unsigned long __must_check copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+ if (access_ok(VERIFY_READ, from, n))
+ n = __copy_from_user(to, from, n);
+ else /* security hole - plug it */
+ memset(to, 0, n);
+ return n;
+}
+
+static inline unsigned long __must_check copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+ if (access_ok(VERIFY_WRITE, to, n))
+ n = __copy_to_user(to, from, n);
+ return n;
+}
+
+static inline unsigned long __must_check copy_in_user(void __user *to, const void __user *from, unsigned long n)
+{
+ if (access_ok(VERIFY_READ, from, n) && access_ok(VERIFY_WRITE, to, n))
+ n = __copy_in_user(to, from, n);
+ return n;
+}
+
+#define __copy_to_user_inatomic __copy_to_user
+#define __copy_from_user_inatomic __copy_from_user
+
+static inline unsigned long __must_check clear_user(void __user *to, unsigned long n)
+{
+ if (access_ok(VERIFY_WRITE, to, n))
+ n = __clear_user(to, n);
+ return n;
+}
+
+static inline long __must_check strncpy_from_user(char *dst, const char __user *src, long count)
+{
+ long res = -EFAULT;
+ if (access_ok(VERIFY_READ, src, 1))
+ res = __strncpy_from_user(dst, src, count);
+ return res;
+}
+
+#define strlen_user(s) strnlen_user(s, ~0UL >> 1)
+
+static inline long __must_check strnlen_user(const char __user *s, long n)
+{
+ unsigned long res = 0;
+
+ if (__addr_ok(s))
+ res = __strnlen_user(s, n);
+
+ return res;
+}
+
+#endif /* __ASM_UACCESS_H */
diff --git a/arch/arm64/include/asm/ucontext.h b/arch/arm64/include/asm/ucontext.h
new file mode 100644
index 000000000000..bde960720892
--- /dev/null
+++ b/arch/arm64/include/asm/ucontext.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_UCONTEXT_H
+#define __ASM_UCONTEXT_H
+
+struct ucontext {
+ unsigned long uc_flags;
+ struct ucontext *uc_link;
+ stack_t uc_stack;
+ sigset_t uc_sigmask;
+ /* glibc uses a 1024-bit sigset_t */
+ __u8 __unused[(1024 - sizeof(sigset_t)) / 8];
+ /* last for future expansion */
+ struct sigcontext uc_mcontext;
+};
+
+#endif /* __ASM_UCONTEXT_H */
diff --git a/arch/arm64/include/asm/unistd.h b/arch/arm64/include/asm/unistd.h
new file mode 100644
index 000000000000..fe18a683274f
--- /dev/null
+++ b/arch/arm64/include/asm/unistd.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#if !defined(__ASM_UNISTD_H) || defined(__SYSCALL)
+#define __ASM_UNISTD_H
+
+#ifndef __SYSCALL_COMPAT
+#include <asm-generic/unistd.h>
+#endif
+
+#if defined(__KERNEL__) && defined(CONFIG_COMPAT)
+#include <asm/unistd32.h>
+#endif
+
+#endif /* __ASM_UNISTD_H */
diff --git a/arch/arm64/include/asm/unistd32.h b/arch/arm64/include/asm/unistd32.h
new file mode 100644
index 000000000000..a50405f5ee42
--- /dev/null
+++ b/arch/arm64/include/asm/unistd32.h
@@ -0,0 +1,758 @@
+/*
+ * Based on arch/arm/include/asm/unistd.h
+ *
+ * Copyright (C) 2001-2005 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#if !defined(__ASM_UNISTD32_H) || defined(__SYSCALL)
+#define __ASM_UNISTD32_H
+
+#ifndef __SYSCALL
+#define __SYSCALL(x, y)
+#endif
+
+/*
+ * This file contains the system call numbers.
+ */
+
+#ifdef __SYSCALL_COMPAT
+
+#define __NR_restart_syscall 0
+__SYSCALL(__NR_restart_syscall, sys_restart_syscall)
+#define __NR_exit 1
+__SYSCALL(__NR_exit, sys_exit)
+#define __NR_fork 2
+__SYSCALL(__NR_fork, sys_fork)
+#define __NR_read 3
+__SYSCALL(__NR_read, sys_read)
+#define __NR_write 4
+__SYSCALL(__NR_write, sys_write)
+#define __NR_open 5
+__SYSCALL(__NR_open, sys_open)
+#define __NR_close 6
+__SYSCALL(__NR_close, sys_close)
+__SYSCALL(7, sys_ni_syscall) /* 7 was sys_waitpid */
+#define __NR_creat 8
+__SYSCALL(__NR_creat, sys_creat)
+#define __NR_link 9
+__SYSCALL(__NR_link, sys_link)
+#define __NR_unlink 10
+__SYSCALL(__NR_unlink, sys_unlink)
+#define __NR_execve 11
+__SYSCALL(__NR_execve, sys_execve)
+#define __NR_chdir 12
+__SYSCALL(__NR_chdir, sys_chdir)
+__SYSCALL(13, sys_ni_syscall) /* 13 was sys_time */
+#define __NR_mknod 14
+__SYSCALL(__NR_mknod, sys_mknod)
+#define __NR_chmod 15
+__SYSCALL(__NR_chmod, sys_chmod)
+#define __NR_lchown 16
+__SYSCALL(__NR_lchown, sys_lchown16)
+__SYSCALL(17, sys_ni_syscall) /* 17 was sys_break */
+__SYSCALL(18, sys_ni_syscall) /* 18 was sys_stat */
+#define __NR_lseek 19
+__SYSCALL(__NR_lseek, sys_lseek)
+#define __NR_getpid 20
+__SYSCALL(__NR_getpid, sys_getpid)
+#define __NR_mount 21
+__SYSCALL(__NR_mount, sys_mount)
+__SYSCALL(22, sys_ni_syscall) /* 22 was sys_umount */
+#define __NR_setuid 23
+__SYSCALL(__NR_setuid, sys_setuid16)
+#define __NR_getuid 24
+__SYSCALL(__NR_getuid, sys_getuid16)
+__SYSCALL(25, sys_ni_syscall) /* 25 was sys_stime */
+#define __NR_ptrace 26
+__SYSCALL(__NR_ptrace, sys_ptrace)
+__SYSCALL(27, sys_ni_syscall) /* 27 was sys_alarm */
+__SYSCALL(28, sys_ni_syscall) /* 28 was sys_fstat */
+#define __NR_pause 29
+__SYSCALL(__NR_pause, sys_pause)
+__SYSCALL(30, sys_ni_syscall) /* 30 was sys_utime */
+__SYSCALL(31, sys_ni_syscall) /* 31 was sys_stty */
+__SYSCALL(32, sys_ni_syscall) /* 32 was sys_gtty */
+#define __NR_access 33
+__SYSCALL(__NR_access, sys_access)
+#define __NR_nice 34
+__SYSCALL(__NR_nice, sys_nice)
+__SYSCALL(35, sys_ni_syscall) /* 35 was sys_ftime */
+#define __NR_sync 36
+__SYSCALL(__NR_sync, sys_sync)
+#define __NR_kill 37
+__SYSCALL(__NR_kill, sys_kill)
+#define __NR_rename 38
+__SYSCALL(__NR_rename, sys_rename)
+#define __NR_mkdir 39
+__SYSCALL(__NR_mkdir, sys_mkdir)
+#define __NR_rmdir 40
+__SYSCALL(__NR_rmdir, sys_rmdir)
+#define __NR_dup 41
+__SYSCALL(__NR_dup, sys_dup)
+#define __NR_pipe 42
+__SYSCALL(__NR_pipe, sys_pipe)
+#define __NR_times 43
+__SYSCALL(__NR_times, sys_times)
+__SYSCALL(44, sys_ni_syscall) /* 44 was sys_prof */
+#define __NR_brk 45
+__SYSCALL(__NR_brk, sys_brk)
+#define __NR_setgid 46
+__SYSCALL(__NR_setgid, sys_setgid16)
+#define __NR_getgid 47
+__SYSCALL(__NR_getgid, sys_getgid16)
+__SYSCALL(48, sys_ni_syscall) /* 48 was sys_signal */
+#define __NR_geteuid 49
+__SYSCALL(__NR_geteuid, sys_geteuid16)
+#define __NR_getegid 50
+__SYSCALL(__NR_getegid, sys_getegid16)
+#define __NR_acct 51
+__SYSCALL(__NR_acct, sys_acct)
+#define __NR_umount2 52
+__SYSCALL(__NR_umount2, sys_umount)
+__SYSCALL(53, sys_ni_syscall) /* 53 was sys_lock */
+#define __NR_ioctl 54
+__SYSCALL(__NR_ioctl, sys_ioctl)
+#define __NR_fcntl 55
+__SYSCALL(__NR_fcntl, sys_fcntl)
+__SYSCALL(56, sys_ni_syscall) /* 56 was sys_mpx */
+#define __NR_setpgid 57
+__SYSCALL(__NR_setpgid, sys_setpgid)
+__SYSCALL(58, sys_ni_syscall) /* 58 was sys_ulimit */
+__SYSCALL(59, sys_ni_syscall) /* 59 was sys_olduname */
+#define __NR_umask 60
+__SYSCALL(__NR_umask, sys_umask)
+#define __NR_chroot 61
+__SYSCALL(__NR_chroot, sys_chroot)
+#define __NR_ustat 62
+__SYSCALL(__NR_ustat, sys_ustat)
+#define __NR_dup2 63
+__SYSCALL(__NR_dup2, sys_dup2)
+#define __NR_getppid 64
+__SYSCALL(__NR_getppid, sys_getppid)
+#define __NR_getpgrp 65
+__SYSCALL(__NR_getpgrp, sys_getpgrp)
+#define __NR_setsid 66
+__SYSCALL(__NR_setsid, sys_setsid)
+#define __NR_sigaction 67
+__SYSCALL(__NR_sigaction, sys_sigaction)
+__SYSCALL(68, sys_ni_syscall) /* 68 was sys_sgetmask */
+__SYSCALL(69, sys_ni_syscall) /* 69 was sys_ssetmask */
+#define __NR_setreuid 70
+__SYSCALL(__NR_setreuid, sys_setreuid16)
+#define __NR_setregid 71
+__SYSCALL(__NR_setregid, sys_setregid16)
+#define __NR_sigsuspend 72
+__SYSCALL(__NR_sigsuspend, sys_sigsuspend)
+#define __NR_sigpending 73
+__SYSCALL(__NR_sigpending, sys_sigpending)
+#define __NR_sethostname 74
+__SYSCALL(__NR_sethostname, sys_sethostname)
+#define __NR_setrlimit 75
+__SYSCALL(__NR_setrlimit, sys_setrlimit)
+__SYSCALL(76, sys_ni_syscall) /* 76 was sys_getrlimit */
+#define __NR_getrusage 77
+__SYSCALL(__NR_getrusage, sys_getrusage)
+#define __NR_gettimeofday 78
+__SYSCALL(__NR_gettimeofday, sys_gettimeofday)
+#define __NR_settimeofday 79
+__SYSCALL(__NR_settimeofday, sys_settimeofday)
+#define __NR_getgroups 80
+__SYSCALL(__NR_getgroups, sys_getgroups16)
+#define __NR_setgroups 81
+__SYSCALL(__NR_setgroups, sys_setgroups16)
+__SYSCALL(82, sys_ni_syscall) /* 82 was sys_select */
+#define __NR_symlink 83
+__SYSCALL(__NR_symlink, sys_symlink)
+__SYSCALL(84, sys_ni_syscall) /* 84 was sys_lstat */
+#define __NR_readlink 85
+__SYSCALL(__NR_readlink, sys_readlink)
+#define __NR_uselib 86
+__SYSCALL(__NR_uselib, sys_uselib)
+#define __NR_swapon 87
+__SYSCALL(__NR_swapon, sys_swapon)
+#define __NR_reboot 88
+__SYSCALL(__NR_reboot, sys_reboot)
+__SYSCALL(89, sys_ni_syscall) /* 89 was sys_readdir */
+__SYSCALL(90, sys_ni_syscall) /* 90 was sys_mmap */
+#define __NR_munmap 91
+__SYSCALL(__NR_munmap, sys_munmap)
+#define __NR_truncate 92
+__SYSCALL(__NR_truncate, sys_truncate)
+#define __NR_ftruncate 93
+__SYSCALL(__NR_ftruncate, sys_ftruncate)
+#define __NR_fchmod 94
+__SYSCALL(__NR_fchmod, sys_fchmod)
+#define __NR_fchown 95
+__SYSCALL(__NR_fchown, sys_fchown16)
+#define __NR_getpriority 96
+__SYSCALL(__NR_getpriority, sys_getpriority)
+#define __NR_setpriority 97
+__SYSCALL(__NR_setpriority, sys_setpriority)
+__SYSCALL(98, sys_ni_syscall) /* 98 was sys_profil */
+#define __NR_statfs 99
+__SYSCALL(__NR_statfs, sys_statfs)
+#define __NR_fstatfs 100
+__SYSCALL(__NR_fstatfs, sys_fstatfs)
+__SYSCALL(101, sys_ni_syscall) /* 101 was sys_ioperm */
+__SYSCALL(102, sys_ni_syscall) /* 102 was sys_socketcall */
+#define __NR_syslog 103
+__SYSCALL(__NR_syslog, sys_syslog)
+#define __NR_setitimer 104
+__SYSCALL(__NR_setitimer, sys_setitimer)
+#define __NR_getitimer 105
+__SYSCALL(__NR_getitimer, sys_getitimer)
+#define __NR_stat 106
+__SYSCALL(__NR_stat, sys_newstat)
+#define __NR_lstat 107
+__SYSCALL(__NR_lstat, sys_newlstat)
+#define __NR_fstat 108
+__SYSCALL(__NR_fstat, sys_newfstat)
+__SYSCALL(109, sys_ni_syscall) /* 109 was sys_uname */
+__SYSCALL(110, sys_ni_syscall) /* 110 was sys_iopl */
+#define __NR_vhangup 111
+__SYSCALL(__NR_vhangup, sys_vhangup)
+__SYSCALL(112, sys_ni_syscall) /* 112 was sys_idle */
+__SYSCALL(113, sys_ni_syscall) /* 113 was sys_syscall */
+#define __NR_wait4 114
+__SYSCALL(__NR_wait4, sys_wait4)
+#define __NR_swapoff 115
+__SYSCALL(__NR_swapoff, sys_swapoff)
+#define __NR_sysinfo 116
+__SYSCALL(__NR_sysinfo, sys_sysinfo)
+__SYSCALL(117, sys_ni_syscall) /* 117 was sys_ipc */
+#define __NR_fsync 118
+__SYSCALL(__NR_fsync, sys_fsync)
+#define __NR_sigreturn 119
+__SYSCALL(__NR_sigreturn, sys_sigreturn)
+#define __NR_clone 120
+__SYSCALL(__NR_clone, sys_clone)
+#define __NR_setdomainname 121
+__SYSCALL(__NR_setdomainname, sys_setdomainname)
+#define __NR_uname 122
+__SYSCALL(__NR_uname, sys_newuname)
+__SYSCALL(123, sys_ni_syscall) /* 123 was sys_modify_ldt */
+#define __NR_adjtimex 124
+__SYSCALL(__NR_adjtimex, sys_adjtimex)
+#define __NR_mprotect 125
+__SYSCALL(__NR_mprotect, sys_mprotect)
+#define __NR_sigprocmask 126
+__SYSCALL(__NR_sigprocmask, sys_sigprocmask)
+__SYSCALL(127, sys_ni_syscall) /* 127 was sys_create_module */
+#define __NR_init_module 128
+__SYSCALL(__NR_init_module, sys_init_module)
+#define __NR_delete_module 129
+__SYSCALL(__NR_delete_module, sys_delete_module)
+__SYSCALL(130, sys_ni_syscall) /* 130 was sys_get_kernel_syms */
+#define __NR_quotactl 131
+__SYSCALL(__NR_quotactl, sys_quotactl)
+#define __NR_getpgid 132
+__SYSCALL(__NR_getpgid, sys_getpgid)
+#define __NR_fchdir 133
+__SYSCALL(__NR_fchdir, sys_fchdir)
+#define __NR_bdflush 134
+__SYSCALL(__NR_bdflush, sys_bdflush)
+#define __NR_sysfs 135
+__SYSCALL(__NR_sysfs, sys_sysfs)
+#define __NR_personality 136
+__SYSCALL(__NR_personality, sys_personality)
+__SYSCALL(137, sys_ni_syscall) /* 137 was sys_afs_syscall */
+#define __NR_setfsuid 138
+__SYSCALL(__NR_setfsuid, sys_setfsuid16)
+#define __NR_setfsgid 139
+__SYSCALL(__NR_setfsgid, sys_setfsgid16)
+#define __NR__llseek 140
+__SYSCALL(__NR__llseek, sys_llseek)
+#define __NR_getdents 141
+__SYSCALL(__NR_getdents, sys_getdents)
+#define __NR__newselect 142
+__SYSCALL(__NR__newselect, sys_select)
+#define __NR_flock 143
+__SYSCALL(__NR_flock, sys_flock)
+#define __NR_msync 144
+__SYSCALL(__NR_msync, sys_msync)
+#define __NR_readv 145
+__SYSCALL(__NR_readv, sys_readv)
+#define __NR_writev 146
+__SYSCALL(__NR_writev, sys_writev)
+#define __NR_getsid 147
+__SYSCALL(__NR_getsid, sys_getsid)
+#define __NR_fdatasync 148
+__SYSCALL(__NR_fdatasync, sys_fdatasync)
+#define __NR__sysctl 149
+__SYSCALL(__NR__sysctl, sys_sysctl)
+#define __NR_mlock 150
+__SYSCALL(__NR_mlock, sys_mlock)
+#define __NR_munlock 151
+__SYSCALL(__NR_munlock, sys_munlock)
+#define __NR_mlockall 152
+__SYSCALL(__NR_mlockall, sys_mlockall)
+#define __NR_munlockall 153
+__SYSCALL(__NR_munlockall, sys_munlockall)
+#define __NR_sched_setparam 154
+__SYSCALL(__NR_sched_setparam, sys_sched_setparam)
+#define __NR_sched_getparam 155
+__SYSCALL(__NR_sched_getparam, sys_sched_getparam)
+#define __NR_sched_setscheduler 156
+__SYSCALL(__NR_sched_setscheduler, sys_sched_setscheduler)
+#define __NR_sched_getscheduler 157
+__SYSCALL(__NR_sched_getscheduler, sys_sched_getscheduler)
+#define __NR_sched_yield 158
+__SYSCALL(__NR_sched_yield, sys_sched_yield)
+#define __NR_sched_get_priority_max 159
+__SYSCALL(__NR_sched_get_priority_max, sys_sched_get_priority_max)
+#define __NR_sched_get_priority_min 160
+__SYSCALL(__NR_sched_get_priority_min, sys_sched_get_priority_min)
+#define __NR_sched_rr_get_interval 161
+__SYSCALL(__NR_sched_rr_get_interval, sys_sched_rr_get_interval)
+#define __NR_nanosleep 162
+__SYSCALL(__NR_nanosleep, sys_nanosleep)
+#define __NR_mremap 163
+__SYSCALL(__NR_mremap, sys_mremap)
+#define __NR_setresuid 164
+__SYSCALL(__NR_setresuid, sys_setresuid16)
+#define __NR_getresuid 165
+__SYSCALL(__NR_getresuid, sys_getresuid16)
+__SYSCALL(166, sys_ni_syscall) /* 166 was sys_vm86 */
+__SYSCALL(167, sys_ni_syscall) /* 167 was sys_query_module */
+#define __NR_poll 168
+__SYSCALL(__NR_poll, sys_poll)
+#define __NR_nfsservctl 169
+__SYSCALL(__NR_nfsservctl, sys_ni_syscall)
+#define __NR_setresgid 170
+__SYSCALL(__NR_setresgid, sys_setresgid16)
+#define __NR_getresgid 171
+__SYSCALL(__NR_getresgid, sys_getresgid16)
+#define __NR_prctl 172
+__SYSCALL(__NR_prctl, sys_prctl)
+#define __NR_rt_sigreturn 173
+__SYSCALL(__NR_rt_sigreturn, sys_rt_sigreturn)
+#define __NR_rt_sigaction 174
+__SYSCALL(__NR_rt_sigaction, sys_rt_sigaction)
+#define __NR_rt_sigprocmask 175
+__SYSCALL(__NR_rt_sigprocmask, sys_rt_sigprocmask)
+#define __NR_rt_sigpending 176
+__SYSCALL(__NR_rt_sigpending, sys_rt_sigpending)
+#define __NR_rt_sigtimedwait 177
+__SYSCALL(__NR_rt_sigtimedwait, sys_rt_sigtimedwait)
+#define __NR_rt_sigqueueinfo 178
+__SYSCALL(__NR_rt_sigqueueinfo, sys_rt_sigqueueinfo)
+#define __NR_rt_sigsuspend 179
+__SYSCALL(__NR_rt_sigsuspend, sys_rt_sigsuspend)
+#define __NR_pread64 180
+__SYSCALL(__NR_pread64, sys_pread64)
+#define __NR_pwrite64 181
+__SYSCALL(__NR_pwrite64, sys_pwrite64)
+#define __NR_chown 182
+__SYSCALL(__NR_chown, sys_chown16)
+#define __NR_getcwd 183
+__SYSCALL(__NR_getcwd, sys_getcwd)
+#define __NR_capget 184
+__SYSCALL(__NR_capget, sys_capget)
+#define __NR_capset 185
+__SYSCALL(__NR_capset, sys_capset)
+#define __NR_sigaltstack 186
+__SYSCALL(__NR_sigaltstack, sys_sigaltstack)
+#define __NR_sendfile 187
+__SYSCALL(__NR_sendfile, sys_sendfile)
+__SYSCALL(188, sys_ni_syscall) /* 188 reserved */
+__SYSCALL(189, sys_ni_syscall) /* 189 reserved */
+#define __NR_vfork 190
+__SYSCALL(__NR_vfork, sys_vfork)
+#define __NR_ugetrlimit 191 /* SuS compliant getrlimit */
+__SYSCALL(__NR_ugetrlimit, sys_getrlimit)
+#define __NR_mmap2 192
+__SYSCALL(__NR_mmap2, sys_mmap2)
+#define __NR_truncate64 193
+__SYSCALL(__NR_truncate64, sys_truncate64)
+#define __NR_ftruncate64 194
+__SYSCALL(__NR_ftruncate64, sys_ftruncate64)
+#define __NR_stat64 195
+__SYSCALL(__NR_stat64, sys_stat64)
+#define __NR_lstat64 196
+__SYSCALL(__NR_lstat64, sys_lstat64)
+#define __NR_fstat64 197
+__SYSCALL(__NR_fstat64, sys_fstat64)
+#define __NR_lchown32 198
+__SYSCALL(__NR_lchown32, sys_lchown)
+#define __NR_getuid32 199
+__SYSCALL(__NR_getuid32, sys_getuid)
+#define __NR_getgid32 200
+__SYSCALL(__NR_getgid32, sys_getgid)
+#define __NR_geteuid32 201
+__SYSCALL(__NR_geteuid32, sys_geteuid)
+#define __NR_getegid32 202
+__SYSCALL(__NR_getegid32, sys_getegid)
+#define __NR_setreuid32 203
+__SYSCALL(__NR_setreuid32, sys_setreuid)
+#define __NR_setregid32 204
+__SYSCALL(__NR_setregid32, sys_setregid)
+#define __NR_getgroups32 205
+__SYSCALL(__NR_getgroups32, sys_getgroups)
+#define __NR_setgroups32 206
+__SYSCALL(__NR_setgroups32, sys_setgroups)
+#define __NR_fchown32 207
+__SYSCALL(__NR_fchown32, sys_fchown)
+#define __NR_setresuid32 208
+__SYSCALL(__NR_setresuid32, sys_setresuid)
+#define __NR_getresuid32 209
+__SYSCALL(__NR_getresuid32, sys_getresuid)
+#define __NR_setresgid32 210
+__SYSCALL(__NR_setresgid32, sys_setresgid)
+#define __NR_getresgid32 211
+__SYSCALL(__NR_getresgid32, sys_getresgid)
+#define __NR_chown32 212
+__SYSCALL(__NR_chown32, sys_chown)
+#define __NR_setuid32 213
+__SYSCALL(__NR_setuid32, sys_setuid)
+#define __NR_setgid32 214
+__SYSCALL(__NR_setgid32, sys_setgid)
+#define __NR_setfsuid32 215
+__SYSCALL(__NR_setfsuid32, sys_setfsuid)
+#define __NR_setfsgid32 216
+__SYSCALL(__NR_setfsgid32, sys_setfsgid)
+#define __NR_getdents64 217
+__SYSCALL(__NR_getdents64, sys_getdents64)
+#define __NR_pivot_root 218
+__SYSCALL(__NR_pivot_root, sys_pivot_root)
+#define __NR_mincore 219
+__SYSCALL(__NR_mincore, sys_mincore)
+#define __NR_madvise 220
+__SYSCALL(__NR_madvise, sys_madvise)
+#define __NR_fcntl64 221
+__SYSCALL(__NR_fcntl64, sys_fcntl64)
+__SYSCALL(222, sys_ni_syscall) /* 222 for tux */
+__SYSCALL(223, sys_ni_syscall) /* 223 is unused */
+#define __NR_gettid 224
+__SYSCALL(__NR_gettid, sys_gettid)
+#define __NR_readahead 225
+__SYSCALL(__NR_readahead, sys_readahead)
+#define __NR_setxattr 226
+__SYSCALL(__NR_setxattr, sys_setxattr)
+#define __NR_lsetxattr 227
+__SYSCALL(__NR_lsetxattr, sys_lsetxattr)
+#define __NR_fsetxattr 228
+__SYSCALL(__NR_fsetxattr, sys_fsetxattr)
+#define __NR_getxattr 229
+__SYSCALL(__NR_getxattr, sys_getxattr)
+#define __NR_lgetxattr 230
+__SYSCALL(__NR_lgetxattr, sys_lgetxattr)
+#define __NR_fgetxattr 231
+__SYSCALL(__NR_fgetxattr, sys_fgetxattr)
+#define __NR_listxattr 232
+__SYSCALL(__NR_listxattr, sys_listxattr)
+#define __NR_llistxattr 233
+__SYSCALL(__NR_llistxattr, sys_llistxattr)
+#define __NR_flistxattr 234
+__SYSCALL(__NR_flistxattr, sys_flistxattr)
+#define __NR_removexattr 235
+__SYSCALL(__NR_removexattr, sys_removexattr)
+#define __NR_lremovexattr 236
+__SYSCALL(__NR_lremovexattr, sys_lremovexattr)
+#define __NR_fremovexattr 237
+__SYSCALL(__NR_fremovexattr, sys_fremovexattr)
+#define __NR_tkill 238
+__SYSCALL(__NR_tkill, sys_tkill)
+#define __NR_sendfile64 239
+__SYSCALL(__NR_sendfile64, sys_sendfile64)
+#define __NR_futex 240
+__SYSCALL(__NR_futex, sys_futex)
+#define __NR_sched_setaffinity 241
+__SYSCALL(__NR_sched_setaffinity, sys_sched_setaffinity)
+#define __NR_sched_getaffinity 242
+__SYSCALL(__NR_sched_getaffinity, sys_sched_getaffinity)
+#define __NR_io_setup 243
+__SYSCALL(__NR_io_setup, sys_io_setup)
+#define __NR_io_destroy 244
+__SYSCALL(__NR_io_destroy, sys_io_destroy)
+#define __NR_io_getevents 245
+__SYSCALL(__NR_io_getevents, sys_io_getevents)
+#define __NR_io_submit 246
+__SYSCALL(__NR_io_submit, sys_io_submit)
+#define __NR_io_cancel 247
+__SYSCALL(__NR_io_cancel, sys_io_cancel)
+#define __NR_exit_group 248
+__SYSCALL(__NR_exit_group, sys_exit_group)
+#define __NR_lookup_dcookie 249
+__SYSCALL(__NR_lookup_dcookie, sys_lookup_dcookie)
+#define __NR_epoll_create 250
+__SYSCALL(__NR_epoll_create, sys_epoll_create)
+#define __NR_epoll_ctl 251
+__SYSCALL(__NR_epoll_ctl, sys_epoll_ctl)
+#define __NR_epoll_wait 252
+__SYSCALL(__NR_epoll_wait, sys_epoll_wait)
+#define __NR_remap_file_pages 253
+__SYSCALL(__NR_remap_file_pages, sys_remap_file_pages)
+__SYSCALL(254, sys_ni_syscall) /* 254 for set_thread_area */
+__SYSCALL(255, sys_ni_syscall) /* 255 for get_thread_area */
+#define __NR_set_tid_address 256
+__SYSCALL(__NR_set_tid_address, sys_set_tid_address)
+#define __NR_timer_create 257
+__SYSCALL(__NR_timer_create, sys_timer_create)
+#define __NR_timer_settime 258
+__SYSCALL(__NR_timer_settime, sys_timer_settime)
+#define __NR_timer_gettime 259
+__SYSCALL(__NR_timer_gettime, sys_timer_gettime)
+#define __NR_timer_getoverrun 260
+__SYSCALL(__NR_timer_getoverrun, sys_timer_getoverrun)
+#define __NR_timer_delete 261
+__SYSCALL(__NR_timer_delete, sys_timer_delete)
+#define __NR_clock_settime 262
+__SYSCALL(__NR_clock_settime, sys_clock_settime)
+#define __NR_clock_gettime 263
+__SYSCALL(__NR_clock_gettime, sys_clock_gettime)
+#define __NR_clock_getres 264
+__SYSCALL(__NR_clock_getres, sys_clock_getres)
+#define __NR_clock_nanosleep 265
+__SYSCALL(__NR_clock_nanosleep, sys_clock_nanosleep)
+#define __NR_statfs64 266
+__SYSCALL(__NR_statfs64, sys_statfs64)
+#define __NR_fstatfs64 267
+__SYSCALL(__NR_fstatfs64, sys_fstatfs64)
+#define __NR_tgkill 268
+__SYSCALL(__NR_tgkill, sys_tgkill)
+#define __NR_utimes 269
+__SYSCALL(__NR_utimes, sys_utimes)
+#define __NR_fadvise64 270
+__SYSCALL(__NR_fadvise64, sys_fadvise64_64)
+#define __NR_pciconfig_iobase 271
+__SYSCALL(__NR_pciconfig_iobase, sys_pciconfig_iobase)
+#define __NR_pciconfig_read 272
+__SYSCALL(__NR_pciconfig_read, sys_pciconfig_read)
+#define __NR_pciconfig_write 273
+__SYSCALL(__NR_pciconfig_write, sys_pciconfig_write)
+#define __NR_mq_open 274
+__SYSCALL(__NR_mq_open, sys_mq_open)
+#define __NR_mq_unlink 275
+__SYSCALL(__NR_mq_unlink, sys_mq_unlink)
+#define __NR_mq_timedsend 276
+__SYSCALL(__NR_mq_timedsend, sys_mq_timedsend)
+#define __NR_mq_timedreceive 277
+__SYSCALL(__NR_mq_timedreceive, sys_mq_timedreceive)
+#define __NR_mq_notify 278
+__SYSCALL(__NR_mq_notify, sys_mq_notify)
+#define __NR_mq_getsetattr 279
+__SYSCALL(__NR_mq_getsetattr, sys_mq_getsetattr)
+#define __NR_waitid 280
+__SYSCALL(__NR_waitid, sys_waitid)
+#define __NR_socket 281
+__SYSCALL(__NR_socket, sys_socket)
+#define __NR_bind 282
+__SYSCALL(__NR_bind, sys_bind)
+#define __NR_connect 283
+__SYSCALL(__NR_connect, sys_connect)
+#define __NR_listen 284
+__SYSCALL(__NR_listen, sys_listen)
+#define __NR_accept 285
+__SYSCALL(__NR_accept, sys_accept)
+#define __NR_getsockname 286
+__SYSCALL(__NR_getsockname, sys_getsockname)
+#define __NR_getpeername 287
+__SYSCALL(__NR_getpeername, sys_getpeername)
+#define __NR_socketpair 288
+__SYSCALL(__NR_socketpair, sys_socketpair)
+#define __NR_send 289
+__SYSCALL(__NR_send, sys_send)
+#define __NR_sendto 290
+__SYSCALL(__NR_sendto, sys_sendto)
+#define __NR_recv 291
+__SYSCALL(__NR_recv, sys_recv)
+#define __NR_recvfrom 292
+__SYSCALL(__NR_recvfrom, sys_recvfrom)
+#define __NR_shutdown 293
+__SYSCALL(__NR_shutdown, sys_shutdown)
+#define __NR_setsockopt 294
+__SYSCALL(__NR_setsockopt, sys_setsockopt)
+#define __NR_getsockopt 295
+__SYSCALL(__NR_getsockopt, sys_getsockopt)
+#define __NR_sendmsg 296
+__SYSCALL(__NR_sendmsg, sys_sendmsg)
+#define __NR_recvmsg 297
+__SYSCALL(__NR_recvmsg, sys_recvmsg)
+#define __NR_semop 298
+__SYSCALL(__NR_semop, sys_semop)
+#define __NR_semget 299
+__SYSCALL(__NR_semget, sys_semget)
+#define __NR_semctl 300
+__SYSCALL(__NR_semctl, sys_semctl)
+#define __NR_msgsnd 301
+__SYSCALL(__NR_msgsnd, sys_msgsnd)
+#define __NR_msgrcv 302
+__SYSCALL(__NR_msgrcv, sys_msgrcv)
+#define __NR_msgget 303
+__SYSCALL(__NR_msgget, sys_msgget)
+#define __NR_msgctl 304
+__SYSCALL(__NR_msgctl, sys_msgctl)
+#define __NR_shmat 305
+__SYSCALL(__NR_shmat, sys_shmat)
+#define __NR_shmdt 306
+__SYSCALL(__NR_shmdt, sys_shmdt)
+#define __NR_shmget 307
+__SYSCALL(__NR_shmget, sys_shmget)
+#define __NR_shmctl 308
+__SYSCALL(__NR_shmctl, sys_shmctl)
+#define __NR_add_key 309
+__SYSCALL(__NR_add_key, sys_add_key)
+#define __NR_request_key 310
+__SYSCALL(__NR_request_key, sys_request_key)
+#define __NR_keyctl 311
+__SYSCALL(__NR_keyctl, sys_keyctl)
+#define __NR_semtimedop 312
+__SYSCALL(__NR_semtimedop, sys_semtimedop)
+#define __NR_vserver 313
+__SYSCALL(__NR_vserver, sys_ni_syscall)
+#define __NR_ioprio_set 314
+__SYSCALL(__NR_ioprio_set, sys_ioprio_set)
+#define __NR_ioprio_get 315
+__SYSCALL(__NR_ioprio_get, sys_ioprio_get)
+#define __NR_inotify_init 316
+__SYSCALL(__NR_inotify_init, sys_inotify_init)
+#define __NR_inotify_add_watch 317
+__SYSCALL(__NR_inotify_add_watch, sys_inotify_add_watch)
+#define __NR_inotify_rm_watch 318
+__SYSCALL(__NR_inotify_rm_watch, sys_inotify_rm_watch)
+#define __NR_mbind 319
+__SYSCALL(__NR_mbind, sys_mbind)
+#define __NR_get_mempolicy 320
+__SYSCALL(__NR_get_mempolicy, sys_get_mempolicy)
+#define __NR_set_mempolicy 321
+__SYSCALL(__NR_set_mempolicy, sys_set_mempolicy)
+#define __NR_openat 322
+__SYSCALL(__NR_openat, sys_openat)
+#define __NR_mkdirat 323
+__SYSCALL(__NR_mkdirat, sys_mkdirat)
+#define __NR_mknodat 324
+__SYSCALL(__NR_mknodat, sys_mknodat)
+#define __NR_fchownat 325
+__SYSCALL(__NR_fchownat, sys_fchownat)
+#define __NR_futimesat 326
+__SYSCALL(__NR_futimesat, sys_futimesat)
+#define __NR_fstatat64 327
+__SYSCALL(__NR_fstatat64, sys_fstatat64)
+#define __NR_unlinkat 328
+__SYSCALL(__NR_unlinkat, sys_unlinkat)
+#define __NR_renameat 329
+__SYSCALL(__NR_renameat, sys_renameat)
+#define __NR_linkat 330
+__SYSCALL(__NR_linkat, sys_linkat)
+#define __NR_symlinkat 331
+__SYSCALL(__NR_symlinkat, sys_symlinkat)
+#define __NR_readlinkat 332
+__SYSCALL(__NR_readlinkat, sys_readlinkat)
+#define __NR_fchmodat 333
+__SYSCALL(__NR_fchmodat, sys_fchmodat)
+#define __NR_faccessat 334
+__SYSCALL(__NR_faccessat, sys_faccessat)
+#define __NR_pselect6 335
+__SYSCALL(__NR_pselect6, sys_pselect6)
+#define __NR_ppoll 336
+__SYSCALL(__NR_ppoll, sys_ppoll)
+#define __NR_unshare 337
+__SYSCALL(__NR_unshare, sys_unshare)
+#define __NR_set_robust_list 338
+__SYSCALL(__NR_set_robust_list, sys_set_robust_list)
+#define __NR_get_robust_list 339
+__SYSCALL(__NR_get_robust_list, sys_get_robust_list)
+#define __NR_splice 340
+__SYSCALL(__NR_splice, sys_splice)
+#define __NR_sync_file_range2 341
+__SYSCALL(__NR_sync_file_range2, sys_sync_file_range2)
+#define __NR_tee 342
+__SYSCALL(__NR_tee, sys_tee)
+#define __NR_vmsplice 343
+__SYSCALL(__NR_vmsplice, sys_vmsplice)
+#define __NR_move_pages 344
+__SYSCALL(__NR_move_pages, sys_move_pages)
+#define __NR_getcpu 345
+__SYSCALL(__NR_getcpu, sys_getcpu)
+#define __NR_epoll_pwait 346
+__SYSCALL(__NR_epoll_pwait, sys_epoll_pwait)
+#define __NR_kexec_load 347
+__SYSCALL(__NR_kexec_load, sys_kexec_load)
+#define __NR_utimensat 348
+__SYSCALL(__NR_utimensat, sys_utimensat)
+#define __NR_signalfd 349
+__SYSCALL(__NR_signalfd, sys_signalfd)
+#define __NR_timerfd_create 350
+__SYSCALL(__NR_timerfd_create, sys_timerfd_create)
+#define __NR_eventfd 351
+__SYSCALL(__NR_eventfd, sys_eventfd)
+#define __NR_fallocate 352
+__SYSCALL(__NR_fallocate, sys_fallocate)
+#define __NR_timerfd_settime 353
+__SYSCALL(__NR_timerfd_settime, sys_timerfd_settime)
+#define __NR_timerfd_gettime 354
+__SYSCALL(__NR_timerfd_gettime, sys_timerfd_gettime)
+#define __NR_signalfd4 355
+__SYSCALL(__NR_signalfd4, sys_signalfd4)
+#define __NR_eventfd2 356
+__SYSCALL(__NR_eventfd2, sys_eventfd2)
+#define __NR_epoll_create1 357
+__SYSCALL(__NR_epoll_create1, sys_epoll_create1)
+#define __NR_dup3 358
+__SYSCALL(__NR_dup3, sys_dup3)
+#define __NR_pipe2 359
+__SYSCALL(__NR_pipe2, sys_pipe2)
+#define __NR_inotify_init1 360
+__SYSCALL(__NR_inotify_init1, sys_inotify_init1)
+#define __NR_preadv 361
+__SYSCALL(__NR_preadv, sys_preadv)
+#define __NR_pwritev 362
+__SYSCALL(__NR_pwritev, sys_pwritev)
+#define __NR_rt_tgsigqueueinfo 363
+__SYSCALL(__NR_rt_tgsigqueueinfo, sys_rt_tgsigqueueinfo)
+#define __NR_perf_event_open 364
+__SYSCALL(__NR_perf_event_open, sys_perf_event_open)
+#define __NR_recvmmsg 365
+__SYSCALL(__NR_recvmmsg, sys_recvmmsg)
+#define __NR_accept4 366
+__SYSCALL(__NR_accept4, sys_accept4)
+#define __NR_fanotify_init 367
+__SYSCALL(__NR_fanotify_init, sys_fanotify_init)
+#define __NR_fanotify_mark 368
+__SYSCALL(__NR_fanotify_mark, sys_fanotify_mark)
+#define __NR_prlimit64 369
+__SYSCALL(__NR_prlimit64, sys_prlimit64)
+#define __NR_name_to_handle_at 370
+__SYSCALL(__NR_name_to_handle_at, sys_name_to_handle_at)
+#define __NR_open_by_handle_at 371
+__SYSCALL(__NR_open_by_handle_at, sys_open_by_handle_at)
+#define __NR_clock_adjtime 372
+__SYSCALL(__NR_clock_adjtime, sys_clock_adjtime)
+#define __NR_syncfs 373
+__SYSCALL(__NR_syncfs, sys_syncfs)
+
+/*
+ * The following SVCs are ARM private.
+ */
+#define __ARM_NR_COMPAT_BASE 0x0f0000
+#define __ARM_NR_compat_cacheflush (__ARM_NR_COMPAT_BASE+2)
+#define __ARM_NR_compat_set_tls (__ARM_NR_COMPAT_BASE+5)
+
+#endif /* __SYSCALL_COMPAT */
+
+#define __NR_compat_syscalls 374
+
+#define __ARCH_WANT_COMPAT_IPC_PARSE_VERSION
+#define __ARCH_WANT_COMPAT_STAT64
+#define __ARCH_WANT_SYS_GETHOSTNAME
+#define __ARCH_WANT_SYS_PAUSE
+#define __ARCH_WANT_SYS_GETPGRP
+#define __ARCH_WANT_SYS_LLSEEK
+#define __ARCH_WANT_SYS_NICE
+#define __ARCH_WANT_SYS_SIGPENDING
+#define __ARCH_WANT_SYS_SIGPROCMASK
+#define __ARCH_WANT_COMPAT_SYS_RT_SIGSUSPEND
+
+#endif /* __ASM_UNISTD32_H */
diff --git a/arch/arm64/include/asm/vdso.h b/arch/arm64/include/asm/vdso.h
new file mode 100644
index 000000000000..839ce0031bd5
--- /dev/null
+++ b/arch/arm64/include/asm/vdso.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_VDSO_H
+#define __ASM_VDSO_H
+
+#ifdef __KERNEL__
+
+/*
+ * Default link address for the vDSO.
+ * Since we randomise the VDSO mapping, there's little point in trying
+ * to prelink this.
+ */
+#define VDSO_LBASE 0x0
+
+#ifndef __ASSEMBLY__
+
+#include <generated/vdso-offsets.h>
+
+#define VDSO_SYMBOL(base, name) \
+({ \
+ (void *)(vdso_offset_##name - VDSO_LBASE + (unsigned long)(base)); \
+})
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_VDSO_H */
diff --git a/arch/arm64/include/asm/vdso_datapage.h b/arch/arm64/include/asm/vdso_datapage.h
new file mode 100644
index 000000000000..de66199673d7
--- /dev/null
+++ b/arch/arm64/include/asm/vdso_datapage.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __ASM_VDSO_DATAPAGE_H
+#define __ASM_VDSO_DATAPAGE_H
+
+#ifdef __KERNEL__
+
+#ifndef __ASSEMBLY__
+
+struct vdso_data {
+ __u64 cs_cycle_last; /* Timebase at clocksource init */
+ __u64 xtime_clock_sec; /* Kernel time */
+ __u64 xtime_clock_nsec;
+ __u64 xtime_coarse_sec; /* Coarse time */
+ __u64 xtime_coarse_nsec;
+ __u64 wtm_clock_sec; /* Wall to monotonic time */
+ __u64 wtm_clock_nsec;
+ __u32 tb_seq_count; /* Timebase sequence counter */
+ __u32 cs_mult; /* Clocksource multiplier */
+ __u32 cs_shift; /* Clocksource shift */
+ __u32 tz_minuteswest; /* Whacky timezone stuff */
+ __u32 tz_dsttime;
+ __u32 use_syscall;
+};
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __KERNEL__ */
+
+#endif /* __ASM_VDSO_DATAPAGE_H */
diff --git a/arch/arm64/kernel/.gitignore b/arch/arm64/kernel/.gitignore
new file mode 100644
index 000000000000..c5f676c3c224
--- /dev/null
+++ b/arch/arm64/kernel/.gitignore
@@ -0,0 +1 @@
+vmlinux.lds
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
new file mode 100644
index 000000000000..e2caff1b812a
--- /dev/null
+++ b/arch/arm64/kernel/Makefile
@@ -0,0 +1,27 @@
+#
+# Makefile for the linux kernel.
+#
+
+CPPFLAGS_vmlinux.lds := -DTEXT_OFFSET=$(TEXT_OFFSET)
+AFLAGS_head.o := -DTEXT_OFFSET=$(TEXT_OFFSET)
+
+# Object file lists.
+arm64-obj-y := cputable.o debug-monitors.o entry.o irq.o fpsimd.o \
+ entry-fpsimd.o process.o ptrace.o setup.o signal.o \
+ sys.o stacktrace.o time.o traps.o io.o vdso.o
+
+arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
+ sys_compat.o
+arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
+arm64-obj-$(CONFIG_SMP) += smp.o
+arm64-obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o
+arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)+= hw_breakpoint.o
+
+obj-y += $(arm64-obj-y) vdso/
+obj-m += $(arm64-obj-m)
+head-y := head.o
+extra-y := $(head-y) vmlinux.lds
+
+# vDSO - this must be built first to generate the symbol offsets
+$(call objectify,$(arm64-obj-y)): $(obj)/vdso/vdso-offsets.h
+$(obj)/vdso/vdso-offsets.h: $(obj)/vdso
diff --git a/arch/arm64/kernel/arm64ksyms.c b/arch/arm64/kernel/arm64ksyms.c
new file mode 100644
index 000000000000..cef3925eaf60
--- /dev/null
+++ b/arch/arm64/kernel/arm64ksyms.c
@@ -0,0 +1,46 @@
+/*
+ * Based on arch/arm/kernel/armksyms.c
+ *
+ * Copyright (C) 2000 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/cryptohash.h>
+#include <linux/delay.h>
+#include <linux/in6.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <asm/checksum.h>
+
+ /* user mem (segment) */
+EXPORT_SYMBOL(__strnlen_user);
+EXPORT_SYMBOL(__strncpy_from_user);
+
+EXPORT_SYMBOL(copy_page);
+
+EXPORT_SYMBOL(__copy_from_user);
+EXPORT_SYMBOL(__copy_to_user);
+EXPORT_SYMBOL(__clear_user);
+
+ /* bitops */
+EXPORT_SYMBOL(__atomic_hash);
+
+ /* physical memory */
+EXPORT_SYMBOL(memstart_addr);
diff --git a/arch/arm64/kernel/asm-offsets.c b/arch/arm64/kernel/asm-offsets.c
new file mode 100644
index 000000000000..a2a4d810bea3
--- /dev/null
+++ b/arch/arm64/kernel/asm-offsets.c
@@ -0,0 +1,108 @@
+/*
+ * Based on arch/arm/kernel/asm-offsets.c
+ *
+ * Copyright (C) 1995-2003 Russell King
+ * 2001-2002 Keith Owens
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/dma-mapping.h>
+#include <asm/thread_info.h>
+#include <asm/memory.h>
+#include <asm/cputable.h>
+#include <asm/vdso_datapage.h>
+#include <linux/kbuild.h>
+
+int main(void)
+{
+ DEFINE(TSK_ACTIVE_MM, offsetof(struct task_struct, active_mm));
+ BLANK();
+ DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
+ DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
+ DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit));
+ DEFINE(TI_TASK, offsetof(struct thread_info, task));
+ DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain));
+ DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
+ BLANK();
+ DEFINE(THREAD_CPU_CONTEXT, offsetof(struct task_struct, thread.cpu_context));
+ BLANK();
+ DEFINE(S_X0, offsetof(struct pt_regs, regs[0]));
+ DEFINE(S_X1, offsetof(struct pt_regs, regs[1]));
+ DEFINE(S_X2, offsetof(struct pt_regs, regs[2]));
+ DEFINE(S_X3, offsetof(struct pt_regs, regs[3]));
+ DEFINE(S_X4, offsetof(struct pt_regs, regs[4]));
+ DEFINE(S_X5, offsetof(struct pt_regs, regs[5]));
+ DEFINE(S_X6, offsetof(struct pt_regs, regs[6]));
+ DEFINE(S_X7, offsetof(struct pt_regs, regs[7]));
+ DEFINE(S_LR, offsetof(struct pt_regs, regs[30]));
+ DEFINE(S_SP, offsetof(struct pt_regs, sp));
+#ifdef CONFIG_COMPAT
+ DEFINE(S_COMPAT_SP, offsetof(struct pt_regs, compat_sp));
+#endif
+ DEFINE(S_PSTATE, offsetof(struct pt_regs, pstate));
+ DEFINE(S_PC, offsetof(struct pt_regs, pc));
+ DEFINE(S_ORIG_X0, offsetof(struct pt_regs, orig_x0));
+ DEFINE(S_SYSCALLNO, offsetof(struct pt_regs, syscallno));
+ DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
+ BLANK();
+ DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id));
+ BLANK();
+ DEFINE(VMA_VM_MM, offsetof(struct vm_area_struct, vm_mm));
+ DEFINE(VMA_VM_FLAGS, offsetof(struct vm_area_struct, vm_flags));
+ BLANK();
+ DEFINE(VM_EXEC, VM_EXEC);
+ BLANK();
+ DEFINE(PAGE_SZ, PAGE_SIZE);
+ BLANK();
+ DEFINE(CPU_INFO_SZ, sizeof(struct cpu_info));
+ DEFINE(CPU_INFO_SETUP, offsetof(struct cpu_info, cpu_setup));
+ BLANK();
+ DEFINE(DMA_BIDIRECTIONAL, DMA_BIDIRECTIONAL);
+ DEFINE(DMA_TO_DEVICE, DMA_TO_DEVICE);
+ DEFINE(DMA_FROM_DEVICE, DMA_FROM_DEVICE);
+ BLANK();
+ DEFINE(CLOCK_REALTIME, CLOCK_REALTIME);
+ DEFINE(CLOCK_MONOTONIC, CLOCK_MONOTONIC);
+ DEFINE(CLOCK_REALTIME_RES, MONOTONIC_RES_NSEC);
+ DEFINE(CLOCK_REALTIME_COARSE, CLOCK_REALTIME_COARSE);
+ DEFINE(CLOCK_MONOTONIC_COARSE,CLOCK_MONOTONIC_COARSE);
+ DEFINE(CLOCK_COARSE_RES, LOW_RES_NSEC);
+ DEFINE(NSEC_PER_SEC, NSEC_PER_SEC);
+ BLANK();
+ DEFINE(VDSO_CS_CYCLE_LAST, offsetof(struct vdso_data, cs_cycle_last));
+ DEFINE(VDSO_XTIME_CLK_SEC, offsetof(struct vdso_data, xtime_clock_sec));
+ DEFINE(VDSO_XTIME_CLK_NSEC, offsetof(struct vdso_data, xtime_clock_nsec));
+ DEFINE(VDSO_XTIME_CRS_SEC, offsetof(struct vdso_data, xtime_coarse_sec));
+ DEFINE(VDSO_XTIME_CRS_NSEC, offsetof(struct vdso_data, xtime_coarse_nsec));
+ DEFINE(VDSO_WTM_CLK_SEC, offsetof(struct vdso_data, wtm_clock_sec));
+ DEFINE(VDSO_WTM_CLK_NSEC, offsetof(struct vdso_data, wtm_clock_nsec));
+ DEFINE(VDSO_TB_SEQ_COUNT, offsetof(struct vdso_data, tb_seq_count));
+ DEFINE(VDSO_CS_MULT, offsetof(struct vdso_data, cs_mult));
+ DEFINE(VDSO_CS_SHIFT, offsetof(struct vdso_data, cs_shift));
+ DEFINE(VDSO_TZ_MINWEST, offsetof(struct vdso_data, tz_minuteswest));
+ DEFINE(VDSO_TZ_DSTTIME, offsetof(struct vdso_data, tz_dsttime));
+ DEFINE(VDSO_USE_SYSCALL, offsetof(struct vdso_data, use_syscall));
+ BLANK();
+ DEFINE(TVAL_TV_SEC, offsetof(struct timeval, tv_sec));
+ DEFINE(TVAL_TV_USEC, offsetof(struct timeval, tv_usec));
+ DEFINE(TSPEC_TV_SEC, offsetof(struct timespec, tv_sec));
+ DEFINE(TSPEC_TV_NSEC, offsetof(struct timespec, tv_nsec));
+ BLANK();
+ DEFINE(TZ_MINWEST, offsetof(struct timezone, tz_minuteswest));
+ DEFINE(TZ_DSTTIME, offsetof(struct timezone, tz_dsttime));
+ return 0;
+}
diff --git a/arch/arm64/kernel/cputable.c b/arch/arm64/kernel/cputable.c
new file mode 100644
index 000000000000..63cfc4a43f4e
--- /dev/null
+++ b/arch/arm64/kernel/cputable.c
@@ -0,0 +1,33 @@
+/*
+ * arch/arm64/kernel/cputable.c
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+
+#include <asm/cputable.h>
+
+extern unsigned long __cpu_setup(void);
+
+struct cpu_info __initdata cpu_table[] = {
+ {
+ .cpu_id_val = 0x000f0000,
+ .cpu_id_mask = 0x000f0000,
+ .cpu_name = "AArch64 Processor",
+ .cpu_setup = __cpu_setup,
+ },
+ { /* Empty */ },
+};
diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/debug-monitors.c
new file mode 100644
index 000000000000..0c3ba9f51376
--- /dev/null
+++ b/arch/arm64/kernel/debug-monitors.c
@@ -0,0 +1,288 @@
+/*
+ * ARMv8 single-step debug support and mdscr context switching.
+ *
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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: Will Deacon <will.deacon@arm.com>
+ */
+
+#include <linux/cpu.h>
+#include <linux/debugfs.h>
+#include <linux/hardirq.h>
+#include <linux/init.h>
+#include <linux/ptrace.h>
+#include <linux/stat.h>
+
+#include <asm/debug-monitors.h>
+#include <asm/local.h>
+#include <asm/cputype.h>
+#include <asm/system_misc.h>
+
+/* Low-level stepping controls. */
+#define DBG_MDSCR_SS (1 << 0)
+#define DBG_SPSR_SS (1 << 21)
+
+/* MDSCR_EL1 enabling bits */
+#define DBG_MDSCR_KDE (1 << 13)
+#define DBG_MDSCR_MDE (1 << 15)
+#define DBG_MDSCR_MASK ~(DBG_MDSCR_KDE | DBG_MDSCR_MDE)
+
+/* Determine debug architecture. */
+u8 debug_monitors_arch(void)
+{
+ return read_cpuid(ID_AA64DFR0_EL1) & 0xf;
+}
+
+/*
+ * MDSCR access routines.
+ */
+static void mdscr_write(u32 mdscr)
+{
+ unsigned long flags;
+ local_dbg_save(flags);
+ asm volatile("msr mdscr_el1, %0" :: "r" (mdscr));
+ local_dbg_restore(flags);
+}
+
+static u32 mdscr_read(void)
+{
+ u32 mdscr;
+ asm volatile("mrs %0, mdscr_el1" : "=r" (mdscr));
+ return mdscr;
+}
+
+/*
+ * Allow root to disable self-hosted debug from userspace.
+ * This is useful if you want to connect an external JTAG debugger.
+ */
+static u32 debug_enabled = 1;
+
+static int create_debug_debugfs_entry(void)
+{
+ debugfs_create_bool("debug_enabled", 0644, NULL, &debug_enabled);
+ return 0;
+}
+fs_initcall(create_debug_debugfs_entry);
+
+static int __init early_debug_disable(char *buf)
+{
+ debug_enabled = 0;
+ return 0;
+}
+
+early_param("nodebugmon", early_debug_disable);
+
+/*
+ * Keep track of debug users on each core.
+ * The ref counts are per-cpu so we use a local_t type.
+ */
+static DEFINE_PER_CPU(local_t, mde_ref_count);
+static DEFINE_PER_CPU(local_t, kde_ref_count);
+
+void enable_debug_monitors(enum debug_el el)
+{
+ u32 mdscr, enable = 0;
+
+ WARN_ON(preemptible());
+
+ if (local_inc_return(&__get_cpu_var(mde_ref_count)) == 1)
+ enable = DBG_MDSCR_MDE;
+
+ if (el == DBG_ACTIVE_EL1 &&
+ local_inc_return(&__get_cpu_var(kde_ref_count)) == 1)
+ enable |= DBG_MDSCR_KDE;
+
+ if (enable && debug_enabled) {
+ mdscr = mdscr_read();
+ mdscr |= enable;
+ mdscr_write(mdscr);
+ }
+}
+
+void disable_debug_monitors(enum debug_el el)
+{
+ u32 mdscr, disable = 0;
+
+ WARN_ON(preemptible());
+
+ if (local_dec_and_test(&__get_cpu_var(mde_ref_count)))
+ disable = ~DBG_MDSCR_MDE;
+
+ if (el == DBG_ACTIVE_EL1 &&
+ local_dec_and_test(&__get_cpu_var(kde_ref_count)))
+ disable &= ~DBG_MDSCR_KDE;
+
+ if (disable) {
+ mdscr = mdscr_read();
+ mdscr &= disable;
+ mdscr_write(mdscr);
+ }
+}
+
+/*
+ * OS lock clearing.
+ */
+static void clear_os_lock(void *unused)
+{
+ asm volatile("msr mdscr_el1, %0" : : "r" (0));
+ isb();
+ asm volatile("msr oslar_el1, %0" : : "r" (0));
+ isb();
+}
+
+static int __cpuinit os_lock_notify(struct notifier_block *self,
+ unsigned long action, void *data)
+{
+ int cpu = (unsigned long)data;
+ if (action == CPU_ONLINE)
+ smp_call_function_single(cpu, clear_os_lock, NULL, 1);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata os_lock_nb = {
+ .notifier_call = os_lock_notify,
+};
+
+static int __cpuinit debug_monitors_init(void)
+{
+ /* Clear the OS lock. */
+ smp_call_function(clear_os_lock, NULL, 1);
+ clear_os_lock(NULL);
+
+ /* Register hotplug handler. */
+ register_cpu_notifier(&os_lock_nb);
+ return 0;
+}
+postcore_initcall(debug_monitors_init);
+
+/*
+ * Single step API and exception handling.
+ */
+static void set_regs_spsr_ss(struct pt_regs *regs)
+{
+ unsigned long spsr;
+
+ spsr = regs->pstate;
+ spsr &= ~DBG_SPSR_SS;
+ spsr |= DBG_SPSR_SS;
+ regs->pstate = spsr;
+}
+
+static void clear_regs_spsr_ss(struct pt_regs *regs)
+{
+ unsigned long spsr;
+
+ spsr = regs->pstate;
+ spsr &= ~DBG_SPSR_SS;
+ regs->pstate = spsr;
+}
+
+static int single_step_handler(unsigned long addr, unsigned int esr,
+ struct pt_regs *regs)
+{
+ siginfo_t info;
+
+ /*
+ * If we are stepping a pending breakpoint, call the hw_breakpoint
+ * handler first.
+ */
+ if (!reinstall_suspended_bps(regs))
+ return 0;
+
+ if (user_mode(regs)) {
+ info.si_signo = SIGTRAP;
+ info.si_errno = 0;
+ info.si_code = TRAP_HWBKPT;
+ info.si_addr = (void __user *)instruction_pointer(regs);
+ force_sig_info(SIGTRAP, &info, current);
+
+ /*
+ * ptrace will disable single step unless explicitly
+ * asked to re-enable it. For other clients, it makes
+ * sense to leave it enabled (i.e. rewind the controls
+ * to the active-not-pending state).
+ */
+ user_rewind_single_step(current);
+ } else {
+ /* TODO: route to KGDB */
+ pr_warning("Unexpected kernel single-step exception at EL1\n");
+ /*
+ * Re-enable stepping since we know that we will be
+ * returning to regs.
+ */
+ set_regs_spsr_ss(regs);
+ }
+
+ return 0;
+}
+
+static int __init single_step_init(void)
+{
+ hook_debug_fault_code(DBG_ESR_EVT_HWSS, single_step_handler, SIGTRAP,
+ TRAP_HWBKPT, "single-step handler");
+ return 0;
+}
+arch_initcall(single_step_init);
+
+/* Re-enable single step for syscall restarting. */
+void user_rewind_single_step(struct task_struct *task)
+{
+ /*
+ * If single step is active for this thread, then set SPSR.SS
+ * to 1 to avoid returning to the active-pending state.
+ */
+ if (test_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP))
+ set_regs_spsr_ss(task_pt_regs(task));
+}
+
+void user_fastforward_single_step(struct task_struct *task)
+{
+ if (test_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP))
+ clear_regs_spsr_ss(task_pt_regs(task));
+}
+
+/* Kernel API */
+void kernel_enable_single_step(struct pt_regs *regs)
+{
+ WARN_ON(!irqs_disabled());
+ set_regs_spsr_ss(regs);
+ mdscr_write(mdscr_read() | DBG_MDSCR_SS);
+ enable_debug_monitors(DBG_ACTIVE_EL1);
+}
+
+void kernel_disable_single_step(void)
+{
+ WARN_ON(!irqs_disabled());
+ mdscr_write(mdscr_read() & ~DBG_MDSCR_SS);
+ disable_debug_monitors(DBG_ACTIVE_EL1);
+}
+
+int kernel_active_single_step(void)
+{
+ WARN_ON(!irqs_disabled());
+ return mdscr_read() & DBG_MDSCR_SS;
+}
+
+/* ptrace API */
+void user_enable_single_step(struct task_struct *task)
+{
+ set_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP);
+ set_regs_spsr_ss(task_pt_regs(task));
+}
+
+void user_disable_single_step(struct task_struct *task)
+{
+ clear_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP);
+}
diff --git a/arch/arm64/kernel/entry-fpsimd.S b/arch/arm64/kernel/entry-fpsimd.S
new file mode 100644
index 000000000000..17988a6e7ea2
--- /dev/null
+++ b/arch/arm64/kernel/entry-fpsimd.S
@@ -0,0 +1,80 @@
+/*
+ * FP/SIMD state saving and restoring
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Catalin Marinas <catalin.marinas@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/assembler.h>
+
+/*
+ * Save the FP registers.
+ *
+ * x0 - pointer to struct fpsimd_state
+ */
+ENTRY(fpsimd_save_state)
+ stp q0, q1, [x0, #16 * 0]
+ stp q2, q3, [x0, #16 * 2]
+ stp q4, q5, [x0, #16 * 4]
+ stp q6, q7, [x0, #16 * 6]
+ stp q8, q9, [x0, #16 * 8]
+ stp q10, q11, [x0, #16 * 10]
+ stp q12, q13, [x0, #16 * 12]
+ stp q14, q15, [x0, #16 * 14]
+ stp q16, q17, [x0, #16 * 16]
+ stp q18, q19, [x0, #16 * 18]
+ stp q20, q21, [x0, #16 * 20]
+ stp q22, q23, [x0, #16 * 22]
+ stp q24, q25, [x0, #16 * 24]
+ stp q26, q27, [x0, #16 * 26]
+ stp q28, q29, [x0, #16 * 28]
+ stp q30, q31, [x0, #16 * 30]!
+ mrs x8, fpsr
+ str w8, [x0, #16 * 2]
+ mrs x8, fpcr
+ str w8, [x0, #16 * 2 + 4]
+ ret
+ENDPROC(fpsimd_save_state)
+
+/*
+ * Load the FP registers.
+ *
+ * x0 - pointer to struct fpsimd_state
+ */
+ENTRY(fpsimd_load_state)
+ ldp q0, q1, [x0, #16 * 0]
+ ldp q2, q3, [x0, #16 * 2]
+ ldp q4, q5, [x0, #16 * 4]
+ ldp q6, q7, [x0, #16 * 6]
+ ldp q8, q9, [x0, #16 * 8]
+ ldp q10, q11, [x0, #16 * 10]
+ ldp q12, q13, [x0, #16 * 12]
+ ldp q14, q15, [x0, #16 * 14]
+ ldp q16, q17, [x0, #16 * 16]
+ ldp q18, q19, [x0, #16 * 18]
+ ldp q20, q21, [x0, #16 * 20]
+ ldp q22, q23, [x0, #16 * 22]
+ ldp q24, q25, [x0, #16 * 24]
+ ldp q26, q27, [x0, #16 * 26]
+ ldp q28, q29, [x0, #16 * 28]
+ ldp q30, q31, [x0, #16 * 30]!
+ ldr w8, [x0, #16 * 2]
+ ldr w9, [x0, #16 * 2 + 4]
+ msr fpsr, x8
+ msr fpcr, x9
+ ret
+ENDPROC(fpsimd_load_state)
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S
new file mode 100644
index 000000000000..38cf853a3667
--- /dev/null
+++ b/arch/arm64/kernel/entry.S
@@ -0,0 +1,695 @@
+/*
+ * Low-level exception handling code
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Authors: Catalin Marinas <catalin.marinas@arm.com>
+ * Will Deacon <will.deacon@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/init.h>
+#include <linux/linkage.h>
+
+#include <asm/assembler.h>
+#include <asm/asm-offsets.h>
+#include <asm/errno.h>
+#include <asm/thread_info.h>
+#include <asm/unistd.h>
+
+/*
+ * Bad Abort numbers
+ *-----------------
+ */
+#define BAD_SYNC 0
+#define BAD_IRQ 1
+#define BAD_FIQ 2
+#define BAD_ERROR 3
+
+ .macro kernel_entry, el, regsize = 64
+ sub sp, sp, #S_FRAME_SIZE - S_LR // room for LR, SP, SPSR, ELR
+ .if \regsize == 32
+ mov w0, w0 // zero upper 32 bits of x0
+ .endif
+ push x28, x29
+ push x26, x27
+ push x24, x25
+ push x22, x23
+ push x20, x21
+ push x18, x19
+ push x16, x17
+ push x14, x15
+ push x12, x13
+ push x10, x11
+ push x8, x9
+ push x6, x7
+ push x4, x5
+ push x2, x3
+ push x0, x1
+ .if \el == 0
+ mrs x21, sp_el0
+ .else
+ add x21, sp, #S_FRAME_SIZE
+ .endif
+ mrs x22, elr_el1
+ mrs x23, spsr_el1
+ stp lr, x21, [sp, #S_LR]
+ stp x22, x23, [sp, #S_PC]
+
+ /*
+ * Set syscallno to -1 by default (overridden later if real syscall).
+ */
+ .if \el == 0
+ mvn x21, xzr
+ str x21, [sp, #S_SYSCALLNO]
+ .endif
+
+ /*
+ * Registers that may be useful after this macro is invoked:
+ *
+ * x21 - aborted SP
+ * x22 - aborted PC
+ * x23 - aborted PSTATE
+ */
+ .endm
+
+ .macro kernel_exit, el, ret = 0
+ ldp x21, x22, [sp, #S_PC] // load ELR, SPSR
+ .if \el == 0
+ ldr x23, [sp, #S_SP] // load return stack pointer
+ .endif
+ .if \ret
+ ldr x1, [sp, #S_X1] // preserve x0 (syscall return)
+ add sp, sp, S_X2
+ .else
+ pop x0, x1
+ .endif
+ pop x2, x3 // load the rest of the registers
+ pop x4, x5
+ pop x6, x7
+ pop x8, x9
+ msr elr_el1, x21 // set up the return data
+ msr spsr_el1, x22
+ .if \el == 0
+ msr sp_el0, x23
+ .endif
+ pop x10, x11
+ pop x12, x13
+ pop x14, x15
+ pop x16, x17
+ pop x18, x19
+ pop x20, x21
+ pop x22, x23
+ pop x24, x25
+ pop x26, x27
+ pop x28, x29
+ ldr lr, [sp], #S_FRAME_SIZE - S_LR // load LR and restore SP
+ eret // return to kernel
+ .endm
+
+ .macro get_thread_info, rd
+ mov \rd, sp
+ and \rd, \rd, #~((1 << 13) - 1) // top of 8K stack
+ .endm
+
+/*
+ * These are the registers used in the syscall handler, and allow us to
+ * have in theory up to 7 arguments to a function - x0 to x6.
+ *
+ * x7 is reserved for the system call number in 32-bit mode.
+ */
+sc_nr .req x25 // number of system calls
+scno .req x26 // syscall number
+stbl .req x27 // syscall table pointer
+tsk .req x28 // current thread_info
+
+/*
+ * Interrupt handling.
+ */
+ .macro irq_handler
+ ldr x1, handle_arch_irq
+ mov x0, sp
+ blr x1
+ .endm
+
+ .text
+
+/*
+ * Exception vectors.
+ */
+ .macro ventry label
+ .align 7
+ b \label
+ .endm
+
+ .align 11
+ENTRY(vectors)
+ ventry el1_sync_invalid // Synchronous EL1t
+ ventry el1_irq_invalid // IRQ EL1t
+ ventry el1_fiq_invalid // FIQ EL1t
+ ventry el1_error_invalid // Error EL1t
+
+ ventry el1_sync // Synchronous EL1h
+ ventry el1_irq // IRQ EL1h
+ ventry el1_fiq_invalid // FIQ EL1h
+ ventry el1_error_invalid // Error EL1h
+
+ ventry el0_sync // Synchronous 64-bit EL0
+ ventry el0_irq // IRQ 64-bit EL0
+ ventry el0_fiq_invalid // FIQ 64-bit EL0
+ ventry el0_error_invalid // Error 64-bit EL0
+
+#ifdef CONFIG_COMPAT
+ ventry el0_sync_compat // Synchronous 32-bit EL0
+ ventry el0_irq_compat // IRQ 32-bit EL0
+ ventry el0_fiq_invalid_compat // FIQ 32-bit EL0
+ ventry el0_error_invalid_compat // Error 32-bit EL0
+#else
+ ventry el0_sync_invalid // Synchronous 32-bit EL0
+ ventry el0_irq_invalid // IRQ 32-bit EL0
+ ventry el0_fiq_invalid // FIQ 32-bit EL0
+ ventry el0_error_invalid // Error 32-bit EL0
+#endif
+END(vectors)
+
+/*
+ * Invalid mode handlers
+ */
+ .macro inv_entry, el, reason, regsize = 64
+ kernel_entry el, \regsize
+ mov x0, sp
+ mov x1, #\reason
+ mrs x2, esr_el1
+ b bad_mode
+ .endm
+
+el0_sync_invalid:
+ inv_entry 0, BAD_SYNC
+ENDPROC(el0_sync_invalid)
+
+el0_irq_invalid:
+ inv_entry 0, BAD_IRQ
+ENDPROC(el0_irq_invalid)
+
+el0_fiq_invalid:
+ inv_entry 0, BAD_FIQ
+ENDPROC(el0_fiq_invalid)
+
+el0_error_invalid:
+ inv_entry 0, BAD_ERROR
+ENDPROC(el0_error_invalid)
+
+#ifdef CONFIG_COMPAT
+el0_fiq_invalid_compat:
+ inv_entry 0, BAD_FIQ, 32
+ENDPROC(el0_fiq_invalid_compat)
+
+el0_error_invalid_compat:
+ inv_entry 0, BAD_ERROR, 32
+ENDPROC(el0_error_invalid_compat)
+#endif
+
+el1_sync_invalid:
+ inv_entry 1, BAD_SYNC
+ENDPROC(el1_sync_invalid)
+
+el1_irq_invalid:
+ inv_entry 1, BAD_IRQ
+ENDPROC(el1_irq_invalid)
+
+el1_fiq_invalid:
+ inv_entry 1, BAD_FIQ
+ENDPROC(el1_fiq_invalid)
+
+el1_error_invalid:
+ inv_entry 1, BAD_ERROR
+ENDPROC(el1_error_invalid)
+
+/*
+ * EL1 mode handlers.
+ */
+ .align 6
+el1_sync:
+ kernel_entry 1
+ mrs x1, esr_el1 // read the syndrome register
+ lsr x24, x1, #26 // exception class
+ cmp x24, #0x25 // data abort in EL1
+ b.eq el1_da
+ cmp x24, #0x18 // configurable trap
+ b.eq el1_undef
+ cmp x24, #0x26 // stack alignment exception
+ b.eq el1_sp_pc
+ cmp x24, #0x22 // pc alignment exception
+ b.eq el1_sp_pc
+ cmp x24, #0x00 // unknown exception in EL1
+ b.eq el1_undef
+ cmp x24, #0x30 // debug exception in EL1
+ b.ge el1_dbg
+ b el1_inv
+el1_da:
+ /*
+ * Data abort handling
+ */
+ mrs x0, far_el1
+ enable_dbg_if_not_stepping x2
+ // re-enable interrupts if they were enabled in the aborted context
+ tbnz x23, #7, 1f // PSR_I_BIT
+ enable_irq
+1:
+ mov x2, sp // struct pt_regs
+ bl do_mem_abort
+
+ // disable interrupts before pulling preserved data off the stack
+ disable_irq
+ kernel_exit 1
+el1_sp_pc:
+ /*
+ * Stack or PC alignment exception handling
+ */
+ mrs x0, far_el1
+ mov x1, x25
+ mov x2, sp
+ b do_sp_pc_abort
+el1_undef:
+ /*
+ * Undefined instruction
+ */
+ mov x0, sp
+ b do_undefinstr
+el1_dbg:
+ /*
+ * Debug exception handling
+ */
+ tbz x24, #0, el1_inv // EL1 only
+ mrs x0, far_el1
+ mov x2, sp // struct pt_regs
+ bl do_debug_exception
+
+ kernel_exit 1
+el1_inv:
+ // TODO: add support for undefined instructions in kernel mode
+ mov x0, sp
+ mov x1, #BAD_SYNC
+ mrs x2, esr_el1
+ b bad_mode
+ENDPROC(el1_sync)
+
+ .align 6
+el1_irq:
+ kernel_entry 1
+ enable_dbg_if_not_stepping x0
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_off
+#endif
+#ifdef CONFIG_PREEMPT
+ get_thread_info tsk
+ ldr x24, [tsk, #TI_PREEMPT] // get preempt count
+ add x0, x24, #1 // increment it
+ str x0, [tsk, #TI_PREEMPT]
+#endif
+ irq_handler
+#ifdef CONFIG_PREEMPT
+ str x24, [tsk, #TI_PREEMPT] // restore preempt count
+ cbnz x24, 1f // preempt count != 0
+ ldr x0, [tsk, #TI_FLAGS] // get flags
+ tbz x0, #TIF_NEED_RESCHED, 1f // needs rescheduling?
+ bl el1_preempt
+1:
+#endif
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_on
+#endif
+ kernel_exit 1
+ENDPROC(el1_irq)
+
+#ifdef CONFIG_PREEMPT
+el1_preempt:
+ mov x24, lr
+1: enable_dbg
+ bl preempt_schedule_irq // irq en/disable is done inside
+ ldr x0, [tsk, #TI_FLAGS] // get new tasks TI_FLAGS
+ tbnz x0, #TIF_NEED_RESCHED, 1b // needs rescheduling?
+ ret x24
+#endif
+
+/*
+ * EL0 mode handlers.
+ */
+ .align 6
+el0_sync:
+ kernel_entry 0
+ mrs x25, esr_el1 // read the syndrome register
+ lsr x24, x25, #26 // exception class
+ cmp x24, #0x15 // SVC in 64-bit state
+ b.eq el0_svc
+ adr lr, ret_from_exception
+ cmp x24, #0x24 // data abort in EL0
+ b.eq el0_da
+ cmp x24, #0x20 // instruction abort in EL0
+ b.eq el0_ia
+ cmp x24, #0x07 // FP/ASIMD access
+ b.eq el0_fpsimd_acc
+ cmp x24, #0x2c // FP/ASIMD exception
+ b.eq el0_fpsimd_exc
+ cmp x24, #0x18 // configurable trap
+ b.eq el0_undef
+ cmp x24, #0x26 // stack alignment exception
+ b.eq el0_sp_pc
+ cmp x24, #0x22 // pc alignment exception
+ b.eq el0_sp_pc
+ cmp x24, #0x00 // unknown exception in EL0
+ b.eq el0_undef
+ cmp x24, #0x30 // debug exception in EL0
+ b.ge el0_dbg
+ b el0_inv
+
+#ifdef CONFIG_COMPAT
+ .align 6
+el0_sync_compat:
+ kernel_entry 0, 32
+ mrs x25, esr_el1 // read the syndrome register
+ lsr x24, x25, #26 // exception class
+ cmp x24, #0x11 // SVC in 32-bit state
+ b.eq el0_svc_compat
+ adr lr, ret_from_exception
+ cmp x24, #0x24 // data abort in EL0
+ b.eq el0_da
+ cmp x24, #0x20 // instruction abort in EL0
+ b.eq el0_ia
+ cmp x24, #0x07 // FP/ASIMD access
+ b.eq el0_fpsimd_acc
+ cmp x24, #0x28 // FP/ASIMD exception
+ b.eq el0_fpsimd_exc
+ cmp x24, #0x00 // unknown exception in EL0
+ b.eq el0_undef
+ cmp x24, #0x30 // debug exception in EL0
+ b.ge el0_dbg
+ b el0_inv
+el0_svc_compat:
+ /*
+ * AArch32 syscall handling
+ */
+ adr stbl, compat_sys_call_table // load compat syscall table pointer
+ uxtw scno, w7 // syscall number in w7 (r7)
+ mov sc_nr, #__NR_compat_syscalls
+ b el0_svc_naked
+
+ .align 6
+el0_irq_compat:
+ kernel_entry 0, 32
+ b el0_irq_naked
+#endif
+
+el0_da:
+ /*
+ * Data abort handling
+ */
+ mrs x0, far_el1
+ disable_step x1
+ isb
+ enable_dbg
+ // enable interrupts before calling the main handler
+ enable_irq
+ mov x1, x25
+ mov x2, sp
+ b do_mem_abort
+el0_ia:
+ /*
+ * Instruction abort handling
+ */
+ mrs x0, far_el1
+ disable_step x1
+ isb
+ enable_dbg
+ // enable interrupts before calling the main handler
+ enable_irq
+ orr x1, x25, #1 << 24 // use reserved ISS bit for instruction aborts
+ mov x2, sp
+ b do_mem_abort
+el0_fpsimd_acc:
+ /*
+ * Floating Point or Advanced SIMD access
+ */
+ mov x0, x25
+ mov x1, sp
+ b do_fpsimd_acc
+el0_fpsimd_exc:
+ /*
+ * Floating Point or Advanced SIMD exception
+ */
+ mov x0, x25
+ mov x1, sp
+ b do_fpsimd_exc
+el0_sp_pc:
+ /*
+ * Stack or PC alignment exception handling
+ */
+ mrs x0, far_el1
+ disable_step x1
+ isb
+ enable_dbg
+ // enable interrupts before calling the main handler
+ enable_irq
+ mov x1, x25
+ mov x2, sp
+ b do_sp_pc_abort
+el0_undef:
+ /*
+ * Undefined instruction
+ */
+ mov x0, sp
+ b do_undefinstr
+el0_dbg:
+ /*
+ * Debug exception handling
+ */
+ tbnz x24, #0, el0_inv // EL0 only
+ mrs x0, far_el1
+ disable_step x1
+ mov x1, x25
+ mov x2, sp
+ b do_debug_exception
+el0_inv:
+ mov x0, sp
+ mov x1, #BAD_SYNC
+ mrs x2, esr_el1
+ b bad_mode
+ENDPROC(el0_sync)
+
+ .align 6
+el0_irq:
+ kernel_entry 0
+el0_irq_naked:
+ disable_step x1
+ isb
+ enable_dbg
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_off
+#endif
+ get_thread_info tsk
+#ifdef CONFIG_PREEMPT
+ ldr x24, [tsk, #TI_PREEMPT] // get preempt count
+ add x23, x24, #1 // increment it
+ str x23, [tsk, #TI_PREEMPT]
+#endif
+ irq_handler
+#ifdef CONFIG_PREEMPT
+ ldr x0, [tsk, #TI_PREEMPT]
+ str x24, [tsk, #TI_PREEMPT]
+ cmp x0, x23
+ b.eq 1f
+ mov x1, #0
+ str x1, [x1] // BUG
+1:
+#endif
+#ifdef CONFIG_TRACE_IRQFLAGS
+ bl trace_hardirqs_on
+#endif
+ b ret_to_user
+ENDPROC(el0_irq)
+
+/*
+ * This is the return code to user mode for abort handlers
+ */
+ret_from_exception:
+ get_thread_info tsk
+ b ret_to_user
+ENDPROC(ret_from_exception)
+
+/*
+ * Register switch for AArch64. The callee-saved registers need to be saved
+ * and restored. On entry:
+ * x0 = previous task_struct (must be preserved across the switch)
+ * x1 = next task_struct
+ * Previous and next are guaranteed not to be the same.
+ *
+ */
+ENTRY(cpu_switch_to)
+ add x8, x0, #THREAD_CPU_CONTEXT
+ mov x9, sp
+ stp x19, x20, [x8], #16 // store callee-saved registers
+ stp x21, x22, [x8], #16
+ stp x23, x24, [x8], #16
+ stp x25, x26, [x8], #16
+ stp x27, x28, [x8], #16
+ stp x29, x9, [x8], #16
+ str lr, [x8]
+ add x8, x1, #THREAD_CPU_CONTEXT
+ ldp x19, x20, [x8], #16 // restore callee-saved registers
+ ldp x21, x22, [x8], #16
+ ldp x23, x24, [x8], #16
+ ldp x25, x26, [x8], #16
+ ldp x27, x28, [x8], #16
+ ldp x29, x9, [x8], #16
+ ldr lr, [x8]
+ mov sp, x9
+ ret
+ENDPROC(cpu_switch_to)
+
+/*
+ * This is the fast syscall return path. We do as little as possible here,
+ * and this includes saving x0 back into the kernel stack.
+ */
+ret_fast_syscall:
+ disable_irq // disable interrupts
+ ldr x1, [tsk, #TI_FLAGS]
+ and x2, x1, #_TIF_WORK_MASK
+ cbnz x2, fast_work_pending
+ tbz x1, #TIF_SINGLESTEP, fast_exit
+ disable_dbg
+ enable_step x2
+fast_exit:
+ kernel_exit 0, ret = 1
+
+/*
+ * Ok, we need to do extra processing, enter the slow path.
+ */
+fast_work_pending:
+ str x0, [sp, #S_X0] // returned x0
+work_pending:
+ tbnz x1, #TIF_NEED_RESCHED, work_resched
+ /* TIF_SIGPENDING or TIF_NOTIFY_RESUME case */
+ ldr x2, [sp, #S_PSTATE]
+ mov x0, sp // 'regs'
+ tst x2, #PSR_MODE_MASK // user mode regs?
+ b.ne no_work_pending // returning to kernel
+ bl do_notify_resume
+ b ret_to_user
+work_resched:
+ enable_dbg
+ bl schedule
+
+/*
+ * "slow" syscall return path.
+ */
+ENTRY(ret_to_user)
+ disable_irq // disable interrupts
+ ldr x1, [tsk, #TI_FLAGS]
+ and x2, x1, #_TIF_WORK_MASK
+ cbnz x2, work_pending
+ tbz x1, #TIF_SINGLESTEP, no_work_pending
+ disable_dbg
+ enable_step x2
+no_work_pending:
+ kernel_exit 0, ret = 0
+ENDPROC(ret_to_user)
+
+/*
+ * This is how we return from a fork.
+ */
+ENTRY(ret_from_fork)
+ bl schedule_tail
+ get_thread_info tsk
+ b ret_to_user
+ENDPROC(ret_from_fork)
+
+/*
+ * SVC handler.
+ */
+ .align 6
+el0_svc:
+ adrp stbl, sys_call_table // load syscall table pointer
+ uxtw scno, w8 // syscall number in w8
+ mov sc_nr, #__NR_syscalls
+el0_svc_naked: // compat entry point
+ stp x0, scno, [sp, #S_ORIG_X0] // save the original x0 and syscall number
+ disable_step x16
+ isb
+ enable_dbg
+ enable_irq
+
+ get_thread_info tsk
+ ldr x16, [tsk, #TI_FLAGS] // check for syscall tracing
+ tbnz x16, #TIF_SYSCALL_TRACE, __sys_trace // are we tracing syscalls?
+ adr lr, ret_fast_syscall // return address
+ cmp scno, sc_nr // check upper syscall limit
+ b.hs ni_sys
+ ldr x16, [stbl, scno, lsl #3] // address in the syscall table
+ br x16 // call sys_* routine
+ni_sys:
+ mov x0, sp
+ b do_ni_syscall
+ENDPROC(el0_svc)
+
+ /*
+ * This is the really slow path. We're going to be doing context
+ * switches, and waiting for our parent to respond.
+ */
+__sys_trace:
+ mov x1, sp
+ mov w0, #0 // trace entry
+ bl syscall_trace
+ adr lr, __sys_trace_return // return address
+ uxtw scno, w0 // syscall number (possibly new)
+ mov x1, sp // pointer to regs
+ cmp scno, sc_nr // check upper syscall limit
+ b.hs ni_sys
+ ldp x0, x1, [sp] // restore the syscall args
+ ldp x2, x3, [sp, #S_X2]
+ ldp x4, x5, [sp, #S_X4]
+ ldp x6, x7, [sp, #S_X6]
+ ldr x16, [stbl, scno, lsl #3] // address in the syscall table
+ br x16 // call sys_* routine
+
+__sys_trace_return:
+ str x0, [sp] // save returned x0
+ mov x1, sp
+ mov w0, #1 // trace exit
+ bl syscall_trace
+ b ret_to_user
+
+/*
+ * Special system call wrappers.
+ */
+ENTRY(sys_execve_wrapper)
+ mov x3, sp
+ b sys_execve
+ENDPROC(sys_execve_wrapper)
+
+ENTRY(sys_clone_wrapper)
+ mov x5, sp
+ b sys_clone
+ENDPROC(sys_clone_wrapper)
+
+ENTRY(sys_rt_sigreturn_wrapper)
+ mov x0, sp
+ b sys_rt_sigreturn
+ENDPROC(sys_rt_sigreturn_wrapper)
+
+ENTRY(sys_sigaltstack_wrapper)
+ ldr x2, [sp, #S_SP]
+ b sys_sigaltstack
+ENDPROC(sys_sigaltstack_wrapper)
+
+ENTRY(handle_arch_irq)
+ .quad 0
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
new file mode 100644
index 000000000000..e8b8357aedb4
--- /dev/null
+++ b/arch/arm64/kernel/fpsimd.c
@@ -0,0 +1,106 @@
+/*
+ * FP/SIMD context switching and fault handling
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Catalin Marinas <catalin.marinas@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/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+
+#include <asm/fpsimd.h>
+#include <asm/cputype.h>
+
+#define FPEXC_IOF (1 << 0)
+#define FPEXC_DZF (1 << 1)
+#define FPEXC_OFF (1 << 2)
+#define FPEXC_UFF (1 << 3)
+#define FPEXC_IXF (1 << 4)
+#define FPEXC_IDF (1 << 7)
+
+/*
+ * Trapped FP/ASIMD access.
+ */
+void do_fpsimd_acc(unsigned int esr, struct pt_regs *regs)
+{
+ /* TODO: implement lazy context saving/restoring */
+ WARN_ON(1);
+}
+
+/*
+ * Raise a SIGFPE for the current process.
+ */
+void do_fpsimd_exc(unsigned int esr, struct pt_regs *regs)
+{
+ siginfo_t info;
+ unsigned int si_code = 0;
+
+ if (esr & FPEXC_IOF)
+ si_code = FPE_FLTINV;
+ else if (esr & FPEXC_DZF)
+ si_code = FPE_FLTDIV;
+ else if (esr & FPEXC_OFF)
+ si_code = FPE_FLTOVF;
+ else if (esr & FPEXC_UFF)
+ si_code = FPE_FLTUND;
+ else if (esr & FPEXC_IXF)
+ si_code = FPE_FLTRES;
+
+ memset(&info, 0, sizeof(info));
+ info.si_signo = SIGFPE;
+ info.si_code = si_code;
+ info.si_addr = (void __user *)instruction_pointer(regs);
+
+ send_sig_info(SIGFPE, &info, current);
+}
+
+void fpsimd_thread_switch(struct task_struct *next)
+{
+ /* check if not kernel threads */
+ if (current->mm)
+ fpsimd_save_state(&current->thread.fpsimd_state);
+ if (next->mm)
+ fpsimd_load_state(&next->thread.fpsimd_state);
+}
+
+void fpsimd_flush_thread(void)
+{
+ memset(&current->thread.fpsimd_state, 0, sizeof(struct fpsimd_state));
+ fpsimd_load_state(&current->thread.fpsimd_state);
+}
+
+/*
+ * FP/SIMD support code initialisation.
+ */
+static int __init fpsimd_init(void)
+{
+ u64 pfr = read_cpuid(ID_AA64PFR0_EL1);
+
+ if (pfr & (0xf << 16)) {
+ pr_notice("Floating-point is not implemented\n");
+ return 0;
+ }
+ elf_hwcap |= HWCAP_FP;
+
+ if (pfr & (0xf << 20))
+ pr_notice("Advanced SIMD is not implemented\n");
+ else
+ elf_hwcap |= HWCAP_ASIMD;
+
+ return 0;
+}
+late_initcall(fpsimd_init);
diff --git a/arch/arm64/kernel/head.S b/arch/arm64/kernel/head.S
new file mode 100644
index 000000000000..a2f02b63eae9
--- /dev/null
+++ b/arch/arm64/kernel/head.S
@@ -0,0 +1,510 @@
+/*
+ * Low-level CPU initialisation
+ * Based on arch/arm/kernel/head.S
+ *
+ * Copyright (C) 1994-2002 Russell King
+ * Copyright (C) 2003-2012 ARM Ltd.
+ * Authors: Catalin Marinas <catalin.marinas@arm.com>
+ * Will Deacon <will.deacon@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+#include <asm/ptrace.h>
+#include <asm/asm-offsets.h>
+#include <asm/memory.h>
+#include <asm/thread_info.h>
+#include <asm/pgtable-hwdef.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+
+/*
+ * swapper_pg_dir is the virtual address of the initial page table. We place
+ * the page tables 3 * PAGE_SIZE below KERNEL_RAM_VADDR. The idmap_pg_dir has
+ * 2 pages and is placed below swapper_pg_dir.
+ */
+#define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET)
+
+#if (KERNEL_RAM_VADDR & 0xfffff) != 0x80000
+#error KERNEL_RAM_VADDR must start at 0xXXX80000
+#endif
+
+#define SWAPPER_DIR_SIZE (3 * PAGE_SIZE)
+#define IDMAP_DIR_SIZE (2 * PAGE_SIZE)
+
+ .globl swapper_pg_dir
+ .equ swapper_pg_dir, KERNEL_RAM_VADDR - SWAPPER_DIR_SIZE
+
+ .globl idmap_pg_dir
+ .equ idmap_pg_dir, swapper_pg_dir - IDMAP_DIR_SIZE
+
+ .macro pgtbl, ttb0, ttb1, phys
+ add \ttb1, \phys, #TEXT_OFFSET - SWAPPER_DIR_SIZE
+ sub \ttb0, \ttb1, #IDMAP_DIR_SIZE
+ .endm
+
+#ifdef CONFIG_ARM64_64K_PAGES
+#define BLOCK_SHIFT PAGE_SHIFT
+#define BLOCK_SIZE PAGE_SIZE
+#else
+#define BLOCK_SHIFT SECTION_SHIFT
+#define BLOCK_SIZE SECTION_SIZE
+#endif
+
+#define KERNEL_START KERNEL_RAM_VADDR
+#define KERNEL_END _end
+
+/*
+ * Initial memory map attributes.
+ */
+#ifndef CONFIG_SMP
+#define PTE_FLAGS PTE_TYPE_PAGE | PTE_AF
+#define PMD_FLAGS PMD_TYPE_SECT | PMD_SECT_AF
+#else
+#define PTE_FLAGS PTE_TYPE_PAGE | PTE_AF | PTE_SHARED
+#define PMD_FLAGS PMD_TYPE_SECT | PMD_SECT_AF | PMD_SECT_S
+#endif
+
+#ifdef CONFIG_ARM64_64K_PAGES
+#define MM_MMUFLAGS PTE_ATTRINDX(MT_NORMAL) | PTE_FLAGS
+#define IO_MMUFLAGS PTE_ATTRINDX(MT_DEVICE_nGnRE) | PTE_XN | PTE_FLAGS
+#else
+#define MM_MMUFLAGS PMD_ATTRINDX(MT_NORMAL) | PMD_FLAGS
+#define IO_MMUFLAGS PMD_ATTRINDX(MT_DEVICE_nGnRE) | PMD_SECT_XN | PMD_FLAGS
+#endif
+
+/*
+ * Kernel startup entry point.
+ * ---------------------------
+ *
+ * The requirements are:
+ * MMU = off, D-cache = off, I-cache = on or off,
+ * x0 = physical address to the FDT blob.
+ *
+ * This code is mostly position independent so you call this at
+ * __pa(PAGE_OFFSET + TEXT_OFFSET).
+ *
+ * Note that the callee-saved registers are used for storing variables
+ * that are useful before the MMU is enabled. The allocations are described
+ * in the entry routines.
+ */
+ __HEAD
+
+ /*
+ * DO NOT MODIFY. Image header expected by Linux boot-loaders.
+ */
+ b stext // branch to kernel start, magic
+ .long 0 // reserved
+ .quad TEXT_OFFSET // Image load offset from start of RAM
+ .quad 0 // reserved
+ .quad 0 // reserved
+
+ENTRY(stext)
+ mov x21, x0 // x21=FDT
+ bl el2_setup // Drop to EL1
+ mrs x22, midr_el1 // x22=cpuid
+ mov x0, x22
+ bl lookup_processor_type
+ mov x23, x0 // x23=current cpu_table
+ cbz x23, __error_p // invalid processor (x23=0)?
+ bl __calc_phys_offset // x24=PHYS_OFFSET, x28=PHYS_OFFSET-PAGE_OFFSET
+ bl __vet_fdt
+ bl __create_page_tables // x25=TTBR0, x26=TTBR1
+ /*
+ * The following calls CPU specific code in a position independent
+ * manner. See arch/arm64/mm/proc.S for details. x23 = base of
+ * cpu_info structure selected by lookup_processor_type above.
+ * On return, the CPU will be ready for the MMU to be turned on and
+ * the TCR will have been set.
+ */
+ ldr x27, __switch_data // address to jump to after
+ // MMU has been enabled
+ adr lr, __enable_mmu // return (PIC) address
+ ldr x12, [x23, #CPU_INFO_SETUP]
+ add x12, x12, x28 // __virt_to_phys
+ br x12 // initialise processor
+ENDPROC(stext)
+
+/*
+ * If we're fortunate enough to boot at EL2, ensure that the world is
+ * sane before dropping to EL1.
+ */
+ENTRY(el2_setup)
+ mrs x0, CurrentEL
+ cmp x0, #PSR_MODE_EL2t
+ ccmp x0, #PSR_MODE_EL2h, #0x4, ne
+ b.eq 1f
+ ret
+
+ /* Hyp configuration. */
+1: mov x0, #(1 << 31) // 64-bit EL1
+ msr hcr_el2, x0
+
+ /* Generic timers. */
+ mrs x0, cnthctl_el2
+ orr x0, x0, #3 // Enable EL1 physical timers
+ msr cnthctl_el2, x0
+
+ /* Populate ID registers. */
+ mrs x0, midr_el1
+ mrs x1, mpidr_el1
+ msr vpidr_el2, x0
+ msr vmpidr_el2, x1
+
+ /* sctlr_el1 */
+ mov x0, #0x0800 // Set/clear RES{1,0} bits
+ movk x0, #0x30d0, lsl #16
+ msr sctlr_el1, x0
+
+ /* Coprocessor traps. */
+ mov x0, #0x33ff
+ msr cptr_el2, x0 // Disable copro. traps to EL2
+
+#ifdef CONFIG_COMPAT
+ msr hstr_el2, xzr // Disable CP15 traps to EL2
+#endif
+
+ /* spsr */
+ mov x0, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
+ PSR_MODE_EL1h)
+ msr spsr_el2, x0
+ msr elr_el2, lr
+ eret
+ENDPROC(el2_setup)
+
+ .align 3
+2: .quad .
+ .quad PAGE_OFFSET
+
+#ifdef CONFIG_SMP
+ .pushsection .smp.pen.text, "ax"
+ .align 3
+1: .quad .
+ .quad secondary_holding_pen_release
+
+ /*
+ * This provides a "holding pen" for platforms to hold all secondary
+ * cores are held until we're ready for them to initialise.
+ */
+ENTRY(secondary_holding_pen)
+ bl el2_setup // Drop to EL1
+ mrs x0, mpidr_el1
+ and x0, x0, #15 // CPU number
+ adr x1, 1b
+ ldp x2, x3, [x1]
+ sub x1, x1, x2
+ add x3, x3, x1
+pen: ldr x4, [x3]
+ cmp x4, x0
+ b.eq secondary_startup
+ wfe
+ b pen
+ENDPROC(secondary_holding_pen)
+ .popsection
+
+ENTRY(secondary_startup)
+ /*
+ * Common entry point for secondary CPUs.
+ */
+ mrs x22, midr_el1 // x22=cpuid
+ mov x0, x22
+ bl lookup_processor_type
+ mov x23, x0 // x23=current cpu_table
+ cbz x23, __error_p // invalid processor (x23=0)?
+
+ bl __calc_phys_offset // x24=phys offset
+ pgtbl x25, x26, x24 // x25=TTBR0, x26=TTBR1
+ ldr x12, [x23, #CPU_INFO_SETUP]
+ add x12, x12, x28 // __virt_to_phys
+ blr x12 // initialise processor
+
+ ldr x21, =secondary_data
+ ldr x27, =__secondary_switched // address to jump to after enabling the MMU
+ b __enable_mmu
+ENDPROC(secondary_startup)
+
+ENTRY(__secondary_switched)
+ ldr x0, [x21] // get secondary_data.stack
+ mov sp, x0
+ mov x29, #0
+ b secondary_start_kernel
+ENDPROC(__secondary_switched)
+#endif /* CONFIG_SMP */
+
+/*
+ * Setup common bits before finally enabling the MMU. Essentially this is just
+ * loading the page table pointer and vector base registers.
+ *
+ * On entry to this code, x0 must contain the SCTLR_EL1 value for turning on
+ * the MMU.
+ */
+__enable_mmu:
+ ldr x5, =vectors
+ msr vbar_el1, x5
+ msr ttbr0_el1, x25 // load TTBR0
+ msr ttbr1_el1, x26 // load TTBR1
+ isb
+ b __turn_mmu_on
+ENDPROC(__enable_mmu)
+
+/*
+ * Enable the MMU. This completely changes the structure of the visible memory
+ * space. You will not be able to trace execution through this.
+ *
+ * x0 = system control register
+ * x27 = *virtual* address to jump to upon completion
+ *
+ * other registers depend on the function called upon completion
+ */
+ .align 6
+__turn_mmu_on:
+ msr sctlr_el1, x0
+ isb
+ br x27
+ENDPROC(__turn_mmu_on)
+
+/*
+ * Calculate the start of physical memory.
+ */
+__calc_phys_offset:
+ adr x0, 1f
+ ldp x1, x2, [x0]
+ sub x28, x0, x1 // x28 = PHYS_OFFSET - PAGE_OFFSET
+ add x24, x2, x28 // x24 = PHYS_OFFSET
+ ret
+ENDPROC(__calc_phys_offset)
+
+ .align 3
+1: .quad .
+ .quad PAGE_OFFSET
+
+/*
+ * Macro to populate the PGD for the corresponding block entry in the next
+ * level (tbl) for the given virtual address.
+ *
+ * Preserves: pgd, tbl, virt
+ * Corrupts: tmp1, tmp2
+ */
+ .macro create_pgd_entry, pgd, tbl, virt, tmp1, tmp2
+ lsr \tmp1, \virt, #PGDIR_SHIFT
+ and \tmp1, \tmp1, #PTRS_PER_PGD - 1 // PGD index
+ orr \tmp2, \tbl, #3 // PGD entry table type
+ str \tmp2, [\pgd, \tmp1, lsl #3]
+ .endm
+
+/*
+ * Macro to populate block entries in the page table for the start..end
+ * virtual range (inclusive).
+ *
+ * Preserves: tbl, flags
+ * Corrupts: phys, start, end, pstate
+ */
+ .macro create_block_map, tbl, flags, phys, start, end, idmap=0
+ lsr \phys, \phys, #BLOCK_SHIFT
+ .if \idmap
+ and \start, \phys, #PTRS_PER_PTE - 1 // table index
+ .else
+ lsr \start, \start, #BLOCK_SHIFT
+ and \start, \start, #PTRS_PER_PTE - 1 // table index
+ .endif
+ orr \phys, \flags, \phys, lsl #BLOCK_SHIFT // table entry
+ .ifnc \start,\end
+ lsr \end, \end, #BLOCK_SHIFT
+ and \end, \end, #PTRS_PER_PTE - 1 // table end index
+ .endif
+9999: str \phys, [\tbl, \start, lsl #3] // store the entry
+ .ifnc \start,\end
+ add \start, \start, #1 // next entry
+ add \phys, \phys, #BLOCK_SIZE // next block
+ cmp \start, \end
+ b.ls 9999b
+ .endif
+ .endm
+
+/*
+ * Setup the initial page tables. We only setup the barest amount which is
+ * required to get the kernel running. The following sections are required:
+ * - identity mapping to enable the MMU (low address, TTBR0)
+ * - first few MB of the kernel linear mapping to jump to once the MMU has
+ * been enabled, including the FDT blob (TTBR1)
+ */
+__create_page_tables:
+ pgtbl x25, x26, x24 // idmap_pg_dir and swapper_pg_dir addresses
+
+ /*
+ * Clear the idmap and swapper page tables.
+ */
+ mov x0, x25
+ add x6, x26, #SWAPPER_DIR_SIZE
+1: stp xzr, xzr, [x0], #16
+ stp xzr, xzr, [x0], #16
+ stp xzr, xzr, [x0], #16
+ stp xzr, xzr, [x0], #16
+ cmp x0, x6
+ b.lo 1b
+
+ ldr x7, =MM_MMUFLAGS
+
+ /*
+ * Create the identity mapping.
+ */
+ add x0, x25, #PAGE_SIZE // section table address
+ adr x3, __turn_mmu_on // virtual/physical address
+ create_pgd_entry x25, x0, x3, x5, x6
+ create_block_map x0, x7, x3, x5, x5, idmap=1
+
+ /*
+ * Map the kernel image (starting with PHYS_OFFSET).
+ */
+ add x0, x26, #PAGE_SIZE // section table address
+ mov x5, #PAGE_OFFSET
+ create_pgd_entry x26, x0, x5, x3, x6
+ ldr x6, =KERNEL_END - 1
+ mov x3, x24 // phys offset
+ create_block_map x0, x7, x3, x5, x6
+
+ /*
+ * Map the FDT blob (maximum 2MB; must be within 512MB of
+ * PHYS_OFFSET).
+ */
+ mov x3, x21 // FDT phys address
+ and x3, x3, #~((1 << 21) - 1) // 2MB aligned
+ mov x6, #PAGE_OFFSET
+ sub x5, x3, x24 // subtract PHYS_OFFSET
+ tst x5, #~((1 << 29) - 1) // within 512MB?
+ csel x21, xzr, x21, ne // zero the FDT pointer
+ b.ne 1f
+ add x5, x5, x6 // __va(FDT blob)
+ add x6, x5, #1 << 21 // 2MB for the FDT blob
+ sub x6, x6, #1 // inclusive range
+ create_block_map x0, x7, x3, x5, x6
+1:
+ ret
+ENDPROC(__create_page_tables)
+ .ltorg
+
+ .align 3
+ .type __switch_data, %object
+__switch_data:
+ .quad __mmap_switched
+ .quad __data_loc // x4
+ .quad _data // x5
+ .quad __bss_start // x6
+ .quad _end // x7
+ .quad processor_id // x4
+ .quad __fdt_pointer // x5
+ .quad memstart_addr // x6
+ .quad init_thread_union + THREAD_START_SP // sp
+
+/*
+ * The following fragment of code is executed with the MMU on in MMU mode, and
+ * uses absolute addresses; this is not position independent.
+ */
+__mmap_switched:
+ adr x3, __switch_data + 8
+
+ ldp x4, x5, [x3], #16
+ ldp x6, x7, [x3], #16
+ cmp x4, x5 // Copy data segment if needed
+1: ccmp x5, x6, #4, ne
+ b.eq 2f
+ ldr x16, [x4], #8
+ str x16, [x5], #8
+ b 1b
+2:
+1: cmp x6, x7
+ b.hs 2f
+ str xzr, [x6], #8 // Clear BSS
+ b 1b
+2:
+ ldp x4, x5, [x3], #16
+ ldr x6, [x3], #8
+ ldr x16, [x3]
+ mov sp, x16
+ str x22, [x4] // Save processor ID
+ str x21, [x5] // Save FDT pointer
+ str x24, [x6] // Save PHYS_OFFSET
+ mov x29, #0
+ b start_kernel
+ENDPROC(__mmap_switched)
+
+/*
+ * Exception handling. Something went wrong and we can't proceed. We ought to
+ * tell the user, but since we don't have any guarantee that we're even
+ * running on the right architecture, we do virtually nothing.
+ */
+__error_p:
+ENDPROC(__error_p)
+
+__error:
+1: nop
+ b 1b
+ENDPROC(__error)
+
+/*
+ * This function gets the processor ID in w0 and searches the cpu_table[] for
+ * a match. It returns a pointer to the struct cpu_info it found. The
+ * cpu_table[] must end with an empty (all zeros) structure.
+ *
+ * This routine can be called via C code and it needs to work with the MMU
+ * both disabled and enabled (the offset is calculated automatically).
+ */
+ENTRY(lookup_processor_type)
+ adr x1, __lookup_processor_type_data
+ ldp x2, x3, [x1]
+ sub x1, x1, x2 // get offset between VA and PA
+ add x3, x3, x1 // convert VA to PA
+1:
+ ldp w5, w6, [x3] // load cpu_id_val and cpu_id_mask
+ cbz w5, 2f // end of list?
+ and w6, w6, w0
+ cmp w5, w6
+ b.eq 3f
+ add x3, x3, #CPU_INFO_SZ
+ b 1b
+2:
+ mov x3, #0 // unknown processor
+3:
+ mov x0, x3
+ ret
+ENDPROC(lookup_processor_type)
+
+ .align 3
+ .type __lookup_processor_type_data, %object
+__lookup_processor_type_data:
+ .quad .
+ .quad cpu_table
+ .size __lookup_processor_type_data, . - __lookup_processor_type_data
+
+/*
+ * Determine validity of the x21 FDT pointer.
+ * The dtb must be 8-byte aligned and live in the first 512M of memory.
+ */
+__vet_fdt:
+ tst x21, #0x7
+ b.ne 1f
+ cmp x21, x24
+ b.lt 1f
+ mov x0, #(1 << 29)
+ add x0, x0, x24
+ cmp x21, x0
+ b.ge 1f
+ ret
+1:
+ mov x21, #0
+ ret
+ENDPROC(__vet_fdt)
diff --git a/arch/arm64/kernel/hw_breakpoint.c b/arch/arm64/kernel/hw_breakpoint.c
new file mode 100644
index 000000000000..5ab825c59db9
--- /dev/null
+++ b/arch/arm64/kernel/hw_breakpoint.c
@@ -0,0 +1,880 @@
+/*
+ * HW_breakpoint: a unified kernel/user-space hardware breakpoint facility,
+ * using the CPU's debug registers.
+ *
+ * Copyright (C) 2012 ARM Limited
+ * Author: Will Deacon <will.deacon@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/>.
+ */
+
+#define pr_fmt(fmt) "hw-breakpoint: " fmt
+
+#include <linux/errno.h>
+#include <linux/hw_breakpoint.h>
+#include <linux/perf_event.h>
+#include <linux/ptrace.h>
+#include <linux/smp.h>
+
+#include <asm/compat.h>
+#include <asm/current.h>
+#include <asm/debug-monitors.h>
+#include <asm/hw_breakpoint.h>
+#include <asm/kdebug.h>
+#include <asm/traps.h>
+#include <asm/cputype.h>
+#include <asm/system_misc.h>
+
+/* Breakpoint currently in use for each BRP. */
+static DEFINE_PER_CPU(struct perf_event *, bp_on_reg[ARM_MAX_BRP]);
+
+/* Watchpoint currently in use for each WRP. */
+static DEFINE_PER_CPU(struct perf_event *, wp_on_reg[ARM_MAX_WRP]);
+
+/* Currently stepping a per-CPU kernel breakpoint. */
+static DEFINE_PER_CPU(int, stepping_kernel_bp);
+
+/* Number of BRP/WRP registers on this CPU. */
+static int core_num_brps;
+static int core_num_wrps;
+
+/* Determine number of BRP registers available. */
+static int get_num_brps(void)
+{
+ return ((read_cpuid(ID_AA64DFR0_EL1) >> 12) & 0xf) + 1;
+}
+
+/* Determine number of WRP registers available. */
+static int get_num_wrps(void)
+{
+ return ((read_cpuid(ID_AA64DFR0_EL1) >> 20) & 0xf) + 1;
+}
+
+int hw_breakpoint_slots(int type)
+{
+ /*
+ * We can be called early, so don't rely on
+ * our static variables being initialised.
+ */
+ switch (type) {
+ case TYPE_INST:
+ return get_num_brps();
+ case TYPE_DATA:
+ return get_num_wrps();
+ default:
+ pr_warning("unknown slot type: %d\n", type);
+ return 0;
+ }
+}
+
+#define READ_WB_REG_CASE(OFF, N, REG, VAL) \
+ case (OFF + N): \
+ AARCH64_DBG_READ(N, REG, VAL); \
+ break
+
+#define WRITE_WB_REG_CASE(OFF, N, REG, VAL) \
+ case (OFF + N): \
+ AARCH64_DBG_WRITE(N, REG, VAL); \
+ break
+
+#define GEN_READ_WB_REG_CASES(OFF, REG, VAL) \
+ READ_WB_REG_CASE(OFF, 0, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 1, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 2, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 3, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 4, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 5, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 6, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 7, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 8, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 9, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 10, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 11, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 12, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 13, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 14, REG, VAL); \
+ READ_WB_REG_CASE(OFF, 15, REG, VAL)
+
+#define GEN_WRITE_WB_REG_CASES(OFF, REG, VAL) \
+ WRITE_WB_REG_CASE(OFF, 0, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 1, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 2, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 3, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 4, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 5, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 6, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 7, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 8, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 9, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 10, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 11, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 12, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 13, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 14, REG, VAL); \
+ WRITE_WB_REG_CASE(OFF, 15, REG, VAL)
+
+static u64 read_wb_reg(int reg, int n)
+{
+ u64 val = 0;
+
+ switch (reg + n) {
+ GEN_READ_WB_REG_CASES(AARCH64_DBG_REG_BVR, AARCH64_DBG_REG_NAME_BVR, val);
+ GEN_READ_WB_REG_CASES(AARCH64_DBG_REG_BCR, AARCH64_DBG_REG_NAME_BCR, val);
+ GEN_READ_WB_REG_CASES(AARCH64_DBG_REG_WVR, AARCH64_DBG_REG_NAME_WVR, val);
+ GEN_READ_WB_REG_CASES(AARCH64_DBG_REG_WCR, AARCH64_DBG_REG_NAME_WCR, val);
+ default:
+ pr_warning("attempt to read from unknown breakpoint register %d\n", n);
+ }
+
+ return val;
+}
+
+static void write_wb_reg(int reg, int n, u64 val)
+{
+ switch (reg + n) {
+ GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_BVR, AARCH64_DBG_REG_NAME_BVR, val);
+ GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_BCR, AARCH64_DBG_REG_NAME_BCR, val);
+ GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_WVR, AARCH64_DBG_REG_NAME_WVR, val);
+ GEN_WRITE_WB_REG_CASES(AARCH64_DBG_REG_WCR, AARCH64_DBG_REG_NAME_WCR, val);
+ default:
+ pr_warning("attempt to write to unknown breakpoint register %d\n", n);
+ }
+ isb();
+}
+
+/*
+ * Convert a breakpoint privilege level to the corresponding exception
+ * level.
+ */
+static enum debug_el debug_exception_level(int privilege)
+{
+ switch (privilege) {
+ case AARCH64_BREAKPOINT_EL0:
+ return DBG_ACTIVE_EL0;
+ case AARCH64_BREAKPOINT_EL1:
+ return DBG_ACTIVE_EL1;
+ default:
+ pr_warning("invalid breakpoint privilege level %d\n", privilege);
+ return -EINVAL;
+ }
+}
+
+/*
+ * Install a perf counter breakpoint.
+ */
+int arch_install_hw_breakpoint(struct perf_event *bp)
+{
+ struct arch_hw_breakpoint *info = counter_arch_bp(bp);
+ struct perf_event **slot, **slots;
+ struct debug_info *debug_info = &current->thread.debug;
+ int i, max_slots, ctrl_reg, val_reg, reg_enable;
+ u32 ctrl;
+
+ if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
+ /* Breakpoint */
+ ctrl_reg = AARCH64_DBG_REG_BCR;
+ val_reg = AARCH64_DBG_REG_BVR;
+ slots = __get_cpu_var(bp_on_reg);
+ max_slots = core_num_brps;
+ reg_enable = !debug_info->bps_disabled;
+ } else {
+ /* Watchpoint */
+ ctrl_reg = AARCH64_DBG_REG_WCR;
+ val_reg = AARCH64_DBG_REG_WVR;
+ slots = __get_cpu_var(wp_on_reg);
+ max_slots = core_num_wrps;
+ reg_enable = !debug_info->wps_disabled;
+ }
+
+ for (i = 0; i < max_slots; ++i) {
+ slot = &slots[i];
+
+ if (!*slot) {
+ *slot = bp;
+ break;
+ }
+ }
+
+ if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot"))
+ return -ENOSPC;
+
+ /* Ensure debug monitors are enabled at the correct exception level. */
+ enable_debug_monitors(debug_exception_level(info->ctrl.privilege));
+
+ /* Setup the address register. */
+ write_wb_reg(val_reg, i, info->address);
+
+ /* Setup the control register. */
+ ctrl = encode_ctrl_reg(info->ctrl);
+ write_wb_reg(ctrl_reg, i, reg_enable ? ctrl | 0x1 : ctrl & ~0x1);
+
+ return 0;
+}
+
+void arch_uninstall_hw_breakpoint(struct perf_event *bp)
+{
+ struct arch_hw_breakpoint *info = counter_arch_bp(bp);
+ struct perf_event **slot, **slots;
+ int i, max_slots, base;
+
+ if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
+ /* Breakpoint */
+ base = AARCH64_DBG_REG_BCR;
+ slots = __get_cpu_var(bp_on_reg);
+ max_slots = core_num_brps;
+ } else {
+ /* Watchpoint */
+ base = AARCH64_DBG_REG_WCR;
+ slots = __get_cpu_var(wp_on_reg);
+ max_slots = core_num_wrps;
+ }
+
+ /* Remove the breakpoint. */
+ for (i = 0; i < max_slots; ++i) {
+ slot = &slots[i];
+
+ if (*slot == bp) {
+ *slot = NULL;
+ break;
+ }
+ }
+
+ if (WARN_ONCE(i == max_slots, "Can't find any breakpoint slot"))
+ return;
+
+ /* Reset the control register. */
+ write_wb_reg(base, i, 0);
+
+ /* Release the debug monitors for the correct exception level. */
+ disable_debug_monitors(debug_exception_level(info->ctrl.privilege));
+}
+
+static int get_hbp_len(u8 hbp_len)
+{
+ unsigned int len_in_bytes = 0;
+
+ switch (hbp_len) {
+ case ARM_BREAKPOINT_LEN_1:
+ len_in_bytes = 1;
+ break;
+ case ARM_BREAKPOINT_LEN_2:
+ len_in_bytes = 2;
+ break;
+ case ARM_BREAKPOINT_LEN_4:
+ len_in_bytes = 4;
+ break;
+ case ARM_BREAKPOINT_LEN_8:
+ len_in_bytes = 8;
+ break;
+ }
+
+ return len_in_bytes;
+}
+
+/*
+ * Check whether bp virtual address is in kernel space.
+ */
+int arch_check_bp_in_kernelspace(struct perf_event *bp)
+{
+ unsigned int len;
+ unsigned long va;
+ struct arch_hw_breakpoint *info = counter_arch_bp(bp);
+
+ va = info->address;
+ len = get_hbp_len(info->ctrl.len);
+
+ return (va >= TASK_SIZE) && ((va + len - 1) >= TASK_SIZE);
+}
+
+/*
+ * Extract generic type and length encodings from an arch_hw_breakpoint_ctrl.
+ * Hopefully this will disappear when ptrace can bypass the conversion
+ * to generic breakpoint descriptions.
+ */
+int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
+ int *gen_len, int *gen_type)
+{
+ /* Type */
+ switch (ctrl.type) {
+ case ARM_BREAKPOINT_EXECUTE:
+ *gen_type = HW_BREAKPOINT_X;
+ break;
+ case ARM_BREAKPOINT_LOAD:
+ *gen_type = HW_BREAKPOINT_R;
+ break;
+ case ARM_BREAKPOINT_STORE:
+ *gen_type = HW_BREAKPOINT_W;
+ break;
+ case ARM_BREAKPOINT_LOAD | ARM_BREAKPOINT_STORE:
+ *gen_type = HW_BREAKPOINT_RW;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Len */
+ switch (ctrl.len) {
+ case ARM_BREAKPOINT_LEN_1:
+ *gen_len = HW_BREAKPOINT_LEN_1;
+ break;
+ case ARM_BREAKPOINT_LEN_2:
+ *gen_len = HW_BREAKPOINT_LEN_2;
+ break;
+ case ARM_BREAKPOINT_LEN_4:
+ *gen_len = HW_BREAKPOINT_LEN_4;
+ break;
+ case ARM_BREAKPOINT_LEN_8:
+ *gen_len = HW_BREAKPOINT_LEN_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Construct an arch_hw_breakpoint from a perf_event.
+ */
+static int arch_build_bp_info(struct perf_event *bp)
+{
+ struct arch_hw_breakpoint *info = counter_arch_bp(bp);
+
+ /* Type */
+ switch (bp->attr.bp_type) {
+ case HW_BREAKPOINT_X:
+ info->ctrl.type = ARM_BREAKPOINT_EXECUTE;
+ break;
+ case HW_BREAKPOINT_R:
+ info->ctrl.type = ARM_BREAKPOINT_LOAD;
+ break;
+ case HW_BREAKPOINT_W:
+ info->ctrl.type = ARM_BREAKPOINT_STORE;
+ break;
+ case HW_BREAKPOINT_RW:
+ info->ctrl.type = ARM_BREAKPOINT_LOAD | ARM_BREAKPOINT_STORE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Len */
+ switch (bp->attr.bp_len) {
+ case HW_BREAKPOINT_LEN_1:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_1;
+ break;
+ case HW_BREAKPOINT_LEN_2:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_2;
+ break;
+ case HW_BREAKPOINT_LEN_4:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_4;
+ break;
+ case HW_BREAKPOINT_LEN_8:
+ info->ctrl.len = ARM_BREAKPOINT_LEN_8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /*
+ * On AArch64, we only permit breakpoints of length 4, whereas
+ * AArch32 also requires breakpoints of length 2 for Thumb.
+ * Watchpoints can be of length 1, 2, 4 or 8 bytes.
+ */
+ if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE) {
+ if (is_compat_task()) {
+ if (info->ctrl.len != ARM_BREAKPOINT_LEN_2 &&
+ info->ctrl.len != ARM_BREAKPOINT_LEN_4)
+ return -EINVAL;
+ } else if (info->ctrl.len != ARM_BREAKPOINT_LEN_4) {
+ /*
+ * FIXME: Some tools (I'm looking at you perf) assume
+ * that breakpoints should be sizeof(long). This
+ * is nonsense. For now, we fix up the parameter
+ * but we should probably return -EINVAL instead.
+ */
+ info->ctrl.len = ARM_BREAKPOINT_LEN_4;
+ }
+ }
+
+ /* Address */
+ info->address = bp->attr.bp_addr;
+
+ /*
+ * Privilege
+ * Note that we disallow combined EL0/EL1 breakpoints because
+ * that would complicate the stepping code.
+ */
+ if (arch_check_bp_in_kernelspace(bp))
+ info->ctrl.privilege = AARCH64_BREAKPOINT_EL1;
+ else
+ info->ctrl.privilege = AARCH64_BREAKPOINT_EL0;
+
+ /* Enabled? */
+ info->ctrl.enabled = !bp->attr.disabled;
+
+ return 0;
+}
+
+/*
+ * Validate the arch-specific HW Breakpoint register settings.
+ */
+int arch_validate_hwbkpt_settings(struct perf_event *bp)
+{
+ struct arch_hw_breakpoint *info = counter_arch_bp(bp);
+ int ret;
+ u64 alignment_mask, offset;
+
+ /* Build the arch_hw_breakpoint. */
+ ret = arch_build_bp_info(bp);
+ if (ret)
+ return ret;
+
+ /*
+ * Check address alignment.
+ * We don't do any clever alignment correction for watchpoints
+ * because using 64-bit unaligned addresses is deprecated for
+ * AArch64.
+ *
+ * AArch32 tasks expect some simple alignment fixups, so emulate
+ * that here.
+ */
+ if (is_compat_task()) {
+ if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
+ alignment_mask = 0x7;
+ else
+ alignment_mask = 0x3;
+ offset = info->address & alignment_mask;
+ switch (offset) {
+ case 0:
+ /* Aligned */
+ break;
+ case 1:
+ /* Allow single byte watchpoint. */
+ if (info->ctrl.len == ARM_BREAKPOINT_LEN_1)
+ break;
+ case 2:
+ /* Allow halfword watchpoints and breakpoints. */
+ if (info->ctrl.len == ARM_BREAKPOINT_LEN_2)
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ info->address &= ~alignment_mask;
+ info->ctrl.len <<= offset;
+ } else {
+ if (info->ctrl.type == ARM_BREAKPOINT_EXECUTE)
+ alignment_mask = 0x3;
+ else
+ alignment_mask = 0x7;
+ if (info->address & alignment_mask)
+ return -EINVAL;
+ }
+
+ /*
+ * Disallow per-task kernel breakpoints since these would
+ * complicate the stepping code.
+ */
+ if (info->ctrl.privilege == AARCH64_BREAKPOINT_EL1 && bp->hw.bp_target)
+ return -EINVAL;
+
+ return 0;
+}
+
+/*
+ * Enable/disable all of the breakpoints active at the specified
+ * exception level at the register level.
+ * This is used when single-stepping after a breakpoint exception.
+ */
+static void toggle_bp_registers(int reg, enum debug_el el, int enable)
+{
+ int i, max_slots, privilege;
+ u32 ctrl;
+ struct perf_event **slots;
+
+ switch (reg) {
+ case AARCH64_DBG_REG_BCR:
+ slots = __get_cpu_var(bp_on_reg);
+ max_slots = core_num_brps;
+ break;
+ case AARCH64_DBG_REG_WCR:
+ slots = __get_cpu_var(wp_on_reg);
+ max_slots = core_num_wrps;
+ break;
+ default:
+ return;
+ }
+
+ for (i = 0; i < max_slots; ++i) {
+ if (!slots[i])
+ continue;
+
+ privilege = counter_arch_bp(slots[i])->ctrl.privilege;
+ if (debug_exception_level(privilege) != el)
+ continue;
+
+ ctrl = read_wb_reg(reg, i);
+ if (enable)
+ ctrl |= 0x1;
+ else
+ ctrl &= ~0x1;
+ write_wb_reg(reg, i, ctrl);
+ }
+}
+
+/*
+ * Debug exception handlers.
+ */
+static int breakpoint_handler(unsigned long unused, unsigned int esr,
+ struct pt_regs *regs)
+{
+ int i, step = 0, *kernel_step;
+ u32 ctrl_reg;
+ u64 addr, val;
+ struct perf_event *bp, **slots;
+ struct debug_info *debug_info;
+ struct arch_hw_breakpoint_ctrl ctrl;
+
+ slots = (struct perf_event **)__get_cpu_var(bp_on_reg);
+ addr = instruction_pointer(regs);
+ debug_info = &current->thread.debug;
+
+ for (i = 0; i < core_num_brps; ++i) {
+ rcu_read_lock();
+
+ bp = slots[i];
+
+ if (bp == NULL)
+ goto unlock;
+
+ /* Check if the breakpoint value matches. */
+ val = read_wb_reg(AARCH64_DBG_REG_BVR, i);
+ if (val != (addr & ~0x3))
+ goto unlock;
+
+ /* Possible match, check the byte address select to confirm. */
+ ctrl_reg = read_wb_reg(AARCH64_DBG_REG_BCR, i);
+ decode_ctrl_reg(ctrl_reg, &ctrl);
+ if (!((1 << (addr & 0x3)) & ctrl.len))
+ goto unlock;
+
+ counter_arch_bp(bp)->trigger = addr;
+ perf_bp_event(bp, regs);
+
+ /* Do we need to handle the stepping? */
+ if (!bp->overflow_handler)
+ step = 1;
+unlock:
+ rcu_read_unlock();
+ }
+
+ if (!step)
+ return 0;
+
+ if (user_mode(regs)) {
+ debug_info->bps_disabled = 1;
+ toggle_bp_registers(AARCH64_DBG_REG_BCR, DBG_ACTIVE_EL0, 0);
+
+ /* If we're already stepping a watchpoint, just return. */
+ if (debug_info->wps_disabled)
+ return 0;
+
+ if (test_thread_flag(TIF_SINGLESTEP))
+ debug_info->suspended_step = 1;
+ else
+ user_enable_single_step(current);
+ } else {
+ toggle_bp_registers(AARCH64_DBG_REG_BCR, DBG_ACTIVE_EL1, 0);
+ kernel_step = &__get_cpu_var(stepping_kernel_bp);
+
+ if (*kernel_step != ARM_KERNEL_STEP_NONE)
+ return 0;
+
+ if (kernel_active_single_step()) {
+ *kernel_step = ARM_KERNEL_STEP_SUSPEND;
+ } else {
+ *kernel_step = ARM_KERNEL_STEP_ACTIVE;
+ kernel_enable_single_step(regs);
+ }
+ }
+
+ return 0;
+}
+
+static int watchpoint_handler(unsigned long addr, unsigned int esr,
+ struct pt_regs *regs)
+{
+ int i, step = 0, *kernel_step, access;
+ u32 ctrl_reg;
+ u64 val, alignment_mask;
+ struct perf_event *wp, **slots;
+ struct debug_info *debug_info;
+ struct arch_hw_breakpoint *info;
+ struct arch_hw_breakpoint_ctrl ctrl;
+
+ slots = (struct perf_event **)__get_cpu_var(wp_on_reg);
+ debug_info = &current->thread.debug;
+
+ for (i = 0; i < core_num_wrps; ++i) {
+ rcu_read_lock();
+
+ wp = slots[i];
+
+ if (wp == NULL)
+ goto unlock;
+
+ info = counter_arch_bp(wp);
+ /* AArch32 watchpoints are either 4 or 8 bytes aligned. */
+ if (is_compat_task()) {
+ if (info->ctrl.len == ARM_BREAKPOINT_LEN_8)
+ alignment_mask = 0x7;
+ else
+ alignment_mask = 0x3;
+ } else {
+ alignment_mask = 0x7;
+ }
+
+ /* Check if the watchpoint value matches. */
+ val = read_wb_reg(AARCH64_DBG_REG_WVR, i);
+ if (val != (addr & ~alignment_mask))
+ goto unlock;
+
+ /* Possible match, check the byte address select to confirm. */
+ ctrl_reg = read_wb_reg(AARCH64_DBG_REG_WCR, i);
+ decode_ctrl_reg(ctrl_reg, &ctrl);
+ if (!((1 << (addr & alignment_mask)) & ctrl.len))
+ goto unlock;
+
+ /*
+ * Check that the access type matches.
+ * 0 => load, otherwise => store
+ */
+ access = (esr & AARCH64_ESR_ACCESS_MASK) ? HW_BREAKPOINT_W :
+ HW_BREAKPOINT_R;
+ if (!(access & hw_breakpoint_type(wp)))
+ goto unlock;
+
+ info->trigger = addr;
+ perf_bp_event(wp, regs);
+
+ /* Do we need to handle the stepping? */
+ if (!wp->overflow_handler)
+ step = 1;
+
+unlock:
+ rcu_read_unlock();
+ }
+
+ if (!step)
+ return 0;
+
+ /*
+ * We always disable EL0 watchpoints because the kernel can
+ * cause these to fire via an unprivileged access.
+ */
+ toggle_bp_registers(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL0, 0);
+
+ if (user_mode(regs)) {
+ debug_info->wps_disabled = 1;
+
+ /* If we're already stepping a breakpoint, just return. */
+ if (debug_info->bps_disabled)
+ return 0;
+
+ if (test_thread_flag(TIF_SINGLESTEP))
+ debug_info->suspended_step = 1;
+ else
+ user_enable_single_step(current);
+ } else {
+ toggle_bp_registers(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL1, 0);
+ kernel_step = &__get_cpu_var(stepping_kernel_bp);
+
+ if (*kernel_step != ARM_KERNEL_STEP_NONE)
+ return 0;
+
+ if (kernel_active_single_step()) {
+ *kernel_step = ARM_KERNEL_STEP_SUSPEND;
+ } else {
+ *kernel_step = ARM_KERNEL_STEP_ACTIVE;
+ kernel_enable_single_step(regs);
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Handle single-step exception.
+ */
+int reinstall_suspended_bps(struct pt_regs *regs)
+{
+ struct debug_info *debug_info = &current->thread.debug;
+ int handled_exception = 0, *kernel_step;
+
+ kernel_step = &__get_cpu_var(stepping_kernel_bp);
+
+ /*
+ * Called from single-step exception handler.
+ * Return 0 if execution can resume, 1 if a SIGTRAP should be
+ * reported.
+ */
+ if (user_mode(regs)) {
+ if (debug_info->bps_disabled) {
+ debug_info->bps_disabled = 0;
+ toggle_bp_registers(AARCH64_DBG_REG_BCR, DBG_ACTIVE_EL0, 1);
+ handled_exception = 1;
+ }
+
+ if (debug_info->wps_disabled) {
+ debug_info->wps_disabled = 0;
+ toggle_bp_registers(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL0, 1);
+ handled_exception = 1;
+ }
+
+ if (handled_exception) {
+ if (debug_info->suspended_step) {
+ debug_info->suspended_step = 0;
+ /* Allow exception handling to fall-through. */
+ handled_exception = 0;
+ } else {
+ user_disable_single_step(current);
+ }
+ }
+ } else if (*kernel_step != ARM_KERNEL_STEP_NONE) {
+ toggle_bp_registers(AARCH64_DBG_REG_BCR, DBG_ACTIVE_EL1, 1);
+ toggle_bp_registers(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL1, 1);
+
+ if (!debug_info->wps_disabled)
+ toggle_bp_registers(AARCH64_DBG_REG_WCR, DBG_ACTIVE_EL0, 1);
+
+ if (*kernel_step != ARM_KERNEL_STEP_SUSPEND) {
+ kernel_disable_single_step();
+ handled_exception = 1;
+ } else {
+ handled_exception = 0;
+ }
+
+ *kernel_step = ARM_KERNEL_STEP_NONE;
+ }
+
+ return !handled_exception;
+}
+
+/*
+ * Context-switcher for restoring suspended breakpoints.
+ */
+void hw_breakpoint_thread_switch(struct task_struct *next)
+{
+ /*
+ * current next
+ * disabled: 0 0 => The usual case, NOTIFY_DONE
+ * 0 1 => Disable the registers
+ * 1 0 => Enable the registers
+ * 1 1 => NOTIFY_DONE. per-task bps will
+ * get taken care of by perf.
+ */
+
+ struct debug_info *current_debug_info, *next_debug_info;
+
+ current_debug_info = &current->thread.debug;
+ next_debug_info = &next->thread.debug;
+
+ /* Update breakpoints. */
+ if (current_debug_info->bps_disabled != next_debug_info->bps_disabled)
+ toggle_bp_registers(AARCH64_DBG_REG_BCR,
+ DBG_ACTIVE_EL0,
+ !next_debug_info->bps_disabled);
+
+ /* Update watchpoints. */
+ if (current_debug_info->wps_disabled != next_debug_info->wps_disabled)
+ toggle_bp_registers(AARCH64_DBG_REG_WCR,
+ DBG_ACTIVE_EL0,
+ !next_debug_info->wps_disabled);
+}
+
+/*
+ * CPU initialisation.
+ */
+static void reset_ctrl_regs(void *unused)
+{
+ int i;
+
+ for (i = 0; i < core_num_brps; ++i) {
+ write_wb_reg(AARCH64_DBG_REG_BCR, i, 0UL);
+ write_wb_reg(AARCH64_DBG_REG_BVR, i, 0UL);
+ }
+
+ for (i = 0; i < core_num_wrps; ++i) {
+ write_wb_reg(AARCH64_DBG_REG_WCR, i, 0UL);
+ write_wb_reg(AARCH64_DBG_REG_WVR, i, 0UL);
+ }
+}
+
+static int __cpuinit hw_breakpoint_reset_notify(struct notifier_block *self,
+ unsigned long action,
+ void *hcpu)
+{
+ int cpu = (long)hcpu;
+ if (action == CPU_ONLINE)
+ smp_call_function_single(cpu, reset_ctrl_regs, NULL, 1);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata hw_breakpoint_reset_nb = {
+ .notifier_call = hw_breakpoint_reset_notify,
+};
+
+/*
+ * One-time initialisation.
+ */
+static int __init arch_hw_breakpoint_init(void)
+{
+ core_num_brps = get_num_brps();
+ core_num_wrps = get_num_wrps();
+
+ pr_info("found %d breakpoint and %d watchpoint registers.\n",
+ core_num_brps, core_num_wrps);
+
+ /*
+ * Reset the breakpoint resources. We assume that a halting
+ * debugger will leave the world in a nice state for us.
+ */
+ smp_call_function(reset_ctrl_regs, NULL, 1);
+ reset_ctrl_regs(NULL);
+
+ /* Register debug fault handlers. */
+ hook_debug_fault_code(DBG_ESR_EVT_HWBP, breakpoint_handler, SIGTRAP,
+ TRAP_HWBKPT, "hw-breakpoint handler");
+ hook_debug_fault_code(DBG_ESR_EVT_HWWP, watchpoint_handler, SIGTRAP,
+ TRAP_HWBKPT, "hw-watchpoint handler");
+
+ /* Register hotplug notifier. */
+ register_cpu_notifier(&hw_breakpoint_reset_nb);
+
+ return 0;
+}
+arch_initcall(arch_hw_breakpoint_init);
+
+void hw_breakpoint_pmu_read(struct perf_event *bp)
+{
+}
+
+/*
+ * Dummy function to register with die_notifier.
+ */
+int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
+ unsigned long val, void *data)
+{
+ return NOTIFY_DONE;
+}
diff --git a/arch/arm64/kernel/io.c b/arch/arm64/kernel/io.c
new file mode 100644
index 000000000000..7d37ead4d199
--- /dev/null
+++ b/arch/arm64/kernel/io.c
@@ -0,0 +1,64 @@
+/*
+ * Based on arch/arm/kernel/io.c
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/io.h>
+
+/*
+ * Copy data from IO memory space to "real" memory space.
+ */
+void __memcpy_fromio(void *to, const volatile void __iomem *from, size_t count)
+{
+ unsigned char *t = to;
+ while (count) {
+ count--;
+ *t = readb(from);
+ t++;
+ from++;
+ }
+}
+EXPORT_SYMBOL(__memcpy_fromio);
+
+/*
+ * Copy data from "real" memory space to IO memory space.
+ */
+void __memcpy_toio(volatile void __iomem *to, const void *from, size_t count)
+{
+ const unsigned char *f = from;
+ while (count) {
+ count--;
+ writeb(*f, to);
+ f++;
+ to++;
+ }
+}
+EXPORT_SYMBOL(__memcpy_toio);
+
+/*
+ * "memset" on IO memory space.
+ */
+void __memset_io(volatile void __iomem *dst, int c, size_t count)
+{
+ while (count) {
+ count--;
+ writeb(c, dst);
+ dst++;
+ }
+}
+EXPORT_SYMBOL(__memset_io);
diff --git a/arch/arm64/kernel/irq.c b/arch/arm64/kernel/irq.c
new file mode 100644
index 000000000000..0373c6609eaf
--- /dev/null
+++ b/arch/arm64/kernel/irq.c
@@ -0,0 +1,84 @@
+/*
+ * Based on arch/arm/kernel/irq.c
+ *
+ * Copyright (C) 1992 Linus Torvalds
+ * Modifications for ARM processor Copyright (C) 1995-2000 Russell King.
+ * Support for Dynamic Tick Timer Copyright (C) 2004-2005 Nokia Corporation.
+ * Dynamic Tick Timer written by Tony Lindgren <tony@atomide.com> and
+ * Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>.
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel_stat.h>
+#include <linux/irq.h>
+#include <linux/smp.h>
+#include <linux/init.h>
+#include <linux/of_irq.h>
+#include <linux/seq_file.h>
+#include <linux/ratelimit.h>
+
+unsigned long irq_err_count;
+
+int arch_show_interrupts(struct seq_file *p, int prec)
+{
+#ifdef CONFIG_SMP
+ show_ipi_list(p, prec);
+#endif
+ seq_printf(p, "%*s: %10lu\n", prec, "Err", irq_err_count);
+ return 0;
+}
+
+/*
+ * handle_IRQ handles all hardware IRQ's. Decoded IRQs should
+ * not come via this function. Instead, they should provide their
+ * own 'handler'. Used by platform code implementing C-based 1st
+ * level decoding.
+ */
+void handle_IRQ(unsigned int irq, struct pt_regs *regs)
+{
+ struct pt_regs *old_regs = set_irq_regs(regs);
+
+ irq_enter();
+
+ /*
+ * Some hardware gives randomly wrong interrupts. Rather
+ * than crashing, do something sensible.
+ */
+ if (unlikely(irq >= nr_irqs)) {
+ pr_warn_ratelimited("Bad IRQ%u\n", irq);
+ ack_bad_irq(irq);
+ } else {
+ generic_handle_irq(irq);
+ }
+
+ irq_exit();
+ set_irq_regs(old_regs);
+}
+
+/*
+ * Interrupt controllers supported by the kernel.
+ */
+static const struct of_device_id intctrl_of_match[] __initconst = {
+ /* IRQ controllers { .compatible, .data } info to go here */
+ {}
+};
+
+void __init init_IRQ(void)
+{
+ of_irq_init(intctrl_of_match);
+
+ if (!handle_arch_irq)
+ panic("No interrupt controller found.");
+}
diff --git a/arch/arm64/kernel/kuser32.S b/arch/arm64/kernel/kuser32.S
new file mode 100644
index 000000000000..8b69ecb1d8bc
--- /dev/null
+++ b/arch/arm64/kernel/kuser32.S
@@ -0,0 +1,77 @@
+/*
+ * Low-level user helpers placed in the vectors page for AArch32.
+ * Based on the kuser helpers in arch/arm/kernel/entry-armv.S.
+ *
+ * Copyright (C) 2005-2011 Nicolas Pitre <nico@fluxnic.net>
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ *
+ * AArch32 user helpers.
+ *
+ * Each segment is 32-byte aligned and will be moved to the top of the high
+ * vector page. New segments (if ever needed) must be added in front of
+ * existing ones. This mechanism should be used only for things that are
+ * really small and justified, and not be abused freely.
+ *
+ * See Documentation/arm/kernel_user_helpers.txt for formal definitions.
+ */
+ .align 5
+ .globl __kuser_helper_start
+__kuser_helper_start:
+
+__kuser_cmpxchg64: // 0xffff0f60
+ .inst 0xe92d00f0 // push {r4, r5, r6, r7}
+ .inst 0xe1c040d0 // ldrd r4, r5, [r0]
+ .inst 0xe1c160d0 // ldrd r6, r7, [r1]
+ .inst 0xf57ff05f // dmb sy
+ .inst 0xe1b20f9f // 1: ldrexd r0, r1, [r2]
+ .inst 0xe0303004 // eors r3, r0, r4
+ .inst 0x00313005 // eoreqs r3, r1, r5
+ .inst 0x01a23f96 // strexdeq r3, r6, [r2]
+ .inst 0x03330001 // teqeq r3, #1
+ .inst 0x0afffff9 // beq 1b
+ .inst 0xf57ff05f // dmb sy
+ .inst 0xe2730000 // rsbs r0, r3, #0
+ .inst 0xe8bd00f0 // pop {r4, r5, r6, r7}
+ .inst 0xe12fff1e // bx lr
+
+ .align 5
+__kuser_memory_barrier: // 0xffff0fa0
+ .inst 0xf57ff05f // dmb sy
+ .inst 0xe12fff1e // bx lr
+
+ .align 5
+__kuser_cmpxchg: // 0xffff0fc0
+ .inst 0xf57ff05f // dmb sy
+ .inst 0xe1923f9f // 1: ldrex r3, [r2]
+ .inst 0xe0533000 // subs r3, r3, r0
+ .inst 0x01823f91 // strexeq r3, r1, [r2]
+ .inst 0x03330001 // teqeq r3, #1
+ .inst 0x0afffffa // beq 1b
+ .inst 0xe2730000 // rsbs r0, r3, #0
+ .inst 0xeaffffef // b <__kuser_memory_barrier>
+
+ .align 5
+__kuser_get_tls: // 0xffff0fe0
+ .inst 0xee1d0f70 // mrc p15, 0, r0, c13, c0, 3
+ .inst 0xe12fff1e // bx lr
+ .rep 5
+ .word 0
+ .endr
+
+__kuser_helper_version: // 0xffff0ffc
+ .word ((__kuser_helper_end - __kuser_helper_start) >> 5)
+ .globl __kuser_helper_end
+__kuser_helper_end:
diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
new file mode 100644
index 000000000000..ca0e3d55da99
--- /dev/null
+++ b/arch/arm64/kernel/module.c
@@ -0,0 +1,456 @@
+/*
+ * AArch64 loadable module support.
+ *
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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: Will Deacon <will.deacon@arm.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/elf.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/moduleloader.h>
+#include <linux/vmalloc.h>
+
+void *module_alloc(unsigned long size)
+{
+ return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
+ GFP_KERNEL, PAGE_KERNEL_EXEC, -1,
+ __builtin_return_address(0));
+}
+
+enum aarch64_reloc_op {
+ RELOC_OP_NONE,
+ RELOC_OP_ABS,
+ RELOC_OP_PREL,
+ RELOC_OP_PAGE,
+};
+
+static u64 do_reloc(enum aarch64_reloc_op reloc_op, void *place, u64 val)
+{
+ switch (reloc_op) {
+ case RELOC_OP_ABS:
+ return val;
+ case RELOC_OP_PREL:
+ return val - (u64)place;
+ case RELOC_OP_PAGE:
+ return (val & ~0xfff) - ((u64)place & ~0xfff);
+ case RELOC_OP_NONE:
+ return 0;
+ }
+
+ pr_err("do_reloc: unknown relocation operation %d\n", reloc_op);
+ return 0;
+}
+
+static int reloc_data(enum aarch64_reloc_op op, void *place, u64 val, int len)
+{
+ u64 imm_mask = (1 << len) - 1;
+ s64 sval = do_reloc(op, place, val);
+
+ switch (len) {
+ case 16:
+ *(s16 *)place = sval;
+ break;
+ case 32:
+ *(s32 *)place = sval;
+ break;
+ case 64:
+ *(s64 *)place = sval;
+ break;
+ default:
+ pr_err("Invalid length (%d) for data relocation\n", len);
+ return 0;
+ }
+
+ /*
+ * Extract the upper value bits (including the sign bit) and
+ * shift them to bit 0.
+ */
+ sval = (s64)(sval & ~(imm_mask >> 1)) >> (len - 1);
+
+ /*
+ * Overflow has occurred if the value is not representable in
+ * len bits (i.e the bottom len bits are not sign-extended and
+ * the top bits are not all zero).
+ */
+ if ((u64)(sval + 1) > 2)
+ return -ERANGE;
+
+ return 0;
+}
+
+enum aarch64_imm_type {
+ INSN_IMM_MOVNZ,
+ INSN_IMM_MOVK,
+ INSN_IMM_ADR,
+ INSN_IMM_26,
+ INSN_IMM_19,
+ INSN_IMM_16,
+ INSN_IMM_14,
+ INSN_IMM_12,
+ INSN_IMM_9,
+};
+
+static u32 encode_insn_immediate(enum aarch64_imm_type type, u32 insn, u64 imm)
+{
+ u32 immlo, immhi, lomask, himask, mask;
+ int shift;
+
+ switch (type) {
+ case INSN_IMM_MOVNZ:
+ /*
+ * For signed MOVW relocations, we have to manipulate the
+ * instruction encoding depending on whether or not the
+ * immediate is less than zero.
+ */
+ insn &= ~(3 << 29);
+ if ((s64)imm >= 0) {
+ /* >=0: Set the instruction to MOVZ (opcode 10b). */
+ insn |= 2 << 29;
+ } else {
+ /*
+ * <0: Set the instruction to MOVN (opcode 00b).
+ * Since we've masked the opcode already, we
+ * don't need to do anything other than
+ * inverting the new immediate field.
+ */
+ imm = ~imm;
+ }
+ case INSN_IMM_MOVK:
+ mask = BIT(16) - 1;
+ shift = 5;
+ break;
+ case INSN_IMM_ADR:
+ lomask = 0x3;
+ himask = 0x7ffff;
+ immlo = imm & lomask;
+ imm >>= 2;
+ immhi = imm & himask;
+ imm = (immlo << 24) | (immhi);
+ mask = (lomask << 24) | (himask);
+ shift = 5;
+ break;
+ case INSN_IMM_26:
+ mask = BIT(26) - 1;
+ shift = 0;
+ break;
+ case INSN_IMM_19:
+ mask = BIT(19) - 1;
+ shift = 5;
+ break;
+ case INSN_IMM_16:
+ mask = BIT(16) - 1;
+ shift = 5;
+ break;
+ case INSN_IMM_14:
+ mask = BIT(14) - 1;
+ shift = 5;
+ break;
+ case INSN_IMM_12:
+ mask = BIT(12) - 1;
+ shift = 10;
+ break;
+ case INSN_IMM_9:
+ mask = BIT(9) - 1;
+ shift = 12;
+ break;
+ default:
+ pr_err("encode_insn_immediate: unknown immediate encoding %d\n",
+ type);
+ return 0;
+ }
+
+ /* Update the immediate field. */
+ insn &= ~(mask << shift);
+ insn |= (imm & mask) << shift;
+
+ return insn;
+}
+
+static int reloc_insn_movw(enum aarch64_reloc_op op, void *place, u64 val,
+ int lsb, enum aarch64_imm_type imm_type)
+{
+ u64 imm, limit = 0;
+ s64 sval;
+ u32 insn = *(u32 *)place;
+
+ sval = do_reloc(op, place, val);
+ sval >>= lsb;
+ imm = sval & 0xffff;
+
+ /* Update the instruction with the new encoding. */
+ *(u32 *)place = encode_insn_immediate(imm_type, insn, imm);
+
+ /* Shift out the immediate field. */
+ sval >>= 16;
+
+ /*
+ * For unsigned immediates, the overflow check is straightforward.
+ * For signed immediates, the sign bit is actually the bit past the
+ * most significant bit of the field.
+ * The INSN_IMM_16 immediate type is unsigned.
+ */
+ if (imm_type != INSN_IMM_16) {
+ sval++;
+ limit++;
+ }
+
+ /* Check the upper bits depending on the sign of the immediate. */
+ if ((u64)sval > limit)
+ return -ERANGE;
+
+ return 0;
+}
+
+static int reloc_insn_imm(enum aarch64_reloc_op op, void *place, u64 val,
+ int lsb, int len, enum aarch64_imm_type imm_type)
+{
+ u64 imm, imm_mask;
+ s64 sval;
+ u32 insn = *(u32 *)place;
+
+ /* Calculate the relocation value. */
+ sval = do_reloc(op, place, val);
+ sval >>= lsb;
+
+ /* Extract the value bits and shift them to bit 0. */
+ imm_mask = (BIT(lsb + len) - 1) >> lsb;
+ imm = sval & imm_mask;
+
+ /* Update the instruction's immediate field. */
+ *(u32 *)place = encode_insn_immediate(imm_type, insn, imm);
+
+ /*
+ * Extract the upper value bits (including the sign bit) and
+ * shift them to bit 0.
+ */
+ sval = (s64)(sval & ~(imm_mask >> 1)) >> (len - 1);
+
+ /*
+ * Overflow has occurred if the upper bits are not all equal to
+ * the sign bit of the value.
+ */
+ if ((u64)(sval + 1) >= 2)
+ return -ERANGE;
+
+ return 0;
+}
+
+int apply_relocate_add(Elf64_Shdr *sechdrs,
+ const char *strtab,
+ unsigned int symindex,
+ unsigned int relsec,
+ struct module *me)
+{
+ unsigned int i;
+ int ovf;
+ bool overflow_check;
+ Elf64_Sym *sym;
+ void *loc;
+ u64 val;
+ Elf64_Rela *rel = (void *)sechdrs[relsec].sh_addr;
+
+ for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
+ /* loc corresponds to P in the AArch64 ELF document. */
+ loc = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ + rel[i].r_offset;
+
+ /* sym is the ELF symbol we're referring to. */
+ sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
+ + ELF64_R_SYM(rel[i].r_info);
+
+ /* val corresponds to (S + A) in the AArch64 ELF document. */
+ val = sym->st_value + rel[i].r_addend;
+
+ /* Check for overflow by default. */
+ overflow_check = true;
+
+ /* Perform the static relocation. */
+ switch (ELF64_R_TYPE(rel[i].r_info)) {
+ /* Null relocations. */
+ case R_ARM_NONE:
+ case R_AARCH64_NONE:
+ ovf = 0;
+ break;
+
+ /* Data relocations. */
+ case R_AARCH64_ABS64:
+ overflow_check = false;
+ ovf = reloc_data(RELOC_OP_ABS, loc, val, 64);
+ break;
+ case R_AARCH64_ABS32:
+ ovf = reloc_data(RELOC_OP_ABS, loc, val, 32);
+ break;
+ case R_AARCH64_ABS16:
+ ovf = reloc_data(RELOC_OP_ABS, loc, val, 16);
+ break;
+ case R_AARCH64_PREL64:
+ overflow_check = false;
+ ovf = reloc_data(RELOC_OP_PREL, loc, val, 64);
+ break;
+ case R_AARCH64_PREL32:
+ ovf = reloc_data(RELOC_OP_PREL, loc, val, 32);
+ break;
+ case R_AARCH64_PREL16:
+ ovf = reloc_data(RELOC_OP_PREL, loc, val, 16);
+ break;
+
+ /* MOVW instruction relocations. */
+ case R_AARCH64_MOVW_UABS_G0_NC:
+ overflow_check = false;
+ case R_AARCH64_MOVW_UABS_G0:
+ ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
+ INSN_IMM_16);
+ break;
+ case R_AARCH64_MOVW_UABS_G1_NC:
+ overflow_check = false;
+ case R_AARCH64_MOVW_UABS_G1:
+ ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
+ INSN_IMM_16);
+ break;
+ case R_AARCH64_MOVW_UABS_G2_NC:
+ overflow_check = false;
+ case R_AARCH64_MOVW_UABS_G2:
+ ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
+ INSN_IMM_16);
+ break;
+ case R_AARCH64_MOVW_UABS_G3:
+ /* We're using the top bits so we can't overflow. */
+ overflow_check = false;
+ ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 48,
+ INSN_IMM_16);
+ break;
+ case R_AARCH64_MOVW_SABS_G0:
+ ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 0,
+ INSN_IMM_MOVNZ);
+ break;
+ case R_AARCH64_MOVW_SABS_G1:
+ ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 16,
+ INSN_IMM_MOVNZ);
+ break;
+ case R_AARCH64_MOVW_SABS_G2:
+ ovf = reloc_insn_movw(RELOC_OP_ABS, loc, val, 32,
+ INSN_IMM_MOVNZ);
+ break;
+ case R_AARCH64_MOVW_PREL_G0_NC:
+ overflow_check = false;
+ ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
+ INSN_IMM_MOVK);
+ break;
+ case R_AARCH64_MOVW_PREL_G0:
+ ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 0,
+ INSN_IMM_MOVNZ);
+ break;
+ case R_AARCH64_MOVW_PREL_G1_NC:
+ overflow_check = false;
+ ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
+ INSN_IMM_MOVK);
+ break;
+ case R_AARCH64_MOVW_PREL_G1:
+ ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 16,
+ INSN_IMM_MOVNZ);
+ break;
+ case R_AARCH64_MOVW_PREL_G2_NC:
+ overflow_check = false;
+ ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
+ INSN_IMM_MOVK);
+ break;
+ case R_AARCH64_MOVW_PREL_G2:
+ ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 32,
+ INSN_IMM_MOVNZ);
+ break;
+ case R_AARCH64_MOVW_PREL_G3:
+ /* We're using the top bits so we can't overflow. */
+ overflow_check = false;
+ ovf = reloc_insn_movw(RELOC_OP_PREL, loc, val, 48,
+ INSN_IMM_MOVNZ);
+ break;
+
+ /* Immediate instruction relocations. */
+ case R_AARCH64_LD_PREL_LO19:
+ ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
+ INSN_IMM_19);
+ break;
+ case R_AARCH64_ADR_PREL_LO21:
+ ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 0, 21,
+ INSN_IMM_ADR);
+ break;
+ case R_AARCH64_ADR_PREL_PG_HI21_NC:
+ overflow_check = false;
+ case R_AARCH64_ADR_PREL_PG_HI21:
+ ovf = reloc_insn_imm(RELOC_OP_PAGE, loc, val, 12, 21,
+ INSN_IMM_ADR);
+ break;
+ case R_AARCH64_ADD_ABS_LO12_NC:
+ case R_AARCH64_LDST8_ABS_LO12_NC:
+ overflow_check = false;
+ ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 0, 12,
+ INSN_IMM_12);
+ break;
+ case R_AARCH64_LDST16_ABS_LO12_NC:
+ overflow_check = false;
+ ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 1, 11,
+ INSN_IMM_12);
+ break;
+ case R_AARCH64_LDST32_ABS_LO12_NC:
+ overflow_check = false;
+ ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 2, 10,
+ INSN_IMM_12);
+ break;
+ case R_AARCH64_LDST64_ABS_LO12_NC:
+ overflow_check = false;
+ ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 3, 9,
+ INSN_IMM_12);
+ break;
+ case R_AARCH64_LDST128_ABS_LO12_NC:
+ overflow_check = false;
+ ovf = reloc_insn_imm(RELOC_OP_ABS, loc, val, 4, 8,
+ INSN_IMM_12);
+ break;
+ case R_AARCH64_TSTBR14:
+ ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 14,
+ INSN_IMM_14);
+ break;
+ case R_AARCH64_CONDBR19:
+ ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 19,
+ INSN_IMM_19);
+ break;
+ case R_AARCH64_JUMP26:
+ case R_AARCH64_CALL26:
+ ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26,
+ INSN_IMM_26);
+ break;
+
+ default:
+ pr_err("module %s: unsupported RELA relocation: %llu\n",
+ me->name, ELF64_R_TYPE(rel[i].r_info));
+ return -ENOEXEC;
+ }
+
+ if (overflow_check && ovf == -ERANGE)
+ goto overflow;
+
+ }
+
+ return 0;
+
+overflow:
+ pr_err("module %s: overflow in relocation type %d val %Lx\n",
+ me->name, (int)ELF64_R_TYPE(rel[i].r_info), val);
+ return -ENOEXEC;
+}
diff --git a/arch/arm64/kernel/perf_event.c b/arch/arm64/kernel/perf_event.c
new file mode 100644
index 000000000000..ecbf2d81ec5c
--- /dev/null
+++ b/arch/arm64/kernel/perf_event.c
@@ -0,0 +1,1368 @@
+/*
+ * PMU support
+ *
+ * Copyright (C) 2012 ARM Limited
+ * Author: Will Deacon <will.deacon@arm.com>
+ *
+ * This code is based heavily on the ARMv7 perf event 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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/>.
+ */
+#define pr_fmt(fmt) "hw perfevents: " fmt
+
+#include <linux/bitmap.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/perf_event.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+#include <asm/cputype.h>
+#include <asm/irq.h>
+#include <asm/irq_regs.h>
+#include <asm/pmu.h>
+#include <asm/stacktrace.h>
+
+/*
+ * ARMv8 supports a maximum of 32 events.
+ * The cycle counter is included in this total.
+ */
+#define ARMPMU_MAX_HWEVENTS 32
+
+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);
+
+#define to_arm_pmu(p) (container_of(p, struct arm_pmu, pmu))
+
+/* Set at runtime when we know what CPU type we are. */
+static struct arm_pmu *cpu_pmu;
+
+int
+armpmu_get_max_events(void)
+{
+ int max_events = 0;
+
+ if (cpu_pmu != NULL)
+ max_events = cpu_pmu->num_events;
+
+ return max_events;
+}
+EXPORT_SYMBOL_GPL(armpmu_get_max_events);
+
+int perf_num_counters(void)
+{
+ return armpmu_get_max_events();
+}
+EXPORT_SYMBOL_GPL(perf_num_counters);
+
+#define HW_OP_UNSUPPORTED 0xFFFF
+
+#define C(_x) \
+ PERF_COUNT_HW_CACHE_##_x
+
+#define CACHE_OP_UNSUPPORTED 0xFFFF
+
+static int
+armpmu_map_cache_event(const unsigned (*cache_map)
+ [PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX],
+ u64 config)
+{
+ unsigned int cache_type, cache_op, cache_result, ret;
+
+ 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;
+
+ ret = (int)(*cache_map)[cache_type][cache_op][cache_result];
+
+ if (ret == CACHE_OP_UNSUPPORTED)
+ return -ENOENT;
+
+ return ret;
+}
+
+static int
+armpmu_map_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config)
+{
+ int mapping = (*event_map)[config];
+ return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping;
+}
+
+static int
+armpmu_map_raw_event(u32 raw_event_mask, u64 config)
+{
+ return (int)(config & raw_event_mask);
+}
+
+static int map_cpu_event(struct perf_event *event,
+ const unsigned (*event_map)[PERF_COUNT_HW_MAX],
+ const unsigned (*cache_map)
+ [PERF_COUNT_HW_CACHE_MAX]
+ [PERF_COUNT_HW_CACHE_OP_MAX]
+ [PERF_COUNT_HW_CACHE_RESULT_MAX],
+ u32 raw_event_mask)
+{
+ u64 config = event->attr.config;
+
+ switch (event->attr.type) {
+ case PERF_TYPE_HARDWARE:
+ return armpmu_map_event(event_map, config);
+ case PERF_TYPE_HW_CACHE:
+ return armpmu_map_cache_event(cache_map, config);
+ case PERF_TYPE_RAW:
+ return armpmu_map_raw_event(raw_event_mask, config);
+ }
+
+ return -ENOENT;
+}
+
+int
+armpmu_event_set_period(struct perf_event *event,
+ struct hw_perf_event *hwc,
+ int idx)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+ s64 left = local64_read(&hwc->period_left);
+ s64 period = hwc->sample_period;
+ int ret = 0;
+
+ 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 > (s64)armpmu->max_period)
+ left = armpmu->max_period;
+
+ local64_set(&hwc->prev_count, (u64)-left);
+
+ armpmu->write_counter(idx, (u64)(-left) & 0xffffffff);
+
+ perf_event_update_userpage(event);
+
+ return ret;
+}
+
+u64
+armpmu_event_update(struct perf_event *event,
+ struct hw_perf_event *hwc,
+ int idx)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+ u64 delta, prev_raw_count, new_raw_count;
+
+again:
+ prev_raw_count = local64_read(&hwc->prev_count);
+ new_raw_count = armpmu->read_counter(idx);
+
+ if (local64_cmpxchg(&hwc->prev_count, prev_raw_count,
+ new_raw_count) != prev_raw_count)
+ goto again;
+
+ delta = (new_raw_count - prev_raw_count) & armpmu->max_period;
+
+ local64_add(delta, &event->count);
+ local64_sub(delta, &hwc->period_left);
+
+ return new_raw_count;
+}
+
+static void
+armpmu_read(struct perf_event *event)
+{
+ struct hw_perf_event *hwc = &event->hw;
+
+ /* Don't read disabled counters! */
+ if (hwc->idx < 0)
+ return;
+
+ armpmu_event_update(event, hwc, hwc->idx);
+}
+
+static void
+armpmu_stop(struct perf_event *event, int flags)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+
+ /*
+ * ARM pmu always has to update the counter, so ignore
+ * PERF_EF_UPDATE, see comments in armpmu_start().
+ */
+ if (!(hwc->state & PERF_HES_STOPPED)) {
+ armpmu->disable(hwc, hwc->idx);
+ barrier(); /* why? */
+ armpmu_event_update(event, hwc, hwc->idx);
+ hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE;
+ }
+}
+
+static void
+armpmu_start(struct perf_event *event, int flags)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+
+ /*
+ * ARM pmu always has to reprogram the period, so ignore
+ * PERF_EF_RELOAD, see the comment below.
+ */
+ if (flags & PERF_EF_RELOAD)
+ WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
+
+ hwc->state = 0;
+ /*
+ * Set the period again. Some counters can't be stopped, so when we
+ * were stopped we simply disabled the IRQ source and the counter
+ * may have been left counting. If we don't do this step then we may
+ * get an interrupt too soon or *way* too late if the overflow has
+ * happened since disabling.
+ */
+ armpmu_event_set_period(event, hwc, hwc->idx);
+ armpmu->enable(hwc, hwc->idx);
+}
+
+static void
+armpmu_del(struct perf_event *event, int flags)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+ struct pmu_hw_events *hw_events = armpmu->get_hw_events();
+ struct hw_perf_event *hwc = &event->hw;
+ int idx = hwc->idx;
+
+ WARN_ON(idx < 0);
+
+ armpmu_stop(event, PERF_EF_UPDATE);
+ hw_events->events[idx] = NULL;
+ clear_bit(idx, hw_events->used_mask);
+
+ perf_event_update_userpage(event);
+}
+
+static int
+armpmu_add(struct perf_event *event, int flags)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+ struct pmu_hw_events *hw_events = armpmu->get_hw_events();
+ struct hw_perf_event *hwc = &event->hw;
+ int idx;
+ int err = 0;
+
+ perf_pmu_disable(event->pmu);
+
+ /* If we don't have a space for the counter then finish early. */
+ idx = armpmu->get_event_idx(hw_events, hwc);
+ if (idx < 0) {
+ err = idx;
+ goto out;
+ }
+
+ /*
+ * If there is an event in the counter we are going to use then make
+ * sure it is disabled.
+ */
+ event->hw.idx = idx;
+ armpmu->disable(hwc, idx);
+ hw_events->events[idx] = event;
+
+ hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+ if (flags & PERF_EF_START)
+ armpmu_start(event, PERF_EF_RELOAD);
+
+ /* Propagate our changes to the userspace mapping. */
+ perf_event_update_userpage(event);
+
+out:
+ perf_pmu_enable(event->pmu);
+ return err;
+}
+
+static int
+validate_event(struct pmu_hw_events *hw_events,
+ struct perf_event *event)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+ struct hw_perf_event fake_event = event->hw;
+ struct pmu *leader_pmu = event->group_leader->pmu;
+
+ if (event->pmu != leader_pmu || event->state <= PERF_EVENT_STATE_OFF)
+ return 1;
+
+ return armpmu->get_event_idx(hw_events, &fake_event) >= 0;
+}
+
+static int
+validate_group(struct perf_event *event)
+{
+ struct perf_event *sibling, *leader = event->group_leader;
+ struct pmu_hw_events fake_pmu;
+ DECLARE_BITMAP(fake_used_mask, ARMPMU_MAX_HWEVENTS);
+
+ /*
+ * Initialise the fake PMU. We only need to populate the
+ * used_mask for the purposes of validation.
+ */
+ memset(fake_used_mask, 0, sizeof(fake_used_mask));
+ fake_pmu.used_mask = fake_used_mask;
+
+ if (!validate_event(&fake_pmu, leader))
+ return -EINVAL;
+
+ list_for_each_entry(sibling, &leader->sibling_list, group_entry) {
+ if (!validate_event(&fake_pmu, sibling))
+ return -EINVAL;
+ }
+
+ if (!validate_event(&fake_pmu, event))
+ return -EINVAL;
+
+ return 0;
+}
+
+static void
+armpmu_release_hardware(struct arm_pmu *armpmu)
+{
+ int i, irq, irqs;
+ struct platform_device *pmu_device = armpmu->plat_device;
+
+ irqs = min(pmu_device->num_resources, num_possible_cpus());
+
+ for (i = 0; i < irqs; ++i) {
+ if (!cpumask_test_and_clear_cpu(i, &armpmu->active_irqs))
+ continue;
+ irq = platform_get_irq(pmu_device, i);
+ if (irq >= 0)
+ free_irq(irq, armpmu);
+ }
+}
+
+static int
+armpmu_reserve_hardware(struct arm_pmu *armpmu)
+{
+ int i, err, irq, irqs;
+ struct platform_device *pmu_device = armpmu->plat_device;
+
+ if (!pmu_device) {
+ pr_err("no PMU device registered\n");
+ return -ENODEV;
+ }
+
+ irqs = min(pmu_device->num_resources, num_possible_cpus());
+ if (irqs < 1) {
+ pr_err("no irqs for PMUs defined\n");
+ 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, armpmu->handle_irq,
+ IRQF_NOBALANCING,
+ "arm-pmu", armpmu);
+ if (err) {
+ pr_err("unable to request IRQ%d for ARM PMU counters\n",
+ irq);
+ armpmu_release_hardware(armpmu);
+ return err;
+ }
+
+ cpumask_set_cpu(i, &armpmu->active_irqs);
+ }
+
+ return 0;
+}
+
+static void
+hw_perf_event_destroy(struct perf_event *event)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+ atomic_t *active_events = &armpmu->active_events;
+ struct mutex *pmu_reserve_mutex = &armpmu->reserve_mutex;
+
+ if (atomic_dec_and_mutex_lock(active_events, pmu_reserve_mutex)) {
+ armpmu_release_hardware(armpmu);
+ mutex_unlock(pmu_reserve_mutex);
+ }
+}
+
+static int
+event_requires_mode_exclusion(struct perf_event_attr *attr)
+{
+ return attr->exclude_idle || attr->exclude_user ||
+ attr->exclude_kernel || attr->exclude_hv;
+}
+
+static int
+__hw_perf_event_init(struct perf_event *event)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+ struct hw_perf_event *hwc = &event->hw;
+ int mapping, err;
+
+ mapping = armpmu->map_event(event);
+
+ if (mapping < 0) {
+ pr_debug("event %x:%llx not supported\n", event->attr.type,
+ event->attr.config);
+ return mapping;
+ }
+
+ /*
+ * We don't assign an index until we actually place the event onto
+ * hardware. Use -1 to signify that we haven't decided where to put it
+ * yet. For SMP systems, each core has it's own PMU so we can't do any
+ * clever allocation or constraints checking at this point.
+ */
+ hwc->idx = -1;
+ hwc->config_base = 0;
+ hwc->config = 0;
+ hwc->event_base = 0;
+
+ /*
+ * Check whether we need to exclude the counter from certain modes.
+ */
+ if ((!armpmu->set_event_filter ||
+ armpmu->set_event_filter(hwc, &event->attr)) &&
+ event_requires_mode_exclusion(&event->attr)) {
+ pr_debug("ARM performance counters do not support mode exclusion\n");
+ return -EPERM;
+ }
+
+ /*
+ * Store the event encoding into the config_base field.
+ */
+ hwc->config_base |= (unsigned long)mapping;
+
+ if (!hwc->sample_period) {
+ /*
+ * For non-sampling runs, limit the sample_period to half
+ * of the counter width. That way, the new counter value
+ * is far less likely to overtake the previous one unless
+ * you have some serious IRQ latency issues.
+ */
+ hwc->sample_period = armpmu->max_period >> 1;
+ hwc->last_period = hwc->sample_period;
+ local64_set(&hwc->period_left, hwc->sample_period);
+ }
+
+ err = 0;
+ if (event->group_leader != event) {
+ err = validate_group(event);
+ if (err)
+ return -EINVAL;
+ }
+
+ return err;
+}
+
+static int armpmu_event_init(struct perf_event *event)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(event->pmu);
+ int err = 0;
+ atomic_t *active_events = &armpmu->active_events;
+
+ if (armpmu->map_event(event) == -ENOENT)
+ return -ENOENT;
+
+ event->destroy = hw_perf_event_destroy;
+
+ if (!atomic_inc_not_zero(active_events)) {
+ mutex_lock(&armpmu->reserve_mutex);
+ if (atomic_read(active_events) == 0)
+ err = armpmu_reserve_hardware(armpmu);
+
+ if (!err)
+ atomic_inc(active_events);
+ mutex_unlock(&armpmu->reserve_mutex);
+ }
+
+ if (err)
+ return err;
+
+ err = __hw_perf_event_init(event);
+ if (err)
+ hw_perf_event_destroy(event);
+
+ return err;
+}
+
+static void armpmu_enable(struct pmu *pmu)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(pmu);
+ struct pmu_hw_events *hw_events = armpmu->get_hw_events();
+ int enabled = bitmap_weight(hw_events->used_mask, armpmu->num_events);
+
+ if (enabled)
+ armpmu->start();
+}
+
+static void armpmu_disable(struct pmu *pmu)
+{
+ struct arm_pmu *armpmu = to_arm_pmu(pmu);
+ armpmu->stop();
+}
+
+static void __init armpmu_init(struct arm_pmu *armpmu)
+{
+ atomic_set(&armpmu->active_events, 0);
+ mutex_init(&armpmu->reserve_mutex);
+
+ armpmu->pmu = (struct pmu) {
+ .pmu_enable = armpmu_enable,
+ .pmu_disable = armpmu_disable,
+ .event_init = armpmu_event_init,
+ .add = armpmu_add,
+ .del = armpmu_del,
+ .start = armpmu_start,
+ .stop = armpmu_stop,
+ .read = armpmu_read,
+ };
+}
+
+int __init armpmu_register(struct arm_pmu *armpmu, char *name, int type)
+{
+ armpmu_init(armpmu);
+ return perf_pmu_register(&armpmu->pmu, name, type);
+}
+
+/*
+ * ARMv8 PMUv3 Performance Events handling code.
+ * Common event types.
+ */
+enum armv8_pmuv3_perf_types {
+ /* Required events. */
+ ARMV8_PMUV3_PERFCTR_PMNC_SW_INCR = 0x00,
+ ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL = 0x03,
+ ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS = 0x04,
+ ARMV8_PMUV3_PERFCTR_PC_BRANCH_MIS_PRED = 0x10,
+ ARMV8_PMUV3_PERFCTR_CLOCK_CYCLES = 0x11,
+ ARMV8_PMUV3_PERFCTR_PC_BRANCH_PRED = 0x12,
+
+ /* At least one of the following is required. */
+ ARMV8_PMUV3_PERFCTR_INSTR_EXECUTED = 0x08,
+ ARMV8_PMUV3_PERFCTR_OP_SPEC = 0x1B,
+
+ /* Common architectural events. */
+ ARMV8_PMUV3_PERFCTR_MEM_READ = 0x06,
+ ARMV8_PMUV3_PERFCTR_MEM_WRITE = 0x07,
+ ARMV8_PMUV3_PERFCTR_EXC_TAKEN = 0x09,
+ ARMV8_PMUV3_PERFCTR_EXC_EXECUTED = 0x0A,
+ ARMV8_PMUV3_PERFCTR_CID_WRITE = 0x0B,
+ ARMV8_PMUV3_PERFCTR_PC_WRITE = 0x0C,
+ ARMV8_PMUV3_PERFCTR_PC_IMM_BRANCH = 0x0D,
+ ARMV8_PMUV3_PERFCTR_PC_PROC_RETURN = 0x0E,
+ ARMV8_PMUV3_PERFCTR_MEM_UNALIGNED_ACCESS = 0x0F,
+ ARMV8_PMUV3_PERFCTR_TTBR_WRITE = 0x1C,
+
+ /* Common microarchitectural events. */
+ ARMV8_PMUV3_PERFCTR_L1_ICACHE_REFILL = 0x01,
+ ARMV8_PMUV3_PERFCTR_ITLB_REFILL = 0x02,
+ ARMV8_PMUV3_PERFCTR_DTLB_REFILL = 0x05,
+ ARMV8_PMUV3_PERFCTR_MEM_ACCESS = 0x13,
+ ARMV8_PMUV3_PERFCTR_L1_ICACHE_ACCESS = 0x14,
+ ARMV8_PMUV3_PERFCTR_L1_DCACHE_WB = 0x15,
+ ARMV8_PMUV3_PERFCTR_L2_CACHE_ACCESS = 0x16,
+ ARMV8_PMUV3_PERFCTR_L2_CACHE_REFILL = 0x17,
+ ARMV8_PMUV3_PERFCTR_L2_CACHE_WB = 0x18,
+ ARMV8_PMUV3_PERFCTR_BUS_ACCESS = 0x19,
+ ARMV8_PMUV3_PERFCTR_MEM_ERROR = 0x1A,
+ ARMV8_PMUV3_PERFCTR_BUS_CYCLES = 0x1D,
+
+ /*
+ * This isn't an architected event.
+ * We detect this event number and use the cycle counter instead.
+ */
+ ARMV8_PMUV3_PERFCTR_CPU_CYCLES = 0xFF,
+};
+
+/* PMUv3 HW events mapping. */
+static const unsigned armv8_pmuv3_perf_map[PERF_COUNT_HW_MAX] = {
+ [PERF_COUNT_HW_CPU_CYCLES] = ARMV8_PMUV3_PERFCTR_CPU_CYCLES,
+ [PERF_COUNT_HW_INSTRUCTIONS] = ARMV8_PMUV3_PERFCTR_INSTR_EXECUTED,
+ [PERF_COUNT_HW_CACHE_REFERENCES] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS,
+ [PERF_COUNT_HW_CACHE_MISSES] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL,
+ [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = HW_OP_UNSUPPORTED,
+ [PERF_COUNT_HW_BRANCH_MISSES] = ARMV8_PMUV3_PERFCTR_PC_BRANCH_MIS_PRED,
+ [PERF_COUNT_HW_BUS_CYCLES] = HW_OP_UNSUPPORTED,
+ [PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = HW_OP_UNSUPPORTED,
+ [PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = HW_OP_UNSUPPORTED,
+};
+
+static const unsigned armv8_pmuv3_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)] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS,
+ [C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_REFILL,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_L1_DCACHE_ACCESS,
+ [C(RESULT_MISS)] = ARMV8_PMUV3_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)] = 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(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)] = 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(ITLB)] = {
+ [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(BPU)] = {
+ [C(OP_READ)] = {
+ [C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_PC_BRANCH_PRED,
+ [C(RESULT_MISS)] = ARMV8_PMUV3_PERFCTR_PC_BRANCH_MIS_PRED,
+ },
+ [C(OP_WRITE)] = {
+ [C(RESULT_ACCESS)] = ARMV8_PMUV3_PERFCTR_PC_BRANCH_PRED,
+ [C(RESULT_MISS)] = ARMV8_PMUV3_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 ARMV8_IDX_CYCLE_COUNTER 0
+#define ARMV8_IDX_COUNTER0 1
+#define ARMV8_IDX_COUNTER_LAST (ARMV8_IDX_CYCLE_COUNTER + cpu_pmu->num_events - 1)
+
+#define ARMV8_MAX_COUNTERS 32
+#define ARMV8_COUNTER_MASK (ARMV8_MAX_COUNTERS - 1)
+
+/*
+ * ARMv8 low level PMU access
+ */
+
+/*
+ * Perf Event to low level counters mapping
+ */
+#define ARMV8_IDX_TO_COUNTER(x) \
+ (((x) - ARMV8_IDX_COUNTER0) & ARMV8_COUNTER_MASK)
+
+/*
+ * Per-CPU PMCR: config reg
+ */
+#define ARMV8_PMCR_E (1 << 0) /* Enable all counters */
+#define ARMV8_PMCR_P (1 << 1) /* Reset all counters */
+#define ARMV8_PMCR_C (1 << 2) /* Cycle counter reset */
+#define ARMV8_PMCR_D (1 << 3) /* CCNT counts every 64th cpu cycle */
+#define ARMV8_PMCR_X (1 << 4) /* Export to ETM */
+#define ARMV8_PMCR_DP (1 << 5) /* Disable CCNT if non-invasive debug*/
+#define ARMV8_PMCR_N_SHIFT 11 /* Number of counters supported */
+#define ARMV8_PMCR_N_MASK 0x1f
+#define ARMV8_PMCR_MASK 0x3f /* Mask for writable bits */
+
+/*
+ * PMOVSR: counters overflow flag status reg
+ */
+#define ARMV8_OVSR_MASK 0xffffffff /* Mask for writable bits */
+#define ARMV8_OVERFLOWED_MASK ARMV8_OVSR_MASK
+
+/*
+ * PMXEVTYPER: Event selection reg
+ */
+#define ARMV8_EVTYPE_MASK 0xc00000ff /* Mask for writable bits */
+#define ARMV8_EVTYPE_EVENT 0xff /* Mask for EVENT bits */
+
+/*
+ * Event filters for PMUv3
+ */
+#define ARMV8_EXCLUDE_EL1 (1 << 31)
+#define ARMV8_EXCLUDE_EL0 (1 << 30)
+#define ARMV8_INCLUDE_EL2 (1 << 27)
+
+static inline u32 armv8pmu_pmcr_read(void)
+{
+ u32 val;
+ asm volatile("mrs %0, pmcr_el0" : "=r" (val));
+ return val;
+}
+
+static inline void armv8pmu_pmcr_write(u32 val)
+{
+ val &= ARMV8_PMCR_MASK;
+ isb();
+ asm volatile("msr pmcr_el0, %0" :: "r" (val));
+}
+
+static inline int armv8pmu_has_overflowed(u32 pmovsr)
+{
+ return pmovsr & ARMV8_OVERFLOWED_MASK;
+}
+
+static inline int armv8pmu_counter_valid(int idx)
+{
+ return idx >= ARMV8_IDX_CYCLE_COUNTER && idx <= ARMV8_IDX_COUNTER_LAST;
+}
+
+static inline int armv8pmu_counter_has_overflowed(u32 pmnc, int idx)
+{
+ int ret = 0;
+ u32 counter;
+
+ if (!armv8pmu_counter_valid(idx)) {
+ pr_err("CPU%u checking wrong counter %d overflow status\n",
+ smp_processor_id(), idx);
+ } else {
+ counter = ARMV8_IDX_TO_COUNTER(idx);
+ ret = pmnc & BIT(counter);
+ }
+
+ return ret;
+}
+
+static inline int armv8pmu_select_counter(int idx)
+{
+ u32 counter;
+
+ if (!armv8pmu_counter_valid(idx)) {
+ pr_err("CPU%u selecting wrong PMNC counter %d\n",
+ smp_processor_id(), idx);
+ return -EINVAL;
+ }
+
+ counter = ARMV8_IDX_TO_COUNTER(idx);
+ asm volatile("msr pmselr_el0, %0" :: "r" (counter));
+ isb();
+
+ return idx;
+}
+
+static inline u32 armv8pmu_read_counter(int idx)
+{
+ u32 value = 0;
+
+ if (!armv8pmu_counter_valid(idx))
+ pr_err("CPU%u reading wrong counter %d\n",
+ smp_processor_id(), idx);
+ else if (idx == ARMV8_IDX_CYCLE_COUNTER)
+ asm volatile("mrs %0, pmccntr_el0" : "=r" (value));
+ else if (armv8pmu_select_counter(idx) == idx)
+ asm volatile("mrs %0, pmxevcntr_el0" : "=r" (value));
+
+ return value;
+}
+
+static inline void armv8pmu_write_counter(int idx, u32 value)
+{
+ if (!armv8pmu_counter_valid(idx))
+ pr_err("CPU%u writing wrong counter %d\n",
+ smp_processor_id(), idx);
+ else if (idx == ARMV8_IDX_CYCLE_COUNTER)
+ asm volatile("msr pmccntr_el0, %0" :: "r" (value));
+ else if (armv8pmu_select_counter(idx) == idx)
+ asm volatile("msr pmxevcntr_el0, %0" :: "r" (value));
+}
+
+static inline void armv8pmu_write_evtype(int idx, u32 val)
+{
+ if (armv8pmu_select_counter(idx) == idx) {
+ val &= ARMV8_EVTYPE_MASK;
+ asm volatile("msr pmxevtyper_el0, %0" :: "r" (val));
+ }
+}
+
+static inline int armv8pmu_enable_counter(int idx)
+{
+ u32 counter;
+
+ if (!armv8pmu_counter_valid(idx)) {
+ pr_err("CPU%u enabling wrong PMNC counter %d\n",
+ smp_processor_id(), idx);
+ return -EINVAL;
+ }
+
+ counter = ARMV8_IDX_TO_COUNTER(idx);
+ asm volatile("msr pmcntenset_el0, %0" :: "r" (BIT(counter)));
+ return idx;
+}
+
+static inline int armv8pmu_disable_counter(int idx)
+{
+ u32 counter;
+
+ if (!armv8pmu_counter_valid(idx)) {
+ pr_err("CPU%u disabling wrong PMNC counter %d\n",
+ smp_processor_id(), idx);
+ return -EINVAL;
+ }
+
+ counter = ARMV8_IDX_TO_COUNTER(idx);
+ asm volatile("msr pmcntenclr_el0, %0" :: "r" (BIT(counter)));
+ return idx;
+}
+
+static inline int armv8pmu_enable_intens(int idx)
+{
+ u32 counter;
+
+ if (!armv8pmu_counter_valid(idx)) {
+ pr_err("CPU%u enabling wrong PMNC counter IRQ enable %d\n",
+ smp_processor_id(), idx);
+ return -EINVAL;
+ }
+
+ counter = ARMV8_IDX_TO_COUNTER(idx);
+ asm volatile("msr pmintenset_el1, %0" :: "r" (BIT(counter)));
+ return idx;
+}
+
+static inline int armv8pmu_disable_intens(int idx)
+{
+ u32 counter;
+
+ if (!armv8pmu_counter_valid(idx)) {
+ pr_err("CPU%u disabling wrong PMNC counter IRQ enable %d\n",
+ smp_processor_id(), idx);
+ return -EINVAL;
+ }
+
+ counter = ARMV8_IDX_TO_COUNTER(idx);
+ asm volatile("msr pmintenclr_el1, %0" :: "r" (BIT(counter)));
+ isb();
+ /* Clear the overflow flag in case an interrupt is pending. */
+ asm volatile("msr pmovsclr_el0, %0" :: "r" (BIT(counter)));
+ isb();
+ return idx;
+}
+
+static inline u32 armv8pmu_getreset_flags(void)
+{
+ u32 value;
+
+ /* Read */
+ asm volatile("mrs %0, pmovsclr_el0" : "=r" (value));
+
+ /* Write to clear flags */
+ value &= ARMV8_OVSR_MASK;
+ asm volatile("msr pmovsclr_el0, %0" :: "r" (value));
+
+ return value;
+}
+
+static void armv8pmu_enable_event(struct hw_perf_event *hwc, int idx)
+{
+ unsigned long flags;
+ 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
+ */
+ armv8pmu_disable_counter(idx);
+
+ /*
+ * Set event (if destined for PMNx counters).
+ */
+ armv8pmu_write_evtype(idx, hwc->config_base);
+
+ /*
+ * Enable interrupt for this counter
+ */
+ armv8pmu_enable_intens(idx);
+
+ /*
+ * Enable counter
+ */
+ armv8pmu_enable_counter(idx);
+
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static void armv8pmu_disable_event(struct hw_perf_event *hwc, int idx)
+{
+ unsigned long flags;
+ struct pmu_hw_events *events = cpu_pmu->get_hw_events();
+
+ /*
+ * Disable counter and interrupt
+ */
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+
+ /*
+ * Disable counter
+ */
+ armv8pmu_disable_counter(idx);
+
+ /*
+ * Disable interrupt for this counter
+ */
+ armv8pmu_disable_intens(idx);
+
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static irqreturn_t armv8pmu_handle_irq(int irq_num, void *dev)
+{
+ u32 pmovsr;
+ struct perf_sample_data data;
+ struct pmu_hw_events *cpuc;
+ struct pt_regs *regs;
+ int idx;
+
+ /*
+ * Get and reset the IRQ flags
+ */
+ pmovsr = armv8pmu_getreset_flags();
+
+ /*
+ * Did an overflow occur?
+ */
+ if (!armv8pmu_has_overflowed(pmovsr))
+ return IRQ_NONE;
+
+ /*
+ * Handle the counter(s) overflow(s)
+ */
+ regs = get_irq_regs();
+
+ cpuc = &__get_cpu_var(cpu_hw_events);
+ for (idx = 0; idx < cpu_pmu->num_events; ++idx) {
+ struct perf_event *event = cpuc->events[idx];
+ struct hw_perf_event *hwc;
+
+ /* Ignore if we don't have an event. */
+ if (!event)
+ continue;
+
+ /*
+ * We have a single interrupt for all counters. Check that
+ * each counter has overflowed before we process it.
+ */
+ if (!armv8pmu_counter_has_overflowed(pmovsr, idx))
+ continue;
+
+ hwc = &event->hw;
+ armpmu_event_update(event, hwc, idx);
+ perf_sample_data_init(&data, 0, hwc->last_period);
+ if (!armpmu_event_set_period(event, hwc, idx))
+ continue;
+
+ if (perf_event_overflow(event, &data, regs))
+ cpu_pmu->disable(hwc, idx);
+ }
+
+ /*
+ * Handle the pending perf events.
+ *
+ * Note: this call *must* be run with interrupts disabled. For
+ * platforms that can have the PMU interrupts raised as an NMI, this
+ * will not work.
+ */
+ irq_work_run();
+
+ return IRQ_HANDLED;
+}
+
+static void armv8pmu_start(void)
+{
+ unsigned long flags;
+ struct pmu_hw_events *events = cpu_pmu->get_hw_events();
+
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+ /* Enable all counters */
+ armv8pmu_pmcr_write(armv8pmu_pmcr_read() | ARMV8_PMCR_E);
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static void armv8pmu_stop(void)
+{
+ unsigned long flags;
+ struct pmu_hw_events *events = cpu_pmu->get_hw_events();
+
+ raw_spin_lock_irqsave(&events->pmu_lock, flags);
+ /* Disable all counters */
+ armv8pmu_pmcr_write(armv8pmu_pmcr_read() & ~ARMV8_PMCR_E);
+ raw_spin_unlock_irqrestore(&events->pmu_lock, flags);
+}
+
+static int armv8pmu_get_event_idx(struct pmu_hw_events *cpuc,
+ struct hw_perf_event *event)
+{
+ int idx;
+ unsigned long evtype = event->config_base & ARMV8_EVTYPE_EVENT;
+
+ /* Always place a cycle counter into the cycle counter. */
+ if (evtype == ARMV8_PMUV3_PERFCTR_CPU_CYCLES) {
+ if (test_and_set_bit(ARMV8_IDX_CYCLE_COUNTER, cpuc->used_mask))
+ return -EAGAIN;
+
+ return ARMV8_IDX_CYCLE_COUNTER;
+ }
+
+ /*
+ * For anything other than a cycle counter, try and use
+ * the events counters
+ */
+ for (idx = ARMV8_IDX_COUNTER0; idx < cpu_pmu->num_events; ++idx) {
+ if (!test_and_set_bit(idx, cpuc->used_mask))
+ return idx;
+ }
+
+ /* The counters are all in use. */
+ return -EAGAIN;
+}
+
+/*
+ * Add an event filter to a given event. This will only work for PMUv2 PMUs.
+ */
+static int armv8pmu_set_event_filter(struct hw_perf_event *event,
+ struct perf_event_attr *attr)
+{
+ unsigned long config_base = 0;
+
+ if (attr->exclude_idle)
+ return -EPERM;
+ if (attr->exclude_user)
+ config_base |= ARMV8_EXCLUDE_EL0;
+ if (attr->exclude_kernel)
+ config_base |= ARMV8_EXCLUDE_EL1;
+ if (!attr->exclude_hv)
+ config_base |= ARMV8_INCLUDE_EL2;
+
+ /*
+ * Install the filter into config_base as this is used to
+ * construct the event type.
+ */
+ event->config_base = config_base;
+
+ return 0;
+}
+
+static void armv8pmu_reset(void *info)
+{
+ u32 idx, nb_cnt = cpu_pmu->num_events;
+
+ /* The counter and interrupt enable registers are unknown at reset. */
+ for (idx = ARMV8_IDX_CYCLE_COUNTER; idx < nb_cnt; ++idx)
+ armv8pmu_disable_event(NULL, idx);
+
+ /* Initialize & Reset PMNC: C and P bits. */
+ armv8pmu_pmcr_write(ARMV8_PMCR_P | ARMV8_PMCR_C);
+
+ /* Disable access from userspace. */
+ asm volatile("msr pmuserenr_el0, %0" :: "r" (0));
+}
+
+static int armv8_pmuv3_map_event(struct perf_event *event)
+{
+ return map_cpu_event(event, &armv8_pmuv3_perf_map,
+ &armv8_pmuv3_perf_cache_map, 0xFF);
+}
+
+static struct arm_pmu armv8pmu = {
+ .handle_irq = armv8pmu_handle_irq,
+ .enable = armv8pmu_enable_event,
+ .disable = armv8pmu_disable_event,
+ .read_counter = armv8pmu_read_counter,
+ .write_counter = armv8pmu_write_counter,
+ .get_event_idx = armv8pmu_get_event_idx,
+ .start = armv8pmu_start,
+ .stop = armv8pmu_stop,
+ .reset = armv8pmu_reset,
+ .max_period = (1LLU << 32) - 1,
+};
+
+static u32 __init armv8pmu_read_num_pmnc_events(void)
+{
+ u32 nb_cnt;
+
+ /* Read the nb of CNTx counters supported from PMNC */
+ nb_cnt = (armv8pmu_pmcr_read() >> ARMV8_PMCR_N_SHIFT) & ARMV8_PMCR_N_MASK;
+
+ /* Add the CPU cycles counter and return */
+ return nb_cnt + 1;
+}
+
+static struct arm_pmu *__init armv8_pmuv3_pmu_init(void)
+{
+ armv8pmu.name = "arm/armv8-pmuv3";
+ armv8pmu.map_event = armv8_pmuv3_map_event;
+ armv8pmu.num_events = armv8pmu_read_num_pmnc_events();
+ armv8pmu.set_event_filter = armv8pmu_set_event_filter;
+ return &armv8pmu;
+}
+
+/*
+ * Ensure the PMU has sane values out of reset.
+ * This requires SMP to be available, so exists as a separate initcall.
+ */
+static int __init
+cpu_pmu_reset(void)
+{
+ if (cpu_pmu && cpu_pmu->reset)
+ return on_each_cpu(cpu_pmu->reset, NULL, 1);
+ return 0;
+}
+arch_initcall(cpu_pmu_reset);
+
+/*
+ * PMU platform driver and devicetree bindings.
+ */
+static struct of_device_id armpmu_of_device_ids[] = {
+ {.compatible = "arm,armv8-pmuv3"},
+ {},
+};
+
+static int __devinit armpmu_device_probe(struct platform_device *pdev)
+{
+ if (!cpu_pmu)
+ return -ENODEV;
+
+ cpu_pmu->plat_device = pdev;
+ return 0;
+}
+
+static struct platform_driver armpmu_driver = {
+ .driver = {
+ .name = "arm-pmu",
+ .of_match_table = armpmu_of_device_ids,
+ },
+ .probe = armpmu_device_probe,
+};
+
+static int __init register_pmu_driver(void)
+{
+ return platform_driver_register(&armpmu_driver);
+}
+device_initcall(register_pmu_driver);
+
+static struct pmu_hw_events *armpmu_get_cpu_events(void)
+{
+ return &__get_cpu_var(cpu_hw_events);
+}
+
+static void __init cpu_pmu_init(struct arm_pmu *armpmu)
+{
+ int cpu;
+ for_each_possible_cpu(cpu) {
+ struct pmu_hw_events *events = &per_cpu(cpu_hw_events, cpu);
+ events->events = per_cpu(hw_events, cpu);
+ events->used_mask = per_cpu(used_mask, cpu);
+ raw_spin_lock_init(&events->pmu_lock);
+ }
+ armpmu->get_hw_events = armpmu_get_cpu_events;
+}
+
+static int __init init_hw_perf_events(void)
+{
+ u64 dfr = read_cpuid(ID_AA64DFR0_EL1);
+
+ switch ((dfr >> 8) & 0xf) {
+ case 0x1: /* PMUv3 */
+ cpu_pmu = armv8_pmuv3_pmu_init();
+ break;
+ }
+
+ if (cpu_pmu) {
+ pr_info("enabled with %s PMU driver, %d counters available\n",
+ cpu_pmu->name, cpu_pmu->num_events);
+ cpu_pmu_init(cpu_pmu);
+ armpmu_register(cpu_pmu, "cpu", PERF_TYPE_RAW);
+ } else {
+ pr_info("no hardware support available\n");
+ }
+
+ return 0;
+}
+early_initcall(init_hw_perf_events);
+
+/*
+ * Callchain handling code.
+ */
+struct frame_tail {
+ struct frame_tail __user *fp;
+ unsigned long lr;
+} __attribute__((packed));
+
+/*
+ * Get the return address for a single stackframe and return a pointer to the
+ * next frame tail.
+ */
+static struct frame_tail __user *
+user_backtrace(struct frame_tail __user *tail,
+ struct perf_callchain_entry *entry)
+{
+ struct frame_tail buftail;
+ unsigned long err;
+
+ /* Also check accessibility of one struct frame_tail beyond */
+ if (!access_ok(VERIFY_READ, tail, sizeof(buftail)))
+ return NULL;
+
+ pagefault_disable();
+ err = __copy_from_user_inatomic(&buftail, tail, sizeof(buftail));
+ pagefault_enable();
+
+ if (err)
+ return NULL;
+
+ perf_callchain_store(entry, buftail.lr);
+
+ /*
+ * Frame pointers should strictly progress back up the stack
+ * (towards higher addresses).
+ */
+ if (tail >= buftail.fp)
+ return NULL;
+
+ return buftail.fp;
+}
+
+void perf_callchain_user(struct perf_callchain_entry *entry,
+ struct pt_regs *regs)
+{
+ struct frame_tail __user *tail;
+
+ tail = (struct frame_tail __user *)regs->regs[29];
+
+ while (entry->nr < PERF_MAX_STACK_DEPTH &&
+ tail && !((unsigned long)tail & 0xf))
+ tail = user_backtrace(tail, entry);
+}
+
+/*
+ * Gets called by walk_stackframe() for every stackframe. This will be called
+ * whist unwinding the stackframe and is like a subroutine return so we use
+ * the PC.
+ */
+static int callchain_trace(struct stackframe *frame, void *data)
+{
+ struct perf_callchain_entry *entry = data;
+ perf_callchain_store(entry, frame->pc);
+ return 0;
+}
+
+void perf_callchain_kernel(struct perf_callchain_entry *entry,
+ struct pt_regs *regs)
+{
+ struct stackframe frame;
+
+ frame.fp = regs->regs[29];
+ frame.sp = regs->sp;
+ frame.pc = regs->pc;
+ walk_stackframe(&frame, callchain_trace, entry);
+}
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
new file mode 100644
index 000000000000..f22965ea1cfc
--- /dev/null
+++ b/arch/arm64/kernel/process.c
@@ -0,0 +1,408 @@
+/*
+ * Based on arch/arm/kernel/process.c
+ *
+ * Original Copyright (C) 1995 Linus Torvalds
+ * Copyright (C) 1996-2000 Russell King - Converted to ARM.
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdarg.h>
+
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/user.h>
+#include <linux/delay.h>
+#include <linux/reboot.h>
+#include <linux/interrupt.h>
+#include <linux/kallsyms.h>
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/elfcore.h>
+#include <linux/pm.h>
+#include <linux/tick.h>
+#include <linux/utsname.h>
+#include <linux/uaccess.h>
+#include <linux/random.h>
+#include <linux/hw_breakpoint.h>
+#include <linux/personality.h>
+#include <linux/notifier.h>
+
+#include <asm/compat.h>
+#include <asm/cacheflush.h>
+#include <asm/processor.h>
+#include <asm/stacktrace.h>
+#include <asm/fpsimd.h>
+
+static void setup_restart(void)
+{
+ /*
+ * Tell the mm system that we are going to reboot -
+ * we may need it to insert some 1:1 mappings so that
+ * soft boot works.
+ */
+ setup_mm_for_reboot();
+
+ /* Clean and invalidate caches */
+ flush_cache_all();
+
+ /* Turn D-cache off */
+ cpu_cache_off();
+
+ /* Push out any further dirty data, and ensure cache is empty */
+ flush_cache_all();
+}
+
+void soft_restart(unsigned long addr)
+{
+ setup_restart();
+ cpu_reset(addr);
+}
+
+/*
+ * Function pointers to optional machine specific functions
+ */
+void (*pm_power_off)(void);
+EXPORT_SYMBOL_GPL(pm_power_off);
+
+void (*pm_restart)(const char *cmd);
+EXPORT_SYMBOL_GPL(pm_restart);
+
+
+/*
+ * This is our default idle handler.
+ */
+static void default_idle(void)
+{
+ /*
+ * This should do all the clock switching and wait for interrupt
+ * tricks
+ */
+ cpu_do_idle();
+ local_irq_enable();
+}
+
+void (*pm_idle)(void) = default_idle;
+EXPORT_SYMBOL_GPL(pm_idle);
+
+/*
+ * The idle thread, has rather strange semantics for calling pm_idle,
+ * but this is what x86 does and we need to do the same, so that
+ * things like cpuidle get called in the same way. The only difference
+ * is that we always respect 'hlt_counter' to prevent low power idle.
+ */
+void cpu_idle(void)
+{
+ local_fiq_enable();
+
+ /* endless idle loop with no priority at all */
+ while (1) {
+ tick_nohz_idle_enter();
+ rcu_idle_enter();
+ while (!need_resched()) {
+ /*
+ * We need to disable interrupts here to ensure
+ * we don't miss a wakeup call.
+ */
+ local_irq_disable();
+ if (!need_resched()) {
+ stop_critical_timings();
+ pm_idle();
+ start_critical_timings();
+ /*
+ * pm_idle functions should always return
+ * with IRQs enabled.
+ */
+ WARN_ON(irqs_disabled());
+ } else {
+ local_irq_enable();
+ }
+ }
+ rcu_idle_exit();
+ tick_nohz_idle_exit();
+ schedule_preempt_disabled();
+ }
+}
+
+void machine_shutdown(void)
+{
+#ifdef CONFIG_SMP
+ smp_send_stop();
+#endif
+}
+
+void machine_halt(void)
+{
+ machine_shutdown();
+ while (1);
+}
+
+void machine_power_off(void)
+{
+ machine_shutdown();
+ if (pm_power_off)
+ pm_power_off();
+}
+
+void machine_restart(char *cmd)
+{
+ machine_shutdown();
+
+ /* Disable interrupts first */
+ local_irq_disable();
+ local_fiq_disable();
+
+ /* Now call the architecture specific reboot code. */
+ if (pm_restart)
+ pm_restart(cmd);
+
+ /*
+ * Whoops - the architecture was unable to reboot.
+ */
+ printk("Reboot failed -- System halted\n");
+ while (1);
+}
+
+void __show_regs(struct pt_regs *regs)
+{
+ int i;
+
+ printk("CPU: %d %s (%s %.*s)\n",
+ raw_smp_processor_id(), print_tainted(),
+ init_utsname()->release,
+ (int)strcspn(init_utsname()->version, " "),
+ init_utsname()->version);
+ print_symbol("PC is at %s\n", instruction_pointer(regs));
+ print_symbol("LR is at %s\n", regs->regs[30]);
+ printk("pc : [<%016llx>] lr : [<%016llx>] pstate: %08llx\n",
+ regs->pc, regs->regs[30], regs->pstate);
+ printk("sp : %016llx\n", regs->sp);
+ for (i = 29; i >= 0; i--) {
+ printk("x%-2d: %016llx ", i, regs->regs[i]);
+ if (i % 2 == 0)
+ printk("\n");
+ }
+ printk("\n");
+}
+
+void show_regs(struct pt_regs * regs)
+{
+ printk("\n");
+ printk("Pid: %d, comm: %20s\n", task_pid_nr(current), current->comm);
+ __show_regs(regs);
+}
+
+/*
+ * Free current thread data structures etc..
+ */
+void exit_thread(void)
+{
+}
+
+void flush_thread(void)
+{
+ fpsimd_flush_thread();
+ flush_ptrace_hw_breakpoint(current);
+}
+
+void release_thread(struct task_struct *dead_task)
+{
+}
+
+int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
+{
+ fpsimd_save_state(&current->thread.fpsimd_state);
+ *dst = *src;
+ return 0;
+}
+
+asmlinkage void ret_from_fork(void) asm("ret_from_fork");
+
+int copy_thread(unsigned long clone_flags, unsigned long stack_start,
+ unsigned long stk_sz, struct task_struct *p,
+ struct pt_regs *regs)
+{
+ struct pt_regs *childregs = task_pt_regs(p);
+ unsigned long tls = p->thread.tp_value;
+
+ *childregs = *regs;
+ childregs->regs[0] = 0;
+
+ if (is_compat_thread(task_thread_info(p)))
+ childregs->compat_sp = stack_start;
+ else {
+ /*
+ * Read the current TLS pointer from tpidr_el0 as it may be
+ * out-of-sync with the saved value.
+ */
+ asm("mrs %0, tpidr_el0" : "=r" (tls));
+ childregs->sp = stack_start;
+ }
+
+ memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context));
+ p->thread.cpu_context.sp = (unsigned long)childregs;
+ p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
+
+ /* If a TLS pointer was passed to clone, use that for the new thread. */
+ if (clone_flags & CLONE_SETTLS)
+ tls = regs->regs[3];
+ p->thread.tp_value = tls;
+
+ ptrace_hw_copy_thread(p);
+
+ return 0;
+}
+
+static void tls_thread_switch(struct task_struct *next)
+{
+ unsigned long tpidr, tpidrro;
+
+ if (!is_compat_task()) {
+ asm("mrs %0, tpidr_el0" : "=r" (tpidr));
+ current->thread.tp_value = tpidr;
+ }
+
+ if (is_compat_thread(task_thread_info(next))) {
+ tpidr = 0;
+ tpidrro = next->thread.tp_value;
+ } else {
+ tpidr = next->thread.tp_value;
+ tpidrro = 0;
+ }
+
+ asm(
+ " msr tpidr_el0, %0\n"
+ " msr tpidrro_el0, %1"
+ : : "r" (tpidr), "r" (tpidrro));
+}
+
+/*
+ * Thread switching.
+ */
+struct task_struct *__switch_to(struct task_struct *prev,
+ struct task_struct *next)
+{
+ struct task_struct *last;
+
+ fpsimd_thread_switch(next);
+ tls_thread_switch(next);
+ hw_breakpoint_thread_switch(next);
+
+ /* the actual thread switch */
+ last = cpu_switch_to(prev, next);
+
+ return last;
+}
+
+/*
+ * Fill in the task's elfregs structure for a core dump.
+ */
+int dump_task_regs(struct task_struct *t, elf_gregset_t *elfregs)
+{
+ elf_core_copy_regs(elfregs, task_pt_regs(t));
+ return 1;
+}
+
+/*
+ * fill in the fpe structure for a core dump...
+ */
+int dump_fpu (struct pt_regs *regs, struct user_fp *fp)
+{
+ return 0;
+}
+EXPORT_SYMBOL(dump_fpu);
+
+/*
+ * Shuffle the argument into the correct register before calling the
+ * thread function. x1 is the thread argument, x2 is the pointer to
+ * the thread function, and x3 points to the exit function.
+ */
+extern void kernel_thread_helper(void);
+asm( ".section .text\n"
+" .align\n"
+" .type kernel_thread_helper, #function\n"
+"kernel_thread_helper:\n"
+" mov x0, x1\n"
+" mov x30, x3\n"
+" br x2\n"
+" .size kernel_thread_helper, . - kernel_thread_helper\n"
+" .previous");
+
+#define kernel_thread_exit do_exit
+
+/*
+ * Create a kernel thread.
+ */
+pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
+{
+ struct pt_regs regs;
+
+ memset(&regs, 0, sizeof(regs));
+
+ regs.regs[1] = (unsigned long)arg;
+ regs.regs[2] = (unsigned long)fn;
+ regs.regs[3] = (unsigned long)kernel_thread_exit;
+ regs.pc = (unsigned long)kernel_thread_helper;
+ regs.pstate = PSR_MODE_EL1h;
+
+ return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
+}
+EXPORT_SYMBOL(kernel_thread);
+
+unsigned long get_wchan(struct task_struct *p)
+{
+ struct stackframe frame;
+ int count = 0;
+ if (!p || p == current || p->state == TASK_RUNNING)
+ return 0;
+
+ frame.fp = thread_saved_fp(p);
+ frame.sp = thread_saved_sp(p);
+ frame.pc = thread_saved_pc(p);
+ do {
+ int ret = unwind_frame(&frame);
+ if (ret < 0)
+ return 0;
+ if (!in_sched_functions(frame.pc))
+ return frame.pc;
+ } while (count ++ < 16);
+ return 0;
+}
+
+unsigned long arch_align_stack(unsigned long sp)
+{
+ if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
+ sp -= get_random_int() & ~PAGE_MASK;
+ return sp & ~0xf;
+}
+
+static unsigned long randomize_base(unsigned long base)
+{
+ unsigned long range_end = base + (STACK_RND_MASK << PAGE_SHIFT) + 1;
+ return randomize_range(base, range_end, 0) ? : base;
+}
+
+unsigned long arch_randomize_brk(struct mm_struct *mm)
+{
+ return randomize_base(mm->brk);
+}
+
+unsigned long randomize_et_dyn(unsigned long base)
+{
+ return randomize_base(base);
+}
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
new file mode 100644
index 000000000000..ac3550ecc7b5
--- /dev/null
+++ b/arch/arm64/kernel/ptrace.c
@@ -0,0 +1,1126 @@
+/*
+ * Based on arch/arm/kernel/ptrace.c
+ *
+ * By Ross Biro 1/23/92
+ * edited by Linus Torvalds
+ * ARM modifications Copyright (C) 2000 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/ptrace.h>
+#include <linux/user.h>
+#include <linux/security.h>
+#include <linux/init.h>
+#include <linux/signal.h>
+#include <linux/uaccess.h>
+#include <linux/perf_event.h>
+#include <linux/hw_breakpoint.h>
+#include <linux/regset.h>
+#include <linux/tracehook.h>
+#include <linux/elf.h>
+
+#include <asm/compat.h>
+#include <asm/debug-monitors.h>
+#include <asm/pgtable.h>
+#include <asm/traps.h>
+#include <asm/system_misc.h>
+
+/*
+ * TODO: does not yet catch signals sent when the child dies.
+ * in exit.c or in signal.c.
+ */
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ */
+void ptrace_disable(struct task_struct *child)
+{
+}
+
+/*
+ * Handle hitting a breakpoint.
+ */
+static int ptrace_break(struct pt_regs *regs)
+{
+ siginfo_t info = {
+ .si_signo = SIGTRAP,
+ .si_errno = 0,
+ .si_code = TRAP_BRKPT,
+ .si_addr = (void __user *)instruction_pointer(regs),
+ };
+
+ force_sig_info(SIGTRAP, &info, current);
+ return 0;
+}
+
+static int arm64_break_trap(unsigned long addr, unsigned int esr,
+ struct pt_regs *regs)
+{
+ return ptrace_break(regs);
+}
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+/*
+ * Handle hitting a HW-breakpoint.
+ */
+static void ptrace_hbptriggered(struct perf_event *bp,
+ struct perf_sample_data *data,
+ struct pt_regs *regs)
+{
+ struct arch_hw_breakpoint *bkpt = counter_arch_bp(bp);
+ siginfo_t info = {
+ .si_signo = SIGTRAP,
+ .si_errno = 0,
+ .si_code = TRAP_HWBKPT,
+ .si_addr = (void __user *)(bkpt->trigger),
+ };
+
+#ifdef CONFIG_COMPAT
+ int i;
+
+ if (!is_compat_task())
+ goto send_sig;
+
+ for (i = 0; i < ARM_MAX_BRP; ++i) {
+ if (current->thread.debug.hbp_break[i] == bp) {
+ info.si_errno = (i << 1) + 1;
+ break;
+ }
+ }
+ for (i = ARM_MAX_BRP; i < ARM_MAX_HBP_SLOTS && !bp; ++i) {
+ if (current->thread.debug.hbp_watch[i] == bp) {
+ info.si_errno = -((i << 1) + 1);
+ break;
+ }
+ }
+
+send_sig:
+#endif
+ force_sig_info(SIGTRAP, &info, current);
+}
+
+/*
+ * Unregister breakpoints from this task and reset the pointers in
+ * the thread_struct.
+ */
+void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
+{
+ int i;
+ struct thread_struct *t = &tsk->thread;
+
+ for (i = 0; i < ARM_MAX_BRP; i++) {
+ if (t->debug.hbp_break[i]) {
+ unregister_hw_breakpoint(t->debug.hbp_break[i]);
+ t->debug.hbp_break[i] = NULL;
+ }
+ }
+
+ for (i = 0; i < ARM_MAX_WRP; i++) {
+ if (t->debug.hbp_watch[i]) {
+ unregister_hw_breakpoint(t->debug.hbp_watch[i]);
+ t->debug.hbp_watch[i] = NULL;
+ }
+ }
+}
+
+void ptrace_hw_copy_thread(struct task_struct *tsk)
+{
+ memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
+}
+
+static struct perf_event *ptrace_hbp_get_event(unsigned int note_type,
+ struct task_struct *tsk,
+ unsigned long idx)
+{
+ struct perf_event *bp = ERR_PTR(-EINVAL);
+
+ switch (note_type) {
+ case NT_ARM_HW_BREAK:
+ if (idx < ARM_MAX_BRP)
+ bp = tsk->thread.debug.hbp_break[idx];
+ break;
+ case NT_ARM_HW_WATCH:
+ if (idx < ARM_MAX_WRP)
+ bp = tsk->thread.debug.hbp_watch[idx];
+ break;
+ }
+
+ return bp;
+}
+
+static int ptrace_hbp_set_event(unsigned int note_type,
+ struct task_struct *tsk,
+ unsigned long idx,
+ struct perf_event *bp)
+{
+ int err = -EINVAL;
+
+ switch (note_type) {
+ case NT_ARM_HW_BREAK:
+ if (idx < ARM_MAX_BRP) {
+ tsk->thread.debug.hbp_break[idx] = bp;
+ err = 0;
+ }
+ break;
+ case NT_ARM_HW_WATCH:
+ if (idx < ARM_MAX_WRP) {
+ tsk->thread.debug.hbp_watch[idx] = bp;
+ err = 0;
+ }
+ break;
+ }
+
+ return err;
+}
+
+static struct perf_event *ptrace_hbp_create(unsigned int note_type,
+ struct task_struct *tsk,
+ unsigned long idx)
+{
+ struct perf_event *bp;
+ struct perf_event_attr attr;
+ int err, type;
+
+ switch (note_type) {
+ case NT_ARM_HW_BREAK:
+ type = HW_BREAKPOINT_X;
+ break;
+ case NT_ARM_HW_WATCH:
+ type = HW_BREAKPOINT_RW;
+ break;
+ default:
+ return ERR_PTR(-EINVAL);
+ }
+
+ ptrace_breakpoint_init(&attr);
+
+ /*
+ * Initialise fields to sane defaults
+ * (i.e. values that will pass validation).
+ */
+ attr.bp_addr = 0;
+ attr.bp_len = HW_BREAKPOINT_LEN_4;
+ attr.bp_type = type;
+ attr.disabled = 1;
+
+ bp = register_user_hw_breakpoint(&attr, ptrace_hbptriggered, NULL, tsk);
+ if (IS_ERR(bp))
+ return bp;
+
+ err = ptrace_hbp_set_event(note_type, tsk, idx, bp);
+ if (err)
+ return ERR_PTR(err);
+
+ return bp;
+}
+
+static int ptrace_hbp_fill_attr_ctrl(unsigned int note_type,
+ struct arch_hw_breakpoint_ctrl ctrl,
+ struct perf_event_attr *attr)
+{
+ int err, len, type;
+
+ err = arch_bp_generic_fields(ctrl, &len, &type);
+ if (err)
+ return err;
+
+ switch (note_type) {
+ case NT_ARM_HW_BREAK:
+ if ((type & HW_BREAKPOINT_X) != type)
+ return -EINVAL;
+ break;
+ case NT_ARM_HW_WATCH:
+ if ((type & HW_BREAKPOINT_RW) != type)
+ return -EINVAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ attr->bp_len = len;
+ attr->bp_type = type;
+ attr->disabled = !ctrl.enabled;
+
+ return 0;
+}
+
+static int ptrace_hbp_get_resource_info(unsigned int note_type, u32 *info)
+{
+ u8 num;
+ u32 reg = 0;
+
+ switch (note_type) {
+ case NT_ARM_HW_BREAK:
+ num = hw_breakpoint_slots(TYPE_INST);
+ break;
+ case NT_ARM_HW_WATCH:
+ num = hw_breakpoint_slots(TYPE_DATA);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ reg |= debug_monitors_arch();
+ reg <<= 8;
+ reg |= num;
+
+ *info = reg;
+ return 0;
+}
+
+static int ptrace_hbp_get_ctrl(unsigned int note_type,
+ struct task_struct *tsk,
+ unsigned long idx,
+ u32 *ctrl)
+{
+ struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx);
+
+ if (IS_ERR(bp))
+ return PTR_ERR(bp);
+
+ *ctrl = bp ? encode_ctrl_reg(counter_arch_bp(bp)->ctrl) : 0;
+ return 0;
+}
+
+static int ptrace_hbp_get_addr(unsigned int note_type,
+ struct task_struct *tsk,
+ unsigned long idx,
+ u64 *addr)
+{
+ struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx);
+
+ if (IS_ERR(bp))
+ return PTR_ERR(bp);
+
+ *addr = bp ? bp->attr.bp_addr : 0;
+ return 0;
+}
+
+static struct perf_event *ptrace_hbp_get_initialised_bp(unsigned int note_type,
+ struct task_struct *tsk,
+ unsigned long idx)
+{
+ struct perf_event *bp = ptrace_hbp_get_event(note_type, tsk, idx);
+
+ if (!bp)
+ bp = ptrace_hbp_create(note_type, tsk, idx);
+
+ return bp;
+}
+
+static int ptrace_hbp_set_ctrl(unsigned int note_type,
+ struct task_struct *tsk,
+ unsigned long idx,
+ u32 uctrl)
+{
+ int err;
+ struct perf_event *bp;
+ struct perf_event_attr attr;
+ struct arch_hw_breakpoint_ctrl ctrl;
+
+ bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx);
+ if (IS_ERR(bp)) {
+ err = PTR_ERR(bp);
+ return err;
+ }
+
+ attr = bp->attr;
+ decode_ctrl_reg(uctrl, &ctrl);
+ err = ptrace_hbp_fill_attr_ctrl(note_type, ctrl, &attr);
+ if (err)
+ return err;
+
+ return modify_user_hw_breakpoint(bp, &attr);
+}
+
+static int ptrace_hbp_set_addr(unsigned int note_type,
+ struct task_struct *tsk,
+ unsigned long idx,
+ u64 addr)
+{
+ int err;
+ struct perf_event *bp;
+ struct perf_event_attr attr;
+
+ bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx);
+ if (IS_ERR(bp)) {
+ err = PTR_ERR(bp);
+ return err;
+ }
+
+ attr = bp->attr;
+ attr.bp_addr = addr;
+ err = modify_user_hw_breakpoint(bp, &attr);
+ return err;
+}
+
+#define PTRACE_HBP_ADDR_SZ sizeof(u64)
+#define PTRACE_HBP_CTRL_SZ sizeof(u32)
+#define PTRACE_HBP_REG_OFF sizeof(u32)
+
+static int hw_break_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ unsigned int note_type = regset->core_note_type;
+ int ret, idx = 0, offset = PTRACE_HBP_REG_OFF, limit;
+ u32 info, ctrl;
+ u64 addr;
+
+ /* Resource info */
+ ret = ptrace_hbp_get_resource_info(note_type, &info);
+ if (ret)
+ return ret;
+
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &info, 0, 4);
+ if (ret)
+ return ret;
+
+ /* (address, ctrl) registers */
+ limit = regset->n * regset->size;
+ while (count && offset < limit) {
+ ret = ptrace_hbp_get_addr(note_type, target, idx, &addr);
+ if (ret)
+ return ret;
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &addr,
+ offset, offset + PTRACE_HBP_ADDR_SZ);
+ if (ret)
+ return ret;
+ offset += PTRACE_HBP_ADDR_SZ;
+
+ ret = ptrace_hbp_get_ctrl(note_type, target, idx, &ctrl);
+ if (ret)
+ return ret;
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, &ctrl,
+ offset, offset + PTRACE_HBP_CTRL_SZ);
+ if (ret)
+ return ret;
+ offset += PTRACE_HBP_CTRL_SZ;
+ idx++;
+ }
+
+ return 0;
+}
+
+static int hw_break_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ unsigned int note_type = regset->core_note_type;
+ int ret, idx = 0, offset = PTRACE_HBP_REG_OFF, limit;
+ u32 ctrl;
+ u64 addr;
+
+ /* Resource info */
+ ret = user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 0, 4);
+ if (ret)
+ return ret;
+
+ /* (address, ctrl) registers */
+ limit = regset->n * regset->size;
+ while (count && offset < limit) {
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &addr,
+ offset, offset + PTRACE_HBP_ADDR_SZ);
+ if (ret)
+ return ret;
+ ret = ptrace_hbp_set_addr(note_type, target, idx, addr);
+ if (ret)
+ return ret;
+ offset += PTRACE_HBP_ADDR_SZ;
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &ctrl,
+ offset, offset + PTRACE_HBP_CTRL_SZ);
+ if (ret)
+ return ret;
+ ret = ptrace_hbp_set_ctrl(note_type, target, idx, ctrl);
+ if (ret)
+ return ret;
+ offset += PTRACE_HBP_CTRL_SZ;
+ idx++;
+ }
+
+ return 0;
+}
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
+
+static int gpr_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct user_pt_regs *uregs = &task_pt_regs(target)->user_regs;
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, -1);
+}
+
+static int gpr_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+ struct user_pt_regs newregs;
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newregs, 0, -1);
+ if (ret)
+ return ret;
+
+ if (!valid_user_regs(&newregs))
+ return -EINVAL;
+
+ task_pt_regs(target)->user_regs = newregs;
+ return 0;
+}
+
+/*
+ * TODO: update fp accessors for lazy context switching (sync/flush hwstate)
+ */
+static int fpr_get(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct user_fpsimd_state *uregs;
+ uregs = &target->thread.fpsimd_state.user_fpsimd;
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0, -1);
+}
+
+static int fpr_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+ struct user_fpsimd_state newstate;
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &newstate, 0, -1);
+ if (ret)
+ return ret;
+
+ target->thread.fpsimd_state.user_fpsimd = newstate;
+ return ret;
+}
+
+static int tls_get(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ unsigned long *tls = &target->thread.tp_value;
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, tls, 0, -1);
+}
+
+static int tls_set(struct task_struct *target, const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ int ret;
+ unsigned long tls;
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, &tls, 0, -1);
+ if (ret)
+ return ret;
+
+ target->thread.tp_value = tls;
+ return ret;
+}
+
+enum aarch64_regset {
+ REGSET_GPR,
+ REGSET_FPR,
+ REGSET_TLS,
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ REGSET_HW_BREAK,
+ REGSET_HW_WATCH,
+#endif
+};
+
+static const struct user_regset aarch64_regsets[] = {
+ [REGSET_GPR] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = sizeof(struct user_pt_regs) / sizeof(u64),
+ .size = sizeof(u64),
+ .align = sizeof(u64),
+ .get = gpr_get,
+ .set = gpr_set
+ },
+ [REGSET_FPR] = {
+ .core_note_type = NT_PRFPREG,
+ .n = sizeof(struct user_fpsimd_state) / sizeof(u32),
+ /*
+ * We pretend we have 32-bit registers because the fpsr and
+ * fpcr are 32-bits wide.
+ */
+ .size = sizeof(u32),
+ .align = sizeof(u32),
+ .get = fpr_get,
+ .set = fpr_set
+ },
+ [REGSET_TLS] = {
+ .core_note_type = NT_ARM_TLS,
+ .n = 1,
+ .size = sizeof(void *),
+ .align = sizeof(void *),
+ .get = tls_get,
+ .set = tls_set,
+ },
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ [REGSET_HW_BREAK] = {
+ .core_note_type = NT_ARM_HW_BREAK,
+ .n = sizeof(struct user_hwdebug_state) / sizeof(u32),
+ .size = sizeof(u32),
+ .align = sizeof(u32),
+ .get = hw_break_get,
+ .set = hw_break_set,
+ },
+ [REGSET_HW_WATCH] = {
+ .core_note_type = NT_ARM_HW_WATCH,
+ .n = sizeof(struct user_hwdebug_state) / sizeof(u32),
+ .size = sizeof(u32),
+ .align = sizeof(u32),
+ .get = hw_break_get,
+ .set = hw_break_set,
+ },
+#endif
+};
+
+static const struct user_regset_view user_aarch64_view = {
+ .name = "aarch64", .e_machine = EM_AARCH64,
+ .regsets = aarch64_regsets, .n = ARRAY_SIZE(aarch64_regsets)
+};
+
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+
+enum compat_regset {
+ REGSET_COMPAT_GPR,
+ REGSET_COMPAT_VFP,
+};
+
+static int compat_gpr_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ int ret = 0;
+ unsigned int i, start, num_regs;
+
+ /* Calculate the number of AArch32 registers contained in count */
+ num_regs = count / regset->size;
+
+ /* Convert pos into an register number */
+ start = pos / regset->size;
+
+ if (start + num_regs > regset->n)
+ return -EIO;
+
+ for (i = 0; i < num_regs; ++i) {
+ unsigned int idx = start + i;
+ void *reg;
+
+ switch (idx) {
+ case 15:
+ reg = (void *)&task_pt_regs(target)->pc;
+ break;
+ case 16:
+ reg = (void *)&task_pt_regs(target)->pstate;
+ break;
+ case 17:
+ reg = (void *)&task_pt_regs(target)->orig_x0;
+ break;
+ default:
+ reg = (void *)&task_pt_regs(target)->regs[idx];
+ }
+
+ ret = copy_to_user(ubuf, reg, sizeof(compat_ulong_t));
+
+ if (ret)
+ break;
+ else
+ ubuf += sizeof(compat_ulong_t);
+ }
+
+ return ret;
+}
+
+static int compat_gpr_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct pt_regs newregs;
+ int ret = 0;
+ unsigned int i, start, num_regs;
+
+ /* Calculate the number of AArch32 registers contained in count */
+ num_regs = count / regset->size;
+
+ /* Convert pos into an register number */
+ start = pos / regset->size;
+
+ if (start + num_regs > regset->n)
+ return -EIO;
+
+ newregs = *task_pt_regs(target);
+
+ for (i = 0; i < num_regs; ++i) {
+ unsigned int idx = start + i;
+ void *reg;
+
+ switch (idx) {
+ case 15:
+ reg = (void *)&newregs.pc;
+ break;
+ case 16:
+ reg = (void *)&newregs.pstate;
+ break;
+ case 17:
+ reg = (void *)&newregs.orig_x0;
+ break;
+ default:
+ reg = (void *)&newregs.regs[idx];
+ }
+
+ ret = copy_from_user(reg, ubuf, sizeof(compat_ulong_t));
+
+ if (ret)
+ goto out;
+ else
+ ubuf += sizeof(compat_ulong_t);
+ }
+
+ if (valid_user_regs(&newregs.user_regs))
+ *task_pt_regs(target) = newregs;
+ else
+ ret = -EINVAL;
+
+out:
+ return ret;
+}
+
+static int compat_vfp_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct user_fpsimd_state *uregs;
+ compat_ulong_t fpscr;
+ int ret;
+
+ uregs = &target->thread.fpsimd_state.user_fpsimd;
+
+ /*
+ * The VFP registers are packed into the fpsimd_state, so they all sit
+ * nicely together for us. We just need to create the fpscr separately.
+ */
+ ret = user_regset_copyout(&pos, &count, &kbuf, &ubuf, uregs, 0,
+ VFP_STATE_SIZE - sizeof(compat_ulong_t));
+
+ if (count && !ret) {
+ fpscr = (uregs->fpsr & VFP_FPSCR_STAT_MASK) |
+ (uregs->fpcr & VFP_FPSCR_CTRL_MASK);
+ ret = put_user(fpscr, (compat_ulong_t *)ubuf);
+ }
+
+ return ret;
+}
+
+static int compat_vfp_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ struct user_fpsimd_state *uregs;
+ compat_ulong_t fpscr;
+ int ret;
+
+ if (pos + count > VFP_STATE_SIZE)
+ return -EIO;
+
+ uregs = &target->thread.fpsimd_state.user_fpsimd;
+
+ ret = user_regset_copyin(&pos, &count, &kbuf, &ubuf, uregs, 0,
+ VFP_STATE_SIZE - sizeof(compat_ulong_t));
+
+ if (count && !ret) {
+ ret = get_user(fpscr, (compat_ulong_t *)ubuf);
+ uregs->fpsr = fpscr & VFP_FPSCR_STAT_MASK;
+ uregs->fpcr = fpscr & VFP_FPSCR_CTRL_MASK;
+ }
+
+ return ret;
+}
+
+static const struct user_regset aarch32_regsets[] = {
+ [REGSET_COMPAT_GPR] = {
+ .core_note_type = NT_PRSTATUS,
+ .n = COMPAT_ELF_NGREG,
+ .size = sizeof(compat_elf_greg_t),
+ .align = sizeof(compat_elf_greg_t),
+ .get = compat_gpr_get,
+ .set = compat_gpr_set
+ },
+ [REGSET_COMPAT_VFP] = {
+ .core_note_type = NT_ARM_VFP,
+ .n = VFP_STATE_SIZE / sizeof(compat_ulong_t),
+ .size = sizeof(compat_ulong_t),
+ .align = sizeof(compat_ulong_t),
+ .get = compat_vfp_get,
+ .set = compat_vfp_set
+ },
+};
+
+static const struct user_regset_view user_aarch32_view = {
+ .name = "aarch32", .e_machine = EM_ARM,
+ .regsets = aarch32_regsets, .n = ARRAY_SIZE(aarch32_regsets)
+};
+
+int aarch32_break_trap(struct pt_regs *regs)
+{
+ unsigned int instr;
+ bool bp = false;
+ void __user *pc = (void __user *)instruction_pointer(regs);
+
+ if (compat_thumb_mode(regs)) {
+ /* get 16-bit Thumb instruction */
+ get_user(instr, (u16 __user *)pc);
+ if (instr == AARCH32_BREAK_THUMB2_LO) {
+ /* get second half of 32-bit Thumb-2 instruction */
+ get_user(instr, (u16 __user *)(pc + 2));
+ bp = instr == AARCH32_BREAK_THUMB2_HI;
+ } else {
+ bp = instr == AARCH32_BREAK_THUMB;
+ }
+ } else {
+ /* 32-bit ARM instruction */
+ get_user(instr, (u32 __user *)pc);
+ bp = (instr & ~0xf0000000) == AARCH32_BREAK_ARM;
+ }
+
+ if (bp)
+ return ptrace_break(regs);
+ return 1;
+}
+
+static int compat_ptrace_read_user(struct task_struct *tsk, compat_ulong_t off,
+ compat_ulong_t __user *ret)
+{
+ compat_ulong_t tmp;
+
+ if (off & 3)
+ return -EIO;
+
+ if (off == PT_TEXT_ADDR)
+ tmp = tsk->mm->start_code;
+ else if (off == PT_DATA_ADDR)
+ tmp = tsk->mm->start_data;
+ else if (off == PT_TEXT_END_ADDR)
+ tmp = tsk->mm->end_code;
+ else if (off < sizeof(compat_elf_gregset_t))
+ return copy_regset_to_user(tsk, &user_aarch32_view,
+ REGSET_COMPAT_GPR, off,
+ sizeof(compat_ulong_t), ret);
+ else if (off >= COMPAT_USER_SZ)
+ return -EIO;
+ else
+ tmp = 0;
+
+ return put_user(tmp, ret);
+}
+
+static int compat_ptrace_write_user(struct task_struct *tsk, compat_ulong_t off,
+ compat_ulong_t val)
+{
+ int ret;
+
+ if (off & 3 || off >= COMPAT_USER_SZ)
+ return -EIO;
+
+ if (off >= sizeof(compat_elf_gregset_t))
+ return 0;
+
+ ret = copy_regset_from_user(tsk, &user_aarch32_view,
+ REGSET_COMPAT_GPR, off,
+ sizeof(compat_ulong_t),
+ &val);
+ return ret;
+}
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+
+/*
+ * Convert a virtual register number into an index for a thread_info
+ * breakpoint array. Breakpoints are identified using positive numbers
+ * whilst watchpoints are negative. The registers are laid out as pairs
+ * of (address, control), each pair mapping to a unique hw_breakpoint struct.
+ * Register 0 is reserved for describing resource information.
+ */
+static int compat_ptrace_hbp_num_to_idx(compat_long_t num)
+{
+ return (abs(num) - 1) >> 1;
+}
+
+static int compat_ptrace_hbp_get_resource_info(u32 *kdata)
+{
+ u8 num_brps, num_wrps, debug_arch, wp_len;
+ u32 reg = 0;
+
+ num_brps = hw_breakpoint_slots(TYPE_INST);
+ num_wrps = hw_breakpoint_slots(TYPE_DATA);
+
+ debug_arch = debug_monitors_arch();
+ wp_len = 8;
+ reg |= debug_arch;
+ reg <<= 8;
+ reg |= wp_len;
+ reg <<= 8;
+ reg |= num_wrps;
+ reg <<= 8;
+ reg |= num_brps;
+
+ *kdata = reg;
+ return 0;
+}
+
+static int compat_ptrace_hbp_get(unsigned int note_type,
+ struct task_struct *tsk,
+ compat_long_t num,
+ u32 *kdata)
+{
+ u64 addr = 0;
+ u32 ctrl = 0;
+
+ int err, idx = compat_ptrace_hbp_num_to_idx(num);;
+
+ if (num & 1) {
+ err = ptrace_hbp_get_addr(note_type, tsk, idx, &addr);
+ *kdata = (u32)addr;
+ } else {
+ err = ptrace_hbp_get_ctrl(note_type, tsk, idx, &ctrl);
+ *kdata = ctrl;
+ }
+
+ return err;
+}
+
+static int compat_ptrace_hbp_set(unsigned int note_type,
+ struct task_struct *tsk,
+ compat_long_t num,
+ u32 *kdata)
+{
+ u64 addr;
+ u32 ctrl;
+
+ int err, idx = compat_ptrace_hbp_num_to_idx(num);
+
+ if (num & 1) {
+ addr = *kdata;
+ err = ptrace_hbp_set_addr(note_type, tsk, idx, addr);
+ } else {
+ ctrl = *kdata;
+ err = ptrace_hbp_set_ctrl(note_type, tsk, idx, ctrl);
+ }
+
+ return err;
+}
+
+static int compat_ptrace_gethbpregs(struct task_struct *tsk, compat_long_t num,
+ compat_ulong_t __user *data)
+{
+ int ret;
+ u32 kdata;
+ mm_segment_t old_fs = get_fs();
+
+ set_fs(KERNEL_DS);
+ /* Watchpoint */
+ if (num < 0) {
+ ret = compat_ptrace_hbp_get(NT_ARM_HW_WATCH, tsk, num, &kdata);
+ /* Resource info */
+ } else if (num == 0) {
+ ret = compat_ptrace_hbp_get_resource_info(&kdata);
+ /* Breakpoint */
+ } else {
+ ret = compat_ptrace_hbp_get(NT_ARM_HW_BREAK, tsk, num, &kdata);
+ }
+ set_fs(old_fs);
+
+ if (!ret)
+ ret = put_user(kdata, data);
+
+ return ret;
+}
+
+static int compat_ptrace_sethbpregs(struct task_struct *tsk, compat_long_t num,
+ compat_ulong_t __user *data)
+{
+ int ret;
+ u32 kdata = 0;
+ mm_segment_t old_fs = get_fs();
+
+ if (num == 0)
+ return 0;
+
+ ret = get_user(kdata, data);
+ if (ret)
+ return ret;
+
+ set_fs(KERNEL_DS);
+ if (num < 0)
+ ret = compat_ptrace_hbp_set(NT_ARM_HW_WATCH, tsk, num, &kdata);
+ else
+ ret = compat_ptrace_hbp_set(NT_ARM_HW_BREAK, tsk, num, &kdata);
+ set_fs(old_fs);
+
+ return ret;
+}
+#endif /* CONFIG_HAVE_HW_BREAKPOINT */
+
+long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
+ compat_ulong_t caddr, compat_ulong_t cdata)
+{
+ unsigned long addr = caddr;
+ unsigned long data = cdata;
+ void __user *datap = compat_ptr(data);
+ int ret;
+
+ switch (request) {
+ case PTRACE_PEEKUSR:
+ ret = compat_ptrace_read_user(child, addr, datap);
+ break;
+
+ case PTRACE_POKEUSR:
+ ret = compat_ptrace_write_user(child, addr, data);
+ break;
+
+ case COMPAT_PTRACE_GETREGS:
+ ret = copy_regset_to_user(child,
+ &user_aarch32_view,
+ REGSET_COMPAT_GPR,
+ 0, sizeof(compat_elf_gregset_t),
+ datap);
+ break;
+
+ case COMPAT_PTRACE_SETREGS:
+ ret = copy_regset_from_user(child,
+ &user_aarch32_view,
+ REGSET_COMPAT_GPR,
+ 0, sizeof(compat_elf_gregset_t),
+ datap);
+ break;
+
+ case COMPAT_PTRACE_GET_THREAD_AREA:
+ ret = put_user((compat_ulong_t)child->thread.tp_value,
+ (compat_ulong_t __user *)datap);
+ break;
+
+ case COMPAT_PTRACE_SET_SYSCALL:
+ task_pt_regs(child)->syscallno = data;
+ ret = 0;
+ break;
+
+ case COMPAT_PTRACE_GETVFPREGS:
+ ret = copy_regset_to_user(child,
+ &user_aarch32_view,
+ REGSET_COMPAT_VFP,
+ 0, VFP_STATE_SIZE,
+ datap);
+ break;
+
+ case COMPAT_PTRACE_SETVFPREGS:
+ ret = copy_regset_from_user(child,
+ &user_aarch32_view,
+ REGSET_COMPAT_VFP,
+ 0, VFP_STATE_SIZE,
+ datap);
+ break;
+
+#ifdef CONFIG_HAVE_HW_BREAKPOINT
+ case COMPAT_PTRACE_GETHBPREGS:
+ ret = compat_ptrace_gethbpregs(child, addr, datap);
+ break;
+
+ case COMPAT_PTRACE_SETHBPREGS:
+ ret = compat_ptrace_sethbpregs(child, addr, datap);
+ break;
+#endif
+
+ default:
+ ret = compat_ptrace_request(child, request, addr,
+ data);
+ break;
+ }
+
+ return ret;
+}
+#endif /* CONFIG_COMPAT */
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+#ifdef CONFIG_COMPAT
+ if (is_compat_thread(task_thread_info(task)))
+ return &user_aarch32_view;
+#endif
+ return &user_aarch64_view;
+}
+
+long arch_ptrace(struct task_struct *child, long request,
+ unsigned long addr, unsigned long data)
+{
+ return ptrace_request(child, request, addr, data);
+}
+
+
+static int __init ptrace_break_init(void)
+{
+ hook_debug_fault_code(DBG_ESR_EVT_BRK, arm64_break_trap, SIGTRAP,
+ TRAP_BRKPT, "ptrace BRK handler");
+ return 0;
+}
+core_initcall(ptrace_break_init);
+
+
+asmlinkage int syscall_trace(int dir, struct pt_regs *regs)
+{
+ unsigned long saved_reg;
+
+ if (!test_thread_flag(TIF_SYSCALL_TRACE))
+ return regs->syscallno;
+
+ if (is_compat_task()) {
+ /* AArch32 uses ip (r12) for scratch */
+ saved_reg = regs->regs[12];
+ regs->regs[12] = dir;
+ } else {
+ /*
+ * Save X7. X7 is used to denote syscall entry/exit:
+ * X7 = 0 -> entry, = 1 -> exit
+ */
+ saved_reg = regs->regs[7];
+ regs->regs[7] = dir;
+ }
+
+ if (dir)
+ tracehook_report_syscall_exit(regs, 0);
+ else if (tracehook_report_syscall_entry(regs))
+ regs->syscallno = ~0UL;
+
+ if (is_compat_task())
+ regs->regs[12] = saved_reg;
+ else
+ regs->regs[7] = saved_reg;
+
+ return regs->syscallno;
+}
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
new file mode 100644
index 000000000000..48ffb9fb3fe3
--- /dev/null
+++ b/arch/arm64/kernel/setup.c
@@ -0,0 +1,347 @@
+/*
+ * Based on arch/arm/kernel/setup.c
+ *
+ * Copyright (C) 1995-2001 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/utsname.h>
+#include <linux/initrd.h>
+#include <linux/console.h>
+#include <linux/bootmem.h>
+#include <linux/seq_file.h>
+#include <linux/screen_info.h>
+#include <linux/init.h>
+#include <linux/kexec.h>
+#include <linux/crash_dump.h>
+#include <linux/root_dev.h>
+#include <linux/cpu.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/memblock.h>
+#include <linux/of_fdt.h>
+
+#include <asm/cputype.h>
+#include <asm/elf.h>
+#include <asm/cputable.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm/traps.h>
+#include <asm/memblock.h>
+
+unsigned int processor_id;
+EXPORT_SYMBOL(processor_id);
+
+unsigned int elf_hwcap __read_mostly;
+EXPORT_SYMBOL_GPL(elf_hwcap);
+
+static const char *cpu_name;
+static const char *machine_name;
+phys_addr_t __fdt_pointer __initdata;
+
+/*
+ * Standard memory resources
+ */
+static struct resource mem_res[] = {
+ {
+ .name = "Kernel code",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_MEM
+ },
+ {
+ .name = "Kernel data",
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_MEM
+ }
+};
+
+#define kernel_code mem_res[0]
+#define kernel_data mem_res[1]
+
+void __init early_print(const char *str, ...)
+{
+ char buf[256];
+ va_list ap;
+
+ va_start(ap, str);
+ vsnprintf(buf, sizeof(buf), str, ap);
+ va_end(ap);
+
+ printk("%s", buf);
+}
+
+static void __init setup_processor(void)
+{
+ struct cpu_info *cpu_info;
+
+ /*
+ * locate processor in the list of supported processor
+ * types. The linker builds this table for us from the
+ * entries in arch/arm/mm/proc.S
+ */
+ cpu_info = lookup_processor_type(read_cpuid_id());
+ if (!cpu_info) {
+ printk("CPU configuration botched (ID %08x), unable to continue.\n",
+ read_cpuid_id());
+ while (1);
+ }
+
+ cpu_name = cpu_info->cpu_name;
+
+ printk("CPU: %s [%08x] revision %d\n",
+ cpu_name, read_cpuid_id(), read_cpuid_id() & 15);
+
+ sprintf(init_utsname()->machine, "aarch64");
+ elf_hwcap = 0;
+}
+
+static void __init setup_machine_fdt(phys_addr_t dt_phys)
+{
+ struct boot_param_header *devtree;
+ unsigned long dt_root;
+
+ /* Check we have a non-NULL DT pointer */
+ if (!dt_phys) {
+ early_print("\n"
+ "Error: NULL or invalid device tree blob\n"
+ "The dtb must be 8-byte aligned and passed in the first 512MB of memory\n"
+ "\nPlease check your bootloader.\n");
+
+ while (true)
+ cpu_relax();
+
+ }
+
+ devtree = phys_to_virt(dt_phys);
+
+ /* Check device tree validity */
+ if (be32_to_cpu(devtree->magic) != OF_DT_HEADER) {
+ early_print("\n"
+ "Error: invalid device tree blob at physical address 0x%p (virtual address 0x%p)\n"
+ "Expected 0x%x, found 0x%x\n"
+ "\nPlease check your bootloader.\n",
+ dt_phys, devtree, OF_DT_HEADER,
+ be32_to_cpu(devtree->magic));
+
+ while (true)
+ cpu_relax();
+ }
+
+ initial_boot_params = devtree;
+ dt_root = of_get_flat_dt_root();
+
+ machine_name = of_get_flat_dt_prop(dt_root, "model", NULL);
+ if (!machine_name)
+ machine_name = of_get_flat_dt_prop(dt_root, "compatible", NULL);
+ if (!machine_name)
+ machine_name = "<unknown>";
+ pr_info("Machine: %s\n", machine_name);
+
+ /* Retrieve various information from the /chosen node */
+ of_scan_flat_dt(early_init_dt_scan_chosen, boot_command_line);
+ /* Initialize {size,address}-cells info */
+ of_scan_flat_dt(early_init_dt_scan_root, NULL);
+ /* Setup memory, calling early_init_dt_add_memory_arch */
+ of_scan_flat_dt(early_init_dt_scan_memory, NULL);
+}
+
+void __init early_init_dt_add_memory_arch(u64 base, u64 size)
+{
+ size &= PAGE_MASK;
+ memblock_add(base, size);
+}
+
+void * __init early_init_dt_alloc_memory_arch(u64 size, u64 align)
+{
+ return __va(memblock_alloc(size, align));
+}
+
+/*
+ * Limit the memory size that was specified via FDT.
+ */
+static int __init early_mem(char *p)
+{
+ phys_addr_t limit;
+
+ if (!p)
+ return 1;
+
+ limit = memparse(p, &p) & PAGE_MASK;
+ pr_notice("Memory limited to %lldMB\n", limit >> 20);
+
+ memblock_enforce_memory_limit(limit);
+
+ return 0;
+}
+early_param("mem", early_mem);
+
+static void __init request_standard_resources(void)
+{
+ struct memblock_region *region;
+ struct resource *res;
+
+ kernel_code.start = virt_to_phys(_text);
+ kernel_code.end = virt_to_phys(_etext - 1);
+ kernel_data.start = virt_to_phys(_sdata);
+ kernel_data.end = virt_to_phys(_end - 1);
+
+ for_each_memblock(memory, region) {
+ res = alloc_bootmem_low(sizeof(*res));
+ res->name = "System RAM";
+ res->start = __pfn_to_phys(memblock_region_memory_base_pfn(region));
+ res->end = __pfn_to_phys(memblock_region_memory_end_pfn(region)) - 1;
+ res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+
+ request_resource(&iomem_resource, res);
+
+ if (kernel_code.start >= res->start &&
+ kernel_code.end <= res->end)
+ request_resource(res, &kernel_code);
+ if (kernel_data.start >= res->start &&
+ kernel_data.end <= res->end)
+ request_resource(res, &kernel_data);
+ }
+}
+
+void __init setup_arch(char **cmdline_p)
+{
+ setup_processor();
+
+ setup_machine_fdt(__fdt_pointer);
+
+ init_mm.start_code = (unsigned long) _text;
+ init_mm.end_code = (unsigned long) _etext;
+ init_mm.end_data = (unsigned long) _edata;
+ init_mm.brk = (unsigned long) _end;
+
+ *cmdline_p = boot_command_line;
+
+ parse_early_param();
+
+ arm64_memblock_init();
+
+ paging_init();
+ request_standard_resources();
+
+ unflatten_device_tree();
+
+#ifdef CONFIG_SMP
+ smp_init_cpus();
+#endif
+
+#ifdef CONFIG_VT
+#if defined(CONFIG_VGA_CONSOLE)
+ conswitchp = &vga_con;
+#elif defined(CONFIG_DUMMY_CONSOLE)
+ conswitchp = &dummy_con;
+#endif
+#endif
+}
+
+static DEFINE_PER_CPU(struct cpu, cpu_data);
+
+static int __init topology_init(void)
+{
+ int i;
+
+ for_each_possible_cpu(i) {
+ struct cpu *cpu = &per_cpu(cpu_data, i);
+ cpu->hotpluggable = 1;
+ register_cpu(cpu, i);
+ }
+
+ return 0;
+}
+subsys_initcall(topology_init);
+
+static const char *hwcap_str[] = {
+ "fp",
+ "asimd",
+ NULL
+};
+
+static int c_show(struct seq_file *m, void *v)
+{
+ int i;
+
+ seq_printf(m, "Processor\t: %s rev %d (%s)\n",
+ cpu_name, read_cpuid_id() & 15, ELF_PLATFORM);
+
+ for_each_online_cpu(i) {
+ /*
+ * glibc reads /proc/cpuinfo to determine the number of
+ * online processors, looking for lines beginning with
+ * "processor". Give glibc what it expects.
+ */
+#ifdef CONFIG_SMP
+ seq_printf(m, "processor\t: %d\n", i);
+#endif
+ seq_printf(m, "BogoMIPS\t: %lu.%02lu\n\n",
+ loops_per_jiffy / (500000UL/HZ),
+ loops_per_jiffy / (5000UL/HZ) % 100);
+ }
+
+ /* dump out the processor features */
+ seq_puts(m, "Features\t: ");
+
+ for (i = 0; hwcap_str[i]; i++)
+ if (elf_hwcap & (1 << i))
+ seq_printf(m, "%s ", hwcap_str[i]);
+
+ seq_printf(m, "\nCPU implementer\t: 0x%02x\n", read_cpuid_id() >> 24);
+ seq_printf(m, "CPU architecture: AArch64\n");
+ seq_printf(m, "CPU variant\t: 0x%x\n", (read_cpuid_id() >> 20) & 15);
+ seq_printf(m, "CPU part\t: 0x%03x\n", (read_cpuid_id() >> 4) & 0xfff);
+ seq_printf(m, "CPU revision\t: %d\n", read_cpuid_id() & 15);
+
+ seq_puts(m, "\n");
+
+ seq_printf(m, "Hardware\t: %s\n", machine_name);
+
+ return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+ return *pos < 1 ? (void *)1 : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ ++*pos;
+ return NULL;
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+const struct seq_operations cpuinfo_op = {
+ .start = c_start,
+ .next = c_next,
+ .stop = c_stop,
+ .show = c_show
+};
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
new file mode 100644
index 000000000000..8807ba2cf262
--- /dev/null
+++ b/arch/arm64/kernel/signal.c
@@ -0,0 +1,437 @@
+/*
+ * Based on arch/arm/kernel/signal.c
+ *
+ * Copyright (C) 1995-2009 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/personality.h>
+#include <linux/freezer.h>
+#include <linux/uaccess.h>
+#include <linux/tracehook.h>
+#include <linux/ratelimit.h>
+
+#include <asm/compat.h>
+#include <asm/debug-monitors.h>
+#include <asm/elf.h>
+#include <asm/cacheflush.h>
+#include <asm/ucontext.h>
+#include <asm/unistd.h>
+#include <asm/fpsimd.h>
+#include <asm/signal32.h>
+#include <asm/vdso.h>
+
+/*
+ * Do a signal return; undo the signal stack. These are aligned to 128-bit.
+ */
+struct rt_sigframe {
+ struct siginfo info;
+ struct ucontext uc;
+};
+
+static int preserve_fpsimd_context(struct fpsimd_context __user *ctx)
+{
+ struct fpsimd_state *fpsimd = &current->thread.fpsimd_state;
+ int err;
+
+ /* dump the hardware registers to the fpsimd_state structure */
+ fpsimd_save_state(fpsimd);
+
+ /* copy the FP and status/control registers */
+ err = __copy_to_user(ctx->vregs, fpsimd->vregs, sizeof(fpsimd->vregs));
+ __put_user_error(fpsimd->fpsr, &ctx->fpsr, err);
+ __put_user_error(fpsimd->fpcr, &ctx->fpcr, err);
+
+ /* copy the magic/size information */
+ __put_user_error(FPSIMD_MAGIC, &ctx->head.magic, err);
+ __put_user_error(sizeof(struct fpsimd_context), &ctx->head.size, err);
+
+ return err ? -EFAULT : 0;
+}
+
+static int restore_fpsimd_context(struct fpsimd_context __user *ctx)
+{
+ struct fpsimd_state fpsimd;
+ __u32 magic, size;
+ int err = 0;
+
+ /* check the magic/size information */
+ __get_user_error(magic, &ctx->head.magic, err);
+ __get_user_error(size, &ctx->head.size, err);
+ if (err)
+ return -EFAULT;
+ if (magic != FPSIMD_MAGIC || size != sizeof(struct fpsimd_context))
+ return -EINVAL;
+
+ /* copy the FP and status/control registers */
+ err = __copy_from_user(fpsimd.vregs, ctx->vregs,
+ sizeof(fpsimd.vregs));
+ __get_user_error(fpsimd.fpsr, &ctx->fpsr, err);
+ __get_user_error(fpsimd.fpcr, &ctx->fpcr, err);
+
+ /* load the hardware registers from the fpsimd_state structure */
+ if (!err) {
+ preempt_disable();
+ fpsimd_load_state(&fpsimd);
+ preempt_enable();
+ }
+
+ return err ? -EFAULT : 0;
+}
+
+static int restore_sigframe(struct pt_regs *regs,
+ struct rt_sigframe __user *sf)
+{
+ sigset_t set;
+ int i, err;
+ struct aux_context __user *aux =
+ (struct aux_context __user *)sf->uc.uc_mcontext.__reserved;
+
+ err = __copy_from_user(&set, &sf->uc.uc_sigmask, sizeof(set));
+ if (err == 0)
+ set_current_blocked(&set);
+
+ for (i = 0; i < 31; i++)
+ __get_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i],
+ err);
+ __get_user_error(regs->sp, &sf->uc.uc_mcontext.sp, err);
+ __get_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err);
+ __get_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err);
+
+ /*
+ * Avoid sys_rt_sigreturn() restarting.
+ */
+ regs->syscallno = ~0UL;
+
+ err |= !valid_user_regs(&regs->user_regs);
+
+ if (err == 0)
+ err |= restore_fpsimd_context(&aux->fpsimd);
+
+ return err;
+}
+
+asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame;
+
+ /* Always make any pending restarted system calls return -EINTR */
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+ /*
+ * Since we stacked the signal on a 128-bit boundary, then 'sp' should
+ * be word aligned here.
+ */
+ if (regs->sp & 15)
+ goto badframe;
+
+ frame = (struct rt_sigframe __user *)regs->sp;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof (*frame)))
+ goto badframe;
+
+ if (restore_sigframe(regs, frame))
+ goto badframe;
+
+ if (do_sigaltstack(&frame->uc.uc_stack,
+ NULL, regs->sp) == -EFAULT)
+ goto badframe;
+
+ return regs->regs[0];
+
+badframe:
+ if (show_unhandled_signals)
+ pr_info_ratelimited("%s[%d]: bad frame in %s: pc=%08llx sp=%08llx\n",
+ current->comm, task_pid_nr(current), __func__,
+ regs->pc, regs->sp);
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+asmlinkage long sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss,
+ unsigned long sp)
+{
+ return do_sigaltstack(uss, uoss, sp);
+}
+
+static int setup_sigframe(struct rt_sigframe __user *sf,
+ struct pt_regs *regs, sigset_t *set)
+{
+ int i, err = 0;
+ struct aux_context __user *aux =
+ (struct aux_context __user *)sf->uc.uc_mcontext.__reserved;
+
+ for (i = 0; i < 31; i++)
+ __put_user_error(regs->regs[i], &sf->uc.uc_mcontext.regs[i],
+ err);
+ __put_user_error(regs->sp, &sf->uc.uc_mcontext.sp, err);
+ __put_user_error(regs->pc, &sf->uc.uc_mcontext.pc, err);
+ __put_user_error(regs->pstate, &sf->uc.uc_mcontext.pstate, err);
+
+ __put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err);
+
+ err |= __copy_to_user(&sf->uc.uc_sigmask, set, sizeof(*set));
+
+ if (err == 0)
+ err |= preserve_fpsimd_context(&aux->fpsimd);
+
+ /* set the "end" magic */
+ __put_user_error(0, &aux->end.magic, err);
+ __put_user_error(0, &aux->end.size, err);
+
+ return err;
+}
+
+static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
+ int framesize)
+{
+ unsigned long sp, sp_top;
+ void __user *frame;
+
+ sp = sp_top = regs->sp;
+
+ /*
+ * This is the X/Open sanctioned signal stack switching.
+ */
+ if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp))
+ sp = sp_top = current->sas_ss_sp + current->sas_ss_size;
+
+ /* room for stack frame (FP, LR) */
+ sp -= 16;
+
+ sp = (sp - framesize) & ~15;
+ frame = (void __user *)sp;
+
+ /*
+ * Check that we can actually write to the signal frame.
+ */
+ if (!access_ok(VERIFY_WRITE, frame, sp_top - sp))
+ frame = NULL;
+
+ return frame;
+}
+
+static int setup_return(struct pt_regs *regs, struct k_sigaction *ka,
+ void __user *frame, int usig)
+{
+ int err = 0;
+ __sigrestore_t sigtramp;
+ unsigned long __user *sp = (unsigned long __user *)regs->sp;
+
+ /* set up the stack frame */
+ __put_user_error(regs->regs[29], sp - 2, err);
+ __put_user_error(regs->regs[30], sp - 1, err);
+
+ regs->regs[0] = usig;
+ regs->regs[29] = regs->sp - 16;
+ regs->sp = (unsigned long)frame;
+ regs->pc = (unsigned long)ka->sa.sa_handler;
+
+ if (ka->sa.sa_flags & SA_RESTORER)
+ sigtramp = ka->sa.sa_restorer;
+ else
+ sigtramp = VDSO_SYMBOL(current->mm->context.vdso, sigtramp);
+
+ regs->regs[30] = (unsigned long)sigtramp;
+
+ return err;
+}
+
+static int setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct rt_sigframe __user *frame;
+ stack_t stack;
+ int err = 0;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame));
+ if (!frame)
+ return 1;
+
+ __put_user_error(0, &frame->uc.uc_flags, err);
+ __put_user_error(NULL, &frame->uc.uc_link, err);
+
+ memset(&stack, 0, sizeof(stack));
+ stack.ss_sp = (void __user *)current->sas_ss_sp;
+ stack.ss_flags = sas_ss_flags(regs->sp);
+ stack.ss_size = current->sas_ss_size;
+ err |= __copy_to_user(&frame->uc.uc_stack, &stack, sizeof(stack));
+
+ err |= setup_sigframe(frame, regs, set);
+ if (err == 0)
+ err = setup_return(regs, ka, frame, usig);
+
+ if (err == 0 && ka->sa.sa_flags & SA_SIGINFO) {
+ err |= copy_siginfo_to_user(&frame->info, info);
+ regs->regs[1] = (unsigned long)&frame->info;
+ regs->regs[2] = (unsigned long)&frame->uc;
+ }
+
+ return err;
+}
+
+static void setup_restart_syscall(struct pt_regs *regs)
+{
+ if (is_compat_task())
+ compat_setup_restart_syscall(regs);
+ else
+ regs->regs[8] = __NR_restart_syscall;
+}
+
+/*
+ * OK, we're invoking a handler
+ */
+static void handle_signal(unsigned long sig, struct k_sigaction *ka,
+ siginfo_t *info, struct pt_regs *regs)
+{
+ struct thread_info *thread = current_thread_info();
+ struct task_struct *tsk = current;
+ sigset_t *oldset = sigmask_to_save();
+ int usig = sig;
+ int ret;
+
+ /*
+ * translate the signal
+ */
+ if (usig < 32 && thread->exec_domain && thread->exec_domain->signal_invmap)
+ usig = thread->exec_domain->signal_invmap[usig];
+
+ /*
+ * Set up the stack frame
+ */
+ if (is_compat_task()) {
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ ret = compat_setup_rt_frame(usig, ka, info, oldset,
+ regs);
+ else
+ ret = compat_setup_frame(usig, ka, oldset, regs);
+ } else {
+ ret = setup_rt_frame(usig, ka, info, oldset, regs);
+ }
+
+ /*
+ * Check that the resulting registers are actually sane.
+ */
+ ret |= !valid_user_regs(&regs->user_regs);
+
+ if (ret != 0) {
+ force_sigsegv(sig, tsk);
+ return;
+ }
+
+ /*
+ * Fast forward the stepping logic so we step into the signal
+ * handler.
+ */
+ user_fastforward_single_step(tsk);
+
+ signal_delivered(sig, info, ka, regs, 0);
+}
+
+/*
+ * Note that 'init' is a special process: it doesn't get signals it doesn't
+ * want to handle. Thus you cannot kill init even with a SIGKILL even by
+ * mistake.
+ *
+ * Note that we go through the signals twice: once to check the signals that
+ * the kernel can handle, and then we build all the user-level signal handling
+ * stack-frames in one go after that.
+ */
+static void do_signal(struct pt_regs *regs)
+{
+ unsigned long continue_addr = 0, restart_addr = 0;
+ struct k_sigaction ka;
+ siginfo_t info;
+ int signr, retval = 0;
+ int syscall = (int)regs->syscallno;
+
+ /*
+ * If we were from a system call, check for system call restarting...
+ */
+ if (syscall >= 0) {
+ continue_addr = regs->pc;
+ restart_addr = continue_addr - (compat_thumb_mode(regs) ? 2 : 4);
+ retval = regs->regs[0];
+
+ /*
+ * Avoid additional syscall restarting via ret_to_user.
+ */
+ regs->syscallno = ~0UL;
+
+ /*
+ * Prepare for system call restart. We do this here so that a
+ * debugger will see the already changed PC.
+ */
+ switch (retval) {
+ case -ERESTARTNOHAND:
+ case -ERESTARTSYS:
+ case -ERESTARTNOINTR:
+ case -ERESTART_RESTARTBLOCK:
+ regs->regs[0] = regs->orig_x0;
+ regs->pc = restart_addr;
+ break;
+ }
+ }
+
+ /*
+ * Get the signal to deliver. When running under ptrace, at this point
+ * the debugger may change all of our registers.
+ */
+ signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+ if (signr > 0) {
+ /*
+ * Depending on the signal settings, we may need to revert the
+ * decision to restart the system call, but skip this if a
+ * debugger has chosen to restart at a different PC.
+ */
+ if (regs->pc == restart_addr &&
+ (retval == -ERESTARTNOHAND ||
+ retval == -ERESTART_RESTARTBLOCK ||
+ (retval == -ERESTARTSYS &&
+ !(ka.sa.sa_flags & SA_RESTART)))) {
+ regs->regs[0] = -EINTR;
+ regs->pc = continue_addr;
+ }
+
+ handle_signal(signr, &ka, &info, regs);
+ return;
+ }
+
+ /*
+ * Handle restarting a different system call. As above, if a debugger
+ * has chosen to restart at a different PC, ignore the restart.
+ */
+ if (syscall >= 0 && regs->pc == restart_addr) {
+ if (retval == -ERESTART_RESTARTBLOCK)
+ setup_restart_syscall(regs);
+ user_rewind_single_step(current);
+ }
+
+ restore_saved_sigmask();
+}
+
+asmlinkage void do_notify_resume(struct pt_regs *regs,
+ unsigned int thread_flags)
+{
+ if (thread_flags & _TIF_SIGPENDING)
+ do_signal(regs);
+
+ if (thread_flags & _TIF_NOTIFY_RESUME) {
+ clear_thread_flag(TIF_NOTIFY_RESUME);
+ tracehook_notify_resume(regs);
+ }
+}
diff --git a/arch/arm64/kernel/signal32.c b/arch/arm64/kernel/signal32.c
new file mode 100644
index 000000000000..ac74c2f261e3
--- /dev/null
+++ b/arch/arm64/kernel/signal32.c
@@ -0,0 +1,876 @@
+/*
+ * Based on arch/arm/kernel/signal.c
+ *
+ * Copyright (C) 1995-2009 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ * Modified by Will Deacon <will.deacon@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/>.
+ */
+
+#define __SYSCALL_COMPAT
+
+#include <linux/compat.h>
+#include <linux/signal.h>
+#include <linux/syscalls.h>
+#include <linux/ratelimit.h>
+
+#include <asm/fpsimd.h>
+#include <asm/signal32.h>
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+
+typedef struct compat_siginfo {
+ int si_signo;
+ int si_errno;
+ int si_code;
+
+ union {
+ /* The padding is the same size as AArch64. */
+ int _pad[SI_PAD_SIZE];
+
+ /* kill() */
+ struct {
+ compat_pid_t _pid; /* sender's pid */
+ __compat_uid32_t _uid; /* sender's uid */
+ } _kill;
+
+ /* POSIX.1b timers */
+ struct {
+ compat_timer_t _tid; /* timer id */
+ int _overrun; /* overrun count */
+ compat_sigval_t _sigval; /* same as below */
+ int _sys_private; /* not to be passed to user */
+ } _timer;
+
+ /* POSIX.1b signals */
+ struct {
+ compat_pid_t _pid; /* sender's pid */
+ __compat_uid32_t _uid; /* sender's uid */
+ compat_sigval_t _sigval;
+ } _rt;
+
+ /* SIGCHLD */
+ struct {
+ compat_pid_t _pid; /* which child */
+ __compat_uid32_t _uid; /* sender's uid */
+ int _status; /* exit code */
+ compat_clock_t _utime;
+ compat_clock_t _stime;
+ } _sigchld;
+
+ /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */
+ struct {
+ compat_uptr_t _addr; /* faulting insn/memory ref. */
+ short _addr_lsb; /* LSB of the reported address */
+ } _sigfault;
+
+ /* SIGPOLL */
+ struct {
+ compat_long_t _band; /* POLL_IN, POLL_OUT, POLL_MSG */
+ int _fd;
+ } _sigpoll;
+ } _sifields;
+} compat_siginfo_t;
+
+struct compat_sigaction {
+ compat_uptr_t sa_handler;
+ compat_ulong_t sa_flags;
+ compat_uptr_t sa_restorer;
+ compat_sigset_t sa_mask;
+};
+
+struct compat_old_sigaction {
+ compat_uptr_t sa_handler;
+ compat_old_sigset_t sa_mask;
+ compat_ulong_t sa_flags;
+ compat_uptr_t sa_restorer;
+};
+
+typedef struct compat_sigaltstack {
+ compat_uptr_t ss_sp;
+ int ss_flags;
+ compat_size_t ss_size;
+} compat_stack_t;
+
+struct compat_sigcontext {
+ /* We always set these two fields to 0 */
+ compat_ulong_t trap_no;
+ compat_ulong_t error_code;
+
+ compat_ulong_t oldmask;
+ compat_ulong_t arm_r0;
+ compat_ulong_t arm_r1;
+ compat_ulong_t arm_r2;
+ compat_ulong_t arm_r3;
+ compat_ulong_t arm_r4;
+ compat_ulong_t arm_r5;
+ compat_ulong_t arm_r6;
+ compat_ulong_t arm_r7;
+ compat_ulong_t arm_r8;
+ compat_ulong_t arm_r9;
+ compat_ulong_t arm_r10;
+ compat_ulong_t arm_fp;
+ compat_ulong_t arm_ip;
+ compat_ulong_t arm_sp;
+ compat_ulong_t arm_lr;
+ compat_ulong_t arm_pc;
+ compat_ulong_t arm_cpsr;
+ compat_ulong_t fault_address;
+};
+
+struct compat_ucontext {
+ compat_ulong_t uc_flags;
+ struct compat_ucontext *uc_link;
+ compat_stack_t uc_stack;
+ struct compat_sigcontext uc_mcontext;
+ compat_sigset_t uc_sigmask;
+ int __unused[32 - (sizeof (compat_sigset_t) / sizeof (int))];
+ compat_ulong_t uc_regspace[128] __attribute__((__aligned__(8)));
+};
+
+struct compat_vfp_sigframe {
+ compat_ulong_t magic;
+ compat_ulong_t size;
+ struct compat_user_vfp {
+ compat_u64 fpregs[32];
+ compat_ulong_t fpscr;
+ } ufp;
+ struct compat_user_vfp_exc {
+ compat_ulong_t fpexc;
+ compat_ulong_t fpinst;
+ compat_ulong_t fpinst2;
+ } ufp_exc;
+} __attribute__((__aligned__(8)));
+
+#define VFP_MAGIC 0x56465001
+#define VFP_STORAGE_SIZE sizeof(struct compat_vfp_sigframe)
+
+struct compat_aux_sigframe {
+ struct compat_vfp_sigframe vfp;
+
+ /* Something that isn't a valid magic number for any coprocessor. */
+ unsigned long end_magic;
+} __attribute__((__aligned__(8)));
+
+struct compat_sigframe {
+ struct compat_ucontext uc;
+ compat_ulong_t retcode[2];
+};
+
+struct compat_rt_sigframe {
+ struct compat_siginfo info;
+ struct compat_sigframe sig;
+};
+
+#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
+
+/*
+ * For ARM syscalls, the syscall number has to be loaded into r7.
+ * We do not support an OABI userspace.
+ */
+#define MOV_R7_NR_SIGRETURN (0xe3a07000 | __NR_sigreturn)
+#define SVC_SYS_SIGRETURN (0xef000000 | __NR_sigreturn)
+#define MOV_R7_NR_RT_SIGRETURN (0xe3a07000 | __NR_rt_sigreturn)
+#define SVC_SYS_RT_SIGRETURN (0xef000000 | __NR_rt_sigreturn)
+
+/*
+ * For Thumb syscalls, we also pass the syscall number via r7. We therefore
+ * need two 16-bit instructions.
+ */
+#define SVC_THUMB_SIGRETURN (((0xdf00 | __NR_sigreturn) << 16) | \
+ 0x2700 | __NR_sigreturn)
+#define SVC_THUMB_RT_SIGRETURN (((0xdf00 | __NR_rt_sigreturn) << 16) | \
+ 0x2700 | __NR_rt_sigreturn)
+
+const compat_ulong_t aarch32_sigret_code[6] = {
+ /*
+ * AArch32 sigreturn code.
+ * We don't construct an OABI SWI - instead we just set the imm24 field
+ * to the EABI syscall number so that we create a sane disassembly.
+ */
+ MOV_R7_NR_SIGRETURN, SVC_SYS_SIGRETURN, SVC_THUMB_SIGRETURN,
+ MOV_R7_NR_RT_SIGRETURN, SVC_SYS_RT_SIGRETURN, SVC_THUMB_RT_SIGRETURN,
+};
+
+static inline int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set)
+{
+ compat_sigset_t cset;
+
+ cset.sig[0] = set->sig[0] & 0xffffffffull;
+ cset.sig[1] = set->sig[0] >> 32;
+
+ return copy_to_user(uset, &cset, sizeof(*uset));
+}
+
+static inline int get_sigset_t(sigset_t *set,
+ const compat_sigset_t __user *uset)
+{
+ compat_sigset_t s32;
+
+ if (copy_from_user(&s32, uset, sizeof(*uset)))
+ return -EFAULT;
+
+ set->sig[0] = s32.sig[0] | (((long)s32.sig[1]) << 32);
+ return 0;
+}
+
+int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
+{
+ int err;
+
+ if (!access_ok(VERIFY_WRITE, to, sizeof(*to)))
+ return -EFAULT;
+
+ /* If you change siginfo_t structure, please be sure
+ * this code is fixed accordingly.
+ * It should never copy any pad contained in the structure
+ * to avoid security leaks, but must copy the generic
+ * 3 ints plus the relevant union member.
+ * This routine must convert siginfo from 64bit to 32bit as well
+ * at the same time.
+ */
+ err = __put_user(from->si_signo, &to->si_signo);
+ err |= __put_user(from->si_errno, &to->si_errno);
+ err |= __put_user((short)from->si_code, &to->si_code);
+ if (from->si_code < 0)
+ err |= __copy_to_user(&to->_sifields._pad, &from->_sifields._pad,
+ SI_PAD_SIZE);
+ else switch (from->si_code & __SI_MASK) {
+ case __SI_KILL:
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ break;
+ case __SI_TIMER:
+ err |= __put_user(from->si_tid, &to->si_tid);
+ err |= __put_user(from->si_overrun, &to->si_overrun);
+ err |= __put_user((compat_uptr_t)(unsigned long)from->si_ptr,
+ &to->si_ptr);
+ break;
+ case __SI_POLL:
+ err |= __put_user(from->si_band, &to->si_band);
+ err |= __put_user(from->si_fd, &to->si_fd);
+ break;
+ case __SI_FAULT:
+ err |= __put_user((compat_uptr_t)(unsigned long)from->si_addr,
+ &to->si_addr);
+#ifdef BUS_MCEERR_AO
+ /*
+ * Other callers might not initialize the si_lsb field,
+ * so check explicitely for the right codes here.
+ */
+ if (from->si_code == BUS_MCEERR_AR || from->si_code == BUS_MCEERR_AO)
+ err |= __put_user(from->si_addr_lsb, &to->si_addr_lsb);
+#endif
+ break;
+ case __SI_CHLD:
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ err |= __put_user(from->si_status, &to->si_status);
+ err |= __put_user(from->si_utime, &to->si_utime);
+ err |= __put_user(from->si_stime, &to->si_stime);
+ break;
+ case __SI_RT: /* This is not generated by the kernel as of now. */
+ case __SI_MESGQ: /* But this is */
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ err |= __put_user((compat_uptr_t)(unsigned long)from->si_ptr, &to->si_ptr);
+ break;
+ default: /* this is just in case for now ... */
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ break;
+ }
+ return err;
+}
+
+int copy_siginfo_from_user32(siginfo_t *to, compat_siginfo_t __user *from)
+{
+ memset(to, 0, sizeof *to);
+
+ if (copy_from_user(to, from, __ARCH_SI_PREAMBLE_SIZE) ||
+ copy_from_user(to->_sifields._pad,
+ from->_sifields._pad, SI_PAD_SIZE))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*
+ * VFP save/restore code.
+ */
+static int compat_preserve_vfp_context(struct compat_vfp_sigframe __user *frame)
+{
+ struct fpsimd_state *fpsimd = &current->thread.fpsimd_state;
+ compat_ulong_t magic = VFP_MAGIC;
+ compat_ulong_t size = VFP_STORAGE_SIZE;
+ compat_ulong_t fpscr, fpexc;
+ int err = 0;
+
+ /*
+ * Save the hardware registers to the fpsimd_state structure.
+ * Note that this also saves V16-31, which aren't visible
+ * in AArch32.
+ */
+ fpsimd_save_state(fpsimd);
+
+ /* Place structure header on the stack */
+ __put_user_error(magic, &frame->magic, err);
+ __put_user_error(size, &frame->size, err);
+
+ /*
+ * Now copy the FP registers. Since the registers are packed,
+ * we can copy the prefix we want (V0-V15) as it is.
+ * FIXME: Won't work if big endian.
+ */
+ err |= __copy_to_user(&frame->ufp.fpregs, fpsimd->vregs,
+ sizeof(frame->ufp.fpregs));
+
+ /* Create an AArch32 fpscr from the fpsr and the fpcr. */
+ fpscr = (fpsimd->fpsr & VFP_FPSCR_STAT_MASK) |
+ (fpsimd->fpcr & VFP_FPSCR_CTRL_MASK);
+ __put_user_error(fpscr, &frame->ufp.fpscr, err);
+
+ /*
+ * The exception register aren't available so we fake up a
+ * basic FPEXC and zero everything else.
+ */
+ fpexc = (1 << 30);
+ __put_user_error(fpexc, &frame->ufp_exc.fpexc, err);
+ __put_user_error(0, &frame->ufp_exc.fpinst, err);
+ __put_user_error(0, &frame->ufp_exc.fpinst2, err);
+
+ return err ? -EFAULT : 0;
+}
+
+static int compat_restore_vfp_context(struct compat_vfp_sigframe __user *frame)
+{
+ struct fpsimd_state fpsimd;
+ compat_ulong_t magic = VFP_MAGIC;
+ compat_ulong_t size = VFP_STORAGE_SIZE;
+ compat_ulong_t fpscr;
+ int err = 0;
+
+ __get_user_error(magic, &frame->magic, err);
+ __get_user_error(size, &frame->size, err);
+
+ if (err)
+ return -EFAULT;
+ if (magic != VFP_MAGIC || size != VFP_STORAGE_SIZE)
+ return -EINVAL;
+
+ /*
+ * Copy the FP registers into the start of the fpsimd_state.
+ * FIXME: Won't work if big endian.
+ */
+ err |= __copy_from_user(fpsimd.vregs, frame->ufp.fpregs,
+ sizeof(frame->ufp.fpregs));
+
+ /* Extract the fpsr and the fpcr from the fpscr */
+ __get_user_error(fpscr, &frame->ufp.fpscr, err);
+ fpsimd.fpsr = fpscr & VFP_FPSCR_STAT_MASK;
+ fpsimd.fpcr = fpscr & VFP_FPSCR_CTRL_MASK;
+
+ /*
+ * We don't need to touch the exception register, so
+ * reload the hardware state.
+ */
+ if (!err) {
+ preempt_disable();
+ fpsimd_load_state(&fpsimd);
+ preempt_enable();
+ }
+
+ return err ? -EFAULT : 0;
+}
+
+/*
+ * atomically swap in the new signal mask, and wait for a signal.
+ */
+asmlinkage int compat_sys_sigsuspend(int restart, compat_ulong_t oldmask,
+ compat_old_sigset_t mask)
+{
+ sigset_t blocked;
+
+ siginitset(&current->blocked, mask);
+ return sigsuspend(&blocked);
+}
+
+asmlinkage int compat_sys_sigaction(int sig,
+ const struct compat_old_sigaction __user *act,
+ struct compat_old_sigaction __user *oact)
+{
+ struct k_sigaction new_ka, old_ka;
+ int ret;
+ compat_old_sigset_t mask;
+ compat_uptr_t handler, restorer;
+
+ if (act) {
+ if (!access_ok(VERIFY_READ, act, sizeof(*act)) ||
+ __get_user(handler, &act->sa_handler) ||
+ __get_user(restorer, &act->sa_restorer) ||
+ __get_user(new_ka.sa.sa_flags, &act->sa_flags) ||
+ __get_user(mask, &act->sa_mask))
+ return -EFAULT;
+
+ new_ka.sa.sa_handler = compat_ptr(handler);
+ new_ka.sa.sa_restorer = compat_ptr(restorer);
+ siginitset(&new_ka.sa.sa_mask, mask);
+ }
+
+ ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+
+ if (!ret && oact) {
+ if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) ||
+ __put_user(ptr_to_compat(old_ka.sa.sa_handler),
+ &oact->sa_handler) ||
+ __put_user(ptr_to_compat(old_ka.sa.sa_restorer),
+ &oact->sa_restorer) ||
+ __put_user(old_ka.sa.sa_flags, &oact->sa_flags) ||
+ __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask))
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+asmlinkage int compat_sys_rt_sigaction(int sig,
+ const struct compat_sigaction __user *act,
+ struct compat_sigaction __user *oact,
+ compat_size_t sigsetsize)
+{
+ struct k_sigaction new_ka, old_ka;
+ int ret;
+
+ /* XXX: Don't preclude handling different sized sigset_t's. */
+ if (sigsetsize != sizeof(compat_sigset_t))
+ return -EINVAL;
+
+ if (act) {
+ compat_uptr_t handler, restorer;
+
+ ret = get_user(handler, &act->sa_handler);
+ new_ka.sa.sa_handler = compat_ptr(handler);
+ ret |= get_user(restorer, &act->sa_restorer);
+ new_ka.sa.sa_restorer = compat_ptr(restorer);
+ ret |= get_sigset_t(&new_ka.sa.sa_mask, &act->sa_mask);
+ ret |= __get_user(new_ka.sa.sa_flags, &act->sa_flags);
+ if (ret)
+ return -EFAULT;
+ }
+
+ ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL);
+ if (!ret && oact) {
+ ret = put_user(ptr_to_compat(old_ka.sa.sa_handler), &oact->sa_handler);
+ ret |= put_sigset_t(&oact->sa_mask, &old_ka.sa.sa_mask);
+ ret |= __put_user(old_ka.sa.sa_flags, &oact->sa_flags);
+ }
+ return ret;
+}
+
+int compat_do_sigaltstack(compat_uptr_t compat_uss, compat_uptr_t compat_uoss,
+ compat_ulong_t sp)
+{
+ compat_stack_t __user *newstack = compat_ptr(compat_uss);
+ compat_stack_t __user *oldstack = compat_ptr(compat_uoss);
+ compat_uptr_t ss_sp;
+ int ret;
+ mm_segment_t old_fs;
+ stack_t uss, uoss;
+
+ /* Marshall the compat new stack into a stack_t */
+ if (newstack) {
+ if (get_user(ss_sp, &newstack->ss_sp) ||
+ __get_user(uss.ss_flags, &newstack->ss_flags) ||
+ __get_user(uss.ss_size, &newstack->ss_size))
+ return -EFAULT;
+ uss.ss_sp = compat_ptr(ss_sp);
+ }
+
+ old_fs = get_fs();
+ set_fs(KERNEL_DS);
+ /* The __user pointer casts are valid because of the set_fs() */
+ ret = do_sigaltstack(
+ newstack ? (stack_t __user *) &uss : NULL,
+ oldstack ? (stack_t __user *) &uoss : NULL,
+ (unsigned long)sp);
+ set_fs(old_fs);
+
+ /* Convert the old stack_t into a compat stack. */
+ if (!ret && oldstack &&
+ (put_user(ptr_to_compat(uoss.ss_sp), &oldstack->ss_sp) ||
+ __put_user(uoss.ss_flags, &oldstack->ss_flags) ||
+ __put_user(uoss.ss_size, &oldstack->ss_size)))
+ return -EFAULT;
+ return ret;
+}
+
+static int compat_restore_sigframe(struct pt_regs *regs,
+ struct compat_sigframe __user *sf)
+{
+ int err;
+ sigset_t set;
+ struct compat_aux_sigframe __user *aux;
+
+ err = get_sigset_t(&set, &sf->uc.uc_sigmask);
+ if (err == 0) {
+ sigdelsetmask(&set, ~_BLOCKABLE);
+ set_current_blocked(&set);
+ }
+
+ __get_user_error(regs->regs[0], &sf->uc.uc_mcontext.arm_r0, err);
+ __get_user_error(regs->regs[1], &sf->uc.uc_mcontext.arm_r1, err);
+ __get_user_error(regs->regs[2], &sf->uc.uc_mcontext.arm_r2, err);
+ __get_user_error(regs->regs[3], &sf->uc.uc_mcontext.arm_r3, err);
+ __get_user_error(regs->regs[4], &sf->uc.uc_mcontext.arm_r4, err);
+ __get_user_error(regs->regs[5], &sf->uc.uc_mcontext.arm_r5, err);
+ __get_user_error(regs->regs[6], &sf->uc.uc_mcontext.arm_r6, err);
+ __get_user_error(regs->regs[7], &sf->uc.uc_mcontext.arm_r7, err);
+ __get_user_error(regs->regs[8], &sf->uc.uc_mcontext.arm_r8, err);
+ __get_user_error(regs->regs[9], &sf->uc.uc_mcontext.arm_r9, err);
+ __get_user_error(regs->regs[10], &sf->uc.uc_mcontext.arm_r10, err);
+ __get_user_error(regs->regs[11], &sf->uc.uc_mcontext.arm_fp, err);
+ __get_user_error(regs->regs[12], &sf->uc.uc_mcontext.arm_ip, err);
+ __get_user_error(regs->compat_sp, &sf->uc.uc_mcontext.arm_sp, err);
+ __get_user_error(regs->compat_lr, &sf->uc.uc_mcontext.arm_lr, err);
+ __get_user_error(regs->pc, &sf->uc.uc_mcontext.arm_pc, err);
+ __get_user_error(regs->pstate, &sf->uc.uc_mcontext.arm_cpsr, err);
+
+ /*
+ * Avoid compat_sys_sigreturn() restarting.
+ */
+ regs->syscallno = ~0UL;
+
+ err |= !valid_user_regs(&regs->user_regs);
+
+ aux = (struct compat_aux_sigframe __user *) sf->uc.uc_regspace;
+ if (err == 0)
+ err |= compat_restore_vfp_context(&aux->vfp);
+
+ return err;
+}
+
+asmlinkage int compat_sys_sigreturn(struct pt_regs *regs)
+{
+ struct compat_sigframe __user *frame;
+
+ /* Always make any pending restarted system calls return -EINTR */
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+ /*
+ * Since we stacked the signal on a 64-bit boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (regs->compat_sp & 7)
+ goto badframe;
+
+ frame = (struct compat_sigframe __user *)regs->compat_sp;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof (*frame)))
+ goto badframe;
+
+ if (compat_restore_sigframe(regs, frame))
+ goto badframe;
+
+ return regs->regs[0];
+
+badframe:
+ if (show_unhandled_signals)
+ pr_info_ratelimited("%s[%d]: bad frame in %s: pc=%08llx sp=%08llx\n",
+ current->comm, task_pid_nr(current), __func__,
+ regs->pc, regs->sp);
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+asmlinkage int compat_sys_rt_sigreturn(struct pt_regs *regs)
+{
+ struct compat_rt_sigframe __user *frame;
+
+ /* Always make any pending restarted system calls return -EINTR */
+ current_thread_info()->restart_block.fn = do_no_restart_syscall;
+
+ /*
+ * Since we stacked the signal on a 64-bit boundary,
+ * then 'sp' should be word aligned here. If it's
+ * not, then the user is trying to mess with us.
+ */
+ if (regs->compat_sp & 7)
+ goto badframe;
+
+ frame = (struct compat_rt_sigframe __user *)regs->compat_sp;
+
+ if (!access_ok(VERIFY_READ, frame, sizeof (*frame)))
+ goto badframe;
+
+ if (compat_restore_sigframe(regs, &frame->sig))
+ goto badframe;
+
+ if (compat_do_sigaltstack(ptr_to_compat(&frame->sig.uc.uc_stack),
+ ptr_to_compat((void __user *)NULL),
+ regs->compat_sp) == -EFAULT)
+ goto badframe;
+
+ return regs->regs[0];
+
+badframe:
+ if (show_unhandled_signals)
+ pr_info_ratelimited("%s[%d]: bad frame in %s: pc=%08llx sp=%08llx\n",
+ current->comm, task_pid_nr(current), __func__,
+ regs->pc, regs->sp);
+ force_sig(SIGSEGV, current);
+ return 0;
+}
+
+static inline void __user *compat_get_sigframe(struct k_sigaction *ka,
+ struct pt_regs *regs,
+ int framesize)
+{
+ compat_ulong_t sp = regs->compat_sp;
+ void __user *frame;
+
+ /*
+ * This is the X/Open sanctioned signal stack switching.
+ */
+ if ((ka->sa.sa_flags & SA_ONSTACK) && !sas_ss_flags(sp))
+ sp = current->sas_ss_sp + current->sas_ss_size;
+
+ /*
+ * ATPCS B01 mandates 8-byte alignment
+ */
+ frame = compat_ptr((compat_uptr_t)((sp - framesize) & ~7));
+
+ /*
+ * Check that we can actually write to the signal frame.
+ */
+ if (!access_ok(VERIFY_WRITE, frame, framesize))
+ frame = NULL;
+
+ return frame;
+}
+
+static int compat_setup_return(struct pt_regs *regs, struct k_sigaction *ka,
+ compat_ulong_t __user *rc, void __user *frame,
+ int usig)
+{
+ compat_ulong_t handler = ptr_to_compat(ka->sa.sa_handler);
+ compat_ulong_t retcode;
+ compat_ulong_t spsr = regs->pstate & ~PSR_f;
+ int thumb;
+
+ /* Check if the handler is written for ARM or Thumb */
+ thumb = handler & 1;
+
+ if (thumb) {
+ spsr |= COMPAT_PSR_T_BIT;
+ spsr &= ~COMPAT_PSR_IT_MASK;
+ } else {
+ spsr &= ~COMPAT_PSR_T_BIT;
+ }
+
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ retcode = ptr_to_compat(ka->sa.sa_restorer);
+ } else {
+ /* Set up sigreturn pointer */
+ unsigned int idx = thumb << 1;
+
+ if (ka->sa.sa_flags & SA_SIGINFO)
+ idx += 3;
+
+ retcode = AARCH32_VECTORS_BASE +
+ AARCH32_KERN_SIGRET_CODE_OFFSET +
+ (idx << 2) + thumb;
+ }
+
+ regs->regs[0] = usig;
+ regs->compat_sp = ptr_to_compat(frame);
+ regs->compat_lr = retcode;
+ regs->pc = handler;
+ regs->pstate = spsr;
+
+ return 0;
+}
+
+static int compat_setup_sigframe(struct compat_sigframe __user *sf,
+ struct pt_regs *regs, sigset_t *set)
+{
+ struct compat_aux_sigframe __user *aux;
+ int err = 0;
+
+ __put_user_error(regs->regs[0], &sf->uc.uc_mcontext.arm_r0, err);
+ __put_user_error(regs->regs[1], &sf->uc.uc_mcontext.arm_r1, err);
+ __put_user_error(regs->regs[2], &sf->uc.uc_mcontext.arm_r2, err);
+ __put_user_error(regs->regs[3], &sf->uc.uc_mcontext.arm_r3, err);
+ __put_user_error(regs->regs[4], &sf->uc.uc_mcontext.arm_r4, err);
+ __put_user_error(regs->regs[5], &sf->uc.uc_mcontext.arm_r5, err);
+ __put_user_error(regs->regs[6], &sf->uc.uc_mcontext.arm_r6, err);
+ __put_user_error(regs->regs[7], &sf->uc.uc_mcontext.arm_r7, err);
+ __put_user_error(regs->regs[8], &sf->uc.uc_mcontext.arm_r8, err);
+ __put_user_error(regs->regs[9], &sf->uc.uc_mcontext.arm_r9, err);
+ __put_user_error(regs->regs[10], &sf->uc.uc_mcontext.arm_r10, err);
+ __put_user_error(regs->regs[11], &sf->uc.uc_mcontext.arm_fp, err);
+ __put_user_error(regs->regs[12], &sf->uc.uc_mcontext.arm_ip, err);
+ __put_user_error(regs->compat_sp, &sf->uc.uc_mcontext.arm_sp, err);
+ __put_user_error(regs->compat_lr, &sf->uc.uc_mcontext.arm_lr, err);
+ __put_user_error(regs->pc, &sf->uc.uc_mcontext.arm_pc, err);
+ __put_user_error(regs->pstate, &sf->uc.uc_mcontext.arm_cpsr, err);
+
+ __put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.trap_no, err);
+ __put_user_error((compat_ulong_t)0, &sf->uc.uc_mcontext.error_code, err);
+ __put_user_error(current->thread.fault_address, &sf->uc.uc_mcontext.fault_address, err);
+ __put_user_error(set->sig[0], &sf->uc.uc_mcontext.oldmask, err);
+
+ err |= put_sigset_t(&sf->uc.uc_sigmask, set);
+
+ aux = (struct compat_aux_sigframe __user *) sf->uc.uc_regspace;
+
+ if (err == 0)
+ err |= compat_preserve_vfp_context(&aux->vfp);
+ __put_user_error(0, &aux->end_magic, err);
+
+ return err;
+}
+
+/*
+ * 32-bit signal handling routines called from signal.c
+ */
+int compat_setup_rt_frame(int usig, struct k_sigaction *ka, siginfo_t *info,
+ sigset_t *set, struct pt_regs *regs)
+{
+ struct compat_rt_sigframe __user *frame;
+ compat_stack_t stack;
+ int err = 0;
+
+ frame = compat_get_sigframe(ka, regs, sizeof(*frame));
+
+ if (!frame)
+ return 1;
+
+ err |= copy_siginfo_to_user32(&frame->info, info);
+
+ __put_user_error(0, &frame->sig.uc.uc_flags, err);
+ __put_user_error(NULL, &frame->sig.uc.uc_link, err);
+
+ memset(&stack, 0, sizeof(stack));
+ stack.ss_sp = (compat_uptr_t)current->sas_ss_sp;
+ stack.ss_flags = sas_ss_flags(regs->compat_sp);
+ stack.ss_size = current->sas_ss_size;
+ err |= __copy_to_user(&frame->sig.uc.uc_stack, &stack, sizeof(stack));
+
+ err |= compat_setup_sigframe(&frame->sig, regs, set);
+ if (err == 0)
+ err = compat_setup_return(regs, ka, frame->sig.retcode, frame,
+ usig);
+
+ if (err == 0) {
+ regs->regs[1] = (compat_ulong_t)(unsigned long)&frame->info;
+ regs->regs[2] = (compat_ulong_t)(unsigned long)&frame->sig.uc;
+ }
+
+ return err;
+}
+
+int compat_setup_frame(int usig, struct k_sigaction *ka, sigset_t *set,
+ struct pt_regs *regs)
+{
+ struct compat_sigframe __user *frame;
+ int err = 0;
+
+ frame = compat_get_sigframe(ka, regs, sizeof(*frame));
+
+ if (!frame)
+ return 1;
+
+ __put_user_error(0x5ac3c35a, &frame->uc.uc_flags, err);
+
+ err |= compat_setup_sigframe(frame, regs, set);
+ if (err == 0)
+ err = compat_setup_return(regs, ka, frame->retcode, frame, usig);
+
+ return err;
+}
+
+/*
+ * RT signals don't have generic compat wrappers.
+ * See arch/powerpc/kernel/signal_32.c
+ */
+asmlinkage int compat_sys_rt_sigprocmask(int how, compat_sigset_t __user *set,
+ compat_sigset_t __user *oset,
+ compat_size_t sigsetsize)
+{
+ sigset_t s;
+ sigset_t __user *up;
+ int ret;
+ mm_segment_t old_fs = get_fs();
+
+ if (set) {
+ if (get_sigset_t(&s, set))
+ return -EFAULT;
+ }
+
+ set_fs(KERNEL_DS);
+ /* This is valid because of the set_fs() */
+ up = (sigset_t __user *) &s;
+ ret = sys_rt_sigprocmask(how, set ? up : NULL, oset ? up : NULL,
+ sigsetsize);
+ set_fs(old_fs);
+ if (ret)
+ return ret;
+ if (oset) {
+ if (put_sigset_t(oset, &s))
+ return -EFAULT;
+ }
+ return 0;
+}
+
+asmlinkage int compat_sys_rt_sigpending(compat_sigset_t __user *set,
+ compat_size_t sigsetsize)
+{
+ sigset_t s;
+ int ret;
+ mm_segment_t old_fs = get_fs();
+
+ set_fs(KERNEL_DS);
+ /* The __user pointer cast is valid because of the set_fs() */
+ ret = sys_rt_sigpending((sigset_t __user *) &s, sigsetsize);
+ set_fs(old_fs);
+ if (!ret) {
+ if (put_sigset_t(set, &s))
+ return -EFAULT;
+ }
+ return ret;
+}
+
+asmlinkage int compat_sys_rt_sigqueueinfo(int pid, int sig,
+ compat_siginfo_t __user *uinfo)
+{
+ siginfo_t info;
+ int ret;
+ mm_segment_t old_fs = get_fs();
+
+ ret = copy_siginfo_from_user32(&info, uinfo);
+ if (unlikely(ret))
+ return ret;
+
+ set_fs (KERNEL_DS);
+ /* The __user pointer cast is valid because of the set_fs() */
+ ret = sys_rt_sigqueueinfo(pid, sig, (siginfo_t __user *) &info);
+ set_fs (old_fs);
+ return ret;
+}
+
+void compat_setup_restart_syscall(struct pt_regs *regs)
+{
+ regs->regs[7] = __NR_restart_syscall;
+}
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
new file mode 100644
index 000000000000..b711525be21f
--- /dev/null
+++ b/arch/arm64/kernel/smp.c
@@ -0,0 +1,469 @@
+/*
+ * SMP initialisation and IPI support
+ * Based on arch/arm/kernel/smp.c
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/cache.h>
+#include <linux/profile.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/err.h>
+#include <linux/cpu.h>
+#include <linux/smp.h>
+#include <linux/seq_file.h>
+#include <linux/irq.h>
+#include <linux/percpu.h>
+#include <linux/clockchips.h>
+#include <linux/completion.h>
+#include <linux/of.h>
+
+#include <asm/atomic.h>
+#include <asm/cacheflush.h>
+#include <asm/cputype.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/processor.h>
+#include <asm/sections.h>
+#include <asm/tlbflush.h>
+#include <asm/ptrace.h>
+#include <asm/mmu_context.h>
+
+/*
+ * as from 2.5, kernels no longer have an init_tasks structure
+ * so we need some other way of telling a new secondary core
+ * where to place its SVC stack
+ */
+struct secondary_data secondary_data;
+volatile unsigned long secondary_holding_pen_release = -1;
+
+enum ipi_msg_type {
+ IPI_RESCHEDULE,
+ IPI_CALL_FUNC,
+ IPI_CALL_FUNC_SINGLE,
+ IPI_CPU_STOP,
+};
+
+static DEFINE_RAW_SPINLOCK(boot_lock);
+
+/*
+ * Write secondary_holding_pen_release in a way that is guaranteed to be
+ * visible to all observers, irrespective of whether they're taking part
+ * in coherency or not. This is necessary for the hotplug code to work
+ * reliably.
+ */
+static void __cpuinit write_pen_release(int val)
+{
+ void *start = (void *)&secondary_holding_pen_release;
+ unsigned long size = sizeof(secondary_holding_pen_release);
+
+ secondary_holding_pen_release = val;
+ __flush_dcache_area(start, size);
+}
+
+/*
+ * Boot a secondary CPU, and assign it the specified idle task.
+ * This also gives us the initial stack to use for this CPU.
+ */
+static int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ unsigned long timeout;
+
+ /*
+ * Set synchronisation state between this boot processor
+ * and the secondary one
+ */
+ raw_spin_lock(&boot_lock);
+
+ /*
+ * Update the pen release flag.
+ */
+ write_pen_release(cpu);
+
+ /*
+ * Send an event, causing the secondaries to read pen_release.
+ */
+ sev();
+
+ timeout = jiffies + (1 * HZ);
+ while (time_before(jiffies, timeout)) {
+ if (secondary_holding_pen_release == -1UL)
+ break;
+ udelay(10);
+ }
+
+ /*
+ * Now the secondary core is starting up let it run its
+ * calibrations, then wait for it to finish
+ */
+ raw_spin_unlock(&boot_lock);
+
+ return secondary_holding_pen_release != -1 ? -ENOSYS : 0;
+}
+
+static DECLARE_COMPLETION(cpu_running);
+
+int __cpuinit __cpu_up(unsigned int cpu, struct task_struct *idle)
+{
+ int ret;
+
+ /*
+ * We need to tell the secondary core where to find its stack and the
+ * page tables.
+ */
+ secondary_data.stack = task_stack_page(idle) + THREAD_START_SP;
+ __flush_dcache_area(&secondary_data, sizeof(secondary_data));
+
+ /*
+ * Now bring the CPU into our world.
+ */
+ ret = boot_secondary(cpu, idle);
+ if (ret == 0) {
+ /*
+ * CPU was successfully started, wait for it to come online or
+ * time out.
+ */
+ wait_for_completion_timeout(&cpu_running,
+ msecs_to_jiffies(1000));
+
+ if (!cpu_online(cpu)) {
+ pr_crit("CPU%u: failed to come online\n", cpu);
+ ret = -EIO;
+ }
+ } else {
+ pr_err("CPU%u: failed to boot: %d\n", cpu, ret);
+ }
+
+ secondary_data.stack = NULL;
+
+ return ret;
+}
+
+/*
+ * This is the secondary CPU boot entry. We're using this CPUs
+ * idle thread stack, but a set of temporary page tables.
+ */
+asmlinkage void __cpuinit secondary_start_kernel(void)
+{
+ struct mm_struct *mm = &init_mm;
+ unsigned int cpu = smp_processor_id();
+
+ printk("CPU%u: Booted secondary processor\n", cpu);
+
+ /*
+ * All kernel threads share the same mm context; grab a
+ * reference and switch to it.
+ */
+ atomic_inc(&mm->mm_count);
+ current->active_mm = mm;
+ cpumask_set_cpu(cpu, mm_cpumask(mm));
+
+ /*
+ * TTBR0 is only used for the identity mapping at this stage. Make it
+ * point to zero page to avoid speculatively fetching new entries.
+ */
+ cpu_set_reserved_ttbr0();
+ flush_tlb_all();
+
+ preempt_disable();
+ trace_hardirqs_off();
+
+ /*
+ * Let the primary processor know we're out of the
+ * pen, then head off into the C entry point
+ */
+ write_pen_release(-1);
+
+ /*
+ * Synchronise with the boot thread.
+ */
+ raw_spin_lock(&boot_lock);
+ raw_spin_unlock(&boot_lock);
+
+ /*
+ * Enable local interrupts.
+ */
+ notify_cpu_starting(cpu);
+ local_irq_enable();
+ local_fiq_enable();
+
+ /*
+ * OK, now it's safe to let the boot CPU continue. Wait for
+ * the CPU migration code to notice that the CPU is online
+ * before we continue.
+ */
+ set_cpu_online(cpu, true);
+ while (!cpu_active(cpu))
+ cpu_relax();
+
+ /*
+ * OK, it's off to the idle thread for us
+ */
+ cpu_idle();
+}
+
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+ unsigned long bogosum = loops_per_jiffy * num_online_cpus();
+
+ pr_info("SMP: Total of %d processors activated (%lu.%02lu BogoMIPS).\n",
+ num_online_cpus(), bogosum / (500000/HZ),
+ (bogosum / (5000/HZ)) % 100);
+}
+
+void __init smp_prepare_boot_cpu(void)
+{
+}
+
+static void (*smp_cross_call)(const struct cpumask *, unsigned int);
+static phys_addr_t cpu_release_addr[NR_CPUS];
+
+/*
+ * Enumerate the possible CPU set from the device tree.
+ */
+void __init smp_init_cpus(void)
+{
+ const char *enable_method;
+ struct device_node *dn = NULL;
+ int cpu = 0;
+
+ while ((dn = of_find_node_by_type(dn, "cpu"))) {
+ if (cpu >= NR_CPUS)
+ goto next;
+
+ /*
+ * We currently support only the "spin-table" enable-method.
+ */
+ enable_method = of_get_property(dn, "enable-method", NULL);
+ if (!enable_method || strcmp(enable_method, "spin-table")) {
+ pr_err("CPU %d: missing or invalid enable-method property: %s\n",
+ cpu, enable_method);
+ goto next;
+ }
+
+ /*
+ * Determine the address from which the CPU is polling.
+ */
+ if (of_property_read_u64(dn, "cpu-release-addr",
+ &cpu_release_addr[cpu])) {
+ pr_err("CPU %d: missing or invalid cpu-release-addr property\n",
+ cpu);
+ goto next;
+ }
+
+ set_cpu_possible(cpu, true);
+next:
+ cpu++;
+ }
+
+ /* sanity check */
+ if (cpu > NR_CPUS)
+ pr_warning("no. of cores (%d) greater than configured maximum of %d - clipping\n",
+ cpu, NR_CPUS);
+}
+
+void __init smp_prepare_cpus(unsigned int max_cpus)
+{
+ int cpu;
+ void **release_addr;
+ unsigned int ncores = num_possible_cpus();
+
+ /*
+ * are we trying to boot more cores than exist?
+ */
+ if (max_cpus > ncores)
+ max_cpus = ncores;
+
+ /*
+ * Initialise the present map (which describes the set of CPUs
+ * actually populated at the present time) and release the
+ * secondaries from the bootloader.
+ */
+ for_each_possible_cpu(cpu) {
+ if (max_cpus == 0)
+ break;
+
+ if (!cpu_release_addr[cpu])
+ continue;
+
+ release_addr = __va(cpu_release_addr[cpu]);
+ release_addr[0] = (void *)__pa(secondary_holding_pen);
+ __flush_dcache_area(release_addr, sizeof(release_addr[0]));
+
+ set_cpu_present(cpu, true);
+ max_cpus--;
+ }
+
+ /*
+ * Send an event to wake up the secondaries.
+ */
+ sev();
+}
+
+
+void __init set_smp_cross_call(void (*fn)(const struct cpumask *, unsigned int))
+{
+ smp_cross_call = fn;
+}
+
+void arch_send_call_function_ipi_mask(const struct cpumask *mask)
+{
+ smp_cross_call(mask, IPI_CALL_FUNC);
+}
+
+void arch_send_call_function_single_ipi(int cpu)
+{
+ smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC_SINGLE);
+}
+
+static const char *ipi_types[NR_IPI] = {
+#define S(x,s) [x - IPI_RESCHEDULE] = s
+ S(IPI_RESCHEDULE, "Rescheduling interrupts"),
+ S(IPI_CALL_FUNC, "Function call interrupts"),
+ S(IPI_CALL_FUNC_SINGLE, "Single function call interrupts"),
+ S(IPI_CPU_STOP, "CPU stop interrupts"),
+};
+
+void show_ipi_list(struct seq_file *p, int prec)
+{
+ unsigned int cpu, i;
+
+ for (i = 0; i < NR_IPI; i++) {
+ seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i + IPI_RESCHEDULE,
+ prec >= 4 ? " " : "");
+ for_each_present_cpu(cpu)
+ seq_printf(p, "%10u ",
+ __get_irq_stat(cpu, ipi_irqs[i]));
+ seq_printf(p, " %s\n", ipi_types[i]);
+ }
+}
+
+u64 smp_irq_stat_cpu(unsigned int cpu)
+{
+ u64 sum = 0;
+ int i;
+
+ for (i = 0; i < NR_IPI; i++)
+ sum += __get_irq_stat(cpu, ipi_irqs[i]);
+
+ return sum;
+}
+
+static DEFINE_RAW_SPINLOCK(stop_lock);
+
+/*
+ * ipi_cpu_stop - handle IPI from smp_send_stop()
+ */
+static void ipi_cpu_stop(unsigned int cpu)
+{
+ if (system_state == SYSTEM_BOOTING ||
+ system_state == SYSTEM_RUNNING) {
+ raw_spin_lock(&stop_lock);
+ pr_crit("CPU%u: stopping\n", cpu);
+ dump_stack();
+ raw_spin_unlock(&stop_lock);
+ }
+
+ set_cpu_online(cpu, false);
+
+ local_fiq_disable();
+ local_irq_disable();
+
+ while (1)
+ cpu_relax();
+}
+
+/*
+ * Main handler for inter-processor interrupts
+ */
+void handle_IPI(int ipinr, struct pt_regs *regs)
+{
+ unsigned int cpu = smp_processor_id();
+ struct pt_regs *old_regs = set_irq_regs(regs);
+
+ if (ipinr >= IPI_RESCHEDULE && ipinr < IPI_RESCHEDULE + NR_IPI)
+ __inc_irq_stat(cpu, ipi_irqs[ipinr - IPI_RESCHEDULE]);
+
+ switch (ipinr) {
+ case IPI_RESCHEDULE:
+ scheduler_ipi();
+ break;
+
+ case IPI_CALL_FUNC:
+ irq_enter();
+ generic_smp_call_function_interrupt();
+ irq_exit();
+ break;
+
+ case IPI_CALL_FUNC_SINGLE:
+ irq_enter();
+ generic_smp_call_function_single_interrupt();
+ irq_exit();
+ break;
+
+ case IPI_CPU_STOP:
+ irq_enter();
+ ipi_cpu_stop(cpu);
+ irq_exit();
+ break;
+
+ default:
+ pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr);
+ break;
+ }
+ set_irq_regs(old_regs);
+}
+
+void smp_send_reschedule(int cpu)
+{
+ smp_cross_call(cpumask_of(cpu), IPI_RESCHEDULE);
+}
+
+void smp_send_stop(void)
+{
+ unsigned long timeout;
+
+ if (num_online_cpus() > 1) {
+ cpumask_t mask;
+
+ cpumask_copy(&mask, cpu_online_mask);
+ cpu_clear(smp_processor_id(), mask);
+
+ smp_cross_call(&mask, IPI_CPU_STOP);
+ }
+
+ /* Wait up to one second for other CPUs to stop */
+ timeout = USEC_PER_SEC;
+ while (num_online_cpus() > 1 && timeout--)
+ udelay(1);
+
+ if (num_online_cpus() > 1)
+ pr_warning("SMP: failed to stop secondary CPUs\n");
+}
+
+/*
+ * not supported here
+ */
+int setup_profiling_timer(unsigned int multiplier)
+{
+ return -EINVAL;
+}
diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c
new file mode 100644
index 000000000000..d25459ff57fc
--- /dev/null
+++ b/arch/arm64/kernel/stacktrace.c
@@ -0,0 +1,127 @@
+/*
+ * Stack tracing support
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/stacktrace.h>
+
+#include <asm/stacktrace.h>
+
+/*
+ * AArch64 PCS assigns the frame pointer to x29.
+ *
+ * A simple function prologue looks like this:
+ * sub sp, sp, #0x10
+ * stp x29, x30, [sp]
+ * mov x29, sp
+ *
+ * A simple function epilogue looks like this:
+ * mov sp, x29
+ * ldp x29, x30, [sp]
+ * add sp, sp, #0x10
+ */
+int unwind_frame(struct stackframe *frame)
+{
+ unsigned long high, low;
+ unsigned long fp = frame->fp;
+
+ low = frame->sp;
+ high = ALIGN(low, THREAD_SIZE);
+
+ if (fp < low || fp > high || fp & 0xf)
+ return -EINVAL;
+
+ frame->sp = fp + 0x10;
+ frame->fp = *(unsigned long *)(fp);
+ frame->pc = *(unsigned long *)(fp + 8);
+
+ return 0;
+}
+
+void notrace walk_stackframe(struct stackframe *frame,
+ int (*fn)(struct stackframe *, void *), void *data)
+{
+ while (1) {
+ int ret;
+
+ if (fn(frame, data))
+ break;
+ ret = unwind_frame(frame);
+ if (ret < 0)
+ break;
+ }
+}
+EXPORT_SYMBOL(walk_stackframe);
+
+#ifdef CONFIG_STACKTRACE
+struct stack_trace_data {
+ struct stack_trace *trace;
+ unsigned int no_sched_functions;
+ unsigned int skip;
+};
+
+static int save_trace(struct stackframe *frame, void *d)
+{
+ struct stack_trace_data *data = d;
+ struct stack_trace *trace = data->trace;
+ unsigned long addr = frame->pc;
+
+ if (data->no_sched_functions && in_sched_functions(addr))
+ return 0;
+ if (data->skip) {
+ data->skip--;
+ return 0;
+ }
+
+ trace->entries[trace->nr_entries++] = addr;
+
+ return trace->nr_entries >= trace->max_entries;
+}
+
+void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
+{
+ struct stack_trace_data data;
+ struct stackframe frame;
+
+ data.trace = trace;
+ data.skip = trace->skip;
+
+ if (tsk != current) {
+ data.no_sched_functions = 1;
+ frame.fp = thread_saved_fp(tsk);
+ frame.sp = thread_saved_sp(tsk);
+ frame.pc = thread_saved_pc(tsk);
+ } else {
+ register unsigned long current_sp asm("sp");
+ data.no_sched_functions = 0;
+ frame.fp = (unsigned long)__builtin_frame_address(0);
+ frame.sp = current_sp;
+ frame.pc = (unsigned long)save_stack_trace_tsk;
+ }
+
+ walk_stackframe(&frame, save_trace, &data);
+ if (trace->nr_entries < trace->max_entries)
+ trace->entries[trace->nr_entries++] = ULONG_MAX;
+}
+
+void save_stack_trace(struct stack_trace *trace)
+{
+ save_stack_trace_tsk(current, trace);
+}
+EXPORT_SYMBOL_GPL(save_stack_trace);
+#endif
diff --git a/arch/arm64/kernel/sys.c b/arch/arm64/kernel/sys.c
new file mode 100644
index 000000000000..905fcfb0ddd0
--- /dev/null
+++ b/arch/arm64/kernel/sys.c
@@ -0,0 +1,138 @@
+/*
+ * AArch64-specific system calls implementation
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Catalin Marinas <catalin.marinas@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+
+/*
+ * Clone a task - this clones the calling program thread.
+ */
+asmlinkage long sys_clone(unsigned long clone_flags, unsigned long newsp,
+ int __user *parent_tidptr, unsigned long tls_val,
+ int __user *child_tidptr, struct pt_regs *regs)
+{
+ if (!newsp)
+ newsp = regs->sp;
+ /* 16-byte aligned stack mandatory on AArch64 */
+ if (newsp & 15)
+ return -EINVAL;
+ return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
+}
+
+/*
+ * sys_execve() executes a new program.
+ */
+asmlinkage long sys_execve(const char __user *filenamei,
+ const char __user *const __user *argv,
+ const char __user *const __user *envp,
+ struct pt_regs *regs)
+{
+ long error;
+ char * filename;
+
+ filename = getname(filenamei);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ goto out;
+ error = do_execve(filename, argv, envp, regs);
+ putname(filename);
+out:
+ return error;
+}
+
+int kernel_execve(const char *filename,
+ const char *const argv[],
+ const char *const envp[])
+{
+ struct pt_regs regs;
+ int ret;
+
+ memset(&regs, 0, sizeof(struct pt_regs));
+ ret = do_execve(filename,
+ (const char __user *const __user *)argv,
+ (const char __user *const __user *)envp, &regs);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Save argc to the register structure for userspace.
+ */
+ regs.regs[0] = ret;
+
+ /*
+ * We were successful. We won't be returning to our caller, but
+ * instead to user space by manipulating the kernel stack.
+ */
+ asm( "add x0, %0, %1\n\t"
+ "mov x1, %2\n\t"
+ "mov x2, %3\n\t"
+ "bl memmove\n\t" /* copy regs to top of stack */
+ "mov x27, #0\n\t" /* not a syscall */
+ "mov x28, %0\n\t" /* thread structure */
+ "mov sp, x0\n\t" /* reposition stack pointer */
+ "b ret_to_user"
+ :
+ : "r" (current_thread_info()),
+ "Ir" (THREAD_START_SP - sizeof(regs)),
+ "r" (&regs),
+ "Ir" (sizeof(regs))
+ : "x0", "x1", "x2", "x27", "x28", "x30", "memory");
+
+ out:
+ return ret;
+}
+EXPORT_SYMBOL(kernel_execve);
+
+asmlinkage long sys_mmap(unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags,
+ unsigned long fd, off_t off)
+{
+ if (offset_in_page(off) != 0)
+ return -EINVAL;
+
+ return sys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
+}
+
+/*
+ * Wrappers to pass the pt_regs argument.
+ */
+#define sys_execve sys_execve_wrapper
+#define sys_clone sys_clone_wrapper
+#define sys_rt_sigreturn sys_rt_sigreturn_wrapper
+#define sys_sigaltstack sys_sigaltstack_wrapper
+
+#include <asm/syscalls.h>
+
+#undef __SYSCALL
+#define __SYSCALL(nr, sym) [nr] = sym,
+
+/*
+ * The sys_call_table array must be 4K aligned to be accessible from
+ * kernel/entry.S.
+ */
+void *sys_call_table[__NR_syscalls] __aligned(4096) = {
+ [0 ... __NR_syscalls - 1] = sys_ni_syscall,
+#include <asm/unistd.h>
+};
diff --git a/arch/arm64/kernel/sys32.S b/arch/arm64/kernel/sys32.S
new file mode 100644
index 000000000000..5e4dc93cc31f
--- /dev/null
+++ b/arch/arm64/kernel/sys32.S
@@ -0,0 +1,282 @@
+/*
+ * Compat system call wrappers
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Authors: Will Deacon <will.deacon@arm.com>
+ * Catalin Marinas <catalin.marinas@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+
+#include <asm/assembler.h>
+#include <asm/asm-offsets.h>
+
+/*
+ * System call wrappers for the AArch32 compatibility layer.
+ */
+compat_sys_fork_wrapper:
+ mov x0, sp
+ b compat_sys_fork
+ENDPROC(compat_sys_fork_wrapper)
+
+compat_sys_vfork_wrapper:
+ mov x0, sp
+ b compat_sys_vfork
+ENDPROC(compat_sys_vfork_wrapper)
+
+compat_sys_execve_wrapper:
+ mov x3, sp
+ b compat_sys_execve
+ENDPROC(compat_sys_execve_wrapper)
+
+compat_sys_clone_wrapper:
+ mov x5, sp
+ b compat_sys_clone
+ENDPROC(compat_sys_clone_wrapper)
+
+compat_sys_sigreturn_wrapper:
+ mov x0, sp
+ mov x27, #0 // prevent syscall restart handling (why)
+ b compat_sys_sigreturn
+ENDPROC(compat_sys_sigreturn_wrapper)
+
+compat_sys_rt_sigreturn_wrapper:
+ mov x0, sp
+ mov x27, #0 // prevent syscall restart handling (why)
+ b compat_sys_rt_sigreturn
+ENDPROC(compat_sys_rt_sigreturn_wrapper)
+
+compat_sys_sigaltstack_wrapper:
+ ldr x2, [sp, #S_COMPAT_SP]
+ b compat_do_sigaltstack
+ENDPROC(compat_sys_sigaltstack_wrapper)
+
+compat_sys_statfs64_wrapper:
+ mov w3, #84
+ cmp w1, #88
+ csel w1, w3, w1, eq
+ b compat_sys_statfs64
+ENDPROC(compat_sys_statfs64_wrapper)
+
+compat_sys_fstatfs64_wrapper:
+ mov w3, #84
+ cmp w1, #88
+ csel w1, w3, w1, eq
+ b compat_sys_fstatfs64
+ENDPROC(compat_sys_fstatfs64_wrapper)
+
+/*
+ * Wrappers for AArch32 syscalls that either take 64-bit parameters
+ * in registers or that take 32-bit parameters which require sign
+ * extension.
+ */
+compat_sys_lseek_wrapper:
+ sxtw x1, w1
+ b sys_lseek
+ENDPROC(compat_sys_lseek_wrapper)
+
+compat_sys_pread64_wrapper:
+ orr x3, x4, x5, lsl #32
+ b sys_pread64
+ENDPROC(compat_sys_pread64_wrapper)
+
+compat_sys_pwrite64_wrapper:
+ orr x3, x4, x5, lsl #32
+ b sys_pwrite64
+ENDPROC(compat_sys_pwrite64_wrapper)
+
+compat_sys_truncate64_wrapper:
+ orr x1, x2, x3, lsl #32
+ b sys_truncate
+ENDPROC(compat_sys_truncate64_wrapper)
+
+compat_sys_ftruncate64_wrapper:
+ orr x1, x2, x3, lsl #32
+ b sys_ftruncate
+ENDPROC(compat_sys_ftruncate64_wrapper)
+
+compat_sys_readahead_wrapper:
+ orr x1, x2, x3, lsl #32
+ mov w2, w4
+ b sys_readahead
+ENDPROC(compat_sys_readahead_wrapper)
+
+compat_sys_lookup_dcookie:
+ orr x0, x0, x1, lsl #32
+ mov w1, w2
+ mov w2, w3
+ b sys_lookup_dcookie
+ENDPROC(compat_sys_lookup_dcookie)
+
+compat_sys_fadvise64_64_wrapper:
+ mov w6, w1
+ orr x1, x2, x3, lsl #32
+ orr x2, x4, x5, lsl #32
+ mov w3, w6
+ b sys_fadvise64_64
+ENDPROC(compat_sys_fadvise64_64_wrapper)
+
+compat_sys_sync_file_range2_wrapper:
+ orr x2, x2, x3, lsl #32
+ orr x3, x4, x5, lsl #32
+ b sys_sync_file_range2
+ENDPROC(compat_sys_sync_file_range2_wrapper)
+
+compat_sys_fallocate_wrapper:
+ orr x2, x2, x3, lsl #32
+ orr x3, x4, x5, lsl #32
+ b sys_fallocate
+ENDPROC(compat_sys_fallocate_wrapper)
+
+compat_sys_fanotify_mark_wrapper:
+ orr x2, x2, x3, lsl #32
+ mov w3, w4
+ mov w4, w5
+ b sys_fanotify_mark
+ENDPROC(compat_sys_fanotify_mark_wrapper)
+
+/*
+ * Use the compat system call wrappers.
+ */
+#define sys_fork compat_sys_fork_wrapper
+#define sys_open compat_sys_open
+#define sys_execve compat_sys_execve_wrapper
+#define sys_lseek compat_sys_lseek_wrapper
+#define sys_mount compat_sys_mount
+#define sys_ptrace compat_sys_ptrace
+#define sys_times compat_sys_times
+#define sys_ioctl compat_sys_ioctl
+#define sys_fcntl compat_sys_fcntl
+#define sys_ustat compat_sys_ustat
+#define sys_sigaction compat_sys_sigaction
+#define sys_sigsuspend compat_sys_sigsuspend
+#define sys_sigpending compat_sys_sigpending
+#define sys_setrlimit compat_sys_setrlimit
+#define sys_getrusage compat_sys_getrusage
+#define sys_gettimeofday compat_sys_gettimeofday
+#define sys_settimeofday compat_sys_settimeofday
+#define sys_statfs compat_sys_statfs
+#define sys_fstatfs compat_sys_fstatfs
+#define sys_setitimer compat_sys_setitimer
+#define sys_getitimer compat_sys_getitimer
+#define sys_newstat compat_sys_newstat
+#define sys_newlstat compat_sys_newlstat
+#define sys_newfstat compat_sys_newfstat
+#define sys_wait4 compat_sys_wait4
+#define sys_sysinfo compat_sys_sysinfo
+#define sys_sigreturn compat_sys_sigreturn_wrapper
+#define sys_clone compat_sys_clone_wrapper
+#define sys_adjtimex compat_sys_adjtimex
+#define sys_sigprocmask compat_sys_sigprocmask
+#define sys_getdents compat_sys_getdents
+#define sys_select compat_sys_select
+#define sys_readv compat_sys_readv
+#define sys_writev compat_sys_writev
+#define sys_sysctl compat_sys_sysctl
+#define sys_sched_rr_get_interval compat_sys_sched_rr_get_interval
+#define sys_nanosleep compat_sys_nanosleep
+#define sys_rt_sigreturn compat_sys_rt_sigreturn_wrapper
+#define sys_rt_sigaction compat_sys_rt_sigaction
+#define sys_rt_sigprocmask compat_sys_rt_sigprocmask
+#define sys_rt_sigpending compat_sys_rt_sigpending
+#define sys_rt_sigtimedwait compat_sys_rt_sigtimedwait
+#define sys_rt_sigqueueinfo compat_sys_rt_sigqueueinfo
+#define sys_rt_sigsuspend compat_sys_rt_sigsuspend
+#define sys_pread64 compat_sys_pread64_wrapper
+#define sys_pwrite64 compat_sys_pwrite64_wrapper
+#define sys_sigaltstack compat_sys_sigaltstack_wrapper
+#define sys_sendfile compat_sys_sendfile
+#define sys_vfork compat_sys_vfork_wrapper
+#define sys_getrlimit compat_sys_getrlimit
+#define sys_mmap2 sys_mmap_pgoff
+#define sys_truncate64 compat_sys_truncate64_wrapper
+#define sys_ftruncate64 compat_sys_ftruncate64_wrapper
+#define sys_getdents64 compat_sys_getdents64
+#define sys_fcntl64 compat_sys_fcntl64
+#define sys_readahead compat_sys_readahead_wrapper
+#define sys_futex compat_sys_futex
+#define sys_sched_setaffinity compat_sys_sched_setaffinity
+#define sys_sched_getaffinity compat_sys_sched_getaffinity
+#define sys_io_setup compat_sys_io_setup
+#define sys_io_getevents compat_sys_io_getevents
+#define sys_io_submit compat_sys_io_submit
+#define sys_lookup_dcookie compat_sys_lookup_dcookie
+#define sys_timer_create compat_sys_timer_create
+#define sys_timer_settime compat_sys_timer_settime
+#define sys_timer_gettime compat_sys_timer_gettime
+#define sys_clock_settime compat_sys_clock_settime
+#define sys_clock_gettime compat_sys_clock_gettime
+#define sys_clock_getres compat_sys_clock_getres
+#define sys_clock_nanosleep compat_sys_clock_nanosleep
+#define sys_statfs64 compat_sys_statfs64_wrapper
+#define sys_fstatfs64 compat_sys_fstatfs64_wrapper
+#define sys_utimes compat_sys_utimes
+#define sys_fadvise64_64 compat_sys_fadvise64_64_wrapper
+#define sys_mq_open compat_sys_mq_open
+#define sys_mq_timedsend compat_sys_mq_timedsend
+#define sys_mq_timedreceive compat_sys_mq_timedreceive
+#define sys_mq_notify compat_sys_mq_notify
+#define sys_mq_getsetattr compat_sys_mq_getsetattr
+#define sys_waitid compat_sys_waitid
+#define sys_recv compat_sys_recv
+#define sys_recvfrom compat_sys_recvfrom
+#define sys_setsockopt compat_sys_setsockopt
+#define sys_getsockopt compat_sys_getsockopt
+#define sys_sendmsg compat_sys_sendmsg
+#define sys_recvmsg compat_sys_recvmsg
+#define sys_semctl compat_sys_semctl
+#define sys_msgsnd compat_sys_msgsnd
+#define sys_msgrcv compat_sys_msgrcv
+#define sys_msgctl compat_sys_msgctl
+#define sys_shmat compat_sys_shmat
+#define sys_shmctl compat_sys_shmctl
+#define sys_keyctl compat_sys_keyctl
+#define sys_semtimedop compat_sys_semtimedop
+#define sys_mbind compat_sys_mbind
+#define sys_get_mempolicy compat_sys_get_mempolicy
+#define sys_set_mempolicy compat_sys_set_mempolicy
+#define sys_openat compat_sys_openat
+#define sys_futimesat compat_sys_futimesat
+#define sys_pselect6 compat_sys_pselect6
+#define sys_ppoll compat_sys_ppoll
+#define sys_set_robust_list compat_sys_set_robust_list
+#define sys_get_robust_list compat_sys_get_robust_list
+#define sys_sync_file_range2 compat_sys_sync_file_range2_wrapper
+#define sys_vmsplice compat_sys_vmsplice
+#define sys_move_pages compat_sys_move_pages
+#define sys_epoll_pwait compat_sys_epoll_pwait
+#define sys_kexec_load compat_sys_kexec_load
+#define sys_utimensat compat_sys_utimensat
+#define sys_signalfd compat_sys_signalfd
+#define sys_fallocate compat_sys_fallocate_wrapper
+#define sys_timerfd_settime compat_sys_timerfd_settime
+#define sys_timerfd_gettime compat_sys_timerfd_gettime
+#define sys_signalfd4 compat_sys_signalfd4
+#define sys_preadv compat_sys_preadv
+#define sys_pwritev compat_sys_pwritev
+#define sys_rt_tgsigqueueinfo compat_sys_rt_tgsigqueueinfo
+#define sys_recvmmsg compat_sys_recvmmsg
+#define sys_fanotify_mark compat_sys_fanotify_mark_wrapper
+
+#undef __SYSCALL
+#define __SYSCALL(x, y) .quad y // x
+#define __SYSCALL_COMPAT
+
+/*
+ * The system calls table must be 4KB aligned.
+ */
+ .align 12
+ENTRY(compat_sys_call_table)
+#include <asm/unistd.h>
diff --git a/arch/arm64/kernel/sys_compat.c b/arch/arm64/kernel/sys_compat.c
new file mode 100644
index 000000000000..967e92fdff01
--- /dev/null
+++ b/arch/arm64/kernel/sys_compat.c
@@ -0,0 +1,164 @@
+/*
+ * Based on arch/arm/kernel/sys_arm.c
+ *
+ * Copyright (C) People who wrote linux/arch/i386/kernel/sys_i386.c
+ * Copyright (C) 1995, 1996 Russell King.
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define __SYSCALL_COMPAT
+
+#include <linux/compat.h>
+#include <linux/personality.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+
+#include <asm/cacheflush.h>
+#include <asm/unistd.h>
+
+asmlinkage int compat_sys_fork(struct pt_regs *regs)
+{
+ return do_fork(SIGCHLD, regs->compat_sp, regs, 0, NULL, NULL);
+}
+
+asmlinkage int compat_sys_clone(unsigned long clone_flags, unsigned long newsp,
+ int __user *parent_tidptr, int tls_val,
+ int __user *child_tidptr, struct pt_regs *regs)
+{
+ if (!newsp)
+ newsp = regs->compat_sp;
+
+ return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
+}
+
+asmlinkage int compat_sys_vfork(struct pt_regs *regs)
+{
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->compat_sp,
+ regs, 0, NULL, NULL);
+}
+
+asmlinkage int compat_sys_execve(const char __user *filenamei,
+ compat_uptr_t argv, compat_uptr_t envp,
+ struct pt_regs *regs)
+{
+ int error;
+ char * filename;
+
+ filename = getname(filenamei);
+ error = PTR_ERR(filename);
+ if (IS_ERR(filename))
+ goto out;
+ error = compat_do_execve(filename, compat_ptr(argv), compat_ptr(envp),
+ regs);
+ putname(filename);
+out:
+ return error;
+}
+
+asmlinkage int compat_sys_sched_rr_get_interval(compat_pid_t pid,
+ struct compat_timespec __user *interval)
+{
+ struct timespec t;
+ int ret;
+ mm_segment_t old_fs = get_fs();
+
+ set_fs(KERNEL_DS);
+ ret = sys_sched_rr_get_interval(pid, (struct timespec __user *)&t);
+ set_fs(old_fs);
+ if (put_compat_timespec(&t, interval))
+ return -EFAULT;
+ return ret;
+}
+
+asmlinkage int compat_sys_sendfile(int out_fd, int in_fd,
+ compat_off_t __user *offset, s32 count)
+{
+ mm_segment_t old_fs = get_fs();
+ int ret;
+ off_t of;
+
+ if (offset && get_user(of, offset))
+ return -EFAULT;
+
+ set_fs(KERNEL_DS);
+ ret = sys_sendfile(out_fd, in_fd, offset ? (off_t __user *)&of : NULL,
+ count);
+ set_fs(old_fs);
+
+ if (offset && put_user(of, offset))
+ return -EFAULT;
+ return ret;
+}
+
+static inline void
+do_compat_cache_op(unsigned long start, unsigned long end, int flags)
+{
+ struct mm_struct *mm = current->active_mm;
+ struct vm_area_struct *vma;
+
+ if (end < start || flags)
+ return;
+
+ down_read(&mm->mmap_sem);
+ vma = find_vma(mm, start);
+ if (vma && vma->vm_start < end) {
+ if (start < vma->vm_start)
+ start = vma->vm_start;
+ if (end > vma->vm_end)
+ end = vma->vm_end;
+ up_read(&mm->mmap_sem);
+ __flush_cache_user_range(start & PAGE_MASK, PAGE_ALIGN(end));
+ return;
+ }
+ up_read(&mm->mmap_sem);
+}
+
+/*
+ * Handle all unrecognised system calls.
+ */
+long compat_arm_syscall(struct pt_regs *regs)
+{
+ unsigned int no = regs->regs[7];
+
+ switch (no) {
+ /*
+ * Flush a region from virtual address 'r0' to virtual address 'r1'
+ * _exclusive_. There is no alignment requirement on either address;
+ * user space does not need to know the hardware cache layout.
+ *
+ * r2 contains flags. It should ALWAYS be passed as ZERO until it
+ * is defined to be something else. For now we ignore it, but may
+ * the fires of hell burn in your belly if you break this rule. ;)
+ *
+ * (at a later date, we may want to allow this call to not flush
+ * various aspects of the cache. Passing '0' will guarantee that
+ * everything necessary gets flushed to maintain consistency in
+ * the specified region).
+ */
+ case __ARM_NR_compat_cacheflush:
+ do_compat_cache_op(regs->regs[0], regs->regs[1], regs->regs[2]);
+ return 0;
+
+ case __ARM_NR_compat_set_tls:
+ current->thread.tp_value = regs->regs[0];
+ asm ("msr tpidrro_el0, %0" : : "r" (regs->regs[0]));
+ return 0;
+
+ default:
+ return -ENOSYS;
+ }
+}
diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c
new file mode 100644
index 000000000000..3b4b7258f492
--- /dev/null
+++ b/arch/arm64/kernel/time.c
@@ -0,0 +1,65 @@
+/*
+ * Based on arch/arm/kernel/time.c
+ *
+ * Copyright (C) 1991, 1992, 1995 Linus Torvalds
+ * Modifications for ARM (C) 1994-2001 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/time.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/timex.h>
+#include <linux/errno.h>
+#include <linux/profile.h>
+#include <linux/syscore_ops.h>
+#include <linux/timer.h>
+#include <linux/irq.h>
+
+#include <clocksource/arm_generic.h>
+
+#include <asm/thread_info.h>
+#include <asm/stacktrace.h>
+
+#ifdef CONFIG_SMP
+unsigned long profile_pc(struct pt_regs *regs)
+{
+ struct stackframe frame;
+
+ if (!in_lock_functions(regs->pc))
+ return regs->pc;
+
+ frame.fp = regs->regs[29];
+ frame.sp = regs->sp;
+ frame.pc = regs->pc;
+ do {
+ int ret = unwind_frame(&frame);
+ if (ret < 0)
+ return 0;
+ } while (in_lock_functions(frame.pc));
+
+ return frame.pc;
+}
+EXPORT_SYMBOL(profile_pc);
+#endif
+
+void __init time_init(void)
+{
+ arm_generic_timer_init();
+}
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
new file mode 100644
index 000000000000..3883f842434f
--- /dev/null
+++ b/arch/arm64/kernel/traps.c
@@ -0,0 +1,348 @@
+/*
+ * Based on arch/arm/kernel/traps.c
+ *
+ * Copyright (C) 1995-2009 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/signal.h>
+#include <linux/personality.h>
+#include <linux/kallsyms.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/hardirq.h>
+#include <linux/kdebug.h>
+#include <linux/module.h>
+#include <linux/kexec.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+
+#include <asm/atomic.h>
+#include <asm/traps.h>
+#include <asm/stacktrace.h>
+#include <asm/exception.h>
+#include <asm/system_misc.h>
+
+static const char *handler[]= {
+ "Synchronous Abort",
+ "IRQ",
+ "FIQ",
+ "Error"
+};
+
+int show_unhandled_signals = 1;
+
+/*
+ * Dump out the contents of some memory nicely...
+ */
+static void dump_mem(const char *lvl, const char *str, unsigned long bottom,
+ unsigned long top)
+{
+ unsigned long first;
+ mm_segment_t fs;
+ int i;
+
+ /*
+ * We need to switch to kernel mode so that we can use __get_user
+ * to safely read from kernel space. Note that we now dump the
+ * code first, just in case the backtrace kills us.
+ */
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ printk("%s%s(0x%016lx to 0x%016lx)\n", lvl, str, bottom, top);
+
+ for (first = bottom & ~31; first < top; first += 32) {
+ unsigned long p;
+ char str[sizeof(" 12345678") * 8 + 1];
+
+ memset(str, ' ', sizeof(str));
+ str[sizeof(str) - 1] = '\0';
+
+ for (p = first, i = 0; i < 8 && p < top; i++, p += 4) {
+ if (p >= bottom && p < top) {
+ unsigned int val;
+ if (__get_user(val, (unsigned int *)p) == 0)
+ sprintf(str + i * 9, " %08x", val);
+ else
+ sprintf(str + i * 9, " ????????");
+ }
+ }
+ printk("%s%04lx:%s\n", lvl, first & 0xffff, str);
+ }
+
+ set_fs(fs);
+}
+
+static void dump_backtrace_entry(unsigned long where, unsigned long stack)
+{
+ print_ip_sym(where);
+ if (in_exception_text(where))
+ dump_mem("", "Exception stack", stack,
+ stack + sizeof(struct pt_regs));
+}
+
+static void dump_instr(const char *lvl, struct pt_regs *regs)
+{
+ unsigned long addr = instruction_pointer(regs);
+ mm_segment_t fs;
+ char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
+ int i;
+
+ /*
+ * We need to switch to kernel mode so that we can use __get_user
+ * to safely read from kernel space. Note that we now dump the
+ * code first, just in case the backtrace kills us.
+ */
+ fs = get_fs();
+ set_fs(KERNEL_DS);
+
+ for (i = -4; i < 1; i++) {
+ unsigned int val, bad;
+
+ bad = __get_user(val, &((u32 *)addr)[i]);
+
+ if (!bad)
+ p += sprintf(p, i == 0 ? "(%08x) " : "%08x ", val);
+ else {
+ p += sprintf(p, "bad PC value");
+ break;
+ }
+ }
+ printk("%sCode: %s\n", lvl, str);
+
+ set_fs(fs);
+}
+
+static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk)
+{
+ struct stackframe frame;
+ const register unsigned long current_sp asm ("sp");
+
+ pr_debug("%s(regs = %p tsk = %p)\n", __func__, regs, tsk);
+
+ if (!tsk)
+ tsk = current;
+
+ if (regs) {
+ frame.fp = regs->regs[29];
+ frame.sp = regs->sp;
+ frame.pc = regs->pc;
+ } else if (tsk == current) {
+ frame.fp = (unsigned long)__builtin_frame_address(0);
+ frame.sp = current_sp;
+ frame.pc = (unsigned long)dump_backtrace;
+ } else {
+ /*
+ * task blocked in __switch_to
+ */
+ frame.fp = thread_saved_fp(tsk);
+ frame.sp = thread_saved_sp(tsk);
+ frame.pc = thread_saved_pc(tsk);
+ }
+
+ printk("Call trace:\n");
+ while (1) {
+ unsigned long where = frame.pc;
+ int ret;
+
+ ret = unwind_frame(&frame);
+ if (ret < 0)
+ break;
+ dump_backtrace_entry(where, frame.sp);
+ }
+}
+
+void dump_stack(void)
+{
+ dump_backtrace(NULL, NULL);
+}
+
+EXPORT_SYMBOL(dump_stack);
+
+void show_stack(struct task_struct *tsk, unsigned long *sp)
+{
+ dump_backtrace(NULL, tsk);
+ barrier();
+}
+
+#ifdef CONFIG_PREEMPT
+#define S_PREEMPT " PREEMPT"
+#else
+#define S_PREEMPT ""
+#endif
+#ifdef CONFIG_SMP
+#define S_SMP " SMP"
+#else
+#define S_SMP ""
+#endif
+
+static int __die(const char *str, int err, struct thread_info *thread,
+ struct pt_regs *regs)
+{
+ struct task_struct *tsk = thread->task;
+ static int die_counter;
+ int ret;
+
+ pr_emerg("Internal error: %s: %x [#%d]" S_PREEMPT S_SMP "\n",
+ str, err, ++die_counter);
+
+ /* trap and error numbers are mostly meaningless on ARM */
+ ret = notify_die(DIE_OOPS, str, regs, err, 0, SIGSEGV);
+ if (ret == NOTIFY_STOP)
+ return ret;
+
+ print_modules();
+ __show_regs(regs);
+ pr_emerg("Process %.*s (pid: %d, stack limit = 0x%p)\n",
+ TASK_COMM_LEN, tsk->comm, task_pid_nr(tsk), thread + 1);
+
+ if (!user_mode(regs) || in_interrupt()) {
+ dump_mem(KERN_EMERG, "Stack: ", regs->sp,
+ THREAD_SIZE + (unsigned long)task_stack_page(tsk));
+ dump_backtrace(regs, tsk);
+ dump_instr(KERN_EMERG, regs);
+ }
+
+ return ret;
+}
+
+static DEFINE_RAW_SPINLOCK(die_lock);
+
+/*
+ * This function is protected against re-entrancy.
+ */
+void die(const char *str, struct pt_regs *regs, int err)
+{
+ struct thread_info *thread = current_thread_info();
+ int ret;
+
+ oops_enter();
+
+ raw_spin_lock_irq(&die_lock);
+ console_verbose();
+ bust_spinlocks(1);
+ ret = __die(str, err, thread, regs);
+
+ if (regs && kexec_should_crash(thread->task))
+ crash_kexec(regs);
+
+ bust_spinlocks(0);
+ add_taint(TAINT_DIE);
+ raw_spin_unlock_irq(&die_lock);
+ oops_exit();
+
+ if (in_interrupt())
+ panic("Fatal exception in interrupt");
+ if (panic_on_oops)
+ panic("Fatal exception");
+ if (ret != NOTIFY_STOP)
+ do_exit(SIGSEGV);
+}
+
+void arm64_notify_die(const char *str, struct pt_regs *regs,
+ struct siginfo *info, int err)
+{
+ if (user_mode(regs))
+ force_sig_info(info->si_signo, info, current);
+ else
+ die(str, regs, err);
+}
+
+asmlinkage void __exception do_undefinstr(struct pt_regs *regs)
+{
+ siginfo_t info;
+ void __user *pc = (void __user *)instruction_pointer(regs);
+
+#ifdef CONFIG_COMPAT
+ /* check for AArch32 breakpoint instructions */
+ if (compat_user_mode(regs) && aarch32_break_trap(regs) == 0)
+ return;
+#endif
+
+ if (show_unhandled_signals) {
+ pr_info("%s[%d]: undefined instruction: pc=%p\n",
+ current->comm, task_pid_nr(current), pc);
+ dump_instr(KERN_INFO, regs);
+ }
+
+ info.si_signo = SIGILL;
+ info.si_errno = 0;
+ info.si_code = ILL_ILLOPC;
+ info.si_addr = pc;
+
+ arm64_notify_die("Oops - undefined instruction", regs, &info, 0);
+}
+
+long compat_arm_syscall(struct pt_regs *regs);
+
+asmlinkage long do_ni_syscall(struct pt_regs *regs)
+{
+#ifdef CONFIG_COMPAT
+ long ret;
+ if (is_compat_task()) {
+ ret = compat_arm_syscall(regs);
+ if (ret != -ENOSYS)
+ return ret;
+ }
+#endif
+
+ if (show_unhandled_signals) {
+ pr_info("%s[%d]: syscall %d\n", current->comm,
+ task_pid_nr(current), (int)regs->syscallno);
+ dump_instr("", regs);
+ if (user_mode(regs))
+ __show_regs(regs);
+ }
+
+ return sys_ni_syscall();
+}
+
+/*
+ * bad_mode handles the impossible case in the exception vector.
+ */
+asmlinkage void bad_mode(struct pt_regs *regs, int reason, unsigned int esr)
+{
+ console_verbose();
+
+ pr_crit("Bad mode in %s handler detected, code 0x%08x\n",
+ handler[reason], esr);
+
+ die("Oops - bad mode", regs, 0);
+ local_irq_disable();
+ panic("bad mode");
+}
+
+void __pte_error(const char *file, int line, unsigned long val)
+{
+ printk("%s:%d: bad pte %016lx.\n", file, line, val);
+}
+
+void __pmd_error(const char *file, int line, unsigned long val)
+{
+ printk("%s:%d: bad pmd %016lx.\n", file, line, val);
+}
+
+void __pgd_error(const char *file, int line, unsigned long val)
+{
+ printk("%s:%d: bad pgd %016lx.\n", file, line, val);
+}
+
+void __init trap_init(void)
+{
+ return;
+}
diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c
new file mode 100644
index 000000000000..17948fc7d663
--- /dev/null
+++ b/arch/arm64/kernel/vdso.c
@@ -0,0 +1,261 @@
+/*
+ * VDSO implementation for AArch64 and vector page setup for AArch32.
+ *
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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: Will Deacon <will.deacon@arm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/clocksource.h>
+#include <linux/elf.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/signal.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include <asm/cacheflush.h>
+#include <asm/signal32.h>
+#include <asm/vdso.h>
+#include <asm/vdso_datapage.h>
+
+extern char vdso_start, vdso_end;
+static unsigned long vdso_pages;
+static struct page **vdso_pagelist;
+
+/*
+ * The vDSO data page.
+ */
+static union {
+ struct vdso_data data;
+ u8 page[PAGE_SIZE];
+} vdso_data_store __page_aligned_data;
+struct vdso_data *vdso_data = &vdso_data_store.data;
+
+#ifdef CONFIG_COMPAT
+/*
+ * Create and map the vectors page for AArch32 tasks.
+ */
+static struct page *vectors_page[1];
+
+static int alloc_vectors_page(void)
+{
+ extern char __kuser_helper_start[], __kuser_helper_end[];
+ int kuser_sz = __kuser_helper_end - __kuser_helper_start;
+ unsigned long vpage;
+
+ vpage = get_zeroed_page(GFP_ATOMIC);
+
+ if (!vpage)
+ return -ENOMEM;
+
+ /* kuser helpers */
+ memcpy((void *)vpage + 0x1000 - kuser_sz, __kuser_helper_start,
+ kuser_sz);
+
+ /* sigreturn code */
+ memcpy((void *)vpage + AARCH32_KERN_SIGRET_CODE_OFFSET,
+ aarch32_sigret_code, sizeof(aarch32_sigret_code));
+
+ flush_icache_range(vpage, vpage + PAGE_SIZE);
+ vectors_page[0] = virt_to_page(vpage);
+
+ return 0;
+}
+arch_initcall(alloc_vectors_page);
+
+int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp)
+{
+ struct mm_struct *mm = current->mm;
+ unsigned long addr = AARCH32_VECTORS_BASE;
+ int ret;
+
+ down_write(&mm->mmap_sem);
+ current->mm->context.vdso = (void *)addr;
+
+ /* Map vectors page at the high address. */
+ ret = install_special_mapping(mm, addr, PAGE_SIZE,
+ VM_READ|VM_EXEC|VM_MAYREAD|VM_MAYEXEC,
+ vectors_page);
+
+ up_write(&mm->mmap_sem);
+
+ return ret;
+}
+#endif /* CONFIG_COMPAT */
+
+static int __init vdso_init(void)
+{
+ struct page *pg;
+ char *vbase;
+ int i, ret = 0;
+
+ vdso_pages = (&vdso_end - &vdso_start) >> PAGE_SHIFT;
+ pr_info("vdso: %ld pages (%ld code, %ld data) at base %p\n",
+ vdso_pages + 1, vdso_pages, 1L, &vdso_start);
+
+ /* Allocate the vDSO pagelist, plus a page for the data. */
+ vdso_pagelist = kzalloc(sizeof(struct page *) * (vdso_pages + 1),
+ GFP_KERNEL);
+ if (vdso_pagelist == NULL) {
+ pr_err("Failed to allocate vDSO pagelist!\n");
+ return -ENOMEM;
+ }
+
+ /* Grab the vDSO code pages. */
+ for (i = 0; i < vdso_pages; i++) {
+ pg = virt_to_page(&vdso_start + i*PAGE_SIZE);
+ ClearPageReserved(pg);
+ get_page(pg);
+ vdso_pagelist[i] = pg;
+ }
+
+ /* Sanity check the shared object header. */
+ vbase = vmap(vdso_pagelist, 1, 0, PAGE_KERNEL);
+ if (vbase == NULL) {
+ pr_err("Failed to map vDSO pagelist!\n");
+ return -ENOMEM;
+ } else if (memcmp(vbase, "\177ELF", 4)) {
+ pr_err("vDSO is not a valid ELF object!\n");
+ ret = -EINVAL;
+ goto unmap;
+ }
+
+ /* Grab the vDSO data page. */
+ pg = virt_to_page(vdso_data);
+ get_page(pg);
+ vdso_pagelist[i] = pg;
+
+unmap:
+ vunmap(vbase);
+ return ret;
+}
+arch_initcall(vdso_init);
+
+int arch_setup_additional_pages(struct linux_binprm *bprm,
+ int uses_interp)
+{
+ struct mm_struct *mm = current->mm;
+ unsigned long vdso_base, vdso_mapping_len;
+ int ret;
+
+ /* Be sure to map the data page */
+ vdso_mapping_len = (vdso_pages + 1) << PAGE_SHIFT;
+
+ down_write(&mm->mmap_sem);
+ vdso_base = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0);
+ if (IS_ERR_VALUE(vdso_base)) {
+ ret = vdso_base;
+ goto up_fail;
+ }
+ mm->context.vdso = (void *)vdso_base;
+
+ ret = install_special_mapping(mm, vdso_base, vdso_mapping_len,
+ VM_READ|VM_EXEC|
+ VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
+ vdso_pagelist);
+ if (ret) {
+ mm->context.vdso = NULL;
+ goto up_fail;
+ }
+
+up_fail:
+ up_write(&mm->mmap_sem);
+
+ return ret;
+}
+
+const char *arch_vma_name(struct vm_area_struct *vma)
+{
+ /*
+ * We can re-use the vdso pointer in mm_context_t for identifying
+ * the vectors page for compat applications. The vDSO will always
+ * sit above TASK_UNMAPPED_BASE and so we don't need to worry about
+ * it conflicting with the vectors base.
+ */
+ if (vma->vm_mm && vma->vm_start == (long)vma->vm_mm->context.vdso) {
+#ifdef CONFIG_COMPAT
+ if (vma->vm_start == AARCH32_VECTORS_BASE)
+ return "[vectors]";
+#endif
+ return "[vdso]";
+ }
+
+ return NULL;
+}
+
+/*
+ * We define AT_SYSINFO_EHDR, so we need these function stubs to keep
+ * Linux happy.
+ */
+int in_gate_area_no_mm(unsigned long addr)
+{
+ return 0;
+}
+
+int in_gate_area(struct mm_struct *mm, unsigned long addr)
+{
+ return 0;
+}
+
+struct vm_area_struct *get_gate_vma(struct mm_struct *mm)
+{
+ return NULL;
+}
+
+/*
+ * Update the vDSO data page to keep in sync with kernel timekeeping.
+ */
+void update_vsyscall(struct timespec *ts, struct timespec *wtm,
+ struct clocksource *clock, u32 mult)
+{
+ struct timespec xtime_coarse;
+ u32 use_syscall = strcmp(clock->name, "arch_sys_counter");
+
+ ++vdso_data->tb_seq_count;
+ smp_wmb();
+
+ xtime_coarse = __current_kernel_time();
+ vdso_data->use_syscall = use_syscall;
+ vdso_data->xtime_coarse_sec = xtime_coarse.tv_sec;
+ vdso_data->xtime_coarse_nsec = xtime_coarse.tv_nsec;
+
+ if (!use_syscall) {
+ vdso_data->cs_cycle_last = clock->cycle_last;
+ vdso_data->xtime_clock_sec = ts->tv_sec;
+ vdso_data->xtime_clock_nsec = ts->tv_nsec;
+ vdso_data->cs_mult = mult;
+ vdso_data->cs_shift = clock->shift;
+ vdso_data->wtm_clock_sec = wtm->tv_sec;
+ vdso_data->wtm_clock_nsec = wtm->tv_nsec;
+ }
+
+ smp_wmb();
+ ++vdso_data->tb_seq_count;
+}
+
+void update_vsyscall_tz(void)
+{
+ ++vdso_data->tb_seq_count;
+ smp_wmb();
+ vdso_data->tz_minuteswest = sys_tz.tz_minuteswest;
+ vdso_data->tz_dsttime = sys_tz.tz_dsttime;
+ smp_wmb();
+ ++vdso_data->tb_seq_count;
+}
diff --git a/arch/arm64/kernel/vdso/.gitignore b/arch/arm64/kernel/vdso/.gitignore
new file mode 100644
index 000000000000..b8cc94e9698b
--- /dev/null
+++ b/arch/arm64/kernel/vdso/.gitignore
@@ -0,0 +1,2 @@
+vdso.lds
+vdso-offsets.h
diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile
new file mode 100644
index 000000000000..d8064af42e62
--- /dev/null
+++ b/arch/arm64/kernel/vdso/Makefile
@@ -0,0 +1,63 @@
+#
+# Building a vDSO image for AArch64.
+#
+# Author: Will Deacon <will.deacon@arm.com>
+# Heavily based on the vDSO Makefiles for other archs.
+#
+
+obj-vdso := gettimeofday.o note.o sigreturn.o
+
+# Build rules
+targets := $(obj-vdso) vdso.so vdso.so.dbg
+obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
+
+ccflags-y := -shared -fno-common -fno-builtin
+ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 \
+ $(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
+
+obj-y += vdso.o
+extra-y += vdso.lds vdso-offsets.h
+CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
+
+# Force dependency (incbin is bad)
+$(obj)/vdso.o : $(obj)/vdso.so
+
+# Link rule for the .so file, .lds has to be first
+$(obj)/vdso.so.dbg: $(src)/vdso.lds $(obj-vdso)
+ $(call if_changed,vdsold)
+
+# Strip rule for the .so file
+$(obj)/%.so: OBJCOPYFLAGS := -S
+$(obj)/%.so: $(obj)/%.so.dbg FORCE
+ $(call if_changed,objcopy)
+
+# Generate VDSO offsets using helper script
+gen-vdsosym := $(srctree)/$(src)/gen_vdso_offsets.sh
+quiet_cmd_vdsosym = VDSOSYM $@
+define cmd_vdsosym
+ $(NM) $< | $(gen-vdsosym) | LC_ALL=C sort > $@ && \
+ cp $@ include/generated/
+endef
+
+$(obj)/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE
+ $(call if_changed,vdsosym)
+
+# Assembly rules for the .S files
+$(obj-vdso): %.o: %.S
+ $(call if_changed_dep,vdsoas)
+
+# Actual build commands
+quiet_cmd_vdsold = VDSOL $@
+ cmd_vdsold = $(CC) $(c_flags) -Wl,-T $^ -o $@
+quiet_cmd_vdsoas = VDSOA $@
+ cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $<
+
+# Install commands for the unstripped file
+quiet_cmd_vdso_install = INSTALL $@
+ cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
+
+vdso.so: $(obj)/vdso.so.dbg
+ @mkdir -p $(MODLIB)/vdso
+ $(call cmd,vdso_install)
+
+vdso_install: vdso.so
diff --git a/arch/arm64/kernel/vdso/gen_vdso_offsets.sh b/arch/arm64/kernel/vdso/gen_vdso_offsets.sh
new file mode 100755
index 000000000000..01924ff071ad
--- /dev/null
+++ b/arch/arm64/kernel/vdso/gen_vdso_offsets.sh
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+#
+# Match symbols in the DSO that look like VDSO_*; produce a header file
+# of constant offsets into the shared object.
+#
+# Doing this inside the Makefile will break the $(filter-out) function,
+# causing Kbuild to rebuild the vdso-offsets header file every time.
+#
+# Author: Will Deacon <will.deacon@arm.com
+#
+
+LC_ALL=C
+sed -n -e 's/^00*/0/' -e \
+'s/^\([0-9a-fA-F]*\) . VDSO_\([a-zA-Z0-9_]*\)$/\#define vdso_offset_\2\t0x\1/p'
diff --git a/arch/arm64/kernel/vdso/gettimeofday.S b/arch/arm64/kernel/vdso/gettimeofday.S
new file mode 100644
index 000000000000..dcb8c203a3b2
--- /dev/null
+++ b/arch/arm64/kernel/vdso/gettimeofday.S
@@ -0,0 +1,242 @@
+/*
+ * Userspace implementations of gettimeofday() and friends.
+ *
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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: Will Deacon <will.deacon@arm.com>
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm-offsets.h>
+#include <asm/unistd.h>
+
+#define NSEC_PER_SEC_LO16 0xca00
+#define NSEC_PER_SEC_HI16 0x3b9a
+
+vdso_data .req x6
+use_syscall .req w7
+seqcnt .req w8
+
+ .macro seqcnt_acquire
+9999: ldr seqcnt, [vdso_data, #VDSO_TB_SEQ_COUNT]
+ tbnz seqcnt, #0, 9999b
+ dmb ishld
+ ldr use_syscall, [vdso_data, #VDSO_USE_SYSCALL]
+ .endm
+
+ .macro seqcnt_read, cnt
+ dmb ishld
+ ldr \cnt, [vdso_data, #VDSO_TB_SEQ_COUNT]
+ .endm
+
+ .macro seqcnt_check, cnt, fail
+ cmp \cnt, seqcnt
+ b.ne \fail
+ .endm
+
+ .text
+
+/* int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz); */
+ENTRY(__kernel_gettimeofday)
+ .cfi_startproc
+ mov x2, x30
+ .cfi_register x30, x2
+
+ /* Acquire the sequence counter and get the timespec. */
+ adr vdso_data, _vdso_data
+1: seqcnt_acquire
+ cbnz use_syscall, 4f
+
+ /* If tv is NULL, skip to the timezone code. */
+ cbz x0, 2f
+ bl __do_get_tspec
+ seqcnt_check w13, 1b
+
+ /* Convert ns to us. */
+ mov x11, #1000
+ udiv x10, x10, x11
+ stp x9, x10, [x0, #TVAL_TV_SEC]
+2:
+ /* If tz is NULL, return 0. */
+ cbz x1, 3f
+ ldp w4, w5, [vdso_data, #VDSO_TZ_MINWEST]
+ seqcnt_read w13
+ seqcnt_check w13, 1b
+ stp w4, w5, [x1, #TZ_MINWEST]
+3:
+ mov x0, xzr
+ ret x2
+4:
+ /* Syscall fallback. */
+ mov x8, #__NR_gettimeofday
+ svc #0
+ ret x2
+ .cfi_endproc
+ENDPROC(__kernel_gettimeofday)
+
+/* int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); */
+ENTRY(__kernel_clock_gettime)
+ .cfi_startproc
+ cmp w0, #CLOCK_REALTIME
+ ccmp w0, #CLOCK_MONOTONIC, #0x4, ne
+ b.ne 2f
+
+ mov x2, x30
+ .cfi_register x30, x2
+
+ /* Get kernel timespec. */
+ adr vdso_data, _vdso_data
+1: seqcnt_acquire
+ cbnz use_syscall, 7f
+
+ bl __do_get_tspec
+ seqcnt_check w13, 1b
+
+ cmp w0, #CLOCK_MONOTONIC
+ b.ne 6f
+
+ /* Get wtm timespec. */
+ ldp x14, x15, [vdso_data, #VDSO_WTM_CLK_SEC]
+
+ /* Check the sequence counter. */
+ seqcnt_read w13
+ seqcnt_check w13, 1b
+ b 4f
+2:
+ cmp w0, #CLOCK_REALTIME_COARSE
+ ccmp w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
+ b.ne 8f
+
+ /* Get coarse timespec. */
+ adr vdso_data, _vdso_data
+3: seqcnt_acquire
+ ldp x9, x10, [vdso_data, #VDSO_XTIME_CRS_SEC]
+
+ cmp w0, #CLOCK_MONOTONIC_COARSE
+ b.ne 6f
+
+ /* Get wtm timespec. */
+ ldp x14, x15, [vdso_data, #VDSO_WTM_CLK_SEC]
+
+ /* Check the sequence counter. */
+ seqcnt_read w13
+ seqcnt_check w13, 3b
+4:
+ /* Add on wtm timespec. */
+ add x9, x9, x14
+ add x10, x10, x15
+
+ /* Normalise the new timespec. */
+ mov x14, #NSEC_PER_SEC_LO16
+ movk x14, #NSEC_PER_SEC_HI16, lsl #16
+ cmp x10, x14
+ b.lt 5f
+ sub x10, x10, x14
+ add x9, x9, #1
+5:
+ cmp x10, #0
+ b.ge 6f
+ add x10, x10, x14
+ sub x9, x9, #1
+
+6: /* Store to the user timespec. */
+ stp x9, x10, [x1, #TSPEC_TV_SEC]
+ mov x0, xzr
+ ret x2
+7:
+ mov x30, x2
+8: /* Syscall fallback. */
+ mov x8, #__NR_clock_gettime
+ svc #0
+ ret
+ .cfi_endproc
+ENDPROC(__kernel_clock_gettime)
+
+/* int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); */
+ENTRY(__kernel_clock_getres)
+ .cfi_startproc
+ cbz w1, 3f
+
+ cmp w0, #CLOCK_REALTIME
+ ccmp w0, #CLOCK_MONOTONIC, #0x4, ne
+ b.ne 1f
+
+ ldr x2, 5f
+ b 2f
+1:
+ cmp w0, #CLOCK_REALTIME_COARSE
+ ccmp w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
+ b.ne 4f
+ ldr x2, 6f
+2:
+ stp xzr, x2, [x1]
+
+3: /* res == NULL. */
+ mov w0, wzr
+ ret
+
+4: /* Syscall fallback. */
+ mov x8, #__NR_clock_getres
+ svc #0
+ ret
+5:
+ .quad CLOCK_REALTIME_RES
+6:
+ .quad CLOCK_COARSE_RES
+ .cfi_endproc
+ENDPROC(__kernel_clock_getres)
+
+/*
+ * Read the current time from the architected counter.
+ * Expects vdso_data to be initialised.
+ * Clobbers the temporary registers (x9 - x15).
+ * Returns:
+ * - (x9, x10) = (ts->tv_sec, ts->tv_nsec)
+ * - (x11, x12) = (xtime->tv_sec, xtime->tv_nsec)
+ * - w13 = vDSO sequence counter
+ */
+ENTRY(__do_get_tspec)
+ .cfi_startproc
+
+ /* Read from the vDSO data page. */
+ ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
+ ldp x11, x12, [vdso_data, #VDSO_XTIME_CLK_SEC]
+ ldp w14, w15, [vdso_data, #VDSO_CS_MULT]
+ seqcnt_read w13
+
+ /* Read the physical counter. */
+ isb
+ mrs x9, cntpct_el0
+
+ /* Calculate cycle delta and convert to ns. */
+ sub x10, x9, x10
+ /* We can only guarantee 56 bits of precision. */
+ movn x9, #0xff0, lsl #48
+ and x10, x9, x10
+ mul x10, x10, x14
+ lsr x10, x10, x15
+
+ /* Use the kernel time to calculate the new timespec. */
+ add x10, x12, x10
+ mov x14, #NSEC_PER_SEC_LO16
+ movk x14, #NSEC_PER_SEC_HI16, lsl #16
+ udiv x15, x10, x14
+ add x9, x15, x11
+ mul x14, x14, x15
+ sub x10, x10, x14
+
+ ret
+ .cfi_endproc
+ENDPROC(__do_get_tspec)
diff --git a/arch/arm64/kernel/vdso/note.S b/arch/arm64/kernel/vdso/note.S
new file mode 100644
index 000000000000..b82c85e5d972
--- /dev/null
+++ b/arch/arm64/kernel/vdso/note.S
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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: Will Deacon <will.deacon@arm.com>
+ *
+ * This supplies .note.* sections to go into the PT_NOTE inside the vDSO text.
+ * Here we can supply some information useful to userland.
+ */
+
+#include <linux/uts.h>
+#include <linux/version.h>
+#include <linux/elfnote.h>
+
+ELFNOTE_START(Linux, 0, "a")
+ .long LINUX_VERSION_CODE
+ELFNOTE_END
diff --git a/arch/arm64/kernel/vdso/sigreturn.S b/arch/arm64/kernel/vdso/sigreturn.S
new file mode 100644
index 000000000000..20d98effa7dd
--- /dev/null
+++ b/arch/arm64/kernel/vdso/sigreturn.S
@@ -0,0 +1,37 @@
+/*
+ * Sigreturn trampoline for returning from a signal when the SA_RESTORER
+ * flag is not set.
+ *
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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: Will Deacon <will.deacon@arm.com>
+ */
+
+#include <linux/linkage.h>
+#include <asm/unistd.h>
+
+ .text
+
+ nop
+ENTRY(__kernel_rt_sigreturn)
+ .cfi_startproc
+ .cfi_signal_frame
+ .cfi_def_cfa x29, 0
+ .cfi_offset x29, 0 * 8
+ .cfi_offset x30, 1 * 8
+ mov x8, #__NR_rt_sigreturn
+ svc #0
+ .cfi_endproc
+ENDPROC(__kernel_rt_sigreturn)
diff --git a/arch/arm64/kernel/vdso/vdso.S b/arch/arm64/kernel/vdso/vdso.S
new file mode 100644
index 000000000000..60c1db54b41a
--- /dev/null
+++ b/arch/arm64/kernel/vdso/vdso.S
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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: Will Deacon <will.deacon@arm.com>
+ */
+
+#include <linux/init.h>
+#include <linux/linkage.h>
+#include <linux/const.h>
+#include <asm/page.h>
+
+ __PAGE_ALIGNED_DATA
+
+ .globl vdso_start, vdso_end
+ .balign PAGE_SIZE
+vdso_start:
+ .incbin "arch/arm64/kernel/vdso/vdso.so"
+ .balign PAGE_SIZE
+vdso_end:
+
+ .previous
diff --git a/arch/arm64/kernel/vdso/vdso.lds.S b/arch/arm64/kernel/vdso/vdso.lds.S
new file mode 100644
index 000000000000..8154b8d1c826
--- /dev/null
+++ b/arch/arm64/kernel/vdso/vdso.lds.S
@@ -0,0 +1,100 @@
+/*
+ * GNU linker script for the VDSO library.
+*
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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: Will Deacon <will.deacon@arm.com>
+ * Heavily based on the vDSO linker scripts for other archs.
+ */
+
+#include <linux/const.h>
+#include <asm/page.h>
+#include <asm/vdso.h>
+
+OUTPUT_FORMAT("elf64-littleaarch64", "elf64-bigaarch64", "elf64-littleaarch64")
+OUTPUT_ARCH(aarch64)
+
+SECTIONS
+{
+ . = VDSO_LBASE + SIZEOF_HEADERS;
+
+ .hash : { *(.hash) } :text
+ .gnu.hash : { *(.gnu.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+
+ .note : { *(.note.*) } :text :note
+
+ . = ALIGN(16);
+
+ .text : { *(.text*) } :text =0xd503201f
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+
+ .eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
+ .eh_frame : { KEEP (*(.eh_frame)) } :text
+
+ .dynamic : { *(.dynamic) } :text :dynamic
+
+ .rodata : { *(.rodata*) } :text
+
+ _end = .;
+ PROVIDE(end = .);
+
+ . = ALIGN(PAGE_SIZE);
+ PROVIDE(_vdso_data = .);
+
+ /DISCARD/ : {
+ *(.note.GNU-stack)
+ *(.data .data.* .gnu.linkonce.d.* .sdata*)
+ *(.bss .sbss .dynbss .dynsbss)
+ }
+}
+
+/*
+ * We must supply the ELF program headers explicitly to get just one
+ * PT_LOAD segment, and set the flags explicitly to make segments read-only.
+ */
+PHDRS
+{
+ text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
+ dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
+ note PT_NOTE FLAGS(4); /* PF_R */
+ eh_frame_hdr PT_GNU_EH_FRAME;
+}
+
+/*
+ * This controls what symbols we export from the DSO.
+ */
+VERSION
+{
+ LINUX_2.6.39 {
+ global:
+ __kernel_rt_sigreturn;
+ __kernel_gettimeofday;
+ __kernel_clock_gettime;
+ __kernel_clock_getres;
+ local: *;
+ };
+}
+
+/*
+ * Make the sigreturn code visible to the kernel.
+ */
+VDSO_sigtramp = __kernel_rt_sigreturn;
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
new file mode 100644
index 000000000000..3fae2be8b016
--- /dev/null
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -0,0 +1,126 @@
+/*
+ * ld script to make ARM Linux kernel
+ * taken from the i386 version by Russell King
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ */
+
+#include <asm-generic/vmlinux.lds.h>
+#include <asm/thread_info.h>
+#include <asm/memory.h>
+#include <asm/page.h>
+
+#define ARM_EXIT_KEEP(x)
+#define ARM_EXIT_DISCARD(x) x
+
+OUTPUT_ARCH(aarch64)
+ENTRY(stext)
+
+jiffies = jiffies_64;
+
+SECTIONS
+{
+ /*
+ * XXX: The linker does not define how output sections are
+ * assigned to input sections when there are multiple statements
+ * matching the same input section name. There is no documented
+ * order of matching.
+ */
+ /DISCARD/ : {
+ ARM_EXIT_DISCARD(EXIT_TEXT)
+ ARM_EXIT_DISCARD(EXIT_DATA)
+ EXIT_CALL
+ *(.discard)
+ *(.discard.*)
+ }
+
+ . = PAGE_OFFSET + TEXT_OFFSET;
+
+ .head.text : {
+ _text = .;
+ HEAD_TEXT
+ }
+ .text : { /* Real text segment */
+ _stext = .; /* Text and read-only data */
+ *(.smp.pen.text)
+ __exception_text_start = .;
+ *(.exception.text)
+ __exception_text_end = .;
+ IRQENTRY_TEXT
+ TEXT_TEXT
+ SCHED_TEXT
+ LOCK_TEXT
+ *(.fixup)
+ *(.gnu.warning)
+ . = ALIGN(16);
+ *(.got) /* Global offset table */
+ }
+
+ RO_DATA(PAGE_SIZE)
+
+ _etext = .; /* End of text and rodata section */
+
+ . = ALIGN(PAGE_SIZE);
+ __init_begin = .;
+
+ INIT_TEXT_SECTION(8)
+ .exit.text : {
+ ARM_EXIT_KEEP(EXIT_TEXT)
+ }
+ . = ALIGN(16);
+ .init.data : {
+ INIT_DATA
+ INIT_SETUP(16)
+ INIT_CALLS
+ CON_INITCALL
+ SECURITY_INITCALL
+ INIT_RAM_FS
+ }
+ .exit.data : {
+ ARM_EXIT_KEEP(EXIT_DATA)
+ }
+
+ PERCPU_SECTION(64)
+
+ __init_end = .;
+ . = ALIGN(THREAD_SIZE);
+ __data_loc = .;
+
+ .data : AT(__data_loc) {
+ _data = .; /* address in memory */
+ _sdata = .;
+
+ /*
+ * first, the init task union, aligned
+ * to an 8192 byte boundary.
+ */
+ INIT_TASK_DATA(THREAD_SIZE)
+ NOSAVE_DATA
+ CACHELINE_ALIGNED_DATA(64)
+ READ_MOSTLY_DATA(64)
+
+ /*
+ * The exception fixup table (might need resorting at runtime)
+ */
+ . = ALIGN(32);
+ __start___ex_table = .;
+ *(__ex_table)
+ __stop___ex_table = .;
+
+ /*
+ * and the usual data section
+ */
+ DATA_DATA
+ CONSTRUCTORS
+
+ _edata = .;
+ }
+ _edata_loc = __data_loc + SIZEOF(.data);
+
+ NOTES
+
+ BSS_SECTION(0, 0, 0)
+ _end = .;
+
+ STABS_DEBUG
+ .comment 0 : { *(.comment) }
+}
diff --git a/arch/arm64/lib/Makefile b/arch/arm64/lib/Makefile
new file mode 100644
index 000000000000..2fb7f6092aae
--- /dev/null
+++ b/arch/arm64/lib/Makefile
@@ -0,0 +1,4 @@
+lib-y := bitops.o delay.o \
+ strncpy_from_user.o strnlen_user.o clear_user.o \
+ copy_from_user.o copy_to_user.o copy_in_user.o \
+ copy_page.o clear_page.o
diff --git a/arch/arm64/lib/bitops.c b/arch/arm64/lib/bitops.c
new file mode 100644
index 000000000000..aa4965e60acc
--- /dev/null
+++ b/arch/arm64/lib/bitops.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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>
+#include <linux/spinlock.h>
+#include <linux/atomic.h>
+
+#ifdef CONFIG_SMP
+arch_spinlock_t __atomic_hash[ATOMIC_HASH_SIZE] __lock_aligned = {
+ [0 ... (ATOMIC_HASH_SIZE-1)] = __ARCH_SPIN_LOCK_UNLOCKED
+};
+#endif
diff --git a/arch/arm64/lib/clear_page.S b/arch/arm64/lib/clear_page.S
new file mode 100644
index 000000000000..ef08e905e35b
--- /dev/null
+++ b/arch/arm64/lib/clear_page.S
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+#include <linux/const.h>
+#include <asm/assembler.h>
+#include <asm/page.h>
+
+/*
+ * Clear page @dest
+ *
+ * Parameters:
+ * x0 - dest
+ */
+ENTRY(clear_page)
+ mrs x1, dczid_el0
+ and w1, w1, #0xf
+ mov x2, #4
+ lsl x1, x2, x1
+
+1: dc zva, x0
+ add x0, x0, x1
+ tst x0, #(PAGE_SIZE - 1)
+ b.ne 1b
+ ret
+ENDPROC(clear_page)
diff --git a/arch/arm64/lib/clear_user.S b/arch/arm64/lib/clear_user.S
new file mode 100644
index 000000000000..6e0ed93d51fe
--- /dev/null
+++ b/arch/arm64/lib/clear_user.S
@@ -0,0 +1,58 @@
+/*
+ * Based on arch/arm/lib/clear_user.S
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+ .text
+
+/* Prototype: int __clear_user(void *addr, size_t sz)
+ * Purpose : clear some user memory
+ * Params : addr - user memory address to clear
+ * : sz - number of bytes to clear
+ * Returns : number of bytes NOT cleared
+ *
+ * Alignment fixed up by hardware.
+ */
+ENTRY(__clear_user)
+ mov x2, x1 // save the size for fixup return
+ subs x1, x1, #8
+ b.mi 2f
+1:
+USER(9f, str xzr, [x0], #8 )
+ subs x1, x1, #8
+ b.pl 1b
+2: adds x1, x1, #4
+ b.mi 3f
+USER(9f, str wzr, [x0], #4 )
+ sub x1, x1, #4
+3: adds x1, x1, #2
+ b.mi 4f
+USER(9f, strh wzr, [x0], #2 )
+ sub x1, x1, #2
+4: adds x1, x1, #1
+ b.mi 5f
+ strb wzr, [x0]
+5: mov x0, #0
+ ret
+ENDPROC(__clear_user)
+
+ .section .fixup,"ax"
+ .align 2
+9: mov x0, x2 // return the original size
+ ret
+ .previous
diff --git a/arch/arm64/lib/copy_from_user.S b/arch/arm64/lib/copy_from_user.S
new file mode 100644
index 000000000000..5e27add9d362
--- /dev/null
+++ b/arch/arm64/lib/copy_from_user.S
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+/*
+ * Copy from user space to a kernel buffer (alignment handled by the hardware)
+ *
+ * Parameters:
+ * x0 - to
+ * x1 - from
+ * x2 - n
+ * Returns:
+ * x0 - bytes not copied
+ */
+ENTRY(__copy_from_user)
+ add x4, x1, x2 // upper user buffer boundary
+ subs x2, x2, #8
+ b.mi 2f
+1:
+USER(9f, ldr x3, [x1], #8 )
+ subs x2, x2, #8
+ str x3, [x0], #8
+ b.pl 1b
+2: adds x2, x2, #4
+ b.mi 3f
+USER(9f, ldr w3, [x1], #4 )
+ sub x2, x2, #4
+ str w3, [x0], #4
+3: adds x2, x2, #2
+ b.mi 4f
+USER(9f, ldrh w3, [x1], #2 )
+ sub x2, x2, #2
+ strh w3, [x0], #2
+4: adds x2, x2, #1
+ b.mi 5f
+USER(9f, ldrb w3, [x1] )
+ strb w3, [x0]
+5: mov x0, #0
+ ret
+ENDPROC(__copy_from_user)
+
+ .section .fixup,"ax"
+ .align 2
+9: sub x2, x4, x1
+ mov x3, x2
+10: strb wzr, [x0], #1 // zero remaining buffer space
+ subs x3, x3, #1
+ b.ne 10b
+ mov x0, x2 // bytes not copied
+ ret
+ .previous
diff --git a/arch/arm64/lib/copy_in_user.S b/arch/arm64/lib/copy_in_user.S
new file mode 100644
index 000000000000..84b6c9bb9b93
--- /dev/null
+++ b/arch/arm64/lib/copy_in_user.S
@@ -0,0 +1,63 @@
+/*
+ * Copy from user space to user space
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+/*
+ * Copy from user space to user space (alignment handled by the hardware)
+ *
+ * Parameters:
+ * x0 - to
+ * x1 - from
+ * x2 - n
+ * Returns:
+ * x0 - bytes not copied
+ */
+ENTRY(__copy_in_user)
+ add x4, x0, x2 // upper user buffer boundary
+ subs x2, x2, #8
+ b.mi 2f
+1:
+USER(9f, ldr x3, [x1], #8 )
+ subs x2, x2, #8
+USER(9f, str x3, [x0], #8 )
+ b.pl 1b
+2: adds x2, x2, #4
+ b.mi 3f
+USER(9f, ldr w3, [x1], #4 )
+ sub x2, x2, #4
+USER(9f, str w3, [x0], #4 )
+3: adds x2, x2, #2
+ b.mi 4f
+USER(9f, ldrh w3, [x1], #2 )
+ sub x2, x2, #2
+USER(9f, strh w3, [x0], #2 )
+4: adds x2, x2, #1
+ b.mi 5f
+USER(9f, ldrb w3, [x1] )
+USER(9f, strb w3, [x0] )
+5: mov x0, #0
+ ret
+ENDPROC(__copy_in_user)
+
+ .section .fixup,"ax"
+ .align 2
+9: sub x0, x4, x0 // bytes not copied
+ ret
+ .previous
diff --git a/arch/arm64/lib/copy_page.S b/arch/arm64/lib/copy_page.S
new file mode 100644
index 000000000000..512b9a7b980e
--- /dev/null
+++ b/arch/arm64/lib/copy_page.S
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+#include <linux/const.h>
+#include <asm/assembler.h>
+#include <asm/page.h>
+
+/*
+ * Copy a page from src to dest (both are page aligned)
+ *
+ * Parameters:
+ * x0 - dest
+ * x1 - src
+ */
+ENTRY(copy_page)
+ /* Assume cache line size is 64 bytes. */
+ prfm pldl1strm, [x1, #64]
+1: ldp x2, x3, [x1]
+ ldp x4, x5, [x1, #16]
+ ldp x6, x7, [x1, #32]
+ ldp x8, x9, [x1, #48]
+ add x1, x1, #64
+ prfm pldl1strm, [x1, #64]
+ stnp x2, x3, [x0]
+ stnp x4, x5, [x0, #16]
+ stnp x6, x7, [x0, #32]
+ stnp x8, x9, [x0, #48]
+ add x0, x0, #64
+ tst x1, #(PAGE_SIZE - 1)
+ b.ne 1b
+ ret
+ENDPROC(copy_page)
diff --git a/arch/arm64/lib/copy_to_user.S b/arch/arm64/lib/copy_to_user.S
new file mode 100644
index 000000000000..a0aeeb9b7a28
--- /dev/null
+++ b/arch/arm64/lib/copy_to_user.S
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+/*
+ * Copy to user space from a kernel buffer (alignment handled by the hardware)
+ *
+ * Parameters:
+ * x0 - to
+ * x1 - from
+ * x2 - n
+ * Returns:
+ * x0 - bytes not copied
+ */
+ENTRY(__copy_to_user)
+ add x4, x0, x2 // upper user buffer boundary
+ subs x2, x2, #8
+ b.mi 2f
+1:
+ ldr x3, [x1], #8
+ subs x2, x2, #8
+USER(9f, str x3, [x0], #8 )
+ b.pl 1b
+2: adds x2, x2, #4
+ b.mi 3f
+ ldr w3, [x1], #4
+ sub x2, x2, #4
+USER(9f, str w3, [x0], #4 )
+3: adds x2, x2, #2
+ b.mi 4f
+ ldrh w3, [x1], #2
+ sub x2, x2, #2
+USER(9f, strh w3, [x0], #2 )
+4: adds x2, x2, #1
+ b.mi 5f
+ ldrb w3, [x1]
+USER(9f, strb w3, [x0] )
+5: mov x0, #0
+ ret
+ENDPROC(__copy_to_user)
+
+ .section .fixup,"ax"
+ .align 2
+9: sub x0, x4, x0 // bytes not copied
+ ret
+ .previous
diff --git a/arch/arm64/lib/delay.c b/arch/arm64/lib/delay.c
new file mode 100644
index 000000000000..dad4ec9bbfd1
--- /dev/null
+++ b/arch/arm64/lib/delay.c
@@ -0,0 +1,55 @@
+/*
+ * Delay loops based on the OpenRISC implementation.
+ *
+ * Copyright (C) 2012 ARM Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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: Will Deacon <will.deacon@arm.com>
+ */
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/timex.h>
+
+void __delay(unsigned long cycles)
+{
+ cycles_t start = get_cycles();
+
+ while ((get_cycles() - start) < cycles)
+ cpu_relax();
+}
+EXPORT_SYMBOL(__delay);
+
+inline void __const_udelay(unsigned long xloops)
+{
+ unsigned long loops;
+
+ loops = xloops * loops_per_jiffy * HZ;
+ __delay(loops >> 32);
+}
+EXPORT_SYMBOL(__const_udelay);
+
+void __udelay(unsigned long usecs)
+{
+ __const_udelay(usecs * 0x10C7UL); /* 2**32 / 1000000 (rounded up) */
+}
+EXPORT_SYMBOL(__udelay);
+
+void __ndelay(unsigned long nsecs)
+{
+ __const_udelay(nsecs * 0x5UL); /* 2**32 / 1000000000 (rounded up) */
+}
+EXPORT_SYMBOL(__ndelay);
diff --git a/arch/arm64/lib/strncpy_from_user.S b/arch/arm64/lib/strncpy_from_user.S
new file mode 100644
index 000000000000..56e448a831a0
--- /dev/null
+++ b/arch/arm64/lib/strncpy_from_user.S
@@ -0,0 +1,50 @@
+/*
+ * Based on arch/arm/lib/strncpy_from_user.S
+ *
+ * Copyright (C) 1995-2000 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/errno.h>
+
+ .text
+ .align 5
+
+/*
+ * Copy a string from user space to kernel space.
+ * x0 = dst, x1 = src, x2 = byte length
+ * returns the number of characters copied (strlen of copied string),
+ * -EFAULT on exception, or "len" if we fill the whole buffer
+ */
+ENTRY(__strncpy_from_user)
+ mov x4, x1
+1: subs x2, x2, #1
+ bmi 2f
+USER(9f, ldrb w3, [x1], #1 )
+ strb w3, [x0], #1
+ cbnz w3, 1b
+ sub x1, x1, #1 // take NUL character out of count
+2: sub x0, x1, x4
+ ret
+ENDPROC(__strncpy_from_user)
+
+ .section .fixup,"ax"
+ .align 0
+9: strb wzr, [x0] // null terminate
+ mov x0, #-EFAULT
+ ret
+ .previous
diff --git a/arch/arm64/lib/strnlen_user.S b/arch/arm64/lib/strnlen_user.S
new file mode 100644
index 000000000000..7f7b176a5646
--- /dev/null
+++ b/arch/arm64/lib/strnlen_user.S
@@ -0,0 +1,47 @@
+/*
+ * Based on arch/arm/lib/strnlen_user.S
+ *
+ * Copyright (C) 1995-2000 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/errno.h>
+
+ .text
+ .align 5
+
+/* Prototype: unsigned long __strnlen_user(const char *str, long n)
+ * Purpose : get length of a string in user memory
+ * Params : str - address of string in user memory
+ * Returns : length of string *including terminator*
+ * or zero on exception, or n if too long
+ */
+ENTRY(__strnlen_user)
+ mov x2, x0
+1: subs x1, x1, #1
+ b.mi 2f
+USER(9f, ldrb w3, [x0], #1 )
+ cbnz w3, 1b
+2: sub x0, x0, x2
+ ret
+ENDPROC(__strnlen_user)
+
+ .section .fixup,"ax"
+ .align 0
+9: mov x0, #0
+ ret
+ .previous
diff --git a/arch/arm64/mm/Makefile b/arch/arm64/mm/Makefile
new file mode 100644
index 000000000000..3140a2abcdc2
--- /dev/null
+++ b/arch/arm64/mm/Makefile
@@ -0,0 +1,4 @@
+obj-y := dma-mapping.o extable.o fault.o init.o \
+ cache.o copypage.o flush.o \
+ ioremap.o mmap.o pgd.o mmu.o \
+ context.o tlb.o proc.o
diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S
new file mode 100644
index 000000000000..abe69b80cf7f
--- /dev/null
+++ b/arch/arm64/mm/cache.S
@@ -0,0 +1,168 @@
+/*
+ * Cache maintenance
+ *
+ * Copyright (C) 2001 Deep Blue Solutions Ltd.
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <asm/assembler.h>
+
+#include "proc-macros.S"
+
+/*
+ * __flush_dcache_all()
+ *
+ * Flush the whole D-cache.
+ *
+ * Corrupted registers: x0-x7, x9-x11
+ */
+ENTRY(__flush_dcache_all)
+ dsb sy // ensure ordering with previous memory accesses
+ mrs x0, clidr_el1 // read clidr
+ and x3, x0, #0x7000000 // extract loc from clidr
+ lsr x3, x3, #23 // left align loc bit field
+ cbz x3, finished // if loc is 0, then no need to clean
+ mov x10, #0 // start clean at cache level 0
+loop1:
+ add x2, x10, x10, lsr #1 // work out 3x current cache level
+ lsr x1, x0, x2 // extract cache type bits from clidr
+ and x1, x1, #7 // mask of the bits for current cache only
+ cmp x1, #2 // see what cache we have at this level
+ b.lt skip // skip if no cache, or just i-cache
+ save_and_disable_irqs x9 // make CSSELR and CCSIDR access atomic
+ msr csselr_el1, x10 // select current cache level in csselr
+ isb // isb to sych the new cssr&csidr
+ mrs x1, ccsidr_el1 // read the new ccsidr
+ restore_irqs x9
+ and x2, x1, #7 // extract the length of the cache lines
+ add x2, x2, #4 // add 4 (line length offset)
+ mov x4, #0x3ff
+ and x4, x4, x1, lsr #3 // find maximum number on the way size
+ clz x5, x4 // find bit position of way size increment
+ mov x7, #0x7fff
+ and x7, x7, x1, lsr #13 // extract max number of the index size
+loop2:
+ mov x9, x4 // create working copy of max way size
+loop3:
+ lsl x6, x9, x5
+ orr x11, x10, x6 // factor way and cache number into x11
+ lsl x6, x7, x2
+ orr x11, x11, x6 // factor index number into x11
+ dc cisw, x11 // clean & invalidate by set/way
+ subs x9, x9, #1 // decrement the way
+ b.ge loop3
+ subs x7, x7, #1 // decrement the index
+ b.ge loop2
+skip:
+ add x10, x10, #2 // increment cache number
+ cmp x3, x10
+ b.gt loop1
+finished:
+ mov x10, #0 // swith back to cache level 0
+ msr csselr_el1, x10 // select current cache level in csselr
+ dsb sy
+ isb
+ ret
+ENDPROC(__flush_dcache_all)
+
+/*
+ * flush_cache_all()
+ *
+ * Flush the entire cache system. The data cache flush is now achieved
+ * using atomic clean / invalidates working outwards from L1 cache. This
+ * is done using Set/Way based cache maintainance instructions. The
+ * instruction cache can still be invalidated back to the point of
+ * unification in a single instruction.
+ */
+ENTRY(flush_cache_all)
+ mov x12, lr
+ bl __flush_dcache_all
+ mov x0, #0
+ ic ialluis // I+BTB cache invalidate
+ ret x12
+ENDPROC(flush_cache_all)
+
+/*
+ * flush_icache_range(start,end)
+ *
+ * Ensure that the I and D caches are coherent within specified region.
+ * This is typically used when code has been written to a memory region,
+ * and will be executed.
+ *
+ * - start - virtual start address of region
+ * - end - virtual end address of region
+ */
+ENTRY(flush_icache_range)
+ /* FALLTHROUGH */
+
+/*
+ * __flush_cache_user_range(start,end)
+ *
+ * Ensure that the I and D caches are coherent within specified region.
+ * This is typically used when code has been written to a memory region,
+ * and will be executed.
+ *
+ * - start - virtual start address of region
+ * - end - virtual end address of region
+ */
+ENTRY(__flush_cache_user_range)
+ dcache_line_size x2, x3
+ sub x3, x2, #1
+ bic x4, x0, x3
+1:
+USER(9f, dc cvau, x4 ) // clean D line to PoU
+ add x4, x4, x2
+ cmp x4, x1
+ b.lo 1b
+ dsb sy
+
+ icache_line_size x2, x3
+ sub x3, x2, #1
+ bic x4, x0, x3
+1:
+USER(9f, ic ivau, x4 ) // invalidate I line PoU
+ add x4, x4, x2
+ cmp x4, x1
+ b.lo 1b
+9: // ignore any faulting cache operation
+ dsb sy
+ isb
+ ret
+ENDPROC(flush_icache_range)
+ENDPROC(__flush_cache_user_range)
+
+/*
+ * __flush_kern_dcache_page(kaddr)
+ *
+ * Ensure that the data held in the page kaddr is written back to the
+ * page in question.
+ *
+ * - kaddr - kernel address
+ * - size - size in question
+ */
+ENTRY(__flush_dcache_area)
+ dcache_line_size x2, x3
+ add x1, x0, x1
+ sub x3, x2, #1
+ bic x0, x0, x3
+1: dc civac, x0 // clean & invalidate D line / unified line
+ add x0, x0, x2
+ cmp x0, x1
+ b.lo 1b
+ dsb sy
+ ret
+ENDPROC(__flush_dcache_area)
diff --git a/arch/arm64/mm/context.c b/arch/arm64/mm/context.c
new file mode 100644
index 000000000000..baa758d37021
--- /dev/null
+++ b/arch/arm64/mm/context.c
@@ -0,0 +1,159 @@
+/*
+ * Based on arch/arm/mm/context.c
+ *
+ * Copyright (C) 2002-2003 Deep Blue Solutions Ltd, all rights reserved.
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/percpu.h>
+
+#include <asm/mmu_context.h>
+#include <asm/tlbflush.h>
+#include <asm/cachetype.h>
+
+#define asid_bits(reg) \
+ (((read_cpuid(ID_AA64MMFR0_EL1) & 0xf0) >> 2) + 8)
+
+#define ASID_FIRST_VERSION (1 << MAX_ASID_BITS)
+
+static DEFINE_RAW_SPINLOCK(cpu_asid_lock);
+unsigned int cpu_last_asid = ASID_FIRST_VERSION;
+
+/*
+ * We fork()ed a process, and we need a new context for the child to run in.
+ */
+void __init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+ mm->context.id = 0;
+ raw_spin_lock_init(&mm->context.id_lock);
+}
+
+static void flush_context(void)
+{
+ /* set the reserved TTBR0 before flushing the TLB */
+ cpu_set_reserved_ttbr0();
+ flush_tlb_all();
+ if (icache_is_aivivt())
+ __flush_icache_all();
+}
+
+#ifdef CONFIG_SMP
+
+static void set_mm_context(struct mm_struct *mm, unsigned int asid)
+{
+ unsigned long flags;
+
+ /*
+ * Locking needed for multi-threaded applications where the same
+ * mm->context.id could be set from different CPUs during the
+ * broadcast. This function is also called via IPI so the
+ * mm->context.id_lock has to be IRQ-safe.
+ */
+ raw_spin_lock_irqsave(&mm->context.id_lock, flags);
+ if (likely((mm->context.id ^ cpu_last_asid) >> MAX_ASID_BITS)) {
+ /*
+ * Old version of ASID found. Set the new one and reset
+ * mm_cpumask(mm).
+ */
+ mm->context.id = asid;
+ cpumask_clear(mm_cpumask(mm));
+ }
+ raw_spin_unlock_irqrestore(&mm->context.id_lock, flags);
+
+ /*
+ * Set the mm_cpumask(mm) bit for the current CPU.
+ */
+ cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
+}
+
+/*
+ * Reset the ASID on the current CPU. This function call is broadcast from the
+ * CPU handling the ASID rollover and holding cpu_asid_lock.
+ */
+static void reset_context(void *info)
+{
+ unsigned int asid;
+ unsigned int cpu = smp_processor_id();
+ struct mm_struct *mm = current->active_mm;
+
+ smp_rmb();
+ asid = cpu_last_asid + cpu;
+
+ flush_context();
+ set_mm_context(mm, asid);
+
+ /* set the new ASID */
+ cpu_switch_mm(mm->pgd, mm);
+}
+
+#else
+
+static inline void set_mm_context(struct mm_struct *mm, unsigned int asid)
+{
+ mm->context.id = asid;
+ cpumask_copy(mm_cpumask(mm), cpumask_of(smp_processor_id()));
+}
+
+#endif
+
+void __new_context(struct mm_struct *mm)
+{
+ unsigned int asid;
+ unsigned int bits = asid_bits();
+
+ raw_spin_lock(&cpu_asid_lock);
+#ifdef CONFIG_SMP
+ /*
+ * Check the ASID again, in case the change was broadcast from another
+ * CPU before we acquired the lock.
+ */
+ if (!unlikely((mm->context.id ^ cpu_last_asid) >> MAX_ASID_BITS)) {
+ cpumask_set_cpu(smp_processor_id(), mm_cpumask(mm));
+ raw_spin_unlock(&cpu_asid_lock);
+ return;
+ }
+#endif
+ /*
+ * At this point, it is guaranteed that the current mm (with an old
+ * ASID) isn't active on any other CPU since the ASIDs are changed
+ * simultaneously via IPI.
+ */
+ asid = ++cpu_last_asid;
+
+ /*
+ * If we've used up all our ASIDs, we need to start a new version and
+ * flush the TLB.
+ */
+ if (unlikely((asid & ((1 << bits) - 1)) == 0)) {
+ /* increment the ASID version */
+ cpu_last_asid += (1 << MAX_ASID_BITS) - (1 << bits);
+ if (cpu_last_asid == 0)
+ cpu_last_asid = ASID_FIRST_VERSION;
+ asid = cpu_last_asid + smp_processor_id();
+ flush_context();
+#ifdef CONFIG_SMP
+ smp_wmb();
+ smp_call_function(reset_context, NULL, 1);
+#endif
+ cpu_last_asid += NR_CPUS - 1;
+ }
+
+ set_mm_context(mm, asid);
+ raw_spin_unlock(&cpu_asid_lock);
+}
diff --git a/arch/arm64/mm/copypage.c b/arch/arm64/mm/copypage.c
new file mode 100644
index 000000000000..9aecbace4128
--- /dev/null
+++ b/arch/arm64/mm/copypage.c
@@ -0,0 +1,34 @@
+/*
+ * Based on arch/arm/mm/copypage.c
+ *
+ * Copyright (C) 2002 Deep Blue Solutions Ltd, All Rights Reserved.
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/mm.h>
+
+#include <asm/page.h>
+#include <asm/cacheflush.h>
+
+void __cpu_copy_user_page(void *kto, const void *kfrom, unsigned long vaddr)
+{
+ copy_page(kto, kfrom);
+ __flush_dcache_area(kto, PAGE_SIZE);
+}
+
+void __cpu_clear_user_page(void *kaddr, unsigned long vaddr)
+{
+ clear_page(kaddr);
+}
diff --git a/arch/arm64/mm/dma-mapping.c b/arch/arm64/mm/dma-mapping.c
new file mode 100644
index 000000000000..5eb244453a5b
--- /dev/null
+++ b/arch/arm64/mm/dma-mapping.c
@@ -0,0 +1,79 @@
+/*
+ * SWIOTLB-based DMA API implementation
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Catalin Marinas <catalin.marinas@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/gfp.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/vmalloc.h>
+#include <linux/swiotlb.h>
+
+#include <asm/cacheflush.h>
+
+struct dma_map_ops *dma_ops;
+EXPORT_SYMBOL(dma_ops);
+
+static void *arm64_swiotlb_alloc_coherent(struct device *dev, size_t size,
+ dma_addr_t *dma_handle, gfp_t flags,
+ struct dma_attrs *attrs)
+{
+ if (IS_ENABLED(CONFIG_ZONE_DMA32) &&
+ dev->coherent_dma_mask <= DMA_BIT_MASK(32))
+ flags |= GFP_DMA32;
+ return swiotlb_alloc_coherent(dev, size, dma_handle, flags);
+}
+
+static void arm64_swiotlb_free_coherent(struct device *dev, size_t size,
+ void *vaddr, dma_addr_t dma_handle,
+ struct dma_attrs *attrs)
+{
+ swiotlb_free_coherent(dev, size, vaddr, dma_handle);
+}
+
+static struct dma_map_ops arm64_swiotlb_dma_ops = {
+ .alloc = arm64_swiotlb_alloc_coherent,
+ .free = arm64_swiotlb_free_coherent,
+ .map_page = swiotlb_map_page,
+ .unmap_page = swiotlb_unmap_page,
+ .map_sg = swiotlb_map_sg_attrs,
+ .unmap_sg = swiotlb_unmap_sg_attrs,
+ .sync_single_for_cpu = swiotlb_sync_single_for_cpu,
+ .sync_single_for_device = swiotlb_sync_single_for_device,
+ .sync_sg_for_cpu = swiotlb_sync_sg_for_cpu,
+ .sync_sg_for_device = swiotlb_sync_sg_for_device,
+ .dma_supported = swiotlb_dma_supported,
+ .mapping_error = swiotlb_dma_mapping_error,
+};
+
+void __init swiotlb_init_with_default_size(size_t default_size, int verbose);
+
+void __init arm64_swiotlb_init(size_t max_size)
+{
+ dma_ops = &arm64_swiotlb_dma_ops;
+ swiotlb_init_with_default_size(min((size_t)SZ_64M, max_size), 1);
+}
+
+#define PREALLOC_DMA_DEBUG_ENTRIES 4096
+
+static int __init dma_debug_do_init(void)
+{
+ dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
+ return 0;
+}
+fs_initcall(dma_debug_do_init);
diff --git a/arch/arm64/mm/extable.c b/arch/arm64/mm/extable.c
new file mode 100644
index 000000000000..79444279ba8c
--- /dev/null
+++ b/arch/arm64/mm/extable.c
@@ -0,0 +1,17 @@
+/*
+ * Based on arch/arm/mm/extable.c
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+int fixup_exception(struct pt_regs *regs)
+{
+ const struct exception_table_entry *fixup;
+
+ fixup = search_exception_tables(instruction_pointer(regs));
+ if (fixup)
+ regs->pc = fixup->fixup;
+
+ return fixup != NULL;
+}
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
new file mode 100644
index 000000000000..1909a69983ca
--- /dev/null
+++ b/arch/arm64/mm/fault.c
@@ -0,0 +1,534 @@
+/*
+ * Based on arch/arm/mm/fault.c
+ *
+ * Copyright (C) 1995 Linus Torvalds
+ * Copyright (C) 1995-2004 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/hardirq.h>
+#include <linux/init.h>
+#include <linux/kprobes.h>
+#include <linux/uaccess.h>
+#include <linux/page-flags.h>
+#include <linux/sched.h>
+#include <linux/highmem.h>
+#include <linux/perf_event.h>
+
+#include <asm/exception.h>
+#include <asm/debug-monitors.h>
+#include <asm/system_misc.h>
+#include <asm/pgtable.h>
+#include <asm/tlbflush.h>
+
+/*
+ * Dump out the page tables associated with 'addr' in mm 'mm'.
+ */
+void show_pte(struct mm_struct *mm, unsigned long addr)
+{
+ pgd_t *pgd;
+
+ if (!mm)
+ mm = &init_mm;
+
+ pr_alert("pgd = %p\n", mm->pgd);
+ pgd = pgd_offset(mm, addr);
+ pr_alert("[%08lx] *pgd=%016llx", addr, pgd_val(*pgd));
+
+ do {
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ if (pgd_none_or_clear_bad(pgd))
+ break;
+
+ pud = pud_offset(pgd, addr);
+ if (pud_none_or_clear_bad(pud))
+ break;
+
+ pmd = pmd_offset(pud, addr);
+ printk(", *pmd=%016llx", pmd_val(*pmd));
+ if (pmd_none_or_clear_bad(pmd))
+ break;
+
+ pte = pte_offset_map(pmd, addr);
+ printk(", *pte=%016llx", pte_val(*pte));
+ pte_unmap(pte);
+ } while(0);
+
+ printk("\n");
+}
+
+/*
+ * The kernel tried to access some page that wasn't present.
+ */
+static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr,
+ unsigned int esr, struct pt_regs *regs)
+{
+ /*
+ * Are we prepared to handle this kernel fault?
+ */
+ if (fixup_exception(regs))
+ return;
+
+ /*
+ * No handler, we'll have to terminate things with extreme prejudice.
+ */
+ bust_spinlocks(1);
+ pr_alert("Unable to handle kernel %s at virtual address %08lx\n",
+ (addr < PAGE_SIZE) ? "NULL pointer dereference" :
+ "paging request", addr);
+
+ show_pte(mm, addr);
+ die("Oops", regs, esr);
+ bust_spinlocks(0);
+ do_exit(SIGKILL);
+}
+
+/*
+ * Something tried to access memory that isn't in our memory map. User mode
+ * accesses just cause a SIGSEGV
+ */
+static void __do_user_fault(struct task_struct *tsk, unsigned long addr,
+ unsigned int esr, unsigned int sig, int code,
+ struct pt_regs *regs)
+{
+ struct siginfo si;
+
+ if (show_unhandled_signals) {
+ pr_info("%s[%d]: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
+ tsk->comm, task_pid_nr(tsk), sig, addr, esr);
+ show_pte(tsk->mm, addr);
+ show_regs(regs);
+ }
+
+ tsk->thread.fault_address = addr;
+ si.si_signo = sig;
+ si.si_errno = 0;
+ si.si_code = code;
+ si.si_addr = (void __user *)addr;
+ force_sig_info(sig, &si, tsk);
+}
+
+void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *regs)
+{
+ struct task_struct *tsk = current;
+ struct mm_struct *mm = tsk->active_mm;
+
+ /*
+ * If we are in kernel mode at this point, we have no context to
+ * handle this fault with.
+ */
+ if (user_mode(regs))
+ __do_user_fault(tsk, addr, esr, SIGSEGV, SEGV_MAPERR, regs);
+ else
+ __do_kernel_fault(mm, addr, esr, regs);
+}
+
+#define VM_FAULT_BADMAP 0x010000
+#define VM_FAULT_BADACCESS 0x020000
+
+#define ESR_WRITE (1 << 6)
+#define ESR_LNX_EXEC (1 << 24)
+
+/*
+ * Check that the permissions on the VMA allow for the fault which occurred.
+ * If we encountered a write fault, we must have write permission, otherwise
+ * we allow any permission.
+ */
+static inline bool access_error(unsigned int esr, struct vm_area_struct *vma)
+{
+ unsigned int mask = VM_READ | VM_WRITE | VM_EXEC;
+
+ if (esr & ESR_WRITE)
+ mask = VM_WRITE;
+ if (esr & ESR_LNX_EXEC)
+ mask = VM_EXEC;
+
+ return vma->vm_flags & mask ? false : true;
+}
+
+static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
+ unsigned int esr, unsigned int flags,
+ struct task_struct *tsk)
+{
+ struct vm_area_struct *vma;
+ int fault;
+
+ vma = find_vma(mm, addr);
+ fault = VM_FAULT_BADMAP;
+ if (unlikely(!vma))
+ goto out;
+ if (unlikely(vma->vm_start > addr))
+ goto check_stack;
+
+ /*
+ * Ok, we have a good vm_area for this memory access, so we can handle
+ * it.
+ */
+good_area:
+ if (access_error(esr, vma)) {
+ fault = VM_FAULT_BADACCESS;
+ goto out;
+ }
+
+ return handle_mm_fault(mm, vma, addr & PAGE_MASK, flags);
+
+check_stack:
+ if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr))
+ goto good_area;
+out:
+ return fault;
+}
+
+static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
+ struct pt_regs *regs)
+{
+ struct task_struct *tsk;
+ struct mm_struct *mm;
+ int fault, sig, code;
+ int write = esr & ESR_WRITE;
+ unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
+ (write ? FAULT_FLAG_WRITE : 0);
+
+ tsk = current;
+ mm = tsk->mm;
+
+ /* Enable interrupts if they were enabled in the parent context. */
+ if (interrupts_enabled(regs))
+ local_irq_enable();
+
+ /*
+ * If we're in an interrupt or have no user context, we must not take
+ * the fault.
+ */
+ if (in_atomic() || !mm)
+ goto no_context;
+
+ /*
+ * As per x86, we may deadlock here. However, since the kernel only
+ * validly references user space from well defined areas of the code,
+ * we can bug out early if this is from code which shouldn't.
+ */
+ if (!down_read_trylock(&mm->mmap_sem)) {
+ if (!user_mode(regs) && !search_exception_tables(regs->pc))
+ goto no_context;
+retry:
+ down_read(&mm->mmap_sem);
+ } else {
+ /*
+ * The above down_read_trylock() might have succeeded in which
+ * case, we'll have missed the might_sleep() from down_read().
+ */
+ might_sleep();
+#ifdef CONFIG_DEBUG_VM
+ if (!user_mode(regs) && !search_exception_tables(regs->pc))
+ goto no_context;
+#endif
+ }
+
+ fault = __do_page_fault(mm, addr, esr, flags, tsk);
+
+ /*
+ * If we need to retry but a fatal signal is pending, handle the
+ * signal first. We do not need to release the mmap_sem because it
+ * would already be released in __lock_page_or_retry in mm/filemap.c.
+ */
+ if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+ return 0;
+
+ /*
+ * Major/minor page fault accounting is only done on the initial
+ * attempt. If we go through a retry, it is extremely likely that the
+ * page will be found in page cache at that point.
+ */
+
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
+ if (flags & FAULT_FLAG_ALLOW_RETRY) {
+ if (fault & VM_FAULT_MAJOR) {
+ tsk->maj_flt++;
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs,
+ addr);
+ } else {
+ tsk->min_flt++;
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs,
+ addr);
+ }
+ if (fault & VM_FAULT_RETRY) {
+ /*
+ * Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk of
+ * starvation.
+ */
+ flags &= ~FAULT_FLAG_ALLOW_RETRY;
+ goto retry;
+ }
+ }
+
+ up_read(&mm->mmap_sem);
+
+ /*
+ * Handle the "normal" case first - VM_FAULT_MAJOR / VM_FAULT_MINOR
+ */
+ if (likely(!(fault & (VM_FAULT_ERROR | VM_FAULT_BADMAP |
+ VM_FAULT_BADACCESS))))
+ return 0;
+
+ if (fault & VM_FAULT_OOM) {
+ /*
+ * We ran out of memory, call the OOM killer, and return to
+ * userspace (which will retry the fault, or kill us if we got
+ * oom-killed).
+ */
+ pagefault_out_of_memory();
+ return 0;
+ }
+
+ /*
+ * If we are in kernel mode at this point, we have no context to
+ * handle this fault with.
+ */
+ if (!user_mode(regs))
+ goto no_context;
+
+ if (fault & VM_FAULT_SIGBUS) {
+ /*
+ * We had some memory, but were unable to successfully fix up
+ * this page fault.
+ */
+ sig = SIGBUS;
+ code = BUS_ADRERR;
+ } else {
+ /*
+ * Something tried to access memory that isn't in our memory
+ * map.
+ */
+ sig = SIGSEGV;
+ code = fault == VM_FAULT_BADACCESS ?
+ SEGV_ACCERR : SEGV_MAPERR;
+ }
+
+ __do_user_fault(tsk, addr, esr, sig, code, regs);
+ return 0;
+
+no_context:
+ __do_kernel_fault(mm, addr, esr, regs);
+ return 0;
+}
+
+/*
+ * First Level Translation Fault Handler
+ *
+ * We enter here because the first level page table doesn't contain a valid
+ * entry for the address.
+ *
+ * If the address is in kernel space (>= TASK_SIZE), then we are probably
+ * faulting in the vmalloc() area.
+ *
+ * If the init_task's first level page tables contains the relevant entry, we
+ * copy the it to this task. If not, we send the process a signal, fixup the
+ * exception, or oops the kernel.
+ *
+ * NOTE! We MUST NOT take any locks for this case. We may be in an interrupt
+ * or a critical region, and should only copy the information from the master
+ * page table, nothing more.
+ */
+static int __kprobes do_translation_fault(unsigned long addr,
+ unsigned int esr,
+ struct pt_regs *regs)
+{
+ if (addr < TASK_SIZE)
+ return do_page_fault(addr, esr, regs);
+
+ do_bad_area(addr, esr, regs);
+ return 0;
+}
+
+/*
+ * Some section permission faults need to be handled gracefully. They can
+ * happen due to a __{get,put}_user during an oops.
+ */
+static int do_sect_fault(unsigned long addr, unsigned int esr,
+ struct pt_regs *regs)
+{
+ do_bad_area(addr, esr, regs);
+ return 0;
+}
+
+/*
+ * This abort handler always returns "fault".
+ */
+static int do_bad(unsigned long addr, unsigned int esr, struct pt_regs *regs)
+{
+ return 1;
+}
+
+static struct fault_info {
+ int (*fn)(unsigned long addr, unsigned int esr, struct pt_regs *regs);
+ int sig;
+ int code;
+ const char *name;
+} fault_info[] = {
+ { do_bad, SIGBUS, 0, "ttbr address size fault" },
+ { do_bad, SIGBUS, 0, "level 1 address size fault" },
+ { do_bad, SIGBUS, 0, "level 2 address size fault" },
+ { do_bad, SIGBUS, 0, "level 3 address size fault" },
+ { do_translation_fault, SIGSEGV, SEGV_MAPERR, "input address range fault" },
+ { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 1 translation fault" },
+ { do_translation_fault, SIGSEGV, SEGV_MAPERR, "level 2 translation fault" },
+ { do_page_fault, SIGSEGV, SEGV_MAPERR, "level 3 translation fault" },
+ { do_bad, SIGBUS, 0, "reserved access flag fault" },
+ { do_bad, SIGSEGV, SEGV_ACCERR, "level 1 access flag fault" },
+ { do_bad, SIGSEGV, SEGV_ACCERR, "level 2 access flag fault" },
+ { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 access flag fault" },
+ { do_bad, SIGBUS, 0, "reserved permission fault" },
+ { do_bad, SIGSEGV, SEGV_ACCERR, "level 1 permission fault" },
+ { do_sect_fault, SIGSEGV, SEGV_ACCERR, "level 2 permission fault" },
+ { do_page_fault, SIGSEGV, SEGV_ACCERR, "level 3 permission fault" },
+ { do_bad, SIGBUS, 0, "synchronous external abort" },
+ { do_bad, SIGBUS, 0, "asynchronous external abort" },
+ { do_bad, SIGBUS, 0, "unknown 18" },
+ { do_bad, SIGBUS, 0, "unknown 19" },
+ { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
+ { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
+ { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
+ { do_bad, SIGBUS, 0, "synchronous abort (translation table walk)" },
+ { do_bad, SIGBUS, 0, "synchronous parity error" },
+ { do_bad, SIGBUS, 0, "asynchronous parity error" },
+ { do_bad, SIGBUS, 0, "unknown 26" },
+ { do_bad, SIGBUS, 0, "unknown 27" },
+ { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" },
+ { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" },
+ { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" },
+ { do_bad, SIGBUS, 0, "synchronous parity error (translation table walk" },
+ { do_bad, SIGBUS, 0, "unknown 32" },
+ { do_bad, SIGBUS, BUS_ADRALN, "alignment fault" },
+ { do_bad, SIGBUS, 0, "debug event" },
+ { do_bad, SIGBUS, 0, "unknown 35" },
+ { do_bad, SIGBUS, 0, "unknown 36" },
+ { do_bad, SIGBUS, 0, "unknown 37" },
+ { do_bad, SIGBUS, 0, "unknown 38" },
+ { do_bad, SIGBUS, 0, "unknown 39" },
+ { do_bad, SIGBUS, 0, "unknown 40" },
+ { do_bad, SIGBUS, 0, "unknown 41" },
+ { do_bad, SIGBUS, 0, "unknown 42" },
+ { do_bad, SIGBUS, 0, "unknown 43" },
+ { do_bad, SIGBUS, 0, "unknown 44" },
+ { do_bad, SIGBUS, 0, "unknown 45" },
+ { do_bad, SIGBUS, 0, "unknown 46" },
+ { do_bad, SIGBUS, 0, "unknown 47" },
+ { do_bad, SIGBUS, 0, "unknown 48" },
+ { do_bad, SIGBUS, 0, "unknown 49" },
+ { do_bad, SIGBUS, 0, "unknown 50" },
+ { do_bad, SIGBUS, 0, "unknown 51" },
+ { do_bad, SIGBUS, 0, "implementation fault (lockdown abort)" },
+ { do_bad, SIGBUS, 0, "unknown 53" },
+ { do_bad, SIGBUS, 0, "unknown 54" },
+ { do_bad, SIGBUS, 0, "unknown 55" },
+ { do_bad, SIGBUS, 0, "unknown 56" },
+ { do_bad, SIGBUS, 0, "unknown 57" },
+ { do_bad, SIGBUS, 0, "implementation fault (coprocessor abort)" },
+ { do_bad, SIGBUS, 0, "unknown 59" },
+ { do_bad, SIGBUS, 0, "unknown 60" },
+ { do_bad, SIGBUS, 0, "unknown 61" },
+ { do_bad, SIGBUS, 0, "unknown 62" },
+ { do_bad, SIGBUS, 0, "unknown 63" },
+};
+
+/*
+ * Dispatch a data abort to the relevant handler.
+ */
+asmlinkage void __exception do_mem_abort(unsigned long addr, unsigned int esr,
+ struct pt_regs *regs)
+{
+ const struct fault_info *inf = fault_info + (esr & 63);
+ struct siginfo info;
+
+ if (!inf->fn(addr, esr, regs))
+ return;
+
+ pr_alert("Unhandled fault: %s (0x%08x) at 0x%016lx\n",
+ inf->name, esr, addr);
+
+ info.si_signo = inf->sig;
+ info.si_errno = 0;
+ info.si_code = inf->code;
+ info.si_addr = (void __user *)addr;
+ arm64_notify_die("", regs, &info, esr);
+}
+
+/*
+ * Handle stack alignment exceptions.
+ */
+asmlinkage void __exception do_sp_pc_abort(unsigned long addr,
+ unsigned int esr,
+ struct pt_regs *regs)
+{
+ struct siginfo info;
+
+ info.si_signo = SIGBUS;
+ info.si_errno = 0;
+ info.si_code = BUS_ADRALN;
+ info.si_addr = (void __user *)addr;
+ arm64_notify_die("", regs, &info, esr);
+}
+
+static struct fault_info debug_fault_info[] = {
+ { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware breakpoint" },
+ { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware single-step" },
+ { do_bad, SIGTRAP, TRAP_HWBKPT, "hardware watchpoint" },
+ { do_bad, SIGBUS, 0, "unknown 3" },
+ { do_bad, SIGTRAP, TRAP_BRKPT, "aarch32 BKPT" },
+ { do_bad, SIGTRAP, 0, "aarch32 vector catch" },
+ { do_bad, SIGTRAP, TRAP_BRKPT, "aarch64 BRK" },
+ { do_bad, SIGBUS, 0, "unknown 7" },
+};
+
+void __init hook_debug_fault_code(int nr,
+ int (*fn)(unsigned long, unsigned int, struct pt_regs *),
+ int sig, int code, const char *name)
+{
+ BUG_ON(nr < 0 || nr >= ARRAY_SIZE(debug_fault_info));
+
+ debug_fault_info[nr].fn = fn;
+ debug_fault_info[nr].sig = sig;
+ debug_fault_info[nr].code = code;
+ debug_fault_info[nr].name = name;
+}
+
+asmlinkage int __exception do_debug_exception(unsigned long addr,
+ unsigned int esr,
+ struct pt_regs *regs)
+{
+ const struct fault_info *inf = debug_fault_info + DBG_ESR_EVT(esr);
+ struct siginfo info;
+
+ if (!inf->fn(addr, esr, regs))
+ return 1;
+
+ pr_alert("Unhandled debug exception: %s (0x%08x) at 0x%016lx\n",
+ inf->name, esr, addr);
+
+ info.si_signo = inf->sig;
+ info.si_errno = 0;
+ info.si_code = inf->code;
+ info.si_addr = (void __user *)addr;
+ arm64_notify_die("", regs, &info, esr);
+
+ return 0;
+}
diff --git a/arch/arm64/mm/flush.c b/arch/arm64/mm/flush.c
new file mode 100644
index 000000000000..c144adb1682f
--- /dev/null
+++ b/arch/arm64/mm/flush.c
@@ -0,0 +1,135 @@
+/*
+ * Based on arch/arm/mm/flush.c
+ *
+ * Copyright (C) 1995-2002 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cachetype.h>
+#include <asm/tlbflush.h>
+
+#include "mm.h"
+
+void flush_cache_mm(struct mm_struct *mm)
+{
+}
+
+void flush_cache_range(struct vm_area_struct *vma, unsigned long start,
+ unsigned long end)
+{
+ if (vma->vm_flags & VM_EXEC)
+ __flush_icache_all();
+}
+
+void flush_cache_page(struct vm_area_struct *vma, unsigned long user_addr,
+ unsigned long pfn)
+{
+}
+
+static void flush_ptrace_access(struct vm_area_struct *vma, struct page *page,
+ unsigned long uaddr, void *kaddr,
+ unsigned long len)
+{
+ if (vma->vm_flags & VM_EXEC) {
+ unsigned long addr = (unsigned long)kaddr;
+ if (icache_is_aliasing()) {
+ __flush_dcache_area(kaddr, len);
+ __flush_icache_all();
+ } else {
+ flush_icache_range(addr, addr + len);
+ }
+ }
+}
+
+/*
+ * Copy user data from/to a page which is mapped into a different processes
+ * address space. Really, we want to allow our "user space" model to handle
+ * this.
+ *
+ * Note that this code needs to run on the current CPU.
+ */
+void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
+ unsigned long uaddr, void *dst, const void *src,
+ unsigned long len)
+{
+#ifdef CONFIG_SMP
+ preempt_disable();
+#endif
+ memcpy(dst, src, len);
+ flush_ptrace_access(vma, page, uaddr, dst, len);
+#ifdef CONFIG_SMP
+ preempt_enable();
+#endif
+}
+
+void __flush_dcache_page(struct page *page)
+{
+ __flush_dcache_area(page_address(page), PAGE_SIZE);
+}
+
+void __sync_icache_dcache(pte_t pte, unsigned long addr)
+{
+ unsigned long pfn;
+ struct page *page;
+
+ pfn = pte_pfn(pte);
+ if (!pfn_valid(pfn))
+ return;
+
+ page = pfn_to_page(pfn);
+ if (!test_and_set_bit(PG_dcache_clean, &page->flags)) {
+ __flush_dcache_page(page);
+ __flush_icache_all();
+ } else if (icache_is_aivivt()) {
+ __flush_icache_all();
+ }
+}
+
+/*
+ * Ensure cache coherency between kernel mapping and userspace mapping of this
+ * page.
+ */
+void flush_dcache_page(struct page *page)
+{
+ struct address_space *mapping;
+
+ /*
+ * The zero page is never written to, so never has any dirty cache
+ * lines, and therefore never needs to be flushed.
+ */
+ if (page == ZERO_PAGE(0))
+ return;
+
+ mapping = page_mapping(page);
+ if (mapping && mapping_mapped(mapping)) {
+ __flush_dcache_page(page);
+ __flush_icache_all();
+ set_bit(PG_dcache_clean, &page->flags);
+ } else {
+ clear_bit(PG_dcache_clean, &page->flags);
+ }
+}
+EXPORT_SYMBOL(flush_dcache_page);
+
+/*
+ * Additional functions defined in assembly.
+ */
+EXPORT_SYMBOL(flush_cache_all);
+EXPORT_SYMBOL(flush_icache_range);
diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
new file mode 100644
index 000000000000..5f719ba949bc
--- /dev/null
+++ b/arch/arm64/mm/init.c
@@ -0,0 +1,437 @@
+/*
+ * Based on arch/arm/mm/init.c
+ *
+ * Copyright (C) 1995-2005 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/errno.h>
+#include <linux/swap.h>
+#include <linux/init.h>
+#include <linux/bootmem.h>
+#include <linux/mman.h>
+#include <linux/nodemask.h>
+#include <linux/initrd.h>
+#include <linux/gfp.h>
+#include <linux/memblock.h>
+#include <linux/sort.h>
+#include <linux/of_fdt.h>
+
+#include <asm/prom.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+#include <asm/sizes.h>
+#include <asm/tlb.h>
+
+#include "mm.h"
+
+static unsigned long phys_initrd_start __initdata = 0;
+static unsigned long phys_initrd_size __initdata = 0;
+
+phys_addr_t memstart_addr __read_mostly = 0;
+
+void __init early_init_dt_setup_initrd_arch(unsigned long start,
+ unsigned long end)
+{
+ phys_initrd_start = start;
+ phys_initrd_size = end - start;
+}
+
+static int __init early_initrd(char *p)
+{
+ unsigned long start, size;
+ char *endp;
+
+ start = memparse(p, &endp);
+ if (*endp == ',') {
+ size = memparse(endp + 1, NULL);
+
+ phys_initrd_start = start;
+ phys_initrd_size = size;
+ }
+ return 0;
+}
+early_param("initrd", early_initrd);
+
+#define MAX_DMA32_PFN ((4UL * 1024 * 1024 * 1024) >> PAGE_SHIFT)
+
+static void __init zone_sizes_init(unsigned long min, unsigned long max)
+{
+ struct memblock_region *reg;
+ unsigned long zone_size[MAX_NR_ZONES], zhole_size[MAX_NR_ZONES];
+ unsigned long max_dma32 = min;
+
+ memset(zone_size, 0, sizeof(zone_size));
+
+#ifdef CONFIG_ZONE_DMA32
+ /* 4GB maximum for 32-bit only capable devices */
+ max_dma32 = min(max, MAX_DMA32_PFN);
+ zone_size[ZONE_DMA32] = max_dma32 - min;
+#endif
+ zone_size[ZONE_NORMAL] = max - max_dma32;
+
+ memcpy(zhole_size, zone_size, sizeof(zhole_size));
+
+ for_each_memblock(memory, reg) {
+ unsigned long start = memblock_region_memory_base_pfn(reg);
+ unsigned long end = memblock_region_memory_end_pfn(reg);
+
+ if (start >= max)
+ continue;
+#ifdef CONFIG_ZONE_DMA32
+ if (start < max_dma32) {
+ unsigned long dma_end = min(end, max_dma32);
+ zhole_size[ZONE_DMA32] -= dma_end - start;
+ }
+#endif
+ if (end > max_dma32) {
+ unsigned long normal_end = min(end, max);
+ unsigned long normal_start = max(start, max_dma32);
+ zhole_size[ZONE_NORMAL] -= normal_end - normal_start;
+ }
+ }
+
+ free_area_init_node(0, zone_size, min, zhole_size);
+}
+
+#ifdef CONFIG_HAVE_ARCH_PFN_VALID
+int pfn_valid(unsigned long pfn)
+{
+ return memblock_is_memory(pfn << PAGE_SHIFT);
+}
+EXPORT_SYMBOL(pfn_valid);
+#endif
+
+#ifndef CONFIG_SPARSEMEM
+static void arm64_memory_present(void)
+{
+}
+#else
+static void arm64_memory_present(void)
+{
+ struct memblock_region *reg;
+
+ for_each_memblock(memory, reg)
+ memory_present(0, memblock_region_memory_base_pfn(reg),
+ memblock_region_memory_end_pfn(reg));
+}
+#endif
+
+void __init arm64_memblock_init(void)
+{
+ u64 *reserve_map, base, size;
+
+ /* Register the kernel text, kernel data and initrd with memblock */
+ memblock_reserve(__pa(_text), _end - _text);
+#ifdef CONFIG_BLK_DEV_INITRD
+ if (phys_initrd_size) {
+ memblock_reserve(phys_initrd_start, phys_initrd_size);
+
+ /* Now convert initrd to virtual addresses */
+ initrd_start = __phys_to_virt(phys_initrd_start);
+ initrd_end = initrd_start + phys_initrd_size;
+ }
+#endif
+
+ /*
+ * Reserve the page tables. These are already in use,
+ * and can only be in node 0.
+ */
+ memblock_reserve(__pa(swapper_pg_dir), SWAPPER_DIR_SIZE);
+ memblock_reserve(__pa(idmap_pg_dir), IDMAP_DIR_SIZE);
+
+ /* Reserve the dtb region */
+ memblock_reserve(virt_to_phys(initial_boot_params),
+ be32_to_cpu(initial_boot_params->totalsize));
+
+ /*
+ * Process the reserve map. This will probably overlap the initrd
+ * and dtb locations which are already reserved, but overlapping
+ * doesn't hurt anything
+ */
+ reserve_map = ((void*)initial_boot_params) +
+ be32_to_cpu(initial_boot_params->off_mem_rsvmap);
+ while (1) {
+ base = be64_to_cpup(reserve_map++);
+ size = be64_to_cpup(reserve_map++);
+ if (!size)
+ break;
+ memblock_reserve(base, size);
+ }
+
+ memblock_allow_resize();
+ memblock_dump_all();
+}
+
+void __init bootmem_init(void)
+{
+ unsigned long min, max;
+
+ min = PFN_UP(memblock_start_of_DRAM());
+ max = PFN_DOWN(memblock_end_of_DRAM());
+
+ /*
+ * Sparsemem tries to allocate bootmem in memory_present(), so must be
+ * done after the fixed reservations.
+ */
+ arm64_memory_present();
+
+ sparse_init();
+ zone_sizes_init(min, max);
+
+ high_memory = __va((max << PAGE_SHIFT) - 1) + 1;
+ max_pfn = max_low_pfn = max;
+}
+
+static inline int free_area(unsigned long pfn, unsigned long end, char *s)
+{
+ unsigned int pages = 0, size = (end - pfn) << (PAGE_SHIFT - 10);
+
+ for (; pfn < end; pfn++) {
+ struct page *page = pfn_to_page(pfn);
+ ClearPageReserved(page);
+ init_page_count(page);
+ __free_page(page);
+ pages++;
+ }
+
+ if (size && s)
+ pr_info("Freeing %s memory: %dK\n", s, size);
+
+ return pages;
+}
+
+/*
+ * Poison init memory with an undefined instruction (0x0).
+ */
+static inline void poison_init_mem(void *s, size_t count)
+{
+ memset(s, 0, count);
+}
+
+#ifndef CONFIG_SPARSEMEM_VMEMMAP
+static inline void free_memmap(unsigned long start_pfn, unsigned long end_pfn)
+{
+ struct page *start_pg, *end_pg;
+ unsigned long pg, pgend;
+
+ /*
+ * Convert start_pfn/end_pfn to a struct page pointer.
+ */
+ start_pg = pfn_to_page(start_pfn - 1) + 1;
+ end_pg = pfn_to_page(end_pfn - 1) + 1;
+
+ /*
+ * Convert to physical addresses, and round start upwards and end
+ * downwards.
+ */
+ pg = (unsigned long)PAGE_ALIGN(__pa(start_pg));
+ pgend = (unsigned long)__pa(end_pg) & PAGE_MASK;
+
+ /*
+ * If there are free pages between these, free the section of the
+ * memmap array.
+ */
+ if (pg < pgend)
+ free_bootmem(pg, pgend - pg);
+}
+
+/*
+ * The mem_map array can get very big. Free the unused area of the memory map.
+ */
+static void __init free_unused_memmap(void)
+{
+ unsigned long start, prev_end = 0;
+ struct memblock_region *reg;
+
+ for_each_memblock(memory, reg) {
+ start = __phys_to_pfn(reg->base);
+
+#ifdef CONFIG_SPARSEMEM
+ /*
+ * Take care not to free memmap entries that don't exist due
+ * to SPARSEMEM sections which aren't present.
+ */
+ start = min(start, ALIGN(prev_end, PAGES_PER_SECTION));
+#endif
+ /*
+ * If we had a previous bank, and there is a space between the
+ * current bank and the previous, free it.
+ */
+ if (prev_end && prev_end < start)
+ free_memmap(prev_end, start);
+
+ /*
+ * Align up here since the VM subsystem insists that the
+ * memmap entries are valid from the bank end aligned to
+ * MAX_ORDER_NR_PAGES.
+ */
+ prev_end = ALIGN(start + __phys_to_pfn(reg->size),
+ MAX_ORDER_NR_PAGES);
+ }
+
+#ifdef CONFIG_SPARSEMEM
+ if (!IS_ALIGNED(prev_end, PAGES_PER_SECTION))
+ free_memmap(prev_end, ALIGN(prev_end, PAGES_PER_SECTION));
+#endif
+}
+#endif /* !CONFIG_SPARSEMEM_VMEMMAP */
+
+/*
+ * mem_init() marks the free areas in the mem_map and tells us how much memory
+ * is free. This is done after various parts of the system have claimed their
+ * memory after the kernel image.
+ */
+void __init mem_init(void)
+{
+ unsigned long reserved_pages, free_pages;
+ struct memblock_region *reg;
+
+#if CONFIG_SWIOTLB
+ extern void __init arm64_swiotlb_init(size_t max_size);
+ arm64_swiotlb_init(max_pfn << (PAGE_SHIFT - 1));
+#endif
+
+ max_mapnr = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map;
+
+#ifndef CONFIG_SPARSEMEM_VMEMMAP
+ /* this will put all unused low memory onto the freelists */
+ free_unused_memmap();
+#endif
+
+ totalram_pages += free_all_bootmem();
+
+ reserved_pages = free_pages = 0;
+
+ for_each_memblock(memory, reg) {
+ unsigned int pfn1, pfn2;
+ struct page *page, *end;
+
+ pfn1 = __phys_to_pfn(reg->base);
+ pfn2 = pfn1 + __phys_to_pfn(reg->size);
+
+ page = pfn_to_page(pfn1);
+ end = pfn_to_page(pfn2 - 1) + 1;
+
+ do {
+ if (PageReserved(page))
+ reserved_pages++;
+ else if (!page_count(page))
+ free_pages++;
+ page++;
+ } while (page < end);
+ }
+
+ /*
+ * Since our memory may not be contiguous, calculate the real number
+ * of pages we have in this system.
+ */
+ pr_info("Memory:");
+ num_physpages = 0;
+ for_each_memblock(memory, reg) {
+ unsigned long pages = memblock_region_memory_end_pfn(reg) -
+ memblock_region_memory_base_pfn(reg);
+ num_physpages += pages;
+ printk(" %ldMB", pages >> (20 - PAGE_SHIFT));
+ }
+ printk(" = %luMB total\n", num_physpages >> (20 - PAGE_SHIFT));
+
+ pr_notice("Memory: %luk/%luk available, %luk reserved\n",
+ nr_free_pages() << (PAGE_SHIFT-10),
+ free_pages << (PAGE_SHIFT-10),
+ reserved_pages << (PAGE_SHIFT-10));
+
+#define MLK(b, t) b, t, ((t) - (b)) >> 10
+#define MLM(b, t) b, t, ((t) - (b)) >> 20
+#define MLK_ROUNDUP(b, t) b, t, DIV_ROUND_UP(((t) - (b)), SZ_1K)
+
+ pr_notice("Virtual kernel memory layout:\n"
+ " vmalloc : 0x%16lx - 0x%16lx (%6ld MB)\n"
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ " vmemmap : 0x%16lx - 0x%16lx (%6ld MB)\n"
+#endif
+ " modules : 0x%16lx - 0x%16lx (%6ld MB)\n"
+ " memory : 0x%16lx - 0x%16lx (%6ld MB)\n"
+ " .init : 0x%p" " - 0x%p" " (%6ld kB)\n"
+ " .text : 0x%p" " - 0x%p" " (%6ld kB)\n"
+ " .data : 0x%p" " - 0x%p" " (%6ld kB)\n",
+ MLM(VMALLOC_START, VMALLOC_END),
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+ MLM((unsigned long)virt_to_page(PAGE_OFFSET),
+ (unsigned long)virt_to_page(high_memory)),
+#endif
+ MLM(MODULES_VADDR, MODULES_END),
+ MLM(PAGE_OFFSET, (unsigned long)high_memory),
+
+ MLK_ROUNDUP(__init_begin, __init_end),
+ MLK_ROUNDUP(_text, _etext),
+ MLK_ROUNDUP(_sdata, _edata));
+
+#undef MLK
+#undef MLM
+#undef MLK_ROUNDUP
+
+ /*
+ * Check boundaries twice: Some fundamental inconsistencies can be
+ * detected at build time already.
+ */
+#ifdef CONFIG_COMPAT
+ BUILD_BUG_ON(TASK_SIZE_32 > TASK_SIZE_64);
+#endif
+ BUILD_BUG_ON(TASK_SIZE_64 > MODULES_VADDR);
+ BUG_ON(TASK_SIZE_64 > MODULES_VADDR);
+
+ if (PAGE_SIZE >= 16384 && num_physpages <= 128) {
+ extern int sysctl_overcommit_memory;
+ /*
+ * On a machine this small we won't get anywhere without
+ * overcommit, so turn it on by default.
+ */
+ sysctl_overcommit_memory = OVERCOMMIT_ALWAYS;
+ }
+}
+
+void free_initmem(void)
+{
+ poison_init_mem(__init_begin, __init_end - __init_begin);
+ totalram_pages += free_area(__phys_to_pfn(__pa(__init_begin)),
+ __phys_to_pfn(__pa(__init_end)),
+ "init");
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+
+static int keep_initrd;
+
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+ if (!keep_initrd) {
+ poison_init_mem((void *)start, PAGE_ALIGN(end) - start);
+ totalram_pages += free_area(__phys_to_pfn(__pa(start)),
+ __phys_to_pfn(__pa(end)),
+ "initrd");
+ }
+}
+
+static int __init keepinitrd_setup(char *__unused)
+{
+ keep_initrd = 1;
+ return 1;
+}
+
+__setup("keepinitrd", keepinitrd_setup);
+#endif
diff --git a/arch/arm64/mm/ioremap.c b/arch/arm64/mm/ioremap.c
new file mode 100644
index 000000000000..1725cd6db37a
--- /dev/null
+++ b/arch/arm64/mm/ioremap.c
@@ -0,0 +1,84 @@
+/*
+ * Based on arch/arm/mm/ioremap.c
+ *
+ * (C) Copyright 1995 1996 Linus Torvalds
+ * Hacked for ARM by Phil Blundell <philb@gnu.org>
+ * Hacked to allow all architectures to build, and various cleanups
+ * by Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/io.h>
+
+static void __iomem *__ioremap_caller(phys_addr_t phys_addr, size_t size,
+ pgprot_t prot, void *caller)
+{
+ unsigned long last_addr;
+ unsigned long offset = phys_addr & ~PAGE_MASK;
+ int err;
+ unsigned long addr;
+ struct vm_struct *area;
+
+ /*
+ * Page align the mapping address and size, taking account of any
+ * offset.
+ */
+ phys_addr &= PAGE_MASK;
+ size = PAGE_ALIGN(size + offset);
+
+ /*
+ * Don't allow wraparound, zero size or outside PHYS_MASK.
+ */
+ last_addr = phys_addr + size - 1;
+ if (!size || last_addr < phys_addr || (last_addr & ~PHYS_MASK))
+ return NULL;
+
+ /*
+ * Don't allow RAM to be mapped.
+ */
+ if (WARN_ON(pfn_valid(__phys_to_pfn(phys_addr))))
+ return NULL;
+
+ area = get_vm_area_caller(size, VM_IOREMAP, caller);
+ if (!area)
+ return NULL;
+ addr = (unsigned long)area->addr;
+
+ err = ioremap_page_range(addr, addr + size, phys_addr, prot);
+ if (err) {
+ vunmap((void *)addr);
+ return NULL;
+ }
+
+ return (void __iomem *)(offset + addr);
+}
+
+void __iomem *__ioremap(phys_addr_t phys_addr, size_t size, pgprot_t prot)
+{
+ return __ioremap_caller(phys_addr, size, prot,
+ __builtin_return_address(0));
+}
+EXPORT_SYMBOL(__ioremap);
+
+void __iounmap(volatile void __iomem *io_addr)
+{
+ void *addr = (void *)(PAGE_MASK & (unsigned long)io_addr);
+
+ vunmap(addr);
+}
+EXPORT_SYMBOL(__iounmap);
diff --git a/arch/arm64/mm/mm.h b/arch/arm64/mm/mm.h
new file mode 100644
index 000000000000..d8d6e7851c14
--- /dev/null
+++ b/arch/arm64/mm/mm.h
@@ -0,0 +1,2 @@
+extern void __flush_dcache_page(struct page *page);
+extern void __init bootmem_init(void);
diff --git a/arch/arm64/mm/mmap.c b/arch/arm64/mm/mmap.c
new file mode 100644
index 000000000000..7c7be7855638
--- /dev/null
+++ b/arch/arm64/mm/mmap.c
@@ -0,0 +1,144 @@
+/*
+ * Based on arch/arm/mm/mmap.c
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/elf.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/export.h>
+#include <linux/shm.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+#include <linux/personality.h>
+#include <linux/random.h>
+
+#include <asm/cputype.h>
+
+/*
+ * Leave enough space between the mmap area and the stack to honour ulimit in
+ * the face of randomisation.
+ */
+#define MIN_GAP (SZ_128M + ((STACK_RND_MASK << PAGE_SHIFT) + 1))
+#define MAX_GAP (STACK_TOP/6*5)
+
+static int mmap_is_legacy(void)
+{
+ if (current->personality & ADDR_COMPAT_LAYOUT)
+ return 1;
+
+ if (rlimit(RLIMIT_STACK) == RLIM_INFINITY)
+ return 1;
+
+ return sysctl_legacy_va_layout;
+}
+
+/*
+ * Since get_random_int() returns the same value within a 1 jiffy window, we
+ * will almost always get the same randomisation for the stack and mmap
+ * region. This will mean the relative distance between stack and mmap will be
+ * the same.
+ *
+ * To avoid this we can shift the randomness by 1 bit.
+ */
+static unsigned long mmap_rnd(void)
+{
+ unsigned long rnd = 0;
+
+ if (current->flags & PF_RANDOMIZE)
+ rnd = (long)get_random_int() & (STACK_RND_MASK >> 1);
+
+ return rnd << (PAGE_SHIFT + 1);
+}
+
+static unsigned long mmap_base(void)
+{
+ unsigned long gap = rlimit(RLIMIT_STACK);
+
+ if (gap < MIN_GAP)
+ gap = MIN_GAP;
+ else if (gap > MAX_GAP)
+ gap = MAX_GAP;
+
+ return PAGE_ALIGN(STACK_TOP - gap - mmap_rnd());
+}
+
+/*
+ * This function, called very early during the creation of a new process VM
+ * image, sets up which VM layout function to use:
+ */
+void arch_pick_mmap_layout(struct mm_struct *mm)
+{
+ /*
+ * Fall back to the standard layout if the personality bit is set, or
+ * if the expected stack growth is unlimited:
+ */
+ if (mmap_is_legacy()) {
+ mm->mmap_base = TASK_UNMAPPED_BASE;
+ mm->get_unmapped_area = arch_get_unmapped_area;
+ mm->unmap_area = arch_unmap_area;
+ } else {
+ mm->mmap_base = mmap_base();
+ mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+ mm->unmap_area = arch_unmap_area_topdown;
+ }
+}
+EXPORT_SYMBOL_GPL(arch_pick_mmap_layout);
+
+
+/*
+ * You really shouldn't be using read() or write() on /dev/mem. This might go
+ * away in the future.
+ */
+int valid_phys_addr_range(unsigned long addr, size_t size)
+{
+ if (addr < PHYS_OFFSET)
+ return 0;
+ if (addr + size > __pa(high_memory - 1) + 1)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Do not allow /dev/mem mappings beyond the supported physical range.
+ */
+int valid_mmap_phys_addr_range(unsigned long pfn, size_t size)
+{
+ return !(((pfn << PAGE_SHIFT) + size) & ~PHYS_MASK);
+}
+
+#ifdef CONFIG_STRICT_DEVMEM
+
+#include <linux/ioport.h>
+
+/*
+ * devmem_is_allowed() checks to see if /dev/mem access to a certain address
+ * is valid. The argument is a physical page number. We mimic x86 here by
+ * disallowing access to system RAM as well as device-exclusive MMIO regions.
+ * This effectively disable read()/write() on /dev/mem.
+ */
+int devmem_is_allowed(unsigned long pfn)
+{
+ if (iomem_is_exclusive(pfn << PAGE_SHIFT))
+ return 0;
+ if (!page_is_ram(pfn))
+ return 1;
+ return 0;
+}
+
+#endif
diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
new file mode 100644
index 000000000000..a6885d896ab6
--- /dev/null
+++ b/arch/arm64/mm/mmu.c
@@ -0,0 +1,395 @@
+/*
+ * Based on arch/arm/mm/mmu.c
+ *
+ * Copyright (C) 1995-2005 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/mman.h>
+#include <linux/nodemask.h>
+#include <linux/memblock.h>
+#include <linux/fs.h>
+
+#include <asm/cputype.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+#include <asm/sizes.h>
+#include <asm/tlb.h>
+#include <asm/mmu_context.h>
+
+#include "mm.h"
+
+/*
+ * Empty_zero_page is a special page that is used for zero-initialized data
+ * and COW.
+ */
+struct page *empty_zero_page;
+EXPORT_SYMBOL(empty_zero_page);
+
+pgprot_t pgprot_default;
+EXPORT_SYMBOL(pgprot_default);
+
+static pmdval_t prot_sect_kernel;
+
+struct cachepolicy {
+ const char policy[16];
+ u64 mair;
+ u64 tcr;
+};
+
+static struct cachepolicy cache_policies[] __initdata = {
+ {
+ .policy = "uncached",
+ .mair = 0x44, /* inner, outer non-cacheable */
+ .tcr = TCR_IRGN_NC | TCR_ORGN_NC,
+ }, {
+ .policy = "writethrough",
+ .mair = 0xaa, /* inner, outer write-through, read-allocate */
+ .tcr = TCR_IRGN_WT | TCR_ORGN_WT,
+ }, {
+ .policy = "writeback",
+ .mair = 0xee, /* inner, outer write-back, read-allocate */
+ .tcr = TCR_IRGN_WBnWA | TCR_ORGN_WBnWA,
+ }
+};
+
+/*
+ * These are useful for identifying cache coherency problems by allowing the
+ * cache or the cache and writebuffer to be turned off. It changes the Normal
+ * memory caching attributes in the MAIR_EL1 register.
+ */
+static int __init early_cachepolicy(char *p)
+{
+ int i;
+ u64 tmp;
+
+ for (i = 0; i < ARRAY_SIZE(cache_policies); i++) {
+ int len = strlen(cache_policies[i].policy);
+
+ if (memcmp(p, cache_policies[i].policy, len) == 0)
+ break;
+ }
+ if (i == ARRAY_SIZE(cache_policies)) {
+ pr_err("ERROR: unknown or unsupported cache policy: %s\n", p);
+ return 0;
+ }
+
+ flush_cache_all();
+
+ /*
+ * Modify MT_NORMAL attributes in MAIR_EL1.
+ */
+ asm volatile(
+ " mrs %0, mair_el1\n"
+ " bfi %0, %1, #%2, #8\n"
+ " msr mair_el1, %0\n"
+ " isb\n"
+ : "=&r" (tmp)
+ : "r" (cache_policies[i].mair), "i" (MT_NORMAL * 8));
+
+ /*
+ * Modify TCR PTW cacheability attributes.
+ */
+ asm volatile(
+ " mrs %0, tcr_el1\n"
+ " bic %0, %0, %2\n"
+ " orr %0, %0, %1\n"
+ " msr tcr_el1, %0\n"
+ " isb\n"
+ : "=&r" (tmp)
+ : "r" (cache_policies[i].tcr), "r" (TCR_IRGN_MASK | TCR_ORGN_MASK));
+
+ flush_cache_all();
+
+ return 0;
+}
+early_param("cachepolicy", early_cachepolicy);
+
+/*
+ * Adjust the PMD section entries according to the CPU in use.
+ */
+static void __init init_mem_pgprot(void)
+{
+ pteval_t default_pgprot;
+ int i;
+
+ default_pgprot = PTE_ATTRINDX(MT_NORMAL);
+ prot_sect_kernel = PMD_TYPE_SECT | PMD_SECT_AF | PMD_ATTRINDX(MT_NORMAL);
+
+#ifdef CONFIG_SMP
+ /*
+ * Mark memory with the "shared" attribute for SMP systems
+ */
+ default_pgprot |= PTE_SHARED;
+ prot_sect_kernel |= PMD_SECT_S;
+#endif
+
+ for (i = 0; i < 16; i++) {
+ unsigned long v = pgprot_val(protection_map[i]);
+ protection_map[i] = __pgprot(v | default_pgprot);
+ }
+
+ pgprot_default = __pgprot(PTE_TYPE_PAGE | PTE_AF | default_pgprot);
+}
+
+pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
+ unsigned long size, pgprot_t vma_prot)
+{
+ if (!pfn_valid(pfn))
+ return pgprot_noncached(vma_prot);
+ else if (file->f_flags & O_SYNC)
+ return pgprot_writecombine(vma_prot);
+ return vma_prot;
+}
+EXPORT_SYMBOL(phys_mem_access_prot);
+
+static void __init *early_alloc(unsigned long sz)
+{
+ void *ptr = __va(memblock_alloc(sz, sz));
+ memset(ptr, 0, sz);
+ return ptr;
+}
+
+static void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
+ unsigned long end, unsigned long pfn)
+{
+ pte_t *pte;
+
+ if (pmd_none(*pmd)) {
+ pte = early_alloc(PTRS_PER_PTE * sizeof(pte_t));
+ __pmd_populate(pmd, __pa(pte), PMD_TYPE_TABLE);
+ }
+ BUG_ON(pmd_bad(*pmd));
+
+ pte = pte_offset_kernel(pmd, addr);
+ do {
+ set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC));
+ pfn++;
+ } while (pte++, addr += PAGE_SIZE, addr != end);
+}
+
+static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
+ unsigned long end, phys_addr_t phys)
+{
+ pmd_t *pmd;
+ unsigned long next;
+
+ /*
+ * Check for initial section mappings in the pgd/pud and remove them.
+ */
+ if (pud_none(*pud) || pud_bad(*pud)) {
+ pmd = early_alloc(PTRS_PER_PMD * sizeof(pmd_t));
+ pud_populate(&init_mm, pud, pmd);
+ }
+
+ pmd = pmd_offset(pud, addr);
+ do {
+ next = pmd_addr_end(addr, end);
+ /* try section mapping first */
+ if (((addr | next | phys) & ~SECTION_MASK) == 0)
+ set_pmd(pmd, __pmd(phys | prot_sect_kernel));
+ else
+ alloc_init_pte(pmd, addr, next, __phys_to_pfn(phys));
+ phys += next - addr;
+ } while (pmd++, addr = next, addr != end);
+}
+
+static void __init alloc_init_pud(pgd_t *pgd, unsigned long addr,
+ unsigned long end, unsigned long phys)
+{
+ pud_t *pud = pud_offset(pgd, addr);
+ unsigned long next;
+
+ do {
+ next = pud_addr_end(addr, end);
+ alloc_init_pmd(pud, addr, next, phys);
+ phys += next - addr;
+ } while (pud++, addr = next, addr != end);
+}
+
+/*
+ * Create the page directory entries and any necessary page tables for the
+ * mapping specified by 'md'.
+ */
+static void __init create_mapping(phys_addr_t phys, unsigned long virt,
+ phys_addr_t size)
+{
+ unsigned long addr, length, end, next;
+ pgd_t *pgd;
+
+ if (virt < VMALLOC_START) {
+ pr_warning("BUG: not creating mapping for 0x%016llx at 0x%016lx - outside kernel range\n",
+ phys, virt);
+ return;
+ }
+
+ addr = virt & PAGE_MASK;
+ length = PAGE_ALIGN(size + (virt & ~PAGE_MASK));
+
+ pgd = pgd_offset_k(addr);
+ end = addr + length;
+ do {
+ next = pgd_addr_end(addr, end);
+ alloc_init_pud(pgd, addr, next, phys);
+ phys += next - addr;
+ } while (pgd++, addr = next, addr != end);
+}
+
+static void __init map_mem(void)
+{
+ struct memblock_region *reg;
+
+ /* map all the memory banks */
+ for_each_memblock(memory, reg) {
+ phys_addr_t start = reg->base;
+ phys_addr_t end = start + reg->size;
+
+ if (start >= end)
+ break;
+
+ create_mapping(start, __phys_to_virt(start), end - start);
+ }
+}
+
+/*
+ * paging_init() sets up the page tables, initialises the zone memory
+ * maps and sets up the zero page.
+ */
+void __init paging_init(void)
+{
+ void *zero_page;
+
+ /*
+ * Maximum PGDIR_SIZE addressable via the initial direct kernel
+ * mapping in swapper_pg_dir.
+ */
+ memblock_set_current_limit((PHYS_OFFSET & PGDIR_MASK) + PGDIR_SIZE);
+
+ init_mem_pgprot();
+ map_mem();
+
+ /*
+ * Finally flush the caches and tlb to ensure that we're in a
+ * consistent state.
+ */
+ flush_cache_all();
+ flush_tlb_all();
+
+ /* allocate the zero page. */
+ zero_page = early_alloc(PAGE_SIZE);
+
+ bootmem_init();
+
+ empty_zero_page = virt_to_page(zero_page);
+ __flush_dcache_page(empty_zero_page);
+
+ /*
+ * TTBR0 is only used for the identity mapping at this stage. Make it
+ * point to zero page to avoid speculatively fetching new entries.
+ */
+ cpu_set_reserved_ttbr0();
+ flush_tlb_all();
+}
+
+/*
+ * Enable the identity mapping to allow the MMU disabling.
+ */
+void setup_mm_for_reboot(void)
+{
+ cpu_switch_mm(idmap_pg_dir, &init_mm);
+ flush_tlb_all();
+}
+
+/*
+ * Check whether a kernel address is valid (derived from arch/x86/).
+ */
+int kern_addr_valid(unsigned long addr)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *pte;
+
+ if ((((long)addr) >> VA_BITS) != -1UL)
+ return 0;
+
+ pgd = pgd_offset_k(addr);
+ if (pgd_none(*pgd))
+ return 0;
+
+ pud = pud_offset(pgd, addr);
+ if (pud_none(*pud))
+ return 0;
+
+ pmd = pmd_offset(pud, addr);
+ if (pmd_none(*pmd))
+ return 0;
+
+ pte = pte_offset_kernel(pmd, addr);
+ if (pte_none(*pte))
+ return 0;
+
+ return pfn_valid(pte_pfn(*pte));
+}
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
+#ifdef CONFIG_ARM64_64K_PAGES
+int __meminit vmemmap_populate(struct page *start_page,
+ unsigned long size, int node)
+{
+ return vmemmap_populate_basepages(start_page, size, node);
+}
+#else /* !CONFIG_ARM64_64K_PAGES */
+int __meminit vmemmap_populate(struct page *start_page,
+ unsigned long size, int node)
+{
+ unsigned long addr = (unsigned long)start_page;
+ unsigned long end = (unsigned long)(start_page + size);
+ unsigned long next;
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+
+ do {
+ next = pmd_addr_end(addr, end);
+
+ pgd = vmemmap_pgd_populate(addr, node);
+ if (!pgd)
+ return -ENOMEM;
+
+ pud = vmemmap_pud_populate(pgd, addr, node);
+ if (!pud)
+ return -ENOMEM;
+
+ pmd = pmd_offset(pud, addr);
+ if (pmd_none(*pmd)) {
+ void *p = NULL;
+
+ p = vmemmap_alloc_block_buf(PMD_SIZE, node);
+ if (!p)
+ return -ENOMEM;
+
+ set_pmd(pmd, __pmd(__pa(p) | prot_sect_kernel));
+ } else
+ vmemmap_verify((pte_t *)pmd, node, addr, next);
+ } while (addr = next, addr != end);
+
+ return 0;
+}
+#endif /* CONFIG_ARM64_64K_PAGES */
+#endif /* CONFIG_SPARSEMEM_VMEMMAP */
diff --git a/arch/arm64/mm/pgd.c b/arch/arm64/mm/pgd.c
new file mode 100644
index 000000000000..7083cdada657
--- /dev/null
+++ b/arch/arm64/mm/pgd.c
@@ -0,0 +1,54 @@
+/*
+ * PGD allocation/freeing
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Catalin Marinas <catalin.marinas@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/mm.h>
+#include <linux/gfp.h>
+#include <linux/highmem.h>
+#include <linux/slab.h>
+
+#include <asm/pgalloc.h>
+#include <asm/page.h>
+#include <asm/tlbflush.h>
+
+#include "mm.h"
+
+#define PGD_SIZE (PTRS_PER_PGD * sizeof(pgd_t))
+
+pgd_t *pgd_alloc(struct mm_struct *mm)
+{
+ pgd_t *new_pgd;
+
+ if (PGD_SIZE == PAGE_SIZE)
+ new_pgd = (pgd_t *)get_zeroed_page(GFP_KERNEL);
+ else
+ new_pgd = kzalloc(PGD_SIZE, GFP_KERNEL);
+
+ if (!new_pgd)
+ return NULL;
+
+ return new_pgd;
+}
+
+void pgd_free(struct mm_struct *mm, pgd_t *pgd)
+{
+ if (PGD_SIZE == PAGE_SIZE)
+ free_page((unsigned long)pgd);
+ else
+ kfree(pgd);
+}
diff --git a/arch/arm64/mm/proc-macros.S b/arch/arm64/mm/proc-macros.S
new file mode 100644
index 000000000000..8957b822010b
--- /dev/null
+++ b/arch/arm64/mm/proc-macros.S
@@ -0,0 +1,55 @@
+/*
+ * Based on arch/arm/mm/proc-macros.S
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
+
+/*
+ * vma_vm_mm - get mm pointer from vma pointer (vma->vm_mm)
+ */
+ .macro vma_vm_mm, rd, rn
+ ldr \rd, [\rn, #VMA_VM_MM]
+ .endm
+
+/*
+ * mmid - get context id from mm pointer (mm->context.id)
+ */
+ .macro mmid, rd, rn
+ ldr \rd, [\rn, #MM_CONTEXT_ID]
+ .endm
+
+/*
+ * dcache_line_size - get the minimum D-cache line size from the CTR register.
+ */
+ .macro dcache_line_size, reg, tmp
+ mrs \tmp, ctr_el0 // read CTR
+ lsr \tmp, \tmp, #16
+ and \tmp, \tmp, #0xf // cache line size encoding
+ mov \reg, #4 // bytes per word
+ lsl \reg, \reg, \tmp // actual cache line size
+ .endm
+
+/*
+ * icache_line_size - get the minimum I-cache line size from the CTR register.
+ */
+ .macro icache_line_size, reg, tmp
+ mrs \tmp, ctr_el0 // read CTR
+ and \tmp, \tmp, #0xf // cache line size encoding
+ mov \reg, #4 // bytes per word
+ lsl \reg, \reg, \tmp // actual cache line size
+ .endm
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
new file mode 100644
index 000000000000..f1d8b9bbfdad
--- /dev/null
+++ b/arch/arm64/mm/proc.S
@@ -0,0 +1,175 @@
+/*
+ * Based on arch/arm/mm/proc.S
+ *
+ * Copyright (C) 2001 Deep Blue Solutions Ltd.
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Catalin Marinas <catalin.marinas@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/init.h>
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/asm-offsets.h>
+#include <asm/hwcap.h>
+#include <asm/pgtable-hwdef.h>
+#include <asm/pgtable.h>
+
+#include "proc-macros.S"
+
+#ifndef CONFIG_SMP
+/* PTWs cacheable, inner/outer WBWA not shareable */
+#define TCR_FLAGS TCR_IRGN_WBWA | TCR_ORGN_WBWA
+#else
+/* PTWs cacheable, inner/outer WBWA shareable */
+#define TCR_FLAGS TCR_IRGN_WBWA | TCR_ORGN_WBWA | TCR_SHARED
+#endif
+
+#define MAIR(attr, mt) ((attr) << ((mt) * 8))
+
+/*
+ * cpu_cache_off()
+ *
+ * Turn the CPU D-cache off.
+ */
+ENTRY(cpu_cache_off)
+ mrs x0, sctlr_el1
+ bic x0, x0, #1 << 2 // clear SCTLR.C
+ msr sctlr_el1, x0
+ isb
+ ret
+ENDPROC(cpu_cache_off)
+
+/*
+ * cpu_reset(loc)
+ *
+ * Perform a soft reset of the system. Put the CPU into the same state
+ * as it would be if it had been reset, and branch to what would be the
+ * reset vector. It must be executed with the flat identity mapping.
+ *
+ * - loc - location to jump to for soft reset
+ */
+ .align 5
+ENTRY(cpu_reset)
+ mrs x1, sctlr_el1
+ bic x1, x1, #1
+ msr sctlr_el1, x1 // disable the MMU
+ isb
+ ret x0
+ENDPROC(cpu_reset)
+
+/*
+ * cpu_do_idle()
+ *
+ * Idle the processor (wait for interrupt).
+ */
+ENTRY(cpu_do_idle)
+ dsb sy // WFI may enter a low-power mode
+ wfi
+ ret
+ENDPROC(cpu_do_idle)
+
+/*
+ * cpu_switch_mm(pgd_phys, tsk)
+ *
+ * Set the translation table base pointer to be pgd_phys.
+ *
+ * - pgd_phys - physical address of new TTB
+ */
+ENTRY(cpu_do_switch_mm)
+ mmid w1, x1 // get mm->context.id
+ bfi x0, x1, #48, #16 // set the ASID
+ msr ttbr0_el1, x0 // set TTBR0
+ isb
+ ret
+ENDPROC(cpu_do_switch_mm)
+
+cpu_name:
+ .ascii "AArch64 Processor"
+ .align
+
+ .section ".text.init", #alloc, #execinstr
+
+/*
+ * __cpu_setup
+ *
+ * Initialise the processor for turning the MMU on. Return in x0 the
+ * value of the SCTLR_EL1 register.
+ */
+ENTRY(__cpu_setup)
+ /*
+ * Preserve the link register across the function call.
+ */
+ mov x28, lr
+ bl __flush_dcache_all
+ mov lr, x28
+ ic iallu // I+BTB cache invalidate
+ dsb sy
+
+ mov x0, #3 << 20
+ msr cpacr_el1, x0 // Enable FP/ASIMD
+ mov x0, #1
+ msr oslar_el1, x0 // Set the debug OS lock
+ tlbi vmalle1is // invalidate I + D TLBs
+ /*
+ * Memory region attributes for LPAE:
+ *
+ * n = AttrIndx[2:0]
+ * n MAIR
+ * DEVICE_nGnRnE 000 00000000
+ * DEVICE_nGnRE 001 00000100
+ * DEVICE_GRE 010 00001100
+ * NORMAL_NC 011 01000100
+ * NORMAL 100 11111111
+ */
+ ldr x5, =MAIR(0x00, MT_DEVICE_nGnRnE) | \
+ MAIR(0x04, MT_DEVICE_nGnRE) | \
+ MAIR(0x0c, MT_DEVICE_GRE) | \
+ MAIR(0x44, MT_NORMAL_NC) | \
+ MAIR(0xff, MT_NORMAL)
+ msr mair_el1, x5
+ /*
+ * Prepare SCTLR
+ */
+ adr x5, crval
+ ldp w5, w6, [x5]
+ mrs x0, sctlr_el1
+ bic x0, x0, x5 // clear bits
+ orr x0, x0, x6 // set bits
+ /*
+ * Set/prepare TCR and TTBR. We use 512GB (39-bit) address range for
+ * both user and kernel.
+ */
+ ldr x10, =TCR_TxSZ(VA_BITS) | TCR_FLAGS | TCR_IPS_40BIT | \
+ TCR_ASID16 | (1 << 31)
+#ifdef CONFIG_ARM64_64K_PAGES
+ orr x10, x10, TCR_TG0_64K
+ orr x10, x10, TCR_TG1_64K
+#endif
+ msr tcr_el1, x10
+ ret // return to head.S
+ENDPROC(__cpu_setup)
+
+ /*
+ * n n T
+ * U E WT T UD US IHBS
+ * CE0 XWHW CZ ME TEEA S
+ * .... .IEE .... NEAI TE.I ..AD DEN0 ACAM
+ * 0011 0... 1101 ..0. ..0. 10.. .... .... < hardware reserved
+ * .... .100 .... 01.1 11.1 ..01 0001 1101 < software settings
+ */
+ .type crval, #object
+crval:
+ .word 0x030802e2 // clear
+ .word 0x0405d11d // set
diff --git a/arch/arm64/mm/tlb.S b/arch/arm64/mm/tlb.S
new file mode 100644
index 000000000000..8ae80a18e8ec
--- /dev/null
+++ b/arch/arm64/mm/tlb.S
@@ -0,0 +1,71 @@
+/*
+ * Based on arch/arm/mm/tlb.S
+ *
+ * Copyright (C) 1997-2002 Russell King
+ * Copyright (C) 2012 ARM Ltd.
+ * Written by Catalin Marinas <catalin.marinas@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/asm-offsets.h>
+#include <asm/page.h>
+#include <asm/tlbflush.h>
+#include "proc-macros.S"
+
+/*
+ * __cpu_flush_user_tlb_range(start, end, vma)
+ *
+ * Invalidate a range of TLB entries in the specified address space.
+ *
+ * - start - start address (may not be aligned)
+ * - end - end address (exclusive, may not be aligned)
+ * - vma - vma_struct describing address range
+ */
+ENTRY(__cpu_flush_user_tlb_range)
+ vma_vm_mm x3, x2 // get vma->vm_mm
+ mmid x3, x3 // get vm_mm->context.id
+ dsb sy
+ lsr x0, x0, #12 // align address
+ lsr x1, x1, #12
+ bfi x0, x3, #48, #16 // start VA and ASID
+ bfi x1, x3, #48, #16 // end VA and ASID
+1: tlbi vae1is, x0 // TLB invalidate by address and ASID
+ add x0, x0, #1
+ cmp x0, x1
+ b.lo 1b
+ dsb sy
+ ret
+ENDPROC(__cpu_flush_user_tlb_range)
+
+/*
+ * __cpu_flush_kern_tlb_range(start,end)
+ *
+ * Invalidate a range of kernel TLB entries.
+ *
+ * - start - start address (may not be aligned)
+ * - end - end address (exclusive, may not be aligned)
+ */
+ENTRY(__cpu_flush_kern_tlb_range)
+ dsb sy
+ lsr x0, x0, #12 // align address
+ lsr x1, x1, #12
+1: tlbi vaae1is, x0 // TLB invalidate by address
+ add x0, x0, #1
+ cmp x0, x1
+ b.lo 1b
+ dsb sy
+ isb
+ ret
+ENDPROC(__cpu_flush_kern_tlb_range)
diff --git a/arch/c6x/include/asm/Kbuild b/arch/c6x/include/asm/Kbuild
index 3af601e31e66..f08e89183cda 100644
--- a/arch/c6x/include/asm/Kbuild
+++ b/arch/c6x/include/asm/Kbuild
@@ -2,6 +2,7 @@ include include/asm-generic/Kbuild.asm
generic-y += atomic.h
generic-y += auxvec.h
+generic-y += barrier.h
generic-y += bitsperlong.h
generic-y += bugs.h
generic-y += cputime.h
diff --git a/arch/c6x/include/asm/barrier.h b/arch/c6x/include/asm/barrier.h
deleted file mode 100644
index 538240e85909..000000000000
--- a/arch/c6x/include/asm/barrier.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Port on Texas Instruments TMS320C6x architecture
- *
- * Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated
- * Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com)
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
-#ifndef _ASM_C6X_BARRIER_H
-#define _ASM_C6X_BARRIER_H
-
-#define nop() asm("NOP\n");
-
-#define mb() barrier()
-#define rmb() barrier()
-#define wmb() barrier()
-#define set_mb(var, value) do { var = value; mb(); } while (0)
-#define set_wmb(var, value) do { var = value; wmb(); } while (0)
-
-#define smp_mb() barrier()
-#define smp_rmb() barrier()
-#define smp_wmb() barrier()
-#define smp_read_barrier_depends() do { } while (0)
-
-#endif /* _ASM_C6X_BARRIER_H */
diff --git a/arch/cris/kernel/process.c b/arch/cris/kernel/process.c
index 66fd01728790..7f65be6f7f17 100644
--- a/arch/cris/kernel/process.c
+++ b/arch/cris/kernel/process.c
@@ -25,6 +25,7 @@
#include <linux/elfcore.h>
#include <linux/mqueue.h>
#include <linux/reboot.h>
+#include <linux/rcupdate.h>
//#define DEBUG
@@ -74,6 +75,7 @@ void cpu_idle (void)
{
/* endless idle loop with no priority at all */
while (1) {
+ rcu_idle_enter();
while (!need_resched()) {
void (*idle)(void);
/*
@@ -86,6 +88,7 @@ void cpu_idle (void)
idle = default_idle;
idle();
}
+ rcu_idle_exit();
schedule_preempt_disabled();
}
}
diff --git a/arch/frv/kernel/process.c b/arch/frv/kernel/process.c
index ff95f50efea5..2eb7fa5bf9d8 100644
--- a/arch/frv/kernel/process.c
+++ b/arch/frv/kernel/process.c
@@ -25,6 +25,7 @@
#include <linux/reboot.h>
#include <linux/interrupt.h>
#include <linux/pagemap.h>
+#include <linux/rcupdate.h>
#include <asm/asm-offsets.h>
#include <asm/uaccess.h>
@@ -69,12 +70,14 @@ void cpu_idle(void)
{
/* endless idle loop with no priority at all */
while (1) {
+ rcu_idle_enter();
while (!need_resched()) {
check_pgt_cache();
if (!frv_dma_inprogress && idle)
idle();
}
+ rcu_idle_exit();
schedule_preempt_disabled();
}
diff --git a/arch/frv/mb93090-mb00/pci-vdk.c b/arch/frv/mb93090-mb00/pci-vdk.c
index d04ed14bbf0c..71e9bcf58105 100644
--- a/arch/frv/mb93090-mb00/pci-vdk.c
+++ b/arch/frv/mb93090-mb00/pci-vdk.c
@@ -330,10 +330,8 @@ void __init pcibios_fixup_bus(struct pci_bus *bus)
pci_read_bridge_bases(bus);
if (bus->number == 0) {
- struct list_head *ln;
struct pci_dev *dev;
- for (ln=bus->devices.next; ln != &bus->devices; ln=ln->next) {
- dev = pci_dev_b(ln);
+ list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->devfn == 0) {
dev->resource[0].start = 0;
dev->resource[0].end = 0;
diff --git a/arch/h8300/kernel/process.c b/arch/h8300/kernel/process.c
index 0e9c315be104..f153ed1a4c08 100644
--- a/arch/h8300/kernel/process.c
+++ b/arch/h8300/kernel/process.c
@@ -36,6 +36,7 @@
#include <linux/reboot.h>
#include <linux/fs.h>
#include <linux/slab.h>
+#include <linux/rcupdate.h>
#include <asm/uaccess.h>
#include <asm/traps.h>
@@ -78,8 +79,10 @@ void (*idle)(void) = default_idle;
void cpu_idle(void)
{
while (1) {
+ rcu_idle_enter();
while (!need_resched())
idle();
+ rcu_idle_exit();
schedule_preempt_disabled();
}
}
diff --git a/arch/ia64/Kconfig b/arch/ia64/Kconfig
index 310cf5781fad..3c720ef6c32d 100644
--- a/arch/ia64/Kconfig
+++ b/arch/ia64/Kconfig
@@ -25,6 +25,7 @@ config IA64
select HAVE_GENERIC_HARDIRQS
select HAVE_MEMBLOCK
select HAVE_MEMBLOCK_NODE_MAP
+ select HAVE_VIRT_CPU_ACCOUNTING
select ARCH_DISCARD_MEMBLOCK
select GENERIC_IRQ_PROBE
select GENERIC_PENDING_IRQ if SMP
@@ -340,17 +341,6 @@ config FORCE_MAX_ZONEORDER
default "17" if HUGETLB_PAGE
default "11"
-config VIRT_CPU_ACCOUNTING
- bool "Deterministic task and CPU time accounting"
- default n
- help
- Select this option to enable more accurate task and CPU time
- accounting. This is done by reading a CPU counter on each
- kernel entry and exit and on transitions within the kernel
- between system, softirq and hardirq state, so there is a
- small performance impact.
- If in doubt, say N here.
-
config SMP
bool "Symmetric multi-processing support"
select USE_GENERIC_SMP_HELPERS
diff --git a/arch/ia64/include/asm/numa.h b/arch/ia64/include/asm/numa.h
index 6a8a27cfae3e..2e27ef175652 100644
--- a/arch/ia64/include/asm/numa.h
+++ b/arch/ia64/include/asm/numa.h
@@ -59,7 +59,7 @@ extern struct node_cpuid_s node_cpuid[NR_CPUS];
*/
extern u8 numa_slit[MAX_NUMNODES * MAX_NUMNODES];
-#define node_distance(from,to) (numa_slit[(from) * num_online_nodes() + (to)])
+#define node_distance(from,to) (numa_slit[(from) * MAX_NUMNODES + (to)])
extern int paddr_to_nid(unsigned long paddr);
diff --git a/arch/ia64/include/asm/switch_to.h b/arch/ia64/include/asm/switch_to.h
index cb2412fcd17f..d38c7ea5eea5 100644
--- a/arch/ia64/include/asm/switch_to.h
+++ b/arch/ia64/include/asm/switch_to.h
@@ -30,13 +30,6 @@ extern struct task_struct *ia64_switch_to (void *next_task);
extern void ia64_save_extra (struct task_struct *task);
extern void ia64_load_extra (struct task_struct *task);
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
-extern void ia64_account_on_switch (struct task_struct *prev, struct task_struct *next);
-# define IA64_ACCOUNT_ON_SWITCH(p,n) ia64_account_on_switch(p,n)
-#else
-# define IA64_ACCOUNT_ON_SWITCH(p,n)
-#endif
-
#ifdef CONFIG_PERFMON
DECLARE_PER_CPU(unsigned long, pfm_syst_info);
# define PERFMON_IS_SYSWIDE() (__get_cpu_var(pfm_syst_info) & 0x1)
@@ -49,7 +42,6 @@ extern void ia64_account_on_switch (struct task_struct *prev, struct task_struct
|| PERFMON_IS_SYSWIDE())
#define __switch_to(prev,next,last) do { \
- IA64_ACCOUNT_ON_SWITCH(prev, next); \
if (IA64_HAS_EXTRA_STATE(prev)) \
ia64_save_extra(prev); \
if (IA64_HAS_EXTRA_STATE(next)) \
diff --git a/arch/ia64/kernel/machine_kexec.c b/arch/ia64/kernel/machine_kexec.c
index 070e8effa175..5151a649c96b 100644
--- a/arch/ia64/kernel/machine_kexec.c
+++ b/arch/ia64/kernel/machine_kexec.c
@@ -85,12 +85,13 @@ static void ia64_machine_kexec(struct unw_frame_info *info, void *arg)
struct kimage *image = arg;
relocate_new_kernel_t rnk;
void *pal_addr = efi_get_pal_addr();
- unsigned long code_addr = (unsigned long)page_address(image->control_code_page);
+ unsigned long code_addr;
int ii;
u64 fp, gp;
ia64_fptr_t *init_handler = (ia64_fptr_t *)ia64_os_init_on_kdump;
BUG_ON(!image);
+ code_addr = (unsigned long)page_address(image->control_code_page);
if (image->type == KEXEC_TYPE_CRASH) {
crash_save_this_cpu();
current->thread.ksp = (__u64)info->sw - 16;
diff --git a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c
index dd6fc1449741..ee31fe9b310e 100644
--- a/arch/ia64/kernel/process.c
+++ b/arch/ia64/kernel/process.c
@@ -29,6 +29,7 @@
#include <linux/kdebug.h>
#include <linux/utsname.h>
#include <linux/tracehook.h>
+#include <linux/rcupdate.h>
#include <asm/cpu.h>
#include <asm/delay.h>
@@ -196,8 +197,8 @@ do_notify_resume_user(sigset_t *unused, struct sigscratch *scr, long in_syscall)
ia64_do_signal(scr, in_syscall);
}
- if (test_thread_flag(TIF_NOTIFY_RESUME)) {
- clear_thread_flag(TIF_NOTIFY_RESUME);
+ if (test_and_clear_thread_flag(TIF_NOTIFY_RESUME)) {
+ local_irq_enable(); /* force interrupt enable */
tracehook_notify_resume(&scr->pt);
}
@@ -279,6 +280,7 @@ cpu_idle (void)
/* endless idle loop with no priority at all */
while (1) {
+ rcu_idle_enter();
if (can_do_pal_halt) {
current_thread_info()->status &= ~TS_POLLING;
/*
@@ -309,6 +311,7 @@ cpu_idle (void)
normal_xtp();
#endif
}
+ rcu_idle_exit();
schedule_preempt_disabled();
check_pgt_cache();
if (cpu_is_offline(cpu))
diff --git a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c
index ecc904b33c5f..80ff9acc5edf 100644
--- a/arch/ia64/kernel/time.c
+++ b/arch/ia64/kernel/time.c
@@ -83,32 +83,36 @@ static struct clocksource *itc_clocksource;
extern cputime_t cycle_to_cputime(u64 cyc);
+static void vtime_account_user(struct task_struct *tsk)
+{
+ cputime_t delta_utime;
+ struct thread_info *ti = task_thread_info(tsk);
+
+ if (ti->ac_utime) {
+ delta_utime = cycle_to_cputime(ti->ac_utime);
+ account_user_time(tsk, delta_utime, delta_utime);
+ ti->ac_utime = 0;
+ }
+}
+
/*
* Called from the context switch with interrupts disabled, to charge all
* accumulated times to the current process, and to prepare accounting on
* the next process.
*/
-void ia64_account_on_switch(struct task_struct *prev, struct task_struct *next)
+void vtime_task_switch(struct task_struct *prev)
{
struct thread_info *pi = task_thread_info(prev);
- struct thread_info *ni = task_thread_info(next);
- cputime_t delta_stime, delta_utime;
- __u64 now;
+ struct thread_info *ni = task_thread_info(current);
- now = ia64_get_itc();
-
- delta_stime = cycle_to_cputime(pi->ac_stime + (now - pi->ac_stamp));
if (idle_task(smp_processor_id()) != prev)
- account_system_time(prev, 0, delta_stime, delta_stime);
+ vtime_account_system(prev);
else
- account_idle_time(delta_stime);
+ vtime_account_idle(prev);
- if (pi->ac_utime) {
- delta_utime = cycle_to_cputime(pi->ac_utime);
- account_user_time(prev, delta_utime, delta_utime);
- }
+ vtime_account_user(prev);
- pi->ac_stamp = ni->ac_stamp = now;
+ pi->ac_stamp = ni->ac_stamp;
ni->ac_stime = ni->ac_utime = 0;
}
@@ -116,29 +120,32 @@ void ia64_account_on_switch(struct task_struct *prev, struct task_struct *next)
* Account time for a transition between system, hard irq or soft irq state.
* Note that this function is called with interrupts enabled.
*/
-void account_system_vtime(struct task_struct *tsk)
+static cputime_t vtime_delta(struct task_struct *tsk)
{
struct thread_info *ti = task_thread_info(tsk);
- unsigned long flags;
cputime_t delta_stime;
__u64 now;
- local_irq_save(flags);
-
now = ia64_get_itc();
delta_stime = cycle_to_cputime(ti->ac_stime + (now - ti->ac_stamp));
- if (irq_count() || idle_task(smp_processor_id()) != tsk)
- account_system_time(tsk, 0, delta_stime, delta_stime);
- else
- account_idle_time(delta_stime);
ti->ac_stime = 0;
-
ti->ac_stamp = now;
- local_irq_restore(flags);
+ return delta_stime;
+}
+
+void vtime_account_system(struct task_struct *tsk)
+{
+ cputime_t delta = vtime_delta(tsk);
+
+ account_system_time(tsk, 0, delta, delta);
+}
+
+void vtime_account_idle(struct task_struct *tsk)
+{
+ account_idle_time(vtime_delta(tsk));
}
-EXPORT_SYMBOL_GPL(account_system_vtime);
/*
* Called from the timer interrupt handler to charge accumulated user time
@@ -146,14 +153,7 @@ EXPORT_SYMBOL_GPL(account_system_vtime);
*/
void account_process_tick(struct task_struct *p, int user_tick)
{
- struct thread_info *ti = task_thread_info(p);
- cputime_t delta_utime;
-
- if (ti->ac_utime) {
- delta_utime = cycle_to_cputime(ti->ac_utime);
- account_user_time(p, delta_utime, delta_utime);
- ti->ac_utime = 0;
- }
+ vtime_account_user(p);
}
#endif /* CONFIG_VIRT_CPU_ACCOUNTING */
diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c
index 81acc7a57f3e..5faa66c5c2a8 100644
--- a/arch/ia64/pci/pci.c
+++ b/arch/ia64/pci/pci.c
@@ -295,7 +295,6 @@ static __devinit acpi_status add_window(struct acpi_resource *res, void *data)
window->resource.flags = flags;
window->resource.start = addr.minimum + offset;
window->resource.end = window->resource.start + addr.address_length - 1;
- window->resource.child = NULL;
window->offset = offset;
if (insert_resource(root, &window->resource)) {
@@ -357,7 +356,7 @@ pci_acpi_scan_root(struct acpi_pci_root *root)
&windows);
if (windows) {
controller->window =
- kmalloc_node(sizeof(*controller->window) * windows,
+ kzalloc_node(sizeof(*controller->window) * windows,
GFP_KERNEL, controller->node);
if (!controller->window)
goto out2;
@@ -461,14 +460,6 @@ void pcibios_set_master (struct pci_dev *dev)
/* No special bus mastering setup handling */
}
-void __devinit
-pcibios_update_irq (struct pci_dev *dev, int irq)
-{
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-
- /* ??? FIXME -- record old value for shutdown. */
-}
-
int
pcibios_enable_device (struct pci_dev *dev, int mask)
{
diff --git a/arch/ia64/sn/kernel/io_common.c b/arch/ia64/sn/kernel/io_common.c
index fbb5f2f87eed..8630875e74b5 100644
--- a/arch/ia64/sn/kernel/io_common.c
+++ b/arch/ia64/sn/kernel/io_common.c
@@ -229,7 +229,6 @@ void sn_pci_fixup_slot(struct pci_dev *dev, struct pcidev_info *pcidev_info,
{
int segment = pci_domain_nr(dev->bus);
struct pcibus_bussoft *bs;
- struct pci_bus *host_pci_bus;
struct pci_dev *host_pci_dev;
unsigned int bus_no, devfn;
@@ -245,8 +244,7 @@ void sn_pci_fixup_slot(struct pci_dev *dev, struct pcidev_info *pcidev_info,
bus_no = (pcidev_info->pdi_slot_host_handle >> 32) & 0xff;
devfn = pcidev_info->pdi_slot_host_handle & 0xffffffff;
- host_pci_bus = pci_find_bus(segment, bus_no);
- host_pci_dev = pci_get_slot(host_pci_bus, devfn);
+ host_pci_dev = pci_get_domain_bus_and_slot(segment, bus_no, devfn);
pcidev_info->host_pci_dev = host_pci_dev;
pcidev_info->pdi_linux_pcidev = dev;
diff --git a/arch/ia64/xen/xencomm.c b/arch/ia64/xen/xencomm.c
index 1f5d7ac82e97..73d903ca2d64 100644
--- a/arch/ia64/xen/xencomm.c
+++ b/arch/ia64/xen/xencomm.c
@@ -17,6 +17,7 @@
*/
#include <linux/mm.h>
+#include <linux/err.h>
static unsigned long kernel_virtual_offset;
static int is_xencomm_initialized;
@@ -98,7 +99,7 @@ xencomm_vtop(unsigned long vaddr)
/* We assume the page is modified. */
page = follow_page(vma, vaddr, FOLL_WRITE | FOLL_TOUCH);
- if (!page)
+ if (IS_ERR_OR_NULL(page))
return ~0UL;
return (page_to_pfn(page) << PAGE_SHIFT) | (vaddr & ~PAGE_MASK);
diff --git a/arch/m32r/kernel/process.c b/arch/m32r/kernel/process.c
index 3a4a32b27208..384e63f3a4c4 100644
--- a/arch/m32r/kernel/process.c
+++ b/arch/m32r/kernel/process.c
@@ -26,6 +26,7 @@
#include <linux/ptrace.h>
#include <linux/unistd.h>
#include <linux/hardirq.h>
+#include <linux/rcupdate.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -82,6 +83,7 @@ void cpu_idle (void)
{
/* endless idle loop with no priority at all */
while (1) {
+ rcu_idle_enter();
while (!need_resched()) {
void (*idle)(void) = pm_idle;
@@ -90,6 +92,7 @@ void cpu_idle (void)
idle();
}
+ rcu_idle_exit();
schedule_preempt_disabled();
}
}
diff --git a/arch/m68k/amiga/platform.c b/arch/m68k/amiga/platform.c
index 80076d368b7e..6083088c0cca 100644
--- a/arch/m68k/amiga/platform.c
+++ b/arch/m68k/amiga/platform.c
@@ -56,10 +56,7 @@ static int __init amiga_init_bus(void)
n = AMIGAHW_PRESENT(ZORRO3) ? 4 : 2;
pdev = platform_device_register_simple("amiga-zorro", -1,
zorro_resources, n);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
-
- return 0;
+ return PTR_RET(pdev);
}
subsys_initcall(amiga_init_bus);
diff --git a/arch/m68k/include/asm/apollohw.h b/arch/m68k/include/asm/apollohw.h
index 635ef4f89010..6c19e0c22411 100644
--- a/arch/m68k/include/asm/apollohw.h
+++ b/arch/m68k/include/asm/apollohw.h
@@ -46,18 +46,6 @@ struct SCN2681 {
};
-#if 0
-struct mc146818 {
-
- unsigned int second1:4, second2:4, alarm_second1:4, alarm_second2:4,
- minute1:4, minute2:4, alarm_minute1:4, alarm_minute2:4;
- unsigned int hours1:4, hours2:4, alarm_hours1:4, alarm_hours2:4,
- day_of_week1:4, day_of_week2:4, day_of_month1:4, day_of_month2:4;
- unsigned int month1:4, month2:4, year1:4, year2:4, :16;
-
-};
-#endif
-
struct mc146818 {
unsigned char second, alarm_second;
unsigned char minute, alarm_minute;
diff --git a/arch/m68k/kernel/pcibios.c b/arch/m68k/kernel/pcibios.c
index b2988aa1840b..73fa0b56a06c 100644
--- a/arch/m68k/kernel/pcibios.c
+++ b/arch/m68k/kernel/pcibios.c
@@ -87,11 +87,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
return 0;
}
-void pcibios_update_irq(struct pci_dev *dev, int irq)
-{
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-}
-
void __devinit pcibios_fixup_bus(struct pci_bus *bus)
{
struct pci_dev *dev;
diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c
index c488e3cfab53..ac2892e49c7c 100644
--- a/arch/m68k/kernel/process.c
+++ b/arch/m68k/kernel/process.c
@@ -25,6 +25,7 @@
#include <linux/reboot.h>
#include <linux/init_task.h>
#include <linux/mqueue.h>
+#include <linux/rcupdate.h>
#include <asm/uaccess.h>
#include <asm/traps.h>
@@ -75,8 +76,10 @@ void cpu_idle(void)
{
/* endless idle loop with no priority at all */
while (1) {
+ rcu_idle_enter();
while (!need_resched())
idle();
+ rcu_idle_exit();
schedule_preempt_disabled();
}
}
diff --git a/arch/m68k/kernel/time.c b/arch/m68k/kernel/time.c
index 707f0573ec6b..5d0bcaad2e55 100644
--- a/arch/m68k/kernel/time.c
+++ b/arch/m68k/kernel/time.c
@@ -100,10 +100,7 @@ static int __init rtc_init(void)
return -ENODEV;
pdev = platform_device_register_simple("rtc-generic", -1, NULL, 0);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
-
- return 0;
+ return PTR_RET(pdev);
}
module_init(rtc_init);
diff --git a/arch/m68k/platform/coldfire/clk.c b/arch/m68k/platform/coldfire/clk.c
index 75f9ee967ea7..9cd13b4ce42b 100644
--- a/arch/m68k/platform/coldfire/clk.c
+++ b/arch/m68k/platform/coldfire/clk.c
@@ -146,9 +146,3 @@ struct clk_ops clk_ops1 = {
};
#endif /* MCFPM_PPMCR1 */
#endif /* MCFPM_PPMCR0 */
-
-struct clk *devm_clk_get(struct device *dev, const char *id)
-{
- return NULL;
-}
-EXPORT_SYMBOL(devm_clk_get);
diff --git a/arch/m68k/q40/config.c b/arch/m68k/q40/config.c
index 8a1ce327c963..1adb5b7b0d1a 100644
--- a/arch/m68k/q40/config.c
+++ b/arch/m68k/q40/config.c
@@ -338,9 +338,6 @@ static __init int q40_add_kbd_device(void)
return -ENODEV;
pdev = platform_device_register_simple("q40kbd", -1, NULL, 0);
- if (IS_ERR(pdev))
- return PTR_ERR(pdev);
-
- return 0;
+ return PTR_RET(pdev);
}
arch_initcall(q40_add_kbd_device);
diff --git a/arch/mips/kernel/smp-cmp.c b/arch/mips/kernel/smp-cmp.c
index e7e03ecf5495..afc379ca3753 100644
--- a/arch/mips/kernel/smp-cmp.c
+++ b/arch/mips/kernel/smp-cmp.c
@@ -102,7 +102,7 @@ static void cmp_init_secondary(void)
c->vpe_id = (read_c0_tcbind() >> TCBIND_CURVPE_SHIFT) & TCBIND_CURVPE;
#endif
#ifdef CONFIG_MIPS_MT_SMTC
- c->tc_id = (read_c0_tcbind() >> TCBIND_CURTC_SHIFT) & TCBIND_CURTC;
+ c->tc_id = (read_c0_tcbind() & TCBIND_CURTC) >> TCBIND_CURTC_SHIFT;
#endif
}
diff --git a/arch/mips/mm/gup.c b/arch/mips/mm/gup.c
index 33aadbcf170b..dcfd573871c1 100644
--- a/arch/mips/mm/gup.c
+++ b/arch/mips/mm/gup.c
@@ -152,6 +152,8 @@ static int gup_huge_pud(pud_t pud, unsigned long addr, unsigned long end,
do {
VM_BUG_ON(compound_head(page) != head);
pages[*nr] = page;
+ if (PageTail(page))
+ get_huge_page_tail(page);
(*nr)++;
page++;
refs++;
diff --git a/arch/mips/mti-malta/malta-int.c b/arch/mips/mti-malta/malta-int.c
index 7b13a4caeea4..fea823f18479 100644
--- a/arch/mips/mti-malta/malta-int.c
+++ b/arch/mips/mti-malta/malta-int.c
@@ -273,16 +273,19 @@ asmlinkage void plat_irq_dispatch(void)
unsigned int pending = read_c0_cause() & read_c0_status() & ST0_IM;
int irq;
+ if (unlikely(!pending)) {
+ spurious_interrupt();
+ return;
+ }
+
irq = irq_ffs(pending);
if (irq == MIPSCPU_INT_I8259A)
malta_hw0_irqdispatch();
else if (gic_present && ((1 << irq) & ipi_map[smp_processor_id()]))
malta_ipi_irqdispatch();
- else if (irq >= 0)
- do_IRQ(MIPS_CPU_IRQ_BASE + irq);
else
- spurious_interrupt();
+ do_IRQ(MIPS_CPU_IRQ_BASE + irq);
}
#ifdef CONFIG_MIPS_MT_SMP
diff --git a/arch/mips/mti-malta/malta-platform.c b/arch/mips/mti-malta/malta-platform.c
index 4c35301720e7..80562b81f0f2 100644
--- a/arch/mips/mti-malta/malta-platform.c
+++ b/arch/mips/mti-malta/malta-platform.c
@@ -138,11 +138,6 @@ static int __init malta_add_devices(void)
if (err)
return err;
- /*
- * Set RTC to BCD mode to support current alarm code.
- */
- CMOS_WRITE(CMOS_READ(RTC_CONTROL) & ~RTC_DM_BINARY, RTC_CONTROL);
-
return 0;
}
diff --git a/arch/mips/pci/pci-octeon.c b/arch/mips/pci/pci-octeon.c
index 52a1ba70b3b6..c5dfb2c87d44 100644
--- a/arch/mips/pci/pci-octeon.c
+++ b/arch/mips/pci/pci-octeon.c
@@ -117,16 +117,11 @@ int pcibios_plat_dev_init(struct pci_dev *dev)
}
/* Enable the PCIe normal error reporting */
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (pos) {
- /* Update Device Control */
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &config);
- config |= PCI_EXP_DEVCTL_CERE; /* Correctable Error Reporting */
- config |= PCI_EXP_DEVCTL_NFERE; /* Non-Fatal Error Reporting */
- config |= PCI_EXP_DEVCTL_FERE; /* Fatal Error Reporting */
- config |= PCI_EXP_DEVCTL_URRE; /* Unsupported Request */
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, config);
- }
+ config = PCI_EXP_DEVCTL_CERE; /* Correctable Error Reporting */
+ config |= PCI_EXP_DEVCTL_NFERE; /* Non-Fatal Error Reporting */
+ config |= PCI_EXP_DEVCTL_FERE; /* Fatal Error Reporting */
+ config |= PCI_EXP_DEVCTL_URRE; /* Unsupported Request */
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, config);
/* Find the Advanced Error Reporting capability */
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c
index 690356808f8a..04e35bcde07c 100644
--- a/arch/mips/pci/pci.c
+++ b/arch/mips/pci/pci.c
@@ -313,12 +313,6 @@ void __devinit pcibios_fixup_bus(struct pci_bus *bus)
}
}
-void __init
-pcibios_update_irq(struct pci_dev *dev, int irq)
-{
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-}
-
#ifdef CONFIG_HOTPLUG
EXPORT_SYMBOL(PCIBIOS_MIN_IO);
EXPORT_SYMBOL(PCIBIOS_MIN_MEM);
diff --git a/arch/mn10300/kernel/process.c b/arch/mn10300/kernel/process.c
index 7dab0cd36466..e9cceba193b6 100644
--- a/arch/mn10300/kernel/process.c
+++ b/arch/mn10300/kernel/process.c
@@ -25,6 +25,7 @@
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/slab.h>
+#include <linux/rcupdate.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/io.h>
@@ -107,6 +108,7 @@ void cpu_idle(void)
{
/* endless idle loop with no priority at all */
for (;;) {
+ rcu_idle_enter();
while (!need_resched()) {
void (*idle)(void);
@@ -121,6 +123,7 @@ void cpu_idle(void)
}
idle();
}
+ rcu_idle_exit();
schedule_preempt_disabled();
}
diff --git a/arch/parisc/kernel/process.c b/arch/parisc/kernel/process.c
index 2c05a9292a81..8c6b6b6561f0 100644
--- a/arch/parisc/kernel/process.c
+++ b/arch/parisc/kernel/process.c
@@ -48,6 +48,7 @@
#include <linux/unistd.h>
#include <linux/kallsyms.h>
#include <linux/uaccess.h>
+#include <linux/rcupdate.h>
#include <asm/io.h>
#include <asm/asm-offsets.h>
@@ -69,8 +70,10 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
+ rcu_idle_enter();
while (!need_resched())
barrier();
+ rcu_idle_exit();
schedule_preempt_disabled();
check_pgt_cache();
}
diff --git a/arch/powerpc/boot/.gitignore b/arch/powerpc/boot/.gitignore
index 1c1aadc8c48f..c32ae5ce9fff 100644
--- a/arch/powerpc/boot/.gitignore
+++ b/arch/powerpc/boot/.gitignore
@@ -1,10 +1,6 @@
addnote
empty.c
hack-coff
-infblock.c
-infblock.h
-infcodes.c
-infcodes.h
inffast.c
inffast.h
inffixed.h
diff --git a/arch/powerpc/include/asm/machdep.h b/arch/powerpc/include/asm/machdep.h
index 42ce570812c1..f7706d722b39 100644
--- a/arch/powerpc/include/asm/machdep.h
+++ b/arch/powerpc/include/asm/machdep.h
@@ -214,6 +214,9 @@ struct machdep_calls {
/* Called after scan and before resource survey */
void (*pcibios_fixup_phb)(struct pci_controller *hose);
+ /* Called during PCI resource reassignment */
+ resource_size_t (*pcibios_window_alignment)(struct pci_bus *, unsigned long type);
+
/* Called to shutdown machine specific hardware not already controlled
* by other drivers.
*/
diff --git a/arch/powerpc/include/asm/time.h b/arch/powerpc/include/asm/time.h
index 3b4b4a8da922..c1f267694acb 100644
--- a/arch/powerpc/include/asm/time.h
+++ b/arch/powerpc/include/asm/time.h
@@ -197,12 +197,6 @@ struct cpu_usage {
DECLARE_PER_CPU(struct cpu_usage, cpu_usage_array);
-#if defined(CONFIG_VIRT_CPU_ACCOUNTING)
-#define account_process_vtime(tsk) account_process_tick(tsk, 0)
-#else
-#define account_process_vtime(tsk) do { } while (0)
-#endif
-
extern void secondary_cpu_time_init(void);
DECLARE_PER_CPU(u64, decrementers_next_tb);
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c
index 2aa04f29e1de..43fea543d686 100644
--- a/arch/powerpc/kernel/pci-common.c
+++ b/arch/powerpc/kernel/pci-common.c
@@ -99,6 +99,26 @@ void pcibios_free_controller(struct pci_controller *phb)
kfree(phb);
}
+/*
+ * The function is used to return the minimal alignment
+ * for memory or I/O windows of the associated P2P bridge.
+ * By default, 4KiB alignment for I/O windows and 1MiB for
+ * memory windows.
+ */
+resource_size_t pcibios_window_alignment(struct pci_bus *bus,
+ unsigned long type)
+{
+ if (ppc_md.pcibios_window_alignment)
+ return ppc_md.pcibios_window_alignment(bus, type);
+
+ /*
+ * PCI core will figure out the default
+ * alignment: 4KiB for I/O and 1MiB for
+ * memory window.
+ */
+ return 1;
+}
+
static resource_size_t pcibios_io_size(const struct pci_controller *hose)
{
#ifdef CONFIG_PPC64
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index 1a1f2ddfb581..e9cb51f5f801 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -514,9 +514,6 @@ struct task_struct *__switch_to(struct task_struct *prev,
local_irq_save(flags);
- account_system_vtime(current);
- account_process_vtime(current);
-
/*
* We can't take a PMU exception inside _switch() since there is a
* window where the kernel stack SLB and the kernel stack are out
diff --git a/arch/powerpc/kernel/time.c b/arch/powerpc/kernel/time.c
index e49e93191b69..eaa9d0e6abca 100644
--- a/arch/powerpc/kernel/time.c
+++ b/arch/powerpc/kernel/time.c
@@ -291,13 +291,12 @@ static inline u64 calculate_stolen_time(u64 stop_tb)
* Account time for a transition between system, hard irq
* or soft irq state.
*/
-void account_system_vtime(struct task_struct *tsk)
+static u64 vtime_delta(struct task_struct *tsk,
+ u64 *sys_scaled, u64 *stolen)
{
- u64 now, nowscaled, delta, deltascaled;
- unsigned long flags;
- u64 stolen, udelta, sys_scaled, user_scaled;
+ u64 now, nowscaled, deltascaled;
+ u64 udelta, delta, user_scaled;
- local_irq_save(flags);
now = mftb();
nowscaled = read_spurr(now);
get_paca()->system_time += now - get_paca()->starttime;
@@ -305,7 +304,7 @@ void account_system_vtime(struct task_struct *tsk)
deltascaled = nowscaled - get_paca()->startspurr;
get_paca()->startspurr = nowscaled;
- stolen = calculate_stolen_time(now);
+ *stolen = calculate_stolen_time(now);
delta = get_paca()->system_time;
get_paca()->system_time = 0;
@@ -322,35 +321,45 @@ void account_system_vtime(struct task_struct *tsk)
* the user ticks get saved up in paca->user_time_scaled to be
* used by account_process_tick.
*/
- sys_scaled = delta;
+ *sys_scaled = delta;
user_scaled = udelta;
if (deltascaled != delta + udelta) {
if (udelta) {
- sys_scaled = deltascaled * delta / (delta + udelta);
- user_scaled = deltascaled - sys_scaled;
+ *sys_scaled = deltascaled * delta / (delta + udelta);
+ user_scaled = deltascaled - *sys_scaled;
} else {
- sys_scaled = deltascaled;
+ *sys_scaled = deltascaled;
}
}
get_paca()->user_time_scaled += user_scaled;
- if (in_interrupt() || idle_task(smp_processor_id()) != tsk) {
- account_system_time(tsk, 0, delta, sys_scaled);
- if (stolen)
- account_steal_time(stolen);
- } else {
- account_idle_time(delta + stolen);
- }
- local_irq_restore(flags);
+ return delta;
+}
+
+void vtime_account_system(struct task_struct *tsk)
+{
+ u64 delta, sys_scaled, stolen;
+
+ delta = vtime_delta(tsk, &sys_scaled, &stolen);
+ account_system_time(tsk, 0, delta, sys_scaled);
+ if (stolen)
+ account_steal_time(stolen);
+}
+
+void vtime_account_idle(struct task_struct *tsk)
+{
+ u64 delta, sys_scaled, stolen;
+
+ delta = vtime_delta(tsk, &sys_scaled, &stolen);
+ account_idle_time(delta + stolen);
}
-EXPORT_SYMBOL_GPL(account_system_vtime);
/*
* Transfer the user and system times accumulated in the paca
* by the exception entry and exit code to the generic process
* user and system time records.
* Must be called with interrupts disabled.
- * Assumes that account_system_vtime() has been called recently
+ * Assumes that vtime_account() has been called recently
* (i.e. since the last entry from usermode) so that
* get_paca()->user_time_scaled is up to date.
*/
@@ -366,6 +375,12 @@ void account_process_tick(struct task_struct *tsk, int user_tick)
account_user_time(tsk, utime, utimescaled);
}
+void vtime_task_switch(struct task_struct *prev)
+{
+ vtime_account(prev);
+ account_process_tick(prev, 0);
+}
+
#else /* ! CONFIG_VIRT_CPU_ACCOUNTING */
#define calc_cputime_factors()
#endif
diff --git a/arch/powerpc/platforms/83xx/suspend.c b/arch/powerpc/platforms/83xx/suspend.c
index 1a046715e461..1d769a29249f 100644
--- a/arch/powerpc/platforms/83xx/suspend.c
+++ b/arch/powerpc/platforms/83xx/suspend.c
@@ -326,7 +326,7 @@ static int pmc_probe(struct platform_device *ofdev)
const struct of_device_id *match;
struct device_node *np = ofdev->dev.of_node;
struct resource res;
- struct pmc_type *type;
+ const struct pmc_type *type;
int ret = 0;
match = of_match_device(pmc_match, &ofdev->dev);
diff --git a/arch/powerpc/platforms/Kconfig.cputype b/arch/powerpc/platforms/Kconfig.cputype
index 30fd01de6bed..72afd2888cad 100644
--- a/arch/powerpc/platforms/Kconfig.cputype
+++ b/arch/powerpc/platforms/Kconfig.cputype
@@ -1,6 +1,7 @@
config PPC64
bool "64-bit kernel"
default n
+ select HAVE_VIRT_CPU_ACCOUNTING
help
This option selects whether a 32-bit or a 64-bit kernel
will be built.
@@ -337,21 +338,6 @@ config PPC_MM_SLICES
default y if (!PPC_FSL_BOOK3E && PPC64 && HUGETLB_PAGE) || (PPC_STD_MMU_64 && PPC_64K_PAGES)
default n
-config VIRT_CPU_ACCOUNTING
- bool "Deterministic task and CPU time accounting"
- depends on PPC64
- default y
- help
- Select this option to enable more accurate task and CPU time
- accounting. This is done by reading a CPU counter on each
- kernel entry and exit and on transitions within the kernel
- between system, softirq and hardirq state, so there is a
- small performance impact. This also enables accounting of
- stolen time on logically-partitioned systems running on
- IBM POWER5-based machines.
-
- If in doubt, say Y here.
-
config PPC_HAVE_PMU_SUPPORT
bool
diff --git a/arch/powerpc/platforms/cell/celleb_pci.c b/arch/powerpc/platforms/cell/celleb_pci.c
index 5822141aa63f..abc8af43ea7c 100644
--- a/arch/powerpc/platforms/cell/celleb_pci.c
+++ b/arch/powerpc/platforms/cell/celleb_pci.c
@@ -472,7 +472,7 @@ int __init celleb_setup_phb(struct pci_controller *phb)
{
struct device_node *dev = phb->dn;
const struct of_device_id *match;
- struct celleb_phb_spec *phb_spec;
+ const struct celleb_phb_spec *phb_spec;
int rc;
match = of_match_node(celleb_phb_match, dev);
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c
index 9cda6a1ad0cf..0e7eccc0f88d 100644
--- a/arch/powerpc/platforms/powernv/pci-ioda.c
+++ b/arch/powerpc/platforms/powernv/pci-ioda.c
@@ -855,7 +855,7 @@ static void __devinit pnv_ioda_setup_PEs(struct pci_bus *bus)
if (pe == NULL)
continue;
/* Leaving the PCIe domain ... single PE# */
- if (dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE)
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_PCI_BRIDGE)
pnv_ioda_setup_bus_PE(dev, pe);
else if (dev->subordinate)
pnv_ioda_setup_PEs(dev->subordinate);
@@ -1139,6 +1139,44 @@ static void __devinit pnv_pci_ioda_fixup_phb(struct pci_controller *hose)
}
}
+/*
+ * Returns the alignment for I/O or memory windows for P2P
+ * bridges. That actually depends on how PEs are segmented.
+ * For now, we return I/O or M32 segment size for PE sensitive
+ * P2P bridges. Otherwise, the default values (4KiB for I/O,
+ * 1MiB for memory) will be returned.
+ *
+ * The current PCI bus might be put into one PE, which was
+ * create against the parent PCI bridge. For that case, we
+ * needn't enlarge the alignment so that we can save some
+ * resources.
+ */
+static resource_size_t pnv_pci_window_alignment(struct pci_bus *bus,
+ unsigned long type)
+{
+ struct pci_dev *bridge;
+ struct pci_controller *hose = pci_bus_to_host(bus);
+ struct pnv_phb *phb = hose->private_data;
+ int num_pci_bridges = 0;
+
+ bridge = bus->self;
+ while (bridge) {
+ if (pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE) {
+ num_pci_bridges++;
+ if (num_pci_bridges >= 2)
+ return 1;
+ }
+
+ bridge = bridge->bus->self;
+ }
+
+ /* We need support prefetchable memory window later */
+ if (type & IORESOURCE_MEM)
+ return phb->ioda.m32_segsize;
+
+ return phb->ioda.io_segsize;
+}
+
/* Prevent enabling devices for which we couldn't properly
* assign a PE
*/
@@ -1306,6 +1344,7 @@ void __init pnv_pci_init_ioda1_phb(struct device_node *np)
*/
ppc_md.pcibios_fixup_phb = pnv_pci_ioda_fixup_phb;
ppc_md.pcibios_enable_device_hook = pnv_pci_enable_device_hook;
+ ppc_md.pcibios_window_alignment = pnv_pci_window_alignment;
pci_add_flags(PCI_PROBE_ONLY | PCI_REASSIGN_ALL_RSRC);
/* Reset IODA tables to a clean state */
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c
index 6e097de00e09..51ffafae561e 100644
--- a/arch/powerpc/sysdev/fsl_msi.c
+++ b/arch/powerpc/sysdev/fsl_msi.c
@@ -368,7 +368,7 @@ static int __devinit fsl_of_msi_probe(struct platform_device *dev)
int err, i, j, irq_index, count;
int rc;
const u32 *p;
- struct fsl_msi_feature *features;
+ const struct fsl_msi_feature *features;
int len;
u32 offset;
static const u32 all_avail[] = { 0, NR_MSI_IRQS };
@@ -502,15 +502,15 @@ static const struct fsl_msi_feature vmpic_msi_feature = {
static const struct of_device_id fsl_of_msi_ids[] = {
{
.compatible = "fsl,mpic-msi",
- .data = (void *)&mpic_msi_feature,
+ .data = &mpic_msi_feature,
},
{
.compatible = "fsl,ipic-msi",
- .data = (void *)&ipic_msi_feature,
+ .data = &ipic_msi_feature,
},
{
.compatible = "fsl,vmpic-msi",
- .data = (void *)&vmpic_msi_feature,
+ .data = &vmpic_msi_feature,
},
{}
};
diff --git a/arch/s390/Kbuild b/arch/s390/Kbuild
index 9858476fa0fe..cc45d25487b0 100644
--- a/arch/s390/Kbuild
+++ b/arch/s390/Kbuild
@@ -5,3 +5,4 @@ obj-$(CONFIG_CRYPTO_HW) += crypto/
obj-$(CONFIG_S390_HYPFS_FS) += hypfs/
obj-$(CONFIG_APPLDATA_BASE) += appldata/
obj-$(CONFIG_MATHEMU) += math-emu/
+obj-y += net/
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 107610e01a29..f9acddd9ace3 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -49,10 +49,13 @@ config GENERIC_LOCKBREAK
config PGSTE
def_bool y if KVM
-config VIRT_CPU_ACCOUNTING
+config ARCH_SUPPORTS_DEBUG_PAGEALLOC
def_bool y
-config ARCH_SUPPORTS_DEBUG_PAGEALLOC
+config KEXEC
+ def_bool y
+
+config AUDIT_ARCH
def_bool y
config S390
@@ -84,11 +87,15 @@ config S390
select HAVE_KERNEL_XZ
select HAVE_ARCH_MUTEX_CPU_RELAX
select HAVE_ARCH_JUMP_LABEL if !MARCH_G5
+ select HAVE_BPF_JIT if 64BIT && PACK_STACK
select ARCH_SAVE_PAGE_KEYS if HIBERNATION
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
select HAVE_MEMBLOCK
select HAVE_MEMBLOCK_NODE_MAP
select HAVE_CMPXCHG_LOCAL
+ select HAVE_CMPXCHG_DOUBLE
+ select HAVE_VIRT_CPU_ACCOUNTING
+ select VIRT_CPU_ACCOUNTING
select ARCH_DISCARD_MEMBLOCK
select BUILDTIME_EXTABLE_SORT
select ARCH_INLINE_SPIN_TRYLOCK
@@ -133,9 +140,79 @@ source "init/Kconfig"
source "kernel/Kconfig.freezer"
-menu "Base setup"
+menu "Processor type and features"
+
+config HAVE_MARCH_Z900_FEATURES
+ def_bool n
+
+config HAVE_MARCH_Z990_FEATURES
+ def_bool n
+ select HAVE_MARCH_Z900_FEATURES
+
+config HAVE_MARCH_Z9_109_FEATURES
+ def_bool n
+ select HAVE_MARCH_Z990_FEATURES
+
+config HAVE_MARCH_Z10_FEATURES
+ def_bool n
+ select HAVE_MARCH_Z9_109_FEATURES
+
+config HAVE_MARCH_Z196_FEATURES
+ def_bool n
+ select HAVE_MARCH_Z10_FEATURES
+
+choice
+ prompt "Processor type"
+ default MARCH_G5
+
+config MARCH_G5
+ bool "System/390 model G5 and G6"
+ depends on !64BIT
+ help
+ Select this to build a 31 bit kernel that works
+ on all ESA/390 and z/Architecture machines.
-comment "Processor type and features"
+config MARCH_Z900
+ bool "IBM zSeries model z800 and z900"
+ select HAVE_MARCH_Z900_FEATURES if 64BIT
+ help
+ Select this to enable optimizations for model z800/z900 (2064 and
+ 2066 series). This will enable some optimizations that are not
+ available on older ESA/390 (31 Bit) only CPUs.
+
+config MARCH_Z990
+ bool "IBM zSeries model z890 and z990"
+ select HAVE_MARCH_Z990_FEATURES if 64BIT
+ help
+ Select this to enable optimizations for model z890/z990 (2084 and
+ 2086 series). The kernel will be slightly faster but will not work
+ on older machines.
+
+config MARCH_Z9_109
+ bool "IBM System z9"
+ select HAVE_MARCH_Z9_109_FEATURES if 64BIT
+ help
+ Select this to enable optimizations for IBM System z9 (2094 and
+ 2096 series). The kernel will be slightly faster but will not work
+ on older machines.
+
+config MARCH_Z10
+ bool "IBM System z10"
+ select HAVE_MARCH_Z10_FEATURES if 64BIT
+ help
+ Select this to enable optimizations for IBM System z10 (2097 and
+ 2098 series). The kernel will be slightly faster but will not work
+ on older machines.
+
+config MARCH_Z196
+ bool "IBM zEnterprise 114 and 196"
+ select HAVE_MARCH_Z196_FEATURES if 64BIT
+ help
+ Select this to enable optimizations for IBM zEnterprise 114 and 196
+ (2818 and 2817 series). The kernel will be slightly faster but will
+ not work on older machines.
+
+endchoice
config 64BIT
def_bool y
@@ -147,6 +224,24 @@ config 64BIT
config 32BIT
def_bool y if !64BIT
+config COMPAT
+ def_bool y
+ prompt "Kernel support for 31 bit emulation"
+ depends on 64BIT
+ select COMPAT_BINFMT_ELF if BINFMT_ELF
+ select ARCH_WANT_OLD_COMPAT_IPC
+ help
+ Select this option if you want to enable your system kernel to
+ handle system-calls from ELF binaries for 31 bit ESA. This option
+ (and some other stuff like libraries and such) is needed for
+ executing 31 bit applications. It is safe to say "Y".
+
+config SYSVIPC_COMPAT
+ def_bool y if COMPAT && SYSVIPC
+
+config KEYS_COMPAT
+ def_bool y if COMPAT && KEYS
+
config SMP
def_bool y
prompt "Symmetric multi-processing support"
@@ -202,6 +297,8 @@ config SCHED_BOOK
Book scheduler support improves the CPU scheduler's decision making
when dealing with machines that have several books.
+source kernel/Kconfig.preempt
+
config MATHEMU
def_bool y
prompt "IEEE FPU emulation"
@@ -211,100 +308,35 @@ config MATHEMU
on older ESA/390 machines. Say Y unless you know your machine doesn't
need this.
-config COMPAT
- def_bool y
- prompt "Kernel support for 31 bit emulation"
- depends on 64BIT
- select COMPAT_BINFMT_ELF if BINFMT_ELF
- select ARCH_WANT_OLD_COMPAT_IPC
- help
- Select this option if you want to enable your system kernel to
- handle system-calls from ELF binaries for 31 bit ESA. This option
- (and some other stuff like libraries and such) is needed for
- executing 31 bit applications. It is safe to say "Y".
+source kernel/Kconfig.hz
-config SYSVIPC_COMPAT
- def_bool y if COMPAT && SYSVIPC
+endmenu
-config KEYS_COMPAT
- def_bool y if COMPAT && KEYS
+menu "Memory setup"
-config AUDIT_ARCH
+config ARCH_SPARSEMEM_ENABLE
def_bool y
+ select SPARSEMEM_VMEMMAP_ENABLE
+ select SPARSEMEM_VMEMMAP
+ select SPARSEMEM_STATIC if !64BIT
-config HAVE_MARCH_Z900_FEATURES
- def_bool n
-
-config HAVE_MARCH_Z990_FEATURES
- def_bool n
- select HAVE_MARCH_Z900_FEATURES
-
-config HAVE_MARCH_Z9_109_FEATURES
- def_bool n
- select HAVE_MARCH_Z990_FEATURES
-
-config HAVE_MARCH_Z10_FEATURES
- def_bool n
- select HAVE_MARCH_Z9_109_FEATURES
-
-config HAVE_MARCH_Z196_FEATURES
- def_bool n
- select HAVE_MARCH_Z10_FEATURES
-
-comment "Code generation options"
-
-choice
- prompt "Processor type"
- default MARCH_G5
-
-config MARCH_G5
- bool "System/390 model G5 and G6"
- depends on !64BIT
- help
- Select this to build a 31 bit kernel that works
- on all ESA/390 and z/Architecture machines.
-
-config MARCH_Z900
- bool "IBM zSeries model z800 and z900"
- select HAVE_MARCH_Z900_FEATURES if 64BIT
- help
- Select this to enable optimizations for model z800/z900 (2064 and
- 2066 series). This will enable some optimizations that are not
- available on older ESA/390 (31 Bit) only CPUs.
+config ARCH_SPARSEMEM_DEFAULT
+ def_bool y
-config MARCH_Z990
- bool "IBM zSeries model z890 and z990"
- select HAVE_MARCH_Z990_FEATURES if 64BIT
- help
- Select this to enable optimizations for model z890/z990 (2084 and
- 2086 series). The kernel will be slightly faster but will not work
- on older machines.
+config ARCH_SELECT_MEMORY_MODEL
+ def_bool y
-config MARCH_Z9_109
- bool "IBM System z9"
- select HAVE_MARCH_Z9_109_FEATURES if 64BIT
- help
- Select this to enable optimizations for IBM System z9 (2094 and
- 2096 series). The kernel will be slightly faster but will not work
- on older machines.
+config ARCH_ENABLE_MEMORY_HOTPLUG
+ def_bool y if SPARSEMEM
-config MARCH_Z10
- bool "IBM System z10"
- select HAVE_MARCH_Z10_FEATURES if 64BIT
- help
- Select this to enable optimizations for IBM System z10 (2097 and
- 2098 series). The kernel will be slightly faster but will not work
- on older machines.
+config ARCH_ENABLE_MEMORY_HOTREMOVE
+ def_bool y
-config MARCH_Z196
- bool "IBM zEnterprise 114 and 196"
- select HAVE_MARCH_Z196_FEATURES if 64BIT
- help
- Select this to enable optimizations for IBM zEnterprise 114 and 196
- (2818 and 2817 series). The kernel will be slightly faster but will
- not work on older machines.
+config FORCE_MAX_ZONEORDER
+ int
+ default "9"
-endchoice
+source "mm/Kconfig"
config PACK_STACK
def_bool y
@@ -368,34 +400,9 @@ config WARN_DYNAMIC_STACK
Say N if you are unsure.
-comment "Kernel preemption"
-
-source "kernel/Kconfig.preempt"
-
-config ARCH_SPARSEMEM_ENABLE
- def_bool y
- select SPARSEMEM_VMEMMAP_ENABLE
- select SPARSEMEM_VMEMMAP
- select SPARSEMEM_STATIC if !64BIT
-
-config ARCH_SPARSEMEM_DEFAULT
- def_bool y
-
-config ARCH_SELECT_MEMORY_MODEL
- def_bool y
-
-config ARCH_ENABLE_MEMORY_HOTPLUG
- def_bool y if SPARSEMEM
-
-config ARCH_ENABLE_MEMORY_HOTREMOVE
- def_bool y
-
-config ARCH_HIBERNATION_POSSIBLE
- def_bool y if 64BIT
-
-source "mm/Kconfig"
+endmenu
-comment "I/O subsystem configuration"
+menu "I/O subsystem"
config QDIO
def_tristate y
@@ -426,13 +433,102 @@ config CHSC_SCH
If unsure, say N.
-comment "Misc"
+config SCM_BUS
+ def_bool y
+ depends on 64BIT
+ prompt "SCM bus driver"
+ help
+ Bus driver for Storage Class Memory.
+
+config EADM_SCH
+ def_tristate m
+ prompt "Support for EADM subchannels"
+ depends on SCM_BUS
+ help
+ This driver allows usage of EADM subchannels. EADM subchannels act
+ as a communication vehicle for SCM increments.
+
+ To compile this driver as a module, choose M here: the
+ module will be called eadm_sch.
+
+endmenu
+
+menu "Dump support"
+
+config CRASH_DUMP
+ bool "kernel crash dumps"
+ depends on 64BIT && SMP
+ select KEXEC
+ help
+ Generate crash dump after being started by kexec.
+ Crash dump kernels are loaded in the main kernel with kexec-tools
+ into a specially reserved region and then later executed after
+ a crash by kdump/kexec.
+ For more details see Documentation/kdump/kdump.txt
+
+config ZFCPDUMP
+ def_bool n
+ prompt "zfcpdump support"
+ select SMP
+ help
+ Select this option if you want to build an zfcpdump enabled kernel.
+ Refer to <file:Documentation/s390/zfcpdump.txt> for more details on this.
+
+endmenu
+
+menu "Executable file formats / Emulations"
source "fs/Kconfig.binfmt"
-config FORCE_MAX_ZONEORDER
- int
- default "9"
+config SECCOMP
+ def_bool y
+ prompt "Enable seccomp to safely compute untrusted bytecode"
+ depends on PROC_FS
+ help
+ This kernel feature is useful for number crunching applications
+ that may need to compute untrusted bytecode during their
+ execution. By using pipes or other transports made available to
+ the process as file descriptors supporting the read/write
+ syscalls, it's possible to isolate those applications in
+ their own address space using seccomp. Once seccomp is
+ enabled via /proc/<pid>/seccomp, it cannot be disabled
+ and the task is only allowed to execute a few safe syscalls
+ defined by each seccomp mode.
+
+ If unsure, say Y.
+
+endmenu
+
+menu "Power Management"
+
+config ARCH_HIBERNATION_POSSIBLE
+ def_bool y if 64BIT
+
+source "kernel/power/Kconfig"
+
+endmenu
+
+source "net/Kconfig"
+
+config PCMCIA
+ def_bool n
+
+config CCW
+ def_bool y
+
+source "drivers/Kconfig"
+
+source "fs/Kconfig"
+
+source "arch/s390/Kconfig.debug"
+
+source "security/Kconfig"
+
+source "crypto/Kconfig"
+
+source "lib/Kconfig"
+
+menu "Virtualization"
config PFAULT
def_bool y
@@ -448,8 +544,8 @@ config PFAULT
this option.
config SHARED_KERNEL
- def_bool y
- prompt "VM shared kernel support"
+ bool "VM shared kernel support"
+ depends on !JUMP_LABEL
help
Select this option, if you want to share the text segment of the
Linux kernel between different VM guests. This reduces memory
@@ -544,8 +640,6 @@ config APPLDATA_NET_SUM
This can also be compiled as a module, which will be called
appldata_net_sum.o.
-source kernel/Kconfig.hz
-
config S390_HYPFS_FS
def_bool y
prompt "s390 hypervisor file system support"
@@ -554,90 +648,21 @@ config S390_HYPFS_FS
This is a virtual file system intended to provide accounting
information in an s390 hypervisor environment.
-config KEXEC
- def_bool n
- prompt "kexec system call"
- help
- kexec is a system call that implements the ability to shutdown your
- current kernel, and to start another kernel. It is like a reboot
- but is independent of hardware/microcode support.
-
-config CRASH_DUMP
- bool "kernel crash dumps"
- depends on 64BIT && SMP
- select KEXEC
- help
- Generate crash dump after being started by kexec.
- Crash dump kernels are loaded in the main kernel with kexec-tools
- into a specially reserved region and then later executed after
- a crash by kdump/kexec.
- For more details see Documentation/kdump/kdump.txt
-
-config ZFCPDUMP
- def_bool n
- prompt "zfcpdump support"
- select SMP
- help
- Select this option if you want to build an zfcpdump enabled kernel.
- Refer to <file:Documentation/s390/zfcpdump.txt> for more details on this.
+source "arch/s390/kvm/Kconfig"
config S390_GUEST
def_bool y
- prompt "s390 guest support for KVM (EXPERIMENTAL)"
+ prompt "s390 support for virtio devices (EXPERIMENTAL)"
depends on 64BIT && EXPERIMENTAL
select VIRTUALIZATION
select VIRTIO
select VIRTIO_RING
select VIRTIO_CONSOLE
help
- Select this option if you want to run the kernel as a guest under
- the KVM hypervisor. This will add detection for KVM as well as a
- virtio transport. If KVM is detected, the virtio console will be
- the default console.
-
-config SECCOMP
- def_bool y
- prompt "Enable seccomp to safely compute untrusted bytecode"
- depends on PROC_FS
- help
- This kernel feature is useful for number crunching applications
- that may need to compute untrusted bytecode during their
- execution. By using pipes or other transports made available to
- the process as file descriptors supporting the read/write
- syscalls, it's possible to isolate those applications in
- their own address space using seccomp. Once seccomp is
- enabled via /proc/<pid>/seccomp, it cannot be disabled
- and the task is only allowed to execute a few safe syscalls
- defined by each seccomp mode.
-
- If unsure, say Y.
-
-endmenu
+ Enabling this option adds support for virtio based paravirtual device
+ drivers on s390.
-menu "Power Management"
-
-source "kernel/power/Kconfig"
+ Select this option if you want to run the kernel as a guest under
+ the KVM hypervisor.
endmenu
-
-source "net/Kconfig"
-
-config PCMCIA
- def_bool n
-
-config CCW
- def_bool y
-
-source "drivers/Kconfig"
-
-source "fs/Kconfig"
-
-source "arch/s390/Kconfig.debug"
-
-source "security/Kconfig"
-
-source "crypto/Kconfig"
-
-source "lib/Kconfig"
-
-source "arch/s390/kvm/Kconfig"
diff --git a/arch/s390/boot/compressed/Makefile b/arch/s390/boot/compressed/Makefile
index 10e22c4ec4a7..3ad8f61c9985 100644
--- a/arch/s390/boot/compressed/Makefile
+++ b/arch/s390/boot/compressed/Makefile
@@ -11,6 +11,7 @@ targets := vmlinux.lds vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 \
sizes.h head$(BITS).o
KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2
+KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
KBUILD_CFLAGS += $(cflags-y)
KBUILD_CFLAGS += $(call cc-option,-mpacked-stack)
KBUILD_CFLAGS += $(call cc-option,-ffreestanding)
diff --git a/arch/s390/boot/compressed/misc.c b/arch/s390/boot/compressed/misc.c
index 465eca756feb..c4c6a1cf221b 100644
--- a/arch/s390/boot/compressed/misc.c
+++ b/arch/s390/boot/compressed/misc.c
@@ -71,34 +71,37 @@ void *memset(void *s, int c, size_t n)
{
char *xs;
- if (c == 0)
- return __builtin_memset(s, 0, n);
-
- xs = (char *) s;
- if (n > 0)
- do {
- *xs++ = c;
- } while (--n > 0);
+ xs = s;
+ while (n--)
+ *xs++ = c;
return s;
}
-void *memcpy(void *__dest, __const void *__src, size_t __n)
+void *memcpy(void *dest, const void *src, size_t n)
{
- return __builtin_memcpy(__dest, __src, __n);
+ const char *s = src;
+ char *d = dest;
+
+ while (n--)
+ *d++ = *s++;
+ return dest;
}
-void *memmove(void *__dest, __const void *__src, size_t __n)
+void *memmove(void *dest, const void *src, size_t n)
{
- char *d;
- const char *s;
-
- if (__dest <= __src)
- return __builtin_memcpy(__dest, __src, __n);
- d = __dest + __n;
- s = __src + __n;
- while (__n--)
- *--d = *--s;
- return __dest;
+ const char *s = src;
+ char *d = dest;
+
+ if (d <= s) {
+ while (n--)
+ *d++ = *s++;
+ } else {
+ d += n;
+ s += n;
+ while (n--)
+ *--d = *--s;
+ }
+ return dest;
}
static void error(char *x)
diff --git a/arch/s390/defconfig b/arch/s390/defconfig
index f39cd710980b..b74400e3e035 100644
--- a/arch/s390/defconfig
+++ b/arch/s390/defconfig
@@ -16,8 +16,8 @@ CONFIG_CGROUPS=y
CONFIG_CPUSETS=y
CONFIG_CGROUP_CPUACCT=y
CONFIG_RESOURCE_COUNTERS=y
-CONFIG_CGROUP_MEMCG=y
-CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+CONFIG_MEMCG=y
+CONFIG_MEMCG_SWAP=y
CONFIG_CGROUP_SCHED=y
CONFIG_RT_GROUP_SCHED=y
CONFIG_BLK_CGROUP=y
@@ -32,20 +32,19 @@ CONFIG_EXPERT=y
CONFIG_PROFILING=y
CONFIG_OPROFILE=y
CONFIG_KPROBES=y
+CONFIG_JUMP_LABEL=y
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODVERSIONS=y
CONFIG_PARTITION_ADVANCED=y
CONFIG_IBM_PARTITION=y
CONFIG_DEFAULT_DEADLINE=y
-CONFIG_PREEMPT=y
+CONFIG_HZ_100=y
CONFIG_MEMORY_HOTPLUG=y
CONFIG_MEMORY_HOTREMOVE=y
CONFIG_KSM=y
-CONFIG_BINFMT_MISC=m
-CONFIG_CMM=m
-CONFIG_HZ_100=y
CONFIG_CRASH_DUMP=y
+CONFIG_BINFMT_MISC=m
CONFIG_HIBERNATION=y
CONFIG_PACKET=y
CONFIG_UNIX=y
@@ -75,6 +74,7 @@ CONFIG_NET_CLS_RSVP=m
CONFIG_NET_CLS_RSVP6=m
CONFIG_NET_CLS_ACT=y
CONFIG_NET_ACT_POLICE=y
+CONFIG_BPF_JIT=y
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEVTMPFS=y
CONFIG_BLK_DEV_LOOP=m
@@ -121,7 +121,6 @@ CONFIG_DEBUG_NOTIFIERS=y
CONFIG_RCU_TRACE=y
CONFIG_KPROBES_SANITY_TEST=y
CONFIG_DEBUG_FORCE_WEAK_PER_CPU=y
-CONFIG_CPU_NOTIFIER_ERROR_INJECT=m
CONFIG_LATENCYTOP=y
CONFIG_DEBUG_PAGEALLOC=y
CONFIG_BLK_DEV_IO_TRACE=y
@@ -173,3 +172,4 @@ CONFIG_CRYPTO_SHA512_S390=m
CONFIG_CRYPTO_DES_S390=m
CONFIG_CRYPTO_AES_S390=m
CONFIG_CRC7=m
+CONFIG_CMM=m
diff --git a/arch/s390/include/asm/appldata.h b/arch/s390/include/asm/appldata.h
index f328294faeae..32a705987156 100644
--- a/arch/s390/include/asm/appldata.h
+++ b/arch/s390/include/asm/appldata.h
@@ -70,7 +70,7 @@ static inline int appldata_asm(struct appldata_product_id *id,
int ry;
if (!MACHINE_IS_VM)
- return -ENOSYS;
+ return -EOPNOTSUPP;
parm_list.diag = 0xdc;
parm_list.function = fn;
parm_list.parlist_length = sizeof(parm_list);
diff --git a/arch/s390/include/asm/chsc.h b/arch/s390/include/asm/chsc.h
index bf115b49f444..aea451fd182e 100644
--- a/arch/s390/include/asm/chsc.h
+++ b/arch/s390/include/asm/chsc.h
@@ -125,32 +125,4 @@ struct chsc_cpd_info {
#define CHSC_INFO_CPD _IOWR(CHSC_IOCTL_MAGIC, 0x87, struct chsc_cpd_info)
#define CHSC_INFO_DCAL _IOWR(CHSC_IOCTL_MAGIC, 0x88, struct chsc_dcal)
-#ifdef __KERNEL__
-
-struct css_general_char {
- u64 : 12;
- u32 dynio : 1; /* bit 12 */
- u32 : 28;
- u32 aif : 1; /* bit 41 */
- u32 : 3;
- u32 mcss : 1; /* bit 45 */
- u32 fcs : 1; /* bit 46 */
- u32 : 1;
- u32 ext_mb : 1; /* bit 48 */
- u32 : 7;
- u32 aif_tdd : 1; /* bit 56 */
- u32 : 1;
- u32 qebsm : 1; /* bit 58 */
- u32 : 8;
- u32 aif_osa : 1; /* bit 67 */
- u32 : 14;
- u32 cib : 1; /* bit 82 */
- u32 : 5;
- u32 fcx : 1; /* bit 88 */
- u32 : 7;
-}__attribute__((packed));
-
-extern struct css_general_char css_general_characteristics;
-
-#endif /* __KERNEL__ */
#endif
diff --git a/arch/s390/include/asm/cio.h b/arch/s390/include/asm/cio.h
index 77043aa44d67..55bde6035216 100644
--- a/arch/s390/include/asm/cio.h
+++ b/arch/s390/include/asm/cio.h
@@ -80,6 +80,18 @@ struct erw {
} __attribute__ ((packed));
/**
+ * struct erw_eadm - EADM Subchannel extended report word
+ * @b: aob error
+ * @r: arsb error
+ */
+struct erw_eadm {
+ __u32 : 16;
+ __u32 b : 1;
+ __u32 r : 1;
+ __u32 : 14;
+} __packed;
+
+/**
* struct sublog - subchannel logout area
* @res0: reserved
* @esf: extended status flags
@@ -170,9 +182,22 @@ struct esw3 {
} __attribute__ ((packed));
/**
+ * struct esw_eadm - EADM Subchannel Extended Status Word (ESW)
+ * @sublog: subchannel logout
+ * @erw: extended report word
+ */
+struct esw_eadm {
+ __u32 sublog;
+ struct erw_eadm erw;
+ __u32 : 32;
+ __u32 : 32;
+ __u32 : 32;
+} __packed;
+
+/**
* struct irb - interruption response block
* @scsw: subchannel status word
- * @esw: extened status word, 4 formats
+ * @esw: extened status word
* @ecw: extended control word
*
* The irb that is handed to the device driver when an interrupt occurs. For
@@ -191,6 +216,7 @@ struct irb {
struct esw1 esw1;
struct esw2 esw2;
struct esw3 esw3;
+ struct esw_eadm eadm;
} esw;
__u8 ecw[32];
} __attribute__ ((packed,aligned(4)));
diff --git a/arch/s390/include/asm/cmpxchg.h b/arch/s390/include/asm/cmpxchg.h
index 8d798e962b63..0f636cbdf342 100644
--- a/arch/s390/include/asm/cmpxchg.h
+++ b/arch/s390/include/asm/cmpxchg.h
@@ -7,7 +7,9 @@
#ifndef __ASM_CMPXCHG_H
#define __ASM_CMPXCHG_H
+#include <linux/mmdebug.h>
#include <linux/types.h>
+#include <linux/bug.h>
extern void __xchg_called_with_bad_pointer(void);
@@ -203,6 +205,65 @@ static inline unsigned long long __cmpxchg64(void *ptr,
})
#endif /* CONFIG_64BIT */
+#define __cmpxchg_double_op(p1, p2, o1, o2, n1, n2, insn) \
+({ \
+ register __typeof__(*(p1)) __old1 asm("2") = (o1); \
+ register __typeof__(*(p2)) __old2 asm("3") = (o2); \
+ register __typeof__(*(p1)) __new1 asm("4") = (n1); \
+ register __typeof__(*(p2)) __new2 asm("5") = (n2); \
+ int cc; \
+ asm volatile( \
+ insn " %[old],%[new],%[ptr]\n" \
+ " ipm %[cc]\n" \
+ " srl %[cc],28" \
+ : [cc] "=d" (cc), [old] "+d" (__old1), "+d" (__old2) \
+ : [new] "d" (__new1), "d" (__new2), \
+ [ptr] "Q" (*(p1)), "Q" (*(p2)) \
+ : "memory", "cc"); \
+ !cc; \
+})
+
+#define __cmpxchg_double_4(p1, p2, o1, o2, n1, n2) \
+ __cmpxchg_double_op(p1, p2, o1, o2, n1, n2, "cds")
+
+#define __cmpxchg_double_8(p1, p2, o1, o2, n1, n2) \
+ __cmpxchg_double_op(p1, p2, o1, o2, n1, n2, "cdsg")
+
+extern void __cmpxchg_double_called_with_bad_pointer(void);
+
+#define __cmpxchg_double(p1, p2, o1, o2, n1, n2) \
+({ \
+ int __ret; \
+ switch (sizeof(*(p1))) { \
+ case 4: \
+ __ret = __cmpxchg_double_4(p1, p2, o1, o2, n1, n2); \
+ break; \
+ case 8: \
+ __ret = __cmpxchg_double_8(p1, p2, o1, o2, n1, n2); \
+ break; \
+ default: \
+ __cmpxchg_double_called_with_bad_pointer(); \
+ } \
+ __ret; \
+})
+
+#define cmpxchg_double(p1, p2, o1, o2, n1, n2) \
+({ \
+ __typeof__(p1) __p1 = (p1); \
+ __typeof__(p2) __p2 = (p2); \
+ int __ret; \
+ BUILD_BUG_ON(sizeof(*(p1)) != sizeof(long)); \
+ BUILD_BUG_ON(sizeof(*(p2)) != sizeof(long)); \
+ VM_BUG_ON((unsigned long)((__p1) + 1) != (unsigned long)(__p2));\
+ if (sizeof(long) == 4) \
+ __ret = __cmpxchg_double_4(__p1, __p2, o1, o2, n1, n2); \
+ else \
+ __ret = __cmpxchg_double_8(__p1, __p2, o1, o2, n1, n2); \
+ __ret; \
+})
+
+#define system_has_cmpxchg_double() 1
+
#include <asm-generic/cmpxchg-local.h>
static inline unsigned long __cmpxchg_local(void *ptr,
diff --git a/arch/s390/include/asm/cpu_mf.h b/arch/s390/include/asm/cpu_mf.h
index a3afecdae145..35f0020b7ba7 100644
--- a/arch/s390/include/asm/cpu_mf.h
+++ b/arch/s390/include/asm/cpu_mf.h
@@ -21,11 +21,15 @@
#define CPU_MF_INT_SF_LSDA (1 << 22) /* loss of sample data alert */
#define CPU_MF_INT_CF_CACA (1 << 7) /* counter auth. change alert */
#define CPU_MF_INT_CF_LCDA (1 << 6) /* loss of counter data alert */
+#define CPU_MF_INT_RI_HALTED (1 << 5) /* run-time instr. halted */
+#define CPU_MF_INT_RI_BUF_FULL (1 << 4) /* run-time instr. program
+ buffer full */
#define CPU_MF_INT_CF_MASK (CPU_MF_INT_CF_CACA|CPU_MF_INT_CF_LCDA)
#define CPU_MF_INT_SF_MASK (CPU_MF_INT_SF_IAE|CPU_MF_INT_SF_ISE| \
CPU_MF_INT_SF_PRA|CPU_MF_INT_SF_SACA| \
CPU_MF_INT_SF_LSDA)
+#define CPU_MF_INT_RI_MASK (CPU_MF_INT_RI_HALTED|CPU_MF_INT_RI_BUF_FULL)
/* CPU measurement facility support */
static inline int cpum_cf_avail(void)
diff --git a/arch/s390/include/asm/cputime.h b/arch/s390/include/asm/cputime.h
index 8709bdef233c..023d5ae24482 100644
--- a/arch/s390/include/asm/cputime.h
+++ b/arch/s390/include/asm/cputime.h
@@ -12,6 +12,9 @@
#include <linux/spinlock.h>
#include <asm/div64.h>
+
+#define __ARCH_HAS_VTIME_ACCOUNT
+
/* We want to use full resolution of the CPU timer: 2**-12 micro-seconds. */
typedef unsigned long long __nocast cputime_t;
diff --git a/arch/s390/include/asm/css_chars.h b/arch/s390/include/asm/css_chars.h
new file mode 100644
index 000000000000..a06ebc2623fb
--- /dev/null
+++ b/arch/s390/include/asm/css_chars.h
@@ -0,0 +1,39 @@
+#ifndef _ASM_CSS_CHARS_H
+#define _ASM_CSS_CHARS_H
+
+#include <linux/types.h>
+
+#ifdef __KERNEL__
+
+struct css_general_char {
+ u64 : 12;
+ u32 dynio : 1; /* bit 12 */
+ u32 : 4;
+ u32 eadm : 1; /* bit 17 */
+ u32 : 23;
+ u32 aif : 1; /* bit 41 */
+ u32 : 3;
+ u32 mcss : 1; /* bit 45 */
+ u32 fcs : 1; /* bit 46 */
+ u32 : 1;
+ u32 ext_mb : 1; /* bit 48 */
+ u32 : 7;
+ u32 aif_tdd : 1; /* bit 56 */
+ u32 : 1;
+ u32 qebsm : 1; /* bit 58 */
+ u32 : 8;
+ u32 aif_osa : 1; /* bit 67 */
+ u32 : 12;
+ u32 eadm_rf : 1; /* bit 80 */
+ u32 : 1;
+ u32 cib : 1; /* bit 82 */
+ u32 : 5;
+ u32 fcx : 1; /* bit 88 */
+ u32 : 19;
+ u32 alt_ssi : 1; /* bit 108 */
+} __packed;
+
+extern struct css_general_char css_general_characteristics;
+
+#endif /* __KERNEL__ */
+#endif
diff --git a/arch/s390/include/asm/eadm.h b/arch/s390/include/asm/eadm.h
new file mode 100644
index 000000000000..8d4847191ecc
--- /dev/null
+++ b/arch/s390/include/asm/eadm.h
@@ -0,0 +1,124 @@
+#ifndef _ASM_S390_EADM_H
+#define _ASM_S390_EADM_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+
+struct arqb {
+ u64 data;
+ u16 fmt:4;
+ u16:12;
+ u16 cmd_code;
+ u16:16;
+ u16 msb_count;
+ u32 reserved[12];
+} __packed;
+
+#define ARQB_CMD_MOVE 1
+
+struct arsb {
+ u16 fmt:4;
+ u32:28;
+ u8 ef;
+ u8:8;
+ u8 ecbi;
+ u8:8;
+ u8 fvf;
+ u16:16;
+ u8 eqc;
+ u32:32;
+ u64 fail_msb;
+ u64 fail_aidaw;
+ u64 fail_ms;
+ u64 fail_scm;
+ u32 reserved[4];
+} __packed;
+
+struct msb {
+ u8 fmt:4;
+ u8 oc:4;
+ u8 flags;
+ u16:12;
+ u16 bs:4;
+ u32 blk_count;
+ u64 data_addr;
+ u64 scm_addr;
+ u64:64;
+} __packed;
+
+struct aidaw {
+ u8 flags;
+ u32 :24;
+ u32 :32;
+ u64 data_addr;
+} __packed;
+
+#define MSB_OC_CLEAR 0
+#define MSB_OC_READ 1
+#define MSB_OC_WRITE 2
+#define MSB_OC_RELEASE 3
+
+#define MSB_FLAG_BNM 0x80
+#define MSB_FLAG_IDA 0x40
+
+#define MSB_BS_4K 0
+#define MSB_BS_1M 1
+
+#define AOB_NR_MSB 124
+
+struct aob {
+ struct arqb request;
+ struct arsb response;
+ struct msb msb[AOB_NR_MSB];
+} __packed __aligned(PAGE_SIZE);
+
+struct aob_rq_header {
+ struct scm_device *scmdev;
+ char data[0];
+};
+
+struct scm_device {
+ u64 address;
+ u64 size;
+ unsigned int nr_max_block;
+ struct device dev;
+ struct {
+ unsigned int persistence:4;
+ unsigned int oper_state:4;
+ unsigned int data_state:4;
+ unsigned int rank:4;
+ unsigned int release:1;
+ unsigned int res_id:8;
+ } __packed attrs;
+};
+
+#define OP_STATE_GOOD 1
+#define OP_STATE_TEMP_ERR 2
+#define OP_STATE_PERM_ERR 3
+
+struct scm_driver {
+ struct device_driver drv;
+ int (*probe) (struct scm_device *scmdev);
+ int (*remove) (struct scm_device *scmdev);
+ void (*notify) (struct scm_device *scmdev);
+ void (*handler) (struct scm_device *scmdev, void *data, int error);
+};
+
+int scm_driver_register(struct scm_driver *scmdrv);
+void scm_driver_unregister(struct scm_driver *scmdrv);
+
+int scm_start_aob(struct aob *aob);
+void scm_irq_handler(struct aob *aob, int error);
+
+struct eadm_ops {
+ int (*eadm_start) (struct aob *aob);
+ struct module *owner;
+};
+
+int scm_get_ref(void);
+void scm_put_ref(void);
+
+void register_eadm_ops(struct eadm_ops *ops);
+void unregister_eadm_ops(struct eadm_ops *ops);
+
+#endif /* _ASM_S390_EADM_H */
diff --git a/arch/s390/include/asm/elf.h b/arch/s390/include/asm/elf.h
index 9b94a160fe7f..178ff966a8ba 100644
--- a/arch/s390/include/asm/elf.h
+++ b/arch/s390/include/asm/elf.h
@@ -101,6 +101,7 @@
#define HWCAP_S390_HPAGE 128
#define HWCAP_S390_ETF3EH 256
#define HWCAP_S390_HIGH_GPRS 512
+#define HWCAP_S390_TE 1024
/*
* These are used to set parameters in the core dumps.
@@ -212,4 +213,6 @@ int arch_setup_additional_pages(struct linux_binprm *, int);
extern unsigned long arch_randomize_brk(struct mm_struct *mm);
#define arch_randomize_brk arch_randomize_brk
+void *fill_cpu_elf_notes(void *ptr, struct save_area *sa);
+
#endif
diff --git a/arch/s390/include/asm/etr.h b/arch/s390/include/asm/etr.h
index a24b03b9fb64..629b79a93165 100644
--- a/arch/s390/include/asm/etr.h
+++ b/arch/s390/include/asm/etr.h
@@ -140,7 +140,7 @@ struct etr_ptff_qto {
/* Inline assembly helper functions */
static inline int etr_setr(struct etr_eacr *ctrl)
{
- int rc = -ENOSYS;
+ int rc = -EOPNOTSUPP;
asm volatile(
" .insn s,0xb2160000,%1\n"
@@ -154,7 +154,7 @@ static inline int etr_setr(struct etr_eacr *ctrl)
/* Stores a format 1 aib with 64 bytes */
static inline int etr_stetr(struct etr_aib *aib)
{
- int rc = -ENOSYS;
+ int rc = -EOPNOTSUPP;
asm volatile(
" .insn s,0xb2170000,%1\n"
@@ -169,7 +169,7 @@ static inline int etr_stetr(struct etr_aib *aib)
static inline int etr_steai(struct etr_aib *aib, unsigned int func)
{
register unsigned int reg0 asm("0") = func;
- int rc = -ENOSYS;
+ int rc = -EOPNOTSUPP;
asm volatile(
" .insn s,0xb2b30000,%1\n"
@@ -190,7 +190,7 @@ static inline int etr_ptff(void *ptff_block, unsigned int func)
{
register unsigned int reg0 asm("0") = func;
register unsigned long reg1 asm("1") = (unsigned long) ptff_block;
- int rc = -ENOSYS;
+ int rc = -EOPNOTSUPP;
asm volatile(
" .word 0x0104\n"
diff --git a/arch/s390/include/asm/hugetlb.h b/arch/s390/include/asm/hugetlb.h
index 799ed0f1643d..2d6e6e380564 100644
--- a/arch/s390/include/asm/hugetlb.h
+++ b/arch/s390/include/asm/hugetlb.h
@@ -66,16 +66,6 @@ static inline pte_t huge_ptep_get(pte_t *ptep)
return pte;
}
-static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
- unsigned long addr, pte_t *ptep)
-{
- pte_t pte = huge_ptep_get(ptep);
-
- mm->context.flush_mm = 1;
- pmd_clear((pmd_t *) ptep);
- return pte;
-}
-
static inline void __pmd_csp(pmd_t *pmdp)
{
register unsigned long reg2 asm("2") = pmd_val(*pmdp);
@@ -117,6 +107,15 @@ static inline void huge_ptep_invalidate(struct mm_struct *mm,
__pmd_csp(pmdp);
}
+static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
+ unsigned long addr, pte_t *ptep)
+{
+ pte_t pte = huge_ptep_get(ptep);
+
+ huge_ptep_invalidate(mm, addr, ptep);
+ return pte;
+}
+
#define huge_ptep_set_access_flags(__vma, __addr, __ptep, __entry, __dirty) \
({ \
int __changed = !pte_same(huge_ptep_get(__ptep), __entry); \
@@ -131,10 +130,7 @@ static inline void huge_ptep_invalidate(struct mm_struct *mm,
({ \
pte_t __pte = huge_ptep_get(__ptep); \
if (pte_write(__pte)) { \
- (__mm)->context.flush_mm = 1; \
- if (atomic_read(&(__mm)->context.attach_count) > 1 || \
- (__mm) != current->active_mm) \
- huge_ptep_invalidate(__mm, __addr, __ptep); \
+ huge_ptep_invalidate(__mm, __addr, __ptep); \
set_huge_pte_at(__mm, __addr, __ptep, \
huge_pte_wrprotect(__pte)); \
} \
diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h
index 2b9d41899d21..6703dd986fd4 100644
--- a/arch/s390/include/asm/irq.h
+++ b/arch/s390/include/asm/irq.h
@@ -19,6 +19,7 @@ enum interruption_class {
EXTINT_IUC,
EXTINT_CMS,
EXTINT_CMC,
+ EXTINT_CMR,
IOINT_CIO,
IOINT_QAI,
IOINT_DAS,
@@ -30,6 +31,7 @@ enum interruption_class {
IOINT_CLW,
IOINT_CTC,
IOINT_APB,
+ IOINT_ADM,
IOINT_CSC,
NMI_NMI,
NR_IRQS,
diff --git a/arch/s390/include/asm/isc.h b/arch/s390/include/asm/isc.h
index 1420a1115948..5ae606456b0a 100644
--- a/arch/s390/include/asm/isc.h
+++ b/arch/s390/include/asm/isc.h
@@ -14,6 +14,7 @@
/* Regular I/O interrupts. */
#define IO_SCH_ISC 3 /* regular I/O subchannels */
#define CONSOLE_ISC 1 /* console I/O subchannel */
+#define EADM_SCH_ISC 4 /* EADM subchannels */
#define CHSC_SCH_ISC 7 /* CHSC subchannels */
/* Adapter interrupts. */
#define QDIO_AIRQ_ISC IO_SCH_ISC /* I/O subchannel in qdio mode */
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h
index aab5555bbbda..bbf8141408cd 100644
--- a/arch/s390/include/asm/lowcore.h
+++ b/arch/s390/include/asm/lowcore.h
@@ -329,9 +329,13 @@ struct _lowcore {
__u8 pad_0x1338[0x1340-0x1338]; /* 0x1338 */
__u32 access_regs_save_area[16]; /* 0x1340 */
__u64 cregs_save_area[16]; /* 0x1380 */
+ __u8 pad_0x1400[0x1800-0x1400]; /* 0x1400 */
+
+ /* Transaction abort diagnostic block */
+ __u8 pgm_tdb[256]; /* 0x1800 */
/* align to the top of the prefix area */
- __u8 pad_0x1400[0x2000-0x1400]; /* 0x1400 */
+ __u8 pad_0x1900[0x2000-0x1900]; /* 0x1900 */
} __packed;
#endif /* CONFIG_32BIT */
diff --git a/arch/s390/include/asm/mmu_context.h b/arch/s390/include/asm/mmu_context.h
index b749c5733657..084e7755ed9b 100644
--- a/arch/s390/include/asm/mmu_context.h
+++ b/arch/s390/include/asm/mmu_context.h
@@ -57,7 +57,7 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
pgd_t *pgd = mm->pgd;
S390_lowcore.user_asce = mm->context.asce_bits | __pa(pgd);
- if (addressing_mode != HOME_SPACE_MODE) {
+ if (s390_user_mode != HOME_SPACE_MODE) {
/* Load primary space page table origin. */
asm volatile(LCTL_OPCODE" 1,1,%0\n"
: : "m" (S390_lowcore.user_asce) );
diff --git a/arch/s390/include/asm/percpu.h b/arch/s390/include/asm/percpu.h
index 6537e72e0853..86fe0ee2cee5 100644
--- a/arch/s390/include/asm/percpu.h
+++ b/arch/s390/include/asm/percpu.h
@@ -20,7 +20,7 @@
#endif
#define arch_this_cpu_to_op(pcp, val, op) \
-do { \
+({ \
typedef typeof(pcp) pcp_op_T__; \
pcp_op_T__ old__, new__, prev__; \
pcp_op_T__ *ptr__; \
@@ -39,13 +39,19 @@ do { \
} \
} while (prev__ != old__); \
preempt_enable(); \
-} while (0)
+ new__; \
+})
#define this_cpu_add_1(pcp, val) arch_this_cpu_to_op(pcp, val, +)
#define this_cpu_add_2(pcp, val) arch_this_cpu_to_op(pcp, val, +)
#define this_cpu_add_4(pcp, val) arch_this_cpu_to_op(pcp, val, +)
#define this_cpu_add_8(pcp, val) arch_this_cpu_to_op(pcp, val, +)
+#define this_cpu_add_return_1(pcp, val) arch_this_cpu_to_op(pcp, val, +)
+#define this_cpu_add_return_2(pcp, val) arch_this_cpu_to_op(pcp, val, +)
+#define this_cpu_add_return_4(pcp, val) arch_this_cpu_to_op(pcp, val, +)
+#define this_cpu_add_return_8(pcp, val) arch_this_cpu_to_op(pcp, val, +)
+
#define this_cpu_and_1(pcp, val) arch_this_cpu_to_op(pcp, val, &)
#define this_cpu_and_2(pcp, val) arch_this_cpu_to_op(pcp, val, &)
#define this_cpu_and_4(pcp, val) arch_this_cpu_to_op(pcp, val, &)
@@ -61,7 +67,7 @@ do { \
#define this_cpu_xor_4(pcp, val) arch_this_cpu_to_op(pcp, val, ^)
#define this_cpu_xor_8(pcp, val) arch_this_cpu_to_op(pcp, val, ^)
-#define arch_this_cpu_cmpxchg(pcp, oval, nval) \
+#define arch_this_cpu_cmpxchg(pcp, oval, nval) \
({ \
typedef typeof(pcp) pcp_op_T__; \
pcp_op_T__ ret__; \
@@ -84,6 +90,44 @@ do { \
#define this_cpu_cmpxchg_4(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)
#define this_cpu_cmpxchg_8(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)
+#define arch_this_cpu_xchg(pcp, nval) \
+({ \
+ typeof(pcp) *ptr__; \
+ typeof(pcp) ret__; \
+ preempt_disable(); \
+ ptr__ = __this_cpu_ptr(&(pcp)); \
+ ret__ = xchg(ptr__, nval); \
+ preempt_enable(); \
+ ret__; \
+})
+
+#define this_cpu_xchg_1(pcp, nval) arch_this_cpu_xchg(pcp, nval)
+#define this_cpu_xchg_2(pcp, nval) arch_this_cpu_xchg(pcp, nval)
+#define this_cpu_xchg_4(pcp, nval) arch_this_cpu_xchg(pcp, nval)
+#ifdef CONFIG_64BIT
+#define this_cpu_xchg_8(pcp, nval) arch_this_cpu_xchg(pcp, nval)
+#endif
+
+#define arch_this_cpu_cmpxchg_double(pcp1, pcp2, o1, o2, n1, n2) \
+({ \
+ typeof(pcp1) o1__ = (o1), n1__ = (n1); \
+ typeof(pcp2) o2__ = (o2), n2__ = (n2); \
+ typeof(pcp1) *p1__; \
+ typeof(pcp2) *p2__; \
+ int ret__; \
+ preempt_disable(); \
+ p1__ = __this_cpu_ptr(&(pcp1)); \
+ p2__ = __this_cpu_ptr(&(pcp2)); \
+ ret__ = __cmpxchg_double(p1__, p2__, o1__, o2__, n1__, n2__); \
+ preempt_enable(); \
+ ret__; \
+})
+
+#define this_cpu_cmpxchg_double_4 arch_this_cpu_cmpxchg_double
+#ifdef CONFIG_64BIT
+#define this_cpu_cmpxchg_double_8 arch_this_cpu_cmpxchg_double
+#endif
+
#include <asm-generic/percpu.h>
#endif /* __ARCH_S390_PERCPU__ */
diff --git a/arch/s390/include/asm/processor.h b/arch/s390/include/asm/processor.h
index 11e4e3236937..f3e0aabfc6bc 100644
--- a/arch/s390/include/asm/processor.h
+++ b/arch/s390/include/asm/processor.h
@@ -11,12 +11,15 @@
#ifndef __ASM_S390_PROCESSOR_H
#define __ASM_S390_PROCESSOR_H
+#ifndef __ASSEMBLY__
+
#include <linux/linkage.h>
#include <linux/irqflags.h>
#include <asm/cpu.h>
#include <asm/page.h>
#include <asm/ptrace.h>
#include <asm/setup.h>
+#include <asm/runtime_instr.h>
/*
* Default implementation of macro that returns current
@@ -75,11 +78,20 @@ struct thread_struct {
unsigned long gmap_addr; /* address of last gmap fault. */
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 */
/* pfault_wait is used to block the process on a pfault event */
unsigned long pfault_wait;
struct list_head list;
+ /* cpu runtime instrumentation */
+ struct runtime_instr_cb *ri_cb;
+ int ri_signum;
+#ifdef CONFIG_64BIT
+ unsigned char trap_tdb[256]; /* Transaction abort diagnose block */
+#endif
};
+#define PER_FLAG_NO_TE 1UL /* Flag to disable transactions. */
+
typedef struct thread_struct thread_struct;
/*
@@ -130,6 +142,12 @@ struct task_struct;
struct mm_struct;
struct seq_file;
+#ifdef CONFIG_64BIT
+extern void show_cacheinfo(struct seq_file *m);
+#else
+static inline void show_cacheinfo(struct seq_file *m) { }
+#endif
+
/* Free all resources held by a thread. */
extern void release_thread(struct task_struct *);
extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
@@ -140,6 +158,7 @@ extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
extern unsigned long thread_saved_pc(struct task_struct *t);
extern void show_code(struct pt_regs *regs);
+extern void print_fn_code(unsigned char *code, unsigned long len);
unsigned long get_wchan(struct task_struct *p);
#define task_pt_regs(tsk) ((struct pt_regs *) \
@@ -331,23 +350,6 @@ extern void (*s390_base_ext_handler_fn)(void);
#define ARCH_LOW_ADDRESS_LIMIT 0x7fffffffUL
-/*
- * Helper macro for exception table entries
- */
-#ifndef CONFIG_64BIT
-#define EX_TABLE(_fault,_target) \
- ".section __ex_table,\"a\"\n" \
- " .align 4\n" \
- " .long " #_fault "," #_target "\n" \
- ".previous\n"
-#else
-#define EX_TABLE(_fault,_target) \
- ".section __ex_table,\"a\"\n" \
- " .align 8\n" \
- " .quad " #_fault "," #_target "\n" \
- ".previous\n"
-#endif
-
extern int memcpy_real(void *, void *, size_t);
extern void memcpy_absolute(void *, void *, size_t);
@@ -358,4 +360,25 @@ extern void memcpy_absolute(void *, void *, size_t);
memcpy_absolute(&(dest), &__tmp, sizeof(__tmp)); \
}
-#endif /* __ASM_S390_PROCESSOR_H */
+/*
+ * Helper macro for exception table entries
+ */
+#define EX_TABLE(_fault, _target) \
+ ".section __ex_table,\"a\"\n" \
+ ".align 4\n" \
+ ".long (" #_fault ") - .\n" \
+ ".long (" #_target ") - .\n" \
+ ".previous\n"
+
+#else /* __ASSEMBLY__ */
+
+#define EX_TABLE(_fault, _target) \
+ .section __ex_table,"a" ; \
+ .align 4 ; \
+ .long (_fault) - . ; \
+ .long (_target) - . ; \
+ .previous
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_S390_PROCESSOR_H */
diff --git a/arch/s390/include/asm/ptrace.h b/arch/s390/include/asm/ptrace.h
index d5f08ea566ed..ce20a53afe91 100644
--- a/arch/s390/include/asm/ptrace.h
+++ b/arch/s390/include/asm/ptrace.h
@@ -235,6 +235,7 @@ typedef struct
#define PSW_MASK_ASC 0x0000C000UL
#define PSW_MASK_CC 0x00003000UL
#define PSW_MASK_PM 0x00000F00UL
+#define PSW_MASK_RI 0x00000000UL
#define PSW_MASK_EA 0x00000000UL
#define PSW_MASK_BA 0x00000000UL
@@ -264,10 +265,11 @@ typedef struct
#define PSW_MASK_ASC 0x0000C00000000000UL
#define PSW_MASK_CC 0x0000300000000000UL
#define PSW_MASK_PM 0x00000F0000000000UL
+#define PSW_MASK_RI 0x0000008000000000UL
#define PSW_MASK_EA 0x0000000100000000UL
#define PSW_MASK_BA 0x0000000080000000UL
-#define PSW_MASK_USER 0x00003F0180000000UL
+#define PSW_MASK_USER 0x00003F8180000000UL
#define PSW_ADDR_AMODE 0x0000000000000000UL
#define PSW_ADDR_INSN 0xFFFFFFFFFFFFFFFFUL
@@ -359,17 +361,19 @@ struct per_struct_kernel {
unsigned char access_id; /* PER trap access identification */
};
-#define PER_EVENT_MASK 0xE9000000UL
+#define PER_EVENT_MASK 0xEB000000UL
#define PER_EVENT_BRANCH 0x80000000UL
#define PER_EVENT_IFETCH 0x40000000UL
#define PER_EVENT_STORE 0x20000000UL
#define PER_EVENT_STORE_REAL 0x08000000UL
+#define PER_EVENT_TRANSACTION_END 0x02000000UL
#define PER_EVENT_NULLIFICATION 0x01000000UL
-#define PER_CONTROL_MASK 0x00a00000UL
+#define PER_CONTROL_MASK 0x00e00000UL
#define PER_CONTROL_BRANCH_ADDRESS 0x00800000UL
+#define PER_CONTROL_SUSPENSION 0x00400000UL
#define PER_CONTROL_ALTERATION 0x00200000UL
#endif
@@ -483,6 +487,8 @@ typedef struct
#define PTRACE_GET_LAST_BREAK 0x5006
#define PTRACE_PEEK_SYSTEM_CALL 0x5007
#define PTRACE_POKE_SYSTEM_CALL 0x5008
+#define PTRACE_ENABLE_TE 0x5009
+#define PTRACE_DISABLE_TE 0x5010
/*
* PT_PROT definition is loosely based on hppa bsd definition in
diff --git a/arch/s390/include/asm/runtime_instr.h b/arch/s390/include/asm/runtime_instr.h
new file mode 100644
index 000000000000..830da737ff85
--- /dev/null
+++ b/arch/s390/include/asm/runtime_instr.h
@@ -0,0 +1,98 @@
+#ifndef _RUNTIME_INSTR_H
+#define _RUNTIME_INSTR_H
+
+#define S390_RUNTIME_INSTR_START 0x1
+#define S390_RUNTIME_INSTR_STOP 0x2
+
+struct runtime_instr_cb {
+ __u64 buf_current;
+ __u64 buf_origin;
+ __u64 buf_limit;
+
+ __u32 valid : 1;
+ __u32 pstate : 1;
+ __u32 pstate_set_buf : 1;
+ __u32 home_space : 1;
+ __u32 altered : 1;
+ __u32 : 3;
+ __u32 pstate_sample : 1;
+ __u32 sstate_sample : 1;
+ __u32 pstate_collect : 1;
+ __u32 sstate_collect : 1;
+ __u32 : 1;
+ __u32 halted_int : 1;
+ __u32 int_requested : 1;
+ __u32 buffer_full_int : 1;
+ __u32 key : 4;
+ __u32 : 9;
+ __u32 rgs : 3;
+
+ __u32 mode : 4;
+ __u32 next : 1;
+ __u32 mae : 1;
+ __u32 : 2;
+ __u32 call_type_br : 1;
+ __u32 return_type_br : 1;
+ __u32 other_type_br : 1;
+ __u32 bc_other_type : 1;
+ __u32 emit : 1;
+ __u32 tx_abort : 1;
+ __u32 : 2;
+ __u32 bp_xn : 1;
+ __u32 bp_xt : 1;
+ __u32 bp_ti : 1;
+ __u32 bp_ni : 1;
+ __u32 suppr_y : 1;
+ __u32 suppr_z : 1;
+
+ __u32 dc_miss_extra : 1;
+ __u32 lat_lev_ignore : 1;
+ __u32 ic_lat_lev : 4;
+ __u32 dc_lat_lev : 4;
+
+ __u64 reserved1;
+ __u64 scaling_factor;
+ __u64 rsic;
+ __u64 reserved2;
+} __packed __aligned(8);
+
+extern struct runtime_instr_cb runtime_instr_empty_cb;
+
+static inline void load_runtime_instr_cb(struct runtime_instr_cb *cb)
+{
+ asm volatile(".insn rsy,0xeb0000000060,0,0,%0" /* LRIC */
+ : : "Q" (*cb));
+}
+
+static inline void store_runtime_instr_cb(struct runtime_instr_cb *cb)
+{
+ asm volatile(".insn rsy,0xeb0000000061,0,0,%0" /* STRIC */
+ : "=Q" (*cb) : : "cc");
+}
+
+static inline void save_ri_cb(struct runtime_instr_cb *cb_prev)
+{
+#ifdef CONFIG_64BIT
+ if (cb_prev)
+ store_runtime_instr_cb(cb_prev);
+#endif
+}
+
+static inline void restore_ri_cb(struct runtime_instr_cb *cb_next,
+ struct runtime_instr_cb *cb_prev)
+{
+#ifdef CONFIG_64BIT
+ if (cb_next)
+ load_runtime_instr_cb(cb_next);
+ else if (cb_prev)
+ load_runtime_instr_cb(&runtime_instr_empty_cb);
+#endif
+}
+
+#ifdef CONFIG_64BIT
+extern void exit_thread_runtime_instr(void);
+#else
+static inline void exit_thread_runtime_instr(void) { }
+#endif
+
+#endif /* _RUNTIME_INSTR_H */
diff --git a/arch/s390/include/asm/scsw.h b/arch/s390/include/asm/scsw.h
index 4071d00978cb..4af99cdaddf5 100644
--- a/arch/s390/include/asm/scsw.h
+++ b/arch/s390/include/asm/scsw.h
@@ -1,7 +1,7 @@
/*
* Helper functions for scsw access.
*
- * Copyright IBM Corp. 2008, 2009
+ * Copyright IBM Corp. 2008, 2012
* Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
@@ -9,7 +9,7 @@
#define _ASM_S390_SCSW_H_
#include <linux/types.h>
-#include <asm/chsc.h>
+#include <asm/css_chars.h>
#include <asm/cio.h>
/**
@@ -100,14 +100,46 @@ struct tm_scsw {
} __attribute__ ((packed));
/**
+ * struct eadm_scsw - subchannel status word for eadm subchannels
+ * @key: subchannel key
+ * @eswf: esw format
+ * @cc: deferred condition code
+ * @ectl: extended control
+ * @fctl: function control
+ * @actl: activity control
+ * @stctl: status control
+ * @aob: AOB address
+ * @dstat: device status
+ * @cstat: subchannel status
+ */
+struct eadm_scsw {
+ u32 key:4;
+ u32:1;
+ u32 eswf:1;
+ u32 cc:2;
+ u32:6;
+ u32 ectl:1;
+ u32:2;
+ u32 fctl:3;
+ u32 actl:7;
+ u32 stctl:5;
+ u32 aob;
+ u32 dstat:8;
+ u32 cstat:8;
+ u32:16;
+} __packed;
+
+/**
* union scsw - subchannel status word
* @cmd: command-mode SCSW
* @tm: transport-mode SCSW
+ * @eadm: eadm SCSW
*/
union scsw {
struct cmd_scsw cmd;
struct tm_scsw tm;
-} __attribute__ ((packed));
+ struct eadm_scsw eadm;
+} __packed;
#define SCSW_FCTL_CLEAR_FUNC 0x1
#define SCSW_FCTL_HALT_FUNC 0x2
diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h
index e6859d16ee2d..87b47ca954f1 100644
--- a/arch/s390/include/asm/setup.h
+++ b/arch/s390/include/asm/setup.h
@@ -60,7 +60,7 @@ void create_mem_hole(struct mem_chunk memory_chunk[], unsigned long addr,
#define SECONDARY_SPACE_MODE 2
#define HOME_SPACE_MODE 3
-extern unsigned int addressing_mode;
+extern unsigned int s390_user_mode;
/*
* Machine features detected in head.S
@@ -80,6 +80,7 @@ extern unsigned int addressing_mode;
#define MACHINE_FLAG_LPAR (1UL << 12)
#define MACHINE_FLAG_SPP (1UL << 13)
#define MACHINE_FLAG_TOPOLOGY (1UL << 14)
+#define MACHINE_FLAG_TE (1UL << 15)
#define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM)
#define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM)
@@ -98,6 +99,7 @@ extern unsigned int addressing_mode;
#define MACHINE_HAS_PFMF (0)
#define MACHINE_HAS_SPP (0)
#define MACHINE_HAS_TOPOLOGY (0)
+#define MACHINE_HAS_TE (0)
#else /* CONFIG_64BIT */
#define MACHINE_HAS_IEEE (1)
#define MACHINE_HAS_CSP (1)
@@ -109,6 +111,7 @@ extern unsigned int addressing_mode;
#define MACHINE_HAS_PFMF (S390_lowcore.machine_flags & MACHINE_FLAG_PFMF)
#define MACHINE_HAS_SPP (S390_lowcore.machine_flags & MACHINE_FLAG_SPP)
#define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY)
+#define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE)
#endif /* CONFIG_64BIT */
#define ZFCPDUMP_HSA_SIZE (32UL<<20)
diff --git a/arch/s390/include/asm/smp.h b/arch/s390/include/asm/smp.h
index ce26ac3cb162..b64f15c3b4cc 100644
--- a/arch/s390/include/asm/smp.h
+++ b/arch/s390/include/asm/smp.h
@@ -30,6 +30,8 @@ extern int smp_vcpu_scheduled(int cpu);
extern void smp_yield_cpu(int cpu);
extern void smp_yield(void);
extern void smp_stop_cpu(void);
+extern void smp_cpu_set_polarization(int cpu, int val);
+extern int smp_cpu_get_polarization(int cpu);
#else /* CONFIG_SMP */
@@ -43,7 +45,7 @@ static inline void smp_call_online_cpu(void (*func)(void *), void *data)
func(data);
}
-static inline int smp_find_processor_id(int address) { return 0; }
+static inline int smp_find_processor_id(u16 address) { return 0; }
static inline int smp_store_status(int cpu) { return 0; }
static inline int smp_vcpu_scheduled(int cpu) { return 1; }
static inline void smp_yield_cpu(int cpu) { }
diff --git a/arch/s390/include/asm/string.h b/arch/s390/include/asm/string.h
index 1bd1352fa3b5..7e2dcd7c57ef 100644
--- a/arch/s390/include/asm/string.h
+++ b/arch/s390/include/asm/string.h
@@ -96,7 +96,6 @@ static inline char *strcat(char *dst, const char *src)
static inline char *strcpy(char *dst, const char *src)
{
-#if __GNUC__ < 4
register int r0 asm("0") = 0;
char *ret = dst;
@@ -106,14 +105,10 @@ static inline char *strcpy(char *dst, const char *src)
: "+&a" (dst), "+&a" (src) : "d" (r0)
: "cc", "memory");
return ret;
-#else
- return __builtin_strcpy(dst, src);
-#endif
}
static inline size_t strlen(const char *s)
{
-#if __GNUC__ < 4
register unsigned long r0 asm("0") = 0;
const char *tmp = s;
@@ -122,9 +117,6 @@ static inline size_t strlen(const char *s)
" jo 0b"
: "+d" (r0), "+a" (tmp) : : "cc");
return r0 - (unsigned long) s;
-#else
- return __builtin_strlen(s);
-#endif
}
static inline size_t strnlen(const char * s, size_t n)
diff --git a/arch/s390/include/asm/switch_to.h b/arch/s390/include/asm/switch_to.h
index f223068b7822..f3a9e0f92704 100644
--- a/arch/s390/include/asm/switch_to.h
+++ b/arch/s390/include/asm/switch_to.h
@@ -80,21 +80,19 @@ static inline void restore_access_regs(unsigned int *acrs)
if (prev->mm) { \
save_fp_regs(&prev->thread.fp_regs); \
save_access_regs(&prev->thread.acrs[0]); \
+ save_ri_cb(prev->thread.ri_cb); \
} \
if (next->mm) { \
restore_fp_regs(&next->thread.fp_regs); \
restore_access_regs(&next->thread.acrs[0]); \
+ restore_ri_cb(next->thread.ri_cb, prev->thread.ri_cb); \
update_per_regs(next); \
} \
prev = __switch_to(prev,next); \
} while (0)
-extern void account_vtime(struct task_struct *, struct task_struct *);
-extern void account_tick_vtime(struct task_struct *);
-
#define finish_arch_switch(prev) do { \
set_fs(current->thread.mm_segment); \
- account_vtime(prev, current); \
} while (0)
#endif /* __ASM_SWITCH_TO_H */
diff --git a/arch/s390/include/asm/sysinfo.h b/arch/s390/include/asm/sysinfo.h
index 282ee36f6162..f92428e459f8 100644
--- a/arch/s390/include/asm/sysinfo.h
+++ b/arch/s390/include/asm/sysinfo.h
@@ -17,7 +17,10 @@
#include <asm/bitsperlong.h>
struct sysinfo_1_1_1 {
- unsigned short :16;
+ unsigned char p:1;
+ unsigned char :6;
+ unsigned char t:1;
+ unsigned char :8;
unsigned char ccr;
unsigned char cai;
char reserved_0[28];
@@ -30,9 +33,14 @@ struct sysinfo_1_1_1 {
char model[16];
char model_perm_cap[16];
char model_temp_cap[16];
- char model_cap_rating[4];
- char model_perm_cap_rating[4];
- char model_temp_cap_rating[4];
+ unsigned int model_cap_rating;
+ unsigned int model_perm_cap_rating;
+ unsigned int model_temp_cap_rating;
+ unsigned char typepct[5];
+ unsigned char reserved_2[3];
+ unsigned int ncr;
+ unsigned int npr;
+ unsigned int ntr;
};
struct sysinfo_1_2_1 {
@@ -47,8 +55,9 @@ struct sysinfo_1_2_2 {
char format;
char reserved_0[1];
unsigned short acc_offset;
- char reserved_1[24];
- unsigned int secondary_capability;
+ char reserved_1[20];
+ unsigned int nominal_cap;
+ unsigned int secondary_cap;
unsigned int capability;
unsigned short cpus_total;
unsigned short cpus_configured;
@@ -109,6 +118,8 @@ struct sysinfo_3_2_2 {
char reserved_544[3552];
};
+extern int topology_max_mnest;
+
#define TOPOLOGY_CPU_BITS 64
#define TOPOLOGY_NR_MAG 6
@@ -142,21 +153,7 @@ struct sysinfo_15_1_x {
union topology_entry tle[0];
};
-static inline int stsi(void *sysinfo, int fc, int sel1, int sel2)
-{
- register int r0 asm("0") = (fc << 28) | sel1;
- register int r1 asm("1") = sel2;
-
- asm volatile(
- " stsi 0(%2)\n"
- "0: jz 2f\n"
- "1: lhi %0,%3\n"
- "2:\n"
- EX_TABLE(0b, 1b)
- : "+d" (r0) : "d" (r1), "a" (sysinfo), "K" (-ENOSYS)
- : "cc", "memory");
- return r0;
-}
+int stsi(void *sysinfo, int fc, int sel1, int sel2);
/*
* Service level reporting interface.
diff --git a/arch/s390/include/asm/tlbflush.h b/arch/s390/include/asm/tlbflush.h
index 9fde315f3a7c..1d8fe2b17ef6 100644
--- a/arch/s390/include/asm/tlbflush.h
+++ b/arch/s390/include/asm/tlbflush.h
@@ -90,12 +90,10 @@ static inline void __tlb_flush_mm(struct mm_struct * mm)
static inline void __tlb_flush_mm_cond(struct mm_struct * mm)
{
- spin_lock(&mm->page_table_lock);
if (mm->context.flush_mm) {
__tlb_flush_mm(mm);
mm->context.flush_mm = 0;
}
- spin_unlock(&mm->page_table_lock);
}
/*
diff --git a/arch/s390/include/asm/topology.h b/arch/s390/include/asm/topology.h
index 0837de80c351..9ca305383760 100644
--- a/arch/s390/include/asm/topology.h
+++ b/arch/s390/include/asm/topology.h
@@ -2,8 +2,8 @@
#define _ASM_S390_TOPOLOGY_H
#include <linux/cpumask.h>
-#include <asm/sysinfo.h>
+struct sysinfo_15_1_x;
struct cpu;
#ifdef CONFIG_SCHED_BOOK
@@ -51,24 +51,6 @@ static inline void topology_expect_change(void) { }
#define POLARIZATION_VM (2)
#define POLARIZATION_VH (3)
-extern int cpu_polarization[];
-
-static inline void cpu_set_polarization(int cpu, int val)
-{
-#ifdef CONFIG_SCHED_BOOK
- cpu_polarization[cpu] = val;
-#endif
-}
-
-static inline int cpu_read_polarization(int cpu)
-{
-#ifdef CONFIG_SCHED_BOOK
- return cpu_polarization[cpu];
-#else
- return POLARIZATION_HRZ;
-#endif
-}
-
#ifdef CONFIG_SCHED_BOOK
void s390_init_cpu_topology(void);
#else
diff --git a/arch/s390/include/asm/uaccess.h b/arch/s390/include/asm/uaccess.h
index a8ab18b18b54..34268df959a3 100644
--- a/arch/s390/include/asm/uaccess.h
+++ b/arch/s390/include/asm/uaccess.h
@@ -76,9 +76,22 @@ static inline int __range_ok(unsigned long addr, unsigned long size)
struct exception_table_entry
{
- unsigned long insn, fixup;
+ int insn, fixup;
};
+static inline unsigned long extable_insn(const struct exception_table_entry *x)
+{
+ return (unsigned long)&x->insn + x->insn;
+}
+
+static inline unsigned long extable_fixup(const struct exception_table_entry *x)
+{
+ return (unsigned long)&x->fixup + x->fixup;
+}
+
+#define ARCH_HAS_SORT_EXTABLE
+#define ARCH_HAS_SEARCH_EXTABLE
+
struct uaccess_ops {
size_t (*copy_from_user)(size_t, const void __user *, void *);
size_t (*copy_from_user_small)(size_t, const void __user *, void *);
diff --git a/arch/s390/include/asm/unistd.h b/arch/s390/include/asm/unistd.h
index 6756e78f4808..4e64b5cd1558 100644
--- a/arch/s390/include/asm/unistd.h
+++ b/arch/s390/include/asm/unistd.h
@@ -277,7 +277,9 @@
#define __NR_setns 339
#define __NR_process_vm_readv 340
#define __NR_process_vm_writev 341
-#define NR_syscalls 342
+#define __NR_s390_runtime_instr 342
+#define __NR_kcmp 343
+#define NR_syscalls 344
/*
* There are some system calls that are not present on 64 bit, some
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index 9733b3f0eb6d..4da52fe31743 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -23,10 +23,11 @@ CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w
obj-y := bitmap.o traps.o time.o process.o base.o early.o setup.o vtime.o \
processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o \
debug.o irq.o ipl.o dis.o diag.o mem_detect.o sclp.o vdso.o \
- sysinfo.o jump_label.o lgr.o os_info.o
+ sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o
obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o)
obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
+obj-y += $(if $(CONFIG_64BIT),relocate_kernel64.o,relocate_kernel.o)
extra-y += head.o vmlinux.lds
extra-y += $(if $(CONFIG_64BIT),head64.o,head31.o)
@@ -48,12 +49,11 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
-obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o
-# Kexec part
-S390_KEXEC_OBJS := machine_kexec.o crash.o
-S390_KEXEC_OBJS += $(if $(CONFIG_64BIT),relocate_kernel64.o,relocate_kernel.o)
-obj-$(CONFIG_KEXEC) += $(S390_KEXEC_OBJS)
+ifdef CONFIG_64BIT
+obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o
+obj-y += runtime_instr.o cache.o
+endif
# vdso
obj-$(CONFIG_64BIT) += vdso64/
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index 45ef1a7b08f9..fface87056eb 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -157,6 +157,8 @@ int main(void)
DEFINE(__LC_LAST_BREAK, offsetof(struct _lowcore, breaking_event_addr));
DEFINE(__LC_VDSO_PER_CPU, offsetof(struct _lowcore, vdso_per_cpu_data));
DEFINE(__LC_GMAP, offsetof(struct _lowcore, gmap));
+ DEFINE(__LC_PGM_TDB, offsetof(struct _lowcore, pgm_tdb));
+ DEFINE(__THREAD_trap_tdb, offsetof(struct task_struct, thread.trap_tdb));
DEFINE(__GMAP_ASCE, offsetof(struct gmap, asce));
#endif /* CONFIG_32BIT */
return 0;
diff --git a/arch/s390/kernel/cache.c b/arch/s390/kernel/cache.c
new file mode 100644
index 000000000000..8df8d8a19c98
--- /dev/null
+++ b/arch/s390/kernel/cache.c
@@ -0,0 +1,385 @@
+/*
+ * Extract CPU cache information and expose them via sysfs.
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
+ */
+
+#include <linux/notifier.h>
+#include <linux/seq_file.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <asm/facility.h>
+
+struct cache {
+ unsigned long size;
+ unsigned int line_size;
+ unsigned int associativity;
+ unsigned int nr_sets;
+ unsigned int level : 3;
+ unsigned int type : 2;
+ unsigned int private : 1;
+ struct list_head list;
+};
+
+struct cache_dir {
+ struct kobject *kobj;
+ struct cache_index_dir *index;
+};
+
+struct cache_index_dir {
+ struct kobject kobj;
+ int cpu;
+ struct cache *cache;
+ struct cache_index_dir *next;
+};
+
+enum {
+ CACHE_SCOPE_NOTEXISTS,
+ CACHE_SCOPE_PRIVATE,
+ CACHE_SCOPE_SHARED,
+ CACHE_SCOPE_RESERVED,
+};
+
+enum {
+ CACHE_TYPE_SEPARATE,
+ CACHE_TYPE_DATA,
+ CACHE_TYPE_INSTRUCTION,
+ CACHE_TYPE_UNIFIED,
+};
+
+enum {
+ EXTRACT_TOPOLOGY,
+ EXTRACT_LINE_SIZE,
+ EXTRACT_SIZE,
+ EXTRACT_ASSOCIATIVITY,
+};
+
+enum {
+ CACHE_TI_UNIFIED = 0,
+ CACHE_TI_INSTRUCTION = 0,
+ CACHE_TI_DATA,
+};
+
+struct cache_info {
+ unsigned char : 4;
+ unsigned char scope : 2;
+ unsigned char type : 2;
+};
+
+#define CACHE_MAX_LEVEL 8
+
+union cache_topology {
+ struct cache_info ci[CACHE_MAX_LEVEL];
+ unsigned long long raw;
+};
+
+static const char * const cache_type_string[] = {
+ "Data",
+ "Instruction",
+ "Unified",
+};
+
+static struct cache_dir *cache_dir_cpu[NR_CPUS];
+static LIST_HEAD(cache_list);
+
+void show_cacheinfo(struct seq_file *m)
+{
+ struct cache *cache;
+ int index = 0;
+
+ list_for_each_entry(cache, &cache_list, list) {
+ seq_printf(m, "cache%-11d: ", index);
+ seq_printf(m, "level=%d ", cache->level);
+ seq_printf(m, "type=%s ", cache_type_string[cache->type]);
+ seq_printf(m, "scope=%s ", cache->private ? "Private" : "Shared");
+ seq_printf(m, "size=%luK ", cache->size >> 10);
+ seq_printf(m, "line_size=%u ", cache->line_size);
+ seq_printf(m, "associativity=%d", cache->associativity);
+ seq_puts(m, "\n");
+ index++;
+ }
+}
+
+static inline unsigned long ecag(int ai, int li, int ti)
+{
+ unsigned long cmd, val;
+
+ cmd = ai << 4 | li << 1 | ti;
+ asm volatile(".insn rsy,0xeb000000004c,%0,0,0(%1)" /* ecag */
+ : "=d" (val) : "a" (cmd));
+ return val;
+}
+
+static int __init cache_add(int level, int private, int type)
+{
+ struct cache *cache;
+ int ti;
+
+ cache = kzalloc(sizeof(*cache), GFP_KERNEL);
+ if (!cache)
+ return -ENOMEM;
+ ti = type == CACHE_TYPE_DATA ? CACHE_TI_DATA : CACHE_TI_UNIFIED;
+ cache->size = ecag(EXTRACT_SIZE, level, ti);
+ cache->line_size = ecag(EXTRACT_LINE_SIZE, level, ti);
+ cache->associativity = ecag(EXTRACT_ASSOCIATIVITY, level, ti);
+ cache->nr_sets = cache->size / cache->associativity;
+ cache->nr_sets /= cache->line_size;
+ cache->private = private;
+ cache->level = level + 1;
+ cache->type = type - 1;
+ list_add_tail(&cache->list, &cache_list);
+ return 0;
+}
+
+static void __init cache_build_info(void)
+{
+ struct cache *cache, *next;
+ union cache_topology ct;
+ int level, private, rc;
+
+ ct.raw = ecag(EXTRACT_TOPOLOGY, 0, 0);
+ for (level = 0; level < CACHE_MAX_LEVEL; level++) {
+ switch (ct.ci[level].scope) {
+ case CACHE_SCOPE_NOTEXISTS:
+ case CACHE_SCOPE_RESERVED:
+ return;
+ case CACHE_SCOPE_SHARED:
+ private = 0;
+ break;
+ case CACHE_SCOPE_PRIVATE:
+ private = 1;
+ break;
+ }
+ if (ct.ci[level].type == CACHE_TYPE_SEPARATE) {
+ rc = cache_add(level, private, CACHE_TYPE_DATA);
+ rc |= cache_add(level, private, CACHE_TYPE_INSTRUCTION);
+ } else {
+ rc = cache_add(level, private, ct.ci[level].type);
+ }
+ if (rc)
+ goto error;
+ }
+ return;
+error:
+ list_for_each_entry_safe(cache, next, &cache_list, list) {
+ list_del(&cache->list);
+ kfree(cache);
+ }
+}
+
+static struct cache_dir *__cpuinit cache_create_cache_dir(int cpu)
+{
+ struct cache_dir *cache_dir;
+ struct kobject *kobj = NULL;
+ struct device *dev;
+
+ dev = get_cpu_device(cpu);
+ if (!dev)
+ goto out;
+ kobj = kobject_create_and_add("cache", &dev->kobj);
+ if (!kobj)
+ goto out;
+ cache_dir = kzalloc(sizeof(*cache_dir), GFP_KERNEL);
+ if (!cache_dir)
+ goto out;
+ cache_dir->kobj = kobj;
+ cache_dir_cpu[cpu] = cache_dir;
+ return cache_dir;
+out:
+ kobject_put(kobj);
+ return NULL;
+}
+
+static struct cache_index_dir *kobj_to_cache_index_dir(struct kobject *kobj)
+{
+ return container_of(kobj, struct cache_index_dir, kobj);
+}
+
+static void cache_index_release(struct kobject *kobj)
+{
+ struct cache_index_dir *index;
+
+ index = kobj_to_cache_index_dir(kobj);
+ kfree(index);
+}
+
+static ssize_t cache_index_show(struct kobject *kobj,
+ struct attribute *attr, char *buf)
+{
+ struct kobj_attribute *kobj_attr;
+
+ kobj_attr = container_of(attr, struct kobj_attribute, attr);
+ return kobj_attr->show(kobj, kobj_attr, buf);
+}
+
+#define DEFINE_CACHE_ATTR(_name, _format, _value) \
+static ssize_t cache_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, \
+ char *buf) \
+{ \
+ struct cache_index_dir *index; \
+ \
+ index = kobj_to_cache_index_dir(kobj); \
+ return sprintf(buf, _format, _value); \
+} \
+static struct kobj_attribute cache_##_name##_attr = \
+ __ATTR(_name, 0444, cache_##_name##_show, NULL);
+
+DEFINE_CACHE_ATTR(size, "%luK\n", index->cache->size >> 10);
+DEFINE_CACHE_ATTR(coherency_line_size, "%u\n", index->cache->line_size);
+DEFINE_CACHE_ATTR(number_of_sets, "%u\n", index->cache->nr_sets);
+DEFINE_CACHE_ATTR(ways_of_associativity, "%u\n", index->cache->associativity);
+DEFINE_CACHE_ATTR(type, "%s\n", cache_type_string[index->cache->type]);
+DEFINE_CACHE_ATTR(level, "%d\n", index->cache->level);
+
+static ssize_t shared_cpu_map_func(struct kobject *kobj, int type, char *buf)
+{
+ struct cache_index_dir *index;
+ int len;
+
+ index = kobj_to_cache_index_dir(kobj);
+ len = type ?
+ cpulist_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu)) :
+ cpumask_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu));
+ len += sprintf(&buf[len], "\n");
+ return len;
+}
+
+static ssize_t shared_cpu_map_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return shared_cpu_map_func(kobj, 0, buf);
+}
+static struct kobj_attribute cache_shared_cpu_map_attr =
+ __ATTR(shared_cpu_map, 0444, shared_cpu_map_show, NULL);
+
+static ssize_t shared_cpu_list_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return shared_cpu_map_func(kobj, 1, buf);
+}
+static struct kobj_attribute cache_shared_cpu_list_attr =
+ __ATTR(shared_cpu_list, 0444, shared_cpu_list_show, NULL);
+
+static struct attribute *cache_index_default_attrs[] = {
+ &cache_type_attr.attr,
+ &cache_size_attr.attr,
+ &cache_number_of_sets_attr.attr,
+ &cache_ways_of_associativity_attr.attr,
+ &cache_level_attr.attr,
+ &cache_coherency_line_size_attr.attr,
+ &cache_shared_cpu_map_attr.attr,
+ &cache_shared_cpu_list_attr.attr,
+ NULL,
+};
+
+static const struct sysfs_ops cache_index_ops = {
+ .show = cache_index_show,
+};
+
+static struct kobj_type cache_index_type = {
+ .sysfs_ops = &cache_index_ops,
+ .release = cache_index_release,
+ .default_attrs = cache_index_default_attrs,
+};
+
+static int __cpuinit cache_create_index_dir(struct cache_dir *cache_dir,
+ struct cache *cache, int index,
+ int cpu)
+{
+ struct cache_index_dir *index_dir;
+ int rc;
+
+ index_dir = kzalloc(sizeof(*index_dir), GFP_KERNEL);
+ if (!index_dir)
+ return -ENOMEM;
+ index_dir->cache = cache;
+ index_dir->cpu = cpu;
+ rc = kobject_init_and_add(&index_dir->kobj, &cache_index_type,
+ cache_dir->kobj, "index%d", index);
+ if (rc)
+ goto out;
+ index_dir->next = cache_dir->index;
+ cache_dir->index = index_dir;
+ return 0;
+out:
+ kfree(index_dir);
+ return rc;
+}
+
+static int __cpuinit cache_add_cpu(int cpu)
+{
+ struct cache_dir *cache_dir;
+ struct cache *cache;
+ int rc, index = 0;
+
+ if (list_empty(&cache_list))
+ return 0;
+ cache_dir = cache_create_cache_dir(cpu);
+ if (!cache_dir)
+ return -ENOMEM;
+ list_for_each_entry(cache, &cache_list, list) {
+ if (!cache->private)
+ break;
+ rc = cache_create_index_dir(cache_dir, cache, index, cpu);
+ if (rc)
+ return rc;
+ index++;
+ }
+ return 0;
+}
+
+static void __cpuinit cache_remove_cpu(int cpu)
+{
+ struct cache_index_dir *index, *next;
+ struct cache_dir *cache_dir;
+
+ cache_dir = cache_dir_cpu[cpu];
+ if (!cache_dir)
+ return;
+ index = cache_dir->index;
+ while (index) {
+ next = index->next;
+ kobject_put(&index->kobj);
+ index = next;
+ }
+ kobject_put(cache_dir->kobj);
+ kfree(cache_dir);
+ cache_dir_cpu[cpu] = NULL;
+}
+
+static int __cpuinit cache_hotplug(struct notifier_block *nfb,
+ unsigned long action, void *hcpu)
+{
+ int cpu = (long)hcpu;
+ int rc = 0;
+
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_ONLINE:
+ rc = cache_add_cpu(cpu);
+ if (rc)
+ cache_remove_cpu(cpu);
+ break;
+ case CPU_DEAD:
+ cache_remove_cpu(cpu);
+ break;
+ }
+ return rc ? NOTIFY_BAD : NOTIFY_OK;
+}
+
+static int __init cache_init(void)
+{
+ int cpu;
+
+ if (!test_facility(34))
+ return 0;
+ cache_build_info();
+ for_each_online_cpu(cpu)
+ cache_add_cpu(cpu);
+ hotcpu_notifier(cache_hotplug, 0);
+ return 0;
+}
+device_initcall(cache_init);
diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S
index 2d82cfcbce5b..3afba804fe97 100644
--- a/arch/s390/kernel/compat_wrapper.S
+++ b/arch/s390/kernel/compat_wrapper.S
@@ -1646,3 +1646,16 @@ ENTRY(compat_sys_process_vm_writev_wrapper)
llgf %r0,164(%r15) # unsigned long
stg %r0,160(%r15)
jg compat_sys_process_vm_writev
+
+ENTRY(sys_s390_runtime_instr_wrapper)
+ lgfr %r2,%r2 # int
+ lgfr %r3,%r3 # int
+ jg sys_s390_runtime_instr
+
+ENTRY(sys_kcmp_wrapper)
+ lgfr %r2,%r2 # pid_t
+ lgfr %r3,%r3 # pid_t
+ lgfr %r4,%r4 # int
+ llgfr %r5,%r5 # unsigned long
+ llgfr %r6,%r6 # unsigned long
+ jg sys_kcmp
diff --git a/arch/s390/kernel/crash.c b/arch/s390/kernel/crash.c
deleted file mode 100644
index 3819153de8bd..000000000000
--- a/arch/s390/kernel/crash.c
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright IBM Corp. 2005
- *
- * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
- *
- */
-
-#include <linux/threads.h>
-#include <linux/kexec.h>
-#include <linux/reboot.h>
-
-void machine_crash_shutdown(struct pt_regs *regs)
-{
-}
diff --git a/arch/s390/kernel/crash_dump.c b/arch/s390/kernel/crash_dump.c
index cc1172b26873..fb8d8781a011 100644
--- a/arch/s390/kernel/crash_dump.c
+++ b/arch/s390/kernel/crash_dump.c
@@ -13,8 +13,9 @@
#include <linux/slab.h>
#include <linux/bootmem.h>
#include <linux/elf.h>
-#include <asm/ipl.h>
#include <asm/os_info.h>
+#include <asm/elf.h>
+#include <asm/ipl.h>
#define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))
#define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
diff --git a/arch/s390/kernel/dis.c b/arch/s390/kernel/dis.c
index 619c5d350726..cc84a24c023f 100644
--- a/arch/s390/kernel/dis.c
+++ b/arch/s390/kernel/dis.c
@@ -315,6 +315,11 @@ enum {
LONG_INSN_POPCNT,
LONG_INSN_RISBHG,
LONG_INSN_RISBLG,
+ LONG_INSN_RINEXT,
+ LONG_INSN_RIEMIT,
+ LONG_INSN_TABORT,
+ LONG_INSN_TBEGIN,
+ LONG_INSN_TBEGINC,
};
static char *long_insn_name[] = {
@@ -329,7 +334,12 @@ static char *long_insn_name[] = {
[LONG_INSN_LLGHRL] = "llghrl",
[LONG_INSN_POPCNT] = "popcnt",
[LONG_INSN_RISBHG] = "risbhg",
- [LONG_INSN_RISBLG] = "risblk",
+ [LONG_INSN_RISBLG] = "risblg",
+ [LONG_INSN_RINEXT] = "rinext",
+ [LONG_INSN_RIEMIT] = "riemit",
+ [LONG_INSN_TABORT] = "tabort",
+ [LONG_INSN_TBEGIN] = "tbegin",
+ [LONG_INSN_TBEGINC] = "tbeginc",
};
static struct insn opcode[] = {
@@ -582,6 +592,17 @@ static struct insn opcode_a7[] = {
{ "", 0, INSTR_INVALID }
};
+static struct insn opcode_aa[] = {
+#ifdef CONFIG_64BIT
+ { { 0, LONG_INSN_RINEXT }, 0x00, INSTR_RI_RI },
+ { "rion", 0x01, INSTR_RI_RI },
+ { "tric", 0x02, INSTR_RI_RI },
+ { "rioff", 0x03, INSTR_RI_RI },
+ { { 0, LONG_INSN_RIEMIT }, 0x04, INSTR_RI_RI },
+#endif
+ { "", 0, INSTR_INVALID }
+};
+
static struct insn opcode_b2[] = {
#ifdef CONFIG_64BIT
{ "sske", 0x2b, INSTR_RRF_M0RR },
@@ -594,6 +615,9 @@ static struct insn opcode_b2[] = {
{ "lpswe", 0xb2, INSTR_S_RD },
{ "srnmt", 0xb9, INSTR_S_RD },
{ "lfas", 0xbd, INSTR_S_RD },
+ { "etndg", 0xec, INSTR_RRE_R0 },
+ { { 0, LONG_INSN_TABORT }, 0xfc, INSTR_S_RD },
+ { "tend", 0xf8, INSTR_S_RD },
#endif
{ "stidp", 0x02, INSTR_S_RD },
{ "sck", 0x04, INSTR_S_RD },
@@ -1150,6 +1174,7 @@ static struct insn opcode_e3[] = {
{ "stfh", 0xcb, INSTR_RXY_RRRD },
{ "chf", 0xcd, INSTR_RXY_RRRD },
{ "clhf", 0xcf, INSTR_RXY_RRRD },
+ { "ntstg", 0x25, INSTR_RXY_RRRD },
#endif
{ "lrv", 0x1e, INSTR_RXY_RRRD },
{ "lrvh", 0x1f, INSTR_RXY_RRRD },
@@ -1173,6 +1198,8 @@ static struct insn opcode_e5[] = {
{ "mvhhi", 0x44, INSTR_SIL_RDI },
{ "mvhi", 0x4c, INSTR_SIL_RDI },
{ "mvghi", 0x48, INSTR_SIL_RDI },
+ { { 0, LONG_INSN_TBEGIN }, 0x60, INSTR_SIL_RDU },
+ { { 0, LONG_INSN_TBEGINC }, 0x61, INSTR_SIL_RDU },
#endif
{ "lasp", 0x00, INSTR_SSE_RDRD },
{ "tprot", 0x01, INSTR_SSE_RDRD },
@@ -1210,6 +1237,9 @@ static struct insn opcode_eb[] = {
{ "cliy", 0x55, INSTR_SIY_URD },
{ "oiy", 0x56, INSTR_SIY_URD },
{ "xiy", 0x57, INSTR_SIY_URD },
+ { "lric", 0x60, INSTR_RSY_RDRM },
+ { "stric", 0x61, INSTR_RSY_RDRM },
+ { "mric", 0x62, INSTR_RSY_RDRM },
{ "icmh", 0x80, INSTR_RSE_RURD },
{ "icmh", 0x80, INSTR_RSY_RURD },
{ "icmy", 0x81, INSTR_RSY_RURD },
@@ -1408,6 +1438,9 @@ static struct insn *find_insn(unsigned char *code)
case 0xa7:
table = opcode_a7;
break;
+ case 0xaa:
+ table = opcode_aa;
+ break;
case 0xb2:
table = opcode_b2;
break;
@@ -1601,3 +1634,26 @@ void show_code(struct pt_regs *regs)
}
printk("\n");
}
+
+void print_fn_code(unsigned char *code, unsigned long len)
+{
+ char buffer[64], *ptr;
+ int opsize, i;
+
+ while (len) {
+ ptr = buffer;
+ opsize = insn_length(*code);
+ ptr += sprintf(ptr, "%p: ", code);
+ for (i = 0; i < opsize; i++)
+ ptr += sprintf(ptr, "%02x", code[i]);
+ *ptr++ = '\t';
+ if (i < 4)
+ *ptr++ = '\t';
+ ptr += print_insn(ptr, code, (unsigned long) code);
+ *ptr++ = '\n';
+ *ptr++ = 0;
+ printk(buffer);
+ code += opsize;
+ len -= opsize;
+ }
+}
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 83c3271c442b..7f4717675c19 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -215,36 +215,54 @@ static noinline __init void init_kernel_storage_key(void)
PAGE_DEFAULT_KEY, 0);
}
-static __initdata struct sysinfo_3_2_2 vmms __aligned(PAGE_SIZE);
+static __initdata char sysinfo_page[PAGE_SIZE] __aligned(PAGE_SIZE);
static noinline __init void detect_machine_type(void)
{
+ struct sysinfo_3_2_2 *vmms = (struct sysinfo_3_2_2 *)&sysinfo_page;
+
/* Check current-configuration-level */
- if ((stsi(NULL, 0, 0, 0) >> 28) <= 2) {
+ if (stsi(NULL, 0, 0, 0) <= 2) {
S390_lowcore.machine_flags |= MACHINE_FLAG_LPAR;
return;
}
/* Get virtual-machine cpu information. */
- if (stsi(&vmms, 3, 2, 2) == -ENOSYS || !vmms.count)
+ if (stsi(vmms, 3, 2, 2) || !vmms->count)
return;
/* Running under KVM? If not we assume z/VM */
- if (!memcmp(vmms.vm[0].cpi, "\xd2\xe5\xd4", 3))
+ if (!memcmp(vmms->vm[0].cpi, "\xd2\xe5\xd4", 3))
S390_lowcore.machine_flags |= MACHINE_FLAG_KVM;
else
S390_lowcore.machine_flags |= MACHINE_FLAG_VM;
}
+static __init void setup_topology(void)
+{
+#ifdef CONFIG_64BIT
+ int max_mnest;
+
+ if (!test_facility(11))
+ return;
+ S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY;
+ for (max_mnest = 6; max_mnest > 1; max_mnest--) {
+ if (stsi(&sysinfo_page, 15, 1, max_mnest) == 0)
+ break;
+ }
+ topology_max_mnest = max_mnest;
+#endif
+}
+
static void early_pgm_check_handler(void)
{
- unsigned long addr;
const struct exception_table_entry *fixup;
+ unsigned long addr;
addr = S390_lowcore.program_old_psw.addr;
fixup = search_exception_tables(addr & PSW_ADDR_INSN);
if (!fixup)
disabled_wait(0);
- S390_lowcore.program_old_psw.addr = fixup->fixup | PSW_ADDR_AMODE;
+ S390_lowcore.program_old_psw.addr = extable_fixup(fixup)|PSW_ADDR_AMODE;
}
static noinline __init void setup_lowcore_early(void)
@@ -267,12 +285,10 @@ static noinline __init void setup_facility_list(void)
static noinline __init void setup_hpage(void)
{
-#ifndef CONFIG_DEBUG_PAGEALLOC
if (!test_facility(2) || !test_facility(8))
return;
S390_lowcore.machine_flags |= MACHINE_FLAG_HPAGE;
__ctl_set_bit(0, 23);
-#endif
}
static __init void detect_mvpg(void)
@@ -366,12 +382,12 @@ static __init void detect_machine_facilities(void)
S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE;
if (test_facility(8))
S390_lowcore.machine_flags |= MACHINE_FLAG_PFMF;
- if (test_facility(11))
- S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY;
if (test_facility(27))
S390_lowcore.machine_flags |= MACHINE_FLAG_MVCOS;
if (test_facility(40))
S390_lowcore.machine_flags |= MACHINE_FLAG_SPP;
+ if (test_facility(50) && test_facility(73))
+ S390_lowcore.machine_flags |= MACHINE_FLAG_TE;
#endif
}
@@ -441,7 +457,6 @@ static void __init setup_boot_command_line(void)
append_to_cmdline(append_ipl_scpdata);
}
-
/*
* Save ipl parameters, clear bss memory, initialize storage keys
* and create a kernel NSS at startup if the SAVESYS= parm is defined
@@ -468,6 +483,7 @@ void __init startup_init(void)
detect_diag44();
detect_machine_facilities();
setup_hpage();
+ setup_topology();
sclp_facilities_detect();
detect_memory_layout(memory_chunk);
#ifdef CONFIG_DYNAMIC_FTRACE
diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S
index 349b7eeb348a..7549985402f7 100644
--- a/arch/s390/kernel/entry64.S
+++ b/arch/s390/kernel/entry64.S
@@ -10,6 +10,7 @@
#include <linux/init.h>
#include <linux/linkage.h>
+#include <asm/processor.h>
#include <asm/cache.h>
#include <asm/errno.h>
#include <asm/ptrace.h>
@@ -412,6 +413,11 @@ ENTRY(pgm_check_handler)
1: UPDATE_VTIME %r14,__LC_SYNC_ENTER_TIMER
LAST_BREAK %r14
lg %r15,__LC_KERNEL_STACK
+ lg %r14,__TI_task(%r12)
+ lghi %r13,__LC_PGM_TDB
+ tm __LC_PGM_ILC+2,0x02 # check for transaction abort
+ jz 2f
+ mvc __THREAD_trap_tdb(256,%r14),0(%r13)
2: aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
la %r11,STACK_FRAME_OVERHEAD(%r15)
stmg %r0,%r7,__PT_R0(%r11)
@@ -422,13 +428,12 @@ ENTRY(pgm_check_handler)
stg %r10,__PT_ARGS(%r11)
tm __LC_PGM_ILC+3,0x80 # check for per exception
jz 0f
- lg %r1,__TI_task(%r12)
tmhh %r8,0x0001 # kernel per event ?
jz pgm_kprobe
oi __TI_flags+7(%r12),_TIF_PER_TRAP
- mvc __THREAD_per_address(8,%r1),__LC_PER_ADDRESS
- mvc __THREAD_per_cause(2,%r1),__LC_PER_CAUSE
- mvc __THREAD_per_paid(1,%r1),__LC_PER_PAID
+ mvc __THREAD_per_address(8,%r14),__LC_PER_ADDRESS
+ mvc __THREAD_per_cause(2,%r14),__LC_PER_CAUSE
+ mvc __THREAD_per_paid(1,%r14),__LC_PER_PAID
0: REENABLE_IRQS
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
larl %r1,pgm_check_table
@@ -1004,9 +1009,7 @@ sie_fault:
.Lhost_id:
.quad 0
- .section __ex_table,"a"
- .quad sie_loop,sie_fault
- .previous
+ EX_TABLE(sie_loop,sie_fault)
#endif
.section .rodata, "a"
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
index dd7630d8aab7..6cdc55b26d68 100644
--- a/arch/s390/kernel/irq.c
+++ b/arch/s390/kernel/irq.c
@@ -30,33 +30,35 @@ struct irq_class {
};
static const struct irq_class intrclass_names[] = {
- {.name = "EXT" },
- {.name = "I/O" },
- {.name = "CLK", .desc = "[EXT] Clock Comparator" },
- {.name = "EXC", .desc = "[EXT] External Call" },
- {.name = "EMS", .desc = "[EXT] Emergency Signal" },
- {.name = "TMR", .desc = "[EXT] CPU Timer" },
- {.name = "TAL", .desc = "[EXT] Timing Alert" },
- {.name = "PFL", .desc = "[EXT] Pseudo Page Fault" },
- {.name = "DSD", .desc = "[EXT] DASD Diag" },
- {.name = "VRT", .desc = "[EXT] Virtio" },
- {.name = "SCP", .desc = "[EXT] Service Call" },
- {.name = "IUC", .desc = "[EXT] IUCV" },
- {.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling" },
- {.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter" },
- {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt" },
- {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" },
- {.name = "DAS", .desc = "[I/O] DASD" },
- {.name = "C15", .desc = "[I/O] 3215" },
- {.name = "C70", .desc = "[I/O] 3270" },
- {.name = "TAP", .desc = "[I/O] Tape" },
- {.name = "VMR", .desc = "[I/O] Unit Record Devices" },
- {.name = "LCS", .desc = "[I/O] LCS" },
- {.name = "CLW", .desc = "[I/O] CLAW" },
- {.name = "CTC", .desc = "[I/O] CTC" },
- {.name = "APB", .desc = "[I/O] AP Bus" },
- {.name = "CSC", .desc = "[I/O] CHSC Subchannel" },
- {.name = "NMI", .desc = "[NMI] Machine Check" },
+ [EXTERNAL_INTERRUPT] = {.name = "EXT"},
+ [IO_INTERRUPT] = {.name = "I/O"},
+ [EXTINT_CLK] = {.name = "CLK", .desc = "[EXT] Clock Comparator"},
+ [EXTINT_EXC] = {.name = "EXC", .desc = "[EXT] External Call"},
+ [EXTINT_EMS] = {.name = "EMS", .desc = "[EXT] Emergency Signal"},
+ [EXTINT_TMR] = {.name = "TMR", .desc = "[EXT] CPU Timer"},
+ [EXTINT_TLA] = {.name = "TAL", .desc = "[EXT] Timing Alert"},
+ [EXTINT_PFL] = {.name = "PFL", .desc = "[EXT] Pseudo Page Fault"},
+ [EXTINT_DSD] = {.name = "DSD", .desc = "[EXT] DASD Diag"},
+ [EXTINT_VRT] = {.name = "VRT", .desc = "[EXT] Virtio"},
+ [EXTINT_SCP] = {.name = "SCP", .desc = "[EXT] Service Call"},
+ [EXTINT_IUC] = {.name = "IUC", .desc = "[EXT] IUCV"},
+ [EXTINT_CMS] = {.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"},
+ [EXTINT_CMC] = {.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"},
+ [EXTINT_CMR] = {.name = "CMR", .desc = "[EXT] CPU-Measurement: RI"},
+ [IOINT_CIO] = {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"},
+ [IOINT_QAI] = {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"},
+ [IOINT_DAS] = {.name = "DAS", .desc = "[I/O] DASD"},
+ [IOINT_C15] = {.name = "C15", .desc = "[I/O] 3215"},
+ [IOINT_C70] = {.name = "C70", .desc = "[I/O] 3270"},
+ [IOINT_TAP] = {.name = "TAP", .desc = "[I/O] Tape"},
+ [IOINT_VMR] = {.name = "VMR", .desc = "[I/O] Unit Record Devices"},
+ [IOINT_LCS] = {.name = "LCS", .desc = "[I/O] LCS"},
+ [IOINT_CLW] = {.name = "CLW", .desc = "[I/O] CLAW"},
+ [IOINT_CTC] = {.name = "CTC", .desc = "[I/O] CTC"},
+ [IOINT_APB] = {.name = "APB", .desc = "[I/O] AP Bus"},
+ [IOINT_ADM] = {.name = "ADM", .desc = "[I/O] EADM Subchannel"},
+ [IOINT_CSC] = {.name = "CSC", .desc = "[I/O] CHSC Subchannel"},
+ [NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"},
};
/*
diff --git a/arch/s390/kernel/kprobes.c b/arch/s390/kernel/kprobes.c
index 8aa634f5944b..d1c7214e157c 100644
--- a/arch/s390/kernel/kprobes.c
+++ b/arch/s390/kernel/kprobes.c
@@ -547,7 +547,7 @@ static int __kprobes kprobe_trap_handler(struct pt_regs *regs, int trapnr)
*/
entry = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
if (entry) {
- regs->psw.addr = entry->fixup | PSW_ADDR_AMODE;
+ regs->psw.addr = extable_fixup(entry) | PSW_ADDR_AMODE;
return 1;
}
diff --git a/arch/s390/kernel/lgr.c b/arch/s390/kernel/lgr.c
index eca94e74d19a..6ea6d69339b5 100644
--- a/arch/s390/kernel/lgr.c
+++ b/arch/s390/kernel/lgr.c
@@ -51,16 +51,6 @@ static struct lgr_info lgr_info_cur;
static struct debug_info *lgr_dbf;
/*
- * Return number of valid stsi levels
- */
-static inline int stsi_0(void)
-{
- int rc = stsi(NULL, 0, 0, 0);
-
- return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
-}
-
-/*
* Copy buffer and then convert it to ASCII
*/
static void cpascii(char *dst, char *src, int size)
@@ -76,7 +66,7 @@ static void lgr_stsi_1_1_1(struct lgr_info *lgr_info)
{
struct sysinfo_1_1_1 *si = (void *) lgr_page;
- if (stsi(si, 1, 1, 1) == -ENOSYS)
+ if (stsi(si, 1, 1, 1))
return;
cpascii(lgr_info->manufacturer, si->manufacturer,
sizeof(si->manufacturer));
@@ -93,7 +83,7 @@ static void lgr_stsi_2_2_2(struct lgr_info *lgr_info)
{
struct sysinfo_2_2_2 *si = (void *) lgr_page;
- if (stsi(si, 2, 2, 2) == -ENOSYS)
+ if (stsi(si, 2, 2, 2))
return;
cpascii(lgr_info->name, si->name, sizeof(si->name));
memcpy(&lgr_info->lpar_number, &si->lpar_number,
@@ -108,7 +98,7 @@ static void lgr_stsi_3_2_2(struct lgr_info *lgr_info)
struct sysinfo_3_2_2 *si = (void *) lgr_page;
int i;
- if (stsi(si, 3, 2, 2) == -ENOSYS)
+ if (stsi(si, 3, 2, 2))
return;
for (i = 0; i < min_t(u8, si->count, VM_LEVEL_MAX); i++) {
cpascii(lgr_info->vm[i].name, si->vm[i].name,
@@ -124,16 +114,17 @@ static void lgr_stsi_3_2_2(struct lgr_info *lgr_info)
*/
static void lgr_info_get(struct lgr_info *lgr_info)
{
+ int level;
+
memset(lgr_info, 0, sizeof(*lgr_info));
stfle(lgr_info->stfle_fac_list, ARRAY_SIZE(lgr_info->stfle_fac_list));
- lgr_info->level = stsi_0();
- if (lgr_info->level == -ENOSYS)
- return;
- if (lgr_info->level >= 1)
+ level = stsi(NULL, 0, 0, 0);
+ lgr_info->level = level;
+ if (level >= 1)
lgr_stsi_1_1_1(lgr_info);
- if (lgr_info->level >= 2)
+ if (level >= 2)
lgr_stsi_2_2_2(lgr_info);
- if (lgr_info->level >= 3)
+ if (level >= 3)
lgr_stsi_3_2_2(lgr_info);
}
diff --git a/arch/s390/kernel/machine_kexec.c b/arch/s390/kernel/machine_kexec.c
index 493304bdf1c7..b3de27700016 100644
--- a/arch/s390/kernel/machine_kexec.c
+++ b/arch/s390/kernel/machine_kexec.c
@@ -21,6 +21,7 @@
#include <asm/reset.h>
#include <asm/ipl.h>
#include <asm/diag.h>
+#include <asm/elf.h>
#include <asm/asm-offsets.h>
#include <asm/os_info.h>
@@ -31,8 +32,6 @@ extern const unsigned long long relocate_kernel_len;
#ifdef CONFIG_CRASH_DUMP
-void *fill_cpu_elf_notes(void *ptr, struct save_area *sa);
-
/*
* Create ELF notes for one CPU
*/
@@ -159,7 +158,7 @@ int machine_kexec_prepare(struct kimage *image)
/* Can't replace kernel image since it is read-only. */
if (ipl_flags & IPL_NSS_VALID)
- return -ENOSYS;
+ return -EOPNOTSUPP;
if (image->type == KEXEC_TYPE_CRASH)
return machine_kexec_prepare_kdump();
@@ -191,6 +190,10 @@ void machine_shutdown(void)
{
}
+void machine_crash_shutdown(struct pt_regs *regs)
+{
+}
+
/*
* Do normal kexec
*/
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c
index 733175373a4c..5024be27df44 100644
--- a/arch/s390/kernel/process.c
+++ b/arch/s390/kernel/process.c
@@ -26,10 +26,12 @@
#include <asm/io.h>
#include <asm/processor.h>
#include <asm/vtimer.h>
+#include <asm/exec.h>
#include <asm/irq.h>
#include <asm/nmi.h>
#include <asm/smp.h>
#include <asm/switch_to.h>
+#include <asm/runtime_instr.h>
#include "entry.h"
asmlinkage void ret_from_fork(void) asm ("ret_from_fork");
@@ -132,6 +134,7 @@ EXPORT_SYMBOL(kernel_thread);
*/
void exit_thread(void)
{
+ exit_thread_runtime_instr();
}
void flush_thread(void)
@@ -170,6 +173,11 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
/* Save access registers to new thread structure. */
save_access_regs(&p->thread.acrs[0]);
+ /* Don't copy runtime instrumentation info */
+ p->thread.ri_cb = NULL;
+ p->thread.ri_signum = 0;
+ frame->childregs.psw.mask &= ~PSW_MASK_RI;
+
#ifndef CONFIG_64BIT
/*
* save fprs to current->thread.fp_regs to merge them with
diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c
index 572d4c9cb33b..753c41d0ffd3 100644
--- a/arch/s390/kernel/processor.c
+++ b/arch/s390/kernel/processor.c
@@ -39,9 +39,9 @@ void __cpuinit cpu_init(void)
*/
static int show_cpuinfo(struct seq_file *m, void *v)
{
- static const char *hwcap_str[10] = {
+ static const char *hwcap_str[] = {
"esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp",
- "edat", "etf3eh", "highgprs"
+ "edat", "etf3eh", "highgprs", "te"
};
unsigned long n = (unsigned long) v - 1;
int i;
@@ -54,10 +54,11 @@ static int show_cpuinfo(struct seq_file *m, void *v)
num_online_cpus(), loops_per_jiffy/(500000/HZ),
(loops_per_jiffy/(5000/HZ))%100);
seq_puts(m, "features\t: ");
- for (i = 0; i < 10; i++)
+ for (i = 0; i < ARRAY_SIZE(hwcap_str); i++)
if (hwcap_str[i] && (elf_hwcap & (1UL << i)))
seq_printf(m, "%s ", hwcap_str[i]);
seq_puts(m, "\n");
+ show_cacheinfo(m);
}
get_online_cpus();
if (cpu_online(n)) {
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index e4be113fbac6..a314c57f4e94 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -42,6 +42,7 @@ enum s390_regset {
REGSET_GENERAL,
REGSET_FP,
REGSET_LAST_BREAK,
+ REGSET_TDB,
REGSET_SYSTEM_CALL,
REGSET_GENERAL_EXTENDED,
};
@@ -52,6 +53,22 @@ void update_per_regs(struct task_struct *task)
struct thread_struct *thread = &task->thread;
struct per_regs old, new;
+#ifdef CONFIG_64BIT
+ /* Take care of the enable/disable of transactional execution. */
+ if (MACHINE_HAS_TE) {
+ unsigned long cr0, cr0_new;
+
+ __ctl_store(cr0, 0, 0);
+ /* set or clear transaction execution bits 8 and 9. */
+ if (task->thread.per_flags & PER_FLAG_NO_TE)
+ cr0_new = cr0 & ~(3UL << 54);
+ else
+ cr0_new = cr0 | (3UL << 54);
+ /* Only load control register 0 if necessary. */
+ if (cr0 != cr0_new)
+ __ctl_load(cr0_new, 0, 0);
+ }
+#endif
/* Copy user specified PER registers */
new.control = thread->per_user.control;
new.start = thread->per_user.start;
@@ -60,6 +77,10 @@ void update_per_regs(struct task_struct *task)
/* merge TIF_SINGLE_STEP into user specified PER registers. */
if (test_tsk_thread_flag(task, TIF_SINGLE_STEP)) {
new.control |= PER_EVENT_IFETCH;
+#ifdef CONFIG_64BIT
+ new.control |= PER_CONTROL_SUSPENSION;
+ new.control |= PER_EVENT_TRANSACTION_END;
+#endif
new.start = 0;
new.end = PSW_ADDR_INSN;
}
@@ -100,6 +121,7 @@ void ptrace_disable(struct task_struct *task)
memset(&task->thread.per_event, 0, sizeof(task->thread.per_event));
clear_tsk_thread_flag(task, TIF_SINGLE_STEP);
clear_tsk_thread_flag(task, TIF_PER_TRAP);
+ task->thread.per_flags = 0;
}
#ifndef CONFIG_64BIT
@@ -416,6 +438,16 @@ long arch_ptrace(struct task_struct *child, long request,
put_user(task_thread_info(child)->last_break,
(unsigned long __user *) data);
return 0;
+ case PTRACE_ENABLE_TE:
+ if (!MACHINE_HAS_TE)
+ return -EIO;
+ child->thread.per_flags &= ~PER_FLAG_NO_TE;
+ return 0;
+ case PTRACE_DISABLE_TE:
+ if (!MACHINE_HAS_TE)
+ return -EIO;
+ child->thread.per_flags |= PER_FLAG_NO_TE;
+ return 0;
default:
/* Removing high order bit from addr (only for 31 bit). */
addr &= PSW_ADDR_INSN;
@@ -903,6 +935,28 @@ static int s390_last_break_set(struct task_struct *target,
return 0;
}
+static int s390_tdb_get(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ void *kbuf, void __user *ubuf)
+{
+ struct pt_regs *regs = task_pt_regs(target);
+ unsigned char *data;
+
+ if (!(regs->int_code & 0x200))
+ return -ENODATA;
+ data = target->thread.trap_tdb;
+ return user_regset_copyout(&pos, &count, &kbuf, &ubuf, data, 0, 256);
+}
+
+static int s390_tdb_set(struct task_struct *target,
+ const struct user_regset *regset,
+ unsigned int pos, unsigned int count,
+ const void *kbuf, const void __user *ubuf)
+{
+ return 0;
+}
+
#endif
static int s390_system_call_get(struct task_struct *target,
@@ -951,6 +1005,14 @@ static const struct user_regset s390_regsets[] = {
.get = s390_last_break_get,
.set = s390_last_break_set,
},
+ [REGSET_TDB] = {
+ .core_note_type = NT_S390_TDB,
+ .n = 1,
+ .size = 256,
+ .align = 1,
+ .get = s390_tdb_get,
+ .set = s390_tdb_set,
+ },
#endif
[REGSET_SYSTEM_CALL] = {
.core_note_type = NT_S390_SYSTEM_CALL,
@@ -1148,6 +1210,14 @@ static const struct user_regset s390_compat_regsets[] = {
.get = s390_compat_last_break_get,
.set = s390_compat_last_break_set,
},
+ [REGSET_TDB] = {
+ .core_note_type = NT_S390_TDB,
+ .n = 1,
+ .size = 256,
+ .align = 1,
+ .get = s390_tdb_get,
+ .set = s390_tdb_set,
+ },
[REGSET_SYSTEM_CALL] = {
.core_note_type = NT_S390_SYSTEM_CALL,
.n = 1,
diff --git a/arch/s390/kernel/runtime_instr.c b/arch/s390/kernel/runtime_instr.c
new file mode 100644
index 000000000000..61066f6f71a5
--- /dev/null
+++ b/arch/s390/kernel/runtime_instr.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright IBM Corp. 2012
+ * Author(s): Jan Glauber <jang@linux.vnet.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/syscalls.h>
+#include <linux/signal.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/kernel_stat.h>
+#include <asm/runtime_instr.h>
+#include <asm/cpu_mf.h>
+#include <asm/irq.h>
+
+/* empty control block to disable RI by loading it */
+struct runtime_instr_cb runtime_instr_empty_cb;
+
+static int runtime_instr_avail(void)
+{
+ return test_facility(64);
+}
+
+static void disable_runtime_instr(void)
+{
+ struct pt_regs *regs = task_pt_regs(current);
+
+ load_runtime_instr_cb(&runtime_instr_empty_cb);
+
+ /*
+ * Make sure the RI bit is deleted from the PSW. If the user did not
+ * switch off RI before the system call the process will get a
+ * specification exception otherwise.
+ */
+ regs->psw.mask &= ~PSW_MASK_RI;
+}
+
+static void init_runtime_instr_cb(struct runtime_instr_cb *cb)
+{
+ cb->buf_limit = 0xfff;
+ if (s390_user_mode == HOME_SPACE_MODE)
+ cb->home_space = 1;
+ cb->int_requested = 1;
+ cb->pstate = 1;
+ cb->pstate_set_buf = 1;
+ cb->pstate_sample = 1;
+ cb->pstate_collect = 1;
+ cb->key = PAGE_DEFAULT_KEY;
+ cb->valid = 1;
+}
+
+void exit_thread_runtime_instr(void)
+{
+ struct task_struct *task = current;
+
+ if (!task->thread.ri_cb)
+ return;
+ disable_runtime_instr();
+ kfree(task->thread.ri_cb);
+ task->thread.ri_signum = 0;
+ task->thread.ri_cb = NULL;
+}
+
+static void runtime_instr_int_handler(struct ext_code ext_code,
+ unsigned int param32, unsigned long param64)
+{
+ struct siginfo info;
+
+ if (!(param32 & CPU_MF_INT_RI_MASK))
+ return;
+
+ kstat_cpu(smp_processor_id()).irqs[EXTINT_CMR]++;
+
+ if (!current->thread.ri_cb)
+ return;
+ if (current->thread.ri_signum < SIGRTMIN ||
+ current->thread.ri_signum > SIGRTMAX) {
+ WARN_ON_ONCE(1);
+ return;
+ }
+
+ memset(&info, 0, sizeof(info));
+ info.si_signo = current->thread.ri_signum;
+ info.si_code = SI_QUEUE;
+ if (param32 & CPU_MF_INT_RI_BUF_FULL)
+ info.si_int = ENOBUFS;
+ else if (param32 & CPU_MF_INT_RI_HALTED)
+ info.si_int = ECANCELED;
+ else
+ return; /* unknown reason */
+
+ send_sig_info(current->thread.ri_signum, &info, current);
+}
+
+SYSCALL_DEFINE2(s390_runtime_instr, int, command, int, signum)
+{
+ struct runtime_instr_cb *cb;
+
+ if (!runtime_instr_avail())
+ return -EOPNOTSUPP;
+
+ if (command == S390_RUNTIME_INSTR_STOP) {
+ preempt_disable();
+ exit_thread_runtime_instr();
+ preempt_enable();
+ return 0;
+ }
+
+ if (command != S390_RUNTIME_INSTR_START ||
+ (signum < SIGRTMIN || signum > SIGRTMAX))
+ return -EINVAL;
+
+ if (!current->thread.ri_cb) {
+ cb = kzalloc(sizeof(*cb), GFP_KERNEL);
+ if (!cb)
+ return -ENOMEM;
+ } else {
+ cb = current->thread.ri_cb;
+ memset(cb, 0, sizeof(*cb));
+ }
+
+ init_runtime_instr_cb(cb);
+ current->thread.ri_signum = signum;
+
+ /* now load the control block to make it available */
+ preempt_disable();
+ current->thread.ri_cb = cb;
+ load_runtime_instr_cb(cb);
+ preempt_enable();
+ return 0;
+}
+
+static int __init runtime_instr_init(void)
+{
+ int rc;
+
+ if (!runtime_instr_avail())
+ return 0;
+
+ measurement_alert_subclass_register();
+ rc = register_external_interrupt(0x1407, runtime_instr_int_handler);
+ if (rc)
+ measurement_alert_subclass_unregister();
+ else
+ pr_info("Runtime instrumentation facility initialized\n");
+ return rc;
+}
+device_initcall(runtime_instr_init);
diff --git a/arch/s390/kernel/s390_ksyms.c b/arch/s390/kernel/s390_ksyms.c
index 57b536649b00..9bdbcef1da9e 100644
--- a/arch/s390/kernel/s390_ksyms.c
+++ b/arch/s390/kernel/s390_ksyms.c
@@ -8,3 +8,5 @@ EXPORT_SYMBOL(_mcount);
#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
EXPORT_SYMBOL(sie64a);
#endif
+EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(memset);
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index f86c81e13c37..afa9fdba200e 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -302,10 +302,10 @@ static int __init parse_vmalloc(char *arg)
}
early_param("vmalloc", parse_vmalloc);
-unsigned int addressing_mode = HOME_SPACE_MODE;
-EXPORT_SYMBOL_GPL(addressing_mode);
+unsigned int s390_user_mode = PRIMARY_SPACE_MODE;
+EXPORT_SYMBOL_GPL(s390_user_mode);
-static int set_amode_primary(void)
+static void __init set_user_mode_primary(void)
{
psw_kernel_bits = (psw_kernel_bits & ~PSW_MASK_ASC) | PSW_ASC_HOME;
psw_user_bits = (psw_user_bits & ~PSW_MASK_ASC) | PSW_ASC_PRIMARY;
@@ -313,48 +313,30 @@ static int set_amode_primary(void)
psw32_user_bits =
(psw32_user_bits & ~PSW32_MASK_ASC) | PSW32_ASC_PRIMARY;
#endif
-
- if (MACHINE_HAS_MVCOS) {
- memcpy(&uaccess, &uaccess_mvcos_switch, sizeof(uaccess));
- return 1;
- } else {
- memcpy(&uaccess, &uaccess_pt, sizeof(uaccess));
- return 0;
- }
-}
-
-/*
- * Switch kernel/user addressing modes?
- */
-static int __init early_parse_switch_amode(char *p)
-{
- addressing_mode = PRIMARY_SPACE_MODE;
- return 0;
+ uaccess = MACHINE_HAS_MVCOS ? uaccess_mvcos_switch : uaccess_pt;
}
-early_param("switch_amode", early_parse_switch_amode);
static int __init early_parse_user_mode(char *p)
{
if (p && strcmp(p, "primary") == 0)
- addressing_mode = PRIMARY_SPACE_MODE;
+ s390_user_mode = PRIMARY_SPACE_MODE;
else if (!p || strcmp(p, "home") == 0)
- addressing_mode = HOME_SPACE_MODE;
+ s390_user_mode = HOME_SPACE_MODE;
else
return 1;
return 0;
}
early_param("user_mode", early_parse_user_mode);
-static void setup_addressing_mode(void)
+static void __init setup_addressing_mode(void)
{
- if (addressing_mode == PRIMARY_SPACE_MODE) {
- if (set_amode_primary())
- pr_info("Address spaces switched, "
- "mvcos available\n");
- else
- pr_info("Address spaces switched, "
- "mvcos not available\n");
- }
+ if (s390_user_mode != PRIMARY_SPACE_MODE)
+ return;
+ set_user_mode_primary();
+ if (MACHINE_HAS_MVCOS)
+ pr_info("Address spaces switched, mvcos available\n");
+ else
+ pr_info("Address spaces switched, mvcos not available\n");
}
void *restart_stack __attribute__((__section__(".data")));
@@ -602,9 +584,7 @@ static void __init setup_memory_end(void)
static void __init setup_vmcoreinfo(void)
{
-#ifdef CONFIG_KEXEC
mem_assign_absolute(S390_lowcore.vmcore_info, paddr_vmcoreinfo_note());
-#endif
}
#ifdef CONFIG_CRASH_DUMP
@@ -974,12 +954,20 @@ static void __init setup_hwcaps(void)
if (MACHINE_HAS_HPAGE)
elf_hwcap |= HWCAP_S390_HPAGE;
+#if defined(CONFIG_64BIT)
/*
* 64-bit register support for 31-bit processes
* HWCAP_S390_HIGH_GPRS is bit 9.
*/
elf_hwcap |= HWCAP_S390_HIGH_GPRS;
+ /*
+ * Transactional execution support HWCAP_S390_TE is bit 10.
+ */
+ if (test_facility(50) && test_facility(73))
+ elf_hwcap |= HWCAP_S390_TE;
+#endif
+
get_cpu_id(&cpu_id);
switch (cpu_id.machine) {
case 0x9672:
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 720fda1620f2..ea431e551c6b 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -66,7 +66,7 @@ struct pcpu {
unsigned long panic_stack; /* panic stack for the cpu */
unsigned long ec_mask; /* bit mask for ec_xxx functions */
int state; /* physical cpu state */
- u32 status; /* last status received via sigp */
+ int polarization; /* physical polarization */
u16 address; /* physical cpu address */
};
@@ -74,6 +74,10 @@ static u8 boot_cpu_type;
static u16 boot_cpu_address;
static struct pcpu pcpu_devices[NR_CPUS];
+/*
+ * The smp_cpu_state_mutex must be held when changing the state or polarization
+ * member of a pcpu data structure within the pcpu_devices arreay.
+ */
DEFINE_MUTEX(smp_cpu_state_mutex);
/*
@@ -99,7 +103,7 @@ static inline int __pcpu_sigp_relax(u16 addr, u8 order, u32 parm, u32 *status)
int cc;
while (1) {
- cc = __pcpu_sigp(addr, order, parm, status);
+ cc = __pcpu_sigp(addr, order, parm, NULL);
if (cc != SIGP_CC_BUSY)
return cc;
cpu_relax();
@@ -111,7 +115,7 @@ static int pcpu_sigp_retry(struct pcpu *pcpu, u8 order, u32 parm)
int cc, retry;
for (retry = 0; ; retry++) {
- cc = __pcpu_sigp(pcpu->address, order, parm, &pcpu->status);
+ cc = __pcpu_sigp(pcpu->address, order, parm, NULL);
if (cc != SIGP_CC_BUSY)
break;
if (retry >= 3)
@@ -122,16 +126,18 @@ static int pcpu_sigp_retry(struct pcpu *pcpu, u8 order, u32 parm)
static inline int pcpu_stopped(struct pcpu *pcpu)
{
+ u32 uninitialized_var(status);
+
if (__pcpu_sigp(pcpu->address, SIGP_SENSE,
- 0, &pcpu->status) != SIGP_CC_STATUS_STORED)
+ 0, &status) != SIGP_CC_STATUS_STORED)
return 0;
- return !!(pcpu->status & (SIGP_STATUS_CHECK_STOP|SIGP_STATUS_STOPPED));
+ return !!(status & (SIGP_STATUS_CHECK_STOP|SIGP_STATUS_STOPPED));
}
static inline int pcpu_running(struct pcpu *pcpu)
{
if (__pcpu_sigp(pcpu->address, SIGP_SENSE_RUNNING,
- 0, &pcpu->status) != SIGP_CC_STATUS_STORED)
+ 0, NULL) != SIGP_CC_STATUS_STORED)
return 1;
/* Status stored condition code is equivalent to cpu not running. */
return 0;
@@ -586,6 +592,16 @@ static inline void smp_get_save_area(int cpu, u16 address) { }
#endif /* CONFIG_ZFCPDUMP || CONFIG_CRASH_DUMP */
+void smp_cpu_set_polarization(int cpu, int val)
+{
+ pcpu_devices[cpu].polarization = val;
+}
+
+int smp_cpu_get_polarization(int cpu)
+{
+ return pcpu_devices[cpu].polarization;
+}
+
static struct sclp_cpu_info *smp_get_cpu_info(void)
{
static int use_sigp_detection;
@@ -628,7 +644,7 @@ static int __devinit __smp_rescan_cpus(struct sclp_cpu_info *info,
pcpu->address = info->cpu[i].address;
pcpu->state = (cpu >= info->configured) ?
CPU_STATE_STANDBY : CPU_STATE_CONFIGURED;
- cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
+ smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
set_cpu_present(cpu, true);
if (sysfs_add && smp_add_present_cpu(cpu) != 0)
set_cpu_present(cpu, false);
@@ -796,7 +812,7 @@ void __init smp_prepare_boot_cpu(void)
pcpu->async_stack = S390_lowcore.async_stack - ASYNC_SIZE;
pcpu->panic_stack = S390_lowcore.panic_stack - PAGE_SIZE;
S390_lowcore.percpu_offset = __per_cpu_offset[0];
- cpu_set_polarization(0, POLARIZATION_UNKNOWN);
+ smp_cpu_set_polarization(0, POLARIZATION_UNKNOWN);
set_cpu_present(0, true);
set_cpu_online(0, true);
}
@@ -862,7 +878,7 @@ static ssize_t cpu_configure_store(struct device *dev,
if (rc)
break;
pcpu->state = CPU_STATE_STANDBY;
- cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
+ smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
topology_expect_change();
break;
case 1:
@@ -872,7 +888,7 @@ static ssize_t cpu_configure_store(struct device *dev,
if (rc)
break;
pcpu->state = CPU_STATE_CONFIGURED;
- cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
+ smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
topology_expect_change();
break;
default:
@@ -959,23 +975,17 @@ static int __cpuinit smp_cpu_notify(struct notifier_block *self,
struct device *s = &c->dev;
int err = 0;
- switch (action) {
+ switch (action & ~CPU_TASKS_FROZEN) {
case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
err = sysfs_create_group(&s->kobj, &cpu_online_attr_group);
break;
case CPU_DEAD:
- case CPU_DEAD_FROZEN:
sysfs_remove_group(&s->kobj, &cpu_online_attr_group);
break;
}
return notifier_from_errno(err);
}
-static struct notifier_block __cpuinitdata smp_cpu_nb = {
- .notifier_call = smp_cpu_notify,
-};
-
static int __devinit smp_add_present_cpu(int cpu)
{
struct cpu *c = &pcpu_devices[cpu].cpu;
@@ -1050,7 +1060,7 @@ static int __init s390_smp_init(void)
{
int cpu, rc;
- register_cpu_notifier(&smp_cpu_nb);
+ hotcpu_notifier(smp_cpu_notify, 0);
#ifdef CONFIG_HOTPLUG_CPU
rc = device_create_file(cpu_subsys.dev_root, &dev_attr_rescan);
if (rc)
diff --git a/arch/s390/kernel/syscalls.S b/arch/s390/kernel/syscalls.S
index bcab2f04ba58..48174850f3b0 100644
--- a/arch/s390/kernel/syscalls.S
+++ b/arch/s390/kernel/syscalls.S
@@ -350,3 +350,5 @@ SYSCALL(sys_syncfs,sys_syncfs,sys_syncfs_wrapper)
SYSCALL(sys_setns,sys_setns,sys_setns_wrapper)
SYSCALL(sys_process_vm_readv,sys_process_vm_readv,compat_sys_process_vm_readv_wrapper) /* 340 */
SYSCALL(sys_process_vm_writev,sys_process_vm_writev,compat_sys_process_vm_writev_wrapper)
+SYSCALL(sys_ni_syscall,sys_s390_runtime_instr,sys_s390_runtime_instr_wrapper)
+SYSCALL(sys_kcmp,sys_kcmp,sys_kcmp_wrapper)
diff --git a/arch/s390/kernel/sysinfo.c b/arch/s390/kernel/sysinfo.c
index fa0eb238dac7..62f89d98e880 100644
--- a/arch/s390/kernel/sysinfo.c
+++ b/arch/s390/kernel/sysinfo.c
@@ -22,17 +22,41 @@
#include <math-emu/soft-fp.h>
#include <math-emu/single.h>
-static inline int stsi_0(void)
+int topology_max_mnest;
+
+/*
+ * stsi - store system information
+ *
+ * Returns the current configuration level if function code 0 was specified.
+ * Otherwise returns 0 on success or a negative value on error.
+ */
+int stsi(void *sysinfo, int fc, int sel1, int sel2)
{
- int rc = stsi(NULL, 0, 0, 0);
- return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
+ register int r0 asm("0") = (fc << 28) | sel1;
+ register int r1 asm("1") = sel2;
+ int rc = 0;
+
+ asm volatile(
+ " stsi 0(%3)\n"
+ "0: jz 2f\n"
+ "1: lhi %1,%4\n"
+ "2:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (r0), "+d" (rc)
+ : "d" (r1), "a" (sysinfo), "K" (-EOPNOTSUPP)
+ : "cc", "memory");
+ if (rc)
+ return rc;
+ return fc ? 0 : ((unsigned int) r0) >> 28;
}
+EXPORT_SYMBOL(stsi);
-static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len)
+static void stsi_1_1_1(struct seq_file *m, struct sysinfo_1_1_1 *info)
{
- if (stsi(info, 1, 1, 1) == -ENOSYS)
- return len;
+ int i;
+ if (stsi(info, 1, 1, 1))
+ return;
EBCASC(info->manufacturer, sizeof(info->manufacturer));
EBCASC(info->type, sizeof(info->type));
EBCASC(info->model, sizeof(info->model));
@@ -41,242 +65,197 @@ static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len)
EBCASC(info->model_capacity, sizeof(info->model_capacity));
EBCASC(info->model_perm_cap, sizeof(info->model_perm_cap));
EBCASC(info->model_temp_cap, sizeof(info->model_temp_cap));
- len += sprintf(page + len, "Manufacturer: %-16.16s\n",
- info->manufacturer);
- len += sprintf(page + len, "Type: %-4.4s\n",
- info->type);
+ seq_printf(m, "Manufacturer: %-16.16s\n", info->manufacturer);
+ seq_printf(m, "Type: %-4.4s\n", info->type);
+ /*
+ * Sigh: the model field has been renamed with System z9
+ * to model_capacity and a new model field has been added
+ * after the plant field. To avoid confusing older programs
+ * the "Model:" prints "model_capacity model" or just
+ * "model_capacity" if the model string is empty .
+ */
+ seq_printf(m, "Model: %-16.16s", info->model_capacity);
if (info->model[0] != '\0')
- /*
- * Sigh: the model field has been renamed with System z9
- * to model_capacity and a new model field has been added
- * after the plant field. To avoid confusing older programs
- * the "Model:" prints "model_capacity model" or just
- * "model_capacity" if the model string is empty .
- */
- len += sprintf(page + len,
- "Model: %-16.16s %-16.16s\n",
- info->model_capacity, info->model);
- else
- len += sprintf(page + len, "Model: %-16.16s\n",
- info->model_capacity);
- len += sprintf(page + len, "Sequence Code: %-16.16s\n",
- info->sequence);
- len += sprintf(page + len, "Plant: %-4.4s\n",
- info->plant);
- len += sprintf(page + len, "Model Capacity: %-16.16s %08u\n",
- info->model_capacity, *(u32 *) info->model_cap_rating);
- if (info->model_perm_cap[0] != '\0')
- len += sprintf(page + len,
- "Model Perm. Capacity: %-16.16s %08u\n",
- info->model_perm_cap,
- *(u32 *) info->model_perm_cap_rating);
- if (info->model_temp_cap[0] != '\0')
- len += sprintf(page + len,
- "Model Temp. Capacity: %-16.16s %08u\n",
- info->model_temp_cap,
- *(u32 *) info->model_temp_cap_rating);
+ seq_printf(m, " %-16.16s", info->model);
+ seq_putc(m, '\n');
+ seq_printf(m, "Sequence Code: %-16.16s\n", info->sequence);
+ seq_printf(m, "Plant: %-4.4s\n", info->plant);
+ seq_printf(m, "Model Capacity: %-16.16s %08u\n",
+ info->model_capacity, info->model_cap_rating);
+ if (info->model_perm_cap_rating)
+ seq_printf(m, "Model Perm. Capacity: %-16.16s %08u\n",
+ info->model_perm_cap,
+ info->model_perm_cap_rating);
+ if (info->model_temp_cap_rating)
+ seq_printf(m, "Model Temp. Capacity: %-16.16s %08u\n",
+ info->model_temp_cap,
+ info->model_temp_cap_rating);
+ if (info->ncr)
+ seq_printf(m, "Nominal Cap. Rating: %08u\n", info->ncr);
+ if (info->npr)
+ seq_printf(m, "Nominal Perm. Rating: %08u\n", info->npr);
+ if (info->ntr)
+ seq_printf(m, "Nominal Temp. Rating: %08u\n", info->ntr);
if (info->cai) {
- len += sprintf(page + len,
- "Capacity Adj. Ind.: %d\n",
- info->cai);
- len += sprintf(page + len, "Capacity Ch. Reason: %d\n",
- info->ccr);
+ seq_printf(m, "Capacity Adj. Ind.: %d\n", info->cai);
+ seq_printf(m, "Capacity Ch. Reason: %d\n", info->ccr);
+ seq_printf(m, "Capacity Transient: %d\n", info->t);
+ }
+ if (info->p) {
+ for (i = 1; i <= ARRAY_SIZE(info->typepct); i++) {
+ seq_printf(m, "Type %d Percentage: %d\n",
+ i, info->typepct[i - 1]);
+ }
}
- return len;
}
-static int stsi_15_1_x(struct sysinfo_15_1_x *info, char *page, int len)
+static void stsi_15_1_x(struct seq_file *m, struct sysinfo_15_1_x *info)
{
static int max_mnest;
int i, rc;
- len += sprintf(page + len, "\n");
+ seq_putc(m, '\n');
if (!MACHINE_HAS_TOPOLOGY)
- return len;
- if (max_mnest) {
- stsi(info, 15, 1, max_mnest);
- } else {
- for (max_mnest = 6; max_mnest > 1; max_mnest--) {
- rc = stsi(info, 15, 1, max_mnest);
- if (rc != -ENOSYS)
- break;
- }
- }
- len += sprintf(page + len, "CPU Topology HW: ");
+ return;
+ if (stsi(info, 15, 1, topology_max_mnest))
+ return;
+ seq_printf(m, "CPU Topology HW: ");
for (i = 0; i < TOPOLOGY_NR_MAG; i++)
- len += sprintf(page + len, " %d", info->mag[i]);
- len += sprintf(page + len, "\n");
+ seq_printf(m, " %d", info->mag[i]);
+ seq_putc(m, '\n');
#ifdef CONFIG_SCHED_MC
store_topology(info);
- len += sprintf(page + len, "CPU Topology SW: ");
+ seq_printf(m, "CPU Topology SW: ");
for (i = 0; i < TOPOLOGY_NR_MAG; i++)
- len += sprintf(page + len, " %d", info->mag[i]);
- len += sprintf(page + len, "\n");
+ seq_printf(m, " %d", info->mag[i]);
+ seq_putc(m, '\n');
#endif
- return len;
}
-static int stsi_1_2_2(struct sysinfo_1_2_2 *info, char *page, int len)
+static void stsi_1_2_2(struct seq_file *m, struct sysinfo_1_2_2 *info)
{
struct sysinfo_1_2_2_extension *ext;
int i;
- if (stsi(info, 1, 2, 2) == -ENOSYS)
- return len;
+ if (stsi(info, 1, 2, 2))
+ return;
ext = (struct sysinfo_1_2_2_extension *)
((unsigned long) info + info->acc_offset);
-
- len += sprintf(page + len, "CPUs Total: %d\n",
- info->cpus_total);
- len += sprintf(page + len, "CPUs Configured: %d\n",
- info->cpus_configured);
- len += sprintf(page + len, "CPUs Standby: %d\n",
- info->cpus_standby);
- len += sprintf(page + len, "CPUs Reserved: %d\n",
- info->cpus_reserved);
-
- if (info->format == 1) {
- /*
- * Sigh 2. According to the specification the alternate
- * capability field is a 32 bit floating point number
- * if the higher order 8 bits are not zero. Printing
- * a floating point number in the kernel is a no-no,
- * always print the number as 32 bit unsigned integer.
- * The user-space needs to know about the strange
- * encoding of the alternate cpu capability.
- */
- len += sprintf(page + len, "Capability: %u %u\n",
- info->capability, ext->alt_capability);
- for (i = 2; i <= info->cpus_total; i++)
- len += sprintf(page + len,
- "Adjustment %02d-way: %u %u\n",
- i, info->adjustment[i-2],
- ext->alt_adjustment[i-2]);
-
- } else {
- len += sprintf(page + len, "Capability: %u\n",
- info->capability);
- for (i = 2; i <= info->cpus_total; i++)
- len += sprintf(page + len,
- "Adjustment %02d-way: %u\n",
- i, info->adjustment[i-2]);
+ seq_printf(m, "CPUs Total: %d\n", info->cpus_total);
+ seq_printf(m, "CPUs Configured: %d\n", info->cpus_configured);
+ seq_printf(m, "CPUs Standby: %d\n", info->cpus_standby);
+ seq_printf(m, "CPUs Reserved: %d\n", info->cpus_reserved);
+ /*
+ * Sigh 2. According to the specification the alternate
+ * capability field is a 32 bit floating point number
+ * if the higher order 8 bits are not zero. Printing
+ * a floating point number in the kernel is a no-no,
+ * always print the number as 32 bit unsigned integer.
+ * The user-space needs to know about the strange
+ * encoding of the alternate cpu capability.
+ */
+ seq_printf(m, "Capability: %u", info->capability);
+ if (info->format == 1)
+ seq_printf(m, " %u", ext->alt_capability);
+ seq_putc(m, '\n');
+ if (info->nominal_cap)
+ seq_printf(m, "Nominal Capability: %d\n", info->nominal_cap);
+ if (info->secondary_cap)
+ seq_printf(m, "Secondary Capability: %d\n", info->secondary_cap);
+ for (i = 2; i <= info->cpus_total; i++) {
+ seq_printf(m, "Adjustment %02d-way: %u",
+ i, info->adjustment[i-2]);
+ if (info->format == 1)
+ seq_printf(m, " %u", ext->alt_adjustment[i-2]);
+ seq_putc(m, '\n');
}
-
- if (info->secondary_capability != 0)
- len += sprintf(page + len, "Secondary Capability: %d\n",
- info->secondary_capability);
- return len;
}
-static int stsi_2_2_2(struct sysinfo_2_2_2 *info, char *page, int len)
+static void stsi_2_2_2(struct seq_file *m, struct sysinfo_2_2_2 *info)
{
- if (stsi(info, 2, 2, 2) == -ENOSYS)
- return len;
-
+ if (stsi(info, 2, 2, 2))
+ return;
EBCASC(info->name, sizeof(info->name));
-
- len += sprintf(page + len, "\n");
- len += sprintf(page + len, "LPAR Number: %d\n",
- info->lpar_number);
-
- len += sprintf(page + len, "LPAR Characteristics: ");
+ seq_putc(m, '\n');
+ seq_printf(m, "LPAR Number: %d\n", info->lpar_number);
+ seq_printf(m, "LPAR Characteristics: ");
if (info->characteristics & LPAR_CHAR_DEDICATED)
- len += sprintf(page + len, "Dedicated ");
+ seq_printf(m, "Dedicated ");
if (info->characteristics & LPAR_CHAR_SHARED)
- len += sprintf(page + len, "Shared ");
+ seq_printf(m, "Shared ");
if (info->characteristics & LPAR_CHAR_LIMITED)
- len += sprintf(page + len, "Limited ");
- len += sprintf(page + len, "\n");
-
- len += sprintf(page + len, "LPAR Name: %-8.8s\n",
- info->name);
-
- len += sprintf(page + len, "LPAR Adjustment: %d\n",
- info->caf);
-
- len += sprintf(page + len, "LPAR CPUs Total: %d\n",
- info->cpus_total);
- len += sprintf(page + len, "LPAR CPUs Configured: %d\n",
- info->cpus_configured);
- len += sprintf(page + len, "LPAR CPUs Standby: %d\n",
- info->cpus_standby);
- len += sprintf(page + len, "LPAR CPUs Reserved: %d\n",
- info->cpus_reserved);
- len += sprintf(page + len, "LPAR CPUs Dedicated: %d\n",
- info->cpus_dedicated);
- len += sprintf(page + len, "LPAR CPUs Shared: %d\n",
- info->cpus_shared);
- return len;
+ seq_printf(m, "Limited ");
+ seq_putc(m, '\n');
+ seq_printf(m, "LPAR Name: %-8.8s\n", info->name);
+ seq_printf(m, "LPAR Adjustment: %d\n", info->caf);
+ seq_printf(m, "LPAR CPUs Total: %d\n", info->cpus_total);
+ seq_printf(m, "LPAR CPUs Configured: %d\n", info->cpus_configured);
+ seq_printf(m, "LPAR CPUs Standby: %d\n", info->cpus_standby);
+ seq_printf(m, "LPAR CPUs Reserved: %d\n", info->cpus_reserved);
+ seq_printf(m, "LPAR CPUs Dedicated: %d\n", info->cpus_dedicated);
+ seq_printf(m, "LPAR CPUs Shared: %d\n", info->cpus_shared);
}
-static int stsi_3_2_2(struct sysinfo_3_2_2 *info, char *page, int len)
+static void stsi_3_2_2(struct seq_file *m, struct sysinfo_3_2_2 *info)
{
int i;
- if (stsi(info, 3, 2, 2) == -ENOSYS)
- return len;
+ if (stsi(info, 3, 2, 2))
+ return;
for (i = 0; i < info->count; i++) {
EBCASC(info->vm[i].name, sizeof(info->vm[i].name));
EBCASC(info->vm[i].cpi, sizeof(info->vm[i].cpi));
- len += sprintf(page + len, "\n");
- len += sprintf(page + len, "VM%02d Name: %-8.8s\n",
- i, info->vm[i].name);
- len += sprintf(page + len, "VM%02d Control Program: %-16.16s\n",
- i, info->vm[i].cpi);
-
- len += sprintf(page + len, "VM%02d Adjustment: %d\n",
- i, info->vm[i].caf);
-
- len += sprintf(page + len, "VM%02d CPUs Total: %d\n",
- i, info->vm[i].cpus_total);
- len += sprintf(page + len, "VM%02d CPUs Configured: %d\n",
- i, info->vm[i].cpus_configured);
- len += sprintf(page + len, "VM%02d CPUs Standby: %d\n",
- i, info->vm[i].cpus_standby);
- len += sprintf(page + len, "VM%02d CPUs Reserved: %d\n",
- i, info->vm[i].cpus_reserved);
+ seq_putc(m, '\n');
+ seq_printf(m, "VM%02d Name: %-8.8s\n", i, info->vm[i].name);
+ seq_printf(m, "VM%02d Control Program: %-16.16s\n", i, info->vm[i].cpi);
+ seq_printf(m, "VM%02d Adjustment: %d\n", i, info->vm[i].caf);
+ seq_printf(m, "VM%02d CPUs Total: %d\n", i, info->vm[i].cpus_total);
+ seq_printf(m, "VM%02d CPUs Configured: %d\n", i, info->vm[i].cpus_configured);
+ seq_printf(m, "VM%02d CPUs Standby: %d\n", i, info->vm[i].cpus_standby);
+ seq_printf(m, "VM%02d CPUs Reserved: %d\n", i, info->vm[i].cpus_reserved);
}
- return len;
}
-static int proc_read_sysinfo(char *page, char **start,
- off_t off, int count,
- int *eof, void *data)
+static int sysinfo_show(struct seq_file *m, void *v)
{
- unsigned long info = get_zeroed_page(GFP_KERNEL);
- int level, len;
+ void *info = (void *)get_zeroed_page(GFP_KERNEL);
+ int level;
if (!info)
return 0;
-
- len = 0;
- level = stsi_0();
+ level = stsi(NULL, 0, 0, 0);
if (level >= 1)
- len = stsi_1_1_1((struct sysinfo_1_1_1 *) info, page, len);
-
+ stsi_1_1_1(m, info);
if (level >= 1)
- len = stsi_15_1_x((struct sysinfo_15_1_x *) info, page, len);
-
+ stsi_15_1_x(m, info);
if (level >= 1)
- len = stsi_1_2_2((struct sysinfo_1_2_2 *) info, page, len);
-
+ stsi_1_2_2(m, info);
if (level >= 2)
- len = stsi_2_2_2((struct sysinfo_2_2_2 *) info, page, len);
-
+ stsi_2_2_2(m, info);
if (level >= 3)
- len = stsi_3_2_2((struct sysinfo_3_2_2 *) info, page, len);
+ stsi_3_2_2(m, info);
+ free_page((unsigned long)info);
+ return 0;
+}
- free_page(info);
- return len;
+static int sysinfo_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, sysinfo_show, NULL);
}
-static __init int create_proc_sysinfo(void)
+static const struct file_operations sysinfo_fops = {
+ .open = sysinfo_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static int __init sysinfo_create_proc(void)
{
- create_proc_read_entry("sysinfo", 0444, NULL,
- proc_read_sysinfo, NULL);
+ proc_create("sysinfo", 0444, NULL, &sysinfo_fops);
return 0;
}
-device_initcall(create_proc_sysinfo);
+device_initcall(sysinfo_create_proc);
/*
* Service levels interface.
@@ -407,7 +386,7 @@ void s390_adjust_jiffies(void)
if (!info)
return;
- if (stsi(info, 1, 2, 2) != -ENOSYS) {
+ if (stsi(info, 1, 2, 2) == 0) {
/*
* Major sigh. The cpu capability encoding is "special".
* If the first 9 bits of info->capability are 0 then it
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index dcec960fc724..2db1011b8b19 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -329,7 +329,7 @@ static unsigned long clock_sync_flags;
* The synchronous get_clock function. It will write the current clock
* value to the clock pointer and return 0 if the clock is in sync with
* the external time source. If the clock mode is local it will return
- * -ENOSYS and -EAGAIN if the clock is not in sync with the external
+ * -EOPNOTSUPP and -EAGAIN if the clock is not in sync with the external
* reference.
*/
int get_sync_clock(unsigned long long *clock)
@@ -347,7 +347,7 @@ int get_sync_clock(unsigned long long *clock)
return 0;
if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags) &&
!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
- return -ENOSYS;
+ return -EOPNOTSUPP;
if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags) &&
!test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
return -EACCES;
diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c
index 05151e06c388..54d93f4b6818 100644
--- a/arch/s390/kernel/topology.c
+++ b/arch/s390/kernel/topology.c
@@ -17,6 +17,7 @@
#include <linux/cpu.h>
#include <linux/smp.h>
#include <linux/mm.h>
+#include <asm/sysinfo.h>
#define PTF_HORIZONTAL (0UL)
#define PTF_VERTICAL (1UL)
@@ -44,9 +45,6 @@ static struct mask_info book_info;
cpumask_t cpu_book_map[NR_CPUS];
unsigned char cpu_book_id[NR_CPUS];
-/* smp_cpu_state_mutex must be held when accessing this array */
-int cpu_polarization[NR_CPUS];
-
static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu)
{
cpumask_t mask;
@@ -75,10 +73,7 @@ static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu,
{
unsigned int cpu;
- for (cpu = find_first_bit(&tl_cpu->mask[0], TOPOLOGY_CPU_BITS);
- cpu < TOPOLOGY_CPU_BITS;
- cpu = find_next_bit(&tl_cpu->mask[0], TOPOLOGY_CPU_BITS, cpu + 1))
- {
+ for_each_set_bit(cpu, &tl_cpu->mask[0], TOPOLOGY_CPU_BITS) {
unsigned int rcpu;
int lcpu;
@@ -94,7 +89,7 @@ static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu,
} else {
cpu_core_id[lcpu] = core->id;
}
- cpu_set_polarization(lcpu, tl_cpu->pp);
+ smp_cpu_set_polarization(lcpu, tl_cpu->pp);
}
}
return core;
@@ -201,7 +196,7 @@ static void topology_update_polarization_simple(void)
mutex_lock(&smp_cpu_state_mutex);
for_each_possible_cpu(cpu)
- cpu_set_polarization(cpu, POLARIZATION_HRZ);
+ smp_cpu_set_polarization(cpu, POLARIZATION_HRZ);
mutex_unlock(&smp_cpu_state_mutex);
}
@@ -231,7 +226,7 @@ int topology_set_cpu_management(int fc)
if (rc)
return -EBUSY;
for_each_possible_cpu(cpu)
- cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
+ smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
return rc;
}
@@ -250,12 +245,10 @@ static void update_cpu_core_map(void)
void store_topology(struct sysinfo_15_1_x *info)
{
- int rc;
-
- rc = stsi(info, 15, 1, 3);
- if (rc != -ENOSYS)
- return;
- stsi(info, 15, 1, 2);
+ if (topology_max_mnest >= 3)
+ stsi(info, 15, 1, 3);
+ else
+ stsi(info, 15, 1, 2);
}
int arch_update_cpu_topology(void)
@@ -415,7 +408,7 @@ static ssize_t cpu_polarization_show(struct device *dev,
ssize_t count;
mutex_lock(&smp_cpu_state_mutex);
- switch (cpu_read_polarization(cpu)) {
+ switch (smp_cpu_get_polarization(cpu)) {
case POLARIZATION_HRZ:
count = sprintf(buf, "horizontal\n");
break;
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index 01775c04a90e..3d2b0fa37db0 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -57,6 +57,23 @@ static int kstack_depth_to_print = 12;
static int kstack_depth_to_print = 20;
#endif /* CONFIG_64BIT */
+static inline void __user *get_trap_ip(struct pt_regs *regs)
+{
+#ifdef CONFIG_64BIT
+ unsigned long address;
+
+ if (regs->int_code & 0x200)
+ address = *(unsigned long *)(current->thread.trap_tdb + 24);
+ else
+ address = regs->psw.addr;
+ return (void __user *)
+ ((address - (regs->int_code >> 16)) & PSW_ADDR_INSN);
+#else
+ return (void __user *)
+ ((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN);
+#endif
+}
+
/*
* For show_trace we have tree different stack to consider:
* - the panic stack which is used if the kernel stack has overflown
@@ -214,7 +231,6 @@ void show_registers(struct pt_regs *regs)
void show_regs(struct pt_regs *regs)
{
- print_modules();
printk("CPU: %d %s %s %.*s\n",
task_thread_info(current)->cpu, print_tainted(),
init_utsname()->release,
@@ -254,6 +270,7 @@ void die(struct pt_regs *regs, const char *str)
#endif
printk("\n");
notify_die(DIE_OOPS, str, regs, 0, regs->int_code & 0xffff, SIGSEGV);
+ print_modules();
show_regs(regs);
bust_spinlocks(0);
add_taint(TAINT_DIE);
@@ -285,12 +302,6 @@ int is_valid_bugaddr(unsigned long addr)
return 1;
}
-static inline void __user *get_psw_address(struct pt_regs *regs)
-{
- return (void __user *)
- ((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN);
-}
-
static void __kprobes do_trap(struct pt_regs *regs,
int si_signo, int si_code, char *str)
{
@@ -304,14 +315,14 @@ static void __kprobes do_trap(struct pt_regs *regs,
info.si_signo = si_signo;
info.si_errno = 0;
info.si_code = si_code;
- info.si_addr = get_psw_address(regs);
+ info.si_addr = get_trap_ip(regs);
force_sig_info(si_signo, &info, current);
report_user_fault(regs, si_signo);
} else {
const struct exception_table_entry *fixup;
fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
if (fixup)
- regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE;
+ regs->psw.addr = extable_fixup(fixup) | PSW_ADDR_AMODE;
else {
enum bug_trap_type btt;
@@ -381,6 +392,11 @@ DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN,
"translation exception")
+#ifdef CONFIG_64BIT
+DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN,
+ "transaction constraint exception")
+#endif
+
static inline void do_fp_trap(struct pt_regs *regs, int fpc)
{
int si_code = 0;
@@ -408,7 +424,7 @@ static void __kprobes illegal_op(struct pt_regs *regs)
__u16 __user *location;
int signal = 0;
- location = get_psw_address(regs);
+ location = get_trap_ip(regs);
if (user_mode(regs)) {
if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
@@ -476,7 +492,7 @@ void specification_exception(struct pt_regs *regs)
__u16 __user *location = NULL;
int signal = 0;
- location = (__u16 __user *) get_psw_address(regs);
+ location = (__u16 __user *) get_trap_ip(regs);
if (user_mode(regs)) {
get_user(*((__u16 *) opcode), location);
@@ -525,7 +541,7 @@ static void data_exception(struct pt_regs *regs)
__u16 __user *location;
int signal = 0;
- location = get_psw_address(regs);
+ location = get_trap_ip(regs);
if (MACHINE_HAS_IEEE)
asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
@@ -641,6 +657,7 @@ void __init trap_init(void)
pgm_check_table[0x12] = &translation_exception;
pgm_check_table[0x13] = &special_op_exception;
#ifdef CONFIG_64BIT
+ pgm_check_table[0x18] = &transaction_exception;
pgm_check_table[0x38] = &do_asce_exception;
pgm_check_table[0x39] = &do_dat_exception;
pgm_check_table[0x3A] = &do_dat_exception;
diff --git a/arch/s390/kernel/vdso.c b/arch/s390/kernel/vdso.c
index 9a19ca367c17..d7776281cb60 100644
--- a/arch/s390/kernel/vdso.c
+++ b/arch/s390/kernel/vdso.c
@@ -85,7 +85,7 @@ struct vdso_data *vdso_data = &vdso_data_store.data;
static void vdso_init_data(struct vdso_data *vd)
{
vd->ectg_available =
- addressing_mode != HOME_SPACE_MODE && test_facility(31);
+ s390_user_mode != HOME_SPACE_MODE && test_facility(31);
}
#ifdef CONFIG_64BIT
@@ -102,7 +102,7 @@ int vdso_alloc_per_cpu(struct _lowcore *lowcore)
lowcore->vdso_per_cpu_data = __LC_PASTE;
- if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled)
+ if (s390_user_mode == HOME_SPACE_MODE || !vdso_enabled)
return 0;
segment_table = __get_free_pages(GFP_KERNEL, SEGMENT_ORDER);
@@ -147,7 +147,7 @@ void vdso_free_per_cpu(struct _lowcore *lowcore)
unsigned long segment_table, page_table, page_frame;
u32 *psal, *aste;
- if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled)
+ if (s390_user_mode == HOME_SPACE_MODE || !vdso_enabled)
return;
psal = (u32 *)(addr_t) lowcore->paste[4];
@@ -165,7 +165,7 @@ static void vdso_init_cr5(void)
{
unsigned long cr5;
- if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled)
+ if (s390_user_mode == HOME_SPACE_MODE || !vdso_enabled)
return;
cr5 = offsetof(struct _lowcore, paste);
__ctl_load(cr5, 5, 5);
diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c
index 4fc97b40a6e1..790334427895 100644
--- a/arch/s390/kernel/vtime.c
+++ b/arch/s390/kernel/vtime.c
@@ -99,7 +99,7 @@ static int do_account_vtime(struct task_struct *tsk, int hardirq_offset)
return virt_timer_forward(user + system);
}
-void account_vtime(struct task_struct *prev, struct task_struct *next)
+void vtime_task_switch(struct task_struct *prev)
{
struct thread_info *ti;
@@ -107,7 +107,7 @@ void account_vtime(struct task_struct *prev, struct task_struct *next)
ti = task_thread_info(prev);
ti->user_timer = S390_lowcore.user_timer;
ti->system_timer = S390_lowcore.system_timer;
- ti = task_thread_info(next);
+ ti = task_thread_info(current);
S390_lowcore.user_timer = ti->user_timer;
S390_lowcore.system_timer = ti->system_timer;
}
@@ -122,7 +122,7 @@ void account_process_tick(struct task_struct *tsk, int user_tick)
* Update process times based on virtual cpu times stored by entry.S
* to the lowcore fields user_timer, system_timer & steal_clock.
*/
-void account_system_vtime(struct task_struct *tsk)
+void vtime_account(struct task_struct *tsk)
{
struct thread_info *ti = task_thread_info(tsk);
u64 timer, system;
@@ -138,7 +138,7 @@ void account_system_vtime(struct task_struct *tsk)
virt_timer_forward(system);
}
-EXPORT_SYMBOL_GPL(account_system_vtime);
+EXPORT_SYMBOL_GPL(vtime_account);
void __kprobes vtime_stop_cpu(void)
{
@@ -378,9 +378,8 @@ static int __cpuinit s390_nohz_notify(struct notifier_block *self,
long cpu = (long) hcpu;
idle = &per_cpu(s390_idle, cpu);
- switch (action) {
+ switch (action & ~CPU_TASKS_FROZEN) {
case CPU_DYING:
- case CPU_DYING_FROZEN:
idle->nohz_delay = 0;
default:
break;
diff --git a/arch/s390/kvm/Kconfig b/arch/s390/kvm/Kconfig
index 78eb9847008f..9b04a32e5695 100644
--- a/arch/s390/kvm/Kconfig
+++ b/arch/s390/kvm/Kconfig
@@ -5,7 +5,7 @@ source "virt/kvm/Kconfig"
menuconfig VIRTUALIZATION
def_bool y
- prompt "Virtualization"
+ prompt "KVM"
---help---
Say Y here to get to see options for using your Linux host to run other
operating systems inside virtual machines (guests).
diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c
index 60da903d6f3e..310be61bead7 100644
--- a/arch/s390/kvm/priv.c
+++ b/arch/s390/kvm/priv.c
@@ -211,7 +211,7 @@ static void handle_stsi_3_2_2(struct kvm_vcpu *vcpu, struct sysinfo_3_2_2 *mem)
spin_unlock(&fi->lock);
/* deal with other level 3 hypervisors */
- if (stsi(mem, 3, 2, 2) == -ENOSYS)
+ if (stsi(mem, 3, 2, 2))
mem->count = 0;
if (mem->count < 8)
mem->count++;
@@ -259,7 +259,7 @@ static int handle_stsi(struct kvm_vcpu *vcpu)
mem = get_zeroed_page(GFP_KERNEL);
if (!mem)
goto out_fail;
- if (stsi((void *) mem, fc, sel1, sel2) == -ENOSYS)
+ if (stsi((void *) mem, fc, sel1, sel2))
goto out_mem;
break;
case 3:
diff --git a/arch/s390/lib/Makefile b/arch/s390/lib/Makefile
index 761ab8b56afc..6ab0d0b5cec8 100644
--- a/arch/s390/lib/Makefile
+++ b/arch/s390/lib/Makefile
@@ -4,6 +4,7 @@
lib-y += delay.o string.o uaccess_std.o uaccess_pt.o
obj-y += usercopy.o
-obj-$(CONFIG_32BIT) += div64.o qrnnd.o ucmpdi2.o
+obj-$(CONFIG_32BIT) += div64.o qrnnd.o ucmpdi2.o mem32.o
+obj-$(CONFIG_64BIT) += mem64.o
lib-$(CONFIG_64BIT) += uaccess_mvcos.o
lib-$(CONFIG_SMP) += spinlock.o
diff --git a/arch/s390/lib/mem32.S b/arch/s390/lib/mem32.S
new file mode 100644
index 000000000000..14ca9244b615
--- /dev/null
+++ b/arch/s390/lib/mem32.S
@@ -0,0 +1,92 @@
+/*
+ * String handling functions.
+ *
+ * Copyright IBM Corp. 2012
+ */
+
+#include <linux/linkage.h>
+
+/*
+ * memset implementation
+ *
+ * This code corresponds to the C construct below. We do distinguish
+ * between clearing (c == 0) and setting a memory array (c != 0) simply
+ * because nearly all memset invocations in the kernel clear memory and
+ * the xc instruction is preferred in such cases.
+ *
+ * void *memset(void *s, int c, size_t n)
+ * {
+ * if (likely(c == 0))
+ * return __builtin_memset(s, 0, n);
+ * return __builtin_memset(s, c, n);
+ * }
+ */
+ENTRY(memset)
+ basr %r5,%r0
+.Lmemset_base:
+ ltr %r4,%r4
+ bzr %r14
+ ltr %r3,%r3
+ jnz .Lmemset_fill
+ ahi %r4,-1
+ lr %r3,%r4
+ srl %r3,8
+ ltr %r3,%r3
+ lr %r1,%r2
+ je .Lmemset_clear_rest
+.Lmemset_clear_loop:
+ xc 0(256,%r1),0(%r1)
+ la %r1,256(%r1)
+ brct %r3,.Lmemset_clear_loop
+.Lmemset_clear_rest:
+ ex %r4,.Lmemset_xc-.Lmemset_base(%r5)
+ br %r14
+.Lmemset_fill:
+ stc %r3,0(%r2)
+ chi %r4,1
+ lr %r1,%r2
+ ber %r14
+ ahi %r4,-2
+ lr %r3,%r4
+ srl %r3,8
+ ltr %r3,%r3
+ je .Lmemset_fill_rest
+.Lmemset_fill_loop:
+ mvc 1(256,%r1),0(%r1)
+ la %r1,256(%r1)
+ brct %r3,.Lmemset_fill_loop
+.Lmemset_fill_rest:
+ ex %r4,.Lmemset_mvc-.Lmemset_base(%r5)
+ br %r14
+.Lmemset_xc:
+ xc 0(1,%r1),0(%r1)
+.Lmemset_mvc:
+ mvc 1(1,%r1),0(%r1)
+
+/*
+ * memcpy implementation
+ *
+ * void *memcpy(void *dest, const void *src, size_t n)
+ */
+ENTRY(memcpy)
+ basr %r5,%r0
+.Lmemcpy_base:
+ ltr %r4,%r4
+ bzr %r14
+ ahi %r4,-1
+ lr %r0,%r4
+ srl %r0,8
+ ltr %r0,%r0
+ lr %r1,%r2
+ jnz .Lmemcpy_loop
+.Lmemcpy_rest:
+ ex %r4,.Lmemcpy_mvc-.Lmemcpy_base(%r5)
+ br %r14
+.Lmemcpy_loop:
+ mvc 0(256,%r1),0(%r3)
+ la %r1,256(%r1)
+ la %r3,256(%r3)
+ brct %r0,.Lmemcpy_loop
+ j .Lmemcpy_rest
+.Lmemcpy_mvc:
+ mvc 0(1,%r1),0(%r3)
diff --git a/arch/s390/lib/mem64.S b/arch/s390/lib/mem64.S
new file mode 100644
index 000000000000..c6d553e85ab1
--- /dev/null
+++ b/arch/s390/lib/mem64.S
@@ -0,0 +1,88 @@
+/*
+ * String handling functions.
+ *
+ * Copyright IBM Corp. 2012
+ */
+
+#include <linux/linkage.h>
+
+/*
+ * memset implementation
+ *
+ * This code corresponds to the C construct below. We do distinguish
+ * between clearing (c == 0) and setting a memory array (c != 0) simply
+ * because nearly all memset invocations in the kernel clear memory and
+ * the xc instruction is preferred in such cases.
+ *
+ * void *memset(void *s, int c, size_t n)
+ * {
+ * if (likely(c == 0))
+ * return __builtin_memset(s, 0, n);
+ * return __builtin_memset(s, c, n);
+ * }
+ */
+ENTRY(memset)
+ ltgr %r4,%r4
+ bzr %r14
+ ltgr %r3,%r3
+ jnz .Lmemset_fill
+ aghi %r4,-1
+ srlg %r3,%r4,8
+ ltgr %r3,%r3
+ lgr %r1,%r2
+ jz .Lmemset_clear_rest
+.Lmemset_clear_loop:
+ xc 0(256,%r1),0(%r1)
+ la %r1,256(%r1)
+ brctg %r3,.Lmemset_clear_loop
+.Lmemset_clear_rest:
+ larl %r3,.Lmemset_xc
+ ex %r4,0(%r3)
+ br %r14
+.Lmemset_fill:
+ stc %r3,0(%r2)
+ cghi %r4,1
+ lgr %r1,%r2
+ ber %r14
+ aghi %r4,-2
+ srlg %r3,%r4,8
+ ltgr %r3,%r3
+ jz .Lmemset_fill_rest
+.Lmemset_fill_loop:
+ mvc 1(256,%r1),0(%r1)
+ la %r1,256(%r1)
+ brctg %r3,.Lmemset_fill_loop
+.Lmemset_fill_rest:
+ larl %r3,.Lmemset_mvc
+ ex %r4,0(%r3)
+ br %r14
+.Lmemset_xc:
+ xc 0(1,%r1),0(%r1)
+.Lmemset_mvc:
+ mvc 1(1,%r1),0(%r1)
+
+/*
+ * memcpy implementation
+ *
+ * void *memcpy(void *dest, const void *src, size_t n)
+ */
+ENTRY(memcpy)
+ ltgr %r4,%r4
+ bzr %r14
+ aghi %r4,-1
+ srlg %r5,%r4,8
+ ltgr %r5,%r5
+ lgr %r1,%r2
+ jnz .Lmemcpy_loop
+.Lmemcpy_rest:
+ larl %r5,.Lmemcpy_mvc
+ ex %r4,0(%r5)
+ br %r14
+.Lmemcpy_loop:
+ mvc 0(256,%r1),0(%r3)
+ la %r1,256(%r1)
+ la %r3,256(%r3)
+ brctg %r5,.Lmemcpy_loop
+ j .Lmemcpy_rest
+.Lmemcpy_mvc:
+ mvc 0(1,%r1),0(%r3)
diff --git a/arch/s390/lib/string.c b/arch/s390/lib/string.c
index 846ec64ab2c9..b647d5ff0ad9 100644
--- a/arch/s390/lib/string.c
+++ b/arch/s390/lib/string.c
@@ -43,11 +43,7 @@ static inline char *__strnend(const char *s, size_t n)
*/
size_t strlen(const char *s)
{
-#if __GNUC__ < 4
return __strend(s) - s;
-#else
- return __builtin_strlen(s);
-#endif
}
EXPORT_SYMBOL(strlen);
@@ -73,7 +69,6 @@ EXPORT_SYMBOL(strnlen);
*/
char *strcpy(char *dest, const char *src)
{
-#if __GNUC__ < 4
register int r0 asm("0") = 0;
char *ret = dest;
@@ -82,9 +77,6 @@ char *strcpy(char *dest, const char *src)
: "+&a" (dest), "+&a" (src) : "d" (r0)
: "cc", "memory" );
return ret;
-#else
- return __builtin_strcpy(dest, src);
-#endif
}
EXPORT_SYMBOL(strcpy);
@@ -106,7 +98,7 @@ size_t strlcpy(char *dest, const char *src, size_t size)
if (size) {
size_t len = (ret >= size) ? size-1 : ret;
dest[len] = '\0';
- __builtin_memcpy(dest, src, len);
+ memcpy(dest, src, len);
}
return ret;
}
@@ -124,8 +116,8 @@ EXPORT_SYMBOL(strlcpy);
char *strncpy(char *dest, const char *src, size_t n)
{
size_t len = __strnend(src, n) - src;
- __builtin_memset(dest + len, 0, n - len);
- __builtin_memcpy(dest, src, len);
+ memset(dest + len, 0, n - len);
+ memcpy(dest, src, len);
return dest;
}
EXPORT_SYMBOL(strncpy);
@@ -171,7 +163,7 @@ size_t strlcat(char *dest, const char *src, size_t n)
if (len >= n)
len = n - 1;
dest[len] = '\0';
- __builtin_memcpy(dest, src, len);
+ memcpy(dest, src, len);
}
return res;
}
@@ -194,7 +186,7 @@ char *strncat(char *dest, const char *src, size_t n)
char *p = __strend(dest);
p[len] = '\0';
- __builtin_memcpy(p, src, len);
+ memcpy(p, src, len);
return dest;
}
EXPORT_SYMBOL(strncat);
@@ -348,41 +340,3 @@ void *memscan(void *s, int c, size_t n)
return (void *) ret;
}
EXPORT_SYMBOL(memscan);
-
-/**
- * memcpy - Copy one area of memory to another
- * @dest: Where to copy to
- * @src: Where to copy from
- * @n: The size of the area.
- *
- * returns a pointer to @dest
- */
-void *memcpy(void *dest, const void *src, size_t n)
-{
- return __builtin_memcpy(dest, src, n);
-}
-EXPORT_SYMBOL(memcpy);
-
-/**
- * memset - Fill a region of memory with the given value
- * @s: Pointer to the start of the area.
- * @c: The byte to fill the area with
- * @n: The size of the area.
- *
- * returns a pointer to @s
- */
-void *memset(void *s, int c, size_t n)
-{
- char *xs;
-
- if (c == 0)
- return __builtin_memset(s, 0, n);
-
- xs = (char *) s;
- if (n > 0)
- do {
- *xs++ = c;
- } while (--n > 0);
- return s;
-}
-EXPORT_SYMBOL(memset);
diff --git a/arch/s390/lib/uaccess_pt.c b/arch/s390/lib/uaccess_pt.c
index 60ee2b883797..2d37bb861faf 100644
--- a/arch/s390/lib/uaccess_pt.c
+++ b/arch/s390/lib/uaccess_pt.c
@@ -2,69 +2,82 @@
* User access functions based on page table walks for enhanced
* system layout without hardware support.
*
- * Copyright IBM Corp. 2006
+ * Copyright IBM Corp. 2006, 2012
* Author(s): Gerald Schaefer (gerald.schaefer@de.ibm.com)
*/
#include <linux/errno.h>
#include <linux/hardirq.h>
#include <linux/mm.h>
+#include <linux/hugetlb.h>
#include <asm/uaccess.h>
#include <asm/futex.h>
#include "uaccess.h"
-static inline pte_t *follow_table(struct mm_struct *mm, unsigned long addr)
+
+/*
+ * Returns kernel address for user virtual address. If the returned address is
+ * >= -4095 (IS_ERR_VALUE(x) returns true), a fault has occured and the address
+ * contains the (negative) exception code.
+ */
+static __always_inline unsigned long follow_table(struct mm_struct *mm,
+ unsigned long addr, int write)
{
pgd_t *pgd;
pud_t *pud;
pmd_t *pmd;
+ pte_t *ptep;
pgd = pgd_offset(mm, addr);
if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
- return (pte_t *) 0x3a;
+ return -0x3aUL;
pud = pud_offset(pgd, addr);
if (pud_none(*pud) || unlikely(pud_bad(*pud)))
- return (pte_t *) 0x3b;
+ return -0x3bUL;
pmd = pmd_offset(pud, addr);
- if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
- return (pte_t *) 0x10;
+ if (pmd_none(*pmd))
+ return -0x10UL;
+ if (pmd_huge(*pmd)) {
+ if (write && (pmd_val(*pmd) & _SEGMENT_ENTRY_RO))
+ return -0x04UL;
+ return (pmd_val(*pmd) & HPAGE_MASK) + (addr & ~HPAGE_MASK);
+ }
+ if (unlikely(pmd_bad(*pmd)))
+ return -0x10UL;
+
+ ptep = pte_offset_map(pmd, addr);
+ if (!pte_present(*ptep))
+ return -0x11UL;
+ if (write && !pte_write(*ptep))
+ return -0x04UL;
- return pte_offset_map(pmd, addr);
+ return (pte_val(*ptep) & PAGE_MASK) + (addr & ~PAGE_MASK);
}
static __always_inline size_t __user_copy_pt(unsigned long uaddr, void *kptr,
size_t n, int write_user)
{
struct mm_struct *mm = current->mm;
- unsigned long offset, pfn, done, size;
- pte_t *pte;
+ unsigned long offset, done, size, kaddr;
void *from, *to;
done = 0;
retry:
spin_lock(&mm->page_table_lock);
do {
- pte = follow_table(mm, uaddr);
- if ((unsigned long) pte < 0x1000)
+ kaddr = follow_table(mm, uaddr, write_user);
+ if (IS_ERR_VALUE(kaddr))
goto fault;
- if (!pte_present(*pte)) {
- pte = (pte_t *) 0x11;
- goto fault;
- } else if (write_user && !pte_write(*pte)) {
- pte = (pte_t *) 0x04;
- goto fault;
- }
- pfn = pte_pfn(*pte);
- offset = uaddr & (PAGE_SIZE - 1);
+ offset = uaddr & ~PAGE_MASK;
size = min(n - done, PAGE_SIZE - offset);
if (write_user) {
- to = (void *)((pfn << PAGE_SHIFT) + offset);
+ to = (void *) kaddr;
from = kptr + done;
} else {
- from = (void *)((pfn << PAGE_SHIFT) + offset);
+ from = (void *) kaddr;
to = kptr + done;
}
memcpy(to, from, size);
@@ -75,7 +88,7 @@ retry:
return n - done;
fault:
spin_unlock(&mm->page_table_lock);
- if (__handle_fault(uaddr, (unsigned long) pte, write_user))
+ if (__handle_fault(uaddr, -kaddr, write_user))
return n - done;
goto retry;
}
@@ -84,27 +97,22 @@ fault:
* Do DAT for user address by page table walk, return kernel address.
* This function needs to be called with current->mm->page_table_lock held.
*/
-static __always_inline unsigned long __dat_user_addr(unsigned long uaddr)
+static __always_inline unsigned long __dat_user_addr(unsigned long uaddr,
+ int write)
{
struct mm_struct *mm = current->mm;
- unsigned long pfn;
- pte_t *pte;
+ unsigned long kaddr;
int rc;
retry:
- pte = follow_table(mm, uaddr);
- if ((unsigned long) pte < 0x1000)
- goto fault;
- if (!pte_present(*pte)) {
- pte = (pte_t *) 0x11;
+ kaddr = follow_table(mm, uaddr, write);
+ if (IS_ERR_VALUE(kaddr))
goto fault;
- }
- pfn = pte_pfn(*pte);
- return (pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE - 1));
+ return kaddr;
fault:
spin_unlock(&mm->page_table_lock);
- rc = __handle_fault(uaddr, (unsigned long) pte, 0);
+ rc = __handle_fault(uaddr, -kaddr, write);
spin_lock(&mm->page_table_lock);
if (!rc)
goto retry;
@@ -159,11 +167,9 @@ static size_t clear_user_pt(size_t n, void __user *to)
static size_t strnlen_user_pt(size_t count, const char __user *src)
{
- char *addr;
unsigned long uaddr = (unsigned long) src;
struct mm_struct *mm = current->mm;
- unsigned long offset, pfn, done, len;
- pte_t *pte;
+ unsigned long offset, done, len, kaddr;
size_t len_str;
if (segment_eq(get_fs(), KERNEL_DS))
@@ -172,19 +178,13 @@ static size_t strnlen_user_pt(size_t count, const char __user *src)
retry:
spin_lock(&mm->page_table_lock);
do {
- pte = follow_table(mm, uaddr);
- if ((unsigned long) pte < 0x1000)
- goto fault;
- if (!pte_present(*pte)) {
- pte = (pte_t *) 0x11;
+ kaddr = follow_table(mm, uaddr, 0);
+ if (IS_ERR_VALUE(kaddr))
goto fault;
- }
- pfn = pte_pfn(*pte);
- offset = uaddr & (PAGE_SIZE-1);
- addr = (char *)(pfn << PAGE_SHIFT) + offset;
+ offset = uaddr & ~PAGE_MASK;
len = min(count - done, PAGE_SIZE - offset);
- len_str = strnlen(addr, len);
+ len_str = strnlen((char *) kaddr, len);
done += len_str;
uaddr += len_str;
} while ((len_str == len) && (done < count));
@@ -192,7 +192,7 @@ retry:
return done + 1;
fault:
spin_unlock(&mm->page_table_lock);
- if (__handle_fault(uaddr, (unsigned long) pte, 0))
+ if (__handle_fault(uaddr, -kaddr, 0))
return 0;
goto retry;
}
@@ -225,11 +225,10 @@ static size_t copy_in_user_pt(size_t n, void __user *to,
const void __user *from)
{
struct mm_struct *mm = current->mm;
- unsigned long offset_from, offset_to, offset_max, pfn_from, pfn_to,
- uaddr, done, size, error_code;
+ unsigned long offset_max, uaddr, done, size, error_code;
unsigned long uaddr_from = (unsigned long) from;
unsigned long uaddr_to = (unsigned long) to;
- pte_t *pte_from, *pte_to;
+ unsigned long kaddr_to, kaddr_from;
int write_user;
if (segment_eq(get_fs(), KERNEL_DS)) {
@@ -242,38 +241,23 @@ retry:
do {
write_user = 0;
uaddr = uaddr_from;
- pte_from = follow_table(mm, uaddr_from);
- error_code = (unsigned long) pte_from;
- if (error_code < 0x1000)
- goto fault;
- if (!pte_present(*pte_from)) {
- error_code = 0x11;
+ kaddr_from = follow_table(mm, uaddr_from, 0);
+ error_code = kaddr_from;
+ if (IS_ERR_VALUE(error_code))
goto fault;
- }
write_user = 1;
uaddr = uaddr_to;
- pte_to = follow_table(mm, uaddr_to);
- error_code = (unsigned long) pte_to;
- if (error_code < 0x1000)
- goto fault;
- if (!pte_present(*pte_to)) {
- error_code = 0x11;
+ kaddr_to = follow_table(mm, uaddr_to, 1);
+ error_code = (unsigned long) kaddr_to;
+ if (IS_ERR_VALUE(error_code))
goto fault;
- } else if (!pte_write(*pte_to)) {
- error_code = 0x04;
- goto fault;
- }
- pfn_from = pte_pfn(*pte_from);
- pfn_to = pte_pfn(*pte_to);
- offset_from = uaddr_from & (PAGE_SIZE-1);
- offset_to = uaddr_from & (PAGE_SIZE-1);
- offset_max = max(offset_from, offset_to);
+ offset_max = max(uaddr_from & ~PAGE_MASK,
+ uaddr_to & ~PAGE_MASK);
size = min(n - done, PAGE_SIZE - offset_max);
- memcpy((void *)(pfn_to << PAGE_SHIFT) + offset_to,
- (void *)(pfn_from << PAGE_SHIFT) + offset_from, size);
+ memcpy((void *) kaddr_to, (void *) kaddr_from, size);
done += size;
uaddr_from += size;
uaddr_to += size;
@@ -282,7 +266,7 @@ retry:
return n - done;
fault:
spin_unlock(&mm->page_table_lock);
- if (__handle_fault(uaddr, error_code, write_user))
+ if (__handle_fault(uaddr, -error_code, write_user))
return n - done;
goto retry;
}
@@ -341,7 +325,7 @@ int futex_atomic_op_pt(int op, u32 __user *uaddr, int oparg, int *old)
return __futex_atomic_op_pt(op, uaddr, oparg, old);
spin_lock(&current->mm->page_table_lock);
uaddr = (u32 __force __user *)
- __dat_user_addr((__force unsigned long) uaddr);
+ __dat_user_addr((__force unsigned long) uaddr, 1);
if (!uaddr) {
spin_unlock(&current->mm->page_table_lock);
return -EFAULT;
@@ -378,7 +362,7 @@ int futex_atomic_cmpxchg_pt(u32 *uval, u32 __user *uaddr,
return __futex_atomic_cmpxchg_pt(uval, uaddr, oldval, newval);
spin_lock(&current->mm->page_table_lock);
uaddr = (u32 __force __user *)
- __dat_user_addr((__force unsigned long) uaddr);
+ __dat_user_addr((__force unsigned long) uaddr, 1);
if (!uaddr) {
spin_unlock(&current->mm->page_table_lock);
return -EFAULT;
diff --git a/arch/s390/mm/Makefile b/arch/s390/mm/Makefile
index d98fe9004a52..0f5536b0c1a1 100644
--- a/arch/s390/mm/Makefile
+++ b/arch/s390/mm/Makefile
@@ -3,7 +3,7 @@
#
obj-y := init.o fault.o extmem.o mmap.o vmem.o pgtable.o maccess.o \
- page-states.o gup.o
+ page-states.o gup.o extable.o
obj-$(CONFIG_CMM) += cmm.o
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
obj-$(CONFIG_DEBUG_SET_MODULE_RONX) += pageattr.o
diff --git a/arch/s390/mm/extable.c b/arch/s390/mm/extable.c
new file mode 100644
index 000000000000..4d1ee88864e8
--- /dev/null
+++ b/arch/s390/mm/extable.c
@@ -0,0 +1,81 @@
+#include <linux/module.h>
+#include <linux/sort.h>
+#include <asm/uaccess.h>
+
+/*
+ * Search one exception table for an entry corresponding to the
+ * given instruction address, and return the address of the entry,
+ * or NULL if none is found.
+ * We use a binary search, and thus we assume that the table is
+ * already sorted.
+ */
+const struct exception_table_entry *
+search_extable(const struct exception_table_entry *first,
+ const struct exception_table_entry *last,
+ unsigned long value)
+{
+ const struct exception_table_entry *mid;
+ unsigned long addr;
+
+ while (first <= last) {
+ mid = ((last - first) >> 1) + first;
+ addr = extable_insn(mid);
+ if (addr < value)
+ first = mid + 1;
+ else if (addr > value)
+ last = mid - 1;
+ else
+ return mid;
+ }
+ return NULL;
+}
+
+/*
+ * The exception table needs to be sorted so that the binary
+ * search that we use to find entries in it works properly.
+ * This is used both for the kernel exception table and for
+ * the exception tables of modules that get loaded.
+ *
+ */
+static int cmp_ex(const void *a, const void *b)
+{
+ const struct exception_table_entry *x = a, *y = b;
+
+ /* This compare is only valid after normalization. */
+ return x->insn - y->insn;
+}
+
+void sort_extable(struct exception_table_entry *start,
+ struct exception_table_entry *finish)
+{
+ struct exception_table_entry *p;
+ int i;
+
+ /* Normalize entries to being relative to the start of the section */
+ for (p = start, i = 0; p < finish; p++, i += 8)
+ p->insn += i;
+ sort(start, finish - start, sizeof(*start), cmp_ex, NULL);
+ /* Denormalize all entries */
+ for (p = start, i = 0; p < finish; p++, i += 8)
+ p->insn -= i;
+}
+
+#ifdef CONFIG_MODULES
+/*
+ * If the exception table is sorted, any referring to the module init
+ * will be at the beginning or the end.
+ */
+void trim_init_extable(struct module *m)
+{
+ /* Trim the beginning */
+ while (m->num_exentries &&
+ within_module_init(extable_insn(&m->extable[0]), m)) {
+ m->extable++;
+ m->num_exentries--;
+ }
+ /* Trim the end */
+ while (m->num_exentries &&
+ within_module_init(extable_insn(&m->extable[m->num_exentries-1]), m))
+ m->num_exentries--;
+}
+#endif /* CONFIG_MODULES */
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c
index 6c013f544146..ac9122ca1152 100644
--- a/arch/s390/mm/fault.c
+++ b/arch/s390/mm/fault.c
@@ -111,7 +111,7 @@ static inline int user_space_fault(unsigned long trans_exc_code)
if (trans_exc_code == 2)
/* Access via secondary space, set_fs setting decides */
return current->thread.mm_segment.ar4;
- if (addressing_mode == HOME_SPACE_MODE)
+ if (s390_user_mode == HOME_SPACE_MODE)
/* User space if the access has been done via home space. */
return trans_exc_code == 3;
/*
@@ -163,7 +163,7 @@ static noinline void do_no_context(struct pt_regs *regs)
/* Are we prepared to handle this kernel fault? */
fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
if (fixup) {
- regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE;
+ regs->psw.addr = extable_fixup(fixup) | PSW_ADDR_AMODE;
return;
}
@@ -628,9 +628,8 @@ static int __cpuinit pfault_cpu_notify(struct notifier_block *self,
struct thread_struct *thread, *next;
struct task_struct *tsk;
- switch (action) {
+ switch (action & ~CPU_TASKS_FROZEN) {
case CPU_DEAD:
- case CPU_DEAD_FROZEN:
spin_lock_irq(&pfault_lock);
list_for_each_entry_safe(thread, next, &pfault_list, list) {
thread->pfault_wait = 0;
diff --git a/arch/s390/mm/gup.c b/arch/s390/mm/gup.c
index 65cb06e2af4e..eeaf8023851f 100644
--- a/arch/s390/mm/gup.c
+++ b/arch/s390/mm/gup.c
@@ -154,6 +154,43 @@ static inline int gup_pud_range(pgd_t *pgdp, pgd_t pgd, unsigned long addr,
return 1;
}
+/*
+ * Like get_user_pages_fast() except its IRQ-safe in that it won't fall
+ * back to the regular GUP.
+ */
+int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
+ struct page **pages)
+{
+ struct mm_struct *mm = current->mm;
+ unsigned long addr, len, end;
+ unsigned long next, flags;
+ pgd_t *pgdp, pgd;
+ int nr = 0;
+
+ start &= PAGE_MASK;
+ addr = start;
+ len = (unsigned long) nr_pages << PAGE_SHIFT;
+ end = start + len;
+ if (unlikely(!access_ok(write ? VERIFY_WRITE : VERIFY_READ,
+ (void __user *)start, len)))
+ return 0;
+
+ local_irq_save(flags);
+ pgdp = pgd_offset(mm, addr);
+ do {
+ pgd = *pgdp;
+ barrier();
+ next = pgd_addr_end(addr, end);
+ if (pgd_none(pgd))
+ break;
+ if (!gup_pud_range(pgdp, pgd, addr, next, write, pages, &nr))
+ break;
+ } while (pgdp++, addr = next, addr != end);
+ local_irq_restore(flags);
+
+ return nr;
+}
+
/**
* get_user_pages_fast() - pin user pages in memory
* @start: starting user address
diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c
index 6adbc082618a..81e596c65dee 100644
--- a/arch/s390/mm/init.c
+++ b/arch/s390/mm/init.c
@@ -42,7 +42,7 @@ pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((__aligned__(PAGE_SIZE)));
unsigned long empty_zero_page, zero_page_mask;
EXPORT_SYMBOL(empty_zero_page);
-static unsigned long setup_zero_pages(void)
+static unsigned long __init setup_zero_pages(void)
{
struct cpuid cpu_id;
unsigned int order;
@@ -212,7 +212,7 @@ void free_initmem(void)
}
#ifdef CONFIG_BLK_DEV_INITRD
-void free_initrd_mem(unsigned long start, unsigned long end)
+void __init free_initrd_mem(unsigned long start, unsigned long end)
{
free_init_pages("initrd memory", start, end);
}
diff --git a/arch/s390/mm/pgtable.c b/arch/s390/mm/pgtable.c
index 18df31d1f2c9..b402991e43d7 100644
--- a/arch/s390/mm/pgtable.c
+++ b/arch/s390/mm/pgtable.c
@@ -609,8 +609,8 @@ static inline unsigned int atomic_xor_bits(atomic_t *v, unsigned int bits)
*/
unsigned long *page_table_alloc(struct mm_struct *mm, unsigned long vmaddr)
{
- struct page *page;
- unsigned long *table;
+ unsigned long *uninitialized_var(table);
+ struct page *uninitialized_var(page);
unsigned int mask, bit;
if (mm_has_pgste(mm))
@@ -796,7 +796,7 @@ int s390_enable_sie(void)
struct mm_struct *mm, *old_mm;
/* Do we have switched amode? If no, we cannot do sie */
- if (addressing_mode == HOME_SPACE_MODE)
+ if (s390_user_mode == HOME_SPACE_MODE)
return -EINVAL;
/* Do we have pgstes? if yes, we are done */
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
index 6f896e75ab49..c22abf900c9e 100644
--- a/arch/s390/mm/vmem.c
+++ b/arch/s390/mm/vmem.c
@@ -107,7 +107,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0));
pm_dir = pmd_offset(pu_dir, address);
-#ifdef CONFIG_64BIT
+#if defined(CONFIG_64BIT) && !defined(CONFIG_DEBUG_PAGEALLOC)
if (MACHINE_HAS_HPAGE && !(address & ~HPAGE_MASK) &&
(address + HPAGE_SIZE <= start + size) &&
(address >= HPAGE_SIZE)) {
diff --git a/arch/s390/net/Makefile b/arch/s390/net/Makefile
new file mode 100644
index 000000000000..90568c33ddb0
--- /dev/null
+++ b/arch/s390/net/Makefile
@@ -0,0 +1,4 @@
+#
+# Arch-specific network modules
+#
+obj-$(CONFIG_BPF_JIT) += bpf_jit.o bpf_jit_comp.o
diff --git a/arch/s390/net/bpf_jit.S b/arch/s390/net/bpf_jit.S
new file mode 100644
index 000000000000..7e45d13816c1
--- /dev/null
+++ b/arch/s390/net/bpf_jit.S
@@ -0,0 +1,130 @@
+/*
+ * BPF Jit compiler for s390, help functions.
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+#include <linux/linkage.h>
+
+/*
+ * Calling convention:
+ * registers %r2, %r6-%r8, %r10-%r11, %r13, %r15 are call saved
+ * %r2: skb pointer
+ * %r3: offset parameter
+ * %r5: BPF A accumulator
+ * %r8: return address
+ * %r9: save register for skb pointer
+ * %r10: skb->data
+ * %r11: skb->len - skb->data_len (headlen)
+ * %r12: BPF X accumulator
+ *
+ * skb_copy_bits takes 4 parameters:
+ * %r2 = skb pointer
+ * %r3 = offset into skb data
+ * %r4 = length to copy
+ * %r5 = pointer to temp buffer
+ */
+#define SKBDATA %r8
+
+ /* A = *(u32 *) (skb->data+K+X) */
+ENTRY(sk_load_word_ind)
+ ar %r3,%r12 # offset += X
+ bmr %r8 # < 0 -> return with cc
+
+ /* A = *(u32 *) (skb->data+K) */
+ENTRY(sk_load_word)
+ llgfr %r1,%r3 # extend offset
+ ahi %r3,4 # offset + 4
+ clr %r11,%r3 # hlen <= offset + 4 ?
+ jl sk_load_word_slow
+ l %r5,0(%r1,%r10) # get word from skb
+ xr %r1,%r1 # set cc to zero
+ br %r8
+
+sk_load_word_slow:
+ lgr %r9,%r2 # save %r2
+ lhi %r4,4 # 4 bytes
+ la %r5,160(%r15) # pointer to temp buffer
+ brasl %r14,skb_copy_bits # get data from skb
+ l %r5,160(%r15) # load result from temp buffer
+ ltgr %r2,%r2 # set cc to (%r2 != 0)
+ lgr %r2,%r9 # restore %r2
+ br %r8
+
+ /* A = *(u16 *) (skb->data+K+X) */
+ENTRY(sk_load_half_ind)
+ ar %r3,%r12 # offset += X
+ bmr %r8 # < 0 -> return with cc
+
+ /* A = *(u16 *) (skb->data+K) */
+ENTRY(sk_load_half)
+ llgfr %r1,%r3 # extend offset
+ ahi %r3,2 # offset + 2
+ clr %r11,%r3 # hlen <= offset + 2 ?
+ jl sk_load_half_slow
+ llgh %r5,0(%r1,%r10) # get half from skb
+ xr %r1,%r1 # set cc to zero
+ br %r8
+
+sk_load_half_slow:
+ lgr %r9,%r2 # save %r2
+ lhi %r4,2 # 2 bytes
+ la %r5,162(%r15) # pointer to temp buffer
+ brasl %r14,skb_copy_bits # get data from skb
+ xc 160(2,%r15),160(%r15)
+ l %r5,160(%r15) # load result from temp buffer
+ ltgr %r2,%r2 # set cc to (%r2 != 0)
+ lgr %r2,%r9 # restore %r2
+ br %r8
+
+ /* A = *(u8 *) (skb->data+K+X) */
+ENTRY(sk_load_byte_ind)
+ ar %r3,%r12 # offset += X
+ bmr %r8 # < 0 -> return with cc
+
+ /* A = *(u8 *) (skb->data+K) */
+ENTRY(sk_load_byte)
+ llgfr %r1,%r3 # extend offset
+ clr %r11,%r3 # hlen < offset ?
+ jle sk_load_byte_slow
+ lhi %r5,0
+ ic %r5,0(%r1,%r10) # get byte from skb
+ xr %r1,%r1 # set cc to zero
+ br %r8
+
+sk_load_byte_slow:
+ lgr %r9,%r2 # save %r2
+ lhi %r4,1 # 1 bytes
+ la %r5,163(%r15) # pointer to temp buffer
+ brasl %r14,skb_copy_bits # get data from skb
+ xc 160(3,%r15),160(%r15)
+ l %r5,160(%r15) # load result from temp buffer
+ ltgr %r2,%r2 # set cc to (%r2 != 0)
+ lgr %r2,%r9 # restore %r2
+ br %r8
+
+ /* A = (*(u8 *)(skb->data+K) & 0xf) << 2 */
+ENTRY(sk_load_byte_msh)
+ llgfr %r1,%r3 # extend offset
+ clr %r11,%r3 # hlen < offset ?
+ jle sk_load_byte_slow
+ lhi %r12,0
+ ic %r12,0(%r1,%r10) # get byte from skb
+ nill %r12,0x0f
+ sll %r12,2
+ xr %r1,%r1 # set cc to zero
+ br %r8
+
+sk_load_byte_msh_slow:
+ lgr %r9,%r2 # save %r2
+ lhi %r4,2 # 2 bytes
+ la %r5,162(%r15) # pointer to temp buffer
+ brasl %r14,skb_copy_bits # get data from skb
+ xc 160(3,%r15),160(%r15)
+ l %r12,160(%r15) # load result from temp buffer
+ nill %r12,0x0f
+ sll %r12,2
+ ltgr %r2,%r2 # set cc to (%r2 != 0)
+ lgr %r2,%r9 # restore %r2
+ br %r8
diff --git a/arch/s390/net/bpf_jit_comp.c b/arch/s390/net/bpf_jit_comp.c
new file mode 100644
index 000000000000..9b355b406afa
--- /dev/null
+++ b/arch/s390/net/bpf_jit_comp.c
@@ -0,0 +1,776 @@
+/*
+ * BPF Jit compiler for s390.
+ *
+ * Copyright IBM Corp. 2012
+ *
+ * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
+ */
+#include <linux/moduleloader.h>
+#include <linux/netdevice.h>
+#include <linux/filter.h>
+#include <asm/cacheflush.h>
+#include <asm/processor.h>
+#include <asm/facility.h>
+
+/*
+ * Conventions:
+ * %r2 = skb pointer
+ * %r3 = offset parameter
+ * %r4 = scratch register / length parameter
+ * %r5 = BPF A accumulator
+ * %r8 = return address
+ * %r9 = save register for skb pointer
+ * %r10 = skb->data
+ * %r11 = skb->len - skb->data_len (headlen)
+ * %r12 = BPF X accumulator
+ * %r13 = literal pool pointer
+ * 0(%r15) - 63(%r15) scratch memory array with BPF_MEMWORDS
+ */
+int bpf_jit_enable __read_mostly;
+
+/*
+ * assembly code in arch/x86/net/bpf_jit.S
+ */
+extern u8 sk_load_word[], sk_load_half[], sk_load_byte[], sk_load_byte_msh[];
+extern u8 sk_load_word_ind[], sk_load_half_ind[], sk_load_byte_ind[];
+
+struct bpf_jit {
+ unsigned int seen;
+ u8 *start;
+ u8 *prg;
+ u8 *mid;
+ u8 *lit;
+ u8 *end;
+ u8 *base_ip;
+ u8 *ret0_ip;
+ u8 *exit_ip;
+ unsigned int off_load_word;
+ unsigned int off_load_half;
+ unsigned int off_load_byte;
+ unsigned int off_load_bmsh;
+ unsigned int off_load_iword;
+ unsigned int off_load_ihalf;
+ unsigned int off_load_ibyte;
+};
+
+#define BPF_SIZE_MAX 4096 /* Max size for program */
+
+#define SEEN_DATAREF 1 /* might call external helpers */
+#define SEEN_XREG 2 /* ebx is used */
+#define SEEN_MEM 4 /* use mem[] for temporary storage */
+#define SEEN_RET0 8 /* pc_ret0 points to a valid return 0 */
+#define SEEN_LITERAL 16 /* code uses literals */
+#define SEEN_LOAD_WORD 32 /* code uses sk_load_word */
+#define SEEN_LOAD_HALF 64 /* code uses sk_load_half */
+#define SEEN_LOAD_BYTE 128 /* code uses sk_load_byte */
+#define SEEN_LOAD_BMSH 256 /* code uses sk_load_byte_msh */
+#define SEEN_LOAD_IWORD 512 /* code uses sk_load_word_ind */
+#define SEEN_LOAD_IHALF 1024 /* code uses sk_load_half_ind */
+#define SEEN_LOAD_IBYTE 2048 /* code uses sk_load_byte_ind */
+
+#define EMIT2(op) \
+({ \
+ if (jit->prg + 2 <= jit->mid) \
+ *(u16 *) jit->prg = op; \
+ jit->prg += 2; \
+})
+
+#define EMIT4(op) \
+({ \
+ if (jit->prg + 4 <= jit->mid) \
+ *(u32 *) jit->prg = op; \
+ jit->prg += 4; \
+})
+
+#define EMIT4_DISP(op, disp) \
+({ \
+ unsigned int __disp = (disp) & 0xfff; \
+ EMIT4(op | __disp); \
+})
+
+#define EMIT4_IMM(op, imm) \
+({ \
+ unsigned int __imm = (imm) & 0xffff; \
+ EMIT4(op | __imm); \
+})
+
+#define EMIT4_PCREL(op, pcrel) \
+({ \
+ long __pcrel = ((pcrel) >> 1) & 0xffff; \
+ EMIT4(op | __pcrel); \
+})
+
+#define EMIT6(op1, op2) \
+({ \
+ if (jit->prg + 6 <= jit->mid) { \
+ *(u32 *) jit->prg = op1; \
+ *(u16 *) (jit->prg + 4) = op2; \
+ } \
+ jit->prg += 6; \
+})
+
+#define EMIT6_DISP(op1, op2, disp) \
+({ \
+ unsigned int __disp = (disp) & 0xfff; \
+ EMIT6(op1 | __disp, op2); \
+})
+
+#define EMIT6_IMM(op, imm) \
+({ \
+ unsigned int __imm = (imm); \
+ EMIT6(op | (__imm >> 16), __imm & 0xffff); \
+})
+
+#define EMIT_CONST(val) \
+({ \
+ unsigned int ret; \
+ ret = (unsigned int) (jit->lit - jit->base_ip); \
+ jit->seen |= SEEN_LITERAL; \
+ if (jit->lit + 4 <= jit->end) \
+ *(u32 *) jit->lit = val; \
+ jit->lit += 4; \
+ ret; \
+})
+
+#define EMIT_FN_CONST(bit, fn) \
+({ \
+ unsigned int ret; \
+ ret = (unsigned int) (jit->lit - jit->base_ip); \
+ if (jit->seen & bit) { \
+ jit->seen |= SEEN_LITERAL; \
+ if (jit->lit + 8 <= jit->end) \
+ *(void **) jit->lit = fn; \
+ jit->lit += 8; \
+ } \
+ ret; \
+})
+
+static void bpf_jit_prologue(struct bpf_jit *jit)
+{
+ /* Save registers and create stack frame if necessary */
+ if (jit->seen & SEEN_DATAREF) {
+ /* stmg %r8,%r15,88(%r15) */
+ EMIT6(0xeb8ff058, 0x0024);
+ /* lgr %r14,%r15 */
+ EMIT4(0xb90400ef);
+ /* ahi %r15,<offset> */
+ EMIT4_IMM(0xa7fa0000, (jit->seen & SEEN_MEM) ? -112 : -80);
+ /* stg %r14,152(%r15) */
+ EMIT6(0xe3e0f098, 0x0024);
+ } else if ((jit->seen & SEEN_XREG) && (jit->seen & SEEN_LITERAL))
+ /* stmg %r12,%r13,120(%r15) */
+ EMIT6(0xebcdf078, 0x0024);
+ else if (jit->seen & SEEN_XREG)
+ /* stg %r12,120(%r15) */
+ EMIT6(0xe3c0f078, 0x0024);
+ else if (jit->seen & SEEN_LITERAL)
+ /* stg %r13,128(%r15) */
+ EMIT6(0xe3d0f080, 0x0024);
+
+ /* Setup literal pool */
+ if (jit->seen & SEEN_LITERAL) {
+ /* basr %r13,0 */
+ EMIT2(0x0dd0);
+ jit->base_ip = jit->prg;
+ }
+ jit->off_load_word = EMIT_FN_CONST(SEEN_LOAD_WORD, sk_load_word);
+ jit->off_load_half = EMIT_FN_CONST(SEEN_LOAD_HALF, sk_load_half);
+ jit->off_load_byte = EMIT_FN_CONST(SEEN_LOAD_BYTE, sk_load_byte);
+ jit->off_load_bmsh = EMIT_FN_CONST(SEEN_LOAD_BMSH, sk_load_byte_msh);
+ jit->off_load_iword = EMIT_FN_CONST(SEEN_LOAD_IWORD, sk_load_word_ind);
+ jit->off_load_ihalf = EMIT_FN_CONST(SEEN_LOAD_IHALF, sk_load_half_ind);
+ jit->off_load_ibyte = EMIT_FN_CONST(SEEN_LOAD_IBYTE, sk_load_byte_ind);
+
+ /* Filter needs to access skb data */
+ if (jit->seen & SEEN_DATAREF) {
+ /* l %r11,<len>(%r2) */
+ EMIT4_DISP(0x58b02000, offsetof(struct sk_buff, len));
+ /* s %r11,<data_len>(%r2) */
+ EMIT4_DISP(0x5bb02000, offsetof(struct sk_buff, data_len));
+ /* lg %r10,<data>(%r2) */
+ EMIT6_DISP(0xe3a02000, 0x0004,
+ offsetof(struct sk_buff, data));
+ }
+}
+
+static void bpf_jit_epilogue(struct bpf_jit *jit)
+{
+ /* Return 0 */
+ if (jit->seen & SEEN_RET0) {
+ jit->ret0_ip = jit->prg;
+ /* lghi %r2,0 */
+ EMIT4(0xa7290000);
+ }
+ jit->exit_ip = jit->prg;
+ /* Restore registers */
+ if (jit->seen & SEEN_DATAREF)
+ /* lmg %r8,%r15,<offset>(%r15) */
+ EMIT6_DISP(0xeb8ff000, 0x0004,
+ (jit->seen & SEEN_MEM) ? 200 : 168);
+ else if ((jit->seen & SEEN_XREG) && (jit->seen & SEEN_LITERAL))
+ /* lmg %r12,%r13,120(%r15) */
+ EMIT6(0xebcdf078, 0x0004);
+ else if (jit->seen & SEEN_XREG)
+ /* lg %r12,120(%r15) */
+ EMIT6(0xe3c0f078, 0x0004);
+ else if (jit->seen & SEEN_LITERAL)
+ /* lg %r13,128(%r15) */
+ EMIT6(0xe3d0f080, 0x0004);
+ /* br %r14 */
+ EMIT2(0x07fe);
+}
+
+/*
+ * make sure we dont leak kernel information to user
+ */
+static void bpf_jit_noleaks(struct bpf_jit *jit, struct sock_filter *filter)
+{
+ /* Clear temporary memory if (seen & SEEN_MEM) */
+ if (jit->seen & SEEN_MEM)
+ /* xc 0(64,%r15),0(%r15) */
+ EMIT6(0xd73ff000, 0xf000);
+ /* Clear X if (seen & SEEN_XREG) */
+ if (jit->seen & SEEN_XREG)
+ /* lhi %r12,0 */
+ EMIT4(0xa7c80000);
+ /* Clear A if the first register does not set it. */
+ switch (filter[0].code) {
+ case BPF_S_LD_W_ABS:
+ case BPF_S_LD_H_ABS:
+ case BPF_S_LD_B_ABS:
+ case BPF_S_LD_W_LEN:
+ case BPF_S_LD_W_IND:
+ case BPF_S_LD_H_IND:
+ case BPF_S_LD_B_IND:
+ case BPF_S_LDX_B_MSH:
+ case BPF_S_LD_IMM:
+ case BPF_S_LD_MEM:
+ case BPF_S_MISC_TXA:
+ case BPF_S_ANC_PROTOCOL:
+ case BPF_S_ANC_PKTTYPE:
+ case BPF_S_ANC_IFINDEX:
+ case BPF_S_ANC_MARK:
+ case BPF_S_ANC_QUEUE:
+ case BPF_S_ANC_HATYPE:
+ case BPF_S_ANC_RXHASH:
+ case BPF_S_ANC_CPU:
+ case BPF_S_RET_K:
+ /* first instruction sets A register */
+ break;
+ default: /* A = 0 */
+ /* lhi %r5,0 */
+ EMIT4(0xa7580000);
+ }
+}
+
+static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
+ unsigned int *addrs, int i, int last)
+{
+ unsigned int K;
+ int offset;
+ unsigned int mask;
+
+ K = filter->k;
+ switch (filter->code) {
+ case BPF_S_ALU_ADD_X: /* A += X */
+ jit->seen |= SEEN_XREG;
+ /* ar %r5,%r12 */
+ EMIT2(0x1a5c);
+ break;
+ case BPF_S_ALU_ADD_K: /* A += K */
+ if (!K)
+ break;
+ if (K <= 16383)
+ /* ahi %r5,<K> */
+ EMIT4_IMM(0xa75a0000, K);
+ else if (test_facility(21))
+ /* alfi %r5,<K> */
+ EMIT6_IMM(0xc25b0000, K);
+ else
+ /* a %r5,<d(K)>(%r13) */
+ EMIT4_DISP(0x5a50d000, EMIT_CONST(K));
+ break;
+ case BPF_S_ALU_SUB_X: /* A -= X */
+ jit->seen |= SEEN_XREG;
+ /* sr %r5,%r12 */
+ EMIT2(0x1b5c);
+ break;
+ case BPF_S_ALU_SUB_K: /* A -= K */
+ if (!K)
+ break;
+ if (K <= 16384)
+ /* ahi %r5,-K */
+ EMIT4_IMM(0xa75a0000, -K);
+ else if (test_facility(21))
+ /* alfi %r5,-K */
+ EMIT6_IMM(0xc25b0000, -K);
+ else
+ /* s %r5,<d(K)>(%r13) */
+ EMIT4_DISP(0x5b50d000, EMIT_CONST(K));
+ break;
+ case BPF_S_ALU_MUL_X: /* A *= X */
+ jit->seen |= SEEN_XREG;
+ /* msr %r5,%r12 */
+ EMIT4(0xb252005c);
+ break;
+ case BPF_S_ALU_MUL_K: /* A *= K */
+ if (K <= 16383)
+ /* mhi %r5,K */
+ EMIT4_IMM(0xa75c0000, K);
+ else if (test_facility(34))
+ /* msfi %r5,<K> */
+ EMIT6_IMM(0xc2510000, K);
+ else
+ /* ms %r5,<d(K)>(%r13) */
+ EMIT4_DISP(0x7150d000, EMIT_CONST(K));
+ break;
+ case BPF_S_ALU_DIV_X: /* A /= X */
+ jit->seen |= SEEN_XREG | SEEN_RET0;
+ /* ltr %r12,%r12 */
+ EMIT2(0x12cc);
+ /* jz <ret0> */
+ EMIT4_PCREL(0xa7840000, (jit->ret0_ip - jit->prg));
+ /* lhi %r4,0 */
+ EMIT4(0xa7480000);
+ /* dr %r4,%r12 */
+ EMIT2(0x1d4c);
+ break;
+ case BPF_S_ALU_DIV_K: /* A = reciprocal_divide(A, K) */
+ /* m %r4,<d(K)>(%r13) */
+ EMIT4_DISP(0x5c40d000, EMIT_CONST(K));
+ /* lr %r5,%r4 */
+ EMIT2(0x1854);
+ break;
+ case BPF_S_ALU_AND_X: /* A &= X */
+ jit->seen |= SEEN_XREG;
+ /* nr %r5,%r12 */
+ EMIT2(0x145c);
+ break;
+ case BPF_S_ALU_AND_K: /* A &= K */
+ if (test_facility(21))
+ /* nilf %r5,<K> */
+ EMIT6_IMM(0xc05b0000, K);
+ else
+ /* n %r5,<d(K)>(%r13) */
+ EMIT4_DISP(0x5450d000, EMIT_CONST(K));
+ break;
+ case BPF_S_ALU_OR_X: /* A |= X */
+ jit->seen |= SEEN_XREG;
+ /* or %r5,%r12 */
+ EMIT2(0x165c);
+ break;
+ case BPF_S_ALU_OR_K: /* A |= K */
+ if (test_facility(21))
+ /* oilf %r5,<K> */
+ EMIT6_IMM(0xc05d0000, K);
+ else
+ /* o %r5,<d(K)>(%r13) */
+ EMIT4_DISP(0x5650d000, EMIT_CONST(K));
+ break;
+ case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */
+ jit->seen |= SEEN_XREG;
+ /* xr %r5,%r12 */
+ EMIT2(0x175c);
+ break;
+ case BPF_S_ALU_LSH_X: /* A <<= X; */
+ jit->seen |= SEEN_XREG;
+ /* sll %r5,0(%r12) */
+ EMIT4(0x8950c000);
+ break;
+ case BPF_S_ALU_LSH_K: /* A <<= K */
+ if (K == 0)
+ break;
+ /* sll %r5,K */
+ EMIT4_DISP(0x89500000, K);
+ break;
+ case BPF_S_ALU_RSH_X: /* A >>= X; */
+ jit->seen |= SEEN_XREG;
+ /* srl %r5,0(%r12) */
+ EMIT4(0x8850c000);
+ break;
+ case BPF_S_ALU_RSH_K: /* A >>= K; */
+ if (K == 0)
+ break;
+ /* srl %r5,K */
+ EMIT4_DISP(0x88500000, K);
+ break;
+ case BPF_S_ALU_NEG: /* A = -A */
+ /* lnr %r5,%r5 */
+ EMIT2(0x1155);
+ break;
+ case BPF_S_JMP_JA: /* ip += K */
+ offset = addrs[i + K] + jit->start - jit->prg;
+ EMIT4_PCREL(0xa7f40000, offset);
+ break;
+ case BPF_S_JMP_JGT_K: /* ip += (A > K) ? jt : jf */
+ mask = 0x200000; /* jh */
+ goto kbranch;
+ case BPF_S_JMP_JGE_K: /* ip += (A >= K) ? jt : jf */
+ mask = 0xa00000; /* jhe */
+ goto kbranch;
+ case BPF_S_JMP_JEQ_K: /* ip += (A == K) ? jt : jf */
+ mask = 0x800000; /* je */
+kbranch: /* Emit compare if the branch targets are different */
+ if (filter->jt != filter->jf) {
+ if (K <= 16383)
+ /* chi %r5,<K> */
+ EMIT4_IMM(0xa75e0000, K);
+ else if (test_facility(21))
+ /* clfi %r5,<K> */
+ EMIT6_IMM(0xc25f0000, K);
+ else
+ /* c %r5,<d(K)>(%r13) */
+ EMIT4_DISP(0x5950d000, EMIT_CONST(K));
+ }
+branch: if (filter->jt == filter->jf) {
+ if (filter->jt == 0)
+ break;
+ /* j <jt> */
+ offset = addrs[i + filter->jt] + jit->start - jit->prg;
+ EMIT4_PCREL(0xa7f40000, offset);
+ break;
+ }
+ if (filter->jt != 0) {
+ /* brc <mask>,<jt> */
+ offset = addrs[i + filter->jt] + jit->start - jit->prg;
+ EMIT4_PCREL(0xa7040000 | mask, offset);
+ }
+ if (filter->jf != 0) {
+ /* brc <mask^15>,<jf> */
+ offset = addrs[i + filter->jf] + jit->start - jit->prg;
+ EMIT4_PCREL(0xa7040000 | (mask ^ 0xf00000), offset);
+ }
+ break;
+ case BPF_S_JMP_JSET_K: /* ip += (A & K) ? jt : jf */
+ mask = 0x700000; /* jnz */
+ /* Emit test if the branch targets are different */
+ if (filter->jt != filter->jf) {
+ if (K > 65535) {
+ /* lr %r4,%r5 */
+ EMIT2(0x1845);
+ /* n %r4,<d(K)>(%r13) */
+ EMIT4_DISP(0x5440d000, EMIT_CONST(K));
+ } else
+ /* tmll %r5,K */
+ EMIT4_IMM(0xa7510000, K);
+ }
+ goto branch;
+ case BPF_S_JMP_JGT_X: /* ip += (A > X) ? jt : jf */
+ mask = 0x200000; /* jh */
+ goto xbranch;
+ case BPF_S_JMP_JGE_X: /* ip += (A >= X) ? jt : jf */
+ mask = 0xa00000; /* jhe */
+ goto xbranch;
+ case BPF_S_JMP_JEQ_X: /* ip += (A == X) ? jt : jf */
+ mask = 0x800000; /* je */
+xbranch: /* Emit compare if the branch targets are different */
+ if (filter->jt != filter->jf) {
+ jit->seen |= SEEN_XREG;
+ /* cr %r5,%r12 */
+ EMIT2(0x195c);
+ }
+ goto branch;
+ case BPF_S_JMP_JSET_X: /* ip += (A & X) ? jt : jf */
+ mask = 0x700000; /* jnz */
+ /* Emit test if the branch targets are different */
+ if (filter->jt != filter->jf) {
+ jit->seen |= SEEN_XREG;
+ /* lr %r4,%r5 */
+ EMIT2(0x1845);
+ /* nr %r4,%r12 */
+ EMIT2(0x144c);
+ }
+ goto branch;
+ case BPF_S_LD_W_ABS: /* A = *(u32 *) (skb->data+K) */
+ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_WORD;
+ offset = jit->off_load_word;
+ goto load_abs;
+ case BPF_S_LD_H_ABS: /* A = *(u16 *) (skb->data+K) */
+ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_HALF;
+ offset = jit->off_load_half;
+ goto load_abs;
+ case BPF_S_LD_B_ABS: /* A = *(u8 *) (skb->data+K) */
+ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_BYTE;
+ offset = jit->off_load_byte;
+load_abs: if ((int) K < 0)
+ goto out;
+call_fn: /* lg %r1,<d(function)>(%r13) */
+ EMIT6_DISP(0xe310d000, 0x0004, offset);
+ /* l %r3,<d(K)>(%r13) */
+ EMIT4_DISP(0x5830d000, EMIT_CONST(K));
+ /* basr %r8,%r1 */
+ EMIT2(0x0d81);
+ /* jnz <ret0> */
+ EMIT4_PCREL(0xa7740000, (jit->ret0_ip - jit->prg));
+ break;
+ case BPF_S_LD_W_IND: /* A = *(u32 *) (skb->data+K+X) */
+ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IWORD;
+ offset = jit->off_load_iword;
+ goto call_fn;
+ case BPF_S_LD_H_IND: /* A = *(u16 *) (skb->data+K+X) */
+ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IHALF;
+ offset = jit->off_load_ihalf;
+ goto call_fn;
+ case BPF_S_LD_B_IND: /* A = *(u8 *) (skb->data+K+X) */
+ jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IBYTE;
+ offset = jit->off_load_ibyte;
+ goto call_fn;
+ case BPF_S_LDX_B_MSH:
+ /* X = (*(u8 *)(skb->data+K) & 0xf) << 2 */
+ jit->seen |= SEEN_RET0;
+ if ((int) K < 0) {
+ /* j <ret0> */
+ EMIT4_PCREL(0xa7f40000, (jit->ret0_ip - jit->prg));
+ break;
+ }
+ jit->seen |= SEEN_DATAREF | SEEN_LOAD_BMSH;
+ offset = jit->off_load_bmsh;
+ goto call_fn;
+ case BPF_S_LD_W_LEN: /* A = skb->len; */
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
+ /* l %r5,<d(len)>(%r2) */
+ EMIT4_DISP(0x58502000, offsetof(struct sk_buff, len));
+ break;
+ case BPF_S_LDX_W_LEN: /* X = skb->len; */
+ jit->seen |= SEEN_XREG;
+ /* l %r12,<d(len)>(%r2) */
+ EMIT4_DISP(0x58c02000, offsetof(struct sk_buff, len));
+ break;
+ case BPF_S_LD_IMM: /* A = K */
+ if (K <= 16383)
+ /* lhi %r5,K */
+ EMIT4_IMM(0xa7580000, K);
+ else if (test_facility(21))
+ /* llilf %r5,<K> */
+ EMIT6_IMM(0xc05f0000, K);
+ else
+ /* l %r5,<d(K)>(%r13) */
+ EMIT4_DISP(0x5850d000, EMIT_CONST(K));
+ break;
+ case BPF_S_LDX_IMM: /* X = K */
+ jit->seen |= SEEN_XREG;
+ if (K <= 16383)
+ /* lhi %r12,<K> */
+ EMIT4_IMM(0xa7c80000, K);
+ else if (test_facility(21))
+ /* llilf %r12,<K> */
+ EMIT6_IMM(0xc0cf0000, K);
+ else
+ /* l %r12,<d(K)>(%r13) */
+ EMIT4_DISP(0x58c0d000, EMIT_CONST(K));
+ break;
+ case BPF_S_LD_MEM: /* A = mem[K] */
+ jit->seen |= SEEN_MEM;
+ /* l %r5,<K>(%r15) */
+ EMIT4_DISP(0x5850f000,
+ (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
+ break;
+ case BPF_S_LDX_MEM: /* X = mem[K] */
+ jit->seen |= SEEN_XREG | SEEN_MEM;
+ /* l %r12,<K>(%r15) */
+ EMIT4_DISP(0x58c0f000,
+ (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
+ break;
+ case BPF_S_MISC_TAX: /* X = A */
+ jit->seen |= SEEN_XREG;
+ /* lr %r12,%r5 */
+ EMIT2(0x18c5);
+ break;
+ case BPF_S_MISC_TXA: /* A = X */
+ jit->seen |= SEEN_XREG;
+ /* lr %r5,%r12 */
+ EMIT2(0x185c);
+ break;
+ case BPF_S_RET_K:
+ if (K == 0) {
+ jit->seen |= SEEN_RET0;
+ if (last)
+ break;
+ /* j <ret0> */
+ EMIT4_PCREL(0xa7f40000, jit->ret0_ip - jit->prg);
+ } else {
+ if (K <= 16383)
+ /* lghi %r2,K */
+ EMIT4_IMM(0xa7290000, K);
+ else
+ /* llgf %r2,<K>(%r13) */
+ EMIT6_DISP(0xe320d000, 0x0016, EMIT_CONST(K));
+ /* j <exit> */
+ if (last && !(jit->seen & SEEN_RET0))
+ break;
+ EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg);
+ }
+ break;
+ case BPF_S_RET_A:
+ /* llgfr %r2,%r5 */
+ EMIT4(0xb9160025);
+ /* j <exit> */
+ EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg);
+ break;
+ case BPF_S_ST: /* mem[K] = A */
+ jit->seen |= SEEN_MEM;
+ /* st %r5,<K>(%r15) */
+ EMIT4_DISP(0x5050f000,
+ (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
+ break;
+ case BPF_S_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */
+ jit->seen |= SEEN_XREG | SEEN_MEM;
+ /* st %r12,<K>(%r15) */
+ EMIT4_DISP(0x50c0f000,
+ (jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
+ break;
+ case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
+ /* lhi %r5,0 */
+ EMIT4(0xa7580000);
+ /* icm %r5,3,<d(protocol)>(%r2) */
+ EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, protocol));
+ break;
+ case BPF_S_ANC_IFINDEX: /* if (!skb->dev) return 0;
+ * A = skb->dev->ifindex */
+ BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
+ jit->seen |= SEEN_RET0;
+ /* lg %r1,<d(dev)>(%r2) */
+ EMIT6_DISP(0xe3102000, 0x0004, offsetof(struct sk_buff, dev));
+ /* ltgr %r1,%r1 */
+ EMIT4(0xb9020011);
+ /* jz <ret0> */
+ EMIT4_PCREL(0xa7840000, jit->ret0_ip - jit->prg);
+ /* l %r5,<d(ifindex)>(%r1) */
+ EMIT4_DISP(0x58501000, offsetof(struct net_device, ifindex));
+ break;
+ case BPF_S_ANC_MARK: /* A = skb->mark */
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
+ /* l %r5,<d(mark)>(%r2) */
+ EMIT4_DISP(0x58502000, offsetof(struct sk_buff, mark));
+ break;
+ case BPF_S_ANC_QUEUE: /* A = skb->queue_mapping */
+ BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2);
+ /* lhi %r5,0 */
+ EMIT4(0xa7580000);
+ /* icm %r5,3,<d(queue_mapping)>(%r2) */
+ EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, queue_mapping));
+ break;
+ case BPF_S_ANC_HATYPE: /* if (!skb->dev) return 0;
+ * A = skb->dev->type */
+ BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2);
+ jit->seen |= SEEN_RET0;
+ /* lg %r1,<d(dev)>(%r2) */
+ EMIT6_DISP(0xe3102000, 0x0004, offsetof(struct sk_buff, dev));
+ /* ltgr %r1,%r1 */
+ EMIT4(0xb9020011);
+ /* jz <ret0> */
+ EMIT4_PCREL(0xa7840000, jit->ret0_ip - jit->prg);
+ /* lhi %r5,0 */
+ EMIT4(0xa7580000);
+ /* 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));
+ break;
+ case BPF_S_ANC_CPU: /* A = smp_processor_id() */
+#ifdef CONFIG_SMP
+ /* l %r5,<d(cpu_nr)> */
+ EMIT4_DISP(0x58500000, offsetof(struct _lowcore, cpu_nr));
+#else
+ /* lhi %r5,0 */
+ EMIT4(0xa7580000);
+#endif
+ break;
+ default: /* too complex, give up */
+ goto out;
+ }
+ addrs[i] = jit->prg - jit->start;
+ return 0;
+out:
+ return -1;
+}
+
+void bpf_jit_compile(struct sk_filter *fp)
+{
+ unsigned long size, prg_len, lit_len;
+ struct bpf_jit jit, cjit;
+ unsigned int *addrs;
+ int pass, i;
+
+ if (!bpf_jit_enable)
+ return;
+ addrs = kmalloc(fp->len * sizeof(*addrs), GFP_KERNEL);
+ if (addrs == NULL)
+ return;
+ memset(addrs, 0, fp->len * sizeof(*addrs));
+ memset(&jit, 0, sizeof(cjit));
+ memset(&cjit, 0, sizeof(cjit));
+
+ for (pass = 0; pass < 10; pass++) {
+ jit.prg = jit.start;
+ jit.lit = jit.mid;
+
+ bpf_jit_prologue(&jit);
+ bpf_jit_noleaks(&jit, fp->insns);
+ for (i = 0; i < fp->len; i++) {
+ if (bpf_jit_insn(&jit, fp->insns + i, addrs, i,
+ i == fp->len - 1))
+ goto out;
+ }
+ bpf_jit_epilogue(&jit);
+ if (jit.start) {
+ WARN_ON(jit.prg > cjit.prg || jit.lit > cjit.lit);
+ if (memcmp(&jit, &cjit, sizeof(jit)) == 0)
+ break;
+ } else if (jit.prg == cjit.prg && jit.lit == cjit.lit) {
+ prg_len = jit.prg - jit.start;
+ lit_len = jit.lit - jit.mid;
+ size = max_t(unsigned long, prg_len + lit_len,
+ sizeof(struct work_struct));
+ if (size >= BPF_SIZE_MAX)
+ goto out;
+ jit.start = module_alloc(size);
+ if (!jit.start)
+ goto out;
+ jit.prg = jit.mid = jit.start + prg_len;
+ jit.lit = jit.end = jit.start + prg_len + lit_len;
+ jit.base_ip += (unsigned long) jit.start;
+ jit.exit_ip += (unsigned long) jit.start;
+ jit.ret0_ip += (unsigned long) jit.start;
+ }
+ cjit = jit;
+ }
+ if (bpf_jit_enable > 1) {
+ pr_err("flen=%d proglen=%lu pass=%d image=%p\n",
+ fp->len, jit.end - jit.start, pass, jit.start);
+ if (jit.start) {
+ printk(KERN_ERR "JIT code:\n");
+ print_fn_code(jit.start, jit.mid - jit.start);
+ print_hex_dump(KERN_ERR, "JIT literals:\n",
+ DUMP_PREFIX_ADDRESS, 16, 1,
+ jit.mid, jit.end - jit.mid, false);
+ }
+ }
+ if (jit.start)
+ fp->bpf_func = (void *) jit.start;
+out:
+ kfree(addrs);
+}
+
+static void jit_free_defer(struct work_struct *arg)
+{
+ module_free(NULL, arg);
+}
+
+/* run from softirq, we must use a work_struct to call
+ * module_free() from process context
+ */
+void bpf_jit_free(struct sk_filter *fp)
+{
+ struct work_struct *work;
+
+ if (fp->bpf_func == sk_run_filter)
+ return;
+ work = (struct work_struct *)fp->bpf_func;
+ INIT_WORK(work, jit_free_defer);
+ schedule_work(work);
+}
diff --git a/arch/score/kernel/process.c b/arch/score/kernel/process.c
index 2707023c7563..637970cfd3f4 100644
--- a/arch/score/kernel/process.c
+++ b/arch/score/kernel/process.c
@@ -27,6 +27,7 @@
#include <linux/reboot.h>
#include <linux/elfcore.h>
#include <linux/pm.h>
+#include <linux/rcupdate.h>
void (*pm_power_off)(void);
EXPORT_SYMBOL(pm_power_off);
@@ -50,9 +51,10 @@ void __noreturn cpu_idle(void)
{
/* endless idle loop with no priority at all */
while (1) {
+ rcu_idle_enter();
while (!need_resched())
barrier();
-
+ rcu_idle_exit();
schedule_preempt_disabled();
}
}
diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c
index 40db2d0aef3f..a7e078f2e2e4 100644
--- a/arch/sh/drivers/pci/pci.c
+++ b/arch/sh/drivers/pci/pci.c
@@ -192,11 +192,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
return pci_enable_resources(dev, mask);
}
-void __init pcibios_update_irq(struct pci_dev *dev, int irq)
-{
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-}
-
static void __init
pcibios_bus_report_status_early(struct pci_channel *hose,
int top_bus, int current_bus,
diff --git a/arch/sh/kernel/cpu/sh5/entry.S b/arch/sh/kernel/cpu/sh5/entry.S
index b7cf6a547f11..7e605b95592a 100644
--- a/arch/sh/kernel/cpu/sh5/entry.S
+++ b/arch/sh/kernel/cpu/sh5/entry.S
@@ -933,7 +933,7 @@ ret_with_reschedule:
pta restore_all, tr1
- movi _TIF_SIGPENDING, r8
+ movi (_TIF_SIGPENDING|_TIF_NOTIFY_RESUME), r8
and r8, r7, r8
pta work_notifysig, tr0
bne r8, ZERO, tr0
diff --git a/arch/sh/kernel/entry-common.S b/arch/sh/kernel/entry-common.S
index f67601cb3f1f..b96489d8b27d 100644
--- a/arch/sh/kernel/entry-common.S
+++ b/arch/sh/kernel/entry-common.S
@@ -139,7 +139,7 @@ work_pending:
! r8: current_thread_info
! t: result of "tst #_TIF_NEED_RESCHED, r0"
bf/s work_resched
- tst #_TIF_SIGPENDING, r0
+ tst #(_TIF_SIGPENDING | _TIF_NOTIFY_RESUME), r0
work_notifysig:
bt/s __restore_all
mov r15, r4
diff --git a/arch/sparc/kernel/leon_pci.c b/arch/sparc/kernel/leon_pci.c
index 21dcda75a520..fc0521161568 100644
--- a/arch/sparc/kernel/leon_pci.c
+++ b/arch/sparc/kernel/leon_pci.c
@@ -102,15 +102,6 @@ int pcibios_enable_device(struct pci_dev *dev, int mask)
return pci_enable_resources(dev, mask);
}
-void __devinit pcibios_update_irq(struct pci_dev *dev, int irq)
-{
-#ifdef CONFIG_PCI_DEBUG
- printk(KERN_DEBUG "LEONPCI: Assigning IRQ %02d to %s\n", irq,
- pci_name(dev));
-#endif
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-}
-
/* in/out routines taken from pcic.c
*
* This probably belongs here rather than ioport.c because
diff --git a/arch/sparc/kernel/module.c b/arch/sparc/kernel/module.c
index 15e0a1693976..f1ddc0d23679 100644
--- a/arch/sparc/kernel/module.c
+++ b/arch/sparc/kernel/module.c
@@ -48,9 +48,7 @@ void *module_alloc(unsigned long size)
return NULL;
ret = module_map(size);
- if (!ret)
- ret = ERR_PTR(-ENOMEM);
- else
+ if (ret)
memset(ret, 0, size);
return ret;
@@ -116,6 +114,10 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
v = sym->st_value + rel[i].r_addend;
switch (ELF_R_TYPE(rel[i].r_info) & 0xff) {
+ case R_SPARC_DISP32:
+ v -= (Elf_Addr) location;
+ *loc32 = v;
+ break;
#ifdef CONFIG_SPARC64
case R_SPARC_64:
location[0] = v >> 56;
@@ -128,11 +130,6 @@ int apply_relocate_add(Elf_Shdr *sechdrs,
location[7] = v >> 0;
break;
- case R_SPARC_DISP32:
- v -= (Elf_Addr) location;
- *loc32 = v;
- break;
-
case R_SPARC_WDISP19:
v -= (Elf_Addr) location;
*loc32 = (*loc32 & ~0x7ffff) |
diff --git a/arch/sparc/kernel/pci.c b/arch/sparc/kernel/pci.c
index 065b88c4f868..acc8c838ff72 100644
--- a/arch/sparc/kernel/pci.c
+++ b/arch/sparc/kernel/pci.c
@@ -622,10 +622,6 @@ void __devinit pcibios_fixup_bus(struct pci_bus *pbus)
{
}
-void pcibios_update_irq(struct pci_dev *pdev, int irq)
-{
-}
-
resource_size_t pcibios_align_resource(void *data, const struct resource *res,
resource_size_t size, resource_size_t align)
{
diff --git a/arch/tile/Kconfig b/arch/tile/Kconfig
index 932e4430f7f3..c9a3c1fe7297 100644
--- a/arch/tile/Kconfig
+++ b/arch/tile/Kconfig
@@ -412,14 +412,6 @@ config TILE_USB
config NEED_BOUNCE_POOL
def_bool USB_OHCI_HCD
-config HOTPLUG
- bool "Support for hot-pluggable devices"
- ---help---
- Say Y here if you want to plug devices into your computer while
- the system is running, and be able to use them quickly. In many
- cases, the devices can likewise be unplugged at any time too.
- One well-known example of this is USB.
-
source "drivers/pci/hotplug/Kconfig"
endmenu
diff --git a/arch/tile/include/asm/topology.h b/arch/tile/include/asm/topology.h
index 7a7ce390534f..d5e86c9f74fd 100644
--- a/arch/tile/include/asm/topology.h
+++ b/arch/tile/include/asm/topology.h
@@ -69,7 +69,6 @@ static inline const struct cpumask *cpumask_of_node(int node)
| 1*SD_BALANCE_FORK \
| 0*SD_BALANCE_WAKE \
| 0*SD_WAKE_AFFINE \
- | 0*SD_PREFER_LOCAL \
| 0*SD_SHARE_CPUPOWER \
| 0*SD_SHARE_PKG_RESOURCES \
| 0*SD_SERIALIZE \
diff --git a/arch/tile/include/gxio/iorpc_trio.h b/arch/tile/include/gxio/iorpc_trio.h
index 15fb77992083..58105c31228b 100644
--- a/arch/tile/include/gxio/iorpc_trio.h
+++ b/arch/tile/include/gxio/iorpc_trio.h
@@ -25,21 +25,23 @@
#include <linux/module.h>
#include <asm/pgtable.h>
-#define GXIO_TRIO_OP_ALLOC_ASIDS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1400)
+#define GXIO_TRIO_OP_DEALLOC_ASID IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1400)
+#define GXIO_TRIO_OP_ALLOC_ASIDS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1401)
-#define GXIO_TRIO_OP_ALLOC_MEMORY_MAPS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1402)
+#define GXIO_TRIO_OP_ALLOC_MEMORY_MAPS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1404)
-#define GXIO_TRIO_OP_ALLOC_PIO_REGIONS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x140e)
-#define GXIO_TRIO_OP_INIT_PIO_REGION_AUX IORPC_OPCODE(IORPC_FORMAT_NONE, 0x140f)
+#define GXIO_TRIO_OP_ALLOC_PIO_REGIONS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1412)
-#define GXIO_TRIO_OP_INIT_MEMORY_MAP_MMU_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1417)
-#define GXIO_TRIO_OP_GET_PORT_PROPERTY IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1418)
-#define GXIO_TRIO_OP_CONFIG_LEGACY_INTR IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1419)
-#define GXIO_TRIO_OP_CONFIG_MSI_INTR IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x141a)
+#define GXIO_TRIO_OP_INIT_PIO_REGION_AUX IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1414)
-#define GXIO_TRIO_OP_SET_MPS_MRS IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141c)
-#define GXIO_TRIO_OP_FORCE_RC_LINK_UP IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141d)
-#define GXIO_TRIO_OP_FORCE_EP_LINK_UP IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141e)
+#define GXIO_TRIO_OP_INIT_MEMORY_MAP_MMU_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141e)
+#define GXIO_TRIO_OP_GET_PORT_PROPERTY IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141f)
+#define GXIO_TRIO_OP_CONFIG_LEGACY_INTR IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1420)
+#define GXIO_TRIO_OP_CONFIG_MSI_INTR IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1421)
+
+#define GXIO_TRIO_OP_SET_MPS_MRS IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1423)
+#define GXIO_TRIO_OP_FORCE_RC_LINK_UP IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1424)
+#define GXIO_TRIO_OP_FORCE_EP_LINK_UP IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1425)
#define GXIO_TRIO_OP_GET_MMIO_BASE IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8000)
#define GXIO_TRIO_OP_CHECK_MMIO_OFFSET IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8001)
diff --git a/arch/tile/kernel/pci.c b/arch/tile/kernel/pci.c
index 33c10864d2f7..759822687e8f 100644
--- a/arch/tile/kernel/pci.c
+++ b/arch/tile/kernel/pci.c
@@ -246,16 +246,13 @@ static void __devinit fixup_read_and_payload_sizes(void)
/* Scan for the smallest maximum payload size. */
while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
- int pcie_caps_offset;
u32 devcap;
int max_payload;
- pcie_caps_offset = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (pcie_caps_offset == 0)
+ if (!pci_is_pcie(dev))
continue;
- pci_read_config_dword(dev, pcie_caps_offset + PCI_EXP_DEVCAP,
- &devcap);
+ pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &devcap);
max_payload = devcap & PCI_EXP_DEVCAP_PAYLOAD;
if (max_payload < smallest_max_payload)
smallest_max_payload = max_payload;
@@ -263,21 +260,10 @@ static void __devinit fixup_read_and_payload_sizes(void)
/* Now, set the max_payload_size for all devices to that value. */
new_values = (max_read_size << 12) | (smallest_max_payload << 5);
- while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
- int pcie_caps_offset;
- u16 devctl;
-
- pcie_caps_offset = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (pcie_caps_offset == 0)
- continue;
-
- pci_read_config_word(dev, pcie_caps_offset + PCI_EXP_DEVCTL,
- &devctl);
- devctl &= ~(PCI_EXP_DEVCTL_PAYLOAD | PCI_EXP_DEVCTL_READRQ);
- devctl |= new_values;
- pci_write_config_word(dev, pcie_caps_offset + PCI_EXP_DEVCTL,
- devctl);
- }
+ while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL)
+ pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_PAYLOAD | PCI_EXP_DEVCTL_READRQ,
+ new_values);
}
@@ -404,14 +390,6 @@ void pcibios_set_master(struct pci_dev *dev)
}
/*
- * This is called from the generic Linux layer.
- */
-void __devinit pcibios_update_irq(struct pci_dev *dev, int irq)
-{
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-}
-
-/*
* Enable memory and/or address decoding, as appropriate, for the
* device described by the 'dev' struct.
*
diff --git a/arch/tile/kernel/pci_gx.c b/arch/tile/kernel/pci_gx.c
index 0e213e35ffc3..2ba6d052f85d 100644
--- a/arch/tile/kernel/pci_gx.c
+++ b/arch/tile/kernel/pci_gx.c
@@ -1034,14 +1034,6 @@ char __devinit *pcibios_setup(char *str)
}
/*
- * This is called from the generic Linux layer.
- */
-void __devinit pcibios_update_irq(struct pci_dev *dev, int irq)
-{
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-}
-
-/*
* Enable memory address decoding, as appropriate, for the
* device described by the 'dev' struct. The I/O decoding
* is disabled, though the TILE-Gx supports I/O addressing.
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index 664a60e8dfb4..c17de0db6736 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -705,6 +705,7 @@ static void stack_proc(void *arg)
struct task_struct *from = current, *to = arg;
to->thread.saved_task = from;
+ rcu_switch(from, to);
switch_to(from, to, from);
}
diff --git a/arch/um/include/asm/processor-generic.h b/arch/um/include/asm/processor-generic.h
index 69f1c57a8d0d..33a6a2423bd2 100644
--- a/arch/um/include/asm/processor-generic.h
+++ b/arch/um/include/asm/processor-generic.h
@@ -20,14 +20,6 @@ struct mm_struct;
struct thread_struct {
struct task_struct *saved_task;
- /*
- * This flag is set to 1 before calling do_fork (and analyzed in
- * copy_thread) to mark that we are begin called from userspace (fork /
- * vfork / clone), and reset to 0 after. It is left to 0 when called
- * from kernelspace (i.e. kernel_thread() or fork_idle(),
- * as of 2.6.11).
- */
- int forking;
struct pt_regs regs;
int singlestep_syscall;
void *fault_addr;
@@ -58,7 +50,6 @@ struct thread_struct {
#define INIT_THREAD \
{ \
- .forking = 0, \
.regs = EMPTY_REGS, \
.fault_addr = NULL, \
.prev_sched = NULL, \
diff --git a/arch/um/include/shared/common-offsets.h b/arch/um/include/shared/common-offsets.h
index 40db8f71deae..2df313b6a586 100644
--- a/arch/um/include/shared/common-offsets.h
+++ b/arch/um/include/shared/common-offsets.h
@@ -7,16 +7,6 @@ DEFINE(UM_KERN_PAGE_MASK, PAGE_MASK);
DEFINE(UM_KERN_PAGE_SHIFT, PAGE_SHIFT);
DEFINE(UM_NSEC_PER_SEC, NSEC_PER_SEC);
-DEFINE_STR(UM_KERN_EMERG, KERN_EMERG);
-DEFINE_STR(UM_KERN_ALERT, KERN_ALERT);
-DEFINE_STR(UM_KERN_CRIT, KERN_CRIT);
-DEFINE_STR(UM_KERN_ERR, KERN_ERR);
-DEFINE_STR(UM_KERN_WARNING, KERN_WARNING);
-DEFINE_STR(UM_KERN_NOTICE, KERN_NOTICE);
-DEFINE_STR(UM_KERN_INFO, KERN_INFO);
-DEFINE_STR(UM_KERN_DEBUG, KERN_DEBUG);
-DEFINE_STR(UM_KERN_CONT, KERN_CONT);
-
DEFINE(UM_ELF_CLASS, ELF_CLASS);
DEFINE(UM_ELFCLASS32, ELFCLASS32);
DEFINE(UM_ELFCLASS64, ELFCLASS64);
diff --git a/arch/um/include/shared/user.h b/arch/um/include/shared/user.h
index 4fa82c055aab..cef068563336 100644
--- a/arch/um/include/shared/user.h
+++ b/arch/um/include/shared/user.h
@@ -26,6 +26,17 @@
extern void panic(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
+/* Requires preincluding include/linux/kern_levels.h */
+#define UM_KERN_EMERG KERN_EMERG
+#define UM_KERN_ALERT KERN_ALERT
+#define UM_KERN_CRIT KERN_CRIT
+#define UM_KERN_ERR KERN_ERR
+#define UM_KERN_WARNING KERN_WARNING
+#define UM_KERN_NOTICE KERN_NOTICE
+#define UM_KERN_INFO KERN_INFO
+#define UM_KERN_DEBUG KERN_DEBUG
+#define UM_KERN_CONT KERN_CONT
+
#ifdef UML_CONFIG_PRINTK
extern int printk(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
diff --git a/arch/um/kernel/exec.c b/arch/um/kernel/exec.c
index 6cade9366364..8c82786da823 100644
--- a/arch/um/kernel/exec.c
+++ b/arch/um/kernel/exec.c
@@ -39,34 +39,21 @@ void flush_thread(void)
void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
{
+ get_safe_registers(regs->regs.gp, regs->regs.fp);
PT_REGS_IP(regs) = eip;
PT_REGS_SP(regs) = esp;
-}
-EXPORT_SYMBOL(start_thread);
-
-static long execve1(const char *file,
- const char __user *const __user *argv,
- const char __user *const __user *env)
-{
- long error;
-
- error = do_execve(file, argv, env, &current->thread.regs);
- if (error == 0) {
- task_lock(current);
- current->ptrace &= ~PT_DTRACE;
+ current->ptrace &= ~PT_DTRACE;
#ifdef SUBARCH_EXECVE1
- SUBARCH_EXECVE1(&current->thread.regs.regs);
+ SUBARCH_EXECVE1(regs->regs);
#endif
- task_unlock(current);
- }
- return error;
}
+EXPORT_SYMBOL(start_thread);
long um_execve(const char *file, const char __user *const __user *argv, const char __user *const __user *env)
{
long err;
- err = execve1(file, argv, env);
+ err = do_execve(file, argv, env, &current->thread.regs);
if (!err)
UML_LONGJMP(current->thread.exec_buf, 1);
return err;
@@ -81,7 +68,7 @@ long sys_execve(const char __user *file, const char __user *const __user *argv,
filename = getname(file);
error = PTR_ERR(filename);
if (IS_ERR(filename)) goto out;
- error = execve1(filename, argv, env);
+ error = do_execve(filename, argv, env, &current->thread.regs);
putname(filename);
out:
return error;
diff --git a/arch/um/kernel/process.c b/arch/um/kernel/process.c
index 57fc7028714a..c5f5afa50745 100644
--- a/arch/um/kernel/process.c
+++ b/arch/um/kernel/process.c
@@ -181,11 +181,12 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
struct pt_regs *regs)
{
void (*handler)(void);
+ int kthread = current->flags & PF_KTHREAD;
int ret = 0;
p->thread = (struct thread_struct) INIT_THREAD;
- if (current->thread.forking) {
+ if (!kthread) {
memcpy(&p->thread.regs.regs, &regs->regs,
sizeof(p->thread.regs.regs));
PT_REGS_SET_SYSCALL_RETURN(&p->thread.regs, 0);
@@ -195,8 +196,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
handler = fork_handler;
arch_copy_thread(&current->thread.arch, &p->thread.arch);
- }
- else {
+ } else {
get_safe_registers(p->thread.regs.regs.gp, p->thread.regs.regs.fp);
p->thread.request.u.thread = current->thread.request.u.thread;
handler = new_thread_handler;
@@ -204,7 +204,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
new_thread(task_stack_page(p), &p->thread.switch_buf, handler);
- if (current->thread.forking) {
+ if (!kthread) {
clear_flushed_tls(p);
/*
diff --git a/arch/um/kernel/signal.c b/arch/um/kernel/signal.c
index 7362d58efc29..cc9c2350e417 100644
--- a/arch/um/kernel/signal.c
+++ b/arch/um/kernel/signal.c
@@ -22,9 +22,13 @@ static void handle_signal(struct pt_regs *regs, unsigned long signr,
struct k_sigaction *ka, siginfo_t *info)
{
sigset_t *oldset = sigmask_to_save();
+ int singlestep = 0;
unsigned long sp;
int err;
+ if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
+ singlestep = 1;
+
/* Did we come from a system call? */
if (PT_REGS_SYSCALL_NR(regs) >= 0) {
/* If so, check system call restarting.. */
@@ -61,7 +65,7 @@ static void handle_signal(struct pt_regs *regs, unsigned long signr,
if (err)
force_sigsegv(signr, current);
else
- signal_delivered(signr, info, ka, regs, 0);
+ signal_delivered(signr, info, ka, regs, singlestep);
}
static int kern_do_signal(struct pt_regs *regs)
diff --git a/arch/um/kernel/syscall.c b/arch/um/kernel/syscall.c
index f958cb876ee3..a4c6d8eee74c 100644
--- a/arch/um/kernel/syscall.c
+++ b/arch/um/kernel/syscall.c
@@ -17,25 +17,25 @@
long sys_fork(void)
{
- long ret;
-
- current->thread.forking = 1;
- ret = do_fork(SIGCHLD, UPT_SP(&current->thread.regs.regs),
+ return do_fork(SIGCHLD, UPT_SP(&current->thread.regs.regs),
&current->thread.regs, 0, NULL, NULL);
- current->thread.forking = 0;
- return ret;
}
long sys_vfork(void)
{
- long ret;
-
- current->thread.forking = 1;
- ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
+ return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
UPT_SP(&current->thread.regs.regs),
&current->thread.regs, 0, NULL, NULL);
- current->thread.forking = 0;
- return ret;
+}
+
+long sys_clone(unsigned long clone_flags, unsigned long newsp,
+ void __user *parent_tid, void __user *child_tid)
+{
+ if (!newsp)
+ newsp = UPT_SP(&current->thread.regs.regs);
+
+ return do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
+ child_tid);
}
long old_mmap(unsigned long addr, unsigned long len,
diff --git a/arch/um/scripts/Makefile.rules b/arch/um/scripts/Makefile.rules
index d50270d26b42..15889df9b466 100644
--- a/arch/um/scripts/Makefile.rules
+++ b/arch/um/scripts/Makefile.rules
@@ -8,7 +8,7 @@ USER_OBJS += $(filter %_user.o,$(obj-y) $(obj-m) $(USER_SINGLE_OBJS))
USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file))
$(USER_OBJS:.o=.%): \
- c_flags = -Wp,-MD,$(depfile) $(USER_CFLAGS) -include user.h $(CFLAGS_$(basetarget).o)
+ c_flags = -Wp,-MD,$(depfile) $(USER_CFLAGS) -include $(srctree)/include/linux/kern_levels.h -include user.h $(CFLAGS_$(basetarget).o)
# These are like USER_OBJS but filter USER_CFLAGS through unprofile instead of
# using it directly.
diff --git a/arch/unicore32/kernel/pci.c b/arch/unicore32/kernel/pci.c
index 46cb6c9de6c9..b0056f68d321 100644
--- a/arch/unicore32/kernel/pci.c
+++ b/arch/unicore32/kernel/pci.c
@@ -154,14 +154,6 @@ void __init puv3_pci_adjust_zones(unsigned long *zone_size,
zhole_size[0] = 0;
}
-void __devinit pcibios_update_irq(struct pci_dev *dev, int irq)
-{
- if (debug_pci)
- printk(KERN_DEBUG "PCI: Assigning IRQ %02d to %s\n",
- irq, pci_name(dev));
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-}
-
/*
* If the bus contains any of these devices, then we must not turn on
* parity checking of any kind.
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 8ec3a1aa4abd..7f9a395c5254 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -7,11 +7,13 @@ config 64BIT
Say no to build a 32-bit kernel - formerly known as i386
config X86_32
- def_bool !64BIT
+ def_bool y
+ depends on !64BIT
select CLKSRC_I8253
config X86_64
- def_bool 64BIT
+ def_bool y
+ depends on 64BIT
select X86_DEV_DMA_OPS
### Arch settings
@@ -36,6 +38,7 @@ config X86
select HAVE_KRETPROBES
select HAVE_OPTPROBES
select HAVE_FTRACE_MCOUNT_RECORD
+ select HAVE_FENTRY if X86_64
select HAVE_C_RECORDMCOUNT
select HAVE_DYNAMIC_FTRACE
select HAVE_FUNCTION_TRACER
@@ -60,6 +63,8 @@ config X86
select HAVE_MIXED_BREAKPOINTS_REGS
select PERF_EVENTS
select HAVE_PERF_EVENTS_NMI
+ select HAVE_PERF_REGS
+ select HAVE_PERF_USER_STACK_DUMP
select ANON_INODES
select HAVE_ALIGNED_STRUCT_PAGE if SLUB && !M386
select HAVE_CMPXCHG_LOCAL if !M386
@@ -97,9 +102,12 @@ config X86
select KTIME_SCALAR if X86_32
select GENERIC_STRNCPY_FROM_USER
select GENERIC_STRNLEN_USER
+ select HAVE_RCU_USER_QS if X86_64
+ select HAVE_IRQ_TIME_ACCOUNTING
config INSTRUCTION_DECODER
- def_bool (KPROBES || PERF_EVENTS || UPROBES)
+ def_bool y
+ depends on KPROBES || PERF_EVENTS || UPROBES
config OUTPUT_FORMAT
string
@@ -127,13 +135,15 @@ config SBUS
bool
config NEED_DMA_MAP_STATE
- def_bool (X86_64 || INTEL_IOMMU || DMA_API_DEBUG)
+ def_bool y
+ depends on X86_64 || INTEL_IOMMU || DMA_API_DEBUG
config NEED_SG_DMA_LENGTH
def_bool y
config GENERIC_ISA_DMA
- def_bool ISA_DMA_API
+ def_bool y
+ depends on ISA_DMA_API
config GENERIC_BUG
def_bool y
@@ -150,13 +160,16 @@ config GENERIC_GPIO
bool
config ARCH_MAY_HAVE_PC_FDC
- def_bool ISA_DMA_API
+ def_bool y
+ depends on ISA_DMA_API
config RWSEM_GENERIC_SPINLOCK
- def_bool !X86_XADD
+ def_bool y
+ depends on !X86_XADD
config RWSEM_XCHGADD_ALGORITHM
- def_bool X86_XADD
+ def_bool y
+ depends on X86_XADD
config GENERIC_CALIBRATE_DELAY
def_bool y
@@ -746,13 +759,14 @@ config SWIOTLB
def_bool y if X86_64
---help---
Support for software bounce buffers used on x86-64 systems
- which don't have a hardware IOMMU (e.g. the current generation
- of Intel's x86-64 CPUs). Using this PCI devices which can only
- access 32-bits of memory can be used on systems with more than
- 3 GB of memory. If unsure, say Y.
+ which don't have a hardware IOMMU. Using this PCI devices
+ which can only access 32-bits of memory can be used on systems
+ with more than 3 GB of memory.
+ If unsure, say Y.
config IOMMU_HELPER
- def_bool (CALGARY_IOMMU || GART_IOMMU || SWIOTLB || AMD_IOMMU)
+ def_bool y
+ depends on CALGARY_IOMMU || GART_IOMMU || SWIOTLB || AMD_IOMMU
config MAXSMP
bool "Enable Maximum number of SMP Processors and NUMA Nodes"
@@ -796,17 +810,6 @@ config SCHED_MC
making when dealing with multi-core CPU chips at a cost of slightly
increased overhead in some places. If unsure say N here.
-config IRQ_TIME_ACCOUNTING
- bool "Fine granularity task level IRQ time accounting"
- default n
- ---help---
- Select this option to enable fine granularity task irq time
- accounting. This is done by reading a timestamp on each
- transitions between softirq and hardirq state, so there can be a
- small performance impact.
-
- If in doubt, say N here.
-
source "kernel/Kconfig.preempt"
config X86_UP_APIC
@@ -871,6 +874,7 @@ config X86_REROUTE_FOR_BROKEN_BOOT_IRQS
config X86_MCE
bool "Machine Check / overheating reporting"
+ default y
---help---
Machine Check support allows the processor to notify the
kernel if it detects a problem (e.g. overheating, data corruption).
@@ -982,25 +986,25 @@ config X86_REBOOTFIXUPS
Say N otherwise.
config MICROCODE
- tristate "/dev/cpu/microcode - microcode support"
+ tristate "CPU microcode loading support"
select FW_LOADER
---help---
+
If you say Y here, you will be able to update the microcode on
certain Intel and AMD processors. The Intel support is for the
- IA32 family, e.g. Pentium Pro, Pentium II, Pentium III,
- Pentium 4, Xeon etc. The AMD support is for family 0x10 and
- 0x11 processors, e.g. Opteron, Phenom and Turion 64 Ultra.
- You will obviously need the actual microcode binary data itself
- which is not shipped with the Linux kernel.
+ IA32 family, e.g. Pentium Pro, Pentium II, Pentium III, Pentium 4,
+ Xeon etc. The AMD support is for families 0x10 and later. You will
+ obviously need the actual microcode binary data itself which is not
+ shipped with the Linux kernel.
This option selects the general module only, you need to select
at least one vendor specific module as well.
- To compile this driver as a module, choose M here: the
- module will be called microcode.
+ To compile this driver as a module, choose M here: the module
+ will be called microcode.
config MICROCODE_INTEL
- bool "Intel microcode patch loading support"
+ bool "Intel microcode loading support"
depends on MICROCODE
default MICROCODE
select FW_LOADER
@@ -1013,7 +1017,7 @@ config MICROCODE_INTEL
<http://www.urbanmyth.org/microcode/>.
config MICROCODE_AMD
- bool "AMD microcode patch loading support"
+ bool "AMD microcode loading support"
depends on MICROCODE
select FW_LOADER
---help---
@@ -1159,10 +1163,12 @@ config X86_PAE
consumes more pagetable space per process.
config ARCH_PHYS_ADDR_T_64BIT
- def_bool X86_64 || X86_PAE
+ def_bool y
+ depends on X86_64 || X86_PAE
config ARCH_DMA_ADDR_T_64BIT
- def_bool X86_64 || HIGHMEM64G
+ def_bool y
+ depends on X86_64 || HIGHMEM64G
config DIRECT_GBPAGES
bool "Enable 1GB pages for kernel pagetables" if EXPERT
@@ -1285,8 +1291,8 @@ config ARCH_SELECT_MEMORY_MODEL
depends on ARCH_SPARSEMEM_ENABLE
config ARCH_MEMORY_PROBE
- def_bool X86_64
- depends on MEMORY_HOTPLUG
+ def_bool y
+ depends on X86_64 && MEMORY_HOTPLUG
config ARCH_PROC_KCORE_TEXT
def_bool y
@@ -1487,6 +1493,17 @@ config ARCH_RANDOM
If supported, this is a high bandwidth, cryptographically
secure hardware random number generator.
+config X86_SMAP
+ def_bool y
+ prompt "Supervisor Mode Access Prevention" if EXPERT
+ ---help---
+ Supervisor Mode Access Prevention (SMAP) is a security
+ feature in newer Intel processors. There is a small
+ performance cost if this enabled and turned on; there is
+ also a small increase in the kernel size if this is enabled.
+
+ If unsure, say Y.
+
config EFI
bool "EFI runtime service support"
depends on ACPI
@@ -1975,7 +1992,6 @@ config PCI_MMCONFIG
config PCI_CNB20LE_QUIRK
bool "Read CNB20LE Host Bridge Windows" if EXPERT
- default n
depends on PCI && EXPERIMENTAL
help
Read the PCI windows out of the CNB20LE host bridge. This allows
@@ -2186,18 +2202,18 @@ config COMPAT
depends on IA32_EMULATION || X86_X32
select ARCH_WANT_OLD_COMPAT_IPC
+if COMPAT
config COMPAT_FOR_U64_ALIGNMENT
- def_bool COMPAT
- depends on X86_64
+ def_bool y
config SYSVIPC_COMPAT
def_bool y
- depends on COMPAT && SYSVIPC
+ depends on SYSVIPC
config KEYS_COMPAT
- bool
- depends on COMPAT && KEYS
- default y
+ def_bool y
+ depends on KEYS
+endif
endmenu
diff --git a/arch/x86/Kconfig.cpu b/arch/x86/Kconfig.cpu
index 706e12e9984b..f3b86d0df44e 100644
--- a/arch/x86/Kconfig.cpu
+++ b/arch/x86/Kconfig.cpu
@@ -306,7 +306,8 @@ config X86_INTERNODE_CACHE_SHIFT
default X86_L1_CACHE_SHIFT
config X86_CMPXCHG
- def_bool X86_64 || (X86_32 && !M386)
+ def_bool y
+ depends on X86_64 || (X86_32 && !M386)
config X86_L1_CACHE_SHIFT
int
@@ -317,7 +318,7 @@ config X86_L1_CACHE_SHIFT
config X86_XADD
def_bool y
- depends on X86_64 || !M386
+ depends on !M386
config X86_PPRO_FENCE
bool "PentiumPro memory ordering errata workaround"
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index 682e9c210baa..474ca35b1bce 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -142,7 +142,7 @@ KBUILD_CFLAGS += $(call cc-option,-mno-avx,)
KBUILD_CFLAGS += $(mflags-y)
KBUILD_AFLAGS += $(mflags-y)
-archscripts:
+archscripts: scripts_basic
$(Q)$(MAKE) $(build)=arch/x86/tools relocs
###
diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index e398bb5d63bb..8a84501acb1b 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -28,6 +28,9 @@ VMLINUX_OBJS = $(obj)/vmlinux.lds $(obj)/head_$(BITS).o $(obj)/misc.o \
$(obj)/string.o $(obj)/cmdline.o $(obj)/early_serial_console.o \
$(obj)/piggy.o
+$(obj)/eboot.o: KBUILD_CFLAGS += -fshort-wchar -mno-red-zone
+$(obj)/efi_stub_$(BITS).o: KBUILD_CLFAGS += -fshort-wchar -mno-red-zone
+
ifeq ($(CONFIG_EFI_STUB), y)
VMLINUX_OBJS += $(obj)/eboot.o $(obj)/efi_stub_$(BITS).o
endif
diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c
index b3e0227df2c9..c760e073963e 100644
--- a/arch/x86/boot/compressed/eboot.c
+++ b/arch/x86/boot/compressed/eboot.c
@@ -276,8 +276,9 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
nr_gops = size / sizeof(void *);
for (i = 0; i < nr_gops; i++) {
struct efi_graphics_output_mode_info *info;
- efi_guid_t pciio_proto = EFI_PCI_IO_PROTOCOL_GUID;
- void *pciio;
+ efi_guid_t conout_proto = EFI_CONSOLE_OUT_DEVICE_GUID;
+ bool conout_found = false;
+ void *dummy;
void *h = gop_handle[i];
status = efi_call_phys3(sys_table->boottime->handle_protocol,
@@ -285,19 +286,21 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
if (status != EFI_SUCCESS)
continue;
- efi_call_phys3(sys_table->boottime->handle_protocol,
- h, &pciio_proto, &pciio);
+ status = efi_call_phys3(sys_table->boottime->handle_protocol,
+ h, &conout_proto, &dummy);
+
+ if (status == EFI_SUCCESS)
+ conout_found = true;
status = efi_call_phys4(gop->query_mode, gop,
gop->mode->mode, &size, &info);
- if (status == EFI_SUCCESS && (!first_gop || pciio)) {
+ if (status == EFI_SUCCESS && (!first_gop || conout_found)) {
/*
- * Apple provide GOPs that are not backed by
- * real hardware (they're used to handle
- * multiple displays). The workaround is to
- * search for a GOP implementing the PCIIO
- * protocol, and if one isn't found, to just
- * fallback to the first GOP.
+ * Systems that use the UEFI Console Splitter may
+ * provide multiple GOP devices, not all of which are
+ * backed by real hardware. The workaround is to search
+ * for a GOP implementing the ConOut protocol, and if
+ * one isn't found, to just fall back to the first GOP.
*/
width = info->horizontal_resolution;
height = info->vertical_resolution;
@@ -308,10 +311,10 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
pixels_per_scan_line = info->pixels_per_scan_line;
/*
- * Once we've found a GOP supporting PCIIO,
+ * Once we've found a GOP supporting ConOut,
* don't bother looking any further.
*/
- if (pciio)
+ if (conout_found)
break;
first_gop = gop;
@@ -328,7 +331,6 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
si->lfb_width = width;
si->lfb_height = height;
si->lfb_base = fb_base;
- si->lfb_size = fb_size;
si->pages = 1;
if (pixel_format == PIXEL_RGB_RESERVED_8BIT_PER_COLOR) {
@@ -376,6 +378,10 @@ static efi_status_t setup_gop(struct screen_info *si, efi_guid_t *proto,
si->rsvd_pos = 0;
}
+ si->lfb_size = si->lfb_linelength * si->lfb_height;
+
+ si->capabilities |= VIDEO_CAPABILITY_SKIP_QUIRKS;
+
free_handle:
efi_call_phys1(sys_table->boottime->free_pool, gop_handle);
return status;
diff --git a/arch/x86/boot/compressed/eboot.h b/arch/x86/boot/compressed/eboot.h
index 3b6e15627c55..e5b0a8f91c5f 100644
--- a/arch/x86/boot/compressed/eboot.h
+++ b/arch/x86/boot/compressed/eboot.h
@@ -14,6 +14,10 @@
#define EFI_PAGE_SIZE (1UL << EFI_PAGE_SHIFT)
#define EFI_READ_CHUNK_SIZE (1024 * 1024)
+#define EFI_CONSOLE_OUT_DEVICE_GUID \
+ EFI_GUID(0xd3b36f2c, 0xd551, 0x11d4, 0x9a, 0x46, 0x0, 0x90, 0x27, \
+ 0x3f, 0xc1, 0x4d)
+
#define PIXEL_RGB_RESERVED_8BIT_PER_COLOR 0
#define PIXEL_BGR_RESERVED_8BIT_PER_COLOR 1
#define PIXEL_BIT_MASK 2
diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S
index b4e15dd6786a..2a017441b8b2 100644
--- a/arch/x86/boot/header.S
+++ b/arch/x86/boot/header.S
@@ -32,10 +32,6 @@ SYSSEG = 0x1000 /* historical load address >> 4 */
#define SVGA_MODE ASK_VGA
#endif
-#ifndef RAMDISK
-#define RAMDISK 0
-#endif
-
#ifndef ROOT_RDONLY
#define ROOT_RDONLY 1
#endif
diff --git a/arch/x86/configs/i386_defconfig b/arch/x86/configs/i386_defconfig
index 119db67dcb03..5598547281a7 100644
--- a/arch/x86/configs/i386_defconfig
+++ b/arch/x86/configs/i386_defconfig
@@ -8,6 +8,8 @@ CONFIG_TASK_DELAY_ACCT=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
CONFIG_LOG_BUF_SHIFT=18
CONFIG_CGROUPS=y
CONFIG_CGROUP_FREEZER=y
@@ -34,8 +36,6 @@ CONFIG_SGI_PARTITION=y
CONFIG_SUN_PARTITION=y
CONFIG_KARMA_PARTITION=y
CONFIG_EFI_PARTITION=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
CONFIG_X86_GENERIC=y
CONFIG_HPET_TIMER=y
@@ -144,8 +144,6 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEBUG_DEVRES=y
CONFIG_CONNECTOR=y
CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=16384
CONFIG_BLK_DEV_SD=y
CONFIG_BLK_DEV_SR=y
CONFIG_BLK_DEV_SR_VENDOR=y
@@ -231,8 +229,6 @@ CONFIG_SND_HRTIMER=y
CONFIG_SND_HDA_INTEL=y
CONFIG_SND_HDA_HWDEP=y
CONFIG_HIDRAW=y
-CONFIG_HID_PID=y
-CONFIG_USB_HIDDEV=y
CONFIG_HID_GYRATION=y
CONFIG_LOGITECH_FF=y
CONFIG_HID_NTRIG=y
@@ -243,11 +239,11 @@ CONFIG_HID_SAMSUNG=y
CONFIG_HID_SONY=y
CONFIG_HID_SUNPLUS=y
CONFIG_HID_TOPSEED=y
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
CONFIG_USB=y
CONFIG_USB_DEBUG=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
-CONFIG_USB_DEVICEFS=y
-# CONFIG_USB_DEVICE_CLASS is not set
CONFIG_USB_MON=y
CONFIG_USB_EHCI_HCD=y
# CONFIG_USB_EHCI_TT_NEWSCHED is not set
@@ -262,10 +258,9 @@ CONFIG_RTC_CLASS=y
CONFIG_DMADEVICES=y
CONFIG_EEEPC_LAPTOP=y
CONFIG_EFI_VARS=y
-CONFIG_EXT3_FS=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-CONFIG_EXT3_FS_POSIX_ACL=y
-CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
# CONFIG_PRINT_QUOTA_WARNING is not set
@@ -280,7 +275,6 @@ CONFIG_PROC_KCORE=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_HUGETLBFS=y
CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
CONFIG_NFS_V3_ACL=y
CONFIG_NFS_V4=y
CONFIG_ROOT_NFS=y
@@ -299,13 +293,11 @@ CONFIG_DEBUG_KERNEL=y
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_STACK_USAGE=y
-CONFIG_SYSCTL_SYSCALL_CHECK=y
CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_PROVIDE_OHCI1394_DMA_INIT=y
CONFIG_EARLY_PRINTK_DBGP=y
CONFIG_DEBUG_STACKOVERFLOW=y
# CONFIG_DEBUG_RODATA_TEST is not set
-CONFIG_DEBUG_NX_TEST=m
CONFIG_DEBUG_BOOT_PARAMS=y
CONFIG_OPTIMIZE_INLINING=y
CONFIG_KEYS_DEBUG_PROC_KEYS=y
@@ -316,4 +308,3 @@ CONFIG_SECURITY_SELINUX_BOOTPARAM=y
CONFIG_SECURITY_SELINUX_DISABLE=y
CONFIG_CRYPTO_AES_586=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
-CONFIG_CRC_T10DIF=y
diff --git a/arch/x86/configs/x86_64_defconfig b/arch/x86/configs/x86_64_defconfig
index 76eb2903809f..671524d0f6c0 100644
--- a/arch/x86/configs/x86_64_defconfig
+++ b/arch/x86/configs/x86_64_defconfig
@@ -8,6 +8,8 @@ CONFIG_TASK_DELAY_ACCT=y
CONFIG_TASK_XACCT=y
CONFIG_TASK_IO_ACCOUNTING=y
CONFIG_AUDIT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
CONFIG_LOG_BUF_SHIFT=18
CONFIG_CGROUPS=y
CONFIG_CGROUP_FREEZER=y
@@ -34,8 +36,6 @@ CONFIG_SGI_PARTITION=y
CONFIG_SUN_PARTITION=y
CONFIG_KARMA_PARTITION=y
CONFIG_EFI_PARTITION=y
-CONFIG_NO_HZ=y
-CONFIG_HIGH_RES_TIMERS=y
CONFIG_SMP=y
CONFIG_CALGARY_IOMMU=y
CONFIG_NR_CPUS=64
@@ -144,8 +144,6 @@ CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
CONFIG_DEBUG_DEVRES=y
CONFIG_CONNECTOR=y
CONFIG_BLK_DEV_LOOP=y
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_SIZE=16384
CONFIG_BLK_DEV_SD=y
CONFIG_BLK_DEV_SR=y
CONFIG_BLK_DEV_SR_VENDOR=y
@@ -227,8 +225,6 @@ CONFIG_SND_HRTIMER=y
CONFIG_SND_HDA_INTEL=y
CONFIG_SND_HDA_HWDEP=y
CONFIG_HIDRAW=y
-CONFIG_HID_PID=y
-CONFIG_USB_HIDDEV=y
CONFIG_HID_GYRATION=y
CONFIG_LOGITECH_FF=y
CONFIG_HID_NTRIG=y
@@ -239,11 +235,11 @@ CONFIG_HID_SAMSUNG=y
CONFIG_HID_SONY=y
CONFIG_HID_SUNPLUS=y
CONFIG_HID_TOPSEED=y
+CONFIG_HID_PID=y
+CONFIG_USB_HIDDEV=y
CONFIG_USB=y
CONFIG_USB_DEBUG=y
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
-CONFIG_USB_DEVICEFS=y
-# CONFIG_USB_DEVICE_CLASS is not set
CONFIG_USB_MON=y
CONFIG_USB_EHCI_HCD=y
# CONFIG_USB_EHCI_TT_NEWSCHED is not set
@@ -262,10 +258,9 @@ CONFIG_AMD_IOMMU_STATS=y
CONFIG_INTEL_IOMMU=y
# CONFIG_INTEL_IOMMU_DEFAULT_ON is not set
CONFIG_EFI_VARS=y
-CONFIG_EXT3_FS=y
-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
-CONFIG_EXT3_FS_POSIX_ACL=y
-CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
CONFIG_QUOTA=y
CONFIG_QUOTA_NETLINK_INTERFACE=y
# CONFIG_PRINT_QUOTA_WARNING is not set
@@ -280,7 +275,6 @@ CONFIG_PROC_KCORE=y
CONFIG_TMPFS_POSIX_ACL=y
CONFIG_HUGETLBFS=y
CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
CONFIG_NFS_V3_ACL=y
CONFIG_NFS_V4=y
CONFIG_ROOT_NFS=y
@@ -298,13 +292,11 @@ CONFIG_DEBUG_KERNEL=y
CONFIG_SCHEDSTATS=y
CONFIG_TIMER_STATS=y
CONFIG_DEBUG_STACK_USAGE=y
-CONFIG_SYSCTL_SYSCALL_CHECK=y
CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_PROVIDE_OHCI1394_DMA_INIT=y
CONFIG_EARLY_PRINTK_DBGP=y
CONFIG_DEBUG_STACKOVERFLOW=y
# CONFIG_DEBUG_RODATA_TEST is not set
-CONFIG_DEBUG_NX_TEST=m
CONFIG_DEBUG_BOOT_PARAMS=y
CONFIG_OPTIMIZE_INLINING=y
CONFIG_KEYS_DEBUG_PROC_KEYS=y
@@ -314,4 +306,3 @@ CONFIG_SECURITY_SELINUX=y
CONFIG_SECURITY_SELINUX_BOOTPARAM=y
CONFIG_SECURITY_SELINUX_DISABLE=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
-CONFIG_CRC_T10DIF=y
diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c
index 673ac9b63d6b..efc6a958b71d 100644
--- a/arch/x86/ia32/ia32_signal.c
+++ b/arch/x86/ia32/ia32_signal.c
@@ -32,6 +32,7 @@
#include <asm/sigframe.h>
#include <asm/sighandling.h>
#include <asm/sys_ia32.h>
+#include <asm/smap.h>
#define FIX_EFLAGS __FIX_EFLAGS
@@ -162,7 +163,8 @@ asmlinkage long sys32_sigaltstack(const stack_ia32_t __user *uss_ptr,
}
seg = get_fs();
set_fs(KERNEL_DS);
- ret = do_sigaltstack(uss_ptr ? &uss : NULL, &uoss, regs->sp);
+ ret = do_sigaltstack((stack_t __force __user *) (uss_ptr ? &uss : NULL),
+ (stack_t __force __user *) &uoss, regs->sp);
set_fs(seg);
if (ret >= 0 && uoss_ptr) {
if (!access_ok(VERIFY_WRITE, uoss_ptr, sizeof(stack_ia32_t)))
@@ -250,11 +252,12 @@ static int ia32_restore_sigcontext(struct pt_regs *regs,
get_user_ex(tmp, &sc->fpstate);
buf = compat_ptr(tmp);
- err |= restore_i387_xstate_ia32(buf);
get_user_ex(*pax, &sc->ax);
} get_user_catch(err);
+ err |= restore_xstate_sig(buf, 1);
+
return err;
}
@@ -361,7 +364,7 @@ static int ia32_setup_sigcontext(struct sigcontext_ia32 __user *sc,
*/
static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
size_t frame_size,
- void **fpstate)
+ void __user **fpstate)
{
unsigned long sp;
@@ -381,9 +384,12 @@ static void __user *get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
sp = (unsigned long) ka->sa.sa_restorer;
if (used_math()) {
- sp = sp - sig_xstate_ia32_size;
- *fpstate = (struct _fpstate_ia32 *) sp;
- if (save_i387_xstate_ia32(*fpstate) < 0)
+ unsigned long fx_aligned, math_size;
+
+ sp = alloc_mathframe(sp, 1, &fx_aligned, &math_size);
+ *fpstate = (struct _fpstate_ia32 __user *) sp;
+ if (save_xstate_sig(*fpstate, (void __user *)fx_aligned,
+ math_size) < 0)
return (void __user *) -1L;
}
@@ -448,7 +454,7 @@ int ia32_setup_frame(int sig, struct k_sigaction *ka,
* These are actually not used anymore, but left because some
* gdb versions depend on them as a marker.
*/
- put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
+ put_user_ex(*((u64 *)&code), (u64 __user *)frame->retcode);
} put_user_catch(err);
if (err)
@@ -502,7 +508,6 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
put_user_ex(sig, &frame->sig);
put_user_ex(ptr_to_compat(&frame->info), &frame->pinfo);
put_user_ex(ptr_to_compat(&frame->uc), &frame->puc);
- err |= copy_siginfo_to_user32(&frame->info, info);
/* Create the ucontext. */
if (cpu_has_xsave)
@@ -514,9 +519,6 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
put_user_ex(sas_ss_flags(regs->sp),
&frame->uc.uc_stack.ss_flags);
put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
- err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
- regs, set->sig[0]);
- err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
if (ka->sa.sa_flags & SA_RESTORER)
restorer = ka->sa.sa_restorer;
@@ -529,9 +531,14 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
* Not actually used anymore, but left because some gdb
* versions need it.
*/
- put_user_ex(*((u64 *)&code), (u64 *)frame->retcode);
+ put_user_ex(*((u64 *)&code), (u64 __user *)frame->retcode);
} put_user_catch(err);
+ err |= copy_siginfo_to_user32(&frame->info, info);
+ err |= ia32_setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
+ regs, set->sig[0]);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
if (err)
return -EFAULT;
diff --git a/arch/x86/ia32/ia32entry.S b/arch/x86/ia32/ia32entry.S
index 20e5f7ba0e6b..9c289504e680 100644
--- a/arch/x86/ia32/ia32entry.S
+++ b/arch/x86/ia32/ia32entry.S
@@ -14,6 +14,7 @@
#include <asm/segment.h>
#include <asm/irqflags.h>
#include <asm/asm.h>
+#include <asm/smap.h>
#include <linux/linkage.h>
#include <linux/err.h>
@@ -146,8 +147,10 @@ ENTRY(ia32_sysenter_target)
SAVE_ARGS 0,1,0
/* no need to do an access_ok check here because rbp has been
32bit zero extended */
+ ASM_STAC
1: movl (%rbp),%ebp
_ASM_EXTABLE(1b,ia32_badarg)
+ ASM_CLAC
orl $TS_COMPAT,TI_status+THREAD_INFO(%rsp,RIP-ARGOFFSET)
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET)
CFI_REMEMBER_STATE
@@ -301,8 +304,10 @@ ENTRY(ia32_cstar_target)
/* no need to do an access_ok check here because r8 has been
32bit zero extended */
/* hardware stack frame is complete now */
+ ASM_STAC
1: movl (%r8),%r9d
_ASM_EXTABLE(1b,ia32_badarg)
+ ASM_CLAC
orl $TS_COMPAT,TI_status+THREAD_INFO(%rsp,RIP-ARGOFFSET)
testl $_TIF_WORK_SYSCALL_ENTRY,TI_flags+THREAD_INFO(%rsp,RIP-ARGOFFSET)
CFI_REMEMBER_STATE
@@ -365,6 +370,7 @@ cstar_tracesys:
END(ia32_cstar_target)
ia32_badarg:
+ ASM_CLAC
movq $-EFAULT,%rax
jmp ia32_sysret
CFI_ENDPROC
diff --git a/arch/x86/ia32/sys_ia32.c b/arch/x86/ia32/sys_ia32.c
index 4540bece0946..c5b938d92eab 100644
--- a/arch/x86/ia32/sys_ia32.c
+++ b/arch/x86/ia32/sys_ia32.c
@@ -287,7 +287,7 @@ asmlinkage long sys32_sigaction(int sig, struct old_sigaction32 __user *act,
return ret;
}
-asmlinkage long sys32_waitpid(compat_pid_t pid, unsigned int *stat_addr,
+asmlinkage long sys32_waitpid(compat_pid_t pid, unsigned int __user *stat_addr,
int options)
{
return compat_sys_wait4(pid, stat_addr, options, NULL);
diff --git a/arch/x86/include/asm/alternative-asm.h b/arch/x86/include/asm/alternative-asm.h
index 952bd0100c5c..372231c22a47 100644
--- a/arch/x86/include/asm/alternative-asm.h
+++ b/arch/x86/include/asm/alternative-asm.h
@@ -1,3 +1,6 @@
+#ifndef _ASM_X86_ALTERNATIVE_ASM_H
+#define _ASM_X86_ALTERNATIVE_ASM_H
+
#ifdef __ASSEMBLY__
#include <asm/asm.h>
@@ -5,10 +8,10 @@
#ifdef CONFIG_SMP
.macro LOCK_PREFIX
672: lock
- .section .smp_locks,"a"
+ .pushsection .smp_locks,"a"
.balign 4
.long 672b - .
- .previous
+ .popsection
.endm
#else
.macro LOCK_PREFIX
@@ -24,3 +27,5 @@
.endm
#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_X86_ALTERNATIVE_ASM_H */
diff --git a/arch/x86/include/asm/alternative.h b/arch/x86/include/asm/alternative.h
index 70780689599a..58ed6d96a6ac 100644
--- a/arch/x86/include/asm/alternative.h
+++ b/arch/x86/include/asm/alternative.h
@@ -29,10 +29,10 @@
#ifdef CONFIG_SMP
#define LOCK_PREFIX_HERE \
- ".section .smp_locks,\"a\"\n" \
- ".balign 4\n" \
- ".long 671f - .\n" /* offset */ \
- ".previous\n" \
+ ".pushsection .smp_locks,\"a\"\n" \
+ ".balign 4\n" \
+ ".long 671f - .\n" /* offset */ \
+ ".popsection\n" \
"671:"
#define LOCK_PREFIX LOCK_PREFIX_HERE "\n\tlock; "
@@ -60,7 +60,7 @@ extern void alternatives_smp_module_add(struct module *mod, char *name,
void *locks, void *locks_end,
void *text, void *text_end);
extern void alternatives_smp_module_del(struct module *mod);
-extern void alternatives_smp_switch(int smp);
+extern void alternatives_enable_smp(void);
extern int alternatives_text_reserved(void *start, void *end);
extern bool skip_smp_alternatives;
#else
@@ -68,7 +68,7 @@ static inline void alternatives_smp_module_add(struct module *mod, char *name,
void *locks, void *locks_end,
void *text, void *text_end) {}
static inline void alternatives_smp_module_del(struct module *mod) {}
-static inline void alternatives_smp_switch(int smp) {}
+static inline void alternatives_enable_smp(void) {}
static inline int alternatives_text_reserved(void *start, void *end)
{
return 0;
@@ -99,30 +99,30 @@ static inline int alternatives_text_reserved(void *start, void *end)
/* alternative assembly primitive: */
#define ALTERNATIVE(oldinstr, newinstr, feature) \
OLDINSTR(oldinstr) \
- ".section .altinstructions,\"a\"\n" \
+ ".pushsection .altinstructions,\"a\"\n" \
ALTINSTR_ENTRY(feature, 1) \
- ".previous\n" \
- ".section .discard,\"aw\",@progbits\n" \
+ ".popsection\n" \
+ ".pushsection .discard,\"aw\",@progbits\n" \
DISCARD_ENTRY(1) \
- ".previous\n" \
- ".section .altinstr_replacement, \"ax\"\n" \
+ ".popsection\n" \
+ ".pushsection .altinstr_replacement, \"ax\"\n" \
ALTINSTR_REPLACEMENT(newinstr, feature, 1) \
- ".previous"
+ ".popsection"
#define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2)\
OLDINSTR(oldinstr) \
- ".section .altinstructions,\"a\"\n" \
+ ".pushsection .altinstructions,\"a\"\n" \
ALTINSTR_ENTRY(feature1, 1) \
ALTINSTR_ENTRY(feature2, 2) \
- ".previous\n" \
- ".section .discard,\"aw\",@progbits\n" \
+ ".popsection\n" \
+ ".pushsection .discard,\"aw\",@progbits\n" \
DISCARD_ENTRY(1) \
DISCARD_ENTRY(2) \
- ".previous\n" \
- ".section .altinstr_replacement, \"ax\"\n" \
+ ".popsection\n" \
+ ".pushsection .altinstr_replacement, \"ax\"\n" \
ALTINSTR_REPLACEMENT(newinstr1, feature1, 1) \
ALTINSTR_REPLACEMENT(newinstr2, feature2, 2) \
- ".previous"
+ ".popsection"
/*
* This must be included *after* the definition of ALTERNATIVE due to
diff --git a/arch/x86/include/asm/bitops.h b/arch/x86/include/asm/bitops.h
index 72f5009deb5a..6dfd0195bb55 100644
--- a/arch/x86/include/asm/bitops.h
+++ b/arch/x86/include/asm/bitops.h
@@ -355,7 +355,7 @@ static int test_bit(int nr, const volatile unsigned long *addr);
*/
static inline unsigned long __ffs(unsigned long word)
{
- asm("bsf %1,%0"
+ asm("rep; bsf %1,%0"
: "=r" (word)
: "rm" (word));
return word;
@@ -369,7 +369,7 @@ static inline unsigned long __ffs(unsigned long word)
*/
static inline unsigned long ffz(unsigned long word)
{
- asm("bsf %1,%0"
+ asm("rep; bsf %1,%0"
: "=r" (word)
: "r" (~word));
return word;
@@ -417,10 +417,9 @@ static inline int ffs(int x)
* We cannot do this on 32 bits because at the very least some
* 486 CPUs did not behave this way.
*/
- long tmp = -1;
asm("bsfl %1,%0"
: "=r" (r)
- : "rm" (x), "0" (tmp));
+ : "rm" (x), "0" (-1));
#elif defined(CONFIG_X86_CMOV)
asm("bsfl %1,%0\n\t"
"cmovzl %2,%0"
@@ -459,10 +458,9 @@ static inline int fls(int x)
* We cannot do this on 32 bits because at the very least some
* 486 CPUs did not behave this way.
*/
- long tmp = -1;
asm("bsrl %1,%0"
: "=r" (r)
- : "rm" (x), "0" (tmp));
+ : "rm" (x), "0" (-1));
#elif defined(CONFIG_X86_CMOV)
asm("bsrl %1,%0\n\t"
"cmovzl %2,%0"
@@ -490,13 +488,13 @@ static inline int fls(int x)
#ifdef CONFIG_X86_64
static __always_inline int fls64(__u64 x)
{
- long bitpos = -1;
+ int bitpos = -1;
/*
* AMD64 says BSRQ won't clobber the dest reg if x==0; Intel64 says the
* dest reg is undefined if x==0, but their CPU architect says its
* value is written to set it to the same as before.
*/
- asm("bsrq %1,%0"
+ asm("bsrq %1,%q0"
: "+r" (bitpos)
: "rm" (x));
return bitpos + 1;
diff --git a/arch/x86/include/asm/calling.h b/arch/x86/include/asm/calling.h
index a9e3a740f697..7f8422a28a46 100644
--- a/arch/x86/include/asm/calling.h
+++ b/arch/x86/include/asm/calling.h
@@ -49,38 +49,36 @@ For 32-bit we have the following conventions - kernel is built with
#include "dwarf2.h"
/*
- * 64-bit system call stack frame layout defines and helpers, for
- * assembly code (note that the seemingly unnecessary parentheses
- * are to prevent cpp from inserting spaces in expressions that get
- * passed to macros):
+ * 64-bit system call stack frame layout defines and helpers,
+ * for assembly code:
*/
-#define R15 (0)
-#define R14 (8)
-#define R13 (16)
-#define R12 (24)
-#define RBP (32)
-#define RBX (40)
+#define R15 0
+#define R14 8
+#define R13 16
+#define R12 24
+#define RBP 32
+#define RBX 40
/* arguments: interrupts/non tracing syscalls only save up to here: */
-#define R11 (48)
-#define R10 (56)
-#define R9 (64)
-#define R8 (72)
-#define RAX (80)
-#define RCX (88)
-#define RDX (96)
-#define RSI (104)
-#define RDI (112)
-#define ORIG_RAX (120) /* + error_code */
+#define R11 48
+#define R10 56
+#define R9 64
+#define R8 72
+#define RAX 80
+#define RCX 88
+#define RDX 96
+#define RSI 104
+#define RDI 112
+#define ORIG_RAX 120 /* + error_code */
/* end of arguments */
/* cpu exception frame or undefined in case of fast syscall: */
-#define RIP (128)
-#define CS (136)
-#define EFLAGS (144)
-#define RSP (152)
-#define SS (160)
+#define RIP 128
+#define CS 136
+#define EFLAGS 144
+#define RSP 152
+#define SS 160
#define ARGOFFSET R11
#define SWFRAME ORIG_RAX
diff --git a/arch/x86/include/asm/cpufeature.h b/arch/x86/include/asm/cpufeature.h
index 6b7ee5ff6820..16cae425d1f8 100644
--- a/arch/x86/include/asm/cpufeature.h
+++ b/arch/x86/include/asm/cpufeature.h
@@ -97,6 +97,7 @@
#define X86_FEATURE_EXTD_APICID (3*32+26) /* has extended APICID (8 bits) */
#define X86_FEATURE_AMD_DCM (3*32+27) /* multi-node processor */
#define X86_FEATURE_APERFMPERF (3*32+28) /* APERFMPERF */
+#define X86_FEATURE_EAGER_FPU (3*32+29) /* "eagerfpu" Non lazy FPU restore */
/* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */
#define X86_FEATURE_XMM3 (4*32+ 0) /* "pni" SSE-3 */
@@ -209,6 +210,7 @@
#define X86_FEATURE_RTM (9*32+11) /* Restricted Transactional Memory */
#define X86_FEATURE_RDSEED (9*32+18) /* The RDSEED instruction */
#define X86_FEATURE_ADX (9*32+19) /* The ADCX and ADOX instructions */
+#define X86_FEATURE_SMAP (9*32+20) /* Supervisor Mode Access Prevention */
#if defined(__KERNEL__) && !defined(__ASSEMBLY__)
@@ -299,12 +301,14 @@ extern const char * const x86_power_flags[32];
#define cpu_has_xmm4_2 boot_cpu_has(X86_FEATURE_XMM4_2)
#define cpu_has_x2apic boot_cpu_has(X86_FEATURE_X2APIC)
#define cpu_has_xsave boot_cpu_has(X86_FEATURE_XSAVE)
+#define cpu_has_xsaveopt boot_cpu_has(X86_FEATURE_XSAVEOPT)
#define cpu_has_osxsave boot_cpu_has(X86_FEATURE_OSXSAVE)
#define cpu_has_hypervisor boot_cpu_has(X86_FEATURE_HYPERVISOR)
#define cpu_has_pclmulqdq boot_cpu_has(X86_FEATURE_PCLMULQDQ)
#define cpu_has_perfctr_core boot_cpu_has(X86_FEATURE_PERFCTR_CORE)
#define cpu_has_cx8 boot_cpu_has(X86_FEATURE_CX8)
#define cpu_has_cx16 boot_cpu_has(X86_FEATURE_CX16)
+#define cpu_has_eager_fpu boot_cpu_has(X86_FEATURE_EAGER_FPU)
#if defined(CONFIG_X86_INVLPG) || defined(CONFIG_X86_64)
# define cpu_has_invlpg 1
diff --git a/arch/x86/include/asm/fpu-internal.h b/arch/x86/include/asm/fpu-internal.h
index 75f4c6d6a331..831dbb9c6c02 100644
--- a/arch/x86/include/asm/fpu-internal.h
+++ b/arch/x86/include/asm/fpu-internal.h
@@ -12,6 +12,7 @@
#include <linux/kernel_stat.h>
#include <linux/regset.h>
+#include <linux/compat.h>
#include <linux/slab.h>
#include <asm/asm.h>
#include <asm/cpufeature.h>
@@ -20,43 +21,76 @@
#include <asm/user.h>
#include <asm/uaccess.h>
#include <asm/xsave.h>
+#include <asm/smap.h>
-extern unsigned int sig_xstate_size;
+#ifdef CONFIG_X86_64
+# include <asm/sigcontext32.h>
+# include <asm/user32.h>
+int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+ compat_sigset_t *set, struct pt_regs *regs);
+int ia32_setup_frame(int sig, struct k_sigaction *ka,
+ compat_sigset_t *set, struct pt_regs *regs);
+#else
+# define user_i387_ia32_struct user_i387_struct
+# define user32_fxsr_struct user_fxsr_struct
+# define ia32_setup_frame __setup_frame
+# define ia32_setup_rt_frame __setup_rt_frame
+#endif
+
+extern unsigned int mxcsr_feature_mask;
extern void fpu_init(void);
+extern void eager_fpu_init(void);
DECLARE_PER_CPU(struct task_struct *, fpu_owner_task);
+extern void convert_from_fxsr(struct user_i387_ia32_struct *env,
+ struct task_struct *tsk);
+extern void convert_to_fxsr(struct task_struct *tsk,
+ const struct user_i387_ia32_struct *env);
+
extern user_regset_active_fn fpregs_active, xfpregs_active;
extern user_regset_get_fn fpregs_get, xfpregs_get, fpregs_soft_get,
xstateregs_get;
extern user_regset_set_fn fpregs_set, xfpregs_set, fpregs_soft_set,
xstateregs_set;
-
/*
* xstateregs_active == fpregs_active. Please refer to the comment
* at the definition of fpregs_active.
*/
#define xstateregs_active fpregs_active
-extern struct _fpx_sw_bytes fx_sw_reserved;
-#ifdef CONFIG_IA32_EMULATION
-extern unsigned int sig_xstate_ia32_size;
-extern struct _fpx_sw_bytes fx_sw_reserved_ia32;
-struct _fpstate_ia32;
-struct _xstate_ia32;
-extern int save_i387_xstate_ia32(void __user *buf);
-extern int restore_i387_xstate_ia32(void __user *buf);
-#endif
-
#ifdef CONFIG_MATH_EMULATION
+# define HAVE_HWFP (boot_cpu_data.hard_math)
extern void finit_soft_fpu(struct i387_soft_struct *soft);
#else
+# define HAVE_HWFP 1
static inline void finit_soft_fpu(struct i387_soft_struct *soft) {}
#endif
+static inline int is_ia32_compat_frame(void)
+{
+ return config_enabled(CONFIG_IA32_EMULATION) &&
+ test_thread_flag(TIF_IA32);
+}
+
+static inline int is_ia32_frame(void)
+{
+ return config_enabled(CONFIG_X86_32) || is_ia32_compat_frame();
+}
+
+static inline int is_x32_frame(void)
+{
+ return config_enabled(CONFIG_X86_X32_ABI) && test_thread_flag(TIF_X32);
+}
+
#define X87_FSW_ES (1 << 7) /* Exception Summary */
+static __always_inline __pure bool use_eager_fpu(void)
+{
+ return static_cpu_has(X86_FEATURE_EAGER_FPU);
+}
+
static __always_inline __pure bool use_xsaveopt(void)
{
return static_cpu_has(X86_FEATURE_XSAVEOPT);
@@ -72,6 +106,13 @@ static __always_inline __pure bool use_fxsr(void)
return static_cpu_has(X86_FEATURE_FXSR);
}
+static inline void fx_finit(struct i387_fxsave_struct *fx)
+{
+ memset(fx, 0, xstate_size);
+ fx->cwd = 0x37f;
+ fx->mxcsr = MXCSR_DEFAULT;
+}
+
extern void __sanitize_i387_state(struct task_struct *);
static inline void sanitize_i387_state(struct task_struct *tsk)
@@ -81,131 +122,121 @@ static inline void sanitize_i387_state(struct task_struct *tsk)
__sanitize_i387_state(tsk);
}
-#ifdef CONFIG_X86_64
-static inline int fxrstor_checking(struct i387_fxsave_struct *fx)
-{
- int err;
-
- /* See comment in fxsave() below. */
-#ifdef CONFIG_AS_FXSAVEQ
- asm volatile("1: fxrstorq %[fx]\n\t"
- "2:\n"
- ".section .fixup,\"ax\"\n"
- "3: movl $-1,%[err]\n"
- " jmp 2b\n"
- ".previous\n"
- _ASM_EXTABLE(1b, 3b)
- : [err] "=r" (err)
- : [fx] "m" (*fx), "0" (0));
-#else
- asm volatile("1: rex64/fxrstor (%[fx])\n\t"
- "2:\n"
- ".section .fixup,\"ax\"\n"
- "3: movl $-1,%[err]\n"
- " jmp 2b\n"
- ".previous\n"
- _ASM_EXTABLE(1b, 3b)
- : [err] "=r" (err)
- : [fx] "R" (fx), "m" (*fx), "0" (0));
-#endif
- return err;
+#define user_insn(insn, output, input...) \
+({ \
+ int err; \
+ asm volatile(ASM_STAC "\n" \
+ "1:" #insn "\n\t" \
+ "2: " ASM_CLAC "\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: movl $-1,%[err]\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE(1b, 3b) \
+ : [err] "=r" (err), output \
+ : "0"(0), input); \
+ err; \
+})
+
+#define check_insn(insn, output, input...) \
+({ \
+ int err; \
+ asm volatile("1:" #insn "\n\t" \
+ "2:\n" \
+ ".section .fixup,\"ax\"\n" \
+ "3: movl $-1,%[err]\n" \
+ " jmp 2b\n" \
+ ".previous\n" \
+ _ASM_EXTABLE(1b, 3b) \
+ : [err] "=r" (err), output \
+ : "0"(0), input); \
+ err; \
+})
+
+static inline int fsave_user(struct i387_fsave_struct __user *fx)
+{
+ return user_insn(fnsave %[fx]; fwait, [fx] "=m" (*fx), "m" (*fx));
}
static inline int fxsave_user(struct i387_fxsave_struct __user *fx)
{
- int err;
+ if (config_enabled(CONFIG_X86_32))
+ return user_insn(fxsave %[fx], [fx] "=m" (*fx), "m" (*fx));
+ else if (config_enabled(CONFIG_AS_FXSAVEQ))
+ return user_insn(fxsaveq %[fx], [fx] "=m" (*fx), "m" (*fx));
- /*
- * Clear the bytes not touched by the fxsave and reserved
- * for the SW usage.
- */
- err = __clear_user(&fx->sw_reserved,
- sizeof(struct _fpx_sw_bytes));
- if (unlikely(err))
- return -EFAULT;
-
- /* See comment in fxsave() below. */
-#ifdef CONFIG_AS_FXSAVEQ
- asm volatile("1: fxsaveq %[fx]\n\t"
- "2:\n"
- ".section .fixup,\"ax\"\n"
- "3: movl $-1,%[err]\n"
- " jmp 2b\n"
- ".previous\n"
- _ASM_EXTABLE(1b, 3b)
- : [err] "=r" (err), [fx] "=m" (*fx)
- : "0" (0));
-#else
- asm volatile("1: rex64/fxsave (%[fx])\n\t"
- "2:\n"
- ".section .fixup,\"ax\"\n"
- "3: movl $-1,%[err]\n"
- " jmp 2b\n"
- ".previous\n"
- _ASM_EXTABLE(1b, 3b)
- : [err] "=r" (err), "=m" (*fx)
- : [fx] "R" (fx), "0" (0));
-#endif
- if (unlikely(err) &&
- __clear_user(fx, sizeof(struct i387_fxsave_struct)))
- err = -EFAULT;
- /* No need to clear here because the caller clears USED_MATH */
- return err;
+ /* See comment in fpu_fxsave() below. */
+ return user_insn(rex64/fxsave (%[fx]), "=m" (*fx), [fx] "R" (fx));
}
-static inline void fpu_fxsave(struct fpu *fpu)
+static inline int fxrstor_checking(struct i387_fxsave_struct *fx)
{
- /* Using "rex64; fxsave %0" is broken because, if the memory operand
- uses any extended registers for addressing, a second REX prefix
- will be generated (to the assembler, rex64 followed by semicolon
- is a separate instruction), and hence the 64-bitness is lost. */
+ if (config_enabled(CONFIG_X86_32))
+ return check_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
+ else if (config_enabled(CONFIG_AS_FXSAVEQ))
+ return check_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
-#ifdef CONFIG_AS_FXSAVEQ
- /* Using "fxsaveq %0" would be the ideal choice, but is only supported
- starting with gas 2.16. */
- __asm__ __volatile__("fxsaveq %0"
- : "=m" (fpu->state->fxsave));
-#else
- /* Using, as a workaround, the properly prefixed form below isn't
- accepted by any binutils version so far released, complaining that
- the same type of prefix is used twice if an extended register is
- needed for addressing (fix submitted to mainline 2005-11-21).
- asm volatile("rex64/fxsave %0"
- : "=m" (fpu->state->fxsave));
- This, however, we can work around by forcing the compiler to select
- an addressing mode that doesn't require extended registers. */
- asm volatile("rex64/fxsave (%[fx])"
- : "=m" (fpu->state->fxsave)
- : [fx] "R" (&fpu->state->fxsave));
-#endif
+ /* See comment in fpu_fxsave() below. */
+ return check_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx),
+ "m" (*fx));
}
-#else /* CONFIG_X86_32 */
+static inline int fxrstor_user(struct i387_fxsave_struct __user *fx)
+{
+ if (config_enabled(CONFIG_X86_32))
+ return user_insn(fxrstor %[fx], "=m" (*fx), [fx] "m" (*fx));
+ else if (config_enabled(CONFIG_AS_FXSAVEQ))
+ return user_insn(fxrstorq %[fx], "=m" (*fx), [fx] "m" (*fx));
-/* perform fxrstor iff the processor has extended states, otherwise frstor */
-static inline int fxrstor_checking(struct i387_fxsave_struct *fx)
+ /* See comment in fpu_fxsave() below. */
+ return user_insn(rex64/fxrstor (%[fx]), "=m" (*fx), [fx] "R" (fx),
+ "m" (*fx));
+}
+
+static inline int frstor_checking(struct i387_fsave_struct *fx)
{
- /*
- * The "nop" is needed to make the instructions the same
- * length.
- */
- alternative_input(
- "nop ; frstor %1",
- "fxrstor %1",
- X86_FEATURE_FXSR,
- "m" (*fx));
+ return check_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
+}
- return 0;
+static inline int frstor_user(struct i387_fsave_struct __user *fx)
+{
+ return user_insn(frstor %[fx], "=m" (*fx), [fx] "m" (*fx));
}
static inline void fpu_fxsave(struct fpu *fpu)
{
- asm volatile("fxsave %[fx]"
- : [fx] "=m" (fpu->state->fxsave));
+ if (config_enabled(CONFIG_X86_32))
+ asm volatile( "fxsave %[fx]" : [fx] "=m" (fpu->state->fxsave));
+ else if (config_enabled(CONFIG_AS_FXSAVEQ))
+ asm volatile("fxsaveq %0" : "=m" (fpu->state->fxsave));
+ else {
+ /* Using "rex64; fxsave %0" is broken because, if the memory
+ * operand uses any extended registers for addressing, a second
+ * REX prefix will be generated (to the assembler, rex64
+ * followed by semicolon is a separate instruction), and hence
+ * the 64-bitness is lost.
+ *
+ * Using "fxsaveq %0" would be the ideal choice, but is only
+ * supported starting with gas 2.16.
+ *
+ * Using, as a workaround, the properly prefixed form below
+ * isn't accepted by any binutils version so far released,
+ * complaining that the same type of prefix is used twice if
+ * an extended register is needed for addressing (fix submitted
+ * to mainline 2005-11-21).
+ *
+ * asm volatile("rex64/fxsave %0" : "=m" (fpu->state->fxsave));
+ *
+ * This, however, we can work around by forcing the compiler to
+ * select an addressing mode that doesn't require extended
+ * registers.
+ */
+ asm volatile( "rex64/fxsave (%[fx])"
+ : "=m" (fpu->state->fxsave)
+ : [fx] "R" (&fpu->state->fxsave));
+ }
}
-#endif /* CONFIG_X86_64 */
-
/*
* These must be called with preempt disabled. Returns
* 'true' if the FPU state is still intact.
@@ -248,17 +279,14 @@ static inline int __save_init_fpu(struct task_struct *tsk)
return fpu_save_init(&tsk->thread.fpu);
}
-static inline int fpu_fxrstor_checking(struct fpu *fpu)
-{
- return fxrstor_checking(&fpu->state->fxsave);
-}
-
static inline int fpu_restore_checking(struct fpu *fpu)
{
if (use_xsave())
- return fpu_xrstor_checking(fpu);
+ return fpu_xrstor_checking(&fpu->state->xsave);
+ else if (use_fxsr())
+ return fxrstor_checking(&fpu->state->fxsave);
else
- return fpu_fxrstor_checking(fpu);
+ return frstor_checking(&fpu->state->fsave);
}
static inline int restore_fpu_checking(struct task_struct *tsk)
@@ -310,15 +338,52 @@ static inline void __thread_set_has_fpu(struct task_struct *tsk)
static inline void __thread_fpu_end(struct task_struct *tsk)
{
__thread_clear_has_fpu(tsk);
- stts();
+ if (!use_eager_fpu())
+ stts();
}
static inline void __thread_fpu_begin(struct task_struct *tsk)
{
- clts();
+ if (!use_eager_fpu())
+ clts();
__thread_set_has_fpu(tsk);
}
+static inline void __drop_fpu(struct task_struct *tsk)
+{
+ if (__thread_has_fpu(tsk)) {
+ /* Ignore delayed exceptions from user space */
+ asm volatile("1: fwait\n"
+ "2:\n"
+ _ASM_EXTABLE(1b, 2b));
+ __thread_fpu_end(tsk);
+ }
+}
+
+static inline void drop_fpu(struct task_struct *tsk)
+{
+ /*
+ * Forget coprocessor state..
+ */
+ preempt_disable();
+ tsk->fpu_counter = 0;
+ __drop_fpu(tsk);
+ clear_used_math();
+ preempt_enable();
+}
+
+static inline void drop_init_fpu(struct task_struct *tsk)
+{
+ if (!use_eager_fpu())
+ drop_fpu(tsk);
+ else {
+ if (use_xsave())
+ xrstor_state(init_xstate_buf, -1);
+ else
+ fxrstor_checking(&init_xstate_buf->i387);
+ }
+}
+
/*
* FPU state switching for scheduling.
*
@@ -352,7 +417,12 @@ static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct ta
{
fpu_switch_t fpu;
- fpu.preload = tsk_used_math(new) && new->fpu_counter > 5;
+ /*
+ * If the task has used the math, pre-load the FPU on xsave processors
+ * or if the past 5 consecutive context-switches used math.
+ */
+ fpu.preload = tsk_used_math(new) && (use_eager_fpu() ||
+ new->fpu_counter > 5);
if (__thread_has_fpu(old)) {
if (!__save_init_fpu(old))
cpu = ~0;
@@ -364,14 +434,14 @@ static inline fpu_switch_t switch_fpu_prepare(struct task_struct *old, struct ta
new->fpu_counter++;
__thread_set_has_fpu(new);
prefetch(new->thread.fpu.state);
- } else
+ } else if (!use_eager_fpu())
stts();
} else {
old->fpu_counter = 0;
old->thread.fpu.last_cpu = ~0;
if (fpu.preload) {
new->fpu_counter++;
- if (fpu_lazy_restore(new, cpu))
+ if (!use_eager_fpu() && fpu_lazy_restore(new, cpu))
fpu.preload = 0;
else
prefetch(new->thread.fpu.state);
@@ -391,44 +461,40 @@ static inline void switch_fpu_finish(struct task_struct *new, fpu_switch_t fpu)
{
if (fpu.preload) {
if (unlikely(restore_fpu_checking(new)))
- __thread_fpu_end(new);
+ drop_init_fpu(new);
}
}
/*
* Signal frame handlers...
*/
-extern int save_i387_xstate(void __user *buf);
-extern int restore_i387_xstate(void __user *buf);
+extern int save_xstate_sig(void __user *buf, void __user *fx, int size);
+extern int __restore_xstate_sig(void __user *buf, void __user *fx, int size);
-static inline void __clear_fpu(struct task_struct *tsk)
+static inline int xstate_sigframe_size(void)
{
- if (__thread_has_fpu(tsk)) {
- /* Ignore delayed exceptions from user space */
- asm volatile("1: fwait\n"
- "2:\n"
- _ASM_EXTABLE(1b, 2b));
- __thread_fpu_end(tsk);
+ return use_xsave() ? xstate_size + FP_XSTATE_MAGIC2_SIZE : xstate_size;
+}
+
+static inline int restore_xstate_sig(void __user *buf, int ia32_frame)
+{
+ void __user *buf_fx = buf;
+ int size = xstate_sigframe_size();
+
+ if (ia32_frame && use_fxsr()) {
+ buf_fx = buf + sizeof(struct i387_fsave_struct);
+ size += sizeof(struct i387_fsave_struct);
}
+
+ return __restore_xstate_sig(buf, buf_fx, size);
}
/*
- * The actual user_fpu_begin/end() functions
- * need to be preemption-safe.
+ * Need to be preemption-safe.
*
- * NOTE! user_fpu_end() must be used only after you
- * have saved the FP state, and user_fpu_begin() must
- * be used only immediately before restoring it.
- * These functions do not do any save/restore on
- * their own.
+ * NOTE! user_fpu_begin() must be used only immediately before restoring
+ * it. This function does not do any save/restore on their own.
*/
-static inline void user_fpu_end(void)
-{
- preempt_disable();
- __thread_fpu_end(current);
- preempt_enable();
-}
-
static inline void user_fpu_begin(void)
{
preempt_disable();
@@ -437,25 +503,32 @@ static inline void user_fpu_begin(void)
preempt_enable();
}
+static inline void __save_fpu(struct task_struct *tsk)
+{
+ if (use_xsave())
+ xsave_state(&tsk->thread.fpu.state->xsave, -1);
+ else
+ fpu_fxsave(&tsk->thread.fpu);
+}
+
/*
* These disable preemption on their own and are safe
*/
static inline void save_init_fpu(struct task_struct *tsk)
{
WARN_ON_ONCE(!__thread_has_fpu(tsk));
+
+ if (use_eager_fpu()) {
+ __save_fpu(tsk);
+ return;
+ }
+
preempt_disable();
__save_init_fpu(tsk);
__thread_fpu_end(tsk);
preempt_enable();
}
-static inline void clear_fpu(struct task_struct *tsk)
-{
- preempt_disable();
- __clear_fpu(tsk);
- preempt_enable();
-}
-
/*
* i387 state interaction
*/
@@ -510,11 +583,34 @@ static inline void fpu_free(struct fpu *fpu)
}
}
-static inline void fpu_copy(struct fpu *dst, struct fpu *src)
+static inline void fpu_copy(struct task_struct *dst, struct task_struct *src)
{
- memcpy(dst->state, src->state, xstate_size);
+ if (use_eager_fpu()) {
+ memset(&dst->thread.fpu.state->xsave, 0, xstate_size);
+ __save_fpu(dst);
+ } else {
+ struct fpu *dfpu = &dst->thread.fpu;
+ struct fpu *sfpu = &src->thread.fpu;
+
+ unlazy_fpu(src);
+ memcpy(dfpu->state, sfpu->state, xstate_size);
+ }
}
-extern void fpu_finit(struct fpu *fpu);
+static inline unsigned long
+alloc_mathframe(unsigned long sp, int ia32_frame, unsigned long *buf_fx,
+ unsigned long *size)
+{
+ unsigned long frame_size = xstate_sigframe_size();
+
+ *buf_fx = sp = round_down(sp - frame_size, 64);
+ if (ia32_frame && use_fxsr()) {
+ frame_size += sizeof(struct i387_fsave_struct);
+ sp -= sizeof(struct i387_fsave_struct);
+ }
+
+ *size = frame_size;
+ return sp;
+}
#endif
diff --git a/arch/x86/include/asm/ftrace.h b/arch/x86/include/asm/ftrace.h
index b0767bc08740..9a25b522d377 100644
--- a/arch/x86/include/asm/ftrace.h
+++ b/arch/x86/include/asm/ftrace.h
@@ -3,38 +3,54 @@
#ifdef __ASSEMBLY__
- .macro MCOUNT_SAVE_FRAME
- /* taken from glibc */
- subq $0x38, %rsp
- movq %rax, (%rsp)
- movq %rcx, 8(%rsp)
- movq %rdx, 16(%rsp)
- movq %rsi, 24(%rsp)
- movq %rdi, 32(%rsp)
- movq %r8, 40(%rsp)
- movq %r9, 48(%rsp)
+ /* skip is set if the stack was already partially adjusted */
+ .macro MCOUNT_SAVE_FRAME skip=0
+ /*
+ * We add enough stack to save all regs.
+ */
+ subq $(SS+8-\skip), %rsp
+ movq %rax, RAX(%rsp)
+ movq %rcx, RCX(%rsp)
+ movq %rdx, RDX(%rsp)
+ movq %rsi, RSI(%rsp)
+ movq %rdi, RDI(%rsp)
+ movq %r8, R8(%rsp)
+ movq %r9, R9(%rsp)
+ /* Move RIP to its proper location */
+ movq SS+8(%rsp), %rdx
+ movq %rdx, RIP(%rsp)
.endm
- .macro MCOUNT_RESTORE_FRAME
- movq 48(%rsp), %r9
- movq 40(%rsp), %r8
- movq 32(%rsp), %rdi
- movq 24(%rsp), %rsi
- movq 16(%rsp), %rdx
- movq 8(%rsp), %rcx
- movq (%rsp), %rax
- addq $0x38, %rsp
+ .macro MCOUNT_RESTORE_FRAME skip=0
+ movq R9(%rsp), %r9
+ movq R8(%rsp), %r8
+ movq RDI(%rsp), %rdi
+ movq RSI(%rsp), %rsi
+ movq RDX(%rsp), %rdx
+ movq RCX(%rsp), %rcx
+ movq RAX(%rsp), %rax
+ addq $(SS+8-\skip), %rsp
.endm
#endif
#ifdef CONFIG_FUNCTION_TRACER
-#define MCOUNT_ADDR ((long)(mcount))
+#ifdef CC_USING_FENTRY
+# define MCOUNT_ADDR ((long)(__fentry__))
+#else
+# define MCOUNT_ADDR ((long)(mcount))
+#endif
#define MCOUNT_INSN_SIZE 5 /* sizeof mcount call */
+#ifdef CONFIG_DYNAMIC_FTRACE
+#define ARCH_SUPPORTS_FTRACE_OPS 1
+#define ARCH_SUPPORTS_FTRACE_SAVE_REGS
+#endif
+
#ifndef __ASSEMBLY__
extern void mcount(void);
extern atomic_t modifying_ftrace_code;
+extern void __fentry__(void);
static inline unsigned long ftrace_call_adjust(unsigned long addr)
{
diff --git a/arch/x86/include/asm/futex.h b/arch/x86/include/asm/futex.h
index 71ecbcba1a4e..f373046e63ec 100644
--- a/arch/x86/include/asm/futex.h
+++ b/arch/x86/include/asm/futex.h
@@ -9,10 +9,13 @@
#include <asm/asm.h>
#include <asm/errno.h>
#include <asm/processor.h>
+#include <asm/smap.h>
#define __futex_atomic_op1(insn, ret, oldval, uaddr, oparg) \
- asm volatile("1:\t" insn "\n" \
- "2:\t.section .fixup,\"ax\"\n" \
+ asm volatile("\t" ASM_STAC "\n" \
+ "1:\t" insn "\n" \
+ "2:\t" ASM_CLAC "\n" \
+ "\t.section .fixup,\"ax\"\n" \
"3:\tmov\t%3, %1\n" \
"\tjmp\t2b\n" \
"\t.previous\n" \
@@ -21,12 +24,14 @@
: "i" (-EFAULT), "0" (oparg), "1" (0))
#define __futex_atomic_op2(insn, ret, oldval, uaddr, oparg) \
- asm volatile("1:\tmovl %2, %0\n" \
+ asm volatile("\t" ASM_STAC "\n" \
+ "1:\tmovl %2, %0\n" \
"\tmovl\t%0, %3\n" \
"\t" insn "\n" \
"2:\t" LOCK_PREFIX "cmpxchgl %3, %2\n" \
"\tjnz\t1b\n" \
- "3:\t.section .fixup,\"ax\"\n" \
+ "3:\t" ASM_CLAC "\n" \
+ "\t.section .fixup,\"ax\"\n" \
"4:\tmov\t%5, %1\n" \
"\tjmp\t3b\n" \
"\t.previous\n" \
@@ -122,8 +127,10 @@ static inline int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr,
if (!access_ok(VERIFY_WRITE, uaddr, sizeof(u32)))
return -EFAULT;
- asm volatile("1:\t" LOCK_PREFIX "cmpxchgl %4, %2\n"
- "2:\t.section .fixup, \"ax\"\n"
+ asm volatile("\t" ASM_STAC "\n"
+ "1:\t" LOCK_PREFIX "cmpxchgl %4, %2\n"
+ "2:\t" ASM_CLAC "\n"
+ "\t.section .fixup, \"ax\"\n"
"3:\tmov %3, %0\n"
"\tjmp 2b\n"
"\t.previous\n"
diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h
index d3895dbf4ddb..81f04cee5f74 100644
--- a/arch/x86/include/asm/hardirq.h
+++ b/arch/x86/include/asm/hardirq.h
@@ -18,6 +18,10 @@ typedef struct {
#ifdef CONFIG_SMP
unsigned int irq_resched_count;
unsigned int irq_call_count;
+ /*
+ * irq_tlb_count is double-counted in irq_call_count, so it must be
+ * subtracted from irq_call_count when displaying irq_call_count
+ */
unsigned int irq_tlb_count;
#endif
#ifdef CONFIG_X86_THERMAL_VECTOR
diff --git a/arch/x86/include/asm/hpet.h b/arch/x86/include/asm/hpet.h
index 2c392d663dce..434e2106cc87 100644
--- a/arch/x86/include/asm/hpet.h
+++ b/arch/x86/include/asm/hpet.h
@@ -35,8 +35,6 @@
#define HPET_ID_NUMBER_SHIFT 8
#define HPET_ID_VENDOR_SHIFT 16
-#define HPET_ID_VENDOR_8086 0x8086
-
#define HPET_CFG_ENABLE 0x001
#define HPET_CFG_LEGACY 0x002
#define HPET_LEGACY_8254 2
diff --git a/arch/x86/include/asm/i387.h b/arch/x86/include/asm/i387.h
index 257d9cca214f..ed8089d69094 100644
--- a/arch/x86/include/asm/i387.h
+++ b/arch/x86/include/asm/i387.h
@@ -19,12 +19,37 @@ struct pt_regs;
struct user_i387_struct;
extern int init_fpu(struct task_struct *child);
+extern void fpu_finit(struct fpu *fpu);
extern int dump_fpu(struct pt_regs *, struct user_i387_struct *);
extern void math_state_restore(void);
extern bool irq_fpu_usable(void);
-extern void kernel_fpu_begin(void);
-extern void kernel_fpu_end(void);
+
+/*
+ * Careful: __kernel_fpu_begin/end() must be called with preempt disabled
+ * and they don't touch the preempt state on their own.
+ * If you enable preemption after __kernel_fpu_begin(), preempt notifier
+ * should call the __kernel_fpu_end() to prevent the kernel/user FPU
+ * state from getting corrupted. KVM for example uses this model.
+ *
+ * All other cases use kernel_fpu_begin/end() which disable preemption
+ * during kernel FPU usage.
+ */
+extern void __kernel_fpu_begin(void);
+extern void __kernel_fpu_end(void);
+
+static inline void kernel_fpu_begin(void)
+{
+ WARN_ON_ONCE(!irq_fpu_usable());
+ preempt_disable();
+ __kernel_fpu_begin();
+}
+
+static inline void kernel_fpu_end(void)
+{
+ __kernel_fpu_end();
+ preempt_enable();
+}
/*
* Some instructions like VIA's padlock instructions generate a spurious
diff --git a/arch/x86/include/asm/iommu_table.h b/arch/x86/include/asm/iommu_table.h
index f229b13a5f30..f42a04735a0a 100644
--- a/arch/x86/include/asm/iommu_table.h
+++ b/arch/x86/include/asm/iommu_table.h
@@ -48,7 +48,7 @@ struct iommu_table_entry {
#define __IOMMU_INIT(_detect, _depend, _early_init, _late_init, _finish)\
- static const struct iommu_table_entry const \
+ static const struct iommu_table_entry \
__iommu_entry_##_detect __used \
__attribute__ ((unused, __section__(".iommu_table"), \
aligned((sizeof(void *))))) \
@@ -63,10 +63,10 @@ struct iommu_table_entry {
* to stop detecting the other IOMMUs after yours has been detected.
*/
#define IOMMU_INIT_POST(_detect) \
- __IOMMU_INIT(_detect, pci_swiotlb_detect_4gb, 0, 0, 0)
+ __IOMMU_INIT(_detect, pci_swiotlb_detect_4gb, NULL, NULL, 0)
#define IOMMU_INIT_POST_FINISH(detect) \
- __IOMMU_INIT(_detect, pci_swiotlb_detect_4gb, 0, 0, 1)
+ __IOMMU_INIT(_detect, pci_swiotlb_detect_4gb, NULL, NULL, 1)
/*
* A more sophisticated version of IOMMU_INIT. This variant requires:
diff --git a/arch/x86/include/asm/kprobes.h b/arch/x86/include/asm/kprobes.h
index 547882539157..d3ddd17405d0 100644
--- a/arch/x86/include/asm/kprobes.h
+++ b/arch/x86/include/asm/kprobes.h
@@ -27,6 +27,7 @@
#include <asm/insn.h>
#define __ARCH_WANT_KPROBES_INSN_SLOT
+#define ARCH_SUPPORTS_KPROBES_ON_FTRACE
struct pt_regs;
struct kprobe;
diff --git a/arch/x86/include/asm/kvm.h b/arch/x86/include/asm/kvm.h
index 246617efd67f..41e08cb6a092 100644
--- a/arch/x86/include/asm/kvm.h
+++ b/arch/x86/include/asm/kvm.h
@@ -9,6 +9,22 @@
#include <linux/types.h>
#include <linux/ioctl.h>
+#define DE_VECTOR 0
+#define DB_VECTOR 1
+#define BP_VECTOR 3
+#define OF_VECTOR 4
+#define BR_VECTOR 5
+#define UD_VECTOR 6
+#define NM_VECTOR 7
+#define DF_VECTOR 8
+#define TS_VECTOR 10
+#define NP_VECTOR 11
+#define SS_VECTOR 12
+#define GP_VECTOR 13
+#define PF_VECTOR 14
+#define MF_VECTOR 16
+#define MC_VECTOR 18
+
/* Select x86 specific features in <linux/kvm.h> */
#define __KVM_HAVE_PIT
#define __KVM_HAVE_IOAPIC
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 09155d64cf7e..1eaa6b056670 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -75,22 +75,6 @@
#define KVM_HPAGE_MASK(x) (~(KVM_HPAGE_SIZE(x) - 1))
#define KVM_PAGES_PER_HPAGE(x) (KVM_HPAGE_SIZE(x) / PAGE_SIZE)
-#define DE_VECTOR 0
-#define DB_VECTOR 1
-#define BP_VECTOR 3
-#define OF_VECTOR 4
-#define BR_VECTOR 5
-#define UD_VECTOR 6
-#define NM_VECTOR 7
-#define DF_VECTOR 8
-#define TS_VECTOR 10
-#define NP_VECTOR 11
-#define SS_VECTOR 12
-#define GP_VECTOR 13
-#define PF_VECTOR 14
-#define MF_VECTOR 16
-#define MC_VECTOR 18
-
#define SELECTOR_TI_MASK (1 << 2)
#define SELECTOR_RPL_MASK 0x03
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h
index a3ac52b29cbf..54d73b1f00a0 100644
--- a/arch/x86/include/asm/mce.h
+++ b/arch/x86/include/asm/mce.h
@@ -116,19 +116,9 @@ struct mce_log {
/* Software defined banks */
#define MCE_EXTENDED_BANK 128
#define MCE_THERMAL_BANK MCE_EXTENDED_BANK + 0
-
-#define K8_MCE_THRESHOLD_BASE (MCE_EXTENDED_BANK + 1) /* MCE_AMD */
-#define K8_MCE_THRESHOLD_BANK_0 (MCE_THRESHOLD_BASE + 0 * 9)
-#define K8_MCE_THRESHOLD_BANK_1 (MCE_THRESHOLD_BASE + 1 * 9)
-#define K8_MCE_THRESHOLD_BANK_2 (MCE_THRESHOLD_BASE + 2 * 9)
-#define K8_MCE_THRESHOLD_BANK_3 (MCE_THRESHOLD_BASE + 3 * 9)
-#define K8_MCE_THRESHOLD_BANK_4 (MCE_THRESHOLD_BASE + 4 * 9)
-#define K8_MCE_THRESHOLD_BANK_5 (MCE_THRESHOLD_BASE + 5 * 9)
-#define K8_MCE_THRESHOLD_DRAM_ECC (MCE_THRESHOLD_BANK_4 + 0)
-
+#define K8_MCE_THRESHOLD_BASE (MCE_EXTENDED_BANK + 1)
#ifdef __KERNEL__
-
extern void mce_register_decode_chain(struct notifier_block *nb);
extern void mce_unregister_decode_chain(struct notifier_block *nb);
@@ -171,6 +161,7 @@ DECLARE_PER_CPU(struct device *, mce_device);
#ifdef CONFIG_X86_MCE_INTEL
extern int mce_cmci_disabled;
extern int mce_ignore_ce;
+extern int mce_bios_cmci_threshold;
void mce_intel_feature_init(struct cpuinfo_x86 *c);
void cmci_clear(void);
void cmci_reenable(void);
diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h
index 4ebe157bf73d..43d921b4752c 100644
--- a/arch/x86/include/asm/microcode.h
+++ b/arch/x86/include/asm/microcode.h
@@ -15,8 +15,8 @@ struct microcode_ops {
enum ucode_state (*request_microcode_user) (int cpu,
const void __user *buf, size_t size);
- enum ucode_state (*request_microcode_fw) (int cpu,
- struct device *device);
+ enum ucode_state (*request_microcode_fw) (int cpu, struct device *,
+ bool refresh_fw);
void (*microcode_fini_cpu) (int cpu);
@@ -49,12 +49,6 @@ static inline struct microcode_ops * __init init_intel_microcode(void)
#ifdef CONFIG_MICROCODE_AMD
extern struct microcode_ops * __init init_amd_microcode(void);
extern void __exit exit_amd_microcode(void);
-
-static inline void get_ucode_data(void *to, const u8 *from, size_t n)
-{
- memcpy(to, from, n);
-}
-
#else
static inline struct microcode_ops * __init init_amd_microcode(void)
{
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index cb4e43bce98a..4fabcdf1cfa7 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -262,4 +262,6 @@ static inline void perf_check_microcode(void) { }
static inline void amd_pmu_disable_virt(void) { }
#endif
+#define arch_perf_out_copy_user copy_from_user_nmi
+
#endif /* _ASM_X86_PERF_EVENT_H */
diff --git a/arch/x86/include/asm/perf_regs.h b/arch/x86/include/asm/perf_regs.h
new file mode 100644
index 000000000000..3f2207bfd17b
--- /dev/null
+++ b/arch/x86/include/asm/perf_regs.h
@@ -0,0 +1,33 @@
+#ifndef _ASM_X86_PERF_REGS_H
+#define _ASM_X86_PERF_REGS_H
+
+enum perf_event_x86_regs {
+ PERF_REG_X86_AX,
+ PERF_REG_X86_BX,
+ PERF_REG_X86_CX,
+ PERF_REG_X86_DX,
+ PERF_REG_X86_SI,
+ PERF_REG_X86_DI,
+ PERF_REG_X86_BP,
+ PERF_REG_X86_SP,
+ PERF_REG_X86_IP,
+ PERF_REG_X86_FLAGS,
+ PERF_REG_X86_CS,
+ PERF_REG_X86_SS,
+ PERF_REG_X86_DS,
+ PERF_REG_X86_ES,
+ PERF_REG_X86_FS,
+ PERF_REG_X86_GS,
+ PERF_REG_X86_R8,
+ PERF_REG_X86_R9,
+ PERF_REG_X86_R10,
+ PERF_REG_X86_R11,
+ PERF_REG_X86_R12,
+ PERF_REG_X86_R13,
+ PERF_REG_X86_R14,
+ PERF_REG_X86_R15,
+
+ PERF_REG_X86_32_MAX = PERF_REG_X86_GS + 1,
+ PERF_REG_X86_64_MAX = PERF_REG_X86_R15 + 1,
+};
+#endif /* _ASM_X86_PERF_REGS_H */
diff --git a/arch/x86/include/asm/pgtable_types.h b/arch/x86/include/asm/pgtable_types.h
index 013286a10c2c..db8fec6d2953 100644
--- a/arch/x86/include/asm/pgtable_types.h
+++ b/arch/x86/include/asm/pgtable_types.h
@@ -303,11 +303,9 @@ void set_pte_vaddr(unsigned long vaddr, pte_t pte);
extern void native_pagetable_reserve(u64 start, u64 end);
#ifdef CONFIG_X86_32
-extern void native_pagetable_setup_start(pgd_t *base);
-extern void native_pagetable_setup_done(pgd_t *base);
+extern void native_pagetable_init(void);
#else
-#define native_pagetable_setup_start x86_init_pgd_noop
-#define native_pagetable_setup_done x86_init_pgd_noop
+#define native_pagetable_init paging_init
#endif
struct seq_file;
diff --git a/arch/x86/include/asm/processor-flags.h b/arch/x86/include/asm/processor-flags.h
index aea1d1d848c7..680cf09ed100 100644
--- a/arch/x86/include/asm/processor-flags.h
+++ b/arch/x86/include/asm/processor-flags.h
@@ -65,6 +65,7 @@
#define X86_CR4_PCIDE 0x00020000 /* enable PCID support */
#define X86_CR4_OSXSAVE 0x00040000 /* enable xsave and xrestore */
#define X86_CR4_SMEP 0x00100000 /* enable SMEP support */
+#define X86_CR4_SMAP 0x00200000 /* enable SMAP support */
/*
* x86-64 Task Priority Register, CR8
diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
index d048cad9bcad..b98c0d958ebb 100644
--- a/arch/x86/include/asm/processor.h
+++ b/arch/x86/include/asm/processor.h
@@ -423,7 +423,6 @@ DECLARE_INIT_PER_CPU(irq_stack_union);
DECLARE_PER_CPU(char *, irq_stack_ptr);
DECLARE_PER_CPU(unsigned int, irq_count);
-extern unsigned long kernel_eflags;
extern asmlinkage void ignore_sysret(void);
#else /* X86_64 */
#ifdef CONFIG_CC_STACKPROTECTOR
@@ -759,6 +758,8 @@ static inline void update_debugctlmsr(unsigned long debugctlmsr)
wrmsrl(MSR_IA32_DEBUGCTLMSR, debugctlmsr);
}
+extern void set_task_blockstep(struct task_struct *task, bool on);
+
/*
* from system description table in BIOS. Mostly for MCA use, but
* others may find it useful:
diff --git a/arch/x86/include/asm/rcu.h b/arch/x86/include/asm/rcu.h
new file mode 100644
index 000000000000..d1ac07a23979
--- /dev/null
+++ b/arch/x86/include/asm/rcu.h
@@ -0,0 +1,32 @@
+#ifndef _ASM_X86_RCU_H
+#define _ASM_X86_RCU_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/rcupdate.h>
+#include <asm/ptrace.h>
+
+static inline void exception_enter(struct pt_regs *regs)
+{
+ rcu_user_exit();
+}
+
+static inline void exception_exit(struct pt_regs *regs)
+{
+#ifdef CONFIG_RCU_USER_QS
+ if (user_mode(regs))
+ rcu_user_enter();
+#endif
+}
+
+#else /* __ASSEMBLY__ */
+
+#ifdef CONFIG_RCU_USER_QS
+# define SCHEDULE_USER call schedule_user
+#else
+# define SCHEDULE_USER call schedule
+#endif
+
+#endif /* !__ASSEMBLY__ */
+
+#endif
diff --git a/arch/x86/include/asm/signal.h b/arch/x86/include/asm/signal.h
index 598457cbd0f8..323973f4abf1 100644
--- a/arch/x86/include/asm/signal.h
+++ b/arch/x86/include/asm/signal.h
@@ -31,6 +31,10 @@ typedef struct {
unsigned long sig[_NSIG_WORDS];
} sigset_t;
+#ifndef CONFIG_COMPAT
+typedef sigset_t compat_sigset_t;
+#endif
+
#else
/* Here we must cater to libcs that poke about in kernel headers. */
diff --git a/arch/x86/include/asm/smap.h b/arch/x86/include/asm/smap.h
new file mode 100644
index 000000000000..8d3120f4e270
--- /dev/null
+++ b/arch/x86/include/asm/smap.h
@@ -0,0 +1,91 @@
+/*
+ * Supervisor Mode Access Prevention support
+ *
+ * Copyright (C) 2012 Intel Corporation
+ * Author: H. Peter Anvin <hpa@linux.intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; version 2
+ * of the License.
+ */
+
+#ifndef _ASM_X86_SMAP_H
+#define _ASM_X86_SMAP_H
+
+#include <linux/stringify.h>
+#include <asm/nops.h>
+#include <asm/cpufeature.h>
+
+/* "Raw" instruction opcodes */
+#define __ASM_CLAC .byte 0x0f,0x01,0xca
+#define __ASM_STAC .byte 0x0f,0x01,0xcb
+
+#ifdef __ASSEMBLY__
+
+#include <asm/alternative-asm.h>
+
+#ifdef CONFIG_X86_SMAP
+
+#define ASM_CLAC \
+ 661: ASM_NOP3 ; \
+ .pushsection .altinstr_replacement, "ax" ; \
+ 662: __ASM_CLAC ; \
+ .popsection ; \
+ .pushsection .altinstructions, "a" ; \
+ altinstruction_entry 661b, 662b, X86_FEATURE_SMAP, 3, 3 ; \
+ .popsection
+
+#define ASM_STAC \
+ 661: ASM_NOP3 ; \
+ .pushsection .altinstr_replacement, "ax" ; \
+ 662: __ASM_STAC ; \
+ .popsection ; \
+ .pushsection .altinstructions, "a" ; \
+ altinstruction_entry 661b, 662b, X86_FEATURE_SMAP, 3, 3 ; \
+ .popsection
+
+#else /* CONFIG_X86_SMAP */
+
+#define ASM_CLAC
+#define ASM_STAC
+
+#endif /* CONFIG_X86_SMAP */
+
+#else /* __ASSEMBLY__ */
+
+#include <asm/alternative.h>
+
+#ifdef CONFIG_X86_SMAP
+
+static __always_inline void clac(void)
+{
+ /* Note: a barrier is implicit in alternative() */
+ alternative(ASM_NOP3, __stringify(__ASM_CLAC), X86_FEATURE_SMAP);
+}
+
+static __always_inline void stac(void)
+{
+ /* Note: a barrier is implicit in alternative() */
+ alternative(ASM_NOP3, __stringify(__ASM_STAC), X86_FEATURE_SMAP);
+}
+
+/* These macros can be used in asm() statements */
+#define ASM_CLAC \
+ ALTERNATIVE(ASM_NOP3, __stringify(__ASM_CLAC), X86_FEATURE_SMAP)
+#define ASM_STAC \
+ ALTERNATIVE(ASM_NOP3, __stringify(__ASM_STAC), X86_FEATURE_SMAP)
+
+#else /* CONFIG_X86_SMAP */
+
+static inline void clac(void) { }
+static inline void stac(void) { }
+
+#define ASM_CLAC
+#define ASM_STAC
+
+#endif /* CONFIG_X86_SMAP */
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_X86_SMAP_H */
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index f2b83bc7d784..cdf5674dd23a 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -1,6 +1,135 @@
#ifndef __SVM_H
#define __SVM_H
+#define SVM_EXIT_READ_CR0 0x000
+#define SVM_EXIT_READ_CR3 0x003
+#define SVM_EXIT_READ_CR4 0x004
+#define SVM_EXIT_READ_CR8 0x008
+#define SVM_EXIT_WRITE_CR0 0x010
+#define SVM_EXIT_WRITE_CR3 0x013
+#define SVM_EXIT_WRITE_CR4 0x014
+#define SVM_EXIT_WRITE_CR8 0x018
+#define SVM_EXIT_READ_DR0 0x020
+#define SVM_EXIT_READ_DR1 0x021
+#define SVM_EXIT_READ_DR2 0x022
+#define SVM_EXIT_READ_DR3 0x023
+#define SVM_EXIT_READ_DR4 0x024
+#define SVM_EXIT_READ_DR5 0x025
+#define SVM_EXIT_READ_DR6 0x026
+#define SVM_EXIT_READ_DR7 0x027
+#define SVM_EXIT_WRITE_DR0 0x030
+#define SVM_EXIT_WRITE_DR1 0x031
+#define SVM_EXIT_WRITE_DR2 0x032
+#define SVM_EXIT_WRITE_DR3 0x033
+#define SVM_EXIT_WRITE_DR4 0x034
+#define SVM_EXIT_WRITE_DR5 0x035
+#define SVM_EXIT_WRITE_DR6 0x036
+#define SVM_EXIT_WRITE_DR7 0x037
+#define SVM_EXIT_EXCP_BASE 0x040
+#define SVM_EXIT_INTR 0x060
+#define SVM_EXIT_NMI 0x061
+#define SVM_EXIT_SMI 0x062
+#define SVM_EXIT_INIT 0x063
+#define SVM_EXIT_VINTR 0x064
+#define SVM_EXIT_CR0_SEL_WRITE 0x065
+#define SVM_EXIT_IDTR_READ 0x066
+#define SVM_EXIT_GDTR_READ 0x067
+#define SVM_EXIT_LDTR_READ 0x068
+#define SVM_EXIT_TR_READ 0x069
+#define SVM_EXIT_IDTR_WRITE 0x06a
+#define SVM_EXIT_GDTR_WRITE 0x06b
+#define SVM_EXIT_LDTR_WRITE 0x06c
+#define SVM_EXIT_TR_WRITE 0x06d
+#define SVM_EXIT_RDTSC 0x06e
+#define SVM_EXIT_RDPMC 0x06f
+#define SVM_EXIT_PUSHF 0x070
+#define SVM_EXIT_POPF 0x071
+#define SVM_EXIT_CPUID 0x072
+#define SVM_EXIT_RSM 0x073
+#define SVM_EXIT_IRET 0x074
+#define SVM_EXIT_SWINT 0x075
+#define SVM_EXIT_INVD 0x076
+#define SVM_EXIT_PAUSE 0x077
+#define SVM_EXIT_HLT 0x078
+#define SVM_EXIT_INVLPG 0x079
+#define SVM_EXIT_INVLPGA 0x07a
+#define SVM_EXIT_IOIO 0x07b
+#define SVM_EXIT_MSR 0x07c
+#define SVM_EXIT_TASK_SWITCH 0x07d
+#define SVM_EXIT_FERR_FREEZE 0x07e
+#define SVM_EXIT_SHUTDOWN 0x07f
+#define SVM_EXIT_VMRUN 0x080
+#define SVM_EXIT_VMMCALL 0x081
+#define SVM_EXIT_VMLOAD 0x082
+#define SVM_EXIT_VMSAVE 0x083
+#define SVM_EXIT_STGI 0x084
+#define SVM_EXIT_CLGI 0x085
+#define SVM_EXIT_SKINIT 0x086
+#define SVM_EXIT_RDTSCP 0x087
+#define SVM_EXIT_ICEBP 0x088
+#define SVM_EXIT_WBINVD 0x089
+#define SVM_EXIT_MONITOR 0x08a
+#define SVM_EXIT_MWAIT 0x08b
+#define SVM_EXIT_MWAIT_COND 0x08c
+#define SVM_EXIT_XSETBV 0x08d
+#define SVM_EXIT_NPF 0x400
+
+#define SVM_EXIT_ERR -1
+
+#define SVM_EXIT_REASONS \
+ { SVM_EXIT_READ_CR0, "read_cr0" }, \
+ { SVM_EXIT_READ_CR3, "read_cr3" }, \
+ { SVM_EXIT_READ_CR4, "read_cr4" }, \
+ { SVM_EXIT_READ_CR8, "read_cr8" }, \
+ { SVM_EXIT_WRITE_CR0, "write_cr0" }, \
+ { SVM_EXIT_WRITE_CR3, "write_cr3" }, \
+ { SVM_EXIT_WRITE_CR4, "write_cr4" }, \
+ { SVM_EXIT_WRITE_CR8, "write_cr8" }, \
+ { SVM_EXIT_READ_DR0, "read_dr0" }, \
+ { SVM_EXIT_READ_DR1, "read_dr1" }, \
+ { SVM_EXIT_READ_DR2, "read_dr2" }, \
+ { SVM_EXIT_READ_DR3, "read_dr3" }, \
+ { SVM_EXIT_WRITE_DR0, "write_dr0" }, \
+ { SVM_EXIT_WRITE_DR1, "write_dr1" }, \
+ { SVM_EXIT_WRITE_DR2, "write_dr2" }, \
+ { SVM_EXIT_WRITE_DR3, "write_dr3" }, \
+ { SVM_EXIT_WRITE_DR5, "write_dr5" }, \
+ { SVM_EXIT_WRITE_DR7, "write_dr7" }, \
+ { SVM_EXIT_EXCP_BASE + DB_VECTOR, "DB excp" }, \
+ { SVM_EXIT_EXCP_BASE + BP_VECTOR, "BP excp" }, \
+ { SVM_EXIT_EXCP_BASE + UD_VECTOR, "UD excp" }, \
+ { SVM_EXIT_EXCP_BASE + PF_VECTOR, "PF excp" }, \
+ { SVM_EXIT_EXCP_BASE + NM_VECTOR, "NM excp" }, \
+ { SVM_EXIT_EXCP_BASE + MC_VECTOR, "MC excp" }, \
+ { SVM_EXIT_INTR, "interrupt" }, \
+ { SVM_EXIT_NMI, "nmi" }, \
+ { SVM_EXIT_SMI, "smi" }, \
+ { SVM_EXIT_INIT, "init" }, \
+ { SVM_EXIT_VINTR, "vintr" }, \
+ { SVM_EXIT_CPUID, "cpuid" }, \
+ { SVM_EXIT_INVD, "invd" }, \
+ { SVM_EXIT_HLT, "hlt" }, \
+ { SVM_EXIT_INVLPG, "invlpg" }, \
+ { SVM_EXIT_INVLPGA, "invlpga" }, \
+ { SVM_EXIT_IOIO, "io" }, \
+ { SVM_EXIT_MSR, "msr" }, \
+ { SVM_EXIT_TASK_SWITCH, "task_switch" }, \
+ { SVM_EXIT_SHUTDOWN, "shutdown" }, \
+ { SVM_EXIT_VMRUN, "vmrun" }, \
+ { SVM_EXIT_VMMCALL, "hypercall" }, \
+ { SVM_EXIT_VMLOAD, "vmload" }, \
+ { SVM_EXIT_VMSAVE, "vmsave" }, \
+ { SVM_EXIT_STGI, "stgi" }, \
+ { SVM_EXIT_CLGI, "clgi" }, \
+ { SVM_EXIT_SKINIT, "skinit" }, \
+ { SVM_EXIT_WBINVD, "wbinvd" }, \
+ { SVM_EXIT_MONITOR, "monitor" }, \
+ { SVM_EXIT_MWAIT, "mwait" }, \
+ { SVM_EXIT_XSETBV, "xsetbv" }, \
+ { SVM_EXIT_NPF, "npf" }
+
+#ifdef __KERNEL__
+
enum {
INTERCEPT_INTR,
INTERCEPT_NMI,
@@ -264,81 +393,6 @@ struct __attribute__ ((__packed__)) vmcb {
#define SVM_EXITINFO_REG_MASK 0x0F
-#define SVM_EXIT_READ_CR0 0x000
-#define SVM_EXIT_READ_CR3 0x003
-#define SVM_EXIT_READ_CR4 0x004
-#define SVM_EXIT_READ_CR8 0x008
-#define SVM_EXIT_WRITE_CR0 0x010
-#define SVM_EXIT_WRITE_CR3 0x013
-#define SVM_EXIT_WRITE_CR4 0x014
-#define SVM_EXIT_WRITE_CR8 0x018
-#define SVM_EXIT_READ_DR0 0x020
-#define SVM_EXIT_READ_DR1 0x021
-#define SVM_EXIT_READ_DR2 0x022
-#define SVM_EXIT_READ_DR3 0x023
-#define SVM_EXIT_READ_DR4 0x024
-#define SVM_EXIT_READ_DR5 0x025
-#define SVM_EXIT_READ_DR6 0x026
-#define SVM_EXIT_READ_DR7 0x027
-#define SVM_EXIT_WRITE_DR0 0x030
-#define SVM_EXIT_WRITE_DR1 0x031
-#define SVM_EXIT_WRITE_DR2 0x032
-#define SVM_EXIT_WRITE_DR3 0x033
-#define SVM_EXIT_WRITE_DR4 0x034
-#define SVM_EXIT_WRITE_DR5 0x035
-#define SVM_EXIT_WRITE_DR6 0x036
-#define SVM_EXIT_WRITE_DR7 0x037
-#define SVM_EXIT_EXCP_BASE 0x040
-#define SVM_EXIT_INTR 0x060
-#define SVM_EXIT_NMI 0x061
-#define SVM_EXIT_SMI 0x062
-#define SVM_EXIT_INIT 0x063
-#define SVM_EXIT_VINTR 0x064
-#define SVM_EXIT_CR0_SEL_WRITE 0x065
-#define SVM_EXIT_IDTR_READ 0x066
-#define SVM_EXIT_GDTR_READ 0x067
-#define SVM_EXIT_LDTR_READ 0x068
-#define SVM_EXIT_TR_READ 0x069
-#define SVM_EXIT_IDTR_WRITE 0x06a
-#define SVM_EXIT_GDTR_WRITE 0x06b
-#define SVM_EXIT_LDTR_WRITE 0x06c
-#define SVM_EXIT_TR_WRITE 0x06d
-#define SVM_EXIT_RDTSC 0x06e
-#define SVM_EXIT_RDPMC 0x06f
-#define SVM_EXIT_PUSHF 0x070
-#define SVM_EXIT_POPF 0x071
-#define SVM_EXIT_CPUID 0x072
-#define SVM_EXIT_RSM 0x073
-#define SVM_EXIT_IRET 0x074
-#define SVM_EXIT_SWINT 0x075
-#define SVM_EXIT_INVD 0x076
-#define SVM_EXIT_PAUSE 0x077
-#define SVM_EXIT_HLT 0x078
-#define SVM_EXIT_INVLPG 0x079
-#define SVM_EXIT_INVLPGA 0x07a
-#define SVM_EXIT_IOIO 0x07b
-#define SVM_EXIT_MSR 0x07c
-#define SVM_EXIT_TASK_SWITCH 0x07d
-#define SVM_EXIT_FERR_FREEZE 0x07e
-#define SVM_EXIT_SHUTDOWN 0x07f
-#define SVM_EXIT_VMRUN 0x080
-#define SVM_EXIT_VMMCALL 0x081
-#define SVM_EXIT_VMLOAD 0x082
-#define SVM_EXIT_VMSAVE 0x083
-#define SVM_EXIT_STGI 0x084
-#define SVM_EXIT_CLGI 0x085
-#define SVM_EXIT_SKINIT 0x086
-#define SVM_EXIT_RDTSCP 0x087
-#define SVM_EXIT_ICEBP 0x088
-#define SVM_EXIT_WBINVD 0x089
-#define SVM_EXIT_MONITOR 0x08a
-#define SVM_EXIT_MWAIT 0x08b
-#define SVM_EXIT_MWAIT_COND 0x08c
-#define SVM_EXIT_XSETBV 0x08d
-#define SVM_EXIT_NPF 0x400
-
-#define SVM_EXIT_ERR -1
-
#define SVM_CR0_SELECTIVE_MASK (X86_CR0_TS | X86_CR0_MP)
#define SVM_VMLOAD ".byte 0x0f, 0x01, 0xda"
@@ -350,3 +404,4 @@ struct __attribute__ ((__packed__)) vmcb {
#endif
+#endif
diff --git a/arch/x86/include/asm/sys_ia32.h b/arch/x86/include/asm/sys_ia32.h
index 3fda9db48819..4ca1c611b552 100644
--- a/arch/x86/include/asm/sys_ia32.h
+++ b/arch/x86/include/asm/sys_ia32.h
@@ -40,7 +40,7 @@ asmlinkage long sys32_sigaction(int, struct old_sigaction32 __user *,
struct old_sigaction32 __user *);
asmlinkage long sys32_alarm(unsigned int);
-asmlinkage long sys32_waitpid(compat_pid_t, unsigned int *, int);
+asmlinkage long sys32_waitpid(compat_pid_t, unsigned int __user *, int);
asmlinkage long sys32_sysfs(int, u32, u32);
asmlinkage long sys32_sched_rr_get_interval(compat_pid_t,
diff --git a/arch/x86/include/asm/thread_info.h b/arch/x86/include/asm/thread_info.h
index 89f794f007ec..c535d847e3b5 100644
--- a/arch/x86/include/asm/thread_info.h
+++ b/arch/x86/include/asm/thread_info.h
@@ -89,6 +89,7 @@ struct thread_info {
#define TIF_NOTSC 16 /* TSC is not accessible in userland */
#define TIF_IA32 17 /* IA32 compatibility process */
#define TIF_FORK 18 /* ret_from_fork */
+#define TIF_NOHZ 19 /* in adaptive nohz mode */
#define TIF_MEMDIE 20 /* is terminating due to OOM killer */
#define TIF_DEBUG 21 /* uses debug registers */
#define TIF_IO_BITMAP 22 /* uses I/O bitmap */
@@ -114,6 +115,7 @@ struct thread_info {
#define _TIF_NOTSC (1 << TIF_NOTSC)
#define _TIF_IA32 (1 << TIF_IA32)
#define _TIF_FORK (1 << TIF_FORK)
+#define _TIF_NOHZ (1 << TIF_NOHZ)
#define _TIF_DEBUG (1 << TIF_DEBUG)
#define _TIF_IO_BITMAP (1 << TIF_IO_BITMAP)
#define _TIF_FORCED_TF (1 << TIF_FORCED_TF)
@@ -126,12 +128,13 @@ struct thread_info {
/* work to do in syscall_trace_enter() */
#define _TIF_WORK_SYSCALL_ENTRY \
(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_EMU | _TIF_SYSCALL_AUDIT | \
- _TIF_SECCOMP | _TIF_SINGLESTEP | _TIF_SYSCALL_TRACEPOINT)
+ _TIF_SECCOMP | _TIF_SINGLESTEP | _TIF_SYSCALL_TRACEPOINT | \
+ _TIF_NOHZ)
/* work to do in syscall_trace_leave() */
#define _TIF_WORK_SYSCALL_EXIT \
(_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT | _TIF_SINGLESTEP | \
- _TIF_SYSCALL_TRACEPOINT)
+ _TIF_SYSCALL_TRACEPOINT | _TIF_NOHZ)
/* work to do on interrupt/exception return */
#define _TIF_WORK_MASK \
@@ -141,7 +144,8 @@ struct thread_info {
/* work to do on any return to user space */
#define _TIF_ALLWORK_MASK \
- ((0x0000FFFF & ~_TIF_SECCOMP) | _TIF_SYSCALL_TRACEPOINT)
+ ((0x0000FFFF & ~_TIF_SECCOMP) | _TIF_SYSCALL_TRACEPOINT | \
+ _TIF_NOHZ)
/* Only used for 64 bit */
#define _TIF_DO_NOTIFY_MASK \
diff --git a/arch/x86/include/asm/uaccess.h b/arch/x86/include/asm/uaccess.h
index e1f3a17034fc..a91acfbb1a98 100644
--- a/arch/x86/include/asm/uaccess.h
+++ b/arch/x86/include/asm/uaccess.h
@@ -9,6 +9,7 @@
#include <linux/string.h>
#include <asm/asm.h>
#include <asm/page.h>
+#include <asm/smap.h>
#define VERIFY_READ 0
#define VERIFY_WRITE 1
@@ -192,9 +193,10 @@ extern int __get_user_bad(void);
#ifdef CONFIG_X86_32
#define __put_user_asm_u64(x, addr, err, errret) \
- asm volatile("1: movl %%eax,0(%2)\n" \
+ asm volatile(ASM_STAC "\n" \
+ "1: movl %%eax,0(%2)\n" \
"2: movl %%edx,4(%2)\n" \
- "3:\n" \
+ "3: " ASM_CLAC "\n" \
".section .fixup,\"ax\"\n" \
"4: movl %3,%0\n" \
" jmp 3b\n" \
@@ -205,9 +207,10 @@ extern int __get_user_bad(void);
: "A" (x), "r" (addr), "i" (errret), "0" (err))
#define __put_user_asm_ex_u64(x, addr) \
- asm volatile("1: movl %%eax,0(%1)\n" \
+ asm volatile(ASM_STAC "\n" \
+ "1: movl %%eax,0(%1)\n" \
"2: movl %%edx,4(%1)\n" \
- "3:\n" \
+ "3: " ASM_CLAC "\n" \
_ASM_EXTABLE_EX(1b, 2b) \
_ASM_EXTABLE_EX(2b, 3b) \
: : "A" (x), "r" (addr))
@@ -379,8 +382,9 @@ do { \
} while (0)
#define __get_user_asm(x, addr, err, itype, rtype, ltype, errret) \
- asm volatile("1: mov"itype" %2,%"rtype"1\n" \
- "2:\n" \
+ asm volatile(ASM_STAC "\n" \
+ "1: mov"itype" %2,%"rtype"1\n" \
+ "2: " ASM_CLAC "\n" \
".section .fixup,\"ax\"\n" \
"3: mov %3,%0\n" \
" xor"itype" %"rtype"1,%"rtype"1\n" \
@@ -443,8 +447,9 @@ struct __large_struct { unsigned long buf[100]; };
* aliasing issues.
*/
#define __put_user_asm(x, addr, err, itype, rtype, ltype, errret) \
- asm volatile("1: mov"itype" %"rtype"1,%2\n" \
- "2:\n" \
+ asm volatile(ASM_STAC "\n" \
+ "1: mov"itype" %"rtype"1,%2\n" \
+ "2: " ASM_CLAC "\n" \
".section .fixup,\"ax\"\n" \
"3: mov %3,%0\n" \
" jmp 2b\n" \
@@ -463,13 +468,13 @@ struct __large_struct { unsigned long buf[100]; };
* uaccess_try and catch
*/
#define uaccess_try do { \
- int prev_err = current_thread_info()->uaccess_err; \
current_thread_info()->uaccess_err = 0; \
+ stac(); \
barrier();
#define uaccess_catch(err) \
+ clac(); \
(err) |= (current_thread_info()->uaccess_err ? -EFAULT : 0); \
- current_thread_info()->uaccess_err = prev_err; \
} while (0)
/**
@@ -569,6 +574,9 @@ strncpy_from_user(char *dst, const char __user *src, long count);
extern __must_check long strlen_user(const char __user *str);
extern __must_check long strnlen_user(const char __user *str, long n);
+unsigned long __must_check clear_user(void __user *mem, unsigned long len);
+unsigned long __must_check __clear_user(void __user *mem, unsigned long len);
+
/*
* movsl can be slow when source and dest are not both 8-byte aligned
*/
diff --git a/arch/x86/include/asm/uaccess_32.h b/arch/x86/include/asm/uaccess_32.h
index 576e39bca6ad..7f760a9f1f61 100644
--- a/arch/x86/include/asm/uaccess_32.h
+++ b/arch/x86/include/asm/uaccess_32.h
@@ -213,7 +213,4 @@ static inline unsigned long __must_check copy_from_user(void *to,
return n;
}
-unsigned long __must_check clear_user(void __user *mem, unsigned long len);
-unsigned long __must_check __clear_user(void __user *mem, unsigned long len);
-
#endif /* _ASM_X86_UACCESS_32_H */
diff --git a/arch/x86/include/asm/uaccess_64.h b/arch/x86/include/asm/uaccess_64.h
index d8def8b3dba0..142810c457dc 100644
--- a/arch/x86/include/asm/uaccess_64.h
+++ b/arch/x86/include/asm/uaccess_64.h
@@ -217,9 +217,6 @@ int __copy_in_user(void __user *dst, const void __user *src, unsigned size)
}
}
-__must_check unsigned long clear_user(void __user *mem, unsigned long len);
-__must_check unsigned long __clear_user(void __user *mem, unsigned long len);
-
static __must_check __always_inline int
__copy_from_user_inatomic(void *dst, const void __user *src, unsigned size)
{
diff --git a/arch/x86/include/asm/uprobes.h b/arch/x86/include/asm/uprobes.h
index f3971bbcd1de..8ff8be7835ab 100644
--- a/arch/x86/include/asm/uprobes.h
+++ b/arch/x86/include/asm/uprobes.h
@@ -42,10 +42,11 @@ struct arch_uprobe {
};
struct arch_uprobe_task {
- unsigned long saved_trap_nr;
#ifdef CONFIG_X86_64
unsigned long saved_scratch_register;
#endif
+ unsigned int saved_trap_nr;
+ unsigned int saved_tf;
};
extern int arch_uprobe_analyze_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long addr);
diff --git a/arch/x86/include/asm/vdso.h b/arch/x86/include/asm/vdso.h
index bb0522850b74..fddb53d63915 100644
--- a/arch/x86/include/asm/vdso.h
+++ b/arch/x86/include/asm/vdso.h
@@ -11,7 +11,8 @@ extern const char VDSO32_PRELINK[];
#define VDSO32_SYMBOL(base, name) \
({ \
extern const char VDSO32_##name[]; \
- (void *)(VDSO32_##name - VDSO32_PRELINK + (unsigned long)(base)); \
+ (void __user *)(VDSO32_##name - VDSO32_PRELINK + \
+ (unsigned long)(base)); \
})
#endif
diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h
index 74fcb963595b..36ec21c36d68 100644
--- a/arch/x86/include/asm/vmx.h
+++ b/arch/x86/include/asm/vmx.h
@@ -25,6 +25,88 @@
*
*/
+#define VMX_EXIT_REASONS_FAILED_VMENTRY 0x80000000
+
+#define EXIT_REASON_EXCEPTION_NMI 0
+#define EXIT_REASON_EXTERNAL_INTERRUPT 1
+#define EXIT_REASON_TRIPLE_FAULT 2
+
+#define EXIT_REASON_PENDING_INTERRUPT 7
+#define EXIT_REASON_NMI_WINDOW 8
+#define EXIT_REASON_TASK_SWITCH 9
+#define EXIT_REASON_CPUID 10
+#define EXIT_REASON_HLT 12
+#define EXIT_REASON_INVD 13
+#define EXIT_REASON_INVLPG 14
+#define EXIT_REASON_RDPMC 15
+#define EXIT_REASON_RDTSC 16
+#define EXIT_REASON_VMCALL 18
+#define EXIT_REASON_VMCLEAR 19
+#define EXIT_REASON_VMLAUNCH 20
+#define EXIT_REASON_VMPTRLD 21
+#define EXIT_REASON_VMPTRST 22
+#define EXIT_REASON_VMREAD 23
+#define EXIT_REASON_VMRESUME 24
+#define EXIT_REASON_VMWRITE 25
+#define EXIT_REASON_VMOFF 26
+#define EXIT_REASON_VMON 27
+#define EXIT_REASON_CR_ACCESS 28
+#define EXIT_REASON_DR_ACCESS 29
+#define EXIT_REASON_IO_INSTRUCTION 30
+#define EXIT_REASON_MSR_READ 31
+#define EXIT_REASON_MSR_WRITE 32
+#define EXIT_REASON_INVALID_STATE 33
+#define EXIT_REASON_MWAIT_INSTRUCTION 36
+#define EXIT_REASON_MONITOR_INSTRUCTION 39
+#define EXIT_REASON_PAUSE_INSTRUCTION 40
+#define EXIT_REASON_MCE_DURING_VMENTRY 41
+#define EXIT_REASON_TPR_BELOW_THRESHOLD 43
+#define EXIT_REASON_APIC_ACCESS 44
+#define EXIT_REASON_EPT_VIOLATION 48
+#define EXIT_REASON_EPT_MISCONFIG 49
+#define EXIT_REASON_WBINVD 54
+#define EXIT_REASON_XSETBV 55
+#define EXIT_REASON_INVPCID 58
+
+#define VMX_EXIT_REASONS \
+ { EXIT_REASON_EXCEPTION_NMI, "EXCEPTION_NMI" }, \
+ { EXIT_REASON_EXTERNAL_INTERRUPT, "EXTERNAL_INTERRUPT" }, \
+ { EXIT_REASON_TRIPLE_FAULT, "TRIPLE_FAULT" }, \
+ { EXIT_REASON_PENDING_INTERRUPT, "PENDING_INTERRUPT" }, \
+ { EXIT_REASON_NMI_WINDOW, "NMI_WINDOW" }, \
+ { EXIT_REASON_TASK_SWITCH, "TASK_SWITCH" }, \
+ { EXIT_REASON_CPUID, "CPUID" }, \
+ { EXIT_REASON_HLT, "HLT" }, \
+ { EXIT_REASON_INVLPG, "INVLPG" }, \
+ { EXIT_REASON_RDPMC, "RDPMC" }, \
+ { EXIT_REASON_RDTSC, "RDTSC" }, \
+ { EXIT_REASON_VMCALL, "VMCALL" }, \
+ { EXIT_REASON_VMCLEAR, "VMCLEAR" }, \
+ { EXIT_REASON_VMLAUNCH, "VMLAUNCH" }, \
+ { EXIT_REASON_VMPTRLD, "VMPTRLD" }, \
+ { EXIT_REASON_VMPTRST, "VMPTRST" }, \
+ { EXIT_REASON_VMREAD, "VMREAD" }, \
+ { EXIT_REASON_VMRESUME, "VMRESUME" }, \
+ { EXIT_REASON_VMWRITE, "VMWRITE" }, \
+ { EXIT_REASON_VMOFF, "VMOFF" }, \
+ { EXIT_REASON_VMON, "VMON" }, \
+ { EXIT_REASON_CR_ACCESS, "CR_ACCESS" }, \
+ { EXIT_REASON_DR_ACCESS, "DR_ACCESS" }, \
+ { EXIT_REASON_IO_INSTRUCTION, "IO_INSTRUCTION" }, \
+ { EXIT_REASON_MSR_READ, "MSR_READ" }, \
+ { EXIT_REASON_MSR_WRITE, "MSR_WRITE" }, \
+ { EXIT_REASON_MWAIT_INSTRUCTION, "MWAIT_INSTRUCTION" }, \
+ { EXIT_REASON_MONITOR_INSTRUCTION, "MONITOR_INSTRUCTION" }, \
+ { EXIT_REASON_PAUSE_INSTRUCTION, "PAUSE_INSTRUCTION" }, \
+ { EXIT_REASON_MCE_DURING_VMENTRY, "MCE_DURING_VMENTRY" }, \
+ { EXIT_REASON_TPR_BELOW_THRESHOLD, "TPR_BELOW_THRESHOLD" }, \
+ { EXIT_REASON_APIC_ACCESS, "APIC_ACCESS" }, \
+ { EXIT_REASON_EPT_VIOLATION, "EPT_VIOLATION" }, \
+ { EXIT_REASON_EPT_MISCONFIG, "EPT_MISCONFIG" }, \
+ { EXIT_REASON_WBINVD, "WBINVD" }
+
+#ifdef __KERNEL__
+
#include <linux/types.h>
/*
@@ -241,49 +323,6 @@ enum vmcs_field {
HOST_RIP = 0x00006c16,
};
-#define VMX_EXIT_REASONS_FAILED_VMENTRY 0x80000000
-
-#define EXIT_REASON_EXCEPTION_NMI 0
-#define EXIT_REASON_EXTERNAL_INTERRUPT 1
-#define EXIT_REASON_TRIPLE_FAULT 2
-
-#define EXIT_REASON_PENDING_INTERRUPT 7
-#define EXIT_REASON_NMI_WINDOW 8
-#define EXIT_REASON_TASK_SWITCH 9
-#define EXIT_REASON_CPUID 10
-#define EXIT_REASON_HLT 12
-#define EXIT_REASON_INVD 13
-#define EXIT_REASON_INVLPG 14
-#define EXIT_REASON_RDPMC 15
-#define EXIT_REASON_RDTSC 16
-#define EXIT_REASON_VMCALL 18
-#define EXIT_REASON_VMCLEAR 19
-#define EXIT_REASON_VMLAUNCH 20
-#define EXIT_REASON_VMPTRLD 21
-#define EXIT_REASON_VMPTRST 22
-#define EXIT_REASON_VMREAD 23
-#define EXIT_REASON_VMRESUME 24
-#define EXIT_REASON_VMWRITE 25
-#define EXIT_REASON_VMOFF 26
-#define EXIT_REASON_VMON 27
-#define EXIT_REASON_CR_ACCESS 28
-#define EXIT_REASON_DR_ACCESS 29
-#define EXIT_REASON_IO_INSTRUCTION 30
-#define EXIT_REASON_MSR_READ 31
-#define EXIT_REASON_MSR_WRITE 32
-#define EXIT_REASON_INVALID_STATE 33
-#define EXIT_REASON_MWAIT_INSTRUCTION 36
-#define EXIT_REASON_MONITOR_INSTRUCTION 39
-#define EXIT_REASON_PAUSE_INSTRUCTION 40
-#define EXIT_REASON_MCE_DURING_VMENTRY 41
-#define EXIT_REASON_TPR_BELOW_THRESHOLD 43
-#define EXIT_REASON_APIC_ACCESS 44
-#define EXIT_REASON_EPT_VIOLATION 48
-#define EXIT_REASON_EPT_MISCONFIG 49
-#define EXIT_REASON_WBINVD 54
-#define EXIT_REASON_XSETBV 55
-#define EXIT_REASON_INVPCID 58
-
/*
* Interruption-information format
*/
@@ -488,3 +527,5 @@ enum vm_instruction_error_number {
};
#endif
+
+#endif
diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h
index 38155f667144..57693498519c 100644
--- a/arch/x86/include/asm/x86_init.h
+++ b/arch/x86/include/asm/x86_init.h
@@ -81,12 +81,13 @@ struct x86_init_mapping {
/**
* struct x86_init_paging - platform specific paging functions
- * @pagetable_setup_start: platform specific pre paging_init() call
- * @pagetable_setup_done: platform specific post paging_init() call
+ * @pagetable_init: platform specific paging initialization call to setup
+ * the kernel pagetables and prepare accessors functions.
+ * Callback must call paging_init(). Called once after the
+ * direct mapping for phys memory is available.
*/
struct x86_init_paging {
- void (*pagetable_setup_start)(pgd_t *base);
- void (*pagetable_setup_done)(pgd_t *base);
+ void (*pagetable_init)(void);
};
/**
diff --git a/arch/x86/include/asm/xen/page.h b/arch/x86/include/asm/xen/page.h
index 93971e841dd5..472b9b783019 100644
--- a/arch/x86/include/asm/xen/page.h
+++ b/arch/x86/include/asm/xen/page.h
@@ -51,7 +51,8 @@ extern unsigned long set_phys_range_identity(unsigned long pfn_s,
extern int m2p_add_override(unsigned long mfn, struct page *page,
struct gnttab_map_grant_ref *kmap_op);
-extern int m2p_remove_override(struct page *page, bool clear_pte);
+extern int m2p_remove_override(struct page *page,
+ struct gnttab_map_grant_ref *kmap_op);
extern struct page *m2p_find_override(unsigned long mfn);
extern unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn);
diff --git a/arch/x86/include/asm/xor_32.h b/arch/x86/include/asm/xor_32.h
index 454570891bdc..aabd5850bdb9 100644
--- a/arch/x86/include/asm/xor_32.h
+++ b/arch/x86/include/asm/xor_32.h
@@ -534,38 +534,6 @@ static struct xor_block_template xor_block_p5_mmx = {
* Copyright (C) 1999 Zach Brown (with obvious credit due Ingo)
*/
-#define XMMS_SAVE \
-do { \
- preempt_disable(); \
- cr0 = read_cr0(); \
- clts(); \
- asm volatile( \
- "movups %%xmm0,(%0) ;\n\t" \
- "movups %%xmm1,0x10(%0) ;\n\t" \
- "movups %%xmm2,0x20(%0) ;\n\t" \
- "movups %%xmm3,0x30(%0) ;\n\t" \
- : \
- : "r" (xmm_save) \
- : "memory"); \
-} while (0)
-
-#define XMMS_RESTORE \
-do { \
- asm volatile( \
- "sfence ;\n\t" \
- "movups (%0),%%xmm0 ;\n\t" \
- "movups 0x10(%0),%%xmm1 ;\n\t" \
- "movups 0x20(%0),%%xmm2 ;\n\t" \
- "movups 0x30(%0),%%xmm3 ;\n\t" \
- : \
- : "r" (xmm_save) \
- : "memory"); \
- write_cr0(cr0); \
- preempt_enable(); \
-} while (0)
-
-#define ALIGN16 __attribute__((aligned(16)))
-
#define OFFS(x) "16*("#x")"
#define PF_OFFS(x) "256+16*("#x")"
#define PF0(x) " prefetchnta "PF_OFFS(x)"(%1) ;\n"
@@ -587,10 +555,8 @@ static void
xor_sse_2(unsigned long bytes, unsigned long *p1, unsigned long *p2)
{
unsigned long lines = bytes >> 8;
- char xmm_save[16*4] ALIGN16;
- int cr0;
- XMMS_SAVE;
+ kernel_fpu_begin();
asm volatile(
#undef BLOCK
@@ -633,7 +599,7 @@ xor_sse_2(unsigned long bytes, unsigned long *p1, unsigned long *p2)
:
: "memory");
- XMMS_RESTORE;
+ kernel_fpu_end();
}
static void
@@ -641,10 +607,8 @@ xor_sse_3(unsigned long bytes, unsigned long *p1, unsigned long *p2,
unsigned long *p3)
{
unsigned long lines = bytes >> 8;
- char xmm_save[16*4] ALIGN16;
- int cr0;
- XMMS_SAVE;
+ kernel_fpu_begin();
asm volatile(
#undef BLOCK
@@ -694,7 +658,7 @@ xor_sse_3(unsigned long bytes, unsigned long *p1, unsigned long *p2,
:
: "memory" );
- XMMS_RESTORE;
+ kernel_fpu_end();
}
static void
@@ -702,10 +666,8 @@ xor_sse_4(unsigned long bytes, unsigned long *p1, unsigned long *p2,
unsigned long *p3, unsigned long *p4)
{
unsigned long lines = bytes >> 8;
- char xmm_save[16*4] ALIGN16;
- int cr0;
- XMMS_SAVE;
+ kernel_fpu_begin();
asm volatile(
#undef BLOCK
@@ -762,7 +724,7 @@ xor_sse_4(unsigned long bytes, unsigned long *p1, unsigned long *p2,
:
: "memory" );
- XMMS_RESTORE;
+ kernel_fpu_end();
}
static void
@@ -770,10 +732,8 @@ xor_sse_5(unsigned long bytes, unsigned long *p1, unsigned long *p2,
unsigned long *p3, unsigned long *p4, unsigned long *p5)
{
unsigned long lines = bytes >> 8;
- char xmm_save[16*4] ALIGN16;
- int cr0;
- XMMS_SAVE;
+ kernel_fpu_begin();
/* Make sure GCC forgets anything it knows about p4 or p5,
such that it won't pass to the asm volatile below a
@@ -850,7 +810,7 @@ xor_sse_5(unsigned long bytes, unsigned long *p1, unsigned long *p2,
like assuming they have some legal value. */
asm("" : "=r" (p4), "=r" (p5));
- XMMS_RESTORE;
+ kernel_fpu_end();
}
static struct xor_block_template xor_block_pIII_sse = {
diff --git a/arch/x86/include/asm/xor_64.h b/arch/x86/include/asm/xor_64.h
index b9b2323e90fe..5fc06d0b7eb5 100644
--- a/arch/x86/include/asm/xor_64.h
+++ b/arch/x86/include/asm/xor_64.h
@@ -34,41 +34,7 @@
* no advantages to be gotten from x86-64 here anyways.
*/
-typedef struct {
- unsigned long a, b;
-} __attribute__((aligned(16))) xmm_store_t;
-
-/* Doesn't use gcc to save the XMM registers, because there is no easy way to
- tell it to do a clts before the register saving. */
-#define XMMS_SAVE \
-do { \
- preempt_disable(); \
- asm volatile( \
- "movq %%cr0,%0 ;\n\t" \
- "clts ;\n\t" \
- "movups %%xmm0,(%1) ;\n\t" \
- "movups %%xmm1,0x10(%1) ;\n\t" \
- "movups %%xmm2,0x20(%1) ;\n\t" \
- "movups %%xmm3,0x30(%1) ;\n\t" \
- : "=&r" (cr0) \
- : "r" (xmm_save) \
- : "memory"); \
-} while (0)
-
-#define XMMS_RESTORE \
-do { \
- asm volatile( \
- "sfence ;\n\t" \
- "movups (%1),%%xmm0 ;\n\t" \
- "movups 0x10(%1),%%xmm1 ;\n\t" \
- "movups 0x20(%1),%%xmm2 ;\n\t" \
- "movups 0x30(%1),%%xmm3 ;\n\t" \
- "movq %0,%%cr0 ;\n\t" \
- : \
- : "r" (cr0), "r" (xmm_save) \
- : "memory"); \
- preempt_enable(); \
-} while (0)
+#include <asm/i387.h>
#define OFFS(x) "16*("#x")"
#define PF_OFFS(x) "256+16*("#x")"
@@ -91,10 +57,8 @@ static void
xor_sse_2(unsigned long bytes, unsigned long *p1, unsigned long *p2)
{
unsigned int lines = bytes >> 8;
- unsigned long cr0;
- xmm_store_t xmm_save[4];
- XMMS_SAVE;
+ kernel_fpu_begin();
asm volatile(
#undef BLOCK
@@ -135,7 +99,7 @@ xor_sse_2(unsigned long bytes, unsigned long *p1, unsigned long *p2)
: [inc] "r" (256UL)
: "memory");
- XMMS_RESTORE;
+ kernel_fpu_end();
}
static void
@@ -143,11 +107,8 @@ xor_sse_3(unsigned long bytes, unsigned long *p1, unsigned long *p2,
unsigned long *p3)
{
unsigned int lines = bytes >> 8;
- xmm_store_t xmm_save[4];
- unsigned long cr0;
-
- XMMS_SAVE;
+ kernel_fpu_begin();
asm volatile(
#undef BLOCK
#define BLOCK(i) \
@@ -194,7 +155,7 @@ xor_sse_3(unsigned long bytes, unsigned long *p1, unsigned long *p2,
[p1] "+r" (p1), [p2] "+r" (p2), [p3] "+r" (p3)
: [inc] "r" (256UL)
: "memory");
- XMMS_RESTORE;
+ kernel_fpu_end();
}
static void
@@ -202,10 +163,8 @@ xor_sse_4(unsigned long bytes, unsigned long *p1, unsigned long *p2,
unsigned long *p3, unsigned long *p4)
{
unsigned int lines = bytes >> 8;
- xmm_store_t xmm_save[4];
- unsigned long cr0;
- XMMS_SAVE;
+ kernel_fpu_begin();
asm volatile(
#undef BLOCK
@@ -261,7 +220,7 @@ xor_sse_4(unsigned long bytes, unsigned long *p1, unsigned long *p2,
: [inc] "r" (256UL)
: "memory" );
- XMMS_RESTORE;
+ kernel_fpu_end();
}
static void
@@ -269,10 +228,8 @@ xor_sse_5(unsigned long bytes, unsigned long *p1, unsigned long *p2,
unsigned long *p3, unsigned long *p4, unsigned long *p5)
{
unsigned int lines = bytes >> 8;
- xmm_store_t xmm_save[4];
- unsigned long cr0;
- XMMS_SAVE;
+ kernel_fpu_begin();
asm volatile(
#undef BLOCK
@@ -336,7 +293,7 @@ xor_sse_5(unsigned long bytes, unsigned long *p1, unsigned long *p2,
: [inc] "r" (256UL)
: "memory");
- XMMS_RESTORE;
+ kernel_fpu_end();
}
static struct xor_block_template xor_block_sse = {
diff --git a/arch/x86/include/asm/xor_avx.h b/arch/x86/include/asm/xor_avx.h
index 2510d35f480e..7ea79c5fa1f2 100644
--- a/arch/x86/include/asm/xor_avx.h
+++ b/arch/x86/include/asm/xor_avx.h
@@ -20,32 +20,6 @@
#include <linux/compiler.h>
#include <asm/i387.h>
-#define ALIGN32 __aligned(32)
-
-#define YMM_SAVED_REGS 4
-
-#define YMMS_SAVE \
-do { \
- preempt_disable(); \
- cr0 = read_cr0(); \
- clts(); \
- asm volatile("vmovaps %%ymm0, %0" : "=m" (ymm_save[0]) : : "memory"); \
- asm volatile("vmovaps %%ymm1, %0" : "=m" (ymm_save[32]) : : "memory"); \
- asm volatile("vmovaps %%ymm2, %0" : "=m" (ymm_save[64]) : : "memory"); \
- asm volatile("vmovaps %%ymm3, %0" : "=m" (ymm_save[96]) : : "memory"); \
-} while (0);
-
-#define YMMS_RESTORE \
-do { \
- asm volatile("sfence" : : : "memory"); \
- asm volatile("vmovaps %0, %%ymm3" : : "m" (ymm_save[96])); \
- asm volatile("vmovaps %0, %%ymm2" : : "m" (ymm_save[64])); \
- asm volatile("vmovaps %0, %%ymm1" : : "m" (ymm_save[32])); \
- asm volatile("vmovaps %0, %%ymm0" : : "m" (ymm_save[0])); \
- write_cr0(cr0); \
- preempt_enable(); \
-} while (0);
-
#define BLOCK4(i) \
BLOCK(32 * i, 0) \
BLOCK(32 * (i + 1), 1) \
@@ -60,10 +34,9 @@ do { \
static void xor_avx_2(unsigned long bytes, unsigned long *p0, unsigned long *p1)
{
- unsigned long cr0, lines = bytes >> 9;
- char ymm_save[32 * YMM_SAVED_REGS] ALIGN32;
+ unsigned long lines = bytes >> 9;
- YMMS_SAVE
+ kernel_fpu_begin();
while (lines--) {
#undef BLOCK
@@ -82,16 +55,15 @@ do { \
p1 = (unsigned long *)((uintptr_t)p1 + 512);
}
- YMMS_RESTORE
+ kernel_fpu_end();
}
static void xor_avx_3(unsigned long bytes, unsigned long *p0, unsigned long *p1,
unsigned long *p2)
{
- unsigned long cr0, lines = bytes >> 9;
- char ymm_save[32 * YMM_SAVED_REGS] ALIGN32;
+ unsigned long lines = bytes >> 9;
- YMMS_SAVE
+ kernel_fpu_begin();
while (lines--) {
#undef BLOCK
@@ -113,16 +85,15 @@ do { \
p2 = (unsigned long *)((uintptr_t)p2 + 512);
}
- YMMS_RESTORE
+ kernel_fpu_end();
}
static void xor_avx_4(unsigned long bytes, unsigned long *p0, unsigned long *p1,
unsigned long *p2, unsigned long *p3)
{
- unsigned long cr0, lines = bytes >> 9;
- char ymm_save[32 * YMM_SAVED_REGS] ALIGN32;
+ unsigned long lines = bytes >> 9;
- YMMS_SAVE
+ kernel_fpu_begin();
while (lines--) {
#undef BLOCK
@@ -147,16 +118,15 @@ do { \
p3 = (unsigned long *)((uintptr_t)p3 + 512);
}
- YMMS_RESTORE
+ kernel_fpu_end();
}
static void xor_avx_5(unsigned long bytes, unsigned long *p0, unsigned long *p1,
unsigned long *p2, unsigned long *p3, unsigned long *p4)
{
- unsigned long cr0, lines = bytes >> 9;
- char ymm_save[32 * YMM_SAVED_REGS] ALIGN32;
+ unsigned long lines = bytes >> 9;
- YMMS_SAVE
+ kernel_fpu_begin();
while (lines--) {
#undef BLOCK
@@ -184,7 +154,7 @@ do { \
p4 = (unsigned long *)((uintptr_t)p4 + 512);
}
- YMMS_RESTORE
+ kernel_fpu_end();
}
static struct xor_block_template xor_block_avx = {
diff --git a/arch/x86/include/asm/xsave.h b/arch/x86/include/asm/xsave.h
index 8a1b6f9b594a..0415cdabb5a6 100644
--- a/arch/x86/include/asm/xsave.h
+++ b/arch/x86/include/asm/xsave.h
@@ -34,17 +34,14 @@
extern unsigned int xstate_size;
extern u64 pcntxt_mask;
extern u64 xstate_fx_sw_bytes[USER_XSTATE_FX_SW_WORDS];
+extern struct xsave_struct *init_xstate_buf;
extern void xsave_init(void);
extern void update_regset_xstate_info(unsigned int size, u64 xstate_mask);
extern int init_fpu(struct task_struct *child);
-extern int check_for_xstate(struct i387_fxsave_struct __user *buf,
- void __user *fpstate,
- struct _fpx_sw_bytes *sw);
-static inline int fpu_xrstor_checking(struct fpu *fpu)
+static inline int fpu_xrstor_checking(struct xsave_struct *fx)
{
- struct xsave_struct *fx = &fpu->state->xsave;
int err;
asm volatile("1: .byte " REX_PREFIX "0x0f,0xae,0x2f\n\t"
@@ -69,13 +66,13 @@ static inline int xsave_user(struct xsave_struct __user *buf)
* Clear the xsave header first, so that reserved fields are
* initialized to zero.
*/
- err = __clear_user(&buf->xsave_hdr,
- sizeof(struct xsave_hdr_struct));
+ err = __clear_user(&buf->xsave_hdr, sizeof(buf->xsave_hdr));
if (unlikely(err))
return -EFAULT;
- __asm__ __volatile__("1: .byte " REX_PREFIX "0x0f,0xae,0x27\n"
- "2:\n"
+ __asm__ __volatile__(ASM_STAC "\n"
+ "1: .byte " REX_PREFIX "0x0f,0xae,0x27\n"
+ "2: " ASM_CLAC "\n"
".section .fixup,\"ax\"\n"
"3: movl $-1,%[err]\n"
" jmp 2b\n"
@@ -84,9 +81,6 @@ static inline int xsave_user(struct xsave_struct __user *buf)
: [err] "=r" (err)
: "D" (buf), "a" (-1), "d" (-1), "0" (0)
: "memory");
- if (unlikely(err) && __clear_user(buf, xstate_size))
- err = -EFAULT;
- /* No need to clear here because the caller clears USED_MATH */
return err;
}
@@ -97,8 +91,9 @@ static inline int xrestore_user(struct xsave_struct __user *buf, u64 mask)
u32 lmask = mask;
u32 hmask = mask >> 32;
- __asm__ __volatile__("1: .byte " REX_PREFIX "0x0f,0xae,0x2f\n"
- "2:\n"
+ __asm__ __volatile__(ASM_STAC "\n"
+ "1: .byte " REX_PREFIX "0x0f,0xae,0x2f\n"
+ "2: " ASM_CLAC "\n"
".section .fixup,\"ax\"\n"
"3: movl $-1,%[err]\n"
" jmp 2b\n"
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 8215e5652d97..8d7a619718b5 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -100,6 +100,8 @@ obj-$(CONFIG_SWIOTLB) += pci-swiotlb.o
obj-$(CONFIG_OF) += devicetree.o
obj-$(CONFIG_UPROBES) += uprobes.o
+obj-$(CONFIG_PERF_EVENTS) += perf_regs.o
+
###
# 64 bit specific files
ifeq ($(CONFIG_X86_64),y)
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index b2297e58c6ed..e651f7a589ac 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -656,7 +656,7 @@ static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int *pcpu)
acpi_register_lapic(physid, ACPI_MADT_ENABLED);
/*
- * If mp_register_lapic successfully generates a new logical cpu
+ * If acpi_register_lapic successfully generates a new logical cpu
* number, then the following will get us exactly what was mapped
*/
cpumask_andnot(new_map, cpu_present_mask, tmp_map);
diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c
index 1b8e5a03d942..11676cf65aee 100644
--- a/arch/x86/kernel/acpi/sleep.c
+++ b/arch/x86/kernel/acpi/sleep.c
@@ -43,17 +43,22 @@ int acpi_suspend_lowlevel(void)
header->video_mode = saved_video_mode;
+ header->pmode_behavior = 0;
+
#ifndef CONFIG_64BIT
store_gdt((struct desc_ptr *)&header->pmode_gdt);
- if (rdmsr_safe(MSR_EFER, &header->pmode_efer_low,
- &header->pmode_efer_high))
- header->pmode_efer_low = header->pmode_efer_high = 0;
+ if (!rdmsr_safe(MSR_EFER,
+ &header->pmode_efer_low,
+ &header->pmode_efer_high))
+ header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_EFER);
#endif /* !CONFIG_64BIT */
header->pmode_cr0 = read_cr0();
- header->pmode_cr4 = read_cr4_safe();
- header->pmode_behavior = 0;
+ if (__this_cpu_read(cpu_info.cpuid_level) >= 0) {
+ header->pmode_cr4 = read_cr4();
+ header->pmode_behavior |= (1 << WAKEUP_BEHAVIOR_RESTORE_CR4);
+ }
if (!rdmsr_safe(MSR_IA32_MISC_ENABLE,
&header->pmode_misc_en_low,
&header->pmode_misc_en_high))
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index ced4534baed5..ef5ccca79a6c 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -23,19 +23,6 @@
#define MAX_PATCH_LEN (255-1)
-#ifdef CONFIG_HOTPLUG_CPU
-static int smp_alt_once;
-
-static int __init bootonly(char *str)
-{
- smp_alt_once = 1;
- return 1;
-}
-__setup("smp-alt-boot", bootonly);
-#else
-#define smp_alt_once 1
-#endif
-
static int __initdata_or_module debug_alternative;
static int __init debug_alt(char *str)
@@ -317,7 +304,7 @@ static void alternatives_smp_lock(const s32 *start, const s32 *end,
/* turn DS segment override prefix into lock prefix */
if (*ptr == 0x3e)
text_poke(ptr, ((unsigned char []){0xf0}), 1);
- };
+ }
mutex_unlock(&text_mutex);
}
@@ -326,9 +313,6 @@ static void alternatives_smp_unlock(const s32 *start, const s32 *end,
{
const s32 *poff;
- if (noreplace_smp)
- return;
-
mutex_lock(&text_mutex);
for (poff = start; poff < end; poff++) {
u8 *ptr = (u8 *)poff + *poff;
@@ -338,7 +322,7 @@ static void alternatives_smp_unlock(const s32 *start, const s32 *end,
/* turn lock prefix into DS segment override prefix */
if (*ptr == 0xf0)
text_poke(ptr, ((unsigned char []){0x3E}), 1);
- };
+ }
mutex_unlock(&text_mutex);
}
@@ -359,7 +343,7 @@ struct smp_alt_module {
};
static LIST_HEAD(smp_alt_modules);
static DEFINE_MUTEX(smp_alt);
-static int smp_mode = 1; /* protected by smp_alt */
+static bool uniproc_patched = false; /* protected by smp_alt */
void __init_or_module alternatives_smp_module_add(struct module *mod,
char *name,
@@ -368,19 +352,18 @@ void __init_or_module alternatives_smp_module_add(struct module *mod,
{
struct smp_alt_module *smp;
- if (noreplace_smp)
- return;
+ mutex_lock(&smp_alt);
+ if (!uniproc_patched)
+ goto unlock;
- if (smp_alt_once) {
- if (boot_cpu_has(X86_FEATURE_UP))
- alternatives_smp_unlock(locks, locks_end,
- text, text_end);
- return;
- }
+ if (num_possible_cpus() == 1)
+ /* Don't bother remembering, we'll never have to undo it. */
+ goto smp_unlock;
smp = kzalloc(sizeof(*smp), GFP_KERNEL);
if (NULL == smp)
- return; /* we'll run the (safe but slow) SMP code then ... */
+ /* we'll run the (safe but slow) SMP code then ... */
+ goto unlock;
smp->mod = mod;
smp->name = name;
@@ -392,11 +375,10 @@ void __init_or_module alternatives_smp_module_add(struct module *mod,
__func__, smp->locks, smp->locks_end,
smp->text, smp->text_end, smp->name);
- mutex_lock(&smp_alt);
list_add_tail(&smp->next, &smp_alt_modules);
- if (boot_cpu_has(X86_FEATURE_UP))
- alternatives_smp_unlock(smp->locks, smp->locks_end,
- smp->text, smp->text_end);
+smp_unlock:
+ alternatives_smp_unlock(locks, locks_end, text, text_end);
+unlock:
mutex_unlock(&smp_alt);
}
@@ -404,24 +386,18 @@ void __init_or_module alternatives_smp_module_del(struct module *mod)
{
struct smp_alt_module *item;
- if (smp_alt_once || noreplace_smp)
- return;
-
mutex_lock(&smp_alt);
list_for_each_entry(item, &smp_alt_modules, next) {
if (mod != item->mod)
continue;
list_del(&item->next);
- mutex_unlock(&smp_alt);
- DPRINTK("%s: %s\n", __func__, item->name);
kfree(item);
- return;
+ break;
}
mutex_unlock(&smp_alt);
}
-bool skip_smp_alternatives;
-void alternatives_smp_switch(int smp)
+void alternatives_enable_smp(void)
{
struct smp_alt_module *mod;
@@ -436,34 +412,21 @@ void alternatives_smp_switch(int smp)
pr_info("lockdep: fixing up alternatives\n");
#endif
- if (noreplace_smp || smp_alt_once || skip_smp_alternatives)
- return;
- BUG_ON(!smp && (num_online_cpus() > 1));
+ /* Why bother if there are no other CPUs? */
+ BUG_ON(num_possible_cpus() == 1);
mutex_lock(&smp_alt);
- /*
- * Avoid unnecessary switches because it forces JIT based VMs to
- * throw away all cached translations, which can be quite costly.
- */
- if (smp == smp_mode) {
- /* nothing */
- } else if (smp) {
+ if (uniproc_patched) {
pr_info("switching to SMP code\n");
+ BUG_ON(num_online_cpus() != 1);
clear_cpu_cap(&boot_cpu_data, X86_FEATURE_UP);
clear_cpu_cap(&cpu_data(0), X86_FEATURE_UP);
list_for_each_entry(mod, &smp_alt_modules, next)
alternatives_smp_lock(mod->locks, mod->locks_end,
mod->text, mod->text_end);
- } else {
- pr_info("switching to UP code\n");
- set_cpu_cap(&boot_cpu_data, X86_FEATURE_UP);
- set_cpu_cap(&cpu_data(0), X86_FEATURE_UP);
- list_for_each_entry(mod, &smp_alt_modules, next)
- alternatives_smp_unlock(mod->locks, mod->locks_end,
- mod->text, mod->text_end);
+ uniproc_patched = false;
}
- smp_mode = smp;
mutex_unlock(&smp_alt);
}
@@ -540,40 +503,22 @@ void __init alternative_instructions(void)
apply_alternatives(__alt_instructions, __alt_instructions_end);
- /* switch to patch-once-at-boottime-only mode and free the
- * tables in case we know the number of CPUs will never ever
- * change */
-#ifdef CONFIG_HOTPLUG_CPU
- if (num_possible_cpus() < 2)
- smp_alt_once = 1;
-#endif
-
#ifdef CONFIG_SMP
- if (smp_alt_once) {
- if (1 == num_possible_cpus()) {
- pr_info("switching to UP code\n");
- set_cpu_cap(&boot_cpu_data, X86_FEATURE_UP);
- set_cpu_cap(&cpu_data(0), X86_FEATURE_UP);
-
- alternatives_smp_unlock(__smp_locks, __smp_locks_end,
- _text, _etext);
- }
- } else {
+ /* Patch to UP if other cpus not imminent. */
+ if (!noreplace_smp && (num_present_cpus() == 1 || setup_max_cpus <= 1)) {
+ uniproc_patched = true;
alternatives_smp_module_add(NULL, "core kernel",
__smp_locks, __smp_locks_end,
_text, _etext);
-
- /* Only switch to UP mode if we don't immediately boot others */
- if (num_present_cpus() == 1 || setup_max_cpus <= 1)
- alternatives_smp_switch(0);
}
-#endif
- apply_paravirt(__parainstructions, __parainstructions_end);
- if (smp_alt_once)
+ if (!uniproc_patched || num_possible_cpus() == 1)
free_init_pages("SMP alternatives",
(unsigned long)__smp_locks,
(unsigned long)__smp_locks_end);
+#endif
+
+ apply_paravirt(__parainstructions, __parainstructions_end);
restart_nmi();
}
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 24deb3082328..b17416e72fbd 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1934,7 +1934,7 @@ void smp_error_interrupt(struct pt_regs *regs)
apic_printk(APIC_DEBUG, KERN_CONT " : %s", error_interrupt_reason[i]);
i++;
v1 >>= 1;
- };
+ }
apic_printk(APIC_DEBUG, KERN_CONT "\n");
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 9d92e19039f0..f7e98a2c0d12 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -737,6 +737,72 @@ static unsigned int __cpuinit amd_size_cache(struct cpuinfo_x86 *c,
}
#endif
+static void __cpuinit cpu_set_tlb_flushall_shift(struct cpuinfo_x86 *c)
+{
+ if (!cpu_has_invlpg)
+ return;
+
+ tlb_flushall_shift = 5;
+
+ if (c->x86 <= 0x11)
+ tlb_flushall_shift = 4;
+}
+
+static void __cpuinit cpu_detect_tlb_amd(struct cpuinfo_x86 *c)
+{
+ u32 ebx, eax, ecx, edx;
+ u16 mask = 0xfff;
+
+ if (c->x86 < 0xf)
+ return;
+
+ if (c->extended_cpuid_level < 0x80000006)
+ return;
+
+ cpuid(0x80000006, &eax, &ebx, &ecx, &edx);
+
+ tlb_lld_4k[ENTRIES] = (ebx >> 16) & mask;
+ tlb_lli_4k[ENTRIES] = ebx & mask;
+
+ /*
+ * K8 doesn't have 2M/4M entries in the L2 TLB so read out the L1 TLB
+ * characteristics from the CPUID function 0x80000005 instead.
+ */
+ if (c->x86 == 0xf) {
+ cpuid(0x80000005, &eax, &ebx, &ecx, &edx);
+ mask = 0xff;
+ }
+
+ /* Handle DTLB 2M and 4M sizes, fall back to L1 if L2 is disabled */
+ if (!((eax >> 16) & mask)) {
+ u32 a, b, c, d;
+
+ cpuid(0x80000005, &a, &b, &c, &d);
+ tlb_lld_2m[ENTRIES] = (a >> 16) & 0xff;
+ } else {
+ tlb_lld_2m[ENTRIES] = (eax >> 16) & mask;
+ }
+
+ /* a 4M entry uses two 2M entries */
+ tlb_lld_4m[ENTRIES] = tlb_lld_2m[ENTRIES] >> 1;
+
+ /* Handle ITLB 2M and 4M sizes, fall back to L1 if L2 is disabled */
+ if (!(eax & mask)) {
+ /* Erratum 658 */
+ if (c->x86 == 0x15 && c->x86_model <= 0x1f) {
+ tlb_lli_2m[ENTRIES] = 1024;
+ } else {
+ cpuid(0x80000005, &eax, &ebx, &ecx, &edx);
+ tlb_lli_2m[ENTRIES] = eax & 0xff;
+ }
+ } else
+ tlb_lli_2m[ENTRIES] = eax & mask;
+
+ tlb_lli_4m[ENTRIES] = tlb_lli_2m[ENTRIES] >> 1;
+
+ cpu_set_tlb_flushall_shift(c);
+}
+
static const struct cpu_dev __cpuinitconst amd_cpu_dev = {
.c_vendor = "AMD",
.c_ident = { "AuthenticAMD" },
@@ -756,6 +822,7 @@ static const struct cpu_dev __cpuinitconst amd_cpu_dev = {
.c_size_cache = amd_size_cache,
#endif
.c_early_init = early_init_amd,
+ .c_detect_tlb = cpu_detect_tlb_amd,
.c_bsp_init = bsp_init_amd,
.c_init = init_amd,
.c_x86_vendor = X86_VENDOR_AMD,
diff --git a/arch/x86/kernel/cpu/bugs.c b/arch/x86/kernel/cpu/bugs.c
index c97bb7b5a9f8..d0e910da16c5 100644
--- a/arch/x86/kernel/cpu/bugs.c
+++ b/arch/x86/kernel/cpu/bugs.c
@@ -165,10 +165,15 @@ void __init check_bugs(void)
print_cpu_info(&boot_cpu_data);
#endif
check_config();
- check_fpu();
check_hlt();
check_popad();
init_utsname()->machine[1] =
'0' + (boot_cpu_data.x86 > 6 ? 6 : boot_cpu_data.x86);
alternative_instructions();
+
+ /*
+ * kernel_fpu_begin/end() in check_fpu() relies on the patched
+ * alternative instructions.
+ */
+ check_fpu();
}
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index a5fbc3c5fccc..7505f7b13e71 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -259,23 +259,36 @@ static inline void squash_the_stupid_serial_number(struct cpuinfo_x86 *c)
}
#endif
-static int disable_smep __cpuinitdata;
static __init int setup_disable_smep(char *arg)
{
- disable_smep = 1;
+ setup_clear_cpu_cap(X86_FEATURE_SMEP);
return 1;
}
__setup("nosmep", setup_disable_smep);
-static __cpuinit void setup_smep(struct cpuinfo_x86 *c)
+static __always_inline void setup_smep(struct cpuinfo_x86 *c)
{
- if (cpu_has(c, X86_FEATURE_SMEP)) {
- if (unlikely(disable_smep)) {
- setup_clear_cpu_cap(X86_FEATURE_SMEP);
- clear_in_cr4(X86_CR4_SMEP);
- } else
- set_in_cr4(X86_CR4_SMEP);
- }
+ if (cpu_has(c, X86_FEATURE_SMEP))
+ set_in_cr4(X86_CR4_SMEP);
+}
+
+static __init int setup_disable_smap(char *arg)
+{
+ setup_clear_cpu_cap(X86_FEATURE_SMAP);
+ return 1;
+}
+__setup("nosmap", setup_disable_smap);
+
+static __always_inline void setup_smap(struct cpuinfo_x86 *c)
+{
+ unsigned long eflags;
+
+ /* This should have been cleared long ago */
+ raw_local_save_flags(eflags);
+ BUG_ON(eflags & X86_EFLAGS_AC);
+
+ if (cpu_has(c, X86_FEATURE_SMAP))
+ set_in_cr4(X86_CR4_SMAP);
}
/*
@@ -476,7 +489,7 @@ void __cpuinit cpu_detect_tlb(struct cpuinfo_x86 *c)
printk(KERN_INFO "Last level iTLB entries: 4KB %d, 2MB %d, 4MB %d\n" \
"Last level dTLB entries: 4KB %d, 2MB %d, 4MB %d\n" \
- "tlb_flushall_shift is 0x%x\n",
+ "tlb_flushall_shift: %d\n",
tlb_lli_4k[ENTRIES], tlb_lli_2m[ENTRIES],
tlb_lli_4m[ENTRIES], tlb_lld_4k[ENTRIES],
tlb_lld_2m[ENTRIES], tlb_lld_4m[ENTRIES],
@@ -712,8 +725,6 @@ static void __init early_identify_cpu(struct cpuinfo_x86 *c)
c->cpu_index = 0;
filter_cpuid_features(c, false);
- setup_smep(c);
-
if (this_cpu->c_bsp_init)
this_cpu->c_bsp_init(c);
}
@@ -798,8 +809,6 @@ static void __cpuinit generic_identify(struct cpuinfo_x86 *c)
c->phys_proc_id = c->initial_apicid;
}
- setup_smep(c);
-
get_model_name(c); /* Default name */
detect_nopl(c);
@@ -864,6 +873,10 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
/* Disable the PN if appropriate */
squash_the_stupid_serial_number(c);
+ /* Set up SMEP/SMAP */
+ setup_smep(c);
+ setup_smap(c);
+
/*
* The vendor-specific functions might have changed features.
* Now we do "generic changes."
@@ -942,8 +955,7 @@ void __init identify_boot_cpu(void)
#else
vgetcpu_set_mode();
#endif
- if (boot_cpu_data.cpuid_level >= 2)
- cpu_detect_tlb(&boot_cpu_data);
+ cpu_detect_tlb(&boot_cpu_data);
}
void __cpuinit identify_secondary_cpu(struct cpuinfo_x86 *c)
@@ -1023,14 +1035,16 @@ void __cpuinit print_cpu_info(struct cpuinfo_x86 *c)
printk(KERN_CONT "%s ", vendor);
if (c->x86_model_id[0])
- printk(KERN_CONT "%s", c->x86_model_id);
+ printk(KERN_CONT "%s", strim(c->x86_model_id));
else
printk(KERN_CONT "%d86", c->x86);
+ printk(KERN_CONT " (fam: %02x, model: %02x", c->x86, c->x86_model);
+
if (c->x86_mask || c->cpuid_level >= 0)
- printk(KERN_CONT " stepping %02x\n", c->x86_mask);
+ printk(KERN_CONT ", stepping: %02x)\n", c->x86_mask);
else
- printk(KERN_CONT "\n");
+ printk(KERN_CONT ")\n");
print_cpu_msr(c);
}
@@ -1113,11 +1127,10 @@ void syscall_init(void)
/* Flags to clear on syscall */
wrmsrl(MSR_SYSCALL_MASK,
- X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|X86_EFLAGS_IOPL);
+ X86_EFLAGS_TF|X86_EFLAGS_DF|X86_EFLAGS_IF|
+ X86_EFLAGS_IOPL|X86_EFLAGS_AC);
}
-unsigned long kernel_eflags;
-
/*
* Copies of the original ist values from the tss are only accessed during
* debugging, no special alignment required.
@@ -1297,9 +1310,6 @@ void __cpuinit cpu_init(void)
dbg_restore_debug_regs();
fpu_init();
- xsave_init();
-
- raw_local_save_flags(kernel_eflags);
if (is_uv_system())
uv_cpu_init();
@@ -1352,6 +1362,5 @@ void __cpuinit cpu_init(void)
dbg_restore_debug_regs();
fpu_init();
- xsave_init();
}
#endif
diff --git a/arch/x86/kernel/cpu/intel.c b/arch/x86/kernel/cpu/intel.c
index 0a4ce2980a5a..198e019a531a 100644
--- a/arch/x86/kernel/cpu/intel.c
+++ b/arch/x86/kernel/cpu/intel.c
@@ -648,6 +648,10 @@ static void __cpuinit intel_detect_tlb(struct cpuinfo_x86 *c)
int i, j, n;
unsigned int regs[4];
unsigned char *desc = (unsigned char *)regs;
+
+ if (c->cpuid_level < 2)
+ return;
+
/* Number of times to iterate */
n = cpuid_eax(2) & 0xFF;
diff --git a/arch/x86/kernel/cpu/mcheck/mce-inject.c b/arch/x86/kernel/cpu/mcheck/mce-inject.c
index fc4beb393577..ddc72f839332 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-inject.c
+++ b/arch/x86/kernel/cpu/mcheck/mce-inject.c
@@ -78,6 +78,7 @@ static void raise_exception(struct mce *m, struct pt_regs *pregs)
}
static cpumask_var_t mce_inject_cpumask;
+static DEFINE_MUTEX(mce_inject_mutex);
static int mce_raise_notify(unsigned int cmd, struct pt_regs *regs)
{
@@ -194,7 +195,11 @@ static void raise_mce(struct mce *m)
put_online_cpus();
} else
#endif
+ {
+ preempt_disable();
raise_local();
+ preempt_enable();
+ }
}
/* Error injection interface */
@@ -225,7 +230,10 @@ static ssize_t mce_write(struct file *filp, const char __user *ubuf,
* so do it a jiffie or two later everywhere.
*/
schedule_timeout(2);
+
+ mutex_lock(&mce_inject_mutex);
raise_mce(&m);
+ mutex_unlock(&mce_inject_mutex);
return usize;
}
diff --git a/arch/x86/kernel/cpu/mcheck/mce-internal.h b/arch/x86/kernel/cpu/mcheck/mce-internal.h
index ed44c8a65858..6a05c1d327a9 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-internal.h
+++ b/arch/x86/kernel/cpu/mcheck/mce-internal.h
@@ -28,6 +28,18 @@ extern int mce_ser;
extern struct mce_bank *mce_banks;
+#ifdef CONFIG_X86_MCE_INTEL
+unsigned long mce_intel_adjust_timer(unsigned long interval);
+void mce_intel_cmci_poll(void);
+void mce_intel_hcpu_update(unsigned long cpu);
+#else
+# define mce_intel_adjust_timer mce_adjust_timer_default
+static inline void mce_intel_cmci_poll(void) { }
+static inline void mce_intel_hcpu_update(unsigned long cpu) { }
+#endif
+
+void mce_timer_kick(unsigned long interval);
+
#ifdef CONFIG_ACPI_APEI
int apei_write_mce(struct mce *m);
ssize_t apei_read_mce(struct mce *m, u64 *record_id);
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 292d0258311c..29e87d3b2843 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -83,6 +83,7 @@ static int mce_dont_log_ce __read_mostly;
int mce_cmci_disabled __read_mostly;
int mce_ignore_ce __read_mostly;
int mce_ser __read_mostly;
+int mce_bios_cmci_threshold __read_mostly;
struct mce_bank *mce_banks __read_mostly;
@@ -1266,6 +1267,14 @@ static unsigned long check_interval = 5 * 60; /* 5 minutes */
static DEFINE_PER_CPU(unsigned long, mce_next_interval); /* in jiffies */
static DEFINE_PER_CPU(struct timer_list, mce_timer);
+static unsigned long mce_adjust_timer_default(unsigned long interval)
+{
+ return interval;
+}
+
+static unsigned long (*mce_adjust_timer)(unsigned long interval) =
+ mce_adjust_timer_default;
+
static void mce_timer_fn(unsigned long data)
{
struct timer_list *t = &__get_cpu_var(mce_timer);
@@ -1276,6 +1285,7 @@ static void mce_timer_fn(unsigned long data)
if (mce_available(__this_cpu_ptr(&cpu_info))) {
machine_check_poll(MCP_TIMESTAMP,
&__get_cpu_var(mce_poll_banks));
+ mce_intel_cmci_poll();
}
/*
@@ -1283,14 +1293,38 @@ static void mce_timer_fn(unsigned long data)
* polling interval, otherwise increase the polling interval.
*/
iv = __this_cpu_read(mce_next_interval);
- if (mce_notify_irq())
+ if (mce_notify_irq()) {
iv = max(iv / 2, (unsigned long) HZ/100);
- else
+ } else {
iv = min(iv * 2, round_jiffies_relative(check_interval * HZ));
+ iv = mce_adjust_timer(iv);
+ }
__this_cpu_write(mce_next_interval, iv);
+ /* Might have become 0 after CMCI storm subsided */
+ if (iv) {
+ t->expires = jiffies + iv;
+ add_timer_on(t, smp_processor_id());
+ }
+}
- t->expires = jiffies + iv;
- add_timer_on(t, smp_processor_id());
+/*
+ * Ensure that the timer is firing in @interval from now.
+ */
+void mce_timer_kick(unsigned long interval)
+{
+ struct timer_list *t = &__get_cpu_var(mce_timer);
+ unsigned long when = jiffies + interval;
+ unsigned long iv = __this_cpu_read(mce_next_interval);
+
+ if (timer_pending(t)) {
+ if (time_before(when, t->expires))
+ mod_timer_pinned(t, when);
+ } else {
+ t->expires = round_jiffies(when);
+ add_timer_on(t, smp_processor_id());
+ }
+ if (interval < iv)
+ __this_cpu_write(mce_next_interval, interval);
}
/* Must not be called in IRQ context where del_timer_sync() can deadlock */
@@ -1585,6 +1619,7 @@ static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c)
switch (c->x86_vendor) {
case X86_VENDOR_INTEL:
mce_intel_feature_init(c);
+ mce_adjust_timer = mce_intel_adjust_timer;
break;
case X86_VENDOR_AMD:
mce_amd_feature_init(c);
@@ -1594,23 +1629,28 @@ static void __mcheck_cpu_init_vendor(struct cpuinfo_x86 *c)
}
}
-static void __mcheck_cpu_init_timer(void)
+static void mce_start_timer(unsigned int cpu, struct timer_list *t)
{
- struct timer_list *t = &__get_cpu_var(mce_timer);
- unsigned long iv = check_interval * HZ;
+ unsigned long iv = mce_adjust_timer(check_interval * HZ);
- setup_timer(t, mce_timer_fn, smp_processor_id());
+ __this_cpu_write(mce_next_interval, iv);
- if (mce_ignore_ce)
+ if (mce_ignore_ce || !iv)
return;
- __this_cpu_write(mce_next_interval, iv);
- if (!iv)
- return;
t->expires = round_jiffies(jiffies + iv);
add_timer_on(t, smp_processor_id());
}
+static void __mcheck_cpu_init_timer(void)
+{
+ struct timer_list *t = &__get_cpu_var(mce_timer);
+ unsigned int cpu = smp_processor_id();
+
+ setup_timer(t, mce_timer_fn, cpu);
+ mce_start_timer(cpu, t);
+}
+
/* Handle unconfigured int18 (should never happen) */
static void unexpected_machine_check(struct pt_regs *regs, long error_code)
{
@@ -1907,6 +1947,7 @@ static struct miscdevice mce_chrdev_device = {
* check, or 0 to not wait
* mce=bootlog Log MCEs from before booting. Disabled by default on AMD.
* mce=nobootlog Don't log MCEs from before booting.
+ * mce=bios_cmci_threshold Don't program the CMCI threshold
*/
static int __init mcheck_enable(char *str)
{
@@ -1926,6 +1967,8 @@ static int __init mcheck_enable(char *str)
mce_ignore_ce = 1;
else if (!strcmp(str, "bootlog") || !strcmp(str, "nobootlog"))
mce_bootlog = (str[0] == 'b');
+ else if (!strcmp(str, "bios_cmci_threshold"))
+ mce_bios_cmci_threshold = 1;
else if (isdigit(str[0])) {
get_option(&str, &tolerant);
if (*str == ',') {
@@ -2166,6 +2209,11 @@ static struct dev_ext_attribute dev_attr_cmci_disabled = {
&mce_cmci_disabled
};
+static struct dev_ext_attribute dev_attr_bios_cmci_threshold = {
+ __ATTR(bios_cmci_threshold, 0444, device_show_int, NULL),
+ &mce_bios_cmci_threshold
+};
+
static struct device_attribute *mce_device_attrs[] = {
&dev_attr_tolerant.attr,
&dev_attr_check_interval.attr,
@@ -2174,6 +2222,7 @@ static struct device_attribute *mce_device_attrs[] = {
&dev_attr_dont_log_ce.attr,
&dev_attr_ignore_ce.attr,
&dev_attr_cmci_disabled.attr,
+ &dev_attr_bios_cmci_threshold.attr,
NULL
};
@@ -2294,38 +2343,33 @@ mce_cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
unsigned int cpu = (unsigned long)hcpu;
struct timer_list *t = &per_cpu(mce_timer, cpu);
- switch (action) {
+ switch (action & ~CPU_TASKS_FROZEN) {
case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
mce_device_create(cpu);
if (threshold_cpu_callback)
threshold_cpu_callback(action, cpu);
break;
case CPU_DEAD:
- case CPU_DEAD_FROZEN:
if (threshold_cpu_callback)
threshold_cpu_callback(action, cpu);
mce_device_remove(cpu);
+ mce_intel_hcpu_update(cpu);
break;
case CPU_DOWN_PREPARE:
- case CPU_DOWN_PREPARE_FROZEN:
- del_timer_sync(t);
smp_call_function_single(cpu, mce_disable_cpu, &action, 1);
+ del_timer_sync(t);
break;
case CPU_DOWN_FAILED:
- case CPU_DOWN_FAILED_FROZEN:
- if (!mce_ignore_ce && check_interval) {
- t->expires = round_jiffies(jiffies +
- per_cpu(mce_next_interval, cpu));
- add_timer_on(t, cpu);
- }
smp_call_function_single(cpu, mce_reenable_cpu, &action, 1);
+ mce_start_timer(cpu, t);
break;
- case CPU_POST_DEAD:
+ }
+
+ if (action == CPU_POST_DEAD) {
/* intentionally ignoring frozen here */
cmci_rediscover(cpu);
- break;
}
+
return NOTIFY_OK;
}
diff --git a/arch/x86/kernel/cpu/mcheck/mce_intel.c b/arch/x86/kernel/cpu/mcheck/mce_intel.c
index 38e49bc95ffc..5f88abf07e9c 100644
--- a/arch/x86/kernel/cpu/mcheck/mce_intel.c
+++ b/arch/x86/kernel/cpu/mcheck/mce_intel.c
@@ -15,6 +15,8 @@
#include <asm/msr.h>
#include <asm/mce.h>
+#include "mce-internal.h"
+
/*
* Support for Intel Correct Machine Check Interrupts. This allows
* the CPU to raise an interrupt when a corrected machine check happened.
@@ -30,7 +32,22 @@ static DEFINE_PER_CPU(mce_banks_t, mce_banks_owned);
*/
static DEFINE_RAW_SPINLOCK(cmci_discover_lock);
-#define CMCI_THRESHOLD 1
+#define CMCI_THRESHOLD 1
+#define CMCI_POLL_INTERVAL (30 * HZ)
+#define CMCI_STORM_INTERVAL (1 * HZ)
+#define CMCI_STORM_THRESHOLD 15
+
+static DEFINE_PER_CPU(unsigned long, cmci_time_stamp);
+static DEFINE_PER_CPU(unsigned int, cmci_storm_cnt);
+static DEFINE_PER_CPU(unsigned int, cmci_storm_state);
+
+enum {
+ CMCI_STORM_NONE,
+ CMCI_STORM_ACTIVE,
+ CMCI_STORM_SUBSIDED,
+};
+
+static atomic_t cmci_storm_on_cpus;
static int cmci_supported(int *banks)
{
@@ -53,6 +70,93 @@ static int cmci_supported(int *banks)
return !!(cap & MCG_CMCI_P);
}
+void mce_intel_cmci_poll(void)
+{
+ if (__this_cpu_read(cmci_storm_state) == CMCI_STORM_NONE)
+ return;
+ machine_check_poll(MCP_TIMESTAMP, &__get_cpu_var(mce_banks_owned));
+}
+
+void mce_intel_hcpu_update(unsigned long cpu)
+{
+ if (per_cpu(cmci_storm_state, cpu) == CMCI_STORM_ACTIVE)
+ atomic_dec(&cmci_storm_on_cpus);
+
+ per_cpu(cmci_storm_state, cpu) = CMCI_STORM_NONE;
+}
+
+unsigned long mce_intel_adjust_timer(unsigned long interval)
+{
+ int r;
+
+ if (interval < CMCI_POLL_INTERVAL)
+ return interval;
+
+ switch (__this_cpu_read(cmci_storm_state)) {
+ case CMCI_STORM_ACTIVE:
+ /*
+ * We switch back to interrupt mode once the poll timer has
+ * silenced itself. That means no events recorded and the
+ * timer interval is back to our poll interval.
+ */
+ __this_cpu_write(cmci_storm_state, CMCI_STORM_SUBSIDED);
+ r = atomic_sub_return(1, &cmci_storm_on_cpus);
+ if (r == 0)
+ pr_notice("CMCI storm subsided: switching to interrupt mode\n");
+ /* FALLTHROUGH */
+
+ case CMCI_STORM_SUBSIDED:
+ /*
+ * We wait for all cpus to go back to SUBSIDED
+ * state. When that happens we switch back to
+ * interrupt mode.
+ */
+ if (!atomic_read(&cmci_storm_on_cpus)) {
+ __this_cpu_write(cmci_storm_state, CMCI_STORM_NONE);
+ cmci_reenable();
+ cmci_recheck();
+ }
+ return CMCI_POLL_INTERVAL;
+ default:
+ /*
+ * We have shiny weather. Let the poll do whatever it
+ * thinks.
+ */
+ return interval;
+ }
+}
+
+static bool cmci_storm_detect(void)
+{
+ unsigned int cnt = __this_cpu_read(cmci_storm_cnt);
+ unsigned long ts = __this_cpu_read(cmci_time_stamp);
+ unsigned long now = jiffies;
+ int r;
+
+ if (__this_cpu_read(cmci_storm_state) != CMCI_STORM_NONE)
+ return true;
+
+ if (time_before_eq(now, ts + CMCI_STORM_INTERVAL)) {
+ cnt++;
+ } else {
+ cnt = 1;
+ __this_cpu_write(cmci_time_stamp, now);
+ }
+ __this_cpu_write(cmci_storm_cnt, cnt);
+
+ if (cnt <= CMCI_STORM_THRESHOLD)
+ return false;
+
+ cmci_clear();
+ __this_cpu_write(cmci_storm_state, CMCI_STORM_ACTIVE);
+ r = atomic_add_return(1, &cmci_storm_on_cpus);
+ mce_timer_kick(CMCI_POLL_INTERVAL);
+
+ if (r == 1)
+ pr_notice("CMCI storm detected: switching to poll mode\n");
+ return true;
+}
+
/*
* The interrupt handler. This is called on every event.
* Just call the poller directly to log any events.
@@ -61,33 +165,28 @@ static int cmci_supported(int *banks)
*/
static void intel_threshold_interrupt(void)
{
+ if (cmci_storm_detect())
+ return;
machine_check_poll(MCP_TIMESTAMP, &__get_cpu_var(mce_banks_owned));
mce_notify_irq();
}
-static void print_update(char *type, int *hdr, int num)
-{
- if (*hdr == 0)
- printk(KERN_INFO "CPU %d MCA banks", smp_processor_id());
- *hdr = 1;
- printk(KERN_CONT " %s:%d", type, num);
-}
-
/*
* Enable CMCI (Corrected Machine Check Interrupt) for available MCE banks
* on this CPU. Use the algorithm recommended in the SDM to discover shared
* banks.
*/
-static void cmci_discover(int banks, int boot)
+static void cmci_discover(int banks)
{
unsigned long *owned = (void *)&__get_cpu_var(mce_banks_owned);
unsigned long flags;
- int hdr = 0;
int i;
+ int bios_wrong_thresh = 0;
raw_spin_lock_irqsave(&cmci_discover_lock, flags);
for (i = 0; i < banks; i++) {
u64 val;
+ int bios_zero_thresh = 0;
if (test_bit(i, owned))
continue;
@@ -96,29 +195,52 @@ static void cmci_discover(int banks, int boot)
/* Already owned by someone else? */
if (val & MCI_CTL2_CMCI_EN) {
- if (test_and_clear_bit(i, owned) && !boot)
- print_update("SHD", &hdr, i);
+ clear_bit(i, owned);
__clear_bit(i, __get_cpu_var(mce_poll_banks));
continue;
}
- val &= ~MCI_CTL2_CMCI_THRESHOLD_MASK;
- val |= MCI_CTL2_CMCI_EN | CMCI_THRESHOLD;
+ if (!mce_bios_cmci_threshold) {
+ val &= ~MCI_CTL2_CMCI_THRESHOLD_MASK;
+ val |= CMCI_THRESHOLD;
+ } else if (!(val & MCI_CTL2_CMCI_THRESHOLD_MASK)) {
+ /*
+ * If bios_cmci_threshold boot option was specified
+ * but the threshold is zero, we'll try to initialize
+ * it to 1.
+ */
+ bios_zero_thresh = 1;
+ val |= CMCI_THRESHOLD;
+ }
+
+ val |= MCI_CTL2_CMCI_EN;
wrmsrl(MSR_IA32_MCx_CTL2(i), val);
rdmsrl(MSR_IA32_MCx_CTL2(i), val);
/* Did the enable bit stick? -- the bank supports CMCI */
if (val & MCI_CTL2_CMCI_EN) {
- if (!test_and_set_bit(i, owned) && !boot)
- print_update("CMCI", &hdr, i);
+ set_bit(i, owned);
__clear_bit(i, __get_cpu_var(mce_poll_banks));
+ /*
+ * We are able to set thresholds for some banks that
+ * had a threshold of 0. This means the BIOS has not
+ * set the thresholds properly or does not work with
+ * this boot option. Note down now and report later.
+ */
+ if (mce_bios_cmci_threshold && bios_zero_thresh &&
+ (val & MCI_CTL2_CMCI_THRESHOLD_MASK))
+ bios_wrong_thresh = 1;
} else {
WARN_ON(!test_bit(i, __get_cpu_var(mce_poll_banks)));
}
}
raw_spin_unlock_irqrestore(&cmci_discover_lock, flags);
- if (hdr)
- printk(KERN_CONT "\n");
+ if (mce_bios_cmci_threshold && bios_wrong_thresh) {
+ pr_info_once(
+ "bios_cmci_threshold: Some banks do not have valid thresholds set\n");
+ pr_info_once(
+ "bios_cmci_threshold: Make sure your BIOS supports this boot option\n");
+ }
}
/*
@@ -156,7 +278,7 @@ void cmci_clear(void)
continue;
/* Disable CMCI */
rdmsrl(MSR_IA32_MCx_CTL2(i), val);
- val &= ~(MCI_CTL2_CMCI_EN|MCI_CTL2_CMCI_THRESHOLD_MASK);
+ val &= ~MCI_CTL2_CMCI_EN;
wrmsrl(MSR_IA32_MCx_CTL2(i), val);
__clear_bit(i, __get_cpu_var(mce_banks_owned));
}
@@ -186,7 +308,7 @@ void cmci_rediscover(int dying)
continue;
/* Recheck banks in case CPUs don't all have the same */
if (cmci_supported(&banks))
- cmci_discover(banks, 0);
+ cmci_discover(banks);
}
set_cpus_allowed_ptr(current, old);
@@ -200,7 +322,7 @@ void cmci_reenable(void)
{
int banks;
if (cmci_supported(&banks))
- cmci_discover(banks, 0);
+ cmci_discover(banks);
}
static void intel_init_cmci(void)
@@ -211,7 +333,7 @@ static void intel_init_cmci(void)
return;
mce_threshold_vector = intel_threshold_interrupt;
- cmci_discover(banks, 1);
+ cmci_discover(banks);
/*
* For CPU #0 this runs with still disabled APIC, but that's
* ok because only the vector is set up. We still do another
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h
index 6605a81ba339..8b6defe7eefc 100644
--- a/arch/x86/kernel/cpu/perf_event.h
+++ b/arch/x86/kernel/cpu/perf_event.h
@@ -586,6 +586,8 @@ extern struct event_constraint intel_westmere_pebs_event_constraints[];
extern struct event_constraint intel_snb_pebs_event_constraints[];
+extern struct event_constraint intel_ivb_pebs_event_constraints[];
+
struct event_constraint *intel_pebs_constraints(struct perf_event *event);
void intel_pmu_pebs_enable(struct perf_event *event);
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
index 7bfb5bec8630..eebd5ffe1bba 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
@@ -209,6 +209,15 @@ static int perf_ibs_precise_event(struct perf_event *event, u64 *config)
return -EOPNOTSUPP;
}
+static const struct perf_event_attr ibs_notsupp = {
+ .exclude_user = 1,
+ .exclude_kernel = 1,
+ .exclude_hv = 1,
+ .exclude_idle = 1,
+ .exclude_host = 1,
+ .exclude_guest = 1,
+};
+
static int perf_ibs_init(struct perf_event *event)
{
struct hw_perf_event *hwc = &event->hw;
@@ -229,6 +238,9 @@ static int perf_ibs_init(struct perf_event *event)
if (event->pmu != &perf_ibs->pmu)
return -ENOENT;
+ if (perf_flags(&event->attr) & perf_flags(&ibs_notsupp))
+ return -EINVAL;
+
if (config & ~perf_ibs->config_mask)
return -EINVAL;
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
index 0d3d63afa76a..6bca492b8547 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -2048,7 +2048,6 @@ __init int intel_pmu_init(void)
case 42: /* SandyBridge */
case 45: /* SandyBridge, "Romely-EP" */
x86_add_quirk(intel_sandybridge_quirk);
- case 58: /* IvyBridge */
memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
sizeof(hw_cache_event_ids));
memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs,
@@ -2073,6 +2072,29 @@ __init int intel_pmu_init(void)
pr_cont("SandyBridge events, ");
break;
+ case 58: /* IvyBridge */
+ memcpy(hw_cache_event_ids, snb_hw_cache_event_ids,
+ sizeof(hw_cache_event_ids));
+ memcpy(hw_cache_extra_regs, snb_hw_cache_extra_regs,
+ sizeof(hw_cache_extra_regs));
+
+ intel_pmu_lbr_init_snb();
+
+ x86_pmu.event_constraints = intel_snb_event_constraints;
+ x86_pmu.pebs_constraints = intel_ivb_pebs_event_constraints;
+ x86_pmu.pebs_aliases = intel_pebs_aliases_snb;
+ x86_pmu.extra_regs = intel_snb_extra_regs;
+ /* all extra regs are per-cpu when HT is on */
+ x86_pmu.er_flags |= ERF_HAS_RSP_1;
+ x86_pmu.er_flags |= ERF_NO_HT_SHARING;
+
+ /* UOPS_ISSUED.ANY,c=1,i=1 to count stall cycles */
+ intel_perfmon_event_map[PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] =
+ X86_CONFIG(.event=0x0e, .umask=0x01, .inv=1, .cmask=1);
+
+ pr_cont("IvyBridge events, ");
+ break;
+
default:
switch (x86_pmu.version) {
diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c
index e38d97bf4259..826054a4f2ee 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_ds.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c
@@ -407,6 +407,20 @@ struct event_constraint intel_snb_pebs_event_constraints[] = {
EVENT_CONSTRAINT_END
};
+struct event_constraint intel_ivb_pebs_event_constraints[] = {
+ INTEL_UEVENT_CONSTRAINT(0x01c0, 0x2), /* INST_RETIRED.PRECDIST */
+ INTEL_UEVENT_CONSTRAINT(0x01c2, 0xf), /* UOPS_RETIRED.ALL */
+ INTEL_UEVENT_CONSTRAINT(0x02c2, 0xf), /* UOPS_RETIRED.RETIRE_SLOTS */
+ INTEL_EVENT_CONSTRAINT(0xc4, 0xf), /* BR_INST_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xc5, 0xf), /* BR_MISP_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xcd, 0x8), /* MEM_TRANS_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xd0, 0xf), /* MEM_UOP_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xd1, 0xf), /* MEM_LOAD_UOPS_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xd2, 0xf), /* MEM_LOAD_UOPS_LLC_HIT_RETIRED.* */
+ INTEL_EVENT_CONSTRAINT(0xd3, 0xf), /* MEM_LOAD_UOPS_LLC_MISS_RETIRED.* */
+ EVENT_CONSTRAINT_END
+};
+
struct event_constraint *intel_pebs_constraints(struct perf_event *event)
{
struct event_constraint *c;
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
index 0a5571080e74..99d96a4978b5 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
@@ -661,6 +661,11 @@ static void snb_uncore_msr_init_box(struct intel_uncore_box *box)
}
}
+static struct uncore_event_desc snb_uncore_events[] = {
+ INTEL_UNCORE_EVENT_DESC(clockticks, "event=0xff,umask=0x00"),
+ { /* end: all zeroes */ },
+};
+
static struct attribute *snb_uncore_formats_attr[] = {
&format_attr_event.attr,
&format_attr_umask.attr,
@@ -704,6 +709,7 @@ static struct intel_uncore_type snb_uncore_cbox = {
.constraints = snb_uncore_cbox_constraints,
.ops = &snb_uncore_msr_ops,
.format_group = &snb_uncore_format_group,
+ .event_descs = snb_uncore_events,
};
static struct intel_uncore_type *snb_msr_uncores[] = {
@@ -1944,7 +1950,7 @@ struct intel_uncore_box *uncore_alloc_box(struct intel_uncore_type *type, int cp
static struct intel_uncore_box *
uncore_pmu_to_box(struct intel_uncore_pmu *pmu, int cpu)
{
- static struct intel_uncore_box *box;
+ struct intel_uncore_box *box;
box = *per_cpu_ptr(pmu->box, cpu);
if (box)
@@ -2341,6 +2347,27 @@ int uncore_pmu_event_init(struct perf_event *event)
return ret;
}
+static ssize_t uncore_get_attr_cpumask(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int n = cpulist_scnprintf(buf, PAGE_SIZE - 2, &uncore_cpu_mask);
+
+ buf[n++] = '\n';
+ buf[n] = '\0';
+ return n;
+}
+
+static DEVICE_ATTR(cpumask, S_IRUGO, uncore_get_attr_cpumask, NULL);
+
+static struct attribute *uncore_pmu_attrs[] = {
+ &dev_attr_cpumask.attr,
+ NULL,
+};
+
+static struct attribute_group uncore_pmu_attr_group = {
+ .attrs = uncore_pmu_attrs,
+};
+
static int __init uncore_pmu_register(struct intel_uncore_pmu *pmu)
{
int ret;
@@ -2378,8 +2405,8 @@ static void __init uncore_type_exit(struct intel_uncore_type *type)
free_percpu(type->pmus[i].box);
kfree(type->pmus);
type->pmus = NULL;
- kfree(type->attr_groups[1]);
- type->attr_groups[1] = NULL;
+ kfree(type->events_group);
+ type->events_group = NULL;
}
static void __init uncore_types_exit(struct intel_uncore_type **types)
@@ -2431,9 +2458,10 @@ static int __init uncore_type_init(struct intel_uncore_type *type)
for (j = 0; j < i; j++)
attrs[j] = &type->event_descs[j].attr.attr;
- type->attr_groups[1] = events_group;
+ type->events_group = events_group;
}
+ type->pmu_group = &uncore_pmu_attr_group;
type->pmus = pmus;
return 0;
fail:
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.h b/arch/x86/kernel/cpu/perf_event_intel_uncore.h
index 5b81c1856aac..e68a4550e952 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore.h
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.h
@@ -369,10 +369,12 @@ struct intel_uncore_type {
struct intel_uncore_pmu *pmus;
struct intel_uncore_ops *ops;
struct uncore_event_desc *event_descs;
- const struct attribute_group *attr_groups[3];
+ const struct attribute_group *attr_groups[4];
};
-#define format_group attr_groups[0]
+#define pmu_group attr_groups[0]
+#define format_group attr_groups[1]
+#define events_group attr_groups[2]
struct intel_uncore_ops {
void (*init_box)(struct intel_uncore_box *);
diff --git a/arch/x86/kernel/cpu/proc.c b/arch/x86/kernel/cpu/proc.c
index 8022c6681485..fbd895562292 100644
--- a/arch/x86/kernel/cpu/proc.c
+++ b/arch/x86/kernel/cpu/proc.c
@@ -140,10 +140,7 @@ static int show_cpuinfo(struct seq_file *m, void *v)
static void *c_start(struct seq_file *m, loff_t *pos)
{
- if (*pos == 0) /* just in case, cpu 0 is not the first */
- *pos = cpumask_first(cpu_online_mask);
- else
- *pos = cpumask_next(*pos - 1, cpu_online_mask);
+ *pos = cpumask_next(*pos - 1, cpu_online_mask);
if ((*pos) < nr_cpu_ids)
return &cpu_data(*pos);
return NULL;
diff --git a/arch/x86/kernel/cpuid.c b/arch/x86/kernel/cpuid.c
index 39472dd2323f..60c78917190c 100644
--- a/arch/x86/kernel/cpuid.c
+++ b/arch/x86/kernel/cpuid.c
@@ -199,12 +199,14 @@ static int __init cpuid_init(void)
goto out_chrdev;
}
cpuid_class->devnode = cpuid_devnode;
+ get_online_cpus();
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();
err = 0;
goto out;
@@ -214,6 +216,7 @@ out_class:
for_each_online_cpu(i) {
cpuid_device_destroy(i);
}
+ put_online_cpus();
class_destroy(cpuid_class);
out_chrdev:
__unregister_chrdev(CPUID_MAJOR, 0, NR_CPUS, "cpu/cpuid");
@@ -225,11 +228,13 @@ static void __exit cpuid_exit(void)
{
int cpu = 0;
+ get_online_cpus();
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();
}
module_init(cpuid_init);
diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c
index 3ae2ced4a874..b1581527a236 100644
--- a/arch/x86/kernel/devicetree.c
+++ b/arch/x86/kernel/devicetree.c
@@ -342,6 +342,47 @@ const struct irq_domain_ops ioapic_irq_domain_ops = {
.xlate = ioapic_xlate,
};
+static void dt_add_ioapic_domain(unsigned int ioapic_num,
+ struct device_node *np)
+{
+ struct irq_domain *id;
+ struct mp_ioapic_gsi *gsi_cfg;
+ int ret;
+ int num;
+
+ gsi_cfg = mp_ioapic_gsi_routing(ioapic_num);
+ num = gsi_cfg->gsi_end - gsi_cfg->gsi_base + 1;
+
+ id = irq_domain_add_linear(np, num, &ioapic_irq_domain_ops,
+ (void *)ioapic_num);
+ BUG_ON(!id);
+ if (gsi_cfg->gsi_base == 0) {
+ /*
+ * The first NR_IRQS_LEGACY irq descs are allocated in
+ * early_irq_init() and need just a mapping. The
+ * remaining irqs need both. All of them are preallocated
+ * and assigned so we can keep the 1:1 mapping which the ioapic
+ * is having.
+ */
+ ret = irq_domain_associate_many(id, 0, 0, NR_IRQS_LEGACY);
+ if (ret)
+ pr_err("Error mapping legacy IRQs: %d\n", ret);
+
+ if (num > NR_IRQS_LEGACY) {
+ ret = irq_create_strict_mappings(id, NR_IRQS_LEGACY,
+ NR_IRQS_LEGACY, num - NR_IRQS_LEGACY);
+ if (ret)
+ pr_err("Error creating mapping for the "
+ "remaining IRQs: %d\n", ret);
+ }
+ irq_set_default_host(id);
+ } else {
+ ret = irq_create_strict_mappings(id, gsi_cfg->gsi_base, 0, num);
+ if (ret)
+ pr_err("Error creating IRQ mapping: %d\n", ret);
+ }
+}
+
static void __init ioapic_add_ofnode(struct device_node *np)
{
struct resource r;
@@ -356,15 +397,7 @@ static void __init ioapic_add_ofnode(struct device_node *np)
for (i = 0; i < nr_ioapics; i++) {
if (r.start == mpc_ioapic_addr(i)) {
- struct irq_domain *id;
- struct mp_ioapic_gsi *gsi_cfg;
-
- gsi_cfg = mp_ioapic_gsi_routing(i);
-
- id = irq_domain_add_legacy(np, 32, gsi_cfg->gsi_base, 0,
- &ioapic_irq_domain_ops,
- (void*)i);
- BUG_ON(!id);
+ dt_add_ioapic_domain(i, np);
return;
}
}
diff --git a/arch/x86/kernel/entry_32.S b/arch/x86/kernel/entry_32.S
index 623f28837476..0750e3ba87c0 100644
--- a/arch/x86/kernel/entry_32.S
+++ b/arch/x86/kernel/entry_32.S
@@ -57,6 +57,7 @@
#include <asm/cpufeature.h>
#include <asm/alternative-asm.h>
#include <asm/asm.h>
+#include <asm/smap.h>
/* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this. */
#include <linux/elf-em.h>
@@ -407,7 +408,9 @@ sysenter_past_esp:
*/
cmpl $__PAGE_OFFSET-3,%ebp
jae syscall_fault
+ ASM_STAC
1: movl (%ebp),%ebp
+ ASM_CLAC
movl %ebp,PT_EBP(%esp)
_ASM_EXTABLE(1b,syscall_fault)
@@ -488,6 +491,7 @@ ENDPROC(ia32_sysenter_target)
# system call handler stub
ENTRY(system_call)
RING0_INT_FRAME # can't unwind into user space anyway
+ ASM_CLAC
pushl_cfi %eax # save orig_eax
SAVE_ALL
GET_THREAD_INFO(%ebp)
@@ -670,6 +674,7 @@ END(syscall_exit_work)
RING0_INT_FRAME # can't unwind into user space anyway
syscall_fault:
+ ASM_CLAC
GET_THREAD_INFO(%ebp)
movl $-EFAULT,PT_EAX(%esp)
jmp resume_userspace
@@ -825,6 +830,7 @@ END(interrupt)
*/
.p2align CONFIG_X86_L1_CACHE_SHIFT
common_interrupt:
+ ASM_CLAC
addl $-0x80,(%esp) /* Adjust vector into the [-256,-1] range */
SAVE_ALL
TRACE_IRQS_OFF
@@ -841,6 +847,7 @@ ENDPROC(common_interrupt)
#define BUILD_INTERRUPT3(name, nr, fn) \
ENTRY(name) \
RING0_INT_FRAME; \
+ ASM_CLAC; \
pushl_cfi $~(nr); \
SAVE_ALL; \
TRACE_IRQS_OFF \
@@ -857,6 +864,7 @@ ENDPROC(name)
ENTRY(coprocessor_error)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi $0
pushl_cfi $do_coprocessor_error
jmp error_code
@@ -865,6 +873,7 @@ END(coprocessor_error)
ENTRY(simd_coprocessor_error)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi $0
#ifdef CONFIG_X86_INVD_BUG
/* AMD 486 bug: invd from userspace calls exception 19 instead of #GP */
@@ -886,6 +895,7 @@ END(simd_coprocessor_error)
ENTRY(device_not_available)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi $-1 # mark this as an int
pushl_cfi $do_device_not_available
jmp error_code
@@ -906,6 +916,7 @@ END(native_irq_enable_sysexit)
ENTRY(overflow)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi $0
pushl_cfi $do_overflow
jmp error_code
@@ -914,6 +925,7 @@ END(overflow)
ENTRY(bounds)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi $0
pushl_cfi $do_bounds
jmp error_code
@@ -922,6 +934,7 @@ END(bounds)
ENTRY(invalid_op)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi $0
pushl_cfi $do_invalid_op
jmp error_code
@@ -930,6 +943,7 @@ END(invalid_op)
ENTRY(coprocessor_segment_overrun)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi $0
pushl_cfi $do_coprocessor_segment_overrun
jmp error_code
@@ -938,6 +952,7 @@ END(coprocessor_segment_overrun)
ENTRY(invalid_TSS)
RING0_EC_FRAME
+ ASM_CLAC
pushl_cfi $do_invalid_TSS
jmp error_code
CFI_ENDPROC
@@ -945,6 +960,7 @@ END(invalid_TSS)
ENTRY(segment_not_present)
RING0_EC_FRAME
+ ASM_CLAC
pushl_cfi $do_segment_not_present
jmp error_code
CFI_ENDPROC
@@ -952,6 +968,7 @@ END(segment_not_present)
ENTRY(stack_segment)
RING0_EC_FRAME
+ ASM_CLAC
pushl_cfi $do_stack_segment
jmp error_code
CFI_ENDPROC
@@ -959,6 +976,7 @@ END(stack_segment)
ENTRY(alignment_check)
RING0_EC_FRAME
+ ASM_CLAC
pushl_cfi $do_alignment_check
jmp error_code
CFI_ENDPROC
@@ -966,6 +984,7 @@ END(alignment_check)
ENTRY(divide_error)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi $0 # no error code
pushl_cfi $do_divide_error
jmp error_code
@@ -975,6 +994,7 @@ END(divide_error)
#ifdef CONFIG_X86_MCE
ENTRY(machine_check)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi $0
pushl_cfi machine_check_vector
jmp error_code
@@ -984,6 +1004,7 @@ END(machine_check)
ENTRY(spurious_interrupt_bug)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi $0
pushl_cfi $do_spurious_interrupt_bug
jmp error_code
@@ -1109,17 +1130,21 @@ ENTRY(ftrace_caller)
pushl %eax
pushl %ecx
pushl %edx
- movl 0xc(%esp), %eax
+ pushl $0 /* Pass NULL as regs pointer */
+ movl 4*4(%esp), %eax
movl 0x4(%ebp), %edx
+ leal function_trace_op, %ecx
subl $MCOUNT_INSN_SIZE, %eax
.globl ftrace_call
ftrace_call:
call ftrace_stub
+ addl $4,%esp /* skip NULL pointer */
popl %edx
popl %ecx
popl %eax
+ftrace_ret:
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
.globl ftrace_graph_call
ftrace_graph_call:
@@ -1131,6 +1156,71 @@ ftrace_stub:
ret
END(ftrace_caller)
+ENTRY(ftrace_regs_caller)
+ pushf /* push flags before compare (in cs location) */
+ cmpl $0, function_trace_stop
+ jne ftrace_restore_flags
+
+ /*
+ * i386 does not save SS and ESP when coming from kernel.
+ * Instead, to get sp, &regs->sp is used (see ptrace.h).
+ * Unfortunately, that means eflags must be at the same location
+ * as the current return ip is. We move the return ip into the
+ * ip location, and move flags into the return ip location.
+ */
+ pushl 4(%esp) /* save return ip into ip slot */
+
+ pushl $0 /* Load 0 into orig_ax */
+ pushl %gs
+ pushl %fs
+ pushl %es
+ pushl %ds
+ pushl %eax
+ pushl %ebp
+ pushl %edi
+ pushl %esi
+ pushl %edx
+ pushl %ecx
+ pushl %ebx
+
+ movl 13*4(%esp), %eax /* Get the saved flags */
+ movl %eax, 14*4(%esp) /* Move saved flags into regs->flags location */
+ /* clobbering return ip */
+ movl $__KERNEL_CS,13*4(%esp)
+
+ movl 12*4(%esp), %eax /* Load ip (1st parameter) */
+ subl $MCOUNT_INSN_SIZE, %eax /* Adjust ip */
+ movl 0x4(%ebp), %edx /* Load parent ip (2nd parameter) */
+ leal function_trace_op, %ecx /* Save ftrace_pos in 3rd parameter */
+ pushl %esp /* Save pt_regs as 4th parameter */
+
+GLOBAL(ftrace_regs_call)
+ call ftrace_stub
+
+ addl $4, %esp /* Skip pt_regs */
+ movl 14*4(%esp), %eax /* Move flags back into cs */
+ movl %eax, 13*4(%esp) /* Needed to keep addl from modifying flags */
+ movl 12*4(%esp), %eax /* Get return ip from regs->ip */
+ movl %eax, 14*4(%esp) /* Put return ip back for ret */
+
+ popl %ebx
+ popl %ecx
+ popl %edx
+ popl %esi
+ popl %edi
+ popl %ebp
+ popl %eax
+ popl %ds
+ popl %es
+ popl %fs
+ popl %gs
+ addl $8, %esp /* Skip orig_ax and ip */
+ popf /* Pop flags at end (no addl to corrupt flags) */
+ jmp ftrace_ret
+
+ftrace_restore_flags:
+ popf
+ jmp ftrace_stub
#else /* ! CONFIG_DYNAMIC_FTRACE */
ENTRY(mcount)
@@ -1171,9 +1261,6 @@ END(mcount)
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
ENTRY(ftrace_graph_caller)
- cmpl $0, function_trace_stop
- jne ftrace_stub
-
pushl %eax
pushl %ecx
pushl %edx
@@ -1207,6 +1294,7 @@ return_to_handler:
ENTRY(page_fault)
RING0_EC_FRAME
+ ASM_CLAC
pushl_cfi $do_page_fault
ALIGN
error_code:
@@ -1279,6 +1367,7 @@ END(page_fault)
ENTRY(debug)
RING0_INT_FRAME
+ ASM_CLAC
cmpl $ia32_sysenter_target,(%esp)
jne debug_stack_correct
FIX_STACK 12, debug_stack_correct, debug_esp_fix_insn
@@ -1303,6 +1392,7 @@ END(debug)
*/
ENTRY(nmi)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi %eax
movl %ss, %eax
cmpw $__ESPFIX_SS, %ax
@@ -1373,6 +1463,7 @@ END(nmi)
ENTRY(int3)
RING0_INT_FRAME
+ ASM_CLAC
pushl_cfi $-1 # mark this as an int
SAVE_ALL
TRACE_IRQS_OFF
@@ -1393,6 +1484,7 @@ END(general_protection)
#ifdef CONFIG_KVM_GUEST
ENTRY(async_page_fault)
RING0_EC_FRAME
+ ASM_CLAC
pushl_cfi $do_async_page_fault
jmp error_code
CFI_ENDPROC
diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S
index 69babd8c834f..44531acd9a81 100644
--- a/arch/x86/kernel/entry_64.S
+++ b/arch/x86/kernel/entry_64.S
@@ -56,6 +56,8 @@
#include <asm/ftrace.h>
#include <asm/percpu.h>
#include <asm/asm.h>
+#include <asm/rcu.h>
+#include <asm/smap.h>
#include <linux/err.h>
/* Avoid __ASSEMBLER__'ifying <linux/audit.h> just for this. */
@@ -68,25 +70,51 @@
.section .entry.text, "ax"
#ifdef CONFIG_FUNCTION_TRACER
+
+#ifdef CC_USING_FENTRY
+# define function_hook __fentry__
+#else
+# define function_hook mcount
+#endif
+
#ifdef CONFIG_DYNAMIC_FTRACE
-ENTRY(mcount)
+
+ENTRY(function_hook)
retq
-END(mcount)
+END(function_hook)
+
+/* skip is set if stack has been adjusted */
+.macro ftrace_caller_setup skip=0
+ MCOUNT_SAVE_FRAME \skip
+
+ /* Load the ftrace_ops into the 3rd parameter */
+ leaq function_trace_op, %rdx
+
+ /* Load ip into the first parameter */
+ movq RIP(%rsp), %rdi
+ subq $MCOUNT_INSN_SIZE, %rdi
+ /* Load the parent_ip into the second parameter */
+#ifdef CC_USING_FENTRY
+ movq SS+16(%rsp), %rsi
+#else
+ movq 8(%rbp), %rsi
+#endif
+.endm
ENTRY(ftrace_caller)
+ /* Check if tracing was disabled (quick check) */
cmpl $0, function_trace_stop
jne ftrace_stub
- MCOUNT_SAVE_FRAME
-
- movq 0x38(%rsp), %rdi
- movq 8(%rbp), %rsi
- subq $MCOUNT_INSN_SIZE, %rdi
+ ftrace_caller_setup
+ /* regs go into 4th parameter (but make it NULL) */
+ movq $0, %rcx
GLOBAL(ftrace_call)
call ftrace_stub
MCOUNT_RESTORE_FRAME
+ftrace_return:
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
GLOBAL(ftrace_graph_call)
@@ -97,8 +125,78 @@ GLOBAL(ftrace_stub)
retq
END(ftrace_caller)
+ENTRY(ftrace_regs_caller)
+ /* Save the current flags before compare (in SS location)*/
+ pushfq
+
+ /* Check if tracing was disabled (quick check) */
+ cmpl $0, function_trace_stop
+ jne ftrace_restore_flags
+
+ /* skip=8 to skip flags saved in SS */
+ ftrace_caller_setup 8
+
+ /* Save the rest of pt_regs */
+ movq %r15, R15(%rsp)
+ movq %r14, R14(%rsp)
+ movq %r13, R13(%rsp)
+ movq %r12, R12(%rsp)
+ movq %r11, R11(%rsp)
+ movq %r10, R10(%rsp)
+ movq %rbp, RBP(%rsp)
+ movq %rbx, RBX(%rsp)
+ /* Copy saved flags */
+ movq SS(%rsp), %rcx
+ movq %rcx, EFLAGS(%rsp)
+ /* Kernel segments */
+ movq $__KERNEL_DS, %rcx
+ movq %rcx, SS(%rsp)
+ movq $__KERNEL_CS, %rcx
+ movq %rcx, CS(%rsp)
+ /* Stack - skipping return address */
+ leaq SS+16(%rsp), %rcx
+ movq %rcx, RSP(%rsp)
+
+ /* regs go into 4th parameter */
+ leaq (%rsp), %rcx
+
+GLOBAL(ftrace_regs_call)
+ call ftrace_stub
+
+ /* Copy flags back to SS, to restore them */
+ movq EFLAGS(%rsp), %rax
+ movq %rax, SS(%rsp)
+
+ /* Handlers can change the RIP */
+ movq RIP(%rsp), %rax
+ movq %rax, SS+8(%rsp)
+
+ /* restore the rest of pt_regs */
+ movq R15(%rsp), %r15
+ movq R14(%rsp), %r14
+ movq R13(%rsp), %r13
+ movq R12(%rsp), %r12
+ movq R10(%rsp), %r10
+ movq RBP(%rsp), %rbp
+ movq RBX(%rsp), %rbx
+
+ /* skip=8 to skip flags saved in SS */
+ MCOUNT_RESTORE_FRAME 8
+
+ /* Restore flags */
+ popfq
+
+ jmp ftrace_return
+ftrace_restore_flags:
+ popfq
+ jmp ftrace_stub
+
+END(ftrace_regs_caller)
+
+
#else /* ! CONFIG_DYNAMIC_FTRACE */
-ENTRY(mcount)
+
+ENTRY(function_hook)
cmpl $0, function_trace_stop
jne ftrace_stub
@@ -119,8 +217,12 @@ GLOBAL(ftrace_stub)
trace:
MCOUNT_SAVE_FRAME
- movq 0x38(%rsp), %rdi
+ movq RIP(%rsp), %rdi
+#ifdef CC_USING_FENTRY
+ movq SS+16(%rsp), %rsi
+#else
movq 8(%rbp), %rsi
+#endif
subq $MCOUNT_INSN_SIZE, %rdi
call *ftrace_trace_function
@@ -128,20 +230,22 @@ trace:
MCOUNT_RESTORE_FRAME
jmp ftrace_stub
-END(mcount)
+END(function_hook)
#endif /* CONFIG_DYNAMIC_FTRACE */
#endif /* CONFIG_FUNCTION_TRACER */
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
ENTRY(ftrace_graph_caller)
- cmpl $0, function_trace_stop
- jne ftrace_stub
-
MCOUNT_SAVE_FRAME
+#ifdef CC_USING_FENTRY
+ leaq SS+16(%rsp), %rdi
+ movq $0, %rdx /* No framepointers needed */
+#else
leaq 8(%rbp), %rdi
- movq 0x38(%rsp), %rsi
movq (%rbp), %rdx
+#endif
+ movq RIP(%rsp), %rsi
subq $MCOUNT_INSN_SIZE, %rsi
call prepare_ftrace_return
@@ -342,15 +446,15 @@ ENDPROC(native_usergs_sysret64)
.macro SAVE_ARGS_IRQ
cld
/* start from rbp in pt_regs and jump over */
- movq_cfi rdi, RDI-RBP
- movq_cfi rsi, RSI-RBP
- movq_cfi rdx, RDX-RBP
- movq_cfi rcx, RCX-RBP
- movq_cfi rax, RAX-RBP
- movq_cfi r8, R8-RBP
- movq_cfi r9, R9-RBP
- movq_cfi r10, R10-RBP
- movq_cfi r11, R11-RBP
+ movq_cfi rdi, (RDI-RBP)
+ movq_cfi rsi, (RSI-RBP)
+ movq_cfi rdx, (RDX-RBP)
+ movq_cfi rcx, (RCX-RBP)
+ movq_cfi rax, (RAX-RBP)
+ movq_cfi r8, (R8-RBP)
+ movq_cfi r9, (R9-RBP)
+ movq_cfi r10, (R10-RBP)
+ movq_cfi r11, (R11-RBP)
/* Save rbp so that we can unwind from get_irq_regs() */
movq_cfi rbp, 0
@@ -384,7 +488,7 @@ ENDPROC(native_usergs_sysret64)
.endm
ENTRY(save_rest)
- PARTIAL_FRAME 1 REST_SKIP+8
+ PARTIAL_FRAME 1 (REST_SKIP+8)
movq 5*8+16(%rsp), %r11 /* save return address */
movq_cfi rbx, RBX+16
movq_cfi rbp, RBP+16
@@ -440,7 +544,7 @@ ENTRY(ret_from_fork)
LOCK ; btr $TIF_FORK,TI_flags(%r8)
- pushq_cfi kernel_eflags(%rip)
+ pushq_cfi $0x0002
popfq_cfi # reset kernel eflags
call schedule_tail # rdi: 'prev' task parameter
@@ -465,7 +569,8 @@ END(ret_from_fork)
* System call entry. Up to 6 arguments in registers are supported.
*
* SYSCALL does not save anything on the stack and does not change the
- * stack pointer.
+ * stack pointer. However, it does mask the flags register for us, so
+ * CLD and CLAC are not needed.
*/
/*
@@ -565,7 +670,7 @@ sysret_careful:
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_NONE)
pushq_cfi %rdi
- call schedule
+ SCHEDULE_USER
popq_cfi %rdi
jmp sysret_check
@@ -678,7 +783,7 @@ int_careful:
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_NONE)
pushq_cfi %rdi
- call schedule
+ SCHEDULE_USER
popq_cfi %rdi
DISABLE_INTERRUPTS(CLBR_NONE)
TRACE_IRQS_OFF
@@ -884,6 +989,7 @@ END(interrupt)
*/
.p2align CONFIG_X86_L1_CACHE_SHIFT
common_interrupt:
+ ASM_CLAC
XCPT_FRAME
addq $-0x80,(%rsp) /* Adjust vector to [-256,-1] range */
interrupt do_IRQ
@@ -974,7 +1080,7 @@ retint_careful:
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_NONE)
pushq_cfi %rdi
- call schedule
+ SCHEDULE_USER
popq_cfi %rdi
GET_THREAD_INFO(%rcx)
DISABLE_INTERRUPTS(CLBR_NONE)
@@ -1023,6 +1129,7 @@ END(common_interrupt)
*/
.macro apicinterrupt num sym do_sym
ENTRY(\sym)
+ ASM_CLAC
INTR_FRAME
pushq_cfi $~(\num)
.Lcommon_\sym:
@@ -1077,6 +1184,7 @@ apicinterrupt IRQ_WORK_VECTOR \
*/
.macro zeroentry sym do_sym
ENTRY(\sym)
+ ASM_CLAC
INTR_FRAME
PARAVIRT_ADJUST_EXCEPTION_FRAME
pushq_cfi $-1 /* ORIG_RAX: no syscall to restart */
@@ -1094,6 +1202,7 @@ END(\sym)
.macro paranoidzeroentry sym do_sym
ENTRY(\sym)
+ ASM_CLAC
INTR_FRAME
PARAVIRT_ADJUST_EXCEPTION_FRAME
pushq_cfi $-1 /* ORIG_RAX: no syscall to restart */
@@ -1112,6 +1221,7 @@ END(\sym)
#define INIT_TSS_IST(x) PER_CPU_VAR(init_tss) + (TSS_ist + ((x) - 1) * 8)
.macro paranoidzeroentry_ist sym do_sym ist
ENTRY(\sym)
+ ASM_CLAC
INTR_FRAME
PARAVIRT_ADJUST_EXCEPTION_FRAME
pushq_cfi $-1 /* ORIG_RAX: no syscall to restart */
@@ -1131,6 +1241,7 @@ END(\sym)
.macro errorentry sym do_sym
ENTRY(\sym)
+ ASM_CLAC
XCPT_FRAME
PARAVIRT_ADJUST_EXCEPTION_FRAME
subq $ORIG_RAX-R15, %rsp
@@ -1149,6 +1260,7 @@ END(\sym)
/* error code is on the stack already */
.macro paranoiderrorentry sym do_sym
ENTRY(\sym)
+ ASM_CLAC
XCPT_FRAME
PARAVIRT_ADJUST_EXCEPTION_FRAME
subq $ORIG_RAX-R15, %rsp
@@ -1449,7 +1561,7 @@ paranoid_userspace:
paranoid_schedule:
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_ANY)
- call schedule
+ SCHEDULE_USER
DISABLE_INTERRUPTS(CLBR_ANY)
TRACE_IRQS_OFF
jmp paranoid_userspace
diff --git a/arch/x86/kernel/ftrace.c b/arch/x86/kernel/ftrace.c
index c3a7cb4bf6e6..1d414029f1d8 100644
--- a/arch/x86/kernel/ftrace.c
+++ b/arch/x86/kernel/ftrace.c
@@ -206,6 +206,21 @@ static int
ftrace_modify_code(unsigned long ip, unsigned const char *old_code,
unsigned const char *new_code);
+/*
+ * Should never be called:
+ * As it is only called by __ftrace_replace_code() which is called by
+ * ftrace_replace_code() that x86 overrides, and by ftrace_update_code()
+ * which is called to turn mcount into nops or nops into function calls
+ * but not to convert a function from not using regs to one that uses
+ * regs, which ftrace_modify_call() is for.
+ */
+int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
+ unsigned long addr)
+{
+ WARN_ON(1);
+ return -EINVAL;
+}
+
int ftrace_update_ftrace_func(ftrace_func_t func)
{
unsigned long ip = (unsigned long)(&ftrace_call);
@@ -220,6 +235,14 @@ int ftrace_update_ftrace_func(ftrace_func_t func)
ret = ftrace_modify_code(ip, old, new);
+ /* Also update the regs callback function */
+ if (!ret) {
+ ip = (unsigned long)(&ftrace_regs_call);
+ memcpy(old, &ftrace_regs_call, MCOUNT_INSN_SIZE);
+ new = ftrace_call_replace(ip, (unsigned long)func);
+ ret = ftrace_modify_code(ip, old, new);
+ }
+
atomic_dec(&modifying_ftrace_code);
return ret;
@@ -299,6 +322,32 @@ static int add_brk_on_nop(struct dyn_ftrace *rec)
return add_break(rec->ip, old);
}
+/*
+ * If the record has the FTRACE_FL_REGS set, that means that it
+ * wants to convert to a callback that saves all regs. If FTRACE_FL_REGS
+ * is not not set, then it wants to convert to the normal callback.
+ */
+static unsigned long get_ftrace_addr(struct dyn_ftrace *rec)
+{
+ if (rec->flags & FTRACE_FL_REGS)
+ return (unsigned long)FTRACE_REGS_ADDR;
+ else
+ return (unsigned long)FTRACE_ADDR;
+}
+
+/*
+ * The FTRACE_FL_REGS_EN is set when the record already points to
+ * a function that saves all the regs. Basically the '_EN' version
+ * represents the current state of the function.
+ */
+static unsigned long get_ftrace_old_addr(struct dyn_ftrace *rec)
+{
+ if (rec->flags & FTRACE_FL_REGS_EN)
+ return (unsigned long)FTRACE_REGS_ADDR;
+ else
+ return (unsigned long)FTRACE_ADDR;
+}
+
static int add_breakpoints(struct dyn_ftrace *rec, int enable)
{
unsigned long ftrace_addr;
@@ -306,7 +355,7 @@ static int add_breakpoints(struct dyn_ftrace *rec, int enable)
ret = ftrace_test_record(rec, enable);
- ftrace_addr = (unsigned long)FTRACE_ADDR;
+ ftrace_addr = get_ftrace_addr(rec);
switch (ret) {
case FTRACE_UPDATE_IGNORE:
@@ -316,6 +365,10 @@ static int add_breakpoints(struct dyn_ftrace *rec, int enable)
/* converting nop to call */
return add_brk_on_nop(rec);
+ case FTRACE_UPDATE_MODIFY_CALL_REGS:
+ case FTRACE_UPDATE_MODIFY_CALL:
+ ftrace_addr = get_ftrace_old_addr(rec);
+ /* fall through */
case FTRACE_UPDATE_MAKE_NOP:
/* converting a call to a nop */
return add_brk_on_call(rec, ftrace_addr);
@@ -360,13 +413,21 @@ static int remove_breakpoint(struct dyn_ftrace *rec)
* If not, don't touch the breakpoint, we make just create
* a disaster.
*/
- ftrace_addr = (unsigned long)FTRACE_ADDR;
+ ftrace_addr = get_ftrace_addr(rec);
+ nop = ftrace_call_replace(ip, ftrace_addr);
+
+ if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) == 0)
+ goto update;
+
+ /* Check both ftrace_addr and ftrace_old_addr */
+ ftrace_addr = get_ftrace_old_addr(rec);
nop = ftrace_call_replace(ip, ftrace_addr);
if (memcmp(&ins[1], &nop[1], MCOUNT_INSN_SIZE - 1) != 0)
return -EINVAL;
}
+ update:
return probe_kernel_write((void *)ip, &nop[0], 1);
}
@@ -405,12 +466,14 @@ static int add_update(struct dyn_ftrace *rec, int enable)
ret = ftrace_test_record(rec, enable);
- ftrace_addr = (unsigned long)FTRACE_ADDR;
+ ftrace_addr = get_ftrace_addr(rec);
switch (ret) {
case FTRACE_UPDATE_IGNORE:
return 0;
+ case FTRACE_UPDATE_MODIFY_CALL_REGS:
+ case FTRACE_UPDATE_MODIFY_CALL:
case FTRACE_UPDATE_MAKE_CALL:
/* converting nop to call */
return add_update_call(rec, ftrace_addr);
@@ -455,12 +518,14 @@ static int finish_update(struct dyn_ftrace *rec, int enable)
ret = ftrace_update_record(rec, enable);
- ftrace_addr = (unsigned long)FTRACE_ADDR;
+ ftrace_addr = get_ftrace_addr(rec);
switch (ret) {
case FTRACE_UPDATE_IGNORE:
return 0;
+ case FTRACE_UPDATE_MODIFY_CALL_REGS:
+ case FTRACE_UPDATE_MODIFY_CALL:
case FTRACE_UPDATE_MAKE_CALL:
/* converting nop to call */
return finish_update_call(rec, ftrace_addr);
diff --git a/arch/x86/kernel/head_32.S b/arch/x86/kernel/head_32.S
index d42ab17b7397..957a47aec64e 100644
--- a/arch/x86/kernel/head_32.S
+++ b/arch/x86/kernel/head_32.S
@@ -287,27 +287,28 @@ ENTRY(startup_32_smp)
leal -__PAGE_OFFSET(%ecx),%esp
default_entry:
-
/*
* New page tables may be in 4Mbyte page mode and may
* be using the global pages.
*
* NOTE! If we are on a 486 we may have no cr4 at all!
- * So we do not try to touch it unless we really have
- * some bits in it to set. This won't work if the BSP
- * implements cr4 but this AP does not -- very unlikely
- * but be warned! The same applies to the pse feature
- * if not equally supported. --macro
- *
- * NOTE! We have to correct for the fact that we're
- * not yet offset PAGE_OFFSET..
+ * Specifically, cr4 exists if and only if CPUID exists,
+ * which in turn exists if and only if EFLAGS.ID exists.
*/
-#define cr4_bits pa(mmu_cr4_features)
- movl cr4_bits,%edx
- andl %edx,%edx
- jz 6f
- movl %cr4,%eax # Turn on paging options (PSE,PAE,..)
- orl %edx,%eax
+ movl $X86_EFLAGS_ID,%ecx
+ pushl %ecx
+ popfl
+ pushfl
+ popl %eax
+ pushl $0
+ popfl
+ pushfl
+ popl %edx
+ xorl %edx,%eax
+ testl %ecx,%eax
+ jz 6f # No ID flag = no CPUID = no CR4
+
+ movl pa(mmu_cr4_features),%eax
movl %eax,%cr4
testb $X86_CR4_PAE, %al # check if PAE is enabled
diff --git a/arch/x86/kernel/i387.c b/arch/x86/kernel/i387.c
index f250431fb505..675a05012449 100644
--- a/arch/x86/kernel/i387.c
+++ b/arch/x86/kernel/i387.c
@@ -19,24 +19,17 @@
#include <asm/fpu-internal.h>
#include <asm/user.h>
-#ifdef CONFIG_X86_64
-# include <asm/sigcontext32.h>
-# include <asm/user32.h>
-#else
-# define save_i387_xstate_ia32 save_i387_xstate
-# define restore_i387_xstate_ia32 restore_i387_xstate
-# define _fpstate_ia32 _fpstate
-# define _xstate_ia32 _xstate
-# define sig_xstate_ia32_size sig_xstate_size
-# define fx_sw_reserved_ia32 fx_sw_reserved
-# define user_i387_ia32_struct user_i387_struct
-# define user32_fxsr_struct user_fxsr_struct
-#endif
-
/*
* Were we in an interrupt that interrupted kernel mode?
*
- * We can do a kernel_fpu_begin/end() pair *ONLY* if that
+ * For now, with eagerfpu we will return interrupted kernel FPU
+ * state as not-idle. TBD: Ideally we can change the return value
+ * to something like __thread_has_fpu(current). But we need to
+ * be careful of doing __thread_clear_has_fpu() before saving
+ * the FPU etc for supporting nested uses etc. For now, take
+ * the simple route!
+ *
+ * On others, we can do a kernel_fpu_begin/end() pair *ONLY* if that
* pair does nothing at all: the thread must not have fpu (so
* that we don't try to save the FPU state), and TS must
* be set (so that the clts/stts pair does nothing that is
@@ -44,6 +37,9 @@
*/
static inline bool interrupted_kernel_fpu_idle(void)
{
+ if (use_eager_fpu())
+ return 0;
+
return !__thread_has_fpu(current) &&
(read_cr0() & X86_CR0_TS);
}
@@ -77,29 +73,29 @@ bool irq_fpu_usable(void)
}
EXPORT_SYMBOL(irq_fpu_usable);
-void kernel_fpu_begin(void)
+void __kernel_fpu_begin(void)
{
struct task_struct *me = current;
- WARN_ON_ONCE(!irq_fpu_usable());
- preempt_disable();
if (__thread_has_fpu(me)) {
__save_init_fpu(me);
__thread_clear_has_fpu(me);
- /* We do 'stts()' in kernel_fpu_end() */
- } else {
+ /* We do 'stts()' in __kernel_fpu_end() */
+ } else if (!use_eager_fpu()) {
this_cpu_write(fpu_owner_task, NULL);
clts();
}
}
-EXPORT_SYMBOL(kernel_fpu_begin);
+EXPORT_SYMBOL(__kernel_fpu_begin);
-void kernel_fpu_end(void)
+void __kernel_fpu_end(void)
{
- stts();
- preempt_enable();
+ if (use_eager_fpu())
+ math_state_restore();
+ else
+ stts();
}
-EXPORT_SYMBOL(kernel_fpu_end);
+EXPORT_SYMBOL(__kernel_fpu_end);
void unlazy_fpu(struct task_struct *tsk)
{
@@ -113,23 +109,15 @@ void unlazy_fpu(struct task_struct *tsk)
}
EXPORT_SYMBOL(unlazy_fpu);
-#ifdef CONFIG_MATH_EMULATION
-# define HAVE_HWFP (boot_cpu_data.hard_math)
-#else
-# define HAVE_HWFP 1
-#endif
-
-static unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
+unsigned int mxcsr_feature_mask __read_mostly = 0xffffffffu;
unsigned int xstate_size;
EXPORT_SYMBOL_GPL(xstate_size);
-unsigned int sig_xstate_ia32_size = sizeof(struct _fpstate_ia32);
static struct i387_fxsave_struct fx_scratch __cpuinitdata;
static void __cpuinit mxcsr_feature_mask_init(void)
{
unsigned long mask = 0;
- clts();
if (cpu_has_fxsr) {
memset(&fx_scratch, 0, sizeof(struct i387_fxsave_struct));
asm volatile("fxsave %0" : : "m" (fx_scratch));
@@ -138,7 +126,6 @@ static void __cpuinit mxcsr_feature_mask_init(void)
mask = 0x0000ffbf;
}
mxcsr_feature_mask &= mask;
- stts();
}
static void __cpuinit init_thread_xstate(void)
@@ -192,9 +179,8 @@ void __cpuinit fpu_init(void)
init_thread_xstate();
mxcsr_feature_mask_init();
- /* clean state in init */
- current_thread_info()->status = 0;
- clear_used_math();
+ xsave_init();
+ eager_fpu_init();
}
void fpu_finit(struct fpu *fpu)
@@ -205,12 +191,7 @@ void fpu_finit(struct fpu *fpu)
}
if (cpu_has_fxsr) {
- struct i387_fxsave_struct *fx = &fpu->state->fxsave;
-
- memset(fx, 0, xstate_size);
- fx->cwd = 0x37f;
- if (cpu_has_xmm)
- fx->mxcsr = MXCSR_DEFAULT;
+ fx_finit(&fpu->state->fxsave);
} else {
struct i387_fsave_struct *fp = &fpu->state->fsave;
memset(fp, 0, xstate_size);
@@ -454,7 +435,7 @@ static inline u32 twd_fxsr_to_i387(struct i387_fxsave_struct *fxsave)
* FXSR floating point environment conversions.
*/
-static void
+void
convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
{
struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave;
@@ -491,8 +472,8 @@ convert_from_fxsr(struct user_i387_ia32_struct *env, struct task_struct *tsk)
memcpy(&to[i], &from[i], sizeof(to[0]));
}
-static void convert_to_fxsr(struct task_struct *tsk,
- const struct user_i387_ia32_struct *env)
+void convert_to_fxsr(struct task_struct *tsk,
+ const struct user_i387_ia32_struct *env)
{
struct i387_fxsave_struct *fxsave = &tsk->thread.fpu.state->fxsave;
@@ -589,223 +570,6 @@ int fpregs_set(struct task_struct *target, const struct user_regset *regset,
}
/*
- * Signal frame handlers.
- */
-
-static inline int save_i387_fsave(struct _fpstate_ia32 __user *buf)
-{
- struct task_struct *tsk = current;
- struct i387_fsave_struct *fp = &tsk->thread.fpu.state->fsave;
-
- fp->status = fp->swd;
- if (__copy_to_user(buf, fp, sizeof(struct i387_fsave_struct)))
- return -1;
- return 1;
-}
-
-static int save_i387_fxsave(struct _fpstate_ia32 __user *buf)
-{
- struct task_struct *tsk = current;
- struct i387_fxsave_struct *fx = &tsk->thread.fpu.state->fxsave;
- struct user_i387_ia32_struct env;
- int err = 0;
-
- convert_from_fxsr(&env, tsk);
- if (__copy_to_user(buf, &env, sizeof(env)))
- return -1;
-
- err |= __put_user(fx->swd, &buf->status);
- err |= __put_user(X86_FXSR_MAGIC, &buf->magic);
- if (err)
- return -1;
-
- if (__copy_to_user(&buf->_fxsr_env[0], fx, xstate_size))
- return -1;
- return 1;
-}
-
-static int save_i387_xsave(void __user *buf)
-{
- struct task_struct *tsk = current;
- struct _fpstate_ia32 __user *fx = buf;
- int err = 0;
-
-
- sanitize_i387_state(tsk);
-
- /*
- * For legacy compatible, we always set FP/SSE bits in the bit
- * vector while saving the state to the user context.
- * This will enable us capturing any changes(during sigreturn) to
- * the FP/SSE bits by the legacy applications which don't touch
- * xstate_bv in the xsave header.
- *
- * xsave aware applications can change the xstate_bv in the xsave
- * header as well as change any contents in the memory layout.
- * xrestore as part of sigreturn will capture all the changes.
- */
- tsk->thread.fpu.state->xsave.xsave_hdr.xstate_bv |= XSTATE_FPSSE;
-
- if (save_i387_fxsave(fx) < 0)
- return -1;
-
- err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved_ia32,
- sizeof(struct _fpx_sw_bytes));
- err |= __put_user(FP_XSTATE_MAGIC2,
- (__u32 __user *) (buf + sig_xstate_ia32_size
- - FP_XSTATE_MAGIC2_SIZE));
- if (err)
- return -1;
-
- return 1;
-}
-
-int save_i387_xstate_ia32(void __user *buf)
-{
- struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
- struct task_struct *tsk = current;
-
- if (!used_math())
- return 0;
-
- if (!access_ok(VERIFY_WRITE, buf, sig_xstate_ia32_size))
- return -EACCES;
- /*
- * This will cause a "finit" to be triggered by the next
- * attempted FPU operation by the 'current' process.
- */
- clear_used_math();
-
- if (!HAVE_HWFP) {
- return fpregs_soft_get(current, NULL,
- 0, sizeof(struct user_i387_ia32_struct),
- NULL, fp) ? -1 : 1;
- }
-
- unlazy_fpu(tsk);
-
- if (cpu_has_xsave)
- return save_i387_xsave(fp);
- if (cpu_has_fxsr)
- return save_i387_fxsave(fp);
- else
- return save_i387_fsave(fp);
-}
-
-static inline int restore_i387_fsave(struct _fpstate_ia32 __user *buf)
-{
- struct task_struct *tsk = current;
-
- return __copy_from_user(&tsk->thread.fpu.state->fsave, buf,
- sizeof(struct i387_fsave_struct));
-}
-
-static int restore_i387_fxsave(struct _fpstate_ia32 __user *buf,
- unsigned int size)
-{
- struct task_struct *tsk = current;
- struct user_i387_ia32_struct env;
- int err;
-
- err = __copy_from_user(&tsk->thread.fpu.state->fxsave, &buf->_fxsr_env[0],
- size);
- /* mxcsr reserved bits must be masked to zero for security reasons */
- tsk->thread.fpu.state->fxsave.mxcsr &= mxcsr_feature_mask;
- if (err || __copy_from_user(&env, buf, sizeof(env)))
- return 1;
- convert_to_fxsr(tsk, &env);
-
- return 0;
-}
-
-static int restore_i387_xsave(void __user *buf)
-{
- struct _fpx_sw_bytes fx_sw_user;
- struct _fpstate_ia32 __user *fx_user =
- ((struct _fpstate_ia32 __user *) buf);
- struct i387_fxsave_struct __user *fx =
- (struct i387_fxsave_struct __user *) &fx_user->_fxsr_env[0];
- struct xsave_hdr_struct *xsave_hdr =
- &current->thread.fpu.state->xsave.xsave_hdr;
- u64 mask;
- int err;
-
- if (check_for_xstate(fx, buf, &fx_sw_user))
- goto fx_only;
-
- mask = fx_sw_user.xstate_bv;
-
- err = restore_i387_fxsave(buf, fx_sw_user.xstate_size);
-
- xsave_hdr->xstate_bv &= pcntxt_mask;
- /*
- * These bits must be zero.
- */
- xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0;
-
- /*
- * Init the state that is not present in the memory layout
- * and enabled by the OS.
- */
- mask = ~(pcntxt_mask & ~mask);
- xsave_hdr->xstate_bv &= mask;
-
- return err;
-fx_only:
- /*
- * Couldn't find the extended state information in the memory
- * layout. Restore the FP/SSE and init the other extended state
- * enabled by the OS.
- */
- xsave_hdr->xstate_bv = XSTATE_FPSSE;
- return restore_i387_fxsave(buf, sizeof(struct i387_fxsave_struct));
-}
-
-int restore_i387_xstate_ia32(void __user *buf)
-{
- int err;
- struct task_struct *tsk = current;
- struct _fpstate_ia32 __user *fp = (struct _fpstate_ia32 __user *) buf;
-
- if (HAVE_HWFP)
- clear_fpu(tsk);
-
- if (!buf) {
- if (used_math()) {
- clear_fpu(tsk);
- clear_used_math();
- }
-
- return 0;
- } else
- if (!access_ok(VERIFY_READ, buf, sig_xstate_ia32_size))
- return -EACCES;
-
- if (!used_math()) {
- err = init_fpu(tsk);
- if (err)
- return err;
- }
-
- if (HAVE_HWFP) {
- if (cpu_has_xsave)
- err = restore_i387_xsave(buf);
- else if (cpu_has_fxsr)
- err = restore_i387_fxsave(fp, sizeof(struct
- i387_fxsave_struct));
- else
- err = restore_i387_fsave(fp);
- } else {
- err = fpregs_soft_set(current, NULL,
- 0, sizeof(struct user_i387_ia32_struct),
- NULL, fp) != 0;
- }
- set_used_math();
-
- return err;
-}
-
-/*
* FPU state for core dumps.
* This is only used for a.out dumps now.
* It is declared generically using elf_fpregset_t (which is
diff --git a/arch/x86/kernel/i8259.c b/arch/x86/kernel/i8259.c
index 36d1853e91af..9a5c460404dc 100644
--- a/arch/x86/kernel/i8259.c
+++ b/arch/x86/kernel/i8259.c
@@ -263,7 +263,7 @@ static void i8259A_shutdown(void)
* out of.
*/
outb(0xff, PIC_MASTER_IMR); /* mask all of 8259A-1 */
- outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-1 */
+ outb(0xff, PIC_SLAVE_IMR); /* mask all of 8259A-2 */
}
static struct syscore_ops i8259_syscore_ops = {
diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c
index d44f7829968e..e4595f105910 100644
--- a/arch/x86/kernel/irq.c
+++ b/arch/x86/kernel/irq.c
@@ -92,7 +92,8 @@ int arch_show_interrupts(struct seq_file *p, int prec)
seq_printf(p, " Rescheduling interrupts\n");
seq_printf(p, "%*s: ", prec, "CAL");
for_each_online_cpu(j)
- seq_printf(p, "%10u ", irq_stats(j)->irq_call_count);
+ seq_printf(p, "%10u ", irq_stats(j)->irq_call_count -
+ irq_stats(j)->irq_tlb_count);
seq_printf(p, " Function call interrupts\n");
seq_printf(p, "%*s: ", prec, "TLB");
for_each_online_cpu(j)
@@ -147,7 +148,6 @@ u64 arch_irq_stat_cpu(unsigned int cpu)
#ifdef CONFIG_SMP
sum += irq_stats(cpu)->irq_resched_count;
sum += irq_stats(cpu)->irq_call_count;
- sum += irq_stats(cpu)->irq_tlb_count;
#endif
#ifdef CONFIG_X86_THERMAL_VECTOR
sum += irq_stats(cpu)->irq_thermal_count;
diff --git a/arch/x86/kernel/kprobes.c b/arch/x86/kernel/kprobes.c
index e2f751efb7b1..57916c0d3cf6 100644
--- a/arch/x86/kernel/kprobes.c
+++ b/arch/x86/kernel/kprobes.c
@@ -541,6 +541,23 @@ reenter_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb
return 1;
}
+#ifdef KPROBES_CAN_USE_FTRACE
+static void __kprobes skip_singlestep(struct kprobe *p, struct pt_regs *regs,
+ struct kprobe_ctlblk *kcb)
+{
+ /*
+ * Emulate singlestep (and also recover regs->ip)
+ * as if there is a 5byte nop
+ */
+ regs->ip = (unsigned long)p->addr + MCOUNT_INSN_SIZE;
+ if (unlikely(p->post_handler)) {
+ kcb->kprobe_status = KPROBE_HIT_SSDONE;
+ p->post_handler(p, regs, 0);
+ }
+ __this_cpu_write(current_kprobe, NULL);
+}
+#endif
+
/*
* Interrupts are disabled on entry as trap3 is an interrupt gate and they
* remain disabled throughout this function.
@@ -599,6 +616,12 @@ static int __kprobes kprobe_handler(struct pt_regs *regs)
} else if (kprobe_running()) {
p = __this_cpu_read(current_kprobe);
if (p->break_handler && p->break_handler(p, regs)) {
+#ifdef KPROBES_CAN_USE_FTRACE
+ if (kprobe_ftrace(p)) {
+ skip_singlestep(p, regs, kcb);
+ return 1;
+ }
+#endif
setup_singlestep(p, regs, kcb, 0);
return 1;
}
@@ -1052,6 +1075,50 @@ int __kprobes longjmp_break_handler(struct kprobe *p, struct pt_regs *regs)
return 0;
}
+#ifdef KPROBES_CAN_USE_FTRACE
+/* Ftrace callback handler for kprobes */
+void __kprobes kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *ops, struct pt_regs *regs)
+{
+ struct kprobe *p;
+ struct kprobe_ctlblk *kcb;
+ unsigned long flags;
+
+ /* Disable irq for emulating a breakpoint and avoiding preempt */
+ local_irq_save(flags);
+
+ p = get_kprobe((kprobe_opcode_t *)ip);
+ if (unlikely(!p) || kprobe_disabled(p))
+ goto end;
+
+ kcb = get_kprobe_ctlblk();
+ if (kprobe_running()) {
+ kprobes_inc_nmissed_count(p);
+ } else {
+ /* Kprobe handler expects regs->ip = ip + 1 as breakpoint hit */
+ regs->ip = ip + sizeof(kprobe_opcode_t);
+
+ __this_cpu_write(current_kprobe, p);
+ kcb->kprobe_status = KPROBE_HIT_ACTIVE;
+ if (!p->pre_handler || !p->pre_handler(p, regs))
+ skip_singlestep(p, regs, kcb);
+ /*
+ * If pre_handler returns !0, it sets regs->ip and
+ * resets current kprobe.
+ */
+ }
+end:
+ local_irq_restore(flags);
+}
+
+int __kprobes arch_prepare_kprobe_ftrace(struct kprobe *p)
+{
+ p->ainsn.insn = NULL;
+ p->ainsn.boostable = -1;
+ return 0;
+}
+#endif
+
int __init arch_init_kprobes(void)
{
return arch_init_optprobes();
diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c
index 82746f942cd8..7720ff5a9ee2 100644
--- a/arch/x86/kernel/microcode_amd.c
+++ b/arch/x86/kernel/microcode_amd.c
@@ -75,20 +75,113 @@ struct microcode_amd {
static struct equiv_cpu_entry *equiv_cpu_table;
-/* page-sized ucode patch buffer */
-void *patch;
+struct ucode_patch {
+ struct list_head plist;
+ void *data;
+ u32 patch_id;
+ u16 equiv_cpu;
+};
+
+static LIST_HEAD(pcache);
+
+static u16 find_equiv_id(unsigned int cpu)
+{
+ struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+ int i = 0;
+
+ if (!equiv_cpu_table)
+ return 0;
+
+ while (equiv_cpu_table[i].installed_cpu != 0) {
+ if (uci->cpu_sig.sig == equiv_cpu_table[i].installed_cpu)
+ return equiv_cpu_table[i].equiv_cpu;
+
+ i++;
+ }
+ return 0;
+}
+
+static u32 find_cpu_family_by_equiv_cpu(u16 equiv_cpu)
+{
+ int i = 0;
+
+ BUG_ON(!equiv_cpu_table);
+
+ while (equiv_cpu_table[i].equiv_cpu != 0) {
+ if (equiv_cpu == equiv_cpu_table[i].equiv_cpu)
+ return equiv_cpu_table[i].installed_cpu;
+ i++;
+ }
+ return 0;
+}
+
+/*
+ * a small, trivial cache of per-family ucode patches
+ */
+static struct ucode_patch *cache_find_patch(u16 equiv_cpu)
+{
+ struct ucode_patch *p;
+
+ list_for_each_entry(p, &pcache, plist)
+ if (p->equiv_cpu == equiv_cpu)
+ return p;
+ return NULL;
+}
+
+static void update_cache(struct ucode_patch *new_patch)
+{
+ struct ucode_patch *p;
+
+ list_for_each_entry(p, &pcache, plist) {
+ if (p->equiv_cpu == new_patch->equiv_cpu) {
+ if (p->patch_id >= new_patch->patch_id)
+ /* we already have the latest patch */
+ return;
+
+ list_replace(&p->plist, &new_patch->plist);
+ kfree(p->data);
+ kfree(p);
+ return;
+ }
+ }
+ /* no patch found, add it */
+ list_add_tail(&new_patch->plist, &pcache);
+}
+
+static void free_cache(void)
+{
+ struct ucode_patch *p, *tmp;
+
+ list_for_each_entry_safe(p, tmp, &pcache, plist) {
+ __list_del(p->plist.prev, p->plist.next);
+ kfree(p->data);
+ kfree(p);
+ }
+}
+
+static struct ucode_patch *find_patch(unsigned int cpu)
+{
+ u16 equiv_id;
+
+ equiv_id = find_equiv_id(cpu);
+ if (!equiv_id)
+ return NULL;
+
+ return cache_find_patch(equiv_id);
+}
static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
{
struct cpuinfo_x86 *c = &cpu_data(cpu);
+ csig->sig = cpuid_eax(0x00000001);
csig->rev = c->microcode;
pr_info("CPU%d: patch_level=0x%08x\n", cpu, csig->rev);
return 0;
}
-static unsigned int verify_ucode_size(int cpu, u32 patch_size,
+static unsigned int verify_patch_size(int cpu, u32 patch_size,
unsigned int size)
{
struct cpuinfo_x86 *c = &cpu_data(cpu);
@@ -118,95 +211,37 @@ static unsigned int verify_ucode_size(int cpu, u32 patch_size,
return patch_size;
}
-static u16 find_equiv_id(void)
+static int apply_microcode_amd(int cpu)
{
- unsigned int current_cpu_id, i = 0;
-
- BUG_ON(equiv_cpu_table == NULL);
-
- current_cpu_id = cpuid_eax(0x00000001);
-
- while (equiv_cpu_table[i].installed_cpu != 0) {
- if (current_cpu_id == equiv_cpu_table[i].installed_cpu)
- return equiv_cpu_table[i].equiv_cpu;
-
- i++;
- }
- return 0;
-}
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+ struct microcode_amd *mc_amd;
+ struct ucode_cpu_info *uci;
+ struct ucode_patch *p;
+ u32 rev, dummy;
-/*
- * we signal a good patch is found by returning its size > 0
- */
-static int get_matching_microcode(int cpu, const u8 *ucode_ptr,
- unsigned int leftover_size, int rev,
- unsigned int *current_size)
-{
- struct microcode_header_amd *mc_hdr;
- unsigned int actual_size, patch_size;
- u16 equiv_cpu_id;
+ BUG_ON(raw_smp_processor_id() != cpu);
- /* size of the current patch we're staring at */
- patch_size = *(u32 *)(ucode_ptr + 4);
- *current_size = patch_size + SECTION_HDR_SIZE;
+ uci = ucode_cpu_info + cpu;
- equiv_cpu_id = find_equiv_id();
- if (!equiv_cpu_id)
+ p = find_patch(cpu);
+ if (!p)
return 0;
- /*
- * let's look at the patch header itself now
- */
- mc_hdr = (struct microcode_header_amd *)(ucode_ptr + SECTION_HDR_SIZE);
+ mc_amd = p->data;
+ uci->mc = p->data;
- if (mc_hdr->processor_rev_id != equiv_cpu_id)
- return 0;
+ rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
- /* ucode might be chipset specific -- currently we don't support this */
- if (mc_hdr->nb_dev_id || mc_hdr->sb_dev_id) {
- pr_err("CPU%d: chipset specific code not yet supported\n",
- cpu);
+ /* need to apply patch? */
+ if (rev >= mc_amd->hdr.patch_id) {
+ c->microcode = rev;
return 0;
}
- if (mc_hdr->patch_id <= rev)
- return 0;
-
- /*
- * now that the header looks sane, verify its size
- */
- actual_size = verify_ucode_size(cpu, patch_size, leftover_size);
- if (!actual_size)
- return 0;
-
- /* clear the patch buffer */
- memset(patch, 0, PAGE_SIZE);
-
- /* all looks ok, get the binary patch */
- get_ucode_data(patch, ucode_ptr + SECTION_HDR_SIZE, actual_size);
-
- return actual_size;
-}
-
-static int apply_microcode_amd(int cpu)
-{
- u32 rev, dummy;
- int cpu_num = raw_smp_processor_id();
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu_num;
- struct microcode_amd *mc_amd = uci->mc;
- struct cpuinfo_x86 *c = &cpu_data(cpu);
-
- /* We should bind the task to the CPU */
- BUG_ON(cpu_num != cpu);
-
- if (mc_amd == NULL)
- return 0;
-
wrmsrl(MSR_AMD64_PATCH_LOADER, (u64)(long)&mc_amd->hdr.data_code);
- /* get patch id after patching */
- rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
- /* check current patch id and patch's id for match */
+ /* verify patch application was successful */
+ rdmsr(MSR_AMD64_PATCH_LEVEL, rev, dummy);
if (rev != mc_amd->hdr.patch_id) {
pr_err("CPU%d: update failed for patch_level=0x%08x\n",
cpu, mc_amd->hdr.patch_id);
@@ -238,7 +273,7 @@ static int install_equiv_cpu_table(const u8 *buf)
return -ENOMEM;
}
- get_ucode_data(equiv_cpu_table, buf + CONTAINER_HDR_SZ, size);
+ memcpy(equiv_cpu_table, buf + CONTAINER_HDR_SZ, size);
/* add header length */
return size + CONTAINER_HDR_SZ;
@@ -250,61 +285,113 @@ static void free_equiv_cpu_table(void)
equiv_cpu_table = NULL;
}
-static enum ucode_state
-generic_load_microcode(int cpu, const u8 *data, size_t size)
+static void cleanup(void)
{
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
- struct microcode_header_amd *mc_hdr = NULL;
- unsigned int mc_size, leftover, current_size = 0;
+ free_equiv_cpu_table();
+ free_cache();
+}
+
+/*
+ * We return the current size even if some of the checks failed so that
+ * we can skip over the next patch. If we return a negative value, we
+ * signal a grave error like a memory allocation has failed and the
+ * driver cannot continue functioning normally. In such cases, we tear
+ * down everything we've used up so far and exit.
+ */
+static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover)
+{
+ struct cpuinfo_x86 *c = &cpu_data(cpu);
+ struct microcode_header_amd *mc_hdr;
+ struct ucode_patch *patch;
+ unsigned int patch_size, crnt_size, ret;
+ u32 proc_fam;
+ u16 proc_id;
+
+ patch_size = *(u32 *)(fw + 4);
+ crnt_size = patch_size + SECTION_HDR_SIZE;
+ mc_hdr = (struct microcode_header_amd *)(fw + SECTION_HDR_SIZE);
+ proc_id = mc_hdr->processor_rev_id;
+
+ proc_fam = find_cpu_family_by_equiv_cpu(proc_id);
+ if (!proc_fam) {
+ pr_err("No patch family for equiv ID: 0x%04x\n", proc_id);
+ return crnt_size;
+ }
+
+ /* check if patch is for the current family */
+ proc_fam = ((proc_fam >> 8) & 0xf) + ((proc_fam >> 20) & 0xff);
+ if (proc_fam != c->x86)
+ return crnt_size;
+
+ if (mc_hdr->nb_dev_id || mc_hdr->sb_dev_id) {
+ pr_err("Patch-ID 0x%08x: chipset-specific code unsupported.\n",
+ mc_hdr->patch_id);
+ return crnt_size;
+ }
+
+ ret = verify_patch_size(cpu, patch_size, leftover);
+ if (!ret) {
+ pr_err("Patch-ID 0x%08x: size mismatch.\n", mc_hdr->patch_id);
+ return crnt_size;
+ }
+
+ patch = kzalloc(sizeof(*patch), GFP_KERNEL);
+ if (!patch) {
+ pr_err("Patch allocation failure.\n");
+ return -EINVAL;
+ }
+
+ patch->data = kzalloc(patch_size, GFP_KERNEL);
+ if (!patch->data) {
+ pr_err("Patch data allocation failure.\n");
+ kfree(patch);
+ return -EINVAL;
+ }
+
+ /* All looks ok, copy patch... */
+ memcpy(patch->data, fw + SECTION_HDR_SIZE, patch_size);
+ INIT_LIST_HEAD(&patch->plist);
+ patch->patch_id = mc_hdr->patch_id;
+ patch->equiv_cpu = proc_id;
+
+ /* ... and add to cache. */
+ update_cache(patch);
+
+ return crnt_size;
+}
+
+static enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size)
+{
+ enum ucode_state ret = UCODE_ERROR;
+ unsigned int leftover;
+ u8 *fw = (u8 *)data;
+ int crnt_size = 0;
int offset;
- const u8 *ucode_ptr = data;
- void *new_mc = NULL;
- unsigned int new_rev = uci->cpu_sig.rev;
- enum ucode_state state = UCODE_ERROR;
- offset = install_equiv_cpu_table(ucode_ptr);
+ offset = install_equiv_cpu_table(data);
if (offset < 0) {
pr_err("failed to create equivalent cpu table\n");
- goto out;
+ return ret;
}
- ucode_ptr += offset;
+ fw += offset;
leftover = size - offset;
- if (*(u32 *)ucode_ptr != UCODE_UCODE_TYPE) {
+ if (*(u32 *)fw != UCODE_UCODE_TYPE) {
pr_err("invalid type field in container file section header\n");
- goto free_table;
+ free_equiv_cpu_table();
+ return ret;
}
while (leftover) {
- mc_size = get_matching_microcode(cpu, ucode_ptr, leftover,
- new_rev, &current_size);
- if (mc_size) {
- mc_hdr = patch;
- new_mc = patch;
- new_rev = mc_hdr->patch_id;
- goto out_ok;
- }
-
- ucode_ptr += current_size;
- leftover -= current_size;
- }
+ crnt_size = verify_and_add_patch(cpu, fw, leftover);
+ if (crnt_size < 0)
+ return ret;
- if (!new_mc) {
- state = UCODE_NFOUND;
- goto free_table;
+ fw += crnt_size;
+ leftover -= crnt_size;
}
-out_ok:
- uci->mc = new_mc;
- state = UCODE_OK;
- pr_debug("CPU%d update ucode (0x%08x -> 0x%08x)\n",
- cpu, uci->cpu_sig.rev, new_rev);
-
-free_table:
- free_equiv_cpu_table();
-
-out:
- return state;
+ return UCODE_OK;
}
/*
@@ -315,7 +402,7 @@ out:
*
* This legacy file is always smaller than 2K in size.
*
- * Starting at family 15h they are in family specific firmware files:
+ * Beginning with family 15h, they are in family-specific firmware files:
*
* amd-ucode/microcode_amd_fam15h.bin
* amd-ucode/microcode_amd_fam16h.bin
@@ -323,12 +410,17 @@ out:
*
* These might be larger than 2K.
*/
-static enum ucode_state request_microcode_amd(int cpu, struct device *device)
+static enum ucode_state request_microcode_amd(int cpu, struct device *device,
+ bool refresh_fw)
{
char fw_name[36] = "amd-ucode/microcode_amd.bin";
- const struct firmware *fw;
- enum ucode_state ret = UCODE_NFOUND;
struct cpuinfo_x86 *c = &cpu_data(cpu);
+ enum ucode_state ret = UCODE_NFOUND;
+ const struct firmware *fw;
+
+ /* reload ucode container only on the boot cpu */
+ if (!refresh_fw || c->cpu_index != boot_cpu_data.cpu_index)
+ return UCODE_OK;
if (c->x86 >= 0x15)
snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86);
@@ -344,12 +436,17 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device)
goto fw_release;
}
- ret = generic_load_microcode(cpu, fw->data, fw->size);
+ /* free old equiv table */
+ free_equiv_cpu_table();
+
+ ret = load_microcode_amd(cpu, fw->data, fw->size);
+ if (ret != UCODE_OK)
+ cleanup();
-fw_release:
+ fw_release:
release_firmware(fw);
-out:
+ out:
return ret;
}
@@ -383,14 +480,10 @@ struct microcode_ops * __init init_amd_microcode(void)
return NULL;
}
- patch = (void *)get_zeroed_page(GFP_KERNEL);
- if (!patch)
- return NULL;
-
return &microcode_amd_ops;
}
void __exit exit_amd_microcode(void)
{
- free_page((unsigned long)patch);
+ cleanup();
}
diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c
index 9e5bcf1e2376..3a04b224d0c0 100644
--- a/arch/x86/kernel/microcode_core.c
+++ b/arch/x86/kernel/microcode_core.c
@@ -279,19 +279,18 @@ static struct platform_device *microcode_pdev;
static int reload_for_cpu(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
+ enum ucode_state ustate;
int err = 0;
- if (uci->valid) {
- enum ucode_state ustate;
-
- ustate = microcode_ops->request_microcode_fw(cpu, &microcode_pdev->dev);
- if (ustate == UCODE_OK)
- apply_microcode_on_target(cpu);
- else
- if (ustate == UCODE_ERROR)
- err = -EINVAL;
- }
+ if (!uci->valid)
+ return err;
+ ustate = microcode_ops->request_microcode_fw(cpu, &microcode_pdev->dev, true);
+ if (ustate == UCODE_OK)
+ apply_microcode_on_target(cpu);
+ else
+ if (ustate == UCODE_ERROR)
+ err = -EINVAL;
return err;
}
@@ -373,18 +372,15 @@ static void microcode_fini_cpu(int cpu)
static enum ucode_state microcode_resume_cpu(int cpu)
{
- struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
-
- if (!uci->mc)
- return UCODE_NFOUND;
-
pr_debug("CPU%d updated upon resume\n", cpu);
- apply_microcode_on_target(cpu);
+
+ if (apply_microcode_on_target(cpu))
+ return UCODE_ERROR;
return UCODE_OK;
}
-static enum ucode_state microcode_init_cpu(int cpu)
+static enum ucode_state microcode_init_cpu(int cpu, bool refresh_fw)
{
enum ucode_state ustate;
@@ -395,7 +391,8 @@ static enum ucode_state microcode_init_cpu(int cpu)
if (system_state != SYSTEM_RUNNING)
return UCODE_NFOUND;
- ustate = microcode_ops->request_microcode_fw(cpu, &microcode_pdev->dev);
+ ustate = microcode_ops->request_microcode_fw(cpu, &microcode_pdev->dev,
+ refresh_fw);
if (ustate == UCODE_OK) {
pr_debug("CPU%d updated upon init\n", cpu);
@@ -408,14 +405,11 @@ static enum ucode_state microcode_init_cpu(int cpu)
static enum ucode_state microcode_update_cpu(int cpu)
{
struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
- enum ucode_state ustate;
if (uci->valid)
- ustate = microcode_resume_cpu(cpu);
- else
- ustate = microcode_init_cpu(cpu);
+ return microcode_resume_cpu(cpu);
- return ustate;
+ return microcode_init_cpu(cpu, false);
}
static int mc_device_add(struct device *dev, struct subsys_interface *sif)
@@ -431,7 +425,7 @@ static int mc_device_add(struct device *dev, struct subsys_interface *sif)
if (err)
return err;
- if (microcode_init_cpu(cpu) == UCODE_ERROR)
+ if (microcode_init_cpu(cpu, true) == UCODE_ERROR)
return -EINVAL;
return err;
@@ -480,34 +474,41 @@ mc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu)
struct device *dev;
dev = get_cpu_device(cpu);
- switch (action) {
+
+ switch (action & ~CPU_TASKS_FROZEN) {
case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
microcode_update_cpu(cpu);
- case CPU_DOWN_FAILED:
- case CPU_DOWN_FAILED_FROZEN:
pr_debug("CPU%d added\n", cpu);
+ /*
+ * "break" is missing on purpose here because we want to fall
+ * through in order to create the sysfs group.
+ */
+
+ case CPU_DOWN_FAILED:
if (sysfs_create_group(&dev->kobj, &mc_attr_group))
pr_err("Failed to create group for CPU%d\n", cpu);
break;
+
case CPU_DOWN_PREPARE:
- case CPU_DOWN_PREPARE_FROZEN:
/* Suspend is in progress, only remove the interface */
sysfs_remove_group(&dev->kobj, &mc_attr_group);
pr_debug("CPU%d removed\n", cpu);
break;
/*
+ * case CPU_DEAD:
+ *
* When a CPU goes offline, don't free up or invalidate the copy of
* the microcode in kernel memory, so that we can reuse it when the
* CPU comes back online without unnecessarily requesting the userspace
* for it again.
*/
- case CPU_UP_CANCELED_FROZEN:
- /* The CPU refused to come up during a system resume */
- microcode_fini_cpu(cpu);
- break;
}
+
+ /* The CPU refused to come up during a system resume */
+ if (action == CPU_UP_CANCELED_FROZEN)
+ microcode_fini_cpu(cpu);
+
return NOTIFY_OK;
}
diff --git a/arch/x86/kernel/microcode_intel.c b/arch/x86/kernel/microcode_intel.c
index 0327e2b3c408..3544aed39338 100644
--- a/arch/x86/kernel/microcode_intel.c
+++ b/arch/x86/kernel/microcode_intel.c
@@ -405,7 +405,8 @@ static int get_ucode_fw(void *to, const void *from, size_t n)
return 0;
}
-static enum ucode_state request_microcode_fw(int cpu, struct device *device)
+static enum ucode_state request_microcode_fw(int cpu, struct device *device,
+ bool refresh_fw)
{
char name[30];
struct cpuinfo_x86 *c = &cpu_data(cpu);
diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c
index eb113693f043..a7c5661f8496 100644
--- a/arch/x86/kernel/msr.c
+++ b/arch/x86/kernel/msr.c
@@ -257,12 +257,14 @@ static int __init msr_init(void)
goto out_chrdev;
}
msr_class->devnode = msr_devnode;
+ get_online_cpus();
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();
err = 0;
goto out;
@@ -271,6 +273,7 @@ out_class:
i = 0;
for_each_online_cpu(i)
msr_device_destroy(i);
+ put_online_cpus();
class_destroy(msr_class);
out_chrdev:
__unregister_chrdev(MSR_MAJOR, 0, NR_CPUS, "cpu/msr");
@@ -281,11 +284,13 @@ out:
static void __exit msr_exit(void)
{
int cpu = 0;
+ get_online_cpus();
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();
}
module_init(msr_init);
diff --git a/arch/x86/kernel/perf_regs.c b/arch/x86/kernel/perf_regs.c
new file mode 100644
index 000000000000..e309cc5c276e
--- /dev/null
+++ b/arch/x86/kernel/perf_regs.c
@@ -0,0 +1,105 @@
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/perf_event.h>
+#include <linux/bug.h>
+#include <linux/stddef.h>
+#include <asm/perf_regs.h>
+#include <asm/ptrace.h>
+
+#ifdef CONFIG_X86_32
+#define PERF_REG_X86_MAX PERF_REG_X86_32_MAX
+#else
+#define PERF_REG_X86_MAX PERF_REG_X86_64_MAX
+#endif
+
+#define PT_REGS_OFFSET(id, r) [id] = offsetof(struct pt_regs, r)
+
+static unsigned int pt_regs_offset[PERF_REG_X86_MAX] = {
+ PT_REGS_OFFSET(PERF_REG_X86_AX, ax),
+ PT_REGS_OFFSET(PERF_REG_X86_BX, bx),
+ PT_REGS_OFFSET(PERF_REG_X86_CX, cx),
+ PT_REGS_OFFSET(PERF_REG_X86_DX, dx),
+ PT_REGS_OFFSET(PERF_REG_X86_SI, si),
+ PT_REGS_OFFSET(PERF_REG_X86_DI, di),
+ PT_REGS_OFFSET(PERF_REG_X86_BP, bp),
+ PT_REGS_OFFSET(PERF_REG_X86_SP, sp),
+ PT_REGS_OFFSET(PERF_REG_X86_IP, ip),
+ PT_REGS_OFFSET(PERF_REG_X86_FLAGS, flags),
+ PT_REGS_OFFSET(PERF_REG_X86_CS, cs),
+ PT_REGS_OFFSET(PERF_REG_X86_SS, ss),
+#ifdef CONFIG_X86_32
+ PT_REGS_OFFSET(PERF_REG_X86_DS, ds),
+ PT_REGS_OFFSET(PERF_REG_X86_ES, es),
+ PT_REGS_OFFSET(PERF_REG_X86_FS, fs),
+ PT_REGS_OFFSET(PERF_REG_X86_GS, gs),
+#else
+ /*
+ * The pt_regs struct does not store
+ * ds, es, fs, gs in 64 bit mode.
+ */
+ (unsigned int) -1,
+ (unsigned int) -1,
+ (unsigned int) -1,
+ (unsigned int) -1,
+#endif
+#ifdef CONFIG_X86_64
+ PT_REGS_OFFSET(PERF_REG_X86_R8, r8),
+ PT_REGS_OFFSET(PERF_REG_X86_R9, r9),
+ PT_REGS_OFFSET(PERF_REG_X86_R10, r10),
+ PT_REGS_OFFSET(PERF_REG_X86_R11, r11),
+ PT_REGS_OFFSET(PERF_REG_X86_R12, r12),
+ PT_REGS_OFFSET(PERF_REG_X86_R13, r13),
+ PT_REGS_OFFSET(PERF_REG_X86_R14, r14),
+ PT_REGS_OFFSET(PERF_REG_X86_R15, r15),
+#endif
+};
+
+u64 perf_reg_value(struct pt_regs *regs, int idx)
+{
+ if (WARN_ON_ONCE(idx >= ARRAY_SIZE(pt_regs_offset)))
+ return 0;
+
+ return regs_get_register(regs, pt_regs_offset[idx]);
+}
+
+#define REG_RESERVED (~((1ULL << PERF_REG_X86_MAX) - 1ULL))
+
+#ifdef CONFIG_X86_32
+int perf_reg_validate(u64 mask)
+{
+ if (!mask || mask & REG_RESERVED)
+ return -EINVAL;
+
+ return 0;
+}
+
+u64 perf_reg_abi(struct task_struct *task)
+{
+ return PERF_SAMPLE_REGS_ABI_32;
+}
+#else /* CONFIG_X86_64 */
+#define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \
+ (1ULL << PERF_REG_X86_ES) | \
+ (1ULL << PERF_REG_X86_FS) | \
+ (1ULL << PERF_REG_X86_GS))
+
+int perf_reg_validate(u64 mask)
+{
+ if (!mask || mask & REG_RESERVED)
+ return -EINVAL;
+
+ if (mask & REG_NOSUPPORT)
+ return -EINVAL;
+
+ return 0;
+}
+
+u64 perf_reg_abi(struct task_struct *task)
+{
+ if (test_tsk_thread_flag(task, TIF_IA32))
+ return PERF_SAMPLE_REGS_ABI_32;
+ else
+ return PERF_SAMPLE_REGS_ABI_64;
+}
+#endif /* CONFIG_X86_32 */
diff --git a/arch/x86/kernel/probe_roms.c b/arch/x86/kernel/probe_roms.c
index 0bc72e2069e3..d5f15c3f7b25 100644
--- a/arch/x86/kernel/probe_roms.c
+++ b/arch/x86/kernel/probe_roms.c
@@ -150,7 +150,7 @@ static struct resource *find_oprom(struct pci_dev *pdev)
return oprom;
}
-void *pci_map_biosrom(struct pci_dev *pdev)
+void __iomem *pci_map_biosrom(struct pci_dev *pdev)
{
struct resource *oprom = find_oprom(pdev);
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index ef6a8456f719..dc3567e083f9 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -66,15 +66,13 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
{
int ret;
- unlazy_fpu(src);
-
*dst = *src;
if (fpu_allocated(&src->thread.fpu)) {
memset(&dst->thread.fpu, 0, sizeof(dst->thread.fpu));
ret = fpu_alloc(&dst->thread.fpu);
if (ret)
return ret;
- fpu_copy(&dst->thread.fpu, &src->thread.fpu);
+ fpu_copy(dst, src);
}
return 0;
}
@@ -97,16 +95,6 @@ void arch_task_cache_init(void)
SLAB_PANIC | SLAB_NOTRACK, NULL);
}
-static inline void drop_fpu(struct task_struct *tsk)
-{
- /*
- * Forget coprocessor state..
- */
- tsk->fpu_counter = 0;
- clear_fpu(tsk);
- clear_used_math();
-}
-
/*
* Free current thread data structures etc..
*/
@@ -163,7 +151,13 @@ void flush_thread(void)
flush_ptrace_hw_breakpoint(tsk);
memset(tsk->thread.tls_array, 0, sizeof(tsk->thread.tls_array));
- drop_fpu(tsk);
+ drop_init_fpu(tsk);
+ /*
+ * Free the FPU state for non xsave platforms. They get reallocated
+ * lazily at the first use.
+ */
+ if (!use_eager_fpu())
+ free_thread_xstate(tsk);
}
static void hard_disable_TSC(void)
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index 516fa186121b..b9ff83c7135b 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -190,10 +190,6 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
regs->cs = __USER_CS;
regs->ip = new_ip;
regs->sp = new_sp;
- /*
- * Free the old FP and other extended state
- */
- free_thread_xstate(current);
}
EXPORT_SYMBOL_GPL(start_thread);
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c
index 0a980c9d7cb8..8a6d20ce1978 100644
--- a/arch/x86/kernel/process_64.c
+++ b/arch/x86/kernel/process_64.c
@@ -232,10 +232,6 @@ start_thread_common(struct pt_regs *regs, unsigned long new_ip,
regs->cs = _cs;
regs->ss = _ss;
regs->flags = X86_EFLAGS_IF;
- /*
- * Free the old FP and other extended state
- */
- free_thread_xstate(current);
}
void
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index c4c6a5c2bf0f..b00b33a18390 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -21,6 +21,7 @@
#include <linux/signal.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
+#include <linux/rcupdate.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
@@ -1332,9 +1333,6 @@ static const struct user_regset_view user_x86_64_view = {
#define genregs32_get genregs_get
#define genregs32_set genregs_set
-#define user_i387_ia32_struct user_i387_struct
-#define user32_fxsr_struct user_fxsr_struct
-
#endif /* CONFIG_X86_64 */
#if defined CONFIG_X86_32 || defined CONFIG_IA32_EMULATION
@@ -1463,6 +1461,8 @@ long syscall_trace_enter(struct pt_regs *regs)
{
long ret = 0;
+ rcu_user_exit();
+
/*
* If we stepped into a sysenter/syscall insn, it trapped in
* kernel mode; do_debug() cleared TF and set TIF_SINGLESTEP.
@@ -1526,4 +1526,6 @@ void syscall_trace_leave(struct pt_regs *regs)
!test_thread_flag(TIF_SYSCALL_EMU);
if (step || test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall_exit(regs, step);
+
+ rcu_user_enter();
}
diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
index f4b9b80e1b95..4f165479c453 100644
--- a/arch/x86/kernel/setup.c
+++ b/arch/x86/kernel/setup.c
@@ -961,9 +961,7 @@ void __init setup_arch(char **cmdline_p)
kvmclock_init();
#endif
- x86_init.paging.pagetable_setup_start(swapper_pg_dir);
- paging_init();
- x86_init.paging.pagetable_setup_done(swapper_pg_dir);
+ x86_init.paging.pagetable_init();
if (boot_cpu_data.cpuid_level >= 0) {
/* A CPU has %cr4 if and only if it has CPUID */
diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c
index b280908a376e..b33144c8b309 100644
--- a/arch/x86/kernel/signal.c
+++ b/arch/x86/kernel/signal.c
@@ -114,11 +114,12 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc,
regs->orig_ax = -1; /* disable syscall checks */
get_user_ex(buf, &sc->fpstate);
- err |= restore_i387_xstate(buf);
get_user_ex(*pax, &sc->ax);
} get_user_catch(err);
+ err |= restore_xstate_sig(buf, config_enabled(CONFIG_X86_32));
+
return err;
}
@@ -206,35 +207,32 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
void __user **fpstate)
{
/* Default to using normal stack */
+ unsigned long math_size = 0;
unsigned long sp = regs->sp;
+ unsigned long buf_fx = 0;
int onsigstack = on_sig_stack(sp);
-#ifdef CONFIG_X86_64
/* redzone */
- sp -= 128;
-#endif /* CONFIG_X86_64 */
+ if (config_enabled(CONFIG_X86_64))
+ sp -= 128;
if (!onsigstack) {
/* This is the X/Open sanctioned signal stack switching. */
if (ka->sa.sa_flags & SA_ONSTACK) {
if (current->sas_ss_size)
sp = current->sas_ss_sp + current->sas_ss_size;
- } else {
-#ifdef CONFIG_X86_32
- /* This is the legacy signal stack switching. */
- if ((regs->ss & 0xffff) != __USER_DS &&
- !(ka->sa.sa_flags & SA_RESTORER) &&
- ka->sa.sa_restorer)
+ } else if (config_enabled(CONFIG_X86_32) &&
+ (regs->ss & 0xffff) != __USER_DS &&
+ !(ka->sa.sa_flags & SA_RESTORER) &&
+ ka->sa.sa_restorer) {
+ /* This is the legacy signal stack switching. */
sp = (unsigned long) ka->sa.sa_restorer;
-#endif /* CONFIG_X86_32 */
}
}
if (used_math()) {
- sp -= sig_xstate_size;
-#ifdef CONFIG_X86_64
- sp = round_down(sp, 64);
-#endif /* CONFIG_X86_64 */
+ sp = alloc_mathframe(sp, config_enabled(CONFIG_X86_32),
+ &buf_fx, &math_size);
*fpstate = (void __user *)sp;
}
@@ -247,8 +245,9 @@ get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size,
if (onsigstack && !likely(on_sig_stack(sp)))
return (void __user *)-1L;
- /* save i387 state */
- if (used_math() && save_i387_xstate(*fpstate) < 0)
+ /* save i387 and extended state */
+ if (used_math() &&
+ save_xstate_sig(*fpstate, (void __user *)buf_fx, math_size) < 0)
return (void __user *)-1L;
return (void __user *)sp;
@@ -357,7 +356,6 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
put_user_ex(sig, &frame->sig);
put_user_ex(&frame->info, &frame->pinfo);
put_user_ex(&frame->uc, &frame->puc);
- err |= copy_siginfo_to_user(&frame->info, info);
/* Create the ucontext. */
if (cpu_has_xsave)
@@ -369,9 +367,6 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
put_user_ex(sas_ss_flags(regs->sp),
&frame->uc.uc_stack.ss_flags);
put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
- err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
- regs, set->sig[0]);
- err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
/* Set up to return from userspace. */
restorer = VDSO32_SYMBOL(current->mm->context.vdso, rt_sigreturn);
@@ -388,6 +383,11 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
*/
put_user_ex(*((u64 *)&rt_retcode), (u64 *)frame->retcode);
} put_user_catch(err);
+
+ err |= copy_siginfo_to_user(&frame->info, info);
+ err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
+ regs, set->sig[0]);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
if (err)
return -EFAULT;
@@ -436,8 +436,6 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
put_user_ex(sas_ss_flags(regs->sp),
&frame->uc.uc_stack.ss_flags);
put_user_ex(me->sas_ss_size, &frame->uc.uc_stack.ss_size);
- err |= setup_sigcontext(&frame->uc.uc_mcontext, fp, regs, set->sig[0]);
- err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
/* Set up to return from userspace. If provided, use a stub
already in userspace. */
@@ -450,6 +448,9 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
}
} put_user_catch(err);
+ err |= setup_sigcontext(&frame->uc.uc_mcontext, fp, regs, set->sig[0]);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
if (err)
return -EFAULT;
@@ -474,6 +475,75 @@ static int __setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
}
#endif /* CONFIG_X86_32 */
+static int x32_setup_rt_frame(int sig, struct k_sigaction *ka,
+ siginfo_t *info, compat_sigset_t *set,
+ struct pt_regs *regs)
+{
+#ifdef CONFIG_X86_X32_ABI
+ struct rt_sigframe_x32 __user *frame;
+ void __user *restorer;
+ int err = 0;
+ void __user *fpstate = NULL;
+
+ frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
+
+ if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
+ return -EFAULT;
+
+ if (ka->sa.sa_flags & SA_SIGINFO) {
+ if (copy_siginfo_to_user32(&frame->info, info))
+ return -EFAULT;
+ }
+
+ put_user_try {
+ /* Create the ucontext. */
+ if (cpu_has_xsave)
+ put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
+ else
+ put_user_ex(0, &frame->uc.uc_flags);
+ put_user_ex(0, &frame->uc.uc_link);
+ put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
+ put_user_ex(sas_ss_flags(regs->sp),
+ &frame->uc.uc_stack.ss_flags);
+ put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
+ put_user_ex(0, &frame->uc.uc__pad0);
+
+ if (ka->sa.sa_flags & SA_RESTORER) {
+ restorer = ka->sa.sa_restorer;
+ } else {
+ /* could use a vstub here */
+ restorer = NULL;
+ err |= -EFAULT;
+ }
+ put_user_ex(restorer, &frame->pretcode);
+ } put_user_catch(err);
+
+ err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
+ regs, set->sig[0]);
+ err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
+
+ if (err)
+ return -EFAULT;
+
+ /* Set up registers for signal handler */
+ regs->sp = (unsigned long) frame;
+ regs->ip = (unsigned long) ka->sa.sa_handler;
+
+ /* We use the x32 calling convention here... */
+ regs->di = sig;
+ regs->si = (unsigned long) &frame->info;
+ regs->dx = (unsigned long) &frame->uc;
+
+ loadsegment(ds, __USER_DS);
+ loadsegment(es, __USER_DS);
+
+ regs->cs = __USER_CS;
+ regs->ss = __USER_DS;
+#endif /* CONFIG_X86_X32_ABI */
+
+ return 0;
+}
+
#ifdef CONFIG_X86_32
/*
* Atomically swap in the new signal mask, and wait for a signal.
@@ -612,55 +682,22 @@ static int signr_convert(int sig)
return sig;
}
-#ifdef CONFIG_X86_32
-
-#define is_ia32 1
-#define ia32_setup_frame __setup_frame
-#define ia32_setup_rt_frame __setup_rt_frame
-
-#else /* !CONFIG_X86_32 */
-
-#ifdef CONFIG_IA32_EMULATION
-#define is_ia32 test_thread_flag(TIF_IA32)
-#else /* !CONFIG_IA32_EMULATION */
-#define is_ia32 0
-#endif /* CONFIG_IA32_EMULATION */
-
-#ifdef CONFIG_X86_X32_ABI
-#define is_x32 test_thread_flag(TIF_X32)
-
-static int x32_setup_rt_frame(int sig, struct k_sigaction *ka,
- siginfo_t *info, compat_sigset_t *set,
- struct pt_regs *regs);
-#else /* !CONFIG_X86_X32_ABI */
-#define is_x32 0
-#endif /* CONFIG_X86_X32_ABI */
-
-int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
- sigset_t *set, struct pt_regs *regs);
-int ia32_setup_frame(int sig, struct k_sigaction *ka,
- sigset_t *set, struct pt_regs *regs);
-
-#endif /* CONFIG_X86_32 */
-
static int
setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
struct pt_regs *regs)
{
int usig = signr_convert(sig);
sigset_t *set = sigmask_to_save();
+ compat_sigset_t *cset = (compat_sigset_t *) set;
/* Set up the stack frame */
- if (is_ia32) {
+ if (is_ia32_frame()) {
if (ka->sa.sa_flags & SA_SIGINFO)
- return ia32_setup_rt_frame(usig, ka, info, set, regs);
+ return ia32_setup_rt_frame(usig, ka, info, cset, regs);
else
- return ia32_setup_frame(usig, ka, set, regs);
-#ifdef CONFIG_X86_X32_ABI
- } else if (is_x32) {
- return x32_setup_rt_frame(usig, ka, info,
- (compat_sigset_t *)set, regs);
-#endif
+ return ia32_setup_frame(usig, ka, cset, regs);
+ } else if (is_x32_frame()) {
+ return x32_setup_rt_frame(usig, ka, info, cset, regs);
} else {
return __setup_rt_frame(sig, ka, info, set, regs);
}
@@ -779,6 +816,8 @@ static void do_signal(struct pt_regs *regs)
void
do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
{
+ rcu_user_exit();
+
#ifdef CONFIG_X86_MCE
/* notify userspace of pending MCEs */
if (thread_info_flags & _TIF_MCE_NOTIFY)
@@ -804,6 +843,8 @@ do_notify_resume(struct pt_regs *regs, void *unused, __u32 thread_info_flags)
#ifdef CONFIG_X86_32
clear_thread_flag(TIF_IRET);
#endif /* CONFIG_X86_32 */
+
+ rcu_user_enter();
}
void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
@@ -824,72 +865,6 @@ void signal_fault(struct pt_regs *regs, void __user *frame, char *where)
}
#ifdef CONFIG_X86_X32_ABI
-static int x32_setup_rt_frame(int sig, struct k_sigaction *ka,
- siginfo_t *info, compat_sigset_t *set,
- struct pt_regs *regs)
-{
- struct rt_sigframe_x32 __user *frame;
- void __user *restorer;
- int err = 0;
- void __user *fpstate = NULL;
-
- frame = get_sigframe(ka, regs, sizeof(*frame), &fpstate);
-
- if (!access_ok(VERIFY_WRITE, frame, sizeof(*frame)))
- return -EFAULT;
-
- if (ka->sa.sa_flags & SA_SIGINFO) {
- if (copy_siginfo_to_user32(&frame->info, info))
- return -EFAULT;
- }
-
- put_user_try {
- /* Create the ucontext. */
- if (cpu_has_xsave)
- put_user_ex(UC_FP_XSTATE, &frame->uc.uc_flags);
- else
- put_user_ex(0, &frame->uc.uc_flags);
- put_user_ex(0, &frame->uc.uc_link);
- put_user_ex(current->sas_ss_sp, &frame->uc.uc_stack.ss_sp);
- put_user_ex(sas_ss_flags(regs->sp),
- &frame->uc.uc_stack.ss_flags);
- put_user_ex(current->sas_ss_size, &frame->uc.uc_stack.ss_size);
- put_user_ex(0, &frame->uc.uc__pad0);
- err |= setup_sigcontext(&frame->uc.uc_mcontext, fpstate,
- regs, set->sig[0]);
- err |= __copy_to_user(&frame->uc.uc_sigmask, set, sizeof(*set));
-
- if (ka->sa.sa_flags & SA_RESTORER) {
- restorer = ka->sa.sa_restorer;
- } else {
- /* could use a vstub here */
- restorer = NULL;
- err |= -EFAULT;
- }
- put_user_ex(restorer, &frame->pretcode);
- } put_user_catch(err);
-
- if (err)
- return -EFAULT;
-
- /* Set up registers for signal handler */
- regs->sp = (unsigned long) frame;
- regs->ip = (unsigned long) ka->sa.sa_handler;
-
- /* We use the x32 calling convention here... */
- regs->di = sig;
- regs->si = (unsigned long) &frame->info;
- regs->dx = (unsigned long) &frame->uc;
-
- loadsegment(ds, __USER_DS);
- loadsegment(es, __USER_DS);
-
- regs->cs = __USER_CS;
- regs->ss = __USER_DS;
-
- return 0;
-}
-
asmlinkage long sys32_x32_rt_sigreturn(struct pt_regs *regs)
{
struct rt_sigframe_x32 __user *frame;
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 7c5a8c314c02..c80a33bc528b 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -665,7 +665,8 @@ static int __cpuinit do_boot_cpu(int apicid, int cpu, struct task_struct *idle)
unsigned long boot_error = 0;
int timeout;
- alternatives_smp_switch(1);
+ /* Just in case we booted with a single CPU. */
+ alternatives_enable_smp();
idle->thread.sp = (unsigned long) (((struct pt_regs *)
(THREAD_SIZE + task_stack_page(idle))) - 1);
@@ -1053,20 +1054,6 @@ out:
preempt_enable();
}
-void arch_disable_nonboot_cpus_begin(void)
-{
- /*
- * Avoid the smp alternatives switch during the disable_nonboot_cpus().
- * In the suspend path, we will be back in the SMP mode shortly anyways.
- */
- skip_smp_alternatives = true;
-}
-
-void arch_disable_nonboot_cpus_end(void)
-{
- skip_smp_alternatives = false;
-}
-
void arch_enable_nonboot_cpus_begin(void)
{
set_mtrr_aps_delayed_init();
@@ -1256,9 +1243,6 @@ void native_cpu_die(unsigned int cpu)
if (per_cpu(cpu_state, cpu) == CPU_DEAD) {
if (system_state == SYSTEM_RUNNING)
pr_info("CPU %u is now offline\n", cpu);
-
- if (1 == num_online_cpus())
- alternatives_smp_switch(0);
return;
}
msleep(100);
diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c
index c346d1161488..cd3b2438a980 100644
--- a/arch/x86/kernel/step.c
+++ b/arch/x86/kernel/step.c
@@ -157,6 +157,33 @@ static int enable_single_step(struct task_struct *child)
return 1;
}
+void set_task_blockstep(struct task_struct *task, bool on)
+{
+ unsigned long debugctl;
+
+ /*
+ * Ensure irq/preemption can't change debugctl in between.
+ * Note also that both TIF_BLOCKSTEP and debugctl should
+ * be changed atomically wrt preemption.
+ * FIXME: this means that set/clear TIF_BLOCKSTEP is simply
+ * wrong if task != current, SIGKILL can wakeup the stopped
+ * tracee and set/clear can play with the running task, this
+ * can confuse the next __switch_to_xtra().
+ */
+ local_irq_disable();
+ debugctl = get_debugctlmsr();
+ if (on) {
+ debugctl |= DEBUGCTLMSR_BTF;
+ set_tsk_thread_flag(task, TIF_BLOCKSTEP);
+ } else {
+ debugctl &= ~DEBUGCTLMSR_BTF;
+ clear_tsk_thread_flag(task, TIF_BLOCKSTEP);
+ }
+ if (task == current)
+ update_debugctlmsr(debugctl);
+ local_irq_enable();
+}
+
/*
* Enable single or block step.
*/
@@ -169,19 +196,10 @@ static void enable_step(struct task_struct *child, bool block)
* So no one should try to use debugger block stepping in a program
* that uses user-mode single stepping itself.
*/
- if (enable_single_step(child) && block) {
- unsigned long debugctl = get_debugctlmsr();
-
- debugctl |= DEBUGCTLMSR_BTF;
- update_debugctlmsr(debugctl);
- set_tsk_thread_flag(child, TIF_BLOCKSTEP);
- } else if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) {
- unsigned long debugctl = get_debugctlmsr();
-
- debugctl &= ~DEBUGCTLMSR_BTF;
- update_debugctlmsr(debugctl);
- clear_tsk_thread_flag(child, TIF_BLOCKSTEP);
- }
+ if (enable_single_step(child) && block)
+ set_task_blockstep(child, true);
+ else if (test_tsk_thread_flag(child, TIF_BLOCKSTEP))
+ set_task_blockstep(child, false);
}
void user_enable_single_step(struct task_struct *child)
@@ -199,13 +217,8 @@ void user_disable_single_step(struct task_struct *child)
/*
* Make sure block stepping (BTF) is disabled.
*/
- if (test_tsk_thread_flag(child, TIF_BLOCKSTEP)) {
- unsigned long debugctl = get_debugctlmsr();
-
- debugctl &= ~DEBUGCTLMSR_BTF;
- update_debugctlmsr(debugctl);
- clear_tsk_thread_flag(child, TIF_BLOCKSTEP);
- }
+ if (test_tsk_thread_flag(child, TIF_BLOCKSTEP))
+ set_task_blockstep(child, false);
/* Always clear TIF_SINGLESTEP... */
clear_tsk_thread_flag(child, TIF_SINGLESTEP);
diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
index b481341c9369..8276dc6794cc 100644
--- a/arch/x86/kernel/traps.c
+++ b/arch/x86/kernel/traps.c
@@ -55,6 +55,7 @@
#include <asm/i387.h>
#include <asm/fpu-internal.h>
#include <asm/mce.h>
+#include <asm/rcu.h>
#include <asm/mach_traps.h>
@@ -107,30 +108,45 @@ static inline void preempt_conditional_cli(struct pt_regs *regs)
dec_preempt_count();
}
-static void __kprobes
-do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
- long error_code, siginfo_t *info)
+static int __kprobes
+do_trap_no_signal(struct task_struct *tsk, int trapnr, char *str,
+ struct pt_regs *regs, long error_code)
{
- struct task_struct *tsk = current;
-
#ifdef CONFIG_X86_32
if (regs->flags & X86_VM_MASK) {
/*
- * traps 0, 1, 3, 4, and 5 should be forwarded to vm86.
+ * Traps 0, 1, 3, 4, and 5 should be forwarded to vm86.
* On nmi (interrupt 2), do_trap should not be called.
*/
- if (trapnr < X86_TRAP_UD)
- goto vm86_trap;
- goto trap_signal;
+ if (trapnr < X86_TRAP_UD) {
+ if (!handle_vm86_trap((struct kernel_vm86_regs *) regs,
+ error_code, trapnr))
+ return 0;
+ }
+ return -1;
}
#endif
+ if (!user_mode(regs)) {
+ if (!fixup_exception(regs)) {
+ tsk->thread.error_code = error_code;
+ tsk->thread.trap_nr = trapnr;
+ die(str, regs, error_code);
+ }
+ return 0;
+ }
- if (!user_mode(regs))
- goto kernel_trap;
+ return -1;
+}
-#ifdef CONFIG_X86_32
-trap_signal:
-#endif
+static void __kprobes
+do_trap(int trapnr, int signr, char *str, struct pt_regs *regs,
+ long error_code, siginfo_t *info)
+{
+ struct task_struct *tsk = current;
+
+
+ if (!do_trap_no_signal(tsk, trapnr, str, regs, error_code))
+ return;
/*
* We want error_code and trap_nr set for userspace faults and
* kernelspace faults which result in die(), but not
@@ -158,33 +174,20 @@ trap_signal:
force_sig_info(signr, info, tsk);
else
force_sig(signr, tsk);
- return;
-
-kernel_trap:
- if (!fixup_exception(regs)) {
- tsk->thread.error_code = error_code;
- tsk->thread.trap_nr = trapnr;
- die(str, regs, error_code);
- }
- return;
-
-#ifdef CONFIG_X86_32
-vm86_trap:
- if (handle_vm86_trap((struct kernel_vm86_regs *) regs,
- error_code, trapnr))
- goto trap_signal;
- return;
-#endif
}
#define DO_ERROR(trapnr, signr, str, name) \
dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
{ \
- if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
- == NOTIFY_STOP) \
+ exception_enter(regs); \
+ if (notify_die(DIE_TRAP, str, regs, error_code, \
+ trapnr, signr) == NOTIFY_STOP) { \
+ exception_exit(regs); \
return; \
+ } \
conditional_sti(regs); \
do_trap(trapnr, signr, str, regs, error_code, NULL); \
+ exception_exit(regs); \
}
#define DO_ERROR_INFO(trapnr, signr, str, name, sicode, siaddr) \
@@ -195,11 +198,15 @@ dotraplinkage void do_##name(struct pt_regs *regs, long error_code) \
info.si_errno = 0; \
info.si_code = sicode; \
info.si_addr = (void __user *)siaddr; \
- if (notify_die(DIE_TRAP, str, regs, error_code, trapnr, signr) \
- == NOTIFY_STOP) \
+ exception_enter(regs); \
+ if (notify_die(DIE_TRAP, str, regs, error_code, \
+ trapnr, signr) == NOTIFY_STOP) { \
+ exception_exit(regs); \
return; \
+ } \
conditional_sti(regs); \
do_trap(trapnr, signr, str, regs, error_code, &info); \
+ exception_exit(regs); \
}
DO_ERROR_INFO(X86_TRAP_DE, SIGFPE, "divide error", divide_error, FPE_INTDIV,
@@ -222,12 +229,14 @@ DO_ERROR_INFO(X86_TRAP_AC, SIGBUS, "alignment check", alignment_check,
/* Runs on IST stack */
dotraplinkage void do_stack_segment(struct pt_regs *regs, long error_code)
{
+ exception_enter(regs);
if (notify_die(DIE_TRAP, "stack segment", regs, error_code,
- X86_TRAP_SS, SIGBUS) == NOTIFY_STOP)
- return;
- preempt_conditional_sti(regs);
- do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL);
- preempt_conditional_cli(regs);
+ X86_TRAP_SS, SIGBUS) != NOTIFY_STOP) {
+ preempt_conditional_sti(regs);
+ do_trap(X86_TRAP_SS, SIGBUS, "stack segment", regs, error_code, NULL);
+ preempt_conditional_cli(regs);
+ }
+ exception_exit(regs);
}
dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
@@ -235,6 +244,7 @@ dotraplinkage void do_double_fault(struct pt_regs *regs, long error_code)
static const char str[] = "double fault";
struct task_struct *tsk = current;
+ exception_enter(regs);
/* Return not checked because double check cannot be ignored */
notify_die(DIE_TRAP, str, regs, error_code, X86_TRAP_DF, SIGSEGV);
@@ -255,16 +265,29 @@ do_general_protection(struct pt_regs *regs, long error_code)
{
struct task_struct *tsk;
+ exception_enter(regs);
conditional_sti(regs);
#ifdef CONFIG_X86_32
- if (regs->flags & X86_VM_MASK)
- goto gp_in_vm86;
+ if (regs->flags & X86_VM_MASK) {
+ local_irq_enable();
+ handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
+ goto exit;
+ }
#endif
tsk = current;
- if (!user_mode(regs))
- goto gp_in_kernel;
+ if (!user_mode(regs)) {
+ if (fixup_exception(regs))
+ goto exit;
+
+ tsk->thread.error_code = error_code;
+ tsk->thread.trap_nr = X86_TRAP_GP;
+ if (notify_die(DIE_GPF, "general protection fault", regs, error_code,
+ X86_TRAP_GP, SIGSEGV) != NOTIFY_STOP)
+ die("general protection fault", regs, error_code);
+ goto exit;
+ }
tsk->thread.error_code = error_code;
tsk->thread.trap_nr = X86_TRAP_GP;
@@ -279,25 +302,8 @@ do_general_protection(struct pt_regs *regs, long error_code)
}
force_sig(SIGSEGV, tsk);
- return;
-
-#ifdef CONFIG_X86_32
-gp_in_vm86:
- local_irq_enable();
- handle_vm86_fault((struct kernel_vm86_regs *) regs, error_code);
- return;
-#endif
-
-gp_in_kernel:
- if (fixup_exception(regs))
- return;
-
- tsk->thread.error_code = error_code;
- tsk->thread.trap_nr = X86_TRAP_GP;
- if (notify_die(DIE_GPF, "general protection fault", regs, error_code,
- X86_TRAP_GP, SIGSEGV) == NOTIFY_STOP)
- return;
- die("general protection fault", regs, error_code);
+exit:
+ exception_exit(regs);
}
/* May run on IST stack. */
@@ -312,15 +318,16 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
ftrace_int3_handler(regs))
return;
#endif
+ exception_enter(regs);
#ifdef CONFIG_KGDB_LOW_LEVEL_TRAP
if (kgdb_ll_trap(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
SIGTRAP) == NOTIFY_STOP)
- return;
+ goto exit;
#endif /* CONFIG_KGDB_LOW_LEVEL_TRAP */
if (notify_die(DIE_INT3, "int3", regs, error_code, X86_TRAP_BP,
SIGTRAP) == NOTIFY_STOP)
- return;
+ goto exit;
/*
* Let others (NMI) know that the debug stack is in use
@@ -331,6 +338,8 @@ dotraplinkage void __kprobes notrace do_int3(struct pt_regs *regs, long error_co
do_trap(X86_TRAP_BP, SIGTRAP, "int3", regs, error_code, NULL);
preempt_conditional_cli(regs);
debug_stack_usage_dec();
+exit:
+ exception_exit(regs);
}
#ifdef CONFIG_X86_64
@@ -391,6 +400,8 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
unsigned long dr6;
int si_code;
+ exception_enter(regs);
+
get_debugreg(dr6, 6);
/* Filter out all the reserved bits which are preset to 1 */
@@ -406,7 +417,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
/* Catch kmemcheck conditions first of all! */
if ((dr6 & DR_STEP) && kmemcheck_trap(regs))
- return;
+ goto exit;
/* DR6 may or may not be cleared by the CPU */
set_debugreg(0, 6);
@@ -421,7 +432,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
if (notify_die(DIE_DEBUG, "debug", regs, PTR_ERR(&dr6), error_code,
SIGTRAP) == NOTIFY_STOP)
- return;
+ goto exit;
/*
* Let others (NMI) know that the debug stack is in use
@@ -437,7 +448,7 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
X86_TRAP_DB);
preempt_conditional_cli(regs);
debug_stack_usage_dec();
- return;
+ goto exit;
}
/*
@@ -458,7 +469,8 @@ dotraplinkage void __kprobes do_debug(struct pt_regs *regs, long error_code)
preempt_conditional_cli(regs);
debug_stack_usage_dec();
- return;
+exit:
+ exception_exit(regs);
}
/*
@@ -555,14 +567,17 @@ dotraplinkage void do_coprocessor_error(struct pt_regs *regs, long error_code)
#ifdef CONFIG_X86_32
ignore_fpu_irq = 1;
#endif
-
+ exception_enter(regs);
math_error(regs, error_code, X86_TRAP_MF);
+ exception_exit(regs);
}
dotraplinkage void
do_simd_coprocessor_error(struct pt_regs *regs, long error_code)
{
+ exception_enter(regs);
math_error(regs, error_code, X86_TRAP_XF);
+ exception_exit(regs);
}
dotraplinkage void
@@ -613,11 +628,12 @@ void math_state_restore(void)
}
__thread_fpu_begin(tsk);
+
/*
* Paranoid restore. send a SIGSEGV if we fail to restore the state.
*/
if (unlikely(restore_fpu_checking(tsk))) {
- __thread_fpu_end(tsk);
+ drop_init_fpu(tsk);
force_sig(SIGSEGV, tsk);
return;
}
@@ -629,6 +645,9 @@ EXPORT_SYMBOL_GPL(math_state_restore);
dotraplinkage void __kprobes
do_device_not_available(struct pt_regs *regs, long error_code)
{
+ exception_enter(regs);
+ BUG_ON(use_eager_fpu());
+
#ifdef CONFIG_MATH_EMULATION
if (read_cr0() & X86_CR0_EM) {
struct math_emu_info info = { };
@@ -637,6 +656,7 @@ do_device_not_available(struct pt_regs *regs, long error_code)
info.regs = regs;
math_emulate(&info);
+ exception_exit(regs);
return;
}
#endif
@@ -644,12 +664,15 @@ do_device_not_available(struct pt_regs *regs, long error_code)
#ifdef CONFIG_X86_32
conditional_sti(regs);
#endif
+ exception_exit(regs);
}
#ifdef CONFIG_X86_32
dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
{
siginfo_t info;
+
+ exception_enter(regs);
local_irq_enable();
info.si_signo = SIGILL;
@@ -657,10 +680,11 @@ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
info.si_code = ILL_BADSTK;
info.si_addr = NULL;
if (notify_die(DIE_TRAP, "iret exception", regs, error_code,
- X86_TRAP_IRET, SIGILL) == NOTIFY_STOP)
- return;
- do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code,
- &info);
+ X86_TRAP_IRET, SIGILL) != NOTIFY_STOP) {
+ do_trap(X86_TRAP_IRET, SIGILL, "iret exception", regs, error_code,
+ &info);
+ }
+ exception_exit(regs);
}
#endif
diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index 36fd42091fa7..9538f00827a9 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -41,6 +41,9 @@
/* Adjust the return address of a call insn */
#define UPROBE_FIX_CALL 0x2
+/* Instruction will modify TF, don't change it */
+#define UPROBE_FIX_SETF 0x4
+
#define UPROBE_FIX_RIP_AX 0x8000
#define UPROBE_FIX_RIP_CX 0x4000
@@ -239,6 +242,10 @@ static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn)
insn_get_opcode(insn); /* should be a nop */
switch (OPCODE1(insn)) {
+ case 0x9d:
+ /* popf */
+ auprobe->fixups |= UPROBE_FIX_SETF;
+ break;
case 0xc3: /* ret/lret */
case 0xcb:
case 0xc2:
@@ -646,7 +653,7 @@ void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
* Skip these instructions as per the currently known x86 ISA.
* 0x66* { 0x90 | 0x0f 0x1f | 0x0f 0x19 | 0x87 0xc0 }
*/
-bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
+static bool __skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
{
int i;
@@ -673,3 +680,46 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
}
return false;
}
+
+bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
+{
+ bool ret = __skip_sstep(auprobe, regs);
+ if (ret && (regs->flags & X86_EFLAGS_TF))
+ send_sig(SIGTRAP, current, 0);
+ return ret;
+}
+
+void arch_uprobe_enable_step(struct arch_uprobe *auprobe)
+{
+ struct task_struct *task = current;
+ struct arch_uprobe_task *autask = &task->utask->autask;
+ struct pt_regs *regs = task_pt_regs(task);
+
+ autask->saved_tf = !!(regs->flags & X86_EFLAGS_TF);
+
+ regs->flags |= X86_EFLAGS_TF;
+ if (test_tsk_thread_flag(task, TIF_BLOCKSTEP))
+ set_task_blockstep(task, false);
+}
+
+void arch_uprobe_disable_step(struct arch_uprobe *auprobe)
+{
+ struct task_struct *task = current;
+ struct arch_uprobe_task *autask = &task->utask->autask;
+ bool trapped = (task->utask->state == UTASK_SSTEP_TRAPPED);
+ struct pt_regs *regs = task_pt_regs(task);
+ /*
+ * The state of TIF_BLOCKSTEP was not saved so we can get an extra
+ * SIGTRAP if we do not clear TF. We need to examine the opcode to
+ * make it right.
+ */
+ if (unlikely(trapped)) {
+ if (!autask->saved_tf)
+ regs->flags &= ~X86_EFLAGS_TF;
+ } else {
+ if (autask->saved_tf)
+ send_sig(SIGTRAP, task, 0);
+ else if (!(auprobe->fixups & UPROBE_FIX_SETF))
+ regs->flags &= ~X86_EFLAGS_TF;
+ }
+}
diff --git a/arch/x86/kernel/x8664_ksyms_64.c b/arch/x86/kernel/x8664_ksyms_64.c
index 6020f6f5927c..1330dd102950 100644
--- a/arch/x86/kernel/x8664_ksyms_64.c
+++ b/arch/x86/kernel/x8664_ksyms_64.c
@@ -13,9 +13,13 @@
#include <asm/ftrace.h>
#ifdef CONFIG_FUNCTION_TRACER
-/* mcount is defined in assembly */
+/* mcount and __fentry__ are defined in assembly */
+#ifdef CC_USING_FENTRY
+EXPORT_SYMBOL(__fentry__);
+#else
EXPORT_SYMBOL(mcount);
#endif
+#endif
EXPORT_SYMBOL(__get_user_1);
EXPORT_SYMBOL(__get_user_2);
diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c
index 9f3167e891ef..7a3d075a814a 100644
--- a/arch/x86/kernel/x86_init.c
+++ b/arch/x86/kernel/x86_init.c
@@ -26,7 +26,6 @@
void __cpuinit x86_init_noop(void) { }
void __init x86_init_uint_noop(unsigned int unused) { }
-void __init x86_init_pgd_noop(pgd_t *unused) { }
int __init iommu_init_noop(void) { return 0; }
void iommu_shutdown_noop(void) { }
@@ -68,8 +67,7 @@ struct x86_init_ops x86_init __initdata = {
},
.paging = {
- .pagetable_setup_start = native_pagetable_setup_start,
- .pagetable_setup_done = native_pagetable_setup_done,
+ .pagetable_init = native_pagetable_init,
},
.timers = {
diff --git a/arch/x86/kernel/xsave.c b/arch/x86/kernel/xsave.c
index 3d3e20709119..ada87a329edc 100644
--- a/arch/x86/kernel/xsave.c
+++ b/arch/x86/kernel/xsave.c
@@ -10,9 +10,7 @@
#include <linux/compat.h>
#include <asm/i387.h>
#include <asm/fpu-internal.h>
-#ifdef CONFIG_IA32_EMULATION
-#include <asm/sigcontext32.h>
-#endif
+#include <asm/sigframe.h>
#include <asm/xcr.h>
/*
@@ -23,13 +21,9 @@ u64 pcntxt_mask;
/*
* Represents init state for the supported extended state.
*/
-static struct xsave_struct *init_xstate_buf;
-
-struct _fpx_sw_bytes fx_sw_reserved;
-#ifdef CONFIG_IA32_EMULATION
-struct _fpx_sw_bytes fx_sw_reserved_ia32;
-#endif
+struct xsave_struct *init_xstate_buf;
+static struct _fpx_sw_bytes fx_sw_reserved, fx_sw_reserved_ia32;
static unsigned int *xstate_offsets, *xstate_sizes, xstate_features;
/*
@@ -44,9 +38,9 @@ static unsigned int *xstate_offsets, *xstate_sizes, xstate_features;
*/
void __sanitize_i387_state(struct task_struct *tsk)
{
- u64 xstate_bv;
- int feature_bit = 0x2;
struct i387_fxsave_struct *fx = &tsk->thread.fpu.state->fxsave;
+ int feature_bit = 0x2;
+ u64 xstate_bv;
if (!fx)
return;
@@ -104,213 +98,326 @@ void __sanitize_i387_state(struct task_struct *tsk)
* Check for the presence of extended state information in the
* user fpstate pointer in the sigcontext.
*/
-int check_for_xstate(struct i387_fxsave_struct __user *buf,
- void __user *fpstate,
- struct _fpx_sw_bytes *fx_sw_user)
+static inline int check_for_xstate(struct i387_fxsave_struct __user *buf,
+ void __user *fpstate,
+ struct _fpx_sw_bytes *fx_sw)
{
int min_xstate_size = sizeof(struct i387_fxsave_struct) +
sizeof(struct xsave_hdr_struct);
unsigned int magic2;
- int err;
- err = __copy_from_user(fx_sw_user, &buf->sw_reserved[0],
- sizeof(struct _fpx_sw_bytes));
- if (err)
- return -EFAULT;
+ if (__copy_from_user(fx_sw, &buf->sw_reserved[0], sizeof(*fx_sw)))
+ return -1;
- /*
- * First Magic check failed.
- */
- if (fx_sw_user->magic1 != FP_XSTATE_MAGIC1)
- return -EINVAL;
+ /* Check for the first magic field and other error scenarios. */
+ if (fx_sw->magic1 != FP_XSTATE_MAGIC1 ||
+ fx_sw->xstate_size < min_xstate_size ||
+ fx_sw->xstate_size > xstate_size ||
+ fx_sw->xstate_size > fx_sw->extended_size)
+ return -1;
/*
- * Check for error scenarios.
- */
- if (fx_sw_user->xstate_size < min_xstate_size ||
- fx_sw_user->xstate_size > xstate_size ||
- fx_sw_user->xstate_size > fx_sw_user->extended_size)
- return -EINVAL;
-
- err = __get_user(magic2, (__u32 *) (((void *)fpstate) +
- fx_sw_user->extended_size -
- FP_XSTATE_MAGIC2_SIZE));
- if (err)
- return err;
- /*
* Check for the presence of second magic word at the end of memory
* layout. This detects the case where the user just copied the legacy
* fpstate layout with out copying the extended state information
* in the memory layout.
*/
- if (magic2 != FP_XSTATE_MAGIC2)
- return -EFAULT;
+ if (__get_user(magic2, (__u32 __user *)(fpstate + fx_sw->xstate_size))
+ || magic2 != FP_XSTATE_MAGIC2)
+ return -1;
return 0;
}
-#ifdef CONFIG_X86_64
/*
* Signal frame handlers.
*/
-
-int save_i387_xstate(void __user *buf)
+static inline int save_fsave_header(struct task_struct *tsk, void __user *buf)
{
- struct task_struct *tsk = current;
- int err = 0;
-
- if (!access_ok(VERIFY_WRITE, buf, sig_xstate_size))
- return -EACCES;
+ if (use_fxsr()) {
+ struct xsave_struct *xsave = &tsk->thread.fpu.state->xsave;
+ struct user_i387_ia32_struct env;
+ struct _fpstate_ia32 __user *fp = buf;
- BUG_ON(sig_xstate_size < xstate_size);
+ convert_from_fxsr(&env, tsk);
- if ((unsigned long)buf % 64)
- pr_err("%s: bad fpstate %p\n", __func__, buf);
-
- if (!used_math())
- return 0;
-
- if (user_has_fpu()) {
- if (use_xsave())
- err = xsave_user(buf);
- else
- err = fxsave_user(buf);
-
- if (err)
- return err;
- user_fpu_end();
+ if (__copy_to_user(buf, &env, sizeof(env)) ||
+ __put_user(xsave->i387.swd, &fp->status) ||
+ __put_user(X86_FXSR_MAGIC, &fp->magic))
+ return -1;
} else {
- sanitize_i387_state(tsk);
- if (__copy_to_user(buf, &tsk->thread.fpu.state->fxsave,
- xstate_size))
+ struct i387_fsave_struct __user *fp = buf;
+ u32 swd;
+ if (__get_user(swd, &fp->swd) || __put_user(swd, &fp->status))
return -1;
}
- clear_used_math(); /* trigger finit */
+ return 0;
+}
- if (use_xsave()) {
- struct _fpstate __user *fx = buf;
- struct _xstate __user *x = buf;
- u64 xstate_bv;
+static inline int save_xstate_epilog(void __user *buf, int ia32_frame)
+{
+ struct xsave_struct __user *x = buf;
+ struct _fpx_sw_bytes *sw_bytes;
+ u32 xstate_bv;
+ int err;
- err = __copy_to_user(&fx->sw_reserved, &fx_sw_reserved,
- sizeof(struct _fpx_sw_bytes));
+ /* Setup the bytes not touched by the [f]xsave and reserved for SW. */
+ sw_bytes = ia32_frame ? &fx_sw_reserved_ia32 : &fx_sw_reserved;
+ err = __copy_to_user(&x->i387.sw_reserved, sw_bytes, sizeof(*sw_bytes));
- err |= __put_user(FP_XSTATE_MAGIC2,
- (__u32 __user *) (buf + sig_xstate_size
- - FP_XSTATE_MAGIC2_SIZE));
+ if (!use_xsave())
+ return err;
- /*
- * Read the xstate_bv which we copied (directly from the cpu or
- * from the state in task struct) to the user buffers and
- * set the FP/SSE bits.
- */
- err |= __get_user(xstate_bv, &x->xstate_hdr.xstate_bv);
+ err |= __put_user(FP_XSTATE_MAGIC2, (__u32 *)(buf + xstate_size));
- /*
- * For legacy compatible, we always set FP/SSE bits in the bit
- * vector while saving the state to the user context. This will
- * enable us capturing any changes(during sigreturn) to
- * the FP/SSE bits by the legacy applications which don't touch
- * xstate_bv in the xsave header.
- *
- * xsave aware apps can change the xstate_bv in the xsave
- * header as well as change any contents in the memory layout.
- * xrestore as part of sigreturn will capture all the changes.
- */
- xstate_bv |= XSTATE_FPSSE;
+ /*
+ * Read the xstate_bv which we copied (directly from the cpu or
+ * from the state in task struct) to the user buffers.
+ */
+ err |= __get_user(xstate_bv, (__u32 *)&x->xsave_hdr.xstate_bv);
- err |= __put_user(xstate_bv, &x->xstate_hdr.xstate_bv);
+ /*
+ * For legacy compatible, we always set FP/SSE bits in the bit
+ * vector while saving the state to the user context. This will
+ * enable us capturing any changes(during sigreturn) to
+ * the FP/SSE bits by the legacy applications which don't touch
+ * xstate_bv in the xsave header.
+ *
+ * xsave aware apps can change the xstate_bv in the xsave
+ * header as well as change any contents in the memory layout.
+ * xrestore as part of sigreturn will capture all the changes.
+ */
+ xstate_bv |= XSTATE_FPSSE;
- if (err)
- return err;
- }
+ err |= __put_user(xstate_bv, (__u32 *)&x->xsave_hdr.xstate_bv);
- return 1;
+ return err;
+}
+
+static inline int save_user_xstate(struct xsave_struct __user *buf)
+{
+ int err;
+
+ if (use_xsave())
+ err = xsave_user(buf);
+ else if (use_fxsr())
+ err = fxsave_user((struct i387_fxsave_struct __user *) buf);
+ else
+ err = fsave_user((struct i387_fsave_struct __user *) buf);
+
+ if (unlikely(err) && __clear_user(buf, xstate_size))
+ err = -EFAULT;
+ return err;
}
/*
- * Restore the extended state if present. Otherwise, restore the FP/SSE
- * state.
+ * Save the fpu, extended register state to the user signal frame.
+ *
+ * 'buf_fx' is the 64-byte aligned pointer at which the [f|fx|x]save
+ * state is copied.
+ * 'buf' points to the 'buf_fx' or to the fsave header followed by 'buf_fx'.
+ *
+ * buf == buf_fx for 64-bit frames and 32-bit fsave frame.
+ * buf != buf_fx for 32-bit frames with fxstate.
+ *
+ * If the fpu, extended register state is live, save the state directly
+ * to the user frame pointed by the aligned pointer 'buf_fx'. Otherwise,
+ * copy the thread's fpu state to the user frame starting at 'buf_fx'.
+ *
+ * If this is a 32-bit frame with fxstate, put a fsave header before
+ * the aligned state at 'buf_fx'.
+ *
+ * For [f]xsave state, update the SW reserved fields in the [f]xsave frame
+ * indicating the absence/presence of the extended state to the user.
*/
-static int restore_user_xstate(void __user *buf)
+int save_xstate_sig(void __user *buf, void __user *buf_fx, int size)
{
- struct _fpx_sw_bytes fx_sw_user;
- u64 mask;
- int err;
+ struct xsave_struct *xsave = &current->thread.fpu.state->xsave;
+ struct task_struct *tsk = current;
+ int ia32_fxstate = (buf != buf_fx);
- if (((unsigned long)buf % 64) ||
- check_for_xstate(buf, buf, &fx_sw_user))
- goto fx_only;
+ ia32_fxstate &= (config_enabled(CONFIG_X86_32) ||
+ config_enabled(CONFIG_IA32_EMULATION));
- mask = fx_sw_user.xstate_bv;
+ if (!access_ok(VERIFY_WRITE, buf, size))
+ return -EACCES;
- /*
- * restore the state passed by the user.
- */
- err = xrestore_user(buf, mask);
- if (err)
- return err;
+ if (!HAVE_HWFP)
+ return fpregs_soft_get(current, NULL, 0,
+ sizeof(struct user_i387_ia32_struct), NULL,
+ (struct _fpstate_ia32 __user *) buf) ? -1 : 1;
- /*
- * init the state skipped by the user.
- */
- mask = pcntxt_mask & ~mask;
- if (unlikely(mask))
- xrstor_state(init_xstate_buf, mask);
+ if (user_has_fpu()) {
+ /* Save the live register state to the user directly. */
+ if (save_user_xstate(buf_fx))
+ return -1;
+ /* Update the thread's fxstate to save the fsave header. */
+ if (ia32_fxstate)
+ fpu_fxsave(&tsk->thread.fpu);
+ } else {
+ sanitize_i387_state(tsk);
+ if (__copy_to_user(buf_fx, xsave, xstate_size))
+ return -1;
+ }
+
+ /* Save the fsave header for the 32-bit frames. */
+ if ((ia32_fxstate || !use_fxsr()) && save_fsave_header(tsk, buf))
+ return -1;
+
+ if (use_fxsr() && save_xstate_epilog(buf_fx, ia32_fxstate))
+ return -1;
+
+ drop_init_fpu(tsk); /* trigger finit */
return 0;
+}
-fx_only:
- /*
- * couldn't find the extended state information in the
- * memory layout. Restore just the FP/SSE and init all
- * the other extended state.
- */
- xrstor_state(init_xstate_buf, pcntxt_mask & ~XSTATE_FPSSE);
- return fxrstor_checking((__force struct i387_fxsave_struct *)buf);
+static inline void
+sanitize_restored_xstate(struct task_struct *tsk,
+ struct user_i387_ia32_struct *ia32_env,
+ u64 xstate_bv, int fx_only)
+{
+ struct xsave_struct *xsave = &tsk->thread.fpu.state->xsave;
+ struct xsave_hdr_struct *xsave_hdr = &xsave->xsave_hdr;
+
+ if (use_xsave()) {
+ /* These bits must be zero. */
+ xsave_hdr->reserved1[0] = xsave_hdr->reserved1[1] = 0;
+
+ /*
+ * Init the state that is not present in the memory
+ * layout and not enabled by the OS.
+ */
+ if (fx_only)
+ xsave_hdr->xstate_bv = XSTATE_FPSSE;
+ else
+ xsave_hdr->xstate_bv &= (pcntxt_mask & xstate_bv);
+ }
+
+ if (use_fxsr()) {
+ /*
+ * mscsr reserved bits must be masked to zero for security
+ * reasons.
+ */
+ xsave->i387.mxcsr &= mxcsr_feature_mask;
+
+ convert_to_fxsr(tsk, ia32_env);
+ }
}
/*
- * This restores directly out of user space. Exceptions are handled.
+ * Restore the extended state if present. Otherwise, restore the FP/SSE state.
*/
-int restore_i387_xstate(void __user *buf)
+static inline int restore_user_xstate(void __user *buf, u64 xbv, int fx_only)
{
+ if (use_xsave()) {
+ if ((unsigned long)buf % 64 || fx_only) {
+ u64 init_bv = pcntxt_mask & ~XSTATE_FPSSE;
+ xrstor_state(init_xstate_buf, init_bv);
+ return fxrstor_user(buf);
+ } else {
+ u64 init_bv = pcntxt_mask & ~xbv;
+ if (unlikely(init_bv))
+ xrstor_state(init_xstate_buf, init_bv);
+ return xrestore_user(buf, xbv);
+ }
+ } else if (use_fxsr()) {
+ return fxrstor_user(buf);
+ } else
+ return frstor_user(buf);
+}
+
+int __restore_xstate_sig(void __user *buf, void __user *buf_fx, int size)
+{
+ int ia32_fxstate = (buf != buf_fx);
struct task_struct *tsk = current;
- int err = 0;
+ int state_size = xstate_size;
+ u64 xstate_bv = 0;
+ int fx_only = 0;
+
+ ia32_fxstate &= (config_enabled(CONFIG_X86_32) ||
+ config_enabled(CONFIG_IA32_EMULATION));
if (!buf) {
- if (used_math())
- goto clear;
+ drop_init_fpu(tsk);
return 0;
- } else
- if (!access_ok(VERIFY_READ, buf, sig_xstate_size))
- return -EACCES;
+ }
- if (!used_math()) {
- err = init_fpu(tsk);
- if (err)
- return err;
+ if (!access_ok(VERIFY_READ, buf, size))
+ return -EACCES;
+
+ if (!used_math() && init_fpu(tsk))
+ return -1;
+
+ if (!HAVE_HWFP) {
+ return fpregs_soft_set(current, NULL,
+ 0, sizeof(struct user_i387_ia32_struct),
+ NULL, buf) != 0;
}
- user_fpu_begin();
- if (use_xsave())
- err = restore_user_xstate(buf);
- else
- err = fxrstor_checking((__force struct i387_fxsave_struct *)
- buf);
- if (unlikely(err)) {
+ if (use_xsave()) {
+ struct _fpx_sw_bytes fx_sw_user;
+ if (unlikely(check_for_xstate(buf_fx, buf_fx, &fx_sw_user))) {
+ /*
+ * Couldn't find the extended state information in the
+ * memory layout. Restore just the FP/SSE and init all
+ * the other extended state.
+ */
+ state_size = sizeof(struct i387_fxsave_struct);
+ fx_only = 1;
+ } else {
+ state_size = fx_sw_user.xstate_size;
+ xstate_bv = fx_sw_user.xstate_bv;
+ }
+ }
+
+ if (ia32_fxstate) {
+ /*
+ * For 32-bit frames with fxstate, copy the user state to the
+ * thread's fpu state, reconstruct fxstate from the fsave
+ * header. Sanitize the copied state etc.
+ */
+ struct xsave_struct *xsave = &tsk->thread.fpu.state->xsave;
+ struct user_i387_ia32_struct env;
+ int err = 0;
+
+ /*
+ * Drop the current fpu which clears used_math(). This ensures
+ * that any context-switch during the copy of the new state,
+ * avoids the intermediate state from getting restored/saved.
+ * Thus avoiding the new restored state from getting corrupted.
+ * We will be ready to restore/save the state only after
+ * set_used_math() is again set.
+ */
+ drop_fpu(tsk);
+
+ if (__copy_from_user(xsave, buf_fx, state_size) ||
+ __copy_from_user(&env, buf, sizeof(env))) {
+ err = -1;
+ } else {
+ sanitize_restored_xstate(tsk, &env, xstate_bv, fx_only);
+ set_used_math();
+ }
+
+ if (use_eager_fpu())
+ math_state_restore();
+
+ return err;
+ } else {
/*
- * Encountered an error while doing the restore from the
- * user buffer, clear the fpu state.
+ * For 64-bit frames and 32-bit fsave frames, restore the user
+ * state to the registers directly (with exceptions handled).
*/
-clear:
- clear_fpu(tsk);
- clear_used_math();
+ user_fpu_begin();
+ if (restore_user_xstate(buf_fx, xstate_bv, fx_only)) {
+ drop_init_fpu(tsk);
+ return -1;
+ }
}
- return err;
+
+ return 0;
}
-#endif
/*
* Prepare the SW reserved portion of the fxsave memory layout, indicating
@@ -321,31 +428,22 @@ clear:
*/
static void prepare_fx_sw_frame(void)
{
- int size_extended = (xstate_size - sizeof(struct i387_fxsave_struct)) +
- FP_XSTATE_MAGIC2_SIZE;
+ int fsave_header_size = sizeof(struct i387_fsave_struct);
+ int size = xstate_size + FP_XSTATE_MAGIC2_SIZE;
- sig_xstate_size = sizeof(struct _fpstate) + size_extended;
-
-#ifdef CONFIG_IA32_EMULATION
- sig_xstate_ia32_size = sizeof(struct _fpstate_ia32) + size_extended;
-#endif
-
- memset(&fx_sw_reserved, 0, sizeof(fx_sw_reserved));
+ if (config_enabled(CONFIG_X86_32))
+ size += fsave_header_size;
fx_sw_reserved.magic1 = FP_XSTATE_MAGIC1;
- fx_sw_reserved.extended_size = sig_xstate_size;
+ fx_sw_reserved.extended_size = size;
fx_sw_reserved.xstate_bv = pcntxt_mask;
fx_sw_reserved.xstate_size = xstate_size;
-#ifdef CONFIG_IA32_EMULATION
- memcpy(&fx_sw_reserved_ia32, &fx_sw_reserved,
- sizeof(struct _fpx_sw_bytes));
- fx_sw_reserved_ia32.extended_size = sig_xstate_ia32_size;
-#endif
-}
-#ifdef CONFIG_X86_64
-unsigned int sig_xstate_size = sizeof(struct _fpstate);
-#endif
+ if (config_enabled(CONFIG_IA32_EMULATION)) {
+ fx_sw_reserved_ia32 = fx_sw_reserved;
+ fx_sw_reserved_ia32.extended_size += fsave_header_size;
+ }
+}
/*
* Enable the extended processor state save/restore feature
@@ -384,19 +482,21 @@ static void __init setup_xstate_features(void)
/*
* setup the xstate image representing the init state
*/
-static void __init setup_xstate_init(void)
+static void __init setup_init_fpu_buf(void)
{
- setup_xstate_features();
-
/*
* Setup init_xstate_buf to represent the init state of
* all the features managed by the xsave
*/
init_xstate_buf = alloc_bootmem_align(xstate_size,
__alignof__(struct xsave_struct));
- init_xstate_buf->i387.mxcsr = MXCSR_DEFAULT;
+ fx_finit(&init_xstate_buf->i387);
+
+ if (!cpu_has_xsave)
+ return;
+
+ setup_xstate_features();
- clts();
/*
* Init all the features state with header_bv being 0x0
*/
@@ -406,9 +506,21 @@ static void __init setup_xstate_init(void)
* of any feature which is not represented by all zero's.
*/
xsave_state(init_xstate_buf, -1);
- stts();
}
+static enum { AUTO, ENABLE, DISABLE } eagerfpu = AUTO;
+static int __init eager_fpu_setup(char *s)
+{
+ if (!strcmp(s, "on"))
+ eagerfpu = ENABLE;
+ else if (!strcmp(s, "off"))
+ eagerfpu = DISABLE;
+ else if (!strcmp(s, "auto"))
+ eagerfpu = AUTO;
+ return 1;
+}
+__setup("eagerfpu=", eager_fpu_setup);
+
/*
* Enable and initialize the xsave feature.
*/
@@ -445,8 +557,11 @@ static void __init xstate_enable_boot_cpu(void)
update_regset_xstate_info(xstate_size, pcntxt_mask);
prepare_fx_sw_frame();
+ setup_init_fpu_buf();
- setup_xstate_init();
+ /* Auto enable eagerfpu for xsaveopt */
+ if (cpu_has_xsaveopt && eagerfpu != DISABLE)
+ eagerfpu = ENABLE;
pr_info("enabled xstate_bv 0x%llx, cntxt size 0x%x\n",
pcntxt_mask, xstate_size);
@@ -471,3 +586,43 @@ void __cpuinit xsave_init(void)
next_func = xstate_enable;
this_func();
}
+
+static inline void __init eager_fpu_init_bp(void)
+{
+ current->thread.fpu.state =
+ alloc_bootmem_align(xstate_size, __alignof__(struct xsave_struct));
+ if (!init_xstate_buf)
+ setup_init_fpu_buf();
+}
+
+void __cpuinit eager_fpu_init(void)
+{
+ static __refdata void (*boot_func)(void) = eager_fpu_init_bp;
+
+ clear_used_math();
+ current_thread_info()->status = 0;
+
+ if (eagerfpu == ENABLE)
+ setup_force_cpu_cap(X86_FEATURE_EAGER_FPU);
+
+ if (!cpu_has_eager_fpu) {
+ stts();
+ return;
+ }
+
+ if (boot_func) {
+ boot_func();
+ boot_func = NULL;
+ }
+
+ /*
+ * This is same as math_state_restore(). But use_xsave() is
+ * not yet patched to use math_state_restore().
+ */
+ init_fpu(current);
+ __thread_fpu_begin(current);
+ if (cpu_has_xsave)
+ xrstor_state(init_xstate_buf, -1);
+ else
+ fxrstor_checking(&init_xstate_buf->i387);
+}
diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h
index a71faf727ff3..bca63f04dccb 100644
--- a/arch/x86/kvm/trace.h
+++ b/arch/x86/kvm/trace.h
@@ -183,95 +183,6 @@ TRACE_EVENT(kvm_apic,
#define KVM_ISA_VMX 1
#define KVM_ISA_SVM 2
-#define VMX_EXIT_REASONS \
- { EXIT_REASON_EXCEPTION_NMI, "EXCEPTION_NMI" }, \
- { EXIT_REASON_EXTERNAL_INTERRUPT, "EXTERNAL_INTERRUPT" }, \
- { EXIT_REASON_TRIPLE_FAULT, "TRIPLE_FAULT" }, \
- { EXIT_REASON_PENDING_INTERRUPT, "PENDING_INTERRUPT" }, \
- { EXIT_REASON_NMI_WINDOW, "NMI_WINDOW" }, \
- { EXIT_REASON_TASK_SWITCH, "TASK_SWITCH" }, \
- { EXIT_REASON_CPUID, "CPUID" }, \
- { EXIT_REASON_HLT, "HLT" }, \
- { EXIT_REASON_INVLPG, "INVLPG" }, \
- { EXIT_REASON_RDPMC, "RDPMC" }, \
- { EXIT_REASON_RDTSC, "RDTSC" }, \
- { EXIT_REASON_VMCALL, "VMCALL" }, \
- { EXIT_REASON_VMCLEAR, "VMCLEAR" }, \
- { EXIT_REASON_VMLAUNCH, "VMLAUNCH" }, \
- { EXIT_REASON_VMPTRLD, "VMPTRLD" }, \
- { EXIT_REASON_VMPTRST, "VMPTRST" }, \
- { EXIT_REASON_VMREAD, "VMREAD" }, \
- { EXIT_REASON_VMRESUME, "VMRESUME" }, \
- { EXIT_REASON_VMWRITE, "VMWRITE" }, \
- { EXIT_REASON_VMOFF, "VMOFF" }, \
- { EXIT_REASON_VMON, "VMON" }, \
- { EXIT_REASON_CR_ACCESS, "CR_ACCESS" }, \
- { EXIT_REASON_DR_ACCESS, "DR_ACCESS" }, \
- { EXIT_REASON_IO_INSTRUCTION, "IO_INSTRUCTION" }, \
- { EXIT_REASON_MSR_READ, "MSR_READ" }, \
- { EXIT_REASON_MSR_WRITE, "MSR_WRITE" }, \
- { EXIT_REASON_MWAIT_INSTRUCTION, "MWAIT_INSTRUCTION" }, \
- { EXIT_REASON_MONITOR_INSTRUCTION, "MONITOR_INSTRUCTION" }, \
- { EXIT_REASON_PAUSE_INSTRUCTION, "PAUSE_INSTRUCTION" }, \
- { EXIT_REASON_MCE_DURING_VMENTRY, "MCE_DURING_VMENTRY" }, \
- { EXIT_REASON_TPR_BELOW_THRESHOLD, "TPR_BELOW_THRESHOLD" }, \
- { EXIT_REASON_APIC_ACCESS, "APIC_ACCESS" }, \
- { EXIT_REASON_EPT_VIOLATION, "EPT_VIOLATION" }, \
- { EXIT_REASON_EPT_MISCONFIG, "EPT_MISCONFIG" }, \
- { EXIT_REASON_WBINVD, "WBINVD" }
-
-#define SVM_EXIT_REASONS \
- { SVM_EXIT_READ_CR0, "read_cr0" }, \
- { SVM_EXIT_READ_CR3, "read_cr3" }, \
- { SVM_EXIT_READ_CR4, "read_cr4" }, \
- { SVM_EXIT_READ_CR8, "read_cr8" }, \
- { SVM_EXIT_WRITE_CR0, "write_cr0" }, \
- { SVM_EXIT_WRITE_CR3, "write_cr3" }, \
- { SVM_EXIT_WRITE_CR4, "write_cr4" }, \
- { SVM_EXIT_WRITE_CR8, "write_cr8" }, \
- { SVM_EXIT_READ_DR0, "read_dr0" }, \
- { SVM_EXIT_READ_DR1, "read_dr1" }, \
- { SVM_EXIT_READ_DR2, "read_dr2" }, \
- { SVM_EXIT_READ_DR3, "read_dr3" }, \
- { SVM_EXIT_WRITE_DR0, "write_dr0" }, \
- { SVM_EXIT_WRITE_DR1, "write_dr1" }, \
- { SVM_EXIT_WRITE_DR2, "write_dr2" }, \
- { SVM_EXIT_WRITE_DR3, "write_dr3" }, \
- { SVM_EXIT_WRITE_DR5, "write_dr5" }, \
- { SVM_EXIT_WRITE_DR7, "write_dr7" }, \
- { SVM_EXIT_EXCP_BASE + DB_VECTOR, "DB excp" }, \
- { SVM_EXIT_EXCP_BASE + BP_VECTOR, "BP excp" }, \
- { SVM_EXIT_EXCP_BASE + UD_VECTOR, "UD excp" }, \
- { SVM_EXIT_EXCP_BASE + PF_VECTOR, "PF excp" }, \
- { SVM_EXIT_EXCP_BASE + NM_VECTOR, "NM excp" }, \
- { SVM_EXIT_EXCP_BASE + MC_VECTOR, "MC excp" }, \
- { SVM_EXIT_INTR, "interrupt" }, \
- { SVM_EXIT_NMI, "nmi" }, \
- { SVM_EXIT_SMI, "smi" }, \
- { SVM_EXIT_INIT, "init" }, \
- { SVM_EXIT_VINTR, "vintr" }, \
- { SVM_EXIT_CPUID, "cpuid" }, \
- { SVM_EXIT_INVD, "invd" }, \
- { SVM_EXIT_HLT, "hlt" }, \
- { SVM_EXIT_INVLPG, "invlpg" }, \
- { SVM_EXIT_INVLPGA, "invlpga" }, \
- { SVM_EXIT_IOIO, "io" }, \
- { SVM_EXIT_MSR, "msr" }, \
- { SVM_EXIT_TASK_SWITCH, "task_switch" }, \
- { SVM_EXIT_SHUTDOWN, "shutdown" }, \
- { SVM_EXIT_VMRUN, "vmrun" }, \
- { SVM_EXIT_VMMCALL, "hypercall" }, \
- { SVM_EXIT_VMLOAD, "vmload" }, \
- { SVM_EXIT_VMSAVE, "vmsave" }, \
- { SVM_EXIT_STGI, "stgi" }, \
- { SVM_EXIT_CLGI, "clgi" }, \
- { SVM_EXIT_SKINIT, "skinit" }, \
- { SVM_EXIT_WBINVD, "wbinvd" }, \
- { SVM_EXIT_MONITOR, "monitor" }, \
- { SVM_EXIT_MWAIT, "mwait" }, \
- { SVM_EXIT_XSETBV, "xsetbv" }, \
- { SVM_EXIT_NPF, "npf" }
-
/*
* Tracepoint for kvm guest exit:
*/
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index b1eb202ee76a..851aa7c3b890 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -1493,8 +1493,12 @@ 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 (user_has_fpu())
- clts();
+ /*
+ * If the FPU is not active (through the host task or
+ * the guest vcpu), then restore the cr0.TS bit.
+ */
+ if (!user_has_fpu() && !vmx->vcpu.guest_fpu_loaded)
+ stts();
load_gdt(&__get_cpu_var(host_gdt));
}
@@ -3743,7 +3747,7 @@ static void vmx_set_constant_host_state(void)
unsigned long tmpl;
struct desc_ptr dt;
- vmcs_writel(HOST_CR0, read_cr0() | X86_CR0_TS); /* 22.2.3 */
+ vmcs_writel(HOST_CR0, read_cr0() & ~X86_CR0_TS); /* 22.2.3 */
vmcs_writel(HOST_CR4, read_cr4()); /* 22.2.3, 22.2.5 */
vmcs_writel(HOST_CR3, read_cr3()); /* 22.2.3 FIXME: shadow tables */
@@ -4543,7 +4547,7 @@ static int handle_cr(struct kvm_vcpu *vcpu)
vcpu->run->exit_reason = KVM_EXIT_SET_TPR;
return 0;
}
- };
+ }
break;
case 2: /* clts */
handle_clts(vcpu);
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 2966c847d489..1f09552572fa 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -5979,7 +5979,7 @@ void kvm_load_guest_fpu(struct kvm_vcpu *vcpu)
*/
kvm_put_guest_xcr0(vcpu);
vcpu->guest_fpu_loaded = 1;
- unlazy_fpu(current);
+ __kernel_fpu_begin();
fpu_restore_checking(&vcpu->arch.guest_fpu);
trace_kvm_fpu(1);
}
@@ -5993,6 +5993,7 @@ void kvm_put_guest_fpu(struct kvm_vcpu *vcpu)
vcpu->guest_fpu_loaded = 0;
fpu_save_init(&vcpu->arch.guest_fpu);
+ __kernel_fpu_end();
++vcpu->stat.fpu_reload;
kvm_make_request(KVM_REQ_DEACTIVATE_FPU, vcpu);
trace_kvm_fpu(0);
diff --git a/arch/x86/lib/copy_user_64.S b/arch/x86/lib/copy_user_64.S
index 5b2995f4557a..a30ca15be21c 100644
--- a/arch/x86/lib/copy_user_64.S
+++ b/arch/x86/lib/copy_user_64.S
@@ -17,6 +17,7 @@
#include <asm/cpufeature.h>
#include <asm/alternative-asm.h>
#include <asm/asm.h>
+#include <asm/smap.h>
/*
* By placing feature2 after feature1 in altinstructions section, we logically
@@ -130,6 +131,7 @@ ENDPROC(bad_from_user)
*/
ENTRY(copy_user_generic_unrolled)
CFI_STARTPROC
+ ASM_STAC
cmpl $8,%edx
jb 20f /* less then 8 bytes, go to byte copy loop */
ALIGN_DESTINATION
@@ -177,6 +179,7 @@ ENTRY(copy_user_generic_unrolled)
decl %ecx
jnz 21b
23: xor %eax,%eax
+ ASM_CLAC
ret
.section .fixup,"ax"
@@ -232,6 +235,7 @@ ENDPROC(copy_user_generic_unrolled)
*/
ENTRY(copy_user_generic_string)
CFI_STARTPROC
+ ASM_STAC
andl %edx,%edx
jz 4f
cmpl $8,%edx
@@ -246,6 +250,7 @@ ENTRY(copy_user_generic_string)
3: rep
movsb
4: xorl %eax,%eax
+ ASM_CLAC
ret
.section .fixup,"ax"
@@ -273,12 +278,14 @@ ENDPROC(copy_user_generic_string)
*/
ENTRY(copy_user_enhanced_fast_string)
CFI_STARTPROC
+ ASM_STAC
andl %edx,%edx
jz 2f
movl %edx,%ecx
1: rep
movsb
2: xorl %eax,%eax
+ ASM_CLAC
ret
.section .fixup,"ax"
diff --git a/arch/x86/lib/copy_user_nocache_64.S b/arch/x86/lib/copy_user_nocache_64.S
index cacddc7163eb..6a4f43c2d9e6 100644
--- a/arch/x86/lib/copy_user_nocache_64.S
+++ b/arch/x86/lib/copy_user_nocache_64.S
@@ -15,6 +15,7 @@
#include <asm/asm-offsets.h>
#include <asm/thread_info.h>
#include <asm/asm.h>
+#include <asm/smap.h>
.macro ALIGN_DESTINATION
#ifdef FIX_ALIGNMENT
@@ -48,6 +49,7 @@
*/
ENTRY(__copy_user_nocache)
CFI_STARTPROC
+ ASM_STAC
cmpl $8,%edx
jb 20f /* less then 8 bytes, go to byte copy loop */
ALIGN_DESTINATION
@@ -95,6 +97,7 @@ ENTRY(__copy_user_nocache)
decl %ecx
jnz 21b
23: xorl %eax,%eax
+ ASM_CLAC
sfence
ret
diff --git a/arch/x86/lib/getuser.S b/arch/x86/lib/getuser.S
index b33b1fb1e6d4..156b9c804670 100644
--- a/arch/x86/lib/getuser.S
+++ b/arch/x86/lib/getuser.S
@@ -33,6 +33,7 @@
#include <asm/asm-offsets.h>
#include <asm/thread_info.h>
#include <asm/asm.h>
+#include <asm/smap.h>
.text
ENTRY(__get_user_1)
@@ -40,8 +41,10 @@ ENTRY(__get_user_1)
GET_THREAD_INFO(%_ASM_DX)
cmp TI_addr_limit(%_ASM_DX),%_ASM_AX
jae bad_get_user
+ ASM_STAC
1: movzb (%_ASM_AX),%edx
xor %eax,%eax
+ ASM_CLAC
ret
CFI_ENDPROC
ENDPROC(__get_user_1)
@@ -53,8 +56,10 @@ ENTRY(__get_user_2)
GET_THREAD_INFO(%_ASM_DX)
cmp TI_addr_limit(%_ASM_DX),%_ASM_AX
jae bad_get_user
+ ASM_STAC
2: movzwl -1(%_ASM_AX),%edx
xor %eax,%eax
+ ASM_CLAC
ret
CFI_ENDPROC
ENDPROC(__get_user_2)
@@ -66,8 +71,10 @@ ENTRY(__get_user_4)
GET_THREAD_INFO(%_ASM_DX)
cmp TI_addr_limit(%_ASM_DX),%_ASM_AX
jae bad_get_user
+ ASM_STAC
3: mov -3(%_ASM_AX),%edx
xor %eax,%eax
+ ASM_CLAC
ret
CFI_ENDPROC
ENDPROC(__get_user_4)
@@ -80,8 +87,10 @@ ENTRY(__get_user_8)
GET_THREAD_INFO(%_ASM_DX)
cmp TI_addr_limit(%_ASM_DX),%_ASM_AX
jae bad_get_user
+ ASM_STAC
4: movq -7(%_ASM_AX),%_ASM_DX
xor %eax,%eax
+ ASM_CLAC
ret
CFI_ENDPROC
ENDPROC(__get_user_8)
@@ -91,6 +100,7 @@ bad_get_user:
CFI_STARTPROC
xor %edx,%edx
mov $(-EFAULT),%_ASM_AX
+ ASM_CLAC
ret
CFI_ENDPROC
END(bad_get_user)
diff --git a/arch/x86/lib/putuser.S b/arch/x86/lib/putuser.S
index 7f951c8f76c4..fc6ba17a7eec 100644
--- a/arch/x86/lib/putuser.S
+++ b/arch/x86/lib/putuser.S
@@ -15,6 +15,7 @@
#include <asm/thread_info.h>
#include <asm/errno.h>
#include <asm/asm.h>
+#include <asm/smap.h>
/*
@@ -31,7 +32,8 @@
#define ENTER CFI_STARTPROC ; \
GET_THREAD_INFO(%_ASM_BX)
-#define EXIT ret ; \
+#define EXIT ASM_CLAC ; \
+ ret ; \
CFI_ENDPROC
.text
@@ -39,6 +41,7 @@ ENTRY(__put_user_1)
ENTER
cmp TI_addr_limit(%_ASM_BX),%_ASM_CX
jae bad_put_user
+ ASM_STAC
1: movb %al,(%_ASM_CX)
xor %eax,%eax
EXIT
@@ -50,6 +53,7 @@ ENTRY(__put_user_2)
sub $1,%_ASM_BX
cmp %_ASM_BX,%_ASM_CX
jae bad_put_user
+ ASM_STAC
2: movw %ax,(%_ASM_CX)
xor %eax,%eax
EXIT
@@ -61,6 +65,7 @@ ENTRY(__put_user_4)
sub $3,%_ASM_BX
cmp %_ASM_BX,%_ASM_CX
jae bad_put_user
+ ASM_STAC
3: movl %eax,(%_ASM_CX)
xor %eax,%eax
EXIT
@@ -72,6 +77,7 @@ ENTRY(__put_user_8)
sub $7,%_ASM_BX
cmp %_ASM_BX,%_ASM_CX
jae bad_put_user
+ ASM_STAC
4: mov %_ASM_AX,(%_ASM_CX)
#ifdef CONFIG_X86_32
5: movl %edx,4(%_ASM_CX)
diff --git a/arch/x86/lib/usercopy_32.c b/arch/x86/lib/usercopy_32.c
index 1781b2f950e2..98f6d6b68f5a 100644
--- a/arch/x86/lib/usercopy_32.c
+++ b/arch/x86/lib/usercopy_32.c
@@ -42,10 +42,11 @@ do { \
int __d0; \
might_fault(); \
__asm__ __volatile__( \
+ ASM_STAC "\n" \
"0: rep; stosl\n" \
" movl %2,%0\n" \
"1: rep; stosb\n" \
- "2:\n" \
+ "2: " ASM_CLAC "\n" \
".section .fixup,\"ax\"\n" \
"3: lea 0(%2,%0,4),%0\n" \
" jmp 2b\n" \
@@ -626,10 +627,12 @@ survive:
return n;
}
#endif
+ stac();
if (movsl_is_ok(to, from, n))
__copy_user(to, from, n);
else
n = __copy_user_intel(to, from, n);
+ clac();
return n;
}
EXPORT_SYMBOL(__copy_to_user_ll);
@@ -637,10 +640,12 @@ EXPORT_SYMBOL(__copy_to_user_ll);
unsigned long __copy_from_user_ll(void *to, const void __user *from,
unsigned long n)
{
+ stac();
if (movsl_is_ok(to, from, n))
__copy_user_zeroing(to, from, n);
else
n = __copy_user_zeroing_intel(to, from, n);
+ clac();
return n;
}
EXPORT_SYMBOL(__copy_from_user_ll);
@@ -648,11 +653,13 @@ EXPORT_SYMBOL(__copy_from_user_ll);
unsigned long __copy_from_user_ll_nozero(void *to, const void __user *from,
unsigned long n)
{
+ stac();
if (movsl_is_ok(to, from, n))
__copy_user(to, from, n);
else
n = __copy_user_intel((void __user *)to,
(const void *)from, n);
+ clac();
return n;
}
EXPORT_SYMBOL(__copy_from_user_ll_nozero);
@@ -660,6 +667,7 @@ EXPORT_SYMBOL(__copy_from_user_ll_nozero);
unsigned long __copy_from_user_ll_nocache(void *to, const void __user *from,
unsigned long n)
{
+ stac();
#ifdef CONFIG_X86_INTEL_USERCOPY
if (n > 64 && cpu_has_xmm2)
n = __copy_user_zeroing_intel_nocache(to, from, n);
@@ -668,6 +676,7 @@ unsigned long __copy_from_user_ll_nocache(void *to, const void __user *from,
#else
__copy_user_zeroing(to, from, n);
#endif
+ clac();
return n;
}
EXPORT_SYMBOL(__copy_from_user_ll_nocache);
@@ -675,6 +684,7 @@ EXPORT_SYMBOL(__copy_from_user_ll_nocache);
unsigned long __copy_from_user_ll_nocache_nozero(void *to, const void __user *from,
unsigned long n)
{
+ stac();
#ifdef CONFIG_X86_INTEL_USERCOPY
if (n > 64 && cpu_has_xmm2)
n = __copy_user_intel_nocache(to, from, n);
@@ -683,6 +693,7 @@ unsigned long __copy_from_user_ll_nocache_nozero(void *to, const void __user *fr
#else
__copy_user(to, from, n);
#endif
+ clac();
return n;
}
EXPORT_SYMBOL(__copy_from_user_ll_nocache_nozero);
diff --git a/arch/x86/lib/usercopy_64.c b/arch/x86/lib/usercopy_64.c
index e5b130bc2d0e..05928aae911e 100644
--- a/arch/x86/lib/usercopy_64.c
+++ b/arch/x86/lib/usercopy_64.c
@@ -18,6 +18,7 @@ unsigned long __clear_user(void __user *addr, unsigned long size)
might_fault();
/* no memory constraint because it doesn't change any memory gcc knows
about */
+ stac();
asm volatile(
" testq %[size8],%[size8]\n"
" jz 4f\n"
@@ -40,6 +41,7 @@ unsigned long __clear_user(void __user *addr, unsigned long size)
: [size8] "=&c"(size), [dst] "=&D" (__d0)
: [size1] "r"(size & 7), "[size8]" (size / 8), "[dst]"(addr),
[zero] "r" (0UL), [eight] "r" (8UL));
+ clac();
return size;
}
EXPORT_SYMBOL(__clear_user);
@@ -82,5 +84,6 @@ copy_user_handle_tail(char *to, char *from, unsigned len, unsigned zerorest)
for (c = 0, zero_len = len; zerorest && zero_len; --zero_len)
if (__put_user_nocheck(c, to++, sizeof(char)))
break;
+ clac();
return len;
}
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c
index 76dcd9d8e0bc..a530b230e7d7 100644
--- a/arch/x86/mm/fault.c
+++ b/arch/x86/mm/fault.c
@@ -18,6 +18,7 @@
#include <asm/pgalloc.h> /* pgd_*(), ... */
#include <asm/kmemcheck.h> /* kmemcheck_*(), ... */
#include <asm/fixmap.h> /* VSYSCALL_START */
+#include <asm/rcu.h> /* exception_enter(), ... */
/*
* Page fault error code bits:
@@ -995,13 +996,24 @@ static int fault_in_kernel_space(unsigned long address)
return address >= TASK_SIZE_MAX;
}
+static inline bool smap_violation(int error_code, struct pt_regs *regs)
+{
+ if (error_code & PF_USER)
+ return false;
+
+ if (!user_mode_vm(regs) && (regs->flags & X86_EFLAGS_AC))
+ return false;
+
+ return true;
+}
+
/*
* This routine handles page faults. It determines the address,
* and the problem, and then passes it off to one of the appropriate
* routines.
*/
-dotraplinkage void __kprobes
-do_page_fault(struct pt_regs *regs, unsigned long error_code)
+static void __kprobes
+__do_page_fault(struct pt_regs *regs, unsigned long error_code)
{
struct vm_area_struct *vma;
struct task_struct *tsk;
@@ -1088,6 +1100,13 @@ do_page_fault(struct pt_regs *regs, unsigned long error_code)
if (unlikely(error_code & PF_RSVD))
pgtable_bad(regs, error_code, address);
+ if (static_cpu_has(X86_FEATURE_SMAP)) {
+ if (unlikely(smap_violation(error_code, regs))) {
+ bad_area_nosemaphore(regs, error_code, address);
+ return;
+ }
+ }
+
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
/*
@@ -1209,3 +1228,11 @@ good_area:
up_read(&mm->mmap_sem);
}
+
+dotraplinkage void __kprobes
+do_page_fault(struct pt_regs *regs, unsigned long error_code)
+{
+ exception_enter(regs);
+ __do_page_fault(regs, error_code);
+ exception_exit(regs);
+}
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index e0e6990723e9..ab1f6a93b527 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -319,7 +319,7 @@ unsigned long __init_refok init_memory_mapping(unsigned long start,
*/
int devmem_is_allowed(unsigned long pagenr)
{
- if (pagenr <= 256)
+ if (pagenr < 256)
return 1;
if (iomem_is_exclusive(pagenr << PAGE_SHIFT))
return 0;
diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c
index 575d86f85ce4..11a58001b4ce 100644
--- a/arch/x86/mm/init_32.c
+++ b/arch/x86/mm/init_32.c
@@ -445,10 +445,10 @@ static inline void permanent_kmaps_init(pgd_t *pgd_base)
}
#endif /* CONFIG_HIGHMEM */
-void __init native_pagetable_setup_start(pgd_t *base)
+void __init native_pagetable_init(void)
{
unsigned long pfn, va;
- pgd_t *pgd;
+ pgd_t *pgd, *base = swapper_pg_dir;
pud_t *pud;
pmd_t *pmd;
pte_t *pte;
@@ -475,10 +475,7 @@ void __init native_pagetable_setup_start(pgd_t *base)
pte_clear(NULL, va, pte);
}
paravirt_alloc_pmd(&init_mm, __pa(base) >> PAGE_SHIFT);
-}
-
-void __init native_pagetable_setup_done(pgd_t *base)
-{
+ paging_init();
}
/*
@@ -493,7 +490,7 @@ void __init native_pagetable_setup_done(pgd_t *base)
* If we're booting paravirtualized under a hypervisor, then there are
* more options: we may already be running PAE, and the pagetable may
* or may not be based in swapper_pg_dir. In any case,
- * paravirt_pagetable_setup_start() will set up swapper_pg_dir
+ * paravirt_pagetable_init() will set up swapper_pg_dir
* appropriately for the rest of the initialization to work.
*
* In general, pagetable_init() assumes that the pagetable may already
@@ -712,7 +709,7 @@ static void __init test_wp_bit(void)
"Checking if this processor honours the WP bit even in supervisor mode...");
/* Any page-aligned address will do, the test is non-destructive */
- __set_fixmap(FIX_WP_TEST, __pa(&swapper_pg_dir), PAGE_READONLY);
+ __set_fixmap(FIX_WP_TEST, __pa(&swapper_pg_dir), PAGE_KERNEL_RO);
boot_cpu_data.wp_works_ok = do_test_wp_bit();
clear_fixmap(FIX_WP_TEST);
diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
index 613cd83e8c0c..0777f042e400 100644
--- a/arch/x86/mm/tlb.c
+++ b/arch/x86/mm/tlb.c
@@ -98,6 +98,8 @@ static void flush_tlb_func(void *info)
{
struct flush_tlb_info *f = info;
+ inc_irq_stat(irq_tlb_count);
+
if (f->flush_mm != this_cpu_read(cpu_tlbstate.active_mm))
return;
@@ -320,7 +322,7 @@ static ssize_t tlbflush_write_file(struct file *file,
if (kstrtos8(buf, 0, &shift))
return -EINVAL;
- if (shift > 64)
+ if (shift < -1 || shift >= BITS_PER_LONG)
return -EINVAL;
tlb_flushall_shift = shift;
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index 505acdd6d600..192397c98606 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -305,7 +305,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
res->flags = flags;
res->start = start;
res->end = end;
- res->child = NULL;
if (!pci_use_crs) {
dev_printk(KERN_DEBUG, &info->bridge->dev,
@@ -434,7 +433,7 @@ probe_pci_root_info(struct pci_root_info *info, struct acpi_device *device,
size = sizeof(*info->res) * info->res_num;
info->res_num = 0;
- info->res = kmalloc(size, GFP_KERNEL);
+ info->res = kzalloc(size, GFP_KERNEL);
if (!info->res)
return;
diff --git a/arch/x86/pci/mmconfig-shared.c b/arch/x86/pci/mmconfig-shared.c
index 937bcece7006..704b9ec043d7 100644
--- a/arch/x86/pci/mmconfig-shared.c
+++ b/arch/x86/pci/mmconfig-shared.c
@@ -585,7 +585,7 @@ static int __init pci_parse_mcfg(struct acpi_table_header *header)
while (i >= sizeof(struct acpi_mcfg_allocation)) {
entries++;
i -= sizeof(struct acpi_mcfg_allocation);
- };
+ }
if (entries == 0) {
pr_err(PREFIX "MMCONFIG has no entries\n");
return -ENODEV;
diff --git a/arch/x86/pci/visws.c b/arch/x86/pci/visws.c
index 6f2f8eeed171..3e6d2a6db866 100644
--- a/arch/x86/pci/visws.c
+++ b/arch/x86/pci/visws.c
@@ -62,11 +62,6 @@ out:
return irq;
}
-void __init pcibios_update_irq(struct pci_dev *dev, int irq)
-{
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-}
-
int __init pci_visws_init(void)
{
pcibios_enable_irq = &pci_visws_enable_irq;
diff --git a/arch/x86/platform/efi/Makefile b/arch/x86/platform/efi/Makefile
index 73b8be0f3675..6db1cc4c7534 100644
--- a/arch/x86/platform/efi/Makefile
+++ b/arch/x86/platform/efi/Makefile
@@ -1 +1,2 @@
obj-$(CONFIG_EFI) += efi.o efi_$(BITS).o efi_stub_$(BITS).o
+obj-$(CONFIG_ACPI_BGRT) += efi-bgrt.o
diff --git a/arch/x86/platform/efi/efi-bgrt.c b/arch/x86/platform/efi/efi-bgrt.c
new file mode 100644
index 000000000000..f6a0c1b8e518
--- /dev/null
+++ b/arch/x86/platform/efi/efi-bgrt.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2012 Intel Corporation
+ * Author: Josh Triplett <josh@joshtriplett.org>
+ *
+ * Based on the bgrt driver:
+ * Copyright 2012 Red Hat, Inc <mjg@redhat.com>
+ * Author: Matthew Garrett
+ *
+ * This 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/acpi.h>
+#include <linux/efi.h>
+#include <linux/efi-bgrt.h>
+
+struct acpi_table_bgrt *bgrt_tab;
+void *bgrt_image;
+size_t bgrt_image_size;
+
+struct bmp_header {
+ u16 id;
+ u32 size;
+} __packed;
+
+void efi_bgrt_init(void)
+{
+ acpi_status status;
+ void __iomem *image;
+ bool ioremapped = false;
+ struct bmp_header bmp_header;
+
+ if (acpi_disabled)
+ return;
+
+ status = acpi_get_table("BGRT", 0,
+ (struct acpi_table_header **)&bgrt_tab);
+ if (ACPI_FAILURE(status))
+ return;
+
+ if (bgrt_tab->version != 1)
+ return;
+ if (bgrt_tab->image_type != 0 || !bgrt_tab->image_address)
+ return;
+
+ image = efi_lookup_mapped_addr(bgrt_tab->image_address);
+ if (!image) {
+ image = ioremap(bgrt_tab->image_address, sizeof(bmp_header));
+ ioremapped = true;
+ if (!image)
+ return;
+ }
+
+ memcpy_fromio(&bmp_header, image, sizeof(bmp_header));
+ if (ioremapped)
+ iounmap(image);
+ bgrt_image_size = bmp_header.size;
+
+ bgrt_image = kmalloc(bgrt_image_size, GFP_KERNEL);
+ if (!bgrt_image)
+ return;
+
+ if (ioremapped) {
+ image = ioremap(bgrt_tab->image_address, bmp_header.size);
+ if (!image) {
+ kfree(bgrt_image);
+ bgrt_image = NULL;
+ return;
+ }
+ }
+
+ memcpy_fromio(bgrt_image, image, bgrt_image_size);
+ if (ioremapped)
+ iounmap(image);
+}
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 92660edaa1e7..aded2a91162a 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -31,6 +31,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/efi.h>
+#include <linux/efi-bgrt.h>
#include <linux/export.h>
#include <linux/bootmem.h>
#include <linux/memblock.h>
@@ -419,10 +420,21 @@ void __init efi_reserve_boot_services(void)
}
}
-static void __init efi_free_boot_services(void)
+static void __init efi_unmap_memmap(void)
+{
+ if (memmap.map) {
+ early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size);
+ memmap.map = NULL;
+ }
+}
+
+void __init efi_free_boot_services(void)
{
void *p;
+ if (!efi_native)
+ return;
+
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
efi_memory_desc_t *md = p;
unsigned long long start = md->phys_addr;
@@ -438,6 +450,8 @@ static void __init efi_free_boot_services(void)
free_bootmem_late(start, size);
}
+
+ efi_unmap_memmap();
}
static int __init efi_systab_init(void *phys)
@@ -732,6 +746,11 @@ void __init efi_init(void)
#endif
}
+void __init efi_late_init(void)
+{
+ efi_bgrt_init();
+}
+
void __init efi_set_executable(efi_memory_desc_t *md, bool executable)
{
u64 addr, npages;
@@ -764,6 +783,34 @@ static void __init runtime_code_page_mkexec(void)
}
/*
+ * We can't ioremap data in EFI boot services RAM, because we've already mapped
+ * it as RAM. So, look it up in the existing EFI memory map instead. Only
+ * callable after efi_enter_virtual_mode and before efi_free_boot_services.
+ */
+void __iomem *efi_lookup_mapped_addr(u64 phys_addr)
+{
+ void *p;
+ if (WARN_ON(!memmap.map))
+ return NULL;
+ for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
+ efi_memory_desc_t *md = p;
+ u64 size = md->num_pages << EFI_PAGE_SHIFT;
+ u64 end = md->phys_addr + size;
+ if (!(md->attribute & EFI_MEMORY_RUNTIME) &&
+ md->type != EFI_BOOT_SERVICES_CODE &&
+ md->type != EFI_BOOT_SERVICES_DATA)
+ continue;
+ if (!md->virt_addr)
+ continue;
+ if (phys_addr >= md->phys_addr && phys_addr < end) {
+ phys_addr += md->virt_addr - md->phys_addr;
+ return (__force void __iomem *)(unsigned long)phys_addr;
+ }
+ }
+ return NULL;
+}
+
+/*
* This function will switch the EFI runtime services to virtual mode.
* Essentially, look through the EFI memmap and map every region that
* has the runtime attribute bit set in its memory descriptor and update
@@ -787,8 +834,10 @@ void __init efi_enter_virtual_mode(void)
* non-native EFI
*/
- if (!efi_native)
- goto out;
+ if (!efi_native) {
+ efi_unmap_memmap();
+ return;
+ }
/* Merge contiguous regions of the same type and attribute */
for (p = memmap.map; p < memmap.map_end; p += memmap.desc_size) {
@@ -878,18 +927,12 @@ void __init efi_enter_virtual_mode(void)
}
/*
- * Thankfully, it does seem that no runtime services other than
- * SetVirtualAddressMap() will touch boot services code, so we can
- * get rid of it all at this point
- */
- efi_free_boot_services();
-
- /*
* Now that EFI is in virtual mode, update the function
* pointers in the runtime service table to the new virtual addresses.
*
* Call EFI services through wrapper functions.
*/
+ efi.runtime_version = efi_systab.fw_revision;
efi.get_time = virt_efi_get_time;
efi.set_time = virt_efi_set_time;
efi.get_wakeup_time = virt_efi_get_wakeup_time;
@@ -906,9 +949,6 @@ void __init efi_enter_virtual_mode(void)
if (__supported_pte_mask & _PAGE_NX)
runtime_code_page_mkexec();
-out:
- early_iounmap(memmap.map, memmap.nr_map * memmap.desc_size);
- memmap.map = NULL;
kfree(new_memmap);
}
diff --git a/arch/x86/realmode/rm/wakeup.h b/arch/x86/realmode/rm/wakeup.h
index 9317e0042f24..7dd86a419f5d 100644
--- a/arch/x86/realmode/rm/wakeup.h
+++ b/arch/x86/realmode/rm/wakeup.h
@@ -36,5 +36,7 @@ extern struct wakeup_header wakeup_header;
/* Wakeup behavior bits */
#define WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE 0
+#define WAKEUP_BEHAVIOR_RESTORE_CR4 1
+#define WAKEUP_BEHAVIOR_RESTORE_EFER 2
#endif /* ARCH_X86_KERNEL_ACPI_RM_WAKEUP_H */
diff --git a/arch/x86/realmode/rm/wakeup_asm.S b/arch/x86/realmode/rm/wakeup_asm.S
index 8905166b0bbb..e56479e58053 100644
--- a/arch/x86/realmode/rm/wakeup_asm.S
+++ b/arch/x86/realmode/rm/wakeup_asm.S
@@ -74,9 +74,18 @@ ENTRY(wakeup_start)
lidtl wakeup_idt
- /* Clear the EFLAGS */
- pushl $0
+ /* Clear the EFLAGS but remember if we have EFLAGS.ID */
+ movl $X86_EFLAGS_ID, %ecx
+ pushl %ecx
popfl
+ pushfl
+ popl %edi
+ pushl $0
+ popfl
+ pushfl
+ popl %edx
+ xorl %edx, %edi
+ andl %ecx, %edi /* %edi is zero iff CPUID & %cr4 are missing */
/* Check header signature... */
movl signature, %eax
@@ -93,8 +102,8 @@ ENTRY(wakeup_start)
/* Restore MISC_ENABLE before entering protected mode, in case
BIOS decided to clear XD_DISABLE during S3. */
- movl pmode_behavior, %eax
- btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %eax
+ movl pmode_behavior, %edi
+ btl $WAKEUP_BEHAVIOR_RESTORE_MISC_ENABLE, %edi
jnc 1f
movl pmode_misc_en, %eax
@@ -110,15 +119,15 @@ ENTRY(wakeup_start)
movl pmode_cr3, %eax
movl %eax, %cr3
- movl pmode_cr4, %ecx
- jecxz 1f
- movl %ecx, %cr4
+ btl $WAKEUP_BEHAVIOR_RESTORE_CR4, %edi
+ jz 1f
+ movl pmode_cr4, %eax
+ movl %eax, %cr4
1:
+ btl $WAKEUP_BEHAVIOR_RESTORE_EFER, %edi
+ jz 1f
movl pmode_efer, %eax
movl pmode_efer + 4, %edx
- movl %eax, %ecx
- orl %edx, %ecx
- jz 1f
movl $MSR_EFER, %ecx
wrmsr
1:
diff --git a/arch/x86/um/Kconfig b/arch/x86/um/Kconfig
index 9926e11a772d..aeaff8bef2f1 100644
--- a/arch/x86/um/Kconfig
+++ b/arch/x86/um/Kconfig
@@ -21,6 +21,7 @@ config 64BIT
config X86_32
def_bool !64BIT
select HAVE_AOUT
+ select ARCH_WANT_IPC_PARSE_VERSION
config X86_64
def_bool 64BIT
diff --git a/arch/x86/um/shared/sysdep/kernel-offsets.h b/arch/x86/um/shared/sysdep/kernel-offsets.h
index 5868526b5eef..46a9df99f3c5 100644
--- a/arch/x86/um/shared/sysdep/kernel-offsets.h
+++ b/arch/x86/um/shared/sysdep/kernel-offsets.h
@@ -7,9 +7,6 @@
#define DEFINE(sym, val) \
asm volatile("\n->" #sym " %0 " #val : : "i" (val))
-#define STR(x) #x
-#define DEFINE_STR(sym, val) asm volatile("\n->" #sym " " STR(val) " " #val: : )
-
#define BLANK() asm volatile("\n->" : : )
#define OFFSET(sym, str, mem) \
diff --git a/arch/x86/um/shared/sysdep/syscalls.h b/arch/x86/um/shared/sysdep/syscalls.h
index bd9a89b67e41..ca255a805ed9 100644
--- a/arch/x86/um/shared/sysdep/syscalls.h
+++ b/arch/x86/um/shared/sysdep/syscalls.h
@@ -1,3 +1,5 @@
+extern long sys_clone(unsigned long clone_flags, unsigned long newsp,
+ void __user *parent_tid, void __user *child_tid);
#ifdef __i386__
#include "syscalls_32.h"
#else
diff --git a/arch/x86/um/signal.c b/arch/x86/um/signal.c
index a508cea13503..ba7363ecf896 100644
--- a/arch/x86/um/signal.c
+++ b/arch/x86/um/signal.c
@@ -416,9 +416,6 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig,
PT_REGS_AX(regs) = (unsigned long) sig;
PT_REGS_DX(regs) = (unsigned long) 0;
PT_REGS_CX(regs) = (unsigned long) 0;
-
- if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
- ptrace_notify(SIGTRAP);
return 0;
}
@@ -466,9 +463,6 @@ int setup_signal_stack_si(unsigned long stack_top, int sig,
PT_REGS_AX(regs) = (unsigned long) sig;
PT_REGS_DX(regs) = (unsigned long) &frame->info;
PT_REGS_CX(regs) = (unsigned long) &frame->uc;
-
- if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
- ptrace_notify(SIGTRAP);
return 0;
}
diff --git a/arch/x86/um/sys_call_table_32.c b/arch/x86/um/sys_call_table_32.c
index 68d1dc91b37b..b5408cecac6c 100644
--- a/arch/x86/um/sys_call_table_32.c
+++ b/arch/x86/um/sys_call_table_32.c
@@ -28,7 +28,7 @@
#define ptregs_execve sys_execve
#define ptregs_iopl sys_iopl
#define ptregs_vm86old sys_vm86old
-#define ptregs_clone sys_clone
+#define ptregs_clone i386_clone
#define ptregs_vm86 sys_vm86
#define ptregs_sigaltstack sys_sigaltstack
#define ptregs_vfork sys_vfork
diff --git a/arch/x86/um/syscalls_32.c b/arch/x86/um/syscalls_32.c
index b853e8600b9d..db444c7218fe 100644
--- a/arch/x86/um/syscalls_32.c
+++ b/arch/x86/um/syscalls_32.c
@@ -3,37 +3,24 @@
* Licensed under the GPL
*/
-#include "linux/sched.h"
-#include "linux/shm.h"
-#include "linux/ipc.h"
-#include "linux/syscalls.h"
-#include "asm/mman.h"
-#include "asm/uaccess.h"
-#include "asm/unistd.h"
+#include <linux/syscalls.h>
+#include <sysdep/syscalls.h>
/*
* The prototype on i386 is:
*
- * int clone(int flags, void * child_stack, int * parent_tidptr, struct user_desc * newtls, int * child_tidptr)
+ * int clone(int flags, void * child_stack, int * parent_tidptr, struct user_desc * newtls
*
* and the "newtls" arg. on i386 is read by copy_thread directly from the
* register saved on the stack.
*/
-long sys_clone(unsigned long clone_flags, unsigned long newsp,
- int __user *parent_tid, void *newtls, int __user *child_tid)
+long i386_clone(unsigned long clone_flags, unsigned long newsp,
+ int __user *parent_tid, void *newtls, int __user *child_tid)
{
- long ret;
-
- if (!newsp)
- newsp = UPT_SP(&current->thread.regs.regs);
-
- current->thread.forking = 1;
- ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
- child_tid);
- current->thread.forking = 0;
- return ret;
+ return sys_clone(clone_flags, newsp, parent_tid, child_tid);
}
+
long sys_sigaction(int sig, const struct old_sigaction __user *act,
struct old_sigaction __user *oact)
{
diff --git a/arch/x86/um/syscalls_64.c b/arch/x86/um/syscalls_64.c
index f3d82bb6e15a..adb08eb5c22a 100644
--- a/arch/x86/um/syscalls_64.c
+++ b/arch/x86/um/syscalls_64.c
@@ -5,12 +5,9 @@
* Licensed under the GPL
*/
-#include "linux/linkage.h"
-#include "linux/personality.h"
-#include "linux/utsname.h"
-#include "asm/prctl.h" /* XXX This should get the constants from libc */
-#include "asm/uaccess.h"
-#include "os.h"
+#include <linux/sched.h>
+#include <asm/prctl.h> /* XXX This should get the constants from libc */
+#include <os.h>
long arch_prctl(struct task_struct *task, int code, unsigned long __user *addr)
{
@@ -79,20 +76,6 @@ long sys_arch_prctl(int code, unsigned long addr)
return arch_prctl(current, code, (unsigned long __user *) addr);
}
-long sys_clone(unsigned long clone_flags, unsigned long newsp,
- void __user *parent_tid, void __user *child_tid)
-{
- long ret;
-
- if (!newsp)
- newsp = UPT_SP(&current->thread.regs.regs);
- current->thread.forking = 1;
- ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
- child_tid);
- current->thread.forking = 0;
- return ret;
-}
-
void arch_switch_to(struct task_struct *to)
{
if ((to->thread.arch.fs == 0) || (to->mm == NULL))
diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index 9642d4a38602..1fbe75a95f15 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -1452,6 +1452,10 @@ asmlinkage void __init xen_start_kernel(void)
pci_request_acs();
xen_acpi_sleep_register();
+
+ /* Avoid searching for BIOS MP tables */
+ x86_init.mpparse.find_smp_config = x86_init_noop;
+ x86_init.mpparse.get_smp_config = x86_init_uint_noop;
}
#ifdef CONFIG_PCI
/* PCI BIOS service won't work from a PV guest. */
diff --git a/arch/x86/xen/mmu.c b/arch/x86/xen/mmu.c
index 5141d808e751..7a769b7526cb 100644
--- a/arch/x86/xen/mmu.c
+++ b/arch/x86/xen/mmu.c
@@ -1174,8 +1174,13 @@ static void xen_exit_mmap(struct mm_struct *mm)
spin_unlock(&mm->page_table_lock);
}
-static void __init xen_pagetable_setup_start(pgd_t *base)
+static void xen_post_allocator_init(void);
+
+static void __init xen_pagetable_init(void)
{
+ paging_init();
+ xen_setup_shared_info();
+ xen_post_allocator_init();
}
static __init void xen_mapping_pagetable_reserve(u64 start, u64 end)
@@ -1192,14 +1197,6 @@ static __init void xen_mapping_pagetable_reserve(u64 start, u64 end)
}
}
-static void xen_post_allocator_init(void);
-
-static void __init xen_pagetable_setup_done(pgd_t *base)
-{
- xen_setup_shared_info();
- xen_post_allocator_init();
-}
-
static void xen_write_cr2(unsigned long cr2)
{
this_cpu_read(xen_vcpu)->arch.cr2 = cr2;
@@ -2068,8 +2065,7 @@ static const struct pv_mmu_ops xen_mmu_ops __initconst = {
void __init xen_init_mmu_ops(void)
{
x86_init.mapping.pagetable_reserve = xen_mapping_pagetable_reserve;
- x86_init.paging.pagetable_setup_start = xen_pagetable_setup_start;
- x86_init.paging.pagetable_setup_done = xen_pagetable_setup_done;
+ x86_init.paging.pagetable_init = xen_pagetable_init;
pv_mmu_ops = xen_mmu_ops;
memset(dummy_mapping, 0xff, PAGE_SIZE);
diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c
index 76ba0e97e530..72213da605f5 100644
--- a/arch/x86/xen/p2m.c
+++ b/arch/x86/xen/p2m.c
@@ -828,9 +828,6 @@ int m2p_add_override(unsigned long mfn, struct page *page,
xen_mc_issue(PARAVIRT_LAZY_MMU);
}
- /* let's use dev_bus_addr to record the old mfn instead */
- kmap_op->dev_bus_addr = page->index;
- page->index = (unsigned long) kmap_op;
}
spin_lock_irqsave(&m2p_override_lock, flags);
list_add(&page->lru, &m2p_overrides[mfn_hash(mfn)]);
@@ -857,7 +854,8 @@ int m2p_add_override(unsigned long mfn, struct page *page,
return 0;
}
EXPORT_SYMBOL_GPL(m2p_add_override);
-int m2p_remove_override(struct page *page, bool clear_pte)
+int m2p_remove_override(struct page *page,
+ struct gnttab_map_grant_ref *kmap_op)
{
unsigned long flags;
unsigned long mfn;
@@ -887,10 +885,8 @@ int m2p_remove_override(struct page *page, bool clear_pte)
WARN_ON(!PagePrivate(page));
ClearPagePrivate(page);
- if (clear_pte) {
- struct gnttab_map_grant_ref *map_op =
- (struct gnttab_map_grant_ref *) page->index;
- set_phys_to_machine(pfn, map_op->dev_bus_addr);
+ set_phys_to_machine(pfn, page->index);
+ if (kmap_op != NULL) {
if (!PageHighMem(page)) {
struct multicall_space mcs;
struct gnttab_unmap_grant_ref *unmap_op;
@@ -902,13 +898,13 @@ int m2p_remove_override(struct page *page, bool clear_pte)
* issued. In this case handle is going to -1 because
* it hasn't been modified yet.
*/
- if (map_op->handle == -1)
+ if (kmap_op->handle == -1)
xen_mc_flush();
/*
- * Now if map_op->handle is negative it means that the
+ * Now if kmap_op->handle is negative it means that the
* hypercall actually returned an error.
*/
- if (map_op->handle == GNTST_general_error) {
+ if (kmap_op->handle == GNTST_general_error) {
printk(KERN_WARNING "m2p_remove_override: "
"pfn %lx mfn %lx, failed to modify kernel mappings",
pfn, mfn);
@@ -918,8 +914,8 @@ int m2p_remove_override(struct page *page, bool clear_pte)
mcs = xen_mc_entry(
sizeof(struct gnttab_unmap_grant_ref));
unmap_op = mcs.args;
- unmap_op->host_addr = map_op->host_addr;
- unmap_op->handle = map_op->handle;
+ unmap_op->host_addr = kmap_op->host_addr;
+ unmap_op->handle = kmap_op->handle;
unmap_op->dev_bus_addr = 0;
MULTI_grant_table_op(mcs.mc,
@@ -930,10 +926,9 @@ int m2p_remove_override(struct page *page, bool clear_pte)
set_pte_at(&init_mm, address, ptep,
pfn_pte(pfn, PAGE_KERNEL));
__flush_tlb_single(address);
- map_op->host_addr = 0;
+ kmap_op->host_addr = 0;
}
- } else
- set_phys_to_machine(pfn, page->index);
+ }
/* p2m(m2p(mfn)) == FOREIGN_FRAME(mfn): the mfn is already present
* somewhere in this domain, even before being added to the
diff --git a/arch/x86/xen/setup.c b/arch/x86/xen/setup.c
index d11ca11d14fc..e2d62d697b5d 100644
--- a/arch/x86/xen/setup.c
+++ b/arch/x86/xen/setup.c
@@ -17,6 +17,7 @@
#include <asm/e820.h>
#include <asm/setup.h>
#include <asm/acpi.h>
+#include <asm/numa.h>
#include <asm/xen/hypervisor.h>
#include <asm/xen/hypercall.h>
@@ -544,4 +545,7 @@ void __init xen_arch_setup(void)
disable_cpufreq();
WARN_ON(set_pm_idle_to_default());
fiddle_vdso();
+#ifdef CONFIG_NUMA
+ numa_off = 1;
+#endif
}
diff --git a/arch/x86/xen/smp.c b/arch/x86/xen/smp.c
index f58dca7a6e52..353c50f18702 100644
--- a/arch/x86/xen/smp.c
+++ b/arch/x86/xen/smp.c
@@ -377,7 +377,8 @@ static int __cpuinit xen_cpu_up(unsigned int cpu, struct task_struct *idle)
return rc;
if (num_online_cpus() == 1)
- alternatives_smp_switch(1);
+ /* Just in case we booted with a single CPU. */
+ alternatives_enable_smp();
rc = xen_smp_intr_init(cpu);
if (rc)
@@ -424,9 +425,6 @@ static void xen_cpu_die(unsigned int cpu)
unbind_from_irqhandler(per_cpu(xen_irq_work, cpu), NULL);
xen_uninit_lock_cpu(cpu);
xen_teardown_timer(cpu);
-
- if (num_online_cpus() == 1)
- alternatives_smp_switch(0);
}
static void __cpuinit xen_play_dead(void) /* used only with HOTPLUG_CPU */
diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig
index 8ed64cfae4ff..744f5ee4ba41 100644
--- a/arch/xtensa/Kconfig
+++ b/arch/xtensa/Kconfig
@@ -172,24 +172,6 @@ config CMDLINE
source "mm/Kconfig"
-config HOTPLUG
- bool "Support for hot-pluggable devices"
- help
- Say Y here if you want to plug devices into your computer while
- the system is running, and be able to use them quickly. In many
- cases, the devices can likewise be unplugged at any time too.
-
- One well known example of this is PCMCIA- or PC-cards, credit-card
- size devices such as network cards, modems or hard drives which are
- plugged into slots found on all modern laptop computers. Another
- example, used on modern desktops as well as laptops, is USB.
-
- Enable HOTPLUG and build a modular kernel. Get agent software
- (from <http://linux-hotplug.sourceforge.net/>) and install it.
- Then your kernel will automatically call out to a user mode "policy
- agent" (/sbin/hotplug) to load modules and set up software needed
- to use devices as you hotplug them.
-
source "drivers/pcmcia/Kconfig"
source "drivers/pci/hotplug/Kconfig"
diff --git a/arch/xtensa/kernel/pci.c b/arch/xtensa/kernel/pci.c
index 69759e9cb3ea..54354de38a70 100644
--- a/arch/xtensa/kernel/pci.c
+++ b/arch/xtensa/kernel/pci.c
@@ -210,14 +210,6 @@ void pcibios_set_master(struct pci_dev *dev)
/* No special bus mastering setup handling */
}
-/* the next one is stolen from the alpha port... */
-
-void __init
-pcibios_update_irq(struct pci_dev *dev, int irq)
-{
- pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
-}
-
int pcibios_enable_device(struct pci_dev *dev, int mask)
{
u16 cmd, old_cmd;
diff --git a/arch/xtensa/kernel/process.c b/arch/xtensa/kernel/process.c
index 2c8d6a3d250a..bc44311aa18c 100644
--- a/arch/xtensa/kernel/process.c
+++ b/arch/xtensa/kernel/process.c
@@ -31,6 +31,7 @@
#include <linux/mqueue.h>
#include <linux/fs.h>
#include <linux/slab.h>
+#include <linux/rcupdate.h>
#include <asm/pgtable.h>
#include <asm/uaccess.h>
@@ -110,8 +111,10 @@ void cpu_idle(void)
/* endless idle loop with no priority at all */
while (1) {
+ rcu_idle_enter();
while (!need_resched())
platform_idle();
+ rcu_idle_exit();
schedule_preempt_disabled();
}
}
diff --git a/block/blk-core.c b/block/blk-core.c
index 4b4dbdfbca89..ee3cb3a5e278 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -2254,9 +2254,11 @@ bool blk_update_request(struct request *req, int error, unsigned int nr_bytes)
error_type = "I/O";
break;
}
- printk(KERN_ERR "end_request: %s error, dev %s, sector %llu\n",
- error_type, req->rq_disk ? req->rq_disk->disk_name : "?",
- (unsigned long long)blk_rq_pos(req));
+ printk_ratelimited(KERN_ERR "end_request: %s error, dev %s, sector %llu\n",
+ error_type, req->rq_disk ?
+ req->rq_disk->disk_name : "?",
+ (unsigned long long)blk_rq_pos(req));
+
}
blk_account_io_completion(req, nr_bytes);
diff --git a/block/ioctl.c b/block/ioctl.c
index 4476e0e85d16..4a85096f5410 100644
--- a/block/ioctl.c
+++ b/block/ioctl.c
@@ -41,7 +41,7 @@ static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user
sizeof(long long) > sizeof(long)) {
long pstart = start, plength = length;
if (pstart != start || plength != length
- || pstart < 0 || plength < 0)
+ || pstart < 0 || plength < 0 || partno > 65535)
return -EINVAL;
}
diff --git a/block/partitions/ibm.c b/block/partitions/ibm.c
index 1104acac780b..47a61474e795 100644
--- a/block/partitions/ibm.c
+++ b/block/partitions/ibm.c
@@ -1,9 +1,8 @@
/*
- * File...........: linux/fs/partitions/ibm.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Volker Sameske <sameske@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
+ * Copyright IBM Corp. 1999, 2012
*/
#include <linux/buffer_head.h>
@@ -17,17 +16,23 @@
#include "check.h"
#include "ibm.h"
+
+union label_t {
+ struct vtoc_volume_label_cdl vol;
+ struct vtoc_volume_label_ldl lnx;
+ struct vtoc_cms_label cms;
+};
+
/*
* compute the block number from a
* cyl-cyl-head-head structure
*/
-static sector_t
-cchh2blk (struct vtoc_cchh *ptr, struct hd_geometry *geo) {
-
+static sector_t cchh2blk(struct vtoc_cchh *ptr, struct hd_geometry *geo)
+{
sector_t cyl;
__u16 head;
- /*decode cylinder and heads for large volumes */
+ /* decode cylinder and heads for large volumes */
cyl = ptr->hh & 0xFFF0;
cyl <<= 12;
cyl |= ptr->cc;
@@ -40,13 +45,12 @@ cchh2blk (struct vtoc_cchh *ptr, struct hd_geometry *geo) {
* compute the block number from a
* cyl-cyl-head-head-block structure
*/
-static sector_t
-cchhb2blk (struct vtoc_cchhb *ptr, struct hd_geometry *geo) {
-
+static sector_t cchhb2blk(struct vtoc_cchhb *ptr, struct hd_geometry *geo)
+{
sector_t cyl;
__u16 head;
- /*decode cylinder and heads for large volumes */
+ /* decode cylinder and heads for large volumes */
cyl = ptr->hh & 0xFFF0;
cyl <<= 12;
cyl |= ptr->cc;
@@ -56,26 +60,243 @@ cchhb2blk (struct vtoc_cchhb *ptr, struct hd_geometry *geo) {
ptr->b;
}
+static int find_label(struct parsed_partitions *state,
+ dasd_information2_t *info,
+ struct hd_geometry *geo,
+ int blocksize,
+ sector_t *labelsect,
+ char name[],
+ char type[],
+ union label_t *label)
+{
+ Sector sect;
+ unsigned char *data;
+ sector_t testsect[3];
+ unsigned char temp[5];
+ int found = 0;
+ int i, testcount;
+
+ /* There a three places where we may find a valid label:
+ * - on an ECKD disk it's block 2
+ * - on an FBA disk it's block 1
+ * - on an CMS formatted FBA disk it is sector 1, even if the block size
+ * is larger than 512 bytes (possible if the DIAG discipline is used)
+ * If we have a valid info structure, then we know exactly which case we
+ * have, otherwise we just search through all possebilities.
+ */
+ if (info) {
+ if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) ||
+ (info->cu_type == 0x3880 && info->dev_type == 0x3370))
+ testsect[0] = info->label_block;
+ else
+ testsect[0] = info->label_block * (blocksize >> 9);
+ testcount = 1;
+ } else {
+ testsect[0] = 1;
+ testsect[1] = (blocksize >> 9);
+ testsect[2] = 2 * (blocksize >> 9);
+ testcount = 3;
+ }
+ for (i = 0; i < testcount; ++i) {
+ data = read_part_sector(state, testsect[i], &sect);
+ if (data == NULL)
+ continue;
+ memcpy(label, data, sizeof(*label));
+ memcpy(temp, data, 4);
+ temp[4] = 0;
+ EBCASC(temp, 4);
+ put_dev_sector(sect);
+ if (!strcmp(temp, "VOL1") ||
+ !strcmp(temp, "LNX1") ||
+ !strcmp(temp, "CMS1")) {
+ if (!strcmp(temp, "VOL1")) {
+ strncpy(type, label->vol.vollbl, 4);
+ strncpy(name, label->vol.volid, 6);
+ } else {
+ strncpy(type, label->lnx.vollbl, 4);
+ strncpy(name, label->lnx.volid, 6);
+ }
+ EBCASC(type, 4);
+ EBCASC(name, 6);
+ *labelsect = testsect[i];
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ memset(label, 0, sizeof(*label));
+
+ return found;
+}
+
+static int find_vol1_partitions(struct parsed_partitions *state,
+ struct hd_geometry *geo,
+ int blocksize,
+ char name[],
+ union label_t *label)
+{
+ sector_t blk;
+ int counter;
+ char tmp[64];
+ Sector sect;
+ unsigned char *data;
+ loff_t offset, size;
+ struct vtoc_format1_label f1;
+ int secperblk;
+
+ snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
+ /*
+ * get start of VTOC from the disk label and then search for format1
+ * and format8 labels
+ */
+ secperblk = blocksize >> 9;
+ blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
+ counter = 0;
+ data = read_part_sector(state, blk * secperblk, &sect);
+ while (data != NULL) {
+ memcpy(&f1, data, sizeof(struct vtoc_format1_label));
+ put_dev_sector(sect);
+ /* skip FMT4 / FMT5 / FMT7 labels */
+ if (f1.DS1FMTID == _ascebc['4']
+ || f1.DS1FMTID == _ascebc['5']
+ || f1.DS1FMTID == _ascebc['7']
+ || f1.DS1FMTID == _ascebc['9']) {
+ blk++;
+ data = read_part_sector(state, blk * secperblk, &sect);
+ continue;
+ }
+ /* only FMT1 and 8 labels valid at this point */
+ if (f1.DS1FMTID != _ascebc['1'] &&
+ f1.DS1FMTID != _ascebc['8'])
+ break;
+ /* OK, we got valid partition data */
+ offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
+ size = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
+ offset + geo->sectors;
+ offset *= secperblk;
+ size *= secperblk;
+ if (counter >= state->limit)
+ break;
+ put_partition(state, counter + 1, offset, size);
+ counter++;
+ blk++;
+ data = read_part_sector(state, blk * secperblk, &sect);
+ }
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
+
+ if (!data)
+ return -1;
+
+ return 1;
+}
+
+static int find_lnx1_partitions(struct parsed_partitions *state,
+ struct hd_geometry *geo,
+ int blocksize,
+ char name[],
+ union label_t *label,
+ sector_t labelsect,
+ loff_t i_size,
+ dasd_information2_t *info)
+{
+ loff_t offset, geo_size, size;
+ char tmp[64];
+ int secperblk;
+
+ snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
+ secperblk = blocksize >> 9;
+ if (label->lnx.ldl_version == 0xf2) {
+ size = label->lnx.formatted_blocks * secperblk;
+ } else {
+ /*
+ * Formated w/o large volume support. If the sanity check
+ * 'size based on geo == size based on i_size' is true, then
+ * we can safely assume that we know the formatted size of
+ * the disk, otherwise we need additional information
+ * that we can only get from a real DASD device.
+ */
+ geo_size = geo->cylinders * geo->heads
+ * geo->sectors * secperblk;
+ size = i_size >> 9;
+ if (size != geo_size) {
+ if (!info) {
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
+ return 1;
+ }
+ if (!strcmp(info->type, "ECKD"))
+ if (geo_size < size)
+ size = geo_size;
+ /* else keep size based on i_size */
+ }
+ }
+ /* first and only partition starts in the first block after the label */
+ offset = labelsect + secperblk;
+ put_partition(state, 1, offset, size - offset);
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
+ return 1;
+}
+
+static int find_cms1_partitions(struct parsed_partitions *state,
+ struct hd_geometry *geo,
+ int blocksize,
+ char name[],
+ union label_t *label,
+ sector_t labelsect)
+{
+ loff_t offset, size;
+ char tmp[64];
+ int secperblk;
+
+ /*
+ * VM style CMS1 labeled disk
+ */
+ blocksize = label->cms.block_size;
+ secperblk = blocksize >> 9;
+ if (label->cms.disk_offset != 0) {
+ snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
+ /* disk is reserved minidisk */
+ offset = label->cms.disk_offset * secperblk;
+ size = (label->cms.block_count - 1) * secperblk;
+ } else {
+ snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name);
+ strlcat(state->pp_buf, tmp, PAGE_SIZE);
+ /*
+ * Special case for FBA devices:
+ * If an FBA device is CMS formatted with blocksize > 512 byte
+ * and the DIAG discipline is used, then the CMS label is found
+ * in sector 1 instead of block 1. However, the partition is
+ * still supposed to start in block 2.
+ */
+ if (labelsect == 1)
+ offset = 2 * secperblk;
+ else
+ offset = labelsect + secperblk;
+ size = label->cms.block_count * secperblk;
+ }
+
+ put_partition(state, 1, offset, size-offset);
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
+ return 1;
+}
+
+
/*
+ * This is the main function, called by check.c
*/
int ibm_partition(struct parsed_partitions *state)
{
struct block_device *bdev = state->bdev;
int blocksize, res;
- loff_t i_size, offset, size, fmt_size;
+ loff_t i_size, offset, size;
dasd_information2_t *info;
struct hd_geometry *geo;
char type[5] = {0,};
char name[7] = {0,};
- union label_t {
- struct vtoc_volume_label_cdl vol;
- struct vtoc_volume_label_ldl lnx;
- struct vtoc_cms_label cms;
- } *label;
- unsigned char *data;
- Sector sect;
sector_t labelsect;
- char tmp[64];
+ union label_t *label;
res = 0;
blocksize = bdev_logical_block_size(bdev);
@@ -84,7 +305,6 @@ int ibm_partition(struct parsed_partitions *state)
i_size = i_size_read(bdev->bd_inode);
if (i_size == 0)
goto out_exit;
-
info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL);
if (info == NULL)
goto out_exit;
@@ -94,176 +314,45 @@ int ibm_partition(struct parsed_partitions *state)
label = kmalloc(sizeof(union label_t), GFP_KERNEL);
if (label == NULL)
goto out_nolab;
-
- if (ioctl_by_bdev(bdev, BIODASDINFO2, (unsigned long)info) != 0 ||
- ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
+ if (ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
goto out_freeall;
-
- /*
- * Special case for FBA disks: label sector does not depend on
- * blocksize.
- */
- if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) ||
- (info->cu_type == 0x3880 && info->dev_type == 0x3370))
- labelsect = info->label_block;
- else
- labelsect = info->label_block * (blocksize >> 9);
-
- /*
- * Get volume label, extract name and type.
- */
- data = read_part_sector(state, labelsect, &sect);
- if (data == NULL)
- goto out_readerr;
-
- memcpy(label, data, sizeof(union label_t));
- put_dev_sector(sect);
-
- if ((!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) {
- strncpy(type, label->vol.vollbl, 4);
- strncpy(name, label->vol.volid, 6);
- } else {
- strncpy(type, label->lnx.vollbl, 4);
- strncpy(name, label->lnx.volid, 6);
+ if (ioctl_by_bdev(bdev, BIODASDINFO2, (unsigned long)info) != 0) {
+ kfree(info);
+ info = NULL;
}
- EBCASC(type, 4);
- EBCASC(name, 6);
-
- res = 1;
- /*
- * Three different formats: LDL, CDL and unformated disk
- *
- * identified by info->format
- *
- * unformated disks we do not have to care about
- */
- if (info->format == DASD_FORMAT_LDL) {
- if (strncmp(type, "CMS1", 4) == 0) {
- /*
- * VM style CMS1 labeled disk
- */
- blocksize = label->cms.block_size;
- if (label->cms.disk_offset != 0) {
- snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name);
- strlcat(state->pp_buf, tmp, PAGE_SIZE);
- /* disk is reserved minidisk */
- offset = label->cms.disk_offset;
- size = (label->cms.block_count - 1)
- * (blocksize >> 9);
- } else {
- snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name);
- strlcat(state->pp_buf, tmp, PAGE_SIZE);
- offset = (info->label_block + 1);
- size = label->cms.block_count
- * (blocksize >> 9);
- }
- put_partition(state, 1, offset*(blocksize >> 9),
- size-offset*(blocksize >> 9));
- } else {
- if (strncmp(type, "LNX1", 4) == 0) {
- snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name);
- strlcat(state->pp_buf, tmp, PAGE_SIZE);
- if (label->lnx.ldl_version == 0xf2) {
- fmt_size = label->lnx.formatted_blocks
- * (blocksize >> 9);
- } else if (!strcmp(info->type, "ECKD")) {
- /* formated w/o large volume support */
- fmt_size = geo->cylinders * geo->heads
- * geo->sectors * (blocksize >> 9);
- } else {
- /* old label and no usable disk geometry
- * (e.g. DIAG) */
- fmt_size = i_size >> 9;
- }
- size = i_size >> 9;
- if (fmt_size < size)
- size = fmt_size;
- offset = (info->label_block + 1);
- } else {
- /* unlabeled disk */
- strlcat(state->pp_buf, "(nonl)", PAGE_SIZE);
- size = i_size >> 9;
- offset = (info->label_block + 1);
- }
- put_partition(state, 1, offset*(blocksize >> 9),
- size-offset*(blocksize >> 9));
+ if (find_label(state, info, geo, blocksize, &labelsect, name, type,
+ label)) {
+ if (!strncmp(type, "VOL1", 4)) {
+ res = find_vol1_partitions(state, geo, blocksize, name,
+ label);
+ } else if (!strncmp(type, "LNX1", 4)) {
+ res = find_lnx1_partitions(state, geo, blocksize, name,
+ label, labelsect, i_size,
+ info);
+ } else if (!strncmp(type, "CMS1", 4)) {
+ res = find_cms1_partitions(state, geo, blocksize, name,
+ label, labelsect);
}
- } else if (info->format == DASD_FORMAT_CDL) {
- /*
- * New style CDL formatted disk
- */
- sector_t blk;
- int counter;
-
+ } else if (info) {
/*
- * check if VOL1 label is available
- * if not, something is wrong, skipping partition detection
+ * ugly but needed for backward compatibility:
+ * If the block device is a DASD (i.e. BIODASDINFO2 works),
+ * then we claim it in any case, even though it has no valid
+ * label. If it has the LDL format, then we simply define a
+ * partition as if it had an LNX1 label.
*/
- if (strncmp(type, "VOL1", 4) == 0) {
- snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name);
- strlcat(state->pp_buf, tmp, PAGE_SIZE);
- /*
- * get block number and read then go through format1
- * labels
- */
- blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
- counter = 0;
- data = read_part_sector(state, blk * (blocksize/512),
- &sect);
- while (data != NULL) {
- struct vtoc_format1_label f1;
-
- memcpy(&f1, data,
- sizeof(struct vtoc_format1_label));
- put_dev_sector(sect);
-
- /* skip FMT4 / FMT5 / FMT7 labels */
- if (f1.DS1FMTID == _ascebc['4']
- || f1.DS1FMTID == _ascebc['5']
- || f1.DS1FMTID == _ascebc['7']
- || f1.DS1FMTID == _ascebc['9']) {
- blk++;
- data = read_part_sector(state,
- blk * (blocksize/512), &sect);
- continue;
- }
-
- /* only FMT1 and 8 labels valid at this point */
- if (f1.DS1FMTID != _ascebc['1'] &&
- f1.DS1FMTID != _ascebc['8'])
- break;
-
- /* OK, we got valid partition data */
- offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
- size = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
- offset + geo->sectors;
- if (counter >= state->limit)
- break;
- put_partition(state, counter + 1,
- offset * (blocksize >> 9),
- size * (blocksize >> 9));
- counter++;
- blk++;
- data = read_part_sector(state,
- blk * (blocksize/512), &sect);
- }
-
- if (!data)
- /* Are we not supposed to report this ? */
- goto out_readerr;
- } else
- printk(KERN_INFO "Expected Label VOL1 not "
- "found, treating as CDL formated Disk");
-
- }
-
- strlcat(state->pp_buf, "\n", PAGE_SIZE);
- goto out_freeall;
-
+ res = 1;
+ if (info->format == DASD_FORMAT_LDL) {
+ strlcat(state->pp_buf, "(nonl)", PAGE_SIZE);
+ size = i_size >> 9;
+ offset = (info->label_block + 1) * (blocksize >> 9);
+ put_partition(state, 1, offset, size-offset);
+ strlcat(state->pp_buf, "\n", PAGE_SIZE);
+ }
+ } else
+ res = 0;
-out_readerr:
- res = -1;
out_freeall:
kfree(label);
out_nolab:
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 36d3daa19a74..dbdefa3fe775 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -2,6 +2,8 @@ menu "Device Drivers"
source "drivers/base/Kconfig"
+source "drivers/bus/Kconfig"
+
source "drivers/connector/Kconfig"
source "drivers/mtd/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 8c30e73cd94c..acb48fa4531c 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -6,6 +6,7 @@
#
obj-y += irqchip/
+obj-y += bus/
# GPIO must come after pinctrl as gpios may need to mux pins etc
obj-y += pinctrl/
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 80998958cf45..119d58db8342 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -385,8 +385,8 @@ config ACPI_CUSTOM_METHOD
to override that restriction).
config ACPI_BGRT
- tristate "Boottime Graphics Resource Table support"
- default n
+ bool "Boottime Graphics Resource Table support"
+ depends on EFI
help
This driver adds support for exposing the ACPI Boottime Graphics
Resource Table, which allows the operating system to obtain
diff --git a/drivers/acpi/bgrt.c b/drivers/acpi/bgrt.c
index 6680df36b963..be6039958545 100644
--- a/drivers/acpi/bgrt.c
+++ b/drivers/acpi/bgrt.c
@@ -1,5 +1,6 @@
/*
* Copyright 2012 Red Hat, Inc <mjg@redhat.com>
+ * Copyright 2012 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,20 +12,10 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/sysfs.h>
-#include <linux/io.h>
-#include <acpi/acpi.h>
-#include <acpi/acpi_bus.h>
+#include <linux/efi-bgrt.h>
-static struct acpi_table_bgrt *bgrt_tab;
static struct kobject *bgrt_kobj;
-struct bmp_header {
- u16 id;
- u32 size;
-} __attribute ((packed));
-
-static struct bmp_header bmp_header;
-
static ssize_t show_version(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -63,18 +54,7 @@ static DEVICE_ATTR(yoffset, S_IRUGO, show_yoffset, NULL);
static ssize_t show_image(struct file *file, struct kobject *kobj,
struct bin_attribute *attr, char *buf, loff_t off, size_t count)
{
- int size = attr->size;
- void __iomem *image = attr->private;
-
- if (off >= size) {
- count = 0;
- } else {
- if (off + count > size)
- count = size - off;
-
- memcpy_fromio(buf, image+off, count);
- }
-
+ memcpy(buf, attr->private + off, count);
return count;
}
@@ -101,45 +81,18 @@ static struct attribute_group bgrt_attribute_group = {
static int __init bgrt_init(void)
{
- acpi_status status;
int ret;
- void __iomem *bgrt;
- if (acpi_disabled)
- return -ENODEV;
-
- status = acpi_get_table("BGRT", 0,
- (struct acpi_table_header **)&bgrt_tab);
-
- if (ACPI_FAILURE(status))
+ if (!bgrt_image)
return -ENODEV;
sysfs_bin_attr_init(&image_attr);
-
- bgrt = ioremap(bgrt_tab->image_address, sizeof(struct bmp_header));
-
- if (!bgrt) {
- ret = -EINVAL;
- goto out_err;
- }
-
- memcpy_fromio(&bmp_header, bgrt, sizeof(bmp_header));
- image_attr.size = bmp_header.size;
- iounmap(bgrt);
-
- image_attr.private = ioremap(bgrt_tab->image_address, image_attr.size);
-
- if (!image_attr.private) {
- ret = -EINVAL;
- goto out_err;
- }
-
+ image_attr.private = bgrt_image;
+ image_attr.size = bgrt_image_size;
bgrt_kobj = kobject_create_and_add("bgrt", acpi_kobj);
- if (!bgrt_kobj) {
- ret = -EINVAL;
- goto out_iounmap;
- }
+ if (!bgrt_kobj)
+ return -EINVAL;
ret = sysfs_create_group(bgrt_kobj, &bgrt_attribute_group);
if (ret)
@@ -155,22 +108,11 @@ out_group:
sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group);
out_kobject:
kobject_put(bgrt_kobj);
-out_iounmap:
- iounmap(image_attr.private);
-out_err:
return ret;
}
-static void __exit bgrt_exit(void)
-{
- iounmap(image_attr.private);
- sysfs_remove_group(bgrt_kobj, &bgrt_attribute_group);
- sysfs_remove_bin_file(bgrt_kobj, &image_attr);
-}
-
module_init(bgrt_init);
-module_exit(bgrt_exit);
-MODULE_AUTHOR("Matthew Garrett");
+MODULE_AUTHOR("Matthew Garrett, Josh Triplett <josh@joshtriplett.org>");
MODULE_DESCRIPTION("BGRT boot graphic support");
MODULE_LICENSE("GPL");
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 72a2c98bc429..bce469c0b48a 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -27,7 +27,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/pci.h>
@@ -71,9 +71,11 @@ static struct acpi_driver acpi_pci_root_driver = {
},
};
+/* Lock to protect both acpi_pci_roots and acpi_pci_drivers lists */
+static DEFINE_MUTEX(acpi_pci_root_lock);
static LIST_HEAD(acpi_pci_roots);
+static LIST_HEAD(acpi_pci_drivers);
-static struct acpi_pci_driver *sub_driver;
static DEFINE_MUTEX(osc_lock);
int acpi_pci_register_driver(struct acpi_pci_driver *driver)
@@ -81,55 +83,46 @@ int acpi_pci_register_driver(struct acpi_pci_driver *driver)
int n = 0;
struct acpi_pci_root *root;
- struct acpi_pci_driver **pptr = &sub_driver;
- while (*pptr)
- pptr = &(*pptr)->next;
- *pptr = driver;
-
- if (!driver->add)
- return 0;
-
- list_for_each_entry(root, &acpi_pci_roots, node) {
- driver->add(root->device->handle);
- n++;
- }
+ mutex_lock(&acpi_pci_root_lock);
+ list_add_tail(&driver->node, &acpi_pci_drivers);
+ if (driver->add)
+ list_for_each_entry(root, &acpi_pci_roots, node) {
+ driver->add(root);
+ n++;
+ }
+ mutex_unlock(&acpi_pci_root_lock);
return n;
}
-
EXPORT_SYMBOL(acpi_pci_register_driver);
void acpi_pci_unregister_driver(struct acpi_pci_driver *driver)
{
struct acpi_pci_root *root;
- struct acpi_pci_driver **pptr = &sub_driver;
- while (*pptr) {
- if (*pptr == driver)
- break;
- pptr = &(*pptr)->next;
- }
- BUG_ON(!*pptr);
- *pptr = (*pptr)->next;
-
- if (!driver->remove)
- return;
-
- list_for_each_entry(root, &acpi_pci_roots, node)
- driver->remove(root->device->handle);
+ mutex_lock(&acpi_pci_root_lock);
+ list_del(&driver->node);
+ if (driver->remove)
+ list_for_each_entry(root, &acpi_pci_roots, node)
+ driver->remove(root);
+ mutex_unlock(&acpi_pci_root_lock);
}
-
EXPORT_SYMBOL(acpi_pci_unregister_driver);
acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus)
{
struct acpi_pci_root *root;
+ acpi_handle handle = NULL;
+ mutex_lock(&acpi_pci_root_lock);
list_for_each_entry(root, &acpi_pci_roots, node)
if ((root->segment == (u16) seg) &&
- (root->secondary.start == (u16) bus))
- return root->device->handle;
- return NULL;
+ (root->secondary.start == (u16) bus)) {
+ handle = root->device->handle;
+ break;
+ }
+ mutex_unlock(&acpi_pci_root_lock);
+ return handle;
}
EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle);
@@ -277,12 +270,15 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags)
struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle)
{
struct acpi_pci_root *root;
+ struct acpi_device *device;
- list_for_each_entry(root, &acpi_pci_roots, node) {
- if (root->device->handle == handle)
- return root;
- }
- return NULL;
+ if (acpi_bus_get_device(handle, &device) ||
+ acpi_match_device_ids(device, root_device_ids))
+ return NULL;
+
+ root = acpi_driver_data(device);
+
+ return root;
}
EXPORT_SYMBOL_GPL(acpi_pci_find_root);
@@ -518,8 +514,9 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
* TBD: Need PCI interface for enumeration/configuration of roots.
*/
- /* TBD: Locking */
+ mutex_lock(&acpi_pci_root_lock);
list_add_tail(&root->node, &acpi_pci_roots);
+ mutex_unlock(&acpi_pci_root_lock);
printk(KERN_INFO PREFIX "%s [%s] (domain %04x %pR)\n",
acpi_device_name(device), acpi_device_bid(device),
@@ -538,7 +535,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
"Bus %04x:%02x not present in PCI namespace\n",
root->segment, (unsigned int)root->secondary.start);
result = -ENODEV;
- goto end;
+ goto out_del_root;
}
/*
@@ -548,7 +545,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
*/
result = acpi_pci_bind_root(device);
if (result)
- goto end;
+ goto out_del_root;
/*
* PCI Routing Table
@@ -633,9 +630,11 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
return 0;
+out_del_root:
+ mutex_lock(&acpi_pci_root_lock);
+ list_del(&root->node);
+ mutex_unlock(&acpi_pci_root_lock);
end:
- if (!list_empty(&root->node))
- list_del(&root->node);
kfree(root);
return result;
}
@@ -643,18 +642,34 @@ end:
static int acpi_pci_root_start(struct acpi_device *device)
{
struct acpi_pci_root *root = acpi_driver_data(device);
+ struct acpi_pci_driver *driver;
+
+ mutex_lock(&acpi_pci_root_lock);
+ list_for_each_entry(driver, &acpi_pci_drivers, node)
+ if (driver->add)
+ driver->add(root);
+ mutex_unlock(&acpi_pci_root_lock);
pci_bus_add_devices(root->bus);
+
return 0;
}
static int acpi_pci_root_remove(struct acpi_device *device, int type)
{
struct acpi_pci_root *root = acpi_driver_data(device);
+ struct acpi_pci_driver *driver;
+
+ mutex_lock(&acpi_pci_root_lock);
+ list_for_each_entry(driver, &acpi_pci_drivers, node)
+ if (driver->remove)
+ driver->remove(root);
device_set_run_wake(root->bus->bridge, false);
pci_acpi_remove_bus_pm_notifier(device);
+ list_del(&root->node);
+ mutex_unlock(&acpi_pci_root_lock);
kfree(root);
return 0;
}
diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c
index e50e31a518af..d22585f21aeb 100644
--- a/drivers/acpi/pci_slot.c
+++ b/drivers/acpi/pci_slot.c
@@ -67,8 +67,8 @@ struct acpi_pci_slot {
struct list_head list; /* node in the list of slots */
};
-static int acpi_pci_slot_add(acpi_handle handle);
-static void acpi_pci_slot_remove(acpi_handle handle);
+static int acpi_pci_slot_add(struct acpi_pci_root *root);
+static void acpi_pci_slot_remove(struct acpi_pci_root *root);
static LIST_HEAD(slot_list);
static DEFINE_MUTEX(slot_list_lock);
@@ -233,45 +233,20 @@ out:
/*
* walk_root_bridge - generic root bridge walker
- * @handle: points to an acpi_pci_root
+ * @root: poiner of an acpi_pci_root
* @user_function: user callback for slot objects
*
* Call user_function for all objects underneath this root bridge.
* Walk p2p bridges underneath us and call user_function on those too.
*/
static int
-walk_root_bridge(acpi_handle handle, acpi_walk_callback user_function)
+walk_root_bridge(struct acpi_pci_root *root, acpi_walk_callback user_function)
{
- int seg, bus;
- unsigned long long tmp;
acpi_status status;
- acpi_handle dummy_handle;
- struct pci_bus *pci_bus;
+ acpi_handle handle = root->device->handle;
+ struct pci_bus *pci_bus = root->bus;
struct callback_args context;
- /* If the bridge doesn't have _STA, we assume it is always there */
- status = acpi_get_handle(handle, "_STA", &dummy_handle);
- if (ACPI_SUCCESS(status)) {
- status = acpi_evaluate_integer(handle, "_STA", NULL, &tmp);
- if (ACPI_FAILURE(status)) {
- info("%s: _STA evaluation failure\n", __func__);
- return 0;
- }
- if ((tmp & ACPI_STA_DEVICE_FUNCTIONING) == 0)
- /* don't register this object */
- return 0;
- }
-
- status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp);
- seg = ACPI_SUCCESS(status) ? tmp : 0;
-
- status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp);
- bus = ACPI_SUCCESS(status) ? tmp : 0;
-
- pci_bus = pci_find_bus(seg, bus);
- if (!pci_bus)
- return 0;
-
context.pci_bus = pci_bus;
context.user_function = user_function;
context.root_handle = handle;
@@ -295,11 +270,11 @@ walk_root_bridge(acpi_handle handle, acpi_walk_callback user_function)
* @handle: points to an acpi_pci_root
*/
static int
-acpi_pci_slot_add(acpi_handle handle)
+acpi_pci_slot_add(struct acpi_pci_root *root)
{
acpi_status status;
- status = walk_root_bridge(handle, register_slot);
+ status = walk_root_bridge(root, register_slot);
if (ACPI_FAILURE(status))
err("%s: register_slot failure - %d\n", __func__, status);
@@ -311,10 +286,11 @@ acpi_pci_slot_add(acpi_handle handle)
* @handle: points to an acpi_pci_root
*/
static void
-acpi_pci_slot_remove(acpi_handle handle)
+acpi_pci_slot_remove(struct acpi_pci_root *root)
{
struct acpi_pci_slot *slot, *tmp;
struct pci_bus *pbus;
+ acpi_handle handle = root->device->handle;
mutex_lock(&slot_list_lock);
list_for_each_entry_safe(slot, tmp, &slot_list, list) {
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 5e6e00bc1652..abea76c36a4b 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -184,6 +184,17 @@ static void device_release(struct kobject *kobj)
struct device *dev = kobj_to_dev(kobj);
struct device_private *p = dev->p;
+ /*
+ * Some platform devices are driven without driver attached
+ * and managed resources may have been acquired. Make sure
+ * all resources are released.
+ *
+ * Drivers still can add resources into device after device
+ * is deleted but alive, so release devres here to avoid
+ * possible memory leak.
+ */
+ devres_release_all(dev);
+
if (dev->release)
dev->release(dev);
else if (dev->type && dev->type->release)
@@ -1196,13 +1207,6 @@ void device_del(struct device *dev)
bus_remove_device(dev);
driver_deferred_probe_del(dev);
- /*
- * Some platform devices are driven without driver attached
- * and managed resources may have been acquired. Make sure
- * all resources are released.
- */
- devres_release_all(dev);
-
/* Notify the platform of the removal, in case they
* need to do anything...
*/
@@ -1861,26 +1865,20 @@ void device_shutdown(void)
*/
#ifdef CONFIG_PRINTK
-int __dev_printk(const char *level, const struct device *dev,
- struct va_format *vaf)
+static int
+create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen)
{
- char dict[128];
- const char *level_extra = "";
- size_t dictlen = 0;
const char *subsys;
-
- if (!dev)
- return printk("%s(NULL device *): %pV", level, vaf);
+ size_t pos = 0;
if (dev->class)
subsys = dev->class->name;
else if (dev->bus)
subsys = dev->bus->name;
else
- goto skip;
+ return 0;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "SUBSYSTEM=%s", subsys);
+ pos += snprintf(hdr + pos, hdrlen - pos, "SUBSYSTEM=%s", subsys);
/*
* Add device identifier DEVICE=:
@@ -1896,32 +1894,63 @@ int __dev_printk(const char *level, const struct device *dev,
c = 'b';
else
c = 'c';
- dictlen++;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "DEVICE=%c%u:%u",
- c, MAJOR(dev->devt), MINOR(dev->devt));
+ pos++;
+ pos += snprintf(hdr + pos, hdrlen - pos,
+ "DEVICE=%c%u:%u",
+ c, MAJOR(dev->devt), MINOR(dev->devt));
} else if (strcmp(subsys, "net") == 0) {
struct net_device *net = to_net_dev(dev);
- dictlen++;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "DEVICE=n%u", net->ifindex);
+ pos++;
+ pos += snprintf(hdr + pos, hdrlen - pos,
+ "DEVICE=n%u", net->ifindex);
} else {
- dictlen++;
- dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
- "DEVICE=+%s:%s", subsys, dev_name(dev));
+ pos++;
+ pos += snprintf(hdr + pos, hdrlen - pos,
+ "DEVICE=+%s:%s", subsys, dev_name(dev));
}
-skip:
- if (level[2])
- level_extra = &level[2]; /* skip past KERN_SOH "L" */
- return printk_emit(0, level[1] - '0',
- dictlen ? dict : NULL, dictlen,
- "%s %s: %s%pV",
- dev_driver_string(dev), dev_name(dev),
- level_extra, vaf);
+ return pos;
+}
+EXPORT_SYMBOL(create_syslog_header);
+
+int dev_vprintk_emit(int level, const struct device *dev,
+ const char *fmt, va_list args)
+{
+ char hdr[128];
+ size_t hdrlen;
+
+ hdrlen = create_syslog_header(dev, hdr, sizeof(hdr));
+
+ return vprintk_emit(0, level, hdrlen ? hdr : NULL, hdrlen, fmt, args);
+}
+EXPORT_SYMBOL(dev_vprintk_emit);
+
+int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...)
+{
+ va_list args;
+ int r;
+
+ va_start(args, fmt);
+
+ r = dev_vprintk_emit(level, dev, fmt, args);
+
+ va_end(args);
+
+ return r;
+}
+EXPORT_SYMBOL(dev_printk_emit);
+
+static int __dev_printk(const char *level, const struct device *dev,
+ struct va_format *vaf)
+{
+ if (!dev)
+ return printk("%s(NULL device *): %pV", level, vaf);
+
+ return dev_printk_emit(level[1] - '0', dev,
+ "%s %s: %pV",
+ dev_driver_string(dev), dev_name(dev), vaf);
}
-EXPORT_SYMBOL(__dev_printk);
int dev_printk(const char *level, const struct device *dev,
const char *fmt, ...)
@@ -1936,6 +1965,7 @@ int dev_printk(const char *level, const struct device *dev,
vaf.va = &args;
r = __dev_printk(level, dev, &vaf);
+
va_end(args);
return r;
@@ -1955,6 +1985,7 @@ int func(const struct device *dev, const char *fmt, ...) \
vaf.va = &args; \
\
r = __dev_printk(kern_level, dev, &vaf); \
+ \
va_end(args); \
\
return r; \
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index 2360adb7a58f..8731979d668a 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -144,6 +144,48 @@ EXPORT_SYMBOL_GPL(devres_alloc);
#endif
/**
+ * devres_for_each_res - Resource iterator
+ * @dev: Device to iterate resource from
+ * @release: Look for resources associated with this release function
+ * @match: Match function (optional)
+ * @match_data: Data for the match function
+ * @fn: Function to be called for each matched resource.
+ * @data: Data for @fn, the 3rd parameter of @fn
+ *
+ * Call @fn for each devres of @dev which is associated with @release
+ * and for which @match returns 1.
+ *
+ * RETURNS:
+ * void
+ */
+void devres_for_each_res(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data,
+ void (*fn)(struct device *, void *, void *),
+ void *data)
+{
+ struct devres_node *node;
+ struct devres_node *tmp;
+ unsigned long flags;
+
+ if (!fn)
+ return;
+
+ spin_lock_irqsave(&dev->devres_lock, flags);
+ list_for_each_entry_safe_reverse(node, tmp,
+ &dev->devres_head, entry) {
+ struct devres *dr = container_of(node, struct devres, node);
+
+ if (node->release != release)
+ continue;
+ if (match && !match(dev, dr->data, match_data))
+ continue;
+ fn(dev, dr->data, data);
+ }
+ spin_unlock_irqrestore(&dev->devres_lock, flags);
+}
+EXPORT_SYMBOL_GPL(devres_for_each_res);
+
+/**
* devres_free - Free device resource data
* @res: Pointer to devres data to free
*
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 803cfc1597a9..6e210802c37b 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -21,6 +21,13 @@
#include <linux/firmware.h>
#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/async.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <linux/syscore_ops.h>
+
+#include "base.h"
MODULE_AUTHOR("Manuel Estrada Sainz");
MODULE_DESCRIPTION("Multi purpose firmware loading support");
@@ -85,23 +92,168 @@ static inline long firmware_loading_timeout(void)
return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT;
}
-/* fw_lock could be moved to 'struct firmware_priv' but since it is just
- * guarding for corner cases a global lock should be OK */
-static DEFINE_MUTEX(fw_lock);
+struct firmware_cache {
+ /* firmware_buf instance will be added into the below list */
+ spinlock_t lock;
+ struct list_head head;
+ int state;
+
+#ifdef CONFIG_PM_SLEEP
+ /*
+ * Names of firmware images which have been cached successfully
+ * will be added into the below list so that device uncache
+ * helper can trace which firmware images have been cached
+ * before.
+ */
+ spinlock_t name_lock;
+ struct list_head fw_names;
+
+ wait_queue_head_t wait_queue;
+ int cnt;
+ struct delayed_work work;
+
+ struct notifier_block pm_notify;
+#endif
+};
-struct firmware_priv {
+struct firmware_buf {
+ struct kref ref;
+ struct list_head list;
struct completion completion;
- struct firmware *fw;
+ struct firmware_cache *fwc;
unsigned long status;
+ void *data;
+ size_t size;
struct page **pages;
int nr_pages;
int page_array_size;
+ char fw_id[];
+};
+
+struct fw_cache_entry {
+ struct list_head list;
+ char name[];
+};
+
+struct firmware_priv {
struct timer_list timeout;
- struct device dev;
bool nowait;
- char fw_id[];
+ struct device dev;
+ struct firmware_buf *buf;
+ struct firmware *fw;
+};
+
+struct fw_name_devm {
+ unsigned long magic;
+ char name[];
};
+#define to_fwbuf(d) container_of(d, struct firmware_buf, ref)
+
+#define FW_LOADER_NO_CACHE 0
+#define FW_LOADER_START_CACHE 1
+
+static int fw_cache_piggyback_on_request(const char *name);
+
+/* fw_lock could be moved to 'struct firmware_priv' but since it is just
+ * guarding for corner cases a global lock should be OK */
+static DEFINE_MUTEX(fw_lock);
+
+static struct firmware_cache fw_cache;
+
+static struct firmware_buf *__allocate_fw_buf(const char *fw_name,
+ struct firmware_cache *fwc)
+{
+ struct firmware_buf *buf;
+
+ buf = kzalloc(sizeof(*buf) + strlen(fw_name) + 1 , GFP_ATOMIC);
+
+ if (!buf)
+ return buf;
+
+ kref_init(&buf->ref);
+ strcpy(buf->fw_id, fw_name);
+ buf->fwc = fwc;
+ init_completion(&buf->completion);
+
+ pr_debug("%s: fw-%s buf=%p\n", __func__, fw_name, buf);
+
+ return buf;
+}
+
+static struct firmware_buf *__fw_lookup_buf(const char *fw_name)
+{
+ struct firmware_buf *tmp;
+ struct firmware_cache *fwc = &fw_cache;
+
+ list_for_each_entry(tmp, &fwc->head, list)
+ if (!strcmp(tmp->fw_id, fw_name))
+ return tmp;
+ return NULL;
+}
+
+static int fw_lookup_and_allocate_buf(const char *fw_name,
+ struct firmware_cache *fwc,
+ struct firmware_buf **buf)
+{
+ struct firmware_buf *tmp;
+
+ spin_lock(&fwc->lock);
+ tmp = __fw_lookup_buf(fw_name);
+ if (tmp) {
+ kref_get(&tmp->ref);
+ spin_unlock(&fwc->lock);
+ *buf = tmp;
+ return 1;
+ }
+ tmp = __allocate_fw_buf(fw_name, fwc);
+ if (tmp)
+ list_add(&tmp->list, &fwc->head);
+ spin_unlock(&fwc->lock);
+
+ *buf = tmp;
+
+ return tmp ? 0 : -ENOMEM;
+}
+
+static struct firmware_buf *fw_lookup_buf(const char *fw_name)
+{
+ struct firmware_buf *tmp;
+ struct firmware_cache *fwc = &fw_cache;
+
+ spin_lock(&fwc->lock);
+ tmp = __fw_lookup_buf(fw_name);
+ spin_unlock(&fwc->lock);
+
+ return tmp;
+}
+
+static void __fw_free_buf(struct kref *ref)
+{
+ struct firmware_buf *buf = to_fwbuf(ref);
+ struct firmware_cache *fwc = buf->fwc;
+ int i;
+
+ pr_debug("%s: fw-%s buf=%p data=%p size=%u\n",
+ __func__, buf->fw_id, buf, buf->data,
+ (unsigned int)buf->size);
+
+ spin_lock(&fwc->lock);
+ list_del(&buf->list);
+ spin_unlock(&fwc->lock);
+
+ vunmap(buf->data);
+ for (i = 0; i < buf->nr_pages; i++)
+ __free_page(buf->pages[i]);
+ kfree(buf->pages);
+ kfree(buf);
+}
+
+static void fw_free_buf(struct firmware_buf *buf)
+{
+ kref_put(&buf->ref, __fw_free_buf);
+}
+
static struct firmware_priv *to_firmware_priv(struct device *dev)
{
return container_of(dev, struct firmware_priv, dev);
@@ -109,9 +261,10 @@ static struct firmware_priv *to_firmware_priv(struct device *dev)
static void fw_load_abort(struct firmware_priv *fw_priv)
{
- set_bit(FW_STATUS_ABORT, &fw_priv->status);
- wmb();
- complete(&fw_priv->completion);
+ struct firmware_buf *buf = fw_priv->buf;
+
+ set_bit(FW_STATUS_ABORT, &buf->status);
+ complete_all(&buf->completion);
}
static ssize_t firmware_timeout_show(struct class *class,
@@ -154,11 +307,7 @@ static struct class_attribute firmware_class_attrs[] = {
static void fw_dev_release(struct device *dev)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- int i;
- for (i = 0; i < fw_priv->nr_pages; i++)
- __free_page(fw_priv->pages[i]);
- kfree(fw_priv->pages);
kfree(fw_priv);
module_put(THIS_MODULE);
@@ -168,7 +317,7 @@ static int firmware_uevent(struct device *dev, struct kobj_uevent_env *env)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->fw_id))
+ if (add_uevent_var(env, "FIRMWARE=%s", fw_priv->buf->fw_id))
return -ENOMEM;
if (add_uevent_var(env, "TIMEOUT=%i", loading_timeout))
return -ENOMEM;
@@ -189,20 +338,16 @@ static ssize_t firmware_loading_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- int loading = test_bit(FW_STATUS_LOADING, &fw_priv->status);
+ int loading = test_bit(FW_STATUS_LOADING, &fw_priv->buf->status);
return sprintf(buf, "%d\n", loading);
}
+/* firmware holds the ownership of pages */
static void firmware_free_data(const struct firmware *fw)
{
- int i;
- vunmap(fw->data);
- if (fw->pages) {
- for (i = 0; i < PFN_UP(fw->size); i++)
- __free_page(fw->pages[i]);
- kfree(fw->pages);
- }
+ WARN_ON(!fw->priv);
+ fw_free_buf(fw->priv);
}
/* Some architectures don't have PAGE_KERNEL_RO */
@@ -227,45 +372,33 @@ static ssize_t firmware_loading_store(struct device *dev,
const char *buf, size_t count)
{
struct firmware_priv *fw_priv = to_firmware_priv(dev);
+ struct firmware_buf *fw_buf = fw_priv->buf;
int loading = simple_strtol(buf, NULL, 10);
int i;
mutex_lock(&fw_lock);
- if (!fw_priv->fw)
+ if (!fw_buf)
goto out;
switch (loading) {
case 1:
- firmware_free_data(fw_priv->fw);
- memset(fw_priv->fw, 0, sizeof(struct firmware));
- /* If the pages are not owned by 'struct firmware' */
- for (i = 0; i < fw_priv->nr_pages; i++)
- __free_page(fw_priv->pages[i]);
- kfree(fw_priv->pages);
- fw_priv->pages = NULL;
- fw_priv->page_array_size = 0;
- fw_priv->nr_pages = 0;
- set_bit(FW_STATUS_LOADING, &fw_priv->status);
+ /* discarding any previous partial load */
+ if (!test_bit(FW_STATUS_DONE, &fw_buf->status)) {
+ for (i = 0; i < fw_buf->nr_pages; i++)
+ __free_page(fw_buf->pages[i]);
+ kfree(fw_buf->pages);
+ fw_buf->pages = NULL;
+ fw_buf->page_array_size = 0;
+ fw_buf->nr_pages = 0;
+ set_bit(FW_STATUS_LOADING, &fw_buf->status);
+ }
break;
case 0:
- if (test_bit(FW_STATUS_LOADING, &fw_priv->status)) {
- vunmap(fw_priv->fw->data);
- fw_priv->fw->data = vmap(fw_priv->pages,
- fw_priv->nr_pages,
- 0, PAGE_KERNEL_RO);
- if (!fw_priv->fw->data) {
- dev_err(dev, "%s: vmap() failed\n", __func__);
- goto err;
- }
- /* Pages are now owned by 'struct firmware' */
- fw_priv->fw->pages = fw_priv->pages;
- fw_priv->pages = NULL;
-
- fw_priv->page_array_size = 0;
- fw_priv->nr_pages = 0;
- complete(&fw_priv->completion);
- clear_bit(FW_STATUS_LOADING, &fw_priv->status);
+ if (test_bit(FW_STATUS_LOADING, &fw_buf->status)) {
+ set_bit(FW_STATUS_DONE, &fw_buf->status);
+ clear_bit(FW_STATUS_LOADING, &fw_buf->status);
+ complete_all(&fw_buf->completion);
break;
}
/* fallthrough */
@@ -273,7 +406,6 @@ static ssize_t firmware_loading_store(struct device *dev,
dev_err(dev, "%s: unexpected value (%d)\n", __func__, loading);
/* fallthrough */
case -1:
- err:
fw_load_abort(fw_priv);
break;
}
@@ -290,21 +422,21 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
{
struct device *dev = kobj_to_dev(kobj);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- struct firmware *fw;
+ struct firmware_buf *buf;
ssize_t ret_count;
mutex_lock(&fw_lock);
- fw = fw_priv->fw;
- if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+ buf = fw_priv->buf;
+ if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
ret_count = -ENODEV;
goto out;
}
- if (offset > fw->size) {
+ if (offset > buf->size) {
ret_count = 0;
goto out;
}
- if (count > fw->size - offset)
- count = fw->size - offset;
+ if (count > buf->size - offset)
+ count = buf->size - offset;
ret_count = count;
@@ -314,11 +446,11 @@ static ssize_t firmware_data_read(struct file *filp, struct kobject *kobj,
int page_ofs = offset & (PAGE_SIZE-1);
int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
- page_data = kmap(fw_priv->pages[page_nr]);
+ page_data = kmap(buf->pages[page_nr]);
memcpy(buffer, page_data + page_ofs, page_cnt);
- kunmap(fw_priv->pages[page_nr]);
+ kunmap(buf->pages[page_nr]);
buffer += page_cnt;
offset += page_cnt;
count -= page_cnt;
@@ -330,12 +462,13 @@ out:
static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
{
+ struct firmware_buf *buf = fw_priv->buf;
int pages_needed = ALIGN(min_size, PAGE_SIZE) >> PAGE_SHIFT;
/* If the array of pages is too small, grow it... */
- if (fw_priv->page_array_size < pages_needed) {
+ if (buf->page_array_size < pages_needed) {
int new_array_size = max(pages_needed,
- fw_priv->page_array_size * 2);
+ buf->page_array_size * 2);
struct page **new_pages;
new_pages = kmalloc(new_array_size * sizeof(void *),
@@ -344,24 +477,24 @@ static int fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size)
fw_load_abort(fw_priv);
return -ENOMEM;
}
- memcpy(new_pages, fw_priv->pages,
- fw_priv->page_array_size * sizeof(void *));
- memset(&new_pages[fw_priv->page_array_size], 0, sizeof(void *) *
- (new_array_size - fw_priv->page_array_size));
- kfree(fw_priv->pages);
- fw_priv->pages = new_pages;
- fw_priv->page_array_size = new_array_size;
+ memcpy(new_pages, buf->pages,
+ buf->page_array_size * sizeof(void *));
+ memset(&new_pages[buf->page_array_size], 0, sizeof(void *) *
+ (new_array_size - buf->page_array_size));
+ kfree(buf->pages);
+ buf->pages = new_pages;
+ buf->page_array_size = new_array_size;
}
- while (fw_priv->nr_pages < pages_needed) {
- fw_priv->pages[fw_priv->nr_pages] =
+ while (buf->nr_pages < pages_needed) {
+ buf->pages[buf->nr_pages] =
alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
- if (!fw_priv->pages[fw_priv->nr_pages]) {
+ if (!buf->pages[buf->nr_pages]) {
fw_load_abort(fw_priv);
return -ENOMEM;
}
- fw_priv->nr_pages++;
+ buf->nr_pages++;
}
return 0;
}
@@ -384,18 +517,19 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
{
struct device *dev = kobj_to_dev(kobj);
struct firmware_priv *fw_priv = to_firmware_priv(dev);
- struct firmware *fw;
+ struct firmware_buf *buf;
ssize_t retval;
if (!capable(CAP_SYS_RAWIO))
return -EPERM;
mutex_lock(&fw_lock);
- fw = fw_priv->fw;
- if (!fw || test_bit(FW_STATUS_DONE, &fw_priv->status)) {
+ buf = fw_priv->buf;
+ if (!buf || test_bit(FW_STATUS_DONE, &buf->status)) {
retval = -ENODEV;
goto out;
}
+
retval = fw_realloc_buffer(fw_priv, offset + count);
if (retval)
goto out;
@@ -408,17 +542,17 @@ static ssize_t firmware_data_write(struct file *filp, struct kobject *kobj,
int page_ofs = offset & (PAGE_SIZE - 1);
int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count);
- page_data = kmap(fw_priv->pages[page_nr]);
+ page_data = kmap(buf->pages[page_nr]);
memcpy(page_data + page_ofs, buffer, page_cnt);
- kunmap(fw_priv->pages[page_nr]);
+ kunmap(buf->pages[page_nr]);
buffer += page_cnt;
offset += page_cnt;
count -= page_cnt;
}
- fw->size = max_t(size_t, offset, fw->size);
+ buf->size = max_t(size_t, offset, buf->size);
out:
mutex_unlock(&fw_lock);
return retval;
@@ -445,35 +579,120 @@ fw_create_instance(struct firmware *firmware, const char *fw_name,
struct firmware_priv *fw_priv;
struct device *f_dev;
- fw_priv = kzalloc(sizeof(*fw_priv) + strlen(fw_name) + 1 , GFP_KERNEL);
+ fw_priv = kzalloc(sizeof(*fw_priv), GFP_KERNEL);
if (!fw_priv) {
dev_err(device, "%s: kmalloc failed\n", __func__);
- return ERR_PTR(-ENOMEM);
+ fw_priv = ERR_PTR(-ENOMEM);
+ goto exit;
}
- fw_priv->fw = firmware;
fw_priv->nowait = nowait;
- strcpy(fw_priv->fw_id, fw_name);
- init_completion(&fw_priv->completion);
+ fw_priv->fw = firmware;
setup_timer(&fw_priv->timeout,
firmware_class_timeout, (u_long) fw_priv);
f_dev = &fw_priv->dev;
device_initialize(f_dev);
- dev_set_name(f_dev, "%s", dev_name(device));
+ dev_set_name(f_dev, "%s", fw_name);
f_dev->parent = device;
f_dev->class = &firmware_class;
-
+exit:
return fw_priv;
}
+/* one pages buffer is mapped/unmapped only once */
+static int fw_map_pages_buf(struct firmware_buf *buf)
+{
+ buf->data = vmap(buf->pages, buf->nr_pages, 0, PAGE_KERNEL_RO);
+ if (!buf->data)
+ return -ENOMEM;
+ return 0;
+}
+
+/* store the pages buffer info firmware from buf */
+static void fw_set_page_data(struct firmware_buf *buf, struct firmware *fw)
+{
+ fw->priv = buf;
+ fw->pages = buf->pages;
+ fw->size = buf->size;
+ fw->data = buf->data;
+
+ pr_debug("%s: fw-%s buf=%p data=%p size=%u\n",
+ __func__, buf->fw_id, buf, buf->data,
+ (unsigned int)buf->size);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static void fw_name_devm_release(struct device *dev, void *res)
+{
+ struct fw_name_devm *fwn = res;
+
+ if (fwn->magic == (unsigned long)&fw_cache)
+ pr_debug("%s: fw_name-%s devm-%p released\n",
+ __func__, fwn->name, res);
+}
+
+static int fw_devm_match(struct device *dev, void *res,
+ void *match_data)
+{
+ struct fw_name_devm *fwn = res;
+
+ return (fwn->magic == (unsigned long)&fw_cache) &&
+ !strcmp(fwn->name, match_data);
+}
+
+static struct fw_name_devm *fw_find_devm_name(struct device *dev,
+ const char *name)
+{
+ struct fw_name_devm *fwn;
+
+ fwn = devres_find(dev, fw_name_devm_release,
+ fw_devm_match, (void *)name);
+ return fwn;
+}
+
+/* add firmware name into devres list */
+static int fw_add_devm_name(struct device *dev, const char *name)
+{
+ struct fw_name_devm *fwn;
+
+ fwn = fw_find_devm_name(dev, name);
+ if (fwn)
+ return 1;
+
+ fwn = devres_alloc(fw_name_devm_release, sizeof(struct fw_name_devm) +
+ strlen(name) + 1, GFP_KERNEL);
+ if (!fwn)
+ return -ENOMEM;
+
+ fwn->magic = (unsigned long)&fw_cache;
+ strcpy(fwn->name, name);
+ devres_add(dev, fwn);
+
+ return 0;
+}
+#else
+static int fw_add_devm_name(struct device *dev, const char *name)
+{
+ return 0;
+}
+#endif
+
+static void _request_firmware_cleanup(const struct firmware **firmware_p)
+{
+ release_firmware(*firmware_p);
+ *firmware_p = NULL;
+}
+
static struct firmware_priv *
_request_firmware_prepare(const struct firmware **firmware_p, const char *name,
struct device *device, bool uevent, bool nowait)
{
struct firmware *firmware;
- struct firmware_priv *fw_priv;
+ struct firmware_priv *fw_priv = NULL;
+ struct firmware_buf *buf;
+ int ret;
if (!firmware_p)
return ERR_PTR(-EINVAL);
@@ -490,18 +709,46 @@ _request_firmware_prepare(const struct firmware **firmware_p, const char *name,
return NULL;
}
- fw_priv = fw_create_instance(firmware, name, device, uevent, nowait);
- if (IS_ERR(fw_priv)) {
- release_firmware(firmware);
+ ret = fw_lookup_and_allocate_buf(name, &fw_cache, &buf);
+ if (!ret)
+ fw_priv = fw_create_instance(firmware, name, device,
+ uevent, nowait);
+
+ if (IS_ERR(fw_priv) || ret < 0) {
+ kfree(firmware);
*firmware_p = NULL;
+ return ERR_PTR(-ENOMEM);
+ } else if (fw_priv) {
+ fw_priv->buf = buf;
+
+ /*
+ * bind with 'buf' now to avoid warning in failure path
+ * of requesting firmware.
+ */
+ firmware->priv = buf;
+ return fw_priv;
}
- return fw_priv;
-}
-static void _request_firmware_cleanup(const struct firmware **firmware_p)
-{
- release_firmware(*firmware_p);
- *firmware_p = NULL;
+ /* share the cached buf, which is inprogessing or completed */
+ check_status:
+ mutex_lock(&fw_lock);
+ if (test_bit(FW_STATUS_ABORT, &buf->status)) {
+ fw_priv = ERR_PTR(-ENOENT);
+ firmware->priv = buf;
+ _request_firmware_cleanup(firmware_p);
+ goto exit;
+ } else if (test_bit(FW_STATUS_DONE, &buf->status)) {
+ fw_priv = NULL;
+ fw_set_page_data(buf, firmware);
+ goto exit;
+ }
+ mutex_unlock(&fw_lock);
+ wait_for_completion(&buf->completion);
+ goto check_status;
+
+exit:
+ mutex_unlock(&fw_lock);
+ return fw_priv;
}
static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
@@ -509,6 +756,8 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
{
int retval = 0;
struct device *f_dev = &fw_priv->dev;
+ struct firmware_buf *buf = fw_priv->buf;
+ struct firmware_cache *fwc = &fw_cache;
dev_set_uevent_suppress(f_dev, true);
@@ -535,7 +784,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
if (uevent) {
dev_set_uevent_suppress(f_dev, false);
- dev_dbg(f_dev, "firmware: requesting %s\n", fw_priv->fw_id);
+ dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id);
if (timeout != MAX_SCHEDULE_TIMEOUT)
mod_timer(&fw_priv->timeout,
round_jiffies_up(jiffies + timeout));
@@ -543,15 +792,40 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent,
kobject_uevent(&fw_priv->dev.kobj, KOBJ_ADD);
}
- wait_for_completion(&fw_priv->completion);
+ wait_for_completion(&buf->completion);
- set_bit(FW_STATUS_DONE, &fw_priv->status);
del_timer_sync(&fw_priv->timeout);
mutex_lock(&fw_lock);
- if (!fw_priv->fw->size || test_bit(FW_STATUS_ABORT, &fw_priv->status))
+ if (!buf->size || test_bit(FW_STATUS_ABORT, &buf->status))
retval = -ENOENT;
- fw_priv->fw = NULL;
+
+ /*
+ * add firmware name into devres list so that we can auto cache
+ * and uncache firmware for device.
+ *
+ * f_dev->parent may has been deleted already, but the problem
+ * should be fixed in devres or driver core.
+ */
+ if (!retval && f_dev->parent)
+ fw_add_devm_name(f_dev->parent, buf->fw_id);
+
+ if (!retval)
+ retval = fw_map_pages_buf(buf);
+
+ /*
+ * After caching firmware image is started, let it piggyback
+ * on request firmware.
+ */
+ if (!retval && fwc->state == FW_LOADER_START_CACHE) {
+ if (fw_cache_piggyback_on_request(buf->fw_id))
+ kref_get(&buf->ref);
+ }
+
+ /* pass the pages buffer to driver at the last minute */
+ fw_set_page_data(buf, fw_priv->fw);
+
+ fw_priv->buf = NULL;
mutex_unlock(&fw_lock);
device_remove_file(f_dev, &dev_attr_loading);
@@ -578,6 +852,8 @@ err_put_dev:
* @name will be used as $FIRMWARE in the uevent environment and
* should be distinctive enough not to be confused with any other
* firmware image for this or any other device.
+ *
+ * Caller must hold the reference count of @device.
**/
int
request_firmware(const struct firmware **firmware_p, const char *name,
@@ -659,6 +935,7 @@ static void request_firmware_work_func(struct work_struct *work)
out:
fw_work->cont(fw, fw_work->context);
+ put_device(fw_work->device);
module_put(fw_work->module);
kfree(fw_work);
@@ -677,9 +954,15 @@ static void request_firmware_work_func(struct work_struct *work)
* @cont: function will be called asynchronously when the firmware
* request is over.
*
- * Asynchronous variant of request_firmware() for user contexts where
- * it is not possible to sleep for long time. It can't be called
- * in atomic contexts.
+ * Caller must hold the reference count of @device.
+ *
+ * Asynchronous variant of request_firmware() for user contexts:
+ * - sleep for as small periods as possible since it may
+ * increase kernel boot time of built-in device drivers
+ * requesting firmware in their ->probe() methods, if
+ * @gfp is GFP_KERNEL.
+ *
+ * - can't sleep at all if @gfp is GFP_ATOMIC.
**/
int
request_firmware_nowait(
@@ -705,18 +988,363 @@ request_firmware_nowait(
return -EFAULT;
}
+ get_device(fw_work->device);
INIT_WORK(&fw_work->work, request_firmware_work_func);
schedule_work(&fw_work->work);
return 0;
}
+/**
+ * cache_firmware - cache one firmware image in kernel memory space
+ * @fw_name: the firmware image name
+ *
+ * Cache firmware in kernel memory so that drivers can use it when
+ * system isn't ready for them to request firmware image from userspace.
+ * Once it returns successfully, driver can use request_firmware or its
+ * nowait version to get the cached firmware without any interacting
+ * with userspace
+ *
+ * Return 0 if the firmware image has been cached successfully
+ * Return !0 otherwise
+ *
+ */
+int cache_firmware(const char *fw_name)
+{
+ int ret;
+ const struct firmware *fw;
+
+ pr_debug("%s: %s\n", __func__, fw_name);
+
+ ret = request_firmware(&fw, fw_name, NULL);
+ if (!ret)
+ kfree(fw);
+
+ pr_debug("%s: %s ret=%d\n", __func__, fw_name, ret);
+
+ return ret;
+}
+
+/**
+ * uncache_firmware - remove one cached firmware image
+ * @fw_name: the firmware image name
+ *
+ * Uncache one firmware image which has been cached successfully
+ * before.
+ *
+ * Return 0 if the firmware cache has been removed successfully
+ * Return !0 otherwise
+ *
+ */
+int uncache_firmware(const char *fw_name)
+{
+ struct firmware_buf *buf;
+ struct firmware fw;
+
+ pr_debug("%s: %s\n", __func__, fw_name);
+
+ if (fw_get_builtin_firmware(&fw, fw_name))
+ return 0;
+
+ buf = fw_lookup_buf(fw_name);
+ if (buf) {
+ fw_free_buf(buf);
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static struct fw_cache_entry *alloc_fw_cache_entry(const char *name)
+{
+ struct fw_cache_entry *fce;
+
+ fce = kzalloc(sizeof(*fce) + strlen(name) + 1, GFP_ATOMIC);
+ if (!fce)
+ goto exit;
+
+ strcpy(fce->name, name);
+exit:
+ return fce;
+}
+
+static int fw_cache_piggyback_on_request(const char *name)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ struct fw_cache_entry *fce;
+ int ret = 0;
+
+ spin_lock(&fwc->name_lock);
+ list_for_each_entry(fce, &fwc->fw_names, list) {
+ if (!strcmp(fce->name, name))
+ goto found;
+ }
+
+ fce = alloc_fw_cache_entry(name);
+ if (fce) {
+ ret = 1;
+ list_add(&fce->list, &fwc->fw_names);
+ pr_debug("%s: fw: %s\n", __func__, name);
+ }
+found:
+ spin_unlock(&fwc->name_lock);
+ return ret;
+}
+
+static void free_fw_cache_entry(struct fw_cache_entry *fce)
+{
+ kfree(fce);
+}
+
+static void __async_dev_cache_fw_image(void *fw_entry,
+ async_cookie_t cookie)
+{
+ struct fw_cache_entry *fce = fw_entry;
+ struct firmware_cache *fwc = &fw_cache;
+ int ret;
+
+ ret = cache_firmware(fce->name);
+ if (ret) {
+ spin_lock(&fwc->name_lock);
+ list_del(&fce->list);
+ spin_unlock(&fwc->name_lock);
+
+ free_fw_cache_entry(fce);
+ }
+
+ spin_lock(&fwc->name_lock);
+ fwc->cnt--;
+ spin_unlock(&fwc->name_lock);
+
+ wake_up(&fwc->wait_queue);
+}
+
+/* called with dev->devres_lock held */
+static void dev_create_fw_entry(struct device *dev, void *res,
+ void *data)
+{
+ struct fw_name_devm *fwn = res;
+ const char *fw_name = fwn->name;
+ struct list_head *head = data;
+ struct fw_cache_entry *fce;
+
+ fce = alloc_fw_cache_entry(fw_name);
+ if (fce)
+ list_add(&fce->list, head);
+}
+
+static int devm_name_match(struct device *dev, void *res,
+ void *match_data)
+{
+ struct fw_name_devm *fwn = res;
+ return (fwn->magic == (unsigned long)match_data);
+}
+
+static void dev_cache_fw_image(struct device *dev, void *data)
+{
+ LIST_HEAD(todo);
+ struct fw_cache_entry *fce;
+ struct fw_cache_entry *fce_next;
+ struct firmware_cache *fwc = &fw_cache;
+
+ devres_for_each_res(dev, fw_name_devm_release,
+ devm_name_match, &fw_cache,
+ dev_create_fw_entry, &todo);
+
+ list_for_each_entry_safe(fce, fce_next, &todo, list) {
+ list_del(&fce->list);
+
+ spin_lock(&fwc->name_lock);
+ fwc->cnt++;
+ list_add(&fce->list, &fwc->fw_names);
+ spin_unlock(&fwc->name_lock);
+
+ async_schedule(__async_dev_cache_fw_image, (void *)fce);
+ }
+}
+
+static void __device_uncache_fw_images(void)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ struct fw_cache_entry *fce;
+
+ spin_lock(&fwc->name_lock);
+ while (!list_empty(&fwc->fw_names)) {
+ fce = list_entry(fwc->fw_names.next,
+ struct fw_cache_entry, list);
+ list_del(&fce->list);
+ spin_unlock(&fwc->name_lock);
+
+ uncache_firmware(fce->name);
+ free_fw_cache_entry(fce);
+
+ spin_lock(&fwc->name_lock);
+ }
+ spin_unlock(&fwc->name_lock);
+}
+
+/**
+ * device_cache_fw_images - cache devices' firmware
+ *
+ * If one device called request_firmware or its nowait version
+ * successfully before, the firmware names are recored into the
+ * device's devres link list, so device_cache_fw_images can call
+ * cache_firmware() to cache these firmwares for the device,
+ * then the device driver can load its firmwares easily at
+ * time when system is not ready to complete loading firmware.
+ */
+static void device_cache_fw_images(void)
+{
+ struct firmware_cache *fwc = &fw_cache;
+ int old_timeout;
+ DEFINE_WAIT(wait);
+
+ pr_debug("%s\n", __func__);
+
+ /*
+ * use small loading timeout for caching devices' firmware
+ * because all these firmware images have been loaded
+ * successfully at lease once, also system is ready for
+ * completing firmware loading now. The maximum size of
+ * firmware in current distributions is about 2M bytes,
+ * so 10 secs should be enough.
+ */
+ old_timeout = loading_timeout;
+ loading_timeout = 10;
+
+ mutex_lock(&fw_lock);
+ fwc->state = FW_LOADER_START_CACHE;
+ dpm_for_each_dev(NULL, dev_cache_fw_image);
+ mutex_unlock(&fw_lock);
+
+ /* wait for completion of caching firmware for all devices */
+ spin_lock(&fwc->name_lock);
+ for (;;) {
+ prepare_to_wait(&fwc->wait_queue, &wait,
+ TASK_UNINTERRUPTIBLE);
+ if (!fwc->cnt)
+ break;
+
+ spin_unlock(&fwc->name_lock);
+
+ schedule();
+
+ spin_lock(&fwc->name_lock);
+ }
+ spin_unlock(&fwc->name_lock);
+ finish_wait(&fwc->wait_queue, &wait);
+
+ loading_timeout = old_timeout;
+}
+
+/**
+ * device_uncache_fw_images - uncache devices' firmware
+ *
+ * uncache all firmwares which have been cached successfully
+ * by device_uncache_fw_images earlier
+ */
+static void device_uncache_fw_images(void)
+{
+ pr_debug("%s\n", __func__);
+ __device_uncache_fw_images();
+}
+
+static void device_uncache_fw_images_work(struct work_struct *work)
+{
+ device_uncache_fw_images();
+}
+
+/**
+ * device_uncache_fw_images_delay - uncache devices firmwares
+ * @delay: number of milliseconds to delay uncache device firmwares
+ *
+ * uncache all devices's firmwares which has been cached successfully
+ * by device_cache_fw_images after @delay milliseconds.
+ */
+static void device_uncache_fw_images_delay(unsigned long delay)
+{
+ schedule_delayed_work(&fw_cache.work,
+ msecs_to_jiffies(delay));
+}
+
+static int fw_pm_notify(struct notifier_block *notify_block,
+ unsigned long mode, void *unused)
+{
+ switch (mode) {
+ case PM_HIBERNATION_PREPARE:
+ case PM_SUSPEND_PREPARE:
+ device_cache_fw_images();
+ break;
+
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+ case PM_POST_RESTORE:
+ /*
+ * In case that system sleep failed and syscore_suspend is
+ * not called.
+ */
+ mutex_lock(&fw_lock);
+ fw_cache.state = FW_LOADER_NO_CACHE;
+ mutex_unlock(&fw_lock);
+
+ device_uncache_fw_images_delay(10 * MSEC_PER_SEC);
+ break;
+ }
+
+ return 0;
+}
+
+/* stop caching firmware once syscore_suspend is reached */
+static int fw_suspend(void)
+{
+ fw_cache.state = FW_LOADER_NO_CACHE;
+ return 0;
+}
+
+static struct syscore_ops fw_syscore_ops = {
+ .suspend = fw_suspend,
+};
+#else
+static int fw_cache_piggyback_on_request(const char *name)
+{
+ return 0;
+}
+#endif
+
+static void __init fw_cache_init(void)
+{
+ spin_lock_init(&fw_cache.lock);
+ INIT_LIST_HEAD(&fw_cache.head);
+ fw_cache.state = FW_LOADER_NO_CACHE;
+
+#ifdef CONFIG_PM_SLEEP
+ spin_lock_init(&fw_cache.name_lock);
+ INIT_LIST_HEAD(&fw_cache.fw_names);
+ fw_cache.cnt = 0;
+
+ init_waitqueue_head(&fw_cache.wait_queue);
+ INIT_DELAYED_WORK(&fw_cache.work,
+ device_uncache_fw_images_work);
+
+ fw_cache.pm_notify.notifier_call = fw_pm_notify;
+ register_pm_notifier(&fw_cache.pm_notify);
+
+ register_syscore_ops(&fw_syscore_ops);
+#endif
+}
+
static int __init firmware_class_init(void)
{
+ fw_cache_init();
return class_register(&firmware_class);
}
static void __exit firmware_class_exit(void)
{
+#ifdef CONFIG_PM_SLEEP
+ unregister_syscore_ops(&fw_syscore_ops);
+ unregister_pm_notifier(&fw_cache.pm_notify);
+#endif
class_unregister(&firmware_class);
}
@@ -726,3 +1354,5 @@ module_exit(firmware_class_exit);
EXPORT_SYMBOL(release_firmware);
EXPORT_SYMBOL(request_firmware);
EXPORT_SYMBOL(request_firmware_nowait);
+EXPORT_SYMBOL_GPL(cache_firmware);
+EXPORT_SYMBOL_GPL(uncache_firmware);
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index a1a722502587..ddeca142293c 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -20,9 +20,13 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
+#include <linux/idr.h>
#include "base.h"
+/* For automatically allocated device IDs */
+static DEFINE_IDA(platform_devid_ida);
+
#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \
driver))
@@ -99,6 +103,9 @@ struct resource *platform_get_resource_byname(struct platform_device *dev,
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
+ if (unlikely(!r->name))
+ continue;
+
if (type == resource_type(r) && !strcmp(r->name, name))
return r;
}
@@ -263,7 +270,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data);
*/
int platform_device_add(struct platform_device *pdev)
{
- int i, ret = 0;
+ int i, ret;
if (!pdev)
return -EINVAL;
@@ -273,10 +280,27 @@ int platform_device_add(struct platform_device *pdev)
pdev->dev.bus = &platform_bus_type;
- if (pdev->id != -1)
+ switch (pdev->id) {
+ default:
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
- else
+ break;
+ case PLATFORM_DEVID_NONE:
dev_set_name(&pdev->dev, "%s", pdev->name);
+ break;
+ case PLATFORM_DEVID_AUTO:
+ /*
+ * Automatically allocated device ID. We mark it as such so
+ * that we remember it must be freed, and we append a suffix
+ * to avoid namespace collision with explicit IDs.
+ */
+ ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
+ if (ret < 0)
+ goto err_out;
+ pdev->id = ret;
+ pdev->id_auto = true;
+ dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
+ break;
+ }
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
@@ -309,6 +333,11 @@ int platform_device_add(struct platform_device *pdev)
return ret;
failed:
+ if (pdev->id_auto) {
+ ida_simple_remove(&platform_devid_ida, pdev->id);
+ pdev->id = PLATFORM_DEVID_AUTO;
+ }
+
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
@@ -317,6 +346,7 @@ int platform_device_add(struct platform_device *pdev)
release_resource(r);
}
+ err_out:
return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);
@@ -336,6 +366,11 @@ void platform_device_del(struct platform_device *pdev)
if (pdev) {
device_del(&pdev->dev);
+ if (pdev->id_auto) {
+ ida_simple_remove(&platform_devid_ida, pdev->id);
+ pdev->id = PLATFORM_DEVID_AUTO;
+ }
+
for (i = 0; i < pdev->num_resources; i++) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c
index 0113adc310dc..b0b072a88f5f 100644
--- a/drivers/base/power/main.c
+++ b/drivers/base/power/main.c
@@ -1324,3 +1324,25 @@ int device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
return async_error;
}
EXPORT_SYMBOL_GPL(device_pm_wait_for_dev);
+
+/**
+ * dpm_for_each_dev - device iterator.
+ * @data: data for the callback.
+ * @fn: function to be called for each device.
+ *
+ * Iterate over devices in dpm_list, and call @fn for each device,
+ * passing it @data.
+ */
+void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *))
+{
+ struct device *dev;
+
+ if (!fn)
+ return;
+
+ device_pm_lock();
+ list_for_each_entry(dev, &dpm_list, power.entry)
+ fn(dev, data);
+ device_pm_unlock();
+}
+EXPORT_SYMBOL_GPL(dpm_for_each_dev);
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index a89734621e51..5b6b1d8e6cc0 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -16,12 +16,14 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/irqdomain.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include "internal.h"
struct regmap_irq_chip_data {
struct mutex lock;
+ struct irq_chip irq_chip;
struct regmap *map;
const struct regmap_irq_chip *chip;
@@ -59,6 +61,14 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
struct regmap *map = d->map;
int i, ret;
+ u32 reg;
+
+ if (d->chip->runtime_pm) {
+ ret = pm_runtime_get_sync(map->dev);
+ if (ret < 0)
+ dev_err(map->dev, "IRQ sync failed to resume: %d\n",
+ ret);
+ }
/*
* If there's been a change in the mask write it back to the
@@ -66,15 +76,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
* suppress pointless writes.
*/
for (i = 0; i < d->chip->num_regs; i++) {
- ret = regmap_update_bits(d->map, d->chip->mask_base +
- (i * map->reg_stride *
- d->irq_reg_stride),
+ reg = d->chip->mask_base +
+ (i * map->reg_stride * d->irq_reg_stride);
+ if (d->chip->mask_invert)
+ ret = regmap_update_bits(d->map, reg,
+ d->mask_buf_def[i], ~d->mask_buf[i]);
+ else
+ ret = regmap_update_bits(d->map, reg,
d->mask_buf_def[i], d->mask_buf[i]);
if (ret != 0)
dev_err(d->map->dev, "Failed to sync masks in %x\n",
- d->chip->mask_base + (i * map->reg_stride));
+ reg);
}
+ if (d->chip->runtime_pm)
+ pm_runtime_put(map->dev);
+
/* If we've changed our wakeup count propagate it to the parent */
if (d->wake_count < 0)
for (i = d->wake_count; i < 0; i++)
@@ -128,8 +145,7 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
return 0;
}
-static struct irq_chip regmap_irq_chip = {
- .name = "regmap",
+static const struct irq_chip regmap_irq_chip = {
.irq_bus_lock = regmap_irq_lock,
.irq_bus_sync_unlock = regmap_irq_sync_unlock,
.irq_disable = regmap_irq_disable,
@@ -144,6 +160,16 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
struct regmap *map = data->map;
int ret, i;
bool handled = false;
+ u32 reg;
+
+ if (chip->runtime_pm) {
+ ret = pm_runtime_get_sync(map->dev);
+ if (ret < 0) {
+ dev_err(map->dev, "IRQ thread failed to resume: %d\n",
+ ret);
+ return IRQ_NONE;
+ }
+ }
/*
* Ignore masked IRQs and ack if we need to; we ack early so
@@ -160,20 +186,20 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
if (ret != 0) {
dev_err(map->dev, "Failed to read IRQ status: %d\n",
ret);
+ if (chip->runtime_pm)
+ pm_runtime_put(map->dev);
return IRQ_NONE;
}
data->status_buf[i] &= ~data->mask_buf[i];
if (data->status_buf[i] && chip->ack_base) {
- ret = regmap_write(map, chip->ack_base +
- (i * map->reg_stride *
- data->irq_reg_stride),
- data->status_buf[i]);
+ reg = chip->ack_base +
+ (i * map->reg_stride * data->irq_reg_stride);
+ ret = regmap_write(map, reg, data->status_buf[i]);
if (ret != 0)
dev_err(map->dev, "Failed to ack 0x%x: %d\n",
- chip->ack_base + (i * map->reg_stride),
- ret);
+ reg, ret);
}
}
@@ -185,6 +211,9 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
}
}
+ if (chip->runtime_pm)
+ pm_runtime_put(map->dev);
+
if (handled)
return IRQ_HANDLED;
else
@@ -197,7 +226,7 @@ static int regmap_irq_map(struct irq_domain *h, unsigned int virq,
struct regmap_irq_chip_data *data = h->host_data;
irq_set_chip_data(virq, data);
- irq_set_chip_and_handler(virq, &regmap_irq_chip, handle_edge_irq);
+ irq_set_chip(virq, &data->irq_chip);
irq_set_nested_thread(virq, 1);
/* ARM needs us to explicitly flag the IRQ as valid
@@ -238,6 +267,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
struct regmap_irq_chip_data *d;
int i;
int ret = -ENOMEM;
+ u32 reg;
for (i = 0; i < chip->num_irqs; i++) {
if (chip->irqs[i].reg_offset % map->reg_stride)
@@ -284,6 +314,13 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
goto err_alloc;
}
+ d->irq_chip = regmap_irq_chip;
+ d->irq_chip.name = chip->name;
+ if (!chip->wake_base) {
+ d->irq_chip.irq_set_wake = NULL;
+ d->irq_chip.flags |= IRQCHIP_MASK_ON_SUSPEND |
+ IRQCHIP_SKIP_SET_WAKE;
+ }
d->irq = irq;
d->map = map;
d->chip = chip;
@@ -303,16 +340,37 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
/* Mask all the interrupts by default */
for (i = 0; i < chip->num_regs; i++) {
d->mask_buf[i] = d->mask_buf_def[i];
- ret = regmap_write(map, chip->mask_base + (i * map->reg_stride
- * d->irq_reg_stride),
- d->mask_buf[i]);
+ reg = chip->mask_base +
+ (i * map->reg_stride * d->irq_reg_stride);
+ if (chip->mask_invert)
+ ret = regmap_update_bits(map, reg,
+ d->mask_buf[i], ~d->mask_buf[i]);
+ else
+ ret = regmap_update_bits(map, reg,
+ d->mask_buf[i], d->mask_buf[i]);
if (ret != 0) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
- chip->mask_base + (i * map->reg_stride), ret);
+ reg, ret);
goto err_alloc;
}
}
+ /* Wake is disabled by default */
+ if (d->wake_buf) {
+ for (i = 0; i < chip->num_regs; i++) {
+ d->wake_buf[i] = d->mask_buf_def[i];
+ reg = chip->wake_base +
+ (i * map->reg_stride * d->irq_reg_stride);
+ ret = regmap_update_bits(map, reg, d->wake_buf[i],
+ d->wake_buf[i]);
+ if (ret != 0) {
+ dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
+ reg, ret);
+ goto err_alloc;
+ }
+ }
+ }
+
if (irq_base)
d->domain = irq_domain_add_legacy(map->dev->of_node,
chip->num_irqs, irq_base, 0,
diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index c241ae2f2f10..52069d29ff12 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -659,13 +659,12 @@ EXPORT_SYMBOL_GPL(devm_regmap_init);
* new cache. This can be used to restore the cache to defaults or to
* update the cache configuration to reflect runtime discovery of the
* hardware.
+ *
+ * No explicit locking is done here, the user needs to ensure that
+ * this function will not race with other calls to regmap.
*/
int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
{
- int ret;
-
- map->lock(map);
-
regcache_exit(map);
regmap_debugfs_exit(map);
@@ -681,11 +680,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
map->cache_bypass = false;
map->cache_only = false;
- ret = regcache_init(map, config);
-
- map->unlock(map);
-
- return ret;
+ return regcache_init(map, config);
}
EXPORT_SYMBOL_GPL(regmap_reinit_cache);
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index a796407123c7..f529407db93f 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -353,18 +353,6 @@ config BLK_DEV_SX8
Use devices /dev/sx8/$N and /dev/sx8/$Np$M.
-config BLK_DEV_UB
- tristate "Low Performance USB Block driver (deprecated)"
- depends on USB
- help
- This driver supports certain USB attached storage devices
- such as flash keys.
-
- If you enable this driver, it is recommended to avoid conflicts
- with usb-storage by enabling USB_LIBUSUAL.
-
- If unsure, say N.
-
config BLK_DEV_RAM
tristate "RAM block device support"
---help---
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 5b795059f8fb..17e82df3df74 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -33,7 +33,6 @@ obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o
obj-$(CONFIG_VIODASD) += viodasd.o
obj-$(CONFIG_BLK_DEV_SX8) += sx8.o
-obj-$(CONFIG_BLK_DEV_UB) += ub.o
obj-$(CONFIG_BLK_DEV_HD) += hd.o
obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o
diff --git a/drivers/block/aoe/aoecmd.c b/drivers/block/aoe/aoecmd.c
index de0435e63b02..887f68f6d79a 100644
--- a/drivers/block/aoe/aoecmd.c
+++ b/drivers/block/aoe/aoecmd.c
@@ -35,6 +35,7 @@ new_skb(ulong len)
skb_reset_mac_header(skb);
skb_reset_network_header(skb);
skb->protocol = __constant_htons(ETH_P_AOE);
+ skb_checksum_none_assert(skb);
}
return skb;
}
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index 38aa6dda6b81..da3311129a0c 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -795,6 +795,7 @@ static void complete_scsi_command(CommandList_struct *c, int timeout,
}
break;
case CMD_PROTOCOL_ERR:
+ cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev,
"%p has protocol error\n", c);
break;
diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c
index a8fddeb3d638..f946d31d6917 100644
--- a/drivers/block/mtip32xx/mtip32xx.c
+++ b/drivers/block/mtip32xx/mtip32xx.c
@@ -1148,11 +1148,15 @@ static bool mtip_pause_ncq(struct mtip_port *port,
reply = port->rxfis + RX_FIS_D2H_REG;
task_file_data = readl(port->mmio+PORT_TFDATA);
- if ((task_file_data & 1) || (fis->command == ATA_CMD_SEC_ERASE_UNIT))
+ if (fis->command == ATA_CMD_SEC_ERASE_UNIT)
+ clear_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
+
+ if ((task_file_data & 1))
return false;
if (fis->command == ATA_CMD_SEC_ERASE_PREP) {
set_bit(MTIP_PF_SE_ACTIVE_BIT, &port->flags);
+ set_bit(MTIP_DDF_SEC_LOCK_BIT, &port->dd->dd_flag);
port->ic_pause_timer = jiffies;
return true;
} else if ((fis->command == ATA_CMD_DOWNLOAD_MICRO) &&
@@ -1900,7 +1904,7 @@ static int exec_drive_command(struct mtip_port *port, u8 *command,
int rv = 0, xfer_sz = command[3];
if (xfer_sz) {
- if (user_buffer)
+ if (!user_buffer)
return -EFAULT;
buf = dmam_alloc_coherent(&port->dd->pdev->dev,
@@ -2043,7 +2047,7 @@ static void mtip_set_timeout(struct host_to_dev_fis *fis, unsigned int *timeout)
*timeout = 240000; /* 4 minutes */
break;
case ATA_CMD_STANDBYNOW1:
- *timeout = 10000; /* 10 seconds */
+ *timeout = 120000; /* 2 minutes */
break;
case 0xF7:
case 0xFA:
@@ -2588,9 +2592,6 @@ static ssize_t mtip_hw_read_registers(struct file *f, char __user *ubuf,
if (!len || size)
return 0;
- if (size < 0)
- return -EINVAL;
-
size += sprintf(&buf[size], "H/ S ACTive : [ 0x");
for (n = dd->slot_groups-1; n >= 0; n--)
@@ -2660,9 +2661,6 @@ static ssize_t mtip_hw_read_flags(struct file *f, char __user *ubuf,
if (!len || size)
return 0;
- if (size < 0)
- return -EINVAL;
-
size += sprintf(&buf[size], "Flag-port : [ %08lX ]\n",
dd->port->flags);
size += sprintf(&buf[size], "Flag-dd : [ %08lX ]\n",
@@ -3214,8 +3212,8 @@ static int mtip_hw_init(struct driver_data *dd)
"Unable to check write protect progress\n");
else
dev_info(&dd->pdev->dev,
- "Write protect progress: %d%% (%d blocks)\n",
- attr242.cur, attr242.data);
+ "Write protect progress: %u%% (%u blocks)\n",
+ attr242.cur, le32_to_cpu(attr242.data));
return rv;
out3:
@@ -3619,6 +3617,10 @@ static void mtip_make_request(struct request_queue *queue, struct bio *bio)
bio_endio(bio, -ENODATA);
return;
}
+ if (unlikely(test_bit(MTIP_DDF_SEC_LOCK_BIT, &dd->dd_flag))) {
+ bio_endio(bio, -ENODATA);
+ return;
+ }
}
if (unlikely(!bio_has_data(bio))) {
@@ -4168,7 +4170,13 @@ static void mtip_pci_shutdown(struct pci_dev *pdev)
/* Table of device ids supported by this driver. */
static DEFINE_PCI_DEVICE_TABLE(mtip_pci_tbl) = {
- { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320H_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320M_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P320S_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P325M_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420H_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P420M_DEVICE_ID) },
+ { PCI_DEVICE(PCI_VENDOR_ID_MICRON, P425M_DEVICE_ID) },
{ 0 }
};
@@ -4199,12 +4207,12 @@ static int __init mtip_init(void)
{
int error;
- printk(KERN_INFO MTIP_DRV_NAME " Version " MTIP_DRV_VERSION "\n");
+ pr_info(MTIP_DRV_NAME " Version " MTIP_DRV_VERSION "\n");
/* Allocate a major block device number to use with this driver. */
error = register_blkdev(0, MTIP_DRV_NAME);
if (error <= 0) {
- printk(KERN_ERR "Unable to register block device (%d)\n",
+ pr_err("Unable to register block device (%d)\n",
error);
return -EBUSY;
}
@@ -4213,7 +4221,7 @@ static int __init mtip_init(void)
if (!dfs_parent) {
dfs_parent = debugfs_create_dir("rssd", NULL);
if (IS_ERR_OR_NULL(dfs_parent)) {
- printk(KERN_WARNING "Error creating debugfs parent\n");
+ pr_warn("Error creating debugfs parent\n");
dfs_parent = NULL;
}
}
diff --git a/drivers/block/mtip32xx/mtip32xx.h b/drivers/block/mtip32xx/mtip32xx.h
index f51fc23d17bb..18627a1d04c5 100644
--- a/drivers/block/mtip32xx/mtip32xx.h
+++ b/drivers/block/mtip32xx/mtip32xx.h
@@ -76,7 +76,13 @@
/* Micron Vendor ID & P320x SSD Device ID */
#define PCI_VENDOR_ID_MICRON 0x1344
-#define P320_DEVICE_ID 0x5150
+#define P320H_DEVICE_ID 0x5150
+#define P320M_DEVICE_ID 0x5151
+#define P320S_DEVICE_ID 0x5152
+#define P325M_DEVICE_ID 0x5153
+#define P420H_DEVICE_ID 0x5160
+#define P420M_DEVICE_ID 0x5161
+#define P425M_DEVICE_ID 0x5163
/* Driver name and version strings */
#define MTIP_DRV_NAME "mtip32xx"
@@ -131,10 +137,12 @@ enum {
MTIP_PF_SVC_THD_STOP_BIT = 8,
/* below are bit numbers in 'dd_flag' defined in driver_data */
+ MTIP_DDF_SEC_LOCK_BIT = 0,
MTIP_DDF_REMOVE_PENDING_BIT = 1,
MTIP_DDF_OVER_TEMP_BIT = 2,
MTIP_DDF_WRITE_PROTECT_BIT = 3,
MTIP_DDF_STOP_IO = ((1 << MTIP_DDF_REMOVE_PENDING_BIT) | \
+ (1 << MTIP_DDF_SEC_LOCK_BIT) | \
(1 << MTIP_DDF_OVER_TEMP_BIT) | \
(1 << MTIP_DDF_WRITE_PROTECT_BIT)),
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index d07c9f7fded6..0c03411c59eb 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -449,6 +449,14 @@ static void nbd_clear_que(struct nbd_device *nbd)
req->errors++;
nbd_end_request(req);
}
+
+ while (!list_empty(&nbd->waiting_queue)) {
+ req = list_entry(nbd->waiting_queue.next, struct request,
+ queuelist);
+ list_del_init(&req->queuelist);
+ req->errors++;
+ nbd_end_request(req);
+ }
}
@@ -598,6 +606,7 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd,
nbd->file = NULL;
nbd_clear_que(nbd);
BUG_ON(!list_empty(&nbd->queue_head));
+ BUG_ON(!list_empty(&nbd->waiting_queue));
if (file)
fput(file);
return 0;
diff --git a/drivers/block/nvme.c b/drivers/block/nvme.c
index 38a2d0631882..931769e133e5 100644
--- a/drivers/block/nvme.c
+++ b/drivers/block/nvme.c
@@ -79,6 +79,7 @@ struct nvme_dev {
char serial[20];
char model[40];
char firmware_rev[8];
+ u32 max_hw_sectors;
};
/*
@@ -835,15 +836,15 @@ static int nvme_identify(struct nvme_dev *dev, unsigned nsid, unsigned cns,
}
static int nvme_get_features(struct nvme_dev *dev, unsigned fid,
- unsigned dword11, dma_addr_t dma_addr)
+ unsigned nsid, dma_addr_t dma_addr)
{
struct nvme_command c;
memset(&c, 0, sizeof(c));
c.features.opcode = nvme_admin_get_features;
+ c.features.nsid = cpu_to_le32(nsid);
c.features.prp1 = cpu_to_le64(dma_addr);
c.features.fid = cpu_to_le32(fid);
- c.features.dword11 = cpu_to_le32(dword11);
return nvme_submit_admin_cmd(dev, &c, NULL);
}
@@ -862,11 +863,51 @@ static int nvme_set_features(struct nvme_dev *dev, unsigned fid,
return nvme_submit_admin_cmd(dev, &c, result);
}
+/**
+ * nvme_cancel_ios - Cancel outstanding I/Os
+ * @queue: The queue to cancel I/Os on
+ * @timeout: True to only cancel I/Os which have timed out
+ */
+static void nvme_cancel_ios(struct nvme_queue *nvmeq, bool timeout)
+{
+ int depth = nvmeq->q_depth - 1;
+ struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
+ unsigned long now = jiffies;
+ int cmdid;
+
+ for_each_set_bit(cmdid, nvmeq->cmdid_data, depth) {
+ void *ctx;
+ nvme_completion_fn fn;
+ static struct nvme_completion cqe = {
+ .status = cpu_to_le16(NVME_SC_ABORT_REQ) << 1,
+ };
+
+ if (timeout && !time_after(now, info[cmdid].timeout))
+ continue;
+ dev_warn(nvmeq->q_dmadev, "Cancelling I/O %d\n", cmdid);
+ ctx = cancel_cmdid(nvmeq, cmdid, &fn);
+ fn(nvmeq->dev, ctx, &cqe);
+ }
+}
+
+static void nvme_free_queue_mem(struct nvme_queue *nvmeq)
+{
+ dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth),
+ (void *)nvmeq->cqes, nvmeq->cq_dma_addr);
+ dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth),
+ nvmeq->sq_cmds, nvmeq->sq_dma_addr);
+ kfree(nvmeq);
+}
+
static void nvme_free_queue(struct nvme_dev *dev, int qid)
{
struct nvme_queue *nvmeq = dev->queues[qid];
int vector = dev->entry[nvmeq->cq_vector].vector;
+ spin_lock_irq(&nvmeq->q_lock);
+ nvme_cancel_ios(nvmeq, false);
+ spin_unlock_irq(&nvmeq->q_lock);
+
irq_set_affinity_hint(vector, NULL);
free_irq(vector, nvmeq);
@@ -876,18 +917,15 @@ static void nvme_free_queue(struct nvme_dev *dev, int qid)
adapter_delete_cq(dev, qid);
}
- dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth),
- (void *)nvmeq->cqes, nvmeq->cq_dma_addr);
- dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth),
- nvmeq->sq_cmds, nvmeq->sq_dma_addr);
- kfree(nvmeq);
+ nvme_free_queue_mem(nvmeq);
}
static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
int depth, int vector)
{
struct device *dmadev = &dev->pci_dev->dev;
- unsigned extra = (depth / 8) + (depth * sizeof(struct nvme_cmd_info));
+ unsigned extra = DIV_ROUND_UP(depth, 8) + (depth *
+ sizeof(struct nvme_cmd_info));
struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq) + extra, GFP_KERNEL);
if (!nvmeq)
return NULL;
@@ -975,7 +1013,7 @@ static __devinit struct nvme_queue *nvme_create_queue(struct nvme_dev *dev,
static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev)
{
- int result;
+ int result = 0;
u32 aqa;
u64 cap;
unsigned long timeout;
@@ -1005,17 +1043,22 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev)
timeout = ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2) + jiffies;
dev->db_stride = NVME_CAP_STRIDE(cap);
- while (!(readl(&dev->bar->csts) & NVME_CSTS_RDY)) {
+ while (!result && !(readl(&dev->bar->csts) & NVME_CSTS_RDY)) {
msleep(100);
if (fatal_signal_pending(current))
- return -EINTR;
+ result = -EINTR;
if (time_after(jiffies, timeout)) {
dev_err(&dev->pci_dev->dev,
"Device not ready; aborting initialisation\n");
- return -ENODEV;
+ result = -ENODEV;
}
}
+ if (result) {
+ nvme_free_queue_mem(nvmeq);
+ return result;
+ }
+
result = queue_request_irq(dev, nvmeq, "nvme admin");
dev->queues[0] = nvmeq;
return result;
@@ -1037,6 +1080,8 @@ static struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write,
offset = offset_in_page(addr);
count = DIV_ROUND_UP(offset + length, PAGE_SIZE);
pages = kcalloc(count, sizeof(*pages), GFP_KERNEL);
+ if (!pages)
+ return ERR_PTR(-ENOMEM);
err = get_user_pages_fast(addr, count, 1, pages);
if (err < count) {
@@ -1146,14 +1191,13 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
return status;
}
-static int nvme_user_admin_cmd(struct nvme_ns *ns,
+static int nvme_user_admin_cmd(struct nvme_dev *dev,
struct nvme_admin_cmd __user *ucmd)
{
- struct nvme_dev *dev = ns->dev;
struct nvme_admin_cmd cmd;
struct nvme_command c;
int status, length;
- struct nvme_iod *iod;
+ struct nvme_iod *uninitialized_var(iod);
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
@@ -1204,7 +1248,7 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
case NVME_IOCTL_ID:
return ns->ns_id;
case NVME_IOCTL_ADMIN_CMD:
- return nvme_user_admin_cmd(ns, (void __user *)arg);
+ return nvme_user_admin_cmd(ns->dev, (void __user *)arg);
case NVME_IOCTL_SUBMIT_IO:
return nvme_submit_io(ns, (void __user *)arg);
default:
@@ -1218,26 +1262,6 @@ static const struct block_device_operations nvme_fops = {
.compat_ioctl = nvme_ioctl,
};
-static void nvme_timeout_ios(struct nvme_queue *nvmeq)
-{
- int depth = nvmeq->q_depth - 1;
- struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
- unsigned long now = jiffies;
- int cmdid;
-
- for_each_set_bit(cmdid, nvmeq->cmdid_data, depth) {
- void *ctx;
- nvme_completion_fn fn;
- static struct nvme_completion cqe = { .status = cpu_to_le16(NVME_SC_ABORT_REQ) << 1, };
-
- if (!time_after(now, info[cmdid].timeout))
- continue;
- dev_warn(nvmeq->q_dmadev, "Timing out I/O %d\n", cmdid);
- ctx = cancel_cmdid(nvmeq, cmdid, &fn);
- fn(nvmeq->dev, ctx, &cqe);
- }
-}
-
static void nvme_resubmit_bios(struct nvme_queue *nvmeq)
{
while (bio_list_peek(&nvmeq->sq_cong)) {
@@ -1269,7 +1293,7 @@ static int nvme_kthread(void *data)
spin_lock_irq(&nvmeq->q_lock);
if (nvme_process_cq(nvmeq))
printk("process_cq did something\n");
- nvme_timeout_ios(nvmeq);
+ nvme_cancel_ios(nvmeq, true);
nvme_resubmit_bios(nvmeq);
spin_unlock_irq(&nvmeq->q_lock);
}
@@ -1339,6 +1363,9 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int nsid,
ns->disk = disk;
lbaf = id->flbas & 0xf;
ns->lba_shift = id->lbaf[lbaf].ds;
+ blk_queue_logical_block_size(ns->queue, 1 << ns->lba_shift);
+ if (dev->max_hw_sectors)
+ blk_queue_max_hw_sectors(ns->queue, dev->max_hw_sectors);
disk->major = nvme_major;
disk->minors = NVME_MINORS;
@@ -1383,7 +1410,7 @@ static int set_queue_count(struct nvme_dev *dev, int count)
static int __devinit nvme_setup_io_queues(struct nvme_dev *dev)
{
- int result, cpu, i, nr_io_queues, db_bar_size;
+ int result, cpu, i, nr_io_queues, db_bar_size, q_depth;
nr_io_queues = num_online_cpus();
result = set_queue_count(dev, nr_io_queues);
@@ -1429,9 +1456,10 @@ static int __devinit nvme_setup_io_queues(struct nvme_dev *dev)
cpu = cpumask_next(cpu, cpu_online_mask);
}
+ q_depth = min_t(int, NVME_CAP_MQES(readq(&dev->bar->cap)) + 1,
+ NVME_Q_DEPTH);
for (i = 0; i < nr_io_queues; i++) {
- dev->queues[i + 1] = nvme_create_queue(dev, i + 1,
- NVME_Q_DEPTH, i);
+ dev->queues[i + 1] = nvme_create_queue(dev, i + 1, q_depth, i);
if (IS_ERR(dev->queues[i + 1]))
return PTR_ERR(dev->queues[i + 1]);
dev->queue_count++;
@@ -1480,6 +1508,10 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev)
memcpy(dev->serial, ctrl->sn, sizeof(ctrl->sn));
memcpy(dev->model, ctrl->mn, sizeof(ctrl->mn));
memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr));
+ if (ctrl->mdts) {
+ int shift = NVME_CAP_MPSMIN(readq(&dev->bar->cap)) + 12;
+ dev->max_hw_sectors = 1 << (ctrl->mdts + shift - 9);
+ }
id_ns = mem;
for (i = 1; i <= nn; i++) {
@@ -1523,8 +1555,6 @@ static int nvme_dev_remove(struct nvme_dev *dev)
list_del(&dev->node);
spin_unlock(&dev_list_lock);
- /* TODO: wait all I/O finished or cancel them */
-
list_for_each_entry_safe(ns, next, &dev->namespaces, list) {
list_del(&ns->list);
del_gendisk(ns->disk);
@@ -1560,15 +1590,33 @@ static void nvme_release_prp_pools(struct nvme_dev *dev)
dma_pool_destroy(dev->prp_small_pool);
}
-/* XXX: Use an ida or something to let remove / add work correctly */
-static void nvme_set_instance(struct nvme_dev *dev)
+static DEFINE_IDA(nvme_instance_ida);
+
+static int nvme_set_instance(struct nvme_dev *dev)
{
- static int instance;
- dev->instance = instance++;
+ int instance, error;
+
+ do {
+ if (!ida_pre_get(&nvme_instance_ida, GFP_KERNEL))
+ return -ENODEV;
+
+ spin_lock(&dev_list_lock);
+ error = ida_get_new(&nvme_instance_ida, &instance);
+ spin_unlock(&dev_list_lock);
+ } while (error == -EAGAIN);
+
+ if (error)
+ return -ENODEV;
+
+ dev->instance = instance;
+ return 0;
}
static void nvme_release_instance(struct nvme_dev *dev)
{
+ spin_lock(&dev_list_lock);
+ ida_remove(&nvme_instance_ida, dev->instance);
+ spin_unlock(&dev_list_lock);
}
static int __devinit nvme_probe(struct pci_dev *pdev,
@@ -1601,7 +1649,10 @@ static int __devinit nvme_probe(struct pci_dev *pdev,
pci_set_drvdata(pdev, dev);
dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
- nvme_set_instance(dev);
+ result = nvme_set_instance(dev);
+ if (result)
+ goto disable;
+
dev->entry[0].vector = pdev->irq;
result = nvme_setup_prp_pools(dev);
@@ -1675,7 +1726,7 @@ static void __devexit nvme_remove(struct pci_dev *pdev)
#define nvme_suspend NULL
#define nvme_resume NULL
-static struct pci_error_handlers nvme_err_handler = {
+static const struct pci_error_handlers nvme_err_handler = {
.error_detected = nvme_error_detected,
.mmio_enabled = nvme_dump_registers,
.link_reset = nvme_link_reset,
@@ -1704,15 +1755,17 @@ static struct pci_driver nvme_driver = {
static int __init nvme_init(void)
{
- int result = -EBUSY;
+ int result;
nvme_thread = kthread_run(nvme_kthread, NULL, "nvme");
if (IS_ERR(nvme_thread))
return PTR_ERR(nvme_thread);
- nvme_major = register_blkdev(nvme_major, "nvme");
- if (nvme_major <= 0)
+ result = register_blkdev(nvme_major, "nvme");
+ if (result < 0)
goto kill_kthread;
+ else if (result > 0)
+ nvme_major = result;
result = pci_register_driver(&nvme_driver);
if (result)
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 9917943a3572..54a55f03115d 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -246,13 +246,12 @@ static int rbd_open(struct block_device *bdev, fmode_t mode)
{
struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
- rbd_get_dev(rbd_dev);
-
- set_device_ro(bdev, rbd_dev->read_only);
-
if ((mode & FMODE_WRITE) && rbd_dev->read_only)
return -EROFS;
+ rbd_get_dev(rbd_dev);
+ set_device_ro(bdev, rbd_dev->read_only);
+
return 0;
}
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
deleted file mode 100644
index fcec0225ac76..000000000000
--- a/drivers/block/ub.c
+++ /dev/null
@@ -1,2474 +0,0 @@
-/*
- * The low performance USB storage driver (ub).
- *
- * Copyright (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
- * Copyright (C) 2004 Pete Zaitcev (zaitcev@yahoo.com)
- *
- * This work is a part of Linux kernel, is derived from it,
- * and is not licensed separately. See file COPYING for details.
- *
- * TODO (sorted by decreasing priority)
- * -- Return sense now that rq allows it (we always auto-sense anyway).
- * -- set readonly flag for CDs, set removable flag for CF readers
- * -- do inquiry and verify we got a disk and not a tape (for LUN mismatch)
- * -- verify the 13 conditions and do bulk resets
- * -- highmem
- * -- move top_sense and work_bcs into separate allocations (if they survive)
- * for cache purists and esoteric architectures.
- * -- Allocate structure for LUN 0 before the first ub_sync_tur, avoid NULL. ?
- * -- prune comments, they are too volumnous
- * -- Resove XXX's
- * -- CLEAR, CLR2STS, CLRRS seem to be ripe for refactoring.
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/usb.h>
-#include <linux/usb_usual.h>
-#include <linux/blkdev.h>
-#include <linux/timer.h>
-#include <linux/scatterlist.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <scsi/scsi.h>
-
-#define DRV_NAME "ub"
-
-#define UB_MAJOR 180
-
-/*
- * The command state machine is the key model for understanding of this driver.
- *
- * The general rule is that all transitions are done towards the bottom
- * of the diagram, thus preventing any loops.
- *
- * An exception to that is how the STAT state is handled. A counter allows it
- * to be re-entered along the path marked with [C].
- *
- * +--------+
- * ! INIT !
- * +--------+
- * !
- * ub_scsi_cmd_start fails ->--------------------------------------\
- * ! !
- * V !
- * +--------+ !
- * ! CMD ! !
- * +--------+ !
- * ! +--------+ !
- * was -EPIPE -->-------------------------------->! CLEAR ! !
- * ! +--------+ !
- * ! ! !
- * was error -->------------------------------------- ! --------->\
- * ! ! !
- * /--<-- cmd->dir == NONE ? ! !
- * ! ! ! !
- * ! V ! !
- * ! +--------+ ! !
- * ! ! DATA ! ! !
- * ! +--------+ ! !
- * ! ! +---------+ ! !
- * ! was -EPIPE -->--------------->! CLR2STS ! ! !
- * ! ! +---------+ ! !
- * ! ! ! ! !
- * ! ! was error -->---- ! --------->\
- * ! was error -->--------------------- ! ------------- ! --------->\
- * ! ! ! ! !
- * ! V ! ! !
- * \--->+--------+ ! ! !
- * ! STAT !<--------------------------/ ! !
- * /--->+--------+ ! !
- * ! ! ! !
- * [C] was -EPIPE -->-----------\ ! !
- * ! ! ! ! !
- * +<---- len == 0 ! ! !
- * ! ! ! ! !
- * ! was error -->--------------------------------------!---------->\
- * ! ! ! ! !
- * +<---- bad CSW ! ! !
- * +<---- bad tag ! ! !
- * ! ! V ! !
- * ! ! +--------+ ! !
- * ! ! ! CLRRS ! ! !
- * ! ! +--------+ ! !
- * ! ! ! ! !
- * \------- ! --------------------[C]--------\ ! !
- * ! ! ! !
- * cmd->error---\ +--------+ ! !
- * ! +--------------->! SENSE !<----------/ !
- * STAT_FAIL----/ +--------+ !
- * ! ! V
- * ! V +--------+
- * \--------------------------------\--------------------->! DONE !
- * +--------+
- */
-
-/*
- * This many LUNs per USB device.
- * Every one of them takes a host, see UB_MAX_HOSTS.
- */
-#define UB_MAX_LUNS 9
-
-/*
- */
-
-#define UB_PARTS_PER_LUN 8
-
-#define UB_MAX_CDB_SIZE 16 /* Corresponds to Bulk */
-
-#define UB_SENSE_SIZE 18
-
-/*
- */
-struct ub_dev;
-
-#define UB_MAX_REQ_SG 9 /* cdrecord requires 32KB and maybe a header */
-#define UB_MAX_SECTORS 64
-
-/*
- * A second is more than enough for a 32K transfer (UB_MAX_SECTORS)
- * even if a webcam hogs the bus, but some devices need time to spin up.
- */
-#define UB_URB_TIMEOUT (HZ*2)
-#define UB_DATA_TIMEOUT (HZ*5) /* ZIP does spin-ups in the data phase */
-#define UB_STAT_TIMEOUT (HZ*5) /* Same spinups and eject for a dataless cmd. */
-#define UB_CTRL_TIMEOUT (HZ/2) /* 500ms ought to be enough to clear a stall */
-
-/*
- * An instance of a SCSI command in transit.
- */
-#define UB_DIR_NONE 0
-#define UB_DIR_READ 1
-#define UB_DIR_ILLEGAL2 2
-#define UB_DIR_WRITE 3
-
-#define UB_DIR_CHAR(c) (((c)==UB_DIR_WRITE)? 'w': \
- (((c)==UB_DIR_READ)? 'r': 'n'))
-
-enum ub_scsi_cmd_state {
- UB_CMDST_INIT, /* Initial state */
- UB_CMDST_CMD, /* Command submitted */
- UB_CMDST_DATA, /* Data phase */
- UB_CMDST_CLR2STS, /* Clearing before requesting status */
- UB_CMDST_STAT, /* Status phase */
- UB_CMDST_CLEAR, /* Clearing a stall (halt, actually) */
- UB_CMDST_CLRRS, /* Clearing before retrying status */
- UB_CMDST_SENSE, /* Sending Request Sense */
- UB_CMDST_DONE /* Final state */
-};
-
-struct ub_scsi_cmd {
- unsigned char cdb[UB_MAX_CDB_SIZE];
- unsigned char cdb_len;
-
- unsigned char dir; /* 0 - none, 1 - read, 3 - write. */
- enum ub_scsi_cmd_state state;
- unsigned int tag;
- struct ub_scsi_cmd *next;
-
- int error; /* Return code - valid upon done */
- unsigned int act_len; /* Return size */
- unsigned char key, asc, ascq; /* May be valid if error==-EIO */
-
- int stat_count; /* Retries getting status. */
- unsigned int timeo; /* jiffies until rq->timeout changes */
-
- unsigned int len; /* Requested length */
- unsigned int current_sg;
- unsigned int nsg; /* sgv[nsg] */
- struct scatterlist sgv[UB_MAX_REQ_SG];
-
- struct ub_lun *lun;
- void (*done)(struct ub_dev *, struct ub_scsi_cmd *);
- void *back;
-};
-
-struct ub_request {
- struct request *rq;
- unsigned int current_try;
- unsigned int nsg; /* sgv[nsg] */
- struct scatterlist sgv[UB_MAX_REQ_SG];
-};
-
-/*
- */
-struct ub_capacity {
- unsigned long nsec; /* Linux size - 512 byte sectors */
- unsigned int bsize; /* Linux hardsect_size */
- unsigned int bshift; /* Shift between 512 and hard sects */
-};
-
-/*
- * This is a direct take-off from linux/include/completion.h
- * The difference is that I do not wait on this thing, just poll.
- * When I want to wait (ub_probe), I just use the stock completion.
- *
- * Note that INIT_COMPLETION takes no lock. It is correct. But why
- * in the bloody hell that thing takes struct instead of pointer to struct
- * is quite beyond me. I just copied it from the stock completion.
- */
-struct ub_completion {
- unsigned int done;
- spinlock_t lock;
-};
-
-static DEFINE_MUTEX(ub_mutex);
-static inline void ub_init_completion(struct ub_completion *x)
-{
- x->done = 0;
- spin_lock_init(&x->lock);
-}
-
-#define UB_INIT_COMPLETION(x) ((x).done = 0)
-
-static void ub_complete(struct ub_completion *x)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&x->lock, flags);
- x->done++;
- spin_unlock_irqrestore(&x->lock, flags);
-}
-
-static int ub_is_completed(struct ub_completion *x)
-{
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&x->lock, flags);
- ret = x->done;
- spin_unlock_irqrestore(&x->lock, flags);
- return ret;
-}
-
-/*
- */
-struct ub_scsi_cmd_queue {
- int qlen, qmax;
- struct ub_scsi_cmd *head, *tail;
-};
-
-/*
- * The block device instance (one per LUN).
- */
-struct ub_lun {
- struct ub_dev *udev;
- struct list_head link;
- struct gendisk *disk;
- int id; /* Host index */
- int num; /* LUN number */
- char name[16];
-
- int changed; /* Media was changed */
- int removable;
- int readonly;
-
- struct ub_request urq;
-
- /* Use Ingo's mempool if or when we have more than one command. */
- /*
- * Currently we never need more than one command for the whole device.
- * However, giving every LUN a command is a cheap and automatic way
- * to enforce fairness between them.
- */
- int cmda[1];
- struct ub_scsi_cmd cmdv[1];
-
- struct ub_capacity capacity;
-};
-
-/*
- * The USB device instance.
- */
-struct ub_dev {
- spinlock_t *lock;
- atomic_t poison; /* The USB device is disconnected */
- int openc; /* protected by ub_lock! */
- /* kref is too implicit for our taste */
- int reset; /* Reset is running */
- int bad_resid;
- unsigned int tagcnt;
- char name[12];
- struct usb_device *dev;
- struct usb_interface *intf;
-
- struct list_head luns;
-
- unsigned int send_bulk_pipe; /* cached pipe values */
- unsigned int recv_bulk_pipe;
- unsigned int send_ctrl_pipe;
- unsigned int recv_ctrl_pipe;
-
- struct tasklet_struct tasklet;
-
- struct ub_scsi_cmd_queue cmd_queue;
- struct ub_scsi_cmd top_rqs_cmd; /* REQUEST SENSE */
- unsigned char top_sense[UB_SENSE_SIZE];
-
- struct ub_completion work_done;
- struct urb work_urb;
- struct timer_list work_timer;
- int last_pipe; /* What might need clearing */
- __le32 signature; /* Learned signature */
- struct bulk_cb_wrap work_bcb;
- struct bulk_cs_wrap work_bcs;
- struct usb_ctrlrequest work_cr;
-
- struct work_struct reset_work;
- wait_queue_head_t reset_wait;
-};
-
-/*
- */
-static void ub_cleanup(struct ub_dev *sc);
-static int ub_request_fn_1(struct ub_lun *lun, struct request *rq);
-static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct ub_request *urq);
-static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct ub_request *urq);
-static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_end_rq(struct request *rq, unsigned int status);
-static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_request *urq, struct ub_scsi_cmd *cmd);
-static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_urb_complete(struct urb *urb);
-static void ub_scsi_action(unsigned long _dev);
-static void ub_scsi_dispatch(struct ub_dev *sc);
-static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc);
-static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_state_stat_counted(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
- int stalled_pipe);
-static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd);
-static void ub_reset_enter(struct ub_dev *sc, int try);
-static void ub_reset_task(struct work_struct *work);
-static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun);
-static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_capacity *ret);
-static int ub_sync_reset(struct ub_dev *sc);
-static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe);
-static int ub_probe_lun(struct ub_dev *sc, int lnum);
-
-/*
- */
-#ifdef CONFIG_USB_LIBUSUAL
-
-#define ub_usb_ids usb_storage_usb_ids
-#else
-
-static const struct usb_device_id ub_usb_ids[] = {
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_BULK) },
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, ub_usb_ids);
-#endif /* CONFIG_USB_LIBUSUAL */
-
-/*
- * Find me a way to identify "next free minor" for add_disk(),
- * and the array disappears the next day. However, the number of
- * hosts has something to do with the naming and /proc/partitions.
- * This has to be thought out in detail before changing.
- * If UB_MAX_HOST was 1000, we'd use a bitmap. Or a better data structure.
- */
-#define UB_MAX_HOSTS 26
-static char ub_hostv[UB_MAX_HOSTS];
-
-#define UB_QLOCK_NUM 5
-static spinlock_t ub_qlockv[UB_QLOCK_NUM];
-static int ub_qlock_next = 0;
-
-static DEFINE_SPINLOCK(ub_lock); /* Locks globals and ->openc */
-
-/*
- * The id allocator.
- *
- * This also stores the host for indexing by minor, which is somewhat dirty.
- */
-static int ub_id_get(void)
-{
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&ub_lock, flags);
- for (i = 0; i < UB_MAX_HOSTS; i++) {
- if (ub_hostv[i] == 0) {
- ub_hostv[i] = 1;
- spin_unlock_irqrestore(&ub_lock, flags);
- return i;
- }
- }
- spin_unlock_irqrestore(&ub_lock, flags);
- return -1;
-}
-
-static void ub_id_put(int id)
-{
- unsigned long flags;
-
- if (id < 0 || id >= UB_MAX_HOSTS) {
- printk(KERN_ERR DRV_NAME ": bad host ID %d\n", id);
- return;
- }
-
- spin_lock_irqsave(&ub_lock, flags);
- if (ub_hostv[id] == 0) {
- spin_unlock_irqrestore(&ub_lock, flags);
- printk(KERN_ERR DRV_NAME ": freeing free host ID %d\n", id);
- return;
- }
- ub_hostv[id] = 0;
- spin_unlock_irqrestore(&ub_lock, flags);
-}
-
-/*
- * This is necessitated by the fact that blk_cleanup_queue does not
- * necesserily destroy the queue. Instead, it may merely decrease q->refcnt.
- * Since our blk_init_queue() passes a spinlock common with ub_dev,
- * we have life time issues when ub_cleanup frees ub_dev.
- */
-static spinlock_t *ub_next_lock(void)
-{
- unsigned long flags;
- spinlock_t *ret;
-
- spin_lock_irqsave(&ub_lock, flags);
- ret = &ub_qlockv[ub_qlock_next];
- ub_qlock_next = (ub_qlock_next + 1) % UB_QLOCK_NUM;
- spin_unlock_irqrestore(&ub_lock, flags);
- return ret;
-}
-
-/*
- * Downcount for deallocation. This rides on two assumptions:
- * - once something is poisoned, its refcount cannot grow
- * - opens cannot happen at this time (del_gendisk was done)
- * If the above is true, we can drop the lock, which we need for
- * blk_cleanup_queue(): the silly thing may attempt to sleep.
- * [Actually, it never needs to sleep for us, but it calls might_sleep()]
- */
-static void ub_put(struct ub_dev *sc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ub_lock, flags);
- --sc->openc;
- if (sc->openc == 0 && atomic_read(&sc->poison)) {
- spin_unlock_irqrestore(&ub_lock, flags);
- ub_cleanup(sc);
- } else {
- spin_unlock_irqrestore(&ub_lock, flags);
- }
-}
-
-/*
- * Final cleanup and deallocation.
- */
-static void ub_cleanup(struct ub_dev *sc)
-{
- struct list_head *p;
- struct ub_lun *lun;
- struct request_queue *q;
-
- while (!list_empty(&sc->luns)) {
- p = sc->luns.next;
- lun = list_entry(p, struct ub_lun, link);
- list_del(p);
-
- /* I don't think queue can be NULL. But... Stolen from sx8.c */
- if ((q = lun->disk->queue) != NULL)
- blk_cleanup_queue(q);
- /*
- * If we zero disk->private_data BEFORE put_disk, we have
- * to check for NULL all over the place in open, release,
- * check_media and revalidate, because the block level
- * semaphore is well inside the put_disk.
- * But we cannot zero after the call, because *disk is gone.
- * The sd.c is blatantly racy in this area.
- */
- /* disk->private_data = NULL; */
- put_disk(lun->disk);
- lun->disk = NULL;
-
- ub_id_put(lun->id);
- kfree(lun);
- }
-
- usb_set_intfdata(sc->intf, NULL);
- usb_put_intf(sc->intf);
- usb_put_dev(sc->dev);
- kfree(sc);
-}
-
-/*
- * The "command allocator".
- */
-static struct ub_scsi_cmd *ub_get_cmd(struct ub_lun *lun)
-{
- struct ub_scsi_cmd *ret;
-
- if (lun->cmda[0])
- return NULL;
- ret = &lun->cmdv[0];
- lun->cmda[0] = 1;
- return ret;
-}
-
-static void ub_put_cmd(struct ub_lun *lun, struct ub_scsi_cmd *cmd)
-{
- if (cmd != &lun->cmdv[0]) {
- printk(KERN_WARNING "%s: releasing a foreign cmd %p\n",
- lun->name, cmd);
- return;
- }
- if (!lun->cmda[0]) {
- printk(KERN_WARNING "%s: releasing a free cmd\n", lun->name);
- return;
- }
- lun->cmda[0] = 0;
-}
-
-/*
- * The command queue.
- */
-static void ub_cmdq_add(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct ub_scsi_cmd_queue *t = &sc->cmd_queue;
-
- if (t->qlen++ == 0) {
- t->head = cmd;
- t->tail = cmd;
- } else {
- t->tail->next = cmd;
- t->tail = cmd;
- }
-
- if (t->qlen > t->qmax)
- t->qmax = t->qlen;
-}
-
-static void ub_cmdq_insert(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct ub_scsi_cmd_queue *t = &sc->cmd_queue;
-
- if (t->qlen++ == 0) {
- t->head = cmd;
- t->tail = cmd;
- } else {
- cmd->next = t->head;
- t->head = cmd;
- }
-
- if (t->qlen > t->qmax)
- t->qmax = t->qlen;
-}
-
-static struct ub_scsi_cmd *ub_cmdq_pop(struct ub_dev *sc)
-{
- struct ub_scsi_cmd_queue *t = &sc->cmd_queue;
- struct ub_scsi_cmd *cmd;
-
- if (t->qlen == 0)
- return NULL;
- if (--t->qlen == 0)
- t->tail = NULL;
- cmd = t->head;
- t->head = cmd->next;
- cmd->next = NULL;
- return cmd;
-}
-
-#define ub_cmdq_peek(sc) ((sc)->cmd_queue.head)
-
-/*
- * The request function is our main entry point
- */
-
-static void ub_request_fn(struct request_queue *q)
-{
- struct ub_lun *lun = q->queuedata;
- struct request *rq;
-
- while ((rq = blk_peek_request(q)) != NULL) {
- if (ub_request_fn_1(lun, rq) != 0) {
- blk_stop_queue(q);
- break;
- }
- }
-}
-
-static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
-{
- struct ub_dev *sc = lun->udev;
- struct ub_scsi_cmd *cmd;
- struct ub_request *urq;
- int n_elem;
-
- if (atomic_read(&sc->poison)) {
- blk_start_request(rq);
- ub_end_rq(rq, DID_NO_CONNECT << 16);
- return 0;
- }
-
- if (lun->changed && rq->cmd_type != REQ_TYPE_BLOCK_PC) {
- blk_start_request(rq);
- ub_end_rq(rq, SAM_STAT_CHECK_CONDITION);
- return 0;
- }
-
- if (lun->urq.rq != NULL)
- return -1;
- if ((cmd = ub_get_cmd(lun)) == NULL)
- return -1;
- memset(cmd, 0, sizeof(struct ub_scsi_cmd));
-
- blk_start_request(rq);
-
- urq = &lun->urq;
- memset(urq, 0, sizeof(struct ub_request));
- urq->rq = rq;
-
- /*
- * get scatterlist from block layer
- */
- sg_init_table(&urq->sgv[0], UB_MAX_REQ_SG);
- n_elem = blk_rq_map_sg(lun->disk->queue, rq, &urq->sgv[0]);
- if (n_elem < 0) {
- /* Impossible, because blk_rq_map_sg should not hit ENOMEM. */
- printk(KERN_INFO "%s: failed request map (%d)\n",
- lun->name, n_elem);
- goto drop;
- }
- if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
- printk(KERN_WARNING "%s: request with %d segments\n",
- lun->name, n_elem);
- goto drop;
- }
- urq->nsg = n_elem;
-
- if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
- ub_cmd_build_packet(sc, lun, cmd, urq);
- } else {
- ub_cmd_build_block(sc, lun, cmd, urq);
- }
- cmd->state = UB_CMDST_INIT;
- cmd->lun = lun;
- cmd->done = ub_rw_cmd_done;
- cmd->back = urq;
-
- cmd->tag = sc->tagcnt++;
- if (ub_submit_scsi(sc, cmd) != 0)
- goto drop;
-
- return 0;
-
-drop:
- ub_put_cmd(lun, cmd);
- ub_end_rq(rq, DID_ERROR << 16);
- return 0;
-}
-
-static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct ub_request *urq)
-{
- struct request *rq = urq->rq;
- unsigned int block, nblks;
-
- if (rq_data_dir(rq) == WRITE)
- cmd->dir = UB_DIR_WRITE;
- else
- cmd->dir = UB_DIR_READ;
-
- cmd->nsg = urq->nsg;
- memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg);
-
- /*
- * build the command
- *
- * The call to blk_queue_logical_block_size() guarantees that request
- * is aligned, but it is given in terms of 512 byte units, always.
- */
- block = blk_rq_pos(rq) >> lun->capacity.bshift;
- nblks = blk_rq_sectors(rq) >> lun->capacity.bshift;
-
- cmd->cdb[0] = (cmd->dir == UB_DIR_READ)? READ_10: WRITE_10;
- /* 10-byte uses 4 bytes of LBA: 2147483648KB, 2097152MB, 2048GB */
- cmd->cdb[2] = block >> 24;
- cmd->cdb[3] = block >> 16;
- cmd->cdb[4] = block >> 8;
- cmd->cdb[5] = block;
- cmd->cdb[7] = nblks >> 8;
- cmd->cdb[8] = nblks;
- cmd->cdb_len = 10;
-
- cmd->len = blk_rq_bytes(rq);
-}
-
-static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct ub_request *urq)
-{
- struct request *rq = urq->rq;
-
- if (blk_rq_bytes(rq) == 0) {
- cmd->dir = UB_DIR_NONE;
- } else {
- if (rq_data_dir(rq) == WRITE)
- cmd->dir = UB_DIR_WRITE;
- else
- cmd->dir = UB_DIR_READ;
- }
-
- cmd->nsg = urq->nsg;
- memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg);
-
- memcpy(&cmd->cdb, rq->cmd, rq->cmd_len);
- cmd->cdb_len = rq->cmd_len;
-
- cmd->len = blk_rq_bytes(rq);
-
- /*
- * To reapply this to every URB is not as incorrect as it looks.
- * In return, we avoid any complicated tracking calculations.
- */
- cmd->timeo = rq->timeout;
-}
-
-static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct ub_lun *lun = cmd->lun;
- struct ub_request *urq = cmd->back;
- struct request *rq;
- unsigned int scsi_status;
-
- rq = urq->rq;
-
- if (cmd->error == 0) {
- if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
- if (cmd->act_len >= rq->resid_len)
- rq->resid_len = 0;
- else
- rq->resid_len -= cmd->act_len;
- scsi_status = 0;
- } else {
- if (cmd->act_len != cmd->len) {
- scsi_status = SAM_STAT_CHECK_CONDITION;
- } else {
- scsi_status = 0;
- }
- }
- } else {
- if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
- /* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
- memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
- rq->sense_len = UB_SENSE_SIZE;
- if (sc->top_sense[0] != 0)
- scsi_status = SAM_STAT_CHECK_CONDITION;
- else
- scsi_status = DID_ERROR << 16;
- } else {
- if (cmd->error == -EIO &&
- (cmd->key == 0 ||
- cmd->key == MEDIUM_ERROR ||
- cmd->key == UNIT_ATTENTION)) {
- if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0)
- return;
- }
- scsi_status = SAM_STAT_CHECK_CONDITION;
- }
- }
-
- urq->rq = NULL;
-
- ub_put_cmd(lun, cmd);
- ub_end_rq(rq, scsi_status);
- blk_start_queue(lun->disk->queue);
-}
-
-static void ub_end_rq(struct request *rq, unsigned int scsi_status)
-{
- int error;
-
- if (scsi_status == 0) {
- error = 0;
- } else {
- error = -EIO;
- rq->errors = scsi_status;
- }
- __blk_end_request_all(rq, error);
-}
-
-static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_request *urq, struct ub_scsi_cmd *cmd)
-{
-
- if (atomic_read(&sc->poison))
- return -ENXIO;
-
- ub_reset_enter(sc, urq->current_try);
-
- if (urq->current_try >= 3)
- return -EIO;
- urq->current_try++;
-
- /* Remove this if anyone complains of flooding. */
- printk(KERN_DEBUG "%s: dir %c len/act %d/%d "
- "[sense %x %02x %02x] retry %d\n",
- sc->name, UB_DIR_CHAR(cmd->dir), cmd->len, cmd->act_len,
- cmd->key, cmd->asc, cmd->ascq, urq->current_try);
-
- memset(cmd, 0, sizeof(struct ub_scsi_cmd));
- ub_cmd_build_block(sc, lun, cmd, urq);
-
- cmd->state = UB_CMDST_INIT;
- cmd->lun = lun;
- cmd->done = ub_rw_cmd_done;
- cmd->back = urq;
-
- cmd->tag = sc->tagcnt++;
-
-#if 0 /* Wasteful */
- return ub_submit_scsi(sc, cmd);
-#else
- ub_cmdq_add(sc, cmd);
- return 0;
-#endif
-}
-
-/*
- * Submit a regular SCSI operation (not an auto-sense).
- *
- * The Iron Law of Good Submit Routine is:
- * Zero return - callback is done, Nonzero return - callback is not done.
- * No exceptions.
- *
- * Host is assumed locked.
- */
-static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
-
- if (cmd->state != UB_CMDST_INIT ||
- (cmd->dir != UB_DIR_NONE && cmd->len == 0)) {
- return -EINVAL;
- }
-
- ub_cmdq_add(sc, cmd);
- /*
- * We can call ub_scsi_dispatch(sc) right away here, but it's a little
- * safer to jump to a tasklet, in case upper layers do something silly.
- */
- tasklet_schedule(&sc->tasklet);
- return 0;
-}
-
-/*
- * Submit the first URB for the queued command.
- * This function does not deal with queueing in any way.
- */
-static int ub_scsi_cmd_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct bulk_cb_wrap *bcb;
- int rc;
-
- bcb = &sc->work_bcb;
-
- /*
- * ``If the allocation length is eighteen or greater, and a device
- * server returns less than eithteen bytes of data, the application
- * client should assume that the bytes not transferred would have been
- * zeroes had the device server returned those bytes.''
- *
- * We zero sense for all commands so that when a packet request
- * fails it does not return a stale sense.
- */
- memset(&sc->top_sense, 0, UB_SENSE_SIZE);
-
- /* set up the command wrapper */
- bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
- bcb->Tag = cmd->tag; /* Endianness is not important */
- bcb->DataTransferLength = cpu_to_le32(cmd->len);
- bcb->Flags = (cmd->dir == UB_DIR_READ) ? 0x80 : 0;
- bcb->Lun = (cmd->lun != NULL) ? cmd->lun->num : 0;
- bcb->Length = cmd->cdb_len;
-
- /* copy the command payload */
- memcpy(bcb->CDB, cmd->cdb, UB_MAX_CDB_SIZE);
-
- UB_INIT_COMPLETION(sc->work_done);
-
- sc->last_pipe = sc->send_bulk_pipe;
- usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->send_bulk_pipe,
- bcb, US_BULK_CB_WRAP_LEN, ub_urb_complete, sc);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
- /* XXX Clear stalls */
- ub_complete(&sc->work_done);
- return rc;
- }
-
- sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
- add_timer(&sc->work_timer);
-
- cmd->state = UB_CMDST_CMD;
- return 0;
-}
-
-/*
- * Timeout handler.
- */
-static void ub_urb_timeout(unsigned long arg)
-{
- struct ub_dev *sc = (struct ub_dev *) arg;
- unsigned long flags;
-
- spin_lock_irqsave(sc->lock, flags);
- if (!ub_is_completed(&sc->work_done))
- usb_unlink_urb(&sc->work_urb);
- spin_unlock_irqrestore(sc->lock, flags);
-}
-
-/*
- * Completion routine for the work URB.
- *
- * This can be called directly from usb_submit_urb (while we have
- * the sc->lock taken) and from an interrupt (while we do NOT have
- * the sc->lock taken). Therefore, bounce this off to a tasklet.
- */
-static void ub_urb_complete(struct urb *urb)
-{
- struct ub_dev *sc = urb->context;
-
- ub_complete(&sc->work_done);
- tasklet_schedule(&sc->tasklet);
-}
-
-static void ub_scsi_action(unsigned long _dev)
-{
- struct ub_dev *sc = (struct ub_dev *) _dev;
- unsigned long flags;
-
- spin_lock_irqsave(sc->lock, flags);
- ub_scsi_dispatch(sc);
- spin_unlock_irqrestore(sc->lock, flags);
-}
-
-static void ub_scsi_dispatch(struct ub_dev *sc)
-{
- struct ub_scsi_cmd *cmd;
- int rc;
-
- while (!sc->reset && (cmd = ub_cmdq_peek(sc)) != NULL) {
- if (cmd->state == UB_CMDST_DONE) {
- ub_cmdq_pop(sc);
- (*cmd->done)(sc, cmd);
- } else if (cmd->state == UB_CMDST_INIT) {
- if ((rc = ub_scsi_cmd_start(sc, cmd)) == 0)
- break;
- cmd->error = rc;
- cmd->state = UB_CMDST_DONE;
- } else {
- if (!ub_is_completed(&sc->work_done))
- break;
- del_timer(&sc->work_timer);
- ub_scsi_urb_compl(sc, cmd);
- }
- }
-}
-
-static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct urb *urb = &sc->work_urb;
- struct bulk_cs_wrap *bcs;
- int endp;
- int len;
- int rc;
-
- if (atomic_read(&sc->poison)) {
- ub_state_done(sc, cmd, -ENODEV);
- return;
- }
-
- endp = usb_pipeendpoint(sc->last_pipe);
- if (usb_pipein(sc->last_pipe))
- endp |= USB_DIR_IN;
-
- if (cmd->state == UB_CMDST_CLEAR) {
- if (urb->status == -EPIPE) {
- /*
- * STALL while clearning STALL.
- * The control pipe clears itself - nothing to do.
- */
- printk(KERN_NOTICE "%s: stall on control pipe\n",
- sc->name);
- goto Bad_End;
- }
-
- /*
- * We ignore the result for the halt clear.
- */
-
- usb_reset_endpoint(sc->dev, endp);
-
- ub_state_sense(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_CLR2STS) {
- if (urb->status == -EPIPE) {
- printk(KERN_NOTICE "%s: stall on control pipe\n",
- sc->name);
- goto Bad_End;
- }
-
- /*
- * We ignore the result for the halt clear.
- */
-
- usb_reset_endpoint(sc->dev, endp);
-
- ub_state_stat(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_CLRRS) {
- if (urb->status == -EPIPE) {
- printk(KERN_NOTICE "%s: stall on control pipe\n",
- sc->name);
- goto Bad_End;
- }
-
- /*
- * We ignore the result for the halt clear.
- */
-
- usb_reset_endpoint(sc->dev, endp);
-
- ub_state_stat_counted(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_CMD) {
- switch (urb->status) {
- case 0:
- break;
- case -EOVERFLOW:
- goto Bad_End;
- case -EPIPE:
- rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
- if (rc != 0) {
- printk(KERN_NOTICE "%s: "
- "unable to submit clear (%d)\n",
- sc->name, rc);
- /*
- * This is typically ENOMEM or some other such shit.
- * Retrying is pointless. Just do Bad End on it...
- */
- ub_state_done(sc, cmd, rc);
- return;
- }
- cmd->state = UB_CMDST_CLEAR;
- return;
- case -ESHUTDOWN: /* unplug */
- case -EILSEQ: /* unplug timeout on uhci */
- ub_state_done(sc, cmd, -ENODEV);
- return;
- default:
- goto Bad_End;
- }
- if (urb->actual_length != US_BULK_CB_WRAP_LEN) {
- goto Bad_End;
- }
-
- if (cmd->dir == UB_DIR_NONE || cmd->nsg < 1) {
- ub_state_stat(sc, cmd);
- return;
- }
-
- // udelay(125); // usb-storage has this
- ub_data_start(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_DATA) {
- if (urb->status == -EPIPE) {
- rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
- if (rc != 0) {
- printk(KERN_NOTICE "%s: "
- "unable to submit clear (%d)\n",
- sc->name, rc);
- ub_state_done(sc, cmd, rc);
- return;
- }
- cmd->state = UB_CMDST_CLR2STS;
- return;
- }
- if (urb->status == -EOVERFLOW) {
- /*
- * A babble? Failure, but we must transfer CSW now.
- */
- cmd->error = -EOVERFLOW; /* A cheap trick... */
- ub_state_stat(sc, cmd);
- return;
- }
-
- if (cmd->dir == UB_DIR_WRITE) {
- /*
- * Do not continue writes in case of a failure.
- * Doing so would cause sectors to be mixed up,
- * which is worse than sectors lost.
- *
- * We must try to read the CSW, or many devices
- * get confused.
- */
- len = urb->actual_length;
- if (urb->status != 0 ||
- len != cmd->sgv[cmd->current_sg].length) {
- cmd->act_len += len;
-
- cmd->error = -EIO;
- ub_state_stat(sc, cmd);
- return;
- }
-
- } else {
- /*
- * If an error occurs on read, we record it, and
- * continue to fetch data in order to avoid bubble.
- *
- * As a small shortcut, we stop if we detect that
- * a CSW mixed into data.
- */
- if (urb->status != 0)
- cmd->error = -EIO;
-
- len = urb->actual_length;
- if (urb->status != 0 ||
- len != cmd->sgv[cmd->current_sg].length) {
- if ((len & 0x1FF) == US_BULK_CS_WRAP_LEN)
- goto Bad_End;
- }
- }
-
- cmd->act_len += urb->actual_length;
-
- if (++cmd->current_sg < cmd->nsg) {
- ub_data_start(sc, cmd);
- return;
- }
- ub_state_stat(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_STAT) {
- if (urb->status == -EPIPE) {
- rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
- if (rc != 0) {
- printk(KERN_NOTICE "%s: "
- "unable to submit clear (%d)\n",
- sc->name, rc);
- ub_state_done(sc, cmd, rc);
- return;
- }
-
- /*
- * Having a stall when getting CSW is an error, so
- * make sure uppper levels are not oblivious to it.
- */
- cmd->error = -EIO; /* A cheap trick... */
-
- cmd->state = UB_CMDST_CLRRS;
- return;
- }
-
- /* Catch everything, including -EOVERFLOW and other nasties. */
- if (urb->status != 0)
- goto Bad_End;
-
- if (urb->actual_length == 0) {
- ub_state_stat_counted(sc, cmd);
- return;
- }
-
- /*
- * Check the returned Bulk protocol status.
- * The status block has to be validated first.
- */
-
- bcs = &sc->work_bcs;
-
- if (sc->signature == cpu_to_le32(0)) {
- /*
- * This is the first reply, so do not perform the check.
- * Instead, remember the signature the device uses
- * for future checks. But do not allow a nul.
- */
- sc->signature = bcs->Signature;
- if (sc->signature == cpu_to_le32(0)) {
- ub_state_stat_counted(sc, cmd);
- return;
- }
- } else {
- if (bcs->Signature != sc->signature) {
- ub_state_stat_counted(sc, cmd);
- return;
- }
- }
-
- if (bcs->Tag != cmd->tag) {
- /*
- * This usually happens when we disagree with the
- * device's microcode about something. For instance,
- * a few of them throw this after timeouts. They buffer
- * commands and reply at commands we timed out before.
- * Without flushing these replies we loop forever.
- */
- ub_state_stat_counted(sc, cmd);
- return;
- }
-
- if (!sc->bad_resid) {
- len = le32_to_cpu(bcs->Residue);
- if (len != cmd->len - cmd->act_len) {
- /*
- * Only start ignoring if this cmd ended well.
- */
- if (cmd->len == cmd->act_len) {
- printk(KERN_NOTICE "%s: "
- "bad residual %d of %d, ignoring\n",
- sc->name, len, cmd->len);
- sc->bad_resid = 1;
- }
- }
- }
-
- switch (bcs->Status) {
- case US_BULK_STAT_OK:
- break;
- case US_BULK_STAT_FAIL:
- ub_state_sense(sc, cmd);
- return;
- case US_BULK_STAT_PHASE:
- goto Bad_End;
- default:
- printk(KERN_INFO "%s: unknown CSW status 0x%x\n",
- sc->name, bcs->Status);
- ub_state_done(sc, cmd, -EINVAL);
- return;
- }
-
- /* Not zeroing error to preserve a babble indicator */
- if (cmd->error != 0) {
- ub_state_sense(sc, cmd);
- return;
- }
- cmd->state = UB_CMDST_DONE;
- ub_cmdq_pop(sc);
- (*cmd->done)(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_SENSE) {
- ub_state_done(sc, cmd, -EIO);
-
- } else {
- printk(KERN_WARNING "%s: wrong command state %d\n",
- sc->name, cmd->state);
- ub_state_done(sc, cmd, -EINVAL);
- return;
- }
- return;
-
-Bad_End: /* Little Excel is dead */
- ub_state_done(sc, cmd, -EIO);
-}
-
-/*
- * Factorization helper for the command state machine:
- * Initiate a data segment transfer.
- */
-static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct scatterlist *sg = &cmd->sgv[cmd->current_sg];
- int pipe;
- int rc;
-
- UB_INIT_COMPLETION(sc->work_done);
-
- if (cmd->dir == UB_DIR_READ)
- pipe = sc->recv_bulk_pipe;
- else
- pipe = sc->send_bulk_pipe;
- sc->last_pipe = pipe;
- usb_fill_bulk_urb(&sc->work_urb, sc->dev, pipe, sg_virt(sg),
- sg->length, ub_urb_complete, sc);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
- /* XXX Clear stalls */
- ub_complete(&sc->work_done);
- ub_state_done(sc, cmd, rc);
- return;
- }
-
- if (cmd->timeo)
- sc->work_timer.expires = jiffies + cmd->timeo;
- else
- sc->work_timer.expires = jiffies + UB_DATA_TIMEOUT;
- add_timer(&sc->work_timer);
-
- cmd->state = UB_CMDST_DATA;
-}
-
-/*
- * Factorization helper for the command state machine:
- * Finish the command.
- */
-static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc)
-{
-
- cmd->error = rc;
- cmd->state = UB_CMDST_DONE;
- ub_cmdq_pop(sc);
- (*cmd->done)(sc, cmd);
-}
-
-/*
- * Factorization helper for the command state machine:
- * Submit a CSW read.
- */
-static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- int rc;
-
- UB_INIT_COMPLETION(sc->work_done);
-
- sc->last_pipe = sc->recv_bulk_pipe;
- usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->recv_bulk_pipe,
- &sc->work_bcs, US_BULK_CS_WRAP_LEN, ub_urb_complete, sc);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
- /* XXX Clear stalls */
- ub_complete(&sc->work_done);
- ub_state_done(sc, cmd, rc);
- return -1;
- }
-
- if (cmd->timeo)
- sc->work_timer.expires = jiffies + cmd->timeo;
- else
- sc->work_timer.expires = jiffies + UB_STAT_TIMEOUT;
- add_timer(&sc->work_timer);
- return 0;
-}
-
-/*
- * Factorization helper for the command state machine:
- * Submit a CSW read and go to STAT state.
- */
-static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
-
- if (__ub_state_stat(sc, cmd) != 0)
- return;
-
- cmd->stat_count = 0;
- cmd->state = UB_CMDST_STAT;
-}
-
-/*
- * Factorization helper for the command state machine:
- * Submit a CSW read and go to STAT state with counter (along [C] path).
- */
-static void ub_state_stat_counted(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
-
- if (++cmd->stat_count >= 4) {
- ub_state_sense(sc, cmd);
- return;
- }
-
- if (__ub_state_stat(sc, cmd) != 0)
- return;
-
- cmd->state = UB_CMDST_STAT;
-}
-
-/*
- * Factorization helper for the command state machine:
- * Submit a REQUEST SENSE and go to SENSE state.
- */
-static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct ub_scsi_cmd *scmd;
- struct scatterlist *sg;
- int rc;
-
- if (cmd->cdb[0] == REQUEST_SENSE) {
- rc = -EPIPE;
- goto error;
- }
-
- scmd = &sc->top_rqs_cmd;
- memset(scmd, 0, sizeof(struct ub_scsi_cmd));
- scmd->cdb[0] = REQUEST_SENSE;
- scmd->cdb[4] = UB_SENSE_SIZE;
- scmd->cdb_len = 6;
- scmd->dir = UB_DIR_READ;
- scmd->state = UB_CMDST_INIT;
- scmd->nsg = 1;
- sg = &scmd->sgv[0];
- sg_init_table(sg, UB_MAX_REQ_SG);
- sg_set_page(sg, virt_to_page(sc->top_sense), UB_SENSE_SIZE,
- (unsigned long)sc->top_sense & (PAGE_SIZE-1));
- scmd->len = UB_SENSE_SIZE;
- scmd->lun = cmd->lun;
- scmd->done = ub_top_sense_done;
- scmd->back = cmd;
-
- scmd->tag = sc->tagcnt++;
-
- cmd->state = UB_CMDST_SENSE;
-
- ub_cmdq_insert(sc, scmd);
- return;
-
-error:
- ub_state_done(sc, cmd, rc);
-}
-
-/*
- * A helper for the command's state machine:
- * Submit a stall clear.
- */
-static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
- int stalled_pipe)
-{
- int endp;
- struct usb_ctrlrequest *cr;
- int rc;
-
- endp = usb_pipeendpoint(stalled_pipe);
- if (usb_pipein (stalled_pipe))
- endp |= USB_DIR_IN;
-
- cr = &sc->work_cr;
- cr->bRequestType = USB_RECIP_ENDPOINT;
- cr->bRequest = USB_REQ_CLEAR_FEATURE;
- cr->wValue = cpu_to_le16(USB_ENDPOINT_HALT);
- cr->wIndex = cpu_to_le16(endp);
- cr->wLength = cpu_to_le16(0);
-
- UB_INIT_COMPLETION(sc->work_done);
-
- usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
- (unsigned char*) cr, NULL, 0, ub_urb_complete, sc);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
- ub_complete(&sc->work_done);
- return rc;
- }
-
- sc->work_timer.expires = jiffies + UB_CTRL_TIMEOUT;
- add_timer(&sc->work_timer);
- return 0;
-}
-
-/*
- */
-static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd)
-{
- unsigned char *sense = sc->top_sense;
- struct ub_scsi_cmd *cmd;
-
- /*
- * Find the command which triggered the unit attention or a check,
- * save the sense into it, and advance its state machine.
- */
- if ((cmd = ub_cmdq_peek(sc)) == NULL) {
- printk(KERN_WARNING "%s: sense done while idle\n", sc->name);
- return;
- }
- if (cmd != scmd->back) {
- printk(KERN_WARNING "%s: "
- "sense done for wrong command 0x%x\n",
- sc->name, cmd->tag);
- return;
- }
- if (cmd->state != UB_CMDST_SENSE) {
- printk(KERN_WARNING "%s: sense done with bad cmd state %d\n",
- sc->name, cmd->state);
- return;
- }
-
- /*
- * Ignoring scmd->act_len, because the buffer was pre-zeroed.
- */
- cmd->key = sense[2] & 0x0F;
- cmd->asc = sense[12];
- cmd->ascq = sense[13];
-
- ub_scsi_urb_compl(sc, cmd);
-}
-
-/*
- * Reset management
- */
-
-static void ub_reset_enter(struct ub_dev *sc, int try)
-{
-
- if (sc->reset) {
- /* This happens often on multi-LUN devices. */
- return;
- }
- sc->reset = try + 1;
-
-#if 0 /* Not needed because the disconnect waits for us. */
- unsigned long flags;
- spin_lock_irqsave(&ub_lock, flags);
- sc->openc++;
- spin_unlock_irqrestore(&ub_lock, flags);
-#endif
-
-#if 0 /* We let them stop themselves. */
- struct ub_lun *lun;
- list_for_each_entry(lun, &sc->luns, link) {
- blk_stop_queue(lun->disk->queue);
- }
-#endif
-
- schedule_work(&sc->reset_work);
-}
-
-static void ub_reset_task(struct work_struct *work)
-{
- struct ub_dev *sc = container_of(work, struct ub_dev, reset_work);
- unsigned long flags;
- struct ub_lun *lun;
- int rc;
-
- if (!sc->reset) {
- printk(KERN_WARNING "%s: Running reset unrequested\n",
- sc->name);
- return;
- }
-
- if (atomic_read(&sc->poison)) {
- ;
- } else if ((sc->reset & 1) == 0) {
- ub_sync_reset(sc);
- msleep(700); /* usb-storage sleeps 6s (!) */
- ub_probe_clear_stall(sc, sc->recv_bulk_pipe);
- ub_probe_clear_stall(sc, sc->send_bulk_pipe);
- } else if (sc->dev->actconfig->desc.bNumInterfaces != 1) {
- ;
- } else {
- rc = usb_lock_device_for_reset(sc->dev, sc->intf);
- if (rc < 0) {
- printk(KERN_NOTICE
- "%s: usb_lock_device_for_reset failed (%d)\n",
- sc->name, rc);
- } else {
- rc = usb_reset_device(sc->dev);
- if (rc < 0) {
- printk(KERN_NOTICE "%s: "
- "usb_lock_device_for_reset failed (%d)\n",
- sc->name, rc);
- }
- usb_unlock_device(sc->dev);
- }
- }
-
- /*
- * In theory, no commands can be running while reset is active,
- * so nobody can ask for another reset, and so we do not need any
- * queues of resets or anything. We do need a spinlock though,
- * to interact with block layer.
- */
- spin_lock_irqsave(sc->lock, flags);
- sc->reset = 0;
- tasklet_schedule(&sc->tasklet);
- list_for_each_entry(lun, &sc->luns, link) {
- blk_start_queue(lun->disk->queue);
- }
- wake_up(&sc->reset_wait);
- spin_unlock_irqrestore(sc->lock, flags);
-}
-
-/*
- * XXX Reset brackets are too much hassle to implement, so just stub them
- * in order to prevent forced unbinding (which deadlocks solid when our
- * ->disconnect method waits for the reset to complete and this kills keventd).
- *
- * XXX Tell Alan to move usb_unlock_device inside of usb_reset_device,
- * or else the post_reset is invoked, and restats I/O on a locked device.
- */
-static int ub_pre_reset(struct usb_interface *iface) {
- return 0;
-}
-
-static int ub_post_reset(struct usb_interface *iface) {
- return 0;
-}
-
-/*
- * This is called from a process context.
- */
-static void ub_revalidate(struct ub_dev *sc, struct ub_lun *lun)
-{
-
- lun->readonly = 0; /* XXX Query this from the device */
-
- lun->capacity.nsec = 0;
- lun->capacity.bsize = 512;
- lun->capacity.bshift = 0;
-
- if (ub_sync_tur(sc, lun) != 0)
- return; /* Not ready */
- lun->changed = 0;
-
- if (ub_sync_read_cap(sc, lun, &lun->capacity) != 0) {
- /*
- * The retry here means something is wrong, either with the
- * device, with the transport, or with our code.
- * We keep this because sd.c has retries for capacity.
- */
- if (ub_sync_read_cap(sc, lun, &lun->capacity) != 0) {
- lun->capacity.nsec = 0;
- lun->capacity.bsize = 512;
- lun->capacity.bshift = 0;
- }
- }
-}
-
-/*
- * The open funcion.
- * This is mostly needed to keep refcounting, but also to support
- * media checks on removable media drives.
- */
-static int ub_bd_open(struct block_device *bdev, fmode_t mode)
-{
- struct ub_lun *lun = bdev->bd_disk->private_data;
- struct ub_dev *sc = lun->udev;
- unsigned long flags;
- int rc;
-
- spin_lock_irqsave(&ub_lock, flags);
- if (atomic_read(&sc->poison)) {
- spin_unlock_irqrestore(&ub_lock, flags);
- return -ENXIO;
- }
- sc->openc++;
- spin_unlock_irqrestore(&ub_lock, flags);
-
- if (lun->removable || lun->readonly)
- check_disk_change(bdev);
-
- /*
- * The sd.c considers ->media_present and ->changed not equivalent,
- * under some pretty murky conditions (a failure of READ CAPACITY).
- * We may need it one day.
- */
- if (lun->removable && lun->changed && !(mode & FMODE_NDELAY)) {
- rc = -ENOMEDIUM;
- goto err_open;
- }
-
- if (lun->readonly && (mode & FMODE_WRITE)) {
- rc = -EROFS;
- goto err_open;
- }
-
- return 0;
-
-err_open:
- ub_put(sc);
- return rc;
-}
-
-static int ub_bd_unlocked_open(struct block_device *bdev, fmode_t mode)
-{
- int ret;
-
- mutex_lock(&ub_mutex);
- ret = ub_bd_open(bdev, mode);
- mutex_unlock(&ub_mutex);
-
- return ret;
-}
-
-
-/*
- */
-static int ub_bd_release(struct gendisk *disk, fmode_t mode)
-{
- struct ub_lun *lun = disk->private_data;
- struct ub_dev *sc = lun->udev;
-
- mutex_lock(&ub_mutex);
- ub_put(sc);
- mutex_unlock(&ub_mutex);
-
- return 0;
-}
-
-/*
- * The ioctl interface.
- */
-static int ub_bd_ioctl(struct block_device *bdev, fmode_t mode,
- unsigned int cmd, unsigned long arg)
-{
- void __user *usermem = (void __user *) arg;
- int ret;
-
- mutex_lock(&ub_mutex);
- ret = scsi_cmd_blk_ioctl(bdev, mode, cmd, usermem);
- mutex_unlock(&ub_mutex);
-
- return ret;
-}
-
-/*
- * This is called by check_disk_change if we reported a media change.
- * The main onjective here is to discover the features of the media such as
- * the capacity, read-only status, etc. USB storage generally does not
- * need to be spun up, but if we needed it, this would be the place.
- *
- * This call can sleep.
- *
- * The return code is not used.
- */
-static int ub_bd_revalidate(struct gendisk *disk)
-{
- struct ub_lun *lun = disk->private_data;
-
- ub_revalidate(lun->udev, lun);
-
- /* XXX Support sector size switching like in sr.c */
- blk_queue_logical_block_size(disk->queue, lun->capacity.bsize);
- set_capacity(disk, lun->capacity.nsec);
- // set_disk_ro(sdkp->disk, lun->readonly);
-
- return 0;
-}
-
-/*
- * The check is called by the block layer to verify if the media
- * is still available. It is supposed to be harmless, lightweight and
- * non-intrusive in case the media was not changed.
- *
- * This call can sleep.
- *
- * The return code is bool!
- */
-static unsigned int ub_bd_check_events(struct gendisk *disk,
- unsigned int clearing)
-{
- struct ub_lun *lun = disk->private_data;
-
- if (!lun->removable)
- return 0;
-
- /*
- * We clean checks always after every command, so this is not
- * as dangerous as it looks. If the TEST_UNIT_READY fails here,
- * the device is actually not ready with operator or software
- * intervention required. One dangerous item might be a drive which
- * spins itself down, and come the time to write dirty pages, this
- * will fail, then block layer discards the data. Since we never
- * spin drives up, such devices simply cannot be used with ub anyway.
- */
- if (ub_sync_tur(lun->udev, lun) != 0) {
- lun->changed = 1;
- return DISK_EVENT_MEDIA_CHANGE;
- }
-
- return lun->changed ? DISK_EVENT_MEDIA_CHANGE : 0;
-}
-
-static const struct block_device_operations ub_bd_fops = {
- .owner = THIS_MODULE,
- .open = ub_bd_unlocked_open,
- .release = ub_bd_release,
- .ioctl = ub_bd_ioctl,
- .check_events = ub_bd_check_events,
- .revalidate_disk = ub_bd_revalidate,
-};
-
-/*
- * Common ->done routine for commands executed synchronously.
- */
-static void ub_probe_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct completion *cop = cmd->back;
- complete(cop);
-}
-
-/*
- * Test if the device has a check condition on it, synchronously.
- */
-static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun)
-{
- struct ub_scsi_cmd *cmd;
- enum { ALLOC_SIZE = sizeof(struct ub_scsi_cmd) };
- unsigned long flags;
- struct completion compl;
- int rc;
-
- init_completion(&compl);
-
- rc = -ENOMEM;
- if ((cmd = kzalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL)
- goto err_alloc;
-
- cmd->cdb[0] = TEST_UNIT_READY;
- cmd->cdb_len = 6;
- cmd->dir = UB_DIR_NONE;
- cmd->state = UB_CMDST_INIT;
- cmd->lun = lun; /* This may be NULL, but that's ok */
- cmd->done = ub_probe_done;
- cmd->back = &compl;
-
- spin_lock_irqsave(sc->lock, flags);
- cmd->tag = sc->tagcnt++;
-
- rc = ub_submit_scsi(sc, cmd);
- spin_unlock_irqrestore(sc->lock, flags);
-
- if (rc != 0)
- goto err_submit;
-
- wait_for_completion(&compl);
-
- rc = cmd->error;
-
- if (rc == -EIO && cmd->key != 0) /* Retries for benh's key */
- rc = cmd->key;
-
-err_submit:
- kfree(cmd);
-err_alloc:
- return rc;
-}
-
-/*
- * Read the SCSI capacity synchronously (for probing).
- */
-static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_capacity *ret)
-{
- struct ub_scsi_cmd *cmd;
- struct scatterlist *sg;
- char *p;
- enum { ALLOC_SIZE = sizeof(struct ub_scsi_cmd) + 8 };
- unsigned long flags;
- unsigned int bsize, shift;
- unsigned long nsec;
- struct completion compl;
- int rc;
-
- init_completion(&compl);
-
- rc = -ENOMEM;
- if ((cmd = kzalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL)
- goto err_alloc;
- p = (char *)cmd + sizeof(struct ub_scsi_cmd);
-
- cmd->cdb[0] = 0x25;
- cmd->cdb_len = 10;
- cmd->dir = UB_DIR_READ;
- cmd->state = UB_CMDST_INIT;
- cmd->nsg = 1;
- sg = &cmd->sgv[0];
- sg_init_table(sg, UB_MAX_REQ_SG);
- sg_set_page(sg, virt_to_page(p), 8, (unsigned long)p & (PAGE_SIZE-1));
- cmd->len = 8;
- cmd->lun = lun;
- cmd->done = ub_probe_done;
- cmd->back = &compl;
-
- spin_lock_irqsave(sc->lock, flags);
- cmd->tag = sc->tagcnt++;
-
- rc = ub_submit_scsi(sc, cmd);
- spin_unlock_irqrestore(sc->lock, flags);
-
- if (rc != 0)
- goto err_submit;
-
- wait_for_completion(&compl);
-
- if (cmd->error != 0) {
- rc = -EIO;
- goto err_read;
- }
- if (cmd->act_len != 8) {
- rc = -EIO;
- goto err_read;
- }
-
- /* sd.c special-cases sector size of 0 to mean 512. Needed? Safe? */
- nsec = be32_to_cpu(*(__be32 *)p) + 1;
- bsize = be32_to_cpu(*(__be32 *)(p + 4));
- switch (bsize) {
- case 512: shift = 0; break;
- case 1024: shift = 1; break;
- case 2048: shift = 2; break;
- case 4096: shift = 3; break;
- default:
- rc = -EDOM;
- goto err_inv_bsize;
- }
-
- ret->bsize = bsize;
- ret->bshift = shift;
- ret->nsec = nsec << shift;
- rc = 0;
-
-err_inv_bsize:
-err_read:
-err_submit:
- kfree(cmd);
-err_alloc:
- return rc;
-}
-
-/*
- */
-static void ub_probe_urb_complete(struct urb *urb)
-{
- struct completion *cop = urb->context;
- complete(cop);
-}
-
-static void ub_probe_timeout(unsigned long arg)
-{
- struct completion *cop = (struct completion *) arg;
- complete(cop);
-}
-
-/*
- * Reset with a Bulk reset.
- */
-static int ub_sync_reset(struct ub_dev *sc)
-{
- int ifnum = sc->intf->cur_altsetting->desc.bInterfaceNumber;
- struct usb_ctrlrequest *cr;
- struct completion compl;
- struct timer_list timer;
- int rc;
-
- init_completion(&compl);
-
- cr = &sc->work_cr;
- cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- cr->bRequest = US_BULK_RESET_REQUEST;
- cr->wValue = cpu_to_le16(0);
- cr->wIndex = cpu_to_le16(ifnum);
- cr->wLength = cpu_to_le16(0);
-
- usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
- (unsigned char*) cr, NULL, 0, ub_probe_urb_complete, &compl);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) {
- printk(KERN_WARNING
- "%s: Unable to submit a bulk reset (%d)\n", sc->name, rc);
- return rc;
- }
-
- init_timer(&timer);
- timer.function = ub_probe_timeout;
- timer.data = (unsigned long) &compl;
- timer.expires = jiffies + UB_CTRL_TIMEOUT;
- add_timer(&timer);
-
- wait_for_completion(&compl);
-
- del_timer_sync(&timer);
- usb_kill_urb(&sc->work_urb);
-
- return sc->work_urb.status;
-}
-
-/*
- * Get number of LUNs by the way of Bulk GetMaxLUN command.
- */
-static int ub_sync_getmaxlun(struct ub_dev *sc)
-{
- int ifnum = sc->intf->cur_altsetting->desc.bInterfaceNumber;
- unsigned char *p;
- enum { ALLOC_SIZE = 1 };
- struct usb_ctrlrequest *cr;
- struct completion compl;
- struct timer_list timer;
- int nluns;
- int rc;
-
- init_completion(&compl);
-
- rc = -ENOMEM;
- if ((p = kmalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL)
- goto err_alloc;
- *p = 55;
-
- cr = &sc->work_cr;
- cr->bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- cr->bRequest = US_BULK_GET_MAX_LUN;
- cr->wValue = cpu_to_le16(0);
- cr->wIndex = cpu_to_le16(ifnum);
- cr->wLength = cpu_to_le16(1);
-
- usb_fill_control_urb(&sc->work_urb, sc->dev, sc->recv_ctrl_pipe,
- (unsigned char*) cr, p, 1, ub_probe_urb_complete, &compl);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0)
- goto err_submit;
-
- init_timer(&timer);
- timer.function = ub_probe_timeout;
- timer.data = (unsigned long) &compl;
- timer.expires = jiffies + UB_CTRL_TIMEOUT;
- add_timer(&timer);
-
- wait_for_completion(&compl);
-
- del_timer_sync(&timer);
- usb_kill_urb(&sc->work_urb);
-
- if ((rc = sc->work_urb.status) < 0)
- goto err_io;
-
- if (sc->work_urb.actual_length != 1) {
- nluns = 0;
- } else {
- if ((nluns = *p) == 55) {
- nluns = 0;
- } else {
- /* GetMaxLUN returns the maximum LUN number */
- nluns += 1;
- if (nluns > UB_MAX_LUNS)
- nluns = UB_MAX_LUNS;
- }
- }
-
- kfree(p);
- return nluns;
-
-err_io:
-err_submit:
- kfree(p);
-err_alloc:
- return rc;
-}
-
-/*
- * Clear initial stalls.
- */
-static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe)
-{
- int endp;
- struct usb_ctrlrequest *cr;
- struct completion compl;
- struct timer_list timer;
- int rc;
-
- init_completion(&compl);
-
- endp = usb_pipeendpoint(stalled_pipe);
- if (usb_pipein (stalled_pipe))
- endp |= USB_DIR_IN;
-
- cr = &sc->work_cr;
- cr->bRequestType = USB_RECIP_ENDPOINT;
- cr->bRequest = USB_REQ_CLEAR_FEATURE;
- cr->wValue = cpu_to_le16(USB_ENDPOINT_HALT);
- cr->wIndex = cpu_to_le16(endp);
- cr->wLength = cpu_to_le16(0);
-
- usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
- (unsigned char*) cr, NULL, 0, ub_probe_urb_complete, &compl);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) {
- printk(KERN_WARNING
- "%s: Unable to submit a probe clear (%d)\n", sc->name, rc);
- return rc;
- }
-
- init_timer(&timer);
- timer.function = ub_probe_timeout;
- timer.data = (unsigned long) &compl;
- timer.expires = jiffies + UB_CTRL_TIMEOUT;
- add_timer(&timer);
-
- wait_for_completion(&compl);
-
- del_timer_sync(&timer);
- usb_kill_urb(&sc->work_urb);
-
- usb_reset_endpoint(sc->dev, endp);
-
- return 0;
-}
-
-/*
- * Get the pipe settings.
- */
-static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev,
- struct usb_interface *intf)
-{
- struct usb_host_interface *altsetting = intf->cur_altsetting;
- struct usb_endpoint_descriptor *ep_in = NULL;
- struct usb_endpoint_descriptor *ep_out = NULL;
- struct usb_endpoint_descriptor *ep;
- int i;
-
- /*
- * Find the endpoints we need.
- * We are expecting a minimum of 2 endpoints - in and out (bulk).
- * We will ignore any others.
- */
- for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
- ep = &altsetting->endpoint[i].desc;
-
- /* Is it a BULK endpoint? */
- if (usb_endpoint_xfer_bulk(ep)) {
- /* BULK in or out? */
- if (usb_endpoint_dir_in(ep)) {
- if (ep_in == NULL)
- ep_in = ep;
- } else {
- if (ep_out == NULL)
- ep_out = ep;
- }
- }
- }
-
- if (ep_in == NULL || ep_out == NULL) {
- printk(KERN_NOTICE "%s: failed endpoint check\n", sc->name);
- return -ENODEV;
- }
-
- /* Calculate and store the pipe values */
- sc->send_ctrl_pipe = usb_sndctrlpipe(dev, 0);
- sc->recv_ctrl_pipe = usb_rcvctrlpipe(dev, 0);
- sc->send_bulk_pipe = usb_sndbulkpipe(dev,
- usb_endpoint_num(ep_out));
- sc->recv_bulk_pipe = usb_rcvbulkpipe(dev,
- usb_endpoint_num(ep_in));
-
- return 0;
-}
-
-/*
- * Probing is done in the process context, which allows us to cheat
- * and not to build a state machine for the discovery.
- */
-static int ub_probe(struct usb_interface *intf,
- const struct usb_device_id *dev_id)
-{
- struct ub_dev *sc;
- int nluns;
- int rc;
- int i;
-
- if (usb_usual_check_type(dev_id, USB_US_TYPE_UB))
- return -ENXIO;
-
- rc = -ENOMEM;
- if ((sc = kzalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL)
- goto err_core;
- sc->lock = ub_next_lock();
- INIT_LIST_HEAD(&sc->luns);
- usb_init_urb(&sc->work_urb);
- tasklet_init(&sc->tasklet, ub_scsi_action, (unsigned long)sc);
- atomic_set(&sc->poison, 0);
- INIT_WORK(&sc->reset_work, ub_reset_task);
- init_waitqueue_head(&sc->reset_wait);
-
- init_timer(&sc->work_timer);
- sc->work_timer.data = (unsigned long) sc;
- sc->work_timer.function = ub_urb_timeout;
-
- ub_init_completion(&sc->work_done);
- sc->work_done.done = 1; /* A little yuk, but oh well... */
-
- sc->dev = interface_to_usbdev(intf);
- sc->intf = intf;
- // sc->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
- usb_set_intfdata(intf, sc);
- usb_get_dev(sc->dev);
- /*
- * Since we give the interface struct to the block level through
- * disk->driverfs_dev, we have to pin it. Otherwise, block_uevent
- * oopses on close after a disconnect (kernels 2.6.16 and up).
- */
- usb_get_intf(sc->intf);
-
- snprintf(sc->name, 12, DRV_NAME "(%d.%d)",
- sc->dev->bus->busnum, sc->dev->devnum);
-
- /* XXX Verify that we can handle the device (from descriptors) */
-
- if (ub_get_pipes(sc, sc->dev, intf) != 0)
- goto err_dev_desc;
-
- /*
- * At this point, all USB initialization is done, do upper layer.
- * We really hate halfway initialized structures, so from the
- * invariants perspective, this ub_dev is fully constructed at
- * this point.
- */
-
- /*
- * This is needed to clear toggles. It is a problem only if we do
- * `rmmod ub && modprobe ub` without disconnects, but we like that.
- */
-#if 0 /* iPod Mini fails if we do this (big white iPod works) */
- ub_probe_clear_stall(sc, sc->recv_bulk_pipe);
- ub_probe_clear_stall(sc, sc->send_bulk_pipe);
-#endif
-
- /*
- * The way this is used by the startup code is a little specific.
- * A SCSI check causes a USB stall. Our common case code sees it
- * and clears the check, after which the device is ready for use.
- * But if a check was not present, any command other than
- * TEST_UNIT_READY ends with a lockup (including REQUEST_SENSE).
- *
- * If we neglect to clear the SCSI check, the first real command fails
- * (which is the capacity readout). We clear that and retry, but why
- * causing spurious retries for no reason.
- *
- * Revalidation may start with its own TEST_UNIT_READY, but that one
- * has to succeed, so we clear checks with an additional one here.
- * In any case it's not our business how revaliadation is implemented.
- */
- for (i = 0; i < 3; i++) { /* Retries for the schwag key from KS'04 */
- if ((rc = ub_sync_tur(sc, NULL)) <= 0) break;
- if (rc != 0x6) break;
- msleep(10);
- }
-
- nluns = 1;
- for (i = 0; i < 3; i++) {
- if ((rc = ub_sync_getmaxlun(sc)) < 0)
- break;
- if (rc != 0) {
- nluns = rc;
- break;
- }
- msleep(100);
- }
-
- for (i = 0; i < nluns; i++) {
- ub_probe_lun(sc, i);
- }
- return 0;
-
-err_dev_desc:
- usb_set_intfdata(intf, NULL);
- usb_put_intf(sc->intf);
- usb_put_dev(sc->dev);
- kfree(sc);
-err_core:
- return rc;
-}
-
-static int ub_probe_lun(struct ub_dev *sc, int lnum)
-{
- struct ub_lun *lun;
- struct request_queue *q;
- struct gendisk *disk;
- int rc;
-
- rc = -ENOMEM;
- if ((lun = kzalloc(sizeof(struct ub_lun), GFP_KERNEL)) == NULL)
- goto err_alloc;
- lun->num = lnum;
-
- rc = -ENOSR;
- if ((lun->id = ub_id_get()) == -1)
- goto err_id;
-
- lun->udev = sc;
-
- snprintf(lun->name, 16, DRV_NAME "%c(%d.%d.%d)",
- lun->id + 'a', sc->dev->bus->busnum, sc->dev->devnum, lun->num);
-
- lun->removable = 1; /* XXX Query this from the device */
- lun->changed = 1; /* ub_revalidate clears only */
- ub_revalidate(sc, lun);
-
- rc = -ENOMEM;
- if ((disk = alloc_disk(UB_PARTS_PER_LUN)) == NULL)
- goto err_diskalloc;
-
- sprintf(disk->disk_name, DRV_NAME "%c", lun->id + 'a');
- disk->major = UB_MAJOR;
- disk->first_minor = lun->id * UB_PARTS_PER_LUN;
- disk->fops = &ub_bd_fops;
- disk->private_data = lun;
- disk->driverfs_dev = &sc->intf->dev;
-
- rc = -ENOMEM;
- if ((q = blk_init_queue(ub_request_fn, sc->lock)) == NULL)
- goto err_blkqinit;
-
- disk->queue = q;
-
- blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);
- blk_queue_max_segments(q, UB_MAX_REQ_SG);
- blk_queue_segment_boundary(q, 0xffffffff); /* Dubious. */
- blk_queue_max_hw_sectors(q, UB_MAX_SECTORS);
- blk_queue_logical_block_size(q, lun->capacity.bsize);
-
- lun->disk = disk;
- q->queuedata = lun;
- list_add(&lun->link, &sc->luns);
-
- set_capacity(disk, lun->capacity.nsec);
- if (lun->removable)
- disk->flags |= GENHD_FL_REMOVABLE;
-
- add_disk(disk);
-
- return 0;
-
-err_blkqinit:
- put_disk(disk);
-err_diskalloc:
- ub_id_put(lun->id);
-err_id:
- kfree(lun);
-err_alloc:
- return rc;
-}
-
-static void ub_disconnect(struct usb_interface *intf)
-{
- struct ub_dev *sc = usb_get_intfdata(intf);
- struct ub_lun *lun;
- unsigned long flags;
-
- /*
- * Prevent ub_bd_release from pulling the rug from under us.
- * XXX This is starting to look like a kref.
- * XXX Why not to take this ref at probe time?
- */
- spin_lock_irqsave(&ub_lock, flags);
- sc->openc++;
- spin_unlock_irqrestore(&ub_lock, flags);
-
- /*
- * Fence stall clearings, operations triggered by unlinkings and so on.
- * We do not attempt to unlink any URBs, because we do not trust the
- * unlink paths in HC drivers. Also, we get -84 upon disconnect anyway.
- */
- atomic_set(&sc->poison, 1);
-
- /*
- * Wait for reset to end, if any.
- */
- wait_event(sc->reset_wait, !sc->reset);
-
- /*
- * Blow away queued commands.
- *
- * Actually, this never works, because before we get here
- * the HCD terminates outstanding URB(s). It causes our
- * SCSI command queue to advance, commands fail to submit,
- * and the whole queue drains. So, we just use this code to
- * print warnings.
- */
- spin_lock_irqsave(sc->lock, flags);
- {
- struct ub_scsi_cmd *cmd;
- int cnt = 0;
- while ((cmd = ub_cmdq_peek(sc)) != NULL) {
- cmd->error = -ENOTCONN;
- cmd->state = UB_CMDST_DONE;
- ub_cmdq_pop(sc);
- (*cmd->done)(sc, cmd);
- cnt++;
- }
- if (cnt != 0) {
- printk(KERN_WARNING "%s: "
- "%d was queued after shutdown\n", sc->name, cnt);
- }
- }
- spin_unlock_irqrestore(sc->lock, flags);
-
- /*
- * Unregister the upper layer.
- */
- list_for_each_entry(lun, &sc->luns, link) {
- del_gendisk(lun->disk);
- /*
- * I wish I could do:
- * queue_flag_set(QUEUE_FLAG_DEAD, q);
- * As it is, we rely on our internal poisoning and let
- * the upper levels to spin furiously failing all the I/O.
- */
- }
-
- /*
- * Testing for -EINPROGRESS is always a bug, so we are bending
- * the rules a little.
- */
- spin_lock_irqsave(sc->lock, flags);
- if (sc->work_urb.status == -EINPROGRESS) { /* janitors: ignore */
- printk(KERN_WARNING "%s: "
- "URB is active after disconnect\n", sc->name);
- }
- spin_unlock_irqrestore(sc->lock, flags);
-
- /*
- * There is virtually no chance that other CPU runs a timeout so long
- * after ub_urb_complete should have called del_timer, but only if HCD
- * didn't forget to deliver a callback on unlink.
- */
- del_timer_sync(&sc->work_timer);
-
- /*
- * At this point there must be no commands coming from anyone
- * and no URBs left in transit.
- */
-
- ub_put(sc);
-}
-
-static struct usb_driver ub_driver = {
- .name = "ub",
- .probe = ub_probe,
- .disconnect = ub_disconnect,
- .id_table = ub_usb_ids,
- .pre_reset = ub_pre_reset,
- .post_reset = ub_post_reset,
-};
-
-static int __init ub_init(void)
-{
- int rc;
- int i;
-
- pr_info("'Low Performance USB Block' driver is deprecated. "
- "Please switch to usb-storage\n");
- for (i = 0; i < UB_QLOCK_NUM; i++)
- spin_lock_init(&ub_qlockv[i]);
-
- if ((rc = register_blkdev(UB_MAJOR, DRV_NAME)) != 0)
- goto err_regblkdev;
-
- if ((rc = usb_register(&ub_driver)) != 0)
- goto err_register;
-
- usb_usual_set_present(USB_US_TYPE_UB);
- return 0;
-
-err_register:
- unregister_blkdev(UB_MAJOR, DRV_NAME);
-err_regblkdev:
- return rc;
-}
-
-static void __exit ub_exit(void)
-{
- usb_deregister(&ub_driver);
-
- unregister_blkdev(UB_MAJOR, DRV_NAME);
- usb_usual_clear_present(USB_US_TYPE_UB);
-}
-
-module_init(ub_init);
-module_exit(ub_exit);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c
index 73f196ca713f..c6decb901e5e 100644
--- a/drivers/block/xen-blkback/blkback.c
+++ b/drivers/block/xen-blkback/blkback.c
@@ -337,7 +337,7 @@ static void xen_blkbk_unmap(struct pending_req *req)
invcount++;
}
- ret = gnttab_unmap_refs(unmap, pages, invcount, false);
+ ret = gnttab_unmap_refs(unmap, NULL, pages, invcount);
BUG_ON(ret);
}
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
new file mode 100644
index 000000000000..bbec35d21fe5
--- /dev/null
+++ b/drivers/bus/Kconfig
@@ -0,0 +1,21 @@
+#
+# Bus Devices
+#
+
+menu "Bus devices"
+
+config OMAP_OCP2SCP
+ tristate "OMAP OCP2SCP DRIVER"
+ help
+ Driver to enable ocp2scp module which transforms ocp interface
+ protocol to scp protocol. In OMAP4, USB PHY is connected via
+ OCP2SCP and in OMAP5, both USB PHY and SATA PHY is connected via
+ OCP2SCP.
+
+config OMAP_INTERCONNECT
+ tristate "OMAP INTERCONNECT DRIVER"
+ depends on ARCH_OMAP2PLUS
+
+ help
+ Driver to enable OMAP interconnect error handling driver.
+endmenu
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
new file mode 100644
index 000000000000..45d997c85453
--- /dev/null
+++ b/drivers/bus/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the bus drivers.
+#
+
+obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
+
+# Interconnect bus driver for OMAP SoCs.
+obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
diff --git a/drivers/bus/omap-ocp2scp.c b/drivers/bus/omap-ocp2scp.c
new file mode 100644
index 000000000000..ff63560b8467
--- /dev/null
+++ b/drivers/bus/omap-ocp2scp.c
@@ -0,0 +1,88 @@
+/*
+ * omap-ocp2scp.c - transform ocp interface protocol to scp protocol
+ *
+ * Copyright (C) 2012 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+static int ocp2scp_remove_devices(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
+static int __devinit omap_ocp2scp_probe(struct platform_device *pdev)
+{
+ int ret;
+ struct device_node *np = pdev->dev.of_node;
+
+ if (np) {
+ ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add resources for ocp2scp child\n");
+ goto err0;
+ }
+ }
+ pm_runtime_enable(&pdev->dev);
+
+ return 0;
+
+err0:
+ device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices);
+
+ return ret;
+}
+
+static int __devexit omap_ocp2scp_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ device_for_each_child(&pdev->dev, NULL, ocp2scp_remove_devices);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id omap_ocp2scp_id_table[] = {
+ { .compatible = "ti,omap-ocp2scp" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, omap_ocp2scp_id_table);
+#endif
+
+static struct platform_driver omap_ocp2scp_driver = {
+ .probe = omap_ocp2scp_probe,
+ .remove = __devexit_p(omap_ocp2scp_remove),
+ .driver = {
+ .name = "omap-ocp2scp",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(omap_ocp2scp_id_table),
+ },
+};
+
+module_platform_driver(omap_ocp2scp_driver);
+
+MODULE_ALIAS("platform: omap-ocp2scp");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP OCP2SCP driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-omap2/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c
index f447e02102bb..44b2b3e57882 100644
--- a/arch/arm/mach-omap2/omap_l3_noc.c
+++ b/drivers/bus/omap_l3_noc.c
@@ -191,7 +191,7 @@ static int __devinit omap4_l3_probe(struct platform_device *pdev)
IRQF_DISABLED, "l3-dbg-irq", l3);
if (ret) {
pr_crit("L3: request_irq failed to register for 0x%x\n",
- 9 + OMAP44XX_IRQ_GIC_START);
+ l3->debug_irq);
goto err3;
}
@@ -201,7 +201,7 @@ static int __devinit omap4_l3_probe(struct platform_device *pdev)
IRQF_DISABLED, "l3-app-irq", l3);
if (ret) {
pr_crit("L3: request_irq failed to register for 0x%x\n",
- 10 + OMAP44XX_IRQ_GIC_START);
+ l3->app_irq);
goto err4;
}
diff --git a/arch/arm/mach-omap2/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h
index a6ce34dc4814..a6ce34dc4814 100644
--- a/arch/arm/mach-omap2/omap_l3_noc.h
+++ b/drivers/bus/omap_l3_noc.h
diff --git a/arch/arm/mach-omap2/omap_l3_smx.c b/drivers/bus/omap_l3_smx.c
index acc216491b8a..acc216491b8a 100644
--- a/arch/arm/mach-omap2/omap_l3_smx.c
+++ b/drivers/bus/omap_l3_smx.c
diff --git a/arch/arm/mach-omap2/omap_l3_smx.h b/drivers/bus/omap_l3_smx.h
index 4f3cebca4179..4f3cebca4179 100644
--- a/arch/arm/mach-omap2/omap_l3_smx.h
+++ b/drivers/bus/omap_l3_smx.h
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index ea6f6325f9ba..72bedad6bf8c 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -418,8 +418,8 @@ config APPLICOM
If unsure, say N.
config SONYPI
- tristate "Sony Vaio Programmable I/O Control Device support (EXPERIMENTAL)"
- depends on EXPERIMENTAL && X86 && PCI && INPUT && !64BIT
+ tristate "Sony Vaio Programmable I/O Control Device support"
+ depends on X86 && PCI && INPUT && !64BIT
---help---
This driver enables access to the Sony Programmable I/O Control
Device which can be found in many (all ?) Sony Vaio laptops.
@@ -566,7 +566,7 @@ source "drivers/char/tpm/Kconfig"
config TELCLOCK
tristate "Telecom clock driver for ATCA SBC"
- depends on EXPERIMENTAL && X86
+ depends on X86
default n
help
The telecom clock device is specific to the MPCBL0010 and MPCBL0050
diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c
index 192000377737..3a5af2f9b015 100644
--- a/drivers/char/agp/sgi-agp.c
+++ b/drivers/char/agp/sgi-agp.c
@@ -289,12 +289,11 @@ static int __devinit agp_sgi_init(void)
j = 0;
list_for_each_entry(info, &tioca_list, ca_list) {
- struct list_head *tmp;
if (list_empty(info->ca_devices))
continue;
- list_for_each(tmp, info->ca_devices) {
+ list_for_each_entry(pdev, info->ca_devices, bus_list) {
u8 cap_ptr;
- pdev = pci_dev_b(tmp);
+
if (pdev->class != (PCI_CLASS_DISPLAY_VGA << 8))
continue;
cap_ptr = pci_find_capability(pdev, PCI_CAP_ID_AGP);
diff --git a/drivers/char/mmtimer.c b/drivers/char/mmtimer.c
index 33dc2298af73..3d6c0671e996 100644
--- a/drivers/char/mmtimer.c
+++ b/drivers/char/mmtimer.c
@@ -826,7 +826,7 @@ static int __init mmtimer_init(void)
/* Allocate list of node ptrs to mmtimer_t's */
timers = kzalloc(sizeof(struct mmtimer_node)*maxn, GFP_KERNEL);
- if (timers == NULL) {
+ if (!timers) {
printk(KERN_ERR "%s: failed to allocate memory for device\n",
MMTIMER_NAME);
goto out3;
@@ -848,7 +848,6 @@ static int __init mmtimer_init(void)
return 0;
out3:
- kfree(timers);
misc_deregister(&mmtimer_miscdev);
out2:
free_irq(SGI_MMTIMER_VECTOR, NULL);
diff --git a/drivers/char/nwbutton.c b/drivers/char/nwbutton.c
index 04a480f86c6c..cfdfe493c6af 100644
--- a/drivers/char/nwbutton.c
+++ b/drivers/char/nwbutton.c
@@ -93,9 +93,9 @@ int button_del_callback (void (*callback) (void))
button_callback_list [lp].count = 0;
callback_count--;
return 0;
- };
+ }
lp--;
- };
+ }
return -EINVAL;
}
diff --git a/drivers/char/nwflash.c b/drivers/char/nwflash.c
index d45c3345b4af..a0e2f7d70355 100644
--- a/drivers/char/nwflash.c
+++ b/drivers/char/nwflash.c
@@ -30,7 +30,6 @@
#include <asm/hardware/dec21285.h>
#include <asm/io.h>
-#include <asm/leds.h>
#include <asm/mach-types.h>
#include <asm/uaccess.h>
@@ -179,9 +178,6 @@ static ssize_t flash_write(struct file *file, const char __user *buf,
written = 0;
- leds_event(led_claim);
- leds_event(led_green_on);
-
nBlock = (int) p >> 16; //block # of 64K bytes
/*
@@ -258,11 +254,6 @@ static ssize_t flash_write(struct file *file, const char __user *buf,
printk(KERN_DEBUG "flash_write: written 0x%X bytes OK.\n", written);
}
- /*
- * restore reg on exit
- */
- leds_event(led_release);
-
mutex_unlock(&nwflash_mutex);
return written;
@@ -334,11 +325,6 @@ static int erase_block(int nBlock)
int temp, temp1;
/*
- * orange LED == erase
- */
- leds_event(led_amber_on);
-
- /*
* reset footbridge to the correct offset 0 (...0..3)
*/
*CSR_ROMWRITEREG = 0;
@@ -446,12 +432,6 @@ static int write_block(unsigned long p, const char __user *buf, int count)
unsigned long timeout;
unsigned long timeout1;
- /*
- * red LED == write
- */
- leds_event(led_amber_off);
- leds_event(led_red_on);
-
pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
/*
@@ -558,17 +538,9 @@ static int write_block(unsigned long p, const char __user *buf, int count)
pWritePtr - FLASH_BASE);
/*
- * no LED == waiting
- */
- leds_event(led_amber_off);
- /*
* wait couple ms
*/
msleep(10);
- /*
- * red LED == write
- */
- leds_event(led_red_on);
goto WriteRetry;
} else {
@@ -583,12 +555,6 @@ static int write_block(unsigned long p, const char __user *buf, int count)
}
}
- /*
- * green LED == read/verify
- */
- leds_event(led_amber_off);
- leds_event(led_green_on);
-
msleep(10);
pWritePtr = (unsigned char *) ((unsigned int) (FLASH_BASE + p));
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index 3f57d5de3957..21721d25e388 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -891,6 +891,14 @@ static void rx_ready_async(MGSLPC_INFO *info, int tcd, struct tty_struct *tty)
int work = 0;
struct mgsl_icount *icount = &info->icount;
+ if (!tty) {
+ /* tty is not available anymore */
+ issue_command(info, CHA, CMD_RXRESET);
+ if (debug_level >= DEBUG_LEVEL_ISR)
+ printk("%s(%d):rx_ready_async(tty=NULL)\n",__FILE__,__LINE__);
+ return;
+ }
+
if (tcd) {
/* early termination, get FIFO count from RBCL register */
fifo_count = (unsigned char)(read_reg(info, CHA+RBCL) & 0x1f);
@@ -980,7 +988,7 @@ static void tx_done(MGSLPC_INFO *info, struct tty_struct *tty)
else
#endif
{
- if (tty->stopped || tty->hw_stopped) {
+ if (tty && (tty->stopped || tty->hw_stopped)) {
tx_stop(info);
return;
}
@@ -1000,7 +1008,7 @@ static void tx_ready(MGSLPC_INFO *info, struct tty_struct *tty)
if (!info->tx_active)
return;
} else {
- if (tty->stopped || tty->hw_stopped) {
+ if (tty && (tty->stopped || tty->hw_stopped)) {
tx_stop(info);
return;
}
@@ -1050,13 +1058,12 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty)
wake_up_interruptible(&info->status_event_wait_q);
wake_up_interruptible(&info->event_wait_q);
- if (tty_port_cts_enabled(&info->port)) {
+ if (tty && tty_port_cts_enabled(&info->port)) {
if (tty->hw_stopped) {
if (info->serial_signals & SerialSignal_CTS) {
if (debug_level >= DEBUG_LEVEL_ISR)
printk("CTS tx start...");
- if (tty)
- tty->hw_stopped = 0;
+ tty->hw_stopped = 0;
tx_start(info, tty);
info->pending_bh |= BH_TRANSMIT;
return;
@@ -1065,8 +1072,7 @@ static void cts_change(MGSLPC_INFO *info, struct tty_struct *tty)
if (!(info->serial_signals & SerialSignal_CTS)) {
if (debug_level >= DEBUG_LEVEL_ISR)
printk("CTS tx stop...");
- if (tty)
- tty->hw_stopped = 1;
+ tty->hw_stopped = 1;
tx_stop(info);
}
}
diff --git a/drivers/char/ppdev.c b/drivers/char/ppdev.c
index 3fcf80ff12f2..d0d824ebf2c1 100644
--- a/drivers/char/ppdev.c
+++ b/drivers/char/ppdev.c
@@ -783,7 +783,8 @@ static int __init ppdev_init (void)
err = PTR_ERR(ppdev_class);
goto out_chrdev;
}
- if (parport_register_driver(&pp_driver)) {
+ err = parport_register_driver(&pp_driver);
+ if (err < 0) {
printk (KERN_WARNING CHRDEV ": unable to register with parport\n");
goto out_class;
}
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index af9437488b6c..91470fdbab2a 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -411,7 +411,7 @@ static int rtc_do_ioctl(unsigned int cmd, unsigned long arg, int kernel)
case RTC_IRQP_READ:
case RTC_IRQP_SET:
return -EINVAL;
- };
+ }
}
#endif
diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c
index ce29e7cce528..e95e0ab0bd87 100644
--- a/drivers/char/tlclk.c
+++ b/drivers/char/tlclk.c
@@ -784,8 +784,10 @@ static int __init tlclk_init(void)
}
tlclk_major = ret;
alarm_events = kzalloc( sizeof(struct tlclk_alarms), GFP_KERNEL);
- if (!alarm_events)
+ if (!alarm_events) {
+ ret = -ENOMEM;
goto out1;
+ }
/* Read telecom clock IRQ number (Set by BIOS) */
if (!request_region(TLCLK_BASE, 8, "telco_clock")) {
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index cdf2f5451c76..060a672ebb7b 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -1941,7 +1941,17 @@ static int __init init(void)
INIT_LIST_HEAD(&pdrvdata.consoles);
INIT_LIST_HEAD(&pdrvdata.portdevs);
- return register_virtio_driver(&virtio_console);
+ err = register_virtio_driver(&virtio_console);
+ if (err < 0) {
+ pr_err("Error %d registering virtio driver\n", err);
+ goto free;
+ }
+ return 0;
+free:
+ if (pdrvdata.debugfs_dir)
+ debugfs_remove_recursive(pdrvdata.debugfs_dir);
+ class_destroy(pdrvdata.class);
+ return err;
}
static void __exit fini(void)
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 9184b5e19edf..71a25b91de00 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -1,4 +1,5 @@
# common clock types
+obj-$(CONFIG_HAVE_CLK) += clk-devres.o
obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed-rate.o clk-gate.o \
clk-mux.o clk-divider.o clk-fixed-factor.o
@@ -17,6 +18,7 @@ obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_ARCH_U8500) += ux500/
+obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
# Chip specific
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c
new file mode 100644
index 000000000000..8f571548870f
--- /dev/null
+++ b/drivers/clk/clk-devres.c
@@ -0,0 +1,55 @@
+/*
+ * This 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/device.h>
+#include <linux/export.h>
+#include <linux/gfp.h>
+
+static void devm_clk_release(struct device *dev, void *res)
+{
+ clk_put(*(struct clk **)res);
+}
+
+struct clk *devm_clk_get(struct device *dev, const char *id)
+{
+ struct clk **ptr, *clk;
+
+ ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ clk = clk_get(dev, id);
+ if (!IS_ERR(clk)) {
+ *ptr = clk;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return clk;
+}
+EXPORT_SYMBOL(devm_clk_get);
+
+static int devm_clk_match(struct device *dev, void *res, void *data)
+{
+ struct clk **c = res;
+ if (!c || !*c) {
+ WARN_ON(!c || !*c);
+ return 0;
+ }
+ return *c == data;
+}
+
+void devm_clk_put(struct device *dev, struct clk *clk)
+{
+ int ret;
+
+ ret = devres_release(dev, devm_clk_release, devm_clk_match, clk);
+
+ WARN_ON(ret);
+}
+EXPORT_SYMBOL(devm_clk_put);
diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c
new file mode 100644
index 000000000000..a885600f5270
--- /dev/null
+++ b/drivers/clk/clk-vt8500.c
@@ -0,0 +1,510 @@
+/*
+ * Clock implementation for VIA/Wondermedia SoC's
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * 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/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+
+/* All clocks share the same lock as none can be changed concurrently */
+static DEFINE_SPINLOCK(_lock);
+
+struct clk_device {
+ struct clk_hw hw;
+ void __iomem *div_reg;
+ unsigned int div_mask;
+ void __iomem *en_reg;
+ int en_bit;
+ spinlock_t *lock;
+};
+
+/*
+ * Add new PLL_TYPE_x definitions here as required. Use the first known model
+ * to support the new type as the name.
+ * Add case statements to vtwm_pll_recalc_rate(), vtwm_pll_round_round() and
+ * vtwm_pll_set_rate() to handle the new PLL_TYPE_x
+ */
+
+#define PLL_TYPE_VT8500 0
+#define PLL_TYPE_WM8650 1
+
+struct clk_pll {
+ struct clk_hw hw;
+ void __iomem *reg;
+ spinlock_t *lock;
+ int type;
+};
+
+static void __iomem *pmc_base;
+
+#define to_clk_device(_hw) container_of(_hw, struct clk_device, hw)
+
+#define VT8500_PMC_BUSY_MASK 0x18
+
+static void vt8500_pmc_wait_busy(void)
+{
+ while (readl(pmc_base) & VT8500_PMC_BUSY_MASK)
+ cpu_relax();
+}
+
+static int vt8500_dclk_enable(struct clk_hw *hw)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 en_val;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(cdev->lock, flags);
+
+ en_val = readl(cdev->en_reg);
+ en_val |= BIT(cdev->en_bit);
+ writel(en_val, cdev->en_reg);
+
+ spin_unlock_irqrestore(cdev->lock, flags);
+ return 0;
+}
+
+static void vt8500_dclk_disable(struct clk_hw *hw)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 en_val;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(cdev->lock, flags);
+
+ en_val = readl(cdev->en_reg);
+ en_val &= ~BIT(cdev->en_bit);
+ writel(en_val, cdev->en_reg);
+
+ spin_unlock_irqrestore(cdev->lock, flags);
+}
+
+static int vt8500_dclk_is_enabled(struct clk_hw *hw)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 en_val = (readl(cdev->en_reg) & BIT(cdev->en_bit));
+
+ return en_val ? 1 : 0;
+}
+
+static unsigned long vt8500_dclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 div = readl(cdev->div_reg) & cdev->div_mask;
+
+ /* Special case for SDMMC devices */
+ if ((cdev->div_mask == 0x3F) && (div & BIT(5)))
+ div = 64 * (div & 0x1f);
+
+ /* div == 0 is actually the highest divisor */
+ if (div == 0)
+ div = (cdev->div_mask + 1);
+
+ return parent_rate / div;
+}
+
+static long vt8500_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ u32 divisor = *prate / rate;
+
+ return *prate / divisor;
+}
+
+static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 divisor = parent_rate / rate;
+ unsigned long flags = 0;
+
+ if (divisor == cdev->div_mask + 1)
+ divisor = 0;
+
+ if (divisor > cdev->div_mask) {
+ pr_err("%s: invalid divisor for clock\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(cdev->lock, flags);
+
+ vt8500_pmc_wait_busy();
+ writel(divisor, cdev->div_reg);
+ vt8500_pmc_wait_busy();
+
+ spin_lock_irqsave(cdev->lock, flags);
+
+ return 0;
+}
+
+
+static const struct clk_ops vt8500_gated_clk_ops = {
+ .enable = vt8500_dclk_enable,
+ .disable = vt8500_dclk_disable,
+ .is_enabled = vt8500_dclk_is_enabled,
+};
+
+static const struct clk_ops vt8500_divisor_clk_ops = {
+ .round_rate = vt8500_dclk_round_rate,
+ .set_rate = vt8500_dclk_set_rate,
+ .recalc_rate = vt8500_dclk_recalc_rate,
+};
+
+static const struct clk_ops vt8500_gated_divisor_clk_ops = {
+ .enable = vt8500_dclk_enable,
+ .disable = vt8500_dclk_disable,
+ .is_enabled = vt8500_dclk_is_enabled,
+ .round_rate = vt8500_dclk_round_rate,
+ .set_rate = vt8500_dclk_set_rate,
+ .recalc_rate = vt8500_dclk_recalc_rate,
+};
+
+#define CLK_INIT_GATED BIT(0)
+#define CLK_INIT_DIVISOR BIT(1)
+#define CLK_INIT_GATED_DIVISOR (CLK_INIT_DIVISOR | CLK_INIT_GATED)
+
+static __init void vtwm_device_clk_init(struct device_node *node)
+{
+ u32 en_reg, div_reg;
+ struct clk *clk;
+ struct clk_device *dev_clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init;
+ int rc;
+ int clk_init_flags = 0;
+
+ dev_clk = kzalloc(sizeof(*dev_clk), GFP_KERNEL);
+ if (WARN_ON(!dev_clk))
+ return;
+
+ dev_clk->lock = &_lock;
+
+ rc = of_property_read_u32(node, "enable-reg", &en_reg);
+ if (!rc) {
+ dev_clk->en_reg = pmc_base + en_reg;
+ rc = of_property_read_u32(node, "enable-bit", &dev_clk->en_bit);
+ if (rc) {
+ pr_err("%s: enable-bit property required for gated clock\n",
+ __func__);
+ return;
+ }
+ clk_init_flags |= CLK_INIT_GATED;
+ }
+
+ rc = of_property_read_u32(node, "divisor-reg", &div_reg);
+ if (!rc) {
+ dev_clk->div_reg = pmc_base + div_reg;
+ /*
+ * use 0x1f as the default mask since it covers
+ * almost all the clocks and reduces dts properties
+ */
+ dev_clk->div_mask = 0x1f;
+
+ of_property_read_u32(node, "divisor-mask", &dev_clk->div_mask);
+ clk_init_flags |= CLK_INIT_DIVISOR;
+ }
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ switch (clk_init_flags) {
+ case CLK_INIT_GATED:
+ init.ops = &vt8500_gated_clk_ops;
+ break;
+ case CLK_INIT_DIVISOR:
+ init.ops = &vt8500_divisor_clk_ops;
+ break;
+ case CLK_INIT_GATED_DIVISOR:
+ init.ops = &vt8500_gated_divisor_clk_ops;
+ break;
+ default:
+ pr_err("%s: Invalid clock description in device tree\n",
+ __func__);
+ kfree(dev_clk);
+ return;
+ }
+
+ init.name = clk_name;
+ init.flags = 0;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ dev_clk->hw.init = &init;
+
+ clk = clk_register(NULL, &dev_clk->hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(dev_clk);
+ return;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+}
+
+
+/* PLL clock related functions */
+
+#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw)
+
+/* Helper macros for PLL_VT8500 */
+#define VT8500_PLL_MUL(x) ((x & 0x1F) << 1)
+#define VT8500_PLL_DIV(x) ((x & 0x100) ? 1 : 2)
+
+#define VT8500_BITS_TO_FREQ(r, m, d) \
+ ((r / d) * m)
+
+#define VT8500_BITS_TO_VAL(m, d) \
+ ((d == 2 ? 0 : 0x100) | ((m >> 1) & 0x1F))
+
+/* Helper macros for PLL_WM8650 */
+#define WM8650_PLL_MUL(x) (x & 0x3FF)
+#define WM8650_PLL_DIV(x) (((x >> 10) & 7) * (1 << ((x >> 13) & 3)))
+
+#define WM8650_BITS_TO_FREQ(r, m, d1, d2) \
+ (r * m / (d1 * (1 << d2)))
+
+#define WM8650_BITS_TO_VAL(m, d1, d2) \
+ ((d2 << 13) | (d1 << 10) | (m & 0x3FF))
+
+
+static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate,
+ u32 *multiplier, u32 *prediv)
+{
+ unsigned long tclk;
+
+ /* sanity check */
+ if ((rate < parent_rate * 4) || (rate > parent_rate * 62)) {
+ pr_err("%s: requested rate out of range\n", __func__);
+ *multiplier = 0;
+ *prediv = 1;
+ return;
+ }
+ if (rate <= parent_rate * 31)
+ /* use the prediv to double the resolution */
+ *prediv = 2;
+ else
+ *prediv = 1;
+
+ *multiplier = rate / (parent_rate / *prediv);
+ tclk = (parent_rate / *prediv) * *multiplier;
+
+ if (tclk != rate)
+ pr_warn("%s: requested rate %lu, found rate %lu\n", __func__,
+ rate, tclk);
+}
+
+static void wm8650_find_pll_bits(unsigned long rate, unsigned long parent_rate,
+ u32 *multiplier, u32 *divisor1, u32 *divisor2)
+{
+ u32 mul, div1, div2;
+ u32 best_mul, best_div1, best_div2;
+ unsigned long tclk, rate_err, best_err;
+
+ best_err = (unsigned long)-1;
+
+ /* Find the closest match (lower or equal to requested) */
+ for (div1 = 5; div1 >= 3; div1--)
+ for (div2 = 3; div2 >= 0; div2--)
+ for (mul = 3; mul <= 1023; mul++) {
+ tclk = parent_rate * mul / (div1 * (1 << div2));
+ if (tclk > rate)
+ continue;
+ /* error will always be +ve */
+ rate_err = rate - tclk;
+ if (rate_err == 0) {
+ *multiplier = mul;
+ *divisor1 = div1;
+ *divisor2 = div2;
+ return;
+ }
+
+ if (rate_err < best_err) {
+ best_err = rate_err;
+ best_mul = mul;
+ best_div1 = div1;
+ best_div2 = div2;
+ }
+ }
+
+ /* if we got here, it wasn't an exact match */
+ pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate,
+ rate - best_err);
+ *multiplier = mul;
+ *divisor1 = div1;
+ *divisor2 = div2;
+}
+
+static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 mul, div1, div2;
+ u32 pll_val;
+ unsigned long flags = 0;
+
+ /* sanity check */
+
+ switch (pll->type) {
+ case PLL_TYPE_VT8500:
+ vt8500_find_pll_bits(rate, parent_rate, &mul, &div1);
+ pll_val = VT8500_BITS_TO_VAL(mul, div1);
+ break;
+ case PLL_TYPE_WM8650:
+ wm8650_find_pll_bits(rate, parent_rate, &mul, &div1, &div2);
+ pll_val = WM8650_BITS_TO_VAL(mul, div1, div2);
+ break;
+ default:
+ pr_err("%s: invalid pll type\n", __func__);
+ return 0;
+ }
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ vt8500_pmc_wait_busy();
+ writel(pll_val, pll->reg);
+ vt8500_pmc_wait_busy();
+
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 mul, div1, div2;
+ long round_rate;
+
+ switch (pll->type) {
+ case PLL_TYPE_VT8500:
+ vt8500_find_pll_bits(rate, *prate, &mul, &div1);
+ round_rate = VT8500_BITS_TO_FREQ(*prate, mul, div1);
+ break;
+ case PLL_TYPE_WM8650:
+ wm8650_find_pll_bits(rate, *prate, &mul, &div1, &div2);
+ round_rate = WM8650_BITS_TO_FREQ(*prate, mul, div1, div2);
+ break;
+ default:
+ round_rate = 0;
+ }
+
+ return round_rate;
+}
+
+static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 pll_val = readl(pll->reg);
+ unsigned long pll_freq;
+
+ switch (pll->type) {
+ case PLL_TYPE_VT8500:
+ pll_freq = parent_rate * VT8500_PLL_MUL(pll_val);
+ pll_freq /= VT8500_PLL_DIV(pll_val);
+ break;
+ case PLL_TYPE_WM8650:
+ pll_freq = parent_rate * WM8650_PLL_MUL(pll_val);
+ pll_freq /= WM8650_PLL_DIV(pll_val);
+ break;
+ default:
+ pll_freq = 0;
+ }
+
+ return pll_freq;
+}
+
+const struct clk_ops vtwm_pll_ops = {
+ .round_rate = vtwm_pll_round_rate,
+ .set_rate = vtwm_pll_set_rate,
+ .recalc_rate = vtwm_pll_recalc_rate,
+};
+
+static __init void vtwm_pll_clk_init(struct device_node *node, int pll_type)
+{
+ u32 reg;
+ struct clk *clk;
+ struct clk_pll *pll_clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init;
+ int rc;
+
+ rc = of_property_read_u32(node, "reg", &reg);
+ if (WARN_ON(rc))
+ return;
+
+ pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
+ if (WARN_ON(!pll_clk))
+ return;
+
+ pll_clk->reg = pmc_base + reg;
+ pll_clk->lock = &_lock;
+ pll_clk->type = pll_type;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = &vtwm_pll_ops;
+ init.flags = 0;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ pll_clk->hw.init = &init;
+
+ clk = clk_register(NULL, &pll_clk->hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(pll_clk);
+ return;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+}
+
+
+/* Wrappers for initialization functions */
+
+static void __init vt8500_pll_init(struct device_node *node)
+{
+ vtwm_pll_clk_init(node, PLL_TYPE_VT8500);
+}
+
+static void __init wm8650_pll_init(struct device_node *node)
+{
+ vtwm_pll_clk_init(node, PLL_TYPE_WM8650);
+}
+
+static const __initconst struct of_device_id clk_match[] = {
+ { .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
+ { .compatible = "via,vt8500-pll-clock", .data = vt8500_pll_init, },
+ { .compatible = "wm,wm8650-pll-clock", .data = wm8650_pll_init, },
+ { .compatible = "via,vt8500-device-clock",
+ .data = vtwm_device_clk_init, },
+ { /* sentinel */ }
+};
+
+void __init vtwm_clk_init(void __iomem *base)
+{
+ if (!base)
+ return;
+
+ pmc_base = base;
+
+ of_clk_init(clk_match);
+}
diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c
index d423c9bdd71a..442a31363873 100644
--- a/drivers/clk/clkdev.c
+++ b/drivers/clk/clkdev.c
@@ -171,51 +171,6 @@ void clk_put(struct clk *clk)
}
EXPORT_SYMBOL(clk_put);
-static void devm_clk_release(struct device *dev, void *res)
-{
- clk_put(*(struct clk **)res);
-}
-
-struct clk *devm_clk_get(struct device *dev, const char *id)
-{
- struct clk **ptr, *clk;
-
- ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL);
- if (!ptr)
- return ERR_PTR(-ENOMEM);
-
- clk = clk_get(dev, id);
- if (!IS_ERR(clk)) {
- *ptr = clk;
- devres_add(dev, ptr);
- } else {
- devres_free(ptr);
- }
-
- return clk;
-}
-EXPORT_SYMBOL(devm_clk_get);
-
-static int devm_clk_match(struct device *dev, void *res, void *data)
-{
- struct clk **c = res;
- if (!c || !*c) {
- WARN_ON(!c || !*c);
- return 0;
- }
- return *c == data;
-}
-
-void devm_clk_put(struct device *dev, struct clk *clk)
-{
- int ret;
-
- ret = devres_destroy(dev, devm_clk_release, devm_clk_match, clk);
-
- WARN_ON(ret);
-}
-EXPORT_SYMBOL(devm_clk_put);
-
void clkdev_add(struct clk_lookup *cl)
{
mutex_lock(&clocks_mutex);
diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c
index 9f6d15546cbe..f00dffb9ad60 100644
--- a/drivers/clk/mxs/clk-imx23.c
+++ b/drivers/clk/mxs/clk-imx23.c
@@ -165,7 +165,7 @@ int __init mx23_clocks_init(void)
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clks[clks_init_on[i]]);
- mxs_timer_init(MX23_INT_TIMER0);
+ mxs_timer_init();
return 0;
}
diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c
index 613e76f3758e..42978f1b4bd2 100644
--- a/drivers/clk/mxs/clk-imx28.c
+++ b/drivers/clk/mxs/clk-imx28.c
@@ -244,7 +244,7 @@ int __init mx28_clocks_init(void)
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clks[clks_init_on[i]]);
- mxs_timer_init(MX28_INT_TIMER0);
+ mxs_timer_init();
return 0;
}
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index d53cd0afc200..6a78073c3808 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -35,3 +35,8 @@ config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
default y
help
Use the always on PRCMU Timer as sched_clock
+
+config CLKSRC_ARM_GENERIC
+ def_bool y if ARM64
+ help
+ This option enables support for the ARM generic timer.
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index d496a55f6bb0..603be366f762 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -14,3 +14,5 @@ obj-$(CONFIG_DW_APB_TIMER_OF) += dw_apb_timer_of.o
obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o
obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o
obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o
+
+obj-$(CONFIG_CLKSRC_ARM_GENERIC) += arm_generic.o
diff --git a/drivers/clocksource/arm_generic.c b/drivers/clocksource/arm_generic.c
new file mode 100644
index 000000000000..c4d9f9566c64
--- /dev/null
+++ b/drivers/clocksource/arm_generic.c
@@ -0,0 +1,232 @@
+/*
+ * Generic timers support
+ *
+ * Copyright (C) 2012 ARM Ltd.
+ * Author: Marc Zyngier <marc.zyngier@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/smp.h>
+#include <linux/cpu.h>
+#include <linux/jiffies.h>
+#include <linux/interrupt.h>
+#include <linux/clockchips.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+
+#include <clocksource/arm_generic.h>
+
+#include <asm/arm_generic.h>
+
+static u32 arch_timer_rate;
+static u64 sched_clock_mult __read_mostly;
+static DEFINE_PER_CPU(struct clock_event_device, arch_timer_evt);
+static int arch_timer_ppi;
+
+static irqreturn_t arch_timer_handle_irq(int irq, void *dev_id)
+{
+ struct clock_event_device *evt = dev_id;
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ if (ctrl & ARCH_TIMER_CTRL_ISTATUS) {
+ ctrl |= ARCH_TIMER_CTRL_IMASK;
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+ evt->event_handler(evt);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static void arch_timer_stop(void)
+{
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+}
+
+static void arch_timer_set_mode(enum clock_event_mode mode,
+ struct clock_event_device *clk)
+{
+ switch (mode) {
+ case CLOCK_EVT_MODE_UNUSED:
+ case CLOCK_EVT_MODE_SHUTDOWN:
+ arch_timer_stop();
+ break;
+ default:
+ break;
+ }
+}
+
+static int arch_timer_set_next_event(unsigned long evt,
+ struct clock_event_device *unused)
+{
+ unsigned long ctrl;
+
+ ctrl = arch_timer_reg_read(ARCH_TIMER_REG_CTRL);
+ ctrl |= ARCH_TIMER_CTRL_ENABLE;
+ ctrl &= ~ARCH_TIMER_CTRL_IMASK;
+
+ arch_timer_reg_write(ARCH_TIMER_REG_TVAL, evt);
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, ctrl);
+
+ return 0;
+}
+
+static void __cpuinit arch_timer_setup(struct clock_event_device *clk)
+{
+ /* Let's make sure the timer is off before doing anything else */
+ arch_timer_stop();
+
+ clk->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_C3STOP;
+ clk->name = "arch_sys_timer";
+ clk->rating = 400;
+ clk->set_mode = arch_timer_set_mode;
+ clk->set_next_event = arch_timer_set_next_event;
+ clk->irq = arch_timer_ppi;
+ clk->cpumask = cpumask_of(smp_processor_id());
+
+ clockevents_config_and_register(clk, arch_timer_rate,
+ 0xf, 0x7fffffff);
+
+ enable_percpu_irq(clk->irq, 0);
+
+ /* Ensure the physical counter is visible to userspace for the vDSO. */
+ arch_counter_enable_user_access();
+}
+
+static void __init arch_timer_calibrate(void)
+{
+ if (arch_timer_rate == 0) {
+ arch_timer_reg_write(ARCH_TIMER_REG_CTRL, 0);
+ arch_timer_rate = arch_timer_reg_read(ARCH_TIMER_REG_FREQ);
+
+ /* Check the timer frequency. */
+ if (arch_timer_rate == 0)
+ panic("Architected timer frequency is set to zero.\n"
+ "You must set this in your .dts file\n");
+ }
+
+ /* Cache the sched_clock multiplier to save a divide in the hot path. */
+
+ sched_clock_mult = NSEC_PER_SEC / arch_timer_rate;
+
+ pr_info("Architected local timer running at %u.%02uMHz.\n",
+ arch_timer_rate / 1000000, (arch_timer_rate / 10000) % 100);
+}
+
+static cycle_t arch_counter_read(struct clocksource *cs)
+{
+ return arch_counter_get_cntpct();
+}
+
+static struct clocksource clocksource_counter = {
+ .name = "arch_sys_counter",
+ .rating = 400,
+ .read = arch_counter_read,
+ .mask = CLOCKSOURCE_MASK(56),
+ .flags = (CLOCK_SOURCE_IS_CONTINUOUS | CLOCK_SOURCE_VALID_FOR_HRES),
+};
+
+int read_current_timer(unsigned long *timer_value)
+{
+ *timer_value = arch_counter_get_cntpct();
+ return 0;
+}
+
+unsigned long long notrace sched_clock(void)
+{
+ return arch_counter_get_cntvct() * sched_clock_mult;
+}
+
+static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
+ unsigned long action, void *hcpu)
+{
+ int cpu = (long)hcpu;
+ struct clock_event_device *clk = per_cpu_ptr(&arch_timer_evt, cpu);
+
+ switch(action) {
+ case CPU_STARTING:
+ case CPU_STARTING_FROZEN:
+ arch_timer_setup(clk);
+ break;
+
+ case CPU_DYING:
+ case CPU_DYING_FROZEN:
+ pr_debug("arch_timer_teardown disable IRQ%d cpu #%d\n",
+ clk->irq, cpu);
+ disable_percpu_irq(clk->irq);
+ arch_timer_set_mode(CLOCK_EVT_MODE_UNUSED, clk);
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block __cpuinitdata arch_timer_cpu_nb = {
+ .notifier_call = arch_timer_cpu_notify,
+};
+
+static const struct of_device_id arch_timer_of_match[] __initconst = {
+ { .compatible = "arm,armv8-timer" },
+ {},
+};
+
+int __init arm_generic_timer_init(void)
+{
+ struct device_node *np;
+ int err;
+ u32 freq;
+
+ np = of_find_matching_node(NULL, arch_timer_of_match);
+ if (!np) {
+ pr_err("arch_timer: can't find DT node\n");
+ return -ENODEV;
+ }
+
+ /* Try to determine the frequency from the device tree or CNTFRQ */
+ if (!of_property_read_u32(np, "clock-frequency", &freq))
+ arch_timer_rate = freq;
+ arch_timer_calibrate();
+
+ arch_timer_ppi = irq_of_parse_and_map(np, 0);
+ pr_info("arch_timer: found %s irq %d\n", np->name, arch_timer_ppi);
+
+ err = request_percpu_irq(arch_timer_ppi, arch_timer_handle_irq,
+ np->name, &arch_timer_evt);
+ if (err) {
+ pr_err("arch_timer: can't register interrupt %d (%d)\n",
+ arch_timer_ppi, err);
+ return err;
+ }
+
+ clocksource_register_hz(&clocksource_counter, arch_timer_rate);
+
+ /* Calibrate the delay loop directly */
+ lpj_fine = arch_timer_rate / HZ;
+
+ /* Immediately configure the timer on the boot CPU */
+ arch_timer_setup(per_cpu_ptr(&arch_timer_evt, smp_processor_id()));
+
+ register_cpu_notifier(&arch_timer_cpu_nb);
+
+ return 0;
+}
diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c
index c0e816468e30..1a40935c85fd 100644
--- a/drivers/cpufreq/powernow-k8.c
+++ b/drivers/cpufreq/powernow-k8.c
@@ -35,7 +35,6 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/cpumask.h>
-#include <linux/sched.h> /* for current / set_cpus_allowed() */
#include <linux/io.h>
#include <linux/delay.h>
@@ -1139,16 +1138,23 @@ static int transition_frequency_pstate(struct powernow_k8_data *data,
return res;
}
-/* Driver entry point to switch to the target frequency */
-static int powernowk8_target(struct cpufreq_policy *pol,
- unsigned targfreq, unsigned relation)
+struct powernowk8_target_arg {
+ struct cpufreq_policy *pol;
+ unsigned targfreq;
+ unsigned relation;
+};
+
+static long powernowk8_target_fn(void *arg)
{
- cpumask_var_t oldmask;
+ struct powernowk8_target_arg *pta = arg;
+ struct cpufreq_policy *pol = pta->pol;
+ unsigned targfreq = pta->targfreq;
+ unsigned relation = pta->relation;
struct powernow_k8_data *data = per_cpu(powernow_data, pol->cpu);
u32 checkfid;
u32 checkvid;
unsigned int newstate;
- int ret = -EIO;
+ int ret;
if (!data)
return -EINVAL;
@@ -1156,29 +1162,16 @@ static int powernowk8_target(struct cpufreq_policy *pol,
checkfid = data->currfid;
checkvid = data->currvid;
- /* only run on specific CPU from here on. */
- /* This is poor form: use a workqueue or smp_call_function_single */
- if (!alloc_cpumask_var(&oldmask, GFP_KERNEL))
- return -ENOMEM;
-
- cpumask_copy(oldmask, tsk_cpus_allowed(current));
- set_cpus_allowed_ptr(current, cpumask_of(pol->cpu));
-
- if (smp_processor_id() != pol->cpu) {
- printk(KERN_ERR PFX "limiting to cpu %u failed\n", pol->cpu);
- goto err_out;
- }
-
if (pending_bit_stuck()) {
printk(KERN_ERR PFX "failing targ, change pending bit set\n");
- goto err_out;
+ return -EIO;
}
pr_debug("targ: cpu %d, %d kHz, min %d, max %d, relation %d\n",
pol->cpu, targfreq, pol->min, pol->max, relation);
if (query_current_values_with_pending_wait(data))
- goto err_out;
+ return -EIO;
if (cpu_family != CPU_HW_PSTATE) {
pr_debug("targ: curr fid 0x%x, vid 0x%x\n",
@@ -1196,7 +1189,7 @@ static int powernowk8_target(struct cpufreq_policy *pol,
if (cpufreq_frequency_table_target(pol, data->powernow_table,
targfreq, relation, &newstate))
- goto err_out;
+ return -EIO;
mutex_lock(&fidvid_mutex);
@@ -1209,9 +1202,8 @@ static int powernowk8_target(struct cpufreq_policy *pol,
ret = transition_frequency_fidvid(data, newstate);
if (ret) {
printk(KERN_ERR PFX "transition frequency failed\n");
- ret = 1;
mutex_unlock(&fidvid_mutex);
- goto err_out;
+ return 1;
}
mutex_unlock(&fidvid_mutex);
@@ -1220,12 +1212,25 @@ static int powernowk8_target(struct cpufreq_policy *pol,
data->powernow_table[newstate].index);
else
pol->cur = find_khz_freq_from_fid(data->currfid);
- ret = 0;
-err_out:
- set_cpus_allowed_ptr(current, oldmask);
- free_cpumask_var(oldmask);
- return ret;
+ return 0;
+}
+
+/* Driver entry point to switch to the target frequency */
+static int powernowk8_target(struct cpufreq_policy *pol,
+ unsigned targfreq, unsigned relation)
+{
+ struct powernowk8_target_arg pta = { .pol = pol, .targfreq = targfreq,
+ .relation = relation };
+
+ /*
+ * Must run on @pol->cpu. cpufreq core is responsible for ensuring
+ * that we're bound to the current CPU and pol->cpu stays online.
+ */
+ if (smp_processor_id() == pol->cpu)
+ return powernowk8_target_fn(&pta);
+ else
+ return work_on_cpu(pol->cpu, powernowk8_target_fn, &pta);
}
/* Driver entry point to verify the policy and range of frequencies */
diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c
index 3934fcc4e00b..17d6958342e7 100644
--- a/drivers/dma/at_hdmac.c
+++ b/drivers/dma/at_hdmac.c
@@ -168,9 +168,9 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc)
}
/**
- * atc_desc_chain - build chain adding a descripor
- * @first: address of first descripor of the chain
- * @prev: address of previous descripor of the chain
+ * atc_desc_chain - build chain adding a descriptor
+ * @first: address of first descriptor of the chain
+ * @prev: address of previous descriptor of the chain
* @desc: descriptor to queue
*
* Called from prep_* functions
@@ -661,7 +661,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
flags);
if (unlikely(!atslave || !sg_len)) {
- dev_dbg(chan2dev(chan), "prep_dma_memcpy: length is zero!\n");
+ dev_dbg(chan2dev(chan), "prep_slave_sg: sg length is zero!\n");
return NULL;
}
@@ -689,6 +689,11 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
mem = sg_dma_address(sg);
len = sg_dma_len(sg);
+ if (unlikely(!len)) {
+ dev_dbg(chan2dev(chan),
+ "prep_slave_sg: sg(%d) data length is zero\n", i);
+ goto err;
+ }
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
mem_width = 0;
@@ -724,6 +729,11 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
mem = sg_dma_address(sg);
len = sg_dma_len(sg);
+ if (unlikely(!len)) {
+ dev_dbg(chan2dev(chan),
+ "prep_slave_sg: sg(%d) data length is zero\n", i);
+ goto err;
+ }
mem_width = 2;
if (unlikely(mem & 3 || len & 3))
mem_width = 0;
@@ -757,6 +767,7 @@ atc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
err_desc_get:
dev_err(chan2dev(chan), "not enough descriptors available\n");
+err:
atc_desc_put(atchan, first);
return NULL;
}
@@ -785,7 +796,7 @@ err_out:
}
/**
- * atc_dma_cyclic_fill_desc - Fill one period decriptor
+ * atc_dma_cyclic_fill_desc - Fill one period descriptor
*/
static int
atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc,
diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c
index 4aeaea77f72e..64256f644252 100644
--- a/drivers/dma/ep93xx_dma.c
+++ b/drivers/dma/ep93xx_dma.c
@@ -1118,7 +1118,7 @@ fail:
* @chan: channel
* @dma_addr: DMA mapped address of the buffer
* @buf_len: length of the buffer (in bytes)
- * @period_len: lenght of a single period
+ * @period_len: length of a single period
* @dir: direction of the operation
* @context: operation context (ignored)
*
diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 8f84761f98ba..094437b9d823 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -1015,7 +1015,7 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data)
/*
* Programming Error
* The DMA_INTERRUPT async_tx is a NULL transfer, which will
- * triger a PE interrupt.
+ * trigger a PE interrupt.
*/
if (stat & FSL_DMA_SR_PE) {
chan_dbg(chan, "irq: Programming Error INT\n");
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index b90aaec4ccc4..2a3fab289db0 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -572,8 +572,8 @@ static void imxdma_tasklet(unsigned long data)
if (desc->desc.callback)
desc->desc.callback(desc->desc.callback_param);
- /* If we are dealing with a cyclic descriptor keep it on ld_active
- * and dont mark the descripor as complete.
+ /* If we are dealing with a cyclic descriptor, keep it on ld_active
+ * and dont mark the descriptor as complete.
* Only in non-cyclic cases it would be marked as complete
*/
if (imxdma_chan_is_doing_cyclic(imxdmac))
diff --git a/drivers/dma/intel_mid_dma.c b/drivers/dma/intel_mid_dma.c
index 222e907bfaaa..02b21d7d38e5 100644
--- a/drivers/dma/intel_mid_dma.c
+++ b/drivers/dma/intel_mid_dma.c
@@ -427,7 +427,7 @@ DMA engine callback Functions*/
* intel_mid_dma_tx_submit - callback to submit DMA transaction
* @tx: dma engine descriptor
*
- * Submit the DMA trasaction for this descriptor, start if ch idle
+ * Submit the DMA transaction for this descriptor, start if ch idle
*/
static dma_cookie_t intel_mid_dma_tx_submit(struct dma_async_tx_descriptor *tx)
{
diff --git a/drivers/dma/intel_mid_dma_regs.h b/drivers/dma/intel_mid_dma_regs.h
index 1bfa9268feaf..17b42192ea58 100644
--- a/drivers/dma/intel_mid_dma_regs.h
+++ b/drivers/dma/intel_mid_dma_regs.h
@@ -168,9 +168,9 @@ union intel_mid_dma_cfg_hi {
* @active_list: current active descriptors
* @queue: current queued up descriptors
* @free_list: current free descriptors
- * @slave: dma slave struture
- * @descs_allocated: total number of decsiptors allocated
- * @dma: dma device struture pointer
+ * @slave: dma slave structure
+ * @descs_allocated: total number of descriptors allocated
+ * @dma: dma device structure pointer
* @busy: bool representing if ch is busy (active txn) or not
* @in_use: bool representing if ch is in use or not
* @raw_tfr: raw trf interrupt received
diff --git a/drivers/dma/ioat/hw.h b/drivers/dma/ioat/hw.h
index 60e675455b6a..d2ff3fda0b18 100644
--- a/drivers/dma/ioat/hw.h
+++ b/drivers/dma/ioat/hw.h
@@ -22,7 +22,6 @@
#define _IOAT_HW_H_
/* PCI Configuration Space Values */
-#define IOAT_PCI_VID 0x8086
#define IOAT_MMIO_BAR 0
/* CB device ID's */
@@ -31,9 +30,6 @@
#define IOAT_PCI_DID_SCNB 0x65FF
#define IOAT_PCI_DID_SNB 0x402F
-#define IOAT_PCI_RID 0x00
-#define IOAT_PCI_SVID 0x8086
-#define IOAT_PCI_SID 0x8086
#define IOAT_VER_1_2 0x12 /* Version 1.2 */
#define IOAT_VER_2_0 0x20 /* Version 2.0 */
#define IOAT_VER_3_0 0x30 /* Version 3.0 */
diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index e4feba6b03c0..5d3bbcd279b4 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -522,7 +522,7 @@ enum desc_status {
/* In the DMAC pool */
FREE,
/*
- * Allocted to some channel during prep_xxx
+ * Allocated to some channel during prep_xxx
* Also may be sitting on the work_list.
*/
PREP,
@@ -1567,17 +1567,19 @@ static int pl330_submit_req(void *ch_id, struct pl330_req *r)
goto xfer_exit;
}
- /* Prefer Secure Channel */
- if (!_manager_ns(thrd))
- r->cfg->nonsecure = 0;
- else
- r->cfg->nonsecure = 1;
/* Use last settings, if not provided */
- if (r->cfg)
+ if (r->cfg) {
+ /* Prefer Secure Channel */
+ if (!_manager_ns(thrd))
+ r->cfg->nonsecure = 0;
+ else
+ r->cfg->nonsecure = 1;
+
ccr = _prepare_ccr(r->cfg);
- else
+ } else {
ccr = readl(regs + CC(thrd->id));
+ }
/* If this req doesn't have valid xfer settings */
if (!_is_valid(ccr)) {
@@ -2928,6 +2930,11 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
num_chan = max_t(int, pi->pcfg.num_peri, pi->pcfg.num_chan);
pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);
+ if (!pdmac->peripherals) {
+ ret = -ENOMEM;
+ dev_err(&adev->dev, "unable to allocate pdmac->peripherals\n");
+ goto probe_err5;
+ }
for (i = 0; i < num_chan; i++) {
pch = &pdmac->peripherals[i];
diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c
index ced98826684a..f72348d0bc41 100644
--- a/drivers/dma/ppc4xx/adma.c
+++ b/drivers/dma/ppc4xx/adma.c
@@ -4446,7 +4446,7 @@ static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev)
ret = -ENOMEM;
goto err_dma_alloc;
}
- dev_dbg(&ofdev->dev, "allocted descriptor pool virt 0x%p phys 0x%llx\n",
+ dev_dbg(&ofdev->dev, "allocated descriptor pool virt 0x%p phys 0x%llx\n",
adev->dma_desc_pool_virt, (u64)adev->dma_desc_pool);
regs = ioremap(res.start, resource_size(&res));
diff --git a/drivers/dma/ste_dma40_ll.h b/drivers/dma/ste_dma40_ll.h
index 51e8e5396e9b..6d47373f3f58 100644
--- a/drivers/dma/ste_dma40_ll.h
+++ b/drivers/dma/ste_dma40_ll.h
@@ -202,7 +202,7 @@
/* LLI related structures */
/**
- * struct d40_phy_lli - The basic configration register for each physical
+ * struct d40_phy_lli - The basic configuration register for each physical
* channel.
*
* @reg_cfg: The configuration register.
diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c
index 24acd711e032..4708467e4d83 100644
--- a/drivers/dma/tegra20-apb-dma.c
+++ b/drivers/dma/tegra20-apb-dma.c
@@ -201,7 +201,7 @@ struct tegra_dma {
struct clk *dma_clk;
spinlock_t global_lock;
void __iomem *base_addr;
- struct tegra_dma_chip_data *chip_data;
+ const struct tegra_dma_chip_data *chip_data;
/* Some register need to be cache before suspend */
u32 reg_gen;
@@ -1173,14 +1173,14 @@ static void tegra_dma_free_chan_resources(struct dma_chan *dc)
}
/* Tegra20 specific DMA controller information */
-static struct tegra_dma_chip_data tegra20_dma_chip_data = {
+static const struct tegra_dma_chip_data tegra20_dma_chip_data = {
.nr_channels = 16,
.max_dma_count = 1024UL * 64,
};
#if defined(CONFIG_OF)
/* Tegra30 specific DMA controller information */
-static struct tegra_dma_chip_data tegra30_dma_chip_data = {
+static const struct tegra_dma_chip_data tegra30_dma_chip_data = {
.nr_channels = 32,
.max_dma_count = 1024UL * 64,
};
@@ -1204,7 +1204,7 @@ static int __devinit tegra_dma_probe(struct platform_device *pdev)
struct tegra_dma *tdma;
int ret;
int i;
- struct tegra_dma_chip_data *cdata = NULL;
+ const struct tegra_dma_chip_data *cdata = NULL;
if (pdev->dev.of_node) {
const struct of_device_id *match;
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c
index 616d90bcb3a4..d5dc9da7f99f 100644
--- a/drivers/edac/edac_mc.c
+++ b/drivers/edac/edac_mc.c
@@ -199,6 +199,36 @@ void *edac_align_ptr(void **p, unsigned size, int n_elems)
return (void *)(((unsigned long)ptr) + align - r);
}
+static void _edac_mc_free(struct mem_ctl_info *mci)
+{
+ int i, chn, row;
+ struct csrow_info *csr;
+ const unsigned int tot_dimms = mci->tot_dimms;
+ const unsigned int tot_channels = mci->num_cschannel;
+ const unsigned int tot_csrows = mci->nr_csrows;
+
+ if (mci->dimms) {
+ for (i = 0; i < tot_dimms; i++)
+ kfree(mci->dimms[i]);
+ kfree(mci->dimms);
+ }
+ if (mci->csrows) {
+ for (row = 0; row < tot_csrows; row++) {
+ csr = mci->csrows[row];
+ if (csr) {
+ if (csr->channels) {
+ for (chn = 0; chn < tot_channels; chn++)
+ kfree(csr->channels[chn]);
+ kfree(csr->channels);
+ }
+ kfree(csr);
+ }
+ }
+ kfree(mci->csrows);
+ }
+ kfree(mci);
+}
+
/**
* edac_mc_alloc: Allocate and partially fill a struct mem_ctl_info structure
* @mc_num: Memory controller number
@@ -413,24 +443,7 @@ struct mem_ctl_info *edac_mc_alloc(unsigned mc_num,
return mci;
error:
- if (mci->dimms) {
- for (i = 0; i < tot_dimms; i++)
- kfree(mci->dimms[i]);
- kfree(mci->dimms);
- }
- if (mci->csrows) {
- for (chn = 0; chn < tot_channels; chn++) {
- csr = mci->csrows[chn];
- if (csr) {
- for (chn = 0; chn < tot_channels; chn++)
- kfree(csr->channels[chn]);
- kfree(csr);
- }
- kfree(mci->csrows[i]);
- }
- kfree(mci->csrows);
- }
- kfree(mci);
+ _edac_mc_free(mci);
return NULL;
}
@@ -445,6 +458,14 @@ void edac_mc_free(struct mem_ctl_info *mci)
{
edac_dbg(1, "\n");
+ /* If we're not yet registered with sysfs free only what was allocated
+ * in edac_mc_alloc().
+ */
+ if (!device_is_registered(&mci->dev)) {
+ _edac_mc_free(mci);
+ return;
+ }
+
/* the mci instance is freed here, when the sysfs object is dropped */
edac_unregister_sysfs(mci);
}
diff --git a/drivers/edac/i3200_edac.c b/drivers/edac/i3200_edac.c
index 47180a08edad..b6653a6fc5d5 100644
--- a/drivers/edac/i3200_edac.c
+++ b/drivers/edac/i3200_edac.c
@@ -391,7 +391,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
for (j = 0; j < nr_channels; j++) {
struct dimm_info *dimm = csrow->channels[j]->dimm;
- dimm->nr_pages = nr_pages / nr_channels;
+ dimm->nr_pages = nr_pages;
dimm->grain = nr_pages << PAGE_SHIFT;
dimm->mtype = MEM_DDR2;
dimm->dtype = DEV_UNKNOWN;
diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c
index 39c63757c2a1..6a49dd00b81b 100644
--- a/drivers/edac/i5000_edac.c
+++ b/drivers/edac/i5000_edac.c
@@ -1012,6 +1012,10 @@ static void handle_channel(struct i5000_pvt *pvt, int slot, int channel,
/* add the number of COLUMN bits */
addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr);
+ /* Dual-rank memories have twice the size */
+ if (dinfo->dual_rank)
+ addrBits++;
+
addrBits += 6; /* add 64 bits per DIMM */
addrBits -= 20; /* divide by 2^^20 */
addrBits -= 3; /* 8 bits per bytes */
diff --git a/drivers/edac/sb_edac.c b/drivers/edac/sb_edac.c
index f3b1f9fafa4b..5715b7c2c517 100644
--- a/drivers/edac/sb_edac.c
+++ b/drivers/edac/sb_edac.c
@@ -513,7 +513,8 @@ static int get_dimm_config(struct mem_ctl_info *mci)
{
struct sbridge_pvt *pvt = mci->pvt_info;
struct dimm_info *dimm;
- int i, j, banks, ranks, rows, cols, size, npages;
+ unsigned i, j, banks, ranks, rows, cols, npages;
+ u64 size;
u32 reg;
enum edac_type mode;
enum mem_type mtype;
@@ -585,10 +586,10 @@ static int get_dimm_config(struct mem_ctl_info *mci)
cols = numcol(mtr);
/* DDR3 has 8 I/O banks */
- size = (rows * cols * banks * ranks) >> (20 - 3);
+ size = ((u64)rows * cols * banks * ranks) >> (20 - 3);
npages = MiB_TO_PAGES(size);
- edac_dbg(0, "mc#%d: channel %d, dimm %d, %d Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
+ edac_dbg(0, "mc#%d: channel %d, dimm %d, %Ld Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
pvt->sbridge_dev->mc, i, j,
size, npages,
banks, ranks, rows, cols);
diff --git a/drivers/extcon/Kconfig b/drivers/extcon/Kconfig
index e175c8ed4ec4..07122a9ef36e 100644
--- a/drivers/extcon/Kconfig
+++ b/drivers/extcon/Kconfig
@@ -21,6 +21,12 @@ config EXTCON_GPIO
Say Y here to enable GPIO based extcon support. Note that GPIO
extcon supports single state per extcon instance.
+config EXTCON_ADC_JACK
+ tristate "ADC Jack extcon support"
+ depends on IIO
+ help
+ Say Y here to enable extcon device driver based on ADC values.
+
config EXTCON_MAX77693
tristate "MAX77693 EXTCON Support"
depends on MFD_MAX77693
@@ -41,7 +47,7 @@ config EXTCON_MAX8997
config EXTCON_ARIZONA
tristate "Wolfson Arizona EXTCON support"
- depends on MFD_ARIZONA
+ depends on MFD_ARIZONA && INPUT
help
Say Y here to enable support for external accessory detection
with Wolfson Arizona devices. These are audio CODECs with
diff --git a/drivers/extcon/Makefile b/drivers/extcon/Makefile
index 88961b332348..f98a3c4d46e0 100644
--- a/drivers/extcon/Makefile
+++ b/drivers/extcon/Makefile
@@ -2,8 +2,9 @@
# Makefile for external connector class (extcon) devices
#
-obj-$(CONFIG_EXTCON) += extcon_class.o
-obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.o
+obj-$(CONFIG_EXTCON) += extcon-class.o
+obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
+obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
diff --git a/drivers/extcon/extcon-adc-jack.c b/drivers/extcon/extcon-adc-jack.c
new file mode 100644
index 000000000000..60ac3fbb4cde
--- /dev/null
+++ b/drivers/extcon/extcon-adc-jack.c
@@ -0,0 +1,198 @@
+/*
+ * drivers/extcon/extcon-adc-jack.c
+ *
+ * Analog Jack extcon driver with ADC-based detection capability.
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * Modified for calling to IIO to get adc by <anish.singh@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/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/iio/consumer.h>
+#include <linux/extcon/extcon-adc-jack.h>
+#include <linux/extcon.h>
+
+/**
+ * struct adc_jack_data - internal data for adc_jack device driver
+ * @edev - extcon device.
+ * @cable_names - list of supported cables.
+ * @num_cables - size of cable_names.
+ * @adc_conditions - list of adc value conditions.
+ * @num_conditions - size of adc_conditions.
+ * @irq - irq number of attach/detach event (0 if not exist).
+ * @handling_delay - interrupt handler will schedule extcon event
+ * handling at handling_delay jiffies.
+ * @handler - extcon event handler called by interrupt handler.
+ * @chan - iio channel being queried.
+ */
+struct adc_jack_data {
+ struct extcon_dev edev;
+
+ const char **cable_names;
+ int num_cables;
+ struct adc_jack_cond *adc_conditions;
+ int num_conditions;
+
+ int irq;
+ unsigned long handling_delay; /* in jiffies */
+ struct delayed_work handler;
+
+ struct iio_channel *chan;
+};
+
+static void adc_jack_handler(struct work_struct *work)
+{
+ struct adc_jack_data *data = container_of(to_delayed_work(work),
+ struct adc_jack_data,
+ handler);
+ u32 state = 0;
+ int ret, adc_val;
+ int i;
+
+ ret = iio_read_channel_raw(data->chan, &adc_val);
+ if (ret < 0) {
+ dev_err(data->edev.dev, "read channel() error: %d\n", ret);
+ return;
+ }
+
+ /* Get state from adc value with adc_conditions */
+ for (i = 0; i < data->num_conditions; i++) {
+ struct adc_jack_cond *def = &data->adc_conditions[i];
+ if (!def->state)
+ break;
+ if (def->min_adc <= adc_val && def->max_adc >= adc_val) {
+ state = def->state;
+ break;
+ }
+ }
+ /* if no def has met, it means state = 0 (no cables attached) */
+
+ extcon_set_state(&data->edev, state);
+}
+
+static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
+{
+ struct adc_jack_data *data = _data;
+
+ schedule_delayed_work(&data->handler, data->handling_delay);
+ return IRQ_HANDLED;
+}
+
+static int __devinit adc_jack_probe(struct platform_device *pdev)
+{
+ struct adc_jack_data *data;
+ struct adc_jack_pdata *pdata = pdev->dev.platform_data;
+ int i, err = 0;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->edev.name = pdata->name;
+
+ if (!pdata->cable_names) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "error: cable_names not defined.\n");
+ goto out;
+ }
+
+ data->edev.supported_cable = pdata->cable_names;
+
+ /* Check the length of array and set num_cables */
+ for (i = 0; data->edev.supported_cable[i]; i++)
+ ;
+ if (i == 0 || i > SUPPORTED_CABLE_MAX) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
+ i - 1);
+ goto out;
+ }
+ data->num_cables = i;
+
+ if (!pdata->adc_conditions ||
+ !pdata->adc_conditions[0].state) {
+ err = -EINVAL;
+ dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
+ goto out;
+ }
+ data->adc_conditions = pdata->adc_conditions;
+
+ /* Check the length of array and set num_conditions */
+ for (i = 0; data->adc_conditions[i].state; i++)
+ ;
+ data->num_conditions = i;
+
+ data->chan = iio_channel_get(dev_name(&pdev->dev),
+ pdata->consumer_channel);
+ if (IS_ERR(data->chan)) {
+ err = PTR_ERR(data->chan);
+ goto out;
+ }
+
+ data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);
+
+ INIT_DELAYED_WORK_DEFERRABLE(&data->handler, adc_jack_handler);
+
+ platform_set_drvdata(pdev, data);
+
+ err = extcon_dev_register(&data->edev, &pdev->dev);
+ if (err)
+ goto out;
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (!data->irq) {
+ dev_err(&pdev->dev, "platform_get_irq failed\n");
+ err = -ENODEV;
+ goto err_irq;
+ }
+
+ err = request_any_context_irq(data->irq, adc_jack_irq_thread,
+ pdata->irq_flags, pdata->name, data);
+
+ if (err) {
+ dev_err(&pdev->dev, "error: irq %d\n", data->irq);
+ err = -EINVAL;
+ goto err_irq;
+ }
+
+ goto out;
+
+err_irq:
+ extcon_dev_unregister(&data->edev);
+out:
+ return err;
+}
+
+static int __devexit adc_jack_remove(struct platform_device *pdev)
+{
+ struct adc_jack_data *data = platform_get_drvdata(pdev);
+
+ free_irq(data->irq, data);
+ cancel_work_sync(&data->handler.work);
+ extcon_dev_unregister(&data->edev);
+
+ return 0;
+}
+
+static struct platform_driver adc_jack_driver = {
+ .probe = adc_jack_probe,
+ .remove = __devexit_p(adc_jack_remove),
+ .driver = {
+ .name = "adc-jack",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(adc_jack_driver);
diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c
index 427a289f32a5..cdab9e598297 100644
--- a/drivers/extcon/extcon-arizona.c
+++ b/drivers/extcon/extcon-arizona.c
@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/gpio.h>
+#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
@@ -30,11 +31,14 @@
#include <linux/mfd/arizona/pdata.h>
#include <linux/mfd/arizona/registers.h>
+#define ARIZONA_NUM_BUTTONS 6
+
struct arizona_extcon_info {
struct device *dev;
struct arizona *arizona;
struct mutex lock;
struct regulator *micvdd;
+ struct input_dev *input;
int micd_mode;
const struct arizona_micd_config *micd_modes;
@@ -54,6 +58,18 @@ static const struct arizona_micd_config micd_default_modes[] = {
{ 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
};
+static struct {
+ u16 status;
+ int report;
+} arizona_lvl_to_key[ARIZONA_NUM_BUTTONS] = {
+ { 0x1, BTN_0 },
+ { 0x2, BTN_1 },
+ { 0x4, BTN_2 },
+ { 0x8, BTN_3 },
+ { 0x10, BTN_4 },
+ { 0x20, BTN_5 },
+};
+
#define ARIZONA_CABLE_MECHANICAL 0
#define ARIZONA_CABLE_MICROPHONE 1
#define ARIZONA_CABLE_HEADPHONE 2
@@ -133,6 +149,7 @@ static void arizona_stop_mic(struct arizona_extcon_info *info)
if (change) {
regulator_disable(info->micvdd);
+ pm_runtime_mark_last_busy(info->dev);
pm_runtime_put_autosuspend(info->dev);
}
}
@@ -141,8 +158,8 @@ static irqreturn_t arizona_micdet(int irq, void *data)
{
struct arizona_extcon_info *info = data;
struct arizona *arizona = info->arizona;
- unsigned int val;
- int ret;
+ unsigned int val, lvl;
+ int ret, i;
mutex_lock(&info->lock);
@@ -219,13 +236,22 @@ static irqreturn_t arizona_micdet(int irq, void *data)
/*
* If we're still detecting and we detect a short then we've
- * got a headphone. Otherwise it's a button press, the
- * button reporting is stubbed out for now.
+ * got a headphone. Otherwise it's a button press.
*/
if (val & 0x3fc) {
if (info->mic) {
dev_dbg(arizona->dev, "Mic button detected\n");
+ lvl = val & ARIZONA_MICD_LVL_MASK;
+ lvl >>= ARIZONA_MICD_LVL_SHIFT;
+
+ for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
+ if (lvl & arizona_lvl_to_key[i].status)
+ input_report_key(info->input,
+ arizona_lvl_to_key[i].report,
+ 1);
+ input_sync(info->input);
+
} else if (info->detecting) {
dev_dbg(arizona->dev, "Headphone detected\n");
info->detecting = false;
@@ -244,6 +270,10 @@ static irqreturn_t arizona_micdet(int irq, void *data)
}
} else {
dev_dbg(arizona->dev, "Mic button released\n");
+ for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
+ input_report_key(info->input,
+ arizona_lvl_to_key[i].report, 0);
+ input_sync(info->input);
}
handled:
@@ -258,7 +288,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
struct arizona_extcon_info *info = data;
struct arizona *arizona = info->arizona;
unsigned int val;
- int ret;
+ int ret, i;
pm_runtime_get_sync(info->dev);
@@ -288,6 +318,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
arizona_stop_mic(info);
+ for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
+ input_report_key(info->input,
+ arizona_lvl_to_key[i].report, 0);
+ input_sync(info->input);
+
ret = extcon_update_state(&info->edev, 0xffffffff, 0);
if (ret != 0)
dev_err(arizona->dev, "Removal report failed: %d\n",
@@ -307,13 +342,13 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct arizona_pdata *pdata;
struct arizona_extcon_info *info;
- int ret, mode;
+ int ret, mode, i;
pdata = dev_get_platdata(arizona->dev);
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) {
- dev_err(&pdev->dev, "failed to allocate memory\n");
+ dev_err(&pdev->dev, "Failed to allocate memory\n");
ret = -ENOMEM;
goto err;
}
@@ -350,7 +385,7 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
ret = extcon_dev_register(&info->edev, arizona->dev);
if (ret < 0) {
- dev_err(arizona->dev, "extcon_dev_regster() failed: %d\n",
+ dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
ret);
goto err;
}
@@ -382,6 +417,20 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
arizona_extcon_set_mode(info, 0);
+ info->input = input_allocate_device();
+ if (!info->input) {
+ dev_err(arizona->dev, "Can't allocate input dev\n");
+ ret = -ENOMEM;
+ goto err_register;
+ }
+
+ for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
+ input_set_capability(info->input, EV_KEY,
+ arizona_lvl_to_key[i].report);
+ info->input->name = "Headset";
+ info->input->phys = "arizona/extcon";
+ info->input->dev.parent = &pdev->dev;
+
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
@@ -391,7 +440,7 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
if (ret != 0) {
dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
ret);
- goto err_register;
+ goto err_input;
}
ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1);
@@ -434,10 +483,23 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);
+ ret = regulator_allow_bypass(info->micvdd, true);
+ if (ret != 0)
+ dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n",
+ ret);
+
pm_runtime_put(&pdev->dev);
+ ret = input_register_device(info->input);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register input device: %d\n", ret);
+ goto err_micdet;
+ }
+
return 0;
+err_micdet:
+ arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
err_fall_wake:
arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0);
err_fall:
@@ -446,6 +508,8 @@ err_rise_wake:
arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
err_rise:
arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
+err_input:
+ input_free_device(info->input);
err_register:
pm_runtime_disable(&pdev->dev);
extcon_dev_unregister(&info->edev);
@@ -468,6 +532,7 @@ static int __devexit arizona_extcon_remove(struct platform_device *pdev)
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
ARIZONA_JD1_ENA, 0);
arizona_clk32k_disable(arizona);
+ input_unregister_device(info->input);
extcon_dev_unregister(&info->edev);
return 0;
diff --git a/drivers/extcon/extcon_class.c b/drivers/extcon/extcon-class.c
index f6419f9db76c..946a3188b2b7 100644
--- a/drivers/extcon/extcon_class.c
+++ b/drivers/extcon/extcon-class.c
@@ -30,6 +30,7 @@
#include <linux/err.h>
#include <linux/extcon.h>
#include <linux/slab.h>
+#include <linux/sysfs.h>
/*
* extcon_cable_name suggests the standard cable names for commonly used
@@ -442,7 +443,7 @@ static int _call_per_cable(struct notifier_block *nb, unsigned long val,
/**
* extcon_register_interest() - Register a notifier for a state change of a
- * specific cable, not a entier set of cables of a
+ * specific cable, not an entier set of cables of a
* extcon device.
* @obj: an empty extcon_specific_cable_nb object to be returned.
* @extcon_name: the name of extcon device.
@@ -498,7 +499,7 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
}
/**
- * extcon_register_notifier() - Register a notifee to get notified by
+ * extcon_register_notifier() - Register a notifiee to get notified by
* any attach status changes from the extcon.
* @edev: the extcon device.
* @nb: a notifier block to be registered.
@@ -515,7 +516,7 @@ int extcon_register_notifier(struct extcon_dev *edev,
EXPORT_SYMBOL_GPL(extcon_register_notifier);
/**
- * extcon_unregister_notifier() - Unregister a notifee from the extcon device.
+ * extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
* @edev: the extcon device.
* @nb: a registered notifier block to be unregistered.
*/
@@ -673,10 +674,12 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
cable->attr_g.name = str;
cable->attr_g.attrs = cable->attrs;
+ sysfs_attr_init(&cable->attr_name.attr);
cable->attr_name.attr.name = "name";
cable->attr_name.attr.mode = 0444;
cable->attr_name.show = cable_name_show;
+ sysfs_attr_init(&cable->attr_state.attr);
cable->attr_state.attr.name = "state";
cable->attr_state.attr.mode = 0644;
cable->attr_state.show = cable_state_show;
@@ -722,6 +725,7 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
goto err_muex;
}
strcpy(name, buf);
+ sysfs_attr_init(&edev->d_attrs_muex[index].attr);
edev->d_attrs_muex[index].attr.name = name;
edev->d_attrs_muex[index].attr.mode = 0000;
edev->attrs_muex[index] = &edev->d_attrs_muex[index]
@@ -802,7 +806,7 @@ EXPORT_SYMBOL_GPL(extcon_dev_register);
/**
* extcon_dev_unregister() - Unregister the extcon device.
- * @edev: the extcon device instance to be unregitered.
+ * @edev: the extcon device instance to be unregistered.
*
* Note that this does not call kfree(edev) because edev was not allocated
* by this class.
diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon-gpio.c
index 3cc152e690b0..3cc152e690b0 100644
--- a/drivers/extcon/extcon_gpio.c
+++ b/drivers/extcon/extcon-gpio.c
diff --git a/drivers/extcon/extcon-max77693.c b/drivers/extcon/extcon-max77693.c
index 38f9e52f358b..e21387e2da5c 100644
--- a/drivers/extcon/extcon-max77693.c
+++ b/drivers/extcon/extcon-max77693.c
@@ -356,7 +356,7 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info,
extcon_set_cable_state(info->edev, "MHL", attached);
break;
default:
- dev_err(info->dev, "faild to detect %s accessory\n",
+ dev_err(info->dev, "failed to detect %s accessory\n",
attached ? "attached" : "detached");
dev_err(info->dev, "- adc:0x%x, adclow:0x%x, adc1k:0x%x\n",
adc, adclow, adc1k);
@@ -548,7 +548,7 @@ static void max77693_muic_irq_work(struct work_struct *work)
curr_adc = info->status[0] & STATUS1_ADC_MASK;
curr_adc >>= STATUS1_ADC_SHIFT;
- /* Check accossory state which is either detached or attached */
+ /* Check accessory state which is either detached or attached */
if (curr_adc == MAX77693_MUIC_ADC_OPEN)
attached = false;
@@ -564,7 +564,7 @@ static void max77693_muic_irq_work(struct work_struct *work)
curr_chg_type = info->status[1] & STATUS2_CHGTYP_MASK;
curr_chg_type >>= STATUS2_CHGTYP_SHIFT;
- /* Check charger accossory state which
+ /* Check charger accessory state which
is either detached or attached */
if (curr_chg_type == MAX77693_CHARGER_TYPE_NONE)
attached = false;
@@ -699,7 +699,7 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
ret = request_threaded_irq(virq, NULL,
max77693_muic_irq_handler,
- 0, muic_irq->name, info);
+ IRQF_ONESHOT, muic_irq->name, info);
if (ret) {
dev_err(&pdev->dev,
"failed: irq request (IRQ: %d,"
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c
index 47408e802ab6..d10c9873dd9a 100644
--- a/drivers/firmware/efivars.c
+++ b/drivers/firmware/efivars.c
@@ -435,12 +435,23 @@ efivar_attr_read(struct efivar_entry *entry, char *buf)
if (status != EFI_SUCCESS)
return -EIO;
- if (var->Attributes & 0x1)
+ if (var->Attributes & EFI_VARIABLE_NON_VOLATILE)
str += sprintf(str, "EFI_VARIABLE_NON_VOLATILE\n");
- if (var->Attributes & 0x2)
+ if (var->Attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)
str += sprintf(str, "EFI_VARIABLE_BOOTSERVICE_ACCESS\n");
- if (var->Attributes & 0x4)
+ if (var->Attributes & EFI_VARIABLE_RUNTIME_ACCESS)
str += sprintf(str, "EFI_VARIABLE_RUNTIME_ACCESS\n");
+ if (var->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD)
+ str += sprintf(str, "EFI_VARIABLE_HARDWARE_ERROR_RECORD\n");
+ if (var->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS)
+ str += sprintf(str,
+ "EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS\n");
+ if (var->Attributes &
+ EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
+ str += sprintf(str,
+ "EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS\n");
+ if (var->Attributes & EFI_VARIABLE_APPEND_WRITE)
+ str += sprintf(str, "EFI_VARIABLE_APPEND_WRITE\n");
return str - buf;
}
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ba7926f5c099..a00b828b1643 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -183,6 +183,12 @@ 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_VT8500
+ bool "VIA/Wondermedia SoC GPIO Support"
+ depends on ARCH_VT8500
+ help
+ Say yes here to support the VT8500/WM8505/WM8650 GPIO controller.
+
config GPIO_XILINX
bool "Xilinx GPIO support"
depends on PPC_OF || MICROBLAZE
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 153caceeb053..a288142ad998 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
+obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c
index 8a420f13905e..ed94b4ea72e9 100644
--- a/drivers/gpio/gpio-lpc32xx.c
+++ b/drivers/gpio/gpio-lpc32xx.c
@@ -308,6 +308,7 @@ static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
{
struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ __set_gpio_level_p012(group, pin, value);
__set_gpio_dir_p012(group, pin, 0);
return 0;
@@ -318,6 +319,7 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
{
struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+ __set_gpio_level_p3(group, pin, value);
__set_gpio_dir_p3(group, pin, 0);
return 0;
@@ -326,6 +328,9 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin,
int value)
{
+ struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+ __set_gpo_level_p3(group, pin, value);
return 0;
}
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index 5a1817eedd1b..9ae29cc0d17f 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -38,7 +38,7 @@ struct mpc8xxx_gpio_chip {
*/
u32 data;
struct irq_domain *irq;
- void *of_dev_id_data;
+ const void *of_dev_id_data;
};
static inline u32 mpc8xxx_gpio2mask(unsigned int gpio)
diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c
index 39e495669961..796fb13e4815 100644
--- a/drivers/gpio/gpio-mxs.c
+++ b/drivers/gpio/gpio-mxs.c
@@ -24,6 +24,7 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -52,8 +53,6 @@
#define GPIO_INT_LEV_MASK (1 << 0)
#define GPIO_INT_POL_MASK (1 << 1)
-#define irq_to_gpio(irq) ((irq) - MXS_GPIO_IRQ_START)
-
enum mxs_gpio_id {
IMX23_GPIO,
IMX28_GPIO,
@@ -63,7 +62,7 @@ struct mxs_gpio_port {
void __iomem *base;
int id;
int irq;
- int virtual_irq_start;
+ struct irq_domain *domain;
struct bgpio_chip bgc;
enum mxs_gpio_id devid;
};
@@ -82,8 +81,7 @@ static inline int is_imx28_gpio(struct mxs_gpio_port *port)
static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
{
- u32 gpio = irq_to_gpio(d->irq);
- u32 pin_mask = 1 << (gpio & 31);
+ u32 pin_mask = 1 << d->hwirq;
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
struct mxs_gpio_port *port = gc->private;
void __iomem *pin_addr;
@@ -120,7 +118,7 @@ static int mxs_gpio_set_irq_type(struct irq_data *d, unsigned int type)
else
writel(pin_mask, pin_addr + MXS_CLR);
- writel(1 << (gpio & 0x1f),
+ writel(pin_mask,
port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
return 0;
@@ -131,7 +129,6 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc)
{
u32 irq_stat;
struct mxs_gpio_port *port = irq_get_handler_data(irq);
- u32 gpio_irq_no_base = port->virtual_irq_start;
desc->irq_data.chip->irq_ack(&desc->irq_data);
@@ -140,7 +137,7 @@ static void mxs_gpio_irq_handler(u32 irq, struct irq_desc *desc)
while (irq_stat != 0) {
int irqoffset = fls(irq_stat) - 1;
- generic_handle_irq(gpio_irq_no_base + irqoffset);
+ generic_handle_irq(irq_find_mapping(port->domain, irqoffset));
irq_stat &= ~(1 << irqoffset);
}
}
@@ -167,12 +164,12 @@ static int mxs_gpio_set_wake_irq(struct irq_data *d, unsigned int enable)
return 0;
}
-static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port)
+static void __init mxs_gpio_init_gc(struct mxs_gpio_port *port, int irq_base)
{
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
- gc = irq_alloc_generic_chip("gpio-mxs", 1, port->virtual_irq_start,
+ gc = irq_alloc_generic_chip("gpio-mxs", 1, irq_base,
port->base, handle_level_irq);
gc->private = port;
@@ -194,7 +191,7 @@ static int mxs_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
struct mxs_gpio_port *port =
container_of(bgc, struct mxs_gpio_port, bgc);
- return port->virtual_irq_start + offset;
+ return irq_find_mapping(port->domain, offset);
}
static struct platform_device_id mxs_gpio_ids[] = {
@@ -226,6 +223,7 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev)
static void __iomem *base;
struct mxs_gpio_port *port;
struct resource *iores = NULL;
+ int irq_base;
int err;
port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
@@ -241,7 +239,6 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev)
port->id = pdev->id;
port->devid = pdev->id_entry->driver_data;
}
- port->virtual_irq_start = MXS_GPIO_IRQ_START + port->id * 32;
port->irq = platform_get_irq(pdev, 0);
if (port->irq < 0)
@@ -275,8 +272,19 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev)
/* clear address has to be used to clear IRQSTAT bits */
writel(~0U, port->base + PINCTRL_IRQSTAT(port) + MXS_CLR);
+ irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
+ if (irq_base < 0)
+ return irq_base;
+
+ port->domain = irq_domain_add_legacy(np, 32, irq_base, 0,
+ &irq_domain_simple_ops, NULL);
+ if (!port->domain) {
+ err = -ENODEV;
+ goto out_irqdesc_free;
+ }
+
/* gpio-mxs can be a generic irq chip */
- mxs_gpio_init_gc(port);
+ mxs_gpio_init_gc(port, irq_base);
/* setup one handler for each entry */
irq_set_chained_handler(port->irq, mxs_gpio_irq_handler);
@@ -287,18 +295,22 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev)
port->base + PINCTRL_DOUT(port), NULL,
port->base + PINCTRL_DOE(port), NULL, 0);
if (err)
- return err;
+ goto out_irqdesc_free;
port->bgc.gc.to_irq = mxs_gpio_to_irq;
port->bgc.gc.base = port->id * 32;
err = gpiochip_add(&port->bgc.gc);
- if (err) {
- bgpio_remove(&port->bgc);
- return err;
- }
+ if (err)
+ goto out_bgpio_remove;
return 0;
+
+out_bgpio_remove:
+ bgpio_remove(&port->bgc);
+out_irqdesc_free:
+ irq_free_descs(irq_base, 32);
+ return err;
}
static struct platform_driver mxs_gpio_driver = {
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 64fbce30c502..94cbc842fbc3 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -1059,7 +1059,7 @@ static int __devinit omap_gpio_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
const struct of_device_id *match;
- struct omap_gpio_platform_data *pdata;
+ const struct omap_gpio_platform_data *pdata;
struct resource *res;
struct gpio_bank *bank;
int ret = 0;
@@ -1441,19 +1441,19 @@ static struct omap_gpio_reg_offs omap4_gpio_regs = {
.fallingdetect = OMAP4_GPIO_FALLINGDETECT,
};
-static struct omap_gpio_platform_data omap2_pdata = {
+const static struct omap_gpio_platform_data omap2_pdata = {
.regs = &omap2_gpio_regs,
.bank_width = 32,
.dbck_flag = false,
};
-static struct omap_gpio_platform_data omap3_pdata = {
+const static struct omap_gpio_platform_data omap3_pdata = {
.regs = &omap2_gpio_regs,
.bank_width = 32,
.dbck_flag = true,
};
-static struct omap_gpio_platform_data omap4_pdata = {
+const static struct omap_gpio_platform_data omap4_pdata = {
.regs = &omap4_gpio_regs,
.bank_width = 32,
.dbck_flag = true,
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index 8af4b06e80f7..a006f0db15af 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -2797,6 +2797,27 @@ static __init void exynos4_gpiolib_init(void)
int group = 0;
void __iomem *gpx_base;
+#ifdef CONFIG_PINCTRL_SAMSUNG
+ /*
+ * This gpio driver includes support for device tree support and
+ * there are platforms using it. In order to maintain
+ * compatibility with those platforms, and to allow non-dt
+ * Exynos4210 platforms to use this gpiolib support, a check
+ * is added to find out if there is a active pin-controller
+ * driver support available. If it is available, this gpiolib
+ * support is ignored and the gpiolib support available in
+ * pin-controller driver is used. This is a temporary check and
+ * will go away when all of the Exynos4210 platforms have
+ * switched to using device tree and the pin-ctrl driver.
+ */
+ struct device_node *pctrl_np;
+ const char *pctrl_compat = "samsung,pinctrl-exynos4210";
+ pctrl_np = of_find_compatible_node(NULL, NULL, pctrl_compat);
+ if (pctrl_np)
+ if (of_device_is_available(pctrl_np))
+ return;
+#endif
+
/* gpio part1 */
gpio_base1 = ioremap(EXYNOS4_PA_GPIO1, SZ_4K);
if (gpio_base1 == NULL) {
diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c
new file mode 100644
index 000000000000..bcd8e4aa7c7d
--- /dev/null
+++ b/drivers/gpio/gpio-vt8500.c
@@ -0,0 +1,316 @@
+/* drivers/gpio/gpio-vt8500.c
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ * Based on arch/arm/mach-vt8500/gpio.c:
+ * - Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+
+/*
+ We handle GPIOs by bank, each bank containing up to 32 GPIOs covered
+ by one set of registers (although not all may be valid).
+
+ Because different SoC's have different register offsets, we pass the
+ register offsets as data in vt8500_gpio_dt_ids[].
+
+ A value of NO_REG is used to indicate that this register is not
+ supported. Only used for ->en at the moment.
+*/
+
+#define NO_REG 0xFFFF
+
+/*
+ * struct vt8500_gpio_bank_regoffsets
+ * @en: offset to enable register of the bank
+ * @dir: offset to direction register of the bank
+ * @data_out: offset to the data out register of the bank
+ * @data_in: offset to the data in register of the bank
+ * @ngpio: highest valid pin in this bank
+ */
+
+struct vt8500_gpio_bank_regoffsets {
+ unsigned int en;
+ unsigned int dir;
+ unsigned int data_out;
+ unsigned int data_in;
+ unsigned char ngpio;
+};
+
+struct vt8500_gpio_data {
+ unsigned int num_banks;
+ struct vt8500_gpio_bank_regoffsets banks[];
+};
+
+#define VT8500_BANK(__en, __dir, __out, __in, __ngpio) \
+{ \
+ .en = __en, \
+ .dir = __dir, \
+ .data_out = __out, \
+ .data_in = __in, \
+ .ngpio = __ngpio, \
+}
+
+static struct vt8500_gpio_data vt8500_data = {
+ .num_banks = 7,
+ .banks = {
+ VT8500_BANK(0x00, 0x20, 0x40, 0x60, 26),
+ VT8500_BANK(0x04, 0x24, 0x44, 0x64, 28),
+ VT8500_BANK(0x08, 0x28, 0x48, 0x68, 31),
+ VT8500_BANK(0x0C, 0x2C, 0x4C, 0x6C, 19),
+ VT8500_BANK(0x10, 0x30, 0x50, 0x70, 19),
+ VT8500_BANK(0x14, 0x34, 0x54, 0x74, 23),
+ VT8500_BANK(NO_REG, 0x3C, 0x5C, 0x7C, 9),
+ },
+};
+
+static struct vt8500_gpio_data wm8505_data = {
+ .num_banks = 10,
+ .banks = {
+ VT8500_BANK(0x40, 0x68, 0x90, 0xB8, 8),
+ VT8500_BANK(0x44, 0x6C, 0x94, 0xBC, 32),
+ VT8500_BANK(0x48, 0x70, 0x98, 0xC0, 6),
+ VT8500_BANK(0x4C, 0x74, 0x9C, 0xC4, 16),
+ VT8500_BANK(0x50, 0x78, 0xA0, 0xC8, 25),
+ VT8500_BANK(0x54, 0x7C, 0xA4, 0xCC, 5),
+ VT8500_BANK(0x58, 0x80, 0xA8, 0xD0, 5),
+ VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12),
+ VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16),
+ VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22),
+ },
+};
+
+/*
+ * No information about which bits are valid so we just make
+ * them all available until its figured out.
+ */
+static struct vt8500_gpio_data wm8650_data = {
+ .num_banks = 9,
+ .banks = {
+ VT8500_BANK(0x40, 0x80, 0xC0, 0x00, 32),
+ VT8500_BANK(0x44, 0x84, 0xC4, 0x04, 32),
+ VT8500_BANK(0x48, 0x88, 0xC8, 0x08, 32),
+ VT8500_BANK(0x4C, 0x8C, 0xCC, 0x0C, 32),
+ VT8500_BANK(0x50, 0x90, 0xD0, 0x10, 32),
+ VT8500_BANK(0x54, 0x94, 0xD4, 0x14, 32),
+ VT8500_BANK(0x58, 0x98, 0xD8, 0x18, 32),
+ VT8500_BANK(0x5C, 0x9C, 0xDC, 0x1C, 32),
+ VT8500_BANK(0x7C, 0xBC, 0xFC, 0x3C, 32),
+ },
+};
+
+struct vt8500_gpio_chip {
+ struct gpio_chip chip;
+
+ const struct vt8500_gpio_bank_regoffsets *regs;
+ void __iomem *base;
+};
+
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static int vt8500_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ u32 val;
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ if (vt8500_chip->regs->en == NO_REG)
+ return 0;
+
+ val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en);
+ val |= BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en);
+
+ return 0;
+}
+
+static void vt8500_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+ u32 val;
+
+ if (vt8500_chip->regs->en == NO_REG)
+ return;
+
+ val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en);
+ val &= ~BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en);
+}
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir);
+ val &= ~BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir);
+
+ return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir);
+ val |= BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir);
+
+ if (value) {
+ val = readl_relaxed(vt8500_chip->base +
+ vt8500_chip->regs->data_out);
+ val |= BIT(offset);
+ writel_relaxed(val, vt8500_chip->base +
+ vt8500_chip->regs->data_out);
+ }
+ return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip, unsigned offset)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ return (readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) >>
+ offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ u32 val = readl_relaxed(vt8500_chip->base +
+ vt8500_chip->regs->data_out);
+ if (value)
+ val |= BIT(offset);
+ else
+ val &= ~BIT(offset);
+
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
+}
+
+static int vt8500_of_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec, u32 *flags)
+{
+ /* bank if specificed in gpiospec->args[0] */
+ if (flags)
+ *flags = gpiospec->args[2];
+
+ return gpiospec->args[1];
+}
+
+static int vt8500_add_chips(struct platform_device *pdev, void __iomem *base,
+ const struct vt8500_gpio_data *data)
+{
+ struct vt8500_gpio_chip *vtchip;
+ struct gpio_chip *chip;
+ int i;
+ int pin_cnt = 0;
+
+ vtchip = devm_kzalloc(&pdev->dev,
+ sizeof(struct vt8500_gpio_chip) * data->num_banks,
+ GFP_KERNEL);
+ if (!vtchip) {
+ pr_err("%s: failed to allocate chip memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < data->num_banks; i++) {
+ vtchip[i].base = base;
+ vtchip[i].regs = &data->banks[i];
+
+ chip = &vtchip[i].chip;
+
+ chip->of_xlate = vt8500_of_xlate;
+ chip->of_gpio_n_cells = 3;
+ chip->of_node = pdev->dev.of_node;
+
+ chip->request = vt8500_gpio_request;
+ chip->free = vt8500_gpio_free;
+ chip->direction_input = vt8500_gpio_direction_input;
+ chip->direction_output = vt8500_gpio_direction_output;
+ chip->get = vt8500_gpio_get_value;
+ chip->set = vt8500_gpio_set_value;
+ chip->can_sleep = 0;
+ chip->base = pin_cnt;
+ chip->ngpio = data->banks[i].ngpio;
+
+ pin_cnt += data->banks[i].ngpio;
+
+ gpiochip_add(chip);
+ }
+ return 0;
+}
+
+static struct of_device_id vt8500_gpio_dt_ids[] = {
+ { .compatible = "via,vt8500-gpio", .data = &vt8500_data, },
+ { .compatible = "wm,wm8505-gpio", .data = &wm8505_data, },
+ { .compatible = "wm,wm8650-gpio", .data = &wm8650_data, },
+ { /* Sentinel */ },
+};
+
+static int __devinit vt8500_gpio_probe(struct platform_device *pdev)
+{
+ void __iomem *gpio_base;
+ struct device_node *np;
+ const struct of_device_id *of_id =
+ of_match_device(vt8500_gpio_dt_ids, &pdev->dev);
+
+ if (!of_id) {
+ dev_err(&pdev->dev, "Failed to find gpio controller\n");
+ return -ENODEV;
+ }
+
+ np = pdev->dev.of_node;
+ if (!np) {
+ dev_err(&pdev->dev, "Missing GPIO description in devicetree\n");
+ return -EFAULT;
+ }
+
+ gpio_base = of_iomap(np, 0);
+ if (!gpio_base) {
+ dev_err(&pdev->dev, "Unable to map GPIO registers\n");
+ of_node_put(np);
+ return -ENOMEM;
+ }
+
+ vt8500_add_chips(pdev, gpio_base, of_id->data);
+
+ return 0;
+}
+
+static struct platform_driver vt8500_gpio_driver = {
+ .probe = vt8500_gpio_probe,
+ .driver = {
+ .name = "vt8500-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = vt8500_gpio_dt_ids,
+ },
+};
+
+module_platform_driver(vt8500_gpio_driver);
+
+MODULE_DESCRIPTION("VT8500 GPIO Driver");
+MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, vt8500_gpio_dt_ids);
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 489e2b162b27..274d25de521e 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -3242,7 +3242,8 @@ i915_gem_object_pin(struct drm_i915_gem_object *obj,
{
int ret;
- BUG_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT);
+ if (WARN_ON(obj->pin_count == DRM_I915_GEM_OBJECT_MAX_PIN_COUNT))
+ return -EBUSY;
if (obj->gtt_space != NULL) {
if ((alignment && obj->gtt_offset & (alignment - 1)) ||
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index bc2ad348e5d8..c040aee1341c 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -4191,12 +4191,6 @@ static void i8xx_update_pll(struct drm_crtc *crtc,
POSTING_READ(DPLL(pipe));
udelay(150);
- I915_WRITE(DPLL(pipe), dpll);
-
- /* Wait for the clocks to stabilize. */
- POSTING_READ(DPLL(pipe));
- udelay(150);
-
/* The LVDS pin pair needs to be on before the DPLLs are enabled.
* This is an exception to the general rule that mode_set doesn't turn
* things on.
@@ -4204,6 +4198,12 @@ static void i8xx_update_pll(struct drm_crtc *crtc,
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
intel_update_lvds(crtc, clock, adjusted_mode);
+ I915_WRITE(DPLL(pipe), dpll);
+
+ /* Wait for the clocks to stabilize. */
+ POSTING_READ(DPLL(pipe));
+ udelay(150);
+
/* The pixel multiplier can only be updated once the
* DPLL is enabled and the clocks are stable.
*
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
index 98f602427eb8..12dc3308ab8c 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -609,7 +609,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
u32 temp;
u32 enable_bits = SDVO_ENABLE;
- if (intel_hdmi->has_audio)
+ if (intel_hdmi->has_audio || mode != DRM_MODE_DPMS_ON)
enable_bits |= SDVO_AUDIO_ENABLE;
temp = I915_READ(intel_hdmi->sdvox_reg);
diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c
index ff23d88880e5..3ca240b4413d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_abi16.c
+++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c
@@ -179,7 +179,7 @@ nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
return 0;
} else
if (init->class == 0x906e) {
- NV_ERROR(dev, "906e not supported yet\n");
+ NV_DEBUG(dev, "906e not supported yet\n");
return -EINVAL;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_gpio.c b/drivers/gpu/drm/nouveau/nv50_gpio.c
index f03490534893..c399d510b27a 100644
--- a/drivers/gpu/drm/nouveau/nv50_gpio.c
+++ b/drivers/gpu/drm/nouveau/nv50_gpio.c
@@ -22,6 +22,7 @@
* Authors: Ben Skeggs
*/
+#include <linux/dmi.h>
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_hw.h"
@@ -110,13 +111,25 @@ nv50_gpio_isr(struct drm_device *dev)
nv_wr32(dev, 0xe074, intr1);
}
+static struct dmi_system_id gpio_reset_ids[] = {
+ {
+ .ident = "Apple Macbook 10,1",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro10,1"),
+ }
+ },
+ { }
+};
+
int
nv50_gpio_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
/* initialise gpios and routing to vbios defaults */
- nouveau_gpio_reset(dev);
+ if (dmi_check_system(gpio_reset_ids))
+ nouveau_gpio_reset(dev);
/* disable, and ack any pending gpio interrupts */
nv_wr32(dev, 0xe050, 0x00000000);
diff --git a/drivers/gpu/drm/nouveau/nvc0_fb.c b/drivers/gpu/drm/nouveau/nvc0_fb.c
index f704e942372e..f376c39310df 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fb.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fb.c
@@ -124,6 +124,7 @@ nvc0_fb_init(struct drm_device *dev)
priv = dev_priv->engine.fb.priv;
nv_wr32(dev, 0x100c10, priv->r100c10 >> 8);
+ nv_mask(dev, 0x17e820, 0x00100000, 0x00000000); /* NV_PLTCG_INTR_EN */
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvc0_fifo.c b/drivers/gpu/drm/nouveau/nvc0_fifo.c
index 7d85553d518c..cd39eb99f5b1 100644
--- a/drivers/gpu/drm/nouveau/nvc0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nvc0_fifo.c
@@ -373,7 +373,8 @@ nvc0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
static void
nvc0_fifo_isr(struct drm_device *dev)
{
- u32 stat = nv_rd32(dev, 0x002100);
+ u32 mask = nv_rd32(dev, 0x002140);
+ u32 stat = nv_rd32(dev, 0x002100) & mask;
if (stat & 0x00000100) {
NV_INFO(dev, "PFIFO: unknown status 0x00000100\n");
diff --git a/drivers/gpu/drm/nouveau/nve0_fifo.c b/drivers/gpu/drm/nouveau/nve0_fifo.c
index e98d144e6eb9..281bece751b6 100644
--- a/drivers/gpu/drm/nouveau/nve0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nve0_fifo.c
@@ -345,7 +345,8 @@ nve0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
static void
nve0_fifo_isr(struct drm_device *dev)
{
- u32 stat = nv_rd32(dev, 0x002100);
+ u32 mask = nv_rd32(dev, 0x002140);
+ u32 stat = nv_rd32(dev, 0x002100) & mask;
if (stat & 0x00000100) {
NV_INFO(dev, "PFIFO: unknown status 0x00000100\n");
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index e721e3087b99..2817101fb167 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -1479,98 +1479,14 @@ static void radeon_legacy_atom_fixup(struct drm_crtc *crtc)
}
}
-/**
- * radeon_get_pll_use_mask - look up a mask of which pplls are in use
- *
- * @crtc: drm crtc
- *
- * Returns the mask of which PPLLs (Pixel PLLs) are in use.
- */
-static u32 radeon_get_pll_use_mask(struct drm_crtc *crtc)
-{
- struct drm_device *dev = crtc->dev;
- struct drm_crtc *test_crtc;
- struct radeon_crtc *radeon_test_crtc;
- u32 pll_in_use = 0;
-
- list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
- if (crtc == test_crtc)
- continue;
-
- radeon_test_crtc = to_radeon_crtc(test_crtc);
- if (radeon_test_crtc->pll_id != ATOM_PPLL_INVALID)
- pll_in_use |= (1 << radeon_test_crtc->pll_id);
- }
- return pll_in_use;
-}
-
-/**
- * radeon_get_shared_dp_ppll - return the PPLL used by another crtc for DP
- *
- * @crtc: drm crtc
- *
- * Returns the PPLL (Pixel PLL) used by another crtc/encoder which is
- * also in DP mode. For DP, a single PPLL can be used for all DP
- * crtcs/encoders.
- */
-static int radeon_get_shared_dp_ppll(struct drm_crtc *crtc)
-{
- struct drm_device *dev = crtc->dev;
- struct drm_encoder *test_encoder;
- struct radeon_crtc *radeon_test_crtc;
-
- list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
- if (test_encoder->crtc && (test_encoder->crtc != crtc)) {
- if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) {
- /* for DP use the same PLL for all */
- radeon_test_crtc = to_radeon_crtc(test_encoder->crtc);
- if (radeon_test_crtc->pll_id != ATOM_PPLL_INVALID)
- return radeon_test_crtc->pll_id;
- }
- }
- }
- return ATOM_PPLL_INVALID;
-}
-
-/**
- * radeon_atom_pick_pll - Allocate a PPLL for use by the crtc.
- *
- * @crtc: drm crtc
- *
- * Returns the PPLL (Pixel PLL) to be used by the crtc. For DP monitors
- * a single PPLL can be used for all DP crtcs/encoders. For non-DP
- * monitors a dedicated PPLL must be used. If a particular board has
- * an external DP PLL, return ATOM_PPLL_INVALID to skip PLL programming
- * as there is no need to program the PLL itself. If we are not able to
- * allocate a PLL, return ATOM_PPLL_INVALID to skip PLL programming to
- * avoid messing up an existing monitor.
- *
- * Asic specific PLL information
- *
- * DCE 6.1
- * - PPLL2 is only available to UNIPHYA (both DP and non-DP)
- * - PPLL0, PPLL1 are available for UNIPHYB/C/D/E/F (both DP and non-DP)
- *
- * DCE 6.0
- * - PPLL0 is available to all UNIPHY (DP only)
- * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC
- *
- * DCE 5.0
- * - DCPLL is available to all UNIPHY (DP only)
- * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC
- *
- * DCE 3.0/4.0/4.1
- * - PPLL1, PPLL2 are available for all UNIPHY (both DP and non-DP) and DAC
- *
- */
static int radeon_atom_pick_pll(struct drm_crtc *crtc)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct radeon_device *rdev = dev->dev_private;
struct drm_encoder *test_encoder;
- u32 pll_in_use;
- int pll;
+ struct drm_crtc *test_crtc;
+ uint32_t pll_in_use = 0;
if (ASIC_IS_DCE61(rdev)) {
list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
@@ -1582,40 +1498,32 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
if ((test_radeon_encoder->encoder_id ==
ENCODER_OBJECT_ID_INTERNAL_UNIPHY) &&
- (dig->linkb == false))
- /* UNIPHY A uses PPLL2 */
+ (dig->linkb == false)) /* UNIPHY A uses PPLL2 */
return ATOM_PPLL2;
- else if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) {
- /* UNIPHY B/C/D/E/F */
- if (rdev->clock.dp_extclk)
- /* skip PPLL programming if using ext clock */
- return ATOM_PPLL_INVALID;
- else {
- /* use the same PPLL for all DP monitors */
- pll = radeon_get_shared_dp_ppll(crtc);
- if (pll != ATOM_PPLL_INVALID)
- return pll;
- }
- }
- break;
}
}
/* UNIPHY B/C/D/E/F */
- pll_in_use = radeon_get_pll_use_mask(crtc);
- if (!(pll_in_use & (1 << ATOM_PPLL0)))
+ list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
+ struct radeon_crtc *radeon_test_crtc;
+
+ if (crtc == test_crtc)
+ continue;
+
+ radeon_test_crtc = to_radeon_crtc(test_crtc);
+ if ((radeon_test_crtc->pll_id == ATOM_PPLL0) ||
+ (radeon_test_crtc->pll_id == ATOM_PPLL1))
+ pll_in_use |= (1 << radeon_test_crtc->pll_id);
+ }
+ if (!(pll_in_use & 4))
return ATOM_PPLL0;
- if (!(pll_in_use & (1 << ATOM_PPLL1)))
- return ATOM_PPLL1;
- DRM_ERROR("unable to allocate a PPLL\n");
- return ATOM_PPLL_INVALID;
+ return ATOM_PPLL1;
} else if (ASIC_IS_DCE4(rdev)) {
list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
if (test_encoder->crtc && (test_encoder->crtc == crtc)) {
/* in DP mode, the DP ref clock can come from PPLL, DCPLL, or ext clock,
* depending on the asic:
* DCE4: PPLL or ext clock
- * DCE5: PPLL, DCPLL, or ext clock
- * DCE6: PPLL, PPLL0, or ext clock
+ * DCE5: DCPLL or ext clock
*
* Setting ATOM_PPLL_INVALID will cause SetPixelClock to skip
* PPLL/DCPLL programming and only program the DP DTO for the
@@ -1623,34 +1531,31 @@ static int radeon_atom_pick_pll(struct drm_crtc *crtc)
*/
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) {
if (rdev->clock.dp_extclk)
- /* skip PPLL programming if using ext clock */
return ATOM_PPLL_INVALID;
else if (ASIC_IS_DCE6(rdev))
- /* use PPLL0 for all DP */
return ATOM_PPLL0;
else if (ASIC_IS_DCE5(rdev))
- /* use DCPLL for all DP */
return ATOM_DCPLL;
- else {
- /* use the same PPLL for all DP monitors */
- pll = radeon_get_shared_dp_ppll(crtc);
- if (pll != ATOM_PPLL_INVALID)
- return pll;
- }
}
- break;
}
}
- /* all other cases */
- pll_in_use = radeon_get_pll_use_mask(crtc);
- if (!(pll_in_use & (1 << ATOM_PPLL2)))
- return ATOM_PPLL2;
- if (!(pll_in_use & (1 << ATOM_PPLL1)))
+
+ /* otherwise, pick one of the plls */
+ list_for_each_entry(test_crtc, &dev->mode_config.crtc_list, head) {
+ struct radeon_crtc *radeon_test_crtc;
+
+ if (crtc == test_crtc)
+ continue;
+
+ radeon_test_crtc = to_radeon_crtc(test_crtc);
+ if ((radeon_test_crtc->pll_id >= ATOM_PPLL1) &&
+ (radeon_test_crtc->pll_id <= ATOM_PPLL2))
+ pll_in_use |= (1 << radeon_test_crtc->pll_id);
+ }
+ if (!(pll_in_use & 1))
return ATOM_PPLL1;
- DRM_ERROR("unable to allocate a PPLL\n");
- return ATOM_PPLL_INVALID;
+ return ATOM_PPLL2;
} else
- /* use PPLL1 or PPLL2 */
return radeon_crtc->crtc_id;
}
@@ -1792,7 +1697,7 @@ static void atombios_crtc_disable(struct drm_crtc *crtc)
break;
}
done:
- radeon_crtc->pll_id = ATOM_PPLL_INVALID;
+ radeon_crtc->pll_id = -1;
}
static const struct drm_crtc_helper_funcs atombios_helper_funcs = {
@@ -1841,6 +1746,6 @@ void radeon_atombios_init_crtc(struct drm_device *dev,
else
radeon_crtc->crtc_offset = 0;
}
- radeon_crtc->pll_id = ATOM_PPLL_INVALID;
+ radeon_crtc->pll_id = -1;
drm_crtc_helper_add(&radeon_crtc->base, &atombios_helper_funcs);
}
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index e93b80a6d4e9..ed3340adeb6f 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -77,13 +77,9 @@ void evergreen_tiling_fields(unsigned tiling_flags, unsigned *bankw,
void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev)
{
u16 ctl, v;
- int cap, err;
+ int err;
- cap = pci_pcie_cap(rdev->pdev);
- if (!cap)
- return;
-
- err = pci_read_config_word(rdev->pdev, cap + PCI_EXP_DEVCTL, &ctl);
+ err = pcie_capability_read_word(rdev->pdev, PCI_EXP_DEVCTL, &ctl);
if (err)
return;
@@ -95,7 +91,7 @@ void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev)
if ((v == 0) || (v == 6) || (v == 7)) {
ctl &= ~PCI_EXP_DEVCTL_READRQ;
ctl |= (2 << 12);
- pci_write_config_word(rdev->pdev, cap + PCI_EXP_DEVCTL, ctl);
+ pcie_capability_write_word(rdev->pdev, PCI_EXP_DEVCTL, ctl);
}
}
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index 8acb34fd3fd5..8d7e33a0b243 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -1182,7 +1182,8 @@ int r100_cp_init(struct radeon_device *rdev, unsigned ring_size)
ring->ready = true;
radeon_ttm_set_active_vram_size(rdev, rdev->mc.real_vram_size);
- if (radeon_ring_supports_scratch_reg(rdev, ring)) {
+ if (!ring->rptr_save_reg /* not resuming from suspend */
+ && radeon_ring_supports_scratch_reg(rdev, ring)) {
r = radeon_scratch_get(rdev, &ring->rptr_save_reg);
if (r) {
DRM_ERROR("failed to get scratch reg for rptr save (%d).\n", r);
diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c
index ba055e9ca007..8d9dc44f1f94 100644
--- a/drivers/gpu/drm/udl/udl_connector.c
+++ b/drivers/gpu/drm/udl/udl_connector.c
@@ -69,6 +69,13 @@ static int udl_get_modes(struct drm_connector *connector)
static int udl_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
+ struct udl_device *udl = connector->dev->dev_private;
+ if (!udl->sku_pixel_limit)
+ return 0;
+
+ if (mode->vdisplay * mode->hdisplay > udl->sku_pixel_limit)
+ return MODE_VIRTUAL_Y;
+
return 0;
}
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
index f2fb8f15e2f1..7e0743358dff 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
@@ -1018,7 +1018,7 @@ int vmw_event_fence_action_create(struct drm_file *file_priv,
}
- event = kzalloc(sizeof(event->event), GFP_KERNEL);
+ event = kzalloc(sizeof(*event), GFP_KERNEL);
if (unlikely(event == NULL)) {
DRM_ERROR("Failed to allocate an event.\n");
ret = -ENOMEM;
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c
index 3df8fc0ec01a..e893f6e1937d 100644
--- a/drivers/gpu/vga/vgaarb.c
+++ b/drivers/gpu/vga/vgaarb.c
@@ -141,7 +141,11 @@ EXPORT_SYMBOL_GPL(vga_default_device);
void vga_set_default_device(struct pci_dev *pdev)
{
- vga_default = pdev;
+ if (vga_default == pdev)
+ return;
+
+ pci_dev_put(vga_default);
+ vga_default = pci_dev_get(pdev);
}
#endif
@@ -577,7 +581,7 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev)
#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
if (vga_default == NULL &&
((vgadev->owns & VGA_RSRC_LEGACY_MASK) == VGA_RSRC_LEGACY_MASK))
- vga_default = pci_dev_get(pdev);
+ vga_set_default_device(pdev);
#endif
vga_arbiter_check_bridge_sharing(vgadev);
@@ -613,10 +617,8 @@ static bool vga_arbiter_del_pci_device(struct pci_dev *pdev)
}
#ifndef __ARCH_HAS_VGA_DEFAULT_DEVICE
- if (vga_default == pdev) {
- pci_dev_put(vga_default);
- vga_default = NULL;
- }
+ if (vga_default == pdev)
+ vga_set_default_device(NULL);
#endif
if (vgadev->decodes & (VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM))
@@ -1066,7 +1068,6 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
}
} else if (strncmp(curr_pos, "target ", 7) == 0) {
- struct pci_bus *pbus;
unsigned int domain, bus, devfn;
struct vga_device *vgadev;
@@ -1085,19 +1086,11 @@ static ssize_t vga_arb_write(struct file *file, const char __user * buf,
pr_debug("vgaarb: %s ==> %x:%x:%x.%x\n", curr_pos,
domain, bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
- pbus = pci_find_bus(domain, bus);
- pr_debug("vgaarb: pbus %p\n", pbus);
- if (pbus == NULL) {
- pr_err("vgaarb: invalid PCI domain and/or bus address %x:%x\n",
- domain, bus);
- ret_val = -ENODEV;
- goto done;
- }
- pdev = pci_get_slot(pbus, devfn);
+ pdev = pci_get_domain_bus_and_slot(domain, bus, devfn);
pr_debug("vgaarb: pdev %p\n", pdev);
if (!pdev) {
- pr_err("vgaarb: invalid PCI address %x:%x\n",
- bus, devfn);
+ pr_err("vgaarb: invalid PCI address %x:%x:%x\n",
+ domain, bus, devfn);
ret_val = -ENODEV;
goto done;
}
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index fbf49503508d..1630150ad2b1 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -307,7 +307,6 @@ config HID_LOGITECH
config HID_LOGITECH_DJ
tristate "Logitech Unifying receivers full support"
depends on HID_LOGITECH
- default m
---help---
Say Y if you want support for Logitech Unifying receivers and devices.
Unifying receivers are capable of pairing up to 6 Logitech compliant
@@ -527,6 +526,14 @@ config HID_PICOLCD_LEDS
---help---
Provide access to PicoLCD's GPO pins via leds class.
+config HID_PICOLCD_CIR
+ bool "CIR via RC class" if EXPERT
+ default !EXPERT
+ depends on HID_PICOLCD
+ depends on HID_PICOLCD=RC_CORE || RC_CORE=y
+ ---help---
+ Provide access to PicoLCD's CIR interface via remote control (LIRC).
+
config HID_PRIMAX
tristate "Primax non-fully HID-compliant devices"
depends on USB_HID
@@ -534,6 +541,15 @@ config HID_PRIMAX
Support for Primax devices that are not fully compliant with the
HID standard.
+config HID_PS3REMOTE
+ tristate "Sony PS3 BD Remote Control"
+ depends on BT_HIDP
+ ---help---
+ Support for the Sony PS3 Blue-ray Disk Remote Control and Logitech
+ Harmony Adapter for PS3, which connect over Bluetooth.
+
+ Support for the 6-axis controllers is provided by HID_SONY.
+
config HID_ROCCAT
tristate "Roccat device support"
depends on USB_HID
@@ -561,7 +577,9 @@ config HID_SONY
tristate "Sony PS3 controller"
depends on USB_HID
---help---
- Support for Sony PS3 controller.
+ Support for Sony PS3 6-axis controllers.
+
+ Support for the Sony PS3 BD Remote is provided by HID_PS3REMOTE.
config HID_SPEEDLINK
tristate "Speedlink VAD Cezanne mouse support"
@@ -690,6 +708,20 @@ config HID_ZYDACRON
---help---
Support for Zydacron remote control.
+config HID_SENSOR_HUB
+ tristate "HID Sensors framework support"
+ depends on USB_HID
+ select MFD_CORE
+ default n
+ -- help---
+ Support for HID Sensor framework. This creates a MFD instance
+ for a sensor hub and identifies all the sensors connected to it.
+ Each sensor is registered as a MFD cell, so that sensor specific
+ processing can be done in a separate driver. Each sensor
+ drivers can use the service provided by this driver to register
+ for events and handle data streams. Each sensor driver can format
+ data and present to user mode using input or IIO interface.
+
endmenu
endif # HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index f975485f88b2..cef68ca859d3 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -69,7 +69,28 @@ obj-$(CONFIG_HID_PRODIKEYS) += hid-prodikeys.o
obj-$(CONFIG_HID_PANTHERLORD) += hid-pl.o
obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o
obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o
+hid-picolcd-y += hid-picolcd_core.o
+ifdef CONFIG_HID_PICOLCD_FB
+hid-picolcd-y += hid-picolcd_fb.o
+endif
+ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+hid-picolcd-y += hid-picolcd_backlight.o
+endif
+ifdef CONFIG_HID_PICOLCD_LCD
+hid-picolcd-y += hid-picolcd_lcd.o
+endif
+ifdef CONFIG_HID_PICOLCD_LEDS
+hid-picolcd-y += hid-picolcd_leds.o
+endif
+ifdef CONFIG_HID_PICOLCD_CIR
+hid-picolcd-y += hid-picolcd_cir.o
+endif
+ifdef CONFIG_DEBUG_FS
+hid-picolcd-y += hid-picolcd_debugfs.o
+endif
+
obj-$(CONFIG_HID_PRIMAX) += hid-primax.o
+obj-$(CONFIG_HID_PS3REMOTE) += hid-ps3remote.o
obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \
hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \
@@ -91,6 +112,7 @@ obj-$(CONFIG_HID_ZYDACRON) += hid-zydacron.o
obj-$(CONFIG_HID_WACOM) += hid-wacom.o
obj-$(CONFIG_HID_WALTOP) += hid-waltop.o
obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o
+obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o
obj-$(CONFIG_USB_HID) += usbhid/
obj-$(CONFIG_USB_MOUSE) += usbhid/
diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c
index 902d1dfeb1b5..0a239885e67c 100644
--- a/drivers/hid/hid-a4tech.c
+++ b/drivers/hid/hid-a4tech.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
index 585344b6d338..06ebdbb6ea02 100644
--- a/drivers/hid/hid-apple.c
+++ b/drivers/hid/hid-apple.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
*/
diff --git a/drivers/hid/hid-aureal.c b/drivers/hid/hid-aureal.c
index ba64b041b8bf..7968187ddf7b 100644
--- a/drivers/hid/hid-aureal.c
+++ b/drivers/hid/hid-aureal.c
@@ -9,7 +9,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
#include <linux/device.h>
diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c
index a1a765a5b08a..a1a5a12c3a6b 100644
--- a/drivers/hid/hid-belkin.c
+++ b/drivers/hid/hid-belkin.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c
index 888ece68a47c..af034d3d9256 100644
--- a/drivers/hid/hid-cherry.c
+++ b/drivers/hid/hid-cherry.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 8bcd168fffae..bd3971bf31bf 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -126,7 +126,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
hid_err(parser->device, "collection stack overflow\n");
- return -1;
+ return -EINVAL;
}
if (parser->device->maxcollection == parser->device->collection_size) {
@@ -134,7 +134,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
parser->device->collection_size * 2, GFP_KERNEL);
if (collection == NULL) {
hid_err(parser->device, "failed to reallocate collection array\n");
- return -1;
+ return -ENOMEM;
}
memcpy(collection, parser->device->collection,
sizeof(struct hid_collection) *
@@ -170,7 +170,7 @@ static int close_collection(struct hid_parser *parser)
{
if (!parser->collection_stack_ptr) {
hid_err(parser->device, "collection stack underflow\n");
- return -1;
+ return -EINVAL;
}
parser->collection_stack_ptr--;
return 0;
@@ -374,7 +374,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
parser->global.report_size = item_udata(item);
- if (parser->global.report_size > 96) {
+ if (parser->global.report_size > 128) {
hid_err(parser->device, "invalid report_size %d\n",
parser->global.report_size);
return -1;
@@ -757,6 +757,7 @@ int hid_open_report(struct hid_device *device)
struct hid_item item;
unsigned int size;
__u8 *start;
+ __u8 *buf;
__u8 *end;
int ret;
static int (*dispatch_type[])(struct hid_parser *parser,
@@ -775,12 +776,21 @@ int hid_open_report(struct hid_device *device)
return -ENODEV;
size = device->dev_rsize;
+ buf = kmemdup(start, size, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
if (device->driver->report_fixup)
- start = device->driver->report_fixup(device, start, &size);
+ start = device->driver->report_fixup(device, buf, &size);
+ else
+ start = buf;
- device->rdesc = kmemdup(start, size, GFP_KERNEL);
- if (device->rdesc == NULL)
+ start = kmemdup(start, size, GFP_KERNEL);
+ kfree(buf);
+ if (start == NULL)
return -ENOMEM;
+
+ device->rdesc = start;
device->rsize = size;
parser = vzalloc(sizeof(struct hid_parser));
@@ -1448,7 +1458,14 @@ void hid_disconnect(struct hid_device *hdev)
}
EXPORT_SYMBOL_GPL(hid_disconnect);
-/* a list of devices for which there is a specialized driver on HID bus */
+/*
+ * A list of devices for which there is a specialized driver on HID bus.
+ *
+ * Please note that for multitouch devices (driven by hid-multitouch driver),
+ * there is a proper autodetection and autoloading in place (based on presence
+ * of HID_DG_CONTACTID), so those devices don't need to be added to this list,
+ * as we are doing the right thing in hid_scan_usage().
+ */
static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
{ HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
@@ -1551,6 +1568,10 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK, USB_DEVICE_ID_HOLTEK_ON_LINE_GRIP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_HOLTEK_ALT, USB_DEVICE_ID_HOLTEK_ALT_KEYBOARD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_1020) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086, USB_DEVICE_ID_SENSOR_HUB_09FA) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_1020) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087, USB_DEVICE_ID_SENSOR_HUB_09FA) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KEYTOUCH, USB_DEVICE_ID_KEYTOUCH_IEC) },
{ HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) },
@@ -1566,6 +1587,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
@@ -1627,6 +1649,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
+#if IS_ENABLED(CONFIG_HID_ROCCAT)
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
@@ -1635,15 +1658,18 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
+#endif
{ HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE) },
{ HID_USB_DEVICE(USB_VENDOR_ID_SUNPLUS, USB_DEVICE_ID_SUNPLUS_WDESKTOP) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM, USB_DEVICE_ID_SENSOR_HUB_7014) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb300) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb304) },
{ HID_USB_DEVICE(USB_VENDOR_ID_THRUSTMASTER, 0xb323) },
@@ -1663,6 +1689,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) },
{ HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) },
diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c
index 9e43aaca9774..3e159a50dac7 100644
--- a/drivers/hid/hid-cypress.c
+++ b/drivers/hid/hid-cypress.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-debug.c b/drivers/hid/hid-debug.c
index 01dd9a7daf7a..933fff0fff1f 100644
--- a/drivers/hid/hid-debug.c
+++ b/drivers/hid/hid-debug.c
@@ -911,15 +911,21 @@ static void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f)
}
-
static int hid_debug_rdesc_show(struct seq_file *f, void *p)
{
struct hid_device *hdev = f->private;
+ const __u8 *rdesc = hdev->rdesc;
+ unsigned rsize = hdev->rsize;
int i;
+ if (!rdesc) {
+ rdesc = hdev->dev_rdesc;
+ rsize = hdev->dev_rsize;
+ }
+
/* dump HID report descriptor */
- for (i = 0; i < hdev->rsize; i++)
- seq_printf(f, "%02x ", hdev->rdesc[i]);
+ for (i = 0; i < rsize; i++)
+ seq_printf(f, "%02x ", rdesc[i]);
seq_printf(f, "\n\n");
/* dump parsed data and input mappings */
diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c
index ca1163e9d42d..6540af2871a7 100644
--- a/drivers/hid/hid-ezkey.c
+++ b/drivers/hid/hid-ezkey.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c
index e88b951cd10d..4442c30ef531 100644
--- a/drivers/hid/hid-gyration.c
+++ b/drivers/hid/hid-gyration.c
@@ -4,7 +4,6 @@
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2006-2008 Jiri Kosina
*/
diff --git a/drivers/hid/hid-holtekff.c b/drivers/hid/hid-holtekff.c
index 4e7542151e22..ff295e60059b 100644
--- a/drivers/hid/hid-holtekff.c
+++ b/drivers/hid/hid-holtekff.c
@@ -100,8 +100,7 @@ static void holtekff_send(struct holtekff_device *holtekff,
holtekff->field->value[i] = data[i];
}
- dbg_hid("sending %02x %02x %02x %02x %02x %02x %02x\n", data[0],
- data[1], data[2], data[3], data[4], data[5], data[6]);
+ dbg_hid("sending %*ph\n", 7, data);
usbhid_submit_report(hid, holtekff->field->report, USB_DIR_OUT);
}
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 1dcb76ff51e3..269b50912a4a 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
*/
/*
@@ -269,7 +268,11 @@
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA 0x72fa
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302 0x7302
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349 0x7349
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7 0x73f7
#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001 0xa001
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224 0x7224
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0 0x72d0
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4 0x72c4
#define USB_VENDOR_ID_ELECOM 0x056e
#define USB_DEVICE_ID_ELECOM_BM084 0x0061
@@ -283,6 +286,9 @@
#define USB_VENDOR_ID_EMS 0x2006
#define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118
+#define USB_VENDOR_ID_FLATFROG 0x25b5
+#define USB_DEVICE_ID_MULTITOUCH_3200 0x0002
+
#define USB_VENDOR_ID_ESSENTIAL_REALITY 0x0d7f
#define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
@@ -296,6 +302,9 @@
#define USB_VENDOR_ID_EZKEY 0x0518
#define USB_DEVICE_ID_BTC_8193 0x0002
+#define USB_VENDOR_ID_FREESCALE 0x15A2
+#define USB_DEVICE_ID_FREESCALE_MX28 0x004F
+
#define USB_VENDOR_ID_FRUCTEL 0x25B6
#define USB_DEVICE_ID_GAMETEL_MT_MODE 0x0002
@@ -305,6 +314,7 @@
#define USB_VENDOR_ID_GENERAL_TOUCH 0x0dfc
#define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100
#define USB_VENDOR_ID_GLAB 0x06c2
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
@@ -419,6 +429,11 @@
#define USB_VENDOR_ID_IMATION 0x0718
#define USB_DEVICE_ID_DISC_STAKKA 0xd000
+#define USB_VENDOR_ID_INTEL_8086 0x8086
+#define USB_VENDOR_ID_INTEL_8087 0x8087
+#define USB_DEVICE_ID_SENSOR_HUB_1020 0x1020
+#define USB_DEVICE_ID_SENSOR_HUB_09FA 0x09FA
+
#define USB_VENDOR_ID_IRTOUCHSYSTEMS 0x6615
#define USB_DEVICE_ID_IRTOUCH_INFRARED_USB 0x0070
@@ -496,6 +511,7 @@
#define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101
#define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110
#define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
+#define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD 0xc20a
#define USB_DEVICE_ID_LOGITECH_RUMBLEPAD 0xc211
#define USB_DEVICE_ID_LOGITECH_EXTREME_3D 0xc215
@@ -652,7 +668,6 @@
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH 0x3000
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001 0x3001
#define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008 0x3008
-#define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001
#define USB_VENDOR_ID_ROCCAT 0x1e7d
#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4
@@ -683,6 +698,7 @@
#define USB_VENDOR_ID_SONY 0x054c
#define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE 0x024b
+#define USB_DEVICE_ID_SONY_PS3_BDREMOTE 0x0306
#define USB_DEVICE_ID_SONY_PS3_CONTROLLER 0x0268
#define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER 0x042f
@@ -695,6 +711,7 @@
#define USB_VENDOR_ID_STANTUM_STM 0x0483
#define USB_DEVICE_ID_MTP_STM 0x3261
+#define USB_DEVICE_ID_SENSOR_HUB_7014 0x7014
#define USB_VENDOR_ID_STANTUM_SITRONIX 0x1403
#define USB_DEVICE_ID_MTP_SITRONIX 0x5001
@@ -758,6 +775,7 @@
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U 0x0005
#define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062 0x0064
#define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850 0x0522
+#define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60 0x0781
#define USB_VENDOR_ID_UNITEC 0x227d
#define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709 0x0709
diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c
index 811bfad64609..d917c0d53685 100644
--- a/drivers/hid/hid-input.c
+++ b/drivers/hid/hid-input.c
@@ -1154,6 +1154,7 @@ static void report_features(struct hid_device *hid)
int hidinput_connect(struct hid_device *hid, unsigned int force)
{
+ struct hid_driver *drv = hid->driver;
struct hid_report *report;
struct hid_input *hidinput = NULL;
struct input_dev *input_dev;
@@ -1228,6 +1229,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
* UGCI) cram a lot of unrelated inputs into the
* same interface. */
hidinput->report = report;
+ if (drv->input_configured)
+ drv->input_configured(hid, hidinput);
if (input_register_device(hidinput->input))
goto out_cleanup;
hidinput = NULL;
@@ -1235,8 +1238,12 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
}
}
- if (hidinput && input_register_device(hidinput->input))
- goto out_cleanup;
+ if (hidinput) {
+ if (drv->input_configured)
+ drv->input_configured(hid, hidinput);
+ if (input_register_device(hidinput->input))
+ goto out_cleanup;
+ }
return 0;
diff --git a/drivers/hid/hid-lcpower.c b/drivers/hid/hid-lcpower.c
index c4fe9bd095b7..22bc14abdfa3 100644
--- a/drivers/hid/hid-lcpower.c
+++ b/drivers/hid/hid-lcpower.c
@@ -24,7 +24,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- if ((usage->hid & HID_USAGE_PAGE) != 0x0ffbc0000)
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
return 0;
switch (usage->hid & HID_USAGE) {
diff --git a/drivers/hid/hid-lenovo-tpkbd.c b/drivers/hid/hid-lenovo-tpkbd.c
index 77d2df04c97b..cea016e94f43 100644
--- a/drivers/hid/hid-lenovo-tpkbd.c
+++ b/drivers/hid/hid-lenovo-tpkbd.c
@@ -56,9 +56,8 @@ static int tpkbd_input_mapping(struct hid_device *hdev,
static int tpkbd_features_set(struct hid_device *hdev)
{
struct hid_report *report;
- struct tpkbd_data_pointer *data_pointer;
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
report->field[0]->value[0] = data_pointer->press_to_select ? 0x01 : 0x02;
@@ -77,14 +76,8 @@ static ssize_t pointer_press_to_select_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
-
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
}
@@ -94,16 +87,10 @@ static ssize_t pointer_press_to_select_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
@@ -119,14 +106,8 @@ static ssize_t pointer_dragging_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
-
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
}
@@ -136,16 +117,10 @@ static ssize_t pointer_dragging_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
@@ -161,14 +136,8 @@ static ssize_t pointer_release_to_select_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
-
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
}
@@ -178,16 +147,10 @@ static ssize_t pointer_release_to_select_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
@@ -203,14 +166,8 @@ static ssize_t pointer_select_right_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
-
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
}
@@ -220,16 +177,10 @@ static ssize_t pointer_select_right_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value))
return -EINVAL;
if (value < 0 || value > 1)
@@ -245,14 +196,8 @@ static ssize_t pointer_sensitivity_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
-
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n",
data_pointer->sensitivity);
@@ -263,16 +208,10 @@ static ssize_t pointer_sensitivity_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
return -EINVAL;
@@ -286,14 +225,10 @@ static ssize_t pointer_press_speed_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+ data_pointer = hid_get_drvdata(hdev);
return snprintf(buf, PAGE_SIZE, "%u\n",
data_pointer->press_speed);
@@ -304,16 +239,10 @@ static ssize_t pointer_press_speed_store(struct device *dev,
const char *buf,
size_t count)
{
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int value;
- hdev = container_of(dev, struct hid_device, dev);
- if (hdev == NULL)
- return -ENODEV;
-
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
return -EINVAL;
@@ -370,15 +299,11 @@ static const struct attribute_group tpkbd_attr_group_pointer = {
static enum led_brightness tpkbd_led_brightness_get(
struct led_classdev *led_cdev)
{
- struct device *dev;
- struct hid_device *hdev;
- struct tpkbd_data_pointer *data_pointer;
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
int led_nr = 0;
- dev = led_cdev->dev->parent;
- hdev = container_of(dev, struct hid_device, dev);
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (led_cdev == &data_pointer->led_micmute)
led_nr = 1;
@@ -390,16 +315,12 @@ static enum led_brightness tpkbd_led_brightness_get(
static void tpkbd_led_brightness_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- struct device *dev;
- struct hid_device *hdev;
+ struct device *dev = led_cdev->dev->parent;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
struct hid_report *report;
- struct tpkbd_data_pointer *data_pointer;
int led_nr = 0;
- dev = led_cdev->dev->parent;
- hdev = container_of(dev, struct hid_device, dev);
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
if (led_cdev == &data_pointer->led_micmute)
led_nr = 1;
@@ -508,17 +429,17 @@ err_free:
static void tpkbd_remove_tp(struct hid_device *hdev)
{
- struct tpkbd_data_pointer *data_pointer;
+ struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
sysfs_remove_group(&hdev->dev.kobj,
&tpkbd_attr_group_pointer);
- data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
led_classdev_unregister(&data_pointer->led_micmute);
led_classdev_unregister(&data_pointer->led_mute);
hid_set_drvdata(hdev, NULL);
+ kfree(data_pointer->led_micmute.name);
+ kfree(data_pointer->led_mute.name);
kfree(data_pointer);
}
diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c
index fc37ed6b108c..a2f8e88b9fa2 100644
--- a/drivers/hid/hid-lg.c
+++ b/drivers/hid/hid-lg.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2010 Hendrik Iben
*/
@@ -109,7 +108,7 @@ static __u8 dfp_rdesc_fixed[] = {
static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
rdesc[84] == 0x8c && rdesc[85] == 0x02) {
@@ -278,7 +277,7 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
0, 0, 0, 0, 0,183,184,185,186,187,
188,189,190,191,192,193,194, 0, 0, 0
};
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
unsigned int hid = usage->hid;
if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
@@ -319,7 +318,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
(field->flags & HID_MAIN_ITEM_RELATIVE))
@@ -335,13 +334,16 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
static int lg_event(struct hid_device *hdev, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
input_event(field->hidinput->input, usage->type, usage->code,
-value);
return 1;
}
+ if (drv_data->quirks & LG_FF4) {
+ return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
+ }
return 0;
}
@@ -358,7 +360,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
return -ENOMEM;
}
drv_data->quirks = id->driver_data;
-
+
hid_set_drvdata(hdev, (void *)drv_data);
if (drv_data->quirks & LG_NOGET)
@@ -380,7 +382,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
/* Setup wireless link with Logitech Wii wheel */
- if(hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
+ 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);
@@ -416,7 +418,7 @@ err_free:
static void lg_remove(struct hid_device *hdev)
{
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
if (drv_data->quirks & LG_FF4)
lg4ff_deinit(hdev);
@@ -476,7 +478,7 @@ static const struct hid_device_id lg_devices[] = {
.driver_data = LG_NOGET | LG_FF4 },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
.driver_data = LG_FF4 },
- { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
+ { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
.driver_data = LG_FF },
{ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
.driver_data = LG_FF2 },
diff --git a/drivers/hid/hid-lg.h b/drivers/hid/hid-lg.h
index d64cf8d2751e..142ce3f5f055 100644
--- a/drivers/hid/hid-lg.h
+++ b/drivers/hid/hid-lg.h
@@ -25,9 +25,13 @@ static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
#endif
#ifdef CONFIG_LOGIWHEELS_FF
+int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
int lg4ff_init(struct hid_device *hdev);
int lg4ff_deinit(struct hid_device *hdev);
#else
+static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; }
static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
#endif
diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
index f3390ee6105c..d7947c701f30 100644
--- a/drivers/hid/hid-lg4ff.c
+++ b/drivers/hid/hid-lg4ff.c
@@ -43,6 +43,11 @@
#define G27_REV_MAJ 0x12
#define G27_REV_MIN 0x38
+#define DFP_X_MIN 0
+#define DFP_X_MAX 16383
+#define DFP_PEDAL_MIN 0
+#define DFP_PEDAL_MAX 255
+
#define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
@@ -53,6 +58,7 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at
static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);
struct lg4ff_device_entry {
+ __u32 product_id;
__u16 range;
__u16 min_range;
__u16 max_range;
@@ -129,26 +135,77 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = {
{G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */
};
+/* Recalculates X axis value accordingly to currently selected range */
+static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
+{
+ __u16 max_range;
+ __s32 new_value;
+
+ if (range == 900)
+ return value;
+ else if (range == 200)
+ return value;
+ else if (range < 200)
+ max_range = 200;
+ else
+ max_range = 900;
+
+ new_value = 8192 + mult_frac(value - 8192, max_range, range);
+ if (new_value < 0)
+ return 0;
+ else if (new_value > 16383)
+ return 16383;
+ else
+ return new_value;
+}
+
+int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
+ struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data)
+{
+ struct lg4ff_device_entry *entry = drv_data->device_props;
+ __s32 new_value = 0;
+
+ if (!entry) {
+ hid_err(hid, "Device properties not found");
+ return 0;
+ }
+
+ switch (entry->product_id) {
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
+ switch (usage->code) {
+ case ABS_X:
+ new_value = lg4ff_adjust_dfp_x_axis(value, entry->range);
+ input_event(field->hidinput->input, usage->type, usage->code, new_value);
+ return 1;
+ default:
+ return 0;
+ }
+ default:
+ return 0;
+ }
+}
+
static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
{
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ __s32 *value = report->field[0]->value;
int x;
-#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
+#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
switch (effect->type) {
case FF_CONSTANT:
x = effect->u.ramp.start_level + 0x80; /* 0x80 is no force */
CLAMP(x);
- report->field[0]->value[0] = 0x11; /* Slot 1 */
- report->field[0]->value[1] = 0x08;
- report->field[0]->value[2] = x;
- report->field[0]->value[3] = 0x80;
- report->field[0]->value[4] = 0x00;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0x11; /* Slot 1 */
+ value[1] = 0x08;
+ value[2] = x;
+ value[3] = 0x80;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
break;
@@ -163,14 +220,15 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ __s32 *value = report->field[0]->value;
- report->field[0]->value[0] = 0xfe;
- report->field[0]->value[1] = 0x0d;
- report->field[0]->value[2] = magnitude >> 13;
- report->field[0]->value[3] = magnitude >> 13;
- report->field[0]->value[4] = magnitude >> 8;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0xfe;
+ value[1] = 0x0d;
+ value[2] = magnitude >> 13;
+ value[3] = magnitude >> 13;
+ value[4] = magnitude >> 8;
+ value[5] = 0x00;
+ value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
@@ -181,16 +239,16 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
struct hid_device *hid = input_get_drvdata(dev);
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ __s32 *value = report->field[0]->value;
magnitude = magnitude * 90 / 65535;
-
- report->field[0]->value[0] = 0xfe;
- report->field[0]->value[1] = 0x03;
- report->field[0]->value[2] = magnitude >> 14;
- report->field[0]->value[3] = magnitude >> 14;
- report->field[0]->value[4] = magnitude;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0xfe;
+ value[1] = 0x03;
+ value[2] = magnitude >> 14;
+ value[3] = magnitude >> 14;
+ value[4] = magnitude;
+ value[5] = 0x00;
+ value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
@@ -200,15 +258,17 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
{
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ __s32 *value = report->field[0]->value;
+
dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
- report->field[0]->value[0] = 0xf8;
- report->field[0]->value[1] = 0x81;
- report->field[0]->value[2] = range & 0x00ff;
- report->field[0]->value[3] = (range & 0xff00) >> 8;
- report->field[0]->value[4] = 0x00;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0xf8;
+ value[1] = 0x81;
+ value[2] = range & 0x00ff;
+ value[3] = (range & 0xff00) >> 8;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
@@ -219,16 +279,18 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
int start_left, start_right, full_range;
+ __s32 *value = report->field[0]->value;
+
dbg_hid("Driving Force Pro: setting range to %u\n", range);
/* Prepare "coarse" limit command */
- report->field[0]->value[0] = 0xf8;
- report->field[0]->value[1] = 0x00; /* Set later */
- report->field[0]->value[2] = 0x00;
- report->field[0]->value[3] = 0x00;
- report->field[0]->value[4] = 0x00;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0xf8;
+ value[1] = 0x00; /* Set later */
+ value[2] = 0x00;
+ value[3] = 0x00;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
if (range > 200) {
report->field[0]->value[1] = 0x03;
@@ -240,13 +302,13 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
usbhid_submit_report(hid, report, USB_DIR_OUT);
/* Prepare "fine" limit command */
- report->field[0]->value[0] = 0x81;
- report->field[0]->value[1] = 0x0b;
- report->field[0]->value[2] = 0x00;
- report->field[0]->value[3] = 0x00;
- report->field[0]->value[4] = 0x00;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ value[0] = 0x81;
+ value[1] = 0x0b;
+ value[2] = 0x00;
+ value[3] = 0x00;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
if (range == 200 || range == 900) { /* Do not apply any fine limit */
usbhid_submit_report(hid, report, USB_DIR_OUT);
@@ -257,11 +319,11 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
start_left = (((full_range - range + 1) * 2047) / full_range);
start_right = 0xfff - start_left;
- report->field[0]->value[2] = start_left >> 4;
- report->field[0]->value[3] = start_right >> 4;
- report->field[0]->value[4] = 0xff;
- report->field[0]->value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
- report->field[0]->value[6] = 0xff;
+ value[2] = start_left >> 4;
+ value[3] = start_right >> 4;
+ value[4] = 0xff;
+ value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
+ value[6] = 0xff;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
@@ -344,14 +406,15 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
{
struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-
- report->field[0]->value[0] = 0xf8;
- report->field[0]->value[1] = 0x12;
- report->field[0]->value[2] = leds;
- report->field[0]->value[3] = 0x00;
- report->field[0]->value[4] = 0x00;
- report->field[0]->value[5] = 0x00;
- report->field[0]->value[6] = 0x00;
+ __s32 *value = report->field[0]->value;
+
+ value[0] = 0xf8;
+ value[1] = 0x12;
+ value[2] = leds;
+ value[3] = 0x00;
+ value[4] = 0x00;
+ value[5] = 0x00;
+ value[6] = 0x00;
usbhid_submit_report(hid, report, USB_DIR_OUT);
}
@@ -360,7 +423,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
{
struct device *dev = led_cdev->dev->parent;
struct hid_device *hid = container_of(dev, struct hid_device, dev);
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hid);
struct lg4ff_device_entry *entry;
int i, state = 0;
@@ -395,7 +458,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
{
struct device *dev = led_cdev->dev->parent;
struct hid_device *hid = container_of(dev, struct hid_device, dev);
- struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
+ struct lg_drv_data *drv_data = hid_get_drvdata(hid);
struct lg4ff_device_entry *entry;
int i, value = 0;
@@ -501,7 +564,7 @@ int lg4ff_init(struct hid_device *hid)
/* Check if autocentering is available and
* set the centering force to zero by default */
if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
- if(rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
+ if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
else
dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
@@ -524,6 +587,7 @@ int lg4ff_init(struct hid_device *hid)
}
drv_data->device_props = entry;
+ entry->product_id = lg4ff_devices[i].product_id;
entry->min_range = lg4ff_devices[i].min_range;
entry->max_range = lg4ff_devices[i].max_range;
entry->set_range = lg4ff_devices[i].set_range;
@@ -534,6 +598,18 @@ int lg4ff_init(struct hid_device *hid)
return error;
dbg_hid("sysfs interface created\n");
+ /* Set default axes parameters */
+ switch (lg4ff_devices[i].product_id) {
+ case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
+ dbg_hid("Setting axes parameters for Driving Force Pro\n");
+ input_set_abs_params(dev, ABS_X, DFP_X_MIN, DFP_X_MAX, 0, 0);
+ input_set_abs_params(dev, ABS_Y, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0);
+ input_set_abs_params(dev, ABS_RZ, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0);
+ break;
+ default:
+ break;
+ }
+
/* Set the maximum range to start with */
entry->range = entry->max_range;
if (entry->set_range != NULL)
@@ -594,6 +670,8 @@ out:
return 0;
}
+
+
int lg4ff_deinit(struct hid_device *hid)
{
struct lg4ff_device_entry *entry;
diff --git a/drivers/hid/hid-logitech-dj.c b/drivers/hid/hid-logitech-dj.c
index 4d524b5f52f5..9500f2f3f8fe 100644
--- a/drivers/hid/hid-logitech-dj.c
+++ b/drivers/hid/hid-logitech-dj.c
@@ -193,6 +193,7 @@ 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,
struct dj_report *dj_report)
@@ -233,6 +234,7 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
if (dj_report->report_params[DEVICE_PAIRED_PARAM_SPFUNCTION] &
SPFUNCTION_DEVICE_LIST_EMPTY) {
dbg_hid("%s: device list is empty\n", __func__);
+ djrcv_dev->querying_devices = false;
return;
}
@@ -243,6 +245,12 @@ static void logi_dj_recv_add_djhid_device(struct dj_receiver_dev *djrcv_dev,
return;
}
+ if (djrcv_dev->paired_dj_devices[dj_report->device_index]) {
+ /* The device is already known. No need to reallocate it. */
+ dbg_hid("%s: device is already known\n", __func__);
+ return;
+ }
+
dj_hiddev = hid_allocate_device();
if (IS_ERR(dj_hiddev)) {
dev_err(&djrcv_hdev->dev, "%s: hid_allocate_device failed\n",
@@ -306,6 +314,7 @@ static void delayedwork_callback(struct work_struct *work)
struct dj_report dj_report;
unsigned long flags;
int count;
+ int retval;
dbg_hid("%s\n", __func__);
@@ -338,6 +347,25 @@ static void delayedwork_callback(struct work_struct *work)
logi_dj_recv_destroy_djhid_device(djrcv_dev, &dj_report);
break;
default:
+ /* A normal report (i. e. not belonging to a pair/unpair notification)
+ * arriving here, means that the report arrived but we did not have a
+ * paired dj_device associated to the report's device_index, this
+ * means that the original "device paired" notification corresponding
+ * to this dj_device never arrived to this driver. The reason is that
+ * hid-core discards all packets coming from a device while probe() is
+ * executing. */
+ if (!djrcv_dev->paired_dj_devices[dj_report.device_index]) {
+ /* ok, we don't know the device, just re-ask the
+ * receiver for the list of connected devices. */
+ retval = logi_dj_recv_query_paired_devices(djrcv_dev);
+ if (!retval) {
+ /* everything went fine, so just leave */
+ break;
+ }
+ dev_err(&djrcv_dev->hdev->dev,
+ "%s:logi_dj_recv_query_paired_devices "
+ "error:%d\n", __func__, retval);
+ }
dbg_hid("%s: unexpected report type\n", __func__);
}
}
@@ -368,6 +396,12 @@ static void logi_dj_recv_forward_null_report(struct dj_receiver_dev *djrcv_dev,
if (!djdev) {
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d\n", dj_report->device_index);
+ kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+
+ if (schedule_work(&djrcv_dev->work) == 0) {
+ dbg_hid("%s: did not schedule the work item, was already "
+ "queued\n", __func__);
+ }
return;
}
@@ -398,6 +432,12 @@ static void logi_dj_recv_forward_report(struct dj_receiver_dev *djrcv_dev,
if (dj_device == NULL) {
dbg_hid("djrcv_dev->paired_dj_devices[dj_report->device_index]"
" is NULL, index %d\n", dj_report->device_index);
+ kfifo_in(&djrcv_dev->notif_fifo, dj_report, sizeof(struct dj_report));
+
+ if (schedule_work(&djrcv_dev->work) == 0) {
+ dbg_hid("%s: did not schedule the work item, was already "
+ "queued\n", __func__);
+ }
return;
}
@@ -439,6 +479,10 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
struct dj_report *dj_report;
int retval;
+ /* no need to protect djrcv_dev->querying_devices */
+ if (djrcv_dev->querying_devices)
+ return 0;
+
dj_report = kzalloc(sizeof(struct dj_report), GFP_KERNEL);
if (!dj_report)
return -ENOMEM;
@@ -450,6 +494,7 @@ static int logi_dj_recv_query_paired_devices(struct dj_receiver_dev *djrcv_dev)
return retval;
}
+
static int logi_dj_recv_switch_to_dj_mode(struct dj_receiver_dev *djrcv_dev,
unsigned timeout)
{
diff --git a/drivers/hid/hid-logitech-dj.h b/drivers/hid/hid-logitech-dj.h
index fd28a5e0ca3b..4a4000340ce1 100644
--- a/drivers/hid/hid-logitech-dj.h
+++ b/drivers/hid/hid-logitech-dj.h
@@ -101,6 +101,7 @@ struct dj_receiver_dev {
struct work_struct work;
struct kfifo notif_fifo;
spinlock_t lock;
+ bool querying_devices;
};
struct dj_device {
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 73647266daad..25ddf3e3aec6 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -392,7 +392,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
__set_bit(EV_ABS, input->evbit);
- error = input_mt_init_slots(input, 16);
+ error = input_mt_init_slots(input, 16, 0);
if (error)
return error;
input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c
index e5c699b6c6f3..3acdcfcc17df 100644
--- a/drivers/hid/hid-microsoft.c
+++ b/drivers/hid/hid-microsoft.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c
index dedf757781ae..cd3643e06fa6 100644
--- a/drivers/hid/hid-monterey.c
+++ b/drivers/hid/hid-monterey.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 59c8b5c1d2de..3eb02b94fc87 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -51,12 +51,12 @@ MODULE_LICENSE("GPL");
#define MT_QUIRK_VALID_IS_INRANGE (1 << 5)
#define MT_QUIRK_VALID_IS_CONFIDENCE (1 << 6)
#define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE (1 << 8)
+#define MT_QUIRK_NO_AREA (1 << 9)
struct mt_slot {
__s32 x, y, p, w, h;
__s32 contactid; /* the device ContactID assigned to this slot */
bool touch_state; /* is the touch valid? */
- bool seen_in_this_frame;/* has this slot been updated */
};
struct mt_class {
@@ -92,8 +92,9 @@ struct mt_device {
__u8 touches_by_report; /* how many touches are present in one report:
* 1 means we should use a serial protocol
* > 1 means hybrid (multitouch) protocol */
+ bool serial_maybe; /* need to check for serial protocol */
bool curvalid; /* is the current contact valid? */
- struct mt_slot *slots;
+ unsigned mt_flags; /* flags to pass to input-mt */
};
/* classes of device behavior */
@@ -115,6 +116,9 @@ struct mt_device {
#define MT_CLS_EGALAX_SERIAL 0x0104
#define MT_CLS_TOPSEED 0x0105
#define MT_CLS_PANASONIC 0x0106
+#define MT_CLS_FLATFROG 0x0107
+#define MT_CLS_GENERALTOUCH_TWOFINGERS 0x0108
+#define MT_CLS_GENERALTOUCH_PWT_TENFINGERS 0x0109
#define MT_DEFAULT_MAXCONTACT 10
@@ -134,25 +138,6 @@ static int cypress_compute_slot(struct mt_device *td)
return -1;
}
-static int find_slot_from_contactid(struct mt_device *td)
-{
- int i;
- for (i = 0; i < td->maxcontacts; ++i) {
- if (td->slots[i].contactid == td->curdata.contactid &&
- td->slots[i].touch_state)
- return i;
- }
- for (i = 0; i < td->maxcontacts; ++i) {
- if (!td->slots[i].seen_in_this_frame &&
- !td->slots[i].touch_state)
- return i;
- }
- /* should not occurs. If this happens that means
- * that the device sent more touches that it says
- * in the report descriptor. It is ignored then. */
- return -1;
-}
-
static struct mt_class mt_classes[] = {
{ .name = MT_CLS_DEFAULT,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP },
@@ -190,7 +175,9 @@ static struct mt_class mt_classes[] = {
MT_QUIRK_SLOT_IS_CONTACTID,
.sn_move = 2048,
.sn_width = 128,
- .sn_height = 128 },
+ .sn_height = 128,
+ .maxcontacts = 60,
+ },
{ .name = MT_CLS_CYPRESS,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
MT_QUIRK_CYPRESS,
@@ -215,7 +202,24 @@ static struct mt_class mt_classes[] = {
{ .name = MT_CLS_PANASONIC,
.quirks = MT_QUIRK_NOT_SEEN_MEANS_UP,
.maxcontacts = 4 },
+ { .name = MT_CLS_GENERALTOUCH_TWOFINGERS,
+ .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
+ MT_QUIRK_VALID_IS_INRANGE |
+ MT_QUIRK_SLOT_IS_CONTACTNUMBER,
+ .maxcontacts = 2
+ },
+ { .name = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
+ .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
+ MT_QUIRK_SLOT_IS_CONTACTNUMBER,
+ .maxcontacts = 10
+ },
+ { .name = MT_CLS_FLATFROG,
+ .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
+ MT_QUIRK_NO_AREA,
+ .sn_move = 2048,
+ .maxcontacts = 40,
+ },
{ }
};
@@ -319,24 +323,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
* 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)
- set_bit(INPUT_PROP_DIRECT, hi->input->propbit);
+ td->mt_flags |= INPUT_MT_DIRECT;
else if (field->application != HID_DG_TOUCHPAD)
return 0;
- /* In case of an indirect device (touchpad), we need to add
- * specific BTN_TOOL_* to be handled by the synaptics xorg
- * driver.
- * We also consider that touchscreens providing buttons are touchpads.
+ /*
+ * Model touchscreens providing buttons as touchpads.
*/
if (field->application == HID_DG_TOUCHPAD ||
- (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON ||
- cls->is_indirect) {
- set_bit(INPUT_PROP_POINTER, hi->input->propbit);
- set_bit(BTN_TOOL_FINGER, hi->input->keybit);
- set_bit(BTN_TOOL_DOUBLETAP, hi->input->keybit);
- set_bit(BTN_TOOL_TRIPLETAP, hi->input->keybit);
- set_bit(BTN_TOOL_QUADTAP, hi->input->keybit);
- }
+ (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
+ td->mt_flags |= INPUT_MT_POINTER;
/* eGalax devices provide a Digitizer.Stylus input which overrides
* the correct Digitizers.Finger X/Y ranges.
@@ -353,8 +349,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
EV_ABS, ABS_MT_POSITION_X);
set_abs(hi->input, ABS_MT_POSITION_X, field,
cls->sn_move);
- /* touchscreen emulation */
- set_abs(hi->input, ABS_X, field, cls->sn_move);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
@@ -363,8 +357,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
EV_ABS, ABS_MT_POSITION_Y);
set_abs(hi->input, ABS_MT_POSITION_Y, field,
cls->sn_move);
- /* touchscreen emulation */
- set_abs(hi->input, ABS_Y, field, cls->sn_move);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
@@ -388,9 +380,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
td->last_field_index = field->index;
return 1;
case HID_DG_CONTACTID:
- if (!td->maxcontacts)
- td->maxcontacts = MT_DEFAULT_MAXCONTACT;
- input_mt_init_slots(hi->input, td->maxcontacts);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
td->touches_by_report++;
@@ -398,18 +387,21 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
case HID_DG_WIDTH:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TOUCH_MAJOR);
- set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
- cls->sn_width);
+ if (!(cls->quirks & MT_QUIRK_NO_AREA))
+ set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
+ cls->sn_width);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
case HID_DG_HEIGHT:
hid_map_usage(hi, usage, bit, max,
EV_ABS, ABS_MT_TOUCH_MINOR);
- set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
- cls->sn_height);
- input_set_abs_params(hi->input,
+ if (!(cls->quirks & MT_QUIRK_NO_AREA)) {
+ set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
+ cls->sn_height);
+ input_set_abs_params(hi->input,
ABS_MT_ORIENTATION, 0, 1, 0, 0);
+ }
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
@@ -418,9 +410,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
EV_ABS, ABS_MT_PRESSURE);
set_abs(hi->input, ABS_MT_PRESSURE, field,
cls->sn_pressure);
- /* touchscreen emulation */
- set_abs(hi->input, ABS_PRESSURE, field,
- cls->sn_pressure);
mt_store_field(usage, td, hi);
td->last_field_index = field->index;
return 1;
@@ -464,7 +453,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
return -1;
}
-static int mt_compute_slot(struct mt_device *td)
+static int mt_compute_slot(struct mt_device *td, struct input_dev *input)
{
__s32 quirks = td->mtclass.quirks;
@@ -480,42 +469,23 @@ static int mt_compute_slot(struct mt_device *td)
if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE)
return td->curdata.contactid - 1;
- return find_slot_from_contactid(td);
+ return input_mt_get_slot_by_key(input, td->curdata.contactid);
}
/*
* this function is called when a whole contact has been processed,
* so that it can assign it to a slot and store the data there
*/
-static void mt_complete_slot(struct mt_device *td)
+static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
{
- td->curdata.seen_in_this_frame = true;
if (td->curvalid) {
- int slotnum = mt_compute_slot(td);
-
- if (slotnum >= 0 && slotnum < td->maxcontacts)
- td->slots[slotnum] = td->curdata;
- }
- td->num_received++;
-}
+ int slotnum = mt_compute_slot(td, input);
+ struct mt_slot *s = &td->curdata;
+ if (slotnum < 0 || slotnum >= td->maxcontacts)
+ return;
-/*
- * this function is called when a whole packet has been received and processed,
- * so that it can decide what to send to the input layer.
- */
-static void mt_emit_event(struct mt_device *td, struct input_dev *input)
-{
- int i;
-
- for (i = 0; i < td->maxcontacts; ++i) {
- struct mt_slot *s = &(td->slots[i]);
- if ((td->mtclass.quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) &&
- !s->seen_in_this_frame) {
- s->touch_state = false;
- }
-
- input_mt_slot(input, i);
+ input_mt_slot(input, slotnum);
input_mt_report_slot_state(input, MT_TOOL_FINGER,
s->touch_state);
if (s->touch_state) {
@@ -532,24 +502,29 @@ static void mt_emit_event(struct mt_device *td, struct input_dev *input)
input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
}
- s->seen_in_this_frame = false;
-
}
- input_mt_report_pointer_emulation(input, true);
+ td->num_received++;
+}
+
+/*
+ * this function is called when a whole packet has been received and processed,
+ * so that it can decide what to send to the input layer.
+ */
+static void mt_sync_frame(struct mt_device *td, struct input_dev *input)
+{
+ input_mt_sync_frame(input);
input_sync(input);
td->num_received = 0;
}
-
-
static int mt_event(struct hid_device *hid, struct hid_field *field,
struct hid_usage *usage, __s32 value)
{
struct mt_device *td = hid_get_drvdata(hid);
__s32 quirks = td->mtclass.quirks;
- if (hid->claimed & HID_CLAIMED_INPUT && td->slots) {
+ if (hid->claimed & HID_CLAIMED_INPUT) {
switch (usage->hid) {
case HID_DG_INRANGE:
if (quirks & MT_QUIRK_ALWAYS_VALID)
@@ -602,11 +577,11 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
}
if (usage->hid == td->last_slot_field)
- mt_complete_slot(td);
+ mt_complete_slot(td, field->hidinput->input);
if (field->index == td->last_field_index
&& td->num_received >= td->num_expected)
- mt_emit_event(td, field->hidinput->input);
+ mt_sync_frame(td, field->hidinput->input);
}
@@ -685,18 +660,45 @@ static void mt_post_parse(struct mt_device *td)
}
}
+static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
+
+{
+ struct mt_device *td = hid_get_drvdata(hdev);
+ struct mt_class *cls = &td->mtclass;
+ struct input_dev *input = hi->input;
+
+ /* Only initialize slots for MT input devices */
+ if (!test_bit(ABS_MT_POSITION_X, input->absbit))
+ return;
+
+ if (!td->maxcontacts)
+ td->maxcontacts = MT_DEFAULT_MAXCONTACT;
+
+ mt_post_parse(td);
+ if (td->serial_maybe)
+ mt_post_parse_default_settings(td);
+
+ if (cls->is_indirect)
+ td->mt_flags |= INPUT_MT_POINTER;
+
+ if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
+ td->mt_flags |= INPUT_MT_DROP_UNUSED;
+
+ input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
+
+ td->mt_flags = 0;
+}
+
static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
int ret, i;
struct mt_device *td;
struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
- if (id) {
- for (i = 0; mt_classes[i].name ; i++) {
- if (id->driver_data == mt_classes[i].name) {
- mtclass = &(mt_classes[i]);
- break;
- }
+ for (i = 0; mt_classes[i].name ; i++) {
+ if (id->driver_data == mt_classes[i].name) {
+ mtclass = &(mt_classes[i]);
+ break;
}
}
@@ -722,6 +724,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto fail;
}
+ if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
+ td->serial_maybe = true;
+
ret = hid_parse(hdev);
if (ret != 0)
goto fail;
@@ -730,20 +735,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret)
goto fail;
- mt_post_parse(td);
-
- if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
- mt_post_parse_default_settings(td);
-
- td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot),
- GFP_KERNEL);
- if (!td->slots) {
- dev_err(&hdev->dev, "cannot allocate multitouch slots\n");
- hid_hw_stop(hdev);
- ret = -ENOMEM;
- goto fail;
- }
-
ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
mt_set_maxcontacts(hdev);
@@ -767,6 +758,32 @@ static int mt_reset_resume(struct hid_device *hdev)
mt_set_input_mode(hdev);
return 0;
}
+
+static int mt_resume(struct hid_device *hdev)
+{
+ struct usb_interface *intf;
+ struct usb_host_interface *interface;
+ struct usb_device *dev;
+
+ if (hdev->bus != BUS_USB)
+ return 0;
+
+ intf = to_usb_interface(hdev->dev.parent);
+ interface = intf->cur_altsetting;
+ dev = hid_to_usb_dev(hdev);
+
+ /* Some Elan legacy devices require SET_IDLE to be set on resume.
+ * It should be safe to send it to other devices too.
+ * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */
+
+ usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ HID_REQ_SET_IDLE,
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ 0, interface->desc.bInterfaceNumber,
+ NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+ return 0;
+}
#endif
static void mt_remove(struct hid_device *hdev)
@@ -774,7 +791,6 @@ static void mt_remove(struct hid_device *hdev)
struct mt_device *td = hid_get_drvdata(hdev);
sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
hid_hw_stop(hdev);
- kfree(td->slots);
kfree(td);
hid_set_drvdata(hdev, NULL);
}
@@ -885,17 +901,37 @@ static const struct hid_device_id mt_devices[] = {
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349) },
{ .driver_data = MT_CLS_EGALAX_SERIAL,
MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
+ USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7) },
+ { .driver_data = MT_CLS_EGALAX_SERIAL,
+ MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
+ { .driver_data = MT_CLS_EGALAX,
+ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+ USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) },
+ { .driver_data = MT_CLS_EGALAX,
+ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+ USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) },
+ { .driver_data = MT_CLS_EGALAX,
+ HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+ USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) },
/* Elo TouchSystems IntelliTouch Plus panel */
{ .driver_data = MT_CLS_DUAL_NSMU_CONTACTID,
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,
+ USB_DEVICE_ID_MULTITOUCH_3200) },
+
/* GeneralTouch panel */
- { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
+ { .driver_data = MT_CLS_GENERALTOUCH_TWOFINGERS,
MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) },
+ { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
+ MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
+ USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS) },
/* Gametel game controller */
{ .driver_data = MT_CLS_DEFAULT,
@@ -1087,11 +1123,13 @@ static struct hid_driver mt_driver = {
.remove = mt_remove,
.input_mapping = mt_input_mapping,
.input_mapped = mt_input_mapped,
+ .input_configured = mt_input_configured,
.feature_mapping = mt_feature_mapping,
.usage_table = mt_grabbed_usages,
.event = mt_event,
#ifdef CONFIG_PM
.reset_resume = mt_reset_resume,
+ .resume = mt_resume,
#endif
};
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index 9fae2ebdd758..86a969f63292 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -882,10 +882,10 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
nd->activate_slack = activate_slack;
nd->act_state = activate_slack;
nd->deactivate_slack = -deactivate_slack;
- nd->sensor_logical_width = 0;
- nd->sensor_logical_height = 0;
- nd->sensor_physical_width = 0;
- nd->sensor_physical_height = 0;
+ nd->sensor_logical_width = 1;
+ nd->sensor_logical_height = 1;
+ nd->sensor_physical_width = 1;
+ nd->sensor_physical_height = 1;
hid_set_drvdata(hdev, nd);
diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c
index f1ea3ff8a98d..4c521de4e7e6 100644
--- a/drivers/hid/hid-petalynx.c
+++ b/drivers/hid/hid-petalynx.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c
deleted file mode 100644
index 27c8ebdfad01..000000000000
--- a/drivers/hid/hid-picolcd.c
+++ /dev/null
@@ -1,2748 +0,0 @@
-/***************************************************************************
- * Copyright (C) 2010 by Bruno Prémont <bonbons@linux-vserver.org> *
- * *
- * Based on Logitech G13 driver (v0.4) *
- * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
- * *
- * This program is free software: 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 driver is distributed in the hope that it will be useful, but *
- * WITHOUT 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 software. If not see <http://www.gnu.org/licenses/>. *
- ***************************************************************************/
-
-#include <linux/hid.h>
-#include <linux/hid-debug.h>
-#include <linux/input.h>
-#include "hid-ids.h"
-#include "usbhid/usbhid.h"
-#include <linux/usb.h>
-
-#include <linux/fb.h>
-#include <linux/vmalloc.h>
-#include <linux/backlight.h>
-#include <linux/lcd.h>
-
-#include <linux/leds.h>
-
-#include <linux/seq_file.h>
-#include <linux/debugfs.h>
-
-#include <linux/completion.h>
-#include <linux/uaccess.h>
-#include <linux/module.h>
-
-#define PICOLCD_NAME "PicoLCD (graphic)"
-
-/* Report numbers */
-#define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */
-#define ERR_SUCCESS 0x00
-#define ERR_PARAMETER_MISSING 0x01
-#define ERR_DATA_MISSING 0x02
-#define ERR_BLOCK_READ_ONLY 0x03
-#define ERR_BLOCK_NOT_ERASABLE 0x04
-#define ERR_BLOCK_TOO_BIG 0x05
-#define ERR_SECTION_OVERFLOW 0x06
-#define ERR_INVALID_CMD_LEN 0x07
-#define ERR_INVALID_DATA_LEN 0x08
-#define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */
-#define REPORT_IR_DATA 0x21 /* LCD: IN[63] */
-#define REPORT_EE_DATA 0x32 /* LCD: IN[63] */
-#define REPORT_MEMORY 0x41 /* LCD: IN[63] */
-#define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */
-#define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */
-#define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */
-#define REPORT_RESET 0x93 /* LCD: OUT[2] */
-#define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */
-#define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */
-#define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */
-#define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */
-#define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */
-#define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */
-#define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */
-#define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */
-#define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */
-#define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */
-#define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */
-#define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */
-#define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */
-#define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */
-#define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */
-#define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */
-#define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */
-#define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */
-
-#ifdef CONFIG_HID_PICOLCD_FB
-/* Framebuffer
- *
- * The PicoLCD use a Topway LCD module of 256x64 pixel
- * This display area is tiled over 4 controllers with 8 tiles
- * each. Each tile has 8x64 pixel, each data byte representing
- * a 1-bit wide vertical line of the tile.
- *
- * The display can be updated at a tile granularity.
- *
- * Chip 1 Chip 2 Chip 3 Chip 4
- * +----------------+----------------+----------------+----------------+
- * | Tile 1 | Tile 1 | Tile 1 | Tile 1 |
- * +----------------+----------------+----------------+----------------+
- * | Tile 2 | Tile 2 | Tile 2 | Tile 2 |
- * +----------------+----------------+----------------+----------------+
- * ...
- * +----------------+----------------+----------------+----------------+
- * | Tile 8 | Tile 8 | Tile 8 | Tile 8 |
- * +----------------+----------------+----------------+----------------+
- */
-#define PICOLCDFB_NAME "picolcdfb"
-#define PICOLCDFB_WIDTH (256)
-#define PICOLCDFB_HEIGHT (64)
-#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
-
-#define PICOLCDFB_UPDATE_RATE_LIMIT 10
-#define PICOLCDFB_UPDATE_RATE_DEFAULT 2
-
-/* Framebuffer visual structures */
-static const struct fb_fix_screeninfo picolcdfb_fix = {
- .id = PICOLCDFB_NAME,
- .type = FB_TYPE_PACKED_PIXELS,
- .visual = FB_VISUAL_MONO01,
- .xpanstep = 0,
- .ypanstep = 0,
- .ywrapstep = 0,
- .line_length = PICOLCDFB_WIDTH / 8,
- .accel = FB_ACCEL_NONE,
-};
-
-static const struct fb_var_screeninfo picolcdfb_var = {
- .xres = PICOLCDFB_WIDTH,
- .yres = PICOLCDFB_HEIGHT,
- .xres_virtual = PICOLCDFB_WIDTH,
- .yres_virtual = PICOLCDFB_HEIGHT,
- .width = 103,
- .height = 26,
- .bits_per_pixel = 1,
- .grayscale = 1,
- .red = {
- .offset = 0,
- .length = 1,
- .msb_right = 0,
- },
- .green = {
- .offset = 0,
- .length = 1,
- .msb_right = 0,
- },
- .blue = {
- .offset = 0,
- .length = 1,
- .msb_right = 0,
- },
- .transp = {
- .offset = 0,
- .length = 0,
- .msb_right = 0,
- },
-};
-#endif /* CONFIG_HID_PICOLCD_FB */
-
-/* Input device
- *
- * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
- * and header for 4x4 key matrix. The built-in keys are part of the matrix.
- */
-static const unsigned short def_keymap[] = {
- KEY_RESERVED, /* none */
- KEY_BACK, /* col 4 + row 1 */
- KEY_HOMEPAGE, /* col 3 + row 1 */
- KEY_RESERVED, /* col 2 + row 1 */
- KEY_RESERVED, /* col 1 + row 1 */
- KEY_SCROLLUP, /* col 4 + row 2 */
- KEY_OK, /* col 3 + row 2 */
- KEY_SCROLLDOWN, /* col 2 + row 2 */
- KEY_RESERVED, /* col 1 + row 2 */
- KEY_RESERVED, /* col 4 + row 3 */
- KEY_RESERVED, /* col 3 + row 3 */
- KEY_RESERVED, /* col 2 + row 3 */
- KEY_RESERVED, /* col 1 + row 3 */
- KEY_RESERVED, /* col 4 + row 4 */
- KEY_RESERVED, /* col 3 + row 4 */
- KEY_RESERVED, /* col 2 + row 4 */
- KEY_RESERVED, /* col 1 + row 4 */
-};
-#define PICOLCD_KEYS ARRAY_SIZE(def_keymap)
-
-/* Description of in-progress IO operation, used for operations
- * that trigger response from device */
-struct picolcd_pending {
- struct hid_report *out_report;
- struct hid_report *in_report;
- struct completion ready;
- int raw_size;
- u8 raw_data[64];
-};
-
-/* Per device data structure */
-struct picolcd_data {
- struct hid_device *hdev;
-#ifdef CONFIG_DEBUG_FS
- struct dentry *debug_reset;
- struct dentry *debug_eeprom;
- struct dentry *debug_flash;
- struct mutex mutex_flash;
- int addr_sz;
-#endif
- u8 version[2];
- unsigned short opmode_delay;
- /* input stuff */
- u8 pressed_keys[2];
- struct input_dev *input_keys;
- struct input_dev *input_cir;
- unsigned short keycode[PICOLCD_KEYS];
-
-#ifdef CONFIG_HID_PICOLCD_FB
- /* Framebuffer stuff */
- u8 fb_update_rate;
- u8 fb_bpp;
- u8 fb_force;
- u8 *fb_vbitmap; /* local copy of what was sent to PicoLCD */
- u8 *fb_bitmap; /* framebuffer */
- struct fb_info *fb_info;
- struct fb_deferred_io fb_defio;
-#endif /* CONFIG_HID_PICOLCD_FB */
-#ifdef CONFIG_HID_PICOLCD_LCD
- struct lcd_device *lcd;
- u8 lcd_contrast;
-#endif /* CONFIG_HID_PICOLCD_LCD */
-#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
- struct backlight_device *backlight;
- u8 lcd_brightness;
- u8 lcd_power;
-#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
-#ifdef CONFIG_HID_PICOLCD_LEDS
- /* LED stuff */
- u8 led_state;
- struct led_classdev *led[8];
-#endif /* CONFIG_HID_PICOLCD_LEDS */
-
- /* Housekeeping stuff */
- spinlock_t lock;
- struct mutex mutex;
- struct picolcd_pending *pending;
- int status;
-#define PICOLCD_BOOTLOADER 1
-#define PICOLCD_FAILED 2
-#define PICOLCD_READY_FB 4
-};
-
-
-/* Find a given report */
-#define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT)
-#define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT)
-
-static struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)
-{
- struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;
- struct hid_report *report = NULL;
-
- list_for_each_entry(report, feature_report_list, list) {
- if (report->id == id)
- return report;
- }
- hid_warn(hdev, "No report with id 0x%x found\n", id);
- return NULL;
-}
-
-#ifdef CONFIG_DEBUG_FS
-static void picolcd_debug_out_report(struct picolcd_data *data,
- struct hid_device *hdev, struct hid_report *report);
-#define usbhid_submit_report(a, b, c) \
- do { \
- picolcd_debug_out_report(hid_get_drvdata(a), a, b); \
- usbhid_submit_report(a, b, c); \
- } while (0)
-#endif
-
-/* Submit a report and wait for a reply from device - if device fades away
- * or does not respond in time, return NULL */
-static struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
- int report_id, const u8 *raw_data, int size)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- struct picolcd_pending *work;
- struct hid_report *report = picolcd_out_report(report_id, hdev);
- unsigned long flags;
- int i, j, k;
-
- if (!report || !data)
- return NULL;
- if (data->status & PICOLCD_FAILED)
- return NULL;
- work = kzalloc(sizeof(*work), GFP_KERNEL);
- if (!work)
- return NULL;
-
- init_completion(&work->ready);
- work->out_report = report;
- work->in_report = NULL;
- work->raw_size = 0;
-
- mutex_lock(&data->mutex);
- spin_lock_irqsave(&data->lock, flags);
- for (i = k = 0; i < report->maxfield; i++)
- for (j = 0; j < report->field[i]->report_count; j++) {
- hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
- k++;
- }
- data->pending = work;
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
- wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
- spin_lock_irqsave(&data->lock, flags);
- data->pending = NULL;
- spin_unlock_irqrestore(&data->lock, flags);
- mutex_unlock(&data->mutex);
- return work;
-}
-
-#ifdef CONFIG_HID_PICOLCD_FB
-/* Send a given tile to PicoLCD */
-static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- struct hid_report *report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, hdev);
- struct hid_report *report2 = picolcd_out_report(REPORT_LCD_DATA, hdev);
- unsigned long flags;
- u8 *tdata;
- int i;
-
- if (!report1 || report1->maxfield != 1 || !report2 || report2->maxfield != 1)
- return -ENODEV;
-
- spin_lock_irqsave(&data->lock, flags);
- hid_set_field(report1->field[0], 0, chip << 2);
- hid_set_field(report1->field[0], 1, 0x02);
- hid_set_field(report1->field[0], 2, 0x00);
- hid_set_field(report1->field[0], 3, 0x00);
- hid_set_field(report1->field[0], 4, 0xb8 | tile);
- hid_set_field(report1->field[0], 5, 0x00);
- hid_set_field(report1->field[0], 6, 0x00);
- hid_set_field(report1->field[0], 7, 0x40);
- hid_set_field(report1->field[0], 8, 0x00);
- hid_set_field(report1->field[0], 9, 0x00);
- hid_set_field(report1->field[0], 10, 32);
-
- hid_set_field(report2->field[0], 0, (chip << 2) | 0x01);
- hid_set_field(report2->field[0], 1, 0x00);
- hid_set_field(report2->field[0], 2, 0x00);
- hid_set_field(report2->field[0], 3, 32);
-
- tdata = data->fb_vbitmap + (tile * 4 + chip) * 64;
- for (i = 0; i < 64; i++)
- if (i < 32)
- hid_set_field(report1->field[0], 11 + i, tdata[i]);
- else
- hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
-
- usbhid_submit_report(data->hdev, report1, USB_DIR_OUT);
- usbhid_submit_report(data->hdev, report2, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
- return 0;
-}
-
-/* Translate a single tile*/
-static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
- int chip, int tile)
-{
- int i, b, changed = 0;
- u8 tdata[64];
- u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
-
- if (bpp == 1) {
- for (b = 7; b >= 0; b--) {
- const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
- for (i = 0; i < 64; i++) {
- tdata[i] <<= 1;
- tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
- }
- }
- } else if (bpp == 8) {
- for (b = 7; b >= 0; b--) {
- const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
- for (i = 0; i < 64; i++) {
- tdata[i] <<= 1;
- tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
- }
- }
- } else {
- /* Oops, we should never get here! */
- WARN_ON(1);
- return 0;
- }
-
- for (i = 0; i < 64; i++)
- if (tdata[i] != vdata[i]) {
- changed = 1;
- vdata[i] = tdata[i];
- }
- return changed;
-}
-
-/* Reconfigure LCD display */
-static int picolcd_fb_reset(struct picolcd_data *data, int clear)
-{
- struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
- int i, j;
- unsigned long flags;
- static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
-
- if (!report || report->maxfield != 1)
- return -ENODEV;
-
- spin_lock_irqsave(&data->lock, flags);
- for (i = 0; i < 4; i++) {
- for (j = 0; j < report->field[0]->maxusage; j++)
- if (j == 0)
- hid_set_field(report->field[0], j, i << 2);
- else if (j < sizeof(mapcmd))
- hid_set_field(report->field[0], j, mapcmd[j]);
- else
- hid_set_field(report->field[0], j, 0);
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- }
-
- data->status |= PICOLCD_READY_FB;
- spin_unlock_irqrestore(&data->lock, flags);
-
- if (data->fb_bitmap) {
- if (clear) {
- memset(data->fb_vbitmap, 0, PICOLCDFB_SIZE);
- memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp);
- }
- data->fb_force = 1;
- }
-
- /* schedule first output of framebuffer */
- if (data->fb_info)
- schedule_delayed_work(&data->fb_info->deferred_work, 0);
-
- return 0;
-}
-
-/* Update fb_vbitmap from the screen_base and send changed tiles to device */
-static void picolcd_fb_update(struct picolcd_data *data)
-{
- int chip, tile, n;
- unsigned long flags;
-
- if (!data)
- return;
-
- spin_lock_irqsave(&data->lock, flags);
- if (!(data->status & PICOLCD_READY_FB)) {
- spin_unlock_irqrestore(&data->lock, flags);
- picolcd_fb_reset(data, 0);
- } else {
- spin_unlock_irqrestore(&data->lock, flags);
- }
-
- /*
- * Translate the framebuffer into the format needed by the PicoLCD.
- * See display layout above.
- * Do this one tile after the other and push those tiles that changed.
- *
- * Wait for our IO to complete as otherwise we might flood the queue!
- */
- n = 0;
- for (chip = 0; chip < 4; chip++)
- for (tile = 0; tile < 8; tile++)
- if (picolcd_fb_update_tile(data->fb_vbitmap,
- data->fb_bitmap, data->fb_bpp, chip, tile) ||
- data->fb_force) {
- n += 2;
- if (!data->fb_info->par)
- return; /* device lost! */
- if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
- usbhid_wait_io(data->hdev);
- n = 0;
- }
- picolcd_fb_send_tile(data->hdev, chip, tile);
- }
- data->fb_force = false;
- if (n)
- usbhid_wait_io(data->hdev);
-}
-
-/* Stub to call the system default and update the image on the picoLCD */
-static void picolcd_fb_fillrect(struct fb_info *info,
- const struct fb_fillrect *rect)
-{
- if (!info->par)
- return;
- sys_fillrect(info, rect);
-
- schedule_delayed_work(&info->deferred_work, 0);
-}
-
-/* Stub to call the system default and update the image on the picoLCD */
-static void picolcd_fb_copyarea(struct fb_info *info,
- const struct fb_copyarea *area)
-{
- if (!info->par)
- return;
- sys_copyarea(info, area);
-
- schedule_delayed_work(&info->deferred_work, 0);
-}
-
-/* Stub to call the system default and update the image on the picoLCD */
-static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
-{
- if (!info->par)
- return;
- sys_imageblit(info, image);
-
- schedule_delayed_work(&info->deferred_work, 0);
-}
-
-/*
- * this is the slow path from userspace. they can seek and write to
- * the fb. it's inefficient to do anything less than a full screen draw
- */
-static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- ssize_t ret;
- if (!info->par)
- return -ENODEV;
- ret = fb_sys_write(info, buf, count, ppos);
- if (ret >= 0)
- schedule_delayed_work(&info->deferred_work, 0);
- return ret;
-}
-
-static int picolcd_fb_blank(int blank, struct fb_info *info)
-{
- if (!info->par)
- return -ENODEV;
- /* We let fb notification do this for us via lcd/backlight device */
- return 0;
-}
-
-static void picolcd_fb_destroy(struct fb_info *info)
-{
- struct picolcd_data *data = info->par;
- u32 *ref_cnt = info->pseudo_palette;
- int may_release;
-
- info->par = NULL;
- if (data)
- data->fb_info = NULL;
- fb_deferred_io_cleanup(info);
-
- ref_cnt--;
- mutex_lock(&info->lock);
- (*ref_cnt)--;
- may_release = !*ref_cnt;
- mutex_unlock(&info->lock);
- if (may_release) {
- vfree((u8 *)info->fix.smem_start);
- framebuffer_release(info);
- }
-}
-
-static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
-{
- __u32 bpp = var->bits_per_pixel;
- __u32 activate = var->activate;
-
- /* only allow 1/8 bit depth (8-bit is grayscale) */
- *var = picolcdfb_var;
- var->activate = activate;
- if (bpp >= 8) {
- var->bits_per_pixel = 8;
- var->red.length = 8;
- var->green.length = 8;
- var->blue.length = 8;
- } else {
- var->bits_per_pixel = 1;
- var->red.length = 1;
- var->green.length = 1;
- var->blue.length = 1;
- }
- return 0;
-}
-
-static int picolcd_set_par(struct fb_info *info)
-{
- struct picolcd_data *data = info->par;
- u8 *tmp_fb, *o_fb;
- if (!data)
- return -ENODEV;
- if (info->var.bits_per_pixel == data->fb_bpp)
- return 0;
- /* switch between 1/8 bit depths */
- if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
- return -EINVAL;
-
- o_fb = data->fb_bitmap;
- tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL);
- if (!tmp_fb)
- return -ENOMEM;
-
- /* translate FB content to new bits-per-pixel */
- if (info->var.bits_per_pixel == 1) {
- int i, b;
- for (i = 0; i < PICOLCDFB_SIZE; i++) {
- u8 p = 0;
- for (b = 0; b < 8; b++) {
- p <<= 1;
- p |= o_fb[i*8+b] ? 0x01 : 0x00;
- }
- tmp_fb[i] = p;
- }
- memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
- info->fix.visual = FB_VISUAL_MONO01;
- info->fix.line_length = PICOLCDFB_WIDTH / 8;
- } else {
- int i;
- memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
- for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
- o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
- info->fix.visual = FB_VISUAL_DIRECTCOLOR;
- info->fix.line_length = PICOLCDFB_WIDTH;
- }
-
- kfree(tmp_fb);
- data->fb_bpp = info->var.bits_per_pixel;
- return 0;
-}
-
-/* Do refcounting on our FB and cleanup per worker if FB is
- * closed after unplug of our device
- * (fb_release holds info->lock and still touches info after
- * we return so we can't release it immediately.
- */
-struct picolcd_fb_cleanup_item {
- struct fb_info *info;
- struct picolcd_fb_cleanup_item *next;
-};
-static struct picolcd_fb_cleanup_item *fb_pending;
-static DEFINE_SPINLOCK(fb_pending_lock);
-
-static void picolcd_fb_do_cleanup(struct work_struct *data)
-{
- struct picolcd_fb_cleanup_item *item;
- unsigned long flags;
-
- do {
- spin_lock_irqsave(&fb_pending_lock, flags);
- item = fb_pending;
- fb_pending = item ? item->next : NULL;
- spin_unlock_irqrestore(&fb_pending_lock, flags);
-
- if (item) {
- u8 *fb = (u8 *)item->info->fix.smem_start;
- /* make sure we do not race against fb core when
- * releasing */
- mutex_lock(&item->info->lock);
- mutex_unlock(&item->info->lock);
- framebuffer_release(item->info);
- vfree(fb);
- }
- } while (item);
-}
-
-static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup);
-
-static int picolcd_fb_open(struct fb_info *info, int u)
-{
- u32 *ref_cnt = info->pseudo_palette;
- ref_cnt--;
-
- (*ref_cnt)++;
- return 0;
-}
-
-static int picolcd_fb_release(struct fb_info *info, int u)
-{
- u32 *ref_cnt = info->pseudo_palette;
- ref_cnt--;
-
- (*ref_cnt)++;
- if (!*ref_cnt) {
- unsigned long flags;
- struct picolcd_fb_cleanup_item *item = (struct picolcd_fb_cleanup_item *)ref_cnt;
- item--;
- spin_lock_irqsave(&fb_pending_lock, flags);
- item->next = fb_pending;
- fb_pending = item;
- spin_unlock_irqrestore(&fb_pending_lock, flags);
- schedule_work(&picolcd_fb_cleanup);
- }
- return 0;
-}
-
-/* Note this can't be const because of struct fb_info definition */
-static struct fb_ops picolcdfb_ops = {
- .owner = THIS_MODULE,
- .fb_destroy = picolcd_fb_destroy,
- .fb_open = picolcd_fb_open,
- .fb_release = picolcd_fb_release,
- .fb_read = fb_sys_read,
- .fb_write = picolcd_fb_write,
- .fb_blank = picolcd_fb_blank,
- .fb_fillrect = picolcd_fb_fillrect,
- .fb_copyarea = picolcd_fb_copyarea,
- .fb_imageblit = picolcd_fb_imageblit,
- .fb_check_var = picolcd_fb_check_var,
- .fb_set_par = picolcd_set_par,
-};
-
-
-/* Callback from deferred IO workqueue */
-static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
-{
- picolcd_fb_update(info->par);
-}
-
-static const struct fb_deferred_io picolcd_fb_defio = {
- .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
- .deferred_io = picolcd_fb_deferred_io,
-};
-
-
-/*
- * The "fb_update_rate" sysfs attribute
- */
-static ssize_t picolcd_fb_update_rate_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
- unsigned i, fb_update_rate = data->fb_update_rate;
- size_t ret = 0;
-
- for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
- if (ret >= PAGE_SIZE)
- break;
- else if (i == fb_update_rate)
- ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
- else
- ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
- if (ret > 0)
- buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
- return ret;
-}
-
-static ssize_t picolcd_fb_update_rate_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
- int i;
- unsigned u;
-
- if (count < 1 || count > 10)
- return -EINVAL;
-
- i = sscanf(buf, "%u", &u);
- if (i != 1)
- return -EINVAL;
-
- if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
- return -ERANGE;
- else if (u == 0)
- u = PICOLCDFB_UPDATE_RATE_DEFAULT;
-
- data->fb_update_rate = u;
- data->fb_defio.delay = HZ / data->fb_update_rate;
- return count;
-}
-
-static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,
- picolcd_fb_update_rate_store);
-
-/* initialize Framebuffer device */
-static int picolcd_init_framebuffer(struct picolcd_data *data)
-{
- struct device *dev = &data->hdev->dev;
- struct fb_info *info = NULL;
- int i, error = -ENOMEM;
- u8 *fb_vbitmap = NULL;
- u8 *fb_bitmap = NULL;
- u32 *palette;
-
- fb_bitmap = vmalloc(PICOLCDFB_SIZE*8);
- if (fb_bitmap == NULL) {
- dev_err(dev, "can't get a free page for framebuffer\n");
- goto err_nomem;
- }
-
- fb_vbitmap = kmalloc(PICOLCDFB_SIZE, GFP_KERNEL);
- if (fb_vbitmap == NULL) {
- dev_err(dev, "can't alloc vbitmap image buffer\n");
- goto err_nomem;
- }
-
- data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
- data->fb_defio = picolcd_fb_defio;
- /* The extra memory is:
- * - struct picolcd_fb_cleanup_item
- * - u32 for ref_count
- * - 256*u32 for pseudo_palette
- */
- info = framebuffer_alloc(257 * sizeof(u32) + sizeof(struct picolcd_fb_cleanup_item), dev);
- if (info == NULL) {
- dev_err(dev, "failed to allocate a framebuffer\n");
- goto err_nomem;
- }
-
- palette = info->par + sizeof(struct picolcd_fb_cleanup_item);
- *palette = 1;
- palette++;
- for (i = 0; i < 256; i++)
- palette[i] = i > 0 && i < 16 ? 0xff : 0;
- info->pseudo_palette = palette;
- info->fbdefio = &data->fb_defio;
- info->screen_base = (char __force __iomem *)fb_bitmap;
- info->fbops = &picolcdfb_ops;
- info->var = picolcdfb_var;
- info->fix = picolcdfb_fix;
- info->fix.smem_len = PICOLCDFB_SIZE*8;
- info->fix.smem_start = (unsigned long)fb_bitmap;
- info->par = data;
- info->flags = FBINFO_FLAG_DEFAULT;
-
- data->fb_vbitmap = fb_vbitmap;
- data->fb_bitmap = fb_bitmap;
- data->fb_bpp = picolcdfb_var.bits_per_pixel;
- error = picolcd_fb_reset(data, 1);
- if (error) {
- dev_err(dev, "failed to configure display\n");
- goto err_cleanup;
- }
- error = device_create_file(dev, &dev_attr_fb_update_rate);
- if (error) {
- dev_err(dev, "failed to create sysfs attributes\n");
- goto err_cleanup;
- }
- fb_deferred_io_init(info);
- data->fb_info = info;
- error = register_framebuffer(info);
- if (error) {
- dev_err(dev, "failed to register framebuffer\n");
- goto err_sysfs;
- }
- /* schedule first output of framebuffer */
- data->fb_force = 1;
- schedule_delayed_work(&info->deferred_work, 0);
- return 0;
-
-err_sysfs:
- fb_deferred_io_cleanup(info);
- device_remove_file(dev, &dev_attr_fb_update_rate);
-err_cleanup:
- data->fb_vbitmap = NULL;
- data->fb_bitmap = NULL;
- data->fb_bpp = 0;
- data->fb_info = NULL;
-
-err_nomem:
- framebuffer_release(info);
- vfree(fb_bitmap);
- kfree(fb_vbitmap);
- return error;
-}
-
-static void picolcd_exit_framebuffer(struct picolcd_data *data)
-{
- struct fb_info *info = data->fb_info;
- u8 *fb_vbitmap = data->fb_vbitmap;
-
- if (!info)
- return;
-
- info->par = NULL;
- device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
- unregister_framebuffer(info);
- data->fb_vbitmap = NULL;
- data->fb_bitmap = NULL;
- data->fb_bpp = 0;
- data->fb_info = NULL;
- kfree(fb_vbitmap);
-}
-
-#define picolcd_fbinfo(d) ((d)->fb_info)
-#else
-static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)
-{
- return 0;
-}
-static inline int picolcd_init_framebuffer(struct picolcd_data *data)
-{
- return 0;
-}
-static inline void picolcd_exit_framebuffer(struct picolcd_data *data)
-{
-}
-#define picolcd_fbinfo(d) NULL
-#endif /* CONFIG_HID_PICOLCD_FB */
-
-#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
-/*
- * backlight class device
- */
-static int picolcd_get_brightness(struct backlight_device *bdev)
-{
- struct picolcd_data *data = bl_get_data(bdev);
- return data->lcd_brightness;
-}
-
-static int picolcd_set_brightness(struct backlight_device *bdev)
-{
- struct picolcd_data *data = bl_get_data(bdev);
- struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev);
- unsigned long flags;
-
- if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
- return -ENODEV;
-
- data->lcd_brightness = bdev->props.brightness & 0x0ff;
- data->lcd_power = bdev->props.power;
- spin_lock_irqsave(&data->lock, flags);
- hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
- return 0;
-}
-
-static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb)
-{
- return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev));
-}
-
-static const struct backlight_ops picolcd_blops = {
- .update_status = picolcd_set_brightness,
- .get_brightness = picolcd_get_brightness,
- .check_fb = picolcd_check_bl_fb,
-};
-
-static int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report)
-{
- struct device *dev = &data->hdev->dev;
- struct backlight_device *bdev;
- struct backlight_properties props;
- if (!report)
- return -ENODEV;
- if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
- report->field[0]->report_size != 8) {
- dev_err(dev, "unsupported BRIGHTNESS report");
- return -EINVAL;
- }
-
- memset(&props, 0, sizeof(props));
- props.type = BACKLIGHT_RAW;
- props.max_brightness = 0xff;
- bdev = backlight_device_register(dev_name(dev), dev, data,
- &picolcd_blops, &props);
- if (IS_ERR(bdev)) {
- dev_err(dev, "failed to register backlight\n");
- return PTR_ERR(bdev);
- }
- bdev->props.brightness = 0xff;
- data->lcd_brightness = 0xff;
- data->backlight = bdev;
- picolcd_set_brightness(bdev);
- return 0;
-}
-
-static void picolcd_exit_backlight(struct picolcd_data *data)
-{
- struct backlight_device *bdev = data->backlight;
-
- data->backlight = NULL;
- if (bdev)
- backlight_device_unregister(bdev);
-}
-
-static inline int picolcd_resume_backlight(struct picolcd_data *data)
-{
- if (!data->backlight)
- return 0;
- return picolcd_set_brightness(data->backlight);
-}
-
-#ifdef CONFIG_PM
-static void picolcd_suspend_backlight(struct picolcd_data *data)
-{
- int bl_power = data->lcd_power;
- if (!data->backlight)
- return;
-
- data->backlight->props.power = FB_BLANK_POWERDOWN;
- picolcd_set_brightness(data->backlight);
- data->lcd_power = data->backlight->props.power = bl_power;
-}
-#endif /* CONFIG_PM */
-#else
-static inline int picolcd_init_backlight(struct picolcd_data *data,
- struct hid_report *report)
-{
- return 0;
-}
-static inline void picolcd_exit_backlight(struct picolcd_data *data)
-{
-}
-static inline int picolcd_resume_backlight(struct picolcd_data *data)
-{
- return 0;
-}
-static inline void picolcd_suspend_backlight(struct picolcd_data *data)
-{
-}
-#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
-
-#ifdef CONFIG_HID_PICOLCD_LCD
-/*
- * lcd class device
- */
-static int picolcd_get_contrast(struct lcd_device *ldev)
-{
- struct picolcd_data *data = lcd_get_data(ldev);
- return data->lcd_contrast;
-}
-
-static int picolcd_set_contrast(struct lcd_device *ldev, int contrast)
-{
- struct picolcd_data *data = lcd_get_data(ldev);
- struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev);
- unsigned long flags;
-
- if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
- return -ENODEV;
-
- data->lcd_contrast = contrast & 0x0ff;
- spin_lock_irqsave(&data->lock, flags);
- hid_set_field(report->field[0], 0, data->lcd_contrast);
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
- return 0;
-}
-
-static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb)
-{
- return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev));
-}
-
-static struct lcd_ops picolcd_lcdops = {
- .get_contrast = picolcd_get_contrast,
- .set_contrast = picolcd_set_contrast,
- .check_fb = picolcd_check_lcd_fb,
-};
-
-static int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report)
-{
- struct device *dev = &data->hdev->dev;
- struct lcd_device *ldev;
-
- if (!report)
- return -ENODEV;
- if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
- report->field[0]->report_size != 8) {
- dev_err(dev, "unsupported CONTRAST report");
- return -EINVAL;
- }
-
- ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops);
- if (IS_ERR(ldev)) {
- dev_err(dev, "failed to register LCD\n");
- return PTR_ERR(ldev);
- }
- ldev->props.max_contrast = 0x0ff;
- data->lcd_contrast = 0xe5;
- data->lcd = ldev;
- picolcd_set_contrast(ldev, 0xe5);
- return 0;
-}
-
-static void picolcd_exit_lcd(struct picolcd_data *data)
-{
- struct lcd_device *ldev = data->lcd;
-
- data->lcd = NULL;
- if (ldev)
- lcd_device_unregister(ldev);
-}
-
-static inline int picolcd_resume_lcd(struct picolcd_data *data)
-{
- if (!data->lcd)
- return 0;
- return picolcd_set_contrast(data->lcd, data->lcd_contrast);
-}
-#else
-static inline int picolcd_init_lcd(struct picolcd_data *data,
- struct hid_report *report)
-{
- return 0;
-}
-static inline void picolcd_exit_lcd(struct picolcd_data *data)
-{
-}
-static inline int picolcd_resume_lcd(struct picolcd_data *data)
-{
- return 0;
-}
-#endif /* CONFIG_HID_PICOLCD_LCD */
-
-#ifdef CONFIG_HID_PICOLCD_LEDS
-/**
- * LED class device
- */
-static void picolcd_leds_set(struct picolcd_data *data)
-{
- struct hid_report *report;
- unsigned long flags;
-
- if (!data->led[0])
- return;
- report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
- if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
- return;
-
- spin_lock_irqsave(&data->lock, flags);
- hid_set_field(report->field[0], 0, data->led_state);
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
-}
-
-static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
- enum led_brightness value)
-{
- struct device *dev;
- struct hid_device *hdev;
- struct picolcd_data *data;
- int i, state = 0;
-
- dev = led_cdev->dev->parent;
- hdev = container_of(dev, struct hid_device, dev);
- data = hid_get_drvdata(hdev);
- for (i = 0; i < 8; i++) {
- if (led_cdev != data->led[i])
- continue;
- state = (data->led_state >> i) & 1;
- if (value == LED_OFF && state) {
- data->led_state &= ~(1 << i);
- picolcd_leds_set(data);
- } else if (value != LED_OFF && !state) {
- data->led_state |= 1 << i;
- picolcd_leds_set(data);
- }
- break;
- }
-}
-
-static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
-{
- struct device *dev;
- struct hid_device *hdev;
- struct picolcd_data *data;
- int i, value = 0;
-
- dev = led_cdev->dev->parent;
- hdev = container_of(dev, struct hid_device, dev);
- data = hid_get_drvdata(hdev);
- for (i = 0; i < 8; i++)
- if (led_cdev == data->led[i]) {
- value = (data->led_state >> i) & 1;
- break;
- }
- return value ? LED_FULL : LED_OFF;
-}
-
-static int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
-{
- struct device *dev = &data->hdev->dev;
- struct led_classdev *led;
- size_t name_sz = strlen(dev_name(dev)) + 8;
- char *name;
- int i, ret = 0;
-
- if (!report)
- return -ENODEV;
- if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
- report->field[0]->report_size != 8) {
- dev_err(dev, "unsupported LED_STATE report");
- return -EINVAL;
- }
-
- for (i = 0; i < 8; i++) {
- led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
- if (!led) {
- dev_err(dev, "can't allocate memory for LED %d\n", i);
- ret = -ENOMEM;
- goto err;
- }
- name = (void *)(&led[1]);
- snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
- led->name = name;
- led->brightness = 0;
- led->max_brightness = 1;
- led->brightness_get = picolcd_led_get_brightness;
- led->brightness_set = picolcd_led_set_brightness;
-
- data->led[i] = led;
- ret = led_classdev_register(dev, data->led[i]);
- if (ret) {
- data->led[i] = NULL;
- kfree(led);
- dev_err(dev, "can't register LED %d\n", i);
- goto err;
- }
- }
- return 0;
-err:
- for (i = 0; i < 8; i++)
- if (data->led[i]) {
- led = data->led[i];
- data->led[i] = NULL;
- led_classdev_unregister(led);
- kfree(led);
- }
- return ret;
-}
-
-static void picolcd_exit_leds(struct picolcd_data *data)
-{
- struct led_classdev *led;
- int i;
-
- for (i = 0; i < 8; i++) {
- led = data->led[i];
- data->led[i] = NULL;
- if (!led)
- continue;
- led_classdev_unregister(led);
- kfree(led);
- }
-}
-
-#else
-static inline int picolcd_init_leds(struct picolcd_data *data,
- struct hid_report *report)
-{
- return 0;
-}
-static inline void picolcd_exit_leds(struct picolcd_data *data)
-{
-}
-static inline int picolcd_leds_set(struct picolcd_data *data)
-{
- return 0;
-}
-#endif /* CONFIG_HID_PICOLCD_LEDS */
-
-/*
- * input class device
- */
-static int picolcd_raw_keypad(struct picolcd_data *data,
- struct hid_report *report, u8 *raw_data, int size)
-{
- /*
- * Keypad event
- * First and second data bytes list currently pressed keys,
- * 0x00 means no key and at most 2 keys may be pressed at same time
- */
- int i, j;
-
- /* determine newly pressed keys */
- for (i = 0; i < size; i++) {
- unsigned int key_code;
- if (raw_data[i] == 0)
- continue;
- for (j = 0; j < sizeof(data->pressed_keys); j++)
- if (data->pressed_keys[j] == raw_data[i])
- goto key_already_down;
- for (j = 0; j < sizeof(data->pressed_keys); j++)
- if (data->pressed_keys[j] == 0) {
- data->pressed_keys[j] = raw_data[i];
- break;
- }
- input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);
- if (raw_data[i] < PICOLCD_KEYS)
- key_code = data->keycode[raw_data[i]];
- else
- key_code = KEY_UNKNOWN;
- if (key_code != KEY_UNKNOWN) {
- dbg_hid(PICOLCD_NAME " got key press for %u:%d",
- raw_data[i], key_code);
- input_report_key(data->input_keys, key_code, 1);
- }
- input_sync(data->input_keys);
-key_already_down:
- continue;
- }
-
- /* determine newly released keys */
- for (j = 0; j < sizeof(data->pressed_keys); j++) {
- unsigned int key_code;
- if (data->pressed_keys[j] == 0)
- continue;
- for (i = 0; i < size; i++)
- if (data->pressed_keys[j] == raw_data[i])
- goto key_still_down;
- input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);
- if (data->pressed_keys[j] < PICOLCD_KEYS)
- key_code = data->keycode[data->pressed_keys[j]];
- else
- key_code = KEY_UNKNOWN;
- if (key_code != KEY_UNKNOWN) {
- dbg_hid(PICOLCD_NAME " got key release for %u:%d",
- data->pressed_keys[j], key_code);
- input_report_key(data->input_keys, key_code, 0);
- }
- input_sync(data->input_keys);
- data->pressed_keys[j] = 0;
-key_still_down:
- continue;
- }
- return 1;
-}
-
-static int picolcd_raw_cir(struct picolcd_data *data,
- struct hid_report *report, u8 *raw_data, int size)
-{
- /* Need understanding of CIR data format to implement ... */
- return 1;
-}
-
-static int picolcd_check_version(struct hid_device *hdev)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- struct picolcd_pending *verinfo;
- int ret = 0;
-
- if (!data)
- return -ENODEV;
-
- verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);
- if (!verinfo) {
- hid_err(hdev, "no version response from PicoLCD\n");
- return -ENODEV;
- }
-
- if (verinfo->raw_size == 2) {
- data->version[0] = verinfo->raw_data[1];
- data->version[1] = verinfo->raw_data[0];
- if (data->status & PICOLCD_BOOTLOADER) {
- hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",
- verinfo->raw_data[1], verinfo->raw_data[0]);
- } else {
- hid_info(hdev, "PicoLCD, firmware version %d.%d\n",
- verinfo->raw_data[1], verinfo->raw_data[0]);
- }
- } else {
- hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");
- ret = -EINVAL;
- }
- kfree(verinfo);
- return ret;
-}
-
-/*
- * Reset our device and wait for answer to VERSION request
- */
-static int picolcd_reset(struct hid_device *hdev)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);
- unsigned long flags;
- int error;
-
- if (!data || !report || report->maxfield != 1)
- return -ENODEV;
-
- spin_lock_irqsave(&data->lock, flags);
- if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
- data->status |= PICOLCD_BOOTLOADER;
-
- /* perform the reset */
- hid_set_field(report->field[0], 0, 1);
- usbhid_submit_report(hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
-
- error = picolcd_check_version(hdev);
- if (error)
- return error;
-
- picolcd_resume_lcd(data);
- picolcd_resume_backlight(data);
-#ifdef CONFIG_HID_PICOLCD_FB
- if (data->fb_info)
- schedule_delayed_work(&data->fb_info->deferred_work, 0);
-#endif /* CONFIG_HID_PICOLCD_FB */
-
- picolcd_leds_set(data);
- return 0;
-}
-
-/*
- * The "operation_mode" sysfs attribute
- */
-static ssize_t picolcd_operation_mode_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
-
- if (data->status & PICOLCD_BOOTLOADER)
- return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");
- else
- return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n");
-}
-
-static ssize_t picolcd_operation_mode_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
- struct hid_report *report = NULL;
- size_t cnt = count;
- int timeout = data->opmode_delay;
- unsigned long flags;
-
- if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) {
- if (data->status & PICOLCD_BOOTLOADER)
- report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);
- buf += 3;
- cnt -= 3;
- } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) {
- if (!(data->status & PICOLCD_BOOTLOADER))
- report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);
- buf += 10;
- cnt -= 10;
- }
- if (!report)
- return -EINVAL;
-
- while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))
- cnt--;
- if (cnt != 0)
- return -EINVAL;
-
- spin_lock_irqsave(&data->lock, flags);
- hid_set_field(report->field[0], 0, timeout & 0xff);
- hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);
- usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
- spin_unlock_irqrestore(&data->lock, flags);
- return count;
-}
-
-static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,
- picolcd_operation_mode_store);
-
-/*
- * The "operation_mode_delay" sysfs attribute
- */
-static ssize_t picolcd_operation_mode_delay_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
-
- return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay);
-}
-
-static ssize_t picolcd_operation_mode_delay_store(struct device *dev,
- struct device_attribute *attr, const char *buf, size_t count)
-{
- struct picolcd_data *data = dev_get_drvdata(dev);
- unsigned u;
- if (sscanf(buf, "%u", &u) != 1)
- return -EINVAL;
- if (u > 30000)
- return -EINVAL;
- else
- data->opmode_delay = u;
- return count;
-}
-
-static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,
- picolcd_operation_mode_delay_store);
-
-
-#ifdef CONFIG_DEBUG_FS
-/*
- * The "reset" file
- */
-static int picolcd_debug_reset_show(struct seq_file *f, void *p)
-{
- if (picolcd_fbinfo((struct picolcd_data *)f->private))
- seq_printf(f, "all fb\n");
- else
- seq_printf(f, "all\n");
- return 0;
-}
-
-static int picolcd_debug_reset_open(struct inode *inode, struct file *f)
-{
- return single_open(f, picolcd_debug_reset_show, inode->i_private);
-}
-
-static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
- size_t count, loff_t *ppos)
-{
- struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
- char buf[32];
- size_t cnt = min(count, sizeof(buf)-1);
- if (copy_from_user(buf, user_buf, cnt))
- return -EFAULT;
-
- while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n'))
- cnt--;
- buf[cnt] = '\0';
- if (strcmp(buf, "all") == 0) {
- picolcd_reset(data->hdev);
- picolcd_fb_reset(data, 1);
- } else if (strcmp(buf, "fb") == 0) {
- picolcd_fb_reset(data, 1);
- } else {
- return -EINVAL;
- }
- return count;
-}
-
-static const struct file_operations picolcd_debug_reset_fops = {
- .owner = THIS_MODULE,
- .open = picolcd_debug_reset_open,
- .read = seq_read,
- .llseek = seq_lseek,
- .write = picolcd_debug_reset_write,
- .release = single_release,
-};
-
-/*
- * The "eeprom" file
- */
-static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
- size_t s, loff_t *off)
-{
- struct picolcd_data *data = f->private_data;
- struct picolcd_pending *resp;
- u8 raw_data[3];
- ssize_t ret = -EIO;
-
- if (s == 0)
- return -EINVAL;
- if (*off > 0x0ff)
- return 0;
-
- /* prepare buffer with info about what we want to read (addr & len) */
- raw_data[0] = *off & 0xff;
- raw_data[1] = (*off >> 8) & 0xff;
- raw_data[2] = s < 20 ? s : 20;
- if (*off + raw_data[2] > 0xff)
- raw_data[2] = 0x100 - *off;
- resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
- sizeof(raw_data));
- if (!resp)
- return -EIO;
-
- if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
- /* successful read :) */
- ret = resp->raw_data[2];
- if (ret > s)
- ret = s;
- if (copy_to_user(u, resp->raw_data+3, ret))
- ret = -EFAULT;
- else
- *off += ret;
- } /* anything else is some kind of IO error */
-
- kfree(resp);
- return ret;
-}
-
-static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
- size_t s, loff_t *off)
-{
- struct picolcd_data *data = f->private_data;
- struct picolcd_pending *resp;
- ssize_t ret = -EIO;
- u8 raw_data[23];
-
- if (s == 0)
- return -EINVAL;
- if (*off > 0x0ff)
- return -ENOSPC;
-
- memset(raw_data, 0, sizeof(raw_data));
- raw_data[0] = *off & 0xff;
- raw_data[1] = (*off >> 8) & 0xff;
- raw_data[2] = min((size_t)20, s);
- if (*off + raw_data[2] > 0xff)
- raw_data[2] = 0x100 - *off;
-
- if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
- return -EFAULT;
- resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
- sizeof(raw_data));
-
- if (!resp)
- return -EIO;
-
- if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
- /* check if written data matches */
- if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) {
- *off += raw_data[2];
- ret = raw_data[2];
- }
- }
- kfree(resp);
- return ret;
-}
-
-/*
- * Notes:
- * - read/write happens in chunks of at most 20 bytes, it's up to userspace
- * to loop in order to get more data.
- * - on write errors on otherwise correct write request the bytes
- * that should have been written are in undefined state.
- */
-static const struct file_operations picolcd_debug_eeprom_fops = {
- .owner = THIS_MODULE,
- .open = simple_open,
- .read = picolcd_debug_eeprom_read,
- .write = picolcd_debug_eeprom_write,
- .llseek = generic_file_llseek,
-};
-
-/*
- * The "flash" file
- */
-/* record a flash address to buf (bounds check to be done by caller) */
-static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
-{
- buf[0] = off & 0xff;
- buf[1] = (off >> 8) & 0xff;
- if (data->addr_sz == 3)
- buf[2] = (off >> 16) & 0xff;
- return data->addr_sz == 2 ? 2 : 3;
-}
-
-/* read a given size of data (bounds check to be done by caller) */
-static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
- char __user *u, size_t s, loff_t *off)
-{
- struct picolcd_pending *resp;
- u8 raw_data[4];
- ssize_t ret = 0;
- int len_off, err = -EIO;
-
- while (s > 0) {
- err = -EIO;
- len_off = _picolcd_flash_setaddr(data, raw_data, *off);
- raw_data[len_off] = s > 32 ? 32 : s;
- resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
- if (!resp || !resp->in_report)
- goto skip;
- if (resp->in_report->id == REPORT_MEMORY ||
- resp->in_report->id == REPORT_BL_READ_MEMORY) {
- if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
- goto skip;
- if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
- err = -EFAULT;
- goto skip;
- }
- *off += raw_data[len_off];
- s -= raw_data[len_off];
- ret += raw_data[len_off];
- err = 0;
- }
-skip:
- kfree(resp);
- if (err)
- return ret > 0 ? ret : err;
- }
- return ret;
-}
-
-static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
- size_t s, loff_t *off)
-{
- struct picolcd_data *data = f->private_data;
-
- if (s == 0)
- return -EINVAL;
- if (*off > 0x05fff)
- return 0;
- if (*off + s > 0x05fff)
- s = 0x06000 - *off;
-
- if (data->status & PICOLCD_BOOTLOADER)
- return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
- else
- return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
-}
-
-/* erase block aligned to 64bytes boundary */
-static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
- loff_t *off)
-{
- struct picolcd_pending *resp;
- u8 raw_data[3];
- int len_off;
- ssize_t ret = -EIO;
-
- if (*off & 0x3f)
- return -EINVAL;
-
- len_off = _picolcd_flash_setaddr(data, raw_data, *off);
- resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
- if (!resp || !resp->in_report)
- goto skip;
- if (resp->in_report->id == REPORT_MEMORY ||
- resp->in_report->id == REPORT_BL_ERASE_MEMORY) {
- if (memcmp(raw_data, resp->raw_data, len_off) != 0)
- goto skip;
- ret = 0;
- }
-skip:
- kfree(resp);
- return ret;
-}
-
-/* write a given size of data (bounds check to be done by caller) */
-static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
- const char __user *u, size_t s, loff_t *off)
-{
- struct picolcd_pending *resp;
- u8 raw_data[36];
- ssize_t ret = 0;
- int len_off, err = -EIO;
-
- while (s > 0) {
- err = -EIO;
- len_off = _picolcd_flash_setaddr(data, raw_data, *off);
- raw_data[len_off] = s > 32 ? 32 : s;
- if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
- err = -EFAULT;
- break;
- }
- resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
- len_off+1+raw_data[len_off]);
- if (!resp || !resp->in_report)
- goto skip;
- if (resp->in_report->id == REPORT_MEMORY ||
- resp->in_report->id == REPORT_BL_WRITE_MEMORY) {
- if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
- goto skip;
- *off += raw_data[len_off];
- s -= raw_data[len_off];
- ret += raw_data[len_off];
- err = 0;
- }
-skip:
- kfree(resp);
- if (err)
- break;
- }
- return ret > 0 ? ret : err;
-}
-
-static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
- size_t s, loff_t *off)
-{
- struct picolcd_data *data = f->private_data;
- ssize_t err, ret = 0;
- int report_erase, report_write;
-
- if (s == 0)
- return -EINVAL;
- if (*off > 0x5fff)
- return -ENOSPC;
- if (s & 0x3f)
- return -EINVAL;
- if (*off & 0x3f)
- return -EINVAL;
-
- if (data->status & PICOLCD_BOOTLOADER) {
- report_erase = REPORT_BL_ERASE_MEMORY;
- report_write = REPORT_BL_WRITE_MEMORY;
- } else {
- report_erase = REPORT_ERASE_MEMORY;
- report_write = REPORT_WRITE_MEMORY;
- }
- mutex_lock(&data->mutex_flash);
- while (s > 0) {
- err = _picolcd_flash_erase64(data, report_erase, off);
- if (err)
- break;
- err = _picolcd_flash_write(data, report_write, u, 64, off);
- if (err < 0)
- break;
- ret += err;
- *off += err;
- s -= err;
- if (err != 64)
- break;
- }
- mutex_unlock(&data->mutex_flash);
- return ret > 0 ? ret : err;
-}
-
-/*
- * Notes:
- * - concurrent writing is prevented by mutex and all writes must be
- * n*64 bytes and 64-byte aligned, each write being preceded by an
- * ERASE which erases a 64byte block.
- * If less than requested was written or an error is returned for an
- * otherwise correct write request the next 64-byte block which should
- * have been written is in undefined state (mostly: original, erased,
- * (half-)written with write error)
- * - reading can happen without special restriction
- */
-static const struct file_operations picolcd_debug_flash_fops = {
- .owner = THIS_MODULE,
- .open = simple_open,
- .read = picolcd_debug_flash_read,
- .write = picolcd_debug_flash_write,
- .llseek = generic_file_llseek,
-};
-
-
-/*
- * Helper code for HID report level dumping/debugging
- */
-static const char *error_codes[] = {
- "success", "parameter missing", "data_missing", "block readonly",
- "block not erasable", "block too big", "section overflow",
- "invalid command length", "invalid data length",
-};
-
-static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
- const size_t data_len)
-{
- int i, j;
- for (i = j = 0; i < data_len && j + 3 < dst_sz; i++) {
- dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];
- dst[j++] = hex_asc[data[i] & 0x0f];
- dst[j++] = ' ';
- }
- if (j < dst_sz) {
- dst[j--] = '\0';
- dst[j] = '\n';
- } else
- dst[j] = '\0';
-}
-
-static void picolcd_debug_out_report(struct picolcd_data *data,
- struct hid_device *hdev, struct hid_report *report)
-{
- u8 raw_data[70];
- int raw_size = (report->size >> 3) + 1;
- char *buff;
-#define BUFF_SZ 256
-
- /* Avoid unnecessary overhead if debugfs is disabled */
- if (list_empty(&hdev->debug_list))
- return;
-
- buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
- if (!buff)
- return;
-
- snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ",
- report->id, raw_size);
- hid_debug_event(hdev, buff);
- if (raw_size + 5 > sizeof(raw_data)) {
- kfree(buff);
- hid_debug_event(hdev, " TOO BIG\n");
- return;
- } else {
- raw_data[0] = report->id;
- hid_output_report(report, raw_data);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
- hid_debug_event(hdev, buff);
- }
-
- switch (report->id) {
- case REPORT_LED_STATE:
- /* 1 data byte with GPO state */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_LED_STATE", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_BRIGHTNESS:
- /* 1 data byte with brightness */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_BRIGHTNESS", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_CONTRAST:
- /* 1 data byte with contrast */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_CONTRAST", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_RESET:
- /* 2 data bytes with reset duration in ms */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_RESET", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",
- raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_LCD_CMD:
- /* 63 data bytes with LCD commands */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_LCD_CMD", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- /* TODO: format decoding */
- break;
- case REPORT_LCD_DATA:
- /* 63 data bytes with LCD data */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_LCD_CMD", report->id, raw_size-1);
- /* TODO: format decoding */
- hid_debug_event(hdev, buff);
- break;
- case REPORT_LCD_CMD_DATA:
- /* 63 data bytes with LCD commands and data */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_LCD_CMD", report->id, raw_size-1);
- /* TODO: format decoding */
- hid_debug_event(hdev, buff);
- break;
- case REPORT_EE_READ:
- /* 3 data bytes with read area description */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_EE_READ", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_EE_WRITE:
- /* 3+1..20 data bytes with write area description */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_EE_WRITE", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- hid_debug_event(hdev, buff);
- if (raw_data[3] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- } else if (raw_data[3] + 4 <= raw_size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- }
- hid_debug_event(hdev, buff);
- break;
- case REPORT_ERASE_MEMORY:
- case REPORT_BL_ERASE_MEMORY:
- /* 3 data bytes with pointer inside erase block */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_ERASE_MEMORY", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- switch (data->addr_sz) {
- case 2:
- snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- break;
- case 3:
- snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",
- raw_data[3], raw_data[2], raw_data[1]);
- break;
- default:
- snprintf(buff, BUFF_SZ, "\tNot supported\n");
- }
- hid_debug_event(hdev, buff);
- break;
- case REPORT_READ_MEMORY:
- case REPORT_BL_READ_MEMORY:
- /* 4 data bytes with read area description */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_READ_MEMORY", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- switch (data->addr_sz) {
- case 2:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- break;
- case 3:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
- raw_data[3], raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
- break;
- default:
- snprintf(buff, BUFF_SZ, "\tNot supported\n");
- }
- hid_debug_event(hdev, buff);
- break;
- case REPORT_WRITE_MEMORY:
- case REPORT_BL_WRITE_MEMORY:
- /* 4+1..32 data bytes with write adrea description */
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_WRITE_MEMORY", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- switch (data->addr_sz) {
- case 2:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- hid_debug_event(hdev, buff);
- if (raw_data[3] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- } else if (raw_data[3] + 4 <= raw_size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- }
- break;
- case 3:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
- raw_data[3], raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
- hid_debug_event(hdev, buff);
- if (raw_data[4] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- } else if (raw_data[4] + 5 <= raw_size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- }
- break;
- default:
- snprintf(buff, BUFF_SZ, "\tNot supported\n");
- }
- hid_debug_event(hdev, buff);
- break;
- case REPORT_SPLASH_RESTART:
- /* TODO */
- break;
- case REPORT_EXIT_KEYBOARD:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_EXIT_KEYBOARD", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
- raw_data[1] | (raw_data[2] << 8),
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_VERSION:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_VERSION", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_DEVID:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_DEVID", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_SPLASH_SIZE:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_SPLASH_SIZE", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_HOOK_VERSION:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_HOOK_VERSION", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_EXIT_FLASHER:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "REPORT_VERSION", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
- raw_data[1] | (raw_data[2] << 8),
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- default:
- snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
- "<unknown>", report->id, raw_size-1);
- hid_debug_event(hdev, buff);
- break;
- }
- wake_up_interruptible(&hdev->debug_wait);
- kfree(buff);
-}
-
-static void picolcd_debug_raw_event(struct picolcd_data *data,
- struct hid_device *hdev, struct hid_report *report,
- u8 *raw_data, int size)
-{
- char *buff;
-
-#define BUFF_SZ 256
- /* Avoid unnecessary overhead if debugfs is disabled */
- if (!hdev->debug_events)
- return;
-
- buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
- if (!buff)
- return;
-
- switch (report->id) {
- case REPORT_ERROR_CODE:
- /* 2 data bytes with affected report and error code */
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_ERROR_CODE", report->id, size-1);
- hid_debug_event(hdev, buff);
- if (raw_data[2] < ARRAY_SIZE(error_codes))
- snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",
- raw_data[2], error_codes[raw_data[2]], raw_data[1]);
- else
- snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_KEY_STATE:
- /* 2 data bytes with key state */
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_KEY_STATE", report->id, size-1);
- hid_debug_event(hdev, buff);
- if (raw_data[1] == 0)
- snprintf(buff, BUFF_SZ, "\tNo key pressed\n");
- else if (raw_data[2] == 0)
- snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",
- raw_data[1], raw_data[1]);
- else
- snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",
- raw_data[1], raw_data[1], raw_data[2], raw_data[2]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_IR_DATA:
- /* Up to 20 byes of IR scancode data */
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_IR_DATA", report->id, size-1);
- hid_debug_event(hdev, buff);
- if (raw_data[1] == 0) {
- snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");
- hid_debug_event(hdev, buff);
- } else if (raw_data[1] + 1 <= size) {
- snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",
- raw_data[1]-1);
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]-1);
- hid_debug_event(hdev, buff);
- } else {
- snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",
- raw_data[1]-1);
- hid_debug_event(hdev, buff);
- }
- break;
- case REPORT_EE_DATA:
- /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_EE_DATA", report->id, size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- hid_debug_event(hdev, buff);
- if (raw_data[3] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- hid_debug_event(hdev, buff);
- } else if (raw_data[3] + 4 <= size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
- hid_debug_event(hdev, buff);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- hid_debug_event(hdev, buff);
- }
- break;
- case REPORT_MEMORY:
- /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_MEMORY", report->id, size-1);
- hid_debug_event(hdev, buff);
- switch (data->addr_sz) {
- case 2:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
- hid_debug_event(hdev, buff);
- if (raw_data[3] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- } else if (raw_data[3] + 4 <= size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- }
- break;
- case 3:
- snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
- raw_data[3], raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
- hid_debug_event(hdev, buff);
- if (raw_data[4] == 0) {
- snprintf(buff, BUFF_SZ, "\tNo data\n");
- } else if (raw_data[4] + 5 <= size) {
- snprintf(buff, BUFF_SZ, "\tData: ");
- hid_debug_event(hdev, buff);
- dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
- } else {
- snprintf(buff, BUFF_SZ, "\tData overflowed\n");
- }
- break;
- default:
- snprintf(buff, BUFF_SZ, "\tNot supported\n");
- }
- hid_debug_event(hdev, buff);
- break;
- case REPORT_VERSION:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_VERSION", report->id, size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
- raw_data[2], raw_data[1]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_BL_ERASE_MEMORY:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_BL_ERASE_MEMORY", report->id, size-1);
- hid_debug_event(hdev, buff);
- /* TODO */
- break;
- case REPORT_BL_READ_MEMORY:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_BL_READ_MEMORY", report->id, size-1);
- hid_debug_event(hdev, buff);
- /* TODO */
- break;
- case REPORT_BL_WRITE_MEMORY:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_BL_WRITE_MEMORY", report->id, size-1);
- hid_debug_event(hdev, buff);
- /* TODO */
- break;
- case REPORT_DEVID:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_DEVID", report->id, size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",
- raw_data[1], raw_data[2], raw_data[3], raw_data[4]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",
- raw_data[5]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_SPLASH_SIZE:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_SPLASH_SIZE", report->id, size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",
- (raw_data[2] << 8) | raw_data[1]);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",
- (raw_data[4] << 8) | raw_data[3]);
- hid_debug_event(hdev, buff);
- break;
- case REPORT_HOOK_VERSION:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "REPORT_HOOK_VERSION", report->id, size-1);
- hid_debug_event(hdev, buff);
- snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
- raw_data[1], raw_data[2]);
- hid_debug_event(hdev, buff);
- break;
- default:
- snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
- "<unknown>", report->id, size-1);
- hid_debug_event(hdev, buff);
- break;
- }
- wake_up_interruptible(&hdev->debug_wait);
- kfree(buff);
-}
-
-static void picolcd_init_devfs(struct picolcd_data *data,
- struct hid_report *eeprom_r, struct hid_report *eeprom_w,
- struct hid_report *flash_r, struct hid_report *flash_w,
- struct hid_report *reset)
-{
- struct hid_device *hdev = data->hdev;
-
- mutex_init(&data->mutex_flash);
-
- /* reset */
- if (reset)
- data->debug_reset = debugfs_create_file("reset", 0600,
- hdev->debug_dir, data, &picolcd_debug_reset_fops);
-
- /* eeprom */
- if (eeprom_r || eeprom_w)
- data->debug_eeprom = debugfs_create_file("eeprom",
- (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
- hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
-
- /* flash */
- if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8)
- data->addr_sz = flash_r->field[0]->report_count - 1;
- else
- data->addr_sz = -1;
- if (data->addr_sz == 2 || data->addr_sz == 3) {
- data->debug_flash = debugfs_create_file("flash",
- (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
- hdev->debug_dir, data, &picolcd_debug_flash_fops);
- } else if (flash_r || flash_w)
- hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n");
-}
-
-static void picolcd_exit_devfs(struct picolcd_data *data)
-{
- struct dentry *dent;
-
- dent = data->debug_reset;
- data->debug_reset = NULL;
- if (dent)
- debugfs_remove(dent);
- dent = data->debug_eeprom;
- data->debug_eeprom = NULL;
- if (dent)
- debugfs_remove(dent);
- dent = data->debug_flash;
- data->debug_flash = NULL;
- if (dent)
- debugfs_remove(dent);
- mutex_destroy(&data->mutex_flash);
-}
-#else
-static inline void picolcd_debug_raw_event(struct picolcd_data *data,
- struct hid_device *hdev, struct hid_report *report,
- u8 *raw_data, int size)
-{
-}
-static inline void picolcd_init_devfs(struct picolcd_data *data,
- struct hid_report *eeprom_r, struct hid_report *eeprom_w,
- struct hid_report *flash_r, struct hid_report *flash_w,
- struct hid_report *reset)
-{
-}
-static inline void picolcd_exit_devfs(struct picolcd_data *data)
-{
-}
-#endif /* CONFIG_DEBUG_FS */
-
-/*
- * Handle raw report as sent by device
- */
-static int picolcd_raw_event(struct hid_device *hdev,
- struct hid_report *report, u8 *raw_data, int size)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- unsigned long flags;
- int ret = 0;
-
- if (!data)
- return 1;
-
- if (report->id == REPORT_KEY_STATE) {
- if (data->input_keys)
- ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);
- } else if (report->id == REPORT_IR_DATA) {
- if (data->input_cir)
- ret = picolcd_raw_cir(data, report, raw_data+1, size-1);
- } else {
- spin_lock_irqsave(&data->lock, flags);
- /*
- * We let the caller of picolcd_send_and_wait() check if the
- * report we got is one of the expected ones or not.
- */
- if (data->pending) {
- memcpy(data->pending->raw_data, raw_data+1, size-1);
- data->pending->raw_size = size-1;
- data->pending->in_report = report;
- complete(&data->pending->ready);
- }
- spin_unlock_irqrestore(&data->lock, flags);
- }
-
- picolcd_debug_raw_event(data, hdev, report, raw_data, size);
- return 1;
-}
-
-#ifdef CONFIG_PM
-static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
-{
- if (PMSG_IS_AUTO(message))
- return 0;
-
- picolcd_suspend_backlight(hid_get_drvdata(hdev));
- dbg_hid(PICOLCD_NAME " device ready for suspend\n");
- return 0;
-}
-
-static int picolcd_resume(struct hid_device *hdev)
-{
- int ret;
- ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
- if (ret)
- dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
- return 0;
-}
-
-static int picolcd_reset_resume(struct hid_device *hdev)
-{
- int ret;
- ret = picolcd_reset(hdev);
- if (ret)
- dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);
- ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);
- if (ret)
- dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);
- ret = picolcd_resume_lcd(hid_get_drvdata(hdev));
- if (ret)
- dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);
- ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
- if (ret)
- dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
- picolcd_leds_set(hid_get_drvdata(hdev));
- return 0;
-}
-#endif
-
-/* initialize keypad input device */
-static int picolcd_init_keys(struct picolcd_data *data,
- struct hid_report *report)
-{
- struct hid_device *hdev = data->hdev;
- struct input_dev *idev;
- int error, i;
-
- if (!report)
- return -ENODEV;
- if (report->maxfield != 1 || report->field[0]->report_count != 2 ||
- report->field[0]->report_size != 8) {
- hid_err(hdev, "unsupported KEY_STATE report\n");
- return -EINVAL;
- }
-
- idev = input_allocate_device();
- if (idev == NULL) {
- hid_err(hdev, "failed to allocate input device\n");
- return -ENOMEM;
- }
- input_set_drvdata(idev, hdev);
- memcpy(data->keycode, def_keymap, sizeof(def_keymap));
- idev->name = hdev->name;
- idev->phys = hdev->phys;
- idev->uniq = hdev->uniq;
- idev->id.bustype = hdev->bus;
- idev->id.vendor = hdev->vendor;
- idev->id.product = hdev->product;
- idev->id.version = hdev->version;
- idev->dev.parent = hdev->dev.parent;
- idev->keycode = &data->keycode;
- idev->keycodemax = PICOLCD_KEYS;
- idev->keycodesize = sizeof(data->keycode[0]);
- input_set_capability(idev, EV_MSC, MSC_SCAN);
- set_bit(EV_REP, idev->evbit);
- for (i = 0; i < PICOLCD_KEYS; i++)
- input_set_capability(idev, EV_KEY, data->keycode[i]);
- error = input_register_device(idev);
- if (error) {
- hid_err(hdev, "error registering the input device\n");
- input_free_device(idev);
- return error;
- }
- data->input_keys = idev;
- return 0;
-}
-
-static void picolcd_exit_keys(struct picolcd_data *data)
-{
- struct input_dev *idev = data->input_keys;
-
- data->input_keys = NULL;
- if (idev)
- input_unregister_device(idev);
-}
-
-/* initialize CIR input device */
-static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
-{
- /* support not implemented yet */
- return 0;
-}
-
-static inline void picolcd_exit_cir(struct picolcd_data *data)
-{
-}
-
-static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
-{
- int error;
-
- error = picolcd_check_version(hdev);
- if (error)
- return error;
-
- if (data->version[0] != 0 && data->version[1] != 3)
- hid_info(hdev, "Device with untested firmware revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",
- dev_name(&hdev->dev));
-
- /* Setup keypad input device */
- error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));
- if (error)
- goto err;
-
- /* Setup CIR input device */
- error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));
- if (error)
- goto err;
-
- /* Set up the framebuffer device */
- error = picolcd_init_framebuffer(data);
- if (error)
- goto err;
-
- /* Setup lcd class device */
- error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));
- if (error)
- goto err;
-
- /* Setup backlight class device */
- error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));
- if (error)
- goto err;
-
- /* Setup the LED class devices */
- error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
- if (error)
- goto err;
-
- picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),
- picolcd_out_report(REPORT_EE_WRITE, hdev),
- picolcd_out_report(REPORT_READ_MEMORY, hdev),
- picolcd_out_report(REPORT_WRITE_MEMORY, hdev),
- picolcd_out_report(REPORT_RESET, hdev));
- return 0;
-err:
- picolcd_exit_leds(data);
- picolcd_exit_backlight(data);
- picolcd_exit_lcd(data);
- picolcd_exit_framebuffer(data);
- picolcd_exit_cir(data);
- picolcd_exit_keys(data);
- return error;
-}
-
-static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
-{
- int error;
-
- error = picolcd_check_version(hdev);
- if (error)
- return error;
-
- if (data->version[0] != 1 && data->version[1] != 0)
- hid_info(hdev, "Device with untested bootloader revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",
- dev_name(&hdev->dev));
-
- picolcd_init_devfs(data, NULL, NULL,
- picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
- picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);
- return 0;
-}
-
-static int picolcd_probe(struct hid_device *hdev,
- const struct hid_device_id *id)
-{
- struct picolcd_data *data;
- int error = -ENOMEM;
-
- dbg_hid(PICOLCD_NAME " hardware probe...\n");
-
- /*
- * Let's allocate the picolcd data structure, set some reasonable
- * defaults, and associate it with the device
- */
- data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);
- if (data == NULL) {
- hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n");
- error = -ENOMEM;
- goto err_no_cleanup;
- }
-
- spin_lock_init(&data->lock);
- mutex_init(&data->mutex);
- data->hdev = hdev;
- data->opmode_delay = 5000;
- if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
- data->status |= PICOLCD_BOOTLOADER;
- hid_set_drvdata(hdev, data);
-
- /* Parse the device reports and start it up */
- error = hid_parse(hdev);
- if (error) {
- hid_err(hdev, "device report parse failed\n");
- goto err_cleanup_data;
- }
-
- error = hid_hw_start(hdev, 0);
- if (error) {
- hid_err(hdev, "hardware start failed\n");
- goto err_cleanup_data;
- }
-
- error = hid_hw_open(hdev);
- if (error) {
- hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n");
- goto err_cleanup_hid_hw;
- }
-
- error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);
- if (error) {
- hid_err(hdev, "failed to create sysfs attributes\n");
- goto err_cleanup_hid_ll;
- }
-
- error = device_create_file(&hdev->dev, &dev_attr_operation_mode);
- if (error) {
- hid_err(hdev, "failed to create sysfs attributes\n");
- goto err_cleanup_sysfs1;
- }
-
- if (data->status & PICOLCD_BOOTLOADER)
- error = picolcd_probe_bootloader(hdev, data);
- else
- error = picolcd_probe_lcd(hdev, data);
- if (error)
- goto err_cleanup_sysfs2;
-
- dbg_hid(PICOLCD_NAME " activated and initialized\n");
- return 0;
-
-err_cleanup_sysfs2:
- device_remove_file(&hdev->dev, &dev_attr_operation_mode);
-err_cleanup_sysfs1:
- device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
-err_cleanup_hid_ll:
- hid_hw_close(hdev);
-err_cleanup_hid_hw:
- hid_hw_stop(hdev);
-err_cleanup_data:
- kfree(data);
-err_no_cleanup:
- hid_set_drvdata(hdev, NULL);
-
- return error;
-}
-
-static void picolcd_remove(struct hid_device *hdev)
-{
- struct picolcd_data *data = hid_get_drvdata(hdev);
- unsigned long flags;
-
- dbg_hid(PICOLCD_NAME " hardware remove...\n");
- spin_lock_irqsave(&data->lock, flags);
- data->status |= PICOLCD_FAILED;
- spin_unlock_irqrestore(&data->lock, flags);
-#ifdef CONFIG_HID_PICOLCD_FB
- /* short-circuit FB as early as possible in order to
- * avoid long delays if we host console.
- */
- if (data->fb_info)
- data->fb_info->par = NULL;
-#endif
-
- picolcd_exit_devfs(data);
- device_remove_file(&hdev->dev, &dev_attr_operation_mode);
- device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
- hid_hw_close(hdev);
- hid_hw_stop(hdev);
- hid_set_drvdata(hdev, NULL);
-
- /* Shortcut potential pending reply that will never arrive */
- spin_lock_irqsave(&data->lock, flags);
- if (data->pending)
- complete(&data->pending->ready);
- spin_unlock_irqrestore(&data->lock, flags);
-
- /* Cleanup LED */
- picolcd_exit_leds(data);
- /* Clean up the framebuffer */
- picolcd_exit_backlight(data);
- picolcd_exit_lcd(data);
- picolcd_exit_framebuffer(data);
- /* Cleanup input */
- picolcd_exit_cir(data);
- picolcd_exit_keys(data);
-
- mutex_destroy(&data->mutex);
- /* Finally, clean up the picolcd data itself */
- kfree(data);
-}
-
-static const struct hid_device_id picolcd_devices[] = {
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
- { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
- { }
-};
-MODULE_DEVICE_TABLE(hid, picolcd_devices);
-
-static struct hid_driver picolcd_driver = {
- .name = "hid-picolcd",
- .id_table = picolcd_devices,
- .probe = picolcd_probe,
- .remove = picolcd_remove,
- .raw_event = picolcd_raw_event,
-#ifdef CONFIG_PM
- .suspend = picolcd_suspend,
- .resume = picolcd_resume,
- .reset_resume = picolcd_reset_resume,
-#endif
-};
-
-static int __init picolcd_init(void)
-{
- return hid_register_driver(&picolcd_driver);
-}
-
-static void __exit picolcd_exit(void)
-{
- hid_unregister_driver(&picolcd_driver);
-#ifdef CONFIG_HID_PICOLCD_FB
- flush_work_sync(&picolcd_fb_cleanup);
- WARN_ON(fb_pending);
-#endif
-}
-
-module_init(picolcd_init);
-module_exit(picolcd_exit);
-MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-picolcd.h b/drivers/hid/hid-picolcd.h
new file mode 100644
index 000000000000..020cef69f6a1
--- /dev/null
+++ b/drivers/hid/hid-picolcd.h
@@ -0,0 +1,309 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * This program is free software: 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 driver is distributed in the hope that it will be useful, but *
+ * WITHOUT 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#define PICOLCD_NAME "PicoLCD (graphic)"
+
+/* Report numbers */
+#define REPORT_ERROR_CODE 0x10 /* LCD: IN[16] */
+#define ERR_SUCCESS 0x00
+#define ERR_PARAMETER_MISSING 0x01
+#define ERR_DATA_MISSING 0x02
+#define ERR_BLOCK_READ_ONLY 0x03
+#define ERR_BLOCK_NOT_ERASABLE 0x04
+#define ERR_BLOCK_TOO_BIG 0x05
+#define ERR_SECTION_OVERFLOW 0x06
+#define ERR_INVALID_CMD_LEN 0x07
+#define ERR_INVALID_DATA_LEN 0x08
+#define REPORT_KEY_STATE 0x11 /* LCD: IN[2] */
+#define REPORT_IR_DATA 0x21 /* LCD: IN[63] */
+#define REPORT_EE_DATA 0x32 /* LCD: IN[63] */
+#define REPORT_MEMORY 0x41 /* LCD: IN[63] */
+#define REPORT_LED_STATE 0x81 /* LCD: OUT[1] */
+#define REPORT_BRIGHTNESS 0x91 /* LCD: OUT[1] */
+#define REPORT_CONTRAST 0x92 /* LCD: OUT[1] */
+#define REPORT_RESET 0x93 /* LCD: OUT[2] */
+#define REPORT_LCD_CMD 0x94 /* LCD: OUT[63] */
+#define REPORT_LCD_DATA 0x95 /* LCD: OUT[63] */
+#define REPORT_LCD_CMD_DATA 0x96 /* LCD: OUT[63] */
+#define REPORT_EE_READ 0xa3 /* LCD: OUT[63] */
+#define REPORT_EE_WRITE 0xa4 /* LCD: OUT[63] */
+#define REPORT_ERASE_MEMORY 0xb2 /* LCD: OUT[2] */
+#define REPORT_READ_MEMORY 0xb3 /* LCD: OUT[3] */
+#define REPORT_WRITE_MEMORY 0xb4 /* LCD: OUT[63] */
+#define REPORT_SPLASH_RESTART 0xc1 /* LCD: OUT[1] */
+#define REPORT_EXIT_KEYBOARD 0xef /* LCD: OUT[2] */
+#define REPORT_VERSION 0xf1 /* LCD: IN[2],OUT[1] Bootloader: IN[2],OUT[1] */
+#define REPORT_BL_ERASE_MEMORY 0xf2 /* Bootloader: IN[36],OUT[4] */
+#define REPORT_BL_READ_MEMORY 0xf3 /* Bootloader: IN[36],OUT[4] */
+#define REPORT_BL_WRITE_MEMORY 0xf4 /* Bootloader: IN[36],OUT[36] */
+#define REPORT_DEVID 0xf5 /* LCD: IN[5], OUT[1] Bootloader: IN[5],OUT[1] */
+#define REPORT_SPLASH_SIZE 0xf6 /* LCD: IN[4], OUT[1] */
+#define REPORT_HOOK_VERSION 0xf7 /* LCD: IN[2], OUT[1] */
+#define REPORT_EXIT_FLASHER 0xff /* Bootloader: OUT[2] */
+
+/* Description of in-progress IO operation, used for operations
+ * that trigger response from device */
+struct picolcd_pending {
+ struct hid_report *out_report;
+ struct hid_report *in_report;
+ struct completion ready;
+ int raw_size;
+ u8 raw_data[64];
+};
+
+
+#define PICOLCD_KEYS 17
+
+/* Per device data structure */
+struct picolcd_data {
+ struct hid_device *hdev;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debug_reset;
+ struct dentry *debug_eeprom;
+ struct dentry *debug_flash;
+ struct mutex mutex_flash;
+ int addr_sz;
+#endif
+ u8 version[2];
+ unsigned short opmode_delay;
+ /* input stuff */
+ u8 pressed_keys[2];
+ struct input_dev *input_keys;
+#ifdef CONFIG_HID_PICOLCD_CIR
+ struct rc_dev *rc_dev;
+#endif
+ unsigned short keycode[PICOLCD_KEYS];
+
+#ifdef CONFIG_HID_PICOLCD_FB
+ /* Framebuffer stuff */
+ struct fb_info *fb_info;
+#endif /* CONFIG_HID_PICOLCD_FB */
+#ifdef CONFIG_HID_PICOLCD_LCD
+ struct lcd_device *lcd;
+ u8 lcd_contrast;
+#endif /* CONFIG_HID_PICOLCD_LCD */
+#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+ struct backlight_device *backlight;
+ u8 lcd_brightness;
+ u8 lcd_power;
+#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
+#ifdef CONFIG_HID_PICOLCD_LEDS
+ /* LED stuff */
+ u8 led_state;
+ struct led_classdev *led[8];
+#endif /* CONFIG_HID_PICOLCD_LEDS */
+
+ /* Housekeeping stuff */
+ spinlock_t lock;
+ struct mutex mutex;
+ struct picolcd_pending *pending;
+ int status;
+#define PICOLCD_BOOTLOADER 1
+#define PICOLCD_FAILED 2
+#define PICOLCD_CIR_SHUN 4
+};
+
+#ifdef CONFIG_HID_PICOLCD_FB
+struct picolcd_fb_data {
+ /* Framebuffer stuff */
+ spinlock_t lock;
+ struct picolcd_data *picolcd;
+ u8 update_rate;
+ u8 bpp;
+ u8 force;
+ u8 ready;
+ u8 *vbitmap; /* local copy of what was sent to PicoLCD */
+ u8 *bitmap; /* framebuffer */
+};
+#endif /* CONFIG_HID_PICOLCD_FB */
+
+/* Find a given report */
+#define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT)
+#define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT)
+
+struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir);
+
+#ifdef CONFIG_DEBUG_FS
+void picolcd_debug_out_report(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report);
+#define usbhid_submit_report(a, b, c) \
+ do { \
+ picolcd_debug_out_report(hid_get_drvdata(a), a, b); \
+ usbhid_submit_report(a, b, c); \
+ } while (0)
+
+void picolcd_debug_raw_event(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report,
+ u8 *raw_data, int size);
+
+void picolcd_init_devfs(struct picolcd_data *data,
+ struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+ struct hid_report *flash_r, struct hid_report *flash_w,
+ struct hid_report *reset);
+
+void picolcd_exit_devfs(struct picolcd_data *data);
+#else
+static inline void picolcd_debug_out_report(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report)
+{
+}
+static inline void picolcd_debug_raw_event(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report,
+ u8 *raw_data, int size)
+{
+}
+static inline void picolcd_init_devfs(struct picolcd_data *data,
+ struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+ struct hid_report *flash_r, struct hid_report *flash_w,
+ struct hid_report *reset)
+{
+}
+static inline void picolcd_exit_devfs(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+
+#ifdef CONFIG_HID_PICOLCD_FB
+int picolcd_fb_reset(struct picolcd_data *data, int clear);
+
+int picolcd_init_framebuffer(struct picolcd_data *data);
+
+void picolcd_exit_framebuffer(struct picolcd_data *data);
+
+void picolcd_fb_refresh(struct picolcd_data *data);
+#define picolcd_fbinfo(d) ((d)->fb_info)
+#else
+static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)
+{
+ return 0;
+}
+static inline int picolcd_init_framebuffer(struct picolcd_data *data)
+{
+ return 0;
+}
+static inline void picolcd_exit_framebuffer(struct picolcd_data *data)
+{
+}
+static inline void picolcd_fb_refresh(struct picolcd_data *data)
+{
+}
+#define picolcd_fbinfo(d) NULL
+#endif /* CONFIG_HID_PICOLCD_FB */
+
+
+#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+int picolcd_init_backlight(struct picolcd_data *data,
+ struct hid_report *report);
+
+void picolcd_exit_backlight(struct picolcd_data *data);
+
+int picolcd_resume_backlight(struct picolcd_data *data);
+
+void picolcd_suspend_backlight(struct picolcd_data *data);
+#else
+static inline int picolcd_init_backlight(struct picolcd_data *data,
+ struct hid_report *report)
+{
+ return 0;
+}
+static inline void picolcd_exit_backlight(struct picolcd_data *data)
+{
+}
+static inline int picolcd_resume_backlight(struct picolcd_data *data)
+{
+ return 0;
+}
+static inline void picolcd_suspend_backlight(struct picolcd_data *data)
+{
+}
+
+#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
+
+
+#ifdef CONFIG_HID_PICOLCD_LCD
+int picolcd_init_lcd(struct picolcd_data *data,
+ struct hid_report *report);
+
+void picolcd_exit_lcd(struct picolcd_data *data);
+
+int picolcd_resume_lcd(struct picolcd_data *data);
+#else
+static inline int picolcd_init_lcd(struct picolcd_data *data,
+ struct hid_report *report)
+{
+ return 0;
+}
+static inline void picolcd_exit_lcd(struct picolcd_data *data)
+{
+}
+static inline int picolcd_resume_lcd(struct picolcd_data *data)
+{
+ return 0;
+}
+#endif /* CONFIG_HID_PICOLCD_LCD */
+
+
+#ifdef CONFIG_HID_PICOLCD_LEDS
+int picolcd_init_leds(struct picolcd_data *data,
+ struct hid_report *report);
+
+void picolcd_exit_leds(struct picolcd_data *data);
+
+void picolcd_leds_set(struct picolcd_data *data);
+#else
+static inline int picolcd_init_leds(struct picolcd_data *data,
+ struct hid_report *report)
+{
+ return 0;
+}
+static inline void picolcd_exit_leds(struct picolcd_data *data)
+{
+}
+static inline void picolcd_leds_set(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_HID_PICOLCD_LEDS */
+
+
+#ifdef CONFIG_HID_PICOLCD_CIR
+int picolcd_raw_cir(struct picolcd_data *data,
+ struct hid_report *report, u8 *raw_data, int size);
+
+int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report);
+
+void picolcd_exit_cir(struct picolcd_data *data);
+#else
+static inline int picolcd_raw_cir(struct picolcd_data *data,
+ struct hid_report *report, u8 *raw_data, int size)
+{
+ return 1;
+}
+static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
+{
+ return 0;
+}
+static inline void picolcd_exit_cir(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_HID_PICOLCD_LIRC */
+
+int picolcd_reset(struct hid_device *hdev);
+struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
+ int report_id, const u8 *raw_data, int size);
diff --git a/drivers/hid/hid-picolcd_backlight.c b/drivers/hid/hid-picolcd_backlight.c
new file mode 100644
index 000000000000..b91f30945f9c
--- /dev/null
+++ b/drivers/hid/hid-picolcd_backlight.c
@@ -0,0 +1,122 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * This program is free software: 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 driver is distributed in the hope that it will be useful, but *
+ * WITHOUT 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/backlight.h>
+
+#include "hid-picolcd.h"
+
+static int picolcd_get_brightness(struct backlight_device *bdev)
+{
+ struct picolcd_data *data = bl_get_data(bdev);
+ return data->lcd_brightness;
+}
+
+static int picolcd_set_brightness(struct backlight_device *bdev)
+{
+ struct picolcd_data *data = bl_get_data(bdev);
+ struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev);
+ unsigned long flags;
+
+ if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+ return -ENODEV;
+
+ data->lcd_brightness = bdev->props.brightness & 0x0ff;
+ data->lcd_power = bdev->props.power;
+ spin_lock_irqsave(&data->lock, flags);
+ hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
+ if (!(data->status & PICOLCD_FAILED))
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+ return 0;
+}
+
+static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb)
+{
+ return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev));
+}
+
+static const struct backlight_ops picolcd_blops = {
+ .update_status = picolcd_set_brightness,
+ .get_brightness = picolcd_get_brightness,
+ .check_fb = picolcd_check_bl_fb,
+};
+
+int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report)
+{
+ struct device *dev = &data->hdev->dev;
+ struct backlight_device *bdev;
+ struct backlight_properties props;
+ if (!report)
+ return -ENODEV;
+ if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+ report->field[0]->report_size != 8) {
+ dev_err(dev, "unsupported BRIGHTNESS report");
+ return -EINVAL;
+ }
+
+ memset(&props, 0, sizeof(props));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = 0xff;
+ bdev = backlight_device_register(dev_name(dev), dev, data,
+ &picolcd_blops, &props);
+ if (IS_ERR(bdev)) {
+ dev_err(dev, "failed to register backlight\n");
+ return PTR_ERR(bdev);
+ }
+ bdev->props.brightness = 0xff;
+ data->lcd_brightness = 0xff;
+ data->backlight = bdev;
+ picolcd_set_brightness(bdev);
+ return 0;
+}
+
+void picolcd_exit_backlight(struct picolcd_data *data)
+{
+ struct backlight_device *bdev = data->backlight;
+
+ data->backlight = NULL;
+ if (bdev)
+ backlight_device_unregister(bdev);
+}
+
+int picolcd_resume_backlight(struct picolcd_data *data)
+{
+ if (!data->backlight)
+ return 0;
+ return picolcd_set_brightness(data->backlight);
+}
+
+#ifdef CONFIG_PM
+void picolcd_suspend_backlight(struct picolcd_data *data)
+{
+ int bl_power = data->lcd_power;
+ if (!data->backlight)
+ return;
+
+ data->backlight->props.power = FB_BLANK_POWERDOWN;
+ picolcd_set_brightness(data->backlight);
+ data->lcd_power = data->backlight->props.power = bl_power;
+}
+#endif /* CONFIG_PM */
+
diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
new file mode 100644
index 000000000000..13ca9191b630
--- /dev/null
+++ b/drivers/hid/hid-picolcd_cir.c
@@ -0,0 +1,152 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * This program is free software: 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 driver is distributed in the hope that it will be useful, but *
+ * WITHOUT 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+
+#include <linux/leds.h>
+
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <media/rc-core.h>
+
+#include "hid-picolcd.h"
+
+
+int picolcd_raw_cir(struct picolcd_data *data,
+ struct hid_report *report, u8 *raw_data, int size)
+{
+ unsigned long flags;
+ int i, w, sz;
+ DEFINE_IR_RAW_EVENT(rawir);
+
+ /* ignore if rc_dev is NULL or status is shunned */
+ spin_lock_irqsave(&data->lock, flags);
+ if (!data->rc_dev || (data->status & PICOLCD_CIR_SHUN)) {
+ spin_unlock_irqrestore(&data->lock, flags);
+ return 1;
+ }
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* PicoLCD USB packets contain 16-bit intervals in network order,
+ * with value negated for pulse. Intervals are in microseconds.
+ *
+ * Note: some userspace LIRC code for PicoLCD says negated values
+ * for space - is it a matter of IR chip? (pulse for my TSOP2236)
+ *
+ * In addition, the first interval seems to be around 15000 + base
+ * interval for non-first report of IR data - thus the quirk below
+ * to get RC_CODE to understand Sony and JVC remotes I have at hand
+ */
+ sz = size > 0 ? min((int)raw_data[0], size-1) : 0;
+ for (i = 0; i+1 < sz; i += 2) {
+ init_ir_raw_event(&rawir);
+ w = (raw_data[i] << 8) | (raw_data[i+1]);
+ rawir.pulse = !!(w & 0x8000);
+ rawir.duration = US_TO_NS(rawir.pulse ? (65536 - w) : w);
+ /* Quirk!! - see above */
+ if (i == 0 && rawir.duration > 15000000)
+ rawir.duration -= 15000000;
+ ir_raw_event_store(data->rc_dev, &rawir);
+ }
+ ir_raw_event_handle(data->rc_dev);
+
+ return 1;
+}
+
+static int picolcd_cir_open(struct rc_dev *dev)
+{
+ struct picolcd_data *data = dev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+ data->status &= ~PICOLCD_CIR_SHUN;
+ spin_unlock_irqrestore(&data->lock, flags);
+ return 0;
+}
+
+static void picolcd_cir_close(struct rc_dev *dev)
+{
+ struct picolcd_data *data = dev->priv;
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+ data->status |= PICOLCD_CIR_SHUN;
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+/* initialize CIR input device */
+int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
+{
+ struct rc_dev *rdev;
+ int ret = 0;
+
+ rdev = rc_allocate_device();
+ if (!rdev)
+ return -ENOMEM;
+
+ rdev->priv = data;
+ rdev->driver_type = RC_DRIVER_IR_RAW;
+ rdev->allowed_protos = RC_TYPE_ALL;
+ rdev->open = picolcd_cir_open;
+ rdev->close = picolcd_cir_close;
+ rdev->input_name = data->hdev->name;
+ rdev->input_phys = data->hdev->phys;
+ rdev->input_id.bustype = data->hdev->bus;
+ rdev->input_id.vendor = data->hdev->vendor;
+ rdev->input_id.product = data->hdev->product;
+ rdev->input_id.version = data->hdev->version;
+ rdev->dev.parent = &data->hdev->dev;
+ rdev->driver_name = PICOLCD_NAME;
+ rdev->map_name = RC_MAP_RC6_MCE;
+ rdev->timeout = MS_TO_NS(100);
+ rdev->rx_resolution = US_TO_NS(1);
+
+ ret = rc_register_device(rdev);
+ if (ret)
+ goto err;
+ data->rc_dev = rdev;
+ return 0;
+
+err:
+ rc_free_device(rdev);
+ return ret;
+}
+
+void picolcd_exit_cir(struct picolcd_data *data)
+{
+ struct rc_dev *rdev = data->rc_dev;
+
+ data->rc_dev = NULL;
+ rc_unregister_device(rdev);
+}
+
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c
new file mode 100644
index 000000000000..86df26e58aba
--- /dev/null
+++ b/drivers/hid/hid-picolcd_core.c
@@ -0,0 +1,689 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * This program is free software: 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 driver is distributed in the hope that it will be useful, but *
+ * WITHOUT 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+
+/* Input device
+ *
+ * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
+ * and header for 4x4 key matrix. The built-in keys are part of the matrix.
+ */
+static const unsigned short def_keymap[PICOLCD_KEYS] = {
+ KEY_RESERVED, /* none */
+ KEY_BACK, /* col 4 + row 1 */
+ KEY_HOMEPAGE, /* col 3 + row 1 */
+ KEY_RESERVED, /* col 2 + row 1 */
+ KEY_RESERVED, /* col 1 + row 1 */
+ KEY_SCROLLUP, /* col 4 + row 2 */
+ KEY_OK, /* col 3 + row 2 */
+ KEY_SCROLLDOWN, /* col 2 + row 2 */
+ KEY_RESERVED, /* col 1 + row 2 */
+ KEY_RESERVED, /* col 4 + row 3 */
+ KEY_RESERVED, /* col 3 + row 3 */
+ KEY_RESERVED, /* col 2 + row 3 */
+ KEY_RESERVED, /* col 1 + row 3 */
+ KEY_RESERVED, /* col 4 + row 4 */
+ KEY_RESERVED, /* col 3 + row 4 */
+ KEY_RESERVED, /* col 2 + row 4 */
+ KEY_RESERVED, /* col 1 + row 4 */
+};
+
+
+/* Find a given report */
+struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)
+{
+ struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;
+ struct hid_report *report = NULL;
+
+ list_for_each_entry(report, feature_report_list, list) {
+ if (report->id == id)
+ return report;
+ }
+ hid_warn(hdev, "No report with id 0x%x found\n", id);
+ return NULL;
+}
+
+/* Submit a report and wait for a reply from device - if device fades away
+ * or does not respond in time, return NULL */
+struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
+ int report_id, const u8 *raw_data, int size)
+{
+ struct picolcd_data *data = hid_get_drvdata(hdev);
+ struct picolcd_pending *work;
+ struct hid_report *report = picolcd_out_report(report_id, hdev);
+ unsigned long flags;
+ int i, j, k;
+
+ if (!report || !data)
+ return NULL;
+ if (data->status & PICOLCD_FAILED)
+ return NULL;
+ work = kzalloc(sizeof(*work), GFP_KERNEL);
+ if (!work)
+ return NULL;
+
+ init_completion(&work->ready);
+ work->out_report = report;
+ work->in_report = NULL;
+ work->raw_size = 0;
+
+ mutex_lock(&data->mutex);
+ spin_lock_irqsave(&data->lock, flags);
+ for (i = k = 0; i < report->maxfield; i++)
+ for (j = 0; j < report->field[i]->report_count; j++) {
+ hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
+ k++;
+ }
+ if (data->status & PICOLCD_FAILED) {
+ kfree(work);
+ work = NULL;
+ } else {
+ data->pending = work;
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+ wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
+ spin_lock_irqsave(&data->lock, flags);
+ data->pending = NULL;
+ }
+ spin_unlock_irqrestore(&data->lock, flags);
+ mutex_unlock(&data->mutex);
+ return work;
+}
+
+/*
+ * input class device
+ */
+static int picolcd_raw_keypad(struct picolcd_data *data,
+ struct hid_report *report, u8 *raw_data, int size)
+{
+ /*
+ * Keypad event
+ * First and second data bytes list currently pressed keys,
+ * 0x00 means no key and at most 2 keys may be pressed at same time
+ */
+ int i, j;
+
+ /* determine newly pressed keys */
+ for (i = 0; i < size; i++) {
+ unsigned int key_code;
+ if (raw_data[i] == 0)
+ continue;
+ for (j = 0; j < sizeof(data->pressed_keys); j++)
+ if (data->pressed_keys[j] == raw_data[i])
+ goto key_already_down;
+ for (j = 0; j < sizeof(data->pressed_keys); j++)
+ if (data->pressed_keys[j] == 0) {
+ data->pressed_keys[j] = raw_data[i];
+ break;
+ }
+ input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);
+ if (raw_data[i] < PICOLCD_KEYS)
+ key_code = data->keycode[raw_data[i]];
+ else
+ key_code = KEY_UNKNOWN;
+ if (key_code != KEY_UNKNOWN) {
+ dbg_hid(PICOLCD_NAME " got key press for %u:%d",
+ raw_data[i], key_code);
+ input_report_key(data->input_keys, key_code, 1);
+ }
+ input_sync(data->input_keys);
+key_already_down:
+ continue;
+ }
+
+ /* determine newly released keys */
+ for (j = 0; j < sizeof(data->pressed_keys); j++) {
+ unsigned int key_code;
+ if (data->pressed_keys[j] == 0)
+ continue;
+ for (i = 0; i < size; i++)
+ if (data->pressed_keys[j] == raw_data[i])
+ goto key_still_down;
+ input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);
+ if (data->pressed_keys[j] < PICOLCD_KEYS)
+ key_code = data->keycode[data->pressed_keys[j]];
+ else
+ key_code = KEY_UNKNOWN;
+ if (key_code != KEY_UNKNOWN) {
+ dbg_hid(PICOLCD_NAME " got key release for %u:%d",
+ data->pressed_keys[j], key_code);
+ input_report_key(data->input_keys, key_code, 0);
+ }
+ input_sync(data->input_keys);
+ data->pressed_keys[j] = 0;
+key_still_down:
+ continue;
+ }
+ return 1;
+}
+
+static int picolcd_check_version(struct hid_device *hdev)
+{
+ struct picolcd_data *data = hid_get_drvdata(hdev);
+ struct picolcd_pending *verinfo;
+ int ret = 0;
+
+ if (!data)
+ return -ENODEV;
+
+ verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);
+ if (!verinfo) {
+ hid_err(hdev, "no version response from PicoLCD\n");
+ return -ENODEV;
+ }
+
+ if (verinfo->raw_size == 2) {
+ data->version[0] = verinfo->raw_data[1];
+ data->version[1] = verinfo->raw_data[0];
+ if (data->status & PICOLCD_BOOTLOADER) {
+ hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",
+ verinfo->raw_data[1], verinfo->raw_data[0]);
+ } else {
+ hid_info(hdev, "PicoLCD, firmware version %d.%d\n",
+ verinfo->raw_data[1], verinfo->raw_data[0]);
+ }
+ } else {
+ hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");
+ ret = -EINVAL;
+ }
+ kfree(verinfo);
+ return ret;
+}
+
+/*
+ * Reset our device and wait for answer to VERSION request
+ */
+int picolcd_reset(struct hid_device *hdev)
+{
+ struct picolcd_data *data = hid_get_drvdata(hdev);
+ struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);
+ unsigned long flags;
+ int error;
+
+ if (!data || !report || report->maxfield != 1)
+ return -ENODEV;
+
+ spin_lock_irqsave(&data->lock, flags);
+ if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
+ data->status |= PICOLCD_BOOTLOADER;
+
+ /* perform the reset */
+ hid_set_field(report->field[0], 0, 1);
+ if (data->status & PICOLCD_FAILED) {
+ spin_unlock_irqrestore(&data->lock, flags);
+ return -ENODEV;
+ }
+ usbhid_submit_report(hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ error = picolcd_check_version(hdev);
+ if (error)
+ return error;
+
+ picolcd_resume_lcd(data);
+ picolcd_resume_backlight(data);
+ picolcd_fb_refresh(data);
+ picolcd_leds_set(data);
+ return 0;
+}
+
+/*
+ * The "operation_mode" sysfs attribute
+ */
+static ssize_t picolcd_operation_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+
+ if (data->status & PICOLCD_BOOTLOADER)
+ return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");
+ else
+ return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n");
+}
+
+static ssize_t picolcd_operation_mode_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+ struct hid_report *report = NULL;
+ size_t cnt = count;
+ int timeout = data->opmode_delay;
+ unsigned long flags;
+
+ if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) {
+ if (data->status & PICOLCD_BOOTLOADER)
+ report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);
+ buf += 3;
+ cnt -= 3;
+ } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) {
+ if (!(data->status & PICOLCD_BOOTLOADER))
+ report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);
+ buf += 10;
+ cnt -= 10;
+ }
+ if (!report)
+ return -EINVAL;
+
+ while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))
+ cnt--;
+ if (cnt != 0)
+ return -EINVAL;
+
+ spin_lock_irqsave(&data->lock, flags);
+ hid_set_field(report->field[0], 0, timeout & 0xff);
+ hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+ return count;
+}
+
+static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,
+ picolcd_operation_mode_store);
+
+/*
+ * The "operation_mode_delay" sysfs attribute
+ */
+static ssize_t picolcd_operation_mode_delay_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay);
+}
+
+static ssize_t picolcd_operation_mode_delay_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+ unsigned u;
+ if (sscanf(buf, "%u", &u) != 1)
+ return -EINVAL;
+ if (u > 30000)
+ return -EINVAL;
+ else
+ data->opmode_delay = u;
+ return count;
+}
+
+static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,
+ picolcd_operation_mode_delay_store);
+
+/*
+ * Handle raw report as sent by device
+ */
+static int picolcd_raw_event(struct hid_device *hdev,
+ struct hid_report *report, u8 *raw_data, int size)
+{
+ struct picolcd_data *data = hid_get_drvdata(hdev);
+ unsigned long flags;
+ int ret = 0;
+
+ if (!data)
+ return 1;
+
+ if (report->id == REPORT_KEY_STATE) {
+ if (data->input_keys)
+ ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);
+ } else if (report->id == REPORT_IR_DATA) {
+ ret = picolcd_raw_cir(data, report, raw_data+1, size-1);
+ } else {
+ spin_lock_irqsave(&data->lock, flags);
+ /*
+ * We let the caller of picolcd_send_and_wait() check if the
+ * report we got is one of the expected ones or not.
+ */
+ if (data->pending) {
+ memcpy(data->pending->raw_data, raw_data+1, size-1);
+ data->pending->raw_size = size-1;
+ data->pending->in_report = report;
+ complete(&data->pending->ready);
+ }
+ spin_unlock_irqrestore(&data->lock, flags);
+ }
+
+ picolcd_debug_raw_event(data, hdev, report, raw_data, size);
+ return 1;
+}
+
+#ifdef CONFIG_PM
+static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
+{
+ if (PMSG_IS_AUTO(message))
+ return 0;
+
+ picolcd_suspend_backlight(hid_get_drvdata(hdev));
+ dbg_hid(PICOLCD_NAME " device ready for suspend\n");
+ return 0;
+}
+
+static int picolcd_resume(struct hid_device *hdev)
+{
+ int ret;
+ ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
+ if (ret)
+ dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
+ return 0;
+}
+
+static int picolcd_reset_resume(struct hid_device *hdev)
+{
+ int ret;
+ ret = picolcd_reset(hdev);
+ if (ret)
+ dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);
+ ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);
+ if (ret)
+ dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);
+ ret = picolcd_resume_lcd(hid_get_drvdata(hdev));
+ if (ret)
+ dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);
+ ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
+ if (ret)
+ dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
+ picolcd_leds_set(hid_get_drvdata(hdev));
+ return 0;
+}
+#endif
+
+/* initialize keypad input device */
+static int picolcd_init_keys(struct picolcd_data *data,
+ struct hid_report *report)
+{
+ struct hid_device *hdev = data->hdev;
+ struct input_dev *idev;
+ int error, i;
+
+ if (!report)
+ return -ENODEV;
+ if (report->maxfield != 1 || report->field[0]->report_count != 2 ||
+ report->field[0]->report_size != 8) {
+ hid_err(hdev, "unsupported KEY_STATE report\n");
+ return -EINVAL;
+ }
+
+ idev = input_allocate_device();
+ if (idev == NULL) {
+ hid_err(hdev, "failed to allocate input device\n");
+ return -ENOMEM;
+ }
+ input_set_drvdata(idev, hdev);
+ memcpy(data->keycode, def_keymap, sizeof(def_keymap));
+ idev->name = hdev->name;
+ idev->phys = hdev->phys;
+ idev->uniq = hdev->uniq;
+ idev->id.bustype = hdev->bus;
+ idev->id.vendor = hdev->vendor;
+ idev->id.product = hdev->product;
+ idev->id.version = hdev->version;
+ idev->dev.parent = &hdev->dev;
+ idev->keycode = &data->keycode;
+ idev->keycodemax = PICOLCD_KEYS;
+ idev->keycodesize = sizeof(data->keycode[0]);
+ input_set_capability(idev, EV_MSC, MSC_SCAN);
+ set_bit(EV_REP, idev->evbit);
+ for (i = 0; i < PICOLCD_KEYS; i++)
+ input_set_capability(idev, EV_KEY, data->keycode[i]);
+ error = input_register_device(idev);
+ if (error) {
+ hid_err(hdev, "error registering the input device\n");
+ input_free_device(idev);
+ return error;
+ }
+ data->input_keys = idev;
+ return 0;
+}
+
+static void picolcd_exit_keys(struct picolcd_data *data)
+{
+ struct input_dev *idev = data->input_keys;
+
+ data->input_keys = NULL;
+ if (idev)
+ input_unregister_device(idev);
+}
+
+static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
+{
+ int error;
+
+ /* Setup keypad input device */
+ error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));
+ if (error)
+ goto err;
+
+ /* Setup CIR input device */
+ error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));
+ if (error)
+ goto err;
+
+ /* Set up the framebuffer device */
+ error = picolcd_init_framebuffer(data);
+ if (error)
+ goto err;
+
+ /* Setup lcd class device */
+ error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));
+ if (error)
+ goto err;
+
+ /* Setup backlight class device */
+ error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));
+ if (error)
+ goto err;
+
+ /* Setup the LED class devices */
+ error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
+ if (error)
+ goto err;
+
+ picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),
+ picolcd_out_report(REPORT_EE_WRITE, hdev),
+ picolcd_out_report(REPORT_READ_MEMORY, hdev),
+ picolcd_out_report(REPORT_WRITE_MEMORY, hdev),
+ picolcd_out_report(REPORT_RESET, hdev));
+ return 0;
+err:
+ picolcd_exit_leds(data);
+ picolcd_exit_backlight(data);
+ picolcd_exit_lcd(data);
+ picolcd_exit_framebuffer(data);
+ picolcd_exit_cir(data);
+ picolcd_exit_keys(data);
+ return error;
+}
+
+static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
+{
+ picolcd_init_devfs(data, NULL, NULL,
+ picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
+ picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);
+ return 0;
+}
+
+static int picolcd_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ struct picolcd_data *data;
+ int error = -ENOMEM;
+
+ dbg_hid(PICOLCD_NAME " hardware probe...\n");
+
+ /*
+ * Let's allocate the picolcd data structure, set some reasonable
+ * defaults, and associate it with the device
+ */
+ data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);
+ if (data == NULL) {
+ hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n");
+ error = -ENOMEM;
+ goto err_no_cleanup;
+ }
+
+ spin_lock_init(&data->lock);
+ mutex_init(&data->mutex);
+ data->hdev = hdev;
+ data->opmode_delay = 5000;
+ if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
+ data->status |= PICOLCD_BOOTLOADER;
+ hid_set_drvdata(hdev, data);
+
+ /* Parse the device reports and start it up */
+ error = hid_parse(hdev);
+ if (error) {
+ hid_err(hdev, "device report parse failed\n");
+ goto err_cleanup_data;
+ }
+
+ error = hid_hw_start(hdev, 0);
+ if (error) {
+ hid_err(hdev, "hardware start failed\n");
+ goto err_cleanup_data;
+ }
+
+ error = hid_hw_open(hdev);
+ if (error) {
+ hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n");
+ goto err_cleanup_hid_hw;
+ }
+
+ error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);
+ if (error) {
+ hid_err(hdev, "failed to create sysfs attributes\n");
+ goto err_cleanup_hid_ll;
+ }
+
+ error = device_create_file(&hdev->dev, &dev_attr_operation_mode);
+ if (error) {
+ hid_err(hdev, "failed to create sysfs attributes\n");
+ goto err_cleanup_sysfs1;
+ }
+
+ if (data->status & PICOLCD_BOOTLOADER)
+ error = picolcd_probe_bootloader(hdev, data);
+ else
+ error = picolcd_probe_lcd(hdev, data);
+ if (error)
+ goto err_cleanup_sysfs2;
+
+ dbg_hid(PICOLCD_NAME " activated and initialized\n");
+ return 0;
+
+err_cleanup_sysfs2:
+ device_remove_file(&hdev->dev, &dev_attr_operation_mode);
+err_cleanup_sysfs1:
+ device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
+err_cleanup_hid_ll:
+ hid_hw_close(hdev);
+err_cleanup_hid_hw:
+ hid_hw_stop(hdev);
+err_cleanup_data:
+ kfree(data);
+err_no_cleanup:
+ hid_set_drvdata(hdev, NULL);
+
+ return error;
+}
+
+static void picolcd_remove(struct hid_device *hdev)
+{
+ struct picolcd_data *data = hid_get_drvdata(hdev);
+ unsigned long flags;
+
+ dbg_hid(PICOLCD_NAME " hardware remove...\n");
+ spin_lock_irqsave(&data->lock, flags);
+ data->status |= PICOLCD_FAILED;
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ picolcd_exit_devfs(data);
+ device_remove_file(&hdev->dev, &dev_attr_operation_mode);
+ device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
+ hid_hw_close(hdev);
+ hid_hw_stop(hdev);
+
+ /* Shortcut potential pending reply that will never arrive */
+ spin_lock_irqsave(&data->lock, flags);
+ if (data->pending)
+ complete(&data->pending->ready);
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ /* Cleanup LED */
+ picolcd_exit_leds(data);
+ /* Clean up the framebuffer */
+ picolcd_exit_backlight(data);
+ picolcd_exit_lcd(data);
+ picolcd_exit_framebuffer(data);
+ /* Cleanup input */
+ picolcd_exit_cir(data);
+ picolcd_exit_keys(data);
+
+ hid_set_drvdata(hdev, NULL);
+ mutex_destroy(&data->mutex);
+ /* Finally, clean up the picolcd data itself */
+ kfree(data);
+}
+
+static const struct hid_device_id picolcd_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, picolcd_devices);
+
+static struct hid_driver picolcd_driver = {
+ .name = "hid-picolcd",
+ .id_table = picolcd_devices,
+ .probe = picolcd_probe,
+ .remove = picolcd_remove,
+ .raw_event = picolcd_raw_event,
+#ifdef CONFIG_PM
+ .suspend = picolcd_suspend,
+ .resume = picolcd_resume,
+ .reset_resume = picolcd_reset_resume,
+#endif
+};
+
+static int __init picolcd_init(void)
+{
+ return hid_register_driver(&picolcd_driver);
+}
+
+static void __exit picolcd_exit(void)
+{
+ hid_unregister_driver(&picolcd_driver);
+}
+
+module_init(picolcd_init);
+module_exit(picolcd_exit);
+MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c
new file mode 100644
index 000000000000..4809aa1bdb9c
--- /dev/null
+++ b/drivers/hid/hid-picolcd_debugfs.c
@@ -0,0 +1,899 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * This program is free software: 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 driver is distributed in the hope that it will be useful, but *
+ * WITHOUT 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+#include "hid-picolcd.h"
+
+
+static int picolcd_debug_reset_show(struct seq_file *f, void *p)
+{
+ if (picolcd_fbinfo((struct picolcd_data *)f->private))
+ seq_printf(f, "all fb\n");
+ else
+ seq_printf(f, "all\n");
+ return 0;
+}
+
+static int picolcd_debug_reset_open(struct inode *inode, struct file *f)
+{
+ return single_open(f, picolcd_debug_reset_show, inode->i_private);
+}
+
+static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
+ char buf[32];
+ size_t cnt = min(count, sizeof(buf)-1);
+ if (copy_from_user(buf, user_buf, cnt))
+ return -EFAULT;
+
+ while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n'))
+ cnt--;
+ buf[cnt] = '\0';
+ if (strcmp(buf, "all") == 0) {
+ picolcd_reset(data->hdev);
+ picolcd_fb_reset(data, 1);
+ } else if (strcmp(buf, "fb") == 0) {
+ picolcd_fb_reset(data, 1);
+ } else {
+ return -EINVAL;
+ }
+ return count;
+}
+
+static const struct file_operations picolcd_debug_reset_fops = {
+ .owner = THIS_MODULE,
+ .open = picolcd_debug_reset_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .write = picolcd_debug_reset_write,
+ .release = single_release,
+};
+
+/*
+ * The "eeprom" file
+ */
+static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
+ size_t s, loff_t *off)
+{
+ struct picolcd_data *data = f->private_data;
+ struct picolcd_pending *resp;
+ u8 raw_data[3];
+ ssize_t ret = -EIO;
+
+ if (s == 0)
+ return -EINVAL;
+ if (*off > 0x0ff)
+ return 0;
+
+ /* prepare buffer with info about what we want to read (addr & len) */
+ raw_data[0] = *off & 0xff;
+ raw_data[1] = (*off >> 8) & 0xff;
+ raw_data[2] = s < 20 ? s : 20;
+ if (*off + raw_data[2] > 0xff)
+ raw_data[2] = 0x100 - *off;
+ resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
+ sizeof(raw_data));
+ if (!resp)
+ return -EIO;
+
+ if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
+ /* successful read :) */
+ ret = resp->raw_data[2];
+ if (ret > s)
+ ret = s;
+ if (copy_to_user(u, resp->raw_data+3, ret))
+ ret = -EFAULT;
+ else
+ *off += ret;
+ } /* anything else is some kind of IO error */
+
+ kfree(resp);
+ return ret;
+}
+
+static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
+ size_t s, loff_t *off)
+{
+ struct picolcd_data *data = f->private_data;
+ struct picolcd_pending *resp;
+ ssize_t ret = -EIO;
+ u8 raw_data[23];
+
+ if (s == 0)
+ return -EINVAL;
+ if (*off > 0x0ff)
+ return -ENOSPC;
+
+ memset(raw_data, 0, sizeof(raw_data));
+ raw_data[0] = *off & 0xff;
+ raw_data[1] = (*off >> 8) & 0xff;
+ raw_data[2] = min_t(size_t, 20, s);
+ if (*off + raw_data[2] > 0xff)
+ raw_data[2] = 0x100 - *off;
+
+ if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
+ return -EFAULT;
+ resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
+ sizeof(raw_data));
+
+ if (!resp)
+ return -EIO;
+
+ if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
+ /* check if written data matches */
+ if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) {
+ *off += raw_data[2];
+ ret = raw_data[2];
+ }
+ }
+ kfree(resp);
+ return ret;
+}
+
+/*
+ * Notes:
+ * - read/write happens in chunks of at most 20 bytes, it's up to userspace
+ * to loop in order to get more data.
+ * - on write errors on otherwise correct write request the bytes
+ * that should have been written are in undefined state.
+ */
+static const struct file_operations picolcd_debug_eeprom_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = picolcd_debug_eeprom_read,
+ .write = picolcd_debug_eeprom_write,
+ .llseek = generic_file_llseek,
+};
+
+/*
+ * The "flash" file
+ */
+/* record a flash address to buf (bounds check to be done by caller) */
+static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
+{
+ buf[0] = off & 0xff;
+ buf[1] = (off >> 8) & 0xff;
+ if (data->addr_sz == 3)
+ buf[2] = (off >> 16) & 0xff;
+ return data->addr_sz == 2 ? 2 : 3;
+}
+
+/* read a given size of data (bounds check to be done by caller) */
+static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
+ char __user *u, size_t s, loff_t *off)
+{
+ struct picolcd_pending *resp;
+ u8 raw_data[4];
+ ssize_t ret = 0;
+ int len_off, err = -EIO;
+
+ while (s > 0) {
+ err = -EIO;
+ len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+ raw_data[len_off] = s > 32 ? 32 : s;
+ resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
+ if (!resp || !resp->in_report)
+ goto skip;
+ if (resp->in_report->id == REPORT_MEMORY ||
+ resp->in_report->id == REPORT_BL_READ_MEMORY) {
+ if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
+ goto skip;
+ if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
+ err = -EFAULT;
+ goto skip;
+ }
+ *off += raw_data[len_off];
+ s -= raw_data[len_off];
+ ret += raw_data[len_off];
+ err = 0;
+ }
+skip:
+ kfree(resp);
+ if (err)
+ return ret > 0 ? ret : err;
+ }
+ return ret;
+}
+
+static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
+ size_t s, loff_t *off)
+{
+ struct picolcd_data *data = f->private_data;
+
+ if (s == 0)
+ return -EINVAL;
+ if (*off > 0x05fff)
+ return 0;
+ if (*off + s > 0x05fff)
+ s = 0x06000 - *off;
+
+ if (data->status & PICOLCD_BOOTLOADER)
+ return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
+ else
+ return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
+}
+
+/* erase block aligned to 64bytes boundary */
+static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
+ loff_t *off)
+{
+ struct picolcd_pending *resp;
+ u8 raw_data[3];
+ int len_off;
+ ssize_t ret = -EIO;
+
+ if (*off & 0x3f)
+ return -EINVAL;
+
+ len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+ resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
+ if (!resp || !resp->in_report)
+ goto skip;
+ if (resp->in_report->id == REPORT_MEMORY ||
+ resp->in_report->id == REPORT_BL_ERASE_MEMORY) {
+ if (memcmp(raw_data, resp->raw_data, len_off) != 0)
+ goto skip;
+ ret = 0;
+ }
+skip:
+ kfree(resp);
+ return ret;
+}
+
+/* write a given size of data (bounds check to be done by caller) */
+static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
+ const char __user *u, size_t s, loff_t *off)
+{
+ struct picolcd_pending *resp;
+ u8 raw_data[36];
+ ssize_t ret = 0;
+ int len_off, err = -EIO;
+
+ while (s > 0) {
+ err = -EIO;
+ len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+ raw_data[len_off] = s > 32 ? 32 : s;
+ if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
+ err = -EFAULT;
+ break;
+ }
+ resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
+ len_off+1+raw_data[len_off]);
+ if (!resp || !resp->in_report)
+ goto skip;
+ if (resp->in_report->id == REPORT_MEMORY ||
+ resp->in_report->id == REPORT_BL_WRITE_MEMORY) {
+ if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
+ goto skip;
+ *off += raw_data[len_off];
+ s -= raw_data[len_off];
+ ret += raw_data[len_off];
+ err = 0;
+ }
+skip:
+ kfree(resp);
+ if (err)
+ break;
+ }
+ return ret > 0 ? ret : err;
+}
+
+static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
+ size_t s, loff_t *off)
+{
+ struct picolcd_data *data = f->private_data;
+ ssize_t err, ret = 0;
+ int report_erase, report_write;
+
+ if (s == 0)
+ return -EINVAL;
+ if (*off > 0x5fff)
+ return -ENOSPC;
+ if (s & 0x3f)
+ return -EINVAL;
+ if (*off & 0x3f)
+ return -EINVAL;
+
+ if (data->status & PICOLCD_BOOTLOADER) {
+ report_erase = REPORT_BL_ERASE_MEMORY;
+ report_write = REPORT_BL_WRITE_MEMORY;
+ } else {
+ report_erase = REPORT_ERASE_MEMORY;
+ report_write = REPORT_WRITE_MEMORY;
+ }
+ mutex_lock(&data->mutex_flash);
+ while (s > 0) {
+ err = _picolcd_flash_erase64(data, report_erase, off);
+ if (err)
+ break;
+ err = _picolcd_flash_write(data, report_write, u, 64, off);
+ if (err < 0)
+ break;
+ ret += err;
+ *off += err;
+ s -= err;
+ if (err != 64)
+ break;
+ }
+ mutex_unlock(&data->mutex_flash);
+ return ret > 0 ? ret : err;
+}
+
+/*
+ * Notes:
+ * - concurrent writing is prevented by mutex and all writes must be
+ * n*64 bytes and 64-byte aligned, each write being preceded by an
+ * ERASE which erases a 64byte block.
+ * If less than requested was written or an error is returned for an
+ * otherwise correct write request the next 64-byte block which should
+ * have been written is in undefined state (mostly: original, erased,
+ * (half-)written with write error)
+ * - reading can happen without special restriction
+ */
+static const struct file_operations picolcd_debug_flash_fops = {
+ .owner = THIS_MODULE,
+ .open = simple_open,
+ .read = picolcd_debug_flash_read,
+ .write = picolcd_debug_flash_write,
+ .llseek = generic_file_llseek,
+};
+
+
+/*
+ * Helper code for HID report level dumping/debugging
+ */
+static const char * const error_codes[] = {
+ "success", "parameter missing", "data_missing", "block readonly",
+ "block not erasable", "block too big", "section overflow",
+ "invalid command length", "invalid data length",
+};
+
+static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
+ const size_t data_len)
+{
+ int i, j;
+ for (i = j = 0; i < data_len && j + 4 < dst_sz; i++) {
+ dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];
+ dst[j++] = hex_asc[data[i] & 0x0f];
+ dst[j++] = ' ';
+ }
+ dst[j] = '\0';
+ if (j > 0)
+ dst[j-1] = '\n';
+ if (i < data_len && j > 2)
+ dst[j-2] = dst[j-3] = '.';
+}
+
+void picolcd_debug_out_report(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report)
+{
+ u8 raw_data[70];
+ int raw_size = (report->size >> 3) + 1;
+ char *buff;
+#define BUFF_SZ 256
+
+ /* Avoid unnecessary overhead if debugfs is disabled */
+ if (list_empty(&hdev->debug_list))
+ return;
+
+ buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
+ if (!buff)
+ return;
+
+ snprintf(buff, BUFF_SZ, "\nout report %d (size %d) = ",
+ report->id, raw_size);
+ hid_debug_event(hdev, buff);
+ if (raw_size + 5 > sizeof(raw_data)) {
+ kfree(buff);
+ hid_debug_event(hdev, " TOO BIG\n");
+ return;
+ } else {
+ raw_data[0] = report->id;
+ hid_output_report(report, raw_data);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
+ hid_debug_event(hdev, buff);
+ }
+
+ switch (report->id) {
+ case REPORT_LED_STATE:
+ /* 1 data byte with GPO state */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_LED_STATE", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_BRIGHTNESS:
+ /* 1 data byte with brightness */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_BRIGHTNESS", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_CONTRAST:
+ /* 1 data byte with contrast */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_CONTRAST", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_RESET:
+ /* 2 data bytes with reset duration in ms */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_RESET", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",
+ raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_LCD_CMD:
+ /* 63 data bytes with LCD commands */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_LCD_CMD", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ /* TODO: format decoding */
+ break;
+ case REPORT_LCD_DATA:
+ /* 63 data bytes with LCD data */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_LCD_CMD", report->id, raw_size-1);
+ /* TODO: format decoding */
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_LCD_CMD_DATA:
+ /* 63 data bytes with LCD commands and data */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_LCD_CMD", report->id, raw_size-1);
+ /* TODO: format decoding */
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_EE_READ:
+ /* 3 data bytes with read area description */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_EE_READ", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_EE_WRITE:
+ /* 3+1..20 data bytes with write area description */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_EE_WRITE", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[3] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ } else if (raw_data[3] + 4 <= raw_size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ }
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_ERASE_MEMORY:
+ case REPORT_BL_ERASE_MEMORY:
+ /* 3 data bytes with pointer inside erase block */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_ERASE_MEMORY", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ switch (data->addr_sz) {
+ case 2:
+ snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ break;
+ case 3:
+ snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",
+ raw_data[3], raw_data[2], raw_data[1]);
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "\tNot supported\n");
+ }
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_READ_MEMORY:
+ case REPORT_BL_READ_MEMORY:
+ /* 4 data bytes with read area description */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_READ_MEMORY", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ switch (data->addr_sz) {
+ case 2:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ break;
+ case 3:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+ raw_data[3], raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "\tNot supported\n");
+ }
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_WRITE_MEMORY:
+ case REPORT_BL_WRITE_MEMORY:
+ /* 4+1..32 data bytes with write adrea description */
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_WRITE_MEMORY", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ switch (data->addr_sz) {
+ case 2:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[3] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ } else if (raw_data[3] + 4 <= raw_size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ }
+ break;
+ case 3:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+ raw_data[3], raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[4] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ } else if (raw_data[4] + 5 <= raw_size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ }
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "\tNot supported\n");
+ }
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_SPLASH_RESTART:
+ /* TODO */
+ break;
+ case REPORT_EXIT_KEYBOARD:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_EXIT_KEYBOARD", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
+ raw_data[1] | (raw_data[2] << 8),
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_VERSION:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_VERSION", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_DEVID:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_DEVID", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_SPLASH_SIZE:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_SPLASH_SIZE", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_HOOK_VERSION:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_HOOK_VERSION", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_EXIT_FLASHER:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "REPORT_VERSION", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
+ raw_data[1] | (raw_data[2] << 8),
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+ "<unknown>", report->id, raw_size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ }
+ wake_up_interruptible(&hdev->debug_wait);
+ kfree(buff);
+}
+
+void picolcd_debug_raw_event(struct picolcd_data *data,
+ struct hid_device *hdev, struct hid_report *report,
+ u8 *raw_data, int size)
+{
+ char *buff;
+
+#define BUFF_SZ 256
+ /* Avoid unnecessary overhead if debugfs is disabled */
+ if (list_empty(&hdev->debug_list))
+ return;
+
+ buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
+ if (!buff)
+ return;
+
+ switch (report->id) {
+ case REPORT_ERROR_CODE:
+ /* 2 data bytes with affected report and error code */
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_ERROR_CODE", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ if (raw_data[2] < ARRAY_SIZE(error_codes))
+ snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",
+ raw_data[2], error_codes[raw_data[2]], raw_data[1]);
+ else
+ snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_KEY_STATE:
+ /* 2 data bytes with key state */
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_KEY_STATE", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ if (raw_data[1] == 0)
+ snprintf(buff, BUFF_SZ, "\tNo key pressed\n");
+ else if (raw_data[2] == 0)
+ snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",
+ raw_data[1], raw_data[1]);
+ else
+ snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",
+ raw_data[1], raw_data[1], raw_data[2], raw_data[2]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_IR_DATA:
+ /* Up to 20 byes of IR scancode data */
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_IR_DATA", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ if (raw_data[1] == 0) {
+ snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");
+ hid_debug_event(hdev, buff);
+ } else if (raw_data[1] + 1 <= size) {
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",
+ raw_data[1]);
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]);
+ hid_debug_event(hdev, buff);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",
+ raw_data[1]-1);
+ hid_debug_event(hdev, buff);
+ }
+ break;
+ case REPORT_EE_DATA:
+ /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_EE_DATA", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[3] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ hid_debug_event(hdev, buff);
+ } else if (raw_data[3] + 4 <= size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+ hid_debug_event(hdev, buff);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ hid_debug_event(hdev, buff);
+ }
+ break;
+ case REPORT_MEMORY:
+ /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_MEMORY", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ switch (data->addr_sz) {
+ case 2:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[3] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ } else if (raw_data[3] + 4 <= size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ }
+ break;
+ case 3:
+ snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+ raw_data[3], raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+ hid_debug_event(hdev, buff);
+ if (raw_data[4] == 0) {
+ snprintf(buff, BUFF_SZ, "\tNo data\n");
+ } else if (raw_data[4] + 5 <= size) {
+ snprintf(buff, BUFF_SZ, "\tData: ");
+ hid_debug_event(hdev, buff);
+ dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
+ } else {
+ snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+ }
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "\tNot supported\n");
+ }
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_VERSION:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_VERSION", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
+ raw_data[2], raw_data[1]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_BL_ERASE_MEMORY:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_BL_ERASE_MEMORY", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ /* TODO */
+ break;
+ case REPORT_BL_READ_MEMORY:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_BL_READ_MEMORY", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ /* TODO */
+ break;
+ case REPORT_BL_WRITE_MEMORY:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_BL_WRITE_MEMORY", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ /* TODO */
+ break;
+ case REPORT_DEVID:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_DEVID", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",
+ raw_data[1], raw_data[2], raw_data[3], raw_data[4]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",
+ raw_data[5]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_SPLASH_SIZE:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_SPLASH_SIZE", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",
+ (raw_data[2] << 8) | raw_data[1]);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",
+ (raw_data[4] << 8) | raw_data[3]);
+ hid_debug_event(hdev, buff);
+ break;
+ case REPORT_HOOK_VERSION:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "REPORT_HOOK_VERSION", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
+ raw_data[1], raw_data[2]);
+ hid_debug_event(hdev, buff);
+ break;
+ default:
+ snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+ "<unknown>", report->id, size-1);
+ hid_debug_event(hdev, buff);
+ break;
+ }
+ wake_up_interruptible(&hdev->debug_wait);
+ kfree(buff);
+}
+
+void picolcd_init_devfs(struct picolcd_data *data,
+ struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+ struct hid_report *flash_r, struct hid_report *flash_w,
+ struct hid_report *reset)
+{
+ struct hid_device *hdev = data->hdev;
+
+ mutex_init(&data->mutex_flash);
+
+ /* reset */
+ if (reset)
+ data->debug_reset = debugfs_create_file("reset", 0600,
+ hdev->debug_dir, data, &picolcd_debug_reset_fops);
+
+ /* eeprom */
+ if (eeprom_r || eeprom_w)
+ data->debug_eeprom = debugfs_create_file("eeprom",
+ (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
+ hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
+
+ /* flash */
+ if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8)
+ data->addr_sz = flash_r->field[0]->report_count - 1;
+ else
+ data->addr_sz = -1;
+ if (data->addr_sz == 2 || data->addr_sz == 3) {
+ data->debug_flash = debugfs_create_file("flash",
+ (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
+ hdev->debug_dir, data, &picolcd_debug_flash_fops);
+ } else if (flash_r || flash_w)
+ hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n");
+}
+
+void picolcd_exit_devfs(struct picolcd_data *data)
+{
+ struct dentry *dent;
+
+ dent = data->debug_reset;
+ data->debug_reset = NULL;
+ if (dent)
+ debugfs_remove(dent);
+ dent = data->debug_eeprom;
+ data->debug_eeprom = NULL;
+ if (dent)
+ debugfs_remove(dent);
+ dent = data->debug_flash;
+ data->debug_flash = NULL;
+ if (dent)
+ debugfs_remove(dent);
+ mutex_destroy(&data->mutex_flash);
+}
+
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
new file mode 100644
index 000000000000..0008a512211d
--- /dev/null
+++ b/drivers/hid/hid-picolcd_fb.c
@@ -0,0 +1,615 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * This program is free software: 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 driver is distributed in the hope that it will be useful, but *
+ * WITHOUT 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/vmalloc.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+/* Framebuffer
+ *
+ * The PicoLCD use a Topway LCD module of 256x64 pixel
+ * This display area is tiled over 4 controllers with 8 tiles
+ * each. Each tile has 8x64 pixel, each data byte representing
+ * a 1-bit wide vertical line of the tile.
+ *
+ * The display can be updated at a tile granularity.
+ *
+ * Chip 1 Chip 2 Chip 3 Chip 4
+ * +----------------+----------------+----------------+----------------+
+ * | Tile 1 | Tile 1 | Tile 1 | Tile 1 |
+ * +----------------+----------------+----------------+----------------+
+ * | Tile 2 | Tile 2 | Tile 2 | Tile 2 |
+ * +----------------+----------------+----------------+----------------+
+ * ...
+ * +----------------+----------------+----------------+----------------+
+ * | Tile 8 | Tile 8 | Tile 8 | Tile 8 |
+ * +----------------+----------------+----------------+----------------+
+ */
+#define PICOLCDFB_NAME "picolcdfb"
+#define PICOLCDFB_WIDTH (256)
+#define PICOLCDFB_HEIGHT (64)
+#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
+
+#define PICOLCDFB_UPDATE_RATE_LIMIT 10
+#define PICOLCDFB_UPDATE_RATE_DEFAULT 2
+
+/* Framebuffer visual structures */
+static const struct fb_fix_screeninfo picolcdfb_fix = {
+ .id = PICOLCDFB_NAME,
+ .type = FB_TYPE_PACKED_PIXELS,
+ .visual = FB_VISUAL_MONO01,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
+ .line_length = PICOLCDFB_WIDTH / 8,
+ .accel = FB_ACCEL_NONE,
+};
+
+static const struct fb_var_screeninfo picolcdfb_var = {
+ .xres = PICOLCDFB_WIDTH,
+ .yres = PICOLCDFB_HEIGHT,
+ .xres_virtual = PICOLCDFB_WIDTH,
+ .yres_virtual = PICOLCDFB_HEIGHT,
+ .width = 103,
+ .height = 26,
+ .bits_per_pixel = 1,
+ .grayscale = 1,
+ .red = {
+ .offset = 0,
+ .length = 1,
+ .msb_right = 0,
+ },
+ .green = {
+ .offset = 0,
+ .length = 1,
+ .msb_right = 0,
+ },
+ .blue = {
+ .offset = 0,
+ .length = 1,
+ .msb_right = 0,
+ },
+ .transp = {
+ .offset = 0,
+ .length = 0,
+ .msb_right = 0,
+ },
+};
+
+/* Send a given tile to PicoLCD */
+static int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap,
+ int chip, int tile)
+{
+ struct hid_report *report1, *report2;
+ unsigned long flags;
+ u8 *tdata;
+ int i;
+
+ report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, data->hdev);
+ if (!report1 || report1->maxfield != 1)
+ return -ENODEV;
+ report2 = picolcd_out_report(REPORT_LCD_DATA, data->hdev);
+ if (!report2 || report2->maxfield != 1)
+ return -ENODEV;
+
+ spin_lock_irqsave(&data->lock, flags);
+ if ((data->status & PICOLCD_FAILED)) {
+ spin_unlock_irqrestore(&data->lock, flags);
+ return -ENODEV;
+ }
+ hid_set_field(report1->field[0], 0, chip << 2);
+ hid_set_field(report1->field[0], 1, 0x02);
+ hid_set_field(report1->field[0], 2, 0x00);
+ hid_set_field(report1->field[0], 3, 0x00);
+ hid_set_field(report1->field[0], 4, 0xb8 | tile);
+ hid_set_field(report1->field[0], 5, 0x00);
+ hid_set_field(report1->field[0], 6, 0x00);
+ hid_set_field(report1->field[0], 7, 0x40);
+ hid_set_field(report1->field[0], 8, 0x00);
+ hid_set_field(report1->field[0], 9, 0x00);
+ hid_set_field(report1->field[0], 10, 32);
+
+ hid_set_field(report2->field[0], 0, (chip << 2) | 0x01);
+ hid_set_field(report2->field[0], 1, 0x00);
+ hid_set_field(report2->field[0], 2, 0x00);
+ hid_set_field(report2->field[0], 3, 32);
+
+ tdata = vbitmap + (tile * 4 + chip) * 64;
+ for (i = 0; i < 64; i++)
+ if (i < 32)
+ hid_set_field(report1->field[0], 11 + i, tdata[i]);
+ else
+ hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
+
+ usbhid_submit_report(data->hdev, report1, USB_DIR_OUT);
+ usbhid_submit_report(data->hdev, report2, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+ return 0;
+}
+
+/* Translate a single tile*/
+static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
+ int chip, int tile)
+{
+ int i, b, changed = 0;
+ u8 tdata[64];
+ u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
+
+ if (bpp == 1) {
+ for (b = 7; b >= 0; b--) {
+ const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
+ for (i = 0; i < 64; i++) {
+ tdata[i] <<= 1;
+ tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
+ }
+ }
+ } else if (bpp == 8) {
+ for (b = 7; b >= 0; b--) {
+ const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
+ for (i = 0; i < 64; i++) {
+ tdata[i] <<= 1;
+ tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
+ }
+ }
+ } else {
+ /* Oops, we should never get here! */
+ WARN_ON(1);
+ return 0;
+ }
+
+ for (i = 0; i < 64; i++)
+ if (tdata[i] != vdata[i]) {
+ changed = 1;
+ vdata[i] = tdata[i];
+ }
+ return changed;
+}
+
+void picolcd_fb_refresh(struct picolcd_data *data)
+{
+ if (data->fb_info)
+ schedule_delayed_work(&data->fb_info->deferred_work, 0);
+}
+
+/* Reconfigure LCD display */
+int picolcd_fb_reset(struct picolcd_data *data, int clear)
+{
+ struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
+ struct picolcd_fb_data *fbdata = data->fb_info->par;
+ int i, j;
+ unsigned long flags;
+ static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
+
+ if (!report || report->maxfield != 1)
+ return -ENODEV;
+
+ spin_lock_irqsave(&data->lock, flags);
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < report->field[0]->maxusage; j++)
+ if (j == 0)
+ hid_set_field(report->field[0], j, i << 2);
+ else if (j < sizeof(mapcmd))
+ hid_set_field(report->field[0], j, mapcmd[j]);
+ else
+ hid_set_field(report->field[0], j, 0);
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ }
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ if (clear) {
+ memset(fbdata->vbitmap, 0, PICOLCDFB_SIZE);
+ memset(fbdata->bitmap, 0, PICOLCDFB_SIZE*fbdata->bpp);
+ }
+ fbdata->force = 1;
+
+ /* schedule first output of framebuffer */
+ if (fbdata->ready)
+ schedule_delayed_work(&data->fb_info->deferred_work, 0);
+ else
+ fbdata->ready = 1;
+
+ return 0;
+}
+
+/* Update fb_vbitmap from the screen_base and send changed tiles to device */
+static void picolcd_fb_update(struct fb_info *info)
+{
+ int chip, tile, n;
+ unsigned long flags;
+ struct picolcd_fb_data *fbdata = info->par;
+ struct picolcd_data *data;
+
+ mutex_lock(&info->lock);
+
+ spin_lock_irqsave(&fbdata->lock, flags);
+ if (!fbdata->ready && fbdata->picolcd)
+ picolcd_fb_reset(fbdata->picolcd, 0);
+ spin_unlock_irqrestore(&fbdata->lock, flags);
+
+ /*
+ * Translate the framebuffer into the format needed by the PicoLCD.
+ * See display layout above.
+ * Do this one tile after the other and push those tiles that changed.
+ *
+ * Wait for our IO to complete as otherwise we might flood the queue!
+ */
+ n = 0;
+ for (chip = 0; chip < 4; chip++)
+ for (tile = 0; tile < 8; tile++) {
+ if (!fbdata->force && !picolcd_fb_update_tile(
+ fbdata->vbitmap, fbdata->bitmap,
+ fbdata->bpp, chip, tile))
+ continue;
+ n += 2;
+ if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
+ spin_lock_irqsave(&fbdata->lock, flags);
+ data = fbdata->picolcd;
+ spin_unlock_irqrestore(&fbdata->lock, flags);
+ mutex_unlock(&info->lock);
+ if (!data)
+ return;
+ usbhid_wait_io(data->hdev);
+ mutex_lock(&info->lock);
+ n = 0;
+ }
+ spin_lock_irqsave(&fbdata->lock, flags);
+ data = fbdata->picolcd;
+ spin_unlock_irqrestore(&fbdata->lock, flags);
+ if (!data || picolcd_fb_send_tile(data,
+ fbdata->vbitmap, chip, tile))
+ goto out;
+ }
+ fbdata->force = false;
+ if (n) {
+ spin_lock_irqsave(&fbdata->lock, flags);
+ data = fbdata->picolcd;
+ spin_unlock_irqrestore(&fbdata->lock, flags);
+ mutex_unlock(&info->lock);
+ if (data)
+ usbhid_wait_io(data->hdev);
+ return;
+ }
+out:
+ mutex_unlock(&info->lock);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect)
+{
+ if (!info->par)
+ return;
+ sys_fillrect(info, rect);
+
+ schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+ if (!info->par)
+ return;
+ sys_copyarea(info, area);
+
+ schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ if (!info->par)
+ return;
+ sys_imageblit(info, image);
+
+ schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/*
+ * this is the slow path from userspace. they can seek and write to
+ * the fb. it's inefficient to do anything less than a full screen draw
+ */
+static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t ret;
+ if (!info->par)
+ return -ENODEV;
+ ret = fb_sys_write(info, buf, count, ppos);
+ if (ret >= 0)
+ schedule_delayed_work(&info->deferred_work, 0);
+ return ret;
+}
+
+static int picolcd_fb_blank(int blank, struct fb_info *info)
+{
+ /* We let fb notification do this for us via lcd/backlight device */
+ return 0;
+}
+
+static void picolcd_fb_destroy(struct fb_info *info)
+{
+ struct picolcd_fb_data *fbdata = info->par;
+
+ /* make sure no work is deferred */
+ fb_deferred_io_cleanup(info);
+
+ /* No thridparty should ever unregister our framebuffer! */
+ WARN_ON(fbdata->picolcd != NULL);
+
+ vfree((u8 *)info->fix.smem_start);
+ framebuffer_release(info);
+}
+
+static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ __u32 bpp = var->bits_per_pixel;
+ __u32 activate = var->activate;
+
+ /* only allow 1/8 bit depth (8-bit is grayscale) */
+ *var = picolcdfb_var;
+ var->activate = activate;
+ if (bpp >= 8) {
+ var->bits_per_pixel = 8;
+ var->red.length = 8;
+ var->green.length = 8;
+ var->blue.length = 8;
+ } else {
+ var->bits_per_pixel = 1;
+ var->red.length = 1;
+ var->green.length = 1;
+ var->blue.length = 1;
+ }
+ return 0;
+}
+
+static int picolcd_set_par(struct fb_info *info)
+{
+ struct picolcd_fb_data *fbdata = info->par;
+ u8 *tmp_fb, *o_fb;
+ if (info->var.bits_per_pixel == fbdata->bpp)
+ return 0;
+ /* switch between 1/8 bit depths */
+ if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
+ return -EINVAL;
+
+ o_fb = fbdata->bitmap;
+ tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL);
+ if (!tmp_fb)
+ return -ENOMEM;
+
+ /* translate FB content to new bits-per-pixel */
+ if (info->var.bits_per_pixel == 1) {
+ int i, b;
+ for (i = 0; i < PICOLCDFB_SIZE; i++) {
+ u8 p = 0;
+ for (b = 0; b < 8; b++) {
+ p <<= 1;
+ p |= o_fb[i*8+b] ? 0x01 : 0x00;
+ }
+ tmp_fb[i] = p;
+ }
+ memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
+ info->fix.visual = FB_VISUAL_MONO01;
+ info->fix.line_length = PICOLCDFB_WIDTH / 8;
+ } else {
+ int i;
+ memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
+ for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
+ o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
+ info->fix.visual = FB_VISUAL_DIRECTCOLOR;
+ info->fix.line_length = PICOLCDFB_WIDTH;
+ }
+
+ kfree(tmp_fb);
+ fbdata->bpp = info->var.bits_per_pixel;
+ return 0;
+}
+
+/* Note this can't be const because of struct fb_info definition */
+static struct fb_ops picolcdfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_destroy = picolcd_fb_destroy,
+ .fb_read = fb_sys_read,
+ .fb_write = picolcd_fb_write,
+ .fb_blank = picolcd_fb_blank,
+ .fb_fillrect = picolcd_fb_fillrect,
+ .fb_copyarea = picolcd_fb_copyarea,
+ .fb_imageblit = picolcd_fb_imageblit,
+ .fb_check_var = picolcd_fb_check_var,
+ .fb_set_par = picolcd_set_par,
+};
+
+
+/* Callback from deferred IO workqueue */
+static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
+{
+ picolcd_fb_update(info);
+}
+
+static const struct fb_deferred_io picolcd_fb_defio = {
+ .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
+ .deferred_io = picolcd_fb_deferred_io,
+};
+
+
+/*
+ * The "fb_update_rate" sysfs attribute
+ */
+static ssize_t picolcd_fb_update_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+ struct picolcd_fb_data *fbdata = data->fb_info->par;
+ unsigned i, fb_update_rate = fbdata->update_rate;
+ size_t ret = 0;
+
+ for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
+ if (ret >= PAGE_SIZE)
+ break;
+ else if (i == fb_update_rate)
+ ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
+ else
+ ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
+ if (ret > 0)
+ buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
+ return ret;
+}
+
+static ssize_t picolcd_fb_update_rate_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct picolcd_data *data = dev_get_drvdata(dev);
+ struct picolcd_fb_data *fbdata = data->fb_info->par;
+ int i;
+ unsigned u;
+
+ if (count < 1 || count > 10)
+ return -EINVAL;
+
+ i = sscanf(buf, "%u", &u);
+ if (i != 1)
+ return -EINVAL;
+
+ if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
+ return -ERANGE;
+ else if (u == 0)
+ u = PICOLCDFB_UPDATE_RATE_DEFAULT;
+
+ fbdata->update_rate = u;
+ data->fb_info->fbdefio->delay = HZ / fbdata->update_rate;
+ return count;
+}
+
+static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,
+ picolcd_fb_update_rate_store);
+
+/* initialize Framebuffer device */
+int picolcd_init_framebuffer(struct picolcd_data *data)
+{
+ struct device *dev = &data->hdev->dev;
+ struct fb_info *info = NULL;
+ struct picolcd_fb_data *fbdata = NULL;
+ int i, error = -ENOMEM;
+ u32 *palette;
+
+ /* The extra memory is:
+ * - 256*u32 for pseudo_palette
+ * - struct fb_deferred_io
+ */
+ info = framebuffer_alloc(256 * sizeof(u32) +
+ sizeof(struct fb_deferred_io) +
+ sizeof(struct picolcd_fb_data) +
+ PICOLCDFB_SIZE, dev);
+ if (info == NULL) {
+ dev_err(dev, "failed to allocate a framebuffer\n");
+ goto err_nomem;
+ }
+
+ info->fbdefio = info->par;
+ *info->fbdefio = picolcd_fb_defio;
+ info->par += sizeof(struct fb_deferred_io);
+ palette = info->par;
+ info->par += 256 * sizeof(u32);
+ for (i = 0; i < 256; i++)
+ palette[i] = i > 0 && i < 16 ? 0xff : 0;
+ info->pseudo_palette = palette;
+ info->fbops = &picolcdfb_ops;
+ info->var = picolcdfb_var;
+ info->fix = picolcdfb_fix;
+ info->fix.smem_len = PICOLCDFB_SIZE*8;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ fbdata = info->par;
+ spin_lock_init(&fbdata->lock);
+ fbdata->picolcd = data;
+ fbdata->update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
+ fbdata->bpp = picolcdfb_var.bits_per_pixel;
+ fbdata->force = 1;
+ fbdata->vbitmap = info->par + sizeof(struct picolcd_fb_data);
+ fbdata->bitmap = vmalloc(PICOLCDFB_SIZE*8);
+ if (fbdata->bitmap == NULL) {
+ dev_err(dev, "can't get a free page for framebuffer\n");
+ goto err_nomem;
+ }
+ info->screen_base = (char __force __iomem *)fbdata->bitmap;
+ info->fix.smem_start = (unsigned long)fbdata->bitmap;
+ memset(fbdata->vbitmap, 0xff, PICOLCDFB_SIZE);
+ data->fb_info = info;
+
+ error = picolcd_fb_reset(data, 1);
+ if (error) {
+ dev_err(dev, "failed to configure display\n");
+ goto err_cleanup;
+ }
+
+ error = device_create_file(dev, &dev_attr_fb_update_rate);
+ if (error) {
+ dev_err(dev, "failed to create sysfs attributes\n");
+ goto err_cleanup;
+ }
+
+ fb_deferred_io_init(info);
+ error = register_framebuffer(info);
+ if (error) {
+ dev_err(dev, "failed to register framebuffer\n");
+ goto err_sysfs;
+ }
+ return 0;
+
+err_sysfs:
+ device_remove_file(dev, &dev_attr_fb_update_rate);
+ fb_deferred_io_cleanup(info);
+err_cleanup:
+ data->fb_info = NULL;
+
+err_nomem:
+ if (fbdata)
+ vfree(fbdata->bitmap);
+ framebuffer_release(info);
+ return error;
+}
+
+void picolcd_exit_framebuffer(struct picolcd_data *data)
+{
+ struct fb_info *info = data->fb_info;
+ struct picolcd_fb_data *fbdata = info->par;
+ unsigned long flags;
+
+ device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
+
+ /* disconnect framebuffer from HID dev */
+ spin_lock_irqsave(&fbdata->lock, flags);
+ fbdata->picolcd = NULL;
+ spin_unlock_irqrestore(&fbdata->lock, flags);
+
+ /* make sure there is no running update - thus that fbdata->picolcd
+ * once obtained under lock is guaranteed not to get free() under
+ * the feet of the deferred work */
+ flush_delayed_work_sync(&info->deferred_work);
+
+ data->fb_info = NULL;
+ unregister_framebuffer(info);
+}
diff --git a/drivers/hid/hid-picolcd_lcd.c b/drivers/hid/hid-picolcd_lcd.c
new file mode 100644
index 000000000000..2d0ddc5ac65f
--- /dev/null
+++ b/drivers/hid/hid-picolcd_lcd.c
@@ -0,0 +1,107 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * This program is free software: 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 driver is distributed in the hope that it will be useful, but *
+ * WITHOUT 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/lcd.h>
+
+#include "hid-picolcd.h"
+
+/*
+ * lcd class device
+ */
+static int picolcd_get_contrast(struct lcd_device *ldev)
+{
+ struct picolcd_data *data = lcd_get_data(ldev);
+ return data->lcd_contrast;
+}
+
+static int picolcd_set_contrast(struct lcd_device *ldev, int contrast)
+{
+ struct picolcd_data *data = lcd_get_data(ldev);
+ struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev);
+ unsigned long flags;
+
+ if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+ return -ENODEV;
+
+ data->lcd_contrast = contrast & 0x0ff;
+ spin_lock_irqsave(&data->lock, flags);
+ hid_set_field(report->field[0], 0, data->lcd_contrast);
+ if (!(data->status & PICOLCD_FAILED))
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+ return 0;
+}
+
+static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb)
+{
+ return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev));
+}
+
+static struct lcd_ops picolcd_lcdops = {
+ .get_contrast = picolcd_get_contrast,
+ .set_contrast = picolcd_set_contrast,
+ .check_fb = picolcd_check_lcd_fb,
+};
+
+int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report)
+{
+ struct device *dev = &data->hdev->dev;
+ struct lcd_device *ldev;
+
+ if (!report)
+ return -ENODEV;
+ if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+ report->field[0]->report_size != 8) {
+ dev_err(dev, "unsupported CONTRAST report");
+ return -EINVAL;
+ }
+
+ ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops);
+ if (IS_ERR(ldev)) {
+ dev_err(dev, "failed to register LCD\n");
+ return PTR_ERR(ldev);
+ }
+ ldev->props.max_contrast = 0x0ff;
+ data->lcd_contrast = 0xe5;
+ data->lcd = ldev;
+ picolcd_set_contrast(ldev, 0xe5);
+ return 0;
+}
+
+void picolcd_exit_lcd(struct picolcd_data *data)
+{
+ struct lcd_device *ldev = data->lcd;
+
+ data->lcd = NULL;
+ if (ldev)
+ lcd_device_unregister(ldev);
+}
+
+int picolcd_resume_lcd(struct picolcd_data *data)
+{
+ if (!data->lcd)
+ return 0;
+ return picolcd_set_contrast(data->lcd, data->lcd_contrast);
+}
+
diff --git a/drivers/hid/hid-picolcd_leds.c b/drivers/hid/hid-picolcd_leds.c
new file mode 100644
index 000000000000..28cb6a4f9634
--- /dev/null
+++ b/drivers/hid/hid-picolcd_leds.c
@@ -0,0 +1,175 @@
+/***************************************************************************
+ * Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org> *
+ * *
+ * Based on Logitech G13 driver (v0.4) *
+ * Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu> *
+ * *
+ * This program is free software: 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 driver is distributed in the hope that it will be useful, but *
+ * WITHOUT 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 software. If not see <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+
+#include <linux/leds.h>
+
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+
+void picolcd_leds_set(struct picolcd_data *data)
+{
+ struct hid_report *report;
+ unsigned long flags;
+
+ if (!data->led[0])
+ return;
+ report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
+ if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+ return;
+
+ spin_lock_irqsave(&data->lock, flags);
+ hid_set_field(report->field[0], 0, data->led_state);
+ if (!(data->status & PICOLCD_FAILED))
+ usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+ spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct device *dev;
+ struct hid_device *hdev;
+ struct picolcd_data *data;
+ int i, state = 0;
+
+ dev = led_cdev->dev->parent;
+ hdev = container_of(dev, struct hid_device, dev);
+ data = hid_get_drvdata(hdev);
+ if (!data)
+ return;
+ for (i = 0; i < 8; i++) {
+ if (led_cdev != data->led[i])
+ continue;
+ state = (data->led_state >> i) & 1;
+ if (value == LED_OFF && state) {
+ data->led_state &= ~(1 << i);
+ picolcd_leds_set(data);
+ } else if (value != LED_OFF && !state) {
+ data->led_state |= 1 << i;
+ picolcd_leds_set(data);
+ }
+ break;
+ }
+}
+
+static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
+{
+ struct device *dev;
+ struct hid_device *hdev;
+ struct picolcd_data *data;
+ int i, value = 0;
+
+ dev = led_cdev->dev->parent;
+ hdev = container_of(dev, struct hid_device, dev);
+ data = hid_get_drvdata(hdev);
+ for (i = 0; i < 8; i++)
+ if (led_cdev == data->led[i]) {
+ value = (data->led_state >> i) & 1;
+ break;
+ }
+ return value ? LED_FULL : LED_OFF;
+}
+
+int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
+{
+ struct device *dev = &data->hdev->dev;
+ struct led_classdev *led;
+ size_t name_sz = strlen(dev_name(dev)) + 8;
+ char *name;
+ int i, ret = 0;
+
+ if (!report)
+ return -ENODEV;
+ if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+ report->field[0]->report_size != 8) {
+ dev_err(dev, "unsupported LED_STATE report");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 8; i++) {
+ led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
+ if (!led) {
+ dev_err(dev, "can't allocate memory for LED %d\n", i);
+ ret = -ENOMEM;
+ goto err;
+ }
+ name = (void *)(&led[1]);
+ snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
+ led->name = name;
+ led->brightness = 0;
+ led->max_brightness = 1;
+ led->brightness_get = picolcd_led_get_brightness;
+ led->brightness_set = picolcd_led_set_brightness;
+
+ data->led[i] = led;
+ ret = led_classdev_register(dev, data->led[i]);
+ if (ret) {
+ data->led[i] = NULL;
+ kfree(led);
+ dev_err(dev, "can't register LED %d\n", i);
+ goto err;
+ }
+ }
+ return 0;
+err:
+ for (i = 0; i < 8; i++)
+ if (data->led[i]) {
+ led = data->led[i];
+ data->led[i] = NULL;
+ led_classdev_unregister(led);
+ kfree(led);
+ }
+ return ret;
+}
+
+void picolcd_exit_leds(struct picolcd_data *data)
+{
+ struct led_classdev *led;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ led = data->led[i];
+ data->led[i] = NULL;
+ if (!led)
+ continue;
+ led_classdev_unregister(led);
+ kfree(led);
+ }
+}
+
+
diff --git a/drivers/hid/hid-primax.c b/drivers/hid/hid-primax.c
index 4d3c60d88318..c15adb0c98a1 100644
--- a/drivers/hid/hid-primax.c
+++ b/drivers/hid/hid-primax.c
@@ -64,29 +64,6 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report,
return 0;
}
-static int px_probe(struct hid_device *hid, const struct hid_device_id *id)
-{
- int ret;
-
- ret = hid_parse(hid);
- if (ret) {
- hid_err(hid, "parse failed\n");
- goto fail;
- }
-
- ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
- if (ret)
- hid_err(hid, "hw start failed\n");
-
-fail:
- return ret;
-}
-
-static void px_remove(struct hid_device *hid)
-{
- hid_hw_stop(hid);
-}
-
static const struct hid_device_id px_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
{ }
@@ -97,8 +74,6 @@ static struct hid_driver px_driver = {
.name = "primax",
.id_table = px_devices,
.raw_event = px_raw_event,
- .probe = px_probe,
- .remove = px_remove,
};
static int __init px_init(void)
diff --git a/drivers/hid/hid-prodikeys.c b/drivers/hid/hid-prodikeys.c
index b71b77ab0dc7..ec8ca3336315 100644
--- a/drivers/hid/hid-prodikeys.c
+++ b/drivers/hid/hid-prodikeys.c
@@ -105,7 +105,7 @@ static ssize_t show_channel(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read channel=%u\n", pk->pm->midi_channel);
@@ -118,7 +118,7 @@ static ssize_t store_channel(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
unsigned channel = 0;
@@ -142,7 +142,7 @@ static ssize_t show_sustain(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read sustain=%u\n", pk->pm->midi_sustain);
@@ -155,7 +155,7 @@ static ssize_t store_sustain(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
unsigned sustain = 0;
@@ -181,7 +181,7 @@ static ssize_t show_octave(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
dbg_hid("pcmidi sysfs read octave=%d\n", pk->pm->midi_octave);
@@ -194,7 +194,7 @@ static ssize_t store_octave(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
int octave = 0;
@@ -759,7 +759,7 @@ static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
struct hid_field *field, struct hid_usage *usage,
unsigned long **bit, int *max)
{
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
struct pcmidi_snd *pm;
pm = pk->pm;
@@ -777,7 +777,7 @@ static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
static int pk_raw_event(struct hid_device *hdev, struct hid_report *report,
u8 *data, int size)
{
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
int ret = 0;
if (1 == pk->pm->ifnum) {
@@ -858,7 +858,7 @@ err_free_pk:
static void pk_remove(struct hid_device *hdev)
{
- struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+ struct pk_device *pk = hid_get_drvdata(hdev);
struct pcmidi_snd *pm;
pm = pk->pm;
diff --git a/drivers/hid/hid-ps3remote.c b/drivers/hid/hid-ps3remote.c
new file mode 100644
index 000000000000..03811e539d71
--- /dev/null
+++ b/drivers/hid/hid-ps3remote.c
@@ -0,0 +1,215 @@
+/*
+ * HID driver for Sony PS3 BD Remote Control
+ *
+ * Copyright (c) 2012 David Dillow <dave@thedillows.org>
+ * Based on a blend of the bluez fakehid user-space code by Marcel Holtmann
+ * and other kernel HID drivers.
+ */
+
+/*
+ * This program is free software; 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: 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.
+ *
+ * There will be no PIN request from the device.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static __u8 ps3remote_rdesc[] = {
+ 0x05, 0x01, /* GUsagePage Generic Desktop */
+ 0x09, 0x05, /* LUsage 0x05 [Game Pad] */
+ 0xA1, 0x01, /* MCollection Application (mouse, keyboard) */
+
+ /* Use collection 1 for joypad buttons */
+ 0xA1, 0x02, /* MCollection Logical (interrelated data) */
+
+ /* Ignore the 1st byte, maybe it is used for a controller
+ * number but it's not needed for correct operation */
+ 0x75, 0x08, /* GReportSize 0x08 [8] */
+ 0x95, 0x01, /* GReportCount 0x01 [1] */
+ 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
+
+ /* Bytes from 2nd to 4th are a bitmap for joypad buttons, for these
+ * buttons multiple keypresses are allowed */
+ 0x05, 0x09, /* GUsagePage Button */
+ 0x19, 0x01, /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */
+ 0x29, 0x18, /* LUsageMaximum 0x18 [Button 24] */
+ 0x14, /* GLogicalMinimum [0] */
+ 0x25, 0x01, /* GLogicalMaximum 0x01 [1] */
+ 0x75, 0x01, /* GReportSize 0x01 [1] */
+ 0x95, 0x18, /* GReportCount 0x18 [24] */
+ 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */
+
+ 0xC0, /* MEndCollection */
+
+ /* Use collection 2 for remote control buttons */
+ 0xA1, 0x02, /* MCollection Logical (interrelated data) */
+
+ /* 5th byte is used for remote control buttons */
+ 0x05, 0x09, /* GUsagePage Button */
+ 0x18, /* LUsageMinimum [No button pressed] */
+ 0x29, 0xFE, /* LUsageMaximum 0xFE [Button 254] */
+ 0x14, /* GLogicalMinimum [0] */
+ 0x26, 0xFE, 0x00, /* GLogicalMaximum 0x00FE [254] */
+ 0x75, 0x08, /* GReportSize 0x08 [8] */
+ 0x95, 0x01, /* GReportCount 0x01 [1] */
+ 0x80, /* MInput */
+
+ /* Ignore bytes from 6th to 11th, 6th to 10th are always constant at
+ * 0xff and 11th is for press indication */
+ 0x75, 0x08, /* GReportSize 0x08 [8] */
+ 0x95, 0x06, /* GReportCount 0x06 [6] */
+ 0x81, 0x01, /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
+
+ /* 12th byte is for battery strength */
+ 0x05, 0x06, /* GUsagePage Generic Device Controls */
+ 0x09, 0x20, /* LUsage 0x20 [Battery Strength] */
+ 0x14, /* GLogicalMinimum [0] */
+ 0x25, 0x05, /* GLogicalMaximum 0x05 [5] */
+ 0x75, 0x08, /* GReportSize 0x08 [8] */
+ 0x95, 0x01, /* GReportCount 0x01 [1] */
+ 0x81, 0x02, /* MInput 0x02 (Data[0] Var[1] Abs[2]) */
+
+ 0xC0, /* MEndCollection */
+
+ 0xC0 /* MEndCollection [Game Pad] */
+};
+
+static const unsigned int ps3remote_keymap_joypad_buttons[] = {
+ [0x01] = KEY_SELECT,
+ [0x02] = BTN_THUMBL, /* L3 */
+ [0x03] = BTN_THUMBR, /* R3 */
+ [0x04] = BTN_START,
+ [0x05] = KEY_UP,
+ [0x06] = KEY_RIGHT,
+ [0x07] = KEY_DOWN,
+ [0x08] = KEY_LEFT,
+ [0x09] = BTN_TL2, /* L2 */
+ [0x0a] = BTN_TR2, /* R2 */
+ [0x0b] = BTN_TL, /* L1 */
+ [0x0c] = BTN_TR, /* R1 */
+ [0x0d] = KEY_OPTION, /* options/triangle */
+ [0x0e] = KEY_BACK, /* back/circle */
+ [0x0f] = BTN_0, /* cross */
+ [0x10] = KEY_SCREEN, /* view/square */
+ [0x11] = KEY_HOMEPAGE, /* PS button */
+ [0x14] = KEY_ENTER,
+};
+static const unsigned int ps3remote_keymap_remote_buttons[] = {
+ [0x00] = KEY_1,
+ [0x01] = KEY_2,
+ [0x02] = KEY_3,
+ [0x03] = KEY_4,
+ [0x04] = KEY_5,
+ [0x05] = KEY_6,
+ [0x06] = KEY_7,
+ [0x07] = KEY_8,
+ [0x08] = KEY_9,
+ [0x09] = KEY_0,
+ [0x0e] = KEY_ESC, /* return */
+ [0x0f] = KEY_CLEAR,
+ [0x16] = KEY_EJECTCD,
+ [0x1a] = KEY_MENU, /* top menu */
+ [0x28] = KEY_TIME,
+ [0x30] = KEY_PREVIOUS,
+ [0x31] = KEY_NEXT,
+ [0x32] = KEY_PLAY,
+ [0x33] = KEY_REWIND, /* scan back */
+ [0x34] = KEY_FORWARD, /* scan forward */
+ [0x38] = KEY_STOP,
+ [0x39] = KEY_PAUSE,
+ [0x40] = KEY_CONTEXT_MENU, /* pop up/menu */
+ [0x60] = KEY_FRAMEBACK, /* slow/step back */
+ [0x61] = KEY_FRAMEFORWARD, /* slow/step forward */
+ [0x63] = KEY_SUBTITLE,
+ [0x64] = KEY_AUDIO,
+ [0x65] = KEY_ANGLE,
+ [0x70] = KEY_INFO, /* display */
+ [0x80] = KEY_BLUE,
+ [0x81] = KEY_RED,
+ [0x82] = KEY_GREEN,
+ [0x83] = KEY_YELLOW,
+};
+
+static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ *rsize = sizeof(ps3remote_rdesc);
+ return ps3remote_rdesc;
+}
+
+static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
+ struct hid_field *field, struct hid_usage *usage,
+ unsigned long **bit, int *max)
+{
+ unsigned int key = usage->hid & HID_USAGE;
+
+ if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
+ return -1;
+
+ switch (usage->collection_index) {
+ case 1:
+ if (key >= ARRAY_SIZE(ps3remote_keymap_joypad_buttons))
+ return -1;
+
+ key = ps3remote_keymap_joypad_buttons[key];
+ if (!key)
+ return -1;
+ break;
+ case 2:
+ if (key >= ARRAY_SIZE(ps3remote_keymap_remote_buttons))
+ return -1;
+
+ key = ps3remote_keymap_remote_buttons[key];
+ if (!key)
+ return -1;
+ break;
+ default:
+ return -1;
+ }
+
+ hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
+ return 1;
+}
+
+static const struct hid_device_id ps3remote_devices[] = {
+ /* PS3 BD Remote Control */
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
+ /* Logitech Harmony Adapter for PS3 */
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, ps3remote_devices);
+
+static struct hid_driver ps3remote_driver = {
+ .name = "ps3_remote",
+ .id_table = ps3remote_devices,
+ .report_fixup = ps3remote_fixup,
+ .input_mapping = ps3remote_mapping,
+};
+
+static int __init ps3remote_init(void)
+{
+ return hid_register_driver(&ps3remote_driver);
+}
+
+static void __exit ps3remote_exit(void)
+{
+ hid_unregister_driver(&ps3remote_driver);
+}
+
+module_init(ps3remote_init);
+module_exit(ps3remote_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Dillow <dave@thedillows.org>, Antonio Ospite <ospite@studenti.unina.it>");
diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c
index 3c1fd8af5e0c..a5821d317229 100644
--- a/drivers/hid/hid-samsung.c
+++ b/drivers/hid/hid-samsung.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk>
*
diff --git a/drivers/hid/hid-sensor-hub.c b/drivers/hid/hid-sensor-hub.c
new file mode 100644
index 000000000000..d9d73e9163eb
--- /dev/null
+++ b/drivers/hid/hid-sensor-hub.c
@@ -0,0 +1,680 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ */
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/usb.h>
+#include "usbhid/usbhid.h"
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mfd/core.h>
+#include <linux/list.h>
+#include <linux/hid-sensor-ids.h>
+#include <linux/hid-sensor-hub.h>
+#include "hid-ids.h"
+
+/**
+ * struct sensor_hub_pending - Synchronous read pending information
+ * @status: Pending status true/false.
+ * @ready: Completion synchronization data.
+ * @usage_id: Usage id for physical device, E.g. Gyro usage id.
+ * @attr_usage_id: Usage Id of a field, E.g. X-AXIS for a gyro.
+ * @raw_size: Response size for a read request.
+ * @raw_data: Place holder for received response.
+ */
+struct sensor_hub_pending {
+ bool status;
+ struct completion ready;
+ u32 usage_id;
+ u32 attr_usage_id;
+ int raw_size;
+ u8 *raw_data;
+};
+
+/**
+ * struct sensor_hub_data - Hold a instance data for a HID hub device
+ * @hsdev: Stored hid instance for current hub device.
+ * @mutex: Mutex to serialize synchronous request.
+ * @lock: Spin lock to protect pending request structure.
+ * @pending: Holds information of pending sync read request.
+ * @dyn_callback_list: Holds callback function
+ * @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).
+ */
+struct sensor_hub_data {
+ struct hid_sensor_hub_device *hsdev;
+ struct mutex mutex;
+ spinlock_t lock;
+ struct sensor_hub_pending pending;
+ struct list_head dyn_callback_list;
+ spinlock_t dyn_callback_lock;
+ struct mfd_cell *hid_sensor_hub_client_devs;
+ int hid_sensor_client_cnt;
+};
+
+/**
+ * struct hid_sensor_hub_callbacks_list - Stores callback list
+ * @list: list head.
+ * @usage_id: usage id for a physical device.
+ * @usage_callback: Stores registered callback functions.
+ * @priv: Private data for a physical device.
+ */
+struct hid_sensor_hub_callbacks_list {
+ struct list_head list;
+ u32 usage_id;
+ struct hid_sensor_hub_callbacks *usage_callback;
+ void *priv;
+};
+
+static int sensor_hub_check_for_sensor_page(struct hid_device *hdev)
+{
+ int i;
+ int ret = -EINVAL;
+
+ for (i = 0; i < hdev->maxcollection; i++) {
+ struct hid_collection *col = &hdev->collection[i];
+ if (col->type == HID_COLLECTION_PHYSICAL &&
+ (col->usage & HID_USAGE_PAGE) == HID_UP_SENSOR) {
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static struct hid_report *sensor_hub_report(int id, struct hid_device *hdev,
+ int dir)
+{
+ struct hid_report *report;
+
+ list_for_each_entry(report, &hdev->report_enum[dir].report_list, list) {
+ if (report->id == id)
+ return report;
+ }
+ hid_warn(hdev, "No report with id 0x%x found\n", id);
+
+ return NULL;
+}
+
+static int sensor_hub_get_physical_device_count(
+ struct hid_report_enum *report_enum)
+{
+ struct hid_report *report;
+ struct hid_field *field;
+ int cnt = 0;
+
+ list_for_each_entry(report, &report_enum->report_list, list) {
+ field = report->field[0];
+ if (report->maxfield && field &&
+ field->physical)
+ cnt++;
+ }
+
+ return cnt;
+}
+
+static void sensor_hub_fill_attr_info(
+ struct hid_sensor_hub_attribute_info *info,
+ s32 index, s32 report_id, s32 units, s32 unit_expo, s32 size)
+{
+ info->index = index;
+ info->report_id = report_id;
+ info->units = units;
+ info->unit_expo = unit_expo;
+ info->size = size/8;
+}
+
+static struct hid_sensor_hub_callbacks *sensor_hub_get_callback(
+ struct hid_device *hdev,
+ u32 usage_id, 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) {
+ *priv = callback->priv;
+ spin_unlock(&pdata->dyn_callback_lock);
+ return callback->usage_callback;
+ }
+ spin_unlock(&pdata->dyn_callback_lock);
+
+ return NULL;
+}
+
+int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ struct hid_sensor_hub_callbacks *usage_callback)
+{
+ struct hid_sensor_hub_callbacks_list *callback;
+ struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev);
+
+ spin_lock(&pdata->dyn_callback_lock);
+ list_for_each_entry(callback, &pdata->dyn_callback_list, list)
+ if (callback->usage_id == usage_id) {
+ spin_unlock(&pdata->dyn_callback_lock);
+ return -EINVAL;
+ }
+ callback = kzalloc(sizeof(*callback), GFP_ATOMIC);
+ if (!callback) {
+ spin_unlock(&pdata->dyn_callback_lock);
+ return -ENOMEM;
+ }
+ callback->usage_callback = usage_callback;
+ callback->usage_id = usage_id;
+ callback->priv = NULL;
+ list_add_tail(&callback->list, &pdata->dyn_callback_list);
+ spin_unlock(&pdata->dyn_callback_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_register_callback);
+
+int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id)
+{
+ struct hid_sensor_hub_callbacks_list *callback;
+ struct sensor_hub_data *pdata = hid_get_drvdata(hsdev->hdev);
+
+ spin_lock(&pdata->dyn_callback_lock);
+ list_for_each_entry(callback, &pdata->dyn_callback_list, list)
+ if (callback->usage_id == usage_id) {
+ list_del(&callback->list);
+ kfree(callback);
+ break;
+ }
+ spin_unlock(&pdata->dyn_callback_lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_remove_callback);
+
+int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+ u32 field_index, s32 value)
+{
+ struct hid_report *report;
+ struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
+ int ret = 0;
+
+ mutex_lock(&data->mutex);
+ report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT);
+ if (!report || (field_index >= report->maxfield)) {
+ ret = -EINVAL;
+ goto done_proc;
+ }
+ hid_set_field(report->field[field_index], 0, value);
+ usbhid_submit_report(hsdev->hdev, report, USB_DIR_OUT);
+ usbhid_wait_io(hsdev->hdev);
+
+done_proc:
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_set_feature);
+
+int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+ u32 field_index, s32 *value)
+{
+ struct hid_report *report;
+ struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
+ int ret = 0;
+
+ mutex_lock(&data->mutex);
+ report = sensor_hub_report(report_id, hsdev->hdev, HID_FEATURE_REPORT);
+ if (!report || (field_index >= report->maxfield)) {
+ ret = -EINVAL;
+ goto done_proc;
+ }
+ usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN);
+ usbhid_wait_io(hsdev->hdev);
+ *value = report->field[field_index]->value[0];
+
+done_proc:
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_get_feature);
+
+
+int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ u32 attr_usage_id, u32 report_id)
+{
+ struct sensor_hub_data *data = hid_get_drvdata(hsdev->hdev);
+ unsigned long flags;
+ struct hid_report *report;
+ int ret_val = 0;
+
+ mutex_lock(&data->mutex);
+ memset(&data->pending, 0, sizeof(data->pending));
+ init_completion(&data->pending.ready);
+ data->pending.usage_id = usage_id;
+ data->pending.attr_usage_id = attr_usage_id;
+ data->pending.raw_size = 0;
+
+ spin_lock_irqsave(&data->lock, flags);
+ data->pending.status = true;
+ report = sensor_hub_report(report_id, hsdev->hdev, HID_INPUT_REPORT);
+ if (!report) {
+ spin_unlock_irqrestore(&data->lock, flags);
+ goto err_free;
+ }
+ usbhid_submit_report(hsdev->hdev, report, USB_DIR_IN);
+ spin_unlock_irqrestore(&data->lock, flags);
+ wait_for_completion_interruptible_timeout(&data->pending.ready, HZ*5);
+ switch (data->pending.raw_size) {
+ case 1:
+ ret_val = *(u8 *)data->pending.raw_data;
+ break;
+ case 2:
+ ret_val = *(u16 *)data->pending.raw_data;
+ break;
+ case 4:
+ ret_val = *(u32 *)data->pending.raw_data;
+ break;
+ default:
+ ret_val = 0;
+ }
+ kfree(data->pending.raw_data);
+
+err_free:
+ data->pending.status = false;
+ mutex_unlock(&data->mutex);
+
+ return ret_val;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_input_attr_get_raw_value);
+
+int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
+ u8 type,
+ u32 usage_id,
+ u32 attr_usage_id,
+ struct hid_sensor_hub_attribute_info *info)
+{
+ int ret = -1;
+ int i, j;
+ int collection_index = -1;
+ struct hid_report *report;
+ struct hid_field *field;
+ struct hid_report_enum *report_enum;
+ struct hid_device *hdev = hsdev->hdev;
+
+ /* Initialize with defaults */
+ info->usage_id = usage_id;
+ info->attrib_id = attr_usage_id;
+ info->report_id = -1;
+ info->index = -1;
+ 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->unit, field->unit_exponent,
+ field->report_size);
+ 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->unit,
+ field->unit_exponent,
+ field->report_size);
+ ret = 0;
+ break;
+ }
+ }
+ }
+ if (ret == 0)
+ break;
+ }
+ }
+
+err_ret:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(sensor_hub_input_get_attribute_info);
+
+#ifdef CONFIG_PM
+static int sensor_hub_suspend(struct hid_device *hdev, pm_message_t message)
+{
+ struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
+ struct hid_sensor_hub_callbacks_list *callback;
+
+ hid_dbg(hdev, " sensor_hub_suspend\n");
+ spin_lock(&pdata->dyn_callback_lock);
+ list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
+ if (callback->usage_callback->suspend)
+ callback->usage_callback->suspend(
+ pdata->hsdev, callback->priv);
+ }
+ spin_unlock(&pdata->dyn_callback_lock);
+
+ return 0;
+}
+
+static int sensor_hub_resume(struct hid_device *hdev)
+{
+ struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
+ struct hid_sensor_hub_callbacks_list *callback;
+
+ hid_dbg(hdev, " sensor_hub_resume\n");
+ spin_lock(&pdata->dyn_callback_lock);
+ list_for_each_entry(callback, &pdata->dyn_callback_list, list) {
+ if (callback->usage_callback->resume)
+ callback->usage_callback->resume(
+ pdata->hsdev, callback->priv);
+ }
+ spin_unlock(&pdata->dyn_callback_lock);
+
+ return 0;
+}
+
+static int sensor_hub_reset_resume(struct hid_device *hdev)
+{
+ return 0;
+}
+#endif
+/*
+ * Handle raw report as sent by device
+ */
+static int sensor_hub_raw_event(struct hid_device *hdev,
+ struct hid_report *report, u8 *raw_data, int size)
+{
+ int i;
+ u8 *ptr;
+ int sz;
+ struct sensor_hub_data *pdata = hid_get_drvdata(hdev);
+ unsigned long flags;
+ struct hid_sensor_hub_callbacks *callback = NULL;
+ struct hid_collection *collection = NULL;
+ void *priv = NULL;
+
+ hid_dbg(hdev, "sensor_hub_raw_event report id:0x%x size:%d type:%d\n",
+ report->id, size, report->type);
+ hid_dbg(hdev, "maxfield:%d\n", report->maxfield);
+ if (report->type != HID_INPUT_REPORT)
+ return 1;
+
+ ptr = raw_data;
+ ptr++; /*Skip report id*/
+
+ if (!report)
+ goto err_report;
+
+ spin_lock_irqsave(&pdata->lock, flags);
+
+ for (i = 0; i < report->maxfield; ++i) {
+
+ hid_dbg(hdev, "%d collection_index:%x hid:%x sz:%x\n",
+ i, report->field[i]->usage->collection_index,
+ report->field[i]->usage->hid,
+ report->field[i]->report_size/8);
+
+ sz = report->field[i]->report_size/8;
+ if (pdata->pending.status && pdata->pending.attr_usage_id ==
+ report->field[i]->usage->hid) {
+ hid_dbg(hdev, "data was pending ...\n");
+ pdata->pending.raw_data = kmalloc(sz, GFP_ATOMIC);
+ if (pdata->pending.raw_data) {
+ memcpy(pdata->pending.raw_data, ptr, sz);
+ pdata->pending.raw_size = sz;
+ } else
+ pdata->pending.raw_size = 0;
+ complete(&pdata->pending.ready);
+ }
+ collection = &hdev->collection[
+ 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);
+ if (callback && callback->capture_sample) {
+ if (report->field[i]->logical)
+ callback->capture_sample(pdata->hsdev,
+ report->field[i]->logical, sz, ptr,
+ callback->pdev);
+ else
+ callback->capture_sample(pdata->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->pdev);
+ spin_unlock_irqrestore(&pdata->lock, flags);
+
+err_report:
+ return 1;
+}
+
+static int sensor_hub_probe(struct hid_device *hdev,
+ const struct hid_device_id *id)
+{
+ int ret;
+ 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;
+
+ sd = kzalloc(sizeof(struct sensor_hub_data), GFP_KERNEL);
+ if (!sd) {
+ hid_err(hdev, "cannot allocate Sensor data\n");
+ return -ENOMEM;
+ }
+ sd->hsdev = kzalloc(sizeof(struct hid_sensor_hub_device), GFP_KERNEL);
+ if (!sd->hsdev) {
+ hid_err(hdev, "cannot allocate hid_sensor_hub_device\n");
+ ret = -ENOMEM;
+ goto err_free_hub;
+ }
+ hid_set_drvdata(hdev, sd);
+ 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);
+ ret = hid_parse(hdev);
+ if (ret) {
+ hid_err(hdev, "parse failed\n");
+ goto err_free;
+ }
+ if (sensor_hub_check_for_sensor_page(hdev) < 0) {
+ hid_err(hdev, "sensor page not found\n");
+ goto err_free;
+ }
+ INIT_LIST_HEAD(&hdev->inputs);
+
+ ret = hid_hw_start(hdev, 0);
+ if (ret) {
+ hid_err(hdev, "hw start failed\n");
+ goto err_free;
+ }
+ ret = hid_hw_open(hdev);
+ if (ret) {
+ hid_err(hdev, "failed to open input interrupt pipe\n");
+ goto err_stop_hw;
+ }
+
+ 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);
+ if (dev_cnt > HID_MAX_PHY_DEVICES) {
+ hid_err(hdev, "Invalid Physical device count\n");
+ ret = -EINVAL;
+ goto err_close;
+ }
+ sd->hid_sensor_hub_client_devs = kzalloc(dev_cnt *
+ sizeof(struct mfd_cell),
+ GFP_KERNEL);
+ if (sd->hid_sensor_hub_client_devs == NULL) {
+ hid_err(hdev, "Failed to allocate memory for mfd cells\n");
+ ret = -ENOMEM;
+ goto err_close;
+ }
+ 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) {
+ name = kasprintf(GFP_KERNEL, "HID-SENSOR-%x",
+ field->physical);
+ if (name == NULL) {
+ hid_err(hdev, "Failed MFD device name\n");
+ ret = -ENOMEM;
+ goto err_free_names;
+ }
+ 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;
+ 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);
+ sd->hid_sensor_client_cnt++;
+ }
+ }
+ 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;
+
+ return ret;
+
+err_free_names:
+ 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);
+err_close:
+ hid_hw_close(hdev);
+err_stop_hw:
+ hid_hw_stop(hdev);
+err_free:
+ kfree(sd->hsdev);
+err_free_hub:
+ kfree(sd);
+
+ return ret;
+}
+
+static void sensor_hub_remove(struct hid_device *hdev)
+{
+ struct sensor_hub_data *data = hid_get_drvdata(hdev);
+ unsigned long flags;
+ int i;
+
+ hid_dbg(hdev, " hardware removed\n");
+ hid_hw_close(hdev);
+ hid_hw_stop(hdev);
+ spin_lock_irqsave(&data->lock, flags);
+ if (data->pending.status)
+ 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)
+ kfree(data->hid_sensor_hub_client_devs[i].name);
+ kfree(data->hid_sensor_hub_client_devs);
+ hid_set_drvdata(hdev, NULL);
+ mutex_destroy(&data->mutex);
+ kfree(data->hsdev);
+ kfree(data);
+}
+
+static const struct hid_device_id sensor_hub_devices[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086,
+ USB_DEVICE_ID_SENSOR_HUB_1020) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087,
+ USB_DEVICE_ID_SENSOR_HUB_1020) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8086,
+ USB_DEVICE_ID_SENSOR_HUB_09FA) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_INTEL_8087,
+ USB_DEVICE_ID_SENSOR_HUB_09FA) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_STANTUM_STM,
+ USB_DEVICE_ID_SENSOR_HUB_7014) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, sensor_hub_devices);
+
+static const struct hid_usage_id sensor_hub_grabbed_usages[] = {
+ { HID_ANY_ID, HID_ANY_ID, HID_ANY_ID },
+ { HID_ANY_ID - 1, HID_ANY_ID - 1, HID_ANY_ID - 1 }
+};
+
+static struct hid_driver sensor_hub_driver = {
+ .name = "hid-sensor-hub",
+ .id_table = sensor_hub_devices,
+ .probe = sensor_hub_probe,
+ .remove = sensor_hub_remove,
+ .raw_event = sensor_hub_raw_event,
+#ifdef CONFIG_PM
+ .suspend = sensor_hub_suspend,
+ .resume = sensor_hub_resume,
+ .reset_resume = sensor_hub_reset_resume,
+#endif
+};
+
+static int __init sensor_hub_init(void)
+{
+ return hid_register_driver(&sensor_hub_driver);
+}
+
+static void __exit sensor_hub_exit(void)
+{
+ hid_unregister_driver(&sensor_hub_driver);
+}
+
+module_init(sensor_hub_init);
+module_exit(sensor_hub_exit);
+
+MODULE_DESCRIPTION("HID Sensor Hub driver");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 5cd25bd907f8..7f33ebf299c2 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -4,7 +4,6 @@
* Copyright (c) 1999 Andreas Gal
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
* Copyright (c) 2006-2008 Jiri Kosina
*/
diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c
index d484a0043dd4..45b4b066a262 100644
--- a/drivers/hid/hid-sunplus.c
+++ b/drivers/hid/hid-sunplus.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby
*/
diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c
index 3aba02be1f26..2e56a1fd2375 100644
--- a/drivers/hid/hid-uclogic.c
+++ b/drivers/hid/hid-uclogic.c
@@ -466,6 +466,86 @@ static __u8 twhl850_rdesc_fixed2[] = {
0xC0 /* End Collection */
};
+/*
+ * See TWHA60 description, device and HID report descriptors at
+ * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_TWHA60
+ */
+
+/* Size of the original descriptors of TWHA60 tablet */
+#define TWHA60_RDESC_ORIG_SIZE0 254
+#define TWHA60_RDESC_ORIG_SIZE1 139
+
+/* Fixed TWHA60 report descriptor, interface 0 (stylus) */
+static __u8 twha60_rdesc_fixed0[] = {
+ 0x05, 0x0D, /* Usage Page (Digitizer), */
+ 0x09, 0x02, /* Usage (Pen), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0x09, /* Report ID (9), */
+ 0x09, 0x20, /* Usage (Stylus), */
+ 0xA0, /* Collection (Physical), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x09, 0x42, /* Usage (Tip Switch), */
+ 0x09, 0x44, /* Usage (Barrel Switch), */
+ 0x09, 0x46, /* Usage (Tablet Pick), */
+ 0x14, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x95, 0x03, /* Report Count (3), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x04, /* Report Count (4), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x09, 0x32, /* Usage (In Range), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x75, 0x10, /* Report Size (16), */
+ 0x95, 0x01, /* Report Count (1), */
+ 0x14, /* Logical Minimum (0), */
+ 0xA4, /* Push, */
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x55, 0xFD, /* Unit Exponent (-3), */
+ 0x65, 0x13, /* Unit (Inch), */
+ 0x34, /* Physical Minimum (0), */
+ 0x09, 0x30, /* Usage (X), */
+ 0x46, 0x10, 0x27, /* Physical Maximum (10000), */
+ 0x27, 0x3F, 0x9C,
+ 0x00, 0x00, /* Logical Maximum (39999), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x09, 0x31, /* Usage (Y), */
+ 0x46, 0x6A, 0x18, /* Physical Maximum (6250), */
+ 0x26, 0xA7, 0x61, /* Logical Maximum (24999), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xB4, /* Pop, */
+ 0x09, 0x30, /* Usage (Tip Pressure), */
+ 0x26, 0xFF, 0x03, /* Logical Maximum (1023), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0xC0, /* End Collection, */
+ 0xC0 /* End Collection */
+};
+
+/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */
+static __u8 twha60_rdesc_fixed1[] = {
+ 0x05, 0x01, /* Usage Page (Desktop), */
+ 0x09, 0x06, /* Usage (Keyboard), */
+ 0xA1, 0x01, /* Collection (Application), */
+ 0x85, 0x05, /* Report ID (5), */
+ 0x05, 0x07, /* Usage Page (Keyboard), */
+ 0x14, /* Logical Minimum (0), */
+ 0x25, 0x01, /* Logical Maximum (1), */
+ 0x75, 0x01, /* Report Size (1), */
+ 0x95, 0x08, /* Report Count (8), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0x95, 0x0C, /* Report Count (12), */
+ 0x19, 0x3A, /* Usage Minimum (KB F1), */
+ 0x29, 0x45, /* Usage Maximum (KB F12), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x0C, /* Report Count (12), */
+ 0x19, 0x68, /* Usage Minimum (KB F13), */
+ 0x29, 0x73, /* Usage Maximum (KB F24), */
+ 0x81, 0x02, /* Input (Variable), */
+ 0x95, 0x08, /* Report Count (8), */
+ 0x81, 0x01, /* Input (Constant), */
+ 0xC0 /* End Collection */
+};
+
static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
@@ -525,6 +605,22 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
break;
}
break;
+ case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60:
+ switch (iface_num) {
+ case 0:
+ if (*rsize == TWHA60_RDESC_ORIG_SIZE0) {
+ rdesc = twha60_rdesc_fixed0;
+ *rsize = sizeof(twha60_rdesc_fixed0);
+ }
+ break;
+ case 1:
+ if (*rsize == TWHA60_RDESC_ORIG_SIZE1) {
+ rdesc = twha60_rdesc_fixed1;
+ *rsize = sizeof(twha60_rdesc_fixed1);
+ }
+ break;
+ }
+ break;
}
return rdesc;
@@ -543,6 +639,8 @@ static const struct hid_device_id uclogic_devices[] = {
USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
+ USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
{ }
};
MODULE_DEVICE_TABLE(hid, uclogic_devices);
diff --git a/drivers/hid/hid-wacom.c b/drivers/hid/hid-wacom.c
index fe23a1eb586b..2f60da9ed066 100644
--- a/drivers/hid/hid-wacom.c
+++ b/drivers/hid/hid-wacom.c
@@ -5,7 +5,6 @@
* Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
* Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
* Copyright (c) 2006-2007 Jiri Kosina
- * Copyright (c) 2007 Paul Walmsley
* Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
* Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
* Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
@@ -33,6 +32,8 @@
#define PAD_DEVICE_ID 0x0F
#define WAC_CMD_LED_CONTROL 0x20
+#define WAC_CMD_ICON_START_STOP 0x21
+#define WAC_CMD_ICON_TRANSFER 0x26
struct wacom_data {
__u16 tool;
@@ -69,6 +70,91 @@ static enum power_supply_property wacom_ac_props[] = {
POWER_SUPPLY_PROP_SCOPE,
};
+static void wacom_scramble(__u8 *image)
+{
+ __u16 mask;
+ __u16 s1;
+ __u16 s2;
+ __u16 r1 ;
+ __u16 r2 ;
+ __u16 r;
+ __u8 buf[256];
+ int i, w, x, y, z;
+
+ for (x = 0; x < 32; x++) {
+ for (y = 0; y < 8; y++)
+ buf[(8 * x) + (7 - y)] = image[(8 * x) + y];
+ }
+
+ /* Change 76543210 into GECA6420 as required by Intuos4 WL
+ * HGFEDCBA HFDB7531
+ */
+ for (x = 0; x < 4; x++) {
+ for (y = 0; y < 4; y++) {
+ for (z = 0; z < 8; z++) {
+ mask = 0x0001;
+ r1 = 0;
+ r2 = 0;
+ i = (x << 6) + (y << 4) + z;
+ s1 = buf[i];
+ s2 = buf[i+8];
+ for (w = 0; w < 8; w++) {
+ r1 |= (s1 & mask);
+ r2 |= (s2 & mask);
+ s1 <<= 1;
+ s2 <<= 1;
+ mask <<= 2;
+ }
+ r = r1 | (r2 << 1);
+ i = (x << 6) + (y << 4) + (z << 1);
+ image[i] = 0xFF & r;
+ image[i+1] = (0xFF00 & r) >> 8;
+ }
+ }
+ }
+}
+
+static void wacom_set_image(struct hid_device *hdev, const char *image,
+ __u8 icon_no)
+{
+ __u8 rep_data[68];
+ __u8 p[256];
+ int ret, i, j;
+
+ for (i = 0; i < 256; i++)
+ p[i] = image[i];
+
+ 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);
+ if (ret < 0)
+ goto err;
+
+ rep_data[0] = WAC_CMD_ICON_TRANSFER;
+ rep_data[1] = icon_no & 0x07;
+
+ wacom_scramble(p);
+
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 64; j++)
+ 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);
+ }
+
+ 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);
+
+err:
+ return;
+}
+
static void wacom_leds_set_brightness(struct led_classdev *led_dev,
enum led_brightness value)
{
@@ -91,7 +177,10 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev,
if (buf) {
buf[0] = WAC_CMD_LED_CONTROL;
buf[1] = led;
- buf[2] = value;
+ buf[2] = value >> 2;
+ buf[3] = value;
+ /* use fixed brightness for OLEDs */
+ buf[4] = 0x08;
hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT);
kfree(buf);
}
@@ -317,6 +406,34 @@ static ssize_t wacom_store_speed(struct device *dev,
static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
wacom_show_speed, wacom_store_speed);
+#define WACOM_STORE(OLED_ID) \
+static ssize_t wacom_oled##OLED_ID##_store(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct hid_device *hdev = container_of(dev, struct hid_device, \
+ dev); \
+ \
+ if (count != 256) \
+ return -EINVAL; \
+ \
+ wacom_set_image(hdev, buf, OLED_ID); \
+ \
+ return count; \
+} \
+ \
+static DEVICE_ATTR(oled##OLED_ID##_img, S_IWUSR | S_IWGRP, NULL, \
+ wacom_oled##OLED_ID##_store)
+
+WACOM_STORE(0);
+WACOM_STORE(1);
+WACOM_STORE(2);
+WACOM_STORE(3);
+WACOM_STORE(4);
+WACOM_STORE(5);
+WACOM_STORE(6);
+WACOM_STORE(7);
+
static int wacom_gr_parse_report(struct hid_device *hdev,
struct wacom_data *wdata,
struct input_dev *input, unsigned char *data)
@@ -717,17 +834,33 @@ static int wacom_probe(struct hid_device *hdev,
hid_warn(hdev,
"can't create sysfs speed attribute err: %d\n", ret);
+#define OLED_INIT(OLED_ID) \
+ do { \
+ ret = device_create_file(&hdev->dev, \
+ &dev_attr_oled##OLED_ID##_img); \
+ if (ret) \
+ hid_warn(hdev, \
+ "can't create sysfs oled attribute, err: %d\n", ret);\
+ } while (0)
+
+OLED_INIT(0);
+OLED_INIT(1);
+OLED_INIT(2);
+OLED_INIT(3);
+OLED_INIT(4);
+OLED_INIT(5);
+OLED_INIT(6);
+OLED_INIT(7);
+
wdata->features = 0;
wacom_set_features(hdev, 1);
if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) {
sprintf(hdev->name, "%s", "Wacom Intuos4 WL");
ret = wacom_initialize_leds(hdev);
- if (ret) {
+ if (ret)
hid_warn(hdev,
"can't create led attribute, err: %d\n", ret);
- goto destroy_leds;
- }
}
wdata->battery.properties = wacom_battery_props;
@@ -740,8 +873,8 @@ static int wacom_probe(struct hid_device *hdev,
ret = power_supply_register(&hdev->dev, &wdata->battery);
if (ret) {
- hid_warn(hdev, "can't create sysfs battery attribute, err: %d\n",
- ret);
+ hid_err(hdev, "can't create sysfs battery attribute, err: %d\n",
+ ret);
goto err_battery;
}
@@ -756,8 +889,8 @@ static int wacom_probe(struct hid_device *hdev,
ret = power_supply_register(&hdev->dev, &wdata->ac);
if (ret) {
- hid_warn(hdev,
- "can't create ac battery attribute, err: %d\n", ret);
+ hid_err(hdev,
+ "can't create ac battery attribute, err: %d\n", ret);
goto err_ac;
}
@@ -767,10 +900,17 @@ static int wacom_probe(struct hid_device *hdev,
err_ac:
power_supply_unregister(&wdata->battery);
err_battery:
+ wacom_destroy_leds(hdev);
+ device_remove_file(&hdev->dev, &dev_attr_oled0_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled1_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled2_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled3_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled4_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled5_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled6_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled7_img);
device_remove_file(&hdev->dev, &dev_attr_speed);
hid_hw_stop(hdev);
-destroy_leds:
- wacom_destroy_leds(hdev);
err_free:
kfree(wdata);
return ret;
@@ -781,6 +921,14 @@ static void wacom_remove(struct hid_device *hdev)
struct wacom_data *wdata = hid_get_drvdata(hdev);
wacom_destroy_leds(hdev);
+ device_remove_file(&hdev->dev, &dev_attr_oled0_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled1_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled2_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled3_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled4_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled5_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled6_img);
+ device_remove_file(&hdev->dev, &dev_attr_oled7_img);
device_remove_file(&hdev->dev, &dev_attr_speed);
hid_hw_stop(hdev);
diff --git a/drivers/hid/hid-waltop.c b/drivers/hid/hid-waltop.c
index 745e4e9a8cf2..bb536ab5941e 100644
--- a/drivers/hid/hid-waltop.c
+++ b/drivers/hid/hid-waltop.c
@@ -638,28 +638,6 @@ static __u8 sirius_battery_free_tablet_rdesc_fixed[] = {
0xC0 /* End Collection */
};
-static int waltop_probe(struct hid_device *hdev,
- const struct hid_device_id *id)
-{
- int ret;
-
- ret = hid_parse(hdev);
- if (ret) {
- hid_err(hdev, "parse failed\n");
- goto err;
- }
-
- ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
- if (ret) {
- hid_err(hdev, "hw start failed\n");
- goto err;
- }
-
- return 0;
-err:
- return ret;
-}
-
static __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc,
unsigned int *rsize)
{
@@ -776,11 +754,6 @@ static int waltop_raw_event(struct hid_device *hdev, struct hid_report *report,
return 0;
}
-static void waltop_remove(struct hid_device *hdev)
-{
- hid_hw_stop(hdev);
-}
-
static const struct hid_device_id waltop_devices[] = {
{ HID_USB_DEVICE(USB_VENDOR_ID_WALTOP,
USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
@@ -803,10 +776,8 @@ MODULE_DEVICE_TABLE(hid, waltop_devices);
static struct hid_driver waltop_driver = {
.name = "waltop",
.id_table = waltop_devices,
- .probe = waltop_probe,
.report_fixup = waltop_report_fixup,
.raw_event = waltop_raw_event,
- .remove = waltop_remove,
};
static int __init waltop_init(void)
diff --git a/drivers/hid/hid-wiimote-ext.c b/drivers/hid/hid-wiimote-ext.c
index 0a1805c9b0e5..bc85bf29062e 100644
--- a/drivers/hid/hid-wiimote-ext.c
+++ b/drivers/hid/hid-wiimote-ext.c
@@ -28,12 +28,14 @@ struct wiimote_ext {
bool mp_plugged;
bool motionp;
__u8 ext_type;
+ __u16 calib[4][3];
};
enum wiiext_type {
WIIEXT_NONE, /* placeholder */
WIIEXT_CLASSIC, /* Nintendo classic controller */
WIIEXT_NUNCHUCK, /* Nintendo nunchuck controller */
+ WIIEXT_BALANCE_BOARD, /* Nintendo balance board controller */
};
enum wiiext_keys {
@@ -126,6 +128,7 @@ error:
static __u8 ext_read(struct wiimote_ext *ext)
{
ssize_t ret;
+ __u8 buf[24], i, j, offs = 0;
__u8 rmem[2], wmem;
__u8 type = WIIEXT_NONE;
@@ -151,6 +154,28 @@ static __u8 ext_read(struct wiimote_ext *ext)
type = WIIEXT_NUNCHUCK;
else if (rmem[0] == 0x01 && rmem[1] == 0x01)
type = WIIEXT_CLASSIC;
+ else if (rmem[0] == 0x04 && rmem[1] == 0x02)
+ type = WIIEXT_BALANCE_BOARD;
+ }
+
+ /* get balance board calibration data */
+ if (type == WIIEXT_BALANCE_BOARD) {
+ ret = wiimote_cmd_read(ext->wdata, 0xa40024, buf, 12);
+ ret += wiimote_cmd_read(ext->wdata, 0xa40024 + 12,
+ buf + 12, 12);
+
+ if (ret != 24) {
+ type = WIIEXT_NONE;
+ } else {
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 4; j++) {
+ ext->calib[j][i] = buf[offs];
+ ext->calib[j][i] <<= 8;
+ ext->calib[j][i] |= buf[offs + 1];
+ offs += 2;
+ }
+ }
+ }
}
wiimote_cmd_release(ext->wdata);
@@ -509,6 +534,71 @@ static void handler_classic(struct wiimote_ext *ext, const __u8 *payload)
input_sync(ext->input);
}
+static void handler_balance_board(struct wiimote_ext *ext, const __u8 *payload)
+{
+ __s32 val[4], tmp;
+ unsigned int i;
+
+ /* Byte | 8 7 6 5 4 3 2 1 |
+ * -----+--------------------------+
+ * 1 | Top Right <15:8> |
+ * 2 | Top Right <7:0> |
+ * -----+--------------------------+
+ * 3 | Bottom Right <15:8> |
+ * 4 | Bottom Right <7:0> |
+ * -----+--------------------------+
+ * 5 | Top Left <15:8> |
+ * 6 | Top Left <7:0> |
+ * -----+--------------------------+
+ * 7 | Bottom Left <15:8> |
+ * 8 | Bottom Left <7:0> |
+ * -----+--------------------------+
+ *
+ * These values represent the weight-measurements of the Wii-balance
+ * board with 16bit precision.
+ *
+ * The balance-board is never reported interleaved with motionp.
+ */
+
+ val[0] = payload[0];
+ val[0] <<= 8;
+ val[0] |= payload[1];
+
+ val[1] = payload[2];
+ val[1] <<= 8;
+ val[1] |= payload[3];
+
+ val[2] = payload[4];
+ val[2] <<= 8;
+ val[2] |= payload[5];
+
+ val[3] = payload[6];
+ val[3] <<= 8;
+ val[3] |= payload[7];
+
+ /* apply calibration data */
+ for (i = 0; i < 4; i++) {
+ if (val[i] < ext->calib[i][1]) {
+ tmp = val[i] - ext->calib[i][0];
+ tmp *= 1700;
+ tmp /= ext->calib[i][1] - ext->calib[i][0];
+ } else {
+ tmp = val[i] - ext->calib[i][1];
+ tmp *= 1700;
+ tmp /= ext->calib[i][2] - ext->calib[i][1];
+ tmp += 1700;
+ }
+ val[i] = tmp;
+ }
+
+ input_report_abs(ext->input, ABS_HAT0X, val[0]);
+ input_report_abs(ext->input, ABS_HAT0Y, val[1]);
+ input_report_abs(ext->input, ABS_HAT1X, val[2]);
+ input_report_abs(ext->input, ABS_HAT1Y, val[3]);
+
+ input_sync(ext->input);
+}
+
/* call this with state.lock spinlock held */
void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
{
@@ -523,6 +613,8 @@ void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
handler_nunchuck(ext, payload);
} else if (ext->ext_type == WIIEXT_CLASSIC) {
handler_classic(ext, payload);
+ } else if (ext->ext_type == WIIEXT_BALANCE_BOARD) {
+ handler_balance_board(ext, payload);
}
}
@@ -551,6 +643,11 @@ static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "motionp+classic\n");
else
return sprintf(buf, "classic\n");
+ } else if (type == WIIEXT_BALANCE_BOARD) {
+ if (motionp)
+ return sprintf(buf, "motionp+balanceboard\n");
+ else
+ return sprintf(buf, "balanceboard\n");
} else {
if (motionp)
return sprintf(buf, "motionp\n");
diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
index 3b6f7bf5a77e..17d15bb610d1 100644
--- a/drivers/hid/hidraw.c
+++ b/drivers/hid/hidraw.c
@@ -42,6 +42,7 @@ static struct cdev hidraw_cdev;
static struct class *hidraw_class;
static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
static DEFINE_MUTEX(minors_lock);
+static void drop_ref(struct hidraw *hid, int exists_bit);
static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
@@ -113,7 +114,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
__u8 *buf;
int ret = 0;
- if (!hidraw_table[minor]) {
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
ret = -ENODEV;
goto out;
}
@@ -261,7 +262,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
}
mutex_lock(&minors_lock);
- if (!hidraw_table[minor]) {
+ if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
err = -ENODEV;
goto out_unlock;
}
@@ -298,36 +299,12 @@ out:
static int hidraw_release(struct inode * inode, struct file * file)
{
unsigned int minor = iminor(inode);
- struct hidraw *dev;
struct hidraw_list *list = file->private_data;
- int ret;
- int i;
-
- mutex_lock(&minors_lock);
- if (!hidraw_table[minor]) {
- ret = -ENODEV;
- goto unlock;
- }
+ drop_ref(hidraw_table[minor], 0);
list_del(&list->node);
- dev = hidraw_table[minor];
- if (!--dev->open) {
- if (list->hidraw->exist) {
- hid_hw_power(dev->hid, PM_HINT_NORMAL);
- hid_hw_close(dev->hid);
- } else {
- kfree(list->hidraw);
- }
- }
-
- for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i)
- kfree(list->buffer[i].value);
kfree(list);
- ret = 0;
-unlock:
- mutex_unlock(&minors_lock);
-
- return ret;
+ return 0;
}
static long hidraw_ioctl(struct file *file, unsigned int cmd,
@@ -529,21 +506,7 @@ EXPORT_SYMBOL_GPL(hidraw_connect);
void hidraw_disconnect(struct hid_device *hid)
{
struct hidraw *hidraw = hid->hidraw;
-
- mutex_lock(&minors_lock);
- hidraw->exist = 0;
-
- device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
-
- hidraw_table[hidraw->minor] = NULL;
-
- if (hidraw->open) {
- hid_hw_close(hid);
- wake_up_interruptible(&hidraw->wait);
- } else {
- kfree(hidraw);
- }
- mutex_unlock(&minors_lock);
+ drop_ref(hidraw, 1);
}
EXPORT_SYMBOL_GPL(hidraw_disconnect);
@@ -559,21 +522,28 @@ int __init hidraw_init(void)
if (result < 0) {
pr_warn("can't get major number\n");
- result = 0;
goto out;
}
hidraw_class = class_create(THIS_MODULE, "hidraw");
if (IS_ERR(hidraw_class)) {
result = PTR_ERR(hidraw_class);
- unregister_chrdev(hidraw_major, "hidraw");
- goto out;
+ goto error_cdev;
}
cdev_init(&hidraw_cdev, &hidraw_ops);
- cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
+ result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
+ if (result < 0)
+ goto error_class;
+
out:
return result;
+
+error_class:
+ class_destroy(hidraw_class);
+error_cdev:
+ unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
+ goto out;
}
void hidraw_exit(void)
@@ -585,3 +555,23 @@ void hidraw_exit(void)
unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
}
+
+static void drop_ref(struct hidraw *hidraw, int exists_bit)
+{
+ mutex_lock(&minors_lock);
+ if (exists_bit) {
+ hid_hw_close(hidraw->hid);
+ hidraw->exist = 0;
+ if (hidraw->open)
+ wake_up_interruptible(&hidraw->wait);
+ } else {
+ --hidraw->open;
+ }
+
+ if (!hidraw->open && !hidraw->exist) {
+ device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
+ hidraw_table[hidraw->minor] = NULL;
+ kfree(hidraw);
+ }
+ mutex_unlock(&minors_lock);
+}
diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
index dedd8e4e5c6d..8e0c4bf94ebc 100644
--- a/drivers/hid/usbhid/hid-core.c
+++ b/drivers/hid/usbhid/hid-core.c
@@ -1415,20 +1415,20 @@ static int hid_post_reset(struct usb_interface *intf)
* configuration descriptors passed, we already know that
* the size of the HID report descriptor has not changed.
*/
- rdesc = kmalloc(hid->rsize, GFP_KERNEL);
+ rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL);
if (!rdesc) {
dbg_hid("couldn't allocate rdesc memory (post_reset)\n");
return 1;
}
status = hid_get_class_descriptor(dev,
interface->desc.bInterfaceNumber,
- HID_DT_REPORT, rdesc, hid->rsize);
+ HID_DT_REPORT, rdesc, hid->dev_rsize);
if (status < 0) {
dbg_hid("reading report descriptor failed (post_reset)\n");
kfree(rdesc);
return 1;
}
- status = memcmp(rdesc, hid->rdesc, hid->rsize);
+ status = memcmp(rdesc, hid->dev_rdesc, hid->dev_rsize);
kfree(rdesc);
if (status != 0) {
dbg_hid("report descriptor changed\n");
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 991e85c7325c..11c7932dc7e6 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -70,12 +70,13 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
{ USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
- { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index 86f8885aeb45..3648f8f0f368 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -26,6 +26,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/hyperv.h>
+#include <linux/version.h>
#include <asm/hyperv.h>
#include "hyperv_vmbus.h"
@@ -38,28 +39,6 @@ struct hv_context hv_context = {
};
/*
- * query_hypervisor_presence
- * - Query the cpuid for presence of windows hypervisor
- */
-static int query_hypervisor_presence(void)
-{
- unsigned int eax;
- unsigned int ebx;
- unsigned int ecx;
- unsigned int edx;
- unsigned int op;
-
- eax = 0;
- ebx = 0;
- ecx = 0;
- edx = 0;
- op = HVCPUID_VERSION_FEATURES;
- cpuid(op, &eax, &ebx, &ecx, &edx);
-
- return ecx & HV_PRESENT_BIT;
-}
-
-/*
* query_hypervisor_info - Get version info of the windows hypervisor
*/
static int query_hypervisor_info(void)
@@ -159,14 +138,13 @@ int hv_init(void)
memset(hv_context.synic_message_page, 0,
sizeof(void *) * NR_CPUS);
- if (!query_hypervisor_presence())
- goto cleanup;
-
max_leaf = query_hypervisor_info();
- /* Write our OS info */
- wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
- hv_context.guestid = HV_LINUX_GUEST_ID;
+ /*
+ * Write our OS ID.
+ */
+ hv_context.guestid = generate_guest_id(0, LINUX_VERSION_CODE, 0);
+ wrmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid);
/* See if the hypercall page is already set */
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c
index 0012eed6d872..ed50e9e83c61 100644
--- a/drivers/hv/hv_kvp.c
+++ b/drivers/hv/hv_kvp.c
@@ -48,13 +48,24 @@ static struct {
void *kvp_context; /* for the channel callback */
} kvp_transaction;
+/*
+ * Before we can accept KVP messages from the host, we need
+ * to handshake with the user level daemon. This state tracks
+ * if we are in the handshake phase.
+ */
+static bool in_hand_shake = true;
+
+/*
+ * This state maintains the version number registered by the daemon.
+ */
+static int dm_reg_value;
+
static void kvp_send_key(struct work_struct *dummy);
-#define TIMEOUT_FIRED 1
-static void kvp_respond_to_host(char *key, char *value, int error);
+static void kvp_respond_to_host(struct hv_kvp_msg *msg, int error);
static void kvp_work_func(struct work_struct *dummy);
-static void kvp_register(void);
+static void kvp_register(int);
static DECLARE_DELAYED_WORK(kvp_work, kvp_work_func);
static DECLARE_WORK(kvp_sendkey_work, kvp_send_key);
@@ -68,7 +79,7 @@ static u8 *recv_buffer;
*/
static void
-kvp_register(void)
+kvp_register(int reg_value)
{
struct cn_msg *msg;
@@ -83,7 +94,7 @@ kvp_register(void)
msg->id.idx = CN_KVP_IDX;
msg->id.val = CN_KVP_VAL;
- kvp_msg->kvp_hdr.operation = KVP_OP_REGISTER;
+ kvp_msg->kvp_hdr.operation = reg_value;
strcpy(version, HV_DRV_VERSION);
msg->len = sizeof(struct hv_kvp_msg);
cn_netlink_send(msg, 0, GFP_ATOMIC);
@@ -97,9 +108,43 @@ kvp_work_func(struct work_struct *dummy)
* If the timer fires, the user-mode component has not responded;
* process the pending transaction.
*/
- kvp_respond_to_host("Unknown key", "Guest timed out", TIMEOUT_FIRED);
+ kvp_respond_to_host(NULL, HV_E_FAIL);
+}
+
+static int kvp_handle_handshake(struct hv_kvp_msg *msg)
+{
+ int ret = 1;
+
+ switch (msg->kvp_hdr.operation) {
+ case KVP_OP_REGISTER:
+ dm_reg_value = KVP_OP_REGISTER;
+ pr_info("KVP: IP injection functionality not available\n");
+ pr_info("KVP: Upgrade the KVP daemon\n");
+ break;
+ case KVP_OP_REGISTER1:
+ dm_reg_value = KVP_OP_REGISTER1;
+ break;
+ default:
+ pr_info("KVP: incompatible daemon\n");
+ pr_info("KVP: KVP version: %d, Daemon version: %d\n",
+ KVP_OP_REGISTER1, msg->kvp_hdr.operation);
+ ret = 0;
+ }
+
+ if (ret) {
+ /*
+ * We have a compatible daemon; complete the handshake.
+ */
+ pr_info("KVP: user-mode registering done.\n");
+ kvp_register(dm_reg_value);
+ kvp_transaction.active = false;
+ if (kvp_transaction.kvp_context)
+ hv_kvp_onchannelcallback(kvp_transaction.kvp_context);
+ }
+ return ret;
}
+
/*
* Callback when data is received from user mode.
*/
@@ -109,29 +154,165 @@ kvp_cn_callback(struct cn_msg *msg, struct netlink_skb_parms *nsp)
{
struct hv_kvp_msg *message;
struct hv_kvp_msg_enumerate *data;
+ int error = 0;
message = (struct hv_kvp_msg *)msg->data;
- switch (message->kvp_hdr.operation) {
+
+ /*
+ * If we are negotiating the version information
+ * with the daemon; handle that first.
+ */
+
+ if (in_hand_shake) {
+ if (kvp_handle_handshake(message))
+ in_hand_shake = false;
+ return;
+ }
+
+ /*
+ * Based on the version of the daemon, we propagate errors from the
+ * daemon differently.
+ */
+
+ data = &message->body.kvp_enum_data;
+
+ switch (dm_reg_value) {
case KVP_OP_REGISTER:
- pr_info("KVP: user-mode registering done.\n");
- kvp_register();
- kvp_transaction.active = false;
- hv_kvp_onchannelcallback(kvp_transaction.kvp_context);
+ /*
+ * Null string is used to pass back error condition.
+ */
+ if (data->data.key[0] == 0)
+ error = HV_S_CONT;
break;
- default:
- data = &message->body.kvp_enum_data;
+ case KVP_OP_REGISTER1:
/*
- * Complete the transaction by forwarding the key value
- * to the host. But first, cancel the timeout.
+ * We use the message header information from
+ * the user level daemon to transmit errors.
*/
- if (cancel_delayed_work_sync(&kvp_work))
- kvp_respond_to_host(data->data.key,
- data->data.value,
- !strlen(data->data.key));
+ error = message->error;
+ break;
+ }
+
+ /*
+ * Complete the transaction by forwarding the key value
+ * to the host. But first, cancel the timeout.
+ */
+ if (cancel_delayed_work_sync(&kvp_work))
+ kvp_respond_to_host(message, error);
+}
+
+
+static int process_ob_ipinfo(void *in_msg, void *out_msg, int op)
+{
+ struct hv_kvp_msg *in = in_msg;
+ struct hv_kvp_ip_msg *out = out_msg;
+ int len;
+
+ switch (op) {
+ case KVP_OP_GET_IP_INFO:
+ /*
+ * Transform all parameters into utf16 encoding.
+ */
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.ip_addr,
+ strlen((char *)in->body.kvp_ip_val.ip_addr),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.ip_addr,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.sub_net,
+ strlen((char *)in->body.kvp_ip_val.sub_net),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.sub_net,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.gate_way,
+ strlen((char *)in->body.kvp_ip_val.gate_way),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.gate_way,
+ MAX_GATEWAY_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.dns_addr,
+ strlen((char *)in->body.kvp_ip_val.dns_addr),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.dns_addr,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ len = utf8s_to_utf16s((char *)in->body.kvp_ip_val.adapter_id,
+ strlen((char *)in->body.kvp_ip_val.adapter_id),
+ UTF16_HOST_ENDIAN,
+ (wchar_t *)out->kvp_ip_val.adapter_id,
+ MAX_IP_ADDR_SIZE);
+ if (len < 0)
+ return len;
+
+ out->kvp_ip_val.dhcp_enabled =
+ in->body.kvp_ip_val.dhcp_enabled;
+ out->kvp_ip_val.addr_family =
+ in->body.kvp_ip_val.addr_family;
+ }
+
+ return 0;
+}
+
+static void process_ib_ipinfo(void *in_msg, void *out_msg, int op)
+{
+ struct hv_kvp_ip_msg *in = in_msg;
+ struct hv_kvp_msg *out = out_msg;
+
+ switch (op) {
+ case KVP_OP_SET_IP_INFO:
+ /*
+ * Transform all parameters into utf8 encoding.
+ */
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.ip_addr,
+ MAX_IP_ADDR_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.ip_addr,
+ MAX_IP_ADDR_SIZE);
+
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.sub_net,
+ MAX_IP_ADDR_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.sub_net,
+ MAX_IP_ADDR_SIZE);
+
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.gate_way,
+ MAX_GATEWAY_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.gate_way,
+ MAX_GATEWAY_SIZE);
+
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.dns_addr,
+ MAX_IP_ADDR_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.dns_addr,
+ MAX_IP_ADDR_SIZE);
+
+ out->body.kvp_ip_val.dhcp_enabled = in->kvp_ip_val.dhcp_enabled;
+
+ default:
+ utf16s_to_utf8s((wchar_t *)in->kvp_ip_val.adapter_id,
+ MAX_ADAPTER_ID_SIZE,
+ UTF16_LITTLE_ENDIAN,
+ (__u8 *)out->body.kvp_ip_val.adapter_id,
+ MAX_ADAPTER_ID_SIZE);
+
+ out->body.kvp_ip_val.addr_family = in->kvp_ip_val.addr_family;
}
}
+
+
+
static void
kvp_send_key(struct work_struct *dummy)
{
@@ -167,6 +348,12 @@ kvp_send_key(struct work_struct *dummy)
*/
switch (message->kvp_hdr.operation) {
+ case KVP_OP_SET_IP_INFO:
+ process_ib_ipinfo(in_msg, message, KVP_OP_SET_IP_INFO);
+ break;
+ case KVP_OP_GET_IP_INFO:
+ process_ib_ipinfo(in_msg, message, KVP_OP_GET_IP_INFO);
+ break;
case KVP_OP_SET:
switch (in_msg->body.kvp_set.data.value_type) {
case REG_SZ:
@@ -243,17 +430,19 @@ kvp_send_key(struct work_struct *dummy)
*/
static void
-kvp_respond_to_host(char *key, char *value, int error)
+kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error)
{
struct hv_kvp_msg *kvp_msg;
struct hv_kvp_exchg_msg_value *kvp_data;
char *key_name;
+ char *value;
struct icmsg_hdr *icmsghdrp;
int keylen = 0;
int valuelen = 0;
u32 buf_len;
struct vmbus_channel *channel;
u64 req_id;
+ int ret;
/*
* If a transaction is not active; log and return.
@@ -287,6 +476,7 @@ kvp_respond_to_host(char *key, char *value, int error)
*/
return;
+ icmsghdrp->status = error;
/*
* If the error parameter is set, terminate the host's enumeration
@@ -294,20 +484,27 @@ kvp_respond_to_host(char *key, char *value, int error)
*/
if (error) {
/*
- * Something failed or the we have timedout;
- * terminate the current host-side iteration.
+ * Something failed or we have timedout;
+ * terminate the current host-side iteration.
*/
- icmsghdrp->status = HV_S_CONT;
goto response_done;
}
- icmsghdrp->status = HV_S_OK;
-
kvp_msg = (struct hv_kvp_msg *)
&recv_buffer[sizeof(struct vmbuspipe_hdr) +
sizeof(struct icmsg_hdr)];
switch (kvp_transaction.kvp_msg->kvp_hdr.operation) {
+ case KVP_OP_GET_IP_INFO:
+ ret = process_ob_ipinfo(msg_to_host,
+ (struct hv_kvp_ip_msg *)kvp_msg,
+ KVP_OP_GET_IP_INFO);
+ if (ret < 0)
+ icmsghdrp->status = HV_E_FAIL;
+
+ goto response_done;
+ case KVP_OP_SET_IP_INFO:
+ goto response_done;
case KVP_OP_GET:
kvp_data = &kvp_msg->body.kvp_get.data;
goto copy_value;
@@ -321,7 +518,7 @@ kvp_respond_to_host(char *key, char *value, int error)
}
kvp_data = &kvp_msg->body.kvp_enum_data.data;
- key_name = key;
+ key_name = msg_to_host->body.kvp_enum_data.data.key;
/*
* The windows host expects the key/value pair to be encoded
@@ -335,6 +532,7 @@ kvp_respond_to_host(char *key, char *value, int error)
kvp_data->key_size = 2*(keylen + 1); /* utf16 encoding */
copy_value:
+ value = msg_to_host->body.kvp_enum_data.data.value;
valuelen = utf8s_to_utf16s(value, strlen(value), UTF16_HOST_ENDIAN,
(wchar_t *) kvp_data->value,
(HV_KVP_EXCHANGE_MAX_VALUE_SIZE / 2) - 2);
@@ -387,7 +585,8 @@ void hv_kvp_onchannelcallback(void *context)
return;
}
- vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE, &recvlen, &requestid);
+ vmbus_recvpacket(channel, recv_buffer, PAGE_SIZE * 2, &recvlen,
+ &requestid);
if (recvlen > 0) {
icmsghdrp = (struct icmsg_hdr *)&recv_buffer[
diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c
index d3ac6a40118b..a0667de7a04c 100644
--- a/drivers/hv/hv_util.c
+++ b/drivers/hv/hv_util.c
@@ -263,7 +263,7 @@ static int util_probe(struct hv_device *dev,
(struct hv_util_service *)dev_id->driver_data;
int ret;
- srv->recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ srv->recv_buffer = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
if (!srv->recv_buffer)
return -ENOMEM;
if (srv->util_init) {
@@ -274,7 +274,7 @@ static int util_probe(struct hv_device *dev,
}
}
- ret = vmbus_open(dev->channel, 2 * PAGE_SIZE, 2 * PAGE_SIZE, NULL, 0,
+ ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
srv->util_cb, dev->channel);
if (ret)
goto error;
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 0614ff3a7d7e..d8d1fadb398a 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -410,10 +410,49 @@ enum {
#define HV_PRESENT_BIT 0x80000000
-#define HV_LINUX_GUEST_ID_LO 0x00000000
-#define HV_LINUX_GUEST_ID_HI 2976579765
-#define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \
- HV_LINUX_GUEST_ID_LO)
+/*
+ * The guest OS needs to register the guest ID with the hypervisor.
+ * The guest ID is a 64 bit entity and the structure of this ID is
+ * specified in the Hyper-V specification:
+ *
+ * http://msdn.microsoft.com/en-us/library/windows/hardware/ff542653%28v=vs.85%29.aspx
+ *
+ * While the current guideline does not specify how Linux guest ID(s)
+ * need to be generated, our plan is to publish the guidelines for
+ * Linux and other guest operating systems that currently are hosted
+ * on Hyper-V. The implementation here conforms to this yet
+ * unpublished guidelines.
+ *
+ *
+ * Bit(s)
+ * 63 - Indicates if the OS is Open Source or not; 1 is Open Source
+ * 62:56 - Os Type; Linux is 0x100
+ * 55:48 - Distro specific identification
+ * 47:16 - Linux kernel version number
+ * 15:0 - Distro specific identification
+ *
+ *
+ */
+
+#define HV_LINUX_VENDOR_ID 0x8100
+
+/*
+ * Generate the guest ID based on the guideline described above.
+ */
+
+static inline __u64 generate_guest_id(__u8 d_info1, __u32 kernel_version,
+ __u16 d_info2)
+{
+ __u64 guest_id = 0;
+
+ guest_id = (((__u64)HV_LINUX_VENDOR_ID) << 48);
+ guest_id |= (((__u64)(d_info1)) << 48);
+ guest_id |= (((__u64)(kernel_version)) << 16);
+ guest_id |= ((__u64)(d_info2));
+
+ return guest_id;
+}
+
#define HV_CPU_POWER_MANAGEMENT (1 << 0)
#define HV_RECOMMENDATIONS_MAX 4
diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 4748086eaaf2..8e1a9ec53003 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -34,6 +34,7 @@
#include <linux/completion.h>
#include <linux/hyperv.h>
#include <asm/hyperv.h>
+#include <asm/hypervisor.h>
#include "hyperv_vmbus.h"
@@ -146,43 +147,9 @@ static ssize_t vmbus_show_device_attr(struct device *dev,
get_channel_info(hv_dev, device_info);
if (!strcmp(dev_attr->attr.name, "class_id")) {
- ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
- "%02x%02x%02x%02x%02x%02x%02x%02x}\n",
- device_info->chn_type.b[3],
- device_info->chn_type.b[2],
- device_info->chn_type.b[1],
- device_info->chn_type.b[0],
- device_info->chn_type.b[5],
- device_info->chn_type.b[4],
- device_info->chn_type.b[7],
- device_info->chn_type.b[6],
- device_info->chn_type.b[8],
- device_info->chn_type.b[9],
- device_info->chn_type.b[10],
- device_info->chn_type.b[11],
- device_info->chn_type.b[12],
- device_info->chn_type.b[13],
- device_info->chn_type.b[14],
- device_info->chn_type.b[15]);
+ ret = sprintf(buf, "{%pUl}\n", device_info->chn_type.b);
} else if (!strcmp(dev_attr->attr.name, "device_id")) {
- ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
- "%02x%02x%02x%02x%02x%02x%02x%02x}\n",
- device_info->chn_instance.b[3],
- device_info->chn_instance.b[2],
- device_info->chn_instance.b[1],
- device_info->chn_instance.b[0],
- device_info->chn_instance.b[5],
- device_info->chn_instance.b[4],
- device_info->chn_instance.b[7],
- device_info->chn_instance.b[6],
- device_info->chn_instance.b[8],
- device_info->chn_instance.b[9],
- device_info->chn_instance.b[10],
- device_info->chn_instance.b[11],
- device_info->chn_instance.b[12],
- device_info->chn_instance.b[13],
- device_info->chn_instance.b[14],
- device_info->chn_instance.b[15]);
+ ret = sprintf(buf, "{%pUl}\n", device_info->chn_instance.b);
} else if (!strcmp(dev_attr->attr.name, "modalias")) {
print_alias_name(hv_dev, alias_name);
ret = sprintf(buf, "vmbus:%s\n", alias_name);
@@ -757,6 +724,9 @@ static int __init hv_acpi_init(void)
{
int ret, t;
+ if (x86_hyper != &x86_hyper_ms_hyperv)
+ return -ENODEV;
+
init_completion(&probe_event);
/*
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index b0a2e4c37e12..c74e73b2069a 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -41,7 +41,7 @@ comment "Native drivers"
config SENSORS_ABITUGURU
tristate "Abit uGuru (rev 1 & 2)"
- depends on X86 && DMI && EXPERIMENTAL
+ depends on X86 && DMI
help
If you say yes here you get support for the sensor part of the first
and second revision of the Abit uGuru chip. The voltage and frequency
@@ -56,7 +56,7 @@ config SENSORS_ABITUGURU
config SENSORS_ABITUGURU3
tristate "Abit uGuru (rev 3)"
- depends on X86 && DMI && EXPERIMENTAL
+ depends on X86 && DMI
help
If you say yes here you get support for the sensor part of the
third revision of the Abit uGuru chip. Only reading the sensors
@@ -70,7 +70,7 @@ config SENSORS_ABITUGURU3
config SENSORS_AD7314
tristate "Analog Devices AD7314 and compatibles"
- depends on SPI && EXPERIMENTAL
+ depends on SPI
help
If you say yes here you get support for the Analog Devices
AD7314, ADT7301 and ADT7302 temperature sensors.
@@ -80,7 +80,7 @@ config SENSORS_AD7314
config SENSORS_AD7414
tristate "Analog Devices AD7414"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Analog Devices
AD7414 temperature monitoring chip.
@@ -90,7 +90,7 @@ config SENSORS_AD7414
config SENSORS_AD7418
tristate "Analog Devices AD7416, AD7417 and AD7418"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Analog Devices
AD7416, AD7417 and AD7418 temperature monitoring chips.
@@ -100,7 +100,7 @@ config SENSORS_AD7418
config SENSORS_ADCXX
tristate "National Semiconductor ADCxxxSxxx"
- depends on SPI_MASTER && EXPERIMENTAL
+ depends on SPI_MASTER
help
If you say yes here you get support for the National Semiconductor
ADC<bb><c>S<sss> chip family, where
@@ -179,9 +179,19 @@ config SENSORS_ADM9240
This driver can also be built as a module. If so, the module
will be called adm9240.
+config SENSORS_ADT7410
+ tristate "Analog Devices ADT7410"
+ depends on I2C
+ help
+ If you say yes here you get support for the Analog Devices
+ ADT7410 temperature monitoring chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called adt7410.
+
config SENSORS_ADT7411
tristate "Analog Devices ADT7411"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Analog Devices
ADT7411 voltage and temperature monitoring chip.
@@ -191,7 +201,7 @@ config SENSORS_ADT7411
config SENSORS_ADT7462
tristate "Analog Devices ADT7462"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Analog Devices
ADT7462 temperature monitoring chips.
@@ -201,7 +211,7 @@ config SENSORS_ADT7462
config SENSORS_ADT7470
tristate "Analog Devices ADT7470"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Analog Devices
ADT7470 temperature monitoring chips.
@@ -236,7 +246,7 @@ config SENSORS_ASC7621
config SENSORS_K8TEMP
tristate "AMD Athlon64/FX or Opteron temperature sensor"
- depends on X86 && PCI && EXPERIMENTAL
+ depends on X86 && PCI
help
If you say yes here you get support for the temperature
sensor(s) inside your CPU. Supported is whole AMD K8
@@ -271,7 +281,7 @@ config SENSORS_FAM15H_POWER
config SENSORS_ASB100
tristate "Asus ASB100 Bach"
- depends on X86 && I2C && EXPERIMENTAL
+ depends on X86 && I2C
select HWMON_VID
help
If you say yes here you get support for the ASB100 Bach sensor
@@ -282,7 +292,7 @@ config SENSORS_ASB100
config SENSORS_ATXP1
tristate "Attansic ATXP1 VID controller"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
select HWMON_VID
help
If you say yes here you get support for the Attansic ATXP1 VID
@@ -336,7 +346,7 @@ config SENSORS_EXYNOS4_TMU
config SENSORS_I5K_AMB
tristate "FB-DIMM AMB temperature sensor on Intel 5000 series chipsets"
- depends on PCI && EXPERIMENTAL
+ depends on PCI
help
If you say yes here you get support for FB-DIMM AMB temperature
monitoring chips on systems with the Intel 5000 series chipset.
@@ -445,7 +455,7 @@ config SENSORS_GPIO_FAN
config SENSORS_HIH6130
tristate "Honeywell Humidicon HIH-6130 humidity/temperature sensor"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for Honeywell Humidicon
HIH-6130 and HIH-6131 Humidicon humidity sensors.
@@ -455,7 +465,7 @@ config SENSORS_HIH6130
config SENSORS_CORETEMP
tristate "Intel Core/Core2/Atom temperature sensor"
- depends on X86 && PCI && EXPERIMENTAL
+ depends on X86 && PCI
help
If you say yes here you get support for the temperature
sensor inside your CPU. Most of the family 6 CPUs
@@ -495,8 +505,8 @@ config SENSORS_IT87
select HWMON_VID
help
If you say yes here you get support for ITE IT8705F, IT8712F,
- IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F and IT8758E
- sensor chips, and the SiS960 clone.
+ IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E,
+ IT8782F, and IT8783E/F sensor chips, and the SiS950 clone.
This driver can also be built as a module. If so, the module
will be called it87.
@@ -527,7 +537,7 @@ config SENSORS_JC42
config SENSORS_LINEAGE
tristate "Lineage Compact Power Line Power Entry Module"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Lineage Compact Power Line
series of DC/DC and AC/DC converters such as CP1800, CP2000AC,
@@ -550,12 +560,12 @@ config SENSORS_LM63
will be called lm63.
config SENSORS_LM70
- tristate "National Semiconductor LM70 / Texas Instruments TMP121"
+ tristate "National Semiconductor LM70 and compatibles"
depends on SPI_MASTER
help
If you say yes here you get support for the National Semiconductor
- LM70 and Texas Instruments TMP121/TMP123 digital temperature
- sensor chips.
+ LM70, LM71, LM74 and Texas Instruments TMP121/TMP123 digital tempera-
+ ture sensor chips.
This driver can also be built as a module. If so, the module
will be called lm70.
@@ -709,7 +719,7 @@ config SENSORS_LTC4151
config SENSORS_LTC4215
tristate "Linear Technology LTC4215"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
default n
help
If you say yes here you get support for Linear Technology LTC4215
@@ -720,7 +730,7 @@ config SENSORS_LTC4215
config SENSORS_LTC4245
tristate "Linear Technology LTC4245"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
default n
help
If you say yes here you get support for Linear Technology LTC4245
@@ -731,7 +741,7 @@ config SENSORS_LTC4245
config SENSORS_LTC4261
tristate "Linear Technology LTC4261"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
default n
help
If you say yes here you get support for Linear Technology LTC4261
@@ -752,7 +762,7 @@ config SENSORS_LM95241
config SENSORS_LM95245
tristate "National Semiconductor LM95245 sensor chip"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for LM95245 sensor chip.
@@ -760,10 +770,11 @@ config SENSORS_LM95245
will be called lm95245.
config SENSORS_MAX1111
- tristate "Maxim MAX1111 Multichannel, Serial 8-bit ADC chip"
+ tristate "Maxim MAX1111 Serial 8-bit ADC chip and compatibles"
depends on SPI_MASTER
help
- Say y here to support Maxim's MAX1111 ADC chips.
+ Say y here to support Maxim's MAX1110, MAX1111, MAX1112, and MAX1113
+ ADC chips.
This driver can also be built as a module. If so, the module
will be called max1111.
@@ -795,7 +806,7 @@ config SENSORS_MAX1619
config SENSORS_MAX1668
tristate "Maxim MAX1668 and compatibles"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for MAX1668, MAX1989 and
MAX1805 chips.
@@ -803,9 +814,18 @@ config SENSORS_MAX1668
This driver can also be built as a module. If so, the module
will be called max1668.
+config SENSORS_MAX197
+ tristate "Maxim MAX197 and compatibles"
+ help
+ Support for the Maxim MAX197 A/D converter.
+ Support will include, but not be limited to, MAX197, and MAX199.
+
+ This driver can also be built as a module. If so, the module
+ will be called max197.
+
config SENSORS_MAX6639
tristate "Maxim MAX6639 sensor chip"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the MAX6639
sensor chips.
@@ -815,7 +835,7 @@ config SENSORS_MAX6639
config SENSORS_MAX6642
tristate "Maxim MAX6642 sensor chip"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for MAX6642 sensor chip.
MAX6642 is a SMBus-Compatible Remote/Local Temperature Sensor
@@ -826,7 +846,7 @@ config SENSORS_MAX6642
config SENSORS_MAX6650
tristate "Maxim MAX6650 sensor chip"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the MAX6650 / MAX6651
sensor chips.
@@ -835,18 +855,18 @@ config SENSORS_MAX6650
will be called max6650.
config SENSORS_MCP3021
- tristate "Microchip MCP3021"
- depends on I2C && EXPERIMENTAL
+ tristate "Microchip MCP3021 and compatibles"
+ depends on I2C
help
- If you say yes here you get support for the MCP3021 chip
- that is a A/D converter (ADC) with 10-bit resolution.
+ If you say yes here you get support for MCP3021 and MCP3221.
+ The MCP3021 is a A/D converter (ADC) with 10-bit and the MCP3221
+ with 12-bit resolution.
This driver can also be built as a module. If so, the module
will be called mcp3021.
config SENSORS_NTC_THERMISTOR
tristate "NTC thermistor support"
- depends on EXPERIMENTAL
help
This driver supports NTC thermistors sensor reading and its
interpretation. The driver can also monitor the temperature and
@@ -951,7 +971,7 @@ config SENSORS_SIS5595
config SENSORS_SMM665
tristate "Summit Microelectronics SMM665"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
default n
help
If you say yes here you get support for the hardware monitoring
@@ -966,7 +986,7 @@ config SENSORS_SMM665
config SENSORS_DME1737
tristate "SMSC DME1737, SCH311x and compatibles"
- depends on I2C && EXPERIMENTAL && !PPC
+ depends on I2C && !PPC
select HWMON_VID
help
If you say yes here you get support for the hardware monitoring
@@ -1042,7 +1062,7 @@ config SENSORS_SMSC47M192
config SENSORS_SMSC47B397
tristate "SMSC LPC47B397-NC"
- depends on EXPERIMENTAL && !PPC
+ depends on !PPC
help
If you say yes here you get support for the SMSC LPC47B397-NC
sensor chip.
@@ -1116,7 +1136,7 @@ config SENSORS_ADS7871
config SENSORS_AMC6821
tristate "Texas Instruments AMC6821"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Texas Instruments
AMC6821 hardware monitoring chips.
@@ -1125,11 +1145,11 @@ config SENSORS_AMC6821
will be called amc6821.
config SENSORS_INA2XX
- tristate "Texas Instruments INA219, INA226"
- depends on I2C && EXPERIMENTAL
+ tristate "Texas Instruments INA219 and compatibles"
+ depends on I2C
help
- If you say yes here you get support for INA219 and INA226 power
- monitor chips.
+ If you say yes here you get support for INA219, INA220, INA226, and
+ INA230 power monitor chips.
The INA2xx driver is configured for the default configuration of
the part as described in the datasheet.
@@ -1149,7 +1169,7 @@ config SENSORS_THMC50
config SENSORS_TMP102
tristate "Texas Instruments TMP102"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for Texas Instruments TMP102
sensor chips.
@@ -1159,7 +1179,7 @@ config SENSORS_TMP102
config SENSORS_TMP401
tristate "Texas Instruments TMP401 and compatibles"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for Texas Instruments TMP401 and
TMP411 temperature sensor chips.
@@ -1169,7 +1189,7 @@ config SENSORS_TMP401
config SENSORS_TMP421
tristate "Texas Instruments TMP421 and compatible"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for Texas Instruments TMP421,
TMP422 and TMP423 temperature sensor chips.
@@ -1261,7 +1281,7 @@ config SENSORS_W83792D
config SENSORS_W83793
tristate "Winbond W83793"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
select HWMON_VID
help
If you say yes here you get support for the Winbond W83793
@@ -1273,7 +1293,7 @@ config SENSORS_W83793
config SENSORS_W83795
tristate "Winbond/Nuvoton W83795G/ADG"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Winbond W83795G and
W83795ADG hardware monitoring chip, including manual fan speed
@@ -1284,7 +1304,7 @@ config SENSORS_W83795
config SENSORS_W83795_FANCTRL
boolean "Include automatic fan control support (DANGEROUS)"
- depends on SENSORS_W83795 && EXPERIMENTAL
+ depends on SENSORS_W83795
default n
help
If you say yes here, support for automatic fan speed control
@@ -1301,7 +1321,7 @@ config SENSORS_W83795_FANCTRL
config SENSORS_W83L785TS
tristate "Winbond W83L785TS-S"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Winbond W83L785TS-S
sensor chip, which is used on the Asus A7N8X, among other
@@ -1312,7 +1332,7 @@ config SENSORS_W83L785TS
config SENSORS_W83L786NG
tristate "Winbond W83L786NG, W83L786NR"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Winbond W83L786NG
and W83L786NR sensor chips.
@@ -1427,7 +1447,7 @@ config SENSORS_ACPI_POWER
config SENSORS_ATK0110
tristate "ASUS ATK0110"
- depends on X86 && EXPERIMENTAL
+ depends on X86
help
If you say yes here you get support for the ACPI hardware
monitoring interface found in many ASUS motherboards. This
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 7aa98119c4ab..a62ce17ddbfc 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o
obj-$(CONFIG_SENSORS_ADS1015) += ads1015.o
obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o
obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o
+obj-$(CONFIG_SENSORS_ADT7410) += adt7410.o
obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o
obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o
obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o
@@ -94,6 +95,7 @@ obj-$(CONFIG_SENSORS_MAX1111) += max1111.o
obj-$(CONFIG_SENSORS_MAX16065) += max16065.o
obj-$(CONFIG_SENSORS_MAX1619) += max1619.o
obj-$(CONFIG_SENSORS_MAX1668) += max1668.o
+obj-$(CONFIG_SENSORS_MAX197) += max197.o
obj-$(CONFIG_SENSORS_MAX6639) += max6639.o
obj-$(CONFIG_SENSORS_MAX6642) += max6642.o
obj-$(CONFIG_SENSORS_MAX6650) += max6650.o
diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c
index d4419b47f3d4..78b81793ddd9 100644
--- a/drivers/hwmon/abituguru.c
+++ b/drivers/hwmon/abituguru.c
@@ -1278,7 +1278,8 @@ static int __devinit abituguru_probe(struct platform_device *pdev)
0x00, 0x01, 0x03, 0x04, 0x0A, 0x08, 0x0E, 0x02,
0x09, 0x06, 0x05, 0x0B, 0x0F, 0x0D, 0x07, 0x0C };
- data = kzalloc(sizeof(struct abituguru_data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(struct abituguru_data),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -1430,8 +1431,6 @@ abituguru_probe_error:
for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
device_remove_file(&pdev->dev,
&abituguru_sysfs_attr[i].dev_attr);
- platform_set_drvdata(pdev, NULL);
- kfree(data);
return res;
}
@@ -1446,8 +1445,6 @@ static int __devexit abituguru_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(abituguru_sysfs_attr); i++)
device_remove_file(&pdev->dev,
&abituguru_sysfs_attr[i].dev_attr);
- platform_set_drvdata(pdev, NULL);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/abituguru3.c b/drivers/hwmon/abituguru3.c
index 5d582aebff87..b174b8b2b4df 100644
--- a/drivers/hwmon/abituguru3.c
+++ b/drivers/hwmon/abituguru3.c
@@ -976,7 +976,8 @@ static int __devinit abituguru3_probe(struct platform_device *pdev)
u8 buf[2];
u16 id;
- data = kzalloc(sizeof(struct abituguru3_data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(struct abituguru3_data),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -1068,7 +1069,6 @@ abituguru3_probe_error:
for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++)
device_remove_file(&pdev->dev,
&abituguru3_sysfs_attr[i].dev_attr);
- kfree(data);
return res;
}
@@ -1084,8 +1084,6 @@ static int __devexit abituguru3_remove(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(abituguru3_sysfs_attr); i++)
device_remove_file(&pdev->dev,
&abituguru3_sysfs_attr[i].dev_attr);
- kfree(data);
-
return 0;
}
diff --git a/drivers/hwmon/ad7314.c b/drivers/hwmon/ad7314.c
index cfec802cf9ca..37c01e72d699 100644
--- a/drivers/hwmon/ad7314.c
+++ b/drivers/hwmon/ad7314.c
@@ -87,10 +87,18 @@ static ssize_t ad7314_show_temperature(struct device *dev,
}
}
+static ssize_t ad7314_show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
+}
+
+static DEVICE_ATTR(name, S_IRUGO, ad7314_show_name, NULL);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO,
ad7314_show_temperature, NULL, 0);
static struct attribute *ad7314_attributes[] = {
+ &dev_attr_name.attr,
&sensor_dev_attr_temp1_input.dev_attr.attr,
NULL,
};
@@ -104,16 +112,16 @@ static int __devinit ad7314_probe(struct spi_device *spi_dev)
int ret;
struct ad7314_data *chip;
- chip = kzalloc(sizeof(*chip), GFP_KERNEL);
- if (chip == NULL) {
- ret = -ENOMEM;
- goto error_ret;
- }
+ chip = devm_kzalloc(&spi_dev->dev, sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
dev_set_drvdata(&spi_dev->dev, chip);
ret = sysfs_create_group(&spi_dev->dev.kobj, &ad7314_group);
if (ret < 0)
- goto error_free_chip;
+ return ret;
+
chip->hwmon_dev = hwmon_device_register(&spi_dev->dev);
if (IS_ERR(chip->hwmon_dev)) {
ret = PTR_ERR(chip->hwmon_dev);
@@ -124,9 +132,6 @@ static int __devinit ad7314_probe(struct spi_device *spi_dev)
return 0;
error_remove_group:
sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group);
-error_free_chip:
- kfree(chip);
-error_ret:
return ret;
}
@@ -136,7 +141,6 @@ static int __devexit ad7314_remove(struct spi_device *spi_dev)
hwmon_device_unregister(chip->hwmon_dev);
sysfs_remove_group(&spi_dev->dev.kobj, &ad7314_group);
- kfree(chip);
return 0;
}
diff --git a/drivers/hwmon/ad7414.c b/drivers/hwmon/ad7414.c
index 06d2d60d1fd0..b420fb3f3a71 100644
--- a/drivers/hwmon/ad7414.c
+++ b/drivers/hwmon/ad7414.c
@@ -185,16 +185,13 @@ static int ad7414_probe(struct i2c_client *client,
int err;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_READ_WORD_DATA)) {
- err = -EOPNOTSUPP;
- goto exit;
- }
+ I2C_FUNC_SMBUS_READ_WORD_DATA))
+ return -EOPNOTSUPP;
- data = kzalloc(sizeof(struct ad7414_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(&client->dev, sizeof(struct ad7414_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->lock);
@@ -214,7 +211,7 @@ static int ad7414_probe(struct i2c_client *client,
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &ad7414_group);
if (err)
- goto exit_free;
+ return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -226,9 +223,6 @@ static int ad7414_probe(struct i2c_client *client,
exit_remove:
sysfs_remove_group(&client->dev.kobj, &ad7414_group);
-exit_free:
- kfree(data);
-exit:
return err;
}
@@ -238,7 +232,6 @@ static int __devexit ad7414_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &ad7414_group);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/ad7418.c b/drivers/hwmon/ad7418.c
index a50a6bef16c4..57d4a6295675 100644
--- a/drivers/hwmon/ad7418.c
+++ b/drivers/hwmon/ad7418.c
@@ -227,16 +227,13 @@ static int ad7418_probe(struct i2c_client *client,
int err;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA |
- I2C_FUNC_SMBUS_WORD_DATA)) {
- err = -EOPNOTSUPP;
- goto exit;
- }
+ I2C_FUNC_SMBUS_WORD_DATA))
+ return -EOPNOTSUPP;
- data = kzalloc(sizeof(struct ad7418_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(&client->dev, sizeof(struct ad7418_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
@@ -268,7 +265,7 @@ static int ad7418_probe(struct i2c_client *client,
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &data->attrs);
if (err)
- goto exit_free;
+ return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -280,9 +277,6 @@ static int ad7418_probe(struct i2c_client *client,
exit_remove:
sysfs_remove_group(&client->dev.kobj, &data->attrs);
-exit_free:
- kfree(data);
-exit:
return err;
}
@@ -291,7 +285,6 @@ static int ad7418_remove(struct i2c_client *client)
struct ad7418_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &data->attrs);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/adcxx.c b/drivers/hwmon/adcxx.c
index a3d3183454ad..f4c5867170d6 100644
--- a/drivers/hwmon/adcxx.c
+++ b/drivers/hwmon/adcxx.c
@@ -141,10 +141,7 @@ static ssize_t adcxx_set_max(struct device *dev,
static ssize_t adcxx_show_name(struct device *dev, struct device_attribute
*devattr, char *buf)
{
- struct spi_device *spi = to_spi_device(dev);
- struct adcxx *adc = spi_get_drvdata(spi);
-
- return sprintf(buf, "adcxx%ds\n", adc->channels);
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
}
static struct sensor_device_attribute ad_input[] = {
@@ -171,7 +168,7 @@ static int __devinit adcxx_probe(struct spi_device *spi)
int status;
int i;
- adc = kzalloc(sizeof *adc, GFP_KERNEL);
+ adc = devm_kzalloc(&spi->dev, sizeof(*adc), GFP_KERNEL);
if (!adc)
return -ENOMEM;
@@ -208,7 +205,6 @@ out_err:
spi_set_drvdata(spi, NULL);
mutex_unlock(&adc->lock);
- kfree(adc);
return status;
}
@@ -224,7 +220,6 @@ static int __devexit adcxx_remove(struct spi_device *spi)
spi_set_drvdata(spi, NULL);
mutex_unlock(&adc->lock);
- kfree(adc);
return 0;
}
diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c
index 80cc465d8ac7..97f4718382f6 100644
--- a/drivers/hwmon/adm1029.c
+++ b/drivers/hwmon/adm1029.c
@@ -342,11 +342,10 @@ static int adm1029_probe(struct i2c_client *client,
struct adm1029_data *data;
int err;
- data = kzalloc(sizeof(struct adm1029_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(&client->dev, sizeof(struct adm1029_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -355,15 +354,13 @@ static int adm1029_probe(struct i2c_client *client,
* Initialize the ADM1029 chip
* Check config register
*/
- if (adm1029_init_client(client) == 0) {
- err = -ENODEV;
- goto exit_free;
- }
+ if (adm1029_init_client(client) == 0)
+ return -ENODEV;
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &adm1029_group);
if (err)
- goto exit_free;
+ return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -375,9 +372,6 @@ static int adm1029_probe(struct i2c_client *client,
exit_remove_files:
sysfs_remove_group(&client->dev.kobj, &adm1029_group);
- exit_free:
- kfree(data);
- exit:
return err;
}
@@ -405,7 +399,6 @@ static int adm1029_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &adm1029_group);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c
index 5a78d102a0fa..8b24d1a4a2b4 100644
--- a/drivers/hwmon/adm9240.c
+++ b/drivers/hwmon/adm9240.c
@@ -500,31 +500,6 @@ static ssize_t set_aout(struct device *dev,
}
static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout);
-/* chassis_clear */
-static ssize_t chassis_clear_legacy(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct i2c_client *client = to_i2c_client(dev);
- long val;
- int err;
-
- err = kstrtol(buf, 10, &val);
- if (err)
- return err;
-
- dev_warn(dev, "Attribute chassis_clear is deprecated, "
- "use intrusion0_alarm instead\n");
-
- if (val == 1) {
- i2c_smbus_write_byte_data(client,
- ADM9240_REG_CHASSIS_CLEAR, 0x80);
- dev_dbg(&client->dev, "chassis intrusion latch cleared\n");
- }
- return count;
-}
-static DEVICE_ATTR(chassis_clear, S_IWUSR, NULL, chassis_clear_legacy);
-
static ssize_t chassis_clear(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
@@ -586,7 +561,6 @@ static struct attribute *adm9240_attributes[] = {
&sensor_dev_attr_fan2_alarm.dev_attr.attr,
&dev_attr_alarms.attr,
&dev_attr_aout_output.attr,
- &dev_attr_chassis_clear.attr,
&sensor_dev_attr_intrusion0_alarm.dev_attr.attr,
&dev_attr_cpu0_vid.attr,
NULL
diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c
index 1958f03efd7a..2798246ad814 100644
--- a/drivers/hwmon/ads1015.c
+++ b/drivers/hwmon/ads1015.c
@@ -156,7 +156,6 @@ static int ads1015_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
for (k = 0; k < ADS1015_CHANNELS; ++k)
device_remove_file(&client->dev, &ads1015_in[k].dev_attr);
- kfree(data);
return 0;
}
@@ -254,11 +253,10 @@ static int ads1015_probe(struct i2c_client *client,
int err;
unsigned int k;
- data = kzalloc(sizeof(struct ads1015_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(&client->dev, sizeof(struct ads1015_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -284,8 +282,6 @@ static int ads1015_probe(struct i2c_client *client,
exit_remove:
for (k = 0; k < ADS1015_CHANNELS; ++k)
device_remove_file(&client->dev, &ads1015_in[k].dev_attr);
- kfree(data);
-exit:
return err;
}
diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c
index bf3fdf495595..1f9e8af0f322 100644
--- a/drivers/hwmon/ads7828.c
+++ b/drivers/hwmon/ads7828.c
@@ -154,7 +154,6 @@ static int ads7828_remove(struct i2c_client *client)
struct ads7828_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &ads7828_group);
- kfree(i2c_get_clientdata(client));
return 0;
}
@@ -217,11 +216,10 @@ static int ads7828_probe(struct i2c_client *client,
struct ads7828_data *data;
int err;
- data = kzalloc(sizeof(struct ads7828_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(&client->dev, sizeof(struct ads7828_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -229,7 +227,7 @@ static int ads7828_probe(struct i2c_client *client,
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &ads7828_group);
if (err)
- goto exit_free;
+ return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -241,9 +239,6 @@ static int ads7828_probe(struct i2c_client *client,
exit_remove:
sysfs_remove_group(&client->dev.kobj, &ads7828_group);
-exit_free:
- kfree(data);
-exit:
return err;
}
diff --git a/drivers/hwmon/ads7871.c b/drivers/hwmon/ads7871.c
index e65c6e45d36b..1b53aa42b6db 100644
--- a/drivers/hwmon/ads7871.c
+++ b/drivers/hwmon/ads7871.c
@@ -139,6 +139,12 @@ static ssize_t show_voltage(struct device *dev,
}
}
+static ssize_t ads7871_show_name(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
+}
+
static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_voltage, NULL, 0);
static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 1);
static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 2);
@@ -148,6 +154,8 @@ static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 5);
static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 6);
static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 7);
+static DEVICE_ATTR(name, S_IRUGO, ads7871_show_name, NULL);
+
static struct attribute *ads7871_attributes[] = {
&sensor_dev_attr_in0_input.dev_attr.attr,
&sensor_dev_attr_in1_input.dev_attr.attr,
@@ -157,6 +165,7 @@ static struct attribute *ads7871_attributes[] = {
&sensor_dev_attr_in5_input.dev_attr.attr,
&sensor_dev_attr_in6_input.dev_attr.attr,
&sensor_dev_attr_in7_input.dev_attr.attr,
+ &dev_attr_name.attr,
NULL
};
@@ -189,20 +198,17 @@ static int __devinit ads7871_probe(struct spi_device *spi)
* because there is no other error checking on an SPI bus
* we need to make sure we really have a chip
*/
- if (val != ret) {
- err = -ENODEV;
- goto exit;
- }
+ if (val != ret)
+ return -ENODEV;
- pdata = kzalloc(sizeof(struct ads7871_data), GFP_KERNEL);
- if (!pdata) {
- err = -ENOMEM;
- goto exit;
- }
+ pdata = devm_kzalloc(&spi->dev, sizeof(struct ads7871_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
err = sysfs_create_group(&spi->dev.kobj, &ads7871_group);
if (err < 0)
- goto error_free;
+ return err;
spi_set_drvdata(spi, pdata);
@@ -216,9 +222,6 @@ static int __devinit ads7871_probe(struct spi_device *spi)
error_remove:
sysfs_remove_group(&spi->dev.kobj, &ads7871_group);
-error_free:
- kfree(pdata);
-exit:
return err;
}
@@ -228,7 +231,6 @@ static int __devexit ads7871_remove(struct spi_device *spi)
hwmon_device_unregister(pdata->hwmon_dev);
sysfs_remove_group(&spi->dev.kobj, &ads7871_group);
- kfree(pdata);
return 0;
}
diff --git a/drivers/hwmon/adt7410.c b/drivers/hwmon/adt7410.c
new file mode 100644
index 000000000000..030c8d7c33a5
--- /dev/null
+++ b/drivers/hwmon/adt7410.c
@@ -0,0 +1,464 @@
+/*
+ * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware
+ * monitoring
+ * This driver handles the ADT7410 and compatible digital temperature sensors.
+ * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22
+ * based on lm75.c by Frodo Looijaard <frodol@dds.nl>
+ * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/i2c.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+/*
+ * ADT7410 registers definition
+ */
+
+#define ADT7410_TEMPERATURE 0
+#define ADT7410_STATUS 2
+#define ADT7410_CONFIG 3
+#define ADT7410_T_ALARM_HIGH 4
+#define ADT7410_T_ALARM_LOW 6
+#define ADT7410_T_CRIT 8
+#define ADT7410_T_HYST 0xA
+
+/*
+ * ADT7410 status
+ */
+#define ADT7410_STAT_T_LOW (1 << 4)
+#define ADT7410_STAT_T_HIGH (1 << 5)
+#define ADT7410_STAT_T_CRIT (1 << 6)
+#define ADT7410_STAT_NOT_RDY (1 << 7)
+
+/*
+ * ADT7410 config
+ */
+#define ADT7410_FAULT_QUEUE_MASK (1 << 0 | 1 << 1)
+#define ADT7410_CT_POLARITY (1 << 2)
+#define ADT7410_INT_POLARITY (1 << 3)
+#define ADT7410_EVENT_MODE (1 << 4)
+#define ADT7410_MODE_MASK (1 << 5 | 1 << 6)
+#define ADT7410_FULL (0 << 5 | 0 << 6)
+#define ADT7410_PD (1 << 5 | 1 << 6)
+#define ADT7410_RESOLUTION (1 << 7)
+
+/*
+ * ADT7410 masks
+ */
+#define ADT7410_T13_VALUE_MASK 0xFFF8
+#define ADT7410_T_HYST_MASK 0xF
+
+/* straight from the datasheet */
+#define ADT7410_TEMP_MIN (-55000)
+#define ADT7410_TEMP_MAX 150000
+
+enum adt7410_type { /* keep sorted in alphabetical order */
+ adt7410,
+};
+
+/* Addresses scanned */
+static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
+ I2C_CLIENT_END };
+
+static const u8 ADT7410_REG_TEMP[4] = {
+ ADT7410_TEMPERATURE, /* input */
+ ADT7410_T_ALARM_HIGH, /* high */
+ ADT7410_T_ALARM_LOW, /* low */
+ ADT7410_T_CRIT, /* critical */
+};
+
+/* Each client has this additional data */
+struct adt7410_data {
+ struct device *hwmon_dev;
+ struct mutex update_lock;
+ u8 config;
+ u8 oldconfig;
+ bool valid; /* true if registers valid */
+ unsigned long last_updated; /* In jiffies */
+ s16 temp[4]; /* Register values,
+ 0 = input
+ 1 = high
+ 2 = low
+ 3 = critical */
+ u8 hyst; /* hysteresis offset */
+};
+
+/*
+ * adt7410 register access by I2C
+ */
+static int adt7410_temp_ready(struct i2c_client *client)
+{
+ int i, status;
+
+ for (i = 0; i < 6; i++) {
+ status = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
+ if (status < 0)
+ return status;
+ if (!(status & ADT7410_STAT_NOT_RDY))
+ return 0;
+ msleep(60);
+ }
+ return -ETIMEDOUT;
+}
+
+static struct adt7410_data *adt7410_update_device(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adt7410_data *data = i2c_get_clientdata(client);
+ struct adt7410_data *ret = data;
+ mutex_lock(&data->update_lock);
+
+ if (time_after(jiffies, data->last_updated + HZ + HZ / 2)
+ || !data->valid) {
+ int i, status;
+
+ dev_dbg(&client->dev, "Starting update\n");
+
+ status = adt7410_temp_ready(client); /* check for new value */
+ if (unlikely(status)) {
+ ret = ERR_PTR(status);
+ goto abort;
+ }
+ for (i = 0; i < ARRAY_SIZE(data->temp); i++) {
+ status = i2c_smbus_read_word_swapped(client,
+ ADT7410_REG_TEMP[i]);
+ if (unlikely(status < 0)) {
+ dev_dbg(dev,
+ "Failed to read value: reg %d, error %d\n",
+ ADT7410_REG_TEMP[i], status);
+ ret = ERR_PTR(status);
+ goto abort;
+ }
+ data->temp[i] = status;
+ }
+ status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST);
+ if (unlikely(status < 0)) {
+ dev_dbg(dev,
+ "Failed to read value: reg %d, error %d\n",
+ ADT7410_T_HYST, status);
+ ret = ERR_PTR(status);
+ goto abort;
+ }
+ data->hyst = status;
+ data->last_updated = jiffies;
+ data->valid = true;
+ }
+
+abort:
+ mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static s16 ADT7410_TEMP_TO_REG(long temp)
+{
+ return DIV_ROUND_CLOSEST(SENSORS_LIMIT(temp, ADT7410_TEMP_MIN,
+ ADT7410_TEMP_MAX) * 128, 1000);
+}
+
+static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg)
+{
+ /* in 13 bit mode, bits 0-2 are status flags - mask them out */
+ if (!(data->config & ADT7410_RESOLUTION))
+ reg &= ADT7410_T13_VALUE_MASK;
+ /*
+ * temperature is stored in twos complement format, in steps of
+ * 1/128°C
+ */
+ return DIV_ROUND_CLOSEST(reg * 1000, 128);
+}
+
+/*-----------------------------------------------------------------------*/
+
+/* sysfs attributes for hwmon */
+
+static ssize_t adt7410_show_temp(struct device *dev,
+ struct device_attribute *da, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct adt7410_data *data = adt7410_update_device(dev);
+
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data,
+ data->temp[attr->index]));
+}
+
+static ssize_t adt7410_set_temp(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adt7410_data *data = i2c_get_clientdata(client);
+ int nr = attr->index;
+ long temp;
+ int ret;
+
+ ret = kstrtol(buf, 10, &temp);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->update_lock);
+ data->temp[nr] = ADT7410_TEMP_TO_REG(temp);
+ ret = i2c_smbus_write_word_swapped(client, ADT7410_REG_TEMP[nr],
+ data->temp[nr]);
+ if (ret)
+ count = ret;
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t adt7410_show_t_hyst(struct device *dev,
+ struct device_attribute *da,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ struct adt7410_data *data;
+ int nr = attr->index;
+ int hyst;
+
+ data = adt7410_update_device(dev);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+ hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000;
+
+ /*
+ * hysteresis is stored as a 4 bit offset in the device, convert it
+ * to an absolute value
+ */
+ if (nr == 2) /* min has positive offset, others have negative */
+ hyst = -hyst;
+ return sprintf(buf, "%d\n",
+ ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst);
+}
+
+static ssize_t adt7410_set_t_hyst(struct device *dev,
+ struct device_attribute *da,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adt7410_data *data = i2c_get_clientdata(client);
+ int limit, ret;
+ long hyst;
+
+ ret = kstrtol(buf, 10, &hyst);
+ if (ret)
+ return ret;
+ /* convert absolute hysteresis value to a 4 bit delta value */
+ limit = ADT7410_REG_TO_TEMP(data, data->temp[1]);
+ hyst = SENSORS_LIMIT(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX);
+ data->hyst = SENSORS_LIMIT(DIV_ROUND_CLOSEST(limit - hyst, 1000),
+ 0, ADT7410_T_HYST_MASK);
+ ret = i2c_smbus_write_byte_data(client, ADT7410_T_HYST, data->hyst);
+ if (ret)
+ return ret;
+
+ return count;
+}
+
+static ssize_t adt7410_show_alarm(struct device *dev,
+ struct device_attribute *da,
+ char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS);
+ if (ret < 0)
+ return ret;
+
+ return sprintf(buf, "%d\n", !!(ret & attr->index));
+}
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO,
+ adt7410_show_temp, adt7410_set_temp, 1);
+static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO,
+ adt7410_show_temp, adt7410_set_temp, 2);
+static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO,
+ adt7410_show_temp, adt7410_set_temp, 3);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO,
+ adt7410_show_t_hyst, adt7410_set_t_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO,
+ adt7410_show_t_hyst, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO,
+ adt7410_show_t_hyst, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm,
+ NULL, ADT7410_STAT_T_LOW);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm,
+ NULL, ADT7410_STAT_T_HIGH);
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm,
+ NULL, ADT7410_STAT_T_CRIT);
+
+static struct attribute *adt7410_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group adt7410_group = {
+ .attrs = adt7410_attributes,
+};
+
+/*-----------------------------------------------------------------------*/
+
+/* device probe and removal */
+
+static int adt7410_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct adt7410_data *data;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA))
+ return -ENODEV;
+
+ data = devm_kzalloc(&client->dev, sizeof(struct adt7410_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, data);
+ mutex_init(&data->update_lock);
+
+ /* configure as specified */
+ ret = i2c_smbus_read_byte_data(client, ADT7410_CONFIG);
+ if (ret < 0) {
+ dev_dbg(&client->dev, "Can't read config? %d\n", ret);
+ return ret;
+ }
+ data->oldconfig = ret;
+ /*
+ * Set to 16 bit resolution, continous conversion and comparator mode.
+ */
+ data->config = ret | ADT7410_FULL | ADT7410_RESOLUTION |
+ ADT7410_EVENT_MODE;
+ if (data->config != data->oldconfig) {
+ ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
+ data->config);
+ if (ret)
+ return ret;
+ }
+ dev_dbg(&client->dev, "Config %02x\n", data->config);
+
+ /* Register sysfs hooks */
+ ret = sysfs_create_group(&client->dev.kobj, &adt7410_group);
+ if (ret)
+ goto exit_restore;
+
+ data->hwmon_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ ret = PTR_ERR(data->hwmon_dev);
+ goto exit_remove;
+ }
+
+ dev_info(&client->dev, "sensor '%s'\n", client->name);
+
+ return 0;
+
+exit_remove:
+ sysfs_remove_group(&client->dev.kobj, &adt7410_group);
+exit_restore:
+ i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->oldconfig);
+ return ret;
+}
+
+static int adt7410_remove(struct i2c_client *client)
+{
+ struct adt7410_data *data = i2c_get_clientdata(client);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&client->dev.kobj, &adt7410_group);
+ if (data->oldconfig != data->config)
+ i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
+ data->oldconfig);
+ return 0;
+}
+
+static const struct i2c_device_id adt7410_ids[] = {
+ { "adt7410", adt7410, },
+ { /* LIST END */ }
+};
+MODULE_DEVICE_TABLE(i2c, adt7410_ids);
+
+#ifdef CONFIG_PM
+static int adt7410_suspend(struct device *dev)
+{
+ int ret;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adt7410_data *data = i2c_get_clientdata(client);
+
+ ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG,
+ data->config | ADT7410_PD);
+ return ret;
+}
+
+static int adt7410_resume(struct device *dev)
+{
+ int ret;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct adt7410_data *data = i2c_get_clientdata(client);
+
+ ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->config);
+ return ret;
+}
+
+static const struct dev_pm_ops adt7410_dev_pm_ops = {
+ .suspend = adt7410_suspend,
+ .resume = adt7410_resume,
+};
+#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops)
+#else
+#define ADT7410_DEV_PM_OPS NULL
+#endif /* CONFIG_PM */
+
+static struct i2c_driver adt7410_driver = {
+ .class = I2C_CLASS_HWMON,
+ .driver = {
+ .name = "adt7410",
+ .pm = ADT7410_DEV_PM_OPS,
+ },
+ .probe = adt7410_probe,
+ .remove = adt7410_remove,
+ .id_table = adt7410_ids,
+ .address_list = normal_i2c,
+};
+
+module_i2c_driver(adt7410_driver);
+
+MODULE_AUTHOR("Hartmut Knaack");
+MODULE_DESCRIPTION("ADT7410 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/adt7411.c b/drivers/hwmon/adt7411.c
index 71bacc56e138..fe72c69a2d68 100644
--- a/drivers/hwmon/adt7411.c
+++ b/drivers/hwmon/adt7411.c
@@ -283,7 +283,7 @@ static int __devinit adt7411_probe(struct i2c_client *client,
struct adt7411_data *data;
int ret;
- data = kzalloc(sizeof(*data), GFP_KERNEL);
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -294,14 +294,14 @@ static int __devinit adt7411_probe(struct i2c_client *client,
ret = adt7411_modify_bit(client, ADT7411_REG_CFG1,
ADT7411_CFG1_START_MONITOR, 1);
if (ret < 0)
- goto exit_free;
+ return ret;
/* force update on first occasion */
data->next_update = jiffies;
ret = sysfs_create_group(&client->dev.kobj, &adt7411_attr_grp);
if (ret)
- goto exit_free;
+ return ret;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -315,8 +315,6 @@ static int __devinit adt7411_probe(struct i2c_client *client,
exit_remove:
sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp);
- exit_free:
- kfree(data);
return ret;
}
@@ -326,7 +324,6 @@ static int __devexit adt7411_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c
index 339269f76e57..baee482aedfc 100644
--- a/drivers/hwmon/adt7462.c
+++ b/drivers/hwmon/adt7462.c
@@ -1931,11 +1931,10 @@ static int adt7462_probe(struct i2c_client *client,
struct adt7462_data *data;
int err;
- data = kzalloc(sizeof(struct adt7462_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(&client->dev, sizeof(struct adt7462_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->lock);
@@ -1946,7 +1945,7 @@ static int adt7462_probe(struct i2c_client *client,
data->attrs.attrs = adt7462_attr;
err = sysfs_create_group(&client->dev.kobj, &data->attrs);
if (err)
- goto exit_free;
+ return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -1958,9 +1957,6 @@ static int adt7462_probe(struct i2c_client *client,
exit_remove:
sysfs_remove_group(&client->dev.kobj, &data->attrs);
-exit_free:
- kfree(data);
-exit:
return err;
}
@@ -1970,7 +1966,6 @@ static int adt7462_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &data->attrs);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c
index 54ec890521ff..39ecb1a3b9ef 100644
--- a/drivers/hwmon/adt7470.c
+++ b/drivers/hwmon/adt7470.c
@@ -1256,11 +1256,10 @@ static int adt7470_probe(struct i2c_client *client,
struct adt7470_data *data;
int err;
- data = kzalloc(sizeof(struct adt7470_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(&client->dev, sizeof(struct adt7470_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
data->num_temp_sensors = -1;
data->auto_update_interval = AUTO_UPDATE_INTERVAL;
@@ -1277,7 +1276,7 @@ static int adt7470_probe(struct i2c_client *client,
data->attrs.attrs = adt7470_attr;
err = sysfs_create_group(&client->dev.kobj, &data->attrs);
if (err)
- goto exit_free;
+ return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -1299,9 +1298,6 @@ exit_unregister:
hwmon_device_unregister(data->hwmon_dev);
exit_remove:
sysfs_remove_group(&client->dev.kobj, &data->attrs);
-exit_free:
- kfree(data);
-exit:
return err;
}
@@ -1313,7 +1309,6 @@ static int adt7470_remove(struct i2c_client *client)
wait_for_completion(&data->auto_update_stop);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &data->attrs);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/amc6821.c b/drivers/hwmon/amc6821.c
index f600fa1f92e3..ae482e3afdac 100644
--- a/drivers/hwmon/amc6821.c
+++ b/drivers/hwmon/amc6821.c
@@ -862,12 +862,10 @@ static int amc6821_probe(
struct amc6821_data *data;
int err;
- data = kzalloc(sizeof(struct amc6821_data), GFP_KERNEL);
- if (!data) {
- dev_err(&client->dev, "out of memory.\n");
+ data = devm_kzalloc(&client->dev, sizeof(struct amc6821_data),
+ GFP_KERNEL);
+ if (!data)
return -ENOMEM;
- }
-
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -877,11 +875,11 @@ static int amc6821_probe(
*/
err = amc6821_init_client(client);
if (err)
- goto err_free;
+ return err;
err = sysfs_create_group(&client->dev.kobj, &amc6821_attr_grp);
if (err)
- goto err_free;
+ return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (!IS_ERR(data->hwmon_dev))
@@ -890,8 +888,6 @@ static int amc6821_probe(
err = PTR_ERR(data->hwmon_dev);
dev_err(&client->dev, "error registering hwmon device.\n");
sysfs_remove_group(&client->dev.kobj, &amc6821_attr_grp);
-err_free:
- kfree(data);
return err;
}
@@ -902,8 +898,6 @@ static int amc6821_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &amc6821_attr_grp);
- kfree(data);
-
return 0;
}
diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c
index 282708860517..8f3f6f2c45fd 100644
--- a/drivers/hwmon/applesmc.c
+++ b/drivers/hwmon/applesmc.c
@@ -53,10 +53,10 @@
#define APPLESMC_MAX_DATA_LENGTH 32
-/* wait up to 32 ms for a status change. */
+/* wait up to 128 ms for a status change. */
#define APPLESMC_MIN_WAIT 0x0010
#define APPLESMC_RETRY_WAIT 0x0100
-#define APPLESMC_MAX_WAIT 0x8000
+#define APPLESMC_MAX_WAIT 0x20000
#define APPLESMC_READ_CMD 0x10
#define APPLESMC_WRITE_CMD 0x11
diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
index 4b8814deabb1..a227be47149f 100644
--- a/drivers/hwmon/asb100.c
+++ b/drivers/hwmon/asb100.c
@@ -787,12 +787,10 @@ static int asb100_probe(struct i2c_client *client,
int err;
struct asb100_data *data;
- data = kzalloc(sizeof(struct asb100_data), GFP_KERNEL);
- if (!data) {
- pr_debug("probe failed, kzalloc failed!\n");
- err = -ENOMEM;
- goto ERROR0;
- }
+ data = devm_kzalloc(&client->dev, sizeof(struct asb100_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->lock);
@@ -801,7 +799,7 @@ static int asb100_probe(struct i2c_client *client,
/* Attach secondary lm75 clients */
err = asb100_detect_subclients(client);
if (err)
- goto ERROR1;
+ return err;
/* Initialize the chip */
asb100_init_client(client);
@@ -829,9 +827,6 @@ ERROR4:
ERROR3:
i2c_unregister_device(data->lm75[1]);
i2c_unregister_device(data->lm75[0]);
-ERROR1:
- kfree(data);
-ERROR0:
return err;
}
@@ -845,8 +840,6 @@ static int asb100_remove(struct i2c_client *client)
i2c_unregister_device(data->lm75[1]);
i2c_unregister_device(data->lm75[0]);
- kfree(data);
-
return 0;
}
diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c
index 4ee578948723..cccb0e9d45b4 100644
--- a/drivers/hwmon/asus_atk0110.c
+++ b/drivers/hwmon/asus_atk0110.c
@@ -962,7 +962,6 @@ static int atk_add_sensor(struct atk_data *data, union acpi_object *obj)
return 1;
out:
- kfree(sensor->acpi_name);
kfree(sensor);
return err;
}
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index 0fa356fe82cc..984a3f13923b 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -815,17 +815,20 @@ static int __init coretemp_init(void)
if (err)
goto exit;
+ get_online_cpus();
for_each_online_cpu(i)
get_core_online(i);
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
+ put_online_cpus();
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
register_hotcpu_notifier(&coretemp_cpu_notifier);
+ put_online_cpus();
return 0;
#ifndef CONFIG_HOTPLUG_CPU
@@ -840,6 +843,7 @@ static void __exit coretemp_exit(void)
{
struct pdev_entry *p, *n;
+ get_online_cpus();
unregister_hotcpu_notifier(&coretemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
@@ -848,6 +852,7 @@ static void __exit coretemp_exit(void)
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
+ put_online_cpus();
platform_driver_unregister(&coretemp_driver);
}
diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c
index e7c6a19f3b25..fe0eeec0b750 100644
--- a/drivers/hwmon/dme1737.c
+++ b/drivers/hwmon/dme1737.c
@@ -2475,11 +2475,9 @@ static int dme1737_i2c_probe(struct i2c_client *client,
struct device *dev = &client->dev;
int err;
- data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(dev, sizeof(struct dme1737_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
data->type = id->driver_data;
@@ -2491,14 +2489,14 @@ static int dme1737_i2c_probe(struct i2c_client *client,
err = dme1737_init_device(dev);
if (err) {
dev_err(dev, "Failed to initialize device.\n");
- goto exit_kfree;
+ return err;
}
/* Create sysfs files */
err = dme1737_create_files(dev);
if (err) {
dev_err(dev, "Failed to create sysfs files.\n");
- goto exit_kfree;
+ return err;
}
/* Register device */
@@ -2513,9 +2511,6 @@ static int dme1737_i2c_probe(struct i2c_client *client,
exit_remove:
dme1737_remove_files(dev);
-exit_kfree:
- kfree(data);
-exit:
return err;
}
@@ -2526,7 +2521,6 @@ static int dme1737_i2c_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
dme1737_remove_files(&client->dev);
- kfree(data);
return 0;
}
@@ -2645,19 +2639,16 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
int err;
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!request_region(res->start, DME1737_EXTENT, "dme1737")) {
+ if (!devm_request_region(dev, res->start, DME1737_EXTENT, "dme1737")) {
dev_err(dev, "Failed to request region 0x%04x-0x%04x.\n",
(unsigned short)res->start,
(unsigned short)res->start + DME1737_EXTENT - 1);
- err = -EBUSY;
- goto exit;
+ return -EBUSY;
}
- data = kzalloc(sizeof(struct dme1737_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit_release_region;
- }
+ data = devm_kzalloc(dev, sizeof(struct dme1737_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
data->addr = res->start;
platform_set_drvdata(pdev, data);
@@ -2683,8 +2674,7 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
(device == SCH5127_DEVICE)) {
data->type = sch5127;
} else {
- err = -ENODEV;
- goto exit_kfree;
+ return -ENODEV;
}
}
@@ -2703,14 +2693,14 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
err = dme1737_init_device(dev);
if (err) {
dev_err(dev, "Failed to initialize device.\n");
- goto exit_kfree;
+ return err;
}
/* Create sysfs files */
err = dme1737_create_files(dev);
if (err) {
dev_err(dev, "Failed to create sysfs files.\n");
- goto exit_kfree;
+ return err;
}
/* Register device */
@@ -2725,12 +2715,6 @@ static int __devinit dme1737_isa_probe(struct platform_device *pdev)
exit_remove_files:
dme1737_remove_files(dev);
-exit_kfree:
- platform_set_drvdata(pdev, NULL);
- kfree(data);
-exit_release_region:
- release_region(res->start, DME1737_EXTENT);
-exit:
return err;
}
@@ -2740,9 +2724,6 @@ static int __devexit dme1737_isa_remove(struct platform_device *pdev)
hwmon_device_unregister(data->hwmon_dev);
dme1737_remove_files(&pdev->dev);
- release_region(data->addr, DME1737_EXTENT);
- platform_set_drvdata(pdev, NULL);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/ds620.c b/drivers/hwmon/ds620.c
index 50663efad412..f1d6b422cf06 100644
--- a/drivers/hwmon/ds620.c
+++ b/drivers/hwmon/ds620.c
@@ -232,11 +232,10 @@ static int ds620_probe(struct i2c_client *client,
struct ds620_data *data;
int err;
- data = kzalloc(sizeof(struct ds620_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(&client->dev, sizeof(struct ds620_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -247,7 +246,7 @@ static int ds620_probe(struct i2c_client *client,
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &ds620_group);
if (err)
- goto exit_free;
+ return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -261,9 +260,6 @@ static int ds620_probe(struct i2c_client *client,
exit_remove_files:
sysfs_remove_group(&client->dev.kobj, &ds620_group);
-exit_free:
- kfree(data);
-exit:
return err;
}
@@ -274,8 +270,6 @@ static int ds620_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &ds620_group);
- kfree(data);
-
return 0;
}
diff --git a/drivers/hwmon/emc1403.c b/drivers/hwmon/emc1403.c
index 149dcb0e148f..68ab94bde3f1 100644
--- a/drivers/hwmon/emc1403.c
+++ b/drivers/hwmon/emc1403.c
@@ -306,11 +306,10 @@ static int emc1403_probe(struct i2c_client *client,
int res;
struct thermal_data *data;
- data = kzalloc(sizeof(struct thermal_data), GFP_KERNEL);
- if (data == NULL) {
- dev_warn(&client->dev, "out of memory");
+ data = devm_kzalloc(&client->dev, sizeof(struct thermal_data),
+ GFP_KERNEL);
+ if (data == NULL)
return -ENOMEM;
- }
i2c_set_clientdata(client, data);
mutex_init(&data->mutex);
@@ -319,21 +318,19 @@ static int emc1403_probe(struct i2c_client *client,
res = sysfs_create_group(&client->dev.kobj, &m_thermal_gr);
if (res) {
dev_warn(&client->dev, "create group failed\n");
- goto thermal_error1;
+ return res;
}
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
res = PTR_ERR(data->hwmon_dev);
dev_warn(&client->dev, "register hwmon dev failed\n");
- goto thermal_error2;
+ goto thermal_error;
}
dev_info(&client->dev, "EMC1403 Thermal chip found\n");
- return res;
+ return 0;
-thermal_error2:
+thermal_error:
sysfs_remove_group(&client->dev.kobj, &m_thermal_gr);
-thermal_error1:
- kfree(data);
return res;
}
@@ -343,7 +340,6 @@ static int emc1403_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &m_thermal_gr);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/emc2103.c b/drivers/hwmon/emc2103.c
index 7bb8e888692c..77f434c58236 100644
--- a/drivers/hwmon/emc2103.c
+++ b/drivers/hwmon/emc2103.c
@@ -590,7 +590,8 @@ emc2103_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- data = kzalloc(sizeof(struct emc2103_data), GFP_KERNEL);
+ data = devm_kzalloc(&client->dev, sizeof(struct emc2103_data),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -608,7 +609,7 @@ emc2103_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (status < 0) {
dev_dbg(&client->dev, "reg 0x%02x, err %d\n", REG_CONF1,
status);
- goto exit_free;
+ return status;
}
/* detect current state of hardware */
@@ -631,7 +632,7 @@ emc2103_probe(struct i2c_client *client, const struct i2c_device_id *id)
/* Register sysfs hooks */
status = sysfs_create_group(&client->dev.kobj, &emc2103_group);
if (status)
- goto exit_free;
+ return status;
if (data->temp_count >= 3) {
status = sysfs_create_group(&client->dev.kobj,
@@ -666,8 +667,6 @@ exit_remove_temp3:
sysfs_remove_group(&client->dev.kobj, &emc2103_temp3_group);
exit_remove:
sysfs_remove_group(&client->dev.kobj, &emc2103_group);
-exit_free:
- kfree(data);
return status;
}
@@ -685,7 +684,6 @@ static int emc2103_remove(struct i2c_client *client)
sysfs_remove_group(&client->dev.kobj, &emc2103_group);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c
index 6d1226365e30..50e4ce2d22d8 100644
--- a/drivers/hwmon/f71882fg.c
+++ b/drivers/hwmon/f71882fg.c
@@ -2274,7 +2274,8 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
int err, i;
u8 start_reg, reg;
- data = kzalloc(sizeof(struct f71882fg_data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(struct f71882fg_data),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -2288,13 +2289,11 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
start_reg = f71882fg_read8(data, F71882FG_REG_START);
if (start_reg & 0x04) {
dev_warn(&pdev->dev, "Hardware monitor is powered down\n");
- err = -ENODEV;
- goto exit_free;
+ return -ENODEV;
}
if (!(start_reg & 0x03)) {
dev_warn(&pdev->dev, "Hardware monitoring not activated\n");
- err = -ENODEV;
- goto exit_free;
+ return -ENODEV;
}
/* Register sysfs interface files */
@@ -2422,8 +2421,6 @@ static int __devinit f71882fg_probe(struct platform_device *pdev)
exit_unregister_sysfs:
f71882fg_remove(pdev); /* Will unregister the sysfs files for us */
return err; /* f71882fg_remove() also frees our data */
-exit_free:
- kfree(data);
return err;
}
@@ -2525,17 +2522,13 @@ static int f71882fg_remove(struct platform_device *pdev)
ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans);
}
}
-
- platform_set_drvdata(pdev, NULL);
- kfree(data);
-
return 0;
}
-static int __init f71882fg_find(int sioaddr, unsigned short *address,
- struct f71882fg_sio_data *sio_data)
+static int __init f71882fg_find(int sioaddr, struct f71882fg_sio_data *sio_data)
{
u16 devid;
+ unsigned short address;
int err = superio_enter(sioaddr);
if (err)
return err;
@@ -2603,25 +2596,25 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address,
goto exit;
}
- *address = superio_inw(sioaddr, SIO_REG_ADDR);
- if (*address == 0) {
+ address = superio_inw(sioaddr, SIO_REG_ADDR);
+ if (address == 0) {
pr_warn("Base address not set\n");
err = -ENODEV;
goto exit;
}
- *address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */
+ address &= ~(REGION_LENGTH - 1); /* Ignore 3 LSB */
- err = 0;
+ err = address;
pr_info("Found %s chip at %#x, revision %d\n",
- f71882fg_names[sio_data->type], (unsigned int)*address,
+ f71882fg_names[sio_data->type], (unsigned int)address,
(int)superio_inb(sioaddr, SIO_REG_DEVREV));
exit:
superio_exit(sioaddr);
return err;
}
-static int __init f71882fg_device_add(unsigned short address,
- const struct f71882fg_sio_data *sio_data)
+static int __init f71882fg_device_add(int address,
+ const struct f71882fg_sio_data *sio_data)
{
struct resource res = {
.start = address,
@@ -2668,19 +2661,21 @@ exit_device_put:
static int __init f71882fg_init(void)
{
- int err = -ENODEV;
- unsigned short address;
+ int err;
+ int address;
struct f71882fg_sio_data sio_data;
memset(&sio_data, 0, sizeof(sio_data));
- if (f71882fg_find(0x2e, &address, &sio_data) &&
- f71882fg_find(0x4e, &address, &sio_data))
- goto exit;
+ address = f71882fg_find(0x2e, &sio_data);
+ if (address < 0)
+ address = f71882fg_find(0x4e, &sio_data);
+ if (address < 0)
+ return address;
err = platform_driver_register(&f71882fg_driver);
if (err)
- goto exit;
+ return err;
err = f71882fg_device_add(address, &sio_data);
if (err)
@@ -2690,7 +2685,6 @@ static int __init f71882fg_init(void)
exit_driver:
platform_driver_unregister(&f71882fg_driver);
-exit:
return err;
}
diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c
index ece4159bd453..f7dba229395f 100644
--- a/drivers/hwmon/f75375s.c
+++ b/drivers/hwmon/f75375s.c
@@ -838,7 +838,8 @@ static int f75375_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- data = kzalloc(sizeof(struct f75375_data), GFP_KERNEL);
+ data = devm_kzalloc(&client->dev, sizeof(struct f75375_data),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -848,7 +849,7 @@ static int f75375_probe(struct i2c_client *client,
err = sysfs_create_group(&client->dev.kobj, &f75375_group);
if (err)
- goto exit_free;
+ return err;
if (data->kind != f75373) {
err = sysfs_chmod_file(&client->dev.kobj,
@@ -875,8 +876,6 @@ static int f75375_probe(struct i2c_client *client,
exit_remove:
sysfs_remove_group(&client->dev.kobj, &f75375_group);
-exit_free:
- kfree(data);
return err;
}
@@ -885,7 +884,6 @@ static int f75375_remove(struct i2c_client *client)
struct f75375_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &f75375_group);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c
index 2764b78a784b..68ad7d255512 100644
--- a/drivers/hwmon/fam15h_power.c
+++ b/drivers/hwmon/fam15h_power.c
@@ -129,12 +129,12 @@ static bool __devinit fam15h_power_is_internal_node0(struct pci_dev *f4)
* counter saturations resulting in bogus power readings.
* We correct this value ourselves to cope with older BIOSes.
*/
-static DEFINE_PCI_DEVICE_TABLE(affected_device) = {
+static const struct pci_device_id affected_device[] = {
{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) },
{ 0 }
};
-static void __devinit tweak_runavg_range(struct pci_dev *pdev)
+static void tweak_runavg_range(struct pci_dev *pdev)
{
u32 val;
@@ -158,6 +158,16 @@ static void __devinit tweak_runavg_range(struct pci_dev *pdev)
REG_TDP_RUNNING_AVERAGE, val);
}
+#ifdef CONFIG_PM
+static int fam15h_power_resume(struct pci_dev *pdev)
+{
+ tweak_runavg_range(pdev);
+ return 0;
+}
+#else
+#define fam15h_power_resume NULL
+#endif
+
static void __devinit fam15h_power_init_data(struct pci_dev *f4,
struct fam15h_power_data *data)
{
@@ -188,7 +198,7 @@ static int __devinit fam15h_power_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct fam15h_power_data *data;
- struct device *dev;
+ struct device *dev = &pdev->dev;
int err;
/*
@@ -198,23 +208,19 @@ static int __devinit fam15h_power_probe(struct pci_dev *pdev,
*/
tweak_runavg_range(pdev);
- if (!fam15h_power_is_internal_node0(pdev)) {
- err = -ENODEV;
- goto exit;
- }
+ if (!fam15h_power_is_internal_node0(pdev))
+ return -ENODEV;
+
+ data = devm_kzalloc(dev, sizeof(struct fam15h_power_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
- data = kzalloc(sizeof(struct fam15h_power_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
fam15h_power_init_data(pdev, data);
- dev = &pdev->dev;
dev_set_drvdata(dev, data);
err = sysfs_create_group(&dev->kobj, &fam15h_power_attr_group);
if (err)
- goto exit_free_data;
+ return err;
data->hwmon_dev = hwmon_device_register(dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -226,9 +232,6 @@ static int __devinit fam15h_power_probe(struct pci_dev *pdev,
exit_remove_group:
sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
-exit_free_data:
- kfree(data);
-exit:
return err;
}
@@ -241,8 +244,6 @@ static void __devexit fam15h_power_remove(struct pci_dev *pdev)
data = dev_get_drvdata(dev);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&dev->kobj, &fam15h_power_attr_group);
- dev_set_drvdata(dev, NULL);
- kfree(data);
}
static DEFINE_PCI_DEVICE_TABLE(fam15h_power_id_table) = {
@@ -256,6 +257,7 @@ static struct pci_driver fam15h_power_driver = {
.id_table = fam15h_power_id_table,
.probe = fam15h_power_probe,
.remove = __devexit_p(fam15h_power_remove),
+ .resume = fam15h_power_resume,
};
module_pci_driver(fam15h_power_driver);
diff --git a/drivers/hwmon/g760a.c b/drivers/hwmon/g760a.c
index ebcd2698e4dc..8b2106f60eda 100644
--- a/drivers/hwmon/g760a.c
+++ b/drivers/hwmon/g760a.c
@@ -207,7 +207,8 @@ static int g760a_probe(struct i2c_client *client,
I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
- data = kzalloc(sizeof(struct g760a_data), GFP_KERNEL);
+ data = devm_kzalloc(&client->dev, sizeof(struct g760a_data),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -223,7 +224,7 @@ static int g760a_probe(struct i2c_client *client,
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &g760a_group);
if (err)
- goto error_sysfs_create_group;
+ return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -235,9 +236,6 @@ static int g760a_probe(struct i2c_client *client,
error_hwmon_device_register:
sysfs_remove_group(&client->dev.kobj, &g760a_group);
-error_sysfs_create_group:
- kfree(data);
-
return err;
}
@@ -246,8 +244,6 @@ static int g760a_remove(struct i2c_client *client)
struct g760a_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &g760a_group);
- kfree(data);
-
return 0;
}
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index 602148299f68..70717d4a5e89 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -5,10 +5,18 @@
* Zero Drift Bi-Directional Current/Power Monitor with I2C Interface
* Datasheet: http://www.ti.com/product/ina219
*
+ * INA220:
+ * Bi-Directional Current/Power Monitor with I2C Interface
+ * Datasheet: http://www.ti.com/product/ina220
+ *
* INA226:
* Bi-Directional Current/Power Monitor with I2C Interface
* Datasheet: http://www.ti.com/product/ina226
*
+ * INA230:
+ * Bi-directional Current/Power Monitor with I2C Interface
+ * Datasheet: http://www.ti.com/product/ina230
+ *
* Copyright (C) 2012 Lothar Felten <l-felten@ti.com>
* Thanks to Jan Volkering
*
@@ -57,18 +65,49 @@
enum ina2xx_ids { ina219, ina226 };
+struct ina2xx_config {
+ u16 config_default;
+ int calibration_factor;
+ int registers;
+ int shunt_div;
+ int bus_voltage_shift;
+ int bus_voltage_lsb; /* uV */
+ int power_lsb; /* uW */
+};
+
struct ina2xx_data {
struct device *hwmon_dev;
+ const struct ina2xx_config *config;
struct mutex update_lock;
bool valid;
unsigned long last_updated;
int kind;
- int registers;
u16 regs[INA2XX_MAX_REGISTERS];
};
+static const struct ina2xx_config ina2xx_config[] = {
+ [ina219] = {
+ .config_default = INA219_CONFIG_DEFAULT,
+ .calibration_factor = 40960000,
+ .registers = INA219_REGISTERS,
+ .shunt_div = 100,
+ .bus_voltage_shift = 3,
+ .bus_voltage_lsb = 4000,
+ .power_lsb = 20000,
+ },
+ [ina226] = {
+ .config_default = INA226_CONFIG_DEFAULT,
+ .calibration_factor = 5120000,
+ .registers = INA226_REGISTERS,
+ .shunt_div = 400,
+ .bus_voltage_shift = 0,
+ .bus_voltage_lsb = 1250,
+ .power_lsb = 25000,
+ },
+};
+
static struct ina2xx_data *ina2xx_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -85,7 +124,7 @@ static struct ina2xx_data *ina2xx_update_device(struct device *dev)
dev_dbg(&client->dev, "Starting ina2xx update\n");
/* Read all registers */
- for (i = 0; i < data->registers; i++) {
+ for (i = 0; i < data->config->registers; i++) {
int rv = i2c_smbus_read_word_swapped(client, i);
if (rv < 0) {
ret = ERR_PTR(rv);
@@ -101,73 +140,26 @@ abort:
return ret;
}
-static int ina219_get_value(struct ina2xx_data *data, u8 reg)
+static int ina2xx_get_value(struct ina2xx_data *data, u8 reg)
{
- /*
- * calculate exact value for the given register
- * we assume default power-on reset settings:
- * bus voltage range 32V
- * gain = /8
- * adc 1 & 2 -> conversion time 532uS
- * mode is continuous shunt and bus
- * calibration value is INA219_CALIBRATION_VALUE
- */
- int val = data->regs[reg];
+ int val;
switch (reg) {
case INA2XX_SHUNT_VOLTAGE:
- /* LSB=10uV. Convert to mV. */
- val = DIV_ROUND_CLOSEST(val, 100);
+ val = DIV_ROUND_CLOSEST(data->regs[reg],
+ data->config->shunt_div);
break;
case INA2XX_BUS_VOLTAGE:
- /* LSB=4mV. Register is not right aligned, convert to mV. */
- val = (val >> 3) * 4;
+ val = (data->regs[reg] >> data->config->bus_voltage_shift)
+ * data->config->bus_voltage_lsb;
+ val = DIV_ROUND_CLOSEST(val, 1000);
break;
case INA2XX_POWER:
- /* LSB=20mW. Convert to uW */
- val = val * 20 * 1000;
- break;
- case INA2XX_CURRENT:
- /* LSB=1mA (selected). Is in mA */
- break;
- default:
- /* programmer goofed */
- WARN_ON_ONCE(1);
- val = 0;
- break;
- }
-
- return val;
-}
-
-static int ina226_get_value(struct ina2xx_data *data, u8 reg)
-{
- /*
- * calculate exact value for the given register
- * we assume default power-on reset settings:
- * bus voltage range 32V
- * gain = /8
- * adc 1 & 2 -> conversion time 532uS
- * mode is continuous shunt and bus
- * calibration value is INA226_CALIBRATION_VALUE
- */
- int val = data->regs[reg];
-
- switch (reg) {
- case INA2XX_SHUNT_VOLTAGE:
- /* LSB=2.5uV. Convert to mV. */
- val = DIV_ROUND_CLOSEST(val, 400);
- break;
- case INA2XX_BUS_VOLTAGE:
- /* LSB=1.25mV. Convert to mV. */
- val = val + DIV_ROUND_CLOSEST(val, 4);
- break;
- case INA2XX_POWER:
- /* LSB=25mW. Convert to uW */
- val = val * 25 * 1000;
+ val = data->regs[reg] * data->config->power_lsb;
break;
case INA2XX_CURRENT:
/* LSB=1mA (selected). Is in mA */
+ val = data->regs[reg];
break;
default:
/* programmer goofed */
@@ -184,23 +176,12 @@ static ssize_t ina2xx_show_value(struct device *dev,
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
struct ina2xx_data *data = ina2xx_update_device(dev);
- int value = 0;
if (IS_ERR(data))
return PTR_ERR(data);
- switch (data->kind) {
- case ina219:
- value = ina219_get_value(data, attr->index);
- break;
- case ina226:
- value = ina226_get_value(data, attr->index);
- break;
- default:
- WARN_ON_ONCE(1);
- break;
- }
- return snprintf(buf, PAGE_SIZE, "%d\n", value);
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ ina2xx_get_value(data, attr->index));
}
/* shunt voltage */
@@ -238,7 +219,7 @@ static int ina2xx_probe(struct i2c_client *client,
struct i2c_adapter *adapter = client->adapter;
struct ina2xx_data *data;
struct ina2xx_platform_data *pdata;
- int ret = 0;
+ int ret;
long shunt = 10000; /* default shunt value 10mOhms */
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
@@ -259,38 +240,15 @@ static int ina2xx_probe(struct i2c_client *client,
/* set the device type */
data->kind = id->driver_data;
+ data->config = &ina2xx_config[data->kind];
- switch (data->kind) {
- case ina219:
- /* device configuration */
- i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
- INA219_CONFIG_DEFAULT);
-
- /* set current LSB to 1mA, shunt is in uOhms */
- /* (equation 13 in datasheet) */
- i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
- 40960000 / shunt);
- dev_info(&client->dev,
- "power monitor INA219 (Rshunt = %li uOhm)\n", shunt);
- data->registers = INA219_REGISTERS;
- break;
- case ina226:
- /* device configuration */
- i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
- INA226_CONFIG_DEFAULT);
-
- /* set current LSB to 1mA, shunt is in uOhms */
- /* (equation 1 in datasheet)*/
- i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
- 5120000 / shunt);
- dev_info(&client->dev,
- "power monitor INA226 (Rshunt = %li uOhm)\n", shunt);
- data->registers = INA226_REGISTERS;
- break;
- default:
- /* unknown device id */
- return -ENODEV;
- }
+ /* device configuration */
+ i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
+ data->config->config_default);
+ /* set current LSB to 1mA, shunt is in uOhms */
+ /* (equation 13 in datasheet) */
+ i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
+ data->config->calibration_factor / shunt);
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -305,6 +263,9 @@ static int ina2xx_probe(struct i2c_client *client,
goto out_err_hwmon;
}
+ dev_info(&client->dev, "power monitor %s (Rshunt = %li uOhm)\n",
+ id->name, shunt);
+
return 0;
out_err_hwmon:
@@ -324,7 +285,9 @@ static int ina2xx_remove(struct i2c_client *client)
static const struct i2c_device_id ina2xx_id[] = {
{ "ina219", ina219 },
+ { "ina220", ina219 },
{ "ina226", ina226 },
+ { "ina230", ina226 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ina2xx_id);
diff --git a/drivers/hwmon/jz4740-hwmon.c b/drivers/hwmon/jz4740-hwmon.c
index 5253d23361d9..dee9eec2036e 100644
--- a/drivers/hwmon/jz4740-hwmon.c
+++ b/drivers/hwmon/jz4740-hwmon.c
@@ -20,6 +20,7 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/io.h>
#include <linux/completion.h>
#include <linux/mfd/core.h>
@@ -106,42 +107,37 @@ static int __devinit jz4740_hwmon_probe(struct platform_device *pdev)
int ret;
struct jz4740_hwmon *hwmon;
- hwmon = kmalloc(sizeof(*hwmon), GFP_KERNEL);
- if (!hwmon) {
- dev_err(&pdev->dev, "Failed to allocate driver structure\n");
+ hwmon = devm_kzalloc(&pdev->dev, sizeof(*hwmon), GFP_KERNEL);
+ if (!hwmon)
return -ENOMEM;
- }
hwmon->cell = mfd_get_cell(pdev);
hwmon->irq = platform_get_irq(pdev, 0);
if (hwmon->irq < 0) {
- ret = hwmon->irq;
- dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret);
- goto err_free;
+ dev_err(&pdev->dev, "Failed to get platform irq: %d\n",
+ hwmon->irq);
+ return hwmon->irq;
}
hwmon->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!hwmon->mem) {
- ret = -ENOENT;
dev_err(&pdev->dev, "Failed to get platform mmio resource\n");
- goto err_free;
+ return -ENOENT;
}
- hwmon->mem = request_mem_region(hwmon->mem->start,
+ hwmon->mem = devm_request_mem_region(&pdev->dev, hwmon->mem->start,
resource_size(hwmon->mem), pdev->name);
if (!hwmon->mem) {
- ret = -EBUSY;
dev_err(&pdev->dev, "Failed to request mmio memory region\n");
- goto err_free;
+ return -EBUSY;
}
- hwmon->base = ioremap_nocache(hwmon->mem->start,
- resource_size(hwmon->mem));
+ hwmon->base = devm_ioremap_nocache(&pdev->dev, hwmon->mem->start,
+ resource_size(hwmon->mem));
if (!hwmon->base) {
- ret = -EBUSY;
dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
- goto err_release_mem_region;
+ return -EBUSY;
}
init_completion(&hwmon->read_completion);
@@ -149,17 +145,18 @@ static int __devinit jz4740_hwmon_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, hwmon);
- ret = request_irq(hwmon->irq, jz4740_hwmon_irq, 0, pdev->name, hwmon);
+ ret = devm_request_irq(&pdev->dev, hwmon->irq, jz4740_hwmon_irq, 0,
+ pdev->name, hwmon);
if (ret) {
dev_err(&pdev->dev, "Failed to request irq: %d\n", ret);
- goto err_iounmap;
+ return ret;
}
disable_irq(hwmon->irq);
ret = sysfs_create_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group);
if (ret) {
dev_err(&pdev->dev, "Failed to create sysfs group: %d\n", ret);
- goto err_free_irq;
+ return ret;
}
hwmon->hwmon = hwmon_device_register(&pdev->dev);
@@ -172,16 +169,6 @@ static int __devinit jz4740_hwmon_probe(struct platform_device *pdev)
err_remove_file:
sysfs_remove_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group);
-err_free_irq:
- free_irq(hwmon->irq, hwmon);
-err_iounmap:
- platform_set_drvdata(pdev, NULL);
- iounmap(hwmon->base);
-err_release_mem_region:
- release_mem_region(hwmon->mem->start, resource_size(hwmon->mem));
-err_free:
- kfree(hwmon);
-
return ret;
}
@@ -192,14 +179,6 @@ static int __devexit jz4740_hwmon_remove(struct platform_device *pdev)
hwmon_device_unregister(hwmon->hwmon);
sysfs_remove_group(&pdev->dev.kobj, &jz4740_hwmon_attr_group);
- free_irq(hwmon->irq, hwmon);
-
- iounmap(hwmon->base);
- release_mem_region(hwmon->mem->start, resource_size(hwmon->mem));
-
- platform_set_drvdata(pdev, NULL);
- kfree(hwmon);
-
return 0;
}
diff --git a/drivers/hwmon/lm70.c b/drivers/hwmon/lm70.c
index 472f79521a96..2d1777a03edb 100644
--- a/drivers/hwmon/lm70.c
+++ b/drivers/hwmon/lm70.c
@@ -43,6 +43,8 @@
#define LM70_CHIP_LM70 0 /* original NS LM70 */
#define LM70_CHIP_TMP121 1 /* TI TMP121/TMP123 */
+#define LM70_CHIP_LM71 2 /* NS LM71 */
+#define LM70_CHIP_LM74 3 /* NS LM74 */
struct lm70 {
struct device *hwmon_dev;
@@ -88,9 +90,13 @@ static ssize_t lm70_sense_temp(struct device *dev,
* Celsius.
* So it's equivalent to multiplying by 0.25 * 1000 = 250.
*
- * TMP121/TMP123:
+ * LM74 and TMP121/TMP123:
* 13 bits of 2's complement data, discard LSB 3 bits,
* resolution 0.0625 degrees celsius.
+ *
+ * LM71:
+ * 14 bits of 2's complement data, discard LSB 2 bits,
+ * resolution 0.0312 degrees celsius.
*/
switch (p_lm70->chip) {
case LM70_CHIP_LM70:
@@ -98,8 +104,13 @@ static ssize_t lm70_sense_temp(struct device *dev,
break;
case LM70_CHIP_TMP121:
+ case LM70_CHIP_LM74:
val = ((int)raw / 8) * 625 / 10;
break;
+
+ case LM70_CHIP_LM71:
+ val = ((int)raw / 4) * 3125 / 100;
+ break;
}
status = sprintf(buf, "%d\n", val); /* millidegrees Celsius */
@@ -113,20 +124,7 @@ static DEVICE_ATTR(temp1_input, S_IRUGO, lm70_sense_temp, NULL);
static ssize_t lm70_show_name(struct device *dev, struct device_attribute
*devattr, char *buf)
{
- struct lm70 *p_lm70 = dev_get_drvdata(dev);
- int ret;
-
- switch (p_lm70->chip) {
- case LM70_CHIP_LM70:
- ret = sprintf(buf, "lm70\n");
- break;
- case LM70_CHIP_TMP121:
- ret = sprintf(buf, "tmp121\n");
- break;
- default:
- ret = -EINVAL;
- }
- return ret;
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
}
static DEVICE_ATTR(name, S_IRUGO, lm70_show_name, NULL);
@@ -139,17 +137,13 @@ static int __devinit lm70_probe(struct spi_device *spi)
struct lm70 *p_lm70;
int status;
- /* signaling is SPI_MODE_0 for both LM70 and TMP121 */
+ /* signaling is SPI_MODE_0 */
if (spi->mode & (SPI_CPOL | SPI_CPHA))
return -EINVAL;
- /* 3-wire link (shared SI/SO) for LM70 */
- if (chip == LM70_CHIP_LM70 && !(spi->mode & SPI_3WIRE))
- return -EINVAL;
-
/* NOTE: we assume 8-bit words, and convert to 16 bits manually */
- p_lm70 = kzalloc(sizeof *p_lm70, GFP_KERNEL);
+ p_lm70 = devm_kzalloc(&spi->dev, sizeof(*p_lm70), GFP_KERNEL);
if (!p_lm70)
return -ENOMEM;
@@ -181,7 +175,6 @@ out_dev_create_file_failed:
device_remove_file(&spi->dev, &dev_attr_temp1_input);
out_dev_create_temp_file_failed:
spi_set_drvdata(spi, NULL);
- kfree(p_lm70);
return status;
}
@@ -193,7 +186,6 @@ static int __devexit lm70_remove(struct spi_device *spi)
device_remove_file(&spi->dev, &dev_attr_temp1_input);
device_remove_file(&spi->dev, &dev_attr_name);
spi_set_drvdata(spi, NULL);
- kfree(p_lm70);
return 0;
}
@@ -202,6 +194,8 @@ static int __devexit lm70_remove(struct spi_device *spi)
static const struct spi_device_id lm70_ids[] = {
{ "lm70", LM70_CHIP_LM70 },
{ "tmp121", LM70_CHIP_TMP121 },
+ { "lm71", LM70_CHIP_LM71 },
+ { "lm74", LM70_CHIP_LM74 },
{ },
};
MODULE_DEVICE_TABLE(spi, lm70_ids);
@@ -219,5 +213,5 @@ static struct spi_driver lm70_driver = {
module_spi_driver(lm70_driver);
MODULE_AUTHOR("Kaiwan N Billimoria");
-MODULE_DESCRIPTION("NS LM70 / TI TMP121/TMP123 Linux driver");
+MODULE_DESCRIPTION("NS LM70 and compatibles Linux driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c
index bf946187bd37..c3d4255ed154 100644
--- a/drivers/hwmon/lm93.c
+++ b/drivers/hwmon/lm93.c
@@ -1830,7 +1830,7 @@ static ssize_t store_fan_smart_tach(struct device *dev,
mutex_lock(&data->update_lock);
/* sanity test, ignore the write otherwise */
- if (0 <= val && val <= 2) {
+ if (val <= 2) {
/* can't enable if pwm freq is 22.5KHz */
if (val) {
u8 ctl4 = lm93_read_byte(client,
diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c
index bd8cdb7b96ed..4b68fb2a31d7 100644
--- a/drivers/hwmon/lm95241.c
+++ b/drivers/hwmon/lm95241.c
@@ -391,11 +391,10 @@ static int lm95241_probe(struct i2c_client *new_client,
struct lm95241_data *data;
int err;
- data = kzalloc(sizeof(struct lm95241_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(&new_client->dev, sizeof(struct lm95241_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(new_client, data);
mutex_init(&data->update_lock);
@@ -406,7 +405,7 @@ static int lm95241_probe(struct i2c_client *new_client,
/* Register sysfs hooks */
err = sysfs_create_group(&new_client->dev.kobj, &lm95241_group);
if (err)
- goto exit_free;
+ return err;
data->hwmon_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -418,9 +417,6 @@ static int lm95241_probe(struct i2c_client *new_client,
exit_remove_files:
sysfs_remove_group(&new_client->dev.kobj, &lm95241_group);
-exit_free:
- kfree(data);
-exit:
return err;
}
@@ -431,7 +427,6 @@ static int lm95241_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &lm95241_group);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c
index 9a46c106a240..2915fd908364 100644
--- a/drivers/hwmon/lm95245.c
+++ b/drivers/hwmon/lm95245.c
@@ -462,11 +462,10 @@ static int lm95245_probe(struct i2c_client *new_client,
struct lm95245_data *data;
int err;
- data = kzalloc(sizeof(struct lm95245_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(&new_client->dev, sizeof(struct lm95245_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(new_client, data);
mutex_init(&data->update_lock);
@@ -477,7 +476,7 @@ static int lm95245_probe(struct i2c_client *new_client,
/* Register sysfs hooks */
err = sysfs_create_group(&new_client->dev.kobj, &lm95245_group);
if (err)
- goto exit_free;
+ return err;
data->hwmon_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -489,9 +488,6 @@ static int lm95245_probe(struct i2c_client *new_client,
exit_remove_files:
sysfs_remove_group(&new_client->dev.kobj, &lm95245_group);
-exit_free:
- kfree(data);
-exit:
return err;
}
@@ -502,7 +498,6 @@ static int lm95245_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &lm95245_group);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/ltc4151.c b/drivers/hwmon/ltc4151.c
index 4d005b219de2..8496baa08bc8 100644
--- a/drivers/hwmon/ltc4151.c
+++ b/drivers/hwmon/ltc4151.c
@@ -181,11 +181,9 @@ static int ltc4151_probe(struct i2c_client *client,
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data) {
- ret = -ENOMEM;
- goto out_kzalloc;
- }
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -193,7 +191,7 @@ static int ltc4151_probe(struct i2c_client *client,
/* Register sysfs hooks */
ret = sysfs_create_group(&client->dev.kobj, &ltc4151_group);
if (ret)
- goto out_sysfs_create_group;
+ return ret;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -205,9 +203,6 @@ static int ltc4151_probe(struct i2c_client *client,
out_hwmon_device_register:
sysfs_remove_group(&client->dev.kobj, &ltc4151_group);
-out_sysfs_create_group:
- kfree(data);
-out_kzalloc:
return ret;
}
@@ -218,8 +213,6 @@ static int ltc4151_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &ltc4151_group);
- kfree(data);
-
return 0;
}
diff --git a/drivers/hwmon/ltc4215.c b/drivers/hwmon/ltc4215.c
index 429c5b2b66fd..98b3d04f98b7 100644
--- a/drivers/hwmon/ltc4215.c
+++ b/drivers/hwmon/ltc4215.c
@@ -253,11 +253,9 @@ static int ltc4215_probe(struct i2c_client *client,
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data) {
- ret = -ENOMEM;
- goto out_kzalloc;
- }
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -268,7 +266,7 @@ static int ltc4215_probe(struct i2c_client *client,
/* Register sysfs hooks */
ret = sysfs_create_group(&client->dev.kobj, &ltc4215_group);
if (ret)
- goto out_sysfs_create_group;
+ return ret;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -280,9 +278,6 @@ static int ltc4215_probe(struct i2c_client *client,
out_hwmon_device_register:
sysfs_remove_group(&client->dev.kobj, &ltc4215_group);
-out_sysfs_create_group:
- kfree(data);
-out_kzalloc:
return ret;
}
@@ -293,8 +288,6 @@ static int ltc4215_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &ltc4215_group);
- kfree(data);
-
return 0;
}
diff --git a/drivers/hwmon/ltc4245.c b/drivers/hwmon/ltc4245.c
index b99b45bafdad..52075914eb0b 100644
--- a/drivers/hwmon/ltc4245.c
+++ b/drivers/hwmon/ltc4245.c
@@ -519,11 +519,9 @@ static int ltc4245_probe(struct i2c_client *client,
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- data = kzalloc(sizeof(*data), GFP_KERNEL);
- if (!data) {
- ret = -ENOMEM;
- goto out_kzalloc;
- }
+ data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -536,7 +534,7 @@ static int ltc4245_probe(struct i2c_client *client,
/* Register sysfs hooks */
ret = ltc4245_sysfs_create_groups(client);
if (ret)
- goto out_sysfs_create_groups;
+ return ret;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -548,9 +546,6 @@ static int ltc4245_probe(struct i2c_client *client,
out_hwmon_device_register:
ltc4245_sysfs_remove_groups(client);
-out_sysfs_create_groups:
- kfree(data);
-out_kzalloc:
return ret;
}
@@ -560,7 +555,6 @@ static int ltc4245_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
ltc4245_sysfs_remove_groups(client);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/max1111.c b/drivers/hwmon/max1111.c
index f3978a46e844..b4eb0889c465 100644
--- a/drivers/hwmon/max1111.c
+++ b/drivers/hwmon/max1111.c
@@ -22,6 +22,8 @@
#include <linux/spi/spi.h>
#include <linux/slab.h>
+enum chips { max1110, max1111, max1112, max1113 };
+
#define MAX1111_TX_BUF_SIZE 1
#define MAX1111_RX_BUF_SIZE 2
@@ -30,6 +32,7 @@
#define MAX1111_CTRL_PD1 (1u << 1)
#define MAX1111_CTRL_SGL (1u << 2)
#define MAX1111_CTRL_UNI (1u << 3)
+#define MAX1110_CTRL_SEL_SH (4)
#define MAX1111_CTRL_SEL_SH (5) /* NOTE: bit 4 is ignored */
#define MAX1111_CTRL_STR (1u << 7)
@@ -42,6 +45,8 @@ struct max1111_data {
uint8_t rx_buf[MAX1111_RX_BUF_SIZE];
struct mutex drvdata_lock;
/* protect msg, xfer and buffers from multiple access */
+ int sel_sh;
+ int lsb;
};
static int max1111_read(struct device *dev, int channel)
@@ -53,7 +58,7 @@ static int max1111_read(struct device *dev, int channel)
/* writing to drvdata struct is not thread safe, wait on mutex */
mutex_lock(&data->drvdata_lock);
- data->tx_buf[0] = (channel << MAX1111_CTRL_SEL_SH) |
+ data->tx_buf[0] = (channel << data->sel_sh) |
MAX1111_CTRL_PD0 | MAX1111_CTRL_PD1 |
MAX1111_CTRL_SGL | MAX1111_CTRL_UNI | MAX1111_CTRL_STR;
@@ -93,12 +98,13 @@ EXPORT_SYMBOL(max1111_read_channel);
static ssize_t show_name(struct device *dev,
struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "max1111\n");
+ return sprintf(buf, "%s\n", to_spi_device(dev)->modalias);
}
static ssize_t show_adc(struct device *dev,
struct device_attribute *attr, char *buf)
{
+ struct max1111_data *data = dev_get_drvdata(dev);
int channel = to_sensor_dev_attr(attr)->index;
int ret;
@@ -107,10 +113,10 @@ static ssize_t show_adc(struct device *dev,
return ret;
/*
- * assume the reference voltage to be 2.048V, with an 8-bit sample,
- * the LSB weight is 8mV
+ * Assume the reference voltage to be 2.048V or 4.096V, with an 8-bit
+ * sample. The LSB weight is 8mV or 16mV depending on the chip type.
*/
- return sprintf(buf, "%d\n", ret * 8);
+ return sprintf(buf, "%d\n", ret * data->lsb);
}
#define MAX1111_ADC_ATTR(_id) \
@@ -121,6 +127,10 @@ static MAX1111_ADC_ATTR(0);
static MAX1111_ADC_ATTR(1);
static MAX1111_ADC_ATTR(2);
static MAX1111_ADC_ATTR(3);
+static MAX1111_ADC_ATTR(4);
+static MAX1111_ADC_ATTR(5);
+static MAX1111_ADC_ATTR(6);
+static MAX1111_ADC_ATTR(7);
static struct attribute *max1111_attributes[] = {
&dev_attr_name.attr,
@@ -135,6 +145,18 @@ static const struct attribute_group max1111_attr_group = {
.attrs = max1111_attributes,
};
+static struct attribute *max1110_attributes[] = {
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group max1110_attr_group = {
+ .attrs = max1110_attributes,
+};
+
static int __devinit setup_transfer(struct max1111_data *data)
{
struct spi_message *m;
@@ -159,6 +181,7 @@ static int __devinit setup_transfer(struct max1111_data *data)
static int __devinit max1111_probe(struct spi_device *spi)
{
+ enum chips chip = spi_get_device_id(spi)->driver_data;
struct max1111_data *data;
int err;
@@ -174,6 +197,24 @@ static int __devinit max1111_probe(struct spi_device *spi)
return -ENOMEM;
}
+ switch (chip) {
+ case max1110:
+ data->lsb = 8;
+ data->sel_sh = MAX1110_CTRL_SEL_SH;
+ break;
+ case max1111:
+ data->lsb = 8;
+ data->sel_sh = MAX1111_CTRL_SEL_SH;
+ break;
+ case max1112:
+ data->lsb = 16;
+ data->sel_sh = MAX1110_CTRL_SEL_SH;
+ break;
+ case max1113:
+ data->lsb = 16;
+ data->sel_sh = MAX1111_CTRL_SEL_SH;
+ break;
+ }
err = setup_transfer(data);
if (err)
return err;
@@ -188,6 +229,14 @@ static int __devinit max1111_probe(struct spi_device *spi)
dev_err(&spi->dev, "failed to create attribute group\n");
return err;
}
+ if (chip == max1110 || chip == max1112) {
+ err = sysfs_create_group(&spi->dev.kobj, &max1110_attr_group);
+ if (err) {
+ dev_err(&spi->dev,
+ "failed to create extended attribute group\n");
+ goto err_remove;
+ }
+ }
data->hwmon_dev = hwmon_device_register(&spi->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -202,6 +251,7 @@ static int __devinit max1111_probe(struct spi_device *spi)
return 0;
err_remove:
+ sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
return err;
}
@@ -211,16 +261,27 @@ static int __devexit max1111_remove(struct spi_device *spi)
struct max1111_data *data = spi_get_drvdata(spi);
hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&spi->dev.kobj, &max1110_attr_group);
sysfs_remove_group(&spi->dev.kobj, &max1111_attr_group);
mutex_destroy(&data->drvdata_lock);
return 0;
}
+static const struct spi_device_id max1111_ids[] = {
+ { "max1110", max1110 },
+ { "max1111", max1111 },
+ { "max1112", max1112 },
+ { "max1113", max1113 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, max1111_ids);
+
static struct spi_driver max1111_driver = {
.driver = {
.name = "max1111",
.owner = THIS_MODULE,
},
+ .id_table = max1111_ids,
.probe = max1111_probe,
.remove = __devexit_p(max1111_remove),
};
@@ -228,6 +289,5 @@ static struct spi_driver max1111_driver = {
module_spi_driver(max1111_driver);
MODULE_AUTHOR("Eric Miao <eric.miao@marvell.com>");
-MODULE_DESCRIPTION("MAX1111 ADC Driver");
+MODULE_DESCRIPTION("MAX1110/MAX1111/MAX1112/MAX1113 ADC Driver");
MODULE_LICENSE("GPL");
-MODULE_ALIAS("spi:max1111");
diff --git a/drivers/hwmon/max1668.c b/drivers/hwmon/max1668.c
index 335b183d7c02..666d9f6263eb 100644
--- a/drivers/hwmon/max1668.c
+++ b/drivers/hwmon/max1668.c
@@ -411,7 +411,8 @@ static int max1668_probe(struct i2c_client *client,
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- data = kzalloc(sizeof(struct max1668_data), GFP_KERNEL);
+ data = devm_kzalloc(&client->dev, sizeof(struct max1668_data),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -422,7 +423,7 @@ static int max1668_probe(struct i2c_client *client,
/* Register sysfs hooks */
err = sysfs_create_group(&client->dev.kobj, &max1668_group_common);
if (err)
- goto error_free;
+ return err;
if (data->type == max1668 || data->type == max1989) {
err = sysfs_create_group(&client->dev.kobj,
@@ -444,8 +445,6 @@ error_sysrem1:
sysfs_remove_group(&client->dev.kobj, &max1668_group_unique);
error_sysrem0:
sysfs_remove_group(&client->dev.kobj, &max1668_group_common);
-error_free:
- kfree(data);
return err;
}
@@ -459,7 +458,6 @@ static int max1668_remove(struct i2c_client *client)
sysfs_remove_group(&client->dev.kobj, &max1668_group_common);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/max197.c b/drivers/hwmon/max197.c
new file mode 100644
index 000000000000..6304f2616fa7
--- /dev/null
+++ b/drivers/hwmon/max197.c
@@ -0,0 +1,349 @@
+/*
+ * Maxim MAX197 A/D Converter driver
+ *
+ * Copyright (c) 2012 Savoir-faire Linux Inc.
+ * Vivien Didelot <vivien.didelot@savoirfairelinux.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.
+ *
+ * For further information, see the Documentation/hwmon/max197 file.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/max197.h>
+
+#define MAX199_LIMIT 4000 /* 4V */
+#define MAX197_LIMIT 10000 /* 10V */
+
+#define MAX197_NUM_CH 8 /* 8 Analog Input Channels */
+
+/* Control byte format */
+#define MAX197_BIP (1 << 3) /* Bipolarity */
+#define MAX197_RNG (1 << 4) /* Full range */
+
+#define MAX197_SCALE 12207 /* Scale coefficient for raw data */
+
+/* List of supported chips */
+enum max197_chips { max197, max199 };
+
+/**
+ * struct max197_data - device instance specific data
+ * @pdata: Platform data.
+ * @hwmon_dev: The hwmon device.
+ * @lock: Read/Write mutex.
+ * @limit: Max range value (10V for MAX197, 4V for MAX199).
+ * @scale: Need to scale.
+ * @ctrl_bytes: Channels control byte.
+ */
+struct max197_data {
+ struct max197_platform_data *pdata;
+ struct device *hwmon_dev;
+ struct mutex lock;
+ int limit;
+ bool scale;
+ u8 ctrl_bytes[MAX197_NUM_CH];
+};
+
+static inline void max197_set_unipolarity(struct max197_data *data, int channel)
+{
+ data->ctrl_bytes[channel] &= ~MAX197_BIP;
+}
+
+static inline void max197_set_bipolarity(struct max197_data *data, int channel)
+{
+ data->ctrl_bytes[channel] |= MAX197_BIP;
+}
+
+static inline void max197_set_half_range(struct max197_data *data, int channel)
+{
+ data->ctrl_bytes[channel] &= ~MAX197_RNG;
+}
+
+static inline void max197_set_full_range(struct max197_data *data, int channel)
+{
+ data->ctrl_bytes[channel] |= MAX197_RNG;
+}
+
+static inline bool max197_is_bipolar(struct max197_data *data, int channel)
+{
+ return data->ctrl_bytes[channel] & MAX197_BIP;
+}
+
+static inline bool max197_is_full_range(struct max197_data *data, int channel)
+{
+ return data->ctrl_bytes[channel] & MAX197_RNG;
+}
+
+/* Function called on read access on in{0,1,2,3,4,5,6,7}_{min,max} */
+static ssize_t max197_show_range(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct max197_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ int channel = attr->index;
+ bool is_min = attr->nr;
+ int range;
+
+ if (mutex_lock_interruptible(&data->lock))
+ return -ERESTARTSYS;
+
+ range = max197_is_full_range(data, channel) ?
+ data->limit : data->limit / 2;
+ if (is_min) {
+ if (max197_is_bipolar(data, channel))
+ range = -range;
+ else
+ range = 0;
+ }
+
+ mutex_unlock(&data->lock);
+
+ return sprintf(buf, "%d\n", range);
+}
+
+/* Function called on write access on in{0,1,2,3,4,5,6,7}_{min,max} */
+static ssize_t max197_store_range(struct device *dev,
+ struct device_attribute *devattr,
+ const char *buf, size_t count)
+{
+ struct max197_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr);
+ int channel = attr->index;
+ bool is_min = attr->nr;
+ long value;
+ int half = data->limit / 2;
+ int full = data->limit;
+
+ if (kstrtol(buf, 10, &value))
+ return -EINVAL;
+
+ if (is_min) {
+ if (value <= -full)
+ value = -full;
+ else if (value < 0)
+ value = -half;
+ else
+ value = 0;
+ } else {
+ if (value >= full)
+ value = full;
+ else
+ value = half;
+ }
+
+ if (mutex_lock_interruptible(&data->lock))
+ return -ERESTARTSYS;
+
+ if (value == 0) {
+ /* We can deduce only the polarity */
+ max197_set_unipolarity(data, channel);
+ } else if (value == -half) {
+ max197_set_bipolarity(data, channel);
+ max197_set_half_range(data, channel);
+ } else if (value == -full) {
+ max197_set_bipolarity(data, channel);
+ max197_set_full_range(data, channel);
+ } else if (value == half) {
+ /* We can deduce only the range */
+ max197_set_half_range(data, channel);
+ } else if (value == full) {
+ /* We can deduce only the range */
+ max197_set_full_range(data, channel);
+ }
+
+ mutex_unlock(&data->lock);
+
+ return count;
+}
+
+/* Function called on read access on in{0,1,2,3,4,5,6,7}_input */
+static ssize_t max197_show_input(struct device *dev,
+ struct device_attribute *devattr,
+ char *buf)
+{
+ struct max197_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int channel = attr->index;
+ s32 value;
+ int ret;
+
+ if (mutex_lock_interruptible(&data->lock))
+ return -ERESTARTSYS;
+
+ ret = data->pdata->convert(data->ctrl_bytes[channel]);
+ if (ret < 0) {
+ dev_err(dev, "conversion failed\n");
+ goto unlock;
+ }
+ value = ret;
+
+ /*
+ * Coefficient to apply on raw value.
+ * See Table 1. Full Scale and Zero Scale in the MAX197 datasheet.
+ */
+ if (data->scale) {
+ value *= MAX197_SCALE;
+ if (max197_is_full_range(data, channel))
+ value *= 2;
+ value /= 10000;
+ }
+
+ ret = sprintf(buf, "%d\n", value);
+
+unlock:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static ssize_t max197_show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ return sprintf(buf, "%s\n", pdev->name);
+}
+
+#define MAX197_SENSOR_DEVICE_ATTR_CH(chan) \
+ static SENSOR_DEVICE_ATTR(in##chan##_input, S_IRUGO, \
+ max197_show_input, NULL, chan); \
+ static SENSOR_DEVICE_ATTR_2(in##chan##_min, S_IRUGO | S_IWUSR, \
+ max197_show_range, \
+ max197_store_range, \
+ true, chan); \
+ static SENSOR_DEVICE_ATTR_2(in##chan##_max, S_IRUGO | S_IWUSR, \
+ max197_show_range, \
+ max197_store_range, \
+ false, chan)
+
+#define MAX197_SENSOR_DEV_ATTR_IN(chan) \
+ &sensor_dev_attr_in##chan##_input.dev_attr.attr, \
+ &sensor_dev_attr_in##chan##_max.dev_attr.attr, \
+ &sensor_dev_attr_in##chan##_min.dev_attr.attr
+
+static DEVICE_ATTR(name, S_IRUGO, max197_show_name, NULL);
+
+MAX197_SENSOR_DEVICE_ATTR_CH(0);
+MAX197_SENSOR_DEVICE_ATTR_CH(1);
+MAX197_SENSOR_DEVICE_ATTR_CH(2);
+MAX197_SENSOR_DEVICE_ATTR_CH(3);
+MAX197_SENSOR_DEVICE_ATTR_CH(4);
+MAX197_SENSOR_DEVICE_ATTR_CH(5);
+MAX197_SENSOR_DEVICE_ATTR_CH(6);
+MAX197_SENSOR_DEVICE_ATTR_CH(7);
+
+static const struct attribute_group max197_sysfs_group = {
+ .attrs = (struct attribute *[]) {
+ &dev_attr_name.attr,
+ MAX197_SENSOR_DEV_ATTR_IN(0),
+ MAX197_SENSOR_DEV_ATTR_IN(1),
+ MAX197_SENSOR_DEV_ATTR_IN(2),
+ MAX197_SENSOR_DEV_ATTR_IN(3),
+ MAX197_SENSOR_DEV_ATTR_IN(4),
+ MAX197_SENSOR_DEV_ATTR_IN(5),
+ MAX197_SENSOR_DEV_ATTR_IN(6),
+ MAX197_SENSOR_DEV_ATTR_IN(7),
+ NULL
+ },
+};
+
+static int __devinit max197_probe(struct platform_device *pdev)
+{
+ int ch, ret;
+ struct max197_data *data;
+ struct max197_platform_data *pdata = pdev->dev.platform_data;
+ enum max197_chips chip = platform_get_device_id(pdev)->driver_data;
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "no platform data supplied\n");
+ return -EINVAL;
+ }
+
+ if (pdata->convert == NULL) {
+ dev_err(&pdev->dev, "no convert function supplied\n");
+ return -EINVAL;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(struct max197_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&pdev->dev, "devm_kzalloc failed\n");
+ return -ENOMEM;
+ }
+
+ data->pdata = pdata;
+ mutex_init(&data->lock);
+
+ if (chip == max197) {
+ data->limit = MAX197_LIMIT;
+ data->scale = true;
+ } else {
+ data->limit = MAX199_LIMIT;
+ data->scale = false;
+ }
+
+ for (ch = 0; ch < MAX197_NUM_CH; ch++)
+ data->ctrl_bytes[ch] = (u8) ch;
+
+ platform_set_drvdata(pdev, data);
+
+ ret = sysfs_create_group(&pdev->dev.kobj, &max197_sysfs_group);
+ if (ret) {
+ dev_err(&pdev->dev, "sysfs create group failed\n");
+ return ret;
+ }
+
+ data->hwmon_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->hwmon_dev)) {
+ ret = PTR_ERR(data->hwmon_dev);
+ dev_err(&pdev->dev, "hwmon device register failed\n");
+ goto error;
+ }
+
+ return 0;
+
+error:
+ sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group);
+ return ret;
+}
+
+static int __devexit max197_remove(struct platform_device *pdev)
+{
+ struct max197_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->hwmon_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &max197_sysfs_group);
+
+ return 0;
+}
+
+static struct platform_device_id max197_device_ids[] = {
+ { "max197", max197 },
+ { "max199", max199 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, max197_device_ids);
+
+static struct platform_driver max197_driver = {
+ .driver = {
+ .name = "max197",
+ .owner = THIS_MODULE,
+ },
+ .probe = max197_probe,
+ .remove = __devexit_p(max197_remove),
+ .id_table = max197_device_ids,
+};
+module_platform_driver(max197_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Savoir-faire Linux Inc. <kernel@savoirfairelinux.com>");
+MODULE_DESCRIPTION("Maxim MAX197 A/D Converter driver");
diff --git a/drivers/hwmon/mcp3021.c b/drivers/hwmon/mcp3021.c
index d0afc0cd3ff4..eedb32292d6d 100644
--- a/drivers/hwmon/mcp3021.c
+++ b/drivers/hwmon/mcp3021.c
@@ -1,8 +1,9 @@
/*
- * mcp3021.c - driver for the Microchip MCP3021 chip
+ * mcp3021.c - driver for Microchip MCP3021 and MCP3221
*
* Copyright (C) 2008-2009, 2012 Freescale Semiconductor, Inc.
* Author: Mingkai Hu <Mingkai.hu@freescale.com>
+ * Reworked by Sven Schuchmann <schuchmann@schleissheimer.de>
*
* This driver export the value of analog input voltage to sysfs, the
* voltage unit is mV. Through the sysfs interface, lm-sensors tool
@@ -34,16 +35,31 @@
#define MCP3021_OUTPUT_RES 10 /* 10-bit resolution */
#define MCP3021_OUTPUT_SCALE 4
+#define MCP3221_SAR_SHIFT 0
+#define MCP3221_SAR_MASK 0xfff
+#define MCP3221_OUTPUT_RES 12 /* 12-bit resolution */
+#define MCP3221_OUTPUT_SCALE 1
+
+enum chips {
+ mcp3021,
+ mcp3221
+};
+
/*
* Client data (each client gets its own)
*/
struct mcp3021_data {
struct device *hwmon_dev;
u32 vdd; /* device power supply */
+ u16 sar_shift;
+ u16 sar_mask;
+ u8 output_res;
+ u8 output_scale;
};
static int mcp3021_read16(struct i2c_client *client)
{
+ struct mcp3021_data *data = i2c_get_clientdata(client);
int ret;
u16 reg;
__be16 buf;
@@ -61,20 +77,20 @@ static int mcp3021_read16(struct i2c_client *client)
* The ten-bit output code is composed of the lower 4-bit of the
* first byte and the upper 6-bit of the second byte.
*/
- reg = (reg >> MCP3021_SAR_SHIFT) & MCP3021_SAR_MASK;
+ reg = (reg >> data->sar_shift) & data->sar_mask;
return reg;
}
-static inline u16 volts_from_reg(u16 vdd, u16 val)
+static inline u16 volts_from_reg(struct mcp3021_data *data, u16 val)
{
if (val == 0)
return 0;
- val = val * MCP3021_OUTPUT_SCALE - MCP3021_OUTPUT_SCALE / 2;
+ val = val * data->output_scale - data->output_scale / 2;
- return val * DIV_ROUND_CLOSEST(vdd,
- (1 << MCP3021_OUTPUT_RES) * MCP3021_OUTPUT_SCALE);
+ return val * DIV_ROUND_CLOSEST(data->vdd,
+ (1 << data->output_res) * data->output_scale);
}
static ssize_t show_in_input(struct device *dev, struct device_attribute *attr,
@@ -88,7 +104,8 @@ static ssize_t show_in_input(struct device *dev, struct device_attribute *attr,
if (reg < 0)
return reg;
- in_input = volts_from_reg(data->vdd, reg);
+ in_input = volts_from_reg(data, reg);
+
return sprintf(buf, "%d\n", in_input);
}
@@ -103,25 +120,39 @@ static int mcp3021_probe(struct i2c_client *client,
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
- data = kzalloc(sizeof(struct mcp3021_data), GFP_KERNEL);
+ data = devm_kzalloc(&client->dev, sizeof(struct mcp3021_data),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
i2c_set_clientdata(client, data);
+ switch (id->driver_data) {
+ case mcp3021:
+ data->sar_shift = MCP3021_SAR_SHIFT;
+ data->sar_mask = MCP3021_SAR_MASK;
+ data->output_res = MCP3021_OUTPUT_RES;
+ data->output_scale = MCP3021_OUTPUT_SCALE;
+ break;
+
+ case mcp3221:
+ data->sar_shift = MCP3221_SAR_SHIFT;
+ data->sar_mask = MCP3221_SAR_MASK;
+ data->output_res = MCP3221_OUTPUT_RES;
+ data->output_scale = MCP3221_OUTPUT_SCALE;
+ break;
+ }
+
if (client->dev.platform_data) {
data->vdd = *(u32 *)client->dev.platform_data;
- if (data->vdd > MCP3021_VDD_MAX ||
- data->vdd < MCP3021_VDD_MIN) {
- err = -EINVAL;
- goto exit_free;
- }
+ if (data->vdd > MCP3021_VDD_MAX || data->vdd < MCP3021_VDD_MIN)
+ return -EINVAL;
} else
data->vdd = MCP3021_VDD_REF;
err = sysfs_create_file(&client->dev.kobj, &dev_attr_in0_input.attr);
if (err)
- goto exit_free;
+ return err;
data->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(data->hwmon_dev)) {
@@ -133,8 +164,6 @@ static int mcp3021_probe(struct i2c_client *client,
exit_remove:
sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
-exit_free:
- kfree(data);
return err;
}
@@ -144,13 +173,13 @@ static int mcp3021_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_file(&client->dev.kobj, &dev_attr_in0_input.attr);
- kfree(data);
return 0;
}
static const struct i2c_device_id mcp3021_id[] = {
- { "mcp3021", 0 },
+ { "mcp3021", mcp3021 },
+ { "mcp3221", mcp3221 },
{ }
};
MODULE_DEVICE_TABLE(i2c, mcp3021_id);
@@ -167,5 +196,5 @@ static struct i2c_driver mcp3021_driver = {
module_i2c_driver(mcp3021_driver);
MODULE_AUTHOR("Mingkai Hu <Mingkai.hu@freescale.com>");
-MODULE_DESCRIPTION("Microchip MCP3021 driver");
+MODULE_DESCRIPTION("Microchip MCP3021/MCP3221 driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/hwmon/sch5627.c b/drivers/hwmon/sch5627.c
index 8342275378b8..49f6230bdcf1 100644
--- a/drivers/hwmon/sch5627.c
+++ b/drivers/hwmon/sch5627.c
@@ -461,8 +461,6 @@ static int sch5627_remove(struct platform_device *pdev)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&pdev->dev.kobj, &sch5627_group);
- platform_set_drvdata(pdev, NULL);
- kfree(data);
return 0;
}
@@ -472,7 +470,8 @@ static int __devinit sch5627_probe(struct platform_device *pdev)
struct sch5627_data *data;
int err, build_code, build_id, hwmon_rev, val;
- data = kzalloc(sizeof(struct sch5627_data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(struct sch5627_data),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
diff --git a/drivers/hwmon/sch5636.c b/drivers/hwmon/sch5636.c
index 96a7e68718ca..517118016192 100644
--- a/drivers/hwmon/sch5636.c
+++ b/drivers/hwmon/sch5636.c
@@ -402,9 +402,6 @@ static int sch5636_remove(struct platform_device *pdev)
device_remove_file(&pdev->dev,
&sch5636_fan_attr[i].dev_attr);
- platform_set_drvdata(pdev, NULL);
- kfree(data);
-
return 0;
}
@@ -414,7 +411,8 @@ static int __devinit sch5636_probe(struct platform_device *pdev)
int i, err, val, revision[2];
char id[4];
- data = kzalloc(sizeof(struct sch5636_data), GFP_KERNEL);
+ data = devm_kzalloc(&pdev->dev, sizeof(struct sch5636_data),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
diff --git a/drivers/hwmon/sch56xx-common.c b/drivers/hwmon/sch56xx-common.c
index 4380f5d07be2..d00b30adc34b 100644
--- a/drivers/hwmon/sch56xx-common.c
+++ b/drivers/hwmon/sch56xx-common.c
@@ -503,10 +503,10 @@ EXPORT_SYMBOL(sch56xx_watchdog_unregister);
* platform dev find, add and remove functions
*/
-static int __init sch56xx_find(int sioaddr, unsigned short *address,
- const char **name)
+static int __init sch56xx_find(int sioaddr, const char **name)
{
u8 devid;
+ unsigned short address;
int err;
err = superio_enter(sioaddr);
@@ -540,20 +540,21 @@ static int __init sch56xx_find(int sioaddr, unsigned short *address,
* Warning the order of the low / high byte is the other way around
* as on most other superio devices!!
*/
- *address = superio_inb(sioaddr, SIO_REG_ADDR) |
+ address = superio_inb(sioaddr, SIO_REG_ADDR) |
superio_inb(sioaddr, SIO_REG_ADDR + 1) << 8;
- if (*address == 0) {
+ if (address == 0) {
pr_warn("Base address not set\n");
err = -ENODEV;
goto exit;
}
+ err = address;
exit:
superio_exit(sioaddr);
return err;
}
-static int __init sch56xx_device_add(unsigned short address, const char *name)
+static int __init sch56xx_device_add(int address, const char *name)
{
struct resource res = {
.start = address,
@@ -593,15 +594,14 @@ exit_device_put:
static int __init sch56xx_init(void)
{
- int err;
- unsigned short address;
- const char *name;
-
- err = sch56xx_find(0x4e, &address, &name);
- if (err)
- err = sch56xx_find(0x2e, &address, &name);
- if (err)
- return err;
+ int address;
+ const char *name = NULL;
+
+ address = sch56xx_find(0x4e, &name);
+ if (address < 0)
+ address = sch56xx_find(0x2e, &name);
+ if (address < 0)
+ return address;
return sch56xx_device_add(address, name);
}
diff --git a/drivers/hwmon/sht15.c b/drivers/hwmon/sht15.c
index 8b011d016621..07a0c1a0b84d 100644
--- a/drivers/hwmon/sht15.c
+++ b/drivers/hwmon/sht15.c
@@ -1,7 +1,7 @@
/*
* sht15.c - support for the SHT15 Temperature and Humidity Sensor
*
- * Portions Copyright (c) 2010-2011 Savoir-faire Linux Inc.
+ * Portions Copyright (c) 2010-2012 Savoir-faire Linux Inc.
* Jerome Oufella <jerome.oufella@savoirfairelinux.com>
* Vivien Didelot <vivien.didelot@savoirfairelinux.com>
*
@@ -24,12 +24,12 @@
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <linux/mutex.h>
+#include <linux/platform_data/sht15.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/err.h>
-#include <linux/sht15.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/atomic.h>
@@ -53,6 +53,9 @@
#define SHT15_STATUS_HEATER 0x04
#define SHT15_STATUS_LOW_BATTERY 0x40
+/* List of supported chips */
+enum sht15_chips { sht10, sht11, sht15, sht71, sht75 };
+
/* Actions the driver may be doing */
enum sht15_state {
SHT15_READING_NOTHING,
@@ -884,14 +887,12 @@ static int sht15_invalidate_voltage(struct notifier_block *nb,
static int __devinit sht15_probe(struct platform_device *pdev)
{
int ret;
- struct sht15_data *data = kzalloc(sizeof(*data), GFP_KERNEL);
+ struct sht15_data *data;
u8 status = 0;
- if (!data) {
- ret = -ENOMEM;
- dev_err(&pdev->dev, "kzalloc failed\n");
- goto error_ret;
- }
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
INIT_WORK(&data->read_work, sht15_bh_read_data);
INIT_WORK(&data->update_supply_work, sht15_update_voltage);
@@ -901,9 +902,8 @@ static int __devinit sht15_probe(struct platform_device *pdev)
init_waitqueue_head(&data->wait_queue);
if (pdev->dev.platform_data == NULL) {
- ret = -EINVAL;
dev_err(&pdev->dev, "no platform data supplied\n");
- goto err_free_data;
+ return -EINVAL;
}
data->pdata = pdev->dev.platform_data;
data->supply_uV = data->pdata->supply_mv * 1000;
@@ -918,7 +918,7 @@ static int __devinit sht15_probe(struct platform_device *pdev)
* If a regulator is available,
* query what the supply voltage actually is!
*/
- data->reg = regulator_get(data->dev, "vcc");
+ data->reg = devm_regulator_get(data->dev, "vcc");
if (!IS_ERR(data->reg)) {
int voltage;
@@ -937,51 +937,51 @@ static int __devinit sht15_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"regulator notifier request failed\n");
regulator_disable(data->reg);
- regulator_put(data->reg);
- goto err_free_data;
+ return ret;
}
}
/* Try requesting the GPIOs */
- ret = gpio_request(data->pdata->gpio_sck, "SHT15 sck");
+ ret = devm_gpio_request(&pdev->dev, data->pdata->gpio_sck, "SHT15 sck");
if (ret) {
dev_err(&pdev->dev, "gpio request failed\n");
goto err_release_reg;
}
gpio_direction_output(data->pdata->gpio_sck, 0);
- ret = gpio_request(data->pdata->gpio_data, "SHT15 data");
+ ret = devm_gpio_request(&pdev->dev, data->pdata->gpio_data,
+ "SHT15 data");
if (ret) {
dev_err(&pdev->dev, "gpio request failed\n");
- goto err_release_gpio_sck;
+ goto err_release_reg;
}
- ret = request_irq(gpio_to_irq(data->pdata->gpio_data),
- sht15_interrupt_fired,
- IRQF_TRIGGER_FALLING,
- "sht15 data",
- data);
+ ret = devm_request_irq(&pdev->dev, gpio_to_irq(data->pdata->gpio_data),
+ sht15_interrupt_fired,
+ IRQF_TRIGGER_FALLING,
+ "sht15 data",
+ data);
if (ret) {
dev_err(&pdev->dev, "failed to get irq for data line\n");
- goto err_release_gpio_data;
+ goto err_release_reg;
}
disable_irq_nosync(gpio_to_irq(data->pdata->gpio_data));
sht15_connection_reset(data);
ret = sht15_soft_reset(data);
if (ret)
- goto err_release_irq;
+ goto err_release_reg;
/* write status with platform data options */
if (status) {
ret = sht15_send_status(data, status);
if (ret)
- goto err_release_irq;
+ goto err_release_reg;
}
ret = sysfs_create_group(&pdev->dev.kobj, &sht15_attr_group);
if (ret) {
dev_err(&pdev->dev, "sysfs create failed\n");
- goto err_release_irq;
+ goto err_release_reg;
}
data->hwmon_dev = hwmon_device_register(data->dev);
@@ -994,21 +994,11 @@ static int __devinit sht15_probe(struct platform_device *pdev)
err_release_sysfs_group:
sysfs_remove_group(&pdev->dev.kobj, &sht15_attr_group);
-err_release_irq:
- free_irq(gpio_to_irq(data->pdata->gpio_data), data);
-err_release_gpio_data:
- gpio_free(data->pdata->gpio_data);
-err_release_gpio_sck:
- gpio_free(data->pdata->gpio_sck);
err_release_reg:
if (!IS_ERR(data->reg)) {
regulator_unregister_notifier(data->reg, &data->nb);
regulator_disable(data->reg);
- regulator_put(data->reg);
}
-err_free_data:
- kfree(data);
-error_ret:
return ret;
}
@@ -1030,89 +1020,33 @@ static int __devexit sht15_remove(struct platform_device *pdev)
if (!IS_ERR(data->reg)) {
regulator_unregister_notifier(data->reg, &data->nb);
regulator_disable(data->reg);
- regulator_put(data->reg);
}
- free_irq(gpio_to_irq(data->pdata->gpio_data), data);
- gpio_free(data->pdata->gpio_data);
- gpio_free(data->pdata->gpio_sck);
mutex_unlock(&data->read_lock);
- kfree(data);
return 0;
}
-/*
- * sht_drivers simultaneously refers to __devinit and __devexit function
- * which causes spurious section mismatch warning. So use __refdata to
- * get rid from this.
- */
-static struct platform_driver __refdata sht_drivers[] = {
- {
- .driver = {
- .name = "sht10",
- .owner = THIS_MODULE,
- },
- .probe = sht15_probe,
- .remove = __devexit_p(sht15_remove),
- }, {
- .driver = {
- .name = "sht11",
- .owner = THIS_MODULE,
- },
- .probe = sht15_probe,
- .remove = __devexit_p(sht15_remove),
- }, {
- .driver = {
- .name = "sht15",
- .owner = THIS_MODULE,
- },
- .probe = sht15_probe,
- .remove = __devexit_p(sht15_remove),
- }, {
- .driver = {
- .name = "sht71",
- .owner = THIS_MODULE,
- },
- .probe = sht15_probe,
- .remove = __devexit_p(sht15_remove),
- }, {
- .driver = {
- .name = "sht75",
- .owner = THIS_MODULE,
- },
- .probe = sht15_probe,
- .remove = __devexit_p(sht15_remove),
- },
+static struct platform_device_id sht15_device_ids[] = {
+ { "sht10", sht10 },
+ { "sht11", sht11 },
+ { "sht15", sht15 },
+ { "sht71", sht71 },
+ { "sht75", sht75 },
+ { }
};
+MODULE_DEVICE_TABLE(platform, sht15_device_ids);
-static int __init sht15_init(void)
-{
- int ret;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(sht_drivers); i++) {
- ret = platform_driver_register(&sht_drivers[i]);
- if (ret)
- goto error_unreg;
- }
-
- return 0;
-
-error_unreg:
- while (--i >= 0)
- platform_driver_unregister(&sht_drivers[i]);
-
- return ret;
-}
-module_init(sht15_init);
-
-static void __exit sht15_exit(void)
-{
- int i;
- for (i = ARRAY_SIZE(sht_drivers) - 1; i >= 0; i--)
- platform_driver_unregister(&sht_drivers[i]);
-}
-module_exit(sht15_exit);
+static struct platform_driver sht15_driver = {
+ .driver = {
+ .name = "sht15",
+ .owner = THIS_MODULE,
+ },
+ .probe = sht15_probe,
+ .remove = __devexit_p(sht15_remove),
+ .id_table = sht15_device_ids,
+};
+module_platform_driver(sht15_driver);
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Sensirion SHT15 temperature and humidity sensor driver");
diff --git a/drivers/hwmon/sht21.c b/drivers/hwmon/sht21.c
index 6c2dede4b8e7..c2565d04cd4a 100644
--- a/drivers/hwmon/sht21.c
+++ b/drivers/hwmon/sht21.c
@@ -199,11 +199,10 @@ static int __devinit sht21_probe(struct i2c_client *client,
return -ENODEV;
}
- sht21 = kzalloc(sizeof(*sht21), GFP_KERNEL);
- if (!sht21) {
- dev_dbg(&client->dev, "kzalloc failed\n");
+ sht21 = devm_kzalloc(&client->dev, sizeof(*sht21), GFP_KERNEL);
+ if (!sht21)
return -ENOMEM;
- }
+
i2c_set_clientdata(client, sht21);
mutex_init(&sht21->lock);
@@ -211,7 +210,7 @@ static int __devinit sht21_probe(struct i2c_client *client,
err = sysfs_create_group(&client->dev.kobj, &sht21_attr_group);
if (err) {
dev_dbg(&client->dev, "could not create sysfs files\n");
- goto fail_free;
+ return err;
}
sht21->hwmon_dev = hwmon_device_register(&client->dev);
if (IS_ERR(sht21->hwmon_dev)) {
@@ -226,9 +225,6 @@ static int __devinit sht21_probe(struct i2c_client *client,
fail_remove_sysfs:
sysfs_remove_group(&client->dev.kobj, &sht21_attr_group);
-fail_free:
- kfree(sht21);
-
return err;
}
@@ -242,7 +238,6 @@ static int __devexit sht21_remove(struct i2c_client *client)
hwmon_device_unregister(sht21->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &sht21_attr_group);
- kfree(sht21);
return 0;
}
diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
index ee4ebc198a94..4cddee04f2e6 100644
--- a/drivers/hwmon/via-cputemp.c
+++ b/drivers/hwmon/via-cputemp.c
@@ -128,12 +128,10 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev)
int err;
u32 eax, edx;
- data = kzalloc(sizeof(struct via_cputemp_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- dev_err(&pdev->dev, "Out of memory\n");
- goto exit;
- }
+ data = devm_kzalloc(&pdev->dev, sizeof(struct via_cputemp_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
data->id = pdev->id;
data->name = "via_cputemp";
@@ -151,8 +149,7 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev)
data->msr_temp = 0x1423;
break;
default:
- err = -ENODEV;
- goto exit_free;
+ return -ENODEV;
}
/* test if we can access the TEMPERATURE MSR */
@@ -160,14 +157,14 @@ static int __devinit via_cputemp_probe(struct platform_device *pdev)
if (err) {
dev_err(&pdev->dev,
"Unable to access TEMPERATURE MSR, giving up\n");
- goto exit_free;
+ return err;
}
platform_set_drvdata(pdev, data);
err = sysfs_create_group(&pdev->dev.kobj, &via_cputemp_group);
if (err)
- goto exit_free;
+ return err;
if (data->msr_vid)
data->vrm = vid_which_vrm();
@@ -192,10 +189,6 @@ exit_remove:
if (data->vrm)
device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
-exit_free:
- platform_set_drvdata(pdev, NULL);
- kfree(data);
-exit:
return err;
}
@@ -207,8 +200,6 @@ static int __devexit via_cputemp_remove(struct platform_device *pdev)
if (data->vrm)
device_remove_file(&pdev->dev, &dev_attr_cpu0_vid);
sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
- platform_set_drvdata(pdev, NULL);
- kfree(data);
return 0;
}
@@ -328,6 +319,7 @@ static int __init via_cputemp_init(void)
if (err)
goto exit;
+ get_online_cpus();
for_each_online_cpu(i) {
struct cpuinfo_x86 *c = &cpu_data(i);
@@ -347,12 +339,14 @@ static int __init via_cputemp_init(void)
#ifndef CONFIG_HOTPLUG_CPU
if (list_empty(&pdev_list)) {
+ put_online_cpus();
err = -ENODEV;
goto exit_driver_unreg;
}
#endif
register_hotcpu_notifier(&via_cputemp_cpu_notifier);
+ put_online_cpus();
return 0;
#ifndef CONFIG_HOTPLUG_CPU
@@ -367,6 +361,7 @@ static void __exit via_cputemp_exit(void)
{
struct pdev_entry *p, *n;
+ get_online_cpus();
unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
mutex_lock(&pdev_list_mutex);
list_for_each_entry_safe(p, n, &pdev_list, list) {
@@ -375,6 +370,7 @@ static void __exit via_cputemp_exit(void)
kfree(p);
}
mutex_unlock(&pdev_list_mutex);
+ put_online_cpus();
platform_driver_unregister(&via_cputemp_driver);
}
diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c
index 386a84538010..84e3dc5e3a83 100644
--- a/drivers/hwmon/vt8231.c
+++ b/drivers/hwmon/vt8231.c
@@ -789,18 +789,16 @@ static int vt8231_probe(struct platform_device *pdev)
/* Reserve the ISA region */
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
- if (!request_region(res->start, VT8231_EXTENT,
- vt8231_driver.driver.name)) {
+ if (!devm_request_region(&pdev->dev, res->start, VT8231_EXTENT,
+ vt8231_driver.driver.name)) {
dev_err(&pdev->dev, "Region 0x%lx-0x%lx already in use!\n",
(unsigned long)res->start, (unsigned long)res->end);
return -ENODEV;
}
- data = kzalloc(sizeof(struct vt8231_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit_release;
- }
+ data = devm_kzalloc(&pdev->dev, sizeof(struct vt8231_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
platform_set_drvdata(pdev, data);
data->addr = res->start;
@@ -812,7 +810,7 @@ static int vt8231_probe(struct platform_device *pdev)
/* Register sysfs hooks */
err = sysfs_create_group(&pdev->dev.kobj, &vt8231_group);
if (err)
- goto exit_free;
+ return err;
/* Must update device information to find out the config field */
data->uch_config = vt8231_read_value(data, VT8231_REG_UCH_CONFIG);
@@ -850,13 +848,6 @@ exit_remove_files:
sysfs_remove_group(&pdev->dev.kobj, &vt8231_group_temps[i]);
sysfs_remove_group(&pdev->dev.kobj, &vt8231_group);
-
-exit_free:
- platform_set_drvdata(pdev, NULL);
- kfree(data);
-
-exit_release:
- release_region(res->start, VT8231_EXTENT);
return err;
}
@@ -875,9 +866,6 @@ static int __devexit vt8231_remove(struct platform_device *pdev)
sysfs_remove_group(&pdev->dev.kobj, &vt8231_group);
- release_region(data->addr, VT8231_EXTENT);
- platform_set_drvdata(pdev, NULL);
- kfree(data);
return 0;
}
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
index 9ade4d4e2185..93ea81a4bf35 100644
--- a/drivers/hwmon/w83791d.c
+++ b/drivers/hwmon/w83791d.c
@@ -259,8 +259,7 @@ static u8 fan_to_reg(long rpm, int div)
((val) + 500) / 1000)
/* for thermal cruise temp tolerance, 4-bits, LSB = 1 degree Celsius */
-#define TOL_TEMP_TO_REG(val) ((val) < 0 ? 0 : \
- (val) >= 15000 ? 15 : \
+#define TOL_TEMP_TO_REG(val) ((val) >= 15000 ? 15 : \
((val) + 500) / 1000)
#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff)
@@ -848,10 +847,10 @@ static ssize_t store_temp_target(struct device *dev,
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
int nr = sensor_attr->index;
- unsigned long val;
+ long val;
u8 target_mask;
- if (kstrtoul(buf, 10, &val))
+ if (kstrtol(buf, 10, &val))
return -EINVAL;
mutex_lock(&data->update_lock);
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 0ba5a2bd562e..06d6f56d4f69 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -296,7 +296,6 @@ struct w83792d_data {
u8 pwmenable[3];
u32 alarms; /* realtime status register encoding,combined */
u8 chassis; /* Chassis status */
- u8 chassis_clear; /* CLR_CHS, clear chassis intrusion detection */
u8 thermal_cruise[3]; /* Smart FanI: Fan1,2,3 target value */
u8 tolerance[3]; /* Fan1,2,3 tolerance(Smart Fan I/II) */
u8 sf2_points[3][4]; /* Smart FanII: Fan1,2,3 temperature points */
@@ -739,7 +738,7 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
}
static ssize_t
-show_chassis(struct device *dev, struct device_attribute *attr,
+show_chassis_clear(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct w83792d_data *data = w83792d_update_device(dev);
@@ -747,52 +746,6 @@ show_chassis(struct device *dev, struct device_attribute *attr,
}
static ssize_t
-show_regs_chassis(struct device *dev, struct device_attribute *attr,
- char *buf)
-{
- dev_warn(dev,
- "Attribute %s is deprecated, use intrusion0_alarm instead\n",
- "chassis");
- return show_chassis(dev, attr, buf);
-}
-
-static ssize_t
-show_chassis_clear(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct w83792d_data *data = w83792d_update_device(dev);
- return sprintf(buf, "%d\n", data->chassis_clear);
-}
-
-static ssize_t
-store_chassis_clear_legacy(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t count)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct w83792d_data *data = i2c_get_clientdata(client);
- unsigned long val;
- int err;
- u8 temp1 = 0, temp2 = 0;
-
- dev_warn(dev,
- "Attribute %s is deprecated, use intrusion0_alarm instead\n",
- "chassis_clear");
-
- err = kstrtoul(buf, 10, &val);
- if (err)
- return err;
-
- mutex_lock(&data->update_lock);
- data->chassis_clear = SENSORS_LIMIT(val, 0, 1);
- temp1 = ((data->chassis_clear) << 7) & 0x80;
- temp2 = w83792d_read_value(client,
- W83792D_REG_CHASSIS_CLR) & 0x7f;
- w83792d_write_value(client, W83792D_REG_CHASSIS_CLR, temp1 | temp2);
- mutex_unlock(&data->update_lock);
-
- return count;
-}
-
-static ssize_t
store_chassis_clear(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
@@ -1116,11 +1069,8 @@ static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 20);
static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 21);
static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 22);
static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, 23);
-static DEVICE_ATTR(chassis, S_IRUGO, show_regs_chassis, NULL);
-static DEVICE_ATTR(chassis_clear, S_IRUGO | S_IWUSR,
- show_chassis_clear, store_chassis_clear_legacy);
static DEVICE_ATTR(intrusion0_alarm, S_IRUGO | S_IWUSR,
- show_chassis, store_chassis_clear);
+ show_chassis_clear, store_chassis_clear);
static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0);
static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1);
static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2);
@@ -1320,8 +1270,6 @@ static struct attribute *w83792d_attributes[] = {
&sensor_dev_attr_pwm3_mode.dev_attr.attr,
&sensor_dev_attr_pwm3_enable.dev_attr.attr,
&dev_attr_alarms.attr,
- &dev_attr_chassis.attr,
- &dev_attr_chassis_clear.attr,
&dev_attr_intrusion0_alarm.attr,
&sensor_dev_attr_tolerance1.dev_attr.attr,
&sensor_dev_attr_thermal_cruise1.dev_attr.attr,
@@ -1627,8 +1575,6 @@ static struct w83792d_data *w83792d_update_device(struct device *dev)
/* Update CaseOpen status and it's CLR_CHS. */
data->chassis = (w83792d_read_value(client,
W83792D_REG_CHASSIS) >> 5) & 0x01;
- data->chassis_clear = (w83792d_read_value(client,
- W83792D_REG_CHASSIS_CLR) >> 7) & 0x01;
/* Update Thermal Cruise/Smart Fan I target value */
for (i = 0; i < 3; i++) {
diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c
index d6b0bdd48651..4fc47e062071 100644
--- a/drivers/hwmon/w83793.c
+++ b/drivers/hwmon/w83793.c
@@ -442,27 +442,6 @@ store_beep_enable(struct device *dev, struct device_attribute *attr,
return count;
}
-/* Write any value to clear chassis alarm */
-static ssize_t
-store_chassis_clear_legacy(struct device *dev,
- struct device_attribute *attr, const char *buf,
- size_t count)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct w83793_data *data = i2c_get_clientdata(client);
- u8 val;
-
- dev_warn(dev, "Attribute chassis is deprecated, "
- "use intrusion0_alarm instead\n");
-
- mutex_lock(&data->update_lock);
- val = w83793_read_value(client, W83793_REG_CLR_CHASSIS);
- val |= 0x80;
- w83793_write_value(client, W83793_REG_CLR_CHASSIS, val);
- mutex_unlock(&data->update_lock);
- return count;
-}
-
/* Write 0 to clear chassis alarm */
static ssize_t
store_chassis_clear(struct device *dev,
@@ -1189,8 +1168,6 @@ static struct sensor_device_attribute_2 w83793_vid[] = {
static DEVICE_ATTR(vrm, S_IWUSR | S_IRUGO, show_vrm, store_vrm);
static struct sensor_device_attribute_2 sda_single_files[] = {
- SENSOR_ATTR_2(chassis, S_IWUSR | S_IRUGO, show_alarm_beep,
- store_chassis_clear_legacy, ALARM_STATUS, 30),
SENSOR_ATTR_2(intrusion0_alarm, S_IWUSR | S_IRUGO, show_alarm_beep,
store_chassis_clear, ALARM_STATUS, 30),
SENSOR_ATTR_2(beep_enable, S_IWUSR | S_IRUGO, show_beep_enable,
diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c
index 5850b7706088..c99c8a0473cf 100644
--- a/drivers/hwmon/w83l786ng.c
+++ b/drivers/hwmon/w83l786ng.c
@@ -668,11 +668,10 @@ w83l786ng_probe(struct i2c_client *client, const struct i2c_device_id *id)
int i, err = 0;
u8 reg_tmp;
- data = kzalloc(sizeof(struct w83l786ng_data), GFP_KERNEL);
- if (!data) {
- err = -ENOMEM;
- goto exit;
- }
+ data = devm_kzalloc(&client->dev, sizeof(struct w83l786ng_data),
+ GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
i2c_set_clientdata(client, data);
mutex_init(&data->update_lock);
@@ -708,8 +707,6 @@ w83l786ng_probe(struct i2c_client *client, const struct i2c_device_id *id)
exit_remove:
sysfs_remove_group(&client->dev.kobj, &w83l786ng_group);
- kfree(data);
-exit:
return err;
}
@@ -721,8 +718,6 @@ w83l786ng_remove(struct i2c_client *client)
hwmon_device_unregister(data->hwmon_dev);
sysfs_remove_group(&client->dev.kobj, &w83l786ng_group);
- kfree(data);
-
return 0;
}
diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c
index 1201a15784c3..db713c0dfba4 100644
--- a/drivers/hwspinlock/hwspinlock_core.c
+++ b/drivers/hwspinlock/hwspinlock_core.c
@@ -552,7 +552,7 @@ EXPORT_SYMBOL_GPL(hwspin_lock_request_specific);
*/
int hwspin_lock_free(struct hwspinlock *hwlock)
{
- struct device *dev = hwlock->bank->dev;
+ struct device *dev;
struct hwspinlock *tmp;
int ret;
@@ -561,6 +561,7 @@ int hwspin_lock_free(struct hwspinlock *hwlock)
return -EINVAL;
}
+ dev = hwlock->bank->dev;
mutex_lock(&hwspinlock_tree_lock);
/* make sure the hwspinlock is used */
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index b76731edbf10..57f7703ce2e8 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -647,7 +647,7 @@ static int __devinit fsl_i2c_probe(struct platform_device *op)
}
if (match->data) {
- struct mpc_i2c_data *data = match->data;
+ const struct mpc_i2c_data *data = match->data;
data->setup(op->dev.of_node, i2c, clock, data->prescaler);
} else {
/* Backwards compatibility */
@@ -730,24 +730,24 @@ static int mpc_i2c_resume(struct device *dev)
SIMPLE_DEV_PM_OPS(mpc_i2c_pm_ops, mpc_i2c_suspend, mpc_i2c_resume);
#endif
-static struct mpc_i2c_data mpc_i2c_data_512x __devinitdata = {
+static const struct mpc_i2c_data mpc_i2c_data_512x __devinitdata = {
.setup = mpc_i2c_setup_512x,
};
-static struct mpc_i2c_data mpc_i2c_data_52xx __devinitdata = {
+static const struct mpc_i2c_data mpc_i2c_data_52xx __devinitdata = {
.setup = mpc_i2c_setup_52xx,
};
-static struct mpc_i2c_data mpc_i2c_data_8313 __devinitdata = {
+static const struct mpc_i2c_data mpc_i2c_data_8313 __devinitdata = {
.setup = mpc_i2c_setup_8xxx,
};
-static struct mpc_i2c_data mpc_i2c_data_8543 __devinitdata = {
+static const struct mpc_i2c_data mpc_i2c_data_8543 __devinitdata = {
.setup = mpc_i2c_setup_8xxx,
.prescaler = 2,
};
-static struct mpc_i2c_data mpc_i2c_data_8544 __devinitdata = {
+static const struct mpc_i2c_data mpc_i2c_data_8544 __devinitdata = {
.setup = mpc_i2c_setup_8xxx,
.prescaler = 3,
};
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 5d19a49803c1..a0e49f6aaf96 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -944,7 +944,8 @@ omap_i2c_probe(struct platform_device *pdev)
struct omap_i2c_dev *dev;
struct i2c_adapter *adap;
struct resource *mem, *irq, *ioarea;
- struct omap_i2c_bus_platform_data *pdata = pdev->dev.platform_data;
+ const struct omap_i2c_bus_platform_data *pdata =
+ pdev->dev.platform_data;
struct device_node *node = pdev->dev.of_node;
const struct of_device_id *match;
irq_handler_t isr;
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 9a08c57bc936..f981ac4e6783 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -27,6 +27,7 @@
#include <linux/slab.h>
#include <linux/i2c-tegra.h>
#include <linux/of_i2c.h>
+#include <linux/of_device.h>
#include <linux/module.h>
#include <asm/unaligned.h>
@@ -114,11 +115,21 @@ enum msg_end_type {
};
/**
+ * struct tegra_i2c_hw_feature : Different HW support on Tegra
+ * @has_continue_xfer_support: Continue transfer supports.
+ */
+
+struct tegra_i2c_hw_feature {
+ bool has_continue_xfer_support;
+};
+
+/**
* struct tegra_i2c_dev - per device i2c context
* @dev: device reference for power management
+ * @hw: Tegra i2c hw feature.
* @adapter: core i2c layer adapter information
- * @clk: clock reference for i2c controller
- * @i2c_clk: clock reference for i2c bus
+ * @div_clk: clock reference for div clock of i2c controller.
+ * @fast_clk: clock reference for fast clock of i2c controller.
* @base: ioremapped registers cookie
* @cont_id: i2c controller id, used for for packet header
* @irq: irq number of transfer complete interrupt
@@ -133,9 +144,10 @@ enum msg_end_type {
*/
struct tegra_i2c_dev {
struct device *dev;
+ const struct tegra_i2c_hw_feature *hw;
struct i2c_adapter adapter;
- struct clk *clk;
- struct clk *i2c_clk;
+ struct clk *div_clk;
+ struct clk *fast_clk;
void __iomem *base;
int cont_id;
int irq;
@@ -351,16 +363,40 @@ static void tegra_dvc_init(struct tegra_i2c_dev *i2c_dev)
dvc_writel(i2c_dev, val, DVC_CTRL_REG1);
}
+static inline int tegra_i2c_clock_enable(struct tegra_i2c_dev *i2c_dev)
+{
+ int ret;
+ ret = clk_prepare_enable(i2c_dev->fast_clk);
+ if (ret < 0) {
+ dev_err(i2c_dev->dev,
+ "Enabling fast clk failed, err %d\n", ret);
+ return ret;
+ }
+ ret = clk_prepare_enable(i2c_dev->div_clk);
+ if (ret < 0) {
+ dev_err(i2c_dev->dev,
+ "Enabling div clk failed, err %d\n", ret);
+ clk_disable_unprepare(i2c_dev->fast_clk);
+ }
+ return ret;
+}
+
+static inline void tegra_i2c_clock_disable(struct tegra_i2c_dev *i2c_dev)
+{
+ clk_disable_unprepare(i2c_dev->div_clk);
+ clk_disable_unprepare(i2c_dev->fast_clk);
+}
+
static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
{
u32 val;
int err = 0;
- clk_prepare_enable(i2c_dev->clk);
+ tegra_i2c_clock_enable(i2c_dev);
- tegra_periph_reset_assert(i2c_dev->clk);
+ tegra_periph_reset_assert(i2c_dev->div_clk);
udelay(2);
- tegra_periph_reset_deassert(i2c_dev->clk);
+ tegra_periph_reset_deassert(i2c_dev->div_clk);
if (i2c_dev->is_dvc)
tegra_dvc_init(i2c_dev);
@@ -369,7 +405,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
(0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
i2c_writel(i2c_dev, val, I2C_CNFG);
i2c_writel(i2c_dev, 0, I2C_INT_MASK);
- clk_set_rate(i2c_dev->clk, i2c_dev->bus_clk_rate * 8);
+ clk_set_rate(i2c_dev->div_clk, i2c_dev->bus_clk_rate * 8);
if (!i2c_dev->is_dvc) {
u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG);
@@ -387,7 +423,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
if (tegra_i2c_flush_fifos(i2c_dev))
err = -ETIMEDOUT;
- clk_disable_unprepare(i2c_dev->clk);
+ tegra_i2c_clock_disable(i2c_dev);
if (i2c_dev->irq_disabled) {
i2c_dev->irq_disabled = 0;
@@ -563,7 +599,7 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
if (i2c_dev->is_suspended)
return -EBUSY;
- clk_prepare_enable(i2c_dev->clk);
+ tegra_i2c_clock_enable(i2c_dev);
for (i = 0; i < num; i++) {
enum msg_end_type end_type = MSG_END_STOP;
if (i < (num - 1)) {
@@ -576,14 +612,19 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
if (ret)
break;
}
- clk_disable_unprepare(i2c_dev->clk);
+ tegra_i2c_clock_disable(i2c_dev);
return ret ?: i;
}
static u32 tegra_i2c_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
- I2C_FUNC_PROTOCOL_MANGLING | I2C_FUNC_NOSTART;
+ struct tegra_i2c_dev *i2c_dev = i2c_get_adapdata(adap);
+ u32 ret = I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR |
+ I2C_FUNC_PROTOCOL_MANGLING;
+
+ if (i2c_dev->hw->has_continue_xfer_support)
+ ret |= I2C_FUNC_NOSTART;
+ return ret;
}
static const struct i2c_algorithm tegra_i2c_algo = {
@@ -591,13 +632,32 @@ static const struct i2c_algorithm tegra_i2c_algo = {
.functionality = tegra_i2c_func,
};
+static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
+ .has_continue_xfer_support = false,
+};
+
+static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
+ .has_continue_xfer_support = true,
+};
+
+#if defined(CONFIG_OF)
+/* Match table for of_platform binding */
+static const struct of_device_id tegra_i2c_of_match[] __devinitconst = {
+ { .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
+ { .compatible = "nvidia,tegra20-i2c", .data = &tegra20_i2c_hw, },
+ { .compatible = "nvidia,tegra20-i2c-dvc", .data = &tegra20_i2c_hw, },
+ {},
+};
+MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);
+#endif
+
static int __devinit tegra_i2c_probe(struct platform_device *pdev)
{
struct tegra_i2c_dev *i2c_dev;
struct tegra_i2c_platform_data *pdata = pdev->dev.platform_data;
struct resource *res;
- struct clk *clk;
- struct clk *i2c_clk;
+ struct clk *div_clk;
+ struct clk *fast_clk;
const unsigned int *prop;
void __iomem *base;
int irq;
@@ -622,16 +682,16 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
}
irq = res->start;
- clk = devm_clk_get(&pdev->dev, NULL);
- if (IS_ERR(clk)) {
+ div_clk = devm_clk_get(&pdev->dev, "div-clk");
+ if (IS_ERR(div_clk)) {
dev_err(&pdev->dev, "missing controller clock");
- return PTR_ERR(clk);
+ return PTR_ERR(div_clk);
}
- i2c_clk = devm_clk_get(&pdev->dev, "i2c");
- if (IS_ERR(i2c_clk)) {
+ fast_clk = devm_clk_get(&pdev->dev, "fast-clk");
+ if (IS_ERR(fast_clk)) {
dev_err(&pdev->dev, "missing bus clock");
- return PTR_ERR(i2c_clk);
+ return PTR_ERR(fast_clk);
}
i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
@@ -641,8 +701,8 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
}
i2c_dev->base = base;
- i2c_dev->clk = clk;
- i2c_dev->i2c_clk = i2c_clk;
+ i2c_dev->div_clk = div_clk;
+ i2c_dev->fast_clk = fast_clk;
i2c_dev->adapter.algo = &tegra_i2c_algo;
i2c_dev->irq = irq;
i2c_dev->cont_id = pdev->id;
@@ -659,11 +719,18 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
i2c_dev->bus_clk_rate = be32_to_cpup(prop);
}
- if (pdev->dev.of_node)
+ i2c_dev->hw = &tegra20_i2c_hw;
+
+ if (pdev->dev.of_node) {
+ const struct of_device_id *match;
+ match = of_match_device(of_match_ptr(tegra_i2c_of_match),
+ &pdev->dev);
+ i2c_dev->hw = match->data;
i2c_dev->is_dvc = of_device_is_compatible(pdev->dev.of_node,
"nvidia,tegra20-i2c-dvc");
- else if (pdev->id == 3)
+ } else if (pdev->id == 3) {
i2c_dev->is_dvc = 1;
+ }
init_completion(&i2c_dev->msg_complete);
platform_set_drvdata(pdev, i2c_dev);
@@ -681,8 +748,6 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
return ret;
}
- clk_prepare_enable(i2c_dev->i2c_clk);
-
i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
i2c_dev->adapter.owner = THIS_MODULE;
i2c_dev->adapter.class = I2C_CLASS_HWMON;
@@ -696,7 +761,6 @@ static int __devinit tegra_i2c_probe(struct platform_device *pdev)
ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
if (ret) {
dev_err(&pdev->dev, "Failed to add I2C adapter\n");
- clk_disable_unprepare(i2c_dev->i2c_clk);
return ret;
}
@@ -751,16 +815,6 @@ static SIMPLE_DEV_PM_OPS(tegra_i2c_pm, tegra_i2c_suspend, tegra_i2c_resume);
#define TEGRA_I2C_PM NULL
#endif
-#if defined(CONFIG_OF)
-/* Match table for of_platform binding */
-static const struct of_device_id tegra_i2c_of_match[] __devinitconst = {
- { .compatible = "nvidia,tegra20-i2c", },
- { .compatible = "nvidia,tegra20-i2c-dvc", },
- {},
-};
-MODULE_DEVICE_TABLE(of, tegra_i2c_of_match);
-#endif
-
static struct platform_driver tegra_i2c_driver = {
.probe = tegra_i2c_probe,
.remove = __devexit_p(tegra_i2c_remove),
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index d4984c8be973..6e3f143fc71d 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -1,5 +1,5 @@
#
-# Industrial I/O subsytem configuration
+# Industrial I/O subsystem configuration
#
menuconfig IIO
@@ -54,10 +54,15 @@ config IIO_CONSUMERS_PER_TRIGGER
This value controls the maximum number of consumers that a
given trigger may handle. Default is 2.
+source "drivers/iio/accel/Kconfig"
source "drivers/iio/adc/Kconfig"
source "drivers/iio/amplifiers/Kconfig"
source "drivers/iio/light/Kconfig"
source "drivers/iio/frequency/Kconfig"
source "drivers/iio/dac/Kconfig"
+source "drivers/iio/common/Kconfig"
+source "drivers/iio/gyro/Kconfig"
+source "drivers/iio/light/Kconfig"
+source "drivers/iio/magnetometer/Kconfig"
endif # IIO
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 34309abb7979..f7fa3c0867b4 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -10,8 +10,13 @@ industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
obj-$(CONFIG_IIO_TRIGGERED_BUFFER) += industrialio-triggered-buffer.o
obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
+obj-y += accel/
obj-y += adc/
obj-y += amplifiers/
obj-y += light/
obj-y += frequency/
obj-y += dac/
+obj-y += common/
+obj-y += gyro/
+obj-y += light/
+obj-y += magnetometer/
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
new file mode 100644
index 000000000000..b2510c4d9a5a
--- /dev/null
+++ b/drivers/iio/accel/Kconfig
@@ -0,0 +1,16 @@
+#
+# Accelerometer drivers
+#
+menu "Accelerometers"
+
+config HID_SENSOR_ACCEL_3D
+ depends on HID_SENSOR_HUB
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select HID_SENSOR_IIO_COMMON
+ tristate "HID Acelerometers 3D"
+ help
+ Say yes here to build support for the HID SENSOR
+ accelerometers 3D.
+
+endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
new file mode 100644
index 000000000000..5bc6855a973e
--- /dev/null
+++ b/drivers/iio/accel/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O accelerometer drivers
+#
+
+obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
new file mode 100644
index 000000000000..314a4057879e
--- /dev/null
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -0,0 +1,418 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-attributes.h"
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Accelerometer-3D: 0x200073*/
+#define DRIVER_NAME "HID-SENSOR-200073"
+
+enum accel_3d_channel {
+ CHANNEL_SCAN_INDEX_X,
+ CHANNEL_SCAN_INDEX_Y,
+ CHANNEL_SCAN_INDEX_Z,
+ ACCEL_3D_CHANNEL_MAX,
+};
+
+struct accel_3d_state {
+ struct hid_sensor_hub_callbacks callbacks;
+ struct hid_sensor_iio_common common_attributes;
+ struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX];
+ u32 accel_val[ACCEL_3D_CHANNEL_MAX];
+};
+
+static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = {
+ HID_USAGE_SENSOR_ACCEL_X_AXIS,
+ HID_USAGE_SENSOR_ACCEL_Y_AXIS,
+ HID_USAGE_SENSOR_ACCEL_Z_AXIS
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec accel_3d_channels[] = {
+ {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_X,
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_Y,
+ }, {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_Z,
+ }
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void accel_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+ int channel, int size)
+{
+ channels[channel].scan_type.sign = 's';
+ /* Real storage bits will change based on the report desc. */
+ channels[channel].scan_type.realbits = size * 8;
+ /* Maximum size of a sample to capture is u32 */
+ channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int accel_3d_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct accel_3d_state *accel_state = iio_priv(indio_dev);
+ int report_id = -1;
+ u32 address;
+ int ret;
+ int ret_type;
+
+ *val = 0;
+ *val2 = 0;
+ switch (mask) {
+ case 0:
+ report_id = accel_state->accel[chan->scan_index].report_id;
+ address = accel_3d_addresses[chan->scan_index];
+ if (report_id >= 0)
+ *val = sensor_hub_input_attr_get_raw_value(
+ accel_state->common_attributes.hsdev,
+ HID_USAGE_SENSOR_ACCEL_3D, address,
+ report_id);
+ else {
+ *val = 0;
+ return -EINVAL;
+ }
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = accel_state->accel[CHANNEL_SCAN_INDEX_X].units;
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = hid_sensor_convert_exponent(
+ accel_state->accel[CHANNEL_SCAN_INDEX_X].unit_expo);
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_read_samp_freq_value(
+ &accel_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_read_raw_hyst_value(
+ &accel_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret_type = -EINVAL;
+ break;
+ }
+
+ return ret_type;
+}
+
+/* Channel write_raw handler */
+static int accel_3d_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct accel_3d_state *accel_state = iio_priv(indio_dev);
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_write_samp_freq_value(
+ &accel_state->common_attributes, val, val2);
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_write_raw_hyst_value(
+ &accel_state->common_attributes, val, val2);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int accel_3d_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static const struct iio_info accel_3d_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &accel_3d_read_raw,
+ .write_raw = &accel_3d_write_raw,
+ .write_raw_get_fmt = &accel_3d_write_raw_get_fmt,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
+{
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int datum_sz;
+
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ if (!buffer) {
+ dev_err(&indio_dev->dev, "Buffer == NULL\n");
+ return;
+ }
+ datum_sz = buffer->access->get_bytes_per_datum(buffer);
+ if (len > datum_sz) {
+ dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+ datum_sz);
+ return;
+ }
+ iio_push_to_buffer(buffer, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct accel_3d_state *accel_state = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "accel_3d_proc_event [%d]\n",
+ accel_state->common_attributes.data_ready);
+ if (accel_state->common_attributes.data_ready)
+ hid_sensor_push_data(indio_dev,
+ (u8 *)accel_state->accel_val,
+ sizeof(accel_state->accel_val));
+
+ return 0;
+}
+
+/* Capture samples in local storage */
+static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct accel_3d_state *accel_state = iio_priv(indio_dev);
+ int offset;
+ int ret = -EINVAL;
+
+ switch (usage_id) {
+ case HID_USAGE_SENSOR_ACCEL_X_AXIS:
+ case HID_USAGE_SENSOR_ACCEL_Y_AXIS:
+ case HID_USAGE_SENSOR_ACCEL_Z_AXIS:
+ offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS;
+ accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] =
+ *(u32 *)raw_data;
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int accel_3d_parse_report(struct platform_device *pdev,
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ unsigned usage_id,
+ struct accel_3d_state *st)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
+ ret = sensor_hub_input_get_attribute_info(hsdev,
+ HID_INPUT_REPORT,
+ usage_id,
+ HID_USAGE_SENSOR_ACCEL_X_AXIS + i,
+ &st->accel[CHANNEL_SCAN_INDEX_X + i]);
+ if (ret < 0)
+ break;
+ accel_3d_adjust_channel_bit_mask(channels,
+ CHANNEL_SCAN_INDEX_X + i,
+ st->accel[CHANNEL_SCAN_INDEX_X + i].size);
+ }
+ dev_dbg(&pdev->dev, "accel_3d %x:%x, %x:%x, %x:%x\n",
+ st->accel[0].index,
+ st->accel[0].report_id,
+ st->accel[1].index, st->accel[1].report_id,
+ st->accel[2].index, st->accel[2].report_id);
+
+ return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int __devinit hid_accel_3d_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ static const char *name = "accel_3d";
+ struct iio_dev *indio_dev;
+ struct accel_3d_state *accel_state;
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_chan_spec *channels;
+
+ indio_dev = iio_device_alloc(sizeof(struct accel_3d_state));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ platform_set_drvdata(pdev, indio_dev);
+
+ accel_state = iio_priv(indio_dev);
+ accel_state->common_attributes.hsdev = hsdev;
+ accel_state->common_attributes.pdev = pdev;
+
+ ret = hid_sensor_parse_common_attributes(hsdev,
+ HID_USAGE_SENSOR_ACCEL_3D,
+ &accel_state->common_attributes);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup common attributes\n");
+ goto error_free_dev;
+ }
+
+ channels = kmemdup(accel_3d_channels,
+ sizeof(accel_3d_channels),
+ GFP_KERNEL);
+ if (!channels) {
+ dev_err(&pdev->dev, "failed to duplicate channels\n");
+ goto error_free_dev;
+ }
+
+ ret = accel_3d_parse_report(pdev, hsdev, channels,
+ HID_USAGE_SENSOR_ACCEL_3D, accel_state);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup attributes\n");
+ goto error_free_dev_mem;
+ }
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels = ARRAY_SIZE(accel_3d_channels);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &accel_3d_info;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ NULL, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+ goto error_free_dev_mem;
+ }
+ accel_state->common_attributes.data_ready = false;
+ ret = hid_sensor_setup_trigger(indio_dev, name,
+ &accel_state->common_attributes);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "trigger setup failed\n");
+ goto error_unreg_buffer_funcs;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_trigger;
+ }
+
+ accel_state->callbacks.send_event = accel_3d_proc_event;
+ accel_state->callbacks.capture_sample = accel_3d_capture_sample;
+ accel_state->callbacks.pdev = pdev;
+ ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D,
+ &accel_state->callbacks);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "callback reg failed\n");
+ goto error_iio_unreg;
+ }
+
+ return ret;
+
+error_iio_unreg:
+ iio_device_unregister(indio_dev);
+error_remove_trigger:
+ hid_sensor_remove_trigger(indio_dev);
+error_unreg_buffer_funcs:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_free_dev_mem:
+ kfree(indio_dev->channels);
+error_free_dev:
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int __devinit hid_accel_3d_remove(struct platform_device *pdev)
+{
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ACCEL_3D);
+ iio_device_unregister(indio_dev);
+ hid_sensor_remove_trigger(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ kfree(indio_dev->channels);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver hid_accel_3d_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = hid_accel_3d_probe,
+ .remove = hid_accel_3d_remove,
+};
+module_platform_driver(hid_accel_3d_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Accel 3D");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 8a78b4f3ef58..492758120338 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -3,6 +3,11 @@
#
menu "Analog to digital converters"
+config AD_SIGMA_DELTA
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
config AD7266
tristate "Analog Devices AD7265/AD7266 ADC driver"
depends on SPI_MASTER
@@ -13,6 +18,33 @@ config AD7266
Say yes here to build support for Analog Devices AD7265 and AD7266
ADCs.
+config AD7791
+ tristate "Analog Devices AD7791 ADC driver"
+ depends on SPI
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7787, AD7788, AD7789,
+ AD7790 and AD7791 SPI analog to digital converters (ADC). If unsure, say
+ N (but it is safe to say "Y").
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7791.
+
+config AD7476
+ tristate "Analog Devices AD7476 and similar 1-channel ADCs driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD7273, AD7274, AD7276,
+ AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
+ AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC).
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7476.
+
config AT91_ADC
tristate "Atmel AT91 ADC"
depends on ARCH_AT91
@@ -22,4 +54,10 @@ config AT91_ADC
help
Say yes here to build support for Atmel AT91 ADC.
+config LP8788_ADC
+ bool "LP8788 ADC driver"
+ depends on MFD_LP8788
+ help
+ Say yes here to build support for TI LP8788 ADC.
+
endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 52eec254c38c..900995d5e179 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -2,5 +2,9 @@
# Makefile for IIO ADC drivers
#
+obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
obj-$(CONFIG_AD7266) += ad7266.o
+obj-$(CONFIG_AD7476) += ad7476.o
+obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
+obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
index 5c3f1ba5a06d..b11f214779a2 100644
--- a/drivers/iio/adc/ad7266.c
+++ b/drivers/iio/adc/ad7266.c
@@ -99,7 +99,7 @@ static irqreturn_t ad7266_trigger_handler(int irq, void *p)
if (ret == 0) {
if (indio_dev->scan_timestamp)
((s64 *)st->data)[1] = pf->timestamp;
- iio_push_to_buffer(buffer, (u8 *)st->data, pf->timestamp);
+ iio_push_to_buffer(buffer, (u8 *)st->data);
}
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/staging/iio/adc/ad7476_core.c b/drivers/iio/adc/ad7476.c
index 4d30a798ba0d..7f2f45a0a48d 100644
--- a/drivers/staging/iio/adc/ad7476_core.c
+++ b/drivers/iio/adc/ad7476.c
@@ -18,8 +18,76 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
-#include "ad7476.h"
+#define RES_MASK(bits) ((1 << (bits)) - 1)
+
+struct ad7476_state;
+
+struct ad7476_chip_info {
+ unsigned int int_vref_uv;
+ struct iio_chan_spec channel[2];
+ void (*reset)(struct ad7476_state *);
+};
+
+struct ad7476_state {
+ struct spi_device *spi;
+ const struct ad7476_chip_info *chip_info;
+ struct regulator *reg;
+ struct spi_transfer xfer;
+ struct spi_message msg;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ * Make the buffer large enough for one 16 bit sample and one 64 bit
+ * aligned 64 bit timestamp.
+ */
+ unsigned char data[ALIGN(2, sizeof(s64)) + sizeof(s64)]
+ ____cacheline_aligned;
+};
+
+enum ad7476_supported_device_ids {
+ ID_AD7091R,
+ ID_AD7276,
+ ID_AD7277,
+ ID_AD7278,
+ ID_AD7466,
+ ID_AD7467,
+ ID_AD7468,
+ ID_AD7495,
+ ID_AD7940,
+};
+
+static irqreturn_t ad7476_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7476_state *st = iio_priv(indio_dev);
+ s64 time_ns;
+ int b_sent;
+
+ b_sent = spi_sync(st->spi, &st->msg);
+ if (b_sent < 0)
+ goto done;
+
+ time_ns = iio_get_time_ns();
+
+ if (indio_dev->scan_timestamp)
+ ((s64 *)st->data)[1] = time_ns;
+
+ iio_push_to_buffer(indio_dev->buffer, st->data);
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void ad7091_reset(struct ad7476_state *st)
+{
+ /* Any transfers with 8 scl cycles will reset the device */
+ spi_read(st->spi, st->data, 1);
+}
static int ad7476_scan_direct(struct ad7476_state *st)
{
@@ -29,7 +97,7 @@ static int ad7476_scan_direct(struct ad7476_state *st)
if (ret)
return ret;
- return (st->data[0] << 8) | st->data[1];
+ return be16_to_cpup((__be16 *)st->data);
}
static int ad7476_read_raw(struct iio_dev *indio_dev,
@@ -40,7 +108,7 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
{
int ret;
struct ad7476_state *st = iio_priv(indio_dev);
- unsigned int scale_uv;
+ int scale_uv;
switch (m) {
case IIO_CHAN_INFO_RAW:
@@ -57,62 +125,80 @@ static int ad7476_read_raw(struct iio_dev *indio_dev,
RES_MASK(st->chip_info->channel[0].scan_type.realbits);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- scale_uv = (st->int_vref_mv * 1000)
- >> st->chip_info->channel[0].scan_type.realbits;
- *val = scale_uv/1000;
- *val2 = (scale_uv%1000)*1000;
+ if (!st->chip_info->int_vref_uv) {
+ scale_uv = regulator_get_voltage(st->reg);
+ if (scale_uv < 0)
+ return scale_uv;
+ } else {
+ scale_uv = st->chip_info->int_vref_uv;
+ }
+ scale_uv >>= chan->scan_type.realbits;
+ *val = scale_uv / 1000;
+ *val2 = (scale_uv % 1000) * 1000;
return IIO_VAL_INT_PLUS_MICRO;
}
return -EINVAL;
}
-#define AD7476_CHAN(bits) \
+#define _AD7476_CHAN(bits, _shift, _info_mask) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+ .info_mask = _info_mask | \
IIO_CHAN_INFO_SCALE_SHARED_BIT, \
.scan_type = { \
.sign = 'u', \
- .realbits = bits, \
+ .realbits = (bits), \
.storagebits = 16, \
- .shift = 12 - bits, \
+ .shift = (_shift), \
+ .endianness = IIO_BE, \
}, \
}
+#define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \
+ IIO_CHAN_INFO_RAW_SEPARATE_BIT)
+#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
+ IIO_CHAN_INFO_RAW_SEPARATE_BIT)
+#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
+
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
- [ID_AD7466] = {
- .channel[0] = AD7476_CHAN(12),
+ [ID_AD7091R] = {
+ .channel[0] = AD7091R_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ .reset = ad7091_reset,
},
- [ID_AD7467] = {
- .channel[0] = AD7476_CHAN(10),
+ [ID_AD7276] = {
+ .channel[0] = AD7940_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
- [ID_AD7468] = {
- .channel[0] = AD7476_CHAN(8),
+ [ID_AD7277] = {
+ .channel[0] = AD7940_CHAN(10),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
- [ID_AD7475] = {
- .channel[0] = AD7476_CHAN(12),
+ [ID_AD7278] = {
+ .channel[0] = AD7940_CHAN(8),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
- [ID_AD7476] = {
+ [ID_AD7466] = {
.channel[0] = AD7476_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
- [ID_AD7477] = {
+ [ID_AD7467] = {
.channel[0] = AD7476_CHAN(10),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
- [ID_AD7478] = {
+ [ID_AD7468] = {
.channel[0] = AD7476_CHAN(8),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
[ID_AD7495] = {
.channel[0] = AD7476_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
- .int_vref_mv = 2500,
+ .int_vref_uv = 2500000,
+ },
+ [ID_AD7940] = {
+ .channel[0] = AD7940_CHAN(14),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
};
@@ -123,10 +209,9 @@ static const struct iio_info ad7476_info = {
static int __devinit ad7476_probe(struct spi_device *spi)
{
- struct ad7476_platform_data *pdata = spi->dev.platform_data;
struct ad7476_state *st;
struct iio_dev *indio_dev;
- int ret, voltage_uv = 0;
+ int ret;
indio_dev = iio_device_alloc(sizeof(*st));
if (indio_dev == NULL) {
@@ -134,25 +219,18 @@ static int __devinit ad7476_probe(struct spi_device *spi)
goto error_ret;
}
st = iio_priv(indio_dev);
- st->reg = regulator_get(&spi->dev, "vcc");
- if (!IS_ERR(st->reg)) {
- ret = regulator_enable(st->reg);
- if (ret)
- goto error_put_reg;
-
- voltage_uv = regulator_get_voltage(st->reg);
- }
st->chip_info =
&ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data];
- if (st->chip_info->int_vref_mv)
- st->int_vref_mv = st->chip_info->int_vref_mv;
- else if (pdata && pdata->vref_mv)
- st->int_vref_mv = pdata->vref_mv;
- else if (voltage_uv)
- st->int_vref_mv = voltage_uv / 1000;
- else
- dev_warn(&spi->dev, "reference voltage unspecified\n");
+ st->reg = regulator_get(&spi->dev, "vcc");
+ if (IS_ERR(st->reg)) {
+ ret = PTR_ERR(st->reg);
+ goto error_free_dev;
+ }
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
spi_set_drvdata(spi, indio_dev);
@@ -173,57 +251,67 @@ static int __devinit ad7476_probe(struct spi_device *spi)
spi_message_init(&st->msg);
spi_message_add_tail(&st->xfer, &st->msg);
- ret = ad7476_register_ring_funcs_and_init(indio_dev);
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &ad7476_trigger_handler, NULL);
if (ret)
goto error_disable_reg;
+ if (st->chip_info->reset)
+ st->chip_info->reset(st);
+
ret = iio_device_register(indio_dev);
if (ret)
goto error_ring_unregister;
return 0;
error_ring_unregister:
- ad7476_ring_cleanup(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
error_disable_reg:
- if (!IS_ERR(st->reg))
- regulator_disable(st->reg);
+ regulator_disable(st->reg);
error_put_reg:
- if (!IS_ERR(st->reg))
- regulator_put(st->reg);
+ regulator_put(st->reg);
+error_free_dev:
iio_device_free(indio_dev);
error_ret:
return ret;
}
-static int ad7476_remove(struct spi_device *spi)
+static int __devexit ad7476_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ad7476_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
- ad7476_ring_cleanup(indio_dev);
- if (!IS_ERR(st->reg)) {
- regulator_disable(st->reg);
- regulator_put(st->reg);
- }
+ iio_triggered_buffer_cleanup(indio_dev);
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
iio_device_free(indio_dev);
return 0;
}
static const struct spi_device_id ad7476_id[] = {
+ {"ad7091r", ID_AD7091R},
+ {"ad7273", ID_AD7277},
+ {"ad7274", ID_AD7276},
+ {"ad7276", ID_AD7276},
+ {"ad7277", ID_AD7277},
+ {"ad7278", ID_AD7278},
{"ad7466", ID_AD7466},
{"ad7467", ID_AD7467},
{"ad7468", ID_AD7468},
- {"ad7475", ID_AD7475},
- {"ad7476", ID_AD7476},
- {"ad7476a", ID_AD7476},
- {"ad7477", ID_AD7477},
- {"ad7477a", ID_AD7477},
- {"ad7478", ID_AD7478},
- {"ad7478a", ID_AD7478},
+ {"ad7475", ID_AD7466},
+ {"ad7476", ID_AD7466},
+ {"ad7476a", ID_AD7466},
+ {"ad7477", ID_AD7467},
+ {"ad7477a", ID_AD7467},
+ {"ad7478", ID_AD7468},
+ {"ad7478a", ID_AD7468},
{"ad7495", ID_AD7495},
+ {"ad7910", ID_AD7467},
+ {"ad7920", ID_AD7466},
+ {"ad7940", ID_AD7940},
{}
};
MODULE_DEVICE_TABLE(spi, ad7476_id);
@@ -240,5 +328,5 @@ static struct spi_driver ad7476_driver = {
module_spi_driver(ad7476_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("Analog Devices AD7475/6/7/8(A) AD7466/7/8 ADC");
+MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c
new file mode 100644
index 000000000000..e93740843b2b
--- /dev/null
+++ b/drivers/iio/adc/ad7791.c
@@ -0,0 +1,460 @@
+/*
+ * AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#include <linux/platform_data/ad7791.h>
+
+#define AD7791_REG_COMM 0x0 /* For writes */
+#define AD7791_REG_STATUS 0x0 /* For reads */
+#define AD7791_REG_MODE 0x1
+#define AD7791_REG_FILTER 0x2
+#define AD7791_REG_DATA 0x3
+
+#define AD7791_MODE_CONTINUOUS 0x00
+#define AD7791_MODE_SINGLE 0x02
+#define AD7791_MODE_POWERDOWN 0x03
+
+#define AD7791_CH_AIN1P_AIN1N 0x00
+#define AD7791_CH_AIN2 0x01
+#define AD7791_CH_AIN1N_AIN1N 0x02
+#define AD7791_CH_AVDD_MONITOR 0x03
+
+#define AD7791_FILTER_CLK_DIV_1 (0x0 << 4)
+#define AD7791_FILTER_CLK_DIV_2 (0x1 << 4)
+#define AD7791_FILTER_CLK_DIV_4 (0x2 << 4)
+#define AD7791_FILTER_CLK_DIV_8 (0x3 << 4)
+#define AD7791_FILTER_CLK_MASK (0x3 << 4)
+#define AD7791_FILTER_RATE_120 0x0
+#define AD7791_FILTER_RATE_100 0x1
+#define AD7791_FILTER_RATE_33_3 0x2
+#define AD7791_FILTER_RATE_20 0x3
+#define AD7791_FILTER_RATE_16_6 0x4
+#define AD7791_FILTER_RATE_16_7 0x5
+#define AD7791_FILTER_RATE_13_3 0x6
+#define AD7791_FILTER_RATE_9_5 0x7
+#define AD7791_FILTER_RATE_MASK 0x7
+
+#define AD7791_MODE_BUFFER BIT(1)
+#define AD7791_MODE_UNIPOLAR BIT(2)
+#define AD7791_MODE_BURNOUT BIT(3)
+#define AD7791_MODE_SEL_MASK (0x3 << 6)
+#define AD7791_MODE_SEL(x) ((x) << 6)
+
+#define DECLARE_AD7787_CHANNELS(name, bits, storagebits) \
+const struct iio_chan_spec name[] = { \
+ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
+ (bits), (storagebits), 0), \
+ AD_SD_CHANNEL(1, 1, AD7791_CH_AIN2, (bits), (storagebits), 0), \
+ AD_SD_SHORTED_CHANNEL(2, 0, AD7791_CH_AIN1N_AIN1N, \
+ (bits), (storagebits), 0), \
+ AD_SD_SUPPLY_CHANNEL(3, 2, AD7791_CH_AVDD_MONITOR, \
+ (bits), (storagebits), 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(4), \
+}
+
+#define DECLARE_AD7791_CHANNELS(name, bits, storagebits) \
+const struct iio_chan_spec name[] = { \
+ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7791_CH_AIN1P_AIN1N, \
+ (bits), (storagebits), 0), \
+ AD_SD_SHORTED_CHANNEL(1, 0, AD7791_CH_AIN1N_AIN1N, \
+ (bits), (storagebits), 0), \
+ AD_SD_SUPPLY_CHANNEL(2, 1, AD7791_CH_AVDD_MONITOR, \
+ (bits), (storagebits), 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(3), \
+}
+
+static DECLARE_AD7787_CHANNELS(ad7787_channels, 24, 32);
+static DECLARE_AD7791_CHANNELS(ad7790_channels, 16, 16);
+static DECLARE_AD7791_CHANNELS(ad7791_channels, 24, 32);
+
+enum {
+ AD7787,
+ AD7788,
+ AD7789,
+ AD7790,
+ AD7791,
+};
+
+enum ad7791_chip_info_flags {
+ AD7791_FLAG_HAS_FILTER = (1 << 0),
+ AD7791_FLAG_HAS_BUFFER = (1 << 1),
+ AD7791_FLAG_HAS_UNIPOLAR = (1 << 2),
+ AD7791_FLAG_HAS_BURNOUT = (1 << 3),
+};
+
+struct ad7791_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ enum ad7791_chip_info_flags flags;
+};
+
+static const struct ad7791_chip_info ad7791_chip_infos[] = {
+ [AD7787] = {
+ .channels = ad7787_channels,
+ .num_channels = ARRAY_SIZE(ad7787_channels),
+ .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+ AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
+ },
+ [AD7788] = {
+ .channels = ad7790_channels,
+ .num_channels = ARRAY_SIZE(ad7790_channels),
+ .flags = AD7791_FLAG_HAS_UNIPOLAR,
+ },
+ [AD7789] = {
+ .channels = ad7791_channels,
+ .num_channels = ARRAY_SIZE(ad7791_channels),
+ .flags = AD7791_FLAG_HAS_UNIPOLAR,
+ },
+ [AD7790] = {
+ .channels = ad7790_channels,
+ .num_channels = ARRAY_SIZE(ad7790_channels),
+ .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+ AD7791_FLAG_HAS_BURNOUT,
+ },
+ [AD7791] = {
+ .channels = ad7791_channels,
+ .num_channels = ARRAY_SIZE(ad7791_channels),
+ .flags = AD7791_FLAG_HAS_FILTER | AD7791_FLAG_HAS_BUFFER |
+ AD7791_FLAG_HAS_UNIPOLAR | AD7791_FLAG_HAS_BURNOUT,
+ },
+};
+
+struct ad7791_state {
+ struct ad_sigma_delta sd;
+ uint8_t mode;
+ uint8_t filter;
+
+ struct regulator *reg;
+ const struct ad7791_chip_info *info;
+};
+
+static struct ad7791_state *ad_sigma_delta_to_ad7791(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7791_state, sd);
+}
+
+static int ad7791_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ ad_sd_set_comm(sd, channel);
+
+ return 0;
+}
+
+static int ad7791_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7791_state *st = ad_sigma_delta_to_ad7791(sd);
+
+ switch (mode) {
+ case AD_SD_MODE_CONTINUOUS:
+ mode = AD7791_MODE_CONTINUOUS;
+ break;
+ case AD_SD_MODE_SINGLE:
+ mode = AD7791_MODE_SINGLE;
+ break;
+ case AD_SD_MODE_IDLE:
+ case AD_SD_MODE_POWERDOWN:
+ mode = AD7791_MODE_POWERDOWN;
+ break;
+ }
+
+ st->mode &= ~AD7791_MODE_SEL_MASK;
+ st->mode |= AD7791_MODE_SEL(mode);
+
+ return ad_sd_write_reg(sd, AD7791_REG_MODE, sizeof(st->mode), st->mode);
+}
+
+static const struct ad_sigma_delta_info ad7791_sigma_delta_info = {
+ .set_channel = ad7791_set_channel,
+ .set_mode = ad7791_set_mode,
+ .has_registers = true,
+ .addr_shift = 4,
+ .read_mask = BIT(3),
+};
+
+static int ad7791_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+ struct ad7791_state *st = iio_priv(indio_dev);
+ bool unipolar = !!(st->mode & AD7791_MODE_UNIPOLAR);
+ unsigned long long scale_pv;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ case IIO_CHAN_INFO_OFFSET:
+ /**
+ * Unipolar: 0 to VREF
+ * Bipolar -VREF to VREF
+ **/
+ if (unipolar)
+ *val = 0;
+ else
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /* The monitor channel uses an internal reference. */
+ if (chan->address == AD7791_CH_AVDD_MONITOR) {
+ scale_pv = 5850000000000ULL;
+ } else {
+ int voltage_uv;
+
+ voltage_uv = regulator_get_voltage(st->reg);
+ if (voltage_uv < 0)
+ return voltage_uv;
+ scale_pv = (unsigned long long)voltage_uv * 1000000;
+ }
+ if (unipolar)
+ scale_pv >>= chan->scan_type.realbits;
+ else
+ scale_pv >>= chan->scan_type.realbits - 1;
+ *val2 = do_div(scale_pv, 1000000000);
+ *val = scale_pv;
+
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+
+ return -EINVAL;
+}
+
+static const char * const ad7791_sample_freq_avail[] = {
+ [AD7791_FILTER_RATE_120] = "120",
+ [AD7791_FILTER_RATE_100] = "100",
+ [AD7791_FILTER_RATE_33_3] = "33.3",
+ [AD7791_FILTER_RATE_20] = "20",
+ [AD7791_FILTER_RATE_16_6] = "16.6",
+ [AD7791_FILTER_RATE_16_7] = "16.7",
+ [AD7791_FILTER_RATE_13_3] = "13.3",
+ [AD7791_FILTER_RATE_9_5] = "9.5",
+};
+
+static ssize_t ad7791_read_frequency(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7791_state *st = iio_priv(indio_dev);
+ unsigned int rate = st->filter & AD7791_FILTER_RATE_MASK;
+
+ return sprintf(buf, "%s\n", ad7791_sample_freq_avail[rate]);
+}
+
+static ssize_t ad7791_write_frequency(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7791_state *st = iio_priv(indio_dev);
+ int i, ret;
+
+ mutex_lock(&indio_dev->mlock);
+ if (iio_buffer_enabled(indio_dev)) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EBUSY;
+ }
+ mutex_unlock(&indio_dev->mlock);
+
+ ret = -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(ad7791_sample_freq_avail); i++) {
+ if (sysfs_streq(ad7791_sample_freq_avail[i], buf)) {
+
+ mutex_lock(&indio_dev->mlock);
+ st->filter &= ~AD7791_FILTER_RATE_MASK;
+ st->filter |= i;
+ ad_sd_write_reg(&st->sd, AD7791_REG_FILTER,
+ sizeof(st->filter), st->filter);
+ mutex_unlock(&indio_dev->mlock);
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret ? ret : len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
+ ad7791_read_frequency,
+ ad7791_write_frequency);
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("120 100 33.3 20 16.7 16.6 13.3 9.5");
+
+static struct attribute *ad7791_attributes[] = {
+ &iio_dev_attr_sampling_frequency.dev_attr.attr,
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7791_attribute_group = {
+ .attrs = ad7791_attributes,
+};
+
+static const struct iio_info ad7791_info = {
+ .read_raw = &ad7791_read_raw,
+ .attrs = &ad7791_attribute_group,
+ .validate_trigger = ad_sd_validate_trigger,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_info ad7791_no_filter_info = {
+ .read_raw = &ad7791_read_raw,
+ .validate_trigger = ad_sd_validate_trigger,
+ .driver_module = THIS_MODULE,
+};
+
+static int __devinit ad7791_setup(struct ad7791_state *st,
+ struct ad7791_platform_data *pdata)
+{
+ /* Set to poweron-reset default values */
+ st->mode = AD7791_MODE_BUFFER;
+ st->filter = AD7791_FILTER_RATE_16_6;
+
+ if (!pdata)
+ return 0;
+
+ if ((st->info->flags & AD7791_FLAG_HAS_BUFFER) && !pdata->buffered)
+ st->mode &= ~AD7791_MODE_BUFFER;
+
+ if ((st->info->flags & AD7791_FLAG_HAS_BURNOUT) &&
+ pdata->burnout_current)
+ st->mode |= AD7791_MODE_BURNOUT;
+
+ if ((st->info->flags & AD7791_FLAG_HAS_UNIPOLAR) && pdata->unipolar)
+ st->mode |= AD7791_MODE_UNIPOLAR;
+
+ return ad_sd_write_reg(&st->sd, AD7791_REG_MODE, sizeof(st->mode),
+ st->mode);
+}
+
+static int __devinit ad7791_probe(struct spi_device *spi)
+{
+ struct ad7791_platform_data *pdata = spi->dev.platform_data;
+ struct iio_dev *indio_dev;
+ struct ad7791_state *st;
+ int ret;
+
+ if (!spi->irq) {
+ dev_err(&spi->dev, "Missing IRQ.\n");
+ return -ENXIO;
+ }
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ st->reg = regulator_get(&spi->dev, "refin");
+ if (IS_ERR(st->reg)) {
+ ret = PTR_ERR(st->reg);
+ goto err_iio_free;
+ }
+
+ ret = regulator_enable(st->reg);
+ if (ret)
+ goto error_put_reg;
+
+ st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data];
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info);
+
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->info->channels;
+ indio_dev->num_channels = st->info->num_channels;
+ if (st->info->flags & AD7791_FLAG_HAS_FILTER)
+ indio_dev->info = &ad7791_info;
+ else
+ indio_dev->info = &ad7791_no_filter_info;
+
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = ad7791_setup(st, pdata);
+ if (ret)
+ goto error_remove_trigger;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_remove_trigger;
+
+ return 0;
+
+error_remove_trigger:
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+error_disable_reg:
+ regulator_disable(st->reg);
+error_put_reg:
+ regulator_put(st->reg);
+err_iio_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int __devexit ad7791_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7791_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+
+ regulator_disable(st->reg);
+ regulator_put(st->reg);
+
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7791_spi_ids[] = {
+ { "ad7787", AD7787 },
+ { "ad7788", AD7788 },
+ { "ad7789", AD7789 },
+ { "ad7790", AD7790 },
+ { "ad7791", AD7791 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7791_spi_ids);
+
+static struct spi_driver ad7791_driver = {
+ .driver = {
+ .name = "ad7791",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad7791_probe,
+ .remove = __devexit_p(ad7791_remove),
+ .id_table = ad7791_spi_ids,
+};
+module_spi_driver(ad7791_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Device AD7787/AD7788/AD7789/AD7790/AD7791 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
new file mode 100644
index 000000000000..67baa1363d7a
--- /dev/null
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -0,0 +1,558 @@
+/*
+ * Support code for Analog Devices Sigma-Delta ADCs
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/err.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#include <asm/unaligned.h>
+
+
+#define AD_SD_COMM_CHAN_MASK 0x3
+
+#define AD_SD_REG_COMM 0x00
+#define AD_SD_REG_DATA 0x03
+
+/**
+ * ad_sd_set_comm() - Set communications register
+ *
+ * @sigma_delta: The sigma delta device
+ * @comm: New value for the communications register
+ */
+void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm)
+{
+ /* Some variants use the lower two bits of the communications register
+ * to select the channel */
+ sigma_delta->comm = comm & AD_SD_COMM_CHAN_MASK;
+}
+EXPORT_SYMBOL_GPL(ad_sd_set_comm);
+
+/**
+ * ad_sd_write_reg() - Write a register
+ *
+ * @sigma_delta: The sigma delta device
+ * @reg: Address of the register
+ * @size: Size of the register (0-3)
+ * @val: Value to write to the register
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
+ unsigned int size, unsigned int val)
+{
+ uint8_t *data = sigma_delta->data;
+ struct spi_transfer t = {
+ .tx_buf = data,
+ .len = size + 1,
+ .cs_change = sigma_delta->bus_locked,
+ };
+ struct spi_message m;
+ int ret;
+
+ data[0] = (reg << sigma_delta->info->addr_shift) | sigma_delta->comm;
+
+ switch (size) {
+ case 3:
+ data[1] = val >> 16;
+ data[2] = val >> 8;
+ data[3] = val;
+ break;
+ case 2:
+ put_unaligned_be16(val, &data[1]);
+ break;
+ case 1:
+ data[1] = val;
+ break;
+ case 0:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+
+ if (sigma_delta->bus_locked)
+ ret = spi_sync_locked(sigma_delta->spi, &m);
+ else
+ ret = spi_sync(sigma_delta->spi, &m);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ad_sd_write_reg);
+
+static int ad_sd_read_reg_raw(struct ad_sigma_delta *sigma_delta,
+ unsigned int reg, unsigned int size, uint8_t *val)
+{
+ uint8_t *data = sigma_delta->data;
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = data,
+ .len = 1,
+ }, {
+ .rx_buf = val,
+ .len = size,
+ .cs_change = sigma_delta->bus_locked,
+ },
+ };
+ struct spi_message m;
+
+ spi_message_init(&m);
+
+ if (sigma_delta->info->has_registers) {
+ data[0] = reg << sigma_delta->info->addr_shift;
+ data[0] |= sigma_delta->info->read_mask;
+ spi_message_add_tail(&t[0], &m);
+ }
+ spi_message_add_tail(&t[1], &m);
+
+ if (sigma_delta->bus_locked)
+ ret = spi_sync_locked(sigma_delta->spi, &m);
+ else
+ ret = spi_sync(sigma_delta->spi, &m);
+
+ return ret;
+}
+
+/**
+ * ad_sd_read_reg() - Read a register
+ *
+ * @sigma_delta: The sigma delta device
+ * @reg: Address of the register
+ * @size: Size of the register (1-4)
+ * @val: Read value
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta,
+ unsigned int reg, unsigned int size, unsigned int *val)
+{
+ int ret;
+
+ ret = ad_sd_read_reg_raw(sigma_delta, reg, size, sigma_delta->data);
+ if (ret < 0)
+ goto out;
+
+ switch (size) {
+ case 4:
+ *val = get_unaligned_be32(sigma_delta->data);
+ break;
+ case 3:
+ *val = (sigma_delta->data[0] << 16) |
+ (sigma_delta->data[1] << 8) |
+ sigma_delta->data[2];
+ break;
+ case 2:
+ *val = get_unaligned_be16(sigma_delta->data);
+ break;
+ case 1:
+ *val = sigma_delta->data[0];
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ad_sd_read_reg);
+
+static int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta,
+ unsigned int mode, unsigned int channel)
+{
+ int ret;
+
+ ret = ad_sigma_delta_set_channel(sigma_delta, channel);
+ if (ret)
+ return ret;
+
+ spi_bus_lock(sigma_delta->spi->master);
+ sigma_delta->bus_locked = true;
+ INIT_COMPLETION(sigma_delta->completion);
+
+ ret = ad_sigma_delta_set_mode(sigma_delta, mode);
+ if (ret < 0)
+ goto out;
+
+ sigma_delta->irq_dis = false;
+ enable_irq(sigma_delta->spi->irq);
+ ret = wait_for_completion_timeout(&sigma_delta->completion, 2*HZ);
+ if (ret == 0) {
+ sigma_delta->irq_dis = true;
+ disable_irq_nosync(sigma_delta->spi->irq);
+ ret = -EIO;
+ } else {
+ ret = 0;
+ }
+out:
+ sigma_delta->bus_locked = false;
+ spi_bus_unlock(sigma_delta->spi->master);
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+
+ return ret;
+}
+
+/**
+ * ad_sd_calibrate_all() - Performs channel calibration
+ * @sigma_delta: The sigma delta device
+ * @cb: Array of channels and calibration type to perform
+ * @n: Number of items in cb
+ *
+ * Returns 0 on success, an error code otherwise.
+ **/
+int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta,
+ const struct ad_sd_calib_data *cb, unsigned int n)
+{
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < n; i++) {
+ ret = ad_sd_calibrate(sigma_delta, cb[i].mode, cb[i].channel);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_calibrate_all);
+
+/**
+ * ad_sigma_delta_single_conversion() - Performs a single data conversion
+ * @indio_dev: The IIO device
+ * @chan: The conversion is done for this channel
+ * @val: Pointer to the location where to store the read value
+ *
+ * Returns: 0 on success, an error value otherwise.
+ */
+int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ unsigned int sample, raw_sample;
+ int ret = 0;
+
+ if (iio_buffer_enabled(indio_dev))
+ return -EBUSY;
+
+ mutex_lock(&indio_dev->mlock);
+ ad_sigma_delta_set_channel(sigma_delta, chan->address);
+
+ spi_bus_lock(sigma_delta->spi->master);
+ sigma_delta->bus_locked = true;
+ INIT_COMPLETION(sigma_delta->completion);
+
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
+
+ sigma_delta->irq_dis = false;
+ enable_irq(sigma_delta->spi->irq);
+ ret = wait_for_completion_interruptible_timeout(
+ &sigma_delta->completion, HZ);
+
+ sigma_delta->bus_locked = false;
+ spi_bus_unlock(sigma_delta->spi->master);
+
+ if (ret == 0)
+ ret = -EIO;
+ if (ret < 0)
+ goto out;
+
+ ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_DATA,
+ DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8),
+ &raw_sample);
+
+out:
+ if (!sigma_delta->irq_dis) {
+ disable_irq_nosync(sigma_delta->spi->irq);
+ sigma_delta->irq_dis = true;
+ }
+
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret)
+ return ret;
+
+ sample = raw_sample >> chan->scan_type.shift;
+ sample &= (1 << chan->scan_type.realbits) - 1;
+ *val = sample;
+
+ ret = ad_sigma_delta_postprocess_sample(sigma_delta, raw_sample);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+}
+EXPORT_SYMBOL_GPL(ad_sigma_delta_single_conversion);
+
+static int ad_sd_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ unsigned int channel;
+ int ret;
+
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ channel = find_first_bit(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+ ret = ad_sigma_delta_set_channel(sigma_delta,
+ indio_dev->channels[channel].address);
+ if (ret)
+ goto err_predisable;
+
+ spi_bus_lock(sigma_delta->spi->master);
+ sigma_delta->bus_locked = true;
+ ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS);
+ if (ret)
+ goto err_unlock;
+
+ sigma_delta->irq_dis = false;
+ enable_irq(sigma_delta->spi->irq);
+
+ return 0;
+
+err_unlock:
+ spi_bus_unlock(sigma_delta->spi->master);
+err_predisable:
+
+ return ret;
+}
+
+static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+
+ INIT_COMPLETION(sigma_delta->completion);
+ wait_for_completion_timeout(&sigma_delta->completion, HZ);
+
+ if (!sigma_delta->irq_dis) {
+ disable_irq_nosync(sigma_delta->spi->irq);
+ sigma_delta->irq_dis = true;
+ }
+
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+
+ sigma_delta->bus_locked = false;
+ return spi_bus_unlock(sigma_delta->spi->master);
+}
+
+static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ unsigned int reg_size;
+ uint8_t data[16];
+ int ret;
+
+ memset(data, 0x00, 16);
+
+ /* Guaranteed to be aligned with 8 byte boundary */
+ if (indio_dev->scan_timestamp)
+ ((s64 *)data)[1] = pf->timestamp;
+
+ reg_size = indio_dev->channels[0].scan_type.realbits +
+ indio_dev->channels[0].scan_type.shift;
+ reg_size = DIV_ROUND_UP(reg_size, 8);
+
+ switch (reg_size) {
+ case 4:
+ case 2:
+ case 1:
+ ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
+ reg_size, &data[0]);
+ break;
+ case 3:
+ /* We store 24 bit samples in a 32 bit word. Keep the upper
+ * byte set to zero. */
+ ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
+ reg_size, &data[1]);
+ break;
+ }
+
+ iio_push_to_buffer(indio_dev->buffer, (uint8_t *)data);
+
+ iio_trigger_notify_done(indio_dev->trig);
+ sigma_delta->irq_dis = false;
+ enable_irq(sigma_delta->spi->irq);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_buffer_setup_ops ad_sd_buffer_setup_ops = {
+ .preenable = &iio_sw_buffer_preenable,
+ .postenable = &ad_sd_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+ .postdisable = &ad_sd_buffer_postdisable,
+ .validate_scan_mask = &iio_validate_scan_mask_onehot,
+};
+
+static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private)
+{
+ struct ad_sigma_delta *sigma_delta = private;
+
+ complete(&sigma_delta->completion);
+ disable_irq_nosync(irq);
+ sigma_delta->irq_dis = true;
+ iio_trigger_poll(sigma_delta->trig, iio_get_time_ns());
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * ad_sd_validate_trigger() - validate_trigger callback for ad_sigma_delta devices
+ * @indio_dev: The IIO device
+ * @trig: The new trigger
+ *
+ * Returns: 0 if the 'trig' matches the trigger registered by the ad_sigma_delta
+ * device, -EINVAL otherwise.
+ */
+int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+
+ if (sigma_delta->trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_validate_trigger);
+
+static const struct iio_trigger_ops ad_sd_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+static int ad_sd_probe_trigger(struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+ int ret;
+
+ sigma_delta->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name,
+ indio_dev->id);
+ if (sigma_delta->trig == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ sigma_delta->trig->ops = &ad_sd_trigger_ops;
+ init_completion(&sigma_delta->completion);
+
+ ret = request_irq(sigma_delta->spi->irq,
+ ad_sd_data_rdy_trig_poll,
+ IRQF_TRIGGER_LOW,
+ indio_dev->name,
+ sigma_delta);
+ if (ret)
+ goto error_free_trig;
+
+ if (!sigma_delta->irq_dis) {
+ sigma_delta->irq_dis = true;
+ disable_irq_nosync(sigma_delta->spi->irq);
+ }
+ sigma_delta->trig->dev.parent = &sigma_delta->spi->dev;
+ sigma_delta->trig->private_data = sigma_delta;
+
+ ret = iio_trigger_register(sigma_delta->trig);
+ if (ret)
+ goto error_free_irq;
+
+ /* select default trigger */
+ indio_dev->trig = sigma_delta->trig;
+
+ return 0;
+
+error_free_irq:
+ free_irq(sigma_delta->spi->irq, sigma_delta);
+error_free_trig:
+ iio_trigger_free(sigma_delta->trig);
+error_ret:
+ return ret;
+}
+
+static void ad_sd_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
+
+ iio_trigger_unregister(sigma_delta->trig);
+ free_irq(sigma_delta->spi->irq, sigma_delta);
+ iio_trigger_free(sigma_delta->trig);
+}
+
+/**
+ * ad_sd_setup_buffer_and_trigger() -
+ * @indio_dev: The IIO device
+ */
+int ad_sd_setup_buffer_and_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ &ad_sd_trigger_handler, &ad_sd_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ ret = ad_sd_probe_trigger(indio_dev);
+ if (ret) {
+ iio_triggered_buffer_cleanup(indio_dev);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_setup_buffer_and_trigger);
+
+/**
+ * ad_sd_cleanup_buffer_and_trigger() -
+ * @indio_dev: The IIO device
+ */
+void ad_sd_cleanup_buffer_and_trigger(struct iio_dev *indio_dev)
+{
+ ad_sd_remove_trigger(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+}
+EXPORT_SYMBOL_GPL(ad_sd_cleanup_buffer_and_trigger);
+
+/**
+ * ad_sd_init() - Initializes a ad_sigma_delta struct
+ * @sigma_delta: The ad_sigma_delta device
+ * @indio_dev: The IIO device which the Sigma Delta device is used for
+ * @spi: The SPI device for the ad_sigma_delta device
+ * @info: Device specific callbacks and options
+ *
+ * This function needs to be called before any other operations are performed on
+ * the ad_sigma_delta struct.
+ */
+int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
+ struct spi_device *spi, const struct ad_sigma_delta_info *info)
+{
+ sigma_delta->spi = spi;
+ sigma_delta->info = info;
+ iio_device_set_drvdata(indio_dev, sigma_delta);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ad_sd_init);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices Sigma-Delta ADCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index 3bd5540238a7..3ed94bf80596 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -82,7 +82,7 @@ static irqreturn_t at91_adc_trigger_handler(int irq, void *p)
*timestamp = pf->timestamp;
}
- buffer->access->store_to(buffer, (u8 *)st->buffer, pf->timestamp);
+ buffer->access->store_to(buffer, (u8 *)st->buffer);
iio_trigger_notify_done(idev->trig);
st->irq_enabled = true;
@@ -545,13 +545,6 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
goto error_free_device;
}
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(&pdev->dev, "No resource defined\n");
- ret = -ENXIO;
- goto error_ret;
- }
-
platform_set_drvdata(pdev, idev);
idev->dev.parent = &pdev->dev;
@@ -566,18 +559,12 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
goto error_free_device;
}
- if (!request_mem_region(res->start, resource_size(res),
- "AT91 adc registers")) {
- dev_err(&pdev->dev, "Resources are unavailable.\n");
- ret = -EBUSY;
- goto error_free_device;
- }
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- st->reg_base = ioremap(res->start, resource_size(res));
+ st->reg_base = devm_request_and_ioremap(&pdev->dev, res);
if (!st->reg_base) {
- dev_err(&pdev->dev, "Failed to map registers.\n");
ret = -ENOMEM;
- goto error_release_mem;
+ goto error_free_device;
}
/*
@@ -592,45 +579,35 @@ static int __devinit at91_adc_probe(struct platform_device *pdev)
idev);
if (ret) {
dev_err(&pdev->dev, "Failed to allocate IRQ.\n");
- goto error_unmap_reg;
+ goto error_free_device;
}
- st->clk = clk_get(&pdev->dev, "adc_clk");
+ st->clk = devm_clk_get(&pdev->dev, "adc_clk");
if (IS_ERR(st->clk)) {
dev_err(&pdev->dev, "Failed to get the clock.\n");
ret = PTR_ERR(st->clk);
goto error_free_irq;
}
- ret = clk_prepare(st->clk);
+ ret = clk_prepare_enable(st->clk);
if (ret) {
- dev_err(&pdev->dev, "Could not prepare the clock.\n");
- goto error_free_clk;
- }
-
- ret = clk_enable(st->clk);
- if (ret) {
- dev_err(&pdev->dev, "Could not enable the clock.\n");
- goto error_unprepare_clk;
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the clock.\n");
+ goto error_free_irq;
}
- st->adc_clk = clk_get(&pdev->dev, "adc_op_clk");
+ st->adc_clk = devm_clk_get(&pdev->dev, "adc_op_clk");
if (IS_ERR(st->adc_clk)) {
dev_err(&pdev->dev, "Failed to get the ADC clock.\n");
ret = PTR_ERR(st->adc_clk);
goto error_disable_clk;
}
- ret = clk_prepare(st->adc_clk);
+ ret = clk_prepare_enable(st->adc_clk);
if (ret) {
- dev_err(&pdev->dev, "Could not prepare the ADC clock.\n");
- goto error_free_adc_clk;
- }
-
- ret = clk_enable(st->adc_clk);
- if (ret) {
- dev_err(&pdev->dev, "Could not enable the ADC clock.\n");
- goto error_unprepare_adc_clk;
+ dev_err(&pdev->dev,
+ "Could not prepare or enable the ADC clock.\n");
+ goto error_disable_clk;
}
/*
@@ -694,23 +671,11 @@ error_remove_triggers:
error_unregister_buffer:
at91_adc_buffer_remove(idev);
error_disable_adc_clk:
- clk_disable(st->adc_clk);
-error_unprepare_adc_clk:
- clk_unprepare(st->adc_clk);
-error_free_adc_clk:
- clk_put(st->adc_clk);
+ clk_disable_unprepare(st->adc_clk);
error_disable_clk:
- clk_disable(st->clk);
-error_unprepare_clk:
- clk_unprepare(st->clk);
-error_free_clk:
- clk_put(st->clk);
+ clk_disable_unprepare(st->clk);
error_free_irq:
free_irq(st->irq, idev);
-error_unmap_reg:
- iounmap(st->reg_base);
-error_release_mem:
- release_mem_region(res->start, resource_size(res));
error_free_device:
iio_device_free(idev);
error_ret:
@@ -720,20 +685,14 @@ error_ret:
static int __devexit at91_adc_remove(struct platform_device *pdev)
{
struct iio_dev *idev = platform_get_drvdata(pdev);
- struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct at91_adc_state *st = iio_priv(idev);
iio_device_unregister(idev);
at91_adc_trigger_remove(idev);
at91_adc_buffer_remove(idev);
clk_disable_unprepare(st->adc_clk);
- clk_put(st->adc_clk);
- clk_disable(st->clk);
- clk_unprepare(st->clk);
- clk_put(st->clk);
+ clk_disable_unprepare(st->clk);
free_irq(st->irq, idev);
- iounmap(st->reg_base);
- release_mem_region(res->start, resource_size(res));
iio_device_free(idev);
return 0;
diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c
new file mode 100644
index 000000000000..a93aaf0bb841
--- /dev/null
+++ b/drivers/iio/adc/lp8788_adc.c
@@ -0,0 +1,264 @@
+/*
+ * TI LP8788 MFD - ADC driver
+ *
+ * Copyright 2012 Texas Instruments
+ *
+ * Author: Milo(Woogyom) Kim <milo.kim@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/driver.h>
+#include <linux/iio/machine.h>
+#include <linux/mfd/lp8788.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+/* register address */
+#define LP8788_ADC_CONF 0x60
+#define LP8788_ADC_RAW 0x61
+#define LP8788_ADC_DONE 0x63
+
+#define ADC_CONV_START 1
+
+struct lp8788_adc {
+ struct lp8788 *lp;
+ struct iio_map *map;
+ struct mutex lock;
+};
+
+static const int lp8788_scale[LPADC_MAX] = {
+ [LPADC_VBATT_5P5] = 1343101,
+ [LPADC_VIN_CHG] = 3052503,
+ [LPADC_IBATT] = 610500,
+ [LPADC_IC_TEMP] = 61050,
+ [LPADC_VBATT_6P0] = 1465201,
+ [LPADC_VBATT_5P0] = 1221001,
+ [LPADC_ADC1] = 610500,
+ [LPADC_ADC2] = 610500,
+ [LPADC_VDD] = 1025641,
+ [LPADC_VCOIN] = 757020,
+ [LPADC_ADC3] = 610500,
+ [LPADC_ADC4] = 610500,
+};
+
+static int lp8788_get_adc_result(struct lp8788_adc *adc, enum lp8788_adc_id id,
+ int *val)
+{
+ unsigned int msb;
+ unsigned int lsb;
+ unsigned int result;
+ u8 data;
+ u8 rawdata[2];
+ int size = ARRAY_SIZE(rawdata);
+ int retry = 5;
+ int ret;
+
+ data = (id << 1) | ADC_CONV_START;
+ ret = lp8788_write_byte(adc->lp, LP8788_ADC_CONF, data);
+ if (ret)
+ goto err_io;
+
+ /* retry until adc conversion is done */
+ data = 0;
+ while (retry--) {
+ usleep_range(100, 200);
+
+ ret = lp8788_read_byte(adc->lp, LP8788_ADC_DONE, &data);
+ if (ret)
+ goto err_io;
+
+ /* conversion done */
+ if (data)
+ break;
+ }
+
+ ret = lp8788_read_multi_bytes(adc->lp, LP8788_ADC_RAW, rawdata, size);
+ if (ret)
+ goto err_io;
+
+ msb = (rawdata[0] << 4) & 0x00000ff0;
+ lsb = (rawdata[1] >> 4) & 0x0000000f;
+ result = msb | lsb;
+ *val = result;
+
+ return 0;
+
+err_io:
+ return ret;
+}
+
+static int lp8788_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct lp8788_adc *adc = iio_priv(indio_dev);
+ enum lp8788_adc_id id = chan->channel;
+ int ret;
+
+ mutex_lock(&adc->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = lp8788_get_adc_result(adc, id, val) ? -EIO : IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = lp8788_scale[id] / 1000000;
+ *val2 = lp8788_scale[id] % 1000000;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+static const struct iio_info lp8788_adc_info = {
+ .read_raw = &lp8788_adc_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define LP8788_CHAN(_id, _type) { \
+ .type = _type, \
+ .indexed = 1, \
+ .channel = LPADC_##_id, \
+ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+ IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
+ .datasheet_name = #_id, \
+}
+
+static const struct iio_chan_spec lp8788_adc_channels[] = {
+ [LPADC_VBATT_5P5] = LP8788_CHAN(VBATT_5P5, IIO_VOLTAGE),
+ [LPADC_VIN_CHG] = LP8788_CHAN(VIN_CHG, IIO_VOLTAGE),
+ [LPADC_IBATT] = LP8788_CHAN(IBATT, IIO_CURRENT),
+ [LPADC_IC_TEMP] = LP8788_CHAN(IC_TEMP, IIO_TEMP),
+ [LPADC_VBATT_6P0] = LP8788_CHAN(VBATT_6P0, IIO_VOLTAGE),
+ [LPADC_VBATT_5P0] = LP8788_CHAN(VBATT_5P0, IIO_VOLTAGE),
+ [LPADC_ADC1] = LP8788_CHAN(ADC1, IIO_VOLTAGE),
+ [LPADC_ADC2] = LP8788_CHAN(ADC2, IIO_VOLTAGE),
+ [LPADC_VDD] = LP8788_CHAN(VDD, IIO_VOLTAGE),
+ [LPADC_VCOIN] = LP8788_CHAN(VCOIN, IIO_VOLTAGE),
+ [LPADC_ADC3] = LP8788_CHAN(ADC3, IIO_VOLTAGE),
+ [LPADC_ADC4] = LP8788_CHAN(ADC4, IIO_VOLTAGE),
+};
+
+/* default maps used by iio consumer (lp8788-charger driver) */
+static struct iio_map lp8788_default_iio_maps[] = {
+ {
+ .consumer_dev_name = "lp8788-charger",
+ .consumer_channel = "lp8788_vbatt_5p0",
+ .adc_channel_label = "VBATT_5P0",
+ },
+ {
+ .consumer_dev_name = "lp8788-charger",
+ .consumer_channel = "lp8788_adc1",
+ .adc_channel_label = "ADC1",
+ },
+ { }
+};
+
+static int lp8788_iio_map_register(struct iio_dev *indio_dev,
+ struct lp8788_platform_data *pdata,
+ struct lp8788_adc *adc)
+{
+ struct iio_map *map;
+ int ret;
+
+ map = (!pdata || !pdata->adc_pdata) ?
+ lp8788_default_iio_maps : pdata->adc_pdata;
+
+ ret = iio_map_array_register(indio_dev, map);
+ if (ret) {
+ dev_err(adc->lp->dev, "iio map err: %d\n", ret);
+ return ret;
+ }
+
+ adc->map = map;
+ return 0;
+}
+
+static inline void lp8788_iio_map_unregister(struct iio_dev *indio_dev,
+ struct lp8788_adc *adc)
+{
+ iio_map_array_unregister(indio_dev, adc->map);
+}
+
+static int __devinit lp8788_adc_probe(struct platform_device *pdev)
+{
+ struct lp8788 *lp = dev_get_drvdata(pdev->dev.parent);
+ struct iio_dev *indio_dev;
+ struct lp8788_adc *adc;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->lp = lp;
+ platform_set_drvdata(pdev, indio_dev);
+
+ ret = lp8788_iio_map_register(indio_dev, lp->pdata, adc);
+ if (ret)
+ goto err_iio_map;
+
+ mutex_init(&adc->lock);
+
+ indio_dev->dev.parent = lp->dev;
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &lp8788_adc_info;
+ indio_dev->channels = lp8788_adc_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lp8788_adc_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(lp->dev, "iio dev register err: %d\n", ret);
+ goto err_iio_device;
+ }
+
+ return 0;
+
+err_iio_device:
+ lp8788_iio_map_unregister(indio_dev, adc);
+err_iio_map:
+ iio_device_free(indio_dev);
+ return ret;
+}
+
+static int __devexit lp8788_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct lp8788_adc *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ lp8788_iio_map_unregister(indio_dev, adc);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver lp8788_adc_driver = {
+ .probe = lp8788_adc_probe,
+ .remove = __devexit_p(lp8788_adc_remove),
+ .driver = {
+ .name = LP8788_DEV_ADC,
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(lp8788_adc_driver);
+
+MODULE_DESCRIPTION("Texas Instruments LP8788 ADC Driver");
+MODULE_AUTHOR("Milo Kim");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:lp8788-adc");
diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
new file mode 100644
index 000000000000..ed45ee54500c
--- /dev/null
+++ b/drivers/iio/common/Kconfig
@@ -0,0 +1,5 @@
+#
+# IIO common modules
+#
+
+source "drivers/iio/common/hid-sensors/Kconfig"
diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile
new file mode 100644
index 000000000000..81584009b21b
--- /dev/null
+++ b/drivers/iio/common/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the IIO common modules.
+# Common modules contains modules, which can be shared among multiple
+# IIO modules. For example if the trigger processing is common for
+# multiple IIO modules then this can be moved to a common module
+# instead of duplicating in each module.
+#
+
+obj-y += hid-sensors/
diff --git a/drivers/iio/common/hid-sensors/Kconfig b/drivers/iio/common/hid-sensors/Kconfig
new file mode 100644
index 000000000000..8e63d81d652a
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/Kconfig
@@ -0,0 +1,26 @@
+#
+# Hid Sensor common modules
+#
+menu "Hid Sensor IIO Common"
+
+config HID_SENSOR_IIO_COMMON
+ tristate "Common modules for all HID Sensor IIO drivers"
+ depends on HID_SENSOR_HUB
+ select IIO_TRIGGER if IIO_BUFFER
+ help
+ Say yes here to build support for HID sensor to use
+ HID sensor common processing for attributes and IIO triggers.
+ There are many attributes which can be shared among multiple
+ HID sensor drivers, this module contains processing for those
+ attributes.
+
+config HID_SENSOR_ENUM_BASE_QUIRKS
+ tristate "ENUM base quirks for HID Sensor IIO drivers"
+ depends on HID_SENSOR_IIO_COMMON
+ help
+ Say yes here to build support for sensor hub FW using
+ enumeration, which is using 1 as base instead of 0.
+ Since logical minimum is still set 0 instead of 1,
+ there is no easy way to differentiate.
+
+endmenu
diff --git a/drivers/iio/common/hid-sensors/Makefile b/drivers/iio/common/hid-sensors/Makefile
new file mode 100644
index 000000000000..1f463e00c242
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Hid sensor common modules.
+#
+
+obj-$(CONFIG_HID_SENSOR_IIO_COMMON) += hid-sensor-iio-common.o
+hid-sensor-iio-common-y := hid-sensor-attributes.o hid-sensor-trigger.o
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
new file mode 100644
index 000000000000..75374955caba
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -0,0 +1,250 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include "hid-sensor-attributes.h"
+
+static int pow_10(unsigned power)
+{
+ int i;
+ int ret = 1;
+ for (i = 0; i < power; ++i)
+ ret = ret * 10;
+
+ return ret;
+}
+
+static void simple_div(int dividend, int divisor, int *whole,
+ int *micro_frac)
+{
+ int rem;
+ int exp = 0;
+
+ *micro_frac = 0;
+ if (divisor == 0) {
+ *whole = 0;
+ return;
+ }
+ *whole = dividend/divisor;
+ rem = dividend % divisor;
+ if (rem) {
+ while (rem <= divisor) {
+ rem *= 10;
+ exp++;
+ }
+ *micro_frac = (rem / divisor) * pow_10(6-exp);
+ }
+}
+
+static void split_micro_fraction(unsigned int no, int exp, int *val1, int *val2)
+{
+ *val1 = no/pow_10(exp);
+ *val2 = no%pow_10(exp) * pow_10(6-exp);
+}
+
+/*
+VTF format uses exponent and variable size format.
+For example if the size is 2 bytes
+0x0067 with VTF16E14 format -> +1.03
+To convert just change to 0x67 to decimal and use two decimal as E14 stands
+for 10^-2.
+Negative numbers are 2's complement
+*/
+static void convert_from_vtf_format(u32 value, int size, int exp,
+ int *val1, int *val2)
+{
+ int sign = 1;
+
+ if (value & BIT(size*8 - 1)) {
+ value = ((1LL << (size * 8)) - value);
+ sign = -1;
+ }
+ exp = hid_sensor_convert_exponent(exp);
+ if (exp >= 0) {
+ *val1 = sign * value * pow_10(exp);
+ *val2 = 0;
+ } else {
+ split_micro_fraction(value, -exp, val1, val2);
+ if (*val1)
+ *val1 = sign * (*val1);
+ else
+ *val2 = sign * (*val2);
+ }
+}
+
+static u32 convert_to_vtf_format(int size, int exp, int val1, int val2)
+{
+ u32 value;
+ int sign = 1;
+
+ if (val1 < 0 || val2 < 0)
+ sign = -1;
+ exp = hid_sensor_convert_exponent(exp);
+ if (exp < 0) {
+ value = abs(val1) * pow_10(-exp);
+ value += abs(val2) / pow_10(6+exp);
+ } else
+ value = abs(val1) / pow_10(exp);
+ if (sign < 0)
+ value = ((1LL << (size * 8)) - value);
+
+ return value;
+}
+
+int hid_sensor_read_samp_freq_value(struct hid_sensor_iio_common *st,
+ int *val1, int *val2)
+{
+ s32 value;
+ int ret;
+
+ ret = sensor_hub_get_feature(st->hsdev,
+ st->poll.report_id,
+ st->poll.index, &value);
+ if (ret < 0 || value < 0) {
+ *val1 = *val2 = 0;
+ return -EINVAL;
+ } else {
+ if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
+ simple_div(1000, value, val1, val2);
+ else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
+ simple_div(1, value, val1, val2);
+ else {
+ *val1 = *val2 = 0;
+ return -EINVAL;
+ }
+ }
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+EXPORT_SYMBOL(hid_sensor_read_samp_freq_value);
+
+int hid_sensor_write_samp_freq_value(struct hid_sensor_iio_common *st,
+ int val1, int val2)
+{
+ s32 value;
+ int ret;
+
+ if (val1 < 0 || val2 < 0)
+ ret = -EINVAL;
+
+ value = val1 * pow_10(6) + val2;
+ if (value) {
+ if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
+ value = pow_10(9)/value;
+ else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
+ value = pow_10(6)/value;
+ else
+ value = 0;
+ }
+ ret = sensor_hub_set_feature(st->hsdev,
+ st->poll.report_id,
+ st->poll.index, value);
+ if (ret < 0 || value < 0)
+ ret = -EINVAL;
+
+ return ret;
+}
+EXPORT_SYMBOL(hid_sensor_write_samp_freq_value);
+
+int hid_sensor_read_raw_hyst_value(struct hid_sensor_iio_common *st,
+ int *val1, int *val2)
+{
+ s32 value;
+ int ret;
+
+ ret = sensor_hub_get_feature(st->hsdev,
+ st->sensitivity.report_id,
+ st->sensitivity.index, &value);
+ if (ret < 0 || value < 0) {
+ *val1 = *val2 = 0;
+ return -EINVAL;
+ } else {
+ convert_from_vtf_format(value, st->sensitivity.size,
+ st->sensitivity.unit_expo,
+ val1, val2);
+ }
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value);
+
+int hid_sensor_write_raw_hyst_value(struct hid_sensor_iio_common *st,
+ int val1, int val2)
+{
+ s32 value;
+ int ret;
+
+ value = convert_to_vtf_format(st->sensitivity.size,
+ st->sensitivity.unit_expo,
+ val1, val2);
+ ret = sensor_hub_set_feature(st->hsdev,
+ st->sensitivity.report_id,
+ st->sensitivity.index, value);
+ if (ret < 0 || value < 0)
+ ret = -EINVAL;
+
+ return ret;
+}
+EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value);
+
+int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ struct hid_sensor_iio_common *st)
+{
+
+ sensor_hub_input_get_attribute_info(hsdev,
+ HID_FEATURE_REPORT, usage_id,
+ HID_USAGE_SENSOR_PROP_REPORT_INTERVAL,
+ &st->poll);
+
+ sensor_hub_input_get_attribute_info(hsdev,
+ HID_FEATURE_REPORT, usage_id,
+ HID_USAGE_SENSOR_PROP_REPORT_STATE,
+ &st->report_state);
+
+ sensor_hub_input_get_attribute_info(hsdev,
+ HID_FEATURE_REPORT, usage_id,
+ HID_USAGE_SENSOR_PROY_POWER_STATE,
+ &st->power_state);
+
+ sensor_hub_input_get_attribute_info(hsdev,
+ HID_FEATURE_REPORT, usage_id,
+ HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS,
+ &st->sensitivity);
+
+ hid_dbg(hsdev->hdev, "common attributes: %x:%x, %x:%x, %x:%x %x:%x\n",
+ st->poll.index, st->poll.report_id,
+ st->report_state.index, st->report_state.report_id,
+ st->power_state.index, st->power_state.report_id,
+ st->sensitivity.index, st->sensitivity.report_id);
+
+ return 0;
+}
+EXPORT_SYMBOL(hid_sensor_parse_common_attributes);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_DESCRIPTION("HID Sensor common attribute processing");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.h b/drivers/iio/common/hid-sensors/hid-sensor-attributes.h
new file mode 100644
index 000000000000..a4676a0c3de5
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.h
@@ -0,0 +1,57 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ */
+#ifndef _HID_SENSORS_ATTRIBUTES_H
+#define _HID_SENSORS_ATTRIBUTES_H
+
+/* Common hid sensor iio structure */
+struct hid_sensor_iio_common {
+ struct hid_sensor_hub_device *hsdev;
+ struct platform_device *pdev;
+ unsigned usage_id;
+ bool data_ready;
+ struct hid_sensor_hub_attribute_info poll;
+ struct hid_sensor_hub_attribute_info report_state;
+ struct hid_sensor_hub_attribute_info power_state;
+ struct hid_sensor_hub_attribute_info sensitivity;
+};
+
+/*Convert from hid unit expo to regular exponent*/
+static inline int hid_sensor_convert_exponent(int unit_expo)
+{
+ if (unit_expo < 0x08)
+ return unit_expo;
+ else if (unit_expo <= 0x0f)
+ return -(0x0f-unit_expo+1);
+ else
+ return 0;
+}
+
+int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ struct hid_sensor_iio_common *st);
+int hid_sensor_write_raw_hyst_value(struct hid_sensor_iio_common *st,
+ int val1, int val2);
+int hid_sensor_read_raw_hyst_value(struct hid_sensor_iio_common *st,
+ int *val1, int *val2);
+int hid_sensor_write_samp_freq_value(struct hid_sensor_iio_common *st,
+ int val1, int val2);
+int hid_sensor_read_samp_freq_value(struct hid_sensor_iio_common *st,
+ int *val1, int *val2);
+
+#endif
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
new file mode 100644
index 000000000000..d4b790d18efb
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -0,0 +1,103 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/sysfs.h>
+#include "hid-sensor-attributes.h"
+#include "hid-sensor-trigger.h"
+
+static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct hid_sensor_iio_common *st = trig->private_data;
+ int state_val;
+
+ state_val = state ? 1 : 0;
+#if (defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS) || \
+ (defined CONFIG_HID_SENSOR_ENUM_BASE_QUIRKS_MODULE)
+ ++state_val;
+#endif
+ st->data_ready = state;
+ 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,
+ st->report_state.index,
+ (s32)state_val);
+
+ return 0;
+}
+
+void hid_sensor_remove_trigger(struct iio_dev *indio_dev)
+{
+ iio_trigger_unregister(indio_dev->trig);
+ iio_trigger_free(indio_dev->trig);
+ indio_dev->trig = NULL;
+}
+EXPORT_SYMBOL(hid_sensor_remove_trigger);
+
+static const struct iio_trigger_ops hid_sensor_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &hid_sensor_data_rdy_trigger_set_state,
+};
+
+int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
+ struct hid_sensor_iio_common *attrb)
+{
+ int ret;
+ struct iio_trigger *trig;
+
+ trig = iio_trigger_alloc("%s-dev%d", name, indio_dev->id);
+ if (trig == NULL) {
+ dev_err(&indio_dev->dev, "Trigger Allocate Failed\n");
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+
+ trig->dev.parent = indio_dev->dev.parent;
+ trig->private_data = attrb;
+ trig->ops = &hid_sensor_trigger_ops;
+ ret = iio_trigger_register(trig);
+
+ if (ret) {
+ dev_err(&indio_dev->dev, "Trigger Register Failed\n");
+ goto error_free_trig;
+ }
+ indio_dev->trig = trig;
+
+ return ret;
+
+error_free_trig:
+ iio_trigger_free(trig);
+error_ret:
+ return ret;
+}
+EXPORT_SYMBOL(hid_sensor_setup_trigger);
+
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_DESCRIPTION("HID Sensor trigger processing");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
new file mode 100644
index 000000000000..fd982971b1b8
--- /dev/null
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
@@ -0,0 +1,26 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ */
+#ifndef _HID_SENSOR_TRIGGER_H
+#define _HID_SENSOR_TRIGGER_H
+
+int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
+ struct hid_sensor_iio_common *attrb);
+void hid_sensor_remove_trigger(struct iio_dev *indio_dev);
+
+#endif
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 1be15fa9d618..b1c0ee5294ca 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -57,11 +57,12 @@ config AD5624R_SPI
config AD5446
tristate "Analog Devices AD5446 and similar single channel DACs driver"
- depends on SPI
+ depends on (SPI_MASTER || I2C)
help
- Say yes here to build support for Analog Devices AD5444, AD5446, AD5450,
- AD5451, AD5452, AD5453, AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601,
- AD5611, AD5620, AD5621, AD5640, AD5660, AD5662 DACs.
+ Say yes here to build support for Analog Devices AD5300, AD5301, AD5310,
+ AD5311, AD5320, AD5321, AD5444, AD5446, AD5450, AD5451, AD5452, AD5453,
+ AD5512A, AD5541A, AD5542A, AD5543, AD5553, AD5601, AD5602, AD5611, AD5612,
+ AD5620, AD5621, AD5622, AD5640, AD5660, AD5662 DACs.
To compile this driver as a module, choose M here: the
module will be called ad5446.
@@ -76,6 +77,17 @@ config AD5504
To compile this driver as a module, choose M here: the
module will be called ad5504.
+config AD5755
+ tristate "Analog Devices AD5755/AD5755-1/AD5757/AD5735/AD5737 DAC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD5755, AD5755-1,
+ AD5757, AD5735, AD5737 quad channel Digital to
+ Analog Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad5755.
+
config AD5764
tristate "Analog Devices AD5764/64R/44/44R DAC driver"
depends on SPI_MASTER
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 9ea3ceeefc07..c0d333b23ba3 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_AD5624R_SPI) += ad5624r_spi.o
obj-$(CONFIG_AD5064) += ad5064.o
obj-$(CONFIG_AD5504) += ad5504.o
obj-$(CONFIG_AD5446) += ad5446.o
+obj-$(CONFIG_AD5755) += ad5755.o
obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o
diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c
index 2ca5059ef89e..3310cbbd41e7 100644
--- a/drivers/iio/dac/ad5446.c
+++ b/drivers/iio/dac/ad5446.c
@@ -14,6 +14,7 @@
#include <linux/sysfs.h>
#include <linux/list.h>
#include <linux/spi/spi.h>
+#include <linux/i2c.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
#include <linux/module.h>
@@ -21,24 +22,40 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
-#include "ad5446.h"
+#define MODE_PWRDWN_1k 0x1
+#define MODE_PWRDWN_100k 0x2
+#define MODE_PWRDWN_TRISTATE 0x3
-static int ad5446_write(struct ad5446_state *st, unsigned val)
-{
- __be16 data = cpu_to_be16(val);
- return spi_write(st->spi, &data, sizeof(data));
-}
+/**
+ * struct ad5446_state - driver instance specific data
+ * @spi: spi_device
+ * @chip_info: chip model specific constants, available modes etc
+ * @reg: supply regulator
+ * @vref_mv: actual reference voltage used
+ */
-static int ad5660_write(struct ad5446_state *st, unsigned val)
-{
- uint8_t data[3];
+struct ad5446_state {
+ struct device *dev;
+ const struct ad5446_chip_info *chip_info;
+ struct regulator *reg;
+ unsigned short vref_mv;
+ unsigned cached_val;
+ unsigned pwr_down_mode;
+ unsigned pwr_down;
+};
- data[0] = (val >> 16) & 0xFF;
- data[1] = (val >> 8) & 0xFF;
- data[2] = val & 0xFF;
+/**
+ * struct ad5446_chip_info - chip specific information
+ * @channel: channel spec for the DAC
+ * @int_vref_mv: AD5620/40/60: the internal reference voltage
+ * @write: chip specific helper function to write to the register
+ */
- return spi_write(st->spi, data, sizeof(data));
-}
+struct ad5446_chip_info {
+ struct iio_chan_spec channel;
+ u16 int_vref_mv;
+ int (*write)(struct ad5446_state *st, unsigned val);
+};
static const char * const ad5446_powerdown_modes[] = {
"1kohm_to_gnd", "100kohm_to_gnd", "three_state"
@@ -110,7 +127,7 @@ static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev,
return ret ? ret : len;
}
-static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = {
+static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = {
{
.name = "powerdown",
.read = ad5446_read_dac_powerdown,
@@ -136,84 +153,7 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info_powerdown[] = {
_AD5446_CHANNEL(bits, storage, shift, NULL)
#define AD5446_CHANNEL_POWERDOWN(bits, storage, shift) \
- _AD5446_CHANNEL(bits, storage, shift, ad5064_ext_info_powerdown)
-
-static const struct ad5446_chip_info ad5446_chip_info_tbl[] = {
- [ID_AD5444] = {
- .channel = AD5446_CHANNEL(12, 16, 2),
- .write = ad5446_write,
- },
- [ID_AD5446] = {
- .channel = AD5446_CHANNEL(14, 16, 0),
- .write = ad5446_write,
- },
- [ID_AD5450] = {
- .channel = AD5446_CHANNEL(8, 16, 6),
- .write = ad5446_write,
- },
- [ID_AD5451] = {
- .channel = AD5446_CHANNEL(10, 16, 4),
- .write = ad5446_write,
- },
- [ID_AD5541A] = {
- .channel = AD5446_CHANNEL(16, 16, 0),
- .write = ad5446_write,
- },
- [ID_AD5512A] = {
- .channel = AD5446_CHANNEL(12, 16, 4),
- .write = ad5446_write,
- },
- [ID_AD5553] = {
- .channel = AD5446_CHANNEL(14, 16, 0),
- .write = ad5446_write,
- },
- [ID_AD5601] = {
- .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6),
- .write = ad5446_write,
- },
- [ID_AD5611] = {
- .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4),
- .write = ad5446_write,
- },
- [ID_AD5621] = {
- .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
- .write = ad5446_write,
- },
- [ID_AD5620_2500] = {
- .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
- .int_vref_mv = 2500,
- .write = ad5446_write,
- },
- [ID_AD5620_1250] = {
- .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
- .int_vref_mv = 1250,
- .write = ad5446_write,
- },
- [ID_AD5640_2500] = {
- .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
- .int_vref_mv = 2500,
- .write = ad5446_write,
- },
- [ID_AD5640_1250] = {
- .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
- .int_vref_mv = 1250,
- .write = ad5446_write,
- },
- [ID_AD5660_2500] = {
- .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
- .int_vref_mv = 2500,
- .write = ad5660_write,
- },
- [ID_AD5660_1250] = {
- .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
- .int_vref_mv = 1250,
- .write = ad5660_write,
- },
- [ID_AD5662] = {
- .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
- .write = ad5660_write,
- },
-};
+ _AD5446_CHANNEL(bits, storage, shift, ad5446_ext_info_powerdown)
static int ad5446_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
@@ -272,14 +212,15 @@ static const struct iio_info ad5446_info = {
.driver_module = THIS_MODULE,
};
-static int __devinit ad5446_probe(struct spi_device *spi)
+static int __devinit ad5446_probe(struct device *dev, const char *name,
+ const struct ad5446_chip_info *chip_info)
{
struct ad5446_state *st;
struct iio_dev *indio_dev;
struct regulator *reg;
int ret, voltage_uv = 0;
- reg = regulator_get(&spi->dev, "vcc");
+ reg = regulator_get(dev, "vcc");
if (!IS_ERR(reg)) {
ret = regulator_enable(reg);
if (ret)
@@ -294,16 +235,15 @@ static int __devinit ad5446_probe(struct spi_device *spi)
goto error_disable_reg;
}
st = iio_priv(indio_dev);
- st->chip_info =
- &ad5446_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+ st->chip_info = chip_info;
- spi_set_drvdata(spi, indio_dev);
+ dev_set_drvdata(dev, indio_dev);
st->reg = reg;
- st->spi = spi;
+ st->dev = dev;
- /* Establish that the iio_dev is a child of the spi device */
- indio_dev->dev.parent = &spi->dev;
- indio_dev->name = spi_get_device_id(spi)->name;
+ /* Establish that the iio_dev is a child of the device */
+ indio_dev->dev.parent = dev;
+ indio_dev->name = name;
indio_dev->info = &ad5446_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = &st->chip_info->channel;
@@ -316,7 +256,7 @@ static int __devinit ad5446_probe(struct spi_device *spi)
else if (voltage_uv)
st->vref_mv = voltage_uv / 1000;
else
- dev_warn(&spi->dev, "reference voltage unspecified\n");
+ dev_warn(dev, "reference voltage unspecified\n");
ret = iio_device_register(indio_dev);
if (ret)
@@ -336,9 +276,9 @@ error_put_reg:
return ret;
}
-static int ad5446_remove(struct spi_device *spi)
+static int ad5446_remove(struct device *dev)
{
- struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct ad5446_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
@@ -351,7 +291,151 @@ static int ad5446_remove(struct spi_device *spi)
return 0;
}
-static const struct spi_device_id ad5446_id[] = {
+#if IS_ENABLED(CONFIG_SPI_MASTER)
+
+static int ad5446_write(struct ad5446_state *st, unsigned val)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ __be16 data = cpu_to_be16(val);
+
+ return spi_write(spi, &data, sizeof(data));
+}
+
+static int ad5660_write(struct ad5446_state *st, unsigned val)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ uint8_t data[3];
+
+ data[0] = (val >> 16) & 0xFF;
+ data[1] = (val >> 8) & 0xFF;
+ data[2] = val & 0xFF;
+
+ return spi_write(spi, data, sizeof(data));
+}
+
+/**
+ * ad5446_supported_spi_device_ids:
+ * The AD5620/40/60 parts are available in different fixed internal reference
+ * voltage options. The actual part numbers may look differently
+ * (and a bit cryptic), however this style is used to make clear which
+ * parts are supported here.
+ */
+enum ad5446_supported_spi_device_ids {
+ ID_AD5300,
+ ID_AD5310,
+ ID_AD5320,
+ ID_AD5444,
+ ID_AD5446,
+ ID_AD5450,
+ ID_AD5451,
+ ID_AD5541A,
+ ID_AD5512A,
+ ID_AD5553,
+ ID_AD5601,
+ ID_AD5611,
+ ID_AD5621,
+ ID_AD5620_2500,
+ ID_AD5620_1250,
+ ID_AD5640_2500,
+ ID_AD5640_1250,
+ ID_AD5660_2500,
+ ID_AD5660_1250,
+ ID_AD5662,
+};
+
+static const struct ad5446_chip_info ad5446_spi_chip_info[] = {
+ [ID_AD5300] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5310] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2),
+ .write = ad5446_write,
+ },
+ [ID_AD5320] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5444] = {
+ .channel = AD5446_CHANNEL(12, 16, 2),
+ .write = ad5446_write,
+ },
+ [ID_AD5446] = {
+ .channel = AD5446_CHANNEL(14, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5450] = {
+ .channel = AD5446_CHANNEL(8, 16, 6),
+ .write = ad5446_write,
+ },
+ [ID_AD5451] = {
+ .channel = AD5446_CHANNEL(10, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5541A] = {
+ .channel = AD5446_CHANNEL(16, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5512A] = {
+ .channel = AD5446_CHANNEL(12, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5553] = {
+ .channel = AD5446_CHANNEL(14, 16, 0),
+ .write = ad5446_write,
+ },
+ [ID_AD5601] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 6),
+ .write = ad5446_write,
+ },
+ [ID_AD5611] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 4),
+ .write = ad5446_write,
+ },
+ [ID_AD5621] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+ .write = ad5446_write,
+ },
+ [ID_AD5620_2500] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+ .int_vref_mv = 2500,
+ .write = ad5446_write,
+ },
+ [ID_AD5620_1250] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 2),
+ .int_vref_mv = 1250,
+ .write = ad5446_write,
+ },
+ [ID_AD5640_2500] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
+ .int_vref_mv = 2500,
+ .write = ad5446_write,
+ },
+ [ID_AD5640_1250] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(14, 16, 0),
+ .int_vref_mv = 1250,
+ .write = ad5446_write,
+ },
+ [ID_AD5660_2500] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+ .int_vref_mv = 2500,
+ .write = ad5660_write,
+ },
+ [ID_AD5660_1250] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+ .int_vref_mv = 1250,
+ .write = ad5660_write,
+ },
+ [ID_AD5662] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(16, 16, 0),
+ .write = ad5660_write,
+ },
+};
+
+static const struct spi_device_id ad5446_spi_ids[] = {
+ {"ad5300", ID_AD5300},
+ {"ad5310", ID_AD5310},
+ {"ad5320", ID_AD5320},
{"ad5444", ID_AD5444},
{"ad5446", ID_AD5446},
{"ad5450", ID_AD5450},
@@ -375,18 +459,160 @@ static const struct spi_device_id ad5446_id[] = {
{"ad5662", ID_AD5662},
{}
};
-MODULE_DEVICE_TABLE(spi, ad5446_id);
+MODULE_DEVICE_TABLE(spi, ad5446_spi_ids);
+
+static int __devinit ad5446_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
-static struct spi_driver ad5446_driver = {
+ return ad5446_probe(&spi->dev, id->name,
+ &ad5446_spi_chip_info[id->driver_data]);
+}
+
+static int __devexit ad5446_spi_remove(struct spi_device *spi)
+{
+ return ad5446_remove(&spi->dev);
+}
+
+static struct spi_driver ad5446_spi_driver = {
.driver = {
.name = "ad5446",
.owner = THIS_MODULE,
},
- .probe = ad5446_probe,
- .remove = __devexit_p(ad5446_remove),
- .id_table = ad5446_id,
+ .probe = ad5446_spi_probe,
+ .remove = __devexit_p(ad5446_spi_remove),
+ .id_table = ad5446_spi_ids,
};
-module_spi_driver(ad5446_driver);
+
+static int __init ad5446_spi_register_driver(void)
+{
+ return spi_register_driver(&ad5446_spi_driver);
+}
+
+static void ad5446_spi_unregister_driver(void)
+{
+ spi_unregister_driver(&ad5446_spi_driver);
+}
+
+#else
+
+static inline int ad5446_spi_register_driver(void) { return 0; }
+static inline void ad5446_spi_unregister_driver(void) { }
+
+#endif
+
+#if IS_ENABLED(CONFIG_I2C)
+
+static int ad5622_write(struct ad5446_state *st, unsigned val)
+{
+ struct i2c_client *client = to_i2c_client(st->dev);
+ __be16 data = cpu_to_be16(val);
+
+ return i2c_master_send(client, (char *)&data, sizeof(data));
+}
+
+/**
+ * ad5446_supported_i2c_device_ids:
+ * The AD5620/40/60 parts are available in different fixed internal reference
+ * voltage options. The actual part numbers may look differently
+ * (and a bit cryptic), however this style is used to make clear which
+ * parts are supported here.
+ */
+enum ad5446_supported_i2c_device_ids {
+ ID_AD5602,
+ ID_AD5612,
+ ID_AD5622,
+};
+
+static const struct ad5446_chip_info ad5446_i2c_chip_info[] = {
+ [ID_AD5602] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(8, 16, 4),
+ .write = ad5622_write,
+ },
+ [ID_AD5612] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(10, 16, 2),
+ .write = ad5622_write,
+ },
+ [ID_AD5622] = {
+ .channel = AD5446_CHANNEL_POWERDOWN(12, 16, 0),
+ .write = ad5622_write,
+ },
+};
+
+static int __devinit ad5446_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ return ad5446_probe(&i2c->dev, id->name,
+ &ad5446_i2c_chip_info[id->driver_data]);
+}
+
+static int __devexit ad5446_i2c_remove(struct i2c_client *i2c)
+{
+ return ad5446_remove(&i2c->dev);
+}
+
+static const struct i2c_device_id ad5446_i2c_ids[] = {
+ {"ad5301", ID_AD5602},
+ {"ad5311", ID_AD5612},
+ {"ad5321", ID_AD5622},
+ {"ad5602", ID_AD5602},
+ {"ad5612", ID_AD5612},
+ {"ad5622", ID_AD5622},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ad5446_i2c_ids);
+
+static struct i2c_driver ad5446_i2c_driver = {
+ .driver = {
+ .name = "ad5446",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5446_i2c_probe,
+ .remove = __devexit_p(ad5446_i2c_remove),
+ .id_table = ad5446_i2c_ids,
+};
+
+static int __init ad5446_i2c_register_driver(void)
+{
+ return i2c_add_driver(&ad5446_i2c_driver);
+}
+
+static void __exit ad5446_i2c_unregister_driver(void)
+{
+ i2c_del_driver(&ad5446_i2c_driver);
+}
+
+#else
+
+static inline int ad5446_i2c_register_driver(void) { return 0; }
+static inline void ad5446_i2c_unregister_driver(void) { }
+
+#endif
+
+static int __init ad5446_init(void)
+{
+ int ret;
+
+ ret = ad5446_spi_register_driver();
+ if (ret)
+ return ret;
+
+ ret = ad5446_i2c_register_driver();
+ if (ret) {
+ ad5446_spi_unregister_driver();
+ return ret;
+ }
+
+ return 0;
+}
+module_init(ad5446_init);
+
+static void __exit ad5446_exit(void)
+{
+ ad5446_i2c_unregister_driver();
+ ad5446_spi_unregister_driver();
+}
+module_exit(ad5446_exit);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC");
diff --git a/drivers/iio/dac/ad5446.h b/drivers/iio/dac/ad5446.h
deleted file mode 100644
index 2934269a56d5..000000000000
--- a/drivers/iio/dac/ad5446.h
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * AD5446 SPI DAC driver
- *
- * Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-#ifndef IIO_DAC_AD5446_H_
-#define IIO_DAC_AD5446_H_
-
-/* DAC Control Bits */
-
-#define AD5446_LOAD (0x0 << 14) /* Load and update */
-#define AD5446_SDO_DIS (0x1 << 14) /* Disable SDO */
-#define AD5446_NOP (0x2 << 14) /* No operation */
-#define AD5446_CLK_RISING (0x3 << 14) /* Clock data on rising edge */
-
-#define AD5620_LOAD (0x0 << 14) /* Load and update Norm Operation*/
-#define AD5620_PWRDWN_1k (0x1 << 14) /* Power-down: 1kOhm to GND */
-#define AD5620_PWRDWN_100k (0x2 << 14) /* Power-down: 100kOhm to GND */
-#define AD5620_PWRDWN_TRISTATE (0x3 << 14) /* Power-down: Three-state */
-
-#define AD5660_LOAD (0x0 << 16) /* Load and update Norm Operation*/
-#define AD5660_PWRDWN_1k (0x1 << 16) /* Power-down: 1kOhm to GND */
-#define AD5660_PWRDWN_100k (0x2 << 16) /* Power-down: 100kOhm to GND */
-#define AD5660_PWRDWN_TRISTATE (0x3 << 16) /* Power-down: Three-state */
-
-#define MODE_PWRDWN_1k 0x1
-#define MODE_PWRDWN_100k 0x2
-#define MODE_PWRDWN_TRISTATE 0x3
-
-/**
- * struct ad5446_state - driver instance specific data
- * @spi: spi_device
- * @chip_info: chip model specific constants, available modes etc
- * @reg: supply regulator
- * @vref_mv: actual reference voltage used
- */
-
-struct ad5446_state {
- struct spi_device *spi;
- const struct ad5446_chip_info *chip_info;
- struct regulator *reg;
- unsigned short vref_mv;
- unsigned cached_val;
- unsigned pwr_down_mode;
- unsigned pwr_down;
-};
-
-/**
- * struct ad5446_chip_info - chip specific information
- * @channel: channel spec for the DAC
- * @int_vref_mv: AD5620/40/60: the internal reference voltage
- * @write: chip specific helper function to write to the register
- */
-
-struct ad5446_chip_info {
- struct iio_chan_spec channel;
- u16 int_vref_mv;
- int (*write)(struct ad5446_state *st, unsigned val);
-};
-
-/**
- * ad5446_supported_device_ids:
- * The AD5620/40/60 parts are available in different fixed internal reference
- * voltage options. The actual part numbers may look differently
- * (and a bit cryptic), however this style is used to make clear which
- * parts are supported here.
- */
-
-enum ad5446_supported_device_ids {
- ID_AD5444,
- ID_AD5446,
- ID_AD5450,
- ID_AD5451,
- ID_AD5541A,
- ID_AD5512A,
- ID_AD5553,
- ID_AD5601,
- ID_AD5611,
- ID_AD5621,
- ID_AD5620_2500,
- ID_AD5620_1250,
- ID_AD5640_2500,
- ID_AD5640_1250,
- ID_AD5660_2500,
- ID_AD5660_1250,
- ID_AD5662,
-};
-
-#endif /* IIO_DAC_AD5446_H_ */
diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c
new file mode 100644
index 000000000000..5db3506034c5
--- /dev/null
+++ b/drivers/iio/dac/ad5755.c
@@ -0,0 +1,650 @@
+/*
+ * AD5755, AD5755-1, AD5757, AD5735, AD5737 Digital to analog converters driver
+ *
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/platform_data/ad5755.h>
+
+#define AD5755_NUM_CHANNELS 4
+
+#define AD5755_ADDR(x) ((x) << 16)
+
+#define AD5755_WRITE_REG_DATA(chan) (chan)
+#define AD5755_WRITE_REG_GAIN(chan) (0x08 | (chan))
+#define AD5755_WRITE_REG_OFFSET(chan) (0x10 | (chan))
+#define AD5755_WRITE_REG_CTRL(chan) (0x1c | (chan))
+
+#define AD5755_READ_REG_DATA(chan) (chan)
+#define AD5755_READ_REG_CTRL(chan) (0x4 | (chan))
+#define AD5755_READ_REG_GAIN(chan) (0x8 | (chan))
+#define AD5755_READ_REG_OFFSET(chan) (0xc | (chan))
+#define AD5755_READ_REG_CLEAR(chan) (0x10 | (chan))
+#define AD5755_READ_REG_SLEW(chan) (0x14 | (chan))
+#define AD5755_READ_REG_STATUS 0x18
+#define AD5755_READ_REG_MAIN 0x19
+#define AD5755_READ_REG_DC_DC 0x1a
+
+#define AD5755_CTRL_REG_SLEW 0x0
+#define AD5755_CTRL_REG_MAIN 0x1
+#define AD5755_CTRL_REG_DAC 0x2
+#define AD5755_CTRL_REG_DC_DC 0x3
+#define AD5755_CTRL_REG_SW 0x4
+
+#define AD5755_READ_FLAG 0x800000
+
+#define AD5755_NOOP 0x1CE000
+
+#define AD5755_DAC_INT_EN BIT(8)
+#define AD5755_DAC_CLR_EN BIT(7)
+#define AD5755_DAC_OUT_EN BIT(6)
+#define AD5755_DAC_INT_CURRENT_SENSE_RESISTOR BIT(5)
+#define AD5755_DAC_DC_DC_EN BIT(4)
+#define AD5755_DAC_VOLTAGE_OVERRANGE_EN BIT(3)
+
+#define AD5755_DC_DC_MAXV 0
+#define AD5755_DC_DC_FREQ_SHIFT 2
+#define AD5755_DC_DC_PHASE_SHIFT 4
+#define AD5755_EXT_DC_DC_COMP_RES BIT(6)
+
+#define AD5755_SLEW_STEP_SIZE_SHIFT 0
+#define AD5755_SLEW_RATE_SHIFT 3
+#define AD5755_SLEW_ENABLE BIT(12)
+
+/**
+ * struct ad5755_chip_info - chip specific information
+ * @channel_template: channel specification
+ * @calib_shift: shift for the calibration data registers
+ * @has_voltage_out: whether the chip has voltage outputs
+ */
+struct ad5755_chip_info {
+ const struct iio_chan_spec channel_template;
+ unsigned int calib_shift;
+ bool has_voltage_out;
+};
+
+/**
+ * struct ad5755_state - driver instance specific data
+ * @spi: spi device the driver is attached to
+ * @chip_info: chip model specific constants, available modes etc
+ * @pwr_down: bitmask which contains hether a channel is powered down or not
+ * @ctrl: software shadow of the channel ctrl registers
+ * @channels: iio channel spec for the device
+ * @data: spi transfer buffers
+ */
+struct ad5755_state {
+ struct spi_device *spi;
+ const struct ad5755_chip_info *chip_info;
+ unsigned int pwr_down;
+ unsigned int ctrl[AD5755_NUM_CHANNELS];
+ struct iio_chan_spec channels[AD5755_NUM_CHANNELS];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+
+ union {
+ u32 d32;
+ u8 d8[4];
+ } data[2] ____cacheline_aligned;
+};
+
+enum ad5755_type {
+ ID_AD5755,
+ ID_AD5757,
+ ID_AD5735,
+ ID_AD5737,
+};
+
+static int ad5755_write_unlocked(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int val)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+
+ st->data[0].d32 = cpu_to_be32((reg << 16) | val);
+
+ return spi_write(st->spi, &st->data[0].d8[1], 3);
+}
+
+static int ad5755_write_ctrl_unlocked(struct iio_dev *indio_dev,
+ unsigned int channel, unsigned int reg, unsigned int val)
+{
+ return ad5755_write_unlocked(indio_dev,
+ AD5755_WRITE_REG_CTRL(channel), (reg << 13) | val);
+}
+
+static int ad5755_write(struct iio_dev *indio_dev, unsigned int reg,
+ unsigned int val)
+{
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad5755_write_unlocked(indio_dev, reg, val);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5755_write_ctrl(struct iio_dev *indio_dev, unsigned int channel,
+ unsigned int reg, unsigned int val)
+{
+ int ret;
+
+ mutex_lock(&indio_dev->mlock);
+ ret = ad5755_write_ctrl_unlocked(indio_dev, channel, reg, val);
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5755_read(struct iio_dev *indio_dev, unsigned int addr)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ struct spi_message m;
+ int ret;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->data[0].d8[1],
+ .len = 3,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &st->data[1].d8[1],
+ .rx_buf = &st->data[1].d8[1],
+ .len = 3,
+ },
+ };
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t[0], &m);
+ spi_message_add_tail(&t[1], &m);
+
+ mutex_lock(&indio_dev->mlock);
+
+ st->data[0].d32 = cpu_to_be32(AD5755_READ_FLAG | (addr << 16));
+ st->data[1].d32 = cpu_to_be32(AD5755_NOOP);
+
+ ret = spi_sync(st->spi, &m);
+ if (ret >= 0)
+ ret = be32_to_cpu(st->data[1].d32) & 0xffff;
+
+ mutex_unlock(&indio_dev->mlock);
+
+ return ret;
+}
+
+static int ad5755_update_dac_ctrl(struct iio_dev *indio_dev,
+ unsigned int channel, unsigned int set, unsigned int clr)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ int ret;
+
+ st->ctrl[channel] |= set;
+ st->ctrl[channel] &= ~clr;
+
+ ret = ad5755_write_ctrl_unlocked(indio_dev, channel,
+ AD5755_CTRL_REG_DAC, st->ctrl[channel]);
+
+ return ret;
+}
+
+static int ad5755_set_channel_pwr_down(struct iio_dev *indio_dev,
+ unsigned int channel, bool pwr_down)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int mask = BIT(channel);
+
+ mutex_lock(&indio_dev->mlock);
+
+ if ((bool)(st->pwr_down & mask) == pwr_down)
+ goto out_unlock;
+
+ if (!pwr_down) {
+ st->pwr_down &= ~mask;
+ ad5755_update_dac_ctrl(indio_dev, channel,
+ AD5755_DAC_INT_EN | AD5755_DAC_DC_DC_EN, 0);
+ udelay(200);
+ ad5755_update_dac_ctrl(indio_dev, channel,
+ AD5755_DAC_OUT_EN, 0);
+ } else {
+ st->pwr_down |= mask;
+ ad5755_update_dac_ctrl(indio_dev, channel,
+ 0, AD5755_DAC_INT_EN | AD5755_DAC_OUT_EN |
+ AD5755_DAC_DC_DC_EN);
+ }
+
+out_unlock:
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+
+static const int ad5755_min_max_table[][2] = {
+ [AD5755_MODE_VOLTAGE_0V_5V] = { 0, 5000 },
+ [AD5755_MODE_VOLTAGE_0V_10V] = { 0, 10000 },
+ [AD5755_MODE_VOLTAGE_PLUSMINUS_5V] = { -5000, 5000 },
+ [AD5755_MODE_VOLTAGE_PLUSMINUS_10V] = { -10000, 10000 },
+ [AD5755_MODE_CURRENT_4mA_20mA] = { 4, 20 },
+ [AD5755_MODE_CURRENT_0mA_20mA] = { 0, 20 },
+ [AD5755_MODE_CURRENT_0mA_24mA] = { 0, 24 },
+};
+
+static void ad5755_get_min_max(struct ad5755_state *st,
+ struct iio_chan_spec const *chan, int *min, int *max)
+{
+ enum ad5755_mode mode = st->ctrl[chan->channel] & 7;
+ *min = ad5755_min_max_table[mode][0];
+ *max = ad5755_min_max_table[mode][1];
+}
+
+static inline int ad5755_get_offset(struct ad5755_state *st,
+ struct iio_chan_spec const *chan)
+{
+ int min, max;
+
+ ad5755_get_min_max(st, chan, &min, &max);
+ return (min * (1 << chan->scan_type.realbits)) / (max - min);
+}
+
+static inline int ad5755_get_scale(struct ad5755_state *st,
+ struct iio_chan_spec const *chan)
+{
+ int min, max;
+
+ ad5755_get_min_max(st, chan, &min, &max);
+ return ((max - min) * 1000000000ULL) >> chan->scan_type.realbits;
+}
+
+static int ad5755_chan_reg_info(struct ad5755_state *st,
+ struct iio_chan_spec const *chan, long info, bool write,
+ unsigned int *reg, unsigned int *shift, unsigned int *offset)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ if (write)
+ *reg = AD5755_WRITE_REG_DATA(chan->address);
+ else
+ *reg = AD5755_READ_REG_DATA(chan->address);
+ *shift = chan->scan_type.shift;
+ *offset = 0;
+ break;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (write)
+ *reg = AD5755_WRITE_REG_OFFSET(chan->address);
+ else
+ *reg = AD5755_READ_REG_OFFSET(chan->address);
+ *shift = st->chip_info->calib_shift;
+ *offset = 32768;
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (write)
+ *reg = AD5755_WRITE_REG_GAIN(chan->address);
+ else
+ *reg = AD5755_READ_REG_GAIN(chan->address);
+ *shift = st->chip_info->calib_shift;
+ *offset = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ad5755_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int reg, shift, offset;
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = ad5755_get_scale(st, chan);
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = ad5755_get_offset(st, chan);
+ return IIO_VAL_INT;
+ default:
+ ret = ad5755_chan_reg_info(st, chan, info, false,
+ &reg, &shift, &offset);
+ if (ret)
+ return ret;
+
+ ret = ad5755_read(indio_dev, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = (ret - offset) >> shift;
+
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ad5755_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int val, int val2, long info)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int shift, reg, offset;
+ int ret;
+
+ ret = ad5755_chan_reg_info(st, chan, info, true,
+ &reg, &shift, &offset);
+ if (ret)
+ return ret;
+
+ val <<= shift;
+ val += offset;
+
+ if (val < 0 || val > 0xffff)
+ return -EINVAL;
+
+ return ad5755_write(indio_dev, reg, val);
+}
+
+static ssize_t ad5755_read_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n",
+ (bool)(st->pwr_down & (1 << chan->channel)));
+}
+
+static ssize_t ad5755_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
+ struct iio_chan_spec const *chan, const char *buf, size_t len)
+{
+ bool pwr_down;
+ int ret;
+
+ ret = strtobool(buf, &pwr_down);
+ if (ret)
+ return ret;
+
+ ret = ad5755_set_channel_pwr_down(indio_dev, chan->channel, pwr_down);
+ return ret ? ret : len;
+}
+
+static const struct iio_info ad5755_info = {
+ .read_raw = ad5755_read_raw,
+ .write_raw = ad5755_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static const struct iio_chan_spec_ext_info ad5755_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ad5755_read_powerdown,
+ .write = ad5755_write_powerdown,
+ },
+ { },
+};
+
+#define AD5755_CHANNEL(_bits) { \
+ .indexed = 1, \
+ .output = 1, \
+ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+ IIO_CHAN_INFO_SCALE_SEPARATE_BIT | \
+ IIO_CHAN_INFO_OFFSET_SEPARATE_BIT | \
+ IIO_CHAN_INFO_CALIBSCALE_SEPARATE_BIT | \
+ IIO_CHAN_INFO_CALIBBIAS_SEPARATE_BIT, \
+ .scan_type = IIO_ST('u', (_bits), 16, 16 - (_bits)), \
+ .ext_info = ad5755_ext_info, \
+}
+
+static const struct ad5755_chip_info ad5755_chip_info_tbl[] = {
+ [ID_AD5735] = {
+ .channel_template = AD5755_CHANNEL(14),
+ .has_voltage_out = true,
+ .calib_shift = 4,
+ },
+ [ID_AD5737] = {
+ .channel_template = AD5755_CHANNEL(14),
+ .has_voltage_out = false,
+ .calib_shift = 4,
+ },
+ [ID_AD5755] = {
+ .channel_template = AD5755_CHANNEL(16),
+ .has_voltage_out = true,
+ .calib_shift = 0,
+ },
+ [ID_AD5757] = {
+ .channel_template = AD5755_CHANNEL(16),
+ .has_voltage_out = false,
+ .calib_shift = 0,
+ },
+};
+
+static bool ad5755_is_valid_mode(struct ad5755_state *st, enum ad5755_mode mode)
+{
+ switch (mode) {
+ case AD5755_MODE_VOLTAGE_0V_5V:
+ case AD5755_MODE_VOLTAGE_0V_10V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
+ return st->chip_info->has_voltage_out;
+ case AD5755_MODE_CURRENT_4mA_20mA:
+ case AD5755_MODE_CURRENT_0mA_20mA:
+ case AD5755_MODE_CURRENT_0mA_24mA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int __devinit ad5755_setup_pdata(struct iio_dev *indio_dev,
+ const struct ad5755_platform_data *pdata)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ unsigned int val;
+ unsigned int i;
+ int ret;
+
+ if (pdata->dc_dc_phase > AD5755_DC_DC_PHASE_90_DEGREE ||
+ pdata->dc_dc_freq > AD5755_DC_DC_FREQ_650kHZ ||
+ pdata->dc_dc_maxv > AD5755_DC_DC_MAXV_29V5)
+ return -EINVAL;
+
+ val = pdata->dc_dc_maxv << AD5755_DC_DC_MAXV;
+ val |= pdata->dc_dc_freq << AD5755_DC_DC_FREQ_SHIFT;
+ val |= pdata->dc_dc_phase << AD5755_DC_DC_PHASE_SHIFT;
+ if (pdata->ext_dc_dc_compenstation_resistor)
+ val |= AD5755_EXT_DC_DC_COMP_RES;
+
+ ret = ad5755_write_ctrl(indio_dev, 0, AD5755_CTRL_REG_DC_DC, val);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) {
+ val = pdata->dac[i].slew.step_size <<
+ AD5755_SLEW_STEP_SIZE_SHIFT;
+ val |= pdata->dac[i].slew.rate <<
+ AD5755_SLEW_RATE_SHIFT;
+ if (pdata->dac[i].slew.enable)
+ val |= AD5755_SLEW_ENABLE;
+
+ ret = ad5755_write_ctrl(indio_dev, i,
+ AD5755_CTRL_REG_SLEW, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) {
+ if (!ad5755_is_valid_mode(st, pdata->dac[i].mode))
+ return -EINVAL;
+
+ val = 0;
+ if (!pdata->dac[i].ext_current_sense_resistor)
+ val |= AD5755_DAC_INT_CURRENT_SENSE_RESISTOR;
+ if (pdata->dac[i].enable_voltage_overrange)
+ val |= AD5755_DAC_VOLTAGE_OVERRANGE_EN;
+ val |= pdata->dac[i].mode;
+
+ ret = ad5755_update_dac_ctrl(indio_dev, i, val, 0);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool __devinit ad5755_is_voltage_mode(enum ad5755_mode mode)
+{
+ switch (mode) {
+ case AD5755_MODE_VOLTAGE_0V_5V:
+ case AD5755_MODE_VOLTAGE_0V_10V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
+ case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int __devinit ad5755_init_channels(struct iio_dev *indio_dev,
+ const struct ad5755_platform_data *pdata)
+{
+ struct ad5755_state *st = iio_priv(indio_dev);
+ struct iio_chan_spec *channels = st->channels;
+ unsigned int i;
+
+ for (i = 0; i < AD5755_NUM_CHANNELS; ++i) {
+ channels[i] = st->chip_info->channel_template;
+ channels[i].channel = i;
+ channels[i].address = i;
+ if (pdata && ad5755_is_voltage_mode(pdata->dac[i].mode))
+ channels[i].type = IIO_VOLTAGE;
+ else
+ channels[i].type = IIO_CURRENT;
+ }
+
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+#define AD5755_DEFAULT_DAC_PDATA { \
+ .mode = AD5755_MODE_CURRENT_4mA_20mA, \
+ .ext_current_sense_resistor = true, \
+ .enable_voltage_overrange = false, \
+ .slew = { \
+ .enable = false, \
+ .rate = AD5755_SLEW_RATE_64k, \
+ .step_size = AD5755_SLEW_STEP_SIZE_1, \
+ }, \
+ }
+
+static const struct ad5755_platform_data ad5755_default_pdata = {
+ .ext_dc_dc_compenstation_resistor = false,
+ .dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE,
+ .dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ,
+ .dc_dc_maxv = AD5755_DC_DC_MAXV_23V,
+ .dac = {
+ [0] = AD5755_DEFAULT_DAC_PDATA,
+ [1] = AD5755_DEFAULT_DAC_PDATA,
+ [2] = AD5755_DEFAULT_DAC_PDATA,
+ [3] = AD5755_DEFAULT_DAC_PDATA,
+ },
+};
+
+static int __devinit ad5755_probe(struct spi_device *spi)
+{
+ enum ad5755_type type = spi_get_device_id(spi)->driver_data;
+ const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev);
+ struct iio_dev *indio_dev;
+ struct ad5755_state *st;
+ int ret;
+
+ indio_dev = iio_device_alloc(sizeof(*st));
+ if (indio_dev == NULL) {
+ dev_err(&spi->dev, "Failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ st->chip_info = &ad5755_chip_info_tbl[type];
+ st->spi = spi;
+ st->pwr_down = 0xf;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad5755_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = AD5755_NUM_CHANNELS;
+
+ if (!pdata)
+ pdata = &ad5755_default_pdata;
+
+ ret = ad5755_init_channels(indio_dev, pdata);
+ if (ret)
+ goto error_free;
+
+ ret = ad5755_setup_pdata(indio_dev, pdata);
+ if (ret)
+ goto error_free;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register iio device: %d\n", ret);
+ goto error_free;
+ }
+
+ return 0;
+
+error_free:
+ iio_device_free(indio_dev);
+
+ return ret;
+}
+
+static int __devexit ad5755_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ iio_device_unregister(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id ad5755_id[] = {
+ { "ad5755", ID_AD5755 },
+ { "ad5755-1", ID_AD5755 },
+ { "ad5757", ID_AD5757 },
+ { "ad5735", ID_AD5735 },
+ { "ad5737", ID_AD5737 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad5755_id);
+
+static struct spi_driver ad5755_driver = {
+ .driver = {
+ .name = "ad5755",
+ .owner = THIS_MODULE,
+ },
+ .probe = ad5755_probe,
+ .remove = __devexit_p(ad5755_remove),
+ .id_table = ad5755_id,
+};
+module_spi_driver(ad5755_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD5755/55-1/57/35/37 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
new file mode 100644
index 000000000000..21e27e2fc68c
--- /dev/null
+++ b/drivers/iio/gyro/Kconfig
@@ -0,0 +1,16 @@
+#
+# IIO Digital Gyroscope Sensor drivers configuration
+#
+menu "Digital gyroscope sensors"
+
+config HID_SENSOR_GYRO_3D
+ depends on HID_SENSOR_HUB
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select HID_SENSOR_IIO_COMMON
+ tristate "HID Gyroscope 3D"
+ help
+ Say yes here to build support for the HID SENSOR
+ Gyroscope 3D.
+
+endmenu
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
new file mode 100644
index 000000000000..8a895d9fcbce
--- /dev/null
+++ b/drivers/iio/gyro/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O gyroscope sensor drivers
+#
+
+obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
new file mode 100644
index 000000000000..4c56ada51c39
--- /dev/null
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -0,0 +1,418 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-attributes.h"
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Gyro-3D: 0x200076*/
+#define DRIVER_NAME "HID-SENSOR-200076"
+
+enum gyro_3d_channel {
+ CHANNEL_SCAN_INDEX_X,
+ CHANNEL_SCAN_INDEX_Y,
+ CHANNEL_SCAN_INDEX_Z,
+ GYRO_3D_CHANNEL_MAX,
+};
+
+struct gyro_3d_state {
+ struct hid_sensor_hub_callbacks callbacks;
+ struct hid_sensor_iio_common common_attributes;
+ struct hid_sensor_hub_attribute_info gyro[GYRO_3D_CHANNEL_MAX];
+ u32 gyro_val[GYRO_3D_CHANNEL_MAX];
+};
+
+static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = {
+ HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS,
+ HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS,
+ HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec gyro_3d_channels[] = {
+ {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_X,
+ }, {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_Y,
+ }, {
+ .type = IIO_ANGL_VEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_Z,
+ }
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void gyro_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+ int channel, int size)
+{
+ channels[channel].scan_type.sign = 's';
+ /* Real storage bits will change based on the report desc. */
+ channels[channel].scan_type.realbits = size * 8;
+ /* Maximum size of a sample to capture is u32 */
+ channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int gyro_3d_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+ int report_id = -1;
+ u32 address;
+ int ret;
+ int ret_type;
+
+ *val = 0;
+ *val2 = 0;
+ switch (mask) {
+ case 0:
+ report_id = gyro_state->gyro[chan->scan_index].report_id;
+ address = gyro_3d_addresses[chan->scan_index];
+ if (report_id >= 0)
+ *val = sensor_hub_input_attr_get_raw_value(
+ gyro_state->common_attributes.hsdev,
+ HID_USAGE_SENSOR_GYRO_3D, address,
+ report_id);
+ else {
+ *val = 0;
+ return -EINVAL;
+ }
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = gyro_state->gyro[CHANNEL_SCAN_INDEX_X].units;
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = hid_sensor_convert_exponent(
+ gyro_state->gyro[CHANNEL_SCAN_INDEX_X].unit_expo);
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_read_samp_freq_value(
+ &gyro_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_read_raw_hyst_value(
+ &gyro_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret_type = -EINVAL;
+ break;
+ }
+
+ return ret_type;
+}
+
+/* Channel write_raw handler */
+static int gyro_3d_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_write_samp_freq_value(
+ &gyro_state->common_attributes, val, val2);
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_write_raw_hyst_value(
+ &gyro_state->common_attributes, val, val2);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int gyro_3d_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static const struct iio_info gyro_3d_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &gyro_3d_read_raw,
+ .write_raw = &gyro_3d_write_raw,
+ .write_raw_get_fmt = &gyro_3d_write_raw_get_fmt,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
+{
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int datum_sz;
+
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ if (!buffer) {
+ dev_err(&indio_dev->dev, "Buffer == NULL\n");
+ return;
+ }
+ datum_sz = buffer->access->get_bytes_per_datum(buffer);
+ if (len > datum_sz) {
+ dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+ datum_sz);
+ return;
+ }
+ iio_push_to_buffer(buffer, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int gyro_3d_proc_event(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "gyro_3d_proc_event [%d]\n",
+ gyro_state->common_attributes.data_ready);
+ if (gyro_state->common_attributes.data_ready)
+ hid_sensor_push_data(indio_dev,
+ (u8 *)gyro_state->gyro_val,
+ sizeof(gyro_state->gyro_val));
+
+ return 0;
+}
+
+/* Capture samples in local storage */
+static int gyro_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct gyro_3d_state *gyro_state = iio_priv(indio_dev);
+ int offset;
+ int ret = -EINVAL;
+
+ switch (usage_id) {
+ case HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS:
+ case HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS:
+ case HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS:
+ offset = usage_id - HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS;
+ gyro_state->gyro_val[CHANNEL_SCAN_INDEX_X + offset] =
+ *(u32 *)raw_data;
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int gyro_3d_parse_report(struct platform_device *pdev,
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ unsigned usage_id,
+ struct gyro_3d_state *st)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
+ ret = sensor_hub_input_get_attribute_info(hsdev,
+ HID_INPUT_REPORT,
+ usage_id,
+ HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS + i,
+ &st->gyro[CHANNEL_SCAN_INDEX_X + i]);
+ if (ret < 0)
+ break;
+ gyro_3d_adjust_channel_bit_mask(channels,
+ CHANNEL_SCAN_INDEX_X + i,
+ st->gyro[CHANNEL_SCAN_INDEX_X + i].size);
+ }
+ dev_dbg(&pdev->dev, "gyro_3d %x:%x, %x:%x, %x:%x\n",
+ st->gyro[0].index,
+ st->gyro[0].report_id,
+ st->gyro[1].index, st->gyro[1].report_id,
+ st->gyro[2].index, st->gyro[2].report_id);
+
+ return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int __devinit hid_gyro_3d_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ static const char *name = "gyro_3d";
+ struct iio_dev *indio_dev;
+ struct gyro_3d_state *gyro_state;
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_chan_spec *channels;
+
+ indio_dev = iio_device_alloc(sizeof(struct gyro_3d_state));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ platform_set_drvdata(pdev, indio_dev);
+
+ gyro_state = iio_priv(indio_dev);
+ gyro_state->common_attributes.hsdev = hsdev;
+ gyro_state->common_attributes.pdev = pdev;
+
+ ret = hid_sensor_parse_common_attributes(hsdev,
+ HID_USAGE_SENSOR_GYRO_3D,
+ &gyro_state->common_attributes);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup common attributes\n");
+ goto error_free_dev;
+ }
+
+ channels = kmemdup(gyro_3d_channels,
+ sizeof(gyro_3d_channels),
+ GFP_KERNEL);
+ if (!channels) {
+ dev_err(&pdev->dev, "failed to duplicate channels\n");
+ goto error_free_dev;
+ }
+
+ ret = gyro_3d_parse_report(pdev, hsdev, channels,
+ HID_USAGE_SENSOR_GYRO_3D, gyro_state);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup attributes\n");
+ goto error_free_dev_mem;
+ }
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels = ARRAY_SIZE(gyro_3d_channels);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &gyro_3d_info;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ NULL, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+ goto error_free_dev_mem;
+ }
+ gyro_state->common_attributes.data_ready = false;
+ ret = hid_sensor_setup_trigger(indio_dev, name,
+ &gyro_state->common_attributes);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "trigger setup failed\n");
+ goto error_unreg_buffer_funcs;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_trigger;
+ }
+
+ gyro_state->callbacks.send_event = gyro_3d_proc_event;
+ gyro_state->callbacks.capture_sample = gyro_3d_capture_sample;
+ gyro_state->callbacks.pdev = pdev;
+ ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D,
+ &gyro_state->callbacks);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "callback reg failed\n");
+ goto error_iio_unreg;
+ }
+
+ return ret;
+
+error_iio_unreg:
+ iio_device_unregister(indio_dev);
+error_remove_trigger:
+ hid_sensor_remove_trigger(indio_dev);
+error_unreg_buffer_funcs:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_free_dev_mem:
+ kfree(indio_dev->channels);
+error_free_dev:
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int __devinit hid_gyro_3d_remove(struct platform_device *pdev)
+{
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_GYRO_3D);
+ iio_device_unregister(indio_dev);
+ hid_sensor_remove_trigger(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ kfree(indio_dev->channels);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver hid_gyro_3d_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = hid_gyro_3d_probe,
+ .remove = hid_gyro_3d_remove,
+};
+module_platform_driver(hid_gyro_3d_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Gyroscope 3D");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 4add9bb40eeb..d4ad37455a67 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -422,7 +422,7 @@ ssize_t iio_buffer_store_enable(struct device *dev,
ret = indio_dev->setup_ops->preenable(indio_dev);
if (ret) {
printk(KERN_ERR
- "Buffer not started:"
+ "Buffer not started: "
"buffer preenable failed\n");
goto error_ret;
}
@@ -431,12 +431,12 @@ ssize_t iio_buffer_store_enable(struct device *dev,
ret = buffer->access->request_update(buffer);
if (ret) {
printk(KERN_INFO
- "Buffer not started:"
+ "Buffer not started: "
"buffer parameter update failed\n");
goto error_ret;
}
}
- /* Definitely possible for devices to support both of these.*/
+ /* Definitely possible for devices to support both of these. */
if (indio_dev->modes & INDIO_BUFFER_TRIGGERED) {
if (!indio_dev->trig) {
printk(KERN_INFO
@@ -456,7 +456,7 @@ ssize_t iio_buffer_store_enable(struct device *dev,
ret = indio_dev->setup_ops->postenable(indio_dev);
if (ret) {
printk(KERN_INFO
- "Buffer not started:"
+ "Buffer not started: "
"postenable failed\n");
indio_dev->currentmode = previous_mode;
if (indio_dev->setup_ops->postdisable)
@@ -657,7 +657,7 @@ EXPORT_SYMBOL_GPL(iio_scan_mask_query);
/**
* struct iio_demux_table() - table describing demux memcpy ops
* @from: index to copy from
- * @to: index to copy to
+ * @to: index to copy to
* @length: how many bytes to copy
* @l: list head used for management
*/
@@ -682,12 +682,11 @@ static unsigned char *iio_demux(struct iio_buffer *buffer,
return buffer->demux_bounce;
}
-int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data,
- s64 timestamp)
+int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data)
{
unsigned char *dataout = iio_demux(buffer, data);
- return buffer->access->store_to(buffer, dataout, timestamp);
+ return buffer->access->store_to(buffer, dataout);
}
EXPORT_SYMBOL_GPL(iio_push_to_buffer);
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 2ec266ef41a3..6eb24dbc081e 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -29,7 +29,7 @@
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
-/* IDA to assign each registered device a unique id*/
+/* IDA to assign each registered device a unique id */
static DEFINE_IDA(iio_ida);
static dev_t iio_devt;
@@ -99,6 +99,7 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_FREQUENCY] = "frequency",
[IIO_CHAN_INFO_PHASE] = "phase",
[IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain",
+ [IIO_CHAN_INFO_HYSTERESIS] = "hysteresis",
};
const struct iio_chan_spec
@@ -365,6 +366,7 @@ static ssize_t iio_read_channel_info(struct device *dev,
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
+ unsigned long long tmp;
int val, val2;
bool scale_db = false;
int ret = indio_dev->info->read_raw(indio_dev, this_attr->c,
@@ -390,6 +392,11 @@ static ssize_t iio_read_channel_info(struct device *dev,
return sprintf(buf, "-%d.%09u\n", val, -val2);
else
return sprintf(buf, "%d.%09u\n", val, val2);
+ case IIO_VAL_FRACTIONAL:
+ tmp = div_s64((s64)val * 1000000000LL, val2);
+ val2 = do_div(tmp, 1000000000LL);
+ val = tmp;
+ return sprintf(buf, "%d.%09u\n", val, val2);
default:
return 0;
}
@@ -729,7 +736,7 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
attrcount = attrcount_orig;
/*
* New channel registration method - relies on the fact a group does
- * not need to be initialized if it is name is NULL.
+ * not need to be initialized if its name is NULL.
*/
if (indio_dev->channels)
for (i = 0; i < indio_dev->num_channels; i++) {
@@ -980,6 +987,6 @@ EXPORT_SYMBOL(iio_device_unregister);
subsys_initcall(iio_init);
module_exit(iio_exit);
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Industrial I/O core");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index b5afc2ff34fd..f2b78d4fe457 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -111,6 +111,7 @@ struct iio_channel *iio_channel_get(const char *name, const char *channel_name)
{
struct iio_map_internal *c_i = NULL, *c = NULL;
struct iio_channel *channel;
+ int err;
if (name == NULL && channel_name == NULL)
return ERR_PTR(-ENODEV);
@@ -130,18 +131,32 @@ struct iio_channel *iio_channel_get(const char *name, const char *channel_name)
if (c == NULL)
return ERR_PTR(-ENODEV);
- channel = kmalloc(sizeof(*channel), GFP_KERNEL);
- if (channel == NULL)
- return ERR_PTR(-ENOMEM);
+ channel = kzalloc(sizeof(*channel), GFP_KERNEL);
+ if (channel == NULL) {
+ err = -ENOMEM;
+ goto error_no_mem;
+ }
channel->indio_dev = c->indio_dev;
- if (c->map->adc_channel_label)
+ if (c->map->adc_channel_label) {
channel->channel =
iio_chan_spec_from_name(channel->indio_dev,
c->map->adc_channel_label);
+ if (channel->channel == NULL) {
+ err = -EINVAL;
+ goto error_no_chan;
+ }
+ }
+
return channel;
+
+error_no_chan:
+ kfree(channel);
+error_no_mem:
+ iio_device_put(c->indio_dev);
+ return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(iio_channel_get);
@@ -229,9 +244,21 @@ void iio_channel_release_all(struct iio_channel *channels)
}
EXPORT_SYMBOL_GPL(iio_channel_release_all);
+static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
+ enum iio_chan_info_enum info)
+{
+ int unused;
+
+ if (val2 == NULL)
+ val2 = &unused;
+
+ return chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel,
+ val, val2, info);
+}
+
int iio_read_channel_raw(struct iio_channel *chan, int *val)
{
- int val2, ret;
+ int ret;
mutex_lock(&chan->indio_dev->info_exist_lock);
if (chan->indio_dev->info == NULL) {
@@ -239,8 +266,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val)
goto err_unlock;
}
- ret = chan->indio_dev->info->read_raw(chan->indio_dev, chan->channel,
- val, &val2, 0);
+ ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
@@ -248,6 +274,100 @@ err_unlock:
}
EXPORT_SYMBOL_GPL(iio_read_channel_raw);
+static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
+ int raw, int *processed, unsigned int scale)
+{
+ int scale_type, scale_val, scale_val2, offset;
+ s64 raw64 = raw;
+ int ret;
+
+ ret = iio_channel_read(chan, &offset, NULL, IIO_CHAN_INFO_SCALE);
+ if (ret == 0)
+ raw64 += offset;
+
+ scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
+ IIO_CHAN_INFO_SCALE);
+ if (scale_type < 0)
+ return scale_type;
+
+ switch (scale_type) {
+ case IIO_VAL_INT:
+ *processed = raw64 * scale_val;
+ break;
+ case IIO_VAL_INT_PLUS_MICRO:
+ if (scale_val2 < 0)
+ *processed = -raw64 * scale_val;
+ else
+ *processed = raw64 * scale_val;
+ *processed += div_s64(raw64 * (s64)scale_val2 * scale,
+ 1000000LL);
+ break;
+ case IIO_VAL_INT_PLUS_NANO:
+ if (scale_val2 < 0)
+ *processed = -raw64 * scale_val;
+ else
+ *processed = raw64 * scale_val;
+ *processed += div_s64(raw64 * (s64)scale_val2 * scale,
+ 1000000000LL);
+ break;
+ case IIO_VAL_FRACTIONAL:
+ *processed = div_s64(raw64 * (s64)scale_val * scale,
+ scale_val2);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
+ int *processed, unsigned int scale)
+{
+ int ret;
+
+ mutex_lock(&chan->indio_dev->info_exist_lock);
+ if (chan->indio_dev->info == NULL) {
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed,
+ scale);
+err_unlock:
+ mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
+
+int iio_read_channel_processed(struct iio_channel *chan, int *val)
+{
+ int ret;
+
+ mutex_lock(&chan->indio_dev->info_exist_lock);
+ if (chan->indio_dev->info == NULL) {
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) {
+ ret = iio_channel_read(chan, val, NULL,
+ IIO_CHAN_INFO_PROCESSED);
+ } else {
+ ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW);
+ if (ret < 0)
+ goto err_unlock;
+ ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, 1);
+ }
+
+err_unlock:
+ mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_processed);
+
int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
{
int ret;
@@ -258,10 +378,7 @@ int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
goto err_unlock;
}
- ret = chan->indio_dev->info->read_raw(chan->indio_dev,
- chan->channel,
- val, val2,
- IIO_CHAN_INFO_SCALE);
+ ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
diff --git a/drivers/iio/kfifo_buf.c b/drivers/iio/kfifo_buf.c
index 6bf9d05f4841..5bc5c860e9ca 100644
--- a/drivers/iio/kfifo_buf.c
+++ b/drivers/iio/kfifo_buf.c
@@ -6,6 +6,7 @@
#include <linux/kfifo.h>
#include <linux/mutex.h>
#include <linux/iio/kfifo_buf.h>
+#include <linux/sched.h>
struct iio_kfifo {
struct iio_buffer buffer;
@@ -22,7 +23,8 @@ static inline int __iio_allocate_kfifo(struct iio_kfifo *buf,
return -EINVAL;
__iio_update_buffer(&buf->buffer, bytes_per_datum, length);
- return kfifo_alloc(&buf->kf, bytes_per_datum*length, GFP_KERNEL);
+ return __kfifo_alloc((struct __kfifo *)&buf->kf, length,
+ bytes_per_datum, GFP_KERNEL);
}
static int iio_request_update_kfifo(struct iio_buffer *r)
@@ -35,6 +37,7 @@ static int iio_request_update_kfifo(struct iio_buffer *r)
kfifo_free(&buf->kf);
ret = __iio_allocate_kfifo(buf, buf->buffer.bytes_per_datum,
buf->buffer.length);
+ r->stufftoread = false;
error_ret:
return ret;
}
@@ -81,6 +84,9 @@ static int iio_set_bytes_per_datum_kfifo(struct iio_buffer *r, size_t bpd)
static int iio_set_length_kfifo(struct iio_buffer *r, int length)
{
+ /* Avoid an invalid state */
+ if (length < 2)
+ length = 2;
if (r->length != length) {
r->length = length;
iio_mark_update_needed_kfifo(r);
@@ -89,14 +95,16 @@ static int iio_set_length_kfifo(struct iio_buffer *r, int length)
}
static int iio_store_to_kfifo(struct iio_buffer *r,
- u8 *data,
- s64 timestamp)
+ u8 *data)
{
int ret;
struct iio_kfifo *kf = iio_to_kfifo(r);
- ret = kfifo_in(&kf->kf, data, r->bytes_per_datum);
- if (ret != r->bytes_per_datum)
+ ret = kfifo_in(&kf->kf, data, 1);
+ if (ret != 1)
return -EBUSY;
+ r->stufftoread = true;
+ wake_up_interruptible(&r->pollq);
+
return 0;
}
@@ -106,11 +114,18 @@ static int iio_read_first_n_kfifo(struct iio_buffer *r,
int ret, copied;
struct iio_kfifo *kf = iio_to_kfifo(r);
- if (n < r->bytes_per_datum)
+ if (n < r->bytes_per_datum || r->bytes_per_datum == 0)
return -EINVAL;
- n = rounddown(n, r->bytes_per_datum);
ret = kfifo_to_user(&kf->kf, buf, n, &copied);
+ if (ret < 0)
+ return ret;
+
+ if (kfifo_is_empty(&kf->kf))
+ r->stufftoread = false;
+ /* verify it is still empty to avoid race */
+ if (!kfifo_is_empty(&kf->kf))
+ r->stufftoread = true;
return copied;
}
@@ -136,7 +151,7 @@ struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev)
iio_buffer_init(&kf->buffer);
kf->buffer.attrs = &iio_kfifo_attribute_group;
kf->buffer.access = &kfifo_access_funcs;
-
+ kf->buffer.length = 2;
return &kf->buffer;
}
EXPORT_SYMBOL(iio_kfifo_allocate);
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 91d15d2f694f..1763c9bcb98a 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -42,4 +42,14 @@ config VCNL4000
To compile this driver as a module, choose M here: the
module will be called vcnl4000.
+config HID_SENSOR_ALS
+ depends on HID_SENSOR_HUB
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select HID_SENSOR_IIO_COMMON
+ tristate "HID ALS"
+ help
+ Say yes here to build support for the HID SENSOR
+ Ambient light sensor.
+
endmenu
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 13f8a782d292..21a8f0df1407 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -5,3 +5,4 @@
obj-$(CONFIG_ADJD_S311) += adjd_s311.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_VCNL4000) += vcnl4000.o
+obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c
index 9a99f43094f0..164b62b91a4b 100644
--- a/drivers/iio/light/adjd_s311.c
+++ b/drivers/iio/light/adjd_s311.c
@@ -187,7 +187,7 @@ static irqreturn_t adjd_s311_trigger_handler(int irq, void *p)
if (indio_dev->scan_timestamp)
*(s64 *)((u8 *)data->buffer + ALIGN(len, sizeof(s64)))
= time_ns;
- iio_push_to_buffer(buffer, (u8 *)data->buffer, time_ns);
+ iio_push_to_buffer(buffer, (u8 *)data->buffer);
done:
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
new file mode 100644
index 000000000000..96e3691e42c4
--- /dev/null
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -0,0 +1,385 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-attributes.h"
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Accelerometer-3D: 0x200041*/
+#define DRIVER_NAME "HID-SENSOR-200041"
+
+#define CHANNEL_SCAN_INDEX_ILLUM 0
+
+struct als_state {
+ struct hid_sensor_hub_callbacks callbacks;
+ struct hid_sensor_iio_common common_attributes;
+ struct hid_sensor_hub_attribute_info als_illum;
+ u32 illum;
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec als_channels[] = {
+ {
+ .type = IIO_INTENSITY,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_BOTH,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_ILLUM,
+ }
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void als_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+ int channel, int size)
+{
+ channels[channel].scan_type.sign = 's';
+ /* Real storage bits will change based on the report desc. */
+ channels[channel].scan_type.realbits = size * 8;
+ /* Maximum size of a sample to capture is u32 */
+ channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int als_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct als_state *als_state = iio_priv(indio_dev);
+ int report_id = -1;
+ u32 address;
+ int ret;
+ int ret_type;
+
+ *val = 0;
+ *val2 = 0;
+ switch (mask) {
+ case 0:
+ switch (chan->scan_index) {
+ case CHANNEL_SCAN_INDEX_ILLUM:
+ report_id = als_state->als_illum.report_id;
+ address =
+ HID_USAGE_SENSOR_LIGHT_ILLUM;
+ break;
+ default:
+ report_id = -1;
+ break;
+ }
+ if (report_id >= 0)
+ *val = sensor_hub_input_attr_get_raw_value(
+ als_state->common_attributes.hsdev,
+ HID_USAGE_SENSOR_ALS, address,
+ report_id);
+ else {
+ *val = 0;
+ return -EINVAL;
+ }
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = als_state->als_illum.units;
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = hid_sensor_convert_exponent(
+ als_state->als_illum.unit_expo);
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_read_samp_freq_value(
+ &als_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_read_raw_hyst_value(
+ &als_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret_type = -EINVAL;
+ break;
+ }
+
+ return ret_type;
+}
+
+/* Channel write_raw handler */
+static int als_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct als_state *als_state = iio_priv(indio_dev);
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_write_samp_freq_value(
+ &als_state->common_attributes, val, val2);
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_write_raw_hyst_value(
+ &als_state->common_attributes, val, val2);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int als_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static const struct iio_info als_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &als_read_raw,
+ .write_raw = &als_write_raw,
+ .write_raw_get_fmt = &als_write_raw_get_fmt,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
+{
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int datum_sz;
+
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ if (!buffer) {
+ dev_err(&indio_dev->dev, "Buffer == NULL\n");
+ return;
+ }
+ datum_sz = buffer->access->get_bytes_per_datum(buffer);
+ if (len > datum_sz) {
+ dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+ datum_sz);
+ return;
+ }
+ iio_push_to_buffer(buffer, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int als_proc_event(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct als_state *als_state = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "als_proc_event [%d]\n",
+ als_state->common_attributes.data_ready);
+ if (als_state->common_attributes.data_ready)
+ hid_sensor_push_data(indio_dev,
+ (u8 *)&als_state->illum,
+ sizeof(als_state->illum));
+
+ return 0;
+}
+
+/* Capture samples in local storage */
+static int als_capture_sample(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct als_state *als_state = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ switch (usage_id) {
+ case HID_USAGE_SENSOR_LIGHT_ILLUM:
+ als_state->illum = *(u32 *)raw_data;
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int als_parse_report(struct platform_device *pdev,
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ unsigned usage_id,
+ struct als_state *st)
+{
+ int ret;
+
+ ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
+ usage_id,
+ HID_USAGE_SENSOR_LIGHT_ILLUM,
+ &st->als_illum);
+ if (ret < 0)
+ return ret;
+ als_adjust_channel_bit_mask(channels, CHANNEL_SCAN_INDEX_ILLUM,
+ st->als_illum.size);
+
+ dev_dbg(&pdev->dev, "als %x:%x\n", st->als_illum.index,
+ st->als_illum.report_id);
+
+ return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int __devinit hid_als_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ static const char *name = "als";
+ struct iio_dev *indio_dev;
+ struct als_state *als_state;
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_chan_spec *channels;
+
+ indio_dev = iio_device_alloc(sizeof(struct als_state));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ platform_set_drvdata(pdev, indio_dev);
+
+ als_state = iio_priv(indio_dev);
+ als_state->common_attributes.hsdev = hsdev;
+ als_state->common_attributes.pdev = pdev;
+
+ ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_ALS,
+ &als_state->common_attributes);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup common attributes\n");
+ goto error_free_dev;
+ }
+
+ channels = kmemdup(als_channels,
+ sizeof(als_channels),
+ GFP_KERNEL);
+ if (!channels) {
+ dev_err(&pdev->dev, "failed to duplicate channels\n");
+ goto error_free_dev;
+ }
+
+ ret = als_parse_report(pdev, hsdev, channels,
+ HID_USAGE_SENSOR_ALS, als_state);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup attributes\n");
+ goto error_free_dev_mem;
+ }
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels =
+ ARRAY_SIZE(als_channels);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &als_info;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ NULL, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+ goto error_free_dev_mem;
+ }
+ als_state->common_attributes.data_ready = false;
+ ret = hid_sensor_setup_trigger(indio_dev, name,
+ &als_state->common_attributes);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "trigger setup failed\n");
+ goto error_unreg_buffer_funcs;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_trigger;
+ }
+
+ als_state->callbacks.send_event = als_proc_event;
+ als_state->callbacks.capture_sample = als_capture_sample;
+ als_state->callbacks.pdev = pdev;
+ ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_ALS,
+ &als_state->callbacks);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "callback reg failed\n");
+ goto error_iio_unreg;
+ }
+
+ return ret;
+
+error_iio_unreg:
+ iio_device_unregister(indio_dev);
+error_remove_trigger:
+ hid_sensor_remove_trigger(indio_dev);
+error_unreg_buffer_funcs:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_free_dev_mem:
+ kfree(indio_dev->channels);
+error_free_dev:
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int __devinit hid_als_remove(struct platform_device *pdev)
+{
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_ALS);
+ iio_device_unregister(indio_dev);
+ hid_sensor_remove_trigger(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ kfree(indio_dev->channels);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver hid_als_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = hid_als_probe,
+ .remove = hid_als_remove,
+};
+module_platform_driver(hid_als_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor ALS");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
new file mode 100644
index 000000000000..c1f0cdd57037
--- /dev/null
+++ b/drivers/iio/magnetometer/Kconfig
@@ -0,0 +1,16 @@
+#
+# Magnetometer sensors
+#
+menu "Magnetometer sensors"
+
+config HID_SENSOR_MAGNETOMETER_3D
+ depends on HID_SENSOR_HUB
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select HID_SENSOR_IIO_COMMON
+ tristate "HID Magenetometer 3D"
+ help
+ Say yes here to build support for the HID SENSOR
+ Magnetometer 3D.
+
+endmenu
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
new file mode 100644
index 000000000000..60dc4f2b1963
--- /dev/null
+++ b/drivers/iio/magnetometer/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for industrial I/O Magnetometer sensor drivers
+#
+
+obj-$(CONFIG_HID_SENSOR_MAGNETOMETER_3D) += hid-sensor-magn-3d.o
diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
new file mode 100644
index 000000000000..c4f0d274f577
--- /dev/null
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -0,0 +1,419 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "../common/hid-sensors/hid-sensor-attributes.h"
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+/*Format: HID-SENSOR-usage_id_in_hex*/
+/*Usage ID from spec for Magnetometer-3D: 0x200083*/
+#define DRIVER_NAME "HID-SENSOR-200083"
+
+enum magn_3d_channel {
+ CHANNEL_SCAN_INDEX_X,
+ CHANNEL_SCAN_INDEX_Y,
+ CHANNEL_SCAN_INDEX_Z,
+ MAGN_3D_CHANNEL_MAX,
+};
+
+struct magn_3d_state {
+ struct hid_sensor_hub_callbacks callbacks;
+ struct hid_sensor_iio_common common_attributes;
+ struct hid_sensor_hub_attribute_info magn[MAGN_3D_CHANNEL_MAX];
+ u32 magn_val[MAGN_3D_CHANNEL_MAX];
+};
+
+static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = {
+ HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS,
+ HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS,
+ HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec magn_3d_channels[] = {
+ {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_X,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_X,
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Y,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_Y,
+ }, {
+ .type = IIO_MAGN,
+ .modified = 1,
+ .channel2 = IIO_MOD_Z,
+ .info_mask = IIO_CHAN_INFO_OFFSET_SHARED_BIT |
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_SAMP_FREQ_SHARED_BIT |
+ IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT,
+ .scan_index = CHANNEL_SCAN_INDEX_Z,
+ }
+};
+
+/* Adjust channel real bits based on report descriptor */
+static void magn_3d_adjust_channel_bit_mask(struct iio_chan_spec *channels,
+ int channel, int size)
+{
+ channels[channel].scan_type.sign = 's';
+ /* Real storage bits will change based on the report desc. */
+ channels[channel].scan_type.realbits = size * 8;
+ /* Maximum size of a sample to capture is u32 */
+ channels[channel].scan_type.storagebits = sizeof(u32) * 8;
+}
+
+/* Channel read_raw handler */
+static int magn_3d_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct magn_3d_state *magn_state = iio_priv(indio_dev);
+ int report_id = -1;
+ u32 address;
+ int ret;
+ int ret_type;
+
+ *val = 0;
+ *val2 = 0;
+ switch (mask) {
+ case 0:
+ report_id =
+ magn_state->magn[chan->scan_index].report_id;
+ address = magn_3d_addresses[chan->scan_index];
+ if (report_id >= 0)
+ *val = sensor_hub_input_attr_get_raw_value(
+ magn_state->common_attributes.hsdev,
+ HID_USAGE_SENSOR_COMPASS_3D, address,
+ report_id);
+ else {
+ *val = 0;
+ return -EINVAL;
+ }
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = magn_state->magn[CHANNEL_SCAN_INDEX_X].units;
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = hid_sensor_convert_exponent(
+ magn_state->magn[CHANNEL_SCAN_INDEX_X].unit_expo);
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_read_samp_freq_value(
+ &magn_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_read_raw_hyst_value(
+ &magn_state->common_attributes, val, val2);
+ ret_type = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret_type = -EINVAL;
+ break;
+ }
+
+ return ret_type;
+}
+
+/* Channel write_raw handler */
+static int magn_3d_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct magn_3d_state *magn_state = iio_priv(indio_dev);
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_write_samp_freq_value(
+ &magn_state->common_attributes, val, val2);
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_write_raw_hyst_value(
+ &magn_state->common_attributes, val, val2);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int magn_3d_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static const struct iio_info magn_3d_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &magn_3d_read_raw,
+ .write_raw = &magn_3d_write_raw,
+ .write_raw_get_fmt = &magn_3d_write_raw_get_fmt,
+};
+
+/* Function to push data to buffer */
+static void hid_sensor_push_data(struct iio_dev *indio_dev, u8 *data, int len)
+{
+ struct iio_buffer *buffer = indio_dev->buffer;
+ int datum_sz;
+
+ dev_dbg(&indio_dev->dev, "hid_sensor_push_data\n");
+ if (!buffer) {
+ dev_err(&indio_dev->dev, "Buffer == NULL\n");
+ return;
+ }
+ datum_sz = buffer->access->get_bytes_per_datum(buffer);
+ if (len > datum_sz) {
+ dev_err(&indio_dev->dev, "Datum size mismatch %d:%d\n", len,
+ datum_sz);
+ return;
+ }
+ iio_push_to_buffer(buffer, (u8 *)data);
+}
+
+/* Callback handler to send event after all samples are received and captured */
+static int magn_3d_proc_event(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct magn_3d_state *magn_state = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "magn_3d_proc_event [%d]\n",
+ magn_state->common_attributes.data_ready);
+ if (magn_state->common_attributes.data_ready)
+ hid_sensor_push_data(indio_dev,
+ (u8 *)magn_state->magn_val,
+ sizeof(magn_state->magn_val));
+
+ return 0;
+}
+
+/* Capture samples in local storage */
+static int magn_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
+ unsigned usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct magn_3d_state *magn_state = iio_priv(indio_dev);
+ int offset;
+ int ret = -EINVAL;
+
+ switch (usage_id) {
+ case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS:
+ case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS:
+ case HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS:
+ offset = usage_id - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS;
+ magn_state->magn_val[CHANNEL_SCAN_INDEX_X + offset] =
+ *(u32 *)raw_data;
+ ret = 0;
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int magn_3d_parse_report(struct platform_device *pdev,
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ unsigned usage_id,
+ struct magn_3d_state *st)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i <= CHANNEL_SCAN_INDEX_Z; ++i) {
+ ret = sensor_hub_input_get_attribute_info(hsdev,
+ HID_INPUT_REPORT,
+ usage_id,
+ HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS + i,
+ &st->magn[CHANNEL_SCAN_INDEX_X + i]);
+ if (ret < 0)
+ break;
+ magn_3d_adjust_channel_bit_mask(channels,
+ CHANNEL_SCAN_INDEX_X + i,
+ st->magn[CHANNEL_SCAN_INDEX_X + i].size);
+ }
+ dev_dbg(&pdev->dev, "magn_3d %x:%x, %x:%x, %x:%x\n",
+ st->magn[0].index,
+ st->magn[0].report_id,
+ st->magn[1].index, st->magn[1].report_id,
+ st->magn[2].index, st->magn[2].report_id);
+
+ return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int __devinit hid_magn_3d_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ static char *name = "magn_3d";
+ struct iio_dev *indio_dev;
+ struct magn_3d_state *magn_state;
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_chan_spec *channels;
+
+ indio_dev = iio_device_alloc(sizeof(struct magn_3d_state));
+ if (indio_dev == NULL) {
+ ret = -ENOMEM;
+ goto error_ret;
+ }
+ platform_set_drvdata(pdev, indio_dev);
+
+ magn_state = iio_priv(indio_dev);
+ magn_state->common_attributes.hsdev = hsdev;
+ magn_state->common_attributes.pdev = pdev;
+
+ ret = hid_sensor_parse_common_attributes(hsdev,
+ HID_USAGE_SENSOR_COMPASS_3D,
+ &magn_state->common_attributes);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup common attributes\n");
+ goto error_free_dev;
+ }
+
+ channels = kmemdup(magn_3d_channels,
+ sizeof(magn_3d_channels),
+ GFP_KERNEL);
+ if (!channels) {
+ dev_err(&pdev->dev, "failed to duplicate channels\n");
+ goto error_free_dev;
+ }
+
+ ret = magn_3d_parse_report(pdev, hsdev, channels,
+ HID_USAGE_SENSOR_COMPASS_3D, magn_state);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup attributes\n");
+ goto error_free_dev_mem;
+ }
+
+ indio_dev->channels = channels;
+ indio_dev->num_channels = ARRAY_SIZE(magn_3d_channels);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &magn_3d_info;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+ NULL, NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to initialize trigger buffer\n");
+ goto error_free_dev_mem;
+ }
+ magn_state->common_attributes.data_ready = false;
+ ret = hid_sensor_setup_trigger(indio_dev, name,
+ &magn_state->common_attributes);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "trigger setup failed\n");
+ goto error_unreg_buffer_funcs;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_trigger;
+ }
+
+ magn_state->callbacks.send_event = magn_3d_proc_event;
+ magn_state->callbacks.capture_sample = magn_3d_capture_sample;
+ magn_state->callbacks.pdev = pdev;
+ ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D,
+ &magn_state->callbacks);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "callback reg failed\n");
+ goto error_iio_unreg;
+ }
+
+ return ret;
+
+error_iio_unreg:
+ iio_device_unregister(indio_dev);
+error_remove_trigger:
+ hid_sensor_remove_trigger(indio_dev);
+error_unreg_buffer_funcs:
+ iio_triggered_buffer_cleanup(indio_dev);
+error_free_dev_mem:
+ kfree(indio_dev->channels);
+error_free_dev:
+ iio_device_free(indio_dev);
+error_ret:
+ return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static int __devinit hid_magn_3d_remove(struct platform_device *pdev)
+{
+ struct hid_sensor_hub_device *hsdev = pdev->dev.platform_data;
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_COMPASS_3D);
+ iio_device_unregister(indio_dev);
+ hid_sensor_remove_trigger(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ kfree(indio_dev->channels);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static struct platform_driver hid_magn_3d_platform_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = hid_magn_3d_probe,
+ .remove = hid_magn_3d_remove,
+};
+module_platform_driver(hid_magn_3d_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Magnetometer 3D");
+MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/infiniband/hw/cxgb4/cm.c b/drivers/infiniband/hw/cxgb4/cm.c
index 51f42061dae9..6cfd4d8fd0bd 100644
--- a/drivers/infiniband/hw/cxgb4/cm.c
+++ b/drivers/infiniband/hw/cxgb4/cm.c
@@ -1361,11 +1361,11 @@ static int abort_rpl(struct c4iw_dev *dev, struct sk_buff *skb)
struct tid_info *t = dev->rdev.lldi.tids;
ep = lookup_tid(t, tid);
- PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
if (!ep) {
printk(KERN_WARNING MOD "Abort rpl to freed endpoint\n");
return 0;
}
+ PDBG("%s ep %p tid %u\n", __func__, ep, ep->hwtid);
mutex_lock(&ep->com.mutex);
switch (ep->com.state) {
case ABORTING:
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c
index 53589000fd07..8615d7cf7e01 100644
--- a/drivers/infiniband/hw/ehca/ehca_irq.c
+++ b/drivers/infiniband/hw/ehca/ehca_irq.c
@@ -42,6 +42,7 @@
*/
#include <linux/slab.h>
+#include <linux/smpboot.h>
#include "ehca_classes.h"
#include "ehca_irq.h"
@@ -652,7 +653,7 @@ void ehca_tasklet_eq(unsigned long data)
ehca_process_eq((struct ehca_shca*)data, 1);
}
-static inline int find_next_online_cpu(struct ehca_comp_pool *pool)
+static int find_next_online_cpu(struct ehca_comp_pool *pool)
{
int cpu;
unsigned long flags;
@@ -662,17 +663,20 @@ static inline int find_next_online_cpu(struct ehca_comp_pool *pool)
ehca_dmp(cpu_online_mask, cpumask_size(), "");
spin_lock_irqsave(&pool->last_cpu_lock, flags);
- cpu = cpumask_next(pool->last_cpu, cpu_online_mask);
- if (cpu >= nr_cpu_ids)
- cpu = cpumask_first(cpu_online_mask);
- pool->last_cpu = cpu;
+ do {
+ cpu = cpumask_next(pool->last_cpu, cpu_online_mask);
+ if (cpu >= nr_cpu_ids)
+ cpu = cpumask_first(cpu_online_mask);
+ pool->last_cpu = cpu;
+ } while (!per_cpu_ptr(pool->cpu_comp_tasks, cpu)->active);
spin_unlock_irqrestore(&pool->last_cpu_lock, flags);
return cpu;
}
static void __queue_comp_task(struct ehca_cq *__cq,
- struct ehca_cpu_comp_task *cct)
+ struct ehca_cpu_comp_task *cct,
+ struct task_struct *thread)
{
unsigned long flags;
@@ -683,7 +687,7 @@ static void __queue_comp_task(struct ehca_cq *__cq,
__cq->nr_callbacks++;
list_add_tail(&__cq->entry, &cct->cq_list);
cct->cq_jobs++;
- wake_up(&cct->wait_queue);
+ wake_up_process(thread);
} else
__cq->nr_callbacks++;
@@ -695,6 +699,7 @@ static void queue_comp_task(struct ehca_cq *__cq)
{
int cpu_id;
struct ehca_cpu_comp_task *cct;
+ struct task_struct *thread;
int cq_jobs;
unsigned long flags;
@@ -702,7 +707,8 @@ static void queue_comp_task(struct ehca_cq *__cq)
BUG_ON(!cpu_online(cpu_id));
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id);
- BUG_ON(!cct);
+ thread = *per_cpu_ptr(pool->cpu_comp_threads, cpu_id);
+ BUG_ON(!cct || !thread);
spin_lock_irqsave(&cct->task_lock, flags);
cq_jobs = cct->cq_jobs;
@@ -710,28 +716,25 @@ static void queue_comp_task(struct ehca_cq *__cq)
if (cq_jobs > 0) {
cpu_id = find_next_online_cpu(pool);
cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu_id);
- BUG_ON(!cct);
+ thread = *per_cpu_ptr(pool->cpu_comp_threads, cpu_id);
+ BUG_ON(!cct || !thread);
}
-
- __queue_comp_task(__cq, cct);
+ __queue_comp_task(__cq, cct, thread);
}
static void run_comp_task(struct ehca_cpu_comp_task *cct)
{
struct ehca_cq *cq;
- unsigned long flags;
-
- spin_lock_irqsave(&cct->task_lock, flags);
while (!list_empty(&cct->cq_list)) {
cq = list_entry(cct->cq_list.next, struct ehca_cq, entry);
- spin_unlock_irqrestore(&cct->task_lock, flags);
+ spin_unlock_irq(&cct->task_lock);
comp_event_callback(cq);
if (atomic_dec_and_test(&cq->nr_events))
wake_up(&cq->wait_completion);
- spin_lock_irqsave(&cct->task_lock, flags);
+ spin_lock_irq(&cct->task_lock);
spin_lock(&cq->task_lock);
cq->nr_callbacks--;
if (!cq->nr_callbacks) {
@@ -740,159 +743,76 @@ static void run_comp_task(struct ehca_cpu_comp_task *cct)
}
spin_unlock(&cq->task_lock);
}
-
- spin_unlock_irqrestore(&cct->task_lock, flags);
}
-static int comp_task(void *__cct)
+static void comp_task_park(unsigned int cpu)
{
- struct ehca_cpu_comp_task *cct = __cct;
- int cql_empty;
- DECLARE_WAITQUEUE(wait, current);
-
- set_current_state(TASK_INTERRUPTIBLE);
- while (!kthread_should_stop()) {
- add_wait_queue(&cct->wait_queue, &wait);
-
- spin_lock_irq(&cct->task_lock);
- cql_empty = list_empty(&cct->cq_list);
- spin_unlock_irq(&cct->task_lock);
- if (cql_empty)
- schedule();
- else
- __set_current_state(TASK_RUNNING);
-
- remove_wait_queue(&cct->wait_queue, &wait);
+ struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
+ struct ehca_cpu_comp_task *target;
+ struct task_struct *thread;
+ struct ehca_cq *cq, *tmp;
+ LIST_HEAD(list);
- spin_lock_irq(&cct->task_lock);
- cql_empty = list_empty(&cct->cq_list);
- spin_unlock_irq(&cct->task_lock);
- if (!cql_empty)
- run_comp_task(__cct);
+ spin_lock_irq(&cct->task_lock);
+ cct->cq_jobs = 0;
+ cct->active = 0;
+ list_splice_init(&cct->cq_list, &list);
+ spin_unlock_irq(&cct->task_lock);
- set_current_state(TASK_INTERRUPTIBLE);
+ cpu = find_next_online_cpu(pool);
+ target = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
+ thread = *per_cpu_ptr(pool->cpu_comp_threads, cpu);
+ spin_lock_irq(&target->task_lock);
+ list_for_each_entry_safe(cq, tmp, &list, entry) {
+ list_del(&cq->entry);
+ __queue_comp_task(cq, target, thread);
}
- __set_current_state(TASK_RUNNING);
-
- return 0;
-}
-
-static struct task_struct *create_comp_task(struct ehca_comp_pool *pool,
- int cpu)
-{
- struct ehca_cpu_comp_task *cct;
-
- cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
- spin_lock_init(&cct->task_lock);
- INIT_LIST_HEAD(&cct->cq_list);
- init_waitqueue_head(&cct->wait_queue);
- cct->task = kthread_create_on_node(comp_task, cct, cpu_to_node(cpu),
- "ehca_comp/%d", cpu);
-
- return cct->task;
+ spin_unlock_irq(&target->task_lock);
}
-static void destroy_comp_task(struct ehca_comp_pool *pool,
- int cpu)
+static void comp_task_stop(unsigned int cpu, bool online)
{
- struct ehca_cpu_comp_task *cct;
- struct task_struct *task;
- unsigned long flags_cct;
-
- cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
-
- spin_lock_irqsave(&cct->task_lock, flags_cct);
+ struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
- task = cct->task;
- cct->task = NULL;
+ spin_lock_irq(&cct->task_lock);
cct->cq_jobs = 0;
-
- spin_unlock_irqrestore(&cct->task_lock, flags_cct);
-
- if (task)
- kthread_stop(task);
+ cct->active = 0;
+ WARN_ON(!list_empty(&cct->cq_list));
+ spin_unlock_irq(&cct->task_lock);
}
-static void __cpuinit take_over_work(struct ehca_comp_pool *pool, int cpu)
+static int comp_task_should_run(unsigned int cpu)
{
struct ehca_cpu_comp_task *cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
- LIST_HEAD(list);
- struct ehca_cq *cq;
- unsigned long flags_cct;
-
- spin_lock_irqsave(&cct->task_lock, flags_cct);
-
- list_splice_init(&cct->cq_list, &list);
-
- while (!list_empty(&list)) {
- cq = list_entry(cct->cq_list.next, struct ehca_cq, entry);
-
- list_del(&cq->entry);
- __queue_comp_task(cq, this_cpu_ptr(pool->cpu_comp_tasks));
- }
-
- spin_unlock_irqrestore(&cct->task_lock, flags_cct);
+ return cct->cq_jobs;
}
-static int __cpuinit comp_pool_callback(struct notifier_block *nfb,
- unsigned long action,
- void *hcpu)
+static void comp_task(unsigned int cpu)
{
- unsigned int cpu = (unsigned long)hcpu;
- struct ehca_cpu_comp_task *cct;
+ struct ehca_cpu_comp_task *cct = this_cpu_ptr(pool->cpu_comp_tasks);
+ int cql_empty;
- switch (action) {
- case CPU_UP_PREPARE:
- case CPU_UP_PREPARE_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_PREPARE)", cpu);
- if (!create_comp_task(pool, cpu)) {
- ehca_gen_err("Can't create comp_task for cpu: %x", cpu);
- return notifier_from_errno(-ENOMEM);
- }
- break;
- case CPU_UP_CANCELED:
- case CPU_UP_CANCELED_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_CANCELED)", cpu);
- cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
- kthread_bind(cct->task, cpumask_any(cpu_online_mask));
- destroy_comp_task(pool, cpu);
- break;
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_ONLINE)", cpu);
- cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
- kthread_bind(cct->task, cpu);
- wake_up_process(cct->task);
- break;
- case CPU_DOWN_PREPARE:
- case CPU_DOWN_PREPARE_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_DOWN_PREPARE)", cpu);
- break;
- case CPU_DOWN_FAILED:
- case CPU_DOWN_FAILED_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_DOWN_FAILED)", cpu);
- break;
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- ehca_gen_dbg("CPU: %x (CPU_DEAD)", cpu);
- destroy_comp_task(pool, cpu);
- take_over_work(pool, cpu);
- break;
+ spin_lock_irq(&cct->task_lock);
+ cql_empty = list_empty(&cct->cq_list);
+ if (!cql_empty) {
+ __set_current_state(TASK_RUNNING);
+ run_comp_task(cct);
}
-
- return NOTIFY_OK;
+ spin_unlock_irq(&cct->task_lock);
}
-static struct notifier_block comp_pool_callback_nb __cpuinitdata = {
- .notifier_call = comp_pool_callback,
- .priority = 0,
+static struct smp_hotplug_thread comp_pool_threads = {
+ .thread_should_run = comp_task_should_run,
+ .thread_fn = comp_task,
+ .thread_comm = "ehca_comp/%u",
+ .cleanup = comp_task_stop,
+ .park = comp_task_park,
};
int ehca_create_comp_pool(void)
{
- int cpu;
- struct task_struct *task;
+ int cpu, ret = -ENOMEM;
if (!ehca_scaling_code)
return 0;
@@ -905,38 +825,46 @@ int ehca_create_comp_pool(void)
pool->last_cpu = cpumask_any(cpu_online_mask);
pool->cpu_comp_tasks = alloc_percpu(struct ehca_cpu_comp_task);
- if (pool->cpu_comp_tasks == NULL) {
- kfree(pool);
- return -EINVAL;
- }
+ if (!pool->cpu_comp_tasks)
+ goto out_pool;
- for_each_online_cpu(cpu) {
- task = create_comp_task(pool, cpu);
- if (task) {
- kthread_bind(task, cpu);
- wake_up_process(task);
- }
+ pool->cpu_comp_threads = alloc_percpu(struct task_struct *);
+ if (!pool->cpu_comp_threads)
+ goto out_tasks;
+
+ for_each_present_cpu(cpu) {
+ struct ehca_cpu_comp_task *cct;
+
+ cct = per_cpu_ptr(pool->cpu_comp_tasks, cpu);
+ spin_lock_init(&cct->task_lock);
+ INIT_LIST_HEAD(&cct->cq_list);
}
- register_hotcpu_notifier(&comp_pool_callback_nb);
+ comp_pool_threads.store = pool->cpu_comp_threads;
+ ret = smpboot_register_percpu_thread(&comp_pool_threads);
+ if (ret)
+ goto out_threads;
- printk(KERN_INFO "eHCA scaling code enabled\n");
+ pr_info("eHCA scaling code enabled\n");
+ return ret;
- return 0;
+out_threads:
+ free_percpu(pool->cpu_comp_threads);
+out_tasks:
+ free_percpu(pool->cpu_comp_tasks);
+out_pool:
+ kfree(pool);
+ return ret;
}
void ehca_destroy_comp_pool(void)
{
- int i;
-
if (!ehca_scaling_code)
return;
- unregister_hotcpu_notifier(&comp_pool_callback_nb);
-
- for_each_online_cpu(i)
- destroy_comp_task(pool, i);
+ smpboot_unregister_percpu_thread(&comp_pool_threads);
+ free_percpu(pool->cpu_comp_threads);
free_percpu(pool->cpu_comp_tasks);
kfree(pool);
}
diff --git a/drivers/infiniband/hw/ehca/ehca_irq.h b/drivers/infiniband/hw/ehca/ehca_irq.h
index 3346cb06cea6..5370199f08c7 100644
--- a/drivers/infiniband/hw/ehca/ehca_irq.h
+++ b/drivers/infiniband/hw/ehca/ehca_irq.h
@@ -58,15 +58,15 @@ void ehca_tasklet_eq(unsigned long data);
void ehca_process_eq(struct ehca_shca *shca, int is_irq);
struct ehca_cpu_comp_task {
- wait_queue_head_t wait_queue;
struct list_head cq_list;
- struct task_struct *task;
spinlock_t task_lock;
int cq_jobs;
+ int active;
};
struct ehca_comp_pool {
- struct ehca_cpu_comp_task *cpu_comp_tasks;
+ struct ehca_cpu_comp_task __percpu *cpu_comp_tasks;
+ struct task_struct * __percpu *cpu_comp_threads;
int last_cpu;
spinlock_t last_cpu_lock;
};
diff --git a/drivers/infiniband/hw/mthca/mthca_reset.c b/drivers/infiniband/hw/mthca/mthca_reset.c
index 4fa3534ec233..74c6a9426047 100644
--- a/drivers/infiniband/hw/mthca/mthca_reset.c
+++ b/drivers/infiniband/hw/mthca/mthca_reset.c
@@ -241,16 +241,16 @@ good:
if (hca_pcie_cap) {
devctl = hca_header[(hca_pcie_cap + PCI_EXP_DEVCTL) / 4];
- if (pci_write_config_word(mdev->pdev, hca_pcie_cap + PCI_EXP_DEVCTL,
- devctl)) {
+ if (pcie_capability_write_word(mdev->pdev, PCI_EXP_DEVCTL,
+ devctl)) {
err = -ENODEV;
mthca_err(mdev, "Couldn't restore HCA PCI Express "
"Device Control register, aborting.\n");
goto out;
}
linkctl = hca_header[(hca_pcie_cap + PCI_EXP_LNKCTL) / 4];
- if (pci_write_config_word(mdev->pdev, hca_pcie_cap + PCI_EXP_LNKCTL,
- linkctl)) {
+ if (pcie_capability_write_word(mdev->pdev, PCI_EXP_LNKCTL,
+ linkctl)) {
err = -ENODEV;
mthca_err(mdev, "Couldn't restore HCA PCI Express "
"Link control register, aborting.\n");
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
index cb5b7f7d4d38..b29a4246ef41 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_verbs.c
@@ -2219,7 +2219,6 @@ static bool ocrdma_poll_success_scqe(struct ocrdma_qp *qp,
u32 wqe_idx;
if (!qp->wqe_wr_id_tbl[tail].signaled) {
- expand = true; /* CQE cannot be consumed yet */
*polled = false; /* WC cannot be consumed yet */
} else {
ibwc->status = IB_WC_SUCCESS;
@@ -2227,10 +2226,11 @@ static bool ocrdma_poll_success_scqe(struct ocrdma_qp *qp,
ibwc->qp = &qp->ibqp;
ocrdma_update_wc(qp, ibwc, tail);
*polled = true;
- wqe_idx = le32_to_cpu(cqe->wq.wqeidx) & OCRDMA_CQE_WQEIDX_MASK;
- if (tail != wqe_idx)
- expand = true; /* Coalesced CQE can't be consumed yet */
}
+ wqe_idx = le32_to_cpu(cqe->wq.wqeidx) & OCRDMA_CQE_WQEIDX_MASK;
+ if (tail != wqe_idx)
+ expand = true; /* Coalesced CQE can't be consumed yet */
+
ocrdma_hwq_inc_tail(&qp->sq);
return expand;
}
diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h
index 7b1b86690024..4d11575c2010 100644
--- a/drivers/infiniband/hw/qib/qib.h
+++ b/drivers/infiniband/hw/qib/qib.h
@@ -87,7 +87,7 @@ struct qlogic_ib_stats {
};
extern struct qlogic_ib_stats qib_stats;
-extern struct pci_error_handlers qib_pci_err_handler;
+extern const struct pci_error_handlers qib_pci_err_handler;
extern struct pci_driver qib_driver;
#define QIB_CHIP_SWVERSION QIB_CHIP_VERS_MAJ
diff --git a/drivers/infiniband/hw/qib/qib_mad.c b/drivers/infiniband/hw/qib/qib_mad.c
index 19f1e6c45fb6..ccb119143d20 100644
--- a/drivers/infiniband/hw/qib/qib_mad.c
+++ b/drivers/infiniband/hw/qib/qib_mad.c
@@ -471,9 +471,10 @@ static int subn_get_portinfo(struct ib_smp *smp, struct ib_device *ibdev,
if (port_num != port) {
ibp = to_iport(ibdev, port_num);
ret = check_mkey(ibp, smp, 0);
- if (ret)
+ if (ret) {
ret = IB_MAD_RESULT_FAILURE;
goto bail;
+ }
}
}
diff --git a/drivers/infiniband/hw/qib/qib_pcie.c b/drivers/infiniband/hw/qib/qib_pcie.c
index 062c301ebf53..c574ec7c85e6 100644
--- a/drivers/infiniband/hw/qib/qib_pcie.c
+++ b/drivers/infiniband/hw/qib/qib_pcie.c
@@ -273,10 +273,9 @@ int qib_pcie_params(struct qib_devdata *dd, u32 minw, u32 *nent,
struct qib_msix_entry *entry)
{
u16 linkstat, speed;
- int pos = 0, pose, ret = 1;
+ int pos = 0, ret = 1;
- pose = pci_pcie_cap(dd->pcidev);
- if (!pose) {
+ if (!pci_is_pcie(dd->pcidev)) {
qib_dev_err(dd, "Can't find PCI Express capability!\n");
/* set up something... */
dd->lbus_width = 1;
@@ -298,7 +297,7 @@ int qib_pcie_params(struct qib_devdata *dd, u32 minw, u32 *nent,
if (!pos)
qib_enable_intx(dd->pcidev);
- pci_read_config_word(dd->pcidev, pose + PCI_EXP_LNKSTA, &linkstat);
+ pcie_capability_read_word(dd->pcidev, PCI_EXP_LNKSTA, &linkstat);
/*
* speed is bits 0-3, linkwidth is bits 4-8
* no defines for them in headers
@@ -516,7 +515,6 @@ static int qib_tune_pcie_coalesce(struct qib_devdata *dd)
{
int r;
struct pci_dev *parent;
- int ppos;
u16 devid;
u32 mask, bits, val;
@@ -529,8 +527,7 @@ static int qib_tune_pcie_coalesce(struct qib_devdata *dd)
qib_devinfo(dd->pcidev, "Parent not root\n");
return 1;
}
- ppos = pci_pcie_cap(parent);
- if (!ppos)
+ if (!pci_is_pcie(parent))
return 1;
if (parent->vendor != 0x8086)
return 1;
@@ -587,7 +584,6 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd)
{
int ret = 1; /* Assume the worst */
struct pci_dev *parent;
- int ppos, epos;
u16 pcaps, pctl, ecaps, ectl;
int rc_sup, ep_sup;
int rc_cur, ep_cur;
@@ -598,19 +594,15 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd)
qib_devinfo(dd->pcidev, "Parent not root\n");
goto bail;
}
- ppos = pci_pcie_cap(parent);
- if (ppos) {
- pci_read_config_word(parent, ppos + PCI_EXP_DEVCAP, &pcaps);
- pci_read_config_word(parent, ppos + PCI_EXP_DEVCTL, &pctl);
- } else
+
+ if (!pci_is_pcie(parent) || !pci_is_pcie(dd->pcidev))
goto bail;
+ pcie_capability_read_word(parent, PCI_EXP_DEVCAP, &pcaps);
+ pcie_capability_read_word(parent, PCI_EXP_DEVCTL, &pctl);
/* Find out supported and configured values for endpoint (us) */
- epos = pci_pcie_cap(dd->pcidev);
- if (epos) {
- pci_read_config_word(dd->pcidev, epos + PCI_EXP_DEVCAP, &ecaps);
- pci_read_config_word(dd->pcidev, epos + PCI_EXP_DEVCTL, &ectl);
- } else
- goto bail;
+ pcie_capability_read_word(dd->pcidev, PCI_EXP_DEVCAP, &ecaps);
+ pcie_capability_read_word(dd->pcidev, PCI_EXP_DEVCTL, &ectl);
+
ret = 0;
/* Find max payload supported by root, endpoint */
rc_sup = fld2val(pcaps, PCI_EXP_DEVCAP_PAYLOAD);
@@ -629,14 +621,14 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd)
rc_cur = rc_sup;
pctl = (pctl & ~PCI_EXP_DEVCTL_PAYLOAD) |
val2fld(rc_cur, PCI_EXP_DEVCTL_PAYLOAD);
- pci_write_config_word(parent, ppos + PCI_EXP_DEVCTL, pctl);
+ pcie_capability_write_word(parent, PCI_EXP_DEVCTL, pctl);
}
/* If less than (allowed, supported), bump endpoint payload */
if (rc_sup > ep_cur) {
ep_cur = rc_sup;
ectl = (ectl & ~PCI_EXP_DEVCTL_PAYLOAD) |
val2fld(ep_cur, PCI_EXP_DEVCTL_PAYLOAD);
- pci_write_config_word(dd->pcidev, epos + PCI_EXP_DEVCTL, ectl);
+ pcie_capability_write_word(dd->pcidev, PCI_EXP_DEVCTL, ectl);
}
/*
@@ -654,13 +646,13 @@ static int qib_tune_pcie_caps(struct qib_devdata *dd)
rc_cur = rc_sup;
pctl = (pctl & ~PCI_EXP_DEVCTL_READRQ) |
val2fld(rc_cur, PCI_EXP_DEVCTL_READRQ);
- pci_write_config_word(parent, ppos + PCI_EXP_DEVCTL, pctl);
+ pcie_capability_write_word(parent, PCI_EXP_DEVCTL, pctl);
}
if (rc_sup > ep_cur) {
ep_cur = rc_sup;
ectl = (ectl & ~PCI_EXP_DEVCTL_READRQ) |
val2fld(ep_cur, PCI_EXP_DEVCTL_READRQ);
- pci_write_config_word(dd->pcidev, epos + PCI_EXP_DEVCTL, ectl);
+ pcie_capability_write_word(dd->pcidev, PCI_EXP_DEVCTL, ectl);
}
bail:
return ret;
@@ -753,7 +745,7 @@ qib_pci_resume(struct pci_dev *pdev)
qib_init(dd, 1); /* same as re-init after reset */
}
-struct pci_error_handlers qib_pci_err_handler = {
+const struct pci_error_handlers qib_pci_err_handler = {
.error_detected = qib_pci_error_detected,
.mmio_enabled = qib_pci_mmio_enabled,
.link_reset = qib_pci_link_reset,
diff --git a/drivers/infiniband/ulp/ipoib/ipoib.h b/drivers/infiniband/ulp/ipoib/ipoib.h
index ca43901ed861..0af216d21f87 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib.h
+++ b/drivers/infiniband/ulp/ipoib/ipoib.h
@@ -262,7 +262,10 @@ struct ipoib_ethtool_st {
u16 max_coalesced_frames;
};
+struct ipoib_neigh_table;
+
struct ipoib_neigh_hash {
+ struct ipoib_neigh_table *ntbl;
struct ipoib_neigh __rcu **buckets;
struct rcu_head rcu;
u32 mask;
@@ -271,9 +274,9 @@ struct ipoib_neigh_hash {
struct ipoib_neigh_table {
struct ipoib_neigh_hash __rcu *htbl;
- rwlock_t rwlock;
atomic_t entries;
struct completion flushed;
+ struct completion deleted;
};
/*
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 3e2085a3ee47..1e19b5ae7c47 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -546,15 +546,15 @@ static void neigh_add_path(struct sk_buff *skb, u8 *daddr,
struct ipoib_neigh *neigh;
unsigned long flags;
+ spin_lock_irqsave(&priv->lock, flags);
neigh = ipoib_neigh_alloc(daddr, dev);
if (!neigh) {
+ spin_unlock_irqrestore(&priv->lock, flags);
++dev->stats.tx_dropped;
dev_kfree_skb_any(skb);
return;
}
- spin_lock_irqsave(&priv->lock, flags);
-
path = __path_find(dev, daddr + 4);
if (!path) {
path = path_rec_create(dev, daddr + 4);
@@ -863,10 +863,10 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
if (test_bit(IPOIB_STOP_NEIGH_GC, &priv->flags))
return;
- write_lock_bh(&ntbl->rwlock);
+ spin_lock_irqsave(&priv->lock, flags);
htbl = rcu_dereference_protected(ntbl->htbl,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
if (!htbl)
goto out_unlock;
@@ -883,16 +883,14 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
struct ipoib_neigh __rcu **np = &htbl->buckets[i];
while ((neigh = rcu_dereference_protected(*np,
- lockdep_is_held(&ntbl->rwlock))) != NULL) {
+ lockdep_is_held(&priv->lock))) != NULL) {
/* was the neigh idle for two GC periods */
if (time_after(neigh_obsolete, neigh->alive)) {
rcu_assign_pointer(*np,
rcu_dereference_protected(neigh->hnext,
- lockdep_is_held(&ntbl->rwlock)));
+ lockdep_is_held(&priv->lock)));
/* remove from path/mc list */
- spin_lock_irqsave(&priv->lock, flags);
list_del(&neigh->list);
- spin_unlock_irqrestore(&priv->lock, flags);
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
} else {
np = &neigh->hnext;
@@ -902,7 +900,7 @@ static void __ipoib_reap_neigh(struct ipoib_dev_priv *priv)
}
out_unlock:
- write_unlock_bh(&ntbl->rwlock);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipoib_reap_neigh(struct work_struct *work)
@@ -947,10 +945,8 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
struct ipoib_neigh *neigh;
u32 hash_val;
- write_lock_bh(&ntbl->rwlock);
-
htbl = rcu_dereference_protected(ntbl->htbl,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
if (!htbl) {
neigh = NULL;
goto out_unlock;
@@ -961,10 +957,10 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
*/
hash_val = ipoib_addr_hash(htbl, daddr);
for (neigh = rcu_dereference_protected(htbl->buckets[hash_val],
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
neigh != NULL;
neigh = rcu_dereference_protected(neigh->hnext,
- lockdep_is_held(&ntbl->rwlock))) {
+ lockdep_is_held(&priv->lock))) {
if (memcmp(daddr, neigh->daddr, INFINIBAND_ALEN) == 0) {
/* found, take one ref on behalf of the caller */
if (!atomic_inc_not_zero(&neigh->refcnt)) {
@@ -987,12 +983,11 @@ struct ipoib_neigh *ipoib_neigh_alloc(u8 *daddr,
/* put in hash */
rcu_assign_pointer(neigh->hnext,
rcu_dereference_protected(htbl->buckets[hash_val],
- lockdep_is_held(&ntbl->rwlock)));
+ lockdep_is_held(&priv->lock)));
rcu_assign_pointer(htbl->buckets[hash_val], neigh);
atomic_inc(&ntbl->entries);
out_unlock:
- write_unlock_bh(&ntbl->rwlock);
return neigh;
}
@@ -1040,35 +1035,29 @@ void ipoib_neigh_free(struct ipoib_neigh *neigh)
struct ipoib_neigh *n;
u32 hash_val;
- write_lock_bh(&ntbl->rwlock);
-
htbl = rcu_dereference_protected(ntbl->htbl,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
if (!htbl)
- goto out_unlock;
+ return;
hash_val = ipoib_addr_hash(htbl, neigh->daddr);
np = &htbl->buckets[hash_val];
for (n = rcu_dereference_protected(*np,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
n != NULL;
n = rcu_dereference_protected(*np,
- lockdep_is_held(&ntbl->rwlock))) {
+ lockdep_is_held(&priv->lock))) {
if (n == neigh) {
/* found */
rcu_assign_pointer(*np,
rcu_dereference_protected(neigh->hnext,
- lockdep_is_held(&ntbl->rwlock)));
+ lockdep_is_held(&priv->lock)));
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
- goto out_unlock;
+ return;
} else {
np = &n->hnext;
}
}
-
-out_unlock:
- write_unlock_bh(&ntbl->rwlock);
-
}
static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
@@ -1080,7 +1069,6 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
clear_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
ntbl->htbl = NULL;
- rwlock_init(&ntbl->rwlock);
htbl = kzalloc(sizeof(*htbl), GFP_KERNEL);
if (!htbl)
return -ENOMEM;
@@ -1095,6 +1083,7 @@ static int ipoib_neigh_hash_init(struct ipoib_dev_priv *priv)
htbl->mask = (size - 1);
htbl->buckets = buckets;
ntbl->htbl = htbl;
+ htbl->ntbl = ntbl;
atomic_set(&ntbl->entries, 0);
/* start garbage collection */
@@ -1111,9 +1100,11 @@ static void neigh_hash_free_rcu(struct rcu_head *head)
struct ipoib_neigh_hash,
rcu);
struct ipoib_neigh __rcu **buckets = htbl->buckets;
+ struct ipoib_neigh_table *ntbl = htbl->ntbl;
kfree(buckets);
kfree(htbl);
+ complete(&ntbl->deleted);
}
void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
@@ -1125,10 +1116,10 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
int i;
/* remove all neigh connected to a given path or mcast */
- write_lock_bh(&ntbl->rwlock);
+ spin_lock_irqsave(&priv->lock, flags);
htbl = rcu_dereference_protected(ntbl->htbl,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
if (!htbl)
goto out_unlock;
@@ -1138,16 +1129,14 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
struct ipoib_neigh __rcu **np = &htbl->buckets[i];
while ((neigh = rcu_dereference_protected(*np,
- lockdep_is_held(&ntbl->rwlock))) != NULL) {
+ lockdep_is_held(&priv->lock))) != NULL) {
/* delete neighs belong to this parent */
if (!memcmp(gid, neigh->daddr + 4, sizeof (union ib_gid))) {
rcu_assign_pointer(*np,
rcu_dereference_protected(neigh->hnext,
- lockdep_is_held(&ntbl->rwlock)));
+ lockdep_is_held(&priv->lock)));
/* remove from parent list */
- spin_lock_irqsave(&priv->lock, flags);
list_del(&neigh->list);
- spin_unlock_irqrestore(&priv->lock, flags);
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
} else {
np = &neigh->hnext;
@@ -1156,7 +1145,7 @@ void ipoib_del_neighs_by_gid(struct net_device *dev, u8 *gid)
}
}
out_unlock:
- write_unlock_bh(&ntbl->rwlock);
+ spin_unlock_irqrestore(&priv->lock, flags);
}
static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
@@ -1164,37 +1153,44 @@ static void ipoib_flush_neighs(struct ipoib_dev_priv *priv)
struct ipoib_neigh_table *ntbl = &priv->ntbl;
struct ipoib_neigh_hash *htbl;
unsigned long flags;
- int i;
+ int i, wait_flushed = 0;
- write_lock_bh(&ntbl->rwlock);
+ init_completion(&priv->ntbl.flushed);
+
+ spin_lock_irqsave(&priv->lock, flags);
htbl = rcu_dereference_protected(ntbl->htbl,
- lockdep_is_held(&ntbl->rwlock));
+ lockdep_is_held(&priv->lock));
if (!htbl)
goto out_unlock;
+ wait_flushed = atomic_read(&priv->ntbl.entries);
+ if (!wait_flushed)
+ goto free_htbl;
+
for (i = 0; i < htbl->size; i++) {
struct ipoib_neigh *neigh;
struct ipoib_neigh __rcu **np = &htbl->buckets[i];
while ((neigh = rcu_dereference_protected(*np,
- lockdep_is_held(&ntbl->rwlock))) != NULL) {
+ lockdep_is_held(&priv->lock))) != NULL) {
rcu_assign_pointer(*np,
rcu_dereference_protected(neigh->hnext,
- lockdep_is_held(&ntbl->rwlock)));
+ lockdep_is_held(&priv->lock)));
/* remove from path/mc list */
- spin_lock_irqsave(&priv->lock, flags);
list_del(&neigh->list);
- spin_unlock_irqrestore(&priv->lock, flags);
call_rcu(&neigh->rcu, ipoib_neigh_reclaim);
}
}
+free_htbl:
rcu_assign_pointer(ntbl->htbl, NULL);
call_rcu(&htbl->rcu, neigh_hash_free_rcu);
out_unlock:
- write_unlock_bh(&ntbl->rwlock);
+ spin_unlock_irqrestore(&priv->lock, flags);
+ if (wait_flushed)
+ wait_for_completion(&priv->ntbl.flushed);
}
static void ipoib_neigh_hash_uninit(struct net_device *dev)
@@ -1203,7 +1199,7 @@ static void ipoib_neigh_hash_uninit(struct net_device *dev)
int stopped;
ipoib_dbg(priv, "ipoib_neigh_hash_uninit\n");
- init_completion(&priv->ntbl.flushed);
+ init_completion(&priv->ntbl.deleted);
set_bit(IPOIB_NEIGH_TBL_FLUSH, &priv->flags);
/* Stop GC if called at init fail need to cancel work */
@@ -1211,10 +1207,9 @@ static void ipoib_neigh_hash_uninit(struct net_device *dev)
if (!stopped)
cancel_delayed_work(&priv->neigh_reap_task);
- if (atomic_read(&priv->ntbl.entries)) {
- ipoib_flush_neighs(priv);
- wait_for_completion(&priv->ntbl.flushed);
- }
+ ipoib_flush_neighs(priv);
+
+ wait_for_completion(&priv->ntbl.deleted);
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 13f4aa7593c8..75367249f447 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -707,9 +707,7 @@ out:
neigh = ipoib_neigh_get(dev, daddr);
spin_lock_irqsave(&priv->lock, flags);
if (!neigh) {
- spin_unlock_irqrestore(&priv->lock, flags);
neigh = ipoib_neigh_alloc(daddr, dev);
- spin_lock_irqsave(&priv->lock, flags);
if (neigh) {
kref_get(&mcast->ah->ref);
neigh->ah = mcast->ah;
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 6c58bfff01a3..118d0300f1fb 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -54,16 +54,9 @@ struct evdev_client {
static struct evdev *evdev_table[EVDEV_MINORS];
static DEFINE_MUTEX(evdev_table_mutex);
-static void evdev_pass_event(struct evdev_client *client,
- struct input_event *event,
- ktime_t mono, ktime_t real)
+static void __pass_event(struct evdev_client *client,
+ const struct input_event *event)
{
- event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
- mono : real);
-
- /* Interrupts are disabled, just acquire the lock. */
- spin_lock(&client->buffer_lock);
-
client->buffer[client->head++] = *event;
client->head &= client->bufsize - 1;
@@ -86,42 +79,74 @@ static void evdev_pass_event(struct evdev_client *client,
client->packet_head = client->head;
kill_fasync(&client->fasync, SIGIO, POLL_IN);
}
+}
+
+static void evdev_pass_values(struct evdev_client *client,
+ const struct input_value *vals, unsigned int count,
+ ktime_t mono, ktime_t real)
+{
+ struct evdev *evdev = client->evdev;
+ const struct input_value *v;
+ struct input_event event;
+ bool wakeup = false;
+
+ event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
+ mono : real);
+
+ /* Interrupts are disabled, just acquire the lock. */
+ spin_lock(&client->buffer_lock);
+
+ for (v = vals; v != vals + count; v++) {
+ event.type = v->type;
+ event.code = v->code;
+ event.value = v->value;
+ __pass_event(client, &event);
+ if (v->type == EV_SYN && v->code == SYN_REPORT)
+ wakeup = true;
+ }
spin_unlock(&client->buffer_lock);
+
+ if (wakeup)
+ wake_up_interruptible(&evdev->wait);
}
/*
- * Pass incoming event to all connected clients.
+ * Pass incoming events to all connected clients.
*/
-static void evdev_event(struct input_handle *handle,
- unsigned int type, unsigned int code, int value)
+static void evdev_events(struct input_handle *handle,
+ const struct input_value *vals, unsigned int count)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;
- struct input_event event;
ktime_t time_mono, time_real;
time_mono = ktime_get();
time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
- event.type = type;
- event.code = code;
- event.value = value;
-
rcu_read_lock();
client = rcu_dereference(evdev->grab);
if (client)
- evdev_pass_event(client, &event, time_mono, time_real);
+ evdev_pass_values(client, vals, count, time_mono, time_real);
else
list_for_each_entry_rcu(client, &evdev->client_list, node)
- evdev_pass_event(client, &event, time_mono, time_real);
+ evdev_pass_values(client, vals, count,
+ time_mono, time_real);
rcu_read_unlock();
+}
- if (type == EV_SYN && code == SYN_REPORT)
- wake_up_interruptible(&evdev->wait);
+/*
+ * Pass incoming event to all connected clients.
+ */
+static void evdev_event(struct input_handle *handle,
+ unsigned int type, unsigned int code, int value)
+{
+ struct input_value vals[] = { { type, code, value } };
+
+ evdev_events(handle, vals, 1);
}
static int evdev_fasync(int fd, struct file *file, int on)
@@ -653,20 +678,22 @@ static int evdev_handle_mt_request(struct input_dev *dev,
unsigned int size,
int __user *ip)
{
- const struct input_mt_slot *mt = dev->mt;
+ const struct input_mt *mt = dev->mt;
unsigned int code;
int max_slots;
int i;
if (get_user(code, &ip[0]))
return -EFAULT;
- if (!input_is_mt_value(code))
+ if (!mt || !input_is_mt_value(code))
return -EINVAL;
max_slots = (size - sizeof(__u32)) / sizeof(__s32);
- for (i = 0; i < dev->mtsize && i < max_slots; i++)
- if (put_user(input_mt_get_value(&mt[i], code), &ip[1 + i]))
+ for (i = 0; i < mt->num_slots && i < max_slots; i++) {
+ int value = input_mt_get_value(&mt->slots[i], code);
+ if (put_user(value, &ip[1 + i]))
return -EFAULT;
+ }
return 0;
}
@@ -1048,6 +1075,7 @@ MODULE_DEVICE_TABLE(input, evdev_ids);
static struct input_handler evdev_handler = {
.event = evdev_event,
+ .events = evdev_events,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
diff --git a/drivers/input/input-mt.c b/drivers/input/input-mt.c
index 70a16c7da8cc..c0ec7d42c3be 100644
--- a/drivers/input/input-mt.c
+++ b/drivers/input/input-mt.c
@@ -14,6 +14,14 @@
#define TRKID_SGN ((TRKID_MAX + 1) >> 1)
+static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
+{
+ if (dev->absinfo && test_bit(src, dev->absbit)) {
+ dev->absinfo[dst] = dev->absinfo[src];
+ dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
+ }
+}
+
/**
* input_mt_init_slots() - initialize MT input slots
* @dev: input device supporting MT events and finger tracking
@@ -25,29 +33,63 @@
* May be called repeatedly. Returns -EINVAL if attempting to
* reinitialize with a different number of slots.
*/
-int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots)
+int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
+ unsigned int flags)
{
+ struct input_mt *mt = dev->mt;
int i;
if (!num_slots)
return 0;
- if (dev->mt)
- return dev->mtsize != num_slots ? -EINVAL : 0;
+ if (mt)
+ return mt->num_slots != num_slots ? -EINVAL : 0;
- dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL);
- if (!dev->mt)
- return -ENOMEM;
+ mt = kzalloc(sizeof(*mt) + num_slots * sizeof(*mt->slots), GFP_KERNEL);
+ if (!mt)
+ goto err_mem;
- dev->mtsize = num_slots;
+ mt->num_slots = num_slots;
+ mt->flags = flags;
input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0);
- input_set_events_per_packet(dev, 6 * num_slots);
+
+ if (flags & (INPUT_MT_POINTER | INPUT_MT_DIRECT)) {
+ __set_bit(EV_KEY, dev->evbit);
+ __set_bit(BTN_TOUCH, dev->keybit);
+
+ copy_abs(dev, ABS_X, ABS_MT_POSITION_X);
+ copy_abs(dev, ABS_Y, ABS_MT_POSITION_Y);
+ copy_abs(dev, ABS_PRESSURE, ABS_MT_PRESSURE);
+ }
+ if (flags & INPUT_MT_POINTER) {
+ __set_bit(BTN_TOOL_FINGER, dev->keybit);
+ __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+ if (num_slots >= 3)
+ __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+ if (num_slots >= 4)
+ __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
+ if (num_slots >= 5)
+ __set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
+ __set_bit(INPUT_PROP_POINTER, dev->propbit);
+ }
+ if (flags & INPUT_MT_DIRECT)
+ __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+ if (flags & INPUT_MT_TRACK) {
+ unsigned int n2 = num_slots * num_slots;
+ mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL);
+ if (!mt->red)
+ goto err_mem;
+ }
/* Mark slots as 'unused' */
for (i = 0; i < num_slots; i++)
- input_mt_set_value(&dev->mt[i], ABS_MT_TRACKING_ID, -1);
+ input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1);
+ dev->mt = mt;
return 0;
+err_mem:
+ kfree(mt);
+ return -ENOMEM;
}
EXPORT_SYMBOL(input_mt_init_slots);
@@ -60,11 +102,11 @@ EXPORT_SYMBOL(input_mt_init_slots);
*/
void input_mt_destroy_slots(struct input_dev *dev)
{
- kfree(dev->mt);
+ if (dev->mt) {
+ kfree(dev->mt->red);
+ kfree(dev->mt);
+ }
dev->mt = NULL;
- dev->mtsize = 0;
- dev->slot = 0;
- dev->trkid = 0;
}
EXPORT_SYMBOL(input_mt_destroy_slots);
@@ -83,18 +125,24 @@ EXPORT_SYMBOL(input_mt_destroy_slots);
void input_mt_report_slot_state(struct input_dev *dev,
unsigned int tool_type, bool active)
{
- struct input_mt_slot *mt;
+ struct input_mt *mt = dev->mt;
+ struct input_mt_slot *slot;
int id;
- if (!dev->mt || !active) {
+ if (!mt)
+ return;
+
+ slot = &mt->slots[mt->slot];
+ slot->frame = mt->frame;
+
+ if (!active) {
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
return;
}
- mt = &dev->mt[dev->slot];
- id = input_mt_get_value(mt, ABS_MT_TRACKING_ID);
- if (id < 0 || input_mt_get_value(mt, ABS_MT_TOOL_TYPE) != tool_type)
- id = input_mt_new_trkid(dev);
+ id = input_mt_get_value(slot, ABS_MT_TRACKING_ID);
+ if (id < 0 || input_mt_get_value(slot, ABS_MT_TOOL_TYPE) != tool_type)
+ id = input_mt_new_trkid(mt);
input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id);
input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type);
@@ -135,13 +183,19 @@ EXPORT_SYMBOL(input_mt_report_finger_count);
*/
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
{
- struct input_mt_slot *oldest = NULL;
- int oldid = dev->trkid;
- int count = 0;
- int i;
+ struct input_mt *mt = dev->mt;
+ struct input_mt_slot *oldest;
+ int oldid, count, i;
+
+ if (!mt)
+ return;
+
+ oldest = 0;
+ oldid = mt->trkid;
+ count = 0;
- for (i = 0; i < dev->mtsize; ++i) {
- struct input_mt_slot *ps = &dev->mt[i];
+ for (i = 0; i < mt->num_slots; ++i) {
+ struct input_mt_slot *ps = &mt->slots[i];
int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
if (id < 0)
@@ -160,13 +214,208 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
if (oldest) {
int x = input_mt_get_value(oldest, ABS_MT_POSITION_X);
int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y);
- int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
input_event(dev, EV_ABS, ABS_X, x);
input_event(dev, EV_ABS, ABS_Y, y);
- input_event(dev, EV_ABS, ABS_PRESSURE, p);
+
+ if (test_bit(ABS_MT_PRESSURE, dev->absbit)) {
+ int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
+ input_event(dev, EV_ABS, ABS_PRESSURE, p);
+ }
} else {
- input_event(dev, EV_ABS, ABS_PRESSURE, 0);
+ if (test_bit(ABS_MT_PRESSURE, dev->absbit))
+ input_event(dev, EV_ABS, ABS_PRESSURE, 0);
}
}
EXPORT_SYMBOL(input_mt_report_pointer_emulation);
+
+/**
+ * input_mt_sync_frame() - synchronize mt frame
+ * @dev: input device with allocated MT slots
+ *
+ * Close the frame and prepare the internal state for a new one.
+ * Depending on the flags, marks unused slots as inactive and performs
+ * pointer emulation.
+ */
+void input_mt_sync_frame(struct input_dev *dev)
+{
+ struct input_mt *mt = dev->mt;
+ struct input_mt_slot *s;
+
+ if (!mt)
+ return;
+
+ if (mt->flags & INPUT_MT_DROP_UNUSED) {
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+ if (s->frame == mt->frame)
+ continue;
+ input_mt_slot(dev, s - mt->slots);
+ input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+ }
+ }
+
+ input_mt_report_pointer_emulation(dev, (mt->flags & INPUT_MT_POINTER));
+
+ mt->frame++;
+}
+EXPORT_SYMBOL(input_mt_sync_frame);
+
+static int adjust_dual(int *begin, int step, int *end, int eq)
+{
+ int f, *p, s, c;
+
+ if (begin == end)
+ return 0;
+
+ f = *begin;
+ p = begin + step;
+ s = p == end ? f + 1 : *p;
+
+ for (; p != end; p += step)
+ if (*p < f)
+ s = f, f = *p;
+ else if (*p < s)
+ s = *p;
+
+ c = (f + s + 1) / 2;
+ if (c == 0 || (c > 0 && !eq))
+ return 0;
+ if (s < 0)
+ c *= 2;
+
+ for (p = begin; p != end; p += step)
+ *p -= c;
+
+ return (c < s && s <= 0) || (f >= 0 && f < c);
+}
+
+static void find_reduced_matrix(int *w, int nr, int nc, int nrc)
+{
+ int i, k, sum;
+
+ for (k = 0; k < nrc; k++) {
+ for (i = 0; i < nr; i++)
+ adjust_dual(w + i, nr, w + i + nrc, nr <= nc);
+ sum = 0;
+ for (i = 0; i < nrc; i += nr)
+ sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr);
+ if (!sum)
+ break;
+ }
+}
+
+static int input_mt_set_matrix(struct input_mt *mt,
+ const struct input_mt_pos *pos, int num_pos)
+{
+ const struct input_mt_pos *p;
+ struct input_mt_slot *s;
+ int *w = mt->red;
+ int x, y;
+
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+ if (!input_mt_is_active(s))
+ continue;
+ x = input_mt_get_value(s, ABS_MT_POSITION_X);
+ y = input_mt_get_value(s, ABS_MT_POSITION_Y);
+ for (p = pos; p != pos + num_pos; p++) {
+ int dx = x - p->x, dy = y - p->y;
+ *w++ = dx * dx + dy * dy;
+ }
+ }
+
+ return w - mt->red;
+}
+
+static void input_mt_set_slots(struct input_mt *mt,
+ int *slots, int num_pos)
+{
+ struct input_mt_slot *s;
+ int *w = mt->red, *p;
+
+ for (p = slots; p != slots + num_pos; p++)
+ *p = -1;
+
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+ if (!input_mt_is_active(s))
+ continue;
+ for (p = slots; p != slots + num_pos; p++)
+ if (*w++ < 0)
+ *p = s - mt->slots;
+ }
+
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+ if (input_mt_is_active(s))
+ continue;
+ for (p = slots; p != slots + num_pos; p++)
+ if (*p < 0) {
+ *p = s - mt->slots;
+ break;
+ }
+ }
+}
+
+/**
+ * input_mt_assign_slots() - perform a best-match assignment
+ * @dev: input device with allocated MT slots
+ * @slots: the slot assignment to be filled
+ * @pos: the position array to match
+ * @num_pos: number of positions
+ *
+ * Performs a best match against the current contacts and returns
+ * the slot assignment list. New contacts are assigned to unused
+ * slots.
+ *
+ * Returns zero on success, or negative error in case of failure.
+ */
+int input_mt_assign_slots(struct input_dev *dev, int *slots,
+ const struct input_mt_pos *pos, int num_pos)
+{
+ struct input_mt *mt = dev->mt;
+ int nrc;
+
+ if (!mt || !mt->red)
+ return -ENXIO;
+ if (num_pos > mt->num_slots)
+ return -EINVAL;
+ if (num_pos < 1)
+ return 0;
+
+ nrc = input_mt_set_matrix(mt, pos, num_pos);
+ find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc);
+ input_mt_set_slots(mt, slots, num_pos);
+
+ return 0;
+}
+EXPORT_SYMBOL(input_mt_assign_slots);
+
+/**
+ * input_mt_get_slot_by_key() - return slot matching key
+ * @dev: input device with allocated MT slots
+ * @key: the key of the sought slot
+ *
+ * Returns the slot of the given key, if it exists, otherwise
+ * set the key on the first unused slot and return.
+ *
+ * If no available slot can be found, -1 is returned.
+ */
+int input_mt_get_slot_by_key(struct input_dev *dev, int key)
+{
+ struct input_mt *mt = dev->mt;
+ struct input_mt_slot *s;
+
+ if (!mt)
+ return -1;
+
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
+ if (input_mt_is_active(s) && s->key == key)
+ return s - mt->slots;
+
+ for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
+ if (!input_mt_is_active(s)) {
+ s->key = key;
+ return s - mt->slots;
+ }
+
+ return -1;
+}
+EXPORT_SYMBOL(input_mt_get_slot_by_key);
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 8921c6180c51..5244f3d05b12 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -47,6 +47,8 @@ static DEFINE_MUTEX(input_mutex);
static struct input_handler *input_table[8];
+static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 };
+
static inline int is_event_supported(unsigned int code,
unsigned long *bm, unsigned int max)
{
@@ -69,42 +71,102 @@ static int input_defuzz_abs_event(int value, int old_val, int fuzz)
return value;
}
+static void input_start_autorepeat(struct input_dev *dev, int code)
+{
+ if (test_bit(EV_REP, dev->evbit) &&
+ dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
+ dev->timer.data) {
+ dev->repeat_key = code;
+ mod_timer(&dev->timer,
+ jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
+ }
+}
+
+static void input_stop_autorepeat(struct input_dev *dev)
+{
+ del_timer(&dev->timer);
+}
+
/*
* Pass event first through all filters and then, if event has not been
* filtered out, through all open handles. This function is called with
* dev->event_lock held and interrupts disabled.
*/
-static void input_pass_event(struct input_dev *dev,
- unsigned int type, unsigned int code, int value)
+static unsigned int input_to_handler(struct input_handle *handle,
+ struct input_value *vals, unsigned int count)
+{
+ struct input_handler *handler = handle->handler;
+ struct input_value *end = vals;
+ struct input_value *v;
+
+ for (v = vals; v != vals + count; v++) {
+ if (handler->filter &&
+ handler->filter(handle, v->type, v->code, v->value))
+ continue;
+ if (end != v)
+ *end = *v;
+ end++;
+ }
+
+ count = end - vals;
+ if (!count)
+ return 0;
+
+ if (handler->events)
+ handler->events(handle, vals, count);
+ else if (handler->event)
+ for (v = vals; v != end; v++)
+ handler->event(handle, v->type, v->code, v->value);
+
+ return count;
+}
+
+/*
+ * Pass values first through all filters and then, if event has not been
+ * filtered out, through all open handles. This function is called with
+ * dev->event_lock held and interrupts disabled.
+ */
+static void input_pass_values(struct input_dev *dev,
+ struct input_value *vals, unsigned int count)
{
- struct input_handler *handler;
struct input_handle *handle;
+ struct input_value *v;
+
+ if (!count)
+ return;
rcu_read_lock();
handle = rcu_dereference(dev->grab);
- if (handle)
- handle->handler->event(handle, type, code, value);
- else {
- bool filtered = false;
-
- list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
- if (!handle->open)
- continue;
+ if (handle) {
+ count = input_to_handler(handle, vals, count);
+ } else {
+ list_for_each_entry_rcu(handle, &dev->h_list, d_node)
+ if (handle->open)
+ count = input_to_handler(handle, vals, count);
+ }
- handler = handle->handler;
- if (!handler->filter) {
- if (filtered)
- break;
+ rcu_read_unlock();
- handler->event(handle, type, code, value);
+ add_input_randomness(vals->type, vals->code, vals->value);
- } else if (handler->filter(handle, type, code, value))
- filtered = true;
+ /* trigger auto repeat for key events */
+ for (v = vals; v != vals + count; v++) {
+ if (v->type == EV_KEY && v->value != 2) {
+ if (v->value)
+ input_start_autorepeat(dev, v->code);
+ else
+ input_stop_autorepeat(dev);
}
}
+}
- rcu_read_unlock();
+static void input_pass_event(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
+{
+ struct input_value vals[] = { { type, code, value } };
+
+ input_pass_values(dev, vals, ARRAY_SIZE(vals));
}
/*
@@ -121,18 +183,12 @@ static void input_repeat_key(unsigned long data)
if (test_bit(dev->repeat_key, dev->key) &&
is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
+ struct input_value vals[] = {
+ { EV_KEY, dev->repeat_key, 2 },
+ input_value_sync
+ };
- input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
-
- if (dev->sync) {
- /*
- * Only send SYN_REPORT if we are not in a middle
- * of driver parsing a new hardware packet.
- * Otherwise assume that the driver will send
- * SYN_REPORT once it's done.
- */
- input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
- }
+ input_pass_values(dev, vals, ARRAY_SIZE(vals));
if (dev->rep[REP_PERIOD])
mod_timer(&dev->timer, jiffies +
@@ -142,30 +198,17 @@ static void input_repeat_key(unsigned long data)
spin_unlock_irqrestore(&dev->event_lock, flags);
}
-static void input_start_autorepeat(struct input_dev *dev, int code)
-{
- if (test_bit(EV_REP, dev->evbit) &&
- dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
- dev->timer.data) {
- dev->repeat_key = code;
- mod_timer(&dev->timer,
- jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
- }
-}
-
-static void input_stop_autorepeat(struct input_dev *dev)
-{
- del_timer(&dev->timer);
-}
-
#define INPUT_IGNORE_EVENT 0
#define INPUT_PASS_TO_HANDLERS 1
#define INPUT_PASS_TO_DEVICE 2
+#define INPUT_SLOT 4
+#define INPUT_FLUSH 8
#define INPUT_PASS_TO_ALL (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
static int input_handle_abs_event(struct input_dev *dev,
unsigned int code, int *pval)
{
+ struct input_mt *mt = dev->mt;
bool is_mt_event;
int *pold;
@@ -174,8 +217,8 @@ static int input_handle_abs_event(struct input_dev *dev,
* "Stage" the event; we'll flush it later, when we
* get actual touch data.
*/
- if (*pval >= 0 && *pval < dev->mtsize)
- dev->slot = *pval;
+ if (mt && *pval >= 0 && *pval < mt->num_slots)
+ mt->slot = *pval;
return INPUT_IGNORE_EVENT;
}
@@ -184,9 +227,8 @@ static int input_handle_abs_event(struct input_dev *dev,
if (!is_mt_event) {
pold = &dev->absinfo[code].value;
- } else if (dev->mt) {
- struct input_mt_slot *mtslot = &dev->mt[dev->slot];
- pold = &mtslot->abs[code - ABS_MT_FIRST];
+ } else if (mt) {
+ pold = &mt->slots[mt->slot].abs[code - ABS_MT_FIRST];
} else {
/*
* Bypass filtering for multi-touch events when
@@ -205,16 +247,16 @@ static int input_handle_abs_event(struct input_dev *dev,
}
/* Flush pending "slot" event */
- if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
- input_abs_set_val(dev, ABS_MT_SLOT, dev->slot);
- input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);
+ if (is_mt_event && mt && mt->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
+ input_abs_set_val(dev, ABS_MT_SLOT, mt->slot);
+ return INPUT_PASS_TO_HANDLERS | INPUT_SLOT;
}
return INPUT_PASS_TO_HANDLERS;
}
-static void input_handle_event(struct input_dev *dev,
- unsigned int type, unsigned int code, int value)
+static int input_get_disposition(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
{
int disposition = INPUT_IGNORE_EVENT;
@@ -227,37 +269,34 @@ static void input_handle_event(struct input_dev *dev,
break;
case SYN_REPORT:
- if (!dev->sync) {
- dev->sync = true;
- disposition = INPUT_PASS_TO_HANDLERS;
- }
+ disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
break;
case SYN_MT_REPORT:
- dev->sync = false;
disposition = INPUT_PASS_TO_HANDLERS;
break;
}
break;
case EV_KEY:
- if (is_event_supported(code, dev->keybit, KEY_MAX) &&
- !!test_bit(code, dev->key) != value) {
+ if (is_event_supported(code, dev->keybit, KEY_MAX)) {
- if (value != 2) {
- __change_bit(code, dev->key);
- if (value)
- input_start_autorepeat(dev, code);
- else
- input_stop_autorepeat(dev);
+ /* auto-repeat bypasses state updates */
+ if (value == 2) {
+ disposition = INPUT_PASS_TO_HANDLERS;
+ break;
}
- disposition = INPUT_PASS_TO_HANDLERS;
+ if (!!test_bit(code, dev->key) != !!value) {
+
+ __change_bit(code, dev->key);
+ disposition = INPUT_PASS_TO_HANDLERS;
+ }
}
break;
case EV_SW:
if (is_event_supported(code, dev->swbit, SW_MAX) &&
- !!test_bit(code, dev->sw) != value) {
+ !!test_bit(code, dev->sw) != !!value) {
__change_bit(code, dev->sw);
disposition = INPUT_PASS_TO_HANDLERS;
@@ -284,7 +323,7 @@ static void input_handle_event(struct input_dev *dev,
case EV_LED:
if (is_event_supported(code, dev->ledbit, LED_MAX) &&
- !!test_bit(code, dev->led) != value) {
+ !!test_bit(code, dev->led) != !!value) {
__change_bit(code, dev->led);
disposition = INPUT_PASS_TO_ALL;
@@ -317,14 +356,48 @@ static void input_handle_event(struct input_dev *dev,
break;
}
- if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
- dev->sync = false;
+ return disposition;
+}
+
+static void input_handle_event(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
+{
+ int disposition;
+
+ disposition = input_get_disposition(dev, type, code, value);
if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
dev->event(dev, type, code, value);
- if (disposition & INPUT_PASS_TO_HANDLERS)
- input_pass_event(dev, type, code, value);
+ if (!dev->vals)
+ return;
+
+ if (disposition & INPUT_PASS_TO_HANDLERS) {
+ struct input_value *v;
+
+ if (disposition & INPUT_SLOT) {
+ v = &dev->vals[dev->num_vals++];
+ v->type = EV_ABS;
+ v->code = ABS_MT_SLOT;
+ v->value = dev->mt->slot;
+ }
+
+ v = &dev->vals[dev->num_vals++];
+ v->type = type;
+ v->code = code;
+ v->value = value;
+ }
+
+ if (disposition & INPUT_FLUSH) {
+ if (dev->num_vals >= 2)
+ input_pass_values(dev, dev->vals, dev->num_vals);
+ dev->num_vals = 0;
+ } else if (dev->num_vals >= dev->max_vals - 2) {
+ dev->vals[dev->num_vals++] = input_value_sync;
+ input_pass_values(dev, dev->vals, dev->num_vals);
+ dev->num_vals = 0;
+ }
+
}
/**
@@ -352,7 +425,6 @@ void input_event(struct input_dev *dev,
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
- add_input_randomness(type, code, value);
input_handle_event(dev, type, code, value);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
@@ -831,10 +903,12 @@ int input_set_keycode(struct input_dev *dev,
if (test_bit(EV_KEY, dev->evbit) &&
!is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
__test_and_clear_bit(old_keycode, dev->key)) {
+ struct input_value vals[] = {
+ { EV_KEY, old_keycode, 0 },
+ input_value_sync
+ };
- input_pass_event(dev, EV_KEY, old_keycode, 0);
- if (dev->sync)
- input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
+ input_pass_values(dev, vals, ARRAY_SIZE(vals));
}
out:
@@ -1416,6 +1490,7 @@ static void input_dev_release(struct device *device)
input_ff_destroy(dev);
input_mt_destroy_slots(dev);
kfree(dev->absinfo);
+ kfree(dev->vals);
kfree(dev);
module_put(THIS_MODULE);
@@ -1751,8 +1826,8 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
int i;
unsigned int events;
- if (dev->mtsize) {
- mt_slots = dev->mtsize;
+ if (dev->mt) {
+ mt_slots = dev->mt->num_slots;
} else if (test_bit(ABS_MT_TRACKING_ID, dev->absbit)) {
mt_slots = dev->absinfo[ABS_MT_TRACKING_ID].maximum -
dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1,
@@ -1778,6 +1853,9 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
if (test_bit(i, dev->relbit))
events++;
+ /* Make room for KEY and MSC events */
+ events += 7;
+
return events;
}
@@ -1816,6 +1894,7 @@ int input_register_device(struct input_dev *dev)
{
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handler *handler;
+ unsigned int packet_size;
const char *path;
int error;
@@ -1828,9 +1907,14 @@ int input_register_device(struct input_dev *dev)
/* Make sure that bitmasks not mentioned in dev->evbit are clean. */
input_cleanse_bitmasks(dev);
- if (!dev->hint_events_per_packet)
- dev->hint_events_per_packet =
- input_estimate_events_per_packet(dev);
+ packet_size = input_estimate_events_per_packet(dev);
+ if (dev->hint_events_per_packet < packet_size)
+ dev->hint_events_per_packet = packet_size;
+
+ dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2;
+ dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
+ if (!dev->vals)
+ return -ENOMEM;
/*
* If delay and period are pre-set by the driver, then autorepeating
diff --git a/drivers/input/keyboard/imx_keypad.c b/drivers/input/keyboard/imx_keypad.c
index ce68e361558c..cdc252612c0b 100644
--- a/drivers/input/keyboard/imx_keypad.c
+++ b/drivers/input/keyboard/imx_keypad.c
@@ -516,9 +516,9 @@ static int __devinit imx_keypad_probe(struct platform_device *pdev)
input_set_drvdata(input_dev, keypad);
/* Ensure that the keypad will stay dormant until opened */
- clk_enable(keypad->clk);
+ clk_prepare_enable(keypad->clk);
imx_keypad_inhibit(keypad);
- clk_disable(keypad->clk);
+ clk_disable_unprepare(keypad->clk);
error = request_irq(irq, imx_keypad_irq_handler, 0,
pdev->name, keypad);
diff --git a/drivers/input/misc/ab8500-ponkey.c b/drivers/input/misc/ab8500-ponkey.c
index f06231b7cab1..84ec691c05aa 100644
--- a/drivers/input/misc/ab8500-ponkey.c
+++ b/drivers/input/misc/ab8500-ponkey.c
@@ -74,8 +74,8 @@ static int __devinit ab8500_ponkey_probe(struct platform_device *pdev)
ponkey->idev = input;
ponkey->ab8500 = ab8500;
- ponkey->irq_dbf = ab8500_irq_get_virq(ab8500, irq_dbf);
- ponkey->irq_dbr = ab8500_irq_get_virq(ab8500, irq_dbr);
+ ponkey->irq_dbf = irq_dbf;
+ ponkey->irq_dbr = irq_dbr;
input->name = "AB8500 POn(PowerOn) Key";
input->dev.parent = &pdev->dev;
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index 736056897e50..6b1797503e34 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -405,7 +405,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
goto exit;
if (test_bit(ABS_MT_SLOT, dev->absbit)) {
int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
- input_mt_init_slots(dev, nslot);
+ input_mt_init_slots(dev, nslot, 0);
} else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
input_set_events_per_packet(dev, 60);
}
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 4a1347e91bdc..cf5af1f495ec 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -1620,7 +1620,7 @@ int alps_init(struct psmouse *psmouse)
case ALPS_PROTO_V3:
case ALPS_PROTO_V4:
set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
- input_mt_init_slots(dev1, 2);
+ input_mt_init_slots(dev1, 2, 0);
input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0);
input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0);
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
index d528c23e194f..3a78f235fa3e 100644
--- a/drivers/input/mouse/bcm5974.c
+++ b/drivers/input/mouse/bcm5974.c
@@ -40,6 +40,7 @@
#include <linux/usb/input.h>
#include <linux/hid.h>
#include <linux/mutex.h>
+#include <linux/input/mt.h>
#define USB_VENDOR_ID_APPLE 0x05ac
@@ -183,26 +184,26 @@ struct tp_finger {
__le16 abs_y; /* absolute y coodinate */
__le16 rel_x; /* relative x coodinate */
__le16 rel_y; /* relative y coodinate */
- __le16 size_major; /* finger size, major axis? */
- __le16 size_minor; /* finger size, minor axis? */
+ __le16 tool_major; /* tool area, major axis */
+ __le16 tool_minor; /* tool area, minor axis */
__le16 orientation; /* 16384 when point, else 15 bit angle */
- __le16 force_major; /* trackpad force, major axis? */
- __le16 force_minor; /* trackpad force, minor axis? */
+ __le16 touch_major; /* touch area, major axis */
+ __le16 touch_minor; /* touch area, minor axis */
__le16 unused[3]; /* zeros */
__le16 multi; /* one finger: varies, more fingers: constant */
} __attribute__((packed,aligned(2)));
/* trackpad finger data size, empirically at least ten fingers */
+#define MAX_FINGERS 16
#define SIZEOF_FINGER sizeof(struct tp_finger)
-#define SIZEOF_ALL_FINGERS (16 * SIZEOF_FINGER)
+#define SIZEOF_ALL_FINGERS (MAX_FINGERS * SIZEOF_FINGER)
#define MAX_FINGER_ORIENTATION 16384
/* device-specific parameters */
struct bcm5974_param {
- int dim; /* logical dimension */
- int fuzz; /* logical noise value */
- int devmin; /* device minimum reading */
- int devmax; /* device maximum reading */
+ int snratio; /* signal-to-noise ratio */
+ int min; /* device minimum reading */
+ int max; /* device maximum reading */
};
/* device-specific configuration */
@@ -219,6 +220,7 @@ struct bcm5974_config {
struct bcm5974_param w; /* finger width limits */
struct bcm5974_param x; /* horizontal limits */
struct bcm5974_param y; /* vertical limits */
+ struct bcm5974_param o; /* orientation limits */
};
/* logical device structure */
@@ -234,23 +236,16 @@ struct bcm5974 {
struct bt_data *bt_data; /* button transferred data */
struct urb *tp_urb; /* trackpad usb request block */
u8 *tp_data; /* trackpad transferred data */
- int fingers; /* number of fingers on trackpad */
+ const struct tp_finger *index[MAX_FINGERS]; /* finger index data */
+ struct input_mt_pos pos[MAX_FINGERS]; /* position array */
+ int slots[MAX_FINGERS]; /* slot assignments */
};
-/* logical dimensions */
-#define DIM_PRESSURE 256 /* maximum finger pressure */
-#define DIM_WIDTH 16 /* maximum finger width */
-#define DIM_X 1280 /* maximum trackpad x value */
-#define DIM_Y 800 /* maximum trackpad y value */
-
/* logical signal quality */
#define SN_PRESSURE 45 /* pressure signal-to-noise ratio */
-#define SN_WIDTH 100 /* width signal-to-noise ratio */
+#define SN_WIDTH 25 /* width signal-to-noise ratio */
#define SN_COORD 250 /* coordinate signal-to-noise ratio */
-
-/* pressure thresholds */
-#define PRESSURE_LOW (2 * DIM_PRESSURE / SN_PRESSURE)
-#define PRESSURE_HIGH (3 * PRESSURE_LOW)
+#define SN_ORIENT 10 /* orientation signal-to-noise ratio */
/* device constants */
static const struct bcm5974_config bcm5974_config_table[] = {
@@ -261,10 +256,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
0,
0x84, sizeof(struct bt_data),
0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4824, 5342 },
- { DIM_Y, DIM_Y / SN_COORD, -172, 5820 }
+ { SN_PRESSURE, 0, 256 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4824, 5342 },
+ { SN_COORD, -172, 5820 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
@@ -273,10 +269,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
0,
0x84, sizeof(struct bt_data),
0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4824, 4824 },
- { DIM_Y, DIM_Y / SN_COORD, -172, 4290 }
+ { SN_PRESSURE, 0, 256 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4824, 4824 },
+ { SN_COORD, -172, 4290 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI,
@@ -285,10 +282,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4460, 5166 },
- { DIM_Y, DIM_Y / SN_COORD, -75, 6700 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4460, 5166 },
+ { SN_COORD, -75, 6700 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI,
@@ -297,10 +295,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4620, 5140 },
- { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4620, 5140 },
+ { SN_COORD, -150, 6600 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI,
@@ -309,10 +308,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4616, 5112 },
- { DIM_Y, DIM_Y / SN_COORD, -142, 5234 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4616, 5112 },
+ { SN_COORD, -142, 5234 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI,
@@ -321,10 +321,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4415, 5050 },
- { DIM_Y, DIM_Y / SN_COORD, -55, 6680 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4415, 5050 },
+ { SN_COORD, -55, 6680 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI,
@@ -333,10 +334,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4620, 5140 },
- { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4620, 5140 },
+ { SN_COORD, -150, 6600 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI,
@@ -345,10 +347,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4750, 5280 },
- { DIM_Y, DIM_Y / SN_COORD, -150, 6730 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4750, 5280 },
+ { SN_COORD, -150, 6730 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI,
@@ -357,10 +360,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4620, 5140 },
- { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4620, 5140 },
+ { SN_COORD, -150, 6600 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{
USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI,
@@ -369,10 +373,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
HAS_INTEGRATED_BUTTON,
0x84, sizeof(struct bt_data),
0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
- { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
- { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
- { DIM_X, DIM_X / SN_COORD, -4750, 5280 },
- { DIM_Y, DIM_Y / SN_COORD, -150, 6730 }
+ { SN_PRESSURE, 0, 300 },
+ { SN_WIDTH, 0, 2048 },
+ { SN_COORD, -4750, 5280 },
+ { SN_COORD, -150, 6730 },
+ { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
},
{}
};
@@ -396,18 +401,11 @@ static inline int raw2int(__le16 x)
return (signed short)le16_to_cpu(x);
}
-/* scale device data to logical dimensions (asserts devmin < devmax) */
-static inline int int2scale(const struct bcm5974_param *p, int x)
-{
- return x * p->dim / (p->devmax - p->devmin);
-}
-
-/* all logical value ranges are [0,dim). */
-static inline int int2bound(const struct bcm5974_param *p, int x)
+static void set_abs(struct input_dev *input, unsigned int code,
+ const struct bcm5974_param *p)
{
- int s = int2scale(p, x);
-
- return clamp_val(s, 0, p->dim - 1);
+ int fuzz = p->snratio ? (p->max - p->min) / p->snratio : 0;
+ input_set_abs_params(input, code, p->min, p->max, fuzz, 0);
}
/* setup which logical events to report */
@@ -416,48 +414,30 @@ static void setup_events_to_report(struct input_dev *input_dev,
{
__set_bit(EV_ABS, input_dev->evbit);
- input_set_abs_params(input_dev, ABS_PRESSURE,
- 0, cfg->p.dim, cfg->p.fuzz, 0);
- input_set_abs_params(input_dev, ABS_TOOL_WIDTH,
- 0, cfg->w.dim, cfg->w.fuzz, 0);
- input_set_abs_params(input_dev, ABS_X,
- 0, cfg->x.dim, cfg->x.fuzz, 0);
- input_set_abs_params(input_dev, ABS_Y,
- 0, cfg->y.dim, cfg->y.fuzz, 0);
+ /* for synaptics only */
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 5, 0);
+ input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 16, 0, 0);
/* finger touch area */
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
- cfg->w.devmin, cfg->w.devmax, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
- cfg->w.devmin, cfg->w.devmax, 0, 0);
+ set_abs(input_dev, ABS_MT_TOUCH_MAJOR, &cfg->w);
+ set_abs(input_dev, ABS_MT_TOUCH_MINOR, &cfg->w);
/* finger approach area */
- input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR,
- cfg->w.devmin, cfg->w.devmax, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR,
- cfg->w.devmin, cfg->w.devmax, 0, 0);
+ set_abs(input_dev, ABS_MT_WIDTH_MAJOR, &cfg->w);
+ set_abs(input_dev, ABS_MT_WIDTH_MINOR, &cfg->w);
/* finger orientation */
- input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
- -MAX_FINGER_ORIENTATION,
- MAX_FINGER_ORIENTATION, 0, 0);
+ set_abs(input_dev, ABS_MT_ORIENTATION, &cfg->o);
/* finger position */
- input_set_abs_params(input_dev, ABS_MT_POSITION_X,
- cfg->x.devmin, cfg->x.devmax, 0, 0);
- input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
- cfg->y.devmin, cfg->y.devmax, 0, 0);
+ set_abs(input_dev, ABS_MT_POSITION_X, &cfg->x);
+ set_abs(input_dev, ABS_MT_POSITION_Y, &cfg->y);
__set_bit(EV_KEY, input_dev->evbit);
- __set_bit(BTN_TOUCH, input_dev->keybit);
- __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
- __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
- __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
- __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
__set_bit(BTN_LEFT, input_dev->keybit);
- __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
if (cfg->caps & HAS_INTEGRATED_BUTTON)
__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
- input_set_events_per_packet(input_dev, 60);
+ input_mt_init_slots(input_dev, MAX_FINGERS,
+ INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
}
/* report button data as logical button state */
@@ -477,24 +457,44 @@ static int report_bt_state(struct bcm5974 *dev, int size)
return 0;
}
-static void report_finger_data(struct input_dev *input,
- const struct bcm5974_config *cfg,
+static void report_finger_data(struct input_dev *input, int slot,
+ const struct input_mt_pos *pos,
const struct tp_finger *f)
{
+ input_mt_slot(input, slot);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+
input_report_abs(input, ABS_MT_TOUCH_MAJOR,
- raw2int(f->force_major) << 1);
+ raw2int(f->touch_major) << 1);
input_report_abs(input, ABS_MT_TOUCH_MINOR,
- raw2int(f->force_minor) << 1);
+ raw2int(f->touch_minor) << 1);
input_report_abs(input, ABS_MT_WIDTH_MAJOR,
- raw2int(f->size_major) << 1);
+ raw2int(f->tool_major) << 1);
input_report_abs(input, ABS_MT_WIDTH_MINOR,
- raw2int(f->size_minor) << 1);
+ raw2int(f->tool_minor) << 1);
input_report_abs(input, ABS_MT_ORIENTATION,
MAX_FINGER_ORIENTATION - raw2int(f->orientation));
- input_report_abs(input, ABS_MT_POSITION_X, raw2int(f->abs_x));
- input_report_abs(input, ABS_MT_POSITION_Y,
- cfg->y.devmin + cfg->y.devmax - raw2int(f->abs_y));
- input_mt_sync(input);
+ input_report_abs(input, ABS_MT_POSITION_X, pos->x);
+ input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
+}
+
+static void report_synaptics_data(struct input_dev *input,
+ const struct bcm5974_config *cfg,
+ const struct tp_finger *f, int raw_n)
+{
+ int abs_p = 0, abs_w = 0;
+
+ if (raw_n) {
+ int p = raw2int(f->touch_major);
+ int w = raw2int(f->tool_major);
+ if (p > 0 && raw2int(f->origin)) {
+ abs_p = clamp_val(256 * p / cfg->p.max, 0, 255);
+ abs_w = clamp_val(16 * w / cfg->w.max, 0, 15);
+ }
+ }
+
+ input_report_abs(input, ABS_PRESSURE, abs_p);
+ input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
}
/* report trackpad data as logical trackpad state */
@@ -503,9 +503,7 @@ static int report_tp_state(struct bcm5974 *dev, int size)
const struct bcm5974_config *c = &dev->cfg;
const struct tp_finger *f;
struct input_dev *input = dev->input;
- int raw_p, raw_w, raw_x, raw_y, raw_n, i;
- int ptest, origin, ibt = 0, nmin = 0, nmax = 0;
- int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0;
+ int raw_n, i, n = 0;
if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0)
return -EIO;
@@ -514,76 +512,29 @@ static int report_tp_state(struct bcm5974 *dev, int size)
f = (const struct tp_finger *)(dev->tp_data + c->tp_offset);
raw_n = (size - c->tp_offset) / SIZEOF_FINGER;
- /* always track the first finger; when detached, start over */
- if (raw_n) {
-
- /* report raw trackpad data */
- for (i = 0; i < raw_n; i++)
- report_finger_data(input, c, &f[i]);
-
- raw_p = raw2int(f->force_major);
- raw_w = raw2int(f->size_major);
- raw_x = raw2int(f->abs_x);
- raw_y = raw2int(f->abs_y);
-
- dprintk(9,
- "bcm5974: "
- "raw: p: %+05d w: %+05d x: %+05d y: %+05d n: %d\n",
- raw_p, raw_w, raw_x, raw_y, raw_n);
-
- ptest = int2bound(&c->p, raw_p);
- origin = raw2int(f->origin);
-
- /* while tracking finger still valid, count all fingers */
- if (ptest > PRESSURE_LOW && origin) {
- abs_p = ptest;
- abs_w = int2bound(&c->w, raw_w);
- abs_x = int2bound(&c->x, raw_x - c->x.devmin);
- abs_y = int2bound(&c->y, c->y.devmax - raw_y);
- while (raw_n--) {
- ptest = int2bound(&c->p,
- raw2int(f->force_major));
- if (ptest > PRESSURE_LOW)
- nmax++;
- if (ptest > PRESSURE_HIGH)
- nmin++;
- f++;
- }
- }
+ for (i = 0; i < raw_n; i++) {
+ if (raw2int(f[i].touch_major) == 0)
+ continue;
+ dev->pos[n].x = raw2int(f[i].abs_x);
+ dev->pos[n].y = c->y.min + c->y.max - raw2int(f[i].abs_y);
+ dev->index[n++] = &f[i];
}
- /* set the integrated button if applicable */
- if (c->tp_type == TYPE2)
- ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
-
- if (dev->fingers < nmin)
- dev->fingers = nmin;
- if (dev->fingers > nmax)
- dev->fingers = nmax;
-
- input_report_key(input, BTN_TOUCH, dev->fingers > 0);
- input_report_key(input, BTN_TOOL_FINGER, dev->fingers == 1);
- input_report_key(input, BTN_TOOL_DOUBLETAP, dev->fingers == 2);
- input_report_key(input, BTN_TOOL_TRIPLETAP, dev->fingers == 3);
- input_report_key(input, BTN_TOOL_QUADTAP, dev->fingers > 3);
-
- input_report_abs(input, ABS_PRESSURE, abs_p);
- input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
+ input_mt_assign_slots(input, dev->slots, dev->pos, n);
- if (abs_p) {
- input_report_abs(input, ABS_X, abs_x);
- input_report_abs(input, ABS_Y, abs_y);
+ for (i = 0; i < n; i++)
+ report_finger_data(input, dev->slots[i],
+ &dev->pos[i], dev->index[i]);
- dprintk(8,
- "bcm5974: abs: p: %+05d w: %+05d x: %+05d y: %+05d "
- "nmin: %d nmax: %d n: %d ibt: %d\n", abs_p, abs_w,
- abs_x, abs_y, nmin, nmax, dev->fingers, ibt);
+ input_mt_sync_frame(input);
- }
+ report_synaptics_data(input, c, f, raw_n);
/* type 2 reports button events via ibt only */
- if (c->tp_type == TYPE2)
+ if (c->tp_type == TYPE2) {
+ int ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
input_report_key(input, BTN_LEFT, ibt);
+ }
input_sync(input);
@@ -742,9 +693,11 @@ static int bcm5974_start_traffic(struct bcm5974 *dev)
goto err_out;
}
- error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
- if (error)
- goto err_reset_mode;
+ if (dev->bt_urb) {
+ error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
+ if (error)
+ goto err_reset_mode;
+ }
error = usb_submit_urb(dev->tp_urb, GFP_KERNEL);
if (error)
@@ -868,19 +821,23 @@ static int bcm5974_probe(struct usb_interface *iface,
mutex_init(&dev->pm_mutex);
/* setup urbs */
- dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!dev->bt_urb)
- goto err_free_devs;
+ if (cfg->tp_type == TYPE1) {
+ dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->bt_urb)
+ goto err_free_devs;
+ }
dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->tp_urb)
goto err_free_bt_urb;
- dev->bt_data = usb_alloc_coherent(dev->udev,
+ if (dev->bt_urb) {
+ dev->bt_data = usb_alloc_coherent(dev->udev,
dev->cfg.bt_datalen, GFP_KERNEL,
&dev->bt_urb->transfer_dma);
- if (!dev->bt_data)
- goto err_free_urb;
+ if (!dev->bt_data)
+ goto err_free_urb;
+ }
dev->tp_data = usb_alloc_coherent(dev->udev,
dev->cfg.tp_datalen, GFP_KERNEL,
@@ -888,10 +845,11 @@ static int bcm5974_probe(struct usb_interface *iface,
if (!dev->tp_data)
goto err_free_bt_buffer;
- usb_fill_int_urb(dev->bt_urb, udev,
- usb_rcvintpipe(udev, cfg->bt_ep),
- dev->bt_data, dev->cfg.bt_datalen,
- bcm5974_irq_button, dev, 1);
+ if (dev->bt_urb)
+ usb_fill_int_urb(dev->bt_urb, udev,
+ usb_rcvintpipe(udev, cfg->bt_ep),
+ dev->bt_data, dev->cfg.bt_datalen,
+ bcm5974_irq_button, dev, 1);
usb_fill_int_urb(dev->tp_urb, udev,
usb_rcvintpipe(udev, cfg->tp_ep),
@@ -929,8 +887,9 @@ err_free_buffer:
usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
dev->tp_data, dev->tp_urb->transfer_dma);
err_free_bt_buffer:
- usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
- dev->bt_data, dev->bt_urb->transfer_dma);
+ if (dev->bt_urb)
+ usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
+ dev->bt_data, dev->bt_urb->transfer_dma);
err_free_urb:
usb_free_urb(dev->tp_urb);
err_free_bt_urb:
@@ -951,8 +910,9 @@ static void bcm5974_disconnect(struct usb_interface *iface)
input_unregister_device(dev->input);
usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
dev->tp_data, dev->tp_urb->transfer_dma);
- usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
- dev->bt_data, dev->bt_urb->transfer_dma);
+ if (dev->bt_urb)
+ usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
+ dev->bt_data, dev->bt_urb->transfer_dma);
usb_free_urb(dev->tp_urb);
usb_free_urb(dev->bt_urb);
kfree(dev);
diff --git a/drivers/input/mouse/elantech.c b/drivers/input/mouse/elantech.c
index 479011004a11..1e8e42fb03a4 100644
--- a/drivers/input/mouse/elantech.c
+++ b/drivers/input/mouse/elantech.c
@@ -1004,7 +1004,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
ETP_WMAX_V2, 0, 0);
}
- input_mt_init_slots(dev, 2);
+ input_mt_init_slots(dev, 2, 0);
input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
break;
@@ -1035,7 +1035,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
ETP_WMAX_V2, 0, 0);
/* Multitouch capable pad, up to 5 fingers. */
- input_mt_init_slots(dev, ETP_MAX_FINGERS);
+ input_mt_init_slots(dev, ETP_MAX_FINGERS, 0);
input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
diff --git a/drivers/input/mouse/rpcmouse.c b/drivers/input/mouse/rpcmouse.c
index 272deddc8db6..21c60fea5d31 100644
--- a/drivers/input/mouse/rpcmouse.c
+++ b/drivers/input/mouse/rpcmouse.c
@@ -42,7 +42,7 @@ static irqreturn_t rpcmouse_irq(int irq, void *dev_id)
x = (short) iomd_readl(IOMD_MOUSEX);
y = (short) iomd_readl(IOMD_MOUSEY);
- b = (short) (__raw_readl(0xe0310000) ^ 0x70);
+ b = (short) (__raw_readl(IOMEM(0xe0310000)) ^ 0x70);
dx = x - rpcmouse_lastx;
dy = y - rpcmouse_lasty;
diff --git a/drivers/input/mouse/sentelic.c b/drivers/input/mouse/sentelic.c
index 3f5649f19082..e582922bacf7 100644
--- a/drivers/input/mouse/sentelic.c
+++ b/drivers/input/mouse/sentelic.c
@@ -721,6 +721,17 @@ static psmouse_ret_t fsp_process_byte(struct psmouse *psmouse)
switch (psmouse->packet[0] >> FSP_PKT_TYPE_SHIFT) {
case FSP_PKT_TYPE_ABS:
+
+ if ((packet[0] == 0x48 || packet[0] == 0x49) &&
+ packet[1] == 0 && packet[2] == 0) {
+ /*
+ * Ignore coordinate noise when finger leaving the
+ * surface, otherwise cursor may jump to upper-left
+ * corner.
+ */
+ packet[3] &= 0xf0;
+ }
+
abs_x = GET_ABS_X(packet);
abs_y = GET_ABS_Y(packet);
@@ -960,7 +971,7 @@ static int fsp_set_input_params(struct psmouse *psmouse)
input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0);
input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0);
- input_mt_init_slots(dev, 2);
+ input_mt_init_slots(dev, 2, 0);
input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0);
}
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index 14eaecea2b70..37033ade79d3 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -1232,7 +1232,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
- input_mt_init_slots(dev, 2);
+ input_mt_init_slots(dev, 2, 0);
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
ABS_MT_POSITION_Y);
/* Image sensors can report per-contact pressure */
@@ -1244,7 +1244,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
} else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
/* Non-image sensors with AGM use semi-mt */
__set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
- input_mt_init_slots(dev, 2);
+ input_mt_init_slots(dev, 2, 0);
set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
ABS_MT_POSITION_Y);
}
diff --git a/drivers/input/serio/ambakmi.c b/drivers/input/serio/ambakmi.c
index 2ffd110bd5bc..2e77246c2e5a 100644
--- a/drivers/input/serio/ambakmi.c
+++ b/drivers/input/serio/ambakmi.c
@@ -72,7 +72,7 @@ static int amba_kmi_open(struct serio *io)
unsigned int divisor;
int ret;
- ret = clk_enable(kmi->clk);
+ ret = clk_prepare_enable(kmi->clk);
if (ret)
goto out;
@@ -92,7 +92,7 @@ static int amba_kmi_open(struct serio *io)
return 0;
clk_disable:
- clk_disable(kmi->clk);
+ clk_disable_unprepare(kmi->clk);
out:
return ret;
}
@@ -104,7 +104,7 @@ static void amba_kmi_close(struct serio *io)
writeb(0, KMICR);
free_irq(kmi->irq, kmi);
- clk_disable(kmi->clk);
+ clk_disable_unprepare(kmi->clk);
}
static int __devinit amba_kmi_probe(struct amba_device *dev,
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index 6918773ce024..d6cc77a53c7e 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -335,6 +335,12 @@ static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
},
{
.matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"),
+ },
+ },
+ {
+ .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"),
DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"),
},
diff --git a/drivers/input/tablet/wacom_wac.c b/drivers/input/tablet/wacom_wac.c
index 532d067a9e07..2a81ce375f75 100644
--- a/drivers/input/tablet/wacom_wac.c
+++ b/drivers/input/tablet/wacom_wac.c
@@ -1530,7 +1530,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
- input_mt_init_slots(input_dev, features->touch_max);
+ input_mt_init_slots(input_dev, features->touch_max, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
0, 255, 0, 0);
@@ -1575,7 +1575,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
case TABLETPC2FG:
if (features->device_type == BTN_TOOL_FINGER) {
- input_mt_init_slots(input_dev, features->touch_max);
+ input_mt_init_slots(input_dev, features->touch_max, 0);
input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
0, MT_TOOL_MAX, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
@@ -1631,7 +1631,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
- input_mt_init_slots(input_dev, features->touch_max);
+ input_mt_init_slots(input_dev, features->touch_max, 0);
if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
__set_bit(BTN_TOOL_TRIPLETAP,
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 4623cc69fc60..e92615d0b1b0 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -1152,7 +1152,7 @@ static int __devinit mxt_probe(struct i2c_client *client,
/* For multi touch */
num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
- error = input_mt_init_slots(input_dev, num_mt_slots);
+ error = input_mt_init_slots(input_dev, num_mt_slots, 0);
if (error)
goto err_free_object;
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
diff --git a/drivers/input/touchscreen/cyttsp_core.c b/drivers/input/touchscreen/cyttsp_core.c
index f030d9ec795d..8e60437ac85b 100644
--- a/drivers/input/touchscreen/cyttsp_core.c
+++ b/drivers/input/touchscreen/cyttsp_core.c
@@ -571,7 +571,7 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
0, CY_MAXZ, 0, 0);
- input_mt_init_slots(input_dev, CY_MAX_ID);
+ input_mt_init_slots(input_dev, CY_MAX_ID, 0);
error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
diff --git a/drivers/input/touchscreen/edt-ft5x06.c b/drivers/input/touchscreen/edt-ft5x06.c
index b06a5e3a665e..099d144ab7c9 100644
--- a/drivers/input/touchscreen/edt-ft5x06.c
+++ b/drivers/input/touchscreen/edt-ft5x06.c
@@ -566,9 +566,12 @@ static ssize_t edt_ft5x06_debugfs_raw_data_read(struct file *file,
}
read = min_t(size_t, count, tsdata->raw_bufsize - *off);
- error = copy_to_user(buf, tsdata->raw_buffer + *off, read);
- if (!error)
- *off += read;
+ if (copy_to_user(buf, tsdata->raw_buffer + *off, read)) {
+ error = -EFAULT;
+ goto out;
+ }
+
+ *off += read;
out:
mutex_unlock(&tsdata->mutex);
return error ?: read;
@@ -779,7 +782,7 @@ static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client,
0, tsdata->num_x * 64 - 1, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y,
0, tsdata->num_y * 64 - 1, 0, 0);
- error = input_mt_init_slots(input, MAX_SUPPORT_POINTS);
+ error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
if (error) {
dev_err(&client->dev, "Unable to init MT slots.\n");
goto err_free_mem;
diff --git a/drivers/input/touchscreen/egalax_ts.c b/drivers/input/touchscreen/egalax_ts.c
index 70524dd34f42..c1e3460f1195 100644
--- a/drivers/input/touchscreen/egalax_ts.c
+++ b/drivers/input/touchscreen/egalax_ts.c
@@ -204,7 +204,7 @@ static int __devinit egalax_ts_probe(struct i2c_client *client,
ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
input_set_abs_params(input_dev,
ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0);
- input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS);
+ input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0);
input_set_drvdata(input_dev, ts);
diff --git a/drivers/input/touchscreen/ili210x.c b/drivers/input/touchscreen/ili210x.c
index c0044175a921..4ac69760ec08 100644
--- a/drivers/input/touchscreen/ili210x.c
+++ b/drivers/input/touchscreen/ili210x.c
@@ -252,7 +252,7 @@ static int __devinit ili210x_i2c_probe(struct i2c_client *client,
input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
/* Multi touch */
- input_mt_init_slots(input, MAX_TOUCHES);
+ input_mt_init_slots(input, MAX_TOUCHES, 0);
input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
diff --git a/drivers/input/touchscreen/mms114.c b/drivers/input/touchscreen/mms114.c
index 49c44bbf548d..560cf09d1c5a 100644
--- a/drivers/input/touchscreen/mms114.c
+++ b/drivers/input/touchscreen/mms114.c
@@ -404,7 +404,7 @@ static int __devinit mms114_probe(struct i2c_client *client,
input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0);
/* For multi touch */
- input_mt_init_slots(input_dev, MMS114_MAX_TOUCH);
+ input_mt_init_slots(input_dev, MMS114_MAX_TOUCH, 0);
input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
0, MMS114_MAX_AREA, 0, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
index 4ccde45b9da2..b49f0b836925 100644
--- a/drivers/input/touchscreen/penmount.c
+++ b/drivers/input/touchscreen/penmount.c
@@ -264,7 +264,7 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)
input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0);
if (pm->maxcontacts > 1) {
- input_mt_init_slots(pm->dev, pm->maxcontacts);
+ input_mt_init_slots(pm->dev, pm->maxcontacts, 0);
input_set_abs_params(pm->dev,
ABS_MT_POSITION_X, 0, max_x, 0, 0);
input_set_abs_params(pm->dev,
diff --git a/drivers/input/touchscreen/usbtouchscreen.c b/drivers/input/touchscreen/usbtouchscreen.c
index e32709e0dd65..721fdb3597ca 100644
--- a/drivers/input/touchscreen/usbtouchscreen.c
+++ b/drivers/input/touchscreen/usbtouchscreen.c
@@ -304,6 +304,45 @@ static int e2i_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
#define EGALAX_PKT_TYPE_REPT 0x80
#define EGALAX_PKT_TYPE_DIAG 0x0A
+static int egalax_init(struct usbtouch_usb *usbtouch)
+{
+ int ret, i;
+ unsigned char *buf;
+ struct usb_device *udev = interface_to_usbdev(usbtouch->interface);
+
+ /*
+ * An eGalax diagnostic packet kicks the device into using the right
+ * protocol. We send a "check active" packet. The response will be
+ * read later and ignored.
+ */
+
+ buf = kmalloc(3, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ buf[0] = EGALAX_PKT_TYPE_DIAG;
+ buf[1] = 1; /* length */
+ buf[2] = 'A'; /* command - check active */
+
+ for (i = 0; i < 3; i++) {
+ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, 0, buf, 3,
+ USB_CTRL_SET_TIMEOUT);
+ if (ret >= 0) {
+ ret = 0;
+ break;
+ }
+ if (ret != -EPIPE)
+ break;
+ }
+
+ kfree(buf);
+
+ return ret;
+}
+
static int egalax_read_data(struct usbtouch_usb *dev, unsigned char *pkt)
{
if ((pkt[0] & EGALAX_PKT_TYPE_MASK) != EGALAX_PKT_TYPE_REPT)
@@ -1056,6 +1095,7 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.process_pkt = usbtouch_process_multi,
.get_pkt_len = egalax_get_pkt_len,
.read_data = egalax_read_data,
+ .init = egalax_init,
},
#endif
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index 8f9ad2f893b8..9a83be6b6584 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -471,7 +471,7 @@ static int w8001_setup(struct w8001 *w8001)
case 5:
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
- input_mt_init_slots(dev, 2);
+ input_mt_init_slots(dev, 2, 0);
input_set_abs_params(dev, ABS_MT_POSITION_X,
0, touch.x, 0, 0);
input_set_abs_params(dev, ABS_MT_POSITION_Y,
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index b64502dfa9f4..e89daf1b21b4 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -266,7 +266,7 @@ static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
static int iommu_init_device(struct device *dev)
{
- struct pci_dev *dma_pdev, *pdev = to_pci_dev(dev);
+ struct pci_dev *dma_pdev = NULL, *pdev = to_pci_dev(dev);
struct iommu_dev_data *dev_data;
struct iommu_group *group;
u16 alias;
@@ -293,7 +293,9 @@ static int iommu_init_device(struct device *dev)
dev_data->alias_data = alias_data;
dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff);
- } else
+ }
+
+ if (dma_pdev == NULL)
dma_pdev = pci_dev_get(pdev);
/* Account for quirked devices */
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 2297ec193eb4..db820d7dd0bc 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2351,7 +2351,7 @@ static int iommu_should_identity_map(struct pci_dev *pdev, int startup)
return 0;
if (pdev->class >> 8 == PCI_CLASS_BRIDGE_PCI)
return 0;
- } else if (pdev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE)
+ } else if (pci_pcie_type(pdev) == PCI_EXP_TYPE_PCI_BRIDGE)
return 0;
/*
@@ -3546,10 +3546,10 @@ found:
struct pci_dev *bridge = bus->self;
if (!bridge || !pci_is_pcie(bridge) ||
- bridge->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE)
+ pci_pcie_type(bridge) == PCI_EXP_TYPE_PCI_BRIDGE)
return 0;
- if (bridge->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
+ 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;
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index e69de29bb2d1..1bb8bf6d7fd4 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -0,0 +1 @@
+# empty
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index c96bbaadeebd..16578d3b52bb 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -506,6 +506,16 @@ config LEDS_TRIGGER_BACKLIGHT
If unsure, say N.
+config LEDS_TRIGGER_CPU
+ bool "LED CPU Trigger"
+ depends on LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled by active CPUs. This shows
+ the active CPUs across an array of LEDs so you can see which
+ CPUs are active on the system at any given moment.
+
+ If unsure, say N.
+
config LEDS_TRIGGER_GPIO
tristate "LED GPIO Trigger"
depends on LEDS_TRIGGERS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index a4429a9217bc..a9b627c4f8ba 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -61,5 +61,6 @@ obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT) += ledtrig-heartbeat.o
obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT) += ledtrig-backlight.o
obj-$(CONFIG_LEDS_TRIGGER_GPIO) += ledtrig-gpio.o
+obj-$(CONFIG_LEDS_TRIGGER_CPU) += ledtrig-cpu.o
obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON) += ledtrig-default-on.o
obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o
diff --git a/drivers/leds/ledtrig-cpu.c b/drivers/leds/ledtrig-cpu.c
new file mode 100644
index 000000000000..b312056da14d
--- /dev/null
+++ b/drivers/leds/ledtrig-cpu.c
@@ -0,0 +1,163 @@
+/*
+ * ledtrig-cpu.c - LED trigger based on CPU activity
+ *
+ * This LED trigger will be registered for each possible CPU and named as
+ * cpu0, cpu1, cpu2, cpu3, etc.
+ *
+ * It can be bound to any LED just like other triggers using either a
+ * board file or via sysfs interface.
+ *
+ * An API named ledtrig_cpu is exported for any user, who want to add CPU
+ * activity indication in their code
+ *
+ * Copyright 2011 Linus Walleij <linus.walleij@linaro.org>
+ * Copyright 2011 - 2012 Bryan Wu <bryan.wu@canonical.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/percpu.h>
+#include <linux/syscore_ops.h>
+#include <linux/rwsem.h>
+#include "leds.h"
+
+#define MAX_NAME_LEN 8
+
+struct led_trigger_cpu {
+ char name[MAX_NAME_LEN];
+ struct led_trigger *_trig;
+ struct mutex lock;
+ int lock_is_inited;
+};
+
+static DEFINE_PER_CPU(struct led_trigger_cpu, cpu_trig);
+
+/**
+ * ledtrig_cpu - emit a CPU event as a trigger
+ * @evt: CPU event to be emitted
+ *
+ * Emit a CPU event on a CPU core, which will trigger a
+ * binded LED to turn on or turn off.
+ */
+void ledtrig_cpu(enum cpu_led_event ledevt)
+{
+ struct led_trigger_cpu *trig = &__get_cpu_var(cpu_trig);
+
+ /* mutex lock should be initialized before calling mutex_call() */
+ if (!trig->lock_is_inited)
+ return;
+
+ mutex_lock(&trig->lock);
+
+ /* Locate the correct CPU LED */
+ switch (ledevt) {
+ case CPU_LED_IDLE_END:
+ case CPU_LED_START:
+ /* Will turn the LED on, max brightness */
+ led_trigger_event(trig->_trig, LED_FULL);
+ break;
+
+ case CPU_LED_IDLE_START:
+ case CPU_LED_STOP:
+ case CPU_LED_HALTED:
+ /* Will turn the LED off */
+ led_trigger_event(trig->_trig, LED_OFF);
+ break;
+
+ default:
+ /* Will leave the LED as it is */
+ break;
+ }
+
+ mutex_unlock(&trig->lock);
+}
+EXPORT_SYMBOL(ledtrig_cpu);
+
+static int ledtrig_cpu_syscore_suspend(void)
+{
+ ledtrig_cpu(CPU_LED_STOP);
+ return 0;
+}
+
+static void ledtrig_cpu_syscore_resume(void)
+{
+ ledtrig_cpu(CPU_LED_START);
+}
+
+static void ledtrig_cpu_syscore_shutdown(void)
+{
+ ledtrig_cpu(CPU_LED_HALTED);
+}
+
+static struct syscore_ops ledtrig_cpu_syscore_ops = {
+ .shutdown = ledtrig_cpu_syscore_shutdown,
+ .suspend = ledtrig_cpu_syscore_suspend,
+ .resume = ledtrig_cpu_syscore_resume,
+};
+
+static int __init ledtrig_cpu_init(void)
+{
+ int cpu;
+
+ /* Supports up to 9999 cpu cores */
+ BUILD_BUG_ON(CONFIG_NR_CPUS > 9999);
+
+ /*
+ * Registering CPU led trigger for each CPU core here
+ * ignores CPU hotplug, but after this CPU hotplug works
+ * fine with this trigger.
+ */
+ for_each_possible_cpu(cpu) {
+ struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
+
+ mutex_init(&trig->lock);
+
+ snprintf(trig->name, MAX_NAME_LEN, "cpu%d", cpu);
+
+ mutex_lock(&trig->lock);
+ led_trigger_register_simple(trig->name, &trig->_trig);
+ trig->lock_is_inited = 1;
+ mutex_unlock(&trig->lock);
+ }
+
+ register_syscore_ops(&ledtrig_cpu_syscore_ops);
+
+ pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n");
+
+ return 0;
+}
+module_init(ledtrig_cpu_init);
+
+static void __exit ledtrig_cpu_exit(void)
+{
+ int cpu;
+
+ for_each_possible_cpu(cpu) {
+ struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu);
+
+ mutex_lock(&trig->lock);
+
+ led_trigger_unregister_simple(trig->_trig);
+ trig->_trig = NULL;
+ memset(trig->name, 0, MAX_NAME_LEN);
+ trig->lock_is_inited = 0;
+
+ mutex_unlock(&trig->lock);
+ mutex_destroy(&trig->lock);
+ }
+
+ unregister_syscore_ops(&ledtrig_cpu_syscore_ops);
+}
+module_exit(ledtrig_cpu_exit);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_AUTHOR("Bryan Wu <bryan.wu@canonical.com>");
+MODULE_DESCRIPTION("CPU LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/lguest/x86/core.c b/drivers/lguest/x86/core.c
index 39809035320a..4af12e1844d5 100644
--- a/drivers/lguest/x86/core.c
+++ b/drivers/lguest/x86/core.c
@@ -203,8 +203,8 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
* we set it now, so we can trap and pass that trap to the Guest if it
* uses the FPU.
*/
- if (cpu->ts)
- unlazy_fpu(current);
+ if (cpu->ts && user_has_fpu())
+ stts();
/*
* SYSENTER is an optimized way of doing system calls. We can't allow
@@ -234,6 +234,10 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
if (boot_cpu_has(X86_FEATURE_SEP))
wrmsr(MSR_IA32_SYSENTER_CS, __KERNEL_CS, 0);
+ /* Clear the host TS bit if it was set above. */
+ if (cpu->ts && user_has_fpu())
+ clts();
+
/*
* If the Guest page faulted, then the cr2 register will tell us the
* bad virtual address. We have to grab this now, because once we
@@ -249,7 +253,7 @@ void lguest_arch_run_guest(struct lg_cpu *cpu)
* a different CPU. So all the critical stuff should be done
* before this.
*/
- else if (cpu->regs->trapnum == 7)
+ else if (cpu->regs->trapnum == 7 && !user_has_fpu())
math_state_restore();
}
diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c
index 831d7517c759..3f8d032f180f 100644
--- a/drivers/macintosh/mediabay.c
+++ b/drivers/macintosh/mediabay.c
@@ -63,7 +63,7 @@ struct media_bay_info {
int value_count;
int timer;
struct macio_dev *mdev;
- struct mb_ops* ops;
+ const struct mb_ops* ops;
int index;
int cached_gpio;
int sleeping;
@@ -669,7 +669,7 @@ static int media_bay_resume(struct macio_dev *mdev)
/* Definitions of "ops" structures.
*/
-static struct mb_ops ohare_mb_ops = {
+static const struct mb_ops ohare_mb_ops = {
.name = "Ohare",
.content = ohare_mb_content,
.power = ohare_mb_power,
@@ -678,7 +678,7 @@ static struct mb_ops ohare_mb_ops = {
.un_reset_ide = ohare_mb_un_reset_ide,
};
-static struct mb_ops heathrow_mb_ops = {
+static const struct mb_ops heathrow_mb_ops = {
.name = "Heathrow",
.content = heathrow_mb_content,
.power = heathrow_mb_power,
@@ -687,7 +687,7 @@ static struct mb_ops heathrow_mb_ops = {
.un_reset_ide = heathrow_mb_un_reset_ide,
};
-static struct mb_ops keylargo_mb_ops = {
+static const struct mb_ops keylargo_mb_ops = {
.name = "KeyLargo",
.init = keylargo_mb_init,
.content = keylargo_mb_content,
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index d8abb90a6c2f..034233eefc82 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1555,6 +1555,7 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
unsigned long arg)
{
struct multipath *m = ti->private;
+ struct pgpath *pgpath;
struct block_device *bdev;
fmode_t mode;
unsigned long flags;
@@ -1570,12 +1571,14 @@ again:
if (!m->current_pgpath)
__choose_pgpath(m, 0);
- if (m->current_pgpath) {
- bdev = m->current_pgpath->path.dev->bdev;
- mode = m->current_pgpath->path.dev->mode;
+ pgpath = m->current_pgpath;
+
+ if (pgpath) {
+ bdev = pgpath->path.dev->bdev;
+ mode = pgpath->path.dev->mode;
}
- if (m->queue_io)
+ if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
r = -EAGAIN;
else if (!bdev)
r = -EIO;
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index f90069029aae..100368eb7991 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -1212,6 +1212,41 @@ struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
return &t->targets[(KEYS_PER_NODE * n) + k];
}
+static int count_device(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
+{
+ unsigned *num_devices = data;
+
+ (*num_devices)++;
+
+ return 0;
+}
+
+/*
+ * Check whether a table has no data devices attached using each
+ * target's iterate_devices method.
+ * Returns false if the result is unknown because a target doesn't
+ * support iterate_devices.
+ */
+bool dm_table_has_no_data_devices(struct dm_table *table)
+{
+ struct dm_target *uninitialized_var(ti);
+ unsigned i = 0, num_devices = 0;
+
+ while (i < dm_table_get_num_targets(table)) {
+ ti = dm_table_get_target(table, i++);
+
+ if (!ti->type->iterate_devices)
+ return false;
+
+ ti->type->iterate_devices(ti, count_device, &num_devices);
+ if (num_devices)
+ return false;
+ }
+
+ return true;
+}
+
/*
* Establish the new table's queue_limits and validate them.
*/
@@ -1354,17 +1389,25 @@ static int device_is_nonrot(struct dm_target *ti, struct dm_dev *dev,
return q && blk_queue_nonrot(q);
}
-static bool dm_table_is_nonrot(struct dm_table *t)
+static int device_is_not_random(struct dm_target *ti, struct dm_dev *dev,
+ sector_t start, sector_t len, void *data)
+{
+ struct request_queue *q = bdev_get_queue(dev->bdev);
+
+ return q && !blk_queue_add_random(q);
+}
+
+static bool dm_table_all_devices_attribute(struct dm_table *t,
+ iterate_devices_callout_fn func)
{
struct dm_target *ti;
unsigned i = 0;
- /* Ensure that all underlying device are non-rotational. */
while (i < dm_table_get_num_targets(t)) {
ti = dm_table_get_target(t, i++);
if (!ti->type->iterate_devices ||
- !ti->type->iterate_devices(ti, device_is_nonrot, NULL))
+ !ti->type->iterate_devices(ti, func, NULL))
return 0;
}
@@ -1396,7 +1439,8 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
if (!dm_table_discard_zeroes_data(t))
q->limits.discard_zeroes_data = 0;
- if (dm_table_is_nonrot(t))
+ /* Ensure that all underlying devices are non-rotational. */
+ if (dm_table_all_devices_attribute(t, device_is_nonrot))
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
else
queue_flag_clear_unlocked(QUEUE_FLAG_NONROT, q);
@@ -1404,6 +1448,15 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
dm_table_set_integrity(t);
/*
+ * Determine whether or not this queue's I/O timings contribute
+ * to the entropy pool, Only request-based targets use this.
+ * Clear QUEUE_FLAG_ADD_RANDOM if any underlying device does not
+ * have it set.
+ */
+ if (blk_queue_add_random(q) && dm_table_all_devices_attribute(t, device_is_not_random))
+ queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q);
+
+ /*
* QUEUE_FLAG_STACKABLE must be set after all queue settings are
* visible to other CPUs because, once the flag is set, incoming bios
* are processed by request-based dm, which refers to the queue
diff --git a/drivers/md/dm-thin.c b/drivers/md/dm-thin.c
index af1fc3b2c2ad..c29410af1e22 100644
--- a/drivers/md/dm-thin.c
+++ b/drivers/md/dm-thin.c
@@ -509,9 +509,9 @@ enum pool_mode {
struct pool_features {
enum pool_mode mode;
- unsigned zero_new_blocks:1;
- unsigned discard_enabled:1;
- unsigned discard_passdown:1;
+ bool zero_new_blocks:1;
+ bool discard_enabled:1;
+ bool discard_passdown:1;
};
struct thin_c;
@@ -580,7 +580,8 @@ struct pool_c {
struct dm_target_callbacks callbacks;
dm_block_t low_water_blocks;
- struct pool_features pf;
+ struct pool_features requested_pf; /* Features requested during table load */
+ struct pool_features adjusted_pf; /* Features used after adjusting for constituent devices */
};
/*
@@ -1839,6 +1840,47 @@ static void __requeue_bios(struct pool *pool)
/*----------------------------------------------------------------
* Binding of control targets to a pool object
*--------------------------------------------------------------*/
+static bool data_dev_supports_discard(struct pool_c *pt)
+{
+ struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
+
+ return q && blk_queue_discard(q);
+}
+
+/*
+ * If discard_passdown was enabled verify that the data device
+ * supports discards. Disable discard_passdown if not.
+ */
+static void disable_passdown_if_not_supported(struct pool_c *pt)
+{
+ struct pool *pool = pt->pool;
+ struct block_device *data_bdev = pt->data_dev->bdev;
+ struct queue_limits *data_limits = &bdev_get_queue(data_bdev)->limits;
+ sector_t block_size = pool->sectors_per_block << SECTOR_SHIFT;
+ const char *reason = NULL;
+ char buf[BDEVNAME_SIZE];
+
+ if (!pt->adjusted_pf.discard_passdown)
+ return;
+
+ if (!data_dev_supports_discard(pt))
+ reason = "discard unsupported";
+
+ else if (data_limits->max_discard_sectors < pool->sectors_per_block)
+ reason = "max discard sectors smaller than a block";
+
+ else if (data_limits->discard_granularity > block_size)
+ reason = "discard granularity larger than a block";
+
+ else if (block_size & (data_limits->discard_granularity - 1))
+ reason = "discard granularity not a factor of block size";
+
+ if (reason) {
+ DMWARN("Data device (%s) %s: Disabling discard passdown.", bdevname(data_bdev, buf), reason);
+ pt->adjusted_pf.discard_passdown = false;
+ }
+}
+
static int bind_control_target(struct pool *pool, struct dm_target *ti)
{
struct pool_c *pt = ti->private;
@@ -1847,31 +1889,16 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti)
* We want to make sure that degraded pools are never upgraded.
*/
enum pool_mode old_mode = pool->pf.mode;
- enum pool_mode new_mode = pt->pf.mode;
+ enum pool_mode new_mode = pt->adjusted_pf.mode;
if (old_mode > new_mode)
new_mode = old_mode;
pool->ti = ti;
pool->low_water_blocks = pt->low_water_blocks;
- pool->pf = pt->pf;
- set_pool_mode(pool, new_mode);
+ pool->pf = pt->adjusted_pf;
- /*
- * If discard_passdown was enabled verify that the data device
- * supports discards. Disable discard_passdown if not; otherwise
- * -EOPNOTSUPP will be returned.
- */
- /* FIXME: pull this out into a sep fn. */
- if (pt->pf.discard_passdown) {
- struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
- if (!q || !blk_queue_discard(q)) {
- char buf[BDEVNAME_SIZE];
- DMWARN("Discard unsupported by data device (%s): Disabling discard passdown.",
- bdevname(pt->data_dev->bdev, buf));
- pool->pf.discard_passdown = 0;
- }
- }
+ set_pool_mode(pool, new_mode);
return 0;
}
@@ -1889,9 +1916,9 @@ static void unbind_control_target(struct pool *pool, struct dm_target *ti)
static void pool_features_init(struct pool_features *pf)
{
pf->mode = PM_WRITE;
- pf->zero_new_blocks = 1;
- pf->discard_enabled = 1;
- pf->discard_passdown = 1;
+ pf->zero_new_blocks = true;
+ pf->discard_enabled = true;
+ pf->discard_passdown = true;
}
static void __pool_destroy(struct pool *pool)
@@ -2119,13 +2146,13 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
argc--;
if (!strcasecmp(arg_name, "skip_block_zeroing"))
- pf->zero_new_blocks = 0;
+ pf->zero_new_blocks = false;
else if (!strcasecmp(arg_name, "ignore_discard"))
- pf->discard_enabled = 0;
+ pf->discard_enabled = false;
else if (!strcasecmp(arg_name, "no_discard_passdown"))
- pf->discard_passdown = 0;
+ pf->discard_passdown = false;
else if (!strcasecmp(arg_name, "read_only"))
pf->mode = PM_READ_ONLY;
@@ -2259,8 +2286,9 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
pt->metadata_dev = metadata_dev;
pt->data_dev = data_dev;
pt->low_water_blocks = low_water_blocks;
- pt->pf = pf;
+ pt->adjusted_pf = pt->requested_pf = pf;
ti->num_flush_requests = 1;
+
/*
* Only need to enable discards if the pool should pass
* them down to the data device. The thin device's discard
@@ -2268,12 +2296,14 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
*/
if (pf.discard_enabled && pf.discard_passdown) {
ti->num_discard_requests = 1;
+
/*
* Setting 'discards_supported' circumvents the normal
* stacking of discard limits (this keeps the pool and
* thin devices' discard limits consistent).
*/
ti->discards_supported = true;
+ ti->discard_zeroes_data_unsupported = true;
}
ti->private = pt;
@@ -2703,7 +2733,7 @@ static int pool_status(struct dm_target *ti, status_type_t type,
format_dev_t(buf2, pt->data_dev->bdev->bd_dev),
(unsigned long)pool->sectors_per_block,
(unsigned long long)pt->low_water_blocks);
- emit_flags(&pt->pf, result, sz, maxlen);
+ emit_flags(&pt->requested_pf, result, sz, maxlen);
break;
}
@@ -2732,20 +2762,21 @@ static int pool_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
}
-static void set_discard_limits(struct pool *pool, struct queue_limits *limits)
+static void set_discard_limits(struct pool_c *pt, struct queue_limits *limits)
{
- /*
- * FIXME: these limits may be incompatible with the pool's data device
- */
+ struct pool *pool = pt->pool;
+ struct queue_limits *data_limits;
+
limits->max_discard_sectors = pool->sectors_per_block;
/*
- * This is just a hint, and not enforced. We have to cope with
- * bios that cover a block partially. A discard that spans a block
- * boundary is not sent to this target.
+ * discard_granularity is just a hint, and not enforced.
*/
- limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
- limits->discard_zeroes_data = pool->pf.zero_new_blocks;
+ if (pt->adjusted_pf.discard_passdown) {
+ data_limits = &bdev_get_queue(pt->data_dev->bdev)->limits;
+ limits->discard_granularity = data_limits->discard_granularity;
+ } else
+ limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
}
static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
@@ -2755,15 +2786,25 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
blk_limits_io_min(limits, 0);
blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
- if (pool->pf.discard_enabled)
- set_discard_limits(pool, limits);
+
+ /*
+ * pt->adjusted_pf is a staging area for the actual features to use.
+ * They get transferred to the live pool in bind_control_target()
+ * called from pool_preresume().
+ */
+ if (!pt->adjusted_pf.discard_enabled)
+ return;
+
+ disable_passdown_if_not_supported(pt);
+
+ set_discard_limits(pt, limits);
}
static struct target_type pool_target = {
.name = "thin-pool",
.features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
DM_TARGET_IMMUTABLE,
- .version = {1, 3, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = pool_ctr,
.dtr = pool_dtr,
@@ -3042,19 +3083,19 @@ static int thin_iterate_devices(struct dm_target *ti,
return 0;
}
+/*
+ * A thin device always inherits its queue limits from its pool.
+ */
static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
{
struct thin_c *tc = ti->private;
- struct pool *pool = tc->pool;
- blk_limits_io_min(limits, 0);
- blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
- set_discard_limits(pool, limits);
+ *limits = bdev_get_queue(tc->pool_dev->bdev)->limits;
}
static struct target_type thin_target = {
.name = "thin",
- .version = {1, 3, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = thin_ctr,
.dtr = thin_dtr,
diff --git a/drivers/md/dm-verity.c b/drivers/md/dm-verity.c
index 254d19268ad2..892ae2766aa6 100644
--- a/drivers/md/dm-verity.c
+++ b/drivers/md/dm-verity.c
@@ -718,8 +718,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
v->hash_dev_block_bits = ffs(num) - 1;
if (sscanf(argv[5], "%llu%c", &num_ll, &dummy) != 1 ||
- num_ll << (v->data_dev_block_bits - SECTOR_SHIFT) !=
- (sector_t)num_ll << (v->data_dev_block_bits - SECTOR_SHIFT)) {
+ (sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT))
+ >> (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll) {
ti->error = "Invalid data blocks";
r = -EINVAL;
goto bad;
@@ -733,8 +733,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
}
if (sscanf(argv[6], "%llu%c", &num_ll, &dummy) != 1 ||
- num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT) !=
- (sector_t)num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT)) {
+ (sector_t)(num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT))
+ >> (v->hash_dev_block_bits - SECTOR_SHIFT) != num_ll) {
ti->error = "Invalid hash start";
r = -EINVAL;
goto bad;
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 4e09b6ff5b49..67ffa391edcf 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -865,10 +865,14 @@ static void dm_done(struct request *clone, int error, bool mapped)
{
int r = error;
struct dm_rq_target_io *tio = clone->end_io_data;
- dm_request_endio_fn rq_end_io = tio->ti->type->rq_end_io;
+ dm_request_endio_fn rq_end_io = NULL;
- if (mapped && rq_end_io)
- r = rq_end_io(tio->ti, clone, error, &tio->info);
+ if (tio->ti) {
+ rq_end_io = tio->ti->type->rq_end_io;
+
+ if (mapped && rq_end_io)
+ r = rq_end_io(tio->ti, clone, error, &tio->info);
+ }
if (r <= 0)
/* The target wants to complete the I/O */
@@ -1588,15 +1592,6 @@ static int map_request(struct dm_target *ti, struct request *clone,
int r, requeued = 0;
struct dm_rq_target_io *tio = clone->end_io_data;
- /*
- * Hold the md reference here for the in-flight I/O.
- * We can't rely on the reference count by device opener,
- * because the device may be closed during the request completion
- * when all bios are completed.
- * See the comment in rq_completed() too.
- */
- dm_get(md);
-
tio->ti = ti;
r = ti->type->map_rq(ti, clone, &tio->info);
switch (r) {
@@ -1628,6 +1623,26 @@ static int map_request(struct dm_target *ti, struct request *clone,
return requeued;
}
+static struct request *dm_start_request(struct mapped_device *md, struct request *orig)
+{
+ struct request *clone;
+
+ blk_start_request(orig);
+ clone = orig->special;
+ atomic_inc(&md->pending[rq_data_dir(clone)]);
+
+ /*
+ * Hold the md reference here for the in-flight I/O.
+ * We can't rely on the reference count by device opener,
+ * because the device may be closed during the request completion
+ * when all bios are completed.
+ * See the comment in rq_completed() too.
+ */
+ dm_get(md);
+
+ return clone;
+}
+
/*
* q->request_fn for request-based dm.
* Called with the queue lock held.
@@ -1657,14 +1672,21 @@ static void dm_request_fn(struct request_queue *q)
pos = blk_rq_pos(rq);
ti = dm_table_find_target(map, pos);
- BUG_ON(!dm_target_is_valid(ti));
+ if (!dm_target_is_valid(ti)) {
+ /*
+ * Must perform setup, that dm_done() requires,
+ * before calling dm_kill_unmapped_request
+ */
+ DMERR_LIMIT("request attempted access beyond the end of device");
+ clone = dm_start_request(md, rq);
+ dm_kill_unmapped_request(clone, -EIO);
+ continue;
+ }
if (ti->type->busy && ti->type->busy(ti))
goto delay_and_out;
- blk_start_request(rq);
- clone = rq->special;
- atomic_inc(&md->pending[rq_data_dir(clone)]);
+ clone = dm_start_request(md, rq);
spin_unlock(q->queue_lock);
if (map_request(ti, clone, md))
@@ -1684,8 +1706,6 @@ delay_and_out:
blk_delay_queue(q, HZ / 10);
out:
dm_table_put(map);
-
- return;
}
int dm_underlying_device_busy(struct request_queue *q)
@@ -2409,7 +2429,7 @@ static void dm_queue_flush(struct mapped_device *md)
*/
struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
{
- struct dm_table *map = ERR_PTR(-EINVAL);
+ struct dm_table *live_map, *map = ERR_PTR(-EINVAL);
struct queue_limits limits;
int r;
@@ -2419,6 +2439,19 @@ struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
if (!dm_suspended_md(md))
goto out;
+ /*
+ * If the new table has no data devices, retain the existing limits.
+ * This helps multipath with queue_if_no_path if all paths disappear,
+ * then new I/O is queued based on these limits, and then some paths
+ * reappear.
+ */
+ if (dm_table_has_no_data_devices(table)) {
+ live_map = dm_get_live_table(md);
+ if (live_map)
+ limits = md->queue->limits;
+ dm_table_put(live_map);
+ }
+
r = dm_calculate_queue_limits(table, &limits);
if (r) {
map = ERR_PTR(r);
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 52eef493d266..6a99fefaa743 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -54,6 +54,7 @@ void dm_table_event_callback(struct dm_table *t,
void (*fn)(void *), void *context);
struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);
struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector);
+bool dm_table_has_no_data_devices(struct dm_table *table);
int dm_calculate_queue_limits(struct dm_table *table,
struct queue_limits *limits);
void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 3f6203a4c7ea..308e87b417e0 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -7619,6 +7619,8 @@ static int remove_and_add_spares(struct mddev *mddev)
}
}
}
+ if (removed)
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
return spares;
}
@@ -7632,9 +7634,11 @@ static void reap_sync_thread(struct mddev *mddev)
!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
/* success...*/
/* activate any spares */
- if (mddev->pers->spare_active(mddev))
+ if (mddev->pers->spare_active(mddev)) {
sysfs_notify(&mddev->kobj, NULL,
"degraded");
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
+ }
}
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
mddev->pers->finish_reshape)
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 1c2eb38f3c51..0138a727c1f3 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -1512,14 +1512,16 @@ static int _enough(struct r10conf *conf, struct geom *geo, int ignore)
do {
int n = conf->copies;
int cnt = 0;
+ int this = first;
while (n--) {
- if (conf->mirrors[first].rdev &&
- first != ignore)
+ if (conf->mirrors[this].rdev &&
+ this != ignore)
cnt++;
- first = (first+1) % geo->raid_disks;
+ this = (this+1) % geo->raid_disks;
}
if (cnt == 0)
return 0;
+ first = (first + geo->near_copies) % geo->raid_disks;
} while (first != 0);
return 1;
}
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index adda94df5eb2..0689173fd9f5 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -393,6 +393,8 @@ static int calc_degraded(struct r5conf *conf)
degraded = 0;
for (i = 0; i < conf->previous_raid_disks; i++) {
struct md_rdev *rdev = rcu_dereference(conf->disks[i].rdev);
+ if (rdev && test_bit(Faulty, &rdev->flags))
+ rdev = rcu_dereference(conf->disks[i].replacement);
if (!rdev || test_bit(Faulty, &rdev->flags))
degraded++;
else if (test_bit(In_sync, &rdev->flags))
@@ -417,6 +419,8 @@ static int calc_degraded(struct r5conf *conf)
degraded2 = 0;
for (i = 0; i < conf->raid_disks; i++) {
struct md_rdev *rdev = rcu_dereference(conf->disks[i].rdev);
+ if (rdev && test_bit(Faulty, &rdev->flags))
+ rdev = rcu_dereference(conf->disks[i].replacement);
if (!rdev || test_bit(Faulty, &rdev->flags))
degraded2++;
else if (test_bit(In_sync, &rdev->flags))
@@ -1587,6 +1591,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
#ifdef CONFIG_MULTICORE_RAID456
init_waitqueue_head(&nsh->ops.wait_for_ops);
#endif
+ spin_lock_init(&nsh->stripe_lock);
list_add(&nsh->lru, &newstripes);
}
@@ -4192,7 +4197,7 @@ static void make_request(struct mddev *mddev, struct bio * bi)
finish_wait(&conf->wait_for_overlap, &w);
set_bit(STRIPE_HANDLE, &sh->state);
clear_bit(STRIPE_DELAYED, &sh->state);
- if ((bi->bi_rw & REQ_NOIDLE) &&
+ if ((bi->bi_rw & REQ_SYNC) &&
!test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
atomic_inc(&conf->preread_active_stripes);
release_stripe_plug(mddev, sh);
diff --git a/drivers/media/dvb/ngene/ngene-cards.c b/drivers/media/dvb/ngene/ngene-cards.c
index 72ee8de02260..0a497be97af8 100644
--- a/drivers/media/dvb/ngene/ngene-cards.c
+++ b/drivers/media/dvb/ngene/ngene-cards.c
@@ -524,7 +524,7 @@ static void ngene_resume(struct pci_dev *dev)
printk(KERN_INFO DEVICE_NAME ": resume\n");
}
-static struct pci_error_handlers ngene_errors = {
+static const struct pci_error_handlers ngene_errors = {
.error_detected = ngene_error_detected,
.link_reset = ngene_link_reset,
.slot_reset = ngene_slot_reset,
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 42b3ce9d80fc..9cce5d70ed52 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -2,6 +2,9 @@
# Makefile for memory devices
#
+ifeq ($(CONFIG_DDR),y)
+obj-$(CONFIG_OF) += of_memory.o
+endif
obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c
index 33a4396b24cb..06d31c99e6ac 100644
--- a/drivers/memory/emif.c
+++ b/drivers/memory/emif.c
@@ -18,6 +18,7 @@
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/module.h>
@@ -25,6 +26,7 @@
#include <linux/spinlock.h>
#include <memory/jedec_ddr.h>
#include "emif.h"
+#include "of_memory.h"
/**
* struct emif_data - Per device static data for driver's use
@@ -49,6 +51,7 @@
* frequency in effect at the moment)
* @plat_data: Pointer to saved platform data.
* @debugfs_root: dentry to the root folder for EMIF in debugfs
+ * @np_ddr: Pointer to ddr device tree node
*/
struct emif_data {
u8 duplicate;
@@ -63,6 +66,7 @@ struct emif_data {
struct emif_regs *curr_regs;
struct emif_platform_data *plat_data;
struct dentry *debugfs_root;
+ struct device_node *np_ddr;
};
static struct emif_data *emif1;
@@ -71,6 +75,7 @@ static unsigned long irq_state;
static u32 t_ck; /* DDR clock period in ps */
static LIST_HEAD(device_list);
+#ifdef CONFIG_DEBUG_FS
static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif,
struct emif_regs *regs)
{
@@ -162,23 +167,23 @@ static int __init_or_module emif_debugfs_init(struct emif_data *emif)
int ret;
dentry = debugfs_create_dir(dev_name(emif->dev), NULL);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
+ if (!dentry) {
+ ret = -ENOMEM;
goto err0;
}
emif->debugfs_root = dentry;
dentry = debugfs_create_file("regcache_dump", S_IRUGO,
emif->debugfs_root, emif, &emif_regdump_fops);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
+ if (!dentry) {
+ ret = -ENOMEM;
goto err1;
}
dentry = debugfs_create_file("mr4", S_IRUGO,
emif->debugfs_root, emif, &emif_mr4_fops);
- if (IS_ERR(dentry)) {
- ret = PTR_ERR(dentry);
+ if (!dentry) {
+ ret = -ENOMEM;
goto err1;
}
@@ -194,6 +199,16 @@ static void __exit emif_debugfs_exit(struct emif_data *emif)
debugfs_remove_recursive(emif->debugfs_root);
emif->debugfs_root = NULL;
}
+#else
+static inline int __init_or_module emif_debugfs_init(struct emif_data *emif)
+{
+ return 0;
+}
+
+static inline void __exit emif_debugfs_exit(struct emif_data *emif)
+{
+}
+#endif
/*
* Calculate the period of DDR clock from frequency value
@@ -1148,6 +1163,168 @@ static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs,
return valid;
}
+#if defined(CONFIG_OF)
+static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
+ struct emif_data *emif)
+{
+ struct emif_custom_configs *cust_cfgs = NULL;
+ int len;
+ const int *lpmode, *poll_intvl;
+
+ lpmode = of_get_property(np_emif, "low-power-mode", &len);
+ poll_intvl = of_get_property(np_emif, "temp-alert-poll-interval", &len);
+
+ if (lpmode || poll_intvl)
+ cust_cfgs = devm_kzalloc(emif->dev, sizeof(*cust_cfgs),
+ GFP_KERNEL);
+
+ if (!cust_cfgs)
+ return;
+
+ if (lpmode) {
+ cust_cfgs->mask |= EMIF_CUSTOM_CONFIG_LPMODE;
+ cust_cfgs->lpmode = *lpmode;
+ of_property_read_u32(np_emif,
+ "low-power-mode-timeout-performance",
+ &cust_cfgs->lpmode_timeout_performance);
+ of_property_read_u32(np_emif,
+ "low-power-mode-timeout-power",
+ &cust_cfgs->lpmode_timeout_power);
+ of_property_read_u32(np_emif,
+ "low-power-mode-freq-threshold",
+ &cust_cfgs->lpmode_freq_threshold);
+ }
+
+ if (poll_intvl) {
+ cust_cfgs->mask |=
+ EMIF_CUSTOM_CONFIG_TEMP_ALERT_POLL_INTERVAL;
+ cust_cfgs->temp_alert_poll_interval_ms = *poll_intvl;
+ }
+
+ if (!is_custom_config_valid(cust_cfgs, emif->dev)) {
+ devm_kfree(emif->dev, cust_cfgs);
+ return;
+ }
+
+ emif->plat_data->custom_configs = cust_cfgs;
+}
+
+static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
+ struct device_node *np_ddr,
+ struct ddr_device_info *dev_info)
+{
+ u32 density = 0, io_width = 0;
+ int len;
+
+ if (of_find_property(np_emif, "cs1-used", &len))
+ dev_info->cs1_used = true;
+
+ if (of_find_property(np_emif, "cal-resistor-per-cs", &len))
+ dev_info->cal_resistors_per_cs = true;
+
+ if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s4"))
+ dev_info->type = DDR_TYPE_LPDDR2_S4;
+ else if (of_device_is_compatible(np_ddr , "jedec,lpddr2-s2"))
+ dev_info->type = DDR_TYPE_LPDDR2_S2;
+
+ of_property_read_u32(np_ddr, "density", &density);
+ of_property_read_u32(np_ddr, "io-width", &io_width);
+
+ /* Convert from density in Mb to the density encoding in jedc_ddr.h */
+ if (density & (density - 1))
+ dev_info->density = 0;
+ else
+ dev_info->density = __fls(density) - 5;
+
+ /* Convert from io_width in bits to io_width encoding in jedc_ddr.h */
+ if (io_width & (io_width - 1))
+ dev_info->io_width = 0;
+ else
+ dev_info->io_width = __fls(io_width) - 1;
+}
+
+static struct emif_data * __init_or_module of_get_memory_device_details(
+ struct device_node *np_emif, struct device *dev)
+{
+ struct emif_data *emif = NULL;
+ struct ddr_device_info *dev_info = NULL;
+ struct emif_platform_data *pd = NULL;
+ struct device_node *np_ddr;
+ int len;
+
+ np_ddr = of_parse_phandle(np_emif, "device-handle", 0);
+ if (!np_ddr)
+ goto error;
+ emif = devm_kzalloc(dev, sizeof(struct emif_data), GFP_KERNEL);
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ dev_info = devm_kzalloc(dev, sizeof(*dev_info), GFP_KERNEL);
+
+ if (!emif || !pd || !dev_info) {
+ dev_err(dev, "%s: Out of memory!!\n",
+ __func__);
+ goto error;
+ }
+
+ emif->plat_data = pd;
+ pd->device_info = dev_info;
+ emif->dev = dev;
+ emif->np_ddr = np_ddr;
+ emif->temperature_level = SDRAM_TEMP_NOMINAL;
+
+ if (of_device_is_compatible(np_emif, "ti,emif-4d"))
+ emif->plat_data->ip_rev = EMIF_4D;
+ else if (of_device_is_compatible(np_emif, "ti,emif-4d5"))
+ emif->plat_data->ip_rev = EMIF_4D5;
+
+ of_property_read_u32(np_emif, "phy-type", &pd->phy_type);
+
+ if (of_find_property(np_emif, "hw-caps-ll-interface", &len))
+ pd->hw_caps |= EMIF_HW_CAPS_LL_INTERFACE;
+
+ of_get_ddr_info(np_emif, np_ddr, dev_info);
+ if (!is_dev_data_valid(pd->device_info->type, pd->device_info->density,
+ pd->device_info->io_width, pd->phy_type, pd->ip_rev,
+ emif->dev)) {
+ dev_err(dev, "%s: invalid device data!!\n", __func__);
+ goto error;
+ }
+ /*
+ * For EMIF instances other than EMIF1 see if the devices connected
+ * are exactly same as on EMIF1(which is typically the case). If so,
+ * mark it as a duplicate of EMIF1. This will save some memory and
+ * computation.
+ */
+ if (emif1 && emif1->np_ddr == np_ddr) {
+ emif->duplicate = true;
+ goto out;
+ } else if (emif1) {
+ dev_warn(emif->dev, "%s: Non-symmetric DDR geometry\n",
+ __func__);
+ }
+
+ of_get_custom_configs(np_emif, emif);
+ emif->plat_data->timings = of_get_ddr_timings(np_ddr, emif->dev,
+ emif->plat_data->device_info->type,
+ &emif->plat_data->timings_arr_size);
+
+ emif->plat_data->min_tck = of_get_min_tck(np_ddr, emif->dev);
+ goto out;
+
+error:
+ return NULL;
+out:
+ return emif;
+}
+
+#else
+
+static struct emif_data * __init_or_module of_get_memory_device_details(
+ struct device_node *np_emif, struct device *dev)
+{
+ return NULL;
+}
+#endif
+
static struct emif_data *__init_or_module get_device_details(
struct platform_device *pdev)
{
@@ -1267,7 +1444,11 @@ static int __init_or_module emif_probe(struct platform_device *pdev)
struct resource *res;
int irq;
- emif = get_device_details(pdev);
+ if (pdev->dev.of_node)
+ emif = of_get_memory_device_details(pdev->dev.of_node, &pdev->dev);
+ else
+ emif = get_device_details(pdev);
+
if (!emif) {
pr_err("%s: error getting device data\n", __func__);
goto error;
@@ -1644,11 +1825,21 @@ static void __attribute__((unused)) freq_post_notify_handling(void)
spin_unlock_irqrestore(&emif_lock, irq_state);
}
+#if defined(CONFIG_OF)
+static const struct of_device_id emif_of_match[] = {
+ { .compatible = "ti,emif-4d" },
+ { .compatible = "ti,emif-4d5" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, emif_of_match);
+#endif
+
static struct platform_driver emif_driver = {
.remove = __exit_p(emif_remove),
.shutdown = emif_shutdown,
.driver = {
.name = "emif",
+ .of_match_table = of_match_ptr(emif_of_match),
},
};
diff --git a/drivers/memory/of_memory.c b/drivers/memory/of_memory.c
new file mode 100644
index 000000000000..60074351f17e
--- /dev/null
+++ b/drivers/memory/of_memory.c
@@ -0,0 +1,153 @@
+/*
+ * OpenFirmware helpers for memory drivers
+ *
+ * Copyright (C) 2012 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.
+ */
+
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/list.h>
+#include <linux/of.h>
+#include <linux/gfp.h>
+#include <memory/jedec_ddr.h>
+#include <linux/export.h>
+
+/**
+ * of_get_min_tck() - extract min timing values for ddr
+ * @np: pointer to ddr device tree node
+ * @device: device requesting for min timing values
+ *
+ * Populates the lpddr2_min_tck structure by extracting data
+ * from device tree node. Returns a pointer to the populated
+ * structure. If any error in populating the structure, returns
+ * default min timings provided by JEDEC.
+ */
+const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
+ struct device *dev)
+{
+ int ret = 0;
+ struct lpddr2_min_tck *min;
+
+ min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL);
+ if (!min)
+ goto default_min_tck;
+
+ ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab);
+ ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD);
+ ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR);
+ ret |= of_property_read_u32(np, "tRASmin-min-tck", &min->tRASmin);
+ ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD);
+ ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR);
+ ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP);
+ ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP);
+ ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE);
+ ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR);
+ ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW);
+
+ if (ret) {
+ devm_kfree(dev, min);
+ goto default_min_tck;
+ }
+
+ return min;
+
+default_min_tck:
+ dev_warn(dev, "%s: using default min-tck values\n", __func__);
+ return &lpddr2_jedec_min_tck;
+}
+EXPORT_SYMBOL(of_get_min_tck);
+
+static int of_do_get_timings(struct device_node *np,
+ struct lpddr2_timings *tim)
+{
+ int ret;
+
+ ret = of_property_read_u32(np, "max-freq", &tim->max_freq);
+ ret |= of_property_read_u32(np, "min-freq", &tim->min_freq);
+ ret |= of_property_read_u32(np, "tRPab", &tim->tRPab);
+ ret |= of_property_read_u32(np, "tRCD", &tim->tRCD);
+ ret |= of_property_read_u32(np, "tWR", &tim->tWR);
+ ret |= of_property_read_u32(np, "tRAS-min", &tim->tRAS_min);
+ ret |= of_property_read_u32(np, "tRRD", &tim->tRRD);
+ ret |= of_property_read_u32(np, "tWTR", &tim->tWTR);
+ ret |= of_property_read_u32(np, "tXP", &tim->tXP);
+ ret |= of_property_read_u32(np, "tRTP", &tim->tRTP);
+ ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR);
+ ret |= of_property_read_u32(np, "tDQSCK-max", &tim->tDQSCK_max);
+ ret |= of_property_read_u32(np, "tFAW", &tim->tFAW);
+ ret |= of_property_read_u32(np, "tZQCS", &tim->tZQCS);
+ ret |= of_property_read_u32(np, "tZQCL", &tim->tZQCL);
+ ret |= of_property_read_u32(np, "tZQinit", &tim->tZQinit);
+ ret |= of_property_read_u32(np, "tRAS-max-ns", &tim->tRAS_max_ns);
+ ret |= of_property_read_u32(np, "tDQSCK-max-derated",
+ &tim->tDQSCK_max_derated);
+
+ return ret;
+}
+
+/**
+ * of_get_ddr_timings() - extracts the ddr timings and updates no of
+ * frequencies available.
+ * @np_ddr: Pointer to ddr device tree node
+ * @dev: Device requesting for ddr timings
+ * @device_type: Type of ddr(LPDDR2 S2/S4)
+ * @nr_frequencies: No of frequencies available for ddr
+ * (updated by this function)
+ *
+ * Populates lpddr2_timings structure by extracting data from device
+ * tree node. Returns pointer to populated structure. If any error
+ * while populating, returns default timings provided by JEDEC.
+ */
+const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
+ struct device *dev, u32 device_type, u32 *nr_frequencies)
+{
+ struct lpddr2_timings *timings = NULL;
+ u32 arr_sz = 0, i = 0;
+ struct device_node *np_tim;
+ char *tim_compat;
+
+ switch (device_type) {
+ case DDR_TYPE_LPDDR2_S2:
+ case DDR_TYPE_LPDDR2_S4:
+ tim_compat = "jedec,lpddr2-timings";
+ break;
+ default:
+ dev_warn(dev, "%s: un-supported memory type\n", __func__);
+ }
+
+ for_each_child_of_node(np_ddr, np_tim)
+ if (of_device_is_compatible(np_tim, tim_compat))
+ arr_sz++;
+
+ if (arr_sz)
+ timings = devm_kzalloc(dev, sizeof(*timings) * arr_sz,
+ GFP_KERNEL);
+
+ if (!timings)
+ goto default_timings;
+
+ for_each_child_of_node(np_ddr, np_tim) {
+ if (of_device_is_compatible(np_tim, tim_compat)) {
+ if (of_do_get_timings(np_tim, &timings[i])) {
+ devm_kfree(dev, timings);
+ goto default_timings;
+ }
+ i++;
+ }
+ }
+
+ *nr_frequencies = arr_sz;
+
+ return timings;
+
+default_timings:
+ dev_warn(dev, "%s: using default timings\n", __func__);
+ *nr_frequencies = ARRAY_SIZE(lpddr2_jedec_timings);
+ return lpddr2_jedec_timings;
+}
+EXPORT_SYMBOL(of_get_ddr_timings);
diff --git a/drivers/memory/of_memory.h b/drivers/memory/of_memory.h
new file mode 100644
index 000000000000..ef2514f553d3
--- /dev/null
+++ b/drivers/memory/of_memory.h
@@ -0,0 +1,36 @@
+/*
+ * OpenFirmware helpers for memory drivers
+ *
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef __LINUX_MEMORY_OF_REG_H
+#define __LINUX_MEMORY_OF_REG_H
+
+#if defined(CONFIG_OF) && defined(CONFIG_DDR)
+extern const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
+ struct device *dev);
+extern const struct lpddr2_timings
+ *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
+ u32 device_type, u32 *nr_frequencies);
+#else
+static inline const struct lpddr2_min_tck
+ *of_get_min_tck(struct device_node *np, struct device *dev)
+{
+ return NULL;
+}
+
+static inline const struct lpddr2_timings
+ *of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
+ u32 device_type, u32 *nr_frequencies)
+{
+ return NULL;
+}
+#endif /* CONFIG_OF && CONFIG_DDR */
+
+#endif /* __LINUX_MEMORY_OF_REG_ */
diff --git a/drivers/memory/tegra20-mc.c b/drivers/memory/tegra20-mc.c
index 3ed49c1c2b91..e6764bb41cb9 100644
--- a/drivers/memory/tegra20-mc.c
+++ b/drivers/memory/tegra20-mc.c
@@ -57,7 +57,7 @@ static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs)
if (offs < 0x24)
val = readl(mc->regs[0] + offs);
- if (offs < 0x400)
+ else if (offs < 0x400)
val = readl(mc->regs[1] + offs - 0x3c);
return val;
@@ -65,14 +65,10 @@ static inline u32 mc_readl(struct tegra20_mc *mc, u32 offs)
static inline void mc_writel(struct tegra20_mc *mc, u32 val, u32 offs)
{
- if (offs < 0x24) {
+ if (offs < 0x24)
writel(val, mc->regs[0] + offs);
- return;
- }
- if (offs < 0x400) {
+ else if (offs < 0x400)
writel(val, mc->regs[1] + offs - 0x3c);
- return;
- }
}
static const char * const tegra20_mc_client[] = {
diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c
index e56ff04eb5cc..802b9ea431fa 100644
--- a/drivers/memory/tegra30-mc.c
+++ b/drivers/memory/tegra30-mc.c
@@ -95,11 +95,11 @@ static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
if (offs < 0x10)
val = readl(mc->regs[0] + offs);
- if (offs < 0x1f0)
+ else if (offs < 0x1f0)
val = readl(mc->regs[1] + offs - 0x3c);
- if (offs < 0x228)
+ else if (offs < 0x228)
val = readl(mc->regs[2] + offs - 0x200);
- if (offs < 0x400)
+ else if (offs < 0x400)
val = readl(mc->regs[3] + offs - 0x284);
return val;
@@ -107,22 +107,14 @@ static inline u32 mc_readl(struct tegra30_mc *mc, u32 offs)
static inline void mc_writel(struct tegra30_mc *mc, u32 val, u32 offs)
{
- if (offs < 0x10) {
+ if (offs < 0x10)
writel(val, mc->regs[0] + offs);
- return;
- }
- if (offs < 0x1f0) {
+ else if (offs < 0x1f0)
writel(val, mc->regs[1] + offs - 0x3c);
- return;
- }
- if (offs < 0x228) {
+ else if (offs < 0x228)
writel(val, mc->regs[2] + offs - 0x200);
- return;
- }
- if (offs < 0x400) {
+ else if (offs < 0x400)
writel(val, mc->regs[3] + offs - 0x284);
- return;
- }
}
static const char * const tegra30_mc_client[] = {
diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c
index 866f95960b4b..29d72a259c85 100644
--- a/drivers/mfd/ab8500-gpadc.c
+++ b/drivers/mfd/ab8500-gpadc.c
@@ -342,7 +342,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
/*
* Delay might be needed for ABB8500 cut 3.0, if not, remove
- * when hardware will be availible
+ * when hardware will be available
*/
msleep(1);
break;
diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c
index 82c9d6450286..352c58b5a90d 100644
--- a/drivers/mfd/da9052-i2c.c
+++ b/drivers/mfd/da9052-i2c.c
@@ -46,7 +46,7 @@ static int da9052_i2c_enable_multiwrite(struct da9052 *da9052)
return 0;
}
-static struct i2c_device_id da9052_i2c_id[] = {
+static const struct i2c_device_id da9052_i2c_id[] = {
{"da9052", DA9052},
{"da9053-aa", DA9053_AA},
{"da9053-ba", DA9053_BA},
@@ -104,7 +104,7 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client,
const struct of_device_id *deviceid;
deviceid = of_match_node(dialog_dt_ids, np);
- id = (const struct i2c_device_id *)deviceid->data;
+ id = deviceid->data;
}
#endif
diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c
index 3a8fa88567b1..ff61efc76ce2 100644
--- a/drivers/mfd/rc5t583.c
+++ b/drivers/mfd/rc5t583.c
@@ -281,7 +281,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
if (i2c->irq) {
ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base);
- /* Still continue with waring if irq init fails */
+ /* Still continue with warning, if irq init fails */
if (ret)
dev_warn(&i2c->dev, "IRQ init failed: %d\n", ret);
else
diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c
index 0f70dce61160..fbabc3cbe350 100644
--- a/drivers/mfd/rdc321x-southbridge.c
+++ b/drivers/mfd/rdc321x-southbridge.c
@@ -1,5 +1,5 @@
/*
- * RDC321x MFD southbrige driver
+ * RDC321x MFD southbridge driver
*
* Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
* Copyright (C) 2010 Bernhard Loos <bernhardloos@googlemail.com>
diff --git a/drivers/mfd/tps65911-comparator.c b/drivers/mfd/tps65911-comparator.c
index 5a62e6bf89ae..0b6e361432c4 100644
--- a/drivers/mfd/tps65911-comparator.c
+++ b/drivers/mfd/tps65911-comparator.c
@@ -136,7 +136,7 @@ static __devinit int tps65911_comparator_probe(struct platform_device *pdev)
ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold);
if (ret < 0) {
- dev_err(&pdev->dev, "cannot set COMP2 theshold\n");
+ dev_err(&pdev->dev, "cannot set COMP2 threshold\n");
return ret;
}
diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c
index 0aac4aff17a5..a050e56a9bbd 100644
--- a/drivers/mfd/wm8994-irq.c
+++ b/drivers/mfd/wm8994-irq.c
@@ -135,6 +135,7 @@ static struct regmap_irq_chip wm8994_irq_chip = {
.status_base = WM8994_INTERRUPT_STATUS_1,
.mask_base = WM8994_INTERRUPT_STATUS_1_MASK,
.ack_base = WM8994_INTERRUPT_STATUS_1,
+ .runtime_pm = true,
};
int wm8994_irq_init(struct wm8994 *wm8994)
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 98a442da892a..99c73352c430 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -105,7 +105,7 @@ config ATMEL_TCB_CLKSRC_BLOCK
config IBM_ASM
tristate "Device driver for IBM RSA service processor"
- depends on X86 && PCI && INPUT && EXPERIMENTAL
+ depends on X86 && PCI && INPUT
---help---
This option enables device driver support for in-band access to the
IBM RSA (Condor) service processor in eServer xSeries systems.
@@ -162,8 +162,8 @@ config SGI_IOC4
Otherwise say N.
config TIFM_CORE
- tristate "TI Flash Media interface support (EXPERIMENTAL)"
- depends on EXPERIMENTAL && PCI
+ tristate "TI Flash Media interface support"
+ depends on PCI
help
If you want support for Texas Instruments(R) Flash Media adapters
you should select this option and then also choose an appropriate
@@ -178,8 +178,8 @@ config TIFM_CORE
be called tifm_core.
config TIFM_7XX1
- tristate "TI Flash Media PCI74xx/PCI76xx host adapter support (EXPERIMENTAL)"
- depends on PCI && TIFM_CORE && EXPERIMENTAL
+ tristate "TI Flash Media PCI74xx/PCI76xx host adapter support"
+ depends on PCI && TIFM_CORE
default TIFM_CORE
help
This option enables support for Texas Instruments(R) PCI74xx and
@@ -192,7 +192,7 @@ config TIFM_7XX1
config ICS932S401
tristate "Integrated Circuits ICS932S401"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for the Integrated Circuits
ICS932S401 clock control chips.
@@ -398,7 +398,7 @@ config EP93XX_PWM
config DS1682
tristate "Dallas DS1682 Total Elapsed Time Recorder with Alarm"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get support for Dallas Semiconductor
DS1682 Total Elapsed Time Recorder.
diff --git a/drivers/misc/bmp085-i2c.c b/drivers/misc/bmp085-i2c.c
index 9943971c13e3..a4f33c995ea1 100644
--- a/drivers/misc/bmp085-i2c.c
+++ b/drivers/misc/bmp085-i2c.c
@@ -57,12 +57,6 @@ static int bmp085_i2c_remove(struct i2c_client *client)
return bmp085_remove(&client->dev);
}
-static const struct of_device_id bmp085_of_match[] = {
- { .compatible = "bosch,bmp085", },
- { },
-};
-MODULE_DEVICE_TABLE(of, bmp085_of_match);
-
static const struct i2c_device_id bmp085_id[] = {
{ BMP085_NAME, 0 },
{ "bmp180", 0 },
@@ -74,7 +68,6 @@ static struct i2c_driver bmp085_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = BMP085_NAME,
- .of_match_table = bmp085_of_match
},
.id_table = bmp085_id,
.probe = bmp085_i2c_probe,
diff --git a/drivers/misc/bmp085-spi.c b/drivers/misc/bmp085-spi.c
index 78aaff9b5231..5e982af99730 100644
--- a/drivers/misc/bmp085-spi.c
+++ b/drivers/misc/bmp085-spi.c
@@ -73,19 +73,8 @@ static struct spi_driver bmp085_spi_driver = {
.remove = __devexit_p(bmp085_spi_remove)
};
-static int __init bmp085_spi_init(void)
-{
- return spi_register_driver(&bmp085_spi_driver);
-}
-
-static void __exit bmp085_spi_exit(void)
-{
- spi_unregister_driver(&bmp085_spi_driver);
-}
+module_spi_driver(bmp085_spi_driver);
MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
MODULE_DESCRIPTION("BMP085 SPI bus driver");
MODULE_LICENSE("GPL");
-
-module_init(bmp085_spi_init);
-module_exit(bmp085_spi_exit);
diff --git a/drivers/misc/c2port/Kconfig b/drivers/misc/c2port/Kconfig
index 33ee834e1b83..0dd690e61d3c 100644
--- a/drivers/misc/c2port/Kconfig
+++ b/drivers/misc/c2port/Kconfig
@@ -3,8 +3,7 @@
#
menuconfig C2PORT
- tristate "Silicon Labs C2 port support (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ tristate "Silicon Labs C2 port support"
default n
help
This option enables support for Silicon Labs C2 port used to
@@ -22,7 +21,7 @@ menuconfig C2PORT
if C2PORT
config C2PORT_DURAMAR_2150
- tristate "C2 port support for Eurotech's Duramar 2150 (EXPERIMENTAL)"
+ tristate "C2 port support for Eurotech's Duramar 2150"
depends on X86
default n
help
diff --git a/drivers/misc/carma/carma-fpga-program.c b/drivers/misc/carma/carma-fpga-program.c
index a2d25e4857e3..eaddfe9db149 100644
--- a/drivers/misc/carma/carma-fpga-program.c
+++ b/drivers/misc/carma/carma-fpga-program.c
@@ -978,7 +978,6 @@ static int fpga_of_probe(struct platform_device *op)
dev_set_drvdata(priv->dev, priv);
dma_cap_zero(mask);
dma_cap_set(DMA_MEMCPY, mask);
- dma_cap_set(DMA_INTERRUPT, mask);
dma_cap_set(DMA_SLAVE, mask);
dma_cap_set(DMA_SG, mask);
diff --git a/drivers/misc/carma/carma-fpga.c b/drivers/misc/carma/carma-fpga.c
index 8c279da07410..0c43297ed9ac 100644
--- a/drivers/misc/carma/carma-fpga.c
+++ b/drivers/misc/carma/carma-fpga.c
@@ -666,7 +666,7 @@ static int data_submit_dma(struct fpga_device *priv, struct data_buf *buf)
src = SYS_FPGA_BLOCK;
tx = chan->device->device_prep_dma_memcpy(chan, dst, src,
REG_BLOCK_SIZE,
- DMA_PREP_INTERRUPT);
+ 0);
if (!tx) {
dev_err(priv->dev, "unable to prep SYS-FPGA DMA\n");
return -ENOMEM;
diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig
index 701edf658970..c9e695ea7c9a 100644
--- a/drivers/misc/eeprom/Kconfig
+++ b/drivers/misc/eeprom/Kconfig
@@ -50,7 +50,7 @@ config EEPROM_LEGACY
config EEPROM_MAX6875
tristate "Maxim MAX6874/5 power supply supervisor"
- depends on I2C && EXPERIMENTAL
+ depends on I2C
help
If you say yes here you get read-only support for the user EEPROM of
the Maxim MAX6874/5 EEPROM-programmable, quad power-supply
diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c
index 25003d6ceb56..4ed93dd54116 100644
--- a/drivers/misc/eeprom/at25.c
+++ b/drivers/misc/eeprom/at25.c
@@ -302,6 +302,61 @@ static ssize_t at25_mem_write(struct memory_accessor *mem, const char *buf,
/*-------------------------------------------------------------------------*/
+static int at25_np_to_chip(struct device *dev,
+ struct device_node *np,
+ struct spi_eeprom *chip)
+{
+ u32 val;
+
+ memset(chip, 0, sizeof(*chip));
+ strncpy(chip->name, np->name, sizeof(chip->name));
+
+ if (of_property_read_u32(np, "size", &val) == 0 ||
+ of_property_read_u32(np, "at25,byte-len", &val) == 0) {
+ chip->byte_len = val;
+ } else {
+ dev_err(dev, "Error: missing \"size\" property\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(np, "pagesize", &val) == 0 ||
+ of_property_read_u32(np, "at25,page-size", &val) == 0) {
+ chip->page_size = (u16)val;
+ } else {
+ dev_err(dev, "Error: missing \"pagesize\" property\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(np, "at25,addr-mode", &val) == 0) {
+ chip->flags = (u16)val;
+ } else {
+ if (of_property_read_u32(np, "address-width", &val)) {
+ dev_err(dev,
+ "Error: missing \"address-width\" property\n");
+ return -ENODEV;
+ }
+ switch (val) {
+ case 8:
+ chip->flags |= EE_ADDR1;
+ break;
+ case 16:
+ chip->flags |= EE_ADDR2;
+ break;
+ case 24:
+ chip->flags |= EE_ADDR3;
+ break;
+ default:
+ dev_err(dev,
+ "Error: bad \"address-width\" property: %u\n",
+ val);
+ return -ENODEV;
+ }
+ if (of_find_property(np, "read-only", NULL))
+ chip->flags |= EE_READONLY;
+ }
+ return 0;
+}
+
static int at25_probe(struct spi_device *spi)
{
struct at25_data *at25 = NULL;
@@ -314,33 +369,11 @@ static int at25_probe(struct spi_device *spi)
/* Chip description */
if (!spi->dev.platform_data) {
if (np) {
- u32 val;
-
- memset(&chip, 0, sizeof(chip));
- strncpy(chip.name, np->name, 10);
-
- err = of_property_read_u32(np, "at25,byte-len", &val);
- if (err) {
- dev_dbg(&spi->dev, "invalid chip dt description\n");
- goto fail;
- }
- chip.byte_len = val;
-
- err = of_property_read_u32(np, "at25,addr-mode", &val);
- if (err) {
- dev_dbg(&spi->dev, "invalid chip dt description\n");
- goto fail;
- }
- chip.flags = (u16)val;
-
- err = of_property_read_u32(np, "at25,page-size", &val);
- if (err) {
- dev_dbg(&spi->dev, "invalid chip dt description\n");
+ err = at25_np_to_chip(&spi->dev, np, &chip);
+ if (err)
goto fail;
- }
- chip.page_size = (u16)val;
} else {
- dev_dbg(&spi->dev, "no chip description\n");
+ dev_err(&spi->dev, "Error: no chip description\n");
err = -ENODEV;
goto fail;
}
diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c
index 6df0da4085e3..12ccdf94e4fa 100644
--- a/drivers/misc/hpilo.c
+++ b/drivers/misc/hpilo.c
@@ -736,7 +736,14 @@ static void ilo_remove(struct pci_dev *pdev)
free_irq(pdev->irq, ilo_hw);
ilo_unmap_device(pdev, ilo_hw);
pci_release_regions(pdev);
- pci_disable_device(pdev);
+ /*
+ * pci_disable_device(pdev) used to be here. But this PCI device has
+ * two functions with interrupt lines connected to a single pin. The
+ * other one is a USB host controller. So when we disable the PIN here
+ * e.g. by rmmod hpilo, the controller stops working. It is because
+ * the interrupt link is disabled in ACPI since it is not refcounted
+ * yet. See acpi_pci_link_free_irq called from acpi_pci_irq_disable.
+ */
kfree(ilo_hw);
ilo_hwdev[(minor / max_ccb)] = 0;
}
@@ -826,7 +833,7 @@ unmap:
free_regions:
pci_release_regions(pdev);
disable:
- pci_disable_device(pdev);
+/* pci_disable_device(pdev); see comment in ilo_remove */
free:
kfree(ilo_hw);
out:
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.c b/drivers/misc/lis3lv02d/lis3lv02d.c
index a981e2a42f92..4a87e5c0a320 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d.c
@@ -39,6 +39,7 @@
#include <linux/miscdevice.h>
#include <linux/pm_runtime.h>
#include <linux/atomic.h>
+#include <linux/of_device.h>
#include "lis3lv02d.h"
#define DRIVER_NAME "lis3lv02d"
@@ -80,6 +81,15 @@
#define LIS3_SENSITIVITY_12B ((LIS3_ACCURACY * 1000) / 1024)
#define LIS3_SENSITIVITY_8B (18 * LIS3_ACCURACY)
+/*
+ * LIS331DLH spec says 1LSBs corresponds 4G/4096 -> 1LSB is 1000/1024 mG.
+ * Below macros defines sensitivity values for +/-2G. Dataout bits for
+ * +/-2G range is 12 bits so 4 bits adjustment must be done to get 12bit
+ * data from 16bit value. Currently this driver supports only 2G range.
+ */
+#define LIS3DLH_SENSITIVITY_2G ((LIS3_ACCURACY * 1000) / 1024)
+#define SHIFT_ADJ_2G 4
+
#define LIS3_DEFAULT_FUZZ_12B 3
#define LIS3_DEFAULT_FLAT_12B 3
#define LIS3_DEFAULT_FUZZ_8B 1
@@ -135,6 +145,19 @@ static s16 lis3lv02d_read_12(struct lis3lv02d *lis3, int reg)
return (s16)((hi << 8) | lo);
}
+/* 12bits for 2G range, 13 bits for 4G range and 14 bits for 8G range */
+static s16 lis331dlh_read_data(struct lis3lv02d *lis3, int reg)
+{
+ u8 lo, hi;
+ int v;
+
+ lis3->read(lis3, reg - 1, &lo);
+ lis3->read(lis3, reg, &hi);
+ v = (int) ((hi << 8) | lo);
+
+ return (s16) v >> lis3->shift_adj;
+}
+
/**
* lis3lv02d_get_axis - For the given axis, give the value converted
* @axis: 1,2,3 - can also be negative
@@ -195,6 +218,7 @@ static void lis3lv02d_get_xyz(struct lis3lv02d *lis3, int *x, int *y, int *z)
static int lis3_12_rates[4] = {40, 160, 640, 2560};
static int lis3_8_rates[2] = {100, 400};
static int lis3_3dc_rates[16] = {0, 1, 10, 25, 50, 100, 200, 400, 1600, 5000};
+static int lis3_3dlh_rates[4] = {50, 100, 400, 1000};
/* ODR is Output Data Rate */
static int lis3lv02d_get_odr(struct lis3lv02d *lis3)
@@ -267,7 +291,7 @@ static int lis3lv02d_selftest(struct lis3lv02d *lis3, s16 results[3])
(LIS3_IRQ1_DATA_READY | LIS3_IRQ2_DATA_READY));
}
- if (lis3->whoami == WAI_3DC) {
+ if ((lis3->whoami == WAI_3DC) || (lis3->whoami == WAI_3DLH)) {
ctlreg = CTRL_REG4;
selftest = CTRL4_ST0;
} else {
@@ -398,9 +422,17 @@ int lis3lv02d_poweron(struct lis3lv02d *lis3)
lis3->read(lis3, CTRL_REG2, &reg);
if (lis3->whoami == WAI_12B)
reg |= CTRL2_BDU | CTRL2_BOOT;
+ else if (lis3->whoami == WAI_3DLH)
+ reg |= CTRL2_BOOT_3DLH;
else
reg |= CTRL2_BOOT_8B;
lis3->write(lis3, CTRL_REG2, reg);
+
+ if (lis3->whoami == WAI_3DLH) {
+ lis3->read(lis3, CTRL_REG4, &reg);
+ reg |= CTRL4_BDU;
+ lis3->write(lis3, CTRL_REG4, reg);
+ }
}
err = lis3lv02d_get_pwron_wait(lis3);
@@ -912,6 +944,154 @@ static void lis3lv02d_8b_configure(struct lis3lv02d *lis3,
}
}
+#ifdef CONFIG_OF
+int lis3lv02d_init_dt(struct lis3lv02d *lis3)
+{
+ struct lis3lv02d_platform_data *pdata;
+ struct device_node *np = lis3->of_node;
+ u32 val;
+
+ if (!lis3->of_node)
+ return 0;
+
+ pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (of_get_property(np, "st,click-single-x", NULL))
+ pdata->click_flags |= LIS3_CLICK_SINGLE_X;
+ if (of_get_property(np, "st,click-double-x", NULL))
+ pdata->click_flags |= LIS3_CLICK_DOUBLE_X;
+
+ if (of_get_property(np, "st,click-single-y", NULL))
+ pdata->click_flags |= LIS3_CLICK_SINGLE_Y;
+ if (of_get_property(np, "st,click-double-y", NULL))
+ pdata->click_flags |= LIS3_CLICK_DOUBLE_Y;
+
+ if (of_get_property(np, "st,click-single-z", NULL))
+ pdata->click_flags |= LIS3_CLICK_SINGLE_Z;
+ if (of_get_property(np, "st,click-double-z", NULL))
+ pdata->click_flags |= LIS3_CLICK_DOUBLE_Z;
+
+ if (!of_property_read_u32(np, "st,click-threshold-x", &val))
+ pdata->click_thresh_x = val;
+ if (!of_property_read_u32(np, "st,click-threshold-y", &val))
+ pdata->click_thresh_y = val;
+ if (!of_property_read_u32(np, "st,click-threshold-z", &val))
+ pdata->click_thresh_z = val;
+
+ if (!of_property_read_u32(np, "st,click-time-limit", &val))
+ pdata->click_time_limit = val;
+ if (!of_property_read_u32(np, "st,click-latency", &val))
+ pdata->click_latency = val;
+ if (!of_property_read_u32(np, "st,click-window", &val))
+ pdata->click_window = val;
+
+ if (of_get_property(np, "st,irq1-disable", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_DISABLE;
+ if (of_get_property(np, "st,irq1-ff-wu-1", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_FF_WU_1;
+ if (of_get_property(np, "st,irq1-ff-wu-2", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_FF_WU_2;
+ if (of_get_property(np, "st,irq1-data-ready", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_DATA_READY;
+ if (of_get_property(np, "st,irq1-click", NULL))
+ pdata->irq_cfg |= LIS3_IRQ1_CLICK;
+
+ if (of_get_property(np, "st,irq2-disable", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_DISABLE;
+ if (of_get_property(np, "st,irq2-ff-wu-1", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_FF_WU_1;
+ if (of_get_property(np, "st,irq2-ff-wu-2", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_FF_WU_2;
+ if (of_get_property(np, "st,irq2-data-ready", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_DATA_READY;
+ if (of_get_property(np, "st,irq2-click", NULL))
+ pdata->irq_cfg |= LIS3_IRQ2_CLICK;
+
+ if (of_get_property(np, "st,irq-open-drain", NULL))
+ pdata->irq_cfg |= LIS3_IRQ_OPEN_DRAIN;
+ if (of_get_property(np, "st,irq-active-low", NULL))
+ pdata->irq_cfg |= LIS3_IRQ_ACTIVE_LOW;
+
+ if (!of_property_read_u32(np, "st,wu-duration-1", &val))
+ pdata->duration1 = val;
+ if (!of_property_read_u32(np, "st,wu-duration-2", &val))
+ pdata->duration2 = val;
+
+ if (of_get_property(np, "st,wakeup-x-lo", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_X_LO;
+ if (of_get_property(np, "st,wakeup-x-hi", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_X_HI;
+ if (of_get_property(np, "st,wakeup-y-lo", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Y_LO;
+ if (of_get_property(np, "st,wakeup-y-hi", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Y_HI;
+ if (of_get_property(np, "st,wakeup-z-lo", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Z_LO;
+ if (of_get_property(np, "st,wakeup-z-hi", NULL))
+ pdata->wakeup_flags |= LIS3_WAKEUP_Z_HI;
+
+ if (!of_property_read_u32(np, "st,highpass-cutoff-hz", &val)) {
+ switch (val) {
+ case 1:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_1HZ;
+ break;
+ case 2:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_2HZ;
+ break;
+ case 4:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_4HZ;
+ break;
+ case 8:
+ pdata->hipass_ctrl = LIS3_HIPASS_CUTFF_8HZ;
+ break;
+ }
+ }
+
+ if (of_get_property(np, "st,hipass1-disable", NULL))
+ pdata->hipass_ctrl |= LIS3_HIPASS1_DISABLE;
+ if (of_get_property(np, "st,hipass2-disable", NULL))
+ pdata->hipass_ctrl |= LIS3_HIPASS2_DISABLE;
+
+ if (of_get_property(np, "st,axis-x", &val))
+ pdata->axis_x = val;
+ if (of_get_property(np, "st,axis-y", &val))
+ pdata->axis_y = val;
+ if (of_get_property(np, "st,axis-z", &val))
+ pdata->axis_z = val;
+
+ if (of_get_property(np, "st,default-rate", NULL))
+ pdata->default_rate = val;
+
+ if (of_get_property(np, "st,min-limit-x", &val))
+ pdata->st_min_limits[0] = val;
+ if (of_get_property(np, "st,min-limit-y", &val))
+ pdata->st_min_limits[1] = val;
+ if (of_get_property(np, "st,min-limit-z", &val))
+ pdata->st_min_limits[2] = val;
+
+ if (of_get_property(np, "st,max-limit-x", &val))
+ pdata->st_max_limits[0] = val;
+ if (of_get_property(np, "st,max-limit-y", &val))
+ pdata->st_max_limits[1] = val;
+ if (of_get_property(np, "st,max-limit-z", &val))
+ pdata->st_max_limits[2] = val;
+
+
+ lis3->pdata = pdata;
+
+ return 0;
+}
+
+#else
+int lis3lv02d_init_dt(struct lis3lv02d *lis3)
+{
+ return 0;
+}
+#endif
+EXPORT_SYMBOL_GPL(lis3lv02d_init_dt);
+
/*
* Initialise the accelerometer and the various subsystems.
* Should be rather independent of the bus system.
@@ -956,6 +1136,16 @@ int lis3lv02d_init_device(struct lis3lv02d *lis3)
lis3->odr_mask = CTRL1_ODR0|CTRL1_ODR1|CTRL1_ODR2|CTRL1_ODR3;
lis3->scale = LIS3_SENSITIVITY_8B;
break;
+ case WAI_3DLH:
+ pr_info("16 bits lis331dlh sensor found\n");
+ lis3->read_data = lis331dlh_read_data;
+ lis3->mdps_max_val = 2048; /* 12 bits for 2G */
+ lis3->shift_adj = SHIFT_ADJ_2G;
+ lis3->pwron_delay = LIS3_PWRON_DELAY_WAI_8B;
+ lis3->odrs = lis3_3dlh_rates;
+ lis3->odr_mask = CTRL1_DR0 | CTRL1_DR1;
+ lis3->scale = LIS3DLH_SENSITIVITY_2G;
+ break;
default:
pr_err("unknown sensor type 0x%X\n", lis3->whoami);
return -EINVAL;
diff --git a/drivers/misc/lis3lv02d/lis3lv02d.h b/drivers/misc/lis3lv02d/lis3lv02d.h
index 2b1482ad3f16..c439c827eea8 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d.h
+++ b/drivers/misc/lis3lv02d/lis3lv02d.h
@@ -26,12 +26,12 @@
/*
* This driver tries to support the "digital" accelerometer chips from
* STMicroelectronics such as LIS3LV02DL, LIS302DL, LIS3L02DQ, LIS331DL,
- * LIS35DE, or LIS202DL. They are very similar in terms of programming, with
- * almost the same registers. In addition to differing on physical properties,
- * they differ on the number of axes (2/3), precision (8/12 bits), and special
- * features (freefall detection, click...). Unfortunately, not all the
- * differences can be probed via a register.
- * They can be connected either via I²C or SPI.
+ * LIS331DLH, LIS35DE, or LIS202DL. They are very similar in terms of
+ * programming, with almost the same registers. In addition to differing
+ * on physical properties, they differ on the number of axes (2/3),
+ * precision (8/12 bits), and special features (freefall detection,
+ * click...). Unfortunately, not all the differences can be probed via
+ * a register. They can be connected either via I²C or SPI.
*/
#include <linux/lis3lv02d.h>
@@ -96,12 +96,22 @@ enum lis3lv02d_reg {
};
enum lis3_who_am_i {
+ WAI_3DLH = 0x32, /* 16 bits: LIS331DLH */
WAI_3DC = 0x33, /* 8 bits: LIS3DC, HP3DC */
WAI_12B = 0x3A, /* 12 bits: LIS3LV02D[LQ]... */
WAI_8B = 0x3B, /* 8 bits: LIS[23]02D[LQ]... */
WAI_6B = 0x52, /* 6 bits: LIS331DLF - not supported */
};
+enum lis3_type {
+ LIS3LV02D,
+ LIS3DC,
+ HP3DC,
+ LIS2302D,
+ LIS331DLF,
+ LIS331DLH,
+};
+
enum lis3lv02d_ctrl1_12b {
CTRL1_Xen = 0x01,
CTRL1_Yen = 0x02,
@@ -129,6 +139,27 @@ enum lis3lv02d_ctrl1_3dc {
CTRL1_ODR3 = 0x80,
};
+enum lis331dlh_ctrl1 {
+ CTRL1_DR0 = 0x08,
+ CTRL1_DR1 = 0x10,
+ CTRL1_PM0 = 0x20,
+ CTRL1_PM1 = 0x40,
+ CTRL1_PM2 = 0x80,
+};
+
+enum lis331dlh_ctrl2 {
+ CTRL2_HPEN1 = 0x04,
+ CTRL2_HPEN2 = 0x08,
+ CTRL2_FDS_3DLH = 0x10,
+ CTRL2_BOOT_3DLH = 0x80,
+};
+
+enum lis331dlh_ctrl4 {
+ CTRL4_STSIGN = 0x08,
+ CTRL4_BLE = 0x40,
+ CTRL4_BDU = 0x80,
+};
+
enum lis3lv02d_ctrl2 {
CTRL2_DAS = 0x01,
CTRL2_SIM = 0x02,
@@ -279,9 +310,14 @@ struct lis3lv02d {
int data_ready_count[2];
atomic_t wake_thread;
unsigned char irq_cfg;
+ unsigned int shift_adj;
struct lis3lv02d_platform_data *pdata; /* for passing board config */
struct mutex mutex; /* Serialize poll and selftest */
+
+#ifdef CONFIG_OF
+ struct device_node *of_node;
+#endif
};
int lis3lv02d_init_device(struct lis3lv02d *lis3);
@@ -290,5 +326,6 @@ void lis3lv02d_joystick_disable(struct lis3lv02d *lis3);
void lis3lv02d_poweroff(struct lis3lv02d *lis3);
int lis3lv02d_poweron(struct lis3lv02d *lis3);
int lis3lv02d_remove_fs(struct lis3lv02d *lis3);
+int lis3lv02d_init_dt(struct lis3lv02d *lis3);
extern struct lis3lv02d lis3_dev;
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
index e8c0019da97a..60ec8689d6e3 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c
@@ -31,6 +31,10 @@
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+
#include "lis3lv02d.h"
#define DRV_NAME "lis3lv02d_i2c"
@@ -90,7 +94,11 @@ static int lis3_i2c_init(struct lis3lv02d *lis3)
if (ret < 0)
return ret;
- reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
+ if (lis3->whoami == WAI_3DLH)
+ reg |= CTRL1_PM0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
+ else
+ reg |= CTRL1_PD0 | CTRL1_Xen | CTRL1_Yen | CTRL1_Zen;
+
return lis3->write(lis3, CTRL_REG1, reg);
}
@@ -98,12 +106,30 @@ static int lis3_i2c_init(struct lis3lv02d *lis3)
static union axis_conversion lis3lv02d_axis_map =
{ .as_array = { LIS3_DEV_X, LIS3_DEV_Y, LIS3_DEV_Z } };
+#ifdef CONFIG_OF
+static struct of_device_id lis3lv02d_i2c_dt_ids[] = {
+ { .compatible = "st,lis3lv02d" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lis3lv02d_i2c_dt_ids);
+#endif
+
static int __devinit lis3lv02d_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int ret = 0;
struct lis3lv02d_platform_data *pdata = client->dev.platform_data;
+#ifdef CONFIG_OF
+ if (of_match_device(lis3lv02d_i2c_dt_ids, &client->dev)) {
+ lis3_dev.of_node = client->dev.of_node;
+ ret = lis3lv02d_init_dt(&lis3_dev);
+ if (ret)
+ return ret;
+ pdata = lis3_dev.pdata;
+ }
+#endif
+
if (pdata) {
if ((pdata->driver_features & LIS3_USE_BLOCK_READ) &&
(i2c_check_functionality(client->adapter,
@@ -231,7 +257,8 @@ static int lis3_i2c_runtime_resume(struct device *dev)
#endif /* CONFIG_PM_RUNTIME */
static const struct i2c_device_id lis3lv02d_id[] = {
- {"lis3lv02d", 0 },
+ {"lis3lv02d", LIS3LV02D},
+ {"lis331dlh", LIS331DLH},
{}
};
@@ -250,6 +277,7 @@ static struct i2c_driver lis3lv02d_i2c_driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lis3_pm_ops,
+ .of_match_table = of_match_ptr(lis3lv02d_i2c_dt_ids),
},
.probe = lis3lv02d_i2c_probe,
.remove = __devexit_p(lis3lv02d_i2c_remove),
diff --git a/drivers/misc/lis3lv02d/lis3lv02d_spi.c b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
index 80880e984b4f..ccb6475fa059 100644
--- a/drivers/misc/lis3lv02d/lis3lv02d_spi.c
+++ b/drivers/misc/lis3lv02d/lis3lv02d_spi.c
@@ -17,6 +17,9 @@
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
#include <linux/pm.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
#include "lis3lv02d.h"
@@ -58,6 +61,14 @@ static int lis3_spi_init(struct lis3lv02d *lis3)
static union axis_conversion lis3lv02d_axis_normal =
{ .as_array = { 1, 2, 3 } };
+#ifdef CONFIG_OF
+static struct of_device_id lis302dl_spi_dt_ids[] = {
+ { .compatible = "st,lis302dl-spi" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, lis302dl_spi_dt_ids);
+#endif
+
static int __devinit lis302dl_spi_probe(struct spi_device *spi)
{
int ret;
@@ -75,6 +86,15 @@ static int __devinit lis302dl_spi_probe(struct spi_device *spi)
lis3_dev.irq = spi->irq;
lis3_dev.ac = lis3lv02d_axis_normal;
lis3_dev.pdata = spi->dev.platform_data;
+
+#ifdef CONFIG_OF
+ if (of_match_device(lis302dl_spi_dt_ids, &spi->dev)) {
+ lis3_dev.of_node = spi->dev.of_node;
+ ret = lis3lv02d_init_dt(&lis3_dev);
+ if (ret)
+ return ret;
+ }
+#endif
spi_set_drvdata(spi, &lis3_dev);
return lis3lv02d_init_device(&lis3_dev);
@@ -121,6 +141,7 @@ static struct spi_driver lis302dl_spi_driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
.pm = &lis3lv02d_spi_pm,
+ .of_match_table = of_match_ptr(lis302dl_spi_dt_ids),
},
.probe = lis302dl_spi_probe,
.remove = __devexit_p(lis302dl_spi_remove),
diff --git a/drivers/misc/mei/Kconfig b/drivers/misc/mei/Kconfig
index 47d78a72db2e..5a79ccde2fdf 100644
--- a/drivers/misc/mei/Kconfig
+++ b/drivers/misc/mei/Kconfig
@@ -1,6 +1,6 @@
config INTEL_MEI
tristate "Intel Management Engine Interface (Intel MEI)"
- depends on X86 && PCI && EXPERIMENTAL && WATCHDOG_CORE
+ depends on X86 && PCI && WATCHDOG_CORE
help
The Intel Management Engine (Intel ME) provides Manageability,
Security and Media services for system containing Intel chipsets.
diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h
index 24c4c962819e..9700532f02f6 100644
--- a/drivers/misc/mei/hw.h
+++ b/drivers/misc/mei/hw.h
@@ -40,47 +40,48 @@
/*
* MEI device IDs
*/
-#define MEI_DEV_ID_82946GZ 0x2974 /* 82946GZ/GL */
-#define MEI_DEV_ID_82G35 0x2984 /* 82G35 Express */
-#define MEI_DEV_ID_82Q965 0x2994 /* 82Q963/Q965 */
-#define MEI_DEV_ID_82G965 0x29A4 /* 82P965/G965 */
-
-#define MEI_DEV_ID_82GM965 0x2A04 /* Mobile PM965/GM965 */
-#define MEI_DEV_ID_82GME965 0x2A14 /* Mobile GME965/GLE960 */
-
-#define MEI_DEV_ID_ICH9_82Q35 0x29B4 /* 82Q35 Express */
-#define MEI_DEV_ID_ICH9_82G33 0x29C4 /* 82G33/G31/P35/P31 Express */
-#define MEI_DEV_ID_ICH9_82Q33 0x29D4 /* 82Q33 Express */
-#define MEI_DEV_ID_ICH9_82X38 0x29E4 /* 82X38/X48 Express */
-#define MEI_DEV_ID_ICH9_3200 0x29F4 /* 3200/3210 Server */
-
-#define MEI_DEV_ID_ICH9_6 0x28B4 /* Bearlake */
-#define MEI_DEV_ID_ICH9_7 0x28C4 /* Bearlake */
-#define MEI_DEV_ID_ICH9_8 0x28D4 /* Bearlake */
-#define MEI_DEV_ID_ICH9_9 0x28E4 /* Bearlake */
-#define MEI_DEV_ID_ICH9_10 0x28F4 /* Bearlake */
-
-#define MEI_DEV_ID_ICH9M_1 0x2A44 /* Cantiga */
-#define MEI_DEV_ID_ICH9M_2 0x2A54 /* Cantiga */
-#define MEI_DEV_ID_ICH9M_3 0x2A64 /* Cantiga */
-#define MEI_DEV_ID_ICH9M_4 0x2A74 /* Cantiga */
-
-#define MEI_DEV_ID_ICH10_1 0x2E04 /* Eaglelake */
-#define MEI_DEV_ID_ICH10_2 0x2E14 /* Eaglelake */
-#define MEI_DEV_ID_ICH10_3 0x2E24 /* Eaglelake */
-#define MEI_DEV_ID_ICH10_4 0x2E34 /* Eaglelake */
-
-#define MEI_DEV_ID_IBXPK_1 0x3B64 /* Calpella */
-#define MEI_DEV_ID_IBXPK_2 0x3B65 /* Calpella */
-
-#define MEI_DEV_ID_CPT_1 0x1C3A /* Cougerpoint */
-#define MEI_DEV_ID_PBG_1 0x1D3A /* PBG */
-
-#define MEI_DEV_ID_PPT_1 0x1E3A /* Pantherpoint PPT */
-#define MEI_DEV_ID_PPT_2 0x1CBA /* Pantherpoint PPT */
-#define MEI_DEV_ID_PPT_3 0x1DBA /* Pantherpoint PPT */
-
-
+#define MEI_DEV_ID_82946GZ 0x2974 /* 82946GZ/GL */
+#define MEI_DEV_ID_82G35 0x2984 /* 82G35 Express */
+#define MEI_DEV_ID_82Q965 0x2994 /* 82Q963/Q965 */
+#define MEI_DEV_ID_82G965 0x29A4 /* 82P965/G965 */
+
+#define MEI_DEV_ID_82GM965 0x2A04 /* Mobile PM965/GM965 */
+#define MEI_DEV_ID_82GME965 0x2A14 /* Mobile GME965/GLE960 */
+
+#define MEI_DEV_ID_ICH9_82Q35 0x29B4 /* 82Q35 Express */
+#define MEI_DEV_ID_ICH9_82G33 0x29C4 /* 82G33/G31/P35/P31 Express */
+#define MEI_DEV_ID_ICH9_82Q33 0x29D4 /* 82Q33 Express */
+#define MEI_DEV_ID_ICH9_82X38 0x29E4 /* 82X38/X48 Express */
+#define MEI_DEV_ID_ICH9_3200 0x29F4 /* 3200/3210 Server */
+
+#define MEI_DEV_ID_ICH9_6 0x28B4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_7 0x28C4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_8 0x28D4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_9 0x28E4 /* Bearlake */
+#define MEI_DEV_ID_ICH9_10 0x28F4 /* Bearlake */
+
+#define MEI_DEV_ID_ICH9M_1 0x2A44 /* Cantiga */
+#define MEI_DEV_ID_ICH9M_2 0x2A54 /* Cantiga */
+#define MEI_DEV_ID_ICH9M_3 0x2A64 /* Cantiga */
+#define MEI_DEV_ID_ICH9M_4 0x2A74 /* Cantiga */
+
+#define MEI_DEV_ID_ICH10_1 0x2E04 /* Eaglelake */
+#define MEI_DEV_ID_ICH10_2 0x2E14 /* Eaglelake */
+#define MEI_DEV_ID_ICH10_3 0x2E24 /* Eaglelake */
+#define MEI_DEV_ID_ICH10_4 0x2E34 /* Eaglelake */
+
+#define MEI_DEV_ID_IBXPK_1 0x3B64 /* Calpella */
+#define MEI_DEV_ID_IBXPK_2 0x3B65 /* Calpella */
+
+#define MEI_DEV_ID_CPT_1 0x1C3A /* Couger Point */
+#define MEI_DEV_ID_PBG_1 0x1D3A /* C600/X79 Patsburg */
+
+#define MEI_DEV_ID_PPT_1 0x1E3A /* Panther Point */
+#define MEI_DEV_ID_PPT_2 0x1CBA /* Panther Point */
+#define MEI_DEV_ID_PPT_3 0x1DBA /* Panther Point */
+
+#define MEI_DEV_ID_LPT 0x8C3A /* Lynx Point */
+#define MEI_DEV_ID_LPT_LP 0x9C3A /* Lynx Point LP */
/*
* MEI HW Section
*/
diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c
index e77f86e69fb5..98f1430e3e14 100644
--- a/drivers/misc/mei/init.c
+++ b/drivers/misc/mei/init.c
@@ -24,6 +24,25 @@
#include "interface.h"
#include <linux/mei.h>
+const char *mei_dev_state_str(int state)
+{
+#define MEI_DEV_STATE(state) case MEI_DEV_##state: return #state
+ switch (state) {
+ MEI_DEV_STATE(INITIALIZING);
+ MEI_DEV_STATE(INIT_CLIENTS);
+ MEI_DEV_STATE(ENABLED);
+ MEI_DEV_STATE(RESETING);
+ MEI_DEV_STATE(DISABLED);
+ MEI_DEV_STATE(RECOVERING_FROM_RESET);
+ MEI_DEV_STATE(POWER_DOWN);
+ MEI_DEV_STATE(POWER_UP);
+ default:
+ return "unkown";
+ }
+#undef MEI_DEV_STATE
+}
+
+
const uuid_le mei_amthi_guid = UUID_LE(0x12f80028, 0xb4b7, 0x4b2d, 0xac,
0xa8, 0x46, 0xe0, 0xff, 0x65,
0x81, 0x4c);
@@ -123,7 +142,7 @@ struct mei_device *mei_device_init(struct pci_dev *pdev)
mutex_init(&dev->device_lock);
init_waitqueue_head(&dev->wait_recvd_msg);
init_waitqueue_head(&dev->wait_stop_wd);
- dev->mei_state = MEI_INITIALIZING;
+ dev->dev_state = MEI_DEV_INITIALIZING;
dev->iamthif_state = MEI_IAMTHIF_IDLE;
dev->wd_interface_reg = false;
@@ -182,7 +201,7 @@ int mei_hw_init(struct mei_device *dev)
}
if (err <= 0 && !dev->recvd_msg) {
- dev->mei_state = MEI_DISABLED;
+ dev->dev_state = MEI_DEV_DISABLED;
dev_dbg(&dev->pdev->dev,
"wait_event_interruptible_timeout failed"
"on wait for ME to turn on ME_RDY.\n");
@@ -192,7 +211,7 @@ int mei_hw_init(struct mei_device *dev)
if (!(((dev->host_hw_state & H_RDY) == H_RDY) &&
((dev->me_hw_state & ME_RDY_HRA) == ME_RDY_HRA))) {
- dev->mei_state = MEI_DISABLED;
+ dev->dev_state = MEI_DEV_DISABLED;
dev_dbg(&dev->pdev->dev,
"host_hw_state = 0x%08x, me_hw_state = 0x%08x.\n",
dev->host_hw_state, dev->me_hw_state);
@@ -258,15 +277,15 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
struct mei_cl_cb *cb_next = NULL;
bool unexpected;
- if (dev->mei_state == MEI_RECOVERING_FROM_RESET) {
+ if (dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) {
dev->need_reset = true;
return;
}
- unexpected = (dev->mei_state != MEI_INITIALIZING &&
- dev->mei_state != MEI_DISABLED &&
- dev->mei_state != MEI_POWER_DOWN &&
- dev->mei_state != MEI_POWER_UP);
+ unexpected = (dev->dev_state != MEI_DEV_INITIALIZING &&
+ dev->dev_state != MEI_DEV_DISABLED &&
+ dev->dev_state != MEI_DEV_POWER_DOWN &&
+ dev->dev_state != MEI_DEV_POWER_UP);
dev->host_hw_state = mei_hcsr_read(dev);
@@ -285,10 +304,10 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
dev->need_reset = false;
- if (dev->mei_state != MEI_INITIALIZING) {
- if (dev->mei_state != MEI_DISABLED &&
- dev->mei_state != MEI_POWER_DOWN)
- dev->mei_state = MEI_RESETING;
+ if (dev->dev_state != MEI_DEV_INITIALIZING) {
+ if (dev->dev_state != MEI_DEV_DISABLED &&
+ dev->dev_state != MEI_DEV_POWER_DOWN)
+ dev->dev_state = MEI_DEV_RESETING;
list_for_each_entry_safe(cl_pos,
cl_next, &dev->file_list, link) {
@@ -311,7 +330,6 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
dev->me_clients_num = 0;
dev->rd_msg_hdr = 0;
- dev->stop = false;
dev->wd_pending = false;
/* update the state of the registers after reset */
@@ -322,7 +340,8 @@ void mei_reset(struct mei_device *dev, int interrupts_enabled)
dev->host_hw_state, dev->me_hw_state);
if (unexpected)
- dev_warn(&dev->pdev->dev, "unexpected reset.\n");
+ dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
+ mei_dev_state_str(dev->dev_state));
/* Wake up all readings so they can be interrupted */
list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
@@ -371,7 +390,7 @@ void mei_host_start_message(struct mei_device *dev)
if (mei_write_message(dev, mei_hdr, (unsigned char *)host_start_req,
mei_hdr->length)) {
dev_dbg(&dev->pdev->dev, "write send version message to FW fail.\n");
- dev->mei_state = MEI_RESETING;
+ dev->dev_state = MEI_DEV_RESETING;
mei_reset(dev, 1);
}
dev->init_clients_state = MEI_START_MESSAGE;
@@ -403,7 +422,7 @@ void mei_host_enum_clients_message(struct mei_device *dev)
host_enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
if (mei_write_message(dev, mei_hdr, (unsigned char *)host_enum_req,
mei_hdr->length)) {
- dev->mei_state = MEI_RESETING;
+ dev->dev_state = MEI_DEV_RESETING;
dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
mei_reset(dev, 1);
}
@@ -444,7 +463,7 @@ void mei_allocate_me_clients_storage(struct mei_device *dev)
sizeof(struct mei_me_client), GFP_KERNEL);
if (!clients) {
dev_dbg(&dev->pdev->dev, "memory allocation for ME clients failed.\n");
- dev->mei_state = MEI_RESETING;
+ dev->dev_state = MEI_DEV_RESETING;
mei_reset(dev, 1);
return ;
}
@@ -490,7 +509,7 @@ int mei_host_client_properties(struct mei_device *dev)
if (mei_write_message(dev, mei_header,
(unsigned char *)host_cli_req,
mei_header->length)) {
- dev->mei_state = MEI_RESETING;
+ dev->dev_state = MEI_DEV_RESETING;
dev_dbg(&dev->pdev->dev, "write send enumeration request message to FW fail.\n");
mei_reset(dev, 1);
return -EIO;
@@ -522,12 +541,12 @@ void mei_cl_init(struct mei_cl *priv, struct mei_device *dev)
priv->dev = dev;
}
-int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid)
+int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *cuuid)
{
- int i, res = -1;
+ int i, res = -ENOENT;
for (i = 0; i < dev->me_clients_num; ++i)
- if (uuid_le_cmp(cuuid,
+ if (uuid_le_cmp(*cuuid,
dev->me_clients[i].props.protocol_name) == 0) {
res = i;
break;
@@ -538,35 +557,35 @@ int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid)
/**
- * mei_find_me_client_update_filext - searches for ME client guid
+ * mei_me_cl_update_filext - searches for ME client guid
* sets client_id in mei_file_private if found
* @dev: the device structure
- * @priv: private file structure to set client_id in
- * @cguid: searched guid of ME client
+ * @cl: private file structure to set client_id in
+ * @cuuid: searched uuid of ME client
* @client_id: id of host client to be set in file private structure
*
* returns ME client index
*/
-u8 mei_find_me_client_update_filext(struct mei_device *dev, struct mei_cl *priv,
- const uuid_le *cguid, u8 client_id)
+int mei_me_cl_update_filext(struct mei_device *dev, struct mei_cl *cl,
+ const uuid_le *cuuid, u8 host_cl_id)
{
int i;
- if (!dev || !priv || !cguid)
- return 0;
+ if (!dev || !cl || !cuuid)
+ return -EINVAL;
/* check for valid client id */
- i = mei_find_me_client_index(dev, *cguid);
+ i = mei_me_cl_by_uuid(dev, cuuid);
if (i >= 0) {
- priv->me_client_id = dev->me_clients[i].client_id;
- priv->state = MEI_FILE_CONNECTING;
- priv->host_client_id = client_id;
+ cl->me_client_id = dev->me_clients[i].client_id;
+ cl->state = MEI_FILE_CONNECTING;
+ cl->host_client_id = host_cl_id;
- list_add_tail(&priv->link, &dev->file_list);
+ list_add_tail(&cl->link, &dev->file_list);
return (u8)i;
}
- return 0;
+ return -ENOENT;
}
/**
@@ -577,16 +596,16 @@ u8 mei_find_me_client_update_filext(struct mei_device *dev, struct mei_cl *priv,
*/
void mei_host_init_iamthif(struct mei_device *dev)
{
- u8 i;
+ int i;
unsigned char *msg_buf;
mei_cl_init(&dev->iamthif_cl, dev);
dev->iamthif_cl.state = MEI_FILE_DISCONNECTED;
/* find ME amthi client */
- i = mei_find_me_client_update_filext(dev, &dev->iamthif_cl,
+ i = mei_me_cl_update_filext(dev, &dev->iamthif_cl,
&mei_amthi_guid, MEI_IAMTHIF_HOST_CLIENT_ID);
- if (dev->iamthif_cl.state != MEI_FILE_CONNECTING) {
+ if (i < 0) {
dev_dbg(&dev->pdev->dev, "failed to find iamthif client.\n");
return;
}
diff --git a/drivers/misc/mei/interface.h b/drivers/misc/mei/interface.h
index fb5c7db4723b..ec6c785a3961 100644
--- a/drivers/misc/mei/interface.h
+++ b/drivers/misc/mei/interface.h
@@ -23,14 +23,6 @@
#include "mei_dev.h"
-#define AMT_WD_DEFAULT_TIMEOUT 120 /* seconds */
-#define AMT_WD_MIN_TIMEOUT 120 /* seconds */
-#define AMT_WD_MAX_TIMEOUT 65535 /* seconds */
-
-#define MEI_WATCHDOG_DATA_SIZE 16
-#define MEI_START_WD_DATA_SIZE 20
-#define MEI_WD_PARAMS_SIZE 4
-
void mei_read_slots(struct mei_device *dev,
unsigned char *buffer,
@@ -64,7 +56,7 @@ int mei_flow_ctrl_creds(struct mei_device *dev, struct mei_cl *cl);
int mei_wd_send(struct mei_device *dev);
-int mei_wd_stop(struct mei_device *dev, bool preserve);
+int mei_wd_stop(struct mei_device *dev);
int mei_wd_host_init(struct mei_device *dev);
/*
* mei_watchdog_register - Registering watchdog interface
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index d78c05e693f7..3533edde04a5 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -221,17 +221,10 @@ static int mei_irq_thread_read_client_message(struct mei_io_list *complete_list,
cl->status = 0;
list_del(&cb_pos->cb_list);
dev_dbg(&dev->pdev->dev,
- "completed read host client = %d,"
- "ME client = %d, "
- "data length = %lu\n",
+ "completed read H cl = %d, ME cl = %d, length = %lu\n",
cl->host_client_id,
cl->me_client_id,
cb_pos->information);
-
- *(cb_pos->response_buffer.data +
- cb_pos->information) = '\0';
- dev_dbg(&dev->pdev->dev, "cb_pos->res_buffer - %s\n",
- cb_pos->response_buffer.data);
list_add_tail(&cb_pos->cb_list,
&complete_list->mei_cb.cb_list);
}
@@ -633,7 +626,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
if (version_res->host_version_supported) {
dev->version.major_version = HBM_MAJOR_VERSION;
dev->version.minor_version = HBM_MINOR_VERSION;
- if (dev->mei_state == MEI_INIT_CLIENTS &&
+ if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
dev->init_clients_state == MEI_START_MESSAGE) {
dev->init_clients_timer = 0;
mei_host_enum_clients_message(dev);
@@ -707,7 +700,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
dev->me_clients[dev->me_client_presentation_num].props
= props_res->client_properties;
- if (dev->mei_state == MEI_INIT_CLIENTS &&
+ if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
dev->init_clients_state ==
MEI_CLIENT_PROPERTIES_MESSAGE) {
dev->me_client_index++;
@@ -734,7 +727,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
* Client ID 2 - Reserved for AMTHI
*/
bitmap_set(dev->host_clients_map, 0, 3);
- dev->mei_state = MEI_ENABLED;
+ dev->dev_state = MEI_DEV_ENABLED;
/* if wd initialization fails, initialization the AMTHI client,
* otherwise the AMTHI client will be initialized after the WD client connect response
@@ -759,7 +752,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
case HOST_ENUM_RES_CMD:
enum_res = (struct hbm_host_enum_response *) mei_msg;
memcpy(dev->me_clients_map, enum_res->valid_addresses, 32);
- if (dev->mei_state == MEI_INIT_CLIENTS &&
+ if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
dev->init_clients_state == MEI_ENUM_CLIENTS_MESSAGE) {
dev->init_clients_timer = 0;
dev->me_client_presentation_num = 0;
@@ -776,7 +769,7 @@ static void mei_irq_thread_read_bus_message(struct mei_device *dev,
break;
case HOST_STOP_RES_CMD:
- dev->mei_state = MEI_DISABLED;
+ dev->dev_state = MEI_DEV_DISABLED;
dev_dbg(&dev->pdev->dev, "resetting because of FW stop response.\n");
mei_reset(dev, 1);
break;
@@ -1224,10 +1217,9 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
}
}
- if (dev->stop && !dev->wd_pending) {
- dev->wd_stopped = true;
+ if (dev->wd_state == MEI_WD_STOPPING) {
+ dev->wd_state = MEI_WD_IDLE;
wake_up_interruptible(&dev->wait_stop_wd);
- return 0;
}
if (dev->extra_write_index) {
@@ -1240,7 +1232,7 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
*slots -= dev->extra_write_index;
dev->extra_write_index = 0;
}
- if (dev->mei_state == MEI_ENABLED) {
+ if (dev->dev_state == MEI_DEV_ENABLED) {
if (dev->wd_pending &&
mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) {
if (mei_wd_send(dev))
@@ -1250,14 +1242,12 @@ static int mei_irq_thread_write_handler(struct mei_io_list *cmpl_list,
dev->wd_pending = false;
- if (dev->wd_timeout)
- *slots -= mei_data2slots(MEI_START_WD_DATA_SIZE);
+ if (dev->wd_state == MEI_WD_RUNNING)
+ *slots -= mei_data2slots(MEI_WD_START_MSG_SIZE);
else
- *slots -= mei_data2slots(MEI_WD_PARAMS_SIZE);
+ *slots -= mei_data2slots(MEI_WD_STOP_MSG_SIZE);
}
}
- if (dev->stop)
- return -ENODEV;
/* complete control write list CB */
dev_dbg(&dev->pdev->dev, "complete control write list cb.\n");
@@ -1361,8 +1351,8 @@ void mei_timer(struct work_struct *work)
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED) {
- if (dev->mei_state == MEI_INIT_CLIENTS) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
+ if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
if (dev->init_clients_timer) {
if (--dev->init_clients_timer == 0) {
dev_dbg(&dev->pdev->dev, "IMEI reset due to init clients timeout ,init clients state = %d.\n",
@@ -1484,8 +1474,8 @@ irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
/* check if ME wants a reset */
if ((dev->me_hw_state & ME_RDY_HRA) == 0 &&
- dev->mei_state != MEI_RESETING &&
- dev->mei_state != MEI_INITIALIZING) {
+ dev->dev_state != MEI_DEV_RESETING &&
+ dev->dev_state != MEI_DEV_INITIALIZING) {
dev_dbg(&dev->pdev->dev, "FW not ready.\n");
mei_reset(dev, 1);
mutex_unlock(&dev->device_lock);
@@ -1498,7 +1488,7 @@ irqreturn_t mei_interrupt_thread_handler(int irq, void *dev_id)
dev_dbg(&dev->pdev->dev, "we need to start the dev.\n");
dev->host_hw_state |= (H_IE | H_IG | H_RDY);
mei_hcsr_set(dev);
- dev->mei_state = MEI_INIT_CLIENTS;
+ dev->dev_state = MEI_DEV_INIT_CLIENTS;
dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
/* link is established
* start sending messages.
diff --git a/drivers/misc/mei/iorw.c b/drivers/misc/mei/iorw.c
index 50f52e21f587..fcba98eb892e 100644
--- a/drivers/misc/mei/iorw.c
+++ b/drivers/misc/mei/iorw.c
@@ -38,7 +38,31 @@
#include <linux/mei.h>
#include "interface.h"
+/**
+ * mei_me_cl_by_id return index to me_clients for client_id
+ *
+ * @dev: the device structure
+ * @client_id: me client id
+ *
+ * Locking: called under "dev->device_lock" lock
+ *
+ * returns index on success, -ENOENT on failure.
+ */
+int mei_me_cl_by_id(struct mei_device *dev, u8 client_id)
+{
+ int i;
+ for (i = 0; i < dev->me_clients_num; i++)
+ if (dev->me_clients[i].client_id == client_id)
+ break;
+ if (WARN_ON(dev->me_clients[i].client_id != client_id))
+ return -ENOENT;
+
+ if (i == dev->me_clients_num)
+ return -ENOENT;
+
+ return i;
+}
/**
* mei_ioctl_connect_client - the connect to fw client IOCTL function
@@ -84,7 +108,7 @@ int mei_ioctl_connect_client(struct file *file,
cb->major_file_operations = MEI_IOCTL;
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
rets = -ENODEV;
goto end;
}
@@ -95,7 +119,7 @@ int mei_ioctl_connect_client(struct file *file,
}
/* find ME client we're trying to connect to */
- i = mei_find_me_client_index(dev, data->in_client_uuid);
+ i = mei_me_cl_by_uuid(dev, &data->in_client_uuid);
if (i >= 0 && !dev->me_clients[i].props.fixed_address) {
cl->me_client_id = dev->me_clients[i].client_id;
cl->state = MEI_FILE_CONNECTING;
@@ -273,19 +297,12 @@ int amthi_read(struct mei_device *dev, struct file *file,
return -ETIMEDOUT;
}
- for (i = 0; i < dev->me_clients_num; i++) {
- if (dev->me_clients[i].client_id ==
- dev->iamthif_cl.me_client_id)
- break;
- }
+ i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id);
- if (i == dev->me_clients_num) {
+ if (i < 0) {
dev_dbg(&dev->pdev->dev, "amthi client not found.\n");
return -ENODEV;
}
- if (WARN_ON(dev->me_clients[i].client_id != cl->me_client_id))
- return -ENODEV;
-
dev_dbg(&dev->pdev->dev, "checking amthi data\n");
cb = find_amthi_read_list_entry(dev, file);
@@ -316,8 +333,7 @@ int amthi_read(struct mei_device *dev, struct file *file,
dev->iamthif_timer = 0;
if (cb) {
- timeout = cb->read_time +
- msecs_to_jiffies(IAMTHIF_READ_TIMER);
+ timeout = cb->read_time + msecs_to_jiffies(IAMTHIF_READ_TIMER);
dev_dbg(&dev->pdev->dev, "amthi timeout = %lud\n",
timeout);
@@ -386,7 +402,7 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl)
if (cl->state != MEI_FILE_CONNECTED)
return -ENODEV;
- if (dev->mei_state != MEI_ENABLED)
+ if (dev->dev_state != MEI_DEV_ENABLED)
return -ENODEV;
dev_dbg(&dev->pdev->dev, "check if read is pending.\n");
@@ -401,19 +417,8 @@ int mei_start_read(struct mei_device *dev, struct mei_cl *cl)
dev_dbg(&dev->pdev->dev, "allocation call back successful. host client = %d, ME client = %d\n",
cl->host_client_id, cl->me_client_id);
-
- for (i = 0; i < dev->me_clients_num; i++) {
- if (dev->me_clients[i].client_id == cl->me_client_id)
- break;
-
- }
-
- if (WARN_ON(dev->me_clients[i].client_id != cl->me_client_id)) {
- rets = -ENODEV;
- goto unlock;
- }
-
- if (i == dev->me_clients_num) {
+ i = mei_me_cl_by_id(dev, cl->me_client_id);
+ if (i < 0) {
rets = -ENODEV;
goto unlock;
}
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 7422c7652845..e8b0858132c1 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -41,11 +41,8 @@
#include <linux/mei.h>
#include "interface.h"
-static const char mei_driver_name[] = "mei";
-
-/* The device pointer */
-/* Currently this driver works as long as there is only a single AMT device. */
-struct pci_dev *mei_device;
+/* AMT device is a singleton on the platform */
+static struct pci_dev *mei_pdev;
/* mei_pci_tbl - PCI Device ID Table */
static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = {
@@ -80,6 +77,8 @@ static DEFINE_PCI_DEVICE_TABLE(mei_pci_tbl) = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_1)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_2)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_PPT_3)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT)},
+ {PCI_DEVICE(PCI_VENDOR_ID_INTEL, MEI_DEV_ID_LPT_LP)},
/* required last entry */
{0, }
@@ -220,10 +219,10 @@ static int mei_open(struct inode *inode, struct file *file)
int err;
err = -ENODEV;
- if (!mei_device)
+ if (!mei_pdev)
goto out;
- dev = pci_get_drvdata(mei_device);
+ dev = pci_get_drvdata(mei_pdev);
if (!dev)
goto out;
@@ -234,18 +233,24 @@ static int mei_open(struct inode *inode, struct file *file)
goto out_unlock;
err = -ENODEV;
- if (dev->mei_state != MEI_ENABLED) {
- dev_dbg(&dev->pdev->dev, "mei_state != MEI_ENABLED mei_state= %d\n",
- dev->mei_state);
+ if (dev->dev_state != MEI_DEV_ENABLED) {
+ dev_dbg(&dev->pdev->dev, "dev_state != MEI_ENABLED dev_state = %s\n",
+ mei_dev_state_str(dev->dev_state));
goto out_unlock;
}
err = -EMFILE;
- if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT)
+ if (dev->open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
+ dev_err(&dev->pdev->dev, "open_handle_count exceded %d",
+ MEI_MAX_OPEN_HANDLE_COUNT);
goto out_unlock;
+ }
cl_id = find_first_zero_bit(dev->host_clients_map, MEI_CLIENTS_MAX);
- if (cl_id >= MEI_CLIENTS_MAX)
+ if (cl_id >= MEI_CLIENTS_MAX) {
+ dev_err(&dev->pdev->dev, "client_id exceded %d",
+ MEI_CLIENTS_MAX) ;
goto out_unlock;
+ }
cl->host_client_id = cl_id;
@@ -386,17 +391,16 @@ static ssize_t mei_read(struct file *file, char __user *ubuf,
dev = cl->dev;
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
rets = -ENODEV;
goto out;
}
if ((cl->sm_state & MEI_WD_STATE_INDEPENDENCE_MSG_SENT) == 0) {
/* Do not allow to read watchdog client */
- i = mei_find_me_client_index(dev, mei_wd_guid);
+ i = mei_me_cl_by_uuid(dev, &mei_wd_guid);
if (i >= 0) {
struct mei_me_client *me_client = &dev->me_clients[i];
-
if (cl->me_client_id == me_client->client_id) {
rets = -EBADF;
goto out;
@@ -541,7 +545,7 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
mutex_unlock(&dev->device_lock);
return -ENODEV;
}
@@ -616,26 +620,16 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
rets = -ENOMEM;
goto unlock_dev;
}
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
rets = -ENODEV;
goto unlock_dev;
}
- for (i = 0; i < dev->me_clients_num; i++) {
- if (dev->me_clients[i].client_id ==
- dev->iamthif_cl.me_client_id)
- break;
- }
-
- if (WARN_ON(dev->me_clients[i].client_id != cl->me_client_id)) {
+ i = mei_me_cl_by_id(dev, dev->iamthif_cl.me_client_id);
+ if (i < 0) {
rets = -ENODEV;
goto unlock_dev;
}
- if (i == dev->me_clients_num ||
- (dev->me_clients[i].client_id !=
- dev->iamthif_cl.me_client_id)) {
- rets = -ENODEV;
- goto unlock_dev;
- } else if (length > dev->me_clients[i].props.max_msg_length ||
+ if (length > dev->me_clients[i].props.max_msg_length ||
length <= 0) {
rets = -EMSGSIZE;
goto unlock_dev;
@@ -688,16 +682,8 @@ static ssize_t mei_write(struct file *file, const char __user *ubuf,
cl->me_client_id);
goto unlock_dev;
}
- for (i = 0; i < dev->me_clients_num; i++) {
- if (dev->me_clients[i].client_id ==
- cl->me_client_id)
- break;
- }
- if (WARN_ON(dev->me_clients[i].client_id != cl->me_client_id)) {
- rets = -ENODEV;
- goto unlock_dev;
- }
- if (i == dev->me_clients_num) {
+ i = mei_me_cl_by_id(dev, cl->me_client_id);
+ if (i < 0) {
rets = -ENODEV;
goto unlock_dev;
}
@@ -790,7 +776,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data)
dev_dbg(&dev->pdev->dev, "IOCTL cmd = 0x%x", cmd);
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
rets = -ENODEV;
goto out;
}
@@ -869,7 +855,7 @@ static unsigned int mei_poll(struct file *file, poll_table *wait)
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED)
+ if (dev->dev_state != MEI_DEV_ENABLED)
goto out;
@@ -966,7 +952,7 @@ static int __devinit mei_probe(struct pci_dev *pdev,
goto end;
}
- if (mei_device) {
+ if (mei_pdev) {
err = -EEXIST;
goto end;
}
@@ -979,7 +965,7 @@ static int __devinit mei_probe(struct pci_dev *pdev,
/* set PCI host mastering */
pci_set_master(pdev);
/* pci request regions for mei driver */
- err = pci_request_regions(pdev, mei_driver_name);
+ err = pci_request_regions(pdev, KBUILD_MODNAME);
if (err) {
dev_err(&pdev->dev, "failed to get pci regions.\n");
goto disable_device;
@@ -1004,12 +990,12 @@ static int __devinit mei_probe(struct pci_dev *pdev,
err = request_threaded_irq(pdev->irq,
NULL,
mei_interrupt_thread_handler,
- IRQF_ONESHOT, mei_driver_name, dev);
+ IRQF_ONESHOT, KBUILD_MODNAME, dev);
else
err = request_threaded_irq(pdev->irq,
mei_interrupt_quick_handler,
mei_interrupt_thread_handler,
- IRQF_SHARED, mei_driver_name, dev);
+ IRQF_SHARED, KBUILD_MODNAME, dev);
if (err) {
dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n",
@@ -1027,7 +1013,7 @@ static int __devinit mei_probe(struct pci_dev *pdev,
if (err)
goto release_irq;
- mei_device = pdev;
+ mei_pdev = pdev;
pci_set_drvdata(pdev, dev);
@@ -1072,7 +1058,7 @@ static void __devexit mei_remove(struct pci_dev *pdev)
{
struct mei_device *dev;
- if (mei_device != pdev)
+ if (mei_pdev != pdev)
return;
dev = pci_get_drvdata(pdev);
@@ -1081,9 +1067,11 @@ static void __devexit mei_remove(struct pci_dev *pdev)
mutex_lock(&dev->device_lock);
- mei_wd_stop(dev, false);
+ cancel_delayed_work(&dev->timer_work);
- mei_device = NULL;
+ mei_wd_stop(dev);
+
+ mei_pdev = NULL;
if (dev->iamthif_cl.state == MEI_FILE_CONNECTED) {
dev->iamthif_cl.state = MEI_FILE_DISCONNECTING;
@@ -1136,12 +1124,15 @@ static int mei_pci_suspend(struct device *device)
if (!dev)
return -ENODEV;
mutex_lock(&dev->device_lock);
+
+ cancel_delayed_work(&dev->timer_work);
+
/* Stop watchdog if exists */
- err = mei_wd_stop(dev, true);
+ err = mei_wd_stop(dev);
/* Set new mei state */
- if (dev->mei_state == MEI_ENABLED ||
- dev->mei_state == MEI_RECOVERING_FROM_RESET) {
- dev->mei_state = MEI_POWER_DOWN;
+ if (dev->dev_state == MEI_DEV_ENABLED ||
+ dev->dev_state == MEI_DEV_RECOVERING_FROM_RESET) {
+ dev->dev_state = MEI_DEV_POWER_DOWN;
mei_reset(dev, 0);
}
mutex_unlock(&dev->device_lock);
@@ -1169,12 +1160,12 @@ static int mei_pci_resume(struct device *device)
err = request_threaded_irq(pdev->irq,
NULL,
mei_interrupt_thread_handler,
- IRQF_ONESHOT, mei_driver_name, dev);
+ IRQF_ONESHOT, KBUILD_MODNAME, dev);
else
err = request_threaded_irq(pdev->irq,
mei_interrupt_quick_handler,
mei_interrupt_thread_handler,
- IRQF_SHARED, mei_driver_name, dev);
+ IRQF_SHARED, KBUILD_MODNAME, dev);
if (err) {
dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n",
@@ -1183,7 +1174,7 @@ static int mei_pci_resume(struct device *device)
}
mutex_lock(&dev->device_lock);
- dev->mei_state = MEI_POWER_UP;
+ dev->dev_state = MEI_DEV_POWER_UP;
mei_reset(dev, 1);
mutex_unlock(&dev->device_lock);
@@ -1201,7 +1192,7 @@ static SIMPLE_DEV_PM_OPS(mei_pm_ops, mei_pci_suspend, mei_pci_resume);
* PCI driver structure
*/
static struct pci_driver mei_driver = {
- .name = mei_driver_name,
+ .name = KBUILD_MODNAME,
.id_table = mei_pci_tbl,
.probe = mei_probe,
.remove = __devexit_p(mei_remove),
diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h
index d61c4ddfc80c..adb35fb9281c 100644
--- a/drivers/misc/mei/mei_dev.h
+++ b/drivers/misc/mei/mei_dev.h
@@ -25,18 +25,20 @@
/*
* watch dog definition
*/
-#define MEI_WATCHDOG_DATA_SIZE 16
-#define MEI_START_WD_DATA_SIZE 20
-#define MEI_WD_PARAMS_SIZE 4
+#define MEI_WD_HDR_SIZE 4
+#define MEI_WD_STOP_MSG_SIZE MEI_WD_HDR_SIZE
+#define MEI_WD_START_MSG_SIZE (MEI_WD_HDR_SIZE + 16)
+
+#define MEI_WD_DEFAULT_TIMEOUT 120 /* seconds */
+#define MEI_WD_MIN_TIMEOUT 120 /* seconds */
+#define MEI_WD_MAX_TIMEOUT 65535 /* seconds */
+
+#define MEI_WD_STOP_TIMEOUT 10 /* msecs */
+
#define MEI_WD_STATE_INDEPENDENCE_MSG_SENT (1 << 0)
#define MEI_RD_MSG_BUF_SIZE (128 * sizeof(u32))
-/*
- * MEI PCI Device object
- */
-extern struct pci_dev *mei_device;
-
/*
* AMTHI Client UUID
@@ -54,19 +56,21 @@ extern const uuid_le mei_wd_guid;
extern const u8 mei_wd_state_independence_msg[3][4];
/*
+ * Number of Maximum MEI Clients
+ */
+#define MEI_CLIENTS_MAX 256
+
+/*
* Number of File descriptors/handles
* that can be opened to the driver.
*
- * Limit to 253: 255 Total Clients
+ * Limit to 253: 256 Total Clients
+ * minus internal client for MEI Bus Messags
* minus internal client for AMTHI
* minus internal client for Watchdog
*/
-#define MEI_MAX_OPEN_HANDLE_COUNT 253
+#define MEI_MAX_OPEN_HANDLE_COUNT (MEI_CLIENTS_MAX - 3)
-/*
- * Number of Maximum MEI Clients
- */
-#define MEI_CLIENTS_MAX 255
/* File state */
enum file_state {
@@ -78,17 +82,19 @@ enum file_state {
};
/* MEI device states */
-enum mei_states {
- MEI_INITIALIZING = 0,
- MEI_INIT_CLIENTS,
- MEI_ENABLED,
- MEI_RESETING,
- MEI_DISABLED,
- MEI_RECOVERING_FROM_RESET,
- MEI_POWER_DOWN,
- MEI_POWER_UP
+enum mei_dev_state {
+ MEI_DEV_INITIALIZING = 0,
+ MEI_DEV_INIT_CLIENTS,
+ MEI_DEV_ENABLED,
+ MEI_DEV_RESETING,
+ MEI_DEV_DISABLED,
+ MEI_DEV_RECOVERING_FROM_RESET,
+ MEI_DEV_POWER_DOWN,
+ MEI_DEV_POWER_UP
};
+const char *mei_dev_state_str(int state);
+
/* init clients states*/
enum mei_init_clients_states {
MEI_START_MESSAGE = 0,
@@ -113,6 +119,12 @@ enum mei_file_transaction_states {
MEI_READ_COMPLETE
};
+enum mei_wd_states {
+ MEI_WD_IDLE,
+ MEI_WD_RUNNING,
+ MEI_WD_STOPPING,
+};
+
/* MEI CB */
enum mei_cb_major_types {
MEI_READ = 0,
@@ -128,7 +140,7 @@ enum mei_cb_major_types {
struct mei_message_data {
u32 size;
unsigned char *data;
-} __packed;
+};
struct mei_cl_cb {
@@ -218,10 +230,9 @@ struct mei_device {
/*
* mei device states
*/
- enum mei_states mei_state;
+ enum mei_dev_state dev_state;
enum mei_init_clients_states init_clients_state;
u16 init_clients_timer;
- bool stop;
bool need_reset;
u32 extra_write_index;
@@ -241,12 +252,11 @@ struct mei_device {
bool mei_host_buffer_is_empty;
struct mei_cl wd_cl;
+ enum mei_wd_states wd_state;
bool wd_interface_reg;
bool wd_pending;
- bool wd_stopped;
- bool wd_bypass; /* if false, don't refresh watchdog ME client */
- u16 wd_timeout; /* seconds ((wd_data[1] << 8) + wd_data[0]) */
- unsigned char wd_data[MEI_START_WD_DATA_SIZE];
+ u16 wd_timeout;
+ unsigned char wd_data[MEI_WD_START_MSG_SIZE];
struct file *iamthif_file_object;
@@ -279,9 +289,10 @@ void mei_host_init_iamthif(struct mei_device *dev);
void mei_allocate_me_clients_storage(struct mei_device *dev);
-u8 mei_find_me_client_update_filext(struct mei_device *dev,
- struct mei_cl *priv,
- const uuid_le *cguid, u8 client_id);
+int mei_me_cl_update_filext(struct mei_device *dev, struct mei_cl *cl,
+ const uuid_le *cguid, u8 host_client_id);
+int mei_me_cl_by_uuid(const struct mei_device *dev, const uuid_le *cuuid);
+int mei_me_cl_by_id(struct mei_device *dev, u8 client_id);
/*
* MEI IO List Functions
@@ -348,7 +359,6 @@ void mei_run_next_iamthif_cmd(struct mei_device *dev);
void mei_free_cb_private(struct mei_cl_cb *priv_cb);
-int mei_find_me_client_index(const struct mei_device *dev, uuid_le cuuid);
/*
* Register Access Function
diff --git a/drivers/misc/mei/wd.c b/drivers/misc/mei/wd.c
index 5133fd77b91c..d96c537f046f 100644
--- a/drivers/misc/mei/wd.c
+++ b/drivers/misc/mei/wd.c
@@ -48,8 +48,8 @@ const uuid_le mei_wd_guid = UUID_LE(0x05B79A6F, 0x4628, 0x4D7F, 0x89,
static void mei_wd_set_start_timeout(struct mei_device *dev, u16 timeout)
{
dev_dbg(&dev->pdev->dev, "wd: set timeout=%d.\n", timeout);
- memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_PARAMS_SIZE);
- memcpy(dev->wd_data + MEI_WD_PARAMS_SIZE, &timeout, sizeof(u16));
+ memcpy(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE);
+ memcpy(dev->wd_data + MEI_WD_HDR_SIZE, &timeout, sizeof(u16));
}
/**
@@ -66,10 +66,11 @@ int mei_wd_host_init(struct mei_device *dev)
/* look for WD client and connect to it */
dev->wd_cl.state = MEI_FILE_DISCONNECTED;
- dev->wd_timeout = AMT_WD_DEFAULT_TIMEOUT;
+ dev->wd_timeout = MEI_WD_DEFAULT_TIMEOUT;
+ dev->wd_state = MEI_WD_IDLE;
/* find ME WD client */
- mei_find_me_client_update_filext(dev, &dev->wd_cl,
+ mei_me_cl_update_filext(dev, &dev->wd_cl,
&mei_wd_guid, MEI_WD_HOST_CLIENT_ID);
dev_dbg(&dev->pdev->dev, "wd: check client\n");
@@ -108,10 +109,10 @@ int mei_wd_send(struct mei_device *dev)
mei_hdr->msg_complete = 1;
mei_hdr->reserved = 0;
- if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_PARAMS_SIZE))
- mei_hdr->length = MEI_START_WD_DATA_SIZE;
- else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_PARAMS_SIZE))
- mei_hdr->length = MEI_WD_PARAMS_SIZE;
+ if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE))
+ mei_hdr->length = MEI_WD_START_MSG_SIZE;
+ else if (!memcmp(dev->wd_data, mei_stop_wd_params, MEI_WD_HDR_SIZE))
+ mei_hdr->length = MEI_WD_STOP_MSG_SIZE;
else
return -EINVAL;
@@ -128,18 +129,17 @@ int mei_wd_send(struct mei_device *dev)
* -EIO when message send fails
* -EINVAL when invalid message is to be sent
*/
-int mei_wd_stop(struct mei_device *dev, bool preserve)
+int mei_wd_stop(struct mei_device *dev)
{
int ret;
- u16 wd_timeout = dev->wd_timeout;
- cancel_delayed_work(&dev->timer_work);
- if (dev->wd_cl.state != MEI_FILE_CONNECTED || !dev->wd_timeout)
+ if (dev->wd_cl.state != MEI_FILE_CONNECTED ||
+ dev->wd_state != MEI_WD_RUNNING)
return 0;
- dev->wd_timeout = 0;
- memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_PARAMS_SIZE);
- dev->stop = true;
+ memcpy(dev->wd_data, mei_stop_wd_params, MEI_WD_STOP_MSG_SIZE);
+
+ dev->wd_state = MEI_WD_STOPPING;
ret = mei_flow_ctrl_creds(dev, &dev->wd_cl);
if (ret < 0)
@@ -161,13 +161,14 @@ int mei_wd_stop(struct mei_device *dev, bool preserve)
} else {
dev->wd_pending = true;
}
- dev->wd_stopped = false;
+
mutex_unlock(&dev->device_lock);
ret = wait_event_interruptible_timeout(dev->wait_stop_wd,
- dev->wd_stopped, 10 * HZ);
+ dev->wd_state == MEI_WD_IDLE,
+ msecs_to_jiffies(MEI_WD_STOP_TIMEOUT));
mutex_lock(&dev->device_lock);
- if (dev->wd_stopped) {
+ if (dev->wd_state == MEI_WD_IDLE) {
dev_dbg(&dev->pdev->dev, "wd: stop completed ret=%d.\n", ret);
ret = 0;
} else {
@@ -177,9 +178,6 @@ int mei_wd_stop(struct mei_device *dev, bool preserve)
"wd: stop failed to complete ret=%d.\n", ret);
}
- if (preserve)
- dev->wd_timeout = wd_timeout;
-
out:
return ret;
}
@@ -196,16 +194,16 @@ static int mei_wd_ops_start(struct watchdog_device *wd_dev)
int err = -ENODEV;
struct mei_device *dev;
- dev = pci_get_drvdata(mei_device);
+ dev = watchdog_get_drvdata(wd_dev);
if (!dev)
return -ENODEV;
mutex_lock(&dev->device_lock);
- if (dev->mei_state != MEI_ENABLED) {
+ if (dev->dev_state != MEI_DEV_ENABLED) {
dev_dbg(&dev->pdev->dev,
- "wd: mei_state != MEI_ENABLED mei_state = %d\n",
- dev->mei_state);
+ "wd: dev_state != MEI_DEV_ENABLED dev_state = %s\n",
+ mei_dev_state_str(dev->dev_state));
goto end_unlock;
}
@@ -233,13 +231,13 @@ end_unlock:
static int mei_wd_ops_stop(struct watchdog_device *wd_dev)
{
struct mei_device *dev;
- dev = pci_get_drvdata(mei_device);
+ dev = watchdog_get_drvdata(wd_dev);
if (!dev)
return -ENODEV;
mutex_lock(&dev->device_lock);
- mei_wd_stop(dev, false);
+ mei_wd_stop(dev);
mutex_unlock(&dev->device_lock);
return 0;
@@ -256,8 +254,8 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev)
{
int ret = 0;
struct mei_device *dev;
- dev = pci_get_drvdata(mei_device);
+ dev = watchdog_get_drvdata(wd_dev);
if (!dev)
return -ENODEV;
@@ -269,6 +267,8 @@ static int mei_wd_ops_ping(struct watchdog_device *wd_dev)
goto end;
}
+ dev->wd_state = MEI_WD_RUNNING;
+
/* Check if we can send the ping to HW*/
if (dev->mei_host_buffer_is_empty &&
mei_flow_ctrl_creds(dev, &dev->wd_cl) > 0) {
@@ -309,13 +309,13 @@ end:
static int mei_wd_ops_set_timeout(struct watchdog_device *wd_dev, unsigned int timeout)
{
struct mei_device *dev;
- dev = pci_get_drvdata(mei_device);
+ dev = watchdog_get_drvdata(wd_dev);
if (!dev)
return -ENODEV;
/* Check Timeout value */
- if (timeout < AMT_WD_MIN_TIMEOUT || timeout > AMT_WD_MAX_TIMEOUT)
+ if (timeout < MEI_WD_MIN_TIMEOUT || timeout > MEI_WD_MAX_TIMEOUT)
return -EINVAL;
mutex_lock(&dev->device_lock);
@@ -341,37 +341,42 @@ static const struct watchdog_ops wd_ops = {
};
static const struct watchdog_info wd_info = {
.identity = INTEL_AMT_WATCHDOG_ID,
- .options = WDIOF_KEEPALIVEPING | WDIOF_ALARMONLY,
+ .options = WDIOF_KEEPALIVEPING |
+ WDIOF_SETTIMEOUT |
+ WDIOF_ALARMONLY,
};
static struct watchdog_device amt_wd_dev = {
.info = &wd_info,
.ops = &wd_ops,
- .timeout = AMT_WD_DEFAULT_TIMEOUT,
- .min_timeout = AMT_WD_MIN_TIMEOUT,
- .max_timeout = AMT_WD_MAX_TIMEOUT,
+ .timeout = MEI_WD_DEFAULT_TIMEOUT,
+ .min_timeout = MEI_WD_MIN_TIMEOUT,
+ .max_timeout = MEI_WD_MAX_TIMEOUT,
};
-void mei_watchdog_register(struct mei_device *dev)
+void mei_watchdog_register(struct mei_device *dev)
{
- dev_dbg(&dev->pdev->dev, "dev->wd_timeout =%d.\n", dev->wd_timeout);
-
if (watchdog_register_device(&amt_wd_dev)) {
dev_err(&dev->pdev->dev,
"wd: unable to register watchdog device.\n");
dev->wd_interface_reg = false;
- } else {
- dev_dbg(&dev->pdev->dev,
- "wd: successfully register watchdog interface.\n");
- dev->wd_interface_reg = true;
+ return;
}
+
+ dev_dbg(&dev->pdev->dev,
+ "wd: successfully register watchdog interface.\n");
+ dev->wd_interface_reg = true;
+ watchdog_set_drvdata(&amt_wd_dev, dev);
}
void mei_watchdog_unregister(struct mei_device *dev)
{
- if (dev->wd_interface_reg)
- watchdog_unregister_device(&amt_wd_dev);
+ if (!dev->wd_interface_reg)
+ return;
+
+ watchdog_set_drvdata(&amt_wd_dev, NULL);
+ watchdog_unregister_device(&amt_wd_dev);
dev->wd_interface_reg = false;
}
diff --git a/drivers/misc/pch_phub.c b/drivers/misc/pch_phub.c
index 9fbcacd703d5..c9f20dae1855 100644
--- a/drivers/misc/pch_phub.c
+++ b/drivers/misc/pch_phub.c
@@ -699,7 +699,7 @@ static int __devinit pch_phub_probe(struct pci_dev *pdev,
chip->pch_phub_base_address = pci_iomap(pdev, 1, 0);
- if (chip->pch_phub_base_address == 0) {
+ if (chip->pch_phub_base_address == NULL) {
dev_err(&pdev->dev, "%s : pci_iomap FAILED", __func__);
ret = -ENOMEM;
goto err_pci_iomap;
@@ -893,18 +893,7 @@ static struct pci_driver pch_phub_driver = {
.resume = pch_phub_resume
};
-static int __init pch_phub_pci_init(void)
-{
- return pci_register_driver(&pch_phub_driver);
-}
-
-static void __exit pch_phub_pci_exit(void)
-{
- pci_unregister_driver(&pch_phub_driver);
-}
-
-module_init(pch_phub_pci_init);
-module_exit(pch_phub_pci_exit);
+module_pci_driver(pch_phub_driver);
MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7223) PHUB");
MODULE_LICENSE("GPL");
diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c
index acfaeeb9e01a..46937b107261 100644
--- a/drivers/misc/ti-st/st_core.c
+++ b/drivers/misc/ti-st/st_core.c
@@ -30,11 +30,13 @@
#include <linux/ti_wilink_st.h>
+extern void st_kim_recv(void *, const unsigned char *, long);
+void st_int_recv(void *, const unsigned char *, long);
/* function pointer pointing to either,
* st_kim_recv during registration to receive fw download responses
* st_int_recv after registration to receive proto stack responses
*/
-void (*st_recv) (void*, const unsigned char*, long);
+static void (*st_recv) (void *, const unsigned char *, long);
/********************************************************************/
static void add_channel_to_table(struct st_data_s *st_gdata,
@@ -100,7 +102,7 @@ int st_int_write(struct st_data_s *st_gdata,
* push the skb received to relevant
* protocol stacks
*/
-void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata)
+static void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata)
{
pr_debug(" %s(prot:%d) ", __func__, chnl_id);
@@ -140,7 +142,7 @@ void st_send_frame(unsigned char chnl_id, struct st_data_s *st_gdata)
* This function is being called with spin lock held, protocol drivers are
* only expected to complete their waits and do nothing more than that.
*/
-void st_reg_complete(struct st_data_s *st_gdata, char err)
+static void st_reg_complete(struct st_data_s *st_gdata, char err)
{
unsigned char i = 0;
pr_info(" %s ", __func__);
@@ -379,7 +381,7 @@ done:
* completely, return that skb which has the pending data.
* In normal cases, return top of txq.
*/
-struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata)
+static struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata)
{
struct sk_buff *returning_skb;
@@ -401,7 +403,7 @@ struct sk_buff *st_int_dequeue(struct st_data_s *st_gdata)
* txq and waitq needs protection since the other contexts
* may be sending data, waking up chip.
*/
-void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb)
+static void st_int_enqueue(struct st_data_s *st_gdata, struct sk_buff *skb)
{
unsigned long flags = 0;
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index 7c14f8fd98db..04a819944f6b 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -63,10 +63,27 @@ static struct platform_device *st_get_plat_device(int id)
* in case of error don't complete so that waiting for proper
* response times out
*/
-void validate_firmware_response(struct kim_data_s *kim_gdata)
+static void validate_firmware_response(struct kim_data_s *kim_gdata)
{
struct sk_buff *skb = kim_gdata->rx_skb;
- if (unlikely(skb->data[5] != 0)) {
+ if (!skb)
+ return;
+
+ /* these magic numbers are the position in the response buffer which
+ * allows us to distinguish whether the response is for the read
+ * version info. command
+ */
+ if (skb->data[2] == 0x01 && skb->data[3] == 0x01 &&
+ skb->data[4] == 0x10 && skb->data[5] == 0x00) {
+ /* fw version response */
+ memcpy(kim_gdata->resp_buffer,
+ kim_gdata->rx_skb->data,
+ kim_gdata->rx_skb->len);
+ complete_all(&kim_gdata->kim_rcvd);
+ kim_gdata->rx_state = ST_W4_PACKET_TYPE;
+ kim_gdata->rx_skb = NULL;
+ kim_gdata->rx_count = 0;
+ } else if (unlikely(skb->data[5] != 0)) {
pr_err("no proper response during fw download");
pr_err("data6 %x", skb->data[5]);
kfree_skb(skb);
@@ -119,7 +136,7 @@ static inline int kim_check_data_len(struct kim_data_s *kim_gdata, int len)
* have been observed to come in bursts of different
* tty_receive and hence the logic
*/
-void kim_int_recv(struct kim_data_s *kim_gdata,
+static void kim_int_recv(struct kim_data_s *kim_gdata,
const unsigned char *data, long count)
{
const unsigned char *ptr;
@@ -207,16 +224,19 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
return -EIO;
}
- if (!wait_for_completion_timeout
- (&kim_gdata->kim_rcvd, msecs_to_jiffies(CMD_RESP_TIME))) {
+ if (!wait_for_completion_interruptible_timeout(
+ &kim_gdata->kim_rcvd, msecs_to_jiffies(CMD_RESP_TIME))) {
pr_err(" waiting for ver info- timed out ");
return -ETIMEDOUT;
}
INIT_COMPLETION(kim_gdata->kim_rcvd);
+ /* the positions 12 & 13 in the response buffer provide with the
+ * chip, major & minor numbers
+ */
version =
- MAKEWORD(kim_gdata->resp_buffer[13],
- kim_gdata->resp_buffer[14]);
+ MAKEWORD(kim_gdata->resp_buffer[12],
+ kim_gdata->resp_buffer[13]);
chip = (version & 0x7C00) >> 10;
min_ver = (version & 0x007F);
maj_ver = (version & 0x0380) >> 7;
@@ -236,7 +256,7 @@ static long read_local_version(struct kim_data_s *kim_gdata, char *bts_scr_name)
return 0;
}
-void skip_change_remote_baud(unsigned char **ptr, long *len)
+static void skip_change_remote_baud(unsigned char **ptr, long *len)
{
unsigned char *nxt_action, *cur_action;
cur_action = *ptr;
@@ -370,9 +390,9 @@ static long download_firmware(struct kim_data_s *kim_gdata)
break;
case ACTION_WAIT_EVENT: /* wait */
pr_debug("W");
- if (!wait_for_completion_timeout
- (&kim_gdata->kim_rcvd,
- msecs_to_jiffies(CMD_RESP_TIME))) {
+ if (!wait_for_completion_interruptible_timeout(
+ &kim_gdata->kim_rcvd,
+ msecs_to_jiffies(CMD_RESP_TIME))) {
pr_err("response timeout during fw download ");
/* timed out */
release_firmware(kim_gdata->fw_entry);
@@ -410,16 +430,10 @@ void st_kim_recv(void *disc_data, const unsigned char *data, long count)
struct st_data_s *st_gdata = (struct st_data_s *)disc_data;
struct kim_data_s *kim_gdata = st_gdata->kim_data;
- /* copy to local buffer */
- if (unlikely(data[4] == 0x01 && data[5] == 0x10 && data[0] == 0x04)) {
- /* must be the read_ver_cmd */
- memcpy(kim_gdata->resp_buffer, data, count);
- complete_all(&kim_gdata->kim_rcvd);
- return;
- } else {
- kim_int_recv(kim_gdata, data, count);
- /* either completes or times out */
- }
+ /* proceed to gather all data and distinguish read fw version response
+ * from other fw responses when data gathering is complete
+ */
+ kim_int_recv(kim_gdata, data, count);
return;
}
@@ -454,11 +468,6 @@ long st_kim_start(void *kim_data)
if (pdata->chip_enable)
pdata->chip_enable(kim_gdata);
- /* Configure BT nShutdown to HIGH state */
- gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
- mdelay(5); /* FIXME: a proper toggle */
- gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH);
- mdelay(100);
/* re-initialize the completion */
INIT_COMPLETION(kim_gdata->ldisc_installed);
/* send notification to UIM */
@@ -467,8 +476,8 @@ long st_kim_start(void *kim_data)
sysfs_notify(&kim_gdata->kim_pdev->dev.kobj,
NULL, "install");
/* wait for ldisc to be installed */
- err = wait_for_completion_timeout(&kim_gdata->ldisc_installed,
- msecs_to_jiffies(LDISC_TIME));
+ err = wait_for_completion_interruptible_timeout(
+ &kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME));
if (!err) {
/* ldisc installation timeout,
* flush uart, power cycle BT_EN */
@@ -500,8 +509,7 @@ long st_kim_start(void *kim_data)
* (b) upon failure to either install ldisc or download firmware.
* The function is responsible to (a) notify UIM about un-installation,
* (b) flush UART if the ldisc was installed.
- * (c) reset BT_EN - pull down nshutdown at the end.
- * (d) invoke platform's chip disabling routine.
+ * (c) invoke platform's chip disabling routine.
*/
long st_kim_stop(void *kim_data)
{
@@ -526,20 +534,13 @@ long st_kim_stop(void *kim_data)
sysfs_notify(&kim_gdata->kim_pdev->dev.kobj, NULL, "install");
/* wait for ldisc to be un-installed */
- err = wait_for_completion_timeout(&kim_gdata->ldisc_installed,
- msecs_to_jiffies(LDISC_TIME));
+ err = wait_for_completion_interruptible_timeout(
+ &kim_gdata->ldisc_installed, msecs_to_jiffies(LDISC_TIME));
if (!err) { /* timeout */
pr_err(" timed out waiting for ldisc to be un-installed");
- return -ETIMEDOUT;
+ err = -ETIMEDOUT;
}
- /* By default configure BT nShutdown to LOW state */
- gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
- mdelay(1);
- gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH);
- mdelay(1);
- gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
-
/* platform specific disable */
if (pdata->chip_disable)
pdata->chip_disable(kim_gdata);
@@ -701,7 +702,7 @@ static const struct file_operations list_debugfs_fops = {
* board-*.c file
*/
-struct dentry *kim_debugfs_dir;
+static struct dentry *kim_debugfs_dir;
static int kim_probe(struct platform_device *pdev)
{
long status;
@@ -731,20 +732,6 @@ static int kim_probe(struct platform_device *pdev)
/* refer to itself */
kim_gdata->core_data->kim_data = kim_gdata;
- /* Claim the chip enable nShutdown gpio from the system */
- kim_gdata->nshutdown = pdata->nshutdown_gpio;
- status = gpio_request(kim_gdata->nshutdown, "kim");
- if (unlikely(status)) {
- pr_err(" gpio %ld request failed ", kim_gdata->nshutdown);
- return status;
- }
-
- /* Configure nShutdown GPIO as output=0 */
- status = gpio_direction_output(kim_gdata->nshutdown, 0);
- if (unlikely(status)) {
- pr_err(" unable to configure gpio %ld", kim_gdata->nshutdown);
- return status;
- }
/* get reference of pdev for request_firmware
*/
kim_gdata->kim_pdev = pdev;
@@ -780,18 +767,10 @@ static int kim_probe(struct platform_device *pdev)
static int kim_remove(struct platform_device *pdev)
{
- /* free the GPIOs requested */
- struct ti_st_plat_data *pdata = pdev->dev.platform_data;
struct kim_data_s *kim_gdata;
kim_gdata = dev_get_drvdata(&pdev->dev);
- /* Free the Bluetooth/FM/GPIO
- * nShutdown gpio from the system
- */
- gpio_free(pdata->nshutdown_gpio);
- pr_info("nshutdown GPIO Freed");
-
debugfs_remove_recursive(kim_debugfs_dir);
sysfs_remove_group(&pdev->dev.kobj, &uim_attr_grp);
pr_info("sysfs entries removed");
@@ -804,7 +783,7 @@ static int kim_remove(struct platform_device *pdev)
return 0;
}
-int kim_suspend(struct platform_device *pdev, pm_message_t state)
+static int kim_suspend(struct platform_device *pdev, pm_message_t state)
{
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
@@ -814,7 +793,7 @@ int kim_suspend(struct platform_device *pdev, pm_message_t state)
return -EOPNOTSUPP;
}
-int kim_resume(struct platform_device *pdev)
+static int kim_resume(struct platform_device *pdev)
{
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
diff --git a/drivers/misc/tifm_7xx1.c b/drivers/misc/tifm_7xx1.c
index ba2479022670..f8d6654391e5 100644
--- a/drivers/misc/tifm_7xx1.c
+++ b/drivers/misc/tifm_7xx1.c
@@ -434,21 +434,9 @@ static struct pci_driver tifm_7xx1_driver = {
.resume = tifm_7xx1_resume,
};
-static int __init tifm_7xx1_init(void)
-{
- return pci_register_driver(&tifm_7xx1_driver);
-}
-
-static void __exit tifm_7xx1_exit(void)
-{
- pci_unregister_driver(&tifm_7xx1_driver);
-}
-
+module_pci_driver(tifm_7xx1_driver);
MODULE_AUTHOR("Alex Dubov");
MODULE_DESCRIPTION("TI FlashMedia host driver");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, tifm_7xx1_pci_tbl);
MODULE_VERSION(DRIVER_VERSION);
-
-module_init(tifm_7xx1_init);
-module_exit(tifm_7xx1_exit);
diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
index d4619e2ec030..2273ce6b6c1a 100644
--- a/drivers/mmc/core/sdio.c
+++ b/drivers/mmc/core/sdio.c
@@ -641,7 +641,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
/*
* If the host and card support UHS-I mode request the card
* to switch to 1.8V signaling level. No 1.8v signalling if
- * UHS mode is not enabled to maintain compatibilty and some
+ * UHS mode is not enabled to maintain compatibility and some
* systems that claim 1.8v signalling in fact do not support
* it.
*/
diff --git a/drivers/mmc/host/at91_mci.c b/drivers/mmc/host/at91_mci.c
index efdb81d21c44..74bed0fc23e7 100644
--- a/drivers/mmc/host/at91_mci.c
+++ b/drivers/mmc/host/at91_mci.c
@@ -356,7 +356,7 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host)
}
/*
- * Update bytes tranfered count during a write operation
+ * Update bytes transfered count during a write operation
*/
static void at91_mci_update_bytes_xfered(struct at91mci_host *host)
{
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index a53c7c478e05..852d5fbda630 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -1022,7 +1022,7 @@ static void atmci_stop_transfer(struct atmel_mci *host)
}
/*
- * Stop data transfer because error(s) occured.
+ * Stop data transfer because error(s) occurred.
*/
static void atmci_stop_transfer_pdc(struct atmel_mci *host)
{
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index f871b31ece5a..38adc330c007 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -446,7 +446,7 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
OMAP_HSMMC_WRITE(host->base, SYSCTL,
OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
if ((OMAP_HSMMC_READ(host->base, SYSCTL) & CEN) != 0x0)
- dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
+ dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stopped\n");
}
static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
@@ -1781,7 +1781,7 @@ static int __devinit omap_hsmmc_probe(struct platform_device *pdev)
if (match) {
pdata = of_get_hsmmc_pdata(&pdev->dev);
if (match->data) {
- u16 *offsetp = match->data;
+ const u16 *offsetp = match->data;
pdata->reg_offset = *offsetp;
}
}
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index c4c504c4802b..effc2acfe778 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -315,7 +315,7 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
new_val = val & (SDHCI_CTRL_LED | \
SDHCI_CTRL_4BITBUS | \
SDHCI_CTRL_D3CD);
- /* ensure the endianess */
+ /* ensure the endianness */
new_val |= ESDHC_HOST_CONTROL_LE;
/* bits 8&9 are reserved on mx25 */
if (!is_imx25_esdhc(imx_data)) {
diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c
index 3135a1a5d75d..58eab9ac1d01 100644
--- a/drivers/mmc/host/vub300.c
+++ b/drivers/mmc/host/vub300.c
@@ -806,7 +806,7 @@ static void command_res_completed(struct urb *urb)
* we suspect a buggy USB host controller
*/
} else if (!vub300->data) {
- /* this means that the command (typically CMD52) suceeded */
+ /* this means that the command (typically CMD52) succeeded */
} else if (vub300->resp.common.header_type != 0x02) {
/*
* this is an error response from the VUB300 chip
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index f2f482bec573..a6e74514e662 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -1123,6 +1123,33 @@ static unsigned long mtdchar_get_unmapped_area(struct file *file,
}
#endif
+static inline unsigned long get_vm_size(struct vm_area_struct *vma)
+{
+ return vma->vm_end - vma->vm_start;
+}
+
+static inline resource_size_t get_vm_offset(struct vm_area_struct *vma)
+{
+ return (resource_size_t) vma->vm_pgoff << PAGE_SHIFT;
+}
+
+/*
+ * Set a new vm offset.
+ *
+ * Verify that the incoming offset really works as a page offset,
+ * and that the offset and size fit in a resource_size_t.
+ */
+static inline int set_vm_offset(struct vm_area_struct *vma, resource_size_t off)
+{
+ pgoff_t pgoff = off >> PAGE_SHIFT;
+ if (off != (resource_size_t) pgoff << PAGE_SHIFT)
+ return -EINVAL;
+ if (off + get_vm_size(vma) - 1 < off)
+ return -EINVAL;
+ vma->vm_pgoff = pgoff;
+ return 0;
+}
+
/*
* set up a mapping for shared memory segments
*/
@@ -1132,20 +1159,29 @@ static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma)
struct mtd_file_info *mfi = file->private_data;
struct mtd_info *mtd = mfi->mtd;
struct map_info *map = mtd->priv;
- unsigned long start;
- unsigned long off;
- u32 len;
+ resource_size_t start, off;
+ unsigned long len, vma_len;
if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) {
- off = vma->vm_pgoff << PAGE_SHIFT;
+ off = get_vm_offset(vma);
start = map->phys;
len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size);
start &= PAGE_MASK;
- if ((vma->vm_end - vma->vm_start + off) > len)
+ vma_len = get_vm_size(vma);
+
+ /* Overflow in off+len? */
+ if (vma_len + off < off)
+ return -EINVAL;
+ /* Does it fit in the mapping? */
+ if (vma_len + off > len)
return -EINVAL;
off += start;
- vma->vm_pgoff = off >> PAGE_SHIFT;
+ /* Did that overflow? */
+ if (off < start)
+ return -EINVAL;
+ if (set_vm_offset(vma, off) < 0)
+ return -EINVAL;
vma->vm_flags |= VM_IO | VM_RESERVED;
#ifdef pgprot_noncached
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 8ca417614c57..598cd0a3adee 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -406,46 +406,6 @@ config MTD_NAND_ATMEL
help
Enables support for NAND Flash / Smart Media Card interface
on Atmel AT91 and AVR32 processors.
-choice
- prompt "ECC management for NAND Flash / SmartMedia on AT91 / AVR32"
- depends on MTD_NAND_ATMEL
-
-config MTD_NAND_ATMEL_ECC_HW
- bool "Hardware ECC"
- depends on ARCH_AT91SAM9263 || ARCH_AT91SAM9260 || AVR32
- help
- Use hardware ECC instead of software ECC when the chip
- supports it.
-
- The hardware ECC controller is capable of single bit error
- correction and 2-bit random detection per page.
-
- NB : hardware and software ECC schemes are incompatible.
- If you switch from one to another, you'll have to erase your
- mtd partition.
-
- If unsure, say Y
-
-config MTD_NAND_ATMEL_ECC_SOFT
- bool "Software ECC"
- help
- Use software ECC.
-
- NB : hardware and software ECC schemes are incompatible.
- If you switch from one to another, you'll have to erase your
- mtd partition.
-
-config MTD_NAND_ATMEL_ECC_NONE
- bool "No ECC (testing only, DANGEROUS)"
- depends on DEBUG_KERNEL
- help
- No ECC will be used.
- It's not a good idea and it should be reserved for testing
- purpose only.
-
- If unsure, say N
-
-endchoice
config MTD_NAND_PXA3xx
tristate "Support for NAND flash devices on PXA3xx"
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
index 98ee43819911..7edadee487ba 100644
--- a/drivers/net/can/janz-ican3.c
+++ b/drivers/net/can/janz-ican3.c
@@ -1391,7 +1391,6 @@ static irqreturn_t ican3_irq(int irq, void *dev_id)
*/
static int ican3_reset_module(struct ican3_dev *mod)
{
- u8 val = 1 << mod->num;
unsigned long start;
u8 runold, runnew;
@@ -1405,8 +1404,7 @@ static int ican3_reset_module(struct ican3_dev *mod)
runold = ioread8(mod->dpm + TARGET_RUNNING);
/* reset the module */
- iowrite8(val, &mod->ctrl->reset_assert);
- iowrite8(val, &mod->ctrl->reset_deassert);
+ iowrite8(0x00, &mod->dpmctrl->hwreset);
/* wait until the module has finished resetting and is running */
start = jiffies;
diff --git a/drivers/net/can/mscan/mpc5xxx_can.c b/drivers/net/can/mscan/mpc5xxx_can.c
index 06adf881ea24..8a8df82988d1 100644
--- a/drivers/net/can/mscan/mpc5xxx_can.c
+++ b/drivers/net/can/mscan/mpc5xxx_can.c
@@ -380,12 +380,12 @@ static int mpc5xxx_can_resume(struct platform_device *ofdev)
}
#endif
-static struct mpc5xxx_can_data __devinitdata mpc5200_can_data = {
+static const struct mpc5xxx_can_data __devinitdata mpc5200_can_data = {
.type = MSCAN_TYPE_MPC5200,
.get_clock = mpc52xx_can_get_clock,
};
-static struct mpc5xxx_can_data __devinitdata mpc5121_can_data = {
+static const struct mpc5xxx_can_data __devinitdata mpc5121_can_data = {
.type = MSCAN_TYPE_MPC5121,
.get_clock = mpc512x_can_get_clock,
};
diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c
index 527dbcf95335..9ded21e79db5 100644
--- a/drivers/net/can/ti_hecc.c
+++ b/drivers/net/can/ti_hecc.c
@@ -984,12 +984,12 @@ static int __devexit ti_hecc_remove(struct platform_device *pdev)
struct net_device *ndev = platform_get_drvdata(pdev);
struct ti_hecc_priv *priv = netdev_priv(ndev);
+ unregister_candev(ndev);
clk_disable(priv->clk);
clk_put(priv->clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
iounmap(priv->base);
release_mem_region(res->start, resource_size(res));
- unregister_candev(ndev);
free_candev(ndev);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/net/ethernet/3com/typhoon.c b/drivers/net/ethernet/3com/typhoon.c
index b15366635147..bb9670f29b59 100644
--- a/drivers/net/ethernet/3com/typhoon.c
+++ b/drivers/net/ethernet/3com/typhoon.c
@@ -364,7 +364,7 @@ typhoon_inc_rxfree_index(u32 *index, const int count)
static inline void
typhoon_inc_tx_index(u32 *index, const int count)
{
- /* if we start using the Hi Tx ring, this needs updateing */
+ /* if we start using the Hi Tx ring, this needs updating */
typhoon_inc_index(index, count, TXLO_ENTRIES);
}
diff --git a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
index 1bf5bbfe778e..55a2e3795055 100644
--- a/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
+++ b/drivers/net/ethernet/atheros/atl1c/atl1c_main.c
@@ -149,7 +149,7 @@ static void atl1c_reset_pcie(struct atl1c_hw *hw, u32 flag)
data &= ~(PCI_ERR_UNC_DLP | PCI_ERR_UNC_FCP);
pci_write_config_dword(pdev, pos + PCI_ERR_UNCOR_SEVER, data);
/* clear error status */
- pci_write_config_word(pdev, pci_pcie_cap(pdev) + PCI_EXP_DEVSTA,
+ pcie_capability_write_word(pdev, PCI_EXP_DEVSTA,
PCI_EXP_DEVSTA_NFED |
PCI_EXP_DEVSTA_FED |
PCI_EXP_DEVSTA_CED |
@@ -2685,7 +2685,7 @@ static void atl1c_io_resume(struct pci_dev *pdev)
netif_device_attach(netdev);
}
-static struct pci_error_handlers atl1c_err_handler = {
+static const struct pci_error_handlers atl1c_err_handler = {
.error_detected = atl1c_io_error_detected,
.slot_reset = atl1c_io_slot_reset,
.resume = atl1c_io_resume,
diff --git a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
index a98acc8a956f..e213da29e73d 100644
--- a/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
+++ b/drivers/net/ethernet/atheros/atl1e/atl1e_main.c
@@ -2489,7 +2489,7 @@ static void atl1e_io_resume(struct pci_dev *pdev)
netif_device_attach(netdev);
}
-static struct pci_error_handlers atl1e_err_handler = {
+static const struct pci_error_handlers atl1e_err_handler = {
.error_detected = atl1e_io_error_detected,
.slot_reset = atl1e_io_slot_reset,
.resume = atl1e_io_resume,
diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c
index 79cebd8525ce..d4310700c7a7 100644
--- a/drivers/net/ethernet/broadcom/bnx2.c
+++ b/drivers/net/ethernet/broadcom/bnx2.c
@@ -8564,7 +8564,7 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
error:
- iounmap(bp->regview);
+ pci_iounmap(pdev, bp->regview);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
@@ -8742,7 +8742,7 @@ static void bnx2_io_resume(struct pci_dev *pdev)
rtnl_unlock();
}
-static struct pci_error_handlers bnx2_err_handler = {
+static const struct pci_error_handlers bnx2_err_handler = {
.error_detected = bnx2_io_error_detected,
.slot_reset = bnx2_io_slot_reset,
.resume = bnx2_io_resume,
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index 6d1a24acb77e..eac25236856c 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -1458,7 +1458,7 @@ struct bnx2x {
int fw_stats_req_sz;
/*
- * FW statistics data shortcut (points at the begining of
+ * FW statistics data shortcut (points at the beginning of
* fw_stats buffer + fw_stats_req_sz).
*/
struct bnx2x_fw_stats_data *fw_stats_data;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
index af20c6ee2cd9..e8e97a7d1d06 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c
@@ -662,14 +662,16 @@ void bnx2x_csum_validate(struct sk_buff *skb, union eth_rx_cqe *cqe,
struct bnx2x_fastpath *fp,
struct bnx2x_eth_q_stats *qstats)
{
- /* Do nothing if no IP/L4 csum validation was done */
-
+ /* Do nothing if no L4 csum validation was done.
+ * We do not check whether IP csum was validated. For IPv4 we assume
+ * that if the card got as far as validating the L4 csum, it also
+ * validated the IP csum. IPv6 has no IP csum.
+ */
if (cqe->fast_path_cqe.status_flags &
- (ETH_FAST_PATH_RX_CQE_IP_XSUM_NO_VALIDATION_FLG |
- ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG))
+ ETH_FAST_PATH_RX_CQE_L4_XSUM_NO_VALIDATION_FLG)
return;
- /* If both IP/L4 validation were done, check if an error was found. */
+ /* If L4 validation was done, check if an error was found. */
if (cqe->fast_path_cqe.type_error_flags &
(ETH_FAST_PATH_RX_CQE_IP_BAD_XSUM_FLG |
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index 211753e01f81..e11485ca037d 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -1162,14 +1162,9 @@ static int bnx2x_send_final_clnup(struct bnx2x *bp, u8 clnup_func,
static u8 bnx2x_is_pcie_pending(struct pci_dev *dev)
{
- int pos;
u16 status;
- pos = pci_pcie_cap(dev);
- if (!pos)
- return false;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status);
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
return status & PCI_EXP_DEVSTA_TRPND;
}
@@ -6135,8 +6130,7 @@ static void bnx2x_init_pxp(struct bnx2x *bp)
u16 devctl;
int r_order, w_order;
- pci_read_config_word(bp->pdev,
- pci_pcie_cap(bp->pdev) + PCI_EXP_DEVCTL, &devctl);
+ pcie_capability_read_word(bp->pdev, PCI_EXP_DEVCTL, &devctl);
DP(NETIF_MSG_HW, "read 0x%x from devctl\n", devctl);
w_order = ((devctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
if (bp->mrrs == -1)
@@ -9380,7 +9374,7 @@ static int __devinit bnx2x_prev_mark_path(struct bnx2x *bp)
static int __devinit bnx2x_do_flr(struct bnx2x *bp)
{
- int i, pos;
+ int i;
u16 status;
struct pci_dev *dev = bp->pdev;
@@ -9397,16 +9391,12 @@ static int __devinit bnx2x_do_flr(struct bnx2x *bp)
return -EINVAL;
}
- pos = pci_pcie_cap(dev);
- if (!pos)
- return -ENOTTY;
-
/* Wait for Transaction Pending bit clean */
for (i = 0; i < 4; i++) {
if (i)
msleep((1 << (i - 1)) * 100);
- pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status);
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
if (!(status & PCI_EXP_DEVSTA_TRPND))
goto clear;
}
@@ -9831,12 +9821,13 @@ static void __devinit bnx2x_get_igu_cam_info(struct bnx2x *bp)
}
#ifdef CONFIG_PCI_MSI
- /*
- * It's expected that number of CAM entries for this functions is equal
- * to the number evaluated based on the MSI-X table size. We want a
- * harsh warning if these values are different!
+ /* Due to new PF resource allocation by MFW T7.4 and above, it's
+ * optional that number of CAM entries will not be equal to the value
+ * advertised in PCI.
+ * Driver should use the minimal value of both as the actual status
+ * block count
*/
- WARN_ON(bp->igu_sb_cnt != igu_sb_cnt);
+ bp->igu_sb_cnt = min_t(int, bp->igu_sb_cnt, igu_sb_cnt);
#endif
if (igu_sb_cnt == 0)
@@ -12166,7 +12157,7 @@ static void bnx2x_io_resume(struct pci_dev *pdev)
rtnl_unlock();
}
-static struct pci_error_handlers bnx2x_err_handler = {
+static const struct pci_error_handlers bnx2x_err_handler = {
.error_detected = bnx2x_io_error_detected,
.slot_reset = bnx2x_io_slot_reset,
.resume = bnx2x_io_resume,
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
index f83e033da6da..acf2fe4ca608 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
@@ -1321,7 +1321,7 @@ void bnx2x_init_mcast_obj(struct bnx2x *bp,
* the current command will be enqueued to the tail of the
* pending commands list.
*
- * Return: 0 is operation was sucessfull and there are no pending completions,
+ * Return: 0 is operation was successfull and there are no pending completions,
* negative if there were errors, positive if there are pending
* completions.
*/
diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c
index bf906c51d82a..388d32213937 100644
--- a/drivers/net/ethernet/broadcom/tg3.c
+++ b/drivers/net/ethernet/broadcom/tg3.c
@@ -3653,17 +3653,9 @@ static int tg3_power_down_prepare(struct tg3 *tp)
tg3_enable_register_access(tp);
/* Restore the CLKREQ setting. */
- if (tg3_flag(tp, CLKREQ_BUG)) {
- u16 lnkctl;
-
- pci_read_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
- &lnkctl);
- lnkctl |= PCI_EXP_LNKCTL_CLKREQ_EN;
- pci_write_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
- lnkctl);
- }
+ if (tg3_flag(tp, CLKREQ_BUG))
+ pcie_capability_set_word(tp->pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
misc_host_ctrl = tr32(TG3PCI_MISC_HOST_CTRL);
tw32(TG3PCI_MISC_HOST_CTRL,
@@ -4434,20 +4426,13 @@ relink:
/* Prevent send BD corruption. */
if (tg3_flag(tp, CLKREQ_BUG)) {
- u16 oldlnkctl, newlnkctl;
-
- pci_read_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
- &oldlnkctl);
if (tp->link_config.active_speed == SPEED_100 ||
tp->link_config.active_speed == SPEED_10)
- newlnkctl = oldlnkctl & ~PCI_EXP_LNKCTL_CLKREQ_EN;
+ pcie_capability_clear_word(tp->pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
else
- newlnkctl = oldlnkctl | PCI_EXP_LNKCTL_CLKREQ_EN;
- if (newlnkctl != oldlnkctl)
- pci_write_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) +
- PCI_EXP_LNKCTL, newlnkctl);
+ pcie_capability_set_word(tp->pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
}
if (current_link_up != netif_carrier_ok(tp->dev)) {
@@ -8054,7 +8039,7 @@ static int tg3_chip_reset(struct tg3 *tp)
udelay(120);
- if (tg3_flag(tp, PCI_EXPRESS) && pci_pcie_cap(tp->pdev)) {
+ if (tg3_flag(tp, PCI_EXPRESS) && pci_is_pcie(tp->pdev)) {
u16 val16;
if (tp->pci_chip_rev_id == CHIPREV_ID_5750_A0) {
@@ -8071,24 +8056,17 @@ static int tg3_chip_reset(struct tg3 *tp)
}
/* Clear the "no snoop" and "relaxed ordering" bits. */
- pci_read_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_DEVCTL,
- &val16);
- val16 &= ~(PCI_EXP_DEVCTL_RELAX_EN |
- PCI_EXP_DEVCTL_NOSNOOP_EN);
+ val16 = PCI_EXP_DEVCTL_RELAX_EN | PCI_EXP_DEVCTL_NOSNOOP_EN;
/*
* Older PCIe devices only support the 128 byte
* MPS setting. Enforce the restriction.
*/
if (!tg3_flag(tp, CPMU_PRESENT))
- val16 &= ~PCI_EXP_DEVCTL_PAYLOAD;
- pci_write_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_DEVCTL,
- val16);
+ val16 |= PCI_EXP_DEVCTL_PAYLOAD;
+ pcie_capability_clear_word(tp->pdev, PCI_EXP_DEVCTL, val16);
/* Clear error status */
- pci_write_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_DEVSTA,
+ pcie_capability_write_word(tp->pdev, PCI_EXP_DEVSTA,
PCI_EXP_DEVSTA_CED |
PCI_EXP_DEVSTA_NFED |
PCI_EXP_DEVSTA_FED |
@@ -14565,9 +14543,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tg3_flag_set(tp, PCI_EXPRESS);
- pci_read_config_word(tp->pdev,
- pci_pcie_cap(tp->pdev) + PCI_EXP_LNKCTL,
- &lnkctl);
+ pcie_capability_read_word(tp->pdev, PCI_EXP_LNKCTL, &lnkctl);
if (lnkctl & PCI_EXP_LNKCTL_CLKREQ_EN) {
if (GET_ASIC_REV(tp->pci_chip_rev_id) ==
ASIC_REV_5906) {
@@ -16397,7 +16373,7 @@ done:
rtnl_unlock();
}
-static struct pci_error_handlers tg3_err_handler = {
+static const struct pci_error_handlers tg3_err_handler = {
.error_detected = tg3_io_error_detected,
.slot_reset = tg3_io_slot_reset,
.resume = tg3_io_resume
diff --git a/drivers/net/ethernet/cadence/at91_ether.c b/drivers/net/ethernet/cadence/at91_ether.c
index 77884191a8c6..4e980a7886fb 100644
--- a/drivers/net/ethernet/cadence/at91_ether.c
+++ b/drivers/net/ethernet/cadence/at91_ether.c
@@ -1086,7 +1086,7 @@ static int __init at91ether_probe(struct platform_device *pdev)
/* Clock */
lp->ether_clk = clk_get(&pdev->dev, "ether_clk");
if (IS_ERR(lp->ether_clk)) {
- res = -ENODEV;
+ res = PTR_ERR(lp->ether_clk);
goto err_ioumap;
}
clk_enable(lp->ether_clk);
diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
index 6505070abcfa..875bbb999aa2 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c
@@ -3036,7 +3036,7 @@ static void t3_io_resume(struct pci_dev *pdev)
t3_resume_ports(adapter);
}
-static struct pci_error_handlers t3_err_handler = {
+static const struct pci_error_handlers t3_err_handler = {
.error_detected = t3_io_error_detected,
.slot_reset = t3_io_slot_reset,
.resume = t3_io_resume,
diff --git a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
index bff8a3cdd3df..aef45d3113ba 100644
--- a/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb3/t3_hw.c
@@ -3289,22 +3289,18 @@ static void config_pcie(struct adapter *adap)
unsigned int log2_width, pldsize;
unsigned int fst_trn_rx, fst_trn_tx, acklat, rpllmt;
- pci_read_config_word(adap->pdev,
- adap->pdev->pcie_cap + PCI_EXP_DEVCTL,
- &val);
+ pcie_capability_read_word(adap->pdev, PCI_EXP_DEVCTL, &val);
pldsize = (val & PCI_EXP_DEVCTL_PAYLOAD) >> 5;
pci_read_config_word(adap->pdev, 0x2, &devid);
if (devid == 0x37) {
- pci_write_config_word(adap->pdev,
- adap->pdev->pcie_cap + PCI_EXP_DEVCTL,
- val & ~PCI_EXP_DEVCTL_READRQ &
- ~PCI_EXP_DEVCTL_PAYLOAD);
+ pcie_capability_write_word(adap->pdev, PCI_EXP_DEVCTL,
+ val & ~PCI_EXP_DEVCTL_READRQ &
+ ~PCI_EXP_DEVCTL_PAYLOAD);
pldsize = 0;
}
- pci_read_config_word(adap->pdev, adap->pdev->pcie_cap + PCI_EXP_LNKCTL,
- &val);
+ pcie_capability_read_word(adap->pdev, PCI_EXP_LNKCTL, &val);
fst_trn_tx = G_NUMFSTTRNSEQ(t3_read_reg(adap, A_PCIE_PEX_CTRL0));
fst_trn_rx = adap->params.rev == 0 ? fst_trn_tx :
@@ -3425,15 +3421,13 @@ out_err:
static void get_pci_mode(struct adapter *adapter, struct pci_params *p)
{
static unsigned short speed_map[] = { 33, 66, 100, 133 };
- u32 pci_mode, pcie_cap;
+ u32 pci_mode;
- pcie_cap = pci_pcie_cap(adapter->pdev);
- if (pcie_cap) {
+ if (pci_is_pcie(adapter->pdev)) {
u16 val;
p->variant = PCI_VARIANT_PCIE;
- pci_read_config_word(adapter->pdev, pcie_cap + PCI_EXP_LNKSTA,
- &val);
+ pcie_capability_read_word(adapter->pdev, PCI_EXP_LNKSTA, &val);
p->width = (val >> 4) & 0x3f;
return;
}
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 5ed49af23d6a..933985420acb 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -3453,7 +3453,7 @@ static void eeh_resume(struct pci_dev *pdev)
rtnl_unlock();
}
-static struct pci_error_handlers cxgb4_eeh = {
+static const struct pci_error_handlers cxgb4_eeh = {
.error_detected = eeh_err_detected,
.slot_reset = eeh_slot_reset,
.resume = eeh_resume,
@@ -3694,15 +3694,7 @@ static void __devinit print_port_info(const struct net_device *dev)
static void __devinit enable_pcie_relaxed_ordering(struct pci_dev *dev)
{
- u16 v;
- int pos;
-
- pos = pci_pcie_cap(dev);
- if (pos > 0) {
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &v);
- v |= PCI_EXP_DEVCTL_RELAX_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, v);
- }
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_RELAX_EN);
}
/*
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index fa947dfa4c30..af1601323173 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -2741,11 +2741,9 @@ static void __devinit get_pci_mode(struct adapter *adapter,
struct pci_params *p)
{
u16 val;
- u32 pcie_cap = pci_pcie_cap(adapter->pdev);
- if (pcie_cap) {
- pci_read_config_word(adapter->pdev, pcie_cap + PCI_EXP_LNKSTA,
- &val);
+ if (pci_is_pcie(adapter->pdev)) {
+ pcie_capability_read_word(adapter->pdev, PCI_EXP_LNKSTA, &val);
p->speed = val & PCI_EXP_LNKSTA_CLS;
p->width = (val & PCI_EXP_LNKSTA_NLW) >> 4;
}
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index 78b8aa8069f0..95d10472f236 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -4106,7 +4106,7 @@ err:
dev_err(&adapter->pdev->dev, "EEH resume failed\n");
}
-static struct pci_error_handlers be_eeh_handlers = {
+static const struct pci_error_handlers be_eeh_handlers = {
.error_detected = be_eeh_err_detected,
.slot_reset = be_eeh_reset,
.resume = be_eeh_resume,
diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c
index 8971921cc1c8..ab6762caa957 100644
--- a/drivers/net/ethernet/freescale/gianfar_ethtool.c
+++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c
@@ -1773,6 +1773,7 @@ static int gfar_get_nfc(struct net_device *dev, struct ethtool_rxnfc *cmd,
}
int gfar_phc_index = -1;
+EXPORT_SYMBOL(gfar_phc_index);
static int gfar_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
diff --git a/drivers/net/ethernet/freescale/gianfar_ptp.c b/drivers/net/ethernet/freescale/gianfar_ptp.c
index c08e5d40fecb..0daa66b8eca0 100644
--- a/drivers/net/ethernet/freescale/gianfar_ptp.c
+++ b/drivers/net/ethernet/freescale/gianfar_ptp.c
@@ -515,7 +515,7 @@ static int gianfar_ptp_probe(struct platform_device *dev)
err = PTR_ERR(etsects->clock);
goto no_clock;
}
- gfar_phc_clock = ptp_clock_index(etsects->clock);
+ gfar_phc_index = ptp_clock_index(etsects->clock);
dev_set_drvdata(&dev->dev, etsects);
@@ -539,7 +539,7 @@ static int gianfar_ptp_remove(struct platform_device *dev)
gfar_write(&etsects->regs->tmr_temask, 0);
gfar_write(&etsects->regs->tmr_ctrl, 0);
- gfar_phc_clock = -1;
+ gfar_phc_index = -1;
ptp_clock_unregister(etsects->clock);
iounmap(etsects->regs);
release_resource(etsects->rsrc);
diff --git a/drivers/net/ethernet/intel/e100.c b/drivers/net/ethernet/intel/e100.c
index 535f94fac4a1..29ce9bd27f94 100644
--- a/drivers/net/ethernet/intel/e100.c
+++ b/drivers/net/ethernet/intel/e100.c
@@ -3157,7 +3157,7 @@ static void e100_io_resume(struct pci_dev *pdev)
}
}
-static struct pci_error_handlers e100_err_handler = {
+static const struct pci_error_handlers e100_err_handler = {
.error_detected = e100_io_error_detected,
.slot_reset = e100_io_slot_reset,
.resume = e100_io_resume,
diff --git a/drivers/net/ethernet/intel/e1000/e1000_main.c b/drivers/net/ethernet/intel/e1000/e1000_main.c
index 3bfbb8df8989..f3f9aeb7d1e1 100644
--- a/drivers/net/ethernet/intel/e1000/e1000_main.c
+++ b/drivers/net/ethernet/intel/e1000/e1000_main.c
@@ -192,7 +192,7 @@ static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev,
static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev);
static void e1000_io_resume(struct pci_dev *pdev);
-static struct pci_error_handlers e1000_err_handler = {
+static const struct pci_error_handlers e1000_err_handler = {
.error_detected = e1000_io_error_detected,
.slot_reset = e1000_io_slot_reset,
.resume = e1000_io_resume,
@@ -3149,6 +3149,17 @@ static netdev_tx_t e1000_xmit_frame(struct sk_buff *skb,
return NETDEV_TX_OK;
}
+ /* On PCI/PCI-X HW, if packet size is less than ETH_ZLEN,
+ * packets may get corrupted during padding by HW.
+ * To WA this issue, pad all small packets manually.
+ */
+ if (skb->len < ETH_ZLEN) {
+ if (skb_pad(skb, ETH_ZLEN - skb->len))
+ return NETDEV_TX_OK;
+ skb->len = ETH_ZLEN;
+ skb_set_tail_pointer(skb, ETH_ZLEN);
+ }
+
mss = skb_shinfo(skb)->gso_size;
/* The controller does a simple calculation to
* make sure there is enough room in the FIFO before
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index d01a099475a1..3f0223ac4c7c 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -5584,16 +5584,15 @@ static void e1000_complete_shutdown(struct pci_dev *pdev, bool sleep,
*/
if (adapter->flags & FLAG_IS_QUAD_PORT) {
struct pci_dev *us_dev = pdev->bus->self;
- int pos = pci_pcie_cap(us_dev);
u16 devctl;
- pci_read_config_word(us_dev, pos + PCI_EXP_DEVCTL, &devctl);
- pci_write_config_word(us_dev, pos + PCI_EXP_DEVCTL,
- (devctl & ~PCI_EXP_DEVCTL_CERE));
+ pcie_capability_read_word(us_dev, PCI_EXP_DEVCTL, &devctl);
+ pcie_capability_write_word(us_dev, PCI_EXP_DEVCTL,
+ (devctl & ~PCI_EXP_DEVCTL_CERE));
e1000_power_off(pdev, sleep, wake);
- pci_write_config_word(us_dev, pos + PCI_EXP_DEVCTL, devctl);
+ pcie_capability_write_word(us_dev, PCI_EXP_DEVCTL, devctl);
} else {
e1000_power_off(pdev, sleep, wake);
}
@@ -5607,25 +5606,15 @@ static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
#else
static void __e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
{
- int pos;
- u16 reg16;
-
/*
* Both device and parent should have the same ASPM setting.
* Disable ASPM in downstream component first and then upstream.
*/
- pos = pci_pcie_cap(pdev);
- pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
- reg16 &= ~state;
- pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
-
- if (!pdev->bus->self)
- return;
+ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL, state);
- pos = pci_pcie_cap(pdev->bus->self);
- pci_read_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, &reg16);
- reg16 &= ~state;
- pci_write_config_word(pdev->bus->self, pos + PCI_EXP_LNKCTL, reg16);
+ if (pdev->bus->self)
+ pcie_capability_clear_word(pdev->bus->self, PCI_EXP_LNKCTL,
+ state);
}
#endif
static void e1000e_disable_aspm(struct pci_dev *pdev, u16 state)
@@ -6486,7 +6475,7 @@ static void __devexit e1000_remove(struct pci_dev *pdev)
}
/* PCI Error Recovery (ERS) */
-static struct pci_error_handlers e1000_err_handler = {
+static const struct pci_error_handlers e1000_err_handler = {
.error_detected = e1000_io_error_detected,
.slot_reset = e1000_io_slot_reset,
.resume = e1000_io_resume,
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index 48cc4fb1a307..f88c822e57a6 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -217,7 +217,7 @@ static pci_ers_result_t igb_io_error_detected(struct pci_dev *,
static pci_ers_result_t igb_io_slot_reset(struct pci_dev *);
static void igb_io_resume(struct pci_dev *);
-static struct pci_error_handlers igb_err_handler = {
+static const struct pci_error_handlers igb_err_handler = {
.error_detected = igb_io_error_detected,
.slot_reset = igb_io_slot_reset,
.resume = igb_io_resume,
@@ -6538,28 +6538,20 @@ static int igb_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
s32 igb_read_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)
{
struct igb_adapter *adapter = hw->back;
- u16 cap_offset;
- cap_offset = adapter->pdev->pcie_cap;
- if (!cap_offset)
+ if (pcie_capability_read_word(adapter->pdev, reg, value))
return -E1000_ERR_CONFIG;
- pci_read_config_word(adapter->pdev, cap_offset + reg, value);
-
return 0;
}
s32 igb_write_pcie_cap_reg(struct e1000_hw *hw, u32 reg, u16 *value)
{
struct igb_adapter *adapter = hw->back;
- u16 cap_offset;
- cap_offset = adapter->pdev->pcie_cap;
- if (!cap_offset)
+ if (pcie_capability_write_word(adapter->pdev, reg, *value))
return -E1000_ERR_CONFIG;
- pci_write_config_word(adapter->pdev, cap_offset + reg, *value);
-
return 0;
}
diff --git a/drivers/net/ethernet/intel/igbvf/netdev.c b/drivers/net/ethernet/intel/igbvf/netdev.c
index 0696abfe9944..0ac11f527a84 100644
--- a/drivers/net/ethernet/intel/igbvf/netdev.c
+++ b/drivers/net/ethernet/intel/igbvf/netdev.c
@@ -2833,7 +2833,7 @@ static void __devexit igbvf_remove(struct pci_dev *pdev)
}
/* PCI Error Recovery (ERS) */
-static struct pci_error_handlers igbvf_err_handler = {
+static const struct pci_error_handlers igbvf_err_handler = {
.error_detected = igbvf_io_error_detected,
.slot_reset = igbvf_io_slot_reset,
.resume = igbvf_io_resume,
diff --git a/drivers/net/ethernet/intel/ixgb/ixgb_main.c b/drivers/net/ethernet/intel/ixgb/ixgb_main.c
index d05fc95befc5..d99a2d51b948 100644
--- a/drivers/net/ethernet/intel/ixgb/ixgb_main.c
+++ b/drivers/net/ethernet/intel/ixgb/ixgb_main.c
@@ -115,7 +115,7 @@ static pci_ers_result_t ixgb_io_error_detected (struct pci_dev *pdev,
static pci_ers_result_t ixgb_io_slot_reset (struct pci_dev *pdev);
static void ixgb_io_resume (struct pci_dev *pdev);
-static struct pci_error_handlers ixgb_err_handler = {
+static const struct pci_error_handlers ixgb_err_handler = {
.error_detected = ixgb_io_error_detected,
.slot_reset = ixgb_io_slot_reset,
.resume = ixgb_io_resume,
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
index 4326f74f7137..ee61819d6088 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
@@ -7527,7 +7527,7 @@ static pci_ers_result_t ixgbe_io_error_detected(struct pci_dev *pdev,
goto skip_bad_vf_detection;
bdev = pdev->bus->self;
- while (bdev && (bdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT))
+ while (bdev && (pci_pcie_type(bdev) != PCI_EXP_TYPE_ROOT_PORT))
bdev = bdev->bus->self;
if (!bdev)
@@ -7677,7 +7677,7 @@ static void ixgbe_io_resume(struct pci_dev *pdev)
netif_device_attach(netdev);
}
-static struct pci_error_handlers ixgbe_err_handler = {
+static const struct pci_error_handlers ixgbe_err_handler = {
.error_detected = ixgbe_io_error_detected,
.slot_reset = ixgbe_io_slot_reset,
.resume = ixgbe_io_resume,
diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
index 60ef64587412..6647383c4ddc 100644
--- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
+++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c
@@ -3256,7 +3256,7 @@ static void ixgbevf_io_resume(struct pci_dev *pdev)
}
/* PCI Error Recovery (ERS) */
-static struct pci_error_handlers ixgbevf_err_handler = {
+static const struct pci_error_handlers ixgbevf_err_handler = {
.error_detected = ixgbevf_io_error_detected,
.slot_reset = ixgbevf_io_slot_reset,
.resume = ixgbevf_io_resume,
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c
index daf417923661..31d02649be41 100644
--- a/drivers/net/ethernet/mellanox/mlx4/icm.c
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.c
@@ -227,9 +227,10 @@ int mlx4_UNMAP_ICM_AUX(struct mlx4_dev *dev)
MLX4_CMD_TIME_CLASS_B, MLX4_CMD_NATIVE);
}
-int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
+int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj)
{
- int i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
+ u32 i = (obj & (table->num_obj - 1)) /
+ (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
int ret = 0;
mutex_lock(&table->mutex);
@@ -262,16 +263,18 @@ out:
return ret;
}
-void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
+void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj)
{
- int i;
+ u32 i;
+ u64 offset;
i = (obj & (table->num_obj - 1)) / (MLX4_TABLE_CHUNK_SIZE / table->obj_size);
mutex_lock(&table->mutex);
if (--table->icm[i]->refcount == 0) {
- mlx4_UNMAP_ICM(dev, table->virt + i * MLX4_TABLE_CHUNK_SIZE,
+ offset = (u64) i * MLX4_TABLE_CHUNK_SIZE;
+ mlx4_UNMAP_ICM(dev, table->virt + offset,
MLX4_TABLE_CHUNK_SIZE / MLX4_ICM_PAGE_SIZE);
mlx4_free_icm(dev, table->icm[i], table->coherent);
table->icm[i] = NULL;
@@ -280,9 +283,11 @@ void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj)
mutex_unlock(&table->mutex);
}
-void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_handle)
+void *mlx4_table_find(struct mlx4_icm_table *table, u32 obj,
+ dma_addr_t *dma_handle)
{
- int idx, offset, dma_offset, i;
+ int offset, dma_offset, i;
+ u64 idx;
struct mlx4_icm_chunk *chunk;
struct mlx4_icm *icm;
struct page *page = NULL;
@@ -292,7 +297,7 @@ void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_han
mutex_lock(&table->mutex);
- idx = (obj & (table->num_obj - 1)) * table->obj_size;
+ idx = (u64) (obj & (table->num_obj - 1)) * table->obj_size;
icm = table->icm[idx / MLX4_TABLE_CHUNK_SIZE];
dma_offset = offset = idx % MLX4_TABLE_CHUNK_SIZE;
@@ -326,10 +331,11 @@ out:
}
int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end)
+ u32 start, u32 end)
{
int inc = MLX4_TABLE_CHUNK_SIZE / table->obj_size;
- int i, err;
+ int err;
+ u32 i;
for (i = start; i <= end; i += inc) {
err = mlx4_table_get(dev, table, i);
@@ -349,9 +355,9 @@ fail:
}
void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end)
+ u32 start, u32 end)
{
- int i;
+ u32 i;
for (i = start; i <= end; i += MLX4_TABLE_CHUNK_SIZE / table->obj_size)
mlx4_table_put(dev, table, i);
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.h b/drivers/net/ethernet/mellanox/mlx4/icm.h
index a67744f53506..dee67fa39107 100644
--- a/drivers/net/ethernet/mellanox/mlx4/icm.h
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.h
@@ -71,17 +71,17 @@ struct mlx4_icm *mlx4_alloc_icm(struct mlx4_dev *dev, int npages,
gfp_t gfp_mask, int coherent);
void mlx4_free_icm(struct mlx4_dev *dev, struct mlx4_icm *icm, int coherent);
-int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj);
-void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, int obj);
+int mlx4_table_get(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj);
+void mlx4_table_put(struct mlx4_dev *dev, struct mlx4_icm_table *table, u32 obj);
int mlx4_table_get_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end);
+ u32 start, u32 end);
void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- int start, int end);
+ u32 start, u32 end);
int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table,
u64 virt, int obj_size, u32 nobj, int reserved,
int use_lowmem, int use_coherent);
void mlx4_cleanup_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table);
-void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_handle);
+void *mlx4_table_find(struct mlx4_icm_table *table, u32 obj, dma_addr_t *dma_handle);
static inline void mlx4_icm_first(struct mlx4_icm *icm,
struct mlx4_icm_iter *iter)
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 2f816c6aed72..dd6ea942625c 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -2300,7 +2300,7 @@ static pci_ers_result_t mlx4_pci_slot_reset(struct pci_dev *pdev)
return ret ? PCI_ERS_RESULT_DISCONNECT : PCI_ERS_RESULT_RECOVERED;
}
-static struct pci_error_handlers mlx4_err_handler = {
+static const struct pci_error_handlers mlx4_err_handler = {
.error_detected = mlx4_pci_err_detected,
.slot_reset = mlx4_pci_slot_reset,
};
diff --git a/drivers/net/ethernet/mellanox/mlx4/reset.c b/drivers/net/ethernet/mellanox/mlx4/reset.c
index 11e7c1cb99bf..dd1b5093d8b1 100644
--- a/drivers/net/ethernet/mellanox/mlx4/reset.c
+++ b/drivers/net/ethernet/mellanox/mlx4/reset.c
@@ -141,16 +141,16 @@ int mlx4_reset(struct mlx4_dev *dev)
/* Now restore the PCI headers */
if (pcie_cap) {
devctl = hca_header[(pcie_cap + PCI_EXP_DEVCTL) / 4];
- if (pci_write_config_word(dev->pdev, pcie_cap + PCI_EXP_DEVCTL,
- devctl)) {
+ if (pcie_capability_write_word(dev->pdev, PCI_EXP_DEVCTL,
+ devctl)) {
err = -ENODEV;
mlx4_err(dev, "Couldn't restore HCA PCI Express "
"Device Control register, aborting.\n");
goto out;
}
linkctl = hca_header[(pcie_cap + PCI_EXP_LNKCTL) / 4];
- if (pci_write_config_word(dev->pdev, pcie_cap + PCI_EXP_LNKCTL,
- linkctl)) {
+ if (pcie_capability_write_word(dev->pdev, PCI_EXP_LNKCTL,
+ linkctl)) {
err = -ENODEV;
mlx4_err(dev, "Couldn't restore HCA PCI Express "
"Link control register, aborting.\n");
diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
index fa85cf1353fd..83516e3369c9 100644
--- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
+++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c
@@ -1078,22 +1078,16 @@ static int myri10ge_reset(struct myri10ge_priv *mgp)
#ifdef CONFIG_MYRI10GE_DCA
static int myri10ge_toggle_relaxed(struct pci_dev *pdev, int on)
{
- int ret, cap, err;
+ int ret;
u16 ctl;
- cap = pci_pcie_cap(pdev);
- if (!cap)
- return 0;
-
- err = pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl);
- if (err)
- return 0;
+ pcie_capability_read_word(pdev, PCI_EXP_DEVCTL, &ctl);
ret = (ctl & PCI_EXP_DEVCTL_RELAX_EN) >> 4;
if (ret != on) {
ctl &= ~PCI_EXP_DEVCTL_RELAX_EN;
ctl |= (on << 4);
- pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl);
+ pcie_capability_write_word(pdev, PCI_EXP_DEVCTL, ctl);
}
return ret;
}
@@ -3192,18 +3186,13 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp)
struct device *dev = &mgp->pdev->dev;
int cap;
unsigned err_cap;
- u16 val;
- u8 ext_type;
int ret;
if (!myri10ge_ecrc_enable || !bridge)
return;
/* check that the bridge is a root port */
- cap = pci_pcie_cap(bridge);
- pci_read_config_word(bridge, cap + PCI_CAP_FLAGS, &val);
- ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4;
- if (ext_type != PCI_EXP_TYPE_ROOT_PORT) {
+ if (pci_pcie_type(bridge) != PCI_EXP_TYPE_ROOT_PORT) {
if (myri10ge_ecrc_enable > 1) {
struct pci_dev *prev_bridge, *old_bridge = bridge;
@@ -3218,11 +3207,8 @@ static void myri10ge_enable_ecrc(struct myri10ge_priv *mgp)
" to force ECRC\n");
return;
}
- cap = pci_pcie_cap(bridge);
- pci_read_config_word(bridge,
- cap + PCI_CAP_FLAGS, &val);
- ext_type = (val & PCI_EXP_FLAGS_TYPE) >> 4;
- } while (ext_type != PCI_EXP_TYPE_ROOT_PORT);
+ } while (pci_pcie_type(bridge) !=
+ PCI_EXP_TYPE_ROOT_PORT);
dev_info(dev,
"Forcing ECRC on non-root port %s"
@@ -3335,11 +3321,10 @@ static void myri10ge_select_firmware(struct myri10ge_priv *mgp)
int overridden = 0;
if (myri10ge_force_firmware == 0) {
- int link_width, exp_cap;
+ int link_width;
u16 lnk;
- exp_cap = pci_pcie_cap(mgp->pdev);
- pci_read_config_word(mgp->pdev, exp_cap + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(mgp->pdev, PCI_EXP_LNKSTA, &lnk);
link_width = (lnk >> 4) & 0x3f;
/* Check to see if Link is less than 8 or if the
diff --git a/drivers/net/ethernet/neterion/s2io.c b/drivers/net/ethernet/neterion/s2io.c
index d958c2299372..de50547c187d 100644
--- a/drivers/net/ethernet/neterion/s2io.c
+++ b/drivers/net/ethernet/neterion/s2io.c
@@ -484,7 +484,7 @@ static DEFINE_PCI_DEVICE_TABLE(s2io_tbl) = {
MODULE_DEVICE_TABLE(pci, s2io_tbl);
-static struct pci_error_handlers s2io_err_handler = {
+static const struct pci_error_handlers s2io_err_handler = {
.error_detected = s2io_io_error_detected,
.slot_reset = s2io_io_slot_reset,
.resume = s2io_io_resume,
diff --git a/drivers/net/ethernet/neterion/vxge/vxge-config.c b/drivers/net/ethernet/neterion/vxge/vxge-config.c
index 32d06824fe3e..c2e420a84d22 100644
--- a/drivers/net/ethernet/neterion/vxge/vxge-config.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-config.c
@@ -757,7 +757,7 @@ __vxge_hw_verify_pci_e_info(struct __vxge_hw_device *hldev)
u16 lnk;
/* Get the negotiated link width and speed from PCI config space */
- pci_read_config_word(dev, dev->pcie_cap + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnk);
if ((lnk & PCI_EXP_LNKSTA_CLS) != 1)
return VXGE_HW_ERR_INVALID_PCI_INFO;
@@ -1982,7 +1982,7 @@ u16 vxge_hw_device_link_width_get(struct __vxge_hw_device *hldev)
struct pci_dev *dev = hldev->pdev;
u16 lnk;
- pci_read_config_word(dev, dev->pcie_cap + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(dev, PCI_EXP_LNKSTA, &lnk);
return (lnk & VXGE_HW_PCI_EXP_LNKCAP_LNK_WIDTH) >> 4;
}
diff --git a/drivers/net/ethernet/neterion/vxge/vxge-main.c b/drivers/net/ethernet/neterion/vxge/vxge-main.c
index de2190443510..cfa71a30dc8d 100644
--- a/drivers/net/ethernet/neterion/vxge/vxge-main.c
+++ b/drivers/net/ethernet/neterion/vxge/vxge-main.c
@@ -4799,7 +4799,7 @@ static void __devexit vxge_remove(struct pci_dev *pdev)
__LINE__);
}
-static struct pci_error_handlers vxge_err_handler = {
+static const struct pci_error_handlers vxge_err_handler = {
.error_detected = vxge_io_error_detected,
.slot_reset = vxge_io_slot_reset,
.resume = vxge_io_resume,
diff --git a/drivers/net/ethernet/octeon/octeon_mgmt.c b/drivers/net/ethernet/octeon/octeon_mgmt.c
index c42bbb16cdae..a688a2ddcfd6 100644
--- a/drivers/net/ethernet/octeon/octeon_mgmt.c
+++ b/drivers/net/ethernet/octeon/octeon_mgmt.c
@@ -722,10 +722,8 @@ static int octeon_mgmt_init_phy(struct net_device *netdev)
octeon_mgmt_adjust_link, 0,
PHY_INTERFACE_MODE_MII);
- if (IS_ERR(p->phydev)) {
- p->phydev = NULL;
+ if (!p->phydev)
return -1;
- }
phy_start_aneg(p->phydev);
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 feb85d56c750..b2a94d02a521 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
@@ -2795,7 +2795,7 @@ static const struct dev_pm_ops pch_gbe_pm_ops = {
};
#endif
-static struct pci_error_handlers pch_gbe_err_handler = {
+static const struct pci_error_handlers pch_gbe_err_handler = {
.error_detected = pch_gbe_io_error_detected,
.slot_reset = pch_gbe_io_slot_reset,
.resume = pch_gbe_io_resume
diff --git a/drivers/net/ethernet/pasemi/pasemi_mac.c b/drivers/net/ethernet/pasemi/pasemi_mac.c
index e559dfa06d6a..6fa74d530e44 100644
--- a/drivers/net/ethernet/pasemi/pasemi_mac.c
+++ b/drivers/net/ethernet/pasemi/pasemi_mac.c
@@ -1101,9 +1101,9 @@ static int pasemi_mac_phy_init(struct net_device *dev)
phydev = of_phy_connect(dev, phy_dn, &pasemi_adjust_link, 0,
PHY_INTERFACE_MODE_SGMII);
- if (IS_ERR(phydev)) {
+ if (!phydev) {
printk(KERN_ERR "%s: Could not attach to phy\n", dev->name);
- return PTR_ERR(phydev);
+ return -ENODEV;
}
mac->phydev = phydev;
diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
index 342b3a79bd0f..df450616ab37 100644
--- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
+++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c
@@ -1378,11 +1378,15 @@ static void netxen_mask_aer_correctable(struct netxen_adapter *adapter)
struct pci_dev *root = pdev->bus->self;
u32 aer_pos;
+ /* root bus? */
+ if (!root)
+ return;
+
if (adapter->ahw.board_type != NETXEN_BRDTYPE_P3_4_GB_MM &&
adapter->ahw.board_type != NETXEN_BRDTYPE_P3_10G_TP)
return;
- if (root->pcie_type != PCI_EXP_TYPE_ROOT_PORT)
+ if (pci_pcie_type(root) != PCI_EXP_TYPE_ROOT_PORT)
return;
aer_pos = pci_find_ext_capability(root, PCI_EXT_CAP_ID_ERR);
@@ -3336,7 +3340,7 @@ netxen_free_vlan_ip_list(struct netxen_adapter *adapter)
{ }
#endif
-static struct pci_error_handlers netxen_err_handler = {
+static const struct pci_error_handlers netxen_err_handler = {
.error_detected = netxen_io_error_detected,
.slot_reset = netxen_io_slot_reset,
.resume = netxen_io_resume,
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
index b8ead696141e..2a179d087207 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
@@ -15,7 +15,7 @@ qlcnic_poll_rsp(struct qlcnic_adapter *adapter)
do {
/* give atleast 1ms for firmware to respond */
- msleep(1);
+ mdelay(1);
if (++timeout > QLCNIC_OS_CRB_RETRY_COUNT)
return QLCNIC_CDRP_RSP_TIMEOUT;
@@ -601,7 +601,7 @@ void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)
qlcnic_fw_cmd_destroy_tx_ctx(adapter);
/* Allow dma queues to drain after context reset */
- msleep(20);
+ mdelay(20);
}
}
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
index 212c12193275..473ce134ca63 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c
@@ -4522,7 +4522,7 @@ static void
qlcnic_restore_indev_addr(struct net_device *dev, unsigned long event)
{ }
#endif
-static struct pci_error_handlers qlcnic_err_handler = {
+static const struct pci_error_handlers qlcnic_err_handler = {
.error_detected = qlcnic_io_error_detected,
.slot_reset = qlcnic_io_slot_reset,
.resume = qlcnic_io_resume,
diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
index b53a3b60b648..b262d6156816 100644
--- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c
+++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c
@@ -4847,7 +4847,7 @@ static void qlge_io_resume(struct pci_dev *pdev)
netif_device_attach(ndev);
}
-static struct pci_error_handlers qlge_err_handler = {
+static const struct pci_error_handlers qlge_err_handler = {
.error_detected = qlge_io_error_detected,
.slot_reset = qlge_io_slot_reset,
.resume = qlge_io_resume,
diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c
index b47d5b35024e..a7cc56007b33 100644
--- a/drivers/net/ethernet/realtek/r8169.c
+++ b/drivers/net/ethernet/realtek/r8169.c
@@ -833,15 +833,8 @@ static void rtl_unlock_work(struct rtl8169_private *tp)
static void rtl_tx_performance_tweak(struct pci_dev *pdev, u16 force)
{
- int cap = pci_pcie_cap(pdev);
-
- if (cap) {
- u16 ctl;
-
- pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl);
- ctl = (ctl & ~PCI_EXP_DEVCTL_READRQ) | force;
- pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl);
- }
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_READRQ, force);
}
struct rtl_cond {
@@ -4739,28 +4732,14 @@ static void rtl_ephy_init(struct rtl8169_private *tp, const struct ephy_info *e,
static void rtl_disable_clock_request(struct pci_dev *pdev)
{
- int cap = pci_pcie_cap(pdev);
-
- if (cap) {
- u16 ctl;
-
- pci_read_config_word(pdev, cap + PCI_EXP_LNKCTL, &ctl);
- ctl &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
- pci_write_config_word(pdev, cap + PCI_EXP_LNKCTL, ctl);
- }
+ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
}
static void rtl_enable_clock_request(struct pci_dev *pdev)
{
- int cap = pci_pcie_cap(pdev);
-
- if (cap) {
- u16 ctl;
-
- pci_read_config_word(pdev, cap + PCI_EXP_LNKCTL, &ctl);
- ctl |= PCI_EXP_LNKCTL_CLKREQ_EN;
- pci_write_config_word(pdev, cap + PCI_EXP_LNKCTL, ctl);
- }
+ pcie_capability_set_word(pdev, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
}
#define R8168_CPCMD_QUIRK_MASK (\
@@ -5405,14 +5384,9 @@ static void rtl_hw_start_8101(struct net_device *dev)
tp->event_slow &= ~RxFIFOOver;
if (tp->mac_version == RTL_GIGA_MAC_VER_13 ||
- tp->mac_version == RTL_GIGA_MAC_VER_16) {
- int cap = pci_pcie_cap(pdev);
-
- if (cap) {
- pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL,
- PCI_EXP_DEVCTL_NOSNOOP_EN);
- }
- }
+ tp->mac_version == RTL_GIGA_MAC_VER_16)
+ pcie_capability_set_word(pdev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_NOSNOOP_EN);
RTL_W8(Cfg9346, Cfg9346_Unlock);
diff --git a/drivers/net/ethernet/seeq/ether3.c b/drivers/net/ethernet/seeq/ether3.c
index df808ac8cb65..6a40dd03a32f 100644
--- a/drivers/net/ethernet/seeq/ether3.c
+++ b/drivers/net/ethernet/seeq/ether3.c
@@ -99,13 +99,13 @@ typedef enum {
* The SEEQ8005 doesn't like us writing to its registers
* too quickly.
*/
-static inline void ether3_outb(int v, const void __iomem *r)
+static inline void ether3_outb(int v, void __iomem *r)
{
writeb(v, r);
udelay(1);
}
-static inline void ether3_outw(int v, const void __iomem *r)
+static inline void ether3_outw(int v, void __iomem *r)
{
writew(v, r);
udelay(1);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index c136162e6473..3be88331d17a 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -1066,7 +1066,7 @@ static int stmmac_open(struct net_device *dev)
} else
priv->tm->enable = 1;
#endif
- clk_enable(priv->stmmac_clk);
+ clk_prepare_enable(priv->stmmac_clk);
stmmac_check_ether_addr(priv);
@@ -1188,7 +1188,7 @@ open_error:
if (priv->phydev)
phy_disconnect(priv->phydev);
- clk_disable(priv->stmmac_clk);
+ clk_disable_unprepare(priv->stmmac_clk);
return ret;
}
@@ -1246,7 +1246,7 @@ static int stmmac_release(struct net_device *dev)
#ifdef CONFIG_STMMAC_DEBUG_FS
stmmac_exit_fs();
#endif
- clk_disable(priv->stmmac_clk);
+ clk_disable_unprepare(priv->stmmac_clk);
return 0;
}
@@ -2178,7 +2178,7 @@ int stmmac_suspend(struct net_device *ndev)
else {
stmmac_set_mac(priv->ioaddr, false);
/* Disable clock in case of PWM is off */
- clk_disable(priv->stmmac_clk);
+ clk_disable_unprepare(priv->stmmac_clk);
}
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
@@ -2203,7 +2203,7 @@ int stmmac_resume(struct net_device *ndev)
priv->hw->mac->pmt(priv->ioaddr, 0);
else
/* enable the clk prevously disabled */
- clk_enable(priv->stmmac_clk);
+ clk_prepare_enable(priv->stmmac_clk);
netif_device_attach(ndev);
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
index 2a0e1abde7e7..4ccd4e2977b7 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_timer.c
@@ -97,19 +97,19 @@ static struct clk *timer_clock;
static void stmmac_tmu_start(unsigned int new_freq)
{
clk_set_rate(timer_clock, new_freq);
- clk_enable(timer_clock);
+ clk_prepare_enable(timer_clock);
}
static void stmmac_tmu_stop(void)
{
- clk_disable(timer_clock);
+ clk_disable_unprepare(timer_clock);
}
int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm)
{
timer_clock = clk_get(NULL, TMU_CHANNEL);
- if (timer_clock == NULL)
+ if (IS_ERR(timer_clock))
return -1;
if (tmu2_register_user(stmmac_timer_handler, (void *)dev) < 0) {
@@ -126,7 +126,7 @@ int stmmac_open_ext_timer(struct net_device *dev, struct stmmac_timer *tm)
int stmmac_close_ext_timer(void)
{
- clk_disable(timer_clock);
+ clk_disable_unprepare(timer_clock);
tmu2_unregister_user();
clk_put(timer_clock);
return 0;
diff --git a/drivers/net/ethernet/sun/niu.c b/drivers/net/ethernet/sun/niu.c
index c2a0fe393267..3208dca66758 100644
--- a/drivers/net/ethernet/sun/niu.c
+++ b/drivers/net/ethernet/sun/niu.c
@@ -9762,9 +9762,8 @@ static int __devinit niu_pci_init_one(struct pci_dev *pdev,
union niu_parent_id parent_id;
struct net_device *dev;
struct niu *np;
- int err, pos;
+ int err;
u64 dma_mask;
- u16 val16;
niu_driver_version();
@@ -9787,8 +9786,7 @@ static int __devinit niu_pci_init_one(struct pci_dev *pdev,
goto err_out_disable_pdev;
}
- pos = pci_pcie_cap(pdev);
- if (pos <= 0) {
+ if (!pci_is_pcie(pdev)) {
dev_err(&pdev->dev, "Cannot find PCI Express capability, aborting\n");
goto err_out_free_res;
}
@@ -9813,14 +9811,11 @@ static int __devinit niu_pci_init_one(struct pci_dev *pdev,
goto err_out_free_dev;
}
- pci_read_config_word(pdev, pos + PCI_EXP_DEVCTL, &val16);
- val16 &= ~PCI_EXP_DEVCTL_NOSNOOP_EN;
- val16 |= (PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE |
- PCI_EXP_DEVCTL_URRE |
- PCI_EXP_DEVCTL_RELAX_EN);
- pci_write_config_word(pdev, pos + PCI_EXP_DEVCTL, val16);
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_NOSNOOP_EN,
+ PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE |
+ PCI_EXP_DEVCTL_RELAX_EN);
dma_mask = DMA_BIT_MASK(44);
err = pci_set_dma_mask(pdev, dma_mask);
diff --git a/drivers/net/irda/sh_sir.c b/drivers/net/irda/sh_sir.c
index 256eddf1f75a..795109425568 100644
--- a/drivers/net/irda/sh_sir.c
+++ b/drivers/net/irda/sh_sir.c
@@ -280,7 +280,7 @@ static int sh_sir_set_baudrate(struct sh_sir_self *self, u32 baudrate)
}
clk = clk_get(NULL, "irda_clk");
- if (!clk) {
+ if (IS_ERR(clk)) {
dev_err(dev, "can not get irda_clk\n");
return -EIO;
}
diff --git a/drivers/net/phy/bcm87xx.c b/drivers/net/phy/bcm87xx.c
index 2346b38b9837..799789518e87 100644
--- a/drivers/net/phy/bcm87xx.c
+++ b/drivers/net/phy/bcm87xx.c
@@ -229,3 +229,5 @@ static void __exit bcm87xx_exit(void)
ARRAY_SIZE(bcm87xx_driver));
}
module_exit(bcm87xx_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
index cf287e0eb408..2165d5fdb8c0 100644
--- a/drivers/net/phy/micrel.c
+++ b/drivers/net/phy/micrel.c
@@ -21,6 +21,12 @@
#include <linux/phy.h>
#include <linux/micrel_phy.h>
+/* Operation Mode Strap Override */
+#define MII_KSZPHY_OMSO 0x16
+#define KSZPHY_OMSO_B_CAST_OFF (1 << 9)
+#define KSZPHY_OMSO_RMII_OVERRIDE (1 << 1)
+#define KSZPHY_OMSO_MII_OVERRIDE (1 << 0)
+
/* general Interrupt control/status reg in vendor specific block. */
#define MII_KSZPHY_INTCS 0x1B
#define KSZPHY_INTCS_JABBER (1 << 15)
@@ -101,6 +107,13 @@ static int kszphy_config_init(struct phy_device *phydev)
return 0;
}
+static int ksz8021_config_init(struct phy_device *phydev)
+{
+ const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE;
+ phy_write(phydev, MII_KSZPHY_OMSO, val);
+ return 0;
+}
+
static int ks8051_config_init(struct phy_device *phydev)
{
int regval;
@@ -128,9 +141,22 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = ks8737_config_intr,
.driver = { .owner = THIS_MODULE,},
}, {
- .phy_id = PHY_ID_KS8041,
+ .phy_id = PHY_ID_KSZ8021,
+ .phy_id_mask = 0x00ffffff,
+ .name = "Micrel KSZ8021",
+ .features = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
+ SUPPORTED_Asym_Pause),
+ .flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+ .config_init = ksz8021_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .ack_interrupt = kszphy_ack_interrupt,
+ .config_intr = kszphy_config_intr,
+ .driver = { .owner = THIS_MODULE,},
+}, {
+ .phy_id = PHY_ID_KSZ8041,
.phy_id_mask = 0x00fffff0,
- .name = "Micrel KS8041",
+ .name = "Micrel KSZ8041",
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -141,9 +167,9 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = kszphy_config_intr,
.driver = { .owner = THIS_MODULE,},
}, {
- .phy_id = PHY_ID_KS8051,
+ .phy_id = PHY_ID_KSZ8051,
.phy_id_mask = 0x00fffff0,
- .name = "Micrel KS8051",
+ .name = "Micrel KSZ8051",
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause
| SUPPORTED_Asym_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -154,8 +180,8 @@ static struct phy_driver ksphy_driver[] = {
.config_intr = kszphy_config_intr,
.driver = { .owner = THIS_MODULE,},
}, {
- .phy_id = PHY_ID_KS8001,
- .name = "Micrel KS8001 or KS8721",
+ .phy_id = PHY_ID_KSZ8001,
+ .name = "Micrel KSZ8001 or KS8721",
.phy_id_mask = 0x00ffffff,
.features = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
.flags = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -201,10 +227,11 @@ MODULE_LICENSE("GPL");
static struct mdio_device_id __maybe_unused micrel_tbl[] = {
{ PHY_ID_KSZ9021, 0x000ffffe },
- { PHY_ID_KS8001, 0x00ffffff },
+ { PHY_ID_KSZ8001, 0x00ffffff },
{ PHY_ID_KS8737, 0x00fffff0 },
- { PHY_ID_KS8041, 0x00fffff0 },
- { PHY_ID_KS8051, 0x00fffff0 },
+ { PHY_ID_KSZ8021, 0x00ffffff },
+ { PHY_ID_KSZ8041, 0x00fffff0 },
+ { PHY_ID_KSZ8051, 0x00fffff0 },
{ }
};
diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
index 6d6192316b30..88e3991464e7 100644
--- a/drivers/net/phy/smsc.c
+++ b/drivers/net/phy/smsc.c
@@ -56,6 +56,32 @@ static int smsc_phy_config_init(struct phy_device *phydev)
return smsc_phy_ack_interrupt (phydev);
}
+static int lan87xx_config_init(struct phy_device *phydev)
+{
+ /*
+ * Make sure the EDPWRDOWN bit is NOT set. Setting this bit on
+ * LAN8710/LAN8720 PHY causes the PHY to misbehave, likely due
+ * to a bug on the chip.
+ *
+ * When the system is powered on with the network cable being
+ * disconnected all the way until after ifconfig ethX up is
+ * issued for the LAN port with this PHY, connecting the cable
+ * afterwards does not cause LINK change detection, while the
+ * expected behavior is the Link UP being detected.
+ */
+ int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
+ if (rc < 0)
+ return rc;
+
+ rc &= ~MII_LAN83C185_EDPWRDOWN;
+
+ rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc);
+ if (rc < 0)
+ return rc;
+
+ return smsc_phy_ack_interrupt(phydev);
+}
+
static int lan911x_config_init(struct phy_device *phydev)
{
return smsc_phy_ack_interrupt(phydev);
@@ -162,7 +188,7 @@ static struct phy_driver smsc_phy_driver[] = {
/* basic functions */
.config_aneg = genphy_config_aneg,
.read_status = genphy_read_status,
- .config_init = smsc_phy_config_init,
+ .config_init = lan87xx_config_init,
/* IRQ related */
.ack_interrupt = smsc_phy_ack_interrupt,
diff --git a/drivers/net/ppp/pppoe.c b/drivers/net/ppp/pppoe.c
index cbf7047decc0..20f31d0d1536 100644
--- a/drivers/net/ppp/pppoe.c
+++ b/drivers/net/ppp/pppoe.c
@@ -570,7 +570,7 @@ static int pppoe_release(struct socket *sock)
po = pppox_sk(sk);
- if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
+ if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) {
dev_put(po->pppoe_dev);
po->pppoe_dev = NULL;
}
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 341b65dbbcd3..f8cd61f449a4 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -848,7 +848,7 @@ static struct netpoll_info *team_netpoll_info(struct team *team)
}
#endif
-static void __team_port_change_check(struct team_port *port, bool linkup);
+static void __team_port_change_port_added(struct team_port *port, bool linkup);
static int team_port_add(struct team *team, struct net_device *port_dev)
{
@@ -948,7 +948,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
team_port_enable(team, port);
list_add_tail_rcu(&port->list, &team->port_list);
__team_compute_features(team);
- __team_port_change_check(port, !!netif_carrier_ok(port_dev));
+ __team_port_change_port_added(port, !!netif_carrier_ok(port_dev));
__team_options_change_check(team);
netdev_info(dev, "Port device %s added\n", portname);
@@ -983,6 +983,8 @@ err_set_mtu:
return err;
}
+static void __team_port_change_port_removed(struct team_port *port);
+
static int team_port_del(struct team *team, struct net_device *port_dev)
{
struct net_device *dev = team->dev;
@@ -999,8 +1001,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
__team_option_inst_mark_removed_port(team, port);
__team_options_change_check(team);
__team_option_inst_del_port(team, port);
- port->removed = true;
- __team_port_change_check(port, false);
+ __team_port_change_port_removed(port);
team_port_disable(team, port);
list_del_rcu(&port->list);
netdev_rx_handler_unregister(port_dev);
@@ -1652,8 +1653,8 @@ static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
&team_nl_family, 0, TEAM_CMD_NOOP);
- if (IS_ERR(hdr)) {
- err = PTR_ERR(hdr);
+ if (!hdr) {
+ err = -EMSGSIZE;
goto err_msg_put;
}
@@ -1847,8 +1848,8 @@ start_again:
hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags | NLM_F_MULTI,
TEAM_CMD_OPTIONS_GET);
- if (IS_ERR(hdr))
- return PTR_ERR(hdr);
+ if (!hdr)
+ return -EMSGSIZE;
if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
goto nla_put_failure;
@@ -2067,8 +2068,8 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
TEAM_CMD_PORT_LIST_GET);
- if (IS_ERR(hdr))
- return PTR_ERR(hdr);
+ if (!hdr)
+ return -EMSGSIZE;
if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
goto nla_put_failure;
@@ -2251,13 +2252,11 @@ static void __team_options_change_check(struct team *team)
}
/* rtnl lock is held */
-static void __team_port_change_check(struct team_port *port, bool linkup)
+
+static void __team_port_change_send(struct team_port *port, bool linkup)
{
int err;
- if (!port->removed && port->state.linkup == linkup)
- return;
-
port->changed = true;
port->state.linkup = linkup;
team_refresh_port_linkup(port);
@@ -2282,6 +2281,23 @@ send_event:
}
+static void __team_port_change_check(struct team_port *port, bool linkup)
+{
+ if (port->state.linkup != linkup)
+ __team_port_change_send(port, linkup);
+}
+
+static void __team_port_change_port_added(struct team_port *port, bool linkup)
+{
+ __team_port_change_send(port, linkup);
+}
+
+static void __team_port_change_port_removed(struct team_port *port)
+{
+ port->removed = true;
+ __team_port_change_send(port, false);
+}
+
static void team_port_change_check(struct team_port *port, bool linkup)
{
struct team *team = port->team;
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index 4fd48df6b989..32e31c5c5dc6 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -962,6 +962,10 @@ static const struct usb_device_id products [] = {
USB_DEVICE (0x2001, 0x3c05),
.driver_info = (unsigned long) &ax88772_info,
}, {
+ // DLink DUB-E100 H/W Ver C1
+ USB_DEVICE (0x2001, 0x1a02),
+ .driver_info = (unsigned long) &ax88772_info,
+}, {
// Linksys USB1000
USB_DEVICE (0x1737, 0x0039),
.driver_info = (unsigned long) &ax88178_info,
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index b1ba68f1a049..3543c9e57824 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -366,16 +366,20 @@ static const struct usb_device_id products[] = {
},
/* 2. Combined interface devices matching on class+protocol */
+ { /* Huawei E367 and possibly others in "Windows mode" */
+ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 7),
+ .driver_info = (unsigned long)&qmi_wwan_info,
+ },
{ /* Huawei E392, E398 and possibly others in "Windows mode" */
USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 17),
.driver_info = (unsigned long)&qmi_wwan_shared,
},
- { /* Pantech UML290 */
- USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff),
+ { /* Pantech UML290, P4200 and more */
+ USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff),
.driver_info = (unsigned long)&qmi_wwan_shared,
},
{ /* Pantech UML290 - newer firmware */
- USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff),
+ USB_VENDOR_AND_INTERFACE_INFO(0x106c, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff),
.driver_info = (unsigned long)&qmi_wwan_shared,
},
@@ -383,6 +387,7 @@ static const struct usb_device_id products[] = {
{QMI_FIXED_INTF(0x19d2, 0x0055, 1)}, /* ZTE (Vodafone) K3520-Z */
{QMI_FIXED_INTF(0x19d2, 0x0063, 4)}, /* ZTE (Vodafone) K3565-Z */
{QMI_FIXED_INTF(0x19d2, 0x0104, 4)}, /* ZTE (Vodafone) K4505-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x0157, 5)}, /* ZTE MF683 */
{QMI_FIXED_INTF(0x19d2, 0x0167, 4)}, /* ZTE MF820D */
{QMI_FIXED_INTF(0x19d2, 0x0326, 4)}, /* ZTE MF821D */
{QMI_FIXED_INTF(0x19d2, 0x1008, 4)}, /* ZTE (Vodafone) K3570-Z */
diff --git a/drivers/net/usb/smsc75xx.c b/drivers/net/usb/smsc75xx.c
index f5ab6e613ec8..376143e8a1aa 100644
--- a/drivers/net/usb/smsc75xx.c
+++ b/drivers/net/usb/smsc75xx.c
@@ -1253,6 +1253,7 @@ static struct usb_driver smsc75xx_driver = {
.probe = usbnet_probe,
.suspend = usbnet_suspend,
.resume = usbnet_resume,
+ .reset_resume = usbnet_resume,
.disconnect = usbnet_disconnect,
.disable_hub_initiated_lpm = 1,
};
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
index 2588848f4a82..d066f2516e47 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_eeprom.c
@@ -2982,6 +2982,10 @@ static u32 ath9k_hw_ar9300_get_eeprom(struct ath_hw *ah,
case EEP_RX_MASK:
return pBase->txrxMask & 0xf;
case EEP_PAPRD:
+ if (AR_SREV_9462(ah))
+ return false;
+ if (!ah->config.enable_paprd);
+ return false;
return !!(pBase->featureEnable & BIT(5));
case EEP_CHAIN_MASK_REDUCE:
return (pBase->miscConfiguration >> 0x3) & 0x1;
diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c
index 68b643c8943c..c8ef30127adb 100644
--- a/drivers/net/wireless/ath/ath9k/debug.c
+++ b/drivers/net/wireless/ath/ath9k/debug.c
@@ -1577,6 +1577,8 @@ int ath9k_init_debug(struct ath_hw *ah)
sc->debug.debugfs_phy, sc, &fops_tx_chainmask);
debugfs_create_file("disable_ani", S_IRUSR | S_IWUSR,
sc->debug.debugfs_phy, sc, &fops_disable_ani);
+ debugfs_create_bool("paprd", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
+ &sc->sc_ah->config.enable_paprd);
debugfs_create_file("regidx", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
sc, &fops_regidx);
debugfs_create_file("regval", S_IRUSR | S_IWUSR, sc->debug.debugfs_phy,
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 48af40151d23..4faf0a395876 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -2497,10 +2497,6 @@ int ath9k_hw_fill_cap_info(struct ath_hw *ah)
pCap->rx_status_len = sizeof(struct ar9003_rxs);
pCap->tx_desc_len = sizeof(struct ar9003_txc);
pCap->txs_len = sizeof(struct ar9003_txs);
- if (!ah->config.paprd_disable &&
- ah->eep_ops->get_eeprom(ah, EEP_PAPRD) &&
- !AR_SREV_9462(ah))
- pCap->hw_caps |= ATH9K_HW_CAP_PAPRD;
} else {
pCap->tx_desc_len = sizeof(struct ath_desc);
if (AR_SREV_9280_20(ah))
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 6599a75f01fe..de6968fc64f4 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -236,7 +236,6 @@ enum ath9k_hw_caps {
ATH9K_HW_CAP_LDPC = BIT(6),
ATH9K_HW_CAP_FASTCLOCK = BIT(7),
ATH9K_HW_CAP_SGI_20 = BIT(8),
- ATH9K_HW_CAP_PAPRD = BIT(9),
ATH9K_HW_CAP_ANT_DIV_COMB = BIT(10),
ATH9K_HW_CAP_2GHZ = BIT(11),
ATH9K_HW_CAP_5GHZ = BIT(12),
@@ -287,12 +286,12 @@ struct ath9k_ops_config {
u8 pcie_clock_req;
u32 pcie_waen;
u8 analog_shiftreg;
- u8 paprd_disable;
u32 ofdm_trig_low;
u32 ofdm_trig_high;
u32 cck_trig_high;
u32 cck_trig_low;
u32 enable_ani;
+ u32 enable_paprd;
int serialize_regmode;
bool rx_intr_mitigation;
bool tx_intr_mitigation;
diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c
index 825a29cc9313..7b88b9c39ccd 100644
--- a/drivers/net/wireless/ath/ath9k/link.c
+++ b/drivers/net/wireless/ath/ath9k/link.c
@@ -423,7 +423,7 @@ set_timer:
cal_interval = min(cal_interval, (u32)short_cal_interval);
mod_timer(&common->ani.timer, jiffies + msecs_to_jiffies(cal_interval));
- if ((sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_PAPRD) && ah->caldata) {
+ if (ah->eep_ops->get_eeprom(ah, EEP_PAPRD) && ah->caldata) {
if (!ah->caldata->paprd_done)
ieee80211_queue_work(sc->hw, &sc->paprd_work);
else if (!ah->paprd_table_write_done)
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index a978984d78a5..ef11dc639461 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -113,41 +113,32 @@ static void ath_pci_aspm_init(struct ath_common *common)
struct ath_hw *ah = sc->sc_ah;
struct pci_dev *pdev = to_pci_dev(sc->dev);
struct pci_dev *parent;
- int pos;
- u8 aspm;
+ u16 aspm;
if (!ah->is_pciexpress)
return;
- pos = pci_pcie_cap(pdev);
- if (!pos)
- return;
-
parent = pdev->bus->self;
if (!parent)
return;
if (ath9k_hw_get_btcoex_scheme(ah) != ATH_BTCOEX_CFG_NONE) {
/* Bluetooth coexistance requires disabling ASPM. */
- pci_read_config_byte(pdev, pos + PCI_EXP_LNKCTL, &aspm);
- aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
- pci_write_config_byte(pdev, pos + PCI_EXP_LNKCTL, aspm);
+ pcie_capability_clear_word(pdev, PCI_EXP_LNKCTL,
+ PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
/*
* Both upstream and downstream PCIe components should
* have the same ASPM settings.
*/
- pos = pci_pcie_cap(parent);
- pci_read_config_byte(parent, pos + PCI_EXP_LNKCTL, &aspm);
- aspm &= ~(PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
- pci_write_config_byte(parent, pos + PCI_EXP_LNKCTL, aspm);
+ pcie_capability_clear_word(parent, PCI_EXP_LNKCTL,
+ PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1);
ath_info(common, "Disabling ASPM since BTCOEX is enabled\n");
return;
}
- pos = pci_pcie_cap(parent);
- pci_read_config_byte(parent, pos + PCI_EXP_LNKCTL, &aspm);
+ pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &aspm);
if (aspm & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) {
ah->aspm_enabled = true;
/* Initialize PCIe PM and SERDES registers. */
diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig
index 3876c7ea54f4..7a28d21ac389 100644
--- a/drivers/net/wireless/b43/Kconfig
+++ b/drivers/net/wireless/b43/Kconfig
@@ -34,8 +34,8 @@ config B43_BCMA
config B43_BCMA_EXTRA
bool "Hardware support that overlaps with the brcmsmac driver"
depends on B43_BCMA
- default n if BRCMSMAC || BRCMSMAC_MODULE
- default y
+ default n if BRCMSMAC
+ default y
config B43_SSB
bool
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
index 49765d34b4e0..7c4ee72f9d56 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -638,6 +638,8 @@ static int brcmf_sdio_pd_probe(struct platform_device *pdev)
oobirq_entry = kzalloc(sizeof(struct brcmf_sdio_oobirq),
GFP_KERNEL);
+ if (!oobirq_entry)
+ return -ENOMEM;
oobirq_entry->irq = res->start;
oobirq_entry->flags = res->flags & IRQF_TRIGGER_MASK;
list_add_tail(&oobirq_entry->list, &oobirq_lh);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
index 2621dd3d7dcd..6f70953f0bad 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
@@ -764,8 +764,11 @@ static void brcmf_c_arp_offload_set(struct brcmf_pub *drvr, int arp_mode)
{
char iovbuf[32];
int retcode;
+ __le32 arp_mode_le;
- brcmf_c_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf));
+ arp_mode_le = cpu_to_le32(arp_mode);
+ brcmf_c_mkiovar("arp_ol", (char *)&arp_mode_le, 4, iovbuf,
+ sizeof(iovbuf));
retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR,
iovbuf, sizeof(iovbuf));
retcode = retcode >= 0 ? 0 : retcode;
@@ -781,8 +784,11 @@ static void brcmf_c_arp_offload_enable(struct brcmf_pub *drvr, int arp_enable)
{
char iovbuf[32];
int retcode;
+ __le32 arp_enable_le;
- brcmf_c_mkiovar("arpoe", (char *)&arp_enable, 4,
+ arp_enable_le = cpu_to_le32(arp_enable);
+
+ brcmf_c_mkiovar("arpoe", (char *)&arp_enable_le, 4,
iovbuf, sizeof(iovbuf));
retcode = brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR,
iovbuf, sizeof(iovbuf));
@@ -800,10 +806,10 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
char iovbuf[BRCMF_EVENTING_MASK_LEN + 12]; /* Room for
"event_msgs" + '\0' + bitvec */
char buf[128], *ptr;
- u32 roaming = 1;
- uint bcn_timeout = 3;
- int scan_assoc_time = 40;
- int scan_unassoc_time = 40;
+ __le32 roaming_le = cpu_to_le32(1);
+ __le32 bcn_timeout_le = cpu_to_le32(3);
+ __le32 scan_assoc_time_le = cpu_to_le32(40);
+ __le32 scan_unassoc_time_le = cpu_to_le32(40);
int i;
struct brcmf_bus_dcmd *cmdlst;
struct list_head *cur, *q;
@@ -829,14 +835,14 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
/* Setup timeout if Beacons are lost and roam is off to report
link down */
- brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf,
+ brcmf_c_mkiovar("bcn_timeout", (char *)&bcn_timeout_le, 4, iovbuf,
sizeof(iovbuf));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
sizeof(iovbuf));
/* Enable/Disable build-in roaming to allowed ext supplicant to take
of romaing */
- brcmf_c_mkiovar("roam_off", (char *)&roaming, 4,
+ brcmf_c_mkiovar("roam_off", (char *)&roaming_le, 4,
iovbuf, sizeof(iovbuf));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
sizeof(iovbuf));
@@ -848,9 +854,9 @@ int brcmf_c_preinit_dcmds(struct brcmf_pub *drvr)
sizeof(iovbuf));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_CHANNEL_TIME,
- (char *)&scan_assoc_time, sizeof(scan_assoc_time));
+ (char *)&scan_assoc_time_le, sizeof(scan_assoc_time_le));
brcmf_proto_cdc_set_dcmd(drvr, 0, BRCMF_C_SET_SCAN_UNASSOC_TIME,
- (char *)&scan_unassoc_time, sizeof(scan_unassoc_time));
+ (char *)&scan_unassoc_time_le, sizeof(scan_unassoc_time_le));
/* Set and enable ARP offload feature */
brcmf_c_arp_offload_set(drvr, BRCMF_ARPOL_MODE);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
index c36e92312443..50b5553b6964 100644
--- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -500,8 +500,10 @@ static void wl_iscan_prep(struct brcmf_scan_params_le *params_le,
params_le->active_time = cpu_to_le32(-1);
params_le->passive_time = cpu_to_le32(-1);
params_le->home_time = cpu_to_le32(-1);
- if (ssid && ssid->SSID_len)
- memcpy(&params_le->ssid_le, ssid, sizeof(struct brcmf_ssid));
+ if (ssid && ssid->SSID_len) {
+ params_le->ssid_le.SSID_len = cpu_to_le32(ssid->SSID_len);
+ memcpy(&params_le->ssid_le.SSID, ssid->SSID, ssid->SSID_len);
+ }
}
static s32
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
index 7ed7d7577024..64a48f06d68b 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
@@ -77,7 +77,7 @@
NL80211_RRF_NO_IBSS)
static const struct ieee80211_regdomain brcms_regdom_x2 = {
- .n_reg_rules = 7,
+ .n_reg_rules = 6,
.alpha2 = "X2",
.reg_rules = {
BRCM_2GHZ_2412_2462,
diff --git a/drivers/net/wireless/iwlegacy/common.h b/drivers/net/wireless/iwlegacy/common.h
index 5f5017767b99..724682669060 100644
--- a/drivers/net/wireless/iwlegacy/common.h
+++ b/drivers/net/wireless/iwlegacy/common.h
@@ -1832,10 +1832,8 @@ int il_enqueue_hcmd(struct il_priv *il, struct il_host_cmd *cmd);
static inline u16
il_pcie_link_ctl(struct il_priv *il)
{
- int pos;
u16 pci_lnk_ctl;
- pos = pci_pcie_cap(il->pci_dev);
- pci_read_config_word(il->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
+ pcie_capability_read_word(il->pci_dev, PCI_EXP_LNKCTL, &pci_lnk_ctl);
return pci_lnk_ctl;
}
diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c
index 1e86ea2266d4..063ecaff5b56 100644
--- a/drivers/net/wireless/iwlwifi/pcie/trans.c
+++ b/drivers/net/wireless/iwlwifi/pcie/trans.c
@@ -675,13 +675,10 @@ static void iwl_set_pwr_vmain(struct iwl_trans *trans)
static u16 iwl_pciexp_link_ctrl(struct iwl_trans *trans)
{
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
- int pos;
u16 pci_lnk_ctl;
- struct pci_dev *pci_dev = trans_pcie->pci_dev;
-
- pos = pci_pcie_cap(pci_dev);
- pci_read_config_word(pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
+ pcie_capability_read_word(trans_pcie->pci_dev, PCI_EXP_LNKCTL,
+ &pci_lnk_ctl);
return pci_lnk_ctl;
}
@@ -1442,6 +1439,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
return err;
err_free_irq:
+ trans_pcie->irq_requested = false;
free_irq(trans_pcie->irq, trans);
error:
iwl_free_isr_ict(trans);
diff --git a/drivers/net/wireless/rtlwifi/pci.c b/drivers/net/wireless/rtlwifi/pci.c
index 80f75d3ba84a..5983631a1b1a 100644
--- a/drivers/net/wireless/rtlwifi/pci.c
+++ b/drivers/net/wireless/rtlwifi/pci.c
@@ -372,13 +372,11 @@ static void rtl_pci_parse_configuration(struct pci_dev *pdev,
struct rtl_pci_priv *pcipriv = rtl_pcipriv(hw);
u8 tmp;
- int pos;
- u8 linkctrl_reg;
+ u16 linkctrl_reg;
/*Link Control Register */
- pos = pci_pcie_cap(pdev);
- pci_read_config_byte(pdev, pos + PCI_EXP_LNKCTL, &linkctrl_reg);
- pcipriv->ndis_adapter.linkctrl_reg = linkctrl_reg;
+ pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &linkctrl_reg);
+ pcipriv->ndis_adapter.linkctrl_reg = (u8)linkctrl_reg;
RT_TRACE(rtlpriv, COMP_INIT, DBG_TRACE, "Link Control Register =%x\n",
pcipriv->ndis_adapter.linkctrl_reg);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
index 44febfde9493..8a7b864faca3 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
@@ -315,7 +315,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
u16 box_reg = 0, box_extreg = 0;
u8 u1b_tmp;
bool isfw_read = false;
- bool bwrite_sucess = false;
+ bool bwrite_success = false;
u8 wait_h2c_limmit = 100;
u8 wait_writeh2c_limmit = 100;
u8 boxcontent[4], boxextcontent[2];
@@ -354,7 +354,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
}
}
- while (!bwrite_sucess) {
+ while (!bwrite_success) {
wait_writeh2c_limmit--;
if (wait_writeh2c_limmit == 0) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -491,7 +491,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
break;
}
- bwrite_sucess = true;
+ bwrite_success = true;
rtlhal->last_hmeboxnum = boxnum + 1;
if (rtlhal->last_hmeboxnum == 4)
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
index 04c3aef8a4f6..2925094b2d91 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/def.h
@@ -117,6 +117,7 @@
#define CHIP_VER_B BIT(4)
#define CHIP_92C_BITMASK BIT(0)
+#define CHIP_UNKNOWN BIT(7)
#define CHIP_92C_1T2R 0x03
#define CHIP_92C 0x01
#define CHIP_88C 0x00
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
index bd0da7ef290b..dd4bb0950a57 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/hw.c
@@ -994,8 +994,16 @@ static enum version_8192c _rtl92ce_read_chip_version(struct ieee80211_hw *hw)
version = (value32 & TYPE_ID) ? VERSION_A_CHIP_92C :
VERSION_A_CHIP_88C;
} else {
- version = (value32 & TYPE_ID) ? VERSION_B_CHIP_92C :
- VERSION_B_CHIP_88C;
+ version = (enum version_8192c) (CHIP_VER_B |
+ ((value32 & TYPE_ID) ? CHIP_92C_BITMASK : 0) |
+ ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : 0));
+ if ((!IS_CHIP_VENDOR_UMC(version)) && (value32 &
+ CHIP_VER_RTL_MASK)) {
+ version = (enum version_8192c)(version |
+ ((((value32 & CHIP_VER_RTL_MASK) == BIT(12))
+ ? CHIP_VENDOR_UMC_B_CUT : CHIP_UNKNOWN) |
+ CHIP_VENDOR_UMC));
+ }
}
switch (version) {
diff --git a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
index 3aa927f8b9b9..7d8f96405f42 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192ce/sw.c
@@ -162,10 +162,12 @@ int rtl92c_init_sw_vars(struct ieee80211_hw *hw)
/* request fw */
if (IS_VENDOR_UMC_A_CUT(rtlhal->version) &&
- !IS_92C_SERIAL(rtlhal->version))
+ !IS_92C_SERIAL(rtlhal->version)) {
rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU.bin";
- else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version))
+ } else if (IS_81xxC_VENDOR_UMC_B_CUT(rtlhal->version)) {
rtlpriv->cfg->fw_name = "rtlwifi/rtl8192cfwU_B.bin";
+ pr_info("****** This B_CUT device may not work with kernels 3.6 and earlier\n");
+ }
rtlpriv->max_fw_size = 0x4000;
pr_info("Using firmware %s\n", rtlpriv->cfg->fw_name);
diff --git a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
index 895ae6c1f354..eb22dccc418b 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192de/fw.c
@@ -365,7 +365,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw,
u8 u1b_tmp;
bool isfw_read = false;
u8 buf_index = 0;
- bool bwrite_sucess = false;
+ bool bwrite_success = false;
u8 wait_h2c_limmit = 100;
u8 wait_writeh2c_limmit = 100;
u8 boxcontent[4], boxextcontent[2];
@@ -408,7 +408,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw,
break;
}
}
- while (!bwrite_sucess) {
+ while (!bwrite_success) {
wait_writeh2c_limmit--;
if (wait_writeh2c_limmit == 0) {
RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -515,7 +515,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw,
"switch case not processed\n");
break;
}
- bwrite_sucess = true;
+ bwrite_success = true;
rtlhal->last_hmeboxnum = boxnum + 1;
if (rtlhal->last_hmeboxnum == 4)
rtlhal->last_hmeboxnum = 0;
diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c
index b8ef8ddcc292..8aa73fac6ad4 100644
--- a/drivers/oprofile/cpu_buffer.c
+++ b/drivers/oprofile/cpu_buffer.c
@@ -451,14 +451,9 @@ static void wq_sync_buffer(struct work_struct *work)
{
struct oprofile_cpu_buffer *b =
container_of(work, struct oprofile_cpu_buffer, work.work);
- if (b->cpu != smp_processor_id()) {
- printk(KERN_DEBUG "WQ on CPU%d, prefer CPU%d\n",
- smp_processor_id(), b->cpu);
-
- if (!cpu_online(b->cpu)) {
- cancel_delayed_work(&b->work);
- return;
- }
+ if (b->cpu != smp_processor_id() && !cpu_online(b->cpu)) {
+ cancel_delayed_work(&b->work);
+ return;
}
sync_buffer(b->cpu);
diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c
index ffddc4f64268..fb6a1fe21b93 100644
--- a/drivers/parisc/dino.c
+++ b/drivers/parisc/dino.c
@@ -477,14 +477,12 @@ dino_card_setup(struct pci_bus *bus, void __iomem *base_addr)
if (ccio_allocate_resource(dino_dev->hba.dev, res, _8MB,
F_EXTEND(0xf0000000UL) | _8MB,
F_EXTEND(0xffffffffUL) &~ _8MB, _8MB) < 0) {
- struct list_head *ln, *tmp_ln;
+ struct pci_dev *dev, *tmp;
printk(KERN_ERR "Dino: cannot attach bus %s\n",
dev_name(bus->bridge));
/* kill the bus, we can't do anything with it */
- list_for_each_safe(ln, tmp_ln, &bus->devices) {
- struct pci_dev *dev = pci_dev_b(ln);
-
+ list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list) {
list_del(&dev->bus_list);
}
@@ -549,7 +547,6 @@ dino_card_fixup(struct pci_dev *dev)
static void __init
dino_fixup_bus(struct pci_bus *bus)
{
- struct list_head *ln;
struct pci_dev *dev;
struct dino_device *dino_dev = DINO_DEV(parisc_walk_tree(bus->bridge));
@@ -596,8 +593,7 @@ dino_fixup_bus(struct pci_bus *bus)
}
- list_for_each(ln, &bus->devices) {
- dev = pci_dev_b(ln);
+ list_for_each_entry(dev, &bus->devices, bus_list) {
if (is_card_dino(&dino_dev->hba.dev->id))
dino_card_fixup(dev);
diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c
index 4f9cf2456f4e..fdd63a6a62d6 100644
--- a/drivers/parisc/lba_pci.c
+++ b/drivers/parisc/lba_pci.c
@@ -629,7 +629,7 @@ truncate_pat_collision(struct resource *root, struct resource *new)
static void
lba_fixup_bus(struct pci_bus *bus)
{
- struct list_head *ln;
+ struct pci_dev *dev;
#ifdef FBB_SUPPORT
u16 status;
#endif
@@ -710,9 +710,8 @@ lba_fixup_bus(struct pci_bus *bus)
}
- list_for_each(ln, &bus->devices) {
+ list_for_each_entry(dev, &bus->devices, bus_list) {
int i;
- struct pci_dev *dev = pci_dev_b(ln);
DBG("lba_fixup_bus() %s\n", pci_name(dev));
@@ -770,7 +769,7 @@ lba_fixup_bus(struct pci_bus *bus)
}
/* Lastly enable FBB/PERR/SERR on all devices too */
- list_for_each(ln, &bus->devices) {
+ list_for_each_entry(dev, &bus->devices, bus_list) {
(void) pci_read_config_word(dev, PCI_COMMAND, &status);
status |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR | fbb_enable;
(void) pci_write_config_word(dev, PCI_COMMAND, status);
diff --git a/drivers/pci/.gitignore b/drivers/pci/.gitignore
deleted file mode 100644
index f297ca8d313e..000000000000
--- a/drivers/pci/.gitignore
+++ /dev/null
@@ -1,4 +0,0 @@
-classlist.h
-devlist.h
-gen-devlist
-
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 848bfb84c04c..6d51aa68ec7a 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -3,7 +3,6 @@
#
config ARCH_SUPPORTS_MSI
bool
- default n
config PCI_MSI
bool "Message Signaled Interrupts (MSI and MSI-X)"
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index ba91a7e17519..3af0478c057b 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -469,3 +469,205 @@ void pci_cfg_access_unlock(struct pci_dev *dev)
raw_spin_unlock_irqrestore(&pci_lock, flags);
}
EXPORT_SYMBOL_GPL(pci_cfg_access_unlock);
+
+static inline int pcie_cap_version(const struct pci_dev *dev)
+{
+ return dev->pcie_flags_reg & PCI_EXP_FLAGS_VERS;
+}
+
+static inline bool pcie_cap_has_devctl(const struct pci_dev *dev)
+{
+ return true;
+}
+
+static inline bool pcie_cap_has_lnkctl(const struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return pcie_cap_version(dev) > 1 ||
+ type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_ENDPOINT ||
+ type == PCI_EXP_TYPE_LEG_END;
+}
+
+static inline bool pcie_cap_has_sltctl(const struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return pcie_cap_version(dev) > 1 ||
+ type == PCI_EXP_TYPE_ROOT_PORT ||
+ (type == PCI_EXP_TYPE_DOWNSTREAM &&
+ dev->pcie_flags_reg & PCI_EXP_FLAGS_SLOT);
+}
+
+static inline bool pcie_cap_has_rtctl(const struct pci_dev *dev)
+{
+ int type = pci_pcie_type(dev);
+
+ return pcie_cap_version(dev) > 1 ||
+ type == PCI_EXP_TYPE_ROOT_PORT ||
+ type == PCI_EXP_TYPE_RC_EC;
+}
+
+static bool pcie_capability_reg_implemented(struct pci_dev *dev, int pos)
+{
+ if (!pci_is_pcie(dev))
+ return false;
+
+ switch (pos) {
+ case PCI_EXP_FLAGS_TYPE:
+ return true;
+ case PCI_EXP_DEVCAP:
+ case PCI_EXP_DEVCTL:
+ case PCI_EXP_DEVSTA:
+ return pcie_cap_has_devctl(dev);
+ case PCI_EXP_LNKCAP:
+ case PCI_EXP_LNKCTL:
+ case PCI_EXP_LNKSTA:
+ return pcie_cap_has_lnkctl(dev);
+ case PCI_EXP_SLTCAP:
+ case PCI_EXP_SLTCTL:
+ case PCI_EXP_SLTSTA:
+ return pcie_cap_has_sltctl(dev);
+ case PCI_EXP_RTCTL:
+ case PCI_EXP_RTCAP:
+ case PCI_EXP_RTSTA:
+ return pcie_cap_has_rtctl(dev);
+ case PCI_EXP_DEVCAP2:
+ case PCI_EXP_DEVCTL2:
+ case PCI_EXP_LNKCAP2:
+ case PCI_EXP_LNKCTL2:
+ case PCI_EXP_LNKSTA2:
+ return pcie_cap_version(dev) > 1;
+ default:
+ return false;
+ }
+}
+
+/*
+ * Note that these accessor functions are only for the "PCI Express
+ * Capability" (see PCIe spec r3.0, sec 7.8). They do not apply to the
+ * other "PCI Express Extended Capabilities" (AER, VC, ACS, MFVC, etc.)
+ */
+int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val)
+{
+ int ret;
+
+ *val = 0;
+ if (pos & 1)
+ return -EINVAL;
+
+ if (pcie_capability_reg_implemented(dev, pos)) {
+ ret = pci_read_config_word(dev, pci_pcie_cap(dev) + pos, val);
+ /*
+ * Reset *val to 0 if pci_read_config_word() fails, it may
+ * have been written as 0xFFFF if hardware error happens
+ * during pci_read_config_word().
+ */
+ if (ret)
+ *val = 0;
+ return ret;
+ }
+
+ /*
+ * For Functions that do not implement the Slot Capabilities,
+ * Slot Status, and Slot Control registers, these spaces must
+ * be hardwired to 0b, with the exception of the Presence Detect
+ * State bit in the Slot Status register of Downstream Ports,
+ * which must be hardwired to 1b. (PCIe Base Spec 3.0, sec 7.8)
+ */
+ if (pci_is_pcie(dev) && pos == PCI_EXP_SLTSTA &&
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
+ *val = PCI_EXP_SLTSTA_PDS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pcie_capability_read_word);
+
+int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val)
+{
+ int ret;
+
+ *val = 0;
+ if (pos & 3)
+ return -EINVAL;
+
+ if (pcie_capability_reg_implemented(dev, pos)) {
+ ret = pci_read_config_dword(dev, pci_pcie_cap(dev) + pos, val);
+ /*
+ * Reset *val to 0 if pci_read_config_dword() fails, it may
+ * have been written as 0xFFFFFFFF if hardware error happens
+ * during pci_read_config_dword().
+ */
+ if (ret)
+ *val = 0;
+ return ret;
+ }
+
+ if (pci_is_pcie(dev) && pos == PCI_EXP_SLTCTL &&
+ pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM) {
+ *val = PCI_EXP_SLTSTA_PDS;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(pcie_capability_read_dword);
+
+int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val)
+{
+ if (pos & 1)
+ return -EINVAL;
+
+ if (!pcie_capability_reg_implemented(dev, pos))
+ return 0;
+
+ return pci_write_config_word(dev, pci_pcie_cap(dev) + pos, val);
+}
+EXPORT_SYMBOL(pcie_capability_write_word);
+
+int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val)
+{
+ if (pos & 3)
+ return -EINVAL;
+
+ if (!pcie_capability_reg_implemented(dev, pos))
+ return 0;
+
+ return pci_write_config_dword(dev, pci_pcie_cap(dev) + pos, val);
+}
+EXPORT_SYMBOL(pcie_capability_write_dword);
+
+int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos,
+ u16 clear, u16 set)
+{
+ int ret;
+ u16 val;
+
+ ret = pcie_capability_read_word(dev, pos, &val);
+ if (!ret) {
+ val &= ~clear;
+ val |= set;
+ ret = pcie_capability_write_word(dev, pos, val);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(pcie_capability_clear_and_set_word);
+
+int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos,
+ u32 clear, u32 set)
+{
+ int ret;
+ u32 val;
+
+ ret = pcie_capability_read_dword(dev, pos, &val);
+ if (!ret) {
+ val &= ~clear;
+ val |= set;
+ ret = pcie_capability_write_dword(dev, pos, val);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(pcie_capability_clear_and_set_dword);
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 4b0970b46e0b..6241fd05bd41 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -87,11 +87,15 @@ EXPORT_SYMBOL_GPL(pci_bus_resource_n);
void pci_bus_remove_resources(struct pci_bus *bus)
{
int i;
+ struct pci_bus_resource *bus_res, *tmp;
for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++)
bus->resource[i] = NULL;
- pci_free_resource_list(&bus->resources);
+ list_for_each_entry_safe(bus_res, tmp, &bus->resources, list) {
+ list_del(&bus_res->list);
+ kfree(bus_res);
+ }
}
/**
diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig
index 66f29bc00be4..b0e46dede1a9 100644
--- a/drivers/pci/hotplug/Kconfig
+++ b/drivers/pci/hotplug/Kconfig
@@ -17,28 +17,6 @@ menuconfig HOTPLUG_PCI
if HOTPLUG_PCI
-config HOTPLUG_PCI_FAKE
- tristate "Fake PCI Hotplug driver"
- help
- Say Y here if you want to use the fake PCI hotplug driver. It can
- be used to simulate PCI hotplug events if even if your system is
- not PCI hotplug capable.
-
- This driver will "emulate" removing PCI devices from the system.
- If the "power" file is written to with "0" then the specified PCI
- device will be completely removed from the kernel.
-
- WARNING, this does NOT turn off the power to the PCI device.
- This is a "logical" removal, not a physical or electrical
- removal.
-
- Use this module at your own risk. You have been warned!
-
- To compile this driver as a module, choose M here: the
- module will be called fakephp.
-
- When in doubt, say N.
-
config HOTPLUG_PCI_COMPAQ
tristate "Compaq PCI Hotplug driver"
depends on X86 && PCI_BIOS
@@ -143,7 +121,7 @@ config HOTPLUG_PCI_SHPC
config HOTPLUG_PCI_RPA
tristate "RPA PCI Hotplug driver"
- depends on PPC_PSERIES && EEH && !HOTPLUG_PCI_FAKE
+ depends on PPC_PSERIES && EEH
help
Say Y here if you have a RPA system that supports PCI Hotplug.
diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile
index 6cd9f3c9887d..c459cd4e39c2 100644
--- a/drivers/pci/hotplug/Makefile
+++ b/drivers/pci/hotplug/Makefile
@@ -23,9 +23,6 @@ obj-$(CONFIG_HOTPLUG_PCI_ACPI) += acpiphp.o
obj-$(CONFIG_HOTPLUG_PCI_ACPI_IBM) += acpiphp_ibm.o
-# Link this last so it doesn't claim devices that have a real hotplug driver
-obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o
-
pci_hotplug-objs := pci_hotplug_core.o pcihp_slot.o
ifdef CONFIG_HOTPLUG_PCI_CPCI
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index ad6fd6695495..3d6d4fd1e3c5 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -115,6 +115,35 @@ static const struct acpi_dock_ops acpiphp_dock_ops = {
.handler = handle_hotplug_event_func,
};
+/* Check whether the PCI device is managed by native PCIe hotplug driver */
+static bool device_is_managed_by_native_pciehp(struct pci_dev *pdev)
+{
+ u32 reg32;
+ acpi_handle tmp;
+ struct acpi_pci_root *root;
+
+ /* Check whether the PCIe port supports native PCIe hotplug */
+ if (pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32))
+ return false;
+ if (!(reg32 & PCI_EXP_SLTCAP_HPC))
+ return false;
+
+ /*
+ * Check whether native PCIe hotplug has been enabled for
+ * this PCIe hierarchy.
+ */
+ tmp = acpi_find_root_bridge_handle(pdev);
+ if (!tmp)
+ return false;
+ root = acpi_pci_find_root(tmp);
+ if (!root)
+ return false;
+ if (!(root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
+ return false;
+
+ return true;
+}
+
/* callback routine to register each ACPI PCI slot object */
static acpi_status
register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
@@ -142,16 +171,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
function = adr & 0xffff;
pdev = pbus->self;
- if (pdev && pci_is_pcie(pdev)) {
- tmp = acpi_find_root_bridge_handle(pdev);
- if (tmp) {
- struct acpi_pci_root *root = acpi_pci_find_root(tmp);
-
- if (root && (root->osc_control_set &
- OSC_PCI_EXPRESS_NATIVE_HP_CONTROL))
- return AE_OK;
- }
- }
+ if (pdev && device_is_managed_by_native_pciehp(pdev))
+ return AE_OK;
newfunc = kzalloc(sizeof(struct acpiphp_func), GFP_KERNEL);
if (!newfunc)
@@ -382,10 +403,10 @@ static inline void config_p2p_bridge_flags(struct acpiphp_bridge *bridge)
/* allocate and initialize host bridge data structure */
-static void add_host_bridge(acpi_handle *handle)
+static void add_host_bridge(struct acpi_pci_root *root)
{
struct acpiphp_bridge *bridge;
- struct acpi_pci_root *root = acpi_pci_find_root(handle);
+ acpi_handle handle = root->device->handle;
bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL);
if (bridge == NULL)
@@ -468,11 +489,12 @@ find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
/* find hot-pluggable slots, and then find P2P bridge */
-static int add_bridge(acpi_handle handle)
+static int add_bridge(struct acpi_pci_root *root)
{
acpi_status status;
unsigned long long tmp;
acpi_handle dummy_handle;
+ acpi_handle handle = root->device->handle;
/* if the bridge doesn't have _STA, we assume it is always there */
status = acpi_get_handle(handle, "_STA", &dummy_handle);
@@ -490,7 +512,7 @@ static int add_bridge(acpi_handle handle)
/* check if this bridge has ejectable slots */
if (detect_ejectable_slots(handle) > 0) {
dbg("found PCI host-bus bridge with hot-pluggable slots\n");
- add_host_bridge(handle);
+ add_host_bridge(root);
}
/* search P2P bridges under this host bridge */
@@ -588,9 +610,10 @@ cleanup_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv)
return AE_OK;
}
-static void remove_bridge(acpi_handle handle)
+static void remove_bridge(struct acpi_pci_root *root)
{
struct acpiphp_bridge *bridge;
+ acpi_handle handle = root->device->handle;
/* cleanup p2p bridges under this host bridge
in a depth-first manner */
@@ -869,17 +892,6 @@ static int __ref enable_device(struct acpiphp_slot *slot)
return retval;
}
-static void disable_bridges(struct pci_bus *bus)
-{
- struct pci_dev *dev;
- list_for_each_entry(dev, &bus->devices, bus_list) {
- if (dev->subordinate) {
- disable_bridges(dev->subordinate);
- pci_disable_device(dev);
- }
- }
-}
-
/* return first device in slot, acquiring a reference on it */
static struct pci_dev *dev_in_slot(struct acpiphp_slot *slot)
{
@@ -931,12 +943,7 @@ static int disable_device(struct acpiphp_slot *slot)
* here.
*/
while ((pdev = dev_in_slot(slot))) {
- pci_stop_bus_device(pdev);
- if (pdev->subordinate) {
- disable_bridges(pdev->subordinate);
- pci_disable_device(pdev);
- }
- __pci_remove_bus_device(pdev);
+ pci_stop_and_remove_bus_device(pdev);
pci_dev_put(pdev);
}
@@ -1477,34 +1484,6 @@ int __init acpiphp_get_num_slots(void)
}
-#if 0
-/**
- * acpiphp_for_each_slot - call function for each slot
- * @fn: callback function
- * @data: context to be passed to callback function
- */
-static int acpiphp_for_each_slot(acpiphp_callback fn, void *data)
-{
- struct list_head *node;
- struct acpiphp_bridge *bridge;
- struct acpiphp_slot *slot;
- int retval = 0;
-
- list_for_each (node, &bridge_list) {
- bridge = (struct acpiphp_bridge *)node;
- for (slot = bridge->slots; slot; slot = slot->next) {
- retval = fn(slot, data);
- if (!retval)
- goto err_exit;
- }
- }
-
- err_exit:
- return retval;
-}
-#endif
-
-
/**
* acpiphp_enable_slot - power on slot
* @slot: ACPI PHP slot
diff --git a/drivers/pci/hotplug/cpcihp_generic.c b/drivers/pci/hotplug/cpcihp_generic.c
index 81af764c629b..a6a71c41cdf8 100644
--- a/drivers/pci/hotplug/cpcihp_generic.c
+++ b/drivers/pci/hotplug/cpcihp_generic.c
@@ -154,12 +154,8 @@ static int __init cpcihp_generic_init(void)
if(!r)
return -EBUSY;
- bus = pci_find_bus(0, bridge_busnr);
- if (!bus) {
- err("Invalid bus number %d", bridge_busnr);
- return -EINVAL;
- }
- dev = pci_get_slot(bus, PCI_DEVFN(bridge_slot, 0));
+ dev = pci_get_domain_bus_and_slot(0, bridge_busnr,
+ PCI_DEVFN(bridge_slot, 0));
if(!dev || dev->hdr_type != PCI_HEADER_TYPE_BRIDGE) {
err("Invalid bridge device %s", bridge);
pci_dev_put(dev);
diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c
index e43908d9b5df..36112fe212d3 100644
--- a/drivers/pci/hotplug/cpqphp_ctrl.c
+++ b/drivers/pci/hotplug/cpqphp_ctrl.c
@@ -2890,27 +2890,8 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func
func->mem_head = mem_node;
} else
return -ENOMEM;
- } else if ((temp_register & 0x0BL) == 0x04) {
- /* Map memory */
- base = temp_register & 0xFFFFFFF0;
- base = ~base + 1;
-
- dbg("CND: length = 0x%x\n", base);
- mem_node = get_resource(&(resources->mem_head), base);
-
- /* allocate the resource to the board */
- if (mem_node) {
- base = mem_node->base;
-
- mem_node->next = func->mem_head;
- func->mem_head = mem_node;
- } else
- return -ENOMEM;
- } else if ((temp_register & 0x0BL) == 0x06) {
- /* Those bits are reserved, we can't handle this */
- return 1;
} else {
- /* Requesting space below 1M */
+ /* Reserved bits or requesting space below 1M */
return NOT_ENOUGH_RESOURCES;
}
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
deleted file mode 100644
index a019c9a712be..000000000000
--- a/drivers/pci/hotplug/fakephp.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/* Works like the fakephp driver used to, except a little better.
- *
- * - It's possible to remove devices with subordinate busses.
- * - New PCI devices that appear via any method, not just a fakephp triggered
- * rescan, will be noticed.
- * - Devices that are removed via any method, not just a fakephp triggered
- * removal, will also be noticed.
- *
- * Uses nothing from the pci-hotplug subsystem.
- *
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/list.h>
-#include <linux/kobject.h>
-#include <linux/sysfs.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/device.h>
-#include <linux/slab.h>
-#include "../pci.h"
-
-struct legacy_slot {
- struct kobject kobj;
- struct pci_dev *dev;
- struct list_head list;
-};
-
-static LIST_HEAD(legacy_list);
-
-static ssize_t legacy_show(struct kobject *kobj, struct attribute *attr,
- char *buf)
-{
- struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
- strcpy(buf, "1\n");
- return 2;
-}
-
-static void remove_callback(void *data)
-{
- pci_stop_and_remove_bus_device((struct pci_dev *)data);
-}
-
-static ssize_t legacy_store(struct kobject *kobj, struct attribute *attr,
- const char *buf, size_t len)
-{
- struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
- unsigned long val;
-
- if (strict_strtoul(buf, 0, &val) < 0)
- return -EINVAL;
-
- if (val)
- pci_rescan_bus(slot->dev->bus);
- else
- sysfs_schedule_callback(&slot->dev->dev.kobj, remove_callback,
- slot->dev, THIS_MODULE);
- return len;
-}
-
-static struct attribute *legacy_attrs[] = {
- &(struct attribute){ .name = "power", .mode = 0644 },
- NULL,
-};
-
-static void legacy_release(struct kobject *kobj)
-{
- struct legacy_slot *slot = container_of(kobj, typeof(*slot), kobj);
-
- pci_dev_put(slot->dev);
- kfree(slot);
-}
-
-static struct kobj_type legacy_ktype = {
- .sysfs_ops = &(const struct sysfs_ops){
- .store = legacy_store, .show = legacy_show
- },
- .release = &legacy_release,
- .default_attrs = legacy_attrs,
-};
-
-static int legacy_add_slot(struct pci_dev *pdev)
-{
- struct legacy_slot *slot = kzalloc(sizeof(*slot), GFP_KERNEL);
-
- if (!slot)
- return -ENOMEM;
-
- if (kobject_init_and_add(&slot->kobj, &legacy_ktype,
- &pci_slots_kset->kobj, "%s",
- dev_name(&pdev->dev))) {
- dev_warn(&pdev->dev, "Failed to created legacy fake slot\n");
- return -EINVAL;
- }
- slot->dev = pci_dev_get(pdev);
-
- list_add(&slot->list, &legacy_list);
-
- return 0;
-}
-
-static int legacy_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct pci_dev *pdev = to_pci_dev(data);
-
- if (action == BUS_NOTIFY_ADD_DEVICE) {
- legacy_add_slot(pdev);
- } else if (action == BUS_NOTIFY_DEL_DEVICE) {
- struct legacy_slot *slot;
-
- list_for_each_entry(slot, &legacy_list, list)
- if (slot->dev == pdev)
- goto found;
-
- dev_warn(&pdev->dev, "Missing legacy fake slot?");
- return -ENODEV;
-found:
- kobject_del(&slot->kobj);
- list_del(&slot->list);
- kobject_put(&slot->kobj);
- }
-
- return 0;
-}
-
-static struct notifier_block legacy_notifier = {
- .notifier_call = legacy_notify
-};
-
-static int __init init_legacy(void)
-{
- struct pci_dev *pdev = NULL;
-
- /* Add existing devices */
- for_each_pci_dev(pdev)
- legacy_add_slot(pdev);
-
- /* Be alerted of any new ones */
- bus_register_notifier(&pci_bus_type, &legacy_notifier);
- return 0;
-}
-module_init(init_legacy);
-
-static void __exit remove_legacy(void)
-{
- struct legacy_slot *slot, *tmp;
-
- bus_unregister_notifier(&pci_bus_type, &legacy_notifier);
-
- list_for_each_entry_safe(slot, tmp, &legacy_list, list) {
- list_del(&slot->list);
- kobject_del(&slot->kobj);
- kobject_put(&slot->kobj);
- }
-}
-module_exit(remove_legacy);
-
-
-MODULE_AUTHOR("Trent Piepho <xyzzy@speakeasy.org>");
-MODULE_DESCRIPTION("Legacy version of the fakephp interface");
-MODULE_LICENSE("GPL");
diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c
index 376d70d17176..24d709b7388c 100644
--- a/drivers/pci/hotplug/pciehp_acpi.c
+++ b/drivers/pci/hotplug/pciehp_acpi.c
@@ -81,16 +81,12 @@ static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots);
/* Dummy driver for dumplicate name detection */
static int __init dummy_probe(struct pcie_device *dev)
{
- int pos;
u32 slot_cap;
acpi_handle handle;
struct dummy_slot *slot, *tmp;
struct pci_dev *pdev = dev->port;
- pos = pci_pcie_cap(pdev);
- if (!pos)
- return -ENODEV;
- pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &slot_cap);
+ pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &slot_cap);
slot = kzalloc(sizeof(*slot), GFP_KERNEL);
if (!slot)
return -ENOMEM;
diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
index 365c6b96c642..916bf4f53aba 100644
--- a/drivers/pci/hotplug/pciehp_core.c
+++ b/drivers/pci/hotplug/pciehp_core.c
@@ -300,24 +300,24 @@ static int pciehp_suspend (struct pcie_device *dev)
static int pciehp_resume (struct pcie_device *dev)
{
+ struct controller *ctrl;
+ struct slot *slot;
+ u8 status;
+
dev_info(&dev->device, "%s ENTRY\n", __func__);
- if (pciehp_force) {
- struct controller *ctrl = get_service_data(dev);
- struct slot *slot;
- u8 status;
+ ctrl = get_service_data(dev);
- /* reinitialize the chipset's event detection logic */
- pcie_enable_notification(ctrl);
+ /* reinitialize the chipset's event detection logic */
+ pcie_enable_notification(ctrl);
- slot = ctrl->slot;
+ slot = ctrl->slot;
- /* Check if slot is occupied */
- pciehp_get_adapter_status(slot, &status);
- if (status)
- pciehp_enable_slot(slot);
- else
- pciehp_disable_slot(slot);
- }
+ /* Check if slot is occupied */
+ pciehp_get_adapter_status(slot, &status);
+ if (status)
+ pciehp_enable_slot(slot);
+ else
+ pciehp_disable_slot(slot);
return 0;
}
#endif /* PM */
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index 302451e8289d..13b2eaf7ba43 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -44,25 +44,25 @@
static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value)
{
struct pci_dev *dev = ctrl->pcie->port;
- return pci_read_config_word(dev, pci_pcie_cap(dev) + reg, value);
+ return pcie_capability_read_word(dev, reg, value);
}
static inline int pciehp_readl(struct controller *ctrl, int reg, u32 *value)
{
struct pci_dev *dev = ctrl->pcie->port;
- return pci_read_config_dword(dev, pci_pcie_cap(dev) + reg, value);
+ return pcie_capability_read_dword(dev, reg, value);
}
static inline int pciehp_writew(struct controller *ctrl, int reg, u16 value)
{
struct pci_dev *dev = ctrl->pcie->port;
- return pci_write_config_word(dev, pci_pcie_cap(dev) + reg, value);
+ return pcie_capability_write_word(dev, reg, value);
}
static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value)
{
struct pci_dev *dev = ctrl->pcie->port;
- return pci_write_config_dword(dev, pci_pcie_cap(dev) + reg, value);
+ return pcie_capability_write_dword(dev, reg, value);
}
/* Power Control Command */
@@ -855,10 +855,6 @@ struct controller *pcie_init(struct pcie_device *dev)
goto abort;
}
ctrl->pcie = dev;
- if (!pci_pcie_cap(pdev)) {
- ctrl_err(ctrl, "Cannot find PCI Express capability\n");
- goto abort_ctrl;
- }
if (pciehp_readl(ctrl, PCI_EXP_SLTCAP, &slot_cap)) {
ctrl_err(ctrl, "Cannot read SLOTCAP register\n");
goto abort_ctrl;
diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c
index 8c05a18c9770..fec2d5b75440 100644
--- a/drivers/pci/hotplug/pcihp_slot.c
+++ b/drivers/pci/hotplug/pcihp_slot.c
@@ -96,17 +96,11 @@ static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp)
static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
{
int pos;
- u16 reg16;
u32 reg32;
if (!hpp)
return;
- /* Find PCI Express capability */
- pos = pci_pcie_cap(dev);
- if (!pos)
- return;
-
if (hpp->revision > 1) {
dev_warn(&dev->dev, "PCIe settings rev %d not supported\n",
hpp->revision);
@@ -114,17 +108,13 @@ static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp)
}
/* Initialize Device Control Register */
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
- reg16 = (reg16 & hpp->pci_exp_devctl_and) | hpp->pci_exp_devctl_or;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16);
+ pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+ ~hpp->pci_exp_devctl_and, hpp->pci_exp_devctl_or);
/* Initialize Link Control Register */
- if (dev->subordinate) {
- pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &reg16);
- reg16 = (reg16 & hpp->pci_exp_lnkctl_and)
- | hpp->pci_exp_lnkctl_or;
- pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, reg16);
- }
+ if (dev->subordinate)
+ pcie_capability_clear_and_set_word(dev, PCI_EXP_LNKCTL,
+ ~hpp->pci_exp_lnkctl_and, hpp->pci_exp_lnkctl_or);
/* Find Advanced Error Reporting Enhanced Capability */
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c
index 74bbaf82638d..aeccc911abb8 100644
--- a/drivers/pci/iov.c
+++ b/drivers/pci/iov.c
@@ -433,8 +433,8 @@ static int sriov_init(struct pci_dev *dev, int pos)
struct resource *res;
struct pci_dev *pdev;
- if (dev->pcie_type != PCI_EXP_TYPE_RC_END &&
- dev->pcie_type != PCI_EXP_TYPE_ENDPOINT)
+ if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END &&
+ pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT)
return -ENODEV;
pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl);
@@ -503,7 +503,7 @@ found:
iov->self = dev;
pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap);
pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link);
- if (dev->pcie_type == PCI_EXP_TYPE_RC_END)
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link);
if (pdev)
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index d6fd6b6d9d4b..9e1d2959e226 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -139,7 +139,6 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
return retval;
return count;
}
-static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
/**
* store_remove_id - remove a PCI device ID from this driver
@@ -185,38 +184,16 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
return retval;
return count;
}
-static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id);
-static int
-pci_create_newid_files(struct pci_driver *drv)
-{
- int error = 0;
+static struct driver_attribute pci_drv_attrs[] = {
+ __ATTR(new_id, S_IWUSR, NULL, store_new_id),
+ __ATTR(remove_id, S_IWUSR, NULL, store_remove_id),
+ __ATTR_NULL,
+};
- if (drv->probe != NULL) {
- error = driver_create_file(&drv->driver, &driver_attr_new_id);
- if (error == 0) {
- error = driver_create_file(&drv->driver,
- &driver_attr_remove_id);
- if (error)
- driver_remove_file(&drv->driver,
- &driver_attr_new_id);
- }
- }
- return error;
-}
-
-static void pci_remove_newid_files(struct pci_driver *drv)
-{
- driver_remove_file(&drv->driver, &driver_attr_remove_id);
- driver_remove_file(&drv->driver, &driver_attr_new_id);
-}
-#else /* !CONFIG_HOTPLUG */
-static inline int pci_create_newid_files(struct pci_driver *drv)
-{
- return 0;
-}
-static inline void pci_remove_newid_files(struct pci_driver *drv) {}
-#endif
+#else
+#define pci_drv_attrs NULL
+#endif /* CONFIG_HOTPLUG */
/**
* pci_match_id - See if a pci device matches a given pci_id table
@@ -1162,8 +1139,6 @@ const struct dev_pm_ops pci_dev_pm_ops = {
int __pci_register_driver(struct pci_driver *drv, struct module *owner,
const char *mod_name)
{
- int error;
-
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = &pci_bus_type;
@@ -1174,19 +1149,7 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner,
INIT_LIST_HEAD(&drv->dynids.list);
/* register with core */
- error = driver_register(&drv->driver);
- if (error)
- goto out;
-
- error = pci_create_newid_files(drv);
- if (error)
- goto out_newid;
-out:
- return error;
-
-out_newid:
- driver_unregister(&drv->driver);
- goto out;
+ return driver_register(&drv->driver);
}
/**
@@ -1202,7 +1165,6 @@ out_newid:
void
pci_unregister_driver(struct pci_driver *drv)
{
- pci_remove_newid_files(drv);
driver_unregister(&drv->driver);
pci_free_dynids(drv);
}
@@ -1302,6 +1264,7 @@ struct bus_type pci_bus_type = {
.shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
.bus_attrs = pci_bus_attrs,
+ .drv_attrs = pci_drv_attrs,
.pm = PCI_PM_OPS_PTR,
};
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index ab4bf5a4c2f1..54858838f098 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -254,52 +254,17 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap)
}
/**
- * pci_pcie_cap2 - query for devices' PCI_CAP_ID_EXP v2 capability structure
- * @dev: PCI device to check
- *
- * Like pci_pcie_cap() but also checks that the PCIe capability version is
- * >= 2. Note that v1 capability structures could be sparse in that not
- * all register fields were required. v2 requires the entire structure to
- * be present size wise, while still allowing for non-implemented registers
- * to exist but they must be hardwired to 0.
- *
- * Due to the differences in the versions of capability structures, one
- * must be careful not to try and access non-existant registers that may
- * exist in early versions - v1 - of Express devices.
- *
- * Returns the offset of the PCIe capability structure as long as the
- * capability version is >= 2; otherwise 0 is returned.
- */
-static int pci_pcie_cap2(struct pci_dev *dev)
-{
- u16 flags;
- int pos;
-
- pos = pci_pcie_cap(dev);
- if (pos) {
- pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags);
- if ((flags & PCI_EXP_FLAGS_VERS) < 2)
- pos = 0;
- }
-
- return pos;
-}
-
-/**
- * pci_find_ext_capability - Find an extended capability
+ * pci_find_next_ext_capability - Find an extended capability
* @dev: PCI device to query
+ * @start: address at which to start looking (0 to start at beginning of list)
* @cap: capability code
*
- * Returns the address of the requested extended capability structure
+ * Returns the address of the next matching extended capability structure
* within the device's PCI configuration space or 0 if the device does
- * not support it. Possible values for @cap:
- *
- * %PCI_EXT_CAP_ID_ERR Advanced Error Reporting
- * %PCI_EXT_CAP_ID_VC Virtual Channel
- * %PCI_EXT_CAP_ID_DSN Device Serial Number
- * %PCI_EXT_CAP_ID_PWR Power Budgeting
+ * not support it. Some capabilities can occur several times, e.g., the
+ * vendor-specific capability, and this provides a way to find them all.
*/
-int pci_find_ext_capability(struct pci_dev *dev, int cap)
+int pci_find_next_ext_capability(struct pci_dev *dev, int start, int cap)
{
u32 header;
int ttl;
@@ -311,6 +276,9 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap)
if (dev->cfg_size <= PCI_CFG_SPACE_SIZE)
return 0;
+ if (start)
+ pos = start;
+
if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL)
return 0;
@@ -322,7 +290,7 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap)
return 0;
while (ttl-- > 0) {
- if (PCI_EXT_CAP_ID(header) == cap)
+ if (PCI_EXT_CAP_ID(header) == cap && pos != start)
return pos;
pos = PCI_EXT_CAP_NEXT(header);
@@ -335,6 +303,26 @@ int pci_find_ext_capability(struct pci_dev *dev, int cap)
return 0;
}
+EXPORT_SYMBOL_GPL(pci_find_next_ext_capability);
+
+/**
+ * pci_find_ext_capability - Find an extended capability
+ * @dev: PCI device to query
+ * @cap: capability code
+ *
+ * Returns the address of the requested extended capability structure
+ * within the device's PCI configuration space or 0 if the device does
+ * not support it. Possible values for @cap:
+ *
+ * %PCI_EXT_CAP_ID_ERR Advanced Error Reporting
+ * %PCI_EXT_CAP_ID_VC Virtual Channel
+ * %PCI_EXT_CAP_ID_DSN Device Serial Number
+ * %PCI_EXT_CAP_ID_PWR Power Budgeting
+ */
+int pci_find_ext_capability(struct pci_dev *dev, int cap)
+{
+ return pci_find_next_ext_capability(dev, 0, cap);
+}
EXPORT_SYMBOL_GPL(pci_find_ext_capability);
static int __pci_find_next_ht_cap(struct pci_dev *dev, int pos, int ht_cap)
@@ -854,21 +842,6 @@ EXPORT_SYMBOL(pci_choose_state);
#define PCI_EXP_SAVE_REGS 7
-#define pcie_cap_has_devctl(type, flags) 1
-#define pcie_cap_has_lnkctl(type, flags) \
- ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
- (type == PCI_EXP_TYPE_ROOT_PORT || \
- type == PCI_EXP_TYPE_ENDPOINT || \
- type == PCI_EXP_TYPE_LEG_END))
-#define pcie_cap_has_sltctl(type, flags) \
- ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
- ((type == PCI_EXP_TYPE_ROOT_PORT) || \
- (type == PCI_EXP_TYPE_DOWNSTREAM && \
- (flags & PCI_EXP_FLAGS_SLOT))))
-#define pcie_cap_has_rtctl(type, flags) \
- ((flags & PCI_EXP_FLAGS_VERS) > 1 || \
- (type == PCI_EXP_TYPE_ROOT_PORT || \
- type == PCI_EXP_TYPE_RC_EC))
static struct pci_cap_saved_state *pci_find_saved_cap(
struct pci_dev *pci_dev, char cap)
@@ -885,13 +858,11 @@ static struct pci_cap_saved_state *pci_find_saved_cap(
static int pci_save_pcie_state(struct pci_dev *dev)
{
- int pos, i = 0;
+ int i = 0;
struct pci_cap_saved_state *save_state;
u16 *cap;
- u16 flags;
- pos = pci_pcie_cap(dev);
- if (!pos)
+ if (!pci_is_pcie(dev))
return 0;
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
@@ -899,60 +870,37 @@ static int pci_save_pcie_state(struct pci_dev *dev)
dev_err(&dev->dev, "buffer not found in %s\n", __func__);
return -ENOMEM;
}
- cap = (u16 *)&save_state->cap.data[0];
-
- pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags);
- if (pcie_cap_has_devctl(dev->pcie_type, flags))
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &cap[i++]);
- if (pcie_cap_has_lnkctl(dev->pcie_type, flags))
- pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]);
- if (pcie_cap_has_sltctl(dev->pcie_type, flags))
- pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]);
- if (pcie_cap_has_rtctl(dev->pcie_type, flags))
- pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]);
-
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return 0;
+ cap = (u16 *)&save_state->cap.data[0];
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_LNKCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_SLTCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_RTCTL, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_LNKCTL2, &cap[i++]);
+ pcie_capability_read_word(dev, PCI_EXP_SLTCTL2, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_LNKCTL2, &cap[i++]);
- pci_read_config_word(dev, pos + PCI_EXP_SLTCTL2, &cap[i++]);
return 0;
}
static void pci_restore_pcie_state(struct pci_dev *dev)
{
- int i = 0, pos;
+ int i = 0;
struct pci_cap_saved_state *save_state;
u16 *cap;
- u16 flags;
save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!save_state || pos <= 0)
- return;
- cap = (u16 *)&save_state->cap.data[0];
-
- pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &flags);
-
- if (pcie_cap_has_devctl(dev->pcie_type, flags))
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, cap[i++]);
- if (pcie_cap_has_lnkctl(dev->pcie_type, flags))
- pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]);
- if (pcie_cap_has_sltctl(dev->pcie_type, flags))
- pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]);
- if (pcie_cap_has_rtctl(dev->pcie_type, flags))
- pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]);
-
- pos = pci_pcie_cap2(dev);
- if (!pos)
+ if (!save_state)
return;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_LNKCTL2, cap[i++]);
- pci_write_config_word(dev, pos + PCI_EXP_SLTCTL2, cap[i++]);
+ cap = (u16 *)&save_state->cap.data[0];
+ pcie_capability_write_word(dev, PCI_EXP_DEVCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_LNKCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_SLTCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_RTCTL, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_DEVCTL2, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_LNKCTL2, cap[i++]);
+ pcie_capability_write_word(dev, PCI_EXP_SLTCTL2, cap[i++]);
}
@@ -1543,7 +1491,7 @@ void pci_pme_wakeup_bus(struct pci_bus *bus)
/**
* pci_wakeup - Wake up a PCI device
- * @dev: Device to handle.
+ * @pci_dev: Device to handle.
* @ign: ignored parameter
*/
static int pci_wakeup(struct pci_dev *pci_dev, void *ign)
@@ -2067,35 +2015,24 @@ void pci_free_cap_save_buffers(struct pci_dev *dev)
*/
void pci_enable_ari(struct pci_dev *dev)
{
- int pos;
u32 cap;
- u16 ctrl;
struct pci_dev *bridge;
if (pcie_ari_disabled || !pci_is_pcie(dev) || dev->devfn)
return;
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI);
- if (!pos)
+ if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI))
return;
bridge = dev->bus->self;
if (!bridge)
return;
- /* ARI is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(bridge);
- if (!pos)
- return;
-
- pci_read_config_dword(bridge, pos + PCI_EXP_DEVCAP2, &cap);
+ pcie_capability_read_dword(bridge, PCI_EXP_DEVCAP2, &cap);
if (!(cap & PCI_EXP_DEVCAP2_ARI))
return;
- pci_read_config_word(bridge, pos + PCI_EXP_DEVCTL2, &ctrl);
- ctrl |= PCI_EXP_DEVCTL2_ARI;
- pci_write_config_word(bridge, pos + PCI_EXP_DEVCTL2, ctrl);
-
+ pcie_capability_set_word(bridge, PCI_EXP_DEVCTL2, PCI_EXP_DEVCTL2_ARI);
bridge->ari_enabled = 1;
}
@@ -2110,20 +2047,14 @@ void pci_enable_ari(struct pci_dev *dev)
*/
void pci_enable_ido(struct pci_dev *dev, unsigned long type)
{
- int pos;
- u16 ctrl;
+ u16 ctrl = 0;
- /* ID-based Ordering is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
if (type & PCI_EXP_IDO_REQUEST)
ctrl |= PCI_EXP_IDO_REQ_EN;
if (type & PCI_EXP_IDO_COMPLETION)
ctrl |= PCI_EXP_IDO_CMP_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
+ if (ctrl)
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, ctrl);
}
EXPORT_SYMBOL(pci_enable_ido);
@@ -2134,20 +2065,14 @@ EXPORT_SYMBOL(pci_enable_ido);
*/
void pci_disable_ido(struct pci_dev *dev, unsigned long type)
{
- int pos;
- u16 ctrl;
+ u16 ctrl = 0;
- /* ID-based Ordering is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
if (type & PCI_EXP_IDO_REQUEST)
- ctrl &= ~PCI_EXP_IDO_REQ_EN;
+ ctrl |= PCI_EXP_IDO_REQ_EN;
if (type & PCI_EXP_IDO_COMPLETION)
- ctrl &= ~PCI_EXP_IDO_CMP_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
+ ctrl |= PCI_EXP_IDO_CMP_EN;
+ if (ctrl)
+ pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, ctrl);
}
EXPORT_SYMBOL(pci_disable_ido);
@@ -2172,17 +2097,11 @@ EXPORT_SYMBOL(pci_disable_ido);
*/
int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type)
{
- int pos;
u32 cap;
u16 ctrl;
int ret;
- /* OBFF is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return -ENOTSUPP;
-
- pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP2, &cap);
+ pcie_capability_read_dword(dev, PCI_EXP_DEVCAP2, &cap);
if (!(cap & PCI_EXP_OBFF_MASK))
return -ENOTSUPP; /* no OBFF support at all */
@@ -2193,7 +2112,7 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type)
return ret;
}
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL2, &ctrl);
if (cap & PCI_EXP_OBFF_WAKE)
ctrl |= PCI_EXP_OBFF_WAKE_EN;
else {
@@ -2211,7 +2130,7 @@ int pci_enable_obff(struct pci_dev *dev, enum pci_obff_signal_type type)
return -ENOTSUPP;
}
}
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
+ pcie_capability_write_word(dev, PCI_EXP_DEVCTL2, ctrl);
return 0;
}
@@ -2225,17 +2144,7 @@ EXPORT_SYMBOL(pci_enable_obff);
*/
void pci_disable_obff(struct pci_dev *dev)
{
- int pos;
- u16 ctrl;
-
- /* OBFF is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
- ctrl &= ~PCI_EXP_OBFF_WAKE_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
+ pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, PCI_EXP_OBFF_WAKE_EN);
}
EXPORT_SYMBOL(pci_disable_obff);
@@ -2248,15 +2157,9 @@ EXPORT_SYMBOL(pci_disable_obff);
*/
static bool pci_ltr_supported(struct pci_dev *dev)
{
- int pos;
u32 cap;
- /* LTR is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return false;
-
- pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP2, &cap);
+ pcie_capability_read_dword(dev, PCI_EXP_DEVCAP2, &cap);
return cap & PCI_EXP_DEVCAP2_LTR;
}
@@ -2273,22 +2176,15 @@ static bool pci_ltr_supported(struct pci_dev *dev)
*/
int pci_enable_ltr(struct pci_dev *dev)
{
- int pos;
- u16 ctrl;
int ret;
- if (!pci_ltr_supported(dev))
- return -ENOTSUPP;
-
- /* LTR is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return -ENOTSUPP;
-
/* Only primary function can enable/disable LTR */
if (PCI_FUNC(dev->devfn) != 0)
return -EINVAL;
+ if (!pci_ltr_supported(dev))
+ return -ENOTSUPP;
+
/* Enable upstream ports first */
if (dev->bus->self) {
ret = pci_enable_ltr(dev->bus->self);
@@ -2296,11 +2192,7 @@ int pci_enable_ltr(struct pci_dev *dev)
return ret;
}
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
- ctrl |= PCI_EXP_LTR_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
-
- return 0;
+ return pcie_capability_set_word(dev, PCI_EXP_DEVCTL2, PCI_EXP_LTR_EN);
}
EXPORT_SYMBOL(pci_enable_ltr);
@@ -2310,24 +2202,14 @@ EXPORT_SYMBOL(pci_enable_ltr);
*/
void pci_disable_ltr(struct pci_dev *dev)
{
- int pos;
- u16 ctrl;
-
- if (!pci_ltr_supported(dev))
- return;
-
- /* LTR is a PCIe cap v2 feature */
- pos = pci_pcie_cap2(dev);
- if (!pos)
- return;
-
/* Only primary function can enable/disable LTR */
if (PCI_FUNC(dev->devfn) != 0)
return;
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL2, &ctrl);
- ctrl &= ~PCI_EXP_LTR_EN;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL2, ctrl);
+ if (!pci_ltr_supported(dev))
+ return;
+
+ pcie_capability_clear_word(dev, PCI_EXP_DEVCTL2, PCI_EXP_LTR_EN);
}
EXPORT_SYMBOL(pci_disable_ltr);
@@ -2410,9 +2292,6 @@ void pci_enable_acs(struct pci_dev *dev)
if (!pci_acs_enable)
return;
- if (!pci_is_pcie(dev))
- return;
-
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ACS);
if (!pos)
return;
@@ -2460,8 +2339,8 @@ bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags)
acs_flags &= (PCI_ACS_RR | PCI_ACS_CR |
PCI_ACS_EC | PCI_ACS_DT);
- if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM ||
- pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM ||
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
pdev->multifunction) {
pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ACS);
if (!pos)
@@ -3177,15 +3056,10 @@ EXPORT_SYMBOL(pci_set_dma_seg_boundary);
static int pcie_flr(struct pci_dev *dev, int probe)
{
int i;
- int pos;
u32 cap;
- u16 status, control;
-
- pos = pci_pcie_cap(dev);
- if (!pos)
- return -ENOTTY;
+ u16 status;
- pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP, &cap);
+ pcie_capability_read_dword(dev, PCI_EXP_DEVCAP, &cap);
if (!(cap & PCI_EXP_DEVCAP_FLR))
return -ENOTTY;
@@ -3197,7 +3071,7 @@ static int pcie_flr(struct pci_dev *dev, int probe)
if (i)
msleep((1 << (i - 1)) * 100);
- pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status);
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
if (!(status & PCI_EXP_DEVSTA_TRPND))
goto clear;
}
@@ -3206,9 +3080,7 @@ static int pcie_flr(struct pci_dev *dev, int probe)
"proceeding with reset anyway\n");
clear:
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &control);
- control |= PCI_EXP_DEVCTL_BCR_FLR;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, control);
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
msleep(100);
@@ -3576,18 +3448,11 @@ EXPORT_SYMBOL(pcix_set_mmrbc);
*/
int pcie_get_readrq(struct pci_dev *dev)
{
- int ret, cap;
u16 ctl;
- cap = pci_pcie_cap(dev);
- if (!cap)
- return -EINVAL;
-
- ret = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
- if (!ret)
- ret = 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12);
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);
- return ret;
+ return 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12);
}
EXPORT_SYMBOL(pcie_get_readrq);
@@ -3601,19 +3466,11 @@ EXPORT_SYMBOL(pcie_get_readrq);
*/
int pcie_set_readrq(struct pci_dev *dev, int rq)
{
- int cap, err = -EINVAL;
- u16 ctl, v;
+ u16 v;
if (rq < 128 || rq > 4096 || !is_power_of_2(rq))
- goto out;
-
- cap = pci_pcie_cap(dev);
- if (!cap)
- goto out;
+ return -EINVAL;
- err = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
- if (err)
- goto out;
/*
* If using the "performance" PCIe config, we clamp the
* read rq size to the max packet size to prevent the
@@ -3631,14 +3488,8 @@ int pcie_set_readrq(struct pci_dev *dev, int rq)
v = (ffs(rq) - 8) << 12;
- if ((ctl & PCI_EXP_DEVCTL_READRQ) != v) {
- ctl &= ~PCI_EXP_DEVCTL_READRQ;
- ctl |= v;
- err = pci_write_config_word(dev, cap + PCI_EXP_DEVCTL, ctl);
- }
-
-out:
- return err;
+ return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_READRQ, v);
}
EXPORT_SYMBOL(pcie_set_readrq);
@@ -3651,18 +3502,11 @@ EXPORT_SYMBOL(pcie_set_readrq);
*/
int pcie_get_mps(struct pci_dev *dev)
{
- int ret, cap;
u16 ctl;
- cap = pci_pcie_cap(dev);
- if (!cap)
- return -EINVAL;
-
- ret = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
- if (!ret)
- ret = 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &ctl);
- return ret;
+ return 128 << ((ctl & PCI_EXP_DEVCTL_PAYLOAD) >> 5);
}
/**
@@ -3675,32 +3519,18 @@ int pcie_get_mps(struct pci_dev *dev)
*/
int pcie_set_mps(struct pci_dev *dev, int mps)
{
- int cap, err = -EINVAL;
- u16 ctl, v;
+ u16 v;
if (mps < 128 || mps > 4096 || !is_power_of_2(mps))
- goto out;
+ return -EINVAL;
v = ffs(mps) - 8;
if (v > dev->pcie_mpss)
- goto out;
+ return -EINVAL;
v <<= 5;
- cap = pci_pcie_cap(dev);
- if (!cap)
- goto out;
-
- err = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl);
- if (err)
- goto out;
-
- if ((ctl & PCI_EXP_DEVCTL_PAYLOAD) != v) {
- ctl &= ~PCI_EXP_DEVCTL_PAYLOAD;
- ctl |= v;
- err = pci_write_config_word(dev, cap + PCI_EXP_DEVCTL, ctl);
- }
-out:
- return err;
+ return pcie_capability_clear_and_set_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_PAYLOAD, v);
}
/**
diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c
index 52229863e9fe..4e24cb8a94ae 100644
--- a/drivers/pci/pcie/aer/aer_inject.c
+++ b/drivers/pci/pcie/aer/aer_inject.c
@@ -288,7 +288,7 @@ static struct pci_dev *pcie_find_root_port(struct pci_dev *dev)
while (1) {
if (!pci_is_pcie(dev))
break;
- if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+ if (pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT)
return dev;
if (!dev->bus->self)
break;
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index 58ad7917553c..030cf12d5468 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -48,7 +48,7 @@ static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
static void aer_error_resume(struct pci_dev *dev);
static pci_ers_result_t aer_root_reset(struct pci_dev *dev);
-static struct pci_error_handlers aer_error_handlers = {
+static const struct pci_error_handlers aer_error_handlers = {
.error_detected = aer_error_detected,
.resume = aer_error_resume,
};
@@ -81,10 +81,11 @@ bool pci_aer_available(void)
static int set_device_error_reporting(struct pci_dev *dev, void *data)
{
bool enable = *((bool *)data);
+ int type = pci_pcie_type(dev);
- if ((dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) ||
- (dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) ||
- (dev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)) {
+ if ((type == PCI_EXP_TYPE_ROOT_PORT) ||
+ (type == PCI_EXP_TYPE_UPSTREAM) ||
+ (type == PCI_EXP_TYPE_DOWNSTREAM)) {
if (enable)
pci_enable_pcie_error_reporting(dev);
else
@@ -121,19 +122,17 @@ static void set_downstream_devices_error_reporting(struct pci_dev *dev,
static void aer_enable_rootport(struct aer_rpc *rpc)
{
struct pci_dev *pdev = rpc->rpd->port;
- int pos, aer_pos;
+ int aer_pos;
u16 reg16;
u32 reg32;
- pos = pci_pcie_cap(pdev);
/* Clear PCIe Capability's Device Status */
- pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
- pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);
+ pcie_capability_read_word(pdev, PCI_EXP_DEVSTA, &reg16);
+ pcie_capability_write_word(pdev, PCI_EXP_DEVSTA, reg16);
/* Disable system error generation in response to error messages */
- pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, &reg16);
- reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
- pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);
+ pcie_capability_clear_word(pdev, PCI_EXP_RTCTL,
+ SYSTEM_ERROR_INTR_ON_MESG_MASK);
aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
/* Clear error status */
@@ -395,9 +394,8 @@ static void aer_error_resume(struct pci_dev *dev)
u16 reg16;
/* Clean up Root device status */
- pos = pci_pcie_cap(dev);
- pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &reg16);
- pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16);
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &reg16);
+ pcie_capability_write_word(dev, PCI_EXP_DEVSTA, reg16);
/* Clean AER Root Error Status */
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c
index 124f20ff11b2..5194a7d41730 100644
--- a/drivers/pci/pcie/aer/aerdrv_acpi.c
+++ b/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -60,7 +60,7 @@ static int aer_hest_parse(struct acpi_hest_header *hest_hdr, void *data)
p = (struct acpi_hest_aer_common *)(hest_hdr + 1);
if (p->flags & ACPI_HEST_GLOBAL) {
if ((pci_is_pcie(info->pci_dev) &&
- info->pci_dev->pcie_type == pcie_type) || bridge)
+ pci_pcie_type(info->pci_dev) == pcie_type) || bridge)
ff = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST);
} else
if (hest_match_pci(p, info->pci_dev))
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
index 0ca053538146..06bad96af415 100644
--- a/drivers/pci/pcie/aer/aerdrv_core.c
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -32,53 +32,28 @@ static bool nosourceid;
module_param(forceload, bool, 0);
module_param(nosourceid, bool, 0);
+#define PCI_EXP_AER_FLAGS (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | \
+ PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE)
+
int pci_enable_pcie_error_reporting(struct pci_dev *dev)
{
- u16 reg16 = 0;
- int pos;
-
if (pcie_aer_get_firmware_first(dev))
return -EIO;
- pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
- if (!pos)
+ if (!pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR))
return -EIO;
- pos = pci_pcie_cap(dev);
- if (!pos)
- return -EIO;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
- reg16 |= (PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE |
- PCI_EXP_DEVCTL_URRE);
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16);
-
- return 0;
+ return pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_AER_FLAGS);
}
EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);
int pci_disable_pcie_error_reporting(struct pci_dev *dev)
{
- u16 reg16 = 0;
- int pos;
-
if (pcie_aer_get_firmware_first(dev))
return -EIO;
- pos = pci_pcie_cap(dev);
- if (!pos)
- return -EIO;
-
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
- reg16 &= ~(PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE |
- PCI_EXP_DEVCTL_URRE);
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16);
-
- return 0;
+ return pcie_capability_clear_word(dev, PCI_EXP_DEVCTL,
+ PCI_EXP_AER_FLAGS);
}
EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);
@@ -151,18 +126,12 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info)
*/
if (atomic_read(&dev->enable_cnt) == 0)
return false;
- pos = pci_pcie_cap(dev);
- if (!pos)
- return false;
/* Check if AER is enabled */
- pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &reg16);
- if (!(reg16 & (
- PCI_EXP_DEVCTL_CERE |
- PCI_EXP_DEVCTL_NFERE |
- PCI_EXP_DEVCTL_FERE |
- PCI_EXP_DEVCTL_URRE)))
+ pcie_capability_read_word(dev, PCI_EXP_DEVCTL, &reg16);
+ if (!(reg16 & PCI_EXP_AER_FLAGS))
return false;
+
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
if (!pos)
return false;
@@ -240,7 +209,7 @@ static bool find_source_device(struct pci_dev *parent,
static int report_error_detected(struct pci_dev *dev, void *data)
{
pci_ers_result_t vote;
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
struct aer_broadcast_data *result_data;
result_data = (struct aer_broadcast_data *) data;
@@ -274,7 +243,7 @@ static int report_error_detected(struct pci_dev *dev, void *data)
static int report_mmio_enabled(struct pci_dev *dev, void *data)
{
pci_ers_result_t vote;
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
struct aer_broadcast_data *result_data;
result_data = (struct aer_broadcast_data *) data;
@@ -292,7 +261,7 @@ static int report_mmio_enabled(struct pci_dev *dev, void *data)
static int report_slot_reset(struct pci_dev *dev, void *data)
{
pci_ers_result_t vote;
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
struct aer_broadcast_data *result_data;
result_data = (struct aer_broadcast_data *) data;
@@ -309,7 +278,7 @@ static int report_slot_reset(struct pci_dev *dev, void *data)
static int report_resume(struct pci_dev *dev, void *data)
{
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
dev->error_state = pci_channel_io_normal;
@@ -465,7 +434,7 @@ static pci_ers_result_t reset_link(struct pci_dev *dev)
if (driver && driver->reset_link) {
status = driver->reset_link(udev);
- } else if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) {
+ } else if (pci_pcie_type(udev) == PCI_EXP_TYPE_DOWNSTREAM) {
status = default_downstream_reset_link(udev);
} else {
dev_printk(KERN_DEBUG, &dev->dev,
@@ -540,14 +509,12 @@ static void do_recovery(struct pci_dev *dev, int severity)
"resume",
report_resume);
- dev_printk(KERN_DEBUG, &dev->dev,
- "AER driver successfully recovered\n");
+ dev_info(&dev->dev, "AER: Device recovery successful\n");
return;
failed:
/* TODO: Should kernel panic here? */
- dev_printk(KERN_DEBUG, &dev->dev,
- "AER driver didn't recover\n");
+ dev_info(&dev->dev, "AER: Device recovery failed\n");
}
/**
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index b500840a143b..213753b283a6 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -125,21 +125,16 @@ static int policy_to_clkpm_state(struct pcie_link_state *link)
static void pcie_set_clkpm_nocheck(struct pcie_link_state *link, int enable)
{
- int pos;
- u16 reg16;
struct pci_dev *child;
struct pci_bus *linkbus = link->pdev->subordinate;
list_for_each_entry(child, &linkbus->devices, bus_list) {
- pos = pci_pcie_cap(child);
- if (!pos)
- return;
- pci_read_config_word(child, pos + PCI_EXP_LNKCTL, &reg16);
if (enable)
- reg16 |= PCI_EXP_LNKCTL_CLKREQ_EN;
+ pcie_capability_set_word(child, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
else
- reg16 &= ~PCI_EXP_LNKCTL_CLKREQ_EN;
- pci_write_config_word(child, pos + PCI_EXP_LNKCTL, reg16);
+ pcie_capability_clear_word(child, PCI_EXP_LNKCTL,
+ PCI_EXP_LNKCTL_CLKREQ_EN);
}
link->clkpm_enabled = !!enable;
}
@@ -157,7 +152,7 @@ static void pcie_set_clkpm(struct pcie_link_state *link, int enable)
static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
{
- int pos, capable = 1, enabled = 1;
+ int capable = 1, enabled = 1;
u32 reg32;
u16 reg16;
struct pci_dev *child;
@@ -165,16 +160,13 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
/* All functions should have the same cap and state, take the worst */
list_for_each_entry(child, &linkbus->devices, bus_list) {
- pos = pci_pcie_cap(child);
- if (!pos)
- return;
- pci_read_config_dword(child, pos + PCI_EXP_LNKCAP, &reg32);
+ pcie_capability_read_dword(child, PCI_EXP_LNKCAP, &reg32);
if (!(reg32 & PCI_EXP_LNKCAP_CLKPM)) {
capable = 0;
enabled = 0;
break;
}
- pci_read_config_word(child, pos + PCI_EXP_LNKCTL, &reg16);
+ pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN))
enabled = 0;
}
@@ -190,7 +182,7 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist)
*/
static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
{
- int ppos, cpos, same_clock = 1;
+ int same_clock = 1;
u16 reg16, parent_reg, child_reg[8];
unsigned long start_jiffies;
struct pci_dev *child, *parent = link->pdev;
@@ -203,46 +195,43 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
BUG_ON(!pci_is_pcie(child));
/* Check downstream component if bit Slot Clock Configuration is 1 */
- cpos = pci_pcie_cap(child);
- pci_read_config_word(child, cpos + PCI_EXP_LNKSTA, &reg16);
+ pcie_capability_read_word(child, PCI_EXP_LNKSTA, &reg16);
if (!(reg16 & PCI_EXP_LNKSTA_SLC))
same_clock = 0;
/* Check upstream component if bit Slot Clock Configuration is 1 */
- ppos = pci_pcie_cap(parent);
- pci_read_config_word(parent, ppos + PCI_EXP_LNKSTA, &reg16);
+ pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &reg16);
if (!(reg16 & PCI_EXP_LNKSTA_SLC))
same_clock = 0;
/* Configure downstream component, all functions */
list_for_each_entry(child, &linkbus->devices, bus_list) {
- cpos = pci_pcie_cap(child);
- pci_read_config_word(child, cpos + PCI_EXP_LNKCTL, &reg16);
+ pcie_capability_read_word(child, PCI_EXP_LNKCTL, &reg16);
child_reg[PCI_FUNC(child->devfn)] = reg16;
if (same_clock)
reg16 |= PCI_EXP_LNKCTL_CCC;
else
reg16 &= ~PCI_EXP_LNKCTL_CCC;
- pci_write_config_word(child, cpos + PCI_EXP_LNKCTL, reg16);
+ pcie_capability_write_word(child, PCI_EXP_LNKCTL, reg16);
}
/* Configure upstream component */
- pci_read_config_word(parent, ppos + PCI_EXP_LNKCTL, &reg16);
+ pcie_capability_read_word(parent, PCI_EXP_LNKCTL, &reg16);
parent_reg = reg16;
if (same_clock)
reg16 |= PCI_EXP_LNKCTL_CCC;
else
reg16 &= ~PCI_EXP_LNKCTL_CCC;
- pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, reg16);
+ pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
/* Retrain link */
reg16 |= PCI_EXP_LNKCTL_RL;
- pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, reg16);
+ pcie_capability_write_word(parent, PCI_EXP_LNKCTL, reg16);
/* Wait for link training end. Break out after waiting for timeout */
start_jiffies = jiffies;
for (;;) {
- pci_read_config_word(parent, ppos + PCI_EXP_LNKSTA, &reg16);
+ pcie_capability_read_word(parent, PCI_EXP_LNKSTA, &reg16);
if (!(reg16 & PCI_EXP_LNKSTA_LT))
break;
if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT))
@@ -255,12 +244,10 @@ static void pcie_aspm_configure_common_clock(struct pcie_link_state *link)
/* Training failed. Restore common clock configurations */
dev_printk(KERN_ERR, &parent->dev,
"ASPM: Could not configure common clock\n");
- list_for_each_entry(child, &linkbus->devices, bus_list) {
- cpos = pci_pcie_cap(child);
- pci_write_config_word(child, cpos + PCI_EXP_LNKCTL,
- child_reg[PCI_FUNC(child->devfn)]);
- }
- pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, parent_reg);
+ list_for_each_entry(child, &linkbus->devices, bus_list)
+ pcie_capability_write_word(child, PCI_EXP_LNKCTL,
+ child_reg[PCI_FUNC(child->devfn)]);
+ pcie_capability_write_word(parent, PCI_EXP_LNKCTL, parent_reg);
}
/* Convert L0s latency encoding to ns */
@@ -305,16 +292,14 @@ struct aspm_register_info {
static void pcie_get_aspm_reg(struct pci_dev *pdev,
struct aspm_register_info *info)
{
- int pos;
u16 reg16;
u32 reg32;
- pos = pci_pcie_cap(pdev);
- pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, &reg32);
+ pcie_capability_read_dword(pdev, PCI_EXP_LNKCAP, &reg32);
info->support = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10;
info->latency_encoding_l0s = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12;
info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15;
- pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
+ pcie_capability_read_word(pdev, PCI_EXP_LNKCTL, &reg16);
info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC;
}
@@ -412,7 +397,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
* do ASPM for now.
*/
list_for_each_entry(child, &linkbus->devices, bus_list) {
- if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
+ if (pci_pcie_type(child) == PCI_EXP_TYPE_PCI_BRIDGE) {
link->aspm_disable = ASPM_STATE_ALL;
break;
}
@@ -420,17 +405,15 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
/* Get and check endpoint acceptable latencies */
list_for_each_entry(child, &linkbus->devices, bus_list) {
- int pos;
u32 reg32, encoding;
struct aspm_latency *acceptable =
&link->acceptable[PCI_FUNC(child->devfn)];
- if (child->pcie_type != PCI_EXP_TYPE_ENDPOINT &&
- child->pcie_type != PCI_EXP_TYPE_LEG_END)
+ if (pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT &&
+ pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END)
continue;
- pos = pci_pcie_cap(child);
- pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, &reg32);
+ pcie_capability_read_dword(child, PCI_EXP_DEVCAP, &reg32);
/* Calculate endpoint L0s acceptable latency */
encoding = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6;
acceptable->l0s = calc_l0s_acceptable(encoding);
@@ -444,13 +427,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist)
static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val)
{
- u16 reg16;
- int pos = pci_pcie_cap(pdev);
-
- pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, &reg16);
- reg16 &= ~0x3;
- reg16 |= val;
- pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16);
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_LNKCTL, 0x3, val);
}
static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state)
@@ -505,7 +482,6 @@ static void free_link_state(struct pcie_link_state *link)
static int pcie_aspm_sanity_check(struct pci_dev *pdev)
{
struct pci_dev *child;
- int pos;
u32 reg32;
/*
@@ -513,8 +489,7 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
* very strange. Disable ASPM for the whole slot
*/
list_for_each_entry(child, &pdev->subordinate->devices, bus_list) {
- pos = pci_pcie_cap(child);
- if (!pos)
+ if (!pci_is_pcie(child))
return -EINVAL;
/*
@@ -530,7 +505,7 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev)
* Disable ASPM for pre-1.1 PCIe device, we follow MS to use
* RBER bit to determine if a function is 1.1 version device
*/
- pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, &reg32);
+ pcie_capability_read_dword(child, PCI_EXP_DEVCAP, &reg32);
if (!(reg32 & PCI_EXP_DEVCAP_RBER) && !aspm_force) {
dev_printk(KERN_INFO, &child->dev, "disabling ASPM"
" on pre-1.1 PCIe device. You can enable it"
@@ -552,7 +527,7 @@ static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev)
INIT_LIST_HEAD(&link->children);
INIT_LIST_HEAD(&link->link);
link->pdev = pdev;
- if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) {
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM) {
struct pcie_link_state *parent;
parent = pdev->bus->parent->self->link_state;
if (!parent) {
@@ -585,12 +560,12 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
if (!pci_is_pcie(pdev) || pdev->link_state)
return;
- if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
- pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+ pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM)
return;
/* VIA has a strange chipset, root port is under a bridge */
- if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT &&
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT &&
pdev->bus->self)
return;
@@ -647,8 +622,8 @@ static void pcie_update_aspm_capable(struct pcie_link_state *root)
if (link->root != root)
continue;
list_for_each_entry(child, &linkbus->devices, bus_list) {
- if ((child->pcie_type != PCI_EXP_TYPE_ENDPOINT) &&
- (child->pcie_type != PCI_EXP_TYPE_LEG_END))
+ if ((pci_pcie_type(child) != PCI_EXP_TYPE_ENDPOINT) &&
+ (pci_pcie_type(child) != PCI_EXP_TYPE_LEG_END))
continue;
pcie_aspm_check_latency(child);
}
@@ -663,8 +638,8 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
if (!pci_is_pcie(pdev) || !parent || !parent->link_state)
return;
- if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
- (parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM))
+ if ((pci_pcie_type(parent) != PCI_EXP_TYPE_ROOT_PORT) &&
+ (pci_pcie_type(parent) != PCI_EXP_TYPE_DOWNSTREAM))
return;
down_read(&pci_bus_sem);
@@ -704,8 +679,8 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev)
if (aspm_disabled || !pci_is_pcie(pdev) || !link)
return;
- if ((pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
- (pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM))
+ if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
return;
/*
* Devices changed PM state, we should recheck if latency
@@ -729,8 +704,8 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
if (aspm_policy != POLICY_POWERSAVE)
return;
- if ((pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
- (pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM))
+ if ((pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT) &&
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM))
return;
down_read(&pci_bus_sem);
@@ -757,8 +732,8 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem,
if (!pci_is_pcie(pdev))
return;
- if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT ||
- pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT ||
+ pci_pcie_type(pdev) == PCI_EXP_TYPE_DOWNSTREAM)
parent = pdev;
if (!parent || !parent->link_state)
return;
@@ -933,8 +908,8 @@ void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev)
struct pcie_link_state *link_state = pdev->link_state;
if (!pci_is_pcie(pdev) ||
- (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
- pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+ pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
return;
if (link_state->aspm_support)
@@ -950,8 +925,8 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev)
struct pcie_link_state *link_state = pdev->link_state;
if (!pci_is_pcie(pdev) ||
- (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT &&
- pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
+ (pci_pcie_type(pdev) != PCI_EXP_TYPE_ROOT_PORT &&
+ pci_pcie_type(pdev) != PCI_EXP_TYPE_DOWNSTREAM) || !link_state)
return;
if (link_state->aspm_support)
diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c
index 001f1b78f39c..9ca0dc9ffd84 100644
--- a/drivers/pci/pcie/pme.c
+++ b/drivers/pci/pcie/pme.c
@@ -57,17 +57,12 @@ struct pcie_pme_service_data {
*/
void pcie_pme_interrupt_enable(struct pci_dev *dev, bool enable)
{
- int rtctl_pos;
- u16 rtctl;
-
- rtctl_pos = pci_pcie_cap(dev) + PCI_EXP_RTCTL;
-
- pci_read_config_word(dev, rtctl_pos, &rtctl);
if (enable)
- rtctl |= PCI_EXP_RTCTL_PMEIE;
+ pcie_capability_set_word(dev, PCI_EXP_RTCTL,
+ PCI_EXP_RTCTL_PMEIE);
else
- rtctl &= ~PCI_EXP_RTCTL_PMEIE;
- pci_write_config_word(dev, rtctl_pos, rtctl);
+ pcie_capability_clear_word(dev, PCI_EXP_RTCTL,
+ PCI_EXP_RTCTL_PMEIE);
}
/**
@@ -120,7 +115,7 @@ static bool pcie_pme_from_pci_bridge(struct pci_bus *bus, u8 devfn)
if (!dev)
return false;
- if (pci_is_pcie(dev) && dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) {
+ if (pci_is_pcie(dev) && pci_pcie_type(dev) == PCI_EXP_TYPE_PCI_BRIDGE) {
down_read(&pci_bus_sem);
if (pcie_pme_walk_bus(bus))
found = true;
@@ -226,18 +221,15 @@ static void pcie_pme_work_fn(struct work_struct *work)
struct pcie_pme_service_data *data =
container_of(work, struct pcie_pme_service_data, work);
struct pci_dev *port = data->srv->port;
- int rtsta_pos;
u32 rtsta;
- rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
-
spin_lock_irq(&data->lock);
for (;;) {
if (data->noirq)
break;
- pci_read_config_dword(port, rtsta_pos, &rtsta);
+ pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
if (rtsta & PCI_EXP_RTSTA_PME) {
/*
* Clear PME status of the port. If there are other
@@ -276,17 +268,14 @@ static irqreturn_t pcie_pme_irq(int irq, void *context)
{
struct pci_dev *port;
struct pcie_pme_service_data *data;
- int rtsta_pos;
u32 rtsta;
unsigned long flags;
port = ((struct pcie_device *)context)->port;
data = get_service_data((struct pcie_device *)context);
- rtsta_pos = pci_pcie_cap(port) + PCI_EXP_RTSTA;
-
spin_lock_irqsave(&data->lock, flags);
- pci_read_config_dword(port, rtsta_pos, &rtsta);
+ pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta);
if (!(rtsta & PCI_EXP_RTSTA_PME)) {
spin_unlock_irqrestore(&data->lock, flags);
@@ -335,13 +324,13 @@ static void pcie_pme_mark_devices(struct pci_dev *port)
struct pci_dev *dev;
/* Check if this is a root port event collector. */
- if (port->pcie_type != PCI_EXP_TYPE_RC_EC || !bus)
+ if (pci_pcie_type(port) != PCI_EXP_TYPE_RC_EC || !bus)
return;
down_read(&pci_bus_sem);
list_for_each_entry(dev, &bus->devices, bus_list)
if (pci_is_pcie(dev)
- && dev->pcie_type == PCI_EXP_TYPE_RC_END)
+ && pci_pcie_type(dev) == PCI_EXP_TYPE_RC_END)
pcie_pme_set_native(dev, NULL);
up_read(&pci_bus_sem);
}
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
index 18bf90f748f6..67be55a7f260 100644
--- a/drivers/pci/pcie/portdrv_bus.c
+++ b/drivers/pci/pcie/portdrv_bus.c
@@ -38,7 +38,7 @@ static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
return 0;
if ((driver->port_type != PCIE_ANY_PORT) &&
- (driver->port_type != pciedev->port->pcie_type))
+ (driver->port_type != pci_pcie_type(pciedev->port)))
return 0;
return 1;
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 75915b30ad19..d03a7a39b2d8 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -200,10 +200,13 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
{
int i, irq = -1;
- /* We have to use INTx if MSI cannot be used for PCIe PME or pciehp. */
+ /*
+ * If MSI cannot be used for PCIe PME or hotplug, we have to use
+ * INTx or other interrupts, e.g. system shared interrupt.
+ */
if (((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) ||
((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())) {
- if (dev->pin)
+ if (dev->irq)
irq = dev->irq;
goto no_msi;
}
@@ -212,8 +215,12 @@ static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
if (!pcie_port_enable_msix(dev, irqs, mask))
return 0;
- /* We're not going to use MSI-X, so try MSI and fall back to INTx */
- if (!pci_enable_msi(dev) || dev->pin)
+ /*
+ * We're not going to use MSI-X, so try MSI and fall back to INTx.
+ * If neither MSI/MSI-X nor INTx available, try other interrupt. On
+ * some platforms, root port doesn't support MSI/MSI-X/INTx in RC mode.
+ */
+ if (!pci_enable_msi(dev) || dev->irq)
irq = dev->irq;
no_msi:
@@ -246,8 +253,7 @@ static void cleanup_service_irqs(struct pci_dev *dev)
*/
static int get_port_device_capability(struct pci_dev *dev)
{
- int services = 0, pos;
- u16 reg16;
+ int services = 0;
u32 reg32;
int cap_mask = 0;
int err;
@@ -265,11 +271,9 @@ static int get_port_device_capability(struct pci_dev *dev)
return 0;
}
- pos = pci_pcie_cap(dev);
- pci_read_config_word(dev, pos + PCI_EXP_FLAGS, &reg16);
/* Hot-Plug Capable */
- if ((cap_mask & PCIE_PORT_SERVICE_HP) && (reg16 & PCI_EXP_FLAGS_SLOT)) {
- pci_read_config_dword(dev, pos + PCI_EXP_SLTCAP, &reg32);
+ if (cap_mask & PCIE_PORT_SERVICE_HP) {
+ pcie_capability_read_dword(dev, PCI_EXP_SLTCAP, &reg32);
if (reg32 & PCI_EXP_SLTCAP_HPC) {
services |= PCIE_PORT_SERVICE_HP;
/*
@@ -277,10 +281,8 @@ static int get_port_device_capability(struct pci_dev *dev)
* enabled by the BIOS and the hot-plug service driver
* is not loaded.
*/
- pos += PCI_EXP_SLTCTL;
- pci_read_config_word(dev, pos, &reg16);
- reg16 &= ~(PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
- pci_write_config_word(dev, pos, reg16);
+ pcie_capability_clear_word(dev, PCI_EXP_SLTCTL,
+ PCI_EXP_SLTCTL_CCIE | PCI_EXP_SLTCTL_HPIE);
}
}
/* AER capable */
@@ -298,7 +300,7 @@ static int get_port_device_capability(struct pci_dev *dev)
services |= PCIE_PORT_SERVICE_VC;
/* Root ports are capable of generating PME too */
if ((cap_mask & PCIE_PORT_SERVICE_PME)
- && dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) {
+ && pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
services |= PCIE_PORT_SERVICE_PME;
/*
* Disable PME interrupt on this port in case it's been enabled
@@ -336,7 +338,7 @@ static int pcie_device_init(struct pci_dev *pdev, int service, int irq)
device->release = release_pcie_device; /* callback to free pcie dev */
dev_set_name(device, "%s:pcie%02x",
pci_name(pdev),
- get_descriptor_id(pdev->pcie_type, service));
+ get_descriptor_id(pci_pcie_type(pdev), service));
device->parent = &pdev->dev;
device_enable_async_suspend(device);
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index e76b44777dbf..0761d90ca279 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -64,14 +64,7 @@ __setup("pcie_ports=", pcie_port_setup);
*/
void pcie_clear_root_pme_status(struct pci_dev *dev)
{
- int rtsta_pos;
- u32 rtsta;
-
- rtsta_pos = pci_pcie_cap(dev) + PCI_EXP_RTSTA;
-
- pci_read_config_dword(dev, rtsta_pos, &rtsta);
- rtsta |= PCI_EXP_RTSTA_PME;
- pci_write_config_dword(dev, rtsta_pos, rtsta);
+ pcie_capability_set_dword(dev, PCI_EXP_RTSTA, PCI_EXP_RTSTA_PME);
}
static int pcie_portdrv_restore_config(struct pci_dev *dev)
@@ -95,7 +88,7 @@ static int pcie_port_resume_noirq(struct device *dev)
* which breaks ACPI-based runtime wakeup on PCI Express, so clear those
* bits now just in case (shouldn't hurt).
*/
- if(pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+ if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)
pcie_clear_root_pme_status(pdev);
return 0;
}
@@ -195,9 +188,9 @@ static int __devinit pcie_portdrv_probe(struct pci_dev *dev,
int status;
if (!pci_is_pcie(dev) ||
- ((dev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) &&
- (dev->pcie_type != PCI_EXP_TYPE_UPSTREAM) &&
- (dev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)))
+ ((pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT) &&
+ (pci_pcie_type(dev) != PCI_EXP_TYPE_UPSTREAM) &&
+ (pci_pcie_type(dev) != PCI_EXP_TYPE_DOWNSTREAM)))
return -ENODEV;
if (!dev->irq && dev->pin) {
@@ -385,11 +378,11 @@ static const struct pci_device_id port_pci_ids[] = { {
};
MODULE_DEVICE_TABLE(pci, port_pci_ids);
-static struct pci_error_handlers pcie_portdrv_err_handler = {
- .error_detected = pcie_portdrv_error_detected,
- .mmio_enabled = pcie_portdrv_mmio_enabled,
- .slot_reset = pcie_portdrv_slot_reset,
- .resume = pcie_portdrv_err_resume,
+static const struct pci_error_handlers pcie_portdrv_err_handler = {
+ .error_detected = pcie_portdrv_error_detected,
+ .mmio_enabled = pcie_portdrv_mmio_enabled,
+ .slot_reset = pcie_portdrv_slot_reset,
+ .resume = pcie_portdrv_err_resume,
};
static struct pci_driver pcie_portdriver = {
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index 9f8a6b79a8ec..ec909afa90b6 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -606,10 +606,10 @@ static void pci_set_bus_speed(struct pci_bus *bus)
u32 linkcap;
u16 linksta;
- pci_read_config_dword(bridge, pos + PCI_EXP_LNKCAP, &linkcap);
+ pcie_capability_read_dword(bridge, PCI_EXP_LNKCAP, &linkcap);
bus->max_bus_speed = pcie_link_speed[linkcap & 0xf];
- pci_read_config_word(bridge, pos + PCI_EXP_LNKSTA, &linksta);
+ pcie_capability_read_word(bridge, PCI_EXP_LNKSTA, &linksta);
pcie_update_link_speed(bus, linksta);
}
}
@@ -729,8 +729,10 @@ int __devinit pci_scan_bridge(struct pci_bus *bus, struct pci_dev *dev, int max,
/* Check if setup is sensible at all */
if (!pass &&
- (primary != bus->number || secondary <= bus->number)) {
- dev_dbg(&dev->dev, "bus configuration invalid, reconfiguring\n");
+ (primary != bus->number || secondary <= bus->number ||
+ secondary > subordinate)) {
+ dev_info(&dev->dev, "bridge configuration invalid ([bus %02x-%02x]), reconfiguring\n",
+ secondary, subordinate);
broken = 1;
}
@@ -932,24 +934,16 @@ void set_pcie_port_type(struct pci_dev *pdev)
pdev->is_pcie = 1;
pdev->pcie_cap = pos;
pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
- pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4;
+ pdev->pcie_flags_reg = reg16;
pci_read_config_word(pdev, pos + PCI_EXP_DEVCAP, &reg16);
pdev->pcie_mpss = reg16 & PCI_EXP_DEVCAP_PAYLOAD;
}
void set_pcie_hotplug_bridge(struct pci_dev *pdev)
{
- int pos;
- u16 reg16;
u32 reg32;
- pos = pci_pcie_cap(pdev);
- if (!pos)
- return;
- pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, &reg16);
- if (!(reg16 & PCI_EXP_FLAGS_SLOT))
- return;
- pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &reg32);
+ pcie_capability_read_dword(pdev, PCI_EXP_SLTCAP, &reg32);
if (reg32 & PCI_EXP_SLTCAP_HPC)
pdev->is_hotplug_bridge = 1;
}
@@ -1163,8 +1157,7 @@ int pci_cfg_space_size(struct pci_dev *dev)
if (class == PCI_CLASS_BRIDGE_HOST)
return pci_cfg_space_size_ext(dev);
- pos = pci_pcie_cap(dev);
- if (!pos) {
+ if (!pci_is_pcie(dev)) {
pos = pci_find_capability(dev, PCI_CAP_ID_PCIX);
if (!pos)
goto fail;
@@ -1386,9 +1379,9 @@ static int only_one_child(struct pci_bus *bus)
if (!parent || !pci_is_pcie(parent))
return 0;
- if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT)
+ if (pci_pcie_type(parent) == PCI_EXP_TYPE_ROOT_PORT)
return 1;
- if (parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM &&
+ if (pci_pcie_type(parent) == PCI_EXP_TYPE_DOWNSTREAM &&
!pci_has_flag(PCI_SCAN_ALL_PCIE_DEVS))
return 1;
return 0;
@@ -1465,7 +1458,7 @@ static int pcie_find_smpss(struct pci_dev *dev, void *data)
*/
if (dev->is_hotplug_bridge && (!list_is_singular(&dev->bus->devices) ||
(dev->bus->self &&
- dev->bus->self->pcie_type != PCI_EXP_TYPE_ROOT_PORT)))
+ pci_pcie_type(dev->bus->self) != PCI_EXP_TYPE_ROOT_PORT)))
*smpss = 0;
if (*smpss > dev->pcie_mpss)
@@ -1481,7 +1474,8 @@ static void pcie_write_mps(struct pci_dev *dev, int mps)
if (pcie_bus_config == PCIE_BUS_PERFORMANCE) {
mps = 128 << dev->pcie_mpss;
- if (dev->pcie_type != PCI_EXP_TYPE_ROOT_PORT && dev->bus->self)
+ if (pci_pcie_type(dev) != PCI_EXP_TYPE_ROOT_PORT &&
+ dev->bus->self)
/* For "Performance", the assumption is made that
* downstream communication will never be larger than
* the MRRS. So, the MPS only needs to be configured
@@ -1756,11 +1750,6 @@ int pci_bus_insert_busn_res(struct pci_bus *b, int bus, int bus_max)
"busn_res: can not insert %pR under %s%pR (conflicts with %s %pR)\n",
res, pci_is_root_bus(b) ? "domain " : "",
parent_res, conflict->name, conflict);
- else
- dev_printk(KERN_DEBUG, &b->dev,
- "busn_res: %pR is inserted under %s%pR\n",
- res, pci_is_root_bus(b) ? "domain " : "",
- parent_res);
return conflict == NULL;
}
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 27911b55c2a5..eb907a8faf2a 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -434,25 +434,6 @@ int pci_proc_detach_device(struct pci_dev *dev)
return 0;
}
-#if 0
-int pci_proc_attach_bus(struct pci_bus* bus)
-{
- struct proc_dir_entry *de = bus->procdir;
-
- if (!proc_initialized)
- return -EACCES;
-
- if (!de) {
- char name[16];
- sprintf(name, "%02x", bus->number);
- de = bus->procdir = proc_mkdir(name, proc_bus_pci_dir);
- if (!de)
- return -ENOMEM;
- }
- return 0;
-}
-#endif /* 0 */
-
int pci_proc_detach_bus(struct pci_bus* bus)
{
struct proc_dir_entry *de = bus->procdir;
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 51553179e967..7a451ff56ecc 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -3081,17 +3081,36 @@ static int reset_intel_generic_dev(struct pci_dev *dev, int probe)
static int reset_intel_82599_sfp_virtfn(struct pci_dev *dev, int probe)
{
- int pos;
+ int i;
+ u16 status;
- pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
- if (!pos)
- return -ENOTTY;
+ /*
+ * http://www.intel.com/content/dam/doc/datasheet/82599-10-gbe-controller-datasheet.pdf
+ *
+ * The 82599 supports FLR on VFs, but FLR support is reported only
+ * in the PF DEVCAP (sec 9.3.10.4), not in the VF DEVCAP (sec 9.5).
+ * Therefore, we can't use pcie_flr(), which checks the VF DEVCAP.
+ */
if (probe)
return 0;
- pci_write_config_word(dev, pos + PCI_EXP_DEVCTL,
- PCI_EXP_DEVCTL_BCR_FLR);
+ /* Wait for Transaction Pending bit clean */
+ for (i = 0; i < 4; i++) {
+ if (i)
+ msleep((1 << (i - 1)) * 100);
+
+ pcie_capability_read_word(dev, PCI_EXP_DEVSTA, &status);
+ if (!(status & PCI_EXP_DEVSTA_TRPND))
+ goto clear;
+ }
+
+ dev_err(&dev->dev, "transaction is not cleared; "
+ "proceeding with reset anyway\n");
+
+clear:
+ pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR);
+
msleep(100);
return 0;
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 04a4861b4749..513972f3ed13 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -32,152 +32,82 @@ static void pci_stop_dev(struct pci_dev *dev)
static void pci_destroy_dev(struct pci_dev *dev)
{
- /* Remove the device from the device lists, and prevent any further
- * list accesses from this device */
down_write(&pci_bus_sem);
list_del(&dev->bus_list);
- dev->bus_list.next = dev->bus_list.prev = NULL;
up_write(&pci_bus_sem);
pci_free_resources(dev);
pci_dev_put(dev);
}
-/**
- * pci_remove_device_safe - remove an unused hotplug device
- * @dev: the device to remove
- *
- * Delete the device structure from the device lists and
- * notify userspace (/sbin/hotplug), but only if the device
- * in question is not being used by a driver.
- * Returns 0 on success.
- */
-#if 0
-int pci_remove_device_safe(struct pci_dev *dev)
-{
- if (pci_dev_driver(dev))
- return -EBUSY;
- pci_destroy_dev(dev);
- return 0;
-}
-#endif /* 0 */
-
-void pci_remove_bus(struct pci_bus *pci_bus)
+void pci_remove_bus(struct pci_bus *bus)
{
- pci_proc_detach_bus(pci_bus);
+ pci_proc_detach_bus(bus);
down_write(&pci_bus_sem);
- list_del(&pci_bus->node);
- pci_bus_release_busn_res(pci_bus);
+ list_del(&bus->node);
+ pci_bus_release_busn_res(bus);
up_write(&pci_bus_sem);
- if (!pci_bus->is_added)
+ if (!bus->is_added)
return;
- pci_remove_legacy_files(pci_bus);
- device_unregister(&pci_bus->dev);
+ pci_remove_legacy_files(bus);
+ device_unregister(&bus->dev);
}
EXPORT_SYMBOL(pci_remove_bus);
-static void __pci_remove_behind_bridge(struct pci_dev *dev);
-/**
- * pci_stop_and_remove_bus_device - remove a PCI device and any children
- * @dev: the device to remove
- *
- * Remove a PCI device from the device lists, informing the drivers
- * that the device has been removed. We also remove any subordinate
- * buses and children in a depth-first manner.
- *
- * For each device we remove, delete the device structure from the
- * device lists, remove the /proc entry, and notify userspace
- * (/sbin/hotplug).
- */
-void __pci_remove_bus_device(struct pci_dev *dev)
+static void pci_stop_bus_device(struct pci_dev *dev)
{
- if (dev->subordinate) {
- struct pci_bus *b = dev->subordinate;
+ struct pci_bus *bus = dev->subordinate;
+ struct pci_dev *child, *tmp;
- __pci_remove_behind_bridge(dev);
- pci_remove_bus(b);
- dev->subordinate = NULL;
+ /*
+ * Stopping an SR-IOV PF device removes all the associated VFs,
+ * which will update the bus->devices list and confuse the
+ * iterator. Therefore, iterate in reverse so we remove the VFs
+ * first, then the PF.
+ */
+ if (bus) {
+ list_for_each_entry_safe_reverse(child, tmp,
+ &bus->devices, bus_list)
+ pci_stop_bus_device(child);
}
- pci_destroy_dev(dev);
-}
-EXPORT_SYMBOL(__pci_remove_bus_device);
-
-void pci_stop_and_remove_bus_device(struct pci_dev *dev)
-{
- pci_stop_bus_device(dev);
- __pci_remove_bus_device(dev);
+ pci_stop_dev(dev);
}
-static void __pci_remove_behind_bridge(struct pci_dev *dev)
+static void pci_remove_bus_device(struct pci_dev *dev)
{
- struct list_head *l, *n;
+ struct pci_bus *bus = dev->subordinate;
+ struct pci_dev *child, *tmp;
- if (dev->subordinate)
- list_for_each_safe(l, n, &dev->subordinate->devices)
- __pci_remove_bus_device(pci_dev_b(l));
-}
+ if (bus) {
+ list_for_each_entry_safe(child, tmp,
+ &bus->devices, bus_list)
+ pci_remove_bus_device(child);
-static void pci_stop_behind_bridge(struct pci_dev *dev)
-{
- struct list_head *l, *n;
+ pci_remove_bus(bus);
+ dev->subordinate = NULL;
+ }
- if (dev->subordinate)
- list_for_each_safe(l, n, &dev->subordinate->devices)
- pci_stop_bus_device(pci_dev_b(l));
+ pci_destroy_dev(dev);
}
/**
- * pci_stop_and_remove_behind_bridge - stop and remove all devices behind
- * a PCI bridge
- * @dev: PCI bridge device
+ * pci_stop_and_remove_bus_device - remove a PCI device and any children
+ * @dev: the device to remove
*
- * Remove all devices on the bus, except for the parent bridge.
- * This also removes any child buses, and any devices they may
- * contain in a depth-first manner.
- */
-void pci_stop_and_remove_behind_bridge(struct pci_dev *dev)
-{
- pci_stop_behind_bridge(dev);
- __pci_remove_behind_bridge(dev);
-}
-
-static void pci_stop_bus_devices(struct pci_bus *bus)
-{
- struct list_head *l, *n;
-
- /*
- * VFs could be removed by pci_stop_and_remove_bus_device() in the
- * pci_stop_bus_devices() code path for PF.
- * aka, bus->devices get updated in the process.
- * but VFs are inserted after PFs when SRIOV is enabled for PF,
- * We can iterate the list backwards to get prev valid PF instead
- * of removed VF.
- */
- list_for_each_prev_safe(l, n, &bus->devices) {
- struct pci_dev *dev = pci_dev_b(l);
- pci_stop_bus_device(dev);
- }
-}
-
-/**
- * pci_stop_bus_device - stop a PCI device and any children
- * @dev: the device to stop
+ * Remove a PCI device from the device lists, informing the drivers
+ * that the device has been removed. We also remove any subordinate
+ * buses and children in a depth-first manner.
*
- * Stop a PCI device (detach the driver, remove from the global list
- * and so on). This also stop any subordinate buses and children in a
- * depth-first manner.
+ * For each device we remove, delete the device structure from the
+ * device lists, remove the /proc entry, and notify userspace
+ * (/sbin/hotplug).
*/
-void pci_stop_bus_device(struct pci_dev *dev)
+void pci_stop_and_remove_bus_device(struct pci_dev *dev)
{
- if (dev->subordinate)
- pci_stop_bus_devices(dev->subordinate);
-
- pci_stop_dev(dev);
+ pci_stop_bus_device(dev);
+ pci_remove_bus_device(dev);
}
-
EXPORT_SYMBOL(pci_stop_and_remove_bus_device);
-EXPORT_SYMBOL(pci_stop_and_remove_behind_bridge);
-EXPORT_SYMBOL_GPL(pci_stop_bus_device);
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
index 48ebdb237f3f..0b3037ab8b93 100644
--- a/drivers/pci/rom.c
+++ b/drivers/pci/rom.c
@@ -167,44 +167,6 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
return rom;
}
-#if 0
-/**
- * pci_map_rom_copy - map a PCI ROM to kernel space, create a copy
- * @pdev: pointer to pci device struct
- * @size: pointer to receive size of pci window over ROM
- *
- * Return: kernel virtual pointer to image of ROM
- *
- * Map a PCI ROM into kernel space. If ROM is boot video ROM,
- * the shadow BIOS copy will be returned instead of the
- * actual ROM.
- */
-void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
-{
- struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
- void __iomem *rom;
-
- rom = pci_map_rom(pdev, size);
- if (!rom)
- return NULL;
-
- if (res->flags & (IORESOURCE_ROM_COPY | IORESOURCE_ROM_SHADOW |
- IORESOURCE_ROM_BIOS_COPY))
- return rom;
-
- res->start = (unsigned long)kmalloc(*size, GFP_KERNEL);
- if (!res->start)
- return rom;
-
- res->end = res->start + *size;
- memcpy_fromio((void*)(unsigned long)res->start, rom, *size);
- pci_unmap_rom(pdev, rom);
- res->flags |= IORESOURCE_ROM_COPY;
-
- return (void __iomem *)(unsigned long)res->start;
-}
-#endif /* 0 */
-
/**
* pci_unmap_rom - unmap the ROM from kernel space
* @pdev: pointer to pci device struct
@@ -226,27 +188,6 @@ void pci_unmap_rom(struct pci_dev *pdev, void __iomem *rom)
pci_disable_rom(pdev);
}
-#if 0
-/**
- * pci_remove_rom - disable the ROM and remove its sysfs attribute
- * @pdev: pointer to pci device struct
- *
- * Remove the rom file in sysfs and disable ROM decoding.
- */
-void pci_remove_rom(struct pci_dev *pdev)
-{
- struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
-
- if (pci_resource_len(pdev, PCI_ROM_RESOURCE))
- sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr);
- if (!(res->flags & (IORESOURCE_ROM_ENABLE |
- IORESOURCE_ROM_SHADOW |
- IORESOURCE_ROM_BIOS_COPY |
- IORESOURCE_ROM_COPY)))
- pci_disable_rom(pdev);
-}
-#endif /* 0 */
-
/**
* pci_cleanup_rom - free the ROM copy created by pci_map_rom_copy
* @pdev: pointer to pci device struct
diff --git a/drivers/pci/search.c b/drivers/pci/search.c
index 993d4a0a2469..bf969ba58e59 100644
--- a/drivers/pci/search.c
+++ b/drivers/pci/search.c
@@ -41,7 +41,7 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev)
continue;
}
/* PCI device should connect to a PCIe bridge */
- if (pdev->pcie_type != PCI_EXP_TYPE_PCI_BRIDGE) {
+ if (pci_pcie_type(pdev) != PCI_EXP_TYPE_PCI_BRIDGE) {
/* Busted hardware? */
WARN_ON_ONCE(1);
return NULL;
@@ -130,16 +130,14 @@ pci_find_next_bus(const struct pci_bus *from)
* decrement the reference count by calling pci_dev_put().
* If no device is found, %NULL is returned.
*/
-struct pci_dev * pci_get_slot(struct pci_bus *bus, unsigned int devfn)
+struct pci_dev *pci_get_slot(struct pci_bus *bus, unsigned int devfn)
{
- struct list_head *tmp;
struct pci_dev *dev;
WARN_ON(in_interrupt());
down_read(&pci_bus_sem);
- list_for_each(tmp, &bus->devices) {
- dev = pci_dev_b(tmp);
+ list_for_each_entry(dev, &bus->devices, bus_list) {
if (dev->devfn == devfn)
goto out;
}
@@ -245,30 +243,14 @@ struct pci_dev *pci_get_subsys(unsigned int vendor, unsigned int device,
unsigned int ss_vendor, unsigned int ss_device,
struct pci_dev *from)
{
- struct pci_dev *pdev;
- struct pci_device_id *id;
-
- /*
- * pci_find_subsys() can be called on the ide_setup() path,
- * super-early in boot. But the down_read() will enable local
- * interrupts, which can cause some machines to crash. So here we
- * detect and flag that situation and bail out early.
- */
- if (unlikely(no_pci_devices()))
- return NULL;
-
- id = kzalloc(sizeof(*id), GFP_KERNEL);
- if (!id)
- return NULL;
- id->vendor = vendor;
- id->device = device;
- id->subvendor = ss_vendor;
- id->subdevice = ss_device;
-
- pdev = pci_get_dev_by_id(id, from);
- kfree(id);
-
- return pdev;
+ struct pci_device_id id = {
+ .vendor = vendor,
+ .device = device,
+ .subvendor = ss_vendor,
+ .subdevice = ss_device,
+ };
+
+ return pci_get_dev_by_id(&id, from);
}
/**
@@ -307,19 +289,16 @@ pci_get_device(unsigned int vendor, unsigned int device, struct pci_dev *from)
*/
struct pci_dev *pci_get_class(unsigned int class, struct pci_dev *from)
{
- struct pci_dev *dev;
- struct pci_device_id *id;
-
- id = kzalloc(sizeof(*id), GFP_KERNEL);
- if (!id)
- return NULL;
- id->vendor = id->device = id->subvendor = id->subdevice = PCI_ANY_ID;
- id->class_mask = PCI_ANY_ID;
- id->class = class;
-
- dev = pci_get_dev_by_id(id, from);
- kfree(id);
- return dev;
+ struct pci_device_id id = {
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .class_mask = PCI_ANY_ID,
+ .class = class,
+ };
+
+ return pci_get_dev_by_id(&id, from);
}
/**
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index fb506137aaee..1e808ca338f8 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -697,6 +697,38 @@ static resource_size_t calculate_memsize(resource_size_t size,
return size;
}
+resource_size_t __weak pcibios_window_alignment(struct pci_bus *bus,
+ unsigned long type)
+{
+ return 1;
+}
+
+#define PCI_P2P_DEFAULT_MEM_ALIGN 0x100000 /* 1MiB */
+#define PCI_P2P_DEFAULT_IO_ALIGN 0x1000 /* 4KiB */
+#define PCI_P2P_DEFAULT_IO_ALIGN_1K 0x400 /* 1KiB */
+
+static resource_size_t window_alignment(struct pci_bus *bus,
+ unsigned long type)
+{
+ resource_size_t align = 1, arch_align;
+
+ if (type & IORESOURCE_MEM)
+ align = PCI_P2P_DEFAULT_MEM_ALIGN;
+ else if (type & IORESOURCE_IO) {
+ /*
+ * Per spec, I/O windows are 4K-aligned, but some
+ * bridges have an extension to support 1K alignment.
+ */
+ if (bus->self->io_window_1k)
+ align = PCI_P2P_DEFAULT_IO_ALIGN_1K;
+ else
+ align = PCI_P2P_DEFAULT_IO_ALIGN;
+ }
+
+ arch_align = pcibios_window_alignment(bus, type);
+ return max(align, arch_align);
+}
+
/**
* pbus_size_io() - size the io window of a given bus
*
@@ -717,17 +749,12 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO);
unsigned long size = 0, size0 = 0, size1 = 0;
resource_size_t children_add_size = 0;
- resource_size_t min_align = 4096, align;
+ resource_size_t min_align, io_align, align;
if (!b_res)
return;
- /*
- * Per spec, I/O windows are 4K-aligned, but some bridges have an
- * extension to support 1K alignment.
- */
- if (bus->self->io_window_1k)
- min_align = 1024;
+ io_align = min_align = window_alignment(bus, IORESOURCE_IO);
list_for_each_entry(dev, &bus->devices, bus_list) {
int i;
@@ -754,8 +781,8 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
}
}
- if (min_align > 4096)
- min_align = 4096;
+ if (min_align > io_align)
+ min_align = io_align;
size0 = calculate_iosize(size, min_size, size1,
resource_size(b_res), min_align);
@@ -785,6 +812,28 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size,
}
}
+static inline resource_size_t calculate_mem_align(resource_size_t *aligns,
+ int max_order)
+{
+ resource_size_t align = 0;
+ resource_size_t min_align = 0;
+ int order;
+
+ for (order = 0; order <= max_order; order++) {
+ resource_size_t align1 = 1;
+
+ align1 <<= (order + 20);
+
+ if (!align)
+ min_align = align1;
+ else if (ALIGN(align + min_align, min_align) < align1)
+ min_align = align1 >> 1;
+ align += aligns[order];
+ }
+
+ return min_align;
+}
+
/**
* pbus_size_mem() - size the memory window of a given bus
*
@@ -864,19 +913,9 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask,
children_add_size += get_res_add_size(realloc_head, r);
}
}
- align = 0;
- min_align = 0;
- for (order = 0; order <= max_order; order++) {
- resource_size_t align1 = 1;
- align1 <<= (order + 20);
-
- if (!align)
- min_align = align1;
- else if (ALIGN(align + min_align, min_align) < align1)
- min_align = align1 >> 1;
- align += aligns[order];
- }
+ min_align = calculate_mem_align(aligns, max_order);
+ min_align = max(min_align, window_alignment(bus, b_res->flags & mask));
size0 = calculate_memsize(size, min_size, 0, resource_size(b_res), min_align);
if (children_add_size > add_size)
add_size = children_add_size;
diff --git a/drivers/pci/setup-irq.c b/drivers/pci/setup-irq.c
index eb219a1d16f7..9bd6864ec5d3 100644
--- a/drivers/pci/setup-irq.c
+++ b/drivers/pci/setup-irq.c
@@ -17,8 +17,13 @@
#include <linux/ioport.h>
#include <linux/cache.h>
+void __weak pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+ dev_dbg(&dev->dev, "assigning IRQ %02d\n", irq);
+ pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+}
-static void __init
+static void
pdev_fixup_irq(struct pci_dev *dev,
u8 (*swizzle)(struct pci_dev *, u8 *),
int (*map_irq)(const struct pci_dev *, u8, u8))
@@ -54,7 +59,7 @@ pdev_fixup_irq(struct pci_dev *dev,
pcibios_update_irq(dev, irq);
}
-void __init
+void
pci_fixup_irqs(u8 (*swizzle)(struct pci_dev *, u8 *),
int (*map_irq)(const struct pci_dev *, u8, u8))
{
diff --git a/drivers/pci/xen-pcifront.c b/drivers/pci/xen-pcifront.c
index d6cc62cb4cf7..def8d0b5620c 100644
--- a/drivers/pci/xen-pcifront.c
+++ b/drivers/pci/xen-pcifront.c
@@ -982,7 +982,6 @@ static int pcifront_detach_devices(struct pcifront_device *pdev)
int err = 0;
int i, num_devs;
unsigned int domain, bus, slot, func;
- struct pci_bus *pci_bus;
struct pci_dev *pci_dev;
char str[64];
@@ -1032,13 +1031,8 @@ static int pcifront_detach_devices(struct pcifront_device *pdev)
goto out;
}
- pci_bus = pci_find_bus(domain, bus);
- if (!pci_bus) {
- dev_dbg(&pdev->xdev->dev, "Cannot get bus %04x:%02x\n",
- domain, bus);
- continue;
- }
- pci_dev = pci_get_slot(pci_bus, PCI_DEVFN(slot, func));
+ pci_dev = pci_get_domain_bus_and_slot(domain, bus,
+ PCI_DEVFN(slot, func));
if (!pci_dev) {
dev_dbg(&pdev->xdev->dev,
"Cannot get PCI device %04x:%02x:%02x.%d\n",
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
index 24caeaf50529..9d3ac998fc1f 100644
--- a/drivers/pcmcia/cardbus.c
+++ b/drivers/pcmcia/cardbus.c
@@ -105,8 +105,17 @@ int __ref cb_alloc(struct pcmcia_socket *s)
*/
void cb_free(struct pcmcia_socket *s)
{
- struct pci_dev *bridge = s->cb_dev;
+ struct pci_dev *bridge, *dev, *tmp;
+ struct pci_bus *bus;
- if (bridge)
- pci_stop_and_remove_behind_bridge(bridge);
+ bridge = s->cb_dev;
+ if (!bridge)
+ return;
+
+ bus = bridge->subordinate;
+ if (!bus)
+ return;
+
+ list_for_each_entry_safe(dev, tmp, &bus->devices, bus_list)
+ pci_stop_and_remove_bus_device(dev);
}
diff --git a/drivers/pcmcia/pxa2xx_base.c b/drivers/pcmcia/pxa2xx_base.c
index 490bb82b5bdb..cfec9dd18ff5 100644
--- a/drivers/pcmcia/pxa2xx_base.c
+++ b/drivers/pcmcia/pxa2xx_base.c
@@ -297,7 +297,7 @@ static int pxa2xx_drv_pcmcia_probe(struct platform_device *dev)
}
clk = clk_get(&dev->dev, NULL);
- if (!clk)
+ if (IS_ERR(clk))
return -ENODEV;
pxa2xx_drv_pcmcia_ops(ops);
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 54e3588bef62..34e94c7f68ca 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -145,6 +145,15 @@ config PINCTRL_COH901
COH 901 335 and COH 901 571/3. They contain 3, 5 or 7
ports of 8 GPIO pins each.
+config PINCTRL_SAMSUNG
+ bool "Samsung pinctrl driver"
+ select PINMUX
+ select PINCONF
+
+config PINCTRL_EXYNOS4
+ bool "Pinctrl driver data for Exynos4 SoC"
+ select PINCTRL_SAMSUNG
+
source "drivers/pinctrl/spear/Kconfig"
endmenu
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index f40b1f81ff2c..6a88113e11d9 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -29,5 +29,7 @@ obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o
obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o
obj-$(CONFIG_PINCTRL_U300) += pinctrl-u300.o
obj-$(CONFIG_PINCTRL_COH901) += pinctrl-coh901.o
+obj-$(CONFIG_PINCTRL_SAMSUNG) += pinctrl-samsung.o
+obj-$(CONFIG_PINCTRL_EXYNOS4) += pinctrl-exynos.o
obj-$(CONFIG_PLAT_SPEAR) += spear/
diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c
new file mode 100644
index 000000000000..21362f48d370
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-exynos.c
@@ -0,0 +1,579 @@
+/*
+ * Exynos specific support for Samsung pinctrl/gpiolib driver with eint support.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Copyright (c) 2012 Linaro Ltd
+ * http://www.linaro.org
+ *
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This file contains the Samsung Exynos specific information required by the
+ * the Samsung pinctrl/gpiolib driver. It also includes the implementation of
+ * external gpio and wakeup interrupt support.
+ */
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irqdomain.h>
+#include <linux/irq.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include <asm/mach/irq.h>
+
+#include "pinctrl-samsung.h"
+#include "pinctrl-exynos.h"
+
+/* list of external wakeup controllers supported */
+static const struct of_device_id exynos_wkup_irq_ids[] = {
+ { .compatible = "samsung,exynos4210-wakeup-eint", },
+};
+
+static void exynos_gpio_irq_unmask(struct irq_data *irqd)
+{
+ struct samsung_pinctrl_drv_data *d = irqd->domain->host_data;
+ struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd);
+ unsigned long reg_mask = d->ctrl->geint_mask + edata->eint_offset;
+ unsigned long mask;
+
+ mask = readl(d->virt_base + reg_mask);
+ mask &= ~(1 << edata->pin);
+ writel(mask, d->virt_base + reg_mask);
+}
+
+static void exynos_gpio_irq_mask(struct irq_data *irqd)
+{
+ struct samsung_pinctrl_drv_data *d = irqd->domain->host_data;
+ struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd);
+ unsigned long reg_mask = d->ctrl->geint_mask + edata->eint_offset;
+ unsigned long mask;
+
+ mask = readl(d->virt_base + reg_mask);
+ mask |= 1 << edata->pin;
+ writel(mask, d->virt_base + reg_mask);
+}
+
+static void exynos_gpio_irq_ack(struct irq_data *irqd)
+{
+ struct samsung_pinctrl_drv_data *d = irqd->domain->host_data;
+ struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd);
+ unsigned long reg_pend = d->ctrl->geint_pend + edata->eint_offset;
+
+ writel(1 << edata->pin, d->virt_base + reg_pend);
+}
+
+static int exynos_gpio_irq_set_type(struct irq_data *irqd, unsigned int type)
+{
+ struct samsung_pinctrl_drv_data *d = irqd->domain->host_data;
+ struct samsung_pin_ctrl *ctrl = d->ctrl;
+ struct exynos_geint_data *edata = irq_data_get_irq_handler_data(irqd);
+ struct samsung_pin_bank *bank = edata->bank;
+ unsigned int shift = EXYNOS_EINT_CON_LEN * edata->pin;
+ unsigned int con, trig_type;
+ unsigned long reg_con = ctrl->geint_con + edata->eint_offset;
+ unsigned int mask;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ trig_type = EXYNOS_EINT_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ trig_type = EXYNOS_EINT_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ trig_type = EXYNOS_EINT_EDGE_BOTH;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ trig_type = EXYNOS_EINT_LEVEL_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ trig_type = EXYNOS_EINT_LEVEL_LOW;
+ break;
+ default:
+ pr_err("unsupported external interrupt type\n");
+ return -EINVAL;
+ }
+
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ __irq_set_handler_locked(irqd->irq, handle_edge_irq);
+ else
+ __irq_set_handler_locked(irqd->irq, handle_level_irq);
+
+ con = readl(d->virt_base + reg_con);
+ con &= ~(EXYNOS_EINT_CON_MASK << shift);
+ con |= trig_type << shift;
+ writel(con, d->virt_base + reg_con);
+
+ reg_con = bank->pctl_offset;
+ shift = edata->pin * bank->func_width;
+ mask = (1 << bank->func_width) - 1;
+
+ con = readl(d->virt_base + reg_con);
+ con &= ~(mask << shift);
+ con |= EXYNOS_EINT_FUNC << shift;
+ writel(con, d->virt_base + reg_con);
+
+ return 0;
+}
+
+/*
+ * irq_chip for gpio interrupts.
+ */
+static struct irq_chip exynos_gpio_irq_chip = {
+ .name = "exynos_gpio_irq_chip",
+ .irq_unmask = exynos_gpio_irq_unmask,
+ .irq_mask = exynos_gpio_irq_mask,
+ .irq_ack = exynos_gpio_irq_ack,
+ .irq_set_type = exynos_gpio_irq_set_type,
+};
+
+/*
+ * given a controller-local external gpio interrupt number, prepare the handler
+ * data for it.
+ */
+static struct exynos_geint_data *exynos_get_eint_data(irq_hw_number_t hw,
+ struct samsung_pinctrl_drv_data *d)
+{
+ struct samsung_pin_bank *bank = d->ctrl->pin_banks;
+ struct exynos_geint_data *eint_data;
+ unsigned int nr_banks = d->ctrl->nr_banks, idx;
+ unsigned int irq_base = 0, eint_offset = 0;
+
+ if (hw >= d->ctrl->nr_gint) {
+ dev_err(d->dev, "unsupported ext-gpio interrupt\n");
+ return NULL;
+ }
+
+ for (idx = 0; idx < nr_banks; idx++, bank++) {
+ if (bank->eint_type != EINT_TYPE_GPIO)
+ continue;
+ if ((hw >= irq_base) && (hw < (irq_base + bank->nr_pins)))
+ break;
+ irq_base += bank->nr_pins;
+ eint_offset += 4;
+ }
+
+ if (idx == nr_banks) {
+ dev_err(d->dev, "pin bank not found for ext-gpio interrupt\n");
+ return NULL;
+ }
+
+ eint_data = devm_kzalloc(d->dev, sizeof(*eint_data), GFP_KERNEL);
+ if (!eint_data) {
+ dev_err(d->dev, "no memory for eint-gpio data\n");
+ return NULL;
+ }
+
+ eint_data->bank = bank;
+ eint_data->pin = hw - irq_base;
+ eint_data->eint_offset = eint_offset;
+ return eint_data;
+}
+
+static int exynos_gpio_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ struct samsung_pinctrl_drv_data *d = h->host_data;
+ struct exynos_geint_data *eint_data;
+
+ eint_data = exynos_get_eint_data(hw, d);
+ if (!eint_data)
+ return -EINVAL;
+
+ irq_set_handler_data(virq, eint_data);
+ irq_set_chip_data(virq, h->host_data);
+ irq_set_chip_and_handler(virq, &exynos_gpio_irq_chip,
+ handle_level_irq);
+ set_irq_flags(virq, IRQF_VALID);
+ return 0;
+}
+
+static void exynos_gpio_irq_unmap(struct irq_domain *h, unsigned int virq)
+{
+ struct samsung_pinctrl_drv_data *d = h->host_data;
+ struct exynos_geint_data *eint_data;
+
+ eint_data = irq_get_handler_data(virq);
+ devm_kfree(d->dev, eint_data);
+}
+
+/*
+ * irq domain callbacks for external gpio interrupt controller.
+ */
+static const struct irq_domain_ops exynos_gpio_irqd_ops = {
+ .map = exynos_gpio_irq_map,
+ .unmap = exynos_gpio_irq_unmap,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static irqreturn_t exynos_eint_gpio_irq(int irq, void *data)
+{
+ struct samsung_pinctrl_drv_data *d = data;
+ struct samsung_pin_ctrl *ctrl = d->ctrl;
+ struct samsung_pin_bank *bank = ctrl->pin_banks;
+ unsigned int svc, group, pin, virq;
+
+ svc = readl(d->virt_base + ctrl->svc);
+ group = EXYNOS_SVC_GROUP(svc);
+ pin = svc & EXYNOS_SVC_NUM_MASK;
+
+ if (!group)
+ return IRQ_HANDLED;
+ bank += (group - 1);
+
+ virq = irq_linear_revmap(d->gpio_irqd, bank->irq_base + pin);
+ if (!virq)
+ return IRQ_NONE;
+ generic_handle_irq(virq);
+ return IRQ_HANDLED;
+}
+
+/*
+ * exynos_eint_gpio_init() - setup handling of external gpio interrupts.
+ * @d: driver data of samsung pinctrl driver.
+ */
+static int exynos_eint_gpio_init(struct samsung_pinctrl_drv_data *d)
+{
+ struct device *dev = d->dev;
+ unsigned int ret;
+
+ if (!d->irq) {
+ dev_err(dev, "irq number not available\n");
+ return -EINVAL;
+ }
+
+ ret = devm_request_irq(dev, d->irq, exynos_eint_gpio_irq,
+ 0, dev_name(dev), d);
+ if (ret) {
+ dev_err(dev, "irq request failed\n");
+ return -ENXIO;
+ }
+
+ d->gpio_irqd = irq_domain_add_linear(dev->of_node, d->ctrl->nr_gint,
+ &exynos_gpio_irqd_ops, d);
+ if (!d->gpio_irqd) {
+ dev_err(dev, "gpio irq domain allocation failed\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static void exynos_wkup_irq_unmask(struct irq_data *irqd)
+{
+ struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd);
+ unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK;
+ unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1);
+ unsigned long reg_mask = d->ctrl->weint_mask + (bank << 2);
+ unsigned long mask;
+
+ mask = readl(d->virt_base + reg_mask);
+ mask &= ~(1 << pin);
+ writel(mask, d->virt_base + reg_mask);
+}
+
+static void exynos_wkup_irq_mask(struct irq_data *irqd)
+{
+ struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd);
+ unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK;
+ unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1);
+ unsigned long reg_mask = d->ctrl->weint_mask + (bank << 2);
+ unsigned long mask;
+
+ mask = readl(d->virt_base + reg_mask);
+ mask |= 1 << pin;
+ writel(mask, d->virt_base + reg_mask);
+}
+
+static void exynos_wkup_irq_ack(struct irq_data *irqd)
+{
+ struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd);
+ unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK;
+ unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1);
+ unsigned long pend = d->ctrl->weint_pend + (bank << 2);
+
+ writel(1 << pin, d->virt_base + pend);
+}
+
+static int exynos_wkup_irq_set_type(struct irq_data *irqd, unsigned int type)
+{
+ struct samsung_pinctrl_drv_data *d = irq_data_get_irq_chip_data(irqd);
+ unsigned int bank = irqd->hwirq / EXYNOS_EINT_MAX_PER_BANK;
+ unsigned int pin = irqd->hwirq & (EXYNOS_EINT_MAX_PER_BANK - 1);
+ unsigned long reg_con = d->ctrl->weint_con + (bank << 2);
+ unsigned long shift = EXYNOS_EINT_CON_LEN * pin;
+ unsigned long con, trig_type;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ trig_type = EXYNOS_EINT_EDGE_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ trig_type = EXYNOS_EINT_EDGE_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ trig_type = EXYNOS_EINT_EDGE_BOTH;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ trig_type = EXYNOS_EINT_LEVEL_HIGH;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ trig_type = EXYNOS_EINT_LEVEL_LOW;
+ break;
+ default:
+ pr_err("unsupported external interrupt type\n");
+ return -EINVAL;
+ }
+
+ if (type & IRQ_TYPE_EDGE_BOTH)
+ __irq_set_handler_locked(irqd->irq, handle_edge_irq);
+ else
+ __irq_set_handler_locked(irqd->irq, handle_level_irq);
+
+ con = readl(d->virt_base + reg_con);
+ con &= ~(EXYNOS_EINT_CON_MASK << shift);
+ con |= trig_type << shift;
+ writel(con, d->virt_base + reg_con);
+ return 0;
+}
+
+/*
+ * irq_chip for wakeup interrupts
+ */
+static struct irq_chip exynos_wkup_irq_chip = {
+ .name = "exynos_wkup_irq_chip",
+ .irq_unmask = exynos_wkup_irq_unmask,
+ .irq_mask = exynos_wkup_irq_mask,
+ .irq_ack = exynos_wkup_irq_ack,
+ .irq_set_type = exynos_wkup_irq_set_type,
+};
+
+/* interrupt handler for wakeup interrupts 0..15 */
+static void exynos_irq_eint0_15(unsigned int irq, struct irq_desc *desc)
+{
+ struct exynos_weint_data *eintd = irq_get_handler_data(irq);
+ struct irq_chip *chip = irq_get_chip(irq);
+ int eint_irq;
+
+ chained_irq_enter(chip, desc);
+ chip->irq_mask(&desc->irq_data);
+
+ if (chip->irq_ack)
+ chip->irq_ack(&desc->irq_data);
+
+ eint_irq = irq_linear_revmap(eintd->domain, eintd->irq);
+ generic_handle_irq(eint_irq);
+ chip->irq_unmask(&desc->irq_data);
+ chained_irq_exit(chip, desc);
+}
+
+static inline void exynos_irq_demux_eint(int irq_base, unsigned long pend,
+ struct irq_domain *domain)
+{
+ unsigned int irq;
+
+ while (pend) {
+ irq = fls(pend) - 1;
+ generic_handle_irq(irq_find_mapping(domain, irq_base + irq));
+ pend &= ~(1 << irq);
+ }
+}
+
+/* interrupt handler for wakeup interrupt 16 */
+static void exynos_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_get_chip(irq);
+ struct exynos_weint_data *eintd = irq_get_handler_data(irq);
+ struct samsung_pinctrl_drv_data *d = eintd->domain->host_data;
+ unsigned long pend;
+ unsigned long mask;
+
+ chained_irq_enter(chip, desc);
+ pend = readl(d->virt_base + d->ctrl->weint_pend + 0x8);
+ mask = readl(d->virt_base + d->ctrl->weint_mask + 0x8);
+ exynos_irq_demux_eint(16, pend & ~mask, eintd->domain);
+ pend = readl(d->virt_base + d->ctrl->weint_pend + 0xC);
+ mask = readl(d->virt_base + d->ctrl->weint_mask + 0xC);
+ exynos_irq_demux_eint(24, pend & ~mask, eintd->domain);
+ chained_irq_exit(chip, desc);
+}
+
+static int exynos_wkup_irq_map(struct irq_domain *h, unsigned int virq,
+ irq_hw_number_t hw)
+{
+ irq_set_chip_and_handler(virq, &exynos_wkup_irq_chip, handle_level_irq);
+ irq_set_chip_data(virq, h->host_data);
+ set_irq_flags(virq, IRQF_VALID);
+ return 0;
+}
+
+/*
+ * irq domain callbacks for external wakeup interrupt controller.
+ */
+static const struct irq_domain_ops exynos_wkup_irqd_ops = {
+ .map = exynos_wkup_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+/*
+ * exynos_eint_wkup_init() - setup handling of external wakeup interrupts.
+ * @d: driver data of samsung pinctrl driver.
+ */
+static int exynos_eint_wkup_init(struct samsung_pinctrl_drv_data *d)
+{
+ struct device *dev = d->dev;
+ struct device_node *wkup_np = NULL;
+ struct device_node *np;
+ struct exynos_weint_data *weint_data;
+ int idx, irq;
+
+ for_each_child_of_node(dev->of_node, np) {
+ if (of_match_node(exynos_wkup_irq_ids, np)) {
+ wkup_np = np;
+ break;
+ }
+ }
+ if (!wkup_np)
+ return -ENODEV;
+
+ d->wkup_irqd = irq_domain_add_linear(wkup_np, d->ctrl->nr_wint,
+ &exynos_wkup_irqd_ops, d);
+ if (!d->wkup_irqd) {
+ dev_err(dev, "wakeup irq domain allocation failed\n");
+ return -ENXIO;
+ }
+
+ weint_data = devm_kzalloc(dev, sizeof(*weint_data) * 17, GFP_KERNEL);
+ if (!weint_data) {
+ dev_err(dev, "could not allocate memory for weint_data\n");
+ return -ENOMEM;
+ }
+
+ irq = irq_of_parse_and_map(wkup_np, 16);
+ if (irq) {
+ weint_data[16].domain = d->wkup_irqd;
+ irq_set_chained_handler(irq, exynos_irq_demux_eint16_31);
+ irq_set_handler_data(irq, &weint_data[16]);
+ } else {
+ dev_err(dev, "irq number for EINT16-32 not found\n");
+ }
+
+ for (idx = 0; idx < 16; idx++) {
+ weint_data[idx].domain = d->wkup_irqd;
+ weint_data[idx].irq = idx;
+
+ irq = irq_of_parse_and_map(wkup_np, idx);
+ if (irq) {
+ irq_set_handler_data(irq, &weint_data[idx]);
+ irq_set_chained_handler(irq, exynos_irq_eint0_15);
+ } else {
+ dev_err(dev, "irq number for eint-%x not found\n", idx);
+ }
+ }
+ return 0;
+}
+
+/* pin banks of exynos4210 pin-controller 0 */
+static struct samsung_pin_bank exynos4210_pin_banks0[] = {
+ EXYNOS_PIN_BANK_EINTG(0x000, EXYNOS4210_GPIO_A0, "gpa0"),
+ EXYNOS_PIN_BANK_EINTG(0x020, EXYNOS4210_GPIO_A1, "gpa1"),
+ EXYNOS_PIN_BANK_EINTG(0x040, EXYNOS4210_GPIO_B, "gpb"),
+ EXYNOS_PIN_BANK_EINTG(0x060, EXYNOS4210_GPIO_C0, "gpc0"),
+ EXYNOS_PIN_BANK_EINTG(0x080, EXYNOS4210_GPIO_C1, "gpc1"),
+ EXYNOS_PIN_BANK_EINTG(0x0A0, EXYNOS4210_GPIO_D0, "gpd0"),
+ EXYNOS_PIN_BANK_EINTG(0x0C0, EXYNOS4210_GPIO_D1, "gpd1"),
+ EXYNOS_PIN_BANK_EINTG(0x0E0, EXYNOS4210_GPIO_E0, "gpe0"),
+ EXYNOS_PIN_BANK_EINTG(0x100, EXYNOS4210_GPIO_E1, "gpe1"),
+ EXYNOS_PIN_BANK_EINTG(0x120, EXYNOS4210_GPIO_E2, "gpe2"),
+ EXYNOS_PIN_BANK_EINTG(0x140, EXYNOS4210_GPIO_E3, "gpe3"),
+ EXYNOS_PIN_BANK_EINTG(0x160, EXYNOS4210_GPIO_E4, "gpe4"),
+ EXYNOS_PIN_BANK_EINTG(0x180, EXYNOS4210_GPIO_F0, "gpf0"),
+ EXYNOS_PIN_BANK_EINTG(0x1A0, EXYNOS4210_GPIO_F1, "gpf1"),
+ EXYNOS_PIN_BANK_EINTG(0x1C0, EXYNOS4210_GPIO_F2, "gpf2"),
+ EXYNOS_PIN_BANK_EINTG(0x1E0, EXYNOS4210_GPIO_F3, "gpf3"),
+};
+
+/* pin banks of exynos4210 pin-controller 1 */
+static struct samsung_pin_bank exynos4210_pin_banks1[] = {
+ EXYNOS_PIN_BANK_EINTG(0x000, EXYNOS4210_GPIO_J0, "gpj0"),
+ EXYNOS_PIN_BANK_EINTG(0x020, EXYNOS4210_GPIO_J1, "gpj1"),
+ EXYNOS_PIN_BANK_EINTG(0x040, EXYNOS4210_GPIO_K0, "gpk0"),
+ EXYNOS_PIN_BANK_EINTG(0x060, EXYNOS4210_GPIO_K1, "gpk1"),
+ EXYNOS_PIN_BANK_EINTG(0x080, EXYNOS4210_GPIO_K2, "gpk2"),
+ EXYNOS_PIN_BANK_EINTG(0x0A0, EXYNOS4210_GPIO_K3, "gpk3"),
+ EXYNOS_PIN_BANK_EINTG(0x0C0, EXYNOS4210_GPIO_L0, "gpl0"),
+ EXYNOS_PIN_BANK_EINTG(0x0E0, EXYNOS4210_GPIO_L1, "gpl1"),
+ EXYNOS_PIN_BANK_EINTG(0x100, EXYNOS4210_GPIO_L2, "gpl2"),
+ EXYNOS_PIN_BANK_EINTN(0x120, EXYNOS4210_GPIO_Y0, "gpy0"),
+ EXYNOS_PIN_BANK_EINTN(0x140, EXYNOS4210_GPIO_Y1, "gpy1"),
+ EXYNOS_PIN_BANK_EINTN(0x160, EXYNOS4210_GPIO_Y2, "gpy2"),
+ EXYNOS_PIN_BANK_EINTN(0x180, EXYNOS4210_GPIO_Y3, "gpy3"),
+ EXYNOS_PIN_BANK_EINTN(0x1A0, EXYNOS4210_GPIO_Y4, "gpy4"),
+ EXYNOS_PIN_BANK_EINTN(0x1C0, EXYNOS4210_GPIO_Y5, "gpy5"),
+ EXYNOS_PIN_BANK_EINTN(0x1E0, EXYNOS4210_GPIO_Y6, "gpy6"),
+ EXYNOS_PIN_BANK_EINTN(0xC00, EXYNOS4210_GPIO_X0, "gpx0"),
+ EXYNOS_PIN_BANK_EINTN(0xC20, EXYNOS4210_GPIO_X1, "gpx1"),
+ EXYNOS_PIN_BANK_EINTN(0xC40, EXYNOS4210_GPIO_X2, "gpx2"),
+ EXYNOS_PIN_BANK_EINTN(0xC60, EXYNOS4210_GPIO_X3, "gpx3"),
+};
+
+/* pin banks of exynos4210 pin-controller 2 */
+static struct samsung_pin_bank exynos4210_pin_banks2[] = {
+ EXYNOS_PIN_BANK_EINTN(0x000, EXYNOS4210_GPIO_Z, "gpz"),
+};
+
+/*
+ * Samsung pinctrl driver data for Exynos4210 SoC. Exynos4210 SoC includes
+ * three gpio/pin-mux/pinconfig controllers.
+ */
+struct samsung_pin_ctrl exynos4210_pin_ctrl[] = {
+ {
+ /* pin-controller instance 0 data */
+ .pin_banks = exynos4210_pin_banks0,
+ .nr_banks = ARRAY_SIZE(exynos4210_pin_banks0),
+ .base = EXYNOS4210_GPIO_A0_START,
+ .nr_pins = EXYNOS4210_GPIOA_NR_PINS,
+ .nr_gint = EXYNOS4210_GPIOA_NR_GINT,
+ .geint_con = EXYNOS_GPIO_ECON_OFFSET,
+ .geint_mask = EXYNOS_GPIO_EMASK_OFFSET,
+ .geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
+ .svc = EXYNOS_SVC_OFFSET,
+ .eint_gpio_init = exynos_eint_gpio_init,
+ .label = "exynos4210-gpio-ctrl0",
+ }, {
+ /* pin-controller instance 1 data */
+ .pin_banks = exynos4210_pin_banks1,
+ .nr_banks = ARRAY_SIZE(exynos4210_pin_banks1),
+ .base = EXYNOS4210_GPIOA_NR_PINS,
+ .nr_pins = EXYNOS4210_GPIOB_NR_PINS,
+ .nr_gint = EXYNOS4210_GPIOB_NR_GINT,
+ .nr_wint = 32,
+ .geint_con = EXYNOS_GPIO_ECON_OFFSET,
+ .geint_mask = EXYNOS_GPIO_EMASK_OFFSET,
+ .geint_pend = EXYNOS_GPIO_EPEND_OFFSET,
+ .weint_con = EXYNOS_WKUP_ECON_OFFSET,
+ .weint_mask = EXYNOS_WKUP_EMASK_OFFSET,
+ .weint_pend = EXYNOS_WKUP_EPEND_OFFSET,
+ .svc = EXYNOS_SVC_OFFSET,
+ .eint_gpio_init = exynos_eint_gpio_init,
+ .eint_wkup_init = exynos_eint_wkup_init,
+ .label = "exynos4210-gpio-ctrl1",
+ }, {
+ /* pin-controller instance 2 data */
+ .pin_banks = exynos4210_pin_banks2,
+ .nr_banks = ARRAY_SIZE(exynos4210_pin_banks2),
+ .base = EXYNOS4210_GPIOA_NR_PINS +
+ EXYNOS4210_GPIOB_NR_PINS,
+ .nr_pins = EXYNOS4210_GPIOC_NR_PINS,
+ .label = "exynos4210-gpio-ctrl2",
+ },
+};
diff --git a/drivers/pinctrl/pinctrl-exynos.h b/drivers/pinctrl/pinctrl-exynos.h
new file mode 100644
index 000000000000..31d0a06174e4
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-exynos.h
@@ -0,0 +1,218 @@
+/*
+ * Exynos specific definitions for Samsung pinctrl and gpiolib driver.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Copyright (c) 2012 Linaro Ltd
+ * http://www.linaro.org
+ *
+ * This file contains the Exynos specific definitions for the Samsung
+ * pinctrl/gpiolib interface drivers.
+ *
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#define EXYNOS_GPIO_START(__gpio) ((__gpio##_START) + (__gpio##_NR))
+
+#define EXYNOS4210_GPIO_A0_NR (8)
+#define EXYNOS4210_GPIO_A1_NR (6)
+#define EXYNOS4210_GPIO_B_NR (8)
+#define EXYNOS4210_GPIO_C0_NR (5)
+#define EXYNOS4210_GPIO_C1_NR (5)
+#define EXYNOS4210_GPIO_D0_NR (4)
+#define EXYNOS4210_GPIO_D1_NR (4)
+#define EXYNOS4210_GPIO_E0_NR (5)
+#define EXYNOS4210_GPIO_E1_NR (8)
+#define EXYNOS4210_GPIO_E2_NR (6)
+#define EXYNOS4210_GPIO_E3_NR (8)
+#define EXYNOS4210_GPIO_E4_NR (8)
+#define EXYNOS4210_GPIO_F0_NR (8)
+#define EXYNOS4210_GPIO_F1_NR (8)
+#define EXYNOS4210_GPIO_F2_NR (8)
+#define EXYNOS4210_GPIO_F3_NR (6)
+#define EXYNOS4210_GPIO_J0_NR (8)
+#define EXYNOS4210_GPIO_J1_NR (5)
+#define EXYNOS4210_GPIO_K0_NR (7)
+#define EXYNOS4210_GPIO_K1_NR (7)
+#define EXYNOS4210_GPIO_K2_NR (7)
+#define EXYNOS4210_GPIO_K3_NR (7)
+#define EXYNOS4210_GPIO_L0_NR (8)
+#define EXYNOS4210_GPIO_L1_NR (3)
+#define EXYNOS4210_GPIO_L2_NR (8)
+#define EXYNOS4210_GPIO_Y0_NR (6)
+#define EXYNOS4210_GPIO_Y1_NR (4)
+#define EXYNOS4210_GPIO_Y2_NR (6)
+#define EXYNOS4210_GPIO_Y3_NR (8)
+#define EXYNOS4210_GPIO_Y4_NR (8)
+#define EXYNOS4210_GPIO_Y5_NR (8)
+#define EXYNOS4210_GPIO_Y6_NR (8)
+#define EXYNOS4210_GPIO_X0_NR (8)
+#define EXYNOS4210_GPIO_X1_NR (8)
+#define EXYNOS4210_GPIO_X2_NR (8)
+#define EXYNOS4210_GPIO_X3_NR (8)
+#define EXYNOS4210_GPIO_Z_NR (7)
+
+enum exynos4210_gpio_xa_start {
+ EXYNOS4210_GPIO_A0_START = 0,
+ EXYNOS4210_GPIO_A1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_A0),
+ EXYNOS4210_GPIO_B_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_A1),
+ EXYNOS4210_GPIO_C0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_B),
+ EXYNOS4210_GPIO_C1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_C0),
+ EXYNOS4210_GPIO_D0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_C1),
+ EXYNOS4210_GPIO_D1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_D0),
+ EXYNOS4210_GPIO_E0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_D1),
+ EXYNOS4210_GPIO_E1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_E0),
+ EXYNOS4210_GPIO_E2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_E1),
+ EXYNOS4210_GPIO_E3_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_E2),
+ EXYNOS4210_GPIO_E4_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_E3),
+ EXYNOS4210_GPIO_F0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_E4),
+ EXYNOS4210_GPIO_F1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_F0),
+ EXYNOS4210_GPIO_F2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_F1),
+ EXYNOS4210_GPIO_F3_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_F2),
+};
+
+enum exynos4210_gpio_xb_start {
+ EXYNOS4210_GPIO_J0_START = 0,
+ EXYNOS4210_GPIO_J1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_J0),
+ EXYNOS4210_GPIO_K0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_J1),
+ EXYNOS4210_GPIO_K1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_K0),
+ EXYNOS4210_GPIO_K2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_K1),
+ EXYNOS4210_GPIO_K3_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_K2),
+ EXYNOS4210_GPIO_L0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_K3),
+ EXYNOS4210_GPIO_L1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_L0),
+ EXYNOS4210_GPIO_L2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_L1),
+ EXYNOS4210_GPIO_Y0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_L2),
+ EXYNOS4210_GPIO_Y1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y0),
+ EXYNOS4210_GPIO_Y2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y1),
+ EXYNOS4210_GPIO_Y3_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y2),
+ EXYNOS4210_GPIO_Y4_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y3),
+ EXYNOS4210_GPIO_Y5_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y4),
+ EXYNOS4210_GPIO_Y6_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y5),
+ EXYNOS4210_GPIO_X0_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_Y6),
+ EXYNOS4210_GPIO_X1_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_X0),
+ EXYNOS4210_GPIO_X2_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_X1),
+ EXYNOS4210_GPIO_X3_START = EXYNOS_GPIO_START(EXYNOS4210_GPIO_X2),
+};
+
+enum exynos4210_gpio_xc_start {
+ EXYNOS4210_GPIO_Z_START = 0,
+};
+
+#define EXYNOS4210_GPIO_A0_IRQ EXYNOS4210_GPIO_A0_START
+#define EXYNOS4210_GPIO_A1_IRQ EXYNOS4210_GPIO_A1_START
+#define EXYNOS4210_GPIO_B_IRQ EXYNOS4210_GPIO_B_START
+#define EXYNOS4210_GPIO_C0_IRQ EXYNOS4210_GPIO_C0_START
+#define EXYNOS4210_GPIO_C1_IRQ EXYNOS4210_GPIO_C1_START
+#define EXYNOS4210_GPIO_D0_IRQ EXYNOS4210_GPIO_D0_START
+#define EXYNOS4210_GPIO_D1_IRQ EXYNOS4210_GPIO_D1_START
+#define EXYNOS4210_GPIO_E0_IRQ EXYNOS4210_GPIO_E0_START
+#define EXYNOS4210_GPIO_E1_IRQ EXYNOS4210_GPIO_E1_START
+#define EXYNOS4210_GPIO_E2_IRQ EXYNOS4210_GPIO_E2_START
+#define EXYNOS4210_GPIO_E3_IRQ EXYNOS4210_GPIO_E3_START
+#define EXYNOS4210_GPIO_E4_IRQ EXYNOS4210_GPIO_E4_START
+#define EXYNOS4210_GPIO_F0_IRQ EXYNOS4210_GPIO_F0_START
+#define EXYNOS4210_GPIO_F1_IRQ EXYNOS4210_GPIO_F1_START
+#define EXYNOS4210_GPIO_F2_IRQ EXYNOS4210_GPIO_F2_START
+#define EXYNOS4210_GPIO_F3_IRQ EXYNOS4210_GPIO_F3_START
+#define EXYNOS4210_GPIO_J0_IRQ EXYNOS4210_GPIO_J0_START
+#define EXYNOS4210_GPIO_J1_IRQ EXYNOS4210_GPIO_J1_START
+#define EXYNOS4210_GPIO_K0_IRQ EXYNOS4210_GPIO_K0_START
+#define EXYNOS4210_GPIO_K1_IRQ EXYNOS4210_GPIO_K1_START
+#define EXYNOS4210_GPIO_K2_IRQ EXYNOS4210_GPIO_K2_START
+#define EXYNOS4210_GPIO_K3_IRQ EXYNOS4210_GPIO_K3_START
+#define EXYNOS4210_GPIO_L0_IRQ EXYNOS4210_GPIO_L0_START
+#define EXYNOS4210_GPIO_L1_IRQ EXYNOS4210_GPIO_L1_START
+#define EXYNOS4210_GPIO_L2_IRQ EXYNOS4210_GPIO_L2_START
+#define EXYNOS4210_GPIO_Z_IRQ EXYNOS4210_GPIO_Z_START
+
+#define EXYNOS4210_GPIOA_NR_PINS EXYNOS_GPIO_START(EXYNOS4210_GPIO_F3)
+#define EXYNOS4210_GPIOA_NR_GINT EXYNOS_GPIO_START(EXYNOS4210_GPIO_F3)
+#define EXYNOS4210_GPIOB_NR_PINS EXYNOS_GPIO_START(EXYNOS4210_GPIO_X3)
+#define EXYNOS4210_GPIOB_NR_GINT EXYNOS_GPIO_START(EXYNOS4210_GPIO_L2)
+#define EXYNOS4210_GPIOC_NR_PINS EXYNOS_GPIO_START(EXYNOS4210_GPIO_Z)
+
+/* External GPIO and wakeup interrupt related definitions */
+#define EXYNOS_GPIO_ECON_OFFSET 0x700
+#define EXYNOS_GPIO_EMASK_OFFSET 0x900
+#define EXYNOS_GPIO_EPEND_OFFSET 0xA00
+#define EXYNOS_WKUP_ECON_OFFSET 0xE00
+#define EXYNOS_WKUP_EMASK_OFFSET 0xF00
+#define EXYNOS_WKUP_EPEND_OFFSET 0xF40
+#define EXYNOS_SVC_OFFSET 0xB08
+#define EXYNOS_EINT_FUNC 0xF
+
+/* helpers to access interrupt service register */
+#define EXYNOS_SVC_GROUP_SHIFT 3
+#define EXYNOS_SVC_GROUP_MASK 0x1f
+#define EXYNOS_SVC_NUM_MASK 7
+#define EXYNOS_SVC_GROUP(x) ((x >> EXYNOS_SVC_GROUP_SHIFT) & \
+ EXYNOS_SVC_GROUP_MASK)
+
+/* Exynos specific external interrupt trigger types */
+#define EXYNOS_EINT_LEVEL_LOW 0
+#define EXYNOS_EINT_LEVEL_HIGH 1
+#define EXYNOS_EINT_EDGE_FALLING 2
+#define EXYNOS_EINT_EDGE_RISING 3
+#define EXYNOS_EINT_EDGE_BOTH 4
+#define EXYNOS_EINT_CON_MASK 0xF
+#define EXYNOS_EINT_CON_LEN 4
+
+#define EXYNOS_EINT_MAX_PER_BANK 8
+#define EXYNOS_EINT_NR_WKUP_EINT
+
+#define EXYNOS_PIN_BANK_EINTN(reg, __gpio, id) \
+ { \
+ .pctl_offset = reg, \
+ .pin_base = (__gpio##_START), \
+ .nr_pins = (__gpio##_NR), \
+ .func_width = 4, \
+ .pud_width = 2, \
+ .drv_width = 2, \
+ .conpdn_width = 2, \
+ .pudpdn_width = 2, \
+ .eint_type = EINT_TYPE_NONE, \
+ .name = id \
+ }
+
+#define EXYNOS_PIN_BANK_EINTG(reg, __gpio, id) \
+ { \
+ .pctl_offset = reg, \
+ .pin_base = (__gpio##_START), \
+ .nr_pins = (__gpio##_NR), \
+ .func_width = 4, \
+ .pud_width = 2, \
+ .drv_width = 2, \
+ .conpdn_width = 2, \
+ .pudpdn_width = 2, \
+ .eint_type = EINT_TYPE_GPIO, \
+ .irq_base = (__gpio##_IRQ), \
+ .name = id \
+ }
+
+/**
+ * struct exynos_geint_data: gpio eint specific data for irq_chip callbacks.
+ * @bank: pin bank from which this gpio interrupt originates.
+ * @pin: pin number within the bank.
+ * @eint_offset: offset to be added to the con/pend/mask register bank base.
+ */
+struct exynos_geint_data {
+ struct samsung_pin_bank *bank;
+ u32 pin;
+ u32 eint_offset;
+};
+
+/**
+ * struct exynos_weint_data: irq specific data for all the wakeup interrupts
+ * generated by the external wakeup interrupt controller.
+ * @domain: irq domain representing the external wakeup interrupts
+ * @irq: interrupt number within the domain.
+ */
+struct exynos_weint_data {
+ struct irq_domain *domain;
+ u32 irq;
+};
diff --git a/drivers/pinctrl/pinctrl-samsung.c b/drivers/pinctrl/pinctrl-samsung.c
new file mode 100644
index 000000000000..dd108a94acf9
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-samsung.c
@@ -0,0 +1,888 @@
+/*
+ * pin-controller/pin-mux/pin-config/gpio-driver for Samsung's SoC's.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Copyright (c) 2012 Linaro Ltd
+ * http://www.linaro.org
+ *
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This driver implements the Samsung pinctrl driver. It supports setting up of
+ * pinmux and pinconf configurations. The gpiolib interface is also included.
+ * External interrupt (gpio and wakeup) support are not included in this driver
+ * but provides extensions to which platform specific implementation of the gpio
+ * and wakeup interrupts can be hooked to.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+
+#include "core.h"
+#include "pinctrl-samsung.h"
+
+#define GROUP_SUFFIX "-grp"
+#define GSUFFIX_LEN sizeof(GROUP_SUFFIX)
+#define FUNCTION_SUFFIX "-mux"
+#define FSUFFIX_LEN sizeof(FUNCTION_SUFFIX)
+
+/* list of all possible config options supported */
+struct pin_config {
+ char *prop_cfg;
+ unsigned int cfg_type;
+} pcfgs[] = {
+ { "samsung,pin-pud", PINCFG_TYPE_PUD },
+ { "samsung,pin-drv", PINCFG_TYPE_DRV },
+ { "samsung,pin-con-pdn", PINCFG_TYPE_CON_PDN },
+ { "samsung,pin-pud-pdn", PINCFG_TYPE_PUD_PDN },
+};
+
+/* check if the selector is a valid pin group selector */
+static int samsung_get_group_count(struct pinctrl_dev *pctldev)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+
+ drvdata = pinctrl_dev_get_drvdata(pctldev);
+ return drvdata->nr_groups;
+}
+
+/* return the name of the group selected by the group selector */
+static const char *samsung_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+
+ drvdata = pinctrl_dev_get_drvdata(pctldev);
+ return drvdata->pin_groups[selector].name;
+}
+
+/* return the pin numbers associated with the specified group */
+static int samsung_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned selector, const unsigned **pins, unsigned *num_pins)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+
+ drvdata = pinctrl_dev_get_drvdata(pctldev);
+ *pins = drvdata->pin_groups[selector].pins;
+ *num_pins = drvdata->pin_groups[selector].num_pins;
+ return 0;
+}
+
+/* create pinctrl_map entries by parsing device tree nodes */
+static int samsung_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np, struct pinctrl_map **maps,
+ unsigned *nmaps)
+{
+ struct device *dev = pctldev->dev;
+ struct pinctrl_map *map;
+ unsigned long *cfg = NULL;
+ char *gname, *fname;
+ int cfg_cnt = 0, map_cnt = 0, idx = 0;
+
+ /* count the number of config options specfied in the node */
+ for (idx = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
+ if (of_find_property(np, pcfgs[idx].prop_cfg, NULL))
+ cfg_cnt++;
+ }
+
+ /*
+ * Find out the number of map entries to create. All the config options
+ * can be accomadated into a single config map entry.
+ */
+ if (cfg_cnt)
+ map_cnt = 1;
+ if (of_find_property(np, "samsung,pin-function", NULL))
+ map_cnt++;
+ if (!map_cnt) {
+ dev_err(dev, "node %s does not have either config or function "
+ "configurations\n", np->name);
+ return -EINVAL;
+ }
+
+ /* Allocate memory for pin-map entries */
+ map = kzalloc(sizeof(*map) * map_cnt, GFP_KERNEL);
+ if (!map) {
+ dev_err(dev, "could not alloc memory for pin-maps\n");
+ return -ENOMEM;
+ }
+ *nmaps = 0;
+
+ /*
+ * Allocate memory for pin group name. The pin group name is derived
+ * from the node name from which these map entries are be created.
+ */
+ gname = kzalloc(strlen(np->name) + GSUFFIX_LEN, GFP_KERNEL);
+ if (!gname) {
+ dev_err(dev, "failed to alloc memory for group name\n");
+ goto free_map;
+ }
+ sprintf(gname, "%s%s", np->name, GROUP_SUFFIX);
+
+ /*
+ * don't have config options? then skip over to creating function
+ * map entries.
+ */
+ if (!cfg_cnt)
+ goto skip_cfgs;
+
+ /* Allocate memory for config entries */
+ cfg = kzalloc(sizeof(*cfg) * cfg_cnt, GFP_KERNEL);
+ if (!cfg) {
+ dev_err(dev, "failed to alloc memory for configs\n");
+ goto free_gname;
+ }
+
+ /* Prepare a list of config settings */
+ for (idx = 0, cfg_cnt = 0; idx < ARRAY_SIZE(pcfgs); idx++) {
+ u32 value;
+ if (!of_property_read_u32(np, pcfgs[idx].prop_cfg, &value))
+ cfg[cfg_cnt++] =
+ PINCFG_PACK(pcfgs[idx].cfg_type, value);
+ }
+
+ /* create the config map entry */
+ map[*nmaps].data.configs.group_or_pin = gname;
+ map[*nmaps].data.configs.configs = cfg;
+ map[*nmaps].data.configs.num_configs = cfg_cnt;
+ map[*nmaps].type = PIN_MAP_TYPE_CONFIGS_GROUP;
+ *nmaps += 1;
+
+skip_cfgs:
+ /* create the function map entry */
+ if (of_find_property(np, "samsung,pin-function", NULL)) {
+ fname = kzalloc(strlen(np->name) + FSUFFIX_LEN, GFP_KERNEL);
+ if (!fname) {
+ dev_err(dev, "failed to alloc memory for func name\n");
+ goto free_cfg;
+ }
+ sprintf(fname, "%s%s", np->name, FUNCTION_SUFFIX);
+
+ map[*nmaps].data.mux.group = gname;
+ map[*nmaps].data.mux.function = fname;
+ map[*nmaps].type = PIN_MAP_TYPE_MUX_GROUP;
+ *nmaps += 1;
+ }
+
+ *maps = map;
+ return 0;
+
+free_cfg:
+ kfree(cfg);
+free_gname:
+ kfree(gname);
+free_map:
+ kfree(map);
+ return -ENOMEM;
+}
+
+/* free the memory allocated to hold the pin-map table */
+static void samsung_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ int idx;
+
+ for (idx = 0; idx < num_maps; idx++) {
+ if (map[idx].type == PIN_MAP_TYPE_MUX_GROUP) {
+ kfree(map[idx].data.mux.function);
+ if (!idx)
+ kfree(map[idx].data.mux.group);
+ } else if (map->type == PIN_MAP_TYPE_CONFIGS_GROUP) {
+ kfree(map[idx].data.configs.configs);
+ if (!idx)
+ kfree(map[idx].data.configs.group_or_pin);
+ }
+ };
+
+ kfree(map);
+}
+
+/* list of pinctrl callbacks for the pinctrl core */
+static struct pinctrl_ops samsung_pctrl_ops = {
+ .get_groups_count = samsung_get_group_count,
+ .get_group_name = samsung_get_group_name,
+ .get_group_pins = samsung_get_group_pins,
+ .dt_node_to_map = samsung_dt_node_to_map,
+ .dt_free_map = samsung_dt_free_map,
+};
+
+/* check if the selector is a valid pin function selector */
+static int samsung_get_functions_count(struct pinctrl_dev *pctldev)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+
+ drvdata = pinctrl_dev_get_drvdata(pctldev);
+ return drvdata->nr_functions;
+}
+
+/* return the name of the pin function specified */
+static const char *samsung_pinmux_get_fname(struct pinctrl_dev *pctldev,
+ unsigned selector)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+
+ drvdata = pinctrl_dev_get_drvdata(pctldev);
+ return drvdata->pmx_functions[selector].name;
+}
+
+/* return the groups associated for the specified function selector */
+static int samsung_pinmux_get_groups(struct pinctrl_dev *pctldev,
+ unsigned selector, const char * const **groups,
+ unsigned * const num_groups)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+
+ drvdata = pinctrl_dev_get_drvdata(pctldev);
+ *groups = drvdata->pmx_functions[selector].groups;
+ *num_groups = drvdata->pmx_functions[selector].num_groups;
+ return 0;
+}
+
+/*
+ * given a pin number that is local to a pin controller, find out the pin bank
+ * and the register base of the pin bank.
+ */
+static void pin_to_reg_bank(struct gpio_chip *gc, unsigned pin,
+ void __iomem **reg, u32 *offset,
+ struct samsung_pin_bank **bank)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+ struct samsung_pin_bank *b;
+
+ drvdata = dev_get_drvdata(gc->dev);
+ b = drvdata->ctrl->pin_banks;
+
+ while ((pin >= b->pin_base) &&
+ ((b->pin_base + b->nr_pins - 1) < pin))
+ b++;
+
+ *reg = drvdata->virt_base + b->pctl_offset;
+ *offset = pin - b->pin_base;
+ if (bank)
+ *bank = b;
+
+ /* some banks have two config registers in a single bank */
+ if (*offset * b->func_width > BITS_PER_LONG)
+ *reg += 4;
+}
+
+/* enable or disable a pinmux function */
+static void samsung_pinmux_setup(struct pinctrl_dev *pctldev, unsigned selector,
+ unsigned group, bool enable)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+ const unsigned int *pins;
+ struct samsung_pin_bank *bank;
+ void __iomem *reg;
+ u32 mask, shift, data, pin_offset, cnt;
+
+ drvdata = pinctrl_dev_get_drvdata(pctldev);
+ pins = drvdata->pin_groups[group].pins;
+
+ /*
+ * for each pin in the pin group selected, program the correspoding pin
+ * pin function number in the config register.
+ */
+ for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++) {
+ pin_to_reg_bank(drvdata->gc, pins[cnt] - drvdata->ctrl->base,
+ &reg, &pin_offset, &bank);
+ mask = (1 << bank->func_width) - 1;
+ shift = pin_offset * bank->func_width;
+
+ data = readl(reg);
+ data &= ~(mask << shift);
+ if (enable)
+ data |= drvdata->pin_groups[group].func << shift;
+ writel(data, reg);
+ }
+}
+
+/* enable a specified pinmux by writing to registers */
+static int samsung_pinmux_enable(struct pinctrl_dev *pctldev, unsigned selector,
+ unsigned group)
+{
+ samsung_pinmux_setup(pctldev, selector, group, true);
+ return 0;
+}
+
+/* disable a specified pinmux by writing to registers */
+static void samsung_pinmux_disable(struct pinctrl_dev *pctldev,
+ unsigned selector, unsigned group)
+{
+ samsung_pinmux_setup(pctldev, selector, group, false);
+}
+
+/*
+ * The calls to gpio_direction_output() and gpio_direction_input()
+ * leads to this function call (via the pinctrl_gpio_direction_{input|output}()
+ * function called from the gpiolib interface).
+ */
+static int samsung_pinmux_gpio_set_direction(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range, unsigned offset, bool input)
+{
+ struct samsung_pin_bank *bank;
+ void __iomem *reg;
+ u32 data, pin_offset, mask, shift;
+
+ pin_to_reg_bank(range->gc, offset, &reg, &pin_offset, &bank);
+ mask = (1 << bank->func_width) - 1;
+ shift = pin_offset * bank->func_width;
+
+ data = readl(reg);
+ data &= ~(mask << shift);
+ if (!input)
+ data |= FUNC_OUTPUT << shift;
+ writel(data, reg);
+ return 0;
+}
+
+/* list of pinmux callbacks for the pinmux vertical in pinctrl core */
+static struct pinmux_ops samsung_pinmux_ops = {
+ .get_functions_count = samsung_get_functions_count,
+ .get_function_name = samsung_pinmux_get_fname,
+ .get_function_groups = samsung_pinmux_get_groups,
+ .enable = samsung_pinmux_enable,
+ .disable = samsung_pinmux_disable,
+ .gpio_set_direction = samsung_pinmux_gpio_set_direction,
+};
+
+/* set or get the pin config settings for a specified pin */
+static int samsung_pinconf_rw(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *config, bool set)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+ struct samsung_pin_bank *bank;
+ void __iomem *reg_base;
+ enum pincfg_type cfg_type = PINCFG_UNPACK_TYPE(*config);
+ u32 data, width, pin_offset, mask, shift;
+ u32 cfg_value, cfg_reg;
+
+ drvdata = pinctrl_dev_get_drvdata(pctldev);
+ pin_to_reg_bank(drvdata->gc, pin - drvdata->ctrl->base, &reg_base,
+ &pin_offset, &bank);
+
+ switch (cfg_type) {
+ case PINCFG_TYPE_PUD:
+ width = bank->pud_width;
+ cfg_reg = PUD_REG;
+ break;
+ case PINCFG_TYPE_DRV:
+ width = bank->drv_width;
+ cfg_reg = DRV_REG;
+ break;
+ case PINCFG_TYPE_CON_PDN:
+ width = bank->conpdn_width;
+ cfg_reg = CONPDN_REG;
+ break;
+ case PINCFG_TYPE_PUD_PDN:
+ width = bank->pudpdn_width;
+ cfg_reg = PUDPDN_REG;
+ break;
+ default:
+ WARN_ON(1);
+ return -EINVAL;
+ }
+
+ mask = (1 << width) - 1;
+ shift = pin_offset * width;
+ data = readl(reg_base + cfg_reg);
+
+ if (set) {
+ cfg_value = PINCFG_UNPACK_VALUE(*config);
+ data &= ~(mask << shift);
+ data |= (cfg_value << shift);
+ writel(data, reg_base + cfg_reg);
+ } else {
+ data >>= shift;
+ data &= mask;
+ *config = PINCFG_PACK(cfg_type, data);
+ }
+ return 0;
+}
+
+/* set the pin config settings for a specified pin */
+static int samsung_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long config)
+{
+ return samsung_pinconf_rw(pctldev, pin, &config, true);
+}
+
+/* get the pin config settings for a specified pin */
+static int samsung_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin,
+ unsigned long *config)
+{
+ return samsung_pinconf_rw(pctldev, pin, config, false);
+}
+
+/* set the pin config settings for a specified pin group */
+static int samsung_pinconf_group_set(struct pinctrl_dev *pctldev,
+ unsigned group, unsigned long config)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+ const unsigned int *pins;
+ unsigned int cnt;
+
+ drvdata = pinctrl_dev_get_drvdata(pctldev);
+ pins = drvdata->pin_groups[group].pins;
+
+ for (cnt = 0; cnt < drvdata->pin_groups[group].num_pins; cnt++)
+ samsung_pinconf_set(pctldev, pins[cnt], config);
+
+ return 0;
+}
+
+/* get the pin config settings for a specified pin group */
+static int samsung_pinconf_group_get(struct pinctrl_dev *pctldev,
+ unsigned int group, unsigned long *config)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+ const unsigned int *pins;
+
+ drvdata = pinctrl_dev_get_drvdata(pctldev);
+ pins = drvdata->pin_groups[group].pins;
+ samsung_pinconf_get(pctldev, pins[0], config);
+ return 0;
+}
+
+/* list of pinconfig callbacks for pinconfig vertical in the pinctrl code */
+static struct pinconf_ops samsung_pinconf_ops = {
+ .pin_config_get = samsung_pinconf_get,
+ .pin_config_set = samsung_pinconf_set,
+ .pin_config_group_get = samsung_pinconf_group_get,
+ .pin_config_group_set = samsung_pinconf_group_set,
+};
+
+/* gpiolib gpio_set callback function */
+static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+ void __iomem *reg;
+ u32 pin_offset, data;
+
+ pin_to_reg_bank(gc, offset, &reg, &pin_offset, NULL);
+ data = readl(reg + DAT_REG);
+ data &= ~(1 << pin_offset);
+ if (value)
+ data |= 1 << pin_offset;
+ writel(data, reg + DAT_REG);
+}
+
+/* gpiolib gpio_get callback function */
+static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+ void __iomem *reg;
+ u32 pin_offset, data;
+
+ pin_to_reg_bank(gc, offset, &reg, &pin_offset, NULL);
+ data = readl(reg + DAT_REG);
+ data >>= pin_offset;
+ data &= 1;
+ return data;
+}
+
+/*
+ * gpiolib gpio_direction_input callback function. The setting of the pin
+ * mux function as 'gpio input' will be handled by the pinctrl susbsystem
+ * interface.
+ */
+static int samsung_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+ return pinctrl_gpio_direction_input(gc->base + offset);
+}
+
+/*
+ * gpiolib gpio_direction_output callback function. The setting of the pin
+ * mux function as 'gpio output' will be handled by the pinctrl susbsystem
+ * interface.
+ */
+static int samsung_gpio_direction_output(struct gpio_chip *gc, unsigned offset,
+ int value)
+{
+ samsung_gpio_set(gc, offset, value);
+ return pinctrl_gpio_direction_output(gc->base + offset);
+}
+
+/*
+ * Parse the pin names listed in the 'samsung,pins' property and convert it
+ * into a list of gpio numbers are create a pin group from it.
+ */
+static int __init samsung_pinctrl_parse_dt_pins(struct platform_device *pdev,
+ struct device_node *cfg_np, struct pinctrl_desc *pctl,
+ unsigned int **pin_list, unsigned int *npins)
+{
+ struct device *dev = &pdev->dev;
+ struct property *prop;
+ struct pinctrl_pin_desc const *pdesc = pctl->pins;
+ unsigned int idx = 0, cnt;
+ const char *pin_name;
+
+ *npins = of_property_count_strings(cfg_np, "samsung,pins");
+ if (*npins < 0) {
+ dev_err(dev, "invalid pin list in %s node", cfg_np->name);
+ return -EINVAL;
+ }
+
+ *pin_list = devm_kzalloc(dev, *npins * sizeof(**pin_list), GFP_KERNEL);
+ if (!*pin_list) {
+ dev_err(dev, "failed to allocate memory for pin list\n");
+ return -ENOMEM;
+ }
+
+ of_property_for_each_string(cfg_np, "samsung,pins", prop, pin_name) {
+ for (cnt = 0; cnt < pctl->npins; cnt++) {
+ if (pdesc[cnt].name) {
+ if (!strcmp(pin_name, pdesc[cnt].name)) {
+ (*pin_list)[idx++] = pdesc[cnt].number;
+ break;
+ }
+ }
+ }
+ if (cnt == pctl->npins) {
+ dev_err(dev, "pin %s not valid in %s node\n",
+ pin_name, cfg_np->name);
+ devm_kfree(dev, *pin_list);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Parse the information about all the available pin groups and pin functions
+ * from device node of the pin-controller. A pin group is formed with all
+ * the pins listed in the "samsung,pins" property.
+ */
+static int __init samsung_pinctrl_parse_dt(struct platform_device *pdev,
+ struct samsung_pinctrl_drv_data *drvdata)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *dev_np = dev->of_node;
+ struct device_node *cfg_np;
+ struct samsung_pin_group *groups, *grp;
+ struct samsung_pmx_func *functions, *func;
+ unsigned *pin_list;
+ unsigned int npins, grp_cnt, func_idx = 0;
+ char *gname, *fname;
+ int ret;
+
+ grp_cnt = of_get_child_count(dev_np);
+ if (!grp_cnt)
+ return -EINVAL;
+
+ groups = devm_kzalloc(dev, grp_cnt * sizeof(*groups), GFP_KERNEL);
+ if (!groups) {
+ dev_err(dev, "failed allocate memory for ping group list\n");
+ return -EINVAL;
+ }
+ grp = groups;
+
+ functions = devm_kzalloc(dev, grp_cnt * sizeof(*functions), GFP_KERNEL);
+ if (!functions) {
+ dev_err(dev, "failed to allocate memory for function list\n");
+ return -EINVAL;
+ }
+ func = functions;
+
+ /*
+ * Iterate over all the child nodes of the pin controller node
+ * and create pin groups and pin function lists.
+ */
+ for_each_child_of_node(dev_np, cfg_np) {
+ u32 function;
+ if (of_find_property(cfg_np, "interrupt-controller", NULL))
+ continue;
+
+ ret = samsung_pinctrl_parse_dt_pins(pdev, cfg_np,
+ &drvdata->pctl, &pin_list, &npins);
+ if (ret)
+ return ret;
+
+ /* derive pin group name from the node name */
+ gname = devm_kzalloc(dev, strlen(cfg_np->name) + GSUFFIX_LEN,
+ GFP_KERNEL);
+ if (!gname) {
+ dev_err(dev, "failed to alloc memory for group name\n");
+ return -ENOMEM;
+ }
+ sprintf(gname, "%s%s", cfg_np->name, GROUP_SUFFIX);
+
+ grp->name = gname;
+ grp->pins = pin_list;
+ grp->num_pins = npins;
+ of_property_read_u32(cfg_np, "samsung,pin-function", &function);
+ grp->func = function;
+ grp++;
+
+ if (!of_find_property(cfg_np, "samsung,pin-function", NULL))
+ continue;
+
+ /* derive function name from the node name */
+ fname = devm_kzalloc(dev, strlen(cfg_np->name) + FSUFFIX_LEN,
+ GFP_KERNEL);
+ if (!fname) {
+ dev_err(dev, "failed to alloc memory for func name\n");
+ return -ENOMEM;
+ }
+ sprintf(fname, "%s%s", cfg_np->name, FUNCTION_SUFFIX);
+
+ func->name = fname;
+ func->groups = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
+ if (!func->groups) {
+ dev_err(dev, "failed to alloc memory for group list "
+ "in pin function");
+ return -ENOMEM;
+ }
+ func->groups[0] = gname;
+ func->num_groups = 1;
+ func++;
+ func_idx++;
+ }
+
+ drvdata->pin_groups = groups;
+ drvdata->nr_groups = grp_cnt;
+ drvdata->pmx_functions = functions;
+ drvdata->nr_functions = func_idx;
+
+ return 0;
+}
+
+/* register the pinctrl interface with the pinctrl subsystem */
+static int __init samsung_pinctrl_register(struct platform_device *pdev,
+ struct samsung_pinctrl_drv_data *drvdata)
+{
+ struct pinctrl_desc *ctrldesc = &drvdata->pctl;
+ struct pinctrl_pin_desc *pindesc, *pdesc;
+ struct samsung_pin_bank *pin_bank;
+ char *pin_names;
+ int pin, bank, ret;
+
+ ctrldesc->name = "samsung-pinctrl";
+ ctrldesc->owner = THIS_MODULE;
+ ctrldesc->pctlops = &samsung_pctrl_ops;
+ ctrldesc->pmxops = &samsung_pinmux_ops;
+ ctrldesc->confops = &samsung_pinconf_ops;
+
+ pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) *
+ drvdata->ctrl->nr_pins, GFP_KERNEL);
+ if (!pindesc) {
+ dev_err(&pdev->dev, "mem alloc for pin descriptors failed\n");
+ return -ENOMEM;
+ }
+ ctrldesc->pins = pindesc;
+ ctrldesc->npins = drvdata->ctrl->nr_pins;
+ ctrldesc->npins = drvdata->ctrl->nr_pins;
+
+ /* dynamically populate the pin number and pin name for pindesc */
+ for (pin = 0, pdesc = pindesc; pin < ctrldesc->npins; pin++, pdesc++)
+ pdesc->number = pin + drvdata->ctrl->base;
+
+ /*
+ * allocate space for storing the dynamically generated names for all
+ * the pins which belong to this pin-controller.
+ */
+ pin_names = devm_kzalloc(&pdev->dev, sizeof(char) * PIN_NAME_LENGTH *
+ drvdata->ctrl->nr_pins, GFP_KERNEL);
+ if (!pin_names) {
+ dev_err(&pdev->dev, "mem alloc for pin names failed\n");
+ return -ENOMEM;
+ }
+
+ /* for each pin, the name of the pin is pin-bank name + pin number */
+ for (bank = 0; bank < drvdata->ctrl->nr_banks; bank++) {
+ pin_bank = &drvdata->ctrl->pin_banks[bank];
+ for (pin = 0; pin < pin_bank->nr_pins; pin++) {
+ sprintf(pin_names, "%s-%d", pin_bank->name, pin);
+ pdesc = pindesc + pin_bank->pin_base + pin;
+ pdesc->name = pin_names;
+ pin_names += PIN_NAME_LENGTH;
+ }
+ }
+
+ drvdata->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, drvdata);
+ if (!drvdata->pctl_dev) {
+ dev_err(&pdev->dev, "could not register pinctrl driver\n");
+ return -EINVAL;
+ }
+
+ drvdata->grange.name = "samsung-pctrl-gpio-range";
+ drvdata->grange.id = 0;
+ drvdata->grange.base = drvdata->ctrl->base;
+ drvdata->grange.npins = drvdata->ctrl->nr_pins;
+ drvdata->grange.gc = drvdata->gc;
+ pinctrl_add_gpio_range(drvdata->pctl_dev, &drvdata->grange);
+
+ ret = samsung_pinctrl_parse_dt(pdev, drvdata);
+ if (ret) {
+ pinctrl_unregister(drvdata->pctl_dev);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* register the gpiolib interface with the gpiolib subsystem */
+static int __init samsung_gpiolib_register(struct platform_device *pdev,
+ struct samsung_pinctrl_drv_data *drvdata)
+{
+ struct gpio_chip *gc;
+ int ret;
+
+ gc = devm_kzalloc(&pdev->dev, sizeof(*gc), GFP_KERNEL);
+ if (!gc) {
+ dev_err(&pdev->dev, "mem alloc for gpio_chip failed\n");
+ return -ENOMEM;
+ }
+
+ drvdata->gc = gc;
+ gc->base = drvdata->ctrl->base;
+ gc->ngpio = drvdata->ctrl->nr_pins;
+ gc->dev = &pdev->dev;
+ gc->set = samsung_gpio_set;
+ gc->get = samsung_gpio_get;
+ gc->direction_input = samsung_gpio_direction_input;
+ gc->direction_output = samsung_gpio_direction_output;
+ gc->label = drvdata->ctrl->label;
+ gc->owner = THIS_MODULE;
+ ret = gpiochip_add(gc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register gpio_chip %s, error "
+ "code: %d\n", gc->label, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* unregister the gpiolib interface with the gpiolib subsystem */
+static int __init samsung_gpiolib_unregister(struct platform_device *pdev,
+ struct samsung_pinctrl_drv_data *drvdata)
+{
+ int ret = gpiochip_remove(drvdata->gc);
+ if (ret) {
+ dev_err(&pdev->dev, "gpio chip remove failed\n");
+ return ret;
+ }
+ return 0;
+}
+
+static const struct of_device_id samsung_pinctrl_dt_match[];
+
+/* retrieve the soc specific data */
+static struct samsung_pin_ctrl *samsung_pinctrl_get_soc_data(
+ struct platform_device *pdev)
+{
+ int id;
+ const struct of_device_id *match;
+ const struct device_node *node = pdev->dev.of_node;
+
+ id = of_alias_get_id(pdev->dev.of_node, "pinctrl");
+ if (id < 0) {
+ dev_err(&pdev->dev, "failed to get alias id\n");
+ return NULL;
+ }
+ match = of_match_node(samsung_pinctrl_dt_match, node);
+ return (struct samsung_pin_ctrl *)match->data + id;
+}
+
+static int __devinit samsung_pinctrl_probe(struct platform_device *pdev)
+{
+ struct samsung_pinctrl_drv_data *drvdata;
+ struct device *dev = &pdev->dev;
+ struct samsung_pin_ctrl *ctrl;
+ struct resource *res;
+ int ret;
+
+ if (!dev->of_node) {
+ dev_err(dev, "device tree node not found\n");
+ return -ENODEV;
+ }
+
+ ctrl = samsung_pinctrl_get_soc_data(pdev);
+ if (!ctrl) {
+ dev_err(&pdev->dev, "driver data not available\n");
+ return -EINVAL;
+ }
+
+ drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
+ if (!drvdata) {
+ dev_err(dev, "failed to allocate memory for driver's "
+ "private data\n");
+ return -ENOMEM;
+ }
+ drvdata->ctrl = ctrl;
+ drvdata->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "cannot find IO resource\n");
+ return -ENOENT;
+ }
+
+ drvdata->virt_base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!drvdata->virt_base) {
+ dev_err(dev, "ioremap failed\n");
+ return -ENODEV;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (res)
+ drvdata->irq = res->start;
+
+ ret = samsung_gpiolib_register(pdev, drvdata);
+ if (ret)
+ return ret;
+
+ ret = samsung_pinctrl_register(pdev, drvdata);
+ if (ret) {
+ samsung_gpiolib_unregister(pdev, drvdata);
+ return ret;
+ }
+
+ if (ctrl->eint_gpio_init)
+ ctrl->eint_gpio_init(drvdata);
+ if (ctrl->eint_wkup_init)
+ ctrl->eint_wkup_init(drvdata);
+
+ platform_set_drvdata(pdev, drvdata);
+ return 0;
+}
+
+static const struct of_device_id samsung_pinctrl_dt_match[] = {
+ { .compatible = "samsung,pinctrl-exynos4210",
+ .data = (void *)exynos4210_pin_ctrl },
+ {},
+};
+MODULE_DEVICE_TABLE(of, samsung_pinctrl_dt_match);
+
+static struct platform_driver samsung_pinctrl_driver = {
+ .probe = samsung_pinctrl_probe,
+ .driver = {
+ .name = "samsung-pinctrl",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(samsung_pinctrl_dt_match),
+ },
+};
+
+static int __init samsung_pinctrl_drv_register(void)
+{
+ return platform_driver_register(&samsung_pinctrl_driver);
+}
+postcore_initcall(samsung_pinctrl_drv_register);
+
+static void __exit samsung_pinctrl_drv_unregister(void)
+{
+ platform_driver_unregister(&samsung_pinctrl_driver);
+}
+module_exit(samsung_pinctrl_drv_unregister);
+
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
+MODULE_DESCRIPTION("Samsung pinctrl driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pinctrl/pinctrl-samsung.h b/drivers/pinctrl/pinctrl-samsung.h
new file mode 100644
index 000000000000..b8956934cda6
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-samsung.h
@@ -0,0 +1,239 @@
+/*
+ * pin-controller/pin-mux/pin-config/gpio-driver for Samsung's SoC's.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ * Copyright (c) 2012 Linaro Ltd
+ * http://www.linaro.org
+ *
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PINCTRL_SAMSUNG_H
+#define __PINCTRL_SAMSUNG_H
+
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/machine.h>
+
+/* register offsets within a pin bank */
+#define DAT_REG 0x4
+#define PUD_REG 0x8
+#define DRV_REG 0xC
+#define CONPDN_REG 0x10
+#define PUDPDN_REG 0x14
+
+/* pinmux function number for pin as gpio output line */
+#define FUNC_OUTPUT 0x1
+
+/**
+ * enum pincfg_type - possible pin configuration types supported.
+ * @PINCFG_TYPE_PUD: Pull up/down configuration.
+ * @PINCFG_TYPE_DRV: Drive strength configuration.
+ * @PINCFG_TYPE_CON_PDN: Pin function in power down mode.
+ * @PINCFG_TYPE_PUD_PDN: Pull up/down configuration in power down mode.
+ */
+enum pincfg_type {
+ PINCFG_TYPE_PUD,
+ PINCFG_TYPE_DRV,
+ PINCFG_TYPE_CON_PDN,
+ PINCFG_TYPE_PUD_PDN,
+};
+
+/*
+ * pin configuration (pull up/down and drive strength) type and its value are
+ * packed together into a 16-bits. The upper 8-bits represent the configuration
+ * type and the lower 8-bits hold the value of the configuration type.
+ */
+#define PINCFG_TYPE_MASK 0xFF
+#define PINCFG_VALUE_SHIFT 8
+#define PINCFG_VALUE_MASK (0xFF << PINCFG_VALUE_SHIFT)
+#define PINCFG_PACK(type, value) (((value) << PINCFG_VALUE_SHIFT) | type)
+#define PINCFG_UNPACK_TYPE(cfg) ((cfg) & PINCFG_TYPE_MASK)
+#define PINCFG_UNPACK_VALUE(cfg) (((cfg) & PINCFG_VALUE_MASK) >> \
+ PINCFG_VALUE_SHIFT)
+/**
+ * enum eint_type - possible external interrupt types.
+ * @EINT_TYPE_NONE: bank does not support external interrupts
+ * @EINT_TYPE_GPIO: bank supportes external gpio interrupts
+ * @EINT_TYPE_WKUP: bank supportes external wakeup interrupts
+ *
+ * Samsung GPIO controller groups all the available pins into banks. The pins
+ * in a pin bank can support external gpio interrupts or external wakeup
+ * interrupts or no interrupts at all. From a software perspective, the only
+ * difference between external gpio and external wakeup interrupts is that
+ * the wakeup interrupts can additionally wakeup the system if it is in
+ * suspended state.
+ */
+enum eint_type {
+ EINT_TYPE_NONE,
+ EINT_TYPE_GPIO,
+ EINT_TYPE_WKUP,
+};
+
+/* maximum length of a pin in pin descriptor (example: "gpa0-0") */
+#define PIN_NAME_LENGTH 10
+
+#define PIN_GROUP(n, p, f) \
+ { \
+ .name = n, \
+ .pins = p, \
+ .num_pins = ARRAY_SIZE(p), \
+ .func = f \
+ }
+
+#define PMX_FUNC(n, g) \
+ { \
+ .name = n, \
+ .groups = g, \
+ .num_groups = ARRAY_SIZE(g), \
+ }
+
+struct samsung_pinctrl_drv_data;
+
+/**
+ * struct samsung_pin_bank: represent a controller pin-bank.
+ * @reg_offset: starting offset of the pin-bank registers.
+ * @pin_base: starting pin number of the bank.
+ * @nr_pins: number of pins included in this bank.
+ * @func_width: width of the function selector bit field.
+ * @pud_width: width of the pin pull up/down selector bit field.
+ * @drv_width: width of the pin driver strength selector bit field.
+ * @conpdn_width: width of the sleep mode function selector bin field.
+ * @pudpdn_width: width of the sleep mode pull up/down selector bit field.
+ * @eint_type: type of the external interrupt supported by the bank.
+ * @irq_base: starting controller local irq number of the bank.
+ * @name: name to be prefixed for each pin in this pin bank.
+ */
+struct samsung_pin_bank {
+ u32 pctl_offset;
+ u32 pin_base;
+ u8 nr_pins;
+ u8 func_width;
+ u8 pud_width;
+ u8 drv_width;
+ u8 conpdn_width;
+ u8 pudpdn_width;
+ enum eint_type eint_type;
+ u32 irq_base;
+ char *name;
+};
+
+/**
+ * struct samsung_pin_ctrl: represent a pin controller.
+ * @pin_banks: list of pin banks included in this controller.
+ * @nr_banks: number of pin banks.
+ * @base: starting system wide pin number.
+ * @nr_pins: number of pins supported by the controller.
+ * @nr_gint: number of external gpio interrupts supported.
+ * @nr_wint: number of external wakeup interrupts supported.
+ * @geint_con: offset of the ext-gpio controller registers.
+ * @geint_mask: offset of the ext-gpio interrupt mask registers.
+ * @geint_pend: offset of the ext-gpio interrupt pending registers.
+ * @weint_con: offset of the ext-wakeup controller registers.
+ * @weint_mask: offset of the ext-wakeup interrupt mask registers.
+ * @weint_pend: offset of the ext-wakeup interrupt pending registers.
+ * @svc: offset of the interrupt service register.
+ * @eint_gpio_init: platform specific callback to setup the external gpio
+ * interrupts for the controller.
+ * @eint_wkup_init: platform specific callback to setup the external wakeup
+ * interrupts for the controller.
+ * @label: for debug information.
+ */
+struct samsung_pin_ctrl {
+ struct samsung_pin_bank *pin_banks;
+ u32 nr_banks;
+
+ u32 base;
+ u32 nr_pins;
+ u32 nr_gint;
+ u32 nr_wint;
+
+ u32 geint_con;
+ u32 geint_mask;
+ u32 geint_pend;
+
+ u32 weint_con;
+ u32 weint_mask;
+ u32 weint_pend;
+
+ u32 svc;
+
+ int (*eint_gpio_init)(struct samsung_pinctrl_drv_data *);
+ int (*eint_wkup_init)(struct samsung_pinctrl_drv_data *);
+ char *label;
+};
+
+/**
+ * struct samsung_pinctrl_drv_data: wrapper for holding driver data together.
+ * @virt_base: register base address of the controller.
+ * @dev: device instance representing the controller.
+ * @irq: interrpt number used by the controller to notify gpio interrupts.
+ * @ctrl: pin controller instance managed by the driver.
+ * @pctl: pin controller descriptor registered with the pinctrl subsystem.
+ * @pctl_dev: cookie representing pinctrl device instance.
+ * @pin_groups: list of pin groups available to the driver.
+ * @nr_groups: number of such pin groups.
+ * @pmx_functions: list of pin functions available to the driver.
+ * @nr_function: number of such pin functions.
+ * @gc: gpio_chip instance registered with gpiolib.
+ * @grange: linux gpio pin range supported by this controller.
+ */
+struct samsung_pinctrl_drv_data {
+ void __iomem *virt_base;
+ struct device *dev;
+ int irq;
+
+ struct samsung_pin_ctrl *ctrl;
+ struct pinctrl_desc pctl;
+ struct pinctrl_dev *pctl_dev;
+
+ const struct samsung_pin_group *pin_groups;
+ unsigned int nr_groups;
+ const struct samsung_pmx_func *pmx_functions;
+ unsigned int nr_functions;
+
+ struct irq_domain *gpio_irqd;
+ struct irq_domain *wkup_irqd;
+
+ struct gpio_chip *gc;
+ struct pinctrl_gpio_range grange;
+};
+
+/**
+ * struct samsung_pin_group: represent group of pins of a pinmux function.
+ * @name: name of the pin group, used to lookup the group.
+ * @pins: the pins included in this group.
+ * @num_pins: number of pins included in this group.
+ * @func: the function number to be programmed when selected.
+ */
+struct samsung_pin_group {
+ const char *name;
+ const unsigned int *pins;
+ u8 num_pins;
+ u8 func;
+};
+
+/**
+ * struct samsung_pmx_func: represent a pin function.
+ * @name: name of the pin function, used to lookup the function.
+ * @groups: one or more names of pin groups that provide this function.
+ * @num_groups: number of groups included in @groups.
+ */
+struct samsung_pmx_func {
+ const char *name;
+ const char **groups;
+ u8 num_groups;
+};
+
+/* list of all exported SoC specific data */
+extern struct samsung_pin_ctrl exynos4210_pin_ctrl[];
+
+#endif /* __PINCTRL_SAMSUNG_H */
diff --git a/drivers/pinctrl/pinctrl-tegra.c b/drivers/pinctrl/pinctrl-tegra.c
index ae52e4e5d098..729b686c3ad2 100644
--- a/drivers/pinctrl/pinctrl-tegra.c
+++ b/drivers/pinctrl/pinctrl-tegra.c
@@ -30,8 +30,6 @@
#include <linux/pinctrl/pinconf.h>
#include <linux/slab.h>
-#include <mach/pinconf-tegra.h>
-
#include "core.h"
#include "pinctrl-tegra.h"
diff --git a/drivers/pinctrl/pinctrl-tegra.h b/drivers/pinctrl/pinctrl-tegra.h
index 705c007a38cc..62e380965c68 100644
--- a/drivers/pinctrl/pinctrl-tegra.h
+++ b/drivers/pinctrl/pinctrl-tegra.h
@@ -16,6 +16,50 @@
#ifndef __PINMUX_TEGRA_H__
#define __PINMUX_TEGRA_H__
+enum tegra_pinconf_param {
+ /* argument: tegra_pinconf_pull */
+ TEGRA_PINCONF_PARAM_PULL,
+ /* argument: tegra_pinconf_tristate */
+ TEGRA_PINCONF_PARAM_TRISTATE,
+ /* argument: Boolean */
+ TEGRA_PINCONF_PARAM_ENABLE_INPUT,
+ /* argument: Boolean */
+ TEGRA_PINCONF_PARAM_OPEN_DRAIN,
+ /* argument: Boolean */
+ TEGRA_PINCONF_PARAM_LOCK,
+ /* argument: Boolean */
+ TEGRA_PINCONF_PARAM_IORESET,
+ /* argument: Boolean */
+ TEGRA_PINCONF_PARAM_HIGH_SPEED_MODE,
+ /* argument: Boolean */
+ TEGRA_PINCONF_PARAM_SCHMITT,
+ /* argument: Boolean */
+ TEGRA_PINCONF_PARAM_LOW_POWER_MODE,
+ /* argument: Integer, range is HW-dependant */
+ TEGRA_PINCONF_PARAM_DRIVE_DOWN_STRENGTH,
+ /* argument: Integer, range is HW-dependant */
+ TEGRA_PINCONF_PARAM_DRIVE_UP_STRENGTH,
+ /* argument: Integer, range is HW-dependant */
+ TEGRA_PINCONF_PARAM_SLEW_RATE_FALLING,
+ /* argument: Integer, range is HW-dependant */
+ TEGRA_PINCONF_PARAM_SLEW_RATE_RISING,
+};
+
+enum tegra_pinconf_pull {
+ TEGRA_PINCONFIG_PULL_NONE,
+ TEGRA_PINCONFIG_PULL_DOWN,
+ TEGRA_PINCONFIG_PULL_UP,
+};
+
+enum tegra_pinconf_tristate {
+ TEGRA_PINCONFIG_DRIVEN,
+ TEGRA_PINCONFIG_TRISTATE,
+};
+
+#define TEGRA_PINCONF_PACK(_param_, _arg_) ((_param_) << 16 | (_arg_))
+#define TEGRA_PINCONF_UNPACK_PARAM(_conf_) ((_conf_) >> 16)
+#define TEGRA_PINCONF_UNPACK_ARG(_conf_) ((_conf_) & 0xffff)
+
/**
* struct tegra_function - Tegra pinctrl mux function
* @name: The name of the function, exported to pinctrl core.
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index c1892f321c46..80978196aae8 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -29,6 +29,13 @@ config APM_POWER
Say Y here to enable support APM status emulation using
battery class devices.
+config GENERIC_ADC_BATTERY
+ tristate "Generic battery support using IIO"
+ depends on IIO
+ help
+ Say Y here to enable support for the generic battery driver
+ which uses IIO framework to read adc.
+
config MAX8925_POWER
tristate "MAX8925 battery charger support"
depends on MFD_MAX8925
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ee58afb1e71f..e0b4d4284e1d 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -5,6 +5,7 @@ power_supply-$(CONFIG_SYSFS) += power_supply_sysfs.o
power_supply-$(CONFIG_LEDS_TRIGGERS) += power_supply_leds.o
obj-$(CONFIG_POWER_SUPPLY) += power_supply.o
+obj-$(CONFIG_GENERIC_ADC_BATTERY) += generic-adc-battery.o
obj-$(CONFIG_PDA_POWER) += pda_power.o
obj-$(CONFIG_APM_POWER) += apm_power.o
diff --git a/drivers/power/generic-adc-battery.c b/drivers/power/generic-adc-battery.c
new file mode 100644
index 000000000000..9bdf44470396
--- /dev/null
+++ b/drivers/power/generic-adc-battery.c
@@ -0,0 +1,422 @@
+/*
+ * Generic battery driver code using IIO
+ * Copyright (C) 2012, Anish Kumar <anish198519851985@gmail.com>
+ * based on jz4740-battery.c
+ * based on s3c_adc_battery.c
+ *
+ * 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/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/timer.h>
+#include <linux/jiffies.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/types.h>
+#include <linux/power/generic-adc-battery.h>
+
+#define JITTER_DEFAULT 10 /* hope 10ms is enough */
+
+enum gab_chan_type {
+ GAB_VOLTAGE = 0,
+ GAB_CURRENT,
+ GAB_POWER,
+ GAB_MAX_CHAN_TYPE
+};
+
+/*
+ * gab_chan_name suggests the standard channel names for commonly used
+ * channel types.
+ */
+static const char *const gab_chan_name[] = {
+ [GAB_VOLTAGE] = "voltage",
+ [GAB_CURRENT] = "current",
+ [GAB_POWER] = "power",
+};
+
+struct gab {
+ struct power_supply psy;
+ struct iio_channel *channel[GAB_MAX_CHAN_TYPE];
+ struct gab_platform_data *pdata;
+ struct delayed_work bat_work;
+ int level;
+ int status;
+ bool cable_plugged;
+};
+
+static struct gab *to_generic_bat(struct power_supply *psy)
+{
+ return container_of(psy, struct gab, psy);
+}
+
+static void gab_ext_power_changed(struct power_supply *psy)
+{
+ struct gab *adc_bat = to_generic_bat(psy);
+
+ schedule_delayed_work(&adc_bat->bat_work, msecs_to_jiffies(0));
+}
+
+static const enum power_supply_property gab_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
+ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+ POWER_SUPPLY_PROP_MODEL_NAME,
+};
+
+/*
+ * This properties are set based on the received platform data and this
+ * should correspond one-to-one with enum chan_type.
+ */
+static const enum power_supply_property gab_dyn_props[] = {
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_POWER_NOW,
+};
+
+static bool gab_charge_finished(struct gab *adc_bat)
+{
+ struct gab_platform_data *pdata = adc_bat->pdata;
+ bool ret = gpio_get_value(pdata->gpio_charge_finished);
+ bool inv = pdata->gpio_inverted;
+
+ if (!gpio_is_valid(pdata->gpio_charge_finished))
+ return false;
+ return ret ^ inv;
+}
+
+static int gab_get_status(struct gab *adc_bat)
+{
+ struct gab_platform_data *pdata = adc_bat->pdata;
+ struct power_supply_info *bat_info;
+
+ bat_info = &pdata->battery_info;
+ if (adc_bat->level == bat_info->charge_full_design)
+ return POWER_SUPPLY_STATUS_FULL;
+ return adc_bat->status;
+}
+
+static enum gab_chan_type gab_prop_to_chan(enum power_supply_property psp)
+{
+ switch (psp) {
+ case POWER_SUPPLY_PROP_POWER_NOW:
+ return GAB_POWER;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ return GAB_VOLTAGE;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ return GAB_CURRENT;
+ default:
+ WARN_ON(1);
+ break;
+ }
+ return GAB_POWER;
+}
+
+static int read_channel(struct gab *adc_bat, enum power_supply_property psp,
+ int *result)
+{
+ int ret;
+ int chan_index;
+
+ chan_index = gab_prop_to_chan(psp);
+ ret = iio_read_channel_processed(adc_bat->channel[chan_index],
+ result);
+ if (ret < 0)
+ pr_err("read channel error\n");
+ return ret;
+}
+
+static int gab_get_property(struct power_supply *psy,
+ enum power_supply_property psp, union power_supply_propval *val)
+{
+ struct gab *adc_bat;
+ struct gab_platform_data *pdata;
+ struct power_supply_info *bat_info;
+ int result = 0;
+ int ret = 0;
+
+ adc_bat = to_generic_bat(psy);
+ if (!adc_bat) {
+ dev_err(psy->dev, "no battery infos ?!\n");
+ return -EINVAL;
+ }
+ pdata = adc_bat->pdata;
+ bat_info = &pdata->battery_info;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ gab_get_status(adc_bat);
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN:
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = pdata->cal_charge(result);
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ case POWER_SUPPLY_PROP_POWER_NOW:
+ ret = read_channel(adc_bat, psp, &result);
+ if (ret < 0)
+ goto err;
+ val->intval = result;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = bat_info->technology;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ val->intval = bat_info->voltage_min_design;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ val->intval = bat_info->voltage_max_design;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = bat_info->charge_full_design;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = bat_info->name;
+ break;
+ default:
+ return -EINVAL;
+ }
+err:
+ return ret;
+}
+
+static void gab_work(struct work_struct *work)
+{
+ struct gab *adc_bat;
+ struct gab_platform_data *pdata;
+ struct delayed_work *delayed_work;
+ bool is_plugged;
+ int status;
+
+ delayed_work = container_of(work, struct delayed_work, work);
+ adc_bat = container_of(delayed_work, struct gab, bat_work);
+ pdata = adc_bat->pdata;
+ status = adc_bat->status;
+
+ is_plugged = power_supply_am_i_supplied(&adc_bat->psy);
+ adc_bat->cable_plugged = is_plugged;
+
+ if (!is_plugged)
+ adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ else if (gab_charge_finished(adc_bat))
+ adc_bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else
+ adc_bat->status = POWER_SUPPLY_STATUS_CHARGING;
+
+ if (status != adc_bat->status)
+ power_supply_changed(&adc_bat->psy);
+}
+
+static irqreturn_t gab_charged(int irq, void *dev_id)
+{
+ struct gab *adc_bat = dev_id;
+ struct gab_platform_data *pdata = adc_bat->pdata;
+ int delay;
+
+ delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT;
+ schedule_delayed_work(&adc_bat->bat_work,
+ msecs_to_jiffies(delay));
+ return IRQ_HANDLED;
+}
+
+static int __devinit gab_probe(struct platform_device *pdev)
+{
+ struct gab *adc_bat;
+ struct power_supply *psy;
+ struct gab_platform_data *pdata = pdev->dev.platform_data;
+ enum power_supply_property *properties;
+ int ret = 0;
+ int chan;
+ int index = 0;
+
+ adc_bat = devm_kzalloc(&pdev->dev, sizeof(*adc_bat), GFP_KERNEL);
+ if (!adc_bat) {
+ dev_err(&pdev->dev, "failed to allocate memory\n");
+ return -ENOMEM;
+ }
+
+ psy = &adc_bat->psy;
+ psy->name = pdata->battery_info.name;
+
+ /* bootup default values for the battery */
+ adc_bat->cable_plugged = false;
+ adc_bat->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ psy->type = POWER_SUPPLY_TYPE_BATTERY;
+ psy->get_property = gab_get_property;
+ psy->external_power_changed = gab_ext_power_changed;
+ adc_bat->pdata = pdata;
+
+ /* calculate the total number of channels */
+ chan = ARRAY_SIZE(gab_chan_name);
+
+ /*
+ * copying the static properties and allocating extra memory for holding
+ * the extra configurable properties received from platform data.
+ */
+ psy->properties = kcalloc(ARRAY_SIZE(gab_props) +
+ ARRAY_SIZE(gab_chan_name),
+ sizeof(*psy->properties), GFP_KERNEL);
+ if (!psy->properties) {
+ ret = -ENOMEM;
+ goto first_mem_fail;
+ }
+
+ memcpy(psy->properties, gab_props, sizeof(gab_props));
+ properties = psy->properties + sizeof(gab_props);
+
+ /*
+ * getting channel from iio and copying the battery properties
+ * based on the channel supported by consumer device.
+ */
+ for (chan = 0; chan < ARRAY_SIZE(gab_chan_name); chan++) {
+ adc_bat->channel[chan] = iio_channel_get(dev_name(&pdev->dev),
+ gab_chan_name[chan]);
+ if (IS_ERR(adc_bat->channel[chan])) {
+ ret = PTR_ERR(adc_bat->channel[chan]);
+ } else {
+ /* copying properties for supported channels only */
+ memcpy(properties + sizeof(*(psy->properties)) * index,
+ &gab_dyn_props[chan],
+ sizeof(gab_dyn_props[chan]));
+ index++;
+ }
+ }
+
+ /* none of the channels are supported so let's bail out */
+ if (index == ARRAY_SIZE(gab_chan_name))
+ goto second_mem_fail;
+
+ /*
+ * Total number of properties is equal to static properties
+ * plus the dynamic properties.Some properties may not be set
+ * as come channels may be not be supported by the device.So
+ * we need to take care of that.
+ */
+ psy->num_properties = ARRAY_SIZE(gab_props) + index;
+
+ ret = power_supply_register(&pdev->dev, psy);
+ if (ret)
+ goto err_reg_fail;
+
+ INIT_DELAYED_WORK(&adc_bat->bat_work, gab_work);
+
+ if (gpio_is_valid(pdata->gpio_charge_finished)) {
+ int irq;
+ ret = gpio_request(pdata->gpio_charge_finished, "charged");
+ if (ret)
+ goto gpio_req_fail;
+
+ irq = gpio_to_irq(pdata->gpio_charge_finished);
+ ret = request_any_context_irq(irq, gab_charged,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "battery charged", adc_bat);
+ if (ret)
+ goto err_gpio;
+ }
+
+ platform_set_drvdata(pdev, adc_bat);
+
+ /* Schedule timer to check current status */
+ schedule_delayed_work(&adc_bat->bat_work,
+ msecs_to_jiffies(0));
+ return 0;
+
+err_gpio:
+ gpio_free(pdata->gpio_charge_finished);
+gpio_req_fail:
+ power_supply_unregister(psy);
+err_reg_fail:
+ for (chan = 0; ARRAY_SIZE(gab_chan_name); chan++)
+ iio_channel_release(adc_bat->channel[chan]);
+second_mem_fail:
+ kfree(psy->properties);
+first_mem_fail:
+ return ret;
+}
+
+static int __devexit gab_remove(struct platform_device *pdev)
+{
+ int chan;
+ struct gab *adc_bat = platform_get_drvdata(pdev);
+ struct gab_platform_data *pdata = adc_bat->pdata;
+
+ power_supply_unregister(&adc_bat->psy);
+
+ if (gpio_is_valid(pdata->gpio_charge_finished)) {
+ free_irq(gpio_to_irq(pdata->gpio_charge_finished), adc_bat);
+ gpio_free(pdata->gpio_charge_finished);
+ }
+
+ for (chan = 0; ARRAY_SIZE(gab_chan_name); chan++)
+ iio_channel_release(adc_bat->channel[chan]);
+
+ kfree(adc_bat->psy.properties);
+ cancel_delayed_work(&adc_bat->bat_work);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int gab_suspend(struct device *dev)
+{
+ struct gab *adc_bat = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&adc_bat->bat_work);
+ adc_bat->status = POWER_SUPPLY_STATUS_UNKNOWN;
+ return 0;
+}
+
+static int gab_resume(struct device *dev)
+{
+ struct gab *adc_bat = dev_get_drvdata(dev);
+ struct gab_platform_data *pdata = adc_bat->pdata;
+ int delay;
+
+ delay = pdata->jitter_delay ? pdata->jitter_delay : JITTER_DEFAULT;
+
+ /* Schedule timer to check current status */
+ schedule_delayed_work(&adc_bat->bat_work,
+ msecs_to_jiffies(delay));
+ return 0;
+}
+
+static const struct dev_pm_ops gab_pm_ops = {
+ .suspend = gab_suspend,
+ .resume = gab_resume,
+};
+
+#define GAB_PM_OPS (&gab_pm_ops)
+#else
+#define GAB_PM_OPS (NULL)
+#endif
+
+static struct platform_driver gab_driver = {
+ .driver = {
+ .name = "generic-adc-battery",
+ .owner = THIS_MODULE,
+ .pm = GAB_PM_OPS
+ },
+ .probe = gab_probe,
+ .remove = __devexit_p(gab_remove),
+};
+module_platform_driver(gab_driver);
+
+MODULE_AUTHOR("anish kumar <anish198519851985@gmail.com>");
+MODULE_DESCRIPTION("generic battery driver using IIO");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index 5d44252b7342..d5e1625bbac2 100644
--- a/drivers/rapidio/devices/tsi721.c
+++ b/drivers/rapidio/devices/tsi721.c
@@ -2219,9 +2219,7 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
struct tsi721_device *priv;
- int cap;
int err;
- u32 regval;
priv = kzalloc(sizeof(struct tsi721_device), GFP_KERNEL);
if (priv == NULL) {
@@ -2330,20 +2328,16 @@ static int __devinit tsi721_probe(struct pci_dev *pdev,
dev_info(&pdev->dev, "Unable to set consistent DMA mask\n");
}
- cap = pci_pcie_cap(pdev);
- BUG_ON(cap == 0);
+ BUG_ON(!pci_is_pcie(pdev));
/* Clear "no snoop" and "relaxed ordering" bits, use default MRRS. */
- pci_read_config_dword(pdev, cap + PCI_EXP_DEVCTL, &regval);
- regval &= ~(PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_RELAX_EN |
- PCI_EXP_DEVCTL_NOSNOOP_EN);
- regval |= 0x2 << MAX_READ_REQUEST_SZ_SHIFT;
- pci_write_config_dword(pdev, cap + PCI_EXP_DEVCTL, regval);
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_READRQ | PCI_EXP_DEVCTL_RELAX_EN |
+ PCI_EXP_DEVCTL_NOSNOOP_EN,
+ 0x2 << MAX_READ_REQUEST_SZ_SHIFT);
/* Adjust PCIe completion timeout. */
- pci_read_config_dword(pdev, cap + PCI_EXP_DEVCTL2, &regval);
- regval &= ~(0x0f);
- pci_write_config_dword(pdev, cap + PCI_EXP_DEVCTL2, regval | 0x2);
+ pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL2, 0xf, 0x2);
/*
* FIXUP: correct offsets of MSI-X tables in the MSI-X Capability Block
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 4e932cc695e9..e98a5e7827df 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -33,9 +33,8 @@ config REGULATOR_DUMMY
help
If this option is enabled then when a regulator lookup fails
and the board has not specified that it has provided full
- constraints then the regulator core will provide an always
- enabled dummy regulator will be provided, allowing consumer
- drivers to continue.
+ constraints the regulator core will provide an always
+ enabled dummy regulator, allowing consumer drivers to continue.
A warning will be generated when this substitution is done.
@@ -50,11 +49,11 @@ config REGULATOR_VIRTUAL_CONSUMER
tristate "Virtual regulator consumer support"
help
This driver provides a virtual consumer for the voltage and
- current regulator API which provides sysfs controls for
- configuring the supplies requested. This is mainly useful
- for test purposes.
+ current regulator API which provides sysfs controls for
+ configuring the supplies requested. This is mainly useful
+ for test purposes.
- If unsure, say no.
+ If unsure, say no.
config REGULATOR_USERSPACE_CONSUMER
tristate "Userspace regulator consumer support"
@@ -63,7 +62,7 @@ config REGULATOR_USERSPACE_CONSUMER
from user space. Userspace consumer driver provides ability to
control power supplies for such devices.
- If unsure, say no.
+ If unsure, say no.
config REGULATOR_GPIO
tristate "GPIO regulator support"
@@ -110,6 +109,17 @@ config REGULATOR_DA9052
This driver supports the voltage regulators of DA9052-BC and
DA9053-AA/Bx PMIC.
+config REGULATOR_FAN53555
+ tristate "Fairchild FAN53555 Regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver supports Fairchild FAN53555 Digitally Programmable
+ TinyBuck Regulator. The FAN53555 is a step-down switching voltage
+ regulator that delivers a digitally programmable output from an
+ input voltage supply of 2.5V to 5.5V. The output voltage is
+ programmed through an I2C interface.
+
config REGULATOR_ANATOP
tristate "Freescale i.MX on-chip ANATOP LDO regulators"
depends on MFD_ANATOP
@@ -172,6 +182,14 @@ config REGULATOR_MAX8660
This driver controls a Maxim 8660/8661 voltage output
regulator via I2C bus.
+config REGULATOR_MAX8907
+ tristate "Maxim 8907 voltage regulator"
+ depends on MFD_MAX8907
+ help
+ This driver controls a Maxim 8907 voltage output regulator
+ via I2C bus. The provided regulator is suitable for Tegra
+ chip to control Step-Down DC-DC and LDOs.
+
config REGULATOR_MAX8925
tristate "Maxim MAX8925 Power Management IC"
depends on MFD_MAX8925
@@ -247,7 +265,7 @@ config REGULATOR_LP8788
config REGULATOR_PCF50633
tristate "NXP PCF50633 regulator driver"
- depends on MFD_PCF50633
+ depends on MFD_PCF50633
help
Say Y here to support the voltage regulators and convertors
on PCF50633
@@ -416,7 +434,7 @@ config REGULATOR_WM8350
depends on MFD_WM8350
help
This driver provides support for the voltage and current regulators
- of the WM8350 AudioPlus PMIC.
+ of the WM8350 AudioPlus PMIC.
config REGULATOR_WM8400
tristate "Wolfson Microelectronics WM8400 AudioPlus PMIC"
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 3342615cf25e..e431eed8a878 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
+obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
@@ -30,6 +31,7 @@ obj-$(CONFIG_REGULATOR_LP8788) += lp8788-ldo.o
obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o
obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
+obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o
obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o
obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o
diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c
index 6f45bfd22e83..167c93f21981 100644
--- a/drivers/regulator/aat2870-regulator.c
+++ b/drivers/regulator/aat2870-regulator.c
@@ -162,7 +162,7 @@ static struct aat2870_regulator *aat2870_get_regulator(int id)
static int aat2870_regulator_probe(struct platform_device *pdev)
{
struct aat2870_regulator *ri;
- struct regulator_config config = { 0 };
+ struct regulator_config config = { };
struct regulator_dev *rdev;
ri = aat2870_get_regulator(pdev->id);
diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c
index c151fd5d8c97..65ad2b36ce36 100644
--- a/drivers/regulator/ab3100.c
+++ b/drivers/regulator/ab3100.c
@@ -347,17 +347,11 @@ static int ab3100_get_voltage_regulator_external(struct regulator_dev *reg)
return abreg->plfdata->external_voltage;
}
-static int ab3100_get_fixed_voltage_regulator(struct regulator_dev *reg)
-{
- return reg->desc->min_uV;
-}
-
static struct regulator_ops regulator_ops_fixed = {
.list_voltage = regulator_list_voltage_linear,
.enable = ab3100_enable_regulator,
.disable = ab3100_disable_regulator,
.is_enabled = ab3100_is_enabled_regulator,
- .get_voltage = ab3100_get_fixed_voltage_regulator,
};
static struct regulator_ops regulator_ops_variable = {
diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 10f2f4d4d190..e3d1d063025a 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -37,6 +37,7 @@
* @voltage_bank: bank to control regulator voltage
* @voltage_reg: register to control regulator voltage
* @voltage_mask: mask to control regulator voltage
+ * @voltage_shift: shift to control regulator voltage
* @delay: startup/set voltage delay in us
*/
struct ab8500_regulator_info {
@@ -50,6 +51,7 @@ struct ab8500_regulator_info {
u8 voltage_bank;
u8 voltage_reg;
u8 voltage_mask;
+ u8 voltage_shift;
unsigned int delay;
};
@@ -195,17 +197,14 @@ static int ab8500_regulator_get_voltage_sel(struct regulator_dev *rdev)
}
dev_vdbg(rdev_get_dev(rdev),
- "%s-get_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
- " 0x%x\n",
- info->desc.name, info->voltage_bank, info->voltage_reg,
- info->voltage_mask, regval);
+ "%s-get_voltage (bank, reg, mask, shift, value): "
+ "0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+ info->desc.name, info->voltage_bank,
+ info->voltage_reg, info->voltage_mask,
+ info->voltage_shift, regval);
- /* vintcore has a different layout */
val = regval & info->voltage_mask;
- if (info->desc.id == AB8500_LDO_INTCORE)
- return val >> 0x3;
- else
- return val;
+ return val >> info->voltage_shift;
}
static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
@@ -221,7 +220,7 @@ static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
}
/* set the registers for the request */
- regval = (u8)selector;
+ regval = (u8)selector << info->voltage_shift;
ret = abx500_mask_and_set_register_interruptible(info->dev,
info->voltage_bank, info->voltage_reg,
info->voltage_mask, regval);
@@ -238,13 +237,6 @@ static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
return ret;
}
-static int ab8500_regulator_enable_time(struct regulator_dev *rdev)
-{
- struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
-
- return info->delay;
-}
-
static int ab8500_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
unsigned int old_sel,
unsigned int new_sel)
@@ -261,22 +253,14 @@ static struct regulator_ops ab8500_regulator_ops = {
.get_voltage_sel = ab8500_regulator_get_voltage_sel,
.set_voltage_sel = ab8500_regulator_set_voltage_sel,
.list_voltage = regulator_list_voltage_table,
- .enable_time = ab8500_regulator_enable_time,
.set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel,
};
-static int ab8500_fixed_get_voltage(struct regulator_dev *rdev)
-{
- return rdev->desc->min_uV;
-}
-
static struct regulator_ops ab8500_regulator_fixed_ops = {
.enable = ab8500_regulator_enable,
.disable = ab8500_regulator_disable,
.is_enabled = ab8500_regulator_is_enabled,
- .get_voltage = ab8500_fixed_get_voltage,
.list_voltage = regulator_list_voltage_linear,
- .enable_time = ab8500_regulator_enable_time,
};
static struct ab8500_regulator_info
@@ -358,6 +342,7 @@ static struct ab8500_regulator_info
.voltage_bank = 0x03,
.voltage_reg = 0x80,
.voltage_mask = 0x38,
+ .voltage_shift = 3,
},
/*
@@ -374,6 +359,7 @@ static struct ab8500_regulator_info
.owner = THIS_MODULE,
.n_voltages = 1,
.min_uV = 2000000,
+ .enable_time = 10000,
},
.delay = 10000,
.update_bank = 0x03,
diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c
index c8f95c07adb6..d184aa35abcb 100644
--- a/drivers/regulator/arizona-ldo1.c
+++ b/drivers/regulator/arizona-ldo1.c
@@ -39,6 +39,8 @@ static struct regulator_ops arizona_ldo1_ops = {
.map_voltage = regulator_map_voltage_linear,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
+ .set_bypass = regulator_set_bypass_regmap,
};
static const struct regulator_desc arizona_ldo1 = {
@@ -49,9 +51,11 @@ static const struct regulator_desc arizona_ldo1 = {
.vsel_reg = ARIZONA_LDO1_CONTROL_1,
.vsel_mask = ARIZONA_LDO1_VSEL_MASK,
+ .bypass_reg = ARIZONA_LDO1_CONTROL_1,
+ .bypass_mask = ARIZONA_LDO1_BYPASS,
.min_uV = 900000,
.uV_step = 50000,
- .n_voltages = 7,
+ .n_voltages = 6,
.owner = THIS_MODULE,
};
diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c
index 450a069aa9b6..d9b1f82cc5bd 100644
--- a/drivers/regulator/arizona-micsupp.c
+++ b/drivers/regulator/arizona-micsupp.c
@@ -82,6 +82,9 @@ static struct regulator_ops arizona_micsupp_ops = {
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
+
+ .get_bypass = regulator_get_bypass_regmap,
+ .set_bypass = regulator_set_bypass_regmap,
};
static const struct regulator_desc arizona_micsupp = {
@@ -95,6 +98,8 @@ static const struct regulator_desc arizona_micsupp = {
.vsel_mask = ARIZONA_LDO2_VSEL_MASK,
.enable_reg = ARIZONA_MIC_CHARGE_PUMP_1,
.enable_mask = ARIZONA_CPMIC_ENA,
+ .bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1,
+ .bypass_mask = ARIZONA_CPMIC_BYPASS,
.owner = THIS_MODULE,
};
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 48385318175a..2e0352dc26bd 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -77,6 +77,7 @@ struct regulator {
struct device *dev;
struct list_head list;
unsigned int always_on:1;
+ unsigned int bypass:1;
int uA_load;
int min_uV;
int max_uV;
@@ -394,6 +395,9 @@ static ssize_t regulator_status_show(struct device *dev,
case REGULATOR_STATUS_STANDBY:
label = "standby";
break;
+ case REGULATOR_STATUS_BYPASS:
+ label = "bypass";
+ break;
case REGULATOR_STATUS_UNDEFINED:
label = "undefined";
break;
@@ -585,6 +589,27 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev,
static DEVICE_ATTR(suspend_standby_state, 0444,
regulator_suspend_standby_state_show, NULL);
+static ssize_t regulator_bypass_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct regulator_dev *rdev = dev_get_drvdata(dev);
+ const char *report;
+ bool bypass;
+ int ret;
+
+ ret = rdev->desc->ops->get_bypass(rdev, &bypass);
+
+ if (ret != 0)
+ report = "unknown";
+ else if (bypass)
+ report = "enabled";
+ else
+ report = "disabled";
+
+ return sprintf(buf, "%s\n", report);
+}
+static DEVICE_ATTR(bypass, 0444,
+ regulator_bypass_show, NULL);
/*
* These are the only attributes are present for all regulators.
@@ -778,6 +803,9 @@ static void print_constraints(struct regulator_dev *rdev)
if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
count += sprintf(buf + count, "standby");
+ if (!count)
+ sprintf(buf, "no parameters");
+
rdev_info(rdev, "%s\n", buf);
if ((constraints->min_uV != constraints->max_uV) &&
@@ -974,6 +1002,7 @@ static int set_supply(struct regulator_dev *rdev,
err = -ENOMEM;
return err;
}
+ supply_rdev->open_count++;
return 0;
}
@@ -1720,6 +1749,9 @@ int regulator_disable_deferred(struct regulator *regulator, int ms)
if (regulator->always_on)
return 0;
+ if (!ms)
+ return regulator_disable(regulator);
+
mutex_lock(&rdev->mutex);
rdev->deferred_disables++;
mutex_unlock(&rdev->mutex);
@@ -2178,9 +2210,12 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
}
}
- if (ret == 0 && best_val >= 0)
+ if (ret == 0 && best_val >= 0) {
+ unsigned long data = best_val;
+
_notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE,
- (void *)best_val);
+ (void *)data);
+ }
trace_regulator_set_voltage_complete(rdev_get_name(rdev), best_val);
@@ -2291,8 +2326,8 @@ int regulator_set_voltage_time(struct regulator *regulator,
EXPORT_SYMBOL_GPL(regulator_set_voltage_time);
/**
- *regulator_set_voltage_time_sel - get raise/fall time
- * @regulator: regulator source
+ * regulator_set_voltage_time_sel - get raise/fall time
+ * @rdev: regulator source device
* @old_selector: selector for starting voltage
* @new_selector: selector for target voltage
*
@@ -2388,6 +2423,8 @@ static int _regulator_get_voltage(struct regulator_dev *rdev)
ret = rdev->desc->ops->list_voltage(rdev, sel);
} else if (rdev->desc->ops->get_voltage) {
ret = rdev->desc->ops->get_voltage(rdev);
+ } else if (rdev->desc->ops->list_voltage) {
+ ret = rdev->desc->ops->list_voltage(rdev, 0);
} else {
return -EINVAL;
}
@@ -2674,6 +2711,100 @@ out:
EXPORT_SYMBOL_GPL(regulator_set_optimum_mode);
/**
+ * regulator_set_bypass_regmap - Default set_bypass() using regmap
+ *
+ * @rdev: device to operate on.
+ * @enable: state to set.
+ */
+int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable)
+{
+ unsigned int val;
+
+ if (enable)
+ val = rdev->desc->bypass_mask;
+ else
+ val = 0;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg,
+ rdev->desc->bypass_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap);
+
+/**
+ * regulator_get_bypass_regmap - Default get_bypass() using regmap
+ *
+ * @rdev: device to operate on.
+ * @enable: current state.
+ */
+int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val);
+ if (ret != 0)
+ return ret;
+
+ *enable = val & rdev->desc->bypass_mask;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap);
+
+/**
+ * regulator_allow_bypass - allow the regulator to go into bypass mode
+ *
+ * @regulator: Regulator to configure
+ * @allow: enable or disable bypass mode
+ *
+ * Allow the regulator to go into bypass mode if all other consumers
+ * for the regulator also enable bypass mode and the machine
+ * constraints allow this. Bypass mode means that the regulator is
+ * simply passing the input directly to the output with no regulation.
+ */
+int regulator_allow_bypass(struct regulator *regulator, bool enable)
+{
+ struct regulator_dev *rdev = regulator->rdev;
+ int ret = 0;
+
+ if (!rdev->desc->ops->set_bypass)
+ return 0;
+
+ if (rdev->constraints &&
+ !(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_BYPASS))
+ return 0;
+
+ mutex_lock(&rdev->mutex);
+
+ if (enable && !regulator->bypass) {
+ rdev->bypass_count++;
+
+ if (rdev->bypass_count == rdev->open_count) {
+ ret = rdev->desc->ops->set_bypass(rdev, enable);
+ if (ret != 0)
+ rdev->bypass_count--;
+ }
+
+ } else if (!enable && regulator->bypass) {
+ rdev->bypass_count--;
+
+ if (rdev->bypass_count != rdev->open_count) {
+ ret = rdev->desc->ops->set_bypass(rdev, enable);
+ if (ret != 0)
+ rdev->bypass_count++;
+ }
+ }
+
+ if (ret == 0)
+ regulator->bypass = enable;
+
+ mutex_unlock(&rdev->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_allow_bypass);
+
+/**
* regulator_register_notifier - register regulator event notifier
* @regulator: regulator source
* @nb: notifier block
@@ -3011,7 +3142,8 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
/* some attributes need specific methods to be displayed */
if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) ||
- (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0)) {
+ (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0) ||
+ (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0)) {
status = device_create_file(dev, &dev_attr_microvolts);
if (status < 0)
return status;
@@ -3036,6 +3168,11 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
if (status < 0)
return status;
}
+ if (ops->get_bypass) {
+ status = device_create_file(dev, &dev_attr_bypass);
+ if (status < 0)
+ return status;
+ }
/* some attributes are type-specific */
if (rdev->desc->type == REGULATOR_CURRENT) {
@@ -3124,6 +3261,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
&rdev->use_count);
debugfs_create_u32("open_count", 0444, rdev->debugfs,
&rdev->open_count);
+ debugfs_create_u32("bypass_count", 0444, rdev->debugfs,
+ &rdev->bypass_count);
}
/**
@@ -3189,8 +3328,10 @@ regulator_register(const struct regulator_desc *regulator_desc,
rdev->desc = regulator_desc;
if (config->regmap)
rdev->regmap = config->regmap;
- else
+ else if (dev_get_regmap(dev, NULL))
rdev->regmap = dev_get_regmap(dev, NULL);
+ else if (dev->parent)
+ rdev->regmap = dev_get_regmap(dev->parent, NULL);
INIT_LIST_HEAD(&rdev->consumer_list);
INIT_LIST_HEAD(&rdev->list);
BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
diff --git a/drivers/regulator/da9052-regulator.c b/drivers/regulator/da9052-regulator.c
index 903299cf15cf..27355b1199e5 100644
--- a/drivers/regulator/da9052-regulator.c
+++ b/drivers/regulator/da9052-regulator.c
@@ -133,8 +133,8 @@ static int da9052_dcdc_set_current_limit(struct regulator_dev *rdev, int min_uA,
max_uA < da9052_current_limits[row][DA9052_MIN_UA])
return -EINVAL;
- for (i = 0; i < DA9052_CURRENT_RANGE; i++) {
- if (min_uA <= da9052_current_limits[row][i]) {
+ for (i = DA9052_CURRENT_RANGE - 1; i >= 0; i--) {
+ if (da9052_current_limits[row][i] <= max_uA) {
reg_val = i;
break;
}
diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c
index 86f655c7f7a1..03a1d7c11ef2 100644
--- a/drivers/regulator/dummy.c
+++ b/drivers/regulator/dummy.c
@@ -30,7 +30,7 @@ static struct regulator_init_data dummy_initdata;
static struct regulator_ops dummy_ops;
static struct regulator_desc dummy_desc = {
- .name = "dummy",
+ .name = "regulator-dummy",
.id = -1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
new file mode 100644
index 000000000000..339f4d732e97
--- /dev/null
+++ b/drivers/regulator/fan53555.c
@@ -0,0 +1,322 @@
+/*
+ * FAN53555 Fairchild Digitally Programmable TinyBuck Regulator Driver.
+ *
+ * Supported Part Numbers:
+ * FAN53555UC00X/01X/03X/04X/05X
+ *
+ * Copyright (c) 2012 Marvell Technology Ltd.
+ * Yunfan Zhang <yfzhang@marvell.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/param.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/regulator/fan53555.h>
+
+/* Voltage setting */
+#define FAN53555_VSEL0 0x00
+#define FAN53555_VSEL1 0x01
+/* Control register */
+#define FAN53555_CONTROL 0x02
+/* IC Type */
+#define FAN53555_ID1 0x03
+/* IC mask version */
+#define FAN53555_ID2 0x04
+/* Monitor register */
+#define FAN53555_MONITOR 0x05
+
+/* VSEL bit definitions */
+#define VSEL_BUCK_EN (1 << 7)
+#define VSEL_MODE (1 << 6)
+#define VSEL_NSEL_MASK 0x3F
+/* Chip ID and Verison */
+#define DIE_ID 0x0F /* ID1 */
+#define DIE_REV 0x0F /* ID2 */
+/* Control bit definitions */
+#define CTL_OUTPUT_DISCHG (1 << 7)
+#define CTL_SLEW_MASK (0x7 << 4)
+#define CTL_SLEW_SHIFT 4
+#define CTL_RESET (1 << 2)
+
+#define FAN53555_NVOLTAGES 64 /* Numbers of voltages */
+
+/* IC Type */
+enum {
+ FAN53555_CHIP_ID_00 = 0,
+ FAN53555_CHIP_ID_01,
+ FAN53555_CHIP_ID_02,
+ FAN53555_CHIP_ID_03,
+ FAN53555_CHIP_ID_04,
+ FAN53555_CHIP_ID_05,
+};
+
+struct fan53555_device_info {
+ struct regmap *regmap;
+ struct device *dev;
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct regulator_init_data *regulator;
+ /* IC Type and Rev */
+ int chip_id;
+ int chip_rev;
+ /* Voltage setting register */
+ unsigned int vol_reg;
+ unsigned int sleep_reg;
+ /* Voltage range and step(linear) */
+ unsigned int vsel_min;
+ unsigned int vsel_step;
+ /* Voltage slew rate limiting */
+ unsigned int slew_rate;
+ /* Sleep voltage cache */
+ unsigned int sleep_vol_cache;
+};
+
+static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ int ret;
+
+ if (di->sleep_vol_cache == uV)
+ return 0;
+ ret = regulator_map_voltage_linear(rdev, uV, uV);
+ if (ret < 0)
+ return -EINVAL;
+ ret = regmap_update_bits(di->regmap, di->sleep_reg,
+ VSEL_NSEL_MASK, ret);
+ if (ret < 0)
+ return -EINVAL;
+ /* Cache the sleep voltage setting.
+ * Might not be the real voltage which is rounded */
+ di->sleep_vol_cache = uV;
+
+ return 0;
+}
+
+static int fan53555_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ regmap_update_bits(di->regmap, di->vol_reg,
+ VSEL_MODE, VSEL_MODE);
+ break;
+ case REGULATOR_MODE_NORMAL:
+ regmap_update_bits(di->regmap, di->vol_reg, VSEL_MODE, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static unsigned int fan53555_get_mode(struct regulator_dev *rdev)
+{
+ struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+ unsigned int val;
+ int ret = 0;
+
+ ret = regmap_read(di->regmap, di->vol_reg, &val);
+ if (ret < 0)
+ return ret;
+ if (val & VSEL_MODE)
+ return REGULATOR_MODE_FAST;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops fan53555_regulator_ops = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .map_voltage = regulator_map_voltage_linear,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_suspend_voltage = fan53555_set_suspend_voltage,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_mode = fan53555_set_mode,
+ .get_mode = fan53555_get_mode,
+};
+
+/* For 00,01,03,05 options:
+ * VOUT = 0.60V + NSELx * 10mV, from 0.60 to 1.23V.
+ * For 04 option:
+ * VOUT = 0.603V + NSELx * 12.826mV, from 0.603 to 1.411V.
+ * */
+static int fan53555_device_setup(struct fan53555_device_info *di,
+ struct fan53555_platform_data *pdata)
+{
+ unsigned int reg, data, mask;
+
+ /* Setup voltage control register */
+ switch (pdata->sleep_vsel_id) {
+ case FAN53555_VSEL_ID_0:
+ di->sleep_reg = FAN53555_VSEL0;
+ di->vol_reg = FAN53555_VSEL1;
+ break;
+ case FAN53555_VSEL_ID_1:
+ di->sleep_reg = FAN53555_VSEL1;
+ di->vol_reg = FAN53555_VSEL0;
+ break;
+ default:
+ dev_err(di->dev, "Invalid VSEL ID!\n");
+ return -EINVAL;
+ }
+ /* Init voltage range and step */
+ switch (di->chip_id) {
+ case FAN53555_CHIP_ID_00:
+ case FAN53555_CHIP_ID_01:
+ case FAN53555_CHIP_ID_03:
+ case FAN53555_CHIP_ID_05:
+ di->vsel_min = 600000;
+ di->vsel_step = 10000;
+ break;
+ case FAN53555_CHIP_ID_04:
+ di->vsel_min = 603000;
+ di->vsel_step = 12826;
+ break;
+ default:
+ dev_err(di->dev,
+ "Chip ID[%d]\n not supported!\n", di->chip_id);
+ return -EINVAL;
+ }
+ /* Init slew rate */
+ if (pdata->slew_rate & 0x7)
+ di->slew_rate = pdata->slew_rate;
+ else
+ di->slew_rate = FAN53555_SLEW_RATE_64MV;
+ reg = FAN53555_CONTROL;
+ data = di->slew_rate << CTL_SLEW_SHIFT;
+ mask = CTL_SLEW_MASK;
+ return regmap_update_bits(di->regmap, reg, mask, data);
+}
+
+static int fan53555_regulator_register(struct fan53555_device_info *di,
+ struct regulator_config *config)
+{
+ struct regulator_desc *rdesc = &di->desc;
+
+ rdesc->name = "fan53555-reg";
+ rdesc->ops = &fan53555_regulator_ops;
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->n_voltages = FAN53555_NVOLTAGES;
+ rdesc->enable_reg = di->vol_reg;
+ rdesc->enable_mask = VSEL_BUCK_EN;
+ rdesc->min_uV = di->vsel_min;
+ rdesc->uV_step = di->vsel_step;
+ rdesc->vsel_reg = di->vol_reg;
+ rdesc->vsel_mask = VSEL_NSEL_MASK;
+ rdesc->owner = THIS_MODULE;
+
+ di->rdev = regulator_register(&di->desc, config);
+ if (IS_ERR(di->rdev))
+ return PTR_ERR(di->rdev);
+ return 0;
+
+}
+
+static struct regmap_config fan53555_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int __devinit fan53555_regulator_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct fan53555_device_info *di;
+ struct fan53555_platform_data *pdata;
+ struct regulator_config config = { };
+ unsigned int val;
+ int ret;
+
+ pdata = client->dev.platform_data;
+ if (!pdata || !pdata->regulator) {
+ dev_err(&client->dev, "Platform data not found!\n");
+ return -ENODEV;
+ }
+
+ di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info),
+ GFP_KERNEL);
+ if (!di) {
+ dev_err(&client->dev, "Failed to allocate device info data!\n");
+ return -ENOMEM;
+ }
+ di->regmap = devm_regmap_init_i2c(client, &fan53555_regmap_config);
+ if (IS_ERR(di->regmap)) {
+ dev_err(&client->dev, "Failed to allocate regmap!\n");
+ return PTR_ERR(di->regmap);
+ }
+ di->dev = &client->dev;
+ di->regulator = pdata->regulator;
+ i2c_set_clientdata(client, di);
+ /* Get chip ID */
+ ret = regmap_read(di->regmap, FAN53555_ID1, &val);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to get chip ID!\n");
+ return -ENODEV;
+ }
+ di->chip_id = val & DIE_ID;
+ /* Get chip revision */
+ ret = regmap_read(di->regmap, FAN53555_ID2, &val);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to get chip Rev!\n");
+ return -ENODEV;
+ }
+ di->chip_rev = val & DIE_REV;
+ dev_info(&client->dev, "FAN53555 Option[%d] Rev[%d] Detected!\n",
+ di->chip_id, di->chip_rev);
+ /* Device init */
+ ret = fan53555_device_setup(di, pdata);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to setup device!\n");
+ return ret;
+ }
+ /* Register regulator */
+ config.dev = di->dev;
+ config.init_data = di->regulator;
+ config.regmap = di->regmap;
+ config.driver_data = di;
+ ret = fan53555_regulator_register(di, &config);
+ if (ret < 0)
+ dev_err(&client->dev, "Failed to register regulator!\n");
+ return ret;
+
+}
+
+static int __devexit fan53555_regulator_remove(struct i2c_client *client)
+{
+ struct fan53555_device_info *di = i2c_get_clientdata(client);
+
+ regulator_unregister(di->rdev);
+ return 0;
+}
+
+static const struct i2c_device_id fan53555_id[] = {
+ {"fan53555", -1},
+ { },
+};
+
+static struct i2c_driver fan53555_regulator_driver = {
+ .driver = {
+ .name = "fan53555-regulator",
+ },
+ .probe = fan53555_regulator_probe,
+ .remove = __devexit_p(fan53555_regulator_remove),
+ .id_table = fan53555_id,
+};
+
+module_i2c_driver(fan53555_regulator_driver);
+
+MODULE_AUTHOR("Yunfan Zhang <yfzhang@marvell.com>");
+MODULE_DESCRIPTION("FAN53555 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c
index 1d145a07ada9..d8ecf49a5777 100644
--- a/drivers/regulator/isl6271a-regulator.c
+++ b/drivers/regulator/isl6271a-regulator.c
@@ -73,13 +73,7 @@ static struct regulator_ops isl_core_ops = {
.map_voltage = regulator_map_voltage_linear,
};
-static int isl6271a_get_fixed_voltage(struct regulator_dev *dev)
-{
- return dev->desc->min_uV;
-}
-
static struct regulator_ops isl_fixed_ops = {
- .get_voltage = isl6271a_get_fixed_voltage,
.list_voltage = regulator_list_voltage_linear,
};
diff --git a/drivers/regulator/lp872x.c b/drivers/regulator/lp872x.c
index 212c38eaba70..708f4b6a17dc 100644
--- a/drivers/regulator/lp872x.c
+++ b/drivers/regulator/lp872x.c
@@ -86,6 +86,10 @@
#define EXTERN_DVS_USED 0
#define MAX_DELAY 6
+/* Default DVS Mode */
+#define LP8720_DEFAULT_DVS 0
+#define LP8725_DEFAULT_DVS BIT(2)
+
/* dump registers in regmap-debugfs */
#define MAX_REGISTERS 0x0F
@@ -269,9 +273,9 @@ static int lp872x_regulator_enable_time(struct regulator_dev *rdev)
return val > MAX_DELAY ? 0 : val * time_step_us;
}
-static void lp872x_set_dvs(struct lp872x *lp, int gpio)
+static void lp872x_set_dvs(struct lp872x *lp, enum lp872x_dvs_sel dvs_sel,
+ int gpio)
{
- enum lp872x_dvs_sel dvs_sel = lp->pdata->dvs->vsel;
enum lp872x_dvs_state state;
state = dvs_sel == SEL_V1 ? DVS_HIGH : DVS_LOW;
@@ -339,10 +343,10 @@ static int lp872x_buck_set_voltage_sel(struct regulator_dev *rdev,
struct lp872x *lp = rdev_get_drvdata(rdev);
enum lp872x_regulator_id buck = rdev_get_id(rdev);
u8 addr, mask = LP872X_VOUT_M;
- struct lp872x_dvs *dvs = lp->pdata->dvs;
+ struct lp872x_dvs *dvs = lp->pdata ? lp->pdata->dvs : NULL;
if (dvs && gpio_is_valid(dvs->gpio))
- lp872x_set_dvs(lp, dvs->gpio);
+ lp872x_set_dvs(lp, dvs->vsel, dvs->gpio);
addr = lp872x_select_buck_vout_addr(lp, buck);
if (!lp872x_is_valid_buck_addr(addr))
@@ -374,8 +378,8 @@ static int lp8725_buck_set_current_limit(struct regulator_dev *rdev,
{
struct lp872x *lp = rdev_get_drvdata(rdev);
enum lp872x_regulator_id buck = rdev_get_id(rdev);
- int i, max = ARRAY_SIZE(lp8725_buck_uA);
- u8 addr, val;
+ int i;
+ u8 addr;
switch (buck) {
case LP8725_ID_BUCK1:
@@ -388,17 +392,15 @@ static int lp8725_buck_set_current_limit(struct regulator_dev *rdev,
return -EINVAL;
}
- for (i = 0 ; i < max ; i++)
+ for (i = ARRAY_SIZE(lp8725_buck_uA) - 1 ; i >= 0; i--) {
if (lp8725_buck_uA[i] >= min_uA &&
lp8725_buck_uA[i] <= max_uA)
- break;
-
- if (i == max)
- return -EINVAL;
-
- val = i << LP8725_BUCK_CL_S;
+ return lp872x_update_bits(lp, addr,
+ LP8725_BUCK_CL_M,
+ i << LP8725_BUCK_CL_S);
+ }
- return lp872x_update_bits(lp, addr, LP8725_BUCK_CL_M, val);
+ return -EINVAL;
}
static int lp8725_buck_get_current_limit(struct regulator_dev *rdev)
@@ -727,39 +729,16 @@ static struct regulator_desc lp8725_regulator_desc[] = {
},
};
-static int lp872x_check_dvs_validity(struct lp872x *lp)
-{
- struct lp872x_dvs *dvs = lp->pdata->dvs;
- u8 val = 0;
- int ret;
-
- ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val);
- if (ret)
- return ret;
-
- ret = 0;
- if (lp->chipid == LP8720) {
- if (val & LP8720_EXT_DVS_M)
- ret = dvs ? 0 : -EINVAL;
- } else {
- if ((val & LP8725_DVS1_M) == EXTERN_DVS_USED)
- ret = dvs ? 0 : -EINVAL;
- }
-
- return ret;
-}
-
static int lp872x_init_dvs(struct lp872x *lp)
{
int ret, gpio;
- struct lp872x_dvs *dvs = lp->pdata->dvs;
+ struct lp872x_dvs *dvs = lp->pdata ? lp->pdata->dvs : NULL;
enum lp872x_dvs_state pinstate;
+ u8 mask[] = { LP8720_EXT_DVS_M, LP8725_DVS1_M | LP8725_DVS2_M };
+ u8 default_dvs_mode[] = { LP8720_DEFAULT_DVS, LP8725_DEFAULT_DVS };
- ret = lp872x_check_dvs_validity(lp);
- if (ret) {
- dev_warn(lp->dev, "invalid dvs data: %d\n", ret);
- return ret;
- }
+ if (!dvs)
+ goto set_default_dvs_mode;
gpio = dvs->gpio;
if (!gpio_is_valid(gpio)) {
@@ -778,6 +757,10 @@ static int lp872x_init_dvs(struct lp872x *lp)
lp->dvs_gpio = gpio;
return 0;
+
+set_default_dvs_mode:
+ return lp872x_update_bits(lp, LP872X_GENERAL_CFG, mask[lp->chipid],
+ default_dvs_mode[lp->chipid]);
}
static int lp872x_config(struct lp872x *lp)
@@ -785,24 +768,29 @@ static int lp872x_config(struct lp872x *lp)
struct lp872x_platform_data *pdata = lp->pdata;
int ret;
- if (!pdata->update_config)
- return 0;
+ if (!pdata || !pdata->update_config)
+ goto init_dvs;
ret = lp872x_write_byte(lp, LP872X_GENERAL_CFG, pdata->general_config);
if (ret)
return ret;
+init_dvs:
return lp872x_init_dvs(lp);
}
static struct regulator_init_data
*lp872x_find_regulator_init_data(int id, struct lp872x *lp)
{
+ struct lp872x_platform_data *pdata = lp->pdata;
int i;
+ if (!pdata)
+ return NULL;
+
for (i = 0; i < lp->num_regulators; i++) {
- if (lp->pdata->regulator_data[i].id == id)
- return lp->pdata->regulator_data[i].init_data;
+ if (pdata->regulator_data[i].id == id)
+ return pdata->regulator_data[i].init_data;
}
return NULL;
@@ -863,18 +851,12 @@ static const struct regmap_config lp872x_regmap_config = {
static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
{
struct lp872x *lp;
- struct lp872x_platform_data *pdata = cl->dev.platform_data;
int ret, size, num_regulators;
const int lp872x_num_regulators[] = {
[LP8720] = LP8720_NUM_REGULATORS,
[LP8725] = LP8725_NUM_REGULATORS,
};
- if (!pdata) {
- dev_err(&cl->dev, "no platform data\n");
- return -EINVAL;
- }
-
lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL);
if (!lp)
goto err_mem;
@@ -894,7 +876,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
}
lp->dev = &cl->dev;
- lp->pdata = pdata;
+ lp->pdata = cl->dev.platform_data;
lp->chipid = id->driver_data;
lp->num_regulators = num_regulators;
i2c_set_clientdata(cl, lp);
diff --git a/drivers/regulator/lp8788-buck.c b/drivers/regulator/lp8788-buck.c
index 6356e821400f..ba3e0aa402de 100644
--- a/drivers/regulator/lp8788-buck.c
+++ b/drivers/regulator/lp8788-buck.c
@@ -69,6 +69,9 @@
#define PIN_HIGH 1
#define ENABLE_TIME_USEC 32
+#define BUCK_FPWM_MASK(x) (1 << (x))
+#define BUCK_FPWM_SHIFT(x) (x)
+
enum lp8788_dvs_state {
DVS_LOW = GPIOF_OUT_INIT_LOW,
DVS_HIGH = GPIOF_OUT_INIT_HIGH,
@@ -86,15 +89,9 @@ enum lp8788_buck_id {
BUCK4,
};
-struct lp8788_pwm_map {
- u8 mask;
- u8 shift;
-};
-
struct lp8788_buck {
struct lp8788 *lp;
struct regulator_dev *regulator;
- struct lp8788_pwm_map *pmap;
void *dvs;
};
@@ -106,29 +103,6 @@ static const int lp8788_buck_vtbl[] = {
1950000, 2000000,
};
-/* buck pwm mode selection : used for set/get_mode in regulator ops
- * @forced pwm : fast mode
- * @auto pwm : normal mode
- */
-static struct lp8788_pwm_map buck_pmap[] = {
- [BUCK1] = {
- .mask = LP8788_FPWM_BUCK1_M,
- .shift = LP8788_FPWM_BUCK1_S,
- },
- [BUCK2] = {
- .mask = LP8788_FPWM_BUCK2_M,
- .shift = LP8788_FPWM_BUCK2_S,
- },
- [BUCK3] = {
- .mask = LP8788_FPWM_BUCK3_M,
- .shift = LP8788_FPWM_BUCK3_S,
- },
- [BUCK4] = {
- .mask = LP8788_FPWM_BUCK4_M,
- .shift = LP8788_FPWM_BUCK4_S,
- },
-};
-
static const u8 buck1_vout_addr[] = {
LP8788_BUCK1_VOUT0, LP8788_BUCK1_VOUT1,
LP8788_BUCK1_VOUT2, LP8788_BUCK1_VOUT3,
@@ -347,41 +321,37 @@ static int lp8788_buck_enable_time(struct regulator_dev *rdev)
static int lp8788_buck_set_mode(struct regulator_dev *rdev, unsigned int mode)
{
struct lp8788_buck *buck = rdev_get_drvdata(rdev);
- struct lp8788_pwm_map *pmap = buck->pmap;
- u8 val;
-
- if (!pmap)
- return -EINVAL;
+ enum lp8788_buck_id id = rdev_get_id(rdev);
+ u8 mask, val;
+ mask = BUCK_FPWM_MASK(id);
switch (mode) {
case REGULATOR_MODE_FAST:
- val = LP8788_FORCE_PWM << pmap->shift;
+ val = LP8788_FORCE_PWM << BUCK_FPWM_SHIFT(id);
break;
case REGULATOR_MODE_NORMAL:
- val = LP8788_AUTO_PWM << pmap->shift;
+ val = LP8788_AUTO_PWM << BUCK_FPWM_SHIFT(id);
break;
default:
return -EINVAL;
}
- return lp8788_update_bits(buck->lp, LP8788_BUCK_PWM, pmap->mask, val);
+ return lp8788_update_bits(buck->lp, LP8788_BUCK_PWM, mask, val);
}
static unsigned int lp8788_buck_get_mode(struct regulator_dev *rdev)
{
struct lp8788_buck *buck = rdev_get_drvdata(rdev);
- struct lp8788_pwm_map *pmap = buck->pmap;
+ enum lp8788_buck_id id = rdev_get_id(rdev);
u8 val;
int ret;
- if (!pmap)
- return -EINVAL;
-
ret = lp8788_read_byte(buck->lp, LP8788_BUCK_PWM, &val);
if (ret)
return ret;
- return val & pmap->mask ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
+ return val & BUCK_FPWM_MASK(id) ?
+ REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
}
static struct regulator_ops lp8788_buck12_ops = {
@@ -459,27 +429,6 @@ static struct regulator_desc lp8788_buck_desc[] = {
},
};
-static int lp8788_set_default_dvs_ctrl_mode(struct lp8788 *lp,
- enum lp8788_buck_id id)
-{
- u8 mask, val;
-
- switch (id) {
- case BUCK1:
- mask = LP8788_BUCK1_DVS_SEL_M;
- val = LP8788_BUCK1_DVS_I2C;
- break;
- case BUCK2:
- mask = LP8788_BUCK2_DVS_SEL_M;
- val = LP8788_BUCK2_DVS_I2C;
- break;
- default:
- return 0;
- }
-
- return lp8788_update_bits(lp, LP8788_BUCK_DVS_SEL, mask, val);
-}
-
static int _gpio_request(struct lp8788_buck *buck, int gpio, char *name)
{
struct device *dev = buck->lp->dev;
@@ -530,6 +479,7 @@ static int lp8788_init_dvs(struct lp8788_buck *buck, enum lp8788_buck_id id)
struct lp8788_platform_data *pdata = buck->lp->pdata;
u8 mask[] = { LP8788_BUCK1_DVS_SEL_M, LP8788_BUCK2_DVS_SEL_M };
u8 val[] = { LP8788_BUCK1_DVS_PIN, LP8788_BUCK2_DVS_PIN };
+ u8 default_dvs_mode[] = { LP8788_BUCK1_DVS_I2C, LP8788_BUCK2_DVS_I2C };
/* no dvs for buck3, 4 */
if (id == BUCK3 || id == BUCK4)
@@ -550,7 +500,8 @@ static int lp8788_init_dvs(struct lp8788_buck *buck, enum lp8788_buck_id id)
val[id]);
set_default_dvs_mode:
- return lp8788_set_default_dvs_ctrl_mode(buck->lp, id);
+ return lp8788_update_bits(buck->lp, LP8788_BUCK_DVS_SEL, mask[id],
+ default_dvs_mode[id]);
}
static __devinit int lp8788_buck_probe(struct platform_device *pdev)
@@ -567,7 +518,6 @@ static __devinit int lp8788_buck_probe(struct platform_device *pdev)
return -ENOMEM;
buck->lp = lp;
- buck->pmap = &buck_pmap[id];
ret = lp8788_init_dvs(buck, id);
if (ret)
diff --git a/drivers/regulator/lp8788-ldo.c b/drivers/regulator/lp8788-ldo.c
index d2122e41a96d..6796eeb47dc6 100644
--- a/drivers/regulator/lp8788-ldo.c
+++ b/drivers/regulator/lp8788-ldo.c
@@ -496,6 +496,7 @@ static struct regulator_desc lp8788_dldo_desc[] = {
.name = "dldo12",
.id = DLDO12,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_B,
@@ -521,6 +522,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo2",
.id = ALDO2,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_B,
@@ -530,6 +532,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo3",
.id = ALDO3,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_B,
@@ -539,6 +542,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo4",
.id = ALDO4,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_B,
@@ -548,6 +552,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo5",
.id = ALDO5,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_C,
@@ -583,6 +588,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo8",
.id = ALDO8,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_C,
@@ -592,6 +598,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo9",
.id = ALDO9,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_C,
@@ -601,6 +608,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
.name = "aldo10",
.id = ALDO10,
.ops = &lp8788_ldo_voltage_fixed_ops,
+ .n_voltages = 1,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_reg = LP8788_EN_LDO_C,
diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c
index c564af6f05a3..2a67d08658ad 100644
--- a/drivers/regulator/max77686.c
+++ b/drivers/regulator/max77686.c
@@ -66,7 +66,7 @@ enum max77686_ramp_rate {
};
struct max77686_data {
- struct regulator_dev **rdev;
+ struct regulator_dev *rdev[MAX77686_REGULATORS];
};
static int max77686_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
@@ -265,6 +265,7 @@ static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev,
rmatch.of_node = NULL;
of_regulator_match(iodev->dev, regulators_np, &rmatch, 1);
rdata[i].initdata = rmatch.init_data;
+ rdata[i].of_node = rmatch.of_node;
}
pdata->regulators = rdata;
@@ -283,10 +284,8 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev)
{
struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev);
- struct regulator_dev **rdev;
struct max77686_data *max77686;
- int i, size;
- int ret = 0;
+ int i, ret = 0;
struct regulator_config config = { };
dev_dbg(&pdev->dev, "%s\n", __func__);
@@ -313,45 +312,38 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev)
if (!max77686)
return -ENOMEM;
- size = sizeof(struct regulator_dev *) * MAX77686_REGULATORS;
- max77686->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
- if (!max77686->rdev)
- return -ENOMEM;
-
- rdev = max77686->rdev;
config.dev = &pdev->dev;
config.regmap = iodev->regmap;
platform_set_drvdata(pdev, max77686);
for (i = 0; i < MAX77686_REGULATORS; i++) {
config.init_data = pdata->regulators[i].initdata;
+ config.of_node = pdata->regulators[i].of_node;
- rdev[i] = regulator_register(&regulators[i], &config);
- if (IS_ERR(rdev[i])) {
- ret = PTR_ERR(rdev[i]);
+ max77686->rdev[i] = regulator_register(&regulators[i], &config);
+ if (IS_ERR(max77686->rdev[i])) {
+ ret = PTR_ERR(max77686->rdev[i]);
dev_err(&pdev->dev,
"regulator init failed for %d\n", i);
- rdev[i] = NULL;
- goto err;
+ max77686->rdev[i] = NULL;
+ goto err;
}
}
return 0;
err:
while (--i >= 0)
- regulator_unregister(rdev[i]);
+ regulator_unregister(max77686->rdev[i]);
return ret;
}
static int __devexit max77686_pmic_remove(struct platform_device *pdev)
{
struct max77686_data *max77686 = platform_get_drvdata(pdev);
- struct regulator_dev **rdev = max77686->rdev;
int i;
for (i = 0; i < MAX77686_REGULATORS; i++)
- if (rdev[i])
- regulator_unregister(rdev[i]);
+ regulator_unregister(max77686->rdev[i]);
return 0;
}
diff --git a/drivers/regulator/max8907-regulator.c b/drivers/regulator/max8907-regulator.c
new file mode 100644
index 000000000000..af7607515ab9
--- /dev/null
+++ b/drivers/regulator/max8907-regulator.c
@@ -0,0 +1,408 @@
+/*
+ * max8907-regulator.c -- support regulators in max8907
+ *
+ * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.com>
+ * Copyright (C) 2010-2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Portions based on drivers/regulator/tps65910-regulator.c,
+ * Copyright 2010 Texas Instruments Inc.
+ * Author: Graeme Gregory <gg@slimlogic.co.uk>
+ * Author: Jorge Eduardo Candelaria <jedu@slimlogic.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/err.h>
+#include <linux/init.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8907.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define MAX8907_II2RR_VERSION_MASK 0xF0
+#define MAX8907_II2RR_VERSION_REV_A 0x00
+#define MAX8907_II2RR_VERSION_REV_B 0x10
+#define MAX8907_II2RR_VERSION_REV_C 0x30
+
+struct max8907_regulator {
+ struct regulator_desc desc[MAX8907_NUM_REGULATORS];
+ struct regulator_dev *rdev[MAX8907_NUM_REGULATORS];
+};
+
+#define REG_MBATT() \
+ [MAX8907_MBATT] = { \
+ .name = "MBATT", \
+ .supply_name = "mbatt", \
+ .id = MAX8907_MBATT, \
+ .ops = &max8907_mbatt_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }
+
+#define REG_LDO(ids, supply, base, min, max, step) \
+ [MAX8907_##ids] = { \
+ .name = #ids, \
+ .supply_name = supply, \
+ .id = MAX8907_##ids, \
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .ops = &max8907_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = (min), \
+ .uV_step = (step), \
+ .vsel_reg = (base) + MAX8907_VOUT, \
+ .vsel_mask = 0x3f, \
+ .enable_reg = (base) + MAX8907_CTL, \
+ .enable_mask = MAX8907_MASK_LDO_EN, \
+ }
+
+#define REG_FIXED(ids, supply, voltage) \
+ [MAX8907_##ids] = { \
+ .name = #ids, \
+ .supply_name = supply, \
+ .id = MAX8907_##ids, \
+ .n_voltages = 1, \
+ .ops = &max8907_fixed_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = (voltage), \
+ }
+
+#define REG_OUT5V(ids, supply, base, voltage) \
+ [MAX8907_##ids] = { \
+ .name = #ids, \
+ .supply_name = supply, \
+ .id = MAX8907_##ids, \
+ .n_voltages = 1, \
+ .ops = &max8907_out5v_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = (voltage), \
+ .enable_reg = (base), \
+ .enable_mask = MAX8907_MASK_OUT5V_EN, \
+ }
+
+#define REG_BBAT(ids, supply, base, min, max, step) \
+ [MAX8907_##ids] = { \
+ .name = #ids, \
+ .supply_name = supply, \
+ .id = MAX8907_##ids, \
+ .n_voltages = ((max) - (min)) / (step) + 1, \
+ .ops = &max8907_bbat_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .min_uV = (min), \
+ .uV_step = (step), \
+ .vsel_reg = (base), \
+ .vsel_mask = MAX8907_MASK_VBBATTCV, \
+ }
+
+#define LDO_750_50(id, supply, base) REG_LDO(id, supply, (base), \
+ 750000, 3900000, 50000)
+#define LDO_650_25(id, supply, base) REG_LDO(id, supply, (base), \
+ 650000, 2225000, 25000)
+
+static struct regulator_ops max8907_mbatt_ops = {
+};
+
+static struct regulator_ops max8907_ldo_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static struct regulator_ops max8907_ldo_hwctl_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops max8907_fixed_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_ops max8907_out5v_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static struct regulator_ops max8907_out5v_hwctl_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_ops max8907_bbat_ops = {
+ .list_voltage = regulator_list_voltage_linear,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_desc max8907_regulators[] = {
+ REG_MBATT(),
+ REG_LDO(SD1, "in-v1", MAX8907_REG_SDCTL1, 650000, 2225000, 25000),
+ REG_LDO(SD2, "in-v2", MAX8907_REG_SDCTL2, 637500, 1425000, 12500),
+ REG_LDO(SD3, "in-v3", MAX8907_REG_SDCTL3, 750000, 3900000, 50000),
+ LDO_750_50(LDO1, "in1", MAX8907_REG_LDOCTL1),
+ LDO_650_25(LDO2, "in2", MAX8907_REG_LDOCTL2),
+ LDO_650_25(LDO3, "in3", MAX8907_REG_LDOCTL3),
+ LDO_750_50(LDO4, "in4", MAX8907_REG_LDOCTL4),
+ LDO_750_50(LDO5, "in5", MAX8907_REG_LDOCTL5),
+ LDO_750_50(LDO6, "in6", MAX8907_REG_LDOCTL6),
+ LDO_750_50(LDO7, "in7", MAX8907_REG_LDOCTL7),
+ LDO_750_50(LDO8, "in8", MAX8907_REG_LDOCTL8),
+ LDO_750_50(LDO9, "in9", MAX8907_REG_LDOCTL9),
+ LDO_750_50(LDO10, "in10", MAX8907_REG_LDOCTL10),
+ LDO_750_50(LDO11, "in11", MAX8907_REG_LDOCTL11),
+ LDO_750_50(LDO12, "in12", MAX8907_REG_LDOCTL12),
+ LDO_750_50(LDO13, "in13", MAX8907_REG_LDOCTL13),
+ LDO_750_50(LDO14, "in14", MAX8907_REG_LDOCTL14),
+ LDO_750_50(LDO15, "in15", MAX8907_REG_LDOCTL15),
+ LDO_750_50(LDO16, "in16", MAX8907_REG_LDOCTL16),
+ LDO_650_25(LDO17, "in17", MAX8907_REG_LDOCTL17),
+ LDO_650_25(LDO18, "in18", MAX8907_REG_LDOCTL18),
+ LDO_750_50(LDO19, "in19", MAX8907_REG_LDOCTL19),
+ LDO_750_50(LDO20, "in20", MAX8907_REG_LDOCTL20),
+ REG_OUT5V(OUT5V, "mbatt", MAX8907_REG_OUT5VEN, 5000000),
+ REG_OUT5V(OUT33V, "mbatt", MAX8907_REG_OUT33VEN, 3300000),
+ REG_BBAT(BBAT, "MBATT", MAX8907_REG_BBAT_CNFG,
+ 2400000, 3000000, 200000),
+ REG_FIXED(SDBY, "MBATT", 1200000),
+ REG_FIXED(VRTC, "MBATT", 3300000),
+};
+
+#ifdef CONFIG_OF
+
+#define MATCH(_name, _id) \
+ [MAX8907_##_id] = { \
+ .name = #_name, \
+ .driver_data = (void *)&max8907_regulators[MAX8907_##_id], \
+ }
+
+static struct of_regulator_match max8907_matches[] = {
+ MATCH(mbatt, MBATT),
+ MATCH(sd1, SD1),
+ MATCH(sd2, SD2),
+ MATCH(sd3, SD3),
+ MATCH(ldo1, LDO1),
+ MATCH(ldo2, LDO2),
+ MATCH(ldo3, LDO3),
+ MATCH(ldo4, LDO4),
+ MATCH(ldo5, LDO5),
+ MATCH(ldo6, LDO6),
+ MATCH(ldo7, LDO7),
+ MATCH(ldo8, LDO8),
+ MATCH(ldo9, LDO9),
+ MATCH(ldo10, LDO10),
+ MATCH(ldo11, LDO11),
+ MATCH(ldo12, LDO12),
+ MATCH(ldo13, LDO13),
+ MATCH(ldo14, LDO14),
+ MATCH(ldo15, LDO15),
+ MATCH(ldo16, LDO16),
+ MATCH(ldo17, LDO17),
+ MATCH(ldo18, LDO18),
+ MATCH(ldo19, LDO19),
+ MATCH(ldo20, LDO20),
+ MATCH(out5v, OUT5V),
+ MATCH(out33v, OUT33V),
+ MATCH(bbat, BBAT),
+ MATCH(sdby, SDBY),
+ MATCH(vrtc, VRTC),
+};
+
+static int max8907_regulator_parse_dt(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.parent->of_node;
+ struct device_node *regulators;
+ int ret;
+
+ if (!pdev->dev.parent->of_node)
+ return 0;
+
+ regulators = of_find_node_by_name(np, "regulators");
+ if (!regulators) {
+ dev_err(&pdev->dev, "regulators node not found\n");
+ return -EINVAL;
+ }
+
+ ret = of_regulator_match(pdev->dev.parent, regulators,
+ max8907_matches,
+ ARRAY_SIZE(max8907_matches));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error parsing regulator init data: %d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static inline struct regulator_init_data *match_init_data(int index)
+{
+ return max8907_matches[index].init_data;
+}
+
+static inline struct device_node *match_of_node(int index)
+{
+ return max8907_matches[index].of_node;
+}
+#else
+static int max8907_regulator_parse_dt(struct platform_device *pdev)
+{
+ return 0;
+}
+
+static inline struct regulator_init_data *match_init_data(int index)
+{
+ return NULL;
+}
+
+static inline struct device_node *match_of_node(int index)
+{
+ return NULL;
+}
+#endif
+
+static __devinit int max8907_regulator_probe(struct platform_device *pdev)
+{
+ struct max8907 *max8907 = dev_get_drvdata(pdev->dev.parent);
+ struct max8907_platform_data *pdata = dev_get_platdata(max8907->dev);
+ int ret;
+ struct max8907_regulator *pmic;
+ unsigned int val;
+ int i;
+ struct regulator_config config = {};
+ struct regulator_init_data *idata;
+ const char *mbatt_rail_name = NULL;
+
+ ret = max8907_regulator_parse_dt(pdev);
+ if (ret)
+ return ret;
+
+ pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic) {
+ dev_err(&pdev->dev, "Failed to alloc pmic\n");
+ return -ENOMEM;
+ }
+ platform_set_drvdata(pdev, pmic);
+
+ memcpy(pmic->desc, max8907_regulators, sizeof(pmic->desc));
+
+ /* Backwards compatibility with MAX8907B; SD1 uses different voltages */
+ regmap_read(max8907->regmap_gen, MAX8907_REG_II2RR, &val);
+ if ((val & MAX8907_II2RR_VERSION_MASK) ==
+ MAX8907_II2RR_VERSION_REV_B) {
+ pmic->desc[MAX8907_SD1].min_uV = 637500;
+ pmic->desc[MAX8907_SD1].uV_step = 12500;
+ pmic->desc[MAX8907_SD1].n_voltages =
+ (1425000 - 637500) / 12500 + 1;
+ }
+
+ for (i = 0; i < MAX8907_NUM_REGULATORS; i++) {
+ config.dev = pdev->dev.parent;
+ if (pdata)
+ idata = pdata->init_data[i];
+ else
+ idata = match_init_data(i);
+ config.init_data = idata;
+ config.driver_data = pmic;
+ config.regmap = max8907->regmap_gen;
+ config.of_node = match_of_node(i);
+
+ switch (pmic->desc[i].id) {
+ case MAX8907_MBATT:
+ if (idata && idata->constraints.name)
+ mbatt_rail_name = idata->constraints.name;
+ else
+ mbatt_rail_name = pmic->desc[i].name;
+ break;
+ case MAX8907_BBAT:
+ case MAX8907_SDBY:
+ case MAX8907_VRTC:
+ idata->supply_regulator = mbatt_rail_name;
+ break;
+ }
+
+ if (pmic->desc[i].ops == &max8907_ldo_ops) {
+ regmap_read(config.regmap, pmic->desc[i].enable_reg,
+ &val);
+ if ((val & MAX8907_MASK_LDO_SEQ) !=
+ MAX8907_MASK_LDO_SEQ)
+ pmic->desc[i].ops = &max8907_ldo_hwctl_ops;
+ } else if (pmic->desc[i].ops == &max8907_out5v_ops) {
+ regmap_read(config.regmap, pmic->desc[i].enable_reg,
+ &val);
+ if ((val & (MAX8907_MASK_OUT5V_VINEN |
+ MAX8907_MASK_OUT5V_ENSRC)) !=
+ MAX8907_MASK_OUT5V_ENSRC)
+ pmic->desc[i].ops = &max8907_out5v_hwctl_ops;
+ }
+
+ pmic->rdev[i] = regulator_register(&pmic->desc[i], &config);
+ if (IS_ERR(pmic->rdev[i])) {
+ dev_err(&pdev->dev,
+ "failed to register %s regulator\n",
+ pmic->desc[i].name);
+ ret = PTR_ERR(pmic->rdev[i]);
+ goto err_unregister_regulator;
+ }
+ }
+
+ return 0;
+
+err_unregister_regulator:
+ while (--i >= 0)
+ regulator_unregister(pmic->rdev[i]);
+ return ret;
+}
+
+static __devexit int max8907_regulator_remove(struct platform_device *pdev)
+{
+ struct max8907_regulator *pmic = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < MAX8907_NUM_REGULATORS; i++)
+ regulator_unregister(pmic->rdev[i]);
+
+ return 0;
+}
+
+static struct platform_driver max8907_regulator_driver = {
+ .driver = {
+ .name = "max8907-regulator",
+ .owner = THIS_MODULE,
+ },
+ .probe = max8907_regulator_probe,
+ .remove = __devexit_p(max8907_regulator_remove),
+};
+
+static int __init max8907_regulator_init(void)
+{
+ return platform_driver_register(&max8907_regulator_driver);
+}
+
+subsys_initcall(max8907_regulator_init);
+
+static void __exit max8907_reg_exit(void)
+{
+ platform_driver_unregister(&max8907_regulator_driver);
+}
+
+module_exit(max8907_reg_exit);
+
+MODULE_DESCRIPTION("MAX8907 regulator driver");
+MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:max8907-regulator");
diff --git a/drivers/regulator/mc13783-regulator.c b/drivers/regulator/mc13783-regulator.c
index 4932e3449fe1..0801a6d0c122 100644
--- a/drivers/regulator/mc13783-regulator.c
+++ b/drivers/regulator/mc13783-regulator.c
@@ -21,6 +21,30 @@
#include <linux/module.h>
#include "mc13xxx.h"
+#define MC13783_REG_SWITCHERS0 24
+/* Enable does not exist for SW1A */
+#define MC13783_REG_SWITCHERS0_SW1AEN 0
+#define MC13783_REG_SWITCHERS0_SW1AVSEL 0
+#define MC13783_REG_SWITCHERS0_SW1AVSEL_M (63 << 0)
+
+#define MC13783_REG_SWITCHERS1 25
+/* Enable does not exist for SW1B */
+#define MC13783_REG_SWITCHERS1_SW1BEN 0
+#define MC13783_REG_SWITCHERS1_SW1BVSEL 0
+#define MC13783_REG_SWITCHERS1_SW1BVSEL_M (63 << 0)
+
+#define MC13783_REG_SWITCHERS2 26
+/* Enable does not exist for SW2A */
+#define MC13783_REG_SWITCHERS2_SW2AEN 0
+#define MC13783_REG_SWITCHERS2_SW2AVSEL 0
+#define MC13783_REG_SWITCHERS2_SW2AVSEL_M (63 << 0)
+
+#define MC13783_REG_SWITCHERS3 27
+/* Enable does not exist for SW2B */
+#define MC13783_REG_SWITCHERS3_SW2BEN 0
+#define MC13783_REG_SWITCHERS3_SW2BVSEL 0
+#define MC13783_REG_SWITCHERS3_SW2BVSEL_M (63 << 0)
+
#define MC13783_REG_SWITCHERS5 29
#define MC13783_REG_SWITCHERS5_SW3EN (1 << 20)
#define MC13783_REG_SWITCHERS5_SW3VSEL 18
@@ -93,6 +117,44 @@
/* Voltage Values */
+static const int mc13783_sw1x_val[] = {
+ 900000, 925000, 950000, 975000,
+ 1000000, 1025000, 1050000, 1075000,
+ 1100000, 1125000, 1150000, 1175000,
+ 1200000, 1225000, 1250000, 1275000,
+ 1300000, 1325000, 1350000, 1375000,
+ 1400000, 1425000, 1450000, 1475000,
+ 1500000, 1525000, 1550000, 1575000,
+ 1600000, 1625000, 1650000, 1675000,
+ 1700000, 1700000, 1700000, 1700000,
+ 1800000, 1800000, 1800000, 1800000,
+ 1850000, 1850000, 1850000, 1850000,
+ 2000000, 2000000, 2000000, 2000000,
+ 2100000, 2100000, 2100000, 2100000,
+ 2200000, 2200000, 2200000, 2200000,
+ 2200000, 2200000, 2200000, 2200000,
+ 2200000, 2200000, 2200000, 2200000,
+};
+
+static const int mc13783_sw2x_val[] = {
+ 900000, 925000, 950000, 975000,
+ 1000000, 1025000, 1050000, 1075000,
+ 1100000, 1125000, 1150000, 1175000,
+ 1200000, 1225000, 1250000, 1275000,
+ 1300000, 1325000, 1350000, 1375000,
+ 1400000, 1425000, 1450000, 1475000,
+ 1500000, 1525000, 1550000, 1575000,
+ 1600000, 1625000, 1650000, 1675000,
+ 1700000, 1700000, 1700000, 1700000,
+ 1800000, 1800000, 1800000, 1800000,
+ 1900000, 1900000, 1900000, 1900000,
+ 2000000, 2000000, 2000000, 2000000,
+ 2100000, 2100000, 2100000, 2100000,
+ 2200000, 2200000, 2200000, 2200000,
+ 2200000, 2200000, 2200000, 2200000,
+ 2200000, 2200000, 2200000, 2200000,
+};
+
static const unsigned int mc13783_sw3_val[] = {
5000000, 5000000, 5000000, 5500000,
};
@@ -188,6 +250,10 @@ static struct regulator_ops mc13783_gpo_regulator_ops;
MC13783_DEFINE(REG, _name, _reg, _vsel_reg, _voltages)
static struct mc13xxx_regulator mc13783_regulators[] = {
+ MC13783_DEFINE_SW(SW1A, SWITCHERS0, SWITCHERS0, mc13783_sw1x_val),
+ MC13783_DEFINE_SW(SW1B, SWITCHERS1, SWITCHERS1, mc13783_sw1x_val),
+ MC13783_DEFINE_SW(SW2A, SWITCHERS2, SWITCHERS2, mc13783_sw2x_val),
+ MC13783_DEFINE_SW(SW2B, SWITCHERS3, SWITCHERS3, mc13783_sw2x_val),
MC13783_DEFINE_SW(SW3, SWITCHERS5, SWITCHERS5, mc13783_sw3_val),
MC13783_FIXED_DEFINE(REG, VAUDIO, REGULATORMODE0, mc13783_vaudio_val),
@@ -238,9 +304,10 @@ static int mc13783_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
BUG_ON(val & ~mask);
+ mc13xxx_lock(priv->mc13xxx);
ret = mc13xxx_reg_read(mc13783, MC13783_REG_POWERMISC, &valread);
if (ret)
- return ret;
+ goto out;
/* Update the stored state for Power Gates. */
priv->powermisc_pwgt_state =
@@ -253,7 +320,10 @@ static int mc13783_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
valread = (valread & ~MC13783_REG_POWERMISC_PWGTSPI_M) |
priv->powermisc_pwgt_state;
- return mc13xxx_reg_write(mc13783, MC13783_REG_POWERMISC, valread);
+ ret = mc13xxx_reg_write(mc13783, MC13783_REG_POWERMISC, valread);
+out:
+ mc13xxx_unlock(priv->mc13xxx);
+ return ret;
}
static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev)
@@ -261,7 +331,6 @@ static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev)
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
int id = rdev_get_id(rdev);
- int ret;
u32 en_val = mc13xxx_regulators[id].enable_bit;
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
@@ -271,12 +340,8 @@ static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev)
id == MC13783_REG_PWGT2SPI)
en_val = 0;
- mc13xxx_lock(priv->mc13xxx);
- ret = mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
+ return mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
en_val);
- mc13xxx_unlock(priv->mc13xxx);
-
- return ret;
}
static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev)
@@ -284,7 +349,6 @@ static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev)
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
int id = rdev_get_id(rdev);
- int ret;
u32 dis_val = 0;
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
@@ -294,12 +358,8 @@ static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev)
id == MC13783_REG_PWGT2SPI)
dis_val = mc13xxx_regulators[id].enable_bit;
- mc13xxx_lock(priv->mc13xxx);
- ret = mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
+ return mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
dis_val);
- mc13xxx_unlock(priv->mc13xxx);
-
- return ret;
}
static int mc13783_gpo_regulator_is_enabled(struct regulator_dev *rdev)
@@ -330,7 +390,6 @@ static struct regulator_ops mc13783_gpo_regulator_ops = {
.is_enabled = mc13783_gpo_regulator_is_enabled,
.list_voltage = regulator_list_voltage_table,
.set_voltage = mc13xxx_fixed_regulator_set_voltage,
- .get_voltage = mc13xxx_fixed_regulator_get_voltage,
};
static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
diff --git a/drivers/regulator/mc13892-regulator.c b/drivers/regulator/mc13892-regulator.c
index b388b746452e..1fa63812f7ac 100644
--- a/drivers/regulator/mc13892-regulator.c
+++ b/drivers/regulator/mc13892-regulator.c
@@ -305,9 +305,10 @@ static int mc13892_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
BUG_ON(val & ~mask);
+ mc13xxx_lock(priv->mc13xxx);
ret = mc13xxx_reg_read(mc13892, MC13892_POWERMISC, &valread);
if (ret)
- return ret;
+ goto out;
/* Update the stored state for Power Gates. */
priv->powermisc_pwgt_state =
@@ -320,14 +321,16 @@ static int mc13892_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
valread = (valread & ~MC13892_POWERMISC_PWGTSPI_M) |
priv->powermisc_pwgt_state;
- return mc13xxx_reg_write(mc13892, MC13892_POWERMISC, valread);
+ ret = mc13xxx_reg_write(mc13892, MC13892_POWERMISC, valread);
+out:
+ mc13xxx_unlock(priv->mc13xxx);
+ return ret;
}
static int mc13892_gpo_regulator_enable(struct regulator_dev *rdev)
{
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
int id = rdev_get_id(rdev);
- int ret;
u32 en_val = mc13892_regulators[id].enable_bit;
u32 mask = mc13892_regulators[id].enable_bit;
@@ -340,18 +343,13 @@ static int mc13892_gpo_regulator_enable(struct regulator_dev *rdev)
if (id == MC13892_GPO4)
mask |= MC13892_POWERMISC_GPO4ADINEN;
- mc13xxx_lock(priv->mc13xxx);
- ret = mc13892_powermisc_rmw(priv, mask, en_val);
- mc13xxx_unlock(priv->mc13xxx);
-
- return ret;
+ return mc13892_powermisc_rmw(priv, mask, en_val);
}
static int mc13892_gpo_regulator_disable(struct regulator_dev *rdev)
{
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
int id = rdev_get_id(rdev);
- int ret;
u32 dis_val = 0;
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
@@ -360,12 +358,8 @@ static int mc13892_gpo_regulator_disable(struct regulator_dev *rdev)
if (id == MC13892_PWGT1SPI || id == MC13892_PWGT2SPI)
dis_val = mc13892_regulators[id].enable_bit;
- mc13xxx_lock(priv->mc13xxx);
- ret = mc13892_powermisc_rmw(priv, mc13892_regulators[id].enable_bit,
+ return mc13892_powermisc_rmw(priv, mc13892_regulators[id].enable_bit,
dis_val);
- mc13xxx_unlock(priv->mc13xxx);
-
- return ret;
}
static int mc13892_gpo_regulator_is_enabled(struct regulator_dev *rdev)
@@ -396,14 +390,13 @@ static struct regulator_ops mc13892_gpo_regulator_ops = {
.is_enabled = mc13892_gpo_regulator_is_enabled,
.list_voltage = regulator_list_voltage_table,
.set_voltage = mc13xxx_fixed_regulator_set_voltage,
- .get_voltage = mc13xxx_fixed_regulator_get_voltage,
};
-static int mc13892_sw_regulator_get_voltage(struct regulator_dev *rdev)
+static int mc13892_sw_regulator_get_voltage_sel(struct regulator_dev *rdev)
{
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
int ret, id = rdev_get_id(rdev);
- unsigned int val, hi;
+ unsigned int val;
dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
@@ -414,17 +407,11 @@ static int mc13892_sw_regulator_get_voltage(struct regulator_dev *rdev)
if (ret)
return ret;
- hi = val & MC13892_SWITCHERS0_SWxHI;
val = (val & mc13892_regulators[id].vsel_mask)
>> mc13892_regulators[id].vsel_shift;
dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val);
- if (hi)
- val = (25000 * val) + 1100000;
- else
- val = (25000 * val) + 600000;
-
return val;
}
@@ -432,37 +419,25 @@ static int mc13892_sw_regulator_set_voltage_sel(struct regulator_dev *rdev,
unsigned selector)
{
struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
- int hi, value, mask, id = rdev_get_id(rdev);
- u32 valread;
+ int volt, mask, id = rdev_get_id(rdev);
+ u32 reg_value;
int ret;
- value = rdev->desc->volt_table[selector];
+ volt = rdev->desc->volt_table[selector];
+ mask = mc13892_regulators[id].vsel_mask;
+ reg_value = selector << mc13892_regulators[id].vsel_shift;
+
+ if (volt > 1375000) {
+ mask |= MC13892_SWITCHERS0_SWxHI;
+ reg_value |= MC13892_SWITCHERS0_SWxHI;
+ } else if (volt < 1100000) {
+ mask |= MC13892_SWITCHERS0_SWxHI;
+ reg_value &= ~MC13892_SWITCHERS0_SWxHI;
+ }
mc13xxx_lock(priv->mc13xxx);
- ret = mc13xxx_reg_read(priv->mc13xxx,
- mc13892_regulators[id].vsel_reg, &valread);
- if (ret)
- goto err;
-
- if (value > 1375000)
- hi = 1;
- else if (value < 1100000)
- hi = 0;
- else
- hi = valread & MC13892_SWITCHERS0_SWxHI;
-
- if (hi) {
- value = (value - 1100000) / 25000;
- value |= MC13892_SWITCHERS0_SWxHI;
- } else
- value = (value - 600000) / 25000;
-
- mask = mc13892_regulators[id].vsel_mask | MC13892_SWITCHERS0_SWxHI;
- valread = (valread & ~mask) |
- (value << mc13892_regulators[id].vsel_shift);
- ret = mc13xxx_reg_write(priv->mc13xxx, mc13892_regulators[id].vsel_reg,
- valread);
-err:
+ ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13892_regulators[id].reg, mask,
+ reg_value);
mc13xxx_unlock(priv->mc13xxx);
return ret;
@@ -471,7 +446,7 @@ err:
static struct regulator_ops mc13892_sw_regulator_ops = {
.list_voltage = regulator_list_voltage_table,
.set_voltage_sel = mc13892_sw_regulator_set_voltage_sel,
- .get_voltage = mc13892_sw_regulator_get_voltage,
+ .get_voltage_sel = mc13892_sw_regulator_get_voltage_sel,
};
static int mc13892_vcam_set_mode(struct regulator_dev *rdev, unsigned int mode)
diff --git a/drivers/regulator/mc13xxx-regulator-core.c b/drivers/regulator/mc13xxx-regulator-core.c
index d6eda28ca5d0..88cbb832d555 100644
--- a/drivers/regulator/mc13xxx-regulator-core.c
+++ b/drivers/regulator/mc13xxx-regulator-core.c
@@ -143,30 +143,21 @@ int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
__func__, id, min_uV, max_uV);
if (min_uV <= rdev->desc->volt_table[0] &&
- rdev->desc->volt_table[0] <= max_uV)
+ rdev->desc->volt_table[0] <= max_uV) {
+ *selector = 0;
return 0;
- else
+ } else {
return -EINVAL;
+ }
}
EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_set_voltage);
-int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev)
-{
- int id = rdev_get_id(rdev);
-
- dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
-
- return rdev->desc->volt_table[0];
-}
-EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_get_voltage);
-
struct regulator_ops mc13xxx_fixed_regulator_ops = {
.enable = mc13xxx_regulator_enable,
.disable = mc13xxx_regulator_disable,
.is_enabled = mc13xxx_regulator_is_enabled,
.list_voltage = regulator_list_voltage_table,
.set_voltage = mc13xxx_fixed_regulator_set_voltage,
- .get_voltage = mc13xxx_fixed_regulator_get_voltage,
};
EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_ops);
diff --git a/drivers/regulator/mc13xxx.h b/drivers/regulator/mc13xxx.h
index eaff5510b6df..06c8903f182a 100644
--- a/drivers/regulator/mc13xxx.h
+++ b/drivers/regulator/mc13xxx.h
@@ -34,7 +34,6 @@ struct mc13xxx_regulator_priv {
extern int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev,
int min_uV, int max_uV, unsigned *selector);
-extern int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev);
#ifdef CONFIG_OF
extern int mc13xxx_get_num_regulators_dt(struct platform_device *pdev);
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 3e4106f2bda9..6f684916fd79 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -92,16 +92,18 @@ struct regulator_init_data *of_get_regulator_init_data(struct device *dev,
EXPORT_SYMBOL_GPL(of_get_regulator_init_data);
/**
- * of_regulator_match - extract regulator init data when node
- * property "regulator-compatible" matches with the regulator name.
+ * of_regulator_match - extract multiple regulator init data from device tree.
* @dev: device requesting the data
* @node: parent device node of the regulators
* @matches: match table for the regulators
* @num_matches: number of entries in match table
*
- * This function uses a match table specified by the regulator driver and
- * looks up the corresponding init data in the device tree if
- * regulator-compatible matches. Note that the match table is modified
+ * This function uses a match table specified by the regulator driver to
+ * parse regulator init data from the device tree. @node is expected to
+ * contain a set of child nodes, each providing the init data for one
+ * regulator. The data parsed from a child node will be matched to a regulator
+ * based on either the deprecated property regulator-compatible if present,
+ * or otherwise the child node's name. Note that the match table is modified
* in place.
*
* Returns the number of matches found or a negative error code on failure.
@@ -112,26 +114,23 @@ int of_regulator_match(struct device *dev, struct device_node *node,
{
unsigned int count = 0;
unsigned int i;
- const char *regulator_comp;
+ const char *name;
struct device_node *child;
if (!dev || !node)
return -EINVAL;
for_each_child_of_node(node, child) {
- regulator_comp = of_get_property(child,
+ name = of_get_property(child,
"regulator-compatible", NULL);
- if (!regulator_comp) {
- dev_err(dev, "regulator-compatible is missing for node %s\n",
- child->name);
- continue;
- }
+ if (!name)
+ name = child->name;
for (i = 0; i < num_matches; i++) {
struct of_regulator_match *match = &matches[i];
if (match->of_node)
continue;
- if (strcmp(match->name, regulator_comp))
+ if (strcmp(match->name, name))
continue;
match->init_data =
diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c
index 46c7e88f8381..2ba7502fa3b2 100644
--- a/drivers/regulator/palmas-regulator.c
+++ b/drivers/regulator/palmas-regulator.c
@@ -443,44 +443,6 @@ static int palmas_list_voltage_ldo(struct regulator_dev *dev,
return 850000 + (selector * 50000);
}
-static int palmas_get_voltage_ldo_sel(struct regulator_dev *dev)
-{
- struct palmas_pmic *pmic = rdev_get_drvdata(dev);
- int id = rdev_get_id(dev);
- int selector;
- unsigned int reg;
- unsigned int addr;
-
- addr = palmas_regs_info[id].vsel_addr;
-
- palmas_ldo_read(pmic->palmas, addr, &reg);
-
- selector = reg & PALMAS_LDO1_VOLTAGE_VSEL_MASK;
-
- /* Adjust selector to match list_voltage ranges */
- if (selector > 49)
- selector = 49;
-
- return selector;
-}
-
-static int palmas_set_voltage_ldo_sel(struct regulator_dev *dev,
- unsigned selector)
-{
- struct palmas_pmic *pmic = rdev_get_drvdata(dev);
- int id = rdev_get_id(dev);
- unsigned int reg = 0;
- unsigned int addr;
-
- addr = palmas_regs_info[id].vsel_addr;
-
- reg = selector;
-
- palmas_ldo_write(pmic->palmas, addr, reg);
-
- return 0;
-}
-
static int palmas_map_voltage_ldo(struct regulator_dev *rdev,
int min_uV, int max_uV)
{
@@ -505,8 +467,8 @@ static struct regulator_ops palmas_ops_ldo = {
.is_enabled = palmas_is_enabled_ldo,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
- .get_voltage_sel = palmas_get_voltage_ldo_sel,
- .set_voltage_sel = palmas_set_voltage_ldo_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
.list_voltage = palmas_list_voltage_ldo,
.map_voltage = palmas_map_voltage_ldo,
};
@@ -757,6 +719,9 @@ static __devinit int palmas_probe(struct platform_device *pdev)
pmic->desc[id].type = REGULATOR_VOLTAGE;
pmic->desc[id].owner = THIS_MODULE;
+ pmic->desc[id].vsel_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
+ palmas_regs_info[id].vsel_addr);
+ pmic->desc[id].vsel_mask = PALMAS_LDO1_VOLTAGE_VSEL_MASK;
pmic->desc[id].enable_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
palmas_regs_info[id].ctrl_addr);
pmic->desc[id].enable_mask = PALMAS_LDO1_CTRL_MODE_ACTIVE;
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index 4669dc9ac74a..926f9c8f2fac 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -24,7 +24,7 @@
#include <linux/mfd/samsung/s2mps11.h>
struct s2mps11_info {
- struct regulator_dev **rdev;
+ struct regulator_dev *rdev[S2MPS11_REGULATOR_MAX];
int ramp_delay2;
int ramp_delay34;
@@ -236,9 +236,8 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
struct regulator_config config = { };
- struct regulator_dev **rdev;
struct s2mps11_info *s2mps11;
- int i, ret, size;
+ int i, ret;
unsigned char ramp_enable, ramp_reg = 0;
if (!pdata) {
@@ -251,13 +250,6 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
if (!s2mps11)
return -ENOMEM;
- size = sizeof(struct regulator_dev *) * S2MPS11_REGULATOR_MAX;
- s2mps11->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
- if (!s2mps11->rdev) {
- return -ENOMEM;
- }
-
- rdev = s2mps11->rdev;
platform_set_drvdata(pdev, s2mps11);
s2mps11->ramp_delay2 = pdata->buck2_ramp_delay;
@@ -297,12 +289,12 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
config.init_data = pdata->regulators[i].initdata;
config.driver_data = s2mps11;
- rdev[i] = regulator_register(&regulators[i], &config);
- if (IS_ERR(rdev[i])) {
- ret = PTR_ERR(rdev[i]);
+ s2mps11->rdev[i] = regulator_register(&regulators[i], &config);
+ if (IS_ERR(s2mps11->rdev[i])) {
+ ret = PTR_ERR(s2mps11->rdev[i]);
dev_err(&pdev->dev, "regulator init failed for %d\n",
i);
- rdev[i] = NULL;
+ s2mps11->rdev[i] = NULL;
goto err;
}
}
@@ -310,8 +302,7 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
return 0;
err:
for (i = 0; i < S2MPS11_REGULATOR_MAX; i++)
- if (rdev[i])
- regulator_unregister(rdev[i]);
+ regulator_unregister(s2mps11->rdev[i]);
return ret;
}
@@ -319,12 +310,10 @@ err:
static int __devexit s2mps11_pmic_remove(struct platform_device *pdev)
{
struct s2mps11_info *s2mps11 = platform_get_drvdata(pdev);
- struct regulator_dev **rdev = s2mps11->rdev;
int i;
for (i = 0; i < S2MPS11_REGULATOR_MAX; i++)
- if (rdev[i])
- regulator_unregister(rdev[i]);
+ regulator_unregister(s2mps11->rdev[i]);
return 0;
}
diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c
index 947ece933d90..058d2f2675e9 100644
--- a/drivers/regulator/tps6524x-regulator.c
+++ b/drivers/regulator/tps6524x-regulator.c
@@ -502,15 +502,13 @@ static int set_current_limit(struct regulator_dev *rdev, int min_uA,
if (info->n_ilimsels == 1)
return -EINVAL;
- for (i = 0; i < info->n_ilimsels; i++)
+ for (i = info->n_ilimsels - 1; i >= 0; i--) {
if (min_uA <= info->ilimsels[i] &&
max_uA >= info->ilimsels[i])
- break;
-
- if (i >= info->n_ilimsels)
- return -EINVAL;
+ return write_field(hw, &info->ilimsel, i);
+ }
- return write_field(hw, &info->ilimsel, i);
+ return -EINVAL;
}
static int get_current_limit(struct regulator_dev *rdev)
diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c
index 82125269b667..ce1e7cb8d513 100644
--- a/drivers/regulator/tps6586x-regulator.c
+++ b/drivers/regulator/tps6586x-regulator.c
@@ -57,9 +57,6 @@
struct tps6586x_regulator {
struct regulator_desc desc;
- int volt_reg;
- int volt_shift;
- int volt_nbits;
int enable_bit[2];
int enable_reg[2];
@@ -81,10 +78,10 @@ static int tps6586x_set_voltage_sel(struct regulator_dev *rdev,
int ret, val, rid = rdev_get_id(rdev);
uint8_t mask;
- val = selector << ri->volt_shift;
- mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift;
+ val = selector << (ffs(rdev->desc->vsel_mask) - 1);
+ mask = rdev->desc->vsel_mask;
- ret = tps6586x_update(parent, ri->volt_reg, val, mask);
+ ret = tps6586x_update(parent, rdev->desc->vsel_reg, val, mask);
if (ret)
return ret;
@@ -100,66 +97,14 @@ static int tps6586x_set_voltage_sel(struct regulator_dev *rdev,
return ret;
}
-static int tps6586x_get_voltage_sel(struct regulator_dev *rdev)
-{
- struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
- struct device *parent = to_tps6586x_dev(rdev);
- uint8_t val, mask;
- int ret;
-
- ret = tps6586x_read(parent, ri->volt_reg, &val);
- if (ret)
- return ret;
-
- mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift;
- val = (val & mask) >> ri->volt_shift;
-
- if (val >= ri->desc.n_voltages)
- BUG();
-
- return val;
-}
-
-static int tps6586x_regulator_enable(struct regulator_dev *rdev)
-{
- struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
- struct device *parent = to_tps6586x_dev(rdev);
-
- return tps6586x_set_bits(parent, ri->enable_reg[0],
- 1 << ri->enable_bit[0]);
-}
-
-static int tps6586x_regulator_disable(struct regulator_dev *rdev)
-{
- struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
- struct device *parent = to_tps6586x_dev(rdev);
-
- return tps6586x_clr_bits(parent, ri->enable_reg[0],
- 1 << ri->enable_bit[0]);
-}
-
-static int tps6586x_regulator_is_enabled(struct regulator_dev *rdev)
-{
- struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
- struct device *parent = to_tps6586x_dev(rdev);
- uint8_t reg_val;
- int ret;
-
- ret = tps6586x_read(parent, ri->enable_reg[0], &reg_val);
- if (ret)
- return ret;
-
- return !!(reg_val & (1 << ri->enable_bit[0]));
-}
-
static struct regulator_ops tps6586x_regulator_ops = {
.list_voltage = regulator_list_voltage_table,
- .get_voltage_sel = tps6586x_get_voltage_sel,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_sel = tps6586x_set_voltage_sel,
- .is_enabled = tps6586x_regulator_is_enabled,
- .enable = tps6586x_regulator_enable,
- .disable = tps6586x_regulator_disable,
+ .is_enabled = regulator_is_enabled_regmap,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
};
static struct regulator_ops tps6586x_sys_regulator_ops = {
@@ -205,10 +150,11 @@ static const unsigned int tps6586x_dvm_voltages[] = {
.n_voltages = ARRAY_SIZE(tps6586x_##vdata##_voltages), \
.volt_table = tps6586x_##vdata##_voltages, \
.owner = THIS_MODULE, \
+ .enable_reg = TPS6586X_SUPPLY##ereg0, \
+ .enable_mask = 1 << (ebit0), \
+ .vsel_reg = TPS6586X_##vreg, \
+ .vsel_mask = ((1 << (nbits)) - 1) << (shift), \
}, \
- .volt_reg = TPS6586X_##vreg, \
- .volt_shift = (shift), \
- .volt_nbits = (nbits), \
.enable_reg[0] = TPS6586X_SUPPLY##ereg0, \
.enable_bit[0] = (ebit0), \
.enable_reg[1] = TPS6586X_SUPPLY##ereg1, \
diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c
index 77a71a5c17c3..7eb986a40746 100644
--- a/drivers/regulator/twl-regulator.c
+++ b/drivers/regulator/twl-regulator.c
@@ -10,6 +10,8 @@
*/
#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/platform_device.h>
@@ -624,18 +626,9 @@ static int twlfixed_list_voltage(struct regulator_dev *rdev, unsigned index)
return info->min_mV * 1000;
}
-static int twlfixed_get_voltage(struct regulator_dev *rdev)
-{
- struct twlreg_info *info = rdev_get_drvdata(rdev);
-
- return info->min_mV * 1000;
-}
-
static struct regulator_ops twl4030fixed_ops = {
.list_voltage = twlfixed_list_voltage,
- .get_voltage = twlfixed_get_voltage,
-
.enable = twl4030reg_enable,
.disable = twl4030reg_disable,
.is_enabled = twl4030reg_is_enabled,
@@ -648,8 +641,6 @@ static struct regulator_ops twl4030fixed_ops = {
static struct regulator_ops twl6030fixed_ops = {
.list_voltage = twlfixed_list_voltage,
- .get_voltage = twlfixed_get_voltage,
-
.enable = twl6030reg_enable,
.disable = twl6030reg_disable,
.is_enabled = twl6030reg_is_enabled,
@@ -659,13 +650,6 @@ static struct regulator_ops twl6030fixed_ops = {
.get_status = twl6030reg_get_status,
};
-static struct regulator_ops twl6030_fixed_resource = {
- .enable = twl6030reg_enable,
- .disable = twl6030reg_disable,
- .is_enabled = twl6030reg_is_enabled,
- .get_status = twl6030reg_get_status,
-};
-
/*
* SMPS status and control
*/
@@ -757,37 +741,32 @@ static int twl6030smps_list_voltage(struct regulator_dev *rdev, unsigned index)
return voltage;
}
-static int
-twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
- unsigned int *selector)
+static int twl6030smps_map_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV)
{
- struct twlreg_info *info = rdev_get_drvdata(rdev);
- int vsel = 0;
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
+ int vsel = 0;
switch (info->flags) {
case 0:
if (min_uV == 0)
vsel = 0;
else if ((min_uV >= 600000) && (min_uV <= 1300000)) {
- int calc_uV;
vsel = DIV_ROUND_UP(min_uV - 600000, 12500);
vsel++;
- calc_uV = twl6030smps_list_voltage(rdev, vsel);
- if (calc_uV > max_uV)
- return -EINVAL;
}
/* Values 1..57 for vsel are linear and can be calculated
* values 58..62 are non linear.
*/
- else if ((min_uV > 1900000) && (max_uV >= 2100000))
+ else if ((min_uV > 1900000) && (min_uV <= 2100000))
vsel = 62;
- else if ((min_uV > 1800000) && (max_uV >= 1900000))
+ else if ((min_uV > 1800000) && (min_uV <= 1900000))
vsel = 61;
- else if ((min_uV > 1500000) && (max_uV >= 1800000))
+ else if ((min_uV > 1500000) && (min_uV <= 1800000))
vsel = 60;
- else if ((min_uV > 1350000) && (max_uV >= 1500000))
+ else if ((min_uV > 1350000) && (min_uV <= 1500000))
vsel = 59;
- else if ((min_uV > 1300000) && (max_uV >= 1350000))
+ else if ((min_uV > 1300000) && (min_uV <= 1350000))
vsel = 58;
else
return -EINVAL;
@@ -796,25 +775,21 @@ twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
if (min_uV == 0)
vsel = 0;
else if ((min_uV >= 700000) && (min_uV <= 1420000)) {
- int calc_uV;
vsel = DIV_ROUND_UP(min_uV - 700000, 12500);
vsel++;
- calc_uV = twl6030smps_list_voltage(rdev, vsel);
- if (calc_uV > max_uV)
- return -EINVAL;
}
/* Values 1..57 for vsel are linear and can be calculated
* values 58..62 are non linear.
*/
- else if ((min_uV > 1900000) && (max_uV >= 2100000))
+ else if ((min_uV > 1900000) && (min_uV <= 2100000))
vsel = 62;
- else if ((min_uV > 1800000) && (max_uV >= 1900000))
+ else if ((min_uV > 1800000) && (min_uV <= 1900000))
vsel = 61;
- else if ((min_uV > 1350000) && (max_uV >= 1800000))
+ else if ((min_uV > 1350000) && (min_uV <= 1800000))
vsel = 60;
- else if ((min_uV > 1350000) && (max_uV >= 1500000))
+ else if ((min_uV > 1350000) && (min_uV <= 1500000))
vsel = 59;
- else if ((min_uV > 1300000) && (max_uV >= 1350000))
+ else if ((min_uV > 1300000) && (min_uV <= 1350000))
vsel = 58;
else
return -EINVAL;
@@ -830,17 +805,23 @@ twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
case SMPS_OFFSET_EN|SMPS_EXTENDED_EN:
if (min_uV == 0) {
vsel = 0;
- } else if ((min_uV >= 2161000) && (max_uV <= 4321000)) {
+ } else if ((min_uV >= 2161000) && (min_uV <= 4321000)) {
vsel = DIV_ROUND_UP(min_uV - 2161000, 38600);
vsel++;
}
break;
}
- *selector = vsel;
+ return vsel;
+}
+
+static int twl6030smps_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct twlreg_info *info = rdev_get_drvdata(rdev);
return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS,
- vsel);
+ selector);
}
static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev)
@@ -852,8 +833,9 @@ static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev)
static struct regulator_ops twlsmps_ops = {
.list_voltage = twl6030smps_list_voltage,
+ .map_voltage = twl6030smps_map_voltage,
- .set_voltage = twl6030smps_set_voltage,
+ .set_voltage_sel = twl6030smps_set_voltage_sel,
.get_voltage_sel = twl6030smps_get_voltage_sel,
.enable = twl6030reg_enable,
@@ -876,7 +858,7 @@ static struct regulator_ops twlsmps_ops = {
0x0, TWL6030, twl6030fixed_ops)
#define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) \
-static struct twlreg_info TWL4030_INFO_##label = { \
+static const struct twlreg_info TWL4030_INFO_##label = { \
.base = offset, \
.id = num, \
.table_len = ARRAY_SIZE(label##_VSEL_table), \
@@ -894,7 +876,7 @@ static struct twlreg_info TWL4030_INFO_##label = { \
}
#define TWL4030_ADJUSTABLE_SMPS(label, offset, num, turnon_delay, remap_conf) \
-static struct twlreg_info TWL4030_INFO_##label = { \
+static const struct twlreg_info TWL4030_INFO_##label = { \
.base = offset, \
.id = num, \
.remap = remap_conf, \
@@ -909,7 +891,7 @@ static struct twlreg_info TWL4030_INFO_##label = { \
}
#define TWL6030_ADJUSTABLE_SMPS(label) \
-static struct twlreg_info TWL6030_INFO_##label = { \
+static const struct twlreg_info TWL6030_INFO_##label = { \
.desc = { \
.name = #label, \
.id = TWL6030_REG_##label, \
@@ -920,7 +902,7 @@ static struct twlreg_info TWL6030_INFO_##label = { \
}
#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \
-static struct twlreg_info TWL6030_INFO_##label = { \
+static const struct twlreg_info TWL6030_INFO_##label = { \
.base = offset, \
.min_mV = min_mVolts, \
.max_mV = max_mVolts, \
@@ -935,7 +917,7 @@ static struct twlreg_info TWL6030_INFO_##label = { \
}
#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \
-static struct twlreg_info TWL6025_INFO_##label = { \
+static const struct twlreg_info TWL6025_INFO_##label = { \
.base = offset, \
.min_mV = min_mVolts, \
.max_mV = max_mVolts, \
@@ -951,7 +933,7 @@ static struct twlreg_info TWL6025_INFO_##label = { \
#define TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, remap_conf, \
family, operations) \
-static struct twlreg_info TWLFIXED_INFO_##label = { \
+static const struct twlreg_info TWLFIXED_INFO_##label = { \
.base = offset, \
.id = num, \
.min_mV = mVolts, \
@@ -981,7 +963,7 @@ static struct twlreg_info TWLRES_INFO_##label = { \
}
#define TWL6025_ADJUSTABLE_SMPS(label, offset) \
-static struct twlreg_info TWLSMPS_INFO_##label = { \
+static const struct twlreg_info TWLSMPS_INFO_##label = { \
.base = offset, \
.min_mV = 600, \
.max_mV = 2100, \
@@ -1138,6 +1120,7 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
{
int i, id;
struct twlreg_info *info;
+ const struct twlreg_info *template;
struct regulator_init_data *initdata;
struct regulation_constraints *c;
struct regulator_dev *rdev;
@@ -1147,17 +1130,17 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
match = of_match_device(twl_of_match, &pdev->dev);
if (match) {
- info = match->data;
- id = info->desc.id;
+ template = match->data;
+ id = template->desc.id;
initdata = of_get_regulator_init_data(&pdev->dev,
pdev->dev.of_node);
drvdata = NULL;
} else {
id = pdev->id;
initdata = pdev->dev.platform_data;
- for (i = 0, info = NULL; i < ARRAY_SIZE(twl_of_match); i++) {
- info = twl_of_match[i].data;
- if (info && info->desc.id == id)
+ for (i = 0, template = NULL; i < ARRAY_SIZE(twl_of_match); i++) {
+ template = twl_of_match[i].data;
+ if (template && template->desc.id == id)
break;
}
if (i == ARRAY_SIZE(twl_of_match))
@@ -1168,12 +1151,16 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
return -EINVAL;
}
- if (!info)
+ if (!template)
return -ENODEV;
if (!initdata)
return -EINVAL;
+ info = kmemdup(template, sizeof (*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
if (drvdata) {
/* copy the driver data into regulator data */
info->features = drvdata->features;
@@ -1234,6 +1221,7 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
if (IS_ERR(rdev)) {
dev_err(&pdev->dev, "can't register %s, %ld\n",
info->desc.name, PTR_ERR(rdev));
+ kfree(info);
return PTR_ERR(rdev);
}
platform_set_drvdata(pdev, rdev);
@@ -1255,7 +1243,11 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
static int __devexit twlreg_remove(struct platform_device *pdev)
{
- regulator_unregister(platform_get_drvdata(pdev));
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct twlreg_info *info = rdev->reg_data;
+
+ regulator_unregister(rdev);
+ kfree(info);
return 0;
}
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
index 7413885be01b..90cbcc683704 100644
--- a/drivers/regulator/wm831x-dcdc.c
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -339,16 +339,15 @@ static int wm831x_buckv_set_current_limit(struct regulator_dev *rdev,
u16 reg = dcdc->base + WM831X_DCDC_CONTROL_2;
int i;
- for (i = 0; i < ARRAY_SIZE(wm831x_dcdc_ilim); i++) {
+ for (i = ARRAY_SIZE(wm831x_dcdc_ilim) - 1; i >= 0; i--) {
if ((min_uA <= wm831x_dcdc_ilim[i]) &&
(wm831x_dcdc_ilim[i] <= max_uA))
- break;
+ return wm831x_set_bits(wm831x, reg,
+ WM831X_DC1_HC_THR_MASK,
+ i << WM831X_DC1_HC_THR_SHIFT);
}
- if (i == ARRAY_SIZE(wm831x_dcdc_ilim))
- return -EINVAL;
- return wm831x_set_bits(wm831x, reg, WM831X_DC1_HC_THR_MASK,
- i << WM831X_DC1_HC_THR_SHIFT);
+ return -EINVAL;
}
static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev)
diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c
index 5cb70ca1e98d..9af512672be1 100644
--- a/drivers/regulator/wm831x-ldo.c
+++ b/drivers/regulator/wm831x-ldo.c
@@ -205,6 +205,8 @@ static int wm831x_gp_ldo_get_status(struct regulator_dev *rdev)
/* Is it reporting under voltage? */
ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS);
+ if (ret < 0)
+ return ret;
if (ret & mask)
return REGULATOR_STATUS_ERROR;
@@ -237,6 +239,8 @@ static struct regulator_ops wm831x_gp_ldo_ops = {
.set_mode = wm831x_gp_ldo_set_mode,
.get_status = wm831x_gp_ldo_get_status,
.get_optimum_mode = wm831x_gp_ldo_get_optimum_mode,
+ .get_bypass = regulator_get_bypass_regmap,
+ .set_bypass = regulator_set_bypass_regmap,
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
@@ -293,6 +297,8 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
ldo->desc.vsel_mask = WM831X_LDO1_ON_VSEL_MASK;
ldo->desc.enable_reg = WM831X_LDO_ENABLE;
ldo->desc.enable_mask = 1 << id;
+ ldo->desc.bypass_reg = ldo->base;
+ ldo->desc.bypass_mask = WM831X_LDO1_SWI;
config.dev = pdev->dev.parent;
if (pdata)
@@ -469,6 +475,8 @@ static int wm831x_aldo_get_status(struct regulator_dev *rdev)
/* Is it reporting under voltage? */
ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS);
+ if (ret < 0)
+ return ret;
if (ret & mask)
return REGULATOR_STATUS_ERROR;
@@ -488,6 +496,8 @@ static struct regulator_ops wm831x_aldo_ops = {
.get_mode = wm831x_aldo_get_mode,
.set_mode = wm831x_aldo_set_mode,
.get_status = wm831x_aldo_get_status,
+ .set_bypass = regulator_set_bypass_regmap,
+ .get_bypass = regulator_get_bypass_regmap,
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
@@ -544,6 +554,8 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
ldo->desc.vsel_mask = WM831X_LDO7_ON_VSEL_MASK;
ldo->desc.enable_reg = WM831X_LDO_ENABLE;
ldo->desc.enable_mask = 1 << id;
+ ldo->desc.bypass_reg = ldo->base;
+ ldo->desc.bypass_mask = WM831X_LDO7_SWI;
config.dev = pdev->dev.parent;
if (pdata)
diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c
index 9035dd053611..27c746ef0636 100644
--- a/drivers/regulator/wm8400-regulator.c
+++ b/drivers/regulator/wm8400-regulator.c
@@ -120,13 +120,8 @@ static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode)
case REGULATOR_MODE_IDLE:
/* Datasheet: standby */
- ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
- WM8400_DC1_ACTIVE, 0);
- if (ret != 0)
- return ret;
return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
- WM8400_DC1_SLEEP, 0);
-
+ WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 0);
default:
return -EINVAL;
}
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 590cfafc7c17..1859f71372e2 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -1008,8 +1008,8 @@ static int rpmsg_probe(struct virtio_device *vdev)
return 0;
free_coherent:
- dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, bufs_va,
- vrp->bufs_dma);
+ dma_free_coherent(vdev->dev.parent->parent, RPMSG_TOTAL_BUF_SPACE,
+ bufs_va, vrp->bufs_dma);
vqs_del:
vdev->config->del_vqs(vrp->vdev);
free_vrp:
@@ -1043,7 +1043,7 @@ static void __devexit rpmsg_remove(struct virtio_device *vdev)
vdev->config->del_vqs(vrp->vdev);
- dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE,
+ dma_free_coherent(vdev->dev.parent->parent, RPMSG_TOTAL_BUF_SPACE,
vrp->rbufs, vrp->bufs_dma);
kfree(vrp);
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
index bf3c2f669c3c..2e5970fe9eeb 100644
--- a/drivers/rtc/rtc-ab8500.c
+++ b/drivers/rtc/rtc-ab8500.c
@@ -462,16 +462,10 @@ static int __devexit ab8500_rtc_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id ab8500_rtc_match[] = {
- { .compatible = "stericsson,ab8500-rtc", },
- {}
-};
-
static struct platform_driver ab8500_rtc_driver = {
.driver = {
.name = "ab8500-rtc",
.owner = THIS_MODULE,
- .of_match_table = ab8500_rtc_match,
},
.probe = ab8500_rtc_probe,
.remove = __devexit_p(ab8500_rtc_remove),
diff --git a/drivers/rtc/rtc-twl.c b/drivers/rtc/rtc-twl.c
index c5d06fe83bba..9277d945bf48 100644
--- a/drivers/rtc/rtc-twl.c
+++ b/drivers/rtc/rtc-twl.c
@@ -495,6 +495,11 @@ static int __devinit twl_rtc_probe(struct platform_device *pdev)
if (ret < 0)
goto out1;
+ /* ensure interrupts are disabled, bootloaders can be strange */
+ ret = twl_rtc_write_u8(0, REG_RTC_INTERRUPTS_REG);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "unable to disable interrupt\n");
+
/* init cached IRQ enable bits */
ret = twl_rtc_read_u8(&rtc_irq_bits, REG_RTC_INTERRUPTS_REG);
if (ret < 0)
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
index 9e94fb147c26..07bf19364a74 100644
--- a/drivers/rtc/rtc-vt8500.c
+++ b/drivers/rtc/rtc-vt8500.c
@@ -23,6 +23,7 @@
#include <linux/bcd.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/of.h>
/*
* Register definitions
@@ -302,12 +303,18 @@ static int __devexit vt8500_rtc_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id wmt_dt_ids[] = {
+ { .compatible = "via,vt8500-rtc", },
+ {}
+};
+
static struct platform_driver vt8500_rtc_driver = {
.probe = vt8500_rtc_probe,
.remove = __devexit_p(vt8500_rtc_remove),
.driver = {
.name = "vt8500-rtc",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(wmt_dt_ids),
},
};
@@ -315,5 +322,5 @@ module_platform_driver(vt8500_rtc_driver);
MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:vt8500-rtc");
diff --git a/drivers/s390/block/Kconfig b/drivers/s390/block/Kconfig
index 8e477bb1f3f6..4a3b62326183 100644
--- a/drivers/s390/block/Kconfig
+++ b/drivers/s390/block/Kconfig
@@ -70,3 +70,21 @@ config DASD_EER
This driver provides a character device interface to the
DASD extended error reporting. This is only needed if you want to
use applications written for the EER facility.
+
+config SCM_BLOCK
+ def_tristate m
+ prompt "Support for Storage Class Memory"
+ depends on S390 && BLOCK && EADM_SCH && SCM_BUS
+ help
+ Block device driver for Storage Class Memory (SCM). This driver
+ provides a block device interface for each available SCM increment.
+
+ To compile this driver as a module, choose M here: the
+ module will be called scm_block.
+
+config SCM_BLOCK_CLUSTER_WRITE
+ def_bool y
+ prompt "SCM force cluster writes"
+ depends on SCM_BLOCK
+ help
+ Force writes to Storage Class Memory (SCM) to be in done in clusters.
diff --git a/drivers/s390/block/Makefile b/drivers/s390/block/Makefile
index 0a89e080b389..c2f4e673e031 100644
--- a/drivers/s390/block/Makefile
+++ b/drivers/s390/block/Makefile
@@ -17,3 +17,9 @@ obj-$(CONFIG_DASD_ECKD) += dasd_eckd_mod.o
obj-$(CONFIG_DASD_FBA) += dasd_fba_mod.o
obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o
obj-$(CONFIG_DCSSBLK) += dcssblk.o
+
+scm_block-objs := scm_drv.o scm_blk.o
+ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
+scm_block-objs += scm_blk_cluster.o
+endif
+obj-$(CONFIG_SCM_BLOCK) += scm_block.o
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 15370a2c5ff0..0595c763dafd 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -534,11 +534,11 @@ static void dasd_change_state(struct dasd_device *device)
if (rc)
device->target = device->state;
- if (device->state == device->target)
- wake_up(&dasd_init_waitq);
-
/* let user-space know that the device status changed */
kobject_uevent(&device->cdev->dev.kobj, KOBJ_CHANGE);
+
+ if (device->state == device->target)
+ wake_up(&dasd_init_waitq);
}
/*
@@ -2157,6 +2157,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
(!dasd_eer_enabled(device))) {
cqr->status = DASD_CQR_FAILED;
+ cqr->intrc = -EAGAIN;
continue;
}
/* Don't try to start requests if device is stopped */
@@ -3270,6 +3271,16 @@ void dasd_generic_path_event(struct ccw_device *cdev, int *path_event)
dasd_schedule_device_bh(device);
}
if (path_event[chp] & PE_PATHGROUP_ESTABLISHED) {
+ if (!(device->path_data.opm & eventlpm) &&
+ !(device->path_data.tbvpm & eventlpm)) {
+ /*
+ * we can not establish a pathgroup on an
+ * unavailable path, so trigger a path
+ * verification first
+ */
+ device->path_data.tbvpm |= eventlpm;
+ dasd_schedule_device_bh(device);
+ }
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"Pathgroup re-established\n");
if (device->discipline->kick_validate)
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
index 157defe5e069..6b556995bb33 100644
--- a/drivers/s390/block/dasd_alias.c
+++ b/drivers/s390/block/dasd_alias.c
@@ -384,6 +384,29 @@ static void _remove_device_from_lcu(struct alias_lcu *lcu,
group->next = NULL;
};
+static int
+suborder_not_supported(struct dasd_ccw_req *cqr)
+{
+ char *sense;
+ char reason;
+ char msg_format;
+ char msg_no;
+
+ sense = dasd_get_sense(&cqr->irb);
+ if (!sense)
+ return 0;
+
+ reason = sense[0];
+ msg_format = (sense[7] & 0xF0);
+ msg_no = (sense[7] & 0x0F);
+
+ /* command reject, Format 0 MSG 4 - invalid parameter */
+ if ((reason == 0x80) && (msg_format == 0x00) && (msg_no == 0x04))
+ return 1;
+
+ return 0;
+}
+
static int read_unit_address_configuration(struct dasd_device *device,
struct alias_lcu *lcu)
{
@@ -435,6 +458,8 @@ static int read_unit_address_configuration(struct dasd_device *device,
do {
rc = dasd_sleep_on(cqr);
+ if (rc && suborder_not_supported(cqr))
+ return -EOPNOTSUPP;
} while (rc && (cqr->retries > 0));
if (rc) {
spin_lock_irqsave(&lcu->lock, flags);
@@ -521,7 +546,7 @@ static void lcu_update_work(struct work_struct *work)
* processing the data
*/
spin_lock_irqsave(&lcu->lock, flags);
- if (rc || (lcu->flags & NEED_UAC_UPDATE)) {
+ if ((rc && (rc != -EOPNOTSUPP)) || (lcu->flags & NEED_UAC_UPDATE)) {
DBF_DEV_EVENT(DBF_WARNING, device, "could not update"
" alias data in lcu (rc = %d), retry later", rc);
schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ);
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 2fb2b9ea97ec..108332b44d98 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -20,6 +20,7 @@
#include <linux/compat.h>
#include <linux/init.h>
+#include <asm/css_chars.h>
#include <asm/debug.h>
#include <asm/idals.h>
#include <asm/ebcdic.h>
@@ -31,8 +32,6 @@
#include "dasd_int.h"
#include "dasd_eckd.h"
-#include "../cio/chsc.h"
-
#ifdef PRINTK_HEADER
#undef PRINTK_HEADER
@@ -140,6 +139,10 @@ dasd_eckd_set_online(struct ccw_device *cdev)
static const int sizes_trk0[] = { 28, 148, 84 };
#define LABEL_SIZE 140
+/* head and record addresses of count_area read in analysis ccw */
+static const int count_area_head[] = { 0, 0, 0, 0, 2 };
+static const int count_area_rec[] = { 1, 2, 3, 4, 1 };
+
static inline unsigned int
round_up_multiple(unsigned int no, unsigned int mult)
{
@@ -212,7 +215,7 @@ check_XRC (struct ccw1 *de_ccw,
rc = get_sync_clock(&data->ep_sys_time);
/* Ignore return code if sync clock is switched off. */
- if (rc == -ENOSYS || rc == -EACCES)
+ if (rc == -EOPNOTSUPP || rc == -EACCES)
rc = 0;
de_ccw->count = sizeof(struct DE_eckd_data);
@@ -323,7 +326,7 @@ static int check_XRC_on_prefix(struct PFX_eckd_data *pfxdata,
rc = get_sync_clock(&pfxdata->define_extent.ep_sys_time);
/* Ignore return code if sync clock is switched off. */
- if (rc == -ENOSYS || rc == -EACCES)
+ if (rc == -EOPNOTSUPP || rc == -EACCES)
rc = 0;
return rc;
}
@@ -1507,7 +1510,8 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
* call might change behaviour of DASD devices.
*/
static int
-dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
+dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav,
+ unsigned long flags)
{
struct dasd_ccw_req *cqr;
int rc;
@@ -1516,10 +1520,19 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
if (IS_ERR(cqr))
return PTR_ERR(cqr);
+ /*
+ * set flags e.g. turn on failfast, to prevent blocking
+ * the calling function should handle failed requests
+ */
+ cqr->flags |= flags;
+
rc = dasd_sleep_on(cqr);
if (!rc)
/* trigger CIO to reprobe devices */
css_schedule_reprobe();
+ else if (cqr->intrc == -EAGAIN)
+ rc = -EAGAIN;
+
dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -1527,7 +1540,8 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
/*
* Valide storage server of current device.
*/
-static void dasd_eckd_validate_server(struct dasd_device *device)
+static int dasd_eckd_validate_server(struct dasd_device *device,
+ unsigned long flags)
{
int rc;
struct dasd_eckd_private *private;
@@ -1536,17 +1550,18 @@ static void dasd_eckd_validate_server(struct dasd_device *device)
private = (struct dasd_eckd_private *) device->private;
if (private->uid.type == UA_BASE_PAV_ALIAS ||
private->uid.type == UA_HYPER_PAV_ALIAS)
- return;
+ return 0;
if (dasd_nopav || MACHINE_IS_VM)
enable_pav = 0;
else
enable_pav = 1;
- rc = dasd_eckd_psf_ssc(device, enable_pav);
+ rc = dasd_eckd_psf_ssc(device, enable_pav, flags);
/* may be requested feature is not available on server,
* therefore just report error and go ahead */
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x "
"returned rc=%d", private->uid.ssid, rc);
+ return rc;
}
/*
@@ -1556,7 +1571,13 @@ static void dasd_eckd_do_validate_server(struct work_struct *work)
{
struct dasd_device *device = container_of(work, struct dasd_device,
kick_validate);
- dasd_eckd_validate_server(device);
+ if (dasd_eckd_validate_server(device, DASD_CQR_FLAGS_FAILFAST)
+ == -EAGAIN) {
+ /* schedule worker again if failed */
+ schedule_work(&device->kick_validate);
+ return;
+ }
+
dasd_put_device(device);
}
@@ -1685,7 +1706,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
if (rc)
goto out_err2;
- dasd_eckd_validate_server(device);
+ dasd_eckd_validate_server(device, 0);
/* device may report different configuration data after LCU setup */
rc = dasd_eckd_read_conf(device);
@@ -1922,7 +1943,10 @@ static int dasd_eckd_end_analysis(struct dasd_block *block)
count_area = NULL;
for (i = 0; i < 3; i++) {
if (private->count_area[i].kl != 4 ||
- private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4) {
+ private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4 ||
+ private->count_area[i].cyl != 0 ||
+ private->count_area[i].head != count_area_head[i] ||
+ private->count_area[i].record != count_area_rec[i]) {
private->uses_cdl = 0;
break;
}
@@ -1934,7 +1958,10 @@ static int dasd_eckd_end_analysis(struct dasd_block *block)
for (i = 0; i < 5; i++) {
if ((private->count_area[i].kl != 0) ||
(private->count_area[i].dl !=
- private->count_area[0].dl))
+ private->count_area[0].dl) ||
+ private->count_area[i].cyl != 0 ||
+ private->count_area[i].head != count_area_head[i] ||
+ private->count_area[i].record != count_area_rec[i])
break;
}
if (i == 5)
@@ -4153,7 +4180,7 @@ static int dasd_eckd_restore_device(struct dasd_device *device)
rc = dasd_alias_make_device_known_to_lcu(device);
if (rc)
return rc;
- dasd_eckd_validate_server(device);
+ dasd_eckd_validate_server(device, DASD_CQR_FLAGS_FAILFAST);
/* RE-Read Configuration Data */
rc = dasd_eckd_read_conf(device);
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index 654c6921a6d4..8252f37d04ed 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -292,12 +292,12 @@ out:
#else
static int dasd_ioctl_reset_profile(struct dasd_block *block)
{
- return -ENOSYS;
+ return -ENOTTY;
}
static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
{
- return -ENOSYS;
+ return -ENOTTY;
}
#endif
diff --git a/drivers/s390/block/scm_blk.c b/drivers/s390/block/scm_blk.c
new file mode 100644
index 000000000000..9978ad4433cb
--- /dev/null
+++ b/drivers/s390/block/scm_blk.c
@@ -0,0 +1,445 @@
+/*
+ * Block driver for s390 storage class memory.
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#define KMSG_COMPONENT "scm_block"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <asm/eadm.h>
+#include "scm_blk.h"
+
+debug_info_t *scm_debug;
+static int scm_major;
+static DEFINE_SPINLOCK(list_lock);
+static LIST_HEAD(inactive_requests);
+static unsigned int nr_requests = 64;
+static atomic_t nr_devices = ATOMIC_INIT(0);
+module_param(nr_requests, uint, S_IRUGO);
+MODULE_PARM_DESC(nr_requests, "Number of parallel requests.");
+
+MODULE_DESCRIPTION("Block driver for s390 storage class memory.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("scm:scmdev*");
+
+static void __scm_free_rq(struct scm_request *scmrq)
+{
+ struct aob_rq_header *aobrq = to_aobrq(scmrq);
+
+ free_page((unsigned long) scmrq->aob);
+ free_page((unsigned long) scmrq->aidaw);
+ __scm_free_rq_cluster(scmrq);
+ kfree(aobrq);
+}
+
+static void scm_free_rqs(void)
+{
+ struct list_head *iter, *safe;
+ struct scm_request *scmrq;
+
+ spin_lock_irq(&list_lock);
+ list_for_each_safe(iter, safe, &inactive_requests) {
+ scmrq = list_entry(iter, struct scm_request, list);
+ list_del(&scmrq->list);
+ __scm_free_rq(scmrq);
+ }
+ spin_unlock_irq(&list_lock);
+}
+
+static int __scm_alloc_rq(void)
+{
+ struct aob_rq_header *aobrq;
+ struct scm_request *scmrq;
+
+ aobrq = kzalloc(sizeof(*aobrq) + sizeof(*scmrq), GFP_KERNEL);
+ if (!aobrq)
+ return -ENOMEM;
+
+ scmrq = (void *) aobrq->data;
+ scmrq->aidaw = (void *) get_zeroed_page(GFP_DMA);
+ scmrq->aob = (void *) get_zeroed_page(GFP_DMA);
+ if (!scmrq->aob || !scmrq->aidaw) {
+ __scm_free_rq(scmrq);
+ return -ENOMEM;
+ }
+
+ if (__scm_alloc_rq_cluster(scmrq)) {
+ __scm_free_rq(scmrq);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&scmrq->list);
+ spin_lock_irq(&list_lock);
+ list_add(&scmrq->list, &inactive_requests);
+ spin_unlock_irq(&list_lock);
+
+ return 0;
+}
+
+static int scm_alloc_rqs(unsigned int nrqs)
+{
+ int ret = 0;
+
+ while (nrqs-- && !ret)
+ ret = __scm_alloc_rq();
+
+ return ret;
+}
+
+static struct scm_request *scm_request_fetch(void)
+{
+ struct scm_request *scmrq = NULL;
+
+ spin_lock(&list_lock);
+ if (list_empty(&inactive_requests))
+ goto out;
+ scmrq = list_first_entry(&inactive_requests, struct scm_request, list);
+ list_del(&scmrq->list);
+out:
+ spin_unlock(&list_lock);
+ return scmrq;
+}
+
+static void scm_request_done(struct scm_request *scmrq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&list_lock, flags);
+ list_add(&scmrq->list, &inactive_requests);
+ spin_unlock_irqrestore(&list_lock, flags);
+}
+
+static int scm_open(struct block_device *blkdev, fmode_t mode)
+{
+ return scm_get_ref();
+}
+
+static int scm_release(struct gendisk *gendisk, fmode_t mode)
+{
+ scm_put_ref();
+ return 0;
+}
+
+static const struct block_device_operations scm_blk_devops = {
+ .owner = THIS_MODULE,
+ .open = scm_open,
+ .release = scm_release,
+};
+
+static void scm_request_prepare(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+ struct scm_device *scmdev = bdev->gendisk->private_data;
+ struct aidaw *aidaw = scmrq->aidaw;
+ struct msb *msb = &scmrq->aob->msb[0];
+ struct req_iterator iter;
+ struct bio_vec *bv;
+
+ msb->bs = MSB_BS_4K;
+ scmrq->aob->request.msb_count = 1;
+ msb->scm_addr = scmdev->address +
+ ((u64) blk_rq_pos(scmrq->request) << 9);
+ msb->oc = (rq_data_dir(scmrq->request) == READ) ?
+ MSB_OC_READ : MSB_OC_WRITE;
+ msb->flags |= MSB_FLAG_IDA;
+ msb->data_addr = (u64) aidaw;
+
+ rq_for_each_segment(bv, scmrq->request, iter) {
+ WARN_ON(bv->bv_offset);
+ msb->blk_count += bv->bv_len >> 12;
+ aidaw->data_addr = (u64) page_address(bv->bv_page);
+ aidaw++;
+ }
+}
+
+static inline void scm_request_init(struct scm_blk_dev *bdev,
+ struct scm_request *scmrq,
+ struct request *req)
+{
+ struct aob_rq_header *aobrq = to_aobrq(scmrq);
+ struct aob *aob = scmrq->aob;
+
+ memset(aob, 0, sizeof(*aob));
+ memset(scmrq->aidaw, 0, PAGE_SIZE);
+ aobrq->scmdev = bdev->scmdev;
+ aob->request.cmd_code = ARQB_CMD_MOVE;
+ aob->request.data = (u64) aobrq;
+ scmrq->request = req;
+ scmrq->bdev = bdev;
+ scmrq->retries = 4;
+ scmrq->error = 0;
+ scm_request_cluster_init(scmrq);
+}
+
+static void scm_ensure_queue_restart(struct scm_blk_dev *bdev)
+{
+ if (atomic_read(&bdev->queued_reqs)) {
+ /* Queue restart is triggered by the next interrupt. */
+ return;
+ }
+ blk_delay_queue(bdev->rq, SCM_QUEUE_DELAY);
+}
+
+void scm_request_requeue(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+
+ scm_release_cluster(scmrq);
+ blk_requeue_request(bdev->rq, scmrq->request);
+ scm_request_done(scmrq);
+ scm_ensure_queue_restart(bdev);
+}
+
+void scm_request_finish(struct scm_request *scmrq)
+{
+ scm_release_cluster(scmrq);
+ blk_end_request_all(scmrq->request, scmrq->error);
+ scm_request_done(scmrq);
+}
+
+static void scm_blk_request(struct request_queue *rq)
+{
+ struct scm_device *scmdev = rq->queuedata;
+ struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev);
+ struct scm_request *scmrq;
+ struct request *req;
+ int ret;
+
+ while ((req = blk_peek_request(rq))) {
+ if (req->cmd_type != REQ_TYPE_FS)
+ continue;
+
+ scmrq = scm_request_fetch();
+ if (!scmrq) {
+ SCM_LOG(5, "no request");
+ scm_ensure_queue_restart(bdev);
+ return;
+ }
+ scm_request_init(bdev, scmrq, req);
+ if (!scm_reserve_cluster(scmrq)) {
+ SCM_LOG(5, "cluster busy");
+ scm_request_done(scmrq);
+ return;
+ }
+ if (scm_need_cluster_request(scmrq)) {
+ blk_start_request(req);
+ scm_initiate_cluster_request(scmrq);
+ return;
+ }
+ scm_request_prepare(scmrq);
+ blk_start_request(req);
+
+ ret = scm_start_aob(scmrq->aob);
+ if (ret) {
+ SCM_LOG(5, "no subchannel");
+ scm_request_requeue(scmrq);
+ return;
+ }
+ atomic_inc(&bdev->queued_reqs);
+ }
+}
+
+static void __scmrq_log_error(struct scm_request *scmrq)
+{
+ struct aob *aob = scmrq->aob;
+
+ if (scmrq->error == -ETIMEDOUT)
+ SCM_LOG(1, "Request timeout");
+ else {
+ SCM_LOG(1, "Request error");
+ SCM_LOG_HEX(1, &aob->response, sizeof(aob->response));
+ }
+ if (scmrq->retries)
+ SCM_LOG(1, "Retry request");
+ else
+ pr_err("An I/O operation to SCM failed with rc=%d\n",
+ scmrq->error);
+}
+
+void scm_blk_irq(struct scm_device *scmdev, void *data, int error)
+{
+ struct scm_request *scmrq = data;
+ struct scm_blk_dev *bdev = scmrq->bdev;
+
+ scmrq->error = error;
+ if (error)
+ __scmrq_log_error(scmrq);
+
+ spin_lock(&bdev->lock);
+ list_add_tail(&scmrq->list, &bdev->finished_requests);
+ spin_unlock(&bdev->lock);
+ tasklet_hi_schedule(&bdev->tasklet);
+}
+
+static void scm_blk_tasklet(struct scm_blk_dev *bdev)
+{
+ struct scm_request *scmrq;
+ unsigned long flags;
+
+ spin_lock_irqsave(&bdev->lock, flags);
+ while (!list_empty(&bdev->finished_requests)) {
+ scmrq = list_first_entry(&bdev->finished_requests,
+ struct scm_request, list);
+ list_del(&scmrq->list);
+ spin_unlock_irqrestore(&bdev->lock, flags);
+
+ if (scmrq->error && scmrq->retries-- > 0) {
+ if (scm_start_aob(scmrq->aob)) {
+ spin_lock_irqsave(&bdev->rq_lock, flags);
+ scm_request_requeue(scmrq);
+ spin_unlock_irqrestore(&bdev->rq_lock, flags);
+ }
+ /* Request restarted or requeued, handle next. */
+ spin_lock_irqsave(&bdev->lock, flags);
+ continue;
+ }
+
+ if (scm_test_cluster_request(scmrq)) {
+ scm_cluster_request_irq(scmrq);
+ spin_lock_irqsave(&bdev->lock, flags);
+ continue;
+ }
+
+ scm_request_finish(scmrq);
+ atomic_dec(&bdev->queued_reqs);
+ spin_lock_irqsave(&bdev->lock, flags);
+ }
+ spin_unlock_irqrestore(&bdev->lock, flags);
+ /* Look out for more requests. */
+ blk_run_queue(bdev->rq);
+}
+
+int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)
+{
+ struct request_queue *rq;
+ int len, ret = -ENOMEM;
+ unsigned int devindex, nr_max_blk;
+
+ devindex = atomic_inc_return(&nr_devices) - 1;
+ /* scma..scmz + scmaa..scmzz */
+ if (devindex > 701) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ bdev->scmdev = scmdev;
+ spin_lock_init(&bdev->rq_lock);
+ spin_lock_init(&bdev->lock);
+ INIT_LIST_HEAD(&bdev->finished_requests);
+ atomic_set(&bdev->queued_reqs, 0);
+ tasklet_init(&bdev->tasklet,
+ (void (*)(unsigned long)) scm_blk_tasklet,
+ (unsigned long) bdev);
+
+ rq = blk_init_queue(scm_blk_request, &bdev->rq_lock);
+ if (!rq)
+ goto out;
+
+ bdev->rq = rq;
+ nr_max_blk = min(scmdev->nr_max_block,
+ (unsigned int) (PAGE_SIZE / sizeof(struct aidaw)));
+
+ blk_queue_logical_block_size(rq, 1 << 12);
+ blk_queue_max_hw_sectors(rq, nr_max_blk << 3); /* 8 * 512 = blk_size */
+ blk_queue_max_segments(rq, nr_max_blk);
+ queue_flag_set_unlocked(QUEUE_FLAG_NONROT, rq);
+ scm_blk_dev_cluster_setup(bdev);
+
+ bdev->gendisk = alloc_disk(SCM_NR_PARTS);
+ if (!bdev->gendisk)
+ goto out_queue;
+
+ rq->queuedata = scmdev;
+ bdev->gendisk->driverfs_dev = &scmdev->dev;
+ bdev->gendisk->private_data = scmdev;
+ bdev->gendisk->fops = &scm_blk_devops;
+ bdev->gendisk->queue = rq;
+ bdev->gendisk->major = scm_major;
+ bdev->gendisk->first_minor = devindex * SCM_NR_PARTS;
+
+ len = snprintf(bdev->gendisk->disk_name, DISK_NAME_LEN, "scm");
+ if (devindex > 25) {
+ len += snprintf(bdev->gendisk->disk_name + len,
+ DISK_NAME_LEN - len, "%c",
+ 'a' + (devindex / 26) - 1);
+ devindex = devindex % 26;
+ }
+ snprintf(bdev->gendisk->disk_name + len, DISK_NAME_LEN - len, "%c",
+ 'a' + devindex);
+
+ /* 512 byte sectors */
+ set_capacity(bdev->gendisk, scmdev->size >> 9);
+ add_disk(bdev->gendisk);
+ return 0;
+
+out_queue:
+ blk_cleanup_queue(rq);
+out:
+ atomic_dec(&nr_devices);
+ return ret;
+}
+
+void scm_blk_dev_cleanup(struct scm_blk_dev *bdev)
+{
+ tasklet_kill(&bdev->tasklet);
+ del_gendisk(bdev->gendisk);
+ blk_cleanup_queue(bdev->gendisk->queue);
+ put_disk(bdev->gendisk);
+}
+
+static int __init scm_blk_init(void)
+{
+ int ret = -EINVAL;
+
+ if (!scm_cluster_size_valid())
+ goto out;
+
+ ret = register_blkdev(0, "scm");
+ if (ret < 0)
+ goto out;
+
+ scm_major = ret;
+ if (scm_alloc_rqs(nr_requests))
+ goto out_unreg;
+
+ scm_debug = debug_register("scm_log", 16, 1, 16);
+ if (!scm_debug)
+ goto out_free;
+
+ debug_register_view(scm_debug, &debug_hex_ascii_view);
+ debug_set_level(scm_debug, 2);
+
+ ret = scm_drv_init();
+ if (ret)
+ goto out_dbf;
+
+ return ret;
+
+out_dbf:
+ debug_unregister(scm_debug);
+out_free:
+ scm_free_rqs();
+out_unreg:
+ unregister_blkdev(scm_major, "scm");
+out:
+ return ret;
+}
+module_init(scm_blk_init);
+
+static void __exit scm_blk_cleanup(void)
+{
+ scm_drv_cleanup();
+ debug_unregister(scm_debug);
+ scm_free_rqs();
+ unregister_blkdev(scm_major, "scm");
+}
+module_exit(scm_blk_cleanup);
diff --git a/drivers/s390/block/scm_blk.h b/drivers/s390/block/scm_blk.h
new file mode 100644
index 000000000000..7ac6bad919ef
--- /dev/null
+++ b/drivers/s390/block/scm_blk.h
@@ -0,0 +1,117 @@
+#ifndef SCM_BLK_H
+#define SCM_BLK_H
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/list.h>
+
+#include <asm/debug.h>
+#include <asm/eadm.h>
+
+#define SCM_NR_PARTS 8
+#define SCM_QUEUE_DELAY 5
+
+struct scm_blk_dev {
+ struct tasklet_struct tasklet;
+ struct request_queue *rq;
+ struct gendisk *gendisk;
+ struct scm_device *scmdev;
+ spinlock_t rq_lock; /* guard the request queue */
+ spinlock_t lock; /* guard the rest of the blockdev */
+ atomic_t queued_reqs;
+ struct list_head finished_requests;
+#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
+ struct list_head cluster_list;
+#endif
+};
+
+struct scm_request {
+ struct scm_blk_dev *bdev;
+ struct request *request;
+ struct aidaw *aidaw;
+ struct aob *aob;
+ struct list_head list;
+ u8 retries;
+ int error;
+#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
+ struct {
+ enum {CLUSTER_NONE, CLUSTER_READ, CLUSTER_WRITE} state;
+ struct list_head list;
+ void **buf;
+ } cluster;
+#endif
+};
+
+#define to_aobrq(rq) container_of((void *) rq, struct aob_rq_header, data)
+
+int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *);
+void scm_blk_dev_cleanup(struct scm_blk_dev *);
+void scm_blk_irq(struct scm_device *, void *, int);
+
+void scm_request_finish(struct scm_request *);
+void scm_request_requeue(struct scm_request *);
+
+int scm_drv_init(void);
+void scm_drv_cleanup(void);
+
+#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
+void __scm_free_rq_cluster(struct scm_request *);
+int __scm_alloc_rq_cluster(struct scm_request *);
+void scm_request_cluster_init(struct scm_request *);
+bool scm_reserve_cluster(struct scm_request *);
+void scm_release_cluster(struct scm_request *);
+void scm_blk_dev_cluster_setup(struct scm_blk_dev *);
+bool scm_need_cluster_request(struct scm_request *);
+void scm_initiate_cluster_request(struct scm_request *);
+void scm_cluster_request_irq(struct scm_request *);
+bool scm_test_cluster_request(struct scm_request *);
+bool scm_cluster_size_valid(void);
+#else
+#define __scm_free_rq_cluster(scmrq) {}
+#define __scm_alloc_rq_cluster(scmrq) 0
+#define scm_request_cluster_init(scmrq) {}
+#define scm_reserve_cluster(scmrq) true
+#define scm_release_cluster(scmrq) {}
+#define scm_blk_dev_cluster_setup(bdev) {}
+#define scm_need_cluster_request(scmrq) false
+#define scm_initiate_cluster_request(scmrq) {}
+#define scm_cluster_request_irq(scmrq) {}
+#define scm_test_cluster_request(scmrq) false
+#define scm_cluster_size_valid() true
+#endif
+
+extern debug_info_t *scm_debug;
+
+#define SCM_LOG(imp, txt) do { \
+ debug_text_event(scm_debug, imp, txt); \
+ } while (0)
+
+static inline void SCM_LOG_HEX(int level, void *data, int length)
+{
+ if (level > scm_debug->level)
+ return;
+ while (length > 0) {
+ debug_event(scm_debug, level, data, length);
+ length -= scm_debug->buf_size;
+ data += scm_debug->buf_size;
+ }
+}
+
+static inline void SCM_LOG_STATE(int level, struct scm_device *scmdev)
+{
+ struct {
+ u64 address;
+ u8 oper_state;
+ u8 rank;
+ } __packed data = {
+ .address = scmdev->address,
+ .oper_state = scmdev->attrs.oper_state,
+ .rank = scmdev->attrs.rank,
+ };
+
+ SCM_LOG_HEX(level, &data, sizeof(data));
+}
+
+#endif /* SCM_BLK_H */
diff --git a/drivers/s390/block/scm_blk_cluster.c b/drivers/s390/block/scm_blk_cluster.c
new file mode 100644
index 000000000000..f4bb61b0cea1
--- /dev/null
+++ b/drivers/s390/block/scm_blk_cluster.c
@@ -0,0 +1,228 @@
+/*
+ * Block driver for s390 storage class memory.
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/blkdev.h>
+#include <linux/genhd.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <asm/eadm.h>
+#include "scm_blk.h"
+
+static unsigned int write_cluster_size = 64;
+module_param(write_cluster_size, uint, S_IRUGO);
+MODULE_PARM_DESC(write_cluster_size,
+ "Number of pages used for contiguous writes.");
+
+#define CLUSTER_SIZE (write_cluster_size * PAGE_SIZE)
+
+void __scm_free_rq_cluster(struct scm_request *scmrq)
+{
+ int i;
+
+ if (!scmrq->cluster.buf)
+ return;
+
+ for (i = 0; i < 2 * write_cluster_size; i++)
+ free_page((unsigned long) scmrq->cluster.buf[i]);
+
+ kfree(scmrq->cluster.buf);
+}
+
+int __scm_alloc_rq_cluster(struct scm_request *scmrq)
+{
+ int i;
+
+ scmrq->cluster.buf = kzalloc(sizeof(void *) * 2 * write_cluster_size,
+ GFP_KERNEL);
+ if (!scmrq->cluster.buf)
+ return -ENOMEM;
+
+ for (i = 0; i < 2 * write_cluster_size; i++) {
+ scmrq->cluster.buf[i] = (void *) get_zeroed_page(GFP_DMA);
+ if (!scmrq->cluster.buf[i])
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&scmrq->cluster.list);
+ return 0;
+}
+
+void scm_request_cluster_init(struct scm_request *scmrq)
+{
+ scmrq->cluster.state = CLUSTER_NONE;
+}
+
+static bool clusters_intersect(struct scm_request *A, struct scm_request *B)
+{
+ unsigned long firstA, lastA, firstB, lastB;
+
+ firstA = ((u64) blk_rq_pos(A->request) << 9) / CLUSTER_SIZE;
+ lastA = (((u64) blk_rq_pos(A->request) << 9) +
+ blk_rq_bytes(A->request) - 1) / CLUSTER_SIZE;
+
+ firstB = ((u64) blk_rq_pos(B->request) << 9) / CLUSTER_SIZE;
+ lastB = (((u64) blk_rq_pos(B->request) << 9) +
+ blk_rq_bytes(B->request) - 1) / CLUSTER_SIZE;
+
+ return (firstB <= lastA && firstA <= lastB);
+}
+
+bool scm_reserve_cluster(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+ struct scm_request *iter;
+
+ if (write_cluster_size == 0)
+ return true;
+
+ spin_lock(&bdev->lock);
+ list_for_each_entry(iter, &bdev->cluster_list, cluster.list) {
+ if (clusters_intersect(scmrq, iter) &&
+ (rq_data_dir(scmrq->request) == WRITE ||
+ rq_data_dir(iter->request) == WRITE)) {
+ spin_unlock(&bdev->lock);
+ return false;
+ }
+ }
+ list_add(&scmrq->cluster.list, &bdev->cluster_list);
+ spin_unlock(&bdev->lock);
+
+ return true;
+}
+
+void scm_release_cluster(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+ unsigned long flags;
+
+ if (write_cluster_size == 0)
+ return;
+
+ spin_lock_irqsave(&bdev->lock, flags);
+ list_del(&scmrq->cluster.list);
+ spin_unlock_irqrestore(&bdev->lock, flags);
+}
+
+void scm_blk_dev_cluster_setup(struct scm_blk_dev *bdev)
+{
+ INIT_LIST_HEAD(&bdev->cluster_list);
+ blk_queue_io_opt(bdev->rq, CLUSTER_SIZE);
+}
+
+static void scm_prepare_cluster_request(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+ struct scm_device *scmdev = bdev->gendisk->private_data;
+ struct request *req = scmrq->request;
+ struct aidaw *aidaw = scmrq->aidaw;
+ struct msb *msb = &scmrq->aob->msb[0];
+ struct req_iterator iter;
+ struct bio_vec *bv;
+ int i = 0;
+ u64 addr;
+
+ switch (scmrq->cluster.state) {
+ case CLUSTER_NONE:
+ scmrq->cluster.state = CLUSTER_READ;
+ /* fall through */
+ case CLUSTER_READ:
+ scmrq->aob->request.msb_count = 1;
+ msb->bs = MSB_BS_4K;
+ msb->oc = MSB_OC_READ;
+ msb->flags = MSB_FLAG_IDA;
+ msb->data_addr = (u64) aidaw;
+ msb->blk_count = write_cluster_size;
+
+ addr = scmdev->address + ((u64) blk_rq_pos(req) << 9);
+ msb->scm_addr = round_down(addr, CLUSTER_SIZE);
+
+ if (msb->scm_addr !=
+ round_down(addr + (u64) blk_rq_bytes(req) - 1,
+ CLUSTER_SIZE))
+ msb->blk_count = 2 * write_cluster_size;
+
+ for (i = 0; i < msb->blk_count; i++) {
+ aidaw->data_addr = (u64) scmrq->cluster.buf[i];
+ aidaw++;
+ }
+
+ break;
+ case CLUSTER_WRITE:
+ msb->oc = MSB_OC_WRITE;
+
+ for (addr = msb->scm_addr;
+ addr < scmdev->address + ((u64) blk_rq_pos(req) << 9);
+ addr += PAGE_SIZE) {
+ aidaw->data_addr = (u64) scmrq->cluster.buf[i];
+ aidaw++;
+ i++;
+ }
+ rq_for_each_segment(bv, req, iter) {
+ aidaw->data_addr = (u64) page_address(bv->bv_page);
+ aidaw++;
+ i++;
+ }
+ for (; i < msb->blk_count; i++) {
+ aidaw->data_addr = (u64) scmrq->cluster.buf[i];
+ aidaw++;
+ }
+ break;
+ }
+}
+
+bool scm_need_cluster_request(struct scm_request *scmrq)
+{
+ if (rq_data_dir(scmrq->request) == READ)
+ return false;
+
+ return blk_rq_bytes(scmrq->request) < CLUSTER_SIZE;
+}
+
+/* Called with queue lock held. */
+void scm_initiate_cluster_request(struct scm_request *scmrq)
+{
+ scm_prepare_cluster_request(scmrq);
+ if (scm_start_aob(scmrq->aob))
+ scm_request_requeue(scmrq);
+}
+
+bool scm_test_cluster_request(struct scm_request *scmrq)
+{
+ return scmrq->cluster.state != CLUSTER_NONE;
+}
+
+void scm_cluster_request_irq(struct scm_request *scmrq)
+{
+ struct scm_blk_dev *bdev = scmrq->bdev;
+ unsigned long flags;
+
+ switch (scmrq->cluster.state) {
+ case CLUSTER_NONE:
+ BUG();
+ break;
+ case CLUSTER_READ:
+ if (scmrq->error) {
+ scm_request_finish(scmrq);
+ break;
+ }
+ scmrq->cluster.state = CLUSTER_WRITE;
+ spin_lock_irqsave(&bdev->rq_lock, flags);
+ scm_initiate_cluster_request(scmrq);
+ spin_unlock_irqrestore(&bdev->rq_lock, flags);
+ break;
+ case CLUSTER_WRITE:
+ scm_request_finish(scmrq);
+ break;
+ }
+}
+
+bool scm_cluster_size_valid(void)
+{
+ return write_cluster_size == 0 || write_cluster_size == 32 ||
+ write_cluster_size == 64 || write_cluster_size == 128;
+}
diff --git a/drivers/s390/block/scm_drv.c b/drivers/s390/block/scm_drv.c
new file mode 100644
index 000000000000..9fa0a908607b
--- /dev/null
+++ b/drivers/s390/block/scm_drv.c
@@ -0,0 +1,81 @@
+/*
+ * Device driver for s390 storage class memory.
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#define KMSG_COMPONENT "scm_block"
+#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/eadm.h>
+#include "scm_blk.h"
+
+static void notify(struct scm_device *scmdev)
+{
+ pr_info("%lu: The capabilities of the SCM increment changed\n",
+ (unsigned long) scmdev->address);
+ SCM_LOG(2, "State changed");
+ SCM_LOG_STATE(2, scmdev);
+}
+
+static int scm_probe(struct scm_device *scmdev)
+{
+ struct scm_blk_dev *bdev;
+ int ret;
+
+ SCM_LOG(2, "probe");
+ SCM_LOG_STATE(2, scmdev);
+
+ if (scmdev->attrs.oper_state != OP_STATE_GOOD)
+ return -EINVAL;
+
+ bdev = kzalloc(sizeof(*bdev), GFP_KERNEL);
+ if (!bdev)
+ return -ENOMEM;
+
+ dev_set_drvdata(&scmdev->dev, bdev);
+ ret = scm_blk_dev_setup(bdev, scmdev);
+ if (ret) {
+ dev_set_drvdata(&scmdev->dev, NULL);
+ kfree(bdev);
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
+static int scm_remove(struct scm_device *scmdev)
+{
+ struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev);
+
+ scm_blk_dev_cleanup(bdev);
+ dev_set_drvdata(&scmdev->dev, NULL);
+ kfree(bdev);
+
+ return 0;
+}
+
+static struct scm_driver scm_drv = {
+ .drv = {
+ .name = "scm_block",
+ .owner = THIS_MODULE,
+ },
+ .notify = notify,
+ .probe = scm_probe,
+ .remove = scm_remove,
+ .handler = scm_blk_irq,
+};
+
+int __init scm_drv_init(void)
+{
+ return scm_driver_register(&scm_drv);
+}
+
+void scm_drv_cleanup(void)
+{
+ scm_driver_unregister(&scm_drv);
+}
diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c
index bb07577e8fd4..699fd3e363df 100644
--- a/drivers/s390/char/con3270.c
+++ b/drivers/s390/char/con3270.c
@@ -35,7 +35,6 @@ static struct raw3270_fn con3270_fn;
*/
struct con3270 {
struct raw3270_view view;
- spinlock_t lock;
struct list_head freemem; /* list of free memory for strings. */
/* Output stuff. */
diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c
index 5b8b8592d311..f4ff515db251 100644
--- a/drivers/s390/char/monreader.c
+++ b/drivers/s390/char/monreader.c
@@ -571,8 +571,11 @@ static int __init mon_init(void)
if (rc)
goto out_iucv;
monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL);
- if (!monreader_device)
+ if (!monreader_device) {
+ rc = -ENOMEM;
goto out_driver;
+ }
+
dev_set_name(monreader_device, "monreader-dev");
monreader_device->bus = &iucv_bus;
monreader_device->parent = iucv_root;
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 3fcc000efc53..4fa21f7e2308 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -334,7 +334,7 @@ sclp_dispatch_evbufs(struct sccb_header *sccb)
reg->receiver_fn(evbuf);
spin_lock_irqsave(&sclp_lock, flags);
} else if (reg == NULL)
- rc = -ENOSYS;
+ rc = -EOPNOTSUPP;
}
spin_unlock_irqrestore(&sclp_lock, flags);
return rc;
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index 4be63be73445..3b13d58fe87b 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -463,7 +463,7 @@ sclp_emit_buffer(struct sclp_buffer *buffer,
/* Use write priority message */
sccb->msg_buf.header.type = EVTYP_PMSGCMD;
else
- return -ENOSYS;
+ return -EOPNOTSUPP;
buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA;
buffer->request.status = SCLP_REQ_FILLED;
buffer->request.callback = sclp_writedata_callback;
diff --git a/drivers/s390/char/tape.h b/drivers/s390/char/tape.h
index c06be6cc2fc3..ea664dd4f56d 100644
--- a/drivers/s390/char/tape.h
+++ b/drivers/s390/char/tape.h
@@ -15,7 +15,6 @@
#include <asm/ccwdev.h>
#include <asm/debug.h>
#include <asm/idals.h>
-#include <linux/blkdev.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtio.h>
diff --git a/drivers/s390/char/tape_std.h b/drivers/s390/char/tape_std.h
index c5816ad9ed7d..8c760c036832 100644
--- a/drivers/s390/char/tape_std.h
+++ b/drivers/s390/char/tape_std.h
@@ -100,11 +100,7 @@ struct tape_request *tape_std_read_block(struct tape_device *, size_t);
void tape_std_read_backward(struct tape_device *device,
struct tape_request *request);
struct tape_request *tape_std_write_block(struct tape_device *, size_t);
-struct tape_request *tape_std_bread(struct tape_device *, struct request *);
-void tape_std_free_bread(struct tape_request *);
void tape_std_check_locate(struct tape_device *, struct tape_request *);
-struct tape_request *tape_std_bwrite(struct request *,
- struct tape_device *, int);
/* Some non-mtop commands. */
int tape_std_assign(struct tape_device *);
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index c131bc40f962..9b3a24e8d3a0 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -321,7 +321,7 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
* only allow for blocking reads to be open
*/
if (filp->f_flags & O_NONBLOCK)
- return -ENOSYS;
+ return -EOPNOTSUPP;
/* Besure this device hasn't already been opened */
spin_lock_bh(&logptr->priv_lock);
diff --git a/drivers/s390/cio/Makefile b/drivers/s390/cio/Makefile
index e1b700a19648..8c4a386e97f6 100644
--- a/drivers/s390/cio/Makefile
+++ b/drivers/s390/cio/Makefile
@@ -8,6 +8,8 @@ ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o
obj-$(CONFIG_CHSC_SCH) += chsc_sch.o
+obj-$(CONFIG_EADM_SCH) += eadm_sch.o
+obj-$(CONFIG_SCM_BUS) += scm.o
obj-$(CONFIG_CCWGROUP) += ccwgroup.o
qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_setup.o
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index cfe0c087fe5c..4d51a7c4eb8b 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -52,6 +52,11 @@ int chsc_error_from_response(int response)
return -EINVAL;
case 0x0004:
return -EOPNOTSUPP;
+ case 0x000b:
+ return -EBUSY;
+ case 0x0100:
+ case 0x0102:
+ return -ENOMEM;
default:
return -EIO;
}
@@ -393,6 +398,20 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
}
}
+static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area)
+{
+ int ret;
+
+ CIO_CRW_EVENT(4, "chsc: scm change notification\n");
+ if (sei_area->rs != 7)
+ return;
+
+ ret = scm_update_information();
+ if (ret)
+ CIO_CRW_EVENT(0, "chsc: updating change notification"
+ " failed (rc=%d).\n", ret);
+}
+
static void chsc_process_sei(struct chsc_sei_area *sei_area)
{
/* Check if we might have lost some information. */
@@ -414,6 +433,9 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area)
case 8: /* channel-path-configuration notification */
chsc_process_sei_chp_config(sei_area);
break;
+ case 12: /* scm change notification */
+ chsc_process_sei_scm_change(sei_area);
+ break;
default: /* other stuff */
CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
sei_area->cc);
@@ -1047,3 +1069,33 @@ out:
return rc;
}
EXPORT_SYMBOL_GPL(chsc_siosl);
+
+/**
+ * chsc_scm_info() - store SCM information (SSI)
+ * @scm_area: request and response block for SSI
+ * @token: continuation token
+ *
+ * Returns 0 on success.
+ */
+int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token)
+{
+ int ccode, ret;
+
+ memset(scm_area, 0, sizeof(*scm_area));
+ scm_area->request.length = 0x0020;
+ scm_area->request.code = 0x004C;
+ scm_area->reqtok = token;
+
+ ccode = chsc(scm_area);
+ if (ccode > 0) {
+ ret = (ccode == 3) ? -ENODEV : -EBUSY;
+ goto out;
+ }
+ ret = chsc_error_from_response(scm_area->response.code);
+ if (ret != 0)
+ CIO_MSG_EVENT(2, "chsc: scm info failed (rc=%04x)\n",
+ scm_area->response.code);
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(chsc_scm_info);
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index 3f15b2aaeaea..662dab4b93e6 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -3,6 +3,7 @@
#include <linux/types.h>
#include <linux/device.h>
+#include <asm/css_chars.h>
#include <asm/chpid.h>
#include <asm/chsc.h>
#include <asm/schid.h>
@@ -118,4 +119,46 @@ int chsc_error_from_response(int response);
int chsc_siosl(struct subchannel_id schid);
+/* Functions and definitions to query storage-class memory. */
+struct sale {
+ u64 sa;
+ u32 p:4;
+ u32 op_state:4;
+ u32 data_state:4;
+ u32 rank:4;
+ u32 r:1;
+ u32:7;
+ u32 rid:8;
+ u32:32;
+} __packed;
+
+struct chsc_scm_info {
+ struct chsc_header request;
+ u32:32;
+ u64 reqtok;
+ u32 reserved1[4];
+ struct chsc_header response;
+ u64:56;
+ u8 rq;
+ u32 mbc;
+ u64 msa;
+ u16 is;
+ u16 mmc;
+ u32 mci;
+ u64 nr_scm_ini;
+ u64 nr_scm_unini;
+ u32 reserved2[10];
+ u64 restok;
+ struct sale scmal[248];
+} __packed;
+
+int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);
+
+#ifdef CONFIG_SCM_BUS
+int scm_update_information(void);
+#else /* CONFIG_SCM_BUS */
+#define scm_update_information() 0
+#endif /* CONFIG_SCM_BUS */
+
+
#endif
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 33d1ef703593..8e927b9f285f 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -1029,7 +1029,7 @@ extern void do_reipl_asm(__u32 schid);
/* Make sure all subchannels are quiet before we re-ipl an lpar. */
void reipl_ccw_dev(struct ccw_dev_id *devid)
{
- struct subchannel_id schid;
+ struct subchannel_id uninitialized_var(schid);
s390_reset_system(NULL, NULL);
if (reipl_find_schid(devid, &schid) != 0)
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 21908e67bf67..b4d572f65f07 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -445,6 +445,7 @@ void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo)
put_device(&sch->dev);
}
}
+EXPORT_SYMBOL_GPL(css_sched_sch_todo);
static void css_sch_todo(struct work_struct *work)
{
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index ed25c8740a9c..fc916f5d7314 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -1426,6 +1426,8 @@ static enum io_sch_action sch_get_action(struct subchannel *sch)
return IO_SCH_REPROBE;
if (cdev->online)
return IO_SCH_VERIFY;
+ if (cdev->private->state == DEV_STATE_NOT_OPER)
+ return IO_SCH_UNREG_ATTACH;
return IO_SCH_NOP;
}
@@ -1519,11 +1521,14 @@ static int io_subchannel_sch_event(struct subchannel *sch, int process)
goto out;
break;
case IO_SCH_UNREG_ATTACH:
+ spin_lock_irqsave(sch->lock, flags);
if (cdev->private->flags.resuming) {
/* Device will be handled later. */
rc = 0;
- goto out;
+ goto out_unlock;
}
+ sch_set_cdev(sch, NULL);
+ spin_unlock_irqrestore(sch->lock, flags);
/* Unregister ccw device. */
ccw_device_unregister(cdev);
break;
diff --git a/drivers/s390/cio/eadm_sch.c b/drivers/s390/cio/eadm_sch.c
new file mode 100644
index 000000000000..6c9673400464
--- /dev/null
+++ b/drivers/s390/cio/eadm_sch.c
@@ -0,0 +1,401 @@
+/*
+ * Driver for s390 eadm subchannels
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#include <linux/kernel_stat.h>
+#include <linux/workqueue.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+#include <asm/css_chars.h>
+#include <asm/debug.h>
+#include <asm/isc.h>
+#include <asm/cio.h>
+#include <asm/scsw.h>
+#include <asm/eadm.h>
+
+#include "eadm_sch.h"
+#include "ioasm.h"
+#include "cio.h"
+#include "css.h"
+#include "orb.h"
+
+MODULE_DESCRIPTION("driver for s390 eadm subchannels");
+MODULE_LICENSE("GPL");
+
+#define EADM_TIMEOUT (5 * HZ)
+static DEFINE_SPINLOCK(list_lock);
+static LIST_HEAD(eadm_list);
+
+static debug_info_t *eadm_debug;
+
+#define EADM_LOG(imp, txt) do { \
+ debug_text_event(eadm_debug, imp, txt); \
+ } while (0)
+
+static void EADM_LOG_HEX(int level, void *data, int length)
+{
+ if (level > eadm_debug->level)
+ return;
+ while (length > 0) {
+ debug_event(eadm_debug, level, data, length);
+ length -= eadm_debug->buf_size;
+ data += eadm_debug->buf_size;
+ }
+}
+
+static void orb_init(union orb *orb)
+{
+ memset(orb, 0, sizeof(union orb));
+ orb->eadm.compat1 = 1;
+ orb->eadm.compat2 = 1;
+ orb->eadm.fmt = 1;
+ orb->eadm.x = 1;
+}
+
+static int eadm_subchannel_start(struct subchannel *sch, struct aob *aob)
+{
+ union orb *orb = &get_eadm_private(sch)->orb;
+ int cc;
+
+ orb_init(orb);
+ orb->eadm.aob = (u32)__pa(aob);
+ orb->eadm.intparm = (u32)(addr_t)sch;
+ orb->eadm.key = PAGE_DEFAULT_KEY >> 4;
+
+ EADM_LOG(6, "start");
+ EADM_LOG_HEX(6, &sch->schid, sizeof(sch->schid));
+
+ cc = ssch(sch->schid, orb);
+ switch (cc) {
+ case 0:
+ sch->schib.scsw.eadm.actl |= SCSW_ACTL_START_PEND;
+ break;
+ case 1: /* status pending */
+ case 2: /* busy */
+ return -EBUSY;
+ case 3: /* not operational */
+ return -ENODEV;
+ }
+ return 0;
+}
+
+static int eadm_subchannel_clear(struct subchannel *sch)
+{
+ int cc;
+
+ cc = csch(sch->schid);
+ if (cc)
+ return -ENODEV;
+
+ sch->schib.scsw.eadm.actl |= SCSW_ACTL_CLEAR_PEND;
+ return 0;
+}
+
+static void eadm_subchannel_timeout(unsigned long data)
+{
+ struct subchannel *sch = (struct subchannel *) data;
+
+ spin_lock_irq(sch->lock);
+ EADM_LOG(1, "timeout");
+ EADM_LOG_HEX(1, &sch->schid, sizeof(sch->schid));
+ if (eadm_subchannel_clear(sch))
+ EADM_LOG(0, "clear failed");
+ spin_unlock_irq(sch->lock);
+}
+
+static void eadm_subchannel_set_timeout(struct subchannel *sch, int expires)
+{
+ struct eadm_private *private = get_eadm_private(sch);
+
+ if (expires == 0) {
+ del_timer(&private->timer);
+ return;
+ }
+ if (timer_pending(&private->timer)) {
+ if (mod_timer(&private->timer, jiffies + expires))
+ return;
+ }
+ private->timer.function = eadm_subchannel_timeout;
+ private->timer.data = (unsigned long) sch;
+ private->timer.expires = jiffies + expires;
+ add_timer(&private->timer);
+}
+
+static void eadm_subchannel_irq(struct subchannel *sch)
+{
+ struct eadm_private *private = get_eadm_private(sch);
+ struct eadm_scsw *scsw = &sch->schib.scsw.eadm;
+ struct irb *irb = (struct irb *)&S390_lowcore.irb;
+ int error = 0;
+
+ EADM_LOG(6, "irq");
+ EADM_LOG_HEX(6, irb, sizeof(*irb));
+
+ kstat_cpu(smp_processor_id()).irqs[IOINT_ADM]++;
+
+ if ((scsw->stctl & (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))
+ && scsw->eswf == 1 && irb->esw.eadm.erw.r)
+ error = -EIO;
+
+ if (scsw->fctl & SCSW_FCTL_CLEAR_FUNC)
+ error = -ETIMEDOUT;
+
+ eadm_subchannel_set_timeout(sch, 0);
+
+ if (private->state != EADM_BUSY) {
+ EADM_LOG(1, "irq unsol");
+ EADM_LOG_HEX(1, irb, sizeof(*irb));
+ private->state = EADM_NOT_OPER;
+ css_sched_sch_todo(sch, SCH_TODO_EVAL);
+ return;
+ }
+ scm_irq_handler((struct aob *)(unsigned long)scsw->aob, error);
+ private->state = EADM_IDLE;
+}
+
+static struct subchannel *eadm_get_idle_sch(void)
+{
+ struct eadm_private *private;
+ struct subchannel *sch;
+ unsigned long flags;
+
+ spin_lock_irqsave(&list_lock, flags);
+ list_for_each_entry(private, &eadm_list, head) {
+ sch = private->sch;
+ spin_lock(sch->lock);
+ if (private->state == EADM_IDLE) {
+ private->state = EADM_BUSY;
+ list_move_tail(&private->head, &eadm_list);
+ spin_unlock(sch->lock);
+ spin_unlock_irqrestore(&list_lock, flags);
+
+ return sch;
+ }
+ spin_unlock(sch->lock);
+ }
+ spin_unlock_irqrestore(&list_lock, flags);
+
+ return NULL;
+}
+
+static int eadm_start_aob(struct aob *aob)
+{
+ struct eadm_private *private;
+ struct subchannel *sch;
+ unsigned long flags;
+ int ret;
+
+ sch = eadm_get_idle_sch();
+ if (!sch)
+ return -EBUSY;
+
+ spin_lock_irqsave(sch->lock, flags);
+ eadm_subchannel_set_timeout(sch, EADM_TIMEOUT);
+ ret = eadm_subchannel_start(sch, aob);
+ if (!ret)
+ goto out_unlock;
+
+ /* Handle start subchannel failure. */
+ eadm_subchannel_set_timeout(sch, 0);
+ private = get_eadm_private(sch);
+ private->state = EADM_NOT_OPER;
+ css_sched_sch_todo(sch, SCH_TODO_EVAL);
+
+out_unlock:
+ spin_unlock_irqrestore(sch->lock, flags);
+
+ return ret;
+}
+
+static int eadm_subchannel_probe(struct subchannel *sch)
+{
+ struct eadm_private *private;
+ int ret;
+
+ private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
+ if (!private)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&private->head);
+ init_timer(&private->timer);
+
+ spin_lock_irq(sch->lock);
+ set_eadm_private(sch, private);
+ private->state = EADM_IDLE;
+ private->sch = sch;
+ sch->isc = EADM_SCH_ISC;
+ ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
+ if (ret) {
+ set_eadm_private(sch, NULL);
+ spin_unlock_irq(sch->lock);
+ kfree(private);
+ goto out;
+ }
+ spin_unlock_irq(sch->lock);
+
+ spin_lock_irq(&list_lock);
+ list_add(&private->head, &eadm_list);
+ spin_unlock_irq(&list_lock);
+
+ if (dev_get_uevent_suppress(&sch->dev)) {
+ dev_set_uevent_suppress(&sch->dev, 0);
+ kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
+ }
+out:
+ return ret;
+}
+
+static void eadm_quiesce(struct subchannel *sch)
+{
+ int ret;
+
+ do {
+ spin_lock_irq(sch->lock);
+ ret = cio_disable_subchannel(sch);
+ spin_unlock_irq(sch->lock);
+ } while (ret == -EBUSY);
+}
+
+static int eadm_subchannel_remove(struct subchannel *sch)
+{
+ struct eadm_private *private = get_eadm_private(sch);
+
+ spin_lock_irq(&list_lock);
+ list_del(&private->head);
+ spin_unlock_irq(&list_lock);
+
+ eadm_quiesce(sch);
+
+ spin_lock_irq(sch->lock);
+ set_eadm_private(sch, NULL);
+ spin_unlock_irq(sch->lock);
+
+ kfree(private);
+
+ return 0;
+}
+
+static void eadm_subchannel_shutdown(struct subchannel *sch)
+{
+ eadm_quiesce(sch);
+}
+
+static int eadm_subchannel_freeze(struct subchannel *sch)
+{
+ return cio_disable_subchannel(sch);
+}
+
+static int eadm_subchannel_restore(struct subchannel *sch)
+{
+ return cio_enable_subchannel(sch, (u32)(unsigned long)sch);
+}
+
+/**
+ * eadm_subchannel_sch_event - process subchannel event
+ * @sch: subchannel
+ * @process: non-zero if function is called in process context
+ *
+ * An unspecified event occurred for this subchannel. Adjust data according
+ * to the current operational state of the subchannel. Return zero when the
+ * event has been handled sufficiently or -EAGAIN when this function should
+ * be called again in process context.
+ */
+static int eadm_subchannel_sch_event(struct subchannel *sch, int process)
+{
+ struct eadm_private *private;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(sch->lock, flags);
+ if (!device_is_registered(&sch->dev))
+ goto out_unlock;
+
+ if (work_pending(&sch->todo_work))
+ goto out_unlock;
+
+ if (cio_update_schib(sch)) {
+ css_sched_sch_todo(sch, SCH_TODO_UNREG);
+ goto out_unlock;
+ }
+ private = get_eadm_private(sch);
+ if (private->state == EADM_NOT_OPER)
+ private->state = EADM_IDLE;
+
+out_unlock:
+ spin_unlock_irqrestore(sch->lock, flags);
+
+ return ret;
+}
+
+static struct css_device_id eadm_subchannel_ids[] = {
+ { .match_flags = 0x1, .type = SUBCHANNEL_TYPE_ADM, },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(css, eadm_subchannel_ids);
+
+static struct css_driver eadm_subchannel_driver = {
+ .drv = {
+ .name = "eadm_subchannel",
+ .owner = THIS_MODULE,
+ },
+ .subchannel_type = eadm_subchannel_ids,
+ .irq = eadm_subchannel_irq,
+ .probe = eadm_subchannel_probe,
+ .remove = eadm_subchannel_remove,
+ .shutdown = eadm_subchannel_shutdown,
+ .sch_event = eadm_subchannel_sch_event,
+ .freeze = eadm_subchannel_freeze,
+ .thaw = eadm_subchannel_restore,
+ .restore = eadm_subchannel_restore,
+};
+
+static struct eadm_ops eadm_ops = {
+ .eadm_start = eadm_start_aob,
+ .owner = THIS_MODULE,
+};
+
+static int __init eadm_sch_init(void)
+{
+ int ret;
+
+ if (!css_general_characteristics.eadm)
+ return -ENXIO;
+
+ eadm_debug = debug_register("eadm_log", 16, 1, 16);
+ if (!eadm_debug)
+ return -ENOMEM;
+
+ debug_register_view(eadm_debug, &debug_hex_ascii_view);
+ debug_set_level(eadm_debug, 2);
+
+ isc_register(EADM_SCH_ISC);
+ ret = css_driver_register(&eadm_subchannel_driver);
+ if (ret)
+ goto cleanup;
+
+ register_eadm_ops(&eadm_ops);
+ return ret;
+
+cleanup:
+ isc_unregister(EADM_SCH_ISC);
+ debug_unregister(eadm_debug);
+ return ret;
+}
+
+static void __exit eadm_sch_exit(void)
+{
+ unregister_eadm_ops(&eadm_ops);
+ css_driver_unregister(&eadm_subchannel_driver);
+ isc_unregister(EADM_SCH_ISC);
+ debug_unregister(eadm_debug);
+}
+module_init(eadm_sch_init);
+module_exit(eadm_sch_exit);
diff --git a/drivers/s390/cio/eadm_sch.h b/drivers/s390/cio/eadm_sch.h
new file mode 100644
index 000000000000..2779be093982
--- /dev/null
+++ b/drivers/s390/cio/eadm_sch.h
@@ -0,0 +1,20 @@
+#ifndef EADM_SCH_H
+#define EADM_SCH_H
+
+#include <linux/device.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include "orb.h"
+
+struct eadm_private {
+ union orb orb;
+ enum {EADM_IDLE, EADM_BUSY, EADM_NOT_OPER} state;
+ struct timer_list timer;
+ struct list_head head;
+ struct subchannel *sch;
+} __aligned(8);
+
+#define get_eadm_private(n) ((struct eadm_private *)dev_get_drvdata(&n->dev))
+#define set_eadm_private(n, p) (dev_set_drvdata(&n->dev, p))
+
+#endif
diff --git a/drivers/s390/cio/orb.h b/drivers/s390/cio/orb.h
index 45a9865c2b36..7a640530e7f5 100644
--- a/drivers/s390/cio/orb.h
+++ b/drivers/s390/cio/orb.h
@@ -59,9 +59,33 @@ struct tm_orb {
u32:32;
} __packed __aligned(4);
+/*
+ * eadm operation request block
+ */
+struct eadm_orb {
+ u32 intparm;
+ u32 key:4;
+ u32:4;
+ u32 compat1:1;
+ u32 compat2:1;
+ u32:21;
+ u32 x:1;
+ u32 aob;
+ u32 css_prio:8;
+ u32:8;
+ u32 scm_prio:8;
+ u32:8;
+ u32:29;
+ u32 fmt:3;
+ u32:32;
+ u32:32;
+ u32:32;
+} __packed __aligned(4);
+
union orb {
struct cmd_orb cmd;
struct tm_orb tm;
+ struct eadm_orb eadm;
} __packed __aligned(4);
#endif /* S390_ORB_H */
diff --git a/drivers/s390/cio/qdio_debug.h b/drivers/s390/cio/qdio_debug.h
index e1f646800ddb..7f8b973da298 100644
--- a/drivers/s390/cio/qdio_debug.h
+++ b/drivers/s390/cio/qdio_debug.h
@@ -37,10 +37,14 @@ static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level)
debug_text_event(qdio_dbf_setup, DBF_ERR, debug_buffer); \
} while (0)
-#define DBF_HEX(addr, len) \
- do { \
- debug_event(qdio_dbf_setup, DBF_ERR, (void*)(addr), len); \
- } while (0)
+static inline void DBF_HEX(void *addr, int len)
+{
+ while (len > 0) {
+ debug_event(qdio_dbf_setup, DBF_ERR, addr, len);
+ len -= qdio_dbf_setup->buf_size;
+ addr += qdio_dbf_setup->buf_size;
+ }
+}
#define DBF_ERROR(text...) \
do { \
@@ -49,11 +53,14 @@ static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level)
debug_text_event(qdio_dbf_error, DBF_ERR, debug_buffer); \
} while (0)
-#define DBF_ERROR_HEX(addr, len) \
- do { \
- debug_event(qdio_dbf_error, DBF_ERR, (void*)(addr), len); \
- } while (0)
-
+static inline void DBF_ERROR_HEX(void *addr, int len)
+{
+ while (len > 0) {
+ debug_event(qdio_dbf_error, DBF_ERR, addr, len);
+ len -= qdio_dbf_error->buf_size;
+ addr += qdio_dbf_error->buf_size;
+ }
+}
#define DBF_DEV_EVENT(level, device, text...) \
do { \
@@ -64,10 +71,15 @@ static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level)
} \
} while (0)
-#define DBF_DEV_HEX(level, device, addr, len) \
- do { \
- debug_event(device->debug_area, level, (void*)(addr), len); \
- } while (0)
+static inline void DBF_DEV_HEX(struct qdio_irq *dev, void *addr,
+ int len, int level)
+{
+ while (len > 0) {
+ debug_event(dev->debug_area, level, addr, len);
+ len -= dev->debug_area->buf_size;
+ addr += dev->debug_area->buf_size;
+ }
+}
void qdio_allocate_dbf(struct qdio_initialize *init_data,
struct qdio_irq *irq_ptr);
diff --git a/drivers/s390/cio/scm.c b/drivers/s390/cio/scm.c
new file mode 100644
index 000000000000..bcf20f3aa51b
--- /dev/null
+++ b/drivers/s390/cio/scm.c
@@ -0,0 +1,317 @@
+/*
+ * Recognize and maintain s390 storage class memory.
+ *
+ * Copyright IBM Corp. 2012
+ * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <asm/eadm.h>
+#include "chsc.h"
+
+static struct device *scm_root;
+static struct eadm_ops *eadm_ops;
+static DEFINE_MUTEX(eadm_ops_mutex);
+
+#define to_scm_dev(n) container_of(n, struct scm_device, dev)
+#define to_scm_drv(d) container_of(d, struct scm_driver, drv)
+
+static int scmdev_probe(struct device *dev)
+{
+ struct scm_device *scmdev = to_scm_dev(dev);
+ struct scm_driver *scmdrv = to_scm_drv(dev->driver);
+
+ return scmdrv->probe ? scmdrv->probe(scmdev) : -ENODEV;
+}
+
+static int scmdev_remove(struct device *dev)
+{
+ struct scm_device *scmdev = to_scm_dev(dev);
+ struct scm_driver *scmdrv = to_scm_drv(dev->driver);
+
+ return scmdrv->remove ? scmdrv->remove(scmdev) : -ENODEV;
+}
+
+static int scmdev_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ return add_uevent_var(env, "MODALIAS=scm:scmdev");
+}
+
+static struct bus_type scm_bus_type = {
+ .name = "scm",
+ .probe = scmdev_probe,
+ .remove = scmdev_remove,
+ .uevent = scmdev_uevent,
+};
+
+/**
+ * scm_driver_register() - register a scm driver
+ * @scmdrv: driver to be registered
+ */
+int scm_driver_register(struct scm_driver *scmdrv)
+{
+ struct device_driver *drv = &scmdrv->drv;
+
+ drv->bus = &scm_bus_type;
+
+ return driver_register(drv);
+}
+EXPORT_SYMBOL_GPL(scm_driver_register);
+
+/**
+ * scm_driver_unregister() - deregister a scm driver
+ * @scmdrv: driver to be deregistered
+ */
+void scm_driver_unregister(struct scm_driver *scmdrv)
+{
+ driver_unregister(&scmdrv->drv);
+}
+EXPORT_SYMBOL_GPL(scm_driver_unregister);
+
+int scm_get_ref(void)
+{
+ int ret = 0;
+
+ mutex_lock(&eadm_ops_mutex);
+ if (!eadm_ops || !try_module_get(eadm_ops->owner))
+ ret = -ENOENT;
+ mutex_unlock(&eadm_ops_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(scm_get_ref);
+
+void scm_put_ref(void)
+{
+ mutex_lock(&eadm_ops_mutex);
+ module_put(eadm_ops->owner);
+ mutex_unlock(&eadm_ops_mutex);
+}
+EXPORT_SYMBOL_GPL(scm_put_ref);
+
+void register_eadm_ops(struct eadm_ops *ops)
+{
+ mutex_lock(&eadm_ops_mutex);
+ eadm_ops = ops;
+ mutex_unlock(&eadm_ops_mutex);
+}
+EXPORT_SYMBOL_GPL(register_eadm_ops);
+
+void unregister_eadm_ops(struct eadm_ops *ops)
+{
+ mutex_lock(&eadm_ops_mutex);
+ eadm_ops = NULL;
+ mutex_unlock(&eadm_ops_mutex);
+}
+EXPORT_SYMBOL_GPL(unregister_eadm_ops);
+
+int scm_start_aob(struct aob *aob)
+{
+ return eadm_ops->eadm_start(aob);
+}
+EXPORT_SYMBOL_GPL(scm_start_aob);
+
+void scm_irq_handler(struct aob *aob, int error)
+{
+ struct aob_rq_header *aobrq = (void *) aob->request.data;
+ struct scm_device *scmdev = aobrq->scmdev;
+ struct scm_driver *scmdrv = to_scm_drv(scmdev->dev.driver);
+
+ scmdrv->handler(scmdev, aobrq->data, error);
+}
+EXPORT_SYMBOL_GPL(scm_irq_handler);
+
+#define scm_attr(name) \
+static ssize_t show_##name(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct scm_device *scmdev = to_scm_dev(dev); \
+ int ret; \
+ \
+ device_lock(dev); \
+ ret = sprintf(buf, "%u\n", scmdev->attrs.name); \
+ device_unlock(dev); \
+ \
+ return ret; \
+} \
+static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
+
+scm_attr(persistence);
+scm_attr(oper_state);
+scm_attr(data_state);
+scm_attr(rank);
+scm_attr(release);
+scm_attr(res_id);
+
+static struct attribute *scmdev_attrs[] = {
+ &dev_attr_persistence.attr,
+ &dev_attr_oper_state.attr,
+ &dev_attr_data_state.attr,
+ &dev_attr_rank.attr,
+ &dev_attr_release.attr,
+ &dev_attr_res_id.attr,
+ NULL,
+};
+
+static struct attribute_group scmdev_attr_group = {
+ .attrs = scmdev_attrs,
+};
+
+static const struct attribute_group *scmdev_attr_groups[] = {
+ &scmdev_attr_group,
+ NULL,
+};
+
+static void scmdev_release(struct device *dev)
+{
+ struct scm_device *scmdev = to_scm_dev(dev);
+
+ kfree(scmdev);
+}
+
+static void scmdev_setup(struct scm_device *scmdev, struct sale *sale,
+ unsigned int size, unsigned int max_blk_count)
+{
+ dev_set_name(&scmdev->dev, "%016llx", (unsigned long long) sale->sa);
+ scmdev->nr_max_block = max_blk_count;
+ scmdev->address = sale->sa;
+ scmdev->size = 1UL << size;
+ scmdev->attrs.rank = sale->rank;
+ scmdev->attrs.persistence = sale->p;
+ scmdev->attrs.oper_state = sale->op_state;
+ scmdev->attrs.data_state = sale->data_state;
+ scmdev->attrs.rank = sale->rank;
+ scmdev->attrs.release = sale->r;
+ scmdev->attrs.res_id = sale->rid;
+ scmdev->dev.parent = scm_root;
+ scmdev->dev.bus = &scm_bus_type;
+ scmdev->dev.release = scmdev_release;
+ scmdev->dev.groups = scmdev_attr_groups;
+}
+
+/*
+ * Check for state-changes, notify the driver and userspace.
+ */
+static void scmdev_update(struct scm_device *scmdev, struct sale *sale)
+{
+ struct scm_driver *scmdrv;
+ bool changed;
+
+ device_lock(&scmdev->dev);
+ changed = scmdev->attrs.rank != sale->rank ||
+ scmdev->attrs.oper_state != sale->op_state;
+ scmdev->attrs.rank = sale->rank;
+ scmdev->attrs.oper_state = sale->op_state;
+ if (!scmdev->dev.driver)
+ goto out;
+ scmdrv = to_scm_drv(scmdev->dev.driver);
+ if (changed && scmdrv->notify)
+ scmdrv->notify(scmdev);
+out:
+ device_unlock(&scmdev->dev);
+ if (changed)
+ kobject_uevent(&scmdev->dev.kobj, KOBJ_CHANGE);
+}
+
+static int check_address(struct device *dev, void *data)
+{
+ struct scm_device *scmdev = to_scm_dev(dev);
+ struct sale *sale = data;
+
+ return scmdev->address == sale->sa;
+}
+
+static struct scm_device *scmdev_find(struct sale *sale)
+{
+ struct device *dev;
+
+ dev = bus_find_device(&scm_bus_type, NULL, sale, check_address);
+
+ return dev ? to_scm_dev(dev) : NULL;
+}
+
+static int scm_add(struct chsc_scm_info *scm_info, size_t num)
+{
+ struct sale *sale, *scmal = scm_info->scmal;
+ struct scm_device *scmdev;
+ int ret;
+
+ for (sale = scmal; sale < scmal + num; sale++) {
+ scmdev = scmdev_find(sale);
+ if (scmdev) {
+ scmdev_update(scmdev, sale);
+ /* Release reference from scm_find(). */
+ put_device(&scmdev->dev);
+ continue;
+ }
+ scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL);
+ if (!scmdev)
+ return -ENODEV;
+ scmdev_setup(scmdev, sale, scm_info->is, scm_info->mbc);
+ ret = device_register(&scmdev->dev);
+ if (ret) {
+ /* Release reference from device_initialize(). */
+ put_device(&scmdev->dev);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int scm_update_information(void)
+{
+ struct chsc_scm_info *scm_info;
+ u64 token = 0;
+ size_t num;
+ int ret;
+
+ scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
+ if (!scm_info)
+ return -ENOMEM;
+
+ do {
+ ret = chsc_scm_info(scm_info, token);
+ if (ret)
+ break;
+
+ num = (scm_info->response.length -
+ (offsetof(struct chsc_scm_info, scmal) -
+ offsetof(struct chsc_scm_info, response))
+ ) / sizeof(struct sale);
+
+ ret = scm_add(scm_info, num);
+ if (ret)
+ break;
+
+ token = scm_info->restok;
+ } while (token);
+
+ free_page((unsigned long)scm_info);
+
+ return ret;
+}
+
+static int __init scm_init(void)
+{
+ int ret;
+
+ ret = bus_register(&scm_bus_type);
+ if (ret)
+ return ret;
+
+ scm_root = root_device_register("scm");
+ if (IS_ERR(scm_root)) {
+ bus_unregister(&scm_bus_type);
+ return PTR_ERR(scm_root);
+ }
+
+ scm_update_information();
+ return 0;
+}
+subsys_initcall_sync(scm_init);
diff --git a/drivers/s390/crypto/Makefile b/drivers/s390/crypto/Makefile
index af3c7f16ea88..771faf7094d6 100644
--- a/drivers/s390/crypto/Makefile
+++ b/drivers/s390/crypto/Makefile
@@ -4,4 +4,5 @@
ap-objs := ap_bus.o
obj-$(CONFIG_ZCRYPT) += ap.o zcrypt_api.o zcrypt_pcicc.o zcrypt_pcixcc.o
-obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o
+obj-$(CONFIG_ZCRYPT) += zcrypt_pcica.o zcrypt_cex2a.o zcrypt_cex4.o
+obj-$(CONFIG_ZCRYPT) += zcrypt_msgtype6.o zcrypt_msgtype50.o
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index ae258a4b4e5e..7b865a7300e6 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -1,5 +1,5 @@
/*
- * Copyright IBM Corp. 2006
+ * Copyright IBM Corp. 2006, 2012
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
@@ -62,13 +62,14 @@ static void ap_interrupt_handler(void *unused1, void *unused2);
static void ap_reset(struct ap_device *ap_dev);
static void ap_config_timeout(unsigned long ptr);
static int ap_select_domain(void);
+static void ap_query_configuration(void);
/*
* Module description.
*/
MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("Adjunct Processor Bus driver, "
- "Copyright IBM Corp. 2006");
+MODULE_DESCRIPTION("Adjunct Processor Bus driver, " \
+ "Copyright IBM Corp. 2006, 2012");
MODULE_LICENSE("GPL");
/*
@@ -84,6 +85,7 @@ module_param_named(poll_thread, ap_thread_flag, int, 0000);
MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off).");
static struct device *ap_root_device = NULL;
+static struct ap_config_info *ap_configuration;
static DEFINE_SPINLOCK(ap_device_list_lock);
static LIST_HEAD(ap_device_list);
@@ -158,6 +160,19 @@ static int ap_interrupts_available(void)
}
/**
+ * ap_configuration_available(): Test if AP configuration
+ * information is available.
+ *
+ * Returns 1 if AP configuration information is available.
+ */
+#ifdef CONFIG_64BIT
+static int ap_configuration_available(void)
+{
+ return test_facility(2) && test_facility(12);
+}
+#endif
+
+/**
* ap_test_queue(): Test adjunct processor queue.
* @qid: The AP queue number
* @queue_depth: Pointer to queue depth value
@@ -242,6 +257,26 @@ __ap_query_functions(ap_qid_t qid, unsigned int *functions)
}
#endif
+#ifdef CONFIG_64BIT
+static inline int __ap_query_configuration(struct ap_config_info *config)
+{
+ register unsigned long reg0 asm ("0") = 0x04000000UL;
+ register unsigned long reg1 asm ("1") = -EINVAL;
+ register unsigned char *reg2 asm ("2") = (unsigned char *)config;
+
+ asm volatile(
+ ".long 0xb2af0000\n" /* PQAP(QCI) */
+ "0: la %1,0\n"
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (reg0), "+d" (reg1), "+d" (reg2)
+ :
+ : "cc");
+
+ return reg1;
+}
+#endif
+
/**
* ap_query_functions(): Query supported functions.
* @qid: The AP queue number
@@ -292,25 +327,6 @@ static int ap_query_functions(ap_qid_t qid, unsigned int *functions)
}
/**
- * ap_4096_commands_availablen(): Check for availability of 4096 bit RSA
- * support.
- * @qid: The AP queue number
- *
- * Returns 1 if 4096 bit RSA keys are support fo the AP, returns 0 if not.
- */
-int ap_4096_commands_available(ap_qid_t qid)
-{
- unsigned int functions;
-
- if (ap_query_functions(qid, &functions))
- return 0;
-
- return test_ap_facility(functions, 1) &&
- test_ap_facility(functions, 2);
-}
-EXPORT_SYMBOL(ap_4096_commands_available);
-
-/**
* ap_queue_enable_interruption(): Enable interruption on an AP.
* @qid: The AP queue number
* @ind: the notification indicator byte
@@ -657,6 +673,34 @@ static ssize_t ap_request_count_show(struct device *dev,
static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL);
+static ssize_t ap_requestq_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ int rc;
+
+ spin_lock_bh(&ap_dev->lock);
+ rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->requestq_count);
+ spin_unlock_bh(&ap_dev->lock);
+ return rc;
+}
+
+static DEVICE_ATTR(requestq_count, 0444, ap_requestq_count_show, NULL);
+
+static ssize_t ap_pendingq_count_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ int rc;
+
+ spin_lock_bh(&ap_dev->lock);
+ rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->pendingq_count);
+ spin_unlock_bh(&ap_dev->lock);
+ return rc;
+}
+
+static DEVICE_ATTR(pendingq_count, 0444, ap_pendingq_count_show, NULL);
+
static ssize_t ap_modalias_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -665,11 +709,23 @@ static ssize_t ap_modalias_show(struct device *dev,
static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL);
+static ssize_t ap_functions_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct ap_device *ap_dev = to_ap_dev(dev);
+ return snprintf(buf, PAGE_SIZE, "0x%08X\n", ap_dev->functions);
+}
+
+static DEVICE_ATTR(ap_functions, 0444, ap_functions_show, NULL);
+
static struct attribute *ap_dev_attrs[] = {
&dev_attr_hwtype.attr,
&dev_attr_depth.attr,
&dev_attr_request_count.attr,
+ &dev_attr_requestq_count.attr,
+ &dev_attr_pendingq_count.attr,
&dev_attr_modalias.attr,
+ &dev_attr_ap_functions.attr,
NULL
};
static struct attribute_group ap_dev_attr_group = {
@@ -772,6 +828,7 @@ static int ap_bus_resume(struct device *dev)
ap_suspend_flag = 0;
if (!ap_interrupts_available())
ap_interrupt_indicator = NULL;
+ ap_query_configuration();
if (!user_set_domain) {
ap_domain_index = -1;
ap_select_domain();
@@ -895,6 +952,20 @@ void ap_driver_unregister(struct ap_driver *ap_drv)
}
EXPORT_SYMBOL(ap_driver_unregister);
+void ap_bus_force_rescan(void)
+{
+ /* Delete the AP bus rescan timer. */
+ del_timer(&ap_config_timer);
+
+ /* processing a synchonuous bus rescan */
+ ap_scan_bus(NULL);
+
+ /* Setup the AP bus rescan timer again. */
+ ap_config_timer.expires = jiffies + ap_config_time * HZ;
+ add_timer(&ap_config_timer);
+}
+EXPORT_SYMBOL(ap_bus_force_rescan);
+
/*
* AP bus attributes.
*/
@@ -997,6 +1068,65 @@ static struct bus_attribute *const ap_bus_attrs[] = {
NULL,
};
+static inline int ap_test_config(unsigned int *field, unsigned int nr)
+{
+ if (nr > 0xFFu)
+ return 0;
+ return ap_test_bit((field + (nr >> 5)), (nr & 0x1f));
+}
+
+/*
+ * ap_test_config_card_id(): Test, whether an AP card ID is configured.
+ * @id AP card ID
+ *
+ * Returns 0 if the card is not configured
+ * 1 if the card is configured or
+ * if the configuration information is not available
+ */
+static inline int ap_test_config_card_id(unsigned int id)
+{
+ if (!ap_configuration)
+ return 1;
+ return ap_test_config(ap_configuration->apm, id);
+}
+
+/*
+ * ap_test_config_domain(): Test, whether an AP usage domain is configured.
+ * @domain AP usage domain ID
+ *
+ * Returns 0 if the usage domain is not configured
+ * 1 if the usage domain is configured or
+ * if the configuration information is not available
+ */
+static inline int ap_test_config_domain(unsigned int domain)
+{
+ if (!ap_configuration)
+ return 1;
+ return ap_test_config(ap_configuration->aqm, domain);
+}
+
+/**
+ * ap_query_configuration(): Query AP configuration information.
+ *
+ * Query information of installed cards and configured domains from AP.
+ */
+static void ap_query_configuration(void)
+{
+#ifdef CONFIG_64BIT
+ if (ap_configuration_available()) {
+ if (!ap_configuration)
+ ap_configuration =
+ kzalloc(sizeof(struct ap_config_info),
+ GFP_KERNEL);
+ if (ap_configuration)
+ __ap_query_configuration(ap_configuration);
+ } else
+ ap_configuration = NULL;
+#else
+ ap_configuration = NULL;
+#endif
+}
+
/**
* ap_select_domain(): Select an AP domain.
*
@@ -1005,6 +1135,7 @@ static struct bus_attribute *const ap_bus_attrs[] = {
static int ap_select_domain(void)
{
int queue_depth, device_type, count, max_count, best_domain;
+ ap_qid_t qid;
int rc, i, j;
/*
@@ -1018,9 +1149,13 @@ static int ap_select_domain(void)
best_domain = -1;
max_count = 0;
for (i = 0; i < AP_DOMAINS; i++) {
+ if (!ap_test_config_domain(i))
+ continue;
count = 0;
for (j = 0; j < AP_DEVICES; j++) {
- ap_qid_t qid = AP_MKQID(j, i);
+ if (!ap_test_config_card_id(j))
+ continue;
+ qid = AP_MKQID(j, i);
rc = ap_query_queue(qid, &queue_depth, &device_type);
if (rc)
continue;
@@ -1169,6 +1304,7 @@ static void ap_scan_bus(struct work_struct *unused)
unsigned int device_functions;
int rc, i;
+ ap_query_configuration();
if (ap_select_domain() != 0)
return;
for (i = 0; i < AP_DEVICES; i++) {
@@ -1176,7 +1312,10 @@ static void ap_scan_bus(struct work_struct *unused)
dev = bus_find_device(&ap_bus_type, NULL,
(void *)(unsigned long)qid,
__ap_scan_bus);
- rc = ap_query_queue(qid, &queue_depth, &device_type);
+ if (ap_test_config_card_id(i))
+ rc = ap_query_queue(qid, &queue_depth, &device_type);
+ else
+ rc = -ENODEV;
if (dev) {
if (rc == -EBUSY) {
set_current_state(TASK_UNINTERRUPTIBLE);
@@ -1217,29 +1356,22 @@ static void ap_scan_bus(struct work_struct *unused)
(unsigned long) ap_dev);
switch (device_type) {
case 0:
+ /* device type probing for old cards */
if (ap_probe_device_type(ap_dev)) {
kfree(ap_dev);
continue;
}
break;
- case 10:
- if (ap_query_functions(qid, &device_functions)) {
- kfree(ap_dev);
- continue;
- }
- if (test_ap_facility(device_functions, 3))
- ap_dev->device_type = AP_DEVICE_TYPE_CEX3C;
- else if (test_ap_facility(device_functions, 4))
- ap_dev->device_type = AP_DEVICE_TYPE_CEX3A;
- else {
- kfree(ap_dev);
- continue;
- }
- break;
default:
ap_dev->device_type = device_type;
}
+ rc = ap_query_functions(qid, &device_functions);
+ if (!rc)
+ ap_dev->functions = device_functions;
+ else
+ ap_dev->functions = 0u;
+
ap_dev->device.bus = &ap_bus_type;
ap_dev->device.parent = ap_root_device;
if (dev_set_name(&ap_dev->device, "card%02x",
@@ -1785,6 +1917,7 @@ int __init ap_module_init(void)
goto out_root;
}
+ ap_query_configuration();
if (ap_select_domain() == 0)
ap_scan_bus(NULL);
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index 52d61995af88..685f6cc022f9 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -1,5 +1,5 @@
/*
- * Copyright IBM Corp. 2006
+ * Copyright IBM Corp. 2006, 2012
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
@@ -83,13 +83,12 @@ int ap_queue_status_invalid_test(struct ap_queue_status *status)
return !(memcmp(status, &invalid, sizeof(struct ap_queue_status)));
}
-#define MAX_AP_FACILITY 31
-
-static inline int test_ap_facility(unsigned int function, unsigned int nr)
+#define AP_MAX_BITS 31
+static inline int ap_test_bit(unsigned int *ptr, unsigned int nr)
{
- if (nr > MAX_AP_FACILITY)
+ if (nr > AP_MAX_BITS)
return 0;
- return function & (unsigned int)(0x80000000 >> nr);
+ return (*ptr & (0x80000000u >> nr)) != 0;
}
#define AP_RESPONSE_NORMAL 0x00
@@ -117,6 +116,15 @@ static inline int test_ap_facility(unsigned int function, unsigned int nr)
#define AP_DEVICE_TYPE_CEX2C 7
#define AP_DEVICE_TYPE_CEX3A 8
#define AP_DEVICE_TYPE_CEX3C 9
+#define AP_DEVICE_TYPE_CEX4 10
+
+/*
+ * Known function facilities
+ */
+#define AP_FUNC_MEX4K 1
+#define AP_FUNC_CRT4K 2
+#define AP_FUNC_COPRO 3
+#define AP_FUNC_ACCEL 4
/*
* AP reset flag states
@@ -151,6 +159,7 @@ struct ap_device {
ap_qid_t qid; /* AP queue id. */
int queue_depth; /* AP queue depth.*/
int device_type; /* AP device type. */
+ unsigned int functions; /* AP device function bitfield. */
int unregistered; /* marks AP device as unregistered */
struct timer_list timeout; /* Timer for request timeouts. */
int reset; /* Reset required after req. timeout. */
@@ -183,6 +192,17 @@ struct ap_message {
struct ap_message *);
};
+struct ap_config_info {
+ unsigned int special_command:1;
+ unsigned int ap_extended:1;
+ unsigned char reserved1:6;
+ unsigned char reserved2[15];
+ unsigned int apm[8]; /* AP ID mask */
+ unsigned int aqm[8]; /* AP queue mask */
+ unsigned int adm[8]; /* AP domain mask */
+ unsigned char reserved4[16];
+} __packed;
+
#define AP_DEVICE(dt) \
.dev_type=(dt), \
.match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE,
@@ -211,10 +231,9 @@ int ap_recv(ap_qid_t, unsigned long long *, void *, size_t);
void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg);
void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg);
void ap_flush_queue(struct ap_device *ap_dev);
+void ap_bus_force_rescan(void);
int ap_module_init(void);
void ap_module_exit(void);
-int ap_4096_commands_available(ap_qid_t qid);
-
#endif /* _AP_BUS_H_ */
diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c
index 2f94132246a1..31cfaa556072 100644
--- a/drivers/s390/crypto/zcrypt_api.c
+++ b/drivers/s390/crypto/zcrypt_api.c
@@ -1,7 +1,7 @@
/*
* zcrypt 2.1.0
*
- * Copyright IBM Corp. 2001, 2006
+ * Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
* Cornelia Huck <cornelia.huck@de.ibm.com>
@@ -9,6 +9,7 @@
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -37,25 +38,39 @@
#include <linux/atomic.h>
#include <asm/uaccess.h>
#include <linux/hw_random.h>
+#include <linux/debugfs.h>
+#include <asm/debug.h>
+#include "zcrypt_debug.h"
#include "zcrypt_api.h"
/*
* Module description.
*/
MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("Cryptographic Coprocessor interface, "
- "Copyright IBM Corp. 2001, 2006");
+MODULE_DESCRIPTION("Cryptographic Coprocessor interface, " \
+ "Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
static DEFINE_SPINLOCK(zcrypt_device_lock);
static LIST_HEAD(zcrypt_device_list);
static int zcrypt_device_count = 0;
static atomic_t zcrypt_open_count = ATOMIC_INIT(0);
+static atomic_t zcrypt_rescan_count = ATOMIC_INIT(0);
+
+atomic_t zcrypt_rescan_req = ATOMIC_INIT(0);
+EXPORT_SYMBOL(zcrypt_rescan_req);
static int zcrypt_rng_device_add(void);
static void zcrypt_rng_device_remove(void);
+static DEFINE_SPINLOCK(zcrypt_ops_list_lock);
+static LIST_HEAD(zcrypt_ops_list);
+
+static debug_info_t *zcrypt_dbf_common;
+static debug_info_t *zcrypt_dbf_devices;
+static struct dentry *debugfs_root;
+
/*
* Device attributes common for all crypto devices.
*/
@@ -85,6 +100,8 @@ static ssize_t zcrypt_online_store(struct device *dev,
if (sscanf(buf, "%d\n", &online) != 1 || online < 0 || online > 1)
return -EINVAL;
zdev->online = online;
+ ZCRYPT_DBF_DEV(DBF_INFO, zdev, "dev%04xo%dman", zdev->ap_dev->qid,
+ zdev->online);
if (!online)
ap_flush_queue(zdev->ap_dev);
return count;
@@ -103,6 +120,24 @@ static struct attribute_group zcrypt_device_attr_group = {
};
/**
+ * Process a rescan of the transport layer.
+ *
+ * Returns 1, if the rescan has been processed, otherwise 0.
+ */
+static inline int zcrypt_process_rescan(void)
+{
+ if (atomic_read(&zcrypt_rescan_req)) {
+ atomic_set(&zcrypt_rescan_req, 0);
+ atomic_inc(&zcrypt_rescan_count);
+ ap_bus_force_rescan();
+ ZCRYPT_DBF_COMMON(DBF_INFO, "rescan%07d",
+ atomic_inc_return(&zcrypt_rescan_count));
+ return 1;
+ }
+ return 0;
+}
+
+/**
* __zcrypt_increase_preference(): Increase preference of a crypto device.
* @zdev: Pointer the crypto device
*
@@ -190,6 +225,7 @@ struct zcrypt_device *zcrypt_device_alloc(size_t max_response_size)
zdev->reply.length = max_response_size;
spin_lock_init(&zdev->lock);
INIT_LIST_HEAD(&zdev->list);
+ zdev->dbf_area = zcrypt_dbf_devices;
return zdev;
out_free:
@@ -215,6 +251,8 @@ int zcrypt_device_register(struct zcrypt_device *zdev)
{
int rc;
+ if (!zdev->ops)
+ return -ENODEV;
rc = sysfs_create_group(&zdev->ap_dev->device.kobj,
&zcrypt_device_attr_group);
if (rc)
@@ -223,6 +261,8 @@ int zcrypt_device_register(struct zcrypt_device *zdev)
kref_init(&zdev->refcount);
spin_lock_bh(&zcrypt_device_lock);
zdev->online = 1; /* New devices are online by default. */
+ ZCRYPT_DBF_DEV(DBF_INFO, zdev, "dev%04xo%dreg", zdev->ap_dev->qid,
+ zdev->online);
list_add_tail(&zdev->list, &zcrypt_device_list);
__zcrypt_increase_preference(zdev);
zcrypt_device_count++;
@@ -269,6 +309,67 @@ void zcrypt_device_unregister(struct zcrypt_device *zdev)
}
EXPORT_SYMBOL(zcrypt_device_unregister);
+void zcrypt_msgtype_register(struct zcrypt_ops *zops)
+{
+ if (zops->owner) {
+ spin_lock_bh(&zcrypt_ops_list_lock);
+ list_add_tail(&zops->list, &zcrypt_ops_list);
+ spin_unlock_bh(&zcrypt_ops_list_lock);
+ }
+}
+EXPORT_SYMBOL(zcrypt_msgtype_register);
+
+void zcrypt_msgtype_unregister(struct zcrypt_ops *zops)
+{
+ spin_lock_bh(&zcrypt_ops_list_lock);
+ list_del_init(&zops->list);
+ spin_unlock_bh(&zcrypt_ops_list_lock);
+}
+EXPORT_SYMBOL(zcrypt_msgtype_unregister);
+
+static inline
+struct zcrypt_ops *__ops_lookup(unsigned char *name, int variant)
+{
+ struct zcrypt_ops *zops;
+ int found = 0;
+
+ spin_lock_bh(&zcrypt_ops_list_lock);
+ list_for_each_entry(zops, &zcrypt_ops_list, list) {
+ if ((zops->variant == variant) &&
+ (!strncmp(zops->owner->name, name, MODULE_NAME_LEN))) {
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_bh(&zcrypt_ops_list_lock);
+
+ if (!found)
+ return NULL;
+ return zops;
+}
+
+struct zcrypt_ops *zcrypt_msgtype_request(unsigned char *name, int variant)
+{
+ struct zcrypt_ops *zops = NULL;
+
+ zops = __ops_lookup(name, variant);
+ if (!zops) {
+ request_module(name);
+ zops = __ops_lookup(name, variant);
+ }
+ if ((!zops) || (!try_module_get(zops->owner)))
+ return NULL;
+ return zops;
+}
+EXPORT_SYMBOL(zcrypt_msgtype_request);
+
+void zcrypt_msgtype_release(struct zcrypt_ops *zops)
+{
+ if (zops)
+ module_put(zops->owner);
+}
+EXPORT_SYMBOL(zcrypt_msgtype_release);
+
/**
* zcrypt_read (): Not supported beyond zcrypt 1.3.1.
*
@@ -640,6 +741,11 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_rsa_modexpo(&mex);
} while (rc == -EAGAIN);
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_rsa_modexpo(&mex);
+ } while (rc == -EAGAIN);
if (rc)
return rc;
return put_user(mex.outputdatalength, &umex->outputdatalength);
@@ -652,6 +758,11 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_rsa_crt(&crt);
} while (rc == -EAGAIN);
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_rsa_crt(&crt);
+ } while (rc == -EAGAIN);
if (rc)
return rc;
return put_user(crt.outputdatalength, &ucrt->outputdatalength);
@@ -664,6 +775,11 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_send_cprb(&xcRB);
} while (rc == -EAGAIN);
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_send_cprb(&xcRB);
+ } while (rc == -EAGAIN);
if (copy_to_user(uxcRB, &xcRB, sizeof(xcRB)))
return -EFAULT;
return rc;
@@ -770,10 +886,15 @@ static long trans_modexpo32(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_rsa_modexpo(&mex64);
} while (rc == -EAGAIN);
- if (!rc)
- rc = put_user(mex64.outputdatalength,
- &umex32->outputdatalength);
- return rc;
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_rsa_modexpo(&mex64);
+ } while (rc == -EAGAIN);
+ if (rc)
+ return rc;
+ return put_user(mex64.outputdatalength,
+ &umex32->outputdatalength);
}
struct compat_ica_rsa_modexpo_crt {
@@ -810,10 +931,15 @@ static long trans_modexpo_crt32(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_rsa_crt(&crt64);
} while (rc == -EAGAIN);
- if (!rc)
- rc = put_user(crt64.outputdatalength,
- &ucrt32->outputdatalength);
- return rc;
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_rsa_crt(&crt64);
+ } while (rc == -EAGAIN);
+ if (rc)
+ return rc;
+ return put_user(crt64.outputdatalength,
+ &ucrt32->outputdatalength);
}
struct compat_ica_xcRB {
@@ -869,6 +995,11 @@ static long trans_xcRB32(struct file *filp, unsigned int cmd,
do {
rc = zcrypt_send_cprb(&xcRB64);
} while (rc == -EAGAIN);
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ do {
+ rc = zcrypt_send_cprb(&xcRB64);
+ } while (rc == -EAGAIN);
xcRB32.reply_control_blk_length = xcRB64.reply_control_blk_length;
xcRB32.reply_data_length = xcRB64.reply_data_length;
xcRB32.status = xcRB64.status;
@@ -1126,6 +1257,9 @@ static int zcrypt_rng_data_read(struct hwrng *rng, u32 *data)
*/
if (zcrypt_rng_buffer_index == 0) {
rc = zcrypt_rng((char *) zcrypt_rng_buffer);
+ /* on failure: retry once again after a requested rescan */
+ if ((rc == -ENODEV) && (zcrypt_process_rescan()))
+ rc = zcrypt_rng((char *) zcrypt_rng_buffer);
if (rc < 0)
return -EIO;
zcrypt_rng_buffer_index = rc / sizeof *data;
@@ -1178,6 +1312,30 @@ static void zcrypt_rng_device_remove(void)
mutex_unlock(&zcrypt_rng_mutex);
}
+int __init zcrypt_debug_init(void)
+{
+ debugfs_root = debugfs_create_dir("zcrypt", NULL);
+
+ zcrypt_dbf_common = debug_register("zcrypt_common", 1, 1, 16);
+ debug_register_view(zcrypt_dbf_common, &debug_hex_ascii_view);
+ debug_set_level(zcrypt_dbf_common, DBF_ERR);
+
+ zcrypt_dbf_devices = debug_register("zcrypt_devices", 1, 1, 16);
+ debug_register_view(zcrypt_dbf_devices, &debug_hex_ascii_view);
+ debug_set_level(zcrypt_dbf_devices, DBF_ERR);
+
+ return 0;
+}
+
+void zcrypt_debug_exit(void)
+{
+ debugfs_remove(debugfs_root);
+ if (zcrypt_dbf_common)
+ debug_unregister(zcrypt_dbf_common);
+ if (zcrypt_dbf_devices)
+ debug_unregister(zcrypt_dbf_devices);
+}
+
/**
* zcrypt_api_init(): Module initialization.
*
@@ -1187,6 +1345,12 @@ int __init zcrypt_api_init(void)
{
int rc;
+ rc = zcrypt_debug_init();
+ if (rc)
+ goto out;
+
+ atomic_set(&zcrypt_rescan_req, 0);
+
/* Register the request sprayer. */
rc = misc_register(&zcrypt_misc_device);
if (rc < 0)
@@ -1216,6 +1380,7 @@ void zcrypt_api_exit(void)
{
remove_proc_entry("driver/z90crypt", NULL);
misc_deregister(&zcrypt_misc_device);
+ zcrypt_debug_exit();
}
module_init(zcrypt_api_init);
diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h
index 7a32c4bc8ef9..89632919c993 100644
--- a/drivers/s390/crypto/zcrypt_api.h
+++ b/drivers/s390/crypto/zcrypt_api.h
@@ -1,7 +1,7 @@
/*
* zcrypt 2.1.0
*
- * Copyright IBM Corp. 2001, 2006
+ * Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
* Cornelia Huck <cornelia.huck@de.ibm.com>
@@ -9,6 +9,7 @@
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,8 +29,10 @@
#ifndef _ZCRYPT_API_H_
#define _ZCRYPT_API_H_
-#include "ap_bus.h"
+#include <linux/atomic.h>
+#include <asm/debug.h>
#include <asm/zcrypt.h>
+#include "ap_bus.h"
/* deprecated status calls */
#define ICAZ90STATUS _IOR(ZCRYPT_IOCTL_MAGIC, 0x10, struct ica_z90_status)
@@ -87,6 +90,9 @@ struct zcrypt_ops {
struct ica_rsa_modexpo_crt *);
long (*send_cprb)(struct zcrypt_device *, struct ica_xcRB *);
long (*rng)(struct zcrypt_device *, char *);
+ struct list_head list; /* zcrypt ops list. */
+ struct module *owner;
+ int variant;
};
struct zcrypt_device {
@@ -108,14 +114,23 @@ struct zcrypt_device {
struct ap_message reply; /* Per-device reply structure. */
int max_exp_bit_length;
+
+ debug_info_t *dbf_area; /* debugging */
};
+/* transport layer rescanning */
+extern atomic_t zcrypt_rescan_req;
+
struct zcrypt_device *zcrypt_device_alloc(size_t);
void zcrypt_device_free(struct zcrypt_device *);
void zcrypt_device_get(struct zcrypt_device *);
int zcrypt_device_put(struct zcrypt_device *);
int zcrypt_device_register(struct zcrypt_device *);
void zcrypt_device_unregister(struct zcrypt_device *);
+void zcrypt_msgtype_register(struct zcrypt_ops *);
+void zcrypt_msgtype_unregister(struct zcrypt_ops *);
+struct zcrypt_ops *zcrypt_msgtype_request(unsigned char *, int);
+void zcrypt_msgtype_release(struct zcrypt_ops *);
int zcrypt_api_init(void);
void zcrypt_api_exit(void);
diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c
index 744c668f586c..1e849d6e1dfe 100644
--- a/drivers/s390/crypto/zcrypt_cex2a.c
+++ b/drivers/s390/crypto/zcrypt_cex2a.c
@@ -1,13 +1,14 @@
/*
* zcrypt 2.1.0
*
- * Copyright IBM Corp. 2001, 2006
+ * Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,6 +36,7 @@
#include "zcrypt_api.h"
#include "zcrypt_error.h"
#include "zcrypt_cex2a.h"
+#include "zcrypt_msgtype50.h"
#define CEX2A_MIN_MOD_SIZE 1 /* 8 bits */
#define CEX2A_MAX_MOD_SIZE 256 /* 2048 bits */
@@ -63,14 +65,12 @@ static struct ap_device_id zcrypt_cex2a_ids[] = {
MODULE_DEVICE_TABLE(ap, zcrypt_cex2a_ids);
MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("CEX2A Cryptographic Coprocessor device driver, "
- "Copyright IBM Corp. 2001, 2006");
+MODULE_DESCRIPTION("CEX2A Cryptographic Coprocessor device driver, " \
+ "Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
static int zcrypt_cex2a_probe(struct ap_device *ap_dev);
static void zcrypt_cex2a_remove(struct ap_device *ap_dev);
-static void zcrypt_cex2a_receive(struct ap_device *, struct ap_message *,
- struct ap_message *);
static struct ap_driver zcrypt_cex2a_driver = {
.probe = zcrypt_cex2a_probe,
@@ -80,344 +80,6 @@ static struct ap_driver zcrypt_cex2a_driver = {
};
/**
- * Convert a ICAMEX message to a type50 MEX message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @mex: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo *mex)
-{
- unsigned char *mod, *exp, *inp;
- int mod_len;
-
- mod_len = mex->inputdatalength;
-
- if (mod_len <= 128) {
- struct type50_meb1_msg *meb1 = ap_msg->message;
- memset(meb1, 0, sizeof(*meb1));
- ap_msg->length = sizeof(*meb1);
- meb1->header.msg_type_code = TYPE50_TYPE_CODE;
- meb1->header.msg_len = sizeof(*meb1);
- meb1->keyblock_type = TYPE50_MEB1_FMT;
- mod = meb1->modulus + sizeof(meb1->modulus) - mod_len;
- exp = meb1->exponent + sizeof(meb1->exponent) - mod_len;
- inp = meb1->message + sizeof(meb1->message) - mod_len;
- } else if (mod_len <= 256) {
- struct type50_meb2_msg *meb2 = ap_msg->message;
- memset(meb2, 0, sizeof(*meb2));
- ap_msg->length = sizeof(*meb2);
- meb2->header.msg_type_code = TYPE50_TYPE_CODE;
- meb2->header.msg_len = sizeof(*meb2);
- meb2->keyblock_type = TYPE50_MEB2_FMT;
- mod = meb2->modulus + sizeof(meb2->modulus) - mod_len;
- exp = meb2->exponent + sizeof(meb2->exponent) - mod_len;
- inp = meb2->message + sizeof(meb2->message) - mod_len;
- } else {
- /* mod_len > 256 = 4096 bit RSA Key */
- struct type50_meb3_msg *meb3 = ap_msg->message;
- memset(meb3, 0, sizeof(*meb3));
- ap_msg->length = sizeof(*meb3);
- meb3->header.msg_type_code = TYPE50_TYPE_CODE;
- meb3->header.msg_len = sizeof(*meb3);
- meb3->keyblock_type = TYPE50_MEB3_FMT;
- mod = meb3->modulus + sizeof(meb3->modulus) - mod_len;
- exp = meb3->exponent + sizeof(meb3->exponent) - mod_len;
- inp = meb3->message + sizeof(meb3->message) - mod_len;
- }
-
- if (copy_from_user(mod, mex->n_modulus, mod_len) ||
- copy_from_user(exp, mex->b_key, mod_len) ||
- copy_from_user(inp, mex->inputdata, mod_len))
- return -EFAULT;
- return 0;
-}
-
-/**
- * Convert a ICACRT message to a type50 CRT message.
- *
- * @zdev: crypto device pointer
- * @zreq: crypto request pointer
- * @crt: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo_crt *crt)
-{
- int mod_len, short_len, long_len, long_offset, limit;
- unsigned char *p, *q, *dp, *dq, *u, *inp;
-
- mod_len = crt->inputdatalength;
- short_len = mod_len / 2;
- long_len = mod_len / 2 + 8;
-
- /*
- * CEX2A cannot handle p, dp, or U > 128 bytes.
- * If we have one of these, we need to do extra checking.
- * For CEX3A the limit is 256 bytes.
- */
- if (zdev->max_mod_size == CEX3A_MAX_MOD_SIZE)
- limit = 256;
- else
- limit = 128;
-
- if (long_len > limit) {
- /*
- * zcrypt_rsa_crt already checked for the leading
- * zeroes of np_prime, bp_key and u_mult_inc.
- */
- long_offset = long_len - limit;
- long_len = limit;
- } else
- long_offset = 0;
-
- /*
- * Instead of doing extra work for p, dp, U > 64 bytes, we'll just use
- * the larger message structure.
- */
- if (long_len <= 64) {
- struct type50_crb1_msg *crb1 = ap_msg->message;
- memset(crb1, 0, sizeof(*crb1));
- ap_msg->length = sizeof(*crb1);
- crb1->header.msg_type_code = TYPE50_TYPE_CODE;
- crb1->header.msg_len = sizeof(*crb1);
- crb1->keyblock_type = TYPE50_CRB1_FMT;
- p = crb1->p + sizeof(crb1->p) - long_len;
- q = crb1->q + sizeof(crb1->q) - short_len;
- dp = crb1->dp + sizeof(crb1->dp) - long_len;
- dq = crb1->dq + sizeof(crb1->dq) - short_len;
- u = crb1->u + sizeof(crb1->u) - long_len;
- inp = crb1->message + sizeof(crb1->message) - mod_len;
- } else if (long_len <= 128) {
- struct type50_crb2_msg *crb2 = ap_msg->message;
- memset(crb2, 0, sizeof(*crb2));
- ap_msg->length = sizeof(*crb2);
- crb2->header.msg_type_code = TYPE50_TYPE_CODE;
- crb2->header.msg_len = sizeof(*crb2);
- crb2->keyblock_type = TYPE50_CRB2_FMT;
- p = crb2->p + sizeof(crb2->p) - long_len;
- q = crb2->q + sizeof(crb2->q) - short_len;
- dp = crb2->dp + sizeof(crb2->dp) - long_len;
- dq = crb2->dq + sizeof(crb2->dq) - short_len;
- u = crb2->u + sizeof(crb2->u) - long_len;
- inp = crb2->message + sizeof(crb2->message) - mod_len;
- } else {
- /* long_len >= 256 */
- struct type50_crb3_msg *crb3 = ap_msg->message;
- memset(crb3, 0, sizeof(*crb3));
- ap_msg->length = sizeof(*crb3);
- crb3->header.msg_type_code = TYPE50_TYPE_CODE;
- crb3->header.msg_len = sizeof(*crb3);
- crb3->keyblock_type = TYPE50_CRB3_FMT;
- p = crb3->p + sizeof(crb3->p) - long_len;
- q = crb3->q + sizeof(crb3->q) - short_len;
- dp = crb3->dp + sizeof(crb3->dp) - long_len;
- dq = crb3->dq + sizeof(crb3->dq) - short_len;
- u = crb3->u + sizeof(crb3->u) - long_len;
- inp = crb3->message + sizeof(crb3->message) - mod_len;
- }
-
- if (copy_from_user(p, crt->np_prime + long_offset, long_len) ||
- copy_from_user(q, crt->nq_prime, short_len) ||
- copy_from_user(dp, crt->bp_key + long_offset, long_len) ||
- copy_from_user(dq, crt->bq_key, short_len) ||
- copy_from_user(u, crt->u_mult_inv + long_offset, long_len) ||
- copy_from_user(inp, crt->inputdata, mod_len))
- return -EFAULT;
-
- return 0;
-}
-
-/**
- * Copy results from a type 80 reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @data: pointer to user output data
- * @length: size of user output data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int convert_type80(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- struct type80_hdr *t80h = reply->message;
- unsigned char *data;
-
- if (t80h->len < sizeof(*t80h) + outputdatalength) {
- /* The result is too short, the CEX2A card may not do that.. */
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
- if (zdev->user_space_type == ZCRYPT_CEX2A)
- BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE);
- else
- BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE);
- data = reply->message + t80h->len - outputdatalength;
- if (copy_to_user(outputdata, data, outputdatalength))
- return -EFAULT;
- return 0;
-}
-
-static int convert_response(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- /* Response type byte is the second byte in the response. */
- switch (((unsigned char *) reply->message)[1]) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- return convert_error(zdev, reply);
- case TYPE80_RSP_CODE:
- return convert_type80(zdev, reply,
- outputdata, outputdatalength);
- default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
-}
-
-/**
- * This function is called from the AP bus code after a crypto request
- * "msg" has finished with the reply message "reply".
- * It is called from tasklet context.
- * @ap_dev: pointer to the AP device
- * @msg: pointer to the AP message
- * @reply: pointer to the AP reply message
- */
-static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
- struct ap_message *msg,
- struct ap_message *reply)
-{
- static struct error_hdr error_reply = {
- .type = TYPE82_RSP_CODE,
- .reply_code = REP82_ERROR_MACHINE_FAILURE,
- };
- struct type80_hdr *t80h;
- int length;
-
- /* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
- t80h = reply->message;
- if (t80h->type == TYPE80_RSP_CODE) {
- if (ap_dev->device_type == AP_DEVICE_TYPE_CEX2A)
- length = min(CEX2A_MAX_RESPONSE_SIZE, (int) t80h->len);
- else
- length = min(CEX3A_MAX_RESPONSE_SIZE, (int) t80h->len);
- memcpy(msg->message, reply->message, length);
- } else
- memcpy(msg->message, reply->message, sizeof error_reply);
-out:
- complete((struct completion *) msg->private);
-}
-
-static atomic_t zcrypt_step = ATOMIC_INIT(0);
-
-/**
- * The request distributor calls this function if it picked the CEX2A
- * device to handle a modexpo request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * CEX2A device to the request distributor
- * @mex: pointer to the modexpo request buffer
- */
-static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo *mex)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- if (zdev->user_space_type == ZCRYPT_CEX2A)
- ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
- else
- ap_msg.message = kmalloc(CEX3A_MAX_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_cex2a_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICAMEX_msg_to_type50MEX_msg(zdev, &ap_msg, mex);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, mex->outputdata,
- mex->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- kfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the CEX2A
- * device to handle a modexpo_crt request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * CEX2A device to the request distributor
- * @crt: pointer to the modexpoc_crt request buffer
- */
-static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo_crt *crt)
-{
- struct ap_message ap_msg;
- struct completion work;
- int rc;
-
- ap_init_message(&ap_msg);
- if (zdev->user_space_type == ZCRYPT_CEX2A)
- ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
- else
- ap_msg.message = kmalloc(CEX3A_MAX_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_cex2a_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &work;
- rc = ICACRT_msg_to_type50CRT_msg(zdev, &ap_msg, crt);
- if (rc)
- goto out_free;
- init_completion(&work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&work);
- if (rc == 0)
- rc = convert_response(zdev, &ap_msg, crt->outputdata,
- crt->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- kfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The crypto operations for a CEX2A card.
- */
-static struct zcrypt_ops zcrypt_cex2a_ops = {
- .rsa_modexpo = zcrypt_cex2a_modexpo,
- .rsa_modexpo_crt = zcrypt_cex2a_modexpo_crt,
-};
-
-/**
* Probe function for CEX2A cards. It always accepts the AP device
* since the bus_match already checked the hardware type.
* @ap_dev: pointer to the AP device.
@@ -449,7 +111,8 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
zdev->min_mod_size = CEX2A_MIN_MOD_SIZE;
zdev->max_mod_size = CEX2A_MAX_MOD_SIZE;
zdev->max_exp_bit_length = CEX2A_MAX_MOD_SIZE;
- if (ap_4096_commands_available(ap_dev->qid)) {
+ if (ap_test_bit(&ap_dev->functions, AP_FUNC_MEX4K) &&
+ ap_test_bit(&ap_dev->functions, AP_FUNC_CRT4K)) {
zdev->max_mod_size = CEX3A_MAX_MOD_SIZE;
zdev->max_exp_bit_length = CEX3A_MAX_MOD_SIZE;
}
@@ -457,16 +120,18 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
zdev->speed_rating = CEX3A_SPEED_RATING;
break;
}
- if (zdev != NULL) {
- zdev->ap_dev = ap_dev;
- zdev->ops = &zcrypt_cex2a_ops;
- zdev->online = 1;
- ap_dev->reply = &zdev->reply;
- ap_dev->private = zdev;
- rc = zcrypt_device_register(zdev);
- }
+ if (!zdev)
+ return -ENODEV;
+ zdev->ops = zcrypt_msgtype_request(MSGTYPE50_NAME,
+ MSGTYPE50_VARIANT_DEFAULT);
+ zdev->ap_dev = ap_dev;
+ zdev->online = 1;
+ ap_dev->reply = &zdev->reply;
+ ap_dev->private = zdev;
+ rc = zcrypt_device_register(zdev);
if (rc) {
ap_dev->private = NULL;
+ zcrypt_msgtype_release(zdev->ops);
zcrypt_device_free(zdev);
}
return rc;
@@ -479,8 +144,10 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
static void zcrypt_cex2a_remove(struct ap_device *ap_dev)
{
struct zcrypt_device *zdev = ap_dev->private;
+ struct zcrypt_ops *zops = zdev->ops;
zcrypt_device_unregister(zdev);
+ zcrypt_msgtype_release(zops);
}
int __init zcrypt_cex2a_init(void)
diff --git a/drivers/s390/crypto/zcrypt_cex4.c b/drivers/s390/crypto/zcrypt_cex4.c
new file mode 100644
index 000000000000..ce1226398ac9
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_cex4.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright IBM Corp. 2012
+ * Author(s): Holger Dengler <hd@linux.vnet.ibm.com>
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_msgtype6.h"
+#include "zcrypt_msgtype50.h"
+#include "zcrypt_error.h"
+#include "zcrypt_cex4.h"
+
+#define CEX4A_MIN_MOD_SIZE 1 /* 8 bits */
+#define CEX4A_MAX_MOD_SIZE_2K 256 /* 2048 bits */
+#define CEX4A_MAX_MOD_SIZE_4K 512 /* 4096 bits */
+
+#define CEX4C_MIN_MOD_SIZE 16 /* 256 bits */
+#define CEX4C_MAX_MOD_SIZE 512 /* 4096 bits */
+
+#define CEX4A_SPEED_RATING 900 /* TODO new card, new speed rating */
+#define CEX4C_SPEED_RATING 6500 /* TODO new card, new speed rating */
+
+#define CEX4A_MAX_MESSAGE_SIZE MSGTYPE50_CRB3_MAX_MSG_SIZE
+#define CEX4C_MAX_MESSAGE_SIZE MSGTYPE06_MAX_MSG_SIZE
+
+#define CEX4_CLEANUP_TIME (15*HZ)
+
+static struct ap_device_id zcrypt_cex4_ids[] = {
+ { AP_DEVICE(AP_DEVICE_TYPE_CEX4) },
+ { /* end of list */ },
+};
+
+MODULE_DEVICE_TABLE(ap, zcrypt_cex4_ids);
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("CEX4 Cryptographic Card device driver, " \
+ "Copyright IBM Corp. 2012");
+MODULE_LICENSE("GPL");
+
+static int zcrypt_cex4_probe(struct ap_device *ap_dev);
+static void zcrypt_cex4_remove(struct ap_device *ap_dev);
+
+static struct ap_driver zcrypt_cex4_driver = {
+ .probe = zcrypt_cex4_probe,
+ .remove = zcrypt_cex4_remove,
+ .ids = zcrypt_cex4_ids,
+ .request_timeout = CEX4_CLEANUP_TIME,
+};
+
+/**
+ * Probe function for CEX4 cards. It always accepts the AP device
+ * since the bus_match already checked the hardware type.
+ * @ap_dev: pointer to the AP device.
+ */
+static int zcrypt_cex4_probe(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev = NULL;
+ int rc = 0;
+
+ switch (ap_dev->device_type) {
+ case AP_DEVICE_TYPE_CEX4:
+ if (ap_test_bit(&ap_dev->functions, AP_FUNC_ACCEL)) {
+ zdev = zcrypt_device_alloc(CEX4A_MAX_MESSAGE_SIZE);
+ if (!zdev)
+ return -ENOMEM;
+ zdev->type_string = "CEX4A";
+ zdev->user_space_type = ZCRYPT_CEX3A;
+ zdev->min_mod_size = CEX4A_MIN_MOD_SIZE;
+ if (ap_test_bit(&ap_dev->functions, AP_FUNC_MEX4K) &&
+ ap_test_bit(&ap_dev->functions, AP_FUNC_CRT4K)) {
+ zdev->max_mod_size =
+ CEX4A_MAX_MOD_SIZE_4K;
+ zdev->max_exp_bit_length =
+ CEX4A_MAX_MOD_SIZE_4K;
+ } else {
+ zdev->max_mod_size =
+ CEX4A_MAX_MOD_SIZE_2K;
+ zdev->max_exp_bit_length =
+ CEX4A_MAX_MOD_SIZE_2K;
+ }
+ zdev->short_crt = 1;
+ zdev->speed_rating = CEX4A_SPEED_RATING;
+ zdev->ops = zcrypt_msgtype_request(MSGTYPE50_NAME,
+ MSGTYPE50_VARIANT_DEFAULT);
+ } else if (ap_test_bit(&ap_dev->functions, AP_FUNC_COPRO)) {
+ zdev = zcrypt_device_alloc(CEX4C_MAX_MESSAGE_SIZE);
+ if (!zdev)
+ return -ENOMEM;
+ zdev->type_string = "CEX4C";
+ zdev->user_space_type = ZCRYPT_CEX3C;
+ zdev->min_mod_size = CEX4C_MIN_MOD_SIZE;
+ zdev->max_mod_size = CEX4C_MAX_MOD_SIZE;
+ zdev->max_exp_bit_length = CEX4C_MAX_MOD_SIZE;
+ zdev->short_crt = 0;
+ zdev->speed_rating = CEX4C_SPEED_RATING;
+ zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
+ MSGTYPE06_VARIANT_DEFAULT);
+ }
+ break;
+ }
+ if (!zdev)
+ return -ENODEV;
+ zdev->ap_dev = ap_dev;
+ zdev->online = 1;
+ ap_dev->reply = &zdev->reply;
+ ap_dev->private = zdev;
+ rc = zcrypt_device_register(zdev);
+ if (rc) {
+ zcrypt_msgtype_release(zdev->ops);
+ ap_dev->private = NULL;
+ zcrypt_device_free(zdev);
+ }
+ return rc;
+}
+
+/**
+ * This is called to remove the extended CEX4 driver information
+ * if an AP device is removed.
+ */
+static void zcrypt_cex4_remove(struct ap_device *ap_dev)
+{
+ struct zcrypt_device *zdev = ap_dev->private;
+ struct zcrypt_ops *zops;
+
+ if (zdev) {
+ zops = zdev->ops;
+ zcrypt_device_unregister(zdev);
+ zcrypt_msgtype_release(zops);
+ }
+}
+
+int __init zcrypt_cex4_init(void)
+{
+ return ap_driver_register(&zcrypt_cex4_driver, THIS_MODULE, "cex4");
+}
+
+void __exit zcrypt_cex4_exit(void)
+{
+ ap_driver_unregister(&zcrypt_cex4_driver);
+}
+
+module_init(zcrypt_cex4_init);
+module_exit(zcrypt_cex4_exit);
diff --git a/drivers/s390/crypto/zcrypt_cex4.h b/drivers/s390/crypto/zcrypt_cex4.h
new file mode 100644
index 000000000000..719571375ccc
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_cex4.h
@@ -0,0 +1,12 @@
+/*
+ * Copyright IBM Corp. 2012
+ * Author(s): Holger Dengler <hd@linux.vnet.ibm.com>
+ */
+
+#ifndef _ZCRYPT_CEX4_H_
+#define _ZCRYPT_CEX4_H_
+
+int zcrypt_cex4_init(void);
+void zcrypt_cex4_exit(void);
+
+#endif /* _ZCRYPT_CEX4_H_ */
diff --git a/drivers/s390/crypto/zcrypt_debug.h b/drivers/s390/crypto/zcrypt_debug.h
new file mode 100644
index 000000000000..841ea72e4a4e
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_debug.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright IBM Corp. 2012
+ * Author(s): Holger Dengler (hd@linux.vnet.ibm.com)
+ */
+#ifndef ZCRYPT_DEBUG_H
+#define ZCRYPT_DEBUG_H
+
+#include <asm/debug.h>
+#include "zcrypt_api.h"
+
+/* that gives us 15 characters in the text event views */
+#define ZCRYPT_DBF_LEN 16
+
+/* sort out low debug levels early to avoid wasted sprints */
+static inline int zcrypt_dbf_passes(debug_info_t *dbf_grp, int level)
+{
+ return (level <= dbf_grp->level);
+}
+
+#define DBF_ERR 3 /* error conditions */
+#define DBF_WARN 4 /* warning conditions */
+#define DBF_INFO 6 /* informational */
+
+#define RC2WARN(rc) ((rc) ? DBF_WARN : DBF_INFO)
+
+#define ZCRYPT_DBF_COMMON(level, text...) \
+ do { \
+ if (zcrypt_dbf_passes(zcrypt_dbf_common, level)) { \
+ char debug_buffer[ZCRYPT_DBF_LEN]; \
+ snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
+ debug_text_event(zcrypt_dbf_common, level, \
+ debug_buffer); \
+ } \
+ } while (0)
+
+#define ZCRYPT_DBF_DEVICES(level, text...) \
+ do { \
+ if (zcrypt_dbf_passes(zcrypt_dbf_devices, level)) { \
+ char debug_buffer[ZCRYPT_DBF_LEN]; \
+ snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
+ debug_text_event(zcrypt_dbf_devices, level, \
+ debug_buffer); \
+ } \
+ } while (0)
+
+#define ZCRYPT_DBF_DEV(level, device, text...) \
+ do { \
+ if (zcrypt_dbf_passes(device->dbf_area, level)) { \
+ char debug_buffer[ZCRYPT_DBF_LEN]; \
+ snprintf(debug_buffer, ZCRYPT_DBF_LEN, text); \
+ debug_text_event(device->dbf_area, level, \
+ debug_buffer); \
+ } \
+ } while (0)
+
+int zcrypt_debug_init(void);
+void zcrypt_debug_exit(void);
+
+#endif /* ZCRYPT_DEBUG_H */
diff --git a/drivers/s390/crypto/zcrypt_error.h b/drivers/s390/crypto/zcrypt_error.h
index 0965e2626d18..0079b6617211 100644
--- a/drivers/s390/crypto/zcrypt_error.h
+++ b/drivers/s390/crypto/zcrypt_error.h
@@ -26,6 +26,8 @@
#ifndef _ZCRYPT_ERROR_H_
#define _ZCRYPT_ERROR_H_
+#include <linux/atomic.h>
+#include "zcrypt_debug.h"
#include "zcrypt_api.h"
/**
@@ -108,16 +110,27 @@ static inline int convert_error(struct zcrypt_device *zdev,
* and then repeat the request.
*/
WARN_ON(1);
+ atomic_set(&zcrypt_rescan_req, 1);
zdev->online = 0;
+ ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
+ zdev->ap_dev->qid,
+ zdev->online, ehdr->reply_code);
return -EAGAIN;
case REP82_ERROR_TRANSPORT_FAIL:
case REP82_ERROR_MACHINE_FAILURE:
// REP88_ERROR_MODULE_FAILURE // '10' CEX2A
/* If a card fails disable it and repeat the request. */
+ atomic_set(&zcrypt_rescan_req, 1);
zdev->online = 0;
+ ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
+ zdev->ap_dev->qid,
+ zdev->online, ehdr->reply_code);
return -EAGAIN;
default:
zdev->online = 0;
+ ZCRYPT_DBF_DEV(DBF_ERR, zdev, "dev%04xo%drc%d",
+ zdev->ap_dev->qid,
+ zdev->online, ehdr->reply_code);
return -EAGAIN; /* repeat the request on a different device. */
}
}
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.c b/drivers/s390/crypto/zcrypt_msgtype50.c
new file mode 100644
index 000000000000..035b6dc31b71
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype50.c
@@ -0,0 +1,531 @@
+/*
+ * zcrypt 2.1.0
+ *
+ * Copyright IBM Corp. 2001, 2012
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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/slab.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_error.h"
+#include "zcrypt_msgtype50.h"
+
+#define CEX3A_MAX_MOD_SIZE 512 /* 4096 bits */
+
+#define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */
+
+#define CEX3A_MAX_RESPONSE_SIZE 0x210 /* 512 bit modulus
+ * (max outputdatalength) +
+ * type80_hdr*/
+
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("Cryptographic Accelerator (message type 50), " \
+ "Copyright IBM Corp. 2001, 2012");
+MODULE_LICENSE("GPL");
+
+static void zcrypt_cex2a_receive(struct ap_device *, struct ap_message *,
+ struct ap_message *);
+
+/**
+ * The type 50 message family is associated with a CEX2A card.
+ *
+ * The four members of the family are described below.
+ *
+ * Note that all unsigned char arrays are right-justified and left-padded
+ * with zeroes.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type50_hdr {
+ unsigned char reserved1;
+ unsigned char msg_type_code; /* 0x50 */
+ unsigned short msg_len;
+ unsigned char reserved2;
+ unsigned char ignored;
+ unsigned short reserved3;
+} __packed;
+
+#define TYPE50_TYPE_CODE 0x50
+
+#define TYPE50_MEB1_FMT 0x0001
+#define TYPE50_MEB2_FMT 0x0002
+#define TYPE50_MEB3_FMT 0x0003
+#define TYPE50_CRB1_FMT 0x0011
+#define TYPE50_CRB2_FMT 0x0012
+#define TYPE50_CRB3_FMT 0x0013
+
+/* Mod-Exp, with a small modulus */
+struct type50_meb1_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0001 */
+ unsigned char reserved[6];
+ unsigned char exponent[128];
+ unsigned char modulus[128];
+ unsigned char message[128];
+} __packed;
+
+/* Mod-Exp, with a large modulus */
+struct type50_meb2_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0002 */
+ unsigned char reserved[6];
+ unsigned char exponent[256];
+ unsigned char modulus[256];
+ unsigned char message[256];
+} __packed;
+
+/* Mod-Exp, with a larger modulus */
+struct type50_meb3_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0003 */
+ unsigned char reserved[6];
+ unsigned char exponent[512];
+ unsigned char modulus[512];
+ unsigned char message[512];
+} __packed;
+
+/* CRT, with a small modulus */
+struct type50_crb1_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0011 */
+ unsigned char reserved[6];
+ unsigned char p[64];
+ unsigned char q[64];
+ unsigned char dp[64];
+ unsigned char dq[64];
+ unsigned char u[64];
+ unsigned char message[128];
+} __packed;
+
+/* CRT, with a large modulus */
+struct type50_crb2_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0012 */
+ unsigned char reserved[6];
+ unsigned char p[128];
+ unsigned char q[128];
+ unsigned char dp[128];
+ unsigned char dq[128];
+ unsigned char u[128];
+ unsigned char message[256];
+} __packed;
+
+/* CRT, with a larger modulus */
+struct type50_crb3_msg {
+ struct type50_hdr header;
+ unsigned short keyblock_type; /* 0x0013 */
+ unsigned char reserved[6];
+ unsigned char p[256];
+ unsigned char q[256];
+ unsigned char dp[256];
+ unsigned char dq[256];
+ unsigned char u[256];
+ unsigned char message[512];
+} __packed;
+
+/**
+ * The type 80 response family is associated with a CEX2A card.
+ *
+ * Note that all unsigned char arrays are right-justified and left-padded
+ * with zeroes.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+
+#define TYPE80_RSP_CODE 0x80
+
+struct type80_hdr {
+ unsigned char reserved1;
+ unsigned char type; /* 0x80 */
+ unsigned short len;
+ unsigned char code; /* 0x00 */
+ unsigned char reserved2[3];
+ unsigned char reserved3[8];
+} __packed;
+
+/**
+ * Convert a ICAMEX message to a type50 MEX message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @mex: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo *mex)
+{
+ unsigned char *mod, *exp, *inp;
+ int mod_len;
+
+ mod_len = mex->inputdatalength;
+
+ if (mod_len <= 128) {
+ struct type50_meb1_msg *meb1 = ap_msg->message;
+ memset(meb1, 0, sizeof(*meb1));
+ ap_msg->length = sizeof(*meb1);
+ meb1->header.msg_type_code = TYPE50_TYPE_CODE;
+ meb1->header.msg_len = sizeof(*meb1);
+ meb1->keyblock_type = TYPE50_MEB1_FMT;
+ mod = meb1->modulus + sizeof(meb1->modulus) - mod_len;
+ exp = meb1->exponent + sizeof(meb1->exponent) - mod_len;
+ inp = meb1->message + sizeof(meb1->message) - mod_len;
+ } else if (mod_len <= 256) {
+ struct type50_meb2_msg *meb2 = ap_msg->message;
+ memset(meb2, 0, sizeof(*meb2));
+ ap_msg->length = sizeof(*meb2);
+ meb2->header.msg_type_code = TYPE50_TYPE_CODE;
+ meb2->header.msg_len = sizeof(*meb2);
+ meb2->keyblock_type = TYPE50_MEB2_FMT;
+ mod = meb2->modulus + sizeof(meb2->modulus) - mod_len;
+ exp = meb2->exponent + sizeof(meb2->exponent) - mod_len;
+ inp = meb2->message + sizeof(meb2->message) - mod_len;
+ } else {
+ /* mod_len > 256 = 4096 bit RSA Key */
+ struct type50_meb3_msg *meb3 = ap_msg->message;
+ memset(meb3, 0, sizeof(*meb3));
+ ap_msg->length = sizeof(*meb3);
+ meb3->header.msg_type_code = TYPE50_TYPE_CODE;
+ meb3->header.msg_len = sizeof(*meb3);
+ meb3->keyblock_type = TYPE50_MEB3_FMT;
+ mod = meb3->modulus + sizeof(meb3->modulus) - mod_len;
+ exp = meb3->exponent + sizeof(meb3->exponent) - mod_len;
+ inp = meb3->message + sizeof(meb3->message) - mod_len;
+ }
+
+ if (copy_from_user(mod, mex->n_modulus, mod_len) ||
+ copy_from_user(exp, mex->b_key, mod_len) ||
+ copy_from_user(inp, mex->inputdata, mod_len))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Convert a ICACRT message to a type50 CRT message.
+ *
+ * @zdev: crypto device pointer
+ * @zreq: crypto request pointer
+ * @crt: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ int mod_len, short_len, long_len, long_offset, limit;
+ unsigned char *p, *q, *dp, *dq, *u, *inp;
+
+ mod_len = crt->inputdatalength;
+ short_len = mod_len / 2;
+ long_len = mod_len / 2 + 8;
+
+ /*
+ * CEX2A cannot handle p, dp, or U > 128 bytes.
+ * If we have one of these, we need to do extra checking.
+ * For CEX3A the limit is 256 bytes.
+ */
+ if (zdev->max_mod_size == CEX3A_MAX_MOD_SIZE)
+ limit = 256;
+ else
+ limit = 128;
+
+ if (long_len > limit) {
+ /*
+ * zcrypt_rsa_crt already checked for the leading
+ * zeroes of np_prime, bp_key and u_mult_inc.
+ */
+ long_offset = long_len - limit;
+ long_len = limit;
+ } else
+ long_offset = 0;
+
+ /*
+ * Instead of doing extra work for p, dp, U > 64 bytes, we'll just use
+ * the larger message structure.
+ */
+ if (long_len <= 64) {
+ struct type50_crb1_msg *crb1 = ap_msg->message;
+ memset(crb1, 0, sizeof(*crb1));
+ ap_msg->length = sizeof(*crb1);
+ crb1->header.msg_type_code = TYPE50_TYPE_CODE;
+ crb1->header.msg_len = sizeof(*crb1);
+ crb1->keyblock_type = TYPE50_CRB1_FMT;
+ p = crb1->p + sizeof(crb1->p) - long_len;
+ q = crb1->q + sizeof(crb1->q) - short_len;
+ dp = crb1->dp + sizeof(crb1->dp) - long_len;
+ dq = crb1->dq + sizeof(crb1->dq) - short_len;
+ u = crb1->u + sizeof(crb1->u) - long_len;
+ inp = crb1->message + sizeof(crb1->message) - mod_len;
+ } else if (long_len <= 128) {
+ struct type50_crb2_msg *crb2 = ap_msg->message;
+ memset(crb2, 0, sizeof(*crb2));
+ ap_msg->length = sizeof(*crb2);
+ crb2->header.msg_type_code = TYPE50_TYPE_CODE;
+ crb2->header.msg_len = sizeof(*crb2);
+ crb2->keyblock_type = TYPE50_CRB2_FMT;
+ p = crb2->p + sizeof(crb2->p) - long_len;
+ q = crb2->q + sizeof(crb2->q) - short_len;
+ dp = crb2->dp + sizeof(crb2->dp) - long_len;
+ dq = crb2->dq + sizeof(crb2->dq) - short_len;
+ u = crb2->u + sizeof(crb2->u) - long_len;
+ inp = crb2->message + sizeof(crb2->message) - mod_len;
+ } else {
+ /* long_len >= 256 */
+ struct type50_crb3_msg *crb3 = ap_msg->message;
+ memset(crb3, 0, sizeof(*crb3));
+ ap_msg->length = sizeof(*crb3);
+ crb3->header.msg_type_code = TYPE50_TYPE_CODE;
+ crb3->header.msg_len = sizeof(*crb3);
+ crb3->keyblock_type = TYPE50_CRB3_FMT;
+ p = crb3->p + sizeof(crb3->p) - long_len;
+ q = crb3->q + sizeof(crb3->q) - short_len;
+ dp = crb3->dp + sizeof(crb3->dp) - long_len;
+ dq = crb3->dq + sizeof(crb3->dq) - short_len;
+ u = crb3->u + sizeof(crb3->u) - long_len;
+ inp = crb3->message + sizeof(crb3->message) - mod_len;
+ }
+
+ if (copy_from_user(p, crt->np_prime + long_offset, long_len) ||
+ copy_from_user(q, crt->nq_prime, short_len) ||
+ copy_from_user(dp, crt->bp_key + long_offset, long_len) ||
+ copy_from_user(dq, crt->bq_key, short_len) ||
+ copy_from_user(u, crt->u_mult_inv + long_offset, long_len) ||
+ copy_from_user(inp, crt->inputdata, mod_len))
+ return -EFAULT;
+
+ return 0;
+}
+
+/**
+ * Copy results from a type 80 reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @data: pointer to user output data
+ * @length: size of user output data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int convert_type80(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ struct type80_hdr *t80h = reply->message;
+ unsigned char *data;
+
+ if (t80h->len < sizeof(*t80h) + outputdatalength) {
+ /* The result is too short, the CEX2A card may not do that.. */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+ if (zdev->user_space_type == ZCRYPT_CEX2A)
+ BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE);
+ else
+ BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE);
+ data = reply->message + t80h->len - outputdatalength;
+ if (copy_to_user(outputdata, data, outputdatalength))
+ return -EFAULT;
+ return 0;
+}
+
+static int convert_response(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ /* Response type byte is the second byte in the response. */
+ switch (((unsigned char *) reply->message)[1]) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ return convert_error(zdev, reply);
+ case TYPE80_RSP_CODE:
+ return convert_type80(zdev, reply,
+ outputdata, outputdatalength);
+ default: /* Unknown response type, this should NEVER EVER happen */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+}
+
+/**
+ * This function is called from the AP bus code after a crypto request
+ * "msg" has finished with the reply message "reply".
+ * It is called from tasklet context.
+ * @ap_dev: pointer to the AP device
+ * @msg: pointer to the AP message
+ * @reply: pointer to the AP reply message
+ */
+static void zcrypt_cex2a_receive(struct ap_device *ap_dev,
+ struct ap_message *msg,
+ struct ap_message *reply)
+{
+ static struct error_hdr error_reply = {
+ .type = TYPE82_RSP_CODE,
+ .reply_code = REP82_ERROR_MACHINE_FAILURE,
+ };
+ struct type80_hdr *t80h;
+ int length;
+
+ /* Copy the reply message to the request message buffer. */
+ if (IS_ERR(reply)) {
+ memcpy(msg->message, &error_reply, sizeof(error_reply));
+ goto out;
+ }
+ t80h = reply->message;
+ if (t80h->type == TYPE80_RSP_CODE) {
+ if (ap_dev->device_type == AP_DEVICE_TYPE_CEX2A)
+ length = min_t(int,
+ CEX2A_MAX_RESPONSE_SIZE, t80h->len);
+ else
+ length = min_t(int,
+ CEX3A_MAX_RESPONSE_SIZE, t80h->len);
+ memcpy(msg->message, reply->message, length);
+ } else
+ memcpy(msg->message, reply->message, sizeof(error_reply));
+out:
+ complete((struct completion *) msg->private);
+}
+
+static atomic_t zcrypt_step = ATOMIC_INIT(0);
+
+/**
+ * The request distributor calls this function if it picked the CEX2A
+ * device to handle a modexpo request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * CEX2A device to the request distributor
+ * @mex: pointer to the modexpo request buffer
+ */
+static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo *mex)
+{
+ struct ap_message ap_msg;
+ struct completion work;
+ int rc;
+
+ ap_init_message(&ap_msg);
+ if (zdev->user_space_type == ZCRYPT_CEX2A)
+ ap_msg.message = kmalloc(MSGTYPE50_CRB2_MAX_MSG_SIZE,
+ GFP_KERNEL);
+ else
+ ap_msg.message = kmalloc(MSGTYPE50_CRB3_MAX_MSG_SIZE,
+ GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_cex2a_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &work;
+ rc = ICAMEX_msg_to_type50MEX_msg(zdev, &ap_msg, mex);
+ if (rc)
+ goto out_free;
+ init_completion(&work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&work);
+ if (rc == 0)
+ rc = convert_response(zdev, &ap_msg, mex->outputdata,
+ mex->outputdatalength);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+ kfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the CEX2A
+ * device to handle a modexpo_crt request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * CEX2A device to the request distributor
+ * @crt: pointer to the modexpoc_crt request buffer
+ */
+static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ struct ap_message ap_msg;
+ struct completion work;
+ int rc;
+
+ ap_init_message(&ap_msg);
+ if (zdev->user_space_type == ZCRYPT_CEX2A)
+ ap_msg.message = kmalloc(MSGTYPE50_CRB2_MAX_MSG_SIZE,
+ GFP_KERNEL);
+ else
+ ap_msg.message = kmalloc(MSGTYPE50_CRB3_MAX_MSG_SIZE,
+ GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_cex2a_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &work;
+ rc = ICACRT_msg_to_type50CRT_msg(zdev, &ap_msg, crt);
+ if (rc)
+ goto out_free;
+ init_completion(&work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&work);
+ if (rc == 0)
+ rc = convert_response(zdev, &ap_msg, crt->outputdata,
+ crt->outputdatalength);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+ kfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The crypto operations for message type 50.
+ */
+static struct zcrypt_ops zcrypt_msgtype50_ops = {
+ .rsa_modexpo = zcrypt_cex2a_modexpo,
+ .rsa_modexpo_crt = zcrypt_cex2a_modexpo_crt,
+ .owner = THIS_MODULE,
+ .variant = MSGTYPE50_VARIANT_DEFAULT,
+};
+
+int __init zcrypt_msgtype50_init(void)
+{
+ zcrypt_msgtype_register(&zcrypt_msgtype50_ops);
+ return 0;
+}
+
+void __exit zcrypt_msgtype50_exit(void)
+{
+ zcrypt_msgtype_unregister(&zcrypt_msgtype50_ops);
+}
+
+module_init(zcrypt_msgtype50_init);
+module_exit(zcrypt_msgtype50_exit);
diff --git a/drivers/s390/crypto/zcrypt_msgtype50.h b/drivers/s390/crypto/zcrypt_msgtype50.h
new file mode 100644
index 000000000000..e56dc72c7733
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype50.h
@@ -0,0 +1,39 @@
+/*
+ * zcrypt 2.1.0
+ *
+ * Copyright IBM Corp. 2001, 2012
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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 _ZCRYPT_MSGTYPE50_H_
+#define _ZCRYPT_MSGTYPE50_H_
+
+#define MSGTYPE50_NAME "zcrypt_msgtype50"
+#define MSGTYPE50_VARIANT_DEFAULT 0
+
+#define MSGTYPE50_CRB2_MAX_MSG_SIZE 0x390 /*sizeof(struct type50_crb2_msg)*/
+#define MSGTYPE50_CRB3_MAX_MSG_SIZE 0x710 /*sizeof(struct type50_crb3_msg)*/
+
+int zcrypt_msgtype50_init(void);
+void zcrypt_msgtype50_exit(void);
+
+#endif /* _ZCRYPT_MSGTYPE50_H_ */
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c
new file mode 100644
index 000000000000..7d97fa5a26d0
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype6.c
@@ -0,0 +1,856 @@
+/*
+ * zcrypt 2.1.0
+ *
+ * Copyright IBM Corp. 2001, 2012
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+
+#include "ap_bus.h"
+#include "zcrypt_api.h"
+#include "zcrypt_error.h"
+#include "zcrypt_msgtype6.h"
+#include "zcrypt_cca_key.h"
+
+#define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */
+#define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */
+
+#define CEIL4(x) ((((x)+3)/4)*4)
+
+struct response_type {
+ struct completion work;
+ int type;
+};
+#define PCIXCC_RESPONSE_TYPE_ICA 0
+#define PCIXCC_RESPONSE_TYPE_XCRB 1
+
+MODULE_AUTHOR("IBM Corporation");
+MODULE_DESCRIPTION("Cryptographic Coprocessor (message type 6), " \
+ "Copyright IBM Corp. 2001, 2012");
+MODULE_LICENSE("GPL");
+
+static void zcrypt_msgtype6_receive(struct ap_device *, struct ap_message *,
+ struct ap_message *);
+
+/**
+ * CPRB
+ * Note that all shorts, ints and longs are little-endian.
+ * All pointer fields are 32-bits long, and mean nothing
+ *
+ * A request CPRB is followed by a request_parameter_block.
+ *
+ * The request (or reply) parameter block is organized thus:
+ * function code
+ * VUD block
+ * key block
+ */
+struct CPRB {
+ unsigned short cprb_len; /* CPRB length */
+ unsigned char cprb_ver_id; /* CPRB version id. */
+ unsigned char pad_000; /* Alignment pad byte. */
+ unsigned char srpi_rtcode[4]; /* SRPI return code LELONG */
+ unsigned char srpi_verb; /* SRPI verb type */
+ unsigned char flags; /* flags */
+ unsigned char func_id[2]; /* function id */
+ unsigned char checkpoint_flag; /* */
+ unsigned char resv2; /* reserved */
+ unsigned short req_parml; /* request parameter buffer */
+ /* length 16-bit little endian */
+ unsigned char req_parmp[4]; /* request parameter buffer *
+ * pointer (means nothing: the *
+ * parameter buffer follows *
+ * the CPRB). */
+ unsigned char req_datal[4]; /* request data buffer */
+ /* length ULELONG */
+ unsigned char req_datap[4]; /* request data buffer */
+ /* pointer */
+ unsigned short rpl_parml; /* reply parameter buffer */
+ /* length 16-bit little endian */
+ unsigned char pad_001[2]; /* Alignment pad bytes. ULESHORT */
+ unsigned char rpl_parmp[4]; /* reply parameter buffer *
+ * pointer (means nothing: the *
+ * parameter buffer follows *
+ * the CPRB). */
+ unsigned char rpl_datal[4]; /* reply data buffer len ULELONG */
+ unsigned char rpl_datap[4]; /* reply data buffer */
+ /* pointer */
+ unsigned short ccp_rscode; /* server reason code ULESHORT */
+ unsigned short ccp_rtcode; /* server return code ULESHORT */
+ unsigned char repd_parml[2]; /* replied parameter len ULESHORT*/
+ unsigned char mac_data_len[2]; /* Mac Data Length ULESHORT */
+ unsigned char repd_datal[4]; /* replied data length ULELONG */
+ unsigned char req_pc[2]; /* PC identifier */
+ unsigned char res_origin[8]; /* resource origin */
+ unsigned char mac_value[8]; /* Mac Value */
+ unsigned char logon_id[8]; /* Logon Identifier */
+ unsigned char usage_domain[2]; /* cdx */
+ unsigned char resv3[18]; /* reserved for requestor */
+ unsigned short svr_namel; /* server name length ULESHORT */
+ unsigned char svr_name[8]; /* server name */
+} __packed;
+
+struct function_and_rules_block {
+ unsigned char function_code[2];
+ unsigned short ulen;
+ unsigned char only_rule[8];
+} __packed;
+
+/**
+ * The following is used to initialize the CPRBX passed to the PCIXCC/CEX2C
+ * card in a type6 message. The 3 fields that must be filled in at execution
+ * time are req_parml, rpl_parml and usage_domain.
+ * Everything about this interface is ascii/big-endian, since the
+ * device does *not* have 'Intel inside'.
+ *
+ * The CPRBX is followed immediately by the parm block.
+ * The parm block contains:
+ * - function code ('PD' 0x5044 or 'PK' 0x504B)
+ * - rule block (one of:)
+ * + 0x000A 'PKCS-1.2' (MCL2 'PD')
+ * + 0x000A 'ZERO-PAD' (MCL2 'PK')
+ * + 0x000A 'ZERO-PAD' (MCL3 'PD' or CEX2C 'PD')
+ * + 0x000A 'MRP ' (MCL3 'PK' or CEX2C 'PK')
+ * - VUD block
+ */
+static struct CPRBX static_cprbx = {
+ .cprb_len = 0x00DC,
+ .cprb_ver_id = 0x02,
+ .func_id = {0x54, 0x32},
+};
+
+/**
+ * Convert a ICAMEX message to a type6 MEX message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @mex: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo *mex)
+{
+ static struct type6_hdr static_type6_hdrX = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ .agent_id = {'C', 'A',},
+ .function_code = {'P', 'K'},
+ };
+ static struct function_and_rules_block static_pke_fnr = {
+ .function_code = {'P', 'K'},
+ .ulen = 10,
+ .only_rule = {'M', 'R', 'P', ' ', ' ', ' ', ' ', ' '}
+ };
+ static struct function_and_rules_block static_pke_fnr_MCL2 = {
+ .function_code = {'P', 'K'},
+ .ulen = 10,
+ .only_rule = {'Z', 'E', 'R', 'O', '-', 'P', 'A', 'D'}
+ };
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ struct function_and_rules_block fr;
+ unsigned short length;
+ char text[0];
+ } __packed * msg = ap_msg->message;
+ int size;
+
+ /* VUD.ciphertext */
+ msg->length = mex->inputdatalength + 2;
+ if (copy_from_user(msg->text, mex->inputdata, mex->inputdatalength))
+ return -EFAULT;
+
+ /* Set up key which is located after the variable length text. */
+ size = zcrypt_type6_mex_key_en(mex, msg->text+mex->inputdatalength, 1);
+ if (size < 0)
+ return size;
+ size += sizeof(*msg) + mex->inputdatalength;
+
+ /* message header, cprbx and f&r */
+ msg->hdr = static_type6_hdrX;
+ msg->hdr.ToCardLen1 = size - sizeof(msg->hdr);
+ msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
+
+ msg->cprbx = static_cprbx;
+ msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
+ msg->cprbx.rpl_msgbl = msg->hdr.FromCardLen1;
+
+ msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
+ static_pke_fnr_MCL2 : static_pke_fnr;
+
+ msg->cprbx.req_parml = size - sizeof(msg->hdr) - sizeof(msg->cprbx);
+
+ ap_msg->length = size;
+ return 0;
+}
+
+/**
+ * Convert a ICACRT message to a type6 CRT message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @crt: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT.
+ */
+static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ static struct type6_hdr static_type6_hdrX = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ .agent_id = {'C', 'A',},
+ .function_code = {'P', 'D'},
+ };
+ static struct function_and_rules_block static_pkd_fnr = {
+ .function_code = {'P', 'D'},
+ .ulen = 10,
+ .only_rule = {'Z', 'E', 'R', 'O', '-', 'P', 'A', 'D'}
+ };
+
+ static struct function_and_rules_block static_pkd_fnr_MCL2 = {
+ .function_code = {'P', 'D'},
+ .ulen = 10,
+ .only_rule = {'P', 'K', 'C', 'S', '-', '1', '.', '2'}
+ };
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ struct function_and_rules_block fr;
+ unsigned short length;
+ char text[0];
+ } __packed * msg = ap_msg->message;
+ int size;
+
+ /* VUD.ciphertext */
+ msg->length = crt->inputdatalength + 2;
+ if (copy_from_user(msg->text, crt->inputdata, crt->inputdatalength))
+ return -EFAULT;
+
+ /* Set up key which is located after the variable length text. */
+ size = zcrypt_type6_crt_key(crt, msg->text + crt->inputdatalength, 1);
+ if (size < 0)
+ return size;
+ size += sizeof(*msg) + crt->inputdatalength; /* total size of msg */
+
+ /* message header, cprbx and f&r */
+ msg->hdr = static_type6_hdrX;
+ msg->hdr.ToCardLen1 = size - sizeof(msg->hdr);
+ msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
+
+ msg->cprbx = static_cprbx;
+ msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
+ msg->cprbx.req_parml = msg->cprbx.rpl_msgbl =
+ size - sizeof(msg->hdr) - sizeof(msg->cprbx);
+
+ msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
+ static_pkd_fnr_MCL2 : static_pkd_fnr;
+
+ ap_msg->length = size;
+ return 0;
+}
+
+/**
+ * Convert a XCRB message to a type6 CPRB message.
+ *
+ * @zdev: crypto device pointer
+ * @ap_msg: pointer to AP message
+ * @xcRB: pointer to user input data
+ *
+ * Returns 0 on success or -EFAULT, -EINVAL.
+ */
+struct type86_fmt2_msg {
+ struct type86_hdr hdr;
+ struct type86_fmt2_ext fmt2;
+} __packed;
+
+static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
+ struct ap_message *ap_msg,
+ struct ica_xcRB *xcRB)
+{
+ static struct type6_hdr static_type6_hdrX = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ };
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ } __packed * msg = ap_msg->message;
+
+ int rcblen = CEIL4(xcRB->request_control_blk_length);
+ int replylen;
+ char *req_data = ap_msg->message + sizeof(struct type6_hdr) + rcblen;
+ char *function_code;
+
+ /* length checks */
+ ap_msg->length = sizeof(struct type6_hdr) +
+ CEIL4(xcRB->request_control_blk_length) +
+ xcRB->request_data_length;
+ if (ap_msg->length > MSGTYPE06_MAX_MSG_SIZE)
+ return -EINVAL;
+ replylen = sizeof(struct type86_fmt2_msg) +
+ CEIL4(xcRB->reply_control_blk_length) +
+ xcRB->reply_data_length;
+ if (replylen > MSGTYPE06_MAX_MSG_SIZE)
+ return -EINVAL;
+
+ /* prepare type6 header */
+ msg->hdr = static_type6_hdrX;
+ memcpy(msg->hdr.agent_id , &(xcRB->agent_ID), sizeof(xcRB->agent_ID));
+ msg->hdr.ToCardLen1 = xcRB->request_control_blk_length;
+ if (xcRB->request_data_length) {
+ msg->hdr.offset2 = msg->hdr.offset1 + rcblen;
+ msg->hdr.ToCardLen2 = xcRB->request_data_length;
+ }
+ msg->hdr.FromCardLen1 = xcRB->reply_control_blk_length;
+ msg->hdr.FromCardLen2 = xcRB->reply_data_length;
+
+ /* prepare CPRB */
+ if (copy_from_user(&(msg->cprbx), xcRB->request_control_blk_addr,
+ xcRB->request_control_blk_length))
+ return -EFAULT;
+ if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) >
+ xcRB->request_control_blk_length)
+ return -EINVAL;
+ function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
+ memcpy(msg->hdr.function_code, function_code,
+ sizeof(msg->hdr.function_code));
+
+ if (memcmp(function_code, "US", 2) == 0)
+ ap_msg->special = 1;
+ else
+ ap_msg->special = 0;
+
+ /* copy data block */
+ if (xcRB->request_data_length &&
+ copy_from_user(req_data, xcRB->request_data_address,
+ xcRB->request_data_length))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Copy results from a type 86 ICA reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @data: pointer to user output data
+ * @length: size of user output data
+ *
+ * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
+ */
+struct type86x_reply {
+ struct type86_hdr hdr;
+ struct type86_fmt2_ext fmt2;
+ struct CPRBX cprbx;
+ unsigned char pad[4]; /* 4 byte function code/rules block ? */
+ unsigned short length;
+ char text[0];
+} __packed;
+
+static int convert_type86_ica(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ static unsigned char static_pad[] = {
+ 0x00, 0x02,
+ 0x1B, 0x7B, 0x5D, 0xB5, 0x75, 0x01, 0x3D, 0xFD,
+ 0x8D, 0xD1, 0xC7, 0x03, 0x2D, 0x09, 0x23, 0x57,
+ 0x89, 0x49, 0xB9, 0x3F, 0xBB, 0x99, 0x41, 0x5B,
+ 0x75, 0x21, 0x7B, 0x9D, 0x3B, 0x6B, 0x51, 0x39,
+ 0xBB, 0x0D, 0x35, 0xB9, 0x89, 0x0F, 0x93, 0xA5,
+ 0x0B, 0x47, 0xF1, 0xD3, 0xBB, 0xCB, 0xF1, 0x9D,
+ 0x23, 0x73, 0x71, 0xFF, 0xF3, 0xF5, 0x45, 0xFB,
+ 0x61, 0x29, 0x23, 0xFD, 0xF1, 0x29, 0x3F, 0x7F,
+ 0x17, 0xB7, 0x1B, 0xA9, 0x19, 0xBD, 0x57, 0xA9,
+ 0xD7, 0x95, 0xA3, 0xCB, 0xED, 0x1D, 0xDB, 0x45,
+ 0x7D, 0x11, 0xD1, 0x51, 0x1B, 0xED, 0x71, 0xE9,
+ 0xB1, 0xD1, 0xAB, 0xAB, 0x21, 0x2B, 0x1B, 0x9F,
+ 0x3B, 0x9F, 0xF7, 0xF7, 0xBD, 0x63, 0xEB, 0xAD,
+ 0xDF, 0xB3, 0x6F, 0x5B, 0xDB, 0x8D, 0xA9, 0x5D,
+ 0xE3, 0x7D, 0x77, 0x49, 0x47, 0xF5, 0xA7, 0xFD,
+ 0xAB, 0x2F, 0x27, 0x35, 0x77, 0xD3, 0x49, 0xC9,
+ 0x09, 0xEB, 0xB1, 0xF9, 0xBF, 0x4B, 0xCB, 0x2B,
+ 0xEB, 0xEB, 0x05, 0xFF, 0x7D, 0xC7, 0x91, 0x8B,
+ 0x09, 0x83, 0xB9, 0xB9, 0x69, 0x33, 0x39, 0x6B,
+ 0x79, 0x75, 0x19, 0xBF, 0xBB, 0x07, 0x1D, 0xBD,
+ 0x29, 0xBF, 0x39, 0x95, 0x93, 0x1D, 0x35, 0xC7,
+ 0xC9, 0x4D, 0xE5, 0x97, 0x0B, 0x43, 0x9B, 0xF1,
+ 0x16, 0x93, 0x03, 0x1F, 0xA5, 0xFB, 0xDB, 0xF3,
+ 0x27, 0x4F, 0x27, 0x61, 0x05, 0x1F, 0xB9, 0x23,
+ 0x2F, 0xC3, 0x81, 0xA9, 0x23, 0x71, 0x55, 0x55,
+ 0xEB, 0xED, 0x41, 0xE5, 0xF3, 0x11, 0xF1, 0x43,
+ 0x69, 0x03, 0xBD, 0x0B, 0x37, 0x0F, 0x51, 0x8F,
+ 0x0B, 0xB5, 0x89, 0x5B, 0x67, 0xA9, 0xD9, 0x4F,
+ 0x01, 0xF9, 0x21, 0x77, 0x37, 0x73, 0x79, 0xC5,
+ 0x7F, 0x51, 0xC1, 0xCF, 0x97, 0xA1, 0x75, 0xAD,
+ 0x35, 0x9D, 0xD3, 0xD3, 0xA7, 0x9D, 0x5D, 0x41,
+ 0x6F, 0x65, 0x1B, 0xCF, 0xA9, 0x87, 0x91, 0x09
+ };
+ struct type86x_reply *msg = reply->message;
+ unsigned short service_rc, service_rs;
+ unsigned int reply_len, pad_len;
+ char *data;
+
+ service_rc = msg->cprbx.ccp_rtcode;
+ if (unlikely(service_rc != 0)) {
+ service_rs = msg->cprbx.ccp_rscode;
+ if (service_rc == 8 && service_rs == 66)
+ return -EINVAL;
+ if (service_rc == 8 && service_rs == 65)
+ return -EINVAL;
+ if (service_rc == 8 && service_rs == 770)
+ return -EINVAL;
+ if (service_rc == 8 && service_rs == 783) {
+ zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD;
+ return -EAGAIN;
+ }
+ if (service_rc == 12 && service_rs == 769)
+ return -EINVAL;
+ if (service_rc == 8 && service_rs == 72)
+ return -EINVAL;
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+ data = msg->text;
+ reply_len = msg->length - 2;
+ if (reply_len > outputdatalength)
+ return -EINVAL;
+ /*
+ * For all encipher requests, the length of the ciphertext (reply_len)
+ * will always equal the modulus length. For MEX decipher requests
+ * the output needs to get padded. Minimum pad size is 10.
+ *
+ * Currently, the cases where padding will be added is for:
+ * - PCIXCC_MCL2 using a CRT form token (since PKD didn't support
+ * ZERO-PAD and CRT is only supported for PKD requests)
+ * - PCICC, always
+ */
+ pad_len = outputdatalength - reply_len;
+ if (pad_len > 0) {
+ if (pad_len < 10)
+ return -EINVAL;
+ /* 'restore' padding left in the PCICC/PCIXCC card. */
+ if (copy_to_user(outputdata, static_pad, pad_len - 1))
+ return -EFAULT;
+ if (put_user(0, outputdata + pad_len - 1))
+ return -EFAULT;
+ }
+ /* Copy the crypto response to user space. */
+ if (copy_to_user(outputdata + pad_len, data, reply_len))
+ return -EFAULT;
+ return 0;
+}
+
+/**
+ * Copy results from a type 86 XCRB reply message back to user space.
+ *
+ * @zdev: crypto device pointer
+ * @reply: reply AP message.
+ * @xcRB: pointer to XCRB
+ *
+ * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
+ */
+static int convert_type86_xcrb(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ struct ica_xcRB *xcRB)
+{
+ struct type86_fmt2_msg *msg = reply->message;
+ char *data = reply->message;
+
+ /* Copy CPRB to user */
+ if (copy_to_user(xcRB->reply_control_blk_addr,
+ data + msg->fmt2.offset1, msg->fmt2.count1))
+ return -EFAULT;
+ xcRB->reply_control_blk_length = msg->fmt2.count1;
+
+ /* Copy data buffer to user */
+ if (msg->fmt2.count2)
+ if (copy_to_user(xcRB->reply_data_addr,
+ data + msg->fmt2.offset2, msg->fmt2.count2))
+ return -EFAULT;
+ xcRB->reply_data_length = msg->fmt2.count2;
+ return 0;
+}
+
+static int convert_type86_rng(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char *buffer)
+{
+ struct {
+ struct type86_hdr hdr;
+ struct type86_fmt2_ext fmt2;
+ struct CPRBX cprbx;
+ } __packed * msg = reply->message;
+ char *data = reply->message;
+
+ if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0)
+ return -EINVAL;
+ memcpy(buffer, data + msg->fmt2.offset2, msg->fmt2.count2);
+ return msg->fmt2.count2;
+}
+
+static int convert_response_ica(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char __user *outputdata,
+ unsigned int outputdatalength)
+{
+ struct type86x_reply *msg = reply->message;
+
+ /* Response type byte is the second byte in the response. */
+ switch (((unsigned char *) reply->message)[1]) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ return convert_error(zdev, reply);
+ case TYPE86_RSP_CODE:
+ if (msg->cprbx.ccp_rtcode &&
+ (msg->cprbx.ccp_rscode == 0x14f) &&
+ (outputdatalength > 256)) {
+ if (zdev->max_exp_bit_length <= 17) {
+ zdev->max_exp_bit_length = 17;
+ return -EAGAIN;
+ } else
+ return -EINVAL;
+ }
+ if (msg->hdr.reply_code)
+ return convert_error(zdev, reply);
+ if (msg->cprbx.cprb_ver_id == 0x02)
+ return convert_type86_ica(zdev, reply,
+ outputdata, outputdatalength);
+ /* Fall through, no break, incorrect cprb version is an unknown
+ * response */
+ default: /* Unknown response type, this should NEVER EVER happen */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+}
+
+static int convert_response_xcrb(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ struct ica_xcRB *xcRB)
+{
+ struct type86x_reply *msg = reply->message;
+
+ /* Response type byte is the second byte in the response. */
+ switch (((unsigned char *) reply->message)[1]) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
+ return convert_error(zdev, reply);
+ case TYPE86_RSP_CODE:
+ if (msg->hdr.reply_code) {
+ memcpy(&(xcRB->status), msg->fmt2.apfs, sizeof(u32));
+ return convert_error(zdev, reply);
+ }
+ if (msg->cprbx.cprb_ver_id == 0x02)
+ return convert_type86_xcrb(zdev, reply, xcRB);
+ /* Fall through, no break, incorrect cprb version is an unknown
+ * response */
+ default: /* Unknown response type, this should NEVER EVER happen */
+ xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+}
+
+static int convert_response_rng(struct zcrypt_device *zdev,
+ struct ap_message *reply,
+ char *data)
+{
+ struct type86x_reply *msg = reply->message;
+
+ switch (msg->hdr.type) {
+ case TYPE82_RSP_CODE:
+ case TYPE88_RSP_CODE:
+ return -EINVAL;
+ case TYPE86_RSP_CODE:
+ if (msg->hdr.reply_code)
+ return -EINVAL;
+ if (msg->cprbx.cprb_ver_id == 0x02)
+ return convert_type86_rng(zdev, reply, data);
+ /* Fall through, no break, incorrect cprb version is an unknown
+ * response */
+ default: /* Unknown response type, this should NEVER EVER happen */
+ zdev->online = 0;
+ return -EAGAIN; /* repeat the request on a different device. */
+ }
+}
+
+/**
+ * This function is called from the AP bus code after a crypto request
+ * "msg" has finished with the reply message "reply".
+ * It is called from tasklet context.
+ * @ap_dev: pointer to the AP device
+ * @msg: pointer to the AP message
+ * @reply: pointer to the AP reply message
+ */
+static void zcrypt_msgtype6_receive(struct ap_device *ap_dev,
+ struct ap_message *msg,
+ struct ap_message *reply)
+{
+ static struct error_hdr error_reply = {
+ .type = TYPE82_RSP_CODE,
+ .reply_code = REP82_ERROR_MACHINE_FAILURE,
+ };
+ struct response_type *resp_type =
+ (struct response_type *) msg->private;
+ struct type86x_reply *t86r;
+ int length;
+
+ /* Copy the reply message to the request message buffer. */
+ if (IS_ERR(reply)) {
+ memcpy(msg->message, &error_reply, sizeof(error_reply));
+ goto out;
+ }
+ t86r = reply->message;
+ if (t86r->hdr.type == TYPE86_RSP_CODE &&
+ t86r->cprbx.cprb_ver_id == 0x02) {
+ switch (resp_type->type) {
+ case PCIXCC_RESPONSE_TYPE_ICA:
+ length = sizeof(struct type86x_reply)
+ + t86r->length - 2;
+ length = min(PCIXCC_MAX_ICA_RESPONSE_SIZE, length);
+ memcpy(msg->message, reply->message, length);
+ break;
+ case PCIXCC_RESPONSE_TYPE_XCRB:
+ length = t86r->fmt2.offset2 + t86r->fmt2.count2;
+ length = min(MSGTYPE06_MAX_MSG_SIZE, length);
+ memcpy(msg->message, reply->message, length);
+ break;
+ default:
+ memcpy(msg->message, &error_reply,
+ sizeof(error_reply));
+ }
+ } else
+ memcpy(msg->message, reply->message, sizeof(error_reply));
+out:
+ complete(&(resp_type->work));
+}
+
+static atomic_t zcrypt_step = ATOMIC_INIT(0);
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a modexpo request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCIXCC/CEX2C device to the request distributor
+ * @mex: pointer to the modexpo request buffer
+ */
+static long zcrypt_msgtype6_modexpo(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo *mex)
+{
+ struct ap_message ap_msg;
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_ICA,
+ };
+ int rc;
+
+ ap_init_message(&ap_msg);
+ ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_msgtype6_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &resp_type;
+ rc = ICAMEX_msg_to_type6MEX_msgX(zdev, &ap_msg, mex);
+ if (rc)
+ goto out_free;
+ init_completion(&resp_type.work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&resp_type.work);
+ if (rc == 0)
+ rc = convert_response_ica(zdev, &ap_msg, mex->outputdata,
+ mex->outputdatalength);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+ free_page((unsigned long) ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a modexpo_crt request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCIXCC/CEX2C device to the request distributor
+ * @crt: pointer to the modexpoc_crt request buffer
+ */
+static long zcrypt_msgtype6_modexpo_crt(struct zcrypt_device *zdev,
+ struct ica_rsa_modexpo_crt *crt)
+{
+ struct ap_message ap_msg;
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_ICA,
+ };
+ int rc;
+
+ ap_init_message(&ap_msg);
+ ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_msgtype6_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &resp_type;
+ rc = ICACRT_msg_to_type6CRT_msgX(zdev, &ap_msg, crt);
+ if (rc)
+ goto out_free;
+ init_completion(&resp_type.work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&resp_type.work);
+ if (rc == 0)
+ rc = convert_response_ica(zdev, &ap_msg, crt->outputdata,
+ crt->outputdatalength);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+ free_page((unsigned long) ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to handle a send_cprb request.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCIXCC/CEX2C device to the request distributor
+ * @xcRB: pointer to the send_cprb request buffer
+ */
+static long zcrypt_msgtype6_send_cprb(struct zcrypt_device *zdev,
+ struct ica_xcRB *xcRB)
+{
+ struct ap_message ap_msg;
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_XCRB,
+ };
+ int rc;
+
+ ap_init_message(&ap_msg);
+ ap_msg.message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_msgtype6_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &resp_type;
+ rc = XCRB_msg_to_type6CPRB_msgX(zdev, &ap_msg, xcRB);
+ if (rc)
+ goto out_free;
+ init_completion(&resp_type.work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&resp_type.work);
+ if (rc == 0)
+ rc = convert_response_xcrb(zdev, &ap_msg, xcRB);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+out_free:
+ kzfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The request distributor calls this function if it picked the PCIXCC/CEX2C
+ * device to generate random data.
+ * @zdev: pointer to zcrypt_device structure that identifies the
+ * PCIXCC/CEX2C device to the request distributor
+ * @buffer: pointer to a memory page to return random data
+ */
+
+static long zcrypt_msgtype6_rng(struct zcrypt_device *zdev,
+ char *buffer)
+{
+ struct ap_message ap_msg;
+ struct response_type resp_type = {
+ .type = PCIXCC_RESPONSE_TYPE_XCRB,
+ };
+ int rc;
+
+ ap_init_message(&ap_msg);
+ ap_msg.message = kmalloc(MSGTYPE06_MAX_MSG_SIZE, GFP_KERNEL);
+ if (!ap_msg.message)
+ return -ENOMEM;
+ ap_msg.receive = zcrypt_msgtype6_receive;
+ ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
+ atomic_inc_return(&zcrypt_step);
+ ap_msg.private = &resp_type;
+ rng_type6CPRB_msgX(zdev->ap_dev, &ap_msg, ZCRYPT_RNG_BUFFER_SIZE);
+ init_completion(&resp_type.work);
+ ap_queue_message(zdev->ap_dev, &ap_msg);
+ rc = wait_for_completion_interruptible(&resp_type.work);
+ if (rc == 0)
+ rc = convert_response_rng(zdev, &ap_msg, buffer);
+ else
+ /* Signal pending. */
+ ap_cancel_message(zdev->ap_dev, &ap_msg);
+ kfree(ap_msg.message);
+ return rc;
+}
+
+/**
+ * The crypto operations for a PCIXCC/CEX2C card.
+ */
+static struct zcrypt_ops zcrypt_msgtype6_norng_ops = {
+ .owner = THIS_MODULE,
+ .variant = MSGTYPE06_VARIANT_NORNG,
+ .rsa_modexpo = zcrypt_msgtype6_modexpo,
+ .rsa_modexpo_crt = zcrypt_msgtype6_modexpo_crt,
+ .send_cprb = zcrypt_msgtype6_send_cprb,
+};
+
+static struct zcrypt_ops zcrypt_msgtype6_ops = {
+ .owner = THIS_MODULE,
+ .variant = MSGTYPE06_VARIANT_DEFAULT,
+ .rsa_modexpo = zcrypt_msgtype6_modexpo,
+ .rsa_modexpo_crt = zcrypt_msgtype6_modexpo_crt,
+ .send_cprb = zcrypt_msgtype6_send_cprb,
+ .rng = zcrypt_msgtype6_rng,
+};
+
+int __init zcrypt_msgtype6_init(void)
+{
+ zcrypt_msgtype_register(&zcrypt_msgtype6_norng_ops);
+ zcrypt_msgtype_register(&zcrypt_msgtype6_ops);
+ return 0;
+}
+
+void __exit zcrypt_msgtype6_exit(void)
+{
+ zcrypt_msgtype_unregister(&zcrypt_msgtype6_norng_ops);
+ zcrypt_msgtype_unregister(&zcrypt_msgtype6_ops);
+}
+
+module_init(zcrypt_msgtype6_init);
+module_exit(zcrypt_msgtype6_exit);
diff --git a/drivers/s390/crypto/zcrypt_msgtype6.h b/drivers/s390/crypto/zcrypt_msgtype6.h
new file mode 100644
index 000000000000..1e500d3c0735
--- /dev/null
+++ b/drivers/s390/crypto/zcrypt_msgtype6.h
@@ -0,0 +1,169 @@
+/*
+ * zcrypt 2.1.0
+ *
+ * Copyright IBM Corp. 2001, 2012
+ * Author(s): Robert Burroughs
+ * Eric Rossman (edrossma@us.ibm.com)
+ *
+ * Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
+ * Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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 _ZCRYPT_MSGTYPE6_H_
+#define _ZCRYPT_MSGTYPE6_H_
+
+#include <asm/zcrypt.h>
+
+#define MSGTYPE06_NAME "zcrypt_msgtype6"
+#define MSGTYPE06_VARIANT_DEFAULT 0
+#define MSGTYPE06_VARIANT_NORNG 1
+
+#define MSGTYPE06_MAX_MSG_SIZE (12*1024)
+
+/**
+ * The type 6 message family is associated with PCICC or PCIXCC cards.
+ *
+ * It contains a message header followed by a CPRB, both of which
+ * are described below.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type6_hdr {
+ unsigned char reserved1; /* 0x00 */
+ unsigned char type; /* 0x06 */
+ unsigned char reserved2[2]; /* 0x0000 */
+ unsigned char right[4]; /* 0x00000000 */
+ unsigned char reserved3[2]; /* 0x0000 */
+ unsigned char reserved4[2]; /* 0x0000 */
+ unsigned char apfs[4]; /* 0x00000000 */
+ unsigned int offset1; /* 0x00000058 (offset to CPRB) */
+ unsigned int offset2; /* 0x00000000 */
+ unsigned int offset3; /* 0x00000000 */
+ unsigned int offset4; /* 0x00000000 */
+ unsigned char agent_id[16]; /* PCICC: */
+ /* 0x0100 */
+ /* 0x4343412d4150504c202020 */
+ /* 0x010101 */
+ /* PCIXCC: */
+ /* 0x4341000000000000 */
+ /* 0x0000000000000000 */
+ unsigned char rqid[2]; /* rqid. internal to 603 */
+ unsigned char reserved5[2]; /* 0x0000 */
+ unsigned char function_code[2]; /* for PKD, 0x5044 (ascii 'PD') */
+ unsigned char reserved6[2]; /* 0x0000 */
+ unsigned int ToCardLen1; /* (request CPRB len + 3) & -4 */
+ unsigned int ToCardLen2; /* db len 0x00000000 for PKD */
+ unsigned int ToCardLen3; /* 0x00000000 */
+ unsigned int ToCardLen4; /* 0x00000000 */
+ unsigned int FromCardLen1; /* response buffer length */
+ unsigned int FromCardLen2; /* db len 0x00000000 for PKD */
+ unsigned int FromCardLen3; /* 0x00000000 */
+ unsigned int FromCardLen4; /* 0x00000000 */
+} __packed;
+
+/**
+ * The type 86 message family is associated with PCICC and PCIXCC cards.
+ *
+ * It contains a message header followed by a CPRB. The CPRB is
+ * the same as the request CPRB, which is described above.
+ *
+ * If format is 1, an error condition exists and no data beyond
+ * the 8-byte message header is of interest.
+ *
+ * The non-error message is shown below.
+ *
+ * Note that all reserved fields must be zeroes.
+ */
+struct type86_hdr {
+ unsigned char reserved1; /* 0x00 */
+ unsigned char type; /* 0x86 */
+ unsigned char format; /* 0x01 (error) or 0x02 (ok) */
+ unsigned char reserved2; /* 0x00 */
+ unsigned char reply_code; /* reply code (see above) */
+ unsigned char reserved3[3]; /* 0x000000 */
+} __packed;
+
+#define TYPE86_RSP_CODE 0x86
+#define TYPE86_FMT2 0x02
+
+struct type86_fmt2_ext {
+ unsigned char reserved[4]; /* 0x00000000 */
+ unsigned char apfs[4]; /* final status */
+ unsigned int count1; /* length of CPRB + parameters */
+ unsigned int offset1; /* offset to CPRB */
+ unsigned int count2; /* 0x00000000 */
+ unsigned int offset2; /* db offset 0x00000000 for PKD */
+ unsigned int count3; /* 0x00000000 */
+ unsigned int offset3; /* 0x00000000 */
+ unsigned int count4; /* 0x00000000 */
+ unsigned int offset4; /* 0x00000000 */
+} __packed;
+
+/**
+ * Prepare a type6 CPRB message for random number generation
+ *
+ * @ap_dev: AP device pointer
+ * @ap_msg: pointer to AP message
+ */
+static inline void rng_type6CPRB_msgX(struct ap_device *ap_dev,
+ struct ap_message *ap_msg,
+ unsigned random_number_length)
+{
+ struct {
+ struct type6_hdr hdr;
+ struct CPRBX cprbx;
+ char function_code[2];
+ short int rule_length;
+ char rule[8];
+ short int verb_length;
+ short int key_length;
+ } __packed * msg = ap_msg->message;
+ static struct type6_hdr static_type6_hdrX = {
+ .type = 0x06,
+ .offset1 = 0x00000058,
+ .agent_id = {'C', 'A'},
+ .function_code = {'R', 'L'},
+ .ToCardLen1 = sizeof(*msg) - sizeof(msg->hdr),
+ .FromCardLen1 = sizeof(*msg) - sizeof(msg->hdr),
+ };
+ static struct CPRBX local_cprbx = {
+ .cprb_len = 0x00dc,
+ .cprb_ver_id = 0x02,
+ .func_id = {0x54, 0x32},
+ .req_parml = sizeof(*msg) - sizeof(msg->hdr) -
+ sizeof(msg->cprbx),
+ .rpl_msgbl = sizeof(*msg) - sizeof(msg->hdr),
+ };
+
+ msg->hdr = static_type6_hdrX;
+ msg->hdr.FromCardLen2 = random_number_length,
+ msg->cprbx = local_cprbx;
+ msg->cprbx.rpl_datal = random_number_length,
+ msg->cprbx.domain = AP_QID_QUEUE(ap_dev->qid);
+ memcpy(msg->function_code, msg->hdr.function_code, 0x02);
+ msg->rule_length = 0x0a;
+ memcpy(msg->rule, "RANDOM ", 8);
+ msg->verb_length = 0x02;
+ msg->key_length = 0x02;
+ ap_msg->length = sizeof(*msg);
+}
+
+int zcrypt_msgtype6_init(void);
+void zcrypt_msgtype6_exit(void);
+
+#endif /* _ZCRYPT_MSGTYPE6_H_ */
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c
index ccb4f8b60c75..c7275e303a0d 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.c
+++ b/drivers/s390/crypto/zcrypt_pcixcc.c
@@ -1,13 +1,14 @@
/*
* zcrypt 2.1.0
*
- * Copyright IBM Corp. 2001, 2006
+ * Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
* Ralph Wuerthner <rwuerthn@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,9 +36,10 @@
#include "ap_bus.h"
#include "zcrypt_api.h"
#include "zcrypt_error.h"
-#include "zcrypt_pcicc.h"
+#include "zcrypt_msgtype6.h"
#include "zcrypt_pcixcc.h"
#include "zcrypt_cca_key.h"
+#include "zcrypt_msgtype6.h"
#define PCIXCC_MIN_MOD_SIZE 16 /* 128 bits */
#define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */
@@ -75,14 +77,12 @@ static struct ap_device_id zcrypt_pcixcc_ids[] = {
MODULE_DEVICE_TABLE(ap, zcrypt_pcixcc_ids);
MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("PCIXCC Cryptographic Coprocessor device driver, "
- "Copyright IBM Corp. 2001, 2006");
+MODULE_DESCRIPTION("PCIXCC Cryptographic Coprocessor device driver, " \
+ "Copyright IBM Corp. 2001, 2012");
MODULE_LICENSE("GPL");
static int zcrypt_pcixcc_probe(struct ap_device *ap_dev);
static void zcrypt_pcixcc_remove(struct ap_device *ap_dev);
-static void zcrypt_pcixcc_receive(struct ap_device *, struct ap_message *,
- struct ap_message *);
static struct ap_driver zcrypt_pcixcc_driver = {
.probe = zcrypt_pcixcc_probe,
@@ -92,766 +92,6 @@ static struct ap_driver zcrypt_pcixcc_driver = {
};
/**
- * The following is used to initialize the CPRBX passed to the PCIXCC/CEX2C
- * card in a type6 message. The 3 fields that must be filled in at execution
- * time are req_parml, rpl_parml and usage_domain.
- * Everything about this interface is ascii/big-endian, since the
- * device does *not* have 'Intel inside'.
- *
- * The CPRBX is followed immediately by the parm block.
- * The parm block contains:
- * - function code ('PD' 0x5044 or 'PK' 0x504B)
- * - rule block (one of:)
- * + 0x000A 'PKCS-1.2' (MCL2 'PD')
- * + 0x000A 'ZERO-PAD' (MCL2 'PK')
- * + 0x000A 'ZERO-PAD' (MCL3 'PD' or CEX2C 'PD')
- * + 0x000A 'MRP ' (MCL3 'PK' or CEX2C 'PK')
- * - VUD block
- */
-static struct CPRBX static_cprbx = {
- .cprb_len = 0x00DC,
- .cprb_ver_id = 0x02,
- .func_id = {0x54,0x32},
-};
-
-/**
- * Convert a ICAMEX message to a type6 MEX message.
- *
- * @zdev: crypto device pointer
- * @ap_msg: pointer to AP message
- * @mex: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICAMEX_msg_to_type6MEX_msgX(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo *mex)
-{
- static struct type6_hdr static_type6_hdrX = {
- .type = 0x06,
- .offset1 = 0x00000058,
- .agent_id = {'C','A',},
- .function_code = {'P','K'},
- };
- static struct function_and_rules_block static_pke_fnr = {
- .function_code = {'P','K'},
- .ulen = 10,
- .only_rule = {'M','R','P',' ',' ',' ',' ',' '}
- };
- static struct function_and_rules_block static_pke_fnr_MCL2 = {
- .function_code = {'P','K'},
- .ulen = 10,
- .only_rule = {'Z','E','R','O','-','P','A','D'}
- };
- struct {
- struct type6_hdr hdr;
- struct CPRBX cprbx;
- struct function_and_rules_block fr;
- unsigned short length;
- char text[0];
- } __attribute__((packed)) *msg = ap_msg->message;
- int size;
-
- /* VUD.ciphertext */
- msg->length = mex->inputdatalength + 2;
- if (copy_from_user(msg->text, mex->inputdata, mex->inputdatalength))
- return -EFAULT;
-
- /* Set up key which is located after the variable length text. */
- size = zcrypt_type6_mex_key_en(mex, msg->text+mex->inputdatalength, 1);
- if (size < 0)
- return size;
- size += sizeof(*msg) + mex->inputdatalength;
-
- /* message header, cprbx and f&r */
- msg->hdr = static_type6_hdrX;
- msg->hdr.ToCardLen1 = size - sizeof(msg->hdr);
- msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
-
- msg->cprbx = static_cprbx;
- msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
- msg->cprbx.rpl_msgbl = msg->hdr.FromCardLen1;
-
- msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
- static_pke_fnr_MCL2 : static_pke_fnr;
-
- msg->cprbx.req_parml = size - sizeof(msg->hdr) - sizeof(msg->cprbx);
-
- ap_msg->length = size;
- return 0;
-}
-
-/**
- * Convert a ICACRT message to a type6 CRT message.
- *
- * @zdev: crypto device pointer
- * @ap_msg: pointer to AP message
- * @crt: pointer to user input data
- *
- * Returns 0 on success or -EFAULT.
- */
-static int ICACRT_msg_to_type6CRT_msgX(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_rsa_modexpo_crt *crt)
-{
- static struct type6_hdr static_type6_hdrX = {
- .type = 0x06,
- .offset1 = 0x00000058,
- .agent_id = {'C','A',},
- .function_code = {'P','D'},
- };
- static struct function_and_rules_block static_pkd_fnr = {
- .function_code = {'P','D'},
- .ulen = 10,
- .only_rule = {'Z','E','R','O','-','P','A','D'}
- };
-
- static struct function_and_rules_block static_pkd_fnr_MCL2 = {
- .function_code = {'P','D'},
- .ulen = 10,
- .only_rule = {'P','K','C','S','-','1','.','2'}
- };
- struct {
- struct type6_hdr hdr;
- struct CPRBX cprbx;
- struct function_and_rules_block fr;
- unsigned short length;
- char text[0];
- } __attribute__((packed)) *msg = ap_msg->message;
- int size;
-
- /* VUD.ciphertext */
- msg->length = crt->inputdatalength + 2;
- if (copy_from_user(msg->text, crt->inputdata, crt->inputdatalength))
- return -EFAULT;
-
- /* Set up key which is located after the variable length text. */
- size = zcrypt_type6_crt_key(crt, msg->text + crt->inputdatalength, 1);
- if (size < 0)
- return size;
- size += sizeof(*msg) + crt->inputdatalength; /* total size of msg */
-
- /* message header, cprbx and f&r */
- msg->hdr = static_type6_hdrX;
- msg->hdr.ToCardLen1 = size - sizeof(msg->hdr);
- msg->hdr.FromCardLen1 = PCIXCC_MAX_ICA_RESPONSE_SIZE - sizeof(msg->hdr);
-
- msg->cprbx = static_cprbx;
- msg->cprbx.domain = AP_QID_QUEUE(zdev->ap_dev->qid);
- msg->cprbx.req_parml = msg->cprbx.rpl_msgbl =
- size - sizeof(msg->hdr) - sizeof(msg->cprbx);
-
- msg->fr = (zdev->user_space_type == ZCRYPT_PCIXCC_MCL2) ?
- static_pkd_fnr_MCL2 : static_pkd_fnr;
-
- ap_msg->length = size;
- return 0;
-}
-
-/**
- * Convert a XCRB message to a type6 CPRB message.
- *
- * @zdev: crypto device pointer
- * @ap_msg: pointer to AP message
- * @xcRB: pointer to user input data
- *
- * Returns 0 on success or -EFAULT, -EINVAL.
- */
-struct type86_fmt2_msg {
- struct type86_hdr hdr;
- struct type86_fmt2_ext fmt2;
-} __attribute__((packed));
-
-static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
- struct ap_message *ap_msg,
- struct ica_xcRB *xcRB)
-{
- static struct type6_hdr static_type6_hdrX = {
- .type = 0x06,
- .offset1 = 0x00000058,
- };
- struct {
- struct type6_hdr hdr;
- struct CPRBX cprbx;
- } __attribute__((packed)) *msg = ap_msg->message;
-
- int rcblen = CEIL4(xcRB->request_control_blk_length);
- int replylen;
- char *req_data = ap_msg->message + sizeof(struct type6_hdr) + rcblen;
- char *function_code;
-
- /* length checks */
- ap_msg->length = sizeof(struct type6_hdr) +
- CEIL4(xcRB->request_control_blk_length) +
- xcRB->request_data_length;
- if (ap_msg->length > PCIXCC_MAX_XCRB_MESSAGE_SIZE)
- return -EINVAL;
- replylen = sizeof(struct type86_fmt2_msg) +
- CEIL4(xcRB->reply_control_blk_length) +
- xcRB->reply_data_length;
- if (replylen > PCIXCC_MAX_XCRB_MESSAGE_SIZE)
- return -EINVAL;
-
- /* prepare type6 header */
- msg->hdr = static_type6_hdrX;
- memcpy(msg->hdr.agent_id , &(xcRB->agent_ID), sizeof(xcRB->agent_ID));
- msg->hdr.ToCardLen1 = xcRB->request_control_blk_length;
- if (xcRB->request_data_length) {
- msg->hdr.offset2 = msg->hdr.offset1 + rcblen;
- msg->hdr.ToCardLen2 = xcRB->request_data_length;
- }
- msg->hdr.FromCardLen1 = xcRB->reply_control_blk_length;
- msg->hdr.FromCardLen2 = xcRB->reply_data_length;
-
- /* prepare CPRB */
- if (copy_from_user(&(msg->cprbx), xcRB->request_control_blk_addr,
- xcRB->request_control_blk_length))
- return -EFAULT;
- if (msg->cprbx.cprb_len + sizeof(msg->hdr.function_code) >
- xcRB->request_control_blk_length)
- return -EINVAL;
- function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
- memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code));
-
- if (memcmp(function_code, "US", 2) == 0)
- ap_msg->special = 1;
- else
- ap_msg->special = 0;
-
- /* copy data block */
- if (xcRB->request_data_length &&
- copy_from_user(req_data, xcRB->request_data_address,
- xcRB->request_data_length))
- return -EFAULT;
- return 0;
-}
-
-/**
- * Prepare a type6 CPRB message for random number generation
- *
- * @ap_dev: AP device pointer
- * @ap_msg: pointer to AP message
- */
-static void rng_type6CPRB_msgX(struct ap_device *ap_dev,
- struct ap_message *ap_msg,
- unsigned random_number_length)
-{
- struct {
- struct type6_hdr hdr;
- struct CPRBX cprbx;
- char function_code[2];
- short int rule_length;
- char rule[8];
- short int verb_length;
- short int key_length;
- } __attribute__((packed)) *msg = ap_msg->message;
- static struct type6_hdr static_type6_hdrX = {
- .type = 0x06,
- .offset1 = 0x00000058,
- .agent_id = {'C', 'A'},
- .function_code = {'R', 'L'},
- .ToCardLen1 = sizeof *msg - sizeof(msg->hdr),
- .FromCardLen1 = sizeof *msg - sizeof(msg->hdr),
- };
- static struct CPRBX local_cprbx = {
- .cprb_len = 0x00dc,
- .cprb_ver_id = 0x02,
- .func_id = {0x54, 0x32},
- .req_parml = sizeof *msg - sizeof(msg->hdr) -
- sizeof(msg->cprbx),
- .rpl_msgbl = sizeof *msg - sizeof(msg->hdr),
- };
-
- msg->hdr = static_type6_hdrX;
- msg->hdr.FromCardLen2 = random_number_length,
- msg->cprbx = local_cprbx;
- msg->cprbx.rpl_datal = random_number_length,
- msg->cprbx.domain = AP_QID_QUEUE(ap_dev->qid);
- memcpy(msg->function_code, msg->hdr.function_code, 0x02);
- msg->rule_length = 0x0a;
- memcpy(msg->rule, "RANDOM ", 8);
- msg->verb_length = 0x02;
- msg->key_length = 0x02;
- ap_msg->length = sizeof *msg;
-}
-
-/**
- * Copy results from a type 86 ICA reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @data: pointer to user output data
- * @length: size of user output data
- *
- * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
- */
-struct type86x_reply {
- struct type86_hdr hdr;
- struct type86_fmt2_ext fmt2;
- struct CPRBX cprbx;
- unsigned char pad[4]; /* 4 byte function code/rules block ? */
- unsigned short length;
- char text[0];
-} __attribute__((packed));
-
-static int convert_type86_ica(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- static unsigned char static_pad[] = {
- 0x00,0x02,
- 0x1B,0x7B,0x5D,0xB5,0x75,0x01,0x3D,0xFD,
- 0x8D,0xD1,0xC7,0x03,0x2D,0x09,0x23,0x57,
- 0x89,0x49,0xB9,0x3F,0xBB,0x99,0x41,0x5B,
- 0x75,0x21,0x7B,0x9D,0x3B,0x6B,0x51,0x39,
- 0xBB,0x0D,0x35,0xB9,0x89,0x0F,0x93,0xA5,
- 0x0B,0x47,0xF1,0xD3,0xBB,0xCB,0xF1,0x9D,
- 0x23,0x73,0x71,0xFF,0xF3,0xF5,0x45,0xFB,
- 0x61,0x29,0x23,0xFD,0xF1,0x29,0x3F,0x7F,
- 0x17,0xB7,0x1B,0xA9,0x19,0xBD,0x57,0xA9,
- 0xD7,0x95,0xA3,0xCB,0xED,0x1D,0xDB,0x45,
- 0x7D,0x11,0xD1,0x51,0x1B,0xED,0x71,0xE9,
- 0xB1,0xD1,0xAB,0xAB,0x21,0x2B,0x1B,0x9F,
- 0x3B,0x9F,0xF7,0xF7,0xBD,0x63,0xEB,0xAD,
- 0xDF,0xB3,0x6F,0x5B,0xDB,0x8D,0xA9,0x5D,
- 0xE3,0x7D,0x77,0x49,0x47,0xF5,0xA7,0xFD,
- 0xAB,0x2F,0x27,0x35,0x77,0xD3,0x49,0xC9,
- 0x09,0xEB,0xB1,0xF9,0xBF,0x4B,0xCB,0x2B,
- 0xEB,0xEB,0x05,0xFF,0x7D,0xC7,0x91,0x8B,
- 0x09,0x83,0xB9,0xB9,0x69,0x33,0x39,0x6B,
- 0x79,0x75,0x19,0xBF,0xBB,0x07,0x1D,0xBD,
- 0x29,0xBF,0x39,0x95,0x93,0x1D,0x35,0xC7,
- 0xC9,0x4D,0xE5,0x97,0x0B,0x43,0x9B,0xF1,
- 0x16,0x93,0x03,0x1F,0xA5,0xFB,0xDB,0xF3,
- 0x27,0x4F,0x27,0x61,0x05,0x1F,0xB9,0x23,
- 0x2F,0xC3,0x81,0xA9,0x23,0x71,0x55,0x55,
- 0xEB,0xED,0x41,0xE5,0xF3,0x11,0xF1,0x43,
- 0x69,0x03,0xBD,0x0B,0x37,0x0F,0x51,0x8F,
- 0x0B,0xB5,0x89,0x5B,0x67,0xA9,0xD9,0x4F,
- 0x01,0xF9,0x21,0x77,0x37,0x73,0x79,0xC5,
- 0x7F,0x51,0xC1,0xCF,0x97,0xA1,0x75,0xAD,
- 0x35,0x9D,0xD3,0xD3,0xA7,0x9D,0x5D,0x41,
- 0x6F,0x65,0x1B,0xCF,0xA9,0x87,0x91,0x09
- };
- struct type86x_reply *msg = reply->message;
- unsigned short service_rc, service_rs;
- unsigned int reply_len, pad_len;
- char *data;
-
- service_rc = msg->cprbx.ccp_rtcode;
- if (unlikely(service_rc != 0)) {
- service_rs = msg->cprbx.ccp_rscode;
- if (service_rc == 8 && service_rs == 66)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 65)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 770)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 783) {
- zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD;
- return -EAGAIN;
- }
- if (service_rc == 12 && service_rs == 769)
- return -EINVAL;
- if (service_rc == 8 && service_rs == 72)
- return -EINVAL;
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
- data = msg->text;
- reply_len = msg->length - 2;
- if (reply_len > outputdatalength)
- return -EINVAL;
- /*
- * For all encipher requests, the length of the ciphertext (reply_len)
- * will always equal the modulus length. For MEX decipher requests
- * the output needs to get padded. Minimum pad size is 10.
- *
- * Currently, the cases where padding will be added is for:
- * - PCIXCC_MCL2 using a CRT form token (since PKD didn't support
- * ZERO-PAD and CRT is only supported for PKD requests)
- * - PCICC, always
- */
- pad_len = outputdatalength - reply_len;
- if (pad_len > 0) {
- if (pad_len < 10)
- return -EINVAL;
- /* 'restore' padding left in the PCICC/PCIXCC card. */
- if (copy_to_user(outputdata, static_pad, pad_len - 1))
- return -EFAULT;
- if (put_user(0, outputdata + pad_len - 1))
- return -EFAULT;
- }
- /* Copy the crypto response to user space. */
- if (copy_to_user(outputdata + pad_len, data, reply_len))
- return -EFAULT;
- return 0;
-}
-
-/**
- * Copy results from a type 86 XCRB reply message back to user space.
- *
- * @zdev: crypto device pointer
- * @reply: reply AP message.
- * @xcRB: pointer to XCRB
- *
- * Returns 0 on success or -EINVAL, -EFAULT, -EAGAIN in case of an error.
- */
-static int convert_type86_xcrb(struct zcrypt_device *zdev,
- struct ap_message *reply,
- struct ica_xcRB *xcRB)
-{
- struct type86_fmt2_msg *msg = reply->message;
- char *data = reply->message;
-
- /* Copy CPRB to user */
- if (copy_to_user(xcRB->reply_control_blk_addr,
- data + msg->fmt2.offset1, msg->fmt2.count1))
- return -EFAULT;
- xcRB->reply_control_blk_length = msg->fmt2.count1;
-
- /* Copy data buffer to user */
- if (msg->fmt2.count2)
- if (copy_to_user(xcRB->reply_data_addr,
- data + msg->fmt2.offset2, msg->fmt2.count2))
- return -EFAULT;
- xcRB->reply_data_length = msg->fmt2.count2;
- return 0;
-}
-
-static int convert_type86_rng(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char *buffer)
-{
- struct {
- struct type86_hdr hdr;
- struct type86_fmt2_ext fmt2;
- struct CPRBX cprbx;
- } __attribute__((packed)) *msg = reply->message;
- char *data = reply->message;
-
- if (msg->cprbx.ccp_rtcode != 0 || msg->cprbx.ccp_rscode != 0)
- return -EINVAL;
- memcpy(buffer, data + msg->fmt2.offset2, msg->fmt2.count2);
- return msg->fmt2.count2;
-}
-
-static int convert_response_ica(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char __user *outputdata,
- unsigned int outputdatalength)
-{
- struct type86x_reply *msg = reply->message;
-
- /* Response type byte is the second byte in the response. */
- switch (((unsigned char *) reply->message)[1]) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- return convert_error(zdev, reply);
- case TYPE86_RSP_CODE:
- if (msg->cprbx.ccp_rtcode &&
- (msg->cprbx.ccp_rscode == 0x14f) &&
- (outputdatalength > 256)) {
- if (zdev->max_exp_bit_length <= 17) {
- zdev->max_exp_bit_length = 17;
- return -EAGAIN;
- } else
- return -EINVAL;
- }
- if (msg->hdr.reply_code)
- return convert_error(zdev, reply);
- if (msg->cprbx.cprb_ver_id == 0x02)
- return convert_type86_ica(zdev, reply,
- outputdata, outputdatalength);
- /* Fall through, no break, incorrect cprb version is an unknown
- * response */
- default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
-}
-
-static int convert_response_xcrb(struct zcrypt_device *zdev,
- struct ap_message *reply,
- struct ica_xcRB *xcRB)
-{
- struct type86x_reply *msg = reply->message;
-
- /* Response type byte is the second byte in the response. */
- switch (((unsigned char *) reply->message)[1]) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
- return convert_error(zdev, reply);
- case TYPE86_RSP_CODE:
- if (msg->hdr.reply_code) {
- memcpy(&(xcRB->status), msg->fmt2.apfs, sizeof(u32));
- return convert_error(zdev, reply);
- }
- if (msg->cprbx.cprb_ver_id == 0x02)
- return convert_type86_xcrb(zdev, reply, xcRB);
- /* Fall through, no break, incorrect cprb version is an unknown
- * response */
- default: /* Unknown response type, this should NEVER EVER happen */
- xcRB->status = 0x0008044DL; /* HDD_InvalidParm */
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
-}
-
-static int convert_response_rng(struct zcrypt_device *zdev,
- struct ap_message *reply,
- char *data)
-{
- struct type86x_reply *msg = reply->message;
-
- switch (msg->hdr.type) {
- case TYPE82_RSP_CODE:
- case TYPE88_RSP_CODE:
- return -EINVAL;
- case TYPE86_RSP_CODE:
- if (msg->hdr.reply_code)
- return -EINVAL;
- if (msg->cprbx.cprb_ver_id == 0x02)
- return convert_type86_rng(zdev, reply, data);
- /* Fall through, no break, incorrect cprb version is an unknown
- * response */
- default: /* Unknown response type, this should NEVER EVER happen */
- zdev->online = 0;
- return -EAGAIN; /* repeat the request on a different device. */
- }
-}
-
-/**
- * This function is called from the AP bus code after a crypto request
- * "msg" has finished with the reply message "reply".
- * It is called from tasklet context.
- * @ap_dev: pointer to the AP device
- * @msg: pointer to the AP message
- * @reply: pointer to the AP reply message
- */
-static void zcrypt_pcixcc_receive(struct ap_device *ap_dev,
- struct ap_message *msg,
- struct ap_message *reply)
-{
- static struct error_hdr error_reply = {
- .type = TYPE82_RSP_CODE,
- .reply_code = REP82_ERROR_MACHINE_FAILURE,
- };
- struct response_type *resp_type =
- (struct response_type *) msg->private;
- struct type86x_reply *t86r;
- int length;
-
- /* Copy the reply message to the request message buffer. */
- if (IS_ERR(reply)) {
- memcpy(msg->message, &error_reply, sizeof(error_reply));
- goto out;
- }
- t86r = reply->message;
- if (t86r->hdr.type == TYPE86_RSP_CODE &&
- t86r->cprbx.cprb_ver_id == 0x02) {
- switch (resp_type->type) {
- case PCIXCC_RESPONSE_TYPE_ICA:
- length = sizeof(struct type86x_reply)
- + t86r->length - 2;
- length = min(PCIXCC_MAX_ICA_RESPONSE_SIZE, length);
- memcpy(msg->message, reply->message, length);
- break;
- case PCIXCC_RESPONSE_TYPE_XCRB:
- length = t86r->fmt2.offset2 + t86r->fmt2.count2;
- length = min(PCIXCC_MAX_XCRB_MESSAGE_SIZE, length);
- memcpy(msg->message, reply->message, length);
- break;
- default:
- memcpy(msg->message, &error_reply, sizeof error_reply);
- }
- } else
- memcpy(msg->message, reply->message, sizeof error_reply);
-out:
- complete(&(resp_type->work));
-}
-
-static atomic_t zcrypt_step = ATOMIC_INIT(0);
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to handle a modexpo request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCIXCC/CEX2C device to the request distributor
- * @mex: pointer to the modexpo request buffer
- */
-static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo *mex)
-{
- struct ap_message ap_msg;
- struct response_type resp_type = {
- .type = PCIXCC_RESPONSE_TYPE_ICA,
- };
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcixcc_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &resp_type;
- rc = ICAMEX_msg_to_type6MEX_msgX(zdev, &ap_msg, mex);
- if (rc)
- goto out_free;
- init_completion(&resp_type.work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_ica(zdev, &ap_msg, mex->outputdata,
- mex->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- free_page((unsigned long) ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to handle a modexpo_crt request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCIXCC/CEX2C device to the request distributor
- * @crt: pointer to the modexpoc_crt request buffer
- */
-static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev,
- struct ica_rsa_modexpo_crt *crt)
-{
- struct ap_message ap_msg;
- struct response_type resp_type = {
- .type = PCIXCC_RESPONSE_TYPE_ICA,
- };
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcixcc_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &resp_type;
- rc = ICACRT_msg_to_type6CRT_msgX(zdev, &ap_msg, crt);
- if (rc)
- goto out_free;
- init_completion(&resp_type.work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_ica(zdev, &ap_msg, crt->outputdata,
- crt->outputdatalength);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- free_page((unsigned long) ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to handle a send_cprb request.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCIXCC/CEX2C device to the request distributor
- * @xcRB: pointer to the send_cprb request buffer
- */
-static long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev,
- struct ica_xcRB *xcRB)
-{
- struct ap_message ap_msg;
- struct response_type resp_type = {
- .type = PCIXCC_RESPONSE_TYPE_XCRB,
- };
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcixcc_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &resp_type;
- rc = XCRB_msg_to_type6CPRB_msgX(zdev, &ap_msg, xcRB);
- if (rc)
- goto out_free;
- init_completion(&resp_type.work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_xcrb(zdev, &ap_msg, xcRB);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
-out_free:
- kzfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The request distributor calls this function if it picked the PCIXCC/CEX2C
- * device to generate random data.
- * @zdev: pointer to zcrypt_device structure that identifies the
- * PCIXCC/CEX2C device to the request distributor
- * @buffer: pointer to a memory page to return random data
- */
-
-static long zcrypt_pcixcc_rng(struct zcrypt_device *zdev,
- char *buffer)
-{
- struct ap_message ap_msg;
- struct response_type resp_type = {
- .type = PCIXCC_RESPONSE_TYPE_XCRB,
- };
- int rc;
-
- ap_init_message(&ap_msg);
- ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
- if (!ap_msg.message)
- return -ENOMEM;
- ap_msg.receive = zcrypt_pcixcc_receive;
- ap_msg.psmid = (((unsigned long long) current->pid) << 32) +
- atomic_inc_return(&zcrypt_step);
- ap_msg.private = &resp_type;
- rng_type6CPRB_msgX(zdev->ap_dev, &ap_msg, ZCRYPT_RNG_BUFFER_SIZE);
- init_completion(&resp_type.work);
- ap_queue_message(zdev->ap_dev, &ap_msg);
- rc = wait_for_completion_interruptible(&resp_type.work);
- if (rc == 0)
- rc = convert_response_rng(zdev, &ap_msg, buffer);
- else
- /* Signal pending. */
- ap_cancel_message(zdev->ap_dev, &ap_msg);
- kfree(ap_msg.message);
- return rc;
-}
-
-/**
- * The crypto operations for a PCIXCC/CEX2C card.
- */
-static struct zcrypt_ops zcrypt_pcixcc_ops = {
- .rsa_modexpo = zcrypt_pcixcc_modexpo,
- .rsa_modexpo_crt = zcrypt_pcixcc_modexpo_crt,
- .send_cprb = zcrypt_pcixcc_send_cprb,
-};
-
-static struct zcrypt_ops zcrypt_pcixcc_with_rng_ops = {
- .rsa_modexpo = zcrypt_pcixcc_modexpo,
- .rsa_modexpo_crt = zcrypt_pcixcc_modexpo_crt,
- .send_cprb = zcrypt_pcixcc_send_cprb,
- .rng = zcrypt_pcixcc_rng,
-};
-
-/**
* Micro-code detection function. Its sends a message to a pcixcc card
* to find out the microcode level.
* @ap_dev: pointer to the AP device.
@@ -1083,9 +323,11 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
return rc;
}
if (rc)
- zdev->ops = &zcrypt_pcixcc_with_rng_ops;
+ zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
+ MSGTYPE06_VARIANT_DEFAULT);
else
- zdev->ops = &zcrypt_pcixcc_ops;
+ zdev->ops = zcrypt_msgtype_request(MSGTYPE06_NAME,
+ MSGTYPE06_VARIANT_NORNG);
ap_dev->reply = &zdev->reply;
ap_dev->private = zdev;
rc = zcrypt_device_register(zdev);
@@ -1095,6 +337,7 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
out_free:
ap_dev->private = NULL;
+ zcrypt_msgtype_release(zdev->ops);
zcrypt_device_free(zdev);
return rc;
}
@@ -1106,8 +349,10 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
static void zcrypt_pcixcc_remove(struct ap_device *ap_dev)
{
struct zcrypt_device *zdev = ap_dev->private;
+ struct zcrypt_ops *zops = zdev->ops;
zcrypt_device_unregister(zdev);
+ zcrypt_msgtype_release(zops);
}
int __init zcrypt_pcixcc_init(void)
diff --git a/drivers/s390/crypto/zcrypt_pcixcc.h b/drivers/s390/crypto/zcrypt_pcixcc.h
index c7cdf599e46b..eacafc8962f2 100644
--- a/drivers/s390/crypto/zcrypt_pcixcc.h
+++ b/drivers/s390/crypto/zcrypt_pcixcc.h
@@ -1,12 +1,13 @@
/*
* zcrypt 2.1.0
*
- * Copyright IBM Corp. 2001, 2006
+ * Copyright IBM Corp. 2001, 2012
* Author(s): Robert Burroughs
* Eric Rossman (edrossma@us.ibm.com)
*
* Hotplug & misc device support: Jochen Roehrig (roehrig@de.ibm.com)
* Major cleanup & driver split: Martin Schwidefsky <schwidefsky@de.ibm.com>
+ * MSGTYPE restruct: Holger Dengler <hd@linux.vnet.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c
index 7a8b09612c41..cf6da7fafe54 100644
--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -2993,7 +2993,7 @@ static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid)
struct sysinfo_2_2_2 *info222 = (struct sysinfo_2_2_2 *)info;
struct sysinfo_3_2_2 *info322 = (struct sysinfo_3_2_2 *)info;
struct ccw_dev_id ccwid;
- int level, rc;
+ int level;
tid->chpid = card->info.chpid;
ccw_device_get_id(CARD_RDEV(card), &ccwid);
@@ -3001,17 +3001,10 @@ static void qeth_get_trap_id(struct qeth_card *card, struct qeth_trap_id *tid)
tid->devno = ccwid.devno;
if (!info)
return;
-
- rc = stsi(NULL, 0, 0, 0);
- if (rc == -ENOSYS)
- level = rc;
- else
- level = (((unsigned int) rc) >> 28);
-
- if ((level >= 2) && (stsi(info222, 2, 2, 2) != -ENOSYS))
+ level = stsi(NULL, 0, 0, 0);
+ if ((level >= 2) && (stsi(info222, 2, 2, 2) == 0))
tid->lparnr = info222->lpar_number;
-
- if ((level >= 3) && (stsi(info322, 3, 2, 2) != -ENOSYS)) {
+ if ((level >= 3) && (stsi(info322, 3, 2, 2) == 0)) {
EBCASC(info322->vm[0].name, sizeof(info322->vm[0].name));
memcpy(tid->vmname, info322->vm[0].name, sizeof(tid->vmname));
}
diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c
index 25417d0e7acb..0bcacf71aef8 100644
--- a/drivers/scsi/aic7xxx/aic79xx_core.c
+++ b/drivers/scsi/aic7xxx/aic79xx_core.c
@@ -2888,7 +2888,7 @@ ahd_handle_lqiphase_error(struct ahd_softc *ahd, u_int lqistat1)
ahd_outb(ahd, CLRINT, CLRSCSIINT);
ahd_unpause(ahd);
} else {
- printk("Reseting Channel for LQI Phase error\n");
+ printk("Resetting Channel for LQI Phase error\n");
ahd_dump_card_state(ahd);
ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
}
diff --git a/drivers/scsi/arm/eesox.c b/drivers/scsi/arm/eesox.c
index edfd12b48c28..968d08358d20 100644
--- a/drivers/scsi/arm/eesox.c
+++ b/drivers/scsi/arm/eesox.c
@@ -273,7 +273,7 @@ static void eesoxscsi_buffer_out(void *buf, int length, void __iomem *base)
{
const void __iomem *reg_fas = base + EESOX_FAS216_OFFSET;
const void __iomem *reg_dmastat = base + EESOX_DMASTAT;
- const void __iomem *reg_dmadata = base + EESOX_DMADATA;
+ void __iomem *reg_dmadata = base + EESOX_DMADATA;
do {
unsigned int status;
diff --git a/drivers/scsi/bfa/bfa_ioc.c b/drivers/scsi/bfa/bfa_ioc.c
index 8cdb79c2fcdf..21ad2902e5ce 100644
--- a/drivers/scsi/bfa/bfa_ioc.c
+++ b/drivers/scsi/bfa/bfa_ioc.c
@@ -5587,7 +5587,7 @@ static bfa_status_t bfa_dconf_flash_write(struct bfa_dconf_mod_s *dconf);
static void bfa_dconf_init_cb(void *arg, bfa_status_t status);
/*
- * Begining state of dconf module. Waiting for an event to start.
+ * Beginning state of dconf module. Waiting for an event to start.
*/
static void
bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
diff --git a/drivers/scsi/bfa/bfa_ioc.h b/drivers/scsi/bfa/bfa_ioc.h
index 1a99d4b5b50f..7b916e04ca56 100644
--- a/drivers/scsi/bfa/bfa_ioc.h
+++ b/drivers/scsi/bfa/bfa_ioc.h
@@ -530,7 +530,7 @@ struct bfa_diag_results_fwping {
struct bfa_diag_qtest_result_s {
u32 status;
- u16 count; /* sucessful queue test count */
+ u16 count; /* successful queue test count */
u8 queue;
u8 rsvd; /* 64-bit align */
};
diff --git a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
index ae1cb7639d99..e0558656c646 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_fcoe.c
@@ -908,7 +908,7 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event,
return;
default:
- printk(KERN_ERR PFX "Unkonwn netevent %ld", event);
+ printk(KERN_ERR PFX "Unknown netevent %ld", event);
return;
}
@@ -1738,7 +1738,7 @@ static int bnx2fc_ulp_get_stats(void *handle)
/**
* bnx2fc_ulp_start - cnic callback to initialize & start adapter instance
*
- * @handle: transport handle pointing to adapter struture
+ * @handle: transport handle pointing to adapter structure
*
* This function maps adapter structure to pcidev structure and initiates
* firmware handshake to enable/initialize on-chip FCoE components.
diff --git a/drivers/scsi/bnx2i/bnx2i_hwi.c b/drivers/scsi/bnx2i/bnx2i_hwi.c
index 33d6630529de..91eec60252ee 100644
--- a/drivers/scsi/bnx2i/bnx2i_hwi.c
+++ b/drivers/scsi/bnx2i/bnx2i_hwi.c
@@ -1264,6 +1264,9 @@ int bnx2i_send_fw_iscsi_init_msg(struct bnx2i_hba *hba)
int rc = 0;
u64 mask64;
+ memset(&iscsi_init, 0x00, sizeof(struct iscsi_kwqe_init1));
+ memset(&iscsi_init2, 0x00, sizeof(struct iscsi_kwqe_init2));
+
bnx2i_adjust_qp_size(hba);
iscsi_init.flags =
diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h
index d3e4d7c6f577..fbf6f0f4b0dd 100644
--- a/drivers/scsi/gdth.h
+++ b/drivers/scsi/gdth.h
@@ -49,15 +49,6 @@
/* GDT_ISA */
#define GDT2_ID 0x0120941c /* GDT2000/2020 */
-/* vendor ID, device IDs (PCI) */
-/* these defines should already exist in <linux/pci.h> */
-#ifndef PCI_VENDOR_ID_VORTEX
-#define PCI_VENDOR_ID_VORTEX 0x1119 /* PCI controller vendor ID */
-#endif
-#ifndef PCI_VENDOR_ID_INTEL
-#define PCI_VENDOR_ID_INTEL 0x8086
-#endif
-
#ifndef PCI_DEVICE_ID_VORTEX_GDT60x0
/* GDT_PCI */
#define PCI_DEVICE_ID_VORTEX_GDT60x0 0 /* GDT6000/6020/6050 */
diff --git a/drivers/scsi/hpsa.c b/drivers/scsi/hpsa.c
index 796482badf13..2b4261cb7742 100644
--- a/drivers/scsi/hpsa.c
+++ b/drivers/scsi/hpsa.c
@@ -1315,8 +1315,9 @@ static void complete_scsi_command(struct CommandList *cp)
}
break;
case CMD_PROTOCOL_ERR:
+ cmd->result = DID_ERROR << 16;
dev_warn(&h->pdev->dev, "cp %p has "
- "protocol error \n", cp);
+ "protocol error\n", cp);
break;
case CMD_HARDWARE_ERR:
cmd->result = DID_ERROR << 16;
diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c
index 467dc38246f9..0a2c5a8ebb82 100644
--- a/drivers/scsi/ipr.c
+++ b/drivers/scsi/ipr.c
@@ -192,7 +192,7 @@ static const struct ipr_chip_t ipr_chip[] = {
{ PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE, IPR_USE_MSI, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] }
};
-static int ipr_max_bus_speeds [] = {
+static int ipr_max_bus_speeds[] = {
IPR_80MBs_SCSI_RATE, IPR_U160_SCSI_RATE, IPR_U320_SCSI_RATE
};
@@ -562,7 +562,7 @@ static void ipr_trc_hook(struct ipr_cmnd *ipr_cmd,
trace_entry->u.add_data = add_data;
}
#else
-#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while(0)
+#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while (0)
#endif
/**
@@ -1002,7 +1002,7 @@ static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type,
**/
static void ipr_update_ata_class(struct ipr_resource_entry *res, unsigned int proto)
{
- switch(proto) {
+ switch (proto) {
case IPR_PROTO_SATA:
case IPR_PROTO_SAS_STP:
res->ata_class = ATA_DEV_ATA;
@@ -3043,7 +3043,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
}
#else
-#define ipr_get_ioa_dump(ioa_cfg, dump) do { } while(0)
+#define ipr_get_ioa_dump(ioa_cfg, dump) do { } while (0)
#endif
/**
@@ -3055,7 +3055,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
**/
static void ipr_release_dump(struct kref *kref)
{
- struct ipr_dump *dump = container_of(kref,struct ipr_dump,kref);
+ struct ipr_dump *dump = container_of(kref, struct ipr_dump, kref);
struct ipr_ioa_cfg *ioa_cfg = dump->ioa_cfg;
unsigned long lock_flags = 0;
int i;
@@ -3142,7 +3142,7 @@ restart:
break;
}
}
- } while(did_work);
+ } while (did_work);
list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
if (res->add_to_ml) {
@@ -3268,7 +3268,7 @@ static ssize_t ipr_show_log_level(struct device *dev,
* number of bytes printed to buffer
**/
static ssize_t ipr_store_log_level(struct device *dev,
- struct device_attribute *attr,
+ struct device_attribute *attr,
const char *buf, size_t count)
{
struct Scsi_Host *shost = class_to_shost(dev);
@@ -3315,7 +3315,7 @@ static ssize_t ipr_store_diagnostics(struct device *dev,
return -EACCES;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -3682,7 +3682,7 @@ static int ipr_update_ioa_ucode(struct ipr_ioa_cfg *ioa_cfg,
unsigned long lock_flags;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -3746,7 +3746,7 @@ static ssize_t ipr_store_update_fw(struct device *dev,
len = snprintf(fname, 99, "%s", buf);
fname[len-1] = '\0';
- if(request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) {
+ if (request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) {
dev_err(&ioa_cfg->pdev->dev, "Firmware file %s not found\n", fname);
return -EIO;
}
@@ -4612,7 +4612,7 @@ static int ipr_slave_alloc(struct scsi_device *sdev)
* Return value:
* SUCCESS / FAILED
**/
-static int __ipr_eh_host_reset(struct scsi_cmnd * scsi_cmd)
+static int __ipr_eh_host_reset(struct scsi_cmnd *scsi_cmd)
{
struct ipr_ioa_cfg *ioa_cfg;
int rc;
@@ -4634,7 +4634,7 @@ static int __ipr_eh_host_reset(struct scsi_cmnd * scsi_cmd)
return rc;
}
-static int ipr_eh_host_reset(struct scsi_cmnd * cmd)
+static int ipr_eh_host_reset(struct scsi_cmnd *cmd)
{
int rc;
@@ -4701,7 +4701,7 @@ static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg,
}
LEAVE;
- return (IPR_IOASC_SENSE_KEY(ioasc) ? -EIO : 0);
+ return IPR_IOASC_SENSE_KEY(ioasc) ? -EIO : 0;
}
/**
@@ -4725,7 +4725,7 @@ static int ipr_sata_reset(struct ata_link *link, unsigned int *classes,
ENTER;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -4753,7 +4753,7 @@ static int ipr_sata_reset(struct ata_link *link, unsigned int *classes,
* Return value:
* SUCCESS / FAILED
**/
-static int __ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd)
+static int __ipr_eh_dev_reset(struct scsi_cmnd *scsi_cmd)
{
struct ipr_cmnd *ipr_cmd;
struct ipr_ioa_cfg *ioa_cfg;
@@ -4811,10 +4811,10 @@ static int __ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd)
res->resetting_device = 0;
LEAVE;
- return (rc ? FAILED : SUCCESS);
+ return rc ? FAILED : SUCCESS;
}
-static int ipr_eh_dev_reset(struct scsi_cmnd * cmd)
+static int ipr_eh_dev_reset(struct scsi_cmnd *cmd)
{
int rc;
@@ -4910,7 +4910,7 @@ static void ipr_abort_timeout(struct ipr_cmnd *ipr_cmd)
* Return value:
* SUCCESS / FAILED
**/
-static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
+static int ipr_cancel_op(struct scsi_cmnd *scsi_cmd)
{
struct ipr_cmnd *ipr_cmd;
struct ipr_ioa_cfg *ioa_cfg;
@@ -4979,7 +4979,7 @@ static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
res->needs_sync_complete = 1;
LEAVE;
- return (IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS);
+ return IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS;
}
/**
@@ -4989,7 +4989,7 @@ static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
* Return value:
* SUCCESS / FAILED
**/
-static int ipr_eh_abort(struct scsi_cmnd * scsi_cmd)
+static int ipr_eh_abort(struct scsi_cmnd *scsi_cmd)
{
unsigned long flags;
int rc;
@@ -5907,7 +5907,7 @@ static int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
* Return value:
* pointer to buffer with description string
**/
-static const char * ipr_ioa_info(struct Scsi_Host *host)
+static const char *ipr_ioa_info(struct Scsi_Host *host)
{
static char buffer[512];
struct ipr_ioa_cfg *ioa_cfg;
@@ -5965,7 +5965,7 @@ static void ipr_ata_phy_reset(struct ata_port *ap)
ENTER;
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
@@ -6005,7 +6005,7 @@ static void ipr_ata_post_internal(struct ata_queued_cmd *qc)
unsigned long flags;
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
@@ -6330,7 +6330,7 @@ static int ipr_invalid_adapter(struct ipr_ioa_cfg *ioa_cfg)
int i;
if ((ioa_cfg->type == 0x5702) && (ioa_cfg->pdev->revision < 4)) {
- for (i = 0; i < ARRAY_SIZE(ipr_blocked_processors); i++){
+ for (i = 0; i < ARRAY_SIZE(ipr_blocked_processors); i++) {
if (__is_processor(ipr_blocked_processors[i]))
return 1;
}
@@ -6608,7 +6608,7 @@ static void ipr_scsi_bus_speed_limit(struct ipr_ioa_cfg *ioa_cfg)
* none
**/
static void ipr_modify_ioafp_mode_page_28(struct ipr_ioa_cfg *ioa_cfg,
- struct ipr_mode_pages *mode_pages)
+ struct ipr_mode_pages *mode_pages)
{
int i, entry_length;
struct ipr_dev_bus_entry *bus;
@@ -8022,7 +8022,7 @@ static void ipr_reset_ioa_job(struct ipr_cmnd *ipr_cmd)
ipr_reinit_ipr_cmnd(ipr_cmd);
ipr_cmd->job_step_failed = ipr_reset_cmd_failed;
rc = ipr_cmd->job_step(ipr_cmd);
- } while(rc == IPR_RC_JOB_CONTINUE);
+ } while (rc == IPR_RC_JOB_CONTINUE);
}
/**
@@ -8283,7 +8283,7 @@ static void ipr_free_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
}
if (ioa_cfg->ipr_cmd_pool)
- pci_pool_destroy (ioa_cfg->ipr_cmd_pool);
+ pci_pool_destroy(ioa_cfg->ipr_cmd_pool);
kfree(ioa_cfg->ipr_cmnd_list);
kfree(ioa_cfg->ipr_cmnd_list_dma);
@@ -8363,8 +8363,8 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
dma_addr_t dma_addr;
int i;
- ioa_cfg->ipr_cmd_pool = pci_pool_create (IPR_NAME, ioa_cfg->pdev,
- sizeof(struct ipr_cmnd), 512, 0);
+ ioa_cfg->ipr_cmd_pool = pci_pool_create(IPR_NAME, ioa_cfg->pdev,
+ sizeof(struct ipr_cmnd), 512, 0);
if (!ioa_cfg->ipr_cmd_pool)
return -ENOMEM;
@@ -8378,7 +8378,7 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
}
for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
- ipr_cmd = pci_pool_alloc (ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
+ ipr_cmd = pci_pool_alloc(ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
if (!ipr_cmd) {
ipr_free_cmd_blks(ioa_cfg);
@@ -8964,7 +8964,7 @@ static void ipr_scan_vsets(struct ipr_ioa_cfg *ioa_cfg)
int target, lun;
for (target = 0; target < IPR_MAX_NUM_TARGETS_PER_BUS; target++)
- for (lun = 0; lun < IPR_MAX_NUM_VSET_LUNS_PER_TARGET; lun++ )
+ for (lun = 0; lun < IPR_MAX_NUM_VSET_LUNS_PER_TARGET; lun++)
scsi_add_device(ioa_cfg->host, IPR_VSET_BUS, target, lun);
}
@@ -9010,7 +9010,7 @@ static void __ipr_remove(struct pci_dev *pdev)
ENTER;
spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
@@ -9139,7 +9139,7 @@ static void ipr_shutdown(struct pci_dev *pdev)
unsigned long lock_flags = 0;
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
- while(ioa_cfg->in_reset_reload) {
+ while (ioa_cfg->in_reset_reload) {
spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -9228,7 +9228,7 @@ static struct pci_device_id ipr_pci_table[] __devinitdata = {
};
MODULE_DEVICE_TABLE(pci, ipr_pci_table);
-static struct pci_error_handlers ipr_err_handler = {
+static const struct pci_error_handlers ipr_err_handler = {
.error_detected = ipr_pci_error_detected,
.slot_reset = ipr_pci_slot_reset,
};
diff --git a/drivers/scsi/isci/host.c b/drivers/scsi/isci/host.c
index 45385f531649..b334fdc1726a 100644
--- a/drivers/scsi/isci/host.c
+++ b/drivers/scsi/isci/host.c
@@ -492,7 +492,7 @@ static void sci_controller_process_completions(struct isci_host *ihost)
u32 event_cycle;
dev_dbg(&ihost->pdev->dev,
- "%s: completion queue begining get:0x%08x\n",
+ "%s: completion queue beginning get:0x%08x\n",
__func__,
ihost->completion_queue_get);
diff --git a/drivers/scsi/isci/init.c b/drivers/scsi/isci/init.c
index 92c1d86d1fc6..9be45a2b2232 100644
--- a/drivers/scsi/isci/init.c
+++ b/drivers/scsi/isci/init.c
@@ -222,7 +222,7 @@ static struct sas_domain_function_template isci_transport_ops = {
* @isci_host: This parameter specifies the lldd specific wrapper for the
* libsas sas_ha struct.
*
- * This method returns an error code indicating sucess or failure. The user
+ * This method returns an error code indicating success or failure. The user
* should check for possible memory allocation error return otherwise, a zero
* indicates success.
*/
diff --git a/drivers/scsi/isci/port.c b/drivers/scsi/isci/port.c
index 2fb85bf75449..13098b09a824 100644
--- a/drivers/scsi/isci/port.c
+++ b/drivers/scsi/isci/port.c
@@ -212,7 +212,7 @@ static void isci_port_link_up(struct isci_host *isci_host,
memcpy(iphy->sas_phy.attached_sas_addr,
iphy->frame_rcvd.iaf.sas_addr, SAS_ADDR_SIZE);
} else {
- dev_err(&isci_host->pdev->dev, "%s: unkown target\n", __func__);
+ dev_err(&isci_host->pdev->dev, "%s: unknown target\n", __func__);
success = false;
}
diff --git a/drivers/scsi/isci/request.c b/drivers/scsi/isci/request.c
index 7a0431c73493..c1bafc3f3fb1 100644
--- a/drivers/scsi/isci/request.c
+++ b/drivers/scsi/isci/request.c
@@ -2240,7 +2240,7 @@ static enum sci_status atapi_data_tc_completion_handler(struct isci_request *ire
status = ireq->sci_status;
sci_change_state(&idev->sm, SCI_STP_DEV_ATAPI_ERROR);
} else {
- /* If receiving any non-sucess TC status, no UF
+ /* If receiving any non-success TC status, no UF
* received yet, then an UF for the status fis
* is coming after (XXX: suspect this is
* actually a protocol error or a bug like the
diff --git a/drivers/scsi/isci/task.c b/drivers/scsi/isci/task.c
index 6bc74eb012c9..b6f19a1db780 100644
--- a/drivers/scsi/isci/task.c
+++ b/drivers/scsi/isci/task.c
@@ -532,7 +532,7 @@ int isci_task_abort_task(struct sas_task *task)
/* The request has already completed and there
* is nothing to do here other than to set the task
* done bit, and indicate that the task abort function
- * was sucessful.
+ * was successful.
*/
spin_lock_irqsave(&task->task_state_lock, flags);
task->task_state_flags |= SAS_TASK_STATE_DONE;
diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c
index 45c15208be9f..628a703abddb 100644
--- a/drivers/scsi/lpfc/lpfc_init.c
+++ b/drivers/scsi/lpfc/lpfc_init.c
@@ -6607,7 +6607,7 @@ out_error:
* we just use some constant number as place holder.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* -ENOMEM - No availble memory
* -EIO - The mailbox failed to complete successfully.
**/
@@ -10425,7 +10425,7 @@ static struct pci_device_id lpfc_id_table[] = {
MODULE_DEVICE_TABLE(pci, lpfc_id_table);
-static struct pci_error_handlers lpfc_err_handler = {
+static const struct pci_error_handlers lpfc_err_handler = {
.error_detected = lpfc_io_error_detected,
.slot_reset = lpfc_io_slot_reset,
.resume = lpfc_io_resume,
diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c
index 9cbd20b1328b..0e7e144507b2 100644
--- a/drivers/scsi/lpfc/lpfc_sli.c
+++ b/drivers/scsi/lpfc/lpfc_sli.c
@@ -4739,7 +4739,7 @@ lpfc_sli4_read_rev(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
* is attached to.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* otherwise - failed to retrieve physical port name
**/
static int
@@ -15209,7 +15209,7 @@ lpfc_check_next_fcf_pri_level(struct lpfc_hba *phba)
/*
* if next_fcf_pri was not set above and the list is not empty then
* we have failed flogis on all of them. So reset flogi failed
- * and start at the begining.
+ * and start at the beginning.
*/
if (!next_fcf_pri && !list_empty(&phba->fcf.fcf_pri_list)) {
list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) {
diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c
index 97825f116954..76ad72d32c3f 100644
--- a/drivers/scsi/megaraid.c
+++ b/drivers/scsi/megaraid.c
@@ -305,12 +305,11 @@ mega_query_adapter(adapter_t *adapter)
adapter->host->sg_tablesize = adapter->sglen;
-
/* use HP firmware and bios version encoding
Note: fw_version[0|1] and bios_version[0|1] were originally shifted
right 8 bits making them zero. This 0 value was hardcoded to fix
sparse warnings. */
- if (adapter->product_info.subsysvid == HP_SUBSYS_VID) {
+ if (adapter->product_info.subsysvid == PCI_VENDOR_ID_HP) {
sprintf (adapter->fw_version, "%c%d%d.%d%d",
adapter->product_info.fw_version[2],
0,
@@ -4716,7 +4715,7 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
* support, since this firmware cannot handle 64 bit
* addressing
*/
- if ((subsysvid == HP_SUBSYS_VID) &&
+ if ((subsysvid == PCI_VENDOR_ID_HP) &&
((subsysid == 0x60E7) || (subsysid == 0x60E8))) {
/*
* which firmware
diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h
index 9a7897f8ca43..4fb2adf6b80d 100644
--- a/drivers/scsi/megaraid.h
+++ b/drivers/scsi/megaraid.h
@@ -45,45 +45,10 @@
#define MAX_DEV_TYPE 32
-#ifndef PCI_VENDOR_ID_LSI_LOGIC
-#define PCI_VENDOR_ID_LSI_LOGIC 0x1000
-#endif
-
-#ifndef PCI_VENDOR_ID_AMI
-#define PCI_VENDOR_ID_AMI 0x101E
-#endif
-
-#ifndef PCI_VENDOR_ID_DELL
-#define PCI_VENDOR_ID_DELL 0x1028
-#endif
-
-#ifndef PCI_VENDOR_ID_INTEL
-#define PCI_VENDOR_ID_INTEL 0x8086
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID
-#define PCI_DEVICE_ID_AMI_MEGARAID 0x9010
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID2
-#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID3
-#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960
-#endif
-
#define PCI_DEVICE_ID_DISCOVERY 0x000E
#define PCI_DEVICE_ID_PERC4_DI 0x000F
#define PCI_DEVICE_ID_PERC4_QC_VERDE 0x0407
-/* Sub-System Vendor IDs */
-#define AMI_SUBSYS_VID 0x101E
-#define DELL_SUBSYS_VID 0x1028
-#define HP_SUBSYS_VID 0x103C
-#define LSI_SUBSYS_VID 0x1000
-#define INTEL_SUBSYS_VID 0x8086
-
#define HBA_SIGNATURE 0x3344
#define HBA_SIGNATURE_471 0xCCCC
#define HBA_SIGNATURE_64BIT 0x0299
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index b25757d1e91b..9d5a56c4b332 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -1209,6 +1209,13 @@ _base_check_enable_msix(struct MPT2SAS_ADAPTER *ioc)
u16 message_control;
+ /* Check whether controller SAS2008 B0 controller,
+ if it is SAS2008 B0 controller use IO-APIC instead of MSIX */
+ if (ioc->pdev->device == MPI2_MFGPAGE_DEVID_SAS2008 &&
+ ioc->pdev->revision == 0x01) {
+ return -EINVAL;
+ }
+
base = pci_find_capability(ioc->pdev, PCI_CAP_ID_MSIX);
if (!base) {
dfailprintk(ioc, printk(MPT2SAS_INFO_FMT "msix not "
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index b1ebd6f8dab3..1ccae45c5270 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -8306,7 +8306,7 @@ _scsih_pci_mmio_enabled(struct pci_dev *pdev)
return PCI_ERS_RESULT_NEED_RESET;
}
-static struct pci_error_handlers _scsih_err_handler = {
+static const struct pci_error_handlers _scsih_err_handler = {
.error_detected = _scsih_pci_error_detected,
.mmio_enabled = _scsih_pci_mmio_enabled,
.slot_reset = _scsih_pci_slot_reset,
diff --git a/drivers/scsi/mvumi.c b/drivers/scsi/mvumi.c
index 88cf1db21a79..783edc7c6b98 100644
--- a/drivers/scsi/mvumi.c
+++ b/drivers/scsi/mvumi.c
@@ -122,7 +122,7 @@ static struct mvumi_res *mvumi_alloc_mem_resource(struct mvumi_hba *mhba,
if (!res) {
dev_err(&mhba->pdev->dev,
- "Failed to allocate memory for resouce manager.\n");
+ "Failed to allocate memory for resource manager.\n");
return NULL;
}
@@ -1007,13 +1007,13 @@ static int mvumi_handshake(struct mvumi_hba *mhba)
tmp |= INT_MAP_COMAOUT | INT_MAP_COMAERR;
iowrite32(tmp, regs + CPU_ENPOINTA_MASK_REG);
iowrite32(mhba->list_num_io, mhba->ib_shadow);
- /* Set InBound List Avaliable count shadow */
+ /* Set InBound List Available count shadow */
iowrite32(lower_32_bits(mhba->ib_shadow_phys),
regs + CLA_INB_AVAL_COUNT_BASEL);
iowrite32(upper_32_bits(mhba->ib_shadow_phys),
regs + CLA_INB_AVAL_COUNT_BASEH);
- /* Set OutBound List Avaliable count shadow */
+ /* Set OutBound List Available count shadow */
iowrite32((mhba->list_num_io-1) | CL_POINTER_TOGGLE,
mhba->ob_shadow);
iowrite32(lower_32_bits(mhba->ob_shadow_phys), regs + 0x5B0);
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
index 9ce3a8f8754f..7cfdf2bd8edb 100644
--- a/drivers/scsi/qla2xxx/qla_nx.c
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -1615,13 +1615,11 @@ qla82xx_get_fw_offs(struct qla_hw_data *ha)
char *
qla82xx_pci_info_str(struct scsi_qla_host *vha, char *str)
{
- int pcie_reg;
struct qla_hw_data *ha = vha->hw;
char lwstr[6];
uint16_t lnk;
- pcie_reg = pci_pcie_cap(ha->pdev);
- pci_read_config_word(ha->pdev, pcie_reg + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(ha->pdev, PCI_EXP_LNKSTA, &lnk);
ha->link_width = (lnk >> 4) & 0x3f;
strcpy(str, "PCIe (");
@@ -2497,7 +2495,6 @@ fw_load_failed:
int
qla82xx_start_firmware(scsi_qla_host_t *vha)
{
- int pcie_cap;
uint16_t lnk;
struct qla_hw_data *ha = vha->hw;
@@ -2528,8 +2525,7 @@ qla82xx_start_firmware(scsi_qla_host_t *vha)
}
/* Negotiated Link width */
- pcie_cap = pci_pcie_cap(ha->pdev);
- pci_read_config_word(ha->pdev, pcie_cap + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(ha->pdev, PCI_EXP_LNKSTA, &lnk);
ha->link_width = (lnk >> 4) & 0x3f;
/* Synchronize with Receive peg */
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
index fb8cd3847d4b..d3052622e77a 100644
--- a/drivers/scsi/qla2xxx/qla_os.c
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -4471,7 +4471,7 @@ qla2xxx_pci_resume(struct pci_dev *pdev)
ha->flags.eeh_busy = 0;
}
-static struct pci_error_handlers qla2xxx_err_handler = {
+static const struct pci_error_handlers qla2xxx_err_handler = {
.error_detected = qla2xxx_pci_error_detected,
.mmio_enabled = qla2xxx_pci_mmio_enabled,
.slot_reset = qla2xxx_pci_slot_reset,
diff --git a/drivers/scsi/qla4xxx/ql4_nx.c b/drivers/scsi/qla4xxx/ql4_nx.c
index 939d7261c37a..807bf76f1b6a 100644
--- a/drivers/scsi/qla4xxx/ql4_nx.c
+++ b/drivers/scsi/qla4xxx/ql4_nx.c
@@ -1566,7 +1566,6 @@ qla4_8xxx_set_qsnt_ready(struct scsi_qla_host *ha)
static int
qla4_8xxx_start_firmware(struct scsi_qla_host *ha, uint32_t image_start)
{
- int pcie_cap;
uint16_t lnk;
/* scrub dma mask expansion register */
@@ -1590,8 +1589,7 @@ qla4_8xxx_start_firmware(struct scsi_qla_host *ha, uint32_t image_start)
}
/* Negotiated Link width */
- pcie_cap = pci_pcie_cap(ha->pdev);
- pci_read_config_word(ha->pdev, pcie_cap + PCI_EXP_LNKSTA, &lnk);
+ pcie_capability_read_word(ha->pdev, PCI_EXP_LNKSTA, &lnk);
ha->link_width = (lnk >> 4) & 0x3f;
/* Synchronize with Receive peg */
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c
index 9da426628b97..79243b76d17e 100644
--- a/drivers/scsi/qla4xxx/ql4_os.c
+++ b/drivers/scsi/qla4xxx/ql4_os.c
@@ -803,7 +803,7 @@ static void qla4xxx_conn_get_stats(struct iscsi_cls_conn *cls_conn,
iscsi_stats_dma);
if (ret != QLA_SUCCESS) {
ql4_printk(KERN_ERR, ha,
- "Unable to retreive iscsi stats\n");
+ "Unable to retrieve iscsi stats\n");
goto free_stats;
}
@@ -4338,7 +4338,7 @@ static int qla4xxx_compare_tuple_ddb(struct scsi_qla_host *ha,
return QLA_ERROR;
/* For multi sessions, driver generates the ISID, so do not compare
- * ISID in reset path since it would be a comparision between the
+ * ISID in reset path since it would be a comparison between the
* driver generated ISID and firmware generated ISID. This could
* lead to adding duplicated DDBs in the list as driver generated
* ISID would not match firmware generated ISID.
@@ -5326,7 +5326,7 @@ static void qla4xxx_destroy_fw_ddb_session(struct scsi_qla_host *ha)
}
}
/**
- * qla4xxx_remove_adapter - calback function to remove adapter.
+ * qla4xxx_remove_adapter - callback function to remove adapter.
* @pci_dev: PCI device pointer
**/
static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)
@@ -6148,7 +6148,7 @@ qla4xxx_pci_resume(struct pci_dev *pdev)
clear_bit(AF_EEH_BUSY, &ha->flags);
}
-static struct pci_error_handlers qla4xxx_err_handler = {
+static const struct pci_error_handlers qla4xxx_err_handler = {
.error_detected = qla4xxx_pci_error_detected,
.mmio_enabled = qla4xxx_pci_mmio_enabled,
.slot_reset = qla4xxx_pci_slot_reset,
diff --git a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c
index 36d1ed7817eb..e2b8e68b57e7 100644
--- a/drivers/scsi/sym53c8xx_2/sym_glue.c
+++ b/drivers/scsi/sym53c8xx_2/sym_glue.c
@@ -2117,7 +2117,7 @@ static struct pci_device_id sym2_id_table[] __devinitdata = {
MODULE_DEVICE_TABLE(pci, sym2_id_table);
-static struct pci_error_handlers sym2_err_handler = {
+static const struct pci_error_handlers sym2_err_handler = {
.error_detected = sym2_io_error_detected,
.mmio_enabled = sym2_io_slot_dump,
.slot_reset = sym2_io_slot_reset,
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index c7030fbee79c..3e79a2f00042 100644
--- a/drivers/scsi/virtio_scsi.c
+++ b/drivers/scsi/virtio_scsi.c
@@ -331,7 +331,7 @@ static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *p_idx,
int i;
for_each_sg(table->sgl, sg_elem, table->nents, i)
- sg_set_buf(&sg[idx++], sg_virt(sg_elem), sg_elem->length);
+ sg[idx++] = *sg_elem;
*p_idx = idx;
}
diff --git a/drivers/scsi/vmw_pvscsi.c b/drivers/scsi/vmw_pvscsi.c
index 4411d4224401..20b3a483c2cc 100644
--- a/drivers/scsi/vmw_pvscsi.c
+++ b/drivers/scsi/vmw_pvscsi.c
@@ -295,7 +295,7 @@ static void ll_adapter_reset(const struct pvscsi_adapter *adapter)
static void ll_bus_reset(const struct pvscsi_adapter *adapter)
{
- dev_dbg(pvscsi_dev(adapter), "Reseting bus on %p\n", adapter);
+ dev_dbg(pvscsi_dev(adapter), "Resetting bus on %p\n", adapter);
pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_BUS, NULL, 0);
}
@@ -304,7 +304,7 @@ static void ll_device_reset(const struct pvscsi_adapter *adapter, u32 target)
{
struct PVSCSICmdDescResetDevice cmd = { 0 };
- dev_dbg(pvscsi_dev(adapter), "Reseting device: target=%u\n", target);
+ dev_dbg(pvscsi_dev(adapter), "Resetting device: target=%u\n", target);
cmd.target = target;
diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c
index 32c26d795ed0..8f32a1323a79 100644
--- a/drivers/sh/intc/core.c
+++ b/drivers/sh/intc/core.c
@@ -355,7 +355,7 @@ int __init register_intc_controller(struct intc_desc *desc)
if (unlikely(res)) {
if (res == -EEXIST) {
res = irq_domain_associate(d->domain,
- irq, irq);
+ irq2, irq2);
if (unlikely(res)) {
pr_err("domain association "
"failure\n");
diff --git a/drivers/sh/pfc/pinctrl.c b/drivers/sh/pfc/pinctrl.c
index 2804eaae804e..0646bf6e7889 100644
--- a/drivers/sh/pfc/pinctrl.c
+++ b/drivers/sh/pfc/pinctrl.c
@@ -208,10 +208,13 @@ static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev,
break;
case PINMUX_TYPE_GPIO:
+ case PINMUX_TYPE_INPUT:
+ case PINMUX_TYPE_OUTPUT:
break;
default:
pr_err("Unsupported mux type (%d), bailing...\n", pinmux_type);
- return -ENOTSUPP;
+ ret = -ENOTSUPP;
+ goto err;
}
ret = 0;
diff --git a/drivers/spi/spi-au1550.c b/drivers/spi/spi-au1550.c
index 5784c8799616..4de66d1cfe51 100644
--- a/drivers/spi/spi-au1550.c
+++ b/drivers/spi/spi-au1550.c
@@ -475,7 +475,7 @@ static irqreturn_t au1550_spi_dma_irq_callback(struct au1550_spi *hw)
/*
* due to an spi error we consider transfer as done,
* so mask all events until before next transfer start
- * and stop the possibly running dma immediatelly
+ * and stop the possibly running dma immediately
*/
au1550_spi_mask_ack_all(hw);
au1xxx_dbdma_stop(hw->dma_rx_ch);
diff --git a/drivers/spi/spi-bfin-sport.c b/drivers/spi/spi-bfin-sport.c
index 1fe51198a622..6555ecd07302 100644
--- a/drivers/spi/spi-bfin-sport.c
+++ b/drivers/spi/spi-bfin-sport.c
@@ -467,7 +467,7 @@ bfin_sport_spi_pump_transfers(unsigned long data)
dev_dbg(drv_data->dev, "IO write error!\n");
drv_data->state = ERROR_STATE;
} else {
- /* Update total byte transfered */
+ /* Update total byte transferred */
message->actual_length += transfer->len;
/* Move to next transfer of this msg */
drv_data->state = bfin_sport_spi_next_transfer(drv_data);
diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index 63e7fc9801cd..de7ebb65e535 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -97,7 +97,7 @@ struct spi_imx_data {
const void *tx_buf;
unsigned int txfifo; /* number of words pushed in tx FIFO */
- struct spi_imx_devtype_data *devtype_data;
+ const struct spi_imx_devtype_data *devtype_data;
int chipselect[0];
};
diff --git a/drivers/spi/spi-oc-tiny.c b/drivers/spi/spi-oc-tiny.c
index 698018fd992b..9d9071b730be 100644
--- a/drivers/spi/spi-oc-tiny.c
+++ b/drivers/spi/spi-oc-tiny.c
@@ -129,7 +129,7 @@ static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
unsigned int i;
if (hw->irq >= 0) {
- /* use intrrupt driven data transfer */
+ /* use interrupt driven data transfer */
hw->len = t->len;
hw->txp = t->tx_buf;
hw->rxp = t->rx_buf;
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index b9b7ad02ef4c..5d59a69a9064 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -1116,7 +1116,7 @@ MODULE_DEVICE_TABLE(of, omap_mcspi_of_match);
static int __devinit omap2_mcspi_probe(struct platform_device *pdev)
{
struct spi_master *master;
- struct omap2_mcspi_platform_config *pdata;
+ const struct omap2_mcspi_platform_config *pdata;
struct omap2_mcspi *mcspi;
struct resource *r;
int status = 0, i;
diff --git a/drivers/spi/spi-ppc4xx.c b/drivers/spi/spi-ppc4xx.c
index 75ac9d48ef46..7a85f22b6474 100644
--- a/drivers/spi/spi-ppc4xx.c
+++ b/drivers/spi/spi-ppc4xx.c
@@ -101,7 +101,7 @@ struct spi_ppc4xx_regs {
u8 dummy;
/*
* Clock divisor modulus register
- * This uses the follwing formula:
+ * This uses the following formula:
* SCPClkOut = OPBCLK/(4(CDM + 1))
* or
* CDM = (OPBCLK/4*SCPClkOut) - 1
@@ -201,7 +201,7 @@ static int spi_ppc4xx_setupxfer(struct spi_device *spi, struct spi_transfer *t)
return -EINVAL;
}
- /* Write new configration */
+ /* Write new configuration */
out_8(&hw->regs->mode, cs->mode);
/* Set the clock */
diff --git a/drivers/spi/spi-topcliff-pch.c b/drivers/spi/spi-topcliff-pch.c
index cd56dcf46320..1284c9b74653 100644
--- a/drivers/spi/spi-topcliff-pch.c
+++ b/drivers/spi/spi-topcliff-pch.c
@@ -505,7 +505,7 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
}
if (unlikely(pspi->max_speed_hz == 0)) {
- dev_err(&pspi->dev, "%s pch_spi_tranfer maxspeed=%d\n",
+ dev_err(&pspi->dev, "%s pch_spi_transfer maxspeed=%d\n",
__func__, pspi->max_speed_hz);
retval = -EINVAL;
goto err_out;
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index e3402d5644dd..d805eef11915 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -122,8 +122,6 @@ source "drivers/staging/android/Kconfig"
source "drivers/staging/telephony/Kconfig"
-source "drivers/staging/ramster/Kconfig"
-
source "drivers/staging/ozwpan/Kconfig"
source "drivers/staging/ccg/Kconfig"
@@ -136,4 +134,14 @@ source "drivers/staging/csr/Kconfig"
source "drivers/staging/omap-thermal/Kconfig"
+source "drivers/staging/ramster/Kconfig"
+
+source "drivers/staging/silicom/Kconfig"
+
+source "drivers/staging/ced1401/Kconfig"
+
+source "drivers/staging/imx-drm/Kconfig"
+
+source "drivers/staging/dgrp/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 3be59d02cae4..76e2ebd596ff 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -54,9 +54,13 @@ obj-$(CONFIG_MFD_NVEC) += nvec/
obj-$(CONFIG_DRM_OMAP) += omapdrm/
obj-$(CONFIG_ANDROID) += android/
obj-$(CONFIG_PHONE) += telephony/
-obj-$(CONFIG_RAMSTER) += ramster/
obj-$(CONFIG_USB_WPAN_HCD) += ozwpan/
obj-$(CONFIG_USB_G_CCG) += ccg/
obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/
obj-$(CONFIG_CSR_WIFI) += csr/
obj-$(CONFIG_OMAP_BANDGAP) += omap-thermal/
+obj-$(CONFIG_ZCACHE2) += ramster/
+obj-$(CONFIG_NET_VENDOR_SILICOM) += silicom/
+obj-$(CONFIG_CED1401) += ced1401/
+obj-$(CONFIG_DRM_IMX) += imx-drm/
+obj-$(CONFIG_DGRP) += dgrp/
diff --git a/drivers/staging/android/alarm-dev.c b/drivers/staging/android/alarm-dev.c
index 5b7064005188..a9b293ff3cc8 100644
--- a/drivers/staging/android/alarm-dev.c
+++ b/drivers/staging/android/alarm-dev.c
@@ -67,10 +67,8 @@ static struct devalarm alarms[ANDROID_ALARM_TYPE_COUNT];
static int is_wakeup(enum android_alarm_type type)
{
- if (type == ANDROID_ALARM_RTC_WAKEUP ||
- type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP)
- return 1;
- return 0;
+ return (type == ANDROID_ALARM_RTC_WAKEUP ||
+ type == ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP);
}
@@ -85,12 +83,9 @@ static void devalarm_start(struct devalarm *alrm, ktime_t exp)
static int devalarm_try_to_cancel(struct devalarm *alrm)
{
- int ret;
if (is_wakeup(alrm->type))
- ret = alarm_try_to_cancel(&alrm->u.alrm);
- else
- ret = hrtimer_try_to_cancel(&alrm->u.hrt);
- return ret;
+ return alarm_try_to_cancel(&alrm->u.alrm);
+ return hrtimer_try_to_cancel(&alrm->u.hrt);
}
static void devalarm_cancel(struct devalarm *alrm)
@@ -223,10 +218,12 @@ from_old_alarm_set:
case ANDROID_ALARM_ELAPSED_REALTIME:
get_monotonic_boottime(&tmp_time);
break;
- case ANDROID_ALARM_TYPE_COUNT:
case ANDROID_ALARM_SYSTEMTIME:
ktime_get_ts(&tmp_time);
break;
+ default:
+ rv = -EINVAL;
+ goto err1;
}
if (copy_to_user((void __user *)arg, &tmp_time,
sizeof(tmp_time))) {
diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c
index 69cf2db1d69c..94a740d2883d 100644
--- a/drivers/staging/android/ashmem.c
+++ b/drivers/staging/android/ashmem.c
@@ -1,20 +1,20 @@
/* mm/ashmem.c
-**
-** Anonymous Shared Memory Subsystem, ashmem
-**
-** Copyright (C) 2008 Google, Inc.
-**
-** Robert Love <rlove@google.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.
-*/
+ *
+ * Anonymous Shared Memory Subsystem, ashmem
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * Robert Love <rlove@google.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.
+ */
#define pr_fmt(fmt) "ashmem: " fmt
diff --git a/drivers/staging/android/binder.c b/drivers/staging/android/binder.c
index 574e99210c36..a807129c7b5a 100644
--- a/drivers/staging/android/binder.c
+++ b/drivers/staging/android/binder.c
@@ -365,7 +365,7 @@ binder_defer_work(struct binder_proc *proc, enum binder_deferred_state defer);
/*
* copied from get_unused_fd_flags
*/
-int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
+static int task_get_unused_fd_flags(struct binder_proc *proc, int flags)
{
struct files_struct *files = proc->files;
int fd, error;
@@ -415,13 +415,13 @@ repeat:
else
__clear_close_on_exec(fd, fdt);
files->next_fd = fd + 1;
-#if 1
+
/* Sanity check */
if (fdt->fd[fd] != NULL) {
pr_warn("get_unused_fd: slot %d not NULL!\n", fd);
fdt->fd[fd] = NULL;
}
-#endif
+
error = fd;
out:
diff --git a/drivers/staging/android/logger.c b/drivers/staging/android/logger.c
index f7b8237d5be7..1d5ed475364b 100644
--- a/drivers/staging/android/logger.c
+++ b/drivers/staging/android/logger.c
@@ -32,38 +32,50 @@
#include <asm/ioctls.h>
-/*
+/**
* struct logger_log - represents a specific log, such as 'main' or 'radio'
+ * @buffer: The actual ring buffer
+ * @misc: The "misc" device representing the log
+ * @wq: The wait queue for @readers
+ * @readers: This log's readers
+ * @mutex: The mutex that protects the @buffer
+ * @w_off: The current write head offset
+ * @head: The head, or location that readers start reading at.
+ * @size: The size of the log
+ * @logs: The list of log channels
*
* This structure lives from module insertion until module removal, so it does
* not need additional reference counting. The structure is protected by the
* mutex 'mutex'.
*/
struct logger_log {
- unsigned char *buffer;/* the ring buffer itself */
- struct miscdevice misc; /* misc device representing the log */
- wait_queue_head_t wq; /* wait queue for readers */
- struct list_head readers; /* this log's readers */
- struct mutex mutex; /* mutex protecting buffer */
- size_t w_off; /* current write head offset */
- size_t head; /* new readers start here */
- size_t size; /* size of the log */
- struct list_head logs; /* list of log channels (myself)*/
+ unsigned char *buffer;
+ struct miscdevice misc;
+ wait_queue_head_t wq;
+ struct list_head readers;
+ struct mutex mutex;
+ size_t w_off;
+ size_t head;
+ size_t size;
+ struct list_head logs;
};
static LIST_HEAD(log_list);
-/*
+/**
* struct logger_reader - a logging device open for reading
+ * @log: The associated log
+ * @list: The associated entry in @logger_log's list
+ * @r_off: The current read head offset.
*
* This object lives from open to release, so we don't need additional
* reference counting. The structure is protected by log->mutex.
*/
struct logger_reader {
- struct logger_log *log; /* associated log */
- struct list_head list; /* entry in logger_log's list */
- size_t r_off; /* current read head offset */
+ struct logger_log *log;
+ struct list_head list;
+ size_t r_off;
};
/* logger_offset - returns index 'n' into the log via (optimized) modulus */
diff --git a/drivers/staging/android/logger.h b/drivers/staging/android/logger.h
index 2cb06e9d8f98..9b929a8c7468 100644
--- a/drivers/staging/android/logger.h
+++ b/drivers/staging/android/logger.h
@@ -20,14 +20,24 @@
#include <linux/types.h>
#include <linux/ioctl.h>
+/**
+ * struct logger_entry - defines a single entry that is given to a logger
+ * @len: The length of the payload
+ * @__pad: Two bytes of padding that appear to be required
+ * @pid: The generating process' process ID
+ * @tid: The generating process' thread ID
+ * @sec: The number of seconds that have elapsed since the Epoch
+ * @nsec: The number of nanoseconds that have elapsed since @sec
+ * @msg: The message that is to be logged
+ */
struct logger_entry {
- __u16 len; /* length of the payload */
- __u16 __pad; /* no matter what, we get 2 bytes of padding */
- __s32 pid; /* generating process's pid */
- __s32 tid; /* generating process's tid */
- __s32 sec; /* seconds since Epoch */
- __s32 nsec; /* nanoseconds */
- char msg[0]; /* the entry's payload */
+ __u16 len;
+ __u16 __pad;
+ __s32 pid;
+ __s32 tid;
+ __s32 sec;
+ __s32 nsec;
+ char msg[0];
};
#define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */
diff --git a/drivers/staging/android/timed_gpio.c b/drivers/staging/android/timed_gpio.c
index 45c522cbe784..e81451425c01 100644
--- a/drivers/staging/android/timed_gpio.c
+++ b/drivers/staging/android/timed_gpio.c
@@ -161,18 +161,7 @@ static struct platform_driver timed_gpio_driver = {
},
};
-static int __init timed_gpio_init(void)
-{
- return platform_driver_register(&timed_gpio_driver);
-}
-
-static void __exit timed_gpio_exit(void)
-{
- platform_driver_unregister(&timed_gpio_driver);
-}
-
-module_init(timed_gpio_init);
-module_exit(timed_gpio_exit);
+module_platform_driver(timed_gpio_driver);
MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
MODULE_DESCRIPTION("timed gpio driver");
diff --git a/drivers/staging/asus_oled/asus_oled.c b/drivers/staging/asus_oled/asus_oled.c
index f63c1d3aeb64..00185478647a 100644
--- a/drivers/staging/asus_oled/asus_oled.c
+++ b/drivers/staging/asus_oled/asus_oled.c
@@ -42,8 +42,6 @@
#define ASUS_OLED_NAME "asus-oled"
#define ASUS_OLED_UNDERSCORE_NAME "asus_oled"
-#define ASUS_OLED_ERROR "Asus OLED Display Error: "
-
#define ASUS_OLED_STATIC 's'
#define ASUS_OLED_ROLL 'r'
#define ASUS_OLED_FLASH 'f'
@@ -57,8 +55,9 @@
#define USB_DEVICE_ID_ASUS_LCM2 0x175b
MODULE_AUTHOR("Jakub Schmidtke, sjakub@gmail.com");
-MODULE_DESCRIPTION("Asus OLED Driver v" ASUS_OLED_VERSION);
+MODULE_DESCRIPTION("Asus OLED Driver");
MODULE_LICENSE("GPL");
+MODULE_VERSION(ASUS_OLED_VERSION);
static struct class *oled_class;
static int oled_num;
@@ -138,6 +137,7 @@ struct asus_oled_dev {
size_t buf_size;
char *buf;
uint8_t enabled;
+ uint8_t enabled_post_resume;
struct device *dev;
};
@@ -383,13 +383,13 @@ static int append_values(struct asus_oled_dev *odev, uint8_t val, size_t count)
default:
i = 0;
- printk(ASUS_OLED_ERROR "Unknown OLED Pack Mode: %d!\n",
+ dev_err(odev->dev, "Unknown OLED Pack Mode: %d!\n",
odev->pack_mode);
break;
}
if (i >= odev->buf_size) {
- printk(ASUS_OLED_ERROR "Buffer overflow! Report a bug:"
+ dev_err(odev->dev, "Buffer overflow! Report a bug:"
"offs: %d >= %d i: %d (x: %d y: %d)\n",
(int) odev->buf_offs, (int) odev->buf_size,
(int) i, (int) x, (int) y);
@@ -435,7 +435,7 @@ static ssize_t odev_set_picture(struct asus_oled_dev *odev,
odev->buf = kmalloc(odev->buf_size, GFP_KERNEL);
if (odev->buf == NULL) {
odev->buf_size = 0;
- printk(ASUS_OLED_ERROR "Out of memory!\n");
+ dev_err(odev->dev, "Out of memory!\n");
return -ENOMEM;
}
@@ -473,7 +473,7 @@ static ssize_t odev_set_picture(struct asus_oled_dev *odev,
odev->pic_mode = buf[1];
break;
default:
- printk(ASUS_OLED_ERROR "Wrong picture mode: '%c'.\n",
+ dev_err(odev->dev, "Wrong picture mode: '%c'.\n",
buf[1]);
return -EIO;
break;
@@ -533,7 +533,7 @@ static ssize_t odev_set_picture(struct asus_oled_dev *odev,
if (odev->buf == NULL) {
odev->buf_size = 0;
- printk(ASUS_OLED_ERROR "Out of memory!\n");
+ dev_err(odev->dev, "Out of memory!\n");
return -ENOMEM;
}
@@ -593,15 +593,15 @@ static ssize_t odev_set_picture(struct asus_oled_dev *odev,
return count;
error_width:
- printk(ASUS_OLED_ERROR "Wrong picture width specified.\n");
+ dev_err(odev->dev, "Wrong picture width specified.\n");
return -EIO;
error_height:
- printk(ASUS_OLED_ERROR "Wrong picture height specified.\n");
+ dev_err(odev->dev, "Wrong picture height specified.\n");
return -EIO;
error_header:
- printk(ASUS_OLED_ERROR "Wrong picture header.\n");
+ dev_err(odev->dev, "Wrong picture header.\n");
return -EIO;
}
@@ -766,11 +766,45 @@ static void asus_oled_disconnect(struct usb_interface *interface)
dev_info(&interface->dev, "Disconnected Asus OLED device\n");
}
+#ifdef CONFIG_PM
+static int asus_oled_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct asus_oled_dev *odev;
+
+ odev = usb_get_intfdata(intf);
+ if (!odev)
+ return -ENODEV;
+
+ odev->enabled_post_resume = odev->enabled;
+ enable_oled(odev, 0);
+
+ return 0;
+}
+
+static int asus_oled_resume(struct usb_interface *intf)
+{
+ struct asus_oled_dev *odev;
+
+ odev = usb_get_intfdata(intf);
+ if (!odev)
+ return -ENODEV;
+
+ enable_oled(odev, odev->enabled_post_resume);
+
+ return 0;
+}
+#else
+#define asus_oled_suspend NULL
+#define asus_oled_resume NULL
+#endif
+
static struct usb_driver oled_driver = {
.name = ASUS_OLED_NAME,
.probe = asus_oled_probe,
.disconnect = asus_oled_disconnect,
.id_table = id_table,
+ .suspend = asus_oled_suspend,
+ .resume = asus_oled_resume,
};
static CLASS_ATTR_STRING(version, S_IRUGO,
diff --git a/drivers/staging/bcm/Bcmchar.c b/drivers/staging/bcm/Bcmchar.c
index cf411d1706b1..3d02c2ebfb8d 100644
--- a/drivers/staging/bcm/Bcmchar.c
+++ b/drivers/staging/bcm/Bcmchar.c
@@ -820,6 +820,7 @@ cntrlEnd:
if (copy_from_user(psFwInfo, IoBuffer.InputBuffer, IoBuffer.InputLength)) {
up(&Adapter->fw_download_sema);
+ kfree(psFwInfo);
return -EFAULT;
}
@@ -829,6 +830,7 @@ cntrlEnd:
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0, "Something else is wrong %lu\n",
psFwInfo->u32FirmwareLength);
up(&Adapter->fw_download_sema);
+ kfree(psFwInfo);
Status = -EINVAL;
return Status;
}
diff --git a/drivers/staging/bcm/CmHost.c b/drivers/staging/bcm/CmHost.c
index b54ec974477f..325b592fd41f 100644
--- a/drivers/staging/bcm/CmHost.c
+++ b/drivers/staging/bcm/CmHost.c
@@ -235,7 +235,7 @@ void ClearTargetDSXBuffer(struct bcm_mini_adapter *Adapter, B_UINT16 TID, BOOLEA
* @ingroup ctrl_pkt_functions
* copy classifier rule into the specified SF index
*/
-static inline VOID CopyClassifierRuleToSF(struct bcm_mini_adapter *Adapter, stConvergenceSLTypes *psfCSType, UINT uiSearchRuleIndex, UINT nClassifierIndex)
+static inline VOID CopyClassifierRuleToSF(struct bcm_mini_adapter *Adapter, struct bcm_convergence_types *psfCSType, UINT uiSearchRuleIndex, UINT nClassifierIndex)
{
struct bcm_classifier_rule *pstClassifierEntry = NULL;
/* VOID *pvPhsContext = NULL; */
@@ -428,7 +428,7 @@ VOID DeleteAllClassifiersForSF(struct bcm_mini_adapter *Adapter, UINT uiSearchRu
* @ingroup ctrl_pkt_functions
*/
static VOID CopyToAdapter(register struct bcm_mini_adapter *Adapter, /* <Pointer to the Adapter structure */
- register pstServiceFlowParamSI psfLocalSet, /* <Pointer to the ServiceFlowParamSI structure */
+ register struct bcm_connect_mgr_params *psfLocalSet, /* Pointer to the connection manager parameters structure */
register UINT uiSearchRuleIndex, /* <Index of Queue, to which this data belongs */
register UCHAR ucDsxType,
stLocalSFAddIndicationAlt *pstAddIndication) {
@@ -439,7 +439,7 @@ static VOID CopyToAdapter(register struct bcm_mini_adapter *Adapter, /* <Pointer
enum E_CLASSIFIER_ACTION eClassifierAction = eInvalidClassifierAction;
B_UINT16 u16PacketClassificationRuleIndex = 0;
int i;
- stConvergenceSLTypes *psfCSType = NULL;
+ struct bcm_convergence_types *psfCSType = NULL;
S_PHS_RULE sPhsRule;
USHORT uVCID = Adapter->PackInfo[uiSearchRuleIndex].usVCID_Value;
UINT UGIValue = 0;
@@ -915,7 +915,7 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
if (!pstAddIndication->sfAuthorizedSet.bValid)
pstAddIndication->sfAuthorizedSet.bValid = 1;
for (nIndex = 0; nIndex < nCurClassifierCnt; nIndex++) {
- stConvergenceSLTypes *psfCSType = NULL;
+ struct bcm_convergence_types *psfCSType = NULL;
psfCSType = &pstAddIndication->sfAuthorizedSet.cConvergenceSLTypes[nIndex];
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "psfCSType = %p", psfCSType);
@@ -999,13 +999,10 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
#ifdef VERSION_D5
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPv6FlowLableLength: 0x%X ",
psfCSType->cCPacketClassificationRule.u8IPv6FlowLableLength);
- BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPv6FlowLable[6]: 0x %02X %02X %02X %02X %02X %02X ",
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[0],
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[1],
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[2],
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[3],
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[4],
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[5]);
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+ DBG_LVL_ALL, "u8IPv6FlowLable[6]: 0x%*ph ",
+ 6, psfCSType->cCPacketClassificationRule.
+ u8IPv6FlowLable);
#endif
}
@@ -1015,13 +1012,9 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u16CID: 0x%X", pstAddIndication->sfAdmittedSet.u16CID);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ServiceClassNameLength: 0x%X",
pstAddIndication->sfAdmittedSet.u8ServiceClassNameLength);
- BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ServiceClassName: 0x %02X %02X %02X %02X %02X %02X",
- pstAddIndication->sfAdmittedSet.u8ServiceClassName[0],
- pstAddIndication->sfAdmittedSet.u8ServiceClassName[1],
- pstAddIndication->sfAdmittedSet.u8ServiceClassName[2],
- pstAddIndication->sfAdmittedSet.u8ServiceClassName[3],
- pstAddIndication->sfAdmittedSet.u8ServiceClassName[4],
- pstAddIndication->sfAdmittedSet.u8ServiceClassName[5]);
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL,
+ "u8ServiceClassName: 0x%*ph",
+ 6, pstAddIndication->sfAdmittedSet.u8ServiceClassName);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8MBSService: 0x%02X", pstAddIndication->sfAdmittedSet.u8MBSService);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8QosParamSet: 0x%02X", pstAddIndication->sfAdmittedSet.u8QosParamSet);
@@ -1066,7 +1059,7 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
nCurClassifierCnt = MAX_CLASSIFIERS_IN_SF;
for (nIndex = 0; nIndex < nCurClassifierCnt; nIndex++) {
- stConvergenceSLTypes *psfCSType = NULL;
+ struct bcm_convergence_types *psfCSType = NULL;
psfCSType = &pstAddIndication->sfAdmittedSet.cConvergenceSLTypes[nIndex];
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, " CCPacketClassificationRuleSI====>");
@@ -1074,10 +1067,10 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
psfCSType->cCPacketClassificationRule.u8ClassifierRulePriority);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPTypeOfServiceLength: 0x%02X",
psfCSType->cCPacketClassificationRule.u8IPTypeOfServiceLength);
- BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPTypeOfService[3]: 0x%02X %02X %02X",
- psfCSType->cCPacketClassificationRule.u8IPTypeOfService[0],
- psfCSType->cCPacketClassificationRule.u8IPTypeOfService[1],
- psfCSType->cCPacketClassificationRule.u8IPTypeOfService[2]);
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+ DBG_LVL_ALL, "u8IPTypeOfService[3]: 0x%*ph",
+ 3, psfCSType->cCPacketClassificationRule.
+ u8IPTypeOfService);
for (uiLoopIndex = 0; uiLoopIndex < 1; uiLoopIndex++)
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8Protocol: 0x%02X ", psfCSType->cCPacketClassificationRule.u8Protocol);
@@ -1098,20 +1091,20 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ProtocolSourcePortRangeLength: 0x%02X ",
psfCSType->cCPacketClassificationRule.u8ProtocolSourcePortRangeLength);
- BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ProtocolSourcePortRange[4]: 0x %02X %02X %02X %02X ",
- psfCSType->cCPacketClassificationRule.u8ProtocolSourcePortRange[0],
- psfCSType->cCPacketClassificationRule.u8ProtocolSourcePortRange[1],
- psfCSType->cCPacketClassificationRule.u8ProtocolSourcePortRange[2],
- psfCSType->cCPacketClassificationRule.u8ProtocolSourcePortRange[3]);
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+ DBG_LVL_ALL, "u8ProtocolSourcePortRange[4]: "
+ "0x%*ph ", 4, psfCSType->
+ cCPacketClassificationRule.
+ u8ProtocolSourcePortRange);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ProtocolDestPortRangeLength: 0x%02X ",
psfCSType->cCPacketClassificationRule.u8ProtocolDestPortRangeLength);
- BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ProtocolDestPortRange[4]: 0x %02X %02X %02X %02X ",
- psfCSType->cCPacketClassificationRule.u8ProtocolDestPortRange[0],
- psfCSType->cCPacketClassificationRule.u8ProtocolDestPortRange[1],
- psfCSType->cCPacketClassificationRule.u8ProtocolDestPortRange[2],
- psfCSType->cCPacketClassificationRule.u8ProtocolDestPortRange[3]);
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+ DBG_LVL_ALL, "u8ProtocolDestPortRange[4]: "
+ "0x%*ph ", 4, psfCSType->
+ cCPacketClassificationRule.
+ u8ProtocolDestPortRange);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8EthernetDestMacAddressLength: 0x%02X ",
psfCSType->cCPacketClassificationRule.u8EthernetDestMacAddressLength);
@@ -1130,10 +1123,10 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
u8EthernetSourceMACAddress);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8EthertypeLength: 0x%02X ", psfCSType->cCPacketClassificationRule.u8EthertypeLength);
- BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8Ethertype[3]: 0x%02X %02X %02X",
- psfCSType->cCPacketClassificationRule.u8Ethertype[0],
- psfCSType->cCPacketClassificationRule.u8Ethertype[1],
- psfCSType->cCPacketClassificationRule.u8Ethertype[2]);
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+ DBG_LVL_ALL, "u8Ethertype[3]: 0x%*ph",
+ 3, psfCSType->cCPacketClassificationRule.
+ u8Ethertype);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u16UserPriority: 0x%X ", psfCSType->cCPacketClassificationRule.u16UserPriority);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u16VLANID: 0x%X ", psfCSType->cCPacketClassificationRule.u16VLANID);
@@ -1147,13 +1140,10 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
#ifdef VERSION_D5
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPv6FlowLableLength: 0x%X ",
psfCSType->cCPacketClassificationRule.u8IPv6FlowLableLength);
- BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8IPv6FlowLable[6]: 0x %02X %02X %02X %02X %02X %02X ",
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[0],
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[1],
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[2],
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[3],
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[4],
- psfCSType->cCPacketClassificationRule.u8IPv6FlowLable[5]);
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL,
+ DBG_LVL_ALL, "u8IPv6FlowLable[6]: 0x%*ph ",
+ 6, psfCSType->cCPacketClassificationRule.
+ u8IPv6FlowLable);
#endif
}
@@ -1162,13 +1152,9 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u32SFID: 0x%X", pstAddIndication->sfActiveSet.u32SFID);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u16CID: 0x%X", pstAddIndication->sfActiveSet.u16CID);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ServiceClassNameLength: 0x%X", pstAddIndication->sfActiveSet.u8ServiceClassNameLength);
- BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8ServiceClassName: 0x %02X %02X %02X %02X %02X %02X",
- pstAddIndication->sfActiveSet.u8ServiceClassName[0],
- pstAddIndication->sfActiveSet.u8ServiceClassName[1],
- pstAddIndication->sfActiveSet.u8ServiceClassName[2],
- pstAddIndication->sfActiveSet.u8ServiceClassName[3],
- pstAddIndication->sfActiveSet.u8ServiceClassName[4],
- pstAddIndication->sfActiveSet.u8ServiceClassName[5]);
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL,
+ "u8ServiceClassName: 0x%*ph",
+ 6, pstAddIndication->sfActiveSet.u8ServiceClassName);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8MBSService: 0x%02X", pstAddIndication->sfActiveSet.u8MBSService);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, "u8QosParamSet: 0x%02X", pstAddIndication->sfActiveSet.u8QosParamSet);
@@ -1212,7 +1198,7 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
nCurClassifierCnt = MAX_CLASSIFIERS_IN_SF;
for (nIndex = 0; nIndex < nCurClassifierCnt; nIndex++) {
- stConvergenceSLTypes *psfCSType = NULL;
+ struct bcm_convergence_types *psfCSType = NULL;
psfCSType = &pstAddIndication->sfActiveSet.cConvergenceSLTypes[nIndex];
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_CONTROL, DBG_LVL_ALL, " CCPacketClassificationRuleSI====>");
@@ -1314,7 +1300,7 @@ static VOID DumpCmControlPacket(PVOID pvBuffer)
static inline ULONG RestoreSFParam(struct bcm_mini_adapter *Adapter, ULONG ulAddrSFParamSet, PUCHAR pucDestBuffer)
{
- UINT nBytesToRead = sizeof(stServiceFlowParamSI);
+ UINT nBytesToRead = sizeof(struct bcm_connect_mgr_params);
if (ulAddrSFParamSet == 0 || NULL == pucDestBuffer) {
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "Got Param address as 0!!");
@@ -1331,7 +1317,7 @@ static inline ULONG RestoreSFParam(struct bcm_mini_adapter *Adapter, ULONG ulAdd
static ULONG StoreSFParam(struct bcm_mini_adapter *Adapter, PUCHAR pucSrcBuffer, ULONG ulAddrSFParamSet)
{
- UINT nBytesToWrite = sizeof(stServiceFlowParamSI);
+ UINT nBytesToWrite = sizeof(struct bcm_connect_mgr_params);
int ret = 0;
if (ulAddrSFParamSet == 0 || NULL == pucSrcBuffer)
@@ -1348,8 +1334,8 @@ static ULONG StoreSFParam(struct bcm_mini_adapter *Adapter, PUCHAR pucSrcBuffer,
ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBuffer, UINT *puBufferLength)
{
stLocalSFAddIndicationAlt *pstAddIndicationAlt = NULL;
- stLocalSFAddIndication *pstAddIndication = NULL;
- stLocalSFDeleteRequest *pstDeletionRequest;
+ struct bcm_add_indication *pstAddIndication = NULL;
+ struct bcm_del_request *pstDeletionRequest;
UINT uiSearchRuleIndex;
ULONG ulSFID;
@@ -1360,7 +1346,7 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
* we can stop the further classifying the pkt for this SF.
*/
if (pstAddIndicationAlt->u8Type == DSD_REQ) {
- pstDeletionRequest = (stLocalSFDeleteRequest *)pvBuffer;
+ pstDeletionRequest = (struct bcm_del_request *)pvBuffer;
ulSFID = ntohl(pstDeletionRequest->u32SFID);
uiSearchRuleIndex = SearchSfid(Adapter, ulSFID);
@@ -1379,12 +1365,12 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
}
/* For DSA_REQ, only up to "psfAuthorizedSet" parameter should be accessed by driver! */
- pstAddIndication = kmalloc(sizeof(*pstAddIndication), GFP_KERNEL);
+ pstAddIndication = kmalloc(sizeof(struct bcm_add_indication), GFP_KERNEL);
if (pstAddIndication == NULL)
return 0;
/* AUTHORIZED SET */
- pstAddIndication->psfAuthorizedSet = (stServiceFlowParamSI *)
+ pstAddIndication->psfAuthorizedSet = (struct bcm_connect_mgr_params *)
GetNextTargetBufferLocation(Adapter, pstAddIndicationAlt->u16TID);
if (!pstAddIndication->psfAuthorizedSet) {
kfree(pstAddIndication);
@@ -1398,10 +1384,10 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
}
/* this can't possibly be right */
- pstAddIndication->psfAuthorizedSet = (stServiceFlowParamSI *)ntohl((ULONG)pstAddIndication->psfAuthorizedSet);
+ pstAddIndication->psfAuthorizedSet = (struct bcm_connect_mgr_params *)ntohl((ULONG)pstAddIndication->psfAuthorizedSet);
if (pstAddIndicationAlt->u8Type == DSA_REQ) {
- stLocalSFAddRequest AddRequest;
+ struct bcm_add_request AddRequest;
AddRequest.u8Type = pstAddIndicationAlt->u8Type;
AddRequest.eConnectionDir = pstAddIndicationAlt->u8Direction;
@@ -1409,8 +1395,8 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
AddRequest.u16CID = pstAddIndicationAlt->u16CID;
AddRequest.u16VCID = pstAddIndicationAlt->u16VCID;
AddRequest.psfParameterSet = pstAddIndication->psfAuthorizedSet;
- (*puBufferLength) = sizeof(stLocalSFAddRequest);
- memcpy(pvBuffer, &AddRequest, sizeof(stLocalSFAddRequest));
+ (*puBufferLength) = sizeof(struct bcm_add_request);
+ memcpy(pvBuffer, &AddRequest, sizeof(struct bcm_add_request));
kfree(pstAddIndication);
return 1;
}
@@ -1426,7 +1412,7 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
pstAddIndication->u8CC = pstAddIndicationAlt->u8CC;
/* ADMITTED SET */
- pstAddIndication->psfAdmittedSet = (stServiceFlowParamSI *)
+ pstAddIndication->psfAdmittedSet = (struct bcm_connect_mgr_params *)
GetNextTargetBufferLocation(Adapter, pstAddIndicationAlt->u16TID);
if (!pstAddIndication->psfAdmittedSet) {
kfree(pstAddIndication);
@@ -1437,10 +1423,10 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
return 0;
}
- pstAddIndication->psfAdmittedSet = (stServiceFlowParamSI *)ntohl((ULONG)pstAddIndication->psfAdmittedSet);
+ pstAddIndication->psfAdmittedSet = (struct bcm_connect_mgr_params *)ntohl((ULONG)pstAddIndication->psfAdmittedSet);
/* ACTIVE SET */
- pstAddIndication->psfActiveSet = (stServiceFlowParamSI *)
+ pstAddIndication->psfActiveSet = (struct bcm_connect_mgr_params *)
GetNextTargetBufferLocation(Adapter, pstAddIndicationAlt->u16TID);
if (!pstAddIndication->psfActiveSet) {
kfree(pstAddIndication);
@@ -1451,10 +1437,10 @@ ULONG StoreCmControlResponseMessage(struct bcm_mini_adapter *Adapter, PVOID pvBu
return 0;
}
- pstAddIndication->psfActiveSet = (stServiceFlowParamSI *)ntohl((ULONG)pstAddIndication->psfActiveSet);
+ pstAddIndication->psfActiveSet = (struct bcm_connect_mgr_params *)ntohl((ULONG)pstAddIndication->psfActiveSet);
- (*puBufferLength) = sizeof(stLocalSFAddIndication);
- *(stLocalSFAddIndication *)pvBuffer = *pstAddIndication;
+ (*puBufferLength) = sizeof(struct bcm_add_indication);
+ *(struct bcm_add_indication *)pvBuffer = *pstAddIndication;
kfree(pstAddIndication);
return 1;
}
@@ -1463,10 +1449,10 @@ static inline stLocalSFAddIndicationAlt
*RestoreCmControlResponseMessage(register struct bcm_mini_adapter *Adapter, register PVOID pvBuffer)
{
ULONG ulStatus = 0;
- stLocalSFAddIndication *pstAddIndication = NULL;
+ struct bcm_add_indication *pstAddIndication = NULL;
stLocalSFAddIndicationAlt *pstAddIndicationDest = NULL;
- pstAddIndication = (stLocalSFAddIndication *)(pvBuffer);
+ pstAddIndication = (struct bcm_add_indication *)(pvBuffer);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "=====>");
if ((pstAddIndication->u8Type == DSD_REQ) ||
(pstAddIndication->u8Type == DSD_RSP) ||
@@ -1553,7 +1539,7 @@ ULONG SetUpTargetDsxBuffers(struct bcm_mini_adapter *Adapter)
if (Adapter->astTargetDsxBuffer[0].ulTargetDsxBuffer)
return 1;
- BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "Size of Each DSX Buffer(Also size of ServiceFlowParamSI): %zx ", sizeof(stServiceFlowParamSI));
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "Size of Each DSX Buffer(Also size of connection manager parameters): %zx ", sizeof(struct bcm_connect_mgr_params));
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "Reading DSX buffer From Target location %x ", DSX_MESSAGE_EXCHANGE_BUFFER);
Status = rdmalt(Adapter, DSX_MESSAGE_EXCHANGE_BUFFER, (PUINT)&ulTargetDsxBuffersBase, sizeof(UINT));
@@ -1564,7 +1550,7 @@ ULONG SetUpTargetDsxBuffers(struct bcm_mini_adapter *Adapter)
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "Base Address Of DSX Target Buffer : 0x%lx", ulTargetDsxBuffersBase);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "Tgt Buffer is Now %lx :", ulTargetDsxBuffersBase);
- ulCntTargetBuffers = DSX_MESSAGE_EXCHANGE_BUFFER_SIZE / sizeof(stServiceFlowParamSI);
+ ulCntTargetBuffers = DSX_MESSAGE_EXCHANGE_BUFFER_SIZE / sizeof(struct bcm_connect_mgr_params);
Adapter->ulTotalTargetBuffersAvailable =
ulCntTargetBuffers > MAX_TARGET_DSX_BUFFERS ?
@@ -1576,7 +1562,7 @@ ULONG SetUpTargetDsxBuffers(struct bcm_mini_adapter *Adapter)
Adapter->astTargetDsxBuffer[i].ulTargetDsxBuffer = ulTargetDsxBuffersBase;
Adapter->astTargetDsxBuffer[i].valid = 1;
Adapter->astTargetDsxBuffer[i].tid = 0;
- ulTargetDsxBuffersBase += sizeof(stServiceFlowParamSI);
+ ulTargetDsxBuffersBase += sizeof(struct bcm_connect_mgr_params);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, " Target DSX Buffer %lx setup at 0x%lx",
i, Adapter->astTargetDsxBuffer[i].ulTargetDsxBuffer);
}
@@ -1647,7 +1633,7 @@ int FreeAdapterDsxBuffer(struct bcm_mini_adapter *Adapter)
BOOLEAN CmControlResponseMessage(struct bcm_mini_adapter *Adapter, /* <Pointer to the Adapter structure */
PVOID pvBuffer /* Starting Address of the Buffer, that contains the AddIndication Data */)
{
- stServiceFlowParamSI *psfLocalSet = NULL;
+ struct bcm_connect_mgr_params *psfLocalSet = NULL;
stLocalSFAddIndicationAlt *pstAddIndication = NULL;
stLocalSFChangeIndicationAlt *pstChangeIndication = NULL;
struct bcm_leader *pLeader = NULL;
@@ -1658,7 +1644,7 @@ BOOLEAN CmControlResponseMessage(struct bcm_mini_adapter *Adapter, /* <Pointer
*/
pstAddIndication = RestoreCmControlResponseMessage(Adapter, pvBuffer);
if (pstAddIndication == NULL) {
- ClearTargetDSXBuffer(Adapter, ((stLocalSFAddIndication *)pvBuffer)->u16TID, FALSE);
+ ClearTargetDSXBuffer(Adapter, ((struct bcm_add_indication *)pvBuffer)->u16TID, FALSE);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_PRINTK, 0, 0, "Error in restoring Service Flow param structure from DSx message");
return FALSE;
}
@@ -1870,10 +1856,10 @@ BOOLEAN CmControlResponseMessage(struct bcm_mini_adapter *Adapter, /* <Pointer
UINT uiSearchRuleIndex;
ULONG ulSFID;
- pLeader->PLength = sizeof(stLocalSFDeleteIndication);
- *((stLocalSFDeleteIndication *)&(Adapter->caDsxReqResp[LEADER_SIZE])) = *((stLocalSFDeleteIndication *)pstAddIndication);
+ pLeader->PLength = sizeof(struct bcm_del_indication);
+ *((struct bcm_del_indication *)&(Adapter->caDsxReqResp[LEADER_SIZE])) = *((struct bcm_del_indication *)pstAddIndication);
- ulSFID = ntohl(((stLocalSFDeleteIndication *)pstAddIndication)->u32SFID);
+ ulSFID = ntohl(((struct bcm_del_indication *)pstAddIndication)->u32SFID);
uiSearchRuleIndex = SearchSfid(Adapter, ulSFID);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "DSD - Removing connection %x", uiSearchRuleIndex);
@@ -1884,7 +1870,7 @@ BOOLEAN CmControlResponseMessage(struct bcm_mini_adapter *Adapter, /* <Pointer
}
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, CONN_MSG, DBG_LVL_ALL, "SENDING DSD RESPONSE TO MAC");
- ((stLocalSFDeleteIndication *)&(Adapter->caDsxReqResp[LEADER_SIZE]))->u8Type = DSD_RSP;
+ ((struct bcm_del_indication *)&(Adapter->caDsxReqResp[LEADER_SIZE]))->u8Type = DSD_RSP;
CopyBufferToControlPacket(Adapter, (PVOID)Adapter->caDsxReqResp);
}
case DSD_RSP:
@@ -1927,7 +1913,7 @@ int get_dsx_sf_data_to_application(struct bcm_mini_adapter *Adapter, UINT uiSFId
VOID OverrideServiceFlowParams(struct bcm_mini_adapter *Adapter, PUINT puiBuffer)
{
B_UINT32 u32NumofSFsinMsg = ntohl(*(puiBuffer + 1));
- stIM_SFHostNotify *pHostInfo = NULL;
+ struct bcm_stim_sfhostnotify *pHostInfo = NULL;
UINT uiSearchRuleIndex = 0;
ULONG ulSFID = 0;
@@ -1936,7 +1922,7 @@ VOID OverrideServiceFlowParams(struct bcm_mini_adapter *Adapter, PUINT puiBuffer
while (u32NumofSFsinMsg != 0 && u32NumofSFsinMsg < NO_OF_QUEUES) {
u32NumofSFsinMsg--;
- pHostInfo = (stIM_SFHostNotify *)puiBuffer;
+ pHostInfo = (struct bcm_stim_sfhostnotify *)puiBuffer;
puiBuffer = (PUINT)(pHostInfo + 1);
ulSFID = ntohl(pHostInfo->SFID);
diff --git a/drivers/staging/bcm/CmHost.h b/drivers/staging/bcm/CmHost.h
index 4cc6f93f2321..1c5a07c7bbe2 100644
--- a/drivers/staging/bcm/CmHost.h
+++ b/drivers/staging/bcm/CmHost.h
@@ -35,8 +35,7 @@ typedef struct stLocalSFAddRequestAlt{
B_UINT16 u16VCID;
- /// \brief structure ParameterSet
- stServiceFlowParamSI sfParameterSet;
+ struct bcm_connect_mgr_params sfParameterSet;
//USE_MEMORY_MANAGER();
}stLocalSFAddRequestAlt;
@@ -50,12 +49,9 @@ typedef struct stLocalSFAddIndicationAlt{
B_UINT16 u16CID;
/// \brief 16bitVCID
B_UINT16 u16VCID;
- /// \brief structure AuthorizedSet
- stServiceFlowParamSI sfAuthorizedSet;
- /// \brief structure AdmittedSet
- stServiceFlowParamSI sfAdmittedSet;
- /// \brief structure ActiveSet
- stServiceFlowParamSI sfActiveSet;
+ struct bcm_connect_mgr_params sfAuthorizedSet;
+ struct bcm_connect_mgr_params sfAdmittedSet;
+ struct bcm_connect_mgr_params sfActiveSet;
B_UINT8 u8CC; /**< Confirmation Code*/
B_UINT8 u8Padd; /**< 8-bit Padding */
@@ -72,12 +68,9 @@ typedef struct stLocalSFAddConfirmationAlt{
B_UINT16 u16CID;
/// \brief 16bitVCID
B_UINT16 u16VCID;
- /// \brief structure AuthorizedSet
- stServiceFlowParamSI sfAuthorizedSet;
- /// \brief structure AdmittedSet
- stServiceFlowParamSI sfAdmittedSet;
- /// \brief structure ActiveSet
- stServiceFlowParamSI sfActiveSet;
+ struct bcm_connect_mgr_params sfAuthorizedSet;
+ struct bcm_connect_mgr_params sfAdmittedSet;
+ struct bcm_connect_mgr_params sfActiveSet;
}stLocalSFAddConfirmationAlt;
@@ -91,16 +84,13 @@ typedef struct stLocalSFChangeRequestAlt{
/// \brief 16bitVCID
B_UINT16 u16VCID;
/*
- //Pointer location at which following Service Flow param Structure can be read
- //from the target. We get only the address location and we need to read out the
- //entire SF param structure at the given location on target
+ //Pointer location at which following connection manager param Structure can be read
+ //from the target. We only get the address location and we need to read out the
+ //entire connection manager param structure at the given location on target
*/
- /// \brief structure AuthorizedSet
- stServiceFlowParamSI sfAuthorizedSet;
- /// \brief structure AdmittedSet
- stServiceFlowParamSI sfAdmittedSet;
- /// \brief structure ParameterSet
- stServiceFlowParamSI sfActiveSet;
+ struct bcm_connect_mgr_params sfAuthorizedSet;
+ struct bcm_connect_mgr_params sfAdmittedSet;
+ struct bcm_connect_mgr_params sfActiveSet;
B_UINT8 u8CC; /**< Confirmation Code*/
B_UINT8 u8Padd; /**< 8-bit Padding */
@@ -117,12 +107,9 @@ typedef struct stLocalSFChangeConfirmationAlt{
B_UINT16 u16CID;
/// \brief 16bitVCID
B_UINT16 u16VCID;
- /// \brief structure AuthorizedSet
- stServiceFlowParamSI sfAuthorizedSet;
- /// \brief structure AdmittedSet
- stServiceFlowParamSI sfAdmittedSet;
- /// \brief structure ActiveSet
- stServiceFlowParamSI sfActiveSet;
+ struct bcm_connect_mgr_params sfAuthorizedSet;
+ struct bcm_connect_mgr_params sfAdmittedSet;
+ struct bcm_connect_mgr_params sfActiveSet;
}stLocalSFChangeConfirmationAlt;
@@ -135,12 +122,9 @@ typedef struct stLocalSFChangeIndicationAlt{
B_UINT16 u16CID;
/// \brief 16bitVCID
B_UINT16 u16VCID;
- /// \brief structure AuthorizedSet
- stServiceFlowParamSI sfAuthorizedSet;
- /// \brief structure AdmittedSet
- stServiceFlowParamSI sfAdmittedSet;
- /// \brief structure ActiveSet
- stServiceFlowParamSI sfActiveSet;
+ struct bcm_connect_mgr_params sfAuthorizedSet;
+ struct bcm_connect_mgr_params sfAdmittedSet;
+ struct bcm_connect_mgr_params sfActiveSet;
B_UINT8 u8CC; /**< Confirmation Code*/
B_UINT8 u8Padd; /**< 8-bit Padding */
diff --git a/drivers/staging/bcm/InterfaceInit.c b/drivers/staging/bcm/InterfaceInit.c
index 8f85de6a57ba..b05f5f73548c 100644
--- a/drivers/staging/bcm/InterfaceInit.c
+++ b/drivers/staging/bcm/InterfaceInit.c
@@ -8,6 +8,7 @@ static struct usb_device_id InterfaceUsbtable[] = {
{ USB_DEVICE(BCM_USB_VENDOR_ID_ZTE, BCM_USB_PRODUCT_ID_226) },
{ USB_DEVICE(BCM_USB_VENDOR_ID_FOXCONN, BCM_USB_PRODUCT_ID_1901) },
{ USB_DEVICE(BCM_USB_VENDOR_ID_ZTE, BCM_USB_PRODUCT_ID_ZTE_TU25) },
+ { USB_DEVICE(BCM_USB_VENDOR_ID_ZTE, BCM_USB_PRODUCT_ID_ZTE_226) },
{ }
};
MODULE_DEVICE_TABLE(usb, InterfaceUsbtable);
@@ -669,16 +670,24 @@ struct class *bcm_class;
static __init int bcm_init(void)
{
- printk(KERN_INFO "%s: %s, %s\n", DRV_NAME, DRV_DESCRIPTION, DRV_VERSION);
- printk(KERN_INFO "%s\n", DRV_COPYRIGHT);
+ int retval;
+
+ pr_info("%s: %s, %s\n", DRV_NAME, DRV_DESCRIPTION, DRV_VERSION);
+ pr_info("%s\n", DRV_COPYRIGHT);
bcm_class = class_create(THIS_MODULE, DRV_NAME);
if (IS_ERR(bcm_class)) {
- printk(KERN_ERR DRV_NAME ": could not create class\n");
+ pr_err(DRV_NAME ": could not create class\n");
return PTR_ERR(bcm_class);
}
- return usb_register(&usbbcm_driver);
+ retval = usb_register(&usbbcm_driver);
+ if (retval < 0) {
+ pr_err(DRV_NAME ": could not register usb driver\n");
+ class_destroy(bcm_class);
+ return retval;
+ }
+ return 0;
}
static __exit void bcm_exit(void)
diff --git a/drivers/staging/bcm/InterfaceInit.h b/drivers/staging/bcm/InterfaceInit.h
index 058315a64c05..866924e35f9c 100644
--- a/drivers/staging/bcm/InterfaceInit.h
+++ b/drivers/staging/bcm/InterfaceInit.h
@@ -1,27 +1,26 @@
#ifndef _INTERFACE_INIT_H
#define _INTERFACE_INIT_H
-#define BCM_USB_VENDOR_ID_T3 0x198f
-#define BCM_USB_VENDOR_ID_FOXCONN 0x0489
-#define BCM_USB_VENDOR_ID_ZTE 0x19d2
+#define BCM_USB_VENDOR_ID_T3 0x198f
+#define BCM_USB_VENDOR_ID_FOXCONN 0x0489
+#define BCM_USB_VENDOR_ID_ZTE 0x19d2
-#define BCM_USB_PRODUCT_ID_T3 0x0300
-#define BCM_USB_PRODUCT_ID_T3B 0x0210
-#define BCM_USB_PRODUCT_ID_T3L 0x0220
-#define BCM_USB_PRODUCT_ID_SM250 0xbccd
-#define BCM_USB_PRODUCT_ID_SYM 0x15E
-#define BCM_USB_PRODUCT_ID_1901 0xe017
-#define BCM_USB_PRODUCT_ID_226 0x0132
-#define BCM_USB_PRODUCT_ID_ZTE_TU25 0x0007
+#define BCM_USB_PRODUCT_ID_T3 0x0300
+#define BCM_USB_PRODUCT_ID_T3B 0x0210
+#define BCM_USB_PRODUCT_ID_T3L 0x0220
+#define BCM_USB_PRODUCT_ID_SM250 0xbccd
+#define BCM_USB_PRODUCT_ID_SYM 0x15E
+#define BCM_USB_PRODUCT_ID_1901 0xe017
+#define BCM_USB_PRODUCT_ID_226 0x0132 /* not sure if this is valid */
+#define BCM_USB_PRODUCT_ID_ZTE_226 0x172
+#define BCM_USB_PRODUCT_ID_ZTE_TU25 0x0007
-#define BCM_USB_MINOR_BASE 192
+#define BCM_USB_MINOR_BASE 192
+int InterfaceInitialize(void);
-INT InterfaceInitialize(void);
+int InterfaceExit(void);
-INT InterfaceExit(void);
-
-INT usbbcm_worker_thread(PS_INTERFACE_ADAPTER psIntfAdapter);
+int usbbcm_worker_thread(PS_INTERFACE_ADAPTER psIntfAdapter);
#endif
-
diff --git a/drivers/staging/bcm/Kconfig b/drivers/staging/bcm/Kconfig
index 96adb1026c4f..83c9752504d4 100644
--- a/drivers/staging/bcm/Kconfig
+++ b/drivers/staging/bcm/Kconfig
@@ -1,6 +1,6 @@
config BCM_WIMAX
tristate "Beceem BCS200/BCS220-3 and BCSM250 wimax support"
- depends on USB && NET && EXPERIMENTAL
+ depends on USB && NET
default N
help
This is an experimental driver for the Beceem WIMAX chipset used
diff --git a/drivers/staging/bcm/Misc.c b/drivers/staging/bcm/Misc.c
index f545716c666d..f13a9582a82f 100644
--- a/drivers/staging/bcm/Misc.c
+++ b/drivers/staging/bcm/Misc.c
@@ -752,7 +752,10 @@ VOID DumpPackInfo(struct bcm_mini_adapter *Adapter)
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "AuthzSet: %x\n", Adapter->PackInfo[uiLoopIndex].bAuthorizedSet);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "ClassifyPrority: %x\n", Adapter->PackInfo[uiLoopIndex].bClassifierPriority);
BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "uiMaxLatency: %x\n", Adapter->PackInfo[uiLoopIndex].uiMaxLatency);
- BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "ServiceClassName: %x %x %x %x\n", Adapter->PackInfo[uiLoopIndex].ucServiceClassName[0], Adapter->PackInfo[uiLoopIndex].ucServiceClassName[1], Adapter->PackInfo[uiLoopIndex].ucServiceClassName[2], Adapter->PackInfo[uiLoopIndex].ucServiceClassName[3]);
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_OTHERS, DUMP_INFO,
+ DBG_LVL_ALL, "ServiceClassName: %*ph\n",
+ 4, Adapter->PackInfo[uiLoopIndex].
+ ucServiceClassName);
/* BCM_DEBUG_PRINT (Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "bHeaderSuppressionEnabled :%X\n", Adapter->PackInfo[uiLoopIndex].bHeaderSuppressionEnabled);
* BCM_DEBUG_PRINT (Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "uiTotalTxBytes:%X\n", Adapter->PackInfo[uiLoopIndex].uiTotalTxBytes);
* BCM_DEBUG_PRINT (Adapter, DBG_TYPE_OTHERS, DUMP_INFO, DBG_LVL_ALL, "uiTotalRxBytes:%X\n", Adapter->PackInfo[uiLoopIndex].uiTotalRxBytes);
diff --git a/drivers/staging/bcm/PHSModule.c b/drivers/staging/bcm/PHSModule.c
index 479574234e4c..6dc0bbcfeab0 100644
--- a/drivers/staging/bcm/PHSModule.c
+++ b/drivers/staging/bcm/PHSModule.c
@@ -66,7 +66,7 @@ Input parameters: IN struct bcm_mini_adapter *Adapter - Miniport Adapte
BOOLEAN bHeaderSuppressionEnabled - indicates if header suprression is enabled for SF.
Return: STATUS_SUCCESS - If the send was successful.
- Other - If an error occured.
+ Other - If an error occurred.
*/
int PHSTransmit(struct bcm_mini_adapter *Adapter,
@@ -346,7 +346,7 @@ int phs_init(PPHS_DEVICE_EXTENSION pPhsdeviceExtension, struct bcm_mini_adapter
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\n phs_init Successfull");
+ BCM_DEBUG_PRINT(Adapter,DBG_TYPE_OTHERS, PHS_DISPATCH, DBG_LVL_ALL, "\n phs_init Successful");
return STATUS_SUCCESS;
}
diff --git a/drivers/staging/bcm/Prototypes.h b/drivers/staging/bcm/Prototypes.h
index 3c8cc5ba2e2e..3ec8f800a5b0 100644
--- a/drivers/staging/bcm/Prototypes.h
+++ b/drivers/staging/bcm/Prototypes.h
@@ -95,7 +95,7 @@ void beceem_parse_target_struct(struct bcm_mini_adapter *Adapter);
int bcm_ioctl_fw_download(struct bcm_mini_adapter *Adapter, struct bcm_firmware_info *psFwInfo);
void CopyMIBSExtendedSFParameters(struct bcm_mini_adapter *Adapter,
- CServiceFlowParamSI *psfLocalSet, UINT uiSearchRuleIndex);
+ struct bcm_connect_mgr_params *psfLocalSet, UINT uiSearchRuleIndex);
VOID ResetCounters(struct bcm_mini_adapter *Adapter);
diff --git a/drivers/staging/bcm/Transmit.c b/drivers/staging/bcm/Transmit.c
index 5e603ce76fea..27e8c890777b 100644
--- a/drivers/staging/bcm/Transmit.c
+++ b/drivers/staging/bcm/Transmit.c
@@ -1,162 +1,149 @@
/**
-@file Transmit.c
-@defgroup tx_functions Transmission
-@section Queueing
-@dot
-digraph transmit1 {
-node[shape=box]
-edge[weight=5;color=red]
-
-bcm_transmit->GetPacketQueueIndex[label="IP Packet"]
-GetPacketQueueIndex->IpVersion4[label="IPV4"]
-GetPacketQueueIndex->IpVersion6[label="IPV6"]
-}
-
-@enddot
-
-@section De-Queueing
-@dot
-digraph transmit2 {
-node[shape=box]
-edge[weight=5;color=red]
-interrupt_service_thread->transmit_packets
-tx_pkt_hdler->transmit_packets
-transmit_packets->CheckAndSendPacketFromIndex
-transmit_packets->UpdateTokenCount
-CheckAndSendPacketFromIndex->PruneQueue
-CheckAndSendPacketFromIndex->IsPacketAllowedForFlow
-CheckAndSendPacketFromIndex->SendControlPacket[label="control pkt"]
-SendControlPacket->bcm_cmd53
-CheckAndSendPacketFromIndex->SendPacketFromQueue[label="data pkt"]
-SendPacketFromQueue->SetupNextSend->bcm_cmd53
-}
-@enddot
-*/
+ * @file Transmit.c
+ * @defgroup tx_functions Transmission
+ * @section Queueing
+ * @dot
+ * digraph transmit1 {
+ * node[shape=box]
+ * edge[weight=5;color=red]
+ *
+ * bcm_transmit->GetPacketQueueIndex[label="IP Packet"]
+ * GetPacketQueueIndex->IpVersion4[label="IPV4"]
+ * GetPacketQueueIndex->IpVersion6[label="IPV6"]
+ * }
+ *
+ * @enddot
+ *
+ * @section De-Queueing
+ * @dot
+ * digraph transmit2 {
+ * node[shape=box]
+ * edge[weight=5;color=red]
+ * interrupt_service_thread->transmit_packets
+ * tx_pkt_hdler->transmit_packets
+ * transmit_packets->CheckAndSendPacketFromIndex
+ * transmit_packets->UpdateTokenCount
+ * CheckAndSendPacketFromIndex->PruneQueue
+ * CheckAndSendPacketFromIndex->IsPacketAllowedForFlow
+ * CheckAndSendPacketFromIndex->SendControlPacket[label="control pkt"]
+ * SendControlPacket->bcm_cmd53
+ * CheckAndSendPacketFromIndex->SendPacketFromQueue[label="data pkt"]
+ * SendPacketFromQueue->SetupNextSend->bcm_cmd53
+ * }
+ * @enddot
+ */
#include "headers.h"
-
/**
-@ingroup ctrl_pkt_functions
-This function dispatches control packet to the h/w interface
-@return zero(success) or -ve value(failure)
-*/
-INT SendControlPacket(struct bcm_mini_adapter *Adapter, char *pControlPacket)
+ * @ingroup ctrl_pkt_functions
+ * This function dispatches control packet to the h/w interface
+ * @return zero(success) or -ve value(failure)
+ */
+int SendControlPacket(struct bcm_mini_adapter *Adapter, char *pControlPacket)
{
struct bcm_leader *PLeader = (struct bcm_leader *)pControlPacket;
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Tx");
- if(!pControlPacket || !Adapter)
- {
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Got NULL Control Packet or Adapter");
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Tx");
+ if (!pControlPacket || !Adapter) {
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Got NULL Control Packet or Adapter");
+ return STATUS_FAILURE;
+ }
+ if ((atomic_read(&Adapter->CurrNumFreeTxDesc) <
+ ((PLeader->PLength-1)/MAX_DEVICE_DESC_SIZE)+1)) {
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "NO FREE DESCRIPTORS TO SEND CONTROL PACKET");
return STATUS_FAILURE;
}
- if((atomic_read( &Adapter->CurrNumFreeTxDesc ) <
- ((PLeader->PLength-1)/MAX_DEVICE_DESC_SIZE)+1))
- {
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "NO FREE DESCRIPTORS TO SEND CONTROL PACKET");
- return STATUS_FAILURE;
- }
/* Update the netdevice statistics */
/* Dump Packet */
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader Status: %x", PLeader->Status);
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader VCID: %x",PLeader->Vcid);
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader Length: %x",PLeader->PLength);
- if(Adapter->device_removed)
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader Status: %x", PLeader->Status);
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader VCID: %x", PLeader->Vcid);
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "Leader Length: %x", PLeader->PLength);
+ if (Adapter->device_removed)
return 0;
if (netif_msg_pktdata(Adapter))
print_hex_dump(KERN_DEBUG, PFX "tx control: ", DUMP_PREFIX_NONE,
- 16, 1, pControlPacket, PLeader->PLength + LEADER_SIZE, 0);
+ 16, 1, pControlPacket, PLeader->PLength + LEADER_SIZE, 0);
Adapter->interface_transmit(Adapter->pvInterfaceAdapter,
- pControlPacket, (PLeader->PLength + LEADER_SIZE));
+ pControlPacket, (PLeader->PLength + LEADER_SIZE));
atomic_dec(&Adapter->CurrNumFreeTxDesc);
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "<=========");
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_CONTROL, DBG_LVL_ALL, "<=========");
return STATUS_SUCCESS;
}
/**
-@ingroup tx_functions
-This function despatches the IP packets with the given vcid
-to the target via the host h/w interface.
-@return zero(success) or -ve value(failure)
-*/
-INT SetupNextSend(struct bcm_mini_adapter *Adapter, struct sk_buff *Packet, USHORT Vcid)
+ * @ingroup tx_functions
+ * This function despatches the IP packets with the given vcid
+ * to the target via the host h/w interface.
+ * @return zero(success) or -ve value(failure)
+ */
+int SetupNextSend(struct bcm_mini_adapter *Adapter, struct sk_buff *Packet, USHORT Vcid)
{
- int status=0;
- BOOLEAN bHeaderSupressionEnabled = FALSE;
- B_UINT16 uiClassifierRuleID;
+ int status = 0;
+ BOOLEAN bHeaderSupressionEnabled = FALSE;
+ B_UINT16 uiClassifierRuleID;
u16 QueueIndex = skb_get_queue_mapping(Packet);
- struct bcm_leader Leader={0};
+ struct bcm_leader Leader = {0};
- if(Packet->len > MAX_DEVICE_DESC_SIZE)
- {
+ if (Packet->len > MAX_DEVICE_DESC_SIZE) {
status = STATUS_FAILURE;
goto errExit;
}
/* Get the Classifier Rule ID */
- uiClassifierRuleID = *((UINT32*) (Packet->cb)+SKB_CB_CLASSIFICATION_OFFSET);
+ uiClassifierRuleID = *((UINT32 *) (Packet->cb) + SKB_CB_CLASSIFICATION_OFFSET);
bHeaderSupressionEnabled = Adapter->PackInfo[QueueIndex].bHeaderSuppressionEnabled
& Adapter->bPHSEnabled;
- if(Adapter->device_removed)
- {
+ if (Adapter->device_removed) {
status = STATUS_FAILURE;
goto errExit;
- }
+ }
status = PHSTransmit(Adapter, &Packet, Vcid, uiClassifierRuleID, bHeaderSupressionEnabled,
- (UINT *)&Packet->len, Adapter->PackInfo[QueueIndex].bEthCSSupport);
+ (UINT *)&Packet->len, Adapter->PackInfo[QueueIndex].bEthCSSupport);
- if(status != STATUS_SUCCESS)
- {
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "PHS Transmit failed..\n");
+ if (status != STATUS_SUCCESS) {
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "PHS Transmit failed..\n");
goto errExit;
}
- Leader.Vcid = Vcid;
+ Leader.Vcid = Vcid;
- if(TCP_ACK == *((UINT32*) (Packet->cb) + SKB_CB_TCPACK_OFFSET ))
+ if (TCP_ACK == *((UINT32 *) (Packet->cb) + SKB_CB_TCPACK_OFFSET))
Leader.Status = LEADER_STATUS_TCP_ACK;
else
Leader.Status = LEADER_STATUS;
- if(Adapter->PackInfo[QueueIndex].bEthCSSupport)
- {
+ if (Adapter->PackInfo[QueueIndex].bEthCSSupport) {
Leader.PLength = Packet->len;
- if(skb_headroom(Packet) < LEADER_SIZE)
- {
- if((status = skb_cow(Packet,LEADER_SIZE)))
- {
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL,"bcm_transmit : Failed To Increase headRoom\n");
+ if (skb_headroom(Packet) < LEADER_SIZE) {
+ status = skb_cow(Packet, LEADER_SIZE);
+ if (status) {
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, NEXT_SEND, DBG_LVL_ALL, "bcm_transmit : Failed To Increase headRoom\n");
goto errExit;
}
}
skb_push(Packet, LEADER_SIZE);
memcpy(Packet->data, &Leader, LEADER_SIZE);
- }
- else
- {
+ } else {
Leader.PLength = Packet->len - ETH_HLEN;
memcpy((struct bcm_leader *)skb_pull(Packet, (ETH_HLEN - LEADER_SIZE)), &Leader, LEADER_SIZE);
}
status = Adapter->interface_transmit(Adapter->pvInterfaceAdapter,
- Packet->data, (Leader.PLength + LEADER_SIZE));
- if(status)
- {
+ Packet->data, (Leader.PLength + LEADER_SIZE));
+ if (status) {
++Adapter->dev->stats.tx_errors;
if (netif_msg_tx_err(Adapter))
pr_info(PFX "%s: transmit error %d\n", Adapter->dev->name,
status);
- }
- else
- {
+ } else {
struct net_device_stats *netstats = &Adapter->dev->stats;
Adapter->PackInfo[QueueIndex].uiTotalTxBytes += Leader.PLength;
@@ -175,7 +162,6 @@ INT SetupNextSend(struct bcm_mini_adapter *Adapter, struct sk_buff *Packet, USH
atomic_dec(&Adapter->CurrNumFreeTxDesc);
errExit:
-
dev_kfree_skb(Packet);
return status;
}
@@ -188,73 +174,65 @@ static int tx_pending(struct bcm_mini_adapter *Adapter)
}
/**
-@ingroup tx_functions
-Transmit thread
-*/
-int tx_pkt_handler(struct bcm_mini_adapter *Adapter /**< pointer to adapter object*/
- )
+ * @ingroup tx_functions
+ * Transmit thread
+ */
+int tx_pkt_handler(struct bcm_mini_adapter *Adapter /**< pointer to adapter object*/)
{
int status = 0;
- while(! kthread_should_stop()) {
+ while (!kthread_should_stop()) {
/* FIXME - the timeout looks like workaround for racey usage of TxPktAvail */
- if(Adapter->LinkUpStatus)
+ if (Adapter->LinkUpStatus)
wait_event_timeout(Adapter->tx_packet_wait_queue,
- tx_pending(Adapter), msecs_to_jiffies(10));
+ tx_pending(Adapter), msecs_to_jiffies(10));
else
wait_event_interruptible(Adapter->tx_packet_wait_queue,
- tx_pending(Adapter));
+ tx_pending(Adapter));
if (Adapter->device_removed)
break;
- if(Adapter->downloadDDR == 1)
- {
- Adapter->downloadDDR +=1;
+ if (Adapter->downloadDDR == 1) {
+ Adapter->downloadDDR += 1;
status = download_ddr_settings(Adapter);
- if(status)
+ if (status)
pr_err(PFX "DDR DOWNLOAD FAILED! %d\n", status);
continue;
}
- //Check end point for halt/stall.
- if(Adapter->bEndPointHalted == TRUE)
- {
+ /* Check end point for halt/stall. */
+ if (Adapter->bEndPointHalted == TRUE) {
Bcm_clear_halt_of_endpoints(Adapter);
Adapter->bEndPointHalted = FALSE;
StartInterruptUrb((PS_INTERFACE_ADAPTER)(Adapter->pvInterfaceAdapter));
}
- if(Adapter->LinkUpStatus && !Adapter->IdleMode)
- {
- if(atomic_read(&Adapter->TotalPacketCount))
- {
+ if (Adapter->LinkUpStatus && !Adapter->IdleMode) {
+ if (atomic_read(&Adapter->TotalPacketCount))
update_per_sf_desc_cnts(Adapter);
- }
}
- if( atomic_read(&Adapter->CurrNumFreeTxDesc) &&
+ if (atomic_read(&Adapter->CurrNumFreeTxDesc) &&
Adapter->LinkStatus == SYNC_UP_REQUEST &&
- !Adapter->bSyncUpRequestSent)
- {
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Calling LinkMessage");
+ !Adapter->bSyncUpRequestSent) {
+
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Calling LinkMessage");
LinkMessage(Adapter);
}
- if((Adapter->IdleMode || Adapter->bShutStatus) && atomic_read(&Adapter->TotalPacketCount))
- {
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Device in Low Power mode...waking up");
- Adapter->usIdleModePattern = ABORT_IDLE_MODE;
- Adapter->bWakeUpDevice = TRUE;
- wake_up(&Adapter->process_rx_cntrlpkt);
+ if ((Adapter->IdleMode || Adapter->bShutStatus) && atomic_read(&Adapter->TotalPacketCount)) {
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Device in Low Power mode...waking up");
+ Adapter->usIdleModePattern = ABORT_IDLE_MODE;
+ Adapter->bWakeUpDevice = TRUE;
+ wake_up(&Adapter->process_rx_cntrlpkt);
}
transmit_packets(Adapter);
-
atomic_set(&Adapter->TxPktAvail, 0);
}
- BCM_DEBUG_PRINT(Adapter,DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Exiting the tx thread..\n");
+ BCM_DEBUG_PRINT(Adapter, DBG_TYPE_TX, TX_PACKETS, DBG_LVL_ALL, "Exiting the tx thread..\n");
Adapter->transmit_packet_thread = NULL;
return 0;
}
diff --git a/drivers/staging/bcm/cntrl_SignalingInterface.h b/drivers/staging/bcm/cntrl_SignalingInterface.h
index 7619e4b819bd..990e809e9680 100644
--- a/drivers/staging/bcm/cntrl_SignalingInterface.h
+++ b/drivers/staging/bcm/cntrl_SignalingInterface.h
@@ -1,423 +1,311 @@
#ifndef CNTRL_SIGNALING_INTERFACE_
#define CNTRL_SIGNALING_INTERFACE_
-
-
-
-#define DSA_REQ 11
-#define DSA_RSP 12
-#define DSA_ACK 13
-#define DSC_REQ 14
-#define DSC_RSP 15
-#define DSC_ACK 16
-#define DSD_REQ 17
-#define DSD_RSP 18
-#define DSD_ACK 19
-#define MAX_CLASSIFIERS_IN_SF 4
-
-
-#define MAX_STRING_LEN 20
-#define MAX_PHS_LENGTHS 255
-#define VENDOR_PHS_PARAM_LENGTH 10
-#define MAX_NUM_ACTIVE_BS 10
-#define AUTH_TOKEN_LENGTH 10
-#define NUM_HARQ_CHANNELS 16 //Changed from 10 to 16 to accommodate all HARQ channels
-#define VENDOR_CLASSIFIER_PARAM_LENGTH 1 //Changed the size to 1 byte since we dnt use it
-#define VENDOR_SPECIF_QOS_PARAM 1
-#define VENDOR_PHS_PARAM_LENGTH 10
-#define MBS_CONTENTS_ID_LENGTH 10
-#define GLOBAL_SF_CLASSNAME_LENGTH 6
-
-#define TYPE_OF_SERVICE_LENGTH 3
-#define IP_MASKED_SRC_ADDRESS_LENGTH 32
-#define IP_MASKED_DEST_ADDRESS_LENGTH 32
-#define PROTOCOL_SRC_PORT_RANGE_LENGTH 4
-#define PROTOCOL_DEST_PORT_RANGE_LENGTH 4
-#define ETHERNET_DEST_MAC_ADDR_LENGTH 12
-#define ETHERNET_SRC_MAC_ADDR_LENGTH 12
-#define NUM_ETHERTYPE_BYTES 3
-#define NUM_IPV6_FLOWLABLE_BYTES 3
-
-
-////////////////////////////////////////////////////////////////////////////////
-////////////////////////structure Definitions///////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////
-/// \brief class cCPacketClassificationRule
-struct _stCPacketClassificationRuleSI{
-
- /** 16bit UserPriority Of The Service Flow*/
- B_UINT16 u16UserPriority;
- /** 16bit VLANID Of The Service Flow*/
- B_UINT16 u16VLANID;
- /** 16bit Packet Classification RuleIndex Of The Service Flow*/
- B_UINT16 u16PacketClassificationRuleIndex;
- /** 8bit Classifier Rule Priority Of The Service Flow*/
- B_UINT8 u8ClassifierRulePriority;
- /** Length of IP TypeOfService field*/
- B_UINT8 u8IPTypeOfServiceLength;
- /** 3bytes IP TypeOfService */
- B_UINT8 u8IPTypeOfService[TYPE_OF_SERVICE_LENGTH];
- /** Protocol used in classification of Service Flow*/
- B_UINT8 u8Protocol;
- /** Length of IP Masked Source Address */
- B_UINT8 u8IPMaskedSourceAddressLength;
- /** IP Masked Source Address used in classification for the Service Flow*/
- B_UINT8 u8IPMaskedSourceAddress[IP_MASKED_SRC_ADDRESS_LENGTH];
- /** Length of IP Destination Address */
- B_UINT8 u8IPDestinationAddressLength;
- /** IP Destination Address used in classification for the Service Flow*/
- B_UINT8 u8IPDestinationAddress[IP_MASKED_DEST_ADDRESS_LENGTH];
- /** Length of Protocol Source Port Range */
- B_UINT8 u8ProtocolSourcePortRangeLength;
- /** Protocol Source Port Range used in the Service Flow*/
- B_UINT8 u8ProtocolSourcePortRange[PROTOCOL_SRC_PORT_RANGE_LENGTH];
- /** Length of Protocol Dest Port Range */
- B_UINT8 u8ProtocolDestPortRangeLength;
- /** Protocol Dest Port Range used in the Service Flow*/
- B_UINT8 u8ProtocolDestPortRange[PROTOCOL_DEST_PORT_RANGE_LENGTH];
- /** Length of Ethernet Destination MAC Address */
- B_UINT8 u8EthernetDestMacAddressLength;
- /** Ethernet Destination MAC Address used in classification of the Service Flow*/
- B_UINT8 u8EthernetDestMacAddress[ETHERNET_DEST_MAC_ADDR_LENGTH];
- /** Length of Ethernet Source MAC Address */
- B_UINT8 u8EthernetSourceMACAddressLength;
- /** Ethernet Source MAC Address used in classification of the Service Flow*/
- B_UINT8 u8EthernetSourceMACAddress[ETHERNET_SRC_MAC_ADDR_LENGTH];
- /** Length of Ethertype */
- B_UINT8 u8EthertypeLength;
- /** 3bytes Ethertype Of The Service Flow*/
- B_UINT8 u8Ethertype[NUM_ETHERTYPE_BYTES];
- /** 8bit Associated PHSI Of The Service Flow*/
- B_UINT8 u8AssociatedPHSI;
- /** Length of Vendor Specific Classifier Param length Of The Service Flow*/
- B_UINT8 u8VendorSpecificClassifierParamLength;
- /** Vendor Specific Classifier Param Of The Service Flow*/
- B_UINT8 u8VendorSpecificClassifierParam[VENDOR_CLASSIFIER_PARAM_LENGTH];
- /** Length Of IPv6 Flow Lable of the Service Flow*/
- B_UINT8 u8IPv6FlowLableLength;
- /** IPv6 Flow Lable Of The Service Flow*/
- B_UINT8 u8IPv6FlowLable[NUM_IPV6_FLOWLABLE_BYTES];
- /** Action associated with the classifier rule*/
- B_UINT8 u8ClassifierActionRule;
- B_UINT16 u16ValidityBitMap;
+#define DSA_REQ 11
+#define DSA_RSP 12
+#define DSA_ACK 13
+#define DSC_REQ 14
+#define DSC_RSP 15
+#define DSC_ACK 16
+#define DSD_REQ 17
+#define DSD_RSP 18
+#define DSD_ACK 19
+#define MAX_CLASSIFIERS_IN_SF 4
+
+#define MAX_STRING_LEN 20
+#define MAX_PHS_LENGTHS 255
+#define VENDOR_PHS_PARAM_LENGTH 10
+#define MAX_NUM_ACTIVE_BS 10
+#define AUTH_TOKEN_LENGTH 10
+#define NUM_HARQ_CHANNELS 16 /* Changed from 10 to 16 to accommodate all HARQ channels */
+#define VENDOR_CLASSIFIER_PARAM_LENGTH 1 /* Changed the size to 1 byte since we dnt use it */
+#define VENDOR_SPECIF_QOS_PARAM 1
+#define VENDOR_PHS_PARAM_LENGTH 10
+#define MBS_CONTENTS_ID_LENGTH 10
+#define GLOBAL_SF_CLASSNAME_LENGTH 6
+
+#define TYPE_OF_SERVICE_LENGTH 3
+#define IP_MASKED_SRC_ADDRESS_LENGTH 32
+#define IP_MASKED_DEST_ADDRESS_LENGTH 32
+#define PROTOCOL_SRC_PORT_RANGE_LENGTH 4
+#define PROTOCOL_DEST_PORT_RANGE_LENGTH 4
+#define ETHERNET_DEST_MAC_ADDR_LENGTH 12
+#define ETHERNET_SRC_MAC_ADDR_LENGTH 12
+#define NUM_ETHERTYPE_BYTES 3
+#define NUM_IPV6_FLOWLABLE_BYTES 3
+
+struct bcm_packet_class_rules {
+ /* 16bit UserPriority Of The Service Flow */
+ B_UINT16 u16UserPriority;
+ /* 16bit VLANID Of The Service Flow */
+ B_UINT16 u16VLANID;
+ /* 16bit Packet Classification RuleIndex Of The Service Flow */
+ B_UINT16 u16PacketClassificationRuleIndex;
+ /* 8bit Classifier Rule Priority Of The Service Flow */
+ B_UINT8 u8ClassifierRulePriority;
+ /* Length of IP TypeOfService field */
+ B_UINT8 u8IPTypeOfServiceLength;
+ /* 3bytes IP TypeOfService */
+ B_UINT8 u8IPTypeOfService[TYPE_OF_SERVICE_LENGTH];
+ /* Protocol used in classification of Service Flow */
+ B_UINT8 u8Protocol;
+ /* Length of IP Masked Source Address */
+ B_UINT8 u8IPMaskedSourceAddressLength;
+ /* IP Masked Source Address used in classification for the Service Flow */
+ B_UINT8 u8IPMaskedSourceAddress[IP_MASKED_SRC_ADDRESS_LENGTH];
+ /* Length of IP Destination Address */
+ B_UINT8 u8IPDestinationAddressLength;
+ /* IP Destination Address used in classification for the Service Flow */
+ B_UINT8 u8IPDestinationAddress[IP_MASKED_DEST_ADDRESS_LENGTH];
+ /* Length of Protocol Source Port Range */
+ B_UINT8 u8ProtocolSourcePortRangeLength;
+ /* Protocol Source Port Range used in the Service Flow */
+ B_UINT8 u8ProtocolSourcePortRange[PROTOCOL_SRC_PORT_RANGE_LENGTH];
+ /* Length of Protocol Dest Port Range */
+ B_UINT8 u8ProtocolDestPortRangeLength;
+ /* Protocol Dest Port Range used in the Service Flow */
+ B_UINT8 u8ProtocolDestPortRange[PROTOCOL_DEST_PORT_RANGE_LENGTH];
+ /* Length of Ethernet Destination MAC Address */
+ B_UINT8 u8EthernetDestMacAddressLength;
+ /* Ethernet Destination MAC Address used in classification of the Service Flow */
+ B_UINT8 u8EthernetDestMacAddress[ETHERNET_DEST_MAC_ADDR_LENGTH];
+ /* Length of Ethernet Source MAC Address */
+ B_UINT8 u8EthernetSourceMACAddressLength;
+ /* Ethernet Source MAC Address used in classification of the Service Flow */
+ B_UINT8 u8EthernetSourceMACAddress[ETHERNET_SRC_MAC_ADDR_LENGTH];
+ /* Length of Ethertype */
+ B_UINT8 u8EthertypeLength;
+ /* 3bytes Ethertype Of The Service Flow */
+ B_UINT8 u8Ethertype[NUM_ETHERTYPE_BYTES];
+ /* 8bit Associated PHSI Of The Service Flow */
+ B_UINT8 u8AssociatedPHSI;
+ /* Length of Vendor Specific Classifier Param length Of The Service Flow */
+ B_UINT8 u8VendorSpecificClassifierParamLength;
+ /* Vendor Specific Classifier Param Of The Service Flow */
+ B_UINT8 u8VendorSpecificClassifierParam[VENDOR_CLASSIFIER_PARAM_LENGTH];
+ /* Length Of IPv6 Flow Lable of the Service Flow */
+ B_UINT8 u8IPv6FlowLableLength;
+ /* IPv6 Flow Lable Of The Service Flow */
+ B_UINT8 u8IPv6FlowLable[NUM_IPV6_FLOWLABLE_BYTES];
+ /* Action associated with the classifier rule */
+ B_UINT8 u8ClassifierActionRule;
+ B_UINT16 u16ValidityBitMap;
};
-typedef struct _stCPacketClassificationRuleSI CCPacketClassificationRuleSI,stCPacketClassificationRuleSI, *pstCPacketClassificationRuleSI;
-
-/// \brief class CPhsRuleSI
-typedef struct _stPhsRuleSI {
- /** 8bit PHS Index Of The Service Flow*/
- B_UINT8 u8PHSI;
- /** PHSF Length Of The Service Flow*/
- B_UINT8 u8PHSFLength;
- /** String of bytes containing header information to be suppressed by the sending CS and reconstructed by the receiving CS*/
- B_UINT8 u8PHSF[MAX_PHS_LENGTHS];
- /** PHSM Length Of The Service Flow*/
- B_UINT8 u8PHSMLength;
- /** PHS Mask for the SF*/
- B_UINT8 u8PHSM[MAX_PHS_LENGTHS];
- /** 8bit Total number of bytes to be suppressed for the Service Flow*/
- B_UINT8 u8PHSS;
- /** 8bit Indicates whether or not Packet Header contents need to be verified prior to suppression */
- B_UINT8 u8PHSV;
- /** Vendor Specific PHS param Length Of The Service Flow*/
- B_UINT8 u8VendorSpecificPHSParamsLength;
- /** Vendor Specific PHS param Of The Service Flow*/
- B_UINT8 u8VendorSpecificPHSParams[VENDOR_PHS_PARAM_LENGTH];
-
- B_UINT8 u8Padding[2];
-}stPhsRuleSI,*pstPhsRuleSI;
-typedef stPhsRuleSI CPhsRuleSI;
-/// \brief structure cConvergenceSLTypes
-struct _stConvergenceSLTypes{
- /** 8bit Phs Classfier Action Of The Service Flow*/
- B_UINT8 u8ClassfierDSCAction;
- /** 8bit Phs DSC Action Of The Service Flow*/
- B_UINT8 u8PhsDSCAction;
- /** 16bit Padding */
- B_UINT8 u8Padding[2];
- /// \brief class cCPacketClassificationRule
- stCPacketClassificationRuleSI cCPacketClassificationRule;
- /// \brief class CPhsRuleSI
- struct _stPhsRuleSI cPhsRule;
+struct bcm_phs_rules {
+ /* 8bit PHS Index Of The Service Flow */
+ B_UINT8 u8PHSI;
+ /* PHSF Length Of The Service Flow */
+ B_UINT8 u8PHSFLength;
+ /* String of bytes containing header information to be suppressed by the sending CS and reconstructed by the receiving CS */
+ B_UINT8 u8PHSF[MAX_PHS_LENGTHS];
+ /* PHSM Length Of The Service Flow */
+ B_UINT8 u8PHSMLength;
+ /* PHS Mask for the SF */
+ B_UINT8 u8PHSM[MAX_PHS_LENGTHS];
+ /* 8bit Total number of bytes to be suppressed for the Service Flow */
+ B_UINT8 u8PHSS;
+ /* 8bit Indicates whether or not Packet Header contents need to be verified prior to suppression */
+ B_UINT8 u8PHSV;
+ /* Vendor Specific PHS param Length Of The Service Flow */
+ B_UINT8 u8VendorSpecificPHSParamsLength;
+ /* Vendor Specific PHS param Of The Service Flow */
+ B_UINT8 u8VendorSpecificPHSParams[VENDOR_PHS_PARAM_LENGTH];
+ B_UINT8 u8Padding[2];
};
-typedef struct _stConvergenceSLTypes stConvergenceSLTypes,CConvergenceSLTypes, *pstConvergenceSLTypes;
-
-
-/// \brief structure CServiceFlowParamSI
-typedef struct _stServiceFlowParamSI{
-
- /** 32bitSFID Of The Service Flow*/
- B_UINT32 u32SFID;
-
- /** 32bit Maximum Sustained Traffic Rate of the Service Flow*/
- B_UINT32 u32MaxSustainedTrafficRate;
-
- /** 32bit Maximum Traffic Burst allowed for the Service Flow*/
- B_UINT32 u32MaxTrafficBurst;
-
- /** 32bit Minimum Reserved Traffic Rate of the Service Flow*/
- B_UINT32 u32MinReservedTrafficRate;
-
- /** 32bit Tolerated Jitter of the Service Flow*/
- B_UINT32 u32ToleratedJitter;
-
- /** 32bit Maximum Latency of the Service Flow*/
- B_UINT32 u32MaximumLatency;
-
- /** 16bitCID Of The Service Flow*/
- B_UINT16 u16CID;
-
- /** 16bit SAID on which the service flow being set up shall be mapped*/
- B_UINT16 u16TargetSAID;
-
- /** 16bit ARQ window size negotiated*/
- B_UINT16 u16ARQWindowSize;
-
- /** 16bit Total Tx delay incl sending, receiving & processing delays */
- B_UINT16 u16ARQRetryTxTimeOut;
-
- /** 16bit Total Rx delay incl sending, receiving & processing delays */
- B_UINT16 u16ARQRetryRxTimeOut;
-
- /** 16bit ARQ block lifetime */
- B_UINT16 u16ARQBlockLifeTime;
-
- /** 16bit ARQ Sync loss timeout*/
- B_UINT16 u16ARQSyncLossTimeOut;
-
- /** 16bit ARQ Purge timeout */
- B_UINT16 u16ARQRxPurgeTimeOut;
-//TODO::Remove this once we move to a new CORR2 driver
- /// \brief Size of an ARQ block
- B_UINT16 u16ARQBlockSize;
-
-//#endif
- /** 16bit Nominal interval b/w consecutive SDU arrivals at MAC SAP*/
- B_UINT16 u16SDUInterArrivalTime;
-
- /** 16bit Specifies the time base for rate measurement */
- B_UINT16 u16TimeBase;
-
- /** 16bit Interval b/w Successive Grant oppurtunities*/
- B_UINT16 u16UnsolicitedGrantInterval;
-
- /** 16bit Interval b/w Successive Polling grant oppurtunities*/
- B_UINT16 u16UnsolicitedPollingInterval;
-
- /** internal var to get the overhead */
- B_UINT16 u16MacOverhead;
-
- /** MBS contents Identifier*/
- B_UINT16 u16MBSContentsID[MBS_CONTENTS_ID_LENGTH];
-
- /** MBS contents Identifier length*/
- B_UINT8 u8MBSContentsIDLength;
-
- /** ServiceClassName Length Of The Service Flow*/
- B_UINT8 u8ServiceClassNameLength;
-
- /** 32bytes ServiceClassName Of The Service Flow*/
- B_UINT8 u8ServiceClassName[32];
-
- /** 8bit Indicates whether or not MBS service is requested for this Serivce Flow*/
- B_UINT8 u8MBSService;
-
- /** 8bit QOS Parameter Set specifies proper application of QoS paramters to Provisioned, Admitted and Active sets*/
- B_UINT8 u8QosParamSet;
-
- /** 8bit Traffic Priority Of the Service Flow */
- B_UINT8 u8TrafficPriority;
-
- /** 8bit Uplink Grant Scheduling Type of The Service Flow */
- B_UINT8 u8ServiceFlowSchedulingType;
-
- /** 8bit Request transmission Policy of the Service Flow*/
- B_UINT8 u8RequesttransmissionPolicy;
-
- /** 8bit Specifies whether SDUs for this Service flow are of FixedLength or Variable length */
- B_UINT8 u8FixedLengthVSVariableLengthSDUIndicator;
-
- /** 8bit Length of the SDU for a fixed length SDU service flow*/
- B_UINT8 u8SDUSize;
- /** 8bit Indicates whether or not ARQ is requested for this connection*/
- B_UINT8 u8ARQEnable;
-
- /**< 8bit Indicates whether or not data has tobe delivered in order to higher layer*/
- B_UINT8 u8ARQDeliverInOrder;
-
- /** 8bit Receiver ARQ ACK processing time */
- B_UINT8 u8RxARQAckProcessingTime;
-
- /** 8bit Convergence Sublayer Specification Of The Service Flow*/
- B_UINT8 u8CSSpecification;
-
- /** 8 bit Type of data delivery service*/
- B_UINT8 u8TypeOfDataDeliveryService;
-
- /** 8bit Specifies whether a service flow may generate Paging */
- B_UINT8 u8PagingPreference;
-
- /** 8bit Indicates the MBS Zone through which the connection or virtual connection is valid */
- B_UINT8 u8MBSZoneIdentifierassignment;
-
- /** 8bit Specifies whether traffic on SF should generate MOB_TRF_IND to MS in sleep mode*/
- B_UINT8 u8TrafficIndicationPreference;
-
- /** 8bit Speciifes the length of predefined Global QoS parameter set encoding for this SF */
- B_UINT8 u8GlobalServicesClassNameLength;
-
- /** 6 byte Speciifes the predefined Global QoS parameter set encoding for this SF */
- B_UINT8 u8GlobalServicesClassName[GLOBAL_SF_CLASSNAME_LENGTH];
-
- /** 8bit Indicates whether or not SN feedback is enabled for the conn */
- B_UINT8 u8SNFeedbackEnabled;
-
- /** Indicates the size of the Fragment Sequence Number for the connection */
- B_UINT8 u8FSNSize;
-
- /** 8bit Number of CIDs in active BS list */
- B_UINT8 u8CIDAllocation4activeBSsLength;
-
- /** CIDs of BS in the active list */
- B_UINT8 u8CIDAllocation4activeBSs[MAX_NUM_ACTIVE_BS];
-
- /** Specifies if PDU extended subheader should be applied on every PDU on this conn*/
- B_UINT8 u8PDUSNExtendedSubheader4HarqReordering;
-
- /** 8bit Specifies whether the connection uses HARQ or not */
- B_UINT8 u8HARQServiceFlows;
-
- /** Specifies the length of Authorization token*/
- B_UINT8 u8AuthTokenLength;
-
- /** Specifies the Authorization token*/
- B_UINT8 u8AuthToken[AUTH_TOKEN_LENGTH];
-
- /** specifes Number of HARQ channels used to carry data length*/
- B_UINT8 u8HarqChannelMappingLength;
-
- /** specifes HARQ channels used to carry data*/
- B_UINT8 u8HARQChannelMapping[NUM_HARQ_CHANNELS];
-
- /** 8bit Length of Vendor Specific QoS Params */
- B_UINT8 u8VendorSpecificQoSParamLength;
-
- /** 1byte Vendor Specific QoS Param Of The Service Flow*/
- B_UINT8 u8VendorSpecificQoSParam[VENDOR_SPECIF_QOS_PARAM];
-
- // indicates total classifiers in the SF
- B_UINT8 u8TotalClassifiers; /**< Total number of valid classifiers*/
- B_UINT8 bValid; /**< Validity flag */
- B_UINT8 u8Padding; /**< Padding byte*/
-
-/**
-Structure for Convergence SubLayer Types with a maximum of 4 classifiers
-*/
- stConvergenceSLTypes cConvergenceSLTypes[MAX_CLASSIFIERS_IN_SF];
-
-} stServiceFlowParamSI, *pstServiceFlowParamSI;
-typedef stServiceFlowParamSI CServiceFlowParamSI;
-
-/**
-structure stLocalSFAddRequest
-*/
-typedef struct _stLocalSFAddRequest{
-
- B_UINT8 u8Type; /**< Type*/
- B_UINT8 eConnectionDir; /**< Connection direction*/
- /// \brief 16 bit TID
- B_UINT16 u16TID; /**< 16bit TID*/
- /// \brief 16bitCID
- B_UINT16 u16CID; /**< 16bit CID*/
- /// \brief 16bitVCID
- B_UINT16 u16VCID; /**< 16bit VCID*/
- /// \brief structure ParameterSet
-
- stServiceFlowParamSI *psfParameterSet; /**< structure ParameterSet*/
-
-}stLocalSFAddRequest, *pstLocalSFAddRequest;
-
-
-/**
-structure stLocalSFAddIndication
-*/
-typedef struct _stLocalSFAddIndication{
-
- B_UINT8 u8Type; /**< Type*/
- B_UINT8 eConnectionDir; /**< Connection Direction*/
- /// \brief 16 bit TID
- B_UINT16 u16TID; /**< TID*/
- /// \brief 16bitCID
- B_UINT16 u16CID; /**< 16bitCID*/
- /// \brief 16bitVCID
- B_UINT16 u16VCID; /**< 16bitVCID*/
-
-
- /// \brief structure AuthorizedSet
- /// \brief structure AuthorizedSet
- stServiceFlowParamSI *psfAuthorizedSet; /**< AuthorizedSet of type stServiceFlowParamSI*/
- /// \brief structure AdmittedSet
- stServiceFlowParamSI *psfAdmittedSet; /**< AdmittedSet of type stServiceFlowParamSI*/
- /// \brief structure ActiveSet
- stServiceFlowParamSI *psfActiveSet; /**< sfActiveSet of type stServiceFlowParamSI*/
- B_UINT8 u8CC; /**< Confirmation Code*/
- B_UINT8 u8Padd; /**< 8-bit Padding */
-
- B_UINT16 u16Padd; /**< 16 bit Padding */
-
-}stLocalSFAddIndication;
+struct bcm_convergence_types {
+ /* 8bit Phs Classfier Action Of The Service Flow */
+ B_UINT8 u8ClassfierDSCAction;
+ /* 8bit Phs DSC Action Of The Service Flow */
+ B_UINT8 u8PhsDSCAction;
+ /* 16bit Padding */
+ B_UINT8 u8Padding[2];
+ /* Packet classification rules structure */
+ struct bcm_packet_class_rules cCPacketClassificationRule;
+ /* Payload header suppression rules structure */
+ struct bcm_phs_rules cPhsRule;
+};
+struct bcm_connect_mgr_params {
+ /* 32bitSFID Of The Service Flow */
+ B_UINT32 u32SFID;
+ /* 32bit Maximum Sustained Traffic Rate of the Service Flow */
+ B_UINT32 u32MaxSustainedTrafficRate;
+ /* 32bit Maximum Traffic Burst allowed for the Service Flow */
+ B_UINT32 u32MaxTrafficBurst;
+ /* 32bit Minimum Reserved Traffic Rate of the Service Flow */
+ B_UINT32 u32MinReservedTrafficRate;
+ /* 32bit Tolerated Jitter of the Service Flow */
+ B_UINT32 u32ToleratedJitter;
+ /* 32bit Maximum Latency of the Service Flow */
+ B_UINT32 u32MaximumLatency;
+ /* 16bitCID Of The Service Flow */
+ B_UINT16 u16CID;
+ /* 16bit SAID on which the service flow being set up shall be mapped */
+ B_UINT16 u16TargetSAID;
+ /* 16bit ARQ window size negotiated */
+ B_UINT16 u16ARQWindowSize;
+ /* 16bit Total Tx delay incl sending, receiving & processing delays */
+ B_UINT16 u16ARQRetryTxTimeOut;
+ /* 16bit Total Rx delay incl sending, receiving & processing delays */
+ B_UINT16 u16ARQRetryRxTimeOut;
+ /* 16bit ARQ block lifetime */
+ B_UINT16 u16ARQBlockLifeTime;
+ /* 16bit ARQ Sync loss timeout */
+ B_UINT16 u16ARQSyncLossTimeOut;
+ /* 16bit ARQ Purge timeout */
+ B_UINT16 u16ARQRxPurgeTimeOut;
+ /* TODO::Remove this once we move to a new CORR2 driver
+ * brief Size of an ARQ block
+ */
+ B_UINT16 u16ARQBlockSize;
+ /* #endif */
+ /* 16bit Nominal interval b/w consecutive SDU arrivals at MAC SAP */
+ B_UINT16 u16SDUInterArrivalTime;
+ /* 16bit Specifies the time base for rate measurement */
+ B_UINT16 u16TimeBase;
+ /* 16bit Interval b/w Successive Grant oppurtunities */
+ B_UINT16 u16UnsolicitedGrantInterval;
+ /* 16bit Interval b/w Successive Polling grant oppurtunities */
+ B_UINT16 u16UnsolicitedPollingInterval;
+ /* internal var to get the overhead */
+ B_UINT16 u16MacOverhead;
+ /* MBS contents Identifier */
+ B_UINT16 u16MBSContentsID[MBS_CONTENTS_ID_LENGTH];
+ /* MBS contents Identifier length */
+ B_UINT8 u8MBSContentsIDLength;
+ /* ServiceClassName Length Of The Service Flow */
+ B_UINT8 u8ServiceClassNameLength;
+ /* 32bytes ServiceClassName Of The Service Flow */
+ B_UINT8 u8ServiceClassName[32];
+ /* 8bit Indicates whether or not MBS service is requested for this Serivce Flow */
+ B_UINT8 u8MBSService;
+ /* 8bit QOS Parameter Set specifies proper application of QoS parameters to Provisioned, Admitted and Active sets */
+ B_UINT8 u8QosParamSet;
+ /* 8bit Traffic Priority Of the Service Flow */
+ B_UINT8 u8TrafficPriority;
+ /* 8bit Uplink Grant Scheduling Type of The Service Flow */
+ B_UINT8 u8ServiceFlowSchedulingType;
+ /* 8bit Request transmission Policy of the Service Flow */
+ B_UINT8 u8RequesttransmissionPolicy;
+ /* 8bit Specifies whether SDUs for this Service flow are of FixedLength or Variable length */
+ B_UINT8 u8FixedLengthVSVariableLengthSDUIndicator;
+ /* 8bit Length of the SDU for a fixed length SDU service flow */
+ B_UINT8 u8SDUSize;
+ /* 8bit Indicates whether or not ARQ is requested for this connection */
+ B_UINT8 u8ARQEnable;
+ /* < 8bit Indicates whether or not data has tobe delivered in order to higher layer */
+ B_UINT8 u8ARQDeliverInOrder;
+ /* 8bit Receiver ARQ ACK processing time */
+ B_UINT8 u8RxARQAckProcessingTime;
+ /* 8bit Convergence Sublayer Specification Of The Service Flow */
+ B_UINT8 u8CSSpecification;
+ /* 8 bit Type of data delivery service */
+ B_UINT8 u8TypeOfDataDeliveryService;
+ /* 8bit Specifies whether a service flow may generate Paging */
+ B_UINT8 u8PagingPreference;
+ /* 8bit Indicates the MBS Zone through which the connection or virtual connection is valid */
+ B_UINT8 u8MBSZoneIdentifierassignment;
+ /* 8bit Specifies whether traffic on SF should generate MOB_TRF_IND to MS in sleep mode */
+ B_UINT8 u8TrafficIndicationPreference;
+ /* 8bit Speciifes the length of predefined Global QoS parameter set encoding for this SF */
+ B_UINT8 u8GlobalServicesClassNameLength;
+ /* 6 byte Speciifes the predefined Global QoS parameter set encoding for this SF */
+ B_UINT8 u8GlobalServicesClassName[GLOBAL_SF_CLASSNAME_LENGTH];
+ /* 8bit Indicates whether or not SN feedback is enabled for the conn */
+ B_UINT8 u8SNFeedbackEnabled;
+ /* Indicates the size of the Fragment Sequence Number for the connection */
+ B_UINT8 u8FSNSize;
+ /* 8bit Number of CIDs in active BS list */
+ B_UINT8 u8CIDAllocation4activeBSsLength;
+ /* CIDs of BS in the active list */
+ B_UINT8 u8CIDAllocation4activeBSs[MAX_NUM_ACTIVE_BS];
+ /* Specifies if PDU extended subheader should be applied on every PDU on this conn */
+ B_UINT8 u8PDUSNExtendedSubheader4HarqReordering;
+ /* 8bit Specifies whether the connection uses HARQ or not */
+ B_UINT8 u8HARQServiceFlows;
+ /* Specifies the length of Authorization token */
+ B_UINT8 u8AuthTokenLength;
+ /* Specifies the Authorization token */
+ B_UINT8 u8AuthToken[AUTH_TOKEN_LENGTH];
+ /* specifes Number of HARQ channels used to carry data length */
+ B_UINT8 u8HarqChannelMappingLength;
+ /* specifes HARQ channels used to carry data */
+ B_UINT8 u8HARQChannelMapping[NUM_HARQ_CHANNELS];
+ /* 8bit Length of Vendor Specific QoS Params */
+ B_UINT8 u8VendorSpecificQoSParamLength;
+ /* 1byte Vendor Specific QoS Param Of The Service Flow */
+ B_UINT8 u8VendorSpecificQoSParam[VENDOR_SPECIF_QOS_PARAM];
+ /* indicates total classifiers in the SF */
+ B_UINT8 u8TotalClassifiers; /* < Total number of valid classifiers */
+ B_UINT8 bValid; /* < Validity flag */
+ B_UINT8 u8Padding; /* < Padding byte */
+ /*
+ * Structure for Convergence SubLayer Types with a maximum of 4 classifiers
+ */
+ struct bcm_convergence_types cConvergenceSLTypes[MAX_CLASSIFIERS_IN_SF];
+};
-typedef struct _stLocalSFAddIndication *pstLocalSFAddIndication;
-/**
-structure stLocalSFChangeRequest is same as structure stLocalSFAddIndication
-*/
-typedef struct _stLocalSFAddIndication stLocalSFChangeRequest, *pstLocalSFChangeRequest;
-/**
-structure stLocalSFChangeIndication is same as structure stLocalSFAddIndication
-*/
-typedef struct _stLocalSFAddIndication stLocalSFChangeIndication, *pstLocalSFChangeIndication;
+struct bcm_add_request {
+ B_UINT8 u8Type; /* < Type */
+ B_UINT8 eConnectionDir; /* < Connection direction */
+ /* brief 16 bit TID */
+ B_UINT16 u16TID; /* < 16bit TID */
+ /* brief 16bitCID */
+ B_UINT16 u16CID; /* < 16bit CID */
+ /* brief 16bitVCID */
+ B_UINT16 u16VCID; /* < 16bit VCID */
+ struct bcm_connect_mgr_params *psfParameterSet; /* < connection manager parameters */
+};
-/**
-structure stLocalSFDeleteRequest
-*/
-typedef struct _stLocalSFDeleteRequest{
- B_UINT8 u8Type; /**< Type*/
- B_UINT8 u8Padding; /**< Padding byte*/
- B_UINT16 u16TID; /**< TID*/
- /// \brief 32bitSFID
- B_UINT32 u32SFID; /**< SFID*/
-}stLocalSFDeleteRequest, *pstLocalSFDeleteRequest;
+struct bcm_add_indication {
+ B_UINT8 u8Type; /* < Type */
+ B_UINT8 eConnectionDir; /* < Connection Direction */
+ /* brief 16 bit TID */
+ B_UINT16 u16TID; /* < TID */
+ /* brief 16bitCID */
+ B_UINT16 u16CID; /* < 16bitCID */
+ /* brief 16bitVCID */
+ B_UINT16 u16VCID; /* < 16bitVCID */
+ struct bcm_connect_mgr_params *psfAuthorizedSet; /* Authorized set of connection manager parameters */
+ struct bcm_connect_mgr_params *psfAdmittedSet; /* Admitted set of connection manager parameters */
+ struct bcm_connect_mgr_params *psfActiveSet; /* Activeset of connection manager parameters */
+ B_UINT8 u8CC; /* <Confirmation Code */
+ B_UINT8 u8Padd; /* < 8-bit Padding */
+ B_UINT16 u16Padd; /* < 16 bit Padding */
+};
-/**
-structure stLocalSFDeleteIndication
-*/
-typedef struct stLocalSFDeleteIndication{
- B_UINT8 u8Type; /**< Type */
- B_UINT8 u8Padding; /**< Padding */
- B_UINT16 u16TID; /**< TID */
- /// \brief 16bitCID
- B_UINT16 u16CID; /**< CID */
- /// \brief 16bitVCID
- B_UINT16 u16VCID; /**< VCID */
- /// \brief 32bitSFID
- B_UINT32 u32SFID; /**< SFID */
- /// \brief 8bit Confirmation code
- B_UINT8 u8ConfirmationCode; /**< Confirmation code */
- B_UINT8 u8Padding1[3]; /**< 3 byte Padding */
-}stLocalSFDeleteIndication;
+struct bcm_del_request {
+ B_UINT8 u8Type; /* < Type */
+ B_UINT8 u8Padding; /* < Padding byte */
+ B_UINT16 u16TID; /* < TID */
+ /* brief 32bitSFID */
+ B_UINT32 u32SFID; /* < SFID */
+};
-typedef struct _stIM_SFHostNotify
-{
- B_UINT32 SFID; //SFID of the service flow
- B_UINT16 newCID; //the new/changed CID
- B_UINT16 VCID; //Get new Vcid if the flow has been made active in CID update TLV, but was inactive earlier or the orig vcid
- B_UINT8 RetainSF; //Indication to Host if the SF is to be retained or deleted; if TRUE-retain else delete
- B_UINT8 QoSParamSet; //QoS paramset of the retained SF
- B_UINT16 u16reserved; //For byte alignment
+struct bcm_del_indication {
+ B_UINT8 u8Type; /* < Type */
+ B_UINT8 u8Padding; /* < Padding */
+ B_UINT16 u16TID; /* < TID */
+ /* brief 16bitCID */
+ B_UINT16 u16CID; /* < CID */
+ /* brief 16bitVCID */
+ B_UINT16 u16VCID; /* < VCID */
+ /* brief 32bitSFID */
+ B_UINT32 u32SFID; /* < SFID */
+ /* brief 8bit Confirmation code */
+ B_UINT8 u8ConfirmationCode; /* < Confirmation code */
+ B_UINT8 u8Padding1[3]; /* < 3 byte Padding */
+};
-} stIM_SFHostNotify;
+struct bcm_stim_sfhostnotify {
+ B_UINT32 SFID; /* SFID of the service flow */
+ B_UINT16 newCID; /* the new/changed CID */
+ B_UINT16 VCID; /* Get new Vcid if the flow has been made active in CID update TLV, but was inactive earlier or the orig vcid */
+ B_UINT8 RetainSF; /* Indication to Host if the SF is to be retained or deleted; if TRUE-retain else delete */
+ B_UINT8 QoSParamSet; /* QoS paramset of the retained SF */
+ B_UINT16 u16reserved; /* For byte alignment */
+};
#endif
diff --git a/drivers/staging/bcm/hostmibs.c b/drivers/staging/bcm/hostmibs.c
index 08d13a4dfd70..10361bb35059 100644
--- a/drivers/staging/bcm/hostmibs.c
+++ b/drivers/staging/bcm/hostmibs.c
@@ -101,7 +101,7 @@ VOID GetDroppedAppCntrlPktMibs(S_MIBS_HOST_STATS_MIBS *pstHostMibs, struct bcm_t
sizeof(S_MIBS_DROPPED_APP_CNTRL_MESSAGES));
}
-VOID CopyMIBSExtendedSFParameters(struct bcm_mini_adapter *Adapter, CServiceFlowParamSI *psfLocalSet, UINT uiSearchRuleIndex)
+VOID CopyMIBSExtendedSFParameters(struct bcm_mini_adapter *Adapter, struct bcm_connect_mgr_params *psfLocalSet, UINT uiSearchRuleIndex)
{
S_MIBS_EXTSERVICEFLOW_PARAMETERS *t = &Adapter->PackInfo[uiSearchRuleIndex].stMibsExtServiceFlowTable;
diff --git a/drivers/staging/bcm/nvm.c b/drivers/staging/bcm/nvm.c
index b179dbab93b5..b034eb5fa6b1 100644
--- a/drivers/staging/bcm/nvm.c
+++ b/drivers/staging/bcm/nvm.c
@@ -577,7 +577,7 @@ static int FlashSectorErase(struct bcm_mini_adapter *Adapter,
* the sector erase cycle is 500 ms to 40000 msec. hence sleeping 10 ms
* won't hamper performance in any case.
*/
- udelay(10000);
+ mdelay(10);
} while ((uiStatus & 0x1) && (iRetries < 400));
if (uiStatus & 0x1) {
@@ -3932,7 +3932,7 @@ int validateFlash2xReadWrite(struct bcm_mini_adapter *Adapter, PFLASH2X_READWRIT
BcmGetSectionValStartOffset(Adapter, ISO_IMAGE2_PART3);
}
- /* since this uiSectEndoffset is the size of iso Image. hence for calculating the vitual endoffset
+ /* since this uiSectEndoffset is the size of iso Image. hence for calculating the virtual endoffset
* it should be added in startoffset. so that check done in last of this function can be valued.
*/
uiSectEndOffset = uiSectStartOffset + uiSectEndOffset;
diff --git a/drivers/staging/bcm/target_params.h b/drivers/staging/bcm/target_params.h
index 14876388b879..ad7ec0054938 100644
--- a/drivers/staging/bcm/target_params.h
+++ b/drivers/staging/bcm/target_params.h
@@ -32,7 +32,7 @@ typedef struct _TARGET_PARAMS
B_UINT32 m_u32PowerSavingModesEnable; //bit 1: 1 Idlemode enable; bit2: 1 Sleepmode Enable
/* PowerSaving Mode Options:
bit 0 = 1: CPE mode - to keep pcmcia if alive;
- bit 1 = 1: CINR reporing in Idlemode Msg
+ bit 1 = 1: CINR reporting in Idlemode Msg
bit 2 = 1: Default PSC Enable in sleepmode*/
B_UINT32 m_u32PowerSavingModeOptions;
diff --git a/drivers/staging/ccg/Kconfig b/drivers/staging/ccg/Kconfig
index 1f00d701da25..8997a8c757aa 100644
--- a/drivers/staging/ccg/Kconfig
+++ b/drivers/staging/ccg/Kconfig
@@ -2,7 +2,7 @@ if USB_GADGET
config USB_G_CCG
tristate "Configurable Composite Gadget (STAGING)"
- depends on STAGING && BLOCK && !USB_ZERO && !USB_ZERO_HNPTEST && !USB_AUDIO && !GADGET_UAC1 && !USB_ETH && !USB_ETH_RNDIS && !USB_ETH_EEM && !USB_G_NCM && !USB_GADGETFS && !USB_FUNCTIONFS && !USB_FUNCTIONFS_ETH && !USB_FUNCTIONFS_RNDIS && !USB_FUNCTIONFS_GENERIC && !USB_FILE_STORAGE && !USB_FILE_STORAGE_TEST && !USB_MASS_STORAGE && !USB_G_SERIAL && !USB_MIDI_GADGET && !USB_G_PRINTER && !USB_CDC_COMPOSITE && !USB_G_NOKIA && !USB_G_ACM_MS && !USB_G_MULTI && !USB_G_MULTI_RNDIS && !USB_G_MULTI_CDC && !USB_G_HID && !USB_G_DBGP && !USB_G_WEBCAM
+ depends on STAGING && BLOCK && NET && !USB_ZERO && !USB_ZERO_HNPTEST && !USB_AUDIO && !GADGET_UAC1 && !USB_ETH && !USB_ETH_RNDIS && !USB_ETH_EEM && !USB_G_NCM && !USB_GADGETFS && !USB_FUNCTIONFS && !USB_FUNCTIONFS_ETH && !USB_FUNCTIONFS_RNDIS && !USB_FUNCTIONFS_GENERIC && !USB_FILE_STORAGE && !USB_FILE_STORAGE_TEST && !USB_MASS_STORAGE && !USB_G_SERIAL && !USB_MIDI_GADGET && !USB_G_PRINTER && !USB_CDC_COMPOSITE && !USB_G_NOKIA && !USB_G_ACM_MS && !USB_G_MULTI && !USB_G_MULTI_RNDIS && !USB_G_MULTI_CDC && !USB_G_HID && !USB_G_DBGP && !USB_G_WEBCAM
help
The Configurable Composite Gadget supports multiple USB
functions: acm, mass storage, rndis and FunctionFS.
@@ -17,4 +17,9 @@ config USB_G_CCG
Configurable Composite Gadget can be compiled "M" only
or not at all.
+ BIG FAT NOTE: DON'T RELY ON THIS USERINTERFACE HERE! AS PART
+ OF THE REWORK DONE HERE WILL BE A NEW USER INTERFACE WITHOUT ANY
+ COMPATIBILITY TO THIS SYSFS INTERFACE HERE. BE AWARE OF THIS
+ BEFORE SELECTING THIS.
+
endif # USB_GADGET
diff --git a/drivers/staging/ccg/Makefile b/drivers/staging/ccg/Makefile
index 693da637a6d8..814fa9de5f57 100644
--- a/drivers/staging/ccg/Makefile
+++ b/drivers/staging/ccg/Makefile
@@ -1,4 +1,2 @@
g_ccg-y := ccg.o
-ccflags-y += -Idrivers/usb/gadget
-
obj-$(CONFIG_USB_G_CCG) += g_ccg.o
diff --git a/drivers/staging/ccg/ccg.c b/drivers/staging/ccg/ccg.c
index 6a7aab8d9bf5..93e1e2ffca0c 100644
--- a/drivers/staging/ccg/ccg.c
+++ b/drivers/staging/ccg/ccg.c
@@ -32,7 +32,7 @@
#include <linux/platform_device.h>
#include <linux/usb/ch9.h>
-#include <linux/usb/composite.h>
+#include "composite.h"
#include <linux/usb/gadget.h>
#include "gadget_chips.h"
@@ -44,19 +44,19 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "../../usb/gadget/usbstring.c"
-#include "../../usb/gadget/config.c"
-#include "../../usb/gadget/epautoconf.c"
-#include "../../usb/gadget/composite.c"
-
-#include "../../usb/gadget/f_mass_storage.c"
-#include "../../usb/gadget/u_serial.c"
-#include "../../usb/gadget/f_acm.c"
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+#include "composite.c"
+
+#include "f_mass_storage.c"
+#include "u_serial.c"
+#include "f_acm.c"
#define USB_ETH_RNDIS y
-#include "../../usb/gadget/f_rndis.c"
-#include "../../usb/gadget/rndis.c"
-#include "../../usb/gadget/u_ether.c"
-#include "../../usb/gadget/f_fs.c"
+#include "f_rndis.c"
+#include "rndis.c"
+#include "u_ether.c"
+#include "f_fs.c"
MODULE_AUTHOR("Mike Lockwood, Andrzej Pietrasiewicz");
MODULE_DESCRIPTION("Configurable Composite USB Gadget");
@@ -728,7 +728,7 @@ static int mass_storage_function_init(struct ccg_usb_function *f,
struct fsg_common *common;
int err;
- memset(&fsg, 0, sizeof fsg);
+ memset(&fsg, 0, sizeof(fsg));
fsg.nluns = 1;
fsg.luns[0].removable = 1;
fsg.vendor_name = iManufacturer;
@@ -1101,13 +1101,7 @@ static struct device_attribute *ccg_usb_attributes[] = {
static int ccg_bind_config(struct usb_configuration *c)
{
struct ccg_dev *dev = _ccg_dev;
- int ret = 0;
-
- ret = ccg_bind_enabled_functions(dev, c);
- if (ret)
- return ret;
-
- return 0;
+ return ccg_bind_enabled_functions(dev, c);
}
static void ccg_unbind_config(struct usb_configuration *c)
@@ -1162,6 +1156,7 @@ static int ccg_usb_unbind(struct usb_composite_dev *cdev)
static struct usb_composite_driver ccg_usb_driver = {
.name = "configurable_usb",
.dev = &device_desc,
+ .bind = ccg_bind,
.unbind = ccg_usb_unbind,
.needs_serial = true,
.iManufacturer = "Linux Foundation",
@@ -1254,8 +1249,10 @@ static int __init init(void)
return PTR_ERR(ccg_class);
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (!dev)
+ if (!dev) {
+ class_destroy(ccg_class);
return -ENOMEM;
+ }
dev->functions = supported_functions;
INIT_LIST_HEAD(&dev->enabled_functions);
@@ -1275,7 +1272,7 @@ static int __init init(void)
composite_driver.setup = ccg_setup;
composite_driver.disconnect = ccg_disconnect;
- err = usb_composite_probe(&ccg_usb_driver, ccg_bind);
+ err = usb_composite_probe(&ccg_usb_driver);
if (err) {
class_destroy(ccg_class);
kfree(dev);
diff --git a/drivers/staging/ccg/composite.c b/drivers/staging/ccg/composite.c
new file mode 100644
index 000000000000..228b4574f228
--- /dev/null
+++ b/drivers/staging/ccg/composite.c
@@ -0,0 +1,1688 @@
+/*
+ * composite.c - infrastructure for Composite USB Gadgets
+ *
+ * Copyright (C) 2006-2008 David Brownell
+ *
+ * This program is free software; 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 VERBOSE_DEBUG */
+
+#include <linux/kallsyms.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/utsname.h>
+
+#include <linux/usb/composite.h>
+#include <asm/unaligned.h>
+
+/*
+ * The code in this file is utility code, used to build a gadget driver
+ * from one or more "function" drivers, one or more "configuration"
+ * objects, and a "usb_composite_driver" by gluing them together along
+ * with the relevant device-wide data.
+ */
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ 1024
+
+static struct usb_composite_driver *composite;
+
+/* Some systems will need runtime overrides for the product identifiers
+ * published in the device descriptor, either numbers or strings or both.
+ * String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+static ushort idVendor;
+module_param(idVendor, ushort, 0644);
+MODULE_PARM_DESC(idVendor, "USB Vendor ID");
+
+static ushort idProduct;
+module_param(idProduct, ushort, 0644);
+MODULE_PARM_DESC(idProduct, "USB Product ID");
+
+static ushort bcdDevice;
+module_param(bcdDevice, ushort, 0644);
+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
+
+static char *iManufacturer;
+module_param(iManufacturer, charp, 0644);
+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
+
+static char *iProduct;
+module_param(iProduct, charp, 0644);
+MODULE_PARM_DESC(iProduct, "USB Product string");
+
+static char *iSerialNumber;
+module_param(iSerialNumber, charp, 0644);
+MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
+
+static char composite_manufacturer[50];
+
+/*-------------------------------------------------------------------------*/
+/**
+ * next_ep_desc() - advance to the next EP descriptor
+ * @t: currect pointer within descriptor array
+ *
+ * Return: next EP descriptor or NULL
+ *
+ * Iterate over @t until either EP descriptor found or
+ * NULL (that indicates end of list) encountered
+ */
+static struct usb_descriptor_header**
+next_ep_desc(struct usb_descriptor_header **t)
+{
+ for (; *t; t++) {
+ if ((*t)->bDescriptorType == USB_DT_ENDPOINT)
+ return t;
+ }
+ return NULL;
+}
+
+/*
+ * for_each_ep_desc()- iterate over endpoint descriptors in the
+ * descriptors list
+ * @start: pointer within descriptor array.
+ * @ep_desc: endpoint descriptor to use as the loop cursor
+ */
+#define for_each_ep_desc(start, ep_desc) \
+ for (ep_desc = next_ep_desc(start); \
+ ep_desc; ep_desc = next_ep_desc(ep_desc+1))
+
+/**
+ * config_ep_by_speed() - configures the given endpoint
+ * according to gadget speed.
+ * @g: pointer to the gadget
+ * @f: usb function
+ * @_ep: the endpoint to configure
+ *
+ * Return: error code, 0 on success
+ *
+ * This function chooses the right descriptors for a given
+ * endpoint according to gadget speed and saves it in the
+ * endpoint desc field. If the endpoint already has a descriptor
+ * assigned to it - overwrites it with currently corresponding
+ * descriptor. The endpoint maxpacket field is updated according
+ * to the chosen descriptor.
+ * Note: the supplied function should hold all the descriptors
+ * for supported speeds
+ */
+int config_ep_by_speed(struct usb_gadget *g,
+ struct usb_function *f,
+ struct usb_ep *_ep)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(g);
+ struct usb_endpoint_descriptor *chosen_desc = NULL;
+ struct usb_descriptor_header **speed_desc = NULL;
+
+ struct usb_ss_ep_comp_descriptor *comp_desc = NULL;
+ int want_comp_desc = 0;
+
+ struct usb_descriptor_header **d_spd; /* cursor for speed desc */
+
+ if (!g || !f || !_ep)
+ return -EIO;
+
+ /* select desired speed */
+ switch (g->speed) {
+ case USB_SPEED_SUPER:
+ if (gadget_is_superspeed(g)) {
+ speed_desc = f->ss_descriptors;
+ want_comp_desc = 1;
+ break;
+ }
+ /* else: Fall trough */
+ case USB_SPEED_HIGH:
+ if (gadget_is_dualspeed(g)) {
+ speed_desc = f->hs_descriptors;
+ break;
+ }
+ /* else: fall through */
+ default:
+ speed_desc = f->descriptors;
+ }
+ /* find descriptors */
+ for_each_ep_desc(speed_desc, d_spd) {
+ chosen_desc = (struct usb_endpoint_descriptor *)*d_spd;
+ if (chosen_desc->bEndpointAddress == _ep->address)
+ goto ep_found;
+ }
+ return -EIO;
+
+ep_found:
+ /* commit results */
+ _ep->maxpacket = usb_endpoint_maxp(chosen_desc);
+ _ep->desc = chosen_desc;
+ _ep->comp_desc = NULL;
+ _ep->maxburst = 0;
+ _ep->mult = 0;
+ if (!want_comp_desc)
+ return 0;
+
+ /*
+ * Companion descriptor should follow EP descriptor
+ * USB 3.0 spec, #9.6.7
+ */
+ comp_desc = (struct usb_ss_ep_comp_descriptor *)*(++d_spd);
+ if (!comp_desc ||
+ (comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP))
+ return -EIO;
+ _ep->comp_desc = comp_desc;
+ if (g->speed == USB_SPEED_SUPER) {
+ switch (usb_endpoint_type(_ep->desc)) {
+ case USB_ENDPOINT_XFER_ISOC:
+ /* mult: bits 1:0 of bmAttributes */
+ _ep->mult = comp_desc->bmAttributes & 0x3;
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ _ep->maxburst = comp_desc->bMaxBurst + 1;
+ break;
+ default:
+ if (comp_desc->bMaxBurst != 0)
+ ERROR(cdev, "ep0 bMaxBurst must be 0\n");
+ _ep->maxburst = 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * usb_add_function() - add a function to a configuration
+ * @config: the configuration
+ * @function: the function being added
+ * Context: single threaded during gadget setup
+ *
+ * After initialization, each configuration must have one or more
+ * functions added to it. Adding a function involves calling its @bind()
+ * method to allocate resources such as interface and string identifiers
+ * and endpoints.
+ *
+ * This function returns the value of the function's bind(), which is
+ * zero for success else a negative errno value.
+ */
+int usb_add_function(struct usb_configuration *config,
+ struct usb_function *function)
+{
+ int value = -EINVAL;
+
+ DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
+ function->name, function,
+ config->label, config);
+
+ if (!function->set_alt || !function->disable)
+ goto done;
+
+ function->config = config;
+ list_add_tail(&function->list, &config->functions);
+
+ /* REVISIT *require* function->bind? */
+ if (function->bind) {
+ value = function->bind(config, function);
+ if (value < 0) {
+ list_del(&function->list);
+ function->config = NULL;
+ }
+ } else
+ value = 0;
+
+ /* We allow configurations that don't work at both speeds.
+ * If we run into a lowspeed Linux system, treat it the same
+ * as full speed ... it's the function drivers that will need
+ * to avoid bulk and ISO transfers.
+ */
+ if (!config->fullspeed && function->descriptors)
+ config->fullspeed = true;
+ if (!config->highspeed && function->hs_descriptors)
+ config->highspeed = true;
+ if (!config->superspeed && function->ss_descriptors)
+ config->superspeed = true;
+
+done:
+ if (value)
+ DBG(config->cdev, "adding '%s'/%p --> %d\n",
+ function->name, function, value);
+ return value;
+}
+
+/**
+ * usb_function_deactivate - prevent function and gadget enumeration
+ * @function: the function that isn't yet ready to respond
+ *
+ * Blocks response of the gadget driver to host enumeration by
+ * preventing the data line pullup from being activated. This is
+ * normally called during @bind() processing to change from the
+ * initial "ready to respond" state, or when a required resource
+ * becomes available.
+ *
+ * For example, drivers that serve as a passthrough to a userspace
+ * daemon can block enumeration unless that daemon (such as an OBEX,
+ * MTP, or print server) is ready to handle host requests.
+ *
+ * Not all systems support software control of their USB peripheral
+ * data pullups.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_deactivate(struct usb_function *function)
+{
+ struct usb_composite_dev *cdev = function->config->cdev;
+ unsigned long flags;
+ int status = 0;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+
+ if (cdev->deactivations == 0)
+ status = usb_gadget_disconnect(cdev->gadget);
+ if (status == 0)
+ cdev->deactivations++;
+
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ return status;
+}
+
+/**
+ * usb_function_activate - allow function and gadget enumeration
+ * @function: function on which usb_function_activate() was called
+ *
+ * Reverses effect of usb_function_deactivate(). If no more functions
+ * are delaying their activation, the gadget driver will respond to
+ * host enumeration procedures.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_activate(struct usb_function *function)
+{
+ struct usb_composite_dev *cdev = function->config->cdev;
+ unsigned long flags;
+ int status = 0;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+
+ if (WARN_ON(cdev->deactivations == 0))
+ status = -EINVAL;
+ else {
+ cdev->deactivations--;
+ if (cdev->deactivations == 0)
+ status = usb_gadget_connect(cdev->gadget);
+ }
+
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ return status;
+}
+
+/**
+ * usb_interface_id() - allocate an unused interface ID
+ * @config: configuration associated with the interface
+ * @function: function handling the interface
+ * Context: single threaded during gadget setup
+ *
+ * usb_interface_id() is called from usb_function.bind() callbacks to
+ * allocate new interface IDs. The function driver will then store that
+ * ID in interface, association, CDC union, and other descriptors. It
+ * will also handle any control requests targeted at that interface,
+ * particularly changing its altsetting via set_alt(). There may
+ * also be class-specific or vendor-specific requests to handle.
+ *
+ * All interface identifier should be allocated using this routine, to
+ * ensure that for example different functions don't wrongly assign
+ * different meanings to the same identifier. Note that since interface
+ * identifiers are configuration-specific, functions used in more than
+ * one configuration (or more than once in a given configuration) need
+ * multiple versions of the relevant descriptors.
+ *
+ * Returns the interface ID which was allocated; or -ENODEV if no
+ * more interface IDs can be allocated.
+ */
+int usb_interface_id(struct usb_configuration *config,
+ struct usb_function *function)
+{
+ unsigned id = config->next_interface_id;
+
+ if (id < MAX_CONFIG_INTERFACES) {
+ config->interface[id] = function;
+ config->next_interface_id = id + 1;
+ return id;
+ }
+ return -ENODEV;
+}
+
+static int config_buf(struct usb_configuration *config,
+ enum usb_device_speed speed, void *buf, u8 type)
+{
+ struct usb_config_descriptor *c = buf;
+ void *next = buf + USB_DT_CONFIG_SIZE;
+ int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
+ struct usb_function *f;
+ int status;
+
+ /* write the config descriptor */
+ c = buf;
+ c->bLength = USB_DT_CONFIG_SIZE;
+ c->bDescriptorType = type;
+ /* wTotalLength is written later */
+ c->bNumInterfaces = config->next_interface_id;
+ c->bConfigurationValue = config->bConfigurationValue;
+ c->iConfiguration = config->iConfiguration;
+ c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
+ c->bMaxPower = config->bMaxPower ? : (CONFIG_USB_GADGET_VBUS_DRAW / 2);
+
+ /* There may be e.g. OTG descriptors */
+ if (config->descriptors) {
+ status = usb_descriptor_fillbuf(next, len,
+ config->descriptors);
+ if (status < 0)
+ return status;
+ len -= status;
+ next += status;
+ }
+
+ /* add each function's descriptors */
+ list_for_each_entry(f, &config->functions, list) {
+ struct usb_descriptor_header **descriptors;
+
+ switch (speed) {
+ case USB_SPEED_SUPER:
+ descriptors = f->ss_descriptors;
+ break;
+ case USB_SPEED_HIGH:
+ descriptors = f->hs_descriptors;
+ break;
+ default:
+ descriptors = f->descriptors;
+ }
+
+ if (!descriptors)
+ continue;
+ status = usb_descriptor_fillbuf(next, len,
+ (const struct usb_descriptor_header **) descriptors);
+ if (status < 0)
+ return status;
+ len -= status;
+ next += status;
+ }
+
+ len = next - buf;
+ c->wTotalLength = cpu_to_le16(len);
+ return len;
+}
+
+static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct usb_configuration *c;
+ u8 type = w_value >> 8;
+ enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+
+ if (gadget->speed == USB_SPEED_SUPER)
+ speed = gadget->speed;
+ else if (gadget_is_dualspeed(gadget)) {
+ int hs = 0;
+ if (gadget->speed == USB_SPEED_HIGH)
+ hs = 1;
+ if (type == USB_DT_OTHER_SPEED_CONFIG)
+ hs = !hs;
+ if (hs)
+ speed = USB_SPEED_HIGH;
+
+ }
+
+ /* This is a lookup by config *INDEX* */
+ w_value &= 0xff;
+ list_for_each_entry(c, &cdev->configs, list) {
+ /* ignore configs that won't work at this speed */
+ switch (speed) {
+ case USB_SPEED_SUPER:
+ if (!c->superspeed)
+ continue;
+ break;
+ case USB_SPEED_HIGH:
+ if (!c->highspeed)
+ continue;
+ break;
+ default:
+ if (!c->fullspeed)
+ continue;
+ }
+
+ if (w_value == 0)
+ return config_buf(c, speed, cdev->req->buf, type);
+ w_value--;
+ }
+ return -EINVAL;
+}
+
+static int count_configs(struct usb_composite_dev *cdev, unsigned type)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct usb_configuration *c;
+ unsigned count = 0;
+ int hs = 0;
+ int ss = 0;
+
+ if (gadget_is_dualspeed(gadget)) {
+ if (gadget->speed == USB_SPEED_HIGH)
+ hs = 1;
+ if (gadget->speed == USB_SPEED_SUPER)
+ ss = 1;
+ if (type == USB_DT_DEVICE_QUALIFIER)
+ hs = !hs;
+ }
+ list_for_each_entry(c, &cdev->configs, list) {
+ /* ignore configs that won't work at this speed */
+ if (ss) {
+ if (!c->superspeed)
+ continue;
+ } else if (hs) {
+ if (!c->highspeed)
+ continue;
+ } else {
+ if (!c->fullspeed)
+ continue;
+ }
+ count++;
+ }
+ return count;
+}
+
+/**
+ * bos_desc() - prepares the BOS descriptor.
+ * @cdev: pointer to usb_composite device to generate the bos
+ * descriptor for
+ *
+ * This function generates the BOS (Binary Device Object)
+ * descriptor and its device capabilities descriptors. The BOS
+ * descriptor should be supported by a SuperSpeed device.
+ */
+static int bos_desc(struct usb_composite_dev *cdev)
+{
+ struct usb_ext_cap_descriptor *usb_ext;
+ struct usb_ss_cap_descriptor *ss_cap;
+ struct usb_dcd_config_params dcd_config_params;
+ struct usb_bos_descriptor *bos = cdev->req->buf;
+
+ bos->bLength = USB_DT_BOS_SIZE;
+ bos->bDescriptorType = USB_DT_BOS;
+
+ bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE);
+ bos->bNumDeviceCaps = 0;
+
+ /*
+ * A SuperSpeed device shall include the USB2.0 extension descriptor
+ * and shall support LPM when operating in USB2.0 HS mode.
+ */
+ usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE);
+ usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
+ usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
+ usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT);
+
+ /*
+ * The Superspeed USB Capability descriptor shall be implemented by all
+ * SuperSpeed devices.
+ */
+ ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
+ ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
+ ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
+ ss_cap->bmAttributes = 0; /* LTM is not supported yet */
+ ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
+ USB_FULL_SPEED_OPERATION |
+ USB_HIGH_SPEED_OPERATION |
+ USB_5GBPS_OPERATION);
+ ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
+
+ /* Get Controller configuration */
+ if (cdev->gadget->ops->get_config_params)
+ cdev->gadget->ops->get_config_params(&dcd_config_params);
+ else {
+ dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
+ dcd_config_params.bU2DevExitLat =
+ cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ }
+ ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+ ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
+
+ return le16_to_cpu(bos->wTotalLength);
+}
+
+static void device_qual(struct usb_composite_dev *cdev)
+{
+ struct usb_qualifier_descriptor *qual = cdev->req->buf;
+
+ qual->bLength = sizeof(*qual);
+ qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+ /* POLICY: same bcdUSB and device type info at both speeds */
+ qual->bcdUSB = cdev->desc.bcdUSB;
+ qual->bDeviceClass = cdev->desc.bDeviceClass;
+ qual->bDeviceSubClass = cdev->desc.bDeviceSubClass;
+ qual->bDeviceProtocol = cdev->desc.bDeviceProtocol;
+ /* ASSUME same EP0 fifo size at both speeds */
+ qual->bMaxPacketSize0 = cdev->gadget->ep0->maxpacket;
+ qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER);
+ qual->bRESERVED = 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void reset_config(struct usb_composite_dev *cdev)
+{
+ struct usb_function *f;
+
+ DBG(cdev, "reset config\n");
+
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->disable)
+ f->disable(f);
+
+ bitmap_zero(f->endpoints, 32);
+ }
+ cdev->config = NULL;
+}
+
+static int set_config(struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *ctrl, unsigned number)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct usb_configuration *c = NULL;
+ int result = -EINVAL;
+ unsigned power = gadget_is_otg(gadget) ? 8 : 100;
+ int tmp;
+
+ if (number) {
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->bConfigurationValue == number) {
+ /*
+ * We disable the FDs of the previous
+ * configuration only if the new configuration
+ * is a valid one
+ */
+ if (cdev->config)
+ reset_config(cdev);
+ result = 0;
+ break;
+ }
+ }
+ if (result < 0)
+ goto done;
+ } else { /* Zero configuration value - need to reset the config */
+ if (cdev->config)
+ reset_config(cdev);
+ result = 0;
+ }
+
+ INFO(cdev, "%s config #%d: %s\n",
+ usb_speed_string(gadget->speed),
+ number, c ? c->label : "unconfigured");
+
+ if (!c)
+ goto done;
+
+ cdev->config = c;
+
+ /* Initialize all interfaces by setting them to altsetting zero. */
+ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
+ struct usb_function *f = c->interface[tmp];
+ struct usb_descriptor_header **descriptors;
+
+ if (!f)
+ break;
+
+ /*
+ * Record which endpoints are used by the function. This is used
+ * to dispatch control requests targeted at that endpoint to the
+ * function's setup callback instead of the current
+ * configuration's setup callback.
+ */
+ switch (gadget->speed) {
+ case USB_SPEED_SUPER:
+ descriptors = f->ss_descriptors;
+ break;
+ case USB_SPEED_HIGH:
+ descriptors = f->hs_descriptors;
+ break;
+ default:
+ descriptors = f->descriptors;
+ }
+
+ for (; *descriptors; ++descriptors) {
+ struct usb_endpoint_descriptor *ep;
+ int addr;
+
+ if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
+ continue;
+
+ ep = (struct usb_endpoint_descriptor *)*descriptors;
+ addr = ((ep->bEndpointAddress & 0x80) >> 3)
+ | (ep->bEndpointAddress & 0x0f);
+ set_bit(addr, f->endpoints);
+ }
+
+ result = f->set_alt(f, tmp, 0);
+ if (result < 0) {
+ DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
+ tmp, f->name, f, result);
+
+ reset_config(cdev);
+ goto done;
+ }
+
+ if (result == USB_GADGET_DELAYED_STATUS) {
+ DBG(cdev,
+ "%s: interface %d (%s) requested delayed status\n",
+ __func__, tmp, f->name);
+ cdev->delayed_status++;
+ DBG(cdev, "delayed_status count %d\n",
+ cdev->delayed_status);
+ }
+ }
+
+ /* when we return, be sure our power usage is valid */
+ power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
+done:
+ usb_gadget_vbus_draw(gadget, power);
+ if (result >= 0 && cdev->delayed_status)
+ result = USB_GADGET_DELAYED_STATUS;
+ return result;
+}
+
+/**
+ * usb_add_config() - add a configuration to a device.
+ * @cdev: wraps the USB gadget
+ * @config: the configuration, with bConfigurationValue assigned
+ * @bind: the configuration's bind function
+ * Context: single threaded during gadget setup
+ *
+ * One of the main tasks of a composite @bind() routine is to
+ * add each of the configurations it supports, using this routine.
+ *
+ * This function returns the value of the configuration's @bind(), which
+ * is zero for success else a negative errno value. Binding configurations
+ * assigns global resources including string IDs, and per-configuration
+ * resources such as interface IDs and endpoints.
+ */
+int usb_add_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *config,
+ int (*bind)(struct usb_configuration *))
+{
+ int status = -EINVAL;
+ struct usb_configuration *c;
+
+ DBG(cdev, "adding config #%u '%s'/%p\n",
+ config->bConfigurationValue,
+ config->label, config);
+
+ if (!config->bConfigurationValue || !bind)
+ goto done;
+
+ /* Prevent duplicate configuration identifiers */
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->bConfigurationValue == config->bConfigurationValue) {
+ status = -EBUSY;
+ goto done;
+ }
+ }
+
+ config->cdev = cdev;
+ list_add_tail(&config->list, &cdev->configs);
+
+ INIT_LIST_HEAD(&config->functions);
+ config->next_interface_id = 0;
+ memset(config->interface, 0, sizeof(config->interface));
+
+ status = bind(config);
+ if (status < 0) {
+ while (!list_empty(&config->functions)) {
+ struct usb_function *f;
+
+ f = list_first_entry(&config->functions,
+ struct usb_function, list);
+ list_del(&f->list);
+ if (f->unbind) {
+ DBG(cdev, "unbind function '%s'/%p\n",
+ f->name, f);
+ f->unbind(config, f);
+ /* may free memory for "f" */
+ }
+ }
+ list_del(&config->list);
+ config->cdev = NULL;
+ } else {
+ unsigned i;
+
+ DBG(cdev, "cfg %d/%p speeds:%s%s%s\n",
+ config->bConfigurationValue, config,
+ config->superspeed ? " super" : "",
+ config->highspeed ? " high" : "",
+ config->fullspeed
+ ? (gadget_is_dualspeed(cdev->gadget)
+ ? " full"
+ : " full/low")
+ : "");
+
+ for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
+ struct usb_function *f = config->interface[i];
+
+ if (!f)
+ continue;
+ DBG(cdev, " interface %d = %s/%p\n",
+ i, f->name, f);
+ }
+ }
+
+ /* set_alt(), or next bind(), sets up
+ * ep->driver_data as needed.
+ */
+ usb_ep_autoconfig_reset(cdev->gadget);
+
+done:
+ if (status)
+ DBG(cdev, "added config '%s'/%u --> %d\n", config->label,
+ config->bConfigurationValue, status);
+ return status;
+}
+
+static void remove_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *config)
+{
+ while (!list_empty(&config->functions)) {
+ struct usb_function *f;
+
+ f = list_first_entry(&config->functions,
+ struct usb_function, list);
+ list_del(&f->list);
+ if (f->unbind) {
+ DBG(cdev, "unbind function '%s'/%p\n", f->name, f);
+ f->unbind(config, f);
+ /* may free memory for "f" */
+ }
+ }
+ list_del(&config->list);
+ if (config->unbind) {
+ DBG(cdev, "unbind config '%s'/%p\n", config->label, config);
+ config->unbind(config);
+ /* may free memory for "c" */
+ }
+}
+
+/**
+ * usb_remove_config() - remove a configuration from a device.
+ * @cdev: wraps the USB gadget
+ * @config: the configuration
+ *
+ * Drivers must call usb_gadget_disconnect before calling this function
+ * to disconnect the device from the host and make sure the host will not
+ * try to enumerate the device while we are changing the config list.
+ */
+void usb_remove_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *config)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+
+ if (cdev->config == config)
+ reset_config(cdev);
+
+ spin_unlock_irqrestore(&cdev->lock, flags);
+
+ remove_config(cdev, config);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* We support strings in multiple languages ... string descriptor zero
+ * says which languages are supported. The typical case will be that
+ * only one language (probably English) is used, with I18N handled on
+ * the host side.
+ */
+
+static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
+{
+ const struct usb_gadget_strings *s;
+ __le16 language;
+ __le16 *tmp;
+
+ while (*sp) {
+ s = *sp;
+ language = cpu_to_le16(s->language);
+ for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
+ if (*tmp == language)
+ goto repeat;
+ }
+ *tmp++ = language;
+repeat:
+ sp++;
+ }
+}
+
+static int lookup_string(
+ struct usb_gadget_strings **sp,
+ void *buf,
+ u16 language,
+ int id
+)
+{
+ struct usb_gadget_strings *s;
+ int value;
+
+ while (*sp) {
+ s = *sp++;
+ if (s->language != language)
+ continue;
+ value = usb_gadget_get_string(s, id, buf);
+ if (value > 0)
+ return value;
+ }
+ return -EINVAL;
+}
+
+static int get_string(struct usb_composite_dev *cdev,
+ void *buf, u16 language, int id)
+{
+ struct usb_configuration *c;
+ struct usb_function *f;
+ int len;
+ const char *str;
+
+ /* Yes, not only is USB's I18N support probably more than most
+ * folk will ever care about ... also, it's all supported here.
+ * (Except for UTF8 support for Unicode's "Astral Planes".)
+ */
+
+ /* 0 == report all available language codes */
+ if (id == 0) {
+ struct usb_string_descriptor *s = buf;
+ struct usb_gadget_strings **sp;
+
+ memset(s, 0, 256);
+ s->bDescriptorType = USB_DT_STRING;
+
+ sp = composite->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+
+ list_for_each_entry(c, &cdev->configs, list) {
+ sp = c->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+
+ list_for_each_entry(f, &c->functions, list) {
+ sp = f->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+ }
+ }
+
+ for (len = 0; len <= 126 && s->wData[len]; len++)
+ continue;
+ if (!len)
+ return -EINVAL;
+
+ s->bLength = 2 * (len + 1);
+ return s->bLength;
+ }
+
+ /* Otherwise, look up and return a specified string. First
+ * check if the string has not been overridden.
+ */
+ if (cdev->manufacturer_override == id)
+ str = iManufacturer ?: composite->iManufacturer ?:
+ composite_manufacturer;
+ else if (cdev->product_override == id)
+ str = iProduct ?: composite->iProduct;
+ else if (cdev->serial_override == id)
+ str = iSerialNumber ?: composite->iSerialNumber;
+ else
+ str = NULL;
+ if (str) {
+ struct usb_gadget_strings strings = {
+ .language = language,
+ .strings = &(struct usb_string) { 0xff, str }
+ };
+ return usb_gadget_get_string(&strings, 0xff, buf);
+ }
+
+ /* String IDs are device-scoped, so we look up each string
+ * table we're told about. These lookups are infrequent;
+ * simpler-is-better here.
+ */
+ if (composite->strings) {
+ len = lookup_string(composite->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->strings) {
+ len = lookup_string(c->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ list_for_each_entry(f, &c->functions, list) {
+ if (!f->strings)
+ continue;
+ len = lookup_string(f->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ }
+ return -EINVAL;
+}
+
+/**
+ * usb_string_id() - allocate an unused string ID
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_id() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure
+ * that for example different functions don't wrongly assign different
+ * meanings to the same identifier.
+ */
+int usb_string_id(struct usb_composite_dev *cdev)
+{
+ if (cdev->next_string_id < 254) {
+ /* string id 0 is reserved by USB spec for list of
+ * supported languages */
+ /* 255 reserved as well? -- mina86 */
+ cdev->next_string_id++;
+ return cdev->next_string_id;
+ }
+ return -ENODEV;
+}
+
+/**
+ * usb_string_ids() - allocate unused string IDs in batch
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * @str: an array of usb_string objects to assign numbers to
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_ids() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then copy IDs from the string table to the appropriate descriptors
+ * and string table for other languages.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
+{
+ int next = cdev->next_string_id;
+
+ for (; str->s; ++str) {
+ if (unlikely(next >= 254))
+ return -ENODEV;
+ str->id = ++next;
+ }
+
+ cdev->next_string_id = next;
+
+ return 0;
+}
+
+/**
+ * usb_string_ids_n() - allocate unused string IDs in batch
+ * @c: the device whose string descriptor IDs are being allocated
+ * @n: number of string IDs to allocate
+ * Context: single threaded during gadget setup
+ *
+ * Returns the first requested ID. This ID and next @n-1 IDs are now
+ * valid IDs. At least provided that @n is non-zero because if it
+ * is, returns last requested ID which is now very useful information.
+ *
+ * @usb_string_ids_n() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)
+{
+ unsigned next = c->next_string_id;
+ if (unlikely(n > 254 || (unsigned)next + n > 254))
+ return -ENODEV;
+ c->next_string_id += n;
+ return next + 1;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->status || req->actual != req->length)
+ DBG((struct usb_composite_dev *) ep->driver_data,
+ "setup complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver(like
+ * device and endpoint feature flags, and their status). It's all
+ * housekeeping for the gadget function we're implementing. Most of
+ * the work is in config and function specific setup.
+ */
+static int
+composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ int status = 0;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u8 intf = w_index & 0xFF;
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+ struct usb_function *f = NULL;
+ u8 endp;
+
+ /* partial re-init of the response message; the function or the
+ * gadget might need to intercept e.g. a control-OUT completion
+ * when we delegate to it.
+ */
+ req->zero = 0;
+ req->complete = composite_setup_complete;
+ req->length = 0;
+ gadget->ep0->driver_data = cdev;
+
+ switch (ctrl->bRequest) {
+
+ /* we handle all standard USB descriptors */
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ goto unknown;
+ switch (w_value >> 8) {
+
+ case USB_DT_DEVICE:
+ cdev->desc.bNumConfigurations =
+ count_configs(cdev, USB_DT_DEVICE);
+ cdev->desc.bMaxPacketSize0 =
+ cdev->gadget->ep0->maxpacket;
+ if (gadget_is_superspeed(gadget)) {
+ if (gadget->speed >= USB_SPEED_SUPER) {
+ cdev->desc.bcdUSB = cpu_to_le16(0x0300);
+ cdev->desc.bMaxPacketSize0 = 9;
+ } else {
+ cdev->desc.bcdUSB = cpu_to_le16(0x0210);
+ }
+ }
+
+ value = min(w_length, (u16) sizeof cdev->desc);
+ memcpy(req->buf, &cdev->desc, value);
+ break;
+ case USB_DT_DEVICE_QUALIFIER:
+ if (!gadget_is_dualspeed(gadget) ||
+ gadget->speed >= USB_SPEED_SUPER)
+ break;
+ device_qual(cdev);
+ value = min_t(int, w_length,
+ sizeof(struct usb_qualifier_descriptor));
+ break;
+ case USB_DT_OTHER_SPEED_CONFIG:
+ if (!gadget_is_dualspeed(gadget) ||
+ gadget->speed >= USB_SPEED_SUPER)
+ break;
+ /* FALLTHROUGH */
+ case USB_DT_CONFIG:
+ value = config_desc(cdev, w_value);
+ if (value >= 0)
+ value = min(w_length, (u16) value);
+ break;
+ case USB_DT_STRING:
+ value = get_string(cdev, req->buf,
+ w_index, w_value & 0xff);
+ if (value >= 0)
+ value = min(w_length, (u16) value);
+ break;
+ case USB_DT_BOS:
+ if (gadget_is_superspeed(gadget)) {
+ value = bos_desc(cdev);
+ value = min(w_length, (u16) value);
+ }
+ break;
+ }
+ break;
+
+ /* any number of configs can work */
+ case USB_REQ_SET_CONFIGURATION:
+ if (ctrl->bRequestType != 0)
+ goto unknown;
+ if (gadget_is_otg(gadget)) {
+ if (gadget->a_hnp_support)
+ DBG(cdev, "HNP available\n");
+ else if (gadget->a_alt_hnp_support)
+ DBG(cdev, "HNP on another port\n");
+ else
+ VDBG(cdev, "HNP inactive\n");
+ }
+ spin_lock(&cdev->lock);
+ value = set_config(cdev, ctrl, w_value);
+ spin_unlock(&cdev->lock);
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ goto unknown;
+ if (cdev->config)
+ *(u8 *)req->buf = cdev->config->bConfigurationValue;
+ else
+ *(u8 *)req->buf = 0;
+ value = min(w_length, (u16) 1);
+ break;
+
+ /* function drivers must handle get/set altsetting; if there's
+ * no get() method, we know only altsetting zero works.
+ */
+ case USB_REQ_SET_INTERFACE:
+ if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+ goto unknown;
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ if (w_value && !f->set_alt)
+ break;
+ value = f->set_alt(f, w_index, w_value);
+ if (value == USB_GADGET_DELAYED_STATUS) {
+ DBG(cdev,
+ "%s: interface %d (%s) requested delayed status\n",
+ __func__, intf, f->name);
+ cdev->delayed_status++;
+ DBG(cdev, "delayed_status count %d\n",
+ cdev->delayed_status);
+ }
+ break;
+ case USB_REQ_GET_INTERFACE:
+ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
+ goto unknown;
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ /* lots of interfaces only need altsetting zero... */
+ value = f->get_alt ? f->get_alt(f, w_index) : 0;
+ if (value < 0)
+ break;
+ *((u8 *)req->buf) = value;
+ value = min(w_length, (u16) 1);
+ break;
+
+ /*
+ * USB 3.0 additions:
+ * Function driver should handle get_status request. If such cb
+ * wasn't supplied we respond with default value = 0
+ * Note: function driver should supply such cb only for the first
+ * interface of the function
+ */
+ case USB_REQ_GET_STATUS:
+ if (!gadget_is_superspeed(gadget))
+ goto unknown;
+ if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))
+ goto unknown;
+ value = 2; /* This is the length of the get_status reply */
+ put_unaligned_le16(0, req->buf);
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ status = f->get_status ? f->get_status(f) : 0;
+ if (status < 0)
+ break;
+ put_unaligned_le16(status & 0x0000ffff, req->buf);
+ break;
+ /*
+ * Function drivers should handle SetFeature/ClearFeature
+ * (FUNCTION_SUSPEND) request. function_suspend cb should be supplied
+ * only for the first interface of the function
+ */
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ if (!gadget_is_superspeed(gadget))
+ goto unknown;
+ if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE))
+ goto unknown;
+ switch (w_value) {
+ case USB_INTRF_FUNC_SUSPEND:
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ value = 0;
+ if (f->func_suspend)
+ value = f->func_suspend(f, w_index >> 8);
+ if (value < 0) {
+ ERROR(cdev,
+ "func_suspend() returned error %d\n",
+ value);
+ value = 0;
+ }
+ break;
+ }
+ break;
+ default:
+unknown:
+ VDBG(cdev,
+ "non-core control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+
+ /* functions always handle their interfaces and endpoints...
+ * punt other recipients (other, WUSB, ...) to the current
+ * configuration code.
+ *
+ * REVISIT it could make sense to let the composite device
+ * take such requests too, if that's ever needed: to work
+ * in config 0, etc.
+ */
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (test_bit(endp, f->endpoints))
+ break;
+ }
+ if (&f->list == &cdev->config->functions)
+ f = NULL;
+ break;
+ }
+
+ if (f && f->setup)
+ value = f->setup(f, ctrl);
+ else {
+ struct usb_configuration *c;
+
+ c = cdev->config;
+ if (c && c->setup)
+ value = c->setup(c, ctrl);
+ }
+
+ goto done;
+ }
+
+ /* respond with data transfer before status phase? */
+ if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
+ req->length = value;
+ req->zero = value < w_length;
+ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0) {
+ DBG(cdev, "ep_queue --> %d\n", value);
+ req->status = 0;
+ composite_setup_complete(gadget->ep0, req);
+ }
+ } else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {
+ WARN(cdev,
+ "%s: Delayed status not supported for w_length != 0",
+ __func__);
+ }
+
+done:
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static void composite_disconnect(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ unsigned long flags;
+
+ /* REVISIT: should we have config and device level
+ * disconnect callbacks?
+ */
+ spin_lock_irqsave(&cdev->lock, flags);
+ if (cdev->config)
+ reset_config(cdev);
+ if (composite->disconnect)
+ composite->disconnect(cdev);
+ spin_unlock_irqrestore(&cdev->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static ssize_t composite_show_suspended(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+
+ return sprintf(buf, "%d\n", cdev->suspended);
+}
+
+static DEVICE_ATTR(suspended, 0444, composite_show_suspended, NULL);
+
+static void
+composite_unbind(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+
+ /* composite_disconnect() must already have been called
+ * by the underlying peripheral controller driver!
+ * so there's no i/o concurrency that could affect the
+ * state protected by cdev->lock.
+ */
+ WARN_ON(cdev->config);
+
+ while (!list_empty(&cdev->configs)) {
+ struct usb_configuration *c;
+ c = list_first_entry(&cdev->configs,
+ struct usb_configuration, list);
+ remove_config(cdev, c);
+ }
+ if (composite->unbind)
+ composite->unbind(cdev);
+
+ if (cdev->req) {
+ kfree(cdev->req->buf);
+ usb_ep_free_request(gadget->ep0, cdev->req);
+ }
+ device_remove_file(&gadget->dev, &dev_attr_suspended);
+ kfree(cdev);
+ set_gadget_data(gadget, NULL);
+ composite = NULL;
+}
+
+static u8 override_id(struct usb_composite_dev *cdev, u8 *desc)
+{
+ if (!*desc) {
+ int ret = usb_string_id(cdev);
+ if (unlikely(ret < 0))
+ WARNING(cdev, "failed to override string ID\n");
+ else
+ *desc = ret;
+ }
+
+ return *desc;
+}
+
+static int composite_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct usb_composite_dev *cdev;
+ int status = -ENOMEM;
+
+ cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
+ if (!cdev)
+ return status;
+
+ spin_lock_init(&cdev->lock);
+ cdev->gadget = gadget;
+ set_gadget_data(gadget, cdev);
+ INIT_LIST_HEAD(&cdev->configs);
+
+ /* preallocate control response and buffer */
+ cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!cdev->req)
+ goto fail;
+ cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
+ if (!cdev->req->buf)
+ goto fail;
+ cdev->req->complete = composite_setup_complete;
+ gadget->ep0->driver_data = cdev;
+
+ cdev->bufsiz = USB_BUFSIZ;
+ cdev->driver = composite;
+
+ /*
+ * As per USB compliance update, a device that is actively drawing
+ * more than 100mA from USB must report itself as bus-powered in
+ * the GetStatus(DEVICE) call.
+ */
+ if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW)
+ usb_gadget_set_selfpowered(gadget);
+
+ /* interface and string IDs start at zero via kzalloc.
+ * we force endpoints to start unassigned; few controller
+ * drivers will zero ep->driver_data.
+ */
+ usb_ep_autoconfig_reset(cdev->gadget);
+
+ /* composite gadget needs to assign strings for whole device (like
+ * serial number), register function drivers, potentially update
+ * power state and consumption, etc
+ */
+ status = composite->bind(cdev);
+ if (status < 0)
+ goto fail;
+
+ cdev->desc = *composite->dev;
+
+ /* standardized runtime overrides for device ID data */
+ if (idVendor)
+ cdev->desc.idVendor = cpu_to_le16(idVendor);
+ else
+ idVendor = le16_to_cpu(cdev->desc.idVendor);
+ if (idProduct)
+ cdev->desc.idProduct = cpu_to_le16(idProduct);
+ else
+ idProduct = le16_to_cpu(cdev->desc.idProduct);
+ if (bcdDevice)
+ cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
+ else
+ bcdDevice = le16_to_cpu(cdev->desc.bcdDevice);
+
+ /* string overrides */
+ if (iManufacturer || !cdev->desc.iManufacturer) {
+ if (!iManufacturer && !composite->iManufacturer &&
+ !*composite_manufacturer)
+ snprintf(composite_manufacturer,
+ sizeof composite_manufacturer,
+ "%s %s with %s",
+ init_utsname()->sysname,
+ init_utsname()->release,
+ gadget->name);
+
+ cdev->manufacturer_override =
+ override_id(cdev, &cdev->desc.iManufacturer);
+ }
+
+ if (iProduct || (!cdev->desc.iProduct && composite->iProduct))
+ cdev->product_override =
+ override_id(cdev, &cdev->desc.iProduct);
+
+ if (iSerialNumber ||
+ (!cdev->desc.iSerialNumber && composite->iSerialNumber))
+ cdev->serial_override =
+ override_id(cdev, &cdev->desc.iSerialNumber);
+
+ /* has userspace failed to provide a serial number? */
+ if (composite->needs_serial && !cdev->desc.iSerialNumber)
+ WARNING(cdev, "userspace failed to provide iSerialNumber\n");
+
+ /* finish up */
+ status = device_create_file(&gadget->dev, &dev_attr_suspended);
+ if (status)
+ goto fail;
+
+ INFO(cdev, "%s ready\n", composite->name);
+ return 0;
+
+fail:
+ composite_unbind(gadget);
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+composite_suspend(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+
+ /* REVISIT: should we have config level
+ * suspend/resume callbacks?
+ */
+ DBG(cdev, "suspend\n");
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->suspend)
+ f->suspend(f);
+ }
+ }
+ if (composite->suspend)
+ composite->suspend(cdev);
+
+ cdev->suspended = 1;
+
+ usb_gadget_vbus_draw(gadget, 2);
+}
+
+static void
+composite_resume(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+ u8 maxpower;
+
+ /* REVISIT: should we have config level
+ * suspend/resume callbacks?
+ */
+ DBG(cdev, "resume\n");
+ if (composite->resume)
+ composite->resume(cdev);
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->resume)
+ f->resume(f);
+ }
+
+ maxpower = cdev->config->bMaxPower;
+
+ usb_gadget_vbus_draw(gadget, maxpower ?
+ (2 * maxpower) : CONFIG_USB_GADGET_VBUS_DRAW);
+ }
+
+ cdev->suspended = 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver composite_driver = {
+ .bind = composite_bind,
+ .unbind = composite_unbind,
+
+ .setup = composite_setup,
+ .disconnect = composite_disconnect,
+
+ .suspend = composite_suspend,
+ .resume = composite_resume,
+
+ .driver = {
+ .owner = THIS_MODULE,
+ },
+};
+
+/**
+ * usb_composite_probe() - register a composite driver
+ * @driver: the driver to register
+ * @bind: the callback used to allocate resources that are shared across the
+ * whole device, such as string IDs, and add its configurations using
+ * @usb_add_config(). This may fail by returning a negative errno
+ * value; it should return zero on successful initialization.
+ * Context: single threaded during gadget setup
+ *
+ * This function is used to register drivers using the composite driver
+ * framework. The return value is zero, or a negative errno value.
+ * Those values normally come from the driver's @bind method, which does
+ * all the work of setting up the driver to match the hardware.
+ *
+ * On successful return, the gadget is ready to respond to requests from
+ * the host, unless one of its components invokes usb_gadget_disconnect()
+ * while it was binding. That would usually be done in order to wait for
+ * some userspace participation.
+ */
+int usb_composite_probe(struct usb_composite_driver *driver)
+{
+ if (!driver || !driver->dev || composite || !driver->bind)
+ return -EINVAL;
+
+ if (!driver->name)
+ driver->name = "composite";
+ if (!driver->iProduct)
+ driver->iProduct = driver->name;
+ composite_driver.function = (char *) driver->name;
+ composite_driver.driver.name = driver->name;
+ composite_driver.max_speed = driver->max_speed;
+ composite = driver;
+
+ return usb_gadget_probe_driver(&composite_driver);
+}
+
+/**
+ * usb_composite_unregister() - unregister a composite driver
+ * @driver: the driver to unregister
+ *
+ * This function is used to unregister drivers using the composite
+ * driver framework.
+ */
+void usb_composite_unregister(struct usb_composite_driver *driver)
+{
+ if (composite != driver)
+ return;
+ usb_gadget_unregister_driver(&composite_driver);
+}
+
+/**
+ * usb_composite_setup_continue() - Continue with the control transfer
+ * @cdev: the composite device who's control transfer was kept waiting
+ *
+ * This function must be called by the USB function driver to continue
+ * with the control transfer's data/status stage in case it had requested to
+ * delay the data/status stages. A USB function's setup handler (e.g. set_alt())
+ * can request the composite framework to delay the setup request's data/status
+ * stages by returning USB_GADGET_DELAYED_STATUS.
+ */
+void usb_composite_setup_continue(struct usb_composite_dev *cdev)
+{
+ int value;
+ struct usb_request *req = cdev->req;
+ unsigned long flags;
+
+ DBG(cdev, "%s\n", __func__);
+ spin_lock_irqsave(&cdev->lock, flags);
+
+ if (cdev->delayed_status == 0) {
+ WARN(cdev, "%s: Unexpected call\n", __func__);
+
+ } else if (--cdev->delayed_status == 0) {
+ DBG(cdev, "%s: Completing delayed status\n", __func__);
+ req->length = 0;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0) {
+ DBG(cdev, "ep_queue --> %d\n", value);
+ req->status = 0;
+ composite_setup_complete(cdev->gadget->ep0, req);
+ }
+ }
+
+ spin_unlock_irqrestore(&cdev->lock, flags);
+}
+
diff --git a/drivers/staging/ccg/composite.h b/drivers/staging/ccg/composite.h
new file mode 100644
index 000000000000..19a5adf18bf4
--- /dev/null
+++ b/drivers/staging/ccg/composite.h
@@ -0,0 +1,395 @@
+/*
+ * composite.h -- framework for usb gadgets which are composite devices
+ *
+ * Copyright (C) 2006-2008 David Brownell
+ *
+ * This program is free software; 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __LINUX_USB_COMPOSITE_H
+#define __LINUX_USB_COMPOSITE_H
+
+/*
+ * This framework is an optional layer on top of the USB Gadget interface,
+ * making it easier to build (a) Composite devices, supporting multiple
+ * functions within any single configuration, and (b) Multi-configuration
+ * devices, also supporting multiple functions but without necessarily
+ * having more than one function per configuration.
+ *
+ * Example: a device with a single configuration supporting both network
+ * link and mass storage functions is a composite device. Those functions
+ * might alternatively be packaged in individual configurations, but in
+ * the composite model the host can use both functions at the same time.
+ */
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/*
+ * USB function drivers should return USB_GADGET_DELAYED_STATUS if they
+ * wish to delay the data/status stages of the control transfer till they
+ * are ready. The control transfer will then be kept from completing till
+ * all the function drivers that requested for USB_GADGET_DELAYED_STAUS
+ * invoke usb_composite_setup_continue().
+ */
+#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */
+
+struct usb_configuration;
+
+/**
+ * struct usb_function - describes one function of a configuration
+ * @name: For diagnostics, identifies the function.
+ * @strings: tables of strings, keyed by identifiers assigned during bind()
+ * and by language IDs provided in control requests
+ * @descriptors: Table of full (or low) speed descriptors, using interface and
+ * string identifiers assigned during @bind(). If this pointer is null,
+ * the function will not be available at full speed (or at low speed).
+ * @hs_descriptors: Table of high speed descriptors, using interface and
+ * string identifiers assigned during @bind(). If this pointer is null,
+ * the function will not be available at high speed.
+ * @ss_descriptors: Table of super speed descriptors, using interface and
+ * string identifiers assigned during @bind(). If this
+ * pointer is null after initiation, the function will not
+ * be available at super speed.
+ * @config: assigned when @usb_add_function() is called; this is the
+ * configuration with which this function is associated.
+ * @bind: Before the gadget can register, all of its functions bind() to the
+ * available resources including string and interface identifiers used
+ * in interface or class descriptors; endpoints; I/O buffers; and so on.
+ * @unbind: Reverses @bind; called as a side effect of unregistering the
+ * driver which added this function.
+ * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
+ * initialize usb_ep.driver data at this time (when it is used).
+ * Note that setting an interface to its current altsetting resets
+ * interface state, and that all interfaces have a disabled state.
+ * @get_alt: Returns the active altsetting. If this is not provided,
+ * then only altsetting zero is supported.
+ * @disable: (REQUIRED) Indicates the function should be disabled. Reasons
+ * include host resetting or reconfiguring the gadget, and disconnection.
+ * @setup: Used for interface-specific control requests.
+ * @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.
+ * @func_suspend: callback to be called when
+ * SetFeature(FUNCTION_SUSPEND) is reseived
+ *
+ * A single USB function uses one or more interfaces, and should in most
+ * cases support operation at both full and high speeds. Each function is
+ * associated by @usb_add_function() with a one configuration; that function
+ * causes @bind() to be called so resources can be allocated as part of
+ * setting up a gadget driver. Those resources include endpoints, which
+ * should be allocated using @usb_ep_autoconfig().
+ *
+ * To support dual speed operation, a function driver provides descriptors
+ * for both high and full speed operation. Except in rare cases that don't
+ * involve bulk endpoints, each speed needs different endpoint descriptors.
+ *
+ * Function drivers choose their own strategies for managing instance data.
+ * The simplest strategy just declares it "static', which means the function
+ * can only be activated once. If the function needs to be exposed in more
+ * than one configuration at a given speed, it needs to support multiple
+ * usb_function structures (one for each configuration).
+ *
+ * A more complex strategy might encapsulate a @usb_function structure inside
+ * a driver-specific instance structure to allows multiple activations. An
+ * example of multiple activations might be a CDC ACM function that supports
+ * two or more distinct instances within the same configuration, providing
+ * several independent logical data links to a USB host.
+ */
+struct usb_function {
+ const char *name;
+ struct usb_gadget_strings **strings;
+ struct usb_descriptor_header **descriptors;
+ struct usb_descriptor_header **hs_descriptors;
+ struct usb_descriptor_header **ss_descriptors;
+
+ struct usb_configuration *config;
+
+ /* REVISIT: bind() functions can be marked __init, which
+ * makes trouble for section mismatch analysis. See if
+ * we can't restructure things to avoid mismatching.
+ * Related: unbind() may kfree() but bind() won't...
+ */
+
+ /* configuration management: bind/unbind */
+ int (*bind)(struct usb_configuration *,
+ struct usb_function *);
+ void (*unbind)(struct usb_configuration *,
+ struct usb_function *);
+
+ /* runtime state management */
+ int (*set_alt)(struct usb_function *,
+ unsigned interface, unsigned alt);
+ int (*get_alt)(struct usb_function *,
+ unsigned interface);
+ void (*disable)(struct usb_function *);
+ int (*setup)(struct usb_function *,
+ const struct usb_ctrlrequest *);
+ void (*suspend)(struct usb_function *);
+ void (*resume)(struct usb_function *);
+
+ /* USB 3.0 additions */
+ int (*get_status)(struct usb_function *);
+ int (*func_suspend)(struct usb_function *,
+ u8 suspend_opt);
+ /* private: */
+ /* internals */
+ struct list_head list;
+ DECLARE_BITMAP(endpoints, 32);
+};
+
+int usb_add_function(struct usb_configuration *, struct usb_function *);
+
+int usb_function_deactivate(struct usb_function *);
+int usb_function_activate(struct usb_function *);
+
+int usb_interface_id(struct usb_configuration *, struct usb_function *);
+
+int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
+ struct usb_ep *_ep);
+
+#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */
+
+/**
+ * struct usb_configuration - represents one gadget configuration
+ * @label: For diagnostics, describes the configuration.
+ * @strings: Tables of strings, keyed by identifiers assigned during @bind()
+ * and by language IDs provided in control requests.
+ * @descriptors: Table of descriptors preceding all function descriptors.
+ * Examples include OTG and vendor-specific descriptors.
+ * @unbind: Reverses @bind; called as a side effect of unregistering the
+ * driver which added this configuration.
+ * @setup: Used to delegate control requests that aren't handled by standard
+ * device infrastructure or directed at a specific interface.
+ * @bConfigurationValue: Copied into configuration descriptor.
+ * @iConfiguration: Copied into configuration descriptor.
+ * @bmAttributes: Copied into configuration descriptor.
+ * @bMaxPower: Copied into configuration descriptor.
+ * @cdev: assigned by @usb_add_config() before calling @bind(); this is
+ * the device associated with this configuration.
+ *
+ * Configurations are building blocks for gadget drivers structured around
+ * function drivers. Simple USB gadgets require only one function and one
+ * configuration, and handle dual-speed hardware by always providing the same
+ * functionality. Slightly more complex gadgets may have more than one
+ * single-function configuration at a given speed; or have configurations
+ * that only work at one speed.
+ *
+ * Composite devices are, by definition, ones with configurations which
+ * include more than one function.
+ *
+ * The lifecycle of a usb_configuration includes allocation, initialization
+ * of the fields described above, and calling @usb_add_config() to set up
+ * internal data and bind it to a specific device. The configuration's
+ * @bind() method is then used to initialize all the functions and then
+ * call @usb_add_function() for them.
+ *
+ * Those functions would normally be independent of each other, but that's
+ * not mandatory. CDC WMC devices are an example where functions often
+ * depend on other functions, with some functions subsidiary to others.
+ * Such interdependency may be managed in any way, so long as all of the
+ * descriptors complete by the time the composite driver returns from
+ * its bind() routine.
+ */
+struct usb_configuration {
+ const char *label;
+ struct usb_gadget_strings **strings;
+ const struct usb_descriptor_header **descriptors;
+
+ /* REVISIT: bind() functions can be marked __init, which
+ * makes trouble for section mismatch analysis. See if
+ * we can't restructure things to avoid mismatching...
+ */
+
+ /* configuration management: unbind/setup */
+ void (*unbind)(struct usb_configuration *);
+ int (*setup)(struct usb_configuration *,
+ const struct usb_ctrlrequest *);
+
+ /* fields in the config descriptor */
+ u8 bConfigurationValue;
+ u8 iConfiguration;
+ u8 bmAttributes;
+ u8 bMaxPower;
+
+ struct usb_composite_dev *cdev;
+
+ /* private: */
+ /* internals */
+ struct list_head list;
+ struct list_head functions;
+ u8 next_interface_id;
+ unsigned superspeed:1;
+ unsigned highspeed:1;
+ unsigned fullspeed:1;
+ struct usb_function *interface[MAX_CONFIG_INTERFACES];
+};
+
+int usb_add_config(struct usb_composite_dev *,
+ struct usb_configuration *,
+ int (*)(struct usb_configuration *));
+
+void usb_remove_config(struct usb_composite_dev *,
+ struct usb_configuration *);
+
+/**
+ * struct usb_composite_driver - groups configurations into a gadget
+ * @name: For diagnostics, identifies the driver.
+ * @iProduct: Used as iProduct override if @dev->iProduct is not set.
+ * If NULL value of @name is taken.
+ * @iManufacturer: Used as iManufacturer override if @dev->iManufacturer is
+ * not set. If NULL a default "<system> <release> with <udc>" value
+ * will be used.
+ * @iSerialNumber: Used as iSerialNumber override if @dev->iSerialNumber is
+ * not set.
+ * @dev: Template descriptor for the device, including default device
+ * identifiers.
+ * @strings: tables of strings, keyed by identifiers assigned during @bind
+ * and language IDs provided in control requests
+ * @max_speed: Highest speed the driver supports.
+ * @needs_serial: set to 1 if the gadget needs userspace to provide
+ * a serial number. If one is not provided, warning will be printed.
+ * @bind: (REQUIRED) Used to allocate resources that are shared across the
+ * whole device, such as string IDs, and add its configurations using
+ * @usb_add_config(). This may fail by returning a negative errno
+ * value; it should return zero on successful initialization.
+ * @unbind: Reverses @bind; called as a side effect of unregistering
+ * this driver.
+ * @disconnect: optional driver disconnect method
+ * @suspend: Notifies when the host stops sending USB traffic,
+ * after function notifications
+ * @resume: Notifies configuration when the host restarts USB traffic,
+ * before function notifications
+ *
+ * Devices default to reporting self powered operation. Devices which rely
+ * on bus powered operation should report this in their @bind method.
+ *
+ * Before returning from @bind, various fields in the template descriptor
+ * may be overridden. These include the idVendor/idProduct/bcdDevice values
+ * normally to bind the appropriate host side driver, and the three strings
+ * (iManufacturer, iProduct, iSerialNumber) normally used to provide user
+ * meaningful device identifiers. (The strings will not be defined unless
+ * they are defined in @dev and @strings.) The correct ep0 maxpacket size
+ * is also reported, as defined by the underlying controller driver.
+ */
+struct usb_composite_driver {
+ const char *name;
+ const char *iProduct;
+ const char *iManufacturer;
+ const char *iSerialNumber;
+ const struct usb_device_descriptor *dev;
+ struct usb_gadget_strings **strings;
+ enum usb_device_speed max_speed;
+ unsigned needs_serial:1;
+
+ int (*bind)(struct usb_composite_dev *cdev);
+ int (*unbind)(struct usb_composite_dev *);
+
+ void (*disconnect)(struct usb_composite_dev *);
+
+ /* global suspend hooks */
+ void (*suspend)(struct usb_composite_dev *);
+ void (*resume)(struct usb_composite_dev *);
+};
+
+extern int usb_composite_probe(struct usb_composite_driver *driver);
+extern void usb_composite_unregister(struct usb_composite_driver *driver);
+extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
+
+
+/**
+ * struct usb_composite_device - represents one composite usb gadget
+ * @gadget: read-only, abstracts the gadget's usb peripheral controller
+ * @req: used for control responses; buffer is pre-allocated
+ * @bufsiz: size of buffer pre-allocated in @req
+ * @config: the currently active configuration
+ *
+ * One of these devices is allocated and initialized before the
+ * associated device driver's bind() is called.
+ *
+ * OPEN ISSUE: it appears that some WUSB devices will need to be
+ * built by combining a normal (wired) gadget with a wireless one.
+ * This revision of the gadget framework should probably try to make
+ * sure doing that won't hurt too much.
+ *
+ * One notion for how to handle Wireless USB devices involves:
+ * (a) a second gadget here, discovery mechanism TBD, but likely
+ * needing separate "register/unregister WUSB gadget" calls;
+ * (b) updates to usb_gadget to include flags "is it wireless",
+ * "is it wired", plus (presumably in a wrapper structure)
+ * bandgroup and PHY info;
+ * (c) presumably a wireless_ep wrapping a usb_ep, and reporting
+ * wireless-specific parameters like maxburst and maxsequence;
+ * (d) configurations that are specific to wireless links;
+ * (e) function drivers that understand wireless configs and will
+ * support wireless for (additional) function instances;
+ * (f) a function to support association setup (like CBAF), not
+ * necessarily requiring a wireless adapter;
+ * (g) composite device setup that can create one or more wireless
+ * configs, including appropriate association setup support;
+ * (h) more, TBD.
+ */
+struct usb_composite_dev {
+ struct usb_gadget *gadget;
+ struct usb_request *req;
+ unsigned bufsiz;
+
+ struct usb_configuration *config;
+
+ /* private: */
+ /* internals */
+ unsigned int suspended:1;
+ struct usb_device_descriptor desc;
+ struct list_head configs;
+ struct usb_composite_driver *driver;
+ u8 next_string_id;
+ u8 manufacturer_override;
+ u8 product_override;
+ u8 serial_override;
+
+ /* the gadget driver won't enable the data pullup
+ * while the deactivation count is nonzero.
+ */
+ unsigned deactivations;
+
+ /* the composite driver won't complete the control transfer's
+ * data/status stages till delayed_status is zero.
+ */
+ int delayed_status;
+
+ /* protects deactivations and delayed_status counts*/
+ spinlock_t lock;
+};
+
+extern int usb_string_id(struct usb_composite_dev *c);
+extern int usb_string_ids_tab(struct usb_composite_dev *c,
+ struct usb_string *str);
+extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n);
+
+
+/* messaging utils */
+#define DBG(d, fmt, args...) \
+ dev_dbg(&(d)->gadget->dev , fmt , ## args)
+#define VDBG(d, fmt, args...) \
+ dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+#define ERROR(d, fmt, args...) \
+ dev_err(&(d)->gadget->dev , fmt , ## args)
+#define WARNING(d, fmt, args...) \
+ dev_warn(&(d)->gadget->dev , fmt , ## args)
+#define INFO(d, fmt, args...) \
+ dev_info(&(d)->gadget->dev , fmt , ## args)
+
+#endif /* __LINUX_USB_COMPOSITE_H */
diff --git a/drivers/staging/ccg/config.c b/drivers/staging/ccg/config.c
new file mode 100644
index 000000000000..7542a72ce51a
--- /dev/null
+++ b/drivers/staging/ccg/config.c
@@ -0,0 +1,158 @@
+/*
+ * usb/gadget/config.c -- simplify building config descriptors
+ *
+ * Copyright (C) 2003 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/device.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+
+/**
+ * usb_descriptor_fillbuf - fill buffer with descriptors
+ * @buf: Buffer to be filled
+ * @buflen: Size of buf
+ * @src: Array of descriptor pointers, terminated by null pointer.
+ *
+ * Copies descriptors into the buffer, returning the length or a
+ * negative error code if they can't all be copied. Useful when
+ * assembling descriptors for an associated set of interfaces used
+ * as part of configuring a composite device; or in other cases where
+ * sets of descriptors need to be marshaled.
+ */
+int
+usb_descriptor_fillbuf(void *buf, unsigned buflen,
+ const struct usb_descriptor_header **src)
+{
+ u8 *dest = buf;
+
+ if (!src)
+ return -EINVAL;
+
+ /* fill buffer from src[] until null descriptor ptr */
+ for (; NULL != *src; src++) {
+ unsigned len = (*src)->bLength;
+
+ if (len > buflen)
+ return -EINVAL;
+ memcpy(dest, *src, len);
+ buflen -= len;
+ dest += len;
+ }
+ return dest - (u8 *)buf;
+}
+
+
+/**
+ * usb_gadget_config_buf - builts a complete configuration descriptor
+ * @config: Header for the descriptor, including characteristics such
+ * as power requirements and number of interfaces.
+ * @desc: Null-terminated vector of pointers to the descriptors (interface,
+ * endpoint, etc) defining all functions in this device configuration.
+ * @buf: Buffer for the resulting configuration descriptor.
+ * @length: Length of buffer. If this is not big enough to hold the
+ * entire configuration descriptor, an error code will be returned.
+ *
+ * This copies descriptors into the response buffer, building a descriptor
+ * for that configuration. It returns the buffer length or a negative
+ * status code. The config.wTotalLength field is set to match the length
+ * of the result, but other descriptor fields (including power usage and
+ * interface count) must be set by the caller.
+ *
+ * Gadget drivers could use this when constructing a config descriptor
+ * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
+ * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
+ */
+int usb_gadget_config_buf(
+ const struct usb_config_descriptor *config,
+ void *buf,
+ unsigned length,
+ const struct usb_descriptor_header **desc
+)
+{
+ struct usb_config_descriptor *cp = buf;
+ int len;
+
+ /* config descriptor first */
+ if (length < USB_DT_CONFIG_SIZE || !desc)
+ return -EINVAL;
+ *cp = *config;
+
+ /* then interface/endpoint/class/vendor/... */
+ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
+ length - USB_DT_CONFIG_SIZE, desc);
+ if (len < 0)
+ return len;
+ len += USB_DT_CONFIG_SIZE;
+ if (len > 0xffff)
+ return -EINVAL;
+
+ /* patch up the config descriptor */
+ cp->bLength = USB_DT_CONFIG_SIZE;
+ cp->bDescriptorType = USB_DT_CONFIG;
+ cp->wTotalLength = cpu_to_le16(len);
+ cp->bmAttributes |= USB_CONFIG_ATT_ONE;
+ return len;
+}
+
+/**
+ * usb_copy_descriptors - copy a vector of USB descriptors
+ * @src: null-terminated vector to copy
+ * Context: initialization code, which may sleep
+ *
+ * This makes a copy of a vector of USB descriptors. Its primary use
+ * is to support usb_function objects which can have multiple copies,
+ * each needing different descriptors. Functions may have static
+ * tables of descriptors, which are used as templates and customized
+ * with identifiers (for interfaces, strings, endpoints, and more)
+ * as needed by a given function instance.
+ */
+struct usb_descriptor_header **
+usb_copy_descriptors(struct usb_descriptor_header **src)
+{
+ struct usb_descriptor_header **tmp;
+ unsigned bytes;
+ unsigned n_desc;
+ void *mem;
+ struct usb_descriptor_header **ret;
+
+ /* count descriptors and their sizes; then add vector size */
+ for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++)
+ bytes += (*tmp)->bLength;
+ bytes += (n_desc + 1) * sizeof(*tmp);
+
+ mem = kmalloc(bytes, GFP_KERNEL);
+ if (!mem)
+ return NULL;
+
+ /* fill in pointers starting at "tmp",
+ * to descriptors copied starting at "mem";
+ * and return "ret"
+ */
+ tmp = mem;
+ ret = mem;
+ mem += (n_desc + 1) * sizeof(*tmp);
+ while (*src) {
+ memcpy(mem, *src, (*src)->bLength);
+ *tmp = mem;
+ tmp++;
+ mem += (*src)->bLength;
+ src++;
+ }
+ *tmp = NULL;
+
+ return ret;
+}
+
diff --git a/drivers/staging/ccg/epautoconf.c b/drivers/staging/ccg/epautoconf.c
new file mode 100644
index 000000000000..51f3d42f5a64
--- /dev/null
+++ b/drivers/staging/ccg/epautoconf.c
@@ -0,0 +1,393 @@
+/*
+ * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
+ *
+ * Copyright (C) 2004 David Brownell
+ *
+ * This program is free software; 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/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "gadget_chips.h"
+
+
+/* we must assign addresses for configurable endpoints (like net2280) */
+static unsigned epnum;
+
+// #define MANY_ENDPOINTS
+#ifdef MANY_ENDPOINTS
+/* more than 15 configurable endpoints */
+static unsigned in_epnum;
+#endif
+
+
+/*
+ * This should work with endpoints from controller drivers sharing the
+ * same endpoint naming convention. By example:
+ *
+ * - ep1, ep2, ... address is fixed, not direction or type
+ * - ep1in, ep2out, ... address and direction are fixed, not type
+ * - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
+ * - ep1in-bulk, ep2out-iso, ... all three are fixed
+ * - ep-* ... no functionality restrictions
+ *
+ * Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal.
+ * Less common restrictions are implied by gadget_is_*().
+ *
+ * NOTE: each endpoint is unidirectional, as specified by its USB
+ * descriptor; and isn't specific to a configuration or altsetting.
+ */
+static int
+ep_matches (
+ struct usb_gadget *gadget,
+ struct usb_ep *ep,
+ struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ep_comp
+)
+{
+ u8 type;
+ const char *tmp;
+ u16 max;
+
+ int num_req_streams = 0;
+
+ /* endpoint already claimed? */
+ if (NULL != ep->driver_data)
+ return 0;
+
+ /* only support ep0 for portable CONTROL traffic */
+ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ if (USB_ENDPOINT_XFER_CONTROL == type)
+ return 0;
+
+ /* some other naming convention */
+ if ('e' != ep->name[0])
+ return 0;
+
+ /* type-restriction: "-iso", "-bulk", or "-int".
+ * direction-restriction: "in", "out".
+ */
+ if ('-' != ep->name[2]) {
+ tmp = strrchr (ep->name, '-');
+ if (tmp) {
+ switch (type) {
+ case USB_ENDPOINT_XFER_INT:
+ /* bulk endpoints handle interrupt transfers,
+ * except the toggle-quirky iso-synch kind
+ */
+ if ('s' == tmp[2]) // == "-iso"
+ return 0;
+ /* for now, avoid PXA "interrupt-in";
+ * it's documented as never using DATA1.
+ */
+ if (gadget_is_pxa (gadget)
+ && 'i' == tmp [1])
+ return 0;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ if ('b' != tmp[1]) // != "-bulk"
+ return 0;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ if ('s' != tmp[2]) // != "-iso"
+ return 0;
+ }
+ } else {
+ tmp = ep->name + strlen (ep->name);
+ }
+
+ /* direction-restriction: "..in-..", "out-.." */
+ tmp--;
+ if (!isdigit (*tmp)) {
+ if (desc->bEndpointAddress & USB_DIR_IN) {
+ if ('n' != *tmp)
+ return 0;
+ } else {
+ if ('t' != *tmp)
+ return 0;
+ }
+ }
+ }
+
+ /*
+ * Get the number of required streams from the EP companion
+ * descriptor and see if the EP matches it
+ */
+ if (usb_endpoint_xfer_bulk(desc)) {
+ if (ep_comp && gadget->max_speed >= USB_SPEED_SUPER) {
+ num_req_streams = ep_comp->bmAttributes & 0x1f;
+ if (num_req_streams > ep->max_streams)
+ return 0;
+ }
+
+ }
+
+ /*
+ * If the protocol driver hasn't yet decided on wMaxPacketSize
+ * and wants to know the maximum possible, provide the info.
+ */
+ if (desc->wMaxPacketSize == 0)
+ desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket);
+
+ /* endpoint maxpacket size is an input parameter, except for bulk
+ * where it's an output parameter representing the full speed limit.
+ * the usb spec fixes high speed bulk maxpacket at 512 bytes.
+ */
+ max = 0x7ff & usb_endpoint_maxp(desc);
+ switch (type) {
+ case USB_ENDPOINT_XFER_INT:
+ /* INT: limit 64 bytes full speed, 1024 high/super speed */
+ if (!gadget_is_dualspeed(gadget) && max > 64)
+ return 0;
+ /* FALLTHROUGH */
+
+ case USB_ENDPOINT_XFER_ISOC:
+ /* ISO: limit 1023 bytes full speed, 1024 high/super speed */
+ if (ep->maxpacket < max)
+ return 0;
+ if (!gadget_is_dualspeed(gadget) && max > 1023)
+ return 0;
+
+ /* BOTH: "high bandwidth" works only at high speed */
+ if ((desc->wMaxPacketSize & cpu_to_le16(3<<11))) {
+ if (!gadget_is_dualspeed(gadget))
+ return 0;
+ /* configure your hardware with enough buffering!! */
+ }
+ break;
+ }
+
+ /* MATCH!! */
+
+ /* report address */
+ desc->bEndpointAddress &= USB_DIR_IN;
+ if (isdigit (ep->name [2])) {
+ u8 num = simple_strtoul (&ep->name [2], NULL, 10);
+ desc->bEndpointAddress |= num;
+#ifdef MANY_ENDPOINTS
+ } else if (desc->bEndpointAddress & USB_DIR_IN) {
+ if (++in_epnum > 15)
+ return 0;
+ desc->bEndpointAddress = USB_DIR_IN | in_epnum;
+#endif
+ } else {
+ if (++epnum > 15)
+ return 0;
+ desc->bEndpointAddress |= epnum;
+ }
+
+ /* report (variable) full speed bulk maxpacket */
+ if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) {
+ int size = ep->maxpacket;
+
+ /* min() doesn't work on bitfields with gcc-3.5 */
+ if (size > 64)
+ size = 64;
+ desc->wMaxPacketSize = cpu_to_le16(size);
+ }
+ ep->address = desc->bEndpointAddress;
+ return 1;
+}
+
+static struct usb_ep *
+find_ep (struct usb_gadget *gadget, const char *name)
+{
+ struct usb_ep *ep;
+
+ list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+ if (0 == strcmp (ep->name, name))
+ return ep;
+ }
+ return NULL;
+}
+
+/**
+ * usb_ep_autoconfig_ss() - choose an endpoint matching the ep
+ * descriptor and ep companion descriptor
+ * @gadget: The device to which the endpoint must belong.
+ * @desc: Endpoint descriptor, with endpoint direction and transfer mode
+ * initialized. For periodic transfers, the maximum packet
+ * size must also be initialized. This is modified on
+ * success.
+ * @ep_comp: Endpoint companion descriptor, with the required
+ * number of streams. Will be modified when the chosen EP
+ * supports a different number of streams.
+ *
+ * This routine replaces the usb_ep_autoconfig when needed
+ * superspeed enhancments. If such enhancemnets are required,
+ * the FD should call usb_ep_autoconfig_ss directly and provide
+ * the additional ep_comp parameter.
+ *
+ * By choosing an endpoint to use with the specified descriptor,
+ * this routine simplifies writing gadget drivers that work with
+ * multiple USB device controllers. The endpoint would be
+ * passed later to usb_ep_enable(), along with some descriptor.
+ *
+ * That second descriptor won't always be the same as the first one.
+ * For example, isochronous endpoints can be autoconfigured for high
+ * bandwidth, and then used in several lower bandwidth altsettings.
+ * Also, high and full speed descriptors will be different.
+ *
+ * Be sure to examine and test the results of autoconfiguration
+ * on your hardware. This code may not make the best choices
+ * about how to use the USB controller, and it can't know all
+ * the restrictions that may apply. Some combinations of driver
+ * and hardware won't be able to autoconfigure.
+ *
+ * On success, this returns an un-claimed usb_ep, and modifies the endpoint
+ * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
+ * is initialized as if the endpoint were used at full speed and
+ * the bmAttribute field in the ep companion descriptor is
+ * updated with the assigned number of streams if it is
+ * different from the original value. To prevent the endpoint
+ * from being returned by a later autoconfig call, claim it by
+ * assigning ep->driver_data to some non-null value.
+ *
+ * On failure, this returns a null endpoint descriptor.
+ */
+struct usb_ep *usb_ep_autoconfig_ss(
+ struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ep_comp
+)
+{
+ struct usb_ep *ep;
+ u8 type;
+
+ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+ /* First, apply chip-specific "best usage" knowledge.
+ * This might make a good usb_gadget_ops hook ...
+ */
+ if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
+ /* ep-e, ep-f are PIO with only 64 byte fifos */
+ ep = find_ep (gadget, "ep-e");
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
+ goto found_ep;
+ ep = find_ep (gadget, "ep-f");
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
+ goto found_ep;
+
+ } else if (gadget_is_goku (gadget)) {
+ if (USB_ENDPOINT_XFER_INT == type) {
+ /* single buffering is enough */
+ ep = find_ep(gadget, "ep3-bulk");
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
+ goto found_ep;
+ } else if (USB_ENDPOINT_XFER_BULK == type
+ && (USB_DIR_IN & desc->bEndpointAddress)) {
+ /* DMA may be available */
+ ep = find_ep(gadget, "ep2-bulk");
+ if (ep && ep_matches(gadget, ep, desc,
+ ep_comp))
+ goto found_ep;
+ }
+
+#ifdef CONFIG_BLACKFIN
+ } else if (gadget_is_musbhdrc(gadget)) {
+ if ((USB_ENDPOINT_XFER_BULK == type) ||
+ (USB_ENDPOINT_XFER_ISOC == type)) {
+ if (USB_DIR_IN & desc->bEndpointAddress)
+ ep = find_ep (gadget, "ep5in");
+ else
+ ep = find_ep (gadget, "ep6out");
+ } else if (USB_ENDPOINT_XFER_INT == type) {
+ if (USB_DIR_IN & desc->bEndpointAddress)
+ ep = find_ep(gadget, "ep1in");
+ else
+ ep = find_ep(gadget, "ep2out");
+ } else
+ ep = NULL;
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
+ goto found_ep;
+#endif
+ }
+
+ /* Second, look at endpoints until an unclaimed one looks usable */
+ list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+ if (ep_matches(gadget, ep, desc, ep_comp))
+ goto found_ep;
+ }
+
+ /* Fail */
+ return NULL;
+found_ep:
+ ep->desc = NULL;
+ ep->comp_desc = NULL;
+ return ep;
+}
+
+/**
+ * usb_ep_autoconfig() - choose an endpoint matching the
+ * descriptor
+ * @gadget: The device to which the endpoint must belong.
+ * @desc: Endpoint descriptor, with endpoint direction and transfer mode
+ * initialized. For periodic transfers, the maximum packet
+ * size must also be initialized. This is modified on success.
+ *
+ * By choosing an endpoint to use with the specified descriptor, this
+ * routine simplifies writing gadget drivers that work with multiple
+ * USB device controllers. The endpoint would be passed later to
+ * usb_ep_enable(), along with some descriptor.
+ *
+ * That second descriptor won't always be the same as the first one.
+ * For example, isochronous endpoints can be autoconfigured for high
+ * bandwidth, and then used in several lower bandwidth altsettings.
+ * Also, high and full speed descriptors will be different.
+ *
+ * Be sure to examine and test the results of autoconfiguration on your
+ * hardware. This code may not make the best choices about how to use the
+ * USB controller, and it can't know all the restrictions that may apply.
+ * Some combinations of driver and hardware won't be able to autoconfigure.
+ *
+ * On success, this returns an un-claimed usb_ep, and modifies the endpoint
+ * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
+ * is initialized as if the endpoint were used at full speed. To prevent
+ * the endpoint from being returned by a later autoconfig call, claim it
+ * by assigning ep->driver_data to some non-null value.
+ *
+ * On failure, this returns a null endpoint descriptor.
+ */
+struct usb_ep *usb_ep_autoconfig(
+ struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc
+)
+{
+ return usb_ep_autoconfig_ss(gadget, desc, NULL);
+}
+
+
+/**
+ * usb_ep_autoconfig_reset - reset endpoint autoconfig state
+ * @gadget: device for which autoconfig state will be reset
+ *
+ * Use this for devices where one configuration may need to assign
+ * endpoint resources very differently from the next one. It clears
+ * state such as ep->driver_data and the record of assigned endpoints
+ * used by usb_ep_autoconfig().
+ */
+void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
+{
+ struct usb_ep *ep;
+
+ list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+ ep->driver_data = NULL;
+ }
+#ifdef MANY_ENDPOINTS
+ in_epnum = 0;
+#endif
+ epnum = 0;
+}
+
diff --git a/drivers/staging/ccg/f_acm.c b/drivers/staging/ccg/f_acm.c
new file mode 100644
index 000000000000..d672250a61fa
--- /dev/null
+++ b/drivers/staging/ccg/f_acm.c
@@ -0,0 +1,814 @@
+/*
+ * f_acm.c -- USB CDC serial (ACM) function driver
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ * Copyright (C) 2009 by Samsung Electronics
+ * Author: Michal Nazarewicz (mina86@mina86.com)
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include "u_serial.h"
+#include "gadget_chips.h"
+
+
+/*
+ * This CDC ACM function support just wraps control functions and
+ * notifications around the generic serial-over-usb code.
+ *
+ * Because CDC ACM is standardized by the USB-IF, many host operating
+ * systems have drivers for it. Accordingly, ACM is the preferred
+ * interop solution for serial-port type connections. The control
+ * models are often not necessary, and in any case don't do much in
+ * this bare-bones implementation.
+ *
+ * Note that even MS-Windows has some support for ACM. However, that
+ * support is somewhat broken because when you use ACM in a composite
+ * device, having multiple interfaces confuses the poor OS. It doesn't
+ * seem to understand CDC Union descriptors. The new "association"
+ * descriptors (roughly equivalent to CDC Unions) may sometimes help.
+ */
+
+struct f_acm {
+ struct gserial port;
+ u8 ctrl_id, data_id;
+ u8 port_num;
+
+ u8 pending;
+
+ /* lock is mostly for pending and notify_req ... they get accessed
+ * by callbacks both from tty (open/close/break) under its spinlock,
+ * and notify_req.complete() which can't use that lock.
+ */
+ spinlock_t lock;
+
+ struct usb_ep *notify;
+ struct usb_request *notify_req;
+
+ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
+
+ /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
+ u16 port_handshake_bits;
+#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */
+#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */
+
+ /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
+ u16 serial_state;
+#define ACM_CTRL_OVERRUN (1 << 6)
+#define ACM_CTRL_PARITY (1 << 5)
+#define ACM_CTRL_FRAMING (1 << 4)
+#define ACM_CTRL_RI (1 << 3)
+#define ACM_CTRL_BRK (1 << 2)
+#define ACM_CTRL_DSR (1 << 1)
+#define ACM_CTRL_DCD (1 << 0)
+};
+
+static inline struct f_acm *func_to_acm(struct usb_function *f)
+{
+ return container_of(f, struct f_acm, port.func);
+}
+
+static inline struct f_acm *port_to_acm(struct gserial *p)
+{
+ return container_of(p, struct f_acm, port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* notification endpoint uses smallish and infrequent fixed-size messages */
+
+#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
+#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */
+
+/* interface and class descriptors: */
+
+static struct usb_interface_assoc_descriptor
+acm_iad_descriptor = {
+ .bLength = sizeof acm_iad_descriptor,
+ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+ /* .bFirstInterface = DYNAMIC, */
+ .bInterfaceCount = 2, // control + data
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
+ .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
+ /* .iFunction = DYNAMIC */
+};
+
+
+static struct usb_interface_descriptor acm_control_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
+ .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
+ /* .iInterface = DYNAMIC */
+};
+
+static struct usb_interface_descriptor acm_data_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ /* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc acm_header_desc = {
+ .bLength = sizeof(acm_header_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+ .bcdCDC = cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor
+acm_call_mgmt_descriptor = {
+ .bLength = sizeof(acm_call_mgmt_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
+ .bmCapabilities = 0,
+ /* .bDataInterface = DYNAMIC */
+};
+
+static struct usb_cdc_acm_descriptor acm_descriptor = {
+ .bLength = sizeof(acm_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_ACM_TYPE,
+ .bmCapabilities = USB_CDC_CAP_LINE,
+};
+
+static struct usb_cdc_union_desc acm_union_desc = {
+ .bLength = sizeof(acm_union_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_UNION_TYPE,
+ /* .bMasterInterface0 = DYNAMIC */
+ /* .bSlaveInterface0 = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor acm_fs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor acm_fs_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor acm_fs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *acm_fs_function[] = {
+ (struct usb_descriptor_header *) &acm_iad_descriptor,
+ (struct usb_descriptor_header *) &acm_control_interface_desc,
+ (struct usb_descriptor_header *) &acm_header_desc,
+ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &acm_union_desc,
+ (struct usb_descriptor_header *) &acm_fs_notify_desc,
+ (struct usb_descriptor_header *) &acm_data_interface_desc,
+ (struct usb_descriptor_header *) &acm_fs_in_desc,
+ (struct usb_descriptor_header *) &acm_fs_out_desc,
+ NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor acm_hs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
+};
+
+static struct usb_endpoint_descriptor acm_hs_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor acm_hs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *acm_hs_function[] = {
+ (struct usb_descriptor_header *) &acm_iad_descriptor,
+ (struct usb_descriptor_header *) &acm_control_interface_desc,
+ (struct usb_descriptor_header *) &acm_header_desc,
+ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &acm_union_desc,
+ (struct usb_descriptor_header *) &acm_hs_notify_desc,
+ (struct usb_descriptor_header *) &acm_data_interface_desc,
+ (struct usb_descriptor_header *) &acm_hs_in_desc,
+ (struct usb_descriptor_header *) &acm_hs_out_desc,
+ NULL,
+};
+
+static struct usb_endpoint_descriptor acm_ss_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor acm_ss_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor acm_ss_bulk_comp_desc = {
+ .bLength = sizeof acm_ss_bulk_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *acm_ss_function[] = {
+ (struct usb_descriptor_header *) &acm_iad_descriptor,
+ (struct usb_descriptor_header *) &acm_control_interface_desc,
+ (struct usb_descriptor_header *) &acm_header_desc,
+ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &acm_union_desc,
+ (struct usb_descriptor_header *) &acm_hs_notify_desc,
+ (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+ (struct usb_descriptor_header *) &acm_data_interface_desc,
+ (struct usb_descriptor_header *) &acm_ss_in_desc,
+ (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+ (struct usb_descriptor_header *) &acm_ss_out_desc,
+ (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+ NULL,
+};
+
+/* string descriptors: */
+
+#define ACM_CTRL_IDX 0
+#define ACM_DATA_IDX 1
+#define ACM_IAD_IDX 2
+
+/* static strings, in UTF-8 */
+static struct usb_string acm_string_defs[] = {
+ [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
+ [ACM_DATA_IDX].s = "CDC ACM Data",
+ [ACM_IAD_IDX ].s = "CDC Serial",
+ { /* ZEROES END LIST */ },
+};
+
+static struct usb_gadget_strings acm_string_table = {
+ .language = 0x0409, /* en-us */
+ .strings = acm_string_defs,
+};
+
+static struct usb_gadget_strings *acm_strings[] = {
+ &acm_string_table,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* ACM control ... data handling is delegated to tty library code.
+ * The main task of this function is to activate and deactivate
+ * that code based on device state; track parameters like line
+ * speed, handshake state, and so on; and issue notifications.
+ */
+
+static void acm_complete_set_line_coding(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct f_acm *acm = ep->driver_data;
+ struct usb_composite_dev *cdev = acm->port.func.config->cdev;
+
+ if (req->status != 0) {
+ DBG(cdev, "acm ttyGS%d completion, err %d\n",
+ acm->port_num, req->status);
+ return;
+ }
+
+ /* normal completion */
+ if (req->actual != sizeof(acm->port_line_coding)) {
+ DBG(cdev, "acm ttyGS%d short resp, len %d\n",
+ acm->port_num, req->actual);
+ usb_ep_set_halt(ep);
+ } else {
+ struct usb_cdc_line_coding *value = req->buf;
+
+ /* REVISIT: we currently just remember this data.
+ * If we change that, (a) validate it first, then
+ * (b) update whatever hardware needs updating,
+ * (c) worry about locking. This is information on
+ * the order of 9600-8-N-1 ... most of which means
+ * nothing unless we control a real RS232 line.
+ */
+ acm->port_line_coding = *value;
+ }
+}
+
+static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct f_acm *acm = func_to_acm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* composite driver infrastructure handles everything except
+ * CDC class messages; interface activation uses set_alt().
+ *
+ * Note CDC spec table 4 lists the ACM request profile. It requires
+ * encapsulated command support ... we don't handle any, and respond
+ * to them by stalling. Options include get/set/clear comm features
+ * (not that useful) and SEND_BREAK.
+ */
+ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+ /* SET_LINE_CODING ... just read and save what the host sends */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_SET_LINE_CODING:
+ if (w_length != sizeof(struct usb_cdc_line_coding)
+ || w_index != acm->ctrl_id)
+ goto invalid;
+
+ value = w_length;
+ cdev->gadget->ep0->driver_data = acm;
+ req->complete = acm_complete_set_line_coding;
+ break;
+
+ /* GET_LINE_CODING ... return what host sent, or initial value */
+ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_GET_LINE_CODING:
+ if (w_index != acm->ctrl_id)
+ goto invalid;
+
+ value = min_t(unsigned, w_length,
+ sizeof(struct usb_cdc_line_coding));
+ memcpy(req->buf, &acm->port_line_coding, value);
+ break;
+
+ /* SET_CONTROL_LINE_STATE ... save what the host sent */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+ if (w_index != acm->ctrl_id)
+ goto invalid;
+
+ value = 0;
+
+ /* FIXME we should not allow data to flow until the
+ * host sets the ACM_CTRL_DTR bit; and when it clears
+ * that bit, we should return to that no-flow state.
+ */
+ acm->port_handshake_bits = w_value;
+ break;
+
+ default:
+invalid:
+ VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
+ acm->port_num, ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = 0;
+ req->length = value;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ ERROR(cdev, "acm response on ttyGS%d, err %d\n",
+ acm->port_num, value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_acm *acm = func_to_acm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ /* we know alt == 0, so this is an activation or a reset */
+
+ if (intf == acm->ctrl_id) {
+ if (acm->notify->driver_data) {
+ VDBG(cdev, "reset acm control interface %d\n", intf);
+ usb_ep_disable(acm->notify);
+ } else {
+ VDBG(cdev, "init acm ctrl interface %d\n", intf);
+ if (config_ep_by_speed(cdev->gadget, f, acm->notify))
+ return -EINVAL;
+ }
+ usb_ep_enable(acm->notify);
+ acm->notify->driver_data = acm;
+
+ } else if (intf == acm->data_id) {
+ if (acm->port.in->driver_data) {
+ DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
+ gserial_disconnect(&acm->port);
+ }
+ if (!acm->port.in->desc || !acm->port.out->desc) {
+ DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
+ if (config_ep_by_speed(cdev->gadget, f,
+ acm->port.in) ||
+ config_ep_by_speed(cdev->gadget, f,
+ acm->port.out)) {
+ acm->port.in->desc = NULL;
+ acm->port.out->desc = NULL;
+ return -EINVAL;
+ }
+ }
+ gserial_connect(&acm->port, acm->port_num);
+
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static void acm_disable(struct usb_function *f)
+{
+ struct f_acm *acm = func_to_acm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
+ gserial_disconnect(&acm->port);
+ usb_ep_disable(acm->notify);
+ acm->notify->driver_data = NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * acm_cdc_notify - issue CDC notification to host
+ * @acm: wraps host to be notified
+ * @type: notification type
+ * @value: Refer to cdc specs, wValue field.
+ * @data: data to be sent
+ * @length: size of data
+ * Context: irqs blocked, acm->lock held, acm_notify_req non-null
+ *
+ * Returns zero on success or a negative errno.
+ *
+ * See section 6.3.5 of the CDC 1.1 specification for information
+ * about the only notification we issue: SerialState change.
+ */
+static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
+ void *data, unsigned length)
+{
+ struct usb_ep *ep = acm->notify;
+ struct usb_request *req;
+ struct usb_cdc_notification *notify;
+ const unsigned len = sizeof(*notify) + length;
+ void *buf;
+ int status;
+
+ req = acm->notify_req;
+ acm->notify_req = NULL;
+ acm->pending = false;
+
+ req->length = len;
+ notify = req->buf;
+ buf = notify + 1;
+
+ notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+ | USB_RECIP_INTERFACE;
+ notify->bNotificationType = type;
+ notify->wValue = cpu_to_le16(value);
+ notify->wIndex = cpu_to_le16(acm->ctrl_id);
+ notify->wLength = cpu_to_le16(length);
+ memcpy(buf, data, length);
+
+ /* ep_queue() can complete immediately if it fills the fifo... */
+ spin_unlock(&acm->lock);
+ status = usb_ep_queue(ep, req, GFP_ATOMIC);
+ spin_lock(&acm->lock);
+
+ if (status < 0) {
+ ERROR(acm->port.func.config->cdev,
+ "acm ttyGS%d can't notify serial state, %d\n",
+ acm->port_num, status);
+ acm->notify_req = req;
+ }
+
+ return status;
+}
+
+static int acm_notify_serial_state(struct f_acm *acm)
+{
+ struct usb_composite_dev *cdev = acm->port.func.config->cdev;
+ int status;
+
+ spin_lock(&acm->lock);
+ if (acm->notify_req) {
+ DBG(cdev, "acm ttyGS%d serial state %04x\n",
+ acm->port_num, acm->serial_state);
+ status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
+ 0, &acm->serial_state, sizeof(acm->serial_state));
+ } else {
+ acm->pending = true;
+ status = 0;
+ }
+ spin_unlock(&acm->lock);
+ return status;
+}
+
+static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_acm *acm = req->context;
+ u8 doit = false;
+
+ /* on this call path we do NOT hold the port spinlock,
+ * which is why ACM needs its own spinlock
+ */
+ spin_lock(&acm->lock);
+ if (req->status != -ESHUTDOWN)
+ doit = acm->pending;
+ acm->notify_req = req;
+ spin_unlock(&acm->lock);
+
+ if (doit)
+ acm_notify_serial_state(acm);
+}
+
+/* connect == the TTY link is open */
+
+static void acm_connect(struct gserial *port)
+{
+ struct f_acm *acm = port_to_acm(port);
+
+ acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
+ acm_notify_serial_state(acm);
+}
+
+static void acm_disconnect(struct gserial *port)
+{
+ struct f_acm *acm = port_to_acm(port);
+
+ acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
+ acm_notify_serial_state(acm);
+}
+
+static int acm_send_break(struct gserial *port, int duration)
+{
+ struct f_acm *acm = port_to_acm(port);
+ u16 state;
+
+ state = acm->serial_state;
+ state &= ~ACM_CTRL_BRK;
+ if (duration)
+ state |= ACM_CTRL_BRK;
+
+ acm->serial_state = state;
+ return acm_notify_serial_state(acm);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ACM function driver setup/binding */
+static int
+acm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_acm *acm = func_to_acm(f);
+ int status;
+ struct usb_ep *ep;
+
+ /* allocate instance-specific interface IDs, and patch descriptors */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ acm->ctrl_id = status;
+ acm_iad_descriptor.bFirstInterface = status;
+
+ acm_control_interface_desc.bInterfaceNumber = status;
+ acm_union_desc .bMasterInterface0 = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ acm->data_id = status;
+
+ acm_data_interface_desc.bInterfaceNumber = status;
+ acm_union_desc.bSlaveInterface0 = status;
+ acm_call_mgmt_descriptor.bDataInterface = status;
+
+ status = -ENODEV;
+
+ /* allocate instance-specific endpoints */
+ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
+ if (!ep)
+ goto fail;
+ acm->port.in = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
+ if (!ep)
+ goto fail;
+ acm->port.out = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
+ if (!ep)
+ goto fail;
+ acm->notify = ep;
+ ep->driver_data = cdev; /* claim */
+
+ /* allocate notification */
+ acm->notify_req = gs_alloc_req(ep,
+ sizeof(struct usb_cdc_notification) + 2,
+ GFP_KERNEL);
+ if (!acm->notify_req)
+ goto fail;
+
+ acm->notify_req->complete = acm_cdc_notify_complete;
+ acm->notify_req->context = acm;
+
+ /* copy descriptors */
+ f->descriptors = usb_copy_descriptors(acm_fs_function);
+ if (!f->descriptors)
+ goto fail;
+
+ /* support all relevant hardware speeds... we expect that when
+ * hardware is dual speed, all bulk-capable endpoints work at
+ * both speeds
+ */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ acm_hs_in_desc.bEndpointAddress =
+ acm_fs_in_desc.bEndpointAddress;
+ acm_hs_out_desc.bEndpointAddress =
+ acm_fs_out_desc.bEndpointAddress;
+ acm_hs_notify_desc.bEndpointAddress =
+ acm_fs_notify_desc.bEndpointAddress;
+
+ /* copy descriptors */
+ f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
+ }
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ acm_ss_in_desc.bEndpointAddress =
+ acm_fs_in_desc.bEndpointAddress;
+ acm_ss_out_desc.bEndpointAddress =
+ acm_fs_out_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->ss_descriptors = usb_copy_descriptors(acm_ss_function);
+ if (!f->ss_descriptors)
+ goto fail;
+ }
+
+ DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ acm->port_num,
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ acm->port.in->name, acm->port.out->name,
+ acm->notify->name);
+ return 0;
+
+fail:
+ if (acm->notify_req)
+ gs_free_req(acm->notify, acm->notify_req);
+
+ /* we might as well release our claims on endpoints */
+ if (acm->notify)
+ acm->notify->driver_data = NULL;
+ if (acm->port.out)
+ acm->port.out->driver_data = NULL;
+ if (acm->port.in)
+ acm->port.in->driver_data = NULL;
+
+ ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
+
+ return status;
+}
+
+static void
+acm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_acm *acm = func_to_acm(f);
+
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ usb_free_descriptors(f->hs_descriptors);
+ if (gadget_is_superspeed(c->cdev->gadget))
+ usb_free_descriptors(f->ss_descriptors);
+ usb_free_descriptors(f->descriptors);
+ gs_free_req(acm->notify, acm->notify_req);
+ kfree(acm);
+}
+
+/* Some controllers can't support CDC ACM ... */
+static inline bool can_support_cdc(struct usb_configuration *c)
+{
+ /* everything else is *probably* fine ... */
+ return true;
+}
+
+/**
+ * acm_bind_config - add a CDC ACM function to a configuration
+ * @c: the configuration to support the CDC ACM instance
+ * @port_num: /dev/ttyGS* port this interface will use
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ *
+ * Caller must have called @gserial_setup() with enough ports to
+ * handle all the ones it binds. Caller is also responsible
+ * for calling @gserial_cleanup() before module unload.
+ */
+int acm_bind_config(struct usb_configuration *c, u8 port_num)
+{
+ struct f_acm *acm;
+ int status;
+
+ if (!can_support_cdc(c))
+ return -EINVAL;
+
+ /* REVISIT might want instance-specific strings to help
+ * distinguish instances ...
+ */
+
+ /* maybe allocate device-global string IDs, and patch descriptors */
+ if (acm_string_defs[ACM_CTRL_IDX].id == 0) {
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ acm_string_defs[ACM_CTRL_IDX].id = status;
+
+ acm_control_interface_desc.iInterface = status;
+
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ acm_string_defs[ACM_DATA_IDX].id = status;
+
+ acm_data_interface_desc.iInterface = status;
+
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ acm_string_defs[ACM_IAD_IDX].id = status;
+
+ acm_iad_descriptor.iFunction = status;
+ }
+
+ /* allocate and initialize one new instance */
+ acm = kzalloc(sizeof *acm, GFP_KERNEL);
+ if (!acm)
+ return -ENOMEM;
+
+ spin_lock_init(&acm->lock);
+
+ acm->port_num = port_num;
+
+ acm->port.connect = acm_connect;
+ acm->port.disconnect = acm_disconnect;
+ acm->port.send_break = acm_send_break;
+
+ acm->port.func.name = "acm";
+ acm->port.func.strings = acm_strings;
+ /* descriptors are per-instance copies */
+ acm->port.func.bind = acm_bind;
+ acm->port.func.unbind = acm_unbind;
+ acm->port.func.set_alt = acm_set_alt;
+ acm->port.func.setup = acm_setup;
+ acm->port.func.disable = acm_disable;
+
+ status = usb_add_function(c, &acm->port.func);
+ if (status)
+ kfree(acm);
+ return status;
+}
diff --git a/drivers/staging/ccg/f_fs.c b/drivers/staging/ccg/f_fs.c
new file mode 100644
index 000000000000..8adc79d1b402
--- /dev/null
+++ b/drivers/staging/ccg/f_fs.c
@@ -0,0 +1,2455 @@
+/*
+ * f_fs.c -- user mode file system API for USB composite function controllers
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Author: Michal Nazarewicz <mina86@mina86.com>
+ *
+ * Based on inode.c (GadgetFS) which was:
+ * Copyright (C) 2003-2004 David Brownell
+ * Copyright (C) 2003 Agilent Technologies
+ *
+ * This program is free software; 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 DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/blkdev.h>
+#include <linux/pagemap.h>
+#include <linux/export.h>
+#include <linux/hid.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/functionfs.h>
+
+
+#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
+
+
+/* Debugging ****************************************************************/
+
+#ifdef VERBOSE_DEBUG
+# define pr_vdebug pr_debug
+# define ffs_dump_mem(prefix, ptr, len) \
+ print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
+#else
+# define pr_vdebug(...) do { } while (0)
+# define ffs_dump_mem(prefix, ptr, len) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define ENTER() pr_vdebug("%s()\n", __func__)
+
+
+/* The data structure and setup file ****************************************/
+
+enum ffs_state {
+ /*
+ * Waiting for descriptors and strings.
+ *
+ * In this state no open(2), read(2) or write(2) on epfiles
+ * may succeed (which should not be the problem as there
+ * should be no such files opened in the first place).
+ */
+ FFS_READ_DESCRIPTORS,
+ FFS_READ_STRINGS,
+
+ /*
+ * We've got descriptors and strings. We are or have called
+ * functionfs_ready_callback(). functionfs_bind() may have
+ * been called but we don't know.
+ *
+ * This is the only state in which operations on epfiles may
+ * succeed.
+ */
+ FFS_ACTIVE,
+
+ /*
+ * All endpoints have been closed. This state is also set if
+ * we encounter an unrecoverable error. The only
+ * unrecoverable error is situation when after reading strings
+ * from user space we fail to initialise epfiles or
+ * functionfs_ready_callback() returns with error (<0).
+ *
+ * In this state no open(2), read(2) or write(2) (both on ep0
+ * as well as epfile) may succeed (at this point epfiles are
+ * unlinked and all closed so this is not a problem; ep0 is
+ * also closed but ep0 file exists and so open(2) on ep0 must
+ * fail).
+ */
+ FFS_CLOSING
+};
+
+
+enum ffs_setup_state {
+ /* There is no setup request pending. */
+ FFS_NO_SETUP,
+ /*
+ * User has read events and there was a setup request event
+ * there. The next read/write on ep0 will handle the
+ * request.
+ */
+ FFS_SETUP_PENDING,
+ /*
+ * There was event pending but before user space handled it
+ * some other event was introduced which canceled existing
+ * setup. If this state is set read/write on ep0 return
+ * -EIDRM. This state is only set when adding event.
+ */
+ FFS_SETUP_CANCELED
+};
+
+
+
+struct ffs_epfile;
+struct ffs_function;
+
+struct ffs_data {
+ struct usb_gadget *gadget;
+
+ /*
+ * Protect access read/write operations, only one read/write
+ * at a time. As a consequence protects ep0req and company.
+ * While setup request is being processed (queued) this is
+ * held.
+ */
+ struct mutex mutex;
+
+ /*
+ * Protect access to endpoint related structures (basically
+ * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
+ * endpoint zero.
+ */
+ spinlock_t eps_lock;
+
+ /*
+ * XXX REVISIT do we need our own request? Since we are not
+ * handling setup requests immediately user space may be so
+ * slow that another setup will be sent to the gadget but this
+ * time not to us but another function and then there could be
+ * a race. Is that the case? Or maybe we can use cdev->req
+ * after all, maybe we just need some spinlock for that?
+ */
+ struct usb_request *ep0req; /* P: mutex */
+ struct completion ep0req_completion; /* P: mutex */
+ int ep0req_status; /* P: mutex */
+
+ /* reference counter */
+ atomic_t ref;
+ /* how many files are opened (EP0 and others) */
+ atomic_t opened;
+
+ /* EP0 state */
+ enum ffs_state state;
+
+ /*
+ * Possible transitions:
+ * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
+ * happens only in ep0 read which is P: mutex
+ * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
+ * happens only in ep0 i/o which is P: mutex
+ * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock
+ * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg
+ */
+ enum ffs_setup_state setup_state;
+
+#define FFS_SETUP_STATE(ffs) \
+ ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \
+ FFS_SETUP_CANCELED, FFS_NO_SETUP))
+
+ /* Events & such. */
+ struct {
+ u8 types[4];
+ unsigned short count;
+ /* XXX REVISIT need to update it in some places, or do we? */
+ unsigned short can_stall;
+ struct usb_ctrlrequest setup;
+
+ wait_queue_head_t waitq;
+ } ev; /* the whole structure, P: ev.waitq.lock */
+
+ /* Flags */
+ unsigned long flags;
+#define FFS_FL_CALL_CLOSED_CALLBACK 0
+#define FFS_FL_BOUND 1
+
+ /* Active function */
+ struct ffs_function *func;
+
+ /*
+ * Device name, write once when file system is mounted.
+ * Intended for user to read if she wants.
+ */
+ const char *dev_name;
+ /* Private data for our user (ie. gadget). Managed by user. */
+ void *private_data;
+
+ /* filled by __ffs_data_got_descs() */
+ /*
+ * Real descriptors are 16 bytes after raw_descs (so you need
+ * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
+ * first full speed descriptor). raw_descs_length and
+ * raw_fs_descs_length do not have those 16 bytes added.
+ */
+ const void *raw_descs;
+ unsigned raw_descs_length;
+ unsigned raw_fs_descs_length;
+ unsigned fs_descs_count;
+ unsigned hs_descs_count;
+
+ unsigned short strings_count;
+ unsigned short interfaces_count;
+ unsigned short eps_count;
+ unsigned short _pad1;
+
+ /* filled by __ffs_data_got_strings() */
+ /* ids in stringtabs are set in functionfs_bind() */
+ const void *raw_strings;
+ struct usb_gadget_strings **stringtabs;
+
+ /*
+ * File system's super block, write once when file system is
+ * mounted.
+ */
+ struct super_block *sb;
+
+ /* File permissions, written once when fs is mounted */
+ struct ffs_file_perms {
+ umode_t mode;
+ uid_t uid;
+ gid_t gid;
+ } file_perms;
+
+ /*
+ * The endpoint files, filled by ffs_epfiles_create(),
+ * destroyed by ffs_epfiles_destroy().
+ */
+ struct ffs_epfile *epfiles;
+};
+
+/* Reference counter handling */
+static void ffs_data_get(struct ffs_data *ffs);
+static void ffs_data_put(struct ffs_data *ffs);
+/* Creates new ffs_data object. */
+static struct ffs_data *__must_check ffs_data_new(void) __attribute__((malloc));
+
+/* Opened counter handling. */
+static void ffs_data_opened(struct ffs_data *ffs);
+static void ffs_data_closed(struct ffs_data *ffs);
+
+/* Called with ffs->mutex held; take over ownership of data. */
+static int __must_check
+__ffs_data_got_descs(struct ffs_data *ffs, char *data, size_t len);
+static int __must_check
+__ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len);
+
+
+/* The function structure ***************************************************/
+
+struct ffs_ep;
+
+struct ffs_function {
+ struct usb_configuration *conf;
+ struct usb_gadget *gadget;
+ struct ffs_data *ffs;
+
+ struct ffs_ep *eps;
+ u8 eps_revmap[16];
+ short *interfaces_nums;
+
+ struct usb_function function;
+};
+
+
+static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
+{
+ return container_of(f, struct ffs_function, function);
+}
+
+static void ffs_func_free(struct ffs_function *func);
+
+static void ffs_func_eps_disable(struct ffs_function *func);
+static int __must_check ffs_func_eps_enable(struct ffs_function *func);
+
+static int ffs_func_bind(struct usb_configuration *,
+ struct usb_function *);
+static void ffs_func_unbind(struct usb_configuration *,
+ struct usb_function *);
+static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
+static void ffs_func_disable(struct usb_function *);
+static int ffs_func_setup(struct usb_function *,
+ const struct usb_ctrlrequest *);
+static void ffs_func_suspend(struct usb_function *);
+static void ffs_func_resume(struct usb_function *);
+
+
+static int ffs_func_revmap_ep(struct ffs_function *func, u8 num);
+static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf);
+
+
+/* The endpoints structures *************************************************/
+
+struct ffs_ep {
+ struct usb_ep *ep; /* P: ffs->eps_lock */
+ struct usb_request *req; /* P: epfile->mutex */
+
+ /* [0]: full speed, [1]: high speed */
+ struct usb_endpoint_descriptor *descs[2];
+
+ u8 num;
+
+ int status; /* P: epfile->mutex */
+};
+
+struct ffs_epfile {
+ /* Protects ep->ep and ep->req. */
+ struct mutex mutex;
+ wait_queue_head_t wait;
+
+ struct ffs_data *ffs;
+ struct ffs_ep *ep; /* P: ffs->eps_lock */
+
+ struct dentry *dentry;
+
+ char name[5];
+
+ unsigned char in; /* P: ffs->eps_lock */
+ unsigned char isoc; /* P: ffs->eps_lock */
+
+ unsigned char _pad;
+};
+
+static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
+static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
+
+static struct inode *__must_check
+ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
+ const struct file_operations *fops,
+ struct dentry **dentry_p);
+
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+ __attribute__((warn_unused_result, nonnull));
+static char *ffs_prepare_buffer(const char * __user buf, size_t len)
+ __attribute__((warn_unused_result, nonnull));
+
+
+/* Control file aka ep0 *****************************************************/
+
+static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct ffs_data *ffs = req->context;
+
+ complete_all(&ffs->ep0req_completion);
+}
+
+static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
+{
+ struct usb_request *req = ffs->ep0req;
+ int ret;
+
+ req->zero = len < le16_to_cpu(ffs->ev.setup.wLength);
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+
+ req->buf = data;
+ req->length = len;
+
+ /*
+ * UDC layer requires to provide a buffer even for ZLP, but should
+ * not use it at all. Let's provide some poisoned pointer to catch
+ * possible bug in the driver.
+ */
+ if (req->buf == NULL)
+ req->buf = (void *)0xDEADBABE;
+
+ INIT_COMPLETION(ffs->ep0req_completion);
+
+ ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
+ if (unlikely(ret < 0))
+ return ret;
+
+ ret = wait_for_completion_interruptible(&ffs->ep0req_completion);
+ if (unlikely(ret)) {
+ usb_ep_dequeue(ffs->gadget->ep0, req);
+ return -EINTR;
+ }
+
+ ffs->setup_state = FFS_NO_SETUP;
+ return ffs->ep0req_status;
+}
+
+static int __ffs_ep0_stall(struct ffs_data *ffs)
+{
+ if (ffs->ev.can_stall) {
+ pr_vdebug("ep0 stall\n");
+ usb_ep_set_halt(ffs->gadget->ep0);
+ ffs->setup_state = FFS_NO_SETUP;
+ return -EL2HLT;
+ } else {
+ pr_debug("bogus ep0 stall!\n");
+ return -ESRCH;
+ }
+}
+
+static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ptr)
+{
+ struct ffs_data *ffs = file->private_data;
+ ssize_t ret;
+ char *data;
+
+ ENTER();
+
+ /* Fast check if setup was canceled */
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+ return -EIDRM;
+
+ /* Acquire mutex */
+ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+ if (unlikely(ret < 0))
+ return ret;
+
+ /* Check state */
+ switch (ffs->state) {
+ case FFS_READ_DESCRIPTORS:
+ case FFS_READ_STRINGS:
+ /* Copy data */
+ if (unlikely(len < 16)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ data = ffs_prepare_buffer(buf, len);
+ if (IS_ERR(data)) {
+ ret = PTR_ERR(data);
+ break;
+ }
+
+ /* Handle data */
+ if (ffs->state == FFS_READ_DESCRIPTORS) {
+ pr_info("read descriptors\n");
+ ret = __ffs_data_got_descs(ffs, data, len);
+ if (unlikely(ret < 0))
+ break;
+
+ ffs->state = FFS_READ_STRINGS;
+ ret = len;
+ } else {
+ pr_info("read strings\n");
+ ret = __ffs_data_got_strings(ffs, data, len);
+ if (unlikely(ret < 0))
+ break;
+
+ ret = ffs_epfiles_create(ffs);
+ if (unlikely(ret)) {
+ ffs->state = FFS_CLOSING;
+ break;
+ }
+
+ ffs->state = FFS_ACTIVE;
+ mutex_unlock(&ffs->mutex);
+
+ ret = functionfs_ready_callback(ffs);
+ if (unlikely(ret < 0)) {
+ ffs->state = FFS_CLOSING;
+ return ret;
+ }
+
+ set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
+ return len;
+ }
+ break;
+
+ case FFS_ACTIVE:
+ data = NULL;
+ /*
+ * We're called from user space, we can use _irq
+ * rather then _irqsave
+ */
+ spin_lock_irq(&ffs->ev.waitq.lock);
+ switch (FFS_SETUP_STATE(ffs)) {
+ case FFS_SETUP_CANCELED:
+ ret = -EIDRM;
+ goto done_spin;
+
+ case FFS_NO_SETUP:
+ ret = -ESRCH;
+ goto done_spin;
+
+ case FFS_SETUP_PENDING:
+ break;
+ }
+
+ /* FFS_SETUP_PENDING */
+ if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ ret = __ffs_ep0_stall(ffs);
+ break;
+ }
+
+ /* FFS_SETUP_PENDING and not stall */
+ len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+
+ data = ffs_prepare_buffer(buf, len);
+ if (IS_ERR(data)) {
+ ret = PTR_ERR(data);
+ break;
+ }
+
+ spin_lock_irq(&ffs->ev.waitq.lock);
+
+ /*
+ * We are guaranteed to be still in FFS_ACTIVE state
+ * but the state of setup could have changed from
+ * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
+ * to check for that. If that happened we copied data
+ * from user space in vain but it's unlikely.
+ *
+ * For sure we are not in FFS_NO_SETUP since this is
+ * the only place FFS_SETUP_PENDING -> FFS_NO_SETUP
+ * transition can be performed and it's protected by
+ * mutex.
+ */
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+ ret = -EIDRM;
+done_spin:
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ } else {
+ /* unlocks spinlock */
+ ret = __ffs_ep0_queue_wait(ffs, data, len);
+ }
+ kfree(data);
+ break;
+
+ default:
+ ret = -EBADFD;
+ break;
+ }
+
+ mutex_unlock(&ffs->mutex);
+ return ret;
+}
+
+static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
+ size_t n)
+{
+ /*
+ * We are holding ffs->ev.waitq.lock and ffs->mutex and we need
+ * to release them.
+ */
+ struct usb_functionfs_event events[n];
+ unsigned i = 0;
+
+ memset(events, 0, sizeof events);
+
+ do {
+ events[i].type = ffs->ev.types[i];
+ if (events[i].type == FUNCTIONFS_SETUP) {
+ events[i].u.setup = ffs->ev.setup;
+ ffs->setup_state = FFS_SETUP_PENDING;
+ }
+ } while (++i < n);
+
+ if (n < ffs->ev.count) {
+ ffs->ev.count -= n;
+ memmove(ffs->ev.types, ffs->ev.types + n,
+ ffs->ev.count * sizeof *ffs->ev.types);
+ } else {
+ ffs->ev.count = 0;
+ }
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ mutex_unlock(&ffs->mutex);
+
+ return unlikely(__copy_to_user(buf, events, sizeof events))
+ ? -EFAULT : sizeof events;
+}
+
+static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ptr)
+{
+ struct ffs_data *ffs = file->private_data;
+ char *data = NULL;
+ size_t n;
+ int ret;
+
+ ENTER();
+
+ /* Fast check if setup was canceled */
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+ return -EIDRM;
+
+ /* Acquire mutex */
+ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+ if (unlikely(ret < 0))
+ return ret;
+
+ /* Check state */
+ if (ffs->state != FFS_ACTIVE) {
+ ret = -EBADFD;
+ goto done_mutex;
+ }
+
+ /*
+ * We're called from user space, we can use _irq rather then
+ * _irqsave
+ */
+ spin_lock_irq(&ffs->ev.waitq.lock);
+
+ switch (FFS_SETUP_STATE(ffs)) {
+ case FFS_SETUP_CANCELED:
+ ret = -EIDRM;
+ break;
+
+ case FFS_NO_SETUP:
+ n = len / sizeof(struct usb_functionfs_event);
+ if (unlikely(!n)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq,
+ ffs->ev.count)) {
+ ret = -EINTR;
+ break;
+ }
+
+ return __ffs_ep0_read_events(ffs, buf,
+ min(n, (size_t)ffs->ev.count));
+
+ case FFS_SETUP_PENDING:
+ if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ ret = __ffs_ep0_stall(ffs);
+ goto done_mutex;
+ }
+
+ len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+
+ if (likely(len)) {
+ data = kmalloc(len, GFP_KERNEL);
+ if (unlikely(!data)) {
+ ret = -ENOMEM;
+ goto done_mutex;
+ }
+ }
+
+ spin_lock_irq(&ffs->ev.waitq.lock);
+
+ /* See ffs_ep0_write() */
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+ ret = -EIDRM;
+ break;
+ }
+
+ /* unlocks spinlock */
+ ret = __ffs_ep0_queue_wait(ffs, data, len);
+ if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len)))
+ ret = -EFAULT;
+ goto done_mutex;
+
+ default:
+ ret = -EBADFD;
+ break;
+ }
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+done_mutex:
+ mutex_unlock(&ffs->mutex);
+ kfree(data);
+ return ret;
+}
+
+static int ffs_ep0_open(struct inode *inode, struct file *file)
+{
+ struct ffs_data *ffs = inode->i_private;
+
+ ENTER();
+
+ if (unlikely(ffs->state == FFS_CLOSING))
+ return -EBUSY;
+
+ file->private_data = ffs;
+ ffs_data_opened(ffs);
+
+ return 0;
+}
+
+static int ffs_ep0_release(struct inode *inode, struct file *file)
+{
+ struct ffs_data *ffs = file->private_data;
+
+ ENTER();
+
+ ffs_data_closed(ffs);
+
+ return 0;
+}
+
+static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
+{
+ struct ffs_data *ffs = file->private_data;
+ struct usb_gadget *gadget = ffs->gadget;
+ long ret;
+
+ ENTER();
+
+ if (code == FUNCTIONFS_INTERFACE_REVMAP) {
+ struct ffs_function *func = ffs->func;
+ ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
+ } else if (gadget && gadget->ops->ioctl) {
+ ret = gadget->ops->ioctl(gadget, code, value);
+ } else {
+ ret = -ENOTTY;
+ }
+
+ return ret;
+}
+
+static const struct file_operations ffs_ep0_operations = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+
+ .open = ffs_ep0_open,
+ .write = ffs_ep0_write,
+ .read = ffs_ep0_read,
+ .release = ffs_ep0_release,
+ .unlocked_ioctl = ffs_ep0_ioctl,
+};
+
+
+/* "Normal" endpoints operations ********************************************/
+
+static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
+{
+ ENTER();
+ if (likely(req->context)) {
+ struct ffs_ep *ep = _ep->driver_data;
+ ep->status = req->status ? req->status : req->actual;
+ complete(req->context);
+ }
+}
+
+static ssize_t ffs_epfile_io(struct file *file,
+ char __user *buf, size_t len, int read)
+{
+ struct ffs_epfile *epfile = file->private_data;
+ struct ffs_ep *ep;
+ char *data = NULL;
+ ssize_t ret;
+ int halt;
+
+ goto first_try;
+ do {
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ mutex_unlock(&epfile->mutex);
+
+first_try:
+ /* Are we still active? */
+ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ /* Wait for endpoint to be enabled */
+ ep = epfile->ep;
+ if (!ep) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ goto error;
+ }
+
+ if (wait_event_interruptible(epfile->wait,
+ (ep = epfile->ep))) {
+ ret = -EINTR;
+ goto error;
+ }
+ }
+
+ /* Do we halt? */
+ halt = !read == !epfile->in;
+ if (halt && epfile->isoc) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Allocate & copy */
+ if (!halt && !data) {
+ data = kzalloc(len, GFP_KERNEL);
+ if (unlikely(!data))
+ return -ENOMEM;
+
+ if (!read &&
+ unlikely(__copy_from_user(data, buf, len))) {
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+
+ /* We will be using request */
+ ret = ffs_mutex_lock(&epfile->mutex,
+ file->f_flags & O_NONBLOCK);
+ if (unlikely(ret))
+ goto error;
+
+ /*
+ * We're called from user space, we can use _irq rather then
+ * _irqsave
+ */
+ spin_lock_irq(&epfile->ffs->eps_lock);
+
+ /*
+ * While we were acquiring mutex endpoint got disabled
+ * or changed?
+ */
+ } while (unlikely(epfile->ep != ep));
+
+ /* Halt */
+ if (unlikely(halt)) {
+ if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep))
+ usb_ep_set_halt(ep->ep);
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ ret = -EBADMSG;
+ } else {
+ /* Fire the request */
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ struct usb_request *req = ep->req;
+ req->context = &done;
+ req->complete = ffs_epfile_io_complete;
+ req->buf = data;
+ req->length = len;
+
+ ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+
+ if (unlikely(ret < 0)) {
+ /* nop */
+ } else if (unlikely(wait_for_completion_interruptible(&done))) {
+ ret = -EINTR;
+ usb_ep_dequeue(ep->ep, req);
+ } else {
+ ret = ep->status;
+ if (read && ret > 0 &&
+ unlikely(copy_to_user(buf, data, ret)))
+ ret = -EFAULT;
+ }
+ }
+
+ mutex_unlock(&epfile->mutex);
+error:
+ kfree(data);
+ return ret;
+}
+
+static ssize_t
+ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
+ loff_t *ptr)
+{
+ ENTER();
+
+ return ffs_epfile_io(file, (char __user *)buf, len, 0);
+}
+
+static ssize_t
+ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
+{
+ ENTER();
+
+ return ffs_epfile_io(file, buf, len, 1);
+}
+
+static int
+ffs_epfile_open(struct inode *inode, struct file *file)
+{
+ struct ffs_epfile *epfile = inode->i_private;
+
+ ENTER();
+
+ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+ return -ENODEV;
+
+ file->private_data = epfile;
+ ffs_data_opened(epfile->ffs);
+
+ return 0;
+}
+
+static int
+ffs_epfile_release(struct inode *inode, struct file *file)
+{
+ struct ffs_epfile *epfile = inode->i_private;
+
+ ENTER();
+
+ ffs_data_closed(epfile->ffs);
+
+ return 0;
+}
+
+static long ffs_epfile_ioctl(struct file *file, unsigned code,
+ unsigned long value)
+{
+ struct ffs_epfile *epfile = file->private_data;
+ int ret;
+
+ ENTER();
+
+ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+ return -ENODEV;
+
+ spin_lock_irq(&epfile->ffs->eps_lock);
+ if (likely(epfile->ep)) {
+ switch (code) {
+ case FUNCTIONFS_FIFO_STATUS:
+ ret = usb_ep_fifo_status(epfile->ep->ep);
+ break;
+ case FUNCTIONFS_FIFO_FLUSH:
+ usb_ep_fifo_flush(epfile->ep->ep);
+ ret = 0;
+ break;
+ case FUNCTIONFS_CLEAR_HALT:
+ ret = usb_ep_clear_halt(epfile->ep->ep);
+ break;
+ case FUNCTIONFS_ENDPOINT_REVMAP:
+ ret = epfile->ep->num;
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+ } else {
+ ret = -ENODEV;
+ }
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+
+ return ret;
+}
+
+static const struct file_operations ffs_epfile_operations = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+
+ .open = ffs_epfile_open,
+ .write = ffs_epfile_write,
+ .read = ffs_epfile_read,
+ .release = ffs_epfile_release,
+ .unlocked_ioctl = ffs_epfile_ioctl,
+};
+
+
+/* File system and super block operations ***********************************/
+
+/*
+ * Mounting the file system creates a controller file, used first for
+ * function configuration then later for event monitoring.
+ */
+
+static struct inode *__must_check
+ffs_sb_make_inode(struct super_block *sb, void *data,
+ const struct file_operations *fops,
+ const struct inode_operations *iops,
+ struct ffs_file_perms *perms)
+{
+ struct inode *inode;
+
+ ENTER();
+
+ inode = new_inode(sb);
+
+ if (likely(inode)) {
+ struct timespec current_time = CURRENT_TIME;
+
+ inode->i_ino = get_next_ino();
+ inode->i_mode = perms->mode;
+ inode->i_uid = perms->uid;
+ inode->i_gid = perms->gid;
+ inode->i_atime = current_time;
+ inode->i_mtime = current_time;
+ inode->i_ctime = current_time;
+ inode->i_private = data;
+ if (fops)
+ inode->i_fop = fops;
+ if (iops)
+ inode->i_op = iops;
+ }
+
+ return inode;
+}
+
+/* Create "regular" file */
+static struct inode *ffs_sb_create_file(struct super_block *sb,
+ const char *name, void *data,
+ const struct file_operations *fops,
+ struct dentry **dentry_p)
+{
+ struct ffs_data *ffs = sb->s_fs_info;
+ struct dentry *dentry;
+ struct inode *inode;
+
+ ENTER();
+
+ dentry = d_alloc_name(sb->s_root, name);
+ if (unlikely(!dentry))
+ return NULL;
+
+ inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
+ if (unlikely(!inode)) {
+ dput(dentry);
+ return NULL;
+ }
+
+ d_add(dentry, inode);
+ if (dentry_p)
+ *dentry_p = dentry;
+
+ return inode;
+}
+
+/* Super block */
+static const struct super_operations ffs_sb_operations = {
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
+};
+
+struct ffs_sb_fill_data {
+ struct ffs_file_perms perms;
+ umode_t root_mode;
+ const char *dev_name;
+ union {
+ /* set by ffs_fs_mount(), read by ffs_sb_fill() */
+ void *private_data;
+ /* set by ffs_sb_fill(), read by ffs_fs_mount */
+ struct ffs_data *ffs_data;
+ };
+};
+
+static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
+{
+ struct ffs_sb_fill_data *data = _data;
+ struct inode *inode;
+ struct ffs_data *ffs;
+
+ ENTER();
+
+ /* Initialise data */
+ ffs = ffs_data_new();
+ if (unlikely(!ffs))
+ goto Enomem;
+
+ ffs->sb = sb;
+ ffs->dev_name = kstrdup(data->dev_name, GFP_KERNEL);
+ if (unlikely(!ffs->dev_name))
+ goto Enomem;
+ ffs->file_perms = data->perms;
+ ffs->private_data = data->private_data;
+
+ /* used by the caller of this function */
+ data->ffs_data = ffs;
+
+ sb->s_fs_info = ffs;
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = FUNCTIONFS_MAGIC;
+ sb->s_op = &ffs_sb_operations;
+ sb->s_time_gran = 1;
+
+ /* Root inode */
+ data->perms.mode = data->root_mode;
+ inode = ffs_sb_make_inode(sb, NULL,
+ &simple_dir_operations,
+ &simple_dir_inode_operations,
+ &data->perms);
+ sb->s_root = d_make_root(inode);
+ if (unlikely(!sb->s_root))
+ goto Enomem;
+
+ /* EP0 file */
+ if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs,
+ &ffs_ep0_operations, NULL)))
+ goto Enomem;
+
+ return 0;
+
+Enomem:
+ return -ENOMEM;
+}
+
+static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
+{
+ ENTER();
+
+ if (!opts || !*opts)
+ return 0;
+
+ for (;;) {
+ char *end, *eq, *comma;
+ unsigned long value;
+
+ /* Option limit */
+ comma = strchr(opts, ',');
+ if (comma)
+ *comma = 0;
+
+ /* Value limit */
+ eq = strchr(opts, '=');
+ if (unlikely(!eq)) {
+ pr_err("'=' missing in %s\n", opts);
+ return -EINVAL;
+ }
+ *eq = 0;
+
+ /* Parse value */
+ value = simple_strtoul(eq + 1, &end, 0);
+ if (unlikely(*end != ',' && *end != 0)) {
+ pr_err("%s: invalid value: %s\n", opts, eq + 1);
+ return -EINVAL;
+ }
+
+ /* Interpret option */
+ switch (eq - opts) {
+ case 5:
+ if (!memcmp(opts, "rmode", 5))
+ data->root_mode = (value & 0555) | S_IFDIR;
+ else if (!memcmp(opts, "fmode", 5))
+ data->perms.mode = (value & 0666) | S_IFREG;
+ else
+ goto invalid;
+ break;
+
+ case 4:
+ if (!memcmp(opts, "mode", 4)) {
+ data->root_mode = (value & 0555) | S_IFDIR;
+ data->perms.mode = (value & 0666) | S_IFREG;
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 3:
+ if (!memcmp(opts, "uid", 3))
+ data->perms.uid = value;
+ else if (!memcmp(opts, "gid", 3))
+ data->perms.gid = value;
+ else
+ goto invalid;
+ break;
+
+ default:
+invalid:
+ pr_err("%s: invalid option\n", opts);
+ return -EINVAL;
+ }
+
+ /* Next iteration */
+ if (!comma)
+ break;
+ opts = comma + 1;
+ }
+
+ return 0;
+}
+
+/* "mount -t functionfs dev_name /dev/function" ends up here */
+
+static struct dentry *
+ffs_fs_mount(struct file_system_type *t, int flags,
+ const char *dev_name, void *opts)
+{
+ struct ffs_sb_fill_data data = {
+ .perms = {
+ .mode = S_IFREG | 0600,
+ .uid = 0,
+ .gid = 0
+ },
+ .root_mode = S_IFDIR | 0500,
+ };
+ struct dentry *rv;
+ int ret;
+ void *ffs_dev;
+
+ ENTER();
+
+ ret = ffs_fs_parse_opts(&data, opts);
+ if (unlikely(ret < 0))
+ return ERR_PTR(ret);
+
+ ffs_dev = functionfs_acquire_dev_callback(dev_name);
+ if (IS_ERR(ffs_dev))
+ return ffs_dev;
+
+ data.dev_name = dev_name;
+ data.private_data = ffs_dev;
+ rv = mount_nodev(t, flags, &data, ffs_sb_fill);
+
+ /* data.ffs_data is set by ffs_sb_fill */
+ if (IS_ERR(rv))
+ functionfs_release_dev_callback(data.ffs_data);
+
+ return rv;
+}
+
+static void
+ffs_fs_kill_sb(struct super_block *sb)
+{
+ ENTER();
+
+ kill_litter_super(sb);
+ if (sb->s_fs_info) {
+ functionfs_release_dev_callback(sb->s_fs_info);
+ ffs_data_put(sb->s_fs_info);
+ }
+}
+
+static struct file_system_type ffs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "functionfs",
+ .mount = ffs_fs_mount,
+ .kill_sb = ffs_fs_kill_sb,
+};
+
+
+/* Driver's main init/cleanup functions *************************************/
+
+static int functionfs_init(void)
+{
+ int ret;
+
+ ENTER();
+
+ ret = register_filesystem(&ffs_fs_type);
+ if (likely(!ret))
+ pr_info("file system registered\n");
+ else
+ pr_err("failed registering file system (%d)\n", ret);
+
+ return ret;
+}
+
+static void functionfs_cleanup(void)
+{
+ ENTER();
+
+ pr_info("unloading\n");
+ unregister_filesystem(&ffs_fs_type);
+}
+
+
+/* ffs_data and ffs_function construction and destruction code **************/
+
+static void ffs_data_clear(struct ffs_data *ffs);
+static void ffs_data_reset(struct ffs_data *ffs);
+
+static void ffs_data_get(struct ffs_data *ffs)
+{
+ ENTER();
+
+ atomic_inc(&ffs->ref);
+}
+
+static void ffs_data_opened(struct ffs_data *ffs)
+{
+ ENTER();
+
+ atomic_inc(&ffs->ref);
+ atomic_inc(&ffs->opened);
+}
+
+static void ffs_data_put(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (unlikely(atomic_dec_and_test(&ffs->ref))) {
+ pr_info("%s(): freeing\n", __func__);
+ ffs_data_clear(ffs);
+ BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
+ waitqueue_active(&ffs->ep0req_completion.wait));
+ kfree(ffs->dev_name);
+ kfree(ffs);
+ }
+}
+
+static void ffs_data_closed(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (atomic_dec_and_test(&ffs->opened)) {
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ }
+
+ ffs_data_put(ffs);
+}
+
+static struct ffs_data *ffs_data_new(void)
+{
+ struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL);
+ if (unlikely(!ffs))
+ return 0;
+
+ ENTER();
+
+ atomic_set(&ffs->ref, 1);
+ atomic_set(&ffs->opened, 0);
+ ffs->state = FFS_READ_DESCRIPTORS;
+ mutex_init(&ffs->mutex);
+ spin_lock_init(&ffs->eps_lock);
+ init_waitqueue_head(&ffs->ev.waitq);
+ init_completion(&ffs->ep0req_completion);
+
+ /* XXX REVISIT need to update it in some places, or do we? */
+ ffs->ev.can_stall = 1;
+
+ return ffs;
+}
+
+static void ffs_data_clear(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags))
+ functionfs_closed_callback(ffs);
+
+ BUG_ON(ffs->gadget);
+
+ if (ffs->epfiles)
+ ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
+
+ kfree(ffs->raw_descs);
+ kfree(ffs->raw_strings);
+ kfree(ffs->stringtabs);
+}
+
+static void ffs_data_reset(struct ffs_data *ffs)
+{
+ ENTER();
+
+ ffs_data_clear(ffs);
+
+ ffs->epfiles = NULL;
+ ffs->raw_descs = NULL;
+ ffs->raw_strings = NULL;
+ ffs->stringtabs = NULL;
+
+ ffs->raw_descs_length = 0;
+ ffs->raw_fs_descs_length = 0;
+ ffs->fs_descs_count = 0;
+ ffs->hs_descs_count = 0;
+
+ ffs->strings_count = 0;
+ ffs->interfaces_count = 0;
+ ffs->eps_count = 0;
+
+ ffs->ev.count = 0;
+
+ ffs->state = FFS_READ_DESCRIPTORS;
+ ffs->setup_state = FFS_NO_SETUP;
+ ffs->flags = 0;
+}
+
+
+static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
+{
+ struct usb_gadget_strings **lang;
+ int first_id;
+
+ ENTER();
+
+ if (WARN_ON(ffs->state != FFS_ACTIVE
+ || test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
+ return -EBADFD;
+
+ first_id = usb_string_ids_n(cdev, ffs->strings_count);
+ if (unlikely(first_id < 0))
+ return first_id;
+
+ ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
+ if (unlikely(!ffs->ep0req))
+ return -ENOMEM;
+ ffs->ep0req->complete = ffs_ep0_complete;
+ ffs->ep0req->context = ffs;
+
+ lang = ffs->stringtabs;
+ for (lang = ffs->stringtabs; *lang; ++lang) {
+ struct usb_string *str = (*lang)->strings;
+ int id = first_id;
+ for (; str->s; ++id, ++str)
+ str->id = id;
+ }
+
+ ffs->gadget = cdev->gadget;
+ ffs_data_get(ffs);
+ return 0;
+}
+
+static void functionfs_unbind(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (!WARN_ON(!ffs->gadget)) {
+ usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
+ ffs->ep0req = NULL;
+ ffs->gadget = NULL;
+ ffs_data_put(ffs);
+ clear_bit(FFS_FL_BOUND, &ffs->flags);
+ }
+}
+
+static int ffs_epfiles_create(struct ffs_data *ffs)
+{
+ struct ffs_epfile *epfile, *epfiles;
+ unsigned i, count;
+
+ ENTER();
+
+ count = ffs->eps_count;
+ epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL);
+ if (!epfiles)
+ return -ENOMEM;
+
+ epfile = epfiles;
+ for (i = 1; i <= count; ++i, ++epfile) {
+ epfile->ffs = ffs;
+ mutex_init(&epfile->mutex);
+ init_waitqueue_head(&epfile->wait);
+ sprintf(epfiles->name, "ep%u", i);
+ if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile,
+ &ffs_epfile_operations,
+ &epfile->dentry))) {
+ ffs_epfiles_destroy(epfiles, i - 1);
+ return -ENOMEM;
+ }
+ }
+
+ ffs->epfiles = epfiles;
+ return 0;
+}
+
+static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
+{
+ struct ffs_epfile *epfile = epfiles;
+
+ ENTER();
+
+ for (; count; --count, ++epfile) {
+ BUG_ON(mutex_is_locked(&epfile->mutex) ||
+ waitqueue_active(&epfile->wait));
+ if (epfile->dentry) {
+ d_delete(epfile->dentry);
+ dput(epfile->dentry);
+ epfile->dentry = NULL;
+ }
+ }
+
+ kfree(epfiles);
+}
+
+static int functionfs_bind_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct ffs_data *ffs)
+{
+ struct ffs_function *func;
+ int ret;
+
+ ENTER();
+
+ func = kzalloc(sizeof *func, GFP_KERNEL);
+ if (unlikely(!func))
+ return -ENOMEM;
+
+ func->function.name = "Function FS Gadget";
+ func->function.strings = ffs->stringtabs;
+
+ func->function.bind = ffs_func_bind;
+ func->function.unbind = ffs_func_unbind;
+ func->function.set_alt = ffs_func_set_alt;
+ func->function.disable = ffs_func_disable;
+ func->function.setup = ffs_func_setup;
+ func->function.suspend = ffs_func_suspend;
+ func->function.resume = ffs_func_resume;
+
+ func->conf = c;
+ func->gadget = cdev->gadget;
+ func->ffs = ffs;
+ ffs_data_get(ffs);
+
+ ret = usb_add_function(c, &func->function);
+ if (unlikely(ret))
+ ffs_func_free(func);
+
+ return ret;
+}
+
+static void ffs_func_free(struct ffs_function *func)
+{
+ struct ffs_ep *ep = func->eps;
+ unsigned count = func->ffs->eps_count;
+ unsigned long flags;
+
+ ENTER();
+
+ /* cleanup after autoconfig */
+ spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ do {
+ if (ep->ep && ep->req)
+ usb_ep_free_request(ep->ep, ep->req);
+ ep->req = NULL;
+ ++ep;
+ } while (--count);
+ spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+
+ ffs_data_put(func->ffs);
+
+ kfree(func->eps);
+ /*
+ * eps and interfaces_nums are allocated in the same chunk so
+ * only one free is required. Descriptors are also allocated
+ * in the same chunk.
+ */
+
+ kfree(func);
+}
+
+static void ffs_func_eps_disable(struct ffs_function *func)
+{
+ struct ffs_ep *ep = func->eps;
+ struct ffs_epfile *epfile = func->ffs->epfiles;
+ unsigned count = func->ffs->eps_count;
+ unsigned long flags;
+
+ spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ do {
+ /* pending requests get nuked */
+ if (likely(ep->ep))
+ usb_ep_disable(ep->ep);
+ epfile->ep = NULL;
+
+ ++ep;
+ ++epfile;
+ } while (--count);
+ spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+}
+
+static int ffs_func_eps_enable(struct ffs_function *func)
+{
+ struct ffs_data *ffs = func->ffs;
+ struct ffs_ep *ep = func->eps;
+ struct ffs_epfile *epfile = ffs->epfiles;
+ unsigned count = ffs->eps_count;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ do {
+ struct usb_endpoint_descriptor *ds;
+ ds = ep->descs[ep->descs[1] ? 1 : 0];
+
+ ep->ep->driver_data = ep;
+ ep->ep->desc = ds;
+ ret = usb_ep_enable(ep->ep);
+ if (likely(!ret)) {
+ epfile->ep = ep;
+ epfile->in = usb_endpoint_dir_in(ds);
+ epfile->isoc = usb_endpoint_xfer_isoc(ds);
+ } else {
+ break;
+ }
+
+ wake_up(&epfile->wait);
+
+ ++ep;
+ ++epfile;
+ } while (--count);
+ spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+
+ return ret;
+}
+
+
+/* Parsing and building descriptors and strings *****************************/
+
+/*
+ * This validates if data pointed by data is a valid USB descriptor as
+ * well as record how many interfaces, endpoints and strings are
+ * required by given configuration. Returns address after the
+ * descriptor or NULL if data is invalid.
+ */
+
+enum ffs_entity_type {
+ FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
+};
+
+typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
+ u8 *valuep,
+ struct usb_descriptor_header *desc,
+ void *priv);
+
+static int __must_check ffs_do_desc(char *data, unsigned len,
+ ffs_entity_callback entity, void *priv)
+{
+ struct usb_descriptor_header *_ds = (void *)data;
+ u8 length;
+ int ret;
+
+ ENTER();
+
+ /* At least two bytes are required: length and type */
+ if (len < 2) {
+ pr_vdebug("descriptor too short\n");
+ return -EINVAL;
+ }
+
+ /* If we have at least as many bytes as the descriptor takes? */
+ length = _ds->bLength;
+ if (len < length) {
+ pr_vdebug("descriptor longer then available data\n");
+ return -EINVAL;
+ }
+
+#define __entity_check_INTERFACE(val) 1
+#define __entity_check_STRING(val) (val)
+#define __entity_check_ENDPOINT(val) ((val) & USB_ENDPOINT_NUMBER_MASK)
+#define __entity(type, val) do { \
+ pr_vdebug("entity " #type "(%02x)\n", (val)); \
+ if (unlikely(!__entity_check_ ##type(val))) { \
+ pr_vdebug("invalid entity's value\n"); \
+ return -EINVAL; \
+ } \
+ ret = entity(FFS_ ##type, &val, _ds, priv); \
+ if (unlikely(ret < 0)) { \
+ pr_debug("entity " #type "(%02x); ret = %d\n", \
+ (val), ret); \
+ return ret; \
+ } \
+ } while (0)
+
+ /* Parse descriptor depending on type. */
+ switch (_ds->bDescriptorType) {
+ case USB_DT_DEVICE:
+ case USB_DT_CONFIG:
+ case USB_DT_STRING:
+ case USB_DT_DEVICE_QUALIFIER:
+ /* function can't have any of those */
+ pr_vdebug("descriptor reserved for gadget: %d\n",
+ _ds->bDescriptorType);
+ return -EINVAL;
+
+ case USB_DT_INTERFACE: {
+ struct usb_interface_descriptor *ds = (void *)_ds;
+ pr_vdebug("interface descriptor\n");
+ if (length != sizeof *ds)
+ goto inv_length;
+
+ __entity(INTERFACE, ds->bInterfaceNumber);
+ if (ds->iInterface)
+ __entity(STRING, ds->iInterface);
+ }
+ break;
+
+ case USB_DT_ENDPOINT: {
+ struct usb_endpoint_descriptor *ds = (void *)_ds;
+ pr_vdebug("endpoint descriptor\n");
+ if (length != USB_DT_ENDPOINT_SIZE &&
+ length != USB_DT_ENDPOINT_AUDIO_SIZE)
+ goto inv_length;
+ __entity(ENDPOINT, ds->bEndpointAddress);
+ }
+ break;
+
+ case HID_DT_HID:
+ pr_vdebug("hid descriptor\n");
+ if (length != sizeof(struct hid_descriptor))
+ goto inv_length;
+ break;
+
+ case USB_DT_OTG:
+ if (length != sizeof(struct usb_otg_descriptor))
+ goto inv_length;
+ break;
+
+ case USB_DT_INTERFACE_ASSOCIATION: {
+ struct usb_interface_assoc_descriptor *ds = (void *)_ds;
+ pr_vdebug("interface association descriptor\n");
+ if (length != sizeof *ds)
+ goto inv_length;
+ if (ds->iFunction)
+ __entity(STRING, ds->iFunction);
+ }
+ break;
+
+ case USB_DT_OTHER_SPEED_CONFIG:
+ case USB_DT_INTERFACE_POWER:
+ case USB_DT_DEBUG:
+ case USB_DT_SECURITY:
+ case USB_DT_CS_RADIO_CONTROL:
+ /* TODO */
+ pr_vdebug("unimplemented descriptor: %d\n", _ds->bDescriptorType);
+ return -EINVAL;
+
+ default:
+ /* We should never be here */
+ pr_vdebug("unknown descriptor: %d\n", _ds->bDescriptorType);
+ return -EINVAL;
+
+inv_length:
+ pr_vdebug("invalid length: %d (descriptor %d)\n",
+ _ds->bLength, _ds->bDescriptorType);
+ return -EINVAL;
+ }
+
+#undef __entity
+#undef __entity_check_DESCRIPTOR
+#undef __entity_check_INTERFACE
+#undef __entity_check_STRING
+#undef __entity_check_ENDPOINT
+
+ return length;
+}
+
+static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
+ ffs_entity_callback entity, void *priv)
+{
+ const unsigned _len = len;
+ unsigned long num = 0;
+
+ ENTER();
+
+ for (;;) {
+ int ret;
+
+ if (num == count)
+ data = NULL;
+
+ /* Record "descriptor" entity */
+ ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
+ if (unlikely(ret < 0)) {
+ pr_debug("entity DESCRIPTOR(%02lx); ret = %d\n",
+ num, ret);
+ return ret;
+ }
+
+ if (!data)
+ return _len - len;
+
+ ret = ffs_do_desc(data, len, entity, priv);
+ if (unlikely(ret < 0)) {
+ pr_debug("%s returns %d\n", __func__, ret);
+ return ret;
+ }
+
+ len -= ret;
+ data += ret;
+ ++num;
+ }
+}
+
+static int __ffs_data_do_entity(enum ffs_entity_type type,
+ u8 *valuep, struct usb_descriptor_header *desc,
+ void *priv)
+{
+ struct ffs_data *ffs = priv;
+
+ ENTER();
+
+ switch (type) {
+ case FFS_DESCRIPTOR:
+ break;
+
+ case FFS_INTERFACE:
+ /*
+ * Interfaces are indexed from zero so if we
+ * encountered interface "n" then there are at least
+ * "n+1" interfaces.
+ */
+ if (*valuep >= ffs->interfaces_count)
+ ffs->interfaces_count = *valuep + 1;
+ break;
+
+ case FFS_STRING:
+ /*
+ * Strings are indexed from 1 (0 is magic ;) reserved
+ * for languages list or some such)
+ */
+ if (*valuep > ffs->strings_count)
+ ffs->strings_count = *valuep;
+ break;
+
+ case FFS_ENDPOINT:
+ /* Endpoints are indexed from 1 as well. */
+ if ((*valuep & USB_ENDPOINT_NUMBER_MASK) > ffs->eps_count)
+ ffs->eps_count = (*valuep & USB_ENDPOINT_NUMBER_MASK);
+ break;
+ }
+
+ return 0;
+}
+
+static int __ffs_data_got_descs(struct ffs_data *ffs,
+ char *const _data, size_t len)
+{
+ unsigned fs_count, hs_count;
+ int fs_len, ret = -EINVAL;
+ char *data = _data;
+
+ ENTER();
+
+ if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC ||
+ get_unaligned_le32(data + 4) != len))
+ goto error;
+ fs_count = get_unaligned_le32(data + 8);
+ hs_count = get_unaligned_le32(data + 12);
+
+ if (!fs_count && !hs_count)
+ goto einval;
+
+ data += 16;
+ len -= 16;
+
+ if (likely(fs_count)) {
+ fs_len = ffs_do_descs(fs_count, data, len,
+ __ffs_data_do_entity, ffs);
+ if (unlikely(fs_len < 0)) {
+ ret = fs_len;
+ goto error;
+ }
+
+ data += fs_len;
+ len -= fs_len;
+ } else {
+ fs_len = 0;
+ }
+
+ if (likely(hs_count)) {
+ ret = ffs_do_descs(hs_count, data, len,
+ __ffs_data_do_entity, ffs);
+ if (unlikely(ret < 0))
+ goto error;
+ } else {
+ ret = 0;
+ }
+
+ if (unlikely(len != ret))
+ goto einval;
+
+ ffs->raw_fs_descs_length = fs_len;
+ ffs->raw_descs_length = fs_len + ret;
+ ffs->raw_descs = _data;
+ ffs->fs_descs_count = fs_count;
+ ffs->hs_descs_count = hs_count;
+
+ return 0;
+
+einval:
+ ret = -EINVAL;
+error:
+ kfree(_data);
+ return ret;
+}
+
+static int __ffs_data_got_strings(struct ffs_data *ffs,
+ char *const _data, size_t len)
+{
+ u32 str_count, needed_count, lang_count;
+ struct usb_gadget_strings **stringtabs, *t;
+ struct usb_string *strings, *s;
+ const char *data = _data;
+
+ ENTER();
+
+ if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
+ get_unaligned_le32(data + 4) != len))
+ goto error;
+ str_count = get_unaligned_le32(data + 8);
+ lang_count = get_unaligned_le32(data + 12);
+
+ /* if one is zero the other must be zero */
+ if (unlikely(!str_count != !lang_count))
+ goto error;
+
+ /* Do we have at least as many strings as descriptors need? */
+ needed_count = ffs->strings_count;
+ if (unlikely(str_count < needed_count))
+ goto error;
+
+ /*
+ * If we don't need any strings just return and free all
+ * memory.
+ */
+ if (!needed_count) {
+ kfree(_data);
+ return 0;
+ }
+
+ /* Allocate everything in one chunk so there's less maintenance. */
+ {
+ struct {
+ struct usb_gadget_strings *stringtabs[lang_count + 1];
+ struct usb_gadget_strings stringtab[lang_count];
+ struct usb_string strings[lang_count*(needed_count+1)];
+ } *d;
+ unsigned i = 0;
+
+ d = kmalloc(sizeof *d, GFP_KERNEL);
+ if (unlikely(!d)) {
+ kfree(_data);
+ return -ENOMEM;
+ }
+
+ stringtabs = d->stringtabs;
+ t = d->stringtab;
+ i = lang_count;
+ do {
+ *stringtabs++ = t++;
+ } while (--i);
+ *stringtabs = NULL;
+
+ stringtabs = d->stringtabs;
+ t = d->stringtab;
+ s = d->strings;
+ strings = s;
+ }
+
+ /* For each language */
+ data += 16;
+ len -= 16;
+
+ do { /* lang_count > 0 so we can use do-while */
+ unsigned needed = needed_count;
+
+ if (unlikely(len < 3))
+ goto error_free;
+ t->language = get_unaligned_le16(data);
+ t->strings = s;
+ ++t;
+
+ data += 2;
+ len -= 2;
+
+ /* For each string */
+ do { /* str_count > 0 so we can use do-while */
+ size_t length = strnlen(data, len);
+
+ if (unlikely(length == len))
+ goto error_free;
+
+ /*
+ * User may provide more strings then we need,
+ * if that's the case we simply ignore the
+ * rest
+ */
+ if (likely(needed)) {
+ /*
+ * s->id will be set while adding
+ * function to configuration so for
+ * now just leave garbage here.
+ */
+ s->s = data;
+ --needed;
+ ++s;
+ }
+
+ data += length + 1;
+ len -= length + 1;
+ } while (--str_count);
+
+ s->id = 0; /* terminator */
+ s->s = NULL;
+ ++s;
+
+ } while (--lang_count);
+
+ /* Some garbage left? */
+ if (unlikely(len))
+ goto error_free;
+
+ /* Done! */
+ ffs->stringtabs = stringtabs;
+ ffs->raw_strings = _data;
+
+ return 0;
+
+error_free:
+ kfree(stringtabs);
+error:
+ kfree(_data);
+ return -EINVAL;
+}
+
+
+/* Events handling and management *******************************************/
+
+static void __ffs_event_add(struct ffs_data *ffs,
+ enum usb_functionfs_event_type type)
+{
+ enum usb_functionfs_event_type rem_type1, rem_type2 = type;
+ int neg = 0;
+
+ /*
+ * Abort any unhandled setup
+ *
+ * We do not need to worry about some cmpxchg() changing value
+ * of ffs->setup_state without holding the lock because when
+ * state is FFS_SETUP_PENDING cmpxchg() in several places in
+ * the source does nothing.
+ */
+ if (ffs->setup_state == FFS_SETUP_PENDING)
+ ffs->setup_state = FFS_SETUP_CANCELED;
+
+ switch (type) {
+ case FUNCTIONFS_RESUME:
+ rem_type2 = FUNCTIONFS_SUSPEND;
+ /* FALL THROUGH */
+ case FUNCTIONFS_SUSPEND:
+ case FUNCTIONFS_SETUP:
+ rem_type1 = type;
+ /* Discard all similar events */
+ break;
+
+ case FUNCTIONFS_BIND:
+ case FUNCTIONFS_UNBIND:
+ case FUNCTIONFS_DISABLE:
+ case FUNCTIONFS_ENABLE:
+ /* Discard everything other then power management. */
+ rem_type1 = FUNCTIONFS_SUSPEND;
+ rem_type2 = FUNCTIONFS_RESUME;
+ neg = 1;
+ break;
+
+ default:
+ BUG();
+ }
+
+ {
+ u8 *ev = ffs->ev.types, *out = ev;
+ unsigned n = ffs->ev.count;
+ for (; n; --n, ++ev)
+ if ((*ev == rem_type1 || *ev == rem_type2) == neg)
+ *out++ = *ev;
+ else
+ pr_vdebug("purging event %d\n", *ev);
+ ffs->ev.count = out - ffs->ev.types;
+ }
+
+ pr_vdebug("adding event %d\n", type);
+ ffs->ev.types[ffs->ev.count++] = type;
+ wake_up_locked(&ffs->ev.waitq);
+}
+
+static void ffs_event_add(struct ffs_data *ffs,
+ enum usb_functionfs_event_type type)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+ __ffs_event_add(ffs, type);
+ spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+}
+
+
+/* Bind/unbind USB function hooks *******************************************/
+
+static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
+ struct usb_descriptor_header *desc,
+ void *priv)
+{
+ struct usb_endpoint_descriptor *ds = (void *)desc;
+ struct ffs_function *func = priv;
+ struct ffs_ep *ffs_ep;
+
+ /*
+ * If hs_descriptors is not NULL then we are reading hs
+ * descriptors now
+ */
+ const int isHS = func->function.hs_descriptors != NULL;
+ unsigned idx;
+
+ if (type != FFS_DESCRIPTOR)
+ return 0;
+
+ if (isHS)
+ func->function.hs_descriptors[(long)valuep] = desc;
+ else
+ func->function.descriptors[(long)valuep] = desc;
+
+ if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
+ return 0;
+
+ idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
+ ffs_ep = func->eps + idx;
+
+ if (unlikely(ffs_ep->descs[isHS])) {
+ pr_vdebug("two %sspeed descriptors for EP %d\n",
+ isHS ? "high" : "full",
+ ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ return -EINVAL;
+ }
+ ffs_ep->descs[isHS] = ds;
+
+ ffs_dump_mem(": Original ep desc", ds, ds->bLength);
+ if (ffs_ep->ep) {
+ ds->bEndpointAddress = ffs_ep->descs[0]->bEndpointAddress;
+ if (!ds->wMaxPacketSize)
+ ds->wMaxPacketSize = ffs_ep->descs[0]->wMaxPacketSize;
+ } else {
+ struct usb_request *req;
+ struct usb_ep *ep;
+
+ pr_vdebug("autoconfig\n");
+ ep = usb_ep_autoconfig(func->gadget, ds);
+ if (unlikely(!ep))
+ return -ENOTSUPP;
+ ep->driver_data = func->eps + idx;
+
+ req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (unlikely(!req))
+ return -ENOMEM;
+
+ ffs_ep->ep = ep;
+ ffs_ep->req = req;
+ func->eps_revmap[ds->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK] = idx + 1;
+ }
+ ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
+
+ return 0;
+}
+
+static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
+ struct usb_descriptor_header *desc,
+ void *priv)
+{
+ struct ffs_function *func = priv;
+ unsigned idx;
+ u8 newValue;
+
+ switch (type) {
+ default:
+ case FFS_DESCRIPTOR:
+ /* Handled in previous pass by __ffs_func_bind_do_descs() */
+ return 0;
+
+ case FFS_INTERFACE:
+ idx = *valuep;
+ if (func->interfaces_nums[idx] < 0) {
+ int id = usb_interface_id(func->conf, &func->function);
+ if (unlikely(id < 0))
+ return id;
+ func->interfaces_nums[idx] = id;
+ }
+ newValue = func->interfaces_nums[idx];
+ break;
+
+ case FFS_STRING:
+ /* String' IDs are allocated when fsf_data is bound to cdev */
+ newValue = func->ffs->stringtabs[0]->strings[*valuep - 1].id;
+ break;
+
+ case FFS_ENDPOINT:
+ /*
+ * USB_DT_ENDPOINT are handled in
+ * __ffs_func_bind_do_descs().
+ */
+ if (desc->bDescriptorType == USB_DT_ENDPOINT)
+ return 0;
+
+ idx = (*valuep & USB_ENDPOINT_NUMBER_MASK) - 1;
+ if (unlikely(!func->eps[idx].ep))
+ return -EINVAL;
+
+ {
+ struct usb_endpoint_descriptor **descs;
+ descs = func->eps[idx].descs;
+ newValue = descs[descs[0] ? 0 : 1]->bEndpointAddress;
+ }
+ break;
+ }
+
+ pr_vdebug("%02x -> %02x\n", *valuep, newValue);
+ *valuep = newValue;
+ return 0;
+}
+
+static int ffs_func_bind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+
+ const int full = !!func->ffs->fs_descs_count;
+ const int high = gadget_is_dualspeed(func->gadget) &&
+ func->ffs->hs_descs_count;
+
+ int ret;
+
+ /* Make it a single chunk, less management later on */
+ struct {
+ struct ffs_ep eps[ffs->eps_count];
+ struct usb_descriptor_header
+ *fs_descs[full ? ffs->fs_descs_count + 1 : 0];
+ struct usb_descriptor_header
+ *hs_descs[high ? ffs->hs_descs_count + 1 : 0];
+ short inums[ffs->interfaces_count];
+ char raw_descs[high ? ffs->raw_descs_length
+ : ffs->raw_fs_descs_length];
+ } *data;
+
+ ENTER();
+
+ /* Only high speed but not supported by gadget? */
+ if (unlikely(!(full | high)))
+ return -ENOTSUPP;
+
+ /* Allocate */
+ data = kmalloc(sizeof *data, GFP_KERNEL);
+ if (unlikely(!data))
+ return -ENOMEM;
+
+ /* Zero */
+ memset(data->eps, 0, sizeof data->eps);
+ memcpy(data->raw_descs, ffs->raw_descs + 16, sizeof data->raw_descs);
+ memset(data->inums, 0xff, sizeof data->inums);
+ for (ret = ffs->eps_count; ret; --ret)
+ data->eps[ret].num = -1;
+
+ /* Save pointers */
+ func->eps = data->eps;
+ func->interfaces_nums = data->inums;
+
+ /*
+ * Go through all the endpoint descriptors and allocate
+ * endpoints first, so that later we can rewrite the endpoint
+ * numbers without worrying that it may be described later on.
+ */
+ if (likely(full)) {
+ func->function.descriptors = data->fs_descs;
+ ret = ffs_do_descs(ffs->fs_descs_count,
+ data->raw_descs,
+ sizeof data->raw_descs,
+ __ffs_func_bind_do_descs, func);
+ if (unlikely(ret < 0))
+ goto error;
+ } else {
+ ret = 0;
+ }
+
+ if (likely(high)) {
+ func->function.hs_descriptors = data->hs_descs;
+ ret = ffs_do_descs(ffs->hs_descs_count,
+ data->raw_descs + ret,
+ (sizeof data->raw_descs) - ret,
+ __ffs_func_bind_do_descs, func);
+ }
+
+ /*
+ * Now handle interface numbers allocation and interface and
+ * endpoint numbers rewriting. We can do that in one go
+ * now.
+ */
+ ret = ffs_do_descs(ffs->fs_descs_count +
+ (high ? ffs->hs_descs_count : 0),
+ data->raw_descs, sizeof data->raw_descs,
+ __ffs_func_bind_do_nums, func);
+ if (unlikely(ret < 0))
+ goto error;
+
+ /* And we're done */
+ ffs_event_add(ffs, FUNCTIONFS_BIND);
+ return 0;
+
+error:
+ /* XXX Do we need to release all claimed endpoints here? */
+ return ret;
+}
+
+
+/* Other USB function hooks *************************************************/
+
+static void ffs_func_unbind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+
+ ENTER();
+
+ if (ffs->func == func) {
+ ffs_func_eps_disable(func);
+ ffs->func = NULL;
+ }
+
+ ffs_event_add(ffs, FUNCTIONFS_UNBIND);
+
+ ffs_func_free(func);
+}
+
+static int ffs_func_set_alt(struct usb_function *f,
+ unsigned interface, unsigned alt)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+ int ret = 0, intf;
+
+ if (alt != (unsigned)-1) {
+ intf = ffs_func_revmap_intf(func, interface);
+ if (unlikely(intf < 0))
+ return intf;
+ }
+
+ if (ffs->func)
+ ffs_func_eps_disable(ffs->func);
+
+ if (ffs->state != FFS_ACTIVE)
+ return -ENODEV;
+
+ if (alt == (unsigned)-1) {
+ ffs->func = NULL;
+ ffs_event_add(ffs, FUNCTIONFS_DISABLE);
+ return 0;
+ }
+
+ ffs->func = func;
+ ret = ffs_func_eps_enable(func);
+ if (likely(ret >= 0))
+ ffs_event_add(ffs, FUNCTIONFS_ENABLE);
+ return ret;
+}
+
+static void ffs_func_disable(struct usb_function *f)
+{
+ ffs_func_set_alt(f, 0, (unsigned)-1);
+}
+
+static int ffs_func_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *creq)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+ unsigned long flags;
+ int ret;
+
+ ENTER();
+
+ pr_vdebug("creq->bRequestType = %02x\n", creq->bRequestType);
+ pr_vdebug("creq->bRequest = %02x\n", creq->bRequest);
+ pr_vdebug("creq->wValue = %04x\n", le16_to_cpu(creq->wValue));
+ pr_vdebug("creq->wIndex = %04x\n", le16_to_cpu(creq->wIndex));
+ pr_vdebug("creq->wLength = %04x\n", le16_to_cpu(creq->wLength));
+
+ /*
+ * Most requests directed to interface go through here
+ * (notable exceptions are set/get interface) so we need to
+ * handle them. All other either handled by composite or
+ * passed to usb_configuration->setup() (if one is set). No
+ * matter, we will handle requests directed to endpoint here
+ * as well (as it's straightforward) but what to do with any
+ * other request?
+ */
+ if (ffs->state != FFS_ACTIVE)
+ return -ENODEV;
+
+ switch (creq->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ ret = ffs_func_revmap_intf(func, le16_to_cpu(creq->wIndex));
+ if (unlikely(ret < 0))
+ return ret;
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex));
+ if (unlikely(ret < 0))
+ return ret;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+ ffs->ev.setup = *creq;
+ ffs->ev.setup.wIndex = cpu_to_le16(ret);
+ __ffs_event_add(ffs, FUNCTIONFS_SETUP);
+ spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+
+ return 0;
+}
+
+static void ffs_func_suspend(struct usb_function *f)
+{
+ ENTER();
+ ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_SUSPEND);
+}
+
+static void ffs_func_resume(struct usb_function *f)
+{
+ ENTER();
+ ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_RESUME);
+}
+
+
+/* Endpoint and interface numbers reverse mapping ***************************/
+
+static int ffs_func_revmap_ep(struct ffs_function *func, u8 num)
+{
+ num = func->eps_revmap[num & USB_ENDPOINT_NUMBER_MASK];
+ return num ? num : -EDOM;
+}
+
+static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
+{
+ short *nums = func->interfaces_nums;
+ unsigned count = func->ffs->interfaces_count;
+
+ for (; count; --count, ++nums) {
+ if (*nums >= 0 && *nums == intf)
+ return nums - func->interfaces_nums;
+ }
+
+ return -EDOM;
+}
+
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+{
+ return nonblock
+ ? likely(mutex_trylock(mutex)) ? 0 : -EAGAIN
+ : mutex_lock_interruptible(mutex);
+}
+
+static char *ffs_prepare_buffer(const char * __user buf, size_t len)
+{
+ char *data;
+
+ if (unlikely(!len))
+ return NULL;
+
+ data = kmalloc(len, GFP_KERNEL);
+ if (unlikely(!data))
+ return ERR_PTR(-ENOMEM);
+
+ if (unlikely(__copy_from_user(data, buf, len))) {
+ kfree(data);
+ return ERR_PTR(-EFAULT);
+ }
+
+ pr_vdebug("Buffer from user space:\n");
+ ffs_dump_mem("", data, len);
+
+ return data;
+}
diff --git a/drivers/staging/ccg/f_mass_storage.c b/drivers/staging/ccg/f_mass_storage.c
new file mode 100644
index 000000000000..4f1142efa6d1
--- /dev/null
+++ b/drivers/staging/ccg/f_mass_storage.c
@@ -0,0 +1,3135 @@
+/*
+ * f_mass_storage.c -- Mass Storage USB Composite Function
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyright (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz <mina86@mina86.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The names of the above-listed copyright holders 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") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * THIS SOFTWARE IS PROVIDED BY 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.
+ */
+
+/*
+ * The Mass Storage Function acts as a USB Mass Storage device,
+ * appearing to the host as a disk drive or as a CD-ROM drive. In
+ * addition to providing an example of a genuinely useful composite
+ * function for a USB device, it also illustrates a technique of
+ * double-buffering for increased throughput.
+ *
+ * For more information about MSF and in particular its module
+ * parameters and sysfs interface read the
+ * <Documentation/usb/mass-storage.txt> file.
+ */
+
+/*
+ * MSF is configured by specifying a fsg_config structure. It has the
+ * following fields:
+ *
+ * nluns Number of LUNs function have (anywhere from 1
+ * to FSG_MAX_LUNS which is 8).
+ * luns An array of LUN configuration values. This
+ * should be filled for each LUN that
+ * function will include (ie. for "nluns"
+ * LUNs). Each element of the array has
+ * the following fields:
+ * ->filename The path to the backing file for the LUN.
+ * Required if LUN is not marked as
+ * removable.
+ * ->ro Flag specifying access to the LUN shall be
+ * read-only. This is implied if CD-ROM
+ * emulation is enabled as well as when
+ * it was impossible to open "filename"
+ * in R/W mode.
+ * ->removable Flag specifying that LUN shall be indicated as
+ * being removable.
+ * ->cdrom Flag specifying that LUN shall be reported as
+ * being a CD-ROM.
+ * ->nofua Flag specifying that FUA flag in SCSI WRITE(10,12)
+ * commands for this LUN shall be ignored.
+ *
+ * vendor_name
+ * product_name
+ * release Information used as a reply to INQUIRY
+ * request. To use default set to NULL,
+ * NULL, 0xffff respectively. The first
+ * field should be 8 and the second 16
+ * characters or less.
+ *
+ * can_stall Set to permit function to halt bulk endpoints.
+ * Disabled on some USB devices known not
+ * to work correctly. You should set it
+ * to true.
+ *
+ * If "removable" is not set for a LUN then a backing file must be
+ * specified. If it is set, then NULL filename means the LUN's medium
+ * is not loaded (an empty string as "filename" in the fsg_config
+ * structure causes error). The CD-ROM emulation includes a single
+ * data track and no audio tracks; hence there need be only one
+ * backing file per LUN.
+ *
+ * This function is heavily based on "File-backed Storage Gadget" by
+ * Alan Stern which in turn is heavily based on "Gadget Zero" by David
+ * Brownell. The driver's SCSI command interface was based on the
+ * "Information technology - Small Computer System Interface - 2"
+ * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93,
+ * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>.
+ * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which
+ * was based on the "Universal Serial Bus Mass Storage Class UFI
+ * Command Specification" document, Revision 1.0, December 14, 1998,
+ * available at
+ * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>.
+ */
+
+/*
+ * Driver Design
+ *
+ * The MSF is fairly straightforward. There is a main kernel
+ * thread that handles most of the work. Interrupt routines field
+ * callbacks from the controller driver: bulk- and interrupt-request
+ * completion notifications, endpoint-0 events, and disconnect events.
+ * Completion events are passed to the main thread by wakeup calls. Many
+ * ep0 requests are handled at interrupt time, but SetInterface,
+ * SetConfiguration, and device reset requests are forwarded to the
+ * thread in the form of "exceptions" using SIGUSR1 signals (since they
+ * should interrupt any ongoing file I/O operations).
+ *
+ * The thread's main routine implements the standard command/data/status
+ * parts of a SCSI interaction. It and its subroutines are full of tests
+ * for pending signals/exceptions -- all this polling is necessary since
+ * the kernel has no setjmp/longjmp equivalents. (Maybe this is an
+ * indication that the driver really wants to be running in userspace.)
+ * An important point is that so long as the thread is alive it keeps an
+ * open reference to the backing file. This will prevent unmounting
+ * the backing file's underlying filesystem and could cause problems
+ * during system shutdown, for example. To prevent such problems, the
+ * thread catches INT, TERM, and KILL signals and converts them into
+ * an EXIT exception.
+ *
+ * In normal operation the main thread is started during the gadget's
+ * fsg_bind() callback and stopped during fsg_unbind(). But it can
+ * also exit when it receives a signal, and there's no point leaving
+ * the gadget running when the thread is dead. As of this moment, MSF
+ * provides no way to deregister the gadget when thread dies -- maybe
+ * a callback functions is needed.
+ *
+ * To provide maximum throughput, the driver uses a circular pipeline of
+ * buffer heads (struct fsg_buffhd). In principle the pipeline can be
+ * arbitrarily long; in practice the benefits don't justify having more
+ * than 2 stages (i.e., double buffering). But it helps to think of the
+ * pipeline as being a long one. Each buffer head contains a bulk-in and
+ * a bulk-out request pointer (since the buffer can be used for both
+ * output and input -- directions always are given from the host's
+ * point of view) as well as a pointer to the buffer and various state
+ * variables.
+ *
+ * Use of the pipeline follows a simple protocol. There is a variable
+ * (fsg->next_buffhd_to_fill) that points to the next buffer head to use.
+ * At any time that buffer head may still be in use from an earlier
+ * request, so each buffer head has a state variable indicating whether
+ * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the
+ * buffer head to be EMPTY, filling the buffer either by file I/O or by
+ * USB I/O (during which the buffer head is BUSY), and marking the buffer
+ * head FULL when the I/O is complete. Then the buffer will be emptied
+ * (again possibly by USB I/O, during which it is marked BUSY) and
+ * finally marked EMPTY again (possibly by a completion routine).
+ *
+ * A module parameter tells the driver to avoid stalling the bulk
+ * endpoints wherever the transport specification allows. This is
+ * necessary for some UDCs like the SuperH, which cannot reliably clear a
+ * halt on a bulk endpoint. However, under certain circumstances the
+ * Bulk-only specification requires a stall. In such cases the driver
+ * will halt the endpoint and set a flag indicating that it should clear
+ * the halt in software during the next device reset. Hopefully this
+ * will permit everything to work correctly. Furthermore, although the
+ * specification allows the bulk-out endpoint to halt when the host sends
+ * too much data, implementing this would cause an unavoidable race.
+ * The driver will always use the "no-stall" approach for OUT transfers.
+ *
+ * One subtle point concerns sending status-stage responses for ep0
+ * requests. Some of these requests, such as device reset, can involve
+ * interrupting an ongoing file I/O operation, which might take an
+ * arbitrarily long time. During that delay the host might give up on
+ * the original ep0 request and issue a new one. When that happens the
+ * driver should not notify the host about completion of the original
+ * request, as the host will no longer be waiting for it. So the driver
+ * assigns to each ep0 request a unique tag, and it keeps track of the
+ * tag value of the request associated with a long-running exception
+ * (device-reset, interface-change, or configuration-change). When the
+ * exception handler is finished, the status-stage response is submitted
+ * only if the current ep0 request tag is equal to the exception request
+ * tag. Thus only the most recently received ep0 request will get a
+ * status-stage response.
+ *
+ * Warning: This driver source file is too long. It ought to be split up
+ * into a header file plus about 3 separate .c files, to handle the details
+ * of the Gadget, USB Mass Storage, and SCSI protocols.
+ */
+
+
+/* #define VERBOSE_DEBUG */
+/* #define DUMP_MSGS */
+
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/dcache.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kref.h>
+#include <linux/kthread.h>
+#include <linux/limits.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/freezer.h>
+#include <linux/utsname.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+#include "gadget_chips.h"
+
+
+/*------------------------------------------------------------------------*/
+
+#define FSG_DRIVER_DESC "Mass Storage Function"
+#define FSG_DRIVER_VERSION "2009/09/11"
+
+static const char fsg_string_interface[] = "Mass Storage";
+
+#define FSG_NO_DEVICE_STRINGS 1
+#define FSG_NO_OTG 1
+#define FSG_NO_INTR_EP 1
+
+#include "storage_common.c"
+
+
+/*-------------------------------------------------------------------------*/
+
+struct fsg_dev;
+struct fsg_common;
+
+/* FSF callback functions */
+struct fsg_operations {
+ /*
+ * Callback function to call when thread exits. If no
+ * callback is set or it returns value lower then zero MSF
+ * will force eject all LUNs it operates on (including those
+ * marked as non-removable or with prevent_medium_removal flag
+ * set).
+ */
+ int (*thread_exits)(struct fsg_common *common);
+
+ /*
+ * Called prior to ejection. Negative return means error,
+ * zero means to continue with ejection, positive means not to
+ * eject.
+ */
+ int (*pre_eject)(struct fsg_common *common,
+ struct fsg_lun *lun, int num);
+ /*
+ * Called after ejection. Negative return means error, zero
+ * or positive is just a success.
+ */
+ int (*post_eject)(struct fsg_common *common,
+ struct fsg_lun *lun, int num);
+};
+
+/* Data shared by all the FSG instances. */
+struct fsg_common {
+ struct usb_gadget *gadget;
+ struct usb_composite_dev *cdev;
+ struct fsg_dev *fsg, *new_fsg;
+ wait_queue_head_t fsg_wait;
+
+ /* filesem protects: backing files in use */
+ struct rw_semaphore filesem;
+
+ /* lock protects: state, all the req_busy's */
+ spinlock_t lock;
+
+ struct usb_ep *ep0; /* Copy of gadget->ep0 */
+ struct usb_request *ep0req; /* Copy of cdev->req */
+ unsigned int ep0_req_tag;
+
+ struct fsg_buffhd *next_buffhd_to_fill;
+ struct fsg_buffhd *next_buffhd_to_drain;
+ struct fsg_buffhd *buffhds;
+
+ int cmnd_size;
+ u8 cmnd[MAX_COMMAND_SIZE];
+
+ unsigned int nluns;
+ unsigned int lun;
+ struct fsg_lun *luns;
+ struct fsg_lun *curlun;
+
+ unsigned int bulk_out_maxpacket;
+ enum fsg_state state; /* For exception handling */
+ unsigned int exception_req_tag;
+
+ enum data_direction data_dir;
+ u32 data_size;
+ u32 data_size_from_cmnd;
+ u32 tag;
+ u32 residue;
+ u32 usb_amount_left;
+
+ unsigned int can_stall:1;
+ unsigned int free_storage_on_release:1;
+ unsigned int phase_error:1;
+ unsigned int short_packet_received:1;
+ unsigned int bad_lun_okay:1;
+ unsigned int running:1;
+
+ int thread_wakeup_needed;
+ struct completion thread_notifier;
+ struct task_struct *thread_task;
+
+ /* Callback functions. */
+ const struct fsg_operations *ops;
+ /* Gadget's private data. */
+ void *private_data;
+
+ /*
+ * Vendor (8 chars), product (16 chars), release (4
+ * hexadecimal digits) and NUL byte
+ */
+ char inquiry_string[8 + 16 + 4 + 1];
+
+ struct kref ref;
+};
+
+struct fsg_config {
+ unsigned nluns;
+ struct fsg_lun_config {
+ const char *filename;
+ char ro;
+ char removable;
+ char cdrom;
+ char nofua;
+ } luns[FSG_MAX_LUNS];
+
+ /* Callback functions. */
+ const struct fsg_operations *ops;
+ /* Gadget's private data. */
+ void *private_data;
+
+ const char *vendor_name; /* 8 characters or less */
+ const char *product_name; /* 16 characters or less */
+ u16 release;
+
+ char can_stall;
+};
+
+struct fsg_dev {
+ struct usb_function function;
+ struct usb_gadget *gadget; /* Copy of cdev->gadget */
+ struct fsg_common *common;
+
+ u16 interface_number;
+
+ unsigned int bulk_in_enabled:1;
+ unsigned int bulk_out_enabled:1;
+
+ unsigned long atomic_bitflags;
+#define IGNORE_BULK_OUT 0
+
+ struct usb_ep *bulk_in;
+ struct usb_ep *bulk_out;
+};
+
+static inline int __fsg_is_set(struct fsg_common *common,
+ const char *func, unsigned line)
+{
+ if (common->fsg)
+ return 1;
+ ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
+ WARN_ON(1);
+ return 0;
+}
+
+#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__))
+
+static inline struct fsg_dev *fsg_from_func(struct usb_function *f)
+{
+ return container_of(f, struct fsg_dev, function);
+}
+
+typedef void (*fsg_routine_t)(struct fsg_dev *);
+
+static int exception_in_progress(struct fsg_common *common)
+{
+ return common->state > FSG_STATE_IDLE;
+}
+
+/* Make bulk-out requests be divisible by the maxpacket size */
+static void set_bulk_out_req_length(struct fsg_common *common,
+ struct fsg_buffhd *bh, unsigned int length)
+{
+ unsigned int rem;
+
+ bh->bulk_out_intended_length = length;
+ rem = length % common->bulk_out_maxpacket;
+ if (rem > 0)
+ length += common->bulk_out_maxpacket - rem;
+ bh->outreq->length = length;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
+{
+ const char *name;
+
+ if (ep == fsg->bulk_in)
+ name = "bulk-in";
+ else if (ep == fsg->bulk_out)
+ name = "bulk-out";
+ else
+ name = ep->name;
+ DBG(fsg, "%s set halt\n", name);
+ return usb_ep_set_halt(ep);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* These routines may be called in process context or in_irq */
+
+/* Caller must hold fsg->lock */
+static void wakeup_thread(struct fsg_common *common)
+{
+ /* Tell the main thread that something has happened */
+ common->thread_wakeup_needed = 1;
+ if (common->thread_task)
+ wake_up_process(common->thread_task);
+}
+
+static void raise_exception(struct fsg_common *common, enum fsg_state new_state)
+{
+ unsigned long flags;
+
+ /*
+ * Do nothing if a higher-priority exception is already in progress.
+ * If a lower-or-equal priority exception is in progress, preempt it
+ * and notify the main thread by sending it a signal.
+ */
+ spin_lock_irqsave(&common->lock, flags);
+ if (common->state <= new_state) {
+ common->exception_req_tag = common->ep0_req_tag;
+ common->state = new_state;
+ if (common->thread_task)
+ send_sig_info(SIGUSR1, SEND_SIG_FORCED,
+ common->thread_task);
+ }
+ spin_unlock_irqrestore(&common->lock, flags);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int ep0_queue(struct fsg_common *common)
+{
+ int rc;
+
+ rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC);
+ common->ep0->driver_data = common;
+ if (rc != 0 && rc != -ESHUTDOWN) {
+ /* We can't do much more than wait for a reset */
+ WARNING(common, "error in submission: %s --> %d\n",
+ common->ep0->name, rc);
+ }
+ return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Completion handlers. These always run in_irq. */
+
+static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct fsg_common *common = ep->driver_data;
+ struct fsg_buffhd *bh = req->context;
+
+ if (req->status || req->actual != req->length)
+ DBG(common, "%s --> %d, %u/%u\n", __func__,
+ req->status, req->actual, req->length);
+ if (req->status == -ECONNRESET) /* Request was cancelled */
+ usb_ep_fifo_flush(ep);
+
+ /* Hold the lock while we update the request and buffer states */
+ smp_wmb();
+ spin_lock(&common->lock);
+ bh->inreq_busy = 0;
+ bh->state = BUF_STATE_EMPTY;
+ wakeup_thread(common);
+ spin_unlock(&common->lock);
+}
+
+static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct fsg_common *common = ep->driver_data;
+ struct fsg_buffhd *bh = req->context;
+
+ dump_msg(common, "bulk-out", req->buf, req->actual);
+ if (req->status || req->actual != bh->bulk_out_intended_length)
+ DBG(common, "%s --> %d, %u/%u\n", __func__,
+ req->status, req->actual, bh->bulk_out_intended_length);
+ if (req->status == -ECONNRESET) /* Request was cancelled */
+ usb_ep_fifo_flush(ep);
+
+ /* Hold the lock while we update the request and buffer states */
+ smp_wmb();
+ spin_lock(&common->lock);
+ bh->outreq_busy = 0;
+ bh->state = BUF_STATE_FULL;
+ wakeup_thread(common);
+ spin_unlock(&common->lock);
+}
+
+static int fsg_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct usb_request *req = fsg->common->ep0req;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ if (!fsg_is_set(fsg->common))
+ return -EOPNOTSUPP;
+
+ ++fsg->common->ep0_req_tag; /* Record arrival of a new request */
+ req->context = NULL;
+ req->length = 0;
+ dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl));
+
+ switch (ctrl->bRequest) {
+
+ case US_BULK_RESET_REQUEST:
+ if (ctrl->bRequestType !=
+ (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+ break;
+ if (w_index != fsg->interface_number || w_value != 0 ||
+ w_length != 0)
+ return -EDOM;
+
+ /*
+ * Raise an exception to stop the current operation
+ * and reinitialize our state.
+ */
+ DBG(fsg, "bulk reset request\n");
+ raise_exception(fsg->common, FSG_STATE_RESET);
+ return DELAYED_STATUS;
+
+ case US_BULK_GET_MAX_LUN:
+ if (ctrl->bRequestType !=
+ (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+ break;
+ if (w_index != fsg->interface_number || w_value != 0 ||
+ w_length != 1)
+ return -EDOM;
+ VDBG(fsg, "get max LUN\n");
+ *(u8 *)req->buf = fsg->common->nluns - 1;
+
+ /* Respond with data/status */
+ req->length = min((u16)1, w_length);
+ return ep0_queue(fsg->common);
+ }
+
+ VDBG(fsg,
+ "unknown class-specific control req %02x.%02x v%04x i%04x l%u\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ le16_to_cpu(ctrl->wValue), w_index, w_length);
+ return -EOPNOTSUPP;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* All the following routines run in process context */
+
+/* Use this for bulk or interrupt transfers, not ep0 */
+static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
+ struct usb_request *req, int *pbusy,
+ enum fsg_buffer_state *state)
+{
+ int rc;
+
+ if (ep == fsg->bulk_in)
+ dump_msg(fsg, "bulk-in", req->buf, req->length);
+
+ spin_lock_irq(&fsg->common->lock);
+ *pbusy = 1;
+ *state = BUF_STATE_BUSY;
+ spin_unlock_irq(&fsg->common->lock);
+ rc = usb_ep_queue(ep, req, GFP_KERNEL);
+ if (rc != 0) {
+ *pbusy = 0;
+ *state = BUF_STATE_EMPTY;
+
+ /* We can't do much more than wait for a reset */
+
+ /*
+ * Note: currently the net2280 driver fails zero-length
+ * submissions if DMA is enabled.
+ */
+ if (rc != -ESHUTDOWN &&
+ !(rc == -EOPNOTSUPP && req->length == 0))
+ WARNING(fsg, "error in submission: %s --> %d\n",
+ ep->name, rc);
+ }
+}
+
+static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ if (!fsg_is_set(common))
+ return false;
+ start_transfer(common->fsg, common->fsg->bulk_in,
+ bh->inreq, &bh->inreq_busy, &bh->state);
+ return true;
+}
+
+static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ if (!fsg_is_set(common))
+ return false;
+ start_transfer(common->fsg, common->fsg->bulk_out,
+ bh->outreq, &bh->outreq_busy, &bh->state);
+ return true;
+}
+
+static int sleep_thread(struct fsg_common *common)
+{
+ int rc = 0;
+
+ /* Wait until a signal arrives or we are woken up */
+ for (;;) {
+ try_to_freeze();
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current)) {
+ rc = -EINTR;
+ break;
+ }
+ if (common->thread_wakeup_needed)
+ break;
+ schedule();
+ }
+ __set_current_state(TASK_RUNNING);
+ common->thread_wakeup_needed = 0;
+ return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_read(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u32 lba;
+ struct fsg_buffhd *bh;
+ int rc;
+ u32 amount_left;
+ loff_t file_offset, file_offset_tmp;
+ unsigned int amount;
+ ssize_t nread;
+
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big.
+ */
+ if (common->cmnd[0] == READ_6)
+ lba = get_unaligned_be24(&common->cmnd[1]);
+ else {
+ lba = get_unaligned_be32(&common->cmnd[2]);
+
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the
+ * cache) and FUA (Force Unit Access = don't read from the
+ * cache), but we don't implement them.
+ */
+ if ((common->cmnd[1] & ~0x18) != 0) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ }
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+ file_offset = ((loff_t) lba) << curlun->blkbits;
+
+ /* Carry out the file reads */
+ amount_left = common->data_size_from_cmnd;
+ if (unlikely(amount_left == 0))
+ return -EIO; /* No default reply */
+
+ for (;;) {
+ /*
+ * Figure out how much we need to read:
+ * Try to read the remaining amount.
+ * But don't read more than the buffer size.
+ * And don't try to read past the end of the file.
+ */
+ amount = min(amount_left, FSG_BUFLEN);
+ amount = min((loff_t)amount,
+ curlun->file_length - file_offset);
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * If we were asked to read past the end of file,
+ * end with an empty buffer.
+ */
+ if (amount == 0) {
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ bh->inreq->length = 0;
+ bh->state = BUF_STATE_FULL;
+ break;
+ }
+
+ /* Perform the read */
+ file_offset_tmp = file_offset;
+ nread = vfs_read(curlun->filp,
+ (char __user *)bh->buf,
+ amount, &file_offset_tmp);
+ VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+ (unsigned long long)file_offset, (int)nread);
+ if (signal_pending(current))
+ return -EINTR;
+
+ if (nread < 0) {
+ LDBG(curlun, "error in file read: %d\n", (int)nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun, "partial file read: %d/%u\n",
+ (int)nread, amount);
+ nread = round_down(nread, curlun->blksize);
+ }
+ file_offset += nread;
+ amount_left -= nread;
+ common->residue -= nread;
+
+ /*
+ * Except at the end of the transfer, nread will be
+ * equal to the buffer size, which is divisible by the
+ * bulk-in maxpacket size.
+ */
+ bh->inreq->length = nread;
+ bh->state = BUF_STATE_FULL;
+
+ /* If an error occurred, report it and its position */
+ if (nread < amount) {
+ curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ if (amount_left == 0)
+ break; /* No more left to read */
+
+ /* Send this buffer and go read some more */
+ bh->inreq->zero = 0;
+ if (!start_in_transfer(common, bh))
+ /* Don't know what to do if common->fsg is NULL */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ }
+
+ return -EIO; /* No default reply */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_write(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u32 lba;
+ struct fsg_buffhd *bh;
+ int get_some_more;
+ u32 amount_left_to_req, amount_left_to_write;
+ loff_t usb_offset, file_offset, file_offset_tmp;
+ unsigned int amount;
+ ssize_t nwritten;
+ int rc;
+
+ if (curlun->ro) {
+ curlun->sense_data = SS_WRITE_PROTECTED;
+ return -EINVAL;
+ }
+ spin_lock(&curlun->filp->f_lock);
+ curlun->filp->f_flags &= ~O_SYNC; /* Default is not to wait */
+ spin_unlock(&curlun->filp->f_lock);
+
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big
+ */
+ if (common->cmnd[0] == WRITE_6)
+ lba = get_unaligned_be24(&common->cmnd[1]);
+ else {
+ lba = get_unaligned_be32(&common->cmnd[2]);
+
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the
+ * cache) and FUA (Force Unit Access = write directly to the
+ * medium). We don't implement DPO; we implement FUA by
+ * performing synchronous output.
+ */
+ if (common->cmnd[1] & ~0x18) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ if (!curlun->nofua && (common->cmnd[1] & 0x08)) { /* FUA */
+ spin_lock(&curlun->filp->f_lock);
+ curlun->filp->f_flags |= O_SYNC;
+ spin_unlock(&curlun->filp->f_lock);
+ }
+ }
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ /* Carry out the file writes */
+ get_some_more = 1;
+ file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits;
+ amount_left_to_req = common->data_size_from_cmnd;
+ amount_left_to_write = common->data_size_from_cmnd;
+
+ while (amount_left_to_write > 0) {
+
+ /* Queue a request for more data from the host */
+ bh = common->next_buffhd_to_fill;
+ if (bh->state == BUF_STATE_EMPTY && get_some_more) {
+
+ /*
+ * Figure out how much we want to get:
+ * Try to get the remaining amount,
+ * but not more than the buffer size.
+ */
+ amount = min(amount_left_to_req, FSG_BUFLEN);
+
+ /* Beyond the end of the backing file? */
+ if (usb_offset >= curlun->file_length) {
+ get_some_more = 0;
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->sense_data_info =
+ usb_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ continue;
+ }
+
+ /* Get the next buffer */
+ usb_offset += amount;
+ common->usb_amount_left -= amount;
+ amount_left_to_req -= amount;
+ if (amount_left_to_req == 0)
+ get_some_more = 0;
+
+ /*
+ * Except at the end of the transfer, amount will be
+ * equal to the buffer size, which is divisible by
+ * the bulk-out maxpacket size.
+ */
+ set_bulk_out_req_length(common, bh, amount);
+ if (!start_out_transfer(common, bh))
+ /* Dunno what to do if common->fsg is NULL */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ continue;
+ }
+
+ /* Write the received data to the backing file */
+ bh = common->next_buffhd_to_drain;
+ if (bh->state == BUF_STATE_EMPTY && !get_some_more)
+ break; /* We stopped early */
+ if (bh->state == BUF_STATE_FULL) {
+ smp_rmb();
+ common->next_buffhd_to_drain = bh->next;
+ bh->state = BUF_STATE_EMPTY;
+
+ /* Did something go wrong with the transfer? */
+ if (bh->outreq->status != 0) {
+ curlun->sense_data = SS_COMMUNICATION_FAILURE;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ amount = bh->outreq->actual;
+ if (curlun->file_length - file_offset < amount) {
+ LERROR(curlun,
+ "write %u @ %llu beyond end %llu\n",
+ amount, (unsigned long long)file_offset,
+ (unsigned long long)curlun->file_length);
+ amount = curlun->file_length - file_offset;
+ }
+
+ /* Don't accept excess data. The spec doesn't say
+ * what to do in this case. We'll ignore the error.
+ */
+ amount = min(amount, bh->bulk_out_intended_length);
+
+ /* Don't write a partial block */
+ amount = round_down(amount, curlun->blksize);
+ if (amount == 0)
+ goto empty_write;
+
+ /* Perform the write */
+ file_offset_tmp = file_offset;
+ nwritten = vfs_write(curlun->filp,
+ (char __user *)bh->buf,
+ amount, &file_offset_tmp);
+ VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
+ (unsigned long long)file_offset, (int)nwritten);
+ if (signal_pending(current))
+ return -EINTR; /* Interrupted! */
+
+ if (nwritten < 0) {
+ LDBG(curlun, "error in file write: %d\n",
+ (int)nwritten);
+ nwritten = 0;
+ } else if (nwritten < amount) {
+ LDBG(curlun, "partial file write: %d/%u\n",
+ (int)nwritten, amount);
+ nwritten = round_down(nwritten, curlun->blksize);
+ }
+ file_offset += nwritten;
+ amount_left_to_write -= nwritten;
+ common->residue -= nwritten;
+
+ /* If an error occurred, report it and its position */
+ if (nwritten < amount) {
+ curlun->sense_data = SS_WRITE_ERROR;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ empty_write:
+ /* Did the host decide to stop early? */
+ if (bh->outreq->actual < bh->bulk_out_intended_length) {
+ common->short_packet_received = 1;
+ break;
+ }
+ continue;
+ }
+
+ /* Wait for something to happen */
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ return -EIO; /* No default reply */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_synchronize_cache(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int rc;
+
+ /* We ignore the requested LBA and write out all file's
+ * dirty data buffers. */
+ rc = fsg_lun_fsync_sub(curlun);
+ if (rc)
+ curlun->sense_data = SS_WRITE_ERROR;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void invalidate_sub(struct fsg_lun *curlun)
+{
+ struct file *filp = curlun->filp;
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ unsigned long rc;
+
+ rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
+ VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc);
+}
+
+static int do_verify(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u32 lba;
+ u32 verification_length;
+ struct fsg_buffhd *bh = common->next_buffhd_to_fill;
+ loff_t file_offset, file_offset_tmp;
+ u32 amount_left;
+ unsigned int amount;
+ ssize_t nread;
+
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big.
+ */
+ lba = get_unaligned_be32(&common->cmnd[2]);
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the
+ * cache) but we don't implement it.
+ */
+ if (common->cmnd[1] & ~0x10) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ verification_length = get_unaligned_be16(&common->cmnd[7]);
+ if (unlikely(verification_length == 0))
+ return -EIO; /* No default reply */
+
+ /* Prepare to carry out the file verify */
+ amount_left = verification_length << curlun->blkbits;
+ file_offset = ((loff_t) lba) << curlun->blkbits;
+
+ /* Write out all the dirty buffers before invalidating them */
+ fsg_lun_fsync_sub(curlun);
+ if (signal_pending(current))
+ return -EINTR;
+
+ invalidate_sub(curlun);
+ if (signal_pending(current))
+ return -EINTR;
+
+ /* Just try to read the requested blocks */
+ while (amount_left > 0) {
+ /*
+ * Figure out how much we need to read:
+ * Try to read the remaining amount, but not more than
+ * the buffer size.
+ * And don't try to read past the end of the file.
+ */
+ amount = min(amount_left, FSG_BUFLEN);
+ amount = min((loff_t)amount,
+ curlun->file_length - file_offset);
+ if (amount == 0) {
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ /* Perform the read */
+ file_offset_tmp = file_offset;
+ nread = vfs_read(curlun->filp,
+ (char __user *) bh->buf,
+ amount, &file_offset_tmp);
+ VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+ (unsigned long long) file_offset,
+ (int) nread);
+ if (signal_pending(current))
+ return -EINTR;
+
+ if (nread < 0) {
+ LDBG(curlun, "error in file verify: %d\n", (int)nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun, "partial file verify: %d/%u\n",
+ (int)nread, amount);
+ nread = round_down(nread, curlun->blksize);
+ }
+ if (nread == 0) {
+ curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ break;
+ }
+ file_offset += nread;
+ amount_left -= nread;
+ }
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u8 *buf = (u8 *) bh->buf;
+
+ if (!curlun) { /* Unsupported LUNs are okay */
+ common->bad_lun_okay = 1;
+ memset(buf, 0, 36);
+ buf[0] = 0x7f; /* Unsupported, no device-type */
+ buf[4] = 31; /* Additional length */
+ return 36;
+ }
+
+ buf[0] = curlun->cdrom ? TYPE_ROM : TYPE_DISK;
+ buf[1] = curlun->removable ? 0x80 : 0;
+ buf[2] = 2; /* ANSI SCSI level 2 */
+ buf[3] = 2; /* SCSI-2 INQUIRY data format */
+ buf[4] = 31; /* Additional length */
+ buf[5] = 0; /* No special options */
+ buf[6] = 0;
+ buf[7] = 0;
+ memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string);
+ return 36;
+}
+
+static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u8 *buf = (u8 *) bh->buf;
+ u32 sd, sdinfo;
+ int valid;
+
+ /*
+ * From the SCSI-2 spec., section 7.9 (Unit attention condition):
+ *
+ * If a REQUEST SENSE command is received from an initiator
+ * with a pending unit attention condition (before the target
+ * generates the contingent allegiance condition), then the
+ * target shall either:
+ * a) report any pending sense data and preserve the unit
+ * attention condition on the logical unit, or,
+ * b) report the unit attention condition, may discard any
+ * pending sense data, and clear the unit attention
+ * condition on the logical unit for that initiator.
+ *
+ * FSG normally uses option a); enable this code to use option b).
+ */
+#if 0
+ if (curlun && curlun->unit_attention_data != SS_NO_SENSE) {
+ curlun->sense_data = curlun->unit_attention_data;
+ curlun->unit_attention_data = SS_NO_SENSE;
+ }
+#endif
+
+ if (!curlun) { /* Unsupported LUNs are okay */
+ common->bad_lun_okay = 1;
+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ sdinfo = 0;
+ valid = 0;
+ } else {
+ sd = curlun->sense_data;
+ sdinfo = curlun->sense_data_info;
+ valid = curlun->info_valid << 7;
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
+ }
+
+ memset(buf, 0, 18);
+ buf[0] = valid | 0x70; /* Valid, current error */
+ buf[2] = SK(sd);
+ put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */
+ buf[7] = 18 - 8; /* Additional sense length */
+ buf[12] = ASC(sd);
+ buf[13] = ASCQ(sd);
+ return 18;
+}
+
+static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u32 lba = get_unaligned_be32(&common->cmnd[2]);
+ int pmi = common->cmnd[8];
+ u8 *buf = (u8 *)bh->buf;
+
+ /* Check the PMI and LBA fields */
+ if (pmi > 1 || (pmi == 0 && lba != 0)) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
+ /* Max logical block */
+ put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
+ return 8;
+}
+
+static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int msf = common->cmnd[1] & 0x02;
+ u32 lba = get_unaligned_be32(&common->cmnd[2]);
+ u8 *buf = (u8 *)bh->buf;
+
+ if (common->cmnd[1] & ~0x02) { /* Mask away MSF */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ memset(buf, 0, 8);
+ buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */
+ store_cdrom_address(&buf[4], msf, lba);
+ return 8;
+}
+
+static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int msf = common->cmnd[1] & 0x02;
+ int start_track = common->cmnd[6];
+ u8 *buf = (u8 *)bh->buf;
+
+ if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
+ start_track > 1) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ memset(buf, 0, 20);
+ buf[1] = (20-2); /* TOC data length */
+ buf[2] = 1; /* First track number */
+ buf[3] = 1; /* Last track number */
+ buf[5] = 0x16; /* Data track, copying allowed */
+ buf[6] = 0x01; /* Only track is number 1 */
+ store_cdrom_address(&buf[8], msf, 0);
+
+ buf[13] = 0x16; /* Lead-out track is data */
+ buf[14] = 0xAA; /* Lead-out track number */
+ store_cdrom_address(&buf[16], msf, curlun->num_sectors);
+ return 20;
+}
+
+static int do_mode_sense(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int mscmnd = common->cmnd[0];
+ u8 *buf = (u8 *) bh->buf;
+ u8 *buf0 = buf;
+ int pc, page_code;
+ int changeable_values, all_pages;
+ int valid_page = 0;
+ int len, limit;
+
+ if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ pc = common->cmnd[2] >> 6;
+ page_code = common->cmnd[2] & 0x3f;
+ if (pc == 3) {
+ curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+ return -EINVAL;
+ }
+ changeable_values = (pc == 1);
+ all_pages = (page_code == 0x3f);
+
+ /*
+ * Write the mode parameter header. Fixed values are: default
+ * medium type, no cache control (DPOFUA), and no block descriptors.
+ * The only variable value is the WriteProtect bit. We will fill in
+ * the mode data length later.
+ */
+ memset(buf, 0, 8);
+ if (mscmnd == MODE_SENSE) {
+ buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */
+ buf += 4;
+ limit = 255;
+ } else { /* MODE_SENSE_10 */
+ buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */
+ buf += 8;
+ limit = 65535; /* Should really be FSG_BUFLEN */
+ }
+
+ /* No block descriptors */
+
+ /*
+ * The mode pages, in numerical order. The only page we support
+ * is the Caching page.
+ */
+ if (page_code == 0x08 || all_pages) {
+ valid_page = 1;
+ buf[0] = 0x08; /* Page code */
+ buf[1] = 10; /* Page length */
+ memset(buf+2, 0, 10); /* None of the fields are changeable */
+
+ if (!changeable_values) {
+ buf[2] = 0x04; /* Write cache enable, */
+ /* Read cache not disabled */
+ /* No cache retention priorities */
+ put_unaligned_be16(0xffff, &buf[4]);
+ /* Don't disable prefetch */
+ /* Minimum prefetch = 0 */
+ put_unaligned_be16(0xffff, &buf[8]);
+ /* Maximum prefetch */
+ put_unaligned_be16(0xffff, &buf[10]);
+ /* Maximum prefetch ceiling */
+ }
+ buf += 12;
+ }
+
+ /*
+ * Check that a valid page was requested and the mode data length
+ * isn't too long.
+ */
+ len = buf - buf0;
+ if (!valid_page || len > limit) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ /* Store the mode data length */
+ if (mscmnd == MODE_SENSE)
+ buf0[0] = len - 1;
+ else
+ put_unaligned_be16(len - 2, buf0);
+ return len;
+}
+
+static int do_start_stop(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int loej, start;
+
+ if (!curlun) {
+ return -EINVAL;
+ } else if (!curlun->removable) {
+ curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+ } else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
+ (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ loej = common->cmnd[4] & 0x02;
+ start = common->cmnd[4] & 0x01;
+
+ /*
+ * Our emulation doesn't support mounting; the medium is
+ * available for use as soon as it is loaded.
+ */
+ if (start) {
+ if (!fsg_lun_is_open(curlun)) {
+ curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ /* Are we allowed to unload the media? */
+ if (curlun->prevent_medium_removal) {
+ LDBG(curlun, "unload attempt prevented\n");
+ curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
+ return -EINVAL;
+ }
+
+ if (!loej)
+ return 0;
+
+ /* Simulate an unload/eject */
+ if (common->ops && common->ops->pre_eject) {
+ int r = common->ops->pre_eject(common, curlun,
+ curlun - common->luns);
+ if (unlikely(r < 0))
+ return r;
+ else if (r)
+ return 0;
+ }
+
+ up_read(&common->filesem);
+ down_write(&common->filesem);
+ fsg_lun_close(curlun);
+ up_write(&common->filesem);
+ down_read(&common->filesem);
+
+ return common->ops && common->ops->post_eject
+ ? min(0, common->ops->post_eject(common, curlun,
+ curlun - common->luns))
+ : 0;
+}
+
+static int do_prevent_allow(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int prevent;
+
+ if (!common->curlun) {
+ return -EINVAL;
+ } else if (!common->curlun->removable) {
+ common->curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+ }
+
+ prevent = common->cmnd[4] & 0x01;
+ if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ if (curlun->prevent_medium_removal && !prevent)
+ fsg_lun_fsync_sub(curlun);
+ curlun->prevent_medium_removal = prevent;
+ return 0;
+}
+
+static int do_read_format_capacities(struct fsg_common *common,
+ struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u8 *buf = (u8 *) bh->buf;
+
+ buf[0] = buf[1] = buf[2] = 0;
+ buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */
+ buf += 4;
+
+ put_unaligned_be32(curlun->num_sectors, &buf[0]);
+ /* Number of blocks */
+ put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
+ buf[4] = 0x02; /* Current capacity */
+ return 12;
+}
+
+static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+
+ /* We don't support MODE SELECT */
+ if (curlun)
+ curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+ int rc;
+
+ rc = fsg_set_halt(fsg, fsg->bulk_in);
+ if (rc == -EAGAIN)
+ VDBG(fsg, "delayed bulk-in endpoint halt\n");
+ while (rc != 0) {
+ if (rc != -EAGAIN) {
+ WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
+ rc = 0;
+ break;
+ }
+
+ /* Wait for a short time and then try again */
+ if (msleep_interruptible(100) != 0)
+ return -EINTR;
+ rc = usb_ep_set_halt(fsg->bulk_in);
+ }
+ return rc;
+}
+
+static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+ int rc;
+
+ DBG(fsg, "bulk-in set wedge\n");
+ rc = usb_ep_set_wedge(fsg->bulk_in);
+ if (rc == -EAGAIN)
+ VDBG(fsg, "delayed bulk-in endpoint wedge\n");
+ while (rc != 0) {
+ if (rc != -EAGAIN) {
+ WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
+ rc = 0;
+ break;
+ }
+
+ /* Wait for a short time and then try again */
+ if (msleep_interruptible(100) != 0)
+ return -EINTR;
+ rc = usb_ep_set_wedge(fsg->bulk_in);
+ }
+ return rc;
+}
+
+static int throw_away_data(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ u32 amount;
+ int rc;
+
+ for (bh = common->next_buffhd_to_drain;
+ bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0;
+ bh = common->next_buffhd_to_drain) {
+
+ /* Throw away the data in a filled buffer */
+ if (bh->state == BUF_STATE_FULL) {
+ smp_rmb();
+ bh->state = BUF_STATE_EMPTY;
+ common->next_buffhd_to_drain = bh->next;
+
+ /* A short packet or an error ends everything */
+ if (bh->outreq->actual < bh->bulk_out_intended_length ||
+ bh->outreq->status != 0) {
+ raise_exception(common,
+ FSG_STATE_ABORT_BULK_OUT);
+ return -EINTR;
+ }
+ continue;
+ }
+
+ /* Try to submit another request if we need one */
+ bh = common->next_buffhd_to_fill;
+ if (bh->state == BUF_STATE_EMPTY
+ && common->usb_amount_left > 0) {
+ amount = min(common->usb_amount_left, FSG_BUFLEN);
+
+ /*
+ * Except at the end of the transfer, amount will be
+ * equal to the buffer size, which is divisible by
+ * the bulk-out maxpacket size.
+ */
+ set_bulk_out_req_length(common, bh, amount);
+ if (!start_out_transfer(common, bh))
+ /* Dunno what to do if common->fsg is NULL */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ common->usb_amount_left -= amount;
+ continue;
+ }
+
+ /* Otherwise wait for something to happen */
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+static int finish_reply(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh = common->next_buffhd_to_fill;
+ int rc = 0;
+
+ switch (common->data_dir) {
+ case DATA_DIR_NONE:
+ break; /* Nothing to send */
+
+ /*
+ * If we don't know whether the host wants to read or write,
+ * this must be CB or CBI with an unknown command. We mustn't
+ * try to send or receive any data. So stall both bulk pipes
+ * if we can and wait for a reset.
+ */
+ case DATA_DIR_UNKNOWN:
+ if (!common->can_stall) {
+ /* Nothing */
+ } else if (fsg_is_set(common)) {
+ fsg_set_halt(common->fsg, common->fsg->bulk_out);
+ rc = halt_bulk_in_endpoint(common->fsg);
+ } else {
+ /* Don't know what to do if common->fsg is NULL */
+ rc = -EIO;
+ }
+ break;
+
+ /* All but the last buffer of data must have already been sent */
+ case DATA_DIR_TO_HOST:
+ if (common->data_size == 0) {
+ /* Nothing to send */
+
+ /* Don't know what to do if common->fsg is NULL */
+ } else if (!fsg_is_set(common)) {
+ rc = -EIO;
+
+ /* If there's no residue, simply send the last buffer */
+ } else if (common->residue == 0) {
+ bh->inreq->zero = 0;
+ if (!start_in_transfer(common, bh))
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+
+ /*
+ * For Bulk-only, mark the end of the data with a short
+ * packet. If we are allowed to stall, halt the bulk-in
+ * endpoint. (Note: This violates the Bulk-Only Transport
+ * specification, which requires us to pad the data if we
+ * don't halt the endpoint. Presumably nobody will mind.)
+ */
+ } else {
+ bh->inreq->zero = 1;
+ if (!start_in_transfer(common, bh))
+ rc = -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ if (common->can_stall)
+ rc = halt_bulk_in_endpoint(common->fsg);
+ }
+ break;
+
+ /*
+ * We have processed all we want from the data the host has sent.
+ * There may still be outstanding bulk-out requests.
+ */
+ case DATA_DIR_FROM_HOST:
+ if (common->residue == 0) {
+ /* Nothing to receive */
+
+ /* Did the host stop sending unexpectedly early? */
+ } else if (common->short_packet_received) {
+ raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+ rc = -EINTR;
+
+ /*
+ * We haven't processed all the incoming data. Even though
+ * we may be allowed to stall, doing so would cause a race.
+ * The controller may already have ACK'ed all the remaining
+ * bulk-out packets, in which case the host wouldn't see a
+ * STALL. Not realizing the endpoint was halted, it wouldn't
+ * clear the halt -- leading to problems later on.
+ */
+#if 0
+ } else if (common->can_stall) {
+ if (fsg_is_set(common))
+ fsg_set_halt(common->fsg,
+ common->fsg->bulk_out);
+ raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+ rc = -EINTR;
+#endif
+
+ /*
+ * We can't stall. Read in the excess data and throw it
+ * all away.
+ */
+ } else {
+ rc = throw_away_data(common);
+ }
+ break;
+ }
+ return rc;
+}
+
+static int send_status(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ struct fsg_buffhd *bh;
+ struct bulk_cs_wrap *csw;
+ int rc;
+ u8 status = US_BULK_STAT_OK;
+ u32 sd, sdinfo = 0;
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ if (curlun) {
+ sd = curlun->sense_data;
+ sdinfo = curlun->sense_data_info;
+ } else if (common->bad_lun_okay)
+ sd = SS_NO_SENSE;
+ else
+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+
+ if (common->phase_error) {
+ DBG(common, "sending phase-error status\n");
+ status = US_BULK_STAT_PHASE;
+ sd = SS_INVALID_COMMAND;
+ } else if (sd != SS_NO_SENSE) {
+ DBG(common, "sending command-failure status\n");
+ status = US_BULK_STAT_FAIL;
+ VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
+ " info x%x\n",
+ SK(sd), ASC(sd), ASCQ(sd), sdinfo);
+ }
+
+ /* Store and send the Bulk-only CSW */
+ csw = (void *)bh->buf;
+
+ csw->Signature = cpu_to_le32(US_BULK_CS_SIGN);
+ csw->Tag = common->tag;
+ csw->Residue = cpu_to_le32(common->residue);
+ csw->Status = status;
+
+ bh->inreq->length = US_BULK_CS_WRAP_LEN;
+ bh->inreq->zero = 0;
+ if (!start_in_transfer(common, bh))
+ /* Don't know what to do if common->fsg is NULL */
+ return -EIO;
+
+ common->next_buffhd_to_fill = bh->next;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Check whether the command is properly formed and whether its data size
+ * and direction agree with the values we already have.
+ */
+static int check_command(struct fsg_common *common, int cmnd_size,
+ enum data_direction data_dir, unsigned int mask,
+ int needs_medium, const char *name)
+{
+ int i;
+ int lun = common->cmnd[1] >> 5;
+ static const char dirletter[4] = {'u', 'o', 'i', 'n'};
+ char hdlen[20];
+ struct fsg_lun *curlun;
+
+ hdlen[0] = 0;
+ if (common->data_dir != DATA_DIR_UNKNOWN)
+ sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir],
+ common->data_size);
+ VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n",
+ name, cmnd_size, dirletter[(int) data_dir],
+ common->data_size_from_cmnd, common->cmnd_size, hdlen);
+
+ /*
+ * We can't reply at all until we know the correct data direction
+ * and size.
+ */
+ if (common->data_size_from_cmnd == 0)
+ data_dir = DATA_DIR_NONE;
+ if (common->data_size < common->data_size_from_cmnd) {
+ /*
+ * Host data size < Device data size is a phase error.
+ * Carry out the command, but only transfer as much as
+ * we are allowed.
+ */
+ common->data_size_from_cmnd = common->data_size;
+ common->phase_error = 1;
+ }
+ common->residue = common->data_size;
+ common->usb_amount_left = common->data_size;
+
+ /* Conflicting data directions is a phase error */
+ if (common->data_dir != data_dir && common->data_size_from_cmnd > 0) {
+ common->phase_error = 1;
+ return -EINVAL;
+ }
+
+ /* Verify the length of the command itself */
+ if (cmnd_size != common->cmnd_size) {
+
+ /*
+ * Special case workaround: There are plenty of buggy SCSI
+ * implementations. Many have issues with cbw->Length
+ * field passing a wrong command size. For those cases we
+ * always try to work around the problem by using the length
+ * sent by the host side provided it is at least as large
+ * as the correct command length.
+ * Examples of such cases would be MS-Windows, which issues
+ * REQUEST SENSE with cbw->Length == 12 where it should
+ * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and
+ * REQUEST SENSE with cbw->Length == 10 where it should
+ * be 6 as well.
+ */
+ if (cmnd_size <= common->cmnd_size) {
+ DBG(common, "%s is buggy! Expected length %d "
+ "but we got %d\n", name,
+ cmnd_size, common->cmnd_size);
+ cmnd_size = common->cmnd_size;
+ } else {
+ common->phase_error = 1;
+ return -EINVAL;
+ }
+ }
+
+ /* Check that the LUN values are consistent */
+ if (common->lun != lun)
+ DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n",
+ common->lun, lun);
+
+ /* Check the LUN */
+ curlun = common->curlun;
+ if (curlun) {
+ if (common->cmnd[0] != REQUEST_SENSE) {
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
+ }
+ } else {
+ common->bad_lun_okay = 0;
+
+ /*
+ * INQUIRY and REQUEST SENSE commands are explicitly allowed
+ * to use unsupported LUNs; all others may not.
+ */
+ if (common->cmnd[0] != INQUIRY &&
+ common->cmnd[0] != REQUEST_SENSE) {
+ DBG(common, "unsupported LUN %d\n", common->lun);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * If a unit attention condition exists, only INQUIRY and
+ * REQUEST SENSE commands are allowed; anything else must fail.
+ */
+ if (curlun && curlun->unit_attention_data != SS_NO_SENSE &&
+ common->cmnd[0] != INQUIRY &&
+ common->cmnd[0] != REQUEST_SENSE) {
+ curlun->sense_data = curlun->unit_attention_data;
+ curlun->unit_attention_data = SS_NO_SENSE;
+ return -EINVAL;
+ }
+
+ /* Check that only command bytes listed in the mask are non-zero */
+ common->cmnd[1] &= 0x1f; /* Mask away the LUN */
+ for (i = 1; i < cmnd_size; ++i) {
+ if (common->cmnd[i] && !(mask & (1 << i))) {
+ if (curlun)
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ }
+
+ /* If the medium isn't mounted and the command needs to access
+ * it, return an error. */
+ if (curlun && !fsg_lun_is_open(curlun) && needs_medium) {
+ curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* wrapper of check_command for data size in blocks handling */
+static int check_command_size_in_blocks(struct fsg_common *common,
+ int cmnd_size, enum data_direction data_dir,
+ unsigned int mask, int needs_medium, const char *name)
+{
+ if (common->curlun)
+ common->data_size_from_cmnd <<= common->curlun->blkbits;
+ return check_command(common, cmnd_size, data_dir,
+ mask, needs_medium, name);
+}
+
+static int do_scsi_command(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ int rc;
+ int reply = -EINVAL;
+ int i;
+ static char unknown[16];
+
+ dump_cdb(common);
+
+ /* Wait for the next buffer to become available for data or status */
+ bh = common->next_buffhd_to_fill;
+ common->next_buffhd_to_drain = bh;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+ common->phase_error = 0;
+ common->short_packet_received = 0;
+
+ down_read(&common->filesem); /* We're using the backing file */
+ switch (common->cmnd[0]) {
+
+ case INQUIRY:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<4), 0,
+ "INQUIRY");
+ if (reply == 0)
+ reply = do_inquiry(common, bh);
+ break;
+
+ case MODE_SELECT:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_FROM_HOST,
+ (1<<1) | (1<<4), 0,
+ "MODE SELECT(6)");
+ if (reply == 0)
+ reply = do_mode_select(common, bh);
+ break;
+
+ case MODE_SELECT_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_FROM_HOST,
+ (1<<1) | (3<<7), 0,
+ "MODE SELECT(10)");
+ if (reply == 0)
+ reply = do_mode_select(common, bh);
+ break;
+
+ case MODE_SENSE:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<1) | (1<<2) | (1<<4), 0,
+ "MODE SENSE(6)");
+ if (reply == 0)
+ reply = do_mode_sense(common, bh);
+ break;
+
+ case MODE_SENSE_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (1<<1) | (1<<2) | (3<<7), 0,
+ "MODE SENSE(10)");
+ if (reply == 0)
+ reply = do_mode_sense(common, bh);
+ break;
+
+ case ALLOW_MEDIUM_REMOVAL:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ (1<<4), 0,
+ "PREVENT-ALLOW MEDIUM REMOVAL");
+ if (reply == 0)
+ reply = do_prevent_allow(common);
+ break;
+
+ case READ_6:
+ i = common->cmnd[4];
+ common->data_size_from_cmnd = (i == 0) ? 256 : i;
+ reply = check_command_size_in_blocks(common, 6,
+ DATA_DIR_TO_HOST,
+ (7<<1) | (1<<4), 1,
+ "READ(6)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case READ_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command_size_in_blocks(common, 10,
+ DATA_DIR_TO_HOST,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "READ(10)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case READ_12:
+ common->data_size_from_cmnd =
+ get_unaligned_be32(&common->cmnd[6]);
+ reply = check_command_size_in_blocks(common, 12,
+ DATA_DIR_TO_HOST,
+ (1<<1) | (0xf<<2) | (0xf<<6), 1,
+ "READ(12)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case READ_CAPACITY:
+ common->data_size_from_cmnd = 8;
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (0xf<<2) | (1<<8), 1,
+ "READ CAPACITY");
+ if (reply == 0)
+ reply = do_read_capacity(common, bh);
+ break;
+
+ case READ_HEADER:
+ if (!common->curlun || !common->curlun->cdrom)
+ goto unknown_cmnd;
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (3<<7) | (0x1f<<1), 1,
+ "READ HEADER");
+ if (reply == 0)
+ reply = do_read_header(common, bh);
+ break;
+
+ case READ_TOC:
+ if (!common->curlun || !common->curlun->cdrom)
+ goto unknown_cmnd;
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (7<<6) | (1<<1), 1,
+ "READ TOC");
+ if (reply == 0)
+ reply = do_read_toc(common, bh);
+ break;
+
+ case READ_FORMAT_CAPACITIES:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (3<<7), 1,
+ "READ FORMAT CAPACITIES");
+ if (reply == 0)
+ reply = do_read_format_capacities(common, bh);
+ break;
+
+ case REQUEST_SENSE:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<4), 0,
+ "REQUEST SENSE");
+ if (reply == 0)
+ reply = do_request_sense(common, bh);
+ break;
+
+ case START_STOP:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ (1<<1) | (1<<4), 0,
+ "START-STOP UNIT");
+ if (reply == 0)
+ reply = do_start_stop(common);
+ break;
+
+ case SYNCHRONIZE_CACHE:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 10, DATA_DIR_NONE,
+ (0xf<<2) | (3<<7), 1,
+ "SYNCHRONIZE CACHE");
+ if (reply == 0)
+ reply = do_synchronize_cache(common);
+ break;
+
+ case TEST_UNIT_READY:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ 0, 1,
+ "TEST UNIT READY");
+ break;
+
+ /*
+ * Although optional, this command is used by MS-Windows. We
+ * support a minimal version: BytChk must be 0.
+ */
+ case VERIFY:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 10, DATA_DIR_NONE,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "VERIFY");
+ if (reply == 0)
+ reply = do_verify(common);
+ break;
+
+ case WRITE_6:
+ i = common->cmnd[4];
+ common->data_size_from_cmnd = (i == 0) ? 256 : i;
+ reply = check_command_size_in_blocks(common, 6,
+ DATA_DIR_FROM_HOST,
+ (7<<1) | (1<<4), 1,
+ "WRITE(6)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ case WRITE_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command_size_in_blocks(common, 10,
+ DATA_DIR_FROM_HOST,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "WRITE(10)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ case WRITE_12:
+ common->data_size_from_cmnd =
+ get_unaligned_be32(&common->cmnd[6]);
+ reply = check_command_size_in_blocks(common, 12,
+ DATA_DIR_FROM_HOST,
+ (1<<1) | (0xf<<2) | (0xf<<6), 1,
+ "WRITE(12)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ /*
+ * Some mandatory commands that we recognize but don't implement.
+ * They don't mean much in this setting. It's left as an exercise
+ * for anyone interested to implement RESERVE and RELEASE in terms
+ * of Posix locks.
+ */
+ case FORMAT_UNIT:
+ case RELEASE:
+ case RESERVE:
+ case SEND_DIAGNOSTIC:
+ /* Fall through */
+
+ default:
+unknown_cmnd:
+ common->data_size_from_cmnd = 0;
+ sprintf(unknown, "Unknown x%02x", common->cmnd[0]);
+ reply = check_command(common, common->cmnd_size,
+ DATA_DIR_UNKNOWN, ~0, 0, unknown);
+ if (reply == 0) {
+ common->curlun->sense_data = SS_INVALID_COMMAND;
+ reply = -EINVAL;
+ }
+ break;
+ }
+ up_read(&common->filesem);
+
+ if (reply == -EINTR || signal_pending(current))
+ return -EINTR;
+
+ /* Set up the single reply buffer for finish_reply() */
+ if (reply == -EINVAL)
+ reply = 0; /* Error reply length */
+ if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) {
+ reply = min((u32)reply, common->data_size_from_cmnd);
+ bh->inreq->length = reply;
+ bh->state = BUF_STATE_FULL;
+ common->residue -= reply;
+ } /* Otherwise it's already set */
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+ struct usb_request *req = bh->outreq;
+ struct bulk_cb_wrap *cbw = req->buf;
+ struct fsg_common *common = fsg->common;
+
+ /* Was this a real packet? Should it be ignored? */
+ if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
+ return -EINVAL;
+
+ /* Is the CBW valid? */
+ if (req->actual != US_BULK_CB_WRAP_LEN ||
+ cbw->Signature != cpu_to_le32(
+ US_BULK_CB_SIGN)) {
+ DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
+ req->actual,
+ le32_to_cpu(cbw->Signature));
+
+ /*
+ * The Bulk-only spec says we MUST stall the IN endpoint
+ * (6.6.1), so it's unavoidable. It also says we must
+ * retain this state until the next reset, but there's
+ * no way to tell the controller driver it should ignore
+ * Clear-Feature(HALT) requests.
+ *
+ * We aren't required to halt the OUT endpoint; instead
+ * we can simply accept and discard any data received
+ * until the next reset.
+ */
+ wedge_bulk_in_endpoint(fsg);
+ set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+ return -EINVAL;
+ }
+
+ /* Is the CBW meaningful? */
+ if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN ||
+ cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
+ DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
+ "cmdlen %u\n",
+ cbw->Lun, cbw->Flags, cbw->Length);
+
+ /*
+ * We can do anything we want here, so let's stall the
+ * bulk pipes if we are allowed to.
+ */
+ if (common->can_stall) {
+ fsg_set_halt(fsg, fsg->bulk_out);
+ halt_bulk_in_endpoint(fsg);
+ }
+ return -EINVAL;
+ }
+
+ /* Save the command for later */
+ common->cmnd_size = cbw->Length;
+ memcpy(common->cmnd, cbw->CDB, common->cmnd_size);
+ if (cbw->Flags & US_BULK_FLAG_IN)
+ common->data_dir = DATA_DIR_TO_HOST;
+ else
+ common->data_dir = DATA_DIR_FROM_HOST;
+ common->data_size = le32_to_cpu(cbw->DataTransferLength);
+ if (common->data_size == 0)
+ common->data_dir = DATA_DIR_NONE;
+ common->lun = cbw->Lun;
+ if (common->lun >= 0 && common->lun < common->nluns)
+ common->curlun = &common->luns[common->lun];
+ else
+ common->curlun = NULL;
+ common->tag = cbw->Tag;
+ return 0;
+}
+
+static int get_next_command(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ int rc = 0;
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ /* Queue a request to read a Bulk-only CBW */
+ set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN);
+ if (!start_out_transfer(common, bh))
+ /* Don't know what to do if common->fsg is NULL */
+ return -EIO;
+
+ /*
+ * We will drain the buffer in software, which means we
+ * can reuse it for the next filling. No need to advance
+ * next_buffhd_to_fill.
+ */
+
+ /* Wait for the CBW to arrive */
+ while (bh->state != BUF_STATE_FULL) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+ smp_rmb();
+ rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO;
+ bh->state = BUF_STATE_EMPTY;
+
+ return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int alloc_request(struct fsg_common *common, struct usb_ep *ep,
+ struct usb_request **preq)
+{
+ *preq = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (*preq)
+ return 0;
+ ERROR(common, "can't allocate request for %s\n", ep->name);
+ return -ENOMEM;
+}
+
+/* Reset interface setting and re-init endpoint state (toggle etc). */
+static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg)
+{
+ struct fsg_dev *fsg;
+ int i, rc = 0;
+
+ if (common->running)
+ DBG(common, "reset interface\n");
+
+reset:
+ /* Deallocate the requests */
+ if (common->fsg) {
+ fsg = common->fsg;
+
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ struct fsg_buffhd *bh = &common->buffhds[i];
+
+ if (bh->inreq) {
+ usb_ep_free_request(fsg->bulk_in, bh->inreq);
+ bh->inreq = NULL;
+ }
+ if (bh->outreq) {
+ usb_ep_free_request(fsg->bulk_out, bh->outreq);
+ bh->outreq = NULL;
+ }
+ }
+
+ /* Disable the endpoints */
+ if (fsg->bulk_in_enabled) {
+ usb_ep_disable(fsg->bulk_in);
+ fsg->bulk_in_enabled = 0;
+ }
+ if (fsg->bulk_out_enabled) {
+ usb_ep_disable(fsg->bulk_out);
+ fsg->bulk_out_enabled = 0;
+ }
+
+ common->fsg = NULL;
+ wake_up(&common->fsg_wait);
+ }
+
+ common->running = 0;
+ if (!new_fsg || rc)
+ return rc;
+
+ common->fsg = new_fsg;
+ fsg = common->fsg;
+
+ /* Enable the endpoints */
+ rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in);
+ if (rc)
+ goto reset;
+ rc = usb_ep_enable(fsg->bulk_in);
+ if (rc)
+ goto reset;
+ fsg->bulk_in->driver_data = common;
+ fsg->bulk_in_enabled = 1;
+
+ rc = config_ep_by_speed(common->gadget, &(fsg->function),
+ fsg->bulk_out);
+ if (rc)
+ goto reset;
+ rc = usb_ep_enable(fsg->bulk_out);
+ if (rc)
+ goto reset;
+ fsg->bulk_out->driver_data = common;
+ fsg->bulk_out_enabled = 1;
+ common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc);
+ clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+
+ /* Allocate the requests */
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ struct fsg_buffhd *bh = &common->buffhds[i];
+
+ rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
+ if (rc)
+ goto reset;
+ rc = alloc_request(common, fsg->bulk_out, &bh->outreq);
+ if (rc)
+ goto reset;
+ bh->inreq->buf = bh->outreq->buf = bh->buf;
+ bh->inreq->context = bh->outreq->context = bh;
+ bh->inreq->complete = bulk_in_complete;
+ bh->outreq->complete = bulk_out_complete;
+ }
+
+ common->running = 1;
+ for (i = 0; i < common->nluns; ++i)
+ common->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+ return rc;
+}
+
+
+/****************************** ALT CONFIGS ******************************/
+
+static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ fsg->common->new_fsg = fsg;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+ return USB_GADGET_DELAYED_STATUS;
+}
+
+static void fsg_disable(struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ fsg->common->new_fsg = NULL;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void handle_exception(struct fsg_common *common)
+{
+ siginfo_t info;
+ int i;
+ struct fsg_buffhd *bh;
+ enum fsg_state old_state;
+ struct fsg_lun *curlun;
+ unsigned int exception_req_tag;
+
+ /*
+ * Clear the existing signals. Anything but SIGUSR1 is converted
+ * into a high-priority EXIT exception.
+ */
+ for (;;) {
+ int sig =
+ dequeue_signal_lock(current, &current->blocked, &info);
+ if (!sig)
+ break;
+ if (sig != SIGUSR1) {
+ if (common->state < FSG_STATE_EXIT)
+ DBG(common, "Main thread exiting on signal\n");
+ raise_exception(common, FSG_STATE_EXIT);
+ }
+ }
+
+ /* Cancel all the pending transfers */
+ if (likely(common->fsg)) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ bh = &common->buffhds[i];
+ if (bh->inreq_busy)
+ usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
+ if (bh->outreq_busy)
+ usb_ep_dequeue(common->fsg->bulk_out,
+ bh->outreq);
+ }
+
+ /* Wait until everything is idle */
+ for (;;) {
+ int num_active = 0;
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ bh = &common->buffhds[i];
+ num_active += bh->inreq_busy + bh->outreq_busy;
+ }
+ if (num_active == 0)
+ break;
+ if (sleep_thread(common))
+ return;
+ }
+
+ /* Clear out the controller's fifos */
+ if (common->fsg->bulk_in_enabled)
+ usb_ep_fifo_flush(common->fsg->bulk_in);
+ if (common->fsg->bulk_out_enabled)
+ usb_ep_fifo_flush(common->fsg->bulk_out);
+ }
+
+ /*
+ * Reset the I/O buffer states and pointers, the SCSI
+ * state, and the exception. Then invoke the handler.
+ */
+ spin_lock_irq(&common->lock);
+
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ bh = &common->buffhds[i];
+ bh->state = BUF_STATE_EMPTY;
+ }
+ common->next_buffhd_to_fill = &common->buffhds[0];
+ common->next_buffhd_to_drain = &common->buffhds[0];
+ exception_req_tag = common->exception_req_tag;
+ old_state = common->state;
+
+ if (old_state == FSG_STATE_ABORT_BULK_OUT)
+ common->state = FSG_STATE_STATUS_PHASE;
+ else {
+ for (i = 0; i < common->nluns; ++i) {
+ curlun = &common->luns[i];
+ curlun->prevent_medium_removal = 0;
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->unit_attention_data = SS_NO_SENSE;
+ curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
+ }
+ common->state = FSG_STATE_IDLE;
+ }
+ spin_unlock_irq(&common->lock);
+
+ /* Carry out any extra actions required for the exception */
+ switch (old_state) {
+ case FSG_STATE_ABORT_BULK_OUT:
+ send_status(common);
+ spin_lock_irq(&common->lock);
+ if (common->state == FSG_STATE_STATUS_PHASE)
+ common->state = FSG_STATE_IDLE;
+ spin_unlock_irq(&common->lock);
+ break;
+
+ case FSG_STATE_RESET:
+ /*
+ * In case we were forced against our will to halt a
+ * bulk endpoint, clear the halt now. (The SuperH UDC
+ * requires this.)
+ */
+ if (!fsg_is_set(common))
+ break;
+ if (test_and_clear_bit(IGNORE_BULK_OUT,
+ &common->fsg->atomic_bitflags))
+ usb_ep_clear_halt(common->fsg->bulk_in);
+
+ if (common->ep0_req_tag == exception_req_tag)
+ ep0_queue(common); /* Complete the status stage */
+
+ /*
+ * Technically this should go here, but it would only be
+ * a waste of time. Ditto for the INTERFACE_CHANGE and
+ * CONFIG_CHANGE cases.
+ */
+ /* for (i = 0; i < common->nluns; ++i) */
+ /* common->luns[i].unit_attention_data = */
+ /* SS_RESET_OCCURRED; */
+ break;
+
+ case FSG_STATE_CONFIG_CHANGE:
+ do_set_interface(common, common->new_fsg);
+ if (common->new_fsg)
+ usb_composite_setup_continue(common->cdev);
+ break;
+
+ case FSG_STATE_EXIT:
+ case FSG_STATE_TERMINATED:
+ do_set_interface(common, NULL); /* Free resources */
+ spin_lock_irq(&common->lock);
+ common->state = FSG_STATE_TERMINATED; /* Stop the thread */
+ spin_unlock_irq(&common->lock);
+ break;
+
+ case FSG_STATE_INTERFACE_CHANGE:
+ case FSG_STATE_DISCONNECT:
+ case FSG_STATE_COMMAND_PHASE:
+ case FSG_STATE_DATA_PHASE:
+ case FSG_STATE_STATUS_PHASE:
+ case FSG_STATE_IDLE:
+ break;
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_main_thread(void *common_)
+{
+ struct fsg_common *common = common_;
+
+ /*
+ * Allow the thread to be killed by a signal, but set the signal mask
+ * to block everything but INT, TERM, KILL, and USR1.
+ */
+ allow_signal(SIGINT);
+ allow_signal(SIGTERM);
+ allow_signal(SIGKILL);
+ allow_signal(SIGUSR1);
+
+ /* Allow the thread to be frozen */
+ set_freezable();
+
+ /*
+ * Arrange for userspace references to be interpreted as kernel
+ * pointers. That way we can pass a kernel pointer to a routine
+ * that expects a __user pointer and it will work okay.
+ */
+ set_fs(get_ds());
+
+ /* The main loop */
+ while (common->state != FSG_STATE_TERMINATED) {
+ if (exception_in_progress(common) || signal_pending(current)) {
+ handle_exception(common);
+ continue;
+ }
+
+ if (!common->running) {
+ sleep_thread(common);
+ continue;
+ }
+
+ if (get_next_command(common))
+ continue;
+
+ spin_lock_irq(&common->lock);
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_DATA_PHASE;
+ spin_unlock_irq(&common->lock);
+
+ if (do_scsi_command(common) || finish_reply(common))
+ continue;
+
+ spin_lock_irq(&common->lock);
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_STATUS_PHASE;
+ spin_unlock_irq(&common->lock);
+
+ if (send_status(common))
+ continue;
+
+ spin_lock_irq(&common->lock);
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_IDLE;
+ spin_unlock_irq(&common->lock);
+ }
+
+ spin_lock_irq(&common->lock);
+ common->thread_task = NULL;
+ spin_unlock_irq(&common->lock);
+
+ if (!common->ops || !common->ops->thread_exits
+ || common->ops->thread_exits(common) < 0) {
+ struct fsg_lun *curlun = common->luns;
+ unsigned i = common->nluns;
+
+ down_write(&common->filesem);
+ for (; i--; ++curlun) {
+ if (!fsg_lun_is_open(curlun))
+ continue;
+
+ fsg_lun_close(curlun);
+ curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
+ }
+ up_write(&common->filesem);
+ }
+
+ /* Let fsg_unbind() know the thread has exited */
+ complete_and_exit(&common->thread_notifier, 0);
+}
+
+
+/*************************** DEVICE ATTRIBUTES ***************************/
+
+static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro);
+static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua);
+static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file);
+
+static struct device_attribute dev_attr_ro_cdrom =
+ __ATTR(ro, 0444, fsg_show_ro, NULL);
+static struct device_attribute dev_attr_file_nonremovable =
+ __ATTR(file, 0444, fsg_show_file, NULL);
+
+
+/****************************** FSG COMMON ******************************/
+
+static void fsg_common_release(struct kref *ref);
+
+static void fsg_lun_release(struct device *dev)
+{
+ /* Nothing needs to be done */
+}
+
+static inline void fsg_common_get(struct fsg_common *common)
+{
+ kref_get(&common->ref);
+}
+
+static inline void fsg_common_put(struct fsg_common *common)
+{
+ kref_put(&common->ref, fsg_common_release);
+}
+
+static struct fsg_common *fsg_common_init(struct fsg_common *common,
+ struct usb_composite_dev *cdev,
+ struct fsg_config *cfg)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct fsg_buffhd *bh;
+ struct fsg_lun *curlun;
+ struct fsg_lun_config *lcfg;
+ int nluns, i, rc;
+ char *pathbuf;
+
+ rc = fsg_num_buffers_validate();
+ if (rc != 0)
+ return ERR_PTR(rc);
+
+ /* Find out how many LUNs there should be */
+ nluns = cfg->nluns;
+ if (nluns < 1 || nluns > FSG_MAX_LUNS) {
+ dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Allocate? */
+ if (!common) {
+ common = kzalloc(sizeof *common, GFP_KERNEL);
+ if (!common)
+ return ERR_PTR(-ENOMEM);
+ common->free_storage_on_release = 1;
+ } else {
+ memset(common, 0, sizeof *common);
+ common->free_storage_on_release = 0;
+ }
+
+ common->buffhds = kcalloc(fsg_num_buffers,
+ sizeof *(common->buffhds), GFP_KERNEL);
+ if (!common->buffhds) {
+ if (common->free_storage_on_release)
+ kfree(common);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ common->ops = cfg->ops;
+ common->private_data = cfg->private_data;
+
+ common->gadget = gadget;
+ common->ep0 = gadget->ep0;
+ common->ep0req = cdev->req;
+ common->cdev = cdev;
+
+ /* Maybe allocate device-global string IDs, and patch descriptors */
+ if (fsg_strings[FSG_STRING_INTERFACE].id == 0) {
+ rc = usb_string_id(cdev);
+ if (unlikely(rc < 0))
+ goto error_release;
+ fsg_strings[FSG_STRING_INTERFACE].id = rc;
+ fsg_intf_desc.iInterface = rc;
+ }
+
+ /*
+ * Create the LUNs, open their backing files, and register the
+ * LUN devices in sysfs.
+ */
+ curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL);
+ if (unlikely(!curlun)) {
+ rc = -ENOMEM;
+ goto error_release;
+ }
+ common->luns = curlun;
+
+ init_rwsem(&common->filesem);
+
+ for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) {
+ curlun->cdrom = !!lcfg->cdrom;
+ curlun->ro = lcfg->cdrom || lcfg->ro;
+ curlun->initially_ro = curlun->ro;
+ curlun->removable = lcfg->removable;
+ curlun->dev.release = fsg_lun_release;
+ curlun->dev.parent = &gadget->dev;
+ /* curlun->dev.driver = &fsg_driver.driver; XXX */
+ dev_set_drvdata(&curlun->dev, &common->filesem);
+ dev_set_name(&curlun->dev, "lun%d", i);
+
+ rc = device_register(&curlun->dev);
+ if (rc) {
+ INFO(common, "failed to register LUN%d: %d\n", i, rc);
+ common->nluns = i;
+ put_device(&curlun->dev);
+ goto error_release;
+ }
+
+ rc = device_create_file(&curlun->dev,
+ curlun->cdrom
+ ? &dev_attr_ro_cdrom
+ : &dev_attr_ro);
+ if (rc)
+ goto error_luns;
+ rc = device_create_file(&curlun->dev,
+ curlun->removable
+ ? &dev_attr_file
+ : &dev_attr_file_nonremovable);
+ if (rc)
+ goto error_luns;
+ rc = device_create_file(&curlun->dev, &dev_attr_nofua);
+ if (rc)
+ goto error_luns;
+
+ if (lcfg->filename) {
+ rc = fsg_lun_open(curlun, lcfg->filename);
+ if (rc)
+ goto error_luns;
+ } else if (!curlun->removable) {
+ ERROR(common, "no file given for LUN%d\n", i);
+ rc = -EINVAL;
+ goto error_luns;
+ }
+ }
+ common->nluns = nluns;
+
+ /* Data buffers cyclic list */
+ bh = common->buffhds;
+ i = fsg_num_buffers;
+ goto buffhds_first_it;
+ do {
+ bh->next = bh + 1;
+ ++bh;
+buffhds_first_it:
+ bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
+ if (unlikely(!bh->buf)) {
+ rc = -ENOMEM;
+ goto error_release;
+ }
+ } while (--i);
+ bh->next = common->buffhds;
+
+ /* Prepare inquiryString */
+ if (cfg->release != 0xffff) {
+ i = cfg->release;
+ } else {
+ i = usb_gadget_controller_number(gadget);
+ if (i >= 0) {
+ i = 0x0300 + i;
+ } else {
+ WARNING(common, "controller '%s' not recognized\n",
+ gadget->name);
+ i = 0x0399;
+ }
+ }
+ snprintf(common->inquiry_string, sizeof common->inquiry_string,
+ "%-8s%-16s%04x", cfg->vendor_name ?: "Linux",
+ /* Assume product name dependent on the first LUN */
+ cfg->product_name ?: (common->luns->cdrom
+ ? "File-Stor Gadget"
+ : "File-CD Gadget"),
+ i);
+
+ /*
+ * Some peripheral controllers are known not to be able to
+ * halt bulk endpoints correctly. If one of them is present,
+ * disable stalls.
+ */
+ common->can_stall = cfg->can_stall &&
+ !(gadget_is_at91(common->gadget));
+
+ spin_lock_init(&common->lock);
+ kref_init(&common->ref);
+
+ /* Tell the thread to start working */
+ common->thread_task =
+ kthread_create(fsg_main_thread, common, "file-storage");
+ if (IS_ERR(common->thread_task)) {
+ rc = PTR_ERR(common->thread_task);
+ goto error_release;
+ }
+ init_completion(&common->thread_notifier);
+ init_waitqueue_head(&common->fsg_wait);
+
+ /* Information */
+ INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
+ INFO(common, "Number of LUNs=%d\n", common->nluns);
+
+ pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+ for (i = 0, nluns = common->nluns, curlun = common->luns;
+ i < nluns;
+ ++curlun, ++i) {
+ char *p = "(no medium)";
+ if (fsg_lun_is_open(curlun)) {
+ p = "(error)";
+ if (pathbuf) {
+ p = d_path(&curlun->filp->f_path,
+ pathbuf, PATH_MAX);
+ if (IS_ERR(p))
+ p = "(error)";
+ }
+ }
+ LINFO(curlun, "LUN: %s%s%sfile: %s\n",
+ curlun->removable ? "removable " : "",
+ curlun->ro ? "read only " : "",
+ curlun->cdrom ? "CD-ROM " : "",
+ p);
+ }
+ kfree(pathbuf);
+
+ DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
+
+ wake_up_process(common->thread_task);
+
+ return common;
+
+error_luns:
+ common->nluns = i + 1;
+error_release:
+ common->state = FSG_STATE_TERMINATED; /* The thread is dead */
+ /* Call fsg_common_release() directly, ref might be not initialised. */
+ fsg_common_release(&common->ref);
+ return ERR_PTR(rc);
+}
+
+static void fsg_common_release(struct kref *ref)
+{
+ struct fsg_common *common = container_of(ref, struct fsg_common, ref);
+
+ /* If the thread isn't already dead, tell it to exit now */
+ if (common->state != FSG_STATE_TERMINATED) {
+ raise_exception(common, FSG_STATE_EXIT);
+ wait_for_completion(&common->thread_notifier);
+ }
+
+ if (likely(common->luns)) {
+ struct fsg_lun *lun = common->luns;
+ unsigned i = common->nluns;
+
+ /* In error recovery common->nluns may be zero. */
+ for (; i; --i, ++lun) {
+ device_remove_file(&lun->dev, &dev_attr_nofua);
+ device_remove_file(&lun->dev,
+ lun->cdrom
+ ? &dev_attr_ro_cdrom
+ : &dev_attr_ro);
+ device_remove_file(&lun->dev,
+ lun->removable
+ ? &dev_attr_file
+ : &dev_attr_file_nonremovable);
+ fsg_lun_close(lun);
+ device_unregister(&lun->dev);
+ }
+
+ kfree(common->luns);
+ }
+
+ {
+ struct fsg_buffhd *bh = common->buffhds;
+ unsigned i = fsg_num_buffers;
+ do {
+ kfree(bh->buf);
+ } while (++bh, --i);
+ }
+
+ kfree(common->buffhds);
+ if (common->free_storage_on_release)
+ kfree(common);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct fsg_common *common = fsg->common;
+
+ DBG(fsg, "unbind\n");
+ if (fsg->common->fsg == fsg) {
+ fsg->common->new_fsg = NULL;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+ /* FIXME: make interruptible or killable somehow? */
+ wait_event(common->fsg_wait, common->fsg != fsg);
+ }
+
+ fsg_common_put(common);
+ usb_free_descriptors(fsg->function.descriptors);
+ usb_free_descriptors(fsg->function.hs_descriptors);
+ usb_free_descriptors(fsg->function.ss_descriptors);
+ kfree(fsg);
+}
+
+static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct usb_gadget *gadget = c->cdev->gadget;
+ int i;
+ struct usb_ep *ep;
+
+ fsg->gadget = gadget;
+
+ /* New interface */
+ i = usb_interface_id(c, f);
+ if (i < 0)
+ return i;
+ fsg_intf_desc.bInterfaceNumber = i;
+ fsg->interface_number = i;
+
+ /* Find all the endpoints we will use */
+ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = fsg->common; /* claim the endpoint */
+ fsg->bulk_in = ep;
+
+ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = fsg->common; /* claim the endpoint */
+ fsg->bulk_out = ep;
+
+ /* Copy descriptors */
+ f->descriptors = usb_copy_descriptors(fsg_fs_function);
+ if (unlikely(!f->descriptors))
+ return -ENOMEM;
+
+ if (gadget_is_dualspeed(gadget)) {
+ /* Assume endpoint addresses are the same for both speeds */
+ fsg_hs_bulk_in_desc.bEndpointAddress =
+ fsg_fs_bulk_in_desc.bEndpointAddress;
+ fsg_hs_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+ f->hs_descriptors = usb_copy_descriptors(fsg_hs_function);
+ if (unlikely(!f->hs_descriptors)) {
+ usb_free_descriptors(f->descriptors);
+ return -ENOMEM;
+ }
+ }
+
+ if (gadget_is_superspeed(gadget)) {
+ unsigned max_burst;
+
+ /* Calculate bMaxBurst, we know packet size is 1024 */
+ max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15);
+
+ fsg_ss_bulk_in_desc.bEndpointAddress =
+ fsg_fs_bulk_in_desc.bEndpointAddress;
+ fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
+
+ fsg_ss_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+ fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
+
+ f->ss_descriptors = usb_copy_descriptors(fsg_ss_function);
+ if (unlikely(!f->ss_descriptors)) {
+ usb_free_descriptors(f->hs_descriptors);
+ usb_free_descriptors(f->descriptors);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+
+autoconf_fail:
+ ERROR(fsg, "unable to autoconfigure all endpoints\n");
+ return -ENOTSUPP;
+}
+
+
+/****************************** ADD FUNCTION ******************************/
+
+static struct usb_gadget_strings *fsg_strings_array[] = {
+ &fsg_stringtab,
+ NULL,
+};
+
+static int fsg_bind_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct fsg_common *common)
+{
+ struct fsg_dev *fsg;
+ int rc;
+
+ fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
+ if (unlikely(!fsg))
+ return -ENOMEM;
+
+ fsg->function.name = FSG_DRIVER_DESC;
+ fsg->function.strings = fsg_strings_array;
+ fsg->function.bind = fsg_bind;
+ fsg->function.unbind = fsg_unbind;
+ fsg->function.setup = fsg_setup;
+ fsg->function.set_alt = fsg_set_alt;
+ fsg->function.disable = fsg_disable;
+
+ fsg->common = common;
+ /*
+ * Our caller holds a reference to common structure so we
+ * don't have to be worry about it being freed until we return
+ * from this function. So instead of incrementing counter now
+ * and decrement in error recovery we increment it only when
+ * call to usb_add_function() was successful.
+ */
+
+ rc = usb_add_function(c, &fsg->function);
+ if (unlikely(rc))
+ kfree(fsg);
+ else
+ fsg_common_get(fsg->common);
+ return rc;
+}
+
+
+/************************* Module parameters *************************/
+
+struct fsg_module_parameters {
+ char *file[FSG_MAX_LUNS];
+ bool ro[FSG_MAX_LUNS];
+ bool removable[FSG_MAX_LUNS];
+ bool cdrom[FSG_MAX_LUNS];
+ bool nofua[FSG_MAX_LUNS];
+
+ unsigned int file_count, ro_count, removable_count, cdrom_count;
+ unsigned int nofua_count;
+ unsigned int luns; /* nluns */
+ bool stall; /* can_stall */
+};
+
+#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \
+ module_param_array_named(prefix ## name, params.name, type, \
+ &prefix ## params.name ## _count, \
+ S_IRUGO); \
+ MODULE_PARM_DESC(prefix ## name, desc)
+
+#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \
+ module_param_named(prefix ## name, params.name, type, \
+ S_IRUGO); \
+ MODULE_PARM_DESC(prefix ## name, desc)
+
+#define FSG_MODULE_PARAMETERS(prefix, params) \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \
+ "names of backing files or devices"); \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \
+ "true to force read-only"); \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \
+ "true to simulate removable media"); \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \
+ "true to simulate CD-ROM instead of disk"); \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool, \
+ "true to ignore SCSI WRITE(10,12) FUA bit"); \
+ _FSG_MODULE_PARAM(prefix, params, luns, uint, \
+ "number of LUNs"); \
+ _FSG_MODULE_PARAM(prefix, params, stall, bool, \
+ "false to prevent bulk stalls")
+
+static void
+fsg_config_from_params(struct fsg_config *cfg,
+ const struct fsg_module_parameters *params)
+{
+ struct fsg_lun_config *lun;
+ unsigned i;
+
+ /* Configure LUNs */
+ cfg->nluns =
+ min(params->luns ?: (params->file_count ?: 1u),
+ (unsigned)FSG_MAX_LUNS);
+ for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) {
+ lun->ro = !!params->ro[i];
+ lun->cdrom = !!params->cdrom[i];
+ lun->removable = !!params->removable[i];
+ lun->filename =
+ params->file_count > i && params->file[i][0]
+ ? params->file[i]
+ : 0;
+ }
+
+ /* Let MSF use defaults */
+ cfg->vendor_name = 0;
+ cfg->product_name = 0;
+ cfg->release = 0xffff;
+
+ cfg->ops = NULL;
+ cfg->private_data = NULL;
+
+ /* Finalise */
+ cfg->can_stall = params->stall;
+}
+
+static inline struct fsg_common *
+fsg_common_from_params(struct fsg_common *common,
+ struct usb_composite_dev *cdev,
+ const struct fsg_module_parameters *params)
+ __attribute__((unused));
+static inline struct fsg_common *
+fsg_common_from_params(struct fsg_common *common,
+ struct usb_composite_dev *cdev,
+ const struct fsg_module_parameters *params)
+{
+ struct fsg_config cfg;
+ fsg_config_from_params(&cfg, params);
+ return fsg_common_init(common, cdev, &cfg);
+}
diff --git a/drivers/staging/ccg/f_rndis.c b/drivers/staging/ccg/f_rndis.c
new file mode 100644
index 000000000000..b1681e45aca7
--- /dev/null
+++ b/drivers/staging/ccg/f_rndis.c
@@ -0,0 +1,918 @@
+/*
+ * f_rndis.c -- RNDIS link function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz (mina86@mina86.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+
+#include <linux/atomic.h>
+
+#include "u_ether.h"
+#include "rndis.h"
+
+
+/*
+ * This function is an RNDIS Ethernet port -- a Microsoft protocol that's
+ * been promoted instead of the standard CDC Ethernet. The published RNDIS
+ * spec is ambiguous, incomplete, and needlessly complex. Variants such as
+ * ActiveSync have even worse status in terms of specification.
+ *
+ * In short: it's a protocol controlled by (and for) Microsoft, not for an
+ * Open ecosystem or markets. Linux supports it *only* because Microsoft
+ * doesn't support the CDC Ethernet standard.
+ *
+ * The RNDIS data transfer model is complex, with multiple Ethernet packets
+ * per USB message, and out of band data. The control model is built around
+ * what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM
+ * (modem, not Ethernet) veneer, with those ACM descriptors being entirely
+ * useless (they're ignored). RNDIS expects to be the only function in its
+ * configuration, so it's no real help if you need composite devices; and
+ * it expects to be the first configuration too.
+ *
+ * There is a single technical advantage of RNDIS over CDC Ethernet, if you
+ * discount the fluff that its RPC can be made to deliver: it doesn't need
+ * a NOP altsetting for the data interface. That lets it work on some of the
+ * "so smart it's stupid" hardware which takes over configuration changes
+ * from the software, and adds restrictions like "no altsettings".
+ *
+ * Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and
+ * have all sorts of contrary-to-specification oddities that can prevent
+ * them from working sanely. Since bugfixes (or accurate specs, letting
+ * Linux work around those bugs) are unlikely to ever come from MSFT, you
+ * may want to avoid using RNDIS on purely operational grounds.
+ *
+ * Omissions from the RNDIS 1.0 specification include:
+ *
+ * - Power management ... references data that's scattered around lots
+ * of other documentation, which is incorrect/incomplete there too.
+ *
+ * - There are various undocumented protocol requirements, like the need
+ * to send garbage in some control-OUT messages.
+ *
+ * - MS-Windows drivers sometimes emit undocumented requests.
+ */
+
+struct f_rndis {
+ struct gether port;
+ u8 ctrl_id, data_id;
+ u8 ethaddr[ETH_ALEN];
+ u32 vendorID;
+ const char *manufacturer;
+ int config;
+
+ struct usb_ep *notify;
+ struct usb_request *notify_req;
+ atomic_t notify_count;
+};
+
+static inline struct f_rndis *func_to_rndis(struct usb_function *f)
+{
+ return container_of(f, struct f_rndis, port.func);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static unsigned int bitrate(struct usb_gadget *g)
+{
+ if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+ return 13 * 1024 * 8 * 1000 * 8;
+ else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return 13 * 512 * 8 * 1000 * 8;
+ else
+ return 19 * 64 * 1 * 1000 * 8;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ */
+
+#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
+#define STATUS_BYTECOUNT 8 /* 8 bytes data */
+
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor rndis_control_intf = {
+ .bLength = sizeof rndis_control_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ /* .bInterfaceNumber = DYNAMIC */
+ /* status endpoint is optional; this could be patched later */
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
+ .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+ /* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc header_desc = {
+ .bLength = sizeof header_desc,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+
+ .bcdCDC = cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
+ .bLength = sizeof call_mgmt_descriptor,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
+
+ .bmCapabilities = 0x00,
+ .bDataInterface = 0x01,
+};
+
+static struct usb_cdc_acm_descriptor rndis_acm_descriptor = {
+ .bLength = sizeof rndis_acm_descriptor,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_ACM_TYPE,
+
+ .bmCapabilities = 0x00,
+};
+
+static struct usb_cdc_union_desc rndis_union_desc = {
+ .bLength = sizeof(rndis_union_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_UNION_TYPE,
+ /* .bMasterInterface0 = DYNAMIC */
+ /* .bSlaveInterface0 = DYNAMIC */
+};
+
+/* the data interface has two bulk endpoints */
+
+static struct usb_interface_descriptor rndis_data_intf = {
+ .bLength = sizeof rndis_data_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ /* .iInterface = DYNAMIC */
+};
+
+
+static struct usb_interface_assoc_descriptor
+rndis_iad_descriptor = {
+ .bLength = sizeof rndis_iad_descriptor,
+ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+ .bFirstInterface = 0, /* XXX, hardcoded */
+ .bInterfaceCount = 2, // control + data
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
+ .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ /* .iFunction = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
+ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+
+static struct usb_endpoint_descriptor fs_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *eth_fs_function[] = {
+ (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
+ /* control interface matches ACM, not Ethernet */
+ (struct usb_descriptor_header *) &rndis_control_intf,
+ (struct usb_descriptor_header *) &header_desc,
+ (struct usb_descriptor_header *) &call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &rndis_acm_descriptor,
+ (struct usb_descriptor_header *) &rndis_union_desc,
+ (struct usb_descriptor_header *) &fs_notify_desc,
+
+ /* data interface has no altsetting */
+ (struct usb_descriptor_header *) &rndis_data_intf,
+ (struct usb_descriptor_header *) &fs_in_desc,
+ (struct usb_descriptor_header *) &fs_out_desc,
+ NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
+ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_endpoint_descriptor hs_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *eth_hs_function[] = {
+ (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
+ /* control interface matches ACM, not Ethernet */
+ (struct usb_descriptor_header *) &rndis_control_intf,
+ (struct usb_descriptor_header *) &header_desc,
+ (struct usb_descriptor_header *) &call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &rndis_acm_descriptor,
+ (struct usb_descriptor_header *) &rndis_union_desc,
+ (struct usb_descriptor_header *) &hs_notify_desc,
+
+ /* data interface has no altsetting */
+ (struct usb_descriptor_header *) &rndis_data_intf,
+ (struct usb_descriptor_header *) &hs_in_desc,
+ (struct usb_descriptor_header *) &hs_out_desc,
+ NULL,
+};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
+ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
+ .bLength = sizeof ss_intr_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 3 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ss_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
+ .bLength = sizeof ss_bulk_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
+static struct usb_descriptor_header *eth_ss_function[] = {
+ (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
+ /* control interface matches ACM, not Ethernet */
+ (struct usb_descriptor_header *) &rndis_control_intf,
+ (struct usb_descriptor_header *) &header_desc,
+ (struct usb_descriptor_header *) &call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &rndis_acm_descriptor,
+ (struct usb_descriptor_header *) &rndis_union_desc,
+ (struct usb_descriptor_header *) &ss_notify_desc,
+ (struct usb_descriptor_header *) &ss_intr_comp_desc,
+
+ /* data interface has no altsetting */
+ (struct usb_descriptor_header *) &rndis_data_intf,
+ (struct usb_descriptor_header *) &ss_in_desc,
+ (struct usb_descriptor_header *) &ss_bulk_comp_desc,
+ (struct usb_descriptor_header *) &ss_out_desc,
+ (struct usb_descriptor_header *) &ss_bulk_comp_desc,
+ NULL,
+};
+
+/* string descriptors: */
+
+static struct usb_string rndis_string_defs[] = {
+ [0].s = "RNDIS Communications Control",
+ [1].s = "RNDIS Ethernet Data",
+ [2].s = "RNDIS",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings rndis_string_table = {
+ .language = 0x0409, /* en-us */
+ .strings = rndis_string_defs,
+};
+
+static struct usb_gadget_strings *rndis_strings[] = {
+ &rndis_string_table,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct sk_buff *rndis_add_header(struct gether *port,
+ struct sk_buff *skb)
+{
+ struct sk_buff *skb2;
+
+ skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
+ if (skb2)
+ rndis_add_hdr(skb2);
+
+ dev_kfree_skb_any(skb);
+ return skb2;
+}
+
+static void rndis_response_available(void *_rndis)
+{
+ struct f_rndis *rndis = _rndis;
+ struct usb_request *req = rndis->notify_req;
+ struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
+ __le32 *data = req->buf;
+ int status;
+
+ if (atomic_inc_return(&rndis->notify_count) != 1)
+ return;
+
+ /* Send RNDIS RESPONSE_AVAILABLE notification; a
+ * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too
+ *
+ * This is the only notification defined by RNDIS.
+ */
+ data[0] = cpu_to_le32(1);
+ data[1] = cpu_to_le32(0);
+
+ status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
+ if (status) {
+ atomic_dec(&rndis->notify_count);
+ DBG(cdev, "notify/0 --> %d\n", status);
+ }
+}
+
+static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_rndis *rndis = req->context;
+ struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
+ int status = req->status;
+
+ /* after TX:
+ * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
+ * - RNDIS_RESPONSE_AVAILABLE (status/irq)
+ */
+ switch (status) {
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ /* connection gone */
+ atomic_set(&rndis->notify_count, 0);
+ break;
+ default:
+ DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
+ ep->name, status,
+ req->actual, req->length);
+ /* FALLTHROUGH */
+ case 0:
+ if (ep != rndis->notify)
+ break;
+
+ /* handle multiple pending RNDIS_RESPONSE_AVAILABLE
+ * notifications by resending until we're done
+ */
+ if (atomic_dec_and_test(&rndis->notify_count))
+ break;
+ status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
+ if (status) {
+ atomic_dec(&rndis->notify_count);
+ DBG(cdev, "notify/1 --> %d\n", status);
+ }
+ break;
+ }
+}
+
+static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_rndis *rndis = req->context;
+ struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
+ int status;
+
+ /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
+// spin_lock(&dev->lock);
+ status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
+ if (status < 0)
+ ERROR(cdev, "RNDIS command error %d, %d/%d\n",
+ status, req->actual, req->length);
+// spin_unlock(&dev->lock);
+}
+
+static int
+rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct f_rndis *rndis = func_to_rndis(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* composite driver infrastructure handles everything except
+ * CDC class messages; interface activation uses set_alt().
+ */
+ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+ /* RNDIS uses the CDC command encapsulation mechanism to implement
+ * an RPC scheme, with much getting/setting of attributes by OID.
+ */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_SEND_ENCAPSULATED_COMMAND:
+ if (w_value || w_index != rndis->ctrl_id)
+ goto invalid;
+ /* read the request; process it later */
+ value = w_length;
+ req->complete = rndis_command_complete;
+ req->context = rndis;
+ /* later, rndis_response_available() sends a notification */
+ break;
+
+ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_GET_ENCAPSULATED_RESPONSE:
+ if (w_value || w_index != rndis->ctrl_id)
+ goto invalid;
+ else {
+ u8 *buf;
+ u32 n;
+
+ /* return the result */
+ buf = rndis_get_next_response(rndis->config, &n);
+ if (buf) {
+ memcpy(req->buf, buf, n);
+ req->complete = rndis_response_complete;
+ req->context = rndis;
+ rndis_free_response(rndis->config, buf);
+ value = n;
+ }
+ /* else stalls ... spec says to avoid that */
+ }
+ break;
+
+ default:
+invalid:
+ VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = (value < w_length);
+ req->length = value;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ ERROR(cdev, "rndis response on err %d\n", value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+
+static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_rndis *rndis = func_to_rndis(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ /* we know alt == 0 */
+
+ if (intf == rndis->ctrl_id) {
+ if (rndis->notify->driver_data) {
+ VDBG(cdev, "reset rndis control %d\n", intf);
+ usb_ep_disable(rndis->notify);
+ }
+ if (!rndis->notify->desc) {
+ VDBG(cdev, "init rndis ctrl %d\n", intf);
+ if (config_ep_by_speed(cdev->gadget, f, rndis->notify))
+ goto fail;
+ }
+ usb_ep_enable(rndis->notify);
+ rndis->notify->driver_data = rndis;
+
+ } else if (intf == rndis->data_id) {
+ struct net_device *net;
+
+ if (rndis->port.in_ep->driver_data) {
+ DBG(cdev, "reset rndis\n");
+ gether_disconnect(&rndis->port);
+ }
+
+ if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
+ DBG(cdev, "init rndis\n");
+ if (config_ep_by_speed(cdev->gadget, f,
+ rndis->port.in_ep) ||
+ config_ep_by_speed(cdev->gadget, f,
+ rndis->port.out_ep)) {
+ rndis->port.in_ep->desc = NULL;
+ rndis->port.out_ep->desc = NULL;
+ goto fail;
+ }
+ }
+
+ /* Avoid ZLPs; they can be troublesome. */
+ rndis->port.is_zlp_ok = false;
+
+ /* RNDIS should be in the "RNDIS uninitialized" state,
+ * either never activated or after rndis_uninit().
+ *
+ * We don't want data to flow here until a nonzero packet
+ * filter is set, at which point it enters "RNDIS data
+ * initialized" state ... but we do want the endpoints
+ * to be activated. It's a strange little state.
+ *
+ * REVISIT the RNDIS gadget code has done this wrong for a
+ * very long time. We need another call to the link layer
+ * code -- gether_updown(...bool) maybe -- to do it right.
+ */
+ rndis->port.cdc_filter = 0;
+
+ DBG(cdev, "RNDIS RX/TX early activation ... \n");
+ net = gether_connect(&rndis->port);
+ if (IS_ERR(net))
+ return PTR_ERR(net);
+
+ rndis_set_param_dev(rndis->config, net,
+ &rndis->port.cdc_filter);
+ } else
+ goto fail;
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static void rndis_disable(struct usb_function *f)
+{
+ struct f_rndis *rndis = func_to_rndis(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ if (!rndis->notify->driver_data)
+ return;
+
+ DBG(cdev, "rndis deactivated\n");
+
+ rndis_uninit(rndis->config);
+ gether_disconnect(&rndis->port);
+
+ usb_ep_disable(rndis->notify);
+ rndis->notify->driver_data = NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * This isn't quite the same mechanism as CDC Ethernet, since the
+ * notification scheme passes less data, but the same set of link
+ * states must be tested. A key difference is that altsettings are
+ * not used to tell whether the link should send packets or not.
+ */
+
+static void rndis_open(struct gether *geth)
+{
+ struct f_rndis *rndis = func_to_rndis(&geth->func);
+ struct usb_composite_dev *cdev = geth->func.config->cdev;
+
+ DBG(cdev, "%s\n", __func__);
+
+ rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3,
+ bitrate(cdev->gadget) / 100);
+ rndis_signal_connect(rndis->config);
+}
+
+static void rndis_close(struct gether *geth)
+{
+ struct f_rndis *rndis = func_to_rndis(&geth->func);
+
+ DBG(geth->func.config->cdev, "%s\n", __func__);
+
+ rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
+ rndis_signal_disconnect(rndis->config);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ethernet function driver setup/binding */
+
+static int
+rndis_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_rndis *rndis = func_to_rndis(f);
+ int status;
+ struct usb_ep *ep;
+
+ /* allocate instance-specific interface IDs */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ rndis->ctrl_id = status;
+ rndis_iad_descriptor.bFirstInterface = status;
+
+ rndis_control_intf.bInterfaceNumber = status;
+ rndis_union_desc.bMasterInterface0 = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ rndis->data_id = status;
+
+ rndis_data_intf.bInterfaceNumber = status;
+ rndis_union_desc.bSlaveInterface0 = status;
+
+ status = -ENODEV;
+
+ /* allocate instance-specific endpoints */
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
+ if (!ep)
+ goto fail;
+ rndis->port.in_ep = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
+ if (!ep)
+ goto fail;
+ rndis->port.out_ep = ep;
+ ep->driver_data = cdev; /* claim */
+
+ /* NOTE: a status/notification endpoint is, strictly speaking,
+ * optional. We don't treat it that way though! It's simpler,
+ * and some newer profiles don't treat it as optional.
+ */
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
+ if (!ep)
+ goto fail;
+ rndis->notify = ep;
+ ep->driver_data = cdev; /* claim */
+
+ status = -ENOMEM;
+
+ /* allocate notification request and buffer */
+ rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!rndis->notify_req)
+ goto fail;
+ rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
+ if (!rndis->notify_req->buf)
+ goto fail;
+ rndis->notify_req->length = STATUS_BYTECOUNT;
+ rndis->notify_req->context = rndis;
+ rndis->notify_req->complete = rndis_response_complete;
+
+ /* copy descriptors, and track endpoint copies */
+ f->descriptors = usb_copy_descriptors(eth_fs_function);
+ if (!f->descriptors)
+ goto fail;
+
+ /* support all relevant hardware speeds... we expect that when
+ * hardware is dual speed, all bulk-capable endpoints work at
+ * both speeds
+ */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ hs_in_desc.bEndpointAddress =
+ fs_in_desc.bEndpointAddress;
+ hs_out_desc.bEndpointAddress =
+ fs_out_desc.bEndpointAddress;
+ hs_notify_desc.bEndpointAddress =
+ fs_notify_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->hs_descriptors = usb_copy_descriptors(eth_hs_function);
+ if (!f->hs_descriptors)
+ goto fail;
+ }
+
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ ss_in_desc.bEndpointAddress =
+ fs_in_desc.bEndpointAddress;
+ ss_out_desc.bEndpointAddress =
+ fs_out_desc.bEndpointAddress;
+ ss_notify_desc.bEndpointAddress =
+ fs_notify_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->ss_descriptors = usb_copy_descriptors(eth_ss_function);
+ if (!f->ss_descriptors)
+ goto fail;
+ }
+
+ rndis->port.open = rndis_open;
+ rndis->port.close = rndis_close;
+
+ status = rndis_register(rndis_response_available, rndis);
+ if (status < 0)
+ goto fail;
+ rndis->config = status;
+
+ rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
+ rndis_set_host_mac(rndis->config, rndis->ethaddr);
+
+ if (rndis->manufacturer && rndis->vendorID &&
+ rndis_set_param_vendor(rndis->config, rndis->vendorID,
+ rndis->manufacturer))
+ goto fail;
+
+ /* NOTE: all that is done without knowing or caring about
+ * the network link ... which is unavailable to this code
+ * until we're activated via set_alt().
+ */
+
+ DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ rndis->port.in_ep->name, rndis->port.out_ep->name,
+ rndis->notify->name);
+ return 0;
+
+fail:
+ if (gadget_is_superspeed(c->cdev->gadget) && f->ss_descriptors)
+ usb_free_descriptors(f->ss_descriptors);
+ if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors)
+ usb_free_descriptors(f->hs_descriptors);
+ if (f->descriptors)
+ usb_free_descriptors(f->descriptors);
+
+ if (rndis->notify_req) {
+ kfree(rndis->notify_req->buf);
+ usb_ep_free_request(rndis->notify, rndis->notify_req);
+ }
+
+ /* we might as well release our claims on endpoints */
+ if (rndis->notify)
+ rndis->notify->driver_data = NULL;
+ if (rndis->port.out_ep->desc)
+ rndis->port.out_ep->driver_data = NULL;
+ if (rndis->port.in_ep->desc)
+ rndis->port.in_ep->driver_data = NULL;
+
+ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+ return status;
+}
+
+static void
+rndis_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_rndis *rndis = func_to_rndis(f);
+
+ rndis_deregister(rndis->config);
+ rndis_exit();
+ rndis_string_defs[0].id = 0;
+
+ if (gadget_is_superspeed(c->cdev->gadget))
+ usb_free_descriptors(f->ss_descriptors);
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ usb_free_descriptors(f->hs_descriptors);
+ usb_free_descriptors(f->descriptors);
+
+ kfree(rndis->notify_req->buf);
+ usb_ep_free_request(rndis->notify, rndis->notify_req);
+
+ kfree(rndis);
+}
+
+/* Some controllers can't support RNDIS ... */
+static inline bool can_support_rndis(struct usb_configuration *c)
+{
+ /* everything else is *presumably* fine */
+ return true;
+}
+
+int
+rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
+ u32 vendorID, const char *manufacturer)
+{
+ struct f_rndis *rndis;
+ int status;
+
+ if (!can_support_rndis(c) || !ethaddr)
+ return -EINVAL;
+
+ /* maybe allocate device-global string IDs */
+ if (rndis_string_defs[0].id == 0) {
+
+ /* ... and setup RNDIS itself */
+ status = rndis_init();
+ if (status < 0)
+ return status;
+
+ /* control interface label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ rndis_string_defs[0].id = status;
+ rndis_control_intf.iInterface = status;
+
+ /* data interface label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ rndis_string_defs[1].id = status;
+ rndis_data_intf.iInterface = status;
+
+ /* IAD iFunction label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ rndis_string_defs[2].id = status;
+ rndis_iad_descriptor.iFunction = status;
+ }
+
+ /* allocate and initialize one new instance */
+ status = -ENOMEM;
+ rndis = kzalloc(sizeof *rndis, GFP_KERNEL);
+ if (!rndis)
+ goto fail;
+
+ memcpy(rndis->ethaddr, ethaddr, ETH_ALEN);
+ rndis->vendorID = vendorID;
+ rndis->manufacturer = manufacturer;
+
+ /* RNDIS activates when the host changes this filter */
+ rndis->port.cdc_filter = 0;
+
+ /* RNDIS has special (and complex) framing */
+ rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
+ rndis->port.wrap = rndis_add_header;
+ rndis->port.unwrap = rndis_rm_hdr;
+
+ rndis->port.func.name = "rndis";
+ rndis->port.func.strings = rndis_strings;
+ /* descriptors are per-instance copies */
+ rndis->port.func.bind = rndis_bind;
+ rndis->port.func.unbind = rndis_unbind;
+ rndis->port.func.set_alt = rndis_set_alt;
+ rndis->port.func.setup = rndis_setup;
+ rndis->port.func.disable = rndis_disable;
+
+ status = usb_add_function(c, &rndis->port.func);
+ if (status) {
+ kfree(rndis);
+fail:
+ rndis_exit();
+ }
+ return status;
+}
diff --git a/drivers/staging/ccg/gadget_chips.h b/drivers/staging/ccg/gadget_chips.h
new file mode 100644
index 000000000000..0ccca58e7a8f
--- /dev/null
+++ b/drivers/staging/ccg/gadget_chips.h
@@ -0,0 +1,150 @@
+/*
+ * USB device controllers have lots of quirks. Use these macros in
+ * gadget drivers or other code that needs to deal with them, and which
+ * autoconfigures instead of using early binding to the hardware.
+ *
+ * This SHOULD eventually work like the ARM mach_is_*() stuff, driven by
+ * some config file that gets updated as new hardware is supported.
+ * (And avoiding all runtime comparisons in typical one-choice configs!)
+ *
+ * NOTE: some of these controller drivers may not be available yet.
+ * Some are available on 2.4 kernels; several are available, but not
+ * yet pushed in the 2.6 mainline tree.
+ */
+
+#ifndef __GADGET_CHIPS_H
+#define __GADGET_CHIPS_H
+
+/*
+ * NOTICE: the entries below are alphabetical and should be kept
+ * that way.
+ *
+ * Always be sure to add new entries to the correct position or
+ * accept the bashing later.
+ *
+ * If you have forgotten the alphabetical order let VIM/EMACS
+ * do that for you.
+ */
+#define gadget_is_amd5536udc(g) (!strcmp("amd5536udc", (g)->name))
+#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name))
+#define gadget_is_atmel_usba(g) (!strcmp("atmel_usba_udc", (g)->name))
+#define gadget_is_bcm63xx(g) (!strcmp("bcm63xx_udc", (g)->name))
+#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
+#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
+#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name))
+#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name))
+#define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name))
+#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name))
+#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
+#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name))
+#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name))
+#define gadget_is_lpc32xx(g) (!strcmp("lpc32xx_udc", (g)->name))
+#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name))
+#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
+#define gadget_is_net2272(g) (!strcmp("net2272", (g)->name))
+#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name))
+#define gadget_is_omap(g) (!strcmp("omap_udc", (g)->name))
+#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name))
+#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name))
+#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name))
+#define gadget_is_r8a66597(g) (!strcmp("r8a66597_udc", (g)->name))
+#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name))
+#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name))
+#define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name))
+#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
+
+/**
+ * usb_gadget_controller_number - support bcdDevice id convention
+ * @gadget: the controller being driven
+ *
+ * Return a 2-digit BCD value associated with the peripheral controller,
+ * suitable for use as part of a bcdDevice value, or a negative error code.
+ *
+ * NOTE: this convention is purely optional, and has no meaning in terms of
+ * any USB specification. If you want to use a different convention in your
+ * gadget driver firmware -- maybe a more formal revision ID -- feel free.
+ *
+ * Hosts see these bcdDevice numbers, and are allowed (but not encouraged!)
+ * to change their behavior accordingly. For example it might help avoiding
+ * some chip bug.
+ */
+static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
+{
+ if (gadget_is_net2280(gadget))
+ return 0x01;
+ else if (gadget_is_dummy(gadget))
+ return 0x02;
+ else if (gadget_is_pxa(gadget))
+ return 0x03;
+ else if (gadget_is_goku(gadget))
+ return 0x06;
+ else if (gadget_is_omap(gadget))
+ return 0x08;
+ else if (gadget_is_pxa27x(gadget))
+ return 0x11;
+ else if (gadget_is_s3c2410(gadget))
+ return 0x12;
+ else if (gadget_is_at91(gadget))
+ return 0x13;
+ else if (gadget_is_imx(gadget))
+ return 0x14;
+ else if (gadget_is_musbhdrc(gadget))
+ return 0x16;
+ else if (gadget_is_atmel_usba(gadget))
+ return 0x18;
+ else if (gadget_is_fsl_usb2(gadget))
+ return 0x19;
+ else if (gadget_is_amd5536udc(gadget))
+ return 0x20;
+ else if (gadget_is_m66592(gadget))
+ return 0x21;
+ else if (gadget_is_fsl_qe(gadget))
+ return 0x22;
+ else if (gadget_is_ci13xxx_pci(gadget))
+ return 0x23;
+ else if (gadget_is_langwell(gadget))
+ return 0x24;
+ else if (gadget_is_r8a66597(gadget))
+ return 0x25;
+ else if (gadget_is_s3c_hsotg(gadget))
+ return 0x26;
+ else if (gadget_is_pch(gadget))
+ return 0x27;
+ else if (gadget_is_ci13xxx_msm(gadget))
+ return 0x28;
+ else if (gadget_is_renesas_usbhs(gadget))
+ return 0x29;
+ else if (gadget_is_s3c_hsudc(gadget))
+ return 0x30;
+ else if (gadget_is_net2272(gadget))
+ return 0x31;
+ else if (gadget_is_dwc3(gadget))
+ return 0x32;
+ else if (gadget_is_lpc32xx(gadget))
+ return 0x33;
+ else if (gadget_is_bcm63xx(gadget))
+ return 0x34;
+
+ return -ENOENT;
+}
+
+
+/**
+ * gadget_supports_altsettings - return true if altsettings work
+ * @gadget: the gadget in question
+ */
+static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
+{
+ /* PXA 21x/25x/26x has no altsettings at all */
+ if (gadget_is_pxa(gadget))
+ return false;
+
+ /* PXA 27x and 3xx have *broken* altsetting support */
+ if (gadget_is_pxa27x(gadget))
+ return false;
+
+ /* Everything else is *presumably* fine ... */
+ return true;
+}
+
+#endif /* __GADGET_CHIPS_H */
diff --git a/drivers/staging/ccg/ndis.h b/drivers/staging/ccg/ndis.h
new file mode 100644
index 000000000000..a19f72dec0cd
--- /dev/null
+++ b/drivers/staging/ccg/ndis.h
@@ -0,0 +1,47 @@
+/*
+ * ndis.h
+ *
+ * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
+ *
+ * Thanks to the cygwin development team,
+ * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ */
+
+#ifndef _LINUX_NDIS_H
+#define _LINUX_NDIS_H
+
+enum NDIS_DEVICE_POWER_STATE {
+ NdisDeviceStateUnspecified = 0,
+ NdisDeviceStateD0,
+ NdisDeviceStateD1,
+ NdisDeviceStateD2,
+ NdisDeviceStateD3,
+ NdisDeviceStateMaximum
+};
+
+struct NDIS_PM_WAKE_UP_CAPABILITIES {
+ enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp;
+ enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp;
+ enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp;
+};
+
+struct NDIS_PNP_CAPABILITIES {
+ __le32 Flags;
+ struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities;
+};
+
+struct NDIS_PM_PACKET_PATTERN {
+ __le32 Priority;
+ __le32 Reserved;
+ __le32 MaskSize;
+ __le32 PatternOffset;
+ __le32 PatternSize;
+ __le32 PatternFlags;
+};
+
+#endif /* _LINUX_NDIS_H */
diff --git a/drivers/staging/ccg/rndis.c b/drivers/staging/ccg/rndis.c
new file mode 100644
index 000000000000..e4192b887de9
--- /dev/null
+++ b/drivers/staging/ccg/rndis.c
@@ -0,0 +1,1175 @@
+/*
+ * RNDIS MSG parser
+ *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, 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 software was originally developed in conformance with
+ * Microsoft's Remote NDIS Specification License Agreement.
+ *
+ * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ * Fixed message length bug in init_response
+ *
+ * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ * Fixed rndis_rm_hdr length bug.
+ *
+ * Copyright (C) 2004 by David Brownell
+ * updates to merge with Linux 2.6, better match RNDIS spec
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/netdevice.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+
+#undef VERBOSE_DEBUG
+
+#include "rndis.h"
+
+
+/* The driver for your USB chip needs to support ep0 OUT to work with
+ * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
+ *
+ * Windows hosts need an INF file like Documentation/usb/linux.inf
+ * and will be happier if you provide the host_addr module parameter.
+ */
+
+#if 0
+static int rndis_debug = 0;
+module_param (rndis_debug, int, 0);
+MODULE_PARM_DESC (rndis_debug, "enable debugging");
+#else
+#define rndis_debug 0
+#endif
+
+#define RNDIS_MAX_CONFIGS 1
+
+
+static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS];
+
+/* Driver Version */
+static const __le32 rndis_driver_version = cpu_to_le32(1);
+
+/* Function Prototypes */
+static rndis_resp_t *rndis_add_response(int configNr, u32 length);
+
+
+/* supported OIDs */
+static const u32 oid_supported_list[] =
+{
+ /* the general stuff */
+ RNDIS_OID_GEN_SUPPORTED_LIST,
+ RNDIS_OID_GEN_HARDWARE_STATUS,
+ RNDIS_OID_GEN_MEDIA_SUPPORTED,
+ RNDIS_OID_GEN_MEDIA_IN_USE,
+ RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
+ RNDIS_OID_GEN_LINK_SPEED,
+ RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE,
+ RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE,
+ RNDIS_OID_GEN_VENDOR_ID,
+ RNDIS_OID_GEN_VENDOR_DESCRIPTION,
+ RNDIS_OID_GEN_VENDOR_DRIVER_VERSION,
+ RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
+ RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE,
+ RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
+ RNDIS_OID_GEN_PHYSICAL_MEDIUM,
+
+ /* the statistical stuff */
+ RNDIS_OID_GEN_XMIT_OK,
+ RNDIS_OID_GEN_RCV_OK,
+ RNDIS_OID_GEN_XMIT_ERROR,
+ RNDIS_OID_GEN_RCV_ERROR,
+ RNDIS_OID_GEN_RCV_NO_BUFFER,
+#ifdef RNDIS_OPTIONAL_STATS
+ RNDIS_OID_GEN_DIRECTED_BYTES_XMIT,
+ RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT,
+ RNDIS_OID_GEN_MULTICAST_BYTES_XMIT,
+ RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT,
+ RNDIS_OID_GEN_BROADCAST_BYTES_XMIT,
+ RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT,
+ RNDIS_OID_GEN_DIRECTED_BYTES_RCV,
+ RNDIS_OID_GEN_DIRECTED_FRAMES_RCV,
+ RNDIS_OID_GEN_MULTICAST_BYTES_RCV,
+ RNDIS_OID_GEN_MULTICAST_FRAMES_RCV,
+ RNDIS_OID_GEN_BROADCAST_BYTES_RCV,
+ RNDIS_OID_GEN_BROADCAST_FRAMES_RCV,
+ RNDIS_OID_GEN_RCV_CRC_ERROR,
+ RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+ /* mandatory 802.3 */
+ /* the general stuff */
+ RNDIS_OID_802_3_PERMANENT_ADDRESS,
+ RNDIS_OID_802_3_CURRENT_ADDRESS,
+ RNDIS_OID_802_3_MULTICAST_LIST,
+ RNDIS_OID_802_3_MAC_OPTIONS,
+ RNDIS_OID_802_3_MAXIMUM_LIST_SIZE,
+
+ /* the statistical stuff */
+ RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT,
+ RNDIS_OID_802_3_XMIT_ONE_COLLISION,
+ RNDIS_OID_802_3_XMIT_MORE_COLLISIONS,
+#ifdef RNDIS_OPTIONAL_STATS
+ RNDIS_OID_802_3_XMIT_DEFERRED,
+ RNDIS_OID_802_3_XMIT_MAX_COLLISIONS,
+ RNDIS_OID_802_3_RCV_OVERRUN,
+ RNDIS_OID_802_3_XMIT_UNDERRUN,
+ RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE,
+ RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST,
+ RNDIS_OID_802_3_XMIT_LATE_COLLISIONS,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+#ifdef RNDIS_PM
+ /* PM and wakeup are "mandatory" for USB, but the RNDIS specs
+ * don't say what they mean ... and the NDIS specs are often
+ * confusing and/or ambiguous in this context. (That is, more
+ * so than their specs for the other OIDs.)
+ *
+ * FIXME someone who knows what these should do, please
+ * implement them!
+ */
+
+ /* power management */
+ OID_PNP_CAPABILITIES,
+ OID_PNP_QUERY_POWER,
+ OID_PNP_SET_POWER,
+
+#ifdef RNDIS_WAKEUP
+ /* wake up host */
+ OID_PNP_ENABLE_WAKE_UP,
+ OID_PNP_ADD_WAKE_UP_PATTERN,
+ OID_PNP_REMOVE_WAKE_UP_PATTERN,
+#endif /* RNDIS_WAKEUP */
+#endif /* RNDIS_PM */
+};
+
+
+/* NDIS Functions */
+static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
+ unsigned buf_len, rndis_resp_t *r)
+{
+ int retval = -ENOTSUPP;
+ u32 length = 4; /* usually */
+ __le32 *outbuf;
+ int i, count;
+ rndis_query_cmplt_type *resp;
+ struct net_device *net;
+ struct rtnl_link_stats64 temp;
+ const struct rtnl_link_stats64 *stats;
+
+ if (!r) return -ENOMEM;
+ resp = (rndis_query_cmplt_type *)r->buf;
+
+ if (!resp) return -ENOMEM;
+
+ if (buf_len && rndis_debug > 1) {
+ pr_debug("query OID %08x value, len %d:\n", OID, buf_len);
+ for (i = 0; i < buf_len; i += 16) {
+ pr_debug("%03d: %08x %08x %08x %08x\n", i,
+ get_unaligned_le32(&buf[i]),
+ get_unaligned_le32(&buf[i + 4]),
+ get_unaligned_le32(&buf[i + 8]),
+ get_unaligned_le32(&buf[i + 12]));
+ }
+ }
+
+ /* response goes here, right after the header */
+ outbuf = (__le32 *)&resp[1];
+ resp->InformationBufferOffset = cpu_to_le32(16);
+
+ net = rndis_per_dev_params[configNr].dev;
+ stats = dev_get_stats(net, &temp);
+
+ switch (OID) {
+
+ /* general oids (table 4-1) */
+
+ /* mandatory */
+ case RNDIS_OID_GEN_SUPPORTED_LIST:
+ pr_debug("%s: RNDIS_OID_GEN_SUPPORTED_LIST\n", __func__);
+ length = sizeof(oid_supported_list);
+ count = length / sizeof(u32);
+ for (i = 0; i < count; i++)
+ outbuf[i] = cpu_to_le32(oid_supported_list[i]);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_HARDWARE_STATUS:
+ pr_debug("%s: RNDIS_OID_GEN_HARDWARE_STATUS\n", __func__);
+ /* Bogus question!
+ * Hardware must be ready to receive high level protocols.
+ * BTW:
+ * reddite ergo quae sunt Caesaris Caesari
+ * et quae sunt Dei Deo!
+ */
+ *outbuf = cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_MEDIA_SUPPORTED:
+ pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__);
+ *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_MEDIA_IN_USE:
+ pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__);
+ /* one medium, one transport... (maybe you do it better) */
+ *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE:
+ pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
+ if (rndis_per_dev_params[configNr].dev) {
+ *outbuf = cpu_to_le32(
+ rndis_per_dev_params[configNr].dev->mtu);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_LINK_SPEED:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__);
+ if (rndis_per_dev_params[configNr].media_state
+ == RNDIS_MEDIA_STATE_DISCONNECTED)
+ *outbuf = cpu_to_le32(0);
+ else
+ *outbuf = cpu_to_le32(
+ rndis_per_dev_params[configNr].speed);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE:
+ pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
+ if (rndis_per_dev_params[configNr].dev) {
+ *outbuf = cpu_to_le32(
+ rndis_per_dev_params[configNr].dev->mtu);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE:
+ pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
+ if (rndis_per_dev_params[configNr].dev) {
+ *outbuf = cpu_to_le32(
+ rndis_per_dev_params[configNr].dev->mtu);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_VENDOR_ID:
+ pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__);
+ *outbuf = cpu_to_le32(
+ rndis_per_dev_params[configNr].vendorID);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_VENDOR_DESCRIPTION:
+ pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__);
+ if (rndis_per_dev_params[configNr].vendorDescr) {
+ length = strlen(rndis_per_dev_params[configNr].
+ vendorDescr);
+ memcpy(outbuf,
+ rndis_per_dev_params[configNr].vendorDescr,
+ length);
+ } else {
+ outbuf[0] = 0;
+ }
+ retval = 0;
+ break;
+
+ case RNDIS_OID_GEN_VENDOR_DRIVER_VERSION:
+ pr_debug("%s: RNDIS_OID_GEN_VENDOR_DRIVER_VERSION\n", __func__);
+ /* Created as LE */
+ *outbuf = rndis_driver_version;
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
+ pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__);
+ *outbuf = cpu_to_le32(*rndis_per_dev_params[configNr].filter);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE:
+ pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__);
+ *outbuf = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);
+ *outbuf = cpu_to_le32(rndis_per_dev_params[configNr]
+ .media_state);
+ retval = 0;
+ break;
+
+ case RNDIS_OID_GEN_PHYSICAL_MEDIUM:
+ pr_debug("%s: RNDIS_OID_GEN_PHYSICAL_MEDIUM\n", __func__);
+ *outbuf = cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ /* The RNDIS specification is incomplete/wrong. Some versions
+ * of MS-Windows expect OIDs that aren't specified there. Other
+ * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
+ */
+ case RNDIS_OID_GEN_MAC_OPTIONS: /* from WinME */
+ pr_debug("%s: RNDIS_OID_GEN_MAC_OPTIONS\n", __func__);
+ *outbuf = cpu_to_le32(
+ RNDIS_MAC_OPTION_RECEIVE_SERIALIZED
+ | RNDIS_MAC_OPTION_FULL_DUPLEX);
+ retval = 0;
+ break;
+
+ /* statistics OIDs (table 4-2) */
+
+ /* mandatory */
+ case RNDIS_OID_GEN_XMIT_OK:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_XMIT_OK\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->tx_packets
+ - stats->tx_errors - stats->tx_dropped);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_RCV_OK:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_RCV_OK\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->rx_packets
+ - stats->rx_errors - stats->rx_dropped);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_XMIT_ERROR:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_XMIT_ERROR\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->tx_errors);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_RCV_ERROR:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_RCV_ERROR\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->rx_errors);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_RCV_NO_BUFFER:
+ pr_debug("%s: RNDIS_OID_GEN_RCV_NO_BUFFER\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->rx_dropped);
+ retval = 0;
+ }
+ break;
+
+ /* ieee802.3 OIDs (table 4-3) */
+
+ /* mandatory */
+ case RNDIS_OID_802_3_PERMANENT_ADDRESS:
+ pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__);
+ if (rndis_per_dev_params[configNr].dev) {
+ length = ETH_ALEN;
+ memcpy(outbuf,
+ rndis_per_dev_params[configNr].host_mac,
+ length);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_802_3_CURRENT_ADDRESS:
+ pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__);
+ if (rndis_per_dev_params[configNr].dev) {
+ length = ETH_ALEN;
+ memcpy(outbuf,
+ rndis_per_dev_params [configNr].host_mac,
+ length);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_802_3_MULTICAST_LIST:
+ pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__);
+ /* Multicast base address only */
+ *outbuf = cpu_to_le32(0xE0000000);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_802_3_MAXIMUM_LIST_SIZE:
+ pr_debug("%s: RNDIS_OID_802_3_MAXIMUM_LIST_SIZE\n", __func__);
+ /* Multicast base address only */
+ *outbuf = cpu_to_le32(1);
+ retval = 0;
+ break;
+
+ case RNDIS_OID_802_3_MAC_OPTIONS:
+ pr_debug("%s: RNDIS_OID_802_3_MAC_OPTIONS\n", __func__);
+ *outbuf = cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ /* ieee802.3 statistics OIDs (table 4-4) */
+
+ /* mandatory */
+ case RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT:
+ pr_debug("%s: RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->rx_frame_errors);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_802_3_XMIT_ONE_COLLISION:
+ pr_debug("%s: RNDIS_OID_802_3_XMIT_ONE_COLLISION\n", __func__);
+ *outbuf = cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_802_3_XMIT_MORE_COLLISIONS:
+ pr_debug("%s: RNDIS_OID_802_3_XMIT_MORE_COLLISIONS\n", __func__);
+ *outbuf = cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ default:
+ pr_warning("%s: query unknown OID 0x%08X\n",
+ __func__, OID);
+ }
+ if (retval < 0)
+ length = 0;
+
+ resp->InformationBufferLength = cpu_to_le32(length);
+ r->length = length + sizeof(*resp);
+ resp->MessageLength = cpu_to_le32(r->length);
+ return retval;
+}
+
+static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
+ rndis_resp_t *r)
+{
+ rndis_set_cmplt_type *resp;
+ int i, retval = -ENOTSUPP;
+ struct rndis_params *params;
+
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_set_cmplt_type *)r->buf;
+ if (!resp)
+ return -ENOMEM;
+
+ if (buf_len && rndis_debug > 1) {
+ pr_debug("set OID %08x value, len %d:\n", OID, buf_len);
+ for (i = 0; i < buf_len; i += 16) {
+ pr_debug("%03d: %08x %08x %08x %08x\n", i,
+ get_unaligned_le32(&buf[i]),
+ get_unaligned_le32(&buf[i + 4]),
+ get_unaligned_le32(&buf[i + 8]),
+ get_unaligned_le32(&buf[i + 12]));
+ }
+ }
+
+ params = &rndis_per_dev_params[configNr];
+ switch (OID) {
+ case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
+
+ /* these NDIS_PACKET_TYPE_* bitflags are shared with
+ * cdc_filter; it's not RNDIS-specific
+ * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
+ * PROMISCUOUS, DIRECTED,
+ * MULTICAST, ALL_MULTICAST, BROADCAST
+ */
+ *params->filter = (u16)get_unaligned_le32(buf);
+ pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER %08x\n",
+ __func__, *params->filter);
+
+ /* this call has a significant side effect: it's
+ * what makes the packet flow start and stop, like
+ * activating the CDC Ethernet altsetting.
+ */
+ retval = 0;
+ if (*params->filter) {
+ params->state = RNDIS_DATA_INITIALIZED;
+ netif_carrier_on(params->dev);
+ if (netif_running(params->dev))
+ netif_wake_queue(params->dev);
+ } else {
+ params->state = RNDIS_INITIALIZED;
+ netif_carrier_off(params->dev);
+ netif_stop_queue(params->dev);
+ }
+ break;
+
+ case RNDIS_OID_802_3_MULTICAST_LIST:
+ /* I think we can ignore this */
+ pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__);
+ retval = 0;
+ break;
+
+ default:
+ pr_warning("%s: set unknown OID 0x%08X, size %d\n",
+ __func__, OID, buf_len);
+ }
+
+ return retval;
+}
+
+/*
+ * Response Functions
+ */
+
+static int rndis_init_response(int configNr, rndis_init_msg_type *buf)
+{
+ rndis_init_cmplt_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ if (!params->dev)
+ return -ENOTSUPP;
+
+ r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_init_cmplt_type *)r->buf;
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_INIT_C);
+ resp->MessageLength = cpu_to_le32(52);
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+ resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION);
+ resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
+ resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
+ resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
+ resp->MaxPacketsPerTransfer = cpu_to_le32(1);
+ resp->MaxTransferSize = cpu_to_le32(
+ params->dev->mtu
+ + sizeof(struct ethhdr)
+ + sizeof(struct rndis_packet_msg_type)
+ + 22);
+ resp->PacketAlignmentFactor = cpu_to_le32(0);
+ resp->AFListOffset = cpu_to_le32(0);
+ resp->AFListSize = cpu_to_le32(0);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
+{
+ rndis_query_cmplt_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ /* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */
+ if (!params->dev)
+ return -ENOTSUPP;
+
+ /*
+ * we need more memory:
+ * gen_ndis_query_resp expects enough space for
+ * rndis_query_cmplt_type followed by data.
+ * oid_supported_list is the largest data reply
+ */
+ r = rndis_add_response(configNr,
+ sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_query_cmplt_type *)r->buf;
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C);
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+
+ if (gen_ndis_query_resp(configNr, le32_to_cpu(buf->OID),
+ le32_to_cpu(buf->InformationBufferOffset)
+ + 8 + (u8 *)buf,
+ le32_to_cpu(buf->InformationBufferLength),
+ r)) {
+ /* OID not supported */
+ resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+ resp->MessageLength = cpu_to_le32(sizeof *resp);
+ resp->InformationBufferLength = cpu_to_le32(0);
+ resp->InformationBufferOffset = cpu_to_le32(0);
+ } else
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+static int rndis_set_response(int configNr, rndis_set_msg_type *buf)
+{
+ u32 BufLength, BufOffset;
+ rndis_set_cmplt_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_set_cmplt_type *)r->buf;
+
+ BufLength = le32_to_cpu(buf->InformationBufferLength);
+ BufOffset = le32_to_cpu(buf->InformationBufferOffset);
+
+#ifdef VERBOSE_DEBUG
+ pr_debug("%s: Length: %d\n", __func__, BufLength);
+ pr_debug("%s: Offset: %d\n", __func__, BufOffset);
+ pr_debug("%s: InfoBuffer: ", __func__);
+
+ for (i = 0; i < BufLength; i++) {
+ pr_debug("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
+ }
+
+ pr_debug("\n");
+#endif
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C);
+ resp->MessageLength = cpu_to_le32(16);
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ if (gen_ndis_set_resp(configNr, le32_to_cpu(buf->OID),
+ ((u8 *)buf) + 8 + BufOffset, BufLength, r))
+ resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+ else
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf)
+{
+ rndis_reset_cmplt_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_reset_cmplt_type *)r->buf;
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_RESET_C);
+ resp->MessageLength = cpu_to_le32(16);
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+ /* resent information */
+ resp->AddressingReset = cpu_to_le32(1);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+static int rndis_keepalive_response(int configNr,
+ rndis_keepalive_msg_type *buf)
+{
+ rndis_keepalive_cmplt_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ /* host "should" check only in RNDIS_DATA_INITIALIZED state */
+
+ r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_keepalive_cmplt_type *)r->buf;
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_KEEPALIVE_C);
+ resp->MessageLength = cpu_to_le32(16);
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+
+/*
+ * Device to Host Comunication
+ */
+static int rndis_indicate_status_msg(int configNr, u32 status)
+{
+ rndis_indicate_status_msg_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ if (params->state == RNDIS_UNINITIALIZED)
+ return -ENOTSUPP;
+
+ r = rndis_add_response(configNr,
+ sizeof(rndis_indicate_status_msg_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_indicate_status_msg_type *)r->buf;
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_INDICATE);
+ resp->MessageLength = cpu_to_le32(20);
+ resp->Status = cpu_to_le32(status);
+ resp->StatusBufferLength = cpu_to_le32(0);
+ resp->StatusBufferOffset = cpu_to_le32(0);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+int rndis_signal_connect(int configNr)
+{
+ rndis_per_dev_params[configNr].media_state
+ = RNDIS_MEDIA_STATE_CONNECTED;
+ return rndis_indicate_status_msg(configNr,
+ RNDIS_STATUS_MEDIA_CONNECT);
+}
+
+int rndis_signal_disconnect(int configNr)
+{
+ rndis_per_dev_params[configNr].media_state
+ = RNDIS_MEDIA_STATE_DISCONNECTED;
+ return rndis_indicate_status_msg(configNr,
+ RNDIS_STATUS_MEDIA_DISCONNECT);
+}
+
+void rndis_uninit(int configNr)
+{
+ u8 *buf;
+ u32 length;
+
+ if (configNr >= RNDIS_MAX_CONFIGS)
+ return;
+ rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED;
+
+ /* drain the response queue */
+ while ((buf = rndis_get_next_response(configNr, &length)))
+ rndis_free_response(configNr, buf);
+}
+
+void rndis_set_host_mac(int configNr, const u8 *addr)
+{
+ rndis_per_dev_params[configNr].host_mac = addr;
+}
+
+/*
+ * Message Parser
+ */
+int rndis_msg_parser(u8 configNr, u8 *buf)
+{
+ u32 MsgType, MsgLength;
+ __le32 *tmp;
+ struct rndis_params *params;
+
+ if (!buf)
+ return -ENOMEM;
+
+ tmp = (__le32 *)buf;
+ MsgType = get_unaligned_le32(tmp++);
+ MsgLength = get_unaligned_le32(tmp++);
+
+ if (configNr >= RNDIS_MAX_CONFIGS)
+ return -ENOTSUPP;
+ params = &rndis_per_dev_params[configNr];
+
+ /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
+ * rx/tx statistics and link status, in addition to KEEPALIVE traffic
+ * and normal HC level polling to see if there's any IN traffic.
+ */
+
+ /* For USB: responses may take up to 10 seconds */
+ switch (MsgType) {
+ case RNDIS_MSG_INIT:
+ pr_debug("%s: RNDIS_MSG_INIT\n",
+ __func__);
+ params->state = RNDIS_INITIALIZED;
+ return rndis_init_response(configNr,
+ (rndis_init_msg_type *)buf);
+
+ case RNDIS_MSG_HALT:
+ pr_debug("%s: RNDIS_MSG_HALT\n",
+ __func__);
+ params->state = RNDIS_UNINITIALIZED;
+ if (params->dev) {
+ netif_carrier_off(params->dev);
+ netif_stop_queue(params->dev);
+ }
+ return 0;
+
+ case RNDIS_MSG_QUERY:
+ return rndis_query_response(configNr,
+ (rndis_query_msg_type *)buf);
+
+ case RNDIS_MSG_SET:
+ return rndis_set_response(configNr,
+ (rndis_set_msg_type *)buf);
+
+ case RNDIS_MSG_RESET:
+ pr_debug("%s: RNDIS_MSG_RESET\n",
+ __func__);
+ return rndis_reset_response(configNr,
+ (rndis_reset_msg_type *)buf);
+
+ case RNDIS_MSG_KEEPALIVE:
+ /* For USB: host does this every 5 seconds */
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_MSG_KEEPALIVE\n",
+ __func__);
+ return rndis_keepalive_response(configNr,
+ (rndis_keepalive_msg_type *)
+ buf);
+
+ default:
+ /* At least Windows XP emits some undefined RNDIS messages.
+ * In one case those messages seemed to relate to the host
+ * suspending itself.
+ */
+ pr_warning("%s: unknown RNDIS message 0x%08X len %d\n",
+ __func__, MsgType, MsgLength);
+ print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET,
+ buf, MsgLength);
+ break;
+ }
+
+ return -ENOTSUPP;
+}
+
+int rndis_register(void (*resp_avail)(void *v), void *v)
+{
+ u8 i;
+
+ if (!resp_avail)
+ return -EINVAL;
+
+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+ if (!rndis_per_dev_params[i].used) {
+ rndis_per_dev_params[i].used = 1;
+ rndis_per_dev_params[i].resp_avail = resp_avail;
+ rndis_per_dev_params[i].v = v;
+ pr_debug("%s: configNr = %d\n", __func__, i);
+ return i;
+ }
+ }
+ pr_debug("failed\n");
+
+ return -ENODEV;
+}
+
+void rndis_deregister(int configNr)
+{
+ pr_debug("%s:\n", __func__);
+
+ if (configNr >= RNDIS_MAX_CONFIGS) return;
+ rndis_per_dev_params[configNr].used = 0;
+}
+
+int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
+{
+ pr_debug("%s:\n", __func__);
+ if (!dev)
+ return -EINVAL;
+ if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+
+ rndis_per_dev_params[configNr].dev = dev;
+ rndis_per_dev_params[configNr].filter = cdc_filter;
+
+ return 0;
+}
+
+int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
+{
+ pr_debug("%s:\n", __func__);
+ if (!vendorDescr) return -1;
+ if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+
+ rndis_per_dev_params[configNr].vendorID = vendorID;
+ rndis_per_dev_params[configNr].vendorDescr = vendorDescr;
+
+ return 0;
+}
+
+int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
+{
+ pr_debug("%s: %u %u\n", __func__, medium, speed);
+ if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+
+ rndis_per_dev_params[configNr].medium = medium;
+ rndis_per_dev_params[configNr].speed = speed;
+
+ return 0;
+}
+
+void rndis_add_hdr(struct sk_buff *skb)
+{
+ struct rndis_packet_msg_type *header;
+
+ if (!skb)
+ return;
+ header = (void *)skb_push(skb, sizeof(*header));
+ memset(header, 0, sizeof *header);
+ header->MessageType = cpu_to_le32(RNDIS_MSG_PACKET);
+ header->MessageLength = cpu_to_le32(skb->len);
+ header->DataOffset = cpu_to_le32(36);
+ header->DataLength = cpu_to_le32(skb->len - sizeof(*header));
+}
+
+void rndis_free_response(int configNr, u8 *buf)
+{
+ rndis_resp_t *r;
+ struct list_head *act, *tmp;
+
+ list_for_each_safe(act, tmp,
+ &(rndis_per_dev_params[configNr].resp_queue))
+ {
+ r = list_entry(act, rndis_resp_t, list);
+ if (r && r->buf == buf) {
+ list_del(&r->list);
+ kfree(r);
+ }
+ }
+}
+
+u8 *rndis_get_next_response(int configNr, u32 *length)
+{
+ rndis_resp_t *r;
+ struct list_head *act, *tmp;
+
+ if (!length) return NULL;
+
+ list_for_each_safe(act, tmp,
+ &(rndis_per_dev_params[configNr].resp_queue))
+ {
+ r = list_entry(act, rndis_resp_t, list);
+ if (!r->send) {
+ r->send = 1;
+ *length = r->length;
+ return r->buf;
+ }
+ }
+
+ return NULL;
+}
+
+static rndis_resp_t *rndis_add_response(int configNr, u32 length)
+{
+ rndis_resp_t *r;
+
+ /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */
+ r = kmalloc(sizeof(rndis_resp_t) + length, GFP_ATOMIC);
+ if (!r) return NULL;
+
+ r->buf = (u8 *)(r + 1);
+ r->length = length;
+ r->send = 0;
+
+ list_add_tail(&r->list,
+ &(rndis_per_dev_params[configNr].resp_queue));
+ return r;
+}
+
+int rndis_rm_hdr(struct gether *port,
+ struct sk_buff *skb,
+ struct sk_buff_head *list)
+{
+ /* tmp points to a struct rndis_packet_msg_type */
+ __le32 *tmp = (void *)skb->data;
+
+ /* MessageType, MessageLength */
+ if (cpu_to_le32(RNDIS_MSG_PACKET)
+ != get_unaligned(tmp++)) {
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+ tmp++;
+
+ /* DataOffset, DataLength */
+ if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
+ dev_kfree_skb_any(skb);
+ return -EOVERFLOW;
+ }
+ skb_trim(skb, get_unaligned_le32(tmp++));
+
+ skb_queue_tail(list, skb);
+ return 0;
+}
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static int rndis_proc_show(struct seq_file *m, void *v)
+{
+ rndis_params *param = m->private;
+
+ seq_printf(m,
+ "Config Nr. %d\n"
+ "used : %s\n"
+ "state : %s\n"
+ "medium : 0x%08X\n"
+ "speed : %d\n"
+ "cable : %s\n"
+ "vendor ID : 0x%08X\n"
+ "vendor : %s\n",
+ param->confignr, (param->used) ? "y" : "n",
+ ({ char *s = "?";
+ switch (param->state) {
+ case RNDIS_UNINITIALIZED:
+ s = "RNDIS_UNINITIALIZED"; break;
+ case RNDIS_INITIALIZED:
+ s = "RNDIS_INITIALIZED"; break;
+ case RNDIS_DATA_INITIALIZED:
+ s = "RNDIS_DATA_INITIALIZED"; break;
+ }; s; }),
+ param->medium,
+ (param->media_state) ? 0 : param->speed*100,
+ (param->media_state) ? "disconnected" : "connected",
+ param->vendorID, param->vendorDescr);
+ return 0;
+}
+
+static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ rndis_params *p = PDE(file->f_path.dentry->d_inode)->data;
+ u32 speed = 0;
+ int i, fl_speed = 0;
+
+ for (i = 0; i < count; i++) {
+ char c;
+ if (get_user(c, buffer))
+ return -EFAULT;
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ fl_speed = 1;
+ speed = speed * 10 + c - '0';
+ break;
+ case 'C':
+ case 'c':
+ rndis_signal_connect(p->confignr);
+ break;
+ case 'D':
+ case 'd':
+ rndis_signal_disconnect(p->confignr);
+ break;
+ default:
+ if (fl_speed) p->speed = speed;
+ else pr_debug("%c is not valid\n", c);
+ break;
+ }
+
+ buffer++;
+ }
+
+ return count;
+}
+
+static int rndis_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, rndis_proc_show, PDE(inode)->data);
+}
+
+static const struct file_operations rndis_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = rndis_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = rndis_proc_write,
+};
+
+#define NAME_TEMPLATE "driver/rndis-%03d"
+
+static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+
+int rndis_init(void)
+{
+ u8 i;
+
+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+ char name [20];
+
+ sprintf(name, NAME_TEMPLATE, i);
+ rndis_connect_state[i] = proc_create_data(name, 0660, NULL,
+ &rndis_proc_fops,
+ (void *)(rndis_per_dev_params + i));
+ if (!rndis_connect_state[i]) {
+ pr_debug("%s: remove entries", __func__);
+ while (i) {
+ sprintf(name, NAME_TEMPLATE, --i);
+ remove_proc_entry(name, NULL);
+ }
+ pr_debug("\n");
+ return -EIO;
+ }
+#endif
+ rndis_per_dev_params[i].confignr = i;
+ rndis_per_dev_params[i].used = 0;
+ rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED;
+ rndis_per_dev_params[i].media_state
+ = RNDIS_MEDIA_STATE_DISCONNECTED;
+ INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue));
+ }
+
+ return 0;
+}
+
+void rndis_exit(void)
+{
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+ u8 i;
+ char name[20];
+
+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+ sprintf(name, NAME_TEMPLATE, i);
+ remove_proc_entry(name, NULL);
+ }
+#endif
+}
diff --git a/drivers/staging/ccg/rndis.h b/drivers/staging/ccg/rndis.h
new file mode 100644
index 000000000000..0647f2f34e89
--- /dev/null
+++ b/drivers/staging/ccg/rndis.h
@@ -0,0 +1,222 @@
+/*
+ * RNDIS Definitions for Remote NDIS
+ *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, 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 software was originally developed in conformance with
+ * Microsoft's Remote NDIS Specification License Agreement.
+ */
+
+#ifndef _LINUX_RNDIS_H
+#define _LINUX_RNDIS_H
+
+#include <linux/rndis.h>
+#include "ndis.h"
+
+#define RNDIS_MAXIMUM_FRAME_SIZE 1518
+#define RNDIS_MAX_TOTAL_SIZE 1558
+
+typedef struct rndis_init_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 MajorVersion;
+ __le32 MinorVersion;
+ __le32 MaxTransferSize;
+} rndis_init_msg_type;
+
+typedef struct rndis_init_cmplt_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+ __le32 MajorVersion;
+ __le32 MinorVersion;
+ __le32 DeviceFlags;
+ __le32 Medium;
+ __le32 MaxPacketsPerTransfer;
+ __le32 MaxTransferSize;
+ __le32 PacketAlignmentFactor;
+ __le32 AFListOffset;
+ __le32 AFListSize;
+} rndis_init_cmplt_type;
+
+typedef struct rndis_halt_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+} rndis_halt_msg_type;
+
+typedef struct rndis_query_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 OID;
+ __le32 InformationBufferLength;
+ __le32 InformationBufferOffset;
+ __le32 DeviceVcHandle;
+} rndis_query_msg_type;
+
+typedef struct rndis_query_cmplt_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+ __le32 InformationBufferLength;
+ __le32 InformationBufferOffset;
+} rndis_query_cmplt_type;
+
+typedef struct rndis_set_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 OID;
+ __le32 InformationBufferLength;
+ __le32 InformationBufferOffset;
+ __le32 DeviceVcHandle;
+} rndis_set_msg_type;
+
+typedef struct rndis_set_cmplt_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+} rndis_set_cmplt_type;
+
+typedef struct rndis_reset_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 Reserved;
+} rndis_reset_msg_type;
+
+typedef struct rndis_reset_cmplt_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 Status;
+ __le32 AddressingReset;
+} rndis_reset_cmplt_type;
+
+typedef struct rndis_indicate_status_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 Status;
+ __le32 StatusBufferLength;
+ __le32 StatusBufferOffset;
+} rndis_indicate_status_msg_type;
+
+typedef struct rndis_keepalive_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+} rndis_keepalive_msg_type;
+
+typedef struct rndis_keepalive_cmplt_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+} rndis_keepalive_cmplt_type;
+
+struct rndis_packet_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 DataOffset;
+ __le32 DataLength;
+ __le32 OOBDataOffset;
+ __le32 OOBDataLength;
+ __le32 NumOOBDataElements;
+ __le32 PerPacketInfoOffset;
+ __le32 PerPacketInfoLength;
+ __le32 VcHandle;
+ __le32 Reserved;
+} __attribute__ ((packed));
+
+struct rndis_config_parameter
+{
+ __le32 ParameterNameOffset;
+ __le32 ParameterNameLength;
+ __le32 ParameterType;
+ __le32 ParameterValueOffset;
+ __le32 ParameterValueLength;
+};
+
+/* implementation specific */
+enum rndis_state
+{
+ RNDIS_UNINITIALIZED,
+ RNDIS_INITIALIZED,
+ RNDIS_DATA_INITIALIZED,
+};
+
+typedef struct rndis_resp_t
+{
+ struct list_head list;
+ u8 *buf;
+ u32 length;
+ int send;
+} rndis_resp_t;
+
+typedef struct rndis_params
+{
+ u8 confignr;
+ u8 used;
+ u16 saved_filter;
+ enum rndis_state state;
+ u32 medium;
+ u32 speed;
+ u32 media_state;
+
+ const u8 *host_mac;
+ u16 *filter;
+ struct net_device *dev;
+
+ u32 vendorID;
+ const char *vendorDescr;
+ void (*resp_avail)(void *v);
+ void *v;
+ struct list_head resp_queue;
+} rndis_params;
+
+/* RNDIS Message parser and other useless functions */
+int rndis_msg_parser (u8 configNr, u8 *buf);
+int rndis_register(void (*resp_avail)(void *v), void *v);
+void rndis_deregister (int configNr);
+int rndis_set_param_dev (u8 configNr, struct net_device *dev,
+ u16 *cdc_filter);
+int rndis_set_param_vendor (u8 configNr, u32 vendorID,
+ const char *vendorDescr);
+int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
+void rndis_add_hdr (struct sk_buff *skb);
+int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
+ struct sk_buff_head *list);
+u8 *rndis_get_next_response (int configNr, u32 *length);
+void rndis_free_response (int configNr, u8 *buf);
+
+void rndis_uninit (int configNr);
+int rndis_signal_connect (int configNr);
+int rndis_signal_disconnect (int configNr);
+int rndis_state (int configNr);
+extern void rndis_set_host_mac (int configNr, const u8 *addr);
+
+int rndis_init(void);
+void rndis_exit (void);
+
+#endif /* _LINUX_RNDIS_H */
diff --git a/drivers/staging/ccg/storage_common.c b/drivers/staging/ccg/storage_common.c
new file mode 100644
index 000000000000..8d9bcd8207c8
--- /dev/null
+++ b/drivers/staging/ccg/storage_common.c
@@ -0,0 +1,893 @@
+/*
+ * storage_common.c -- Common definitions for mass storage functionality
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyeight (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz (mina86@mina86.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 file requires the following identifiers used in USB strings to
+ * be defined (each of type pointer to char):
+ * - fsg_string_manufacturer -- name of the manufacturer
+ * - fsg_string_product -- name of the product
+ * - fsg_string_config -- name of the configuration
+ * - fsg_string_interface -- name of the interface
+ * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS
+ * macro is defined prior to including this file.
+ */
+
+/*
+ * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and
+ * fsg_hs_intr_in_desc objects as well as
+ * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES
+ * macros are not defined.
+ *
+ * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER,
+ * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not
+ * defined (as well as corresponding entries in string tables are
+ * missing) and FSG_STRING_INTERFACE has value of zero.
+ *
+ * When FSG_NO_OTG is defined fsg_otg_desc won't be defined.
+ */
+
+/*
+ * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
+ * sets the number of pipeline buffers (length of the fsg_buffhd array).
+ * The valid range of num_buffers is: num >= 2 && num <= 4.
+ */
+
+
+#include <linux/usb/storage.h>
+#include <scsi/scsi.h>
+#include <asm/unaligned.h>
+
+
+/*
+ * Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+#define FSG_VENDOR_ID 0x0525 /* NetChip */
+#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
+
+
+/*-------------------------------------------------------------------------*/
+
+
+#ifndef DEBUG
+#undef VERBOSE_DEBUG
+#undef DUMP_MSGS
+#endif /* !DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VLDBG LDBG
+#else
+#define VLDBG(lun, fmt, args...) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args)
+#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args)
+#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
+#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args)
+
+/*
+ * Keep those macros in sync with those in
+ * include/linux/usb/composite.h or else GCC will complain. If they
+ * are identical (the same names of arguments, white spaces in the
+ * same places) GCC will allow redefinition otherwise (even if some
+ * white space is removed or added) warning will be issued.
+ *
+ * Those macros are needed here because File Storage Gadget does not
+ * include the composite.h header. For composite gadgets those macros
+ * are redundant since composite.h is included any way.
+ *
+ * One could check whether those macros are already defined (which
+ * would indicate composite.h had been included) or not (which would
+ * indicate we were in FSG) but this is not done because a warning is
+ * desired if definitions here differ from the ones in composite.h.
+ *
+ * We want the definitions to match and be the same in File Storage
+ * Gadget as well as Mass Storage Function (and so composite gadgets
+ * using MSF). If someone changes them in composite.h it will produce
+ * a warning in this file when building MSF.
+ */
+#define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args)
+#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+#define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args)
+#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev , fmt , ## args)
+#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev , fmt , ## args)
+
+
+
+#ifdef DUMP_MSGS
+
+# define dump_msg(fsg, /* const char * */ label, \
+ /* const u8 * */ buf, /* unsigned */ length) do { \
+ if (length < 512) { \
+ DBG(fsg, "%s, length %u:\n", label, length); \
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \
+ 16, 1, buf, length, 0); \
+ } \
+} while (0)
+
+# define dump_cdb(fsg) do { } while (0)
+
+#else
+
+# define dump_msg(fsg, /* const char * */ label, \
+ /* const u8 * */ buf, /* unsigned */ length) do { } while (0)
+
+# ifdef VERBOSE_DEBUG
+
+# define dump_cdb(fsg) \
+ print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \
+ 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \
+
+# else
+
+# define dump_cdb(fsg) do { } while (0)
+
+# endif /* VERBOSE_DEBUG */
+
+#endif /* DUMP_MSGS */
+
+/*-------------------------------------------------------------------------*/
+
+/* CBI Interrupt data structure */
+struct interrupt_data {
+ u8 bType;
+ u8 bValue;
+};
+
+#define CBI_INTERRUPT_DATA_LEN 2
+
+/* CBI Accept Device-Specific Command request */
+#define USB_CBI_ADSC_REQUEST 0x00
+
+
+/* Length of a SCSI Command Data Block */
+#define MAX_COMMAND_SIZE 16
+
+/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
+#define SS_NO_SENSE 0
+#define SS_COMMUNICATION_FAILURE 0x040800
+#define SS_INVALID_COMMAND 0x052000
+#define SS_INVALID_FIELD_IN_CDB 0x052400
+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
+#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
+#define SS_MEDIUM_NOT_PRESENT 0x023a00
+#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
+#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
+#define SS_RESET_OCCURRED 0x062900
+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
+#define SS_UNRECOVERED_READ_ERROR 0x031100
+#define SS_WRITE_ERROR 0x030c02
+#define SS_WRITE_PROTECTED 0x072700
+
+#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */
+#define ASC(x) ((u8) ((x) >> 8))
+#define ASCQ(x) ((u8) (x))
+
+
+/*-------------------------------------------------------------------------*/
+
+
+struct fsg_lun {
+ struct file *filp;
+ loff_t file_length;
+ loff_t num_sectors;
+
+ unsigned int initially_ro:1;
+ unsigned int ro:1;
+ unsigned int removable:1;
+ unsigned int cdrom:1;
+ unsigned int prevent_medium_removal:1;
+ unsigned int registered:1;
+ unsigned int info_valid:1;
+ unsigned int nofua:1;
+
+ u32 sense_data;
+ u32 sense_data_info;
+ u32 unit_attention_data;
+
+ unsigned int blkbits; /* Bits of logical block size of bound block device */
+ unsigned int blksize; /* logical block size of bound block device */
+ struct device dev;
+};
+
+#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL)
+
+static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
+{
+ return container_of(dev, struct fsg_lun, dev);
+}
+
+
+/* Big enough to hold our biggest descriptor */
+#define EP0_BUFSIZE 256
+#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);
+MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers");
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_DEBUG */
+
+/* check if fsg_num_buffers is within a valid range */
+static inline int fsg_num_buffers_validate(void)
+{
+ if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
+ return 0;
+ pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
+ fsg_num_buffers, 2 ,4);
+ return -EINVAL;
+}
+
+/* Default size of buffer length. */
+#define FSG_BUFLEN ((u32)16384)
+
+/* Maximal number of LUNs supported in mass storage function */
+#define FSG_MAX_LUNS 8
+
+enum fsg_buffer_state {
+ BUF_STATE_EMPTY = 0,
+ BUF_STATE_FULL,
+ BUF_STATE_BUSY
+};
+
+struct fsg_buffhd {
+ void *buf;
+ enum fsg_buffer_state state;
+ struct fsg_buffhd *next;
+
+ /*
+ * The NetChip 2280 is faster, and handles some protocol faults
+ * better, if we don't submit any short bulk-out read requests.
+ * So we will record the intended request length here.
+ */
+ unsigned int bulk_out_intended_length;
+
+ struct usb_request *inreq;
+ int inreq_busy;
+ struct usb_request *outreq;
+ int outreq_busy;
+};
+
+enum fsg_state {
+ /* This one isn't used anywhere */
+ FSG_STATE_COMMAND_PHASE = -10,
+ FSG_STATE_DATA_PHASE,
+ FSG_STATE_STATUS_PHASE,
+
+ FSG_STATE_IDLE = 0,
+ FSG_STATE_ABORT_BULK_OUT,
+ FSG_STATE_RESET,
+ FSG_STATE_INTERFACE_CHANGE,
+ FSG_STATE_CONFIG_CHANGE,
+ FSG_STATE_DISCONNECT,
+ FSG_STATE_EXIT,
+ FSG_STATE_TERMINATED
+};
+
+enum data_direction {
+ DATA_DIR_UNKNOWN = 0,
+ DATA_DIR_FROM_HOST,
+ DATA_DIR_TO_HOST,
+ DATA_DIR_NONE
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static inline u32 get_unaligned_be24(u8 *buf)
+{
+ return 0xffffff & (u32) get_unaligned_be32(buf - 1);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+enum {
+#ifndef FSG_NO_DEVICE_STRINGS
+ FSG_STRING_MANUFACTURER = 1,
+ FSG_STRING_PRODUCT,
+ FSG_STRING_SERIAL,
+ FSG_STRING_CONFIG,
+#endif
+ FSG_STRING_INTERFACE
+};
+
+
+#ifndef FSG_NO_OTG
+static struct usb_otg_descriptor
+fsg_otg_desc = {
+ .bLength = sizeof fsg_otg_desc,
+ .bDescriptorType = USB_DT_OTG,
+
+ .bmAttributes = USB_OTG_SRP,
+};
+#endif
+
+/* There is only one interface. */
+
+static struct usb_interface_descriptor
+fsg_intf_desc = {
+ .bLength = sizeof fsg_intf_desc,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bNumEndpoints = 2, /* Adjusted during fsg_bind() */
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */
+ .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */
+ .iInterface = FSG_STRING_INTERFACE,
+};
+
+/*
+ * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
+ * interrupt-in.
+ */
+
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* wMaxPacketSize set by autoconfiguration */
+};
+
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* wMaxPacketSize set by autoconfiguration */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_fs_intr_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 32, /* frames -> 32 ms */
+};
+
+#ifndef FSG_NO_OTG
+# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2
+#else
+# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 1
+#endif
+
+#endif
+
+static struct usb_descriptor_header *fsg_fs_function[] = {
+#ifndef FSG_NO_OTG
+ (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
+#ifndef FSG_NO_INTR_EP
+ (struct usb_descriptor_header *) &fsg_fs_intr_in_desc,
+#endif
+ NULL,
+};
+
+
+/*
+ * USB 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ *
+ * That means alternate endpoint descriptors (bigger packets)
+ * and a "device qualifier" ... plus more construction options
+ * for the configuration descriptor.
+ */
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+ .bInterval = 1, /* NAK every 1 uframe */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_hs_intr_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */
+};
+
+#ifndef FSG_NO_OTG
+# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2
+#else
+# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 1
+#endif
+
+#endif
+
+static struct usb_descriptor_header *fsg_hs_function[] = {
+#ifndef FSG_NO_OTG
+ (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
+#ifndef FSG_NO_INTR_EP
+ (struct usb_descriptor_header *) &fsg_hs_intr_in_desc,
+#endif
+ NULL,
+};
+
+static struct usb_endpoint_descriptor
+fsg_ss_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
+ .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /*.bMaxBurst = DYNAMIC, */
+};
+
+static struct usb_endpoint_descriptor
+fsg_ss_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
+ .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /*.bMaxBurst = DYNAMIC, */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_ss_intr_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_intr_in_comp_desc = {
+ .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ .wBytesPerInterval = cpu_to_le16(2),
+};
+
+#ifndef FSG_NO_OTG
+# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 2
+#else
+# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 1
+#endif
+
+#endif
+
+static __maybe_unused struct usb_ext_cap_descriptor fsg_ext_cap_desc = {
+ .bLength = USB_DT_USB_EXT_CAP_SIZE,
+ .bDescriptorType = USB_DT_DEVICE_CAPABILITY,
+ .bDevCapabilityType = USB_CAP_TYPE_EXT,
+
+ .bmAttributes = cpu_to_le32(USB_LPM_SUPPORT),
+};
+
+static __maybe_unused struct usb_ss_cap_descriptor fsg_ss_cap_desc = {
+ .bLength = USB_DT_USB_SS_CAP_SIZE,
+ .bDescriptorType = USB_DT_DEVICE_CAPABILITY,
+ .bDevCapabilityType = USB_SS_CAP_TYPE,
+
+ /* .bmAttributes = LTM is not supported yet */
+
+ .wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION
+ | USB_FULL_SPEED_OPERATION
+ | USB_HIGH_SPEED_OPERATION
+ | USB_5GBPS_OPERATION),
+ .bFunctionalitySupport = USB_LOW_SPEED_OPERATION,
+ .bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT,
+ .bU2DevExitLat = cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT),
+};
+
+static __maybe_unused struct usb_bos_descriptor fsg_bos_desc = {
+ .bLength = USB_DT_BOS_SIZE,
+ .bDescriptorType = USB_DT_BOS,
+
+ .wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE
+ + USB_DT_USB_EXT_CAP_SIZE
+ + USB_DT_USB_SS_CAP_SIZE),
+
+ .bNumDeviceCaps = 2,
+};
+
+static struct usb_descriptor_header *fsg_ss_function[] = {
+#ifndef FSG_NO_OTG
+ (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
+#ifndef FSG_NO_INTR_EP
+ (struct usb_descriptor_header *) &fsg_ss_intr_in_desc,
+ (struct usb_descriptor_header *) &fsg_ss_intr_in_comp_desc,
+#endif
+ NULL,
+};
+
+/* Maxpacket and other transfer characteristics vary by speed. */
+static __maybe_unused struct usb_endpoint_descriptor *
+fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
+ struct usb_endpoint_descriptor *hs,
+ struct usb_endpoint_descriptor *ss)
+{
+ if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+ return ss;
+ else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return hs;
+ return fs;
+}
+
+
+/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
+static struct usb_string fsg_strings[] = {
+#ifndef FSG_NO_DEVICE_STRINGS
+ {FSG_STRING_MANUFACTURER, fsg_string_manufacturer},
+ {FSG_STRING_PRODUCT, fsg_string_product},
+ {FSG_STRING_SERIAL, ""},
+ {FSG_STRING_CONFIG, fsg_string_config},
+#endif
+ {FSG_STRING_INTERFACE, fsg_string_interface},
+ {}
+};
+
+static struct usb_gadget_strings fsg_stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = fsg_strings,
+};
+
+
+ /*-------------------------------------------------------------------------*/
+
+/*
+ * If the next two routines are called while the gadget is registered,
+ * the caller must own fsg->filesem for writing.
+ */
+
+static void fsg_lun_close(struct fsg_lun *curlun)
+{
+ if (curlun->filp) {
+ LDBG(curlun, "close backing file\n");
+ fput(curlun->filp);
+ curlun->filp = NULL;
+ }
+}
+
+
+static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
+{
+ int ro;
+ struct file *filp = NULL;
+ int rc = -EINVAL;
+ struct inode *inode = NULL;
+ loff_t size;
+ loff_t num_sectors;
+ loff_t min_sectors;
+ unsigned int blkbits;
+ unsigned int blksize;
+
+ /* R/W if we can, R/O if we must */
+ ro = curlun->initially_ro;
+ if (!ro) {
+ filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
+ if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES)
+ ro = 1;
+ }
+ if (ro)
+ filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0);
+ if (IS_ERR(filp)) {
+ LINFO(curlun, "unable to open backing file: %s\n", filename);
+ return PTR_ERR(filp);
+ }
+
+ if (!(filp->f_mode & FMODE_WRITE))
+ ro = 1;
+
+ inode = filp->f_path.dentry->d_inode;
+ if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
+ LINFO(curlun, "invalid file type: %s\n", filename);
+ goto out;
+ }
+
+ /*
+ * If we can't read the file, it's no good.
+ * If we can't write the file, use it read-only.
+ */
+ if (!(filp->f_op->read || filp->f_op->aio_read)) {
+ LINFO(curlun, "file not readable: %s\n", filename);
+ goto out;
+ }
+ if (!(filp->f_op->write || filp->f_op->aio_write))
+ ro = 1;
+
+ size = i_size_read(inode->i_mapping->host);
+ if (size < 0) {
+ LINFO(curlun, "unable to find file size: %s\n", filename);
+ rc = (int) size;
+ goto out;
+ }
+
+ if (curlun->cdrom) {
+ blksize = 2048;
+ blkbits = 11;
+ } else if (inode->i_bdev) {
+ blksize = bdev_logical_block_size(inode->i_bdev);
+ blkbits = blksize_bits(blksize);
+ } else {
+ blksize = 512;
+ blkbits = 9;
+ }
+
+ num_sectors = size >> blkbits; /* File size in logic-block-size blocks */
+ min_sectors = 1;
+ if (curlun->cdrom) {
+ min_sectors = 300; /* Smallest track is 300 frames */
+ if (num_sectors >= 256*60*75) {
+ num_sectors = 256*60*75 - 1;
+ LINFO(curlun, "file too big: %s\n", filename);
+ LINFO(curlun, "using only first %d blocks\n",
+ (int) num_sectors);
+ }
+ }
+ if (num_sectors < min_sectors) {
+ LINFO(curlun, "file too small: %s\n", filename);
+ rc = -ETOOSMALL;
+ goto out;
+ }
+
+ if (fsg_lun_is_open(curlun))
+ fsg_lun_close(curlun);
+
+ curlun->blksize = blksize;
+ curlun->blkbits = blkbits;
+ curlun->ro = ro;
+ curlun->filp = filp;
+ curlun->file_length = size;
+ curlun->num_sectors = num_sectors;
+ LDBG(curlun, "open backing file: %s\n", filename);
+ return 0;
+
+out:
+ fput(filp);
+ return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Sync the file data, don't bother with the metadata.
+ * This code was copied from fs/buffer.c:sys_fdatasync().
+ */
+static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
+{
+ struct file *filp = curlun->filp;
+
+ if (curlun->ro || !filp)
+ return 0;
+ return vfs_fsync(filp, 1);
+}
+
+static void store_cdrom_address(u8 *dest, int msf, u32 addr)
+{
+ if (msf) {
+ /* Convert to Minutes-Seconds-Frames */
+ addr >>= 2; /* Convert to 2048-byte frames */
+ addr += 2*75; /* Lead-in occupies 2 seconds */
+ dest[3] = addr % 75; /* Frames */
+ addr /= 75;
+ dest[2] = addr % 60; /* Seconds */
+ addr /= 60;
+ dest[1] = addr; /* Minutes */
+ dest[0] = 0; /* Reserved */
+ } else {
+ /* Absolute sector */
+ put_unaligned_be32(addr, dest);
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+
+ return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
+ ? curlun->ro
+ : curlun->initially_ro);
+}
+
+static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+
+ return sprintf(buf, "%u\n", curlun->nofua);
+}
+
+static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
+ char *p;
+ ssize_t rc;
+
+ down_read(filesem);
+ if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */
+ p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1);
+ if (IS_ERR(p))
+ rc = PTR_ERR(p);
+ else {
+ rc = strlen(p);
+ memmove(buf, p, rc);
+ buf[rc] = '\n'; /* Add a newline */
+ buf[++rc] = 0;
+ }
+ } else { /* No file, return 0 bytes */
+ *buf = 0;
+ rc = 0;
+ }
+ up_read(filesem);
+ return rc;
+}
+
+
+static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ ssize_t rc;
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
+ unsigned ro;
+
+ rc = kstrtouint(buf, 2, &ro);
+ if (rc)
+ return rc;
+
+ /*
+ * Allow the write-enable status to change only while the
+ * backing file is closed.
+ */
+ down_read(filesem);
+ if (fsg_lun_is_open(curlun)) {
+ LDBG(curlun, "read-only status change prevented\n");
+ rc = -EBUSY;
+ } else {
+ curlun->ro = ro;
+ curlun->initially_ro = ro;
+ LDBG(curlun, "read-only status set to %d\n", curlun->ro);
+ rc = count;
+ }
+ up_read(filesem);
+ return rc;
+}
+
+static ssize_t fsg_store_nofua(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ unsigned nofua;
+ int ret;
+
+ ret = kstrtouint(buf, 2, &nofua);
+ if (ret)
+ return ret;
+
+ /* Sync data when switching from async mode to sync */
+ if (!nofua && curlun->nofua)
+ fsg_lun_fsync_sub(curlun);
+
+ curlun->nofua = nofua;
+
+ return count;
+}
+
+static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
+ int rc = 0;
+
+ if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
+ LDBG(curlun, "eject attempt prevented\n");
+ return -EBUSY; /* "Door is locked" */
+ }
+
+ /* Remove a trailing newline */
+ if (count > 0 && buf[count-1] == '\n')
+ ((char *) buf)[count-1] = 0; /* Ugh! */
+
+ /* Load new medium */
+ down_write(filesem);
+ if (count > 0 && buf[0]) {
+ /* fsg_lun_open() will close existing file if any. */
+ rc = fsg_lun_open(curlun, buf);
+ if (rc == 0)
+ curlun->unit_attention_data =
+ SS_NOT_READY_TO_READY_TRANSITION;
+ } else if (fsg_lun_is_open(curlun)) {
+ fsg_lun_close(curlun);
+ curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
+ }
+ up_write(filesem);
+ return (rc < 0 ? rc : count);
+}
diff --git a/drivers/staging/ccg/u_ether.c b/drivers/staging/ccg/u_ether.c
new file mode 100644
index 000000000000..1154a99dc8db
--- /dev/null
+++ b/drivers/staging/ccg/u_ether.c
@@ -0,0 +1,986 @@
+/*
+ * u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia 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.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+
+#include "u_ether.h"
+
+
+/*
+ * This component encapsulates the Ethernet link glue needed to provide
+ * one (!) network link through the USB gadget stack, normally "usb0".
+ *
+ * The control and data models are handled by the function driver which
+ * connects to this code; such as CDC Ethernet (ECM or EEM),
+ * "CDC Subset", or RNDIS. That includes all descriptor and endpoint
+ * management.
+ *
+ * Link level addressing is handled by this component using module
+ * parameters; if no such parameters are provided, random link level
+ * addresses are used. Each end of the link uses one address. The
+ * host end address is exported in various ways, and is often recorded
+ * in configuration databases.
+ *
+ * The driver which assembles each configuration using such a link is
+ * responsible for ensuring that each configuration includes at most one
+ * instance of is network link. (The network layer provides ways for
+ * this single "physical" link to be used by multiple virtual links.)
+ */
+
+#define UETH__VERSION "29-May-2008"
+
+struct eth_dev {
+ /* lock is held while accessing port_usb
+ * or updating its backlink port_usb->ioport
+ */
+ spinlock_t lock;
+ struct gether *port_usb;
+
+ struct net_device *net;
+ struct usb_gadget *gadget;
+
+ spinlock_t req_lock; /* guard {rx,tx}_reqs */
+ struct list_head tx_reqs, rx_reqs;
+ atomic_t tx_qlen;
+
+ struct sk_buff_head rx_frames;
+
+ unsigned header_len;
+ struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb);
+ int (*unwrap)(struct gether *,
+ struct sk_buff *skb,
+ struct sk_buff_head *list);
+
+ struct work_struct work;
+
+ unsigned long todo;
+#define WORK_RX_MEMORY 0
+
+ bool zlp;
+ u8 host_mac[ETH_ALEN];
+};
+
+/*-------------------------------------------------------------------------*/
+
+#define RX_EXTRA 20 /* bytes guarding against rx overflows */
+
+#define DEFAULT_QLEN 2 /* double buffering by default */
+
+static unsigned qmult = 5;
+module_param(qmult, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");
+
+/* for dual-speed hardware, use deeper queues at high/super speed */
+static inline int qlen(struct usb_gadget *gadget)
+{
+ if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
+ gadget->speed == USB_SPEED_SUPER))
+ return qmult * DEFAULT_QLEN;
+ else
+ return DEFAULT_QLEN;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* REVISIT there must be a better way than having two sets
+ * of debug calls ...
+ */
+
+#undef DBG
+#undef VDBG
+#undef ERROR
+#undef INFO
+
+#define xprintk(d, level, fmt, args...) \
+ printk(level "%s: " fmt , (d)->net->name , ## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DBG(dev, fmt, args...) \
+ xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev, fmt, args...) \
+ do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VDBG DBG
+#else
+#define VDBG(dev, fmt, args...) \
+ do { } while (0)
+#endif /* DEBUG */
+
+#define ERROR(dev, fmt, args...) \
+ xprintk(dev , KERN_ERR , fmt , ## args)
+#define INFO(dev, fmt, args...) \
+ xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
+
+static int ueth_change_mtu(struct net_device *net, int new_mtu)
+{
+ struct eth_dev *dev = netdev_priv(net);
+ unsigned long flags;
+ int status = 0;
+
+ /* don't change MTU on "live" link (peer won't know) */
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb)
+ status = -EBUSY;
+ else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN)
+ status = -ERANGE;
+ else
+ net->mtu = new_mtu;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return status;
+}
+
+static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
+{
+ struct eth_dev *dev = netdev_priv(net);
+
+ strlcpy(p->driver, "g_ether", sizeof p->driver);
+ strlcpy(p->version, UETH__VERSION, sizeof p->version);
+ strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version);
+ strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info);
+}
+
+/* REVISIT can also support:
+ * - WOL (by tracking suspends and issuing remote wakeup)
+ * - msglevel (implies updated messaging)
+ * - ... probably more ethtool ops
+ */
+
+static const struct ethtool_ops ops = {
+ .get_drvinfo = eth_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
+
+static void defer_kevent(struct eth_dev *dev, int flag)
+{
+ if (test_and_set_bit(flag, &dev->todo))
+ return;
+ if (!schedule_work(&dev->work))
+ ERROR(dev, "kevent %d may have been dropped\n", flag);
+ else
+ DBG(dev, "kevent %d scheduled\n", flag);
+}
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req);
+
+static int
+rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
+{
+ struct sk_buff *skb;
+ int retval = -ENOMEM;
+ size_t size = 0;
+ struct usb_ep *out;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb)
+ out = dev->port_usb->out_ep;
+ else
+ out = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (!out)
+ return -ENOTCONN;
+
+
+ /* Padding up to RX_EXTRA handles minor disagreements with host.
+ * Normally we use the USB "terminate on short read" convention;
+ * so allow up to (N*maxpacket), since that memory is normally
+ * already allocated. Some hardware doesn't deal well with short
+ * reads (e.g. DMA must be N*maxpacket), so for now don't trim a
+ * byte off the end (to force hardware errors on overflow).
+ *
+ * RNDIS uses internal framing, and explicitly allows senders to
+ * pad to end-of-packet. That's potentially nice for speed, but
+ * means receivers can't recover lost synch on their own (because
+ * new packets don't only start after a short RX).
+ */
+ size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA;
+ size += dev->port_usb->header_len;
+ size += out->maxpacket - 1;
+ size -= size % out->maxpacket;
+
+ if (dev->port_usb->is_fixed)
+ size = max_t(size_t, size, dev->port_usb->fixed_out_len);
+
+ skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
+ if (skb == NULL) {
+ DBG(dev, "no rx skb\n");
+ goto enomem;
+ }
+
+ /* Some platforms perform better when IP packets are aligned,
+ * but on at least one, checksumming fails otherwise. Note:
+ * RNDIS headers involve variable numbers of LE32 values.
+ */
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ req->buf = skb->data;
+ req->length = size;
+ req->complete = rx_complete;
+ req->context = skb;
+
+ retval = usb_ep_queue(out, req, gfp_flags);
+ if (retval == -ENOMEM)
+enomem:
+ defer_kevent(dev, WORK_RX_MEMORY);
+ if (retval) {
+ DBG(dev, "rx submit --> %d\n", retval);
+ if (skb)
+ dev_kfree_skb_any(skb);
+ spin_lock_irqsave(&dev->req_lock, flags);
+ list_add(&req->list, &dev->rx_reqs);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+ }
+ return retval;
+}
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct sk_buff *skb = req->context, *skb2;
+ struct eth_dev *dev = ep->driver_data;
+ int status = req->status;
+
+ switch (status) {
+
+ /* normal completion */
+ case 0:
+ skb_put(skb, req->actual);
+
+ if (dev->unwrap) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb) {
+ status = dev->unwrap(dev->port_usb,
+ skb,
+ &dev->rx_frames);
+ } else {
+ dev_kfree_skb_any(skb);
+ status = -ENOTCONN;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ } else {
+ skb_queue_tail(&dev->rx_frames, skb);
+ }
+ skb = NULL;
+
+ skb2 = skb_dequeue(&dev->rx_frames);
+ while (skb2) {
+ if (status < 0
+ || ETH_HLEN > skb2->len
+ || skb2->len > ETH_FRAME_LEN) {
+ dev->net->stats.rx_errors++;
+ dev->net->stats.rx_length_errors++;
+ DBG(dev, "rx length %d\n", skb2->len);
+ dev_kfree_skb_any(skb2);
+ goto next_frame;
+ }
+ skb2->protocol = eth_type_trans(skb2, dev->net);
+ dev->net->stats.rx_packets++;
+ dev->net->stats.rx_bytes += skb2->len;
+
+ /* no buffer copies needed, unless hardware can't
+ * use skb buffers.
+ */
+ status = netif_rx(skb2);
+next_frame:
+ skb2 = skb_dequeue(&dev->rx_frames);
+ }
+ break;
+
+ /* software-driven interface shutdown */
+ case -ECONNRESET: /* unlink */
+ case -ESHUTDOWN: /* disconnect etc */
+ VDBG(dev, "rx shutdown, code %d\n", status);
+ goto quiesce;
+
+ /* for hardware automagic (such as pxa) */
+ case -ECONNABORTED: /* endpoint reset */
+ DBG(dev, "rx %s reset\n", ep->name);
+ defer_kevent(dev, WORK_RX_MEMORY);
+quiesce:
+ dev_kfree_skb_any(skb);
+ goto clean;
+
+ /* data overrun */
+ case -EOVERFLOW:
+ dev->net->stats.rx_over_errors++;
+ /* FALLTHROUGH */
+
+ default:
+ dev->net->stats.rx_errors++;
+ DBG(dev, "rx status %d\n", status);
+ break;
+ }
+
+ if (skb)
+ dev_kfree_skb_any(skb);
+ if (!netif_running(dev->net)) {
+clean:
+ spin_lock(&dev->req_lock);
+ list_add(&req->list, &dev->rx_reqs);
+ spin_unlock(&dev->req_lock);
+ req = NULL;
+ }
+ if (req)
+ rx_submit(dev, req, GFP_ATOMIC);
+}
+
+static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
+{
+ unsigned i;
+ struct usb_request *req;
+
+ if (!n)
+ return -ENOMEM;
+
+ /* queue/recycle up to N requests */
+ i = n;
+ list_for_each_entry(req, list, list) {
+ if (i-- == 0)
+ goto extra;
+ }
+ while (i--) {
+ req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (!req)
+ return list_empty(list) ? -ENOMEM : 0;
+ list_add(&req->list, list);
+ }
+ return 0;
+
+extra:
+ /* free extras */
+ for (;;) {
+ struct list_head *next;
+
+ next = req->list.next;
+ list_del(&req->list);
+ usb_ep_free_request(ep, req);
+
+ if (next == list)
+ break;
+
+ req = container_of(next, struct usb_request, list);
+ }
+ return 0;
+}
+
+static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
+{
+ int status;
+
+ spin_lock(&dev->req_lock);
+ status = prealloc(&dev->tx_reqs, link->in_ep, n);
+ if (status < 0)
+ goto fail;
+ status = prealloc(&dev->rx_reqs, link->out_ep, n);
+ if (status < 0)
+ goto fail;
+ goto done;
+fail:
+ DBG(dev, "can't alloc requests\n");
+done:
+ spin_unlock(&dev->req_lock);
+ return status;
+}
+
+static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
+{
+ struct usb_request *req;
+ unsigned long flags;
+
+ /* fill unused rxq slots with some skb */
+ spin_lock_irqsave(&dev->req_lock, flags);
+ while (!list_empty(&dev->rx_reqs)) {
+ req = container_of(dev->rx_reqs.next,
+ struct usb_request, list);
+ list_del_init(&req->list);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+
+ if (rx_submit(dev, req, gfp_flags) < 0) {
+ defer_kevent(dev, WORK_RX_MEMORY);
+ return;
+ }
+
+ spin_lock_irqsave(&dev->req_lock, flags);
+ }
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+}
+
+static void eth_work(struct work_struct *work)
+{
+ struct eth_dev *dev = container_of(work, struct eth_dev, work);
+
+ if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) {
+ if (netif_running(dev->net))
+ rx_fill(dev, GFP_KERNEL);
+ }
+
+ if (dev->todo)
+ DBG(dev, "work done, flags = 0x%lx\n", dev->todo);
+}
+
+static void tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct sk_buff *skb = req->context;
+ struct eth_dev *dev = ep->driver_data;
+
+ switch (req->status) {
+ default:
+ dev->net->stats.tx_errors++;
+ VDBG(dev, "tx err %d\n", req->status);
+ /* FALLTHROUGH */
+ case -ECONNRESET: /* unlink */
+ case -ESHUTDOWN: /* disconnect etc */
+ break;
+ case 0:
+ dev->net->stats.tx_bytes += skb->len;
+ }
+ dev->net->stats.tx_packets++;
+
+ spin_lock(&dev->req_lock);
+ list_add(&req->list, &dev->tx_reqs);
+ spin_unlock(&dev->req_lock);
+ dev_kfree_skb_any(skb);
+
+ atomic_dec(&dev->tx_qlen);
+ if (netif_carrier_ok(dev->net))
+ netif_wake_queue(dev->net);
+}
+
+static inline int is_promisc(u16 cdc_filter)
+{
+ return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
+}
+
+static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ struct eth_dev *dev = netdev_priv(net);
+ int length = skb->len;
+ int retval;
+ struct usb_request *req = NULL;
+ unsigned long flags;
+ struct usb_ep *in;
+ u16 cdc_filter;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb) {
+ in = dev->port_usb->in_ep;
+ cdc_filter = dev->port_usb->cdc_filter;
+ } else {
+ in = NULL;
+ cdc_filter = 0;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (!in) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ /* apply outgoing CDC or RNDIS filters */
+ if (!is_promisc(cdc_filter)) {
+ u8 *dest = skb->data;
+
+ if (is_multicast_ether_addr(dest)) {
+ u16 type;
+
+ /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
+ * SET_ETHERNET_MULTICAST_FILTERS requests
+ */
+ if (is_broadcast_ether_addr(dest))
+ type = USB_CDC_PACKET_TYPE_BROADCAST;
+ else
+ type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
+ if (!(cdc_filter & type)) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+ }
+ /* ignores USB_CDC_PACKET_TYPE_DIRECTED */
+ }
+
+ spin_lock_irqsave(&dev->req_lock, flags);
+ /*
+ * this freelist can be empty if an interrupt triggered disconnect()
+ * and reconfigured the gadget (shutting down this queue) after the
+ * network stack decided to xmit but before we got the spinlock.
+ */
+ if (list_empty(&dev->tx_reqs)) {
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+ return NETDEV_TX_BUSY;
+ }
+
+ req = container_of(dev->tx_reqs.next, struct usb_request, list);
+ list_del(&req->list);
+
+ /* temporarily stop TX queue when the freelist empties */
+ if (list_empty(&dev->tx_reqs))
+ netif_stop_queue(net);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+
+ /* no buffer copies needed, unless the network stack did it
+ * or the hardware can't use skb buffers.
+ * or there's not enough space for extra headers we need
+ */
+ if (dev->wrap) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb)
+ skb = dev->wrap(dev->port_usb, skb);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (!skb)
+ goto drop;
+
+ length = skb->len;
+ }
+ req->buf = skb->data;
+ req->context = skb;
+ req->complete = tx_complete;
+
+ /* NCM requires no zlp if transfer is dwNtbInMaxSize */
+ if (dev->port_usb->is_fixed &&
+ length == dev->port_usb->fixed_in_len &&
+ (length % in->maxpacket) == 0)
+ req->zero = 0;
+ else
+ req->zero = 1;
+
+ /* use zlp framing on tx for strict CDC-Ether conformance,
+ * though any robust network rx path ignores extra padding.
+ * and some hardware doesn't like to write zlps.
+ */
+ if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
+ length++;
+
+ req->length = length;
+
+ /* throttle high/super speed IRQ rate back slightly */
+ if (gadget_is_dualspeed(dev->gadget))
+ req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
+ dev->gadget->speed == USB_SPEED_SUPER)
+ ? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
+ : 0;
+
+ retval = usb_ep_queue(in, req, GFP_ATOMIC);
+ switch (retval) {
+ default:
+ DBG(dev, "tx queue err %d\n", retval);
+ break;
+ case 0:
+ net->trans_start = jiffies;
+ atomic_inc(&dev->tx_qlen);
+ }
+
+ if (retval) {
+ dev_kfree_skb_any(skb);
+drop:
+ dev->net->stats.tx_dropped++;
+ spin_lock_irqsave(&dev->req_lock, flags);
+ if (list_empty(&dev->tx_reqs))
+ netif_start_queue(net);
+ list_add(&req->list, &dev->tx_reqs);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+ }
+ return NETDEV_TX_OK;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
+{
+ DBG(dev, "%s\n", __func__);
+
+ /* fill the rx queue */
+ rx_fill(dev, gfp_flags);
+
+ /* and open the tx floodgates */
+ atomic_set(&dev->tx_qlen, 0);
+ netif_wake_queue(dev->net);
+}
+
+static int eth_open(struct net_device *net)
+{
+ struct eth_dev *dev = netdev_priv(net);
+ struct gether *link;
+
+ DBG(dev, "%s\n", __func__);
+ if (netif_carrier_ok(dev->net))
+ eth_start(dev, GFP_KERNEL);
+
+ spin_lock_irq(&dev->lock);
+ link = dev->port_usb;
+ if (link && link->open)
+ link->open(link);
+ spin_unlock_irq(&dev->lock);
+
+ return 0;
+}
+
+static int eth_stop(struct net_device *net)
+{
+ struct eth_dev *dev = netdev_priv(net);
+ unsigned long flags;
+
+ VDBG(dev, "%s\n", __func__);
+ netif_stop_queue(net);
+
+ DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
+ dev->net->stats.rx_packets, dev->net->stats.tx_packets,
+ dev->net->stats.rx_errors, dev->net->stats.tx_errors
+ );
+
+ /* ensure there are no more active requests */
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb) {
+ struct gether *link = dev->port_usb;
+
+ if (link->close)
+ link->close(link);
+
+ /* NOTE: we have no abort-queue primitive we could use
+ * to cancel all pending I/O. Instead, we disable then
+ * reenable the endpoints ... this idiom may leave toggle
+ * wrong, but that's a self-correcting error.
+ *
+ * REVISIT: we *COULD* just let the transfers complete at
+ * their own pace; the network stack can handle old packets.
+ * For the moment we leave this here, since it works.
+ */
+ usb_ep_disable(link->in_ep);
+ usb_ep_disable(link->out_ep);
+ if (netif_carrier_ok(net)) {
+ DBG(dev, "host still using in/out endpoints\n");
+ usb_ep_enable(link->in_ep);
+ usb_ep_enable(link->out_ep);
+ }
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
+static char *dev_addr;
+module_param(dev_addr, charp, S_IRUGO);
+MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");
+
+/* this address is invisible to ifconfig */
+static char *host_addr;
+module_param(host_addr, charp, S_IRUGO);
+MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
+
+static int get_ether_addr(const char *str, u8 *dev_addr)
+{
+ if (str) {
+ unsigned i;
+
+ for (i = 0; i < 6; i++) {
+ unsigned char num;
+
+ if ((*str == '.') || (*str == ':'))
+ str++;
+ num = hex_to_bin(*str++) << 4;
+ num |= hex_to_bin(*str++);
+ dev_addr [i] = num;
+ }
+ if (is_valid_ether_addr(dev_addr))
+ return 0;
+ }
+ eth_random_addr(dev_addr);
+ return 1;
+}
+
+static struct eth_dev *the_dev;
+
+static const struct net_device_ops eth_netdev_ops = {
+ .ndo_open = eth_open,
+ .ndo_stop = eth_stop,
+ .ndo_start_xmit = eth_start_xmit,
+ .ndo_change_mtu = ueth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static struct device_type gadget_type = {
+ .name = "gadget",
+};
+
+/**
+ * gether_setup_name - initialize one ethernet-over-usb link
+ * @g: gadget to associated with these links
+ * @ethaddr: NULL, or a buffer in which the ethernet address of the
+ * host side of the link is recorded
+ * @netname: name for network device (for example, "usb")
+ * Context: may sleep
+ *
+ * This sets up the single network link that may be exported by a
+ * gadget driver using this framework. The link layer addresses are
+ * set up using module parameters.
+ *
+ * Returns negative errno, or zero on success
+ */
+int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
+ const char *netname)
+{
+ struct eth_dev *dev;
+ struct net_device *net;
+ int status;
+
+ if (the_dev)
+ return -EBUSY;
+
+ net = alloc_etherdev(sizeof *dev);
+ if (!net)
+ return -ENOMEM;
+
+ dev = netdev_priv(net);
+ spin_lock_init(&dev->lock);
+ spin_lock_init(&dev->req_lock);
+ INIT_WORK(&dev->work, eth_work);
+ INIT_LIST_HEAD(&dev->tx_reqs);
+ INIT_LIST_HEAD(&dev->rx_reqs);
+
+ skb_queue_head_init(&dev->rx_frames);
+
+ /* network device setup */
+ dev->net = net;
+ snprintf(net->name, sizeof(net->name), "%s%%d", netname);
+
+ if (get_ether_addr(dev_addr, net->dev_addr))
+ dev_warn(&g->dev,
+ "using random %s ethernet address\n", "self");
+ if (get_ether_addr(host_addr, dev->host_mac))
+ dev_warn(&g->dev,
+ "using random %s ethernet address\n", "host");
+
+ if (ethaddr)
+ memcpy(ethaddr, dev->host_mac, ETH_ALEN);
+
+ net->netdev_ops = &eth_netdev_ops;
+
+ SET_ETHTOOL_OPS(net, &ops);
+
+ dev->gadget = g;
+ SET_NETDEV_DEV(net, &g->dev);
+ SET_NETDEV_DEVTYPE(net, &gadget_type);
+
+ status = register_netdev(net);
+ if (status < 0) {
+ dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
+ free_netdev(net);
+ } else {
+ INFO(dev, "MAC %pM\n", net->dev_addr);
+ INFO(dev, "HOST MAC %pM\n", dev->host_mac);
+
+ the_dev = dev;
+
+ /* two kinds of host-initiated state changes:
+ * - iff DATA transfer is active, carrier is "on"
+ * - tx queueing enabled if open *and* carrier is "on"
+ */
+ netif_carrier_off(net);
+ }
+
+ return status;
+}
+
+/**
+ * gether_cleanup - remove Ethernet-over-USB device
+ * Context: may sleep
+ *
+ * This is called to free all resources allocated by @gether_setup().
+ */
+void gether_cleanup(void)
+{
+ if (!the_dev)
+ return;
+
+ unregister_netdev(the_dev->net);
+ flush_work_sync(&the_dev->work);
+ free_netdev(the_dev->net);
+
+ the_dev = NULL;
+}
+
+
+/**
+ * gether_connect - notify network layer that USB link is active
+ * @link: the USB link, set up with endpoints, descriptors matching
+ * current device speed, and any framing wrapper(s) set up.
+ * Context: irqs blocked
+ *
+ * This is called to activate endpoints and let the network layer know
+ * the connection is active ("carrier detect"). It may cause the I/O
+ * queues to open and start letting network packets flow, but will in
+ * any case activate the endpoints so that they respond properly to the
+ * USB host.
+ *
+ * Verify net_device pointer returned using IS_ERR(). If it doesn't
+ * indicate some error code (negative errno), ep->driver_data values
+ * have been overwritten.
+ */
+struct net_device *gether_connect(struct gether *link)
+{
+ struct eth_dev *dev = the_dev;
+ int result = 0;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ link->in_ep->driver_data = dev;
+ result = usb_ep_enable(link->in_ep);
+ if (result != 0) {
+ DBG(dev, "enable %s --> %d\n",
+ link->in_ep->name, result);
+ goto fail0;
+ }
+
+ link->out_ep->driver_data = dev;
+ result = usb_ep_enable(link->out_ep);
+ if (result != 0) {
+ DBG(dev, "enable %s --> %d\n",
+ link->out_ep->name, result);
+ goto fail1;
+ }
+
+ if (result == 0)
+ result = alloc_requests(dev, link, qlen(dev->gadget));
+
+ if (result == 0) {
+ dev->zlp = link->is_zlp_ok;
+ DBG(dev, "qlen %d\n", qlen(dev->gadget));
+
+ dev->header_len = link->header_len;
+ dev->unwrap = link->unwrap;
+ dev->wrap = link->wrap;
+
+ spin_lock(&dev->lock);
+ dev->port_usb = link;
+ link->ioport = dev;
+ if (netif_running(dev->net)) {
+ if (link->open)
+ link->open(link);
+ } else {
+ if (link->close)
+ link->close(link);
+ }
+ spin_unlock(&dev->lock);
+
+ netif_carrier_on(dev->net);
+ if (netif_running(dev->net))
+ eth_start(dev, GFP_ATOMIC);
+
+ /* on error, disable any endpoints */
+ } else {
+ (void) usb_ep_disable(link->out_ep);
+fail1:
+ (void) usb_ep_disable(link->in_ep);
+ }
+fail0:
+ /* caller is responsible for cleanup on error */
+ if (result < 0)
+ return ERR_PTR(result);
+ return dev->net;
+}
+
+/**
+ * gether_disconnect - notify network layer that USB link is inactive
+ * @link: the USB link, on which gether_connect() was called
+ * Context: irqs blocked
+ *
+ * This is called to deactivate endpoints and let the network layer know
+ * the connection went inactive ("no carrier").
+ *
+ * On return, the state is as if gether_connect() had never been called.
+ * The endpoints are inactive, and accordingly without active USB I/O.
+ * Pointers to endpoint descriptors and endpoint private data are nulled.
+ */
+void gether_disconnect(struct gether *link)
+{
+ struct eth_dev *dev = link->ioport;
+ struct usb_request *req;
+
+ WARN_ON(!dev);
+ if (!dev)
+ return;
+
+ DBG(dev, "%s\n", __func__);
+
+ netif_stop_queue(dev->net);
+ netif_carrier_off(dev->net);
+
+ /* disable endpoints, forcing (synchronous) completion
+ * of all pending i/o. then free the request objects
+ * and forget about the endpoints.
+ */
+ usb_ep_disable(link->in_ep);
+ spin_lock(&dev->req_lock);
+ while (!list_empty(&dev->tx_reqs)) {
+ req = container_of(dev->tx_reqs.next,
+ struct usb_request, list);
+ list_del(&req->list);
+
+ spin_unlock(&dev->req_lock);
+ usb_ep_free_request(link->in_ep, req);
+ spin_lock(&dev->req_lock);
+ }
+ spin_unlock(&dev->req_lock);
+ link->in_ep->driver_data = NULL;
+ link->in_ep->desc = NULL;
+
+ usb_ep_disable(link->out_ep);
+ spin_lock(&dev->req_lock);
+ while (!list_empty(&dev->rx_reqs)) {
+ req = container_of(dev->rx_reqs.next,
+ struct usb_request, list);
+ list_del(&req->list);
+
+ spin_unlock(&dev->req_lock);
+ usb_ep_free_request(link->out_ep, req);
+ spin_lock(&dev->req_lock);
+ }
+ spin_unlock(&dev->req_lock);
+ link->out_ep->driver_data = NULL;
+ link->out_ep->desc = NULL;
+
+ /* finish forgetting about this USB link episode */
+ dev->header_len = 0;
+ dev->unwrap = NULL;
+ dev->wrap = NULL;
+
+ spin_lock(&dev->lock);
+ dev->port_usb = NULL;
+ link->ioport = NULL;
+ spin_unlock(&dev->lock);
+}
diff --git a/drivers/staging/ccg/u_ether.h b/drivers/staging/ccg/u_ether.h
new file mode 100644
index 000000000000..6f4a1623d854
--- /dev/null
+++ b/drivers/staging/ccg/u_ether.h
@@ -0,0 +1,154 @@
+/*
+ * u_ether.h -- interface to USB gadget "ethernet link" utilities
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia 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.
+ */
+
+#ifndef __U_ETHER_H
+#define __U_ETHER_H
+
+#include <linux/err.h>
+#include <linux/if_ether.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+
+#include "gadget_chips.h"
+
+
+/*
+ * This represents the USB side of an "ethernet" link, managed by a USB
+ * function which provides control and (maybe) framing. Two functions
+ * in different configurations could share the same ethernet link/netdev,
+ * using different host interaction models.
+ *
+ * There is a current limitation that only one instance of this link may
+ * be present in any given configuration. When that's a problem, network
+ * layer facilities can be used to package multiple logical links on this
+ * single "physical" one.
+ */
+struct gether {
+ struct usb_function func;
+
+ /* updated by gether_{connect,disconnect} */
+ struct eth_dev *ioport;
+
+ /* endpoints handle full and/or high speeds */
+ struct usb_ep *in_ep;
+ struct usb_ep *out_ep;
+
+ bool is_zlp_ok;
+
+ u16 cdc_filter;
+
+ /* hooks for added framing, as needed for RNDIS and EEM. */
+ u32 header_len;
+ /* NCM requires fixed size bundles */
+ bool is_fixed;
+ u32 fixed_out_len;
+ u32 fixed_in_len;
+ struct sk_buff *(*wrap)(struct gether *port,
+ struct sk_buff *skb);
+ int (*unwrap)(struct gether *port,
+ struct sk_buff *skb,
+ struct sk_buff_head *list);
+
+ /* called on network open/close */
+ void (*open)(struct gether *);
+ void (*close)(struct gether *);
+};
+
+#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
+ |USB_CDC_PACKET_TYPE_ALL_MULTICAST \
+ |USB_CDC_PACKET_TYPE_PROMISCUOUS \
+ |USB_CDC_PACKET_TYPE_DIRECTED)
+
+/* variant of gether_setup that allows customizing network device name */
+int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
+ const char *netname);
+
+/* netdev setup/teardown as directed by the gadget driver */
+/* gether_setup - initialize one ethernet-over-usb link
+ * @g: gadget to associated with these links
+ * @ethaddr: NULL, or a buffer in which the ethernet address of the
+ * host side of the link is recorded
+ * Context: may sleep
+ *
+ * This sets up the single network link that may be exported by a
+ * gadget driver using this framework. The link layer addresses are
+ * set up using module parameters.
+ *
+ * Returns negative errno, or zero on success
+ */
+static inline int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
+{
+ return gether_setup_name(g, ethaddr, "usb");
+}
+
+void gether_cleanup(void);
+
+/* connect/disconnect is handled by individual functions */
+struct net_device *gether_connect(struct gether *);
+void gether_disconnect(struct gether *);
+
+/* Some controllers can't support CDC Ethernet (ECM) ... */
+static inline bool can_support_ecm(struct usb_gadget *gadget)
+{
+ if (!gadget_supports_altsettings(gadget))
+ return false;
+
+ /* Everything else is *presumably* fine ... but this is a bit
+ * chancy, so be **CERTAIN** there are no hardware issues with
+ * your controller. Add it above if it can't handle CDC.
+ */
+ return true;
+}
+
+/* each configuration may bind one instance of an ethernet link */
+int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
+int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
+int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
+int eem_bind_config(struct usb_configuration *c);
+
+#ifdef USB_ETH_RNDIS
+
+int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
+ u32 vendorID, const char *manufacturer);
+
+#else
+
+static inline int
+rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
+ u32 vendorID, const char *manufacturer)
+{
+ return 0;
+}
+
+#endif
+
+/**
+ * rndis_bind_config - add RNDIS network link to a configuration
+ * @c: the configuration to support the network link
+ * @ethaddr: a buffer in which the ethernet address of the host side
+ * side of the link was recorded
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ *
+ * Caller must have called @gether_setup(). Caller is also responsible
+ * for calling @gether_cleanup() before module unload.
+ */
+static inline int rndis_bind_config(struct usb_configuration *c,
+ u8 ethaddr[ETH_ALEN])
+{
+ return rndis_bind_config_vendor(c, ethaddr, 0, NULL);
+}
+
+
+#endif /* __U_ETHER_H */
diff --git a/drivers/staging/ccg/u_serial.c b/drivers/staging/ccg/u_serial.c
new file mode 100644
index 000000000000..5b3f5fffea92
--- /dev/null
+++ b/drivers/staging/ccg/u_serial.c
@@ -0,0 +1,1341 @@
+/*
+ * u_serial.c - utilities for USB gadget "serial port"/TTY support
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * This code also borrows from usbserial.c, which is
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
+ * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include "u_serial.h"
+
+
+/*
+ * This component encapsulates the TTY layer glue needed to provide basic
+ * "serial port" functionality through the USB gadget stack. Each such
+ * port is exposed through a /dev/ttyGS* node.
+ *
+ * After initialization (gserial_setup), these TTY port devices stay
+ * available until they are removed (gserial_cleanup). Each one may be
+ * connected to a USB function (gserial_connect), or disconnected (with
+ * gserial_disconnect) when the USB host issues a config change event.
+ * Data can only flow when the port is connected to the host.
+ *
+ * A given TTY port can be made available in multiple configurations.
+ * For example, each one might expose a ttyGS0 node which provides a
+ * login application. In one case that might use CDC ACM interface 0,
+ * while another configuration might use interface 3 for that. The
+ * work to handle that (including descriptor management) is not part
+ * of this component.
+ *
+ * Configurations may expose more than one TTY port. For example, if
+ * ttyGS0 provides login service, then ttyGS1 might provide dialer access
+ * for a telephone or fax link. And ttyGS2 might be something that just
+ * needs a simple byte stream interface for some messaging protocol that
+ * is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
+ */
+
+#define PREFIX "ttyGS"
+
+/*
+ * gserial is the lifecycle interface, used by USB functions
+ * gs_port is the I/O nexus, used by the tty driver
+ * tty_struct links to the tty/filesystem framework
+ *
+ * gserial <---> gs_port ... links will be null when the USB link is
+ * inactive; managed by gserial_{connect,disconnect}(). each gserial
+ * instance can wrap its own USB control protocol.
+ * gserial->ioport == usb_ep->driver_data ... gs_port
+ * gs_port->port_usb ... gserial
+ *
+ * gs_port <---> tty_struct ... links will be null when the TTY file
+ * isn't opened; managed by gs_open()/gs_close()
+ * gserial->port_tty ... tty_struct
+ * tty_struct->driver_data ... gserial
+ */
+
+/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the
+ * next layer of buffering. For TX that's a circular buffer; for RX
+ * consider it a NOP. A third layer is provided by the TTY code.
+ */
+#define QUEUE_SIZE 16
+#define WRITE_BUF_SIZE 8192 /* TX only */
+
+/* circular buffer */
+struct gs_buf {
+ unsigned buf_size;
+ char *buf_buf;
+ char *buf_get;
+ char *buf_put;
+};
+
+/*
+ * The port structure holds info for each port, one for each minor number
+ * (and thus for each /dev/ node).
+ */
+struct gs_port {
+ struct tty_port port;
+ spinlock_t port_lock; /* guard port_* access */
+
+ struct gserial *port_usb;
+
+ bool openclose; /* open/close in progress */
+ u8 port_num;
+
+ struct list_head read_pool;
+ int read_started;
+ int read_allocated;
+ struct list_head read_queue;
+ unsigned n_read;
+ struct tasklet_struct push;
+
+ struct list_head write_pool;
+ int write_started;
+ int write_allocated;
+ struct gs_buf port_write_buf;
+ wait_queue_head_t drain_wait; /* wait while writes drain */
+
+ /* REVISIT this state ... */
+ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
+};
+
+/* increase N_PORTS if you need more */
+#define N_PORTS 4
+static struct portmaster {
+ struct mutex lock; /* protect open/close */
+ struct gs_port *port;
+} ports[N_PORTS];
+static unsigned n_ports;
+
+#define GS_CLOSE_TIMEOUT 15 /* seconds */
+
+
+
+#ifdef VERBOSE_DEBUG
+#define pr_vdebug(fmt, arg...) \
+ pr_debug(fmt, ##arg)
+#else
+#define pr_vdebug(fmt, arg...) \
+ ({ if (0) pr_debug(fmt, ##arg); })
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* Circular Buffer */
+
+/*
+ * gs_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+static int gs_buf_alloc(struct gs_buf *gb, unsigned size)
+{
+ gb->buf_buf = kmalloc(size, GFP_KERNEL);
+ if (gb->buf_buf == NULL)
+ return -ENOMEM;
+
+ gb->buf_size = size;
+ gb->buf_put = gb->buf_buf;
+ gb->buf_get = gb->buf_buf;
+
+ return 0;
+}
+
+/*
+ * gs_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+static void gs_buf_free(struct gs_buf *gb)
+{
+ kfree(gb->buf_buf);
+ gb->buf_buf = NULL;
+}
+
+/*
+ * gs_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+static void gs_buf_clear(struct gs_buf *gb)
+{
+ gb->buf_get = gb->buf_put;
+ /* equivalent to a get of all data available */
+}
+
+/*
+ * gs_buf_data_avail
+ *
+ * Return the number of bytes of data written into the circular
+ * buffer.
+ */
+static unsigned gs_buf_data_avail(struct gs_buf *gb)
+{
+ return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
+}
+
+/*
+ * gs_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+static unsigned gs_buf_space_avail(struct gs_buf *gb)
+{
+ return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
+}
+
+/*
+ * gs_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned
+gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
+{
+ unsigned len;
+
+ len = gs_buf_space_avail(gb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = gb->buf_buf + gb->buf_size - gb->buf_put;
+ if (count > len) {
+ memcpy(gb->buf_put, buf, len);
+ memcpy(gb->buf_buf, buf+len, count - len);
+ gb->buf_put = gb->buf_buf + count - len;
+ } else {
+ memcpy(gb->buf_put, buf, count);
+ if (count < len)
+ gb->buf_put += count;
+ else /* count == len */
+ gb->buf_put = gb->buf_buf;
+ }
+
+ return count;
+}
+
+/*
+ * gs_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned
+gs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
+{
+ unsigned len;
+
+ len = gs_buf_data_avail(gb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = gb->buf_buf + gb->buf_size - gb->buf_get;
+ if (count > len) {
+ memcpy(buf, gb->buf_get, len);
+ memcpy(buf+len, gb->buf_buf, count - len);
+ gb->buf_get = gb->buf_buf + count - len;
+ } else {
+ memcpy(buf, gb->buf_get, count);
+ if (count < len)
+ gb->buf_get += count;
+ else /* count == len */
+ gb->buf_get = gb->buf_buf;
+ }
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* I/O glue between TTY (upper) and USB function (lower) driver layers */
+
+/*
+ * gs_alloc_req
+ *
+ * Allocate a usb_request and its buffer. Returns a pointer to the
+ * usb_request or NULL if there is an error.
+ */
+struct usb_request *
+gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+ if (req != NULL) {
+ req->length = len;
+ req->buf = kmalloc(len, kmalloc_flags);
+ if (req->buf == NULL) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+ }
+
+ return req;
+}
+
+/*
+ * gs_free_req
+ *
+ * Free a usb_request and its buffer.
+ */
+void gs_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+}
+
+/*
+ * gs_send_packet
+ *
+ * If there is data to send, a packet is built in the given
+ * buffer and the size is returned. If there is no data to
+ * send, 0 is returned.
+ *
+ * Called with port_lock held.
+ */
+static unsigned
+gs_send_packet(struct gs_port *port, char *packet, unsigned size)
+{
+ unsigned len;
+
+ len = gs_buf_data_avail(&port->port_write_buf);
+ if (len < size)
+ size = len;
+ if (size != 0)
+ size = gs_buf_get(&port->port_write_buf, packet, size);
+ return size;
+}
+
+/*
+ * gs_start_tx
+ *
+ * This function finds available write requests, calls
+ * gs_send_packet to fill these packets with data, and
+ * continues until either there are no more write requests
+ * available or no more data to send. This function is
+ * run whenever data arrives or write requests are available.
+ *
+ * Context: caller owns port_lock; port_usb is non-null.
+ */
+static int gs_start_tx(struct gs_port *port)
+/*
+__releases(&port->port_lock)
+__acquires(&port->port_lock)
+*/
+{
+ struct list_head *pool = &port->write_pool;
+ struct usb_ep *in = port->port_usb->in;
+ int status = 0;
+ bool do_tty_wake = false;
+
+ while (!list_empty(pool)) {
+ struct usb_request *req;
+ int len;
+
+ if (port->write_started >= QUEUE_SIZE)
+ break;
+
+ req = list_entry(pool->next, struct usb_request, list);
+ len = gs_send_packet(port, req->buf, in->maxpacket);
+ if (len == 0) {
+ wake_up_interruptible(&port->drain_wait);
+ break;
+ }
+ do_tty_wake = true;
+
+ req->length = len;
+ list_del(&req->list);
+ req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
+
+ pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
+ port->port_num, len, *((u8 *)req->buf),
+ *((u8 *)req->buf+1), *((u8 *)req->buf+2));
+
+ /* Drop lock while we call out of driver; completions
+ * could be issued while we do so. Disconnection may
+ * happen too; maybe immediately before we queue this!
+ *
+ * NOTE that we may keep sending data for a while after
+ * the TTY closed (dev->ioport->port_tty is NULL).
+ */
+ spin_unlock(&port->port_lock);
+ status = usb_ep_queue(in, req, GFP_ATOMIC);
+ spin_lock(&port->port_lock);
+
+ if (status) {
+ pr_debug("%s: %s %s err %d\n",
+ __func__, "queue", in->name, status);
+ list_add(&req->list, pool);
+ break;
+ }
+
+ port->write_started++;
+
+ /* abort immediately after disconnect */
+ if (!port->port_usb)
+ break;
+ }
+
+ if (do_tty_wake && port->port.tty)
+ tty_wakeup(port->port.tty);
+ return status;
+}
+
+/*
+ * Context: caller owns port_lock, and port_usb is set
+ */
+static unsigned gs_start_rx(struct gs_port *port)
+/*
+__releases(&port->port_lock)
+__acquires(&port->port_lock)
+*/
+{
+ struct list_head *pool = &port->read_pool;
+ struct usb_ep *out = port->port_usb->out;
+
+ while (!list_empty(pool)) {
+ struct usb_request *req;
+ int status;
+ struct tty_struct *tty;
+
+ /* no more rx if closed */
+ tty = port->port.tty;
+ if (!tty)
+ break;
+
+ if (port->read_started >= QUEUE_SIZE)
+ break;
+
+ req = list_entry(pool->next, struct usb_request, list);
+ list_del(&req->list);
+ req->length = out->maxpacket;
+
+ /* drop lock while we call out; the controller driver
+ * may need to call us back (e.g. for disconnect)
+ */
+ spin_unlock(&port->port_lock);
+ status = usb_ep_queue(out, req, GFP_ATOMIC);
+ spin_lock(&port->port_lock);
+
+ if (status) {
+ pr_debug("%s: %s %s err %d\n",
+ __func__, "queue", out->name, status);
+ list_add(&req->list, pool);
+ break;
+ }
+ port->read_started++;
+
+ /* abort immediately after disconnect */
+ if (!port->port_usb)
+ break;
+ }
+ return port->read_started;
+}
+
+/*
+ * RX tasklet takes data out of the RX queue and hands it up to the TTY
+ * layer until it refuses to take any more data (or is throttled back).
+ * Then it issues reads for any further data.
+ *
+ * If the RX queue becomes full enough that no usb_request is queued,
+ * the OUT endpoint may begin NAKing as soon as its FIFO fills up.
+ * So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
+ * can be buffered before the TTY layer's buffers (currently 64 KB).
+ */
+static void gs_rx_push(unsigned long _port)
+{
+ struct gs_port *port = (void *)_port;
+ struct tty_struct *tty;
+ struct list_head *queue = &port->read_queue;
+ bool disconnect = false;
+ bool do_push = false;
+
+ /* hand any queued data to the tty */
+ spin_lock_irq(&port->port_lock);
+ tty = port->port.tty;
+ while (!list_empty(queue)) {
+ struct usb_request *req;
+
+ req = list_first_entry(queue, struct usb_request, list);
+
+ /* discard data if tty was closed */
+ if (!tty)
+ goto recycle;
+
+ /* leave data queued if tty was rx throttled */
+ if (test_bit(TTY_THROTTLED, &tty->flags))
+ break;
+
+ switch (req->status) {
+ case -ESHUTDOWN:
+ disconnect = true;
+ pr_vdebug(PREFIX "%d: shutdown\n", port->port_num);
+ break;
+
+ default:
+ /* presumably a transient fault */
+ pr_warning(PREFIX "%d: unexpected RX status %d\n",
+ port->port_num, req->status);
+ /* FALLTHROUGH */
+ case 0:
+ /* normal completion */
+ break;
+ }
+
+ /* push data to (open) tty */
+ if (req->actual) {
+ char *packet = req->buf;
+ unsigned size = req->actual;
+ unsigned n;
+ int count;
+
+ /* we may have pushed part of this packet already... */
+ n = port->n_read;
+ if (n) {
+ packet += n;
+ size -= n;
+ }
+
+ count = tty_insert_flip_string(tty, packet, size);
+ if (count)
+ do_push = true;
+ if (count != size) {
+ /* stop pushing; TTY layer can't handle more */
+ port->n_read += count;
+ pr_vdebug(PREFIX "%d: rx block %d/%d\n",
+ port->port_num,
+ count, req->actual);
+ break;
+ }
+ port->n_read = 0;
+ }
+recycle:
+ list_move(&req->list, &port->read_pool);
+ port->read_started--;
+ }
+
+ /* Push from tty to ldisc; without low_latency set this is handled by
+ * a workqueue, so we won't get callbacks and can hold port_lock
+ */
+ if (tty && do_push)
+ tty_flip_buffer_push(tty);
+
+
+ /* We want our data queue to become empty ASAP, keeping data
+ * in the tty and ldisc (not here). If we couldn't push any
+ * this time around, there may be trouble unless there's an
+ * implicit tty_unthrottle() call on its way...
+ *
+ * REVISIT we should probably add a timer to keep the tasklet
+ * from starving ... but it's not clear that case ever happens.
+ */
+ if (!list_empty(queue) && tty) {
+ if (!test_bit(TTY_THROTTLED, &tty->flags)) {
+ if (do_push)
+ tasklet_schedule(&port->push);
+ else
+ pr_warning(PREFIX "%d: RX not scheduled?\n",
+ port->port_num);
+ }
+ }
+
+ /* If we're still connected, refill the USB RX queue. */
+ if (!disconnect && port->port_usb)
+ gs_start_rx(port);
+
+ spin_unlock_irq(&port->port_lock);
+}
+
+static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gs_port *port = ep->driver_data;
+
+ /* Queue all received data until the tty layer is ready for it. */
+ spin_lock(&port->port_lock);
+ list_add_tail(&req->list, &port->read_queue);
+ tasklet_schedule(&port->push);
+ spin_unlock(&port->port_lock);
+}
+
+static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gs_port *port = ep->driver_data;
+
+ spin_lock(&port->port_lock);
+ list_add(&req->list, &port->write_pool);
+ port->write_started--;
+
+ switch (req->status) {
+ default:
+ /* presumably a transient fault */
+ pr_warning("%s: unexpected %s status %d\n",
+ __func__, ep->name, req->status);
+ /* FALL THROUGH */
+ case 0:
+ /* normal completion */
+ gs_start_tx(port);
+ break;
+
+ case -ESHUTDOWN:
+ /* disconnect */
+ pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
+ break;
+ }
+
+ spin_unlock(&port->port_lock);
+}
+
+static void gs_free_requests(struct usb_ep *ep, struct list_head *head,
+ int *allocated)
+{
+ struct usb_request *req;
+
+ while (!list_empty(head)) {
+ req = list_entry(head->next, struct usb_request, list);
+ list_del(&req->list);
+ gs_free_req(ep, req);
+ if (allocated)
+ (*allocated)--;
+ }
+}
+
+static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
+ void (*fn)(struct usb_ep *, struct usb_request *),
+ int *allocated)
+{
+ int i;
+ struct usb_request *req;
+ int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;
+
+ /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
+ * do quite that many this time, don't fail ... we just won't
+ * be as speedy as we might otherwise be.
+ */
+ for (i = 0; i < n; i++) {
+ req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
+ if (!req)
+ return list_empty(head) ? -ENOMEM : 0;
+ req->complete = fn;
+ list_add_tail(&req->list, head);
+ if (allocated)
+ (*allocated)++;
+ }
+ return 0;
+}
+
+/**
+ * gs_start_io - start USB I/O streams
+ * @dev: encapsulates endpoints to use
+ * Context: holding port_lock; port_tty and port_usb are non-null
+ *
+ * We only start I/O when something is connected to both sides of
+ * this port. If nothing is listening on the host side, we may
+ * be pointlessly filling up our TX buffers and FIFO.
+ */
+static int gs_start_io(struct gs_port *port)
+{
+ struct list_head *head = &port->read_pool;
+ struct usb_ep *ep = port->port_usb->out;
+ int status;
+ unsigned started;
+
+ /* Allocate RX and TX I/O buffers. We can't easily do this much
+ * earlier (with GFP_KERNEL) because the requests are coupled to
+ * endpoints, as are the packet sizes we'll be using. Different
+ * configurations may use different endpoints with a given port;
+ * and high speed vs full speed changes packet sizes too.
+ */
+ status = gs_alloc_requests(ep, head, gs_read_complete,
+ &port->read_allocated);
+ if (status)
+ return status;
+
+ status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
+ gs_write_complete, &port->write_allocated);
+ if (status) {
+ gs_free_requests(ep, head, &port->read_allocated);
+ return status;
+ }
+
+ /* queue read requests */
+ port->n_read = 0;
+ started = gs_start_rx(port);
+
+ /* unblock any pending writes into our circular buffer */
+ if (started) {
+ tty_wakeup(port->port.tty);
+ } else {
+ gs_free_requests(ep, head, &port->read_allocated);
+ gs_free_requests(port->port_usb->in, &port->write_pool,
+ &port->write_allocated);
+ status = -EIO;
+ }
+
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* TTY Driver */
+
+/*
+ * gs_open sets up the link between a gs_port and its associated TTY.
+ * That link is broken *only* by TTY close(), and all driver methods
+ * know that.
+ */
+static int gs_open(struct tty_struct *tty, struct file *file)
+{
+ int port_num = tty->index;
+ struct gs_port *port;
+ int status;
+
+ do {
+ mutex_lock(&ports[port_num].lock);
+ port = ports[port_num].port;
+ if (!port)
+ status = -ENODEV;
+ else {
+ spin_lock_irq(&port->port_lock);
+
+ /* already open? Great. */
+ if (port->port.count) {
+ status = 0;
+ port->port.count++;
+
+ /* currently opening/closing? wait ... */
+ } else if (port->openclose) {
+ status = -EBUSY;
+
+ /* ... else we do the work */
+ } else {
+ status = -EAGAIN;
+ port->openclose = true;
+ }
+ spin_unlock_irq(&port->port_lock);
+ }
+ mutex_unlock(&ports[port_num].lock);
+
+ switch (status) {
+ default:
+ /* fully handled */
+ return status;
+ case -EAGAIN:
+ /* must do the work */
+ break;
+ case -EBUSY:
+ /* wait for EAGAIN task to finish */
+ msleep(1);
+ /* REVISIT could have a waitchannel here, if
+ * concurrent open performance is important
+ */
+ break;
+ }
+ } while (status != -EAGAIN);
+
+ /* Do the "real open" */
+ spin_lock_irq(&port->port_lock);
+
+ /* allocate circular buffer on first open */
+ if (port->port_write_buf.buf_buf == NULL) {
+
+ spin_unlock_irq(&port->port_lock);
+ status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
+ spin_lock_irq(&port->port_lock);
+
+ if (status) {
+ pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n",
+ port->port_num, tty, file);
+ port->openclose = false;
+ goto exit_unlock_port;
+ }
+ }
+
+ /* REVISIT if REMOVED (ports[].port NULL), abort the open
+ * to let rmmod work faster (but this way isn't wrong).
+ */
+
+ /* REVISIT maybe wait for "carrier detect" */
+
+ tty->driver_data = port;
+ port->port.tty = tty;
+
+ port->port.count = 1;
+ port->openclose = false;
+
+ /* if connected, start the I/O stream */
+ if (port->port_usb) {
+ struct gserial *gser = port->port_usb;
+
+ pr_debug("gs_open: start ttyGS%d\n", port->port_num);
+ gs_start_io(port);
+
+ if (gser->connect)
+ gser->connect(gser);
+ }
+
+ pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
+
+ status = 0;
+
+exit_unlock_port:
+ spin_unlock_irq(&port->port_lock);
+ return status;
+}
+
+static int gs_writes_finished(struct gs_port *p)
+{
+ int cond;
+
+ /* return true on disconnect or empty buffer */
+ spin_lock_irq(&p->port_lock);
+ cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);
+ spin_unlock_irq(&p->port_lock);
+
+ return cond;
+}
+
+static void gs_close(struct tty_struct *tty, struct file *file)
+{
+ struct gs_port *port = tty->driver_data;
+ struct gserial *gser;
+
+ spin_lock_irq(&port->port_lock);
+
+ if (port->port.count != 1) {
+ if (port->port.count == 0)
+ WARN_ON(1);
+ else
+ --port->port.count;
+ goto exit;
+ }
+
+ pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
+
+ /* mark port as closing but in use; we can drop port lock
+ * and sleep if necessary
+ */
+ port->openclose = true;
+ port->port.count = 0;
+
+ gser = port->port_usb;
+ if (gser && gser->disconnect)
+ gser->disconnect(gser);
+
+ /* wait for circular write buffer to drain, disconnect, or at
+ * most GS_CLOSE_TIMEOUT seconds; then discard the rest
+ */
+ if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) {
+ spin_unlock_irq(&port->port_lock);
+ wait_event_interruptible_timeout(port->drain_wait,
+ gs_writes_finished(port),
+ GS_CLOSE_TIMEOUT * HZ);
+ spin_lock_irq(&port->port_lock);
+ gser = port->port_usb;
+ }
+
+ /* Iff we're disconnected, there can be no I/O in flight so it's
+ * ok to free the circular buffer; else just scrub it. And don't
+ * let the push tasklet fire again until we're re-opened.
+ */
+ if (gser == NULL)
+ gs_buf_free(&port->port_write_buf);
+ else
+ gs_buf_clear(&port->port_write_buf);
+
+ tty->driver_data = NULL;
+ port->port.tty = NULL;
+
+ port->openclose = false;
+
+ pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
+ port->port_num, tty, file);
+
+ wake_up_interruptible(&port->port.close_wait);
+exit:
+ spin_unlock_irq(&port->port_lock);
+}
+
+static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int status;
+
+ pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
+ port->port_num, tty, count);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (count)
+ count = gs_buf_put(&port->port_write_buf, buf, count);
+ /* treat count == 0 as flush_chars() */
+ if (port->port_usb)
+ status = gs_start_tx(port);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return count;
+}
+
+static int gs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int status;
+
+ pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n",
+ port->port_num, tty, ch, __builtin_return_address(0));
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ status = gs_buf_put(&port->port_write_buf, &ch, 1);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return status;
+}
+
+static void gs_flush_chars(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+
+ pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb)
+ gs_start_tx(port);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int gs_write_room(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int room = 0;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb)
+ room = gs_buf_space_avail(&port->port_write_buf);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
+ port->port_num, tty, room);
+
+ return room;
+}
+
+static int gs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int chars = 0;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ chars = gs_buf_data_avail(&port->port_write_buf);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
+ port->port_num, tty, chars);
+
+ return chars;
+}
+
+/* undo side effects of setting TTY_THROTTLED */
+static void gs_unthrottle(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb) {
+ /* Kickstart read queue processing. We don't do xon/xoff,
+ * rts/cts, or other handshaking with the host, but if the
+ * read queue backs up enough we'll be NAKing OUT packets.
+ */
+ tasklet_schedule(&port->push);
+ pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int gs_break_ctl(struct tty_struct *tty, int duration)
+{
+ struct gs_port *port = tty->driver_data;
+ int status = 0;
+ struct gserial *gser;
+
+ pr_vdebug("gs_break_ctl: ttyGS%d, send break (%d) \n",
+ port->port_num, duration);
+
+ spin_lock_irq(&port->port_lock);
+ gser = port->port_usb;
+ if (gser && gser->send_break)
+ status = gser->send_break(gser, duration);
+ spin_unlock_irq(&port->port_lock);
+
+ return status;
+}
+
+static const struct tty_operations gs_tty_ops = {
+ .open = gs_open,
+ .close = gs_close,
+ .write = gs_write,
+ .put_char = gs_put_char,
+ .flush_chars = gs_flush_chars,
+ .write_room = gs_write_room,
+ .chars_in_buffer = gs_chars_in_buffer,
+ .unthrottle = gs_unthrottle,
+ .break_ctl = gs_break_ctl,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct tty_driver *gs_tty_driver;
+
+static int
+gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
+{
+ struct gs_port *port;
+
+ port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
+ if (port == NULL)
+ return -ENOMEM;
+
+ tty_port_init(&port->port);
+ spin_lock_init(&port->port_lock);
+ init_waitqueue_head(&port->drain_wait);
+
+ tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
+
+ INIT_LIST_HEAD(&port->read_pool);
+ INIT_LIST_HEAD(&port->read_queue);
+ INIT_LIST_HEAD(&port->write_pool);
+
+ port->port_num = port_num;
+ port->port_line_coding = *coding;
+
+ ports[port_num].port = port;
+
+ return 0;
+}
+
+/**
+ * gserial_setup - initialize TTY driver for one or more ports
+ * @g: gadget to associate with these ports
+ * @count: how many ports to support
+ * Context: may sleep
+ *
+ * The TTY stack needs to know in advance how many devices it should
+ * plan to manage. Use this call to set up the ports you will be
+ * exporting through USB. Later, connect them to functions based
+ * on what configuration is activated by the USB host; and disconnect
+ * them as appropriate.
+ *
+ * An example would be a two-configuration device in which both
+ * configurations expose port 0, but through different functions.
+ * One configuration could even expose port 1 while the other
+ * one doesn't.
+ *
+ * Returns negative errno or zero.
+ */
+int gserial_setup(struct usb_gadget *g, unsigned count)
+{
+ unsigned i;
+ struct usb_cdc_line_coding coding;
+ int status;
+
+ if (count == 0 || count > N_PORTS)
+ return -EINVAL;
+
+ gs_tty_driver = alloc_tty_driver(count);
+ if (!gs_tty_driver)
+ return -ENOMEM;
+
+ gs_tty_driver->driver_name = "g_serial";
+ gs_tty_driver->name = PREFIX;
+ /* uses dynamically assigned dev_t values */
+
+ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ gs_tty_driver->init_termios = tty_std_termios;
+
+ /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
+ * MS-Windows. Otherwise, most of these flags shouldn't affect
+ * anything unless we were to actually hook up to a serial line.
+ */
+ gs_tty_driver->init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ gs_tty_driver->init_termios.c_ispeed = 9600;
+ gs_tty_driver->init_termios.c_ospeed = 9600;
+
+ coding.dwDTERate = cpu_to_le32(9600);
+ coding.bCharFormat = 8;
+ coding.bParityType = USB_CDC_NO_PARITY;
+ coding.bDataBits = USB_CDC_1_STOP_BITS;
+
+ tty_set_operations(gs_tty_driver, &gs_tty_ops);
+
+ /* make devices be openable */
+ for (i = 0; i < count; i++) {
+ mutex_init(&ports[i].lock);
+ status = gs_port_alloc(i, &coding);
+ if (status) {
+ count = i;
+ goto fail;
+ }
+ }
+ n_ports = count;
+
+ /* export the driver ... */
+ status = tty_register_driver(gs_tty_driver);
+ if (status) {
+ pr_err("%s: cannot register, err %d\n",
+ __func__, status);
+ goto fail;
+ }
+
+ /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
+ for (i = 0; i < count; i++) {
+ struct device *tty_dev;
+
+ tty_dev = tty_register_device(gs_tty_driver, i, &g->dev);
+ if (IS_ERR(tty_dev))
+ pr_warning("%s: no classdev for port %d, err %ld\n",
+ __func__, i, PTR_ERR(tty_dev));
+ }
+
+ pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
+ count, (count == 1) ? "" : "s");
+
+ return status;
+fail:
+ while (count--)
+ kfree(ports[count].port);
+ put_tty_driver(gs_tty_driver);
+ gs_tty_driver = NULL;
+ return status;
+}
+
+static int gs_closed(struct gs_port *port)
+{
+ int cond;
+
+ spin_lock_irq(&port->port_lock);
+ cond = (port->port.count == 0) && !port->openclose;
+ spin_unlock_irq(&port->port_lock);
+ return cond;
+}
+
+/**
+ * gserial_cleanup - remove TTY-over-USB driver and devices
+ * Context: may sleep
+ *
+ * This is called to free all resources allocated by @gserial_setup().
+ * Accordingly, it may need to wait until some open /dev/ files have
+ * closed.
+ *
+ * The caller must have issued @gserial_disconnect() for any ports
+ * that had previously been connected, so that there is never any
+ * I/O pending when it's called.
+ */
+void gserial_cleanup(void)
+{
+ unsigned i;
+ struct gs_port *port;
+
+ if (!gs_tty_driver)
+ return;
+
+ /* start sysfs and /dev/ttyGS* node removal */
+ for (i = 0; i < n_ports; i++)
+ tty_unregister_device(gs_tty_driver, i);
+
+ for (i = 0; i < n_ports; i++) {
+ /* prevent new opens */
+ mutex_lock(&ports[i].lock);
+ port = ports[i].port;
+ ports[i].port = NULL;
+ mutex_unlock(&ports[i].lock);
+
+ tasklet_kill(&port->push);
+
+ /* wait for old opens to finish */
+ wait_event(port->port.close_wait, gs_closed(port));
+
+ WARN_ON(port->port_usb != NULL);
+
+ kfree(port);
+ }
+ n_ports = 0;
+
+ tty_unregister_driver(gs_tty_driver);
+ put_tty_driver(gs_tty_driver);
+ gs_tty_driver = NULL;
+
+ pr_debug("%s: cleaned up ttyGS* support\n", __func__);
+}
+
+/**
+ * gserial_connect - notify TTY I/O glue that USB link is active
+ * @gser: the function, set up with endpoints and descriptors
+ * @port_num: which port is active
+ * Context: any (usually from irq)
+ *
+ * This is called activate endpoints and let the TTY layer know that
+ * the connection is active ... not unlike "carrier detect". It won't
+ * necessarily start I/O queues; unless the TTY is held open by any
+ * task, there would be no point. However, the endpoints will be
+ * activated so the USB host can perform I/O, subject to basic USB
+ * hardware flow control.
+ *
+ * Caller needs to have set up the endpoints and USB function in @dev
+ * before calling this, as well as the appropriate (speed-specific)
+ * endpoint descriptors, and also have set up the TTY driver by calling
+ * @gserial_setup().
+ *
+ * Returns negative errno or zero.
+ * On success, ep->driver_data will be overwritten.
+ */
+int gserial_connect(struct gserial *gser, u8 port_num)
+{
+ struct gs_port *port;
+ unsigned long flags;
+ int status;
+
+ if (!gs_tty_driver || port_num >= n_ports)
+ return -ENXIO;
+
+ /* we "know" gserial_cleanup() hasn't been called */
+ port = ports[port_num].port;
+
+ /* activate the endpoints */
+ status = usb_ep_enable(gser->in);
+ if (status < 0)
+ return status;
+ gser->in->driver_data = port;
+
+ status = usb_ep_enable(gser->out);
+ if (status < 0)
+ goto fail_out;
+ gser->out->driver_data = port;
+
+ /* then tell the tty glue that I/O can work */
+ spin_lock_irqsave(&port->port_lock, flags);
+ gser->ioport = port;
+ port->port_usb = gser;
+
+ /* REVISIT unclear how best to handle this state...
+ * we don't really couple it with the Linux TTY.
+ */
+ gser->port_line_coding = port->port_line_coding;
+
+ /* REVISIT if waiting on "carrier detect", signal. */
+
+ /* if it's already open, start I/O ... and notify the serial
+ * protocol about open/close status (connect/disconnect).
+ */
+ if (port->port.count) {
+ pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
+ gs_start_io(port);
+ if (gser->connect)
+ gser->connect(gser);
+ } else {
+ if (gser->disconnect)
+ gser->disconnect(gser);
+ }
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return status;
+
+fail_out:
+ usb_ep_disable(gser->in);
+ gser->in->driver_data = NULL;
+ return status;
+}
+
+/**
+ * gserial_disconnect - notify TTY I/O glue that USB link is inactive
+ * @gser: the function, on which gserial_connect() was called
+ * Context: any (usually from irq)
+ *
+ * This is called to deactivate endpoints and let the TTY layer know
+ * that the connection went inactive ... not unlike "hangup".
+ *
+ * On return, the state is as if gserial_connect() had never been called;
+ * there is no active USB I/O on these endpoints.
+ */
+void gserial_disconnect(struct gserial *gser)
+{
+ struct gs_port *port = gser->ioport;
+ unsigned long flags;
+
+ if (!port)
+ return;
+
+ /* tell the TTY glue not to do I/O here any more */
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ /* REVISIT as above: how best to track this? */
+ port->port_line_coding = gser->port_line_coding;
+
+ port->port_usb = NULL;
+ gser->ioport = NULL;
+ if (port->port.count > 0 || port->openclose) {
+ wake_up_interruptible(&port->drain_wait);
+ if (port->port.tty)
+ tty_hangup(port->port.tty);
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ /* disable endpoints, aborting down any active I/O */
+ usb_ep_disable(gser->out);
+ gser->out->driver_data = NULL;
+
+ usb_ep_disable(gser->in);
+ gser->in->driver_data = NULL;
+
+ /* finally, free any unused/unusable I/O buffers */
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port.count == 0 && !port->openclose)
+ gs_buf_free(&port->port_write_buf);
+ gs_free_requests(gser->out, &port->read_pool, NULL);
+ gs_free_requests(gser->out, &port->read_queue, NULL);
+ gs_free_requests(gser->in, &port->write_pool, NULL);
+
+ port->read_allocated = port->read_started =
+ port->write_allocated = port->write_started = 0;
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
diff --git a/drivers/staging/ccg/u_serial.h b/drivers/staging/ccg/u_serial.h
new file mode 100644
index 000000000000..9b0fe6450fbf
--- /dev/null
+++ b/drivers/staging/ccg/u_serial.h
@@ -0,0 +1,65 @@
+/*
+ * u_serial.h - interface to USB gadget "serial port"/TTY utilities
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+#ifndef __U_SERIAL_H
+#define __U_SERIAL_H
+
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+
+/*
+ * One non-multiplexed "serial" I/O port ... there can be several of these
+ * on any given USB peripheral device, if it provides enough endpoints.
+ *
+ * The "u_serial" utility component exists to do one thing: manage TTY
+ * style I/O using the USB peripheral endpoints listed here, including
+ * hookups to sysfs and /dev for each logical "tty" device.
+ *
+ * REVISIT at least ACM could support tiocmget() if needed.
+ *
+ * REVISIT someday, allow multiplexing several TTYs over these endpoints.
+ */
+struct gserial {
+ struct usb_function func;
+
+ /* port is managed by gserial_{connect,disconnect} */
+ struct gs_port *ioport;
+
+ struct usb_ep *in;
+ struct usb_ep *out;
+
+ /* REVISIT avoid this CDC-ACM support harder ... */
+ struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
+
+ /* notification callbacks */
+ void (*connect)(struct gserial *p);
+ void (*disconnect)(struct gserial *p);
+ int (*send_break)(struct gserial *p, int duration);
+};
+
+/* utilities to allocate/free request and buffer */
+struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags);
+void gs_free_req(struct usb_ep *, struct usb_request *req);
+
+/* port setup/teardown is handled by gadget driver */
+int gserial_setup(struct usb_gadget *g, unsigned n_ports);
+void gserial_cleanup(void);
+
+/* connect/disconnect is handled by individual functions */
+int gserial_connect(struct gserial *, u8 port_num);
+void gserial_disconnect(struct gserial *);
+
+/* functions are bound to configurations by a config or gadget driver */
+int acm_bind_config(struct usb_configuration *c, u8 port_num);
+int gser_bind_config(struct usb_configuration *c, u8 port_num);
+int obex_bind_config(struct usb_configuration *c, u8 port_num);
+
+#endif /* __U_SERIAL_H */
diff --git a/drivers/staging/ccg/usbstring.c b/drivers/staging/ccg/usbstring.c
new file mode 100644
index 000000000000..4d25b9009edf
--- /dev/null
+++ b/drivers/staging/ccg/usbstring.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2003 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/nls.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+
+/**
+ * usb_gadget_get_string - fill out a string descriptor
+ * @table: of c strings encoded using UTF-8
+ * @id: string id, from low byte of wValue in get string descriptor
+ * @buf: at least 256 bytes, must be 16-bit aligned
+ *
+ * Finds the UTF-8 string matching the ID, and converts it into a
+ * string descriptor in utf16-le.
+ * Returns length of descriptor (always even) or negative errno
+ *
+ * If your driver needs stings in multiple languages, you'll probably
+ * "switch (wIndex) { ... }" in your ep0 string descriptor logic,
+ * using this routine after choosing which set of UTF-8 strings to use.
+ * Note that US-ASCII is a strict subset of UTF-8; any string bytes with
+ * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
+ * characters (which are also widely used in C strings).
+ */
+int
+usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
+{
+ struct usb_string *s;
+ int len;
+
+ /* descriptor 0 has the language id */
+ if (id == 0) {
+ buf [0] = 4;
+ buf [1] = USB_DT_STRING;
+ buf [2] = (u8) table->language;
+ buf [3] = (u8) (table->language >> 8);
+ return 4;
+ }
+ for (s = table->strings; s && s->s; s++)
+ if (s->id == id)
+ break;
+
+ /* unrecognized: stall. */
+ if (!s || !s->s)
+ return -EINVAL;
+
+ /* string descriptors have length, tag, then UTF16-LE text */
+ len = min ((size_t) 126, strlen (s->s));
+ len = utf8s_to_utf16s(s->s, len, UTF16_LITTLE_ENDIAN,
+ (wchar_t *) &buf[2], 126);
+ if (len < 0)
+ return -EINVAL;
+ buf [0] = (len + 1) * 2;
+ buf [1] = USB_DT_STRING;
+ return buf [0];
+}
+
diff --git a/drivers/staging/ced1401/Kconfig b/drivers/staging/ced1401/Kconfig
new file mode 100644
index 000000000000..ae36d1b2ba99
--- /dev/null
+++ b/drivers/staging/ced1401/Kconfig
@@ -0,0 +1,6 @@
+config CED1401
+ tristate "Cambridge Electronic Design 1401 USB support"
+ depends on USB
+ help
+ This driver supports the Cambridge Electronic Design 1401 USB device
+ (whatever that is.)
diff --git a/drivers/staging/ced1401/Makefile b/drivers/staging/ced1401/Makefile
new file mode 100644
index 000000000000..f0c114b2b4b9
--- /dev/null
+++ b/drivers/staging/ced1401/Makefile
@@ -0,0 +1,3 @@
+
+obj-$(CONFIG_CED1401) := cedusb.o
+cedusb-objs := usb1401.o ced_ioc.o
diff --git a/drivers/staging/ced1401/TODO b/drivers/staging/ced1401/TODO
new file mode 100644
index 000000000000..9fd5630bdf4d
--- /dev/null
+++ b/drivers/staging/ced1401/TODO
@@ -0,0 +1,10 @@
+TODO:
+ - coding syle fixes
+ - build warning fixups
+ - ioctl auditing
+ - usb api auditing
+ - proper USB minor number (it's stomping on an existing one right now.)
+
+Please send patches to Greg Kroah-Hartman <gregkh@linuxfoundation.org> and Cc:
+Alois Schlögl <alois.schloegl@ist.ac.at>
+
diff --git a/drivers/staging/ced1401/ced_ioc.c b/drivers/staging/ced1401/ced_ioc.c
new file mode 100644
index 000000000000..c9492edaaddb
--- /dev/null
+++ b/drivers/staging/ced1401/ced_ioc.c
@@ -0,0 +1,1515 @@
+/* ced_ioc.c
+ ioctl part of the 1401 usb device driver for linux.
+ Copyright (C) 2010 Cambridge Electronic Design Ltd
+ Author Greg P Smith (greg@ced.co.uk)
+
+ This program is free software; 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/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/page-flags.h>
+#include <linux/pagemap.h>
+#include <linux/jiffies.h>
+
+#include "usb1401.h"
+
+/****************************************************************************
+** FlushOutBuff
+**
+** Empties the Output buffer and sets int lines. Used from user level only
+****************************************************************************/
+void FlushOutBuff(DEVICE_EXTENSION * pdx)
+{
+ dev_dbg(&pdx->interface->dev, "%s currentState=%d", __func__,
+ pdx->sCurrentState);
+ if (pdx->sCurrentState == U14ERR_TIME) /* Do nothing if hardware in trouble */
+ return;
+// CharSend_Cancel(pdx); /* Kill off any pending I/O */
+ spin_lock_irq(&pdx->charOutLock);
+ pdx->dwNumOutput = 0;
+ pdx->dwOutBuffGet = 0;
+ pdx->dwOutBuffPut = 0;
+ spin_unlock_irq(&pdx->charOutLock);
+}
+
+/****************************************************************************
+**
+** FlushInBuff
+**
+** Empties the input buffer and sets int lines
+****************************************************************************/
+void FlushInBuff(DEVICE_EXTENSION * pdx)
+{
+ dev_dbg(&pdx->interface->dev, "%s currentState=%d", __func__,
+ pdx->sCurrentState);
+ if (pdx->sCurrentState == U14ERR_TIME) /* Do nothing if hardware in trouble */
+ return;
+// CharRead_Cancel(pDevObject); /* Kill off any pending I/O */
+ spin_lock_irq(&pdx->charInLock);
+ pdx->dwNumInput = 0;
+ pdx->dwInBuffGet = 0;
+ pdx->dwInBuffPut = 0;
+ spin_unlock_irq(&pdx->charInLock);
+}
+
+/****************************************************************************
+** PutChars
+**
+** Utility routine to copy chars into the output buffer and fire them off.
+** called from user mode, holds charOutLock.
+****************************************************************************/
+static int PutChars(DEVICE_EXTENSION * pdx, const char *pCh,
+ unsigned int uCount)
+{
+ int iReturn;
+ spin_lock_irq(&pdx->charOutLock); // get the output spin lock
+ if ((OUTBUF_SZ - pdx->dwNumOutput) >= uCount) {
+ unsigned int u;
+ for (u = 0; u < uCount; u++) {
+ pdx->outputBuffer[pdx->dwOutBuffPut++] = pCh[u];
+ if (pdx->dwOutBuffPut >= OUTBUF_SZ)
+ pdx->dwOutBuffPut = 0;
+ }
+ pdx->dwNumOutput += uCount;
+ spin_unlock_irq(&pdx->charOutLock);
+ iReturn = SendChars(pdx); // ...give a chance to transmit data
+ } else {
+ iReturn = U14ERR_NOOUT; // no room at the out (ha-ha)
+ spin_unlock_irq(&pdx->charOutLock);
+ }
+ return iReturn;
+}
+
+/*****************************************************************************
+** Add the data in pData (local pointer) of length n to the output buffer, and
+** trigger an output transfer if this is appropriate. User mode.
+** Holds the io_mutex
+*****************************************************************************/
+int SendString(DEVICE_EXTENSION * pdx, const char __user * pData,
+ unsigned int n)
+{
+ int iReturn = U14ERR_NOERROR; // assume all will be well
+ char buffer[OUTBUF_SZ + 1]; // space in our address space for characters
+ if (n > OUTBUF_SZ) // check space in local buffer...
+ return U14ERR_NOOUT; // ...too many characters
+ if (copy_from_user(buffer, pData, n))
+ return -EFAULT;
+ buffer[n] = 0; // terminate for debug purposes
+
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ if (n > 0) // do nothing if nowt to do!
+ {
+ dev_dbg(&pdx->interface->dev, "%s n=%d>%s<", __func__, n,
+ buffer);
+ iReturn = PutChars(pdx, buffer, n);
+ }
+
+ Allowi(pdx, false); // make sure we have input int
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** SendChar
+**
+** Sends a single character to the 1401. User mode, holds io_mutex.
+****************************************************************************/
+int SendChar(DEVICE_EXTENSION * pdx, char c)
+{
+ int iReturn;
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ iReturn = PutChars(pdx, &c, 1);
+ dev_dbg(&pdx->interface->dev, "SendChar >%c< (0x%02x)", c, c);
+ Allowi(pdx, false); // Make sure char reads are running
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+/***************************************************************************
+**
+** Get1401State
+**
+** Retrieves state information from the 1401, adjusts the 1401 state held
+** in the device extension to indicate the current 1401 type.
+**
+** *state is updated with information about the 1401 state as returned by the
+** 1401. The low byte is a code for what 1401 is doing:
+**
+** 0 normal 1401 operation
+** 1 sending chars to host
+** 2 sending block data to host
+** 3 reading block data from host
+** 4 sending an escape sequence to the host
+** 0x80 1401 is executing self-test, in which case the upper word
+** is the last error code seen (or zero for no new error).
+**
+** *error is updated with error information if a self-test error code
+** is returned in the upper word of state.
+**
+** both state and error are set to -1 if there are comms problems, and
+** to zero if there is a simple failure.
+**
+** return error code (U14ERR_NOERROR for OK)
+*/
+int Get1401State(DEVICE_EXTENSION * pdx, __u32 * state, __u32 * error)
+{
+ int nGot;
+ dev_dbg(&pdx->interface->dev, "Get1401State() entry");
+
+ *state = 0xFFFFFFFF; // Start off with invalid state
+ nGot = usb_control_msg(pdx->udev, usb_rcvctrlpipe(pdx->udev, 0),
+ GET_STATUS, (D_TO_H | VENDOR | DEVREQ), 0, 0,
+ pdx->statBuf, sizeof(pdx->statBuf), HZ);
+ if (nGot != sizeof(pdx->statBuf)) {
+ dev_err(&pdx->interface->dev,
+ "Get1401State() FAILED, return code %d", nGot);
+ pdx->sCurrentState = U14ERR_TIME; // Indicate that things are very wrong indeed
+ *state = 0; // Force status values to a known state
+ *error = 0;
+ } else {
+ int nDevice;
+ dev_dbg(&pdx->interface->dev,
+ "Get1401State() Success, state: 0x%x, 0x%x",
+ pdx->statBuf[0], pdx->statBuf[1]);
+
+ *state = pdx->statBuf[0]; // Return the state values to the calling code
+ *error = pdx->statBuf[1];
+
+ nDevice = pdx->udev->descriptor.bcdDevice >> 8; // 1401 type code value
+ switch (nDevice) // so we can clean up current state
+ {
+ case 0:
+ pdx->sCurrentState = U14ERR_U1401;
+ break;
+
+ default: // allow lots of device codes for future 1401s
+ if ((nDevice >= 1) && (nDevice <= 23))
+ pdx->sCurrentState = (short)(nDevice + 6);
+ else
+ pdx->sCurrentState = U14ERR_ILL;
+ break;
+ }
+ }
+
+ return pdx->sCurrentState >= 0 ? U14ERR_NOERROR : pdx->sCurrentState;
+}
+
+/****************************************************************************
+** ReadWrite_Cancel
+**
+** Kills off staged read\write request from the USB if one is pending.
+****************************************************************************/
+int ReadWrite_Cancel(DEVICE_EXTENSION * pdx)
+{
+ dev_dbg(&pdx->interface->dev, "ReadWrite_Cancel entry %d",
+ pdx->bStagedUrbPending);
+#ifdef NOT_WRITTEN_YET
+ int ntStatus = STATUS_SUCCESS;
+ bool bResult = false;
+ unsigned int i;
+ // We can fill this in when we know how we will implement the staged transfer stuff
+ spin_lock_irq(&pdx->stagedLock);
+
+ if (pdx->bStagedUrbPending) // anything to be cancelled? May need more...
+ {
+ dev_info(&pdx->interface - dev,
+ "ReadWrite_Cancel about to cancel Urb");
+
+ // KeClearEvent(&pdx->StagingDoneEvent); // Clear the staging done flag
+ USB_ASSERT(pdx->pStagedIrp != NULL);
+
+ // Release the spinlock first otherwise the completion routine may hang
+ // on the spinlock while this function hands waiting for the event.
+ spin_unlock_irq(&pdx->stagedLock);
+ bResult = IoCancelIrp(pdx->pStagedIrp); // Actually do the cancel
+ if (bResult) {
+ LARGE_INTEGER timeout;
+ timeout.QuadPart = -10000000; // Use a timeout of 1 second
+ dev_info(&pdx->interface - dev,
+ "ReadWrite_Cancel about to wait till done");
+ ntStatus =
+ KeWaitForSingleObject(&pdx->StagingDoneEvent,
+ Executive, KernelMode, FALSE,
+ &timeout);
+ } else {
+ dev_info(&pdx->interface - dev,
+ "ReadWrite_Cancel, cancellation failed");
+ ntStatus = U14ERR_FAIL;
+ }
+ USB_KdPrint(DBGLVL_DEFAULT,
+ ("ReadWrite_Cancel ntStatus = 0x%x decimal %d\n",
+ ntStatus, ntStatus));
+ } else
+ spin_unlock_irq(&pdx->stagedLock);
+
+ dev_info(&pdx->interface - dev, "ReadWrite_Cancel done");
+ return ntStatus;
+#else
+ return U14ERR_NOERROR;
+#endif
+
+}
+
+/***************************************************************************
+** InSelfTest - utility to check in self test. Return 1 for ST, 0 for not or
+** a -ve error code if we failed for some reason.
+***************************************************************************/
+static int InSelfTest(DEVICE_EXTENSION * pdx, unsigned int *pState)
+{
+ unsigned int state, error;
+ int iReturn = Get1401State(pdx, &state, &error); // see if in self-test
+ if (iReturn == U14ERR_NOERROR) // if all still OK
+ iReturn = (state == (unsigned int)-1) || // TX problem or...
+ ((state & 0xff) == 0x80); // ...self test
+ *pState = state; // return actual state
+ return iReturn;
+}
+
+/***************************************************************************
+** Is1401 - ALWAYS CALLED HOLDING THE io_mutex
+**
+** Tests for the current state of the 1401. Sets sCurrentState:
+**
+** U14ERR_NOIF 1401 i/f card not installed (not done here)
+** U14ERR_OFF 1401 apparently not switched on
+** U14ERR_NC 1401 appears to be not connected
+** U14ERR_ILL 1401 if it is there its not very well at all
+** U14ERR_TIME 1401 appears OK, but doesn't communicate - very bad
+** U14ERR_STD 1401 OK and ready for use
+** U14ERR_PLUS 1401+ OK and ready for use
+** U14ERR_U1401 Micro1401 OK and ready for use
+** U14ERR_POWER Power1401 OK and ready for use
+** U14ERR_U14012 Micro1401 mkII OK and ready for use
+**
+** Returns TRUE if a 1401 detected and OK, else FALSE
+****************************************************************************/
+bool Is1401(DEVICE_EXTENSION * pdx)
+{
+ int iReturn;
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ ced_draw_down(pdx); // wait for, then kill outstanding Urbs
+ FlushInBuff(pdx); // Clear out input buffer & pipe
+ FlushOutBuff(pdx); // Clear output buffer & pipe
+
+ // The next call returns 0 if OK, but has returned 1 in the past, meaning that
+ // usb_unlock_device() is needed... now it always is
+ iReturn = usb_lock_device_for_reset(pdx->udev, pdx->interface);
+
+ // release the io_mutex because if we don't, we will deadlock due to system
+ // calls back into the driver.
+ mutex_unlock(&pdx->io_mutex); // locked, so we will not get system calls
+ if (iReturn >= 0) // if we failed
+ {
+ iReturn = usb_reset_device(pdx->udev); // try to do the reset
+ usb_unlock_device(pdx->udev); // undo the lock
+ }
+
+ mutex_lock(&pdx->io_mutex); // hold stuff off while we wait
+ pdx->dwDMAFlag = MODE_CHAR; // Clear DMA mode flag regardless!
+ if (iReturn == 0) // if all is OK still
+ {
+ unsigned int state;
+ iReturn = InSelfTest(pdx, &state); // see if likely in self test
+ if (iReturn > 0) // do we need to wait for self-test?
+ {
+ unsigned long ulTimeOut = jiffies + 30 * HZ; // when to give up
+ while ((iReturn > 0) && time_before(jiffies, ulTimeOut)) {
+ schedule(); // let other stuff run
+ iReturn = InSelfTest(pdx, &state); // see if done yet
+ }
+ }
+
+ if (iReturn == 0) // if all is OK...
+ iReturn = state == 0; // then sucess is that the state is 0
+ } else
+ iReturn = 0; // we failed
+ pdx->bForceReset = false; // Clear forced reset flag now
+
+ return iReturn > 0;
+}
+
+/****************************************************************************
+** QuickCheck - ALWAYS CALLED HOLDING THE io_mutex
+** This is used to test for a 1401. It will try to do a quick check if all is
+** OK, that is the 1401 was OK the last time it was asked, and there is no DMA
+** in progress, and if the bTestBuff flag is set, the character buffers must be
+** empty too. If the quick check shows that the state is still the same, then
+** all is OK.
+**
+** If any of the above conditions are not met, or if the state or type of the
+** 1401 has changed since the previous test, the full Is1401 test is done, but
+** only if bCanReset is also TRUE.
+**
+** The return value is TRUE if a useable 1401 is found, FALSE if not
+*/
+bool QuickCheck(DEVICE_EXTENSION * pdx, bool bTestBuff, bool bCanReset)
+{
+ bool bRet = false; // assume it will fail and we will reset
+ bool bShortTest;
+
+ bShortTest = ((pdx->dwDMAFlag == MODE_CHAR) && // no DMA running
+ (!pdx->bForceReset) && // Not had a real reset forced
+ (pdx->sCurrentState >= U14ERR_STD)); // No 1401 errors stored
+
+ dev_dbg(&pdx->interface->dev,
+ "%s DMAFlag:%d, state:%d, force:%d, testBuff:%d, short:%d",
+ __func__, pdx->dwDMAFlag, pdx->sCurrentState, pdx->bForceReset,
+ bTestBuff, bShortTest);
+
+ if ((bTestBuff) && // Buffer check requested, and...
+ (pdx->dwNumInput || pdx->dwNumOutput)) // ...characters were in the buffer?
+ {
+ bShortTest = false; // Then do the full test
+ dev_dbg(&pdx->interface->dev,
+ "%s will reset as buffers not empty", __func__);
+ }
+
+ if (bShortTest || !bCanReset) // Still OK to try the short test?
+ { // Always test if no reset - we want state update
+ unsigned int state, error;
+ dev_dbg(&pdx->interface->dev, "%s->Get1401State", __func__);
+ if (Get1401State(pdx, &state, &error) == U14ERR_NOERROR) // Check on the 1401 state
+ {
+ if ((state & 0xFF) == 0) // If call worked, check the status value
+ bRet = true; // If that was zero, all is OK, no reset needed
+ }
+ }
+
+ if (!bRet && bCanReset) // If all not OK, then
+ {
+ dev_info(&pdx->interface->dev, "%s->Is1401 %d %d %d %d",
+ __func__, bShortTest, pdx->sCurrentState, bTestBuff,
+ pdx->bForceReset);
+ bRet = Is1401(pdx); // do full test
+ }
+
+ return bRet;
+}
+
+/****************************************************************************
+** Reset1401
+**
+** Resets the 1401 and empties the i/o buffers
+*****************************************************************************/
+int Reset1401(DEVICE_EXTENSION * pdx)
+{
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ dev_dbg(&pdx->interface->dev, "ABout to call QuickCheck");
+ QuickCheck(pdx, true, true); // Check 1401, reset if not OK
+ mutex_unlock(&pdx->io_mutex);
+ return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+** GetChar
+**
+** Gets a single character from the 1401
+****************************************************************************/
+int GetChar(DEVICE_EXTENSION * pdx)
+{
+ int iReturn = U14ERR_NOIN; // assume we will get nothing
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+
+ dev_dbg(&pdx->interface->dev, "GetChar");
+
+ Allowi(pdx, false); // Make sure char reads are running
+ SendChars(pdx); // and send any buffered chars
+
+ spin_lock_irq(&pdx->charInLock);
+ if (pdx->dwNumInput > 0) // worth looking
+ {
+ iReturn = pdx->inputBuffer[pdx->dwInBuffGet++];
+ if (pdx->dwInBuffGet >= INBUF_SZ)
+ pdx->dwInBuffGet = 0;
+ pdx->dwNumInput--;
+ } else
+ iReturn = U14ERR_NOIN; // no input data to read
+ spin_unlock_irq(&pdx->charInLock);
+
+ Allowi(pdx, false); // Make sure char reads are running
+
+ mutex_unlock(&pdx->io_mutex); // Protect disconnect from new i/o
+ return iReturn;
+}
+
+/****************************************************************************
+** GetString
+**
+** Gets a string from the 1401. Returns chars up to the next CR or when
+** there are no more to read or nowhere to put them. CR is translated to
+** 0 and counted as a character. If the string does not end in a 0, we will
+** add one, if there is room, but it is not counted as a character.
+**
+** returns the count of characters (including the terminator, or 0 if none
+** or a negative error code.
+****************************************************************************/
+int GetString(DEVICE_EXTENSION * pdx, char __user * pUser, int n)
+{
+ int nAvailable; // character in the buffer
+ int iReturn = U14ERR_NOIN;
+ if (n <= 0)
+ return -ENOMEM;
+
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ Allowi(pdx, false); // Make sure char reads are running
+ SendChars(pdx); // and send any buffered chars
+
+ spin_lock_irq(&pdx->charInLock);
+ nAvailable = pdx->dwNumInput; // characters available now
+ if (nAvailable > n) // read max of space in pUser...
+ nAvailable = n; // ...or input characters
+
+ if (nAvailable > 0) // worth looking?
+ {
+ char buffer[INBUF_SZ + 1]; // space for a linear copy of data
+ int nGot = 0;
+ int nCopyToUser; // number to copy to user
+ char cData;
+ do {
+ cData = pdx->inputBuffer[pdx->dwInBuffGet++];
+ if (cData == CR_CHAR) // replace CR with zero
+ cData = (char)0;
+
+ if (pdx->dwInBuffGet >= INBUF_SZ)
+ pdx->dwInBuffGet = 0; // wrap buffer pointer
+
+ buffer[nGot++] = cData; // save the output
+ }
+ while ((nGot < nAvailable) && cData);
+
+ nCopyToUser = nGot; // what to copy...
+ if (cData) // do we need null
+ {
+ buffer[nGot] = (char)0; // make it tidy
+ if (nGot < n) // if space in user buffer...
+ ++nCopyToUser; // ...copy the 0 as well.
+ }
+
+ pdx->dwNumInput -= nGot;
+ spin_unlock_irq(&pdx->charInLock);
+
+ dev_dbg(&pdx->interface->dev,
+ "GetString read %d characters >%s<", nGot, buffer);
+ if (copy_to_user(pUser, buffer, nCopyToUser))
+ iReturn = -EFAULT;
+ else
+ iReturn = nGot; // report characters read
+ } else
+ spin_unlock_irq(&pdx->charInLock);
+
+ Allowi(pdx, false); // Make sure char reads are running
+ mutex_unlock(&pdx->io_mutex); // Protect disconnect from new i/o
+
+ return iReturn;
+}
+
+/*******************************************************************************
+** Get count of characters in the inout buffer.
+*******************************************************************************/
+int Stat1401(DEVICE_EXTENSION * pdx)
+{
+ int iReturn;
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ Allowi(pdx, false); // make sure we allow pending chars
+ SendChars(pdx); // in both directions
+ iReturn = pdx->dwNumInput; // no lock as single read
+ mutex_unlock(&pdx->io_mutex); // Protect disconnect from new i/o
+ return iReturn;
+}
+
+/****************************************************************************
+** LineCount
+**
+** Returns the number of newline chars in the buffer. There is no need for
+** any fancy interlocks as we only read the interrupt routine data, and the
+** system is arranged so nothing can be destroyed.
+****************************************************************************/
+int LineCount(DEVICE_EXTENSION * pdx)
+{
+ int iReturn = 0; // will be count of line ends
+
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ Allowi(pdx, false); // Make sure char reads are running
+ SendChars(pdx); // and send any buffered chars
+ spin_lock_irq(&pdx->charInLock); // Get protection
+
+ if (pdx->dwNumInput > 0) // worth looking?
+ {
+ unsigned int dwIndex = pdx->dwInBuffGet; // start at first available
+ unsigned int dwEnd = pdx->dwInBuffPut; // Position for search end
+ do {
+ if (pdx->inputBuffer[dwIndex++] == CR_CHAR)
+ ++iReturn; // inc count if CR
+
+ if (dwIndex >= INBUF_SZ) // see if we fall off buff
+ dwIndex = 0;
+ }
+ while (dwIndex != dwEnd); // go to last avaliable
+ }
+
+ spin_unlock_irq(&pdx->charInLock);
+ dev_dbg(&pdx->interface->dev, "LineCount returned %d", iReturn);
+ mutex_unlock(&pdx->io_mutex); // Protect disconnect from new i/o
+ return iReturn;
+}
+
+/****************************************************************************
+** GetOutBufSpace
+**
+** Gets the space in the output buffer. Called from user code.
+*****************************************************************************/
+int GetOutBufSpace(DEVICE_EXTENSION * pdx)
+{
+ int iReturn;
+ mutex_lock(&pdx->io_mutex); // Protect disconnect from new i/o
+ SendChars(pdx); // send any buffered chars
+ iReturn = (int)(OUTBUF_SZ - pdx->dwNumOutput); // no lock needed for single read
+ dev_dbg(&pdx->interface->dev, "OutBufSpace %d", iReturn);
+ mutex_unlock(&pdx->io_mutex); // Protect disconnect from new i/o
+ return iReturn;
+}
+
+/****************************************************************************
+**
+** ClearArea
+**
+** Clears up a transfer area. This is always called in the context of a user
+** request, never from a call-back.
+****************************************************************************/
+int ClearArea(DEVICE_EXTENSION * pdx, int nArea)
+{
+ int iReturn = U14ERR_NOERROR;
+
+ if ((nArea < 0) || (nArea >= MAX_TRANSAREAS)) {
+ iReturn = U14ERR_BADAREA;
+ dev_err(&pdx->interface->dev, "%s Attempt to clear area %d",
+ __func__, nArea);
+ } else {
+ TRANSAREA *pTA = &pdx->rTransDef[nArea]; // to save typing
+ if (!pTA->bUsed) // if not used...
+ iReturn = U14ERR_NOTSET; // ...nothing to be done
+ else {
+ // We must save the memory we return as we shouldn't mess with memory while
+ // holding a spin lock.
+ struct page **pPages = 0; // save page address list
+ int nPages = 0; // and number of pages
+ int np;
+
+ dev_dbg(&pdx->interface->dev, "%s area %d", __func__,
+ nArea);
+ spin_lock_irq(&pdx->stagedLock);
+ if ((pdx->StagedId == nArea)
+ && (pdx->dwDMAFlag > MODE_CHAR)) {
+ iReturn = U14ERR_UNLOCKFAIL; // cannot delete as in use
+ dev_err(&pdx->interface->dev,
+ "%s call on area %d while active",
+ __func__, nArea);
+ } else {
+ pPages = pTA->pPages; // save page address list
+ nPages = pTA->nPages; // and page count
+ if (pTA->dwEventSz) // if events flagging in use
+ wake_up_interruptible(&pTA->wqEvent); // release anything that was waiting
+
+ if (pdx->bXFerWaiting
+ && (pdx->rDMAInfo.wIdent == nArea))
+ pdx->bXFerWaiting = false; // Cannot have pending xfer if area cleared
+
+ // Clean out the TRANSAREA except for the wait queue, which is at the end
+ // This sets bUsed to false and dwEventSz to 0 to say area not used and no events.
+ memset(pTA, 0,
+ sizeof(TRANSAREA) -
+ sizeof(wait_queue_head_t));
+ }
+ spin_unlock_irq(&pdx->stagedLock);
+
+ if (pPages) // if we decided to release the memory
+ {
+ // Now we must undo the pinning down of the pages. We will assume the worst and mark
+ // all the pages as dirty. Don't be tempted to move this up above as you must not be
+ // holding a spin lock to do this stuff as it is not atomic.
+ dev_dbg(&pdx->interface->dev, "%s nPages=%d",
+ __func__, nPages);
+
+ for (np = 0; np < nPages; ++np) {
+ if (pPages[np]) {
+ SetPageDirty(pPages[np]);
+ page_cache_release(pPages[np]);
+ }
+ }
+
+ kfree(pPages);
+ dev_dbg(&pdx->interface->dev,
+ "%s kfree(pPages) done", __func__);
+ }
+ }
+ }
+
+ return iReturn;
+}
+
+/****************************************************************************
+** SetArea
+**
+** Sets up a transfer area - the functional part. Called by both
+** SetTransfer and SetCircular.
+****************************************************************************/
+static int SetArea(DEVICE_EXTENSION * pdx, int nArea, char __user * puBuf,
+ unsigned int dwLength, bool bCircular, bool bCircToHost)
+{
+ // Start by working out the page aligned start of the area and the size
+ // of the area in pages, allowing for the start not being aligned and the
+ // end needing to be rounded up to a page boundary.
+ unsigned long ulStart = ((unsigned long)puBuf) & PAGE_MASK;
+ unsigned int ulOffset = ((unsigned long)puBuf) & (PAGE_SIZE - 1);
+ int len = (dwLength + ulOffset + PAGE_SIZE - 1) >> PAGE_SHIFT;
+
+ TRANSAREA *pTA = &pdx->rTransDef[nArea]; // to save typing
+ struct page **pPages = 0; // space for page tables
+ int nPages = 0; // and number of pages
+
+ int iReturn = ClearArea(pdx, nArea); // see if OK to use this area
+ if ((iReturn != U14ERR_NOTSET) && // if not area unused and...
+ (iReturn != U14ERR_NOERROR)) // ...not all OK, then...
+ return iReturn; // ...we cannot use this area
+
+ if (!access_ok(VERIFY_WRITE, puBuf, dwLength)) // if we cannot access the memory...
+ return -EFAULT; // ...then we are done
+
+ // Now allocate space to hold the page pointer and virtual address pointer tables
+ pPages =
+ (struct page **)kmalloc(len * sizeof(struct page *), GFP_KERNEL);
+ if (!pPages) {
+ iReturn = U14ERR_NOMEMORY;
+ goto error;
+ }
+ dev_dbg(&pdx->interface->dev, "%s %p, length=%06x, circular %d",
+ __func__, puBuf, dwLength, bCircular);
+
+ // To pin down user pages we must first acquire the mapping semaphore.
+ down_read(&current->mm->mmap_sem); // get memory map semaphore
+ nPages =
+ get_user_pages(current, current->mm, ulStart, len, 1, 0, pPages, 0);
+ up_read(&current->mm->mmap_sem); // release the semaphore
+ dev_dbg(&pdx->interface->dev, "%s nPages = %d", __func__, nPages);
+
+ if (nPages > 0) // if we succeeded
+ {
+ // If you are tempted to use page_address (form LDD3), forget it. You MUST use
+ // kmap() or kmap_atomic() to get a virtual address. page_address will give you
+ // (null) or at least it does in this context with an x86 machine.
+ spin_lock_irq(&pdx->stagedLock);
+ pTA->lpvBuff = puBuf; // keep start of region (user address)
+ pTA->dwBaseOffset = ulOffset; // save offset in first page to start of xfer
+ pTA->dwLength = dwLength; // Size if the region in bytes
+ pTA->pPages = pPages; // list of pages that are used by buffer
+ pTA->nPages = nPages; // number of pages
+
+ pTA->bCircular = bCircular;
+ pTA->bCircToHost = bCircToHost;
+
+ pTA->aBlocks[0].dwOffset = 0;
+ pTA->aBlocks[0].dwSize = 0;
+ pTA->aBlocks[1].dwOffset = 0;
+ pTA->aBlocks[1].dwSize = 0;
+ pTA->bUsed = true; // This is now a used block
+
+ spin_unlock_irq(&pdx->stagedLock);
+ iReturn = U14ERR_NOERROR; // say all was well
+ } else {
+ iReturn = U14ERR_LOCKFAIL;
+ goto error;
+ }
+
+ return iReturn;
+
+error:
+ kfree(pPages);
+ return iReturn;
+}
+
+/****************************************************************************
+** SetTransfer
+**
+** Sets up a transfer area record. If the area is already set, we attempt to
+** unset it. Unsetting will fail if the area is booked, and a transfer to that
+** area is in progress. Otherwise, we will release the area and re-assign it.
+****************************************************************************/
+int SetTransfer(DEVICE_EXTENSION * pdx, TRANSFERDESC __user * pTD)
+{
+ int iReturn;
+ TRANSFERDESC td;
+
+ if (copy_from_user(&td, pTD, sizeof(td)))
+ return -EFAULT;
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s area:%d, size:%08x", __func__,
+ td.wAreaNum, td.dwLength);
+ // The strange cast is done so that we don't get warnings in 32-bit linux about the size of the
+ // pointer. The pointer is always passed as a 64-bit object so that we don't have problems using
+ // a 32-bit program on a 64-bit system. unsigned long is 64-bits on a 64-bit system.
+ iReturn =
+ SetArea(pdx, td.wAreaNum,
+ (char __user *)((unsigned long)td.lpvBuff), td.dwLength,
+ false, false);
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+/****************************************************************************
+** UnSetTransfer
+** Erases a transfer area record
+****************************************************************************/
+int UnsetTransfer(DEVICE_EXTENSION * pdx, int nArea)
+{
+ int iReturn;
+ mutex_lock(&pdx->io_mutex);
+ iReturn = ClearArea(pdx, nArea);
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+/****************************************************************************
+** SetEvent
+** Creates an event that we can test for based on a transfer to/from an area.
+** The area must be setup for a transfer. We attempt to simulate the Windows
+** driver behavior for events (as we don't actually use them), which is to
+** pretend that whatever the user asked for was achieved, so we return 1 if
+** try to create one, and 0 if they ask to remove (assuming all else was OK).
+****************************************************************************/
+int SetEvent(DEVICE_EXTENSION * pdx, TRANSFEREVENT __user * pTE)
+{
+ int iReturn = U14ERR_NOERROR;
+ TRANSFEREVENT te;
+
+ // get a local copy of the data
+ if (copy_from_user(&te, pTE, sizeof(te)))
+ return -EFAULT;
+
+ if (te.wAreaNum >= MAX_TRANSAREAS) // the area must exist
+ return U14ERR_BADAREA;
+ else {
+ TRANSAREA *pTA = &pdx->rTransDef[te.wAreaNum];
+ mutex_lock(&pdx->io_mutex); // make sure we have no competitor
+ spin_lock_irq(&pdx->stagedLock);
+ if (pTA->bUsed) // area must be in use
+ {
+ pTA->dwEventSt = te.dwStart; // set area regions
+ pTA->dwEventSz = te.dwLength; // set size (0 cancels it)
+ pTA->bEventToHost = te.wFlags & 1; // set the direction
+ pTA->iWakeUp = 0; // zero the wake up count
+ } else
+ iReturn = U14ERR_NOTSET;
+ spin_unlock_irq(&pdx->stagedLock);
+ mutex_unlock(&pdx->io_mutex);
+ }
+ return iReturn ==
+ U14ERR_NOERROR ? (te.iSetEvent ? 1 : U14ERR_NOERROR) : iReturn;
+}
+
+/****************************************************************************
+** WaitEvent
+** Sleep the process with a timeout waiting for an event. Returns the number
+** of times that a block met the event condition since we last cleared it or
+** 0 if timed out, or -ve error (bad area or not set, or signal).
+****************************************************************************/
+int WaitEvent(DEVICE_EXTENSION * pdx, int nArea, int msTimeOut)
+{
+ int iReturn;
+ if ((unsigned)nArea >= MAX_TRANSAREAS)
+ return U14ERR_BADAREA;
+ else {
+ int iWait;
+ TRANSAREA *pTA = &pdx->rTransDef[nArea];
+ msTimeOut = (msTimeOut * HZ + 999) / 1000; // convert timeout to jiffies
+
+ // We cannot wait holding the mutex, but we check the flags while holding
+ // it. This may well be pointless as another thread could get in between
+ // releasing it and the wait call. However, this would have to clear the
+ // iWakeUp flag. However, the !pTA-bUsed may help us in this case.
+ mutex_lock(&pdx->io_mutex); // make sure we have no competitor
+ if (!pTA->bUsed || !pTA->dwEventSz) // check something to wait for...
+ return U14ERR_NOTSET; // ...else we do nothing
+ mutex_unlock(&pdx->io_mutex);
+
+ if (msTimeOut)
+ iWait =
+ wait_event_interruptible_timeout(pTA->wqEvent,
+ pTA->iWakeUp
+ || !pTA->bUsed,
+ msTimeOut);
+ else
+ iWait =
+ wait_event_interruptible(pTA->wqEvent, pTA->iWakeUp
+ || !pTA->bUsed);
+ if (iWait)
+ iReturn = -ERESTARTSYS; // oops - we have had a SIGNAL
+ else
+ iReturn = pTA->iWakeUp; // else the wakeup count
+
+ spin_lock_irq(&pdx->stagedLock);
+ pTA->iWakeUp = 0; // clear the flag
+ spin_unlock_irq(&pdx->stagedLock);
+ }
+ return iReturn;
+}
+
+/****************************************************************************
+** TestEvent
+** Test the event to see if a WaitEvent would return immediately. Returns the
+** number of times a block completed since the last call, or 0 if none or a
+** negative error.
+****************************************************************************/
+int TestEvent(DEVICE_EXTENSION * pdx, int nArea)
+{
+ int iReturn;
+ if ((unsigned)nArea >= MAX_TRANSAREAS)
+ iReturn = U14ERR_BADAREA;
+ else {
+ TRANSAREA *pTA = &pdx->rTransDef[nArea];
+ mutex_lock(&pdx->io_mutex); // make sure we have no competitor
+ spin_lock_irq(&pdx->stagedLock);
+ iReturn = pTA->iWakeUp; // get wakeup count since last call
+ pTA->iWakeUp = 0; // clear the count
+ spin_unlock_irq(&pdx->stagedLock);
+ mutex_unlock(&pdx->io_mutex);
+ }
+ return iReturn;
+}
+
+/****************************************************************************
+** GetTransferInfo
+** Puts the current state of the 1401 in a TGET_TX_BLOCK.
+*****************************************************************************/
+int GetTransfer(DEVICE_EXTENSION * pdx, TGET_TX_BLOCK __user * pTX)
+{
+ int iReturn = U14ERR_NOERROR;
+ unsigned int dwIdent;
+
+ mutex_lock(&pdx->io_mutex);
+ dwIdent = pdx->StagedId; // area ident for last xfer
+ if (dwIdent >= MAX_TRANSAREAS)
+ iReturn = U14ERR_BADAREA;
+ else {
+ // Return the best information we have - we don't have physical addresses
+ TGET_TX_BLOCK tx;
+ memset(&tx, 0, sizeof(tx)); // clean out local work structure
+ tx.size = pdx->rTransDef[dwIdent].dwLength;
+ tx.linear = (long long)((long)pdx->rTransDef[dwIdent].lpvBuff);
+ tx.avail = GET_TX_MAXENTRIES; // how many blocks we could return
+ tx.used = 1; // number we actually return
+ tx.entries[0].physical =
+ (long long)(tx.linear + pdx->StagedOffset);
+ tx.entries[0].size = tx.size;
+
+ if (copy_to_user(pTX, &tx, sizeof(tx)))
+ iReturn = -EFAULT;
+ }
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+/****************************************************************************
+** KillIO1401
+**
+** Empties the host i/o buffers
+****************************************************************************/
+int KillIO1401(DEVICE_EXTENSION * pdx)
+{
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ mutex_lock(&pdx->io_mutex);
+ FlushOutBuff(pdx);
+ FlushInBuff(pdx);
+ mutex_unlock(&pdx->io_mutex);
+ return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+** BlkTransState
+** Returns a 0 or a 1 for whether DMA is happening. No point holding a mutex
+** for this as it only does one read.
+*****************************************************************************/
+int BlkTransState(DEVICE_EXTENSION * pdx)
+{
+ int iReturn = pdx->dwDMAFlag != MODE_CHAR;
+ dev_dbg(&pdx->interface->dev, "%s = %d", __func__, iReturn);
+ return iReturn;
+}
+
+/****************************************************************************
+** StateOf1401
+**
+** Puts the current state of the 1401 in the Irp return buffer.
+*****************************************************************************/
+int StateOf1401(DEVICE_EXTENSION * pdx)
+{
+ int iReturn;
+ mutex_lock(&pdx->io_mutex);
+
+ QuickCheck(pdx, false, false); // get state up to date, no reset
+ iReturn = pdx->sCurrentState;
+
+ mutex_unlock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s = %d", __func__, iReturn);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** StartSelfTest
+**
+** Initiates a self-test cycle. The assumption is that we have no interrupts
+** active, so we should make sure that this is the case.
+*****************************************************************************/
+int StartSelfTest(DEVICE_EXTENSION * pdx)
+{
+ int nGot;
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ ced_draw_down(pdx); // wait for, then kill outstanding Urbs
+ FlushInBuff(pdx); // Clear out input buffer & pipe
+ FlushOutBuff(pdx); // Clear output buffer & pipe
+// ReadWrite_Cancel(pDeviceObject); /* so things stay tidy */
+ pdx->dwDMAFlag = MODE_CHAR; /* Clear DMA mode flags here */
+
+ nGot = usb_control_msg(pdx->udev, usb_rcvctrlpipe(pdx->udev, 0), DB_SELFTEST, (H_TO_D | VENDOR | DEVREQ), 0, 0, 0, 0, HZ); // allow 1 second timeout
+ pdx->ulSelfTestTime = jiffies + HZ * 30; // 30 seconds into the future
+
+ mutex_unlock(&pdx->io_mutex);
+ if (nGot < 0)
+ dev_err(&pdx->interface->dev, "%s err=%d", __func__, nGot);
+ return nGot < 0 ? U14ERR_FAIL : U14ERR_NOERROR;
+}
+
+/****************************************************************************
+** CheckSelfTest
+**
+** Check progress of a self-test cycle
+****************************************************************************/
+int CheckSelfTest(DEVICE_EXTENSION * pdx, TGET_SELFTEST __user * pGST)
+{
+ unsigned int state, error;
+ int iReturn;
+ TGET_SELFTEST gst; // local work space
+ memset(&gst, 0, sizeof(gst)); // clear out the space (sets code 0)
+
+ mutex_lock(&pdx->io_mutex);
+
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ iReturn = Get1401State(pdx, &state, &error);
+ if (iReturn == U14ERR_NOERROR) // Only accept zero if it happens twice
+ iReturn = Get1401State(pdx, &state, &error);
+
+ if (iReturn != U14ERR_NOERROR) // Self-test can cause comms errors
+ { // so we assume still testing
+ dev_err(&pdx->interface->dev,
+ "%s Get1401State=%d, assuming still testing", __func__,
+ iReturn);
+ state = 0x80; // Force still-testing, no error
+ error = 0;
+ iReturn = U14ERR_NOERROR;
+ }
+
+ if ((state == -1) && (error == -1)) // If Get1401State had problems
+ {
+ dev_err(&pdx->interface->dev,
+ "%s Get1401State failed, assuming still testing",
+ __func__);
+ state = 0x80; // Force still-testing, no error
+ error = 0;
+ }
+
+ if ((state & 0xFF) == 0x80) // If we are still in self-test
+ {
+ if (state & 0x00FF0000) // Have we got an error?
+ {
+ gst.code = (state & 0x00FF0000) >> 16; // read the error code
+ gst.x = error & 0x0000FFFF; // Error data X
+ gst.y = (error & 0xFFFF0000) >> 16; // and data Y
+ dev_dbg(&pdx->interface->dev, "Self-test error code %d",
+ gst.code);
+ } else // No error, check for timeout
+ {
+ unsigned long ulNow = jiffies; // get current time
+ if (time_after(ulNow, pdx->ulSelfTestTime)) {
+ gst.code = -2; // Flag the timeout
+ dev_dbg(&pdx->interface->dev,
+ "Self-test timed-out");
+ } else
+ dev_dbg(&pdx->interface->dev,
+ "Self-test on-going");
+ }
+ } else {
+ gst.code = -1; // Flag the test is done
+ dev_dbg(&pdx->interface->dev, "Self-test done");
+ }
+
+ if (gst.code < 0) // If we have a problem or finished
+ { // If using the 2890 we should reset properly
+ if ((pdx->nPipes == 4) && (pdx->s1401Type <= TYPEPOWER))
+ Is1401(pdx); // Get 1401 reset and OK
+ else
+ QuickCheck(pdx, true, true); // Otherwise check without reset unless problems
+ }
+ mutex_unlock(&pdx->io_mutex);
+
+ if (copy_to_user(pGST, &gst, sizeof(gst)))
+ return -EFAULT;
+
+ return iReturn;
+}
+
+/****************************************************************************
+** TypeOf1401
+**
+** Returns code for standard, plus, micro1401, power1401 or none
+****************************************************************************/
+int TypeOf1401(DEVICE_EXTENSION * pdx)
+{
+ int iReturn = TYPEUNKNOWN;
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ switch (pdx->s1401Type) {
+ case TYPE1401:
+ iReturn = U14ERR_STD;
+ break; // Handle these types directly
+ case TYPEPLUS:
+ iReturn = U14ERR_PLUS;
+ break;
+ case TYPEU1401:
+ iReturn = U14ERR_U1401;
+ break;
+ default:
+ if ((pdx->s1401Type >= TYPEPOWER) && (pdx->s1401Type <= 25))
+ iReturn = pdx->s1401Type + 4; // We can calculate types
+ else // for up-coming 1401 designs
+ iReturn = TYPEUNKNOWN; // Don't know or not there
+ }
+ dev_dbg(&pdx->interface->dev, "%s %d", __func__, iReturn);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** TransferFlags
+**
+** Returns flags on block transfer abilities
+****************************************************************************/
+int TransferFlags(DEVICE_EXTENSION * pdx)
+{
+ int iReturn = U14TF_MULTIA | U14TF_DIAG | // we always have multiple DMA area
+ U14TF_NOTIFY | U14TF_CIRCTH; // diagnostics, notify and circular
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ mutex_lock(&pdx->io_mutex);
+ if (pdx->bIsUSB2) // Set flag for USB2 if appropriate
+ iReturn |= U14TF_USB2;
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/***************************************************************************
+** DbgCmd1401
+** Issues a debug\diagnostic command to the 1401 along with a 32-bit datum
+** This is a utility command used for dbg operations.
+*/
+static int DbgCmd1401(DEVICE_EXTENSION * pdx, unsigned char cmd,
+ unsigned int data)
+{
+ int iReturn;
+ dev_dbg(&pdx->interface->dev, "%s entry", __func__);
+ iReturn = usb_control_msg(pdx->udev, usb_sndctrlpipe(pdx->udev, 0), cmd, (H_TO_D | VENDOR | DEVREQ), (unsigned short)data, (unsigned short)(data >> 16), 0, 0, HZ); // allow 1 second timeout
+ if (iReturn < 0)
+ dev_err(&pdx->interface->dev, "%s fail code=%d", __func__,
+ iReturn);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** DbgPeek
+**
+** Execute the diagnostic peek operation. Uses address, width and repeats.
+****************************************************************************/
+int DbgPeek(DEVICE_EXTENSION * pdx, TDBGBLOCK __user * pDB)
+{
+ int iReturn;
+ TDBGBLOCK db;
+
+ if (copy_from_user(&db, pDB, sizeof(db)))
+ return -EFAULT;
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s @ %08x", __func__, db.iAddr);
+
+ iReturn = DbgCmd1401(pdx, DB_SETADD, db.iAddr);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_PEEK, 0);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** DbgPoke
+**
+** Execute the diagnostic poke operation. Parameters are in the CSBLOCK struct
+** in order address, size, repeats and value to poke.
+****************************************************************************/
+int DbgPoke(DEVICE_EXTENSION * pdx, TDBGBLOCK __user * pDB)
+{
+ int iReturn;
+ TDBGBLOCK db;
+
+ if (copy_from_user(&db, pDB, sizeof(db)))
+ return -EFAULT;
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s @ %08x", __func__, db.iAddr);
+
+ iReturn = DbgCmd1401(pdx, DB_SETADD, db.iAddr);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_POKE, db.iData);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** DbgRampData
+**
+** Execute the diagnostic ramp data operation. Parameters are in the CSBLOCK struct
+** in order address, default, enable mask, size and repeats.
+****************************************************************************/
+int DbgRampData(DEVICE_EXTENSION * pdx, TDBGBLOCK __user * pDB)
+{
+ int iReturn;
+ TDBGBLOCK db;
+
+ if (copy_from_user(&db, pDB, sizeof(db)))
+ return -EFAULT;
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s @ %08x", __func__, db.iAddr);
+
+ iReturn = DbgCmd1401(pdx, DB_SETADD, db.iAddr);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_SETDEF, db.iDefault);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_SETMASK, db.iMask);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_RAMPD, 0);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** DbgRampAddr
+**
+** Execute the diagnostic ramp address operation
+****************************************************************************/
+int DbgRampAddr(DEVICE_EXTENSION * pdx, TDBGBLOCK __user * pDB)
+{
+ int iReturn;
+ TDBGBLOCK db;
+
+ if (copy_from_user(&db, pDB, sizeof(db)))
+ return -EFAULT;
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ iReturn = DbgCmd1401(pdx, DB_SETDEF, db.iDefault);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_SETMASK, db.iMask);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_WIDTH, db.iWidth);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_REPEATS, db.iRepeats);
+ if (iReturn == U14ERR_NOERROR)
+ iReturn = DbgCmd1401(pdx, DB_RAMPA, 0);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** DbgGetData
+**
+** Retrieve the data resulting from the last debug Peek operation
+****************************************************************************/
+int DbgGetData(DEVICE_EXTENSION * pdx, TDBGBLOCK __user * pDB)
+{
+ int iReturn;
+ TDBGBLOCK db;
+ memset(&db, 0, sizeof(db)); // fill returned block with 0s
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ // Read back the last peeked value from the 1401.
+ iReturn = usb_control_msg(pdx->udev, usb_rcvctrlpipe(pdx->udev, 0),
+ DB_DATA, (D_TO_H | VENDOR | DEVREQ), 0, 0,
+ &db.iData, sizeof(db.iData), HZ);
+ if (iReturn == sizeof(db.iData)) {
+ if (copy_to_user(pDB, &db, sizeof(db)))
+ iReturn = -EFAULT;
+ else
+ iReturn = U14ERR_NOERROR;
+ } else
+ dev_err(&pdx->interface->dev, "%s failed, code %d", __func__,
+ iReturn);
+
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** DbgStopLoop
+**
+** Stop any never-ending debug loop, we just call Get1401State for USB
+**
+****************************************************************************/
+int DbgStopLoop(DEVICE_EXTENSION * pdx)
+{
+ int iReturn;
+ unsigned int uState, uErr;
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ iReturn = Get1401State(pdx, &uState, &uErr);
+ mutex_unlock(&pdx->io_mutex);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** SetCircular
+**
+** Sets up a transfer area record for circular transfers. If the area is
+** already set, we attempt to unset it. Unsetting will fail if the area is
+** booked and a transfer to that area is in progress. Otherwise, we will
+** release the area and re-assign it.
+****************************************************************************/
+int SetCircular(DEVICE_EXTENSION * pdx, TRANSFERDESC __user * pTD)
+{
+ int iReturn;
+ bool bToHost;
+ TRANSFERDESC td;
+
+ if (copy_from_user(&td, pTD, sizeof(td)))
+ return -EFAULT;
+
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s area:%d, size:%08x", __func__,
+ td.wAreaNum, td.dwLength);
+ bToHost = td.eSize != 0; // this is used as the tohost flag
+
+ // The strange cast is done so that we don't get warnings in 32-bit linux about the size of the
+ // pointer. The pointer is always passed as a 64-bit object so that we don't have problems using
+ // a 32-bit program on a 64-bit system. unsigned long is 64-bits on a 64-bit system.
+ iReturn =
+ SetArea(pdx, td.wAreaNum,
+ (char __user *)((unsigned long)td.lpvBuff), td.dwLength,
+ true, bToHost);
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+/****************************************************************************
+** GetCircBlock
+**
+** Return the next available block of circularly-transferred data.
+****************************************************************************/
+int GetCircBlock(DEVICE_EXTENSION * pdx, TCIRCBLOCK __user * pCB)
+{
+ int iReturn = U14ERR_NOERROR;
+ unsigned int nArea;
+ TCIRCBLOCK cb;
+
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ if (copy_from_user(&cb, pCB, sizeof(cb)))
+ return -EFAULT;
+
+ mutex_lock(&pdx->io_mutex);
+
+ nArea = cb.nArea; // Retrieve parameters first
+ cb.dwOffset = 0; // set default result (nothing)
+ cb.dwSize = 0;
+
+ if (nArea < MAX_TRANSAREAS) // The area number must be OK
+ {
+ TRANSAREA *pArea = &pdx->rTransDef[nArea]; // Pointer to relevant info
+ spin_lock_irq(&pdx->stagedLock); // Lock others out
+
+ if ((pArea->bUsed) && (pArea->bCircular) && // Must be circular area
+ (pArea->bCircToHost)) // For now at least must be to host
+ {
+ if (pArea->aBlocks[0].dwSize > 0) // Got anything?
+ {
+ cb.dwOffset = pArea->aBlocks[0].dwOffset;
+ cb.dwSize = pArea->aBlocks[0].dwSize;
+ dev_dbg(&pdx->interface->dev,
+ "%s return block 0: %d bytes at %d",
+ __func__, cb.dwSize, cb.dwOffset);
+ }
+ } else
+ iReturn = U14ERR_NOTSET;
+
+ spin_unlock_irq(&pdx->stagedLock);
+ } else
+ iReturn = U14ERR_BADAREA;
+
+ if (copy_to_user(pCB, &cb, sizeof(cb)))
+ iReturn = -EFAULT;
+
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
+
+/****************************************************************************
+** FreeCircBlock
+**
+** Frees a block of circularly-transferred data and returns the next one.
+****************************************************************************/
+int FreeCircBlock(DEVICE_EXTENSION * pdx, TCIRCBLOCK __user * pCB)
+{
+ int iReturn = U14ERR_NOERROR;
+ unsigned int nArea, uStart, uSize;
+ TCIRCBLOCK cb;
+
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ if (copy_from_user(&cb, pCB, sizeof(cb)))
+ return -EFAULT;
+
+ mutex_lock(&pdx->io_mutex);
+
+ nArea = cb.nArea; // Retrieve parameters first
+ uStart = cb.dwOffset;
+ uSize = cb.dwSize;
+ cb.dwOffset = 0; // then set default result (nothing)
+ cb.dwSize = 0;
+
+ if (nArea < MAX_TRANSAREAS) // The area number must be OK
+ {
+ TRANSAREA *pArea = &pdx->rTransDef[nArea]; // Pointer to relevant info
+ spin_lock_irq(&pdx->stagedLock); // Lock others out
+
+ if ((pArea->bUsed) && (pArea->bCircular) && // Must be circular area
+ (pArea->bCircToHost)) // For now at least must be to host
+ {
+ bool bWaiting = false;
+
+ if ((pArea->aBlocks[0].dwSize >= uSize) && // Got anything?
+ (pArea->aBlocks[0].dwOffset == uStart)) // Must be legal data
+ {
+ pArea->aBlocks[0].dwSize -= uSize;
+ pArea->aBlocks[0].dwOffset += uSize;
+ if (pArea->aBlocks[0].dwSize == 0) // Have we emptied this block?
+ {
+ if (pArea->aBlocks[1].dwSize) // Is there a second block?
+ {
+ pArea->aBlocks[0] = pArea->aBlocks[1]; // Copy down block 2 data
+ pArea->aBlocks[1].dwSize = 0; // and mark the second block as unused
+ pArea->aBlocks[1].dwOffset = 0;
+ } else
+ pArea->aBlocks[0].dwOffset = 0;
+ }
+
+ dev_dbg(&pdx->interface->dev,
+ "%s free %d bytes at %d, return %d bytes at %d, wait=%d",
+ __func__, uSize, uStart,
+ pArea->aBlocks[0].dwSize,
+ pArea->aBlocks[0].dwOffset,
+ pdx->bXFerWaiting);
+
+ // Return the next available block of memory as well
+ if (pArea->aBlocks[0].dwSize > 0) // Got anything?
+ {
+ cb.dwOffset =
+ pArea->aBlocks[0].dwOffset;
+ cb.dwSize = pArea->aBlocks[0].dwSize;
+ }
+
+ bWaiting = pdx->bXFerWaiting;
+ if (bWaiting && pdx->bStagedUrbPending) {
+ dev_err(&pdx->interface->dev,
+ "%s ERROR: waiting xfer and staged Urb pending!",
+ __func__);
+ bWaiting = false;
+ }
+ } else {
+ dev_err(&pdx->interface->dev,
+ "%s ERROR: freeing %d bytes at %d, block 0 is %d bytes at %d",
+ __func__, uSize, uStart,
+ pArea->aBlocks[0].dwSize,
+ pArea->aBlocks[0].dwOffset);
+ iReturn = U14ERR_NOMEMORY;
+ }
+
+ // If we have one, kick off pending transfer
+ if (bWaiting) // Got a block xfer waiting?
+ {
+ int RWMStat =
+ ReadWriteMem(pdx, !pdx->rDMAInfo.bOutWard,
+ pdx->rDMAInfo.wIdent,
+ pdx->rDMAInfo.dwOffset,
+ pdx->rDMAInfo.dwSize);
+ if (RWMStat != U14ERR_NOERROR)
+ dev_err(&pdx->interface->dev,
+ "%s rw setup failed %d",
+ __func__, RWMStat);
+ }
+ } else
+ iReturn = U14ERR_NOTSET;
+
+ spin_unlock_irq(&pdx->stagedLock);
+ } else
+ iReturn = U14ERR_BADAREA;
+
+ if (copy_to_user(pCB, &cb, sizeof(cb)))
+ return -EFAULT;
+
+ mutex_unlock(&pdx->io_mutex);
+ return iReturn;
+}
diff --git a/drivers/staging/ced1401/ced_ioctl.h b/drivers/staging/ced1401/ced_ioctl.h
new file mode 100644
index 000000000000..0895c9414b4f
--- /dev/null
+++ b/drivers/staging/ced1401/ced_ioctl.h
@@ -0,0 +1,345 @@
+/*
+ * IOCTL calls for the CED1401 driver
+ * Copyright (C) 2010 Cambridge Electronic Design Ltd
+ * Author Greg P Smith (greg@ced.co.uk)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __CED_IOCTL_H__
+#define __CED_IOCTL_H__
+
+#include <linux/ioctl.h>
+
+/* dma modes, only MODE_CHAR and MODE_LINEAR are used in this driver */
+#define MODE_CHAR 0
+#define MODE_LINEAR 1
+
+/****************************************************************************
+** TypeDefs
+*****************************************************************************/
+
+typedef unsigned short TBLOCKENTRY; /* index the blk transfer table 0-7 */
+
+typedef struct TransferDesc {
+ long long lpvBuff; /* address of transfer area (for 64 or 32 bit) */
+ unsigned int dwLength; /* length of the area */
+ TBLOCKENTRY wAreaNum; /* number of transfer area to set up */
+ short eSize; /* element size - is tohost flag for circular */
+} TRANSFERDESC;
+
+typedef TRANSFERDESC * LPTRANSFERDESC;
+
+typedef struct TransferEvent {
+ unsigned int dwStart; /* offset into the area */
+ unsigned int dwLength; /* length of the region */
+ unsigned short wAreaNum; /* the area number */
+ unsigned short wFlags; /* bit 0 set for toHost */
+ int iSetEvent; /* could be dummy in LINUX */
+} TRANSFEREVENT;
+
+#define MAX_TRANSFER_SIZE 0x4000 /* Maximum data bytes per IRP */
+#define MAX_AREA_LENGTH 0x100000 /* Maximum size of transfer area */
+#define MAX_TRANSAREAS 8 /* definitions for dma set up */
+
+typedef struct TGetSelfTest {
+ int code; /* self-test error code */
+ int x, y; /* additional information */
+} TGET_SELFTEST;
+
+/* Debug block used for several commands. Not all fields are used for all commands. */
+typedef struct TDbgBlock {
+ int iAddr; /* the address in the 1401 */
+ int iRepeats; /* number of repeats */
+ int iWidth; /* width in bytes 1, 2, 4 */
+ int iDefault; /* default value */
+ int iMask; /* mask to apply */
+ int iData; /* data for poke, result for peek */
+} TDBGBLOCK;
+
+/* Used to collect information about a circular block from the device driver */
+typedef struct TCircBlock {
+ unsigned int nArea; /* the area to collect information from */
+ unsigned int dwOffset; /* offset into the area to the available block */
+ unsigned int dwSize; /* size of the area */
+} TCIRCBLOCK;
+
+/* Used to clollect the 1401 status */
+typedef struct TCSBlock {
+ unsigned int uiState;
+ unsigned int uiError;
+} TCSBLOCK;
+
+/*
+ * As seen by the user, an ioctl call looks like: int ioctl(int fd, unsigned
+ * long cmd, char* argp); We will then have all sorts of variants on this that
+ * can be used to pass stuff to our driver. We will generate macros for each
+ * type of call so as to provide some sort of type safety in the calling:
+ */
+#define CED_MAGIC_IOC 0xce
+
+/* NBNB: READ and WRITE are from the point of view of the device, not user. */
+typedef struct ced_ioc_string {
+ int nChars;
+ char buffer[256];
+} CED_IOC_STRING;
+
+#define IOCTL_CED_SENDSTRING(n) _IOC(_IOC_WRITE, CED_MAGIC_IOC, 2, n)
+
+#define IOCTL_CED_RESET1401 _IO(CED_MAGIC_IOC, 3)
+#define IOCTL_CED_GETCHAR _IO(CED_MAGIC_IOC, 4)
+#define IOCTL_CED_SENDCHAR _IO(CED_MAGIC_IOC, 5)
+#define IOCTL_CED_STAT1401 _IO(CED_MAGIC_IOC, 6)
+#define IOCTL_CED_LINECOUNT _IO(CED_MAGIC_IOC, 7)
+#define IOCTL_CED_GETSTRING(nMax) _IOC(_IOC_READ, CED_MAGIC_IOC, 8, nMax)
+
+#define IOCTL_CED_SETTRANSFER _IOW(CED_MAGIC_IOC, 11, TRANSFERDESC)
+#define IOCTL_CED_UNSETTRANSFER _IO(CED_MAGIC_IOC, 12)
+#define IOCTL_CED_SETEVENT _IOW(CED_MAGIC_IOC, 13, TRANSFEREVENT)
+#define IOCTL_CED_GETOUTBUFSPACE _IO(CED_MAGIC_IOC, 14)
+#define IOCTL_CED_GETBASEADDRESS _IO(CED_MAGIC_IOC, 15)
+#define IOCTL_CED_GETDRIVERREVISION _IO(CED_MAGIC_IOC, 16)
+
+#define IOCTL_CED_GETTRANSFER _IOR(CED_MAGIC_IOC, 17, TGET_TX_BLOCK)
+#define IOCTL_CED_KILLIO1401 _IO(CED_MAGIC_IOC, 18)
+#define IOCTL_CED_BLKTRANSSTATE _IO(CED_MAGIC_IOC, 19)
+
+#define IOCTL_CED_STATEOF1401 _IO(CED_MAGIC_IOC, 23)
+#define IOCTL_CED_GRAB1401 _IO(CED_MAGIC_IOC, 25)
+#define IOCTL_CED_FREE1401 _IO(CED_MAGIC_IOC, 26)
+#define IOCTL_CED_STARTSELFTEST _IO(CED_MAGIC_IOC, 31)
+#define IOCTL_CED_CHECKSELFTEST _IOR(CED_MAGIC_IOC, 32, TGET_SELFTEST)
+#define IOCTL_CED_TYPEOF1401 _IO(CED_MAGIC_IOC, 33)
+#define IOCTL_CED_TRANSFERFLAGS _IO(CED_MAGIC_IOC, 34)
+
+#define IOCTL_CED_DBGPEEK _IOW(CED_MAGIC_IOC, 35, TDBGBLOCK)
+#define IOCTL_CED_DBGPOKE _IOW(CED_MAGIC_IOC, 36, TDBGBLOCK)
+#define IOCTL_CED_DBGRAMPDATA _IOW(CED_MAGIC_IOC, 37, TDBGBLOCK)
+#define IOCTL_CED_DBGRAMPADDR _IOW(CED_MAGIC_IOC, 38, TDBGBLOCK)
+#define IOCTL_CED_DBGGETDATA _IOR(CED_MAGIC_IOC, 39, TDBGBLOCK)
+#define IOCTL_CED_DBGSTOPLOOP _IO(CED_MAGIC_IOC, 40)
+#define IOCTL_CED_FULLRESET _IO(CED_MAGIC_IOC, 41)
+#define IOCTL_CED_SETCIRCULAR _IOW(CED_MAGIC_IOC, 42, TRANSFERDESC)
+#define IOCTL_CED_GETCIRCBLOCK _IOWR(CED_MAGIC_IOC, 43, TCIRCBLOCK)
+#define IOCTL_CED_FREECIRCBLOCK _IOWR(CED_MAGIC_IOC, 44, TCIRCBLOCK)
+#define IOCTL_CED_WAITEVENT _IO(CED_MAGIC_IOC, 45)
+#define IOCTL_CED_TESTEVENT _IO(CED_MAGIC_IOC, 46)
+
+#ifndef __KERNEL__
+/*
+ * If nothing said about return value, it is a U14ERR_... error code
+ * (U14ERR_NOERROR for none)
+ */
+inline int CED_SendString(int fh, const char *szText, int n)
+{
+ return ioctl(fh, IOCTL_CED_SENDSTRING(n), szText);
+}
+
+inline int CED_Reset1401(int fh)
+{
+ return ioctl(fh, IOCTL_CED_RESET1401);
+}
+
+/* Return the singe character or a -ve error code. */
+inline int CED_GetChar(int fh)
+{
+ return ioctl(fh, IOCTL_CED_GETCHAR);
+}
+
+/* Return character count in input buffer */
+inline int CED_Stat1401(int fh)
+{
+ return ioctl(fh, IOCTL_CED_STAT1401);
+}
+
+inline int CED_SendChar(int fh, char c)
+{
+ return ioctl(fh, IOCTL_CED_SENDCHAR, c);
+}
+
+inline int CED_LineCount(int fh)
+{
+ return ioctl(fh, IOCTL_CED_LINECOUNT);
+}
+
+/*
+ * return the count of characters returned. If the string was terminated by CR
+ * or 0, then the 0 is part of the count. Otherwise, we will add a zero if
+ * there is room, but it is not included in the count. The return value is 0
+ * if there was nothing to read.
+ */
+inline int CED_GetString(int fh, char *szText, int nMax)
+{
+ return ioctl(fh, IOCTL_CED_GETSTRING(nMax), szText);
+}
+
+/* returns space in the output buffer. */
+inline int CED_GetOutBufSpace(int fh)
+{
+ return ioctl(fh, IOCTL_CED_GETOUTBUFSPACE);
+}
+
+/* This always returns -1 as not implemented. */
+inline int CED_GetBaseAddress(int fh)
+{
+ return ioctl(fh, IOCTL_CED_GETBASEADDRESS);
+}
+
+/* returns the major revision <<16 | minor revision. */
+inline int CED_GetDriverRevision(int fh)
+{
+ return ioctl(fh, IOCTL_CED_GETDRIVERREVISION);
+}
+
+inline int CED_SetTransfer(int fh, TRANSFERDESC *pTD)
+{
+ return ioctl(fh, IOCTL_CED_SETTRANSFER, pTD);
+}
+
+inline int CED_UnsetTransfer(int fh, int nArea)
+{
+ return ioctl(fh, IOCTL_CED_UNSETTRANSFER, nArea);
+}
+
+inline int CED_SetEvent(int fh, TRANSFEREVENT *pTE)
+{
+ return ioctl(fh, IOCTL_CED_SETEVENT, pTE);
+}
+
+inline int CED_GetTransfer(int fh, TGET_TX_BLOCK *pTX)
+{
+ return ioctl(fh, IOCTL_CED_GETTRANSFER, pTX);
+}
+
+inline int CED_KillIO1401(int fh)
+{
+ return ioctl(fh, IOCTL_CED_KILLIO1401);
+}
+
+/* returns 0 if no active DMA, 1 if active */
+inline int CED_BlkTransState(int fh)
+{
+ return ioctl(fh, IOCTL_CED_BLKTRANSSTATE);
+}
+
+inline int CED_StateOf1401(int fh)
+{
+ return ioctl(fh, IOCTL_CED_STATEOF1401);
+}
+
+inline int CED_Grab1401(int fh)
+{
+ return ioctl(fh, IOCTL_CED_GRAB1401);
+}
+
+inline int CED_Free1401(int fh)
+{
+ return ioctl(fh, IOCTL_CED_FREE1401);
+}
+
+inline int CED_StartSelfTest(int fh)
+{
+ return ioctl(fh, IOCTL_CED_STARTSELFTEST);
+}
+
+inline int CED_CheckSelfTest(int fh, TGET_SELFTEST *pGST)
+{
+ return ioctl(fh, IOCTL_CED_CHECKSELFTEST, pGST);
+}
+
+inline int CED_TypeOf1401(int fh)
+{
+ return ioctl(fh, IOCTL_CED_TYPEOF1401);
+}
+
+inline int CED_TransferFlags(int fh)
+{
+ return ioctl(fh, IOCTL_CED_TRANSFERFLAGS);
+}
+
+inline int CED_DbgPeek(int fh, TDBGBLOCK *pDB)
+{
+ return ioctl(fh, IOCTL_CED_DBGPEEK, pDB);
+}
+
+inline int CED_DbgPoke(int fh, TDBGBLOCK *pDB)
+{
+ return ioctl(fh, IOCTL_CED_DBGPOKE, pDB);
+}
+
+inline int CED_DbgRampData(int fh, TDBGBLOCK *pDB)
+{
+ return ioctl(fh, IOCTL_CED_DBGRAMPDATA, pDB);
+}
+
+inline int CED_DbgRampAddr(int fh, TDBGBLOCK *pDB)
+{
+ return ioctl(fh, IOCTL_CED_DBGRAMPADDR, pDB);
+}
+
+inline int CED_DbgGetData(int fh, TDBGBLOCK *pDB)
+{
+ return ioctl(fh, IOCTL_CED_DBGGETDATA, pDB);
+}
+
+inline int CED_DbgStopLoop(int fh)
+{
+ return ioctl(fh, IOCTL_CED_DBGSTOPLOOP);
+}
+
+inline int CED_FullReset(int fh)
+{
+ return ioctl(fh, IOCTL_CED_FULLRESET);
+}
+
+inline int CED_SetCircular(int fh, TRANSFERDESC *pTD)
+{
+ return ioctl(fh, IOCTL_CED_SETCIRCULAR, pTD);
+}
+
+inline int CED_GetCircBlock(int fh, TCIRCBLOCK *pCB)
+{
+ return ioctl(fh, IOCTL_CED_GETCIRCBLOCK, pCB);
+}
+
+inline int CED_FreeCircBlock(int fh, TCIRCBLOCK *pCB)
+{
+ return ioctl(fh, IOCTL_CED_FREECIRCBLOCK, pCB);
+}
+
+inline int CED_WaitEvent(int fh, int nArea, int msTimeOut)
+{
+ return ioctl(fh, IOCTL_CED_WAITEVENT, (nArea & 0xff)|(msTimeOut << 8));
+}
+
+inline int CED_TestEvent(int fh, int nArea)
+{
+ return ioctl(fh, IOCTL_CED_TESTEVENT, nArea);
+}
+#endif
+
+#ifdef NOTWANTEDYET
+#define IOCTL_CED_REGCALLBACK _IO(CED_MAGIC_IOC, 9) /* Not used */
+#define IOCTL_CED_GETMONITORBUF _IO(CED_MAGIC_IOC, 10) /* Not used */
+
+#define IOCTL_CED_BYTECOUNT _IO(CED_MAGIC_IOC, 20) /* Not used */
+#define IOCTL_CED_ZEROBLOCKCOUNT _IO(CED_MAGIC_IOC, 21) /* Not used */
+#define IOCTL_CED_STOPCIRCULAR _IO(CED_MAGIC_IOC, 22) /* Not used */
+
+#define IOCTL_CED_REGISTERS1401 _IO(CED_MAGIC_IOC, 24) /* Not used */
+#define IOCTL_CED_STEP1401 _IO(CED_MAGIC_IOC, 27) /* Not used */
+#define IOCTL_CED_SET1401REGISTERS _IO(CED_MAGIC_IOC, 28) /* Not used */
+#define IOCTL_CED_STEPTILL1401 _IO(CED_MAGIC_IOC, 29) /* Not used */
+#define IOCTL_CED_SETORIN _IO(CED_MAGIC_IOC, 30) /* Not used */
+
+#endif
+
+/* __CED_IOCTL_H__ */
+#endif
diff --git a/drivers/staging/ced1401/machine.h b/drivers/staging/ced1401/machine.h
new file mode 100644
index 000000000000..af073790b942
--- /dev/null
+++ b/drivers/staging/ced1401/machine.h
@@ -0,0 +1,127 @@
+/*****************************************************************************
+**
+** machine.h
+**
+** Copyright (c) Cambridge Electronic Design Limited 1991,1992,2010
+**
+** This program is free software; 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.
+**
+** Contact CED: Cambridge Electronic Design Limited, Science Park, Milton Road
+** Cambridge, CB6 0FE.
+** www.ced.co.uk
+** greg@ced.co.uk
+**
+** This file is included at the start of 'C' or 'C++' source file to define
+** things for cross-platform/compiler interoperability. This used to deal with
+** MSDOS/16-bit stuff, but this was all removed in Decemeber 2010. There are
+** three things to consider: Windows, LINUX, mac OSX (BSD Unix) and 32 vs 64
+** bit. At the time of writing (DEC 2010) there is a consensus on the following
+** and their unsigned equivalents:
+**
+** type bits
+** char 8
+** short 16
+** int 32
+** long long 64
+**
+** long is a problem as it is always 64 bits on linux/unix and is always 32 bits
+** on windows.
+** On windows, we define _IS_WINDOWS_ and one of WIN32 or WIN64.
+** On linux we define LINUX
+** On Max OSX we define MACOSX
+**
+*/
+
+#ifndef __MACHINE_H__
+#define __MACHINE_H__
+#ifndef __KERNEL__
+#include <float.h>
+#include <limits.h>
+#endif
+
+/*
+** The initial section is to identify the operating system
+*/
+#if (defined(__linux__) || defined(_linux) || defined(__linux)) && !defined(LINUX)
+#define LINUX 1
+#endif
+
+#if (defined(__WIN32__) || defined(_WIN32)) && !defined(WIN32)
+#define WIN32 1
+#endif
+
+#if defined(__APPLE__)
+#define MACOSX
+#endif
+
+#if defined(_WIN64)
+#undef WIN32
+#undef WIN64
+#define WIN64 1
+#endif
+
+#if defined(WIN32) || defined(WIN64)
+#define _IS_WINDOWS_ 1
+#endif
+
+#if defined(LINUX) || defined(MAXOSX)
+ #define FAR
+
+ typedef int BOOL; // To match Windows
+ typedef char * LPSTR;
+ typedef const char * LPCSTR;
+ typedef unsigned short WORD;
+ typedef unsigned int DWORD;
+ typedef unsigned char BYTE;
+ typedef BYTE BOOLEAN;
+ typedef unsigned char UCHAR;
+ #define __packed __attribute__((packed))
+ typedef BYTE * LPBYTE;
+ #define HIWORD(x) (WORD)(((x)>>16) & 0xffff)
+ #define LOWORD(x) (WORD)((x) & 0xffff)
+#endif
+
+#ifdef _IS_WINDOWS_
+#include <windows.h>
+#define __packed
+#endif
+
+/*
+** Sort out the DllExport and DllImport macros. The GCC compiler has its own
+** syntax for this, though it also supports the MS specific __declspec() as
+** a synonym.
+*/
+#ifdef GNUC
+ #define DllExport __attribute__((dllexport))
+ #define DllImport __attribute__((dllimport))
+#endif
+
+#ifndef DllExport
+#ifdef _IS_WINDOWS_
+ #define DllExport __declspec(dllexport)
+ #define DllImport __declspec(dllimport)
+#else
+ #define DllExport
+ #define DllImport
+#endif
+#endif /* _IS_WINDOWS_ */
+
+
+#ifndef TRUE
+ #define TRUE 1
+ #define FALSE 0
+#endif
+
+#endif
diff --git a/drivers/staging/ced1401/usb1401.c b/drivers/staging/ced1401/usb1401.c
new file mode 100644
index 000000000000..6ba0ef652561
--- /dev/null
+++ b/drivers/staging/ced1401/usb1401.c
@@ -0,0 +1,1637 @@
+/***********************************************************************************
+ CED1401 usb driver. This basic loading is based on the usb-skeleton.c code that is:
+ Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ Copyright (C) 2012 Alois Schloegl <alois.schloegl@ist.ac.at>
+ There is not a great deal of the skeleton left.
+
+ All the remainder dealing specifically with the CED1401 is based on drivers written
+ by CED for other systems (mainly Windows) and is:
+ Copyright (C) 2010 Cambridge Electronic Design Ltd
+ Author Greg P Smith (greg@ced.co.uk)
+
+ This program is free software; 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.
+
+Endpoints
+*********
+There are 4 endpoints plus the control endpoint in the standard interface
+provided by most 1401s. The control endpoint is used for standard USB requests,
+plus various CED-specific transactions such as start self test, debug and get
+the 1401 status. The other endpoints are:
+
+ 1 Characters to the 1401
+ 2 Characters from the 1401
+ 3 Block data to the 1401
+ 4 Block data to the host.
+
+inside the driver these are indexed as an array from 0 to 3, transactions
+over the control endpoint are carried out using a separate mechanism. The
+use of the endpoints is mostly straightforward, with the driver issuing
+IO request packets (IRPs) as required to transfer data to and from the 1401.
+The handling of endpoint 2 is different because it is used for characters
+from the 1401, which can appear spontaneously and without any other driver
+activity - for example to repeatedly request DMA transfers in Spike2. The
+desired effect is achieved by using an interrupt endpoint which can be
+polled to see if it has data available, and writing the driver so that it
+always maintains a pending read IRP from that endpoint which will read the
+character data and terminate as soon as the 1401 makes data available. This
+works very well, some care is taken with when you kick off this character
+read IRP to avoid it being active when it is not wanted but generally it
+is running all the time.
+
+In the 2270, there are only three endpoints plus the control endpoint. In
+addition to the transactions mentioned above, the control endpoint is used
+to transfer character data to the 1401. The other endpoints are used as:
+
+ 1 Characters from the 1401
+ 2 Block data to the 1401
+ 3 Block data to the host.
+
+The type of interface available is specified by the interface subclass field
+in the interface descriptor provided by the 1401. See the USB_INT_ constants
+for the values that this field can hold.
+
+****************************************************************************
+Linux implementation
+
+Although Linux Device Drivers (3rd Edition) was a major source of information,
+it is very out of date. A lot of information was gleaned from the latest
+usb_skeleton.c code (you need to download the kernel sources to get this).
+
+To match the Windows version, everything is done using ioctl calls. All the
+device state is held in the DEVICE_EXTENSION (named to match Windows use).
+Block transfers are done by using get_user_pages() to pin down a list of
+pages that we hold a pointer to in the device driver. We also allocate a
+coherent transfer buffer of size STAGED_SZ (this must be a multiple of the
+bulk endpoint size so that the 1401 does not realise that we break large
+transfers down into smaller pieces). We use kmap_atomic() to get a kernel
+va for each page, as it is required, for copying; see CopyUserSpace().
+
+All character and data transfers are done using asynchronous IO. All Urbs are
+tracked by anchoring them. Status and debug ioctls are implemented with the
+synchronous non-Urb based transfers.
+*/
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/mm.h>
+#include <linux/highmem.h>
+#include <linux/version.h>
+#if ( LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,35) )
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/uaccess.h>
+#endif
+
+#include "usb1401.h"
+
+/* Define these values to match your devices */
+#define USB_CED_VENDOR_ID 0x0525
+#define USB_CED_PRODUCT_ID 0xa0f0
+
+/* table of devices that work with this driver */
+static const struct usb_device_id ced_table[] = {
+ {USB_DEVICE(USB_CED_VENDOR_ID, USB_CED_PRODUCT_ID)},
+ {} /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, ced_table);
+
+/* Get a minor range for your devices from the usb maintainer */
+#define USB_CED_MINOR_BASE 192
+
+/* our private defines. if this grows any larger, use your own .h file */
+#define MAX_TRANSFER (PAGE_SIZE - 512)
+/* MAX_TRANSFER is chosen so that the VM is not stressed by
+ allocations > PAGE_SIZE and the number of packets in a page
+ is an integer 512 is the largest possible packet on EHCI */
+#define WRITES_IN_FLIGHT 8
+/* arbitrarily chosen */
+
+/*
+The cause for these errors is that the driver makes use of the functions usb_buffer_alloc() and usb_buffer_free() which got renamed in kernel 2.6.35. This is stated in the Changelog: USB: rename usb_buffer_alloc() and usb_buffer_free() users
+ For more clearance what the functions actually do,
+ usb_buffer_alloc() is renamed to usb_alloc_coherent()
+ usb_buffer_free() is renamed to usb_free_coherent()
+ This is needed on Debian 2.6.32-5-amd64
+*/
+#if ( LINUX_VERSION_CODE < KERNEL_VERSION(2,6,35) )
+#define usb_alloc_coherent usb_buffer_alloc
+#define usb_free_coherent usb_buffer_free
+#define noop_llseek NULL
+#endif
+
+static struct usb_driver ced_driver;
+
+static void ced_delete(struct kref *kref)
+{
+ DEVICE_EXTENSION *pdx = to_DEVICE_EXTENSION(kref);
+
+ // Free up the output buffer, then free the output urb. Note that the interface member
+ // of pdx will probably be NULL, so cannot be used to get to dev.
+ usb_free_coherent(pdx->udev, OUTBUF_SZ, pdx->pCoherCharOut,
+ pdx->pUrbCharOut->transfer_dma);
+ usb_free_urb(pdx->pUrbCharOut);
+
+ // Do the same for chan input
+ usb_free_coherent(pdx->udev, INBUF_SZ, pdx->pCoherCharIn,
+ pdx->pUrbCharIn->transfer_dma);
+ usb_free_urb(pdx->pUrbCharIn);
+
+ // Do the same for the block transfers
+ usb_free_coherent(pdx->udev, STAGED_SZ, pdx->pCoherStagedIO,
+ pdx->pStagedUrb->transfer_dma);
+ usb_free_urb(pdx->pStagedUrb);
+
+ usb_put_dev(pdx->udev);
+ kfree(pdx);
+}
+
+// This is the driver end of the open() call from user space.
+static int ced_open(struct inode *inode, struct file *file)
+{
+ DEVICE_EXTENSION *pdx;
+ int retval = 0;
+ int subminor = iminor(inode);
+ struct usb_interface *interface =
+ usb_find_interface(&ced_driver, subminor);
+ if (!interface) {
+ pr_err("%s - error, can't find device for minor %d", __func__,
+ subminor);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ pdx = usb_get_intfdata(interface);
+ if (!pdx) {
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ dev_dbg(&interface->dev, "%s got pdx", __func__);
+
+ /* increment our usage count for the device */
+ kref_get(&pdx->kref);
+
+ /* lock the device to allow correctly handling errors
+ * in resumption */
+ mutex_lock(&pdx->io_mutex);
+
+ if (!pdx->open_count++) {
+ retval = usb_autopm_get_interface(interface);
+ if (retval) {
+ pdx->open_count--;
+ mutex_unlock(&pdx->io_mutex);
+ kref_put(&pdx->kref, ced_delete);
+ goto exit;
+ }
+ } else { //uncomment this block if you want exclusive open
+ dev_err(&interface->dev, "%s fail: already open", __func__);
+ retval = -EBUSY;
+ pdx->open_count--;
+ mutex_unlock(&pdx->io_mutex);
+ kref_put(&pdx->kref, ced_delete);
+ goto exit;
+ }
+ /* prevent the device from being autosuspended */
+
+ /* save our object in the file's private structure */
+ file->private_data = pdx;
+ mutex_unlock(&pdx->io_mutex);
+
+exit:
+ return retval;
+}
+
+static int ced_release(struct inode *inode, struct file *file)
+{
+ DEVICE_EXTENSION *pdx = file->private_data;
+ if (pdx == NULL)
+ return -ENODEV;
+
+ dev_dbg(&pdx->interface->dev, "%s called", __func__);
+ mutex_lock(&pdx->io_mutex);
+ if (!--pdx->open_count && pdx->interface) // Allow autosuspend
+ usb_autopm_put_interface(pdx->interface);
+ mutex_unlock(&pdx->io_mutex);
+
+ kref_put(&pdx->kref, ced_delete); // decrement the count on our device
+ return 0;
+}
+
+static int ced_flush(struct file *file, fl_owner_t id)
+{
+ int res;
+ DEVICE_EXTENSION *pdx = file->private_data;
+ if (pdx == NULL)
+ return -ENODEV;
+
+ dev_dbg(&pdx->interface->dev, "%s char in pend=%d", __func__,
+ pdx->bReadCharsPending);
+
+ /* wait for io to stop */
+ mutex_lock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s got io_mutex", __func__);
+ ced_draw_down(pdx);
+
+ /* read out errors, leave subsequent opens a clean slate */
+ spin_lock_irq(&pdx->err_lock);
+ res = pdx->errors ? (pdx->errors == -EPIPE ? -EPIPE : -EIO) : 0;
+ pdx->errors = 0;
+ spin_unlock_irq(&pdx->err_lock);
+
+ mutex_unlock(&pdx->io_mutex);
+ dev_dbg(&pdx->interface->dev, "%s exit reached", __func__);
+
+ return res;
+}
+
+/***************************************************************************
+** CanAcceptIoRequests
+** If the device is removed, interface is set NULL. We also clear our pointer
+** from the interface, so we should make sure that pdx is not NULL. This will
+** not help with a device extension held by a file.
+** return true if can accept new io requests, else false
+*/
+static bool CanAcceptIoRequests(DEVICE_EXTENSION * pdx)
+{
+ return pdx && pdx->interface; // Can we accept IO requests
+}
+
+/****************************************************************************
+** Callback routine to complete writes. This may need to fire off another
+** urb to complete the transfer.
+****************************************************************************/
+static void ced_writechar_callback(struct urb *pUrb)
+{
+ DEVICE_EXTENSION *pdx = pUrb->context;
+ int nGot = pUrb->actual_length; // what we transferred
+
+ if (pUrb->status) { // sync/async unlink faults aren't errors
+ if (!
+ (pUrb->status == -ENOENT || pUrb->status == -ECONNRESET
+ || pUrb->status == -ESHUTDOWN)) {
+ dev_err(&pdx->interface->dev,
+ "%s - nonzero write bulk status received: %d",
+ __func__, pUrb->status);
+ }
+
+ spin_lock(&pdx->err_lock);
+ pdx->errors = pUrb->status;
+ spin_unlock(&pdx->err_lock);
+ nGot = 0; // and tidy up again if so
+
+ spin_lock(&pdx->charOutLock); // already at irq level
+ pdx->dwOutBuffGet = 0; // Reset the output buffer
+ pdx->dwOutBuffPut = 0;
+ pdx->dwNumOutput = 0; // Clear the char count
+ pdx->bPipeError[0] = 1; // Flag an error for later
+ pdx->bSendCharsPending = false; // Allow other threads again
+ spin_unlock(&pdx->charOutLock); // already at irq level
+ dev_dbg(&pdx->interface->dev,
+ "%s - char out done, 0 chars sent", __func__);
+ } else {
+ dev_dbg(&pdx->interface->dev,
+ "%s - char out done, %d chars sent", __func__, nGot);
+ spin_lock(&pdx->charOutLock); // already at irq level
+ pdx->dwNumOutput -= nGot; // Now adjust the char send buffer
+ pdx->dwOutBuffGet += nGot; // to match what we did
+ if (pdx->dwOutBuffGet >= OUTBUF_SZ) // Can't do this any earlier as data could be overwritten
+ pdx->dwOutBuffGet = 0;
+
+ if (pdx->dwNumOutput > 0) // if more to be done...
+ {
+ int nPipe = 0; // The pipe number to use
+ int iReturn;
+ char *pDat = &pdx->outputBuffer[pdx->dwOutBuffGet];
+ unsigned int dwCount = pdx->dwNumOutput; // maximum to send
+ if ((pdx->dwOutBuffGet + dwCount) > OUTBUF_SZ) // does it cross buffer end?
+ dwCount = OUTBUF_SZ - pdx->dwOutBuffGet;
+ spin_unlock(&pdx->charOutLock); // we are done with stuff that changes
+ memcpy(pdx->pCoherCharOut, pDat, dwCount); // copy output data to the buffer
+ usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev,
+ usb_sndbulkpipe(pdx->udev,
+ pdx->epAddr[0]),
+ pdx->pCoherCharOut, dwCount,
+ ced_writechar_callback, pdx);
+ pdx->pUrbCharOut->transfer_flags |=
+ URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted); // in case we need to kill it
+ iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_ATOMIC);
+ dev_dbg(&pdx->interface->dev, "%s n=%d>%s<", __func__,
+ dwCount, pDat);
+ spin_lock(&pdx->charOutLock); // grab lock for errors
+ if (iReturn) {
+ pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later
+ pdx->bSendCharsPending = false; // Allow other threads again
+ usb_unanchor_urb(pdx->pUrbCharOut);
+ dev_err(&pdx->interface->dev,
+ "%s usb_submit_urb() returned %d",
+ __func__, iReturn);
+ }
+ } else
+ pdx->bSendCharsPending = false; // Allow other threads again
+ spin_unlock(&pdx->charOutLock); // already at irq level
+ }
+}
+
+/****************************************************************************
+** SendChars
+** Transmit the characters in the output buffer to the 1401. This may need
+** breaking down into multiple transfers.
+****************************************************************************/
+int SendChars(DEVICE_EXTENSION * pdx)
+{
+ int iReturn = U14ERR_NOERROR;
+
+ spin_lock_irq(&pdx->charOutLock); // Protect ourselves
+
+ if ((!pdx->bSendCharsPending) && // Not currently sending
+ (pdx->dwNumOutput > 0) && // has characters to output
+ (CanAcceptIoRequests(pdx))) // and current activity is OK
+ {
+ unsigned int dwCount = pdx->dwNumOutput; // Get a copy of the character count
+ pdx->bSendCharsPending = true; // Set flag to lock out other threads
+
+ dev_dbg(&pdx->interface->dev,
+ "Send %d chars to 1401, EP0 flag %d\n", dwCount,
+ pdx->nPipes == 3);
+ // If we have only 3 end points we must send the characters to the 1401 using EP0.
+ if (pdx->nPipes == 3) {
+ // For EP0 character transmissions to the 1401, we have to hang about until they
+ // are gone, as otherwise without more character IO activity they will never go.
+ unsigned int count = dwCount; // Local char counter
+ unsigned int index = 0; // The index into the char buffer
+
+ spin_unlock_irq(&pdx->charOutLock); // Free spinlock as we call USBD
+
+ while ((count > 0) && (iReturn == U14ERR_NOERROR)) {
+ // We have to break the transfer up into 64-byte chunks because of a 2270 problem
+ int n = count > 64 ? 64 : count; // Chars for this xfer, max of 64
+ int nSent = usb_control_msg(pdx->udev,
+ usb_sndctrlpipe(pdx->udev, 0), // use end point 0
+ DB_CHARS, // bRequest
+ (H_TO_D | VENDOR | DEVREQ), // to the device, vendor request to the device
+ 0, 0, // value and index are both 0
+ &pdx->outputBuffer[index], // where to send from
+ n, // how much to send
+ 1000); // timeout in jiffies
+ if (nSent <= 0) {
+ iReturn = nSent ? nSent : -ETIMEDOUT; // if 0 chars says we timed out
+ dev_err(&pdx->interface->dev,
+ "Send %d chars by EP0 failed: %d",
+ n, iReturn);
+ } else {
+ dev_dbg(&pdx->interface->dev,
+ "Sent %d chars by EP0", n);
+ count -= nSent;
+ index += nSent;
+ }
+ }
+
+ spin_lock_irq(&pdx->charOutLock); // Protect pdx changes, released by general code
+ pdx->dwOutBuffGet = 0; // so reset the output buffer
+ pdx->dwOutBuffPut = 0;
+ pdx->dwNumOutput = 0; // and clear the buffer count
+ pdx->bSendCharsPending = false; // Allow other threads again
+ } else { // Here for sending chars normally - we hold the spin lock
+ int nPipe = 0; // The pipe number to use
+ char *pDat = &pdx->outputBuffer[pdx->dwOutBuffGet];
+
+ if ((pdx->dwOutBuffGet + dwCount) > OUTBUF_SZ) // does it cross buffer end?
+ dwCount = OUTBUF_SZ - pdx->dwOutBuffGet;
+ spin_unlock_irq(&pdx->charOutLock); // we are done with stuff that changes
+ memcpy(pdx->pCoherCharOut, pDat, dwCount); // copy output data to the buffer
+ usb_fill_bulk_urb(pdx->pUrbCharOut, pdx->udev,
+ usb_sndbulkpipe(pdx->udev,
+ pdx->epAddr[0]),
+ pdx->pCoherCharOut, dwCount,
+ ced_writechar_callback, pdx);
+ pdx->pUrbCharOut->transfer_flags |=
+ URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(pdx->pUrbCharOut, &pdx->submitted);
+ iReturn = usb_submit_urb(pdx->pUrbCharOut, GFP_KERNEL);
+ spin_lock_irq(&pdx->charOutLock); // grab lock for errors
+ if (iReturn) {
+ pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later
+ pdx->bSendCharsPending = false; // Allow other threads again
+ usb_unanchor_urb(pdx->pUrbCharOut); // remove from list of active urbs
+ }
+ }
+ } else if (pdx->bSendCharsPending && (pdx->dwNumOutput > 0))
+ dev_dbg(&pdx->interface->dev,
+ "SendChars bSendCharsPending:true");
+
+ dev_dbg(&pdx->interface->dev, "SendChars exit code: %d", iReturn);
+ spin_unlock_irq(&pdx->charOutLock); // Now let go of the spinlock
+ return iReturn;
+}
+
+/***************************************************************************
+** CopyUserSpace
+** This moves memory between pinned down user space and the pCoherStagedIO
+** memory buffer we use for transfers. Copy n bytes in the directions that
+** is defined by pdx->StagedRead. The user space is determined by the area
+** in pdx->StagedId and the offset in pdx->StagedDone. The user
+** area may well not start on a page boundary, so allow for that.
+**
+** We have a table of physical pages that describe the area, so we can use
+** this to get a virtual address that the kernel can use.
+**
+** pdx Is our device extension which holds all we know about the transfer.
+** n The number of bytes to move one way or the other.
+***************************************************************************/
+static void CopyUserSpace(DEVICE_EXTENSION * pdx, int n)
+{
+ unsigned int nArea = pdx->StagedId;
+ if (nArea < MAX_TRANSAREAS) {
+ TRANSAREA *pArea = &pdx->rTransDef[nArea]; // area to be used
+ unsigned int dwOffset =
+ pdx->StagedDone + pdx->StagedOffset + pArea->dwBaseOffset;
+ char *pCoherBuf = pdx->pCoherStagedIO; // coherent buffer
+ if (!pArea->bUsed) {
+ dev_err(&pdx->interface->dev, "%s area %d unused",
+ __func__, nArea);
+ return;
+ }
+
+ while (n) {
+ int nPage = dwOffset >> PAGE_SHIFT; // page number in table
+ if (nPage < pArea->nPages) {
+ char *pvAddress =
+ (char *)kmap_atomic(pArea->pPages[nPage]);
+ if (pvAddress) {
+ unsigned int uiPageOff = dwOffset & (PAGE_SIZE - 1); // offset into the page
+ size_t uiXfer = PAGE_SIZE - uiPageOff; // max to transfer on this page
+ if (uiXfer > n) // limit byte count if too much
+ uiXfer = n; // for the page
+ if (pdx->StagedRead)
+ memcpy(pvAddress + uiPageOff,
+ pCoherBuf, uiXfer);
+ else
+ memcpy(pCoherBuf,
+ pvAddress + uiPageOff,
+ uiXfer);
+ kunmap_atomic(pvAddress);
+ dwOffset += uiXfer;
+ pCoherBuf += uiXfer;
+ n -= uiXfer;
+ } else {
+ dev_err(&pdx->interface->dev,
+ "%s did not map page %d",
+ __func__, nPage);
+ return;
+ }
+
+ } else {
+ dev_err(&pdx->interface->dev,
+ "%s exceeded pages %d", __func__,
+ nPage);
+ return;
+ }
+ }
+ } else
+ dev_err(&pdx->interface->dev, "%s bad area %d", __func__,
+ nArea);
+}
+
+// Forward declarations for stuff used circularly
+static int StageChunk(DEVICE_EXTENSION * pdx);
+/***************************************************************************
+** ReadWrite_Complete
+**
+** Completion routine for our staged read/write Irps
+*/
+static void staged_callback(struct urb *pUrb)
+{
+ DEVICE_EXTENSION *pdx = pUrb->context;
+ unsigned int nGot = pUrb->actual_length; // what we transferred
+ bool bCancel = false;
+ bool bRestartCharInput; // used at the end
+
+ spin_lock(&pdx->stagedLock); // stop ReadWriteMem() action while this routine is running
+ pdx->bStagedUrbPending = false; // clear the flag for staged IRP pending
+
+ if (pUrb->status) { // sync/async unlink faults aren't errors
+ if (!
+ (pUrb->status == -ENOENT || pUrb->status == -ECONNRESET
+ || pUrb->status == -ESHUTDOWN)) {
+ dev_err(&pdx->interface->dev,
+ "%s - nonzero write bulk status received: %d",
+ __func__, pUrb->status);
+ } else
+ dev_info(&pdx->interface->dev,
+ "%s - staged xfer cancelled", __func__);
+
+ spin_lock(&pdx->err_lock);
+ pdx->errors = pUrb->status;
+ spin_unlock(&pdx->err_lock);
+ nGot = 0; // and tidy up again if so
+ bCancel = true;
+ } else {
+ dev_dbg(&pdx->interface->dev, "%s %d chars xferred", __func__,
+ nGot);
+ if (pdx->StagedRead) // if reading, save to user space
+ CopyUserSpace(pdx, nGot); // copy from buffer to user
+ if (nGot == 0)
+ dev_dbg(&pdx->interface->dev, "%s ZLP", __func__);
+ }
+
+ // Update the transfer length based on the TransferBufferLength value in the URB
+ pdx->StagedDone += nGot;
+
+ dev_dbg(&pdx->interface->dev, "%s, done %d bytes of %d", __func__,
+ pdx->StagedDone, pdx->StagedLength);
+
+ if ((pdx->StagedDone == pdx->StagedLength) || // If no more to do
+ (bCancel)) // or this IRP was cancelled
+ {
+ TRANSAREA *pArea = &pdx->rTransDef[pdx->StagedId]; // Transfer area info
+ dev_dbg(&pdx->interface->dev,
+ "%s transfer done, bytes %d, cancel %d", __func__,
+ pdx->StagedDone, bCancel);
+
+ // Here is where we sort out what to do with this transfer if using a circular buffer. We have
+ // a completed transfer that can be assumed to fit into the transfer area. We should be able to
+ // add this to the end of a growing block or to use it to start a new block unless the code
+ // that calculates the offset to use (in ReadWriteMem) is totally duff.
+ if ((pArea->bCircular) && (pArea->bCircToHost) && (!bCancel) && // Time to sort out circular buffer info?
+ (pdx->StagedRead)) // Only for tohost transfers for now
+ {
+ if (pArea->aBlocks[1].dwSize > 0) // If block 1 is in use we must append to it
+ {
+ if (pdx->StagedOffset ==
+ (pArea->aBlocks[1].dwOffset +
+ pArea->aBlocks[1].dwSize)) {
+ pArea->aBlocks[1].dwSize +=
+ pdx->StagedLength;
+ dev_dbg(&pdx->interface->dev,
+ "RWM_Complete, circ block 1 now %d bytes at %d",
+ pArea->aBlocks[1].dwSize,
+ pArea->aBlocks[1].dwOffset);
+ } else {
+ // Here things have gone very, very, wrong, but I cannot see how this can actually be achieved
+ pArea->aBlocks[1].dwOffset =
+ pdx->StagedOffset;
+ pArea->aBlocks[1].dwSize =
+ pdx->StagedLength;
+ dev_err(&pdx->interface->dev,
+ "%s ERROR, circ block 1 re-started %d bytes at %d",
+ __func__,
+ pArea->aBlocks[1].dwSize,
+ pArea->aBlocks[1].dwOffset);
+ }
+ } else // If block 1 is not used, we try to add to block 0
+ {
+ if (pArea->aBlocks[0].dwSize > 0) // Got stored block 0 information?
+ { // Must append onto the existing block 0
+ if (pdx->StagedOffset ==
+ (pArea->aBlocks[0].dwOffset +
+ pArea->aBlocks[0].dwSize)) {
+ pArea->aBlocks[0].dwSize += pdx->StagedLength; // Just add this transfer in
+ dev_dbg(&pdx->interface->dev,
+ "RWM_Complete, circ block 0 now %d bytes at %d",
+ pArea->aBlocks[0].
+ dwSize,
+ pArea->aBlocks[0].
+ dwOffset);
+ } else // If it doesn't append, put into new block 1
+ {
+ pArea->aBlocks[1].dwOffset =
+ pdx->StagedOffset;
+ pArea->aBlocks[1].dwSize =
+ pdx->StagedLength;
+ dev_dbg(&pdx->interface->dev,
+ "RWM_Complete, circ block 1 started %d bytes at %d",
+ pArea->aBlocks[1].
+ dwSize,
+ pArea->aBlocks[1].
+ dwOffset);
+ }
+ } else // No info stored yet, just save in block 0
+ {
+ pArea->aBlocks[0].dwOffset =
+ pdx->StagedOffset;
+ pArea->aBlocks[0].dwSize =
+ pdx->StagedLength;
+ dev_dbg(&pdx->interface->dev,
+ "RWM_Complete, circ block 0 started %d bytes at %d",
+ pArea->aBlocks[0].dwSize,
+ pArea->aBlocks[0].dwOffset);
+ }
+ }
+ }
+
+ if (!bCancel) // Don't generate an event if cancelled
+ {
+ dev_dbg(&pdx->interface->dev,
+ "RWM_Complete, bCircular %d, bToHost %d, eStart %d, eSize %d",
+ pArea->bCircular, pArea->bEventToHost,
+ pArea->dwEventSt, pArea->dwEventSz);
+ if ((pArea->dwEventSz) && // Set a user-mode event...
+ (pdx->StagedRead == pArea->bEventToHost)) // ...on transfers in this direction?
+ {
+ int iWakeUp = 0; // assume
+ // If we have completed the right sort of DMA transfer then set the event to notify
+ // the user code to wake up anyone that is waiting.
+ if ((pArea->bCircular) && // Circular areas use a simpler test
+ (pArea->bCircToHost)) // only in supported direction
+ { // Is total data waiting up to size limit?
+ unsigned int dwTotal =
+ pArea->aBlocks[0].dwSize +
+ pArea->aBlocks[1].dwSize;
+ iWakeUp = (dwTotal >= pArea->dwEventSz);
+ } else {
+ unsigned int transEnd =
+ pdx->StagedOffset +
+ pdx->StagedLength;
+ unsigned int eventEnd =
+ pArea->dwEventSt + pArea->dwEventSz;
+ iWakeUp = (pdx->StagedOffset < eventEnd)
+ && (transEnd > pArea->dwEventSt);
+ }
+
+ if (iWakeUp) {
+ dev_dbg(&pdx->interface->dev,
+ "About to set event to notify app");
+ wake_up_interruptible(&pArea->wqEvent); // wake up waiting processes
+ ++pArea->iWakeUp; // increment wakeup count
+ }
+ }
+ }
+
+ pdx->dwDMAFlag = MODE_CHAR; // Switch back to char mode before ReadWriteMem call
+
+ if (!bCancel) // Don't look for waiting transfer if cancelled
+ {
+ // If we have a transfer waiting, kick it off
+ if (pdx->bXFerWaiting) // Got a block xfer waiting?
+ {
+ int iReturn;
+ dev_info(&pdx->interface->dev,
+ "*** RWM_Complete *** pending transfer will now be set up!!!");
+ iReturn =
+ ReadWriteMem(pdx, !pdx->rDMAInfo.bOutWard,
+ pdx->rDMAInfo.wIdent,
+ pdx->rDMAInfo.dwOffset,
+ pdx->rDMAInfo.dwSize);
+
+ if (iReturn)
+ dev_err(&pdx->interface->dev,
+ "RWM_Complete rw setup failed %d",
+ iReturn);
+ }
+ }
+
+ } else // Here for more to do
+ StageChunk(pdx); // fire off the next bit
+
+ // While we hold the stagedLock, see if we should reallow character input ints
+ // Don't allow if cancelled, or if a new block has started or if there is a waiting block.
+ // This feels wrong as we should ask which spin lock protects dwDMAFlag.
+ bRestartCharInput = !bCancel && (pdx->dwDMAFlag == MODE_CHAR)
+ && !pdx->bXFerWaiting;
+
+ spin_unlock(&pdx->stagedLock); // Finally release the lock again
+
+ // This is not correct as dwDMAFlag is protected by the staged lock, but it is treated
+ // in Allowi as if it were protected by the char lock. In any case, most systems will
+ // not be upset by char input during DMA... sigh. Needs sorting out.
+ if (bRestartCharInput) // may be out of date, but...
+ Allowi(pdx, true); // ...Allowi tests a lock too.
+ dev_dbg(&pdx->interface->dev, "%s done", __func__);
+}
+
+/****************************************************************************
+** StageChunk
+**
+** Generates the next chunk of data making up a staged transfer.
+**
+** The calling code must have acquired the staging spinlock before calling
+** this function, and is responsible for releasing it. We are at callback level.
+****************************************************************************/
+static int StageChunk(DEVICE_EXTENSION * pdx)
+{
+ int iReturn = U14ERR_NOERROR;
+ unsigned int ChunkSize;
+ int nPipe = pdx->StagedRead ? 3 : 2; // The pipe number to use for reads or writes
+ if (pdx->nPipes == 3)
+ nPipe--; // Adjust for the 3-pipe case
+ if (nPipe < 0) // and trap case that should never happen
+ return U14ERR_FAIL;
+
+ if (!CanAcceptIoRequests(pdx)) // got sudden remove?
+ {
+ dev_info(&pdx->interface->dev, "%s sudden remove, giving up",
+ __func__);
+ return U14ERR_FAIL; // could do with a better error
+ }
+
+ ChunkSize = (pdx->StagedLength - pdx->StagedDone); // transfer length remaining
+ if (ChunkSize > STAGED_SZ) // make sure to keep legal
+ ChunkSize = STAGED_SZ; // limit to max allowed
+
+ if (!pdx->StagedRead) // if writing...
+ CopyUserSpace(pdx, ChunkSize); // ...copy data into the buffer
+
+ usb_fill_bulk_urb(pdx->pStagedUrb, pdx->udev,
+ pdx->StagedRead ? usb_rcvbulkpipe(pdx->udev,
+ pdx->
+ epAddr[nPipe]) :
+ usb_sndbulkpipe(pdx->udev, pdx->epAddr[nPipe]),
+ pdx->pCoherStagedIO, ChunkSize, staged_callback, pdx);
+ pdx->pStagedUrb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ usb_anchor_urb(pdx->pStagedUrb, &pdx->submitted); // in case we need to kill it
+ iReturn = usb_submit_urb(pdx->pStagedUrb, GFP_ATOMIC);
+ if (iReturn) {
+ usb_unanchor_urb(pdx->pStagedUrb); // kill it
+ pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later
+ dev_err(&pdx->interface->dev, "%s submit urb failed, code %d",
+ __func__, iReturn);
+ } else
+ pdx->bStagedUrbPending = true; // Set the flag for staged URB pending
+ dev_dbg(&pdx->interface->dev, "%s done so far:%d, this size:%d",
+ __func__, pdx->StagedDone, ChunkSize);
+
+ return iReturn;
+}
+
+/***************************************************************************
+** ReadWriteMem
+**
+** This routine is used generally for block read and write operations.
+** Breaks up a read or write in to specified sized chunks, as specified by pipe
+** information on maximum transfer size.
+**
+** Any code that calls this must be holding the stagedLock
+**
+** Arguments:
+** DeviceObject - pointer to our FDO (Functional Device Object)
+** Read - TRUE for read, FALSE for write. This is from POV of the driver
+** wIdent - the transfer area number - defines memory area and more.
+** dwOffs - the start offset within the transfer area of the start of this
+** transfer.
+** dwLen - the number of bytes to transfer.
+*/
+int ReadWriteMem(DEVICE_EXTENSION * pdx, bool Read, unsigned short wIdent,
+ unsigned int dwOffs, unsigned int dwLen)
+{
+ TRANSAREA *pArea = &pdx->rTransDef[wIdent]; // Transfer area info
+
+ if (!CanAcceptIoRequests(pdx)) // Are we in a state to accept new requests?
+ {
+ dev_err(&pdx->interface->dev, "%s can't accept requests",
+ __func__);
+ return U14ERR_FAIL;
+ }
+
+ dev_dbg(&pdx->interface->dev,
+ "%s xfer %d bytes to %s, offset %d, area %d", __func__, dwLen,
+ Read ? "host" : "1401", dwOffs, wIdent);
+
+ // Amazingly, we can get an escape sequence back before the current staged Urb is done, so we
+ // have to check for this situation and, if so, wait until all is OK.
+ if (pdx->bStagedUrbPending) {
+ pdx->bXFerWaiting = true; // Flag we are waiting
+ dev_info(&pdx->interface->dev,
+ "%s xfer is waiting, as previous staged pending",
+ __func__);
+ return U14ERR_NOERROR;
+ }
+
+ if (dwLen == 0) // allow 0-len read or write; just return success
+ {
+ dev_dbg(&pdx->interface->dev,
+ "%s OK; zero-len read/write request", __func__);
+ return U14ERR_NOERROR;
+ }
+
+ if ((pArea->bCircular) && // Circular transfer?
+ (pArea->bCircToHost) && (Read)) // In a supported direction
+ { // If so, we sort out offset ourself
+ bool bWait = false; // Flag for transfer having to wait
+
+ dev_dbg(&pdx->interface->dev,
+ "Circular buffers are %d at %d and %d at %d",
+ pArea->aBlocks[0].dwSize, pArea->aBlocks[0].dwOffset,
+ pArea->aBlocks[1].dwSize, pArea->aBlocks[1].dwOffset);
+ if (pArea->aBlocks[1].dwSize > 0) // Using the second block already?
+ {
+ dwOffs = pArea->aBlocks[1].dwOffset + pArea->aBlocks[1].dwSize; // take offset from that
+ bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset; // Wait if will overwrite block 0?
+ bWait |= (dwOffs + dwLen) > pArea->dwLength; // or if it overflows the buffer
+ } else // Area 1 not in use, try to use area 0
+ {
+ if (pArea->aBlocks[0].dwSize == 0) // Reset block 0 if not in use
+ pArea->aBlocks[0].dwOffset = 0;
+ dwOffs =
+ pArea->aBlocks[0].dwOffset +
+ pArea->aBlocks[0].dwSize;
+ if ((dwOffs + dwLen) > pArea->dwLength) // Off the end of the buffer?
+ {
+ pArea->aBlocks[1].dwOffset = 0; // Set up to use second block
+ dwOffs = 0;
+ bWait = (dwOffs + dwLen) > pArea->aBlocks[0].dwOffset; // Wait if will overwrite block 0?
+ bWait |= (dwOffs + dwLen) > pArea->dwLength; // or if it overflows the buffer
+ }
+ }
+
+ if (bWait) // This transfer will have to wait?
+ {
+ pdx->bXFerWaiting = true; // Flag we are waiting
+ dev_dbg(&pdx->interface->dev,
+ "%s xfer waiting for circular buffer space",
+ __func__);
+ return U14ERR_NOERROR;
+ }
+
+ dev_dbg(&pdx->interface->dev,
+ "%s circular xfer, %d bytes starting at %d", __func__,
+ dwLen, dwOffs);
+ }
+ // Save the parameters for the read\write transfer
+ pdx->StagedRead = Read; // Save the parameters for this read
+ pdx->StagedId = wIdent; // ID allows us to get transfer area info
+ pdx->StagedOffset = dwOffs; // The area within the transfer area
+ pdx->StagedLength = dwLen;
+ pdx->StagedDone = 0; // Initialise the byte count
+ pdx->dwDMAFlag = MODE_LINEAR; // Set DMA mode flag at this point
+ pdx->bXFerWaiting = false; // Clearly not a transfer waiting now
+
+// KeClearEvent(&pdx->StagingDoneEvent); // Clear the transfer done event
+ StageChunk(pdx); // fire off the first chunk
+
+ return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+**
+** ReadChar
+**
+** Reads a character a buffer. If there is no more
+** data we return FALSE. Used as part of decoding a DMA request.
+**
+****************************************************************************/
+static bool ReadChar(unsigned char *pChar, char *pBuf, unsigned int *pdDone,
+ unsigned int dGot)
+{
+ bool bRead = false;
+ unsigned int dDone = *pdDone;
+
+ if (dDone < dGot) // If there is more data
+ {
+ *pChar = (unsigned char)pBuf[dDone]; // Extract the next char
+ dDone++; // Increment the done count
+ *pdDone = dDone;
+ bRead = true; // and flag success
+ }
+
+ return bRead;
+}
+
+#ifdef NOTUSED
+/****************************************************************************
+**
+** ReadWord
+**
+** Reads a word from the 1401, just uses ReadChar twice; passes on any error
+**
+*****************************************************************************/
+static bool ReadWord(unsigned short *pWord, char *pBuf, unsigned int *pdDone,
+ unsigned int dGot)
+{
+ if (ReadChar((unsigned char *)pWord, pBuf, pdDone, dGot))
+ return ReadChar(((unsigned char *)pWord) + 1, pBuf, pdDone,
+ dGot);
+ else
+ return false;
+}
+#endif
+
+/****************************************************************************
+** ReadHuff
+**
+** Reads a coded number in and returns it, Code is:
+** If data is in range 0..127 we recieve 1 byte. If data in range 128-16383
+** we recieve two bytes, top bit of first indicates another on its way. If
+** data in range 16383-4194303 we get three bytes, top two bits of first set
+** to indicate three byte total.
+**
+*****************************************************************************/
+static bool ReadHuff(volatile unsigned int *pDWord, char *pBuf,
+ unsigned int *pdDone, unsigned int dGot)
+{
+ unsigned char ucData; /* for each read to ReadChar */
+ bool bReturn = true; /* assume we will succeed */
+ unsigned int dwData = 0; /* Accumulator for the data */
+
+ if (ReadChar(&ucData, pBuf, pdDone, dGot)) {
+ dwData = ucData; /* copy the data */
+ if ((dwData & 0x00000080) != 0) { /* Bit set for more data ? */
+ dwData &= 0x0000007F; /* Clear the relevant bit */
+ if (ReadChar(&ucData, pBuf, pdDone, dGot)) {
+ dwData = (dwData << 8) | ucData;
+ if ((dwData & 0x00004000) != 0) { /* three byte sequence ? */
+ dwData &= 0x00003FFF; /* Clear the relevant bit */
+ if (ReadChar
+ (&ucData, pBuf, pdDone, dGot))
+ dwData = (dwData << 8) | ucData;
+ else
+ bReturn = false;
+ }
+ } else
+ bReturn = false; /* couldn't read data */
+ }
+ } else
+ bReturn = false;
+
+ *pDWord = dwData; /* return the data */
+ return bReturn;
+}
+
+/***************************************************************************
+**
+** ReadDMAInfo
+**
+** Tries to read info about the dma request from the 1401 and decode it into
+** the dma descriptor block. We have at this point had the escape character
+** from the 1401 and now we must read in the rest of the information about
+** the transfer request. Returns FALSE if 1401 fails to respond or obselete
+** code from 1401 or bad parameters.
+**
+** The pBuf char pointer does not include the initial escape character, so
+** we start handling the data at offset zero.
+**
+*****************************************************************************/
+static bool ReadDMAInfo(volatile DMADESC * pDmaDesc, DEVICE_EXTENSION * pdx,
+ char *pBuf, unsigned int dwCount)
+{
+ bool bResult = false; // assume we won't succeed
+ unsigned char ucData;
+ unsigned int dDone = 0; // We haven't parsed anything so far
+
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ if (ReadChar(&ucData, pBuf, &dDone, dwCount)) {
+ unsigned char ucTransCode = (ucData & 0x0F); // get code for transfer type
+ unsigned short wIdent = ((ucData >> 4) & 0x07); // and area identifier
+
+ // fill in the structure we were given
+ pDmaDesc->wTransType = ucTransCode; // type of transfer
+ pDmaDesc->wIdent = wIdent; // area to use
+ pDmaDesc->dwSize = 0; // initialise other bits
+ pDmaDesc->dwOffset = 0;
+
+ dev_dbg(&pdx->interface->dev, "%s type: %d ident: %d", __func__,
+ pDmaDesc->wTransType, pDmaDesc->wIdent);
+
+ pDmaDesc->bOutWard = (ucTransCode != TM_EXTTOHOST); // set transfer direction
+
+ switch (ucTransCode) {
+ case TM_EXTTOHOST: // Extended linear transfer modes (the only ones!)
+ case TM_EXTTO1401:
+ {
+ bResult =
+ ReadHuff(&(pDmaDesc->dwOffset), pBuf,
+ &dDone, dwCount)
+ && ReadHuff(&(pDmaDesc->dwSize), pBuf,
+ &dDone, dwCount);
+ if (bResult) {
+ dev_dbg(&pdx->interface->dev,
+ "%s xfer offset & size %d %d",
+ __func__, pDmaDesc->dwOffset,
+ pDmaDesc->dwSize);
+
+ if ((wIdent >= MAX_TRANSAREAS) || // Illegal area number, or...
+ (!pdx->rTransDef[wIdent].bUsed) || // area not set up, or...
+ (pDmaDesc->dwOffset > pdx->rTransDef[wIdent].dwLength) || // range/size
+ ((pDmaDesc->dwOffset +
+ pDmaDesc->dwSize) >
+ (pdx->rTransDef[wIdent].
+ dwLength))) {
+ bResult = false; // bad parameter(s)
+ dev_dbg(&pdx->interface->dev,
+ "%s bad param - id %d, bUsed %d, offset %d, size %d, area length %d",
+ __func__, wIdent,
+ pdx->rTransDef[wIdent].
+ bUsed,
+ pDmaDesc->dwOffset,
+ pDmaDesc->dwSize,
+ pdx->rTransDef[wIdent].
+ dwLength);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ } else
+ bResult = false;
+
+ if (!bResult) // now check parameters for validity
+ dev_err(&pdx->interface->dev, "%s error reading Esc sequence",
+ __func__);
+
+ return bResult;
+}
+
+/****************************************************************************
+**
+** Handle1401Esc
+**
+** Deals with an escape sequence coming from the 1401. This can either be
+** a DMA transfer request of various types or a response to an escape sequence
+** sent to the 1401. This is called from a callback.
+**
+** Parameters are
+**
+** dwCount - the number of characters in the device extension char in buffer,
+** this is known to be at least 2 or we will not be called.
+**
+****************************************************************************/
+static int Handle1401Esc(DEVICE_EXTENSION * pdx, char *pCh,
+ unsigned int dwCount)
+{
+ int iReturn = U14ERR_FAIL;
+
+ // I have no idea what this next test is about. '?' is 0x3f, which is area 3, code
+ // 15. At the moment, this is not used, so it does no harm, but unless someone can
+ // tell me what this is for, it should be removed from this and the Windows driver.
+ if (pCh[0] == '?') // Is this an information response
+ { // Parse and save the information
+ } else {
+ spin_lock(&pdx->stagedLock); // Lock others out
+
+ if (ReadDMAInfo(&pdx->rDMAInfo, pdx, pCh, dwCount)) // Get DMA parameters
+ {
+ unsigned short wTransType = pdx->rDMAInfo.wTransType; // check transfer type
+
+ dev_dbg(&pdx->interface->dev,
+ "%s xfer to %s, offset %d, length %d", __func__,
+ pdx->rDMAInfo.bOutWard ? "1401" : "host",
+ pdx->rDMAInfo.dwOffset, pdx->rDMAInfo.dwSize);
+
+ if (pdx->bXFerWaiting) // Check here for badly out of kilter...
+ { // This can never happen, really
+ dev_err(&pdx->interface->dev,
+ "ERROR: DMA setup while transfer still waiting");
+ spin_unlock(&pdx->stagedLock);
+ } else {
+ if ((wTransType == TM_EXTTOHOST)
+ || (wTransType == TM_EXTTO1401)) {
+ iReturn =
+ ReadWriteMem(pdx,
+ !pdx->rDMAInfo.
+ bOutWard,
+ pdx->rDMAInfo.wIdent,
+ pdx->rDMAInfo.dwOffset,
+ pdx->rDMAInfo.dwSize);
+ if (iReturn != U14ERR_NOERROR)
+ dev_err(&pdx->interface->dev,
+ "%s ReadWriteMem() failed %d",
+ __func__, iReturn);
+ } else // This covers non-linear transfer setup
+ dev_err(&pdx->interface->dev,
+ "%s Unknown block xfer type %d",
+ __func__, wTransType);
+ }
+ } else // Failed to read parameters
+ dev_err(&pdx->interface->dev, "%s ReadDMAInfo() fail",
+ __func__);
+
+ spin_unlock(&pdx->stagedLock); // OK here
+ }
+
+ dev_dbg(&pdx->interface->dev, "%s returns %d", __func__, iReturn);
+
+ return iReturn;
+}
+
+/****************************************************************************
+** Callback for the character read complete or error
+****************************************************************************/
+static void ced_readchar_callback(struct urb *pUrb)
+{
+ DEVICE_EXTENSION *pdx = pUrb->context;
+ int nGot = pUrb->actual_length; // what we transferred
+
+ if (pUrb->status) // Do we have a problem to handle?
+ {
+ int nPipe = pdx->nPipes == 4 ? 1 : 0; // The pipe number to use for error
+ // sync/async unlink faults aren't errors... just saying device removed or stopped
+ if (!
+ (pUrb->status == -ENOENT || pUrb->status == -ECONNRESET
+ || pUrb->status == -ESHUTDOWN)) {
+ dev_err(&pdx->interface->dev,
+ "%s - nonzero write bulk status received: %d",
+ __func__, pUrb->status);
+ } else
+ dev_dbg(&pdx->interface->dev,
+ "%s - 0 chars pUrb->status=%d (shutdown?)",
+ __func__, pUrb->status);
+
+ spin_lock(&pdx->err_lock);
+ pdx->errors = pUrb->status;
+ spin_unlock(&pdx->err_lock);
+ nGot = 0; // and tidy up again if so
+
+ spin_lock(&pdx->charInLock); // already at irq level
+ pdx->bPipeError[nPipe] = 1; // Flag an error for later
+ } else {
+ if ((nGot > 1) && ((pdx->pCoherCharIn[0] & 0x7f) == 0x1b)) // Esc sequence?
+ {
+ Handle1401Esc(pdx, &pdx->pCoherCharIn[1], nGot - 1); // handle it
+ spin_lock(&pdx->charInLock); // already at irq level
+ } else {
+ spin_lock(&pdx->charInLock); // already at irq level
+ if (nGot > 0) {
+ unsigned int i;
+ if (nGot < INBUF_SZ) {
+ pdx->pCoherCharIn[nGot] = 0; // tidy the string
+ dev_dbg(&pdx->interface->dev,
+ "%s got %d chars >%s<",
+ __func__, nGot,
+ pdx->pCoherCharIn);
+ }
+ // We know that whatever we read must fit in the input buffer
+ for (i = 0; i < nGot; i++) {
+ pdx->inputBuffer[pdx->dwInBuffPut++] =
+ pdx->pCoherCharIn[i] & 0x7F;
+ if (pdx->dwInBuffPut >= INBUF_SZ)
+ pdx->dwInBuffPut = 0;
+ }
+
+ if ((pdx->dwNumInput + nGot) <= INBUF_SZ)
+ pdx->dwNumInput += nGot; // Adjust the buffer count accordingly
+ } else
+ dev_dbg(&pdx->interface->dev, "%s read ZLP",
+ __func__);
+ }
+ }
+
+ pdx->bReadCharsPending = false; // No longer have a pending read
+ spin_unlock(&pdx->charInLock); // already at irq level
+
+ Allowi(pdx, true); // see if we can do the next one
+}
+
+/****************************************************************************
+** Allowi
+**
+** This is used to make sure that there is always a pending input transfer so
+** we can pick up any inward transfers. This can be called in multiple contexts
+** so we use the irqsave version of the spinlock.
+****************************************************************************/
+int Allowi(DEVICE_EXTENSION * pdx, bool bInCallback)
+{
+ int iReturn = U14ERR_NOERROR;
+ unsigned long flags;
+ spin_lock_irqsave(&pdx->charInLock, flags); // can be called in multiple contexts
+
+ // We don't want char input running while DMA is in progress as we know that this
+ // can cause sequencing problems for the 2270. So don't. It will also allow the
+ // ERR response to get back to the host code too early on some PCs, even if there
+ // is no actual driver failure, so we don't allow this at all.
+ if (!pdx->bInDrawDown && // stop input if
+ !pdx->bReadCharsPending && // If no read request outstanding
+ (pdx->dwNumInput < (INBUF_SZ / 2)) && // and there is some space
+ (pdx->dwDMAFlag == MODE_CHAR) && // not doing any DMA
+ (!pdx->bXFerWaiting) && // no xfer waiting to start
+ (CanAcceptIoRequests(pdx))) // and activity is generally OK
+ { // then off we go
+ unsigned int nMax = INBUF_SZ - pdx->dwNumInput; // max we could read
+ int nPipe = pdx->nPipes == 4 ? 1 : 0; // The pipe number to use
+
+ dev_dbg(&pdx->interface->dev, "%s %d chars in input buffer",
+ __func__, pdx->dwNumInput);
+
+ usb_fill_int_urb(pdx->pUrbCharIn, pdx->udev,
+ usb_rcvintpipe(pdx->udev, pdx->epAddr[nPipe]),
+ pdx->pCoherCharIn, nMax, ced_readchar_callback,
+ pdx, pdx->bInterval);
+ pdx->pUrbCharIn->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; // short xfers are OK by default
+ usb_anchor_urb(pdx->pUrbCharIn, &pdx->submitted); // in case we need to kill it
+ iReturn =
+ usb_submit_urb(pdx->pUrbCharIn,
+ bInCallback ? GFP_ATOMIC : GFP_KERNEL);
+ if (iReturn) {
+ usb_unanchor_urb(pdx->pUrbCharIn); // remove from list of active Urbs
+ pdx->bPipeError[nPipe] = 1; // Flag an error to be handled later
+ dev_err(&pdx->interface->dev,
+ "%s submit urb failed: %d", __func__, iReturn);
+ } else
+ pdx->bReadCharsPending = true; // Flag that we are active here
+ }
+
+ spin_unlock_irqrestore(&pdx->charInLock, flags);
+
+ return iReturn;
+
+}
+
+/*****************************************************************************
+** The ioctl entry point to the driver that is used by us to talk to it.
+** inode The device node (no longer in 3.0.0 kernels)
+** file The file that is open, which holds our pdx pointer
+** ulArg The argument passed in. Note that long is 64-bits in 64-bit system, i.e. it is big
+** enough for a 64-bit pointer.
+*****************************************************************************/
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+static long ced_ioctl(struct file *file, unsigned int cmd, unsigned long ulArg)
+#else
+static int ced_ioctl(struct inode *node, struct file *file, unsigned int cmd,
+ unsigned long ulArg)
+#endif
+{
+ int err = 0;
+ DEVICE_EXTENSION *pdx = file->private_data;
+ if (!CanAcceptIoRequests(pdx)) // check we still exist
+ return -ENODEV;
+
+ // Check that access is allowed, where is is needed. Anything that would have an indeterminate
+ // size will be checked by the specific command.
+ if (_IOC_DIR(cmd) & _IOC_READ) // read from point of view of user...
+ err = !access_ok(VERIFY_WRITE, (void __user *)ulArg, _IOC_SIZE(cmd)); // is kernel write
+ else if (_IOC_DIR(cmd) & _IOC_WRITE) // and write from point of view of user...
+ err = !access_ok(VERIFY_READ, (void __user *)ulArg, _IOC_SIZE(cmd)); // is kernel read
+ if (err)
+ return -EFAULT;
+
+ switch (_IOC_NR(cmd)) {
+ case _IOC_NR(IOCTL_CED_SENDSTRING(0)):
+ return SendString(pdx, (const char __user *)ulArg,
+ _IOC_SIZE(cmd));
+
+ case _IOC_NR(IOCTL_CED_RESET1401):
+ return Reset1401(pdx);
+
+ case _IOC_NR(IOCTL_CED_GETCHAR):
+ return GetChar(pdx);
+
+ case _IOC_NR(IOCTL_CED_SENDCHAR):
+ return SendChar(pdx, (char)ulArg);
+
+ case _IOC_NR(IOCTL_CED_STAT1401):
+ return Stat1401(pdx);
+
+ case _IOC_NR(IOCTL_CED_LINECOUNT):
+ return LineCount(pdx);
+
+ case _IOC_NR(IOCTL_CED_GETSTRING(0)):
+ return GetString(pdx, (char __user *)ulArg, _IOC_SIZE(cmd));
+
+ case _IOC_NR(IOCTL_CED_SETTRANSFER):
+ return SetTransfer(pdx, (TRANSFERDESC __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_UNSETTRANSFER):
+ return UnsetTransfer(pdx, (int)ulArg);
+
+ case _IOC_NR(IOCTL_CED_SETEVENT):
+ return SetEvent(pdx, (TRANSFEREVENT __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_GETOUTBUFSPACE):
+ return GetOutBufSpace(pdx);
+
+ case _IOC_NR(IOCTL_CED_GETBASEADDRESS):
+ return -1;
+
+ case _IOC_NR(IOCTL_CED_GETDRIVERREVISION):
+ return (2 << 24) | (DRIVERMAJREV << 16) | DRIVERMINREV; // USB | MAJOR | MINOR
+
+ case _IOC_NR(IOCTL_CED_GETTRANSFER):
+ return GetTransfer(pdx, (TGET_TX_BLOCK __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_KILLIO1401):
+ return KillIO1401(pdx);
+
+ case _IOC_NR(IOCTL_CED_STATEOF1401):
+ return StateOf1401(pdx);
+
+ case _IOC_NR(IOCTL_CED_GRAB1401):
+ case _IOC_NR(IOCTL_CED_FREE1401):
+ return U14ERR_NOERROR;
+
+ case _IOC_NR(IOCTL_CED_STARTSELFTEST):
+ return StartSelfTest(pdx);
+
+ case _IOC_NR(IOCTL_CED_CHECKSELFTEST):
+ return CheckSelfTest(pdx, (TGET_SELFTEST __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_TYPEOF1401):
+ return TypeOf1401(pdx);
+
+ case _IOC_NR(IOCTL_CED_TRANSFERFLAGS):
+ return TransferFlags(pdx);
+
+ case _IOC_NR(IOCTL_CED_DBGPEEK):
+ return DbgPeek(pdx, (TDBGBLOCK __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_DBGPOKE):
+ return DbgPoke(pdx, (TDBGBLOCK __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_DBGRAMPDATA):
+ return DbgRampData(pdx, (TDBGBLOCK __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_DBGRAMPADDR):
+ return DbgRampAddr(pdx, (TDBGBLOCK __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_DBGGETDATA):
+ return DbgGetData(pdx, (TDBGBLOCK __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_DBGSTOPLOOP):
+ return DbgStopLoop(pdx);
+
+ case _IOC_NR(IOCTL_CED_FULLRESET):
+ pdx->bForceReset = true; // Set a flag for a full reset
+ break;
+
+ case _IOC_NR(IOCTL_CED_SETCIRCULAR):
+ return SetCircular(pdx, (TRANSFERDESC __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_GETCIRCBLOCK):
+ return GetCircBlock(pdx, (TCIRCBLOCK __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_FREECIRCBLOCK):
+ return FreeCircBlock(pdx, (TCIRCBLOCK __user *) ulArg);
+
+ case _IOC_NR(IOCTL_CED_WAITEVENT):
+ return WaitEvent(pdx, (int)(ulArg & 0xff), (int)(ulArg >> 8));
+
+ case _IOC_NR(IOCTL_CED_TESTEVENT):
+ return TestEvent(pdx, (int)ulArg);
+
+ default:
+ return U14ERR_NO_SUCH_FN;
+ }
+ return U14ERR_NOERROR;
+}
+
+static const struct file_operations ced_fops = {
+ .owner = THIS_MODULE,
+ .open = ced_open,
+ .release = ced_release,
+ .flush = ced_flush,
+ .llseek = noop_llseek,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
+ .unlocked_ioctl = ced_ioctl,
+#else
+ .ioctl = ced_ioctl,
+#endif
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with the driver core
+ */
+static struct usb_class_driver ced_class = {
+ .name = "cedusb%d",
+ .fops = &ced_fops,
+ .minor_base = USB_CED_MINOR_BASE,
+};
+
+// Check that the device that matches a 1401 vendor and product ID is OK to use and
+// initialise our DEVICE_EXTENSION.
+static int ced_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ DEVICE_EXTENSION *pdx;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int i, bcdDevice;
+ int retval = -ENOMEM;
+
+ // allocate memory for our device extension and initialize it
+ pdx = kzalloc(sizeof(*pdx), GFP_KERNEL);
+ if (!pdx) {
+ dev_err(&interface->dev, "Out of memory\n");
+ goto error;
+ }
+
+ for (i = 0; i < MAX_TRANSAREAS; ++i) // Initialise the wait queues
+ {
+ init_waitqueue_head(&pdx->rTransDef[i].wqEvent);
+ }
+
+ // Put initialises for our stuff here. Note that all of *pdx is zero, so
+ // no need to explicitly zero it.
+ spin_lock_init(&pdx->charOutLock);
+ spin_lock_init(&pdx->charInLock);
+ spin_lock_init(&pdx->stagedLock);
+
+ // Initialises from the skeleton stuff
+ kref_init(&pdx->kref);
+ mutex_init(&pdx->io_mutex);
+ spin_lock_init(&pdx->err_lock);
+ init_usb_anchor(&pdx->submitted);
+
+ pdx->udev = usb_get_dev(interface_to_usbdev(interface));
+ pdx->interface = interface;
+
+ // Attempt to identify the device
+ bcdDevice = pdx->udev->descriptor.bcdDevice;
+ i = (bcdDevice >> 8);
+ if (i == 0)
+ pdx->s1401Type = TYPEU1401;
+ else if ((i >= 1) && (i <= 23))
+ pdx->s1401Type = i + 2;
+ else {
+ dev_err(&interface->dev, "%s Unknown device. bcdDevice = %d",
+ __func__, bcdDevice);
+ goto error;
+ }
+ // set up the endpoint information. We only care about the number of EP as
+ // we know that we are dealing with a 1401 device.
+ iface_desc = interface->cur_altsetting;
+ pdx->nPipes = iface_desc->desc.bNumEndpoints;
+ dev_info(&interface->dev, "1401Type=%d with %d End Points",
+ pdx->s1401Type, pdx->nPipes);
+ if ((pdx->nPipes < 3) || (pdx->nPipes > 4))
+ goto error;
+
+ // Allocate the URBs we hold for performing transfers
+ pdx->pUrbCharOut = usb_alloc_urb(0, GFP_KERNEL); // character output URB
+ pdx->pUrbCharIn = usb_alloc_urb(0, GFP_KERNEL); // character input URB
+ pdx->pStagedUrb = usb_alloc_urb(0, GFP_KERNEL); // block transfer URB
+ if (!pdx->pUrbCharOut || !pdx->pUrbCharIn || !pdx->pStagedUrb) {
+ dev_err(&interface->dev, "%s URB alloc failed", __func__);
+ goto error;
+ }
+
+ pdx->pCoherStagedIO =
+ usb_alloc_coherent(pdx->udev, STAGED_SZ, GFP_KERNEL,
+ &pdx->pStagedUrb->transfer_dma);
+ pdx->pCoherCharOut =
+ usb_alloc_coherent(pdx->udev, OUTBUF_SZ, GFP_KERNEL,
+ &pdx->pUrbCharOut->transfer_dma);
+ pdx->pCoherCharIn =
+ usb_alloc_coherent(pdx->udev, INBUF_SZ, GFP_KERNEL,
+ &pdx->pUrbCharIn->transfer_dma);
+ if (!pdx->pCoherCharOut || !pdx->pCoherCharIn || !pdx->pCoherStagedIO) {
+ dev_err(&interface->dev, "%s Coherent buffer alloc failed",
+ __func__);
+ goto error;
+ }
+
+ for (i = 0; i < pdx->nPipes; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ pdx->epAddr[i] = endpoint->bEndpointAddress;
+ dev_info(&interface->dev, "Pipe %d, ep address %02x", i,
+ pdx->epAddr[i]);
+ if (((pdx->nPipes == 3) && (i == 0)) || // if char input end point
+ ((pdx->nPipes == 4) && (i == 1))) {
+ pdx->bInterval = endpoint->bInterval; // save the endpoint interrupt interval
+ dev_info(&interface->dev, "Pipe %d, bInterval = %d", i,
+ pdx->bInterval);
+ }
+ // Detect USB2 by checking last ep size (64 if USB1)
+ if (i == pdx->nPipes - 1) // if this is the last ep (bulk)
+ {
+ pdx->bIsUSB2 =
+ le16_to_cpu(endpoint->wMaxPacketSize) > 64;
+ dev_info(&pdx->interface->dev, "USB%d",
+ pdx->bIsUSB2 + 1);
+ }
+ }
+
+ /* save our data pointer in this interface device */
+ usb_set_intfdata(interface, pdx);
+
+ /* we can register the device now, as it is ready */
+ retval = usb_register_dev(interface, &ced_class);
+ if (retval) {
+ /* something prevented us from registering this driver */
+ dev_err(&interface->dev,
+ "Not able to get a minor for this device.\n");
+ usb_set_intfdata(interface, NULL);
+ goto error;
+ }
+
+ /* let the user know what node this device is now attached to */
+ dev_info(&interface->dev,
+ "USB CEDUSB device now attached to cedusb #%d",
+ interface->minor);
+ return 0;
+
+error:
+ if (pdx)
+ kref_put(&pdx->kref, ced_delete); // frees allocated memory
+ return retval;
+}
+
+static void ced_disconnect(struct usb_interface *interface)
+{
+ DEVICE_EXTENSION *pdx = usb_get_intfdata(interface);
+ int minor = interface->minor; // save for message at the end
+ int i;
+
+ usb_set_intfdata(interface, NULL); // remove the pdx from the interface
+ usb_deregister_dev(interface, &ced_class); // give back our minor device number
+
+ mutex_lock(&pdx->io_mutex); // stop more I/O starting while...
+ ced_draw_down(pdx); // ...wait for then kill any io
+ for (i = 0; i < MAX_TRANSAREAS; ++i) {
+ int iErr = ClearArea(pdx, i); // ...release any used memory
+ if (iErr == U14ERR_UNLOCKFAIL)
+ dev_err(&pdx->interface->dev, "%s Area %d was in used",
+ __func__, i);
+ }
+ pdx->interface = NULL; // ...we kill off link to interface
+ mutex_unlock(&pdx->io_mutex);
+
+ usb_kill_anchored_urbs(&pdx->submitted);
+
+ kref_put(&pdx->kref, ced_delete); // decrement our usage count
+
+ dev_info(&interface->dev, "USB cedusb #%d now disconnected", minor);
+}
+
+// Wait for all the urbs we know of to be done with, then kill off any that
+// are left. NBNB we will need to have a mechanism to stop circular xfers
+// from trying to fire off more urbs. We will wait up to 3 seconds for Urbs
+// to be done.
+void ced_draw_down(DEVICE_EXTENSION * pdx)
+{
+ int time;
+ dev_dbg(&pdx->interface->dev, "%s called", __func__);
+
+ pdx->bInDrawDown = true;
+ time = usb_wait_anchor_empty_timeout(&pdx->submitted, 3000);
+ if (!time) // if we timed out we kill the urbs
+ {
+ usb_kill_anchored_urbs(&pdx->submitted);
+ dev_err(&pdx->interface->dev, "%s timed out", __func__);
+ }
+ pdx->bInDrawDown = false;
+}
+
+static int ced_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+ if (!pdx)
+ return 0;
+ ced_draw_down(pdx);
+
+ dev_dbg(&pdx->interface->dev, "%s called", __func__);
+ return 0;
+}
+
+static int ced_resume(struct usb_interface *intf)
+{
+ DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+ if (!pdx)
+ return 0;
+ dev_dbg(&pdx->interface->dev, "%s called", __func__);
+ return 0;
+}
+
+static int ced_pre_reset(struct usb_interface *intf)
+{
+ DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+ mutex_lock(&pdx->io_mutex);
+ ced_draw_down(pdx);
+ return 0;
+}
+
+static int ced_post_reset(struct usb_interface *intf)
+{
+ DEVICE_EXTENSION *pdx = usb_get_intfdata(intf);
+ dev_dbg(&pdx->interface->dev, "%s", __func__);
+
+ /* we are sure no URBs are active - no locking needed */
+ pdx->errors = -EPIPE;
+ mutex_unlock(&pdx->io_mutex);
+
+ return 0;
+}
+
+static struct usb_driver ced_driver = {
+ .name = "cedusb",
+ .probe = ced_probe,
+ .disconnect = ced_disconnect,
+ .suspend = ced_suspend,
+ .resume = ced_resume,
+ .pre_reset = ced_pre_reset,
+ .post_reset = ced_post_reset,
+ .id_table = ced_table,
+ .supports_autosuspend = 1,
+};
+
+module_usb_driver(ced_driver);
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/ced1401/usb1401.h b/drivers/staging/ced1401/usb1401.h
new file mode 100644
index 000000000000..331ca9859829
--- /dev/null
+++ b/drivers/staging/ced1401/usb1401.h
@@ -0,0 +1,249 @@
+/* usb1401.h
+ Header file for the CED 1401 USB device driver for Linux
+ Copyright (C) 2010 Cambridge Electronic Design Ltd
+ Author Greg P Smith (greg@ced.co.uk)
+
+ This program is free software; 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 __USB1401_H__
+#define __USB1401_H__
+#include "use1401.h"
+#include "ced_ioctl.h"
+
+#ifndef UINT
+#define UINT unsigned int
+#endif
+
+/// Device type codes, but these don't need to be extended - a succession is assumed
+/// These are set for usb from the bcdDevice field (suitably mangled). Future devices
+/// will be added in order of device creation to the list, so the names here are just
+/// to help use remember which device is which. The U14ERR_... values follow the same
+/// pattern for modern devices.
+#define TYPEUNKNOWN -1 // dont know
+#define TYPE1401 0 // standard 1401
+#define TYPEPLUS 1 // 1401 plus
+#define TYPEU1401 2 // u1401
+#define TYPEPOWER 3 // Power1401
+#define TYPEU14012 4 // u1401 mkII
+#define TYPEPOWER2 5 // Power1401 mk II
+#define TYPEMICRO3 6 // Micro1401-3
+#define TYPEPOWER3 7 // Power1401-3
+
+/// Some useful defines of constants. DONT FORGET to change the version in the
+/// resources whenever you change it here!.
+#define DRIVERMAJREV 2 // driver revision level major (match windows)
+#define DRIVERMINREV 0 // driver revision level minor
+
+/// Definitions of the various block transfer command codes
+#define TM_EXTTOHOST 8 // extended tohost
+#define TM_EXTTO1401 9 // extended to1401
+
+/// Definitions of values in usbReqtype. Used in sorting out setup actions
+#define H_TO_D 0x00
+#define D_TO_H 0x80
+#define VENDOR 0x40
+#define DEVREQ 0x00
+#define INTREQ 0x01
+#define ENDREQ 0x02
+
+/// Definition of values in usbRequest, again used to sort out setup
+#define GET_STATUS 0x00
+#define CLEAR_FEATURE 0x01
+#define SET_FEATURE 0x03
+#define SET_ADDRESS 0x05
+#define GET_DESC 0x06
+#define SET_DESC 0x07
+#define GET_CONF 0x08
+#define SET_CONF 0x09
+#define GET_INTERFACE 0x0a
+#define SET_INTERFACE 0x0b
+#define SYNCH_FRAME 0x0c
+
+/// Definitions of the various debug command codes understood by the 1401. These
+/// are used in various vendor-specific commands to achieve the desired effect
+#define DB_GRAB 0x50 /* Grab is a NOP for USB */
+#define DB_FREE 0x51 /* Free is a NOP for the USB */
+#define DB_SETADD 0x52 /* Set debug address (double) */
+#define DB_SELFTEST 0x53 /* Start self test */
+#define DB_SETMASK 0x54 /* Set enable mask (double) */
+#define DB_SETDEF 0x55 /* Set default mask (double) */
+#define DB_PEEK 0x56 /* Peek address, save result */
+#define DB_POKE 0x57 /* Poke address with data (double) */
+#define DB_RAMPD 0x58 /* Ramp data at debug address */
+#define DB_RAMPA 0x59 /* Ramp address bus */
+#define DB_REPEATS 0x5A /* Set repeats for operations (double) */
+#define DB_WIDTH 0x5B /* Set width for operations (byte) */
+#define DB_DATA 0x5C /* Get 4-byte data read by PEEK */
+#define DB_CHARS 0x5D /* Send chars via EP0 control write */
+
+#define CR_CHAR 0x0D /* The carriage return character */
+#define CR_CHAR_80 0x8d /* and with bit 7 set */
+
+/// A structure holding information about a block of memory for use in circular transfers
+typedef struct circBlk
+{
+ volatile UINT dwOffset; /* Offset within area of block start */
+ volatile UINT dwSize; /* Size of the block, in bytes (0 = unused) */
+} CIRCBLK;
+
+/// A structure holding all of the information about a transfer area - an area of
+/// memory set up for use either as a source or destination in DMA transfers.
+typedef struct transarea
+{
+ void* lpvBuff; // User address of xfer area saved for completeness
+ UINT dwBaseOffset; // offset to start of xfer area in first page
+ UINT dwLength; // Length of xfer area, in bytes
+ struct page **pPages; // Points at array of locked down pages
+ int nPages; // number of pages that are locked down
+ bool bUsed; // Is this structure in use?
+ bool bCircular; // Is this area for circular transfers?
+ bool bCircToHost; // Flag for direction of circular transfer
+ bool bEventToHost; // Set event on transfer to host?
+ int iWakeUp; // Set 1 on event, cleared by TestEvent()
+ UINT dwEventSt; // Defines section within xfer area for...
+ UINT dwEventSz; // ...notification by the event SZ is 0 if unset
+ CIRCBLK aBlocks[2]; // Info on a pair of circular blocks
+ wait_queue_head_t wqEvent; // The wait queue for events in this area MUST BE LAST
+} TRANSAREA;
+
+/// The DMADESC structure is used to hold information on the transfer in progress. It
+/// is set up by ReadDMAInfo, using information sent by the 1401 in an escape sequence.
+typedef struct dmadesc
+{
+ unsigned short wTransType; /* transfer type as TM_xxx above */
+ unsigned short wIdent; /* identifier word */
+ unsigned int dwSize; /* bytes to transfer */
+ unsigned int dwOffset; /* offset into transfer area for trans */
+ bool bOutWard; /* true when data is going TO 1401 */
+} DMADESC;
+
+#define INBUF_SZ 256 /* input buffer size */
+#define OUTBUF_SZ 256 /* output buffer size */
+#define STAGED_SZ 0x10000 // size of coherent buffer for staged transfers
+
+/// Structure to hold all of our device specific stuff. We are making this as similar as we
+/// can to the Windows driver to help in our understanding of what is going on.
+typedef struct _DEVICE_EXTENSION
+{
+ char inputBuffer[INBUF_SZ]; /* The two buffers */
+ char outputBuffer[OUTBUF_SZ]; /* accessed by the host functions */
+ volatile unsigned int dwNumInput; /* num of chars in input buffer */
+ volatile unsigned int dwInBuffGet; /* where to get from input buffer */
+ volatile unsigned int dwInBuffPut; /* where to put into input buffer */
+ volatile unsigned int dwNumOutput; /* num of chars in output buffer */
+ volatile unsigned int dwOutBuffGet; /* where to get from output buffer*/
+ volatile unsigned int dwOutBuffPut; /* where to put into output buffer*/
+
+ volatile bool bSendCharsPending; /* Flag to indicate sendchar active */
+ volatile bool bReadCharsPending; /* Flag to indicate a read is primed */
+ char* pCoherCharOut; /* special aligned buffer for chars to 1401 */
+ struct urb* pUrbCharOut; /* urb used for chars to 1401 */
+ char* pCoherCharIn; /* special aligned buffer for chars to host */
+ struct urb* pUrbCharIn; /* urb used for chars to host */
+
+ spinlock_t charOutLock; /* to protect the outputBuffer and outputting */
+ spinlock_t charInLock; /* to protect the inputBuffer and char reads */
+ __u8 bInterval; /* Interrupt end point interval */
+
+ volatile unsigned int dwDMAFlag; /* state of DMA */
+ TRANSAREA rTransDef[MAX_TRANSAREAS];/* transfer area info */
+ volatile DMADESC rDMAInfo; // info on current DMA transfer
+ volatile bool bXFerWaiting; // Flag set if DMA transfer stalled
+ volatile bool bInDrawDown; // Flag that we want to halt transfers
+
+ // Parameters relating to a block read\write that is in progress. Some of these values
+ // are equivalent to values in rDMAInfo. The values here are those in use, while those
+ // in rDMAInfo are those recieved from the 1401 via an escape sequence. If another
+ // escape sequence arrives before the previous xfer ends, rDMAInfo values are updated while these
+ // are used to finish off the current transfer.
+ volatile short StagedId; // The transfer area id for this transfer
+ volatile bool StagedRead; // Flag TRUE for read from 1401, FALSE for write
+ volatile unsigned int StagedLength; // Total length of this transfer
+ volatile unsigned int StagedOffset; // Offset within memory area for transfer start
+ volatile unsigned int StagedDone; // Bytes transferred so far
+ volatile bool bStagedUrbPending; // Flag to indicate active
+ char* pCoherStagedIO; // buffer used for block transfers
+ struct urb* pStagedUrb; // The URB to use
+ spinlock_t stagedLock; // protects ReadWriteMem() and circular buffer stuff
+
+ short s1401Type; // type of 1401 attached
+ short sCurrentState; // current error state
+ bool bIsUSB2; // type of the interface we connect to
+ bool bForceReset; // Flag to make sure we get a real reset
+ __u32 statBuf[2]; // buffer for 1401 state info
+
+ unsigned long ulSelfTestTime; // used to timeout self test
+
+ int nPipes; // Should be 3 or 4 depending on 1401 usb chip
+ int bPipeError[4]; // set non-zero if an error on one of the pipe
+ __u8 epAddr[4]; // addresses of the 3/4 end points
+
+ struct usb_device *udev; // the usb device for this device
+ struct usb_interface *interface; // the interface for this device, NULL if removed
+ struct usb_anchor submitted; // in case we need to retract our submissions
+ struct mutex io_mutex; // synchronize I/O with disconnect, one user-mode caller at a time
+
+ int errors; // the last request tanked
+ int open_count; // count the number of openers
+ spinlock_t err_lock; // lock for errors
+ struct kref kref;
+}DEVICE_EXTENSION, *PDEVICE_EXTENSION;
+#define to_DEVICE_EXTENSION(d) container_of(d, DEVICE_EXTENSION, kref)
+
+/// Definitions of routimes used between compilation object files
+// in usb1401.c
+extern int Allowi(DEVICE_EXTENSION* pdx, bool bInCallback);
+extern int SendChars(DEVICE_EXTENSION* pdx);
+extern void ced_draw_down(DEVICE_EXTENSION *pdx);
+extern int ReadWriteMem(DEVICE_EXTENSION *pdx, bool Read, unsigned short wIdent,
+ unsigned int dwOffs, unsigned int dwLen);
+
+// in ced_ioc.c
+extern int ClearArea(DEVICE_EXTENSION *pdx, int nArea);
+extern int SendString(DEVICE_EXTENSION* pdx, const char __user* pData, unsigned int n);
+extern int SendChar(DEVICE_EXTENSION *pdx, char c);
+extern int Get1401State(DEVICE_EXTENSION* pdx, __u32* state, __u32* error);
+extern int ReadWrite_Cancel(DEVICE_EXTENSION *pdx);
+extern bool Is1401(DEVICE_EXTENSION* pdx);
+extern bool QuickCheck(DEVICE_EXTENSION* pdx, bool bTestBuff, bool bCanReset);
+extern int Reset1401(DEVICE_EXTENSION *pdx);
+extern int GetChar(DEVICE_EXTENSION *pdx);
+extern int GetString(DEVICE_EXTENSION *pdx, char __user* pUser, int n);
+extern int SetTransfer(DEVICE_EXTENSION *pdx, TRANSFERDESC __user *pTD);
+extern int UnsetTransfer(DEVICE_EXTENSION *pdx, int nArea);
+extern int SetEvent(DEVICE_EXTENSION *pdx, TRANSFEREVENT __user*pTE);
+extern int Stat1401(DEVICE_EXTENSION *pdx);
+extern int LineCount(DEVICE_EXTENSION *pdx);
+extern int GetOutBufSpace(DEVICE_EXTENSION *pdx);
+extern int GetTransfer(DEVICE_EXTENSION *pdx, TGET_TX_BLOCK __user *pGTB);
+extern int KillIO1401(DEVICE_EXTENSION *pdx);
+extern int BlkTransState(DEVICE_EXTENSION *pdx);
+extern int StateOf1401(DEVICE_EXTENSION *pdx);
+extern int StartSelfTest(DEVICE_EXTENSION *pdx);
+extern int CheckSelfTest(DEVICE_EXTENSION *pdx, TGET_SELFTEST __user *pGST);
+extern int TypeOf1401(DEVICE_EXTENSION *pdx);
+extern int TransferFlags(DEVICE_EXTENSION *pdx);
+extern int DbgPeek(DEVICE_EXTENSION *pdx, TDBGBLOCK __user* pDB);
+extern int DbgPoke(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgRampData(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgRampAddr(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgGetData(DEVICE_EXTENSION *pdx, TDBGBLOCK __user *pDB);
+extern int DbgStopLoop(DEVICE_EXTENSION *pdx);
+extern int SetCircular(DEVICE_EXTENSION *pdx, TRANSFERDESC __user *pTD);
+extern int GetCircBlock(DEVICE_EXTENSION *pdx, TCIRCBLOCK __user* pCB);
+extern int FreeCircBlock(DEVICE_EXTENSION *pdx, TCIRCBLOCK __user* pCB);
+extern int WaitEvent(DEVICE_EXTENSION *pdx, int nArea, int msTimeOut);
+extern int TestEvent(DEVICE_EXTENSION *pdx, int nArea);
+#endif
diff --git a/drivers/staging/ced1401/use1401.h b/drivers/staging/ced1401/use1401.h
new file mode 100644
index 000000000000..86294e21db0c
--- /dev/null
+++ b/drivers/staging/ced1401/use1401.h
@@ -0,0 +1,287 @@
+/****************************************************************************
+** use1401.h
+** Copyright (C) Cambridge Electronic Design Ltd, 1992-2010
+** Authors: Paul Cox, Tim Bergel, Greg Smith
+** See CVS for revisions.
+**
+** Because the size of a long is different between 32-bit and 64-bit on some
+** systems, we avoid this in this interface.
+****************************************************************************/
+#ifndef __USE1401_H__
+#define __USE1401_H__
+#include "machine.h"
+
+// Some definitions to make things compatible. If you want to use Use1401 directly
+// from a Windows program you should define U14_NOT_DLL, in which case you also
+// MUST make sure that your application startup code calls U14InitLib().
+// DLL_USE1401 is defined when you are building the Use1401 dll, not otherwise.
+#ifdef _IS_WINDOWS_
+#ifndef U14_NOT_DLL
+#ifdef DLL_USE1401
+#define U14API(retType) retType DllExport __stdcall
+#else
+#define U14API(retType) retType DllImport __stdcall
+#endif
+#endif
+
+#define U14ERRBASE -500
+#define U14LONG long
+#endif
+
+#ifdef LINUX
+#define U14ERRBASE -1000
+#define U14LONG int
+#endif
+
+#ifdef _QT
+#ifndef U14_NOT_DLL
+#undef U14API
+#define U14API(retType) retType __declspec(dllimport) __stdcall
+#endif
+#undef U14LONG
+#define U14LONG int
+#endif
+
+#ifndef U14API
+#define U14API(retType) retType
+#endif
+
+#ifndef U14LONG
+#define U14LONG long
+#endif
+
+/// Error codes: We need them here as user space can see them.
+#define U14ERR_NOERROR 0 // no problems
+
+/// Device error codes, but these don't need to be extended - a succession is assumed
+#define U14ERR_STD 4 // standard 1401 connected
+#define U14ERR_U1401 5 // u1401 connected
+#define U14ERR_PLUS 6 // 1401 plus connected
+#define U14ERR_POWER 7 // Power1401 connected
+#define U14ERR_U14012 8 // u1401 mkII connected
+#define U14ERR_POWER2 9
+#define U14ERR_U14013 10
+#define U14ERR_POWER3 11
+
+/// NBNB Error numbers need shifting as some linux error codes start at 512
+#define U14ERR(n) (n+U14ERRBASE)
+#define U14ERR_OFF U14ERR(0) /* 1401 there but switched off */
+#define U14ERR_NC U14ERR(-1) /* 1401 not connected */
+#define U14ERR_ILL U14ERR(-2) /* if present it is ill */
+#define U14ERR_NOIF U14ERR(-3) /* I/F card missing */
+#define U14ERR_TIME U14ERR(-4) /* 1401 failed to come ready */
+#define U14ERR_BADSW U14ERR(-5) /* I/F card bad switches */
+#define U14ERR_PTIME U14ERR(-6) /* 1401plus failed to come ready */
+#define U14ERR_NOINT U14ERR(-7) /* couldn't grab the int vector */
+#define U14ERR_INUSE U14ERR(-8) /* 1401 is already in use */
+#define U14ERR_NODMA U14ERR(-9) /* couldn't get DMA channel */
+#define U14ERR_BADHAND U14ERR(-10) /* handle provided was bad */
+#define U14ERR_BAD1401NUM U14ERR(-11) /* 1401 number provided was bad */
+
+#define U14ERR_NO_SUCH_FN U14ERR(-20) /* no such function */
+#define U14ERR_NO_SUCH_SUBFN U14ERR(-21) /* no such sub function */
+#define U14ERR_NOOUT U14ERR(-22) /* no room in output buffer */
+#define U14ERR_NOIN U14ERR(-23) /* no input in buffer */
+#define U14ERR_STRLEN U14ERR(-24) /* string longer than buffer */
+#define U14ERR_ERR_STRLEN U14ERR(-24) /* string longer than buffer */
+#define U14ERR_LOCKFAIL U14ERR(-25) /* failed to lock memory */
+#define U14ERR_UNLOCKFAIL U14ERR(-26) /* failed to unlock memory */
+#define U14ERR_ALREADYSET U14ERR(-27) /* area already set up */
+#define U14ERR_NOTSET U14ERR(-28) /* area not set up */
+#define U14ERR_BADAREA U14ERR(-29) /* illegal area number */
+#define U14ERR_FAIL U14ERR(-30) /* we failed for some other reason*/
+
+#define U14ERR_NOFILE U14ERR(-40) /* command file not found */
+#define U14ERR_READERR U14ERR(-41) /* error reading command file */
+#define U14ERR_UNKNOWN U14ERR(-42) /* unknown command */
+#define U14ERR_HOSTSPACE U14ERR(-43) /* not enough host space to load */
+#define U14ERR_LOCKERR U14ERR(-44) /* could not lock resource/command*/
+#define U14ERR_CLOADERR U14ERR(-45) /* CLOAD command failed */
+
+#define U14ERR_TOXXXERR U14ERR(-60) /* tohost/1401 failed */
+#define U14ERR_NO386ENH U14ERR(-80) /* not 386 enhanced mode */
+#define U14ERR_NO1401DRIV U14ERR(-81) /* no device driver */
+#define U14ERR_DRIVTOOOLD U14ERR(-82) /* device driver too old */
+
+#define U14ERR_TIMEOUT U14ERR(-90) /* timeout occurred */
+
+#define U14ERR_BUFF_SMALL U14ERR(-100) /* buffer for getstring too small */
+#define U14ERR_CBALREADY U14ERR(-101) /* there is already a callback */
+#define U14ERR_BADDEREG U14ERR(-102) /* bad parameter to deregcallback */
+#define U14ERR_NOMEMORY U14ERR(-103) /* no memory for allocation */
+
+#define U14ERR_DRIVCOMMS U14ERR(-110) /* failed talking to driver */
+#define U14ERR_OUTOFMEMORY U14ERR(-111) /* needed memory and couldnt get it*/
+
+/// 1401 type codes.
+#define U14TYPE1401 0 /* standard 1401 */
+#define U14TYPEPLUS 1 /* 1401 plus */
+#define U14TYPEU1401 2 /* u1401 */
+#define U14TYPEPOWER 3 /* power1401 */
+#define U14TYPEU14012 4 /* u1401 mk II */
+#define U14TYPEPOWER2 5 /* power1401 mk II */
+#define U14TYPEU14013 6 /* u1401-3 */
+#define U14TYPEPOWER3 7 /* power1401-3 */
+#define U14TYPEUNKNOWN -1 /* dont know */
+
+/// Transfer flags to allow driver capabilities to be interrogated
+
+/// Constants for transfer flags
+#define U14TF_USEDMA 1 /* Transfer flag for use DMA */
+#define U14TF_MULTIA 2 /* Transfer flag for multi areas */
+#define U14TF_FIFO 4 /* for FIFO interface card */
+#define U14TF_USB2 8 /* for USB2 interface and 1401 */
+#define U14TF_NOTIFY 16 /* for event notifications */
+#define U14TF_SHORT 32 /* for PCI can short cycle */
+#define U14TF_PCI2 64 /* for new PCI card 1401-70 */
+#define U14TF_CIRCTH 128 /* Circular-mode to host */
+#define U14TF_DIAG 256 /* Diagnostics/debug functions */
+#define U14TF_CIRC14 512 /* Circular-mode to 1401 */
+
+/// Definitions of element sizes for DMA transfers - to allow byte-swapping
+#define ESZBYTES 0 /* BYTE element size value */
+#define ESZWORDS 1 /* WORD element size value */
+#define ESZLONGS 2 /* long element size value */
+#define ESZUNKNOWN 0 /* unknown element size value */
+
+/// These define required access types for the debug/diagnostics function
+#define BYTE_SIZE 1 /* 8-bit access */
+#define WORD_SIZE 2 /* 16-bit access */
+#define LONG_SIZE 3 /* 32-bit access */
+
+/// Stuff used by U14_GetTransfer
+#define GET_TX_MAXENTRIES 257 /* (max length / page size + 1) */
+
+#ifdef _IS_WINDOWS_
+#pragma pack(1)
+
+typedef struct /* used for U14_GetTransfer results */
+{ /* Info on a single mapped block */
+ U14LONG physical;
+ U14LONG size;
+} TXENTRY;
+
+typedef struct TGetTxBlock /* used for U14_GetTransfer results */
+{ /* matches structure in VXD */
+ U14LONG size;
+ U14LONG linear;
+ short seg;
+ short reserved;
+ short avail; /* number of available entries */
+ short used; /* number of used entries */
+ TXENTRY entries[GET_TX_MAXENTRIES]; /* Array of mapped block info */
+} TGET_TX_BLOCK;
+
+typedef TGET_TX_BLOCK *LPGET_TX_BLOCK;
+
+#pragma pack()
+#endif
+
+#ifdef LINUX
+typedef struct /* used for U14_GetTransfer results */
+{ /* Info on a single mapped block */
+ long long physical;
+ long size;
+} TXENTRY;
+
+typedef struct TGetTxBlock /* used for U14_GetTransfer results */
+{ /* matches structure in VXD */
+ long long linear; /* linear address */
+ long size; /* total size of the mapped area, holds id when called */
+ short seg; /* segment of the address for Win16 */
+ short reserved;
+ short avail; /* number of available entries */
+ short used; /* number of used entries */
+ TXENTRY entries[GET_TX_MAXENTRIES]; /* Array of mapped block info */
+} TGET_TX_BLOCK;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+U14API(int) U14WhenToTimeOut(short hand); // when to timeout in ms
+U14API(short) U14PassedTime(int iTime); // non-zero if iTime passed
+
+U14API(short) U14LastErrCode(short hand);
+
+U14API(short) U14Open1401(short n1401);
+U14API(short) U14Close1401(short hand);
+U14API(short) U14Reset1401(short hand);
+U14API(short) U14ForceReset(short hand);
+U14API(short) U14TypeOf1401(short hand);
+U14API(short) U14NameOf1401(short hand, char* pBuf, WORD wMax);
+
+U14API(short) U14Stat1401(short hand);
+U14API(short) U14CharCount(short hand);
+U14API(short) U14LineCount(short hand);
+
+U14API(short) U14SendString(short hand, const char* pString);
+U14API(short) U14GetString(short hand, char* pBuffer, WORD wMaxLen);
+U14API(short) U14SendChar(short hand, char cChar);
+U14API(short) U14GetChar(short hand, char* pcChar);
+
+U14API(short) U14LdCmd(short hand, const char* command);
+U14API(DWORD) U14Ld(short hand, const char* vl, const char* str);
+
+U14API(short) U14SetTransArea(short hand, WORD wArea, void *pvBuff,
+ DWORD dwLength, short eSz);
+U14API(short) U14UnSetTransfer(short hand, WORD wArea);
+U14API(short) U14SetTransferEvent(short hand, WORD wArea, BOOL bEvent,
+ BOOL bToHost, DWORD dwStart, DWORD dwLength);
+U14API(int) U14TestTransferEvent(short hand, WORD wArea);
+U14API(int) U14WaitTransferEvent(short hand, WORD wArea, int msTimeOut);
+U14API(short) U14GetTransfer(short hand, TGET_TX_BLOCK *pTransBlock);
+
+U14API(short) U14ToHost(short hand, char* pAddrHost,DWORD dwSize,DWORD dw1401,
+ short eSz);
+U14API(short) U14To1401(short hand, const char* pAddrHost,DWORD dwSize,DWORD dw1401,
+ short eSz);
+
+U14API(short) U14SetCircular(short hand, WORD wArea, BOOL bToHost, void *pvBuff,
+ DWORD dwLength);
+
+U14API(int) U14GetCircBlk(short hand, WORD wArea, DWORD *pdwOffs);
+U14API(int) U14FreeCircBlk(short hand, WORD wArea, DWORD dwOffs, DWORD dwSize,
+ DWORD *pdwOffs);
+
+U14API(short) U14StrToLongs(const char* pszBuff, U14LONG *palNums, short sMaxLongs);
+U14API(short) U14LongsFrom1401(short hand, U14LONG *palBuff, short sMaxLongs);
+
+U14API(void) U14SetTimeout(short hand, int lTimeout);
+U14API(int) U14GetTimeout(short hand);
+U14API(short) U14OutBufSpace(short hand);
+U14API(int) U14BaseAddr1401(short hand);
+U14API(int) U14DriverVersion(short hand);
+U14API(int) U14DriverType(short hand);
+U14API(short) U14DriverName(short hand, char* pBuf, WORD wMax);
+U14API(short) U14GetUserMemorySize(short hand, DWORD *pMemorySize);
+U14API(short) U14KillIO1401(short hand);
+
+U14API(short) U14BlkTransState(short hand);
+U14API(short) U14StateOf1401(short hand);
+
+U14API(short) U14Grab1401(short hand);
+U14API(short) U14Free1401(short hand);
+U14API(short) U14Peek1401(short hand, DWORD dwAddr, int nSize, int nRepeats);
+U14API(short) U14Poke1401(short hand, DWORD dwAddr, DWORD dwValue, int nSize, int nRepeats);
+U14API(short) U14Ramp1401(short hand, DWORD dwAddr, DWORD dwDef, DWORD dwEnable, int nSize, int nRepeats);
+U14API(short) U14RampAddr(short hand, DWORD dwDef, DWORD dwEnable, int nSize, int nRepeats);
+U14API(short) U14StopDebugLoop(short hand);
+U14API(short) U14GetDebugData(short hand, U14LONG *plValue);
+
+U14API(short) U14StartSelfTest(short hand);
+U14API(short) U14CheckSelfTest(short hand, U14LONG *pData);
+U14API(short) U14TransferFlags(short hand);
+U14API(void) U14GetErrorString(short nErr, char* pStr, WORD wMax);
+U14API(int) U14MonitorRev(short hand);
+U14API(void) U14CloseAll(void);
+
+U14API(short) U14WorkingSet(DWORD dwMinKb, DWORD dwMaxKb);
+U14API(int) U14InitLib(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* End of ifndef __USE1401_H__ */
diff --git a/drivers/staging/ced1401/use14_ioc.h b/drivers/staging/ced1401/use14_ioc.h
new file mode 100644
index 000000000000..15ca63888380
--- /dev/null
+++ b/drivers/staging/ced1401/use14_ioc.h
@@ -0,0 +1,301 @@
+/* use14_ioc.h
+** definitions of use1401 module stuff that is shared between use1401 and the driver.
+** Copyright (C) Cambridge Electronic Design Limited 2010
+** Author Greg P Smith
+************************************************************************************/
+#ifndef __USE14_IOC_H__
+#define __USE14_IOC_H__
+
+#define MAX_TRANSAREAS 8 /* The number of transfer areas supported by driver */
+
+#define i386
+#include "winioctl.h" /* needed so we can access driver */
+
+/*
+** Defines for IOCTL functions to ask driver to perform. These must be matched
+** in both use1401 and in the driver. The IOCTL code contains a command
+** identifier, plus other information about the device, the type of access
+** with which the file must have been opened, and the type of buffering.
+** The IOCTL function codes from 0x80 to 0xFF are for developer use.
+*/
+#define FILE_DEVICE_CED1401 0x8001
+#define FNNUMBASE 0x800
+
+#define U14_OPEN1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_CLOSE1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+1, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SENDSTRING CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+2, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_RESET1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+3, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETCHAR CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+4, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SENDCHAR CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+5, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STAT1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+6, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_LINECOUNT CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+7, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETSTRING CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+8, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_REGCALLBACK CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+9, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETMONITORBUF CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+10, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SETTRANSFER CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+11, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_UNSETTRANSFER CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+12, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SETTRANSEVENT CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+13, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETOUTBUFSPACE CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+14, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETBASEADDRESS CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+15, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETDRIVERREVISION CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+16, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETTRANSFER CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+17, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_KILLIO1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+18, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_BLKTRANSSTATE CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+19, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_BYTECOUNT CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+20, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_ZEROBLOCKCOUNT CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+21, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STOPCIRCULAR CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+22, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STATEOF1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+23, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_REGISTERS1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+24, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GRAB1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+25, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_FREE1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+26, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STEP1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+27, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SET1401REGISTERS CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+28, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STEPTILL1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+29, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SETORIN CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+30, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_STARTSELFTEST CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+31, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_CHECKSELFTEST CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+32, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_TYPEOF1401 CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+33, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_TRANSFERFLAGS CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+34, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGPEEK CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+35, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGPOKE CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+36, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGRAMPDATA CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+37, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGRAMPADDR CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+38, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGGETDATA CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+39, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_DBGSTOPLOOP CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+40, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_FULLRESET CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+41, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_SETCIRCULAR CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+42, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_GETCIRCBLK CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+43, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+#define U14_FREECIRCBLK CTL_CODE( FILE_DEVICE_CED1401, \
+ FNNUMBASE+44, \
+ METHOD_BUFFERED, \
+ FILE_ANY_ACCESS)
+
+//--------------- Structures that are shared with the driver -------------
+#pragma pack(1)
+
+typedef struct /* used for get/set standard 1401 registers */
+{
+ short sPC;
+ char A;
+ char X;
+ char Y;
+ char stat;
+ char rubbish;
+} T1401REGISTERS;
+
+typedef union /* to communicate with 1401 driver status & control funcs */
+{
+ char chrs[22];
+ short ints[11];
+ long longs[5];
+ T1401REGISTERS registers;
+} TCSBLOCK;
+
+typedef TCSBLOCK* LPTCSBLOCK;
+
+typedef struct paramBlk
+{
+ short sState;
+ TCSBLOCK csBlock;
+} PARAMBLK;
+
+typedef PARAMBLK* PPARAMBLK;
+
+typedef struct TransferDesc /* Structure and type for SetTransArea */
+{
+ WORD wArea; /* number of transfer area to set up */
+ void FAR * lpvBuff; /* address of transfer area */
+ DWORD dwLength; /* length of area to set up */
+ short eSize; /* size to move (for swapping on MAC) */
+} TRANSFERDESC;
+
+typedef TRANSFERDESC FAR * LPTRANSFERDESC;
+
+/* This is the structure used to set up a transfer area */
+typedef struct VXTransferDesc /* use1401.c and use1432x.x use only */
+{
+ WORD wArea; /* number of transfer area to set up */
+ WORD wAddrSel; /* 16 bit selector for area */
+ DWORD dwAddrOfs; /* 32 bit offset for area start */
+ DWORD dwLength; /* length of area to set up */
+} VXTRANSFERDESC;
+
+#pragma pack()
+
+#endif \ No newline at end of file
diff --git a/drivers/staging/ced1401/userspace/use1401.c b/drivers/staging/ced1401/userspace/use1401.c
new file mode 100644
index 000000000000..d4c63168ea27
--- /dev/null
+++ b/drivers/staging/ced1401/userspace/use1401.c
@@ -0,0 +1,3035 @@
+/****************************************************************************
+** use1401.c
+** Copyright (C) Cambridge Electronic Design Ltd, 1992-2010
+**
+** This program is free software; 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.
+**
+** Contact CED: Cambridge Electronic Design Limited, Science Park, Milton Road
+** Cambridge, CB6 0FE.
+** www.ced.co.uk
+** greg@ced.co.uk
+**
+** Title: USE1401.C
+** Version: 4.00
+** Author: Paul Cox, Tim Bergel, Greg Smith
+**
+** The code was vigorously pruned in DEC 2010 to remove the macintosh options
+** and to get rid of the 16-bit support. It has also been aligned with the
+** Linux version. See CVS for revisions. This will work for Win 9x onwards.
+****************************************************************************
+**
+** Notes on Windows interface to driver
+** ************************************
+**
+** Under Windows 9x and NT, Use1401 uses DeviceIoControl to get access to
+** the 1401 driver. This has parameters for the device handle, the function
+** code, an input pointer and byte count, an output pointer and byte count
+** and a pointer to a DWORD to hold the output byte count. Note that input
+** and output are from the point-of-view of the driver, so the output stuff
+** is used to read values from the 1401, not send to the 1401. The use of
+** these parameters varies with the function in use and the operating
+** system; there are five separate DIOC calls SendString, GetString and
+** SetTransferArea all have their own specialised calls, the rest use the
+** Status1401 or Control1401 functions.
+**
+** There are two basic styles of DIOC call used, one for Win9x VxD drivers
+** and one for NT Kernel-mode and WDM drivers (see below for tables showing
+** the different parameters used. The array bUseNTDIOC[] selects between
+** these two calling styles.
+**
+** Function codes
+** In Win3.x, simple function codes from 0 to 40 were used, shifted left 8
+** bits with a sub-function code in the lower 8 bits. These were also used
+** in the Windows 95 driver, though we had to add 1 to the code value to
+** avoid problems (Open from CreateFile is zero), and the sub-function code
+** is now unused. We found that this gave some problems with Windows 98
+** as the function code values are reserved by microsoft, so we switched to
+** using the NT function codes instead. The NT codes are generated using the
+** CTL_CODE macro, essentially this gives 0x80012000 | (func << 2), where
+** func is the original 0 to 34 value. The driver will handle both types of
+** code and Use1432 only uses the NT codes if it knows the driver is new
+** enough. The array bUseNTCodes[] holds flags on the type of codes required.
+** GPS/TDB Dec 2010: we removed the bUseNTCodes array as this is always true
+** as we no longer support ancient versions.
+**
+** The CreateFile and CloseFile function calls are also handled
+** by DIOC, using the special function codes 0 and -1 respectively.
+**
+** Input pointer and buffer size
+** These are intended for data sent to the device driver. In nearly all cases
+** they are unused in calls to the Win95 driver, the NT driver uses them
+** for all information sent to the driver. The table below shows the pointer
+** and byte count used for the various calls:
+**
+** Win 95 Win NT
+** SendString NULL, 0 pStr, nStr
+** GetString NULL, 0 NULL, 0
+** SetTransferArea pBuf, nBuf (unused?) pDesc, nDesc
+** GetTransfer NULL, 0 NULL, 0
+** Status1401 NULL, 0 NULL, 0
+** Control1401 NULL, 0 pBlk, nBlk
+**
+** pStr and nStr are pointers to a char buffer and the buffer length for
+** string I/O, note that these are temporary buffers owned by the DLL, not
+** application memory, pBuf and nBuf are the transfer area buffer (I think
+** these are unused), pDesc and nDesc are the TRANSFERDESC structure, pBlk
+** and nBlk are the TCSBLOCK structure.
+**
+**
+** Output pointer and buffer size
+** These are intended for data read from the device driver. These are used
+** for almost all information sent to the Win95 driver, the NT driver uses
+** them for information read from the driver, chiefly the error code. The
+** table below shows the pointer and byte count used for the various calls:
+**
+** Win 95 Win NT
+** SendString pStr, nStr pPar, nPar
+** GetString pStr, nStr+2 pStr, nStr+2
+** SetTransferArea pDesc, nDesc pPar, nPar
+** GetTransfer pGet, nGet pGet, nGet
+** Status1401 pBlk, nBlk pPar, nPar
+** Control1401 pBlk, nBlk pPar, nPar
+**
+** pStr and nStr are pointers to a char buffer and the buffer length for
+** string I/O, the +2 for GetString refers to two spare bytes at the start
+** used to hold the string length and returning an error code for NT. Note
+** again that these are (and must be) DLL-owned temporary buffers. pPar
+** and nPar are a PARAM structure used in NT (it holds an error code and a
+** TCSBLOCK structure). pDesc and nDesc are the VXTRANSFERDESC structure,
+** pBlk and nBlk are the TCSBLOCK structure. pGet and nGet indicate the
+** TGET_TX_BLOCK structure used for GetTransfer.
+**
+**
+** The output byte count
+** Both drivers return the output buffer size here, regardless of the actual
+** bytes output. This is used to check that we did get through to the driver.
+**
+** Multiple 1401s
+** **************
+**
+** We have code that tries to support the use of multiple 1401s, but there
+** are problems: The lDriverVersion and lDriverType variables are global, not
+** per-1401 (a particular problem as the U14 functions that use them don't
+** have a hand parameter). In addition, the mechansim for finding a free
+** 1401 depends upon the 1401 device driver open operation failing if it's
+** already in use, which doesn't always happen, particularly with the VxDs.
+** The code in TryToOpen tries to fix this by relying on TYPEOF1401 to detect
+** the 1401-in-use state - the VxDs contain special code to help this. This is
+** working OK but multiple 1401 support works better with the Win2000 drivers.
+**
+** USB driver
+** **********
+**
+** The USB driver, which runs on both Win98 and NT2000, uses the NT-style
+** calling convention, both for the DIOC codes and the DIOC parameters. The
+** TryToOpen function has been altered to look for an NT driver first in
+** the appropriate circumstances, and to set the driver DIOC flags up in
+** the correct state.
+**
+** Adding a new 1401 type - now almost nothing to do
+** *************************************************
+**
+** The 1401 types are defined by a set of U14TYPExxxx codes in USE1401.H.
+** You should add a new one of these to keep things tidy for applications.
+**
+** DRIVERET_MAX (below) specifies the maximum allowed type code from the
+** 1401 driver; I have set this high to accomodate as yet undesigned 1401
+** types. Similarly, as long as the command file names follow the ARM,
+** ARN, ARO sequence, these are calculated by the ExtForType function, so
+** you don't need to do anything here either.
+**
+** Version number
+** **************
+** The new U14InitLib() function returns 0 if the OS is incapable of use,
+** otherwise is returns the version of the USE1401 library. This is done
+** in three parts: Major(31-24).Minor(23-16).Revision.(15-0) (brackets are
+** the bits used). The Major number starts at 2 for the first revision with
+** the U14InitLib() function. Changes to the Major version means that we
+** have broken backwards compatibility. Minor number changes mean that we
+** have added new functionality that does not break backwards compatibility.
+** we starts at 0. Revision changes mean we have fixed something. Each index
+** returns to 0 when a higer one changes.
+*/
+#define U14LIB_MAJOR 4
+#define U14LIB_MINOR 0
+#define U14LIB_REVISION 0
+#define U14LIB_VERSION ((U14LIB_MAJOR<<24) | (U14LIB_MINOR<<16) | U14LIB_REVISION)
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "USE1401.H"
+
+#ifdef _IS_WINDOWS_
+#include <io.h>
+#include <windows.h>
+#pragma warning(disable: 4100) /* Disable "Unused formal parameter" warning */
+#include <assert.h>
+#include "process.h"
+
+
+#define sprintf wsprintf
+#define PATHSEP '\\'
+#define PATHSEPSTR "\\"
+#define DEFCMDPATH "\\1401\\" // default command path if all else fails
+#define MINDRIVERMAJREV 1 // minimum driver revision level we need
+#define __packed // does nothing in Windows
+
+#include "use14_ioc.h" // links to device driver stuff
+#endif
+
+#ifdef LINUX
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sched.h>
+#include <libgen.h>
+#define PATHSEP '/'
+#define PATHSEPSTR "/"
+#define DEFCMDPATH "/var/1401/" // default command path if all else fails
+#define MINDRIVERMAJREV 2 // minimum driver revision level we need
+
+#include "ced_ioctl.h" // links to device driver stuff
+#endif
+
+#define MAX1401 8 // The number of 1401s that can be supported
+
+/*
+** These are the 1401 type codes returned by the driver, they are a slightly
+** odd sequence & start for reasons of compatability with the DOS driver.
+** The maximum code value is the upper limit of 1401 device types.
+*/
+#define DRIVRET_STD 4 // Codes for 1401 types matching driver values
+#define DRIVRET_U1401 5 // This table does not need extending, as
+#define DRIVRET_PLUS 6 // we can calculate values now.
+#define DRIVRET_POWER 7 // but we need all of these values still
+#define DRIVRET_MAX 26 // Maximum tolerated code - future designs
+
+/*
+** These variables store data that will be used to generate the last
+** error string. For now, a string will hold the 1401 command file name.
+*/
+static char szLastName[20]; // additional text information
+
+/*
+** Information stored per handle. NBNB, driverType and DriverVersion used to be
+** only stored once for all handles... i.e. nonsensical. This change means that
+** three U14...() calls now include handles that were previously void. We have
+** set a constructor and a destructor call for the library (see the end) to
+** initialise important structures, or call use1401_load().
+*/
+static short asDriverType[MAX1401] = {0};
+static int lLastDriverVersion = U14ERR_NO1401DRIV;
+static int lLastDriverType = U14TYPEUNKNOWN;
+static int alDriverVersion[MAX1401]; // version/type of each driver
+static int alTimeOutPeriod[MAX1401]; // timeout time in milliseconds
+static short asLastRetCode[MAX1401]; // last code from a fn call
+static short asType1401[MAX1401] = {0}; // The type of the 1401
+static BOOL abGrabbed[MAX1401] = {0}; // Flag for grabbed, set true by grab1401
+static int iAttached = 0; // counts process attaches so can let go
+
+#ifdef _IS_WINDOWS_
+/****************************************************************************
+** Windows NT Specific Variables and internal types
+****************************************************************************/
+static HANDLE aHand1401[MAX1401] = {0}; // handles for 1401s
+static HANDLE aXferEvent[MAX1401] = {0}; // transfer events for the 1401s
+static LPVOID apAreas[MAX1401][MAX_TRANSAREAS]; // Locked areas
+static DWORD auAreas[MAX1401][MAX_TRANSAREAS]; // Size of locked areas
+static BOOL bWindows9x = FALSE; // if we are Windows 95 or better
+#ifdef _WIN64
+#define USE_NT_DIOC(ind) TRUE
+#else
+static BOOL abUseNTDIOC[MAX1401]; // Use NT-style DIOC parameters */
+#define USE_NT_DIOC(ind) abUseNTDIOC[ind]
+#endif
+
+#endif
+
+#ifdef LINUX
+static int aHand1401[MAX1401] = {0}; // handles for 1401s
+#define INVALID_HANDLE_VALUE 0 // to avoid code differences
+#endif
+
+
+/*
+** The CmdHead relates to backwards compatibility with ancient Microsoft (and Sperry!)
+** versions of BASIC, where this header was needed so we could load a command into
+** memory.
+*/
+#pragma pack(1) // pack our structure
+typedef struct CmdHead // defines header block on command
+{ // for PC commands
+ char acBasic[5]; // BASIC information - needed to align things
+ WORD wBasicSz; // size as seen by BASIC
+ WORD wCmdSize; // size of the following info
+} __packed CMDHEAD;
+#pragma pack() // back to normal
+
+/*
+** The rest of the header looks like this...
+** int iRelPnt; relocation pointer... actual start
+** char acName[8]; string holding the command name
+** BYTE bMonRev; monitor revision level
+** BYTE bCmdRev; command revision level
+*/
+
+typedef CMDHEAD *LPCMDHEAD; // pointer to a command header
+
+#define MAXSTRLEN 255 // maximum string length we use
+#define TOHOST FALSE
+#define TO1401 TRUE
+
+static short CheckHandle(short h)
+{
+ if ((h < 0) || (h >= MAX1401)) // must be legal range...
+ return U14ERR_BADHAND;
+ if (aHand1401[h] <= 0) // must be open
+ return U14ERR_BADHAND;
+ return U14ERR_NOERROR;
+}
+
+#ifdef _IS_WINDOWS_
+/****************************************************************************
+** U14Status1401 Used for functions which do not pass any data in but
+** get data back
+****************************************************************************/
+static short U14Status1401(short sHand, LONG lCode, TCSBLOCK* pBlk)
+{
+ DWORD dwBytes = 0;
+
+ if ((sHand < 0) || (sHand >= MAX1401)) /* Check parameters */
+ return U14ERR_BADHAND;
+#ifndef _WIN64
+ if (!USE_NT_DIOC(sHand))
+ { /* Windows 9x DIOC methods? */
+ if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, pBlk,sizeof(TCSBLOCK),&dwBytes,NULL))
+ return (short)((dwBytes>=sizeof(TCSBLOCK)) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS);
+ else
+ return (short)GetLastError();
+ }
+ else
+#endif
+ { /* Windows NT or USB driver */
+ PARAMBLK rWork;
+ rWork.sState = U14ERR_DRIVCOMMS;
+ if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, &rWork,sizeof(PARAMBLK),&dwBytes,NULL) &&
+ (dwBytes >= sizeof(PARAMBLK)))
+ {
+ *pBlk = rWork.csBlock;
+ return rWork.sState;
+ }
+ }
+
+ return U14ERR_DRIVCOMMS;
+}
+
+/****************************************************************************
+** U14Control1401 Used for functions which pass data in and only expect
+** an error code back
+****************************************************************************/
+static short U14Control1401(short sHand, LONG lCode, TCSBLOCK* pBlk)
+{
+ DWORD dwBytes = 0;
+
+ if ((sHand < 0) || (sHand >= MAX1401)) /* Check parameters */
+ return U14ERR_BADHAND;
+
+#ifndef _WIN64
+ if (!USE_NT_DIOC(sHand))
+ { /* Windows 9x DIOC methods */
+ if (DeviceIoControl(aHand1401[sHand], lCode, NULL, 0, pBlk, sizeof(TCSBLOCK), &dwBytes, NULL))
+ return (short)(dwBytes >= sizeof(TCSBLOCK) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS);
+ else
+ return (short)GetLastError();
+ }
+ else
+#endif
+ { /* Windows NT or later */
+ PARAMBLK rWork;
+ rWork.sState = U14ERR_DRIVCOMMS;
+ if (DeviceIoControl(aHand1401[sHand], lCode, pBlk, sizeof(TCSBLOCK), &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
+ (dwBytes >= sizeof(PARAMBLK)))
+ return rWork.sState;
+ }
+
+ return U14ERR_DRIVCOMMS;
+}
+#endif
+
+/****************************************************************************
+** SafeTickCount
+** Gets time in approximately units of a millisecond.
+*****************************************************************************/
+static long SafeTickCount()
+{
+#ifdef _IS_WINDOWS_
+ return GetTickCount();
+#endif
+#ifdef LINUX
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec*1000 + tv.tv_usec/1000);
+#endif
+}
+
+/****************************************************************************
+** A utility routine to get the command file extension for a given type
+** of 1401. We assume the type code is vaguely legal.
+****************************************************************************/
+static int ExtForType(short sType, char* szExt)
+{
+ szExt[0] = 0; /* Default return is a blank string */
+ switch (sType)
+ {
+ case U14TYPE1401: strcpy(szExt, ".CMD"); break; // Standard 1401
+ case U14TYPEPLUS: strcpy(szExt, ".GXC"); break; // 1401 plus
+ default: // All others are in a predictable sequence
+ strcpy(szExt, ".ARM");
+ szExt[3] = (char)('M' + sType - U14TYPEU1401);
+ if (szExt[3] > 'Z') // Wrap round to ARA after ARZ
+ szExt[3] = (char)(szExt[3] - 26);
+ }
+ return 0;
+}
+
+/****************************************************************************
+** U14WhenToTimeOut
+** Returns the time to time out in time units suitable for the machine
+** we are running on ie millsecs for pc/linux, or Mac/
+****************************************************************************/
+U14API(int) U14WhenToTimeOut(short hand)
+{
+ int iNow = SafeTickCount();
+ if ((hand >= 0) && (hand < MAX1401))
+ iNow += alTimeOutPeriod[hand];
+ return iNow;
+}
+
+/****************************************************************************
+** U14PassedTime
+** Returns non zero if the timed passed in has been passed 0 if not
+****************************************************************************/
+U14API(short) U14PassedTime(int lCheckTime)
+{
+ return (short)((SafeTickCount()-lCheckTime) > 0);
+}
+
+/****************************************************************************
+** TranslateString
+** Tidies up string that U14GetString returns. Converts all the commas in a
+** string to spaces. Removes terminating CR character. May do more in future.
+****************************************************************************/
+static void TranslateString(char* pStr)
+{
+ int i = 0;
+ while (pStr[i])
+ {
+ if (pStr[i] == ',')
+ pStr[i] = ' '; /* convert comma to space */
+ ++i;
+ }
+
+ if ((i > 0) && (pStr[i-1] == '\n')) /* kill terminating LF */
+ pStr[i-1] = (char)0;
+}
+
+/****************************************************************************
+** U14StrToLongs
+** Converts a string to an array of longs and returns the number of values
+****************************************************************************/
+U14API(short) U14StrToLongs(const char* pszBuff, U14LONG *palNums, short sMaxLongs)
+{
+ WORD wChInd = 0; // index into source
+ short sLgInd = 0; // index into result longs
+
+ while (pszBuff[wChInd] && // until we get to end of string...
+ (sLgInd < sMaxLongs)) // ...or filled the buffer
+ {
+ // Why not use a C Library converter?
+ switch (pszBuff[wChInd])
+ {
+ case '-':
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ {
+ BOOL bDone = FALSE; // true at end of number
+ int iSign = 1; // sign of number
+ long lValue = 0;
+
+ while ((!bDone) && pszBuff[wChInd])
+ {
+ switch (pszBuff[wChInd])
+ {
+ case '-':
+ iSign = -1; // swap sign
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ lValue *= 10; // move to next digit base 10
+ lValue += ((int)pszBuff[wChInd]-(int)'0');
+ break;
+
+ default: // end of number
+ bDone = TRUE;
+ break;
+ }
+ wChInd++; // move onto next character
+ }
+ palNums[sLgInd] = lValue * iSign;
+ sLgInd++;
+ }
+ break;
+
+ default:
+ wChInd++; // look at next char
+ break;
+ }
+ }
+ return (sLgInd);
+}
+
+
+/****************************************************************************
+** U14LongsFrom1401
+** Gets the next waiting line from the 1401 and converts it longs
+** Returns the number of numbers read or an error.
+****************************************************************************/
+U14API(short) U14LongsFrom1401(short hand, U14LONG *palBuff, short sMaxLongs)
+{
+ char szWork[MAXSTRLEN];
+ short sResult = U14GetString(hand, szWork, MAXSTRLEN);/* get reply from 1401 */
+ if (sResult == U14ERR_NOERROR) /* if no error convert */
+ sResult = U14StrToLongs(szWork, palBuff, sMaxLongs);
+ return sResult;
+}
+
+/****************************************************************************
+** U14CheckErr
+** Sends the ERR command to the 1401 and gets the result. Returns 0, a
+** negative error code, or the first error value.
+****************************************************************************/
+U14API(short) U14CheckErr(short hand)
+{
+ short sResult = U14SendString(hand, ";ERR;");
+ if (sResult == U14ERR_NOERROR)
+ {
+ U14LONG er[3];
+ sResult = U14LongsFrom1401(hand, er, 3);
+ if (sResult > 0)
+ {
+ sResult = (short)er[0]; /* Either zero or an error value */
+#ifdef _DEBUG
+ if (er[0] != 0)
+ {
+ char szMsg[50];
+ sprintf(szMsg, "U14CheckErr returned %d,%d\n", er[0], er[1]);
+ OutputDebugString(szMsg);
+ }
+#endif
+ }
+ else
+ {
+ if (sResult == 0)
+ sResult = U14ERR_TIMEOUT; /* No numbers equals timeout */
+ }
+ }
+
+ return sResult;
+}
+
+/****************************************************************************
+** U14LastErrCode
+** Returns the last code from the driver. This is for Windows where all calls
+** go through the Control and Status routines, so we can save any error.
+****************************************************************************/
+U14API(short) U14LastErrCode(short hand)
+{
+ if ((hand < 0) || (hand >= MAX1401))
+ return U14ERR_BADHAND;
+ return asLastRetCode[hand];
+}
+
+/****************************************************************************
+** U14SetTimeout
+** Set the timeout period for 1401 comms in milliseconds
+****************************************************************************/
+U14API(void) U14SetTimeout(short hand, int lTimeOut)
+{
+ if ((hand < 0) || (hand >= MAX1401))
+ return;
+ alTimeOutPeriod[hand] = lTimeOut;
+}
+
+/****************************************************************************
+** U14GetTimeout
+** Get the timeout period for 1401 comms in milliseconds
+****************************************************************************/
+U14API(int) U14GetTimeout(short hand)
+{
+ if ((hand < 0) || (hand >= MAX1401))
+ return U14ERR_BADHAND;
+ return alTimeOutPeriod[hand];
+}
+
+/****************************************************************************
+** U14OutBufSpace
+** Return the space in the output buffer, or an error.
+****************************************************************************/
+U14API(short) U14OutBufSpace(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_GETOUTBUFSPACE,&csBlock);
+ if (sErr == U14ERR_NOERROR)
+ sErr = csBlock.ints[0];
+ return sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_GetOutBufSpace(aHand1401[hand]) : sErr;
+#endif
+}
+
+
+/****************************************************************************
+** U14BaseAddr1401
+** Returns the 1401 base address or an error code. Meaningless nowadays
+****************************************************************************/
+U14API(int) U14BaseAddr1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ int iError = U14Status1401(hand, U14_GETBASEADDRESS,&csBlock);
+ if (iError == U14ERR_NOERROR)
+ iError = csBlock.longs[0];
+ return iError;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_GetBaseAddress(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14StateOf1401
+** Return error state, either NOERROR or a negative code.
+****************************************************************************/
+U14API(short) U14StateOf1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_STATEOF1401, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ {
+ sErr = csBlock.ints[0]; // returned 1401 state
+ if ((sErr >= DRIVRET_STD) && (sErr <= DRIVRET_MAX))
+ sErr = U14ERR_NOERROR;
+ }
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ sErr = (short)CED_StateOf1401(aHand1401[hand]);
+ if ((sErr >= DRIVRET_STD) && (sErr <= DRIVRET_MAX))
+ sErr = U14ERR_NOERROR;
+ }
+#endif
+ return sErr;
+}
+
+/****************************************************************************
+** U14DriverVersion
+** Returns the driver version. Hi word is major revision, low word is minor.
+** If you pass in a silly handle (like -1), we return the version of the last
+** driver we know of (to cope with PCI and no 1401 attached).
+****************************************************************************/
+U14API(int) U14DriverVersion(short hand)
+{
+ return CheckHandle(hand) != U14ERR_NOERROR ? lLastDriverVersion : alDriverVersion[hand];
+}
+
+/****************************************************************************
+** U14DriverType
+** Returns the driver type. The type, 0=ISA/NU-Bus, 1=PCI, 2=USB, 3=HSS
+** If you pass in a silly handle (like -1), we return the type of the last
+** driver we know of (to cope with PCI and no 1401 attached).
+****************************************************************************/
+U14API(int) U14DriverType(short hand)
+{
+ return CheckHandle(hand) != U14ERR_NOERROR ? lLastDriverType : asDriverType[hand];
+}
+
+/****************************************************************************
+** U14DriverName
+** Returns the driver type as 3 character (ISA, PCI, USB or HSS))
+****************************************************************************/
+U14API(short) U14DriverName(short hand, char* pBuf, WORD wMax)
+{
+ char* pName;
+ *pBuf = 0; // Start off with a blank string
+ switch (U14DriverType(hand)) // Results according to type
+ {
+ case 0: pName = "ISA"; break;
+ case 1: pName = "PCI"; break;
+ case 2: pName = "USB"; break;
+ case 3: pName = "HSS"; break;
+ default: pName = "???"; break;
+ }
+ strncpy(pBuf, pName, wMax); // Copy the correct name to return
+
+ return U14ERR_NOERROR;
+}
+
+/****************************************************************************
+** U14BlkTransState
+** Returns 0 no transfer in progress, 1 transfer in progress or an error code
+****************************************************************************/
+U14API(short) U14BlkTransState(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_BLKTRANSSTATE, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ sErr = csBlock.ints[0];
+ return sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_BlkTransState(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14Grab1401
+** Take control of the 1401 for diagnostics purposes. USB does nothing.
+****************************************************************************/
+U14API(short) U14Grab1401(short hand)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+#ifdef _IS_WINDOWS_
+ if (abGrabbed[hand]) // 1401 should not have been grabbed
+ sErr = U14ERR_ALREADYSET; // Error code defined for this
+ else
+ {
+ TCSBLOCK csBlock;
+ sErr = U14Control1401(hand, U14_GRAB1401, &csBlock);
+ }
+#endif
+#ifdef LINUX
+ // 1401 should not have been grabbed
+ sErr = abGrabbed[hand] ? U14ERR_ALREADYSET : CED_Grab1401(aHand1401[hand]);
+#endif
+ if (sErr == U14ERR_NOERROR)
+ abGrabbed[hand] = TRUE;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14Free1401
+****************************************************************************/
+U14API(short) U14Free1401(short hand)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+#ifdef _IS_WINDOWS_
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+ TCSBLOCK csBlock;
+ sErr = U14Control1401(hand, U14_FREE1401, &csBlock);
+ }
+ else
+ sErr = U14ERR_NOTSET;
+#endif
+#ifdef LINUX
+ // 1401 should not have been grabbed
+ sErr = abGrabbed[hand] ? CED_Free1401(aHand1401[hand]) : U14ERR_NOTSET;
+#endif
+ if (sErr == U14ERR_NOERROR)
+ abGrabbed[hand] = FALSE;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14Peek1401
+** DESCRIPTION Cause the 1401 to do one or more peek operations.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop
+** is called. After the peek is done, use U14GetDebugData to retrieve
+** the results of the peek.
+****************************************************************************/
+U14API(short) U14Peek1401(short hand, DWORD dwAddr, int nSize, int nRepeats)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ csBlock.longs[0] = (long)dwAddr;
+ csBlock.longs[1] = nSize;
+ csBlock.longs[2] = nRepeats;
+ sErr = U14Control1401(hand, U14_DBGPEEK, &csBlock);
+#endif
+#ifdef LINUX
+ TDBGBLOCK dbb;
+ dbb.iAddr = (int)dwAddr;
+ dbb.iWidth = nSize;
+ dbb.iRepeats = nRepeats;
+ sErr = CED_DbgPeek(aHand1401[hand], &dbb);
+#endif
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14Poke1401
+** DESCRIPTION Cause the 1401 to do one or more poke operations.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop
+** is called.
+****************************************************************************/
+U14API(short) U14Poke1401(short hand, DWORD dwAddr, DWORD dwValue,
+ int nSize, int nRepeats)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ csBlock.longs[0] = (long)dwAddr;
+ csBlock.longs[1] = nSize;
+ csBlock.longs[2] = nRepeats;
+ csBlock.longs[3] = (long)dwValue;
+ sErr = U14Control1401(hand, U14_DBGPOKE, &csBlock);
+#endif
+#ifdef LINUX
+ TDBGBLOCK dbb;
+ dbb.iAddr = (int)dwAddr;
+ dbb.iWidth = nSize;
+ dbb.iRepeats= nRepeats;
+ dbb.iData = (int)dwValue;
+ sErr = CED_DbgPoke(aHand1401[hand], &dbb);
+#endif
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14Ramp1401
+** DESCRIPTION Cause the 1401 to loop, writing a ramp to a location.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop.
+****************************************************************************/
+U14API(short) U14Ramp1401(short hand, DWORD dwAddr, DWORD dwDef, DWORD dwEnable,
+ int nSize, int nRepeats)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ csBlock.longs[0] = (long)dwAddr;
+ csBlock.longs[1] = (long)dwDef;
+ csBlock.longs[2] = (long)dwEnable;
+ csBlock.longs[3] = nSize;
+ csBlock.longs[4] = nRepeats;
+ sErr = U14Control1401(hand, U14_DBGRAMPDATA, &csBlock);
+#endif
+#ifdef LINUX
+ TDBGBLOCK dbb;
+ dbb.iAddr = (int)dwAddr;
+ dbb.iDefault = (int)dwDef;
+ dbb.iMask = (int)dwEnable;
+ dbb.iWidth = nSize;
+ dbb.iRepeats = nRepeats;
+ sErr = CED_DbgRampAddr(aHand1401[hand], &dbb);
+#endif
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14RampAddr
+** DESCRIPTION Cause the 1401 to loop, reading from a ramping location.
+** If lRepeats is zero, the loop will continue until U14StopDebugLoop
+****************************************************************************/
+U14API(short) U14RampAddr(short hand, DWORD dwDef, DWORD dwEnable,
+ int nSize, int nRepeats)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ csBlock.longs[0] = (long)dwDef;
+ csBlock.longs[1] = (long)dwEnable;
+ csBlock.longs[2] = nSize;
+ csBlock.longs[3] = nRepeats;
+ sErr = U14Control1401(hand, U14_DBGRAMPADDR, &csBlock);
+#endif
+#ifdef LINUX
+ TDBGBLOCK dbb;
+ dbb.iDefault = (int)dwDef;
+ dbb.iMask = (int)dwEnable;
+ dbb.iWidth = nSize;
+ dbb.iRepeats = nRepeats;
+ sErr = CED_DbgRampAddr(aHand1401[hand], &dbb);
+#endif
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14StopDebugLoop
+** DESCRIPTION Stops a peek\poke\ramp that, with repeats set to zero,
+** will otherwise continue forever.
+****************************************************************************/
+U14API(short) U14StopDebugLoop(short hand)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+#ifdef _IS_WINDOWS_
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+ TCSBLOCK csBlock;
+ sErr = U14Control1401(hand, U14_DBGSTOPLOOP, &csBlock);
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+#endif
+#ifdef LINUX
+ sErr = abGrabbed[hand] ? CED_DbgStopLoop(aHand1401[hand]) : U14ERR_NOTSET;
+#endif
+ return sErr;
+}
+
+/****************************************************************************
+** U14GetDebugData
+** DESCRIPTION Returns the result from a previous peek operation.
+****************************************************************************/
+U14API(short) U14GetDebugData(short hand, U14LONG* plValue)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ if (abGrabbed[hand]) // 1401 should have been grabbed
+ {
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ sErr = U14Status1401(hand, U14_DBGGETDATA, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ *plValue = csBlock.longs[0]; // Return the data
+#endif
+#ifdef LINUX
+ TDBGBLOCK dbb;
+ sErr = CED_DbgGetData(aHand1401[hand], &dbb);
+ if (sErr == U14ERR_NOERROR)
+ *plValue = dbb.iData; /* Return the data */
+#endif
+ }
+ else
+ sErr = U14ERR_NOTSET;
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14StartSelfTest
+****************************************************************************/
+U14API(short) U14StartSelfTest(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ return U14Control1401(hand, U14_STARTSELFTEST, &csBlock);
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_StartSelfTest(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14CheckSelfTest
+****************************************************************************/
+U14API(short) U14CheckSelfTest(short hand, U14LONG *pData)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_CHECKSELFTEST, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ {
+ pData[0] = csBlock.longs[0]; /* Return the results to user */
+ pData[1] = csBlock.longs[1];
+ pData[2] = csBlock.longs[2];
+ }
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR) /* Check parameters */
+ {
+ TGET_SELFTEST gst;
+ sErr = CED_CheckSelfTest(aHand1401[hand], &gst);
+ if (sErr == U14ERR_NOERROR)
+ {
+ pData[0] = gst.code; /* Return the results to user */
+ pData[1] = gst.x;
+ pData[2] = gst.y;
+ }
+ }
+#endif
+ return sErr;
+}
+
+/****************************************************************************
+** U14GetUserMemorySize
+****************************************************************************/
+U14API(short) U14GetUserMemorySize(short hand, DWORD *pMemorySize)
+{
+ // The original 1401 used a different command for getting the size
+ short sErr = U14SendString(hand, (asType1401[hand] == U14TYPE1401) ? "MEMTOP;" : "MEMTOP,?;");
+ *pMemorySize = 0; /* if we get error then leave size set at 0 */
+ if (sErr == U14ERR_NOERROR)
+ {
+ U14LONG alLimits[4];
+ sErr = U14LongsFrom1401(hand, alLimits, 4);
+ if (sErr > 0) /* +ve sErr is the number of values read */
+ {
+ sErr = U14ERR_NOERROR; /* All OK, flag success */
+ if (asType1401[hand] == U14TYPE1401) /* result for standard */
+ *pMemorySize = alLimits[0] - alLimits[1]; /* memtop-membot */
+ else
+ *pMemorySize = alLimits[0]; /* result for plus or u1401 */
+ }
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14TypeOf1401
+** Returns the type of the 1401, maybe unknown
+****************************************************************************/
+U14API(short) U14TypeOf1401(short hand)
+{
+ if ((hand < 0) || (hand >= MAX1401)) /* Check parameters */
+ return U14ERR_BADHAND;
+ else
+ return asType1401[hand];
+}
+
+/****************************************************************************
+** U14NameOf1401
+** Returns the type of the 1401 as a string, blank if unknown
+****************************************************************************/
+U14API(short) U14NameOf1401(short hand, char* pBuf, WORD wMax)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ char* pName;
+ switch (asType1401[hand]) // Results according to type
+ {
+ case U14TYPE1401: pName = "Std 1401"; break;
+ case U14TYPEPLUS: pName = "1401plus"; break;
+ case U14TYPEU1401: pName = "micro1401"; break;
+ case U14TYPEPOWER: pName = "Power1401"; break;
+ case U14TYPEU14012:pName = "Micro1401 mk II"; break;
+ case U14TYPEPOWER2:pName = "Power1401 mk II"; break;
+ case U14TYPEU14013:pName = "Micro1401-3"; break;
+ case U14TYPEPOWER3:pName = "Power1401-3"; break;
+ default: pName = "Unknown";
+ }
+ strncpy(pBuf, pName, wMax);
+ }
+ return sErr;
+}
+
+/****************************************************************************
+** U14TransferFlags
+** Returns the driver block transfer flags.
+** Bits can be set - see U14TF_ constants in use1401.h
+*****************************************************************************/
+U14API(short) U14TransferFlags(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_TRANSFERFLAGS, &csBlock);
+ return (sErr == U14ERR_NOERROR) ? (short)csBlock.ints[0] : sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_TransferFlags(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** GetDriverVersion
+** Actually reads driver version from the device driver.
+** Hi word is major revision, low word is minor revision.
+** Assumes that hand has been checked. Also codes driver type in bits 24 up.
+*****************************************************************************/
+static int GetDriverVersion(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ int iErr = U14Status1401(hand, U14_GETDRIVERREVISION, &csBlock);
+ if (iErr == U14ERR_NOERROR)
+ iErr = csBlock.longs[0];
+ return iErr;
+#endif
+#ifdef LINUX
+ return CED_GetDriverRevision(aHand1401[hand]);
+#endif
+}
+
+/****************************************************************************
+** U14MonitorRev
+** Returns the 1401 monitor revision number.
+** The number returned is the minor revision - the part after the
+** decimal point - plus the major revision times 1000.
+*****************************************************************************/
+U14API(int) U14MonitorRev(short hand)
+{
+ int iRev = 0;
+ int iErr = CheckHandle(hand);
+ if (iErr != U14ERR_NOERROR) // Check open and in use
+ return iErr;
+
+ if (asType1401[hand] >= U14TYPEPOWER2) // The Power2 onwards can give us the monitor
+ { // revision directly for all versions
+ iErr = U14SendString(hand, "INFO,S,28;");
+ if (iErr == U14ERR_NOERROR)
+ {
+ U14LONG lVals[2]; // Read a single number being the revision
+ iErr = U14LongsFrom1401(hand, lVals, 1);
+ if (iErr > 0)
+ {
+ iErr = U14ERR_NOERROR;
+ iRev = lVals[0]; // This is the minor part of the revision
+ iRev += asType1401[hand] * 10000;
+ }
+ }
+ }
+ else
+ { /* Do it the hard way for older hardware */
+ iErr = U14SendString(hand, ";CLIST;"); /* ask for command levels */
+ if (iErr == U14ERR_NOERROR)
+ {
+ while (iErr == U14ERR_NOERROR)
+ {
+ char wstr[50];
+ iErr = U14GetString(hand, wstr, 45);
+ if (iErr == U14ERR_NOERROR)
+ {
+ char *pstr = strstr(wstr,"RESET"); /* Is this the RESET command? */
+ if ((pstr == wstr) && (wstr[5] == ' '))
+ {
+ char *pstr2;
+ size_t l;
+ pstr += 6; /* Move past RESET and followinmg char */
+ l = strlen(pstr); /* The length of text remaining */
+ while (((pstr[l-1] == ' ') || (pstr[l-1] == 13)) && (l > 0))
+ {
+ pstr[l-1] = 0; /* Tidy up string at the end */
+ l--; /* by removing spaces and CRs */
+ }
+ pstr2 = strchr(pstr, '.'); /* Find the decimal point */
+ if (pstr2 != NULL) /* If we found the DP */
+ {
+ *pstr2 = 0; /* End pstr string at DP */
+ pstr2++; /* Now past the decimal point */
+ iRev = atoi(pstr2); /* Get the number after point */
+ }
+ iRev += (atoi(pstr) * 1000); /* Add first bit * 1000 */
+ }
+ if ((strlen(wstr) < 3) && (wstr[0] == ' '))
+ break; /* Spot the last line of results */
+ }
+ }
+ }
+ }
+ if (iErr == U14ERR_NOERROR) /* Return revision if no error */
+ iErr = iRev;
+
+ return iErr;
+}
+
+/****************************************************************************
+** U14TryToOpen Tries to open the 1401 number passed
+** Note : This will succeed with NT driver even if no I/F card or
+** 1401 switched off, so we check state and close the driver
+** if the state is unsatisfactory in U14Open1401.
+****************************************************************************/
+#ifdef _IS_WINDOWS_
+#define U14NAMEOLD "\\\\.\\CED_140%d"
+#define U14NAMENEW "\\\\.\\CED%d"
+static short U14TryToOpen(int n1401, long* plRetVal, short* psHandle)
+{
+ short sErr = U14ERR_NOERROR;
+ HANDLE hDevice = INVALID_HANDLE_VALUE;
+ DWORD dwErr = 0;
+ int nFirst, nLast, nDev = 0; /* Used for the search for a 1401 */
+ BOOL bOldName = FALSE; /* start by looking for a modern driver */
+
+ if (n1401 == 0) /* If we need to look for a 1401 */
+ {
+ nFirst = 1; /* Set the search range */
+ nLast = MAX1401; /* through all the possible 1401s */
+ }
+ else
+ nFirst = nLast = n1401; /* Otherwise just one 1401 */
+
+ while (hDevice == INVALID_HANDLE_VALUE) /* Loop to try for a 1401 */
+ {
+ for (nDev = nFirst; nDev <= nLast; nDev++)
+ {
+ char szDevName[40]; /* name of the device to open */
+ sprintf(szDevName, bOldName ? U14NAMEOLD : U14NAMENEW, nDev);
+ hDevice = CreateFile(szDevName, GENERIC_WRITE | GENERIC_READ,
+ 0, 0, /* Unshared mode does nothing as this is a device */
+ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hDevice != INVALID_HANDLE_VALUE)/* Check 1401 if opened */
+ {
+ TCSBLOCK csBlock;
+ assert(aHand1401[nDev-1] == INVALID_HANDLE_VALUE); // assert if already open
+ aHand1401[nDev-1] = hDevice; /* Save handle for now */
+
+#ifndef _WIN64
+ // Use DIOC method if not windows 9x or if using new device name
+ abUseNTDIOC[nDev-1] = (BOOL)(!bWindows9x || !bOldName);
+#endif
+ sErr = U14Status1401((short)(nDev-1), U14_TYPEOF1401, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ {
+ *plRetVal = csBlock.ints[0];
+ if (csBlock.ints[0] == U14ERR_INUSE)/* Prevent multi opens */
+ {
+ CloseHandle(hDevice); /* treat as open failure */
+ hDevice = INVALID_HANDLE_VALUE;
+ aHand1401[nDev-1] = INVALID_HANDLE_VALUE;
+ sErr = U14ERR_INUSE;
+ }
+ else
+ break; /* Exit from for loop on success */
+ }
+ else
+ {
+ CloseHandle(hDevice); /* Give up if func fails */
+ hDevice = INVALID_HANDLE_VALUE;
+ aHand1401[nDev-1] = INVALID_HANDLE_VALUE;
+ }
+ }
+ else
+ {
+ DWORD dwe = GetLastError(); /* Get error code otherwise */
+ if ((dwe != ERROR_FILE_NOT_FOUND) || (dwErr == 0))
+ dwErr = dwe; /* Ignore repeats of 'not found' */
+ }
+ }
+
+ if ((hDevice == INVALID_HANDLE_VALUE) &&/* No device found, and... */
+ (bWindows9x) && /* ...old names are allowed, and... */
+ (bOldName == FALSE)) /* ...not tried old names yet */
+ bOldName = TRUE; /* Set flag and go round again */
+ else
+ break; /* otherwise that's all folks */
+ }
+
+ if (hDevice != INVALID_HANDLE_VALUE) /* If we got our device open */
+ *psHandle = (short)(nDev-1); /* return 1401 number opened */
+ else
+ {
+ if (dwErr == ERROR_FILE_NOT_FOUND) /* Sort out the error codes */
+ sErr = U14ERR_NO1401DRIV; /* if file not found */
+ else if (dwErr == ERROR_NOT_SUPPORTED)
+ sErr = U14ERR_DRIVTOOOLD; /* if DIOC not supported */
+ else if (dwErr == ERROR_ACCESS_DENIED)
+ sErr = U14ERR_INUSE;
+ else
+ sErr = U14ERR_DRIVCOMMS; /* otherwise assume comms problem */
+ }
+ return sErr;
+}
+#endif
+#ifdef LINUX
+static short U14TryToOpen(int n1401, long* plRetVal, short* psHandle)
+{
+ short sErr = U14ERR_NOERROR;
+ int fh = 0; // will be 1401 handle
+ int iErr = 0;
+ int nFirst, nLast, nDev = 0; // Used for the search for a 1401
+
+ if (n1401 == 0) // If we need to look for a 1401
+ {
+ nFirst = 1; /* Set the search range */
+ nLast = MAX1401; /* through all the possible 1401s */
+ }
+ else
+ nFirst = nLast = n1401; /* Otherwise just one 1401 */
+
+ for (nDev = nFirst; nDev <= nLast; nDev++)
+ {
+ char szDevName[40]; // name of the device to open
+ sprintf(szDevName,"/dev/cedusb/%d", nDev-1);
+ fh = open(szDevName, O_RDWR); // can only be opened once at a time
+ if (fh > 0) // Check 1401 if opened
+ {
+ int iType1401 = CED_TypeOf1401(fh); // get 1401 type
+ aHand1401[nDev-1] = fh; // Save handle for now
+ if (iType1401 >= 0)
+ {
+ *plRetVal = iType1401;
+ break; // Exit from for loop on success
+ }
+ else
+ {
+ close(fh); // Give up if func fails
+ fh = 0;
+ aHand1401[nDev-1] = 0;
+ }
+ }
+ else
+ {
+ if (((errno != ENODEV) && (errno != ENOENT)) || (iErr == 0))
+ iErr = errno; // Ignore repeats of 'not found'
+ }
+ }
+
+
+ if (fh) // If we got our device open
+ *psHandle = (short)(nDev-1); // return 1401 number opened
+ else
+ {
+ if ((iErr == ENODEV) || (iErr == ENOENT)) // Sort out the error codes
+ sErr = U14ERR_NO1401DRIV; // if file not found
+ else if (iErr == EBUSY)
+ sErr = U14ERR_INUSE;
+ else
+ sErr = U14ERR_DRIVCOMMS; // otherwise assume comms problem
+ }
+
+ return sErr;
+}
+#endif
+/****************************************************************************
+** U14Open1401
+** Tries to get the 1401 for use by this application
+*****************************************************************************/
+U14API(short) U14Open1401(short n1401)
+{
+ long lRetVal = -1;
+ short sErr;
+ short hand = 0;
+
+ if ((n1401 < 0) || (n1401 > MAX1401)) // must check the 1401 number
+ return U14ERR_BAD1401NUM;
+
+ szLastName[0] = 0; /* initialise the error info string */
+
+ sErr = U14TryToOpen(n1401, &lRetVal, &hand);
+ if (sErr == U14ERR_NOERROR)
+ {
+ long lDriverVersion = GetDriverVersion(hand); /* get driver revision */
+ long lDriverRev = -1;
+ if (lDriverVersion >= 0) /* can use it if all OK */
+ {
+ lLastDriverType = (lDriverVersion >> 24) & 0x000000FF;
+ asDriverType[hand] = (short)lLastDriverType; /* Drv type */
+ lLastDriverVersion = lDriverVersion & 0x00FFFFFF;
+ alDriverVersion[hand] = lLastDriverVersion; /* Actual version */
+ lDriverRev = ((lDriverVersion>>16) & 0x00FF); /* use hi word */
+ }
+ else
+ {
+ U14Close1401(hand); /* If there is a problem we should close */
+ return (short)lDriverVersion; /* and return the error code */
+ }
+
+ if (lDriverRev < MINDRIVERMAJREV) /* late enough version? */
+ {
+ U14Close1401(hand); /* If there is a problem we should close */
+ return U14ERR_DRIVTOOOLD; /* too old */
+ }
+
+ asLastRetCode[hand] = U14ERR_NOERROR; /* Initialise this 1401s info */
+ abGrabbed[hand] = FALSE; /* we are not in single step mode */
+ U14SetTimeout(hand, 3000); /* set 3 seconds as default timeout */
+
+ switch (lRetVal)
+ {
+ case DRIVRET_STD: asType1401[hand] = U14TYPE1401; break; /* Some we do by hand */
+ case DRIVRET_U1401:asType1401[hand] = U14TYPEU1401; break;
+ case DRIVRET_PLUS: asType1401[hand] = U14TYPEPLUS; break;
+ default: // For the power upwards, we can calculate the codes
+ if ((lRetVal >= DRIVRET_POWER) && (lRetVal <= DRIVRET_MAX))
+ asType1401[hand] = (short)(lRetVal - (DRIVRET_POWER - U14TYPEPOWER));
+ else
+ asType1401[hand] = U14TYPEUNKNOWN;
+ break;
+ }
+ U14KillIO1401(hand); /* resets the 1401 buffers */
+
+ if (asType1401[hand] != U14TYPEUNKNOWN) /* If all seems OK so far */
+ {
+ sErr = U14CheckErr(hand); /* we can check 1401 comms now */
+ if (sErr != 0) /* If this failed to go OK */
+ U14Reset1401(hand); /* Reset the 1401 to try to sort it out */
+ }
+
+ sErr = U14StateOf1401(hand);/* Get the state of the 1401 for return */
+ if (sErr == U14ERR_NOERROR)
+ sErr = hand; /* return the handle if no problem */
+ else
+ U14Close1401(hand); /* If there is a problem we should close */
+ }
+
+ return sErr;
+}
+
+
+/****************************************************************************
+** U14Close1401
+** Closes the 1401 so someone else can use it.
+****************************************************************************/
+U14API(short) U14Close1401(short hand)
+{
+ int j;
+ int iAreaMask = 0; // Mask for active areas
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR) // Check open and in use
+ return sErr;
+
+ for (j = 0; j<MAX_TRANSAREAS; ++j)
+ {
+ TGET_TX_BLOCK gtb;
+ int iReturn = U14GetTransfer(hand, &gtb); // get area information
+ if (iReturn == U14ERR_NOERROR) // ignore if any problem
+ if (gtb.used)
+ iAreaMask |= (1 << j); // set a bit for each used area
+ }
+
+ if (iAreaMask) // if any areas are in use
+ {
+ U14Reset1401(hand); // in case an active transfer running
+ for (j = 0; j < MAX_TRANSAREAS; ++j) // Locate locked areas
+ if (iAreaMask & (1 << j)) // And kill off any transfers
+ U14UnSetTransfer(hand, (WORD)j);
+ }
+
+#ifdef _IS_WINDOWS_
+ if (aXferEvent[hand]) // if this 1401 has an open event handle
+ {
+ CloseHandle(aXferEvent[hand]); // close down the handle
+ aXferEvent[hand] = NULL; // and mark it as gone
+ }
+
+ if (CloseHandle(aHand1401[hand]))
+#endif
+#ifdef LINUX
+ if (close(aHand1401[hand]) == 0) // make sure that close works
+#endif
+ {
+ aHand1401[hand] = INVALID_HANDLE_VALUE;
+ asType1401[hand] = U14TYPEUNKNOWN;
+ return U14ERR_NOERROR;
+ }
+ else
+ return U14ERR_BADHAND; /* BUGBUG GetLastError() ? */
+}
+
+/**************************************************************************
+**
+** Look for open 1401s and attempt to close them down. 32-bit windows only.
+**************************************************************************/
+U14API(void) U14CloseAll(void)
+{
+ int i;
+ for (i = 0; i < MAX1401; i++) // Tidy up and make safe
+ if (aHand1401[i] != INVALID_HANDLE_VALUE)
+ U14Close1401((short)i); // Last ditch close 1401
+}
+
+/****************************************************************************
+** U14Reset1401
+** Resets the 1401
+****************************************************************************/
+U14API(short) U14Reset1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ return U14Control1401(hand, U14_RESET1401, &csBlock);
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_Reset1401(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14ForceReset
+** Sets the 1401 full reset flag, so that next call to Reset1401 will
+** always cause a genuine reset.
+*****************************************************************************/
+U14API(short) U14ForceReset(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ return U14Control1401(hand, U14_FULLRESET, &csBlock);
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_FullReset(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14KillIO1401
+** Removes any pending IO from the buffers.
+*****************************************************************************/
+U14API(short) U14KillIO1401(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ return U14Control1401(hand, U14_KILLIO1401, &csBlock);
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_KillIO1401(aHand1401[hand]) : sErr;
+#endif
+}
+
+
+/****************************************************************************
+** U14SendString
+** Send characters to the 1401
+*****************************************************************************/
+U14API(short) U14SendString(short hand, const char* pString)
+{
+ int nChars; // length we are sending
+ long lTimeOutTicks; // when to time out
+ BOOL bSpaceToSend; // space to send yet
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+
+ nChars = (int)strlen(pString); // get string length we want to send
+ if (nChars > MAXSTRLEN)
+ return U14ERR_STRLEN; // String too long
+
+#ifdef _IS_WINDOWS_
+ // To get here we must wait for the buffer to have some space
+ lTimeOutTicks = U14WhenToTimeOut(hand);
+ do
+ {
+ bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
+ }
+ while (!bSpaceToSend && !U14PassedTime(lTimeOutTicks));
+
+ if (!bSpaceToSend) /* Last-ditch attempt to avoid timeout */
+ { /* This can happen with anti-virus or network activity! */
+ int i;
+ for (i = 0; (i < 4) && (!bSpaceToSend); ++i)
+ {
+ Sleep(25); /* Give other threads a chance for a while */
+ bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
+ }
+ }
+
+ if (asLastRetCode[hand] == U14ERR_NOERROR) /* no errors? */
+ {
+ if (bSpaceToSend)
+ {
+ PARAMBLK rData;
+ DWORD dwBytes;
+ char tstr[MAXSTRLEN+5]; /* Buffer for chars */
+
+ if ((hand < 0) || (hand >= MAX1401))
+ sErr = U14ERR_BADHAND;
+ else
+ {
+ strcpy(tstr, pString); /* Into local buf */
+#ifndef _WIN64
+ if (!USE_NT_DIOC(hand)) /* Using WIN 95 driver access? */
+ {
+ int iOK = DeviceIoControl(aHand1401[hand], (DWORD)U14_SENDSTRING,
+ NULL, 0, tstr, nChars,
+ &dwBytes, NULL);
+ if (iOK)
+ sErr = (dwBytes >= (DWORD)nChars) ? U14ERR_NOERROR : U14ERR_DRIVCOMMS;
+ else
+ sErr = (short)GetLastError();
+ }
+ else
+#endif
+ {
+ int iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_SENDSTRING,
+ tstr, nChars,
+ &rData,sizeof(PARAMBLK),&dwBytes,NULL);
+ if (iOK && (dwBytes >= sizeof(PARAMBLK)))
+ sErr = rData.sState;
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+
+ if (sErr != U14ERR_NOERROR) // If we have had a comms error
+ U14ForceReset(hand); // make sure we get real reset
+ }
+
+ return sErr;
+
+ }
+ else
+ {
+ U14ForceReset(hand); // make sure we get real reset
+ return U14ERR_TIMEOUT;
+ }
+ }
+ else
+ return asLastRetCode[hand];
+#endif
+#ifdef LINUX
+ // Just try to send it and see what happens!
+ sErr = CED_SendString(aHand1401[hand], pString, nChars);
+ if (sErr != U14ERR_NOOUT) // if any result except "no room in output"...
+ {
+ if (sErr != U14ERR_NOERROR) // if a problem...
+ U14ForceReset(hand); // ...make sure we get real reset next time
+ return sErr; // ... we are done as nothing we can do
+ }
+
+ // To get here we must wait for the buffer to have some space
+ lTimeOutTicks = U14WhenToTimeOut(hand);
+ do
+ {
+ bSpaceToSend = (BOOL)((long)U14OutBufSpace(hand) >= nChars);
+ if (!bSpaceToSend)
+ sched_yield(); // let others have fun while we wait
+ }
+ while (!bSpaceToSend && !U14PassedTime(lTimeOutTicks));
+
+ if (asLastRetCode[hand] == U14ERR_NOERROR) /* no errors? */
+ {
+ if (bSpaceToSend)
+ {
+ sErr = CED_SendString(aHand1401[hand], pString, nChars);
+ if (sErr != U14ERR_NOERROR) // If we have had a comms error
+ U14ForceReset(hand); // make sure we get real reset
+ return sErr;
+ }
+ else
+ {
+ U14ForceReset(hand); // make sure we get real reset
+ return U14ERR_TIMEOUT;
+ }
+ }
+ else
+ return asLastRetCode[hand];
+#endif
+}
+
+/****************************************************************************
+** U14SendChar
+** Send character to the 1401
+*****************************************************************************/
+U14API(short) U14SendChar(short hand, char cChar)
+{
+#ifdef _IS_WINDOWS_
+ char sz[2]=" "; // convert to a string and send
+ sz[0] = cChar;
+ sz[1] = 0;
+ return(U14SendString(hand, sz)); // String routines are better
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_SendChar(aHand1401[hand], cChar) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14GetString
+** Get a string from the 1401. Returns a null terminated string.
+** The string is all the characters up to the next CR in the buffer
+** or the end of the buffer if that comes first. This only returns text
+** if there is a CR in the buffer. The terminating CR character is removed.
+** wMaxLen Is the size of the buffer and must be at least 2 or an error.
+** Returns U14ERR_NOERR if OK with the result in the string or a negative
+** error code. Any error from the device causes us to set up for
+** a full reset.
+****************************************************************************/
+U14API(short) U14GetString(short hand, char* pBuffer, WORD wMaxLen)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR) // If an error...
+ return sErr; // ...bail out!
+
+#ifdef _IS_WINDOWS_
+ if (wMaxLen>1) // we need space for terminating 0
+ {
+ BOOL bLineToGet; // true when a line to get
+ long lTimeOutTicks = U14WhenToTimeOut(hand);
+ do
+ bLineToGet = (BOOL)(U14LineCount(hand) != 0);
+ while (!bLineToGet && !U14PassedTime(lTimeOutTicks));
+
+ if (!bLineToGet) /* Last-ditch attempt to avoid timeout */
+ { /* This can happen with anti-virus or network activity! */
+ int i;
+ for (i = 0; (i < 4) && (!bLineToGet); ++i)
+ {
+ Sleep(25); /* Give other threads a chance for a while */
+ bLineToGet = (BOOL)(U14LineCount(hand) != 0);
+ }
+ }
+
+ if (bLineToGet)
+ {
+ if (asLastRetCode[hand] == U14ERR_NOERROR) /* all ok so far */
+ {
+ DWORD dwBytes = 0;
+ *((WORD *)pBuffer) = wMaxLen; /* set up length */
+#ifndef _WIN64
+ if (!USE_NT_DIOC(hand)) /* Win 95 DIOC here ? */
+ {
+ char tstr[MAXSTRLEN+5]; /* Buffer for Win95 chars */
+ int iOK;
+
+ if (wMaxLen > MAXSTRLEN) /* Truncate length */
+ wMaxLen = MAXSTRLEN;
+
+ *((WORD *)tstr) = wMaxLen; /* set len */
+
+ iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_GETSTRING,
+ NULL, 0, tstr, wMaxLen+sizeof(short),
+ &dwBytes, NULL);
+ if (iOK) /* Device IO control OK ? */
+ {
+ if (dwBytes >= 0) /* If driver OK */
+ {
+ strcpy(pBuffer, tstr);
+ sErr = U14ERR_NOERROR;
+ }
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+ else
+ {
+ sErr = (short)GetLastError();
+ if (sErr > 0) /* Errors are -ve */
+ sErr = (short)-sErr;
+ }
+ }
+ else
+#endif
+ { /* Here for NT, the DLL must own the buffer */
+ HANDLE hMem = GlobalAlloc(GMEM_MOVEABLE,wMaxLen+sizeof(short));
+ if (hMem)
+ {
+ char* pMem = (char*)GlobalLock(hMem);
+ if (pMem)
+ {
+ int iOK = DeviceIoControl(aHand1401[hand],(DWORD)U14_GETSTRING,
+ NULL, 0, pMem, wMaxLen+sizeof(short),
+ &dwBytes, NULL);
+ if (iOK) /* Device IO control OK ? */
+ {
+ if (dwBytes >= wMaxLen)
+ {
+ strcpy(pBuffer, pMem+sizeof(short));
+ sErr = *((SHORT*)pMem);
+ }
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+ else
+ sErr = U14ERR_DRIVCOMMS;
+
+ GlobalUnlock(hMem);
+ }
+ else
+ sErr = U14ERR_OUTOFMEMORY;
+
+ GlobalFree(hMem);
+ }
+ else
+ sErr = U14ERR_OUTOFMEMORY;
+ }
+
+ if (sErr == U14ERR_NOERROR) // If all OK...
+ TranslateString(pBuffer); // ...convert any commas to spaces
+ else // If we have had a comms error...
+ U14ForceReset(hand); // ...make sure we get real reset
+
+ }
+ else
+ sErr = asLastRetCode[hand];
+ }
+ else
+ {
+ sErr = U14ERR_TIMEOUT;
+ U14ForceReset(hand); // make sure we get real reset
+ }
+ }
+ else
+ sErr = U14ERR_BUFF_SMALL;
+ return sErr;
+#endif
+#ifdef LINUX
+ if (wMaxLen>1) // we need space for terminating 0
+ {
+ BOOL bLineToGet; // true when a line to get
+ long lTimeOutTicks = U14WhenToTimeOut(hand);
+ do
+ {
+ bLineToGet = (BOOL)(U14LineCount(hand) != 0);
+ if (!bLineToGet)
+ sched_yield();
+
+ }
+ while (!bLineToGet && !U14PassedTime(lTimeOutTicks));
+
+ if (bLineToGet)
+ {
+ sErr = CED_GetString(aHand1401[hand], pBuffer, wMaxLen-1); // space for terminator
+ if (sErr >=0) // if we were OK...
+ {
+ if (sErr >= wMaxLen) // this should NOT happen unless
+ sErr = U14ERR_DRIVCOMMS; // ...driver Comms are very bad
+ else
+ {
+ pBuffer[sErr] = 0; // OK, so terminate the string...
+ TranslateString(pBuffer); // ...and convert commas to spaces.
+ }
+ }
+
+ if (sErr < U14ERR_NOERROR) // If we have had a comms error
+ U14ForceReset(hand); // make sure we get real reset
+ }
+ else
+ {
+ sErr = U14ERR_TIMEOUT;
+ U14ForceReset(hand); // make sure we get real reset
+ }
+ }
+ else
+ sErr = U14ERR_BUFF_SMALL;
+
+ return sErr >= U14ERR_NOERROR ? U14ERR_NOERROR : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14GetChar
+** Get a character from the 1401. CR returned as CR.
+*****************************************************************************/
+U14API(short) U14GetChar(short hand, char* pcChar)
+{
+#ifdef _IS_WINDOWS_
+ char sz[2]; // read a very short string
+ short sErr = U14GetString(hand, sz, 2); // read one char and nul terminate it
+ *pcChar = sz[0]; // copy to result, NB char translate done by GetString
+ if (sErr == U14ERR_NOERROR)
+ { // undo translate of CR to zero
+ if (*pcChar == '\0') // by converting back
+ *pcChar = '\n'; // What a nasty thing to have to do
+ }
+ return sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR) // Check parameters
+ return sErr;
+ sErr = CED_GetChar(aHand1401[hand]); // get one char, if available
+ if (sErr >= 0)
+ {
+ *pcChar = (char)sErr; // return if it we have one
+ return U14ERR_NOERROR; // say all OK
+ }
+ else
+ return sErr;
+#endif
+}
+
+/****************************************************************************
+** U14Stat1401
+** Returns 0 for no lines or error or non zero for something waiting
+****************************************************************************/
+U14API(short) U14Stat1401(short hand)
+{
+ return ((short)(U14LineCount(hand) > 0));
+}
+
+/****************************************************************************
+** U14CharCount
+** Returns the number of characters in the input buffer
+*****************************************************************************/
+U14API(short) U14CharCount(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_STAT1401, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ sErr = csBlock.ints[0];
+ return sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_Stat1401(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14LineCount
+** Returns the number of CR characters in the input buffer
+*****************************************************************************/
+U14API(short) U14LineCount(short hand)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14Status1401(hand, U14_LINECOUNT, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ sErr = csBlock.ints[0];
+ return sErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_LineCount(aHand1401[hand]) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14GetErrorString
+** Converts error code supplied to a decent descriptive string.
+** NOTE: This function may use some extra information stored
+** internally in the DLL. This information is stored on a
+** per-process basis, but it might be altered if you call
+** other functions after getting an error and before using
+** this function.
+****************************************************************************/
+U14API(void) U14GetErrorString(short nErr, char* pStr, WORD wMax)
+{
+ char wstr[150];
+
+ switch (nErr) /* Basically, we do this with a switch block */
+ {
+ case U14ERR_OFF:
+ sprintf(wstr, "The 1401 is apparently switched off (code %d)", nErr);
+ break;
+
+ case U14ERR_NC:
+ sprintf(wstr, "The 1401 is not connected to the interface card (code %d)", nErr);
+ break;
+
+ case U14ERR_ILL:
+ sprintf(wstr, "The 1401 is not working correctly (code %d)", nErr);
+ break;
+
+ case U14ERR_NOIF:
+ sprintf(wstr, "The 1401 interface card was not detected (code %d)", nErr);
+ break;
+
+ case U14ERR_TIME:
+ sprintf(wstr, "The 1401 fails to become ready for use (code %d)", nErr);
+ break;
+
+ case U14ERR_BADSW:
+ sprintf(wstr, "The 1401 interface card jumpers are incorrect (code %d)", nErr);
+ break;
+
+ case U14ERR_NOINT:
+ sprintf(wstr, "The 1401 interrupt is not available for use (code %d)", nErr);
+ break;
+
+ case U14ERR_INUSE:
+ sprintf(wstr, "The 1401 is already in use by another program (code %d)", nErr);
+ break;
+
+ case U14ERR_NODMA:
+ sprintf(wstr, "The 1401 DMA channel is not available for use (code %d)", nErr);
+ break;
+
+ case U14ERR_BADHAND:
+ sprintf(wstr, "The application supplied an incorrect 1401 handle (code %d)", nErr);
+ break;
+
+ case U14ERR_BAD1401NUM:
+ sprintf(wstr, "The application used an incorrect 1401 number (code %d)", nErr);
+ break;
+
+ case U14ERR_NO_SUCH_FN:
+ sprintf(wstr, "The code passed to the 1401 driver is invalid (code %d)", nErr);
+ break;
+
+ case U14ERR_NO_SUCH_SUBFN:
+ sprintf(wstr, "The sub-code passed to the 1401 driver is invalid (code %d)", nErr);
+ break;
+
+ case U14ERR_NOOUT:
+ sprintf(wstr, "No room in buffer for characters for the 1401 (code %d)", nErr);
+ break;
+
+ case U14ERR_NOIN:
+ sprintf(wstr, "No characters from the 1401 are available (code %d)", nErr);
+ break;
+
+ case U14ERR_STRLEN:
+ sprintf(wstr, "A string sent to or read from the 1401 was too long (code %d)", nErr);
+ break;
+
+ case U14ERR_LOCKFAIL:
+ sprintf(wstr, "Failed to lock host memory for data transfer (code %d)", nErr);
+ break;
+
+ case U14ERR_UNLOCKFAIL:
+ sprintf(wstr, "Failed to unlock host memory after data transfer (code %d)", nErr);
+ break;
+
+ case U14ERR_ALREADYSET:
+ sprintf(wstr, "The transfer area used is already set up (code %d)", nErr);
+ break;
+
+ case U14ERR_NOTSET:
+ sprintf(wstr, "The transfer area used has not been set up (code %d)", nErr);
+ break;
+
+ case U14ERR_BADAREA:
+ sprintf(wstr, "The transfer area number is incorrect (code %d)", nErr);
+ break;
+
+ case U14ERR_NOFILE:
+ sprintf(wstr, "The command file %s could not be opened (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_READERR:
+ sprintf(wstr, "The command file %s could not be read (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_UNKNOWN:
+ sprintf(wstr, "The %s command resource could not be found (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_HOSTSPACE:
+ sprintf(wstr, "Unable to allocate memory for loading command %s (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_LOCKERR:
+ sprintf(wstr, "Unable to lock memory for loading command %s (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_CLOADERR:
+ sprintf(wstr, "Error in loading command %s, bad command format (code %d)", szLastName, nErr);
+ break;
+
+ case U14ERR_TOXXXERR:
+ sprintf(wstr, "Error detected after data transfer to or from the 1401 (code %d)", nErr);
+ break;
+
+ case U14ERR_NO386ENH:
+ sprintf(wstr, "Windows 3.1 is not running in 386 enhanced mode (code %d)", nErr);
+ break;
+
+ case U14ERR_NO1401DRIV:
+ sprintf(wstr, "The 1401 device driver cannot be found (code %d)\nUSB: check plugged in and powered\nOther: not installed?", nErr);
+ break;
+
+ case U14ERR_DRIVTOOOLD:
+ sprintf(wstr, "The 1401 device driver is too old for use (code %d)", nErr);
+ break;
+
+ case U14ERR_TIMEOUT:
+ sprintf(wstr, "Character transmissions to the 1401 timed-out (code %d)", nErr);
+ break;
+
+ case U14ERR_BUFF_SMALL:
+ sprintf(wstr, "Buffer for text from the 1401 was too small (code %d)", nErr);
+ break;
+
+ case U14ERR_CBALREADY:
+ sprintf(wstr, "1401 monitor callback already set up (code %d)", nErr);
+ break;
+
+ case U14ERR_BADDEREG:
+ sprintf(wstr, "1401 monitor callback deregister invalid (code %d)", nErr);
+ break;
+
+ case U14ERR_DRIVCOMMS:
+ sprintf(wstr, "1401 device driver communications failed (code %d)", nErr);
+ break;
+
+ case U14ERR_OUTOFMEMORY:
+ sprintf(wstr, "Failed to allocate or lock memory for text from the 1401 (code %d)", nErr);
+ break;
+
+ default:
+ sprintf(wstr, "1401 error code %d returned; this code is unknown", nErr);
+ break;
+
+ }
+ if ((WORD)strlen(wstr) >= wMax-1) /* Check for string being too long */
+ wstr[wMax-1] = 0; /* and truncate it if so */
+ strcpy(pStr, wstr); /* Return the error string */
+}
+
+/***************************************************************************
+** U14GetTransfer
+** Get a TGET_TX_BLOCK describing a transfer area (held in the block)
+***************************************************************************/
+U14API(short) U14GetTransfer(short hand, TGET_TX_BLOCK *pTransBlock)
+{
+ short sErr = CheckHandle(hand);
+#ifdef _IS_WINDOWS_
+ if (sErr == U14ERR_NOERROR)
+ {
+ DWORD dwBytes = 0;
+ BOOL bOK = DeviceIoControl(aHand1401[hand], (DWORD)U14_GETTRANSFER, NULL, 0, pTransBlock,
+ sizeof(TGET_TX_BLOCK), &dwBytes, NULL);
+
+ if (bOK && (dwBytes >= sizeof(TGET_TX_BLOCK)))
+ sErr = U14ERR_NOERROR;
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+ return sErr;
+#endif
+#ifdef LINUX
+ return (sErr == U14ERR_NOERROR) ? CED_GetTransfer(aHand1401[hand], pTransBlock) : sErr;
+#endif
+}
+/////////////////////////////////////////////////////////////////////////////
+// U14WorkingSet
+// For Win32 only, adjusts process working set so that minimum is at least
+// dwMinKb and maximum is at least dwMaxKb.
+// Return value is zero if all went OK, or a code from 1 to 3 indicating the
+// cause of the failure:
+//
+// 1 unable to access process (insufficient rights?)
+// 2 unable to read process working set
+// 3 unable to set process working set - bad parameters?
+U14API(short) U14WorkingSet(DWORD dwMinKb, DWORD dwMaxKb)
+{
+#ifdef _IS_WINDOWS_
+ short sRetVal = 0; // 0 means all is OK
+ HANDLE hProcess;
+ DWORD dwVer = GetVersion();
+ if (dwVer & 0x80000000) // is this not NT?
+ return 0; // then give up right now
+
+ // Now attempt to get information on working set size
+ hProcess = OpenProcess(STANDARD_RIGHTS_REQUIRED |
+ PROCESS_QUERY_INFORMATION |
+ PROCESS_SET_QUOTA,
+ FALSE, _getpid());
+ if (hProcess)
+ {
+ SIZE_T dwMinSize,dwMaxSize;
+ if (GetProcessWorkingSetSize(hProcess, &dwMinSize, &dwMaxSize))
+ {
+ DWORD dwMin = dwMinKb << 10; // convert from kb to bytes
+ DWORD dwMax = dwMaxKb << 10;
+
+ // if we get here, we have managed to read the current size
+ if (dwMin > dwMinSize) // need to change sizes?
+ dwMinSize = dwMin;
+
+ if (dwMax > dwMaxSize)
+ dwMaxSize = dwMax;
+
+ if (!SetProcessWorkingSetSize(hProcess, dwMinSize, dwMaxSize))
+ sRetVal = 3; // failed to change size
+ }
+ else
+ sRetVal = 2; // failed to read original size
+
+ CloseHandle(hProcess);
+ }
+ else
+ sRetVal = 1; // failed to get handle
+
+ return sRetVal;
+#endif
+#ifdef LINUX
+ if (dwMinKb | dwMaxKb)
+ {
+ // to stop compiler moaning
+ }
+ return U14ERR_NOERROR;
+#endif
+}
+
+/****************************************************************************
+** U14UnSetTransfer Cancels a transfer area
+** wArea The index of a block previously used in by SetTransfer
+*****************************************************************************/
+U14API(short) U14UnSetTransfer(short hand, WORD wArea)
+{
+ short sErr = CheckHandle(hand);
+#ifdef _IS_WINDOWS_
+ if (sErr == U14ERR_NOERROR)
+ {
+ TCSBLOCK csBlock;
+ csBlock.ints[0] = (short)wArea; /* Area number into control block */
+ sErr = U14Control1401(hand, U14_UNSETTRANSFER, &csBlock); /* Free area */
+
+ VirtualUnlock(apAreas[hand][wArea], auAreas[hand][wArea]);/* Unlock */
+ apAreas[hand][wArea] = NULL; /* Clear locations */
+ auAreas[hand][wArea] = 0;
+ }
+ return sErr;
+#endif
+#ifdef LINUX
+ return (sErr == U14ERR_NOERROR) ? CED_UnsetTransfer(aHand1401[hand], wArea) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14SetTransArea Sets an area up to be used for transfers
+** WORD wArea The area number to set up
+** void *pvBuff The address of the buffer for the data.
+** DWORD dwLength The length of the buffer for the data
+** short eSz The element size (used for byte swapping on the Mac)
+****************************************************************************/
+U14API(short) U14SetTransArea(short hand, WORD wArea, void *pvBuff,
+ DWORD dwLength, short eSz)
+{
+ TRANSFERDESC td;
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+ if (wArea >= MAX_TRANSAREAS) // Is this a valid area number
+ return U14ERR_BADAREA;
+
+#ifdef _IS_WINDOWS_
+ assert(apAreas[hand][wArea] == NULL);
+ assert(auAreas[hand][wArea] == 0);
+
+ apAreas[hand][wArea] = pvBuff; /* Save data for later */
+ auAreas[hand][wArea] = dwLength;
+
+ if (!VirtualLock(pvBuff, dwLength)) /* Lock using WIN32 calls */
+ {
+ apAreas[hand][wArea] = NULL; /* Clear locations */
+ auAreas[hand][wArea] = 0;
+ return U14ERR_LOCKERR; /* VirtualLock failed */
+ }
+#ifndef _WIN64
+ if (!USE_NT_DIOC(hand)) /* Use Win 9x DIOC? */
+ {
+ DWORD dwBytes;
+ VXTRANSFERDESC vxDesc; /* Structure to pass to VXD */
+ vxDesc.wArea = wArea; /* Copy across simple params */
+ vxDesc.dwLength = dwLength;
+
+ // Check we are not asking an old driver for more than area 0
+ if ((wArea != 0) && (U14DriverVersion(hand) < 0x00010002L))
+ sErr = U14ERR_DRIVTOOOLD;
+ else
+ {
+ vxDesc.dwAddrOfs = (DWORD)pvBuff; /* 32 bit offset */
+ vxDesc.wAddrSel = 0;
+
+ if (DeviceIoControl(aHand1401[hand], (DWORD)U14_SETTRANSFER,
+ pvBuff,dwLength, /* Will translate pointer */
+ &vxDesc,sizeof(VXTRANSFERDESC),
+ &dwBytes,NULL))
+ {
+ if (dwBytes >= sizeof(VXTRANSFERDESC)) /* Driver OK ? */
+ sErr = U14ERR_NOERROR;
+ else
+ sErr = U14ERR_DRIVCOMMS; /* Else never got there */
+ }
+ else
+ sErr = (short)GetLastError();
+ }
+ }
+ else
+#endif
+ {
+ PARAMBLK rWork;
+ DWORD dwBytes;
+ td.wArea = wArea; /* Pure NT - put data into struct */
+ td.lpvBuff = pvBuff;
+ td.dwLength = dwLength;
+ td.eSize = 0; // Dummy element size
+
+ if (DeviceIoControl(aHand1401[hand],(DWORD)U14_SETTRANSFER,
+ &td,sizeof(TRANSFERDESC),
+ &rWork,sizeof(PARAMBLK),&dwBytes,NULL))
+ {
+ if (dwBytes >= sizeof(PARAMBLK)) // maybe error from driver?
+ sErr = rWork.sState; // will report any error
+ else
+ sErr = U14ERR_DRIVCOMMS; // Else never got there
+ }
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+
+ if (sErr != U14ERR_NOERROR)
+ {
+ if (sErr != U14ERR_LOCKERR) // unless lock failed...
+ VirtualUnlock(pvBuff, dwLength); // ...release the lock
+ apAreas[hand][wArea] = NULL; // Clear locations
+ auAreas[hand][wArea] = 0;
+ }
+
+ return sErr;
+#endif
+#ifdef LINUX
+ // The strange cast is so that it works in 64 and 32-bit linux as long is 64-bits
+ // in the 64 bit version.
+ td.lpvBuff = (long long)((unsigned long)pvBuff);
+ td.wAreaNum = wArea;
+ td.dwLength = dwLength;
+ td.eSize = eSz; // Dummy element size
+ return CED_SetTransfer(aHand1401[hand], &td);
+#endif
+}
+
+/****************************************************************************
+** U14SetTransferEvent Sets an event for notification of application
+** wArea The tranfer area index, from 0 to MAXAREAS-1
+** bEvent True to create an event, false to remove it
+** bToHost Set 0 for notification on to1401 tranfers, 1 for
+** notification of transfers to the host PC
+** dwStart The offset of the sub-area of interest
+** dwLength The size of the sub-area of interest
+**
+** The device driver will set the event supplied to the signalled state
+** whenever a DMA transfer to/from the specified area is completed. The
+** transfer has to be in the direction specified by bToHost, and overlap
+** that part of the whole transfer area specified by dwStart and dwLength.
+** It is important that this function is called with bEvent false to release
+** the event once 1401 activity is finished.
+**
+** Returns 1 if an event handle exists, 0 if all OK and no event handle or
+** a negative code for an error.
+****************************************************************************/
+U14API(short) U14SetTransferEvent(short hand, WORD wArea, BOOL bEvent,
+ BOOL bToHost, DWORD dwStart, DWORD dwLength)
+{
+#ifdef _IS_WINDOWS_
+ TCSBLOCK csBlock;
+ short sErr = U14TransferFlags(hand); // see if we can handle events
+ if (sErr >= U14ERR_NOERROR) // check handle is OK
+ {
+ bEvent = bEvent && ((sErr & U14TF_NOTIFY) != 0); // remove request if we cannot do events
+ if (wArea >= MAX_TRANSAREAS) // Check a valid area...
+ return U14ERR_BADAREA; // ...and bail of not
+
+ // We can hold an event for each area, so see if we need to change the
+ // state of the event.
+ if ((bEvent != 0) != (aXferEvent[hand] != 0)) // change of event state?
+ {
+ if (bEvent) // want one and none present
+ aXferEvent[hand] = CreateEvent(NULL, FALSE, FALSE, NULL);
+ else
+ {
+ CloseHandle(aXferEvent[hand]); // clear the existing event
+ aXferEvent[hand] = NULL; // and clear handle
+ }
+ }
+
+ // We have to store the parameters differently for 64-bit operations
+ // because a handle is 64 bits long. The drivers know of this and
+ // handle the information appropriately.
+#ifdef _WIN64
+ csBlock.longs[0] = wArea; // Pass paramaters into the driver...
+ if (bToHost != 0) // The direction flag is held in the
+ csBlock.longs[0] |= 0x10000; // upper word of the transfer area value
+ *((HANDLE*)&csBlock.longs[1]) = aXferEvent[hand]; // The event handle is 64-bits
+ csBlock.longs[3] = dwStart; // Thankfully these two remain
+ csBlock.longs[4] = dwLength; // as unsigned 32-bit values
+#else
+ csBlock.longs[0] = wArea; // pass paramaters into the driver...
+ csBlock.longs[1] = (long)aXferEvent[hand]; // ...especially the event handle
+ csBlock.longs[2] = bToHost;
+ csBlock.longs[3] = dwStart;
+ csBlock.longs[4] = dwLength;
+#endif
+ sErr = U14Control1401(hand, U14_SETTRANSEVENT, &csBlock);
+ if (sErr == U14ERR_NOERROR)
+ sErr = (short)(aXferEvent[hand] != NULL); // report if we have a flag
+ }
+
+ return sErr;
+#endif
+#ifdef LINUX
+ TRANSFEREVENT te;
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+
+ if (wArea >= MAX_TRANSAREAS) // Is this a valid area number
+ return U14ERR_BADAREA;
+
+ te.wAreaNum = wArea; // copy parameters to the control block
+ te.wFlags = bToHost ? 1 : 0; // bit 0 sets the direction
+ te.dwStart = dwStart; // start offset of the event area
+ te.dwLength = dwLength; // size of the event area
+ te.iSetEvent = bEvent; // in Windows, this creates/destroys the event
+ return CED_SetEvent(aHand1401[hand], &te);
+#endif
+}
+
+/****************************************************************************
+** U14TestTransferEvent
+** Would a U14WaitTransferEvent() call return immediately? return 1 if so,
+** 0 if not or a negative code if a problem.
+****************************************************************************/
+U14API(int) U14TestTransferEvent(short hand, WORD wArea)
+{
+#ifdef _IS_WINDOWS_
+ int iErr = CheckHandle(hand);
+ if (iErr == U14ERR_NOERROR)
+ {
+ if (aXferEvent[hand]) // if a handle is set...
+ iErr = WaitForSingleObject(aXferEvent[hand], 0) == WAIT_OBJECT_0;
+ }
+ return iErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_TestEvent(aHand1401[hand], wArea) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14WaitTransferEvent
+** Wait for a transfer event with a timeout.
+** msTimeOut is 0 for an infinite wait, else it is the maximum time to wait
+** in milliseconds in range 0-0x00ffffff.
+** Returns If no event handle then return immediately. Else return 1 if
+** timed out or 0=event, and a negative code if a problem.
+****************************************************************************/
+U14API(int) U14WaitTransferEvent(short hand, WORD wArea, int msTimeOut)
+{
+#ifdef _IS_WINDOWS_
+ int iErr = CheckHandle(hand);
+ if (iErr == U14ERR_NOERROR)
+ {
+ if (aXferEvent[hand])
+ {
+ if (msTimeOut == 0)
+ msTimeOut = INFINITE;
+ iErr = WaitForSingleObject(aXferEvent[hand], msTimeOut) != WAIT_OBJECT_0;
+ }
+ else
+ iErr = TRUE; // say we timed out if no event
+ }
+ return iErr;
+#endif
+#ifdef LINUX
+ short sErr = CheckHandle(hand);
+ return (sErr == U14ERR_NOERROR) ? CED_WaitEvent(aHand1401[hand], wArea, msTimeOut) : sErr;
+#endif
+}
+
+/****************************************************************************
+** U14SetCircular Sets an area up for circular DMA transfers
+** WORD wArea The area number to set up
+** BOOL bToHost Sets the direction of data transfer
+** void *pvBuff The address of the buffer for the data
+** DWORD dwLength The length of the buffer for the data
+****************************************************************************/
+U14API(short) U14SetCircular(short hand, WORD wArea, BOOL bToHost,
+ void *pvBuff, DWORD dwLength)
+{
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+
+ if (wArea >= MAX_TRANSAREAS) /* Is this a valid area number */
+ return U14ERR_BADAREA;
+
+ if (!bToHost) /* For now, support tohost transfers only */
+ return U14ERR_BADAREA; /* best error code I can find */
+#ifdef _IS_WINDOWS_
+ assert(apAreas[hand][wArea] == NULL);
+ assert(auAreas[hand][wArea] == 0);
+
+ apAreas[hand][wArea] = pvBuff; /* Save data for later */
+ auAreas[hand][wArea] = dwLength;
+
+ if (!VirtualLock(pvBuff, dwLength)) /* Lock using WIN32 calls */
+ sErr = U14ERR_LOCKERR; /* VirtualLock failed */
+ else
+ {
+ PARAMBLK rWork;
+ DWORD dwBytes;
+ TRANSFERDESC txDesc;
+ txDesc.wArea = wArea; /* Pure NT - put data into struct */
+ txDesc.lpvBuff = pvBuff;
+ txDesc.dwLength = dwLength;
+ txDesc.eSize = (short)bToHost; /* Use this for direction flag */
+
+ if (DeviceIoControl(aHand1401[hand],(DWORD)U14_SETCIRCULAR,
+ &txDesc, sizeof(TRANSFERDESC),
+ &rWork, sizeof(PARAMBLK),&dwBytes,NULL))
+ {
+ if (dwBytes >= sizeof(PARAMBLK)) /* error from driver? */
+ sErr = rWork.sState; /* No, just return driver data */
+ else
+ sErr = U14ERR_DRIVCOMMS; /* Else never got there */
+ }
+ else
+ sErr = U14ERR_DRIVCOMMS;
+ }
+
+ if (sErr != U14ERR_NOERROR)
+ {
+ if (sErr != U14ERR_LOCKERR)
+ VirtualUnlock(pvBuff, dwLength); /* Release NT lock */
+ apAreas[hand][wArea] = NULL; /* Clear locations */
+ auAreas[hand][wArea] = 0;
+ }
+
+ return sErr;
+#endif
+#ifdef LINUX
+ else
+ {
+ TRANSFERDESC td;
+ td.lpvBuff = (long long)((unsigned long)pvBuff);
+ td.wAreaNum = wArea;
+ td.dwLength = dwLength;
+ td.eSize = (short)bToHost; /* Use this for direction flag */
+ return CED_SetCircular(aHand1401[hand], &td);
+ }
+#endif
+}
+
+/****************************************************************************
+** Function GetCircBlk returns the size (& start offset) of the next
+** available block of circular data.
+****************************************************************************/
+U14API(int) U14GetCircBlk(short hand, WORD wArea, DWORD *pdwOffs)
+{
+ int lErr = CheckHandle(hand);
+ if (lErr != U14ERR_NOERROR)
+ return lErr;
+
+ if (wArea >= MAX_TRANSAREAS) // Is this a valid area number?
+ return U14ERR_BADAREA;
+ else
+ {
+#ifdef _IS_WINDOWS_
+ PARAMBLK rWork;
+ TCSBLOCK csBlock;
+ DWORD dwBytes;
+ csBlock.longs[0] = wArea; // Area number into control block
+ rWork.sState = U14ERR_DRIVCOMMS;
+ if (DeviceIoControl(aHand1401[hand], (DWORD)U14_GETCIRCBLK, &csBlock, sizeof(TCSBLOCK), &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
+ (dwBytes >= sizeof(PARAMBLK)))
+ lErr = rWork.sState;
+ else
+ lErr = U14ERR_DRIVCOMMS;
+
+ if (lErr == U14ERR_NOERROR) // Did everything go OK?
+ { // Yes, we can pass the results back
+ lErr = rWork.csBlock.longs[1]; // Return the block information
+ *pdwOffs = rWork.csBlock.longs[0]; // Offset is first in array
+ }
+#endif
+#ifdef LINUX
+ TCIRCBLOCK cb;
+ cb.nArea = wArea; // Area number into control block
+ cb.dwOffset = 0;
+ cb.dwSize = 0;
+ lErr = CED_GetCircBlock(aHand1401[hand], &cb);
+ if (lErr == U14ERR_NOERROR) // Did everything go OK?
+ { // Yes, we can pass the results back
+ lErr = cb.dwSize; // return the size
+ *pdwOffs = cb.dwOffset; // and the offset
+ }
+#endif
+ }
+ return lErr;
+}
+
+/****************************************************************************
+** Function FreeCircBlk marks the specified area of memory as free for
+** resuse for circular transfers and returns the size (& start
+** offset) of the next available block of circular data.
+****************************************************************************/
+U14API(int) U14FreeCircBlk(short hand, WORD wArea, DWORD dwOffs, DWORD dwSize,
+ DWORD *pdwOffs)
+{
+ int lErr = CheckHandle(hand);
+ if (lErr != U14ERR_NOERROR)
+ return lErr;
+
+ if (wArea < MAX_TRANSAREAS) // Is this a valid area number
+ {
+#ifdef _IS_WINDOWS_
+ PARAMBLK rWork;
+ TCSBLOCK csBlock;
+ DWORD dwBytes;
+ csBlock.longs[0] = wArea; // Area number into control block
+ csBlock.longs[1] = dwOffs;
+ csBlock.longs[2] = dwSize;
+ rWork.sState = U14ERR_DRIVCOMMS;
+ if (DeviceIoControl(aHand1401[hand], (DWORD)U14_FREECIRCBLK, &csBlock, sizeof(TCSBLOCK),
+ &rWork, sizeof(PARAMBLK), &dwBytes, NULL) &&
+ (dwBytes >= sizeof(PARAMBLK)))
+ lErr = rWork.sState;
+ else
+ lErr = U14ERR_DRIVCOMMS;
+ if (lErr == U14ERR_NOERROR) // Did everything work OK?
+ { // Yes, we can pass the results back
+ lErr = rWork.csBlock.longs[1]; // Return the block information
+ *pdwOffs = rWork.csBlock.longs[0]; // Offset is first in array
+ }
+#endif
+#ifdef LINUX
+ TCIRCBLOCK cb;
+ cb.nArea = wArea; // Area number into control block
+ cb.dwOffset = dwOffs;
+ cb.dwSize = dwSize;
+
+ lErr = CED_FreeCircBlock(aHand1401[hand], &cb);
+ if (lErr == U14ERR_NOERROR) // Did everything work OK?
+ { // Yes, we can pass the results back
+ lErr = cb.dwSize; // Return the block information
+ *pdwOffs = cb.dwOffset; // Offset is first in array
+ }
+#endif
+ }
+ else
+ lErr = U14ERR_BADAREA;
+
+ return lErr;
+}
+
+/****************************************************************************
+** Transfer
+** Transfer moves data to 1401 or to host
+** Assumes memory is allocated and locked,
+** which it should be to get a pointer
+*****************************************************************************/
+static short Transfer(short hand, BOOL bTo1401, char* pData,
+ DWORD dwSize, DWORD dw1401, short eSz)
+{
+ char strcopy[MAXSTRLEN+1]; // to hold copy of work string
+ short sResult = U14SetTransArea(hand, 0, (void *)pData, dwSize, eSz);
+ if (sResult == U14ERR_NOERROR) // no error
+ {
+ sprintf(strcopy, // data offset is always 0
+ "TO%s,$%X,$%X,0;", bTo1401 ? "1401" : "HOST", dw1401, dwSize);
+
+ U14SendString(hand, strcopy); // send transfer string
+
+ sResult = U14CheckErr(hand); // Use ERR command to check for done
+ if (sResult > 0)
+ sResult = U14ERR_TOXXXERR; // If a 1401 error, use this code
+
+ U14UnSetTransfer(hand, 0);
+ }
+ return sResult;
+}
+
+/****************************************************************************
+** Function ToHost transfers data into the host from the 1401
+****************************************************************************/
+U14API(short) U14ToHost(short hand, char* pAddrHost, DWORD dwSize,
+ DWORD dw1401, short eSz)
+{
+ short sErr = CheckHandle(hand);
+ if ((sErr == U14ERR_NOERROR) && dwSize) // TOHOST is a constant
+ sErr = Transfer(hand, TOHOST, pAddrHost, dwSize, dw1401, eSz);
+ return sErr;
+}
+
+/****************************************************************************
+** Function To1401 transfers data into the 1401 from the host
+****************************************************************************/
+U14API(short) U14To1401(short hand, const char* pAddrHost,DWORD dwSize,
+ DWORD dw1401, short eSz)
+{
+ short sErr = CheckHandle(hand);
+ if ((sErr == U14ERR_NOERROR) && dwSize) // TO1401 is a constant
+ sErr = Transfer(hand, TO1401, (char*)pAddrHost, dwSize, dw1401, eSz);
+ return sErr;
+}
+
+/****************************************************************************
+** Function LdCmd Loads a command from a full path or just a file
+*****************************************************************************/
+#ifdef _IS_WINDOWS_
+#define file_exist(name) (_access(name, 0) != -1)
+#define file_open(name) _lopen(name, OF_READ)
+#define file_close(h) _lclose(h)
+#define file_seek(h, pos) _llseek(h, pos, FILE_BEGIN)
+#define file_read(h, buffer, size) (_lread(h, buffer, size) == size)
+#endif
+#ifdef LINUX
+#define file_exist(name) (access(name, F_OK) != -1)
+#define file_open(name) open(name, O_RDONLY)
+#define file_close(h) close(h)
+#define file_seek(h, pos) lseek(h, pos, SEEK_SET)
+#define file_read(h, buffer, size) (read(h, buffer, size) == (ssize_t)size)
+static DWORD GetModuleFileName(void* dummy, char* buffer, int max)
+{
+ // The following works for Linux systems with a /proc file system.
+ char szProcPath[32];
+ sprintf(szProcPath, "/proc/%d/exe", getpid()); // attempt to read link
+ if (readlink(szProcPath, buffer, max) != -1)
+ {
+ dirname (buffer);
+ strcat (buffer, "/");
+ return strlen(buffer);
+ }
+ return 0;
+}
+#endif
+
+U14API(short) U14LdCmd(short hand, const char* command)
+{
+ char strcopy[MAXSTRLEN+1]; // to hold copy of work string
+ BOOL bGotIt = FALSE; // have we found the command file?
+ int iFHandle; // file handle of command
+#define FNSZ 260
+ char filnam[FNSZ]; // space to build name in
+ char szCmd[25]; // just the command name with extension
+
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+
+ if (strchr(command, '.') != NULL) // see if we have full name
+ {
+ if (file_exist(command)) // If the file exists
+ {
+ strcpy(filnam, command); // use name as is
+ bGotIt = TRUE; // Flag no more searching
+ }
+ else // not found, get file name for search
+ {
+ char* pStr = strrchr(command, PATHSEP); // Point to last separator
+ if (pStr != NULL) // Check we got it
+ {
+ pStr++; // move past the backslash
+ strcpy(szCmd, pStr); // copy file name as is
+ }
+ else
+ strcpy(szCmd, command); // use as is
+ }
+ }
+ else // File extension not supplied, so build the command file name
+ {
+ char szExt[8];
+ strcpy(szCmd, command); // Build command file name
+ ExtForType(asType1401[hand], szExt);// File extension string
+ strcat(szCmd, szExt); // add it to the end
+ }
+
+ // Next place to look is in the 1401 folder in the same place as the
+ // application was run from.
+ if (!bGotIt) // Still not got it?
+ {
+ DWORD dwLen = GetModuleFileName(NULL, filnam, FNSZ); // Get app path
+ if (dwLen > 0) // and use it as path if found
+ {
+ char* pStr = strrchr(filnam, PATHSEP); // Point to last separator
+ if (pStr != NULL)
+ {
+ *(++pStr) = 0; // Terminate string there
+ if (strlen(filnam) < FNSZ-6) // make sure we have space
+ {
+ strcat(filnam, "1401" PATHSEPSTR); // add in 1401 subdir
+ strcat(filnam,szCmd);
+ bGotIt = (BOOL)file_exist(filnam); // See if file exists
+ }
+ }
+ }
+ }
+
+ // Next place to look is in whatever path is set by the 1401DIR environment
+ // variable, if it exists.
+ if (!bGotIt) // Need to do more searches?/
+ {
+ char* pStr = getenv("1401DIR"); // Try to find environment var
+ if (pStr != NULL) // and use it as path if found
+ {
+ strcpy(filnam, pStr); // Use path in environment
+ if (filnam[strlen(filnam)-1] != PATHSEP)// We need separator
+ strcat(filnam, PATHSEPSTR);
+ strcat(filnam, szCmd);
+ bGotIt = (BOOL)file_exist(filnam); // Got this one?
+ }
+ }
+
+ // Last place to look is the default location.
+ if (!bGotIt) // Need to do more searches?
+ {
+ strcpy(filnam, DEFCMDPATH); // Use default path
+ strcat(filnam, szCmd);
+ bGotIt = file_exist(filnam); // Got this one?
+ }
+
+ iFHandle = file_open(filnam);
+ if (iFHandle == -1)
+ sErr = U14ERR_NOFILE;
+ else
+ { // first read in the header block
+ CMDHEAD rCmdHead; // to hold the command header
+ if (file_read(iFHandle, &rCmdHead, sizeof(CMDHEAD)))
+ {
+ size_t nComSize = rCmdHead.wCmdSize;
+ char* pMem = malloc(nComSize);
+ if (pMem != NULL)
+ {
+ file_seek(iFHandle, sizeof(CMDHEAD));
+ if (file_read(iFHandle, pMem, (UINT)nComSize))
+ {
+ sErr = U14SetTransArea(hand, 0, (void *)pMem, (DWORD)nComSize, ESZBYTES);
+ if (sErr == U14ERR_NOERROR)
+ {
+ sprintf(strcopy, "CLOAD,0,$%X;", (int)nComSize);
+ sErr = U14SendString(hand, strcopy);
+ if (sErr == U14ERR_NOERROR)
+ {
+ sErr = U14CheckErr(hand); // Use ERR to check for done
+ if (sErr > 0)
+ sErr = U14ERR_CLOADERR; // If an error, this code
+ }
+ U14UnSetTransfer(hand, 0); // release transfer area
+ }
+ }
+ else
+ sErr = U14ERR_READERR;
+ free(pMem);
+ }
+ else
+ sErr = U14ERR_HOSTSPACE; // memory allocate failed
+ }
+ else
+ sErr = U14ERR_READERR;
+
+ file_close(iFHandle); // close the file
+ }
+
+ return sErr;
+}
+
+
+/****************************************************************************
+** Ld
+** Loads a command into the 1401
+** Returns NOERROR code or a long with error in lo word and index of
+** command that failed in high word
+****************************************************************************/
+U14API(DWORD) U14Ld(short hand, const char* vl, const char* str)
+{
+ DWORD dwIndex = 0; // index to current command
+ long lErr = U14ERR_NOERROR; // what the error was that went wrong
+ char strcopy[MAXSTRLEN+1]; // stores unmodified str parameter
+ char szFExt[8]; // The command file extension
+ short sErr = CheckHandle(hand);
+ if (sErr != U14ERR_NOERROR)
+ return sErr;
+
+ ExtForType(asType1401[hand], szFExt); // File extension string
+ strcpy(strcopy, str); // to avoid changing original
+
+ // now break out one command at a time and see if loaded
+ if (*str) // if anything there
+ {
+ BOOL bDone = FALSE; // true when finished all commands
+ int iLoop1 = 0; // Point at start of string for command name
+ int iLoop2 = 0; // and at start of str parameter
+ do // repeat until end of str
+ {
+ char filnam[MAXSTRLEN+1]; // filename to use
+ char szFName[MAXSTRLEN+1]; // filename work string
+
+ if (!strcopy[iLoop1]) // at the end of the string?
+ bDone = TRUE; // set the finish flag
+
+ if (bDone || (strcopy[iLoop1] == ',')) // end of cmd?
+ {
+ U14LONG er[5]; // Used to read back error results
+ ++dwIndex; // Keep count of command number, first is 1
+ szFName[iLoop2]=(char)0; // null terminate name of command
+
+ strncpy(szLastName, szFName, sizeof(szLastName)); // Save for error info
+ szLastName[sizeof(szLastName)-1] = 0;
+ strncat(szLastName, szFExt, sizeof(szLastName)); // with extension included
+ szLastName[sizeof(szLastName)-1] = 0;
+
+ U14SendString(hand, szFName); // ask if loaded
+ U14SendString(hand, ";ERR;"); // add err return
+
+ lErr = U14LongsFrom1401(hand, er, 5);
+ if (lErr > 0)
+ {
+ lErr = U14ERR_NOERROR;
+ if (er[0] == 255) // if command not loaded at all
+ {
+ if (vl && *vl) // if we have a path name
+ {
+ strcpy(filnam, vl);
+ if (strchr("\\/:", filnam[strlen(filnam)-1]) == NULL)
+ strcat(filnam, PATHSEPSTR); // add separator if none found
+ strcat(filnam, szFName); // add the file name
+ strcat(filnam, szFExt); // and extension
+ }
+ else
+ strcpy(filnam, szFName); // simple name
+
+ lErr = U14LdCmd(hand, filnam); // load cmd
+ if (lErr != U14ERR_NOERROR) // spot any errors
+ bDone = TRUE; // give up if an error
+ }
+ }
+ else
+ bDone = TRUE; // give up if an error
+
+ iLoop2 = 0; // Reset pointer to command name string
+ ++iLoop1; // and move on through str parameter
+ }
+ else
+ szFName[iLoop2++] = strcopy[iLoop1++]; // no command end, so copy 1 char
+ }
+ while (!bDone);
+ }
+
+ if (lErr == U14ERR_NOERROR)
+ {
+ szLastName[0] = 0; // No error, so clean out command name here
+ return lErr;
+ }
+ else
+ return ((dwIndex<<16) | ((DWORD)lErr & 0x0000FFFF));
+}
+
+// Initialise the library (if not initialised) and return the library version
+U14API(int) U14InitLib(void)
+{
+ int iRetVal = U14LIB_VERSION;
+ if (iAttached == 0) // only do this the first time please
+ {
+ int i;
+#ifdef _IS_WINDOWS_
+ int j;
+ DWORD dwVersion = GetVersion();
+ bWindows9x = FALSE; // Assume not Win9x
+
+ if (dwVersion & 0x80000000) // if not windows NT
+ {
+ if ((LOBYTE(LOWORD(dwVersion)) < 4) && // if Win32s or...
+ (HIBYTE(LOWORD(dwVersion)) < 95)) // ...below Windows 95
+ iRetVal = 0; // We do not support this
+ else
+ bWindows9x = TRUE; // Flag we have Win9x
+ }
+#endif
+
+ for (i = 0; i < MAX1401; i++) // initialise the device area
+ {
+ aHand1401[i] = INVALID_HANDLE_VALUE; // Clear handle values
+ asType1401[i] = U14TYPEUNKNOWN; // and 1401 type codes
+ alTimeOutPeriod[i] = 3000; // 3 second timeouts
+#ifdef _IS_WINDOWS_
+#ifndef _WIN64
+ abUseNTDIOC[i] = (BOOL)!bWindows9x;
+#endif
+ aXferEvent[i] = NULL; // there are no Xfer events
+ for (j = 0; j < MAX_TRANSAREAS; j++) // Clear out locked area info
+ {
+ apAreas[i][j] = NULL;
+ auAreas[i][j] = 0;
+ }
+#endif
+ }
+ }
+ return iRetVal;
+}
+
+///--------------------------------------------------------------------------------
+/// Functions called when the library is loaded and unloaded to give us a chance to
+/// setup the library.
+
+
+#ifdef _IS_WINDOWS_
+#ifndef U14_NOT_DLL
+/****************************************************************************
+** FUNCTION: DllMain(HANDLE, DWORD, LPVOID)
+** LibMain is called by Windows when the DLL is initialized, Thread Attached,
+** and other times. Refer to SDK documentation, as to the different ways this
+** may be called.
+****************************************************************************/
+INT APIENTRY DllMain(HANDLE hInst, DWORD ul_reason_being_called, LPVOID lpReserved)
+{
+ int iRetVal = 1;
+
+ switch (ul_reason_being_called)
+ {
+ case DLL_PROCESS_ATTACH:
+ iRetVal = U14InitLib() > 0; // does nothing if iAttached != 0
+ ++iAttached; // count times attached
+ break;
+
+ case DLL_PROCESS_DETACH:
+ if (--iAttached == 0) // last man out?
+ U14CloseAll(); // release all open handles
+ break;
+ }
+ return iRetVal;
+
+ UNREFERENCED_PARAMETER(lpReserved);
+}
+#endif
+#endif
+#ifdef LINUX
+void __attribute__((constructor)) use1401_load(void)
+{
+ U14InitLib();
+ ++iAttached;
+}
+
+void __attribute__((destructor)) use1401_unload(void)
+{
+ if (--iAttached == 0) // last man out?
+ U14CloseAll(); // release all open handles
+}
+#endif
diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig
index 6cee7855b019..2093403af253 100644
--- a/drivers/staging/comedi/Kconfig
+++ b/drivers/staging/comedi/Kconfig
@@ -549,6 +549,23 @@ menuconfig COMEDI_PCI_DRIVERS
if COMEDI_PCI_DRIVERS
+config COMEDI_8255_PCI
+ tristate "Generic PCI based 8255 digital i/o board support"
+ select COMEDI_8255
+ ---help---
+ Enable support for PCI based 8255 digital i/o boards. This driver
+ provides a PCI wrapper around the generic 8255 driver.
+
+ Supported boards:
+ ADlink - PCI-7224, PCI-7248, and PCI-7296
+ Measurement Computing - PCI-DIO24, PCI-DIO24H, PCI-DIO48H and
+ PCI-DIO96H
+ National Instruments - PCI-DIO-96, PCI-DIO-96B, PXI-6508, PCI-6503,
+ PCI-6503B, PCI-6503X, and PXI-6503
+
+ To compile this driver as a module, choose M here: the module will
+ be called 8255_pci.
+
config COMEDI_ADDI_APCI_035
tristate "ADDI-DATA APCI_035 support"
depends on VIRT_TO_BUS
@@ -676,30 +693,16 @@ config COMEDI_ADL_PCI6208
To compile this driver as a module, choose M here: the module will be
called adl_pci6208.
-config COMEDI_ADL_PCI7230
- tristate "ADLink PCI-7230 digital io board support"
- ---help---
- Enable support for ADlink PCI-7230 digital io board support
-
- To compile this driver as a module, choose M here: the module will be
- called adl_pci7230.
-
-config COMEDI_ADL_PCI7296
- tristate "ADLink PCI-7296 96 ch. digital io board support"
- select COMEDI_8255
+config COMEDI_ADL_PCI7X3X
+ tristate "ADLink PCI-723X/743X isolated digital i/o board support"
---help---
- Enable support for ADlink PCI-7296 96 ch. digital io board support
+ Enable support for ADlink PCI-723X/743X isolated digital i/o boards.
+ Supported boards include the 32-channel PCI-7230 (16 in/16 out),
+ PCI-7233 (32 in), and PCI-7234 (32 out) as well as the 64-channel
+ PCI-7432 (32 in/32 out), PCI-7433 (64 in), and PCI-7434 (64 out).
To compile this driver as a module, choose M here: the module will be
- called adl_pci7296.
-
-config COMEDI_ADL_PCI7432
- tristate "ADLink PCI-7432 64 ch. isolated digital io board support"
- ---help---
- Enable support for ADlink PCI-7432 64 ch. isolated digital io board
-
- To compile this driver as a module, choose M here: the module will be
- called adl_pci7432.
+ called adl_pci7x3x.
config COMEDI_ADL_PCI8164
tristate "ADLink PCI-8164 4 Axes Motion Control board support"
@@ -935,16 +938,6 @@ config COMEDI_CB_PCIDDA
To compile this driver as a module, choose M here: the module will be
called cb_pcidda.
-config COMEDI_CB_PCIDIO
- tristate "MeasurementComputing PCI-DIO series support"
- select COMEDI_8255
- ---help---
- Enable support for ComputerBoards/MeasurementComputing PCI-DIO series
- PCI-DIO24, PCI-DIO24H and PCI-DIO48H
-
- To compile this driver as a module, choose M here: the module will be
- called cb_pcidio.
-
config COMEDI_CB_PCIMDAS
tristate "MeasurementComputing PCIM-DAS1602/16 support"
select COMEDI_8255
@@ -1039,15 +1032,12 @@ config COMEDI_NI_LABPC
called ni_labpc.
config COMEDI_NI_PCIDIO
- tristate "NI PCI-DIO32HS, PCI-DIO96, PCI-6533, PCI-6503 support"
+ tristate "NI PCI-DIO32HS, PCI-6533, PCI-6534 support"
select COMEDI_MITE
select COMEDI_8255
---help---
Enable support for National Instruments PCI-DIO-32HS, PXI-6533,
- PCI-DIO-96, PCI-DIO-96B, PXI-6508, PCI-6503, PCI-6503B, PCI-6503X,
- PXI-6503, PCI-6533 and PCI-6534
- The DIO-96 appears as four 8255 subdevices. See the 8255
- driver notes for details.
+ PCI-6533 and PCI-6534
To compile this driver as a module, choose M here: the module will be
called ni_pcidio.
@@ -1262,8 +1252,8 @@ config COMEDI_8255
that has an 8255 chip. For multifunction boards, the main driver will
configure the 8255 subdevice automatically.
- Note that most PCI 8255 boards do NOT work with this driver, and
- need a separate driver as a wrapper.
+ Note that most PCI based 8255 boards use the 8255_pci driver as a
+ wrapper around this driver.
To compile this driver as a module, choose M here: the module will be
called 8255.
diff --git a/drivers/staging/comedi/comedi.h b/drivers/staging/comedi/comedi.h
index 8ea55aef10a7..133f013e0f6d 100644
--- a/drivers/staging/comedi/comedi.h
+++ b/drivers/staging/comedi/comedi.h
@@ -208,92 +208,92 @@
/* subdevice types */
- enum comedi_subdevice_type {
- COMEDI_SUBD_UNUSED, /* unused by driver */
- COMEDI_SUBD_AI, /* analog input */
- COMEDI_SUBD_AO, /* analog output */
- COMEDI_SUBD_DI, /* digital input */
- COMEDI_SUBD_DO, /* digital output */
- COMEDI_SUBD_DIO, /* digital input/output */
- COMEDI_SUBD_COUNTER, /* counter */
- COMEDI_SUBD_TIMER, /* timer */
- COMEDI_SUBD_MEMORY, /* memory, EEPROM, DPRAM */
- COMEDI_SUBD_CALIB, /* calibration DACs */
- COMEDI_SUBD_PROC, /* processor, DSP */
- COMEDI_SUBD_SERIAL, /* serial IO */
- COMEDI_SUBD_PWM /* PWM */
- };
+enum comedi_subdevice_type {
+ COMEDI_SUBD_UNUSED, /* unused by driver */
+ COMEDI_SUBD_AI, /* analog input */
+ COMEDI_SUBD_AO, /* analog output */
+ COMEDI_SUBD_DI, /* digital input */
+ COMEDI_SUBD_DO, /* digital output */
+ COMEDI_SUBD_DIO, /* digital input/output */
+ COMEDI_SUBD_COUNTER, /* counter */
+ COMEDI_SUBD_TIMER, /* timer */
+ COMEDI_SUBD_MEMORY, /* memory, EEPROM, DPRAM */
+ COMEDI_SUBD_CALIB, /* calibration DACs */
+ COMEDI_SUBD_PROC, /* processor, DSP */
+ COMEDI_SUBD_SERIAL, /* serial IO */
+ COMEDI_SUBD_PWM /* PWM */
+};
/* configuration instructions */
- enum configuration_ids {
- INSN_CONFIG_DIO_INPUT = 0,
- INSN_CONFIG_DIO_OUTPUT = 1,
- INSN_CONFIG_DIO_OPENDRAIN = 2,
- INSN_CONFIG_ANALOG_TRIG = 16,
+enum configuration_ids {
+ INSN_CONFIG_DIO_INPUT = 0,
+ INSN_CONFIG_DIO_OUTPUT = 1,
+ INSN_CONFIG_DIO_OPENDRAIN = 2,
+ INSN_CONFIG_ANALOG_TRIG = 16,
/* INSN_CONFIG_WAVEFORM = 17, */
/* INSN_CONFIG_TRIG = 18, */
/* INSN_CONFIG_COUNTER = 19, */
- INSN_CONFIG_ALT_SOURCE = 20,
- INSN_CONFIG_DIGITAL_TRIG = 21,
- INSN_CONFIG_BLOCK_SIZE = 22,
- INSN_CONFIG_TIMER_1 = 23,
- INSN_CONFIG_FILTER = 24,
- INSN_CONFIG_CHANGE_NOTIFY = 25,
-
- /*ALPHA*/ INSN_CONFIG_SERIAL_CLOCK = 26,
- INSN_CONFIG_BIDIRECTIONAL_DATA = 27,
- INSN_CONFIG_DIO_QUERY = 28,
- INSN_CONFIG_PWM_OUTPUT = 29,
- INSN_CONFIG_GET_PWM_OUTPUT = 30,
- INSN_CONFIG_ARM = 31,
- INSN_CONFIG_DISARM = 32,
- INSN_CONFIG_GET_COUNTER_STATUS = 33,
- INSN_CONFIG_RESET = 34,
- /* Use CTR as single pulsegenerator */
- INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR = 1001,
- /* Use CTR as pulsetraingenerator */
- INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR = 1002,
- /* Use the counter as encoder */
- INSN_CONFIG_GPCT_QUADRATURE_ENCODER = 1003,
- INSN_CONFIG_SET_GATE_SRC = 2001, /* Set gate source */
- INSN_CONFIG_GET_GATE_SRC = 2002, /* Get gate source */
- /* Set master clock source */
- INSN_CONFIG_SET_CLOCK_SRC = 2003,
- INSN_CONFIG_GET_CLOCK_SRC = 2004, /* Get master clock source */
- INSN_CONFIG_SET_OTHER_SRC = 2005, /* Set other source */
- /* INSN_CONFIG_GET_OTHER_SRC = 2006,*//* Get other source */
- /* Get size in bytes of subdevice's on-board fifos used during
- * streaming input/output */
- INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE = 2006,
- INSN_CONFIG_SET_COUNTER_MODE = 4097,
- /* INSN_CONFIG_8254_SET_MODE is deprecated */
- INSN_CONFIG_8254_SET_MODE = INSN_CONFIG_SET_COUNTER_MODE,
- INSN_CONFIG_8254_READ_STATUS = 4098,
- INSN_CONFIG_SET_ROUTING = 4099,
- INSN_CONFIG_GET_ROUTING = 4109,
-/* PWM */
- INSN_CONFIG_PWM_SET_PERIOD = 5000, /* sets frequency */
- INSN_CONFIG_PWM_GET_PERIOD = 5001, /* gets frequency */
- INSN_CONFIG_GET_PWM_STATUS = 5002, /* is it running? */
- /* sets H bridge: duty cycle and sign bit for a relay at the
- * same time */
- INSN_CONFIG_PWM_SET_H_BRIDGE = 5003,
- /* gets H bridge data: duty cycle and the sign bit */
- INSN_CONFIG_PWM_GET_H_BRIDGE = 5004
- };
-
- enum comedi_io_direction {
- COMEDI_INPUT = 0,
- COMEDI_OUTPUT = 1,
- COMEDI_OPENDRAIN = 2
- };
-
- enum comedi_support_level {
- COMEDI_UNKNOWN_SUPPORT = 0,
- COMEDI_SUPPORTED,
- COMEDI_UNSUPPORTED
- };
+ INSN_CONFIG_ALT_SOURCE = 20,
+ INSN_CONFIG_DIGITAL_TRIG = 21,
+ INSN_CONFIG_BLOCK_SIZE = 22,
+ INSN_CONFIG_TIMER_1 = 23,
+ INSN_CONFIG_FILTER = 24,
+ INSN_CONFIG_CHANGE_NOTIFY = 25,
+
+ INSN_CONFIG_SERIAL_CLOCK = 26, /*ALPHA*/
+ INSN_CONFIG_BIDIRECTIONAL_DATA = 27,
+ INSN_CONFIG_DIO_QUERY = 28,
+ INSN_CONFIG_PWM_OUTPUT = 29,
+ INSN_CONFIG_GET_PWM_OUTPUT = 30,
+ INSN_CONFIG_ARM = 31,
+ INSN_CONFIG_DISARM = 32,
+ INSN_CONFIG_GET_COUNTER_STATUS = 33,
+ INSN_CONFIG_RESET = 34,
+ /* Use CTR as single pulsegenerator */
+ INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR = 1001,
+ /* Use CTR as pulsetraingenerator */
+ INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR = 1002,
+ /* Use the counter as encoder */
+ INSN_CONFIG_GPCT_QUADRATURE_ENCODER = 1003,
+ INSN_CONFIG_SET_GATE_SRC = 2001, /* Set gate source */
+ INSN_CONFIG_GET_GATE_SRC = 2002, /* Get gate source */
+ /* Set master clock source */
+ INSN_CONFIG_SET_CLOCK_SRC = 2003,
+ INSN_CONFIG_GET_CLOCK_SRC = 2004, /* Get master clock source */
+ INSN_CONFIG_SET_OTHER_SRC = 2005, /* Set other source */
+ /* INSN_CONFIG_GET_OTHER_SRC = 2006,*//* Get other source */
+ /* Get size in bytes of subdevice's on-board fifos used during
+ * streaming input/output */
+ INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE = 2006,
+ INSN_CONFIG_SET_COUNTER_MODE = 4097,
+ /* INSN_CONFIG_8254_SET_MODE is deprecated */
+ INSN_CONFIG_8254_SET_MODE = INSN_CONFIG_SET_COUNTER_MODE,
+ INSN_CONFIG_8254_READ_STATUS = 4098,
+ INSN_CONFIG_SET_ROUTING = 4099,
+ INSN_CONFIG_GET_ROUTING = 4109,
+ /* PWM */
+ INSN_CONFIG_PWM_SET_PERIOD = 5000, /* sets frequency */
+ INSN_CONFIG_PWM_GET_PERIOD = 5001, /* gets frequency */
+ INSN_CONFIG_GET_PWM_STATUS = 5002, /* is it running? */
+ /* sets H bridge: duty cycle and sign bit for a relay at the
+ * same time */
+ INSN_CONFIG_PWM_SET_H_BRIDGE = 5003,
+ /* gets H bridge data: duty cycle and the sign bit */
+ INSN_CONFIG_PWM_GET_H_BRIDGE = 5004
+};
+
+enum comedi_io_direction {
+ COMEDI_INPUT = 0,
+ COMEDI_OUTPUT = 1,
+ COMEDI_OPENDRAIN = 2
+};
+
+enum comedi_support_level {
+ COMEDI_UNKNOWN_SUPPORT = 0,
+ COMEDI_SUPPORTED,
+ COMEDI_UNSUPPORTED
+};
/* ioctls */
@@ -317,133 +317,133 @@
/* structures */
- struct comedi_trig {
- unsigned int subdev; /* subdevice */
- unsigned int mode; /* mode */
- unsigned int flags;
- unsigned int n_chan; /* number of channels */
- unsigned int *chanlist; /* channel/range list */
- short *data; /* data list, size depends on subd flags */
- unsigned int n; /* number of scans */
- unsigned int trigsrc;
- unsigned int trigvar;
- unsigned int trigvar1;
- unsigned int data_len;
- unsigned int unused[3];
- };
-
- struct comedi_insn {
- unsigned int insn;
- unsigned int n;
- unsigned int __user *data;
- unsigned int subdev;
- unsigned int chanspec;
- unsigned int unused[3];
- };
-
- struct comedi_insnlist {
- unsigned int n_insns;
- struct comedi_insn __user *insns;
- };
-
- struct comedi_cmd {
- unsigned int subdev;
- unsigned int flags;
-
- unsigned int start_src;
- unsigned int start_arg;
-
- unsigned int scan_begin_src;
- unsigned int scan_begin_arg;
-
- unsigned int convert_src;
- unsigned int convert_arg;
-
- unsigned int scan_end_src;
- unsigned int scan_end_arg;
-
- unsigned int stop_src;
- unsigned int stop_arg;
-
- unsigned int __user *chanlist; /* channel/range list */
- unsigned int chanlist_len;
-
- short __user *data; /* data list, size depends on subd flags */
- unsigned int data_len;
- };
-
- struct comedi_chaninfo {
- unsigned int subdev;
- unsigned int __user *maxdata_list;
- unsigned int __user *flaglist;
- unsigned int __user *rangelist;
- unsigned int unused[4];
- };
-
- struct comedi_rangeinfo {
- unsigned int range_type;
- void __user *range_ptr;
- };
-
- struct comedi_krange {
- int min; /* fixed point, multiply by 1e-6 */
- int max; /* fixed point, multiply by 1e-6 */
- unsigned int flags;
- };
-
- struct comedi_subdinfo {
- unsigned int type;
- unsigned int n_chan;
- unsigned int subd_flags;
- unsigned int timer_type;
- unsigned int len_chanlist;
- unsigned int maxdata;
- unsigned int flags; /* channel flags */
- unsigned int range_type; /* lookup in kernel */
- unsigned int settling_time_0;
- /* see support_level enum for values */
- unsigned insn_bits_support;
- unsigned int unused[8];
- };
-
- struct comedi_devinfo {
- unsigned int version_code;
- unsigned int n_subdevs;
- char driver_name[COMEDI_NAMELEN];
- char board_name[COMEDI_NAMELEN];
- int read_subdevice;
- int write_subdevice;
- int unused[30];
- };
-
- struct comedi_devconfig {
- char board_name[COMEDI_NAMELEN];
- int options[COMEDI_NDEVCONFOPTS];
- };
-
- struct comedi_bufconfig {
- unsigned int subdevice;
- unsigned int flags;
-
- unsigned int maximum_size;
- unsigned int size;
-
- unsigned int unused[4];
- };
-
- struct comedi_bufinfo {
- unsigned int subdevice;
- unsigned int bytes_read;
-
- unsigned int buf_write_ptr;
- unsigned int buf_read_ptr;
- unsigned int buf_write_count;
- unsigned int buf_read_count;
-
- unsigned int bytes_written;
-
- unsigned int unused[4];
- };
+struct comedi_trig {
+ unsigned int subdev; /* subdevice */
+ unsigned int mode; /* mode */
+ unsigned int flags;
+ unsigned int n_chan; /* number of channels */
+ unsigned int *chanlist; /* channel/range list */
+ short *data; /* data list, size depends on subd flags */
+ unsigned int n; /* number of scans */
+ unsigned int trigsrc;
+ unsigned int trigvar;
+ unsigned int trigvar1;
+ unsigned int data_len;
+ unsigned int unused[3];
+};
+
+struct comedi_insn {
+ unsigned int insn;
+ unsigned int n;
+ unsigned int __user *data;
+ unsigned int subdev;
+ unsigned int chanspec;
+ unsigned int unused[3];
+};
+
+struct comedi_insnlist {
+ unsigned int n_insns;
+ struct comedi_insn __user *insns;
+};
+
+struct comedi_cmd {
+ unsigned int subdev;
+ unsigned int flags;
+
+ unsigned int start_src;
+ unsigned int start_arg;
+
+ unsigned int scan_begin_src;
+ unsigned int scan_begin_arg;
+
+ unsigned int convert_src;
+ unsigned int convert_arg;
+
+ unsigned int scan_end_src;
+ unsigned int scan_end_arg;
+
+ unsigned int stop_src;
+ unsigned int stop_arg;
+
+ unsigned int *chanlist; /* channel/range list */
+ unsigned int chanlist_len;
+
+ short __user *data; /* data list, size depends on subd flags */
+ unsigned int data_len;
+};
+
+struct comedi_chaninfo {
+ unsigned int subdev;
+ unsigned int __user *maxdata_list;
+ unsigned int __user *flaglist;
+ unsigned int __user *rangelist;
+ unsigned int unused[4];
+};
+
+struct comedi_rangeinfo {
+ unsigned int range_type;
+ void __user *range_ptr;
+};
+
+struct comedi_krange {
+ int min; /* fixed point, multiply by 1e-6 */
+ int max; /* fixed point, multiply by 1e-6 */
+ unsigned int flags;
+};
+
+struct comedi_subdinfo {
+ unsigned int type;
+ unsigned int n_chan;
+ unsigned int subd_flags;
+ unsigned int timer_type;
+ unsigned int len_chanlist;
+ unsigned int maxdata;
+ unsigned int flags; /* channel flags */
+ unsigned int range_type; /* lookup in kernel */
+ unsigned int settling_time_0;
+ /* see support_level enum for values */
+ unsigned insn_bits_support;
+ unsigned int unused[8];
+};
+
+struct comedi_devinfo {
+ unsigned int version_code;
+ unsigned int n_subdevs;
+ char driver_name[COMEDI_NAMELEN];
+ char board_name[COMEDI_NAMELEN];
+ int read_subdevice;
+ int write_subdevice;
+ int unused[30];
+};
+
+struct comedi_devconfig {
+ char board_name[COMEDI_NAMELEN];
+ int options[COMEDI_NDEVCONFOPTS];
+};
+
+struct comedi_bufconfig {
+ unsigned int subdevice;
+ unsigned int flags;
+
+ unsigned int maximum_size;
+ unsigned int size;
+
+ unsigned int unused[4];
+};
+
+struct comedi_bufinfo {
+ unsigned int subdevice;
+ unsigned int bytes_read;
+
+ unsigned int buf_write_ptr;
+ unsigned int buf_read_ptr;
+ unsigned int buf_write_count;
+ unsigned int buf_read_count;
+
+ unsigned int bytes_written;
+
+ unsigned int unused[4];
+};
/* range stuff */
@@ -495,306 +495,306 @@
*/
- enum i8254_mode {
- I8254_MODE0 = (0 << 1), /* Interrupt on terminal count */
- I8254_MODE1 = (1 << 1), /* Hardware retriggerable one-shot */
- I8254_MODE2 = (2 << 1), /* Rate generator */
- I8254_MODE3 = (3 << 1), /* Square wave mode */
- I8254_MODE4 = (4 << 1), /* Software triggered strobe */
- I8254_MODE5 = (5 << 1), /* Hardware triggered strobe
- * (retriggerable) */
- I8254_BCD = 1, /* use binary-coded decimal instead of binary
+enum i8254_mode {
+ I8254_MODE0 = (0 << 1), /* Interrupt on terminal count */
+ I8254_MODE1 = (1 << 1), /* Hardware retriggerable one-shot */
+ I8254_MODE2 = (2 << 1), /* Rate generator */
+ I8254_MODE3 = (3 << 1), /* Square wave mode */
+ I8254_MODE4 = (4 << 1), /* Software triggered strobe */
+ I8254_MODE5 = (5 << 1), /* Hardware triggered strobe
+ * (retriggerable) */
+ I8254_BCD = 1, /* use binary-coded decimal instead of binary
* (pretty useless) */
- I8254_BINARY = 0
- };
-
- static inline unsigned NI_USUAL_PFI_SELECT(unsigned pfi_channel)
- {
- if (pfi_channel < 10)
- return 0x1 + pfi_channel;
- else
- return 0xb + pfi_channel;
- }
-
- static inline unsigned NI_USUAL_RTSI_SELECT(unsigned rtsi_channel)
- {
- if (rtsi_channel < 7)
- return 0xb + rtsi_channel;
- else
- return 0x1b;
- }
+ I8254_BINARY = 0
+};
+
+static inline unsigned NI_USUAL_PFI_SELECT(unsigned pfi_channel)
+{
+ if (pfi_channel < 10)
+ return 0x1 + pfi_channel;
+ else
+ return 0xb + pfi_channel;
+}
+
+static inline unsigned NI_USUAL_RTSI_SELECT(unsigned rtsi_channel)
+{
+ if (rtsi_channel < 7)
+ return 0xb + rtsi_channel;
+ else
+ return 0x1b;
+}
/* mode bits for NI general-purpose counters, set with
* INSN_CONFIG_SET_COUNTER_MODE */
#define NI_GPCT_COUNTING_MODE_SHIFT 16
#define NI_GPCT_INDEX_PHASE_BITSHIFT 20
#define NI_GPCT_COUNTING_DIRECTION_SHIFT 24
- enum ni_gpct_mode_bits {
- NI_GPCT_GATE_ON_BOTH_EDGES_BIT = 0x4,
- NI_GPCT_EDGE_GATE_MODE_MASK = 0x18,
- NI_GPCT_EDGE_GATE_STARTS_STOPS_BITS = 0x0,
- NI_GPCT_EDGE_GATE_STOPS_STARTS_BITS = 0x8,
- NI_GPCT_EDGE_GATE_STARTS_BITS = 0x10,
- NI_GPCT_EDGE_GATE_NO_STARTS_NO_STOPS_BITS = 0x18,
- NI_GPCT_STOP_MODE_MASK = 0x60,
- NI_GPCT_STOP_ON_GATE_BITS = 0x00,
- NI_GPCT_STOP_ON_GATE_OR_TC_BITS = 0x20,
- NI_GPCT_STOP_ON_GATE_OR_SECOND_TC_BITS = 0x40,
- NI_GPCT_LOAD_B_SELECT_BIT = 0x80,
- NI_GPCT_OUTPUT_MODE_MASK = 0x300,
- NI_GPCT_OUTPUT_TC_PULSE_BITS = 0x100,
- NI_GPCT_OUTPUT_TC_TOGGLE_BITS = 0x200,
- NI_GPCT_OUTPUT_TC_OR_GATE_TOGGLE_BITS = 0x300,
- NI_GPCT_HARDWARE_DISARM_MASK = 0xc00,
- NI_GPCT_NO_HARDWARE_DISARM_BITS = 0x000,
- NI_GPCT_DISARM_AT_TC_BITS = 0x400,
- NI_GPCT_DISARM_AT_GATE_BITS = 0x800,
- NI_GPCT_DISARM_AT_TC_OR_GATE_BITS = 0xc00,
- NI_GPCT_LOADING_ON_TC_BIT = 0x1000,
- NI_GPCT_LOADING_ON_GATE_BIT = 0x4000,
- NI_GPCT_COUNTING_MODE_MASK = 0x7 << NI_GPCT_COUNTING_MODE_SHIFT,
- NI_GPCT_COUNTING_MODE_NORMAL_BITS =
- 0x0 << NI_GPCT_COUNTING_MODE_SHIFT,
- NI_GPCT_COUNTING_MODE_QUADRATURE_X1_BITS =
- 0x1 << NI_GPCT_COUNTING_MODE_SHIFT,
- NI_GPCT_COUNTING_MODE_QUADRATURE_X2_BITS =
- 0x2 << NI_GPCT_COUNTING_MODE_SHIFT,
- NI_GPCT_COUNTING_MODE_QUADRATURE_X4_BITS =
- 0x3 << NI_GPCT_COUNTING_MODE_SHIFT,
- NI_GPCT_COUNTING_MODE_TWO_PULSE_BITS =
- 0x4 << NI_GPCT_COUNTING_MODE_SHIFT,
- NI_GPCT_COUNTING_MODE_SYNC_SOURCE_BITS =
- 0x6 << NI_GPCT_COUNTING_MODE_SHIFT,
- NI_GPCT_INDEX_PHASE_MASK = 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
- NI_GPCT_INDEX_PHASE_LOW_A_LOW_B_BITS =
- 0x0 << NI_GPCT_INDEX_PHASE_BITSHIFT,
- NI_GPCT_INDEX_PHASE_LOW_A_HIGH_B_BITS =
- 0x1 << NI_GPCT_INDEX_PHASE_BITSHIFT,
- NI_GPCT_INDEX_PHASE_HIGH_A_LOW_B_BITS =
- 0x2 << NI_GPCT_INDEX_PHASE_BITSHIFT,
- NI_GPCT_INDEX_PHASE_HIGH_A_HIGH_B_BITS =
- 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
- NI_GPCT_INDEX_ENABLE_BIT = 0x400000,
- NI_GPCT_COUNTING_DIRECTION_MASK =
- 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
- NI_GPCT_COUNTING_DIRECTION_DOWN_BITS =
- 0x00 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
- NI_GPCT_COUNTING_DIRECTION_UP_BITS =
- 0x1 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
- NI_GPCT_COUNTING_DIRECTION_HW_UP_DOWN_BITS =
- 0x2 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
- NI_GPCT_COUNTING_DIRECTION_HW_GATE_BITS =
- 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
- NI_GPCT_RELOAD_SOURCE_MASK = 0xc000000,
- NI_GPCT_RELOAD_SOURCE_FIXED_BITS = 0x0,
- NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS = 0x4000000,
- NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS = 0x8000000,
- NI_GPCT_OR_GATE_BIT = 0x10000000,
- NI_GPCT_INVERT_OUTPUT_BIT = 0x20000000
- };
+enum ni_gpct_mode_bits {
+ NI_GPCT_GATE_ON_BOTH_EDGES_BIT = 0x4,
+ NI_GPCT_EDGE_GATE_MODE_MASK = 0x18,
+ NI_GPCT_EDGE_GATE_STARTS_STOPS_BITS = 0x0,
+ NI_GPCT_EDGE_GATE_STOPS_STARTS_BITS = 0x8,
+ NI_GPCT_EDGE_GATE_STARTS_BITS = 0x10,
+ NI_GPCT_EDGE_GATE_NO_STARTS_NO_STOPS_BITS = 0x18,
+ NI_GPCT_STOP_MODE_MASK = 0x60,
+ NI_GPCT_STOP_ON_GATE_BITS = 0x00,
+ NI_GPCT_STOP_ON_GATE_OR_TC_BITS = 0x20,
+ NI_GPCT_STOP_ON_GATE_OR_SECOND_TC_BITS = 0x40,
+ NI_GPCT_LOAD_B_SELECT_BIT = 0x80,
+ NI_GPCT_OUTPUT_MODE_MASK = 0x300,
+ NI_GPCT_OUTPUT_TC_PULSE_BITS = 0x100,
+ NI_GPCT_OUTPUT_TC_TOGGLE_BITS = 0x200,
+ NI_GPCT_OUTPUT_TC_OR_GATE_TOGGLE_BITS = 0x300,
+ NI_GPCT_HARDWARE_DISARM_MASK = 0xc00,
+ NI_GPCT_NO_HARDWARE_DISARM_BITS = 0x000,
+ NI_GPCT_DISARM_AT_TC_BITS = 0x400,
+ NI_GPCT_DISARM_AT_GATE_BITS = 0x800,
+ NI_GPCT_DISARM_AT_TC_OR_GATE_BITS = 0xc00,
+ NI_GPCT_LOADING_ON_TC_BIT = 0x1000,
+ NI_GPCT_LOADING_ON_GATE_BIT = 0x4000,
+ NI_GPCT_COUNTING_MODE_MASK = 0x7 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_NORMAL_BITS =
+ 0x0 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_QUADRATURE_X1_BITS =
+ 0x1 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_QUADRATURE_X2_BITS =
+ 0x2 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_QUADRATURE_X4_BITS =
+ 0x3 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_TWO_PULSE_BITS =
+ 0x4 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_COUNTING_MODE_SYNC_SOURCE_BITS =
+ 0x6 << NI_GPCT_COUNTING_MODE_SHIFT,
+ NI_GPCT_INDEX_PHASE_MASK = 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_LOW_A_LOW_B_BITS =
+ 0x0 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_LOW_A_HIGH_B_BITS =
+ 0x1 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_HIGH_A_LOW_B_BITS =
+ 0x2 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_PHASE_HIGH_A_HIGH_B_BITS =
+ 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT,
+ NI_GPCT_INDEX_ENABLE_BIT = 0x400000,
+ NI_GPCT_COUNTING_DIRECTION_MASK =
+ 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_DOWN_BITS =
+ 0x00 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_UP_BITS =
+ 0x1 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_HW_UP_DOWN_BITS =
+ 0x2 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_COUNTING_DIRECTION_HW_GATE_BITS =
+ 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT,
+ NI_GPCT_RELOAD_SOURCE_MASK = 0xc000000,
+ NI_GPCT_RELOAD_SOURCE_FIXED_BITS = 0x0,
+ NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS = 0x4000000,
+ NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS = 0x8000000,
+ NI_GPCT_OR_GATE_BIT = 0x10000000,
+ NI_GPCT_INVERT_OUTPUT_BIT = 0x20000000
+};
/* Bits for setting a clock source with
* INSN_CONFIG_SET_CLOCK_SRC when using NI general-purpose counters. */
- enum ni_gpct_clock_source_bits {
- NI_GPCT_CLOCK_SRC_SELECT_MASK = 0x3f,
- NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS = 0x0,
- NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS = 0x1,
- NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS = 0x2,
- NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS = 0x3,
- NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS = 0x4,
- NI_GPCT_NEXT_TC_CLOCK_SRC_BITS = 0x5,
- /* NI 660x-specific */
- NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS = 0x6,
- NI_GPCT_PXI10_CLOCK_SRC_BITS = 0x7,
- NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS = 0x8,
- NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS = 0x9,
- NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK = 0x30000000,
- NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS = 0x0,
- /* divide source by 2 */
- NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS = 0x10000000,
- /* divide source by 8 */
- NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS = 0x20000000,
- NI_GPCT_INVERT_CLOCK_SRC_BIT = 0x80000000
- };
- static inline unsigned NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(unsigned n)
- {
- /* NI 660x-specific */
- return 0x10 + n;
- }
- static inline unsigned NI_GPCT_RTSI_CLOCK_SRC_BITS(unsigned n)
- {
- return 0x18 + n;
- }
- static inline unsigned NI_GPCT_PFI_CLOCK_SRC_BITS(unsigned n)
- {
- /* no pfi on NI 660x */
- return 0x20 + n;
- }
+enum ni_gpct_clock_source_bits {
+ NI_GPCT_CLOCK_SRC_SELECT_MASK = 0x3f,
+ NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS = 0x0,
+ NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS = 0x1,
+ NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS = 0x2,
+ NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS = 0x3,
+ NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS = 0x4,
+ NI_GPCT_NEXT_TC_CLOCK_SRC_BITS = 0x5,
+ /* NI 660x-specific */
+ NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS = 0x6,
+ NI_GPCT_PXI10_CLOCK_SRC_BITS = 0x7,
+ NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS = 0x8,
+ NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS = 0x9,
+ NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK = 0x30000000,
+ NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS = 0x0,
+ /* divide source by 2 */
+ NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS = 0x10000000,
+ /* divide source by 8 */
+ NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS = 0x20000000,
+ NI_GPCT_INVERT_CLOCK_SRC_BIT = 0x80000000
+};
+static inline unsigned NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(unsigned n)
+{
+ /* NI 660x-specific */
+ return 0x10 + n;
+}
+static inline unsigned NI_GPCT_RTSI_CLOCK_SRC_BITS(unsigned n)
+{
+ return 0x18 + n;
+}
+static inline unsigned NI_GPCT_PFI_CLOCK_SRC_BITS(unsigned n)
+{
+ /* no pfi on NI 660x */
+ return 0x20 + n;
+}
/* Possibilities for setting a gate source with
INSN_CONFIG_SET_GATE_SRC when using NI general-purpose counters.
May be bitwise-or'd with CR_EDGE or CR_INVERT. */
- enum ni_gpct_gate_select {
- /* m-series gates */
- NI_GPCT_TIMESTAMP_MUX_GATE_SELECT = 0x0,
- NI_GPCT_AI_START2_GATE_SELECT = 0x12,
- NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT = 0x13,
- NI_GPCT_NEXT_OUT_GATE_SELECT = 0x14,
- NI_GPCT_AI_START1_GATE_SELECT = 0x1c,
- NI_GPCT_NEXT_SOURCE_GATE_SELECT = 0x1d,
- NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT = 0x1e,
- NI_GPCT_LOGIC_LOW_GATE_SELECT = 0x1f,
- /* more gates for 660x */
- NI_GPCT_SOURCE_PIN_i_GATE_SELECT = 0x100,
- NI_GPCT_GATE_PIN_i_GATE_SELECT = 0x101,
- /* more gates for 660x "second gate" */
- NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT = 0x201,
- NI_GPCT_SELECTED_GATE_GATE_SELECT = 0x21e,
- /* m-series "second gate" sources are unknown,
- * we should add them here with an offset of 0x300 when
- * known. */
- NI_GPCT_DISABLED_GATE_SELECT = 0x8000,
- };
- static inline unsigned NI_GPCT_GATE_PIN_GATE_SELECT(unsigned n)
- {
- return 0x102 + n;
- }
- static inline unsigned NI_GPCT_RTSI_GATE_SELECT(unsigned n)
- {
- return NI_USUAL_RTSI_SELECT(n);
- }
- static inline unsigned NI_GPCT_PFI_GATE_SELECT(unsigned n)
- {
- return NI_USUAL_PFI_SELECT(n);
- }
- static inline unsigned NI_GPCT_UP_DOWN_PIN_GATE_SELECT(unsigned n)
- {
- return 0x202 + n;
- }
+enum ni_gpct_gate_select {
+ /* m-series gates */
+ NI_GPCT_TIMESTAMP_MUX_GATE_SELECT = 0x0,
+ NI_GPCT_AI_START2_GATE_SELECT = 0x12,
+ NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT = 0x13,
+ NI_GPCT_NEXT_OUT_GATE_SELECT = 0x14,
+ NI_GPCT_AI_START1_GATE_SELECT = 0x1c,
+ NI_GPCT_NEXT_SOURCE_GATE_SELECT = 0x1d,
+ NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT = 0x1e,
+ NI_GPCT_LOGIC_LOW_GATE_SELECT = 0x1f,
+ /* more gates for 660x */
+ NI_GPCT_SOURCE_PIN_i_GATE_SELECT = 0x100,
+ NI_GPCT_GATE_PIN_i_GATE_SELECT = 0x101,
+ /* more gates for 660x "second gate" */
+ NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT = 0x201,
+ NI_GPCT_SELECTED_GATE_GATE_SELECT = 0x21e,
+ /* m-series "second gate" sources are unknown,
+ * we should add them here with an offset of 0x300 when
+ * known. */
+ NI_GPCT_DISABLED_GATE_SELECT = 0x8000,
+};
+static inline unsigned NI_GPCT_GATE_PIN_GATE_SELECT(unsigned n)
+{
+ return 0x102 + n;
+}
+static inline unsigned NI_GPCT_RTSI_GATE_SELECT(unsigned n)
+{
+ return NI_USUAL_RTSI_SELECT(n);
+}
+static inline unsigned NI_GPCT_PFI_GATE_SELECT(unsigned n)
+{
+ return NI_USUAL_PFI_SELECT(n);
+}
+static inline unsigned NI_GPCT_UP_DOWN_PIN_GATE_SELECT(unsigned n)
+{
+ return 0x202 + n;
+}
/* Possibilities for setting a source with
INSN_CONFIG_SET_OTHER_SRC when using NI general-purpose counters. */
- enum ni_gpct_other_index {
- NI_GPCT_SOURCE_ENCODER_A,
- NI_GPCT_SOURCE_ENCODER_B,
- NI_GPCT_SOURCE_ENCODER_Z
- };
- enum ni_gpct_other_select {
- /* m-series gates */
- /* Still unknown, probably only need NI_GPCT_PFI_OTHER_SELECT */
- NI_GPCT_DISABLED_OTHER_SELECT = 0x8000,
- };
- static inline unsigned NI_GPCT_PFI_OTHER_SELECT(unsigned n)
- {
- return NI_USUAL_PFI_SELECT(n);
- }
+enum ni_gpct_other_index {
+ NI_GPCT_SOURCE_ENCODER_A,
+ NI_GPCT_SOURCE_ENCODER_B,
+ NI_GPCT_SOURCE_ENCODER_Z
+};
+enum ni_gpct_other_select {
+ /* m-series gates */
+ /* Still unknown, probably only need NI_GPCT_PFI_OTHER_SELECT */
+ NI_GPCT_DISABLED_OTHER_SELECT = 0x8000,
+};
+static inline unsigned NI_GPCT_PFI_OTHER_SELECT(unsigned n)
+{
+ return NI_USUAL_PFI_SELECT(n);
+}
/* start sources for ni general-purpose counters for use with
INSN_CONFIG_ARM */
- enum ni_gpct_arm_source {
- NI_GPCT_ARM_IMMEDIATE = 0x0,
- NI_GPCT_ARM_PAIRED_IMMEDIATE = 0x1, /* Start both the counter
- * and the adjacent paired
- * counter simultaneously */
- /* NI doesn't document bits for selecting hardware arm triggers.
- * If the NI_GPCT_ARM_UNKNOWN bit is set, we will pass the least
- * significant bits (3 bits for 660x or 5 bits for m-series)
- * through to the hardware. This will at least allow someone to
- * figure out what the bits do later. */
- NI_GPCT_ARM_UNKNOWN = 0x1000,
- };
+enum ni_gpct_arm_source {
+ NI_GPCT_ARM_IMMEDIATE = 0x0,
+ NI_GPCT_ARM_PAIRED_IMMEDIATE = 0x1, /* Start both the counter
+ * and the adjacent paired
+ * counter simultaneously */
+ /* NI doesn't document bits for selecting hardware arm triggers.
+ * If the NI_GPCT_ARM_UNKNOWN bit is set, we will pass the least
+ * significant bits (3 bits for 660x or 5 bits for m-series)
+ * through to the hardware. This will at least allow someone to
+ * figure out what the bits do later. */
+ NI_GPCT_ARM_UNKNOWN = 0x1000,
+};
/* digital filtering options for ni 660x for use with INSN_CONFIG_FILTER. */
- enum ni_gpct_filter_select {
- NI_GPCT_FILTER_OFF = 0x0,
- NI_GPCT_FILTER_TIMEBASE_3_SYNC = 0x1,
- NI_GPCT_FILTER_100x_TIMEBASE_1 = 0x2,
- NI_GPCT_FILTER_20x_TIMEBASE_1 = 0x3,
- NI_GPCT_FILTER_10x_TIMEBASE_1 = 0x4,
- NI_GPCT_FILTER_2x_TIMEBASE_1 = 0x5,
- NI_GPCT_FILTER_2x_TIMEBASE_3 = 0x6
- };
+enum ni_gpct_filter_select {
+ NI_GPCT_FILTER_OFF = 0x0,
+ NI_GPCT_FILTER_TIMEBASE_3_SYNC = 0x1,
+ NI_GPCT_FILTER_100x_TIMEBASE_1 = 0x2,
+ NI_GPCT_FILTER_20x_TIMEBASE_1 = 0x3,
+ NI_GPCT_FILTER_10x_TIMEBASE_1 = 0x4,
+ NI_GPCT_FILTER_2x_TIMEBASE_1 = 0x5,
+ NI_GPCT_FILTER_2x_TIMEBASE_3 = 0x6
+};
/* PFI digital filtering options for ni m-series for use with
* INSN_CONFIG_FILTER. */
- enum ni_pfi_filter_select {
- NI_PFI_FILTER_OFF = 0x0,
- NI_PFI_FILTER_125ns = 0x1,
- NI_PFI_FILTER_6425ns = 0x2,
- NI_PFI_FILTER_2550us = 0x3
- };
+enum ni_pfi_filter_select {
+ NI_PFI_FILTER_OFF = 0x0,
+ NI_PFI_FILTER_125ns = 0x1,
+ NI_PFI_FILTER_6425ns = 0x2,
+ NI_PFI_FILTER_2550us = 0x3
+};
/* master clock sources for ni mio boards and INSN_CONFIG_SET_CLOCK_SRC */
- enum ni_mio_clock_source {
- NI_MIO_INTERNAL_CLOCK = 0,
- NI_MIO_RTSI_CLOCK = 1, /* doesn't work for m-series, use
- NI_MIO_PLL_RTSI_CLOCK() */
- /* the NI_MIO_PLL_* sources are m-series only */
- NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK = 2,
- NI_MIO_PLL_PXI10_CLOCK = 3,
- NI_MIO_PLL_RTSI0_CLOCK = 4
- };
- static inline unsigned NI_MIO_PLL_RTSI_CLOCK(unsigned rtsi_channel)
- {
- return NI_MIO_PLL_RTSI0_CLOCK + rtsi_channel;
- }
+enum ni_mio_clock_source {
+ NI_MIO_INTERNAL_CLOCK = 0,
+ NI_MIO_RTSI_CLOCK = 1, /* doesn't work for m-series, use
+ NI_MIO_PLL_RTSI_CLOCK() */
+ /* the NI_MIO_PLL_* sources are m-series only */
+ NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK = 2,
+ NI_MIO_PLL_PXI10_CLOCK = 3,
+ NI_MIO_PLL_RTSI0_CLOCK = 4
+};
+static inline unsigned NI_MIO_PLL_RTSI_CLOCK(unsigned rtsi_channel)
+{
+ return NI_MIO_PLL_RTSI0_CLOCK + rtsi_channel;
+}
/* Signals which can be routed to an NI RTSI pin with INSN_CONFIG_SET_ROUTING.
The numbers assigned are not arbitrary, they correspond to the bits required
to program the board. */
- enum ni_rtsi_routing {
- NI_RTSI_OUTPUT_ADR_START1 = 0,
- NI_RTSI_OUTPUT_ADR_START2 = 1,
- NI_RTSI_OUTPUT_SCLKG = 2,
- NI_RTSI_OUTPUT_DACUPDN = 3,
- NI_RTSI_OUTPUT_DA_START1 = 4,
- NI_RTSI_OUTPUT_G_SRC0 = 5,
- NI_RTSI_OUTPUT_G_GATE0 = 6,
- NI_RTSI_OUTPUT_RGOUT0 = 7,
- NI_RTSI_OUTPUT_RTSI_BRD_0 = 8,
- NI_RTSI_OUTPUT_RTSI_OSC = 12 /* pre-m-series always have RTSI
- * clock on line 7 */
- };
- static inline unsigned NI_RTSI_OUTPUT_RTSI_BRD(unsigned n)
- {
- return NI_RTSI_OUTPUT_RTSI_BRD_0 + n;
- }
+enum ni_rtsi_routing {
+ NI_RTSI_OUTPUT_ADR_START1 = 0,
+ NI_RTSI_OUTPUT_ADR_START2 = 1,
+ NI_RTSI_OUTPUT_SCLKG = 2,
+ NI_RTSI_OUTPUT_DACUPDN = 3,
+ NI_RTSI_OUTPUT_DA_START1 = 4,
+ NI_RTSI_OUTPUT_G_SRC0 = 5,
+ NI_RTSI_OUTPUT_G_GATE0 = 6,
+ NI_RTSI_OUTPUT_RGOUT0 = 7,
+ NI_RTSI_OUTPUT_RTSI_BRD_0 = 8,
+ NI_RTSI_OUTPUT_RTSI_OSC = 12 /* pre-m-series always have RTSI
+ * clock on line 7 */
+};
+static inline unsigned NI_RTSI_OUTPUT_RTSI_BRD(unsigned n)
+{
+ return NI_RTSI_OUTPUT_RTSI_BRD_0 + n;
+}
/* Signals which can be routed to an NI PFI pin on an m-series board with
* INSN_CONFIG_SET_ROUTING. These numbers are also returned by
* INSN_CONFIG_GET_ROUTING on pre-m-series boards, even though their routing
* cannot be changed. The numbers assigned are not arbitrary, they correspond
* to the bits required to program the board. */
- enum ni_pfi_routing {
- NI_PFI_OUTPUT_PFI_DEFAULT = 0,
- NI_PFI_OUTPUT_AI_START1 = 1,
- NI_PFI_OUTPUT_AI_START2 = 2,
- NI_PFI_OUTPUT_AI_CONVERT = 3,
- NI_PFI_OUTPUT_G_SRC1 = 4,
- NI_PFI_OUTPUT_G_GATE1 = 5,
- NI_PFI_OUTPUT_AO_UPDATE_N = 6,
- NI_PFI_OUTPUT_AO_START1 = 7,
- NI_PFI_OUTPUT_AI_START_PULSE = 8,
- NI_PFI_OUTPUT_G_SRC0 = 9,
- NI_PFI_OUTPUT_G_GATE0 = 10,
- NI_PFI_OUTPUT_EXT_STROBE = 11,
- NI_PFI_OUTPUT_AI_EXT_MUX_CLK = 12,
- NI_PFI_OUTPUT_GOUT0 = 13,
- NI_PFI_OUTPUT_GOUT1 = 14,
- NI_PFI_OUTPUT_FREQ_OUT = 15,
- NI_PFI_OUTPUT_PFI_DO = 16,
- NI_PFI_OUTPUT_I_ATRIG = 17,
- NI_PFI_OUTPUT_RTSI0 = 18,
- NI_PFI_OUTPUT_PXI_STAR_TRIGGER_IN = 26,
- NI_PFI_OUTPUT_SCXI_TRIG1 = 27,
- NI_PFI_OUTPUT_DIO_CHANGE_DETECT_RTSI = 28,
- NI_PFI_OUTPUT_CDI_SAMPLE = 29,
- NI_PFI_OUTPUT_CDO_UPDATE = 30
- };
- static inline unsigned NI_PFI_OUTPUT_RTSI(unsigned rtsi_channel)
- {
- return NI_PFI_OUTPUT_RTSI0 + rtsi_channel;
- }
+enum ni_pfi_routing {
+ NI_PFI_OUTPUT_PFI_DEFAULT = 0,
+ NI_PFI_OUTPUT_AI_START1 = 1,
+ NI_PFI_OUTPUT_AI_START2 = 2,
+ NI_PFI_OUTPUT_AI_CONVERT = 3,
+ NI_PFI_OUTPUT_G_SRC1 = 4,
+ NI_PFI_OUTPUT_G_GATE1 = 5,
+ NI_PFI_OUTPUT_AO_UPDATE_N = 6,
+ NI_PFI_OUTPUT_AO_START1 = 7,
+ NI_PFI_OUTPUT_AI_START_PULSE = 8,
+ NI_PFI_OUTPUT_G_SRC0 = 9,
+ NI_PFI_OUTPUT_G_GATE0 = 10,
+ NI_PFI_OUTPUT_EXT_STROBE = 11,
+ NI_PFI_OUTPUT_AI_EXT_MUX_CLK = 12,
+ NI_PFI_OUTPUT_GOUT0 = 13,
+ NI_PFI_OUTPUT_GOUT1 = 14,
+ NI_PFI_OUTPUT_FREQ_OUT = 15,
+ NI_PFI_OUTPUT_PFI_DO = 16,
+ NI_PFI_OUTPUT_I_ATRIG = 17,
+ NI_PFI_OUTPUT_RTSI0 = 18,
+ NI_PFI_OUTPUT_PXI_STAR_TRIGGER_IN = 26,
+ NI_PFI_OUTPUT_SCXI_TRIG1 = 27,
+ NI_PFI_OUTPUT_DIO_CHANGE_DETECT_RTSI = 28,
+ NI_PFI_OUTPUT_CDI_SAMPLE = 29,
+ NI_PFI_OUTPUT_CDO_UPDATE = 30
+};
+static inline unsigned NI_PFI_OUTPUT_RTSI(unsigned rtsi_channel)
+{
+ return NI_PFI_OUTPUT_RTSI0 + rtsi_channel;
+}
/* Signals which can be routed to output on a NI PFI pin on a 660x board
with INSN_CONFIG_SET_ROUTING. The numbers assigned are
@@ -802,113 +802,112 @@ INSN_CONFIG_ARM */
to program the board. Lines 0 to 7 can only be set to
NI_660X_PFI_OUTPUT_DIO. Lines 32 to 39 can only be set to
NI_660X_PFI_OUTPUT_COUNTER. */
- enum ni_660x_pfi_routing {
- NI_660X_PFI_OUTPUT_COUNTER = 1, /* counter */
- NI_660X_PFI_OUTPUT_DIO = 2, /* static digital output */
- };
+enum ni_660x_pfi_routing {
+ NI_660X_PFI_OUTPUT_COUNTER = 1, /* counter */
+ NI_660X_PFI_OUTPUT_DIO = 2, /* static digital output */
+};
/* NI External Trigger lines. These values are not arbitrary, but are related
* to the bits required to program the board (offset by 1 for historical
* reasons). */
- static inline unsigned NI_EXT_PFI(unsigned pfi_channel)
- {
- return NI_USUAL_PFI_SELECT(pfi_channel) - 1;
- }
- static inline unsigned NI_EXT_RTSI(unsigned rtsi_channel)
- {
- return NI_USUAL_RTSI_SELECT(rtsi_channel) - 1;
- }
+static inline unsigned NI_EXT_PFI(unsigned pfi_channel)
+{
+ return NI_USUAL_PFI_SELECT(pfi_channel) - 1;
+}
+static inline unsigned NI_EXT_RTSI(unsigned rtsi_channel)
+{
+ return NI_USUAL_RTSI_SELECT(rtsi_channel) - 1;
+}
/* status bits for INSN_CONFIG_GET_COUNTER_STATUS */
- enum comedi_counter_status_flags {
- COMEDI_COUNTER_ARMED = 0x1,
- COMEDI_COUNTER_COUNTING = 0x2,
- COMEDI_COUNTER_TERMINAL_COUNT = 0x4,
- };
+enum comedi_counter_status_flags {
+ COMEDI_COUNTER_ARMED = 0x1,
+ COMEDI_COUNTER_COUNTING = 0x2,
+ COMEDI_COUNTER_TERMINAL_COUNT = 0x4,
+};
/* Clock sources for CDIO subdevice on NI m-series boards. Used as the
* scan_begin_arg for a comedi_command. These sources may also be bitwise-or'd
* with CR_INVERT to change polarity. */
- enum ni_m_series_cdio_scan_begin_src {
- NI_CDIO_SCAN_BEGIN_SRC_GROUND = 0,
- NI_CDIO_SCAN_BEGIN_SRC_AI_START = 18,
- NI_CDIO_SCAN_BEGIN_SRC_AI_CONVERT = 19,
- NI_CDIO_SCAN_BEGIN_SRC_PXI_STAR_TRIGGER = 20,
- NI_CDIO_SCAN_BEGIN_SRC_G0_OUT = 28,
- NI_CDIO_SCAN_BEGIN_SRC_G1_OUT = 29,
- NI_CDIO_SCAN_BEGIN_SRC_ANALOG_TRIGGER = 30,
- NI_CDIO_SCAN_BEGIN_SRC_AO_UPDATE = 31,
- NI_CDIO_SCAN_BEGIN_SRC_FREQ_OUT = 32,
- NI_CDIO_SCAN_BEGIN_SRC_DIO_CHANGE_DETECT_IRQ = 33
- };
- static inline unsigned NI_CDIO_SCAN_BEGIN_SRC_PFI(unsigned pfi_channel)
- {
- return NI_USUAL_PFI_SELECT(pfi_channel);
- }
- static inline unsigned
- NI_CDIO_SCAN_BEGIN_SRC_RTSI(unsigned rtsi_channel)
- {
- return NI_USUAL_RTSI_SELECT(rtsi_channel);
- }
+enum ni_m_series_cdio_scan_begin_src {
+ NI_CDIO_SCAN_BEGIN_SRC_GROUND = 0,
+ NI_CDIO_SCAN_BEGIN_SRC_AI_START = 18,
+ NI_CDIO_SCAN_BEGIN_SRC_AI_CONVERT = 19,
+ NI_CDIO_SCAN_BEGIN_SRC_PXI_STAR_TRIGGER = 20,
+ NI_CDIO_SCAN_BEGIN_SRC_G0_OUT = 28,
+ NI_CDIO_SCAN_BEGIN_SRC_G1_OUT = 29,
+ NI_CDIO_SCAN_BEGIN_SRC_ANALOG_TRIGGER = 30,
+ NI_CDIO_SCAN_BEGIN_SRC_AO_UPDATE = 31,
+ NI_CDIO_SCAN_BEGIN_SRC_FREQ_OUT = 32,
+ NI_CDIO_SCAN_BEGIN_SRC_DIO_CHANGE_DETECT_IRQ = 33
+};
+static inline unsigned NI_CDIO_SCAN_BEGIN_SRC_PFI(unsigned pfi_channel)
+{
+ return NI_USUAL_PFI_SELECT(pfi_channel);
+}
+static inline unsigned NI_CDIO_SCAN_BEGIN_SRC_RTSI(unsigned rtsi_channel)
+{
+ return NI_USUAL_RTSI_SELECT(rtsi_channel);
+}
/* scan_begin_src for scan_begin_arg==TRIG_EXT with analog output command on NI
* boards. These scan begin sources can also be bitwise-or'd with CR_INVERT to
* change polarity. */
- static inline unsigned NI_AO_SCAN_BEGIN_SRC_PFI(unsigned pfi_channel)
- {
- return NI_USUAL_PFI_SELECT(pfi_channel);
- }
- static inline unsigned NI_AO_SCAN_BEGIN_SRC_RTSI(unsigned rtsi_channel)
- {
- return NI_USUAL_RTSI_SELECT(rtsi_channel);
- }
+static inline unsigned NI_AO_SCAN_BEGIN_SRC_PFI(unsigned pfi_channel)
+{
+ return NI_USUAL_PFI_SELECT(pfi_channel);
+}
+static inline unsigned NI_AO_SCAN_BEGIN_SRC_RTSI(unsigned rtsi_channel)
+{
+ return NI_USUAL_RTSI_SELECT(rtsi_channel);
+}
/* Bits for setting a clock source with
* INSN_CONFIG_SET_CLOCK_SRC when using NI frequency output subdevice. */
- enum ni_freq_out_clock_source_bits {
- NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC, /* 10 MHz */
- NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC /* 100 KHz */
- };
+enum ni_freq_out_clock_source_bits {
+ NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC, /* 10 MHz */
+ NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC /* 100 KHz */
+};
/* Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for
* 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
- enum amplc_dio_clock_source {
- AMPLC_DIO_CLK_CLKN, /* per channel external clock
- input/output pin (pin is only an
- input when clock source set to this
- value, otherwise it is an output) */
- AMPLC_DIO_CLK_10MHZ, /* 10 MHz internal clock */
- AMPLC_DIO_CLK_1MHZ, /* 1 MHz internal clock */
- AMPLC_DIO_CLK_100KHZ, /* 100 kHz internal clock */
- AMPLC_DIO_CLK_10KHZ, /* 10 kHz internal clock */
- AMPLC_DIO_CLK_1KHZ, /* 1 kHz internal clock */
- AMPLC_DIO_CLK_OUTNM1, /* output of preceding counter channel
- (for channel 0, preceding counter
- channel is channel 2 on preceding
- counter subdevice, for first counter
- subdevice, preceding counter
- subdevice is the last counter
- subdevice) */
- AMPLC_DIO_CLK_EXT /* per chip external input pin */
- };
+enum amplc_dio_clock_source {
+ AMPLC_DIO_CLK_CLKN, /* per channel external clock
+ input/output pin (pin is only an
+ input when clock source set to this
+ value, otherwise it is an output) */
+ AMPLC_DIO_CLK_10MHZ, /* 10 MHz internal clock */
+ AMPLC_DIO_CLK_1MHZ, /* 1 MHz internal clock */
+ AMPLC_DIO_CLK_100KHZ, /* 100 kHz internal clock */
+ AMPLC_DIO_CLK_10KHZ, /* 10 kHz internal clock */
+ AMPLC_DIO_CLK_1KHZ, /* 1 kHz internal clock */
+ AMPLC_DIO_CLK_OUTNM1, /* output of preceding counter channel
+ (for channel 0, preceding counter
+ channel is channel 2 on preceding
+ counter subdevice, for first counter
+ subdevice, preceding counter
+ subdevice is the last counter
+ subdevice) */
+ AMPLC_DIO_CLK_EXT /* per chip external input pin */
+};
/* Values for setting a gate source with INSN_CONFIG_SET_GATE_SRC for
* 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). */
- enum amplc_dio_gate_source {
- AMPLC_DIO_GAT_VCC, /* internal high logic level */
- AMPLC_DIO_GAT_GND, /* internal low logic level */
- AMPLC_DIO_GAT_GATN, /* per channel external gate input */
- AMPLC_DIO_GAT_NOUTNM2, /* negated output of counter channel
- minus 2 (for channels 0 or 1,
- channel minus 2 is channel 1 or 2 on
- the preceding counter subdevice, for
- the first counter subdevice the
- preceding counter subdevice is the
- last counter subdevice) */
- AMPLC_DIO_GAT_RESERVED4,
- AMPLC_DIO_GAT_RESERVED5,
- AMPLC_DIO_GAT_RESERVED6,
- AMPLC_DIO_GAT_RESERVED7
- };
+enum amplc_dio_gate_source {
+ AMPLC_DIO_GAT_VCC, /* internal high logic level */
+ AMPLC_DIO_GAT_GND, /* internal low logic level */
+ AMPLC_DIO_GAT_GATN, /* per channel external gate input */
+ AMPLC_DIO_GAT_NOUTNM2, /* negated output of counter channel
+ minus 2 (for channels 0 or 1,
+ channel minus 2 is channel 1 or 2 on
+ the preceding counter subdevice, for
+ the first counter subdevice the
+ preceding counter subdevice is the
+ last counter subdevice) */
+ AMPLC_DIO_GAT_RESERVED4,
+ AMPLC_DIO_GAT_RESERVED5,
+ AMPLC_DIO_GAT_RESERVED6,
+ AMPLC_DIO_GAT_RESERVED7
+};
#endif /* _COMEDI_H */
diff --git a/drivers/staging/comedi/comedi_fops.c b/drivers/staging/comedi/comedi_fops.c
index e82126407e95..c2a32cf95a82 100644
--- a/drivers/staging/comedi/comedi_fops.c
+++ b/drivers/staging/comedi/comedi_fops.c
@@ -370,7 +370,8 @@ static int do_devconfig_ioctl(struct comedi_device *dev,
return -ENOMEM;
if (copy_from_user(aux_data,
- comedi_aux_data(it.options, 0), aux_len)) {
+ (unsigned char __user *
+ )comedi_aux_data(it.options, 0), aux_len)) {
vfree(aux_data);
return -EFAULT;
}
@@ -426,7 +427,7 @@ static int do_bufconfig_ioctl(struct comedi_device *dev,
if (bc.subdevice >= dev->n_subdevices || bc.subdevice < 0)
return -EINVAL;
- s = dev->subdevices + bc.subdevice;
+ s = &dev->subdevices[bc.subdevice];
async = s->async;
if (!async) {
@@ -539,7 +540,7 @@ static int do_subdinfo_ioctl(struct comedi_device *dev,
/* fill subdinfo structs */
for (i = 0; i < dev->n_subdevices; i++) {
- s = dev->subdevices + i;
+ s = &dev->subdevices[i];
us = tmp + i;
us->type = s->type;
@@ -617,7 +618,7 @@ static int do_chaninfo_ioctl(struct comedi_device *dev,
if (it.subdev >= dev->n_subdevices)
return -EINVAL;
- s = dev->subdevices + it.subdev;
+ s = &dev->subdevices[it.subdev];
if (it.maxdata_list) {
if (s->maxdata || !s->maxdata_list)
@@ -685,7 +686,7 @@ static int do_bufinfo_ioctl(struct comedi_device *dev,
if (bi.subdevice >= dev->n_subdevices || bi.subdevice < 0)
return -EINVAL;
- s = dev->subdevices + bi.subdevice;
+ s = &dev->subdevices[bi.subdevice];
if (s->lock && s->lock != file)
return -EACCES;
@@ -882,14 +883,12 @@ static int check_insn_config_length(struct comedi_insn *insn,
/* by default we allow the insn since we don't have checks for
* all possible cases yet */
default:
- printk(KERN_WARNING
- "comedi: no check for data length of config insn id "
- "%i is implemented.\n"
- " Add a check to %s in %s.\n"
- " Assuming n=%i is correct.\n", data[0], __func__,
- __FILE__, insn->n);
+ pr_warn("comedi: No check for data length of config insn id %i is implemented.\n",
+ data[0]);
+ pr_warn("comedi: Add a check to %s in %s.\n",
+ __func__, __FILE__);
+ pr_warn("comedi: Assuming n=%i is correct.\n", insn->n);
return 0;
- break;
}
return -EINVAL;
}
@@ -940,7 +939,7 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
ret = -EINVAL;
break;
}
- s = dev->subdevices + insn->subdev;
+ s = &dev->subdevices[insn->subdev];
if (!s->async) {
DPRINTK("no async\n");
ret = -EINVAL;
@@ -951,7 +950,7 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
ret = -EAGAIN;
break;
}
- ret = s->async->inttrig(dev, s, insn->data[0]);
+ ret = s->async->inttrig(dev, s, data[0]);
if (ret >= 0)
ret = 1;
break;
@@ -969,7 +968,7 @@ static int parse_insn(struct comedi_device *dev, struct comedi_insn *insn,
ret = -EINVAL;
goto out;
}
- s = dev->subdevices + insn->subdev;
+ s = &dev->subdevices[insn->subdev];
if (s->type == COMEDI_SUBD_UNUSED) {
DPRINTK("%d not usable subdevice\n", insn->subdev);
@@ -1133,37 +1132,37 @@ static void comedi_set_subdevice_runflags(struct comedi_subdevice *s,
}
static int do_cmd_ioctl(struct comedi_device *dev,
- struct comedi_cmd __user *cmd, void *file)
+ struct comedi_cmd __user *arg, void *file)
{
- struct comedi_cmd user_cmd;
+ struct comedi_cmd cmd;
struct comedi_subdevice *s;
struct comedi_async *async;
int ret = 0;
- unsigned int __user *chanlist_saver = NULL;
+ unsigned int __user *user_chanlist;
- if (copy_from_user(&user_cmd, cmd, sizeof(struct comedi_cmd))) {
+ if (copy_from_user(&cmd, arg, sizeof(struct comedi_cmd))) {
DPRINTK("bad cmd address\n");
return -EFAULT;
}
/* save user's chanlist pointer so it can be restored later */
- chanlist_saver = user_cmd.chanlist;
+ user_chanlist = (unsigned int __user *)cmd.chanlist;
- if (user_cmd.subdev >= dev->n_subdevices) {
- DPRINTK("%d no such subdevice\n", user_cmd.subdev);
+ if (cmd.subdev >= dev->n_subdevices) {
+ DPRINTK("%d no such subdevice\n", cmd.subdev);
return -ENODEV;
}
- s = dev->subdevices + user_cmd.subdev;
+ s = &dev->subdevices[cmd.subdev];
async = s->async;
if (s->type == COMEDI_SUBD_UNUSED) {
- DPRINTK("%d not valid subdevice\n", user_cmd.subdev);
+ DPRINTK("%d not valid subdevice\n", cmd.subdev);
return -EIO;
}
if (!s->do_cmd || !s->do_cmdtest || !s->async) {
DPRINTK("subdevice %i does not support commands\n",
- user_cmd.subdev);
+ cmd.subdev);
return -EIO;
}
@@ -1181,23 +1180,22 @@ static int do_cmd_ioctl(struct comedi_device *dev,
s->busy = file;
/* make sure channel/gain list isn't too long */
- if (user_cmd.chanlist_len > s->len_chanlist) {
+ if (cmd.chanlist_len > s->len_chanlist) {
DPRINTK("channel/gain list too long %u > %d\n",
- user_cmd.chanlist_len, s->len_chanlist);
+ cmd.chanlist_len, s->len_chanlist);
ret = -EINVAL;
goto cleanup;
}
/* make sure channel/gain list isn't too short */
- if (user_cmd.chanlist_len < 1) {
+ if (cmd.chanlist_len < 1) {
DPRINTK("channel/gain list too short %u < 1\n",
- user_cmd.chanlist_len);
+ cmd.chanlist_len);
ret = -EINVAL;
goto cleanup;
}
- kfree(async->cmd.chanlist);
- async->cmd = user_cmd;
+ async->cmd = cmd;
async->cmd.data = NULL;
/* load channel/gain list */
async->cmd.chanlist =
@@ -1208,7 +1206,7 @@ static int do_cmd_ioctl(struct comedi_device *dev,
goto cleanup;
}
- if (copy_from_user(async->cmd.chanlist, user_cmd.chanlist,
+ if (copy_from_user(async->cmd.chanlist, user_chanlist,
async->cmd.chanlist_len * sizeof(int))) {
DPRINTK("fault reading chanlist\n");
ret = -EFAULT;
@@ -1228,11 +1226,11 @@ static int do_cmd_ioctl(struct comedi_device *dev,
if (async->cmd.flags & TRIG_BOGUS || ret) {
DPRINTK("test returned %d\n", ret);
- user_cmd = async->cmd;
+ cmd = async->cmd;
/* restore chanlist pointer before copying back */
- user_cmd.chanlist = chanlist_saver;
- user_cmd.data = NULL;
- if (copy_to_user(cmd, &user_cmd, sizeof(struct comedi_cmd))) {
+ cmd.chanlist = (unsigned int __force *)user_chanlist;
+ cmd.data = NULL;
+ if (copy_to_user(arg, &cmd, sizeof(struct comedi_cmd))) {
DPRINTK("fault writing cmd\n");
ret = -EFAULT;
goto cleanup;
@@ -1285,77 +1283,77 @@ cleanup:
static int do_cmdtest_ioctl(struct comedi_device *dev,
struct comedi_cmd __user *arg, void *file)
{
- struct comedi_cmd user_cmd;
+ struct comedi_cmd cmd;
struct comedi_subdevice *s;
int ret = 0;
unsigned int *chanlist = NULL;
- unsigned int __user *chanlist_saver = NULL;
+ unsigned int __user *user_chanlist;
- if (copy_from_user(&user_cmd, arg, sizeof(struct comedi_cmd))) {
+ if (copy_from_user(&cmd, arg, sizeof(struct comedi_cmd))) {
DPRINTK("bad cmd address\n");
return -EFAULT;
}
/* save user's chanlist pointer so it can be restored later */
- chanlist_saver = user_cmd.chanlist;
+ user_chanlist = (unsigned int __user *)cmd.chanlist;
- if (user_cmd.subdev >= dev->n_subdevices) {
- DPRINTK("%d no such subdevice\n", user_cmd.subdev);
+ if (cmd.subdev >= dev->n_subdevices) {
+ DPRINTK("%d no such subdevice\n", cmd.subdev);
return -ENODEV;
}
- s = dev->subdevices + user_cmd.subdev;
+ s = &dev->subdevices[cmd.subdev];
if (s->type == COMEDI_SUBD_UNUSED) {
- DPRINTK("%d not valid subdevice\n", user_cmd.subdev);
+ DPRINTK("%d not valid subdevice\n", cmd.subdev);
return -EIO;
}
if (!s->do_cmd || !s->do_cmdtest) {
DPRINTK("subdevice %i does not support commands\n",
- user_cmd.subdev);
+ cmd.subdev);
return -EIO;
}
/* make sure channel/gain list isn't too long */
- if (user_cmd.chanlist_len > s->len_chanlist) {
+ if (cmd.chanlist_len > s->len_chanlist) {
DPRINTK("channel/gain list too long %d > %d\n",
- user_cmd.chanlist_len, s->len_chanlist);
+ cmd.chanlist_len, s->len_chanlist);
ret = -EINVAL;
goto cleanup;
}
/* load channel/gain list */
- if (user_cmd.chanlist) {
+ if (cmd.chanlist) {
chanlist =
- kmalloc(user_cmd.chanlist_len * sizeof(int), GFP_KERNEL);
+ kmalloc(cmd.chanlist_len * sizeof(int), GFP_KERNEL);
if (!chanlist) {
DPRINTK("allocation failed\n");
ret = -ENOMEM;
goto cleanup;
}
- if (copy_from_user(chanlist, user_cmd.chanlist,
- user_cmd.chanlist_len * sizeof(int))) {
+ if (copy_from_user(chanlist, user_chanlist,
+ cmd.chanlist_len * sizeof(int))) {
DPRINTK("fault reading chanlist\n");
ret = -EFAULT;
goto cleanup;
}
/* make sure each element in channel/gain list is valid */
- ret = comedi_check_chanlist(s, user_cmd.chanlist_len, chanlist);
+ ret = comedi_check_chanlist(s, cmd.chanlist_len, chanlist);
if (ret < 0) {
DPRINTK("bad chanlist\n");
goto cleanup;
}
- user_cmd.chanlist = chanlist;
+ cmd.chanlist = chanlist;
}
- ret = s->do_cmdtest(dev, s, &user_cmd);
+ ret = s->do_cmdtest(dev, s, &cmd);
/* restore chanlist pointer before copying back */
- user_cmd.chanlist = chanlist_saver;
+ cmd.chanlist = (unsigned int __force *)user_chanlist;
- if (copy_to_user(arg, &user_cmd, sizeof(struct comedi_cmd))) {
+ if (copy_to_user(arg, &cmd, sizeof(struct comedi_cmd))) {
DPRINTK("bad cmd address\n");
ret = -EFAULT;
goto cleanup;
@@ -1390,7 +1388,7 @@ static int do_lock_ioctl(struct comedi_device *dev, unsigned int arg,
if (arg >= dev->n_subdevices)
return -EINVAL;
- s = dev->subdevices + arg;
+ s = &dev->subdevices[arg];
spin_lock_irqsave(&s->spin_lock, flags);
if (s->busy || s->lock)
@@ -1433,7 +1431,7 @@ static int do_unlock_ioctl(struct comedi_device *dev, unsigned int arg,
if (arg >= dev->n_subdevices)
return -EINVAL;
- s = dev->subdevices + arg;
+ s = &dev->subdevices[arg];
if (s->busy)
return -EBUSY;
@@ -1474,7 +1472,7 @@ static int do_cancel_ioctl(struct comedi_device *dev, unsigned int arg,
if (arg >= dev->n_subdevices)
return -EINVAL;
- s = dev->subdevices + arg;
+ s = &dev->subdevices[arg];
if (s->async == NULL)
return -EINVAL;
@@ -1511,7 +1509,7 @@ static int do_poll_ioctl(struct comedi_device *dev, unsigned int arg,
if (arg >= dev->n_subdevices)
return -EINVAL;
- s = dev->subdevices + arg;
+ s = &dev->subdevices[arg];
if (s->lock && s->lock != file)
return -EACCES;
@@ -2025,7 +2023,8 @@ done:
/*
This function restores a subdevice to an idle state.
*/
-void do_become_nonbusy(struct comedi_device *dev, struct comedi_subdevice *s)
+static void do_become_nonbusy(struct comedi_device *dev,
+ struct comedi_subdevice *s)
{
struct comedi_async *async = s->async;
@@ -2033,9 +2032,11 @@ void do_become_nonbusy(struct comedi_device *dev, struct comedi_subdevice *s)
if (async) {
comedi_reset_async_buf(async);
async->inttrig = NULL;
+ kfree(async->cmd.chanlist);
+ async->cmd.chanlist = NULL;
} else {
- printk(KERN_ERR
- "BUG: (?) do_become_nonbusy called with async=0\n");
+ dev_err(dev->class_dev,
+ "BUG: (?) do_become_nonbusy called with async=NULL\n");
}
s->busy = NULL;
@@ -2140,7 +2141,7 @@ static int comedi_close(struct inode *inode, struct file *file)
if (dev->subdevices) {
for (i = 0; i < dev->n_subdevices; i++) {
- s = dev->subdevices + i;
+ s = &dev->subdevices[i];
if (s->busy == file)
do_cancel(dev, s);
@@ -2211,14 +2212,12 @@ static int __init comedi_init(void)
int i;
int retval;
- printk(KERN_INFO "comedi: version " COMEDI_RELEASE
- " - http://www.comedi.org\n");
+ pr_info("comedi: version " COMEDI_RELEASE " - http://www.comedi.org\n");
if (comedi_num_legacy_minors < 0 ||
comedi_num_legacy_minors > COMEDI_NUM_BOARD_MINORS) {
- printk(KERN_ERR "comedi: error: invalid value for module "
- "parameter \"comedi_num_legacy_minors\". Valid values "
- "are 0 through %i.\n", COMEDI_NUM_BOARD_MINORS);
+ pr_err("comedi: error: invalid value for module parameter \"comedi_num_legacy_minors\". Valid values are 0 through %i.\n",
+ COMEDI_NUM_BOARD_MINORS);
return -EINVAL;
}
@@ -2247,7 +2246,7 @@ static int __init comedi_init(void)
}
comedi_class = class_create(THIS_MODULE, "comedi");
if (IS_ERR(comedi_class)) {
- printk(KERN_ERR "comedi: failed to create class");
+ pr_err("comedi: failed to create class\n");
cdev_del(&comedi_cdev);
unregister_chrdev_region(MKDEV(COMEDI_MAJOR, 0),
COMEDI_NUM_MINORS);
@@ -2295,8 +2294,7 @@ module_exit(comedi_cleanup);
void comedi_error(const struct comedi_device *dev, const char *s)
{
- printk(KERN_ERR "comedi%d: %s: %s\n", dev->minor,
- dev->driver->driver_name, s);
+ dev_err(dev->class_dev, "%s: %s\n", dev->driver->driver_name, s);
}
EXPORT_SYMBOL(comedi_error);
@@ -2364,7 +2362,7 @@ static int is_device_busy(struct comedi_device *dev)
return 0;
for (i = 0; i < dev->n_subdevices; i++) {
- s = dev->subdevices + i;
+ s = &dev->subdevices[i];
if (s->busy)
return 1;
if (s->async && s->async->mmap_count)
@@ -2420,9 +2418,7 @@ int comedi_alloc_board_minor(struct device *hardware_device)
comedi_device_cleanup(info->device);
kfree(info->device);
kfree(info);
- printk(KERN_ERR
- "comedi: error: "
- "ran out of minor numbers for board device files.\n");
+ pr_err("comedi: error: ran out of minor numbers for board device files.\n");
return -EBUSY;
}
info->device->minor = i;
@@ -2499,9 +2495,7 @@ int comedi_alloc_subdevice_minor(struct comedi_device *dev,
spin_unlock(&comedi_file_info_table_lock);
if (i == COMEDI_NUM_MINORS) {
kfree(info);
- printk(KERN_ERR
- "comedi: error: "
- "ran out of minor numbers for board device files.\n");
+ pr_err("comedi: error: ran out of minor numbers for board device files.\n");
return -EBUSY;
}
s->minor = i;
diff --git a/drivers/staging/comedi/comedidev.h b/drivers/staging/comedi/comedidev.h
index f713783ef624..cb67a5cb9c82 100644
--- a/drivers/staging/comedi/comedidev.h
+++ b/drivers/staging/comedi/comedidev.h
@@ -46,7 +46,7 @@
#define DPRINTK(format, args...) do { \
if (comedi_debug) \
- printk(KERN_DEBUG "comedi: " format , ## args); \
+ pr_debug("comedi: " format, ## args); \
} while (0)
#define COMEDI_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c
index 2359151af7e1..1db6bfdbf13b 100644
--- a/drivers/staging/comedi/drivers.c
+++ b/drivers/staging/comedi/drivers.c
@@ -71,7 +71,7 @@ int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices)
dev->n_subdevices = num_subdevices;
for (i = 0; i < num_subdevices; ++i) {
- s = dev->subdevices + i;
+ s = &dev->subdevices[i];
s->device = dev;
s->async_dma_dir = DMA_NONE;
spin_lock_init(&s->spin_lock);
@@ -88,7 +88,7 @@ static void cleanup_device(struct comedi_device *dev)
if (dev->subdevices) {
for (i = 0; i < dev->n_subdevices; i++) {
- s = dev->subdevices + i;
+ s = &dev->subdevices[i];
comedi_free_subdevice_minor(s);
if (s->async) {
comedi_buf_alloc(dev, s, 0);
@@ -119,8 +119,8 @@ static void __comedi_device_detach(struct comedi_device *dev)
if (dev->driver)
dev->driver->detach(dev);
else
- printk(KERN_WARNING
- "BUG: dev->driver=NULL in comedi_device_detach()\n");
+ dev_warn(dev->class_dev,
+ "BUG: dev->driver=NULL in comedi_device_detach()\n");
cleanup_device(dev);
}
@@ -142,8 +142,7 @@ static int comedi_device_postconfig(struct comedi_device *dev)
return ret;
}
if (!dev->board_name) {
- printk(KERN_WARNING "BUG: dev->board_name=<%p>\n",
- dev->board_name);
+ dev_warn(dev->class_dev, "BUG: dev->board_name=NULL\n");
dev->board_name = "BUG";
}
smp_wmb();
@@ -160,10 +159,8 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return -EBUSY;
for (driv = comedi_drivers; driv; driv = driv->next) {
- if (!try_module_get(driv->module)) {
- printk(KERN_INFO "comedi: failed to increment module count, skipping\n");
+ if (!try_module_get(driv->module))
continue;
- }
if (driv->num_names) {
dev->board_ptr = comedi_recognize(driv, it->board_name);
if (dev->board_ptr)
@@ -176,16 +173,21 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
/* recognize has failed if we get here */
/* report valid board names before returning error */
for (driv = comedi_drivers; driv; driv = driv->next) {
- if (!try_module_get(driv->module)) {
- printk(KERN_INFO
- "comedi: failed to increment module count\n");
+ if (!try_module_get(driv->module))
continue;
- }
comedi_report_boards(driv);
module_put(driv->module);
}
return -EIO;
}
+ if (driv->attach == NULL) {
+ /* driver does not support manual configuration */
+ dev_warn(dev->class_dev,
+ "driver '%s' does not support attach using comedi_config\n",
+ driv->driver_name);
+ module_put(driv->module);
+ return -ENOSYS;
+ }
/* initialize dev->driver here so
* comedi_error() can be called from attach */
dev->driver = driv;
@@ -225,8 +227,9 @@ int comedi_driver_unregister(struct comedi_driver *driver)
mutex_lock(&dev->mutex);
if (dev->attached && dev->driver == driver) {
if (dev->use_count)
- printk(KERN_WARNING "BUG! detaching device with use_count=%d\n",
- dev->use_count);
+ dev_warn(dev->class_dev,
+ "BUG! detaching device with use_count=%d\n",
+ dev->use_count);
comedi_device_detach(dev);
}
mutex_unlock(&dev->mutex);
@@ -255,7 +258,7 @@ static int postconfig(struct comedi_device *dev)
int ret;
for (i = 0; i < dev->n_subdevices; i++) {
- s = dev->subdevices + i;
+ s = &dev->subdevices[i];
if (s->type == COMEDI_SUBD_UNUSED)
continue;
@@ -273,8 +276,8 @@ static int postconfig(struct comedi_device *dev)
async =
kzalloc(sizeof(struct comedi_async), GFP_KERNEL);
if (async == NULL) {
- printk(KERN_INFO
- "failed to allocate async struct\n");
+ dev_warn(dev->class_dev,
+ "failed to allocate async struct\n");
return -ENOMEM;
}
init_waitqueue_head(&async->wait_head);
@@ -290,7 +293,8 @@ static int postconfig(struct comedi_device *dev)
async->prealloc_buf = NULL;
async->prealloc_bufsz = 0;
if (comedi_buf_alloc(dev, s, buf_size) < 0) {
- printk(KERN_INFO "Buffer allocation failed\n");
+ dev_warn(dev->class_dev,
+ "Buffer allocation failed\n");
return -ENOMEM;
}
if (s->buf_change) {
@@ -370,17 +374,17 @@ static void comedi_report_boards(struct comedi_driver *driv)
unsigned int i;
const char *const *name_ptr;
- printk(KERN_INFO "comedi: valid board names for %s driver are:\n",
- driv->driver_name);
+ pr_info("comedi: valid board names for %s driver are:\n",
+ driv->driver_name);
name_ptr = driv->board_name;
for (i = 0; i < driv->num_names; i++) {
- printk(KERN_INFO " %s\n", *name_ptr);
+ pr_info(" %s\n", *name_ptr);
name_ptr = (const char **)((char *)name_ptr + driv->offset);
}
if (driv->num_names == 0)
- printk(KERN_INFO " %s\n", driv->driver_name);
+ pr_info(" %s\n", driv->driver_name);
}
static int poll_invalid(struct comedi_device *dev, struct comedi_subdevice *s)
@@ -411,7 +415,6 @@ static int insn_rw_emulate_bits(struct comedi_device *dev,
new_insn.insn = INSN_BITS;
new_insn.chanspec = base_bitfield_channel;
new_insn.n = 2;
- new_insn.data = new_data;
new_insn.subdev = insn->subdev;
if (insn->insn == INSN_WRITE) {
@@ -584,9 +587,9 @@ static unsigned int comedi_buf_munge(struct comedi_async *async,
block_size = num_bytes - count;
if (block_size < 0) {
- printk(KERN_WARNING
- "%s: %s: bug! block_size is negative\n",
- __FILE__, __func__);
+ dev_warn(s->device->class_dev,
+ "%s: %s: bug! block_size is negative\n",
+ __FILE__, __func__);
break;
}
if ((int)(async->munge_ptr + block_size -
@@ -667,7 +670,8 @@ unsigned comedi_buf_write_free(struct comedi_async *async, unsigned int nbytes)
{
if ((int)(async->buf_write_count + nbytes -
async->buf_write_alloc_count) > 0) {
- printk(KERN_INFO "comedi: attempted to write-free more bytes than have been write-allocated.\n");
+ dev_info(async->subdevice->device->class_dev,
+ "attempted to write-free more bytes than have been write-allocated.\n");
nbytes = async->buf_write_alloc_count - async->buf_write_count;
}
async->buf_write_count += nbytes;
@@ -703,8 +707,8 @@ unsigned comedi_buf_read_free(struct comedi_async *async, unsigned int nbytes)
smp_mb();
if ((int)(async->buf_read_count + nbytes -
async->buf_read_alloc_count) > 0) {
- printk(KERN_INFO
- "comedi: attempted to read-free more bytes than have been read-allocated.\n");
+ dev_info(async->subdevice->device->class_dev,
+ "attempted to read-free more bytes than have been read-allocated.\n");
nbytes = async->buf_read_alloc_count - async->buf_read_count;
}
async->buf_read_count += nbytes;
@@ -853,10 +857,9 @@ comedi_auto_config_helper(struct device *hardware_device,
mutex_lock(&comedi_dev->mutex);
if (comedi_dev->attached)
ret = -EBUSY;
- else if (!try_module_get(driver->module)) {
- printk(KERN_INFO "comedi: failed to increment module count\n");
+ else if (!try_module_get(driver->module))
ret = -EIO;
- } else {
+ else {
/* set comedi_dev->driver here for attach wrapper */
comedi_dev->driver = driver;
ret = (*attach_wrapper)(comedi_dev, context);
@@ -884,14 +887,19 @@ static int comedi_auto_config_wrapper(struct comedi_device *dev, void *context)
* has already been copied to it->board_name */
dev->board_ptr = comedi_recognize(driv, it->board_name);
if (dev->board_ptr == NULL) {
- printk(KERN_WARNING
- "comedi: auto config failed to find board entry"
- " '%s' for driver '%s'\n", it->board_name,
- driv->driver_name);
+ dev_warn(dev->class_dev,
+ "auto config failed to find board entry '%s' for driver '%s'\n",
+ it->board_name, driv->driver_name);
comedi_report_boards(driv);
return -EINVAL;
}
}
+ if (!driv->attach) {
+ dev_warn(dev->class_dev,
+ "BUG! driver '%s' using old-style auto config but has no attach handler\n",
+ driv->driver_name);
+ return -EINVAL;
+ }
return driv->attach(dev, it);
}
diff --git a/drivers/staging/comedi/drivers/8253.h b/drivers/staging/comedi/drivers/8253.h
index 3eb45d46e05b..429e0d60c0a3 100644
--- a/drivers/staging/comedi/drivers/8253.h
+++ b/drivers/staging/comedi/drivers/8253.h
@@ -262,8 +262,10 @@ static inline int i8254_load(unsigned long base_address, unsigned int regshift,
return 0;
}
-static inline int i8254_mm_load(void *base_address, unsigned int regshift,
- unsigned int counter_number, unsigned int count,
+static inline int i8254_mm_load(void __iomem *base_address,
+ unsigned int regshift,
+ unsigned int counter_number,
+ unsigned int count,
unsigned int mode)
{
unsigned int byte;
@@ -311,7 +313,8 @@ static inline int i8254_read(unsigned long base_address, unsigned int regshift,
return ret;
}
-static inline int i8254_mm_read(void *base_address, unsigned int regshift,
+static inline int i8254_mm_read(void __iomem *base_address,
+ unsigned int regshift,
unsigned int counter_number)
{
unsigned int byte;
@@ -348,7 +351,7 @@ static inline void i8254_write(unsigned long base_address,
outb(byte, base_address + (counter_number << regshift));
}
-static inline void i8254_mm_write(void *base_address,
+static inline void i8254_mm_write(void __iomem *base_address,
unsigned int regshift,
unsigned int counter_number,
unsigned int count)
@@ -390,7 +393,7 @@ static inline int i8254_set_mode(unsigned long base_address,
return 0;
}
-static inline int i8254_mm_set_mode(void *base_address,
+static inline int i8254_mm_set_mode(void __iomem *base_address,
unsigned int regshift,
unsigned int counter_number,
unsigned int mode)
@@ -419,7 +422,7 @@ static inline int i8254_status(unsigned long base_address,
return inb(base_address + (counter_number << regshift));
}
-static inline int i8254_mm_status(void *base_address,
+static inline int i8254_mm_status(void __iomem *base_address,
unsigned int regshift,
unsigned int counter_number)
{
diff --git a/drivers/staging/comedi/drivers/8255.c b/drivers/staging/comedi/drivers/8255.c
index 4c9977b8a5ae..a256622e2dd7 100644
--- a/drivers/staging/comedi/drivers/8255.c
+++ b/drivers/staging/comedi/drivers/8255.c
@@ -82,6 +82,8 @@ I/O port base address can be found in the output of 'lspci -v'.
#include <linux/ioport.h>
#include <linux/slab.h>
+
+#include "comedi_fc.h"
#include "8255.h"
#define _8255_SIZE 4
@@ -229,39 +231,20 @@ static int subdev_8255_cmdtest(struct comedi_device *dev,
struct comedi_cmd *cmd)
{
int err = 0;
- unsigned int tmp;
-
- /* step 1 */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_FOLLOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
if (err)
return 1;
- /* step 2 */
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -403,7 +386,7 @@ static int dev_8255_attach(struct comedi_device *dev,
return ret;
for (i = 0; i < dev->n_subdevices; i++) {
- s = dev->subdevices + i;
+ s = &dev->subdevices[i];
iobase = it->options[i];
if (!request_region(iobase, _8255_SIZE, "8255")) {
@@ -429,7 +412,7 @@ static void dev_8255_detach(struct comedi_device *dev)
int i;
for (i = 0; i < dev->n_subdevices; i++) {
- s = dev->subdevices + i;
+ s = &dev->subdevices[i];
if (s->type != COMEDI_SUBD_UNUSED) {
spriv = s->private;
release_region(spriv->iobase, _8255_SIZE);
diff --git a/drivers/staging/comedi/drivers/8255_pci.c b/drivers/staging/comedi/drivers/8255_pci.c
new file mode 100644
index 000000000000..7dff3c01dc29
--- /dev/null
+++ b/drivers/staging/comedi/drivers/8255_pci.c
@@ -0,0 +1,353 @@
+/*
+ * COMEDI driver for generic PCI based 8255 digital i/o boards
+ * Copyright (C) 2012 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on the tested adl_pci7296 driver written by:
+ * Jon Grierson <jd@renko.co.uk>
+ * and the experimental cb_pcidio driver written by:
+ * Yoshiya Matsuzaka
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+/*
+Driver: 8255_pci
+Description: Generic PCI based 8255 Digital I/O boards
+Devices: (ADLink) PCI-7224 [adl_pci-7224] - 24 channels
+ (ADLink) PCI-7248 [adl_pci-7248] - 48 channels
+ (ADLink) PCI-7296 [adl_pci-7296] - 96 channels
+ (Measurement Computing) PCI-DIO24 [cb_pci-dio24] - 24 channels
+ (Measurement Computing) PCI-DIO24H [cb_pci-dio24h] - 24 channels
+ (Measurement Computing) PCI-DIO48H [cb_pci-dio48h] - 48 channels
+ (Measurement Computing) PCI-DIO96H [cb_pci-dio96h] - 96 channels
+ (National Instruments) PCI-DIO-96 [ni_pci-dio-96] - 96 channels
+ (National Instruments) PCI-DIO-96B [ni_pci-dio-96b] - 96 channels
+ (National Instruments) PXI-6508 [ni_pxi-6508] - 96 channels
+ (National Instruments) PCI-6503 [ni_pci-6503] - 24 channels
+ (National Instruments) PCI-6503B [ni_pci-6503b] - 24 channels
+ (National Instruments) PCI-6503X [ni_pci-6503x] - 24 channels
+ (National Instruments) PXI-6503 [ni_pxi-6503] - 24 channels
+Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+Updated: Wed, 12 Sep 2012 11:52:01 -0700
+Status: untested
+
+Some of these boards also have an 8254 programmable timer/counter
+chip. This chip is not currently supported by this driver.
+
+Interrupt support for these boards is also not currently supported.
+
+Configuration Options: not applicable, uses PCI auto config
+*/
+
+#include "../comedidev.h"
+
+#include "8255.h"
+
+/*
+ * PCI Device ID's supported by this driver
+ */
+#define PCI_DEVICE_ID_ADLINK_PCI7224 0x7224
+#define PCI_DEVICE_ID_ADLINK_PCI7248 0x7248
+#define PCI_DEVICE_ID_ADLINK_PCI7296 0x7296
+
+/* ComputerBoards is now known as Measurement Computing */
+#define PCI_VENDOR_ID_CB 0x1307
+
+#define PCI_DEVICE_ID_CB_PCIDIO48H 0x000b
+#define PCI_DEVICE_ID_CB_PCIDIO24H 0x0014
+#define PCI_DEVICE_ID_CB_PCIDIO96H 0x0017
+#define PCI_DEVICE_ID_CB_PCIDIO24 0x0028
+
+#define PCI_DEVICE_ID_NI_PCIDIO96 0x0160
+#define PCI_DEVICE_ID_NI_PCI6503 0x0400
+#define PCI_DEVICE_ID_NI_PCI6503B 0x1250
+#define PCI_DEVICE_ID_NI_PXI6508 0x13c0
+#define PCI_DEVICE_ID_NI_PCIDIO96B 0x1630
+#define PCI_DEVICE_ID_NI_PCI6503X 0x17d0
+#define PCI_DEVICE_ID_NI_PXI_6503 0x1800
+
+struct pci_8255_boardinfo {
+ const char *name;
+ unsigned short vendor;
+ unsigned short device;
+ int dio_badr;
+ int is_mmio;
+ int n_8255;
+};
+
+static const struct pci_8255_boardinfo pci_8255_boards[] = {
+ {
+ .name = "adl_pci-7224",
+ .vendor = PCI_VENDOR_ID_ADLINK,
+ .device = PCI_DEVICE_ID_ADLINK_PCI7224,
+ .dio_badr = 2,
+ .n_8255 = 1,
+ }, {
+ .name = "adl_pci-7248",
+ .vendor = PCI_VENDOR_ID_ADLINK,
+ .device = PCI_DEVICE_ID_ADLINK_PCI7248,
+ .dio_badr = 2,
+ .n_8255 = 2,
+ }, {
+ .name = "adl_pci-7296",
+ .vendor = PCI_VENDOR_ID_ADLINK,
+ .device = PCI_DEVICE_ID_ADLINK_PCI7296,
+ .dio_badr = 2,
+ .n_8255 = 4,
+ }, {
+ .name = "cb_pci-dio24",
+ .vendor = PCI_VENDOR_ID_CB,
+ .device = PCI_DEVICE_ID_CB_PCIDIO24,
+ .dio_badr = 2,
+ .n_8255 = 1,
+ }, {
+ .name = "cb_pci-dio24h",
+ .vendor = PCI_VENDOR_ID_CB,
+ .device = PCI_DEVICE_ID_CB_PCIDIO24H,
+ .dio_badr = 2,
+ .n_8255 = 1,
+ }, {
+ .name = "cb_pci-dio48h",
+ .vendor = PCI_VENDOR_ID_CB,
+ .device = PCI_DEVICE_ID_CB_PCIDIO48H,
+ .dio_badr = 1,
+ .n_8255 = 2,
+ }, {
+ .name = "cb_pci-dio96h",
+ .vendor = PCI_VENDOR_ID_CB,
+ .device = PCI_DEVICE_ID_CB_PCIDIO96H,
+ .dio_badr = 2,
+ .n_8255 = 4,
+ }, {
+ .name = "ni_pci-dio-96",
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PCIDIO96,
+ .dio_badr = 1,
+ .is_mmio = 1,
+ .n_8255 = 4,
+ }, {
+ .name = "ni_pci-dio-96b",
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PCIDIO96B,
+ .dio_badr = 1,
+ .is_mmio = 1,
+ .n_8255 = 4,
+ }, {
+ .name = "ni_pxi-6508",
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PXI6508,
+ .dio_badr = 1,
+ .is_mmio = 1,
+ .n_8255 = 4,
+ }, {
+ .name = "ni_pci-6503",
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PCI6503,
+ .dio_badr = 1,
+ .is_mmio = 1,
+ .n_8255 = 1,
+ }, {
+ .name = "ni_pci-6503b",
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PCI6503B,
+ .dio_badr = 1,
+ .is_mmio = 1,
+ .n_8255 = 1,
+ }, {
+ .name = "ni_pci-6503x",
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PCI6503X,
+ .dio_badr = 1,
+ .is_mmio = 1,
+ .n_8255 = 1,
+ }, {
+ .name = "ni_pxi-6503",
+ .vendor = PCI_VENDOR_ID_NI,
+ .device = PCI_DEVICE_ID_NI_PXI_6503,
+ .dio_badr = 1,
+ .is_mmio = 1,
+ .n_8255 = 1,
+ },
+};
+
+struct pci_8255_private {
+ void __iomem *mmio_base;
+};
+
+static int pci_8255_mmio(int dir, int port, int data, unsigned long iobase)
+{
+ void __iomem *mmio_base = (void __iomem *)iobase;
+
+ if (dir) {
+ writeb(data, mmio_base + port);
+ return 0;
+ } else {
+ return readb(mmio_base + port);
+ }
+}
+
+static const void *pci_8255_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ const struct pci_8255_boardinfo *board;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(pci_8255_boards); i++) {
+ board = &pci_8255_boards[i];
+ if (pcidev->vendor == board->vendor &&
+ pcidev->device == board->device)
+ return board;
+ }
+ return NULL;
+}
+
+static int pci_8255_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ const struct pci_8255_boardinfo *board;
+ struct pci_8255_private *devpriv;
+ struct comedi_subdevice *s;
+ resource_size_t iobase;
+ unsigned long len;
+ int ret;
+ int i;
+
+ comedi_set_hw_dev(dev, &pcidev->dev);
+
+ board = pci_8255_find_boardinfo(dev, pcidev);
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret < 0)
+ return ret;
+ devpriv = dev->private;
+
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
+ iobase = pci_resource_start(pcidev, board->dio_badr);
+ len = pci_resource_len(pcidev, board->dio_badr);
+
+ if (board->is_mmio) {
+ devpriv->mmio_base = ioremap(iobase, len);
+ if (!devpriv->mmio_base)
+ return -ENOMEM;
+ }
+ dev->iobase = iobase;
+
+ /*
+ * One, two, or four subdevices are setup by this driver depending
+ * on the number of channels provided by the board. Each subdevice
+ * has 24 channels supported by the 8255 module.
+ */
+ ret = comedi_alloc_subdevices(dev, board->n_8255);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < board->n_8255; i++) {
+ s = &dev->subdevices[i];
+ if (board->is_mmio) {
+ iobase = (unsigned long)(devpriv->mmio_base + (i * 4));
+ ret = subdev_8255_init(dev, s, pci_8255_mmio, iobase);
+ } else {
+ iobase = dev->iobase + (i * 4);
+ ret = subdev_8255_init(dev, s, NULL, iobase);
+ }
+ if (ret)
+ return ret;
+ }
+
+ dev_info(dev->class_dev, "%s attached (%d digital i/o channels)\n",
+ dev->board_name, board->n_8255 * 24);
+
+ return 0;
+}
+
+static void pci_8255_detach(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct pci_8255_boardinfo *board = comedi_board(dev);
+ struct pci_8255_private *devpriv = dev->private;
+ struct comedi_subdevice *s;
+ int i;
+
+ if (dev->subdevices) {
+ for (i = 0; i < board->n_8255; i++) {
+ s = &dev->subdevices[i];
+ subdev_8255_cleanup(dev, s);
+ }
+ }
+ if (pcidev) {
+ if (devpriv->mmio_base)
+ iounmap(devpriv->mmio_base);
+ if (dev->iobase)
+ comedi_pci_disable(pcidev);
+ }
+}
+
+static struct comedi_driver pci_8255_driver = {
+ .driver_name = "8255_pci",
+ .module = THIS_MODULE,
+ .attach_pci = pci_8255_attach_pci,
+ .detach = pci_8255_detach,
+};
+
+static int __devinit pci_8255_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ return comedi_pci_auto_config(dev, &pci_8255_driver);
+}
+
+static void __devexit pci_8255_pci_remove(struct pci_dev *dev)
+{
+ comedi_pci_auto_unconfig(dev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(pci_8255_pci_table) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_ADLINK_PCI7224) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_ADLINK_PCI7248) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_ADLINK_PCI7296) },
+ { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_CB_PCIDIO24) },
+ { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_CB_PCIDIO24H) },
+ { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_CB_PCIDIO48H) },
+ { PCI_DEVICE(PCI_VENDOR_ID_CB, PCI_DEVICE_ID_CB_PCIDIO96H) },
+ { PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCIDIO96) },
+ { PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCIDIO96B) },
+ { PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI6508) },
+ { PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI6503) },
+ { PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI6503B) },
+ { PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PCI6503X) },
+ { PCI_DEVICE(PCI_VENDOR_ID_NI, PCI_DEVICE_ID_NI_PXI_6503) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, pci_8255_pci_table);
+
+static struct pci_driver pci_8255_pci_driver = {
+ .name = "8255_pci",
+ .id_table = pci_8255_pci_table,
+ .probe = pci_8255_pci_probe,
+ .remove = __devexit_p(pci_8255_pci_remove),
+};
+module_comedi_pci_driver(pci_8255_driver, pci_8255_pci_driver);
+
+MODULE_DESCRIPTION("COMEDI - Generic PCI based 8255 Digital I/O boards");
+MODULE_AUTHOR("Comedi http://www.comedi.org");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/Makefile b/drivers/staging/comedi/drivers/Makefile
index 57b19e44d867..a2787c0ca327 100644
--- a/drivers/staging/comedi/drivers/Makefile
+++ b/drivers/staging/comedi/drivers/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_COMEDI_MULTIQ3) += multiq3.o
obj-$(CONFIG_COMEDI_POC) += poc.o
# Comedi PCI drivers
+obj-$(CONFIG_COMEDI_8255_PCI) += 8255_pci.o
obj-$(CONFIG_COMEDI_ADDI_APCI_035) += addi_apci_035.o
obj-$(CONFIG_COMEDI_ADDI_APCI_1032) += addi_apci_1032.o
obj-$(CONFIG_COMEDI_ADDI_APCI_1500) += addi_apci_1500.o
@@ -69,9 +70,7 @@ obj-$(CONFIG_COMEDI_ADDI_APCI_3120) += addi_apci_3120.o
obj-$(CONFIG_COMEDI_ADDI_APCI_3501) += addi_apci_3501.o
obj-$(CONFIG_COMEDI_ADDI_APCI_3XXX) += addi_apci_3xxx.o
obj-$(CONFIG_COMEDI_ADL_PCI6208) += adl_pci6208.o
-obj-$(CONFIG_COMEDI_ADL_PCI7230) += adl_pci7230.o
-obj-$(CONFIG_COMEDI_ADL_PCI7296) += adl_pci7296.o
-obj-$(CONFIG_COMEDI_ADL_PCI7432) += adl_pci7432.o
+obj-$(CONFIG_COMEDI_ADL_PCI7X3X) += adl_pci7x3x.o
obj-$(CONFIG_COMEDI_ADL_PCI8164) += adl_pci8164.o
obj-$(CONFIG_COMEDI_ADL_PCI9111) += adl_pci9111.o
obj-$(CONFIG_COMEDI_ADL_PCI9118) += adl_pci9118.o
@@ -96,7 +95,6 @@ obj-$(CONFIG_COMEDI_KE_COUNTER) += ke_counter.o
obj-$(CONFIG_COMEDI_CB_PCIDAS64) += cb_pcidas64.o
obj-$(CONFIG_COMEDI_CB_PCIDAS) += cb_pcidas.o
obj-$(CONFIG_COMEDI_CB_PCIDDA) += cb_pcidda.o
-obj-$(CONFIG_COMEDI_CB_PCIDIO) += cb_pcidio.o
obj-$(CONFIG_COMEDI_CB_PCIMDAS) += cb_pcimdas.o
obj-$(CONFIG_COMEDI_CB_PCIMDDA) += cb_pcimdda.o
obj-$(CONFIG_COMEDI_ME4000) += me4000.o
diff --git a/drivers/staging/comedi/drivers/acl7225b.c b/drivers/staging/comedi/drivers/acl7225b.c
index ddba5db1e2e1..28c7fd3a96b6 100644
--- a/drivers/staging/comedi/drivers/acl7225b.c
+++ b/drivers/staging/comedi/drivers/acl7225b.c
@@ -81,7 +81,7 @@ static int acl7225b_attach(struct comedi_device *dev,
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* Relays outputs */
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
@@ -91,7 +91,7 @@ static int acl7225b_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->private = (void *)ACL7225_RIO_LO;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* Relays status */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
@@ -101,7 +101,7 @@ static int acl7225b_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->private = (void *)ACL7225_RIO_LO;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* Isolated digital inputs */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
diff --git a/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.c b/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.c
deleted file mode 100644
index b973095146f9..000000000000
--- a/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.c
+++ /dev/null
@@ -1,195 +0,0 @@
-/**
-@verbatim
-
-Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
-
- ADDI-DATA GmbH
- Dieselstrasse 3
- D-77833 Ottersweier
- Tel: +19(0)7223/9493-0
- Fax: +49(0)7223/9493-92
- http://www.addi-data.com
- info@addi-data.com
-
-This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
-
-This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
-You should also find the complete GPL in the COPYING file accompanying this source code.
-
-@endverbatim
-*/
-/*
- +-----------------------------------------------------------------------+
- | (C) ADDI-DATA GmbH Dieselstraße 3 D-77833 Ottersweier |
- +-----------------------------------------------------------------------+
- | Tel : +49 (0) 7223/9493-0 | email : info@addi-data.com |
- | Fax : +49 (0) 7223/9493-92 | Internet : http://www.addi-data.com |
- +-------------------------------+---------------------------------------+
- | Project : ADDI HEADER READ WRITER | Compiler : Visual C++ |
- | Module name : S5920.cpp | Version : 6.0 |
- +-------------------------------+---------------------------------------+
- | Author : E. LIBS Date : 02/05/2002 |
- +-----------------------------------------------------------------------+
- | Description : DLL with the S5920 PCI Controller functions |
- +-----------------------------------------------------------------------+
- | UPDATE'S |
- +-----------------------------------------------------------------------+
- | Date | Author | Description of updates |
- +----------+-----------+------------------------------------------------+
- | 28/08/02 | LIBS Eric | Add return codes each time a function of the |
- | | | Addi Library is called |
- +-----------------------------------------------------------------------+
- | 31/07/03 | KRAUTH J. | Changes for the MSX-Box |
- +-----------------------------------------------------------------------+
-*/
-
-#include "addi_amcc_S5920.h"
-
-/*+----------------------------------------------------------------------------+*/
-/*| Function Name : int i_AddiHeaderRW_ReadEeprom |*/
-/*| (int i_NbOfWordsToRead, |*/
-/*| unsigned int dw_PCIBoardEepromAddress, |*/
-/*| unsigned short w_EepromStartAddress, |*/
-/*| unsigned short * pw_DataRead) |*/
-/*+----------------------------------------------------------------------------+*/
-/*| Task : Read word from the 5920 eeprom. |*/
-/*+----------------------------------------------------------------------------+*/
-/*| Input Parameters : int i_NbOfWordsToRead : Nbr. of word to read |*/
-/*| unsigned int dw_PCIBoardEepromAddress : Address of the eeprom |*/
-/*| unsigned short w_EepromStartAddress : Eeprom start address |*/
-/*+----------------------------------------------------------------------------+*/
-/*| Output Parameters : unsigned short * pw_DataRead : Read data |*/
-/*+----------------------------------------------------------------------------+*/
-/*| Return Value : - |*/
-/*+----------------------------------------------------------------------------+*/
-
-int i_AddiHeaderRW_ReadEeprom(int i_NbOfWordsToRead,
- unsigned int dw_PCIBoardEepromAddress,
- unsigned short w_EepromStartAddress, unsigned short *pw_DataRead)
-{
- unsigned int dw_eeprom_busy = 0;
- int i_Counter = 0;
- int i_WordCounter;
- int i;
- unsigned char pb_ReadByte[1];
- unsigned char b_ReadLowByte = 0;
- unsigned char b_ReadHighByte = 0;
- unsigned char b_SelectedAddressLow = 0;
- unsigned char b_SelectedAddressHigh = 0;
- unsigned short w_ReadWord = 0;
-
- for (i_WordCounter = 0; i_WordCounter < i_NbOfWordsToRead;
- i_WordCounter++) {
- do {
- dw_eeprom_busy =
- inl(dw_PCIBoardEepromAddress +
- AMCC_OP_REG_MCSR);
- dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
- } while (dw_eeprom_busy == EEPROM_BUSY);
-
- for (i_Counter = 0; i_Counter < 2; i_Counter++) {
- b_SelectedAddressLow = (w_EepromStartAddress + i_Counter) % 256; /* Read the low 8 bit part */
- b_SelectedAddressHigh = (w_EepromStartAddress + i_Counter) / 256; /* Read the high 8 bit part */
-
- /* Select the load low address mode */
- outb(NVCMD_LOAD_LOW,
- dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
- 3);
-
- /* Wait on busy */
- do {
- dw_eeprom_busy =
- inl(dw_PCIBoardEepromAddress +
- AMCC_OP_REG_MCSR);
- dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
- } while (dw_eeprom_busy == EEPROM_BUSY);
-
- /* Load the low address */
- outb(b_SelectedAddressLow,
- dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
- 2);
-
- /* Wait on busy */
- do {
- dw_eeprom_busy =
- inl(dw_PCIBoardEepromAddress +
- AMCC_OP_REG_MCSR);
- dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
- } while (dw_eeprom_busy == EEPROM_BUSY);
-
- /* Select the load high address mode */
- outb(NVCMD_LOAD_HIGH,
- dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
- 3);
-
- /* Wait on busy */
- do {
- dw_eeprom_busy =
- inl(dw_PCIBoardEepromAddress +
- AMCC_OP_REG_MCSR);
- dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
- } while (dw_eeprom_busy == EEPROM_BUSY);
-
- /* Load the high address */
- outb(b_SelectedAddressHigh,
- dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
- 2);
-
- /* Wait on busy */
- do {
- dw_eeprom_busy =
- inl(dw_PCIBoardEepromAddress +
- AMCC_OP_REG_MCSR);
- dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
- } while (dw_eeprom_busy == EEPROM_BUSY);
-
- /* Select the READ mode */
- outb(NVCMD_BEGIN_READ,
- dw_PCIBoardEepromAddress + AMCC_OP_REG_MCSR +
- 3);
-
- /* Wait on busy */
- do {
- dw_eeprom_busy =
- inl(dw_PCIBoardEepromAddress +
- AMCC_OP_REG_MCSR);
- dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
- } while (dw_eeprom_busy == EEPROM_BUSY);
-
- /* Read data into the EEPROM */
- *pb_ReadByte =
- inb(dw_PCIBoardEepromAddress +
- AMCC_OP_REG_MCSR + 2);
-
- /* Wait on busy */
- do {
- dw_eeprom_busy =
- inl(dw_PCIBoardEepromAddress +
- AMCC_OP_REG_MCSR);
- dw_eeprom_busy = dw_eeprom_busy & EEPROM_BUSY;
- } while (dw_eeprom_busy == EEPROM_BUSY);
-
- /* Select the upper address part */
- if (i_Counter == 0)
- b_ReadLowByte = pb_ReadByte[0];
- else
- b_ReadHighByte = pb_ReadByte[0];
-
- /* Sleep */
- msleep(1);
-
- }
- w_ReadWord =
- (b_ReadLowByte | (((unsigned short)b_ReadHighByte) *
- 256));
-
- pw_DataRead[i_WordCounter] = w_ReadWord;
-
- w_EepromStartAddress += 2; /* to read the next word */
-
- } /* for (...) i_NbOfWordsToRead */
- return 0;
-}
diff --git a/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.h b/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.h
deleted file mode 100644
index 9afdb1357aa9..000000000000
--- a/drivers/staging/comedi/drivers/addi-data/addi_amcc_S5920.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2004,2005 ADDI-DATA GmbH for the source code of this module.
- *
- * ADDI-DATA GmbH
- * Dieselstrasse 3
- * D-77833 Ottersweier
- * Tel: +19(0)7223/9493-0
- * Fax: +49(0)7223/9493-92
- * http://www.addi-data.com
- * info@addi-data.com
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 2 of the License, or (at your option)
- * any later version.
- */
-
-#define AMCC_OP_REG_MCSR 0x3c
-#define EEPROM_BUSY 0x80000000
-#define NVCMD_LOAD_LOW (0x4 << 5) /* nvRam load low command */
-#define NVCMD_LOAD_HIGH (0x5 << 5) /* nvRam load high command */
-#define NVCMD_BEGIN_READ (0x7 << 5) /* nvRam begin read command */
-#define NVCMD_BEGIN_WRITE (0x6 << 5) /* EEPROM begin write command */
-
-int i_AddiHeaderRW_ReadEeprom(int i_NbOfWordsToRead,
- unsigned int dw_PCIBoardEepromAddress,
- unsigned short w_EepromStartAddress, unsigned short *pw_DataRead);
diff --git a/drivers/staging/comedi/drivers/addi-data/addi_common.c b/drivers/staging/comedi/drivers/addi-data/addi_common.c
index a3d4ed25fb0d..99a96bd96716 100644
--- a/drivers/staging/comedi/drivers/addi-data/addi_common.c
+++ b/drivers/staging/comedi/drivers/addi-data/addi_common.c
@@ -1669,7 +1669,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
return ret;
/* Allocate and Initialise AI Subdevice Structures */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
if ((devpriv->s_EeParameters.i_NbrAiChannel)
|| (this_board->i_NbrAiChannelDiff)) {
dev->read_subdev = s;
@@ -1705,7 +1705,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
}
/* Allocate and Initialise AO Subdevice Structures */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
if (devpriv->s_EeParameters.i_NbrAoChannel) {
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
@@ -1720,7 +1720,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->type = COMEDI_SUBD_UNUSED;
}
/* Allocate and Initialise DI Subdevice Structures */
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
if (devpriv->s_EeParameters.i_NbrDiChannel) {
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
@@ -1738,7 +1738,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->type = COMEDI_SUBD_UNUSED;
}
/* Allocate and Initialise DO Subdevice Structures */
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
if (devpriv->s_EeParameters.i_NbrDoChannel) {
s->type = COMEDI_SUBD_DO;
s->subdev_flags =
@@ -1760,7 +1760,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
}
/* Allocate and Initialise Timer Subdevice Structures */
- s = dev->subdevices + 4;
+ s = &dev->subdevices[4];
if (devpriv->s_EeParameters.i_Timer) {
s->type = COMEDI_SUBD_TIMER;
s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
@@ -1778,7 +1778,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
}
/* Allocate and Initialise TTL */
- s = dev->subdevices + 5;
+ s = &dev->subdevices[5];
if (this_board->i_NbrTTLChannel) {
s->type = COMEDI_SUBD_TTLIO;
s->subdev_flags =
@@ -1797,7 +1797,7 @@ static int i_ADDI_Attach(struct comedi_device *dev, struct comedi_devconfig *it)
}
/* EEPROM */
- s = dev->subdevices + 6;
+ s = &dev->subdevices[6];
if (this_board->i_PCIEeprom) {
s->type = COMEDI_SUBD_MEMORY;
s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
diff --git a/drivers/staging/comedi/drivers/addi-data/amcc_s5933_58.h b/drivers/staging/comedi/drivers/addi-data/amcc_s5933_58.h
deleted file mode 100644
index c26c28c31b97..000000000000
--- a/drivers/staging/comedi/drivers/addi-data/amcc_s5933_58.h
+++ /dev/null
@@ -1,453 +0,0 @@
-/*
- Modified by umesh on 16th may 2001
- Modified by sarath on 22nd may 2001
-*/
-
-/*
- comedi/drivers/amcc_s5933_v_58.h
-
- Stuff for AMCC S5933 PCI Controller
-
- Author: Michal Dobes <majkl@tesnet.cz>
-
- Inspirated from general-purpose AMCC S5933 PCI Matchmaker driver
- made by Andrea Cisternino <acister@pcape1.pi.infn.it>
- and as result of espionage from MITE code made by David A. Schleef.
- Thanks to AMCC for their on-line documentation and bus master DMA
- example.
-*/
-
-#ifndef _AMCC_S5933_H_
-#define _AMCC_S5933_H_
-
-#include <linux/pci.h>
-#include "../../comedidev.h"
-
-/***********Added by sarath for compatibility with APCI3120
-
-*************************/
-
-#define FIFO_ADVANCE_ON_BYTE_2 0x20000000 /* written on base0 */
-
-#define AMWEN_ENABLE 0x02 /* added for step 6 dma written on base2 */
-#define A2P_FIFO_WRITE_ENABLE 0x01
-
-#define AGCSTS_TC_ENABLE 0x10000000 /* Added for transfer count enable bit */
-
-/* ADDON RELATED ADDITIONS */
-/* Constant */
-#define APCI3120_ENABLE_TRANSFER_ADD_ON_LOW 0x00
-#define APCI3120_ENABLE_TRANSFER_ADD_ON_HIGH 0x1200
-#define APCI3120_A2P_FIFO_MANAGEMENT 0x04000400L
-#define APCI3120_AMWEN_ENABLE 0x02
-#define APCI3120_A2P_FIFO_WRITE_ENABLE 0x01
-#define APCI3120_FIFO_ADVANCE_ON_BYTE_2 0x20000000L
-#define APCI3120_ENABLE_WRITE_TC_INT 0x00004000L
-#define APCI3120_CLEAR_WRITE_TC_INT 0x00040000L
-#define APCI3120_DISABLE_AMWEN_AND_A2P_FIFO_WRITE 0x0
-#define APCI3120_DISABLE_BUS_MASTER_ADD_ON 0x0
-#define APCI3120_DISABLE_BUS_MASTER_PCI 0x0
-
- /* ADD_ON ::: this needed since apci supports 16 bit interface to add on */
-#define APCI3120_ADD_ON_AGCSTS_LOW 0x3C
-#define APCI3120_ADD_ON_AGCSTS_HIGH APCI3120_ADD_ON_AGCSTS_LOW + 2
-#define APCI3120_ADD_ON_MWAR_LOW 0x24
-#define APCI3120_ADD_ON_MWAR_HIGH APCI3120_ADD_ON_MWAR_LOW + 2
-#define APCI3120_ADD_ON_MWTC_LOW 0x058
-#define APCI3120_ADD_ON_MWTC_HIGH APCI3120_ADD_ON_MWTC_LOW + 2
-
-/* AMCC */
-#define APCI3120_AMCC_OP_MCSR 0x3C
-#define APCI3120_AMCC_OP_REG_INTCSR 0x38
-
-/*******from here all upward definitions are added by sarath */
-
-/****************************************************************************/
-/* AMCC Operation Register Offsets - PCI */
-/****************************************************************************/
-
-#define AMCC_OP_REG_OMB1 0x00
-#define AMCC_OP_REG_OMB2 0x04
-#define AMCC_OP_REG_OMB3 0x08
-#define AMCC_OP_REG_OMB4 0x0c
-#define AMCC_OP_REG_IMB1 0x10
-#define AMCC_OP_REG_IMB2 0x14
-#define AMCC_OP_REG_IMB3 0x18
-#define AMCC_OP_REG_IMB4 0x1c
-#define AMCC_OP_REG_FIFO 0x20
-#define AMCC_OP_REG_MWAR 0x24
-#define AMCC_OP_REG_MWTC 0x28
-#define AMCC_OP_REG_MRAR 0x2c
-#define AMCC_OP_REG_MRTC 0x30
-#define AMCC_OP_REG_MBEF 0x34
-#define AMCC_OP_REG_INTCSR 0x38
-#define AMCC_OP_REG_INTCSR_SRC (AMCC_OP_REG_INTCSR + 2) /* int source */
-#define AMCC_OP_REG_INTCSR_FEC (AMCC_OP_REG_INTCSR + 3) /* FIFO ctrl */
-#define AMCC_OP_REG_MCSR 0x3c
-#define AMCC_OP_REG_MCSR_NVDATA (AMCC_OP_REG_MCSR + 2) /* Data in byte 2 */
-#define AMCC_OP_REG_MCSR_NVCMD (AMCC_OP_REG_MCSR + 3) /* Command in byte 3 */
-
-#define AMCC_FIFO_DEPTH_DWORD 8
-#define AMCC_FIFO_DEPTH_BYTES (8 * sizeof (u32))
-
-/****************************************************************************/
-/* AMCC Operation Registers Size - PCI */
-/****************************************************************************/
-
-#define AMCC_OP_REG_SIZE 64 /* in bytes */
-
-/****************************************************************************/
-/* AMCC Operation Register Offsets - Add-on */
-/****************************************************************************/
-
-#define AMCC_OP_REG_AIMB1 0x00
-#define AMCC_OP_REG_AIMB2 0x04
-#define AMCC_OP_REG_AIMB3 0x08
-#define AMCC_OP_REG_AIMB4 0x0c
-#define AMCC_OP_REG_AOMB1 0x10
-#define AMCC_OP_REG_AOMB2 0x14
-#define AMCC_OP_REG_AOMB3 0x18
-#define AMCC_OP_REG_AOMB4 0x1c
-#define AMCC_OP_REG_AFIFO 0x20
-#define AMCC_OP_REG_AMWAR 0x24
-#define AMCC_OP_REG_APTA 0x28
-#define AMCC_OP_REG_APTD 0x2c
-#define AMCC_OP_REG_AMRAR 0x30
-#define AMCC_OP_REG_AMBEF 0x34
-#define AMCC_OP_REG_AINT 0x38
-#define AMCC_OP_REG_AGCSTS 0x3c
-#define AMCC_OP_REG_AMWTC 0x58
-#define AMCC_OP_REG_AMRTC 0x5c
-
-/****************************************************************************/
-/* AMCC - Add-on General Control/Status Register */
-/****************************************************************************/
-
-#define AGCSTS_CONTROL_MASK 0xfffff000
-#define AGCSTS_NV_ACC_MASK 0xe0000000
-#define AGCSTS_RESET_MASK 0x0e000000
-#define AGCSTS_NV_DA_MASK 0x00ff0000
-#define AGCSTS_BIST_MASK 0x0000f000
-#define AGCSTS_STATUS_MASK 0x000000ff
-#define AGCSTS_TCZERO_MASK 0x000000c0
-#define AGCSTS_FIFO_ST_MASK 0x0000003f
-
-#define AGCSTS_RESET_MBFLAGS 0x08000000
-#define AGCSTS_RESET_P2A_FIFO 0x04000000
-#define AGCSTS_RESET_A2P_FIFO 0x02000000
-#define AGCSTS_RESET_FIFOS (AGCSTS_RESET_A2P_FIFO | AGCSTS_RESET_P2A_FIFO)
-
-#define AGCSTS_A2P_TCOUNT 0x00000080
-#define AGCSTS_P2A_TCOUNT 0x00000040
-
-#define AGCSTS_FS_P2A_EMPTY 0x00000020
-#define AGCSTS_FS_P2A_HALF 0x00000010
-#define AGCSTS_FS_P2A_FULL 0x00000008
-
-#define AGCSTS_FS_A2P_EMPTY 0x00000004
-#define AGCSTS_FS_A2P_HALF 0x00000002
-#define AGCSTS_FS_A2P_FULL 0x00000001
-
-/****************************************************************************/
-/* AMCC - Add-on Interrupt Control/Status Register */
-/****************************************************************************/
-
-#define AINT_INT_MASK 0x00ff0000
-#define AINT_SEL_MASK 0x0000ffff
-#define AINT_IS_ENSEL_MASK 0x00001f1f
-
-#define AINT_INT_ASSERTED 0x00800000
-#define AINT_BM_ERROR 0x00200000
-#define AINT_BIST_INT 0x00100000
-
-#define AINT_RT_COMPLETE 0x00080000
-#define AINT_WT_COMPLETE 0x00040000
-
-#define AINT_OUT_MB_INT 0x00020000
-#define AINT_IN_MB_INT 0x00010000
-
-#define AINT_READ_COMPL 0x00008000
-#define AINT_WRITE_COMPL 0x00004000
-
-#define AINT_OMB_ENABLE 0x00001000
-#define AINT_OMB_SELECT 0x00000c00
-#define AINT_OMB_BYTE 0x00000300
-
-#define AINT_IMB_ENABLE 0x00000010
-#define AINT_IMB_SELECT 0x0000000c
-#define AINT_IMB_BYTE 0x00000003
-
-/* Enable Bus Mastering */
-#define EN_A2P_TRANSFERS 0x00000400
-/* FIFO Flag Reset */
-#define RESET_A2P_FLAGS 0x04000000L
-/* FIFO Relative Priority */
-#define A2P_HI_PRIORITY 0x00000100L
-/* Identify Interrupt Sources */
-#define ANY_S593X_INT 0x00800000L
-#define READ_TC_INT 0x00080000L
-#define WRITE_TC_INT 0x00040000L
-#define IN_MB_INT 0x00020000L
-#define MASTER_ABORT_INT 0x00100000L
-#define TARGET_ABORT_INT 0x00200000L
-#define BUS_MASTER_INT 0x00200000L
-
-/****************************************************************************/
-
-struct pcilst_struct {
- struct pcilst_struct *next;
- int used;
- struct pci_dev *pcidev;
- unsigned short vendor;
- unsigned short device;
- unsigned int master;
- unsigned char pci_bus;
- unsigned char pci_slot;
- unsigned char pci_func;
- unsigned int io_addr[5];
- unsigned int irq;
-};
-
-struct pcilst_struct *amcc_devices; /* ptr to root list of all amcc devices */
-
-/****************************************************************************/
-
-void v_pci_card_list_init(unsigned short pci_vendor, char display);
-void v_pci_card_list_cleanup(unsigned short pci_vendor);
-struct pcilst_struct *ptr_find_free_pci_card_by_device(unsigned short vendor_id,
- unsigned short
- device_id);
-int i_find_free_pci_card_by_position(unsigned short vendor_id,
- unsigned short device_id,
- unsigned short pci_bus,
- unsigned short pci_slot,
- struct pcilst_struct **card);
-struct pcilst_struct *ptr_select_and_alloc_pci_card(unsigned short vendor_id,
- unsigned short device_id,
- unsigned short pci_bus,
- unsigned short pci_slot);
-
-int i_pci_card_alloc(struct pcilst_struct *amcc);
-int i_pci_card_free(struct pcilst_struct *amcc);
-void v_pci_card_list_display(void);
-int i_pci_card_data(struct pcilst_struct *amcc,
- unsigned char *pci_bus, unsigned char *pci_slot,
- unsigned char *pci_func, unsigned short *io_addr,
- unsigned short *irq, unsigned short *master);
-
-/****************************************************************************/
-
-/* build list of amcc cards in this system */
-void v_pci_card_list_init(unsigned short pci_vendor, char display)
-{
- struct pci_dev *pcidev;
- struct pcilst_struct *amcc, *last;
- int i;
-
- amcc_devices = NULL;
- last = NULL;
-
- pci_for_each_dev(pcidev) {
- if (pcidev->vendor == pci_vendor) {
- amcc = kzalloc(sizeof(*amcc), GFP_KERNEL);
- if (amcc == NULL)
- continue;
-
- amcc->pcidev = pcidev;
- if (last) {
- last->next = amcc;
- } else {
- amcc_devices = amcc;
- }
- last = amcc;
-
- amcc->vendor = pcidev->vendor;
- amcc->device = pcidev->device;
-#if 0
- amcc->master = pcidev->master; /* how get this information under 2.4 kernels? */
-#endif
- amcc->pci_bus = pcidev->bus->number;
- amcc->pci_slot = PCI_SLOT(pcidev->devfn);
- amcc->pci_func = PCI_FUNC(pcidev->devfn);
- for (i = 0; i < 5; i++)
- amcc->io_addr[i] =
- pcidev->resource[i].start & ~3UL;
- amcc->irq = pcidev->irq;
- }
- }
-
- if (display)
- v_pci_card_list_display();
-}
-
-/****************************************************************************/
-/* free up list of amcc cards in this system */
-void v_pci_card_list_cleanup(unsigned short pci_vendor)
-{
- struct pcilst_struct *amcc, *next;
-
- for (amcc = amcc_devices; amcc; amcc = next) {
- next = amcc->next;
- kfree(amcc);
- }
-
- amcc_devices = NULL;
-}
-
-/****************************************************************************/
-/* find first unused card with this device_id */
-struct pcilst_struct *ptr_find_free_pci_card_by_device(unsigned short vendor_id,
- unsigned short device_id)
-{
- struct pcilst_struct *amcc, *next;
-
- for (amcc = amcc_devices; amcc; amcc = next) {
- next = amcc->next;
- if ((!amcc->used) && (amcc->device == device_id)
- && (amcc->vendor == vendor_id))
- return amcc;
-
- }
-
- return NULL;
-}
-
-/****************************************************************************/
-/* find card on requested position */
-int i_find_free_pci_card_by_position(unsigned short vendor_id,
- unsigned short device_id,
- unsigned short pci_bus,
- unsigned short pci_slot,
- struct pcilst_struct **card)
-{
- struct pcilst_struct *amcc, *next;
-
- *card = NULL;
- for (amcc = amcc_devices; amcc; amcc = next) {
- next = amcc->next;
- if ((amcc->vendor == vendor_id) && (amcc->device == device_id)
- && (amcc->pci_bus == pci_bus)
- && (amcc->pci_slot == pci_slot)) {
- if (!(amcc->used)) {
- *card = amcc;
- return 0; /* ok, card is found */
- } else {
- printk
- (" - \nCard on requested position is used b:s %d:%d!\n",
- pci_bus, pci_slot);
- return 2; /* card exist but is used */
- }
- }
- }
-
- return 1; /* no card found */
-}
-
-/****************************************************************************/
-/* mark card as used */
-int i_pci_card_alloc(struct pcilst_struct *amcc)
-{
- if (!amcc)
- return -1;
-
- if (amcc->used)
- return 1;
- amcc->used = 1;
- return 0;
-}
-
-/****************************************************************************/
-/* mark card as free */
-int i_pci_card_free(struct pcilst_struct *amcc)
-{
- if (!amcc)
- return -1;
-
- if (!amcc->used)
- return 1;
- amcc->used = 0;
- return 0;
-}
-
-/****************************************************************************/
-/* display list of found cards */
-void v_pci_card_list_display(void)
-{
- struct pcilst_struct *amcc, *next;
-
- printk("List of pci cards\n");
- printk("bus:slot:func vendor device master io_amcc io_daq irq used\n");
-
- for (amcc = amcc_devices; amcc; amcc = next) {
- next = amcc->next;
- printk
- ("%2d %2d %2d 0x%4x 0x%4x %3s 0x%4x 0x%4x %2d %2d\n",
- amcc->pci_bus, amcc->pci_slot, amcc->pci_func,
- amcc->vendor, amcc->device, amcc->master ? "yes" : "no",
- amcc->io_addr[0], amcc->io_addr[2], amcc->irq, amcc->used);
-
- }
-}
-
-/****************************************************************************/
-/* return all card information for driver */
-int i_pci_card_data(struct pcilst_struct *amcc,
- unsigned char *pci_bus, unsigned char *pci_slot,
- unsigned char *pci_func, unsigned short *io_addr,
- unsigned short *irq, unsigned short *master)
-{
- int i;
-
- if (!amcc)
- return -1;
- *pci_bus = amcc->pci_bus;
- *pci_slot = amcc->pci_slot;
- *pci_func = amcc->pci_func;
- for (i = 0; i < 5; i++)
- io_addr[i] = amcc->io_addr[i];
- *irq = amcc->irq;
- *master = amcc->master;
- return 0;
-}
-
-/****************************************************************************/
-/* select and alloc card */
-struct pcilst_struct *ptr_select_and_alloc_pci_card(unsigned short vendor_id,
- unsigned short device_id,
- unsigned short pci_bus,
- unsigned short pci_slot)
-{
- struct pcilst_struct *card;
-
- if ((pci_bus < 1) & (pci_slot < 1)) { /* use autodetection */
- card = ptr_find_free_pci_card_by_device(vendor_id, device_id);
- if (card == NULL) {
- printk(" - Unused card not found in system!\n");
- return NULL;
- }
- } else {
- switch (i_find_free_pci_card_by_position(vendor_id, device_id,
- pci_bus, pci_slot,
- &card)) {
- case 1:
- printk
- (" - Card not found on requested position b:s %d:%d!\n",
- pci_bus, pci_slot);
- return NULL;
- case 2:
- printk
- (" - Card on requested position is used b:s %d:%d!\n",
- pci_bus, pci_slot);
- return NULL;
- }
- }
-
- if (i_pci_card_alloc(card) != 0) {
- printk(" - Can't allocate card!\n");
- return NULL;
- }
-
- return card;
-}
-
-#endif
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.c
index 595238feaf42..f9a8937be8ed 100644
--- a/drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.c
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_APCI1710.c
@@ -67,7 +67,7 @@ void i_ADDI_AttachPCI1710(struct comedi_device *dev)
return;
/* Allocate and Initialise Timer Subdevice Structures */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_TIMER;
s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
@@ -81,7 +81,7 @@ void i_ADDI_AttachPCI1710(struct comedi_device *dev)
s->insn_bits = i_APCI1710_InsnBitsTimer;
/* Allocate and Initialise DIO Subdevice Structures */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags =
@@ -96,7 +96,7 @@ void i_ADDI_AttachPCI1710(struct comedi_device *dev)
s->insn_write = i_APCI1710_InsnWriteDigitalIOChlOnOff;
/* Allocate and Initialise Chrono Subdevice Structures */
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_CHRONO;
s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
@@ -110,7 +110,7 @@ void i_ADDI_AttachPCI1710(struct comedi_device *dev)
s->insn_bits = i_APCI1710_InsnBitsChronoDigitalIO;
/* Allocate and Initialise PWM Subdevice Structures */
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_PWM;
s->subdev_flags =
SDF_WRITEABLE | SDF_READABLE | SDF_GROUND | SDF_COMMON;
@@ -125,7 +125,7 @@ void i_ADDI_AttachPCI1710(struct comedi_device *dev)
s->insn_bits = i_APCI1710_InsnBitsReadPWMInterrupt;
/* Allocate and Initialise TTLIO Subdevice Structures */
- s = dev->subdevices + 4;
+ s = &dev->subdevices[4];
s->type = COMEDI_SUBD_TTLIO;
s->subdev_flags =
SDF_WRITEABLE | SDF_READABLE | SDF_GROUND | SDF_COMMON;
@@ -139,7 +139,7 @@ void i_ADDI_AttachPCI1710(struct comedi_device *dev)
s->insn_read = i_APCI1710_InsnReadTTLIOAllPortValue;
/* Allocate and Initialise TOR Subdevice Structures */
- s = dev->subdevices + 5;
+ s = &dev->subdevices[5];
s->type = COMEDI_SUBD_TOR;
s->subdev_flags =
SDF_WRITEABLE | SDF_READABLE | SDF_GROUND | SDF_COMMON;
@@ -154,7 +154,7 @@ void i_ADDI_AttachPCI1710(struct comedi_device *dev)
s->insn_bits = i_APCI1710_InsnBitsGetTorCounterProgressStatusAndValue;
/* Allocate and Initialise SSI Subdevice Structures */
- s = dev->subdevices + 6;
+ s = &dev->subdevices[6];
s->type = COMEDI_SUBD_SSI;
s->subdev_flags =
SDF_WRITEABLE | SDF_READABLE | SDF_GROUND | SDF_COMMON;
@@ -167,7 +167,7 @@ void i_ADDI_AttachPCI1710(struct comedi_device *dev)
s->insn_bits = i_APCI1710_InsnBitsSSIDigitalIO;
/* Allocate and Initialise PULSEENCODER Subdevice Structures */
- s = dev->subdevices + 7;
+ s = &dev->subdevices[7];
s->type = COMEDI_SUBD_PULSEENCODER;
s->subdev_flags =
SDF_WRITEABLE | SDF_READABLE | SDF_GROUND | SDF_COMMON;
@@ -181,7 +181,7 @@ void i_ADDI_AttachPCI1710(struct comedi_device *dev)
s->insn_read = i_APCI1710_InsnReadInterruptPulseEncoder;
/* Allocate and Initialise INCREMENTALCOUNTER Subdevice Structures */
- s = dev->subdevices + 8;
+ s = &dev->subdevices[8];
s->type = COMEDI_SUBD_INCREMENTALCOUNTER;
s->subdev_flags =
SDF_WRITEABLE | SDF_READABLE | SDF_GROUND | SDF_COMMON;
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
index ffe390c6da83..f406dfb2a677 100644
--- a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3120.c
@@ -479,57 +479,26 @@ int i_APCI3120_CommandTestAnalogInput(struct comedi_device *dev, struct comedi_s
struct comedi_cmd *cmd)
{
int err = 0;
- int tmp; /* divisor1,divisor2; */
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
- err++;
-
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_FOLLOW)
- err++;
-
- if (cmd->convert_src != TRIG_TIMER)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
- if (cmd->scan_end_src != TRIG_COUNT) {
- cmd->scan_end_src = TRIG_COUNT;
- err++;
- }
-
- if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
- err++;
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1451,7 +1420,7 @@ void v_APCI3120_Interrupt(int irq, void *d)
unsigned short us_TmpValue;
unsigned char b_DummyRead;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
ui_Check = 1;
int_daq = inw(dev->iobase + APCI3120_RD_STATUS) & 0xf000; /* get IRQ reasons */
@@ -1656,7 +1625,7 @@ void v_APCI3120_Interrupt(int irq, void *d)
int i_APCI3120_InterruptHandleEos(struct comedi_device *dev)
{
int n_chan, i;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
int err = 1;
n_chan = devpriv->ui_AiNbrofChannels;
@@ -1698,7 +1667,7 @@ int i_APCI3120_InterruptHandleEos(struct comedi_device *dev)
void v_APCI3120_InterruptDma(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
unsigned int next_dma_buf, samplesinbuf;
unsigned long low_word, high_word, var;
diff --git a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.c b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.c
index f9545b064eaf..38ab49917d7e 100644
--- a/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.c
+++ b/drivers/staging/comedi/drivers/addi-data/hwdrv_apci3200.c
@@ -57,11 +57,8 @@ You should also find the complete GPL in the COPYING file accompanying this sour
+----------------------------------------------------------------------------+
*/
#include "hwdrv_apci3200.h"
-/* Begin JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values */
-#include "addi_amcc_S5920.h"
-/* #define PRINT_INFO */
-/* End JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values */
+/* #define PRINT_INFO */
/* BEGIN JK 06.07.04: Management of sevrals boards */
/*
@@ -90,7 +87,12 @@ You should also find the complete GPL in the COPYING file accompanying this sour
struct str_BoardInfos s_BoardInfos[100]; /* 100 will be the max number of boards to be used */
/* END JK 06.07.04: Management of sevrals boards */
-/* Begin JK 21.10.2004: APCI-3200 / APCI-3300 Reading of EEPROM values */
+#define AMCC_OP_REG_MCSR 0x3c
+#define EEPROM_BUSY 0x80000000
+#define NVCMD_LOAD_LOW (0x4 << 5) /* nvRam load low command */
+#define NVCMD_LOAD_HIGH (0x5 << 5) /* nvRam load high command */
+#define NVCMD_BEGIN_READ (0x7 << 5) /* nvRam begin read command */
+#define NVCMD_BEGIN_WRITE (0x6 << 5) /* EEPROM begin write command */
/*+----------------------------------------------------------------------------+*/
/*| Function Name : int i_AddiHeaderRW_ReadEeprom |*/
@@ -2558,7 +2560,6 @@ int i_APCI3200_CommandTestAnalogInput(struct comedi_device *dev, struct comedi_s
{
int err = 0;
- int tmp; /* divisor1,divisor2; */
unsigned int ui_ConvertTime = 0;
unsigned int ui_ConvertTimeBase = 0;
unsigned int ui_DelayTime = 0;
@@ -2569,41 +2570,32 @@ int i_APCI3200_CommandTestAnalogInput(struct comedi_device *dev, struct comedi_s
int i_Cpt = 0;
double d_ConversionTimeForAllChannels = 0.0;
double d_SCANTimeNewUnit = 0.0;
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
- /* if(i_InterruptFlag==0) */
- if (s_BoardInfos[dev->minor].i_InterruptFlag == 0) {
- err++;
- /* printk("\nThe interrupt should be enabled\n"); */
- }
+
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+
+ if (s_BoardInfos[dev->minor].i_InterruptFlag == 0)
+ err |= -EINVAL;
+
if (err) {
i_APCI3200_Reset(dev);
return 1;
}
- if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) {
- err++;
- }
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= cfc_check_trigger_is_unique(&cmd->start_src);
+ err |= cfc_check_trigger_is_unique(&cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(&cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
if (cmd->start_src == TRIG_EXT) {
i_TriggerEdge = cmd->start_arg & 0xFFFF;
i_Triggermode = cmd->start_arg >> 16;
@@ -2617,21 +2609,6 @@ int i_APCI3200_CommandTestAnalogInput(struct comedi_device *dev, struct comedi_s
}
}
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_FOLLOW)
- err++;
-
- if (cmd->convert_src != TRIG_TIMER)
- err++;
-
- if (cmd->scan_end_src != TRIG_COUNT) {
- cmd->scan_end_src = TRIG_COUNT;
- err++;
- }
-
- if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
- err++;
-
if (err) {
i_APCI3200_Reset(dev);
return 2;
@@ -3495,7 +3472,7 @@ void v_APCI3200_Interrupt(int irq, void *d)
int i_APCI3200_InterruptHandleEos(struct comedi_device *dev)
{
unsigned int ui_StatusRegister = 0;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
/* BEGIN JK 18.10.2004: APCI-3200 Driver update 0.7.57 -> 0.7.68 */
/* comedi_async *async = s->async; */
diff --git a/drivers/staging/comedi/drivers/addi_apci_all.c b/drivers/staging/comedi/drivers/addi_apci_all.c
deleted file mode 100644
index aeb1b2688f3d..000000000000
--- a/drivers/staging/comedi/drivers/addi_apci_all.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#define CONFIG_APCI_035 1
-#define CONFIG_APCI_1032 1
-#define CONFIG_APCI_1500 1
-#define CONFIG_APCI_1516 1
-#define CONFIG_APCI_1564 1
-#define CONFIG_APCI_16XX 1
-#define CONFIG_APCI_1710 1
-#define CONFIG_APCI_2016 1
-#define CONFIG_APCI_2032 1
-#define CONFIG_APCI_2200 1
-#define CONFIG_APCI_3001 1
-#define CONFIG_APCI_3120 1
-#define CONFIG_APCI_3200 1
-#define CONFIG_APCI_3300 1
-#define CONFIG_APCI_3501 1
-#define CONFIG_APCI_3XXX 1
-
-#include "addi-data/addi_common.c"
diff --git a/drivers/staging/comedi/drivers/adl_pci6208.c b/drivers/staging/comedi/drivers/adl_pci6208.c
index 3bec0f6e4a8c..3492ce1156e0 100644
--- a/drivers/staging/comedi/drivers/adl_pci6208.c
+++ b/drivers/staging/comedi/drivers/adl_pci6208.c
@@ -27,14 +27,14 @@
*/
/*
Driver: adl_pci6208
-Description: ADLink PCI-6208A
-Devices: [ADLink] PCI-6208A (adl_pci6208)
+Description: ADLink PCI-6208/6216 Series Multi-channel Analog Output Cards
+Devices: (ADLink) PCI-6208 [adl_pci6208]
+ (ADLink) PCI-6216 [adl_pci6216]
Author: nsyeow <nsyeow@pd.jaring.my>
Updated: Fri, 30 Jan 2004 14:44:27 +0800
Status: untested
-Configuration Options:
- none
+Configuration Options: not applicable, uses PCI auto config
References:
- ni_660x.c
@@ -45,6 +45,12 @@ References:
#include "../comedidev.h"
/*
+ * ADLINK PCI Device ID's supported by this driver
+ */
+#define PCI_DEVICE_ID_PCI6208 0x6208
+#define PCI_DEVICE_ID_PCI6216 0x6216
+
+/*
* PCI-6208/6216-GL register map
*/
#define PCI6208_AO_CONTROL(x) (0x00 + (2 * (x)))
@@ -56,7 +62,7 @@ References:
#define PCI6208_DIO_DI_MASK (0xf0)
#define PCI6208_DIO_DI_SHIFT (4)
-#define PCI6208_MAX_AO_CHANNELS 8
+#define PCI6208_MAX_AO_CHANNELS 16
struct pci6208_board {
const char *name;
@@ -66,9 +72,13 @@ struct pci6208_board {
static const struct pci6208_board pci6208_boards[] = {
{
- .name = "pci6208a",
- .dev_id = 0x6208,
+ .name = "adl_pci6208",
+ .dev_id = PCI_DEVICE_ID_PCI6208,
.ao_chans = 8,
+ }, {
+ .name = "adl_pci6216",
+ .dev_id = PCI_DEVICE_ID_PCI6216,
+ .ao_chans = 16,
},
};
@@ -115,141 +125,122 @@ static int pci6208_ao_rinsn(struct comedi_device *dev,
return insn->n;
}
-static int pci6208_dio_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
+static int pci6208_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned int val;
+
+ val = inw(dev->iobase + PCI6208_DIO);
+ val = (val & PCI6208_DIO_DI_MASK) >> PCI6208_DIO_DI_SHIFT;
+
+ data[1] = val;
+
+ return insn->n;
+}
+
+static int pci6208_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
- unsigned int mask = data[0] & PCI6208_DIO_DO_MASK;
+ unsigned int mask = data[0];
unsigned int bits = data[1];
if (mask) {
s->state &= ~mask;
- s->state |= bits & mask;
+ s->state |= (bits & mask);
outw(s->state, dev->iobase + PCI6208_DIO);
}
- s->state = inw(dev->iobase + PCI6208_DIO);
data[1] = s->state;
return insn->n;
}
-static int pci6208_dio_insn_config(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- int chan = CR_CHAN(insn->chanspec);
- unsigned int mask = 1 << chan;
-
- switch (data[0]) {
- case INSN_CONFIG_DIO_QUERY:
- data[1] = (s->io_bits & mask) ? COMEDI_OUTPUT : COMEDI_INPUT;
- break;
- default:
- return -EINVAL;
- }
-
- return insn->n;
-}
-
-static struct pci_dev *pci6208_find_device(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static const void *pci6208_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- const struct pci6208_board *thisboard;
- struct pci_dev *pci_dev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
+ const struct pci6208_board *boardinfo;
int i;
- for_each_pci_dev(pci_dev) {
- if (pci_dev->vendor != PCI_VENDOR_ID_ADLINK)
- continue;
- for (i = 0; i < ARRAY_SIZE(pci6208_boards); i++) {
- thisboard = &pci6208_boards[i];
- if (thisboard->dev_id != pci_dev->device)
- continue;
- /* was a particular bus/slot requested? */
- if (bus || slot) {
- /* are we on the wrong bus/slot? */
- if (pci_dev->bus->number != bus ||
- PCI_SLOT(pci_dev->devfn) != slot)
- continue;
- }
- dev_dbg(dev->class_dev,
- "Found %s on bus %d, slot, %d, irq=%d\n",
- thisboard->name,
- pci_dev->bus->number,
- PCI_SLOT(pci_dev->devfn),
- pci_dev->irq);
- dev->board_ptr = thisboard;
- return pci_dev;
- }
+ for (i = 0; i < ARRAY_SIZE(pci6208_boards); i++) {
+ boardinfo = &pci6208_boards[i];
+ if (boardinfo->dev_id == pcidev->device)
+ return boardinfo;
}
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
return NULL;
}
-static int pci6208_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int pci6208_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- const struct pci6208_board *thisboard;
+ const struct pci6208_board *boardinfo;
struct pci6208_private *devpriv;
- struct pci_dev *pcidev;
struct comedi_subdevice *s;
+ unsigned int val;
int ret;
+ comedi_set_hw_dev(dev, &pcidev->dev);
+
+ boardinfo = pci6208_find_boardinfo(dev, pcidev);
+ if (!boardinfo)
+ return -ENODEV;
+ dev->board_ptr = boardinfo;
+ dev->board_name = boardinfo->name;
+
ret = alloc_private(dev, sizeof(*devpriv));
if (ret < 0)
return ret;
devpriv = dev->private;
- pcidev = pci6208_find_device(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
- thisboard = comedi_board(dev);
-
- dev->board_name = thisboard->name;
-
- ret = comedi_pci_enable(pcidev, dev->driver->driver_name);
- if (ret) {
- dev_err(dev->class_dev,
- "Failed to enable PCI device and request regions\n");
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
return ret;
- }
dev->iobase = pci_resource_start(pcidev, 2);
- ret = comedi_alloc_subdevices(dev, 2);
+ ret = comedi_alloc_subdevices(dev, 3);
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* analog output subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
- s->n_chan = thisboard->ao_chans;
+ s->n_chan = boardinfo->ao_chans;
s->maxdata = 0xffff;
s->range_table = &range_bipolar10;
s->insn_write = pci6208_ao_winsn;
s->insn_read = pci6208_ao_rinsn;
- s = dev->subdevices + 1;
- /* digital i/o subdevice */
- s->type = COMEDI_SUBD_DIO;
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- s->n_chan = 8;
+ s = &dev->subdevices[1];
+ /* digital input subdevice */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 4;
s->maxdata = 1;
s->range_table = &range_digital;
- s->insn_bits = pci6208_dio_insn_bits;
- s->insn_config = pci6208_dio_insn_config;
+ s->insn_bits = pci6208_di_insn_bits;
+ s = &dev->subdevices[2];
+ /* digital output subdevice */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 4;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci6208_do_insn_bits;
+
+ /*
+ * Get the read back signals from the digital outputs
+ * and save it as the initial state for the subdevice.
+ */
+ val = inw(dev->iobase + PCI6208_DIO);
+ val = (val & PCI6208_DIO_DO_MASK) >> PCI6208_DIO_DO_SHIFT;
+ s->state = val;
s->io_bits = 0x0f;
- s->state = inw(dev->iobase + PCI6208_DIO);
dev_info(dev->class_dev, "%s: %s, I/O base=0x%04lx\n",
dev->driver->driver_name, dev->board_name, dev->iobase);
@@ -264,14 +255,13 @@ static void pci6208_detach(struct comedi_device *dev)
if (pcidev) {
if (dev->iobase)
comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
}
}
static struct comedi_driver adl_pci6208_driver = {
.driver_name = "adl_pci6208",
.module = THIS_MODULE,
- .attach = pci6208_attach,
+ .attach_pci = pci6208_attach_pci,
.detach = pci6208_detach,
};
@@ -287,7 +277,8 @@ static void __devexit adl_pci6208_pci_remove(struct pci_dev *dev)
}
static DEFINE_PCI_DEVICE_TABLE(adl_pci6208_pci_table) = {
- { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, 0x6208) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI6208) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI6216) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, adl_pci6208_pci_table);
diff --git a/drivers/staging/comedi/drivers/adl_pci7230.c b/drivers/staging/comedi/drivers/adl_pci7230.c
deleted file mode 100644
index 7df4c960d5e4..000000000000
--- a/drivers/staging/comedi/drivers/adl_pci7230.c
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- comedi/drivers/adl_pci7230.c
-
- Hardware comedi driver fot PCI7230 Adlink card
- Copyright (C) 2010 David Fernandez <dfcastelao@gmail.com>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- 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.
-
-*/
-/*
-Driver: adl_pci7230
-Description: Driver for the Adlink PCI-7230 32 ch. isolated digital io board
-Devices: [ADLink] PCI-7230 (adl_pci7230)
-Author: David Fernandez <dfcastelao@gmail.com>
-Status: experimental
-Updated: Mon, 14 Apr 2008 15:08:14 +0100
-
-Configuration Options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first supported
- PCI device found will be used.
-*/
-
-#include "../comedidev.h"
-#include <linux/kernel.h>
-
-#define PCI7230_DI 0x00
-#define PCI7230_DO 0x00
-
-#define PCI_DEVICE_ID_PCI7230 0x7230
-
-static int adl_pci7230_do_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- if (data[0]) {
- s->state &= ~data[0];
- s->state |= (data[0] & data[1]);
-
- outl((s->state << 16) & 0xffffffff, dev->iobase + PCI7230_DO);
- }
-
- return insn->n;
-}
-
-static int adl_pci7230_di_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- data[1] = inl(dev->iobase + PCI7230_DI) & 0xffffffff;
-
- return insn->n;
-}
-
-static struct pci_dev *adl_pci7230_find_pci(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
-
- for_each_pci_dev(pcidev) {
- if (pcidev->vendor != PCI_VENDOR_ID_ADLINK ||
- pcidev->device != PCI_DEVICE_ID_PCI7230)
- continue;
- if (bus || slot) {
- /* requested particular bus/slot */
- if (pcidev->bus->number != bus ||
- PCI_SLOT(pcidev->devfn) != slot)
- continue;
- }
- return pcidev;
- }
- printk(KERN_ERR "comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
- dev->minor, bus, slot);
- return NULL;
-}
-
-static int adl_pci7230_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct comedi_subdevice *s;
- struct pci_dev *pcidev;
- int ret;
-
- printk(KERN_INFO "comedi%d: adl_pci7230\n", dev->minor);
-
- dev->board_name = "pci7230";
-
- ret = comedi_alloc_subdevices(dev, 2);
- if (ret)
- return ret;
-
- pcidev = adl_pci7230_find_pci(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
-
- if (comedi_pci_enable(pcidev, "adl_pci7230") < 0) {
- printk(KERN_ERR "comedi%d: Failed to enable PCI device and request regions\n",
- dev->minor);
- return -EIO;
- }
- dev->iobase = pci_resource_start(pcidev, 2);
- printk(KERN_DEBUG "comedi: base addr %4lx\n", dev->iobase);
-
- s = dev->subdevices + 0;
- /* Isolated do */
- s->type = COMEDI_SUBD_DO;
- s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
- s->n_chan = 16;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = adl_pci7230_do_insn_bits;
-
- s = dev->subdevices + 1;
- /* Isolated di */
- s->type = COMEDI_SUBD_DI;
- s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
- s->n_chan = 16;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = adl_pci7230_di_insn_bits;
-
- printk(KERN_DEBUG "comedi: attached\n");
-
- return 1;
-}
-
-static void adl_pci7230_detach(struct comedi_device *dev)
-{
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
-
- if (pcidev) {
- if (dev->iobase)
- comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
- }
-}
-
-static struct comedi_driver adl_pci7230_driver = {
- .driver_name = "adl_pci7230",
- .module = THIS_MODULE,
- .attach = adl_pci7230_attach,
- .detach = adl_pci7230_detach,
-};
-
-static int __devinit adl_pci7230_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *ent)
-{
- return comedi_pci_auto_config(dev, &adl_pci7230_driver);
-}
-
-static void __devexit adl_pci7230_pci_remove(struct pci_dev *dev)
-{
- comedi_pci_auto_unconfig(dev);
-}
-
-static DEFINE_PCI_DEVICE_TABLE(adl_pci7230_pci_table) = {
- { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7230) },
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, adl_pci7230_pci_table);
-
-static struct pci_driver adl_pci7230_pci_driver = {
- .name = "adl_pci7230",
- .id_table = adl_pci7230_pci_table,
- .probe = adl_pci7230_pci_probe,
- .remove = __devexit_p(adl_pci7230_pci_remove),
-};
-module_comedi_pci_driver(adl_pci7230_driver, adl_pci7230_pci_driver);
-
-MODULE_AUTHOR("Comedi http://www.comedi.org");
-MODULE_DESCRIPTION("Comedi low-level driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adl_pci7296.c b/drivers/staging/comedi/drivers/adl_pci7296.c
deleted file mode 100644
index 19b47af9c10e..000000000000
--- a/drivers/staging/comedi/drivers/adl_pci7296.c
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- comedi/drivers/adl_pci7296.c
-
- COMEDI - Linux Control and Measurement Device Interface
- Copyright (C) 2000 David A. Schleef <ds@schleef.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- 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.
-
-*/
-/*
-Driver: adl_pci7296
-Description: Driver for the Adlink PCI-7296 96 ch. digital io board
-Devices: [ADLink] PCI-7296 (adl_pci7296)
-Author: Jon Grierson <jd@renko.co.uk>
-Updated: Mon, 14 Apr 2008 15:05:56 +0100
-Status: testing
-
-Configuration Options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first supported
- PCI device found will be used.
-*/
-
-#include "../comedidev.h"
-#include <linux/kernel.h>
-
-#include "8255.h"
-/* #include "8253.h" */
-
-#define PORT1A 0
-#define PORT2A 4
-#define PORT3A 8
-#define PORT4A 12
-
-#define PCI_DEVICE_ID_PCI7296 0x7296
-
-static struct pci_dev *adl_pci7296_find_pci(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
-
- for_each_pci_dev(pcidev) {
- if (pcidev->vendor != PCI_VENDOR_ID_ADLINK ||
- pcidev->device != PCI_DEVICE_ID_PCI7296)
- continue;
- if (bus || slot) {
- /* requested particular bus/slot */
- if (pcidev->bus->number != bus ||
- PCI_SLOT(pcidev->devfn) != slot)
- continue;
- }
- return pcidev;
- }
- printk(KERN_ERR
- "comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
- dev->minor, bus, slot);
- return NULL;
-}
-
-static int adl_pci7296_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev;
- struct comedi_subdevice *s;
- int ret;
-
- printk(KERN_INFO "comedi%d: attach adl_pci7432\n", dev->minor);
-
- dev->board_name = "pci7432";
-
- ret = comedi_alloc_subdevices(dev, 4);
- if (ret)
- return ret;
-
- pcidev = adl_pci7296_find_pci(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
-
- if (comedi_pci_enable(pcidev, "adl_pci7296") < 0) {
- printk(KERN_ERR
- "comedi%d: Failed to enable PCI device and request regions\n",
- dev->minor);
- return -EIO;
- }
-
- dev->iobase = pci_resource_start(pcidev, 2);
- printk(KERN_INFO "comedi: base addr %4lx\n", dev->iobase);
-
- /* four 8255 digital io subdevices */
- s = dev->subdevices + 0;
- subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase));
-
- s = dev->subdevices + 1;
- ret = subdev_8255_init(dev, s, NULL,
- (unsigned long)(dev->iobase + PORT2A));
- if (ret < 0)
- return ret;
-
- s = dev->subdevices + 2;
- ret = subdev_8255_init(dev, s, NULL,
- (unsigned long)(dev->iobase + PORT3A));
- if (ret < 0)
- return ret;
-
- s = dev->subdevices + 3;
- ret = subdev_8255_init(dev, s, NULL,
- (unsigned long)(dev->iobase + PORT4A));
- if (ret < 0)
- return ret;
-
- printk(KERN_DEBUG "comedi%d: adl_pci7432 attached\n", dev->minor);
-
- return 0;
-}
-
-static void adl_pci7296_detach(struct comedi_device *dev)
-{
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
-
- if (pcidev) {
- if (dev->iobase)
- comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
- }
- if (dev->subdevices) {
- subdev_8255_cleanup(dev, dev->subdevices + 0);
- subdev_8255_cleanup(dev, dev->subdevices + 1);
- subdev_8255_cleanup(dev, dev->subdevices + 2);
- subdev_8255_cleanup(dev, dev->subdevices + 3);
- }
-}
-
-static struct comedi_driver adl_pci7296_driver = {
- .driver_name = "adl_pci7296",
- .module = THIS_MODULE,
- .attach = adl_pci7296_attach,
- .detach = adl_pci7296_detach,
-};
-
-static int __devinit adl_pci7296_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *ent)
-{
- return comedi_pci_auto_config(dev, &adl_pci7296_driver);
-}
-
-static void __devexit adl_pci7296_pci_remove(struct pci_dev *dev)
-{
- comedi_pci_auto_unconfig(dev);
-}
-
-static DEFINE_PCI_DEVICE_TABLE(adl_pci7296_pci_table) = {
- { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7296) },
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, adl_pci7296_pci_table);
-
-static struct pci_driver adl_pci7296_pci_driver = {
- .name = "adl_pci7296",
- .id_table = adl_pci7296_pci_table,
- .probe = adl_pci7296_pci_probe,
- .remove = __devexit_p(adl_pci7296_pci_remove),
-};
-module_comedi_pci_driver(adl_pci7296_driver, adl_pci7296_pci_driver);
-
-MODULE_AUTHOR("Comedi http://www.comedi.org");
-MODULE_DESCRIPTION("Comedi low-level driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adl_pci7432.c b/drivers/staging/comedi/drivers/adl_pci7432.c
deleted file mode 100644
index 6b8d9408e3bc..000000000000
--- a/drivers/staging/comedi/drivers/adl_pci7432.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- comedi/drivers/adl_pci7432.c
-
- Hardware comedi driver fot PCI7432 Adlink card
- Copyright (C) 2004 Michel Lachine <mike@mikelachaine.ca>
-
- This program is free software; 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.
-
-*/
-/*
-Driver: adl_pci7432
-Description: Driver for the Adlink PCI-7432 64 ch. isolated digital io board
-Devices: [ADLink] PCI-7432 (adl_pci7432)
-Author: Michel Lachaine <mike@mikelachaine.ca>
-Status: experimental
-Updated: Mon, 14 Apr 2008 15:08:14 +0100
-
-Configuration Options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first supported
- PCI device found will be used.
-*/
-
-#include "../comedidev.h"
-#include <linux/kernel.h>
-
-#define PCI7432_DI 0x00
-#define PCI7432_DO 0x00
-
-#define PCI_DEVICE_ID_PCI7432 0x7432
-
-static int adl_pci7432_do_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- printk(KERN_DEBUG "comedi: pci7432_do_insn_bits called\n");
- printk(KERN_DEBUG "comedi: data0: %8x data1: %8x\n", data[0], data[1]);
-
- if (data[0]) {
- s->state &= ~data[0];
- s->state |= (data[0] & data[1]);
-
- printk(KERN_DEBUG "comedi: out: %8x on iobase %4lx\n", s->state,
- dev->iobase + PCI7432_DO);
- outl(s->state & 0xffffffff, dev->iobase + PCI7432_DO);
- }
- return insn->n;
-}
-
-static int adl_pci7432_di_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn,
- unsigned int *data)
-{
- printk(KERN_DEBUG "comedi: pci7432_di_insn_bits called\n");
- printk(KERN_DEBUG "comedi: data0: %8x data1: %8x\n", data[0], data[1]);
-
- data[1] = inl(dev->iobase + PCI7432_DI) & 0xffffffff;
- printk(KERN_DEBUG "comedi: data1 %8x\n", data[1]);
-
- return insn->n;
-}
-
-static struct pci_dev *adl_pci7432_find_pci(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
-
- for_each_pci_dev(pcidev) {
- if (pcidev->vendor != PCI_VENDOR_ID_ADLINK ||
- pcidev->device != PCI_DEVICE_ID_PCI7432)
- continue;
- if (bus || slot) {
- /* requested particular bus/slot */
- if (pcidev->bus->number != bus ||
- PCI_SLOT(pcidev->devfn) != slot)
- continue;
- }
- return pcidev;
- }
- printk(KERN_ERR
- "comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
- dev->minor, bus, slot);
- return NULL;
-}
-
-static int adl_pci7432_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev;
- struct comedi_subdevice *s;
- int ret;
-
- printk(KERN_INFO "comedi%d: attach adl_pci7432\n", dev->minor);
-
- dev->board_name = "pci7432";
-
- ret = comedi_alloc_subdevices(dev, 2);
- if (ret)
- return ret;
-
- pcidev = adl_pci7432_find_pci(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
-
- if (comedi_pci_enable(pcidev, "adl_pci7432") < 0) {
- printk(KERN_ERR "comedi%d: Failed to enable PCI device and request regions\n",
- dev->minor);
- return -EIO;
- }
- dev->iobase = pci_resource_start(pcidev, 2);
- printk(KERN_INFO "comedi: base addr %4lx\n", dev->iobase);
-
- s = dev->subdevices + 0;
- s->type = COMEDI_SUBD_DI;
- s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
- s->n_chan = 32;
- s->maxdata = 1;
- s->len_chanlist = 32;
- s->io_bits = 0x00000000;
- s->range_table = &range_digital;
- s->insn_bits = adl_pci7432_di_insn_bits;
-
- s = dev->subdevices + 1;
- s->type = COMEDI_SUBD_DO;
- s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
- s->n_chan = 32;
- s->maxdata = 1;
- s->len_chanlist = 32;
- s->io_bits = 0xffffffff;
- s->range_table = &range_digital;
- s->insn_bits = adl_pci7432_do_insn_bits;
-
- printk(KERN_DEBUG "comedi%d: adl_pci7432 attached\n", dev->minor);
- return 0;
-}
-
-static void adl_pci7432_detach(struct comedi_device *dev)
-{
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
-
- if (pcidev) {
- if (dev->iobase)
- comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
- }
-}
-
-static struct comedi_driver adl_pci7432_driver = {
- .driver_name = "adl_pci7432",
- .module = THIS_MODULE,
- .attach = adl_pci7432_attach,
- .detach = adl_pci7432_detach,
-};
-
-static int __devinit adl_pci7432_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *ent)
-{
- return comedi_pci_auto_config(dev, &adl_pci7432_driver);
-}
-
-static void __devexit adl_pci7432_pci_remove(struct pci_dev *dev)
-{
- comedi_pci_auto_unconfig(dev);
-}
-
-static DEFINE_PCI_DEVICE_TABLE(adl_pci7432_pci_table) = {
- { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7432) },
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, adl_pci7432_pci_table);
-
-static struct pci_driver adl_pci7432_pci_driver = {
- .name = "adl_pci7432",
- .id_table = adl_pci7432_pci_table,
- .probe = adl_pci7432_pci_probe,
- .remove = __devexit_p(adl_pci7432_pci_remove),
-};
-module_comedi_pci_driver(adl_pci7432_driver, adl_pci7432_pci_driver);
-
-MODULE_AUTHOR("Comedi http://www.comedi.org");
-MODULE_DESCRIPTION("Comedi low-level driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adl_pci7x3x.c b/drivers/staging/comedi/drivers/adl_pci7x3x.c
new file mode 100644
index 000000000000..599714e978b5
--- /dev/null
+++ b/drivers/staging/comedi/drivers/adl_pci7x3x.c
@@ -0,0 +1,332 @@
+/*
+ * COMEDI driver for the ADLINK PCI-723x/743x series boards.
+ * Copyright (C) 2012 H Hartley Sweeten <hsweeten@visionengravers.com>
+ *
+ * Based on the adl_pci7230 driver written by:
+ * David Fernandez <dfcastelao@gmail.com>
+ * and the adl_pci7432 driver written by:
+ * Michel Lachaine <mike@mikelachaine.ca>
+ *
+ * COMEDI - Linux Control and Measurement Device Interface
+ * Copyright (C) 2000 David A. Schleef <ds@schleef.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * 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.
+ */
+
+/*
+Driver: adl_pci7x3x
+Description: 32/64-Channel Isolated Digital I/O Boards
+Devices: (ADLink) PCI-7230 [adl_pci7230] - 16 input / 16 output
+ (ADLink) PCI-7233 [adl_pci7233] - 32 input
+ (ADLink) PCI-7234 [adl_pci7234] - 32 output
+ (ADLink) PCI-7432 [adl_pci7432] - 32 input / 32 output
+ (ADLink) PCI-7433 [adl_pci7433] - 64 input
+ (ADLink) PCI-7434 [adl_pci7434] - 64 output
+Author: H Hartley Sweeten <hsweeten@visionengravers.com>
+Updated: Thu, 02 Aug 2012 14:27:46 -0700
+Status: untested
+
+This driver only attaches using the PCI PnP auto config support
+in the comedi core. The module parameter 'comedi_autoconfig'
+must be 1 (default) to enable this feature. The COMEDI_DEVCONFIG
+ioctl, used by the comedi_config utility, is not supported by
+this driver.
+
+The PCI-7230, PCI-7432 and PCI-7433 boards also support external
+interrupt signals on digital input channels 0 and 1. The PCI-7233
+has dual-interrupt sources for change-of-state (COS) on any 16
+digital input channels of LSB and for COS on any 16 digital input
+lines of MSB. Interrupts are not currently supported by this
+driver.
+
+Configuration Options: not applicable
+*/
+
+#include "../comedidev.h"
+
+/*
+ * PCI Device ID's supported by this driver
+ */
+#define PCI_DEVICE_ID_PCI7230 0x7230
+#define PCI_DEVICE_ID_PCI7233 0x7233
+#define PCI_DEVICE_ID_PCI7234 0x7234
+#define PCI_DEVICE_ID_PCI7432 0x7432
+#define PCI_DEVICE_ID_PCI7433 0x7433
+#define PCI_DEVICE_ID_PCI7434 0x7434
+
+/*
+ * Register I/O map (32-bit access only)
+ */
+#define PCI7X3X_DIO_REG 0x00
+#define PCI743X_DIO_REG 0x04
+
+struct adl_pci7x3x_boardinfo {
+ const char *name;
+ unsigned short device;
+ int nsubdevs;
+ int di_nchan;
+ int do_nchan;
+};
+
+static const struct adl_pci7x3x_boardinfo adl_pci7x3x_boards[] = {
+ {
+ .name = "adl_pci7230",
+ .device = PCI_DEVICE_ID_PCI7230,
+ .nsubdevs = 2,
+ .di_nchan = 16,
+ .do_nchan = 16,
+ }, {
+ .name = "adl_pci7233",
+ .device = PCI_DEVICE_ID_PCI7233,
+ .nsubdevs = 1,
+ .di_nchan = 32,
+ }, {
+ .name = "adl_pci7234",
+ .device = PCI_DEVICE_ID_PCI7234,
+ .nsubdevs = 1,
+ .do_nchan = 32,
+ }, {
+ .name = "adl_pci7432",
+ .device = PCI_DEVICE_ID_PCI7432,
+ .nsubdevs = 2,
+ .di_nchan = 32,
+ .do_nchan = 32,
+ }, {
+ .name = "adl_pci7433",
+ .device = PCI_DEVICE_ID_PCI7433,
+ .nsubdevs = 2,
+ .di_nchan = 64,
+ }, {
+ .name = "adl_pci7434",
+ .device = PCI_DEVICE_ID_PCI7434,
+ .nsubdevs = 2,
+ .do_nchan = 64,
+ }
+};
+
+static int adl_pci7x3x_do_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long reg = (unsigned long)s->private;
+ unsigned int mask = data[0];
+ unsigned int bits = data[1];
+
+ if (mask) {
+ s->state &= ~mask;
+ s->state |= (bits & mask);
+
+ outl(s->state, dev->iobase + reg);
+ }
+
+ /*
+ * NOTE: The output register is not readable.
+ * This returned state will not be correct until all the
+ * outputs have been updated.
+ */
+ data[1] = s->state;
+
+ return insn->n;
+}
+
+static int adl_pci7x3x_di_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ unsigned long reg = (unsigned long)s->private;
+
+ data[1] = inl(dev->iobase + reg);
+
+ return insn->n;
+}
+
+static const void *adl_pci7x3x_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ const struct adl_pci7x3x_boardinfo *board;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(adl_pci7x3x_boards); i++) {
+ board = &adl_pci7x3x_boards[i];
+ if (pcidev->device == board->device)
+ return board;
+ }
+ return NULL;
+}
+
+static int adl_pci7x3x_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ const struct adl_pci7x3x_boardinfo *board;
+ struct comedi_subdevice *s;
+ int subdev;
+ int nchan;
+ int ret;
+
+ comedi_set_hw_dev(dev, &pcidev->dev);
+
+ board = adl_pci7x3x_find_boardinfo(dev, pcidev);
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 2);
+
+ /*
+ * One or two subdevices are setup by this driver depending on
+ * the number of digital inputs and/or outputs provided by the
+ * board. Each subdevice has a maximum of 32 channels.
+ *
+ * PCI-7230 - 2 subdevices: 0 - 16 input, 1 - 16 output
+ * PCI-7233 - 1 subdevice: 0 - 32 input
+ * PCI-7234 - 1 subdevice: 0 - 32 output
+ * PCI-7432 - 2 subdevices: 0 - 32 input, 1 - 32 output
+ * PCI-7433 - 2 subdevices: 0 - 32 input, 1 - 32 input
+ * PCI-7434 - 2 subdevices: 0 - 32 output, 1 - 32 output
+ */
+ ret = comedi_alloc_subdevices(dev, board->nsubdevs);
+ if (ret)
+ return ret;
+
+ subdev = 0;
+
+ if (board->di_nchan) {
+ nchan = min(board->di_nchan, 32);
+
+ s = &dev->subdevices[subdev];
+ /* Isolated digital inputs 0 to 15/31 */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = nchan;
+ s->maxdata = 1;
+ s->insn_bits = adl_pci7x3x_di_insn_bits;
+ s->range_table = &range_digital;
+
+ s->private = (void *)PCI7X3X_DIO_REG;
+
+ subdev++;
+
+ nchan = board->di_nchan - nchan;
+ if (nchan) {
+ s = &dev->subdevices[subdev];
+ /* Isolated digital inputs 32 to 63 */
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = nchan;
+ s->maxdata = 1;
+ s->insn_bits = adl_pci7x3x_di_insn_bits;
+ s->range_table = &range_digital;
+
+ s->private = (void *)PCI743X_DIO_REG;
+
+ subdev++;
+ }
+ }
+
+ if (board->do_nchan) {
+ nchan = min(board->do_nchan, 32);
+
+ s = &dev->subdevices[subdev];
+ /* Isolated digital outputs 0 to 15/31 */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = nchan;
+ s->maxdata = 1;
+ s->insn_bits = adl_pci7x3x_do_insn_bits;
+ s->range_table = &range_digital;
+
+ s->private = (void *)PCI7X3X_DIO_REG;
+
+ subdev++;
+
+ nchan = board->do_nchan - nchan;
+ if (nchan) {
+ s = &dev->subdevices[subdev];
+ /* Isolated digital outputs 32 to 63 */
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = nchan;
+ s->maxdata = 1;
+ s->insn_bits = adl_pci7x3x_do_insn_bits;
+ s->range_table = &range_digital;
+
+ s->private = (void *)PCI743X_DIO_REG;
+
+ subdev++;
+ }
+ }
+
+ dev_info(dev->class_dev, "%s attached (%d inputs/%d outputs)\n",
+ dev->board_name, board->di_nchan, board->do_nchan);
+
+ return 0;
+}
+
+static void adl_pci7x3x_detach(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+
+ if (pcidev) {
+ if (dev->iobase)
+ comedi_pci_disable(pcidev);
+ }
+}
+
+static struct comedi_driver adl_pci7x3x_driver = {
+ .driver_name = "adl_pci7x3x",
+ .module = THIS_MODULE,
+ .attach_pci = adl_pci7x3x_attach_pci,
+ .detach = adl_pci7x3x_detach,
+};
+
+static int __devinit adl_pci7x3x_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ return comedi_pci_auto_config(dev, &adl_pci7x3x_driver);
+}
+
+static void __devexit adl_pci7x3x_pci_remove(struct pci_dev *dev)
+{
+ comedi_pci_auto_unconfig(dev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(adl_pci7x3x_pci_table) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7230) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7233) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7234) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7432) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7433) },
+ { PCI_DEVICE(PCI_VENDOR_ID_ADLINK, PCI_DEVICE_ID_PCI7434) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, adl_pci7x3x_pci_table);
+
+static struct pci_driver adl_pci7x3x_pci_driver = {
+ .name = "adl_pci7x3x",
+ .id_table = adl_pci7x3x_pci_table,
+ .probe = adl_pci7x3x_pci_probe,
+ .remove = __devexit_p(adl_pci7x3x_pci_remove),
+};
+module_comedi_pci_driver(adl_pci7x3x_driver, adl_pci7x3x_pci_driver);
+
+MODULE_DESCRIPTION("ADLINK PCI-723x/743x Isolated Digital I/O boards");
+MODULE_AUTHOR("H Hartley Sweeten <hsweeten@visionengravers.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/adl_pci8164.c b/drivers/staging/comedi/drivers/adl_pci8164.c
index 247ef00a7c6c..05e06e7ba9f7 100644
--- a/drivers/staging/comedi/drivers/adl_pci8164.c
+++ b/drivers/staging/comedi/drivers/adl_pci8164.c
@@ -27,11 +27,7 @@ Author: Michel Lachaine <mike@mikelachaine.ca>
Status: experimental
Updated: Mon, 14 Apr 2008 15:10:32 +0100
-Configuration Options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first supported
- PCI device found will be used.
+Configuration Options: not applicable, uses PCI auto config
*/
#include "../comedidev.h"
@@ -216,61 +212,26 @@ static int adl_pci8164_insn_write_buf1(struct comedi_device *dev,
return 2;
}
-static struct pci_dev *adl_pci8164_find_pci(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int adl_pci8164_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
-
- for_each_pci_dev(pcidev) {
- if (pcidev->vendor != PCI_VENDOR_ID_ADLINK ||
- pcidev->device != PCI_DEVICE_ID_PCI8164)
- continue;
- if (bus || slot) {
- /* requested particular bus/slot */
- if (pcidev->bus->number != bus ||
- PCI_SLOT(pcidev->devfn) != slot)
- continue;
- }
- return pcidev;
- }
- printk(KERN_ERR
- "comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
- dev->minor, bus, slot);
- return NULL;
-}
-
-static int adl_pci8164_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev;
struct comedi_subdevice *s;
int ret;
- printk(KERN_INFO "comedi: attempt to attach...\n");
- printk(KERN_INFO "comedi%d: adl_pci8164\n", dev->minor);
+ comedi_set_hw_dev(dev, &pcidev->dev);
- dev->board_name = "pci8164";
+ dev->board_name = dev->driver->driver_name;
- ret = comedi_alloc_subdevices(dev, 4);
+ ret = comedi_pci_enable(pcidev, dev->board_name);
if (ret)
return ret;
-
- pcidev = adl_pci8164_find_pci(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
-
- if (comedi_pci_enable(pcidev, "adl_pci8164") < 0) {
- printk(KERN_ERR "comedi%d: Failed to enable "
- "PCI device and request regions\n", dev->minor);
- return -EIO;
- }
dev->iobase = pci_resource_start(pcidev, 2);
- printk(KERN_DEBUG "comedi: base addr %4lx\n", dev->iobase);
- s = dev->subdevices + 0;
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_PROC;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 4;
@@ -280,7 +241,7 @@ static int adl_pci8164_attach(struct comedi_device *dev,
s->insn_read = adl_pci8164_insn_read_msts;
s->insn_write = adl_pci8164_insn_write_cmd;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_PROC;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 4;
@@ -290,7 +251,7 @@ static int adl_pci8164_attach(struct comedi_device *dev,
s->insn_read = adl_pci8164_insn_read_ssts;
s->insn_write = adl_pci8164_insn_write_otp;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_PROC;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 4;
@@ -300,7 +261,7 @@ static int adl_pci8164_attach(struct comedi_device *dev,
s->insn_read = adl_pci8164_insn_read_buf0;
s->insn_write = adl_pci8164_insn_write_buf0;
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_PROC;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 4;
@@ -310,7 +271,8 @@ static int adl_pci8164_attach(struct comedi_device *dev,
s->insn_read = adl_pci8164_insn_read_buf1;
s->insn_write = adl_pci8164_insn_write_buf1;
- printk(KERN_INFO "comedi: attached\n");
+ dev_info(dev->class_dev, "%s attached\n", dev->board_name);
+
return 0;
}
@@ -321,14 +283,13 @@ static void adl_pci8164_detach(struct comedi_device *dev)
if (pcidev) {
if (dev->iobase)
comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
}
}
static struct comedi_driver adl_pci8164_driver = {
.driver_name = "adl_pci8164",
.module = THIS_MODULE,
- .attach = adl_pci8164_attach,
+ .attach_pci = adl_pci8164_attach_pci,
.detach = adl_pci8164_detach,
};
diff --git a/drivers/staging/comedi/drivers/adl_pci9111.c b/drivers/staging/comedi/drivers/adl_pci9111.c
index a31dae6e07df..a87192ac2846 100644
--- a/drivers/staging/comedi/drivers/adl_pci9111.c
+++ b/drivers/staging/comedi/drivers/adl_pci9111.c
@@ -47,14 +47,7 @@ Supports:
The scanned channels must be consecutive and start from 0. They must
all have the same range and aref.
-Configuration options:
-
- [0] - PCI bus number (optional)
- [1] - PCI slot number (optional)
-
-If bus/slot is not specified, the first available PCI
-device will be used.
-
+Configuration options: not applicable, uses PCI auto config
*/
/*
@@ -86,29 +79,9 @@ TODO:
#define PCI9111_DRIVER_NAME "adl_pci9111"
#define PCI9111_HR_DEVICE_ID 0x9111
-/* TODO: Add other pci9111 board id */
-
-#define PCI9111_IO_RANGE 0x0100
-
#define PCI9111_FIFO_HALF_SIZE 512
-#define PCI9111_AI_CHANNEL_NBR 16
-
-#define PCI9111_AI_RESOLUTION 12
-#define PCI9111_AI_RESOLUTION_MASK 0x0FFF
-#define PCI9111_AI_RESOLUTION_2_CMP_BIT 0x0800
-
-#define PCI9111_HR_AI_RESOLUTION 16
-#define PCI9111_HR_AI_RESOLUTION_MASK 0xFFFF
-#define PCI9111_HR_AI_RESOLUTION_2_CMP_BIT 0x8000
-
#define PCI9111_AI_ACQUISITION_PERIOD_MIN_NS 10000
-#define PCI9111_AO_CHANNEL_NBR 1
-#define PCI9111_AO_RESOLUTION 12
-#define PCI9111_AO_RESOLUTION_MASK 0x0FFF
-#define PCI9111_DI_CHANNEL_NBR 16
-#define PCI9111_DO_CHANNEL_NBR 16
-#define PCI9111_DO_MASK 0xFFFF
#define PCI9111_RANGE_SETTING_DELAY 10
#define PCI9111_AI_INSTANT_READ_UDELAY_US 2
@@ -116,233 +89,49 @@ TODO:
#define PCI9111_8254_CLOCK_PERIOD_NS 500
-#define PCI9111_8254_COUNTER_0 0x00
-#define PCI9111_8254_COUNTER_1 0x40
-#define PCI9111_8254_COUNTER_2 0x80
-#define PCI9111_8254_COUNTER_LATCH 0x00
-#define PCI9111_8254_READ_LOAD_LSB_ONLY 0x10
-#define PCI9111_8254_READ_LOAD_MSB_ONLY 0x20
-#define PCI9111_8254_READ_LOAD_LSB_MSB 0x30
-#define PCI9111_8254_MODE_0 0x00
-#define PCI9111_8254_MODE_1 0x02
-#define PCI9111_8254_MODE_2 0x04
-#define PCI9111_8254_MODE_3 0x06
-#define PCI9111_8254_MODE_4 0x08
-#define PCI9111_8254_MODE_5 0x0A
-#define PCI9111_8254_BINARY_COUNTER 0x00
-#define PCI9111_8254_BCD_COUNTER 0x01
-
-/* IO address map */
-
-#define PCI9111_REGISTER_AD_FIFO_VALUE 0x00 /* AD Data stored
- in FIFO */
-#define PCI9111_REGISTER_DA_OUTPUT 0x00
-#define PCI9111_REGISTER_DIGITAL_IO 0x02
-#define PCI9111_REGISTER_EXTENDED_IO_PORTS 0x04
-#define PCI9111_REGISTER_AD_CHANNEL_CONTROL 0x06 /* Channel
- selection */
-#define PCI9111_REGISTER_AD_CHANNEL_READBACK 0x06
-#define PCI9111_REGISTER_INPUT_SIGNAL_RANGE 0x08
-#define PCI9111_REGISTER_RANGE_STATUS_READBACK 0x08
-#define PCI9111_REGISTER_TRIGGER_MODE_CONTROL 0x0A
-#define PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK 0x0A
-#define PCI9111_REGISTER_SOFTWARE_TRIGGER 0x0E
-#define PCI9111_REGISTER_INTERRUPT_CONTROL 0x0C
-#define PCI9111_REGISTER_8254_COUNTER_0 0x40
-#define PCI9111_REGISTER_8254_COUNTER_1 0x42
-#define PCI9111_REGISTER_8254_COUNTER_2 0X44
-#define PCI9111_REGISTER_8254_CONTROL 0x46
-#define PCI9111_REGISTER_INTERRUPT_CLEAR 0x48
-
-#define PCI9111_TRIGGER_MASK 0x0F
-#define PCI9111_PTRG_OFF (0 << 3)
-#define PCI9111_PTRG_ON (1 << 3)
-#define PCI9111_EITS_EXTERNAL (1 << 2)
-#define PCI9111_EITS_INTERNAL (0 << 2)
-#define PCI9111_TPST_SOFTWARE_TRIGGER (0 << 1)
-#define PCI9111_TPST_TIMER_PACER (1 << 1)
-#define PCI9111_ASCAN_ON (1 << 0)
-#define PCI9111_ASCAN_OFF (0 << 0)
-
-#define PCI9111_ISC0_SET_IRQ_ON_ENDING_OF_AD_CONVERSION (0 << 0)
-#define PCI9111_ISC0_SET_IRQ_ON_FIFO_HALF_FULL (1 << 0)
-#define PCI9111_ISC1_SET_IRQ_ON_TIMER_TICK (0 << 1)
-#define PCI9111_ISC1_SET_IRQ_ON_EXT_TRG (1 << 1)
-#define PCI9111_FFEN_SET_FIFO_ENABLE (0 << 2)
-#define PCI9111_FFEN_SET_FIFO_DISABLE (1 << 2)
-
-#define PCI9111_CHANNEL_MASK 0x0F
-
-#define PCI9111_RANGE_MASK 0x07
-#define PCI9111_FIFO_EMPTY_MASK 0x10
-#define PCI9111_FIFO_HALF_FULL_MASK 0x20
-#define PCI9111_FIFO_FULL_MASK 0x40
-#define PCI9111_AD_BUSY_MASK 0x80
-
-#define PCI9111_IO_BASE (dev->iobase)
-
/*
- * Define inlined function
+ * IO address map and bit defines
*/
-
-#define pci9111_trigger_and_autoscan_get() \
- (inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK)&0x0F)
-
-#define pci9111_trigger_and_autoscan_set(flags) \
- outb(flags, PCI9111_IO_BASE+PCI9111_REGISTER_TRIGGER_MODE_CONTROL)
-
-#define pci9111_interrupt_and_fifo_get() \
- ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_MODE_INTERRUPT_READBACK) \
- >> 4) & 0x03)
-
-#define pci9111_interrupt_and_fifo_set(flags) \
- outb(flags, PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL)
-
-#define pci9111_interrupt_clear() \
- outb(0, PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CLEAR)
-
-#define pci9111_software_trigger() \
- outb(0, PCI9111_IO_BASE+PCI9111_REGISTER_SOFTWARE_TRIGGER)
-
-#define pci9111_fifo_reset() do { \
- outb(PCI9111_FFEN_SET_FIFO_ENABLE, \
- PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL); \
- outb(PCI9111_FFEN_SET_FIFO_DISABLE, \
- PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL); \
- outb(PCI9111_FFEN_SET_FIFO_ENABLE, \
- PCI9111_IO_BASE+PCI9111_REGISTER_INTERRUPT_CONTROL); \
- } while (0)
-
-#define pci9111_is_fifo_full() \
- ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
- PCI9111_FIFO_FULL_MASK) == 0)
-
-#define pci9111_is_fifo_half_full() \
- ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
- PCI9111_FIFO_HALF_FULL_MASK) == 0)
-
-#define pci9111_is_fifo_empty() \
- ((inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK)& \
- PCI9111_FIFO_EMPTY_MASK) == 0)
-
-#define pci9111_ai_channel_set(channel) \
- outb((channel)&PCI9111_CHANNEL_MASK, \
- PCI9111_IO_BASE+PCI9111_REGISTER_AD_CHANNEL_CONTROL)
-
-#define pci9111_ai_channel_get() \
- (inb(PCI9111_IO_BASE+PCI9111_REGISTER_AD_CHANNEL_READBACK) \
- &PCI9111_CHANNEL_MASK)
-
-#define pci9111_ai_range_set(range) \
- outb((range)&PCI9111_RANGE_MASK, \
- PCI9111_IO_BASE+PCI9111_REGISTER_INPUT_SIGNAL_RANGE)
-
-#define pci9111_ai_range_get() \
- (inb(PCI9111_IO_BASE+PCI9111_REGISTER_RANGE_STATUS_READBACK) \
- &PCI9111_RANGE_MASK)
-
-#define pci9111_ai_get_data() \
- (((inw(PCI9111_IO_BASE+PCI9111_REGISTER_AD_FIFO_VALUE)>>4) \
- &PCI9111_AI_RESOLUTION_MASK) \
- ^ PCI9111_AI_RESOLUTION_2_CMP_BIT)
-
-#define pci9111_hr_ai_get_data() \
- ((inw(PCI9111_IO_BASE+PCI9111_REGISTER_AD_FIFO_VALUE) \
- &PCI9111_HR_AI_RESOLUTION_MASK) \
- ^ PCI9111_HR_AI_RESOLUTION_2_CMP_BIT)
-
-#define pci9111_ao_set_data(data) \
- outw(data&PCI9111_AO_RESOLUTION_MASK, \
- PCI9111_IO_BASE+PCI9111_REGISTER_DA_OUTPUT)
-
-#define pci9111_di_get_bits() \
- inw(PCI9111_IO_BASE+PCI9111_REGISTER_DIGITAL_IO)
-
-#define pci9111_do_set_bits(bits) \
- outw(bits, PCI9111_IO_BASE+PCI9111_REGISTER_DIGITAL_IO)
-
-#define pci9111_8254_control_set(flags) \
- outb(flags, PCI9111_IO_BASE+PCI9111_REGISTER_8254_CONTROL)
-
-#define pci9111_8254_counter_0_set(data) \
- do { \
- outb(data & 0xFF, \
- PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_0); \
- outb((data >> 8) & 0xFF, \
- PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_0); \
- } while (0)
-
-#define pci9111_8254_counter_1_set(data) \
- do { \
- outb(data & 0xFF, \
- PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_1); \
- outb((data >> 8) & 0xFF, \
- PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_1); \
- } while (0)
-
-#define pci9111_8254_counter_2_set(data) \
- do { \
- outb(data & 0xFF, \
- PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_2); \
- outb((data >> 8) & 0xFF, \
- PCI9111_IO_BASE+PCI9111_REGISTER_8254_COUNTER_2); \
- } while (0)
-
-static const struct comedi_lrange pci9111_hr_ai_range = {
+#define PCI9111_AI_FIFO_REG 0x00
+#define PCI9111_AO_REG 0x00
+#define PCI9111_DIO_REG 0x02
+#define PCI9111_EDIO_REG 0x04
+#define PCI9111_AI_CHANNEL_REG 0x06
+#define PCI9111_AI_RANGE_STAT_REG 0x08
+#define PCI9111_AI_STAT_AD_BUSY (1 << 7)
+#define PCI9111_AI_STAT_FF_FF (1 << 6)
+#define PCI9111_AI_STAT_FF_HF (1 << 5)
+#define PCI9111_AI_STAT_FF_EF (1 << 4)
+#define PCI9111_AI_RANGE_MASK (7 << 0)
+#define PCI9111_AI_TRIG_CTRL_REG 0x0a
+#define PCI9111_AI_TRIG_CTRL_TRGEVENT (1 << 5)
+#define PCI9111_AI_TRIG_CTRL_POTRG (1 << 4)
+#define PCI9111_AI_TRIG_CTRL_PTRG (1 << 3)
+#define PCI9111_AI_TRIG_CTRL_ETIS (1 << 2)
+#define PCI9111_AI_TRIG_CTRL_TPST (1 << 1)
+#define PCI9111_AI_TRIG_CTRL_ASCAN (1 << 0)
+#define PCI9111_INT_CTRL_REG 0x0c
+#define PCI9111_INT_CTRL_ISC2 (1 << 3)
+#define PCI9111_INT_CTRL_FFEN (1 << 2)
+#define PCI9111_INT_CTRL_ISC1 (1 << 1)
+#define PCI9111_INT_CTRL_ISC0 (1 << 0)
+#define PCI9111_SOFT_TRIG_REG 0x0e
+#define PCI9111_8254_BASE_REG 0x40
+#define PCI9111_INT_CLR_REG 0x48
+
+static const struct comedi_lrange pci9111_ai_range = {
5,
{
- BIP_RANGE(10),
- BIP_RANGE(5),
- BIP_RANGE(2.5),
- BIP_RANGE(1.25),
- BIP_RANGE(0.625)
- }
-};
-
-/* */
-/* Board specification structure */
-/* */
-
-struct pci9111_board {
- const char *name; /* driver name */
- int device_id;
- int ai_channel_nbr; /* num of A/D chans */
- int ao_channel_nbr; /* num of D/A chans */
- int ai_resolution; /* resolution of A/D */
- int ai_resolution_mask;
- int ao_resolution; /* resolution of D/A */
- int ao_resolution_mask;
- const struct comedi_lrange *ai_range_list; /* rangelist for A/D */
- const struct comedi_lrange *ao_range_list; /* rangelist for D/A */
- unsigned int ai_acquisition_period_min_ns;
-};
-
-static const struct pci9111_board pci9111_boards[] = {
- {
- .name = "pci9111_hr",
- .device_id = PCI9111_HR_DEVICE_ID,
- .ai_channel_nbr = PCI9111_AI_CHANNEL_NBR,
- .ao_channel_nbr = PCI9111_AO_CHANNEL_NBR,
- .ai_resolution = PCI9111_HR_AI_RESOLUTION,
- .ai_resolution_mask = PCI9111_HR_AI_RESOLUTION_MASK,
- .ao_resolution = PCI9111_AO_RESOLUTION,
- .ao_resolution_mask = PCI9111_AO_RESOLUTION_MASK,
- .ai_range_list = &pci9111_hr_ai_range,
- .ao_range_list = &range_bipolar10,
- .ai_acquisition_period_min_ns = PCI9111_AI_ACQUISITION_PERIOD_MIN_NS}
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625)
+ }
};
-#define pci9111_board_nbr \
- (sizeof(pci9111_boards)/sizeof(struct pci9111_board))
-
-/* Private data structure */
-
struct pci9111_private_data {
- unsigned long io_range; /* PCI6503 io range */
-
- unsigned long lcr_io_base; /* Local configuration register base
- * address */
- unsigned long lcr_io_range;
+ unsigned long lcr_io_base;
int stop_counter;
int stop_is_none;
@@ -352,23 +141,14 @@ struct pci9111_private_data {
unsigned int chunk_counter;
unsigned int chunk_num_samples;
- int ao_readback; /* Last written analog output data */
+ int ao_readback;
- unsigned int timer_divisor_1; /* Divisor values for the 8254 timer
- * pacer */
- unsigned int timer_divisor_2;
-
- int is_valid; /* Is device valid */
+ unsigned int div1;
+ unsigned int div2;
short ai_bounce_buffer[2 * PCI9111_FIFO_HALF_SIZE];
};
-#define dev_private ((struct pci9111_private_data *)dev->private)
-
-/* ------------------------------------------------------------------ */
-/* PLX9050 SECTION */
-/* ------------------------------------------------------------------ */
-
#define PLX9050_REGISTER_INTERRUPT_CONTROL 0x4c
#define PLX9050_LINTI1_ENABLE (1 << 0)
@@ -404,33 +184,19 @@ static void plx9050_interrupt_control(unsigned long io_base,
outb(flags, io_base + PLX9050_REGISTER_INTERRUPT_CONTROL);
}
-/* ------------------------------------------------------------------ */
-/* MISCELLANEOUS SECTION */
-/* ------------------------------------------------------------------ */
-
-/* 8254 timer */
-
static void pci9111_timer_set(struct comedi_device *dev)
{
- pci9111_8254_control_set(PCI9111_8254_COUNTER_0 |
- PCI9111_8254_READ_LOAD_LSB_MSB |
- PCI9111_8254_MODE_0 |
- PCI9111_8254_BINARY_COUNTER);
+ struct pci9111_private_data *dev_private = dev->private;
+ unsigned long timer_base = dev->iobase + PCI9111_8254_BASE_REG;
- pci9111_8254_control_set(PCI9111_8254_COUNTER_1 |
- PCI9111_8254_READ_LOAD_LSB_MSB |
- PCI9111_8254_MODE_2 |
- PCI9111_8254_BINARY_COUNTER);
-
- pci9111_8254_control_set(PCI9111_8254_COUNTER_2 |
- PCI9111_8254_READ_LOAD_LSB_MSB |
- PCI9111_8254_MODE_2 |
- PCI9111_8254_BINARY_COUNTER);
+ i8254_set_mode(timer_base, 1, 0, I8254_MODE0 | I8254_BINARY);
+ i8254_set_mode(timer_base, 1, 1, I8254_MODE2 | I8254_BINARY);
+ i8254_set_mode(timer_base, 1, 2, I8254_MODE2 | I8254_BINARY);
udelay(1);
- pci9111_8254_counter_2_set(dev_private->timer_divisor_2);
- pci9111_8254_counter_1_set(dev_private->timer_divisor_1);
+ i8254_write(timer_base, 1, 2, dev_private->div2);
+ i8254_write(timer_base, 1, 1, dev_private->div1);
}
enum pci9111_trigger_sources {
@@ -444,47 +210,55 @@ static void pci9111_trigger_source_set(struct comedi_device *dev,
{
int flags;
- flags = pci9111_trigger_and_autoscan_get() & 0x09;
+ /* Read the current trigger mode control bits */
+ flags = inb(dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
+ /* Mask off the EITS and TPST bits */
+ flags &= 0x9;
switch (source) {
case software:
- flags |= PCI9111_EITS_INTERNAL | PCI9111_TPST_SOFTWARE_TRIGGER;
break;
case timer_pacer:
- flags |= PCI9111_EITS_INTERNAL | PCI9111_TPST_TIMER_PACER;
+ flags |= PCI9111_AI_TRIG_CTRL_TPST;
break;
case external:
- flags |= PCI9111_EITS_EXTERNAL;
+ flags |= PCI9111_AI_TRIG_CTRL_ETIS;
break;
}
- pci9111_trigger_and_autoscan_set(flags);
+ outb(flags, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
}
static void pci9111_pretrigger_set(struct comedi_device *dev, bool pretrigger)
{
int flags;
- flags = pci9111_trigger_and_autoscan_get() & 0x07;
+ /* Read the current trigger mode control bits */
+ flags = inb(dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
+ /* Mask off the PTRG bit */
+ flags &= 0x7;
if (pretrigger)
- flags |= PCI9111_PTRG_ON;
+ flags |= PCI9111_AI_TRIG_CTRL_PTRG;
- pci9111_trigger_and_autoscan_set(flags);
+ outb(flags, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
}
static void pci9111_autoscan_set(struct comedi_device *dev, bool autoscan)
{
int flags;
- flags = pci9111_trigger_and_autoscan_get() & 0x0e;
+ /* Read the current trigger mode control bits */
+ flags = inb(dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
+ /* Mask off the ASCAN bit */
+ flags &= 0xe;
if (autoscan)
- flags |= PCI9111_ASCAN_ON;
+ flags |= PCI9111_AI_TRIG_CTRL_ASCAN;
- pci9111_trigger_and_autoscan_set(flags);
+ outb(flags, dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
}
enum pci9111_ISC0_sources {
@@ -503,30 +277,39 @@ static void pci9111_interrupt_source_set(struct comedi_device *dev,
{
int flags;
- flags = pci9111_interrupt_and_fifo_get() & 0x04;
+ /* Read the current interrupt control bits */
+ flags = inb(dev->iobase + PCI9111_AI_TRIG_CTRL_REG);
+ /* Shift the bits so they are compatible with the write register */
+ flags >>= 4;
+ /* Mask off the ISCx bits */
+ flags &= 0xc0;
+ /* Now set the new ISCx bits */
if (irq_0_source == irq_on_fifo_half_full)
- flags |= PCI9111_ISC0_SET_IRQ_ON_FIFO_HALF_FULL;
+ flags |= PCI9111_INT_CTRL_ISC0;
if (irq_1_source == irq_on_external_trigger)
- flags |= PCI9111_ISC1_SET_IRQ_ON_EXT_TRG;
+ flags |= PCI9111_INT_CTRL_ISC1;
- pci9111_interrupt_and_fifo_set(flags);
+ outb(flags, dev->iobase + PCI9111_INT_CTRL_REG);
}
-/* ------------------------------------------------------------------ */
-/* HARDWARE TRIGGERED ANALOG INPUT SECTION */
-/* ------------------------------------------------------------------ */
-
-/* Cancel analog input autoscan */
+static void pci9111_fifo_reset(struct comedi_device *dev)
+{
+ unsigned long int_ctrl_reg = dev->iobase + PCI9111_INT_CTRL_REG;
-#undef AI_DO_CMD_DEBUG
+ /* To reset the FIFO, set FFEN sequence as 0 -> 1 -> 0 */
+ outb(0, int_ctrl_reg);
+ outb(PCI9111_INT_CTRL_FFEN, int_ctrl_reg);
+ outb(0, int_ctrl_reg);
+}
static int pci9111_ai_cancel(struct comedi_device *dev,
struct comedi_subdevice *s)
{
- /* Disable interrupts */
+ struct pci9111_private_data *dev_private = dev->private;
+ /* Disable interrupts */
plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
true, false);
@@ -534,97 +317,65 @@ static int pci9111_ai_cancel(struct comedi_device *dev,
pci9111_autoscan_set(dev, false);
- pci9111_fifo_reset();
-
-#ifdef AI_DO_CMD_DEBUG
- printk(PCI9111_DRIVER_NAME ": ai_cancel\n");
-#endif
+ pci9111_fifo_reset(dev);
return 0;
}
-/* Test analog input command */
-
-#define pci9111_check_trigger_src(src, flags) do { \
- tmp = src; \
- src &= flags; \
- if (!src || tmp != src) \
- error++; \
- } while (false);
-
-static int
-pci9111_ai_do_cmd_test(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_cmd *cmd)
+static int pci9111_ai_do_cmd_test(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
{
+ struct pci9111_private_data *dev_private = dev->private;
int tmp;
int error = 0;
int range, reference;
int i;
- struct pci9111_board *board = (struct pci9111_board *)dev->board_ptr;
- /* Step 1 : check if trigger are trivialy valid */
+ /* Step 1 : check if triggers are trivially valid */
- pci9111_check_trigger_src(cmd->start_src, TRIG_NOW);
- pci9111_check_trigger_src(cmd->scan_begin_src,
- TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
- pci9111_check_trigger_src(cmd->convert_src, TRIG_TIMER | TRIG_EXT);
- pci9111_check_trigger_src(cmd->scan_end_src, TRIG_COUNT);
- pci9111_check_trigger_src(cmd->stop_src, TRIG_COUNT | TRIG_NONE);
+ error |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ error |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
+ error |= cfc_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ error |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ error |= cfc_check_trigger_src(&cmd->stop_src,
+ TRIG_COUNT | TRIG_NONE);
if (error)
return 1;
- /* step 2 : make sure trigger sources are unique and mutually
- * compatible */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->start_src != TRIG_NOW)
- error++;
+ error |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ error |= cfc_check_trigger_is_unique(cmd->convert_src);
+ error |= cfc_check_trigger_is_unique(cmd->stop_src);
- if ((cmd->scan_begin_src != TRIG_TIMER) &&
- (cmd->scan_begin_src != TRIG_FOLLOW) &&
- (cmd->scan_begin_src != TRIG_EXT))
- error++;
+ /* Step 2b : and mutually compatible */
- if ((cmd->convert_src != TRIG_TIMER) && (cmd->convert_src != TRIG_EXT))
- error++;
if ((cmd->convert_src == TRIG_TIMER) &&
!((cmd->scan_begin_src == TRIG_TIMER) ||
(cmd->scan_begin_src == TRIG_FOLLOW)))
- error++;
+ error |= -EINVAL;
if ((cmd->convert_src == TRIG_EXT) &&
!((cmd->scan_begin_src == TRIG_EXT) ||
(cmd->scan_begin_src == TRIG_FOLLOW)))
- error++;
-
-
- if (cmd->scan_end_src != TRIG_COUNT)
- error++;
- if ((cmd->stop_src != TRIG_COUNT) && (cmd->stop_src != TRIG_NONE))
- error++;
+ error |= -EINVAL;
if (error)
return 2;
/* Step 3 : make sure arguments are trivialy compatible */
- if (cmd->chanlist_len < 1) {
- cmd->chanlist_len = 1;
- error++;
- }
-
- if (cmd->chanlist_len > board->ai_channel_nbr) {
- cmd->chanlist_len = board->ai_channel_nbr;
- error++;
- }
-
if ((cmd->start_src == TRIG_NOW) && (cmd->start_arg != 0)) {
cmd->start_arg = 0;
error++;
}
if ((cmd->convert_src == TRIG_TIMER) &&
- (cmd->convert_arg < board->ai_acquisition_period_min_ns)) {
- cmd->convert_arg = board->ai_acquisition_period_min_ns;
+ (cmd->convert_arg < PCI9111_AI_ACQUISITION_PERIOD_MIN_NS)) {
+ cmd->convert_arg = PCI9111_AI_ACQUISITION_PERIOD_MIN_NS;
error++;
}
if ((cmd->convert_src == TRIG_EXT) && (cmd->convert_arg != 0)) {
@@ -633,8 +384,8 @@ pci9111_ai_do_cmd_test(struct comedi_device *dev,
}
if ((cmd->scan_begin_src == TRIG_TIMER) &&
- (cmd->scan_begin_arg < board->ai_acquisition_period_min_ns)) {
- cmd->scan_begin_arg = board->ai_acquisition_period_min_ns;
+ (cmd->scan_begin_arg < PCI9111_AI_ACQUISITION_PERIOD_MIN_NS)) {
+ cmd->scan_begin_arg = PCI9111_AI_ACQUISITION_PERIOD_MIN_NS;
error++;
}
if ((cmd->scan_begin_src == TRIG_FOLLOW)
@@ -670,9 +421,9 @@ pci9111_ai_do_cmd_test(struct comedi_device *dev,
if (cmd->convert_src == TRIG_TIMER) {
tmp = cmd->convert_arg;
i8253_cascade_ns_to_timer_2div(PCI9111_8254_CLOCK_PERIOD_NS,
- &(dev_private->timer_divisor_1),
- &(dev_private->timer_divisor_2),
- &(cmd->convert_arg),
+ &dev_private->div1,
+ &dev_private->div2,
+ &cmd->convert_arg,
cmd->flags & TRIG_ROUND_MASK);
if (tmp != cmd->convert_arg)
error++;
@@ -733,14 +484,6 @@ pci9111_ai_do_cmd_test(struct comedi_device *dev,
error++;
}
}
- } else {
- if ((CR_CHAN(cmd->chanlist[0]) >
- (board->ai_channel_nbr - 1))
- || (CR_CHAN(cmd->chanlist[0]) < 0)) {
- comedi_error(dev,
- "channel number is out of limits\n");
- error++;
- }
}
}
@@ -751,12 +494,11 @@ pci9111_ai_do_cmd_test(struct comedi_device *dev,
}
-/* Analog input command */
-
static int pci9111_ai_do_cmd(struct comedi_device *dev,
- struct comedi_subdevice *subdevice)
+ struct comedi_subdevice *s)
{
- struct comedi_cmd *async_cmd = &subdevice->async->cmd;
+ struct pci9111_private_data *dev_private = dev->private;
+ struct comedi_cmd *async_cmd = &s->async->cmd;
if (!dev->irq) {
comedi_error(dev,
@@ -768,17 +510,20 @@ static int pci9111_ai_do_cmd(struct comedi_device *dev,
/* TODO: handle the case of an external multiplexer */
if (async_cmd->chanlist_len > 1) {
- pci9111_ai_channel_set((async_cmd->chanlist_len) - 1);
+ outb(async_cmd->chanlist_len - 1,
+ dev->iobase + PCI9111_AI_CHANNEL_REG);
pci9111_autoscan_set(dev, true);
} else {
- pci9111_ai_channel_set(CR_CHAN(async_cmd->chanlist[0]));
+ outb(CR_CHAN(async_cmd->chanlist[0]),
+ dev->iobase + PCI9111_AI_CHANNEL_REG);
pci9111_autoscan_set(dev, false);
}
/* Set gain */
/* This is the same gain on every channel */
- pci9111_ai_range_set(CR_RANGE(async_cmd->chanlist[0]));
+ outb(CR_RANGE(async_cmd->chanlist[0]) & PCI9111_AI_RANGE_MASK,
+ dev->iobase + PCI9111_AI_RANGE_STAT_REG);
/* Set counter */
@@ -804,21 +549,9 @@ static int pci9111_ai_do_cmd(struct comedi_device *dev,
dev_private->scan_delay = 0;
switch (async_cmd->convert_src) {
case TRIG_TIMER:
- i8253_cascade_ns_to_timer_2div(PCI9111_8254_CLOCK_PERIOD_NS,
- &(dev_private->timer_divisor_1),
- &(dev_private->timer_divisor_2),
- &(async_cmd->convert_arg),
- async_cmd->
- flags & TRIG_ROUND_MASK);
-#ifdef AI_DO_CMD_DEBUG
- printk(PCI9111_DRIVER_NAME ": divisors = %d, %d\n",
- dev_private->timer_divisor_1,
- dev_private->timer_divisor_2);
-#endif
-
pci9111_trigger_source_set(dev, software);
pci9111_timer_set(dev);
- pci9111_fifo_reset();
+ pci9111_fifo_reset(dev);
pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
irq_on_timer_tick);
pci9111_trigger_source_set(dev, timer_pacer);
@@ -837,7 +570,7 @@ static int pci9111_ai_do_cmd(struct comedi_device *dev,
case TRIG_EXT:
pci9111_trigger_source_set(dev, external);
- pci9111_fifo_reset();
+ pci9111_fifo_reset(dev);
pci9111_interrupt_source_set(dev, irq_on_fifo_half_full,
irq_on_timer_tick);
plx9050_interrupt_control(dev_private->lcr_io_base, true, true,
@@ -856,23 +589,6 @@ static int pci9111_ai_do_cmd(struct comedi_device *dev,
dev_private->chunk_num_samples =
dev_private->chanlist_len * (1 + dev_private->scan_delay);
-#ifdef AI_DO_CMD_DEBUG
- printk(PCI9111_DRIVER_NAME ": start interruptions!\n");
- printk(PCI9111_DRIVER_NAME ": trigger source = %2x\n",
- pci9111_trigger_and_autoscan_get());
- printk(PCI9111_DRIVER_NAME ": irq source = %2x\n",
- pci9111_interrupt_and_fifo_get());
- printk(PCI9111_DRIVER_NAME ": ai_do_cmd\n");
- printk(PCI9111_DRIVER_NAME ": stop counter = %d\n",
- dev_private->stop_counter);
- printk(PCI9111_DRIVER_NAME ": scan delay = %d\n",
- dev_private->scan_delay);
- printk(PCI9111_DRIVER_NAME ": chanlist_len = %d\n",
- dev_private->chanlist_len);
- printk(PCI9111_DRIVER_NAME ": chunk num samples = %d\n",
- dev_private->chunk_num_samples);
-#endif
-
return 0;
}
@@ -881,34 +597,24 @@ static void pci9111_ai_munge(struct comedi_device *dev,
unsigned int num_bytes,
unsigned int start_chan_index)
{
- unsigned int i, num_samples = num_bytes / sizeof(short);
short *array = data;
- int resolution =
- ((struct pci9111_board *)dev->board_ptr)->ai_resolution;
-
- for (i = 0; i < num_samples; i++) {
- if (resolution == PCI9111_HR_AI_RESOLUTION)
- array[i] =
- (array[i] & PCI9111_HR_AI_RESOLUTION_MASK) ^
- PCI9111_HR_AI_RESOLUTION_2_CMP_BIT;
- else
- array[i] =
- ((array[i] >> 4) & PCI9111_AI_RESOLUTION_MASK) ^
- PCI9111_AI_RESOLUTION_2_CMP_BIT;
- }
+ unsigned int maxdata = s->maxdata;
+ unsigned int invert = (maxdata + 1) >> 1;
+ unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
+ unsigned int num_samples = num_bytes / sizeof(short);
+ unsigned int i;
+
+ for (i = 0; i < num_samples; i++)
+ array[i] = ((array[i] >> shift) & maxdata) ^ invert;
}
-/* ------------------------------------------------------------------ */
-/* INTERRUPT SECTION */
-/* ------------------------------------------------------------------ */
-
-#undef INTERRUPT_DEBUG
-
static irqreturn_t pci9111_interrupt(int irq, void *p_device)
{
struct comedi_device *dev = p_device;
- struct comedi_subdevice *subdevice = dev->read_subdev;
+ struct pci9111_private_data *dev_private = dev->private;
+ struct comedi_subdevice *s = dev->read_subdev;
struct comedi_async *async;
+ unsigned int status;
unsigned long irq_flags;
unsigned char intcsr;
@@ -918,7 +624,7 @@ static irqreturn_t pci9111_interrupt(int irq, void *p_device)
return IRQ_NONE;
}
- async = subdevice->async;
+ async = s->async;
spin_lock_irqsave(&dev->spinlock, irq_flags);
@@ -940,37 +646,37 @@ static irqreturn_t pci9111_interrupt(int irq, void *p_device)
(PLX9050_LINTI1_ENABLE | PLX9050_LINTI1_STATUS)) {
/* Interrupt comes from fifo_half-full signal */
- if (pci9111_is_fifo_full()) {
+ status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
+
+ /* '0' means FIFO is full, data may have been lost */
+ if (!(status & PCI9111_AI_STAT_FF_FF)) {
spin_unlock_irqrestore(&dev->spinlock, irq_flags);
comedi_error(dev, PCI9111_DRIVER_NAME " fifo overflow");
- pci9111_interrupt_clear();
- pci9111_ai_cancel(dev, subdevice);
+ outb(0, dev->iobase + PCI9111_INT_CLR_REG);
+ pci9111_ai_cancel(dev, s);
async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
- comedi_event(dev, subdevice);
+ comedi_event(dev, s);
return IRQ_HANDLED;
}
- if (pci9111_is_fifo_half_full()) {
+ /* '0' means FIFO is half-full */
+ if (!(status & PCI9111_AI_STAT_FF_HF)) {
unsigned int num_samples;
unsigned int bytes_written = 0;
-#ifdef INTERRUPT_DEBUG
- printk(PCI9111_DRIVER_NAME ": fifo is half full\n");
-#endif
-
num_samples =
PCI9111_FIFO_HALF_SIZE >
dev_private->stop_counter
&& !dev_private->
stop_is_none ? dev_private->stop_counter :
PCI9111_FIFO_HALF_SIZE;
- insw(PCI9111_IO_BASE + PCI9111_REGISTER_AD_FIFO_VALUE,
+ insw(dev->iobase + PCI9111_AI_FIFO_REG,
dev_private->ai_bounce_buffer, num_samples);
if (dev_private->scan_delay < 1) {
bytes_written =
- cfc_write_array_to_buffer(subdevice,
+ cfc_write_array_to_buffer(s,
dev_private->
ai_bounce_buffer,
num_samples *
@@ -994,7 +700,7 @@ static irqreturn_t pci9111_interrupt(int irq, void *p_device)
bytes_written +=
cfc_write_array_to_buffer
- (subdevice,
+ (s,
dev_private->ai_bounce_buffer
+ position,
to_read * sizeof(short));
@@ -1029,168 +735,135 @@ static irqreturn_t pci9111_interrupt(int irq, void *p_device)
if ((dev_private->stop_counter == 0) && (!dev_private->stop_is_none)) {
async->events |= COMEDI_CB_EOA;
- pci9111_ai_cancel(dev, subdevice);
+ pci9111_ai_cancel(dev, s);
}
- /* Very important, otherwise another interrupt request will be inserted
- * and will cause driver hangs on processing interrupt event. */
-
- pci9111_interrupt_clear();
+ outb(0, dev->iobase + PCI9111_INT_CLR_REG);
spin_unlock_irqrestore(&dev->spinlock, irq_flags);
- comedi_event(dev, subdevice);
+ comedi_event(dev, s);
return IRQ_HANDLED;
}
-/* ------------------------------------------------------------------ */
-/* INSTANT ANALOG INPUT OUTPUT SECTION */
-/* ------------------------------------------------------------------ */
-
-/* analog instant input */
-
-#undef AI_INSN_DEBUG
-
static int pci9111_ai_insn_read(struct comedi_device *dev,
- struct comedi_subdevice *subdevice,
+ struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
- int resolution =
- ((struct pci9111_board *)dev->board_ptr)->ai_resolution;
-
- int timeout, i;
-
-#ifdef AI_INSN_DEBUG
- printk(PCI9111_DRIVER_NAME ": ai_insn set c/r/n = %2x/%2x/%2x\n",
- CR_CHAN((&insn->chanspec)[0]),
- CR_RANGE((&insn->chanspec)[0]), insn->n);
-#endif
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int maxdata = s->maxdata;
+ unsigned int invert = (maxdata + 1) >> 1;
+ unsigned int shift = (maxdata == 0xffff) ? 0 : 4;
+ unsigned int status;
+ int timeout;
+ int i;
- pci9111_ai_channel_set(CR_CHAN((&insn->chanspec)[0]));
+ outb(chan, dev->iobase + PCI9111_AI_CHANNEL_REG);
- if ((pci9111_ai_range_get()) != CR_RANGE((&insn->chanspec)[0]))
- pci9111_ai_range_set(CR_RANGE((&insn->chanspec)[0]));
+ status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
+ if ((status & PCI9111_AI_RANGE_MASK) != range) {
+ outb(range & PCI9111_AI_RANGE_MASK,
+ dev->iobase + PCI9111_AI_RANGE_STAT_REG);
+ }
- pci9111_fifo_reset();
+ pci9111_fifo_reset(dev);
for (i = 0; i < insn->n; i++) {
- pci9111_software_trigger();
+ /* Generate a software trigger */
+ outb(0, dev->iobase + PCI9111_SOFT_TRIG_REG);
timeout = PCI9111_AI_INSTANT_READ_TIMEOUT;
while (timeout--) {
- if (!pci9111_is_fifo_empty())
+ status = inb(dev->iobase + PCI9111_AI_RANGE_STAT_REG);
+ /* '1' means FIFO is not empty */
+ if (status & PCI9111_AI_STAT_FF_EF)
goto conversion_done;
}
comedi_error(dev, "A/D read timeout");
data[i] = 0;
- pci9111_fifo_reset();
+ pci9111_fifo_reset(dev);
return -ETIME;
conversion_done:
- if (resolution == PCI9111_HR_AI_RESOLUTION)
- data[i] = pci9111_hr_ai_get_data();
- else
- data[i] = pci9111_ai_get_data();
+ data[i] = inw(dev->iobase + PCI9111_AI_FIFO_REG);
+ data[i] = ((data[i] >> shift) & maxdata) ^ invert;
}
-#ifdef AI_INSN_DEBUG
- printk(PCI9111_DRIVER_NAME ": ai_insn get c/r/t = %2x/%2x/%2x\n",
- pci9111_ai_channel_get(),
- pci9111_ai_range_get(), pci9111_trigger_and_autoscan_get());
-#endif
-
return i;
}
-/* Analog instant output */
-
-static int
-pci9111_ao_insn_write(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data)
+static int pci9111_ao_insn_write(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
+ struct pci9111_private_data *dev_private = dev->private;
+ unsigned int val = 0;
int i;
for (i = 0; i < insn->n; i++) {
- pci9111_ao_set_data(data[i]);
- dev_private->ao_readback = data[i];
+ val = data[i];
+ outw(val, dev->iobase + PCI9111_AO_REG);
}
+ dev_private->ao_readback = val;
- return i;
+ return insn->n;
}
-/* Analog output readback */
-
static int pci9111_ao_insn_read(struct comedi_device *dev,
struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+ struct comedi_insn *insn,
+ unsigned int *data)
{
+ struct pci9111_private_data *dev_private = dev->private;
int i;
for (i = 0; i < insn->n; i++)
- data[i] = dev_private->ao_readback & PCI9111_AO_RESOLUTION_MASK;
+ data[i] = dev_private->ao_readback;
- return i;
+ return insn->n;
}
-/* ------------------------------------------------------------------ */
-/* DIGITAL INPUT OUTPUT SECTION */
-/* ------------------------------------------------------------------ */
-
-/* Digital inputs */
-
static int pci9111_di_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *subdevice,
- struct comedi_insn *insn, unsigned int *data)
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
- unsigned int bits;
-
- bits = pci9111_di_get_bits();
- data[1] = bits;
+ data[1] = inw(dev->iobase + PCI9111_DIO_REG);
return insn->n;
}
-/* Digital outputs */
-
static int pci9111_do_insn_bits(struct comedi_device *dev,
- struct comedi_subdevice *subdevice,
- struct comedi_insn *insn, unsigned int *data)
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
- unsigned int bits;
-
- /* Only set bits that have been masked */
- /* data[0] = mask */
- /* data[1] = bit state */
+ unsigned int mask = data[0];
+ unsigned int bits = data[1];
- data[0] &= PCI9111_DO_MASK;
+ if (mask) {
+ s->state &= ~mask;
+ s->state |= (bits & mask);
- bits = subdevice->state;
- bits &= ~data[0];
- bits |= data[0] & data[1];
- subdevice->state = bits;
-
- pci9111_do_set_bits(bits);
+ outw(s->state, dev->iobase + PCI9111_DIO_REG);
+ }
- data[1] = bits;
+ data[1] = s->state;
return insn->n;
}
-/* ------------------------------------------------------------------ */
-/* INITIALISATION SECTION */
-/* ------------------------------------------------------------------ */
-
-/* Reset device */
-
static int pci9111_reset(struct comedi_device *dev)
{
- /* Set trigger source to software */
+ struct pci9111_private_data *dev_private = dev->private;
+ /* Set trigger source to software */
plx9050_interrupt_control(dev_private->lcr_io_base, true, true, true,
true, false);
@@ -1198,177 +871,90 @@ static int pci9111_reset(struct comedi_device *dev)
pci9111_pretrigger_set(dev, false);
pci9111_autoscan_set(dev, false);
- /* Reset 8254 chip */
-
- dev_private->timer_divisor_1 = 0;
- dev_private->timer_divisor_2 = 0;
-
+ /* Reset 8254 chip */
+ dev_private->div1 = 0;
+ dev_private->div2 = 0;
pci9111_timer_set(dev);
return 0;
}
-static struct pci_dev *pci9111_find_pci(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int pci9111_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
- int i;
-
- for_each_pci_dev(pcidev) {
- if (pcidev->vendor != PCI_VENDOR_ID_ADLINK)
- continue;
- for (i = 0; i < pci9111_board_nbr; i++) {
- if (pcidev->device != pci9111_boards[i].device_id)
- continue;
- if (bus || slot) {
- /* requested particular bus/slot */
- if (pcidev->bus->number != bus ||
- PCI_SLOT(pcidev->devfn) != slot)
- continue;
- }
- dev->board_ptr = pci9111_boards + i;
- printk(KERN_ERR
- "comedi%d: found %s (b:s:f=%d:%d:%d), irq=%d\n",
- dev->minor, pci9111_boards[i].name,
- pcidev->bus->number, PCI_SLOT(pcidev->devfn),
- PCI_FUNC(pcidev->devfn), pcidev->irq);
- return pcidev;
- }
- }
- printk(KERN_ERR
- "comedi%d: no supported board found! (req. bus/slot : %d/%d)\n",
- dev->minor, bus, slot);
- return NULL;
-}
+ struct pci9111_private_data *dev_private;
+ struct comedi_subdevice *s;
+ int ret;
-static int pci9111_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev;
- struct comedi_subdevice *subdevice;
- unsigned long io_base, io_range, lcr_io_base, lcr_io_range;
- int error;
- const struct pci9111_board *board;
-
- if (alloc_private(dev, sizeof(struct pci9111_private_data)) < 0)
- return -ENOMEM;
- /* Probe the device to determine what device in the series it is. */
-
- printk(KERN_ERR "comedi%d: " PCI9111_DRIVER_NAME " driver\n",
- dev->minor);
-
- pcidev = pci9111_find_pci(dev, it);
- if (!pcidev)
- return -EIO;
comedi_set_hw_dev(dev, &pcidev->dev);
- board = (struct pci9111_board *)dev->board_ptr;
-
- /* TODO: Warn about non-tested boards. */
-
- /* Read local configuration register base address
- * [PCI_BASE_ADDRESS #1]. */
-
- lcr_io_base = pci_resource_start(pcidev, 1);
- lcr_io_range = pci_resource_len(pcidev, 1);
-
- printk
- ("comedi%d: local configuration registers at address 0x%4lx [0x%4lx]\n",
- dev->minor, lcr_io_base, lcr_io_range);
-
- /* Enable PCI device and request regions */
- if (comedi_pci_enable(pcidev, PCI9111_DRIVER_NAME) < 0) {
- printk
- ("comedi%d: Failed to enable PCI device and request regions\n",
- dev->minor);
- return -EIO;
- }
- /* Read PCI6308 register base address [PCI_BASE_ADDRESS #2]. */
+ dev->board_name = dev->driver->driver_name;
- io_base = pci_resource_start(pcidev, 2);
- io_range = pci_resource_len(pcidev, 2);
+ ret = alloc_private(dev, sizeof(*dev_private));
+ if (ret)
+ return ret;
+ dev_private = dev->private;
- printk(KERN_ERR "comedi%d: 6503 registers at address 0x%4lx [0x%4lx]\n",
- dev->minor, io_base, io_range);
-
- dev->iobase = io_base;
- dev->board_name = board->name;
- dev_private->io_range = io_range;
- dev_private->is_valid = 0;
- dev_private->lcr_io_base = lcr_io_base;
- dev_private->lcr_io_range = lcr_io_range;
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
+ dev_private->lcr_io_base = pci_resource_start(pcidev, 1);
+ dev->iobase = pci_resource_start(pcidev, 2);
pci9111_reset(dev);
- /* Irq setup */
-
- dev->irq = 0;
if (pcidev->irq > 0) {
+ ret = request_irq(dev->irq, pci9111_interrupt,
+ IRQF_SHARED, dev->board_name, dev);
+ if (ret)
+ return ret;
dev->irq = pcidev->irq;
-
- if (request_irq(dev->irq, pci9111_interrupt,
- IRQF_SHARED, PCI9111_DRIVER_NAME, dev) != 0) {
- printk(KERN_ERR
- "comedi%d: unable to allocate irq %u\n",
- dev->minor, dev->irq);
- return -EINVAL;
- }
}
- /* TODO: Add external multiplexer setup (according to option[2]). */
-
- error = comedi_alloc_subdevices(dev, 4);
- if (error)
- return error;
-
- subdevice = dev->subdevices + 0;
- dev->read_subdev = subdevice;
-
- subdevice->type = COMEDI_SUBD_AI;
- subdevice->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_CMD_READ;
-
- /* TODO: Add external multiplexer data */
- /* if (devpriv->usemux) { subdevice->n_chan = devpriv->usemux; } */
- /* else { subdevice->n_chan = this_board->n_aichan; } */
-
- subdevice->n_chan = board->ai_channel_nbr;
- subdevice->maxdata = board->ai_resolution_mask;
- subdevice->len_chanlist = board->ai_channel_nbr;
- subdevice->range_table = board->ai_range_list;
- subdevice->cancel = pci9111_ai_cancel;
- subdevice->insn_read = pci9111_ai_insn_read;
- subdevice->do_cmdtest = pci9111_ai_do_cmd_test;
- subdevice->do_cmd = pci9111_ai_do_cmd;
- subdevice->munge = pci9111_ai_munge;
-
- subdevice = dev->subdevices + 1;
- subdevice->type = COMEDI_SUBD_AO;
- subdevice->subdev_flags = SDF_WRITABLE | SDF_COMMON;
- subdevice->n_chan = board->ao_channel_nbr;
- subdevice->maxdata = board->ao_resolution_mask;
- subdevice->len_chanlist = board->ao_channel_nbr;
- subdevice->range_table = board->ao_range_list;
- subdevice->insn_write = pci9111_ao_insn_write;
- subdevice->insn_read = pci9111_ao_insn_read;
-
- subdevice = dev->subdevices + 2;
- subdevice->type = COMEDI_SUBD_DI;
- subdevice->subdev_flags = SDF_READABLE;
- subdevice->n_chan = PCI9111_DI_CHANNEL_NBR;
- subdevice->maxdata = 1;
- subdevice->range_table = &range_digital;
- subdevice->insn_bits = pci9111_di_insn_bits;
-
- subdevice = dev->subdevices + 3;
- subdevice->type = COMEDI_SUBD_DO;
- subdevice->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- subdevice->n_chan = PCI9111_DO_CHANNEL_NBR;
- subdevice->maxdata = 1;
- subdevice->range_table = &range_digital;
- subdevice->insn_bits = pci9111_do_insn_bits;
-
- dev_private->is_valid = 1;
+ ret = comedi_alloc_subdevices(dev, 4);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_CMD_READ;
+ s->n_chan = 16;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 16;
+ s->range_table = &pci9111_ai_range;
+ s->cancel = pci9111_ai_cancel;
+ s->insn_read = pci9111_ai_insn_read;
+ s->do_cmdtest = pci9111_ai_do_cmd_test;
+ s->do_cmd = pci9111_ai_do_cmd;
+ s->munge = pci9111_ai_munge;
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_COMMON;
+ s->n_chan = 1;
+ s->maxdata = 0x0fff;
+ s->len_chanlist = 1;
+ s->range_table = &range_bipolar10;
+ s->insn_write = pci9111_ao_insn_write;
+ s->insn_read = pci9111_ao_insn_read;
+
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci9111_di_insn_bits;
+
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = pci9111_do_insn_bits;
+
+ dev_info(dev->class_dev, "%s attached\n", dev->board_name);
return 0;
}
@@ -1377,23 +963,20 @@ static void pci9111_detach(struct comedi_device *dev)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
- if (dev->private != NULL) {
- if (dev_private->is_valid)
- pci9111_reset(dev);
- }
+ if (dev->iobase)
+ pci9111_reset(dev);
if (dev->irq != 0)
free_irq(dev->irq, dev);
if (pcidev) {
if (dev->iobase)
comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
}
}
static struct comedi_driver adl_pci9111_driver = {
.driver_name = "adl_pci9111",
.module = THIS_MODULE,
- .attach = pci9111_attach,
+ .attach_pci = pci9111_attach_pci,
.detach = pci9111_detach,
};
diff --git a/drivers/staging/comedi/drivers/adl_pci9118.c b/drivers/staging/comedi/drivers/adl_pci9118.c
index a1f74c2590e8..06ff65c85c9f 100644
--- a/drivers/staging/comedi/drivers/adl_pci9118.c
+++ b/drivers/staging/comedi/drivers/adl_pci9118.c
@@ -81,18 +81,6 @@ Configuration options:
* correct channel number on every 12 bit sample
*/
-#undef PCI9118_EXTDEBUG /*
- * if defined then driver prints
- * a lot of messages
- */
-
-#undef DPRINTK
-#ifdef PCI9118_EXTDEBUG
-#define DPRINTK(fmt, args...) printk(fmt, ## args)
-#else
-#define DPRINTK(fmt, args...)
-#endif
-
#define IORANGE_9118 64 /* I hope */
#define PCI9118_CHANLEN 255 /*
* len of chanlist, some source say 256,
@@ -356,43 +344,170 @@ struct pci9118_private {
unsigned int ai_inttrig_start; /* TRIG_INT for start */
};
-#define devpriv ((struct pci9118_private *)dev->private)
-#define this_board ((struct boardtype *)dev->board_ptr)
-
-/*
-==============================================================================
-*/
-
static int check_channel_list(struct comedi_device *dev,
struct comedi_subdevice *s, int n_chan,
- unsigned int *chanlist, int frontadd,
- int backadd);
+ unsigned int *chanlist, int frontadd, int backadd)
+{
+ const struct boardtype *this_board = comedi_board(dev);
+ struct pci9118_private *devpriv = dev->private;
+ unsigned int i, differencial = 0, bipolar = 0;
+
+ /* correct channel and range number check itself comedi/range.c */
+ if (n_chan < 1) {
+ comedi_error(dev, "range/channel list is empty!");
+ return 0;
+ }
+ if ((frontadd + n_chan + backadd) > s->len_chanlist) {
+ printk
+ ("comedi%d: range/channel list is too long for "
+ "actual configuration (%d>%d)!",
+ dev->minor, n_chan, s->len_chanlist - frontadd - backadd);
+ return 0;
+ }
+
+ if (CR_AREF(chanlist[0]) == AREF_DIFF)
+ differencial = 1; /* all input must be diff */
+ if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES)
+ bipolar = 1; /* all input must be bipolar */
+ if (n_chan > 1)
+ for (i = 1; i < n_chan; i++) { /* check S.E/diff */
+ if ((CR_AREF(chanlist[i]) == AREF_DIFF) !=
+ (differencial)) {
+ comedi_error(dev,
+ "Differencial and single ended "
+ "inputs can't be mixtured!");
+ return 0;
+ }
+ if ((CR_RANGE(chanlist[i]) < PCI9118_BIPOLAR_RANGES) !=
+ (bipolar)) {
+ comedi_error(dev,
+ "Bipolar and unipolar ranges "
+ "can't be mixtured!");
+ return 0;
+ }
+ if (!devpriv->usemux && differencial &&
+ (CR_CHAN(chanlist[i]) >= this_board->n_aichand)) {
+ comedi_error(dev,
+ "If AREF_DIFF is used then is "
+ "available only first 8 channels!");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
static int setup_channel_list(struct comedi_device *dev,
struct comedi_subdevice *s, int n_chan,
unsigned int *chanlist, int rot, int frontadd,
- int backadd, int usedma, char eoshandle);
-static void start_pacer(struct comedi_device *dev, int mode,
- unsigned int divisor1, unsigned int divisor2);
-static int pci9118_reset(struct comedi_device *dev);
-static int pci9118_exttrg_add(struct comedi_device *dev, unsigned char source);
-static int pci9118_exttrg_del(struct comedi_device *dev, unsigned char source);
-static int pci9118_ai_cancel(struct comedi_device *dev,
- struct comedi_subdevice *s);
-static void pci9118_calc_divisors(char mode, struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int *tim1, unsigned int *tim2,
- unsigned int flags, int chans,
- unsigned int *div1, unsigned int *div2,
- char usessh, unsigned int chnsshfront);
+ int backadd, int usedma, char useeos)
+{
+ struct pci9118_private *devpriv = dev->private;
+ unsigned int i, differencial = 0, bipolar = 0;
+ unsigned int scanquad, gain, ssh = 0x00;
+
+ if (usedma == 1) {
+ rot = 8;
+ usedma = 0;
+ }
+
+ if (CR_AREF(chanlist[0]) == AREF_DIFF)
+ differencial = 1; /* all input must be diff */
+ if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES)
+ bipolar = 1; /* all input must be bipolar */
+
+ /* All is ok, so we can setup channel/range list */
+
+ if (!bipolar) {
+ devpriv->AdControlReg |= AdControl_UniP;
+ /* set unibipolar */
+ } else {
+ devpriv->AdControlReg &= ((~AdControl_UniP) & 0xff);
+ /* enable bipolar */
+ }
+
+ if (differencial) {
+ devpriv->AdControlReg |= AdControl_Diff;
+ /* enable diff inputs */
+ } else {
+ devpriv->AdControlReg &= ((~AdControl_Diff) & 0xff);
+ /* set single ended inputs */
+ }
+
+ outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL);
+ /* setup mode */
+
+ outl(2, dev->iobase + PCI9118_SCANMOD);
+ /* gods know why this sequence! */
+ outl(0, dev->iobase + PCI9118_SCANMOD);
+ outl(1, dev->iobase + PCI9118_SCANMOD);
+
+#ifdef PCI9118_PARANOIDCHECK
+ devpriv->chanlistlen = n_chan;
+ for (i = 0; i < (PCI9118_CHANLEN + 1); i++)
+ devpriv->chanlist[i] = 0x55aa;
+#endif
+
+ if (frontadd) { /* insert channels for S&H */
+ ssh = devpriv->softsshsample;
+ for (i = 0; i < frontadd; i++) {
+ /* store range list to card */
+ scanquad = CR_CHAN(chanlist[0]);
+ /* get channel number; */
+ gain = CR_RANGE(chanlist[0]);
+ /* get gain number */
+ scanquad |= ((gain & 0x03) << 8);
+ outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
+ ssh = devpriv->softsshhold;
+ }
+ }
+
+ for (i = 0; i < n_chan; i++) { /* store range list to card */
+ scanquad = CR_CHAN(chanlist[i]); /* get channel number */
+#ifdef PCI9118_PARANOIDCHECK
+ devpriv->chanlist[i ^ usedma] = (scanquad & 0xf) << rot;
+#endif
+ gain = CR_RANGE(chanlist[i]); /* get gain number */
+ scanquad |= ((gain & 0x03) << 8);
+ outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
+ }
+
+ if (backadd) { /* insert channels for fit onto 32bit DMA */
+ for (i = 0; i < backadd; i++) { /* store range list to card */
+ scanquad = CR_CHAN(chanlist[0]);
+ /* get channel number */
+ gain = CR_RANGE(chanlist[0]); /* get gain number */
+ scanquad |= ((gain & 0x03) << 8);
+ outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
+ }
+ }
+#ifdef PCI9118_PARANOIDCHECK
+ devpriv->chanlist[n_chan ^ usedma] = devpriv->chanlist[0 ^ usedma];
+ /* for 32bit operations */
+ if (useeos) {
+ for (i = 1; i < n_chan; i++) { /* store range list to card */
+ devpriv->chanlist[(n_chan + i) ^ usedma] =
+ (CR_CHAN(chanlist[i]) & 0xf) << rot;
+ }
+ devpriv->chanlist[(2 * n_chan) ^ usedma] =
+ devpriv->chanlist[0 ^ usedma];
+ /* for 32bit operations */
+ useeos = 2;
+ } else {
+ useeos = 1;
+ }
+#endif
+ outl(0, dev->iobase + PCI9118_SCANMOD); /* close scan queue */
+ /* udelay(100); important delay, or first sample will be crippled */
+
+ return 1; /* we can serve this with scan logic */
+}
-/*
-==============================================================================
-*/
static int pci9118_insn_read_ai(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
-
+ struct pci9118_private *devpriv = dev->private;
int n, timeout;
devpriv->AdControlReg = AdControl_Int & 0xff;
@@ -442,13 +557,11 @@ conv_finish:
}
-/*
-==============================================================================
-*/
static int pci9118_insn_write_ao(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct pci9118_private *devpriv = dev->private;
int n, chanreg, ch;
ch = CR_CHAN(insn->chanspec);
@@ -466,13 +579,11 @@ static int pci9118_insn_write_ao(struct comedi_device *dev,
return n;
}
-/*
-==============================================================================
-*/
static int pci9118_insn_read_ao(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct pci9118_private *devpriv = dev->private;
int n, chan;
chan = CR_CHAN(insn->chanspec);
@@ -482,9 +593,6 @@ static int pci9118_insn_read_ao(struct comedi_device *dev,
return n;
}
-/*
-==============================================================================
-*/
static int pci9118_insn_bits_di(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
@@ -494,9 +602,6 @@ static int pci9118_insn_bits_di(struct comedi_device *dev,
return insn->n;
}
-/*
-==============================================================================
-*/
static int pci9118_insn_bits_do(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
@@ -511,11 +616,10 @@ static int pci9118_insn_bits_do(struct comedi_device *dev,
return insn->n;
}
-/*
-==============================================================================
-*/
static void interrupt_pci9118_ai_mode4_switch(struct comedi_device *dev)
{
+ struct pci9118_private *devpriv = dev->private;
+
devpriv->AdFunctionReg =
AdFunction_PDTrg | AdFunction_PETrg | AdFunction_AM;
outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
@@ -533,6 +637,7 @@ static unsigned int defragment_dma_buffer(struct comedi_device *dev,
short *dma_buffer,
unsigned int num_samples)
{
+ struct pci9118_private *devpriv = dev->private;
unsigned int i = 0, j = 0;
unsigned int start_pos = devpriv->ai_add_front,
stop_pos = devpriv->ai_add_front + devpriv->ai_n_chan;
@@ -551,14 +656,12 @@ static unsigned int defragment_dma_buffer(struct comedi_device *dev,
return j;
}
-/*
-==============================================================================
-*/
static int move_block_from_dma(struct comedi_device *dev,
struct comedi_subdevice *s,
short *dma_buffer,
unsigned int num_samples)
{
+ struct pci9118_private *devpriv = dev->private;
unsigned int num_bytes;
num_samples = defragment_dma_buffer(dev, s, dma_buffer, num_samples);
@@ -574,13 +677,153 @@ static int move_block_from_dma(struct comedi_device *dev,
return 0;
}
-/*
-==============================================================================
-*/
+static int pci9118_exttrg_add(struct comedi_device *dev, unsigned char source)
+{
+ struct pci9118_private *devpriv = dev->private;
+
+ if (source > 3)
+ return -1; /* incorrect source */
+ devpriv->exttrg_users |= (1 << source);
+ devpriv->IntControlReg |= Int_DTrg;
+ outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00,
+ devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+ /* allow INT in AMCC */
+ return 0;
+}
+
+static int pci9118_exttrg_del(struct comedi_device *dev, unsigned char source)
+{
+ struct pci9118_private *devpriv = dev->private;
+
+ if (source > 3)
+ return -1; /* incorrect source */
+ devpriv->exttrg_users &= ~(1 << source);
+ if (!devpriv->exttrg_users) { /* shutdown ext trg intterrupts */
+ devpriv->IntControlReg &= ~Int_DTrg;
+ if (!devpriv->IntControlReg) /* all IRQ disabled */
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) &
+ (~0x00001f00),
+ devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+ /* disable int in AMCC */
+ outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
+ }
+ return 0;
+}
+
+static void pci9118_calc_divisors(char mode, struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ unsigned int *tim1, unsigned int *tim2,
+ unsigned int flags, int chans,
+ unsigned int *div1, unsigned int *div2,
+ char usessh, unsigned int chnsshfront)
+{
+ const struct boardtype *this_board = comedi_board(dev);
+ struct pci9118_private *devpriv = dev->private;
+
+ switch (mode) {
+ case 1:
+ case 4:
+ if (*tim2 < this_board->ai_ns_min)
+ *tim2 = this_board->ai_ns_min;
+ i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, div1, div2,
+ tim2, flags & TRIG_ROUND_NEAREST);
+ break;
+ case 2:
+ if (*tim2 < this_board->ai_ns_min)
+ *tim2 = this_board->ai_ns_min;
+ *div1 = *tim2 / devpriv->i8254_osc_base;
+ /* convert timer (burst) */
+ if (*div1 < this_board->ai_pacer_min)
+ *div1 = this_board->ai_pacer_min;
+ *div2 = *tim1 / devpriv->i8254_osc_base; /* scan timer */
+ *div2 = *div2 / *div1; /* major timer is c1*c2 */
+ if (*div2 < chans)
+ *div2 = chans;
+
+ *tim2 = *div1 * devpriv->i8254_osc_base;
+ /* real convert timer */
+
+ if (usessh & (chnsshfront == 0)) /* use BSSH signal */
+ if (*div2 < (chans + 2))
+ *div2 = chans + 2;
+
+ *tim1 = *div1 * *div2 * devpriv->i8254_osc_base;
+ break;
+ }
+}
+
+static void start_pacer(struct comedi_device *dev, int mode,
+ unsigned int divisor1, unsigned int divisor2)
+{
+ outl(0x74, dev->iobase + PCI9118_CNTCTRL);
+ outl(0xb4, dev->iobase + PCI9118_CNTCTRL);
+/* outl(0x30, dev->iobase + PCI9118_CNTCTRL); */
+ udelay(1);
+
+ if ((mode == 1) || (mode == 2) || (mode == 4)) {
+ outl(divisor2 & 0xff, dev->iobase + PCI9118_CNT2);
+ outl((divisor2 >> 8) & 0xff, dev->iobase + PCI9118_CNT2);
+ outl(divisor1 & 0xff, dev->iobase + PCI9118_CNT1);
+ outl((divisor1 >> 8) & 0xff, dev->iobase + PCI9118_CNT1);
+ }
+}
+
+static int pci9118_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ struct pci9118_private *devpriv = dev->private;
+
+ if (devpriv->usedma)
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_MCSR) &
+ (~EN_A2P_TRANSFERS),
+ devpriv->iobase_a + AMCC_OP_REG_MCSR); /* stop DMA */
+ pci9118_exttrg_del(dev, EXTTRG_AI);
+ start_pacer(dev, 0, 0, 0); /* stop 8254 counters */
+ devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg;
+ outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
+ /*
+ * positive triggers, no S&H, no burst,
+ * burst stop, no post trigger,
+ * no about trigger, trigger stop
+ */
+ devpriv->AdControlReg = 0x00;
+ outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL);
+ /*
+ * bipolar, S.E., use 8254, stop 8354,
+ * internal trigger, soft trigger,
+ * disable INT and DMA
+ */
+ outl(0, dev->iobase + PCI9118_BURST);
+ outl(1, dev->iobase + PCI9118_SCANMOD);
+ outl(2, dev->iobase + PCI9118_SCANMOD); /* reset scan queue */
+ outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */
+
+ devpriv->ai_do = 0;
+ devpriv->usedma = 0;
+
+ devpriv->ai_act_scan = 0;
+ devpriv->ai_act_dmapos = 0;
+ s->async->cur_chan = 0;
+ s->async->inttrig = NULL;
+ devpriv->ai_buf_ptr = 0;
+ devpriv->ai_neverending = 0;
+ devpriv->dma_actbuf = 0;
+
+ if (!devpriv->IntControlReg)
+ outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00,
+ devpriv->iobase_a + AMCC_OP_REG_INTCSR);
+ /* allow INT in AMCC */
+
+ return 0;
+}
+
static char pci9118_decode_error_status(struct comedi_device *dev,
struct comedi_subdevice *s,
unsigned char m)
{
+ struct pci9118_private *devpriv = dev->private;
+
if (m & 0x100) {
comedi_error(dev, "A/D FIFO Full status (Fatal Error!)");
devpriv->ai_maskerr &= ~0x100L;
@@ -613,6 +856,7 @@ static void pci9118_ai_munge(struct comedi_device *dev,
unsigned int num_bytes,
unsigned int start_chan_index)
{
+ struct pci9118_private *devpriv = dev->private;
unsigned int i, num_samples = num_bytes / sizeof(short);
short *array = data;
@@ -627,15 +871,13 @@ static void pci9118_ai_munge(struct comedi_device *dev,
}
}
-/*
-==============================================================================
-*/
static void interrupt_pci9118_ai_onesample(struct comedi_device *dev,
struct comedi_subdevice *s,
unsigned short int_adstat,
unsigned int int_amcc,
unsigned short int_daq)
{
+ struct pci9118_private *devpriv = dev->private;
register short sampl;
s->async->events = 0;
@@ -680,15 +922,13 @@ static void interrupt_pci9118_ai_onesample(struct comedi_device *dev,
comedi_event(dev, s);
}
-/*
-==============================================================================
-*/
static void interrupt_pci9118_ai_dma(struct comedi_device *dev,
struct comedi_subdevice *s,
unsigned short int_adstat,
unsigned int int_amcc,
unsigned short int_daq)
{
+ struct pci9118_private *devpriv = dev->private;
unsigned int next_dma_buf, samplesinbuf, sampls, m;
if (int_amcc & MASTER_ABORT_INT) {
@@ -713,7 +953,6 @@ static void interrupt_pci9118_ai_dma(struct comedi_device *dev,
samplesinbuf = devpriv->dmabuf_use_size[devpriv->dma_actbuf] >> 1;
/* number of received real samples */
-/* DPRINTK("dma_actbuf=%d\n",devpriv->dma_actbuf); */
if (devpriv->dma_doublebuf) { /*
* switch DMA buffers if is used
@@ -735,17 +974,12 @@ static void interrupt_pci9118_ai_dma(struct comedi_device *dev,
* how many samples is to
* end of buffer
*/
-/*
- * DPRINTK("samps=%d m=%d %d %d\n",
- * samplesinbuf,m,s->async->buf_int_count,s->async->buf_int_ptr);
- */
sampls = m;
move_block_from_dma(dev, s,
devpriv->dmabuf_virt[devpriv->dma_actbuf],
samplesinbuf);
m = m - sampls; /* m= how many samples was transferred */
}
-/* DPRINTK("YYY\n"); */
if (!devpriv->ai_neverending)
if (devpriv->ai_act_scan >= devpriv->ai_scans) {
@@ -768,12 +1002,10 @@ static void interrupt_pci9118_ai_dma(struct comedi_device *dev,
comedi_event(dev, s);
}
-/*
-==============================================================================
-*/
static irqreturn_t interrupt_pci9118(int irq, void *d)
{
struct comedi_device *dev = d;
+ struct pci9118_private *devpriv = dev->private;
unsigned int int_daq = 0, int_amcc, int_adstat;
if (!dev->attached)
@@ -784,14 +1016,6 @@ static irqreturn_t interrupt_pci9118(int irq, void *d)
int_amcc = inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR);
/* get INT register from AMCC chip */
-/*
- * DPRINTK("INT daq=0x%01x amcc=0x%08x MWAR=0x%08x
- * MWTC=0x%08x ADSTAT=0x%02x ai_do=%d\n",
- * int_daq, int_amcc, inl(devpriv->iobase_a+AMCC_OP_REG_MWAR),
- * inl(devpriv->iobase_a+AMCC_OP_REG_MWTC),
- * inw(dev->iobase+PCI9118_ADSTAT)&0x1ff,devpriv->ai_do);
- */
-
if ((!int_daq) && (!(int_amcc & ANY_S593X_INT)))
return IRQ_NONE; /* interrupt from other source */
@@ -837,19 +1061,18 @@ static irqreturn_t interrupt_pci9118(int irq, void *d)
}
}
- (devpriv->int_ai_func) (dev, dev->subdevices + 0, int_adstat,
+ (devpriv->int_ai_func) (dev, &dev->subdevices[0], int_adstat,
int_amcc, int_daq);
}
return IRQ_HANDLED;
}
-/*
-==============================================================================
-*/
static int pci9118_ai_inttrig(struct comedi_device *dev,
struct comedi_subdevice *s, unsigned int trignum)
{
+ struct pci9118_private *devpriv = dev->private;
+
if (trignum != devpriv->ai_inttrig_start)
return -EINVAL;
@@ -868,119 +1091,64 @@ static int pci9118_ai_inttrig(struct comedi_device *dev,
return 1;
}
-/*
-==============================================================================
-*/
static int pci9118_ai_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
+ const struct boardtype *this_board = comedi_board(dev);
+ struct pci9118_private *devpriv = dev->private;
int err = 0;
+ unsigned int flags;
int tmp;
unsigned int divisor1 = 0, divisor2 = 0;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT | TRIG_INT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src,
+ TRIG_NOW | TRIG_EXT | TRIG_INT);
- tmp = cmd->scan_begin_src;
+ flags = TRIG_FOLLOW;
if (devpriv->master)
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW;
- else
- cmd->scan_begin_src &= TRIG_FOLLOW;
+ flags |= TRIG_TIMER | TRIG_EXT;
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, flags);
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
+ flags = TRIG_TIMER | TRIG_EXT;
if (devpriv->master)
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT | TRIG_NOW;
- else
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
-
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
+ flags |= TRIG_NOW;
+ err |= cfc_check_trigger_src(&cmd->convert_src, flags);
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE | TRIG_EXT;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src,
+ TRIG_COUNT | TRIG_NONE | TRIG_EXT);
if (err)
return 1;
- /*
- * step 2:
- * make sure trigger sources are
- * unique and mutually compatible
- */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->start_src != TRIG_NOW &&
- cmd->start_src != TRIG_INT && cmd->start_src != TRIG_EXT) {
- cmd->start_src = TRIG_NOW;
- err++;
- }
-
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT &&
- cmd->scan_begin_src != TRIG_INT &&
- cmd->scan_begin_src != TRIG_FOLLOW) {
- cmd->scan_begin_src = TRIG_FOLLOW;
- err++;
- }
-
- if (cmd->convert_src != TRIG_TIMER &&
- cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW) {
- cmd->convert_src = TRIG_TIMER;
- err++;
- }
-
- if (cmd->scan_end_src != TRIG_COUNT) {
- cmd->scan_end_src = TRIG_COUNT;
- err++;
- }
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
- if (cmd->stop_src != TRIG_NONE &&
- cmd->stop_src != TRIG_COUNT &&
- cmd->stop_src != TRIG_INT && cmd->stop_src != TRIG_EXT) {
- cmd->stop_src = TRIG_COUNT;
- err++;
- }
+ /* Step 2b : and mutually compatible */
- if (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) {
- cmd->start_src = TRIG_NOW;
- err++;
- }
+ if (cmd->start_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT)
+ err |= -EINVAL;
- if (cmd->start_src == TRIG_INT && cmd->scan_begin_src == TRIG_INT) {
- cmd->start_src = TRIG_NOW;
- err++;
- }
+ if (cmd->start_src == TRIG_INT && cmd->scan_begin_src == TRIG_INT)
+ err |= -EINVAL;
if ((cmd->scan_begin_src & (TRIG_TIMER | TRIG_EXT)) &&
- (!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW)))) {
- cmd->convert_src = TRIG_TIMER;
- err++;
- }
+ (!(cmd->convert_src & (TRIG_TIMER | TRIG_NOW))))
+ err |= -EINVAL;
if ((cmd->scan_begin_src == TRIG_FOLLOW) &&
- (!(cmd->convert_src & (TRIG_TIMER | TRIG_EXT)))) {
- cmd->convert_src = TRIG_TIMER;
- err++;
- }
+ (!(cmd->convert_src & (TRIG_TIMER | TRIG_EXT))))
+ err |= -EINVAL;
- if (cmd->stop_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT) {
- cmd->stop_src = TRIG_COUNT;
- err++;
- }
+ if (cmd->stop_src == TRIG_EXT && cmd->scan_begin_src == TRIG_EXT)
+ err |= -EINVAL;
if (err)
return 2;
@@ -1074,11 +1242,9 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
if (cmd->scan_begin_src == TRIG_TIMER) {
tmp = cmd->scan_begin_arg;
-/* printk("S1 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */
i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
&divisor2, &cmd->scan_begin_arg,
cmd->flags & TRIG_ROUND_MASK);
-/* printk("S2 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */
if (cmd->scan_begin_arg < this_board->ai_ns_min)
cmd->scan_begin_arg = this_board->ai_ns_min;
if (tmp != cmd->scan_begin_arg)
@@ -1090,7 +1256,6 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
&divisor2, &cmd->convert_arg,
cmd->flags & TRIG_ROUND_MASK);
-/* printk("s1 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */
if (cmd->convert_arg < this_board->ai_ns_min)
cmd->convert_arg = this_board->ai_ns_min;
if (tmp != cmd->convert_arg)
@@ -1104,7 +1269,6 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
cmd->scan_begin_arg =
this_board->ai_ns_min *
(cmd->scan_end_arg + 2);
-/* printk("s2 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */
err++;
}
} else {
@@ -1113,7 +1277,6 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
cmd->scan_begin_arg =
cmd->convert_arg *
cmd->chanlist_len;
-/* printk("s3 timer1=%u timer2=%u\n",cmd->scan_begin_arg,cmd->convert_arg); */
err++;
}
}
@@ -1131,18 +1294,13 @@ static int pci9118_ai_cmdtest(struct comedi_device *dev,
return 0;
}
-/*
-==============================================================================
-*/
static int Compute_and_setup_dma(struct comedi_device *dev)
{
+ struct pci9118_private *devpriv = dev->private;
unsigned int dmalen0, dmalen1, i;
- DPRINTK("adl_pci9118 EDBG: BGN: Compute_and_setup_dma()\n");
dmalen0 = devpriv->dmabuf_size[0];
dmalen1 = devpriv->dmabuf_size[1];
- DPRINTK("1 dmalen0=%d dmalen1=%d ai_data_len=%d\n", dmalen0, dmalen1,
- devpriv->ai_data_len);
/* isn't output buff smaller that our DMA buff? */
if (dmalen0 > (devpriv->ai_data_len)) {
dmalen0 = devpriv->ai_data_len & ~3L; /*
@@ -1154,7 +1312,6 @@ static int Compute_and_setup_dma(struct comedi_device *dev)
* align to 32bit down
*/
}
- DPRINTK("2 dmalen0=%d dmalen1=%d\n", dmalen0, dmalen1);
/* we want wake up every scan? */
if (devpriv->ai_flags & TRIG_WAKE_EOS) {
@@ -1169,11 +1326,6 @@ static int Compute_and_setup_dma(struct comedi_device *dev)
} else {
/* short first DMA buffer to one scan */
dmalen0 = devpriv->ai_n_realscanlen << 1;
- DPRINTK
- ("21 dmalen0=%d ai_n_realscanlen=%d "
- "useeoshandle=%d\n",
- dmalen0, devpriv->ai_n_realscanlen,
- devpriv->useeoshandle);
if (devpriv->useeoshandle)
dmalen0 += 2;
if (dmalen0 < 4) {
@@ -1197,11 +1349,6 @@ static int Compute_and_setup_dma(struct comedi_device *dev)
} else {
/* short second DMA buffer to one scan */
dmalen1 = devpriv->ai_n_realscanlen << 1;
- DPRINTK
- ("22 dmalen1=%d ai_n_realscanlen=%d "
- "useeoshandle=%d\n",
- dmalen1, devpriv->ai_n_realscanlen,
- devpriv->useeoshandle);
if (devpriv->useeoshandle)
dmalen1 -= 2;
if (dmalen1 < 4) {
@@ -1214,7 +1361,6 @@ static int Compute_and_setup_dma(struct comedi_device *dev)
}
}
- DPRINTK("3 dmalen0=%d dmalen1=%d\n", dmalen0, dmalen1);
/* transfer without TRIG_WAKE_EOS */
if (!(devpriv->ai_flags & TRIG_WAKE_EOS)) {
/* if it's possible then align DMA buffers to length of scan */
@@ -1241,15 +1387,9 @@ static int Compute_and_setup_dma(struct comedi_device *dev)
if (dmalen0 >
((devpriv->ai_n_realscanlen << 1) *
devpriv->ai_scans)) {
- DPRINTK
- ("3.0 ai_n_realscanlen=%d ai_scans=%d\n",
- devpriv->ai_n_realscanlen,
- devpriv->ai_scans);
dmalen0 =
(devpriv->ai_n_realscanlen << 1) *
devpriv->ai_scans;
- DPRINTK("3.1 dmalen0=%d dmalen1=%d\n", dmalen0,
- dmalen1);
dmalen0 &= ~3L;
} else { /*
* fits whole measure into
@@ -1261,21 +1401,16 @@ static int Compute_and_setup_dma(struct comedi_device *dev)
dmalen1 =
(devpriv->ai_n_realscanlen << 1) *
devpriv->ai_scans - dmalen0;
- DPRINTK("3.2 dmalen0=%d dmalen1=%d\n", dmalen0,
- dmalen1);
dmalen1 &= ~3L;
}
}
}
- DPRINTK("4 dmalen0=%d dmalen1=%d\n", dmalen0, dmalen1);
-
/* these DMA buffer size will be used */
devpriv->dma_actbuf = 0;
devpriv->dmabuf_use_size[0] = dmalen0;
devpriv->dmabuf_use_size[1] = dmalen1;
- DPRINTK("5 dmalen0=%d dmalen1=%d\n", dmalen0, dmalen1);
#if 0
if (devpriv->ai_n_scanlen < this_board->half_fifo_size) {
devpriv->dmabuf_panic_size[0] =
@@ -1308,18 +1443,14 @@ static int Compute_and_setup_dma(struct comedi_device *dev)
devpriv->iobase_a + AMCC_OP_REG_INTCSR);
/* allow bus mastering */
- DPRINTK("adl_pci9118 EDBG: END: Compute_and_setup_dma()\n");
return 0;
}
-/*
-==============================================================================
-*/
static int pci9118_ai_docmd_sampl(struct comedi_device *dev,
struct comedi_subdevice *s)
{
- DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_sampl(%d,) [%d]\n",
- dev->minor, devpriv->ai_do);
+ struct pci9118_private *devpriv = dev->private;
+
switch (devpriv->ai_do) {
case 1:
devpriv->AdControlReg |= AdControl_TmrTr;
@@ -1366,18 +1497,14 @@ static int pci9118_ai_docmd_sampl(struct comedi_device *dev,
outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
}
- DPRINTK("adl_pci9118 EDBG: END: pci9118_ai_docmd_sampl()\n");
return 0;
}
-/*
-==============================================================================
-*/
static int pci9118_ai_docmd_dma(struct comedi_device *dev,
struct comedi_subdevice *s)
{
- DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_dma(%d,) [%d,%d]\n",
- dev->minor, devpriv->ai_do, devpriv->usedma);
+ struct pci9118_private *devpriv = dev->private;
+
Compute_and_setup_dma(dev);
switch (devpriv->ai_do) {
@@ -1440,20 +1567,17 @@ static int pci9118_ai_docmd_dma(struct comedi_device *dev,
outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL);
}
- DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_docmd_dma()\n");
return 0;
}
-/*
-==============================================================================
-*/
static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
+ const struct boardtype *this_board = comedi_board(dev);
+ struct pci9118_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd;
unsigned int addchans = 0;
int ret = 0;
- DPRINTK("adl_pci9118 EDBG: BGN: pci9118_ai_cmd(%d,)\n", dev->minor);
devpriv->ai12_startstop = 0;
devpriv->ai_flags = cmd->flags;
devpriv->ai_n_chan = cmd->chanlist_len;
@@ -1502,10 +1626,6 @@ static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
devpriv->usessh = 0;
/* no */
- DPRINTK("1 neverending=%d scans=%u usessh=%d ai_startstop=0x%2x\n",
- devpriv->ai_neverending, devpriv->ai_scans, devpriv->usessh,
- devpriv->ai12_startstop);
-
/*
* use additional sample at end of every scan
* to satisty DMA 32 bit transfer?
@@ -1586,12 +1706,6 @@ static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
devpriv->ai_add_back) * (devpriv->ai_n_scanlen /
devpriv->ai_n_chan);
- DPRINTK("2 usedma=%d realscan=%d af=%u n_chan=%d ab=%d n_scanlen=%d\n",
- devpriv->usedma,
- devpriv->ai_n_realscanlen, devpriv->ai_add_front,
- devpriv->ai_n_chan, devpriv->ai_add_back,
- devpriv->ai_n_scanlen);
-
/* check and setup channel list */
if (!check_channel_list(dev, s, devpriv->ai_n_chan,
devpriv->ai_chanlist, devpriv->ai_add_front,
@@ -1688,371 +1802,13 @@ static int pci9118_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
else
ret = pci9118_ai_docmd_sampl(dev, s);
- DPRINTK("adl_pci9118 EDBG: END: pci9118_ai_cmd()\n");
return ret;
}
-/*
-==============================================================================
-*/
-static int check_channel_list(struct comedi_device *dev,
- struct comedi_subdevice *s, int n_chan,
- unsigned int *chanlist, int frontadd, int backadd)
-{
- unsigned int i, differencial = 0, bipolar = 0;
-
- /* correct channel and range number check itself comedi/range.c */
- if (n_chan < 1) {
- comedi_error(dev, "range/channel list is empty!");
- return 0;
- }
- if ((frontadd + n_chan + backadd) > s->len_chanlist) {
- printk
- ("comedi%d: range/channel list is too long for "
- "actual configuration (%d>%d)!",
- dev->minor, n_chan, s->len_chanlist - frontadd - backadd);
- return 0;
- }
-
- if (CR_AREF(chanlist[0]) == AREF_DIFF)
- differencial = 1; /* all input must be diff */
- if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES)
- bipolar = 1; /* all input must be bipolar */
- if (n_chan > 1)
- for (i = 1; i < n_chan; i++) { /* check S.E/diff */
- if ((CR_AREF(chanlist[i]) == AREF_DIFF) !=
- (differencial)) {
- comedi_error(dev,
- "Differencial and single ended "
- "inputs can't be mixtured!");
- return 0;
- }
- if ((CR_RANGE(chanlist[i]) < PCI9118_BIPOLAR_RANGES) !=
- (bipolar)) {
- comedi_error(dev,
- "Bipolar and unipolar ranges "
- "can't be mixtured!");
- return 0;
- }
- if (!devpriv->usemux && differencial &&
- (CR_CHAN(chanlist[i]) >= this_board->n_aichand)) {
- comedi_error(dev,
- "If AREF_DIFF is used then is "
- "available only first 8 channels!");
- return 0;
- }
- }
-
- return 1;
-}
-
-/*
-==============================================================================
-*/
-static int setup_channel_list(struct comedi_device *dev,
- struct comedi_subdevice *s, int n_chan,
- unsigned int *chanlist, int rot, int frontadd,
- int backadd, int usedma, char useeos)
-{
- unsigned int i, differencial = 0, bipolar = 0;
- unsigned int scanquad, gain, ssh = 0x00;
-
- DPRINTK
- ("adl_pci9118 EDBG: BGN: setup_channel_list"
- "(%d,.,%d,.,%d,%d,%d,%d)\n",
- dev->minor, n_chan, rot, frontadd, backadd, usedma);
-
- if (usedma == 1) {
- rot = 8;
- usedma = 0;
- }
-
- if (CR_AREF(chanlist[0]) == AREF_DIFF)
- differencial = 1; /* all input must be diff */
- if (CR_RANGE(chanlist[0]) < PCI9118_BIPOLAR_RANGES)
- bipolar = 1; /* all input must be bipolar */
-
- /* All is ok, so we can setup channel/range list */
-
- if (!bipolar) {
- devpriv->AdControlReg |= AdControl_UniP;
- /* set unibipolar */
- } else {
- devpriv->AdControlReg &= ((~AdControl_UniP) & 0xff);
- /* enable bipolar */
- }
-
- if (differencial) {
- devpriv->AdControlReg |= AdControl_Diff;
- /* enable diff inputs */
- } else {
- devpriv->AdControlReg &= ((~AdControl_Diff) & 0xff);
- /* set single ended inputs */
- }
-
- outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL);
- /* setup mode */
-
- outl(2, dev->iobase + PCI9118_SCANMOD);
- /* gods know why this sequence! */
- outl(0, dev->iobase + PCI9118_SCANMOD);
- outl(1, dev->iobase + PCI9118_SCANMOD);
-
-#ifdef PCI9118_PARANOIDCHECK
- devpriv->chanlistlen = n_chan;
- for (i = 0; i < (PCI9118_CHANLEN + 1); i++)
- devpriv->chanlist[i] = 0x55aa;
-#endif
-
- if (frontadd) { /* insert channels for S&H */
- ssh = devpriv->softsshsample;
- DPRINTK("FA: %04x: ", ssh);
- for (i = 0; i < frontadd; i++) {
- /* store range list to card */
- scanquad = CR_CHAN(chanlist[0]);
- /* get channel number; */
- gain = CR_RANGE(chanlist[0]);
- /* get gain number */
- scanquad |= ((gain & 0x03) << 8);
- outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
- DPRINTK("%02x ", scanquad | ssh);
- ssh = devpriv->softsshhold;
- }
- DPRINTK("\n ");
- }
-
- DPRINTK("SL: ", ssh);
- for (i = 0; i < n_chan; i++) { /* store range list to card */
- scanquad = CR_CHAN(chanlist[i]); /* get channel number */
-#ifdef PCI9118_PARANOIDCHECK
- devpriv->chanlist[i ^ usedma] = (scanquad & 0xf) << rot;
-#endif
- gain = CR_RANGE(chanlist[i]); /* get gain number */
- scanquad |= ((gain & 0x03) << 8);
- outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
- DPRINTK("%02x ", scanquad | ssh);
- }
- DPRINTK("\n ");
-
- if (backadd) { /* insert channels for fit onto 32bit DMA */
- DPRINTK("BA: %04x: ", ssh);
- for (i = 0; i < backadd; i++) { /* store range list to card */
- scanquad = CR_CHAN(chanlist[0]);
- /* get channel number */
- gain = CR_RANGE(chanlist[0]); /* get gain number */
- scanquad |= ((gain & 0x03) << 8);
- outl(scanquad | ssh, dev->iobase + PCI9118_GAIN);
- DPRINTK("%02x ", scanquad | ssh);
- }
- DPRINTK("\n ");
- }
-#ifdef PCI9118_PARANOIDCHECK
- devpriv->chanlist[n_chan ^ usedma] = devpriv->chanlist[0 ^ usedma];
- /* for 32bit operations */
- if (useeos) {
- for (i = 1; i < n_chan; i++) { /* store range list to card */
- devpriv->chanlist[(n_chan + i) ^ usedma] =
- (CR_CHAN(chanlist[i]) & 0xf) << rot;
- }
- devpriv->chanlist[(2 * n_chan) ^ usedma] =
- devpriv->chanlist[0 ^ usedma];
- /* for 32bit operations */
- useeos = 2;
- } else {
- useeos = 1;
- }
-#ifdef PCI9118_EXTDEBUG
- DPRINTK("CHL: ");
- for (i = 0; i <= (useeos * n_chan); i++)
- DPRINTK("%04x ", devpriv->chanlist[i]);
-
- DPRINTK("\n ");
-#endif
-#endif
- outl(0, dev->iobase + PCI9118_SCANMOD); /* close scan queue */
- /* udelay(100); important delay, or first sample will be crippled */
-
- DPRINTK("adl_pci9118 EDBG: END: setup_channel_list()\n");
- return 1; /* we can serve this with scan logic */
-}
-
-/*
-==============================================================================
- calculate 8254 divisors if they are used for dual timing
-*/
-static void pci9118_calc_divisors(char mode, struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int *tim1, unsigned int *tim2,
- unsigned int flags, int chans,
- unsigned int *div1, unsigned int *div2,
- char usessh, unsigned int chnsshfront)
-{
- DPRINTK
- ("adl_pci9118 EDBG: BGN: pci9118_calc_divisors"
- "(%d,%d,.,%u,%u,%u,%d,.,.,,%u,%u)\n",
- mode, dev->minor, *tim1, *tim2, flags, chans, usessh, chnsshfront);
- switch (mode) {
- case 1:
- case 4:
- if (*tim2 < this_board->ai_ns_min)
- *tim2 = this_board->ai_ns_min;
- i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, div1, div2,
- tim2, flags & TRIG_ROUND_NEAREST);
- DPRINTK("OSC base=%u div1=%u div2=%u timer1=%u\n",
- devpriv->i8254_osc_base, *div1, *div2, *tim1);
- break;
- case 2:
- if (*tim2 < this_board->ai_ns_min)
- *tim2 = this_board->ai_ns_min;
- DPRINTK("1 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
- *tim1, *tim2);
- *div1 = *tim2 / devpriv->i8254_osc_base;
- /* convert timer (burst) */
- DPRINTK("2 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
- *tim1, *tim2);
- if (*div1 < this_board->ai_pacer_min)
- *div1 = this_board->ai_pacer_min;
- DPRINTK("3 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
- *tim1, *tim2);
- *div2 = *tim1 / devpriv->i8254_osc_base; /* scan timer */
- DPRINTK("4 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
- *tim1, *tim2);
- *div2 = *div2 / *div1; /* major timer is c1*c2 */
- DPRINTK("5 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
- *tim1, *tim2);
- if (*div2 < chans)
- *div2 = chans;
- DPRINTK("6 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
- *tim1, *tim2);
-
- *tim2 = *div1 * devpriv->i8254_osc_base;
- /* real convert timer */
-
- if (usessh & (chnsshfront == 0)) /* use BSSH signal */
- if (*div2 < (chans + 2))
- *div2 = chans + 2;
-
- DPRINTK("7 div1=%u div2=%u timer1=%u timer2=%u\n", *div1, *div2,
- *tim1, *tim2);
- *tim1 = *div1 * *div2 * devpriv->i8254_osc_base;
- DPRINTK("OSC base=%u div1=%u div2=%u timer1=%u timer2=%u\n",
- devpriv->i8254_osc_base, *div1, *div2, *tim1, *tim2);
- break;
- }
- DPRINTK("adl_pci9118 EDBG: END: pci9118_calc_divisors(%u,%u)\n",
- *div1, *div2);
-}
-
-/*
-==============================================================================
-*/
-static void start_pacer(struct comedi_device *dev, int mode,
- unsigned int divisor1, unsigned int divisor2)
-{
- outl(0x74, dev->iobase + PCI9118_CNTCTRL);
- outl(0xb4, dev->iobase + PCI9118_CNTCTRL);
-/* outl(0x30, dev->iobase + PCI9118_CNTCTRL); */
- udelay(1);
-
- if ((mode == 1) || (mode == 2) || (mode == 4)) {
- outl(divisor2 & 0xff, dev->iobase + PCI9118_CNT2);
- outl((divisor2 >> 8) & 0xff, dev->iobase + PCI9118_CNT2);
- outl(divisor1 & 0xff, dev->iobase + PCI9118_CNT1);
- outl((divisor1 >> 8) & 0xff, dev->iobase + PCI9118_CNT1);
- }
-}
-
-/*
-==============================================================================
-*/
-static int pci9118_exttrg_add(struct comedi_device *dev, unsigned char source)
-{
- if (source > 3)
- return -1; /* incorrect source */
- devpriv->exttrg_users |= (1 << source);
- devpriv->IntControlReg |= Int_DTrg;
- outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
- outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00,
- devpriv->iobase_a + AMCC_OP_REG_INTCSR);
- /* allow INT in AMCC */
- return 0;
-}
-
-/*
-==============================================================================
-*/
-static int pci9118_exttrg_del(struct comedi_device *dev, unsigned char source)
-{
- if (source > 3)
- return -1; /* incorrect source */
- devpriv->exttrg_users &= ~(1 << source);
- if (!devpriv->exttrg_users) { /* shutdown ext trg intterrupts */
- devpriv->IntControlReg &= ~Int_DTrg;
- if (!devpriv->IntControlReg) /* all IRQ disabled */
- outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) &
- (~0x00001f00),
- devpriv->iobase_a + AMCC_OP_REG_INTCSR);
- /* disable int in AMCC */
- outl(devpriv->IntControlReg, dev->iobase + PCI9118_INTCTRL);
- }
- return 0;
-}
-
-/*
-==============================================================================
-*/
-static int pci9118_ai_cancel(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- if (devpriv->usedma)
- outl(inl(devpriv->iobase_a + AMCC_OP_REG_MCSR) &
- (~EN_A2P_TRANSFERS),
- devpriv->iobase_a + AMCC_OP_REG_MCSR); /* stop DMA */
- pci9118_exttrg_del(dev, EXTTRG_AI);
- start_pacer(dev, 0, 0, 0); /* stop 8254 counters */
- devpriv->AdFunctionReg = AdFunction_PDTrg | AdFunction_PETrg;
- outl(devpriv->AdFunctionReg, dev->iobase + PCI9118_ADFUNC);
- /*
- * positive triggers, no S&H, no burst,
- * burst stop, no post trigger,
- * no about trigger, trigger stop
- */
- devpriv->AdControlReg = 0x00;
- outl(devpriv->AdControlReg, dev->iobase + PCI9118_ADCNTRL);
- /*
- * bipolar, S.E., use 8254, stop 8354,
- * internal trigger, soft trigger,
- * disable INT and DMA
- */
- outl(0, dev->iobase + PCI9118_BURST);
- outl(1, dev->iobase + PCI9118_SCANMOD);
- outl(2, dev->iobase + PCI9118_SCANMOD); /* reset scan queue */
- outl(0, dev->iobase + PCI9118_DELFIFO); /* flush FIFO */
-
- devpriv->ai_do = 0;
- devpriv->usedma = 0;
-
- devpriv->ai_act_scan = 0;
- devpriv->ai_act_dmapos = 0;
- s->async->cur_chan = 0;
- s->async->inttrig = NULL;
- devpriv->ai_buf_ptr = 0;
- devpriv->ai_neverending = 0;
- devpriv->dma_actbuf = 0;
-
- if (!devpriv->IntControlReg)
- outl(inl(devpriv->iobase_a + AMCC_OP_REG_INTCSR) | 0x1f00,
- devpriv->iobase_a + AMCC_OP_REG_INTCSR);
- /* allow INT in AMCC */
-
- return 0;
-}
-
-/*
-==============================================================================
-*/
static int pci9118_reset(struct comedi_device *dev)
{
+ struct pci9118_private *devpriv = dev->private;
+
devpriv->IntControlReg = 0;
devpriv->exttrg_users = 0;
inl(dev->iobase + PCI9118_INTCTRL);
@@ -2112,6 +1868,7 @@ static int pci9118_reset(struct comedi_device *dev)
static struct pci_dev *pci9118_find_pci(struct comedi_device *dev,
struct comedi_devconfig *it)
{
+ const struct boardtype *this_board = comedi_board(dev);
struct pci_dev *pcidev = NULL;
int bus = it->options[0];
int slot = it->options[1];
@@ -2150,6 +1907,8 @@ static struct pci_dev *pci9118_find_pci(struct comedi_device *dev,
static int pci9118_attach(struct comedi_device *dev,
struct comedi_devconfig *it)
{
+ const struct boardtype *this_board = comedi_board(dev);
+ struct pci9118_private *devpriv;
struct pci_dev *pcidev;
struct comedi_subdevice *s;
int ret, pages, i;
@@ -2164,11 +1923,12 @@ static int pci9118_attach(struct comedi_device *dev,
else
master = 1;
- ret = alloc_private(dev, sizeof(struct pci9118_private));
+ ret = alloc_private(dev, sizeof(*devpriv));
if (ret < 0) {
printk(" - Allocation failed!\n");
return -ENOMEM;
}
+ devpriv = dev->private;
pcidev = pci9118_find_pci(dev, it);
if (!pcidev)
@@ -2273,7 +2033,7 @@ static int pci9118_attach(struct comedi_device *dev,
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
@@ -2294,7 +2054,7 @@ static int pci9118_attach(struct comedi_device *dev,
s->munge = pci9118_ai_munge;
}
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = this_board->n_aochan;
@@ -2304,7 +2064,7 @@ static int pci9118_attach(struct comedi_device *dev,
s->insn_write = pci9118_insn_write_ao;
s->insn_read = pci9118_insn_read_ao;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = 4;
@@ -2314,7 +2074,7 @@ static int pci9118_attach(struct comedi_device *dev,
s->io_bits = 0; /* all bits input */
s->insn_bits = pci9118_insn_bits_di;
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = 4;
@@ -2345,8 +2105,9 @@ static int pci9118_attach(struct comedi_device *dev,
static void pci9118_detach(struct comedi_device *dev)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct pci9118_private *devpriv = dev->private;
- if (dev->private) {
+ if (devpriv) {
if (devpriv->valid)
pci9118_reset(dev);
if (dev->irq)
diff --git a/drivers/staging/comedi/drivers/adq12b.c b/drivers/staging/comedi/drivers/adq12b.c
index 6df51c8a602a..3a2aa5628be3 100644
--- a/drivers/staging/comedi/drivers/adq12b.c
+++ b/drivers/staging/comedi/drivers/adq12b.c
@@ -133,8 +133,6 @@ struct adq12b_private {
unsigned int digital_state;
};
-#define devpriv ((struct adq12b_private *)dev->private)
-
/*
* "instructions" read/write data in "one-shot" or "software-triggered"
* mode.
@@ -144,6 +142,7 @@ static int adq12b_ai_rinsn(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_insn *insn,
unsigned int *data)
{
+ struct adq12b_private *devpriv = dev->private;
int n, i;
int range, channel;
unsigned char hi, lo, status;
@@ -200,6 +199,7 @@ static int adq12b_do_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct adq12b_private *devpriv = dev->private;
int channel;
for (channel = 0; channel < 8; channel++)
@@ -221,6 +221,7 @@ static int adq12b_do_insn_bits(struct comedi_device *dev,
static int adq12b_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
const struct adq12b_board *board = comedi_board(dev);
+ struct adq12b_private *devpriv;
struct comedi_subdevice *s;
unsigned long iobase;
int unipolar, differential;
@@ -252,19 +253,18 @@ static int adq12b_attach(struct comedi_device *dev, struct comedi_devconfig *it)
dev->board_name = board->name;
-/*
- * Allocate the private structure area. alloc_private() is a
- * convenient macro defined in comedidev.h.
- */
- if (alloc_private(dev, sizeof(struct adq12b_private)) < 0)
- return -ENOMEM;
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret)
+ return ret;
+ devpriv = dev->private;
-/* fill in devpriv structure */
devpriv->unipolar = unipolar;
devpriv->differential = differential;
devpriv->digital_state = 0;
-/* initialize channel and range to -1 so we make sure we always write
- at least once to the CTREG in the instruction */
+ /*
+ * initialize channel and range to -1 so we make sure we
+ * always write at least once to the CTREG in the instruction
+ */
devpriv->last_channel = -1;
devpriv->last_range = -1;
@@ -272,7 +272,7 @@ static int adq12b_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* analog input subdevice */
s->type = COMEDI_SUBD_AI;
if (differential) {
@@ -294,7 +294,7 @@ static int adq12b_attach(struct comedi_device *dev, struct comedi_devconfig *it)
the board can handle */
s->insn_read = adq12b_ai_rinsn;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* digital input subdevice */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
@@ -303,7 +303,7 @@ static int adq12b_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->range_table = &range_digital;
s->insn_bits = adq12b_di_insn_bits;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* digital output subdevice */
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
@@ -321,7 +321,6 @@ static void adq12b_detach(struct comedi_device *dev)
{
if (dev->iobase)
release_region(dev->iobase, ADQ12B_SIZE);
- kfree(devpriv);
}
static const struct adq12b_board adq12b_boards[] = {
diff --git a/drivers/staging/comedi/drivers/adv_pci1710.c b/drivers/staging/comedi/drivers/adv_pci1710.c
index 6b4d0d68e637..def37bcc2a66 100644
--- a/drivers/staging/comedi/drivers/adv_pci1710.c
+++ b/drivers/staging/comedi/drivers/adv_pci1710.c
@@ -45,6 +45,7 @@ Configuration options:
#include "../comedidev.h"
+#include "comedi_fc.h"
#include "8253.h"
#include "amcc_s5933.h"
@@ -52,17 +53,6 @@ Configuration options:
* correct channel number on every 12 bit
* sample */
-#undef PCI171X_EXTDEBUG
-
-#define DRV_NAME "adv_pci1710"
-
-#undef DPRINTK
-#ifdef PCI171X_EXTDEBUG
-#define DPRINTK(fmt, args...) printk(fmt, ## args)
-#else
-#define DPRINTK(fmt, args...)
-#endif
-
#define PCI_VENDOR_ID_ADVANTECH 0x13fe
/* hardware types of the cards */
@@ -211,44 +201,101 @@ struct boardtype {
};
static const struct boardtype boardtypes[] = {
- {"pci1710", 0x1710,
- IORANGE_171x, 1, TYPE_PCI171X,
- 16, 8, 2, 16, 16, 1, 0x0fff, 0x0fff,
- &range_pci1710_3, range_codes_pci1710_3,
- &range_pci171x_da,
- 10000, 2048},
- {"pci1710hg", 0x1710,
- IORANGE_171x, 1, TYPE_PCI171X,
- 16, 8, 2, 16, 16, 1, 0x0fff, 0x0fff,
- &range_pci1710hg, range_codes_pci1710hg,
- &range_pci171x_da,
- 10000, 2048},
- {"pci1711", 0x1711,
- IORANGE_171x, 1, TYPE_PCI171X,
- 16, 0, 2, 16, 16, 1, 0x0fff, 0x0fff,
- &range_pci17x1, range_codes_pci17x1, &range_pci171x_da,
- 10000, 512},
- {"pci1713", 0x1713,
- IORANGE_171x, 1, TYPE_PCI1713,
- 32, 16, 0, 0, 0, 0, 0x0fff, 0x0000,
- &range_pci1710_3, range_codes_pci1710_3, NULL,
- 10000, 2048},
- {"pci1720", 0x1720,
- IORANGE_1720, 0, TYPE_PCI1720,
- 0, 0, 4, 0, 0, 0, 0x0000, 0x0fff,
- NULL, NULL, &range_pci1720,
- 0, 0},
- {"pci1731", 0x1731,
- IORANGE_171x, 1, TYPE_PCI171X,
- 16, 0, 0, 16, 16, 0, 0x0fff, 0x0000,
- &range_pci17x1, range_codes_pci17x1, NULL,
- 10000, 512},
- /* dummy entry corresponding to driver name */
- {.name = DRV_NAME},
+ {
+ .name = "pci1710",
+ .device_id = 0x1710,
+ .iorange = IORANGE_171x,
+ .have_irq = 1,
+ .cardtype = TYPE_PCI171X,
+ .n_aichan = 16,
+ .n_aichand = 8,
+ .n_aochan = 2,
+ .n_dichan = 16,
+ .n_dochan = 16,
+ .n_counter = 1,
+ .ai_maxdata = 0x0fff,
+ .ao_maxdata = 0x0fff,
+ .rangelist_ai = &range_pci1710_3,
+ .rangecode_ai = range_codes_pci1710_3,
+ .rangelist_ao = &range_pci171x_da,
+ .ai_ns_min = 10000,
+ .fifo_half_size = 2048,
+ }, {
+ .name = "pci1710hg",
+ .device_id = 0x1710,
+ .iorange = IORANGE_171x,
+ .have_irq = 1,
+ .cardtype = TYPE_PCI171X,
+ .n_aichan = 16,
+ .n_aichand = 8,
+ .n_aochan = 2,
+ .n_dichan = 16,
+ .n_dochan = 16,
+ .n_counter = 1,
+ .ai_maxdata = 0x0fff,
+ .ao_maxdata = 0x0fff,
+ .rangelist_ai = &range_pci1710hg,
+ .rangecode_ai = range_codes_pci1710hg,
+ .rangelist_ao = &range_pci171x_da,
+ .ai_ns_min = 10000,
+ .fifo_half_size = 2048,
+ }, {
+ .name = "pci1711",
+ .device_id = 0x1711,
+ .iorange = IORANGE_171x,
+ .have_irq = 1,
+ .cardtype = TYPE_PCI171X,
+ .n_aichan = 16,
+ .n_aochan = 2,
+ .n_dichan = 16,
+ .n_dochan = 16,
+ .n_counter = 1,
+ .ai_maxdata = 0x0fff,
+ .ao_maxdata = 0x0fff,
+ .rangelist_ai = &range_pci17x1,
+ .rangecode_ai = range_codes_pci17x1,
+ .rangelist_ao = &range_pci171x_da,
+ .ai_ns_min = 10000,
+ .fifo_half_size = 512,
+ }, {
+ .name = "pci1713",
+ .device_id = 0x1713,
+ .iorange = IORANGE_171x,
+ .have_irq = 1,
+ .cardtype = TYPE_PCI1713,
+ .n_aichan = 32,
+ .n_aichand = 16,
+ .ai_maxdata = 0x0fff,
+ .rangelist_ai = &range_pci1710_3,
+ .rangecode_ai = range_codes_pci1710_3,
+ .ai_ns_min = 10000,
+ .fifo_half_size = 2048,
+ }, {
+ .name = "pci1720",
+ .device_id = 0x1720,
+ .iorange = IORANGE_1720,
+ .cardtype = TYPE_PCI1720,
+ .n_aochan = 4,
+ .ao_maxdata = 0x0fff,
+ .rangelist_ao = &range_pci1720,
+ }, {
+ .name = "pci1731",
+ .device_id = 0x1731,
+ .iorange = IORANGE_171x,
+ .have_irq = 1,
+ .cardtype = TYPE_PCI171X,
+ .n_aichan = 16,
+ .n_dichan = 16,
+ .n_dochan = 16,
+ .ai_maxdata = 0x0fff,
+ .rangelist_ai = &range_pci17x1,
+ .rangecode_ai = range_codes_pci17x1,
+ .ai_ns_min = 10000,
+ .fifo_half_size = 512,
+ },
};
struct pci1710_private {
- char valid; /* card is usable */
char neverending_ai; /* we do unlimited AI */
unsigned int CntrlReg; /* Control register */
unsigned int i8254_osc_base; /* frequence of onboard oscilator */
@@ -278,33 +325,108 @@ struct pci1710_private {
* internal state */
};
-#define devpriv ((struct pci1710_private *)dev->private)
-#define this_board ((const struct boardtype *)dev->board_ptr)
+/* used for gain list programming */
+static const unsigned int muxonechan[] = {
+ 0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707,
+ 0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
+ 0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
+ 0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f
+};
/*
==============================================================================
+ Check if channel list from user is builded correctly
+ If it's ok, then program scan/gain logic.
+ This works for all cards.
*/
-
static int check_channel_list(struct comedi_device *dev,
struct comedi_subdevice *s,
- unsigned int *chanlist, unsigned int n_chan);
+ unsigned int *chanlist, unsigned int n_chan)
+{
+ unsigned int chansegment[32];
+ unsigned int i, nowmustbechan, seglen, segpos;
+
+ /* correct channel and range number check itself comedi/range.c */
+ if (n_chan < 1) {
+ comedi_error(dev, "range/channel list is empty!");
+ return 0;
+ }
+
+ if (n_chan == 1)
+ return 1; /* seglen=1 */
+
+ chansegment[0] = chanlist[0]; /* first channel is every time ok */
+ for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
+ if (chanlist[0] == chanlist[i])
+ break; /* we detected a loop, stop */
+ if ((CR_CHAN(chanlist[i]) & 1) &&
+ (CR_AREF(chanlist[i]) == AREF_DIFF)) {
+ comedi_error(dev, "Odd channel cannot be differential input!\n");
+ return 0;
+ }
+ nowmustbechan = (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
+ if (CR_AREF(chansegment[i - 1]) == AREF_DIFF)
+ nowmustbechan = (nowmustbechan + 1) % s->n_chan;
+ if (nowmustbechan != CR_CHAN(chanlist[i])) {
+ printk("channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
+ i, CR_CHAN(chanlist[i]), nowmustbechan,
+ CR_CHAN(chanlist[0]));
+ return 0;
+ }
+ chansegment[i] = chanlist[i]; /* next correct channel in list */
+ }
+
+ for (i = 0, segpos = 0; i < n_chan; i++) {
+ if (chanlist[i] != chansegment[i % seglen]) {
+ printk("bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
+ i, CR_CHAN(chansegment[i]),
+ CR_RANGE(chansegment[i]),
+ CR_AREF(chansegment[i]),
+ CR_CHAN(chanlist[i % seglen]),
+ CR_RANGE(chanlist[i % seglen]),
+ CR_AREF(chansegment[i % seglen]));
+ return 0;
+ }
+ }
+ return seglen;
+}
+
static void setup_channel_list(struct comedi_device *dev,
struct comedi_subdevice *s,
unsigned int *chanlist, unsigned int n_chan,
- unsigned int seglen);
-static void start_pacer(struct comedi_device *dev, int mode,
- unsigned int divisor1, unsigned int divisor2);
-static int pci1710_reset(struct comedi_device *dev);
-static int pci171x_ai_cancel(struct comedi_device *dev,
- struct comedi_subdevice *s);
+ unsigned int seglen)
+{
+ const struct boardtype *this_board = comedi_board(dev);
+ struct pci1710_private *devpriv = dev->private;
+ unsigned int i, range, chanprog;
-/* used for gain list programming */
-static const unsigned int muxonechan[] = {
- 0x0000, 0x0101, 0x0202, 0x0303, 0x0404, 0x0505, 0x0606, 0x0707,
- 0x0808, 0x0909, 0x0a0a, 0x0b0b, 0x0c0c, 0x0d0d, 0x0e0e, 0x0f0f,
- 0x1010, 0x1111, 0x1212, 0x1313, 0x1414, 0x1515, 0x1616, 0x1717,
- 0x1818, 0x1919, 0x1a1a, 0x1b1b, 0x1c1c, 0x1d1d, 0x1e1e, 0x1f1f
-};
+ devpriv->act_chanlist_len = seglen;
+ devpriv->act_chanlist_pos = 0;
+
+ for (i = 0; i < seglen; i++) { /* store range list to card */
+ chanprog = muxonechan[CR_CHAN(chanlist[i])];
+ outw(chanprog, dev->iobase + PCI171x_MUX); /* select channel */
+ range = this_board->rangecode_ai[CR_RANGE(chanlist[i])];
+ if (CR_AREF(chanlist[i]) == AREF_DIFF)
+ range |= 0x0020;
+ outw(range, dev->iobase + PCI171x_RANGE); /* select gain */
+#ifdef PCI171x_PARANOIDCHECK
+ devpriv->act_chanlist[i] =
+ (CR_CHAN(chanlist[i]) << 12) & 0xf000;
+#endif
+ }
+#ifdef PCI171x_PARANOIDCHECK
+ for ( ; i < n_chan; i++) { /* store remainder of channel list */
+ devpriv->act_chanlist[i] =
+ (CR_CHAN(chanlist[i]) << 12) & 0xf000;
+ }
+#endif
+
+ devpriv->ai_et_MuxVal =
+ CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8);
+ /* select channel interval to scan */
+ outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
+}
/*
==============================================================================
@@ -313,12 +435,13 @@ static int pci171x_insn_read_ai(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct pci1710_private *devpriv = dev->private;
int n, timeout;
#ifdef PCI171x_PARANOIDCHECK
+ const struct boardtype *this_board = comedi_board(dev);
unsigned int idata;
#endif
- DPRINTK("adv_pci1710 EDBG: BGN: pci171x_insn_read_ai(...)\n");
devpriv->CntrlReg &= Control_CNT0;
devpriv->CntrlReg |= Control_SW; /* set software trigger */
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
@@ -327,32 +450,18 @@ static int pci171x_insn_read_ai(struct comedi_device *dev,
setup_channel_list(dev, s, &insn->chanspec, 1, 1);
- DPRINTK("adv_pci1710 A ST=%4x IO=%x\n",
- inw(dev->iobase + PCI171x_STATUS),
- dev->iobase + PCI171x_STATUS);
for (n = 0; n < insn->n; n++) {
outw(0, dev->iobase + PCI171x_SOFTTRG); /* start conversion */
- DPRINTK("adv_pci1710 B n=%d ST=%4x\n", n,
- inw(dev->iobase + PCI171x_STATUS));
/* udelay(1); */
- DPRINTK("adv_pci1710 C n=%d ST=%4x\n", n,
- inw(dev->iobase + PCI171x_STATUS));
timeout = 100;
while (timeout--) {
if (!(inw(dev->iobase + PCI171x_STATUS) & Status_FE))
goto conv_finish;
- if (!(timeout % 10))
- DPRINTK("adv_pci1710 D n=%d tm=%d ST=%4x\n", n,
- timeout,
- inw(dev->iobase + PCI171x_STATUS));
}
comedi_error(dev, "A/D insn timeout");
outb(0, dev->iobase + PCI171x_CLRFIFO);
outb(0, dev->iobase + PCI171x_CLRINT);
data[n] = 0;
- DPRINTK
- ("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n",
- n);
return -ETIME;
conv_finish:
@@ -373,7 +482,6 @@ conv_finish:
outb(0, dev->iobase + PCI171x_CLRFIFO);
outb(0, dev->iobase + PCI171x_CLRINT);
- DPRINTK("adv_pci1710 EDBG: END: pci171x_insn_read_ai(...) n=%d\n", n);
return n;
}
@@ -384,6 +492,7 @@ static int pci171x_insn_write_ao(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct pci1710_private *devpriv = dev->private;
int n, chan, range, ofs;
chan = CR_CHAN(insn->chanspec);
@@ -416,6 +525,7 @@ static int pci171x_insn_read_ao(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct pci1710_private *devpriv = dev->private;
int n, chan;
chan = CR_CHAN(insn->chanspec);
@@ -457,6 +567,23 @@ static int pci171x_insn_bits_do(struct comedi_device *dev,
/*
==============================================================================
*/
+static void start_pacer(struct comedi_device *dev, int mode,
+ unsigned int divisor1, unsigned int divisor2)
+{
+ outw(0xb4, dev->iobase + PCI171x_CNTCTRL);
+ outw(0x74, dev->iobase + PCI171x_CNTCTRL);
+
+ if (mode == 1) {
+ outw(divisor2 & 0xff, dev->iobase + PCI171x_CNT2);
+ outw((divisor2 >> 8) & 0xff, dev->iobase + PCI171x_CNT2);
+ outw(divisor1 & 0xff, dev->iobase + PCI171x_CNT1);
+ outw((divisor1 >> 8) & 0xff, dev->iobase + PCI171x_CNT1);
+ }
+}
+
+/*
+==============================================================================
+*/
static int pci171x_insn_counter_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
@@ -486,6 +613,7 @@ static int pci171x_insn_counter_write(struct comedi_device *dev,
struct comedi_insn *insn,
unsigned int *data)
{
+ struct pci1710_private *devpriv = dev->private;
uint msb, lsb, ccntrl, status;
lsb = data[0] & 0x00FF;
@@ -517,6 +645,7 @@ static int pci171x_insn_counter_config(struct comedi_device *dev,
{
#ifdef unused
/* This doesn't work like a normal Comedi counter config */
+ struct pci1710_private *devpriv = dev->private;
uint ccntrl = 0;
devpriv->cnt0_write_wait = data[0] & 0x20;
@@ -552,6 +681,7 @@ static int pci1720_insn_write_ao(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct pci1710_private *devpriv = dev->private;
int n, rangereg, chan;
chan = CR_CHAN(insn->chanspec);
@@ -575,16 +705,47 @@ static int pci1720_insn_write_ao(struct comedi_device *dev,
/*
==============================================================================
*/
+static int pci171x_ai_cancel(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ const struct boardtype *this_board = comedi_board(dev);
+ struct pci1710_private *devpriv = dev->private;
+
+ switch (this_board->cardtype) {
+ default:
+ devpriv->CntrlReg &= Control_CNT0;
+ devpriv->CntrlReg |= Control_SW;
+
+ outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); /* reset any operations */
+ start_pacer(dev, -1, 0, 0);
+ outb(0, dev->iobase + PCI171x_CLRFIFO);
+ outb(0, dev->iobase + PCI171x_CLRINT);
+ break;
+ }
+
+ devpriv->ai_do = 0;
+ devpriv->ai_act_scan = 0;
+ s->async->cur_chan = 0;
+ devpriv->ai_buf_ptr = 0;
+ devpriv->neverending_ai = 0;
+
+ return 0;
+}
+
+/*
+==============================================================================
+*/
static void interrupt_pci1710_every_sample(void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct pci1710_private *devpriv = dev->private;
+ struct comedi_subdevice *s = &dev->subdevices[0];
int m;
#ifdef PCI171x_PARANOIDCHECK
+ const struct boardtype *this_board = comedi_board(dev);
short sampl;
#endif
- DPRINTK("adv_pci1710 EDBG: BGN: interrupt_pci1710_every_sample(...)\n");
m = inw(dev->iobase + PCI171x_STATUS);
if (m & Status_FE) {
printk("comedi%d: A/D FIFO empty (%4x)\n", dev->minor, m);
@@ -605,11 +766,9 @@ static void interrupt_pci1710_every_sample(void *d)
outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
- DPRINTK("FOR ");
for (; !(inw(dev->iobase + PCI171x_STATUS) & Status_FE);) {
#ifdef PCI171x_PARANOIDCHECK
sampl = inw(dev->iobase + PCI171x_AD_DATA);
- DPRINTK("%04x:", sampl);
if (this_board->cardtype != TYPE_PCI1713)
if ((sampl & 0xf000) !=
devpriv->act_chanlist[s->async->cur_chan]) {
@@ -626,8 +785,6 @@ static void interrupt_pci1710_every_sample(void *d)
comedi_event(dev, s);
return;
}
- DPRINTK("%8d %2d %8d~", s->async->buf_int_ptr,
- s->async->cur_chan, s->async->buf_int_count);
comedi_buf_put(s->async, sampl & 0x0fff);
#else
comedi_buf_put(s->async,
@@ -641,11 +798,6 @@ static void interrupt_pci1710_every_sample(void *d)
if (s->async->cur_chan == 0) { /* one scan done */
devpriv->ai_act_scan++;
- DPRINTK
- ("adv_pci1710 EDBG: EOS1 bic %d bip %d buc %d bup %d\n",
- s->async->buf_int_count, s->async->buf_int_ptr,
- s->async->buf_user_count, s->async->buf_user_ptr);
- DPRINTK("adv_pci1710 EDBG: EOS2\n");
if ((!devpriv->neverending_ai) &&
(devpriv->ai_act_scan >= devpriv->ai_scans)) {
/* all data sampled */
@@ -658,7 +810,6 @@ static void interrupt_pci1710_every_sample(void *d)
}
outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
- DPRINTK("adv_pci1710 EDBG: END: interrupt_pci1710_every_sample(...)\n");
comedi_event(dev, s);
}
@@ -669,12 +820,13 @@ static void interrupt_pci1710_every_sample(void *d)
static int move_block_from_fifo(struct comedi_device *dev,
struct comedi_subdevice *s, int n, int turn)
{
+ struct pci1710_private *devpriv = dev->private;
int i, j;
#ifdef PCI171x_PARANOIDCHECK
+ const struct boardtype *this_board = comedi_board(dev);
int sampl;
#endif
- DPRINTK("adv_pci1710 EDBG: BGN: move_block_from_fifo(...,%d,%d)\n", n,
- turn);
+
j = s->async->cur_chan;
for (i = 0; i < n; i++) {
#ifdef PCI171x_PARANOIDCHECK
@@ -705,7 +857,6 @@ static int move_block_from_fifo(struct comedi_device *dev,
}
}
s->async->cur_chan = j;
- DPRINTK("adv_pci1710 EDBG: END: move_block_from_fifo(...)\n");
return 0;
}
@@ -715,10 +866,11 @@ static int move_block_from_fifo(struct comedi_device *dev,
static void interrupt_pci1710_half_fifo(void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ const struct boardtype *this_board = comedi_board(dev);
+ struct pci1710_private *devpriv = dev->private;
+ struct comedi_subdevice *s = &dev->subdevices[0];
int m, samplesinbuf;
- DPRINTK("adv_pci1710 EDBG: BGN: interrupt_pci1710_half_fifo(...)\n");
m = inw(dev->iobase + PCI171x_STATUS);
if (!(m & Status_FH)) {
printk("comedi%d: A/D FIFO not half full! (%4x)\n",
@@ -760,7 +912,6 @@ static void interrupt_pci1710_half_fifo(void *d)
return;
}
outb(0, dev->iobase + PCI171x_CLRINT); /* clear our INT request */
- DPRINTK("adv_pci1710 EDBG: END: interrupt_pci1710_half_fifo(...)\n");
comedi_event(dev, s);
}
@@ -771,18 +922,14 @@ static void interrupt_pci1710_half_fifo(void *d)
static irqreturn_t interrupt_service_pci1710(int irq, void *d)
{
struct comedi_device *dev = d;
+ struct pci1710_private *devpriv = dev->private;
- DPRINTK("adv_pci1710 EDBG: BGN: interrupt_service_pci1710(%d,...)\n",
- irq);
if (!dev->attached) /* is device attached? */
return IRQ_NONE; /* no, exit */
/* is this interrupt from our board? */
if (!(inw(dev->iobase + PCI171x_STATUS) & Status_IRQ))
return IRQ_NONE; /* no, exit */
- DPRINTK("adv_pci1710 EDBG: interrupt_service_pci1710() ST: %4x\n",
- inw(dev->iobase + PCI171x_STATUS));
-
if (devpriv->ai_et) { /* Switch from initial TRIG_EXT to TRIG_xxx. */
devpriv->ai_et = 0;
devpriv->CntrlReg &= Control_CNT0;
@@ -802,7 +949,6 @@ static irqreturn_t interrupt_service_pci1710(int irq, void *d)
} else {
interrupt_pci1710_half_fifo(d);
}
- DPRINTK("adv_pci1710 EDBG: END: interrupt_service_pci1710(...)\n");
return IRQ_HANDLED;
}
@@ -812,11 +958,11 @@ static irqreturn_t interrupt_service_pci1710(int irq, void *d)
static int pci171x_ai_docmd_and_mode(int mode, struct comedi_device *dev,
struct comedi_subdevice *s)
{
+ const struct boardtype *this_board = comedi_board(dev);
+ struct pci1710_private *devpriv = dev->private;
unsigned int divisor1 = 0, divisor2 = 0;
unsigned int seglen;
- DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_docmd_and_mode(%d,...)\n",
- mode);
start_pacer(dev, -1, 0, 0); /* stop pacer */
seglen = check_channel_list(dev, s, devpriv->ai_chanlist,
@@ -869,10 +1015,6 @@ static int pci171x_ai_docmd_and_mode(int mode, struct comedi_device *dev,
i8253_cascade_ns_to_timer(devpriv->i8254_osc_base, &divisor1,
&divisor2, &devpriv->ai_timer1,
devpriv->ai_flags & TRIG_ROUND_MASK);
- DPRINTK
- ("adv_pci1710 EDBG: OSC base=%u div1=%u div2=%u timer=%u\n",
- devpriv->i8254_osc_base, divisor1, divisor2,
- devpriv->ai_timer1);
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL);
if (mode != 2) {
/* start pacer */
@@ -888,27 +1030,9 @@ static int pci171x_ai_docmd_and_mode(int mode, struct comedi_device *dev,
break;
}
- DPRINTK("adv_pci1710 EDBG: END: pci171x_ai_docmd_and_mode(...)\n");
return 0;
}
-#ifdef PCI171X_EXTDEBUG
-/*
-==============================================================================
-*/
-static void pci171x_cmdtest_out(int e, struct comedi_cmd *cmd)
-{
- printk("adv_pci1710 e=%d startsrc=%x scansrc=%x convsrc=%x\n", e,
- cmd->start_src, cmd->scan_begin_src, cmd->convert_src);
- printk("adv_pci1710 e=%d startarg=%d scanarg=%d convarg=%d\n", e,
- cmd->start_arg, cmd->scan_begin_arg, cmd->convert_arg);
- printk("adv_pci1710 e=%d stopsrc=%x scanend=%x\n", e, cmd->stop_src,
- cmd->scan_end_src);
- printk("adv_pci1710 e=%d stoparg=%d scanendarg=%d chanlistlen=%d\n",
- e, cmd->stop_arg, cmd->scan_end_arg, cmd->chanlist_len);
-}
-#endif
-
/*
==============================================================================
*/
@@ -916,83 +1040,33 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
+ const struct boardtype *this_board = comedi_board(dev);
+ struct pci1710_private *devpriv = dev->private;
int err = 0;
int tmp;
unsigned int divisor1 = 0, divisor2 = 0;
- DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...)\n");
-#ifdef PCI171X_EXTDEBUG
- pci171x_cmdtest_out(-1, cmd);
-#endif
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
-
- if (err) {
-#ifdef PCI171X_EXTDEBUG
- pci171x_cmdtest_out(1, cmd);
-#endif
- DPRINTK(
- "adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=1\n",
- err);
+ if (err)
return 1;
- }
-
- /* step2: make sure trigger srcs are unique and mutually compatible */
-
- if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT) {
- cmd->start_src = TRIG_NOW;
- err++;
- }
- if (cmd->scan_begin_src != TRIG_FOLLOW) {
- cmd->scan_begin_src = TRIG_FOLLOW;
- err++;
- }
+ /* step 2a: make sure trigger sources are unique */
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
- if (cmd->scan_end_src != TRIG_COUNT) {
- cmd->scan_end_src = TRIG_COUNT;
- err++;
- }
-
- if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
- err++;
+ /* step 2b: and mutually compatible */
- if (err) {
-#ifdef PCI171X_EXTDEBUG
- pci171x_cmdtest_out(2, cmd);
-#endif
- DPRINTK(
- "adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=2\n",
- err);
+ if (err)
return 2;
- }
/* step 3: make sure arguments are trivially compatible */
@@ -1034,15 +1108,8 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
}
}
- if (err) {
-#ifdef PCI171X_EXTDEBUG
- pci171x_cmdtest_out(3, cmd);
-#endif
- DPRINTK(
- "adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=3\n",
- err);
+ if (err)
return 3;
- }
/* step 4: fix up any arguments */
@@ -1057,12 +1124,8 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
err++;
}
- if (err) {
- DPRINTK
- ("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) err=%d ret=4\n",
- err);
+ if (err)
return 4;
- }
/* step 5: complain about special chanlist considerations */
@@ -1072,7 +1135,6 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
return 5; /* incorrect channels list */
}
- DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmdtest(...) ret=0\n");
return 0;
}
@@ -1081,9 +1143,9 @@ static int pci171x_ai_cmdtest(struct comedi_device *dev,
*/
static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
{
+ struct pci1710_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd;
- DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cmd(...)\n");
devpriv->ai_n_chan = cmd->chanlist_len;
devpriv->ai_chanlist = cmd->chanlist;
devpriv->ai_flags = cmd->flags;
@@ -1115,162 +1177,12 @@ static int pci171x_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
/*
==============================================================================
- Check if channel list from user is builded correctly
- If it's ok, then program scan/gain logic.
- This works for all cards.
-*/
-static int check_channel_list(struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int *chanlist, unsigned int n_chan)
-{
- unsigned int chansegment[32];
- unsigned int i, nowmustbechan, seglen, segpos;
-
- DPRINTK("adv_pci1710 EDBG: check_channel_list(...,%d)\n", n_chan);
- /* correct channel and range number check itself comedi/range.c */
- if (n_chan < 1) {
- comedi_error(dev, "range/channel list is empty!");
- return 0;
- }
-
- if (n_chan == 1)
- return 1; /* seglen=1 */
-
- chansegment[0] = chanlist[0]; /* first channel is every time ok */
- for (i = 1, seglen = 1; i < n_chan; i++, seglen++) {
- if (chanlist[0] == chanlist[i])
- break; /* we detected a loop, stop */
- if ((CR_CHAN(chanlist[i]) & 1) &&
- (CR_AREF(chanlist[i]) == AREF_DIFF)) {
- comedi_error(dev, "Odd channel cannot be differential input!\n");
- return 0;
- }
- nowmustbechan = (CR_CHAN(chansegment[i - 1]) + 1) % s->n_chan;
- if (CR_AREF(chansegment[i - 1]) == AREF_DIFF)
- nowmustbechan = (nowmustbechan + 1) % s->n_chan;
- if (nowmustbechan != CR_CHAN(chanlist[i])) {
- printk("channel list must be continuous! chanlist[%i]=%d but must be %d or %d!\n",
- i, CR_CHAN(chanlist[i]), nowmustbechan,
- CR_CHAN(chanlist[0]));
- return 0;
- }
- chansegment[i] = chanlist[i]; /* next correct channel in list */
- }
-
- for (i = 0, segpos = 0; i < n_chan; i++) {
- if (chanlist[i] != chansegment[i % seglen]) {
- printk("bad channel, reference or range number! chanlist[%i]=%d,%d,%d and not %d,%d,%d!\n",
- i, CR_CHAN(chansegment[i]),
- CR_RANGE(chansegment[i]),
- CR_AREF(chansegment[i]),
- CR_CHAN(chanlist[i % seglen]),
- CR_RANGE(chanlist[i % seglen]),
- CR_AREF(chansegment[i % seglen]));
- return 0;
- }
- }
- return seglen;
-}
-
-static void setup_channel_list(struct comedi_device *dev,
- struct comedi_subdevice *s,
- unsigned int *chanlist, unsigned int n_chan,
- unsigned int seglen)
-{
- unsigned int i, range, chanprog;
-
- DPRINTK("adv_pci1710 EDBG: setup_channel_list(...,%d,%d)\n", n_chan,
- seglen);
- devpriv->act_chanlist_len = seglen;
- devpriv->act_chanlist_pos = 0;
-
- DPRINTK("SegLen: %d\n", seglen);
- for (i = 0; i < seglen; i++) { /* store range list to card */
- chanprog = muxonechan[CR_CHAN(chanlist[i])];
- outw(chanprog, dev->iobase + PCI171x_MUX); /* select channel */
- range = this_board->rangecode_ai[CR_RANGE(chanlist[i])];
- if (CR_AREF(chanlist[i]) == AREF_DIFF)
- range |= 0x0020;
- outw(range, dev->iobase + PCI171x_RANGE); /* select gain */
-#ifdef PCI171x_PARANOIDCHECK
- devpriv->act_chanlist[i] =
- (CR_CHAN(chanlist[i]) << 12) & 0xf000;
-#endif
- DPRINTK("GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
- devpriv->act_chanlist[i]);
- }
-#ifdef PCI171x_PARANOIDCHECK
- for ( ; i < n_chan; i++) { /* store remainder of channel list */
- devpriv->act_chanlist[i] =
- (CR_CHAN(chanlist[i]) << 12) & 0xf000;
- }
-#endif
-
- devpriv->ai_et_MuxVal =
- CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8);
- /* select channel interval to scan */
- outw(devpriv->ai_et_MuxVal, dev->iobase + PCI171x_MUX);
- DPRINTK("MUX: %4x L%4x.H%4x\n",
- CR_CHAN(chanlist[0]) | (CR_CHAN(chanlist[seglen - 1]) << 8),
- CR_CHAN(chanlist[0]), CR_CHAN(chanlist[seglen - 1]));
-}
-
-/*
-==============================================================================
-*/
-static void start_pacer(struct comedi_device *dev, int mode,
- unsigned int divisor1, unsigned int divisor2)
-{
- DPRINTK("adv_pci1710 EDBG: BGN: start_pacer(%d,%u,%u)\n", mode,
- divisor1, divisor2);
- outw(0xb4, dev->iobase + PCI171x_CNTCTRL);
- outw(0x74, dev->iobase + PCI171x_CNTCTRL);
-
- if (mode == 1) {
- outw(divisor2 & 0xff, dev->iobase + PCI171x_CNT2);
- outw((divisor2 >> 8) & 0xff, dev->iobase + PCI171x_CNT2);
- outw(divisor1 & 0xff, dev->iobase + PCI171x_CNT1);
- outw((divisor1 >> 8) & 0xff, dev->iobase + PCI171x_CNT1);
- }
- DPRINTK("adv_pci1710 EDBG: END: start_pacer(...)\n");
-}
-
-/*
-==============================================================================
-*/
-static int pci171x_ai_cancel(struct comedi_device *dev,
- struct comedi_subdevice *s)
-{
- DPRINTK("adv_pci1710 EDBG: BGN: pci171x_ai_cancel(...)\n");
-
- switch (this_board->cardtype) {
- default:
- devpriv->CntrlReg &= Control_CNT0;
- devpriv->CntrlReg |= Control_SW;
-
- outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); /* reset any operations */
- start_pacer(dev, -1, 0, 0);
- outb(0, dev->iobase + PCI171x_CLRFIFO);
- outb(0, dev->iobase + PCI171x_CLRINT);
- break;
- }
-
- devpriv->ai_do = 0;
- devpriv->ai_act_scan = 0;
- s->async->cur_chan = 0;
- devpriv->ai_buf_ptr = 0;
- devpriv->neverending_ai = 0;
-
- DPRINTK("adv_pci1710 EDBG: END: pci171x_ai_cancel(...)\n");
- return 0;
-}
-
-/*
-==============================================================================
*/
static int pci171x_reset(struct comedi_device *dev)
{
- DPRINTK("adv_pci1710 EDBG: BGN: pci171x_reset(...)\n");
+ const struct boardtype *this_board = comedi_board(dev);
+ struct pci1710_private *devpriv = dev->private;
+
outw(0x30, dev->iobase + PCI171x_CNTCTRL);
devpriv->CntrlReg = Control_SW | Control_CNT0; /* Software trigger, CNT0=external */
outw(devpriv->CntrlReg, dev->iobase + PCI171x_CONTROL); /* reset any operations */
@@ -1291,7 +1203,6 @@ static int pci171x_reset(struct comedi_device *dev)
outb(0, dev->iobase + PCI171x_CLRFIFO); /* clear FIFO */
outb(0, dev->iobase + PCI171x_CLRINT); /* clear INT request */
- DPRINTK("adv_pci1710 EDBG: END: pci171x_reset(...)\n");
return 0;
}
@@ -1300,7 +1211,8 @@ static int pci171x_reset(struct comedi_device *dev)
*/
static int pci1720_reset(struct comedi_device *dev)
{
- DPRINTK("adv_pci1710 EDBG: BGN: pci1720_reset(...)\n");
+ struct pci1710_private *devpriv = dev->private;
+
outb(Syncont_SC0, dev->iobase + PCI1720_SYNCONT); /* set synchronous output mode */
devpriv->da_ranges = 0xAA;
outb(devpriv->da_ranges, dev->iobase + PCI1720_RANGE); /* set all ranges to +/-5V */
@@ -1313,7 +1225,6 @@ static int pci1720_reset(struct comedi_device *dev)
devpriv->ao_data[1] = 0x0800;
devpriv->ao_data[2] = 0x0800;
devpriv->ao_data[3] = 0x0800;
- DPRINTK("adv_pci1710 EDBG: END: pci1720_reset(...)\n");
return 0;
}
@@ -1322,82 +1233,55 @@ static int pci1720_reset(struct comedi_device *dev)
*/
static int pci1710_reset(struct comedi_device *dev)
{
- DPRINTK("adv_pci1710 EDBG: BGN: pci1710_reset(...)\n");
+ const struct boardtype *this_board = comedi_board(dev);
+
switch (this_board->cardtype) {
case TYPE_PCI1720:
return pci1720_reset(dev);
default:
return pci171x_reset(dev);
}
- DPRINTK("adv_pci1710 EDBG: END: pci1710_reset(...)\n");
}
-static struct pci_dev *pci1710_find_pci_dev(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static const void *pci1710_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
- int board_index = this_board - boardtypes;
+ const struct boardtype *this_board;
int i;
- for_each_pci_dev(pcidev) {
- if (bus || slot) {
- if (bus != pcidev->bus->number ||
- slot != PCI_SLOT(pcidev->devfn))
- continue;
- }
- if (pcidev->vendor != PCI_VENDOR_ID_ADVANTECH)
- continue;
- if (strcmp(this_board->name, DRV_NAME) == 0) {
- for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
- if (pcidev->device == boardtypes[i].device_id) {
- board_index = i;
- break;
- }
- }
- if (i == ARRAY_SIZE(boardtypes))
- continue;
- } else {
- if (pcidev->device != boardtypes[board_index].device_id)
- continue;
- }
- dev->board_ptr = &boardtypes[board_index];
- return pcidev;
+ for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
+ this_board = &boardtypes[i];
+ if (pcidev->device == this_board->device_id)
+ return this_board;
}
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
return NULL;
}
-static int pci1710_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int pci1710_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- struct pci_dev *pcidev;
+ const struct boardtype *this_board;
+ struct pci1710_private *devpriv;
struct comedi_subdevice *s;
int ret, subdev, n_subdevices;
- unsigned int irq;
- dev_info(dev->class_dev, DRV_NAME ": attach\n");
+ comedi_set_hw_dev(dev, &pcidev->dev);
- ret = alloc_private(dev, sizeof(struct pci1710_private));
- if (ret < 0)
- return -ENOMEM;
+ this_board = pci1710_find_boardinfo(dev, pcidev);
+ if (!this_board)
+ return -ENODEV;
+ dev->board_ptr = this_board;
+ dev->board_name = this_board->name;
- pcidev = pci1710_find_pci_dev(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret < 0)
+ return ret;
+ devpriv = dev->private;
- ret = comedi_pci_enable(pcidev, DRV_NAME);
+ ret = comedi_pci_enable(pcidev, dev->board_name);
if (ret)
return ret;
-
dev->iobase = pci_resource_start(pcidev, 2);
- irq = pcidev->irq;
-
- dev->board_name = this_board->name;
n_subdevices = 0;
if (this_board->n_aichan)
@@ -1417,30 +1301,17 @@ static int pci1710_attach(struct comedi_device *dev,
pci1710_reset(dev);
- if (this_board->have_irq) {
- if (irq) {
- if (request_irq(irq, interrupt_service_pci1710,
- IRQF_SHARED, "Advantech PCI-1710",
- dev)) {
- dev_dbg(dev->class_dev,
- "unable to allocate IRQ %d, DISABLING IT",
- irq);
- irq = 0; /* Can't use IRQ */
- } else {
- dev_dbg(dev->class_dev, "irq=%u", irq);
- }
- } else {
- dev_dbg(dev->class_dev, "IRQ disabled");
- }
- } else {
- irq = 0;
+ if (this_board->have_irq && pcidev->irq) {
+ ret = request_irq(pcidev->irq, interrupt_service_pci1710,
+ IRQF_SHARED, dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
}
- dev->irq = irq;
subdev = 0;
if (this_board->n_aichan) {
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[subdev];
dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
@@ -1452,7 +1323,7 @@ static int pci1710_attach(struct comedi_device *dev,
s->range_table = this_board->rangelist_ai;
s->cancel = pci171x_ai_cancel;
s->insn_read = pci171x_insn_read_ai;
- if (irq) {
+ if (dev->irq) {
s->subdev_flags |= SDF_CMD_READ;
s->do_cmdtest = pci171x_ai_cmdtest;
s->do_cmd = pci171x_ai_cmd;
@@ -1462,7 +1333,7 @@ static int pci1710_attach(struct comedi_device *dev,
}
if (this_board->n_aochan) {
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[subdev];
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = this_board->n_aochan;
@@ -1482,7 +1353,7 @@ static int pci1710_attach(struct comedi_device *dev,
}
if (this_board->n_dichan) {
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[subdev];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = this_board->n_dichan;
@@ -1495,7 +1366,7 @@ static int pci1710_attach(struct comedi_device *dev,
}
if (this_board->n_dochan) {
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[subdev];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = this_board->n_dochan;
@@ -1510,7 +1381,7 @@ static int pci1710_attach(struct comedi_device *dev,
}
if (this_board->n_counter) {
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[subdev];
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = this_board->n_counter;
@@ -1523,7 +1394,8 @@ static int pci1710_attach(struct comedi_device *dev,
subdev++;
}
- devpriv->valid = 1;
+ dev_info(dev->class_dev, "%s attached, irq %sabled\n",
+ dev->board_name, dev->irq ? "en" : "dis");
return 0;
}
@@ -1532,27 +1404,21 @@ static void pci1710_detach(struct comedi_device *dev)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
- if (dev->private) {
- if (devpriv->valid)
- pci1710_reset(dev);
- if (dev->irq)
- free_irq(dev->irq, dev);
- }
+ if (dev->iobase)
+ pci1710_reset(dev);
+ if (dev->irq)
+ free_irq(dev->irq, dev);
if (pcidev) {
if (dev->iobase)
comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
}
}
static struct comedi_driver adv_pci1710_driver = {
.driver_name = "adv_pci1710",
.module = THIS_MODULE,
- .attach = pci1710_attach,
+ .attach_pci = pci1710_attach_pci,
.detach = pci1710_detach,
- .num_names = ARRAY_SIZE(boardtypes),
- .board_name = &boardtypes[0].name,
- .offset = sizeof(struct boardtype),
};
static int __devinit adv_pci1710_pci_probe(struct pci_dev *dev,
diff --git a/drivers/staging/comedi/drivers/adv_pci1723.c b/drivers/staging/comedi/drivers/adv_pci1723.c
index dfde0f6328dd..df4efc0606de 100644
--- a/drivers/staging/comedi/drivers/adv_pci1723.c
+++ b/drivers/staging/comedi/drivers/adv_pci1723.c
@@ -52,11 +52,6 @@ TODO:
#define PCI_VENDOR_ID_ADVANTECH 0x13fe /* Advantech PCI vendor ID */
-/* hardware types of the cards */
-#define TYPE_PCI1723 0
-
-#define IORANGE_1723 0x2A
-
/* all the registers for the pci1723 board */
#define PCI1723_DA(N) ((N)<<1) /* W: D/A register N (0 to 7) */
@@ -112,63 +107,18 @@ TODO:
#define PCI1723_SELECT_CALIBRATION 0x28 /* Select the calibration Ref_V */
-/* static unsigned short pci_list_builded=0; =1 list of card is know */
-
-static const struct comedi_lrange range_pci1723 = { 1, {
- BIP_RANGE(10)
- }
-};
-
-/*
- * Board descriptions for pci1723 boards.
- */
-struct pci1723_board {
- const char *name;
- int vendor_id; /* PCI vendor a device ID of card */
- int device_id;
- int iorange;
- char cardtype;
- int n_aochan; /* num of D/A chans */
- int n_diochan; /* num of DIO chans */
- int ao_maxdata; /* resolution of D/A */
- const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
-};
-
-static const struct pci1723_board boardtypes[] = {
- {
- .name = "pci1723",
- .vendor_id = PCI_VENDOR_ID_ADVANTECH,
- .device_id = 0x1723,
- .iorange = IORANGE_1723,
- .cardtype = TYPE_PCI1723,
- .n_aochan = 8,
- .n_diochan = 16,
- .ao_maxdata = 0xffff,
- .rangelist_ao = &range_pci1723,
- },
-};
-
-/* This structure is for data unique to this hardware driver. */
struct pci1723_private {
- int valid; /* card is usable; */
-
unsigned char da_range[8]; /* D/A output range for each channel */
-
short ao_data[8]; /* data output buffer */
};
-/* The following macro to make it easy to access the private structure. */
-#define devpriv ((struct pci1723_private *)dev->private)
-
-#define this_board boardtypes
-
/*
* The pci1723 card reset;
*/
static int pci1723_reset(struct comedi_device *dev)
{
+ struct pci1723_private *devpriv = dev->private;
int i;
- DPRINTK("adv_pci1723 EDBG: BGN: pci1723_reset(...)\n");
outw(0x01, dev->iobase + PCI1723_SYN_SET);
/* set synchronous output mode */
@@ -190,7 +140,6 @@ static int pci1723_reset(struct comedi_device *dev)
/* set asynchronous output mode */
outw(0, dev->iobase + PCI1723_SYN_SET);
- DPRINTK("adv_pci1723 EDBG: END: pci1723_reset(...)\n");
return 0;
}
@@ -198,10 +147,10 @@ static int pci1723_insn_read_ao(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct pci1723_private *devpriv = dev->private;
int n, chan;
chan = CR_CHAN(insn->chanspec);
- DPRINTK(" adv_PCI1723 DEBUG: pci1723_insn_read_ao() -----\n");
for (n = 0; n < insn->n; n++)
data[n] = devpriv->ao_data[chan];
@@ -215,11 +164,10 @@ static int pci1723_ao_write_winsn(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct pci1723_private *devpriv = dev->private;
int n, chan;
chan = CR_CHAN(insn->chanspec);
- DPRINTK("PCI1723: the pci1723_ao_write_winsn() ------\n");
-
for (n = 0; n < insn->n; n++) {
devpriv->ao_data[chan] = data[n];
@@ -286,124 +234,73 @@ static int pci1723_dio_insn_bits(struct comedi_device *dev,
return insn->n;
}
-static struct pci_dev *pci1723_find_pci_dev(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int pci1723_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
-
- for_each_pci_dev(pcidev) {
- if (bus || slot) {
- if (bus != pcidev->bus->number ||
- slot != PCI_SLOT(pcidev->devfn))
- continue;
- }
- if (pcidev->vendor != PCI_VENDOR_ID_ADVANTECH)
- continue;
- return pcidev;
- }
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
- return NULL;
-}
-
-static int pci1723_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev;
+ struct pci1723_private *devpriv;
struct comedi_subdevice *s;
- int ret, subdev, n_subdevices;
+ int ret;
- printk(KERN_ERR "comedi%d: adv_pci1723: board=%s",
- dev->minor, this_board->name);
-
- ret = alloc_private(dev, sizeof(struct pci1723_private));
- if (ret < 0) {
- printk(" - Allocation failed!\n");
- return -ENOMEM;
- }
-
- pcidev = pci1723_find_pci_dev(dev, it);
- if (!pcidev)
- return -EIO;
comedi_set_hw_dev(dev, &pcidev->dev);
+ dev->board_name = dev->driver->driver_name;
- ret = comedi_pci_enable(pcidev, "adv_pci1723");
- if (ret)
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret < 0)
return ret;
+ devpriv = dev->private;
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
dev->iobase = pci_resource_start(pcidev, 2);
- dev->board_name = this_board->name;
-
- n_subdevices = 0;
-
- if (this_board->n_aochan)
- n_subdevices++;
- if (this_board->n_diochan)
- n_subdevices++;
-
- ret = comedi_alloc_subdevices(dev, n_subdevices);
+ ret = comedi_alloc_subdevices(dev, 2);
if (ret)
return ret;
- pci1723_reset(dev);
- subdev = 0;
- if (this_board->n_aochan) {
- s = dev->subdevices + subdev;
- dev->write_subdev = s;
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
- s->n_chan = this_board->n_aochan;
- s->maxdata = this_board->ao_maxdata;
- s->len_chanlist = this_board->n_aochan;
- s->range_table = this_board->rangelist_ao;
-
- s->insn_write = pci1723_ao_write_winsn;
- s->insn_read = pci1723_insn_read_ao;
-
- /* read DIO config */
- switch (inw(dev->iobase + PCI1723_DIGITAL_IO_PORT_MODE)
- & 0x03) {
- case 0x00: /* low byte output, high byte output */
- s->io_bits = 0xFFFF;
- break;
- case 0x01: /* low byte input, high byte output */
- s->io_bits = 0xFF00;
- break;
- case 0x02: /* low byte output, high byte input */
- s->io_bits = 0x00FF;
- break;
- case 0x03: /* low byte input, high byte input */
- s->io_bits = 0x0000;
- break;
- }
- /* read DIO port state */
- s->state = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA);
-
- subdev++;
- }
-
- if (this_board->n_diochan) {
- s = dev->subdevices + subdev;
- s->type = COMEDI_SUBD_DIO;
- s->subdev_flags =
- SDF_READABLE | SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
- s->n_chan = this_board->n_diochan;
- s->maxdata = 1;
- s->len_chanlist = this_board->n_diochan;
- s->range_table = &range_digital;
- s->insn_config = pci1723_dio_insn_config;
- s->insn_bits = pci1723_dio_insn_bits;
- subdev++;
+ s = &dev->subdevices[0];
+ dev->write_subdev = s;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITEABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 8;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 8;
+ s->range_table = &range_bipolar10;
+ s->insn_write = pci1723_ao_write_winsn;
+ s->insn_read = pci1723_insn_read_ao;
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->len_chanlist = 16;
+ s->range_table = &range_digital;
+ s->insn_config = pci1723_dio_insn_config;
+ s->insn_bits = pci1723_dio_insn_bits;
+
+ /* read DIO config */
+ switch (inw(dev->iobase + PCI1723_DIGITAL_IO_PORT_MODE) & 0x03) {
+ case 0x00: /* low byte output, high byte output */
+ s->io_bits = 0xFFFF;
+ break;
+ case 0x01: /* low byte input, high byte output */
+ s->io_bits = 0xFF00;
+ break;
+ case 0x02: /* low byte output, high byte input */
+ s->io_bits = 0x00FF;
+ break;
+ case 0x03: /* low byte input, high byte input */
+ s->io_bits = 0x0000;
+ break;
}
-
- devpriv->valid = 1;
+ /* read DIO port state */
+ s->state = inw(dev->iobase + PCI1723_READ_DIGITAL_INPUT_DATA);
pci1723_reset(dev);
+ dev_info(dev->class_dev, "%s attached\n", dev->board_name);
+
return 0;
}
@@ -411,21 +308,18 @@ static void pci1723_detach(struct comedi_device *dev)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
- if (dev->private) {
- if (devpriv->valid)
- pci1723_reset(dev);
- }
if (pcidev) {
- if (dev->iobase)
+ if (dev->iobase) {
+ pci1723_reset(dev);
comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
+ }
}
}
static struct comedi_driver adv_pci1723_driver = {
.driver_name = "adv_pci1723",
.module = THIS_MODULE,
- .attach = pci1723_attach,
+ .attach_pci = pci1723_attach_pci,
.detach = pci1723_detach,
};
diff --git a/drivers/staging/comedi/drivers/adv_pci_dio.c b/drivers/staging/comedi/drivers/adv_pci_dio.c
index 2d4cb7f638b2..a3c22419cd5f 100644
--- a/drivers/staging/comedi/drivers/adv_pci_dio.c
+++ b/drivers/staging/comedi/drivers/adv_pci_dio.c
@@ -36,15 +36,6 @@ Configuration options:
#include "8255.h"
#include "8253.h"
-#undef PCI_DIO_EXTDEBUG /* if defined, enable extensive debug logging */
-
-#undef DPRINTK
-#ifdef PCI_DIO_EXTDEBUG
-#define DPRINTK(fmt, args...) printk(fmt, ## args)
-#else
-#define DPRINTK(fmt, args...)
-#endif
-
#define PCI_VENDOR_ID_ADVANTECH 0x13fe
/* hardware types of the cards */
@@ -250,6 +241,7 @@ struct dio_boardtype {
int device_id;
int main_pci_region; /* main I/O PCI region */
enum hw_cards_id cardtype;
+ int nsubdevs;
struct diosubd_data sdi[MAX_DI_SUBDEVS]; /* DI chans */
struct diosubd_data sdo[MAX_DO_SUBDEVS]; /* DO chans */
struct diosubd_data sdio[MAX_DIO_SUBDEVG]; /* DIO 8255 chans */
@@ -259,126 +251,164 @@ struct dio_boardtype {
};
static const struct dio_boardtype boardtypes[] = {
- {"pci1730", PCI_VENDOR_ID_ADVANTECH, 0x1730, PCIDIO_MAINREG,
- TYPE_PCI1730,
- { {16, PCI1730_DI, 2, 0}, {16, PCI1730_IDI, 2, 0} },
- { {16, PCI1730_DO, 2, 0}, {16, PCI1730_IDO, 2, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- {4, PCI173x_BOARDID, 1, SDF_INTERNAL},
- { {0, 0, 0, 0} },
- IO_8b},
- {"pci1733", PCI_VENDOR_ID_ADVANTECH, 0x1733, PCIDIO_MAINREG,
- TYPE_PCI1733,
- { {0, 0, 0, 0}, {32, PCI1733_IDI, 4, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- {4, PCI173x_BOARDID, 1, SDF_INTERNAL},
- { {0, 0, 0, 0} },
- IO_8b},
- {"pci1734", PCI_VENDOR_ID_ADVANTECH, 0x1734, PCIDIO_MAINREG,
- TYPE_PCI1734,
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {0, 0, 0, 0}, {32, PCI1734_IDO, 4, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- {4, PCI173x_BOARDID, 1, SDF_INTERNAL},
- { {0, 0, 0, 0} },
- IO_8b},
- {"pci1735", PCI_VENDOR_ID_ADVANTECH, 0x1735, PCIDIO_MAINREG,
- TYPE_PCI1735,
- { {32, PCI1735_DI, 4, 0}, {0, 0, 0, 0} },
- { {32, PCI1735_DO, 4, 0}, {0, 0, 0, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { 4, PCI1735_BOARDID, 1, SDF_INTERNAL},
- { {3, PCI1735_C8254, 1, 0} },
- IO_8b},
- {"pci1736", PCI_VENDOR_ID_ADVANTECH, 0x1736, PCI1736_MAINREG,
- TYPE_PCI1736,
- { {0, 0, 0, 0}, {16, PCI1736_IDI, 2, 0} },
- { {0, 0, 0, 0}, {16, PCI1736_IDO, 2, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- {4, PCI1736_BOARDID, 1, SDF_INTERNAL},
- { {0, 0, 0, 0} },
- IO_8b},
- {"pci1739", PCI_VENDOR_ID_ADVANTECH, 0x1739, PCIDIO_MAINREG,
- TYPE_PCI1739,
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {48, PCI1739_DIO, 2, 0}, {0, 0, 0, 0} },
- {0, 0, 0, 0},
- { {0, 0, 0, 0} },
- IO_8b},
- {"pci1750", PCI_VENDOR_ID_ADVANTECH, 0x1750, PCIDIO_MAINREG,
- TYPE_PCI1750,
- { {0, 0, 0, 0}, {16, PCI1750_IDI, 2, 0} },
- { {0, 0, 0, 0}, {16, PCI1750_IDO, 2, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- {0, 0, 0, 0},
- { {0, 0, 0, 0} },
- IO_8b},
- {"pci1751", PCI_VENDOR_ID_ADVANTECH, 0x1751, PCIDIO_MAINREG,
- TYPE_PCI1751,
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {48, PCI1751_DIO, 2, 0}, {0, 0, 0, 0} },
- {0, 0, 0, 0},
- { {3, PCI1751_CNT, 1, 0} },
- IO_8b},
- {"pci1752", PCI_VENDOR_ID_ADVANTECH, 0x1752, PCIDIO_MAINREG,
- TYPE_PCI1752,
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {32, PCI1752_IDO, 2, 0}, {32, PCI1752_IDO2, 2, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- {4, PCI175x_BOARDID, 1, SDF_INTERNAL},
- { {0, 0, 0, 0} },
- IO_16b},
- {"pci1753", PCI_VENDOR_ID_ADVANTECH, 0x1753, PCIDIO_MAINREG,
- TYPE_PCI1753,
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {96, PCI1753_DIO, 4, 0}, {0, 0, 0, 0} },
- {0, 0, 0, 0},
- { {0, 0, 0, 0} },
- IO_8b},
- {"pci1753e", PCI_VENDOR_ID_ADVANTECH, 0x1753, PCIDIO_MAINREG,
- TYPE_PCI1753E,
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {96, PCI1753_DIO, 4, 0}, {96, PCI1753E_DIO, 4, 0} },
- {0, 0, 0, 0},
- { {0, 0, 0, 0} },
- IO_8b},
- {"pci1754", PCI_VENDOR_ID_ADVANTECH, 0x1754, PCIDIO_MAINREG,
- TYPE_PCI1754,
- { {32, PCI1754_IDI, 2, 0}, {32, PCI1754_IDI2, 2, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- {4, PCI175x_BOARDID, 1, SDF_INTERNAL},
- { {0, 0, 0, 0} },
- IO_16b},
- {"pci1756", PCI_VENDOR_ID_ADVANTECH, 0x1756, PCIDIO_MAINREG,
- TYPE_PCI1756,
- { {0, 0, 0, 0}, {32, PCI1756_IDI, 2, 0} },
- { {0, 0, 0, 0}, {32, PCI1756_IDO, 2, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- {4, PCI175x_BOARDID, 1, SDF_INTERNAL},
- { {0, 0, 0, 0} },
- IO_16b},
- {"pci1760", PCI_VENDOR_ID_ADVANTECH, 0x1760, 0,
- TYPE_PCI1760,
- { {0, 0, 0, 0}, {0, 0, 0, 0} }, /* This card have own setup work */
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- {0, 0, 0, 0},
- { {0, 0, 0, 0} },
- IO_8b},
- {"pci1762", PCI_VENDOR_ID_ADVANTECH, 0x1762, PCIDIO_MAINREG,
- TYPE_PCI1762,
- { {0, 0, 0, 0}, {16, PCI1762_IDI, 1, 0} },
- { {0, 0, 0, 0}, {16, PCI1762_RO, 1, 0} },
- { {0, 0, 0, 0}, {0, 0, 0, 0} },
- {4, PCI1762_BOARDID, 1, SDF_INTERNAL},
- { {0, 0, 0, 0} },
- IO_16b}
+ {
+ .name = "pci1730",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1730,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1730,
+ .nsubdevs = 5,
+ .sdi[0] = { 16, PCI1730_DI, 2, 0, },
+ .sdi[1] = { 16, PCI1730_IDI, 2, 0, },
+ .sdo[0] = { 16, PCI1730_DO, 2, 0, },
+ .sdo[1] = { 16, PCI1730_IDO, 2, 0, },
+ .boardid = { 4, PCI173x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_8b,
+ }, {
+ .name = "pci1733",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1733,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1733,
+ .nsubdevs = 2,
+ .sdi[1] = { 32, PCI1733_IDI, 4, 0, },
+ .boardid = { 4, PCI173x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_8b,
+ }, {
+ .name = "pci1734",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1734,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1734,
+ .nsubdevs = 2,
+ .sdo[1] = { 32, PCI1734_IDO, 4, 0, },
+ .boardid = { 4, PCI173x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_8b,
+ }, {
+ .name = "pci1735",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1735,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1735,
+ .nsubdevs = 4,
+ .sdi[0] = { 32, PCI1735_DI, 4, 0, },
+ .sdo[0] = { 32, PCI1735_DO, 4, 0, },
+ .boardid = { 4, PCI1735_BOARDID, 1, SDF_INTERNAL, },
+ .s8254[0] = { 3, PCI1735_C8254, 1, 0, },
+ .io_access = IO_8b,
+ }, {
+ .name = "pci1736",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1736,
+ .main_pci_region = PCI1736_MAINREG,
+ .cardtype = TYPE_PCI1736,
+ .nsubdevs = 3,
+ .sdi[1] = { 16, PCI1736_IDI, 2, 0, },
+ .sdo[1] = { 16, PCI1736_IDO, 2, 0, },
+ .boardid = { 4, PCI1736_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_8b,
+ }, {
+ .name = "pci1739",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1739,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1739,
+ .nsubdevs = 2,
+ .sdio[0] = { 48, PCI1739_DIO, 2, 0, },
+ .io_access = IO_8b,
+ }, {
+ .name = "pci1750",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1750,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1750,
+ .nsubdevs = 2,
+ .sdi[1] = { 16, PCI1750_IDI, 2, 0, },
+ .sdo[1] = { 16, PCI1750_IDO, 2, 0, },
+ .io_access = IO_8b,
+ }, {
+ .name = "pci1751",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1751,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1751,
+ .nsubdevs = 3,
+ .sdio[0] = { 48, PCI1751_DIO, 2, 0, },
+ .s8254[0] = { 3, PCI1751_CNT, 1, 0, },
+ .io_access = IO_8b,
+ }, {
+ .name = "pci1752",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1752,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1752,
+ .nsubdevs = 3,
+ .sdo[0] = { 32, PCI1752_IDO, 2, 0, },
+ .sdo[1] = { 32, PCI1752_IDO2, 2, 0, },
+ .boardid = { 4, PCI175x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_16b,
+ }, {
+ .name = "pci1753",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1753,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1753,
+ .nsubdevs = 4,
+ .sdio[0] = { 96, PCI1753_DIO, 4, 0, },
+ .io_access = IO_8b,
+ }, {
+ .name = "pci1753e",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1753,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1753E,
+ .nsubdevs = 8,
+ .sdio[0] = { 96, PCI1753_DIO, 4, 0, },
+ .sdio[1] = { 96, PCI1753E_DIO, 4, 0, },
+ .io_access = IO_8b,
+ }, {
+ .name = "pci1754",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1754,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1754,
+ .nsubdevs = 3,
+ .sdi[0] = { 32, PCI1754_IDI, 2, 0, },
+ .sdi[1] = { 32, PCI1754_IDI2, 2, 0, },
+ .boardid = { 4, PCI175x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_16b,
+ }, {
+ .name = "pci1756",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1756,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1756,
+ .nsubdevs = 3,
+ .sdi[1] = { 32, PCI1756_IDI, 2, 0, },
+ .sdo[1] = { 32, PCI1756_IDO, 2, 0, },
+ .boardid = { 4, PCI175x_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_16b,
+ }, {
+ /* This card has its own 'attach' */
+ .name = "pci1760",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1760,
+ .main_pci_region = 0,
+ .cardtype = TYPE_PCI1760,
+ .nsubdevs = 4,
+ .io_access = IO_8b,
+ }, {
+ .name = "pci1762",
+ .vendor_id = PCI_VENDOR_ID_ADVANTECH,
+ .device_id = 0x1762,
+ .main_pci_region = PCIDIO_MAINREG,
+ .cardtype = TYPE_PCI1762,
+ .nsubdevs = 3,
+ .sdi[1] = { 16, PCI1762_IDI, 1, 0, },
+ .sdo[1] = { 16, PCI1762_RO, 1, 0, },
+ .boardid = { 4, PCI1762_BOARDID, 1, SDF_INTERNAL, },
+ .io_access = IO_16b,
+ },
};
struct pci_dio_private {
@@ -401,9 +431,6 @@ struct pci_dio_private {
unsigned short IDIFiltrHigh[8]; /* IDI's filter value high signal */
};
-#define devpriv ((struct pci_dio_private *)dev->private)
-#define this_board ((const struct dio_boardtype *)dev->board_ptr)
-
/*
==============================================================================
*/
@@ -694,6 +721,7 @@ static int pci1760_insn_cnt_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct pci_dio_private *devpriv = dev->private;
int ret;
unsigned char chan = CR_CHAN(insn->chanspec) & 0x07;
unsigned char bitmask = 1 << chan;
@@ -736,6 +764,7 @@ static int pci1760_insn_cnt_write(struct comedi_device *dev,
*/
static int pci1760_reset(struct comedi_device *dev)
{
+ struct pci_dio_private *devpriv = dev->private;
int i;
unsigned char omb[4] = { 0x00, 0x00, 0x00, 0x00 };
unsigned char imb[4];
@@ -816,7 +845,7 @@ static int pci1760_reset(struct comedi_device *dev)
*/
static int pci_dio_reset(struct comedi_device *dev)
{
- DPRINTK("adv_pci_dio EDBG: BGN: pci171x_reset(...)\n");
+ const struct dio_boardtype *this_board = comedi_board(dev);
switch (this_board->cardtype) {
case TYPE_PCI1730:
@@ -917,21 +946,17 @@ static int pci_dio_reset(struct comedi_device *dev)
break;
}
- DPRINTK("adv_pci_dio EDBG: END: pci171x_reset(...)\n");
-
return 0;
}
/*
==============================================================================
*/
-static int pci1760_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int pci1760_attach(struct comedi_device *dev)
{
struct comedi_subdevice *s;
- int subdev = 0;
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = 8;
@@ -939,9 +964,8 @@ static int pci1760_attach(struct comedi_device *dev,
s->len_chanlist = 8;
s->range_table = &range_digital;
s->insn_bits = pci1760_insn_bits_di;
- subdev++;
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
s->n_chan = 8;
@@ -950,18 +974,16 @@ static int pci1760_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->state = 0;
s->insn_bits = pci1760_insn_bits_do;
- subdev++;
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_TIMER;
s->subdev_flags = SDF_WRITABLE | SDF_LSAMPL;
s->n_chan = 2;
s->maxdata = 0xffffffff;
s->len_chanlist = 2;
/* s->insn_config=pci1760_insn_pwm_cfg; */
- subdev++;
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 8;
@@ -970,7 +992,6 @@ static int pci1760_attach(struct comedi_device *dev,
s->insn_read = pci1760_insn_cnt_read;
s->insn_write = pci1760_insn_cnt_write;
/* s->insn_config=pci1760_insn_cnt_cfg; */
- subdev++;
return 0;
}
@@ -978,9 +999,12 @@ static int pci1760_attach(struct comedi_device *dev,
/*
==============================================================================
*/
-static int pci_dio_add_di(struct comedi_device *dev, struct comedi_subdevice *s,
- const struct diosubd_data *d, int subdev)
+static int pci_dio_add_di(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ const struct diosubd_data *d)
{
+ const struct dio_boardtype *this_board = comedi_board(dev);
+
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_COMMON | d->specflags;
if (d->chans > 16)
@@ -1005,9 +1029,12 @@ static int pci_dio_add_di(struct comedi_device *dev, struct comedi_subdevice *s,
/*
==============================================================================
*/
-static int pci_dio_add_do(struct comedi_device *dev, struct comedi_subdevice *s,
- const struct diosubd_data *d, int subdev)
+static int pci_dio_add_do(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ const struct diosubd_data *d)
{
+ const struct dio_boardtype *this_board = comedi_board(dev);
+
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
if (d->chans > 16)
@@ -1035,7 +1062,7 @@ static int pci_dio_add_do(struct comedi_device *dev, struct comedi_subdevice *s,
*/
static int pci_dio_add_8254(struct comedi_device *dev,
struct comedi_subdevice *s,
- const struct diosubd_data *d, int subdev)
+ const struct diosubd_data *d)
{
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
@@ -1050,101 +1077,69 @@ static int pci_dio_add_8254(struct comedi_device *dev,
return 0;
}
-static struct pci_dev *pci_dio_find_pci_dev(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static const void *pci_dio_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
+ const struct dio_boardtype *this_board;
int i;
- for_each_pci_dev(pcidev) {
- if (bus || slot) {
- if (bus != pcidev->bus->number ||
- slot != PCI_SLOT(pcidev->devfn))
- continue;
- }
- for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
- if (boardtypes[i].vendor_id != pcidev->vendor)
- continue;
- if (boardtypes[i].device_id != pcidev->device)
- continue;
- dev->board_ptr = boardtypes + i;
- return pcidev;
- }
+ for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
+ this_board = &boardtypes[i];
+ if (this_board->vendor_id == pcidev->vendor &&
+ this_board->device_id == pcidev->device)
+ return this_board;
}
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
return NULL;
}
-static int pci_dio_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int pci_dio_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- struct pci_dev *pcidev;
+ const struct dio_boardtype *this_board;
+ struct pci_dio_private *devpriv;
struct comedi_subdevice *s;
- int ret, subdev, n_subdevices, i, j;
+ int ret, subdev, i, j;
- ret = alloc_private(dev, sizeof(struct pci_dio_private));
- if (ret < 0)
- return -ENOMEM;
-
- pcidev = pci_dio_find_pci_dev(dev, it);
- if (!pcidev)
- return -EIO;
comedi_set_hw_dev(dev, &pcidev->dev);
- if (comedi_pci_enable(pcidev, dev->driver->driver_name)) {
- dev_err(dev->class_dev,
- "Error: Can't enable PCI device and request regions!\n");
- return -EIO;
- }
-
- dev->iobase = pci_resource_start(pcidev, this_board->main_pci_region);
+ this_board = pci_dio_find_boardinfo(dev, pcidev);
+ if (!this_board)
+ return -ENODEV;
+ dev->board_ptr = this_board;
dev->board_name = this_board->name;
- if (this_board->cardtype == TYPE_PCI1760) {
- n_subdevices = 4; /* 8 IDI, 8 IDO, 2 PWM, 8 CNT */
- } else {
- n_subdevices = 0;
- for (i = 0; i < MAX_DI_SUBDEVS; i++)
- if (this_board->sdi[i].chans)
- n_subdevices++;
- for (i = 0; i < MAX_DO_SUBDEVS; i++)
- if (this_board->sdo[i].chans)
- n_subdevices++;
- for (i = 0; i < MAX_DIO_SUBDEVG; i++)
- n_subdevices += this_board->sdio[i].regs;
- if (this_board->boardid.chans)
- n_subdevices++;
- for (i = 0; i < MAX_8254_SUBDEVS; i++)
- if (this_board->s8254[i].chans)
- n_subdevices++;
- }
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret < 0)
+ return ret;
+ devpriv = dev->private;
- ret = comedi_alloc_subdevices(dev, n_subdevices);
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, this_board->main_pci_region);
+
+ ret = comedi_alloc_subdevices(dev, this_board->nsubdevs);
if (ret)
return ret;
subdev = 0;
for (i = 0; i < MAX_DI_SUBDEVS; i++)
if (this_board->sdi[i].chans) {
- s = dev->subdevices + subdev;
- pci_dio_add_di(dev, s, &this_board->sdi[i], subdev);
+ s = &dev->subdevices[subdev];
+ pci_dio_add_di(dev, s, &this_board->sdi[i]);
subdev++;
}
for (i = 0; i < MAX_DO_SUBDEVS; i++)
if (this_board->sdo[i].chans) {
- s = dev->subdevices + subdev;
- pci_dio_add_do(dev, s, &this_board->sdo[i], subdev);
+ s = &dev->subdevices[subdev];
+ pci_dio_add_do(dev, s, &this_board->sdo[i]);
subdev++;
}
for (i = 0; i < MAX_DIO_SUBDEVG; i++)
for (j = 0; j < this_board->sdio[i].regs; j++) {
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[subdev];
subdev_8255_init(dev, s, NULL,
dev->iobase +
this_board->sdio[i].addr +
@@ -1153,21 +1148,21 @@ static int pci_dio_attach(struct comedi_device *dev,
}
if (this_board->boardid.chans) {
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[subdev];
s->type = COMEDI_SUBD_DI;
- pci_dio_add_di(dev, s, &this_board->boardid, subdev);
+ pci_dio_add_di(dev, s, &this_board->boardid);
subdev++;
}
for (i = 0; i < MAX_8254_SUBDEVS; i++)
if (this_board->s8254[i].chans) {
- s = dev->subdevices + subdev;
- pci_dio_add_8254(dev, s, &this_board->s8254[i], subdev);
+ s = &dev->subdevices[subdev];
+ pci_dio_add_8254(dev, s, &this_board->s8254[i]);
subdev++;
}
if (this_board->cardtype == TYPE_PCI1760)
- pci1760_attach(dev, it);
+ pci1760_attach(dev);
devpriv->valid = 1;
@@ -1178,52 +1173,34 @@ static int pci_dio_attach(struct comedi_device *dev,
static void pci_dio_detach(struct comedi_device *dev)
{
+ struct pci_dio_private *devpriv = dev->private;
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
- int i, j;
struct comedi_subdevice *s;
- int subdev;
+ int i;
- if (dev->private) {
+ if (devpriv) {
if (devpriv->valid)
pci_dio_reset(dev);
- subdev = 0;
- for (i = 0; i < MAX_DI_SUBDEVS; i++) {
- if (this_board->sdi[i].chans)
- subdev++;
- }
- for (i = 0; i < MAX_DO_SUBDEVS; i++) {
- if (this_board->sdo[i].chans)
- subdev++;
- }
- for (i = 0; i < MAX_DIO_SUBDEVG; i++) {
- for (j = 0; j < this_board->sdio[i].regs; j++) {
- s = dev->subdevices + subdev;
- subdev_8255_cleanup(dev, s);
- subdev++;
- }
- }
- if (this_board->boardid.chans)
- subdev++;
- for (i = 0; i < MAX_8254_SUBDEVS; i++)
- if (this_board->s8254[i].chans)
- subdev++;
+ }
+ if (dev->subdevices) {
for (i = 0; i < dev->n_subdevices; i++) {
- s = dev->subdevices + i;
+ s = &dev->subdevices[i];
+ if (s->type == COMEDI_SUBD_DIO)
+ subdev_8255_cleanup(dev, s);
s->private = NULL;
}
}
if (pcidev) {
if (dev->iobase)
comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
}
}
static struct comedi_driver adv_pci_dio_driver = {
.driver_name = "adv_pci_dio",
.module = THIS_MODULE,
- .attach = pci_dio_attach,
- .detach = pci_dio_detach
+ .attach_pci = pci_dio_attach_pci,
+ .detach = pci_dio_detach,
};
static int __devinit adv_pci_dio_pci_probe(struct pci_dev *dev,
diff --git a/drivers/staging/comedi/drivers/aio_aio12_8.c b/drivers/staging/comedi/drivers/aio_aio12_8.c
index f7d453f8fe33..8acf60d0f20d 100644
--- a/drivers/staging/comedi/drivers/aio_aio12_8.c
+++ b/drivers/staging/comedi/drivers/aio_aio12_8.c
@@ -25,8 +25,9 @@
Driver: aio_aio12_8
Description: Access I/O Products PC-104 AIO12-8 Analog I/O Board
Author: Pablo Mejia <pablo.mejia@cctechnol.com>
-Devices:
- [Access I/O] PC-104 AIO12-8
+Devices: [Access I/O] PC-104 AIO12-8 (aio_aio12_8)
+ [Access I/O] PC-104 AI12-8 (aio_ai12_8)
+ [Access I/O] PC-104 AO12-8 (aio_ao12_8)
Status: experimental
Configuration Options:
@@ -42,91 +43,118 @@ Notes:
#include <linux/ioport.h>
#include "8255.h"
-#define AIO12_8_STATUS 0x00
-#define AIO12_8_INTERRUPT 0x01
-#define AIO12_8_ADC 0x02
-#define AIO12_8_DAC_0 0x04
-#define AIO12_8_DAC_1 0x06
-#define AIO12_8_DAC_2 0x08
-#define AIO12_8_DAC_3 0x0A
-#define AIO12_8_COUNTER_0 0x0C
-#define AIO12_8_COUNTER_1 0x0D
-#define AIO12_8_COUNTER_2 0x0E
-#define AIO12_8_COUNTER_CONTROL 0x0F
-#define AIO12_8_DIO_0 0x10
-#define AIO12_8_DIO_1 0x11
-#define AIO12_8_DIO_2 0x12
-#define AIO12_8_DIO_STATUS 0x13
-#define AIO12_8_DIO_CONTROL 0x14
-#define AIO12_8_ADC_TRIGGER_CONTROL 0x15
-#define AIO12_8_TRIGGER 0x16
-#define AIO12_8_POWER 0x17
-
-#define STATUS_ADC_EOC 0x80
-
-#define ADC_MODE_NORMAL 0x00
-#define ADC_MODE_INTERNAL_CLOCK 0x40
-#define ADC_MODE_STANDBY 0x80
-#define ADC_MODE_POWERDOWN 0xC0
-
-#define DAC_ENABLE 0x18
+/*
+ * Register map
+ */
+#define AIO12_8_STATUS_REG 0x00
+#define AIO12_8_STATUS_ADC_EOC (1 << 7)
+#define AIO12_8_STATUS_PORT_C_COS (1 << 6)
+#define AIO12_8_STATUS_IRQ_ENA (1 << 2)
+#define AIO12_8_INTERRUPT_REG 0x01
+#define AIO12_8_INTERRUPT_ADC (1 << 7)
+#define AIO12_8_INTERRUPT_COS (1 << 6)
+#define AIO12_8_INTERRUPT_COUNTER1 (1 << 5)
+#define AIO12_8_INTERRUPT_PORT_C3 (1 << 4)
+#define AIO12_8_INTERRUPT_PORT_C0 (1 << 3)
+#define AIO12_8_INTERRUPT_ENA (1 << 2)
+#define AIO12_8_ADC_REG 0x02
+#define AIO12_8_ADC_MODE_NORMAL (0 << 6)
+#define AIO12_8_ADC_MODE_INT_CLK (1 << 6)
+#define AIO12_8_ADC_MODE_STANDBY (2 << 6)
+#define AIO12_8_ADC_MODE_POWERDOWN (3 << 6)
+#define AIO12_8_ADC_ACQ_3USEC (0 << 5)
+#define AIO12_8_ADC_ACQ_PROGRAM (1 << 5)
+#define AIO12_8_ADC_RANGE(x) ((x) << 3)
+#define AIO12_8_ADC_CHAN(x) ((x) << 0)
+#define AIO12_8_DAC_REG(x) (0x04 + (x) * 2)
+#define AIO12_8_8254_BASE_REG 0x0c
+#define AIO12_8_8255_BASE_REG 0x10
+#define AIO12_8_DIO_CONTROL_REG 0x14
+#define AIO12_8_DIO_CONTROL_TST (1 << 0)
+#define AIO12_8_ADC_TRIGGER_REG 0x15
+#define AIO12_8_ADC_TRIGGER_RANGE(x) ((x) << 3)
+#define AIO12_8_ADC_TRIGGER_CHAN(x) ((x) << 0)
+#define AIO12_8_TRIGGER_REG 0x16
+#define AIO12_8_TRIGGER_ADTRIG (1 << 1)
+#define AIO12_8_TRIGGER_DACTRIG (1 << 0)
+#define AIO12_8_COS_REG 0x17
+#define AIO12_8_DAC_ENABLE_REG 0x18
+#define AIO12_8_DAC_ENABLE_REF_ENA (1 << 0)
struct aio12_8_boardtype {
const char *name;
+ int ai_nchan;
+ int ao_nchan;
};
static const struct aio12_8_boardtype board_types[] = {
{
- .name = "aio_aio12_8"},
+ .name = "aio_aio12_8",
+ .ai_nchan = 8,
+ .ao_nchan = 4,
+ }, {
+ .name = "aio_ai12_8",
+ .ai_nchan = 8,
+ }, {
+ .name = "aio_ao12_8",
+ .ao_nchan = 4,
+ },
};
struct aio12_8_private {
unsigned int ao_readback[4];
};
-#define devpriv ((struct aio12_8_private *) dev->private)
-
static int aio_aio12_8_ai_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned int range = CR_RANGE(insn->chanspec);
+ unsigned int val;
+ unsigned char control;
int n;
- unsigned char control =
- ADC_MODE_NORMAL |
- (CR_RANGE(insn->chanspec) << 3) | CR_CHAN(insn->chanspec);
- /* read status to clear EOC latch */
- inb(dev->iobase + AIO12_8_STATUS);
+ /*
+ * Setup the control byte for internal 2MHz clock, 3uS conversion,
+ * at the desired range of the requested channel.
+ */
+ control = AIO12_8_ADC_MODE_NORMAL | AIO12_8_ADC_ACQ_3USEC |
+ AIO12_8_ADC_RANGE(range) | AIO12_8_ADC_CHAN(chan);
+
+ /* Read status to clear EOC latch */
+ inb(dev->iobase + AIO12_8_STATUS_REG);
for (n = 0; n < insn->n; n++) {
int timeout = 5;
/* Setup and start conversion */
- outb(control, dev->iobase + AIO12_8_ADC);
+ outb(control, dev->iobase + AIO12_8_ADC_REG);
/* Wait for conversion to complete */
- while (timeout &&
- !(inb(dev->iobase + AIO12_8_STATUS) & STATUS_ADC_EOC)) {
+ do {
+ val = inb(dev->iobase + AIO12_8_STATUS_REG);
timeout--;
- printk(KERN_ERR "timeout %d\n", timeout);
- udelay(1);
- }
- if (timeout == 0) {
- comedi_error(dev, "ADC timeout");
- return -EIO;
- }
-
- data[n] = inw(dev->iobase + AIO12_8_ADC) & 0x0FFF;
+ if (timeout == 0) {
+ dev_err(dev->class_dev, "ADC timeout\n");
+ return -ETIMEDOUT;
+ }
+ } while (!(val & AIO12_8_STATUS_ADC_EOC));
+
+ data[n] = inw(dev->iobase + AIO12_8_ADC_REG) & s->maxdata;
}
- return n;
+
+ return insn->n;
}
static int aio_aio12_8_ao_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct aio12_8_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ int val = devpriv->ao_readback[chan];
int i;
- int val = devpriv->ao_readback[CR_CHAN(insn->chanspec)];
for (i = 0; i < insn->n; i++)
data[i] = val;
@@ -137,18 +165,22 @@ static int aio_aio12_8_ao_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct aio12_8_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long port = dev->iobase + AIO12_8_DAC_REG(chan);
+ unsigned int val = 0;
int i;
- int chan = CR_CHAN(insn->chanspec);
- unsigned long port = dev->iobase + AIO12_8_DAC_0 + (2 * chan);
/* enable DACs */
- outb(0x01, dev->iobase + DAC_ENABLE);
+ outb(AIO12_8_DAC_ENABLE_REF_ENA, dev->iobase + AIO12_8_DAC_ENABLE_REG);
for (i = 0; i < insn->n; i++) {
- outb(data[i] & 0xFF, port); /* LSB */
- outb((data[i] >> 8) & 0x0F, port + 1); /* MSB */
- devpriv->ao_readback[chan] = data[i];
+ val = data[i];
+ outw(val, port);
}
+
+ devpriv->ao_readback[chan] = val;
+
return insn->n;
}
@@ -166,53 +198,77 @@ static int aio_aio12_8_attach(struct comedi_device *dev,
struct comedi_devconfig *it)
{
const struct aio12_8_boardtype *board = comedi_board(dev);
- int iobase;
+ struct aio12_8_private *devpriv;
struct comedi_subdevice *s;
+ int iobase;
int ret;
+ dev->board_name = board->name;
+
iobase = it->options[0];
- if (!request_region(iobase, 24, "aio_aio12_8")) {
+ if (!request_region(iobase, 32, dev->board_name)) {
printk(KERN_ERR "I/O port conflict");
return -EIO;
}
-
- dev->board_name = board->name;
-
dev->iobase = iobase;
- if (alloc_private(dev, sizeof(struct aio12_8_private)) < 0)
- return -ENOMEM;
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret)
+ return ret;
+ devpriv = dev->private;
- ret = comedi_alloc_subdevices(dev, 3);
+ ret = comedi_alloc_subdevices(dev, 4);
if (ret)
return ret;
s = &dev->subdevices[0];
- s->type = COMEDI_SUBD_AI;
- s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
- s->n_chan = 8;
- s->maxdata = (1 << 12) - 1;
- s->range_table = &range_aio_aio12_8;
- s->insn_read = aio_aio12_8_ai_read;
+ if (board->ai_nchan) {
+ /* Analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = board->ai_nchan;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_aio_aio12_8;
+ s->insn_read = aio_aio12_8_ai_read;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
s = &dev->subdevices[1];
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_DIFF;
- s->n_chan = 4;
- s->maxdata = (1 << 12) - 1;
- s->range_table = &range_aio_aio12_8;
- s->insn_read = aio_aio12_8_ao_read;
- s->insn_write = aio_aio12_8_ao_write;
+ if (board->ao_nchan) {
+ /* Analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 4;
+ s->maxdata = 0x0fff;
+ s->range_table = &range_aio_aio12_8;
+ s->insn_read = aio_aio12_8_ao_read;
+ s->insn_write = aio_aio12_8_ao_write;
+ } else {
+ s->type = COMEDI_SUBD_UNUSED;
+ }
s = &dev->subdevices[2];
- subdev_8255_init(dev, s, NULL, dev->iobase + AIO12_8_DIO_0);
+ /* 8255 Digital i/o subdevice */
+ iobase = dev->iobase + AIO12_8_8255_BASE_REG;
+ ret = subdev_8255_init(dev, s, NULL, iobase);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[3];
+ /* 8254 counter/timer subdevice */
+ s->type = COMEDI_SUBD_UNUSED;
+
+ dev_info(dev->class_dev, "%s: %s attached\n",
+ dev->driver->driver_name, dev->board_name);
return 0;
}
static void aio_aio12_8_detach(struct comedi_device *dev)
{
- subdev_8255_cleanup(dev, &dev->subdevices[2]);
+ if (dev->subdevices)
+ subdev_8255_cleanup(dev, &dev->subdevices[2]);
if (dev->iobase)
release_region(dev->iobase, 24);
}
diff --git a/drivers/staging/comedi/drivers/aio_iiro_16.c b/drivers/staging/comedi/drivers/aio_iiro_16.c
index ba1e3bbf2df8..b2cb8b02b2a1 100644
--- a/drivers/staging/comedi/drivers/aio_iiro_16.c
+++ b/drivers/staging/comedi/drivers/aio_iiro_16.c
@@ -112,7 +112,7 @@ static int aio_iiro_16_attach(struct comedi_device *dev,
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_WRITABLE;
s->n_chan = 16;
@@ -120,7 +120,7 @@ static int aio_iiro_16_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->insn_bits = aio_iiro_16_dio_insn_bits_write;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE;
s->n_chan = 16;
diff --git a/drivers/staging/comedi/drivers/amplc_dio200.c b/drivers/staging/comedi/drivers/amplc_dio200.c
index cc8931fde839..08f305210a69 100644
--- a/drivers/staging/comedi/drivers/amplc_dio200.c
+++ b/drivers/staging/comedi/drivers/amplc_dio200.c
@@ -210,11 +210,15 @@ order they appear in the channel list.
#include "../comedidev.h"
+#include "comedi_fc.h"
#include "8255.h"
#include "8253.h"
#define DIO200_DRIVER_NAME "amplc_dio200"
+#define DO_ISA IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_ISA)
+#define DO_PCI IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_PCI)
+
/* PCI IDs */
#define PCI_VENDOR_ID_AMPLICON 0x14dc
#define PCI_DEVICE_ID_AMPLICON_PCI272 0x000a
@@ -272,12 +276,12 @@ enum dio200_model {
};
enum dio200_layout {
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_ISA)
+#if DO_ISA
pc212_layout,
pc214_layout,
#endif
pc215_layout,
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_ISA)
+#if DO_ISA
pc218_layout,
#endif
pc272_layout
@@ -292,7 +296,7 @@ struct dio200_board {
};
static const struct dio200_board dio200_boards[] = {
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_ISA)
+#if DO_ISA
{
.name = "pc212e",
.bustype = isa_bustype,
@@ -324,7 +328,7 @@ static const struct dio200_board dio200_boards[] = {
.layout = pc272_layout,
},
#endif
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_PCI)
+#if DO_PCI
{
.name = "pci215",
.devid = PCI_DEVICE_ID_AMPLICON_PCI215,
@@ -367,7 +371,7 @@ struct dio200_layout_struct {
};
static const struct dio200_layout_struct dio200_layouts[] = {
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_ISA)
+#if DO_ISA
[pc212_layout] = {
.n_subdevs = 6,
.sdtype = {sd_8255, sd_8254, sd_8254, sd_8254,
@@ -396,7 +400,7 @@ static const struct dio200_layout_struct dio200_layouts[] = {
.has_int_sce = 1,
.has_clk_gat_sce = 1,
},
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_ISA)
+#if DO_ISA
[pc218_layout] = {
.n_subdevs = 7,
.sdtype = {sd_8254, sd_8254, sd_8255, sd_8254,
@@ -449,6 +453,16 @@ struct dio200_subdev_intr {
int continuous;
};
+static inline bool is_pci_board(const struct dio200_board *board)
+{
+ return DO_PCI && board->bustype == pci_bustype;
+}
+
+static inline bool is_isa_board(const struct dio200_board *board)
+{
+ return DO_ISA && board->bustype == isa_bustype;
+}
+
/*
* This function looks for a board matching the supplied PCI device.
*/
@@ -458,7 +472,7 @@ dio200_find_pci_board(struct pci_dev *pci_dev)
unsigned int i;
for (i = 0; i < ARRAY_SIZE(dio200_boards); i++)
- if (dio200_boards[i].bustype == pci_bustype &&
+ if (is_pci_board(&dio200_boards[i]) &&
pci_dev->device == dio200_boards[i].devid)
return &dio200_boards[i];
return NULL;
@@ -758,52 +772,24 @@ dio200_subdev_intr_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
int err = 0;
- unsigned int tmp;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= (TRIG_NOW | TRIG_INT);
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= (TRIG_COUNT | TRIG_NONE);
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually
- compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* these tests are true if more than one _src bit is set */
- if ((cmd->start_src & (cmd->start_src - 1)) != 0)
- err++;
- if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
- err++;
- if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
- err++;
- if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
- err++;
- if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -965,15 +951,15 @@ static irqreturn_t dio200_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
struct dio200_private *devpriv = dev->private;
+ struct comedi_subdevice *s;
int handled;
if (!dev->attached)
return IRQ_NONE;
if (devpriv->intr_sd >= 0) {
- handled = dio200_handle_read_intr(dev,
- dev->subdevices +
- devpriv->intr_sd);
+ s = &dev->subdevices[devpriv->intr_sd];
+ handled = dio200_handle_read_intr(dev, s);
} else {
handled = 0;
}
@@ -1228,12 +1214,10 @@ static void dio200_report_attach(struct comedi_device *dev, unsigned int irq)
char tmpbuf[60];
int tmplen;
- if (IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_ISA) &&
- thisboard->bustype == isa_bustype)
+ if (is_isa_board(thisboard))
tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
"(base %#lx) ", dev->iobase);
- else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_PCI) &&
- thisboard->bustype == pci_bustype)
+ else if (is_pci_board(thisboard))
tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
"(pci %s) ", pci_name(pcidev));
else
@@ -1361,8 +1345,7 @@ static int dio200_attach(struct comedi_device *dev, struct comedi_devconfig *it)
}
/* Process options and reserve resources according to bus type. */
- if (IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_ISA) &&
- thisboard->bustype == isa_bustype) {
+ if (is_isa_board(thisboard)) {
unsigned long iobase;
unsigned int irq;
@@ -1372,8 +1355,7 @@ static int dio200_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret < 0)
return ret;
return dio200_common_attach(dev, iobase, irq, 0);
- } else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_PCI) &&
- thisboard->bustype == pci_bustype) {
+ } else if (is_pci_board(thisboard)) {
struct pci_dev *pci_dev;
pci_dev = dio200_find_pci_dev(dev, it);
@@ -1397,7 +1379,7 @@ static int __devinit dio200_attach_pci(struct comedi_device *dev,
{
int ret;
- if (!IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_PCI))
+ if (!DO_PCI)
return -EINVAL;
dev_info(dev->class_dev, DIO200_DRIVER_NAME ": attach pci %s\n",
@@ -1425,7 +1407,6 @@ static int __devinit dio200_attach_pci(struct comedi_device *dev,
static void dio200_detach(struct comedi_device *dev)
{
const struct dio200_board *thisboard = comedi_board(dev);
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
const struct dio200_layout_struct *layout;
unsigned n;
@@ -1450,13 +1431,16 @@ static void dio200_detach(struct comedi_device *dev)
}
}
}
- if (pcidev) {
- if (dev->iobase)
- comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
- } else {
+ if (is_isa_board(thisboard)) {
if (dev->iobase)
release_region(dev->iobase, DIO200_IO_SIZE);
+ } else if (is_pci_board(thisboard)) {
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ if (pcidev) {
+ if (dev->iobase)
+ comedi_pci_disable(pcidev);
+ pci_dev_put(pcidev);
+ }
}
}
@@ -1477,7 +1461,7 @@ static struct comedi_driver amplc_dio200_driver = {
.num_names = ARRAY_SIZE(dio200_boards),
};
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_DIO200_PCI)
+#if DO_PCI
static DEFINE_PCI_DEVICE_TABLE(dio200_pci_table) = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI215) },
{ PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI272) },
diff --git a/drivers/staging/comedi/drivers/amplc_pc236.c b/drivers/staging/comedi/drivers/amplc_pc236.c
index f50287903038..eacb5e4735d7 100644
--- a/drivers/staging/comedi/drivers/amplc_pc236.c
+++ b/drivers/staging/comedi/drivers/amplc_pc236.c
@@ -56,11 +56,15 @@ unused.
#include "../comedidev.h"
+#include "comedi_fc.h"
#include "8255.h"
#include "plx9052.h"
#define PC236_DRIVER_NAME "amplc_pc236"
+#define DO_ISA IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
+#define DO_PCI IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
+
/* PCI236 PCI configuration register information */
#define PCI_VENDOR_ID_AMPLICON 0x14dc
#define PCI_DEVICE_ID_AMPLICON_PCI236 0x0009
@@ -103,14 +107,14 @@ struct pc236_board {
enum pc236_model model;
};
static const struct pc236_board pc236_boards[] = {
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA)
+#if DO_ISA
{
.name = "pc36at",
.bustype = isa_bustype,
.model = pc36at_model,
},
#endif
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
+#if DO_PCI
{
.name = "pci236",
.devid = PCI_DEVICE_ID_AMPLICON_PCI236,
@@ -135,6 +139,18 @@ struct pc236_private {
int enable_irq;
};
+/* test if ISA supported and this is an ISA board */
+static inline bool is_isa_board(const struct pc236_board *board)
+{
+ return DO_ISA && board->bustype == isa_bustype;
+}
+
+/* test if PCI supported and this is a PCI board */
+static inline bool is_pci_board(const struct pc236_board *board)
+{
+ return DO_PCI && board->bustype == pci_bustype;
+}
+
/*
* This function looks for a board matching the supplied PCI device.
*/
@@ -143,7 +159,7 @@ static const struct pc236_board *pc236_find_pci_board(struct pci_dev *pci_dev)
unsigned int i;
for (i = 0; i < ARRAY_SIZE(pc236_boards); i++)
- if (pc236_boards[i].bustype == pci_bustype &&
+ if (is_pci_board(&pc236_boards[i]) &&
pci_dev->device == pc236_boards[i].devid)
return &pc236_boards[i];
return NULL;
@@ -214,12 +230,13 @@ static int pc236_request_region(struct comedi_device *dev, unsigned long from,
*/
static void pc236_intr_disable(struct comedi_device *dev)
{
+ const struct pc236_board *thisboard = comedi_board(dev);
struct pc236_private *devpriv = dev->private;
unsigned long flags;
spin_lock_irqsave(&dev->spinlock, flags);
devpriv->enable_irq = 0;
- if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) && devpriv->lcr_iobase)
+ if (is_pci_board(thisboard))
outl(PCI236_INTR_DISABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
spin_unlock_irqrestore(&dev->spinlock, flags);
}
@@ -231,12 +248,13 @@ static void pc236_intr_disable(struct comedi_device *dev)
*/
static void pc236_intr_enable(struct comedi_device *dev)
{
+ const struct pc236_board *thisboard = comedi_board(dev);
struct pc236_private *devpriv = dev->private;
unsigned long flags;
spin_lock_irqsave(&dev->spinlock, flags);
devpriv->enable_irq = 1;
- if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) && devpriv->lcr_iobase)
+ if (is_pci_board(thisboard))
outl(PCI236_INTR_ENABLE, devpriv->lcr_iobase + PLX9052_INTCSR);
spin_unlock_irqrestore(&dev->spinlock, flags);
}
@@ -250,6 +268,7 @@ static void pc236_intr_enable(struct comedi_device *dev)
*/
static int pc236_intr_check(struct comedi_device *dev)
{
+ const struct pc236_board *thisboard = comedi_board(dev);
struct pc236_private *devpriv = dev->private;
int retval = 0;
unsigned long flags;
@@ -257,8 +276,7 @@ static int pc236_intr_check(struct comedi_device *dev)
spin_lock_irqsave(&dev->spinlock, flags);
if (devpriv->enable_irq) {
retval = 1;
- if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) &&
- devpriv->lcr_iobase) {
+ if (is_pci_board(thisboard)) {
if ((inl(devpriv->lcr_iobase + PLX9052_INTCSR)
& PLX9052_INTCSR_LI1STAT_MASK)
== PLX9052_INTCSR_LI1STAT_INACTIVE) {
@@ -296,39 +314,20 @@ static int pc236_intr_cmdtest(struct comedi_device *dev,
struct comedi_cmd *cmd)
{
int err = 0;
- int tmp;
- /* step 1 */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_FOLLOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
if (err)
return 1;
- /* step 2: ignored */
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -395,7 +394,7 @@ static int pc236_intr_cancel(struct comedi_device *dev,
static irqreturn_t pc236_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 1;
+ struct comedi_subdevice *s = &dev->subdevices[1];
int handled;
handled = pc236_intr_check(dev);
@@ -414,15 +413,13 @@ static void pc236_report_attach(struct comedi_device *dev, unsigned int irq)
char tmpbuf[60];
int tmplen;
- if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA) &&
- thisboard->bustype == isa_bustype)
+ if (is_isa_board(thisboard))
tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
"(base %#lx) ", dev->iobase);
- else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) &&
- thisboard->bustype == pci_bustype) {
+ else if (is_pci_board(thisboard))
tmplen = scnprintf(tmpbuf, sizeof(tmpbuf),
"(pci %s) ", pci_name(pcidev));
- } else
+ else
tmplen = 0;
if (irq)
tmplen += scnprintf(&tmpbuf[tmplen], sizeof(tmpbuf) - tmplen,
@@ -449,14 +446,14 @@ static int pc236_common_attach(struct comedi_device *dev, unsigned long iobase,
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* digital i/o subdevice (8255) */
ret = subdev_8255_init(dev, s, NULL, iobase);
if (ret < 0) {
dev_err(dev->class_dev, "error! out of memory!\n");
return ret;
}
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
dev->read_subdev = s;
s->type = COMEDI_SUBD_UNUSED;
pc236_intr_disable(dev);
@@ -517,16 +514,14 @@ static int pc236_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return ret;
}
/* Process options according to bus type. */
- if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_ISA) &&
- thisboard->bustype == isa_bustype) {
+ if (is_isa_board(thisboard)) {
unsigned long iobase = it->options[0];
unsigned int irq = it->options[1];
ret = pc236_request_region(dev, iobase, PC236_IO_SIZE);
if (ret < 0)
return ret;
return pc236_common_attach(dev, iobase, irq, 0);
- } else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI) &&
- thisboard->bustype == pci_bustype) {
+ } else if (is_pci_board(thisboard)) {
struct pci_dev *pci_dev;
pci_dev = pc236_find_pci_dev(dev, it);
@@ -550,7 +545,7 @@ static int __devinit pc236_attach_pci(struct comedi_device *dev,
{
int ret;
- if (!IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI))
+ if (!DO_PCI)
return -EINVAL;
dev_info(dev->class_dev, PC236_DRIVER_NAME ": attach pci %s\n",
@@ -577,22 +572,25 @@ static int __devinit pc236_attach_pci(struct comedi_device *dev,
static void pc236_detach(struct comedi_device *dev)
{
+ const struct pc236_board *thisboard = comedi_board(dev);
struct pc236_private *devpriv = dev->private;
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
if (devpriv)
pc236_intr_disable(dev);
if (dev->irq)
free_irq(dev->irq, dev);
if (dev->subdevices)
- subdev_8255_cleanup(dev, dev->subdevices + 0);
- if (pcidev) {
- if (dev->iobase)
- comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
- } else {
+ subdev_8255_cleanup(dev, &dev->subdevices[0]);
+ if (is_isa_board(thisboard)) {
if (dev->iobase)
release_region(dev->iobase, PC236_IO_SIZE);
+ } else if (is_pci_board(thisboard)) {
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ if (pcidev) {
+ if (dev->iobase)
+ comedi_pci_disable(pcidev);
+ pci_dev_put(pcidev);
+ }
}
}
@@ -613,7 +611,7 @@ static struct comedi_driver amplc_pc236_driver = {
.num_names = ARRAY_SIZE(pc236_boards),
};
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC236_PCI)
+#if DO_PCI
static DEFINE_PCI_DEVICE_TABLE(pc236_pci_table) = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI236) },
{0}
diff --git a/drivers/staging/comedi/drivers/amplc_pc263.c b/drivers/staging/comedi/drivers/amplc_pc263.c
index 8191c4e28e0a..60830ccfb903 100644
--- a/drivers/staging/comedi/drivers/amplc_pc263.c
+++ b/drivers/staging/comedi/drivers/amplc_pc263.c
@@ -48,6 +48,9 @@ The state of the outputs can be read.
#define PC263_DRIVER_NAME "amplc_pc263"
+#define DO_ISA IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA)
+#define DO_PCI IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI)
+
/* PCI263 PCI configuration register information */
#define PCI_VENDOR_ID_AMPLICON 0x14dc
#define PCI_DEVICE_ID_AMPLICON_PCI263 0x000c
@@ -70,14 +73,14 @@ struct pc263_board {
enum pc263_model model;
};
static const struct pc263_board pc263_boards[] = {
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA)
+#if DO_ISA
{
.name = "pc263",
.bustype = isa_bustype,
.model = pc263_model,
},
#endif
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI)
+#if DO_PCI
{
.name = "pci263",
.devid = PCI_DEVICE_ID_AMPLICON_PCI263,
@@ -93,6 +96,18 @@ static const struct pc263_board pc263_boards[] = {
#endif
};
+/* test if ISA supported and this is an ISA board */
+static inline bool is_isa_board(const struct pc263_board *board)
+{
+ return DO_ISA && board->bustype == isa_bustype;
+}
+
+/* test if PCI supported and this is a PCI board */
+static inline bool is_pci_board(const struct pc263_board *board)
+{
+ return DO_PCI && board->bustype == pci_bustype;
+}
+
/*
* This function looks for a board matching the supplied PCI device.
*/
@@ -101,7 +116,7 @@ static const struct pc263_board *pc263_find_pci_board(struct pci_dev *pci_dev)
unsigned int i;
for (i = 0; i < ARRAY_SIZE(pc263_boards); i++)
- if (pc263_boards[i].bustype == pci_bustype &&
+ if (is_pci_board(&pc263_boards[i]) &&
pci_dev->device == pc263_boards[i].devid)
return &pc263_boards[i];
return NULL;
@@ -187,11 +202,9 @@ static void pc263_report_attach(struct comedi_device *dev)
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
char tmpbuf[40];
- if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA) &&
- thisboard->bustype == isa_bustype)
+ if (is_isa_board(thisboard))
snprintf(tmpbuf, sizeof(tmpbuf), "(base %#lx) ", dev->iobase);
- else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI) &&
- thisboard->bustype == pci_bustype)
+ else if (is_pci_board(thisboard))
snprintf(tmpbuf, sizeof(tmpbuf), "(pci %s) ",
pci_name(pcidev));
else
@@ -212,7 +225,7 @@ static int pc263_common_attach(struct comedi_device *dev, unsigned long iobase)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* digital output subdevice */
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
@@ -259,15 +272,13 @@ static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
dev_info(dev->class_dev, PC263_DRIVER_NAME ": attach\n");
/* Process options and reserve resources according to bus type. */
- if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_ISA) &&
- thisboard->bustype == isa_bustype) {
+ if (is_isa_board(thisboard)) {
unsigned long iobase = it->options[0];
ret = pc263_request_region(dev, iobase, PC263_IO_SIZE);
if (ret < 0)
return ret;
return pc263_common_attach(dev, iobase);
- } else if (IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI) &&
- thisboard->bustype == pci_bustype) {
+ } else if (is_pci_board(thisboard)) {
struct pci_dev *pci_dev;
pci_dev = pc263_find_pci_dev(dev, it);
@@ -288,7 +299,7 @@ static int pc263_attach(struct comedi_device *dev, struct comedi_devconfig *it)
static int __devinit pc263_attach_pci(struct comedi_device *dev,
struct pci_dev *pci_dev)
{
- if (!IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI))
+ if (!DO_PCI)
return -EINVAL;
dev_info(dev->class_dev, PC263_DRIVER_NAME ": attach pci %s\n",
@@ -310,15 +321,18 @@ static int __devinit pc263_attach_pci(struct comedi_device *dev,
static void pc263_detach(struct comedi_device *dev)
{
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct pc263_board *thisboard = comedi_board(dev);
- if (pcidev) {
- if (dev->iobase)
- comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
- } else {
+ if (is_isa_board(thisboard)) {
if (dev->iobase)
release_region(dev->iobase, PC263_IO_SIZE);
+ } else if (is_pci_board(thisboard)) {
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ if (pcidev) {
+ if (dev->iobase)
+ comedi_pci_disable(pcidev);
+ pci_dev_put(pcidev);
+ }
}
}
@@ -339,7 +353,7 @@ static struct comedi_driver amplc_pc263_driver = {
.num_names = ARRAY_SIZE(pc263_boards),
};
-#if IS_ENABLED(CONFIG_COMEDI_AMPLC_PC263_PCI)
+#if DO_PCI
static DEFINE_PCI_DEVICE_TABLE(pc263_pci_table) = {
{ PCI_DEVICE(PCI_VENDOR_ID_AMPLICON, PCI_DEVICE_ID_AMPLICON_PCI263) },
{0}
diff --git a/drivers/staging/comedi/drivers/amplc_pci224.c b/drivers/staging/comedi/drivers/amplc_pci224.c
index 8bf109e7bb05..1f65ec4d261e 100644
--- a/drivers/staging/comedi/drivers/amplc_pci224.c
+++ b/drivers/staging/comedi/drivers/amplc_pci224.c
@@ -720,53 +720,31 @@ pci224_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
int err = 0;
unsigned int tmp;
- /* Step 1: make sure trigger sources are trivially valid. */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_INT | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_EXT | TRIG_TIMER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_EXT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_EXT | TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src,
+ TRIG_COUNT | TRIG_EXT | TRIG_NONE);
if (err)
return 1;
- /* Step 2: make sure trigger sources are unique and mutually
- * compatible. */
+ /* Step 2a : make sure trigger sources are unique */
- /* these tests are true if more than one _src bit is set */
- if ((cmd->start_src & (cmd->start_src - 1)) != 0)
- err++;
- if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
- err++;
- if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
- err++;
- if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
- err++;
- if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
- /* There's only one external trigger signal (which makes these
- * tests easier). Only one thing can use it. */
+ /* Step 2b : and mutually compatible */
+
+ /*
+ * There's only one external trigger signal (which makes these
+ * tests easier). Only one thing can use it.
+ */
tmp = 0;
if (cmd->start_src & TRIG_EXT)
tmp++;
@@ -775,7 +753,7 @@ pci224_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
if (cmd->stop_src & TRIG_EXT)
tmp++;
if (tmp > 1)
- err++;
+ err |= -EINVAL;
if (err)
return 2;
@@ -1375,7 +1353,7 @@ static int pci224_attach_common(struct comedi_device *dev,
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* Analog output subdevice. */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_CMD_WRITE;
@@ -1523,7 +1501,7 @@ static void pci224_detach(struct comedi_device *dev)
if (dev->subdevices) {
struct comedi_subdevice *s;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* AO subdevice */
kfree(s->range_table_list);
}
diff --git a/drivers/staging/comedi/drivers/amplc_pci230.c b/drivers/staging/comedi/drivers/amplc_pci230.c
index 66e74bd12267..bd8fb876ce2e 100644
--- a/drivers/staging/comedi/drivers/amplc_pci230.c
+++ b/drivers/staging/comedi/drivers/amplc_pci230.c
@@ -193,6 +193,7 @@ for (or detection of) various hardware problems added by Ian Abbott.
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include "comedi_fc.h"
#include "8253.h"
#include "8255.h"
@@ -958,23 +959,11 @@ static int pci230_ao_cmdtest(struct comedi_device *dev,
int err = 0;
unsigned int tmp;
- /* cmdtest tests a particular command to see if it is valid.
- * Using the cmdtest ioctl, a user can create a valid cmd
- * and then have it executes by the cmd ioctl.
- *
- * cmdtest returns 1,2,3,4 or 0, depending on which tests
- * the command passes. */
+ /* Step 1 : check if triggers are trivially valid */
- /* Step 1: make sure trigger sources are trivially valid.
- * "invalid source" returned by comedilib to user mode process
- * if this fails. */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_INT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT);
- tmp = cmd->scan_begin_src;
+ tmp = TRIG_TIMER | TRIG_INT;
if ((thisboard->min_hwver > 0) && (devpriv->hwver >= 2)) {
/*
* For PCI230+ hardware version 2 onwards, allow external
@@ -990,46 +979,23 @@ static int pci230_ao_cmdtest(struct comedi_device *dev,
* scan_begin_src==TRIG_EXT support to be a bonus rather than a
* guarantee!
*/
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_INT | TRIG_EXT;
- } else {
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_INT;
+ tmp |= TRIG_EXT;
}
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, tmp);
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* Step 2: make sure trigger sources are unique and mutually compatible
- * "source conflict" returned by comedilib to user mode process
- * if this fails. */
+ /* Step 2a : make sure trigger sources are unique */
- /* these tests are true if more than one _src bit is set */
- if ((cmd->start_src & (cmd->start_src - 1)) != 0)
- err++;
- if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
- err++;
- if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
- err++;
- if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
- err++;
- if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1610,75 +1576,45 @@ static int pci230_ai_cmdtest(struct comedi_device *dev,
int err = 0;
unsigned int tmp;
- /* cmdtest tests a particular command to see if it is valid.
- * Using the cmdtest ioctl, a user can create a valid cmd
- * and then have it executes by the cmd ioctl.
- *
- * cmdtest returns 1,2,3,4,5 or 0, depending on which tests
- * the command passes. */
+ /* Step 1 : check if triggers are trivially valid */
- /* Step 1: make sure trigger sources are trivially valid.
- * "invalid source" returned by comedilib to user mode process
- * if this fails. */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_INT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
- tmp = cmd->scan_begin_src;
- /* Unfortunately, we cannot trigger a scan off an external source
- * on the PCI260 board, since it uses the PPIC0 (DIO) input, which
- * isn't present on the PCI260. For PCI260+ we can use the
- * EXTTRIG/EXTCONVCLK input on pin 17 instead. */
+ tmp = TRIG_FOLLOW | TRIG_TIMER | TRIG_INT;
if ((thisboard->have_dio) || (thisboard->min_hwver > 0)) {
- cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_INT
- | TRIG_EXT;
- } else {
- cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_INT;
+ /*
+ * Unfortunately, we cannot trigger a scan off an external
+ * source on the PCI260 board, since it uses the PPIC0 (DIO)
+ * input, which isn't present on the PCI260. For PCI260+
+ * we can use the EXTTRIG/EXTCONVCLK input on pin 17 instead.
+ */
+ tmp |= TRIG_EXT;
}
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_INT | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, tmp);
+ err |= cfc_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_INT | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* Step 2: make sure trigger sources are unique and mutually compatible
- * "source conflict" returned by comedilib to user mode process
- * if this fails. */
+ /* Step 2a : make sure trigger sources are unique */
- /* these tests are true if more than one _src bit is set */
- if ((cmd->start_src & (cmd->start_src - 1)) != 0)
- err++;
- if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
- err++;
- if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
- err++;
- if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
- err++;
- if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
- /* If scan_begin_src is not TRIG_FOLLOW, then a monostable will be
- * set up to generate a fixed number of timed conversion pulses. */
+ /*
+ * If scan_begin_src is not TRIG_FOLLOW, then a monostable will be
+ * set up to generate a fixed number of timed conversion pulses.
+ */
if ((cmd->scan_begin_src != TRIG_FOLLOW)
&& (cmd->convert_src != TRIG_TIMER))
- err++;
+ err |= -EINVAL;
if (err)
return 2;
@@ -2838,7 +2774,7 @@ static int pci230_attach_common(struct comedi_device *dev,
if (rc)
return rc;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* analog input subdevice */
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND;
@@ -2855,7 +2791,7 @@ static int pci230_attach_common(struct comedi_device *dev,
s->do_cmdtest = &pci230_ai_cmdtest;
s->cancel = pci230_ai_cancel;
}
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* analog output subdevice */
if (thisboard->ao_chans > 0) {
s->type = COMEDI_SUBD_AO;
@@ -2878,7 +2814,7 @@ static int pci230_attach_common(struct comedi_device *dev,
} else {
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* digital i/o subdevice */
if (thisboard->have_dio) {
rc = subdev_8255_init(dev, s, NULL,
@@ -2941,7 +2877,7 @@ static void pci230_detach(struct comedi_device *dev)
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
if (dev->subdevices && thisboard->have_dio)
- subdev_8255_cleanup(dev, dev->subdevices + 2);
+ subdev_8255_cleanup(dev, &dev->subdevices[2]);
if (dev->irq)
free_irq(dev->irq, dev);
if (pcidev) {
diff --git a/drivers/staging/comedi/drivers/c6xdigio.c b/drivers/staging/comedi/drivers/c6xdigio.c
index 41ed8576f301..070037c22db7 100644
--- a/drivers/staging/comedi/drivers/c6xdigio.c
+++ b/drivers/staging/comedi/drivers/c6xdigio.c
@@ -447,7 +447,7 @@ static int c6xdigio_attach(struct comedi_device *dev,
else if (irq == 0)
printk(KERN_DEBUG "comedi%d: no irq\n", dev->minor);
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* pwm output subdevice */
s->type = COMEDI_SUBD_AO; /* Not sure what to put here */
s->subdev_flags = SDF_WRITEABLE;
@@ -458,7 +458,7 @@ static int c6xdigio_attach(struct comedi_device *dev,
s->maxdata = 500;
s->range_table = &range_bipolar10; /* A suitable lie */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* encoder (counter) subdevice */
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
@@ -468,7 +468,7 @@ static int c6xdigio_attach(struct comedi_device *dev,
s->maxdata = 0xffffff;
s->range_table = &range_unknown;
- /* s = dev->subdevices + 2; */
+ /* s = &dev->subdevices[2]; */
/* pwm output subdevice */
/* s->type = COMEDI_SUBD_COUNTER; // Not sure what to put here */
/* s->subdev_flags = SDF_WRITEABLE; */
diff --git a/drivers/staging/comedi/drivers/cb_das16_cs.c b/drivers/staging/comedi/drivers/cb_das16_cs.c
index 58d45299bf85..6d81d8b40ccc 100644
--- a/drivers/staging/comedi/drivers/cb_das16_cs.c
+++ b/drivers/staging/comedi/drivers/cb_das16_cs.c
@@ -46,6 +46,7 @@ Status: experimental
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
+#include "comedi_fc.h"
#include "8253.h"
#define DAS16CS_SIZE 18
@@ -169,47 +170,26 @@ static int das16cs_ai_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and
- * mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -478,7 +458,7 @@ static int das16cs_attach(struct comedi_device *dev,
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
/* analog input subdevice */
s->type = COMEDI_SUBD_AI;
@@ -491,7 +471,7 @@ static int das16cs_attach(struct comedi_device *dev,
s->do_cmd = das16cs_ai_cmd;
s->do_cmdtest = das16cs_ai_cmdtest;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* analog output subdevice */
if (thisboard->n_ao_chans) {
s->type = COMEDI_SUBD_AO;
@@ -505,7 +485,7 @@ static int das16cs_attach(struct comedi_device *dev,
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* digital i/o subdevice */
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
diff --git a/drivers/staging/comedi/drivers/cb_pcidas.c b/drivers/staging/comedi/drivers/cb_pcidas.c
index 2b6a637c3499..de21a261ff45 100644
--- a/drivers/staging/comedi/drivers/cb_pcidas.c
+++ b/drivers/staging/comedi/drivers/cb_pcidas.c
@@ -45,11 +45,7 @@ Status:
The boards may be autocalibrated using the comedi_calibrate
utility.
-Configuration options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first supported
- PCI device found will be used.
+Configuration options: not applicable, uses PCI auto config
For commands, the scanned channels must be consecutive
(i.e. 4-5-6-7, 2-3-4,...), and must all have the same
@@ -807,58 +803,35 @@ static int cb_pcidas_ai_cmdtest(struct comedi_device *dev,
int tmp;
int i, gain, start_chan;
- /* step 1: trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_NOW | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_NOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
- err++;
- if (cmd->scan_begin_src != TRIG_FOLLOW &&
- cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->convert_src != TRIG_TIMER &&
- cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
- /* make sure trigger sources are compatible with each other */
if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
- err++;
+ err |= -EINVAL;
if (cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_NOW)
- err++;
+ err |= -EINVAL;
if (cmd->start_src == TRIG_EXT &&
(cmd->convert_src == TRIG_EXT || cmd->scan_begin_src == TRIG_EXT))
- err++;
+ err |= -EINVAL;
if (err)
return 2;
@@ -1083,43 +1056,24 @@ static int cb_pcidas_ao_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* step 1: trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_INT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1501,69 +1455,45 @@ static irqreturn_t cb_pcidas_interrupt(int irq, void *d)
return IRQ_HANDLED;
}
-static struct pci_dev *cb_pcidas_find_pci_device(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static const void *cb_pcidas_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
const struct cb_pcidas_board *thisboard;
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
int i;
- for_each_pci_dev(pcidev) {
- /* is it not a computer boards card? */
- if (pcidev->vendor != PCI_VENDOR_ID_CB)
- continue;
- /* loop through cards supported by this driver */
- for (i = 0; i < ARRAY_SIZE(cb_pcidas_boards); i++) {
- thisboard = &cb_pcidas_boards[i];
- if (thisboard->device_id != pcidev->device)
- continue;
- /* was a particular bus/slot requested? */
- if (bus || slot) {
- /* are we on the wrong bus/slot? */
- if (pcidev->bus->number != bus ||
- PCI_SLOT(pcidev->devfn) != slot) {
- continue;
- }
- }
- dev_dbg(dev->class_dev,
- "Found %s on bus %i, slot %i\n",
- thisboard->name,
- pcidev->bus->number, PCI_SLOT(pcidev->devfn));
- dev->board_ptr = thisboard;
- return pcidev;
- }
+ for (i = 0; i < ARRAY_SIZE(cb_pcidas_boards); i++) {
+ thisboard = &cb_pcidas_boards[i];
+ if (thisboard->device_id == pcidev->device)
+ return thisboard;
}
- dev_err(dev->class_dev, "No supported card found\n");
return NULL;
}
-static int cb_pcidas_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int cb_pcidas_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
const struct cb_pcidas_board *thisboard;
struct cb_pcidas_private *devpriv;
- struct pci_dev *pcidev;
struct comedi_subdevice *s;
int i;
int ret;
- if (alloc_private(dev, sizeof(struct cb_pcidas_private)) < 0)
- return -ENOMEM;
- devpriv = dev->private;
-
- pcidev = cb_pcidas_find_pci_device(dev, it);
- if (!pcidev)
- return -EIO;
comedi_set_hw_dev(dev, &pcidev->dev);
- thisboard = comedi_board(dev);
- if (comedi_pci_enable(pcidev, dev->driver->driver_name)) {
- dev_err(dev->class_dev,
- "Failed to enable PCI device and request regions\n");
- return -EIO;
- }
+ thisboard = cb_pcidas_find_boardinfo(dev, pcidev);
+ if (!thisboard)
+ return -ENODEV;
+ dev->board_ptr = thisboard;
+ dev->board_name = thisboard->name;
+
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret)
+ return ret;
+ devpriv = dev->private;
+
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
devpriv->s5933_config = pci_resource_start(pcidev, 0);
devpriv->control_status = pci_resource_start(pcidev, 1);
@@ -1584,13 +1514,11 @@ static int cb_pcidas_attach(struct comedi_device *dev,
}
dev->irq = pcidev->irq;
- dev->board_name = thisboard->name;
-
ret = comedi_alloc_subdevices(dev, 7);
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* analog input subdevice */
dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
@@ -1607,7 +1535,7 @@ static int cb_pcidas_attach(struct comedi_device *dev,
s->cancel = cb_pcidas_cancel;
/* analog output subdevice */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
if (thisboard->ao_nchan) {
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_GROUND;
@@ -1634,14 +1562,14 @@ static int cb_pcidas_attach(struct comedi_device *dev,
}
/* 8255 */
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
ret = subdev_8255_init(dev, s, NULL,
devpriv->pacer_counter_dio + DIO_8255);
if (ret)
return ret;
/* serial EEPROM, */
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_MEMORY;
s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
s->n_chan = 256;
@@ -1649,7 +1577,7 @@ static int cb_pcidas_attach(struct comedi_device *dev,
s->insn_read = eeprom_read_insn;
/* 8800 caldac */
- s = dev->subdevices + 4;
+ s = &dev->subdevices[4];
s->type = COMEDI_SUBD_CALIB;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
s->n_chan = NUM_CHANNELS_8800;
@@ -1660,7 +1588,7 @@ static int cb_pcidas_attach(struct comedi_device *dev,
caldac_8800_write(dev, i, s->maxdata / 2);
/* trim potentiometer */
- s = dev->subdevices + 5;
+ s = &dev->subdevices[5];
s->type = COMEDI_SUBD_CALIB;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
if (thisboard->trimpot == AD7376) {
@@ -1676,7 +1604,7 @@ static int cb_pcidas_attach(struct comedi_device *dev,
cb_pcidas_trimpot_write(dev, i, s->maxdata / 2);
/* dac08 caldac */
- s = dev->subdevices + 6;
+ s = &dev->subdevices[6];
if (thisboard->has_dac08) {
s->type = COMEDI_SUBD_CALIB;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
@@ -1698,7 +1626,10 @@ static int cb_pcidas_attach(struct comedi_device *dev,
outl(devpriv->s5933_intcsr_bits | INTCSR_INBOX_INTR_STATUS,
devpriv->s5933_config + AMCC_OP_REG_INTCSR);
- return 1;
+ dev_info(dev->class_dev, "%s: %s attached\n",
+ dev->driver->driver_name, dev->board_name);
+
+ return 0;
}
static void cb_pcidas_detach(struct comedi_device *dev)
@@ -1715,18 +1646,17 @@ static void cb_pcidas_detach(struct comedi_device *dev)
if (dev->irq)
free_irq(dev->irq, dev);
if (dev->subdevices)
- subdev_8255_cleanup(dev, dev->subdevices + 2);
+ subdev_8255_cleanup(dev, &dev->subdevices[2]);
if (pcidev) {
if (devpriv->s5933_config)
comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
}
}
static struct comedi_driver cb_pcidas_driver = {
.driver_name = "cb_pcidas",
.module = THIS_MODULE,
- .attach = cb_pcidas_attach,
+ .attach_pci = cb_pcidas_attach_pci,
.detach = cb_pcidas_detach,
};
diff --git a/drivers/staging/comedi/drivers/cb_pcidas64.c b/drivers/staging/comedi/drivers/cb_pcidas64.c
index 65cbaabf6456..0472a9088abe 100644
--- a/drivers/staging/comedi/drivers/cb_pcidas64.c
+++ b/drivers/staging/comedi/drivers/cb_pcidas64.c
@@ -1348,7 +1348,7 @@ static int setup_subdevices(struct comedi_device *dev)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* analog input subdevice */
dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
@@ -1379,7 +1379,7 @@ static int setup_subdevices(struct comedi_device *dev)
}
/* analog output subdevice */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
if (board(dev)->ao_nchan) {
s->type = COMEDI_SUBD_AO;
s->subdev_flags =
@@ -1401,7 +1401,7 @@ static int setup_subdevices(struct comedi_device *dev)
}
/* digital input */
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
if (board(dev)->layout == LAYOUT_64XX) {
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
@@ -1414,7 +1414,7 @@ static int setup_subdevices(struct comedi_device *dev)
/* digital output */
if (board(dev)->layout == LAYOUT_64XX) {
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
s->n_chan = 4;
@@ -1425,7 +1425,7 @@ static int setup_subdevices(struct comedi_device *dev)
s->type = COMEDI_SUBD_UNUSED;
/* 8255 */
- s = dev->subdevices + 4;
+ s = &dev->subdevices[4];
if (board(dev)->has_8255) {
if (board(dev)->layout == LAYOUT_4020) {
dio_8255_iobase =
@@ -1442,7 +1442,7 @@ static int setup_subdevices(struct comedi_device *dev)
s->type = COMEDI_SUBD_UNUSED;
/* 8 channel dio for 60xx */
- s = dev->subdevices + 5;
+ s = &dev->subdevices[5];
if (board(dev)->layout == LAYOUT_60XX) {
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
@@ -1455,7 +1455,7 @@ static int setup_subdevices(struct comedi_device *dev)
s->type = COMEDI_SUBD_UNUSED;
/* caldac */
- s = dev->subdevices + 6;
+ s = &dev->subdevices[6];
s->type = COMEDI_SUBD_CALIB;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
s->n_chan = 8;
@@ -1469,7 +1469,7 @@ static int setup_subdevices(struct comedi_device *dev)
caldac_write(dev, i, s->maxdata / 2);
/* 2 channel ad8402 potentiometer */
- s = dev->subdevices + 7;
+ s = &dev->subdevices[7];
if (board(dev)->layout == LAYOUT_64XX) {
s->type = COMEDI_SUBD_CALIB;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
@@ -1483,7 +1483,7 @@ static int setup_subdevices(struct comedi_device *dev)
s->type = COMEDI_SUBD_UNUSED;
/* serial EEPROM, if present */
- s = dev->subdevices + 8;
+ s = &dev->subdevices[8];
if (readl(priv(dev)->plx9080_iobase + PLX_CONTROL_REG) & CTL_EECHK) {
s->type = COMEDI_SUBD_MEMORY;
s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
@@ -1494,7 +1494,7 @@ static int setup_subdevices(struct comedi_device *dev)
s->type = COMEDI_SUBD_UNUSED;
/* user counter subd XXX */
- s = dev->subdevices + 9;
+ s = &dev->subdevices[9];
s->type = COMEDI_SUBD_UNUSED;
return 0;
@@ -1847,7 +1847,7 @@ static void detach(struct comedi_device *dev)
}
}
if (dev->subdevices)
- subdev_8255_cleanup(dev, dev->subdevices + 4);
+ subdev_8255_cleanup(dev, &dev->subdevices[4]);
if (pcidev) {
if (dev->iobase)
comedi_pci_disable(pcidev);
@@ -2108,74 +2108,50 @@ static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
int err = 0;
- int tmp;
unsigned int tmp_arg, tmp_arg2;
int i;
int aref;
unsigned int triggers;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
- tmp = cmd->scan_begin_src;
triggers = TRIG_TIMER;
if (board(dev)->layout == LAYOUT_4020)
triggers |= TRIG_OTHER;
else
triggers |= TRIG_FOLLOW;
- cmd->scan_begin_src &= triggers;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, triggers);
- tmp = cmd->convert_src;
triggers = TRIG_TIMER;
if (board(dev)->layout == LAYOUT_4020)
triggers |= TRIG_NOW;
else
triggers |= TRIG_EXT;
- cmd->convert_src &= triggers;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->convert_src, triggers);
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_EXT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src,
+ TRIG_COUNT | TRIG_EXT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* uniqueness check */
- if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
- err++;
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_OTHER &&
- cmd->scan_begin_src != TRIG_FOLLOW)
- err++;
- if (cmd->convert_src != TRIG_TIMER &&
- cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
- err++;
- if (cmd->stop_src != TRIG_COUNT &&
- cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_EXT)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
- /* compatibility check */
if (cmd->convert_src == TRIG_EXT && cmd->scan_begin_src == TRIG_TIMER)
- err++;
+ err |= -EINVAL;
if (cmd->stop_src != TRIG_COUNT &&
cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_EXT)
- err++;
+ err |= -EINVAL;
if (err)
return 2;
@@ -3466,55 +3442,33 @@ static int ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
int err = 0;
- int tmp;
unsigned int tmp_arg;
int i;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_INT | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* uniqueness check */
- if (cmd->start_src != TRIG_INT && cmd->start_src != TRIG_EXT)
- err++;
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+
+ /* Step 2b : and mutually compatible */
- /* compatibility check */
if (cmd->convert_src == TRIG_EXT && cmd->scan_begin_src == TRIG_TIMER)
- err++;
+ err |= -EINVAL;
if (cmd->stop_src != TRIG_COUNT &&
cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_EXT)
- err++;
+ err |= -EINVAL;
if (err)
return 2;
diff --git a/drivers/staging/comedi/drivers/cb_pcidda.c b/drivers/staging/comedi/drivers/cb_pcidda.c
index 12660a384e59..aef946df27e2 100644
--- a/drivers/staging/comedi/drivers/cb_pcidda.c
+++ b/drivers/staging/comedi/drivers/cb_pcidda.c
@@ -48,6 +48,7 @@ Please report success/failure with other different cards to
#include "../comedidev.h"
+#include "comedi_fc.h"
#include "8255.h"
/* PCI vendor number of ComputerBoards */
@@ -56,14 +57,6 @@ Please report success/failure with other different cards to
/* maximum number of ao channels for supported boards */
#define MAX_AO_CHANNELS 8
-/* PCI-DDA base addresses */
-#define DIGITALIO_BADRINDEX 2
- /* DIGITAL I/O is pci_dev->resource[2] */
-#define DIGITALIO_SIZE 8
- /* DIGITAL I/O uses 8 I/O port addresses */
-#define DAC_BADRINDEX 3
- /* DAC is pci_dev->resource[3] */
-
/* Digital I/O registers */
#define PORT1A 0 /* PORT 1A DATA */
@@ -203,11 +196,6 @@ static const struct cb_pcidda_board cb_pcidda_boards[] = {
};
/*
- * Useful for shorthand access to the particular board structure
- */
-#define thisboard ((const struct cb_pcidda_board *)dev->board_ptr)
-
-/*
* this structure is for data unique to this hardware driver. If
* several hardware drivers keep similar information in this structure,
* feel free to suggest moving the variable to the struct comedi_device
@@ -230,164 +218,6 @@ struct cb_pcidda_private {
};
/*
- * most drivers define the following macro to make it easy to
- * access the private structure.
- */
-#define devpriv ((struct cb_pcidda_private *)dev->private)
-
-/* static int cb_pcidda_ai_rinsn(struct comedi_device *dev,struct comedi_subdevice *s,struct comedi_insn *insn,unsigned int *data); */
-static int cb_pcidda_ao_winsn(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
-
-/* static int cb_pcidda_ai_cmd(struct comedi_device *dev, struct *comedi_subdevice *s);*/
-/* static int cb_pcidda_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s, struct comedi_cmd *cmd); */
-/* static int cb_pcidda_ns_to_timer(unsigned int *ns,int *round); */
-
-static unsigned int cb_pcidda_serial_in(struct comedi_device *dev);
-static void cb_pcidda_serial_out(struct comedi_device *dev, unsigned int value,
- unsigned int num_bits);
-static unsigned int cb_pcidda_read_eeprom(struct comedi_device *dev,
- unsigned int address);
-static void cb_pcidda_calibrate(struct comedi_device *dev, unsigned int channel,
- unsigned int range);
-
-static struct pci_dev *cb_pcidda_find_pci_dev(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
- int i;
-
- for_each_pci_dev(pcidev) {
- if (bus || slot) {
- if (bus != pcidev->bus->number ||
- slot != PCI_SLOT(pcidev->devfn))
- continue;
- }
- if (pcidev->vendor != PCI_VENDOR_ID_CB)
- continue;
-
- for (i = 0; i < ARRAY_SIZE(cb_pcidda_boards); i++) {
- if (cb_pcidda_boards[i].device_id != pcidev->device)
- continue;
- dev->board_ptr = cb_pcidda_boards + i;
- return pcidev;
- }
- }
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
- return NULL;
-}
-
-/*
- * Attach is called by the Comedi core to configure the driver
- * for a particular board.
- */
-static int cb_pcidda_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev;
- struct comedi_subdevice *s;
- int index;
- int ret;
-
-/*
- * Allocate the private structure area.
- */
- if (alloc_private(dev, sizeof(struct cb_pcidda_private)) < 0)
- return -ENOMEM;
-
- pcidev = cb_pcidda_find_pci_dev(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
-
- /*
- * Enable PCI device and request regions.
- */
- if (comedi_pci_enable(pcidev, thisboard->name)) {
- dev_err(dev->class_dev,
- "cb_pcidda: failed to enable PCI device and request regions\n");
- return -EIO;
- }
-
-/*
- * Allocate the I/O ports.
- */
- devpriv->digitalio = pci_resource_start(pcidev, DIGITALIO_BADRINDEX);
- devpriv->dac = pci_resource_start(pcidev, DAC_BADRINDEX);
- dev->iobase = devpriv->dac;
-
-/*
- * Warn about the status of the driver.
- */
- if (thisboard->status == 2)
- printk
- ("WARNING: DRIVER FOR THIS BOARD NOT CHECKED WITH MANUAL. "
- "WORKS ASSUMING FULL COMPATIBILITY WITH PCI-DDA08/12. "
- "PLEASE REPORT USAGE TO <ivanmr@altavista.com>.\n");
-
-/*
- * Initialize dev->board_name.
- */
- dev->board_name = thisboard->name;
-
- ret = comedi_alloc_subdevices(dev, 3);
- if (ret)
- return ret;
-
- s = dev->subdevices + 0;
- /* analog output subdevice */
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITABLE;
- s->n_chan = thisboard->ao_chans;
- s->maxdata = (1 << thisboard->ao_bits) - 1;
- s->range_table = thisboard->ranges;
- s->insn_write = cb_pcidda_ao_winsn;
-
- /* s->subdev_flags |= SDF_CMD_READ; */
- /* s->do_cmd = cb_pcidda_ai_cmd; */
- /* s->do_cmdtest = cb_pcidda_ai_cmdtest; */
-
- /* two 8255 digital io subdevices */
- s = dev->subdevices + 1;
- subdev_8255_init(dev, s, NULL, devpriv->digitalio);
- s = dev->subdevices + 2;
- subdev_8255_init(dev, s, NULL, devpriv->digitalio + PORT2A);
-
- dev_dbg(dev->class_dev, "eeprom:\n");
- for (index = 0; index < EEPROM_SIZE; index++) {
- devpriv->eeprom_data[index] = cb_pcidda_read_eeprom(dev, index);
- dev_dbg(dev->class_dev, "%i:0x%x\n", index,
- devpriv->eeprom_data[index]);
- }
-
- /* set calibrations dacs */
- for (index = 0; index < thisboard->ao_chans; index++)
- cb_pcidda_calibrate(dev, index, devpriv->ao_range[index]);
-
- return 1;
-}
-
-static void cb_pcidda_detach(struct comedi_device *dev)
-{
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
-
- if (pcidev) {
- if (dev->iobase)
- comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
- }
- if (dev->subdevices) {
- subdev_8255_cleanup(dev, dev->subdevices + 1);
- subdev_8255_cleanup(dev, dev->subdevices + 2);
- }
-}
-
-/*
* I will program this later... ;-)
*/
#if 0
@@ -418,56 +248,26 @@ static int cb_pcidda_ai_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* cmdtest tests a particular command to see if it is valid.
- * Using the cmdtest ioctl, a user can create a valid cmd
- * and then have it executes by the cmd ioctl.
- *
- * cmdtest returns 1,2,3,4 or 0, depending on which tests
- * the command passes. */
-
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources are unique and mutually
- * compatible
- */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->scan_begin_src != TRIG_TIMER
- && cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_TIMER && cmd->stop_src != TRIG_EXT)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -581,59 +381,10 @@ static int cb_pcidda_ns_to_timer(unsigned int *ns, int round)
}
#endif
-static int cb_pcidda_ao_winsn(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
-{
- unsigned int command;
- unsigned int channel, range;
-
- channel = CR_CHAN(insn->chanspec);
- range = CR_RANGE(insn->chanspec);
-
- /* adjust calibration dacs if range has changed */
- if (range != devpriv->ao_range[channel])
- cb_pcidda_calibrate(dev, channel, range);
-
- /* output channel configuration */
- command = NOSU | ENABLEDAC;
-
- /* output channel range */
- switch (range) {
- case 0:
- command |= BIP | RANGE10V;
- break;
- case 1:
- command |= BIP | RANGE5V;
- break;
- case 2:
- command |= BIP | RANGE2V5;
- break;
- case 3:
- command |= UNIP | RANGE10V;
- break;
- case 4:
- command |= UNIP | RANGE5V;
- break;
- case 5:
- command |= UNIP | RANGE2V5;
- break;
- }
-
- /* output channel specification */
- command |= channel << 2;
- outw(command, devpriv->dac + DACONTROL);
-
- /* write data */
- outw(data[0], devpriv->dac + DADATA + channel * 2);
-
- /* return the number of samples read/written */
- return 1;
-}
-
/* lowlevel read from eeprom */
static unsigned int cb_pcidda_serial_in(struct comedi_device *dev)
{
+ struct cb_pcidda_private *devpriv = dev->private;
unsigned int value = 0;
int i;
const int value_width = 16; /* number of bits wide values are */
@@ -651,6 +402,7 @@ static unsigned int cb_pcidda_serial_in(struct comedi_device *dev)
static void cb_pcidda_serial_out(struct comedi_device *dev, unsigned int value,
unsigned int num_bits)
{
+ struct cb_pcidda_private *devpriv = dev->private;
int i;
for (i = 1; i <= num_bits; i++) {
@@ -667,6 +419,7 @@ static void cb_pcidda_serial_out(struct comedi_device *dev, unsigned int value,
static unsigned int cb_pcidda_read_eeprom(struct comedi_device *dev,
unsigned int address)
{
+ struct cb_pcidda_private *devpriv = dev->private;
unsigned int i;
unsigned int cal2_bits;
unsigned int value;
@@ -703,6 +456,7 @@ static void cb_pcidda_write_caldac(struct comedi_device *dev,
unsigned int caldac, unsigned int channel,
unsigned int value)
{
+ struct cb_pcidda_private *devpriv = dev->private;
unsigned int cal2_bits;
unsigned int i;
/* caldacs use 3 bit channel specification */
@@ -797,6 +551,7 @@ static unsigned int eeprom_fine_byte(unsigned int word)
static void cb_pcidda_calibrate(struct comedi_device *dev, unsigned int channel,
unsigned int range)
{
+ struct cb_pcidda_private *devpriv = dev->private;
unsigned int coarse_offset, fine_offset, coarse_gain, fine_gain;
/* remember range so we can tell when we need to readjust calibration */
@@ -827,10 +582,164 @@ static void cb_pcidda_calibrate(struct comedi_device *dev, unsigned int channel,
fine_gain_channel(channel), fine_gain);
}
+static int cb_pcidda_ao_winsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct cb_pcidda_private *devpriv = dev->private;
+ unsigned int command;
+ unsigned int channel, range;
+
+ channel = CR_CHAN(insn->chanspec);
+ range = CR_RANGE(insn->chanspec);
+
+ /* adjust calibration dacs if range has changed */
+ if (range != devpriv->ao_range[channel])
+ cb_pcidda_calibrate(dev, channel, range);
+
+ /* output channel configuration */
+ command = NOSU | ENABLEDAC;
+
+ /* output channel range */
+ switch (range) {
+ case 0:
+ command |= BIP | RANGE10V;
+ break;
+ case 1:
+ command |= BIP | RANGE5V;
+ break;
+ case 2:
+ command |= BIP | RANGE2V5;
+ break;
+ case 3:
+ command |= UNIP | RANGE10V;
+ break;
+ case 4:
+ command |= UNIP | RANGE5V;
+ break;
+ case 5:
+ command |= UNIP | RANGE2V5;
+ break;
+ }
+
+ /* output channel specification */
+ command |= channel << 2;
+ outw(command, devpriv->dac + DACONTROL);
+
+ /* write data */
+ outw(data[0], devpriv->dac + DADATA + channel * 2);
+
+ /* return the number of samples read/written */
+ return 1;
+}
+
+static const void *cb_pcidda_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ const struct cb_pcidda_board *thisboard;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cb_pcidda_boards); i++) {
+ thisboard = &cb_pcidda_boards[i];
+ if (thisboard->device_id != pcidev->device)
+ return thisboard;
+ }
+ return NULL;
+}
+
+static int cb_pcidda_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ const struct cb_pcidda_board *thisboard;
+ struct cb_pcidda_private *devpriv;
+ struct comedi_subdevice *s;
+ int index;
+ int ret;
+
+ comedi_set_hw_dev(dev, &pcidev->dev);
+
+ thisboard = cb_pcidda_find_boardinfo(dev, pcidev);
+ if (!pcidev)
+ return -ENODEV;
+ dev->board_ptr = thisboard;
+ dev->board_name = thisboard->name;
+
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret)
+ return ret;
+ devpriv = dev->private;
+
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
+
+ devpriv->digitalio = pci_resource_start(pcidev, 2);
+ devpriv->dac = pci_resource_start(pcidev, 3);
+ dev->iobase = devpriv->dac;
+
+ if (thisboard->status == 2)
+ printk
+ ("WARNING: DRIVER FOR THIS BOARD NOT CHECKED WITH MANUAL. "
+ "WORKS ASSUMING FULL COMPATIBILITY WITH PCI-DDA08/12. "
+ "PLEASE REPORT USAGE TO <ivanmr@altavista.com>.\n");
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->ao_chans;
+ s->maxdata = (1 << thisboard->ao_bits) - 1;
+ s->range_table = thisboard->ranges;
+ s->insn_write = cb_pcidda_ao_winsn;
+
+ /* s->subdev_flags |= SDF_CMD_READ; */
+ /* s->do_cmd = cb_pcidda_ai_cmd; */
+ /* s->do_cmdtest = cb_pcidda_ai_cmdtest; */
+
+ /* two 8255 digital io subdevices */
+ s = &dev->subdevices[1];
+ subdev_8255_init(dev, s, NULL, devpriv->digitalio);
+ s = &dev->subdevices[2];
+ subdev_8255_init(dev, s, NULL, devpriv->digitalio + PORT2A);
+
+ dev_dbg(dev->class_dev, "eeprom:\n");
+ for (index = 0; index < EEPROM_SIZE; index++) {
+ devpriv->eeprom_data[index] = cb_pcidda_read_eeprom(dev, index);
+ dev_dbg(dev->class_dev, "%i:0x%x\n", index,
+ devpriv->eeprom_data[index]);
+ }
+
+ /* set calibrations dacs */
+ for (index = 0; index < thisboard->ao_chans; index++)
+ cb_pcidda_calibrate(dev, index, devpriv->ao_range[index]);
+
+ dev_info(dev->class_dev, "%s attached\n", dev->board_name);
+
+ return 0;
+}
+
+static void cb_pcidda_detach(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+
+ if (dev->subdevices) {
+ subdev_8255_cleanup(dev, &dev->subdevices[1]);
+ subdev_8255_cleanup(dev, &dev->subdevices[2]);
+ }
+ if (pcidev) {
+ if (dev->iobase)
+ comedi_pci_disable(pcidev);
+ }
+}
+
static struct comedi_driver cb_pcidda_driver = {
.driver_name = "cb_pcidda",
.module = THIS_MODULE,
- .attach = cb_pcidda_attach,
+ .attach_pci = cb_pcidda_attach_pci,
.detach = cb_pcidda_detach,
};
diff --git a/drivers/staging/comedi/drivers/cb_pcidio.c b/drivers/staging/comedi/drivers/cb_pcidio.c
deleted file mode 100644
index e370d0d81bbd..000000000000
--- a/drivers/staging/comedi/drivers/cb_pcidio.c
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- comedi/drivers/cb_pcidio.c
- A Comedi driver for PCI-DIO24H & PCI-DIO48H of ComputerBoards (currently MeasurementComputing)
-
- COMEDI - Linux Control and Measurement Device Interface
- Copyright (C) 2000 David A. Schleef <ds@schleef.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- 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.
-
-*/
-/*
-Driver: cb_pcidio
-Description: ComputerBoards' DIO boards with PCI interface
-Devices: [Measurement Computing] PCI-DIO24 (cb_pcidio), PCI-DIO24H, PCI-DIO48H
-Author: Yoshiya Matsuzaka
-Updated: Mon, 29 Oct 2007 15:40:47 +0000
-Status: experimental
-
-This driver has been modified from skel.c of comedi-0.7.70.
-
-Configuration Options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first available PCI device will
- be used.
-
-Passing a zero for an option is the same as leaving it unspecified.
-*/
-
-/*------------------------------ HEADER FILES ---------------------------------*/
-#include "../comedidev.h"
-#include "8255.h"
-
-/*-------------------------- MACROS and DATATYPES -----------------------------*/
-#define PCI_VENDOR_ID_CB 0x1307
-
-/*
- * Board descriptions for two imaginary boards. Describing the
- * boards in this way is optional, and completely driver-dependent.
- * Some drivers use arrays such as this, other do not.
- */
-struct pcidio_board {
- const char *name; /* name of the board */
- int dev_id;
- int n_8255; /* number of 8255 chips on board */
-
- /* indices of base address regions */
- int pcicontroler_badrindex;
- int dioregs_badrindex;
-};
-
-static const struct pcidio_board pcidio_boards[] = {
- {
- .name = "pci-dio24",
- .dev_id = 0x0028,
- .n_8255 = 1,
- .pcicontroler_badrindex = 1,
- .dioregs_badrindex = 2,
- },
- {
- .name = "pci-dio24h",
- .dev_id = 0x0014,
- .n_8255 = 1,
- .pcicontroler_badrindex = 1,
- .dioregs_badrindex = 2,
- },
- {
- .name = "pci-dio48h",
- .dev_id = 0x000b,
- .n_8255 = 2,
- .pcicontroler_badrindex = 0,
- .dioregs_badrindex = 1,
- },
-};
-
-/*
- * Useful for shorthand access to the particular board structure
- */
-#define thisboard ((const struct pcidio_board *)dev->board_ptr)
-
-static struct pci_dev *pcidio_find_pci_dev(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
- int i;
-
- for_each_pci_dev(pcidev) {
- if (bus || slot) {
- if (bus != pcidev->bus->number ||
- slot != PCI_SLOT(pcidev->devfn))
- continue;
- }
- if (pcidev->vendor != PCI_VENDOR_ID_CB)
- continue;
- for (i = 0; i < ARRAY_SIZE(pcidio_boards); i++) {
- if (pcidio_boards[i].dev_id != pcidev->device)
- continue;
-
- dev->board_ptr = pcidio_boards + i;
- return pcidev;
- }
- }
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
- return NULL;
-}
-
-static int pcidio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev;
- int i;
- int ret;
-
- pcidev = pcidio_find_pci_dev(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
-
-/*
- * Initialize dev->board_name. Note that we can use the "thisboard"
- * macro now, since we just initialized it in the last line.
- */
- dev->board_name = thisboard->name;
-
- if (comedi_pci_enable(pcidev, thisboard->name))
- return -EIO;
-
- dev->iobase = pci_resource_start(pcidev, thisboard->dioregs_badrindex);
-
- ret = comedi_alloc_subdevices(dev, thisboard->n_8255);
- if (ret)
- return ret;
-
- for (i = 0; i < thisboard->n_8255; i++) {
- subdev_8255_init(dev, dev->subdevices + i,
- NULL, dev->iobase + i * 4);
- dev_dbg(dev->class_dev, "subdev %d: base = 0x%lx\n", i,
- dev->iobase + i * 4);
- }
-
- return 1;
-}
-
-static void pcidio_detach(struct comedi_device *dev)
-{
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
-
- if (pcidev) {
- if (dev->iobase)
- comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
- }
- if (dev->subdevices) {
- int i;
- for (i = 0; i < thisboard->n_8255; i++)
- subdev_8255_cleanup(dev, dev->subdevices + i);
- }
-}
-
-static struct comedi_driver cb_pcidio_driver = {
- .driver_name = "cb_pcidio",
- .module = THIS_MODULE,
- .attach = pcidio_attach,
- .detach = pcidio_detach,
-};
-
-static int __devinit cb_pcidio_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *ent)
-{
- return comedi_pci_auto_config(dev, &cb_pcidio_driver);
-}
-
-static void __devexit cb_pcidio_pci_remove(struct pci_dev *dev)
-{
- comedi_pci_auto_unconfig(dev);
-}
-
-static DEFINE_PCI_DEVICE_TABLE(cb_pcidio_pci_table) = {
- { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0028) },
- { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x0014) },
- { PCI_DEVICE(PCI_VENDOR_ID_CB, 0x000b) },
- { 0 }
-};
-MODULE_DEVICE_TABLE(pci, cb_pcidio_pci_table);
-
-static struct pci_driver cb_pcidio_pci_driver = {
- .name = "cb_pcidio",
- .id_table = cb_pcidio_pci_table,
- .probe = cb_pcidio_pci_probe,
- .remove = __devexit_p(cb_pcidio_pci_remove),
-};
-module_comedi_pci_driver(cb_pcidio_driver, cb_pcidio_pci_driver);
-
-MODULE_AUTHOR("Comedi http://www.comedi.org");
-MODULE_DESCRIPTION("Comedi low-level driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/cb_pcimdas.c b/drivers/staging/comedi/drivers/cb_pcimdas.c
index c632a89f3ae9..9515b6926662 100644
--- a/drivers/staging/comedi/drivers/cb_pcimdas.c
+++ b/drivers/staging/comedi/drivers/cb_pcimdas.c
@@ -119,11 +119,6 @@ static const struct cb_pcimdas_board cb_pcimdas_boards[] = {
};
/*
- * Useful for shorthand access to the particular board structure
- */
-#define thisboard ((const struct cb_pcimdas_board *)dev->board_ptr)
-
-/*
* this structure is for data unique to this hardware driver. If
* several hardware drivers keep similar information in this structure,
* feel free to suggest moving the variable to the struct comedi_device
@@ -138,160 +133,6 @@ struct cb_pcimdas_private {
};
/*
- * most drivers define the following macro to make it easy to
- * access the private structure.
- */
-#define devpriv ((struct cb_pcimdas_private *)dev->private)
-
-static int cb_pcimdas_ai_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
-static int cb_pcimdas_ao_winsn(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
-static int cb_pcimdas_ao_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
-
-static struct pci_dev *cb_pcimdas_find_pci_dev(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
- int i;
-
- for_each_pci_dev(pcidev) {
- if (bus || slot) {
- if (bus != pcidev->bus->number ||
- slot != PCI_SLOT(pcidev->devfn))
- continue;
- }
- if (pcidev->vendor != PCI_VENDOR_ID_COMPUTERBOARDS)
- continue;
-
- for (i = 0; i < ARRAY_SIZE(cb_pcimdas_boards); i++) {
- if (cb_pcimdas_boards[i].device_id != pcidev->device)
- continue;
-
- dev->board_ptr = cb_pcimdas_boards + i;
- return pcidev;
- }
- }
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
- return NULL;
-}
-
-/*
- * Attach is called by the Comedi core to configure the driver
- * for a particular board. If you specified a board_name array
- * in the driver structure, dev->board_ptr contains that
- * address.
- */
-static int cb_pcimdas_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev;
- struct comedi_subdevice *s;
- unsigned long iobase_8255;
- int ret;
-
-/*
- * Allocate the private structure area.
- */
- if (alloc_private(dev, sizeof(struct cb_pcimdas_private)) < 0)
- return -ENOMEM;
-
- pcidev = cb_pcimdas_find_pci_dev(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
-
- /* Warn about non-tested features */
- switch (thisboard->device_id) {
- case 0x56:
- break;
- default:
- dev_dbg(dev->class_dev, "THIS CARD IS UNSUPPORTED.\n");
- dev_dbg(dev->class_dev,
- "PLEASE REPORT USAGE TO <mocelet@sucs.org>\n");
- }
-
- if (comedi_pci_enable(pcidev, "cb_pcimdas")) {
- dev_err(dev->class_dev,
- "Failed to enable PCI device and request regions\n");
- return -EIO;
- }
-
- dev->iobase = pci_resource_start(pcidev, 2);
- devpriv->BADR3 = pci_resource_start(pcidev, 3);
- iobase_8255 = pci_resource_start(pcidev, 4);
-
-/* Dont support IRQ yet */
-/* get irq */
-/* if(request_irq(pcidev->irq, cb_pcimdas_interrupt, IRQF_SHARED, "cb_pcimdas", dev )) */
-/* { */
-/* printk(" unable to allocate irq %u\n", pcidev->irq); */
-/* return -EINVAL; */
-/* } */
-/* dev->irq = pcidev->irq; */
-
- /* Initialize dev->board_name */
- dev->board_name = thisboard->name;
-
- ret = comedi_alloc_subdevices(dev, 3);
- if (ret)
- return ret;
-
- s = dev->subdevices + 0;
- /* dev->read_subdev=s; */
- /* analog input subdevice */
- s->type = COMEDI_SUBD_AI;
- s->subdev_flags = SDF_READABLE | SDF_GROUND;
- s->n_chan = thisboard->ai_se_chans;
- s->maxdata = (1 << thisboard->ai_bits) - 1;
- s->range_table = &range_unknown;
- s->len_chanlist = 1; /* This is the maximum chanlist length that */
- /* the board can handle */
- s->insn_read = cb_pcimdas_ai_rinsn;
-
- s = dev->subdevices + 1;
- /* analog output subdevice */
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITABLE;
- s->n_chan = thisboard->ao_nchan;
- s->maxdata = 1 << thisboard->ao_bits;
- /* ranges are hardware settable, but not software readable. */
- s->range_table = &range_unknown;
- s->insn_write = &cb_pcimdas_ao_winsn;
- s->insn_read = &cb_pcimdas_ao_rinsn;
-
- s = dev->subdevices + 2;
- /* digital i/o subdevice */
- if (thisboard->has_dio)
- subdev_8255_init(dev, s, NULL, iobase_8255);
- else
- s->type = COMEDI_SUBD_UNUSED;
-
- return 1;
-}
-
-static void cb_pcimdas_detach(struct comedi_device *dev)
-{
- struct pci_dev *pcidev = comedi_to_pci_dev(dev);
-
- if (dev->irq)
- free_irq(dev->irq, dev);
- if (pcidev) {
- if (dev->iobase)
- comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
- }
-}
-
-/*
* "instructions" read/write data in "one-shot" or "software-triggered"
* mode.
*/
@@ -299,6 +140,8 @@ static int cb_pcimdas_ai_rinsn(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ const struct cb_pcimdas_board *thisboard = comedi_board(dev);
+ struct cb_pcimdas_private *devpriv = dev->private;
int n, i;
unsigned int d;
unsigned int busy;
@@ -368,6 +211,7 @@ static int cb_pcimdas_ao_winsn(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct cb_pcimdas_private *devpriv = dev->private;
int i;
int chan = CR_CHAN(insn->chanspec);
@@ -397,6 +241,7 @@ static int cb_pcimdas_ao_rinsn(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct cb_pcimdas_private *devpriv = dev->private;
int i;
int chan = CR_CHAN(insn->chanspec);
@@ -406,10 +251,124 @@ static int cb_pcimdas_ao_rinsn(struct comedi_device *dev,
return i;
}
+static const void *cb_pcimdas_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ const struct cb_pcimdas_board *thisboard;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cb_pcimdas_boards); i++) {
+ thisboard = &cb_pcimdas_boards[i];
+ if (thisboard->device_id == pcidev->device)
+ return thisboard;
+ }
+ return NULL;
+}
+
+static int cb_pcimdas_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ const struct cb_pcimdas_board *thisboard;
+ struct cb_pcimdas_private *devpriv;
+ struct comedi_subdevice *s;
+ unsigned long iobase_8255;
+ int ret;
+
+ comedi_set_hw_dev(dev, &pcidev->dev);
+
+ thisboard = cb_pcimdas_find_boardinfo(dev, pcidev);
+ if (!thisboard)
+ return -ENODEV;
+ dev->board_ptr = thisboard;
+ dev->board_name = thisboard->name;
+
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret)
+ return ret;
+ devpriv = dev->private;
+
+ /* Warn about non-tested features */
+ switch (thisboard->device_id) {
+ case 0x56:
+ break;
+ default:
+ dev_dbg(dev->class_dev, "THIS CARD IS UNSUPPORTED.\n");
+ dev_dbg(dev->class_dev,
+ "PLEASE REPORT USAGE TO <mocelet@sucs.org>\n");
+ }
+
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
+
+ dev->iobase = pci_resource_start(pcidev, 2);
+ devpriv->BADR3 = pci_resource_start(pcidev, 3);
+ iobase_8255 = pci_resource_start(pcidev, 4);
+
+/* Dont support IRQ yet */
+/* get irq */
+/* if(request_irq(pcidev->irq, cb_pcimdas_interrupt, IRQF_SHARED, "cb_pcimdas", dev )) */
+/* { */
+/* printk(" unable to allocate irq %u\n", pcidev->irq); */
+/* return -EINVAL; */
+/* } */
+/* dev->irq = pcidev->irq; */
+
+ ret = comedi_alloc_subdevices(dev, 3);
+ if (ret)
+ return ret;
+
+ s = &dev->subdevices[0];
+ /* dev->read_subdev=s; */
+ /* analog input subdevice */
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = thisboard->ai_se_chans;
+ s->maxdata = (1 << thisboard->ai_bits) - 1;
+ s->range_table = &range_unknown;
+ s->len_chanlist = 1; /* This is the maximum chanlist length that */
+ /* the board can handle */
+ s->insn_read = cb_pcimdas_ai_rinsn;
+
+ s = &dev->subdevices[1];
+ /* analog output subdevice */
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = thisboard->ao_nchan;
+ s->maxdata = 1 << thisboard->ao_bits;
+ /* ranges are hardware settable, but not software readable. */
+ s->range_table = &range_unknown;
+ s->insn_write = &cb_pcimdas_ao_winsn;
+ s->insn_read = &cb_pcimdas_ao_rinsn;
+
+ s = &dev->subdevices[2];
+ /* digital i/o subdevice */
+ if (thisboard->has_dio)
+ subdev_8255_init(dev, s, NULL, iobase_8255);
+ else
+ s->type = COMEDI_SUBD_UNUSED;
+
+ dev_info(dev->class_dev, "%s attached\n", dev->board_name);
+
+ return 0;
+}
+
+static void cb_pcimdas_detach(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (pcidev) {
+ if (dev->iobase)
+ comedi_pci_disable(pcidev);
+ }
+}
+
static struct comedi_driver cb_pcimdas_driver = {
.driver_name = "cb_pcimdas",
.module = THIS_MODULE,
- .attach = cb_pcimdas_attach,
+ .attach_pci = cb_pcimdas_attach_pci,
.detach = cb_pcimdas_detach,
};
diff --git a/drivers/staging/comedi/drivers/cb_pcimdda.c b/drivers/staging/comedi/drivers/cb_pcimdda.c
index a80146133c04..ba9f0599be28 100644
--- a/drivers/staging/comedi/drivers/cb_pcimdda.c
+++ b/drivers/staging/comedi/drivers/cb_pcimdda.c
@@ -57,12 +57,7 @@ output modes on the board:
then issue one comedi_data_read() on any channel on the AO subdevice
to initiate the simultaneous XFER.
-Configuration Options:
- [0] PCI bus (optional)
- [1] PCI slot (optional)
- [2] analog output range jumper setting
- 0 == +/- 5 V
- 1 == +/- 10 V
+Configuration Options: not applicable, uses PCI auto config
*/
/*
@@ -93,337 +88,133 @@ Configuration Options:
#define PCI_ID_PCIM_DDA06_16 0x0053
/*
- * This is straight from skel.c -- I did this in case this source file
- * will someday support more than 1 board...
+ * Register map, 8-bit access only
*/
-struct board_struct {
- const char *name;
- unsigned short device_id;
- int ao_chans;
- int ao_bits;
- int dio_chans;
- int dio_method;
- /* how many bytes into the BADR are the DIO ports */
- int dio_offset;
- int regs_badrindex; /* IO Region for the control, analog output,
- and DIO registers */
- int reg_sz; /* number of bytes of registers in io region */
-};
+#define PCIMDDA_DA_CHAN(x) (0x00 + (x) * 2)
+#define PCIMDDA_8255_BASE_REG 0x0c
-enum DIO_METHODS {
- DIO_NONE = 0,
- DIO_8255,
- DIO_INTERNAL /* unimplemented */
-};
+#define MAX_AO_READBACK_CHANNELS 6
-static const struct board_struct boards[] = {
- {
- .name = "cb_pcimdda06-16",
- .device_id = PCI_ID_PCIM_DDA06_16,
- .ao_chans = 6,
- .ao_bits = 16,
- .dio_chans = 24,
- .dio_method = DIO_8255,
- .dio_offset = 12,
- .regs_badrindex = 3,
- .reg_sz = 16,
- }
+struct cb_pcimdda_private {
+ unsigned int ao_readback[MAX_AO_READBACK_CHANNELS];
};
-/*
- * Useful for shorthand access to the particular board structure
- */
-#define thisboard ((const struct board_struct *)dev->board_ptr)
-
-#define REG_SZ (thisboard->reg_sz)
-#define REGS_BADRINDEX (thisboard->regs_badrindex)
-
-/*
- * this structure is for data unique to this hardware driver. If
- * several hardware drivers keep similar information in this structure,
- * feel free to suggest moving the variable to the struct comedi_device
- * struct.
- */
-struct board_private_struct {
- unsigned long registers; /* set by probe */
- unsigned long dio_registers;
- char attached_to_8255; /* boolean */
- /* would be useful for a PCI device */
- struct pci_dev *pci_dev;
-
-#define MAX_AO_READBACK_CHANNELS 6
- /* Used for AO readback */
- unsigned int ao_readback[MAX_AO_READBACK_CHANNELS];
+static int cb_pcimdda_ao_winsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
+{
+ struct cb_pcimdda_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long offset = dev->iobase + PCIMDDA_DA_CHAN(chan);
+ unsigned int val = 0;
+ int i;
-};
+ for (i = 0; i < insn->n; i++) {
+ val = data[i];
-/*
- * most drivers define the following macro to make it easy to
- * access the private structure.
- */
-#define devpriv ((struct board_private_struct *)dev->private)
+ /*
+ * Write the LSB then MSB.
+ *
+ * If the simultaneous xfer mode is selected by the
+ * jumper on the card, a read instruction is needed
+ * in order to initiate the simultaneous transfer.
+ * Otherwise, the DAC will be updated when the MSB
+ * is written.
+ */
+ outb(val & 0x00ff, offset);
+ outb((val >> 8) & 0x00ff, offset + 1);
+ }
-static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
-static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data);
+ /* Cache the last value for readback */
+ devpriv->ao_readback[chan] = val;
-/*---------------------------------------------------------------------------
- HELPER FUNCTION DECLARATIONS
------------------------------------------------------------------------------*/
+ return insn->n;
+}
-/* returns a maxdata value for a given n_bits */
-static inline unsigned int figure_out_maxdata(int bits)
+static int cb_pcimdda_ao_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
- return ((unsigned int)1 << bits) - 1;
-}
+ struct cb_pcimdda_private *devpriv = dev->private;
+ int chan = CR_CHAN(insn->chanspec);
+ unsigned long offset = dev->iobase + PCIMDDA_DA_CHAN(chan);
+ int i;
-/*
- * Probes for a supported device.
- *
- * Prerequisite: private be allocated already inside dev
- *
- * If the device is found, it returns 0 and has the following side effects:
- *
- * o assigns a struct pci_dev * to dev->private->pci_dev
- * o assigns a struct board * to dev->board_ptr
- * o sets dev->private->registers
- * o sets dev->private->dio_registers
- *
- * Otherwise, returns a -errno on error
- */
-static int probe(struct comedi_device *dev, const struct comedi_devconfig *it);
+ for (i = 0; i < insn->n; i++) {
+ /* Initiate the simultaneous transfer */
+ inw(offset);
+
+ data[i] = devpriv->ao_readback[chan];
+ }
-/*---------------------------------------------------------------------------
- FUNCTION DEFINITIONS
------------------------------------------------------------------------------*/
+ return insn->n;
+}
-/*
- * Attach is called by the Comedi core to configure the driver
- * for a particular board. If you specified a board_name array
- * in the driver structure, dev->board_ptr contains that
- * address.
- */
-static int attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static int cb_pcimdda_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
+ struct cb_pcimdda_private *devpriv;
struct comedi_subdevice *s;
- int err;
+ int ret;
-/*
- * Allocate the private structure area. alloc_private() is a
- * convenient macro defined in comedidev.h.
- * if this function fails (returns negative) then the private area is
- * kfree'd by comedi
- */
- if (alloc_private(dev, sizeof(struct board_private_struct)) < 0)
- return -ENOMEM;
+ comedi_set_hw_dev(dev, &pcidev->dev);
+ dev->board_name = dev->driver->driver_name;
-/*
- * If you can probe the device to determine what device in a series
- * it is, this is the place to do it. Otherwise, dev->board_ptr
- * should already be initialized.
- */
- err = probe(dev, it);
- if (err)
- return err;
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret)
+ return ret;
+ devpriv = dev->private;
-/* Output some info */
- printk("comedi%d: %s: ", dev->minor, thisboard->name);
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 3);
-/*
- * Initialize dev->board_name. Note that we can use the "thisboard"
- * macro now, since we just initialized it in the last line.
- */
- dev->board_name = thisboard->name;
-
- err = comedi_alloc_subdevices(dev, 2);
- if (err)
- return err;
-
- s = dev->subdevices + 0;
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
+ s = &dev->subdevices[0];
/* analog output subdevice */
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
- s->n_chan = thisboard->ao_chans;
- s->maxdata = figure_out_maxdata(thisboard->ao_bits);
- /* this is hard-coded here */
- if (it->options[2])
- s->range_table = &range_bipolar10;
- else
- s->range_table = &range_bipolar5;
- s->insn_write = &ao_winsn;
- s->insn_read = &ao_rinsn;
-
- s = dev->subdevices + 1;
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 6;
+ s->maxdata = 0xffff;
+ s->range_table = &range_bipolar5;
+ s->insn_write = cb_pcimdda_ao_winsn;
+ s->insn_read = cb_pcimdda_ao_rinsn;
+
+ s = &dev->subdevices[1];
/* digital i/o subdevice */
- if (thisboard->dio_chans) {
- switch (thisboard->dio_method) {
- case DIO_8255:
- /*
- * this is a straight 8255, so register us with
- * the 8255 driver
- */
- subdev_8255_init(dev, s, NULL, devpriv->dio_registers);
- devpriv->attached_to_8255 = 1;
- break;
- case DIO_INTERNAL:
- default:
- printk("DIO_INTERNAL not implemented yet!\n");
- return -ENXIO;
- break;
- }
- } else {
- s->type = COMEDI_SUBD_UNUSED;
- }
+ ret = subdev_8255_init(dev, s, NULL,
+ dev->iobase + PCIMDDA_8255_BASE_REG);
+ if (ret)
+ return ret;
- printk("attached\n");
+ dev_info(dev->class_dev, "%s attached\n", dev->board_name);
return 1;
}
-static void detach(struct comedi_device *dev)
-{
- if (devpriv) {
- if (dev->subdevices && devpriv->attached_to_8255) {
- subdev_8255_cleanup(dev, dev->subdevices + 2);
- devpriv->attached_to_8255 = 0;
- }
- if (devpriv->pci_dev) {
- if (devpriv->registers)
- comedi_pci_disable(devpriv->pci_dev);
- pci_dev_put(devpriv->pci_dev);
- }
- }
-}
-
-static int ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static void cb_pcimdda_detach(struct comedi_device *dev)
{
- int i;
- int chan = CR_CHAN(insn->chanspec);
- unsigned long offset = devpriv->registers + chan * 2;
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
- /* Writing a list of values to an AO channel is probably not
- * very useful, but that's how the interface is defined. */
- for (i = 0; i < insn->n; i++) {
- /* first, load the low byte */
- outb((char)(data[i] & 0x00ff), offset);
- /* next, write the high byte -- only after this is written is
- the channel voltage updated in the DAC, unless
- we're in simultaneous xfer mode (jumper on card)
- then a rinsn is necessary to actually update the DAC --
- see ao_rinsn() below... */
- outb((char)(data[i] >> 8 & 0x00ff), offset + 1);
-
- /* for testing only.. the actual rinsn SHOULD do an inw!
- (see the stuff about simultaneous XFER mode on this board) */
- devpriv->ao_readback[chan] = data[i];
+ if (dev->subdevices)
+ subdev_8255_cleanup(dev, &dev->subdevices[1]);
+ if (pcidev) {
+ if (dev->iobase)
+ comedi_pci_disable(pcidev);
}
-
- /* return the number of samples read/written */
- return i;
-}
-
-/* AO subdevices should have a read insn as well as a write insn.
-
- Usually this means copying a value stored in devpriv->ao_readback.
- However, since this board has this jumper setting called "Simultaneous
- Xfer mode" (off by default), we will support it. Simultaneaous xfer
- mode is accomplished by loading ALL the values you want for AO in all the
- channels, then READing off one of the AO registers to initiate the
- instantaneous simultaneous update of all DAC outputs, which makes
- all AO channels update simultaneously. This is useful for some control
- applications, I would imagine.
-*/
-static int ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
-{
- int i;
- int chan = CR_CHAN(insn->chanspec);
-
- for (i = 0; i < insn->n; i++) {
- inw(devpriv->registers + chan * 2);
- /*
- * should I set data[i] to the result of the actual read
- * on the register or the cached unsigned int in
- * devpriv->ao_readback[]?
- */
- data[i] = devpriv->ao_readback[chan];
- }
-
- return i;
-}
-
-/*---------------------------------------------------------------------------
- HELPER FUNCTION DEFINITIONS
------------------------------------------------------------------------------*/
-
-/*
- * Probes for a supported device.
- *
- * Prerequisite: private be allocated already inside dev
- *
- * If the device is found, it returns 0 and has the following side effects:
- *
- * o assigns a struct pci_dev * to dev->private->pci_dev
- * o assigns a struct board * to dev->board_ptr
- * o sets dev->private->registers
- * o sets dev->private->dio_registers
- *
- * Otherwise, returns a -errno on error
- */
-static int probe(struct comedi_device *dev, const struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev = NULL;
- int index;
- unsigned long registers;
-
- for_each_pci_dev(pcidev) {
- /* is it not a computer boards card? */
- if (pcidev->vendor != PCI_VENDOR_ID_COMPUTERBOARDS)
- continue;
- /* loop through cards supported by this driver */
- for (index = 0; index < ARRAY_SIZE(boards); index++) {
- if (boards[index].device_id != pcidev->device)
- continue;
- /* was a particular bus/slot requested? */
- if (it->options[0] || it->options[1]) {
- /* are we on the wrong bus/slot? */
- if (pcidev->bus->number != it->options[0] ||
- PCI_SLOT(pcidev->devfn) != it->options[1]) {
- continue;
- }
- }
- /* found ! */
-
- devpriv->pci_dev = pcidev;
- dev->board_ptr = boards + index;
- if (comedi_pci_enable(pcidev, thisboard->name)) {
- printk
- ("cb_pcimdda: Failed to enable PCI device and request regions\n");
- return -EIO;
- }
- registers =
- pci_resource_start(devpriv->pci_dev,
- REGS_BADRINDEX);
- devpriv->registers = registers;
- devpriv->dio_registers
- = devpriv->registers + thisboard->dio_offset;
- return 0;
- }
- }
-
- printk("cb_pcimdda: No supported ComputerBoards/MeasurementComputing "
- "card found at the requested position\n");
- return -ENODEV;
}
static struct comedi_driver cb_pcimdda_driver = {
.driver_name = "cb_pcimdda",
.module = THIS_MODULE,
- .attach = attach,
- .detach = detach,
+ .attach_pci = cb_pcimdda_attach_pci,
+ .detach = cb_pcimdda_detach,
};
static int __devinit cb_pcimdda_pci_probe(struct pci_dev *dev,
diff --git a/drivers/staging/comedi/drivers/comedi_bond.c b/drivers/staging/comedi/drivers/comedi_bond.c
index 5ed324c4f620..5c768bc76eb1 100644
--- a/drivers/staging/comedi/drivers/comedi_bond.c
+++ b/drivers/staging/comedi/drivers/comedi_bond.c
@@ -59,40 +59,6 @@ Configuration Options:
/* The maxiumum number of channels per subdevice. */
#define MAX_CHANS 256
-#define MODULE_NAME "comedi_bond"
-#ifndef STR
-# define STR1(x) #x
-# define STR(x) STR1(x)
-#endif
-
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "If true, print extra cryptic debugging output useful"
- "only to developers.");
-
-#define LOG_MSG(x...) printk(KERN_INFO MODULE_NAME": "x)
-#define DEBUG(x...) \
- do { \
- if (debug) \
- printk(KERN_DEBUG MODULE_NAME": DEBUG: "x); \
- } while (0)
-#define WARNING(x...) printk(KERN_WARNING MODULE_NAME ": WARNING: "x)
-#define ERROR(x...) printk(KERN_ERR MODULE_NAME ": INTERNAL ERROR: "x)
-
-/*
- * Board descriptions for two imaginary boards. Describing the
- * boards in this way is optional, and completely driver-dependent.
- * Some drivers use arrays such as this, other do not.
- */
-struct BondingBoard {
- const char *name;
-};
-
-/*
- * Useful for shorthand access to the particular board structure
- */
-#define thisboard ((const struct BondingBoard *)dev->board_ptr)
-
struct BondedDevice {
struct comedi_device *dev;
unsigned minor;
@@ -107,7 +73,7 @@ struct BondedDevice {
/* this structure is for data unique to this hardware driver. If
several hardware drivers keep similar information in this structure,
feel free to suggest moving the variable to the struct comedi_device struct. */
-struct Private {
+struct comedi_bond_private {
# define MAX_BOARD_NAME 256
char name[MAX_BOARD_NAME];
struct BondedDevice **devs;
@@ -116,12 +82,6 @@ struct Private {
unsigned nchans;
};
-/*
- * most drivers define the following macro to make it easy to
- * access the private structure.
- */
-#define devpriv ((struct Private *)dev->private)
-
/* DIO devices are slightly special. Although it is possible to
* implement the insn_read/insn_write interface, it is much more
* useful to applications if you implement the insn_bits interface.
@@ -131,6 +91,7 @@ static int bonding_dio_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct comedi_bond_private *devpriv = dev->private;
#define LSAMPL_BITS (sizeof(unsigned int)*8)
unsigned nchans = LSAMPL_BITS, num_done = 0, i;
@@ -177,6 +138,7 @@ static int bonding_dio_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct comedi_bond_private *devpriv = dev->private;
int chan = CR_CHAN(insn->chanspec), ret, io_bits = s->io_bits;
unsigned int io;
struct BondedDevice *bdev;
@@ -230,6 +192,7 @@ static void *Realloc(const void *oldmem, size_t newlen, size_t oldlen)
static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
{
+ struct comedi_bond_private *devpriv = dev->private;
int i;
struct comedi_device *devs_opened[COMEDI_NUM_BOARD_MINORS];
@@ -245,15 +208,18 @@ static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
struct BondedDevice *bdev = NULL;
if (minor < 0 || minor >= COMEDI_NUM_BOARD_MINORS) {
- ERROR("Minor %d is invalid!\n", minor);
+ dev_err(dev->class_dev,
+ "Minor %d is invalid!\n", minor);
return 0;
}
if (minor == dev->minor) {
- ERROR("Cannot bond this driver to itself!\n");
+ dev_err(dev->class_dev,
+ "Cannot bond this driver to itself!\n");
return 0;
}
if (devs_opened[minor]) {
- ERROR("Minor %d specified more than once!\n", minor);
+ dev_err(dev->class_dev,
+ "Minor %d specified more than once!\n", minor);
return 0;
}
@@ -263,7 +229,8 @@ static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
d = devs_opened[minor] = comedi_open(file);
if (!d) {
- ERROR("Minor %u could not be opened\n", minor);
+ dev_err(dev->class_dev,
+ "Minor %u could not be opened\n", minor);
return 0;
}
@@ -272,14 +239,14 @@ static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
sdev + 1)) > -1) {
nchans = comedi_get_n_channels(d, sdev);
if (nchans <= 0) {
- ERROR("comedi_get_n_channels() returned %d "
- "on minor %u subdev %d!\n",
- nchans, minor, sdev);
+ dev_err(dev->class_dev,
+ "comedi_get_n_channels() returned %d on minor %u subdev %d!\n",
+ nchans, minor, sdev);
return 0;
}
bdev = kmalloc(sizeof(*bdev), GFP_KERNEL);
if (!bdev) {
- ERROR("Out of memory.\n");
+ dev_err(dev->class_dev, "Out of memory\n");
return 0;
}
bdev->dev = d;
@@ -302,8 +269,8 @@ static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
Realloc(devpriv->devs,
++devpriv->ndevs * sizeof(bdev), tmp);
if (!devpriv->devs) {
- ERROR("Could not allocate memory. "
- "Out of memory?");
+ dev_err(dev->class_dev,
+ "Could not allocate memory. Out of memory?\n");
return 0;
}
@@ -323,7 +290,7 @@ static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
}
if (!devpriv->nchans) {
- ERROR("No channels found!\n");
+ dev_err(dev->class_dev, "No channels found!\n");
return 0;
}
@@ -333,35 +300,28 @@ static int doDevConfig(struct comedi_device *dev, struct comedi_devconfig *it)
static int bonding_attach(struct comedi_device *dev,
struct comedi_devconfig *it)
{
+ struct comedi_bond_private *devpriv;
struct comedi_subdevice *s;
int ret;
- LOG_MSG("comedi%d\n", dev->minor);
-
- /*
- * Allocate the private structure area. alloc_private() is a
- * convenient macro defined in comedidev.h.
- */
- if (alloc_private(dev, sizeof(struct Private)) < 0)
- return -ENOMEM;
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret)
+ return ret;
+ devpriv = dev->private;
/*
- * Setup our bonding from config params.. sets up our Private struct..
+ * Setup our bonding from config params.. sets up our private struct..
*/
if (!doDevConfig(dev, it))
return -EINVAL;
- /*
- * Initialize dev->board_name. Note that we can use the "thisboard"
- * macro now, since we just initialized it in the last line.
- */
dev->board_name = devpriv->name;
ret = comedi_alloc_subdevices(dev, 1);
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = devpriv->nchans;
@@ -370,9 +330,9 @@ static int bonding_attach(struct comedi_device *dev,
s->insn_bits = bonding_dio_insn_bits;
s->insn_config = bonding_dio_insn_config;
- LOG_MSG("attached with %u DIO channels coming from %u different "
- "subdevices all bonded together. "
- "John Lennon would be proud!\n",
+ dev_info(dev->class_dev,
+ "%s: %s attached, %u channels from %u devices\n",
+ dev->driver->driver_name, dev->board_name,
devpriv->nchans, devpriv->ndevs);
return 1;
@@ -380,6 +340,7 @@ static int bonding_attach(struct comedi_device *dev,
static void bonding_detach(struct comedi_device *dev)
{
+ struct comedi_bond_private *devpriv = dev->private;
unsigned long devs_closed = 0;
if (devpriv) {
@@ -402,25 +363,16 @@ static void bonding_detach(struct comedi_device *dev)
}
}
-static const struct BondingBoard bondingBoards[] = {
- {
- .name = "comedi_bond",
- },
-};
-
static struct comedi_driver bonding_driver = {
.driver_name = "comedi_bond",
.module = THIS_MODULE,
.attach = bonding_attach,
.detach = bonding_detach,
- .board_name = &bondingBoards[0].name,
- .offset = sizeof(struct BondingBoard),
- .num_names = ARRAY_SIZE(bondingBoards),
};
module_comedi_driver(bonding_driver);
MODULE_AUTHOR("Calin A. Culianu");
-MODULE_DESCRIPTION(MODULE_NAME "A driver for COMEDI to bond multiple COMEDI "
+MODULE_DESCRIPTION("comedi_bond: A driver for COMEDI to bond multiple COMEDI "
"devices together as one. In the words of John Lennon: "
"'And the world will live as one...'");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/comedi_fc.h b/drivers/staging/comedi/drivers/comedi_fc.h
index 4b2cfd327995..94481c637a0a 100644
--- a/drivers/staging/comedi/drivers/comedi_fc.h
+++ b/drivers/staging/comedi/drivers/comedi_fc.h
@@ -73,4 +73,36 @@ static inline unsigned int cfc_bytes_per_scan(struct comedi_subdevice *subd)
return num_samples * bytes_per_sample(subd);
}
+/**
+ * cfc_check_trigger_src() - trivially validate a comedi_cmd trigger source
+ * @src: pointer to the trigger source to validate
+ * @flags: bitmask of valid TRIG_* for the trigger
+ *
+ * This is used in "step 1" of the do_cmdtest functions of comedi drivers
+ * to vaildate the comedi_cmd triggers. The mask of the @src against the
+ * @flags allows the userspace comedilib to pass all the comedi_cmd
+ * triggers as TRIG_ANY and get back a bitmask of the valid trigger sources.
+ */
+static inline int cfc_check_trigger_src(unsigned int *src, unsigned int flags)
+{
+ unsigned int orig_src = *src;
+
+ *src = orig_src & flags;
+ if (*src == TRIG_INVALID || *src != orig_src)
+ return -EINVAL;
+ return 0;
+}
+
+/**
+ * cfc_check_trigger_is_unique() - make sure a trigger source is unique
+ * @src: the trigger source to check
+ */
+static inline int cfc_check_trigger_is_unique(unsigned int src)
+{
+ /* this test is true if more than one _src bit is set */
+ if ((src & (src - 1)) != 0)
+ return -EINVAL;
+ return 0;
+}
+
#endif /* _COMEDI_FC_H */
diff --git a/drivers/staging/comedi/drivers/comedi_parport.c b/drivers/staging/comedi/drivers/comedi_parport.c
index 9a63cac2434a..22ef94242590 100644
--- a/drivers/staging/comedi/drivers/comedi_parport.c
+++ b/drivers/staging/comedi/drivers/comedi_parport.c
@@ -85,6 +85,8 @@ pin, which can be used to wake up tasks.
#include <linux/interrupt.h>
#include <linux/ioport.h>
+#include "comedi_fc.h"
+
#define PARPORT_SIZE 3
#define PARPORT_A 0
@@ -96,11 +98,12 @@ struct parport_private {
unsigned int c_data;
int enable_irq;
};
-#define devpriv ((struct parport_private *)(dev->private))
static int parport_insn_a(struct comedi_device *dev, struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct parport_private *devpriv = dev->private;
+
if (data[0]) {
devpriv->a_data &= ~data[0];
devpriv->a_data |= (data[0] & data[1]);
@@ -117,6 +120,8 @@ static int parport_insn_config_a(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct parport_private *devpriv = dev->private;
+
if (data[0]) {
s->io_bits = 0xff;
devpriv->c_data &= ~(1 << 5);
@@ -145,6 +150,8 @@ static int parport_insn_b(struct comedi_device *dev, struct comedi_subdevice *s,
static int parport_insn_c(struct comedi_device *dev, struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct parport_private *devpriv = dev->private;
+
data[0] &= 0x0f;
if (data[0]) {
devpriv->c_data &= ~data[0];
@@ -171,39 +178,20 @@ static int parport_intr_cmdtest(struct comedi_device *dev,
struct comedi_cmd *cmd)
{
int err = 0;
- int tmp;
-
- /* step 1 */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_FOLLOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
if (err)
return 1;
- /* step 2: ignored */
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -245,6 +233,8 @@ static int parport_intr_cmdtest(struct comedi_device *dev,
static int parport_intr_cmd(struct comedi_device *dev,
struct comedi_subdevice *s)
{
+ struct parport_private *devpriv = dev->private;
+
devpriv->c_data |= 0x10;
outb(devpriv->c_data, dev->iobase + PARPORT_C);
@@ -256,7 +246,7 @@ static int parport_intr_cmd(struct comedi_device *dev,
static int parport_intr_cancel(struct comedi_device *dev,
struct comedi_subdevice *s)
{
- printk(KERN_DEBUG "parport_intr_cancel()\n");
+ struct parport_private *devpriv = dev->private;
devpriv->c_data &= ~0x10;
outb(devpriv->c_data, dev->iobase + PARPORT_C);
@@ -269,12 +259,11 @@ static int parport_intr_cancel(struct comedi_device *dev,
static irqreturn_t parport_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 3;
+ struct parport_private *devpriv = dev->private;
+ struct comedi_subdevice *s = &dev->subdevices[3];
- if (!devpriv->enable_irq) {
- printk(KERN_ERR "comedi_parport: bogus irq, ignored\n");
+ if (!devpriv->enable_irq)
return IRQ_NONE;
- }
comedi_buf_put(s->async, 0);
s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOS;
@@ -286,41 +275,42 @@ static irqreturn_t parport_interrupt(int irq, void *d)
static int parport_attach(struct comedi_device *dev,
struct comedi_devconfig *it)
{
+ struct parport_private *devpriv;
int ret;
unsigned int irq;
unsigned long iobase;
struct comedi_subdevice *s;
+ dev->board_name = dev->driver->driver_name;
+
iobase = it->options[0];
- printk(KERN_INFO "comedi%d: parport: 0x%04lx ", dev->minor, iobase);
- if (!request_region(iobase, PARPORT_SIZE, "parport (comedi)")) {
- printk(KERN_ERR "I/O port conflict\n");
+ if (!request_region(iobase, PARPORT_SIZE, dev->board_name)) {
+ dev_err(dev->class_dev, "I/O port conflict\n");
return -EIO;
}
dev->iobase = iobase;
irq = it->options[1];
if (irq) {
- printk(KERN_INFO " irq=%u", irq);
- ret = request_irq(irq, parport_interrupt, 0, "comedi_parport",
+ ret = request_irq(irq, parport_interrupt, 0, dev->board_name,
dev);
if (ret < 0) {
- printk(KERN_ERR " irq not available\n");
+ dev_err(dev->class_dev, "irq not available\n");
return -EINVAL;
}
dev->irq = irq;
}
- dev->board_name = "parport";
ret = comedi_alloc_subdevices(dev, 4);
if (ret)
return ret;
- ret = alloc_private(dev, sizeof(struct parport_private));
+ ret = alloc_private(dev, sizeof(*devpriv));
if (ret < 0)
return ret;
+ devpriv = dev->private;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 8;
@@ -329,7 +319,7 @@ static int parport_attach(struct comedi_device *dev,
s->insn_bits = parport_insn_a;
s->insn_config = parport_insn_config_a;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = 5;
@@ -337,7 +327,7 @@ static int parport_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->insn_bits = parport_insn_b;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
s->n_chan = 4;
@@ -345,7 +335,7 @@ static int parport_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->insn_bits = parport_insn_c;
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
if (irq) {
dev->read_subdev = s;
s->type = COMEDI_SUBD_DI;
@@ -366,8 +356,10 @@ static int parport_attach(struct comedi_device *dev,
devpriv->c_data = 0;
outb(devpriv->c_data, dev->iobase + PARPORT_C);
- printk(KERN_INFO "\n");
- return 1;
+ dev_info(dev->class_dev, "%s: iobase=0x%04lx, irq %sabled",
+ dev->board_name, dev->iobase, dev->irq ? "en" : "dis");
+
+ return 0;
}
static void parport_detach(struct comedi_device *dev)
diff --git a/drivers/staging/comedi/drivers/comedi_test.c b/drivers/staging/comedi/drivers/comedi_test.c
index 523a809708b7..7817def1556c 100644
--- a/drivers/staging/comedi/drivers/comedi_test.c
+++ b/drivers/staging/comedi/drivers/comedi_test.c
@@ -57,14 +57,6 @@ zero volts).
#include "comedi_fc.h"
#include <linux/timer.h>
-/* Board descriptions */
-struct waveform_board {
- const char *name;
- int ai_chans;
- int ai_bits;
- int have_dio;
-};
-
#define N_CHANS 8
/* Data unique to this driver */
@@ -81,7 +73,6 @@ struct waveform_private {
unsigned timer_running:1;
unsigned int ao_loopbacks[N_CHANS];
};
-#define devpriv ((struct waveform_private *)dev->private)
/* 1000 nanosec in a microsec */
static const int nano_per_micro = 1000;
@@ -98,6 +89,7 @@ static const struct comedi_lrange waveform_ai_ranges = {
static short fake_sawtooth(struct comedi_device *dev, unsigned int range_index,
unsigned long current_time)
{
+ struct waveform_private *devpriv = dev->private;
struct comedi_subdevice *s = dev->read_subdev;
unsigned int offset = s->maxdata / 2;
u64 value;
@@ -122,6 +114,7 @@ static short fake_squarewave(struct comedi_device *dev,
unsigned int range_index,
unsigned long current_time)
{
+ struct waveform_private *devpriv = dev->private;
struct comedi_subdevice *s = dev->read_subdev;
unsigned int offset = s->maxdata / 2;
u64 value;
@@ -175,6 +168,7 @@ static short fake_waveform(struct comedi_device *dev, unsigned int channel,
static void waveform_ai_interrupt(unsigned long arg)
{
struct comedi_device *dev = (struct comedi_device *)arg;
+ struct waveform_private *devpriv = dev->private;
struct comedi_async *async = dev->read_subdev->async;
struct comedi_cmd *cmd = &async->cmd;
unsigned int i, j;
@@ -237,44 +231,23 @@ static int waveform_ai_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW | TRIG_TIMER;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW | TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources are unique and mutually compatible
- */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->convert_src != TRIG_NOW && cmd->convert_src != TRIG_TIMER)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -362,6 +335,7 @@ static int waveform_ai_cmdtest(struct comedi_device *dev,
static int waveform_ai_cmd(struct comedi_device *dev,
struct comedi_subdevice *s)
{
+ struct waveform_private *devpriv = dev->private;
struct comedi_cmd *cmd = &s->async->cmd;
if (cmd->flags & TRIG_RT) {
@@ -395,6 +369,8 @@ static int waveform_ai_cmd(struct comedi_device *dev,
static int waveform_ai_cancel(struct comedi_device *dev,
struct comedi_subdevice *s)
{
+ struct waveform_private *devpriv = dev->private;
+
devpriv->timer_running = 0;
del_timer(&devpriv->timer);
return 0;
@@ -404,6 +380,7 @@ static int waveform_ai_insn_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct waveform_private *devpriv = dev->private;
int i, chan = CR_CHAN(insn->chanspec);
for (i = 0; i < insn->n; i++)
@@ -416,6 +393,7 @@ static int waveform_ao_insn_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct waveform_private *devpriv = dev->private;
int i, chan = CR_CHAN(insn->chanspec);
for (i = 0; i < insn->n; i++)
@@ -427,17 +405,19 @@ static int waveform_ao_insn_write(struct comedi_device *dev,
static int waveform_attach(struct comedi_device *dev,
struct comedi_devconfig *it)
{
- const struct waveform_board *board = comedi_board(dev);
+ struct waveform_private *devpriv;
struct comedi_subdevice *s;
int amplitude = it->options[0];
int period = it->options[1];
int i;
int ret;
- dev->board_name = board->name;
+ dev->board_name = dev->driver->driver_name;
- if (alloc_private(dev, sizeof(struct waveform_private)) < 0)
- return -ENOMEM;
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret < 0)
+ return ret;
+ devpriv = dev->private;
/* set default amplitude and period */
if (amplitude <= 0)
@@ -452,13 +432,13 @@ static int waveform_attach(struct comedi_device *dev,
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
/* analog input subdevice */
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
- s->n_chan = board->ai_chans;
- s->maxdata = (1 << board->ai_bits) - 1;
+ s->n_chan = N_CHANS;
+ s->maxdata = 0xffff;
s->range_table = &waveform_ai_ranges;
s->len_chanlist = s->n_chan * 2;
s->insn_read = waveform_ai_insn_read;
@@ -466,13 +446,13 @@ static int waveform_attach(struct comedi_device *dev,
s->do_cmdtest = waveform_ai_cmdtest;
s->cancel = waveform_ai_cancel;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
dev->write_subdev = s;
/* analog output subdevice (loopback) */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
- s->n_chan = board->ai_chans;
- s->maxdata = (1 << board->ai_bits) - 1;
+ s->n_chan = N_CHANS;
+ s->maxdata = 0xffff;
s->range_table = &waveform_ai_ranges;
s->len_chanlist = s->n_chan * 2;
s->insn_write = waveform_ao_insn_write;
@@ -488,35 +468,27 @@ static int waveform_attach(struct comedi_device *dev,
devpriv->timer.function = waveform_ai_interrupt;
devpriv->timer.data = (unsigned long)dev;
- printk(KERN_INFO "comedi%d: comedi_test: "
- "%i microvolt, %li microsecond waveform attached\n", dev->minor,
- devpriv->uvolt_amplitude, devpriv->usec_period);
- return 1;
+ dev_info(dev->class_dev,
+ "%s: %i microvolt, %li microsecond waveform attached\n",
+ dev->board_name,
+ devpriv->uvolt_amplitude, devpriv->usec_period);
+
+ return 0;
}
static void waveform_detach(struct comedi_device *dev)
{
- if (dev->private)
+ struct waveform_private *devpriv = dev->private;
+
+ if (devpriv)
waveform_ai_cancel(dev, dev->read_subdev);
}
-static const struct waveform_board waveform_boards[] = {
- {
- .name = "comedi_test",
- .ai_chans = N_CHANS,
- .ai_bits = 16,
- .have_dio = 0,
- },
-};
-
static struct comedi_driver waveform_driver = {
.driver_name = "comedi_test",
.module = THIS_MODULE,
.attach = waveform_attach,
.detach = waveform_detach,
- .board_name = &waveform_boards[0].name,
- .offset = sizeof(struct waveform_board),
- .num_names = ARRAY_SIZE(waveform_boards),
};
module_comedi_driver(waveform_driver);
diff --git a/drivers/staging/comedi/drivers/contec_pci_dio.c b/drivers/staging/comedi/drivers/contec_pci_dio.c
index 944cfeeb2b2d..178a6a4bb7d5 100644
--- a/drivers/staging/comedi/drivers/contec_pci_dio.c
+++ b/drivers/staging/comedi/drivers/contec_pci_dio.c
@@ -27,51 +27,35 @@ Author: Stefano Rivoir <s.rivoir@gts.it>
Updated: Wed, 27 Jun 2007 13:00:06 +0100
Status: works
-Configuration Options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first supported
- PCI device found will be used.
+Configuration Options: not applicable, uses comedi PCI auto config
*/
#include "../comedidev.h"
-enum contec_model {
- PIO1616L = 0,
-};
-
-struct contec_board {
- const char *name;
- int model;
- int in_ports;
- int out_ports;
- int in_offs;
- int out_offs;
- int out_boffs;
-};
-static const struct contec_board contec_boards[] = {
- {"PIO1616L", PIO1616L, 16, 16, 0, 2, 10},
-};
-
#define PCI_DEVICE_ID_PIO1616L 0x8172
-#define thisboard ((const struct contec_board *)dev->board_ptr)
+/*
+ * Register map
+ */
+#define PIO1616L_DI_REG 0x00
+#define PIO1616L_DO_REG 0x02
static int contec_do_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ unsigned int mask = data[0];
+ unsigned int bits = data[1];
- dev_dbg(dev->class_dev, "contec_do_insn_bits called\n");
- dev_dbg(dev->class_dev, "data: %d %d\n", data[0], data[1]);
+ if (mask) {
+ s->state &= ~mask;
+ s->state |= (bits & mask);
- if (data[0]) {
- s->state &= ~data[0];
- s->state |= data[0] & data[1];
- dev_dbg(dev->class_dev, "out: %d on %lx\n", s->state,
- dev->iobase + thisboard->out_offs);
- outw(s->state, dev->iobase + thisboard->out_offs);
+ outw(s->state, dev->iobase + PIO1616L_DO_REG);
}
+
+ data[1] = s->state;
+
return insn->n;
}
@@ -79,87 +63,49 @@ static int contec_di_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
-
- dev_dbg(dev->class_dev, "contec_di_insn_bits called\n");
- dev_dbg(dev->class_dev, "data: %d %d\n", data[0], data[1]);
-
- data[1] = inw(dev->iobase + thisboard->in_offs);
+ data[1] = inw(dev->iobase + PIO1616L_DI_REG);
return insn->n;
}
-static struct pci_dev *contec_find_pci_dev(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
-
- for_each_pci_dev(pcidev) {
- if (bus || slot) {
- if (bus != pcidev->bus->number ||
- slot != PCI_SLOT(pcidev->devfn))
- continue;
- }
- if (pcidev->vendor != PCI_VENDOR_ID_CONTEC ||
- pcidev->device != PCI_DEVICE_ID_PIO1616L)
- continue;
-
- dev->board_ptr = contec_boards + 0;
- return pcidev;
- }
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
- return NULL;
-}
-
-static int contec_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static int contec_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- struct pci_dev *pcidev;
struct comedi_subdevice *s;
int ret;
- printk("comedi%d: contec: ", dev->minor);
+ comedi_set_hw_dev(dev, &pcidev->dev);
- dev->board_name = thisboard->name;
+ dev->board_name = dev->driver->driver_name;
- ret = comedi_alloc_subdevices(dev, 2);
+ ret = comedi_pci_enable(pcidev, dev->board_name);
if (ret)
return ret;
-
- pcidev = contec_find_pci_dev(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
-
- if (comedi_pci_enable(pcidev, "contec_pci_dio")) {
- printk("error enabling PCI device and request regions!\n");
- return -EIO;
- }
dev->iobase = pci_resource_start(pcidev, 0);
- printk(" base addr %lx ", dev->iobase);
- s = dev->subdevices + 0;
-
- s->type = COMEDI_SUBD_DI;
- s->subdev_flags = SDF_READABLE;
- s->n_chan = 16;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = contec_di_insn_bits;
-
- s = dev->subdevices + 1;
- s->type = COMEDI_SUBD_DO;
- s->subdev_flags = SDF_WRITABLE;
- s->n_chan = 16;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = contec_do_insn_bits;
-
- printk("attached\n");
+ ret = comedi_alloc_subdevices(dev, 2);
+ if (ret)
+ return ret;
- return 1;
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = contec_di_insn_bits;
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = contec_do_insn_bits;
+
+ dev_info(dev->class_dev, "%s attached\n", dev->board_name);
+
+ return 0;
}
static void contec_detach(struct comedi_device *dev)
@@ -169,14 +115,13 @@ static void contec_detach(struct comedi_device *dev)
if (pcidev) {
if (dev->iobase)
comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
}
}
static struct comedi_driver contec_pci_dio_driver = {
.driver_name = "contec_pci_dio",
.module = THIS_MODULE,
- .attach = contec_attach,
+ .attach_pci = contec_attach_pci,
.detach = contec_detach,
};
@@ -192,8 +137,7 @@ static void __devexit contec_pci_dio_pci_remove(struct pci_dev *dev)
}
static DEFINE_PCI_DEVICE_TABLE(contec_pci_dio_pci_table) = {
- { PCI_DEVICE(PCI_VENDOR_ID_CONTEC, PCI_DEVICE_ID_PIO1616L),
- .driver_data = PIO1616L },
+ { PCI_DEVICE(PCI_VENDOR_ID_CONTEC, PCI_DEVICE_ID_PIO1616L) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, contec_pci_dio_pci_table);
diff --git a/drivers/staging/comedi/drivers/daqboard2000.c b/drivers/staging/comedi/drivers/daqboard2000.c
index cad559a1a730..d13c8c5822bf 100644
--- a/drivers/staging/comedi/drivers/daqboard2000.c
+++ b/drivers/staging/comedi/drivers/daqboard2000.c
@@ -31,16 +31,10 @@ Devices: [IOTech] DAQBoard/2000 (daqboard2000)
Much of the functionality of this driver was determined from reading
the source code for the Windows driver.
-The FPGA on the board requires initialization code, which can
-be loaded by comedi_config using the -i
-option. The initialization code is available from http://www.comedi.org
-in the comedi_nonfree_firmware tarball.
-
-Configuration options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first supported
- PCI device found will be used.
+The FPGA on the board requires fimware, which is available from
+http://www.comedi.org in the comedi_nonfree_firmware tarball.
+
+Configuration options: not applicable, uses PCI auto config
*/
/*
This card was obviously never intended to leave the Windows world,
@@ -117,17 +111,17 @@ Configuration options:
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/firmware.h>
#include "8255.h"
+#define DAQBOARD2000_FIRMWARE "daqboard2000_firmware.bin"
+
#define PCI_VENDOR_ID_IOTECH 0x1616
#define DAQBOARD2000_SUBSYSTEM_IDS2 0x0002 /* Daqboard/2000 - 2 Dacs */
#define DAQBOARD2000_SUBSYSTEM_IDS4 0x0004 /* Daqboard/2000 - 4 Dacs */
-#define DAQBOARD2000_DAQ_SIZE 0x1002
-#define DAQBOARD2000_PLX_SIZE 0x100
-
/* Initialization bits for the Serial EEPROM Control Register */
#define DAQBOARD2000_SECRProgPinHi 0x8001767e
#define DAQBOARD2000_SECRProgPinLo 0x8000767e
@@ -143,85 +137,56 @@ Configuration options:
#define DAQBOARD2000_CPLD_INIT 0x0002
#define DAQBOARD2000_CPLD_DONE 0x0004
-/* Available ranges */
-static const struct comedi_lrange range_daqboard2000_ai = { 13, {
- RANGE(-10, 10),
- RANGE(-5, 5),
- RANGE(-2.5,
- 2.5),
- RANGE(-1.25,
- 1.25),
- RANGE(-0.625,
- 0.625),
- RANGE(-0.3125,
- 0.3125),
- RANGE(-0.156,
- 0.156),
- RANGE(0, 10),
- RANGE(0, 5),
- RANGE(0, 2.5),
- RANGE(0, 1.25),
- RANGE(0,
- 0.625),
- RANGE(0,
- 0.3125)
- }
-};
-
-static const struct comedi_lrange range_daqboard2000_ao = { 1, {
- RANGE(-10, 10)
- }
+static const struct comedi_lrange range_daqboard2000_ai = {
+ 13, {
+ BIP_RANGE(10),
+ BIP_RANGE(5),
+ BIP_RANGE(2.5),
+ BIP_RANGE(1.25),
+ BIP_RANGE(0.625),
+ BIP_RANGE(0.3125),
+ BIP_RANGE(0.156),
+ UNI_RANGE(10),
+ UNI_RANGE(5),
+ UNI_RANGE(2.5),
+ UNI_RANGE(1.25),
+ UNI_RANGE(0.625),
+ UNI_RANGE(0.3125)
+ }
};
-struct daqboard2000_hw {
- volatile u16 acqControl; /* 0x00 */
- volatile u16 acqScanListFIFO; /* 0x02 */
- volatile u32 acqPacerClockDivLow; /* 0x04 */
-
- volatile u16 acqScanCounter; /* 0x08 */
- volatile u16 acqPacerClockDivHigh; /* 0x0a */
- volatile u16 acqTriggerCount; /* 0x0c */
- volatile u16 fill2; /* 0x0e */
- volatile u16 acqResultsFIFO; /* 0x10 */
- volatile u16 fill3; /* 0x12 */
- volatile u16 acqResultsShadow; /* 0x14 */
- volatile u16 fill4; /* 0x16 */
- volatile u16 acqAdcResult; /* 0x18 */
- volatile u16 fill5; /* 0x1a */
- volatile u16 dacScanCounter; /* 0x1c */
- volatile u16 fill6; /* 0x1e */
-
- volatile u16 dacControl; /* 0x20 */
- volatile u16 fill7; /* 0x22 */
- volatile s16 dacFIFO; /* 0x24 */
- volatile u16 fill8[2]; /* 0x26 */
- volatile u16 dacPacerClockDiv; /* 0x2a */
- volatile u16 refDacs; /* 0x2c */
- volatile u16 fill9; /* 0x2e */
-
- volatile u16 dioControl; /* 0x30 */
- volatile s16 dioP3hsioData; /* 0x32 */
- volatile u16 dioP3Control; /* 0x34 */
- volatile u16 calEepromControl; /* 0x36 */
- volatile s16 dacSetting[4]; /* 0x38 */
- volatile s16 dioP2ExpansionIO8Bit[32]; /* 0x40 */
-
- volatile u16 ctrTmrControl; /* 0x80 */
- volatile u16 fill10[3]; /* 0x82 */
- volatile s16 ctrInput[4]; /* 0x88 */
- volatile u16 fill11[8]; /* 0x90 */
- volatile u16 timerDivisor[2]; /* 0xa0 */
- volatile u16 fill12[6]; /* 0xa4 */
-
- volatile u16 dmaControl; /* 0xb0 */
- volatile u16 trigControl; /* 0xb2 */
- volatile u16 fill13[2]; /* 0xb4 */
- volatile u16 calEeprom; /* 0xb8 */
- volatile u16 acqDigitalMark; /* 0xba */
- volatile u16 trigDacs; /* 0xbc */
- volatile u16 fill14; /* 0xbe */
- volatile s16 dioP2ExpansionIO16Bit[32]; /* 0xc0 */
-};
+/*
+ * Register Memory Map
+ */
+#define acqControl 0x00 /* u16 */
+#define acqScanListFIFO 0x02 /* u16 */
+#define acqPacerClockDivLow 0x04 /* u32 */
+#define acqScanCounter 0x08 /* u16 */
+#define acqPacerClockDivHigh 0x0a /* u16 */
+#define acqTriggerCount 0x0c /* u16 */
+#define acqResultsFIFO 0x10 /* u16 */
+#define acqResultsShadow 0x14 /* u16 */
+#define acqAdcResult 0x18 /* u16 */
+#define dacScanCounter 0x1c /* u16 */
+#define dacControl 0x20 /* u16 */
+#define dacFIFO 0x24 /* s16 */
+#define dacPacerClockDiv 0x2a /* u16 */
+#define refDacs 0x2c /* u16 */
+#define dioControl 0x30 /* u16 */
+#define dioP3hsioData 0x32 /* s16 */
+#define dioP3Control 0x34 /* u16 */
+#define calEepromControl 0x36 /* u16 */
+#define dacSetting(x) (0x38 + (x)*2) /* s16 */
+#define dioP2ExpansionIO8Bit 0x40 /* s16 */
+#define ctrTmrControl 0x80 /* u16 */
+#define ctrInput(x) (0x88 + (x)*2) /* s16 */
+#define timerDivisor(x) (0xa0 + (x)*2) /* u16 */
+#define dmaControl 0xb0 /* u16 */
+#define trigControl 0xb2 /* u16 */
+#define calEeprom 0xb8 /* u16 */
+#define acqDigitalMark 0xba /* u16 */
+#define trigDacs 0xbc /* u16 */
+#define dioP2ExpansionIO16Bit(x) (0xc0 + (x)*2) /* s16 */
/* Scan Sequencer programming */
#define DAQBOARD2000_SeqStartScanList 0x0011
@@ -311,27 +276,23 @@ static const struct daq200_boardtype boardtypes[] = {
{"ids4", DAQBOARD2000_SUBSYSTEM_IDS4},
};
-#define this_board ((const struct daq200_boardtype *)dev->board_ptr)
-
struct daqboard2000_private {
enum {
card_daqboard_2000
} card;
- void *daq;
+ void __iomem *daq;
void __iomem *plx;
unsigned int ao_readback[2];
};
-#define devpriv ((struct daqboard2000_private *)dev->private)
-
static void writeAcqScanListEntry(struct comedi_device *dev, u16 entry)
{
- struct daqboard2000_hw *fpga = devpriv->daq;
+ struct daqboard2000_private *devpriv = dev->private;
-/* udelay(4); */
- fpga->acqScanListFIFO = entry & 0x00ff;
-/* udelay(4); */
- fpga->acqScanListFIFO = (entry >> 8) & 0x00ff;
+ /* udelay(4); */
+ writew(entry & 0x00ff, devpriv->daq + acqScanListFIFO);
+ /* udelay(4); */
+ writew((entry >> 8) & 0x00ff, devpriv->daq + acqScanListFIFO);
}
static void setup_sampling(struct comedi_device *dev, int chan, int gain)
@@ -372,7 +333,6 @@ static void setup_sampling(struct comedi_device *dev, int chan, int gain)
/* These should be read from EEPROM */
word2 |= 0x0800;
word3 |= 0xc000;
-/* printk("%d %4.4x %4.4x %4.4x %4.4x\n", chan, word0, word1, word2, word3);*/
writeAcqScanListEntry(dev, word0);
writeAcqScanListEntry(dev, word1);
writeAcqScanListEntry(dev, word2);
@@ -384,21 +344,22 @@ static int daqboard2000_ai_insn_read(struct comedi_device *dev,
struct comedi_insn *insn,
unsigned int *data)
{
- int i;
- struct daqboard2000_hw *fpga = devpriv->daq;
+ struct daqboard2000_private *devpriv = dev->private;
+ unsigned int val;
int gain, chan, timeout;
+ int i;
- fpga->acqControl =
- DAQBOARD2000_AcqResetScanListFifo |
- DAQBOARD2000_AcqResetResultsFifo | DAQBOARD2000_AcqResetConfigPipe;
+ writew(DAQBOARD2000_AcqResetScanListFifo |
+ DAQBOARD2000_AcqResetResultsFifo |
+ DAQBOARD2000_AcqResetConfigPipe, devpriv->daq + acqControl);
/*
* If pacer clock is not set to some high value (> 10 us), we
* risk multiple samples to be put into the result FIFO.
*/
/* 1 second, should be long enough */
- fpga->acqPacerClockDivLow = 1000000;
- fpga->acqPacerClockDivHigh = 0;
+ writel(1000000, devpriv->daq + acqPacerClockDivLow);
+ writew(0, devpriv->daq + acqPacerClockDivHigh);
gain = CR_RANGE(insn->chanspec);
chan = CR_CHAN(insn->chanspec);
@@ -410,28 +371,30 @@ static int daqboard2000_ai_insn_read(struct comedi_device *dev,
for (i = 0; i < insn->n; i++) {
setup_sampling(dev, chan, gain);
/* Enable reading from the scanlist FIFO */
- fpga->acqControl = DAQBOARD2000_SeqStartScanList;
+ writew(DAQBOARD2000_SeqStartScanList,
+ devpriv->daq + acqControl);
for (timeout = 0; timeout < 20; timeout++) {
- if (fpga->acqControl & DAQBOARD2000_AcqConfigPipeFull)
+ val = readw(devpriv->daq + acqControl);
+ if (val & DAQBOARD2000_AcqConfigPipeFull)
break;
/* udelay(2); */
}
- fpga->acqControl = DAQBOARD2000_AdcPacerEnable;
+ writew(DAQBOARD2000_AdcPacerEnable, devpriv->daq + acqControl);
for (timeout = 0; timeout < 20; timeout++) {
- if (fpga->acqControl & DAQBOARD2000_AcqLogicScanning)
+ val = readw(devpriv->daq + acqControl);
+ if (val & DAQBOARD2000_AcqLogicScanning)
break;
/* udelay(2); */
}
for (timeout = 0; timeout < 20; timeout++) {
- if (fpga->acqControl &
- DAQBOARD2000_AcqResultsFIFOHasValidData) {
+ val = readw(devpriv->daq + acqControl);
+ if (val & DAQBOARD2000_AcqResultsFIFOHasValidData)
break;
- }
/* udelay(2); */
}
- data[i] = fpga->acqResultsFIFO;
- fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
- fpga->acqControl = DAQBOARD2000_SeqStopScanList;
+ data[i] = readw(devpriv->daq + acqResultsFIFO);
+ writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
+ writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
}
return i;
@@ -442,8 +405,9 @@ static int daqboard2000_ao_insn_read(struct comedi_device *dev,
struct comedi_insn *insn,
unsigned int *data)
{
- int i;
+ struct daqboard2000_private *devpriv = dev->private;
int chan = CR_CHAN(insn->chanspec);
+ int i;
for (i = 0; i < insn->n; i++)
data[i] = devpriv->ao_readback[chan];
@@ -456,28 +420,39 @@ static int daqboard2000_ao_insn_write(struct comedi_device *dev,
struct comedi_insn *insn,
unsigned int *data)
{
- int i;
+ struct daqboard2000_private *devpriv = dev->private;
int chan = CR_CHAN(insn->chanspec);
- struct daqboard2000_hw *fpga = devpriv->daq;
+ unsigned int val;
int timeout;
+ int i;
for (i = 0; i < insn->n; i++) {
+#if 0
/*
- * OK, since it works OK without enabling the DAC's, let's keep
- * it as simple as possible...
+ * OK, since it works OK without enabling the DAC's,
+ * let's keep it as simple as possible...
*/
- /* fpga->dacControl = (chan + 2) * 0x0010 | 0x0001; udelay(1000); */
- fpga->dacSetting[chan] = data[i];
+ writew((chan + 2) * 0x0010 | 0x0001,
+ devpriv->daq + dacControl);
+ udelay(1000);
+#endif
+ writew(data[i], devpriv->daq + dacSetting(chan));
for (timeout = 0; timeout < 20; timeout++) {
- if ((fpga->dacControl & ((chan + 1) * 0x0010)) == 0)
+ val = readw(devpriv->daq + dacControl);
+ if ((val & ((chan + 1) * 0x0010)) == 0)
break;
/* udelay(2); */
}
devpriv->ao_readback[chan] = data[i];
+#if 0
/*
- * Since we never enabled the DAC's, we don't need to disable it...
- * fpga->dacControl = (chan + 2) * 0x0010 | 0x0000; udelay(1000);
+ * Since we never enabled the DAC's, we don't need
+ * to disable it...
*/
+ writew((chan + 2) * 0x0010 | 0x0000,
+ devpriv->daq + dacControl);
+ udelay(1000);
+#endif
}
return i;
@@ -485,7 +460,8 @@ static int daqboard2000_ao_insn_write(struct comedi_device *dev,
static void daqboard2000_resetLocalBus(struct comedi_device *dev)
{
- dev_dbg(dev->class_dev, "daqboard2000_resetLocalBus\n");
+ struct daqboard2000_private *devpriv = dev->private;
+
writel(DAQBOARD2000_SECRLocalBusHi, devpriv->plx + 0x6c);
udelay(10000);
writel(DAQBOARD2000_SECRLocalBusLo, devpriv->plx + 0x6c);
@@ -494,7 +470,8 @@ static void daqboard2000_resetLocalBus(struct comedi_device *dev)
static void daqboard2000_reloadPLX(struct comedi_device *dev)
{
- dev_dbg(dev->class_dev, "daqboard2000_reloadPLX\n");
+ struct daqboard2000_private *devpriv = dev->private;
+
writel(DAQBOARD2000_SECRReloadLo, devpriv->plx + 0x6c);
udelay(10000);
writel(DAQBOARD2000_SECRReloadHi, devpriv->plx + 0x6c);
@@ -505,7 +482,8 @@ static void daqboard2000_reloadPLX(struct comedi_device *dev)
static void daqboard2000_pulseProgPin(struct comedi_device *dev)
{
- dev_dbg(dev->class_dev, "daqboard2000_pulseProgPin 1\n");
+ struct daqboard2000_private *devpriv = dev->private;
+
writel(DAQBOARD2000_SECRProgPinHi, devpriv->plx + 0x6c);
udelay(10000);
writel(DAQBOARD2000_SECRProgPinLo, devpriv->plx + 0x6c);
@@ -514,6 +492,7 @@ static void daqboard2000_pulseProgPin(struct comedi_device *dev)
static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
{
+ struct daqboard2000_private *devpriv = dev->private;
int result = 0;
int i;
int cpld;
@@ -533,6 +512,7 @@ static int daqboard2000_pollCPLD(struct comedi_device *dev, int mask)
static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
{
+ struct daqboard2000_private *devpriv = dev->private;
int result = 0;
udelay(10);
@@ -545,41 +525,29 @@ static int daqboard2000_writeCPLD(struct comedi_device *dev, int data)
}
static int initialize_daqboard2000(struct comedi_device *dev,
- unsigned char *cpld_array, int len)
+ const u8 *cpld_array, size_t len)
{
+ struct daqboard2000_private *devpriv = dev->private;
int result = -EIO;
/* Read the serial EEPROM control register */
int secr;
int retry;
- int i;
+ size_t i;
/* Check to make sure the serial eeprom is present on the board */
secr = readl(devpriv->plx + 0x6c);
- if (!(secr & DAQBOARD2000_EEPROM_PRESENT)) {
-#ifdef DEBUG_EEPROM
- dev_dbg(dev->class_dev, "no serial eeprom\n");
-#endif
+ if (!(secr & DAQBOARD2000_EEPROM_PRESENT))
return -EIO;
- }
for (retry = 0; retry < 3; retry++) {
-#ifdef DEBUG_EEPROM
- dev_dbg(dev->class_dev, "Programming EEPROM try %x\n", retry);
-#endif
-
daqboard2000_resetLocalBus(dev);
daqboard2000_reloadPLX(dev);
daqboard2000_pulseProgPin(dev);
if (daqboard2000_pollCPLD(dev, DAQBOARD2000_CPLD_INIT)) {
for (i = 0; i < len; i++) {
- if (cpld_array[i] == 0xff
- && cpld_array[i + 1] == 0x20) {
-#ifdef DEBUG_EEPROM
- dev_dbg(dev->class_dev,
- "Preamble found at %d\n", i);
-#endif
+ if (cpld_array[i] == 0xff &&
+ cpld_array[i + 1] == 0x20)
break;
- }
}
for (; i < len; i += 2) {
int data =
@@ -588,9 +556,6 @@ static int initialize_daqboard2000(struct comedi_device *dev,
break;
}
if (i >= len) {
-#ifdef DEBUG_EEPROM
- dev_dbg(dev->class_dev, "Programmed\n");
-#endif
daqboard2000_resetLocalBus(dev);
daqboard2000_reloadPLX(dev);
result = 0;
@@ -601,28 +566,45 @@ static int initialize_daqboard2000(struct comedi_device *dev,
return result;
}
+static int daqboard2000_upload_firmware(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct firmware *fw;
+ int ret;
+
+ ret = request_firmware(&fw, DAQBOARD2000_FIRMWARE, &pcidev->dev);
+ if (ret)
+ return ret;
+
+ ret = initialize_daqboard2000(dev, fw->data, fw->size);
+ release_firmware(fw);
+
+ return ret;
+}
+
static void daqboard2000_adcStopDmaTransfer(struct comedi_device *dev)
{
-/* printk("Implement: daqboard2000_adcStopDmaTransfer\n");*/
}
static void daqboard2000_adcDisarm(struct comedi_device *dev)
{
- struct daqboard2000_hw *fpga = devpriv->daq;
+ struct daqboard2000_private *devpriv = dev->private;
/* Disable hardware triggers */
udelay(2);
- fpga->trigControl = DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable;
+ writew(DAQBOARD2000_TrigAnalog | DAQBOARD2000_TrigDisable,
+ devpriv->daq + trigControl);
udelay(2);
- fpga->trigControl = DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable;
+ writew(DAQBOARD2000_TrigTTL | DAQBOARD2000_TrigDisable,
+ devpriv->daq + trigControl);
/* Stop the scan list FIFO from loading the configuration pipe */
udelay(2);
- fpga->acqControl = DAQBOARD2000_SeqStopScanList;
+ writew(DAQBOARD2000_SeqStopScanList, devpriv->daq + acqControl);
/* Stop the pacer clock */
udelay(2);
- fpga->acqControl = DAQBOARD2000_AdcPacerDisable;
+ writew(DAQBOARD2000_AdcPacerDisable, devpriv->daq + acqControl);
/* Stop the input dma (abort channel 1) */
daqboard2000_adcStopDmaTransfer(dev);
@@ -630,41 +612,39 @@ static void daqboard2000_adcDisarm(struct comedi_device *dev)
static void daqboard2000_activateReferenceDacs(struct comedi_device *dev)
{
- struct daqboard2000_hw *fpga = devpriv->daq;
+ struct daqboard2000_private *devpriv = dev->private;
+ unsigned int val;
int timeout;
/* Set the + reference dac value in the FPGA */
- fpga->refDacs = 0x80 | DAQBOARD2000_PosRefDacSelect;
+ writew(0x80 | DAQBOARD2000_PosRefDacSelect, devpriv->daq + refDacs);
for (timeout = 0; timeout < 20; timeout++) {
- if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0)
+ val = readw(devpriv->daq + dacControl);
+ if ((val & DAQBOARD2000_RefBusy) == 0)
break;
udelay(2);
}
-/* printk("DAQBOARD2000_PosRefDacSelect %d\n", timeout);*/
/* Set the - reference dac value in the FPGA */
- fpga->refDacs = 0x80 | DAQBOARD2000_NegRefDacSelect;
+ writew(0x80 | DAQBOARD2000_NegRefDacSelect, devpriv->daq + refDacs);
for (timeout = 0; timeout < 20; timeout++) {
- if ((fpga->dacControl & DAQBOARD2000_RefBusy) == 0)
+ val = readw(devpriv->daq + dacControl);
+ if ((val & DAQBOARD2000_RefBusy) == 0)
break;
udelay(2);
}
-/* printk("DAQBOARD2000_NegRefDacSelect %d\n", timeout);*/
}
static void daqboard2000_initializeCtrs(struct comedi_device *dev)
{
-/* printk("Implement: daqboard2000_initializeCtrs\n");*/
}
static void daqboard2000_initializeTmrs(struct comedi_device *dev)
{
-/* printk("Implement: daqboard2000_initializeTmrs\n");*/
}
static void daqboard2000_dacDisarm(struct comedi_device *dev)
{
-/* printk("Implement: daqboard2000_dacDisarm\n");*/
}
static void daqboard2000_initializeAdc(struct comedi_device *dev)
@@ -680,92 +660,66 @@ static void daqboard2000_initializeDac(struct comedi_device *dev)
daqboard2000_dacDisarm(dev);
}
-/*
-The test command, REMOVE!!:
-
-rmmod daqboard2000 ; rmmod comedi; make install ; modprobe daqboard2000; /usr/sbin/comedi_config /dev/comedi0 daqboard/2000 ; tail -40 /var/log/messages
-*/
-
static int daqboard2000_8255_cb(int dir, int port, int data,
unsigned long ioaddr)
{
- int result = 0;
+ void __iomem *mmio_base = (void __iomem *)ioaddr;
+
if (dir) {
- writew(data, ((void *)ioaddr) + port * 2);
- result = 0;
+ writew(data, mmio_base + port * 2);
+ return 0;
} else {
- result = readw(((void *)ioaddr) + port * 2);
+ return readw(mmio_base + port * 2);
}
-/*
- printk("daqboard2000_8255_cb %x %d %d %2.2x -> %2.2x\n",
- arg, dir, port, data, result);
-*/
- return result;
}
-static struct pci_dev *daqboard2000_find_pci_dev(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static const void *daqboard2000_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
+ const struct daq200_boardtype *board;
int i;
- for_each_pci_dev(pcidev) {
- if (bus || slot) {
- if (bus != pcidev->bus->number ||
- slot != PCI_SLOT(pcidev->devfn))
- continue;
- }
- if (pcidev->vendor != PCI_VENDOR_ID_IOTECH ||
- pcidev->device != 0x0409 ||
- pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
- continue;
-
- for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
- if (boardtypes[i].id != pcidev->subsystem_device)
- continue;
- dev->board_ptr = boardtypes + i;
- return pcidev;
- }
+ if (pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
+ board = &boardtypes[i];
+ if (pcidev->subsystem_device == board->id)
+ return board;
}
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
return NULL;
}
-static int daqboard2000_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int daqboard2000_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- struct pci_dev *pcidev;
+ const struct daq200_boardtype *board;
+ struct daqboard2000_private *devpriv;
struct comedi_subdevice *s;
- resource_size_t pci_base;
- void *aux_data;
- unsigned int aux_len;
int result;
- result = alloc_private(dev, sizeof(struct daqboard2000_private));
+ comedi_set_hw_dev(dev, &pcidev->dev);
+
+ board = daqboard2000_find_boardinfo(dev, pcidev);
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
+ result = alloc_private(dev, sizeof(*devpriv));
if (result < 0)
return -ENOMEM;
+ devpriv = dev->private;
- pcidev = daqboard2000_find_pci_dev(dev, it);
- if (!pcidev)
- return -EIO;
- comedi_set_hw_dev(dev, &pcidev->dev);
-
- result = comedi_pci_enable(pcidev, "daqboard2000");
- if (result < 0) {
- dev_err(dev->class_dev,
- "failed to enable PCI device and request regions\n");
- return -EIO;
- }
+ result = comedi_pci_enable(pcidev, dev->driver->driver_name);
+ if (result < 0)
+ return result;
dev->iobase = 1; /* the "detach" needs this */
- pci_base = pci_resource_start(pcidev, 0);
- devpriv->plx = ioremap(pci_base, DAQBOARD2000_PLX_SIZE);
- pci_base = pci_resource_start(pcidev, 2);
- devpriv->daq = ioremap(pci_base, DAQBOARD2000_DAQ_SIZE);
+ devpriv->plx = ioremap(pci_resource_start(pcidev, 0),
+ pci_resource_len(pcidev, 0));
+ devpriv->daq = ioremap(pci_resource_start(pcidev, 2),
+ pci_resource_len(pcidev, 2));
if (!devpriv->plx || !devpriv->daq)
return -ENOMEM;
@@ -775,36 +729,14 @@ static int daqboard2000_attach(struct comedi_device *dev,
readl(devpriv->plx + 0x6c);
- /*
- u8 interrupt;
- Windows code does restore interrupts, but since we don't use them...
- pci_read_config_byte(pcidev, PCI_INTERRUPT_LINE, &interrupt);
- printk("Interrupt before is: %x\n", interrupt);
- */
-
- aux_data = comedi_aux_data(it->options, 0);
- aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
-
- if (aux_data && aux_len) {
- result = initialize_daqboard2000(dev, aux_data, aux_len);
- } else {
- dev_dbg(dev->class_dev,
- "no FPGA initialization code, aborting\n");
- result = -EIO;
- }
+ result = daqboard2000_upload_firmware(dev);
if (result < 0)
- goto out;
+ return result;
+
daqboard2000_initializeAdc(dev);
daqboard2000_initializeDac(dev);
- /*
- Windows code does restore interrupts, but since we don't use them...
- pci_read_config_byte(pcidev, PCI_INTERRUPT_LINE, &interrupt);
- printk("Interrupt after is: %x\n", interrupt);
- */
-
- dev->board_name = this_board->name;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* ai subdevice */
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
@@ -813,7 +745,7 @@ static int daqboard2000_attach(struct comedi_device *dev,
s->insn_read = daqboard2000_ai_insn_read;
s->range_table = &range_daqboard2000_ai;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* ao subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -821,22 +753,27 @@ static int daqboard2000_attach(struct comedi_device *dev,
s->maxdata = 0xffff;
s->insn_read = daqboard2000_ao_insn_read;
s->insn_write = daqboard2000_ao_insn_write;
- s->range_table = &range_daqboard2000_ao;
+ s->range_table = &range_bipolar10;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
- (unsigned long)(devpriv->daq + 0x40));
+ (unsigned long)(devpriv->daq + dioP2ExpansionIO8Bit));
+ if (result)
+ return result;
-out:
- return result;
+ dev_info(dev->class_dev, "%s: %s attached\n",
+ dev->driver->driver_name, dev->board_name);
+
+ return 0;
}
static void daqboard2000_detach(struct comedi_device *dev)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct daqboard2000_private *devpriv = dev->private;
if (dev->subdevices)
- subdev_8255_cleanup(dev, dev->subdevices + 2);
+ subdev_8255_cleanup(dev, &dev->subdevices[2]);
if (dev->irq)
free_irq(dev->irq, dev);
if (devpriv) {
@@ -855,7 +792,7 @@ static void daqboard2000_detach(struct comedi_device *dev)
static struct comedi_driver daqboard2000_driver = {
.driver_name = "daqboard2000",
.module = THIS_MODULE,
- .attach = daqboard2000_attach,
+ .attach_pci = daqboard2000_attach_pci,
.detach = daqboard2000_detach,
};
@@ -887,3 +824,4 @@ module_comedi_pci_driver(daqboard2000_driver, daqboard2000_pci_driver);
MODULE_AUTHOR("Comedi http://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(DAQBOARD2000_FIRMWARE);
diff --git a/drivers/staging/comedi/drivers/das08.c b/drivers/staging/comedi/drivers/das08.c
index 67a914a10b55..5fd21fa6c1c7 100644
--- a/drivers/staging/comedi/drivers/das08.c
+++ b/drivers/staging/comedi/drivers/das08.c
@@ -32,8 +32,9 @@
* [ComputerBoards] DAS08 (isa-das08), DAS08-PGM (das08-pgm),
* DAS08-PGH (das08-pgh), DAS08-PGL (das08-pgl), DAS08-AOH (das08-aoh),
* DAS08-AOL (das08-aol), DAS08-AOM (das08-aom), DAS08/JR-AO (das08/jr-ao),
- * DAS08/JR-16-AO (das08jr-16-ao), PCI-DAS08 (pci-das08 or das08),
+ * DAS08/JR-16-AO (das08jr-16-ao), PCI-DAS08 (pci-das08),
* PC104-DAS08 (pc104-das08), DAS08/JR/16 (das08jr/16)
+ * Updated: Fri, 31 Aug 2012 19:19:06 +0100
* Status: works
*
* This is a rewrite of the das08 and das08jr drivers.
@@ -41,9 +42,8 @@
* Options (for ISA cards):
* [0] - base io address
*
- * Options (for pci-das08):
- * [0] - bus (optional)
- * [1] = slot (optional)
+ * Manual configuration of PCI cards is not supported; they are
+ * configured automatically.
*
* The das08 driver doesn't support asynchronous commands, since
* the cheap das08 hardware doesn't really support them. The
@@ -61,9 +61,9 @@
#define DRV_NAME "das08"
-#define DO_COMEDI_DRIVER_REGISTER \
- (IS_ENABLED(CONFIG_COMEDI_DAS08_ISA) || \
- IS_ENABLED(CONFIG_COMEDI_DAS08_PCI))
+#define DO_ISA IS_ENABLED(CONFIG_COMEDI_DAS08_ISA)
+#define DO_PCI IS_ENABLED(CONFIG_COMEDI_DAS08_PCI)
+#define DO_COMEDI_DRIVER_REGISTER (DO_ISA || DO_PCI)
#define PCI_VENDOR_ID_COMPUTERBOARDS 0x1307
#define PCI_DEVICE_ID_PCIDAS08 0x29
@@ -236,6 +236,16 @@ static const int *const das08_gainlists[] = {
das08_pgm_gainlist,
};
+static inline bool is_isa_board(const struct das08_board_struct *board)
+{
+ return DO_ISA && board->bustype == isa;
+}
+
+static inline bool is_pci_board(const struct das08_board_struct *board)
+{
+ return DO_PCI && board->bustype == pci;
+}
+
#define TIMEOUT 100000
static int das08_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
@@ -342,9 +352,9 @@ static int das08_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
return insn->n;
}
-static int __maybe_unused
-das08jr_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static int das08jr_di_rbits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
data[0] = 0;
data[1] = inb(dev->iobase + DAS08JR_DIO);
@@ -352,9 +362,9 @@ das08jr_di_rbits(struct comedi_device *dev, struct comedi_subdevice *s,
return insn->n;
}
-static int __maybe_unused
-das08jr_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static int das08jr_do_wbits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
struct das08_private_struct *devpriv = dev->private;
@@ -369,88 +379,92 @@ das08jr_do_wbits(struct comedi_device *dev, struct comedi_subdevice *s,
return insn->n;
}
-static int __maybe_unused
-das08jr_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static void das08_ao_set_data(struct comedi_device *dev,
+ unsigned int chan, unsigned int data)
{
- int n;
- int lsb, msb;
- int chan;
-
- lsb = data[0] & 0xff;
- msb = (data[0] >> 8) & 0xff;
-
- chan = CR_CHAN(insn->chanspec);
+ const struct das08_board_struct *thisboard = comedi_board(dev);
+ struct das08_private_struct *devpriv = dev->private;
+ unsigned char lsb;
+ unsigned char msb;
- for (n = 0; n < insn->n; n++) {
-#if 0
- outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]);
- outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]);
-#else
+ lsb = data & 0xff;
+ msb = (data >> 8) & 0xff;
+ if (thisboard->is_jr) {
outb(lsb, dev->iobase + DAS08JR_AO_LSB(chan));
outb(msb, dev->iobase + DAS08JR_AO_MSB(chan));
-#endif
-
/* load DACs */
inb(dev->iobase + DAS08JR_DIO);
+ } else {
+ outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
+ outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
+ /* load DACs */
+ inb(dev->iobase + DAS08AO_AO_UPDATE);
}
-
- return n;
+ devpriv->ao_readback[chan] = data;
}
-/*
- *
- * The -aox boards have the DACs at a different offset and use
- * a different method to force an update.
- *
- */
-static int __maybe_unused
-das08ao_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+static void das08_ao_initialize(struct comedi_device *dev,
+ struct comedi_subdevice *s)
{
int n;
- int lsb, msb;
- int chan;
+ unsigned int data;
+
+ data = s->maxdata / 2; /* should be about 0 volts */
+ for (n = 0; n < s->n_chan; n++)
+ das08_ao_set_data(dev, n, data);
+}
- lsb = data[0] & 0xff;
- msb = (data[0] >> 8) & 0xf;
+static int das08_ao_winsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ unsigned int n;
+ unsigned int chan;
chan = CR_CHAN(insn->chanspec);
- for (n = 0; n < insn->n; n++) {
-#if 0
- outb(lsb, dev->iobase + devpriv->ao_offset_lsb[chan]);
- outb(msb, dev->iobase + devpriv->ao_offset_msb[chan]);
-#else
- outb(lsb, dev->iobase + DAS08AO_AO_LSB(chan));
- outb(msb, dev->iobase + DAS08AO_AO_MSB(chan));
-#endif
+ for (n = 0; n < insn->n; n++)
+ das08_ao_set_data(dev, chan, *data);
- /* load DACs */
- inb(dev->iobase + DAS08AO_AO_UPDATE);
- }
+ return n;
+}
+
+static int das08_ao_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
+{
+ struct das08_private_struct *devpriv = dev->private;
+ unsigned int n;
+ unsigned int chan;
+
+ chan = CR_CHAN(insn->chanspec);
+
+ for (n = 0; n < insn->n; n++)
+ data[n] = devpriv->ao_readback[chan];
return n;
}
static void i8254_initialize(struct comedi_device *dev)
{
- struct das08_private_struct *devpriv = dev->private;
+ const struct das08_board_struct *thisboard = comedi_board(dev);
+ unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
unsigned int mode = I8254_MODE0 | I8254_BINARY;
int i;
for (i = 0; i < 3; ++i)
- i8254_set_mode(devpriv->i8254_iobase, 0, i, mode);
+ i8254_set_mode(i8254_iobase, 0, i, mode);
}
static int das08_counter_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
- struct das08_private_struct *devpriv = dev->private;
+ const struct das08_board_struct *thisboard = comedi_board(dev);
+ unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
int chan = insn->chanspec;
- data[0] = i8254_read(devpriv->i8254_iobase, 0, chan);
+ data[0] = i8254_read(i8254_iobase, 0, chan);
return 1;
}
@@ -458,10 +472,11 @@ static int das08_counter_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
- struct das08_private_struct *devpriv = dev->private;
+ const struct das08_board_struct *thisboard = comedi_board(dev);
+ unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
int chan = insn->chanspec;
- i8254_write(devpriv->i8254_iobase, 0, chan, data[0]);
+ i8254_write(i8254_iobase, 0, chan, data[0]);
return 1;
}
@@ -469,18 +484,16 @@ static int das08_counter_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
- struct das08_private_struct *devpriv = dev->private;
+ const struct das08_board_struct *thisboard = comedi_board(dev);
+ unsigned long i8254_iobase = dev->iobase + thisboard->i8254_offset;
int chan = insn->chanspec;
- if (insn->n != 2)
- return -EINVAL;
-
switch (data[0]) {
case INSN_CONFIG_SET_COUNTER_MODE:
- i8254_set_mode(devpriv->i8254_iobase, 0, chan, data[1]);
+ i8254_set_mode(i8254_iobase, 0, chan, data[1]);
break;
case INSN_CONFIG_8254_READ_STATUS:
- data[1] = i8254_status(devpriv->i8254_iobase, 0, chan);
+ data[1] = i8254_status(i8254_iobase, 0, chan);
break;
default:
return -EINVAL;
@@ -491,18 +504,14 @@ static int das08_counter_config(struct comedi_device *dev,
#if DO_COMEDI_DRIVER_REGISTER
static const struct das08_board_struct das08_boards[] = {
-#if IS_ENABLED(CONFIG_COMEDI_DAS08_ISA)
+#if DO_ISA
{
.name = "isa-das08", /* cio-das08.pdf */
.bustype = isa,
- .ai = das08_ai_rinsn,
.ai_nbits = 12,
.ai_pg = das08_pg_none,
.ai_encoding = das08_encode12,
- .ao = NULL,
- .ao_nbits = 12,
- .di = das08_di_rbits,
- .do_ = das08_do_wbits,
+ .di_nchan = 3,
.do_nchan = 4,
.i8255_offset = 8,
.i8254_offset = 4,
@@ -511,13 +520,10 @@ static const struct das08_board_struct das08_boards[] = {
{
.name = "das08-pgm", /* cio-das08pgx.pdf */
.bustype = isa,
- .ai = das08_ai_rinsn,
.ai_nbits = 12,
.ai_pg = das08_pgm,
.ai_encoding = das08_encode12,
- .ao = NULL,
- .di = das08_di_rbits,
- .do_ = das08_do_wbits,
+ .di_nchan = 3,
.do_nchan = 4,
.i8255_offset = 0,
.i8254_offset = 0x04,
@@ -526,44 +532,33 @@ static const struct das08_board_struct das08_boards[] = {
{
.name = "das08-pgh", /* cio-das08pgx.pdf */
.bustype = isa,
- .ai = das08_ai_rinsn,
.ai_nbits = 12,
.ai_pg = das08_pgh,
.ai_encoding = das08_encode12,
- .ao = NULL,
- .di = das08_di_rbits,
- .do_ = das08_do_wbits,
+ .di_nchan = 3,
.do_nchan = 4,
- .i8255_offset = 0,
.i8254_offset = 0x04,
.iosize = 16, /* unchecked */
},
{
.name = "das08-pgl", /* cio-das08pgx.pdf */
.bustype = isa,
- .ai = das08_ai_rinsn,
.ai_nbits = 12,
.ai_pg = das08_pgl,
.ai_encoding = das08_encode12,
- .ao = NULL,
- .di = das08_di_rbits,
- .do_ = das08_do_wbits,
+ .di_nchan = 3,
.do_nchan = 4,
- .i8255_offset = 0,
.i8254_offset = 0x04,
.iosize = 16, /* unchecked */
},
{
.name = "das08-aoh", /* cio-das08_aox.pdf */
.bustype = isa,
- .ai = das08_ai_rinsn,
.ai_nbits = 12,
.ai_pg = das08_pgh,
.ai_encoding = das08_encode12,
- .ao = das08ao_ao_winsn, /* 8 */
.ao_nbits = 12,
- .di = das08_di_rbits,
- .do_ = das08_do_wbits,
+ .di_nchan = 3,
.do_nchan = 4,
.i8255_offset = 0x0c,
.i8254_offset = 0x04,
@@ -572,14 +567,11 @@ static const struct das08_board_struct das08_boards[] = {
{
.name = "das08-aol", /* cio-das08_aox.pdf */
.bustype = isa,
- .ai = das08_ai_rinsn,
.ai_nbits = 12,
.ai_pg = das08_pgl,
.ai_encoding = das08_encode12,
- .ao = das08ao_ao_winsn, /* 8 */
.ao_nbits = 12,
- .di = das08_di_rbits,
- .do_ = das08_do_wbits,
+ .di_nchan = 3,
.do_nchan = 4,
.i8255_offset = 0x0c,
.i8254_offset = 0x04,
@@ -588,14 +580,11 @@ static const struct das08_board_struct das08_boards[] = {
{
.name = "das08-aom", /* cio-das08_aox.pdf */
.bustype = isa,
- .ai = das08_ai_rinsn,
.ai_nbits = 12,
.ai_pg = das08_pgm,
.ai_encoding = das08_encode12,
- .ao = das08ao_ao_winsn, /* 8 */
.ao_nbits = 12,
- .di = das08_di_rbits,
- .do_ = das08_do_wbits,
+ .di_nchan = 3,
.do_nchan = 4,
.i8255_offset = 0x0c,
.i8254_offset = 0x04,
@@ -604,152 +593,68 @@ static const struct das08_board_struct das08_boards[] = {
{
.name = "das08/jr-ao", /* cio-das08-jr-ao.pdf */
.bustype = isa,
- .ai = das08_ai_rinsn,
+ .is_jr = true,
.ai_nbits = 12,
.ai_pg = das08_pg_none,
.ai_encoding = das08_encode12,
- .ao = das08jr_ao_winsn,
.ao_nbits = 12,
- .di = das08jr_di_rbits,
- .do_ = das08jr_do_wbits,
+ .di_nchan = 8,
.do_nchan = 8,
- .i8255_offset = 0,
- .i8254_offset = 0,
.iosize = 16, /* unchecked */
},
{
.name = "das08jr-16-ao", /* cio-das08jr-16-ao.pdf */
.bustype = isa,
- .ai = das08_ai_rinsn,
+ .is_jr = true,
.ai_nbits = 16,
.ai_pg = das08_pg_none,
.ai_encoding = das08_encode16,
- .ao = das08jr_ao_winsn,
.ao_nbits = 16,
- .di = das08jr_di_rbits,
- .do_ = das08jr_do_wbits,
+ .di_nchan = 8,
.do_nchan = 8,
- .i8255_offset = 0,
.i8254_offset = 0x04,
.iosize = 16, /* unchecked */
},
{
.name = "pc104-das08",
- .bustype = pc104,
- .ai = das08_ai_rinsn,
+ .bustype = isa,
.ai_nbits = 12,
.ai_pg = das08_pg_none,
.ai_encoding = das08_encode12,
- .ao = NULL,
- .ao_nbits = 0,
- .di = das08_di_rbits,
- .do_ = das08_do_wbits,
+ .di_nchan = 3,
.do_nchan = 4,
- .i8255_offset = 0,
.i8254_offset = 4,
.iosize = 16, /* unchecked */
},
-#if 0
- {
- .name = "das08/f",
- },
- {
- .name = "das08jr",
- },
-#endif
{
.name = "das08jr/16",
.bustype = isa,
- .ai = das08_ai_rinsn,
+ .is_jr = true,
.ai_nbits = 16,
.ai_pg = das08_pg_none,
.ai_encoding = das08_encode16,
- .ao = NULL,
- .ao_nbits = 0,
- .di = das08jr_di_rbits,
- .do_ = das08jr_do_wbits,
+ .di_nchan = 8,
.do_nchan = 8,
- .i8255_offset = 0,
- .i8254_offset = 0,
.iosize = 16, /* unchecked */
},
-#if 0
- {
- .name = "das48-pga", /* cio-das48-pga.pdf */
- },
- {
- .name = "das08-pga-g2", /* a KM board */
- },
-#endif
-#endif /* IS_ENABLED(CONFIG_COMEDI_DAS08_ISA) */
-#if IS_ENABLED(CONFIG_COMEDI_DAS08_PCI)
+#endif /* DO_ISA */
+#if DO_PCI
{
.name = "pci-das08", /* pci-das08 */
.id = PCI_DEVICE_ID_PCIDAS08,
.bustype = pci,
- .ai = das08_ai_rinsn,
.ai_nbits = 12,
.ai_pg = das08_bipolar5,
.ai_encoding = das08_encode12,
- .ao = NULL,
- .ao_nbits = 0,
- .di = das08_di_rbits,
- .do_ = das08_do_wbits,
+ .di_nchan = 3,
.do_nchan = 4,
- .i8255_offset = 0,
.i8254_offset = 4,
.iosize = 8,
},
- { /* wildcard entry matches any supported PCI device */
- .name = DRV_NAME,
- .id = PCI_ANY_ID,
- .bustype = pci,
- },
-#endif /* IS_ENABLED(CONFIG_COMEDI_DAS08_PCI) */
+#endif /* DO_PCI */
};
#endif /* DO_COMEDI_DRIVER_REGISTER */
-#if IS_ENABLED(CONFIG_COMEDI_DAS08_CS)
-struct das08_board_struct das08_cs_boards[NUM_DAS08_CS_BOARDS] = {
- {
- .name = "pcm-das08",
- .id = 0x0, /* XXX */
- .bustype = pcmcia,
- .ai = das08_ai_rinsn,
- .ai_nbits = 12,
- .ai_pg = das08_bipolar5,
- .ai_encoding = das08_pcm_encode12,
- .ao = NULL,
- .ao_nbits = 0,
- .di = das08_di_rbits,
- .do_ = das08_do_wbits,
- .do_nchan = 3,
- .i8255_offset = 0,
- .i8254_offset = 0,
- .iosize = 16,
- },
- /* duplicate so driver name can be used also */
- {
- .name = "das08_cs",
- .id = 0x0, /* XXX */
- .bustype = pcmcia,
- .ai = das08_ai_rinsn,
- .ai_nbits = 12,
- .ai_pg = das08_bipolar5,
- .ai_encoding = das08_pcm_encode12,
- .ao = NULL,
- .ao_nbits = 0,
- .di = das08_di_rbits,
- .do_ = das08_do_wbits,
- .do_nchan = 3,
- .i8255_offset = 0,
- .i8254_offset = 0,
- .iosize = 16,
- },
-};
-EXPORT_SYMBOL_GPL(das08_cs_boards);
-#endif
-
int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
{
const struct das08_board_struct *thisboard = comedi_board(dev);
@@ -765,9 +670,9 @@ int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* ai */
- if (thisboard->ai) {
+ if (thisboard->ai_nbits) {
s->type = COMEDI_SUBD_AI;
/* XXX some boards actually have differential
* inputs instead of single ended.
@@ -778,53 +683,56 @@ int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
s->n_chan = 8;
s->maxdata = (1 << thisboard->ai_nbits) - 1;
s->range_table = das08_ai_lranges[thisboard->ai_pg];
- s->insn_read = thisboard->ai;
+ s->insn_read = das08_ai_rinsn;
devpriv->pg_gainlist = das08_gainlists[thisboard->ai_pg];
} else {
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* ao */
- if (thisboard->ao) {
+ if (thisboard->ao_nbits) {
s->type = COMEDI_SUBD_AO;
-/* XXX lacks read-back insn */
s->subdev_flags = SDF_WRITABLE;
s->n_chan = 2;
s->maxdata = (1 << thisboard->ao_nbits) - 1;
s->range_table = &range_bipolar5;
- s->insn_write = thisboard->ao;
+ s->insn_write = das08_ao_winsn;
+ s->insn_read = das08_ao_rinsn;
+ das08_ao_initialize(dev, s);
} else {
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* di */
- if (thisboard->di) {
+ if (thisboard->di_nchan) {
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
- s->n_chan = (thisboard->di == das08_di_rbits) ? 3 : 8;
+ s->n_chan = thisboard->di_nchan;
s->maxdata = 1;
s->range_table = &range_digital;
- s->insn_bits = thisboard->di;
+ s->insn_bits =
+ thisboard->is_jr ? das08jr_di_rbits : das08_di_rbits;
} else {
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
/* do */
- if (thisboard->do_) {
+ if (thisboard->do_nchan) {
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
s->n_chan = thisboard->do_nchan;
s->maxdata = 1;
s->range_table = &range_digital;
- s->insn_bits = thisboard->do_;
+ s->insn_bits =
+ thisboard->is_jr ? das08jr_do_wbits : das08_do_wbits;
} else {
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 4;
+ s = &dev->subdevices[4];
/* 8255 */
if (thisboard->i8255_offset != 0) {
subdev_8255_init(dev, s, NULL, (unsigned long)(dev->iobase +
@@ -834,7 +742,7 @@ int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 5;
+ s = &dev->subdevices[5];
/* 8254 */
if (thisboard->i8254_offset != 0) {
s->type = COMEDI_SUBD_COUNTER;
@@ -844,8 +752,6 @@ int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
s->insn_read = das08_counter_read;
s->insn_write = das08_counter_write;
s->insn_config = das08_counter_config;
-
- devpriv->i8254_iobase = iobase + thisboard->i8254_offset;
i8254_initialize(dev);
} else {
s->type = COMEDI_SUBD_UNUSED;
@@ -855,50 +761,13 @@ int das08_common_attach(struct comedi_device *dev, unsigned long iobase)
}
EXPORT_SYMBOL_GPL(das08_common_attach);
-static int das08_pci_attach_common(struct comedi_device *dev,
- struct pci_dev *pdev)
-{
- unsigned long iobase;
- unsigned long pci_iobase;
- struct das08_private_struct *devpriv = dev->private;
-
- if (!IS_ENABLED(CONFIG_COMEDI_DAS08_PCI))
- return -EINVAL;
-
- devpriv->pdev = pdev;
- /* enable PCI device and reserve I/O spaces */
- if (comedi_pci_enable(pdev, dev->driver->driver_name)) {
- dev_err(dev->class_dev,
- "Error enabling PCI device and requesting regions\n");
- return -EIO;
- }
- /* read base addresses */
- pci_iobase = pci_resource_start(pdev, 1);
- iobase = pci_resource_start(pdev, 2);
- dev_info(dev->class_dev, "pcibase 0x%lx iobase 0x%lx\n",
- pci_iobase, iobase);
- devpriv->pci_iobase = pci_iobase;
-#if 0
- /* We could enable pci-das08's interrupt here to make it possible
- * to do timed input in this driver, but there is little point since
- * conversions would have to be started by the interrupt handler
- * so you might as well use comedi_rt_timer to emulate commands
- */
- /* set source of interrupt trigger to counter2 output */
- outb(CNTRL_INTR | CNTRL_DIR, pci_iobase + CNTRL);
- /* Enable local interrupt 1 and pci interrupt */
- outw(INTR1_ENABLE | PCI_INTR_ENABLE, pci_iobase + INTCSR);
-#endif
- return das08_common_attach(dev, iobase);
-}
-
static const struct das08_board_struct *
das08_find_pci_board(struct pci_dev *pdev)
{
#if DO_COMEDI_DRIVER_REGISTER
unsigned int i;
for (i = 0; i < ARRAY_SIZE(das08_boards); i++)
- if (das08_boards[i].bustype == pci &&
+ if (is_pci_board(&das08_boards[i]) &&
pdev->device == das08_boards[i].id)
return &das08_boards[i];
#endif
@@ -909,9 +778,10 @@ das08_find_pci_board(struct pci_dev *pdev)
static int __devinit __maybe_unused
das08_attach_pci(struct comedi_device *dev, struct pci_dev *pdev)
{
+ unsigned long iobase;
int ret;
- if (!IS_ENABLED(CONFIG_COMEDI_DAS08_PCI))
+ if (!DO_PCI)
return -EINVAL;
ret = alloc_private(dev, sizeof(struct das08_private_struct));
if (ret < 0)
@@ -922,65 +792,16 @@ das08_attach_pci(struct comedi_device *dev, struct pci_dev *pdev)
dev_err(dev->class_dev, "BUG! cannot determine board type!\n");
return -EINVAL;
}
- /*
- * Need to 'get' the PCI device to match the 'put' in das08_detach().
- * TODO: Remove the pci_dev_get() and matching pci_dev_put() once
- * support for manual attachment of PCI devices via das08_attach()
- * has been removed.
- */
- pci_dev_get(pdev);
- return das08_pci_attach_common(dev, pdev);
-}
-
-static struct pci_dev *das08_find_pci(struct comedi_device *dev,
- int bus, int slot)
-{
- const struct das08_board_struct *thisboard = comedi_board(dev);
- struct pci_dev *pdev;
- unsigned int matchid;
-
- if (bus || slot)
- dev_dbg(dev->class_dev, "Looking for %s at PCI %02X:%02X\n",
- thisboard->name, bus, slot);
- else
- dev_dbg(dev->class_dev, "Looking for %s on PCI buses\n",
- thisboard->name);
-
- matchid = thisboard->id;
- pdev = NULL;
- for_each_pci_dev(pdev) {
- if ((bus || slot) &&
- (bus != pdev->bus->number || slot != PCI_SLOT(pdev->devfn)))
- continue;
- if (pdev->vendor != PCI_VENDOR_ID_COMPUTERBOARDS)
- continue;
- if (matchid == PCI_ANY_ID) {
- /* wildcard board matches any supported PCI board */
- const struct das08_board_struct *foundboard;
- foundboard = das08_find_pci_board(pdev);
- if (foundboard == NULL)
- continue;
- /* replace wildcard board_ptr */
- dev->board_ptr = thisboard = foundboard;
- } else {
- /* match specific PCI board */
- if (pdev->device != matchid)
- continue;
- }
- /* found a match */
- dev_info(dev->class_dev, "Found %s at PCI %s\n",
- thisboard->name, pci_name(pdev));
- return pdev;
- }
- /* no match found */
- if (bus || slot)
+ comedi_set_hw_dev(dev, &pdev->dev);
+ /* enable PCI device and reserve I/O spaces */
+ if (comedi_pci_enable(pdev, dev->driver->driver_name)) {
dev_err(dev->class_dev,
- "No %s cards found at PCI %02X:%02X\n",
- thisboard->name, bus, slot);
- else
- dev_err(dev->class_dev, "No %s cards found on PCI buses\n",
- thisboard->name);
- return NULL;
+ "Error enabling PCI device and requesting regions\n");
+ return -EIO;
+ }
+ /* read base addresses */
+ iobase = pci_resource_start(pdev, 2);
+ return das08_common_attach(dev, iobase);
}
static int __maybe_unused
@@ -997,14 +818,12 @@ das08_attach(struct comedi_device *dev, struct comedi_devconfig *it)
devpriv = dev->private;
dev_info(dev->class_dev, "attach\n");
- if (IS_ENABLED(CONFIG_COMEDI_DAS08_PCI) && thisboard->bustype == pci) {
- struct pci_dev *pdev;
- pdev = das08_find_pci(dev, it->options[0], it->options[1]);
- if (pdev == NULL)
- return -EIO;
- return das08_pci_attach_common(dev, pdev);
- } else if (IS_ENABLED(CONFIG_COMEDI_DAS08_ISA) &&
- (thisboard->bustype == isa || thisboard->bustype == pc104)) {
+ if (is_pci_board(thisboard)) {
+ dev_err(dev->class_dev,
+ "Manual configuration of PCI board '%s' is not supported\n",
+ thisboard->name);
+ return -EIO;
+ } else if (is_isa_board(thisboard)) {
iobase = it->options[0];
dev_info(dev->class_dev, "iobase 0x%lx\n", iobase);
if (!request_region(iobase, thisboard->iosize, DRV_NAME)) {
@@ -1019,26 +838,23 @@ das08_attach(struct comedi_device *dev, struct comedi_devconfig *it)
void das08_common_detach(struct comedi_device *dev)
{
if (dev->subdevices)
- subdev_8255_cleanup(dev, dev->subdevices + 4);
+ subdev_8255_cleanup(dev, &dev->subdevices[4]);
}
EXPORT_SYMBOL_GPL(das08_common_detach);
static void __maybe_unused das08_detach(struct comedi_device *dev)
{
const struct das08_board_struct *thisboard = comedi_board(dev);
- struct das08_private_struct *devpriv = dev->private;
das08_common_detach(dev);
- if (IS_ENABLED(CONFIG_COMEDI_DAS08_ISA) &&
- (thisboard->bustype == isa || thisboard->bustype == pc104)) {
+ if (is_isa_board(thisboard)) {
if (dev->iobase)
release_region(dev->iobase, thisboard->iosize);
- } else if (IS_ENABLED(CONFIG_COMEDI_DAS08_PCI) &&
- thisboard->bustype == pci) {
- if (devpriv && devpriv->pdev) {
- if (devpriv->pci_iobase)
- comedi_pci_disable(devpriv->pdev);
- pci_dev_put(devpriv->pdev);
+ } else if (is_pci_board(thisboard)) {
+ struct pci_dev *pdev = comedi_to_pci_dev(dev);
+ if (pdev) {
+ if (dev->iobase)
+ comedi_pci_disable(pdev);
}
}
}
@@ -1056,7 +872,7 @@ static struct comedi_driver das08_driver = {
};
#endif
-#if IS_ENABLED(CONFIG_COMEDI_DAS08_PCI)
+#if DO_PCI
static DEFINE_PCI_DEVICE_TABLE(das08_pci_table) = {
{ PCI_DEVICE(PCI_VENDOR_ID_COMPUTERBOARDS, PCI_DEVICE_ID_PCIDAS08) },
{0}
@@ -1081,10 +897,10 @@ static struct pci_driver das08_pci_driver = {
.probe = &das08_pci_probe,
.remove = __devexit_p(&das08_pci_remove)
};
-#endif /* CONFIG_COMEDI_DAS08_PCI */
+#endif /* DO_PCI */
#if DO_COMEDI_DRIVER_REGISTER
-#if IS_ENABLED(CONFIG_COMEDI_DAS08_PCI)
+#if DO_PCI
module_comedi_pci_driver(das08_driver, das08_pci_driver);
#else
module_comedi_driver(das08_driver);
diff --git a/drivers/staging/comedi/drivers/das08.h b/drivers/staging/comedi/drivers/das08.h
index 27b6d4ec9032..0314baebae39 100644
--- a/drivers/staging/comedi/drivers/das08.h
+++ b/drivers/staging/comedi/drivers/das08.h
@@ -24,7 +24,7 @@
#ifndef _DAS08_H
#define _DAS08_H
-enum das08_bustype { isa, pci, pcmcia, pc104 };
+enum das08_bustype { isa, pci, pcmcia };
/* different ways ai data is encoded in first two registers */
enum das08_ai_encoding { das08_encode12, das08_encode16, das08_pcm_encode12 };
enum das08_lrange { das08_pg_none, das08_bipolar5, das08_pgh, das08_pgl,
@@ -35,14 +35,12 @@ struct das08_board_struct {
const char *name;
unsigned int id; /* id for pci/pcmcia boards */
enum das08_bustype bustype;
- void *ai;
+ bool is_jr; /* true for 'JR' boards */
unsigned int ai_nbits;
enum das08_lrange ai_pg;
enum das08_ai_encoding ai_encoding;
- void *ao;
unsigned int ao_nbits;
- void *di;
- void *do_;
+ unsigned int di_nchan;
unsigned int do_nchan;
unsigned int i8255_offset;
unsigned int i8254_offset;
@@ -53,14 +51,9 @@ struct das08_private_struct {
unsigned int do_mux_bits; /* bits for do/mux register on boards without separate do register */
unsigned int do_bits; /* bits for do register on boards with register dedicated to digital out only */
const unsigned int *pg_gainlist;
- struct pci_dev *pdev; /* struct for pci-das08 */
- unsigned int pci_iobase; /* additional base address for pci-das08 */
- unsigned int i8254_iobase;
+ unsigned int ao_readback[2]; /* assume 2 AO channels */
};
-#define NUM_DAS08_CS_BOARDS 2
-extern struct das08_board_struct das08_cs_boards[NUM_DAS08_CS_BOARDS];
-
int das08_common_attach(struct comedi_device *dev, unsigned long iobase);
void das08_common_detach(struct comedi_device *dev);
diff --git a/drivers/staging/comedi/drivers/das08_cs.c b/drivers/staging/comedi/drivers/das08_cs.c
index f5700de7b6c0..e4c91e675379 100644
--- a/drivers/staging/comedi/drivers/das08_cs.c
+++ b/drivers/staging/comedi/drivers/das08_cs.c
@@ -58,6 +58,32 @@ Command support does not exist, but could be added for this board.
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
+static const struct das08_board_struct das08_cs_boards[] = {
+ {
+ .name = "pcm-das08",
+ .id = 0x0, /* XXX */
+ .bustype = pcmcia,
+ .ai_nbits = 12,
+ .ai_pg = das08_bipolar5,
+ .ai_encoding = das08_pcm_encode12,
+ .di_nchan = 3,
+ .do_nchan = 3,
+ .iosize = 16,
+ },
+ /* duplicate so driver name can be used also */
+ {
+ .name = "das08_cs",
+ .id = 0x0, /* XXX */
+ .bustype = pcmcia,
+ .ai_nbits = 12,
+ .ai_pg = das08_bipolar5,
+ .ai_encoding = das08_pcm_encode12,
+ .di_nchan = 3,
+ .do_nchan = 3,
+ .iosize = 16,
+ },
+};
+
static struct pcmcia_device *cur_dev;
static int das08_cs_attach(struct comedi_device *dev,
diff --git a/drivers/staging/comedi/drivers/das16.c b/drivers/staging/comedi/drivers/das16.c
index 895cc7783c9c..fcb8a32adb2f 100644
--- a/drivers/staging/comedi/drivers/das16.c
+++ b/drivers/staging/comedi/drivers/das16.c
@@ -402,62 +402,42 @@ static int das16_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s,
int gain, start_chan, i;
int mask;
- /* make sure triggers are valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
- tmp = cmd->scan_begin_src;
mask = TRIG_FOLLOW;
/* if board supports burst mode */
if (board->size > 0x400)
mask |= TRIG_TIMER | TRIG_EXT;
- cmd->scan_begin_src &= mask;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, mask);
tmp = cmd->convert_src;
mask = TRIG_TIMER | TRIG_EXT;
/* if board supports burst mode */
if (board->size > 0x400)
mask |= TRIG_NOW;
- cmd->convert_src &= mask;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->convert_src, mask);
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /**
- * step 2: make sure trigger sources are unique and
- * mutually compatible
- */
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT &&
- cmd->scan_begin_src != TRIG_FOLLOW)
- err++;
- if (cmd->convert_src != TRIG_TIMER &&
- cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
- err++;
- if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
- err++;
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
/* make sure scan_begin_src and convert_src dont conflict */
if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
- err++;
+ err |= -EINVAL;
if (cmd->scan_begin_src != TRIG_FOLLOW && cmd->convert_src != TRIG_NOW)
- err++;
+ err |= -EINVAL;
if (err)
return 2;
@@ -558,7 +538,7 @@ static int das16_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s,
/* utility function that suggests a dma transfer size in bytes */
static unsigned int das16_suggest_transfer_size(struct comedi_device *dev,
- struct comedi_cmd cmd)
+ const struct comedi_cmd *cmd)
{
unsigned int size;
unsigned int freq;
@@ -571,16 +551,16 @@ static unsigned int das16_suggest_transfer_size(struct comedi_device *dev,
/* otherwise, we are relying on dma terminal count interrupt,
* so pick a reasonable size */
- if (cmd.convert_src == TRIG_TIMER)
- freq = 1000000000 / cmd.convert_arg;
- else if (cmd.scan_begin_src == TRIG_TIMER)
- freq = (1000000000 / cmd.scan_begin_arg) * cmd.chanlist_len;
+ if (cmd->convert_src == TRIG_TIMER)
+ freq = 1000000000 / cmd->convert_arg;
+ else if (cmd->scan_begin_src == TRIG_TIMER)
+ freq = (1000000000 / cmd->scan_begin_arg) * cmd->chanlist_len;
/* return some default value */
else
freq = 0xffffffff;
- if (cmd.flags & TRIG_WAKE_EOS) {
- size = sample_size * cmd.chanlist_len;
+ if (cmd->flags & TRIG_WAKE_EOS) {
+ size = sample_size * cmd->chanlist_len;
} else {
/* make buffer fill in no more than 1/3 second */
size = (freq / 3) * sample_size;
@@ -592,7 +572,7 @@ static unsigned int das16_suggest_transfer_size(struct comedi_device *dev,
else if (size < sample_size)
size = sample_size;
- if (cmd.stop_src == TRIG_COUNT && size > devpriv->adc_byte_count)
+ if (cmd->stop_src == TRIG_COUNT && size > devpriv->adc_byte_count)
size = devpriv->adc_byte_count;
return size;
@@ -685,7 +665,7 @@ static int das16_cmd_exec(struct comedi_device *dev, struct comedi_subdevice *s)
set_dma_addr(devpriv->dma_chan,
devpriv->dma_buffer_addr[devpriv->current_buffer]);
/* set appropriate size of transfer */
- devpriv->dma_transfer_size = das16_suggest_transfer_size(dev, *cmd);
+ devpriv->dma_transfer_size = das16_suggest_transfer_size(dev, cmd);
set_dma_count(devpriv->dma_chan, devpriv->dma_transfer_size);
enable_dma(devpriv->dma_chan);
release_dma_lock(flags);
@@ -1268,7 +1248,7 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
/* ai */
if (board->ai) {
@@ -1300,7 +1280,7 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* ao */
if (board->ao) {
s->type = COMEDI_SUBD_AO;
@@ -1318,7 +1298,7 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* di */
if (board->di) {
s->type = COMEDI_SUBD_DI;
@@ -1331,7 +1311,7 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
/* do */
if (board->do_) {
s->type = COMEDI_SUBD_DO;
@@ -1346,7 +1326,7 @@ static int das16_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 4;
+ s = &dev->subdevices[4];
/* 8255 */
if (board->i8255_offset != 0) {
subdev_8255_init(dev, s, NULL, (dev->iobase +
@@ -1376,7 +1356,7 @@ static void das16_detach(struct comedi_device *dev)
das16_reset(dev);
if (dev->subdevices)
- subdev_8255_cleanup(dev, dev->subdevices + 4);
+ subdev_8255_cleanup(dev, &dev->subdevices[4]);
if (devpriv) {
int i;
for (i = 0; i < 2; i++) {
diff --git a/drivers/staging/comedi/drivers/das16m1.c b/drivers/staging/comedi/drivers/das16m1.c
index 200926347861..3f87d7598e5b 100644
--- a/drivers/staging/comedi/drivers/das16m1.c
+++ b/drivers/staging/comedi/drivers/das16m1.c
@@ -170,42 +170,24 @@ static int das16m1_cmd_test(struct comedi_device *dev,
const struct das16m1_board *board = comedi_board(dev);
unsigned int err = 0, tmp, i;
- /* make sure triggers are valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
- if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
- err++;
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -650,7 +632,7 @@ static int das16m1_attach(struct comedi_device *dev,
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
/* ai */
s->type = COMEDI_SUBD_AI;
@@ -666,7 +648,7 @@ static int das16m1_attach(struct comedi_device *dev,
s->cancel = das16m1_cancel;
s->poll = das16m1_poll;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* di */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
@@ -675,7 +657,7 @@ static int das16m1_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->insn_bits = das16m1_di_rbits;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* do */
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
@@ -684,7 +666,7 @@ static int das16m1_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->insn_bits = das16m1_do_wbits;
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
/* 8255 */
subdev_8255_init(dev, s, NULL, dev->iobase + DAS16M1_82C55);
@@ -707,7 +689,7 @@ static int das16m1_attach(struct comedi_device *dev,
static void das16m1_detach(struct comedi_device *dev)
{
if (dev->subdevices)
- subdev_8255_cleanup(dev, dev->subdevices + 3);
+ subdev_8255_cleanup(dev, &dev->subdevices[3]);
if (dev->irq)
free_irq(dev->irq, dev);
if (dev->iobase) {
diff --git a/drivers/staging/comedi/drivers/das1800.c b/drivers/staging/comedi/drivers/das1800.c
index 25e7e56a376f..2555f3297d7b 100644
--- a/drivers/staging/comedi/drivers/das1800.c
+++ b/drivers/staging/comedi/drivers/das1800.c
@@ -656,7 +656,7 @@ static int das1800_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
/* the guts of the interrupt handler, that is shared with das1800_ai_poll */
static void das1800_ai_handler(struct comedi_device *dev)
{
- struct comedi_subdevice *s = dev->subdevices + 0; /* analog input subdevice */
+ struct comedi_subdevice *s = &dev->subdevices[0];
struct comedi_async *async = s->async;
struct comedi_cmd *cmd = &async->cmd;
unsigned int status = inb(dev->iobase + DAS1800_STATUS);
@@ -784,59 +784,35 @@ static int das1800_ai_do_cmdtest(struct comedi_device *dev,
struct comedi_cmd *cmd)
{
int err = 0;
- int tmp;
unsigned int tmp_arg;
int i;
int unipolar;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_EXT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src,
+ TRIG_COUNT | TRIG_EXT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
- /* uniqueness check */
- if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
- err++;
- if (cmd->scan_begin_src != TRIG_FOLLOW &&
- cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_COUNT &&
- cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_EXT)
- err++;
- /* compatibility check */
if (cmd->scan_begin_src != TRIG_FOLLOW &&
cmd->convert_src != TRIG_TIMER)
- err++;
+ err |= -EINVAL;
if (err)
return 2;
@@ -958,14 +934,14 @@ static int das1800_ai_do_cmdtest(struct comedi_device *dev,
}
/* returns appropriate bits for control register a, depending on command */
-static int control_a_bits(struct comedi_cmd cmd)
+static int control_a_bits(const struct comedi_cmd *cmd)
{
int control_a;
control_a = FFEN; /* enable fifo */
- if (cmd.stop_src == TRIG_EXT)
+ if (cmd->stop_src == TRIG_EXT)
control_a |= ATEN;
- switch (cmd.start_src) {
+ switch (cmd->start_src) {
case TRIG_EXT:
control_a |= TGEN | CGSL;
break;
@@ -980,7 +956,7 @@ static int control_a_bits(struct comedi_cmd cmd)
}
/* returns appropriate bits for control register c, depending on command */
-static int control_c_bits(struct comedi_cmd cmd)
+static int control_c_bits(const struct comedi_cmd *cmd)
{
int control_c;
int aref;
@@ -988,18 +964,18 @@ static int control_c_bits(struct comedi_cmd cmd)
/* set clock source to internal or external, select analog reference,
* select unipolar / bipolar
*/
- aref = CR_AREF(cmd.chanlist[0]);
+ aref = CR_AREF(cmd->chanlist[0]);
control_c = UQEN; /* enable upper qram addresses */
if (aref != AREF_DIFF)
control_c |= SD;
if (aref == AREF_COMMON)
control_c |= CMEN;
/* if a unipolar range was selected */
- if (CR_RANGE(cmd.chanlist[0]) & UNIPOLAR)
+ if (CR_RANGE(cmd->chanlist[0]) & UNIPOLAR)
control_c |= UB;
- switch (cmd.scan_begin_src) {
+ switch (cmd->scan_begin_src) {
case TRIG_FOLLOW: /* not in burst mode */
- switch (cmd.convert_src) {
+ switch (cmd->convert_src) {
case TRIG_TIMER:
/* trig on cascaded counters */
control_c |= IPCLK;
@@ -1047,29 +1023,33 @@ static int das1800_set_frequency(struct comedi_device *dev)
}
/* sets up counters */
-static int setup_counters(struct comedi_device *dev, struct comedi_cmd cmd)
+static int setup_counters(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
{
+ unsigned int period;
+
/* setup cascaded counters for conversion/scan frequency */
- switch (cmd.scan_begin_src) {
+ switch (cmd->scan_begin_src) {
case TRIG_FOLLOW: /* not in burst mode */
- if (cmd.convert_src == TRIG_TIMER) {
+ if (cmd->convert_src == TRIG_TIMER) {
/* set conversion frequency */
+ period = cmd->convert_arg;
i8253_cascade_ns_to_timer_2div(TIMER_BASE,
- &(devpriv->divisor1),
- &(devpriv->divisor2),
- &(cmd.convert_arg),
- cmd.
- flags & TRIG_ROUND_MASK);
+ &devpriv->divisor1,
+ &devpriv->divisor2,
+ &period,
+ cmd->flags &
+ TRIG_ROUND_MASK);
if (das1800_set_frequency(dev) < 0)
return -1;
}
break;
case TRIG_TIMER: /* in burst mode */
/* set scan frequency */
- i8253_cascade_ns_to_timer_2div(TIMER_BASE, &(devpriv->divisor1),
- &(devpriv->divisor2),
- &(cmd.scan_begin_arg),
- cmd.flags & TRIG_ROUND_MASK);
+ period = cmd->scan_begin_arg;
+ i8253_cascade_ns_to_timer_2div(TIMER_BASE, &devpriv->divisor1,
+ &devpriv->divisor2, &period,
+ cmd->flags & TRIG_ROUND_MASK);
if (das1800_set_frequency(dev) < 0)
return -1;
break;
@@ -1078,7 +1058,7 @@ static int setup_counters(struct comedi_device *dev, struct comedi_cmd cmd)
}
/* setup counter 0 for 'about triggering' */
- if (cmd.stop_src == TRIG_EXT) {
+ if (cmd->stop_src == TRIG_EXT) {
/* load counter 0 in mode 0 */
i8254_load(dev->iobase + DAS1800_COUNTER, 0, 0, 1, 0);
}
@@ -1087,7 +1067,7 @@ static int setup_counters(struct comedi_device *dev, struct comedi_cmd cmd)
}
/* utility function that suggests a dma transfer size based on the conversion period 'ns' */
-static unsigned int suggest_transfer_size(struct comedi_cmd *cmd)
+static unsigned int suggest_transfer_size(const struct comedi_cmd *cmd)
{
unsigned int size = DMA_BUF_SIZE;
static const int sample_size = 2; /* size in bytes of one sample from board */
@@ -1125,7 +1105,7 @@ static unsigned int suggest_transfer_size(struct comedi_cmd *cmd)
}
/* sets up dma */
-static void setup_dma(struct comedi_device *dev, struct comedi_cmd cmd)
+static void setup_dma(struct comedi_device *dev, const struct comedi_cmd *cmd)
{
unsigned long lock_flags;
const int dual_dma = devpriv->irq_dma_bits & DMA_DUAL;
@@ -1134,7 +1114,7 @@ static void setup_dma(struct comedi_device *dev, struct comedi_cmd cmd)
return;
/* determine a reasonable dma transfer size */
- devpriv->dma_transfer_size = suggest_transfer_size(&cmd);
+ devpriv->dma_transfer_size = suggest_transfer_size(cmd);
lock_flags = claim_dma_lock();
disable_dma(devpriv->dma0);
/* clear flip-flop to make sure 2-byte registers for
@@ -1163,14 +1143,15 @@ static void setup_dma(struct comedi_device *dev, struct comedi_cmd cmd)
}
/* programs channel/gain list into card */
-static void program_chanlist(struct comedi_device *dev, struct comedi_cmd cmd)
+static void program_chanlist(struct comedi_device *dev,
+ const struct comedi_cmd *cmd)
{
int i, n, chan_range;
unsigned long irq_flags;
const int range_mask = 0x3; /* masks unipolar/bipolar bit off range */
const int range_bitshift = 8;
- n = cmd.chanlist_len;
+ n = cmd->chanlist_len;
/* spinlock protects indirect addressing */
spin_lock_irqsave(&dev->spinlock, irq_flags);
outb(QRAM, dev->iobase + DAS1800_SELECT); /* select QRAM for baseAddress + 0x0 */
@@ -1178,9 +1159,9 @@ static void program_chanlist(struct comedi_device *dev, struct comedi_cmd cmd)
/* make channel / gain list */
for (i = 0; i < n; i++) {
chan_range =
- CR_CHAN(cmd.
- chanlist[i]) | ((CR_RANGE(cmd.chanlist[i]) &
- range_mask) << range_bitshift);
+ CR_CHAN(cmd->chanlist[i]) |
+ ((CR_RANGE(cmd->chanlist[i]) & range_mask) <<
+ range_bitshift);
outw(chan_range, dev->iobase + DAS1800_QRAM);
}
outb(n - 1, dev->iobase + DAS1800_QRAM_ADDRESS); /*finish write to QRAM */
@@ -1196,7 +1177,7 @@ static int das1800_ai_do_cmd(struct comedi_device *dev,
int ret;
int control_a, control_c;
struct comedi_async *async = s->async;
- struct comedi_cmd cmd = async->cmd;
+ const struct comedi_cmd *cmd = &async->cmd;
if (!dev->irq) {
comedi_error(dev,
@@ -1206,12 +1187,12 @@ static int das1800_ai_do_cmd(struct comedi_device *dev,
/* disable dma on TRIG_WAKE_EOS, or TRIG_RT
* (because dma in handler is unsafe at hard real-time priority) */
- if (cmd.flags & (TRIG_WAKE_EOS | TRIG_RT))
+ if (cmd->flags & (TRIG_WAKE_EOS | TRIG_RT))
devpriv->irq_dma_bits &= ~DMA_ENABLED;
else
devpriv->irq_dma_bits |= devpriv->dma_bits;
/* interrupt on end of conversion for TRIG_WAKE_EOS */
- if (cmd.flags & TRIG_WAKE_EOS) {
+ if (cmd->flags & TRIG_WAKE_EOS) {
/* interrupt fifo not empty */
devpriv->irq_dma_bits &= ~FIMD;
} else {
@@ -1219,8 +1200,8 @@ static int das1800_ai_do_cmd(struct comedi_device *dev,
devpriv->irq_dma_bits |= FIMD;
}
/* determine how many conversions we need */
- if (cmd.stop_src == TRIG_COUNT)
- devpriv->count = cmd.stop_arg * cmd.chanlist_len;
+ if (cmd->stop_src == TRIG_COUNT)
+ devpriv->count = cmd->stop_arg * cmd->chanlist_len;
das1800_cancel(dev, s);
@@ -1240,9 +1221,9 @@ static int das1800_ai_do_cmd(struct comedi_device *dev,
/* set conversion rate and length for burst mode */
if (control_c & BMDE) {
/* program conversion period with number of microseconds minus 1 */
- outb(cmd.convert_arg / 1000 - 1,
+ outb(cmd->convert_arg / 1000 - 1,
dev->iobase + DAS1800_BURST_RATE);
- outb(cmd.chanlist_len - 1, dev->iobase + DAS1800_BURST_LENGTH);
+ outb(cmd->chanlist_len - 1, dev->iobase + DAS1800_BURST_LENGTH);
}
outb(devpriv->irq_dma_bits, dev->iobase + DAS1800_CONTROL_B); /* enable irq/dma */
outb(control_a, dev->iobase + DAS1800_CONTROL_A); /* enable fifo and triggering */
@@ -1653,7 +1634,7 @@ static int das1800_attach(struct comedi_device *dev,
return retval;
/* analog input subdevice */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_GROUND | SDF_CMD_READ;
@@ -1670,7 +1651,7 @@ static int das1800_attach(struct comedi_device *dev,
s->cancel = das1800_cancel;
/* analog out */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
if (thisboard->ao_ability == 1) {
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -1683,7 +1664,7 @@ static int das1800_attach(struct comedi_device *dev,
}
/* di */
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = 4;
@@ -1692,7 +1673,7 @@ static int das1800_attach(struct comedi_device *dev,
s->insn_bits = das1800_di_rbits;
/* do */
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
s->n_chan = thisboard->do_n_chan;
diff --git a/drivers/staging/comedi/drivers/das6402.c b/drivers/staging/comedi/drivers/das6402.c
index e3afcfa9efc8..e134c46dedff 100644
--- a/drivers/staging/comedi/drivers/das6402.c
+++ b/drivers/staging/comedi/drivers/das6402.c
@@ -152,7 +152,7 @@ static void das6402_setcounter(struct comedi_device *dev)
static irqreturn_t intr_handler(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices;
+ struct comedi_subdevice *s = &dev->subdevices[0];
if (!dev->attached || devpriv->das6402_ignoreirq) {
dev_warn(dev->class_dev, "BUG: spurious interrupt\n");
@@ -312,7 +312,7 @@ static int das6402_attach(struct comedi_device *dev,
return ret;
/* ai subdevice */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
s->n_chan = 8;
diff --git a/drivers/staging/comedi/drivers/das800.c b/drivers/staging/comedi/drivers/das800.c
index a0959a5e8747..215deac0a396 100644
--- a/drivers/staging/comedi/drivers/das800.c
+++ b/drivers/staging/comedi/drivers/das800.c
@@ -435,7 +435,7 @@ static irqreturn_t das800_interrupt(int irq, void *d)
if (fifo_overflow) {
spin_unlock_irqrestore(&dev->spinlock, irq_flags);
comedi_error(dev, "DAS800 FIFO overflow");
- das800_cancel(dev, dev->subdevices + 0);
+ das800_cancel(dev, s);
async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
comedi_event(dev, s);
async->events = 0;
@@ -517,7 +517,7 @@ static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return ret;
/* analog input subdevice */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
@@ -531,7 +531,7 @@ static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->cancel = das800_cancel;
/* di */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = 3;
@@ -540,7 +540,7 @@ static int das800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_bits = das800_di_rbits;
/* do */
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
s->n_chan = 4;
@@ -609,44 +609,24 @@ static int das800_ai_do_cmdtest(struct comedi_device *dev,
int gain, startChan;
int i;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
- err++;
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
diff --git a/drivers/staging/comedi/drivers/dmm32at.c b/drivers/staging/comedi/drivers/dmm32at.c
index 7107f590b1fe..4d5c33c4750f 100644
--- a/drivers/staging/comedi/drivers/dmm32at.c
+++ b/drivers/staging/comedi/drivers/dmm32at.c
@@ -41,6 +41,8 @@ Configuration Options:
#include "../comedidev.h"
#include <linux/ioport.h>
+#include "comedi_fc.h"
+
/* Board register addresses */
#define DMM32AT_MEMSIZE 0x10
@@ -258,47 +260,26 @@ static int dmm32at_ai_cmdtest(struct comedi_device *dev,
int tmp;
int start_chan, gain, i;
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER /*| TRIG_EXT */ ;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER /*| TRIG_EXT */ ;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER /*| TRIG_EXT */);
+ err |= cfc_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER /*| TRIG_EXT */);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually
- * compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -825,7 +806,7 @@ static int dmm32at_attach(struct comedi_device *dev,
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
/* analog input subdevice */
s->type = COMEDI_SUBD_AI;
@@ -841,7 +822,7 @@ static int dmm32at_attach(struct comedi_device *dev,
s->do_cmdtest = dmm32at_ai_cmdtest;
s->cancel = dmm32at_ai_cancel;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* analog output subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -851,7 +832,7 @@ static int dmm32at_attach(struct comedi_device *dev,
s->insn_write = dmm32at_ao_winsn;
s->insn_read = dmm32at_ao_rinsn;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* digital i/o subdevice */
/* get access to the DIO regs */
diff --git a/drivers/staging/comedi/drivers/dt2801.c b/drivers/staging/comedi/drivers/dt2801.c
index d332269375ab..c59a652a1194 100644
--- a/drivers/staging/comedi/drivers/dt2801.c
+++ b/drivers/staging/comedi/drivers/dt2801.c
@@ -532,7 +532,7 @@ static int dt2801_dio_insn_bits(struct comedi_device *dev,
{
int which = 0;
- if (s == dev->subdevices + 4)
+ if (s == &dev->subdevices[3])
which = 1;
if (data[0]) {
@@ -555,7 +555,7 @@ static int dt2801_dio_insn_config(struct comedi_device *dev,
{
int which = 0;
- if (s == dev->subdevices + 4)
+ if (s == &dev->subdevices[3])
which = 1;
/* configure */
@@ -636,7 +636,7 @@ havetype:
dev->board_name = boardtype.name;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* ai subdevice */
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
@@ -652,7 +652,7 @@ havetype:
s->range_table = ai_range_lkup(boardtype.adrangetype, it->options[3]);
s->insn_read = dt2801_ai_insn_read;
- s++;
+ s = &dev->subdevices[1];
/* ao subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -664,7 +664,7 @@ havetype:
s->insn_read = dt2801_ao_insn_read;
s->insn_write = dt2801_ao_insn_write;
- s++;
+ s = &dev->subdevices[2];
/* 1st digital subdevice */
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
@@ -674,7 +674,7 @@ havetype:
s->insn_bits = dt2801_dio_insn_bits;
s->insn_config = dt2801_dio_insn_config;
- s++;
+ s = &dev->subdevices[3];
/* 2nd digital subdevice */
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
diff --git a/drivers/staging/comedi/drivers/dt2811.c b/drivers/staging/comedi/drivers/dt2811.c
index 290b933c5f96..d3a8c1aec9d6 100644
--- a/drivers/staging/comedi/drivers/dt2811.c
+++ b/drivers/staging/comedi/drivers/dt2811.c
@@ -510,7 +510,7 @@ static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
break;
}
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* initialize the ADC subdevice */
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
@@ -530,7 +530,7 @@ static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
break;
}
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* ao subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -542,7 +542,7 @@ static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
devpriv->range_type_list[0] = dac_range_types[devpriv->dac_range[0]];
devpriv->range_type_list[1] = dac_range_types[devpriv->dac_range[1]];
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* di subdevice */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
@@ -551,7 +551,7 @@ static int dt2811_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->maxdata = 1;
s->range_table = &range_digital;
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
/* do subdevice */
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
diff --git a/drivers/staging/comedi/drivers/dt2814.c b/drivers/staging/comedi/drivers/dt2814.c
index 2e39ebe36fb5..064a8f215e4d 100644
--- a/drivers/staging/comedi/drivers/dt2814.c
+++ b/drivers/staging/comedi/drivers/dt2814.c
@@ -45,6 +45,8 @@ addition, the clock does not seem to be very accurate.
#include <linux/ioport.h>
#include <linux/delay.h>
+#include "comedi_fc.h"
+
#define DT2814_SIZE 2
#define DT2814_CSR 0
@@ -129,42 +131,22 @@ static int dt2814_ai_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are
- * unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->stop_src != TRIG_TIMER && cmd->stop_src != TRIG_EXT)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -247,7 +229,7 @@ static irqreturn_t dt2814_interrupt(int irq, void *d)
return IRQ_HANDLED;
}
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
hi = inb(dev->iobase + DT2814_DATA);
lo = inb(dev->iobase + DT2814_DATA);
@@ -346,7 +328,7 @@ static int dt2814_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret < 0)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_CMD_READ;
diff --git a/drivers/staging/comedi/drivers/dt2815.c b/drivers/staging/comedi/drivers/dt2815.c
index 45b20bee4369..b9692ef64c41 100644
--- a/drivers/staging/comedi/drivers/dt2815.c
+++ b/drivers/staging/comedi/drivers/dt2815.c
@@ -185,7 +185,7 @@ static int dt2815_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (alloc_private(dev, sizeof(struct dt2815_private)) < 0)
return -ENOMEM;
- s = dev->subdevices;
+ s = &dev->subdevices[0];
/* ao subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
diff --git a/drivers/staging/comedi/drivers/dt2817.c b/drivers/staging/comedi/drivers/dt2817.c
index beba0447b3ee..502e42e0753e 100644
--- a/drivers/staging/comedi/drivers/dt2817.c
+++ b/drivers/staging/comedi/drivers/dt2817.c
@@ -141,7 +141,7 @@ static int dt2817_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->n_chan = 32;
s->type = COMEDI_SUBD_DIO;
diff --git a/drivers/staging/comedi/drivers/dt282x.c b/drivers/staging/comedi/drivers/dt282x.c
index 1f0b40e4bddd..78d340716d1e 100644
--- a/drivers/staging/comedi/drivers/dt282x.c
+++ b/drivers/staging/comedi/drivers/dt282x.c
@@ -312,7 +312,7 @@ static void dt282x_ao_dma_interrupt(struct comedi_device *dev)
void *ptr;
int size;
int i;
- struct comedi_subdevice *s = dev->subdevices + 1;
+ struct comedi_subdevice *s = &dev->subdevices[1];
outw(devpriv->supcsr | DT2821_CLRDMADNE, dev->iobase + DT2821_SUPCSR);
@@ -345,7 +345,7 @@ static void dt282x_ai_dma_interrupt(struct comedi_device *dev)
int size;
int i;
int ret;
- struct comedi_subdevice *s = dev->subdevices;
+ struct comedi_subdevice *s = &dev->subdevices[0];
outw(devpriv->supcsr | DT2821_CLRDMADNE, dev->iobase + DT2821_SUPCSR);
@@ -457,8 +457,8 @@ static irqreturn_t dt282x_interrupt(int irq, void *d)
return IRQ_HANDLED;
}
- s = dev->subdevices + 0;
- s_ao = dev->subdevices + 1;
+ s = &dev->subdevices[0];
+ s_ao = &dev->subdevices[1];
adcsr = inw(dev->iobase + DT2821_ADCSR);
dacsr = inw(dev->iobase + DT2821_DACSR);
supcsr = inw(dev->iobase + DT2821_SUPCSR);
@@ -582,47 +582,24 @@ static int dt282x_ai_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources are unique
- * and mutually compatible
- */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->scan_begin_src != TRIG_FOLLOW &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -862,44 +839,22 @@ static int dt282x_ao_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_INT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources are unique
- * and mutually compatible
- */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1275,7 +1230,7 @@ static int dt282x_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
/* ai subdevice */
@@ -1294,7 +1249,7 @@ static int dt282x_attach(struct comedi_device *dev, struct comedi_devconfig *it)
opt_ai_range_lkup(boardtype.ispgl, it->options[opt_ai_range]);
devpriv->ad_2scomp = it->options[opt_ai_twos];
- s++;
+ s = &dev->subdevices[1];
s->n_chan = boardtype.dachan;
if (s->n_chan) {
@@ -1320,7 +1275,7 @@ static int dt282x_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->type = COMEDI_SUBD_UNUSED;
}
- s++;
+ s = &dev->subdevices[2];
/* dio subsystem */
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
diff --git a/drivers/staging/comedi/drivers/dt3000.c b/drivers/staging/comedi/drivers/dt3000.c
index 3476cda0fff0..43d05ef97157 100644
--- a/drivers/staging/comedi/drivers/dt3000.c
+++ b/drivers/staging/comedi/drivers/dt3000.c
@@ -63,6 +63,8 @@ AO commands are not supported.
#include "../comedidev.h"
#include <linux/delay.h>
+#include "comedi_fc.h"
+
#define PCI_VENDOR_ID_DT 0x1116
static const struct comedi_lrange range_dt3000_ai = { 4, {
@@ -330,7 +332,7 @@ static irqreturn_t dt3k_interrupt(int irq, void *d)
if (!dev->attached)
return IRQ_NONE;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
status = readw(devpriv->io_addr + DPR_Intr_Flag);
#ifdef DEBUG
debug_intr_flags(status);
@@ -408,37 +410,19 @@ static int dt3k_ai_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -842,7 +826,7 @@ static int dt3000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
/* ai subdevice */
@@ -857,7 +841,7 @@ static int dt3000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->do_cmdtest = dt3k_ai_cmdtest;
s->cancel = dt3k_ai_cancel;
- s++;
+ s = &dev->subdevices[1];
/* ao subsystem */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -868,7 +852,7 @@ static int dt3000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->len_chanlist = 1;
s->range_table = &range_bipolar10;
- s++;
+ s = &dev->subdevices[2];
/* dio subsystem */
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
@@ -879,7 +863,7 @@ static int dt3000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->len_chanlist = 8;
s->range_table = &range_digital;
- s++;
+ s = &dev->subdevices[3];
/* mem subsystem */
s->type = COMEDI_SUBD_MEMORY;
s->subdev_flags = SDF_READABLE;
@@ -890,7 +874,7 @@ static int dt3000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->range_table = &range_unknown;
#if 0
- s++;
+ s = &dev->subdevices[4];
/* proc subsystem */
s->type = COMEDI_SUBD_PROC;
#endif
diff --git a/drivers/staging/comedi/drivers/dt9812.c b/drivers/staging/comedi/drivers/dt9812.c
index 40821c7303ea..bc6f409b7e19 100644
--- a/drivers/staging/comedi/drivers/dt9812.c
+++ b/drivers/staging/comedi/drivers/dt9812.c
@@ -1041,7 +1041,7 @@ static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return ret;
/* digital input subdevice */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = 0;
@@ -1050,7 +1050,7 @@ static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_read = &dt9812_di_rinsn;
/* digital output subdevice */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITEABLE;
s->n_chan = 0;
@@ -1059,7 +1059,7 @@ static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_write = &dt9812_do_winsn;
/* analog input subdevice */
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
s->n_chan = 0;
@@ -1068,7 +1068,7 @@ static int dt9812_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_read = &dt9812_ai_rinsn;
/* analog output subdevice */
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITEABLE;
s->n_chan = 0;
diff --git a/drivers/staging/comedi/drivers/dyna_pci10xx.c b/drivers/staging/comedi/drivers/dyna_pci10xx.c
index 064be9aae3aa..6f612be1b0a5 100644
--- a/drivers/staging/comedi/drivers/dyna_pci10xx.c
+++ b/drivers/staging/comedi/drivers/dyna_pci10xx.c
@@ -41,7 +41,6 @@
#include <linux/mutex.h>
#define PCI_VENDOR_ID_DYNALOG 0x10b5
-#define DRV_NAME "dyna_pci10xx"
#define READ_TIMEOUT 50
@@ -54,59 +53,11 @@ static const struct comedi_lrange range_pci1050_ai = { 3, {
static const char range_codes_pci1050_ai[] = { 0x00, 0x10, 0x30 };
-static const struct comedi_lrange range_pci1050_ao = { 1, {
- UNI_RANGE(10)
- }
-};
-
-static const char range_codes_pci1050_ao[] = { 0x00 };
-
-struct boardtype {
- const char *name;
- int device_id;
- int ai_chans;
- int ai_bits;
- int ao_chans;
- int ao_bits;
- int di_chans;
- int di_bits;
- int do_chans;
- int do_bits;
- const struct comedi_lrange *range_ai;
- const char *range_codes_ai;
- const struct comedi_lrange *range_ao;
- const char *range_codes_ao;
-};
-
-static const struct boardtype boardtypes[] = {
- {
- .name = "dyna_pci1050",
- .device_id = 0x1050,
- .ai_chans = 16,
- .ai_bits = 12,
- .ao_chans = 16,
- .ao_bits = 12,
- .di_chans = 16,
- .di_bits = 16,
- .do_chans = 16,
- .do_bits = 16,
- .range_ai = &range_pci1050_ai,
- .range_codes_ai = range_codes_pci1050_ai,
- .range_ao = &range_pci1050_ao,
- .range_codes_ao = range_codes_pci1050_ao,
- },
- /* dummy entry corresponding to driver name */
- {.name = DRV_NAME},
-};
-
struct dyna_pci10xx_private {
struct mutex mutex;
unsigned long BADR3;
};
-#define thisboard ((const struct boardtype *)dev->board_ptr)
-#define devpriv ((struct dyna_pci10xx_private *)dev->private)
-
/******************************************************************************/
/************************** READ WRITE FUNCTIONS ******************************/
/******************************************************************************/
@@ -116,13 +67,14 @@ static int dyna_pci10xx_insn_read_ai(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct dyna_pci10xx_private *devpriv = dev->private;
int n, counter;
u16 d = 0;
unsigned int chan, range;
/* get the channel number and range */
chan = CR_CHAN(insn->chanspec);
- range = thisboard->range_codes_ai[CR_RANGE((insn->chanspec))];
+ range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))];
mutex_lock(&devpriv->mutex);
/* convert n samples */
@@ -159,11 +111,12 @@ static int dyna_pci10xx_insn_write_ao(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct dyna_pci10xx_private *devpriv = dev->private;
int n;
unsigned int chan, range;
chan = CR_CHAN(insn->chanspec);
- range = thisboard->range_codes_ai[CR_RANGE((insn->chanspec))];
+ range = range_codes_pci1050_ai[CR_RANGE((insn->chanspec))];
mutex_lock(&devpriv->mutex);
for (n = 0; n < insn->n; n++) {
@@ -181,6 +134,7 @@ static int dyna_pci10xx_di_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct dyna_pci10xx_private *devpriv = dev->private;
u16 d = 0;
mutex_lock(&devpriv->mutex);
@@ -200,6 +154,8 @@ static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct dyna_pci10xx_private *devpriv = dev->private;
+
/* The insn data is a mask in data[0] and the new data
* in data[1], each channel cooresponding to a bit.
* s->state contains the previous write data
@@ -223,143 +179,98 @@ static int dyna_pci10xx_do_insn_bits(struct comedi_device *dev,
return insn->n;
}
-static struct pci_dev *dyna_pci10xx_find_pci_dev(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
- int i;
-
- for_each_pci_dev(pcidev) {
- if (bus || slot) {
- if (bus != pcidev->bus->number ||
- slot != PCI_SLOT(pcidev->devfn))
- continue;
- }
- if (pcidev->vendor != PCI_VENDOR_ID_DYNALOG)
- continue;
-
- for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
- if (pcidev->device != boardtypes[i].device_id)
- continue;
-
- dev->board_ptr = &boardtypes[i];
- return pcidev;
- }
- }
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
- return NULL;
-}
-
-static int dyna_pci10xx_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int dyna_pci10xx_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
- struct pci_dev *pcidev;
+ struct dyna_pci10xx_private *devpriv;
struct comedi_subdevice *s;
int ret;
- if (alloc_private(dev, sizeof(struct dyna_pci10xx_private)) < 0) {
- printk(KERN_ERR "comedi: dyna_pci10xx: "
- "failed to allocate memory!\n");
- return -ENOMEM;
- }
-
- pcidev = dyna_pci10xx_find_pci_dev(dev, it);
- if (!pcidev)
- return -EIO;
comedi_set_hw_dev(dev, &pcidev->dev);
- dev->board_name = thisboard->name;
- dev->irq = 0;
-
- if (comedi_pci_enable(pcidev, DRV_NAME)) {
- printk(KERN_ERR "comedi: dyna_pci10xx: "
- "failed to enable PCI device and request regions!");
- return -EIO;
- }
-
- mutex_init(&devpriv->mutex);
+ dev->board_name = dev->driver->driver_name;
- printk(KERN_INFO "comedi: dyna_pci10xx: device found!\n");
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret)
+ return ret;
+ devpriv = dev->private;
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
dev->iobase = pci_resource_start(pcidev, 2);
devpriv->BADR3 = pci_resource_start(pcidev, 3);
+ mutex_init(&devpriv->mutex);
+
ret = comedi_alloc_subdevices(dev, 4);
if (ret)
return ret;
/* analog input */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
- s->n_chan = thisboard->ai_chans;
+ s->n_chan = 16;
s->maxdata = 0x0FFF;
- s->range_table = thisboard->range_ai;
+ s->range_table = &range_pci1050_ai;
s->len_chanlist = 16;
s->insn_read = dyna_pci10xx_insn_read_ai;
/* analog output */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
- s->n_chan = thisboard->ao_chans;
+ s->n_chan = 16;
s->maxdata = 0x0FFF;
- s->range_table = thisboard->range_ao;
+ s->range_table = &range_unipolar10;
s->len_chanlist = 16;
s->insn_write = dyna_pci10xx_insn_write_ao;
/* digital input */
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
- s->n_chan = thisboard->di_chans;
+ s->n_chan = 16;
s->maxdata = 1;
s->range_table = &range_digital;
- s->len_chanlist = thisboard->di_chans;
+ s->len_chanlist = 16;
s->insn_bits = dyna_pci10xx_di_insn_bits;
/* digital output */
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
- s->n_chan = thisboard->do_chans;
+ s->n_chan = 16;
s->maxdata = 1;
s->range_table = &range_digital;
- s->len_chanlist = thisboard->do_chans;
+ s->len_chanlist = 16;
s->state = 0;
s->insn_bits = dyna_pci10xx_do_insn_bits;
- printk(KERN_INFO "comedi: dyna_pci10xx: %s - device setup completed!\n",
- thisboard->name);
+ dev_info(dev->class_dev, "%s attached\n", dev->board_name);
- return 1;
+ return 0;
}
static void dyna_pci10xx_detach(struct comedi_device *dev)
{
struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct dyna_pci10xx_private *devpriv = dev->private;
if (devpriv)
mutex_destroy(&devpriv->mutex);
if (pcidev) {
if (dev->iobase)
comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
}
}
static struct comedi_driver dyna_pci10xx_driver = {
.driver_name = "dyna_pci10xx",
.module = THIS_MODULE,
- .attach = dyna_pci10xx_attach,
+ .attach_pci = dyna_pci10xx_attach_pci,
.detach = dyna_pci10xx_detach,
- .board_name = &boardtypes[0].name,
- .offset = sizeof(struct boardtype),
- .num_names = ARRAY_SIZE(boardtypes),
};
static int __devinit dyna_pci10xx_pci_probe(struct pci_dev *dev,
diff --git a/drivers/staging/comedi/drivers/fl512.c b/drivers/staging/comedi/drivers/fl512.c
index d1da80976f84..ae8e8f460295 100644
--- a/drivers/staging/comedi/drivers/fl512.c
+++ b/drivers/staging/comedi/drivers/fl512.c
@@ -140,7 +140,7 @@ static int fl512_attach(struct comedi_device *dev, struct comedi_devconfig *it)
* this if the definitions of the supdevices, 2 have been defined
*/
/* Analog indput */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* define subdevice as Analog In */
s->type = COMEDI_SUBD_AI;
/* you can read it from userspace */
@@ -156,7 +156,7 @@ static int fl512_attach(struct comedi_device *dev, struct comedi_devconfig *it)
printk(KERN_INFO "comedi: fl512: subdevice 0 initialized\n");
/* Analog output */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* define subdevice as Analog OUT */
s->type = COMEDI_SUBD_AO;
/* you can write it from userspace */
diff --git a/drivers/staging/comedi/drivers/gsc_hpdi.c b/drivers/staging/comedi/drivers/gsc_hpdi.c
index 79f580841dee..abff6603952a 100644
--- a/drivers/staging/comedi/drivers/gsc_hpdi.c
+++ b/drivers/staging/comedi/drivers/gsc_hpdi.c
@@ -436,7 +436,7 @@ static int setup_subdevices(struct comedi_device *dev)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* analog input subdevice */
dev->read_subdev = s;
/* dev->write_subdev = s; */
@@ -723,45 +723,24 @@ static int di_cmd_test(struct comedi_device *dev, struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
int err = 0;
- int tmp;
int i;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually
- * compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* uniqueness check */
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
diff --git a/drivers/staging/comedi/drivers/icp_multi.c b/drivers/staging/comedi/drivers/icp_multi.c
index b10ebdbc1f7e..d696d4d51e28 100644
--- a/drivers/staging/comedi/drivers/icp_multi.c
+++ b/drivers/staging/comedi/drivers/icp_multi.c
@@ -44,10 +44,7 @@ There are 4 x 12-bit Analogue Outputs. Ranges : 5V, 10V, +/-5V, +/-10V
4 x 16-bit counters
-Options:
- [0] - PCI bus number - if bus number and slot number are 0,
- then driver search for first unused card
- [1] - PCI slot number
+Configuration options: not applicable, uses PCI auto config
*/
#include <linux/interrupt.h>
@@ -56,16 +53,7 @@ Options:
#include <linux/delay.h>
#include <linux/pci.h>
-#include "icp_multi.h"
-
-#define DEVICE_ID 0x8000 /* Device ID */
-
-#define ICP_MULTI_EXTDEBUG
-
-/* Hardware types of the cards */
-#define TYPE_ICP_MULTI 0
-
-#define IORANGE_ICP_MULTI 32
+#define PCI_DEVICE_ID_ICP_MULTI 0x8000
#define ICP_MULTI_ADC_CSR 0 /* R/W: ADC command/status register */
#define ICP_MULTI_AI 2 /* R: Analogue input data */
@@ -124,32 +112,10 @@ static const char range_codes_analog[] = { 0x00, 0x20, 0x10, 0x30 };
Data & Structure declarations
==============================================================================
*/
-static unsigned short pci_list_builded; /*>0 list of card is known */
-
-struct boardtype {
- const char *name; /* driver name */
- int device_id;
- int iorange; /* I/O range len */
- char have_irq; /* 1=card support IRQ */
- char cardtype; /* 0=ICP Multi */
- int n_aichan; /* num of A/D chans */
- int n_aichand; /* num of A/D chans in diff mode */
- int n_aochan; /* num of D/A chans */
- int n_dichan; /* num of DI chans */
- int n_dochan; /* num of DO chans */
- int n_ctrs; /* num of counters */
- int ai_maxdata; /* resolution of A/D */
- int ao_maxdata; /* resolution of D/A */
- const struct comedi_lrange *rangelist_ai; /* rangelist for A/D */
- const char *rangecode; /* range codes for programming */
- const struct comedi_lrange *rangelist_ao; /* rangelist for D/A */
-};
struct icp_multi_private {
- struct pcilst_struct *card; /* pointer to card */
char valid; /* card is usable */
void __iomem *io_addr; /* Pointer to mapped io address */
- resource_size_t phys_iobase; /* Physical io address */
unsigned int AdcCmdStatus; /* ADC Command/Status register */
unsigned int DacCmdStatus; /* DAC Command/Status register */
unsigned int IntEnable; /* Interrupt Enable register */
@@ -164,40 +130,14 @@ struct icp_multi_private {
unsigned int do_data; /* Remember digital output data */
};
-#define devpriv ((struct icp_multi_private *)dev->private)
-#define this_board ((const struct boardtype *)dev->board_ptr)
-
-/*
-==============================================================================
-
-Name: setup_channel_list
-
-Description:
- This function sets the appropriate channel selection,
- differential input mode and range bits in the ADC Command/
- Status register.
-
-Parameters:
- struct comedi_device *dev Pointer to current service structure
- struct comedi_subdevice *s Pointer to current subdevice structure
- unsigned int *chanlist Pointer to packed channel list
- unsigned int n_chan Number of channels to scan
-
-Returns:Void
-
-==============================================================================
-*/
static void setup_channel_list(struct comedi_device *dev,
struct comedi_subdevice *s,
unsigned int *chanlist, unsigned int n_chan)
{
+ struct icp_multi_private *devpriv = dev->private;
unsigned int i, range, chanprog;
unsigned int diff;
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp multi EDBG: setup_channel_list(...,%d)\n", n_chan);
-#endif
devpriv->act_chanlist_len = n_chan;
devpriv->act_chanlist_pos = 0;
@@ -228,50 +168,23 @@ static void setup_channel_list(struct comedi_device *dev,
devpriv->AdcCmdStatus |= (chanprog << 8);
/* Get range for current channel */
- range = this_board->rangecode[CR_RANGE(chanlist[i])];
+ range = range_codes_analog[CR_RANGE(chanlist[i])];
/* Set range. bits 4-5 */
devpriv->AdcCmdStatus |= range;
/* Output channel, range, mode to ICP Multi */
writew(devpriv->AdcCmdStatus,
devpriv->io_addr + ICP_MULTI_ADC_CSR);
-
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "GS: %2d. [%4x]=%4x %4x\n", i, chanprog, range,
- devpriv->act_chanlist[i]);
-#endif
}
-
}
-/*
-==============================================================================
-
-Name: icp_multi_insn_read_ai
-
-Description:
- This function reads a single analogue input.
-
-Parameters:
- struct comedi_device *dev Pointer to current device structure
- struct comedi_subdevice *s Pointer to current subdevice structure
- struct comedi_insn *insn Pointer to current comedi instruction
- unsigned int *data Pointer to analogue input data
-
-Returns:int Nmuber of instructions executed
-
-==============================================================================
-*/
static int icp_multi_insn_read_ai(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct icp_multi_private *devpriv = dev->private;
int n, timeout;
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_read_ai(...)\n");
-#endif
/* Disable A/D conversion ready interrupt */
devpriv->IntEnable &= ~ADC_READY;
writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
@@ -283,12 +196,6 @@ static int icp_multi_insn_read_ai(struct comedi_device *dev,
/* Set up appropriate channel, mode and range data, for specified ch */
setup_channel_list(dev, s, &insn->chanspec, 1);
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG "icp_multi A ST=%4x IO=%p\n",
- readw(devpriv->io_addr + ICP_MULTI_ADC_CSR),
- devpriv->io_addr + ICP_MULTI_ADC_CSR);
-#endif
-
for (n = 0; n < insn->n; n++) {
/* Set start ADC bit */
devpriv->AdcCmdStatus |= ADC_ST;
@@ -296,18 +203,8 @@ static int icp_multi_insn_read_ai(struct comedi_device *dev,
devpriv->io_addr + ICP_MULTI_ADC_CSR);
devpriv->AdcCmdStatus &= ~ADC_ST;
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG "icp multi B n=%d ST=%4x\n", n,
- readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
-#endif
-
udelay(1);
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG "icp multi C n=%d ST=%4x\n", n,
- readw(devpriv->io_addr + ICP_MULTI_ADC_CSR));
-#endif
-
/* Wait for conversion to complete, or get fed up waiting */
timeout = 100;
while (timeout--) {
@@ -315,15 +212,6 @@ static int icp_multi_insn_read_ai(struct comedi_device *dev,
ICP_MULTI_ADC_CSR) & ADC_BSY))
goto conv_finish;
-#ifdef ICP_MULTI_EXTDEBUG
- if (!(timeout % 10))
- printk(KERN_DEBUG
- "icp multi D n=%d tm=%d ST=%4x\n", n,
- timeout,
- readw(devpriv->io_addr +
- ICP_MULTI_ADC_CSR));
-#endif
-
udelay(1);
}
@@ -342,11 +230,6 @@ static int icp_multi_insn_read_ai(struct comedi_device *dev,
/* Clear data received */
data[n] = 0;
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n",
- n);
-#endif
return -ETIME;
conv_finish:
@@ -362,41 +245,16 @@ conv_finish:
devpriv->IntStatus |= ADC_READY;
writew(devpriv->IntStatus, devpriv->io_addr + ICP_MULTI_INT_STAT);
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp multi EDBG: END: icp_multi_insn_read_ai(...) n=%d\n", n);
-#endif
return n;
}
-/*
-==============================================================================
-
-Name: icp_multi_insn_write_ao
-
-Description:
- This function writes a single analogue output.
-
-Parameters:
- struct comedi_device *dev Pointer to current device structure
- struct comedi_subdevice *s Pointer to current subdevice structure
- struct comedi_insn *insn Pointer to current comedi instruction
- unsigned int *data Pointer to analogue output data
-
-Returns:int Nmuber of instructions executed
-
-==============================================================================
-*/
static int icp_multi_insn_write_ao(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct icp_multi_private *devpriv = dev->private;
int n, chan, range, timeout;
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp multi EDBG: BGN: icp_multi_insn_write_ao(...)\n");
-#endif
/* Disable D/A conversion ready interrupt */
devpriv->IntEnable &= ~DAC_READY;
writew(devpriv->IntEnable, devpriv->io_addr + ICP_MULTI_INT_EN);
@@ -415,7 +273,7 @@ static int icp_multi_insn_write_ao(struct comedi_device *dev,
/* Bit 5 = 1 : 10V */
/* Bits 8-9 : Channel number */
devpriv->DacCmdStatus &= 0xfccf;
- devpriv->DacCmdStatus |= this_board->rangecode[range];
+ devpriv->DacCmdStatus |= range_codes_analog[range];
devpriv->DacCmdStatus |= (chan << 8);
writew(devpriv->DacCmdStatus, devpriv->io_addr + ICP_MULTI_DAC_CSR);
@@ -429,15 +287,6 @@ static int icp_multi_insn_write_ao(struct comedi_device *dev,
ICP_MULTI_DAC_CSR) & DAC_BSY))
goto dac_ready;
-#ifdef ICP_MULTI_EXTDEBUG
- if (!(timeout % 10))
- printk(KERN_DEBUG
- "icp multi A n=%d tm=%d ST=%4x\n", n,
- timeout,
- readw(devpriv->io_addr +
- ICP_MULTI_DAC_CSR));
-#endif
-
udelay(1);
}
@@ -456,11 +305,6 @@ static int icp_multi_insn_write_ao(struct comedi_device *dev,
/* Clear data received */
devpriv->ao_data[chan] = 0;
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n",
- n);
-#endif
return -ETIME;
dac_ready:
@@ -477,35 +321,14 @@ dac_ready:
devpriv->ao_data[chan] = data[n];
}
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp multi EDBG: END: icp_multi_insn_write_ao(...) n=%d\n", n);
-#endif
return n;
}
-/*
-==============================================================================
-
-Name: icp_multi_insn_read_ao
-
-Description:
- This function reads a single analogue output.
-
-Parameters:
- struct comedi_device *dev Pointer to current device structure
- struct comedi_subdevice *s Pointer to current subdevice structure
- struct comedi_insn *insn Pointer to current comedi instruction
- unsigned int *data Pointer to analogue output data
-
-Returns:int Nmuber of instructions executed
-
-==============================================================================
-*/
static int icp_multi_insn_read_ao(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct icp_multi_private *devpriv = dev->private;
int n, chan;
/* Get channel number */
@@ -518,58 +341,22 @@ static int icp_multi_insn_read_ao(struct comedi_device *dev,
return n;
}
-/*
-==============================================================================
-
-Name: icp_multi_insn_bits_di
-
-Description:
- This function reads the digital inputs.
-
-Parameters:
- struct comedi_device *dev Pointer to current device structure
- struct comedi_subdevice *s Pointer to current subdevice structure
- struct comedi_insn *insn Pointer to current comedi instruction
- unsigned int *data Pointer to analogue output data
-
-Returns:int Nmuber of instructions executed
-
-==============================================================================
-*/
static int icp_multi_insn_bits_di(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct icp_multi_private *devpriv = dev->private;
+
data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
return insn->n;
}
-/*
-==============================================================================
-
-Name: icp_multi_insn_bits_do
-
-Description:
- This function writes the appropriate digital outputs.
-
-Parameters:
- struct comedi_device *dev Pointer to current device structure
- struct comedi_subdevice *s Pointer to current subdevice structure
- struct comedi_insn *insn Pointer to current comedi instruction
- unsigned int *data Pointer to analogue output data
-
-Returns:int Nmuber of instructions executed
-
-==============================================================================
-*/
static int icp_multi_insn_bits_do(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG "icp multi EDBG: BGN: icp_multi_insn_bits_do(...)\n");
-#endif
+ struct icp_multi_private *devpriv = dev->private;
if (data[0]) {
s->state &= ~data[0];
@@ -582,30 +369,9 @@ static int icp_multi_insn_bits_do(struct comedi_device *dev,
data[1] = readw(devpriv->io_addr + ICP_MULTI_DI);
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_insn_bits_do(...)\n");
-#endif
return insn->n;
}
-/*
-==============================================================================
-
-Name: icp_multi_insn_read_ctr
-
-Description:
- This function reads the specified counter.
-
-Parameters:
- struct comedi_device *dev Pointer to current device structure
- struct comedi_subdevice *s Pointer to current subdevice structure
- struct comedi_insn *insn Pointer to current comedi instruction
- unsigned int *data Pointer to counter data
-
-Returns:int Nmuber of instructions executed
-
-==============================================================================
-*/
static int icp_multi_insn_read_ctr(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
@@ -613,24 +379,6 @@ static int icp_multi_insn_read_ctr(struct comedi_device *dev,
return 0;
}
-/*
-==============================================================================
-
-Name: icp_multi_insn_write_ctr
-
-Description:
- This function write to the specified counter.
-
-Parameters:
- struct comedi_device *dev Pointer to current device structure
- struct comedi_subdevice *s Pointer to current subdevice structure
- struct comedi_insn *insn Pointer to current comedi instruction
- unsigned int *data Pointer to counter data
-
-Returns:int Nmuber of instructions executed
-
-==============================================================================
-*/
static int icp_multi_insn_write_ctr(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
@@ -639,44 +387,18 @@ static int icp_multi_insn_write_ctr(struct comedi_device *dev,
return 0;
}
-/*
-==============================================================================
-
-Name: interrupt_service_icp_multi
-
-Description:
- This function is the interrupt service routine for all
- interrupts generated by the icp multi board.
-
-Parameters:
- int irq
- void *d Pointer to current device
-
-==============================================================================
-*/
static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
{
struct comedi_device *dev = d;
+ struct icp_multi_private *devpriv = dev->private;
int int_no;
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp multi EDBG: BGN: interrupt_service_icp_multi(%d,...)\n",
- irq);
-#endif
-
/* Is this interrupt from our board? */
int_no = readw(devpriv->io_addr + ICP_MULTI_INT_STAT) & Status_IRQ;
if (!int_no)
/* No, exit */
return IRQ_NONE;
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp multi EDBG: interrupt_service_icp_multi() ST: %4x\n",
- readw(devpriv->io_addr + ICP_MULTI_INT_STAT));
-#endif
-
/* Determine which interrupt is active & handle it */
switch (int_no) {
case ADC_READY:
@@ -700,44 +422,16 @@ static irqreturn_t interrupt_service_icp_multi(int irq, void *d)
}
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp multi EDBG: END: interrupt_service_icp_multi(...)\n");
-#endif
return IRQ_HANDLED;
}
#if 0
-/*
-==============================================================================
-
-Name: check_channel_list
-
-Description:
- This function checks if the channel list, provided by user
- is built correctly
-
-Parameters:
- struct comedi_device *dev Pointer to current service structure
- struct comedi_subdevice *s Pointer to current subdevice structure
- unsigned int *chanlist Pointer to packed channel list
- unsigned int n_chan Number of channels to scan
-
-Returns:int 0 = failure
- 1 = success
-
-==============================================================================
-*/
static int check_channel_list(struct comedi_device *dev,
struct comedi_subdevice *s,
unsigned int *chanlist, unsigned int n_chan)
{
unsigned int i;
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp multi EDBG: check_channel_list(...,%d)\n", n_chan);
-#endif
/* Check that we at least have one channel to check */
if (n_chan < 1) {
comedi_error(dev, "range/channel list is empty!");
@@ -747,13 +441,13 @@ static int check_channel_list(struct comedi_device *dev,
for (i = 0; i < n_chan; i++) {
/* Check that channel number is < maximum */
if (CR_AREF(chanlist[i]) == AREF_DIFF) {
- if (CR_CHAN(chanlist[i]) > this_board->n_aichand) {
+ if (CR_CHAN(chanlist[i]) > (s->nchan / 2)) {
comedi_error(dev,
"Incorrect differential ai ch-nr");
return 0;
}
} else {
- if (CR_CHAN(chanlist[i]) > this_board->n_aichan) {
+ if (CR_CHAN(chanlist[i]) > s->n_chan) {
comedi_error(dev,
"Incorrect ai channel number");
return 0;
@@ -764,295 +458,189 @@ static int check_channel_list(struct comedi_device *dev,
}
#endif
-/*
-==============================================================================
-
-Name: icp_multi_reset
-
-Description:
- This function resets the icp multi device to a 'safe' state
-
-Parameters:
- struct comedi_device *dev Pointer to current service structure
-
-Returns:int 0 = success
-
-==============================================================================
-*/
static int icp_multi_reset(struct comedi_device *dev)
{
+ struct icp_multi_private *devpriv = dev->private;
unsigned int i;
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp_multi EDBG: BGN: icp_multi_reset(...)\n");
-#endif
/* Clear INT enables and requests */
writew(0, devpriv->io_addr + ICP_MULTI_INT_EN);
writew(0x00ff, devpriv->io_addr + ICP_MULTI_INT_STAT);
- if (this_board->n_aochan)
- /* Set DACs to 0..5V range and 0V output */
- for (i = 0; i < this_board->n_aochan; i++) {
- devpriv->DacCmdStatus &= 0xfcce;
+ /* Set DACs to 0..5V range and 0V output */
+ for (i = 0; i < 4; i++) {
+ devpriv->DacCmdStatus &= 0xfcce;
+
+ /* Set channel number */
+ devpriv->DacCmdStatus |= (i << 8);
- /* Set channel number */
- devpriv->DacCmdStatus |= (i << 8);
+ /* Output 0V */
+ writew(0, devpriv->io_addr + ICP_MULTI_AO);
- /* Output 0V */
- writew(0, devpriv->io_addr + ICP_MULTI_AO);
+ /* Set start conversion bit */
+ devpriv->DacCmdStatus |= DAC_ST;
- /* Set start conversion bit */
- devpriv->DacCmdStatus |= DAC_ST;
+ /* Output to command / status register */
+ writew(devpriv->DacCmdStatus,
+ devpriv->io_addr + ICP_MULTI_DAC_CSR);
- /* Output to command / status register */
- writew(devpriv->DacCmdStatus,
- devpriv->io_addr + ICP_MULTI_DAC_CSR);
+ /* Delay to allow DAC time to recover */
+ udelay(1);
+ }
- /* Delay to allow DAC time to recover */
- udelay(1);
- }
- /* Digital outputs to 0 */
+ /* Digital outputs to 0 */
writew(0, devpriv->io_addr + ICP_MULTI_DO);
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "icp multi EDBG: END: icp_multi_reset(...)\n");
-#endif
return 0;
}
-static int icp_multi_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int icp_multi_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
+ struct icp_multi_private *devpriv;
struct comedi_subdevice *s;
- int ret, subdev, n_subdevices;
- unsigned int irq;
- struct pcilst_struct *card = NULL;
- resource_size_t io_addr[5], iobase;
- unsigned char pci_bus, pci_slot, pci_func;
+ resource_size_t iobase;
+ int ret;
- printk(KERN_WARNING
- "icp_multi EDBG: BGN: icp_multi_attach(...)\n");
+ comedi_set_hw_dev(dev, &pcidev->dev);
+ dev->board_name = dev->driver->driver_name;
- /* Allocate private data storage space */
- ret = alloc_private(dev, sizeof(struct icp_multi_private));
+ ret = alloc_private(dev, sizeof(*devpriv));
if (ret < 0)
return ret;
+ devpriv = dev->private;
- /* Initialise list of PCI cards in system, if not already done so */
- if (pci_list_builded++ == 0) {
- pci_card_list_init(PCI_VENDOR_ID_ICP,
-#ifdef ICP_MULTI_EXTDEBUG
- 1
-#else
- 0
-#endif
- );
- }
-
- printk(KERN_WARNING
- "Anne's comedi%d: icp_multi: board=%s", dev->minor,
- this_board->name);
-
- card = select_and_alloc_pci_card(PCI_VENDOR_ID_ICP,
- this_board->device_id, it->options[0],
- it->options[1]);
-
- if (card == NULL)
- return -EIO;
-
- devpriv->card = card;
-
- if ((pci_card_data(card, &pci_bus, &pci_slot, &pci_func, &io_addr[0],
- &irq)) < 0) {
- printk(KERN_WARNING " - Can't get configuration data!\n");
- return -EIO;
- }
-
- iobase = io_addr[2];
- devpriv->phys_iobase = iobase;
-
- printk(KERN_WARNING
- ", b:s:f=%d:%d:%d, io=0x%8llx \n", pci_bus, pci_slot, pci_func,
- (unsigned long long)iobase);
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
+ iobase = pci_resource_start(pcidev, 2);
+ dev->iobase = iobase;
devpriv->io_addr = ioremap(iobase, ICP_MULTI_SIZE);
-
- if (devpriv->io_addr == NULL) {
- printk(KERN_WARNING "ioremap failed.\n");
+ if (!devpriv->io_addr)
return -ENOMEM;
- }
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG
- "0x%08llx mapped to %p, ", (unsigned long long)iobase,
- devpriv->io_addr);
-#endif
- dev->board_name = this_board->name;
-
- n_subdevices = 0;
- if (this_board->n_aichan)
- n_subdevices++;
- if (this_board->n_aochan)
- n_subdevices++;
- if (this_board->n_dichan)
- n_subdevices++;
- if (this_board->n_dochan)
- n_subdevices++;
- if (this_board->n_ctrs)
- n_subdevices++;
-
- ret = comedi_alloc_subdevices(dev, n_subdevices);
+ ret = comedi_alloc_subdevices(dev, 5);
if (ret)
return ret;
icp_multi_reset(dev);
- if (this_board->have_irq) {
- if (irq) {
- if (request_irq(irq, interrupt_service_icp_multi,
- IRQF_SHARED, "Inova Icp Multi", dev)) {
- printk(KERN_WARNING
- "unable to allocate IRQ %u, DISABLING IT",
- irq);
- irq = 0; /* Can't use IRQ */
- } else
- printk(KERN_WARNING ", irq=%u", irq);
- } else
- printk(KERN_WARNING ", IRQ disabled");
- } else
- irq = 0;
-
- dev->irq = irq;
-
- printk(KERN_WARNING ".\n");
-
- subdev = 0;
-
- if (this_board->n_aichan) {
- s = dev->subdevices + subdev;
- dev->read_subdev = s;
- s->type = COMEDI_SUBD_AI;
- s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND;
- if (this_board->n_aichand)
- s->subdev_flags |= SDF_DIFF;
- s->n_chan = this_board->n_aichan;
- s->maxdata = this_board->ai_maxdata;
- s->len_chanlist = this_board->n_aichan;
- s->range_table = this_board->rangelist_ai;
- s->insn_read = icp_multi_insn_read_ai;
- subdev++;
+ if (pcidev->irq) {
+ ret = request_irq(pcidev->irq, interrupt_service_icp_multi,
+ IRQF_SHARED, dev->board_name, dev);
+ if (ret == 0)
+ dev->irq = pcidev->irq;
}
- if (this_board->n_aochan) {
- s = dev->subdevices + subdev;
- s->type = COMEDI_SUBD_AO;
- s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
- s->n_chan = this_board->n_aochan;
- s->maxdata = this_board->ao_maxdata;
- s->len_chanlist = this_board->n_aochan;
- s->range_table = this_board->rangelist_ao;
- s->insn_write = icp_multi_insn_write_ao;
- s->insn_read = icp_multi_insn_read_ao;
- subdev++;
- }
-
- if (this_board->n_dichan) {
- s = dev->subdevices + subdev;
- s->type = COMEDI_SUBD_DI;
- s->subdev_flags = SDF_READABLE;
- s->n_chan = this_board->n_dichan;
- s->maxdata = 1;
- s->len_chanlist = this_board->n_dichan;
- s->range_table = &range_digital;
- s->io_bits = 0;
- s->insn_bits = icp_multi_insn_bits_di;
- subdev++;
- }
-
- if (this_board->n_dochan) {
- s = dev->subdevices + subdev;
- s->type = COMEDI_SUBD_DO;
- s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
- s->n_chan = this_board->n_dochan;
- s->maxdata = 1;
- s->len_chanlist = this_board->n_dochan;
- s->range_table = &range_digital;
- s->io_bits = (1 << this_board->n_dochan) - 1;
- s->state = 0;
- s->insn_bits = icp_multi_insn_bits_do;
- subdev++;
- }
-
- if (this_board->n_ctrs) {
- s = dev->subdevices + subdev;
- s->type = COMEDI_SUBD_COUNTER;
- s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
- s->n_chan = this_board->n_ctrs;
- s->maxdata = 0xffff;
- s->len_chanlist = this_board->n_ctrs;
- s->state = 0;
- s->insn_read = icp_multi_insn_read_ctr;
- s->insn_write = icp_multi_insn_write_ctr;
- subdev++;
- }
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
+ s->n_chan = 16;
+ s->maxdata = 0x0fff;
+ s->len_chanlist = 16;
+ s->range_table = &range_analog;
+ s->insn_read = icp_multi_insn_read_ai;
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 4;
+ s->maxdata = 0x0fff;
+ s->len_chanlist = 4;
+ s->range_table = &range_analog;
+ s->insn_write = icp_multi_insn_write_ao;
+ s->insn_read = icp_multi_insn_read_ao;
+
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DI;
+ s->subdev_flags = SDF_READABLE;
+ s->n_chan = 16;
+ s->maxdata = 1;
+ s->len_chanlist = 16;
+ s->range_table = &range_digital;
+ s->io_bits = 0;
+ s->insn_bits = icp_multi_insn_bits_di;
+
+ s = &dev->subdevices[3];
+ s->type = COMEDI_SUBD_DO;
+ s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->len_chanlist = 8;
+ s->range_table = &range_digital;
+ s->io_bits = 0xff;
+ s->state = 0;
+ s->insn_bits = icp_multi_insn_bits_do;
+
+ s = &dev->subdevices[4];
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_WRITABLE | SDF_GROUND | SDF_COMMON;
+ s->n_chan = 4;
+ s->maxdata = 0xffff;
+ s->len_chanlist = 4;
+ s->state = 0;
+ s->insn_read = icp_multi_insn_read_ctr;
+ s->insn_write = icp_multi_insn_write_ctr;
devpriv->valid = 1;
-#ifdef ICP_MULTI_EXTDEBUG
- printk(KERN_DEBUG "icp multi EDBG: END: icp_multi_attach(...)\n");
-#endif
+ dev_info(dev->class_dev, "%s attached, irq %sabled\n",
+ dev->board_name, dev->irq ? "en" : "dis");
return 0;
}
static void icp_multi_detach(struct comedi_device *dev)
{
- if (dev->private)
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct icp_multi_private *devpriv = dev->private;
+
+ if (devpriv)
if (devpriv->valid)
icp_multi_reset(dev);
if (dev->irq)
free_irq(dev->irq, dev);
- if (dev->private && devpriv->io_addr)
+ if (devpriv && devpriv->io_addr)
iounmap(devpriv->io_addr);
- if (dev->private && devpriv->card)
- pci_card_free(devpriv->card);
- if (--pci_list_builded == 0)
- pci_card_list_cleanup(PCI_VENDOR_ID_ICP);
+ if (pcidev) {
+ if (dev->iobase)
+ comedi_pci_disable(pcidev);
+ }
}
-static const struct boardtype boardtypes[] = {
- {
- .name = "icp_multi",
- .device_id = DEVICE_ID,
- .iorange = IORANGE_ICP_MULTI,
- .have_irq = 1,
- .cardtype = TYPE_ICP_MULTI,
- .n_aichan = 16,
- .n_aichand = 8,
- .n_aochan = 4,
- .n_dichan = 16,
- .n_dochan = 8,
- .n_ctrs = 4,
- .ai_maxdata = 0x0fff,
- .ao_maxdata = 0x0fff,
- .rangelist_ai = &range_analog,
- .rangecode = range_codes_analog,
- .rangelist_ao = &range_analog,
- },
-};
-
static struct comedi_driver icp_multi_driver = {
.driver_name = "icp_multi",
.module = THIS_MODULE,
- .attach = icp_multi_attach,
+ .attach_pci = icp_multi_attach_pci,
.detach = icp_multi_detach,
- .num_names = ARRAY_SIZE(boardtypes),
- .board_name = &boardtypes[0].name,
- .offset = sizeof(struct boardtype),
};
-module_comedi_driver(icp_multi_driver);
+
+static int __devinit icp_multi_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
+{
+ return comedi_pci_auto_config(dev, &icp_multi_driver);
+}
+
+static void __devexit icp_multi_pci_remove(struct pci_dev *dev)
+{
+ comedi_pci_auto_unconfig(dev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(icp_multi_pci_table) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ICP, PCI_DEVICE_ID_ICP_MULTI) },
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, icp_multi_pci_table);
+
+static struct pci_driver icp_multi_pci_driver = {
+ .name = "icp_multi",
+ .id_table = icp_multi_pci_table,
+ .probe = icp_multi_pci_probe,
+ .remove = __devexit_p(icp_multi_pci_remove),
+};
+module_comedi_pci_driver(icp_multi_driver, icp_multi_pci_driver);
MODULE_AUTHOR("Comedi http://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
diff --git a/drivers/staging/comedi/drivers/icp_multi.h b/drivers/staging/comedi/drivers/icp_multi.h
deleted file mode 100644
index dbf9908cfde6..000000000000
--- a/drivers/staging/comedi/drivers/icp_multi.h
+++ /dev/null
@@ -1,297 +0,0 @@
-/*
- comedi/drivers/icp_multi.h
-
- Stuff for ICP Multi
-
- Author: Anne Smorthit <anne.smorthit@sfwte.ch>
-
-*/
-
-#ifndef _ICP_MULTI_H_
-#define _ICP_MULTI_H_
-
-#include "../comedidev.h"
-
-/****************************************************************************/
-
-struct pcilst_struct {
- struct pcilst_struct *next;
- int used;
- struct pci_dev *pcidev;
- unsigned short vendor;
- unsigned short device;
- unsigned char pci_bus;
- unsigned char pci_slot;
- unsigned char pci_func;
- resource_size_t io_addr[5];
- unsigned int irq;
-};
-
-struct pcilst_struct *inova_devices;
-/* ptr to root list of all Inova devices */
-
-/****************************************************************************/
-
-static void pci_card_list_init(unsigned short pci_vendor, char display);
-static void pci_card_list_cleanup(unsigned short pci_vendor);
-static struct pcilst_struct *find_free_pci_card_by_device(unsigned short
- vendor_id,
- unsigned short
- device_id);
-static int find_free_pci_card_by_position(unsigned short vendor_id,
- unsigned short device_id,
- unsigned short pci_bus,
- unsigned short pci_slot,
- struct pcilst_struct **card);
-static struct pcilst_struct *select_and_alloc_pci_card(unsigned short vendor_id,
- unsigned short device_id,
- unsigned short pci_bus,
- unsigned short pci_slot);
-
-static int pci_card_alloc(struct pcilst_struct *amcc);
-static int pci_card_free(struct pcilst_struct *amcc);
-static void pci_card_list_display(void);
-static int pci_card_data(struct pcilst_struct *amcc,
- unsigned char *pci_bus, unsigned char *pci_slot,
- unsigned char *pci_func, resource_size_t * io_addr,
- unsigned int *irq);
-
-/****************************************************************************/
-
-/* build list of Inova cards in this system */
-static void pci_card_list_init(unsigned short pci_vendor, char display)
-{
- struct pci_dev *pcidev = NULL;
- struct pcilst_struct *inova, *last;
- int i;
-
- inova_devices = NULL;
- last = NULL;
-
- for_each_pci_dev(pcidev) {
- if (pcidev->vendor == pci_vendor) {
- inova = kzalloc(sizeof(*inova), GFP_KERNEL);
- if (!inova) {
- printk
- ("icp_multi: pci_card_list_init: allocation failed\n");
- pci_dev_put(pcidev);
- break;
- }
-
- inova->pcidev = pci_dev_get(pcidev);
- if (last) {
- last->next = inova;
- } else {
- inova_devices = inova;
- }
- last = inova;
-
- inova->vendor = pcidev->vendor;
- inova->device = pcidev->device;
- inova->pci_bus = pcidev->bus->number;
- inova->pci_slot = PCI_SLOT(pcidev->devfn);
- inova->pci_func = PCI_FUNC(pcidev->devfn);
- /* Note: resources may be invalid if PCI device
- * not enabled, but they are corrected in
- * pci_card_alloc. */
- for (i = 0; i < 5; i++)
- inova->io_addr[i] =
- pci_resource_start(pcidev, i);
- inova->irq = pcidev->irq;
- }
- }
-
- if (display)
- pci_card_list_display();
-}
-
-/****************************************************************************/
-/* free up list of amcc cards in this system */
-static void pci_card_list_cleanup(unsigned short pci_vendor)
-{
- struct pcilst_struct *inova, *next;
-
- for (inova = inova_devices; inova; inova = next) {
- next = inova->next;
- pci_dev_put(inova->pcidev);
- kfree(inova);
- }
-
- inova_devices = NULL;
-}
-
-/****************************************************************************/
-/* find first unused card with this device_id */
-static struct pcilst_struct *find_free_pci_card_by_device(unsigned short
- vendor_id,
- unsigned short
- device_id)
-{
- struct pcilst_struct *inova, *next;
-
- for (inova = inova_devices; inova; inova = next) {
- next = inova->next;
- if ((!inova->used) && (inova->device == device_id)
- && (inova->vendor == vendor_id))
- return inova;
-
- }
-
- return NULL;
-}
-
-/****************************************************************************/
-/* find card on requested position */
-static int find_free_pci_card_by_position(unsigned short vendor_id,
- unsigned short device_id,
- unsigned short pci_bus,
- unsigned short pci_slot,
- struct pcilst_struct **card)
-{
- struct pcilst_struct *inova, *next;
-
- *card = NULL;
- for (inova = inova_devices; inova; inova = next) {
- next = inova->next;
- if ((inova->vendor == vendor_id) && (inova->device == device_id)
- && (inova->pci_bus == pci_bus)
- && (inova->pci_slot == pci_slot)) {
- if (!(inova->used)) {
- *card = inova;
- return 0; /* ok, card is found */
- } else {
- return 2; /* card exist but is used */
- }
- }
- }
-
- return 1; /* no card found */
-}
-
-/****************************************************************************/
-/* mark card as used */
-static int pci_card_alloc(struct pcilst_struct *inova)
-{
- int i;
-
- if (!inova) {
- printk(" - BUG!! inova is NULL!\n");
- return -1;
- }
-
- if (inova->used)
- return 1;
- if (comedi_pci_enable(inova->pcidev, "icp_multi")) {
- printk(" - Can't enable PCI device and request regions!\n");
- return -1;
- }
- /* Resources will be accurate now. */
- for (i = 0; i < 5; i++)
- inova->io_addr[i] = pci_resource_start(inova->pcidev, i);
- inova->irq = inova->pcidev->irq;
- inova->used = 1;
- return 0;
-}
-
-/****************************************************************************/
-/* mark card as free */
-static int pci_card_free(struct pcilst_struct *inova)
-{
- if (!inova)
- return -1;
-
- if (!inova->used)
- return 1;
- inova->used = 0;
- comedi_pci_disable(inova->pcidev);
- return 0;
-}
-
-/****************************************************************************/
-/* display list of found cards */
-static void pci_card_list_display(void)
-{
- struct pcilst_struct *inova, *next;
-
- printk("Anne's List of pci cards\n");
- printk("bus:slot:func vendor device io_inova io_daq irq used\n");
-
- for (inova = inova_devices; inova; inova = next) {
- next = inova->next;
- printk
- ("%2d %2d %2d 0x%4x 0x%4x 0x%8llx 0x%8llx %2u %2d\n",
- inova->pci_bus, inova->pci_slot, inova->pci_func,
- inova->vendor, inova->device,
- (unsigned long long)inova->io_addr[0],
- (unsigned long long)inova->io_addr[2], inova->irq,
- inova->used);
-
- }
-}
-
-/****************************************************************************/
-/* return all card information for driver */
-static int pci_card_data(struct pcilst_struct *inova,
- unsigned char *pci_bus, unsigned char *pci_slot,
- unsigned char *pci_func, resource_size_t * io_addr,
- unsigned int *irq)
-{
- int i;
-
- if (!inova)
- return -1;
- *pci_bus = inova->pci_bus;
- *pci_slot = inova->pci_slot;
- *pci_func = inova->pci_func;
- for (i = 0; i < 5; i++)
- io_addr[i] = inova->io_addr[i];
- *irq = inova->irq;
- return 0;
-}
-
-/****************************************************************************/
-/* select and alloc card */
-static struct pcilst_struct *select_and_alloc_pci_card(unsigned short vendor_id,
- unsigned short device_id,
- unsigned short pci_bus,
- unsigned short pci_slot)
-{
- struct pcilst_struct *card;
- int err;
-
- if ((pci_bus < 1) & (pci_slot < 1)) { /* use autodetection */
-
- card = find_free_pci_card_by_device(vendor_id, device_id);
- if (card == NULL) {
- printk(" - Unused card not found in system!\n");
- return NULL;
- }
- } else {
- switch (find_free_pci_card_by_position(vendor_id, device_id,
- pci_bus, pci_slot,
- &card)) {
- case 1:
- printk
- (" - Card not found on requested position b:s %d:%d!\n",
- pci_bus, pci_slot);
- return NULL;
- case 2:
- printk
- (" - Card on requested position is used b:s %d:%d!\n",
- pci_bus, pci_slot);
- return NULL;
- }
- }
-
- err = pci_card_alloc(card);
- if (err != 0) {
- if (err > 0)
- printk(" - Can't allocate card!\n");
- /* else: error already printed. */
- return NULL;
- }
-
- return card;
-}
-
-#endif
diff --git a/drivers/staging/comedi/drivers/ii_pci20kc.c b/drivers/staging/comedi/drivers/ii_pci20kc.c
index 0f9cfe662b9a..65ff1c9b973c 100644
--- a/drivers/staging/comedi/drivers/ii_pci20kc.c
+++ b/drivers/staging/comedi/drivers/ii_pci20kc.c
@@ -224,7 +224,7 @@ static int pci20xxx_attach(struct comedi_device *dev,
dev->minor, devpriv->ioaddr);
for (i = 0; i < PCI20000_MODULES; i++) {
- s = dev->subdevices + i;
+ s = &dev->subdevices[i];
id = readb(devpriv->ioaddr + (i + 1) * PCI20000_OFFSET);
s->private = devpriv->subdev_private + i;
sdp = s->private;
@@ -259,7 +259,7 @@ static int pci20xxx_attach(struct comedi_device *dev,
}
/* initialize struct pci20xxx_private */
- pci20xxx_dio_init(dev, dev->subdevices + PCI20000_MODULES);
+ pci20xxx_dio_init(dev, &dev->subdevices[PCI20000_MODULES]);
return 1;
}
diff --git a/drivers/staging/comedi/drivers/jr3_pci.c b/drivers/staging/comedi/drivers/jr3_pci.c
index 93f94cd7bae2..4a108ea8a9aa 100644
--- a/drivers/staging/comedi/drivers/jr3_pci.c
+++ b/drivers/staging/comedi/drivers/jr3_pci.c
@@ -362,10 +362,11 @@ static int jr3_pci_open(struct comedi_device *dev)
return 0;
}
-int read_idm_word(const u8 * data, size_t size, int *pos, unsigned int *val)
+static int read_idm_word(const u8 *data, size_t size, int *pos,
+ unsigned int *val)
{
int result = 0;
- if (pos != 0 && val != 0) {
+ if (pos && val) {
/* Skip over non hex */
for (; *pos < size && !isxdigit(data[*pos]); (*pos)++) {
}
@@ -875,7 +876,7 @@ static int jr3_pci_attach(struct comedi_device *dev,
p->maxdata_list[56] = 0xffff;
p->maxdata_list[57] = 0xffff;
/* Channel specific range and maxdata */
- dev->subdevices[i].range_table = 0;
+ dev->subdevices[i].range_table = NULL;
dev->subdevices[i].range_table_list =
p->range_table_list;
dev->subdevices[i].maxdata = 0;
@@ -884,7 +885,7 @@ static int jr3_pci_attach(struct comedi_device *dev,
}
/* Reset DSP card */
- devpriv->iobase->channel[0].reset = 0;
+ writel(0, &devpriv->iobase->channel[0].reset);
result = comedi_load_firmware(dev, "jr3pci.idm", jr3_download_firmware);
dev_dbg(dev->class_dev, "Firmare load %d\n", result);
diff --git a/drivers/staging/comedi/drivers/jr3_pci.h b/drivers/staging/comedi/drivers/jr3_pci.h
index a1469611d84e..9c42653d8f18 100644
--- a/drivers/staging/comedi/drivers/jr3_pci.h
+++ b/drivers/staging/comedi/drivers/jr3_pci.h
@@ -97,7 +97,7 @@ enum {
mz = 0x0020,
changeV2 = 0x0040,
changeV1 = 0x0080
-} vect_bits_t;
+};
/* WARNING_BITS */
/* The warning_bits structure shows the bit pattern for the warning
@@ -116,7 +116,7 @@ enum {
mx_near_sat = 0x0008,
my_near_sat = 0x0010,
mz_near_sat = 0x0020
-} warning_bits_t;
+};
/* ERROR_BITS */
/* XX_SAT */
diff --git a/drivers/staging/comedi/drivers/ke_counter.c b/drivers/staging/comedi/drivers/ke_counter.c
index d4e9292483a0..e867b720f666 100644
--- a/drivers/staging/comedi/drivers/ke_counter.c
+++ b/drivers/staging/comedi/drivers/ke_counter.c
@@ -28,11 +28,7 @@ Author: Michael Hillmann
Updated: Mon, 14 Apr 2008 15:42:42 +0100
Status: tested
-Configuration Options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first supported
- PCI device found will be used.
+Configuration Options: not applicable, uses PCI auto config
This driver is a simple driver to read the counter values from
Kolter Electronic PCI Counter Card.
@@ -111,82 +107,53 @@ static int cnt_rinsn(struct comedi_device *dev,
return 1;
}
-static struct pci_dev *cnt_find_pci_dev(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static const void *cnt_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
const struct cnt_board_struct *board;
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
int i;
- /* Probe the device to determine what device in the series it is. */
- for_each_pci_dev(pcidev) {
- if (bus || slot) {
- if (pcidev->bus->number != bus ||
- PCI_SLOT(pcidev->devfn) != slot)
- continue;
- }
- if (pcidev->vendor != PCI_VENDOR_ID_KOLTER)
- continue;
-
- for (i = 0; i < ARRAY_SIZE(cnt_boards); i++) {
- board = &cnt_boards[i];
- if (board->device_id != pcidev->device)
- continue;
-
- dev->board_ptr = board;
- return pcidev;
- }
+ for (i = 0; i < ARRAY_SIZE(cnt_boards); i++) {
+ board = &cnt_boards[i];
+ if (board->device_id == pcidev->device)
+ return board;
}
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
return NULL;
}
-static int cnt_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static int cnt_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
const struct cnt_board_struct *board;
- struct pci_dev *pcidev;
- struct comedi_subdevice *subdevice;
- unsigned long io_base;
- int error;
-
- pcidev = cnt_find_pci_dev(dev, it);
- if (!pcidev)
- return -EIO;
+ struct comedi_subdevice *s;
+ int ret;
+
comedi_set_hw_dev(dev, &pcidev->dev);
- board = comedi_board(dev);
+ board = cnt_find_boardinfo(dev, pcidev);
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
dev->board_name = board->name;
- /* enable PCI device and request regions */
- error = comedi_pci_enable(pcidev, CNT_DRIVER_NAME);
- if (error < 0) {
- printk(KERN_WARNING "comedi%d: "
- "failed to enable PCI device and request regions!\n",
- dev->minor);
- return error;
- }
-
- /* read register base address [PCI_BASE_ADDRESS #0] */
- io_base = pci_resource_start(pcidev, 0);
- dev->iobase = io_base;
+ ret = comedi_pci_enable(pcidev, dev->board_name);
+ if (ret)
+ return ret;
+ dev->iobase = pci_resource_start(pcidev, 0);
- error = comedi_alloc_subdevices(dev, 1);
- if (error)
- return error;
+ ret = comedi_alloc_subdevices(dev, 1);
+ if (ret)
+ return ret;
- subdevice = dev->subdevices + 0;
- dev->read_subdev = subdevice;
+ s = &dev->subdevices[0];
+ dev->read_subdev = s;
- subdevice->type = COMEDI_SUBD_COUNTER;
- subdevice->subdev_flags = SDF_READABLE /* | SDF_COMMON */ ;
- subdevice->n_chan = board->cnt_channel_nbr;
- subdevice->maxdata = (1 << board->cnt_bits) - 1;
- subdevice->insn_read = cnt_rinsn;
- subdevice->insn_write = cnt_winsn;
+ s->type = COMEDI_SUBD_COUNTER;
+ s->subdev_flags = SDF_READABLE /* | SDF_COMMON */ ;
+ s->n_chan = board->cnt_channel_nbr;
+ s->maxdata = (1 << board->cnt_bits) - 1;
+ s->insn_read = cnt_rinsn;
+ s->insn_write = cnt_winsn;
/* select 20MHz clock */
outb(3, dev->iobase + 248);
@@ -196,8 +163,9 @@ static int cnt_attach(struct comedi_device *dev, struct comedi_devconfig *it)
outb(0, dev->iobase + 0x20);
outb(0, dev->iobase + 0x40);
- printk(KERN_INFO "comedi%d: " CNT_DRIVER_NAME " attached.\n",
- dev->minor);
+ dev_info(dev->class_dev, "%s: %s attached\n",
+ dev->driver->driver_name, dev->board_name);
+
return 0;
}
@@ -208,14 +176,13 @@ static void cnt_detach(struct comedi_device *dev)
if (pcidev) {
if (dev->iobase)
comedi_pci_disable(pcidev);
- pci_dev_put(pcidev);
}
}
static struct comedi_driver ke_counter_driver = {
.driver_name = "ke_counter",
.module = THIS_MODULE,
- .attach = cnt_attach,
+ .attach_pci = cnt_attach_pci,
.detach = cnt_detach,
};
diff --git a/drivers/staging/comedi/drivers/me4000.c b/drivers/staging/comedi/drivers/me4000.c
index 9a8258e6fa45..22db35d091f8 100644
--- a/drivers/staging/comedi/drivers/me4000.c
+++ b/drivers/staging/comedi/drivers/me4000.c
@@ -35,13 +35,7 @@ Supports:
- Digital I/O
- Counter
-Configuration Options:
-
- [0] - PCI bus number (optional)
- [1] - PCI slot number (optional)
-
- If bus/slot is not specified, the first available PCI
- device will be used.
+Configuration Options: not applicable, uses PCI auto config
The firmware required by these boards is available in the
comedi_nonfree_firmware tarball available from
@@ -58,51 +52,306 @@ broken.
#include <linux/list.h>
#include <linux/spinlock.h>
-#include "me4000.h"
+#include "comedi_fc.h"
+#include "8253.h"
+
#if 0
/* file removed due to GPL incompatibility */
#include "me4000_fw.h"
#endif
-static const struct me4000_board me4000_boards[] = {
- {"ME-4650", 0x4650, {0, 0}, {16, 0, 0, 0}, {4}, {0} },
-
- {"ME-4660", 0x4660, {0, 0}, {32, 0, 16, 0}, {4}, {3} },
- {"ME-4660i", 0x4661, {0, 0}, {32, 0, 16, 0}, {4}, {3} },
- {"ME-4660s", 0x4662, {0, 0}, {32, 8, 16, 0}, {4}, {3} },
- {"ME-4660is", 0x4663, {0, 0}, {32, 8, 16, 0}, {4}, {3} },
-
- {"ME-4670", 0x4670, {4, 0}, {32, 0, 16, 1}, {4}, {3} },
- {"ME-4670i", 0x4671, {4, 0}, {32, 0, 16, 1}, {4}, {3} },
- {"ME-4670s", 0x4672, {4, 0}, {32, 8, 16, 1}, {4}, {3} },
- {"ME-4670is", 0x4673, {4, 0}, {32, 8, 16, 1}, {4}, {3} },
+#define PCI_VENDOR_ID_MEILHAUS 0x1402
+
+#define PCI_DEVICE_ID_MEILHAUS_ME4650 0x4650
+#define PCI_DEVICE_ID_MEILHAUS_ME4660 0x4660
+#define PCI_DEVICE_ID_MEILHAUS_ME4660I 0x4661
+#define PCI_DEVICE_ID_MEILHAUS_ME4660S 0x4662
+#define PCI_DEVICE_ID_MEILHAUS_ME4660IS 0x4663
+#define PCI_DEVICE_ID_MEILHAUS_ME4670 0x4670
+#define PCI_DEVICE_ID_MEILHAUS_ME4670I 0x4671
+#define PCI_DEVICE_ID_MEILHAUS_ME4670S 0x4672
+#define PCI_DEVICE_ID_MEILHAUS_ME4670IS 0x4673
+#define PCI_DEVICE_ID_MEILHAUS_ME4680 0x4680
+#define PCI_DEVICE_ID_MEILHAUS_ME4680I 0x4681
+#define PCI_DEVICE_ID_MEILHAUS_ME4680S 0x4682
+#define PCI_DEVICE_ID_MEILHAUS_ME4680IS 0x4683
- {"ME-4680", 0x4680, {4, 4}, {32, 0, 16, 1}, {4}, {3} },
- {"ME-4680i", 0x4681, {4, 4}, {32, 0, 16, 1}, {4}, {3} },
- {"ME-4680s", 0x4682, {4, 4}, {32, 8, 16, 1}, {4}, {3} },
- {"ME-4680is", 0x4683, {4, 4}, {32, 8, 16, 1}, {4}, {3} },
+/*
+ * ME4000 Register map and bit defines
+ */
+#define ME4000_AO_CHAN(x) ((x) * 0x18)
+
+#define ME4000_AO_CTRL_REG(x) (0x00 + ME4000_AO_CHAN(x))
+#define ME4000_AO_CTRL_BIT_MODE_0 (1 << 0)
+#define ME4000_AO_CTRL_BIT_MODE_1 (1 << 1)
+#define ME4000_AO_CTRL_MASK_MODE (3 << 0)
+#define ME4000_AO_CTRL_BIT_STOP (1 << 2)
+#define ME4000_AO_CTRL_BIT_ENABLE_FIFO (1 << 3)
+#define ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG (1 << 4)
+#define ME4000_AO_CTRL_BIT_EX_TRIG_EDGE (1 << 5)
+#define ME4000_AO_CTRL_BIT_IMMEDIATE_STOP (1 << 7)
+#define ME4000_AO_CTRL_BIT_ENABLE_DO (1 << 8)
+#define ME4000_AO_CTRL_BIT_ENABLE_IRQ (1 << 9)
+#define ME4000_AO_CTRL_BIT_RESET_IRQ (1 << 10)
+#define ME4000_AO_STATUS_REG(x) (0x04 + ME4000_AO_CHAN(x))
+#define ME4000_AO_STATUS_BIT_FSM (1 << 0)
+#define ME4000_AO_STATUS_BIT_FF (1 << 1)
+#define ME4000_AO_STATUS_BIT_HF (1 << 2)
+#define ME4000_AO_STATUS_BIT_EF (1 << 3)
+#define ME4000_AO_FIFO_REG(x) (0x08 + ME4000_AO_CHAN(x))
+#define ME4000_AO_SINGLE_REG(x) (0x0c + ME4000_AO_CHAN(x))
+#define ME4000_AO_TIMER_REG(x) (0x10 + ME4000_AO_CHAN(x))
+#define ME4000_AI_CTRL_REG 0x74
+#define ME4000_AI_STATUS_REG 0x74
+#define ME4000_AI_CTRL_BIT_MODE_0 (1 << 0)
+#define ME4000_AI_CTRL_BIT_MODE_1 (1 << 1)
+#define ME4000_AI_CTRL_BIT_MODE_2 (1 << 2)
+#define ME4000_AI_CTRL_BIT_SAMPLE_HOLD (1 << 3)
+#define ME4000_AI_CTRL_BIT_IMMEDIATE_STOP (1 << 4)
+#define ME4000_AI_CTRL_BIT_STOP (1 << 5)
+#define ME4000_AI_CTRL_BIT_CHANNEL_FIFO (1 << 6)
+#define ME4000_AI_CTRL_BIT_DATA_FIFO (1 << 7)
+#define ME4000_AI_CTRL_BIT_FULLSCALE (1 << 8)
+#define ME4000_AI_CTRL_BIT_OFFSET (1 << 9)
+#define ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG (1 << 10)
+#define ME4000_AI_CTRL_BIT_EX_TRIG (1 << 11)
+#define ME4000_AI_CTRL_BIT_EX_TRIG_FALLING (1 << 12)
+#define ME4000_AI_CTRL_BIT_EX_IRQ (1 << 13)
+#define ME4000_AI_CTRL_BIT_EX_IRQ_RESET (1 << 14)
+#define ME4000_AI_CTRL_BIT_LE_IRQ (1 << 15)
+#define ME4000_AI_CTRL_BIT_LE_IRQ_RESET (1 << 16)
+#define ME4000_AI_CTRL_BIT_HF_IRQ (1 << 17)
+#define ME4000_AI_CTRL_BIT_HF_IRQ_RESET (1 << 18)
+#define ME4000_AI_CTRL_BIT_SC_IRQ (1 << 19)
+#define ME4000_AI_CTRL_BIT_SC_IRQ_RESET (1 << 20)
+#define ME4000_AI_CTRL_BIT_SC_RELOAD (1 << 21)
+#define ME4000_AI_STATUS_BIT_EF_CHANNEL (1 << 22)
+#define ME4000_AI_STATUS_BIT_HF_CHANNEL (1 << 23)
+#define ME4000_AI_STATUS_BIT_FF_CHANNEL (1 << 24)
+#define ME4000_AI_STATUS_BIT_EF_DATA (1 << 25)
+#define ME4000_AI_STATUS_BIT_HF_DATA (1 << 26)
+#define ME4000_AI_STATUS_BIT_FF_DATA (1 << 27)
+#define ME4000_AI_STATUS_BIT_LE (1 << 28)
+#define ME4000_AI_STATUS_BIT_FSM (1 << 29)
+#define ME4000_AI_CTRL_BIT_EX_TRIG_BOTH (1 << 31)
+#define ME4000_AI_CHANNEL_LIST_REG 0x78
+#define ME4000_AI_LIST_INPUT_SINGLE_ENDED (0 << 5)
+#define ME4000_AI_LIST_INPUT_DIFFERENTIAL (1 << 5)
+#define ME4000_AI_LIST_RANGE_BIPOLAR_10 (0 << 6)
+#define ME4000_AI_LIST_RANGE_BIPOLAR_2_5 (1 << 6)
+#define ME4000_AI_LIST_RANGE_UNIPOLAR_10 (2 << 6)
+#define ME4000_AI_LIST_RANGE_UNIPOLAR_2_5 (3 << 6)
+#define ME4000_AI_LIST_LAST_ENTRY (1 << 8)
+#define ME4000_AI_DATA_REG 0x7c
+#define ME4000_AI_CHAN_TIMER_REG 0x80
+#define ME4000_AI_CHAN_PRE_TIMER_REG 0x84
+#define ME4000_AI_SCAN_TIMER_LOW_REG 0x88
+#define ME4000_AI_SCAN_TIMER_HIGH_REG 0x8c
+#define ME4000_AI_SCAN_PRE_TIMER_LOW_REG 0x90
+#define ME4000_AI_SCAN_PRE_TIMER_HIGH_REG 0x94
+#define ME4000_AI_START_REG 0x98
+#define ME4000_IRQ_STATUS_REG 0x9c
+#define ME4000_IRQ_STATUS_BIT_EX (1 << 0)
+#define ME4000_IRQ_STATUS_BIT_LE (1 << 1)
+#define ME4000_IRQ_STATUS_BIT_AI_HF (1 << 2)
+#define ME4000_IRQ_STATUS_BIT_AO_0_HF (1 << 3)
+#define ME4000_IRQ_STATUS_BIT_AO_1_HF (1 << 4)
+#define ME4000_IRQ_STATUS_BIT_AO_2_HF (1 << 5)
+#define ME4000_IRQ_STATUS_BIT_AO_3_HF (1 << 6)
+#define ME4000_IRQ_STATUS_BIT_SC (1 << 7)
+#define ME4000_DIO_PORT_0_REG 0xa0
+#define ME4000_DIO_PORT_1_REG 0xa4
+#define ME4000_DIO_PORT_2_REG 0xa8
+#define ME4000_DIO_PORT_3_REG 0xac
+#define ME4000_DIO_DIR_REG 0xb0
+#define ME4000_AO_LOADSETREG_XX 0xb4
+#define ME4000_DIO_CTRL_REG 0xb8
+#define ME4000_DIO_CTRL_BIT_MODE_0 (1 << 0)
+#define ME4000_DIO_CTRL_BIT_MODE_1 (1 << 1)
+#define ME4000_DIO_CTRL_BIT_MODE_2 (1 << 2)
+#define ME4000_DIO_CTRL_BIT_MODE_3 (1 << 3)
+#define ME4000_DIO_CTRL_BIT_MODE_4 (1 << 4)
+#define ME4000_DIO_CTRL_BIT_MODE_5 (1 << 5)
+#define ME4000_DIO_CTRL_BIT_MODE_6 (1 << 6)
+#define ME4000_DIO_CTRL_BIT_MODE_7 (1 << 7)
+#define ME4000_DIO_CTRL_BIT_FUNCTION_0 (1 << 8)
+#define ME4000_DIO_CTRL_BIT_FUNCTION_1 (1 << 9)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_0 (1 << 10)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_1 (1 << 11)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_2 (1 << 12)
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_3 (1 << 13)
+#define ME4000_AO_DEMUX_ADJUST_REG 0xbc
+#define ME4000_AO_DEMUX_ADJUST_VALUE 0x4c
+#define ME4000_AI_SAMPLE_COUNTER_REG 0xc0
- {0},
+/*
+ * PLX Register map and bit defines
+ */
+#define PLX_INTCSR 0x4c
+#define PLX_INTCSR_LOCAL_INT1_EN (1 << 0)
+#define PLX_INTCSR_LOCAL_INT1_POL (1 << 1)
+#define PLX_INTCSR_LOCAL_INT1_STATE (1 << 2)
+#define PLX_INTCSR_LOCAL_INT2_EN (1 << 3)
+#define PLX_INTCSR_LOCAL_INT2_POL (1 << 4)
+#define PLX_INTCSR_LOCAL_INT2_STATE (1 << 5)
+#define PLX_INTCSR_PCI_INT_EN (1 << 6)
+#define PLX_INTCSR_SOFT_INT (1 << 7)
+#define PLX_ICR 0x50
+#define PLX_ICR_BIT_EEPROM_CLOCK_SET (1 << 24)
+#define PLX_ICR_BIT_EEPROM_CHIP_SELECT (1 << 25)
+#define PLX_ICR_BIT_EEPROM_WRITE (1 << 26)
+#define PLX_ICR_BIT_EEPROM_READ (1 << 27)
+#define PLX_ICR_BIT_EEPROM_VALID (1 << 28)
+#define PLX_ICR_MASK_EEPROM (0x1f << 24)
+
+#define EEPROM_DELAY 1
+
+#define ME4000_AI_FIFO_COUNT 2048
+
+#define ME4000_AI_MIN_TICKS 66
+#define ME4000_AI_MIN_SAMPLE_TIME 2000
+#define ME4000_AI_BASE_FREQUENCY (unsigned int) 33E6
+
+#define ME4000_AI_CHANNEL_LIST_COUNT 1024
+
+struct me4000_info {
+ unsigned long plx_regbase;
+ unsigned long timer_regbase;
+
+ unsigned int ao_readback[4];
};
-#define ME4000_BOARD_VERSIONS (ARRAY_SIZE(me4000_boards) - 1)
-
-/*-----------------------------------------------------------------------------
- Meilhaus function prototypes
- ---------------------------------------------------------------------------*/
-static int get_registers(struct comedi_device *dev, struct pci_dev *pci_dev_p);
-static int init_board_info(struct comedi_device *dev,
- struct pci_dev *pci_dev_p);
-static int init_ao_context(struct comedi_device *dev);
-static int init_ai_context(struct comedi_device *dev);
-static int init_dio_context(struct comedi_device *dev);
-static int init_cnt_context(struct comedi_device *dev);
-static int xilinx_download(struct comedi_device *dev);
-static int reset_board(struct comedi_device *dev);
+struct me4000_board {
+ const char *name;
+ unsigned short device_id;
+ int ao_nchan;
+ int ao_fifo;
+ int ai_nchan;
+ int ai_diff_nchan;
+ int ai_sh_nchan;
+ int ex_trig_analog;
+ int dio_nchan;
+ int has_counter;
+};
-static int ai_write_chanlist(struct comedi_device *dev,
- struct comedi_subdevice *s,
- struct comedi_cmd *cmd);
+static const struct me4000_board me4000_boards[] = {
+ {
+ .name = "ME-4650",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4650,
+ .ai_nchan = 16,
+ .dio_nchan = 32,
+ }, {
+ .name = "ME-4660",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4660,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ }, {
+ .name = "ME-4660i",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4660I,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ }, {
+ .name = "ME-4660s",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4660S,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ }, {
+ .name = "ME-4660is",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4660IS,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ }, {
+ .name = "ME-4670",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4670,
+ .ao_nchan = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ }, {
+ .name = "ME-4670i",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4670I,
+ .ao_nchan = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ }, {
+ .name = "ME-4670s",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4670S,
+ .ao_nchan = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ }, {
+ .name = "ME-4670is",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4670IS,
+ .ao_nchan = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ }, {
+ .name = "ME-4680",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4680,
+ .ao_nchan = 4,
+ .ao_fifo = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ }, {
+ .name = "ME-4680i",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4680I,
+ .ao_nchan = 4,
+ .ao_fifo = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ }, {
+ .name = "ME-4680s",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4680S,
+ .ao_nchan = 4,
+ .ao_fifo = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ }, {
+ .name = "ME-4680is",
+ .device_id = PCI_DEVICE_ID_MEILHAUS_ME4680IS,
+ .ao_nchan = 4,
+ .ao_fifo = 4,
+ .ai_nchan = 32,
+ .ai_diff_nchan = 16,
+ .ai_sh_nchan = 8,
+ .ex_trig_analog = 1,
+ .dio_nchan = 32,
+ .has_counter = 1,
+ },
+};
static const struct comedi_lrange me4000_ai_range = {
4,
@@ -114,380 +363,6 @@ static const struct comedi_lrange me4000_ai_range = {
}
};
-static const struct comedi_lrange me4000_ao_range = {
- 1,
- {
- BIP_RANGE(10),
- }
-};
-
-static int me4000_probe(struct comedi_device *dev, struct comedi_devconfig *it)
-{
- struct pci_dev *pci_device = NULL;
- int result, i;
- struct me4000_board *board;
-
- /* Allocate private memory */
- if (alloc_private(dev, sizeof(struct me4000_info)) < 0)
- return -ENOMEM;
-
- /*
- * Probe the device to determine what device in the series it is.
- */
- for_each_pci_dev(pci_device) {
- if (pci_device->vendor == PCI_VENDOR_ID_MEILHAUS) {
- for (i = 0; i < ME4000_BOARD_VERSIONS; i++) {
- if (me4000_boards[i].device_id ==
- pci_device->device) {
- /*
- * Was a particular
- * bus/slot requested?
- */
- if ((it->options[0] != 0)
- || (it->options[1] != 0)) {
- /*
- * Are we on the wrong
- * bus/slot?
- */
- if (pci_device->bus->number !=
- it->options[0]
- ||
- PCI_SLOT(pci_device->devfn)
- != it->options[1]) {
- continue;
- }
- }
- dev->board_ptr = me4000_boards + i;
- board =
- (struct me4000_board *)
- dev->board_ptr;
- info->pci_dev_p = pci_device;
- goto found;
- }
- }
- }
- }
-
- printk(KERN_ERR
- "comedi%d: me4000: me4000_probe(): "
- "No supported board found (req. bus/slot : %d/%d)\n",
- dev->minor, it->options[0], it->options[1]);
- return -ENODEV;
-
-found:
-
- printk(KERN_INFO
- "comedi%d: me4000: me4000_probe(): "
- "Found %s at PCI bus %d, slot %d\n",
- dev->minor, me4000_boards[i].name, pci_device->bus->number,
- PCI_SLOT(pci_device->devfn));
-
- /* Set data in device structure */
- dev->board_name = board->name;
-
- /* Enable PCI device and request regions */
- result = comedi_pci_enable(pci_device, dev->board_name);
- if (result) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_probe(): Cannot enable PCI "
- "device and request I/O regions\n", dev->minor);
- return result;
- }
-
- /* Get the PCI base registers */
- result = get_registers(dev, pci_device);
- if (result) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_probe(): "
- "Cannot get registers\n", dev->minor);
- return result;
- }
- /* Initialize board info */
- result = init_board_info(dev, pci_device);
- if (result) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_probe(): "
- "Cannot init baord info\n", dev->minor);
- return result;
- }
-
- /* Init analog output context */
- result = init_ao_context(dev);
- if (result) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_probe(): "
- "Cannot init ao context\n", dev->minor);
- return result;
- }
-
- /* Init analog input context */
- result = init_ai_context(dev);
- if (result) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_probe(): "
- "Cannot init ai context\n", dev->minor);
- return result;
- }
-
- /* Init digital I/O context */
- result = init_dio_context(dev);
- if (result) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_probe(): "
- "Cannot init dio context\n", dev->minor);
- return result;
- }
-
- /* Init counter context */
- result = init_cnt_context(dev);
- if (result) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_probe(): "
- "Cannot init cnt context\n", dev->minor);
- return result;
- }
-
- /* Download the xilinx firmware */
- result = xilinx_download(dev);
- if (result) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_probe(): "
- "Can't download firmware\n", dev->minor);
- return result;
- }
-
- /* Make a hardware reset */
- result = reset_board(dev);
- if (result) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_probe(): Can't reset board\n",
- dev->minor);
- return result;
- }
-
- return 0;
-}
-
-static int get_registers(struct comedi_device *dev, struct pci_dev *pci_dev_p)
-{
- /*--------------------------- plx regbase -------------------------------*/
-
- info->plx_regbase = pci_resource_start(pci_dev_p, 1);
- if (info->plx_regbase == 0) {
- printk(KERN_ERR
- "comedi%d: me4000: get_registers(): "
- "PCI base address 1 is not available\n", dev->minor);
- return -ENODEV;
- }
- info->plx_regbase_size = pci_resource_len(pci_dev_p, 1);
-
- /*--------------------------- me4000 regbase ----------------------------*/
-
- info->me4000_regbase = pci_resource_start(pci_dev_p, 2);
- if (info->me4000_regbase == 0) {
- printk(KERN_ERR
- "comedi%d: me4000: get_registers(): "
- "PCI base address 2 is not available\n", dev->minor);
- return -ENODEV;
- }
- info->me4000_regbase_size = pci_resource_len(pci_dev_p, 2);
-
- /*--------------------------- timer regbase ------------------------------*/
-
- info->timer_regbase = pci_resource_start(pci_dev_p, 3);
- if (info->timer_regbase == 0) {
- printk(KERN_ERR
- "comedi%d: me4000: get_registers(): "
- "PCI base address 3 is not available\n", dev->minor);
- return -ENODEV;
- }
- info->timer_regbase_size = pci_resource_len(pci_dev_p, 3);
-
- /*--------------------------- program regbase ----------------------------*/
-
- info->program_regbase = pci_resource_start(pci_dev_p, 5);
- if (info->program_regbase == 0) {
- printk(KERN_ERR
- "comedi%d: me4000: get_registers(): "
- "PCI base address 5 is not available\n", dev->minor);
- return -ENODEV;
- }
- info->program_regbase_size = pci_resource_len(pci_dev_p, 5);
-
- return 0;
-}
-
-static int init_board_info(struct comedi_device *dev, struct pci_dev *pci_dev_p)
-{
- int result;
-
- /* Init spin locks */
- /* spin_lock_init(&info->preload_lock); */
- /* spin_lock_init(&info->ai_ctrl_lock); */
-
- /* Get the serial number */
- result = pci_read_config_dword(pci_dev_p, 0x2C, &info->serial_no);
- if (result != PCIBIOS_SUCCESSFUL)
- return result;
-
- /* Get the hardware revision */
- result = pci_read_config_byte(pci_dev_p, 0x08, &info->hw_revision);
- if (result != PCIBIOS_SUCCESSFUL)
- return result;
-
- /* Get the vendor id */
- info->vendor_id = pci_dev_p->vendor;
-
- /* Get the device id */
- info->device_id = pci_dev_p->device;
-
- /* Get the irq assigned to the board */
- info->irq = pci_dev_p->irq;
-
- return 0;
-}
-
-static int init_ao_context(struct comedi_device *dev)
-{
- int i;
-
- for (i = 0; i < thisboard->ao.count; i++) {
- /* spin_lock_init(&info->ao_context[i].use_lock); */
- info->ao_context[i].irq = info->irq;
-
- switch (i) {
- case 0:
- info->ao_context[i].ctrl_reg =
- info->me4000_regbase + ME4000_AO_00_CTRL_REG;
- info->ao_context[i].status_reg =
- info->me4000_regbase + ME4000_AO_00_STATUS_REG;
- info->ao_context[i].fifo_reg =
- info->me4000_regbase + ME4000_AO_00_FIFO_REG;
- info->ao_context[i].single_reg =
- info->me4000_regbase + ME4000_AO_00_SINGLE_REG;
- info->ao_context[i].timer_reg =
- info->me4000_regbase + ME4000_AO_00_TIMER_REG;
- info->ao_context[i].irq_status_reg =
- info->me4000_regbase + ME4000_IRQ_STATUS_REG;
- info->ao_context[i].preload_reg =
- info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
- break;
- case 1:
- info->ao_context[i].ctrl_reg =
- info->me4000_regbase + ME4000_AO_01_CTRL_REG;
- info->ao_context[i].status_reg =
- info->me4000_regbase + ME4000_AO_01_STATUS_REG;
- info->ao_context[i].fifo_reg =
- info->me4000_regbase + ME4000_AO_01_FIFO_REG;
- info->ao_context[i].single_reg =
- info->me4000_regbase + ME4000_AO_01_SINGLE_REG;
- info->ao_context[i].timer_reg =
- info->me4000_regbase + ME4000_AO_01_TIMER_REG;
- info->ao_context[i].irq_status_reg =
- info->me4000_regbase + ME4000_IRQ_STATUS_REG;
- info->ao_context[i].preload_reg =
- info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
- break;
- case 2:
- info->ao_context[i].ctrl_reg =
- info->me4000_regbase + ME4000_AO_02_CTRL_REG;
- info->ao_context[i].status_reg =
- info->me4000_regbase + ME4000_AO_02_STATUS_REG;
- info->ao_context[i].fifo_reg =
- info->me4000_regbase + ME4000_AO_02_FIFO_REG;
- info->ao_context[i].single_reg =
- info->me4000_regbase + ME4000_AO_02_SINGLE_REG;
- info->ao_context[i].timer_reg =
- info->me4000_regbase + ME4000_AO_02_TIMER_REG;
- info->ao_context[i].irq_status_reg =
- info->me4000_regbase + ME4000_IRQ_STATUS_REG;
- info->ao_context[i].preload_reg =
- info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
- break;
- case 3:
- info->ao_context[i].ctrl_reg =
- info->me4000_regbase + ME4000_AO_03_CTRL_REG;
- info->ao_context[i].status_reg =
- info->me4000_regbase + ME4000_AO_03_STATUS_REG;
- info->ao_context[i].fifo_reg =
- info->me4000_regbase + ME4000_AO_03_FIFO_REG;
- info->ao_context[i].single_reg =
- info->me4000_regbase + ME4000_AO_03_SINGLE_REG;
- info->ao_context[i].timer_reg =
- info->me4000_regbase + ME4000_AO_03_TIMER_REG;
- info->ao_context[i].irq_status_reg =
- info->me4000_regbase + ME4000_IRQ_STATUS_REG;
- info->ao_context[i].preload_reg =
- info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
- break;
- default:
- break;
- }
- }
-
- return 0;
-}
-
-static int init_ai_context(struct comedi_device *dev)
-{
- info->ai_context.irq = info->irq;
-
- info->ai_context.ctrl_reg = info->me4000_regbase + ME4000_AI_CTRL_REG;
- info->ai_context.status_reg =
- info->me4000_regbase + ME4000_AI_STATUS_REG;
- info->ai_context.channel_list_reg =
- info->me4000_regbase + ME4000_AI_CHANNEL_LIST_REG;
- info->ai_context.data_reg = info->me4000_regbase + ME4000_AI_DATA_REG;
- info->ai_context.chan_timer_reg =
- info->me4000_regbase + ME4000_AI_CHAN_TIMER_REG;
- info->ai_context.chan_pre_timer_reg =
- info->me4000_regbase + ME4000_AI_CHAN_PRE_TIMER_REG;
- info->ai_context.scan_timer_low_reg =
- info->me4000_regbase + ME4000_AI_SCAN_TIMER_LOW_REG;
- info->ai_context.scan_timer_high_reg =
- info->me4000_regbase + ME4000_AI_SCAN_TIMER_HIGH_REG;
- info->ai_context.scan_pre_timer_low_reg =
- info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_LOW_REG;
- info->ai_context.scan_pre_timer_high_reg =
- info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_HIGH_REG;
- info->ai_context.start_reg = info->me4000_regbase + ME4000_AI_START_REG;
- info->ai_context.irq_status_reg =
- info->me4000_regbase + ME4000_IRQ_STATUS_REG;
- info->ai_context.sample_counter_reg =
- info->me4000_regbase + ME4000_AI_SAMPLE_COUNTER_REG;
-
- return 0;
-}
-
-static int init_dio_context(struct comedi_device *dev)
-{
- info->dio_context.dir_reg = info->me4000_regbase + ME4000_DIO_DIR_REG;
- info->dio_context.ctrl_reg = info->me4000_regbase + ME4000_DIO_CTRL_REG;
- info->dio_context.port_0_reg =
- info->me4000_regbase + ME4000_DIO_PORT_0_REG;
- info->dio_context.port_1_reg =
- info->me4000_regbase + ME4000_DIO_PORT_1_REG;
- info->dio_context.port_2_reg =
- info->me4000_regbase + ME4000_DIO_PORT_2_REG;
- info->dio_context.port_3_reg =
- info->me4000_regbase + ME4000_DIO_PORT_3_REG;
-
- return 0;
-}
-
-static int init_cnt_context(struct comedi_device *dev)
-{
- info->cnt_context.ctrl_reg = info->timer_regbase + ME4000_CNT_CTRL_REG;
- info->cnt_context.counter_0_reg =
- info->timer_regbase + ME4000_CNT_COUNTER_0_REG;
- info->cnt_context.counter_1_reg =
- info->timer_regbase + ME4000_CNT_COUNTER_1_REG;
- info->cnt_context.counter_2_reg =
- info->timer_regbase + ME4000_CNT_COUNTER_2_REG;
-
- return 0;
-}
-
#define FIRMWARE_NOT_AVAILABLE 1
#if FIRMWARE_NOT_AVAILABLE
extern unsigned char *xilinx_firm;
@@ -495,11 +370,17 @@ extern unsigned char *xilinx_firm;
static int xilinx_download(struct comedi_device *dev)
{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ struct me4000_info *info = dev->private;
+ unsigned long xilinx_iobase = pci_resource_start(pcidev, 5);
u32 value = 0;
wait_queue_head_t queue;
int idx = 0;
int size = 0;
+ if (!xilinx_iobase)
+ return -ENODEV;
+
init_waitqueue_head(&queue);
/*
@@ -514,14 +395,12 @@ static int xilinx_download(struct comedi_device *dev)
outl(value, info->plx_regbase + PLX_ICR);
/* Init Xilinx with CS1 */
- inb(info->program_regbase + 0xC8);
+ inb(xilinx_iobase + 0xC8);
/* Wait until /INIT pin is set */
udelay(20);
if (!(inl(info->plx_regbase + PLX_INTCSR) & 0x20)) {
- printk(KERN_ERR
- "comedi%d: me4000: xilinx_download(): "
- "Can't init Xilinx\n", dev->minor);
+ dev_err(dev->class_dev, "Can't init Xilinx\n");
return -EIO;
}
@@ -530,8 +409,8 @@ static int xilinx_download(struct comedi_device *dev)
value &= ~0x100;
outl(value, info->plx_regbase + PLX_ICR);
if (FIRMWARE_NOT_AVAILABLE) {
- comedi_error(dev, "xilinx firmware unavailable "
- "due to licensing, aborting");
+ dev_err(dev->class_dev,
+ "xilinx firmware unavailable due to licensing, aborting");
return -EIO;
} else {
/* Download Xilinx firmware */
@@ -540,15 +419,14 @@ static int xilinx_download(struct comedi_device *dev)
udelay(10);
for (idx = 0; idx < size; idx++) {
- outb(xilinx_firm[16 + idx], info->program_regbase);
+ outb(xilinx_firm[16 + idx], xilinx_iobase);
udelay(10);
/* Check if BUSY flag is low */
if (inl(info->plx_regbase + PLX_ICR) & 0x20) {
- printk(KERN_ERR
- "comedi%d: me4000: xilinx_download(): "
- "Xilinx is still busy (idx = %d)\n",
- dev->minor, idx);
+ dev_err(dev->class_dev,
+ "Xilinx is still busy (idx = %d)\n",
+ idx);
return -EIO;
}
}
@@ -557,12 +435,8 @@ static int xilinx_download(struct comedi_device *dev)
/* If done flag is high download was successful */
if (inl(info->plx_regbase + PLX_ICR) & 0x4) {
} else {
- printk(KERN_ERR
- "comedi%d: me4000: xilinx_download(): "
- "DONE flag is not set\n", dev->minor);
- printk(KERN_ERR
- "comedi%d: me4000: xilinx_download(): "
- "Download not successful\n", dev->minor);
+ dev_err(dev->class_dev, "DONE flag is not set\n");
+ dev_err(dev->class_dev, "Download not successful\n");
return -EIO;
}
@@ -574,52 +448,45 @@ static int xilinx_download(struct comedi_device *dev)
return 0;
}
-static int reset_board(struct comedi_device *dev)
+static void me4000_reset(struct comedi_device *dev)
{
- unsigned long icr;
+ struct me4000_info *info = dev->private;
+ unsigned long val;
+ int chan;
/* Make a hardware reset */
- icr = inl(info->plx_regbase + PLX_ICR);
- icr |= 0x40000000;
- outl(icr, info->plx_regbase + PLX_ICR);
- icr &= ~0x40000000;
- outl(icr, info->plx_regbase + PLX_ICR);
+ val = inl(info->plx_regbase + PLX_ICR);
+ val |= 0x40000000;
+ outl(val, info->plx_regbase + PLX_ICR);
+ val &= ~0x40000000;
+ outl(val , info->plx_regbase + PLX_ICR);
/* 0x8000 to the DACs means an output voltage of 0V */
- outl(0x8000, info->me4000_regbase + ME4000_AO_00_SINGLE_REG);
- outl(0x8000, info->me4000_regbase + ME4000_AO_01_SINGLE_REG);
- outl(0x8000, info->me4000_regbase + ME4000_AO_02_SINGLE_REG);
- outl(0x8000, info->me4000_regbase + ME4000_AO_03_SINGLE_REG);
+ for (chan = 0; chan < 4; chan++)
+ outl(0x8000, dev->iobase + ME4000_AO_SINGLE_REG(chan));
/* Set both stop bits in the analog input control register */
outl(ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP,
- info->me4000_regbase + ME4000_AI_CTRL_REG);
+ dev->iobase + ME4000_AI_CTRL_REG);
/* Set both stop bits in the analog output control register */
- outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
- info->me4000_regbase + ME4000_AO_00_CTRL_REG);
- outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
- info->me4000_regbase + ME4000_AO_01_CTRL_REG);
- outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
- info->me4000_regbase + ME4000_AO_02_CTRL_REG);
- outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
- info->me4000_regbase + ME4000_AO_03_CTRL_REG);
+ val = ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP;
+ for (chan = 0; chan < 4; chan++)
+ outl(val, dev->iobase + ME4000_AO_CTRL_REG(chan));
/* Enable interrupts on the PLX */
outl(0x43, info->plx_regbase + PLX_INTCSR);
/* Set the adustment register for AO demux */
outl(ME4000_AO_DEMUX_ADJUST_VALUE,
- info->me4000_regbase + ME4000_AO_DEMUX_ADJUST_REG);
+ dev->iobase + ME4000_AO_DEMUX_ADJUST_REG);
/*
* Set digital I/O direction for port 0
* to output on isolated versions
*/
- if (!(inl(info->me4000_regbase + ME4000_DIO_DIR_REG) & 0x1))
- outl(0x1, info->me4000_regbase + ME4000_DIO_CTRL_REG);
-
- return 0;
+ if (!(inl(dev->iobase + ME4000_DIO_DIR_REG) & 0x1))
+ outl(0x1, dev->iobase + ME4000_DIO_CTRL_REG);
}
/*=============================================================================
@@ -630,7 +497,7 @@ static int me4000_ai_insn_read(struct comedi_device *dev,
struct comedi_subdevice *subdevice,
struct comedi_insn *insn, unsigned int *data)
{
-
+ const struct me4000_board *thisboard = comedi_board(dev);
int chan = CR_CHAN(insn->chanspec);
int rang = CR_RANGE(insn->chanspec);
int aref = CR_AREF(insn->chanspec);
@@ -642,9 +509,8 @@ static int me4000_ai_insn_read(struct comedi_device *dev,
if (insn->n == 0) {
return 0;
} else if (insn->n > 1) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_insn_read(): "
- "Invalid instruction length %d\n", dev->minor, insn->n);
+ dev_err(dev->class_dev, "Invalid instruction length %d\n",
+ insn->n);
return -EINVAL;
}
@@ -662,19 +528,16 @@ static int me4000_ai_insn_read(struct comedi_device *dev,
entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10;
break;
default:
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_insn_read(): "
- "Invalid range specified\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid range specified\n");
return -EINVAL;
}
switch (aref) {
case AREF_GROUND:
case AREF_COMMON:
- if (chan >= thisboard->ai.count) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_insn_read(): "
- "Analog input is not available\n", dev->minor);
+ if (chan >= thisboard->ai_nchan) {
+ dev_err(dev->class_dev,
+ "Analog input is not available\n");
return -EINVAL;
}
entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED | chan;
@@ -682,68 +545,61 @@ static int me4000_ai_insn_read(struct comedi_device *dev,
case AREF_DIFF:
if (rang == 0 || rang == 1) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_insn_read(): "
- "Range must be bipolar when aref = diff\n",
- dev->minor);
+ dev_err(dev->class_dev,
+ "Range must be bipolar when aref = diff\n");
return -EINVAL;
}
- if (chan >= thisboard->ai.diff_count) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_insn_read(): "
- "Analog input is not available\n", dev->minor);
+ if (chan >= thisboard->ai_diff_nchan) {
+ dev_err(dev->class_dev,
+ "Analog input is not available\n");
return -EINVAL;
}
entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL | chan;
break;
default:
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_insn_read(): "
- "Invalid aref specified\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid aref specified\n");
return -EINVAL;
}
entry |= ME4000_AI_LIST_LAST_ENTRY;
/* Clear channel list, data fifo and both stop bits */
- tmp = inl(info->ai_context.ctrl_reg);
+ tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
tmp &= ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO |
ME4000_AI_CTRL_BIT_DATA_FIFO |
ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP);
- outl(tmp, info->ai_context.ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
/* Set the acquisition mode to single */
tmp &= ~(ME4000_AI_CTRL_BIT_MODE_0 | ME4000_AI_CTRL_BIT_MODE_1 |
ME4000_AI_CTRL_BIT_MODE_2);
- outl(tmp, info->ai_context.ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
/* Enable channel list and data fifo */
tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO;
- outl(tmp, info->ai_context.ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
/* Generate channel list entry */
- outl(entry, info->ai_context.channel_list_reg);
+ outl(entry, dev->iobase + ME4000_AI_CHANNEL_LIST_REG);
/* Set the timer to maximum sample rate */
- outl(ME4000_AI_MIN_TICKS, info->ai_context.chan_timer_reg);
- outl(ME4000_AI_MIN_TICKS, info->ai_context.chan_pre_timer_reg);
+ outl(ME4000_AI_MIN_TICKS, dev->iobase + ME4000_AI_CHAN_TIMER_REG);
+ outl(ME4000_AI_MIN_TICKS, dev->iobase + ME4000_AI_CHAN_PRE_TIMER_REG);
/* Start conversion by dummy read */
- inl(info->ai_context.start_reg);
+ inl(dev->iobase + ME4000_AI_START_REG);
/* Wait until ready */
udelay(10);
- if (!(inl(info->ai_context.status_reg) &
+ if (!(inl(dev->iobase + ME4000_AI_STATUS_REG) &
ME4000_AI_STATUS_BIT_EF_DATA)) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_insn_read(): "
- "Value not available after wait\n", dev->minor);
+ dev_err(dev->class_dev, "Value not available after wait\n");
return -EIO;
}
/* Read value from data fifo */
- lval = inl(info->ai_context.data_reg) & 0xFFFF;
+ lval = inl(dev->iobase + ME4000_AI_DATA_REG) & 0xFFFF;
data[0] = lval ^ 0x8000;
return 1;
@@ -755,12 +611,12 @@ static int me4000_ai_cancel(struct comedi_device *dev,
unsigned long tmp;
/* Stop any running conversion */
- tmp = inl(info->ai_context.ctrl_reg);
+ tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
tmp &= ~(ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP);
- outl(tmp, info->ai_context.ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
/* Clear the control register */
- outl(0x0, info->ai_context.ctrl_reg);
+ outl(0x0, dev->iobase + ME4000_AI_CTRL_REG);
return 0;
}
@@ -768,30 +624,25 @@ static int me4000_ai_cancel(struct comedi_device *dev,
static int ai_check_chanlist(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
+ const struct me4000_board *thisboard = comedi_board(dev);
int aref;
int i;
/* Check whether a channel list is available */
if (!cmd->chanlist_len) {
- printk(KERN_ERR
- "comedi%d: me4000: ai_check_chanlist(): "
- "No channel list available\n", dev->minor);
+ dev_err(dev->class_dev, "No channel list available\n");
return -EINVAL;
}
/* Check the channel list size */
if (cmd->chanlist_len > ME4000_AI_CHANNEL_LIST_COUNT) {
- printk(KERN_ERR
- "comedi%d: me4000: ai_check_chanlist(): "
- "Channel list is to large\n", dev->minor);
+ dev_err(dev->class_dev, "Channel list is to large\n");
return -EINVAL;
}
/* Check the pointer */
if (!cmd->chanlist) {
- printk(KERN_ERR
- "comedi%d: me4000: ai_check_chanlist(): "
- "NULL pointer to channel list\n", dev->minor);
+ dev_err(dev->class_dev, "NULL pointer to channel list\n");
return -EFAULT;
}
@@ -799,10 +650,8 @@ static int ai_check_chanlist(struct comedi_device *dev,
aref = CR_AREF(cmd->chanlist[0]);
for (i = 0; i < cmd->chanlist_len; i++) {
if (CR_AREF(cmd->chanlist[i]) != aref) {
- printk(KERN_ERR
- "comedi%d: me4000: ai_check_chanlist(): "
- "Mode is not equal for all entries\n",
- dev->minor);
+ dev_err(dev->class_dev,
+ "Mode is not equal for all entries\n");
return -EINVAL;
}
}
@@ -811,19 +660,17 @@ static int ai_check_chanlist(struct comedi_device *dev,
if (aref == SDF_DIFF) {
for (i = 0; i < cmd->chanlist_len; i++) {
if (CR_CHAN(cmd->chanlist[i]) >=
- thisboard->ai.diff_count) {
- printk(KERN_ERR
- "comedi%d: me4000: ai_check_chanlist():"
- " Channel number to high\n", dev->minor);
+ thisboard->ai_diff_nchan) {
+ dev_err(dev->class_dev,
+ "Channel number to high\n");
return -EINVAL;
}
}
} else {
for (i = 0; i < cmd->chanlist_len; i++) {
- if (CR_CHAN(cmd->chanlist[i]) >= thisboard->ai.count) {
- printk(KERN_ERR
- "comedi%d: me4000: ai_check_chanlist(): "
- "Channel number to high\n", dev->minor);
+ if (CR_CHAN(cmd->chanlist[i]) >= thisboard->ai_nchan) {
+ dev_err(dev->class_dev,
+ "Channel number to high\n");
return -EINVAL;
}
}
@@ -834,11 +681,8 @@ static int ai_check_chanlist(struct comedi_device *dev,
for (i = 0; i < cmd->chanlist_len; i++) {
if (CR_RANGE(cmd->chanlist[i]) != 1 &&
CR_RANGE(cmd->chanlist[i]) != 2) {
- printk(KERN_ERR
- "comedi%d: me4000: ai_check_chanlist(): "
- "Bipolar is not selected in "
- "differential mode\n",
- dev->minor);
+ dev_err(dev->class_dev,
+ "Bipolar is not selected in differential mode\n");
return -EINVAL;
}
}
@@ -906,16 +750,52 @@ static void ai_write_timer(struct comedi_device *dev,
unsigned int init_ticks,
unsigned int scan_ticks, unsigned int chan_ticks)
{
- outl(init_ticks - 1, info->ai_context.scan_pre_timer_low_reg);
- outl(0x0, info->ai_context.scan_pre_timer_high_reg);
+ outl(init_ticks - 1, dev->iobase + ME4000_AI_SCAN_PRE_TIMER_LOW_REG);
+ outl(0x0, dev->iobase + ME4000_AI_SCAN_PRE_TIMER_HIGH_REG);
if (scan_ticks) {
- outl(scan_ticks - 1, info->ai_context.scan_timer_low_reg);
- outl(0x0, info->ai_context.scan_timer_high_reg);
+ outl(scan_ticks - 1, dev->iobase + ME4000_AI_SCAN_TIMER_LOW_REG);
+ outl(0x0, dev->iobase + ME4000_AI_SCAN_TIMER_HIGH_REG);
}
- outl(chan_ticks - 1, info->ai_context.chan_pre_timer_reg);
- outl(chan_ticks - 1, info->ai_context.chan_timer_reg);
+ outl(chan_ticks - 1, dev->iobase + ME4000_AI_CHAN_PRE_TIMER_REG);
+ outl(chan_ticks - 1, dev->iobase + ME4000_AI_CHAN_TIMER_REG);
+}
+
+static int ai_write_chanlist(struct comedi_device *dev,
+ struct comedi_subdevice *s, struct comedi_cmd *cmd)
+{
+ unsigned int entry;
+ unsigned int chan;
+ unsigned int rang;
+ unsigned int aref;
+ int i;
+
+ for (i = 0; i < cmd->chanlist_len; i++) {
+ chan = CR_CHAN(cmd->chanlist[i]);
+ rang = CR_RANGE(cmd->chanlist[i]);
+ aref = CR_AREF(cmd->chanlist[i]);
+
+ entry = chan;
+
+ if (rang == 0)
+ entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5;
+ else if (rang == 1)
+ entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10;
+ else if (rang == 2)
+ entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5;
+ else
+ entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10;
+
+ if (aref == SDF_DIFF)
+ entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL;
+ else
+ entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED;
+
+ outl(entry, dev->iobase + ME4000_AI_CHANNEL_LIST_REG);
+ }
+
+ return 0;
}
static int ai_prepare(struct comedi_device *dev,
@@ -931,7 +811,7 @@ static int ai_prepare(struct comedi_device *dev,
ai_write_timer(dev, init_ticks, scan_ticks, chan_ticks);
/* Reset control register */
- outl(tmp, info->ai_context.ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
/* Start sources */
if ((cmd->start_src == TRIG_EXT &&
@@ -965,19 +845,19 @@ static int ai_prepare(struct comedi_device *dev,
/* Stop triggers */
if (cmd->stop_src == TRIG_COUNT) {
outl(cmd->chanlist_len * cmd->stop_arg,
- info->ai_context.sample_counter_reg);
+ dev->iobase + ME4000_AI_SAMPLE_COUNTER_REG);
tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ;
} else if (cmd->stop_src == TRIG_NONE &&
cmd->scan_end_src == TRIG_COUNT) {
outl(cmd->scan_end_arg,
- info->ai_context.sample_counter_reg);
+ dev->iobase + ME4000_AI_SAMPLE_COUNTER_REG);
tmp |= ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ;
} else {
tmp |= ME4000_AI_CTRL_BIT_HF_IRQ;
}
/* Write the setup to the control register */
- outl(tmp, info->ai_context.ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
/* Write the channel list */
ai_write_chanlist(dev, s, cmd);
@@ -985,42 +865,6 @@ static int ai_prepare(struct comedi_device *dev,
return 0;
}
-static int ai_write_chanlist(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_cmd *cmd)
-{
- unsigned int entry;
- unsigned int chan;
- unsigned int rang;
- unsigned int aref;
- int i;
-
- for (i = 0; i < cmd->chanlist_len; i++) {
- chan = CR_CHAN(cmd->chanlist[i]);
- rang = CR_RANGE(cmd->chanlist[i]);
- aref = CR_AREF(cmd->chanlist[i]);
-
- entry = chan;
-
- if (rang == 0)
- entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_2_5;
- else if (rang == 1)
- entry |= ME4000_AI_LIST_RANGE_UNIPOLAR_10;
- else if (rang == 2)
- entry |= ME4000_AI_LIST_RANGE_BIPOLAR_2_5;
- else
- entry |= ME4000_AI_LIST_RANGE_BIPOLAR_10;
-
- if (aref == SDF_DIFF)
- entry |= ME4000_AI_LIST_INPUT_DIFFERENTIAL;
- else
- entry |= ME4000_AI_LIST_INPUT_SINGLE_ENDED;
-
- outl(entry, info->ai_context.channel_list_reg);
- }
-
- return 0;
-}
-
static int me4000_ai_do_cmd(struct comedi_device *dev,
struct comedi_subdevice *s)
{
@@ -1047,23 +891,11 @@ static int me4000_ai_do_cmd(struct comedi_device *dev,
return err;
/* Start acquistion by dummy read */
- inl(info->ai_context.start_reg);
+ inl(dev->iobase + ME4000_AI_START_REG);
return 0;
}
-/*
- * me4000_ai_do_cmd_test():
- *
- * The demo cmd.c in ./comedilib/demo specifies 6 return values:
- * - success
- * - invalid source
- * - source conflict
- * - invalid argument
- * - argument conflict
- * - invalid chanlist
- * So I tried to adopt this scheme.
- */
static int me4000_ai_do_cmd_test(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
@@ -1080,91 +912,29 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
/* Round the timer arguments */
ai_round_cmd_args(dev, s, cmd, &init_ticks, &scan_ticks, &chan_ticks);
- /*
- * Stage 1. Check if the trigger sources are generally valid.
- */
- switch (cmd->start_src) {
- case TRIG_NOW:
- case TRIG_EXT:
- break;
- case TRIG_ANY:
- cmd->start_src &= TRIG_NOW | TRIG_EXT;
- err++;
- break;
- default:
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid start source\n", dev->minor);
- cmd->start_src = TRIG_NOW;
- err++;
- }
- switch (cmd->scan_begin_src) {
- case TRIG_FOLLOW:
- case TRIG_TIMER:
- case TRIG_EXT:
- break;
- case TRIG_ANY:
- cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT;
- err++;
- break;
- default:
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid scan begin source\n", dev->minor);
- cmd->scan_begin_src = TRIG_FOLLOW;
- err++;
- }
- switch (cmd->convert_src) {
- case TRIG_TIMER:
- case TRIG_EXT:
- break;
- case TRIG_ANY:
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- err++;
- break;
- default:
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid convert source\n", dev->minor);
- cmd->convert_src = TRIG_TIMER;
- err++;
- }
- switch (cmd->scan_end_src) {
- case TRIG_NONE:
- case TRIG_COUNT:
- break;
- case TRIG_ANY:
- cmd->scan_end_src &= TRIG_NONE | TRIG_COUNT;
- err++;
- break;
- default:
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid scan end source\n", dev->minor);
- cmd->scan_end_src = TRIG_NONE;
- err++;
- }
- switch (cmd->stop_src) {
- case TRIG_NONE:
- case TRIG_COUNT:
- break;
- case TRIG_ANY:
- cmd->stop_src &= TRIG_NONE | TRIG_COUNT;
- err++;
- break;
- default:
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid stop source\n", dev->minor);
- cmd->stop_src = TRIG_NONE;
- err++;
- }
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src,
+ TRIG_NONE | TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE | TRIG_COUNT);
+
if (err)
return 1;
- /*
- * Stage 2. Check for trigger source conflicts.
- */
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_end_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
+
if (cmd->start_src == TRIG_NOW &&
cmd->scan_begin_src == TRIG_TIMER &&
cmd->convert_src == TRIG_TIMER) {
@@ -1184,13 +954,7 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
cmd->scan_begin_src == TRIG_EXT &&
cmd->convert_src == TRIG_EXT) {
} else {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid start trigger combination\n", dev->minor);
- cmd->start_src = TRIG_NOW;
- cmd->scan_begin_src = TRIG_FOLLOW;
- cmd->convert_src = TRIG_TIMER;
- err++;
+ err |= -EINVAL;
}
if (cmd->stop_src == TRIG_NONE && cmd->scan_end_src == TRIG_NONE) {
@@ -1201,13 +965,9 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
} else if (cmd->stop_src == TRIG_COUNT &&
cmd->scan_end_src == TRIG_COUNT) {
} else {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid stop trigger combination\n", dev->minor);
- cmd->stop_src = TRIG_NONE;
- cmd->scan_end_src = TRIG_NONE;
- err++;
+ err |= -EINVAL;
}
+
if (err)
return 2;
@@ -1215,30 +975,22 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
* Stage 3. Check if arguments are generally valid.
*/
if (cmd->chanlist_len < 1) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "No channel list\n", dev->minor);
+ dev_err(dev->class_dev, "No channel list\n");
cmd->chanlist_len = 1;
err++;
}
if (init_ticks < 66) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Start arg to low\n", dev->minor);
+ dev_err(dev->class_dev, "Start arg to low\n");
cmd->start_arg = 2000;
err++;
}
if (scan_ticks && scan_ticks < 67) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Scan begin arg to low\n", dev->minor);
+ dev_err(dev->class_dev, "Scan begin arg to low\n");
cmd->scan_begin_arg = 2031;
err++;
}
if (chan_ticks < 66) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Convert arg to low\n", dev->minor);
+ dev_err(dev->class_dev, "Convert arg to low\n");
cmd->convert_arg = 2000;
err++;
}
@@ -1255,23 +1007,17 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid start arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid start arg\n");
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid convert arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid convert arg\n");
cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid scan end arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid scan end arg\n");
/* At least one tick more */
cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31;
@@ -1283,16 +1029,12 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid start arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid start arg\n");
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid convert arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid convert arg\n");
cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
@@ -1302,23 +1044,17 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid start arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid start arg\n");
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid convert arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid convert arg\n");
cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
if (scan_ticks <= cmd->chanlist_len * chan_ticks) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid scan end arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid scan end arg\n");
/* At least one tick more */
cmd->scan_end_arg = 2000 * cmd->chanlist_len + 31;
@@ -1330,16 +1066,12 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid start arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid start arg\n");
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid convert arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid convert arg\n");
cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
@@ -1349,16 +1081,12 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid start arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid start arg\n");
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
if (chan_ticks < ME4000_AI_MIN_TICKS) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid convert arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid convert arg\n");
cmd->convert_arg = 2000; /* 66 ticks at least */
err++;
}
@@ -1368,27 +1096,21 @@ static int me4000_ai_do_cmd_test(struct comedi_device *dev,
/* Check timer arguments */
if (init_ticks < ME4000_AI_MIN_TICKS) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid start arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid start arg\n");
cmd->start_arg = 2000; /* 66 ticks at least */
err++;
}
}
if (cmd->stop_src == TRIG_COUNT) {
if (cmd->stop_arg == 0) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid stop arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid stop arg\n");
cmd->stop_arg = 1;
err++;
}
}
if (cmd->scan_end_src == TRIG_COUNT) {
if (cmd->scan_end_arg == 0) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_do_cmd_test(): "
- "Invalid scan end arg\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid scan end arg\n");
cmd->scan_end_arg = 1;
err++;
}
@@ -1410,8 +1132,7 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
{
unsigned int tmp;
struct comedi_device *dev = dev_id;
- struct comedi_subdevice *s = dev->subdevices;
- struct me4000_ai_context *ai_context = &info->ai_context;
+ struct comedi_subdevice *s = &dev->subdevices[0];
int i;
int c = 0;
long lval;
@@ -1423,17 +1144,15 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
s->async->events = 0;
/* Check if irq number is right */
- if (irq != ai_context->irq) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_isr(): "
- "Incorrect interrupt num: %d\n", dev->minor, irq);
+ if (irq != dev->irq) {
+ dev_err(dev->class_dev, "Incorrect interrupt num: %d\n", irq);
return IRQ_HANDLED;
}
- if (inl(ai_context->irq_status_reg) &
+ if (inl(dev->iobase + ME4000_IRQ_STATUS_REG) &
ME4000_IRQ_STATUS_BIT_AI_HF) {
/* Read status register to find out what happened */
- tmp = inl(ai_context->ctrl_reg);
+ tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) &&
!(tmp & ME4000_AI_STATUS_BIT_HF_DATA) &&
@@ -1447,13 +1166,11 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
ME4000_AI_CTRL_BIT_SC_IRQ);
- outl(tmp, ai_context->ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_isr(): "
- "FIFO overflow\n", dev->minor);
+ dev_err(dev->class_dev, "FIFO overflow\n");
} else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA)
&& !(tmp & ME4000_AI_STATUS_BIT_HF_DATA)
&& (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
@@ -1461,9 +1178,8 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
c = ME4000_AI_FIFO_COUNT / 2;
} else {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_isr(): "
- "Can't determine state of fifo\n", dev->minor);
+ dev_err(dev->class_dev,
+ "Can't determine state of fifo\n");
c = 0;
/*
@@ -1473,18 +1189,16 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
ME4000_AI_CTRL_BIT_SC_IRQ);
- outl(tmp, ai_context->ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
s->async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_isr(): "
- "Undefined FIFO state\n", dev->minor);
+ dev_err(dev->class_dev, "Undefined FIFO state\n");
}
for (i = 0; i < c; i++) {
/* Read value from data fifo */
- lval = inl(ai_context->data_reg) & 0xFFFF;
+ lval = inl(dev->iobase + ME4000_AI_DATA_REG) & 0xFFFF;
lval ^= 0x8000;
if (!comedi_buf_put(s->async, lval)) {
@@ -1495,13 +1209,11 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ |
ME4000_AI_CTRL_BIT_SC_IRQ);
- outl(tmp, ai_context->ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
s->async->events |= COMEDI_CB_OVERFLOW;
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_isr(): "
- "Buffer overflow\n", dev->minor);
+ dev_err(dev->class_dev, "Buffer overflow\n");
break;
}
@@ -1509,33 +1221,33 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
/* Work is done, so reset the interrupt */
tmp |= ME4000_AI_CTRL_BIT_HF_IRQ_RESET;
- outl(tmp, ai_context->ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
tmp &= ~ME4000_AI_CTRL_BIT_HF_IRQ_RESET;
- outl(tmp, ai_context->ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
}
- if (inl(ai_context->irq_status_reg) & ME4000_IRQ_STATUS_BIT_SC) {
+ if (inl(dev->iobase + ME4000_IRQ_STATUS_REG) &
+ ME4000_IRQ_STATUS_BIT_SC) {
s->async->events |= COMEDI_CB_BLOCK | COMEDI_CB_EOA;
/*
* Acquisition is complete, so stop
* conversion and disable all interrupts
*/
- tmp = inl(ai_context->ctrl_reg);
+ tmp = inl(dev->iobase + ME4000_AI_CTRL_REG);
tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
tmp &= ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ);
- outl(tmp, ai_context->ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
/* Poll data until fifo empty */
- while (inl(ai_context->ctrl_reg) & ME4000_AI_STATUS_BIT_EF_DATA) {
+ while (inl(dev->iobase + ME4000_AI_CTRL_REG) &
+ ME4000_AI_STATUS_BIT_EF_DATA) {
/* Read value from data fifo */
- lval = inl(ai_context->data_reg) & 0xFFFF;
+ lval = inl(dev->iobase + ME4000_AI_DATA_REG) & 0xFFFF;
lval ^= 0x8000;
if (!comedi_buf_put(s->async, lval)) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ai_isr(): "
- "Buffer overflow\n", dev->minor);
+ dev_err(dev->class_dev, "Buffer overflow\n");
s->async->events |= COMEDI_CB_OVERFLOW;
break;
}
@@ -1543,9 +1255,9 @@ static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
/* Work is done, so reset the interrupt */
tmp |= ME4000_AI_CTRL_BIT_SC_IRQ_RESET;
- outl(tmp, ai_context->ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
tmp &= ~ME4000_AI_CTRL_BIT_SC_IRQ_RESET;
- outl(tmp, ai_context->ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AI_CTRL_REG);
}
if (s->async->events)
@@ -1562,7 +1274,8 @@ static int me4000_ao_insn_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
-
+ const struct me4000_board *thisboard = comedi_board(dev);
+ struct me4000_info *info = dev->private;
int chan = CR_CHAN(insn->chanspec);
int rang = CR_RANGE(insn->chanspec);
int aref = CR_AREF(insn->chanspec);
@@ -1571,46 +1284,39 @@ static int me4000_ao_insn_write(struct comedi_device *dev,
if (insn->n == 0) {
return 0;
} else if (insn->n > 1) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ao_insn_write(): "
- "Invalid instruction length %d\n", dev->minor, insn->n);
+ dev_err(dev->class_dev, "Invalid instruction length %d\n",
+ insn->n);
return -EINVAL;
}
- if (chan >= thisboard->ao.count) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ao_insn_write(): "
- "Invalid channel %d\n", dev->minor, insn->n);
+ if (chan >= thisboard->ao_nchan) {
+ dev_err(dev->class_dev, "Invalid channel %d\n", insn->n);
return -EINVAL;
}
if (rang != 0) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ao_insn_write(): "
- "Invalid range %d\n", dev->minor, insn->n);
+ dev_err(dev->class_dev, "Invalid range %d\n", insn->n);
return -EINVAL;
}
if (aref != AREF_GROUND && aref != AREF_COMMON) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_ao_insn_write(): "
- "Invalid aref %d\n", dev->minor, insn->n);
+ dev_err(dev->class_dev, "Invalid aref %d\n", insn->n);
return -EINVAL;
}
/* Stop any running conversion */
- tmp = inl(info->ao_context[chan].ctrl_reg);
+ tmp = inl(dev->iobase + ME4000_AO_CTRL_REG(chan));
tmp |= ME4000_AO_CTRL_BIT_IMMEDIATE_STOP;
- outl(tmp, info->ao_context[chan].ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_AO_CTRL_REG(chan));
/* Clear control register and set to single mode */
- outl(0x0, info->ao_context[chan].ctrl_reg);
+ outl(0x0, dev->iobase + ME4000_AO_CTRL_REG(chan));
/* Write data value */
- outl(data[0], info->ao_context[chan].single_reg);
+ outl(data[0], dev->iobase + ME4000_AO_SINGLE_REG(chan));
/* Store in the mirror */
- info->ao_context[chan].mirror = data[0];
+ info->ao_readback[chan] = data[0];
return 1;
}
@@ -1619,18 +1325,17 @@ static int me4000_ao_insn_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct me4000_info *info = dev->private;
int chan = CR_CHAN(insn->chanspec);
if (insn->n == 0) {
return 0;
} else if (insn->n > 1) {
- printk
- ("comedi%d: me4000: me4000_ao_insn_read(): "
- "Invalid instruction length\n", dev->minor);
+ dev_err(dev->class_dev, "Invalid instruction length\n");
return -EINVAL;
}
- data[0] = info->ao_context[chan].mirror;
+ data[0] = info->ao_readback[chan];
return 1;
}
@@ -1659,21 +1364,21 @@ static int me4000_dio_insn_bits(struct comedi_device *dev,
/* Write out the new digital output lines */
outl((s->state >> 0) & 0xFF,
- info->dio_context.port_0_reg);
+ dev->iobase + ME4000_DIO_PORT_0_REG);
outl((s->state >> 8) & 0xFF,
- info->dio_context.port_1_reg);
+ dev->iobase + ME4000_DIO_PORT_1_REG);
outl((s->state >> 16) & 0xFF,
- info->dio_context.port_2_reg);
+ dev->iobase + ME4000_DIO_PORT_2_REG);
outl((s->state >> 24) & 0xFF,
- info->dio_context.port_3_reg);
+ dev->iobase + ME4000_DIO_PORT_3_REG);
}
/* On return, data[1] contains the value of
the digital input and output lines. */
- data[1] = ((inl(info->dio_context.port_0_reg) & 0xFF) << 0) |
- ((inl(info->dio_context.port_1_reg) & 0xFF) << 8) |
- ((inl(info->dio_context.port_2_reg) & 0xFF) << 16) |
- ((inl(info->dio_context.port_3_reg) & 0xFF) << 24);
+ data[1] = ((inl(dev->iobase + ME4000_DIO_PORT_0_REG) & 0xFF) << 0) |
+ ((inl(dev->iobase + ME4000_DIO_PORT_1_REG) & 0xFF) << 8) |
+ ((inl(dev->iobase + ME4000_DIO_PORT_2_REG) & 0xFF) << 16) |
+ ((inl(dev->iobase + ME4000_DIO_PORT_3_REG) & 0xFF) << 24);
return insn->n;
}
@@ -1705,7 +1410,7 @@ static int me4000_dio_insn_config(struct comedi_device *dev,
* On the ME-4000 it is only possible to switch port wise (8 bit)
*/
- tmp = inl(info->dio_context.ctrl_reg);
+ tmp = inl(dev->iobase + ME4000_DIO_CTRL_REG);
if (data[0] == INSN_CONFIG_DIO_OUTPUT) {
if (chan < 8) {
@@ -1719,7 +1424,7 @@ static int me4000_dio_insn_config(struct comedi_device *dev,
* If one the first port is a fixed output
* port and the second is a fixed input port.
*/
- if (!inl(info->dio_context.dir_reg))
+ if (!inl(dev->iobase + ME4000_DIO_DIR_REG))
return -ENODEV;
s->io_bits |= 0xFF00;
@@ -1746,7 +1451,7 @@ static int me4000_dio_insn_config(struct comedi_device *dev,
* If one the first port is a fixed output
* port and the second is a fixed input port.
*/
- if (!inl(info->dio_context.dir_reg))
+ if (!inl(dev->iobase + ME4000_DIO_DIR_REG))
return -ENODEV;
s->io_bits &= ~0xFF;
@@ -1769,7 +1474,7 @@ static int me4000_dio_insn_config(struct comedi_device *dev,
}
}
- outl(tmp, info->dio_context.ctrl_reg);
+ outl(tmp, dev->iobase + ME4000_DIO_CTRL_REG);
return 1;
}
@@ -1778,177 +1483,56 @@ static int me4000_dio_insn_config(struct comedi_device *dev,
Counter section
===========================================================================*/
-static int cnt_reset(struct comedi_device *dev, unsigned int channel)
-{
- switch (channel) {
- case 0:
- outb(0x30, info->cnt_context.ctrl_reg);
- outb(0x00, info->cnt_context.counter_0_reg);
- outb(0x00, info->cnt_context.counter_0_reg);
- break;
- case 1:
- outb(0x70, info->cnt_context.ctrl_reg);
- outb(0x00, info->cnt_context.counter_1_reg);
- outb(0x00, info->cnt_context.counter_1_reg);
- break;
- case 2:
- outb(0xB0, info->cnt_context.ctrl_reg);
- outb(0x00, info->cnt_context.counter_2_reg);
- outb(0x00, info->cnt_context.counter_2_reg);
- break;
- default:
- printk(KERN_ERR
- "comedi%d: me4000: cnt_reset(): Invalid channel\n",
- dev->minor);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int cnt_config(struct comedi_device *dev, unsigned int channel,
- unsigned int mode)
-{
- int tmp = 0;
-
- switch (channel) {
- case 0:
- tmp |= ME4000_CNT_COUNTER_0;
- break;
- case 1:
- tmp |= ME4000_CNT_COUNTER_1;
- break;
- case 2:
- tmp |= ME4000_CNT_COUNTER_2;
- break;
- default:
- printk(KERN_ERR
- "comedi%d: me4000: cnt_config(): Invalid channel\n",
- dev->minor);
- return -EINVAL;
- }
-
- switch (mode) {
- case 0:
- tmp |= ME4000_CNT_MODE_0;
- break;
- case 1:
- tmp |= ME4000_CNT_MODE_1;
- break;
- case 2:
- tmp |= ME4000_CNT_MODE_2;
- break;
- case 3:
- tmp |= ME4000_CNT_MODE_3;
- break;
- case 4:
- tmp |= ME4000_CNT_MODE_4;
- break;
- case 5:
- tmp |= ME4000_CNT_MODE_5;
- break;
- default:
- printk(KERN_ERR
- "comedi%d: me4000: cnt_config(): Invalid counter mode\n",
- dev->minor);
- return -EINVAL;
- }
-
- /* Write the control word */
- tmp |= 0x30;
- outb(tmp, info->cnt_context.ctrl_reg);
-
- return 0;
-}
-
static int me4000_cnt_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+ struct comedi_insn *insn,
+ unsigned int *data)
{
-
+ struct me4000_info *info = dev->private;
int err;
switch (data[0]) {
case GPCT_RESET:
- if (insn->n != 1) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_cnt_insn_config(): "
- "Invalid instruction length%d\n",
- dev->minor, insn->n);
+ if (insn->n != 1)
return -EINVAL;
- }
- err = cnt_reset(dev, insn->chanspec);
+ err = i8254_load(info->timer_regbase, 0, insn->chanspec, 0,
+ I8254_MODE0 | I8254_BINARY);
if (err)
return err;
break;
case GPCT_SET_OPERATION:
- if (insn->n != 2) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_cnt_insn_config(): "
- "Invalid instruction length%d\n",
- dev->minor, insn->n);
+ if (insn->n != 2)
return -EINVAL;
- }
- err = cnt_config(dev, insn->chanspec, data[1]);
+ err = i8254_set_mode(info->timer_regbase, 0, insn->chanspec,
+ (data[1] << 1) | I8254_BINARY);
if (err)
return err;
break;
default:
- printk(KERN_ERR
- "comedi%d: me4000: me4000_cnt_insn_config(): "
- "Invalid instruction\n", dev->minor);
return -EINVAL;
}
- return 2;
+ return insn->n;
}
static int me4000_cnt_insn_read(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
-
- unsigned short tmp;
+ struct me4000_info *info = dev->private;
if (insn->n == 0)
return 0;
if (insn->n > 1) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_cnt_insn_read(): "
- "Invalid instruction length %d\n",
- dev->minor, insn->n);
+ dev_err(dev->class_dev, "Invalid instruction length %d\n",
+ insn->n);
return -EINVAL;
}
- switch (insn->chanspec) {
- case 0:
- tmp = inb(info->cnt_context.counter_0_reg);
- data[0] = tmp;
- tmp = inb(info->cnt_context.counter_0_reg);
- data[0] |= tmp << 8;
- break;
- case 1:
- tmp = inb(info->cnt_context.counter_1_reg);
- data[0] = tmp;
- tmp = inb(info->cnt_context.counter_1_reg);
- data[0] |= tmp << 8;
- break;
- case 2:
- tmp = inb(info->cnt_context.counter_2_reg);
- data[0] = tmp;
- tmp = inb(info->cnt_context.counter_2_reg);
- data[0] |= tmp << 8;
- break;
- default:
- printk(KERN_ERR
- "comedi%d: me4000: me4000_cnt_insn_read(): "
- "Invalid channel %d\n",
- dev->minor, insn->chanspec);
- return -EINVAL;
- }
+ data[0] = i8254_read(info->timer_regbase, 0, insn->chanspec);
return 1;
}
@@ -1957,58 +1541,72 @@ static int me4000_cnt_insn_write(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
-
- unsigned short tmp;
+ struct me4000_info *info = dev->private;
if (insn->n == 0) {
return 0;
} else if (insn->n > 1) {
- printk(KERN_ERR
- "comedi%d: me4000: me4000_cnt_insn_write(): "
- "Invalid instruction length %d\n",
- dev->minor, insn->n);
+ dev_err(dev->class_dev, "Invalid instruction length %d\n",
+ insn->n);
return -EINVAL;
}
- switch (insn->chanspec) {
- case 0:
- tmp = data[0] & 0xFF;
- outb(tmp, info->cnt_context.counter_0_reg);
- tmp = (data[0] >> 8) & 0xFF;
- outb(tmp, info->cnt_context.counter_0_reg);
- break;
- case 1:
- tmp = data[0] & 0xFF;
- outb(tmp, info->cnt_context.counter_1_reg);
- tmp = (data[0] >> 8) & 0xFF;
- outb(tmp, info->cnt_context.counter_1_reg);
- break;
- case 2:
- tmp = data[0] & 0xFF;
- outb(tmp, info->cnt_context.counter_2_reg);
- tmp = (data[0] >> 8) & 0xFF;
- outb(tmp, info->cnt_context.counter_2_reg);
- break;
- default:
- printk(KERN_ERR
- "comedi%d: me4000: me4000_cnt_insn_write(): "
- "Invalid channel %d\n",
- dev->minor, insn->chanspec);
- return -EINVAL;
- }
+ i8254_write(info->timer_regbase, 0, insn->chanspec, data[0]);
return 1;
}
-static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static const void *me4000_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
+ const struct me4000_board *thisboard;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(me4000_boards); i++) {
+ thisboard = &me4000_boards[i];
+ if (thisboard->device_id == pcidev->device)
+ return thisboard;
+ }
+ return NULL;
+}
+
+static int me4000_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ const struct me4000_board *thisboard;
+ struct me4000_info *info;
struct comedi_subdevice *s;
int result;
- result = me4000_probe(dev, it);
+ comedi_set_hw_dev(dev, &pcidev->dev);
+
+ thisboard = me4000_find_boardinfo(dev, pcidev);
+ if (!thisboard)
+ return -ENODEV;
+ dev->board_ptr = thisboard;
+ dev->board_name = thisboard->name;
+
+ result = alloc_private(dev, sizeof(*info));
+ if (result)
+ return result;
+ info = dev->private;
+
+ result = comedi_pci_enable(pcidev, dev->board_name);
+ if (result)
+ return result;
+
+ info->plx_regbase = pci_resource_start(pcidev, 1);
+ dev->iobase = pci_resource_start(pcidev, 2);
+ info->timer_regbase = pci_resource_start(pcidev, 3);
+ if (!info->plx_regbase || !dev->iobase || !info->timer_regbase)
+ return -ENODEV;
+
+ result = xilinx_download(dev);
if (result)
return result;
+ me4000_reset(dev);
+
result = comedi_alloc_subdevices(dev, 4);
if (result)
return result;
@@ -2017,35 +1615,34 @@ static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
Analog input subdevice
========================================================================*/
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
- if (thisboard->ai.count) {
+ if (thisboard->ai_nchan) {
s->type = COMEDI_SUBD_AI;
s->subdev_flags =
SDF_READABLE | SDF_COMMON | SDF_GROUND | SDF_DIFF;
- s->n_chan = thisboard->ai.count;
+ s->n_chan = thisboard->ai_nchan;
s->maxdata = 0xFFFF; /* 16 bit ADC */
s->len_chanlist = ME4000_AI_CHANNEL_LIST_COUNT;
s->range_table = &me4000_ai_range;
s->insn_read = me4000_ai_insn_read;
- if (info->irq > 0) {
- if (request_irq(info->irq, me4000_ai_isr,
- IRQF_SHARED, "ME-4000", dev)) {
- printk
- ("comedi%d: me4000: me4000_attach(): "
- "Unable to allocate irq\n", dev->minor);
+ if (pcidev->irq > 0) {
+ if (request_irq(pcidev->irq, me4000_ai_isr,
+ IRQF_SHARED, dev->board_name, dev)) {
+ dev_warn(dev->class_dev,
+ "request_irq failed\n");
} else {
dev->read_subdev = s;
s->subdev_flags |= SDF_CMD_READ;
s->cancel = me4000_ai_cancel;
s->do_cmdtest = me4000_ai_do_cmd_test;
s->do_cmd = me4000_ai_do_cmd;
+
+ dev->irq = pcidev->irq;
}
} else {
- printk(KERN_WARNING
- "comedi%d: me4000: me4000_attach(): "
- "No interrupt available\n", dev->minor);
+ dev_warn(dev->class_dev, "No interrupt available\n");
}
} else {
s->type = COMEDI_SUBD_UNUSED;
@@ -2055,14 +1652,14 @@ static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
Analog output subdevice
========================================================================*/
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
- if (thisboard->ao.count) {
+ if (thisboard->ao_nchan) {
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITEABLE | SDF_COMMON | SDF_GROUND;
- s->n_chan = thisboard->ao.count;
+ s->n_chan = thisboard->ao_nchan;
s->maxdata = 0xFFFF; /* 16 bit DAC */
- s->range_table = &me4000_ao_range;
+ s->range_table = &range_bipolar10;
s->insn_write = me4000_ao_insn_write;
s->insn_read = me4000_ao_insn_read;
} else {
@@ -2073,12 +1670,12 @@ static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
Digital I/O subdevice
========================================================================*/
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
- if (thisboard->dio.count) {
+ if (thisboard->dio_nchan) {
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- s->n_chan = thisboard->dio.count * 8;
+ s->n_chan = thisboard->dio_nchan;
s->maxdata = 1;
s->range_table = &range_digital;
s->insn_bits = me4000_dio_insn_bits;
@@ -2091,21 +1688,22 @@ static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
* Check for optoisolated ME-4000 version. If one the first
* port is a fixed output port and the second is a fixed input port.
*/
- if (!inl(info->dio_context.dir_reg)) {
+ if (!inl(dev->iobase + ME4000_DIO_DIR_REG)) {
s->io_bits |= 0xFF;
- outl(ME4000_DIO_CTRL_BIT_MODE_0, info->dio_context.dir_reg);
+ outl(ME4000_DIO_CTRL_BIT_MODE_0,
+ dev->iobase + ME4000_DIO_DIR_REG);
}
/*=========================================================================
Counter subdevice
========================================================================*/
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
- if (thisboard->cnt.count) {
+ if (thisboard->has_counter) {
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- s->n_chan = thisboard->cnt.count;
+ s->n_chan = 3;
s->maxdata = 0xFFFF; /* 16 bit counters */
s->insn_read = me4000_cnt_insn_read;
s->insn_write = me4000_cnt_insn_write;
@@ -2119,12 +1717,14 @@ static int me4000_attach(struct comedi_device *dev, struct comedi_devconfig *it)
static void me4000_detach(struct comedi_device *dev)
{
- if (info) {
- if (info->pci_dev_p) {
- reset_board(dev);
- if (info->plx_regbase)
- comedi_pci_disable(info->pci_dev_p);
- pci_dev_put(info->pci_dev_p);
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+
+ if (dev->irq)
+ free_irq(dev->irq, dev);
+ if (pcidev) {
+ if (dev->iobase) {
+ me4000_reset(dev);
+ comedi_pci_disable(pcidev);
}
}
}
@@ -2132,7 +1732,7 @@ static void me4000_detach(struct comedi_device *dev)
static struct comedi_driver me4000_driver = {
.driver_name = "me4000",
.module = THIS_MODULE,
- .attach = me4000_attach,
+ .attach_pci = me4000_attach_pci,
.detach = me4000_detach,
};
@@ -2148,20 +1748,20 @@ static void __devexit me4000_pci_remove(struct pci_dev *dev)
}
static DEFINE_PCI_DEVICE_TABLE(me4000_pci_table) = {
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4650) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4660) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4661) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4662) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4663) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4670) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4671) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4672) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4673) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4680) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4681) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4682) },
- { PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, 0x4683) },
- { 0 }
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4650)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4660)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4660I)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4660S)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4660IS)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4670)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4670I)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4670S)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4670IS)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4680)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4680I)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4680S)},
+ {PCI_DEVICE(PCI_VENDOR_ID_MEILHAUS, PCI_DEVICE_ID_MEILHAUS_ME4680IS)},
+ {0}
};
MODULE_DEVICE_TABLE(pci, me4000_pci_table);
diff --git a/drivers/staging/comedi/drivers/me4000.h b/drivers/staging/comedi/drivers/me4000.h
deleted file mode 100644
index 5a4df4e4b236..000000000000
--- a/drivers/staging/comedi/drivers/me4000.h
+++ /dev/null
@@ -1,409 +0,0 @@
-/*
- me4000.h
- Register descriptions and defines for the ME-4000 board family
-
- COMEDI - Linux Control and Measurement Device Interface
- Copyright (C) 1998-9 David A. Schleef <ds@schleef.org>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- 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 _ME4000_H_
-#define _ME4000_H_
-
-/*=============================================================================
- PCI vendor and device IDs
- ===========================================================================*/
-
-#define PCI_VENDOR_ID_MEILHAUS 0x1402
-
-#define PCI_DEVICE_ID_MEILHAUS_ME4650 0x4650 /* Low Cost version */
-
-#define PCI_DEVICE_ID_MEILHAUS_ME4660 0x4660 /* Standard version */
-#define PCI_DEVICE_ID_MEILHAUS_ME4660I 0x4661 /* Isolated version */
-#define PCI_DEVICE_ID_MEILHAUS_ME4660S 0x4662 /* Standard version with Sample and Hold */
-#define PCI_DEVICE_ID_MEILHAUS_ME4660IS 0x4663 /* Isolated version with Sample and Hold */
-
-#define PCI_DEVICE_ID_MEILHAUS_ME4670 0x4670 /* Standard version */
-#define PCI_DEVICE_ID_MEILHAUS_ME4670I 0x4671 /* Isolated version */
-#define PCI_DEVICE_ID_MEILHAUS_ME4670S 0x4672 /* Standard version with Sample and Hold */
-#define PCI_DEVICE_ID_MEILHAUS_ME4670IS 0x4673 /* Isolated version with Sample and Hold */
-
-#define PCI_DEVICE_ID_MEILHAUS_ME4680 0x4680 /* Standard version */
-#define PCI_DEVICE_ID_MEILHAUS_ME4680I 0x4681 /* Isolated version */
-#define PCI_DEVICE_ID_MEILHAUS_ME4680S 0x4682 /* Standard version with Sample and Hold */
-#define PCI_DEVICE_ID_MEILHAUS_ME4680IS 0x4683 /* Isolated version with Sample and Hold */
-
-/*=============================================================================
- ME-4000 base register offsets
- ===========================================================================*/
-
-#define ME4000_AO_00_CTRL_REG 0x00 /* R/W */
-#define ME4000_AO_00_STATUS_REG 0x04 /* R/_ */
-#define ME4000_AO_00_FIFO_REG 0x08 /* _/W */
-#define ME4000_AO_00_SINGLE_REG 0x0C /* R/W */
-#define ME4000_AO_00_TIMER_REG 0x10 /* _/W */
-
-#define ME4000_AO_01_CTRL_REG 0x18 /* R/W */
-#define ME4000_AO_01_STATUS_REG 0x1C /* R/_ */
-#define ME4000_AO_01_FIFO_REG 0x20 /* _/W */
-#define ME4000_AO_01_SINGLE_REG 0x24 /* R/W */
-#define ME4000_AO_01_TIMER_REG 0x28 /* _/W */
-
-#define ME4000_AO_02_CTRL_REG 0x30 /* R/W */
-#define ME4000_AO_02_STATUS_REG 0x34 /* R/_ */
-#define ME4000_AO_02_FIFO_REG 0x38 /* _/W */
-#define ME4000_AO_02_SINGLE_REG 0x3C /* R/W */
-#define ME4000_AO_02_TIMER_REG 0x40 /* _/W */
-
-#define ME4000_AO_03_CTRL_REG 0x48 /* R/W */
-#define ME4000_AO_03_STATUS_REG 0x4C /* R/_ */
-#define ME4000_AO_03_FIFO_REG 0x50 /* _/W */
-#define ME4000_AO_03_SINGLE_REG 0x54 /* R/W */
-#define ME4000_AO_03_TIMER_REG 0x58 /* _/W */
-
-#define ME4000_AI_CTRL_REG 0x74 /* _/W */
-#define ME4000_AI_STATUS_REG 0x74 /* R/_ */
-#define ME4000_AI_CHANNEL_LIST_REG 0x78 /* _/W */
-#define ME4000_AI_DATA_REG 0x7C /* R/_ */
-#define ME4000_AI_CHAN_TIMER_REG 0x80 /* _/W */
-#define ME4000_AI_CHAN_PRE_TIMER_REG 0x84 /* _/W */
-#define ME4000_AI_SCAN_TIMER_LOW_REG 0x88 /* _/W */
-#define ME4000_AI_SCAN_TIMER_HIGH_REG 0x8C /* _/W */
-#define ME4000_AI_SCAN_PRE_TIMER_LOW_REG 0x90 /* _/W */
-#define ME4000_AI_SCAN_PRE_TIMER_HIGH_REG 0x94 /* _/W */
-#define ME4000_AI_START_REG 0x98 /* R/_ */
-
-#define ME4000_IRQ_STATUS_REG 0x9C /* R/_ */
-
-#define ME4000_DIO_PORT_0_REG 0xA0 /* R/W */
-#define ME4000_DIO_PORT_1_REG 0xA4 /* R/W */
-#define ME4000_DIO_PORT_2_REG 0xA8 /* R/W */
-#define ME4000_DIO_PORT_3_REG 0xAC /* R/W */
-#define ME4000_DIO_DIR_REG 0xB0 /* R/W */
-
-#define ME4000_AO_LOADSETREG_XX 0xB4 /* R/W */
-
-#define ME4000_DIO_CTRL_REG 0xB8 /* R/W */
-
-#define ME4000_AO_DEMUX_ADJUST_REG 0xBC /* -/W */
-
-#define ME4000_AI_SAMPLE_COUNTER_REG 0xC0 /* _/W */
-
-/*=============================================================================
- Value to adjust Demux
- ===========================================================================*/
-
-#define ME4000_AO_DEMUX_ADJUST_VALUE 0x4C
-
-/*=============================================================================
- Counter base register offsets
- ===========================================================================*/
-
-#define ME4000_CNT_COUNTER_0_REG 0x00
-#define ME4000_CNT_COUNTER_1_REG 0x01
-#define ME4000_CNT_COUNTER_2_REG 0x02
-#define ME4000_CNT_CTRL_REG 0x03
-
-/*=============================================================================
- PLX base register offsets
- ===========================================================================*/
-
-#define PLX_INTCSR 0x4C /* Interrupt control and status register */
-#define PLX_ICR 0x50 /* Initialization control register */
-
-/*=============================================================================
- Bits for the PLX_ICSR register
- ===========================================================================*/
-
-#define PLX_INTCSR_LOCAL_INT1_EN 0x01 /* If set, local interrupt 1 is enabled (r/w) */
-#define PLX_INTCSR_LOCAL_INT1_POL 0x02 /* If set, local interrupt 1 polarity is active high (r/w) */
-#define PLX_INTCSR_LOCAL_INT1_STATE 0x04 /* If set, local interrupt 1 is active (r/_) */
-#define PLX_INTCSR_LOCAL_INT2_EN 0x08 /* If set, local interrupt 2 is enabled (r/w) */
-#define PLX_INTCSR_LOCAL_INT2_POL 0x10 /* If set, local interrupt 2 polarity is active high (r/w) */
-#define PLX_INTCSR_LOCAL_INT2_STATE 0x20 /* If set, local interrupt 2 is active (r/_) */
-#define PLX_INTCSR_PCI_INT_EN 0x40 /* If set, PCI interrupt is enabled (r/w) */
-#define PLX_INTCSR_SOFT_INT 0x80 /* If set, a software interrupt is generated (r/w) */
-
-/*=============================================================================
- Bits for the PLX_ICR register
- ===========================================================================*/
-
-#define PLX_ICR_BIT_EEPROM_CLOCK_SET 0x01000000
-#define PLX_ICR_BIT_EEPROM_CHIP_SELECT 0x02000000
-#define PLX_ICR_BIT_EEPROM_WRITE 0x04000000
-#define PLX_ICR_BIT_EEPROM_READ 0x08000000
-#define PLX_ICR_BIT_EEPROM_VALID 0x10000000
-
-#define PLX_ICR_MASK_EEPROM 0x1F000000
-
-#define EEPROM_DELAY 1
-
-/*=============================================================================
- Bits for the ME4000_AO_CTRL_REG register
- ===========================================================================*/
-
-#define ME4000_AO_CTRL_BIT_MODE_0 0x001
-#define ME4000_AO_CTRL_BIT_MODE_1 0x002
-#define ME4000_AO_CTRL_MASK_MODE 0x003
-#define ME4000_AO_CTRL_BIT_STOP 0x004
-#define ME4000_AO_CTRL_BIT_ENABLE_FIFO 0x008
-#define ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG 0x010
-#define ME4000_AO_CTRL_BIT_EX_TRIG_EDGE 0x020
-#define ME4000_AO_CTRL_BIT_IMMEDIATE_STOP 0x080
-#define ME4000_AO_CTRL_BIT_ENABLE_DO 0x100
-#define ME4000_AO_CTRL_BIT_ENABLE_IRQ 0x200
-#define ME4000_AO_CTRL_BIT_RESET_IRQ 0x400
-
-/*=============================================================================
- Bits for the ME4000_AO_STATUS_REG register
- ===========================================================================*/
-
-#define ME4000_AO_STATUS_BIT_FSM 0x01
-#define ME4000_AO_STATUS_BIT_FF 0x02
-#define ME4000_AO_STATUS_BIT_HF 0x04
-#define ME4000_AO_STATUS_BIT_EF 0x08
-
-/*=============================================================================
- Bits for the ME4000_AI_CTRL_REG register
- ===========================================================================*/
-
-#define ME4000_AI_CTRL_BIT_MODE_0 0x00000001
-#define ME4000_AI_CTRL_BIT_MODE_1 0x00000002
-#define ME4000_AI_CTRL_BIT_MODE_2 0x00000004
-#define ME4000_AI_CTRL_BIT_SAMPLE_HOLD 0x00000008
-#define ME4000_AI_CTRL_BIT_IMMEDIATE_STOP 0x00000010
-#define ME4000_AI_CTRL_BIT_STOP 0x00000020
-#define ME4000_AI_CTRL_BIT_CHANNEL_FIFO 0x00000040
-#define ME4000_AI_CTRL_BIT_DATA_FIFO 0x00000080
-#define ME4000_AI_CTRL_BIT_FULLSCALE 0x00000100
-#define ME4000_AI_CTRL_BIT_OFFSET 0x00000200
-#define ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG 0x00000400
-#define ME4000_AI_CTRL_BIT_EX_TRIG 0x00000800
-#define ME4000_AI_CTRL_BIT_EX_TRIG_FALLING 0x00001000
-#define ME4000_AI_CTRL_BIT_EX_IRQ 0x00002000
-#define ME4000_AI_CTRL_BIT_EX_IRQ_RESET 0x00004000
-#define ME4000_AI_CTRL_BIT_LE_IRQ 0x00008000
-#define ME4000_AI_CTRL_BIT_LE_IRQ_RESET 0x00010000
-#define ME4000_AI_CTRL_BIT_HF_IRQ 0x00020000
-#define ME4000_AI_CTRL_BIT_HF_IRQ_RESET 0x00040000
-#define ME4000_AI_CTRL_BIT_SC_IRQ 0x00080000
-#define ME4000_AI_CTRL_BIT_SC_IRQ_RESET 0x00100000
-#define ME4000_AI_CTRL_BIT_SC_RELOAD 0x00200000
-#define ME4000_AI_CTRL_BIT_EX_TRIG_BOTH 0x80000000
-
-/*=============================================================================
- Bits for the ME4000_AI_STATUS_REG register
- ===========================================================================*/
-
-#define ME4000_AI_STATUS_BIT_EF_CHANNEL 0x00400000
-#define ME4000_AI_STATUS_BIT_HF_CHANNEL 0x00800000
-#define ME4000_AI_STATUS_BIT_FF_CHANNEL 0x01000000
-#define ME4000_AI_STATUS_BIT_EF_DATA 0x02000000
-#define ME4000_AI_STATUS_BIT_HF_DATA 0x04000000
-#define ME4000_AI_STATUS_BIT_FF_DATA 0x08000000
-#define ME4000_AI_STATUS_BIT_LE 0x10000000
-#define ME4000_AI_STATUS_BIT_FSM 0x20000000
-
-/*=============================================================================
- Bits for the ME4000_IRQ_STATUS_REG register
- ===========================================================================*/
-
-#define ME4000_IRQ_STATUS_BIT_EX 0x01
-#define ME4000_IRQ_STATUS_BIT_LE 0x02
-#define ME4000_IRQ_STATUS_BIT_AI_HF 0x04
-#define ME4000_IRQ_STATUS_BIT_AO_0_HF 0x08
-#define ME4000_IRQ_STATUS_BIT_AO_1_HF 0x10
-#define ME4000_IRQ_STATUS_BIT_AO_2_HF 0x20
-#define ME4000_IRQ_STATUS_BIT_AO_3_HF 0x40
-#define ME4000_IRQ_STATUS_BIT_SC 0x80
-
-/*=============================================================================
- Bits for the ME4000_DIO_CTRL_REG register
- ===========================================================================*/
-
-#define ME4000_DIO_CTRL_BIT_MODE_0 0x0001
-#define ME4000_DIO_CTRL_BIT_MODE_1 0x0002
-#define ME4000_DIO_CTRL_BIT_MODE_2 0x0004
-#define ME4000_DIO_CTRL_BIT_MODE_3 0x0008
-#define ME4000_DIO_CTRL_BIT_MODE_4 0x0010
-#define ME4000_DIO_CTRL_BIT_MODE_5 0x0020
-#define ME4000_DIO_CTRL_BIT_MODE_6 0x0040
-#define ME4000_DIO_CTRL_BIT_MODE_7 0x0080
-
-#define ME4000_DIO_CTRL_BIT_FUNCTION_0 0x0100
-#define ME4000_DIO_CTRL_BIT_FUNCTION_1 0x0200
-
-#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_0 0x0400
-#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_1 0x0800
-#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_2 0x1000
-#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_3 0x2000
-
-/*=============================================================================
- Information about the hardware capabilities
- ===========================================================================*/
-
-struct me4000_ao_info {
- int count;
- int fifo_count;
-};
-
-struct me4000_ai_info {
- int count;
- int sh_count;
- int diff_count;
- int ex_trig_analog;
-};
-
-struct me4000_dio_info {
- int count;
-};
-
-struct me4000_cnt_info {
- int count;
-};
-
-struct me4000_board {
- const char *name;
- unsigned short device_id;
- struct me4000_ao_info ao;
- struct me4000_ai_info ai;
- struct me4000_dio_info dio;
- struct me4000_cnt_info cnt;
-};
-
-#define thisboard ((const struct me4000_board *)dev->board_ptr)
-
-/*=============================================================================
- Global board and subdevice information structures
- ===========================================================================*/
-
-struct me4000_ao_context {
- int irq;
-
- unsigned long mirror; /* Store the last written value */
-
- unsigned long ctrl_reg;
- unsigned long status_reg;
- unsigned long fifo_reg;
- unsigned long single_reg;
- unsigned long timer_reg;
- unsigned long irq_status_reg;
- unsigned long preload_reg;
-};
-
-struct me4000_ai_context {
- int irq;
-
- unsigned long ctrl_reg;
- unsigned long status_reg;
- unsigned long channel_list_reg;
- unsigned long data_reg;
- unsigned long chan_timer_reg;
- unsigned long chan_pre_timer_reg;
- unsigned long scan_timer_low_reg;
- unsigned long scan_timer_high_reg;
- unsigned long scan_pre_timer_low_reg;
- unsigned long scan_pre_timer_high_reg;
- unsigned long start_reg;
- unsigned long irq_status_reg;
- unsigned long sample_counter_reg;
-};
-
-struct me4000_dio_context {
- unsigned long dir_reg;
- unsigned long ctrl_reg;
- unsigned long port_0_reg;
- unsigned long port_1_reg;
- unsigned long port_2_reg;
- unsigned long port_3_reg;
-};
-
-struct me4000_cnt_context {
- unsigned long ctrl_reg;
- unsigned long counter_0_reg;
- unsigned long counter_1_reg;
- unsigned long counter_2_reg;
-};
-
-struct me4000_info {
- unsigned long plx_regbase; /* PLX configuration space base address */
- unsigned long me4000_regbase; /* Base address of the ME4000 */
- unsigned long timer_regbase; /* Base address of the timer circuit */
- unsigned long program_regbase; /* Base address to set the program pin for the xilinx */
-
- unsigned long plx_regbase_size; /* PLX register set space */
- unsigned long me4000_regbase_size; /* ME4000 register set space */
- unsigned long timer_regbase_size; /* Timer circuit register set space */
- unsigned long program_regbase_size; /* Size of program base address of the ME4000 */
-
- unsigned int serial_no; /* Serial number of the board */
- unsigned char hw_revision; /* Hardware revision of the board */
- unsigned short vendor_id; /* Meilhaus vendor id */
- unsigned short device_id; /* Device id */
-
- struct pci_dev *pci_dev_p; /* General PCI information */
-
- unsigned int irq; /* IRQ assigned from the PCI BIOS */
-
- struct me4000_ai_context ai_context; /* Analog input specific context */
- struct me4000_ao_context ao_context[4]; /* Vector with analog output specific context */
- struct me4000_dio_context dio_context; /* Digital I/O specific context */
- struct me4000_cnt_context cnt_context; /* Counter specific context */
-};
-
-#define info ((struct me4000_info *)dev->private)
-
-/*-----------------------------------------------------------------------------
- Defines for analog input
- ----------------------------------------------------------------------------*/
-
-/* General stuff */
-#define ME4000_AI_FIFO_COUNT 2048
-
-#define ME4000_AI_MIN_TICKS 66
-#define ME4000_AI_MIN_SAMPLE_TIME 2000 /* Minimum sample time [ns] */
-#define ME4000_AI_BASE_FREQUENCY (unsigned int) 33E6
-
-/* Channel list defines and masks */
-#define ME4000_AI_CHANNEL_LIST_COUNT 1024
-
-#define ME4000_AI_LIST_INPUT_SINGLE_ENDED 0x000
-#define ME4000_AI_LIST_INPUT_DIFFERENTIAL 0x020
-
-#define ME4000_AI_LIST_RANGE_BIPOLAR_10 0x000
-#define ME4000_AI_LIST_RANGE_BIPOLAR_2_5 0x040
-#define ME4000_AI_LIST_RANGE_UNIPOLAR_10 0x080
-#define ME4000_AI_LIST_RANGE_UNIPOLAR_2_5 0x0C0
-
-#define ME4000_AI_LIST_LAST_ENTRY 0x100
-
-/*-----------------------------------------------------------------------------
- Defines for counters
- ----------------------------------------------------------------------------*/
-
-#define ME4000_CNT_COUNTER_0 0x00
-#define ME4000_CNT_COUNTER_1 0x40
-#define ME4000_CNT_COUNTER_2 0x80
-
-#define ME4000_CNT_MODE_0 0x00 /* Change state if zero crossing */
-#define ME4000_CNT_MODE_1 0x02 /* Retriggerable One-Shot */
-#define ME4000_CNT_MODE_2 0x04 /* Asymmetrical divider */
-#define ME4000_CNT_MODE_3 0x06 /* Symmetrical divider */
-#define ME4000_CNT_MODE_4 0x08 /* Counter start by software trigger */
-#define ME4000_CNT_MODE_5 0x0A /* Counter start by hardware trigger */
-
-#endif
diff --git a/drivers/staging/comedi/drivers/me_daq.c b/drivers/staging/comedi/drivers/me_daq.c
index 8c6f8b93b277..2ce0b14af589 100644
--- a/drivers/staging/comedi/drivers/me_daq.c
+++ b/drivers/staging/comedi/drivers/me_daq.c
@@ -41,22 +41,14 @@ Configuration options:
If bus/slot is not specified, the first available PCI
device will be used.
-
-The 2600 requires a firmware upload, which can be accomplished
-using the -i or --init-data option of comedi_config.
-The firmware can be
-found in the comedi_nonfree_firmware tarball available
-from http://www.comedi.org
-
*/
#include <linux/interrupt.h>
#include <linux/sched.h>
+#include <linux/firmware.h>
#include "../comedidev.h"
-/*#include "me2600_fw.h" */
-
-#define ME_DRIVER_NAME "me_daq"
+#define ME2600_FIRMWARE "me2600_firmware.bin"
#define PCI_VENDOR_ID_MEILHAUS 0x1402
#define ME2000_DEVICE_ID 0x2000
@@ -198,8 +190,7 @@ struct me_board {
static const struct me_board me_boards[] = {
{
- /* -- ME-2600i -- */
- .name = ME_DRIVER_NAME,
+ .name = "me-2600i",
.device_id = ME2600_DEVICE_ID,
/* Analog Output */
.ao_channel_nbr = 4,
@@ -214,8 +205,7 @@ static const struct me_board me_boards[] = {
.dio_channel_nbr = 32,
},
{
- /* -- ME-2000i -- */
- .name = ME_DRIVER_NAME,
+ .name = "me-2000i",
.device_id = ME2000_DEVICE_ID,
/* Analog Output */
.ao_channel_nbr = 0,
@@ -341,7 +331,7 @@ static int me_dio_insn_bits(struct comedi_device *dev,
/* Analog instant input */
static int me_ai_insn_read(struct comedi_device *dev,
- struct comedi_subdevice *subdevice,
+ struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
unsigned short value;
@@ -435,7 +425,7 @@ static int me_ai_do_cmd_test(struct comedi_device *dev,
/* Analog input command */
static int me_ai_do_cmd(struct comedi_device *dev,
- struct comedi_subdevice *subdevice)
+ struct comedi_subdevice *s)
{
return 0;
}
@@ -524,8 +514,7 @@ static int me_ao_insn_read(struct comedi_device *dev,
/* Xilinx firmware download for card: ME-2600i */
static int me2600_xilinx_download(struct comedi_device *dev,
- unsigned char *me2600_firmware,
- unsigned int length)
+ const u8 *data, size_t size)
{
unsigned int value;
unsigned int file_length;
@@ -552,19 +541,20 @@ static int me2600_xilinx_download(struct comedi_device *dev,
* Byte 8-11: date
* Byte 12-15: reserved
*/
- if (length < 16)
+ if (size < 16)
return -EINVAL;
- file_length = (((unsigned int)me2600_firmware[0] & 0xff) << 24) +
- (((unsigned int)me2600_firmware[1] & 0xff) << 16) +
- (((unsigned int)me2600_firmware[2] & 0xff) << 8) +
- ((unsigned int)me2600_firmware[3] & 0xff);
+
+ file_length = (((unsigned int)data[0] & 0xff) << 24) +
+ (((unsigned int)data[1] & 0xff) << 16) +
+ (((unsigned int)data[2] & 0xff) << 8) +
+ ((unsigned int)data[3] & 0xff);
/*
* Loop for writing firmware byte by byte to xilinx
* Firmware data start at offfset 16
*/
for (i = 0; i < file_length; i++)
- writeb((me2600_firmware[16 + i] & 0xff),
+ writeb((data[16 + i] & 0xff),
dev_private->me_regbase + 0x0);
/* Write 5 dummy values to xilinx */
@@ -590,6 +580,22 @@ static int me2600_xilinx_download(struct comedi_device *dev,
return 0;
}
+static int me2600_upload_firmware(struct comedi_device *dev)
+{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ const struct firmware *fw;
+ int ret;
+
+ ret = request_firmware(&fw, ME2600_FIRMWARE, &pcidev->dev);
+ if (ret)
+ return ret;
+
+ ret = me2600_xilinx_download(dev, fw->data, fw->size);
+ release_firmware(fw);
+
+ return ret;
+}
+
/* Reset device */
static int me_reset(struct comedi_device *dev)
{
@@ -607,44 +613,24 @@ static int me_reset(struct comedi_device *dev)
return 0;
}
-static struct pci_dev *me_find_pci_dev(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static const void *me_find_boardinfo(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
const struct me_board *board;
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
int i;
- for_each_pci_dev(pcidev) {
- if (bus || slot) {
- if (pcidev->bus->number != bus ||
- PCI_SLOT(pcidev->devfn) != slot)
- continue;
- }
- if (pcidev->vendor != PCI_VENDOR_ID_MEILHAUS)
- continue;
-
- for (i = 0; i < ARRAY_SIZE(me_boards); i++) {
- board = &me_boards[i];
- if (board->device_id != pcidev->device)
- continue;
-
- dev->board_ptr = board;
- return pcidev;
- }
+ for (i = 0; i < ARRAY_SIZE(me_boards); i++) {
+ board = &me_boards[i];
+ if (board->device_id == pcidev->device)
+ return board;
}
- dev_err(dev->class_dev,
- "No supported board found! (req. bus %d, slot %d)\n",
- bus, slot);
return NULL;
}
-static int me_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static int me_attach_pci(struct comedi_device *dev, struct pci_dev *pcidev)
{
- struct pci_dev *pci_device;
- struct comedi_subdevice *subdevice;
- struct me_board *board;
+ const struct me_board *board;
+ struct comedi_subdevice *s;
resource_size_t plx_regbase_tmp;
unsigned long plx_regbase_size_tmp;
resource_size_t me_regbase_tmp;
@@ -654,29 +640,28 @@ static int me_attach(struct comedi_device *dev, struct comedi_devconfig *it)
resource_size_t regbase_tmp;
int result, error;
+ comedi_set_hw_dev(dev, &pcidev->dev);
+
+ board = me_find_boardinfo(dev, pcidev);
+ if (!board)
+ return -ENODEV;
+ dev->board_ptr = board;
+ dev->board_name = board->name;
+
/* Allocate private memory */
if (alloc_private(dev, sizeof(struct me_private_data)) < 0)
return -ENOMEM;
- pci_device = me_find_pci_dev(dev, it);
- if (!pci_device)
- return -EIO;
- comedi_set_hw_dev(dev, &pci_device->dev);
- board = (struct me_board *)dev->board_ptr;
-
/* Enable PCI device and request PCI regions */
- if (comedi_pci_enable(pci_device, ME_DRIVER_NAME) < 0) {
+ if (comedi_pci_enable(pcidev, dev->board_name) < 0) {
printk(KERN_ERR "comedi%d: Failed to enable PCI device and "
"request regions\n", dev->minor);
return -EIO;
}
- /* Set data in device structure */
- dev->board_name = board->name;
-
/* Read PLX register base address [PCI_BASE_ADDRESS #0]. */
- plx_regbase_tmp = pci_resource_start(pci_device, 0);
- plx_regbase_size_tmp = pci_resource_len(pci_device, 0);
+ plx_regbase_tmp = pci_resource_start(pcidev, 0);
+ plx_regbase_size_tmp = pci_resource_len(pcidev, 0);
dev_private->plx_regbase =
ioremap(plx_regbase_tmp, plx_regbase_size_tmp);
dev_private->plx_regbase_size = plx_regbase_size_tmp;
@@ -687,8 +672,8 @@ static int me_attach(struct comedi_device *dev, struct comedi_devconfig *it)
/* Read Swap base address [PCI_BASE_ADDRESS #5]. */
- swap_regbase_tmp = pci_resource_start(pci_device, 5);
- swap_regbase_size_tmp = pci_resource_len(pci_device, 5);
+ swap_regbase_tmp = pci_resource_start(pcidev, 5);
+ swap_regbase_size_tmp = pci_resource_len(pcidev, 5);
if (!swap_regbase_tmp)
printk(KERN_ERR "comedi%d: Swap not present\n", dev->minor);
@@ -702,20 +687,20 @@ static int me_attach(struct comedi_device *dev, struct comedi_devconfig *it)
plx_regbase_tmp = swap_regbase_tmp;
swap_regbase_tmp = regbase_tmp;
- result = pci_write_config_dword(pci_device,
+ result = pci_write_config_dword(pcidev,
PCI_BASE_ADDRESS_0,
plx_regbase_tmp);
if (result != PCIBIOS_SUCCESSFUL)
return -EIO;
- result = pci_write_config_dword(pci_device,
+ result = pci_write_config_dword(pcidev,
PCI_BASE_ADDRESS_5,
swap_regbase_tmp);
if (result != PCIBIOS_SUCCESSFUL)
return -EIO;
} else {
plx_regbase_tmp -= 0x80;
- result = pci_write_config_dword(pci_device,
+ result = pci_write_config_dword(pcidev,
PCI_BASE_ADDRESS_0,
plx_regbase_tmp);
if (result != PCIBIOS_SUCCESSFUL)
@@ -726,8 +711,8 @@ static int me_attach(struct comedi_device *dev, struct comedi_devconfig *it)
/* Read Meilhaus register base address [PCI_BASE_ADDRESS #2]. */
- me_regbase_tmp = pci_resource_start(pci_device, 2);
- me_regbase_size_tmp = pci_resource_len(pci_device, 2);
+ me_regbase_tmp = pci_resource_start(pcidev, 2);
+ me_regbase_size_tmp = pci_resource_len(pcidev, 2);
dev_private->me_regbase_size = me_regbase_size_tmp;
dev_private->me_regbase = ioremap(me_regbase_tmp, me_regbase_size_tmp);
if (!dev_private->me_regbase) {
@@ -735,64 +720,55 @@ static int me_attach(struct comedi_device *dev, struct comedi_devconfig *it)
dev->minor);
return -ENOMEM;
}
+
/* Download firmware and reset card */
if (board->device_id == ME2600_DEVICE_ID) {
- unsigned char *aux_data;
- int aux_len;
-
- aux_data = comedi_aux_data(it->options, 0);
- aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
-
- if (!aux_data || aux_len < 1) {
- comedi_error(dev, "You must provide me2600 firmware "
- "using the --init-data option of "
- "comedi_config");
- return -EINVAL;
- }
- me2600_xilinx_download(dev, aux_data, aux_len);
+ result = me2600_upload_firmware(dev);
+ if (result < 0)
+ return result;
}
-
me_reset(dev);
error = comedi_alloc_subdevices(dev, 3);
if (error)
return error;
- subdevice = dev->subdevices + 0;
- subdevice->type = COMEDI_SUBD_AI;
- subdevice->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_CMD_READ;
- subdevice->n_chan = board->ai_channel_nbr;
- subdevice->maxdata = board->ai_resolution_mask;
- subdevice->len_chanlist = board->ai_channel_nbr;
- subdevice->range_table = board->ai_range_list;
- subdevice->cancel = me_ai_cancel;
- subdevice->insn_read = me_ai_insn_read;
- subdevice->do_cmdtest = me_ai_do_cmd_test;
- subdevice->do_cmd = me_ai_do_cmd;
-
- subdevice = dev->subdevices + 1;
- subdevice->type = COMEDI_SUBD_AO;
- subdevice->subdev_flags = SDF_WRITEABLE | SDF_COMMON;
- subdevice->n_chan = board->ao_channel_nbr;
- subdevice->maxdata = board->ao_resolution_mask;
- subdevice->len_chanlist = board->ao_channel_nbr;
- subdevice->range_table = board->ao_range_list;
- subdevice->insn_read = me_ao_insn_read;
- subdevice->insn_write = me_ao_insn_write;
-
- subdevice = dev->subdevices + 2;
- subdevice->type = COMEDI_SUBD_DIO;
- subdevice->subdev_flags = SDF_READABLE | SDF_WRITEABLE;
- subdevice->n_chan = board->dio_channel_nbr;
- subdevice->maxdata = 1;
- subdevice->len_chanlist = board->dio_channel_nbr;
- subdevice->range_table = &range_digital;
- subdevice->insn_bits = me_dio_insn_bits;
- subdevice->insn_config = me_dio_insn_config;
- subdevice->io_bits = 0;
-
- printk(KERN_INFO "comedi%d: " ME_DRIVER_NAME " attached.\n",
- dev->minor);
+ s = &dev->subdevices[0];
+ s->type = COMEDI_SUBD_AI;
+ s->subdev_flags = SDF_READABLE | SDF_COMMON | SDF_CMD_READ;
+ s->n_chan = board->ai_channel_nbr;
+ s->maxdata = board->ai_resolution_mask;
+ s->len_chanlist = board->ai_channel_nbr;
+ s->range_table = board->ai_range_list;
+ s->cancel = me_ai_cancel;
+ s->insn_read = me_ai_insn_read;
+ s->do_cmdtest = me_ai_do_cmd_test;
+ s->do_cmd = me_ai_do_cmd;
+
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AO;
+ s->subdev_flags = SDF_WRITEABLE | SDF_COMMON;
+ s->n_chan = board->ao_channel_nbr;
+ s->maxdata = board->ao_resolution_mask;
+ s->len_chanlist = board->ao_channel_nbr;
+ s->range_table = board->ao_range_list;
+ s->insn_read = me_ao_insn_read;
+ s->insn_write = me_ao_insn_write;
+
+ s = &dev->subdevices[2];
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITEABLE;
+ s->n_chan = board->dio_channel_nbr;
+ s->maxdata = 1;
+ s->len_chanlist = board->dio_channel_nbr;
+ s->range_table = &range_digital;
+ s->insn_bits = me_dio_insn_bits;
+ s->insn_config = me_dio_insn_config;
+ s->io_bits = 0;
+
+ dev_info(dev->class_dev, "%s: %s attached\n",
+ dev->driver->driver_name, dev->board_name);
+
return 0;
}
@@ -818,7 +794,7 @@ static void me_detach(struct comedi_device *dev)
static struct comedi_driver me_daq_driver = {
.driver_name = "me_daq",
.module = THIS_MODULE,
- .attach = me_attach,
+ .attach_pci = me_attach_pci,
.detach = me_detach,
};
@@ -851,3 +827,4 @@ module_comedi_pci_driver(me_daq_driver, me_daq_pci_driver);
MODULE_AUTHOR("Comedi http://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(ME2600_FIRMWARE);
diff --git a/drivers/staging/comedi/drivers/mite.c b/drivers/staging/comedi/drivers/mite.c
index a93166d6a8f8..e27850f628ce 100644
--- a/drivers/staging/comedi/drivers/mite.c
+++ b/drivers/staging/comedi/drivers/mite.c
@@ -49,6 +49,8 @@
/* #define USE_KMALLOC */
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include "mite.h"
#include "comedi_fc.h"
@@ -59,52 +61,38 @@
#define PCI_DAQ_SIZE 4096
#define PCI_DAQ_SIZE_660X 8192
-struct mite_struct *mite_devices;
-EXPORT_SYMBOL(mite_devices);
-
#define TOP_OF_PAGE(x) ((x)|(~(PAGE_MASK)))
-void mite_init(void)
+struct mite_struct *mite_alloc(struct pci_dev *pcidev)
{
- struct pci_dev *pcidev = NULL;
struct mite_struct *mite;
-
- for_each_pci_dev(pcidev) {
- if (pcidev->vendor == PCI_VENDOR_ID_NI) {
- unsigned i;
-
- mite = kzalloc(sizeof(*mite), GFP_KERNEL);
- if (!mite) {
- printk(KERN_ERR "mite: allocation failed\n");
- pci_dev_put(pcidev);
- return;
- }
- spin_lock_init(&mite->lock);
- mite->pcidev = pci_dev_get(pcidev);
- for (i = 0; i < MAX_MITE_DMA_CHANNELS; ++i) {
- mite->channels[i].mite = mite;
- mite->channels[i].channel = i;
- mite->channels[i].done = 1;
- }
- mite->next = mite_devices;
- mite_devices = mite;
+ unsigned int i;
+
+ mite = kzalloc(sizeof(*mite), GFP_KERNEL);
+ if (mite) {
+ spin_lock_init(&mite->lock);
+ mite->pcidev = pcidev;
+ for (i = 0; i < MAX_MITE_DMA_CHANNELS; ++i) {
+ mite->channels[i].mite = mite;
+ mite->channels[i].channel = i;
+ mite->channels[i].done = 1;
}
}
+ return mite;
}
+EXPORT_SYMBOL(mite_alloc);
static void dump_chip_signature(u32 csigr_bits)
{
- printk(KERN_INFO "mite: version = %i, type = %i, mite mode = %i,"
- "interface mode = %i\n",
- mite_csigr_version(csigr_bits), mite_csigr_type(csigr_bits),
- mite_csigr_mmode(csigr_bits), mite_csigr_imode(csigr_bits));
- printk(KERN_INFO "mite: num channels = %i, write post fifo depth = %i,"
- "wins = %i, iowins = %i\n",
- mite_csigr_dmac(csigr_bits), mite_csigr_wpdep(csigr_bits),
- mite_csigr_wins(csigr_bits), mite_csigr_iowins(csigr_bits));
+ pr_info("version = %i, type = %i, mite mode = %i, interface mode = %i\n",
+ mite_csigr_version(csigr_bits), mite_csigr_type(csigr_bits),
+ mite_csigr_mmode(csigr_bits), mite_csigr_imode(csigr_bits));
+ pr_info("num channels = %i, write post fifo depth = %i, wins = %i, iowins = %i\n",
+ mite_csigr_dmac(csigr_bits), mite_csigr_wpdep(csigr_bits),
+ mite_csigr_wins(csigr_bits), mite_csigr_iowins(csigr_bits));
}
-unsigned mite_fifo_size(struct mite_struct *mite, unsigned channel)
+static unsigned mite_fifo_size(struct mite_struct *mite, unsigned channel)
{
unsigned fcr_bits = readl(mite->mite_io_addr + MITE_FCR(channel));
unsigned empty_count = (fcr_bits >> 16) & 0xff;
@@ -121,7 +109,8 @@ int mite_setup2(struct mite_struct *mite, unsigned use_iodwbsr_1)
unsigned unknown_dma_burst_bits;
if (comedi_pci_enable(mite->pcidev, "mite")) {
- printk(KERN_ERR "error enabling mite and requesting io regions\n");
+ dev_err(&mite->pcidev->dev,
+ "error enabling mite and requesting io regions\n");
return -EIO;
}
pci_set_master(mite->pcidev);
@@ -130,11 +119,10 @@ int mite_setup2(struct mite_struct *mite, unsigned use_iodwbsr_1)
mite->mite_phys_addr = addr;
mite->mite_io_addr = ioremap(addr, PCI_MITE_SIZE);
if (!mite->mite_io_addr) {
- printk(KERN_ERR "Failed to remap mite io memory address\n");
+ dev_err(&mite->pcidev->dev,
+ "Failed to remap mite io memory address\n");
return -ENOMEM;
}
- printk(KERN_INFO "MITE:0x%08llx mapped to %p ",
- (unsigned long long)mite->mite_phys_addr, mite->mite_io_addr);
addr = pci_resource_start(mite->pcidev, 1);
mite->daq_phys_addr = addr;
@@ -145,15 +133,15 @@ int mite_setup2(struct mite_struct *mite, unsigned use_iodwbsr_1)
*/
mite->daq_io_addr = ioremap(mite->daq_phys_addr, length);
if (!mite->daq_io_addr) {
- printk(KERN_ERR "Failed to remap daq io memory address\n");
+ dev_err(&mite->pcidev->dev,
+ "Failed to remap daq io memory address\n");
return -ENOMEM;
}
- printk(KERN_INFO "DAQ:0x%08llx mapped to %p\n",
- (unsigned long long)mite->daq_phys_addr, mite->daq_io_addr);
if (use_iodwbsr_1) {
writel(0, mite->mite_io_addr + MITE_IODWBSR);
- printk(KERN_INFO "mite: using I/O Window Base Size register 1\n");
+ dev_info(&mite->pcidev->dev,
+ "using I/O Window Base Size register 1\n");
writel(mite->daq_phys_addr | WENAB |
MITE_IODWBSR_1_WSIZE_bits(length),
mite->mite_io_addr + MITE_IODWBSR_1);
@@ -178,9 +166,9 @@ int mite_setup2(struct mite_struct *mite, unsigned use_iodwbsr_1)
csigr_bits = readl(mite->mite_io_addr + MITE_CSIGR);
mite->num_channels = mite_csigr_dmac(csigr_bits);
if (mite->num_channels > MAX_MITE_DMA_CHANNELS) {
- printk(KERN_WARNING "mite: bug? chip claims to have %i dma "
- "channels. Setting to %i.\n",
- mite->num_channels, MAX_MITE_DMA_CHANNELS);
+ dev_warn(&mite->pcidev->dev,
+ "mite: bug? chip claims to have %i dma channels. Setting to %i.\n",
+ mite->num_channels, MAX_MITE_DMA_CHANNELS);
mite->num_channels = MAX_MITE_DMA_CHANNELS;
}
dump_chip_signature(csigr_bits);
@@ -193,9 +181,7 @@ int mite_setup2(struct mite_struct *mite, unsigned use_iodwbsr_1)
mite->mite_io_addr + MITE_CHCR(i));
}
mite->fifo_size = mite_fifo_size(mite, 0);
- printk(KERN_INFO "mite: fifo size is %i.\n", mite->fifo_size);
- mite->used = 1;
-
+ dev_info(&mite->pcidev->dev, "fifo size is %i.\n", mite->fifo_size);
return 0;
}
EXPORT_SYMBOL(mite_setup2);
@@ -206,17 +192,6 @@ int mite_setup(struct mite_struct *mite)
}
EXPORT_SYMBOL(mite_setup);
-void mite_cleanup(void)
-{
- struct mite_struct *mite, *next;
-
- for (mite = mite_devices; mite; mite = next) {
- pci_dev_put(mite->pcidev);
- next = mite->next;
- kfree(mite);
- }
-}
-
void mite_unsetup(struct mite_struct *mite)
{
/* unsigned long offset, start, length; */
@@ -236,26 +211,43 @@ void mite_unsetup(struct mite_struct *mite)
comedi_pci_disable(mite->pcidev);
mite->mite_phys_addr = 0;
}
-
- mite->used = 0;
}
EXPORT_SYMBOL(mite_unsetup);
-void mite_list_devices(void)
+struct mite_dma_descriptor_ring *mite_alloc_ring(struct mite_struct *mite)
+{
+ struct mite_dma_descriptor_ring *ring =
+ kmalloc(sizeof(struct mite_dma_descriptor_ring), GFP_KERNEL);
+
+ if (ring == NULL)
+ return ring;
+ ring->hw_dev = get_device(&mite->pcidev->dev);
+ if (ring->hw_dev == NULL) {
+ kfree(ring);
+ return NULL;
+ }
+ ring->n_links = 0;
+ ring->descriptors = NULL;
+ ring->descriptors_dma_addr = 0;
+ return ring;
+};
+EXPORT_SYMBOL(mite_alloc_ring);
+
+void mite_free_ring(struct mite_dma_descriptor_ring *ring)
{
- struct mite_struct *mite, *next;
-
- printk(KERN_INFO "Available NI device IDs:");
- if (mite_devices)
- for (mite = mite_devices; mite; mite = next) {
- next = mite->next;
- printk(KERN_INFO " 0x%04x", mite_device_id(mite));
- if (mite->used)
- printk(KERN_INFO "(used)");
+ if (ring) {
+ if (ring->descriptors) {
+ dma_free_coherent(ring->hw_dev,
+ ring->n_links *
+ sizeof(struct mite_dma_descriptor),
+ ring->descriptors,
+ ring->descriptors_dma_addr);
}
- printk(KERN_INFO "\n");
-}
-EXPORT_SYMBOL(mite_list_devices);
+ put_device(ring->hw_dev);
+ kfree(ring);
+ }
+};
+EXPORT_SYMBOL(mite_free_ring);
struct mite_channel *mite_request_channel_in_range(struct mite_struct *mite,
struct
@@ -317,7 +309,7 @@ void mite_dma_arm(struct mite_channel *mite_chan)
int chor;
unsigned long flags;
- MDPRINTK("mite_dma_arm ch%i\n", channel);
+ MDPRINTK("mite_dma_arm ch%i\n", mite_chan->channel);
/*
* memory barrier is intended to insure any twiddling with the buffer
* is done before writing to the mite to arm dma transfer
@@ -365,7 +357,8 @@ int mite_buf_change(struct mite_dma_descriptor_ring *ring,
n_links * sizeof(struct mite_dma_descriptor),
&ring->descriptors_dma_addr, GFP_KERNEL);
if (!ring->descriptors) {
- printk(KERN_ERR "mite: ring buffer allocation failed\n");
+ dev_err(async->subdevice->device->class_dev,
+ "mite: ring buffer allocation failed\n");
return -ENOMEM;
}
ring->n_links = n_links;
@@ -442,8 +435,7 @@ void mite_prep_dma(struct mite_channel *mite_chan,
mcr |= CR_PSIZE32;
break;
default:
- printk(KERN_WARNING "mite: bug! invalid mem bit width for dma "
- "transfer\n");
+ pr_warn("bug! invalid mem bit width for dma transfer\n");
break;
}
writel(mcr, mite->mite_io_addr + MITE_MCR(mite_chan->channel));
@@ -462,8 +454,7 @@ void mite_prep_dma(struct mite_channel *mite_chan,
dcr |= CR_PSIZE32;
break;
default:
- printk(KERN_WARNING "mite: bug! invalid dev bit width for dma "
- "transfer\n");
+ pr_warn("bug! invalid dev bit width for dma transfer\n");
break;
}
writel(dcr, mite->mite_io_addr + MITE_DCR(mite_chan->channel));
@@ -483,7 +474,7 @@ void mite_prep_dma(struct mite_channel *mite_chan,
}
EXPORT_SYMBOL(mite_prep_dma);
-u32 mite_device_bytes_transferred(struct mite_channel *mite_chan)
+static u32 mite_device_bytes_transferred(struct mite_channel *mite_chan)
{
struct mite_struct *mite = mite_chan->mite;
return readl(mite->mite_io_addr + MITE_DAR(mite_chan->channel));
@@ -577,7 +568,8 @@ int mite_sync_input_dma(struct mite_channel *mite_chan,
nbytes = mite_bytes_written_to_memory_lb(mite_chan);
if ((int)(mite_bytes_written_to_memory_ub(mite_chan) -
old_alloc_count) > 0) {
- printk("mite: DMA overwrite of free area\n");
+ dev_warn(async->subdevice->device->class_dev,
+ "mite: DMA overwrite of free area\n");
async->events |= COMEDI_CB_OVERFLOW;
return -1;
}
@@ -621,7 +613,8 @@ int mite_sync_output_dma(struct mite_channel *mite_chan,
(int)(nbytes_ub - stop_count) > 0)
nbytes_ub = stop_count;
if ((int)(nbytes_ub - old_alloc_count) > 0) {
- printk(KERN_ERR "mite: DMA underrun\n");
+ dev_warn(async->subdevice->device->class_dev,
+ "mite: DMA underrun\n");
async->events |= COMEDI_CB_OVERFLOW;
return -1;
}
@@ -672,8 +665,6 @@ EXPORT_SYMBOL(mite_done);
#ifdef DEBUG_MITE
-static void mite_decode(char **bit_str, unsigned int bits);
-
/* names of bits in mite registers */
static const char *const mite_CHOR_strings[] = {
@@ -743,86 +734,80 @@ static const char *const mite_CHSR_strings[] = {
"28", "lpauses", "30", "int",
};
-void mite_dump_regs(struct mite_channel *mite_chan)
-{
- unsigned long mite_io_addr =
- (unsigned long)mite_chan->mite->mite_io_addr;
- unsigned long addr = 0;
- unsigned long temp = 0;
-
- printk(KERN_DEBUG "mite_dump_regs ch%i\n", mite_chan->channel);
- printk(KERN_DEBUG "mite address is =0x%08lx\n", mite_io_addr);
-
- addr = mite_io_addr + MITE_CHOR(channel);
- printk(KERN_DEBUG "mite status[CHOR]at 0x%08lx =0x%08lx\n", addr,
- temp = readl(addr));
- mite_decode(mite_CHOR_strings, temp);
- addr = mite_io_addr + MITE_CHCR(channel);
- printk(KERN_DEBUG "mite status[CHCR]at 0x%08lx =0x%08lx\n", addr,
- temp = readl(addr));
- mite_decode(mite_CHCR_strings, temp);
- addr = mite_io_addr + MITE_TCR(channel);
- printk(KERN_DEBUG "mite status[TCR] at 0x%08lx =0x%08x\n", addr,
- readl(addr));
- addr = mite_io_addr + MITE_MCR(channel);
- printk(KERN_DEBUG "mite status[MCR] at 0x%08lx =0x%08lx\n", addr,
- temp = readl(addr));
- mite_decode(mite_MCR_strings, temp);
-
- addr = mite_io_addr + MITE_MAR(channel);
- printk(KERN_DEBUG "mite status[MAR] at 0x%08lx =0x%08x\n", addr,
- readl(addr));
- addr = mite_io_addr + MITE_DCR(channel);
- printk(KERN_DEBUG "mite status[DCR] at 0x%08lx =0x%08lx\n", addr,
- temp = readl(addr));
- mite_decode(mite_DCR_strings, temp);
- addr = mite_io_addr + MITE_DAR(channel);
- printk(KERN_DEBUG "mite status[DAR] at 0x%08lx =0x%08x\n", addr,
- readl(addr));
- addr = mite_io_addr + MITE_LKCR(channel);
- printk(KERN_DEBUG "mite status[LKCR]at 0x%08lx =0x%08lx\n", addr,
- temp = readl(addr));
- mite_decode(mite_LKCR_strings, temp);
- addr = mite_io_addr + MITE_LKAR(channel);
- printk(KERN_DEBUG "mite status[LKAR]at 0x%08lx =0x%08x\n", addr,
- readl(addr));
- addr = mite_io_addr + MITE_CHSR(channel);
- printk(KERN_DEBUG "mite status[CHSR]at 0x%08lx =0x%08lx\n", addr,
- temp = readl(addr));
- mite_decode(mite_CHSR_strings, temp);
- addr = mite_io_addr + MITE_FCR(channel);
- printk(KERN_DEBUG "mite status[FCR] at 0x%08lx =0x%08x\n\n", addr,
- readl(addr));
-}
-EXPORT_SYMBOL(mite_dump_regs);
-
-static void mite_decode(char **bit_str, unsigned int bits)
+static void mite_decode(const char *const *bit_str, unsigned int bits)
{
int i;
for (i = 31; i >= 0; i--) {
if (bits & (1 << i))
- printk(KERN_DEBUG " %s", bit_str[i]);
+ pr_debug(" %s\n", bit_str[i]);
}
- printk(KERN_DEBUG "\n");
}
-EXPORT_SYMBOL(mite_decode);
-#endif
-#ifdef MODULE
-int __init init_module(void)
+void mite_dump_regs(struct mite_channel *mite_chan)
{
- mite_init();
- mite_list_devices();
+ void __iomem *mite_io_addr = mite_chan->mite->mite_io_addr;
+ unsigned int offset;
+ unsigned int value;
+ int channel = mite_chan->channel;
+
+ pr_debug("mite_dump_regs ch%i\n", channel);
+ pr_debug("mite address is =%p\n", mite_io_addr);
+
+ offset = MITE_CHOR(channel);
+ value = readl(mite_io_addr + offset);
+ pr_debug("mite status[CHOR] at 0x%08x =0x%08x\n", offset, value);
+ mite_decode(mite_CHOR_strings, value);
+ offset = MITE_CHCR(channel);
+ value = readl(mite_io_addr + offset);
+ pr_debug("mite status[CHCR] at 0x%08x =0x%08x\n", offset, value);
+ mite_decode(mite_CHCR_strings, value);
+ offset = MITE_TCR(channel);
+ value = readl(mite_io_addr + offset);
+ pr_debug("mite status[TCR] at 0x%08x =0x%08x\n", offset, value);
+ offset = MITE_MCR(channel);
+ value = readl(mite_io_addr + offset);
+ pr_debug("mite status[MCR] at 0x%08x =0x%08x\n", offset, value);
+ mite_decode(mite_MCR_strings, value);
+ offset = MITE_MAR(channel);
+ value = readl(mite_io_addr + offset);
+ pr_debug("mite status[MAR] at 0x%08x =0x%08x\n", offset, value);
+ offset = MITE_DCR(channel);
+ value = readl(mite_io_addr + offset);
+ pr_debug("mite status[DCR] at 0x%08x =0x%08x\n", offset, value);
+ mite_decode(mite_DCR_strings, value);
+ offset = MITE_DAR(channel);
+ value = readl(mite_io_addr + offset);
+ pr_debug("mite status[DAR] at 0x%08x =0x%08x\n", offset, value);
+ offset = MITE_LKCR(channel);
+ value = readl(mite_io_addr + offset);
+ pr_debug("mite status[LKCR] at 0x%08x =0x%08x\n", offset, value);
+ mite_decode(mite_LKCR_strings, value);
+ offset = MITE_LKAR(channel);
+ value = readl(mite_io_addr + offset);
+ pr_debug("mite status[LKAR] at 0x%08x =0x%08x\n", offset, value);
+ offset = MITE_CHSR(channel);
+ value = readl(mite_io_addr + offset);
+ pr_debug("mite status[CHSR] at 0x%08x =0x%08x\n", offset, value);
+ mite_decode(mite_CHSR_strings, value);
+ offset = MITE_FCR(channel);
+ value = readl(mite_io_addr + offset);
+ pr_debug("mite status[FCR] at 0x%08x =0x%08x\n", offset, value);
+}
+EXPORT_SYMBOL(mite_dump_regs);
+#endif
+static int __init mite_module_init(void)
+{
return 0;
}
-void __exit cleanup_module(void)
+static void __exit mite_module_exit(void)
{
- mite_cleanup();
}
-#endif
+
+module_init(mite_module_init);
+module_exit(mite_module_exit);
MODULE_AUTHOR("Comedi http://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
diff --git a/drivers/staging/comedi/drivers/mite.h b/drivers/staging/comedi/drivers/mite.h
index 83f1b27a4720..255b8ba9c917 100644
--- a/drivers/staging/comedi/drivers/mite.h
+++ b/drivers/staging/comedi/drivers/mite.h
@@ -25,15 +25,16 @@
#define _MITE_H_
#include <linux/pci.h>
+#include <linux/log2.h>
#include "../comedidev.h"
/* #define DEBUG_MITE */
#define PCIMIO_COMPAT
#ifdef DEBUG_MITE
-#define MDPRINTK(format, args...) printk(format , ## args)
+#define MDPRINTK(format, args...) pr_debug(format , ## args)
#else
-#define MDPRINTK(format, args...)
+#define MDPRINTK(format, args...) do { } while (0)
#endif
#define MAX_MITE_DMA_CHANNELS 8
@@ -61,15 +62,11 @@ struct mite_channel {
};
struct mite_struct {
- struct mite_struct *next;
- int used;
-
struct pci_dev *pcidev;
resource_size_t mite_phys_addr;
void __iomem *mite_io_addr;
resource_size_t daq_phys_addr;
void __iomem *daq_io_addr;
-
struct mite_channel channels[MAX_MITE_DMA_CHANNELS];
short channel_allocated[MAX_MITE_DMA_CHANNELS];
int num_channels;
@@ -77,41 +74,12 @@ struct mite_struct {
spinlock_t lock;
};
-static inline struct mite_dma_descriptor_ring *mite_alloc_ring(struct
- mite_struct
- *mite)
-{
- struct mite_dma_descriptor_ring *ring =
- kmalloc(sizeof(struct mite_dma_descriptor_ring), GFP_KERNEL);
- if (ring == NULL)
- return ring;
- ring->hw_dev = get_device(&mite->pcidev->dev);
- if (ring->hw_dev == NULL) {
- kfree(ring);
- return NULL;
- }
- ring->n_links = 0;
- ring->descriptors = NULL;
- ring->descriptors_dma_addr = 0;
- return ring;
-};
-
-static inline void mite_free_ring(struct mite_dma_descriptor_ring *ring)
-{
- if (ring) {
- if (ring->descriptors) {
- dma_free_coherent(ring->hw_dev,
- ring->n_links *
- sizeof(struct mite_dma_descriptor),
- ring->descriptors,
- ring->descriptors_dma_addr);
- }
- put_device(ring->hw_dev);
- kfree(ring);
- }
-};
+struct mite_struct *mite_alloc(struct pci_dev *pcidev);
-extern struct mite_struct *mite_devices;
+static inline void mite_free(struct mite_struct *mite)
+{
+ kfree(mite);
+}
static inline unsigned int mite_irq(struct mite_struct *mite)
{
@@ -123,12 +91,11 @@ static inline unsigned int mite_device_id(struct mite_struct *mite)
return mite->pcidev->device;
};
-void mite_init(void);
-void mite_cleanup(void);
int mite_setup(struct mite_struct *mite);
int mite_setup2(struct mite_struct *mite, unsigned use_iodwbsr_1);
void mite_unsetup(struct mite_struct *mite);
-void mite_list_devices(void);
+struct mite_dma_descriptor_ring *mite_alloc_ring(struct mite_struct *mite);
+void mite_free_ring(struct mite_dma_descriptor_ring *ring);
struct mite_channel *mite_request_channel_in_range(struct mite_struct *mite,
struct
mite_dma_descriptor_ring
@@ -279,8 +246,9 @@ enum MITE_IODWBSR_bits {
static inline unsigned MITE_IODWBSR_1_WSIZE_bits(unsigned size)
{
unsigned order = 0;
- while (size >>= 1)
- ++order;
+
+ BUG_ON(size == 0);
+ order = ilog2(size);
BUG_ON(order < 1);
return (order - 1) & 0x1f;
}
@@ -427,12 +395,10 @@ static inline int CR_RL(unsigned int retry_limit)
{
int value = 0;
- while (retry_limit) {
- retry_limit >>= 1;
- value++;
- }
+ if (retry_limit)
+ value = 1 + ilog2(retry_limit);
if (value > 0x7)
- printk("comedi: bug! retry_limit too large\n");
+ value = 0x7;
return (value & 0x7) << 21;
}
diff --git a/drivers/staging/comedi/drivers/mpc624.c b/drivers/staging/comedi/drivers/mpc624.c
index b928b6763cd5..f8b7faefc961 100644
--- a/drivers/staging/comedi/drivers/mpc624.c
+++ b/drivers/staging/comedi/drivers/mpc624.c
@@ -353,7 +353,7 @@ static int mpc624_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_DIFF;
s->n_chan = 8;
diff --git a/drivers/staging/comedi/drivers/mpc8260cpm.c b/drivers/staging/comedi/drivers/mpc8260cpm.c
index a7fda8f01e8c..c0c33299b7f1 100644
--- a/drivers/staging/comedi/drivers/mpc8260cpm.c
+++ b/drivers/staging/comedi/drivers/mpc8260cpm.c
@@ -137,7 +137,7 @@ static int mpc8260cpm_attach(struct comedi_device *dev,
return ret;
for (i = 0; i < 4; i++) {
- s = dev->subdevices + i;
+ s = &dev->subdevices[i];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 32;
diff --git a/drivers/staging/comedi/drivers/multiq3.c b/drivers/staging/comedi/drivers/multiq3.c
index eccbe1fb4f2c..4625cb4d07c6 100644
--- a/drivers/staging/comedi/drivers/multiq3.c
+++ b/drivers/staging/comedi/drivers/multiq3.c
@@ -204,8 +204,10 @@ static int multiq3_encoder_insn_read(struct comedi_device *dev,
static void encoder_reset(struct comedi_device *dev)
{
+ struct comedi_subdevice *s = &dev->subdevices[4];
int chan;
- for (chan = 0; chan < dev->subdevices[4].n_chan; chan++) {
+
+ for (chan = 0; chan < s->n_chan; chan++) {
int control =
MULTIQ3_CONTROL_MUST | MULTIQ3_AD_MUX_EN | (chan << 3);
outw(control, dev->iobase + MULTIQ3_CONTROL);
@@ -258,7 +260,7 @@ static int multiq3_attach(struct comedi_device *dev,
if (result < 0)
return result;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* ai subdevice */
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
@@ -267,7 +269,7 @@ static int multiq3_attach(struct comedi_device *dev,
s->maxdata = 0x1fff;
s->range_table = &range_bipolar5;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* ao subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -277,7 +279,7 @@ static int multiq3_attach(struct comedi_device *dev,
s->maxdata = 0xfff;
s->range_table = &range_bipolar5;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* di subdevice */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
@@ -286,7 +288,7 @@ static int multiq3_attach(struct comedi_device *dev,
s->maxdata = 1;
s->range_table = &range_digital;
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
/* do subdevice */
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
@@ -296,7 +298,7 @@ static int multiq3_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->state = 0;
- s = dev->subdevices + 4;
+ s = &dev->subdevices[4];
/* encoder (counter) subdevice */
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
diff --git a/drivers/staging/comedi/drivers/ni_6527.c b/drivers/staging/comedi/drivers/ni_6527.c
index a80c52fb2731..51295f32ee89 100644
--- a/drivers/staging/comedi/drivers/ni_6527.c
+++ b/drivers/staging/comedi/drivers/ni_6527.c
@@ -44,8 +44,11 @@ Updated: Sat, 25 Jan 2003 13:24:40 -0800
#include <linux/interrupt.h>
#include "../comedidev.h"
+#include "comedi_fc.h"
#include "mite.h"
+#define DRIVER_NAME "ni_6527"
+
#define NI6527_DIO_SIZE 4096
#define NI6527_MITE_SIZE 4096
@@ -76,16 +79,6 @@ Updated: Sat, 25 Jan 2003 13:24:40 -0800
#define Rising_Edge_Detection_Enable(x) (0x018+(x))
#define Falling_Edge_Detection_Enable(x) (0x020+(x))
-static int ni6527_attach(struct comedi_device *dev,
- struct comedi_devconfig *it);
-static void ni6527_detach(struct comedi_device *dev);
-static struct comedi_driver driver_ni6527 = {
- .driver_name = "ni6527",
- .module = THIS_MODULE,
- .attach = ni6527_attach,
- .detach = ni6527_detach,
-};
-
struct ni6527_board {
int dev_id;
@@ -103,7 +96,6 @@ static const struct ni6527_board ni6527_boards[] = {
},
};
-#define n_ni6527_boards ARRAY_SIZE(ni6527_boards)
#define this_board ((const struct ni6527_board *)dev->board_ptr)
static DEFINE_PCI_DEVICE_TABLE(ni6527_pci_table) = {
@@ -122,8 +114,6 @@ struct ni6527_private {
#define devpriv ((struct ni6527_private *)dev->private)
-static int ni6527_find_device(struct comedi_device *dev, int bus, int slot);
-
static int ni6527_di_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
@@ -212,7 +202,7 @@ static int ni6527_do_insn_bits(struct comedi_device *dev,
static irqreturn_t ni6527_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 2;
+ struct comedi_subdevice *s = &dev->subdevices[2];
unsigned int status;
status = readb(devpriv->mite->daq_io_addr + Change_Status);
@@ -235,40 +225,20 @@ static int ni6527_intr_cmdtest(struct comedi_device *dev,
struct comedi_cmd *cmd)
{
int err = 0;
- int tmp;
-
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_OTHER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_FOLLOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_OTHER);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and */
- /* are mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -364,36 +334,53 @@ static int ni6527_intr_insn_config(struct comedi_device *dev,
return 2;
}
-static int ni6527_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static const struct ni6527_board *
+ni6527_find_boardinfo(struct pci_dev *pcidev)
+{
+ unsigned int dev_id = pcidev->device;
+ unsigned int n;
+
+ for (n = 0; n < ARRAY_SIZE(ni6527_boards); n++) {
+ const struct ni6527_board *board = &ni6527_boards[n];
+ if (board->dev_id == dev_id)
+ return board;
+ }
+ return NULL;
+}
+
+static int __devinit ni6527_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
struct comedi_subdevice *s;
int ret;
- printk(KERN_INFO "comedi%d: ni6527\n", dev->minor);
-
ret = alloc_private(dev, sizeof(struct ni6527_private));
if (ret < 0)
return ret;
- ret = ni6527_find_device(dev, it->options[0], it->options[1]);
- if (ret < 0)
- return ret;
+ dev->board_ptr = ni6527_find_boardinfo(pcidev);
+ if (!dev->board_ptr)
+ return -ENODEV;
+
+ devpriv->mite = mite_alloc(pcidev);
+ if (!devpriv->mite)
+ return -ENOMEM;
ret = mite_setup(devpriv->mite);
if (ret < 0) {
- printk(KERN_ERR "comedi: error setting up mite\n");
+ dev_err(dev->class_dev, "error setting up mite\n");
return ret;
}
dev->board_name = this_board->name;
- printk(KERN_INFO "comedi board: %s, ID=0x%02x\n", dev->board_name,
- readb(devpriv->mite->daq_io_addr + ID_Register));
+ dev_info(dev->class_dev, "board: %s, ID=0x%02x\n", dev->board_name,
+ readb(devpriv->mite->daq_io_addr + ID_Register));
ret = comedi_alloc_subdevices(dev, 3);
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = 24;
@@ -402,7 +389,7 @@ static int ni6527_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_config = ni6527_di_insn_config;
s->insn_bits = ni6527_di_insn_bits;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 24;
@@ -410,7 +397,7 @@ static int ni6527_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->maxdata = 1;
s->insn_bits = ni6527_do_insn_bits;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
dev->read_subdev = s;
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
@@ -432,9 +419,9 @@ static int ni6527_attach(struct comedi_device *dev, struct comedi_devconfig *it)
writeb(0x00, devpriv->mite->daq_io_addr + Master_Interrupt_Control);
ret = request_irq(mite_irq(devpriv->mite), ni6527_interrupt,
- IRQF_SHARED, "ni6527", dev);
+ IRQF_SHARED, DRIVER_NAME, dev);
if (ret < 0)
- printk(KERN_WARNING "comedi i6527 irq not available\n");
+ dev_warn(dev->class_dev, "irq not available\n");
else
dev->irq = mite_irq(devpriv->mite);
@@ -448,73 +435,37 @@ static void ni6527_detach(struct comedi_device *dev)
devpriv->mite->daq_io_addr + Master_Interrupt_Control);
if (dev->irq)
free_irq(dev->irq, dev);
- if (devpriv && devpriv->mite)
+ if (devpriv && devpriv->mite) {
mite_unsetup(devpriv->mite);
-}
-
-static int ni6527_find_device(struct comedi_device *dev, int bus, int slot)
-{
- struct mite_struct *mite;
- int i;
-
- for (mite = mite_devices; mite; mite = mite->next) {
- if (mite->used)
- continue;
- if (bus || slot) {
- if (bus != mite->pcidev->bus->number ||
- slot != PCI_SLOT(mite->pcidev->devfn))
- continue;
- }
- for (i = 0; i < n_ni6527_boards; i++) {
- if (mite_device_id(mite) == ni6527_boards[i].dev_id) {
- dev->board_ptr = ni6527_boards + i;
- devpriv->mite = mite;
- return 0;
- }
- }
+ mite_free(devpriv->mite);
}
- printk(KERN_ERR "comedi 6527: no device found\n");
- mite_list_devices();
- return -EIO;
}
-static int __devinit driver_ni6527_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *ent)
+static struct comedi_driver ni6527_driver = {
+ .driver_name = DRIVER_NAME,
+ .module = THIS_MODULE,
+ .attach_pci = ni6527_attach_pci,
+ .detach = ni6527_detach,
+};
+
+static int __devinit ni6527_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
{
- return comedi_pci_auto_config(dev, &driver_ni6527);
+ return comedi_pci_auto_config(dev, &ni6527_driver);
}
-static void __devexit driver_ni6527_pci_remove(struct pci_dev *dev)
+static void __devexit ni6527_pci_remove(struct pci_dev *dev)
{
comedi_pci_auto_unconfig(dev);
}
-static struct pci_driver driver_ni6527_pci_driver = {
+static struct pci_driver ni6527_pci_driver = {
+ .name = DRIVER_NAME,
.id_table = ni6527_pci_table,
- .probe = &driver_ni6527_pci_probe,
- .remove = __devexit_p(&driver_ni6527_pci_remove)
+ .probe = ni6527_pci_probe,
+ .remove = __devexit_p(ni6527_pci_remove)
};
-
-static int __init driver_ni6527_init_module(void)
-{
- int retval;
-
- retval = comedi_driver_register(&driver_ni6527);
- if (retval < 0)
- return retval;
-
- driver_ni6527_pci_driver.name = (char *)driver_ni6527.driver_name;
- return pci_register_driver(&driver_ni6527_pci_driver);
-}
-
-static void __exit driver_ni6527_cleanup_module(void)
-{
- pci_unregister_driver(&driver_ni6527_pci_driver);
- comedi_driver_unregister(&driver_ni6527);
-}
-
-module_init(driver_ni6527_init_module);
-module_exit(driver_ni6527_cleanup_module);
+module_comedi_pci_driver(ni6527_driver, ni6527_pci_driver);
MODULE_AUTHOR("Comedi http://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
diff --git a/drivers/staging/comedi/drivers/ni_65xx.c b/drivers/staging/comedi/drivers/ni_65xx.c
index bce39f1ea36d..2a73ff57a2fb 100644
--- a/drivers/staging/comedi/drivers/ni_65xx.c
+++ b/drivers/staging/comedi/drivers/ni_65xx.c
@@ -55,6 +55,7 @@ except maybe the 6514.
#include <linux/slab.h>
#include "../comedidev.h"
+#include "comedi_fc.h"
#include "mite.h"
#define NI6514_DIO_SIZE 4096
@@ -109,18 +110,7 @@ static inline unsigned Filter_Enable(unsigned port)
#define OverflowIntEnable 0x02
#define EdgeIntEnable 0x01
-static int ni_65xx_attach(struct comedi_device *dev,
- struct comedi_devconfig *it);
-static void ni_65xx_detach(struct comedi_device *dev);
-static struct comedi_driver driver_ni_65xx = {
- .driver_name = "ni_65xx",
- .module = THIS_MODULE,
- .attach = ni_65xx_attach,
- .detach = ni_65xx_detach,
-};
-
struct ni_65xx_board {
-
int dev_id;
const char *name;
unsigned num_dio_ports;
@@ -325,8 +315,6 @@ static struct ni_65xx_subdevice_private *ni_65xx_alloc_subdevice_private(void)
return subdev_private;
}
-static int ni_65xx_find_device(struct comedi_device *dev, int bus, int slot);
-
static int ni_65xx_config_filter(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
@@ -453,11 +441,9 @@ static int ni_65xx_dio_insn_bits(struct comedi_device *dev,
writeb(bits,
private(dev)->mite->daq_io_addr +
Port_Data(port));
-/* printk("wrote 0x%x to port %i\n", bits, port); */
}
port_read_bits =
readb(private(dev)->mite->daq_io_addr + Port_Data(port));
-/* printk("read 0x%x from port %i\n", port_read_bits, port); */
if (s->type == COMEDI_SUBD_DO && board(dev)->invert_outputs) {
/* Outputs inverted, so invert value read back from
* DO subdevice. (Does not apply to boards with DIO
@@ -478,7 +464,7 @@ static int ni_65xx_dio_insn_bits(struct comedi_device *dev,
static irqreturn_t ni_65xx_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 2;
+ struct comedi_subdevice *s = &dev->subdevices[2];
unsigned int status;
status = readb(private(dev)->mite->daq_io_addr + Change_Status);
@@ -501,40 +487,20 @@ static int ni_65xx_intr_cmdtest(struct comedi_device *dev,
struct comedi_cmd *cmd)
{
int err = 0;
- int tmp;
-
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_OTHER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_FOLLOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_OTHER);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually
- compatible */
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -644,41 +610,55 @@ static int ni_65xx_intr_insn_config(struct comedi_device *dev,
return 2;
}
-static int ni_65xx_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static const struct ni_65xx_board *
+ni_65xx_find_boardinfo(struct pci_dev *pcidev)
+{
+ unsigned int dev_id = pcidev->device;
+ unsigned int n;
+
+ for (n = 0; n < ARRAY_SIZE(ni_65xx_boards); n++) {
+ const struct ni_65xx_board *board = &ni_65xx_boards[n];
+ if (board->dev_id == dev_id)
+ return board;
+ }
+ return NULL;
+}
+
+static int __devinit ni_65xx_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
struct comedi_subdevice *s;
unsigned i;
int ret;
- printk(KERN_INFO "comedi%d: ni_65xx:", dev->minor);
-
ret = alloc_private(dev, sizeof(struct ni_65xx_private));
if (ret < 0)
return ret;
- ret = ni_65xx_find_device(dev, it->options[0], it->options[1]);
- if (ret < 0)
- return ret;
+ dev->board_ptr = ni_65xx_find_boardinfo(pcidev);
+ if (!dev->board_ptr)
+ return -ENODEV;
+
+ private(dev)->mite = mite_alloc(pcidev);
+ if (!private(dev)->mite)
+ return -ENOMEM;
ret = mite_setup(private(dev)->mite);
if (ret < 0) {
- printk(KERN_WARNING "error setting up mite\n");
+ dev_warn(dev->class_dev, "error setting up mite\n");
return ret;
}
dev->board_name = board(dev)->name;
dev->irq = mite_irq(private(dev)->mite);
- printk(KERN_INFO " %s", dev->board_name);
-
- printk(KERN_INFO " ID=0x%02x",
+ dev_info(dev->class_dev, "board: %s, ID=0x%02x", dev->board_name,
readb(private(dev)->mite->daq_io_addr + ID_Register));
ret = comedi_alloc_subdevices(dev, 4);
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
if (board(dev)->num_di_ports) {
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
@@ -696,7 +676,7 @@ static int ni_65xx_attach(struct comedi_device *dev,
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
if (board(dev)->num_do_ports) {
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
@@ -713,7 +693,7 @@ static int ni_65xx_attach(struct comedi_device *dev,
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
if (board(dev)->num_dio_ports) {
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
@@ -737,7 +717,7 @@ static int ni_65xx_attach(struct comedi_device *dev,
s->type = COMEDI_SUBD_UNUSED;
}
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
dev->read_subdev = s;
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
@@ -772,11 +752,9 @@ static int ni_65xx_attach(struct comedi_device *dev,
"ni_65xx", dev);
if (ret < 0) {
dev->irq = 0;
- printk(KERN_WARNING " irq not available");
+ dev_warn(dev->class_dev, "irq not available\n");
}
- printk("\n");
-
return 0;
}
@@ -791,79 +769,46 @@ static void ni_65xx_detach(struct comedi_device *dev)
if (dev->irq)
free_irq(dev->irq, dev);
if (private(dev)) {
+ struct comedi_subdevice *s;
unsigned i;
+
for (i = 0; i < dev->n_subdevices; ++i) {
- kfree(dev->subdevices[i].private);
- dev->subdevices[i].private = NULL;
+ s = &dev->subdevices[i];
+ kfree(s->private);
+ s->private = NULL;
}
- if (private(dev)->mite)
+ if (private(dev)->mite) {
mite_unsetup(private(dev)->mite);
- }
-}
-
-static int ni_65xx_find_device(struct comedi_device *dev, int bus, int slot)
-{
- struct mite_struct *mite;
- int i;
-
- for (mite = mite_devices; mite; mite = mite->next) {
- if (mite->used)
- continue;
- if (bus || slot) {
- if (bus != mite->pcidev->bus->number ||
- slot != PCI_SLOT(mite->pcidev->devfn))
- continue;
- }
- for (i = 0; i < n_ni_65xx_boards; i++) {
- if (mite_device_id(mite) == ni_65xx_boards[i].dev_id) {
- dev->board_ptr = ni_65xx_boards + i;
- private(dev)->mite = mite;
- return 0;
- }
+ mite_free(private(dev)->mite);
}
}
- printk(KERN_WARNING "no device found\n");
- mite_list_devices();
- return -EIO;
}
-static int __devinit driver_ni_65xx_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *ent)
+static struct comedi_driver ni_65xx_driver = {
+ .driver_name = "ni_65xx",
+ .module = THIS_MODULE,
+ .attach_pci = ni_65xx_attach_pci,
+ .detach = ni_65xx_detach,
+};
+
+static int __devinit ni_65xx_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
{
- return comedi_pci_auto_config(dev, &driver_ni_65xx);
+ return comedi_pci_auto_config(dev, &ni_65xx_driver);
}
-static void __devexit driver_ni_65xx_pci_remove(struct pci_dev *dev)
+static void __devexit ni_65xx_pci_remove(struct pci_dev *dev)
{
comedi_pci_auto_unconfig(dev);
}
-static struct pci_driver driver_ni_65xx_pci_driver = {
+static struct pci_driver ni_65xx_pci_driver = {
+ .name = "ni_65xx",
.id_table = ni_65xx_pci_table,
- .probe = &driver_ni_65xx_pci_probe,
- .remove = __devexit_p(&driver_ni_65xx_pci_remove)
+ .probe = ni_65xx_pci_probe,
+ .remove = __devexit_p(ni_65xx_pci_remove)
};
-
-static int __init driver_ni_65xx_init_module(void)
-{
- int retval;
-
- retval = comedi_driver_register(&driver_ni_65xx);
- if (retval < 0)
- return retval;
-
- driver_ni_65xx_pci_driver.name = (char *)driver_ni_65xx.driver_name;
- return pci_register_driver(&driver_ni_65xx_pci_driver);
-}
-
-static void __exit driver_ni_65xx_cleanup_module(void)
-{
- pci_unregister_driver(&driver_ni_65xx_pci_driver);
- comedi_driver_unregister(&driver_ni_65xx);
-}
-
-module_init(driver_ni_65xx_init_module);
-module_exit(driver_ni_65xx_cleanup_module);
+module_comedi_pci_driver(ni_65xx_driver, ni_65xx_pci_driver);
MODULE_AUTHOR("Comedi http://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
diff --git a/drivers/staging/comedi/drivers/ni_660x.c b/drivers/staging/comedi/drivers/ni_660x.c
index 5e863ff343dd..df2f3b0bab48 100644
--- a/drivers/staging/comedi/drivers/ni_660x.c
+++ b/drivers/staging/comedi/drivers/ni_660x.c
@@ -448,68 +448,46 @@ static inline struct ni_660x_private *private(struct comedi_device *dev)
return dev->private;
}
-/* initialized in ni_660x_find_device() */
+/* initialized in ni_660x_attach_pci() */
static inline const struct ni_660x_board *board(struct comedi_device *dev)
{
return dev->board_ptr;
}
-#define n_ni_660x_boards ARRAY_SIZE(ni_660x_boards)
-
-static int ni_660x_attach(struct comedi_device *dev,
- struct comedi_devconfig *it);
+static int ni_660x_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev);
static void ni_660x_detach(struct comedi_device *dev);
static void init_tio_chip(struct comedi_device *dev, int chipset);
static void ni_660x_select_pfi_output(struct comedi_device *dev,
unsigned pfi_channel,
unsigned output_select);
-static struct comedi_driver driver_ni_660x = {
+static struct comedi_driver ni_660x_driver = {
.driver_name = "ni_660x",
.module = THIS_MODULE,
- .attach = ni_660x_attach,
+ .attach_pci = ni_660x_attach_pci,
.detach = ni_660x_detach,
};
-static int __devinit driver_ni_660x_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *ent)
+static int __devinit ni_660x_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
{
- return comedi_pci_auto_config(dev, &driver_ni_660x);
+ return comedi_pci_auto_config(dev, &ni_660x_driver);
}
-static void __devexit driver_ni_660x_pci_remove(struct pci_dev *dev)
+static void __devexit ni_660x_pci_remove(struct pci_dev *dev)
{
comedi_pci_auto_unconfig(dev);
}
-static struct pci_driver driver_ni_660x_pci_driver = {
+static struct pci_driver ni_660x_pci_driver = {
+ .name = "ni_660x",
.id_table = ni_660x_pci_table,
- .probe = &driver_ni_660x_pci_probe,
- .remove = __devexit_p(&driver_ni_660x_pci_remove)
+ .probe = ni_660x_pci_probe,
+ .remove = __devexit_p(ni_660x_pci_remove)
};
+module_comedi_pci_driver(ni_660x_driver, ni_660x_pci_driver);
-static int __init driver_ni_660x_init_module(void)
-{
- int retval;
-
- retval = comedi_driver_register(&driver_ni_660x);
- if (retval < 0)
- return retval;
-
- driver_ni_660x_pci_driver.name = (char *)driver_ni_660x.driver_name;
- return pci_register_driver(&driver_ni_660x_pci_driver);
-}
-
-static void __exit driver_ni_660x_cleanup_module(void)
-{
- pci_unregister_driver(&driver_ni_660x_pci_driver);
- comedi_driver_unregister(&driver_ni_660x);
-}
-
-module_init(driver_ni_660x_init_module);
-module_exit(driver_ni_660x_cleanup_module);
-
-static int ni_660x_find_device(struct comedi_device *dev, int bus, int slot);
static int ni_660x_set_pfi_routing(struct comedi_device *dev, unsigned chan,
unsigned source);
@@ -748,8 +726,6 @@ static enum NI_660x_Register ni_gpct_to_660x_register(enum ni_gpct_register reg)
ni_660x_register = G3InterruptEnable;
break;
default:
- printk(KERN_WARNING "%s: unhandled register 0x%x in switch.\n",
- __func__, reg);
BUG();
return 0;
break;
@@ -773,8 +749,6 @@ static inline void ni_660x_write_register(struct comedi_device *dev,
writel(bits, write_address);
break;
default:
- printk(KERN_WARNING "%s: %s: bug! unhandled case (reg=0x%x) in switch.\n",
- __FILE__, __func__, reg);
BUG();
break;
}
@@ -796,8 +770,6 @@ static inline unsigned ni_660x_read_register(struct comedi_device *dev,
return readl(read_address);
break;
default:
- printk(KERN_WARNING "%s: %s: bug! unhandled case (reg=0x%x) in switch.\n",
- __FILE__, __func__, reg);
BUG();
break;
}
@@ -893,8 +865,8 @@ static int ni_660x_request_mite_channel(struct comedi_device *dev,
return 0;
}
-void ni_660x_release_mite_channel(struct comedi_device *dev,
- struct ni_gpct *counter)
+static void ni_660x_release_mite_channel(struct comedi_device *dev,
+ struct ni_gpct *counter)
{
unsigned long flags;
@@ -985,7 +957,7 @@ static irqreturn_t ni_660x_interrupt(int irq, void *d)
spin_lock_irqsave(&private(dev)->interrupt_lock, flags);
smp_mb();
for (i = 0; i < ni_660x_num_counters(dev); ++i) {
- s = dev->subdevices + NI_660X_GPCT_SUBDEV(i);
+ s = &dev->subdevices[NI_660X_GPCT_SUBDEV(i)];
ni_660x_handle_gpct_interrupt(dev, s);
}
spin_unlock_irqrestore(&private(dev)->interrupt_lock, flags);
@@ -1062,28 +1034,43 @@ static void ni_660x_free_mite_rings(struct comedi_device *dev)
}
}
-static int ni_660x_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static const struct ni_660x_board *
+ni_660x_find_boardinfo(struct pci_dev *pcidev)
+{
+ unsigned int dev_id = pcidev->device;
+ unsigned int n;
+
+ for (n = 0; n < ARRAY_SIZE(ni_660x_boards); n++) {
+ const struct ni_660x_board *board = &ni_660x_boards[n];
+ if (board->dev_id == dev_id)
+ return board;
+ }
+ return NULL;
+}
+
+static int __devinit ni_660x_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
struct comedi_subdevice *s;
int ret;
unsigned i;
unsigned global_interrupt_config_bits;
- printk(KERN_INFO "comedi%d: ni_660x: ", dev->minor);
-
ret = ni_660x_allocate_private(dev);
if (ret < 0)
return ret;
- ret = ni_660x_find_device(dev, it->options[0], it->options[1]);
- if (ret < 0)
- return ret;
+ dev->board_ptr = ni_660x_find_boardinfo(pcidev);
+ if (!dev->board_ptr)
+ return -ENODEV;
+ private(dev)->mite = mite_alloc(pcidev);
+ if (!private(dev)->mite)
+ return -ENOMEM;
dev->board_name = board(dev)->name;
ret = mite_setup2(private(dev)->mite, 1);
if (ret < 0) {
- printk(KERN_WARNING "error setting up mite\n");
+ dev_warn(dev->class_dev, "error setting up mite\n");
return ret;
}
comedi_set_hw_dev(dev, &private(dev)->mite->pcidev->dev);
@@ -1091,17 +1078,15 @@ static int ni_660x_attach(struct comedi_device *dev,
if (ret < 0)
return ret;
- printk(KERN_INFO " %s ", dev->board_name);
-
ret = comedi_alloc_subdevices(dev, 2 + NI_660X_MAX_NUM_COUNTERS);
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* Old GENERAL-PURPOSE COUNTER/TIME (GPCT) subdevice, no longer used */
s->type = COMEDI_SUBD_UNUSED;
- s = dev->subdevices + NI_660X_DIO_SUBDEV;
+ s = &dev->subdevices[NI_660X_DIO_SUBDEV];
/* DIGITAL I/O SUBDEVICE */
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
@@ -1124,7 +1109,7 @@ static int ni_660x_attach(struct comedi_device *dev,
if (private(dev)->counter_dev == NULL)
return -ENOMEM;
for (i = 0; i < NI_660X_MAX_NUM_COUNTERS; ++i) {
- s = dev->subdevices + NI_660X_GPCT_SUBDEV(i);
+ s = &dev->subdevices[NI_660X_GPCT_SUBDEV(i)];
if (i < ni_660x_num_counters(dev)) {
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags =
@@ -1174,7 +1159,7 @@ static int ni_660x_attach(struct comedi_device *dev,
ret = request_irq(mite_irq(private(dev)->mite), ni_660x_interrupt,
IRQF_SHARED, "ni_660x", dev);
if (ret < 0) {
- printk(KERN_WARNING " irq not available\n");
+ dev_warn(dev->class_dev, " irq not available\n");
return ret;
}
dev->irq = mite_irq(private(dev)->mite);
@@ -1183,7 +1168,7 @@ static int ni_660x_attach(struct comedi_device *dev,
global_interrupt_config_bits |= Cascade_Int_Enable_Bit;
ni_660x_write_register(dev, 0, global_interrupt_config_bits,
GlobalInterruptConfigRegister);
- printk(KERN_INFO "attached\n");
+ dev_info(dev->class_dev, "ni_660x: %s attached\n", dev->board_name);
return 0;
}
@@ -1197,6 +1182,7 @@ static void ni_660x_detach(struct comedi_device *dev)
if (private(dev)->mite) {
ni_660x_free_mite_rings(dev);
mite_unsetup(private(dev)->mite);
+ mite_free(private(dev)->mite);
}
}
}
@@ -1240,33 +1226,6 @@ static int ni_660x_GPCT_winsn(struct comedi_device *dev,
return ni_tio_winsn(subdev_to_counter(s), insn, data);
}
-static int ni_660x_find_device(struct comedi_device *dev, int bus, int slot)
-{
- struct mite_struct *mite;
- int i;
-
- for (mite = mite_devices; mite; mite = mite->next) {
- if (mite->used)
- continue;
- if (bus || slot) {
- if (bus != mite->pcidev->bus->number ||
- slot != PCI_SLOT(mite->pcidev->devfn))
- continue;
- }
-
- for (i = 0; i < n_ni_660x_boards; i++) {
- if (mite_device_id(mite) == ni_660x_boards[i].dev_id) {
- dev->board_ptr = ni_660x_boards + i;
- private(dev)->mite = mite;
- return 0;
- }
- }
- }
- printk(KERN_WARNING "no device found\n");
- mite_list_devices();
- return -EIO;
-}
-
static int ni_660x_dio_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
diff --git a/drivers/staging/comedi/drivers/ni_670x.c b/drivers/staging/comedi/drivers/ni_670x.c
index 9c57618f2c5b..eac6dc047bb0 100644
--- a/drivers/staging/comedi/drivers/ni_670x.c
+++ b/drivers/staging/comedi/drivers/ni_670x.c
@@ -187,37 +187,22 @@ static int ni_670x_dio_insn_config(struct comedi_device *dev,
return insn->n;
}
-static int ni_670x_find_device(struct comedi_device *dev, int bus, int slot)
+static const struct ni_670x_board *
+ni_670x_find_boardinfo(struct pci_dev *pcidev)
{
- struct ni_670x_private *devpriv = dev->private;
- struct mite_struct *mite;
- int i;
-
- for (mite = mite_devices; mite; mite = mite->next) {
- if (mite->used)
- continue;
- if (bus || slot) {
- if (bus != mite->pcidev->bus->number
- || slot != PCI_SLOT(mite->pcidev->devfn))
- continue;
- }
-
- for (i = 0; i < ARRAY_SIZE(ni_670x_boards); i++) {
- if (mite_device_id(mite) == ni_670x_boards[i].dev_id) {
- dev->board_ptr = ni_670x_boards + i;
- devpriv->mite = mite;
+ unsigned int dev_id = pcidev->device;
+ unsigned int n;
- return 0;
- }
- }
+ for (n = 0; n < ARRAY_SIZE(ni_670x_boards); n++) {
+ const struct ni_670x_board *board = &ni_670x_boards[n];
+ if (board->dev_id == dev_id)
+ return board;
}
- dev_warn(dev->class_dev, "no device found\n");
- mite_list_devices();
- return -EIO;
+ return NULL;
}
-static int ni_670x_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int __devinit ni_670x_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
const struct ni_670x_board *thisboard;
struct ni_670x_private *devpriv;
@@ -229,10 +214,12 @@ static int ni_670x_attach(struct comedi_device *dev,
if (ret < 0)
return ret;
devpriv = dev->private;
-
- ret = ni_670x_find_device(dev, it->options[0], it->options[1]);
- if (ret < 0)
- return ret;
+ dev->board_ptr = ni_670x_find_boardinfo(pcidev);
+ if (!dev->board_ptr)
+ return -ENODEV;
+ devpriv->mite = mite_alloc(pcidev);
+ if (!devpriv->mite)
+ return -ENOMEM;
thisboard = comedi_board(dev);
ret = mite_setup(devpriv->mite);
@@ -241,13 +228,12 @@ static int ni_670x_attach(struct comedi_device *dev,
return ret;
}
dev->board_name = thisboard->name;
- dev->irq = mite_irq(devpriv->mite);
ret = comedi_alloc_subdevices(dev, 2);
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* analog output subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -271,7 +257,7 @@ static int ni_670x_attach(struct comedi_device *dev,
s->insn_write = &ni_670x_ao_winsn;
s->insn_read = &ni_670x_ao_rinsn;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* digital i/o subdevice */
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
@@ -298,20 +284,20 @@ static void ni_670x_detach(struct comedi_device *dev)
struct comedi_subdevice *s;
if (dev->n_subdevices) {
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
if (s)
kfree(s->range_table_list);
}
- if (devpriv && devpriv->mite)
+ if (devpriv && devpriv->mite) {
mite_unsetup(devpriv->mite);
- if (dev->irq)
- free_irq(dev->irq, dev);
+ mite_free(devpriv->mite);
+ }
}
static struct comedi_driver ni_670x_driver = {
.driver_name = "ni_670x",
.module = THIS_MODULE,
- .attach = ni_670x_attach,
+ .attach_pci = ni_670x_attach_pci,
.detach = ni_670x_detach,
};
diff --git a/drivers/staging/comedi/drivers/ni_at_a2150.c b/drivers/staging/comedi/drivers/ni_at_a2150.c
index b53a4286f8cb..83950807b672 100644
--- a/drivers/staging/comedi/drivers/ni_at_a2150.c
+++ b/drivers/staging/comedi/drivers/ni_at_a2150.c
@@ -321,45 +321,23 @@ static int a2150_ai_cmdtest(struct comedi_device *dev,
int startChan;
int i;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources are unique and mutually
- * compatible
- */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -832,7 +810,7 @@ static int a2150_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return ret;
/* analog input subdevice */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_OTHER | SDF_CMD_READ;
diff --git a/drivers/staging/comedi/drivers/ni_at_ao.c b/drivers/staging/comedi/drivers/ni_at_ao.c
index 62c8c44a8d28..93938cec93e7 100644
--- a/drivers/staging/comedi/drivers/ni_at_ao.c
+++ b/drivers/staging/comedi/drivers/ni_at_ao.c
@@ -358,7 +358,7 @@ static int atao_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* analog output subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -371,7 +371,7 @@ static int atao_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_write = &atao_ao_winsn;
s->insn_read = &atao_ao_rinsn;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* digital i/o subdevice */
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
@@ -381,7 +381,7 @@ static int atao_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_bits = atao_dio_insn_bits;
s->insn_config = atao_dio_insn_config;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* caldac subdevice */
s->type = COMEDI_SUBD_CALIB;
s->subdev_flags = SDF_WRITABLE | SDF_INTERNAL;
@@ -390,7 +390,7 @@ static int atao_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_read = atao_calib_insn_read;
s->insn_write = atao_calib_insn_write;
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
/* eeprom subdevice */
/* s->type=COMEDI_SUBD_EEPROM; */
s->type = COMEDI_SUBD_UNUSED;
diff --git a/drivers/staging/comedi/drivers/ni_atmio.c b/drivers/staging/comedi/drivers/ni_atmio.c
index 6448373878ed..cac25572f6bb 100644
--- a/drivers/staging/comedi/drivers/ni_atmio.c
+++ b/drivers/staging/comedi/drivers/ni_atmio.c
@@ -489,7 +489,7 @@ static int ni_atmio_attach(struct comedi_device *dev,
/* generic E series stuff in ni_mio_common.c */
- ret = ni_E_init(dev, it);
+ ret = ni_E_init(dev);
if (ret < 0)
return ret;
diff --git a/drivers/staging/comedi/drivers/ni_atmio16d.c b/drivers/staging/comedi/drivers/ni_atmio16d.c
index 2c78d3dd242a..e91a620f9db3 100644
--- a/drivers/staging/comedi/drivers/ni_atmio16d.c
+++ b/drivers/staging/comedi/drivers/ni_atmio16d.c
@@ -40,6 +40,7 @@ Devices: [National Instruments] AT-MIO-16 (atmio16), AT-MIO-16D (atmio16d)
#include <linux/ioport.h>
+#include "comedi_fc.h"
#include "8255.h"
/* Configuration and Status Registers */
@@ -234,7 +235,7 @@ static void reset_atmio16d(struct comedi_device *dev)
static irqreturn_t atmio16d_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
comedi_buf_put(s->async, inw(dev->iobase + AD_FIFO_REG));
@@ -246,45 +247,26 @@ static int atmio16d_ai_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
- int err = 0, tmp;
+ int err = 0;
- /* make sure triggers are valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_TIMER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique & mutually compatible */
- /* note that mutual compatibility is not an issue here */
- if (cmd->scan_begin_src != TRIG_FOLLOW &&
- cmd->scan_begin_src != TRIG_EXT &&
- cmd->scan_begin_src != TRIG_TIMER)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -724,7 +706,7 @@ static int atmio16d_attach(struct comedi_device *dev,
devpriv->dac1_coding = it->options[12];
/* setup sub-devices */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
/* ai subdevice */
s->type = COMEDI_SUBD_AI;
@@ -749,7 +731,7 @@ static int atmio16d_attach(struct comedi_device *dev,
}
/* ao subdevice */
- s++;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
s->n_chan = 2;
@@ -775,7 +757,7 @@ static int atmio16d_attach(struct comedi_device *dev,
}
/* Digital I/O */
- s++;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
s->n_chan = 8;
@@ -785,7 +767,7 @@ static int atmio16d_attach(struct comedi_device *dev,
s->range_table = &range_digital;
/* 8255 subdevice */
- s++;
+ s = &dev->subdevices[3];
if (board->has_8255)
subdev_8255_init(dev, s, NULL, dev->iobase);
else
@@ -793,7 +775,7 @@ static int atmio16d_attach(struct comedi_device *dev,
/* don't yet know how to deal with counter/timers */
#if 0
- s++;
+ s = &dev->subdevices[4];
/* do */
s->type = COMEDI_SUBD_TIMER;
s->n_chan = 0;
@@ -807,9 +789,12 @@ static int atmio16d_attach(struct comedi_device *dev,
static void atmio16d_detach(struct comedi_device *dev)
{
const struct atmio16_board_t *board = comedi_board(dev);
+ struct comedi_subdevice *s;
- if (dev->subdevices && board->has_8255)
- subdev_8255_cleanup(dev, dev->subdevices + 3);
+ if (dev->subdevices && board->has_8255) {
+ s = &dev->subdevices[3];
+ subdev_8255_cleanup(dev, s);
+ }
if (dev->irq)
free_irq(dev->irq, dev);
reset_atmio16d(dev);
diff --git a/drivers/staging/comedi/drivers/ni_daq_700.c b/drivers/staging/comedi/drivers/ni_daq_700.c
index 83016b411851..2ba0ade45c64 100644
--- a/drivers/staging/comedi/drivers/ni_daq_700.c
+++ b/drivers/staging/comedi/drivers/ni_daq_700.c
@@ -1,6 +1,6 @@
/*
* comedi/drivers/ni_daq_700.c
- * Driver for DAQCard-700 DIO only
+ * Driver for DAQCard-700 DIO/AI
* copied from 8255
*
* COMEDI - Linux Control and Measurement Device Interface
@@ -29,14 +29,25 @@ Author: Fred Brooks <nsaspook@nsaspook.com>,
based on ni_daq_dio24 by Daniel Vecino Castel <dvecino@able.es>
Devices: [National Instruments] PCMCIA DAQ-Card-700 (ni_daq_700)
Status: works
-Updated: Thu, 21 Feb 2008 12:07:20 +0000
+Updated: Wed, 19 Sep 2012 12:07:20 +0000
-The daqcard-700 appears in Comedi as a single digital I/O subdevice with
-16 channels. The channel 0 corresponds to the daqcard-700's output
+The daqcard-700 appears in Comedi as a digital I/O subdevice (0) with
+16 channels and a analog input subdevice (1) with 16 single-ended channels.
+
+Digital: The channel 0 corresponds to the daqcard-700's output
port, bit 0; channel 8 corresponds to the input port, bit 0.
-Direction configuration: channels 0-7 output, 8-15 input (8225 device
+Digital direction configuration: channels 0-7 output, 8-15 input (8225 device
emu as port A output, port B input, port C N/A).
+
+Analog: The input range is 0 to 4095 for -10 to +10 volts
+IRQ is assigned but not used.
+
+Version 0.1 Original DIO only driver
+Version 0.2 DIO and basic AI analog input support on 16 se channels
+
+Manuals: Register level: http://www.ni.com/pdf/manuals/340698.pdf
+ User Manual: http://www.ni.com/pdf/manuals/320676d.pdf
*/
#include <linux/interrupt.h>
@@ -51,16 +62,29 @@ emu as port A output, port B input, port C N/A).
static struct pcmcia_device *pcmcia_cur_dev;
-struct dio700_board {
+struct daq700_board {
const char *name;
};
-#define DIO_W 0x04
-#define DIO_R 0x05
-
-static int subdev_700_insn(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
- unsigned int *data)
+/* daqcard700 registers */
+#define DIO_W 0x04 /* WO 8bit */
+#define DIO_R 0x05 /* RO 8bit */
+#define CMD_R1 0x00 /* WO 8bit */
+#define CMD_R2 0x07 /* RW 8bit */
+#define CMD_R3 0x05 /* W0 8bit */
+#define STA_R1 0x00 /* RO 8bit */
+#define STA_R2 0x01 /* RO 8bit */
+#define ADFIFO_R 0x02 /* RO 16bit */
+#define ADCLEAR_R 0x01 /* WO 8bit */
+#define CDA_R0 0x08 /* RW 8bit */
+#define CDA_R1 0x09 /* RW 8bit */
+#define CDA_R2 0x0A /* RW 8bit */
+#define CMO_R 0x0B /* RO 8bit */
+#define TIC_R 0x06 /* WO 8bit */
+
+static int daq700_dio_insn_bits(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
if (data[0]) {
s->state &= ~data[0];
@@ -76,7 +100,7 @@ static int subdev_700_insn(struct comedi_device *dev,
return insn->n;
}
-static int subdev_700_insn_config(struct comedi_device *dev,
+static int daq700_dio_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
@@ -97,9 +121,90 @@ static int subdev_700_insn_config(struct comedi_device *dev,
return insn->n;
}
-static int dio700_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static int daq700_ai_rinsn(struct comedi_device *dev,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn, unsigned int *data)
{
- const struct dio700_board *thisboard = comedi_board(dev);
+ int n, i, chan;
+ int d;
+ unsigned int status;
+ enum { TIMEOUT = 100 };
+
+ chan = CR_CHAN(insn->chanspec);
+ /* write channel to multiplexer */
+ /* set mask scan bit high to disable scanning */
+ outb(chan | 0x80, dev->iobase + CMD_R1);
+
+ /* convert n samples */
+ for (n = 0; n < insn->n; n++) {
+ /* trigger conversion with out0 L to H */
+ outb(0x00, dev->iobase + CMD_R2); /* enable ADC conversions */
+ outb(0x30, dev->iobase + CMO_R); /* mode 0 out0 L, from H */
+ /* mode 1 out0 H, L to H, start conversion */
+ outb(0x32, dev->iobase + CMO_R);
+ /* wait for conversion to end */
+ for (i = 0; i < TIMEOUT; i++) {
+ status = inb(dev->iobase + STA_R2);
+ if ((status & 0x03) != 0) {
+ dev_info(dev->class_dev,
+ "Overflow/run Error\n");
+ return -EOVERFLOW;
+ }
+ status = inb(dev->iobase + STA_R1);
+ if ((status & 0x02) != 0) {
+ dev_info(dev->class_dev, "Data Error\n");
+ return -ENODATA;
+ }
+ if ((status & 0x11) == 0x01) {
+ /* ADC conversion complete */
+ break;
+ }
+ udelay(1);
+ }
+ if (i == TIMEOUT) {
+ dev_info(dev->class_dev,
+ "timeout during ADC conversion\n");
+ return -ETIMEDOUT;
+ }
+ /* read data */
+ d = inw(dev->iobase + ADFIFO_R);
+ /* mangle the data as necessary */
+ /* Bipolar Offset Binary: 0 to 4095 for -10 to +10 */
+ d &= 0x0fff;
+ d ^= 0x0800;
+ data[n] = d;
+ }
+ return n;
+}
+
+/*
+ * Data acquisition is enabled.
+ * The counter 0 output is high.
+ * The I/O connector pin CLK1 drives counter 1 source.
+ * Multiple-channel scanning is disabled.
+ * All interrupts are disabled.
+ * The analog input range is set to +-10 V
+ * The analog input mode is single-ended.
+ * The analog input circuitry is initialized to channel 0.
+ * The A/D FIFO is cleared.
+ */
+static void daq700_ai_config(struct comedi_device *dev,
+ struct comedi_subdevice *s)
+{
+ unsigned long iobase = dev->iobase;
+
+ outb(0x80, iobase + CMD_R1); /* disable scanning, ADC to chan 0 */
+ outb(0x00, iobase + CMD_R2); /* clear all bits */
+ outb(0x00, iobase + CMD_R3); /* set +-10 range */
+ outb(0x32, iobase + CMO_R); /* config counter mode1, out0 to H */
+ outb(0x00, iobase + TIC_R); /* clear counter interrupt */
+ outb(0x00, iobase + ADCLEAR_R); /* clear the ADC FIFO */
+ inw(iobase + ADFIFO_R); /* read 16bit junk from FIFO to clear */
+}
+
+static int daq700_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+{
+ const struct daq700_board *thisboard = comedi_board(dev);
struct comedi_subdevice *s;
struct pcmcia_device *link;
int ret;
@@ -116,23 +221,33 @@ static int dio700_attach(struct comedi_device *dev, struct comedi_devconfig *it)
dev->board_name = thisboard->name;
- ret = comedi_alloc_subdevices(dev, 1);
+ ret = comedi_alloc_subdevices(dev, 2);
if (ret)
return ret;
/* DAQCard-700 dio */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 16;
s->range_table = &range_digital;
s->maxdata = 1;
- s->insn_bits = subdev_700_insn;
- s->insn_config = subdev_700_insn_config;
-
+ s->insn_bits = daq700_dio_insn_bits;
+ s->insn_config = daq700_dio_insn_config;
s->state = 0;
s->io_bits = 0x00ff;
+ /* DAQCard-700 ai */
+ s = &dev->subdevices[1];
+ s->type = COMEDI_SUBD_AI;
+ /* we support single-ended (ground) */
+ s->subdev_flags = SDF_READABLE | SDF_GROUND;
+ s->n_chan = 16;
+ s->maxdata = (1 << 12) - 1;
+ s->range_table = &range_bipolar10;
+ s->insn_read = daq700_ai_rinsn;
+ daq700_ai_config(dev, s);
+
dev_info(dev->class_dev, "%s: %s, io 0x%lx\n",
dev->driver->driver_name,
dev->board_name,
@@ -141,12 +256,12 @@ static int dio700_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return 0;
}
-static void dio700_detach(struct comedi_device *dev)
+static void daq700_detach(struct comedi_device *dev)
{
/* nothing to cleanup */
}
-static const struct dio700_board dio700_boards[] = {
+static const struct daq700_board daq700_boards[] = {
{
.name = "daqcard-700",
}, {
@@ -154,17 +269,17 @@ static const struct dio700_board dio700_boards[] = {
},
};
-static struct comedi_driver driver_dio700 = {
+static struct comedi_driver daq700_driver = {
.driver_name = "ni_daq_700",
.module = THIS_MODULE,
- .attach = dio700_attach,
- .detach = dio700_detach,
- .board_name = &dio700_boards[0].name,
- .num_names = ARRAY_SIZE(dio700_boards),
- .offset = sizeof(struct dio700_board),
+ .attach = daq700_attach,
+ .detach = daq700_detach,
+ .board_name = &daq700_boards[0].name,
+ .num_names = ARRAY_SIZE(daq700_boards),
+ .offset = sizeof(struct daq700_board),
};
-static int dio700_pcmcia_config_loop(struct pcmcia_device *p_dev,
+static int daq700_pcmcia_config_loop(struct pcmcia_device *p_dev,
void *priv_data)
{
if (p_dev->config_index == 0)
@@ -173,14 +288,14 @@ static int dio700_pcmcia_config_loop(struct pcmcia_device *p_dev,
return pcmcia_request_io(p_dev);
}
-static int dio700_cs_attach(struct pcmcia_device *link)
+static int daq700_cs_attach(struct pcmcia_device *link)
{
int ret;
link->config_flags |= CONF_ENABLE_IRQ | CONF_AUTO_AUDIO |
CONF_AUTO_SET_IO;
- ret = pcmcia_loop_config(link, dio700_pcmcia_config_loop, NULL);
+ ret = pcmcia_loop_config(link, daq700_pcmcia_config_loop, NULL);
if (ret)
goto failed;
@@ -199,52 +314,53 @@ failed:
return ret;
}
-static void dio700_cs_detach(struct pcmcia_device *link)
+static void daq700_cs_detach(struct pcmcia_device *link)
{
pcmcia_disable_device(link);
pcmcia_cur_dev = NULL;
}
-static const struct pcmcia_device_id dio700_cs_ids[] = {
+static const struct pcmcia_device_id daq700_cs_ids[] = {
PCMCIA_DEVICE_MANF_CARD(0x010b, 0x4743),
PCMCIA_DEVICE_NULL
};
-MODULE_DEVICE_TABLE(pcmcia, dio700_cs_ids);
+MODULE_DEVICE_TABLE(pcmcia, daq700_cs_ids);
-static struct pcmcia_driver dio700_cs_driver = {
+static struct pcmcia_driver daq700_cs_driver = {
.name = "ni_daq_700",
.owner = THIS_MODULE,
- .probe = dio700_cs_attach,
- .remove = dio700_cs_detach,
- .id_table = dio700_cs_ids,
+ .probe = daq700_cs_attach,
+ .remove = daq700_cs_detach,
+ .id_table = daq700_cs_ids,
};
-static int __init dio700_cs_init(void)
+static int __init daq700_cs_init(void)
{
int ret;
- ret = comedi_driver_register(&driver_dio700);
+ ret = comedi_driver_register(&daq700_driver);
if (ret < 0)
return ret;
- ret = pcmcia_register_driver(&dio700_cs_driver);
+ ret = pcmcia_register_driver(&daq700_cs_driver);
if (ret < 0) {
- comedi_driver_unregister(&driver_dio700);
+ comedi_driver_unregister(&daq700_driver);
return ret;
}
return 0;
}
-module_init(dio700_cs_init);
+module_init(daq700_cs_init);
-static void __exit dio700_cs_exit(void)
+static void __exit daq700_cs_exit(void)
{
- pcmcia_unregister_driver(&dio700_cs_driver);
- comedi_driver_unregister(&driver_dio700);
+ pcmcia_unregister_driver(&daq700_cs_driver);
+ comedi_driver_unregister(&daq700_driver);
}
-module_exit(dio700_cs_exit);
+module_exit(daq700_cs_exit);
MODULE_AUTHOR("Fred Brooks <nsaspook@nsaspook.com>");
MODULE_DESCRIPTION(
- "Comedi driver for National Instruments PCMCIA DAQCard-700 DIO");
+ "Comedi driver for National Instruments PCMCIA DAQCard-700 DIO/AI");
+MODULE_VERSION("0.2.00");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/comedi/drivers/ni_daq_dio24.c b/drivers/staging/comedi/drivers/ni_daq_dio24.c
index e27cae0eb8a2..0ca222bbcbe6 100644
--- a/drivers/staging/comedi/drivers/ni_daq_dio24.c
+++ b/drivers/staging/comedi/drivers/ni_daq_dio24.c
@@ -164,7 +164,7 @@ static int dio24_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return ret;
/* 8255 dio */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
subdev_8255_init(dev, s, NULL, dev->iobase);
return 0;
@@ -172,8 +172,12 @@ static int dio24_attach(struct comedi_device *dev, struct comedi_devconfig *it)
static void dio24_detach(struct comedi_device *dev)
{
- if (dev->subdevices)
- subdev_8255_cleanup(dev, dev->subdevices + 0);
+ struct comedi_subdevice *s;
+
+ if (dev->subdevices) {
+ s = &dev->subdevices[0];
+ subdev_8255_cleanup(dev, s);
+ }
if (thisboard->bustype != pcmcia_bustype && dev->iobase)
release_region(dev->iobase, DIO24_SIZE);
if (dev->irq)
@@ -304,7 +308,7 @@ MODULE_DESCRIPTION("Comedi driver for National Instruments "
"PCMCIA DAQ-Card DIO-24");
MODULE_LICENSE("GPL");
-struct pcmcia_driver dio24_cs_driver = {
+static struct pcmcia_driver dio24_cs_driver = {
.probe = dio24_cs_attach,
.remove = dio24_cs_detach,
.suspend = dio24_cs_suspend,
diff --git a/drivers/staging/comedi/drivers/ni_labpc.c b/drivers/staging/comedi/drivers/ni_labpc.c
index ab8b787c78bb..28b91a6c3789 100644
--- a/drivers/staging/comedi/drivers/ni_labpc.c
+++ b/drivers/staging/comedi/drivers/ni_labpc.c
@@ -73,9 +73,6 @@ NI manuals:
*/
-#undef LABPC_DEBUG
-/* #define LABPC_DEBUG enable debugging messages */
-
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/io.h>
@@ -209,7 +206,13 @@ NI manuals:
#define INIT_A1_BITS 0x70
#define COUNTER_B_BASE_REG 0x18
-static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it);
+enum scan_mode {
+ MODE_SINGLE_CHAN,
+ MODE_SINGLE_CHAN_INTERVAL,
+ MODE_MULT_CHAN_UP,
+ MODE_MULT_CHAN_DOWN,
+};
+
static int labpc_cancel(struct comedi_device *dev, struct comedi_subdevice *s);
static irqreturn_t labpc_interrupt(int irq, void *d);
static int labpc_drain_fifo(struct comedi_device *dev);
@@ -240,12 +243,10 @@ static int labpc_eeprom_write_insn(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data);
-static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd);
+static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd,
+ enum scan_mode scan_mode);
#ifdef CONFIG_ISA_DMA_API
-static unsigned int labpc_suggest_transfer_size(struct comedi_cmd cmd);
-#endif
-#ifdef CONFIG_COMEDI_PCI_DRIVERS
-static int labpc_find_device(struct comedi_device *dev, int bus, int slot);
+static unsigned int labpc_suggest_transfer_size(const struct comedi_cmd *cmd);
#endif
static int labpc_dio_mem_callback(int dir, int port, int data,
unsigned long arg);
@@ -261,13 +262,6 @@ static int labpc_eeprom_write(struct comedi_device *dev,
static void write_caldac(struct comedi_device *dev, unsigned int channel,
unsigned int value);
-enum scan_mode {
- MODE_SINGLE_CHAN,
- MODE_SINGLE_CHAN_INTERVAL,
- MODE_MULT_CHAN_UP,
- MODE_MULT_CHAN_DOWN,
-};
-
/* analog input ranges */
#define NUM_LABPC_PLUS_AI_RANGES 16
/* indicates unipolar ranges */
@@ -416,12 +410,12 @@ static inline void labpc_outb(unsigned int byte, unsigned long address)
static inline unsigned int labpc_readb(unsigned long address)
{
- return readb((void *)address);
+ return readb((void __iomem *)address);
}
static inline void labpc_writeb(unsigned int byte, unsigned long address)
{
- writeb(byte, (void *)address);
+ writeb(byte, (void __iomem *)address);
}
static const struct labpc_board_struct labpc_boards[] = {
@@ -495,33 +489,14 @@ static const int sample_size = 2;
#define devpriv ((struct labpc_private *)dev->private)
-static struct comedi_driver driver_labpc = {
- .driver_name = DRV_NAME,
- .module = THIS_MODULE,
- .attach = labpc_attach,
- .detach = labpc_common_detach,
- .num_names = ARRAY_SIZE(labpc_boards),
- .board_name = &labpc_boards[0].name,
- .offset = sizeof(struct labpc_board_struct),
-};
-
-#ifdef CONFIG_COMEDI_PCI_DRIVERS
-static DEFINE_PCI_DEVICE_TABLE(labpc_pci_table) = {
- {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x161)},
- {0}
-};
-
-MODULE_DEVICE_TABLE(pci, labpc_pci_table);
-#endif /* CONFIG_COMEDI_PCI_DRIVERS */
-
static inline int labpc_counter_load(struct comedi_device *dev,
unsigned long base_address,
unsigned int counter_number,
unsigned int count, unsigned int mode)
{
if (thisboard->memory_mapped_io)
- return i8254_mm_load((void *)base_address, 0, counter_number,
- count, mode);
+ return i8254_mm_load((void __iomem *)base_address, 0,
+ counter_number, count, mode);
else
return i8254_load(base_address, 0, counter_number, count, mode);
}
@@ -538,25 +513,16 @@ int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
short lsb, msb;
int ret;
- printk(KERN_ERR "comedi%d: ni_labpc: %s, io 0x%lx", dev->minor,
- thisboard->name,
- iobase);
- if (irq)
- printk(", irq %u", irq);
- if (dma_chan)
- printk(", dma %u", dma_chan);
- printk("\n");
-
+ dev_info(dev->class_dev, "ni_labpc: %s\n", thisboard->name);
if (iobase == 0) {
- printk(KERN_ERR "io base address is zero!\n");
+ dev_err(dev->class_dev, "io base address is zero!\n");
return -EINVAL;
}
/* request io regions for isa boards */
if (thisboard->bustype == isa_bustype) {
/* check if io addresses are available */
- if (!request_region(iobase, LABPC_SIZE,
- driver_labpc.driver_name)) {
- printk(KERN_ERR "I/O port conflict\n");
+ if (!request_region(iobase, LABPC_SIZE, DRV_NAME)) {
+ dev_err(dev->class_dev, "I/O port conflict\n");
return -EIO;
}
}
@@ -588,8 +554,9 @@ int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
|| thisboard->bustype == pcmcia_bustype)
isr_flags |= IRQF_SHARED;
if (request_irq(irq, labpc_interrupt, isr_flags,
- driver_labpc.driver_name, dev)) {
- printk(KERN_ERR "unable to allocate irq %u\n", irq);
+ DRV_NAME, dev)) {
+ dev_err(dev->class_dev, "unable to allocate irq %u\n",
+ irq);
return -EINVAL;
}
}
@@ -598,19 +565,21 @@ int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
#ifdef CONFIG_ISA_DMA_API
/* grab dma channel */
if (dma_chan > 3) {
- printk(KERN_ERR " invalid dma channel %u\n", dma_chan);
+ dev_err(dev->class_dev, "invalid dma channel %u\n", dma_chan);
return -EINVAL;
} else if (dma_chan) {
/* allocate dma buffer */
devpriv->dma_buffer =
kmalloc(dma_buffer_size, GFP_KERNEL | GFP_DMA);
if (devpriv->dma_buffer == NULL) {
- printk(KERN_ERR " failed to allocate dma buffer\n");
+ dev_err(dev->class_dev,
+ "failed to allocate dma buffer\n");
return -ENOMEM;
}
- if (request_dma(dma_chan, driver_labpc.driver_name)) {
- printk(KERN_ERR " failed to allocate dma channel %u\n",
- dma_chan);
+ if (request_dma(dma_chan, DRV_NAME)) {
+ dev_err(dev->class_dev,
+ "failed to allocate dma channel %u\n",
+ dma_chan);
return -EINVAL;
}
devpriv->dma_chan = dma_chan;
@@ -628,7 +597,7 @@ int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
return ret;
/* analog input subdevice */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
s->type = COMEDI_SUBD_AI;
s->subdev_flags =
@@ -643,7 +612,7 @@ int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
s->cancel = labpc_cancel;
/* analog output */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
if (thisboard->has_ao) {
/*
* Could provide command support, except it only has a
@@ -670,7 +639,7 @@ int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
}
/* 8255 dio */
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* if board uses io memory we have to give a custom callback
* function to the 8255 driver */
if (thisboard->memory_mapped_io)
@@ -680,7 +649,7 @@ int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
subdev_8255_init(dev, s, NULL, dev->iobase + DIO_BASE_REG);
/* calibration subdevices for boards that have one */
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
if (thisboard->register_layout == labpc_1200_layout) {
s->type = COMEDI_SUBD_CALIB;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
@@ -695,7 +664,7 @@ int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
s->type = COMEDI_SUBD_UNUSED;
/* EEPROM */
- s = dev->subdevices + 4;
+ s = &dev->subdevices[4];
if (thisboard->register_layout == labpc_1200_layout) {
s->type = COMEDI_SUBD_MEMORY;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
@@ -706,12 +675,6 @@ int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
for (i = 0; i < EEPROM_SIZE; i++)
devpriv->eeprom_data[i] = labpc_eeprom_read(dev, i);
-#ifdef LABPC_DEBUG
- printk(KERN_ERR " eeprom:");
- for (i = 0; i < EEPROM_SIZE; i++)
- printk(" %i:0x%x ", i, devpriv->eeprom_data[i]);
- printk("\n");
-#endif
} else
s->type = COMEDI_SUBD_UNUSED;
@@ -719,14 +682,52 @@ int labpc_common_attach(struct comedi_device *dev, unsigned long iobase,
}
EXPORT_SYMBOL_GPL(labpc_common_attach);
+static const struct labpc_board_struct *
+labpc_pci_find_boardinfo(struct pci_dev *pcidev)
+{
+ unsigned int device_id = pcidev->device;
+ unsigned int n;
+
+ for (n = 0; n < ARRAY_SIZE(labpc_boards); n++) {
+ const struct labpc_board_struct *board = &labpc_boards[n];
+ if (board->bustype == pci_bustype &&
+ board->device_id == device_id)
+ return board;
+ }
+ return NULL;
+}
+
+static int __devinit labpc_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
+{
+ unsigned long iobase;
+ unsigned int irq;
+ int ret;
+
+ if (!IS_ENABLED(CONFIG_COMEDI_PCI_DRIVERS))
+ return -ENODEV;
+ ret = alloc_private(dev, sizeof(struct labpc_private));
+ if (ret < 0)
+ return ret;
+ dev->board_ptr = labpc_pci_find_boardinfo(pcidev);
+ if (!dev->board_ptr)
+ return -ENODEV;
+ devpriv->mite = mite_alloc(pcidev);
+ if (!devpriv->mite)
+ return -ENOMEM;
+ ret = mite_setup(devpriv->mite);
+ if (ret < 0)
+ return ret;
+ iobase = (unsigned long)devpriv->mite->daq_io_addr;
+ irq = mite_irq(devpriv->mite);
+ return labpc_common_attach(dev, iobase, irq, 0);
+}
+
static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
unsigned long iobase = 0;
unsigned int irq = 0;
unsigned int dma_chan = 0;
-#ifdef CONFIG_COMEDI_PCI_DRIVERS
- int retval;
-#endif
/* allocate and initialize dev->private */
if (alloc_private(dev, sizeof(struct labpc_private)) < 0)
@@ -740,34 +741,26 @@ static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
irq = it->options[1];
dma_chan = it->options[2];
#else
- printk(KERN_ERR " this driver has not been built with ISA DMA "
- "support.\n");
+ dev_err(dev->class_dev,
+ "ni_labpc driver has not been built with ISA DMA support.\n");
return -EINVAL;
#endif
break;
case pci_bustype:
#ifdef CONFIG_COMEDI_PCI_DRIVERS
- retval = labpc_find_device(dev, it->options[0], it->options[1]);
- if (retval < 0)
- return retval;
- retval = mite_setup(devpriv->mite);
- if (retval < 0)
- return retval;
- iobase = (unsigned long)devpriv->mite->daq_io_addr;
- irq = mite_irq(devpriv->mite);
+ dev_err(dev->class_dev,
+ "manual configuration of PCI board '%s' is not supported\n",
+ thisboard->name);
+ return -EINVAL;
#else
- printk(KERN_ERR " this driver has not been built with PCI "
- "support.\n");
+ dev_err(dev->class_dev,
+ "ni_labpc driver has not been built with PCI support.\n");
return -EINVAL;
#endif
break;
- case pcmcia_bustype:
- printk
- (" this driver does not support pcmcia cards, use ni_labpc_cs.o\n");
- return -EINVAL;
- break;
default:
- printk(KERN_ERR "bug! couldn't determine board type\n");
+ dev_err(dev->class_dev,
+ "ni_labpc: bug! couldn't determine board type\n");
return -EINVAL;
break;
}
@@ -775,42 +768,14 @@ static int labpc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return labpc_common_attach(dev, iobase, irq, dma_chan);
}
-/* adapted from ni_pcimio for finding mite based boards (pc-1200) */
-#ifdef CONFIG_COMEDI_PCI_DRIVERS
-static int labpc_find_device(struct comedi_device *dev, int bus, int slot)
-{
- struct mite_struct *mite;
- int i;
- for (mite = mite_devices; mite; mite = mite->next) {
- if (mite->used)
- continue;
-/* if bus/slot are specified then make sure we have the right bus/slot */
- if (bus || slot) {
- if (bus != mite->pcidev->bus->number
- || slot != PCI_SLOT(mite->pcidev->devfn))
- continue;
- }
- for (i = 0; i < driver_labpc.num_names; i++) {
- if (labpc_boards[i].bustype != pci_bustype)
- continue;
- if (mite_device_id(mite) == labpc_boards[i].device_id) {
- devpriv->mite = mite;
-/* fixup board pointer, in case we were using the dummy "ni_labpc" entry */
- dev->board_ptr = &labpc_boards[i];
- return 0;
- }
- }
- }
- printk(KERN_ERR "no device found\n");
- mite_list_devices();
- return -EIO;
-}
-#endif
-
void labpc_common_detach(struct comedi_device *dev)
{
- if (dev->subdevices)
- subdev_8255_cleanup(dev, dev->subdevices + 2);
+ struct comedi_subdevice *s;
+
+ if (dev->subdevices) {
+ s = &dev->subdevices[2];
+ subdev_8255_cleanup(dev, s);
+ }
#ifdef CONFIG_ISA_DMA_API
/* only free stuff if it has been allocated by _attach */
kfree(devpriv->dma_buffer);
@@ -822,8 +787,10 @@ void labpc_common_detach(struct comedi_device *dev)
if (thisboard->bustype == isa_bustype && dev->iobase)
release_region(dev->iobase, LABPC_SIZE);
#ifdef CONFIG_COMEDI_PCI_DRIVERS
- if (devpriv->mite)
+ if (devpriv->mite) {
mite_unsetup(devpriv->mite);
+ mite_free(devpriv->mite);
+ }
#endif
};
EXPORT_SYMBOL_GPL(labpc_common_detach);
@@ -868,21 +835,19 @@ static enum scan_mode labpc_ai_scan_mode(const struct comedi_cmd *cmd)
if (CR_CHAN(cmd->chanlist[0]) > CR_CHAN(cmd->chanlist[1]))
return MODE_MULT_CHAN_DOWN;
- printk(KERN_ERR "ni_labpc: bug! this should never happen\n");
-
+ pr_err("ni_labpc: bug! cannot determine AI scan mode\n");
return 0;
}
static int labpc_ai_chanlist_invalid(const struct comedi_device *dev,
- const struct comedi_cmd *cmd)
+ const struct comedi_cmd *cmd,
+ enum scan_mode mode)
{
- int mode, channel, range, aref, i;
+ int channel, range, aref, i;
if (cmd->chanlist == NULL)
return 0;
- mode = labpc_ai_scan_mode(cmd);
-
if (mode == MODE_SINGLE_CHAN)
return 0;
@@ -924,7 +889,8 @@ static int labpc_ai_chanlist_invalid(const struct comedi_device *dev,
}
break;
default:
- printk(KERN_ERR "ni_labpc: bug! in chanlist check\n");
+ dev_err(dev->class_dev,
+ "ni_labpc: bug! in chanlist check\n");
return 1;
break;
}
@@ -945,9 +911,10 @@ static int labpc_ai_chanlist_invalid(const struct comedi_device *dev,
return 0;
}
-static int labpc_use_continuous_mode(const struct comedi_cmd *cmd)
+static int labpc_use_continuous_mode(const struct comedi_cmd *cmd,
+ enum scan_mode mode)
{
- if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN)
+ if (mode == MODE_SINGLE_CHAN)
return 1;
if (cmd->scan_begin_src == TRIG_FOLLOW)
@@ -956,24 +923,25 @@ static int labpc_use_continuous_mode(const struct comedi_cmd *cmd)
return 0;
}
-static unsigned int labpc_ai_convert_period(const struct comedi_cmd *cmd)
+static unsigned int labpc_ai_convert_period(const struct comedi_cmd *cmd,
+ enum scan_mode mode)
{
if (cmd->convert_src != TRIG_TIMER)
return 0;
- if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
- cmd->scan_begin_src == TRIG_TIMER)
+ if (mode == MODE_SINGLE_CHAN && cmd->scan_begin_src == TRIG_TIMER)
return cmd->scan_begin_arg;
return cmd->convert_arg;
}
-static void labpc_set_ai_convert_period(struct comedi_cmd *cmd, unsigned int ns)
+static void labpc_set_ai_convert_period(struct comedi_cmd *cmd,
+ enum scan_mode mode, unsigned int ns)
{
if (cmd->convert_src != TRIG_TIMER)
return;
- if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
+ if (mode == MODE_SINGLE_CHAN &&
cmd->scan_begin_src == TRIG_TIMER) {
cmd->scan_begin_arg = ns;
if (cmd->convert_arg > cmd->scan_begin_arg)
@@ -982,25 +950,25 @@ static void labpc_set_ai_convert_period(struct comedi_cmd *cmd, unsigned int ns)
cmd->convert_arg = ns;
}
-static unsigned int labpc_ai_scan_period(const struct comedi_cmd *cmd)
+static unsigned int labpc_ai_scan_period(const struct comedi_cmd *cmd,
+ enum scan_mode mode)
{
if (cmd->scan_begin_src != TRIG_TIMER)
return 0;
- if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
- cmd->convert_src == TRIG_TIMER)
+ if (mode == MODE_SINGLE_CHAN && cmd->convert_src == TRIG_TIMER)
return 0;
return cmd->scan_begin_arg;
}
-static void labpc_set_ai_scan_period(struct comedi_cmd *cmd, unsigned int ns)
+static void labpc_set_ai_scan_period(struct comedi_cmd *cmd,
+ enum scan_mode mode, unsigned int ns)
{
if (cmd->scan_begin_src != TRIG_TIMER)
return;
- if (labpc_ai_scan_mode(cmd) == MODE_SINGLE_CHAN &&
- cmd->convert_src == TRIG_TIMER)
+ if (mode == MODE_SINGLE_CHAN && cmd->convert_src == TRIG_TIMER)
return;
cmd->scan_begin_arg = ns;
@@ -1011,54 +979,33 @@ static int labpc_ai_cmdtest(struct comedi_device *dev,
{
int err = 0;
int tmp, tmp2;
- int stop_mask;
+ unsigned int stop_mask;
+ enum scan_mode mode;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
stop_mask = TRIG_COUNT | TRIG_NONE;
if (thisboard->register_layout == labpc_1200_layout)
stop_mask |= TRIG_EXT;
- cmd->stop_src &= stop_mask;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->stop_src, stop_mask);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_EXT)
- err++;
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_FOLLOW &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_COUNT &&
- cmd->stop_src != TRIG_EXT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
/* can't have external stop and start triggers at once */
if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT)
@@ -1133,14 +1080,15 @@ static int labpc_ai_cmdtest(struct comedi_device *dev,
tmp = cmd->convert_arg;
tmp2 = cmd->scan_begin_arg;
- labpc_adc_timing(dev, cmd);
+ mode = labpc_ai_scan_mode(cmd);
+ labpc_adc_timing(dev, cmd, mode);
if (tmp != cmd->convert_arg || tmp2 != cmd->scan_begin_arg)
err++;
if (err)
return 4;
- if (labpc_ai_chanlist_invalid(dev, cmd))
+ if (labpc_ai_chanlist_invalid(dev, cmd, mode))
return 5;
return 0;
@@ -1156,6 +1104,7 @@ static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
struct comedi_async *async = s->async;
struct comedi_cmd *cmd = &async->cmd;
enum transfer_type xfer;
+ enum scan_mode mode;
unsigned long flags;
if (!dev->irq) {
@@ -1221,6 +1170,7 @@ static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
} else
xfer = fifo_not_empty_transfer;
devpriv->current_transfer = xfer;
+ mode = labpc_ai_scan_mode(cmd);
/* setup command6 register for 1200 boards */
if (thisboard->register_layout == labpc_1200_layout) {
@@ -1245,7 +1195,7 @@ static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
else
devpriv->command6_bits &= ~A1_INTR_EN_BIT;
/* are we scanning up or down through channels? */
- if (labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_UP)
+ if (mode == MODE_MULT_CHAN_UP)
devpriv->command6_bits |= ADC_SCAN_UP_BIT;
else
devpriv->command6_bits &= ~ADC_SCAN_UP_BIT;
@@ -1256,19 +1206,18 @@ static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
/* setup channel list, etc (command1 register) */
devpriv->command1_bits = 0;
- if (labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_UP)
+ if (mode == MODE_MULT_CHAN_UP)
channel = CR_CHAN(cmd->chanlist[cmd->chanlist_len - 1]);
else
channel = CR_CHAN(cmd->chanlist[0]);
/* munge channel bits for differential / scan disabled mode */
- if (labpc_ai_scan_mode(cmd) != MODE_SINGLE_CHAN && aref == AREF_DIFF)
+ if (mode != MODE_SINGLE_CHAN && aref == AREF_DIFF)
channel *= 2;
devpriv->command1_bits |= ADC_CHAN_BITS(channel);
devpriv->command1_bits |= thisboard->ai_range_code[range];
devpriv->write_byte(devpriv->command1_bits, dev->iobase + COMMAND1_REG);
/* manual says to set scan enable bit on second pass */
- if (labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_UP ||
- labpc_ai_scan_mode(cmd) == MODE_MULT_CHAN_DOWN) {
+ if (mode == MODE_MULT_CHAN_UP || mode == MODE_MULT_CHAN_DOWN) {
devpriv->command1_bits |= ADC_SCAN_EN_BIT;
/* need a brief delay before enabling scan, or scan
* list will get screwed when you switch
@@ -1283,7 +1232,7 @@ static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
devpriv->command4_bits |= EXT_CONVERT_DISABLE_BIT;
/* XXX should discard first scan when using interval scanning
* since manual says it is not synced with scan clock */
- if (labpc_use_continuous_mode(cmd) == 0) {
+ if (labpc_use_continuous_mode(cmd, mode) == 0) {
devpriv->command4_bits |= INTERVAL_SCAN_EN_BIT;
if (cmd->scan_begin_src == TRIG_EXT)
devpriv->command4_bits |= EXT_SCAN_EN_BIT;
@@ -1301,7 +1250,7 @@ static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
if (cmd->convert_src == TRIG_TIMER || cmd->scan_begin_src == TRIG_TIMER) {
/* set up pacing */
- labpc_adc_timing(dev, cmd);
+ labpc_adc_timing(dev, cmd, mode);
/* load counter b0 in mode 3 */
ret = labpc_counter_load(dev, dev->iobase + COUNTER_B_BASE_REG,
0, devpriv->divisor_b0, 3);
@@ -1311,7 +1260,7 @@ static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
}
}
/* set up conversion pacing */
- if (labpc_ai_convert_period(cmd)) {
+ if (labpc_ai_convert_period(cmd, mode)) {
/* load counter a0 in mode 2 */
ret = labpc_counter_load(dev, dev->iobase + COUNTER_A_BASE_REG,
0, devpriv->divisor_a0, 2);
@@ -1324,7 +1273,7 @@ static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
dev->iobase + COUNTER_A_CONTROL_REG);
/* set up scan pacing */
- if (labpc_ai_scan_period(cmd)) {
+ if (labpc_ai_scan_period(cmd, mode)) {
/* load counter b1 in mode 2 */
ret = labpc_counter_load(dev, dev->iobase + COUNTER_B_BASE_REG,
1, devpriv->divisor_b1, 2);
@@ -1347,7 +1296,7 @@ static int labpc_ai_cmd(struct comedi_device *dev, struct comedi_subdevice *s)
set_dma_addr(devpriv->dma_chan,
virt_to_bus(devpriv->dma_buffer));
/* set appropriate size of transfer */
- devpriv->dma_transfer_size = labpc_suggest_transfer_size(*cmd);
+ devpriv->dma_transfer_size = labpc_suggest_transfer_size(cmd);
if (cmd->stop_src == TRIG_COUNT &&
devpriv->count * sample_size < devpriv->dma_transfer_size) {
devpriv->dma_transfer_size =
@@ -1786,8 +1735,8 @@ static int labpc_eeprom_write_insn(struct comedi_device *dev,
/* only allow writes to user area of eeprom */
if (channel < 16 || channel > 127) {
- printk
- ("eeprom writes are only allowed to channels 16 through 127 (the pointer and user areas)");
+ dev_dbg(dev->class_dev,
+ "eeprom writes are only allowed to channels 16 through 127 (the pointer and user areas)\n");
return -EINVAL;
}
@@ -1800,13 +1749,13 @@ static int labpc_eeprom_write_insn(struct comedi_device *dev,
#ifdef CONFIG_ISA_DMA_API
/* utility function that suggests a dma transfer size in bytes */
-static unsigned int labpc_suggest_transfer_size(struct comedi_cmd cmd)
+static unsigned int labpc_suggest_transfer_size(const struct comedi_cmd *cmd)
{
unsigned int size;
unsigned int freq;
- if (cmd.convert_src == TRIG_TIMER)
- freq = 1000000000 / cmd.convert_arg;
+ if (cmd->convert_src == TRIG_TIMER)
+ freq = 1000000000 / cmd->convert_arg;
/* return some default value */
else
freq = 0xffffffff;
@@ -1825,24 +1774,29 @@ static unsigned int labpc_suggest_transfer_size(struct comedi_cmd cmd)
#endif
/* figures out what counter values to use based on command */
-static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd)
+static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd,
+ enum scan_mode mode)
{
/* max value for 16 bit counter in mode 2 */
const int max_counter_value = 0x10000;
/* min value for 16 bit counter in mode 2 */
const int min_counter_value = 2;
unsigned int base_period;
+ unsigned int scan_period;
+ unsigned int convert_period;
/*
* if both convert and scan triggers are TRIG_TIMER, then they
* both rely on counter b0
*/
- if (labpc_ai_convert_period(cmd) && labpc_ai_scan_period(cmd)) {
+ convert_period = labpc_ai_convert_period(cmd, mode);
+ scan_period = labpc_ai_scan_period(cmd, mode);
+ if (convert_period && scan_period) {
/*
* pick the lowest b0 divisor value we can (for maximum input
* clock speed on convert and scan counters)
*/
- devpriv->divisor_b0 = (labpc_ai_scan_period(cmd) - 1) /
+ devpriv->divisor_b0 = (scan_period - 1) /
(LABPC_TIMER_BASE * max_counter_value) + 1;
if (devpriv->divisor_b0 < min_counter_value)
devpriv->divisor_b0 = min_counter_value;
@@ -1856,25 +1810,19 @@ static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd)
default:
case TRIG_ROUND_NEAREST:
devpriv->divisor_a0 =
- (labpc_ai_convert_period(cmd) +
- (base_period / 2)) / base_period;
+ (convert_period + (base_period / 2)) / base_period;
devpriv->divisor_b1 =
- (labpc_ai_scan_period(cmd) +
- (base_period / 2)) / base_period;
+ (scan_period + (base_period / 2)) / base_period;
break;
case TRIG_ROUND_UP:
devpriv->divisor_a0 =
- (labpc_ai_convert_period(cmd) + (base_period -
- 1)) / base_period;
+ (convert_period + (base_period - 1)) / base_period;
devpriv->divisor_b1 =
- (labpc_ai_scan_period(cmd) + (base_period -
- 1)) / base_period;
+ (scan_period + (base_period - 1)) / base_period;
break;
case TRIG_ROUND_DOWN:
- devpriv->divisor_a0 =
- labpc_ai_convert_period(cmd) / base_period;
- devpriv->divisor_b1 =
- labpc_ai_scan_period(cmd) / base_period;
+ devpriv->divisor_a0 = convert_period / base_period;
+ devpriv->divisor_b1 = scan_period / base_period;
break;
}
/* make sure a0 and b1 values are acceptable */
@@ -1887,18 +1835,15 @@ static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd)
if (devpriv->divisor_b1 > max_counter_value)
devpriv->divisor_b1 = max_counter_value;
/* write corrected timings to command */
- labpc_set_ai_convert_period(cmd,
+ labpc_set_ai_convert_period(cmd, mode,
base_period * devpriv->divisor_a0);
- labpc_set_ai_scan_period(cmd,
+ labpc_set_ai_scan_period(cmd, mode,
base_period * devpriv->divisor_b1);
/*
* if only one TRIG_TIMER is used, we can employ the generic
* cascaded timing functions
*/
- } else if (labpc_ai_scan_period(cmd)) {
- unsigned int scan_period;
-
- scan_period = labpc_ai_scan_period(cmd);
+ } else if (scan_period) {
/*
* calculate cascaded counter values
* that give desired scan timing
@@ -1908,11 +1853,8 @@ static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd)
&(devpriv->divisor_b0),
&scan_period,
cmd->flags & TRIG_ROUND_MASK);
- labpc_set_ai_scan_period(cmd, scan_period);
- } else if (labpc_ai_convert_period(cmd)) {
- unsigned int convert_period;
-
- convert_period = labpc_ai_convert_period(cmd);
+ labpc_set_ai_scan_period(cmd, mode, scan_period);
+ } else if (convert_period) {
/*
* calculate cascaded counter values
* that give desired conversion timing
@@ -1922,7 +1864,7 @@ static void labpc_adc_timing(struct comedi_device *dev, struct comedi_cmd *cmd)
&(devpriv->divisor_b0),
&convert_period,
cmd->flags & TRIG_ROUND_MASK);
- labpc_set_ai_convert_period(cmd, convert_period);
+ labpc_set_ai_convert_period(cmd, mode, convert_period);
}
}
@@ -1930,10 +1872,10 @@ static int labpc_dio_mem_callback(int dir, int port, int data,
unsigned long iobase)
{
if (dir) {
- writeb(data, (void *)(iobase + port));
+ writeb(data, (void __iomem *)(iobase + port));
return 0;
} else {
- return readb((void *)(iobase + port));
+ return readb((void __iomem *)(iobase + port));
}
}
@@ -2136,57 +2078,44 @@ static void write_caldac(struct comedi_device *dev, unsigned int channel,
devpriv->write_byte(devpriv->command5_bits, dev->iobase + COMMAND5_REG);
}
+static struct comedi_driver labpc_driver = {
+ .driver_name = DRV_NAME,
+ .module = THIS_MODULE,
+ .attach = labpc_attach,
+ .attach_pci = labpc_attach_pci,
+ .detach = labpc_common_detach,
+ .num_names = ARRAY_SIZE(labpc_boards),
+ .board_name = &labpc_boards[0].name,
+ .offset = sizeof(struct labpc_board_struct),
+};
+
#ifdef CONFIG_COMEDI_PCI_DRIVERS
-static int __devinit driver_labpc_pci_probe(struct pci_dev *dev,
- const struct pci_device_id *ent)
+static DEFINE_PCI_DEVICE_TABLE(labpc_pci_table) = {
+ {PCI_DEVICE(PCI_VENDOR_ID_NI, 0x161)},
+ {0}
+};
+MODULE_DEVICE_TABLE(pci, labpc_pci_table);
+
+static int __devinit labpc_pci_probe(struct pci_dev *dev,
+ const struct pci_device_id *ent)
{
- return comedi_pci_auto_config(dev, &driver_labpc);
+ return comedi_pci_auto_config(dev, &labpc_driver);
}
-static void __devexit driver_labpc_pci_remove(struct pci_dev *dev)
+static void __devexit labpc_pci_remove(struct pci_dev *dev)
{
comedi_pci_auto_unconfig(dev);
}
-static struct pci_driver driver_labpc_pci_driver = {
+static struct pci_driver labpc_pci_driver = {
+ .name = DRV_NAME,
.id_table = labpc_pci_table,
- .probe = &driver_labpc_pci_probe,
- .remove = __devexit_p(&driver_labpc_pci_remove)
+ .probe = labpc_pci_probe,
+ .remove = __devexit_p(labpc_pci_remove)
};
-
-static int __init driver_labpc_init_module(void)
-{
- int retval;
-
- retval = comedi_driver_register(&driver_labpc);
- if (retval < 0)
- return retval;
-
- driver_labpc_pci_driver.name = (char *)driver_labpc.driver_name;
- return pci_register_driver(&driver_labpc_pci_driver);
-}
-
-static void __exit driver_labpc_cleanup_module(void)
-{
- pci_unregister_driver(&driver_labpc_pci_driver);
- comedi_driver_unregister(&driver_labpc);
-}
-
-module_init(driver_labpc_init_module);
-module_exit(driver_labpc_cleanup_module);
+module_comedi_pci_driver(labpc_driver, labpc_pci_driver);
#else
-static int __init driver_labpc_init_module(void)
-{
- return comedi_driver_register(&driver_labpc);
-}
-
-static void __exit driver_labpc_cleanup_module(void)
-{
- comedi_driver_unregister(&driver_labpc);
-}
-
-module_init(driver_labpc_init_module);
-module_exit(driver_labpc_cleanup_module);
+module_comedi_driver(labpc_driver);
#endif
diff --git a/drivers/staging/comedi/drivers/ni_labpc_cs.c b/drivers/staging/comedi/drivers/ni_labpc_cs.c
index dbb61b6b3ed1..eb0417eb6d7d 100644
--- a/drivers/staging/comedi/drivers/ni_labpc_cs.c
+++ b/drivers/staging/comedi/drivers/ni_labpc_cs.c
@@ -270,7 +270,7 @@ MODULE_AUTHOR("Frank Mori Hess <fmhess@users.sourceforge.net>");
MODULE_DESCRIPTION("Comedi driver for National Instruments Lab-PC");
MODULE_LICENSE("GPL");
-struct pcmcia_driver labpc_cs_driver = {
+static struct pcmcia_driver labpc_cs_driver = {
.probe = labpc_cs_attach,
.remove = labpc_cs_detach,
.suspend = labpc_cs_suspend,
@@ -291,7 +291,7 @@ static void __exit exit_labpc_cs(void)
pcmcia_unregister_driver(&labpc_cs_driver);
}
-int __init labpc_init_module(void)
+static int __init labpc_init_module(void)
{
int ret;
@@ -302,7 +302,7 @@ int __init labpc_init_module(void)
return comedi_driver_register(&driver_labpc_cs);
}
-void __exit labpc_exit_module(void)
+static void __exit labpc_exit_module(void)
{
exit_labpc_cs();
comedi_driver_unregister(&driver_labpc_cs);
diff --git a/drivers/staging/comedi/drivers/ni_mio_common.c b/drivers/staging/comedi/drivers/ni_mio_common.c
index cf0e0d147f8c..3e5fdae93163 100644
--- a/drivers/staging/comedi/drivers/ni_mio_common.c
+++ b/drivers/staging/comedi/drivers/ni_mio_common.c
@@ -644,10 +644,10 @@ static void ni_release_ao_mite_channel(struct comedi_device *dev)
#endif /* PCIDMA */
}
-void ni_release_gpct_mite_channel(struct comedi_device *dev,
- unsigned gpct_index)
-{
#ifdef PCIDMA
+static void ni_release_gpct_mite_channel(struct comedi_device *dev,
+ unsigned gpct_index)
+{
unsigned long flags;
BUG_ON(gpct_index >= NUM_GPCT);
@@ -663,8 +663,8 @@ void ni_release_gpct_mite_channel(struct comedi_device *dev,
mite_release_channel(mite_chan);
}
spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
-#endif /* PCIDMA */
}
+#endif /* PCIDMA */
static void ni_release_cdo_mite_channel(struct comedi_device *dev)
{
@@ -872,7 +872,7 @@ static irqreturn_t ni_E_interrupt(int irq, void *d)
#ifdef PCIDMA
static void ni_sync_ai_dma(struct comedi_device *dev)
{
- struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_AI_SUBDEV];
unsigned long flags;
spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
@@ -884,7 +884,7 @@ static void ni_sync_ai_dma(struct comedi_device *dev)
static void mite_handle_b_linkc(struct mite_struct *mite,
struct comedi_device *dev)
{
- struct comedi_subdevice *s = dev->subdevices + NI_AO_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_AO_SUBDEV];
unsigned long flags;
spin_lock_irqsave(&devpriv->mite_channel_lock, flags);
@@ -942,7 +942,7 @@ static void ni_handle_eos(struct comedi_device *dev, struct comedi_subdevice *s)
static void shutdown_ai_command(struct comedi_device *dev)
{
- struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_AI_SUBDEV];
#ifdef PCIDMA
ni_ai_drain_dma(dev);
@@ -984,8 +984,9 @@ static void handle_gpct_interrupt(struct comedi_device *dev,
unsigned short counter_index)
{
#ifdef PCIDMA
- struct comedi_subdevice *s =
- dev->subdevices + NI_GPCT_SUBDEV(counter_index);
+ struct comedi_subdevice *s;
+
+ s = &dev->subdevices[NI_GPCT_SUBDEV(counter_index)];
ni_tio_handle_interrupt(&devpriv->counter_dev->counters[counter_index],
s);
@@ -1018,7 +1019,7 @@ static void ack_a_interrupt(struct comedi_device *dev, unsigned short a_status)
static void handle_a_interrupt(struct comedi_device *dev, unsigned short status,
unsigned ai_mite_status)
{
- struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_AI_SUBDEV];
/* 67xx boards don't have ai subdevice, but their gpct0 might generate an a interrupt */
if (s->type == COMEDI_SUBD_UNUSED)
@@ -1150,7 +1151,7 @@ static void ack_b_interrupt(struct comedi_device *dev, unsigned short b_status)
static void handle_b_interrupt(struct comedi_device *dev,
unsigned short b_status, unsigned ao_mite_status)
{
- struct comedi_subdevice *s = dev->subdevices + NI_AO_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_AO_SUBDEV];
/* unsigned short ack=0; */
#ifdef DEBUG_INTERRUPT
printk("ni_mio_common: interrupt: b_status=%04x m1_status=%08x\n",
@@ -1422,7 +1423,7 @@ static void ni_ai_fifo_read(struct comedi_device *dev,
static void ni_handle_fifo_half_full(struct comedi_device *dev)
{
int n;
- struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_AI_SUBDEV];
n = boardtype.ai_fifo_depth / 2;
@@ -1470,7 +1471,7 @@ static int ni_ai_drain_dma(struct comedi_device *dev)
*/
static void ni_handle_fifo_dregs(struct comedi_device *dev)
{
- struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_AI_SUBDEV];
short data[2];
u32 dl;
short fifo_empty;
@@ -1534,7 +1535,7 @@ static void ni_handle_fifo_dregs(struct comedi_device *dev)
static void get_last_sample_611x(struct comedi_device *dev)
{
- struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_AI_SUBDEV];
short data;
u32 dl;
@@ -1551,7 +1552,7 @@ static void get_last_sample_611x(struct comedi_device *dev)
static void get_last_sample_6143(struct comedi_device *dev)
{
- struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_AI_SUBDEV];
short data;
u32 dl;
@@ -1598,7 +1599,7 @@ static void ni_ai_munge(struct comedi_device *dev, struct comedi_subdevice *s,
static int ni_ai_setup_MITE_dma(struct comedi_device *dev)
{
- struct comedi_subdevice *s = dev->subdevices + NI_AI_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_AI_SUBDEV];
int retval;
unsigned long flags;
@@ -1637,7 +1638,7 @@ static int ni_ai_setup_MITE_dma(struct comedi_device *dev)
static int ni_ao_setup_MITE_dma(struct comedi_device *dev)
{
- struct comedi_subdevice *s = dev->subdevices + NI_AO_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_AO_SUBDEV];
int retval;
unsigned long flags;
@@ -1765,20 +1766,18 @@ static int ni_ai_reset(struct comedi_device *dev, struct comedi_subdevice *s)
static int ni_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
{
- unsigned long flags = 0;
+ unsigned long flags;
int count;
/* lock to avoid race with interrupt handler */
- if (in_interrupt() == 0)
- spin_lock_irqsave(&dev->spinlock, flags);
+ spin_lock_irqsave(&dev->spinlock, flags);
#ifndef PCIDMA
ni_handle_fifo_dregs(dev);
#else
ni_sync_ai_dma(dev);
#endif
count = s->async->buf_write_count - s->async->buf_read_count;
- if (in_interrupt() == 0)
- spin_unlock_irqrestore(&dev->spinlock, flags);
+ spin_unlock_irqrestore(&dev->spinlock, flags);
return count;
}
@@ -1880,7 +1879,7 @@ static int ni_ai_insn_read(struct comedi_device *dev,
return insn->n;
}
-void ni_prime_channelgain_list(struct comedi_device *dev)
+static void ni_prime_channelgain_list(struct comedi_device *dev)
{
int i;
devpriv->stc_writew(dev, AI_CONVERT_Pulse, AI_Command_1_Register);
@@ -2165,61 +2164,38 @@ static int ni_ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
{
int err = 0;
int tmp;
- int sources;
+ unsigned int sources;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- if ((cmd->flags & CMDF_WRITE)) {
+ if ((cmd->flags & CMDF_WRITE))
cmd->flags &= ~CMDF_WRITE;
- }
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_INT | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src,
+ TRIG_NOW | TRIG_INT | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
- tmp = cmd->convert_src;
sources = TRIG_TIMER | TRIG_EXT;
- if ((boardtype.reg_type == ni_reg_611x)
- || (boardtype.reg_type == ni_reg_6143))
+ if (boardtype.reg_type == ni_reg_611x ||
+ boardtype.reg_type == ni_reg_6143)
sources |= TRIG_NOW;
- cmd->convert_src &= sources;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->convert_src, sources);
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->start_src != TRIG_NOW &&
- cmd->start_src != TRIG_INT && cmd->start_src != TRIG_EXT)
- err++;
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT &&
- cmd->scan_begin_src != TRIG_OTHER)
- err++;
- if (cmd->convert_src != TRIG_TIMER &&
- cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -3357,44 +3333,28 @@ static int ni_ao_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
int err = 0;
int tmp;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- if ((cmd->flags & CMDF_WRITE) == 0) {
+ if ((cmd->flags & CMDF_WRITE) == 0)
cmd->flags |= CMDF_WRITE;
- }
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_INT | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -3644,51 +3604,21 @@ static int ni_cdio_cmdtest(struct comedi_device *dev,
{
int err = 0;
int tmp;
- int sources;
unsigned i;
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- sources = TRIG_INT;
- cmd->start_src &= sources;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_INT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique... */
-
- if (cmd->start_src != TRIG_INT)
- err++;
- if (cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->convert_src != TRIG_NOW)
- err++;
- if (cmd->stop_src != TRIG_NONE)
- err++;
- /* ... and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -3852,7 +3782,7 @@ static int ni_cdio_cancel(struct comedi_device *dev, struct comedi_subdevice *s)
static void handle_cdio_interrupt(struct comedi_device *dev)
{
unsigned cdio_status;
- struct comedi_subdevice *s = dev->subdevices + NI_DIO_SUBDEV;
+ struct comedi_subdevice *s = &dev->subdevices[NI_DIO_SUBDEV];
#ifdef PCIDMA
unsigned long flags;
#endif
@@ -4101,13 +4031,17 @@ static int ni_serial_sw_readwrite8(struct comedi_device *dev,
static void mio_common_detach(struct comedi_device *dev)
{
+ struct comedi_subdevice *s;
+
if (dev->private) {
if (devpriv->counter_dev) {
ni_gpct_device_destroy(devpriv->counter_dev);
}
}
- if (dev->subdevices && boardtype.has_8255)
- subdev_8255_cleanup(dev, dev->subdevices + NI_8255_DIO_SUBDEV);
+ if (dev->subdevices && boardtype.has_8255) {
+ s = &dev->subdevices[NI_8255_DIO_SUBDEV];
+ subdev_8255_cleanup(dev, s);
+ }
}
static void init_ao_67xx(struct comedi_device *dev, struct comedi_subdevice *s)
@@ -4399,7 +4333,7 @@ static int ni_alloc_private(struct comedi_device *dev)
return 0;
};
-static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
+static int ni_E_init(struct comedi_device *dev)
{
struct comedi_subdevice *s;
unsigned j;
@@ -4417,7 +4351,7 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
/* analog input subdevice */
- s = dev->subdevices + NI_AI_SUBDEV;
+ s = &dev->subdevices[NI_AI_SUBDEV];
dev->read_subdev = s;
if (boardtype.n_adchan) {
s->type = COMEDI_SUBD_AI;
@@ -4449,7 +4383,7 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
/* analog output subdevice */
- s = dev->subdevices + NI_AO_SUBDEV;
+ s = &dev->subdevices[NI_AO_SUBDEV];
if (boardtype.n_aochan) {
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE | SDF_DEGLITCH | SDF_GROUND;
@@ -4488,7 +4422,7 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
/* digital i/o subdevice */
- s = dev->subdevices + NI_DIO_SUBDEV;
+ s = &dev->subdevices[NI_DIO_SUBDEV];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
s->maxdata = 1;
@@ -4516,7 +4450,7 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
}
/* 8255 device */
- s = dev->subdevices + NI_8255_DIO_SUBDEV;
+ s = &dev->subdevices[NI_8255_DIO_SUBDEV];
if (boardtype.has_8255) {
subdev_8255_init(dev, s, ni_8255_callback, (unsigned long)dev);
} else {
@@ -4524,11 +4458,11 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
}
/* formerly general purpose counter/timer device, but no longer used */
- s = dev->subdevices + NI_UNUSED_SUBDEV;
+ s = &dev->subdevices[NI_UNUSED_SUBDEV];
s->type = COMEDI_SUBD_UNUSED;
/* calibration subdevice -- ai and ao */
- s = dev->subdevices + NI_CALIBRATION_SUBDEV;
+ s = &dev->subdevices[NI_CALIBRATION_SUBDEV];
s->type = COMEDI_SUBD_CALIB;
if (boardtype.reg_type & ni_reg_m_series_mask) {
/* internal PWM analog output used for AI nonlinearity calibration */
@@ -4551,7 +4485,7 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
}
/* EEPROM */
- s = dev->subdevices + NI_EEPROM_SUBDEV;
+ s = &dev->subdevices[NI_EEPROM_SUBDEV];
s->type = COMEDI_SUBD_MEMORY;
s->subdev_flags = SDF_READABLE | SDF_INTERNAL;
s->maxdata = 0xff;
@@ -4564,7 +4498,7 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
}
/* PFI */
- s = dev->subdevices + NI_PFI_DIO_SUBDEV;
+ s = &dev->subdevices[NI_PFI_DIO_SUBDEV];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
if (boardtype.reg_type & ni_reg_m_series_mask) {
@@ -4586,7 +4520,7 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
ni_set_bits(dev, IO_Bidirection_Pin_Register, ~0, 0);
/* cs5529 calibration adc */
- s = dev->subdevices + NI_CS5529_CALIBRATION_SUBDEV;
+ s = &dev->subdevices[NI_CS5529_CALIBRATION_SUBDEV];
if (boardtype.reg_type & ni_reg_67xx_mask) {
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_INTERNAL;
@@ -4602,7 +4536,7 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
}
/* Serial */
- s = dev->subdevices + NI_SERIAL_SUBDEV;
+ s = &dev->subdevices[NI_SERIAL_SUBDEV];
s->type = COMEDI_SUBD_SERIAL;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
s->n_chan = 1;
@@ -4612,7 +4546,7 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
devpriv->serial_hw_mode = 0;
/* RTSI */
- s = dev->subdevices + NI_RTSI_SUBDEV;
+ s = &dev->subdevices[NI_RTSI_SUBDEV];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_INTERNAL;
s->n_chan = 8;
@@ -4633,7 +4567,7 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
NUM_GPCT);
/* General purpose counters */
for (j = 0; j < NUM_GPCT; ++j) {
- s = dev->subdevices + NI_GPCT_SUBDEV(j);
+ s = &dev->subdevices[NI_GPCT_SUBDEV(j)];
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags =
SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL | SDF_CMD_READ
@@ -4659,7 +4593,7 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
}
/* Frequency output */
- s = dev->subdevices + NI_FREQ_OUT_SUBDEV;
+ s = &dev->subdevices[NI_FREQ_OUT_SUBDEV];
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 1;
@@ -4669,7 +4603,8 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_config = &ni_freq_out_insn_config;
/* ai configuration */
- ni_ai_reset(dev, dev->subdevices + NI_AI_SUBDEV);
+ s = &dev->subdevices[NI_AI_SUBDEV];
+ ni_ai_reset(dev, s);
if ((boardtype.reg_type & ni_reg_6xxx_mask) == 0) {
/* BEAM is this needed for PCI-6143 ?? */
devpriv->clock_and_fout =
@@ -4688,7 +4623,8 @@ static int ni_E_init(struct comedi_device *dev, struct comedi_devconfig *it)
Clock_and_FOUT_Register);
/* analog output configuration */
- ni_ao_reset(dev, dev->subdevices + NI_AO_SUBDEV);
+ s = &dev->subdevices[NI_AO_SUBDEV];
+ ni_ao_reset(dev, s);
if (dev->irq) {
devpriv->stc_writew(dev,
diff --git a/drivers/staging/comedi/drivers/ni_mio_cs.c b/drivers/staging/comedi/drivers/ni_mio_cs.c
index b85765d266c2..ca4f8e06e75b 100644
--- a/drivers/staging/comedi/drivers/ni_mio_cs.c
+++ b/drivers/staging/comedi/drivers/ni_mio_cs.c
@@ -382,7 +382,7 @@ static int mio_cs_attach(struct comedi_device *dev, struct comedi_devconfig *it)
devpriv->stc_writel = &win_out2;
devpriv->stc_readl = &win_in2;
- ret = ni_E_init(dev, it);
+ ret = ni_E_init(dev);
if (ret < 0)
return ret;
@@ -421,7 +421,7 @@ MODULE_AUTHOR("David A. Schleef <ds@schleef.org>");
MODULE_DESCRIPTION("Comedi driver for National Instruments DAQCard E series");
MODULE_LICENSE("GPL");
-struct pcmcia_driver ni_mio_cs_driver = {
+static struct pcmcia_driver ni_mio_cs_driver = {
.probe = &cs_attach,
.remove = &cs_detach,
.suspend = &mio_cs_suspend,
diff --git a/drivers/staging/comedi/drivers/ni_pcidio.c b/drivers/staging/comedi/drivers/ni_pcidio.c
index 0a55de968039..bc9313ec985c 100644
--- a/drivers/staging/comedi/drivers/ni_pcidio.c
+++ b/drivers/staging/comedi/drivers/ni_pcidio.c
@@ -1,8 +1,6 @@
/*
comedi/drivers/ni_pcidio.c
- driver for National Instruments PCI-DIO-96/PCI-6508
- National Instruments PCI-DIO-32HS
- National Instruments PCI-6503
+ driver for National Instruments PCI-DIO-32HS
COMEDI - Linux Control and Measurement Device Interface
Copyright (C) 1999,2002 David A. Schleef <ds@schleef.org>
@@ -24,17 +22,14 @@
*/
/*
Driver: ni_pcidio
-Description: National Instruments PCI-DIO32HS, PCI-DIO96, PCI-6533, PCI-6503
+Description: National Instruments PCI-DIO32HS, PCI-6533
Author: ds
Status: works
-Devices: [National Instruments] PCI-DIO-32HS (ni_pcidio), PXI-6533,
- PCI-DIO-96, PCI-DIO-96B, PXI-6508, PCI-6503, PCI-6503B, PCI-6503X,
- PXI-6503, PCI-6533, PCI-6534
+Devices: [National Instruments] PCI-DIO-32HS (ni_pcidio)
+ [National Instruments] PXI-6533, PCI-6533 (pxi-6533)
+ [National Instruments] PCI-6534 (pci-6534)
Updated: Mon, 09 Jan 2012 14:27:23 +0000
-The DIO-96 appears as four 8255 subdevices. See the 8255
-driver notes for details.
-
The DIO32HS board appears as one subdevice, with 32 channels.
Each channel is individually I/O configurable. The channel order
is 0=A0, 1=A1, 2=A2, ... 8=B0, 16=C0, 24=D0. The driver only
@@ -56,49 +51,28 @@ it are contained in the
comedi_nonfree_firmware tarball available from http://www.comedi.org
*/
-/*
- This driver is for both the NI PCI-DIO-32HS and the PCI-DIO-96,
- which have very different architectures. But, since the '96 is
- so simple, it is included here.
-
- Manuals (available from ftp://ftp.natinst.com/support/manuals)
-
- 320938c.pdf PCI-DIO-96/PXI-6508/PCI-6503 User Manual
- 321464b.pdf AT/PCI-DIO-32HS User Manual
- 341329A.pdf PCI-6533 Register-Level Programmer Manual
- 341330A.pdf DAQ-DIO Technical Reference Manual
-
- */
-
#define USE_DMA
/* #define DEBUG 1 */
/* #define DEBUG_FLAGS */
#include <linux/interrupt.h>
#include <linux/sched.h>
+#include <linux/firmware.h>
#include "../comedidev.h"
+#include "comedi_fc.h"
#include "mite.h"
-#include "8255.h"
#undef DPRINTK
#ifdef DEBUG
-#define DPRINTK(format, args...) printk(format, ## args)
+#define DPRINTK(format, args...) pr_debug(format, ## args)
#else
-#define DPRINTK(format, args...)
+#define DPRINTK(format, args...) do { } while (0)
#endif
#define PCI_DIO_SIZE 4096
#define PCI_MITE_SIZE 4096
-/* defines for the PCI-DIO-96 */
-
-#define NIDIO_8255_BASE(x) ((x)*4)
-#define NIDIO_A 0
-#define NIDIO_B 4
-#define NIDIO_C 8
-#define NIDIO_D 12
-
/* defines for the PCI-DIO-32HS */
#define Window_Address 4 /* W */
@@ -258,6 +232,14 @@ static inline unsigned secondary_DMAChannel_bits(unsigned channel)
#define Protocol_Register_8 88 /* 32 bit */
#define StartDelay Protocol_Register_8
+/* Firmware files for PCI-6524 */
+#define FW_PCI_6534_MAIN "ni6534a.bin"
+#define FW_PCI_6534_SCARAB_DI "niscrb01.bin"
+#define FW_PCI_6534_SCARAB_DO "niscrb02.bin"
+MODULE_FIRMWARE(FW_PCI_6534_MAIN);
+MODULE_FIRMWARE(FW_PCI_6534_SCARAB_DI);
+MODULE_FIRMWARE(FW_PCI_6534_SCARAB_DO);
+
enum pci_6534_firmware_registers { /* 16 bit */
Firmware_Control_Register = 0x100,
Firmware_Status_Register = 0x104,
@@ -297,76 +279,23 @@ static int ni_pcidio_cancel(struct comedi_device *dev,
struct comedi_subdevice *s);
struct nidio_board {
-
int dev_id;
const char *name;
- int n_8255;
- unsigned int is_diodaq:1;
unsigned int uses_firmware:1;
};
static const struct nidio_board nidio_boards[] = {
{
- .dev_id = 0x1150,
- .name = "pci-dio-32hs",
- .n_8255 = 0,
- .is_diodaq = 1,
- },
- {
- .dev_id = 0x1320,
- .name = "pxi-6533",
- .n_8255 = 0,
- .is_diodaq = 1,
- },
- {
- .dev_id = 0x12b0,
- .name = "pci-6534",
- .n_8255 = 0,
- .is_diodaq = 1,
- .uses_firmware = 1,
- },
- {
- .dev_id = 0x0160,
- .name = "pci-dio-96",
- .n_8255 = 4,
- .is_diodaq = 0,
- },
- {
- .dev_id = 0x1630,
- .name = "pci-dio-96b",
- .n_8255 = 4,
- .is_diodaq = 0,
- },
- {
- .dev_id = 0x13c0,
- .name = "pxi-6508",
- .n_8255 = 4,
- .is_diodaq = 0,
- },
- {
- .dev_id = 0x0400,
- .name = "pci-6503",
- .n_8255 = 1,
- .is_diodaq = 0,
- },
- {
- .dev_id = 0x1250,
- .name = "pci-6503b",
- .n_8255 = 1,
- .is_diodaq = 0,
- },
- {
- .dev_id = 0x17d0,
- .name = "pci-6503x",
- .n_8255 = 1,
- .is_diodaq = 0,
- },
- {
- .dev_id = 0x1800,
- .name = "pxi-6503",
- .n_8255 = 1,
- .is_diodaq = 0,
- },
+ .dev_id = 0x1150,
+ .name = "pci-dio-32hs",
+ }, {
+ .dev_id = 0x1320,
+ .name = "pxi-6533",
+ }, {
+ .dev_id = 0x12b0,
+ .name = "pci-6534",
+ .uses_firmware = 1,
+ },
};
#define n_nidio_boards ARRAY_SIZE(nidio_boards)
@@ -442,17 +371,8 @@ static void ni_pcidio_release_di_mite_channel(struct comedi_device *dev)
spin_unlock_irqrestore(&devpriv->mite_channel_lock, flags);
}
-static int nidio96_8255_cb(int dir, int port, int data, unsigned long iobase)
-{
- if (dir) {
- writeb(data, (void *)(iobase + port));
- return 0;
- } else {
- return readb((void *)(iobase + port));
- }
-}
-
-void ni_pcidio_event(struct comedi_device *dev, struct comedi_subdevice *s)
+static void ni_pcidio_event(struct comedi_device *dev,
+ struct comedi_subdevice *s)
{
if (s->
async->events & (COMEDI_CB_EOA | COMEDI_CB_ERROR |
@@ -480,7 +400,7 @@ static int ni_pcidio_poll(struct comedi_device *dev, struct comedi_subdevice *s)
static irqreturn_t nidio_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices;
+ struct comedi_subdevice *s = &dev->subdevices[0];
struct comedi_async *async = s->async;
struct mite_struct *mite = devpriv->mite;
@@ -511,18 +431,12 @@ static irqreturn_t nidio_interrupt(int irq, void *d)
ni_pcidio_print_flags(flags);
ni_pcidio_print_status(status);
- /* printk("buf[0]=%08x\n",*(unsigned int *)async->prealloc_buf); */
- /* printk("buf[4096]=%08x\n",
- *(unsigned int *)(async->prealloc_buf+4096)); */
-
spin_lock(&devpriv->mite_channel_lock);
if (devpriv->di_mite_chan)
m_status = mite_get_status(devpriv->di_mite_chan);
#ifdef MITE_DEBUG
mite_print_chsr(m_status);
#endif
- /* printk("mite_bytes_transferred: %d\n",
- mite_bytes_transferred(mite,DI_DMA_CHAN)); */
/* mite_dump_regs(mite); */
if (m_status & CHSR_INT) {
@@ -617,7 +531,7 @@ static irqreturn_t nidio_interrupt(int irq, void *d)
}
#if 0
else {
- printk("ni_pcidio: unknown interrupt\n");
+ DPRINTK("ni_pcidio: unknown interrupt\n");
async->events |= COMEDI_CB_ERROR | COMEDI_CB_EOA;
writeb(0x00,
devpriv->mite->daq_io_addr +
@@ -648,38 +562,47 @@ out:
}
#ifdef DEBUG_FLAGS
+static const char *bit_set_string(unsigned int bits, unsigned int bit,
+ const char *const strings[])
+{
+ return (bits & (1U << bit)) ? strings[bit] : "";
+}
+
static const char *const flags_strings[] = {
- "TransferReady", "CountExpired", "2", "3",
- "4", "Waited", "PrimaryTC", "SecondaryTC",
+ " TransferReady", " CountExpired", " 2", " 3",
+ " 4", " Waited", " PrimaryTC", " SecondaryTC",
};
+
static void ni_pcidio_print_flags(unsigned int flags)
{
- int i;
-
- printk(KERN_INFO "group_1_flags:");
- for (i = 7; i >= 0; i--) {
- if (flags & (1 << i))
- printk(" %s", flags_strings[i]);
- }
- printk("\n");
+ pr_debug("group_1_flags:%s%s%s%s%s%s%s%s\n",
+ bit_set_string(flags, 7, flags_strings),
+ bit_set_string(flags, 6, flags_strings),
+ bit_set_string(flags, 5, flags_strings),
+ bit_set_string(flags, 4, flags_strings),
+ bit_set_string(flags, 3, flags_strings),
+ bit_set_string(flags, 2, flags_strings),
+ bit_set_string(flags, 1, flags_strings),
+ bit_set_string(flags, 0, flags_strings));
}
-static char *status_strings[] = {
- "DataLeft1", "Reserved1", "Req1", "StopTrig1",
- "DataLeft2", "Reserved2", "Req2", "StopTrig2",
+static const char *const status_strings[] = {
+ " DataLeft1", " Reserved1", " Req1", " StopTrig1",
+ " DataLeft2", " Reserved2", " Req2", " StopTrig2",
};
static void ni_pcidio_print_status(unsigned int flags)
{
- int i;
-
- printk(KERN_INFO "group_status:");
- for (i = 7; i >= 0; i--) {
- if (flags & (1 << i))
- printk(" %s", status_strings[i]);
- }
- printk("\n");
+ pr_debug("group_status:%s%s%s%s%s%s%s%s\n",
+ bit_set_string(flags, 7, status_strings),
+ bit_set_string(flags, 6, status_strings),
+ bit_set_string(flags, 5, status_strings),
+ bit_set_string(flags, 4, status_strings),
+ bit_set_string(flags, 3, status_strings),
+ bit_set_string(flags, 2, status_strings),
+ bit_set_string(flags, 1, status_strings),
+ bit_set_string(flags, 0, status_strings));
}
#endif
@@ -761,45 +684,25 @@ static int ni_pcidio_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_INT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually
- compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->start_src != TRIG_NOW && cmd->start_src != TRIG_INT)
- err++;
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1065,10 +968,12 @@ static int ni_pcidio_change(struct comedi_device *dev,
}
static int pci_6534_load_fpga(struct comedi_device *dev, int fpga_index,
- u8 *data, int data_len)
+ const u8 *data, size_t data_len)
{
static const int timeout = 1000;
- int i, j;
+ int i;
+ size_t j;
+
writew(0x80 | fpga_index,
devpriv->mite->daq_io_addr + Firmware_Control_Register);
writew(0xc0 | fpga_index,
@@ -1079,8 +984,9 @@ static int pci_6534_load_fpga(struct comedi_device *dev, int fpga_index,
udelay(1);
}
if (i == timeout) {
- printk(KERN_WARNING "ni_pcidio: failed to load fpga %i, "
- "waiting for status 0x2\n", fpga_index);
+ dev_warn(dev->class_dev,
+ "ni_pcidio: failed to load fpga %i, waiting for status 0x2\n",
+ fpga_index);
return -EIO;
}
writew(0x80 | fpga_index,
@@ -1091,8 +997,9 @@ static int pci_6534_load_fpga(struct comedi_device *dev, int fpga_index,
udelay(1);
}
if (i == timeout) {
- printk(KERN_WARNING "ni_pcidio: failed to load fpga %i, "
- "waiting for status 0x3\n", fpga_index);
+ dev_warn(dev->class_dev,
+ "ni_pcidio: failed to load fpga %i, waiting for status 0x3\n",
+ fpga_index);
return -EIO;
}
for (j = 0; j + 1 < data_len;) {
@@ -1107,8 +1014,9 @@ static int pci_6534_load_fpga(struct comedi_device *dev, int fpga_index,
udelay(1);
}
if (i == timeout) {
- printk("ni_pcidio: failed to load word into fpga %i\n",
- fpga_index);
+ dev_warn(dev->class_dev,
+ "ni_pcidio: failed to load word into fpga %i\n",
+ fpga_index);
return -EIO;
}
if (need_resched())
@@ -1147,85 +1055,72 @@ static void pci_6534_init_main_fpga(struct comedi_device *dev)
writel(0, devpriv->mite->daq_io_addr + FPGA_SCBMS_Counter_Register);
}
-static int pci_6534_upload_firmware(struct comedi_device *dev, int options[])
+static int pci_6534_upload_firmware(struct comedi_device *dev)
{
int ret;
- void *main_fpga_data, *scarab_a_data, *scarab_b_data;
- int main_fpga_data_len, scarab_a_data_len, scarab_b_data_len;
+ const struct firmware *fw;
+ static const char *const fw_file[3] = {
+ FW_PCI_6534_SCARAB_DI, /* loaded into scarab A for DI */
+ FW_PCI_6534_SCARAB_DO, /* loaded into scarab B for DO */
+ FW_PCI_6534_MAIN, /* loaded into main FPGA */
+ };
+ int n;
- if (options[COMEDI_DEVCONF_AUX_DATA_LENGTH] == 0)
- return 0;
ret = pci_6534_reset_fpgas(dev);
if (ret < 0)
return ret;
- main_fpga_data = comedi_aux_data(options, 0);
- main_fpga_data_len = options[COMEDI_DEVCONF_AUX_DATA0_LENGTH];
- ret = pci_6534_load_fpga(dev, 2, main_fpga_data, main_fpga_data_len);
- if (ret < 0)
- return ret;
- pci_6534_init_main_fpga(dev);
- scarab_a_data = comedi_aux_data(options, 1);
- scarab_a_data_len = options[COMEDI_DEVCONF_AUX_DATA1_LENGTH];
- ret = pci_6534_load_fpga(dev, 0, scarab_a_data, scarab_a_data_len);
- if (ret < 0)
- return ret;
- scarab_b_data = comedi_aux_data(options, 2);
- scarab_b_data_len = options[COMEDI_DEVCONF_AUX_DATA2_LENGTH];
- ret = pci_6534_load_fpga(dev, 1, scarab_b_data, scarab_b_data_len);
- if (ret < 0)
- return ret;
- return 0;
+ /* load main FPGA first, then the two scarabs */
+ for (n = 2; n >= 0; n--) {
+ ret = request_firmware(&fw, fw_file[n],
+ &devpriv->mite->pcidev->dev);
+ if (ret == 0) {
+ ret = pci_6534_load_fpga(dev, n, fw->data, fw->size);
+ if (ret == 0 && n == 2)
+ pci_6534_init_main_fpga(dev);
+ release_firmware(fw);
+ }
+ if (ret < 0)
+ break;
+ }
+ return ret;
}
-static int nidio_find_device(struct comedi_device *dev, int bus, int slot)
+static const struct nidio_board *
+nidio_find_boardinfo(struct pci_dev *pcidev)
{
- struct mite_struct *mite;
- int i;
-
- for (mite = mite_devices; mite; mite = mite->next) {
- if (mite->used)
- continue;
- if (bus || slot) {
- if (bus != mite->pcidev->bus->number ||
- slot != PCI_SLOT(mite->pcidev->devfn))
- continue;
- }
- for (i = 0; i < n_nidio_boards; i++) {
- if (mite_device_id(mite) == nidio_boards[i].dev_id) {
- dev->board_ptr = nidio_boards + i;
- devpriv->mite = mite;
+ unsigned int dev_id = pcidev->device;
+ unsigned int n;
- return 0;
- }
- }
+ for (n = 0; n < ARRAY_SIZE(nidio_boards); n++) {
+ const struct nidio_board *board = &nidio_boards[n];
+ if (board->dev_id == dev_id)
+ return board;
}
- printk(KERN_WARNING "no device found\n");
- mite_list_devices();
- return -EIO;
+ return NULL;
}
-static int nidio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static int __devinit nidio_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
struct comedi_subdevice *s;
- int i;
int ret;
- int n_subdevices;
unsigned int irq;
- printk(KERN_INFO "comedi%d: nidio:", dev->minor);
-
ret = alloc_private(dev, sizeof(struct nidio96_private));
if (ret < 0)
return ret;
spin_lock_init(&devpriv->mite_channel_lock);
- ret = nidio_find_device(dev, it->options[0], it->options[1]);
- if (ret < 0)
- return ret;
+ dev->board_ptr = nidio_find_boardinfo(pcidev);
+ if (!dev->board_ptr)
+ return -ENODEV;
+ devpriv->mite = mite_alloc(pcidev);
+ if (!devpriv->mite)
+ return -ENOMEM;
ret = mite_setup(devpriv->mite);
if (ret < 0) {
- printk(KERN_WARNING "error setting up mite\n");
+ dev_warn(dev->class_dev, "error setting up mite\n");
return ret;
}
comedi_set_hw_dev(dev, &devpriv->mite->pcidev->dev);
@@ -1235,84 +1130,60 @@ static int nidio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
dev->board_name = this_board->name;
irq = mite_irq(devpriv->mite);
- printk(KERN_INFO " %s", dev->board_name);
if (this_board->uses_firmware) {
- ret = pci_6534_upload_firmware(dev, it->options);
+ ret = pci_6534_upload_firmware(dev);
if (ret < 0)
return ret;
}
- if (!this_board->is_diodaq)
- n_subdevices = this_board->n_8255;
- else
- n_subdevices = 1;
- ret = comedi_alloc_subdevices(dev, n_subdevices);
+ ret = comedi_alloc_subdevices(dev, 1);
if (ret)
return ret;
- if (!this_board->is_diodaq) {
- for (i = 0; i < this_board->n_8255; i++) {
- subdev_8255_init(dev, dev->subdevices + i,
- nidio96_8255_cb,
- (unsigned long)(devpriv->mite->
- daq_io_addr +
- NIDIO_8255_BASE(i)));
- }
- } else {
-
- printk(KERN_INFO " rev=%d",
- readb(devpriv->mite->daq_io_addr + Chip_Version));
-
- s = dev->subdevices + 0;
-
- dev->read_subdev = s;
- s->type = COMEDI_SUBD_DIO;
- s->subdev_flags =
- SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL | SDF_PACKED |
- SDF_CMD_READ;
- s->n_chan = 32;
- s->range_table = &range_digital;
- s->maxdata = 1;
- s->insn_config = &ni_pcidio_insn_config;
- s->insn_bits = &ni_pcidio_insn_bits;
- s->do_cmd = &ni_pcidio_cmd;
- s->do_cmdtest = &ni_pcidio_cmdtest;
- s->cancel = &ni_pcidio_cancel;
- s->len_chanlist = 32; /* XXX */
- s->buf_change = &ni_pcidio_change;
- s->async_dma_dir = DMA_BIDIRECTIONAL;
- s->poll = &ni_pcidio_poll;
-
- writel(0, devpriv->mite->daq_io_addr + Port_IO(0));
- writel(0, devpriv->mite->daq_io_addr + Port_Pin_Directions(0));
- writel(0, devpriv->mite->daq_io_addr + Port_Pin_Mask(0));
-
- /* disable interrupts on board */
- writeb(0x00,
- devpriv->mite->daq_io_addr +
- Master_DMA_And_Interrupt_Control);
-
- ret = request_irq(irq, nidio_interrupt, IRQF_SHARED,
- "ni_pcidio", dev);
- if (ret < 0)
- printk(KERN_WARNING " irq not available");
+ dev_info(dev->class_dev, "%s rev=%d\n", dev->board_name,
+ readb(devpriv->mite->daq_io_addr + Chip_Version));
+
+ s = &dev->subdevices[0];
+
+ dev->read_subdev = s;
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags =
+ SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL | SDF_PACKED |
+ SDF_CMD_READ;
+ s->n_chan = 32;
+ s->range_table = &range_digital;
+ s->maxdata = 1;
+ s->insn_config = &ni_pcidio_insn_config;
+ s->insn_bits = &ni_pcidio_insn_bits;
+ s->do_cmd = &ni_pcidio_cmd;
+ s->do_cmdtest = &ni_pcidio_cmdtest;
+ s->cancel = &ni_pcidio_cancel;
+ s->len_chanlist = 32; /* XXX */
+ s->buf_change = &ni_pcidio_change;
+ s->async_dma_dir = DMA_BIDIRECTIONAL;
+ s->poll = &ni_pcidio_poll;
+
+ writel(0, devpriv->mite->daq_io_addr + Port_IO(0));
+ writel(0, devpriv->mite->daq_io_addr + Port_Pin_Directions(0));
+ writel(0, devpriv->mite->daq_io_addr + Port_Pin_Mask(0));
+
+ /* disable interrupts on board */
+ writeb(0x00,
+ devpriv->mite->daq_io_addr +
+ Master_DMA_And_Interrupt_Control);
- dev->irq = irq;
- }
+ ret = request_irq(irq, nidio_interrupt, IRQF_SHARED,
+ "ni_pcidio", dev);
+ if (ret < 0)
+ dev_warn(dev->class_dev, "irq not available\n");
- printk("\n");
+ dev->irq = irq;
return 0;
}
static void nidio_detach(struct comedi_device *dev)
{
- int i;
-
- if (this_board && !this_board->is_diodaq) {
- for (i = 0; i < this_board->n_8255; i++)
- subdev_8255_cleanup(dev, dev->subdevices + i);
- }
if (dev->irq)
free_irq(dev->irq, dev);
if (devpriv) {
@@ -1320,15 +1191,17 @@ static void nidio_detach(struct comedi_device *dev)
mite_free_ring(devpriv->di_mite_ring);
devpriv->di_mite_ring = NULL;
}
- if (devpriv->mite)
+ if (devpriv->mite) {
mite_unsetup(devpriv->mite);
+ mite_free(devpriv->mite);
+ }
}
}
static struct comedi_driver ni_pcidio_driver = {
.driver_name = "ni_pcidio",
.module = THIS_MODULE,
- .attach = nidio_attach,
+ .attach_pci = nidio_attach_pci,
.detach = nidio_detach,
};
@@ -1347,13 +1220,6 @@ static DEFINE_PCI_DEVICE_TABLE(ni_pcidio_pci_table) = {
{ PCI_DEVICE(PCI_VENDOR_ID_NI, 0x1150) },
{ PCI_DEVICE(PCI_VENDOR_ID_NI, 0x1320) },
{ PCI_DEVICE(PCI_VENDOR_ID_NI, 0x12b0) },
- { PCI_DEVICE(PCI_VENDOR_ID_NI, 0x0160) },
- { PCI_DEVICE(PCI_VENDOR_ID_NI, 0x1630) },
- { PCI_DEVICE(PCI_VENDOR_ID_NI, 0x13c0) },
- { PCI_DEVICE(PCI_VENDOR_ID_NI, 0x0400) },
- { PCI_DEVICE(PCI_VENDOR_ID_NI, 0x1250) },
- { PCI_DEVICE(PCI_VENDOR_ID_NI, 0x17d0) },
- { PCI_DEVICE(PCI_VENDOR_ID_NI, 0x1800) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, ni_pcidio_pci_table);
diff --git a/drivers/staging/comedi/drivers/ni_pcimio.c b/drivers/staging/comedi/drivers/ni_pcimio.c
index 89f4d43c6d08..f284a90720ec 100644
--- a/drivers/staging/comedi/drivers/ni_pcimio.c
+++ b/drivers/staging/comedi/drivers/ni_pcimio.c
@@ -1188,8 +1188,6 @@ static const struct ni_board_struct ni_boards[] = {
},
};
-#define n_pcimio_boards ARRAY_SIZE(ni_boards)
-
struct ni_private {
NI_PRIVATE_COMMON};
#define devpriv ((struct ni_private *)dev->private)
@@ -1502,7 +1500,6 @@ static uint32_t m_series_stc_readl(struct comedi_device *dev, int reg)
#include "ni_mio_common.c"
-static int pcimio_find_device(struct comedi_device *dev, int bus, int slot);
static int pcimio_ai_change(struct comedi_device *dev,
struct comedi_subdevice *s, unsigned long new_size);
static int pcimio_ao_change(struct comedi_device *dev,
@@ -1584,24 +1581,45 @@ static void pcimio_detach(struct comedi_device *dev)
mite_free_ring(devpriv->cdo_mite_ring);
mite_free_ring(devpriv->gpct_mite_ring[0]);
mite_free_ring(devpriv->gpct_mite_ring[1]);
- if (devpriv->mite)
+ if (devpriv->mite) {
mite_unsetup(devpriv->mite);
+ mite_free(devpriv->mite);
+ }
}
}
-static int pcimio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static const struct ni_board_struct *
+pcimio_find_boardinfo(struct pci_dev *pcidev)
+{
+ unsigned int device_id = pcidev->device;
+ unsigned int n;
+
+ for (n = 0; n < ARRAY_SIZE(ni_boards); n++) {
+ const struct ni_board_struct *board = &ni_boards[n];
+ if (board->device_id == device_id)
+ return board;
+ }
+ return NULL;
+}
+
+static int __devinit pcimio_attach_pci(struct comedi_device *dev,
+ struct pci_dev *pcidev)
{
int ret;
- dev_info(dev->class_dev, "ni_pcimio: attach\n");
+ dev_info(dev->class_dev, "ni_pcimio: attach %s\n", pci_name(pcidev));
ret = ni_alloc_private(dev);
if (ret < 0)
return ret;
- ret = pcimio_find_device(dev, it->options[0], it->options[1]);
- if (ret < 0)
- return ret;
+ dev->board_ptr = pcimio_find_boardinfo(pcidev);
+ if (!dev->board_ptr)
+ return -ENODEV;
+
+ devpriv->mite = mite_alloc(pcidev);
+ if (!devpriv->mite)
+ return -ENOMEM;
dev_dbg(dev->class_dev, "%s\n", boardtype.name);
dev->board_name = boardtype.name;
@@ -1659,7 +1677,7 @@ static int pcimio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
}
}
- ret = ni_E_init(dev, it);
+ ret = ni_E_init(dev);
if (ret < 0)
return ret;
@@ -1672,34 +1690,6 @@ static int pcimio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return ret;
}
-static int pcimio_find_device(struct comedi_device *dev, int bus, int slot)
-{
- struct mite_struct *mite;
- int i;
-
- for (mite = mite_devices; mite; mite = mite->next) {
- if (mite->used)
- continue;
- if (bus || slot) {
- if (bus != mite->pcidev->bus->number ||
- slot != PCI_SLOT(mite->pcidev->devfn))
- continue;
- }
-
- for (i = 0; i < n_pcimio_boards; i++) {
- if (mite_device_id(mite) == ni_boards[i].device_id) {
- dev->board_ptr = ni_boards + i;
- devpriv->mite = mite;
-
- return 0;
- }
- }
- }
- pr_warn("no device found\n");
- mite_list_devices();
- return -EIO;
-}
-
static int pcimio_ai_change(struct comedi_device *dev,
struct comedi_subdevice *s, unsigned long new_size)
{
@@ -1765,7 +1755,7 @@ static int pcimio_dio_change(struct comedi_device *dev,
static struct comedi_driver ni_pcimio_driver = {
.driver_name = "ni_pcimio",
.module = THIS_MODULE,
- .attach = pcimio_attach,
+ .attach_pci = pcimio_attach_pci,
.detach = pcimio_detach,
};
@@ -1844,7 +1834,7 @@ static struct pci_driver ni_pcimio_pci_driver = {
.probe = ni_pcimio_pci_probe,
.remove = __devexit_p(ni_pcimio_pci_remove)
};
-module_comedi_pci_driver(ni_pcimio_driver, ni_pcimio_pci_driver)
+module_comedi_pci_driver(ni_pcimio_driver, ni_pcimio_pci_driver);
MODULE_AUTHOR("Comedi http://www.comedi.org");
MODULE_DESCRIPTION("Comedi low-level driver");
diff --git a/drivers/staging/comedi/drivers/ni_tiocmd.c b/drivers/staging/comedi/drivers/ni_tiocmd.c
index a9611587460a..8ee93d359bed 100644
--- a/drivers/staging/comedi/drivers/ni_tiocmd.c
+++ b/drivers/staging/comedi/drivers/ni_tiocmd.c
@@ -48,6 +48,7 @@ TODO:
Support use of both banks X and Y
*/
+#include "comedi_fc.h"
#include "ni_tio_internal.h"
#include "mite.h"
@@ -237,61 +238,35 @@ EXPORT_SYMBOL_GPL(ni_tio_cmd);
int ni_tio_cmdtest(struct ni_gpct *counter, struct comedi_cmd *cmd)
{
int err = 0;
- int tmp;
- int sources;
+ unsigned int sources;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
sources = TRIG_NOW | TRIG_INT | TRIG_OTHER;
if (ni_tio_counting_mode_registers_present(counter->counter_dev))
sources |= TRIG_EXT;
- cmd->start_src &= sources;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW | TRIG_EXT | TRIG_OTHER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- sources = TRIG_NOW | TRIG_EXT | TRIG_OTHER;
- cmd->convert_src &= sources;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, sources);
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_FOLLOW | TRIG_EXT | TRIG_OTHER);
+ err |= cfc_check_trigger_src(&cmd->convert_src,
+ TRIG_NOW | TRIG_EXT | TRIG_OTHER);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique... */
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+
+ /* Step 2b : and mutually compatible */
- if (cmd->start_src != TRIG_NOW &&
- cmd->start_src != TRIG_INT &&
- cmd->start_src != TRIG_EXT && cmd->start_src != TRIG_OTHER)
- err++;
- if (cmd->scan_begin_src != TRIG_FOLLOW &&
- cmd->scan_begin_src != TRIG_EXT &&
- cmd->scan_begin_src != TRIG_OTHER)
- err++;
- if (cmd->convert_src != TRIG_OTHER &&
- cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
- err++;
- if (cmd->stop_src != TRIG_NONE)
- err++;
- /* ... and mutually compatible */
if (cmd->convert_src != TRIG_NOW && cmd->scan_begin_src != TRIG_FOLLOW)
- err++;
+ err |= -EINVAL;
if (err)
return 2;
diff --git a/drivers/staging/comedi/drivers/pcl711.c b/drivers/staging/comedi/drivers/pcl711.c
index bb72d0bc2975..89305a14eb5c 100644
--- a/drivers/staging/comedi/drivers/pcl711.c
+++ b/drivers/staging/comedi/drivers/pcl711.c
@@ -64,6 +64,7 @@ supported.
#include <linux/ioport.h>
#include <linux/delay.h>
+#include "comedi_fc.h"
#include "8253.h"
#define PCL711_SIZE 16
@@ -168,7 +169,7 @@ static irqreturn_t pcl711_interrupt(int irq, void *d)
int data;
struct comedi_device *dev = d;
const struct pcl711_board *board = comedi_board(dev);
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
if (!dev->attached) {
comedi_error(dev, "spurious interrupt");
@@ -266,42 +267,24 @@ static int pcl711_ai_cmdtest(struct comedi_device *dev,
int tmp;
int err = 0;
- /* step 1 */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2 */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -520,7 +503,7 @@ static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret < 0)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* AI subdevice */
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
@@ -536,7 +519,7 @@ static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->do_cmd = pcl711_ai_cmd;
}
- s++;
+ s = &dev->subdevices[1];
/* AO subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -547,7 +530,7 @@ static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_write = pcl711_ao_insn;
s->insn_read = pcl711_ao_insn_read;
- s++;
+ s = &dev->subdevices[2];
/* 16-bit digital input */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
@@ -557,7 +540,7 @@ static int pcl711_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->range_table = &range_digital;
s->insn_bits = pcl711_di_insn_bits;
- s++;
+ s = &dev->subdevices[3];
/* 16-bit digital out */
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
diff --git a/drivers/staging/comedi/drivers/pcl724.c b/drivers/staging/comedi/drivers/pcl724.c
index c8fe23ca899d..7b3c4293c01b 100644
--- a/drivers/staging/comedi/drivers/pcl724.c
+++ b/drivers/staging/comedi/drivers/pcl724.c
@@ -99,6 +99,7 @@ static int subdev_8255mapped_cb(int dir, int port, int data,
static int pcl724_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
const struct pcl724_board *board = comedi_board(dev);
+ struct comedi_subdevice *s;
unsigned long iobase;
unsigned int iorange;
int ret, i, n_subdevices;
@@ -161,14 +162,13 @@ static int pcl724_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return ret;
for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
if (board->is_pet48) {
- subdev_8255_init(dev, dev->subdevices + i,
- subdev_8255mapped_cb,
+ subdev_8255_init(dev, s, subdev_8255mapped_cb,
(unsigned long)(dev->iobase +
i * 0x1000));
} else
- subdev_8255_init(dev, dev->subdevices + i,
- subdev_8255_cb,
+ subdev_8255_init(dev, s, subdev_8255_cb,
(unsigned long)(dev->iobase +
SIZE_8255 * i));
}
@@ -179,10 +179,13 @@ static int pcl724_attach(struct comedi_device *dev, struct comedi_devconfig *it)
static void pcl724_detach(struct comedi_device *dev)
{
const struct pcl724_board *board = comedi_board(dev);
+ struct comedi_subdevice *s;
int i;
- for (i = 0; i < dev->n_subdevices; i++)
- subdev_8255_cleanup(dev, dev->subdevices + i);
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ subdev_8255_cleanup(dev, s);
+ }
#ifdef PCL724_IRQ
if (dev->irq)
free_irq(dev->irq, dev);
diff --git a/drivers/staging/comedi/drivers/pcl725.c b/drivers/staging/comedi/drivers/pcl725.c
index d5b60cf7c93f..21fbc1ae1e73 100644
--- a/drivers/staging/comedi/drivers/pcl725.c
+++ b/drivers/staging/comedi/drivers/pcl725.c
@@ -62,7 +62,7 @@ static int pcl725_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* do */
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
@@ -71,7 +71,7 @@ static int pcl725_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_bits = pcl725_do_insn;
s->range_table = &range_digital;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* di */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
diff --git a/drivers/staging/comedi/drivers/pcl726.c b/drivers/staging/comedi/drivers/pcl726.c
index 2b10f1d83085..07e72de982ac 100644
--- a/drivers/staging/comedi/drivers/pcl726.c
+++ b/drivers/staging/comedi/drivers/pcl726.c
@@ -290,7 +290,7 @@ static int pcl726_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* ao */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
@@ -316,7 +316,7 @@ static int pcl726_attach(struct comedi_device *dev, struct comedi_devconfig *it)
devpriv->bipolar[i] = 1; /* bipolar range */
}
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* di */
if (!board->have_dio) {
s->type = COMEDI_SUBD_UNUSED;
@@ -330,7 +330,7 @@ static int pcl726_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->range_table = &range_digital;
}
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* do */
if (!board->have_dio) {
s->type = COMEDI_SUBD_UNUSED;
diff --git a/drivers/staging/comedi/drivers/pcl730.c b/drivers/staging/comedi/drivers/pcl730.c
index 4675ec57082e..e3de49937452 100644
--- a/drivers/staging/comedi/drivers/pcl730.c
+++ b/drivers/staging/comedi/drivers/pcl730.c
@@ -84,7 +84,7 @@ static int pcl730_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* Isolated do */
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
@@ -94,7 +94,7 @@ static int pcl730_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->range_table = &range_digital;
s->private = (void *)PCL730_IDIO_LO;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* Isolated di */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
@@ -104,7 +104,7 @@ static int pcl730_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->range_table = &range_digital;
s->private = (void *)PCL730_IDIO_LO;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* TTL do */
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
@@ -114,7 +114,7 @@ static int pcl730_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->range_table = &range_digital;
s->private = (void *)PCL730_DIO_LO;
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
/* TTL di */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
diff --git a/drivers/staging/comedi/drivers/pcl812.c b/drivers/staging/comedi/drivers/pcl812.c
index 578fd8920be1..3cf55ff93083 100644
--- a/drivers/staging/comedi/drivers/pcl812.c
+++ b/drivers/staging/comedi/drivers/pcl812.c
@@ -117,6 +117,7 @@
#include <linux/io.h>
#include <asm/dma.h>
+#include "comedi_fc.h"
#include "8253.h"
/* hardware types of the cards */
@@ -533,49 +534,31 @@ static int pcl812_ai_cmdtest(struct comedi_device *dev,
{
const struct pcl812_board *board = comedi_board(dev);
int err = 0;
+ unsigned int flags;
int tmp, divisor1, divisor2;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
- tmp = cmd->convert_src;
if (devpriv->use_ext_trg)
- cmd->convert_src &= TRIG_EXT;
+ flags = TRIG_EXT;
else
- cmd->convert_src &= TRIG_TIMER;
-
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
+ flags = TRIG_TIMER;
+ err |= cfc_check_trigger_src(&cmd->convert_src, flags);
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources are
- * unique and mutually compatible
- */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -806,7 +789,7 @@ static irqreturn_t interrupt_pcl812_ai_int(int irq, void *d)
char err = 1;
unsigned int mask, timeout;
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
unsigned int next_chan;
s->async->events = 0;
@@ -909,7 +892,7 @@ static void transfer_from_dma_buf(struct comedi_device *dev,
static irqreturn_t interrupt_pcl812_ai_dma(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
unsigned long dma_flags;
int len, bufptr;
short *ptr;
@@ -1267,7 +1250,7 @@ no_dma:
/* analog input */
if (board->n_aichan > 0) {
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[subdev];
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE;
switch (board->board_type) {
@@ -1409,7 +1392,7 @@ no_dma:
/* analog output */
if (board->n_aochan > 0) {
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[subdev];
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE | SDF_GROUND;
s->n_chan = board->n_aochan;
@@ -1438,7 +1421,7 @@ no_dma:
/* digital input */
if (board->n_dichan > 0) {
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[subdev];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = board->n_dichan;
@@ -1451,7 +1434,7 @@ no_dma:
/* digital output */
if (board->n_dochan > 0) {
- s = dev->subdevices + subdev;
+ s = &dev->subdevices[subdev];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
s->n_chan = board->n_dochan;
diff --git a/drivers/staging/comedi/drivers/pcl816.c b/drivers/staging/comedi/drivers/pcl816.c
index ba6911f063cb..0822de058e4d 100644
--- a/drivers/staging/comedi/drivers/pcl816.c
+++ b/drivers/staging/comedi/drivers/pcl816.c
@@ -41,6 +41,7 @@ Configuration Options:
#include <linux/io.h>
#include <asm/dma.h>
+#include "comedi_fc.h"
#include "8253.h"
#define DEBUG(x) x
@@ -258,7 +259,7 @@ static int pcl816_ai_insn_read(struct comedi_device *dev,
static irqreturn_t interrupt_pcl816_ai_mode13_int(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
int low, hi;
int timeout = 50; /* wait max 50us */
@@ -349,7 +350,7 @@ static void transfer_from_dma_buf(struct comedi_device *dev,
static irqreturn_t interrupt_pcl816_ai_mode13_dma(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
int len, bufptr, this_dma_buf;
unsigned long dma_flags;
short *ptr;
@@ -458,48 +459,23 @@ static int pcl816_ai_cmdtest(struct comedi_device *dev,
pcl816_cmdtest_out(-1, cmd);
);
- /* step 1: make sure trigger sources are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_EXT | TRIG_TIMER;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_EXT | TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
+ /* Step 2a : make sure trigger sources are unique */
- /*
- * step 2: make sure trigger sources
- * are unique and mutually compatible
- */
-
- if (cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_TIMER) {
- cmd->convert_src = TRIG_TIMER;
- err++;
- }
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
- if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
- err++;
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1183,7 +1159,7 @@ no_dma:
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
if (board->n_aichan > 0) {
s->type = COMEDI_SUBD_AI;
devpriv->sub_ai = s;
diff --git a/drivers/staging/comedi/drivers/pcl818.c b/drivers/staging/comedi/drivers/pcl818.c
index 34169c16fb92..d4b0859d81f2 100644
--- a/drivers/staging/comedi/drivers/pcl818.c
+++ b/drivers/staging/comedi/drivers/pcl818.c
@@ -107,6 +107,7 @@ A word or two about DMA. Driver support DMA operations at two ways:
#include <linux/io.h>
#include <asm/dma.h>
+#include "comedi_fc.h"
#include "8253.h"
/* #define PCL818_MODE13_AO 1 */
@@ -477,7 +478,7 @@ static int pcl818_do_insn_bits(struct comedi_device *dev,
static irqreturn_t interrupt_pcl818_ai_mode13_int(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
int low;
int timeout = 50; /* wait max 50us */
@@ -536,7 +537,7 @@ conv_finish:
static irqreturn_t interrupt_pcl818_ai_mode13_dma(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
int i, len, bufptr;
unsigned long flags;
short *ptr;
@@ -615,7 +616,7 @@ static irqreturn_t interrupt_pcl818_ai_mode13_dma(int irq, void *d)
static irqreturn_t interrupt_pcl818_ai_mode13_dma_rtc(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
unsigned long tmp;
unsigned int top1, top2, i, bufptr;
long ofs_dats;
@@ -720,7 +721,7 @@ static irqreturn_t interrupt_pcl818_ai_mode13_dma_rtc(int irq, void *d)
static irqreturn_t interrupt_pcl818_ai_mode13_fifo(int irq, void *d)
{
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
int i, len, lo;
outb(0, dev->iobase + PCL818_FI_INTCLR); /* clear fifo int request */
@@ -811,7 +812,7 @@ static irqreturn_t interrupt_pcl818(int irq, void *d)
being reprogrammed while a DMA transfer is in
progress.
*/
- struct comedi_subdevice *s = dev->subdevices + 0;
+ struct comedi_subdevice *s = &dev->subdevices[0];
devpriv->ai_act_scan = 0;
devpriv->neverending_ai = 0;
pcl818_ai_cancel(dev, s);
@@ -1261,43 +1262,23 @@ static int ai_cmdtest(struct comedi_device *dev, struct comedi_subdevice *s,
int err = 0;
int tmp, divisor1 = 0, divisor2 = 0;
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
- if (cmd->stop_src != TRIG_NONE && cmd->stop_src != TRIG_COUNT)
- err++;
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1763,7 +1744,7 @@ no_dma:
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
if (!board->n_aichan_se) {
s->type = COMEDI_SUBD_UNUSED;
} else {
@@ -1829,7 +1810,7 @@ no_dma:
}
}
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
if (!board->n_aochan) {
s->type = COMEDI_SUBD_UNUSED;
} else {
@@ -1862,7 +1843,7 @@ no_dma:
}
}
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
if (!board->n_dichan) {
s->type = COMEDI_SUBD_UNUSED;
} else {
@@ -1875,7 +1856,7 @@ no_dma:
s->insn_bits = pcl818_di_insn_bits;
}
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
if (!board->n_dochan) {
s->type = COMEDI_SUBD_UNUSED;
} else {
diff --git a/drivers/staging/comedi/drivers/pcm3724.c b/drivers/staging/comedi/drivers/pcm3724.c
index 62c22ccfb780..4102547dc6a8 100644
--- a/drivers/staging/comedi/drivers/pcm3724.c
+++ b/drivers/staging/comedi/drivers/pcm3724.c
@@ -119,6 +119,8 @@ static int compute_buffer(int config, int devno, struct comedi_subdevice *s)
static void do_3724_config(struct comedi_device *dev,
struct comedi_subdevice *s, int chanspec)
{
+ struct comedi_subdevice *s_dio1 = &dev->subdevices[0];
+ struct comedi_subdevice *s_dio2 = &dev->subdevices[1];
int config;
int buffer_config;
unsigned long port_8255_cfg;
@@ -136,10 +138,10 @@ static void do_3724_config(struct comedi_device *dev,
if (!(s->io_bits & 0xff0000))
config |= CR_C_IO;
- buffer_config = compute_buffer(0, 0, dev->subdevices);
- buffer_config = compute_buffer(buffer_config, 1, (dev->subdevices) + 1);
+ buffer_config = compute_buffer(0, 0, s_dio1);
+ buffer_config = compute_buffer(buffer_config, 1, s_dio2);
- if (s == dev->subdevices)
+ if (s == s_dio1)
port_8255_cfg = dev->iobase + _8255_CR;
else
port_8255_cfg = dev->iobase + SIZE_8255 + _8255_CR;
@@ -154,6 +156,7 @@ static void do_3724_config(struct comedi_device *dev,
static void enable_chan(struct comedi_device *dev, struct comedi_subdevice *s,
int chanspec)
{
+ struct comedi_subdevice *s_dio1 = &dev->subdevices[0];
unsigned int mask;
int gatecfg;
struct priv_pcm3724 *priv;
@@ -162,9 +165,9 @@ static void enable_chan(struct comedi_device *dev, struct comedi_subdevice *s,
priv = dev->private;
mask = 1 << CR_CHAN(chanspec);
- if (s == dev->subdevices) /* subdev 0 */
+ if (s == s_dio1)
priv->dio_1 |= mask;
- else /* subdev 1 */
+ else
priv->dio_2 |= mask;
if (priv->dio_1 & 0xff0000)
@@ -231,6 +234,7 @@ static int pcm3724_attach(struct comedi_device *dev,
struct comedi_devconfig *it)
{
const struct pcm3724_board *board = comedi_board(dev);
+ struct comedi_subdevice *s;
unsigned long iobase;
unsigned int iorange;
int ret, i, n_subdevices;
@@ -263,9 +267,10 @@ static int pcm3724_attach(struct comedi_device *dev,
return ret;
for (i = 0; i < dev->n_subdevices; i++) {
- subdev_8255_init(dev, dev->subdevices + i, subdev_8255_cb,
+ s = &dev->subdevices[i];
+ subdev_8255_init(dev, s, subdev_8255_cb,
(unsigned long)(dev->iobase + SIZE_8255 * i));
- ((dev->subdevices) + i)->insn_config = subdev_3724_insn_config;
+ s->insn_config = subdev_3724_insn_config;
}
return 0;
}
@@ -273,11 +278,14 @@ static int pcm3724_attach(struct comedi_device *dev,
static void pcm3724_detach(struct comedi_device *dev)
{
const struct pcm3724_board *board = comedi_board(dev);
+ struct comedi_subdevice *s;
int i;
if (dev->subdevices) {
- for (i = 0; i < dev->n_subdevices; i++)
- subdev_8255_cleanup(dev, dev->subdevices + i);
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
+ subdev_8255_cleanup(dev, s);
+ }
}
if (dev->iobase)
release_region(dev->iobase, board->io_range);
diff --git a/drivers/staging/comedi/drivers/pcm3730.c b/drivers/staging/comedi/drivers/pcm3730.c
index d65e0bda2c44..067f14d22610 100644
--- a/drivers/staging/comedi/drivers/pcm3730.c
+++ b/drivers/staging/comedi/drivers/pcm3730.c
@@ -72,7 +72,7 @@ static int pcm3730_attach(struct comedi_device *dev,
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
s->maxdata = 1;
@@ -81,7 +81,7 @@ static int pcm3730_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->private = (void *)PCM3730_DOA;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
s->maxdata = 1;
@@ -90,7 +90,7 @@ static int pcm3730_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->private = (void *)PCM3730_DOB;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
s->maxdata = 1;
@@ -99,7 +99,7 @@ static int pcm3730_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->private = (void *)PCM3730_DOC;
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->maxdata = 1;
@@ -108,7 +108,7 @@ static int pcm3730_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->private = (void *)PCM3730_DIA;
- s = dev->subdevices + 4;
+ s = &dev->subdevices[4];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->maxdata = 1;
@@ -117,7 +117,7 @@ static int pcm3730_attach(struct comedi_device *dev,
s->range_table = &range_digital;
s->private = (void *)PCM3730_DIB;
- s = dev->subdevices + 5;
+ s = &dev->subdevices[5];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->maxdata = 1;
diff --git a/drivers/staging/comedi/drivers/pcm_common.c b/drivers/staging/comedi/drivers/pcm_common.c
index 474af7bc6c8b..85ee05ece9c8 100644
--- a/drivers/staging/comedi/drivers/pcm_common.c
+++ b/drivers/staging/comedi/drivers/pcm_common.c
@@ -1,60 +1,30 @@
#include "../comedidev.h"
+
+#include "comedi_fc.h"
#include "pcm_common.h"
-/*
- * 'do_cmdtest' function for an 'INTERRUPT' subdevice. This is for
- * the PCM drivers.
- */
int comedi_pcm_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
int err = 0;
- unsigned int tmp;
-
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= (TRIG_NOW | TRIG_INT);
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= (TRIG_COUNT | TRIG_NONE);
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and
- * mutually compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* these tests are true if more than one _src bit is set */
- if ((cmd->start_src & (cmd->start_src - 1)) != 0)
- err++;
- if ((cmd->scan_begin_src & (cmd->scan_begin_src - 1)) != 0)
- err++;
- if ((cmd->convert_src & (cmd->convert_src - 1)) != 0)
- err++;
- if ((cmd->scan_end_src & (cmd->scan_end_src - 1)) != 0)
- err++;
- if ((cmd->stop_src & (cmd->stop_src - 1)) != 0)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
diff --git a/drivers/staging/comedi/drivers/pcmad.c b/drivers/staging/comedi/drivers/pcmad.c
index 54d19c943967..5efeb9205c2e 100644
--- a/drivers/staging/comedi/drivers/pcmad.c
+++ b/drivers/staging/comedi/drivers/pcmad.c
@@ -127,7 +127,7 @@ static int pcmad_attach(struct comedi_device *dev, struct comedi_devconfig *it)
dev->board_name = board->name;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | AREF_GROUND;
s->n_chan = 16; /* XXX */
diff --git a/drivers/staging/comedi/drivers/pcmda12.c b/drivers/staging/comedi/drivers/pcmda12.c
index 291ce7c1bdb1..28af8f6873eb 100644
--- a/drivers/staging/comedi/drivers/pcmda12.c
+++ b/drivers/staging/comedi/drivers/pcmda12.c
@@ -195,7 +195,7 @@ static int pcmda12_attach(struct comedi_device *dev,
if (ret)
return ret;
- s = dev->subdevices;
+ s = &dev->subdevices[0];
s->private = NULL;
s->maxdata = (0x1 << BITS) - 1;
s->range_table = &pcmda12_ranges;
diff --git a/drivers/staging/comedi/drivers/pcmmio.c b/drivers/staging/comedi/drivers/pcmmio.c
index 3d2e6f01c4b7..a10bf0a2987f 100644
--- a/drivers/staging/comedi/drivers/pcmmio.c
+++ b/drivers/staging/comedi/drivers/pcmmio.c
@@ -526,6 +526,7 @@ static irqreturn_t interrupt_pcmmio(int irq, void *d)
{
int asic, got1 = 0;
struct comedi_device *dev = (struct comedi_device *)d;
+ int i;
for (asic = 0; asic < MAX_ASICS; ++asic) {
if (irq == devpriv->asics[asic].irq) {
@@ -583,9 +584,8 @@ static irqreturn_t interrupt_pcmmio(int irq, void *d)
printk
(KERN_DEBUG "got edge detect interrupt %d asic %d which_chans: %06x\n",
irq, asic, triggered);
- for (s = dev->subdevices + 2;
- s < dev->subdevices + dev->n_subdevices;
- ++s) {
+ for (i = 2; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
/*
* this is an interrupt subdev,
* and it matches this asic!
@@ -1076,9 +1076,8 @@ static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return ret;
/* First, AI */
- sdev_no = 0;
- s = dev->subdevices + sdev_no;
- s->private = devpriv->sprivs + sdev_no;
+ s = &dev->subdevices[0];
+ s->private = &devpriv->sprivs[0];
s->maxdata = (1 << board->ai_bits) - 1;
s->range_table = board->ai_range_table;
s->subdev_flags = SDF_READABLE | SDF_GROUND | SDF_DIFF;
@@ -1092,9 +1091,8 @@ static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
outb(0, subpriv->iobase + 4 + 3);
/* Next, AO */
- ++sdev_no;
- s = dev->subdevices + sdev_no;
- s->private = devpriv->sprivs + sdev_no;
+ s = &dev->subdevices[1];
+ s->private = &devpriv->sprivs[1];
s->maxdata = (1 << board->ao_bits) - 1;
s->range_table = board->ao_range_table;
s->subdev_flags = SDF_READABLE;
@@ -1108,14 +1106,13 @@ static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
outb(0, subpriv->iobase + 3);
outb(0, subpriv->iobase + 4 + 3);
- ++sdev_no;
port = 0;
asic = 0;
- for (; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
+ for (sdev_no = 2; sdev_no < dev->n_subdevices; ++sdev_no) {
int byte_no;
- s = dev->subdevices + sdev_no;
- s->private = devpriv->sprivs + sdev_no;
+ s = &dev->subdevices[sdev_no];
+ s->private = &devpriv->sprivs[sdev_no];
s->maxdata = 1;
s->range_table = &range_digital;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
@@ -1200,15 +1197,6 @@ static int pcmmio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
* multiple irqs..
*/
- if (irq[0]) {
- printk(KERN_DEBUG "comedi%d: irq: %u\n", dev->minor, irq[0]);
- if (board->dio_num_asics == 2 && irq[1])
- printk(KERN_DEBUG "comedi%d: second ASIC irq: %u\n",
- dev->minor, irq[1]);
- } else {
- printk(KERN_INFO "comedi%d: (IRQ mode disabled)\n", dev->minor);
- }
-
printk(KERN_INFO "comedi%d: attached\n", dev->minor);
return 1;
diff --git a/drivers/staging/comedi/drivers/pcmuio.c b/drivers/staging/comedi/drivers/pcmuio.c
index feef3d02f35a..0e32119bc3f9 100644
--- a/drivers/staging/comedi/drivers/pcmuio.c
+++ b/drivers/staging/comedi/drivers/pcmuio.c
@@ -442,7 +442,7 @@ static void pcmuio_stop_intr(struct comedi_device *dev,
subpriv->intr.enabled_mask = 0;
subpriv->intr.active = 0;
- s->async->inttrig = 0;
+ s->async->inttrig = NULL;
nports = subpriv->intr.num_asic_chans / CHANS_PER_PORT;
firstport = subpriv->intr.asic_chan / CHANS_PER_PORT;
switch_page(dev, asic, PAGE_ENAB);
@@ -456,6 +456,7 @@ static irqreturn_t interrupt_pcmuio(int irq, void *d)
{
int asic, got1 = 0;
struct comedi_device *dev = (struct comedi_device *)d;
+ int i;
for (asic = 0; asic < MAX_ASICS; ++asic) {
if (irq == devpriv->asics[asic].irq) {
@@ -507,9 +508,8 @@ static irqreturn_t interrupt_pcmuio(int irq, void *d)
printk
("PCMUIO DEBUG: got edge detect interrupt %d asic %d which_chans: %06x\n",
irq, asic, triggered);
- for (s = dev->subdevices;
- s < dev->subdevices + dev->n_subdevices;
- ++s) {
+ for (i = 0; i < dev->n_subdevices; i++) {
+ s = &dev->subdevices[i];
if (subpriv->intr.asic == asic) { /* this is an interrupt subdev, and it matches this asic! */
unsigned long flags;
unsigned oldevents;
@@ -683,7 +683,7 @@ pcmuio_inttrig_start_intr(struct comedi_device *dev, struct comedi_subdevice *s,
return -EINVAL;
spin_lock_irqsave(&subpriv->intr.spinlock, flags);
- s->async->inttrig = 0;
+ s->async->inttrig = NULL;
if (subpriv->intr.active)
event = pcmuio_start_intr(dev, s);
@@ -811,8 +811,8 @@ static int pcmuio_attach(struct comedi_device *dev, struct comedi_devconfig *it)
for (sdev_no = 0; sdev_no < (int)dev->n_subdevices; ++sdev_no) {
int byte_no;
- s = dev->subdevices + sdev_no;
- s->private = devpriv->sprivs + sdev_no;
+ s = &dev->subdevices[sdev_no];
+ s->private = &devpriv->sprivs[sdev_no];
s->maxdata = 1;
s->range_table = &range_digital;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
diff --git a/drivers/staging/comedi/drivers/poc.c b/drivers/staging/comedi/drivers/poc.c
index c253bb9ef335..78dfe167b147 100644
--- a/drivers/staging/comedi/drivers/poc.c
+++ b/drivers/staging/comedi/drivers/poc.c
@@ -164,7 +164,7 @@ static int poc_attach(struct comedi_device *dev, struct comedi_devconfig *it)
return -ENOMEM;
/* analog output subdevice */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = board->type;
s->n_chan = board->n_chan;
s->maxdata = (1 << board->n_bits) - 1;
diff --git a/drivers/staging/comedi/drivers/quatech_daqp_cs.c b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
index a029147c9b69..3e276f7a3380 100644
--- a/drivers/staging/comedi/drivers/quatech_daqp_cs.c
+++ b/drivers/staging/comedi/drivers/quatech_daqp_cs.c
@@ -56,6 +56,8 @@ Devices: [Quatech] DAQP-208 (daqp), DAQP-308
#include <linux/completion.h>
+#include "comedi_fc.h"
+
/* Maximum number of separate DAQP devices we'll allow */
#define MAX_DEV 4
@@ -456,51 +458,26 @@ static int daqp_ai_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources
- * are unique and mutually compatible
- */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_FOLLOW)
- err++;
- if (cmd->convert_src != TRIG_NOW && cmd->convert_src != TRIG_TIMER)
- err++;
- if (cmd->scan_begin_src == TRIG_FOLLOW && cmd->convert_src == TRIG_NOW)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -878,7 +855,7 @@ static int daqp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
printk(KERN_INFO "comedi%d: attaching daqp%d (io 0x%04lx)\n",
dev->minor, it->options[0], dev->iobase);
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
s->private = local;
s->type = COMEDI_SUBD_AI;
@@ -892,7 +869,7 @@ static int daqp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->do_cmd = daqp_ai_cmd;
s->cancel = daqp_ai_cancel;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
dev->write_subdev = s;
s->private = local;
s->type = COMEDI_SUBD_AO;
@@ -903,7 +880,7 @@ static int daqp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->range_table = &range_daqp_ao;
s->insn_write = daqp_ao_insn_write;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
s->private = local;
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
@@ -911,7 +888,7 @@ static int daqp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->len_chanlist = 1;
s->insn_read = daqp_di_insn_read;
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->private = local;
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITEABLE;
diff --git a/drivers/staging/comedi/drivers/rtd520.c b/drivers/staging/comedi/drivers/rtd520.c
index 5aa8be1e7b92..41d24b08913b 100644
--- a/drivers/staging/comedi/drivers/rtd520.c
+++ b/drivers/staging/comedi/drivers/rtd520.c
@@ -106,6 +106,8 @@ Configuration options:
#include "../comedidev.h"
+#include "comedi_fc.h"
+
#define DRV_NAME "rtd520"
/*======================================================================
@@ -783,7 +785,7 @@ static irqreturn_t rtd_interrupt(int irq, /* interrupt number (ignored) */
void *d)
{ /* our data *//* cpu context (ignored) */
struct comedi_device *dev = d;
- struct comedi_subdevice *s = dev->subdevices + 0; /* analog in subdevice */
+ struct comedi_subdevice *s = &dev->subdevices[0];
struct rtdPrivate *devpriv = dev->private;
u32 overrun;
u16 status;
@@ -964,7 +966,7 @@ static int rtd_ai_poll(struct comedi_device *dev, struct comedi_subdevice *s)
/*
cmdtest tests a particular command to see if it is valid.
Using the cmdtest ioctl, a user can create a valid cmd
- and then have it executed by the cmd ioctl (asyncronously).
+ and then have it executed by the cmd ioctl (asynchronously).
cmdtest returns 1,2,3,4 or 0, depending on which tests
the command passes.
@@ -976,52 +978,25 @@ static int rtd_ai_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique
- and mutually compatible */
- /* note that mutual compatibility is not an issue here */
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT) {
- err++;
- }
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1706,7 +1681,7 @@ static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
dev->read_subdev = s;
/* analog input subdevice */
s->type = COMEDI_SUBD_AI;
@@ -1726,7 +1701,7 @@ static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->cancel = rtd_ai_cancel;
/* s->poll = rtd_ai_poll; *//* not ready yet */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* analog output subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -1736,7 +1711,7 @@ static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_write = rtd_ao_winsn;
s->insn_read = rtd_ao_rinsn;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* digital i/o subdevice */
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
@@ -1748,7 +1723,7 @@ static int rtd_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_config = rtd_dio_insn_config;
/* timer/counter subdevices (not currently supported) */
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 3;
diff --git a/drivers/staging/comedi/drivers/rti800.c b/drivers/staging/comedi/drivers/rti800.c
index f7fa940d9783..137885b1681a 100644
--- a/drivers/staging/comedi/drivers/rti800.c
+++ b/drivers/staging/comedi/drivers/rti800.c
@@ -360,7 +360,7 @@ static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
devpriv->dac1_coding = it->options[8];
devpriv->muxgain_bits = -1;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* ai subdevice */
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
@@ -379,7 +379,7 @@ static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
break;
}
- s++;
+ s = &dev->subdevices[1];
if (board->has_ao) {
/* ao subdevice (only on rti815) */
s->type = COMEDI_SUBD_AO;
@@ -409,7 +409,7 @@ static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->type = COMEDI_SUBD_UNUSED;
}
- s++;
+ s = &dev->subdevices[2];
/* di */
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
@@ -418,7 +418,7 @@ static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->maxdata = 1;
s->range_table = &range_digital;
- s++;
+ s = &dev->subdevices[3];
/* do */
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
@@ -429,7 +429,7 @@ static int rti800_attach(struct comedi_device *dev, struct comedi_devconfig *it)
/* don't yet know how to deal with counter/timers */
#if 0
- s++;
+ s = &dev->subdevices[4];
/* do */
s->type = COMEDI_SUBD_TIMER;
#endif
diff --git a/drivers/staging/comedi/drivers/rti802.c b/drivers/staging/comedi/drivers/rti802.c
index fc16508181d4..3f9d0278be50 100644
--- a/drivers/staging/comedi/drivers/rti802.c
+++ b/drivers/staging/comedi/drivers/rti802.c
@@ -111,7 +111,7 @@ static int rti802_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices;
+ s = &dev->subdevices[0];
/* ao subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
diff --git a/drivers/staging/comedi/drivers/s526.c b/drivers/staging/comedi/drivers/s526.c
index 737a194dfce3..a1e256293bd6 100644
--- a/drivers/staging/comedi/drivers/s526.c
+++ b/drivers/staging/comedi/drivers/s526.c
@@ -83,36 +83,6 @@ comedi_config /dev/comedi0 s526 0x2C0,0x3
#define REG_EED 0x32
#define REG_EEC 0x34
-static const int s526_ports[] = {
- REG_TCR,
- REG_WDC,
- REG_DAC,
- REG_ADC,
- REG_ADD,
- REG_DIO,
- REG_IER,
- REG_ISR,
- REG_MSC,
- REG_C0L,
- REG_C0H,
- REG_C0M,
- REG_C0C,
- REG_C1L,
- REG_C1H,
- REG_C1M,
- REG_C1C,
- REG_C2L,
- REG_C2H,
- REG_C2M,
- REG_C2C,
- REG_C3L,
- REG_C3H,
- REG_C3M,
- REG_C3C,
- REG_EED,
- REG_EEC
-};
-
struct counter_mode_register_t {
#if defined(__LITTLE_ENDIAN_BITFIELD)
unsigned short coutSource:1;
@@ -148,122 +118,48 @@ union cmReg {
unsigned short value;
};
-#define MAX_GPCT_CONFIG_DATA 6
-
-/* Different Application Classes for GPCT Subdevices */
-/* The list is not exhaustive and needs discussion! */
-enum S526_GPCT_APP_CLASS {
- CountingAndTimeMeasurement,
- SinglePulseGeneration,
- PulseTrainGeneration,
- PositionMeasurement,
- Miscellaneous
-};
-
-/* Config struct for different GPCT subdevice Application Classes and
- their options
-*/
-struct s526GPCTConfig {
- enum S526_GPCT_APP_CLASS app;
- int data[MAX_GPCT_CONFIG_DATA];
-};
-
-/*
- * Board descriptions for two imaginary boards. Describing the
- * boards in this way is optional, and completely driver-dependent.
- * Some drivers use arrays such as this, other do not.
- */
-struct s526_board {
- const char *name;
- int gpct_chans;
- int gpct_bits;
- int ad_chans;
- int ad_bits;
- int da_chans;
- int da_bits;
- int have_dio;
-};
-
-static const struct s526_board s526_boards[] = {
- {
- .name = "s526",
- .gpct_chans = 4,
- .gpct_bits = 24,
- .ad_chans = 8,
- .ad_bits = 16,
- .da_chans = 4,
- .da_bits = 16,
- .have_dio = 1,
- }
-};
-
-#define ADDR_REG(reg) (dev->iobase + (reg))
-#define ADDR_CHAN_REG(reg, chan) (dev->iobase + (reg) + (chan) * 8)
-
-/* this structure is for data unique to this hardware driver. If
- several hardware drivers keep similar information in this structure,
- feel free to suggest moving the variable to the struct comedi_device
- struct.
-*/
struct s526_private {
unsigned int ao_readback[2];
- struct s526GPCTConfig s526_gpct_config[4];
- unsigned short s526_ai_config;
+ unsigned int gpct_config[4];
+ unsigned short ai_config;
};
-/*
- * most drivers define the following macro to make it easy to
- * access the private structure.
- */
-#define devpriv ((struct s526_private *)dev->private)
-
static int s526_gpct_rinsn(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
unsigned int *data)
{
- int i; /* counts the Data */
- int counter_channel = CR_CHAN(insn->chanspec);
- unsigned short datalow;
- unsigned short datahigh;
-
- /* Check if (n > 0) */
- if (insn->n <= 0) {
- printk(KERN_ERR "s526: INSN_READ: n should be > 0\n");
- return -EINVAL;
- }
- /* Read the low word first */
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long chan_iobase = dev->iobase + chan * 8;
+ unsigned int lo;
+ unsigned int hi;
+ int i;
+
for (i = 0; i < insn->n; i++) {
- datalow = inw(ADDR_CHAN_REG(REG_C0L, counter_channel));
- datahigh = inw(ADDR_CHAN_REG(REG_C0H, counter_channel));
- data[i] = (int)(datahigh & 0x00FF);
- data[i] = (data[i] << 16) | (datalow & 0xFFFF);
- /* printk("s526 GPCT[%d]: %x(0x%04x, 0x%04x)\n",
- counter_channel, data[i], datahigh, datalow); */
+ /* Read the low word first */
+ lo = inw(chan_iobase + REG_C0L) & 0xffff;
+ hi = inw(chan_iobase + REG_C0H) & 0xff;
+
+ data[i] = (hi << 16) | lo;
}
- return i;
+
+ return insn->n;
}
static int s526_gpct_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
- struct comedi_insn *insn, unsigned int *data)
+ struct comedi_insn *insn,
+ unsigned int *data)
{
- int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
- int i;
- short value;
+ struct s526_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long chan_iobase = dev->iobase + chan * 8;
+ unsigned int val;
union cmReg cmReg;
- /* printk("s526: GPCT_INSN_CONFIG: Configuring Channel %d\n",
- subdev_channel); */
-
- for (i = 0; i < MAX_GPCT_CONFIG_DATA; i++) {
- devpriv->s526_gpct_config[subdev_channel].data[i] =
- insn->data[i];
-/* printk("data[%d]=%x\n", i, insn->data[i]); */
- }
-
/* Check what type of Counter the user requested, data[0] contains */
/* the Application type */
- switch (insn->data[0]) {
+ switch (data[0]) {
case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
/*
data[0]: Application Type
@@ -271,9 +167,7 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
data[2]: Pre-load Register Value
data[3]: Conter Control Register
*/
- printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring Encoder\n");
- devpriv->s526_gpct_config[subdev_channel].app =
- PositionMeasurement;
+ devpriv->gpct_config[chan] = data[0];
#if 0
/* Example of Counter Application */
@@ -290,34 +184,32 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
cmReg.reg.preloadRegSel = 0; /* PR0 */
cmReg.reg.reserved = 0;
- outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+ outw(cmReg.value, chan_iobase + REG_C0M);
- outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
- outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+ outw(0x0001, chan_iobase + REG_C0H);
+ outw(0x3C68, chan_iobase + REG_C0L);
/* Reset the counter */
- outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ outw(0x8000, chan_iobase + REG_C0C);
/* Load the counter from PR0 */
- outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ outw(0x4000, chan_iobase + REG_C0C);
/* Reset RCAP (fires one-shot) */
- outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ outw(0x0008, chan_iobase + REG_C0C);
#endif
#if 1
/* Set Counter Mode Register */
- cmReg.value = insn->data[1] & 0xFFFF;
-
-/* printk("s526: Counter Mode register=%x\n", cmReg.value); */
- outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+ cmReg.value = data[1] & 0xffff;
+ outw(cmReg.value, chan_iobase + REG_C0M);
/* Reset the counter if it is software preload */
if (cmReg.reg.autoLoadResetRcap == 0) {
/* Reset the counter */
- outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ outw(0x8000, chan_iobase + REG_C0C);
/* Load the counter from PR0
- * outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ * outw(0x4000, chan_iobase + REG_C0C);
*/
}
#else
@@ -325,47 +217,47 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
cmReg.reg.countDirCtrl = 0;
/* data[1] contains GPCT_X1, GPCT_X2 or GPCT_X4 */
- if (insn->data[1] == GPCT_X2)
+ if (data[1] == GPCT_X2)
cmReg.reg.clockSource = 1;
- else if (insn->data[1] == GPCT_X4)
+ else if (data[1] == GPCT_X4)
cmReg.reg.clockSource = 2;
else
cmReg.reg.clockSource = 0;
/* When to take into account the indexpulse: */
- /*if (insn->data[2] == GPCT_IndexPhaseLowLow) {
- } else if (insn->data[2] == GPCT_IndexPhaseLowHigh) {
- } else if (insn->data[2] == GPCT_IndexPhaseHighLow) {
- } else if (insn->data[2] == GPCT_IndexPhaseHighHigh) {
+ /*if (data[2] == GPCT_IndexPhaseLowLow) {
+ } else if (data[2] == GPCT_IndexPhaseLowHigh) {
+ } else if (data[2] == GPCT_IndexPhaseHighLow) {
+ } else if (data[2] == GPCT_IndexPhaseHighHigh) {
}*/
/* Take into account the index pulse? */
- if (insn->data[3] == GPCT_RESET_COUNTER_ON_INDEX)
+ if (data[3] == GPCT_RESET_COUNTER_ON_INDEX)
/* Auto load with INDEX^ */
cmReg.reg.autoLoadResetRcap = 4;
/* Set Counter Mode Register */
- cmReg.value = (short)(insn->data[1] & 0xFFFF);
- outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+ cmReg.value = data[1] & 0xffff;
+ outw(cmReg.value, chan_iobase + REG_C0M);
/* Load the pre-load register high word */
- value = (short)((insn->data[2] >> 16) & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+ val = (data[2] >> 16) & 0xffff;
+ outw(val, chan_iobase + REG_C0H);
/* Load the pre-load register low word */
- value = (short)(insn->data[2] & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+ val = data[2] & 0xffff;
+ outw(val, chan_iobase + REG_C0L);
/* Write the Counter Control Register */
- if (insn->data[3] != 0) {
- value = (short)(insn->data[3] & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ if (data[3]) {
+ val = data[3] & 0xffff;
+ outw(val, chan_iobase + REG_C0C);
}
/* Reset the counter if it is software preload */
if (cmReg.reg.autoLoadResetRcap == 0) {
/* Reset the counter */
- outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ outw(0x8000, chan_iobase + REG_C0C);
/* Load the counter from PR0 */
- outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ outw(0x4000, chan_iobase + REG_C0C);
}
#endif
break;
@@ -378,40 +270,38 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
data[3]: Pre-load Register 1 Value
data[4]: Conter Control Register
*/
- printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring SPG\n");
- devpriv->s526_gpct_config[subdev_channel].app =
- SinglePulseGeneration;
+ devpriv->gpct_config[chan] = data[0];
/* Set Counter Mode Register */
- cmReg.value = (short)(insn->data[1] & 0xFFFF);
+ cmReg.value = data[1] & 0xffff;
cmReg.reg.preloadRegSel = 0; /* PR0 */
- outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+ outw(cmReg.value, chan_iobase + REG_C0M);
/* Load the pre-load register 0 high word */
- value = (short)((insn->data[2] >> 16) & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+ val = (data[2] >> 16) & 0xffff;
+ outw(val, chan_iobase + REG_C0H);
/* Load the pre-load register 0 low word */
- value = (short)(insn->data[2] & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+ val = data[2] & 0xffff;
+ outw(val, chan_iobase + REG_C0L);
/* Set Counter Mode Register */
- cmReg.value = (short)(insn->data[1] & 0xFFFF);
+ cmReg.value = data[1] & 0xffff;
cmReg.reg.preloadRegSel = 1; /* PR1 */
- outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+ outw(cmReg.value, chan_iobase + REG_C0M);
/* Load the pre-load register 1 high word */
- value = (short)((insn->data[3] >> 16) & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+ val = (data[3] >> 16) & 0xffff;
+ outw(val, chan_iobase + REG_C0H);
/* Load the pre-load register 1 low word */
- value = (short)(insn->data[3] & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+ val = data[3] & 0xffff;
+ outw(val, chan_iobase + REG_C0L);
/* Write the Counter Control Register */
- if (insn->data[4] != 0) {
- value = (short)(insn->data[4] & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ if (data[4]) {
+ val = data[4] & 0xffff;
+ outw(val, chan_iobase + REG_C0C);
}
break;
@@ -423,45 +313,42 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
data[3]: Pre-load Register 1 Value
data[4]: Conter Control Register
*/
- printk(KERN_INFO "s526: GPCT_INSN_CONFIG: Configuring PTG\n");
- devpriv->s526_gpct_config[subdev_channel].app =
- PulseTrainGeneration;
+ devpriv->gpct_config[chan] = data[0];
/* Set Counter Mode Register */
- cmReg.value = (short)(insn->data[1] & 0xFFFF);
+ cmReg.value = data[1] & 0xffff;
cmReg.reg.preloadRegSel = 0; /* PR0 */
- outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+ outw(cmReg.value, chan_iobase + REG_C0M);
/* Load the pre-load register 0 high word */
- value = (short)((insn->data[2] >> 16) & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+ val = (data[2] >> 16) & 0xffff;
+ outw(val, chan_iobase + REG_C0H);
/* Load the pre-load register 0 low word */
- value = (short)(insn->data[2] & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+ val = data[2] & 0xffff;
+ outw(val, chan_iobase + REG_C0L);
/* Set Counter Mode Register */
- cmReg.value = (short)(insn->data[1] & 0xFFFF);
+ cmReg.value = data[1] & 0xffff;
cmReg.reg.preloadRegSel = 1; /* PR1 */
- outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
+ outw(cmReg.value, chan_iobase + REG_C0M);
/* Load the pre-load register 1 high word */
- value = (short)((insn->data[3] >> 16) & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
+ val = (data[3] >> 16) & 0xffff;
+ outw(val, chan_iobase + REG_C0H);
/* Load the pre-load register 1 low word */
- value = (short)(insn->data[3] & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+ val = data[3] & 0xffff;
+ outw(val, chan_iobase + REG_C0L);
/* Write the Counter Control Register */
- if (insn->data[4] != 0) {
- value = (short)(insn->data[4] & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0C, subdev_channel));
+ if (data[4]) {
+ val = data[4] & 0xffff;
+ outw(val, chan_iobase + REG_C0C);
}
break;
default:
- printk(KERN_ERR "s526: unsupported GPCT_insn_config\n");
return -EINVAL;
break;
}
@@ -470,65 +357,40 @@ static int s526_gpct_insn_config(struct comedi_device *dev,
}
static int s526_gpct_winsn(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_insn *insn,
+ struct comedi_subdevice *s,
+ struct comedi_insn *insn,
unsigned int *data)
{
- int subdev_channel = CR_CHAN(insn->chanspec); /* Unpack chanspec */
- short value;
- union cmReg cmReg;
-
- printk(KERN_INFO "s526: GPCT_INSN_WRITE on channel %d\n",
- subdev_channel);
- cmReg.value = inw(ADDR_CHAN_REG(REG_C0M, subdev_channel));
- printk(KERN_INFO "s526: Counter Mode Register: %x\n", cmReg.value);
- /* Check what Application of Counter this channel is configured for */
- switch (devpriv->s526_gpct_config[subdev_channel].app) {
- case PositionMeasurement:
- printk(KERN_INFO "S526: INSN_WRITE: PM\n");
- outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
- subdev_channel));
- outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
- break;
+ struct s526_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
+ unsigned long chan_iobase = dev->iobase + chan * 8;
- case SinglePulseGeneration:
- printk(KERN_INFO "S526: INSN_WRITE: SPG\n");
- outw(0xFFFF & ((*data) >> 16), ADDR_CHAN_REG(REG_C0H,
- subdev_channel));
- outw(0xFFFF & (*data), ADDR_CHAN_REG(REG_C0L, subdev_channel));
- break;
+ inw(chan_iobase + REG_C0M); /* Is this read required? */
- case PulseTrainGeneration:
+ /* Check what Application of Counter this channel is configured for */
+ switch (devpriv->gpct_config[chan]) {
+ case INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR:
/* data[0] contains the PULSE_WIDTH
data[1] contains the PULSE_PERIOD
@pre PULSE_PERIOD > PULSE_WIDTH > 0
The above periods must be expressed as a multiple of the
pulse frequency on the selected source
*/
- printk(KERN_INFO "S526: INSN_WRITE: PTG\n");
- if ((insn->data[1] > insn->data[0]) && (insn->data[0] > 0)) {
- (devpriv->s526_gpct_config[subdev_channel]).data[0] =
- insn->data[0];
- (devpriv->s526_gpct_config[subdev_channel]).data[1] =
- insn->data[1];
- } else {
- printk(KERN_ERR "s526: INSN_WRITE: PTG: Problem with Pulse params -> %d %d\n",
- insn->data[0], insn->data[1]);
+ if ((data[1] <= data[0]) || !data[0])
return -EINVAL;
- }
- value = (short)((*data >> 16) & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0H, subdev_channel));
- value = (short)(*data & 0xFFFF);
- outw(value, ADDR_CHAN_REG(REG_C0L, subdev_channel));
+ /* Fall thru to write the PULSE_WIDTH */
+
+ case INSN_CONFIG_GPCT_QUADRATURE_ENCODER:
+ case INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR:
+ outw((data[0] >> 16) & 0xffff, chan_iobase + REG_C0H);
+ outw(data[0] & 0xffff, chan_iobase + REG_C0L);
break;
- default: /* Impossible */
- printk
- ("s526: INSN_WRITE: Functionality %d not implemented yet\n",
- devpriv->s526_gpct_config[subdev_channel].app);
+
+ default:
return -EINVAL;
- break;
}
- /* return the number of samples written */
+
return insn->n;
}
@@ -537,6 +399,7 @@ static int s526_ai_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct s526_private *devpriv = dev->private;
int result = -EINVAL;
if (insn->n < 1)
@@ -552,62 +415,50 @@ static int s526_ai_insn_config(struct comedi_device *dev,
* INSN_READ handler. */
/* Enable ADC interrupt */
- outw(ISR_ADC_DONE, ADDR_REG(REG_IER));
-/* printk("s526: ADC current value: 0x%04x\n", inw(ADDR_REG(REG_ADC))); */
- devpriv->s526_ai_config = (data[0] & 0x3FF) << 5;
+ outw(ISR_ADC_DONE, dev->iobase + REG_IER);
+ devpriv->ai_config = (data[0] & 0x3ff) << 5;
if (data[1] > 0)
- devpriv->s526_ai_config |= 0x8000; /* set the delay */
+ devpriv->ai_config |= 0x8000; /* set the delay */
- devpriv->s526_ai_config |= 0x0001; /* ADC start bit. */
+ devpriv->ai_config |= 0x0001; /* ADC start bit */
return result;
}
-/*
- * "instructions" read/write data in "one-shot" or "software-triggered"
- * mode.
- */
static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct s526_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
int n, i;
- int chan = CR_CHAN(insn->chanspec);
unsigned short value;
unsigned int d;
unsigned int status;
/* Set configured delay, enable channel for this channel only,
* select "ADC read" channel, set "ADC start" bit. */
- value = (devpriv->s526_ai_config & 0x8000) |
- ((1 << 5) << chan) | (chan << 1) | 0x0001;
+ value = (devpriv->ai_config & 0x8000) |
+ ((1 << 5) << chan) | (chan << 1) | 0x0001;
/* convert n samples */
for (n = 0; n < insn->n; n++) {
/* trigger conversion */
- outw(value, ADDR_REG(REG_ADC));
-/* printk("s526: Wrote 0x%04x to ADC\n", value); */
-/* printk("s526: ADC reg=0x%04x\n", inw(ADDR_REG(REG_ADC))); */
+ outw(value, dev->iobase + REG_ADC);
#define TIMEOUT 100
/* wait for conversion to end */
for (i = 0; i < TIMEOUT; i++) {
- status = inw(ADDR_REG(REG_ISR));
+ status = inw(dev->iobase + REG_ISR);
if (status & ISR_ADC_DONE) {
- outw(ISR_ADC_DONE, ADDR_REG(REG_ISR));
+ outw(ISR_ADC_DONE, dev->iobase + REG_ISR);
break;
}
}
- if (i == TIMEOUT) {
- /* printk() should be used instead of printk()
- * whenever the code can be called from real-time. */
- printk(KERN_ERR "s526: ADC(0x%04x) timeout\n",
- inw(ADDR_REG(REG_ISR)));
+ if (i == TIMEOUT)
return -ETIMEDOUT;
- }
/* read data */
- d = inw(ADDR_REG(REG_ADD));
-/* printk("AI[%d]=0x%04x\n", n, (unsigned short)(d & 0xFFFF)); */
+ d = inw(dev->iobase + REG_ADD);
/* munge data */
data[n] = d ^ 0x8000;
@@ -620,40 +471,30 @@ static int s526_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
static int s526_ao_winsn(struct comedi_device *dev, struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
- int i;
- int chan = CR_CHAN(insn->chanspec);
+ struct s526_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
unsigned short val;
+ int i;
-/* printk("s526_ao_winsn\n"); */
val = chan << 1;
-/* outw(val, dev->iobase + REG_DAC); */
- outw(val, ADDR_REG(REG_DAC));
+ outw(val, dev->iobase + REG_DAC);
- /* Writing a list of values to an AO channel is probably not
- * very useful, but that's how the interface is defined. */
for (i = 0; i < insn->n; i++) {
- /* a typical programming sequence */
- /* write the data to preload register
- * outw(data[i], dev->iobase + REG_ADD);
- */
- /* write the data to preload register */
- outw(data[i], ADDR_REG(REG_ADD));
+ outw(data[i], dev->iobase + REG_ADD);
devpriv->ao_readback[chan] = data[i];
-/* outw(val + 1, dev->iobase + REG_DAC); starts the D/A conversion. */
- outw(val + 1, ADDR_REG(REG_DAC)); /*starts the D/A conversion.*/
+ /* starts the D/A conversion */
+ outw(val + 1, dev->iobase + REG_DAC);
}
- /* return the number of samples read/written */
return i;
}
-/* AO subdevices should have a read insn as well as a write insn.
- * Usually this means copying a value stored in devpriv. */
static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
+ struct s526_private *devpriv = dev->private;
+ unsigned int chan = CR_CHAN(insn->chanspec);
int i;
- int chan = CR_CHAN(insn->chanspec);
for (i = 0; i < insn->n; i++)
data[i] = devpriv->ao_readback[chan];
@@ -661,30 +502,18 @@ static int s526_ao_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
return i;
}
-/* DIO devices are slightly special. Although it is possible to
- * implement the insn_read/insn_write interface, it is much more
- * useful to applications if you implement the insn_bits interface.
- * This allows packed reading/writing of the DIO channels. The
- * comedi core can convert between insn_bits and insn_read/write */
static int s526_dio_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
- /* The insn data is a mask in data[0] and the new data
- * in data[1], each channel cooresponding to a bit. */
if (data[0]) {
s->state &= ~data[0];
s->state |= data[0] & data[1];
- /* Write out the new digital output lines */
- outw(s->state, ADDR_REG(REG_DIO));
+
+ outw(s->state, dev->iobase + REG_DIO);
}
- /* on return, data[1] contains the value of the digital
- * input and output lines. */
- data[1] = inw(ADDR_REG(REG_DIO)) & 0xFF; /* low 8 bits are the data */
- /* or we could just return the software copy of the output values if
- * it was a purely digital output subdevice */
- /* data[1]=s->state & 0xFF; */
+ data[1] = inw(dev->iobase + REG_DIO) & 0xff;
return insn->n;
}
@@ -693,16 +522,9 @@ static int s526_dio_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
{
- int chan = CR_CHAN(insn->chanspec);
+ unsigned int chan = CR_CHAN(insn->chanspec);
int group, mask;
- printk(KERN_INFO "S526 DIO insn_config\n");
-
- /* The input or output configuration of each digital line is
- * configured by a special insn_config instruction. chanspec
- * contains the channel to be changed, and data[0] contains the
- * value COMEDI_INPUT or COMEDI_OUTPUT. */
-
group = chan >> 2;
mask = 0xF << (group << 2);
switch (data[0]) {
@@ -721,88 +543,60 @@ static int s526_dio_insn_config(struct comedi_device *dev,
default:
return -EINVAL;
}
- outw(s->state, ADDR_REG(REG_DIO));
+ outw(s->state, dev->iobase + REG_DIO);
return 1;
}
static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
{
- const struct s526_board *board = comedi_board(dev);
+ struct s526_private *devpriv;
struct comedi_subdevice *s;
int iobase;
- int i, n;
int ret;
-/* short value; */
-/* int subdev_channel = 0; */
- union cmReg cmReg;
- printk(KERN_INFO "comedi%d: s526: ", dev->minor);
+ dev->board_name = dev->driver->driver_name;
iobase = it->options[0];
- if (!iobase || !request_region(iobase, S526_IOSIZE, board->name)) {
+ if (!iobase || !request_region(iobase, S526_IOSIZE, dev->board_name)) {
comedi_error(dev, "I/O port conflict");
return -EIO;
}
dev->iobase = iobase;
- printk("iobase=0x%lx\n", dev->iobase);
-
- /*** make it a little quieter, exw, 8/29/06
- for (i = 0; i < S526_NUM_PORTS; i++) {
- printk("0x%02x: 0x%04x\n", ADDR_REG(s526_ports[i]),
- inw(ADDR_REG(s526_ports[i])));
- }
- ***/
-
- dev->board_name = board->name;
-
-/*
- * Allocate the private structure area. alloc_private() is a
- * convenient macro defined in comedidev.h.
- */
- if (alloc_private(dev, sizeof(struct s526_private)) < 0)
- return -ENOMEM;
+ ret = alloc_private(dev, sizeof(*devpriv));
+ if (ret)
+ return ret;
+ devpriv = dev->private;
ret = comedi_alloc_subdevices(dev, 4);
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* GENERAL-PURPOSE COUNTER/TIME (GPCT) */
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE | SDF_LSAMPL;
- /* KG: What does SDF_LSAMPL (see multiq3.c) mean? */
- s->n_chan = board->gpct_chans;
+ s->n_chan = 4;
s->maxdata = 0x00ffffff; /* 24 bit counter */
s->insn_read = s526_gpct_rinsn;
s->insn_config = s526_gpct_insn_config;
s->insn_write = s526_gpct_winsn;
- /* Command are not implemented yet, however they are necessary to
- allocate the necessary memory for the comedi_async struct (used
- to trigger the GPCT in case of pulsegenerator function */
- /* s->do_cmd = s526_gpct_cmd; */
- /* s->do_cmdtest = s526_gpct_cmdtest; */
- /* s->cancel = s526_gpct_cancel; */
-
- s = dev->subdevices + 1;
- /* dev->read_subdev=s; */
+ s = &dev->subdevices[1];
/* analog input subdevice */
s->type = COMEDI_SUBD_AI;
- /* we support differential */
s->subdev_flags = SDF_READABLE | SDF_DIFF;
/* channels 0 to 7 are the regular differential inputs */
/* channel 8 is "reference 0" (+10V), channel 9 is "reference 1" (0V) */
s->n_chan = 10;
s->maxdata = 0xffff;
s->range_table = &range_bipolar10;
- s->len_chanlist = 16; /* This is the maximum chanlist length that
- the board can handle */
+ s->len_chanlist = 16;
s->insn_read = s526_ai_rinsn;
s->insn_config = s526_ai_insn_config;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* analog output subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -812,105 +606,18 @@ static int s526_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_write = s526_ao_winsn;
s->insn_read = s526_ao_rinsn;
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
/* digital i/o subdevice */
- if (board->have_dio) {
- s->type = COMEDI_SUBD_DIO;
- s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
- s->n_chan = 8;
- s->maxdata = 1;
- s->range_table = &range_digital;
- s->insn_bits = s526_dio_insn_bits;
- s->insn_config = s526_dio_insn_config;
- } else {
- s->type = COMEDI_SUBD_UNUSED;
- }
-
- printk(KERN_INFO "attached\n");
-
- return 1;
-
-#if 0
- /* Example of Counter Application */
- /* One-shot (software trigger) */
- cmReg.reg.coutSource = 0; /* out RCAP */
- cmReg.reg.coutPolarity = 1; /* Polarity inverted */
- cmReg.reg.autoLoadResetRcap = 1;/* Auto load 0:disabled, 1:enabled */
- cmReg.reg.hwCtEnableSource = 3; /* NOT RCAP */
- cmReg.reg.ctEnableCtrl = 2; /* Hardware */
- cmReg.reg.clockSource = 2; /* Internal */
- cmReg.reg.countDir = 1; /* Down */
- cmReg.reg.countDirCtrl = 1; /* Software */
- cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
- cmReg.reg.preloadRegSel = 0; /* PR0 */
- cmReg.reg.reserved = 0;
-
- outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, subdev_channel));
-
- outw(0x0001, ADDR_CHAN_REG(REG_C0H, subdev_channel));
- outw(0x3C68, ADDR_CHAN_REG(REG_C0L, subdev_channel));
-
- /* Reset the counter */
- outw(0x8000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
- /* Load the counter from PR0 */
- outw(0x4000, ADDR_CHAN_REG(REG_C0C, subdev_channel));
- /* Reset RCAP (fires one-shot) */
- outw(0x0008, ADDR_CHAN_REG(REG_C0C, subdev_channel));
-
-#else
-
- /* Set Counter Mode Register */
- cmReg.reg.coutSource = 0; /* out RCAP */
- cmReg.reg.coutPolarity = 0; /* Polarity inverted */
- cmReg.reg.autoLoadResetRcap = 0; /* Auto load disabled */
- cmReg.reg.hwCtEnableSource = 2; /* NOT RCAP */
- cmReg.reg.ctEnableCtrl = 1; /* 1: Software, >1 : Hardware */
- cmReg.reg.clockSource = 3; /* x4 */
- cmReg.reg.countDir = 0; /* up */
- cmReg.reg.countDirCtrl = 0; /* quadrature */
- cmReg.reg.outputRegLatchCtrl = 0; /* latch on read */
- cmReg.reg.preloadRegSel = 0; /* PR0 */
- cmReg.reg.reserved = 0;
-
- n = 0;
- printk(KERN_INFO "Mode reg=0x%04x, 0x%04lx\n",
- cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
- outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
- udelay(1000);
- printk(KERN_INFO "Read back mode reg=0x%04x\n",
- inw(ADDR_CHAN_REG(REG_C0M, n)));
-
- /* Load the pre-load register high word */
-/* value = (short) (0x55); */
-/* outw(value, ADDR_CHAN_REG(REG_C0H, n)); */
-
- /* Load the pre-load register low word */
-/* value = (short)(0xaa55); */
-/* outw(value, ADDR_CHAN_REG(REG_C0L, n)); */
-
- /* Write the Counter Control Register */
-/* outw(value, ADDR_CHAN_REG(REG_C0C, 0)); */
-
- /* Reset the counter if it is software preload */
- if (cmReg.reg.autoLoadResetRcap == 0) {
- /* Reset the counter */
- outw(0x8000, ADDR_CHAN_REG(REG_C0C, n));
- /* Load the counter from PR0 */
- outw(0x4000, ADDR_CHAN_REG(REG_C0C, n));
- }
+ s->type = COMEDI_SUBD_DIO;
+ s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
+ s->n_chan = 8;
+ s->maxdata = 1;
+ s->range_table = &range_digital;
+ s->insn_bits = s526_dio_insn_bits;
+ s->insn_config = s526_dio_insn_config;
- outw(cmReg.value, ADDR_CHAN_REG(REG_C0M, n));
- udelay(1000);
- printk(KERN_INFO "Read back mode reg=0x%04x\n",
- inw(ADDR_CHAN_REG(REG_C0M, n)));
+ dev_info(dev->class_dev, "%s attached\n", dev->board_name);
-#endif
- printk(KERN_INFO "Current registres:\n");
-
- for (i = 0; i < S526_NUM_PORTS; i++) {
- printk(KERN_INFO "0x%02lx: 0x%04x\n",
- ADDR_REG(s526_ports[i]), inw(ADDR_REG(s526_ports[i])));
- }
return 1;
}
@@ -925,9 +632,6 @@ static struct comedi_driver s526_driver = {
.module = THIS_MODULE,
.attach = s526_attach,
.detach = s526_detach,
- .board_name = &s526_boards[0].name,
- .offset = sizeof(struct s526_board),
- .num_names = ARRAY_SIZE(s526_boards),
};
module_comedi_driver(s526_driver);
diff --git a/drivers/staging/comedi/drivers/s626.c b/drivers/staging/comedi/drivers/s626.c
index f90578e5e727..551d68b7837c 100644
--- a/drivers/staging/comedi/drivers/s626.c
+++ b/drivers/staging/comedi/drivers/s626.c
@@ -32,11 +32,7 @@ Authors: Gianluca Palli <gpalli@deis.unibo.it>,
Updated: Fri, 15 Feb 2008 10:28:42 +0000
Status: experimental
-Configuration options:
- [0] - PCI bus of device (optional)
- [1] - PCI slot of device (optional)
- If bus/slot is not specified, the first supported
- PCI device found will be used.
+Configuration options: not applicable, uses PCI auto config
INSN_CONFIG instructions:
analog input:
@@ -82,45 +78,8 @@ INSN_CONFIG instructions:
#define PCI_SUBVENDOR_ID_S626 0x6000
#define PCI_SUBDEVICE_ID_S626 0x0272
-struct s626_board {
- const char *name;
- int vendor_id;
- int device_id;
- int subvendor_id;
- int subdevice_id;
- int ai_chans;
- int ai_bits;
- int ao_chans;
- int ao_bits;
- int dio_chans;
- int dio_banks;
- int enc_chans;
-};
-
-static const struct s626_board s626_boards[] = {
- {
- .name = "s626",
- .vendor_id = PCI_VENDOR_ID_S626,
- .device_id = PCI_DEVICE_ID_S626,
- .subvendor_id = PCI_SUBVENDOR_ID_S626,
- .subdevice_id = PCI_SUBDEVICE_ID_S626,
- .ai_chans = S626_ADC_CHANNELS,
- .ai_bits = 14,
- .ao_chans = S626_DAC_CHANNELS,
- .ao_bits = 13,
- .dio_chans = S626_DIO_CHANNELS,
- .dio_banks = S626_DIO_BANKS,
- .enc_chans = S626_ENCODER_CHANNELS,
- }
-};
-
-#define thisboard ((const struct s626_board *)dev->board_ptr)
-
struct s626_private {
- struct pci_dev *pdev;
void __iomem *base_addr;
- int got_regions;
- short allocatedBuf;
uint8_t ai_cmd_running; /* ai_cmd is running */
uint8_t ai_continous; /* continous acquisition */
int ai_sample_count; /* number of samples to acquire */
@@ -139,9 +98,7 @@ struct s626_private {
/* Pointer to logical adrs of DMA buffer used to hold DAC data. */
uint16_t Dacpol; /* Image of DAC polarity register. */
uint8_t TrimSetpoint[12]; /* Images of TrimDAC setpoints */
- uint16_t ChargeEnabled; /* Image of MISC2 Battery */
/* Charge Enabled (0 or WRMISC2_CHARGE_ENABLE). */
- uint16_t WDInterval; /* Image of MISC2 watchdog interval control bits. */
uint32_t I2CAdrs;
/* I2C device address for onboard EEPROM (board rev dependent). */
/* short I2Cards; */
@@ -1548,56 +1505,28 @@ static int s626_ai_cmdtest(struct comedi_device *dev,
int err = 0;
int tmp;
- /* cmdtest tests a particular command to see if it is valid. Using
- * the cmdtest ioctl, a user can create a valid cmd and then have it
- * executes by the cmd ioctl.
- *
- * cmdtest returns 1,2,3,4 or 0, depending on which tests the
- * command passes. */
-
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_INT | TRIG_EXT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT | TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src,
+ TRIG_NOW | TRIG_INT | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT | TRIG_FOLLOW);
+ err |= cfc_check_trigger_src(&cmd->convert_src,
+ TRIG_TIMER | TRIG_EXT | TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually
- compatible */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT
- && cmd->scan_begin_src != TRIG_FOLLOW)
- err++;
- if (cmd->convert_src != TRIG_TIMER &&
- cmd->convert_src != TRIG_EXT && cmd->convert_src != TRIG_NOW)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1847,6 +1776,9 @@ static int s626_dio_insn_config(struct comedi_device *dev,
/* Now this function initializes the value of the counter (data[0])
and set the subdevice. To complete with trigger and interrupt
configuration */
+/* FIXME: data[0] is supposed to be an INSN_CONFIG_xxx constant indicating
+ * what is being configured, but this function appears to be using data[0]
+ * as a variable. */
static int s626_enc_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn, unsigned int *data)
@@ -1868,7 +1800,7 @@ static int s626_enc_insn_config(struct comedi_device *dev,
/* (data==NULL) ? (Preloadvalue=0) : (Preloadvalue=data[0]); */
k->SetMode(dev, k, Setup, TRUE);
- Preload(dev, k, *(insn->data));
+ Preload(dev, k, data[0]);
k->PulseIndex(dev, k);
SetLatchSource(dev, k, valueSrclatch);
k->SetEnable(dev, k, (uint16_t) (enab != 0));
@@ -1920,6 +1852,7 @@ static void WriteMISC2(struct comedi_device *dev, uint16_t NewImage)
static void CloseDMAB(struct comedi_device *dev, struct bufferDMA *pdma,
size_t bsize)
{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
void *vbptr;
dma_addr_t vpptr;
@@ -1930,7 +1863,7 @@ static void CloseDMAB(struct comedi_device *dev, struct bufferDMA *pdma,
vbptr = pdma->LogicalBase;
vpptr = pdma->PhysicalBase;
if (vbptr) {
- pci_free_consistent(devpriv->pdev, bsize, vbptr, vpptr);
+ pci_free_consistent(pcidev, bsize, vbptr, vpptr);
pdma->LogicalBase = NULL;
pdma->PhysicalBase = 0;
}
@@ -2476,146 +2409,317 @@ static void CountersInit(struct comedi_device *dev)
}
}
-static struct pci_dev *s626_find_pci(struct comedi_device *dev,
- struct comedi_devconfig *it)
+static int s626_allocate_dma_buffers(struct comedi_device *dev)
{
- struct pci_dev *pcidev = NULL;
- int bus = it->options[0];
- int slot = it->options[1];
- int i;
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+ void *addr;
+ dma_addr_t appdma;
- for (i = 0; i < ARRAY_SIZE(s626_boards) && !pcidev; i++) {
- do {
- pcidev = pci_get_subsys(s626_boards[i].vendor_id,
- s626_boards[i].device_id,
- s626_boards[i].subvendor_id,
- s626_boards[i].subdevice_id,
- pcidev);
-
- if ((bus || slot) && pcidev) {
- /* matches requested bus/slot */
- if (pcidev->bus->number == bus &&
- PCI_SLOT(pcidev->devfn) == slot)
- break;
- } else {
- break;
- }
- } while (1);
- }
- return pcidev;
+ addr = pci_alloc_consistent(pcidev, DMABUF_SIZE, &appdma);
+ if (!addr)
+ return -ENOMEM;
+ devpriv->ANABuf.LogicalBase = addr;
+ devpriv->ANABuf.PhysicalBase = appdma;
+
+ addr = pci_alloc_consistent(pcidev, DMABUF_SIZE, &appdma);
+ if (!addr)
+ return -ENOMEM;
+ devpriv->RPSBuf.LogicalBase = addr;
+ devpriv->RPSBuf.PhysicalBase = appdma;
+
+ return 0;
}
-static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it)
+static void s626_initialize(struct comedi_device *dev)
{
-/* uint8_t PollList; */
-/* uint16_t AdcData; */
-/* uint16_t StartVal; */
-/* uint16_t index; */
-/* unsigned int data[16]; */
- int result;
+ dma_addr_t pPhysBuf;
+ uint16_t chan;
int i;
- int ret;
- resource_size_t resourceStart;
- dma_addr_t appdma;
- struct comedi_subdevice *s;
- if (alloc_private(dev, sizeof(struct s626_private)) < 0)
- return -ENOMEM;
+ /* Enable DEBI and audio pins, enable I2C interface */
+ MC_ENABLE(P_MC1, MC1_DEBI | MC1_AUDIO | MC1_I2C);
- devpriv->pdev = s626_find_pci(dev, it);
- if (!devpriv->pdev) {
- printk(KERN_ERR "s626_attach: Board not present!!!\n");
- return -ENODEV;
- }
+ /*
+ * Configure DEBI operating mode
+ *
+ * Local bus is 16 bits wide
+ * Declare DEBI transfer timeout interval
+ * Set up byte lane steering
+ * Intel-compatible local bus (DEBI never times out)
+ */
+ WR7146(P_DEBICFG, DEBI_CFG_SLAVE16 |
+ (DEBI_TOUT << DEBI_CFG_TOUT_BIT) |
+ DEBI_SWAP | DEBI_CFG_INTEL);
+
+ /* Disable MMU paging */
+ WR7146(P_DEBIPAGE, DEBI_PAGE_DISABLE);
- result = comedi_pci_enable(devpriv->pdev, "s626");
- if (result < 0) {
- printk(KERN_ERR "s626_attach: comedi_pci_enable fails\n");
- return -ENODEV;
+ /* Init GPIO so that ADC Start* is negated */
+ WR7146(P_GPIO, GPIO_BASE | GPIO1_HI);
+
+ /* I2C device address for onboard eeprom (revb) */
+ devpriv->I2CAdrs = 0xA0;
+
+ /*
+ * Issue an I2C ABORT command to halt any I2C
+ * operation in progress and reset BUSY flag.
+ */
+ WR7146(P_I2CSTAT, I2C_CLKSEL | I2C_ABORT);
+ MC_ENABLE(P_MC2, MC2_UPLD_IIC);
+ while ((RR7146(P_MC2) & MC2_UPLD_IIC) == 0)
+ ;
+
+ /*
+ * Per SAA7146 data sheet, write to STATUS
+ * reg twice to reset all I2C error flags.
+ */
+ for (i = 0; i < 2; i++) {
+ WR7146(P_I2CSTAT, I2C_CLKSEL);
+ MC_ENABLE(P_MC2, MC2_UPLD_IIC);
+ while (!MC_TEST(P_MC2, MC2_UPLD_IIC))
+ ;
}
- devpriv->got_regions = 1;
- resourceStart = pci_resource_start(devpriv->pdev, 0);
+ /*
+ * Init audio interface functional attributes: set DAC/ADC
+ * serial clock rates, invert DAC serial clock so that
+ * DAC data setup times are satisfied, enable DAC serial
+ * clock out.
+ */
+ WR7146(P_ACON2, ACON2_INIT);
+
+ /*
+ * Set up TSL1 slot list, which is used to control the
+ * accumulation of ADC data: RSD1 = shift data in on SD1.
+ * SIB_A1 = store data uint8_t at next available location
+ * in FB BUFFER1 register.
+ */
+ WR7146(P_TSL1, RSD1 | SIB_A1);
+ WR7146(P_TSL1 + 4, RSD1 | SIB_A1 | EOS);
+
+ /* Enable TSL1 slot list so that it executes all the time */
+ WR7146(P_ACON1, ACON1_ADCSTART);
+
+ /*
+ * Initialize RPS registers used for ADC
+ */
+
+ /* Physical start of RPS program */
+ WR7146(P_RPSADDR1, (uint32_t)devpriv->RPSBuf.PhysicalBase);
+ /* RPS program performs no explicit mem writes */
+ WR7146(P_RPSPAGE1, 0);
+ /* Disable RPS timeouts */
+ WR7146(P_RPS1_TOUT, 0);
+
+#if 0
+ /*
+ * SAA7146 BUG WORKAROUND
+ *
+ * Initialize SAA7146 ADC interface to a known state by
+ * invoking ADCs until FB BUFFER 1 register shows that it
+ * is correctly receiving ADC data. This is necessary
+ * because the SAA7146 ADC interface does not start up in
+ * a defined state after a PCI reset.
+ */
+
+ {
+ uint8_t PollList;
+ uint16_t AdcData;
+ uint16_t StartVal;
+ uint16_t index;
+ unsigned int data[16];
+
+ /* Create a simple polling list for analog input channel 0 */
+ PollList = EOPL;
+ ResetADC(dev, &PollList);
- devpriv->base_addr = ioremap(resourceStart, SIZEOF_ADDRESS_SPACE);
- if (devpriv->base_addr == NULL) {
- printk(KERN_ERR "s626_attach: IOREMAP failed\n");
- return -ENODEV;
+ /* Get initial ADC value */
+ s626_ai_rinsn(dev, dev->subdevices, NULL, data);
+ StartVal = data[0];
+
+ /*
+ * VERSION 2.01 CHANGE: TIMEOUT ADDED TO PREVENT HANGED EXECUTION.
+ *
+ * Invoke ADCs until the new ADC value differs from the initial
+ * value or a timeout occurs. The timeout protects against the
+ * possibility that the driver is restarting and the ADC data is a
+ * fixed value resulting from the applied ADC analog input being
+ * unusually quiet or at the rail.
+ */
+ for (index = 0; index < 500; index++) {
+ s626_ai_rinsn(dev, dev->subdevices, NULL, data);
+ AdcData = data[0];
+ if (AdcData != StartVal)
+ break;
}
- if (devpriv->base_addr) {
- /* disable master interrupt */
- writel(0, devpriv->base_addr + P_IER);
+ }
+#endif /* SAA7146 BUG WORKAROUND */
- /* soft reset */
- writel(MC1_SOFT_RESET, devpriv->base_addr + P_MC1);
+ /*
+ * Initialize the DAC interface
+ */
- /* DMA FIXME DMA// */
+ /*
+ * Init Audio2's output DMAC attributes:
+ * burst length = 1 DWORD
+ * threshold = 1 DWORD.
+ */
+ WR7146(P_PCI_BT_A, 0);
- /* adc buffer allocation */
- devpriv->allocatedBuf = 0;
+ /*
+ * Init Audio2's output DMA physical addresses. The protection
+ * address is set to 1 DWORD past the base address so that a
+ * single DWORD will be transferred each time a DMA transfer is
+ * enabled.
+ */
+ pPhysBuf = devpriv->ANABuf.PhysicalBase +
+ (DAC_WDMABUF_OS * sizeof(uint32_t));
+ WR7146(P_BASEA2_OUT, (uint32_t) pPhysBuf);
+ WR7146(P_PROTA2_OUT, (uint32_t) (pPhysBuf + sizeof(uint32_t)));
- devpriv->ANABuf.LogicalBase =
- pci_alloc_consistent(devpriv->pdev, DMABUF_SIZE, &appdma);
+ /*
+ * Cache Audio2's output DMA buffer logical address. This is
+ * where DAC data is buffered for A2 output DMA transfers.
+ */
+ devpriv->pDacWBuf = (uint32_t *)devpriv->ANABuf.LogicalBase +
+ DAC_WDMABUF_OS;
- if (devpriv->ANABuf.LogicalBase == NULL) {
- printk(KERN_ERR "s626_attach: DMA Memory mapping error\n");
- return -ENOMEM;
- }
+ /*
+ * Audio2's output channels does not use paging. The
+ * protection violation handling bit is set so that the
+ * DMAC will automatically halt and its PCI address pointer
+ * will be reset when the protection address is reached.
+ */
+ WR7146(P_PAGEA2_OUT, 8);
- devpriv->ANABuf.PhysicalBase = appdma;
+ /*
+ * Initialize time slot list 2 (TSL2), which is used to control
+ * the clock generation for and serialization of data to be sent
+ * to the DAC devices. Slot 0 is a NOP that is used to trap TSL
+ * execution; this permits other slots to be safely modified
+ * without first turning off the TSL sequencer (which is
+ * apparently impossible to do). Also, SD3 (which is driven by a
+ * pull-up resistor) is shifted in and stored to the MSB of
+ * FB_BUFFER2 to be used as evidence that the slot sequence has
+ * not yet finished executing.
+ */
- devpriv->allocatedBuf++;
+ /* Slot 0: Trap TSL execution, shift 0xFF into FB_BUFFER2 */
+ SETVECT(0, XSD2 | RSD3 | SIB_A2 | EOS);
- devpriv->RPSBuf.LogicalBase =
- pci_alloc_consistent(devpriv->pdev, DMABUF_SIZE, &appdma);
+ /*
+ * Initialize slot 1, which is constant. Slot 1 causes a
+ * DWORD to be transferred from audio channel 2's output FIFO
+ * to the FIFO's output buffer so that it can be serialized
+ * and sent to the DAC during subsequent slots. All remaining
+ * slots are dynamically populated as required by the target
+ * DAC device.
+ */
- if (devpriv->RPSBuf.LogicalBase == NULL) {
- printk(KERN_ERR "s626_attach: DMA Memory mapping error\n");
- return -ENOMEM;
- }
+ /* Slot 1: Fetch DWORD from Audio2's output FIFO */
+ SETVECT(1, LF_A2);
+
+ /* Start DAC's audio interface (TSL2) running */
+ WR7146(P_ACON1, ACON1_DACSTART);
+
+ /*
+ * Init Trim DACs to calibrated values. Do it twice because the
+ * SAA7146 audio channel does not always reset properly and
+ * sometimes causes the first few TrimDAC writes to malfunction.
+ */
+ LoadTrimDACs(dev);
+ LoadTrimDACs(dev);
+
+ /*
+ * Manually init all gate array hardware in case this is a soft
+ * reset (we have no way of determining whether this is a warm
+ * or cold start). This is necessary because the gate array will
+ * reset only in response to a PCI hard reset; there is no soft
+ * reset function.
+ */
- devpriv->RPSBuf.PhysicalBase = appdma;
+ /*
+ * Init all DAC outputs to 0V and init all DAC setpoint and
+ * polarity images.
+ */
+ for (chan = 0; chan < S626_DAC_CHANNELS; chan++)
+ SetDAC(dev, chan, 0);
- devpriv->allocatedBuf++;
+ /* Init counters */
+ CountersInit(dev);
- }
+ /*
+ * Without modifying the state of the Battery Backup enab, disable
+ * the watchdog timer, set DIO channels 0-5 to operate in the
+ * standard DIO (vs. counter overflow) mode, disable the battery
+ * charger, and reset the watchdog interval selector to zero.
+ */
+ WriteMISC2(dev, (uint16_t)(DEBIread(dev, LP_RDMISC2) &
+ MISC2_BATT_ENABLE));
- dev->board_ptr = s626_boards;
- dev->board_name = thisboard->name;
+ /* Initialize the digital I/O subsystem */
+ s626_dio_init(dev);
- ret = comedi_alloc_subdevices(dev, 6);
+ /* enable interrupt test */
+ /* writel(IRQ_GPIO3 | IRQ_RPS1, devpriv->base_addr + P_IER); */
+}
+
+static int s626_attach_pci(struct comedi_device *dev, struct pci_dev *pcidev)
+{
+ struct comedi_subdevice *s;
+ int ret;
+
+ comedi_set_hw_dev(dev, &pcidev->dev);
+ dev->board_name = dev->driver->driver_name;
+
+ if (alloc_private(dev, sizeof(struct s626_private)) < 0)
+ return -ENOMEM;
+
+ ret = comedi_pci_enable(pcidev, dev->board_name);
if (ret)
return ret;
+ dev->iobase = 1; /* detach needs this */
- dev->iobase = (unsigned long)devpriv->base_addr;
- dev->irq = devpriv->pdev->irq;
+ devpriv->base_addr = ioremap(pci_resource_start(pcidev, 0),
+ pci_resource_len(pcidev, 0));
+ if (!devpriv->base_addr)
+ return -ENOMEM;
- /* set up interrupt handler */
- if (dev->irq == 0) {
- printk(KERN_ERR " unknown irq (bad)\n");
- } else {
- ret = request_irq(dev->irq, s626_irq_handler, IRQF_SHARED,
- "s626", dev);
+ /* disable master interrupt */
+ writel(0, devpriv->base_addr + P_IER);
- if (ret < 0) {
- printk(KERN_ERR " irq not available\n");
- dev->irq = 0;
- }
+ /* soft reset */
+ writel(MC1_SOFT_RESET, devpriv->base_addr + P_MC1);
+
+ /* DMA FIXME DMA// */
+
+ ret = s626_allocate_dma_buffers(dev);
+ if (ret)
+ return ret;
+
+ if (pcidev->irq) {
+ ret = request_irq(pcidev->irq, s626_irq_handler, IRQF_SHARED,
+ dev->board_name, dev);
+
+ if (ret == 0)
+ dev->irq = pcidev->irq;
}
+ ret = comedi_alloc_subdevices(dev, 6);
+ if (ret)
+ return ret;
+
s = dev->subdevices + 0;
/* analog input subdevice */
dev->read_subdev = s;
/* we support single-ended (ground) and differential */
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_DIFF | SDF_CMD_READ;
- s->n_chan = thisboard->ai_chans;
+ s->n_chan = S626_ADC_CHANNELS;
s->maxdata = (0xffff >> 2);
s->range_table = &s626_range_table;
- s->len_chanlist = thisboard->ai_chans; /* This is the maximum chanlist
- length that the board can
- handle */
+ s->len_chanlist = S626_ADC_CHANNELS;
s->insn_config = s626_ai_insn_config;
s->insn_read = s626_ai_insn_read;
s->do_cmd = s626_ai_cmd;
@@ -2626,7 +2730,7 @@ static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it)
/* analog output subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
- s->n_chan = thisboard->ao_chans;
+ s->n_chan = S626_DAC_CHANNELS;
s->maxdata = (0x3fff);
s->range_table = &range_bipolar10;
s->insn_write = s626_ao_winsn;
@@ -2672,7 +2776,7 @@ static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it)
/* encoder (counter) subdevice */
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE | SDF_LSAMPL;
- s->n_chan = thisboard->enc_chans;
+ s->n_chan = S626_ENCODER_CHANNELS;
s->private = enc_private_data;
s->insn_config = s626_enc_insn_config;
s->insn_read = s626_enc_insn_read;
@@ -2680,269 +2784,17 @@ static int s626_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->maxdata = 0xffffff;
s->range_table = &range_unknown;
- /* stop ai_command */
- devpriv->ai_cmd_running = 0;
-
- if (devpriv->base_addr && (devpriv->allocatedBuf == 2)) {
- dma_addr_t pPhysBuf;
- uint16_t chan;
-
- /* enab DEBI and audio pins, enable I2C interface. */
- MC_ENABLE(P_MC1, MC1_DEBI | MC1_AUDIO | MC1_I2C);
- /* Configure DEBI operating mode. */
- WR7146(P_DEBICFG, DEBI_CFG_SLAVE16 /* Local bus is 16 */
- /* bits wide. */
- | (DEBI_TOUT << DEBI_CFG_TOUT_BIT)
-
- /* Declare DEBI */
- /* transfer timeout */
- /* interval. */
- |DEBI_SWAP /* Set up byte lane */
- /* steering. */
- | DEBI_CFG_INTEL); /* Intel-compatible */
- /* local bus (DEBI */
- /* never times out). */
-
- /* DEBI INIT S626 WR7146( P_DEBICFG, DEBI_CFG_INTEL | DEBI_CFG_TOQ */
- /* | DEBI_CFG_INCQ| DEBI_CFG_16Q); //end */
-
- /* Paging is disabled. */
- WR7146(P_DEBIPAGE, DEBI_PAGE_DISABLE); /* Disable MMU paging. */
-
- /* Init GPIO so that ADC Start* is negated. */
- WR7146(P_GPIO, GPIO_BASE | GPIO1_HI);
-
- /* IsBoardRevA is a boolean that indicates whether the board is RevA.
- *
- * VERSION 2.01 CHANGE: REV A & B BOARDS NOW SUPPORTED BY DYNAMIC
- * EEPROM ADDRESS SELECTION. Initialize the I2C interface, which
- * is used to access the onboard serial EEPROM. The EEPROM's I2C
- * DeviceAddress is hardwired to a value that is dependent on the
- * 626 board revision. On all board revisions, the EEPROM stores
- * TrimDAC calibration constants for analog I/O. On RevB and
- * higher boards, the DeviceAddress is hardwired to 0 to enable
- * the EEPROM to also store the PCI SubVendorID and SubDeviceID;
- * this is the address at which the SAA7146 expects a
- * configuration EEPROM to reside. On RevA boards, the EEPROM
- * device address, which is hardwired to 4, prevents the SAA7146
- * from retrieving PCI sub-IDs, so the SAA7146 uses its built-in
- * default values, instead.
- */
-
- /* devpriv->I2Cards= IsBoardRevA ? 0xA8 : 0xA0; // Set I2C EEPROM */
- /* DeviceType (0xA0) */
- /* and DeviceAddress<<1. */
-
- devpriv->I2CAdrs = 0xA0; /* I2C device address for onboard */
- /* eeprom(revb) */
+ s626_initialize(dev);
- /* Issue an I2C ABORT command to halt any I2C operation in */
- /* progress and reset BUSY flag. */
- WR7146(P_I2CSTAT, I2C_CLKSEL | I2C_ABORT);
- /* Write I2C control: abort any I2C activity. */
- MC_ENABLE(P_MC2, MC2_UPLD_IIC);
- /* Invoke command upload */
- while ((RR7146(P_MC2) & MC2_UPLD_IIC) == 0)
- ;
- /* and wait for upload to complete. */
-
- /* Per SAA7146 data sheet, write to STATUS reg twice to
- * reset all I2C error flags. */
- for (i = 0; i < 2; i++) {
- WR7146(P_I2CSTAT, I2C_CLKSEL);
- /* Write I2C control: reset error flags. */
- MC_ENABLE(P_MC2, MC2_UPLD_IIC); /* Invoke command upload */
- while (!MC_TEST(P_MC2, MC2_UPLD_IIC))
- ;
- /* and wait for upload to complete. */
- }
-
- /* Init audio interface functional attributes: set DAC/ADC
- * serial clock rates, invert DAC serial clock so that
- * DAC data setup times are satisfied, enable DAC serial
- * clock out.
- */
+ dev_info(dev->class_dev, "%s attached\n", dev->board_name);
- WR7146(P_ACON2, ACON2_INIT);
-
- /* Set up TSL1 slot list, which is used to control the
- * accumulation of ADC data: RSD1 = shift data in on SD1.
- * SIB_A1 = store data uint8_t at next available location in
- * FB BUFFER1 register. */
- WR7146(P_TSL1, RSD1 | SIB_A1);
- /* Fetch ADC high data uint8_t. */
- WR7146(P_TSL1 + 4, RSD1 | SIB_A1 | EOS);
- /* Fetch ADC low data uint8_t; end of TSL1. */
-
- /* enab TSL1 slot list so that it executes all the time. */
- WR7146(P_ACON1, ACON1_ADCSTART);
-
- /* Initialize RPS registers used for ADC. */
-
- /* Physical start of RPS program. */
- WR7146(P_RPSADDR1, (uint32_t) devpriv->RPSBuf.PhysicalBase);
-
- WR7146(P_RPSPAGE1, 0);
- /* RPS program performs no explicit mem writes. */
- WR7146(P_RPS1_TOUT, 0); /* Disable RPS timeouts. */
-
- /* SAA7146 BUG WORKAROUND. Initialize SAA7146 ADC interface
- * to a known state by invoking ADCs until FB BUFFER 1
- * register shows that it is correctly receiving ADC data.
- * This is necessary because the SAA7146 ADC interface does
- * not start up in a defined state after a PCI reset.
- */
-
-/* PollList = EOPL; // Create a simple polling */
-/* // list for analog input */
-/* // channel 0. */
-/* ResetADC( dev, &PollList ); */
-
-/* s626_ai_rinsn(dev,dev->subdevices,NULL,data); //( &AdcData ); // */
-/* //Get initial ADC */
-/* //value. */
-
-/* StartVal = data[0]; */
-
-/* // VERSION 2.01 CHANGE: TIMEOUT ADDED TO PREVENT HANGED EXECUTION. */
-/* // Invoke ADCs until the new ADC value differs from the initial */
-/* // value or a timeout occurs. The timeout protects against the */
-/* // possibility that the driver is restarting and the ADC data is a */
-/* // fixed value resulting from the applied ADC analog input being */
-/* // unusually quiet or at the rail. */
-
-/* for ( index = 0; index < 500; index++ ) */
-/* { */
-/* s626_ai_rinsn(dev,dev->subdevices,NULL,data); */
-/* AdcData = data[0]; //ReadADC( &AdcData ); */
-/* if ( AdcData != StartVal ) */
-/* break; */
-/* } */
-
- /* end initADC */
-
- /* init the DAC interface */
-
- /* Init Audio2's output DMAC attributes: burst length = 1
- * DWORD, threshold = 1 DWORD.
- */
- WR7146(P_PCI_BT_A, 0);
-
- /* Init Audio2's output DMA physical addresses. The protection
- * address is set to 1 DWORD past the base address so that a
- * single DWORD will be transferred each time a DMA transfer is
- * enabled. */
-
- pPhysBuf =
- devpriv->ANABuf.PhysicalBase +
- (DAC_WDMABUF_OS * sizeof(uint32_t));
-
- WR7146(P_BASEA2_OUT, (uint32_t) pPhysBuf); /* Buffer base adrs. */
- WR7146(P_PROTA2_OUT, (uint32_t) (pPhysBuf + sizeof(uint32_t))); /* Protection address. */
-
- /* Cache Audio2's output DMA buffer logical address. This is
- * where DAC data is buffered for A2 output DMA transfers. */
- devpriv->pDacWBuf =
- (uint32_t *) devpriv->ANABuf.LogicalBase + DAC_WDMABUF_OS;
-
- /* Audio2's output channels does not use paging. The protection
- * violation handling bit is set so that the DMAC will
- * automatically halt and its PCI address pointer will be reset
- * when the protection address is reached. */
-
- WR7146(P_PAGEA2_OUT, 8);
-
- /* Initialize time slot list 2 (TSL2), which is used to control
- * the clock generation for and serialization of data to be sent
- * to the DAC devices. Slot 0 is a NOP that is used to trap TSL
- * execution; this permits other slots to be safely modified
- * without first turning off the TSL sequencer (which is
- * apparently impossible to do). Also, SD3 (which is driven by a
- * pull-up resistor) is shifted in and stored to the MSB of
- * FB_BUFFER2 to be used as evidence that the slot sequence has
- * not yet finished executing.
- */
-
- SETVECT(0, XSD2 | RSD3 | SIB_A2 | EOS);
- /* Slot 0: Trap TSL execution, shift 0xFF into FB_BUFFER2. */
-
- /* Initialize slot 1, which is constant. Slot 1 causes a
- * DWORD to be transferred from audio channel 2's output FIFO
- * to the FIFO's output buffer so that it can be serialized
- * and sent to the DAC during subsequent slots. All remaining
- * slots are dynamically populated as required by the target
- * DAC device.
- */
- SETVECT(1, LF_A2);
- /* Slot 1: Fetch DWORD from Audio2's output FIFO. */
-
- /* Start DAC's audio interface (TSL2) running. */
- WR7146(P_ACON1, ACON1_DACSTART);
-
- /* end init DAC interface */
-
- /* Init Trim DACs to calibrated values. Do it twice because the
- * SAA7146 audio channel does not always reset properly and
- * sometimes causes the first few TrimDAC writes to malfunction.
- */
-
- LoadTrimDACs(dev);
- LoadTrimDACs(dev); /* Insurance. */
-
- /* Manually init all gate array hardware in case this is a soft
- * reset (we have no way of determining whether this is a warm
- * or cold start). This is necessary because the gate array will
- * reset only in response to a PCI hard reset; there is no soft
- * reset function. */
-
- /* Init all DAC outputs to 0V and init all DAC setpoint and
- * polarity images.
- */
- for (chan = 0; chan < S626_DAC_CHANNELS; chan++)
- SetDAC(dev, chan, 0);
-
- /* Init image of WRMISC2 Battery Charger Enabled control bit.
- * This image is used when the state of the charger control bit,
- * which has no direct hardware readback mechanism, is queried.
- */
- devpriv->ChargeEnabled = 0;
-
- /* Init image of watchdog timer interval in WRMISC2. This image
- * maintains the value of the control bits of MISC2 are
- * continuously reset to zero as long as the WD timer is disabled.
- */
- devpriv->WDInterval = 0;
-
- /* Init Counter Interrupt enab mask for RDMISC2. This mask is
- * applied against MISC2 when testing to determine which timer
- * events are requesting interrupt service.
- */
- devpriv->CounterIntEnabs = 0;
-
- /* Init counters. */
- CountersInit(dev);
-
- /* Without modifying the state of the Battery Backup enab, disable
- * the watchdog timer, set DIO channels 0-5 to operate in the
- * standard DIO (vs. counter overflow) mode, disable the battery
- * charger, and reset the watchdog interval selector to zero.
- */
- WriteMISC2(dev, (uint16_t) (DEBIread(dev,
- LP_RDMISC2) &
- MISC2_BATT_ENABLE));
-
- /* Initialize the digital I/O subsystem. */
- s626_dio_init(dev);
-
- /* enable interrupt test */
- /* writel(IRQ_GPIO3 | IRQ_RPS1,devpriv->base_addr+P_IER); */
- }
-
- return 1;
+ return 0;
}
static void s626_detach(struct comedi_device *dev)
{
+ struct pci_dev *pcidev = comedi_to_pci_dev(dev);
+
if (devpriv) {
/* stop ai_command */
devpriv->ai_cmd_running = 0;
@@ -2967,18 +2819,17 @@ static void s626_detach(struct comedi_device *dev)
free_irq(dev->irq, dev);
if (devpriv->base_addr)
iounmap(devpriv->base_addr);
- if (devpriv->pdev) {
- if (devpriv->got_regions)
- comedi_pci_disable(devpriv->pdev);
- pci_dev_put(devpriv->pdev);
- }
+ }
+ if (pcidev) {
+ if (dev->iobase)
+ comedi_pci_disable(pcidev);
}
}
static struct comedi_driver s626_driver = {
.driver_name = "s626",
.module = THIS_MODULE,
- .attach = s626_attach,
+ .attach_pci = s626_attach_pci,
.detach = s626_detach,
};
diff --git a/drivers/staging/comedi/drivers/s626.h b/drivers/staging/comedi/drivers/s626.h
index 8a8f196cf153..ff4b3a5e4e5f 100644
--- a/drivers/staging/comedi/drivers/s626.h
+++ b/drivers/staging/comedi/drivers/s626.h
@@ -73,7 +73,6 @@
#include <linux/slab.h>
#define S626_SIZE 0x0200
-#define SIZEOF_ADDRESS_SPACE 0x0200
#define DMABUF_SIZE 4096 /* 4k pages */
#define S626_ADC_CHANNELS 16
diff --git a/drivers/staging/comedi/drivers/serial2002.c b/drivers/staging/comedi/drivers/serial2002.c
index c18314be8c82..5bf84cfbdceb 100644
--- a/drivers/staging/comedi/drivers/serial2002.c
+++ b/drivers/staging/comedi/drivers/serial2002.c
@@ -588,7 +588,9 @@ static int serial_2002_open(struct comedi_device *dev)
kfree(s->range_table_list);
s->range_table = NULL;
s->range_table_list = NULL;
- if (range) {
+ if (kind == 1 || kind == 2) {
+ s->range_table = &range_digital;
+ } else if (range) {
s->range_table_list = range_table_list =
kmalloc(sizeof
(struct
@@ -798,7 +800,7 @@ static int serial2002_attach(struct comedi_device *dev,
return ret;
/* digital input subdevice */
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = 0;
@@ -807,7 +809,7 @@ static int serial2002_attach(struct comedi_device *dev,
s->insn_read = &serial2002_di_rinsn;
/* digital output subdevice */
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITEABLE;
s->n_chan = 0;
@@ -816,7 +818,7 @@ static int serial2002_attach(struct comedi_device *dev,
s->insn_write = &serial2002_do_winsn;
/* analog input subdevice */
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
s->n_chan = 0;
@@ -825,7 +827,7 @@ static int serial2002_attach(struct comedi_device *dev,
s->insn_read = &serial2002_ai_rinsn;
/* analog output subdevice */
- s = dev->subdevices + 3;
+ s = &dev->subdevices[3];
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITEABLE;
s->n_chan = 0;
@@ -835,7 +837,7 @@ static int serial2002_attach(struct comedi_device *dev,
s->insn_read = &serial2002_ao_rinsn;
/* encoder input subdevice */
- s = dev->subdevices + 4;
+ s = &dev->subdevices[4];
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE | SDF_LSAMPL;
s->n_chan = 0;
diff --git a/drivers/staging/comedi/drivers/skel.c b/drivers/staging/comedi/drivers/skel.c
index 9a68eebefca0..b70cdf300bbd 100644
--- a/drivers/staging/comedi/drivers/skel.c
+++ b/drivers/staging/comedi/drivers/skel.c
@@ -76,6 +76,8 @@ Configuration Options:
#include <linux/pci.h> /* for PCI devices */
+#include "comedi_fc.h"
+
/* Imaginary registers for the imaginary board */
#define SKEL_SIZE 0
@@ -238,7 +240,7 @@ static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it)
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* dev->read_subdev=s; */
/* analog input subdevice */
s->type = COMEDI_SUBD_AI;
@@ -256,7 +258,7 @@ static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it)
*/
s->do_cmdtest = skel_ai_cmdtest;
- s = dev->subdevices + 1;
+ s = &dev->subdevices[1];
/* analog output subdevice */
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITABLE;
@@ -266,7 +268,7 @@ static int skel_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_write = skel_ao_winsn;
s->insn_read = skel_ao_rinsn;
- s = dev->subdevices + 2;
+ s = &dev->subdevices[2];
/* digital i/o subdevice */
if (thisboard->have_dio) {
s->type = COMEDI_SUBD_DIO;
@@ -349,60 +351,40 @@ static int skel_ai_rinsn(struct comedi_device *dev, struct comedi_subdevice *s,
return n;
}
+/*
+ * cmdtest tests a particular command to see if it is valid.
+ * Using the cmdtest ioctl, a user can create a valid cmd
+ * and then have it executes by the cmd ioctl.
+ *
+ * cmdtest returns 1,2,3,4 or 0, depending on which tests
+ * the command passes.
+ */
static int skel_ai_cmdtest(struct comedi_device *dev,
- struct comedi_subdevice *s, struct comedi_cmd *cmd)
+ struct comedi_subdevice *s,
+ struct comedi_cmd *cmd)
{
int err = 0;
int tmp;
- /* cmdtest tests a particular command to see if it is valid.
- * Using the cmdtest ioctl, a user can create a valid cmd
- * and then have it executes by the cmd ioctl.
- *
- * cmdtest returns 1,2,3,4 or 0, depending on which tests
- * the command passes. */
-
- /* step 1: make sure trigger sources are trivially valid */
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /* step 2: make sure trigger sources are unique and mutually compatible
- */
+ /* Step 2a : make sure trigger sources are unique */
- /* note that mutual compatibility is not an issue here */
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
diff --git a/drivers/staging/comedi/drivers/ssv_dnp.c b/drivers/staging/comedi/drivers/ssv_dnp.c
index 84b9f2a4280b..ae3aa1c5caef 100644
--- a/drivers/staging/comedi/drivers/ssv_dnp.c
+++ b/drivers/staging/comedi/drivers/ssv_dnp.c
@@ -177,15 +177,13 @@ static int dnp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
struct comedi_subdevice *s;
int ret;
- printk(KERN_INFO "comedi%d: dnp: ", dev->minor);
-
dev->board_name = board->name;
ret = comedi_alloc_subdevices(dev, 1);
if (ret)
return ret;
- s = dev->subdevices + 0;
+ s = &dev->subdevices[0];
/* digital i/o subdevice */
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
@@ -195,8 +193,6 @@ static int dnp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
s->insn_bits = dnp_dio_insn_bits;
s->insn_config = dnp_dio_insn_config;
- printk("attached\n");
-
/* We use the I/O ports 0x22,0x23 and 0xa3-0xa9, which are always
* allocated for the primary 8259, so we don't need to allocate them
* ourselves. */
@@ -209,6 +205,7 @@ static int dnp_attach(struct comedi_device *dev, struct comedi_devconfig *it)
outb(PCMR, CSCIR);
outb((inb(CSCDR) & 0xAA), CSCDR);
+ dev_info(dev->class_dev, "%s: attached\n", dev->board_name);
return 1;
}
diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c
index 11ee83681da7..b536bba74351 100644
--- a/drivers/staging/comedi/drivers/usbdux.c
+++ b/drivers/staging/comedi/drivers/usbdux.c
@@ -98,6 +98,8 @@ sampling rate. If you sample two channels you get 4kHz and so on.
#include "../comedidev.h"
+#include "comedi_fc.h"
+
/* timeout for the USB-transfer in ms*/
#define BULK_TIMEOUT 1000
@@ -404,7 +406,7 @@ static void usbduxsub_ai_IsocIrq(struct urb *urb)
/* the private structure of the subdevice is struct usbduxsub */
this_usbduxsub = this_comedidev->private;
/* subdevice which is the AD converter */
- s = this_comedidev->subdevices + SUBDEV_AD;
+ s = &this_comedidev->subdevices[SUBDEV_AD];
/* first we test if something unusual has just happened */
switch (urb->status) {
@@ -604,7 +606,7 @@ static void usbduxsub_ao_IsocIrq(struct urb *urb)
/* the private structure of the subdevice is struct usbduxsub */
this_usbduxsub = this_comedidev->private;
- s = this_comedidev->subdevices + SUBDEV_DA;
+ s = &this_comedidev->subdevices[SUBDEV_DA];
switch (urb->status) {
case 0:
@@ -929,9 +931,9 @@ static int usbduxsub_submit_OutURBs(struct usbduxsub *usbduxsub)
static int usbdux_ai_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
- int err = 0, tmp, i;
- unsigned int tmpTimer;
struct usbduxsub *this_usbduxsub = dev->private;
+ int err = 0, i;
+ unsigned int tmpTimer;
if (!(this_usbduxsub->probed))
return -ENODEV;
@@ -939,51 +941,23 @@ static int usbdux_ai_cmdtest(struct comedi_device *dev,
dev_dbg(&this_usbduxsub->interface->dev,
"comedi%d: usbdux_ai_cmdtest\n", dev->minor);
- /* make sure triggers are valid */
- /* Only immediate triggers are allowed */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_INT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- /* trigger should happen timed */
- tmp = cmd->scan_begin_src;
- /* start a new _scan_ with a timer */
- cmd->scan_begin_src &= TRIG_TIMER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- /* scanning is continuous */
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- /* issue a trigger when scan is finished and start a new scan */
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- /* trigger at the end of count events or not, stop condition or not */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources are unique and mutually compatible
- * note that mutual compatibility is not an issue here
- */
- if (cmd->scan_begin_src != TRIG_FOLLOW &&
- cmd->scan_begin_src != TRIG_EXT &&
- cmd->scan_begin_src != TRIG_TIMER)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1488,8 +1462,9 @@ static int usbdux_ao_inttrig(struct comedi_device *dev,
static int usbdux_ao_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s, struct comedi_cmd *cmd)
{
- int err = 0, tmp;
struct usbduxsub *this_usbduxsub = dev->private;
+ int err = 0;
+ unsigned int flags;
if (!this_usbduxsub)
return -EFAULT;
@@ -1500,69 +1475,46 @@ static int usbdux_ao_cmdtest(struct comedi_device *dev,
dev_dbg(&this_usbduxsub->interface->dev,
"comedi%d: usbdux_ao_cmdtest\n", dev->minor);
- /* make sure triggers are valid */
- /* Only immediate triggers are allowed */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_INT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
- /* trigger should happen timed */
- tmp = cmd->scan_begin_src;
- /* just now we scan also in the high speed mode every frame */
- /* this is due to ehci driver limitations */
if (0) { /* (this_usbduxsub->high_speed) */
- /* start immediately a new scan */
/* the sampling rate is set by the coversion rate */
- cmd->scan_begin_src &= TRIG_FOLLOW;
+ flags = TRIG_FOLLOW;
} else {
/* start a new scan (output at once) with a timer */
- cmd->scan_begin_src &= TRIG_TIMER;
+ flags = TRIG_TIMER;
}
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, flags);
- /* scanning is continuous */
- tmp = cmd->convert_src;
- /* we always output at 1kHz just now all channels at once */
if (0) { /* (this_usbduxsub->high_speed) */
/*
- * in usb-2.0 only one conversion it transmitted but with 8kHz/n
+ * in usb-2.0 only one conversion it transmitted
+ * but with 8kHz/n
*/
- cmd->convert_src &= TRIG_TIMER;
+ flags = TRIG_TIMER;
} else {
- /* all conversion events happen simultaneously with a rate of
- * 1kHz/n */
- cmd->convert_src &= TRIG_NOW;
+ /*
+ * all conversion events happen simultaneously with
+ * a rate of 1kHz/n
+ */
+ flags = TRIG_NOW;
}
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- /* issue a trigger when scan is finished and start a new scan */
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->convert_src, flags);
- /* trigger at the end of count events or not, stop condition or not */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources are unique and mutually compatible
- * note that mutual compatibility is not an issue here
- */
- if (cmd->scan_begin_src != TRIG_FOLLOW &&
- cmd->scan_begin_src != TRIG_EXT &&
- cmd->scan_begin_src != TRIG_TIMER)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1947,7 +1899,7 @@ static void usbduxsub_pwm_irq(struct urb *urb)
/* the private structure of the subdevice is struct usbduxsub */
this_usbduxsub = this_comedidev->private;
- s = this_comedidev->subdevices + SUBDEV_DA;
+ s = &this_comedidev->subdevices[SUBDEV_DA];
switch (urb->status) {
case 0:
@@ -2293,10 +2245,8 @@ static void tidy_up(struct usbduxsub *usbduxsub_tmp)
usbduxsub_tmp->pwm_cmd_running = 0;
}
-/* common part of attach and attach_usb */
static int usbdux_attach_common(struct comedi_device *dev,
- struct usbduxsub *udev,
- void *aux_data, int aux_len)
+ struct usbduxsub *udev)
{
int ret;
struct comedi_subdevice *s = NULL;
@@ -2306,10 +2256,6 @@ static int usbdux_attach_common(struct comedi_device *dev,
/* pointer back to the corresponding comedi device */
udev->comedidev = dev;
- /* trying to upload the firmware into the chip */
- if (aux_data)
- firmwareUpload(udev, aux_data, aux_len);
-
dev->board_name = "usbdux";
/* set number of subdevices */
@@ -2331,7 +2277,7 @@ static int usbdux_attach_common(struct comedi_device *dev,
dev->private = udev;
/* the first subdevice is the A/D converter */
- s = dev->subdevices + SUBDEV_AD;
+ s = &dev->subdevices[SUBDEV_AD];
/* the URBs get the comedi subdevice */
/* which is responsible for reading */
/* this is the subdevice which reads data */
@@ -2358,7 +2304,7 @@ static int usbdux_attach_common(struct comedi_device *dev,
s->range_table = (&range_usbdux_ai_range);
/* analog out */
- s = dev->subdevices + SUBDEV_DA;
+ s = &dev->subdevices[SUBDEV_DA];
/* analog out */
s->type = COMEDI_SUBD_AO;
/* backward pointer */
@@ -2384,7 +2330,7 @@ static int usbdux_attach_common(struct comedi_device *dev,
s->insn_write = usbdux_ao_insn_write;
/* digital I/O */
- s = dev->subdevices + SUBDEV_DIO;
+ s = &dev->subdevices[SUBDEV_DIO];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 8;
@@ -2396,7 +2342,7 @@ static int usbdux_attach_common(struct comedi_device *dev,
s->private = NULL;
/* counter */
- s = dev->subdevices + SUBDEV_COUNTER;
+ s = &dev->subdevices[SUBDEV_COUNTER];
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_WRITABLE | SDF_READABLE;
s->n_chan = 4;
@@ -2407,7 +2353,7 @@ static int usbdux_attach_common(struct comedi_device *dev,
if (udev->high_speed) {
/* timer / pwm */
- s = dev->subdevices + SUBDEV_PWM;
+ s = &dev->subdevices[SUBDEV_PWM];
s->type = COMEDI_SUBD_PWM;
s->subdev_flags = SDF_WRITABLE | SDF_PWM_HBRIDGE;
s->n_chan = 8;
@@ -2429,48 +2375,6 @@ static int usbdux_attach_common(struct comedi_device *dev,
return 0;
}
-/* is called when comedi-config is called */
-static int usbdux_attach(struct comedi_device *dev, struct comedi_devconfig *it)
-{
- int ret;
- int index;
- int i;
- void *aux_data;
- int aux_len;
-
- dev->private = NULL;
-
- aux_data = comedi_aux_data(it->options, 0);
- aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
- if (aux_data == NULL)
- aux_len = 0;
- else if (aux_len == 0)
- aux_data = NULL;
-
- down(&start_stop_sem);
- /* find a valid device which has been detected by the probe function of
- * the usb */
- index = -1;
- for (i = 0; i < NUMUSBDUX; i++) {
- if ((usbduxsub[i].probed) && (!usbduxsub[i].attached)) {
- index = i;
- break;
- }
- }
-
- if (index < 0) {
- printk(KERN_ERR
- "comedi%d: usbdux: error: attach failed, no usbdux devs connected to the usb bus.\n",
- dev->minor);
- ret = -ENODEV;
- } else
- ret = usbdux_attach_common(dev, &usbduxsub[index],
- aux_data, aux_len);
- up(&start_stop_sem);
- return ret;
-}
-
-/* is called from comedi_usb_auto_config() */
static int usbdux_attach_usb(struct comedi_device *dev,
struct usb_interface *uinterf)
{
@@ -2492,7 +2396,7 @@ static int usbdux_attach_usb(struct comedi_device *dev,
dev->minor);
ret = -ENODEV;
} else
- ret = usbdux_attach_common(dev, this_usbduxsub, NULL, 0);
+ ret = usbdux_attach_common(dev, this_usbduxsub);
up(&start_stop_sem);
return ret;
}
@@ -2513,9 +2417,8 @@ static void usbdux_detach(struct comedi_device *dev)
static struct comedi_driver usbdux_driver = {
.driver_name = "usbdux",
.module = THIS_MODULE,
- .attach = usbdux_attach,
- .detach = usbdux_detach,
.attach_usb = usbdux_attach_usb,
+ .detach = usbdux_detach,
};
static void usbdux_firmware_request_complete_handler(const struct firmware *fw,
diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c
index 8eb41257c6ce..1154a7e2895d 100644
--- a/drivers/staging/comedi/drivers/usbduxfast.c
+++ b/drivers/staging/comedi/drivers/usbduxfast.c
@@ -346,7 +346,7 @@ static void usbduxfastsub_ai_Irq(struct urb *urb)
return;
}
/* subdevice which is the AD converter */
- s = this_comedidev->subdevices + SUBDEV_AD;
+ s = &this_comedidev->subdevices[SUBDEV_AD];
/* first we test if something unusual has just happened */
switch (urb->status) {
@@ -549,10 +549,10 @@ static int usbduxfast_ai_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
- int err = 0, stop_mask = 0;
+ struct usbduxfastsub_s *udfs = dev->private;
+ int err = 0;
long int steps, tmp;
int minSamplPer;
- struct usbduxfastsub_s *udfs = dev->private;
if (!udfs->probed)
return -ENODEV;
@@ -563,57 +563,31 @@ static int usbduxfast_ai_cmdtest(struct comedi_device *dev,
"scan_begin_arg=%u\n",
dev->minor, cmd->convert_arg, cmd->scan_begin_arg);
#endif
- /* step 1: make sure trigger sources are trivially valid */
-
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_EXT | TRIG_INT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- tmp = cmd->scan_begin_src;
- cmd->scan_begin_src &= TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_TIMER | TRIG_EXT;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- tmp = cmd->stop_src;
- stop_mask = TRIG_COUNT | TRIG_NONE;
- cmd->stop_src &= stop_mask;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src,
+ TRIG_NOW | TRIG_EXT | TRIG_INT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src,
+ TRIG_TIMER | TRIG_FOLLOW | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_TIMER | TRIG_EXT);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources are unique and mutually compatible
- */
+ /* Step 2a : make sure trigger sources are unique */
- if (cmd->start_src != TRIG_NOW &&
- cmd->start_src != TRIG_EXT && cmd->start_src != TRIG_INT)
- err++;
- if (cmd->scan_begin_src != TRIG_TIMER &&
- cmd->scan_begin_src != TRIG_FOLLOW &&
- cmd->scan_begin_src != TRIG_EXT)
- err++;
- if (cmd->convert_src != TRIG_TIMER && cmd->convert_src != TRIG_EXT)
- err++;
- if (cmd->stop_src != TRIG_COUNT &&
- cmd->stop_src != TRIG_EXT && cmd->stop_src != TRIG_NONE)
- err++;
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->scan_begin_src);
+ err |= cfc_check_trigger_is_unique(cmd->convert_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
/* can't have external stop and start triggers at once */
if (cmd->start_src == TRIG_EXT && cmd->stop_src == TRIG_EXT)
- err++;
+ err |= -EINVAL;
if (err)
return 2;
@@ -1430,10 +1404,8 @@ static void tidy_up(struct usbduxfastsub_s *udfs)
udfs->ai_cmd_running = 0;
}
-/* common part of attach and attach_usb */
static int usbduxfast_attach_common(struct comedi_device *dev,
- struct usbduxfastsub_s *udfs,
- void *aux_data, int aux_len)
+ struct usbduxfastsub_s *udfs)
{
int ret;
struct comedi_subdevice *s;
@@ -1441,9 +1413,6 @@ static int usbduxfast_attach_common(struct comedi_device *dev,
down(&udfs->sem);
/* pointer back to the corresponding comedi device */
udfs->comedidev = dev;
- /* trying to upload the firmware into the chip */
- if (aux_data)
- firmwareUpload(udfs, aux_data, aux_len);
dev->board_name = "usbduxfast";
ret = comedi_alloc_subdevices(dev, 1);
if (ret) {
@@ -1453,7 +1422,7 @@ static int usbduxfast_attach_common(struct comedi_device *dev,
/* private structure is also simply the usb-structure */
dev->private = udfs;
/* the first subdevice is the A/D converter */
- s = dev->subdevices + SUBDEV_AD;
+ s = &dev->subdevices[SUBDEV_AD];
/*
* the URBs get the comedi subdevice which is responsible for reading
* this is the subdevice which reads data
@@ -1485,48 +1454,6 @@ static int usbduxfast_attach_common(struct comedi_device *dev,
return 0;
}
-/* is called for COMEDI_DEVCONFIG ioctl (when comedi_config is run) */
-static int usbduxfast_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- int ret;
- int index;
- int i;
- void *aux_data;
- int aux_len;
-
- dev->private = NULL;
-
- aux_data = comedi_aux_data(it->options, 0);
- aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
- if (aux_data == NULL)
- aux_len = 0;
- else if (aux_len == 0)
- aux_data = NULL;
- down(&start_stop_sem);
- /*
- * find a valid device which has been detected by the
- * probe function of the usb
- */
- index = -1;
- for (i = 0; i < NUMUSBDUXFAST; i++) {
- if (usbduxfastsub[i].probed && !usbduxfastsub[i].attached) {
- index = i;
- break;
- }
- }
- if (index < 0) {
- dev_err(dev->class_dev,
- "usbduxfast: error: attach failed, no usbduxfast devs connected to the usb bus.\n");
- ret = -ENODEV;
- } else
- ret = usbduxfast_attach_common(dev, &usbduxfastsub[index],
- aux_data, aux_len);
- up(&start_stop_sem);
- return ret;
-}
-
-/* is called from comedi_usb_auto_config() */
static int usbduxfast_attach_usb(struct comedi_device *dev,
struct usb_interface *uinterf)
{
@@ -1545,7 +1472,7 @@ static int usbduxfast_attach_usb(struct comedi_device *dev,
"usbduxfast: error: attach_usb failed, already attached\n");
ret = -ENODEV;
} else
- ret = usbduxfast_attach_common(dev, udfs, NULL, 0);
+ ret = usbduxfast_attach_common(dev, udfs);
up(&start_stop_sem);
return ret;
}
@@ -1568,9 +1495,8 @@ static void usbduxfast_detach(struct comedi_device *dev)
static struct comedi_driver usbduxfast_driver = {
.driver_name = "usbduxfast",
.module = THIS_MODULE,
- .attach = usbduxfast_attach,
- .detach = usbduxfast_detach,
.attach_usb = usbduxfast_attach_usb,
+ .detach = usbduxfast_detach,
};
static void usbduxfast_firmware_request_complete_handler(const struct firmware
diff --git a/drivers/staging/comedi/drivers/usbduxsigma.c b/drivers/staging/comedi/drivers/usbduxsigma.c
index f54ab8c2fcfd..b1694121f845 100644
--- a/drivers/staging/comedi/drivers/usbduxsigma.c
+++ b/drivers/staging/comedi/drivers/usbduxsigma.c
@@ -356,7 +356,7 @@ static void usbduxsub_ai_IsocIrq(struct urb *urb)
/* the private structure of the subdevice is struct usbduxsub */
this_usbduxsub = this_comedidev->private;
/* subdevice which is the AD converter */
- s = this_comedidev->subdevices + SUBDEV_AD;
+ s = &this_comedidev->subdevices[SUBDEV_AD];
/* first we test if something unusual has just happened */
switch (urb->status) {
@@ -558,7 +558,7 @@ static void usbduxsub_ao_IsocIrq(struct urb *urb)
/* the private structure of the subdevice is struct usbduxsub */
this_usbduxsub = this_comedidev->private;
- s = this_comedidev->subdevices + SUBDEV_DA;
+ s = &this_comedidev->subdevices[SUBDEV_DA];
switch (urb->status) {
case 0:
@@ -897,9 +897,9 @@ static int usbdux_ai_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
- int err = 0, tmp, i;
- unsigned int tmpTimer;
struct usbduxsub *this_usbduxsub = dev->private;
+ int err = 0, i;
+ unsigned int tmpTimer;
if (!(this_usbduxsub->probed))
return -ENODEV;
@@ -907,51 +907,23 @@ static int usbdux_ai_cmdtest(struct comedi_device *dev,
dev_dbg(&this_usbduxsub->interface->dev,
"comedi%d: usbdux_ai_cmdtest\n", dev->minor);
- /* make sure triggers are valid */
- /* Only immediate triggers are allowed */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_INT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
-
- /* trigger should happen timed */
- tmp = cmd->scan_begin_src;
- /* start a new _scan_ with a timer */
- cmd->scan_begin_src &= TRIG_TIMER;
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- /* scanning is continuous */
- tmp = cmd->convert_src;
- cmd->convert_src &= TRIG_NOW;
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
- /* issue a trigger when scan is finished and start a new scan */
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- /* trigger at the end of count events or not, stop condition or not */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, TRIG_TIMER);
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources are unique and mutually compatible
- * note that mutual compatibility is not an issue here
- */
- if (cmd->scan_begin_src != TRIG_FOLLOW &&
- cmd->scan_begin_src != TRIG_EXT &&
- cmd->scan_begin_src != TRIG_TIMER)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1558,8 +1530,9 @@ static int usbdux_ao_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
- int err = 0, tmp;
struct usbduxsub *this_usbduxsub = dev->private;
+ int err = 0;
+ unsigned int flags;
if (!this_usbduxsub)
return -EFAULT;
@@ -1570,63 +1543,35 @@ static int usbdux_ao_cmdtest(struct comedi_device *dev,
dev_dbg(&this_usbduxsub->interface->dev,
"comedi%d: usbdux_ao_cmdtest\n", dev->minor);
- /* make sure triggers are valid */
- /* Only immediate triggers are allowed */
- tmp = cmd->start_src;
- cmd->start_src &= TRIG_NOW | TRIG_INT;
- if (!cmd->start_src || tmp != cmd->start_src)
- err++;
+ /* Step 1 : check if triggers are trivially valid */
+
+ err |= cfc_check_trigger_src(&cmd->start_src, TRIG_NOW | TRIG_INT);
- /* trigger should happen timed */
- tmp = cmd->scan_begin_src;
- /* just now we scan also in the high speed mode every frame */
- /* this is due to ehci driver limitations */
if (0) { /* (this_usbduxsub->high_speed) */
- /* start immediately a new scan */
- /* the sampling rate is set by the coversion rate */
- cmd->scan_begin_src &= TRIG_FOLLOW;
+ /*
+ * start immediately a new scan
+ * the sampling rate is set by the coversion rate
+ */
+ flags = TRIG_FOLLOW;
} else {
/* start a new scan (output at once) with a timer */
- cmd->scan_begin_src &= TRIG_TIMER;
+ flags = TRIG_TIMER;
}
- if (!cmd->scan_begin_src || tmp != cmd->scan_begin_src)
- err++;
-
- /* scanning is continuous */
- tmp = cmd->convert_src;
+ err |= cfc_check_trigger_src(&cmd->scan_begin_src, flags);
- /* all conversion events happen simultaneously */
- cmd->convert_src &= TRIG_NOW;
-
- if (!cmd->convert_src || tmp != cmd->convert_src)
- err++;
-
- /* issue a trigger when scan is finished and start a new scan */
- tmp = cmd->scan_end_src;
- cmd->scan_end_src &= TRIG_COUNT;
- if (!cmd->scan_end_src || tmp != cmd->scan_end_src)
- err++;
-
- /* trigger at the end of count events or not, stop condition or not */
- tmp = cmd->stop_src;
- cmd->stop_src &= TRIG_COUNT | TRIG_NONE;
- if (!cmd->stop_src || tmp != cmd->stop_src)
- err++;
+ err |= cfc_check_trigger_src(&cmd->convert_src, TRIG_NOW);
+ err |= cfc_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
+ err |= cfc_check_trigger_src(&cmd->stop_src, TRIG_COUNT | TRIG_NONE);
if (err)
return 1;
- /*
- * step 2: make sure trigger sources
- * are unique and mutually compatible
- * note that mutual compatibility is not an issue here
- */
- if (cmd->scan_begin_src != TRIG_FOLLOW &&
- cmd->scan_begin_src != TRIG_EXT &&
- cmd->scan_begin_src != TRIG_TIMER)
- err++;
- if (cmd->stop_src != TRIG_COUNT && cmd->stop_src != TRIG_NONE)
- err++;
+ /* Step 2a : make sure trigger sources are unique */
+
+ err |= cfc_check_trigger_is_unique(cmd->start_src);
+ err |= cfc_check_trigger_is_unique(cmd->stop_src);
+
+ /* Step 2b : and mutually compatible */
if (err)
return 2;
@@ -1950,7 +1895,7 @@ static void usbduxsub_pwm_irq(struct urb *urb)
/* the private structure of the subdevice is struct usbduxsub */
this_usbduxsub = this_comedidev->private;
- s = this_comedidev->subdevices + SUBDEV_DA;
+ s = &this_comedidev->subdevices[SUBDEV_DA];
switch (urb->status) {
case 0:
@@ -2301,10 +2246,8 @@ static void tidy_up(struct usbduxsub *usbduxsub_tmp)
usbduxsub_tmp->pwm_cmd_running = 0;
}
-/* common part of attach and attach_usb */
static int usbduxsigma_attach_common(struct comedi_device *dev,
- struct usbduxsub *uds,
- void *aux_data, int aux_len)
+ struct usbduxsub *uds)
{
int ret;
struct comedi_subdevice *s;
@@ -2314,9 +2257,6 @@ static int usbduxsigma_attach_common(struct comedi_device *dev,
down(&uds->sem);
/* pointer back to the corresponding comedi device */
uds->comedidev = dev;
- /* trying to upload the firmware into the FX2 */
- if (aux_data)
- firmwareUpload(uds, aux_data, aux_len);
dev->board_name = "usbduxsigma";
/* set number of subdevices */
if (uds->high_speed)
@@ -2331,7 +2271,7 @@ static int usbduxsigma_attach_common(struct comedi_device *dev,
/* private structure is also simply the usb-structure */
dev->private = uds;
/* the first subdevice is the A/D converter */
- s = dev->subdevices + SUBDEV_AD;
+ s = &dev->subdevices[SUBDEV_AD];
/* the URBs get the comedi subdevice */
/* which is responsible for reading */
/* this is the subdevice which reads data */
@@ -2358,7 +2298,7 @@ static int usbduxsigma_attach_common(struct comedi_device *dev,
/* range table to convert to physical units */
s->range_table = (&range_usbdux_ai_range);
/* analog output subdevice */
- s = dev->subdevices + SUBDEV_DA;
+ s = &dev->subdevices[SUBDEV_DA];
/* analog out */
s->type = COMEDI_SUBD_AO;
/* backward pointer */
@@ -2383,7 +2323,7 @@ static int usbduxsigma_attach_common(struct comedi_device *dev,
s->insn_read = usbdux_ao_insn_read;
s->insn_write = usbdux_ao_insn_write;
/* digital I/O subdevice */
- s = dev->subdevices + SUBDEV_DIO;
+ s = &dev->subdevices[SUBDEV_DIO];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
/* 8 external and 16 internal channels */
@@ -2396,7 +2336,7 @@ static int usbduxsigma_attach_common(struct comedi_device *dev,
s->private = NULL;
if (uds->high_speed) {
/* timer / pwm subdevice */
- s = dev->subdevices + SUBDEV_PWM;
+ s = &dev->subdevices[SUBDEV_PWM];
s->type = COMEDI_SUBD_PWM;
s->subdev_flags = SDF_WRITABLE | SDF_PWM_HBRIDGE;
s->n_chan = 8;
@@ -2419,47 +2359,6 @@ static int usbduxsigma_attach_common(struct comedi_device *dev,
return 0;
}
-/* is called for COMEDI_DEVCONFIG ioctl (when comedi_config is run) */
-static int usbduxsigma_attach(struct comedi_device *dev,
- struct comedi_devconfig *it)
-{
- int ret;
- int index;
- int i;
- void *aux_data;
- int aux_len;
-
- dev->private = NULL;
-
- aux_data = comedi_aux_data(it->options, 0);
- aux_len = it->options[COMEDI_DEVCONF_AUX_DATA_LENGTH];
- if (aux_data == NULL)
- aux_len = 0;
- else if (aux_len == 0)
- aux_data = NULL;
-
- down(&start_stop_sem);
- /* find a valid device which has been detected by the probe function of
- * the usb */
- index = -1;
- for (i = 0; i < NUMUSBDUX; i++) {
- if ((usbduxsub[i].probed) && (!usbduxsub[i].attached)) {
- index = i;
- break;
- }
- }
- if (index < 0) {
- dev_err(dev->class_dev,
- "usbduxsigma: error: attach failed, dev not connected to the usb bus.\n");
- ret = -ENODEV;
- } else
- ret = usbduxsigma_attach_common(dev, &usbduxsub[index],
- aux_data, aux_len);
- up(&start_stop_sem);
- return ret;
-}
-
-/* is called from comedi_usb_auto_config() */
static int usbduxsigma_attach_usb(struct comedi_device *dev,
struct usb_interface *uinterf)
{
@@ -2478,7 +2377,7 @@ static int usbduxsigma_attach_usb(struct comedi_device *dev,
"usbduxsigma: error: attach_usb failed, already attached\n");
ret = -ENODEV;
} else
- ret = usbduxsigma_attach_common(dev, uds, NULL, 0);
+ ret = usbduxsigma_attach_common(dev, uds);
up(&start_stop_sem);
return ret;
}
@@ -2499,9 +2398,8 @@ static void usbduxsigma_detach(struct comedi_device *dev)
static struct comedi_driver usbduxsigma_driver = {
.driver_name = "usbduxsigma",
.module = THIS_MODULE,
- .attach = usbduxsigma_attach,
- .detach = usbduxsigma_detach,
.attach_usb = usbduxsigma_attach_usb,
+ .detach = usbduxsigma_detach,
};
static void usbdux_firmware_request_complete_handler(const struct firmware *fw,
diff --git a/drivers/staging/comedi/drivers/vmk80xx.c b/drivers/staging/comedi/drivers/vmk80xx.c
index 94010fc05905..df277aa591bb 100644
--- a/drivers/staging/comedi/drivers/vmk80xx.c
+++ b/drivers/staging/comedi/drivers/vmk80xx.c
@@ -546,6 +546,7 @@ static int vmk80xx_ai_rinsn(struct comedi_device *cdev,
reg[0] = VMK8055_AI2_REG;
break;
case VMK8061_MODEL:
+ default:
reg[0] = VMK8061_AI_REG1;
reg[1] = VMK8061_AI_REG2;
dev->usb_tx_buf[0] = VMK8061_CMD_RD_AI;
@@ -904,6 +905,7 @@ static int vmk80xx_cnt_rinsn(struct comedi_device *cdev,
reg[0] = VMK8055_CNT2_REG;
break;
case VMK8061_MODEL:
+ default:
reg[0] = VMK8061_CNT_REG;
reg[1] = VMK8061_CNT_REG;
dev->usb_tx_buf[0] = VMK8061_CMD_RD_CNT;
@@ -1119,7 +1121,7 @@ static int vmk80xx_attach_common(struct comedi_device *cdev,
return ret;
}
/* Analog input subdevice */
- s = cdev->subdevices + VMK80XX_SUBD_AI;
+ s = &cdev->subdevices[VMK80XX_SUBD_AI];
s->type = COMEDI_SUBD_AI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
s->n_chan = dev->board.ai_chans;
@@ -1127,7 +1129,7 @@ static int vmk80xx_attach_common(struct comedi_device *cdev,
s->range_table = dev->board.range;
s->insn_read = vmk80xx_ai_rinsn;
/* Analog output subdevice */
- s = cdev->subdevices + VMK80XX_SUBD_AO;
+ s = &cdev->subdevices[VMK80XX_SUBD_AO];
s->type = COMEDI_SUBD_AO;
s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
s->n_chan = dev->board.ao_chans;
@@ -1139,7 +1141,7 @@ static int vmk80xx_attach_common(struct comedi_device *cdev,
s->insn_read = vmk80xx_ao_rinsn;
}
/* Digital input subdevice */
- s = cdev->subdevices + VMK80XX_SUBD_DI;
+ s = &cdev->subdevices[VMK80XX_SUBD_DI];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_GROUND;
s->n_chan = dev->board.di_chans;
@@ -1147,7 +1149,7 @@ static int vmk80xx_attach_common(struct comedi_device *cdev,
s->insn_read = vmk80xx_di_rinsn;
s->insn_bits = vmk80xx_di_bits;
/* Digital output subdevice */
- s = cdev->subdevices + VMK80XX_SUBD_DO;
+ s = &cdev->subdevices[VMK80XX_SUBD_DO];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITEABLE | SDF_GROUND;
s->n_chan = dev->board.do_chans;
@@ -1159,7 +1161,7 @@ static int vmk80xx_attach_common(struct comedi_device *cdev,
s->insn_read = vmk80xx_do_rinsn;
}
/* Counter subdevice */
- s = cdev->subdevices + VMK80XX_SUBD_CNT;
+ s = &cdev->subdevices[VMK80XX_SUBD_CNT];
s->type = COMEDI_SUBD_COUNTER;
s->subdev_flags = SDF_READABLE;
s->n_chan = dev->board.cnt_chans;
@@ -1172,7 +1174,7 @@ static int vmk80xx_attach_common(struct comedi_device *cdev,
}
/* PWM subdevice */
if (dev->board.model == VMK8061_MODEL) {
- s = cdev->subdevices + VMK80XX_SUBD_PWM;
+ s = &cdev->subdevices[VMK80XX_SUBD_PWM];
s->type = COMEDI_SUBD_PWM;
s->subdev_flags = SDF_READABLE | SDF_WRITEABLE;
s->n_chan = dev->board.pwm_chans;
diff --git a/drivers/staging/comedi/kcomedilib/kcomedilib_main.c b/drivers/staging/comedi/kcomedilib/kcomedilib_main.c
index 0252b4408851..3f20ea55b8d0 100644
--- a/drivers/staging/comedi/kcomedilib/kcomedilib_main.c
+++ b/drivers/staging/comedi/kcomedilib/kcomedilib_main.c
@@ -80,7 +80,9 @@ int comedi_close(struct comedi_device *d)
}
EXPORT_SYMBOL(comedi_close);
-static int comedi_do_insn(struct comedi_device *dev, struct comedi_insn *insn)
+static int comedi_do_insn(struct comedi_device *dev,
+ struct comedi_insn *insn,
+ unsigned int *data)
{
struct comedi_subdevice *s;
int ret = 0;
@@ -90,7 +92,7 @@ static int comedi_do_insn(struct comedi_device *dev, struct comedi_insn *insn)
ret = -EINVAL;
goto error;
}
- s = dev->subdevices + insn->subdev;
+ s = &dev->subdevices[insn->subdev];
if (s->type == COMEDI_SUBD_UNUSED) {
printk(KERN_ERR "%d not useable subdevice\n", insn->subdev);
@@ -115,11 +117,11 @@ static int comedi_do_insn(struct comedi_device *dev, struct comedi_insn *insn)
switch (insn->insn) {
case INSN_BITS:
- ret = s->insn_bits(dev, s, insn, insn->data);
+ ret = s->insn_bits(dev, s, insn, data);
break;
case INSN_CONFIG:
/* XXX should check instruction length */
- ret = s->insn_config(dev, s, insn, insn->data);
+ ret = s->insn_config(dev, s, insn, data);
break;
default:
ret = -EINVAL;
@@ -140,11 +142,10 @@ int comedi_dio_config(struct comedi_device *dev, unsigned int subdev,
memset(&insn, 0, sizeof(insn));
insn.insn = INSN_CONFIG;
insn.n = 1;
- insn.data = &io;
insn.subdev = subdev;
insn.chanspec = CR_PACK(chan, 0, 0);
- return comedi_do_insn(dev, &insn);
+ return comedi_do_insn(dev, &insn, &io);
}
EXPORT_SYMBOL(comedi_dio_config);
@@ -158,13 +159,12 @@ int comedi_dio_bitfield(struct comedi_device *dev, unsigned int subdev,
memset(&insn, 0, sizeof(insn));
insn.insn = INSN_BITS;
insn.n = 2;
- insn.data = data;
insn.subdev = subdev;
data[0] = mask;
data[1] = *bits;
- ret = comedi_do_insn(dev, &insn);
+ ret = comedi_do_insn(dev, &insn, data);
*bits = data[1];
@@ -175,11 +175,14 @@ EXPORT_SYMBOL(comedi_dio_bitfield);
int comedi_find_subdevice_by_type(struct comedi_device *dev, int type,
unsigned int subd)
{
+ struct comedi_subdevice *s;
+
if (subd > dev->n_subdevices)
return -ENODEV;
for (; subd < dev->n_subdevices; subd++) {
- if (dev->subdevices[subd].type == type)
+ s = &dev->subdevices[subd];
+ if (s->type == type)
return subd;
}
return -1;
@@ -188,7 +191,7 @@ EXPORT_SYMBOL(comedi_find_subdevice_by_type);
int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice)
{
- struct comedi_subdevice *s = dev->subdevices + subdevice;
+ struct comedi_subdevice *s = &dev->subdevices[subdevice];
return s->n_chan;
}
diff --git a/drivers/staging/comedi/range.c b/drivers/staging/comedi/range.c
index 41f95237789d..59ff0cf73381 100644
--- a/drivers/staging/comedi/range.c
+++ b/drivers/staging/comedi/range.c
@@ -68,7 +68,7 @@ int do_rangeinfo_ioctl(struct comedi_device *dev,
return -EINVAL;
if (subd >= dev->n_subdevices)
return -EINVAL;
- s = dev->subdevices + subd;
+ s = &dev->subdevices[subd];
if (s->range_table) {
lr = s->range_table;
} else if (s->range_table_list) {
@@ -131,6 +131,7 @@ static int aref_invalid(struct comedi_subdevice *s, unsigned int chanspec)
int comedi_check_chanlist(struct comedi_subdevice *s, int n,
unsigned int *chanlist)
{
+ struct comedi_device *dev = s->device;
int i;
int chan;
@@ -139,10 +140,10 @@ int comedi_check_chanlist(struct comedi_subdevice *s, int n,
if (CR_CHAN(chanlist[i]) >= s->n_chan ||
CR_RANGE(chanlist[i]) >= s->range_table->length
|| aref_invalid(s, chanlist[i])) {
- printk(KERN_ERR "bad chanlist[%d]=0x%08x "
- "in_chan=%d range length=%d\n", i,
- chanlist[i], s->n_chan,
- s->range_table->length);
+ dev_warn(dev->class_dev,
+ "bad chanlist[%d]=0x%08x in_chan=%d range length=%d\n",
+ i, chanlist[i], s->n_chan,
+ s->range_table->length);
return -EINVAL;
}
} else if (s->range_table_list) {
@@ -152,13 +153,14 @@ int comedi_check_chanlist(struct comedi_subdevice *s, int n,
CR_RANGE(chanlist[i]) >=
s->range_table_list[chan]->length
|| aref_invalid(s, chanlist[i])) {
- printk(KERN_ERR "bad chanlist[%d]=0x%08x\n",
- i, chanlist[i]);
+ dev_warn(dev->class_dev,
+ "bad chanlist[%d]=0x%08x\n",
+ i, chanlist[i]);
return -EINVAL;
}
}
} else {
- printk(KERN_ERR "comedi: (bug) no range type list!\n");
+ dev_err(dev->class_dev, "(bug) no range type list!\n");
return -EINVAL;
}
return 0;
diff --git a/drivers/staging/cptm1217/clearpad_tm1217.c b/drivers/staging/cptm1217/clearpad_tm1217.c
index 0d924d3a2ab1..a49b0da60049 100644
--- a/drivers/staging/cptm1217/clearpad_tm1217.c
+++ b/drivers/staging/cptm1217/clearpad_tm1217.c
@@ -658,18 +658,7 @@ static struct i2c_driver cp_tm1217_driver = {
.resume = cp_tm1217_resume,
};
-static int __init clearpad_tm1217_init(void)
-{
- return i2c_add_driver(&cp_tm1217_driver);
-}
-
-static void __exit clearpad_tm1217_exit(void)
-{
- i2c_del_driver(&cp_tm1217_driver);
-}
-
-module_init(clearpad_tm1217_init);
-module_exit(clearpad_tm1217_exit);
+module_i2c_driver(cp_tm1217_driver);
MODULE_AUTHOR("Ramesh Agarwal <ramesh.agarwal@intel.com>");
MODULE_DESCRIPTION("Synaptics TM1217 TouchScreen Driver");
diff --git a/drivers/staging/crystalhd/crystalhd_lnx.c b/drivers/staging/crystalhd/crystalhd_lnx.c
index d9e3d618f7f4..166203aeb7b4 100644
--- a/drivers/staging/crystalhd/crystalhd_lnx.c
+++ b/drivers/staging/crystalhd/crystalhd_lnx.c
@@ -373,13 +373,15 @@ static int __devinit chd_dec_init_chdev(struct crystalhd_adp *adp)
/* register crystalhd class */
crystalhd_class = class_create(THIS_MODULE, "crystalhd");
if (IS_ERR(crystalhd_class)) {
+ rc = PTR_ERR(crystalhd_class);
BCMLOG_ERR("failed to create class\n");
- goto fail;
+ goto class_create_fail;
}
dev = device_create(crystalhd_class, NULL, MKDEV(adp->chd_dec_major, 0),
NULL, "crystalhd");
if (IS_ERR(dev)) {
+ rc = PTR_ERR(dev);
BCMLOG_ERR("failed to create device\n");
goto device_create_fail;
}
@@ -410,6 +412,8 @@ elem_pool_fail:
device_destroy(crystalhd_class, MKDEV(adp->chd_dec_major, 0));
device_create_fail:
class_destroy(crystalhd_class);
+class_create_fail:
+ unregister_chrdev(adp->chd_dec_major, CRYSTALHD_API_NAME);
fail:
return rc;
}
diff --git a/drivers/staging/csr/Makefile b/drivers/staging/csr/Makefile
index afda44b0a925..ab626edc5ba4 100644
--- a/drivers/staging/csr/Makefile
+++ b/drivers/staging/csr/Makefile
@@ -25,7 +25,6 @@ csr_wifi-y := bh.o \
unifi_event.o \
unifi_pdu_processing.o \
unifi_sme.o \
- csr_formatted_io.o \
csr_wifi_hip_card_sdio.o \
csr_wifi_hip_card_sdio_intr.o \
csr_wifi_hip_card_sdio_mem.o \
diff --git a/drivers/staging/csr/bh.c b/drivers/staging/csr/bh.c
index b089c28d5610..addee05a4516 100644
--- a/drivers/staging/csr/bh.c
+++ b/drivers/staging/csr/bh.c
@@ -32,45 +32,49 @@
* 0 on success or else a Linux error code.
* ---------------------------------------------------------------------------
*/
-int
-uf_start_thread(unifi_priv_t *priv, struct uf_thread *thread, int (*func)(void *))
+int uf_start_thread(unifi_priv_t *priv,
+ struct uf_thread *thread, int (*func)(void *))
{
- if (thread->thread_task != NULL) {
- unifi_error(priv, "%s thread already started\n", thread->name);
- return 0;
- }
-
- /* Start the kernel thread that handles all h/w accesses. */
- thread->thread_task = kthread_run(func, priv, "%s", thread->name);
- if (IS_ERR(thread->thread_task)) {
- return PTR_ERR(thread->thread_task);
- }
-
- /* Module parameter overides the thread priority */
- if (bh_priority != -1) {
- if (bh_priority >= 0 && bh_priority <= MAX_RT_PRIO) {
- struct sched_param param;
- priv->bh_thread.prio = bh_priority;
- unifi_trace(priv, UDBG1, "%s thread (RT) priority = %d\n",
- thread->name, bh_priority);
- param.sched_priority = bh_priority;
- sched_setscheduler(thread->thread_task, SCHED_FIFO, &param);
- } else if (bh_priority > MAX_RT_PRIO && bh_priority <= MAX_PRIO) {
- priv->bh_thread.prio = bh_priority;
- unifi_trace(priv, UDBG1, "%s thread priority = %d\n",
- thread->name, PRIO_TO_NICE(bh_priority));
- set_user_nice(thread->thread_task, PRIO_TO_NICE(bh_priority));
- } else {
- priv->bh_thread.prio = DEFAULT_PRIO;
- unifi_warning(priv, "%s thread unsupported (%d) priority\n",
- thread->name, bh_priority);
- }
- } else {
- priv->bh_thread.prio = DEFAULT_PRIO;
- }
- unifi_trace(priv, UDBG2, "Started %s thread\n", thread->name);
-
- return 0;
+ if (thread->thread_task != NULL) {
+ unifi_error(priv, "%s thread already started\n", thread->name);
+ return 0;
+ }
+
+ /* Start the kernel thread that handles all h/w accesses. */
+ thread->thread_task = kthread_run(func, priv, "%s", thread->name);
+ if (IS_ERR(thread->thread_task))
+ return PTR_ERR(thread->thread_task);
+
+ /* Module parameter overides the thread priority */
+ if (bh_priority != -1) {
+ if (bh_priority >= 0 && bh_priority <= MAX_RT_PRIO) {
+ struct sched_param param;
+ priv->bh_thread.prio = bh_priority;
+ unifi_trace(priv, UDBG1,
+ "%s thread (RT) priority = %d\n",
+ thread->name, bh_priority);
+ param.sched_priority = bh_priority;
+ sched_setscheduler(thread->thread_task,
+ SCHED_FIFO, &param);
+ } else if (bh_priority > MAX_RT_PRIO &&
+ bh_priority <= MAX_PRIO) {
+ priv->bh_thread.prio = bh_priority;
+ unifi_trace(priv, UDBG1, "%s thread priority = %d\n",
+ thread->name,
+ PRIO_TO_NICE(bh_priority));
+ set_user_nice(thread->thread_task,
+ PRIO_TO_NICE(bh_priority));
+ } else {
+ priv->bh_thread.prio = DEFAULT_PRIO;
+ unifi_warning(priv,
+ "%s thread unsupported (%d) priority\n",
+ thread->name, bh_priority);
+ }
+ } else
+ priv->bh_thread.prio = DEFAULT_PRIO;
+ unifi_trace(priv, UDBG2, "Started %s thread\n", thread->name);
+
+ return 0;
} /* uf_start_thread() */
@@ -88,18 +92,18 @@ uf_start_thread(unifi_priv_t *priv, struct uf_thread *thread, int (*func)(void *
*
* ---------------------------------------------------------------------------
*/
- void
-uf_stop_thread(unifi_priv_t *priv, struct uf_thread *thread)
+void uf_stop_thread(unifi_priv_t *priv, struct uf_thread *thread)
{
- if (!thread->thread_task) {
- unifi_notice(priv, "%s thread is already stopped\n", thread->name);
- return;
- }
+ if (!thread->thread_task) {
+ unifi_notice(priv, "%s thread is already stopped\n",
+ thread->name);
+ return;
+ }
- unifi_trace(priv, UDBG2, "Stopping %s thread\n", thread->name);
+ unifi_trace(priv, UDBG2, "Stopping %s thread\n", thread->name);
- kthread_stop(thread->thread_task);
- thread->thread_task = NULL;
+ kthread_stop(thread->thread_task);
+ thread->thread_task = NULL;
} /* uf_stop_thread() */
@@ -118,23 +122,24 @@ uf_stop_thread(unifi_priv_t *priv, struct uf_thread *thread)
*
* ---------------------------------------------------------------------------
*/
- void
+void
uf_wait_for_thread_to_stop(unifi_priv_t *priv, struct uf_thread *thread)
{
- /*
- * kthread_stop() cannot handle the thread exiting while
- * kthread_should_stop() is false, so sleep until kthread_stop()
- * wakes us up.
- */
- unifi_trace(priv, UDBG2, "%s waiting for the stop signal.\n", thread->name);
- set_current_state(TASK_INTERRUPTIBLE);
- if (!kthread_should_stop()) {
- unifi_trace(priv, UDBG2, "%s schedule....\n", thread->name);
- schedule();
- }
-
- thread->thread_task = NULL;
- unifi_trace(priv, UDBG2, "%s exiting....\n", thread->name);
+ /*
+ * kthread_stop() cannot handle the thread exiting while
+ * kthread_should_stop() is false, so sleep until kthread_stop()
+ * wakes us up
+ */
+ unifi_trace(priv, UDBG2, "%s waiting for the stop signal.\n",
+ thread->name);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!kthread_should_stop()) {
+ unifi_trace(priv, UDBG2, "%s schedule....\n", thread->name);
+ schedule();
+ }
+
+ thread->thread_task = NULL;
+ unifi_trace(priv, UDBG2, "%s exiting....\n", thread->name);
} /* uf_wait_for_thread_to_stop() */
@@ -155,39 +160,41 @@ uf_wait_for_thread_to_stop(unifi_priv_t *priv, struct uf_thread *thread)
* None.
* ---------------------------------------------------------------------------
*/
- static void
+static void
handle_bh_error(unifi_priv_t *priv)
{
- u8 conf_param = CONFIG_IND_ERROR;
- u8 interfaceTag = 0; /* used as a loop counter */
+ netInterface_priv_t *interfacePriv;
+ u8 conf_param = CONFIG_IND_ERROR;
+ u8 interfaceTag;
- /* Block unifi_run_bh() until the error has been handled. */
- priv->bh_thread.block_thread = 1;
+ /* Block unifi_run_bh() until the error has been handled. */
+ priv->bh_thread.block_thread = 1;
- /* Consider UniFi to be uninitialised */
- priv->init_progress = UNIFI_INIT_NONE;
+ /* Consider UniFi to be uninitialised */
+ priv->init_progress = UNIFI_INIT_NONE;
- /* Stop the network traffic */
- for( interfaceTag =0; interfaceTag <CSR_WIFI_NUM_INTERFACES;interfaceTag ++) {
- netInterface_priv_t *interfacePriv = priv->interfacePriv[interfaceTag];
- if (interfacePriv->netdev_registered == 1) {
- netif_carrier_off(priv->netdev[interfaceTag]);
- }
- }
+ /* Stop the network traffic */
+ for (interfaceTag = 0;
+ interfaceTag < CSR_WIFI_NUM_INTERFACES; interfaceTag++) {
+ interfacePriv = priv->interfacePriv[interfaceTag];
+ if (interfacePriv->netdev_registered)
+ netif_carrier_off(priv->netdev[interfaceTag]);
+ }
#ifdef CSR_NATIVE_LINUX
- /* Force any client waiting on an mlme_wait_for_reply() to abort. */
- uf_abort_mlme(priv);
+ /* Force any client waiting on an mlme_wait_for_reply() to abort. */
+ uf_abort_mlme(priv);
- /* Cancel any pending workqueue tasks */
- flush_workqueue(priv->unifi_workqueue);
+ /* Cancel any pending workqueue tasks */
+ flush_workqueue(priv->unifi_workqueue);
#endif /* CSR_NATIVE_LINUX */
- unifi_error(priv, "handle_bh_error: fatal error is reported to the SME.\n");
- /* Notify the clients (SME or unifi_manager) for the error. */
- ul_log_config_ind(priv, &conf_param, sizeof(u8));
+ unifi_error(priv,
+ "handle_bh_error: fatal error is reported to the SME.\n");
+ /* Notify the clients (SME or unifi_manager) for the error. */
+ ul_log_config_ind(priv, &conf_param, sizeof(u8));
} /* handle_bh_error() */
diff --git a/drivers/staging/csr/csr_formatted_io.c b/drivers/staging/csr/csr_formatted_io.c
deleted file mode 100644
index 7213cc8fb577..000000000000
--- a/drivers/staging/csr/csr_formatted_io.c
+++ /dev/null
@@ -1,27 +0,0 @@
-/*****************************************************************************
-
- (c) Cambridge Silicon Radio Limited 2010
- All rights reserved and confidential information of CSR
-
- Refer to LICENSE.txt included with this source for details
- on the license terms.
-
-*****************************************************************************/
-#include <linux/kernel.h>
-#include "csr_formatted_io.h"
-
-s32 CsrSnprintf(char *dest, size_t n, const char *fmt, ...)
-{
- s32 r;
- va_list args;
- va_start(args, fmt);
- r = vsnprintf(dest, n, fmt, args);
- va_end(args);
-
- if (dest && (n > 0))
- {
- dest[n - 1] = '\0';
- }
-
- return r;
-}
diff --git a/drivers/staging/csr/csr_formatted_io.h b/drivers/staging/csr/csr_formatted_io.h
deleted file mode 100644
index 2e238cb98d51..000000000000
--- a/drivers/staging/csr/csr_formatted_io.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef CSR_FORMATTED_IO_H__
-#define CSR_FORMATTED_IO_H__
-/*****************************************************************************
-
- (c) Cambridge Silicon Radio Limited 2010
- All rights reserved and confidential information of CSR
-
- Refer to LICENSE.txt included with this source for details
- on the license terms.
-
-*****************************************************************************/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#include <linux/types.h>
-
-s32 CsrSnprintf(char *dest, size_t n, const char *fmt, ...);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/drivers/staging/csr/csr_framework_ext.c b/drivers/staging/csr/csr_framework_ext.c
index 12e7ddf3220a..f91a4bf4e68f 100644
--- a/drivers/staging/csr/csr_framework_ext.c
+++ b/drivers/staging/csr/csr_framework_ext.c
@@ -12,20 +12,9 @@
#include <linux/version.h>
#include <linux/kthread.h>
#include <linux/module.h>
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
-#include <linux/slab.h>
-#endif
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 19)
#include <linux/freezer.h>
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
-#include <asm/semaphore.h>
-#else
#include <linux/semaphore.h>
-#endif
-
+#include <linux/slab.h>
#include <linux/bitops.h>
#include "csr_framework_ext.h"
diff --git a/drivers/staging/csr/csr_panic.c b/drivers/staging/csr/csr_panic.c
index 353a829bb74c..095f7fa3ae2c 100644
--- a/drivers/staging/csr/csr_panic.c
+++ b/drivers/staging/csr/csr_panic.c
@@ -9,7 +9,6 @@
*****************************************************************************/
#include <linux/kernel.h>
-#include <linux/version.h>
#include <linux/module.h>
#include "csr_panic.h"
diff --git a/drivers/staging/csr/csr_time.c b/drivers/staging/csr/csr_time.c
index 83586ca34e8c..7b597f7622a2 100644
--- a/drivers/staging/csr/csr_time.c
+++ b/drivers/staging/csr/csr_time.c
@@ -10,13 +10,6 @@
#include <linux/kernel.h>
#include <linux/version.h>
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 16)
-#include <linux/autoconf.h>
-#include <linux/config.h>
-#endif
-
#include <linux/time.h>
#include <linux/module.h>
@@ -24,20 +17,18 @@
CsrTime CsrTimeGet(CsrTime *high)
{
- struct timespec ts;
- u64 time;
- CsrTime low;
+ struct timespec ts;
+ u64 time;
+ CsrTime low;
- ts = current_kernel_time();
- time = (u64) ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
+ ts = current_kernel_time();
+ time = (u64) ts.tv_sec * 1000000 + ts.tv_nsec / 1000;
- if (high != NULL)
- {
- *high = (CsrTime) ((time >> 32) & 0xFFFFFFFF);
- }
+ if (high != NULL)
+ *high = (CsrTime) ((time >> 32) & 0xFFFFFFFF);
- low = (CsrTime) (time & 0xFFFFFFFF);
+ low = (CsrTime) (time & 0xFFFFFFFF);
- return low;
+ return low;
}
EXPORT_SYMBOL_GPL(CsrTimeGet);
diff --git a/drivers/staging/csr/csr_wifi_hip_card_sdio.c b/drivers/staging/csr/csr_wifi_hip_card_sdio.c
index 44ab00c53fec..cf148a0fec6a 100644
--- a/drivers/staging/csr/csr_wifi_hip_card_sdio.c
+++ b/drivers/staging/csr/csr_wifi_hip_card_sdio.c
@@ -1612,13 +1612,13 @@ static CsrResult card_allocate_memory_resources(card_t *card)
/* Reset any state carried forward from a previous life */
card->fh_command_queue.q_rd_ptr = 0;
card->fh_command_queue.q_wr_ptr = 0;
- (void)CsrSnprintf(card->fh_command_queue.name, UNIFI_QUEUE_NAME_MAX_LENGTH,
+ (void)scnprintf(card->fh_command_queue.name, UNIFI_QUEUE_NAME_MAX_LENGTH,
"fh_cmd_q");
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
{
card->fh_traffic_queue[i].q_rd_ptr = 0;
card->fh_traffic_queue[i].q_wr_ptr = 0;
- (void)CsrSnprintf(card->fh_traffic_queue[i].name,
+ (void)scnprintf(card->fh_traffic_queue[i].name,
UNIFI_QUEUE_NAME_MAX_LENGTH, "fh_data_q%d", i);
}
#ifndef CSR_WIFI_HIP_TA_DISABLE
@@ -1826,13 +1826,13 @@ static void card_init_soft_queues(card_t *card)
/* Reset any state carried forward from a previous life */
card->fh_command_queue.q_rd_ptr = 0;
card->fh_command_queue.q_wr_ptr = 0;
- (void)CsrSnprintf(card->fh_command_queue.name, UNIFI_QUEUE_NAME_MAX_LENGTH,
+ (void)scnprintf(card->fh_command_queue.name, UNIFI_QUEUE_NAME_MAX_LENGTH,
"fh_cmd_q");
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
{
card->fh_traffic_queue[i].q_rd_ptr = 0;
card->fh_traffic_queue[i].q_wr_ptr = 0;
- (void)CsrSnprintf(card->fh_traffic_queue[i].name,
+ (void)scnprintf(card->fh_traffic_queue[i].name,
UNIFI_QUEUE_NAME_MAX_LENGTH, "fh_data_q%d", i);
}
#ifndef CSR_WIFI_HIP_TA_DISABLE
diff --git a/drivers/staging/csr/csr_wifi_hip_download.c b/drivers/staging/csr/csr_wifi_hip_download.c
index 8e4a4608ba5c..6db672caaa02 100644
--- a/drivers/staging/csr/csr_wifi_hip_download.c
+++ b/drivers/staging/csr/csr_wifi_hip_download.c
@@ -250,6 +250,7 @@ static CsrResult do_patch_convert_download(card_t *card, void *dlpriv, xbv1_t *p
if (r != CSR_RESULT_SUCCESS)
{
unifi_error(card->ospriv, "Failed to find BOOT_LOADER_CONTROL\n");
+ kfree(pfw);
return CSR_RESULT_FAILURE;
}
@@ -265,6 +266,7 @@ static CsrResult do_patch_convert_download(card_t *card, void *dlpriv, xbv1_t *p
desc = unifi_fw_open_buffer(card->ospriv, pfw, psize);
if (!desc)
{
+ kfree(pfw);
return CSR_WIFI_HIP_RESULT_NO_MEMORY;
}
diff --git a/drivers/staging/csr/csr_wifi_hip_send.c b/drivers/staging/csr/csr_wifi_hip_send.c
index 684d30459d75..86aa23cefe30 100644
--- a/drivers/staging/csr/csr_wifi_hip_send.c
+++ b/drivers/staging/csr/csr_wifi_hip_send.c
@@ -172,13 +172,8 @@ static CsrResult send_signal(card_t *card, const u8 *sigptr, u32 siglen,
{
const u8 *sig = sigptr;
- unifi_error(card->ospriv, "Signal(%d): %02x %02x %02x %02x %02x %02x %02x %02x"
- " %02x %02x %02x %02x %02x %02x %02x %02x\n",
- siglen,
- sig[0], sig[1], sig[2], sig[3],
- sig[4], sig[5], sig[6], sig[7],
- sig[8], sig[9], sig[10], sig[11],
- sig[12], sig[13], sig[14], sig[15]);
+ unifi_error(card->ospriv, "Signal(%d): %*ph\n", siglen,
+ 16, sig);
unifi_error(card->ospriv, "Bulkdata pointer %p(%d), %p(%d)\n",
bulkdata != NULL?bulkdata->d[0].os_data_ptr : NULL,
bulkdata != NULL?bulkdata->d[0].data_length : 0,
diff --git a/drivers/staging/csr/csr_wifi_hip_udi.c b/drivers/staging/csr/csr_wifi_hip_udi.c
index 07cfd36c4971..a65b822db698 100644
--- a/drivers/staging/csr/csr_wifi_hip_udi.c
+++ b/drivers/staging/csr/csr_wifi_hip_udi.c
@@ -64,104 +64,104 @@ s32 unifi_print_status(card_t *card, char *str, s32 *remain)
}
i = n = 0;
- written = CsrSnprintf(p, remaining, "Chip ID %u\n",
+ written = scnprintf(p, remaining, "Chip ID %u\n",
(u16)card->chip_id);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "Chip Version %04X\n",
+ written = scnprintf(p, remaining, "Chip Version %04X\n",
card->chip_version);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "HIP v%u.%u\n",
+ written = scnprintf(p, remaining, "HIP v%u.%u\n",
(card->config_data.version >> 8) & 0xFF,
card->config_data.version & 0xFF);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "Build %lu: %s\n",
+ written = scnprintf(p, remaining, "Build %u: %s\n",
card->build_id, card->build_id_string);
UNIFI_SNPRINTF_RET(p, remaining, written);
cfg = &card->config_data;
- written = CsrSnprintf(p, remaining, "sdio ctrl offset %u\n",
+ written = scnprintf(p, remaining, "sdio ctrl offset %u\n",
cfg->sdio_ctrl_offset);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "fromhost sigbuf handle %u\n",
+ written = scnprintf(p, remaining, "fromhost sigbuf handle %u\n",
cfg->fromhost_sigbuf_handle);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "tohost_sigbuf_handle %u\n",
+ written = scnprintf(p, remaining, "tohost_sigbuf_handle %u\n",
cfg->tohost_sigbuf_handle);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "num_fromhost_sig_frags %u\n",
+ written = scnprintf(p, remaining, "num_fromhost_sig_frags %u\n",
cfg->num_fromhost_sig_frags);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "num_tohost_sig_frags %u\n",
+ written = scnprintf(p, remaining, "num_tohost_sig_frags %u\n",
cfg->num_tohost_sig_frags);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "num_fromhost_data_slots %u\n",
+ written = scnprintf(p, remaining, "num_fromhost_data_slots %u\n",
cfg->num_fromhost_data_slots);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "num_tohost_data_slots %u\n",
+ written = scnprintf(p, remaining, "num_tohost_data_slots %u\n",
cfg->num_tohost_data_slots);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "data_slot_size %u\n",
+ written = scnprintf(p, remaining, "data_slot_size %u\n",
cfg->data_slot_size);
UNIFI_SNPRINTF_RET(p, remaining, written);
/* Added by protocol version 0x0001 */
- written = CsrSnprintf(p, remaining, "overlay_size %u\n",
+ written = scnprintf(p, remaining, "overlay_size %u\n",
(u16)cfg->overlay_size);
UNIFI_SNPRINTF_RET(p, remaining, written);
/* Added by protocol version 0x0300 */
- written = CsrSnprintf(p, remaining, "data_slot_round %u\n",
+ written = scnprintf(p, remaining, "data_slot_round %u\n",
cfg->data_slot_round);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "sig_frag_size %u\n",
+ written = scnprintf(p, remaining, "sig_frag_size %u\n",
cfg->sig_frag_size);
UNIFI_SNPRINTF_RET(p, remaining, written);
/* Added by protocol version 0x0300 */
- written = CsrSnprintf(p, remaining, "tohost_sig_pad %u\n",
+ written = scnprintf(p, remaining, "tohost_sig_pad %u\n",
cfg->tohost_signal_padding);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "\nInternal state:\n");
+ written = scnprintf(p, remaining, "\nInternal state:\n");
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "Last PHY PANIC: %04x:%04x\n",
+ written = scnprintf(p, remaining, "Last PHY PANIC: %04x:%04x\n",
card->last_phy_panic_code, card->last_phy_panic_arg);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "Last MAC PANIC: %04x:%04x\n",
+ written = scnprintf(p, remaining, "Last MAC PANIC: %04x:%04x\n",
card->last_mac_panic_code, card->last_mac_panic_arg);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "fhsr: %u\n",
+ written = scnprintf(p, remaining, "fhsr: %u\n",
(u16)card->from_host_signals_r);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "fhsw: %u\n",
+ written = scnprintf(p, remaining, "fhsw: %u\n",
(u16)card->from_host_signals_w);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "thsr: %u\n",
+ written = scnprintf(p, remaining, "thsr: %u\n",
(u16)card->to_host_signals_r);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "thsw: %u\n",
+ written = scnprintf(p, remaining, "thsw: %u\n",
(u16)card->to_host_signals_w);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining,
- "fh buffer contains: %u signals, %u bytes\n",
+ written = scnprintf(p, remaining,
+ "fh buffer contains: %d signals, %td bytes\n",
card->fh_buffer.count,
card->fh_buffer.ptr - card->fh_buffer.buf);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "paused: ");
+ written = scnprintf(p, remaining, "paused: ");
UNIFI_SNPRINTF_RET(p, remaining, written);
for (i = 0; i < sizeof(card->tx_q_paused_flag) / sizeof(card->tx_q_paused_flag[0]); i++)
{
- written = CsrSnprintf(p, remaining, card->tx_q_paused_flag[i]?"1" : "0");
+ written = scnprintf(p, remaining, card->tx_q_paused_flag[i]?"1" : "0");
UNIFI_SNPRINTF_RET(p, remaining, written);
}
- written = CsrSnprintf(p, remaining, "\n");
+ written = scnprintf(p, remaining, "\n");
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining,
+ written = scnprintf(p, remaining,
"fh command q: %u waiting, %u free of %u:\n",
CSR_WIFI_HIP_Q_SLOTS_USED(&card->fh_command_queue),
CSR_WIFI_HIP_Q_SLOTS_FREE(&card->fh_command_queue),
@@ -169,7 +169,7 @@ s32 unifi_print_status(card_t *card, char *str, s32 *remain)
UNIFI_SNPRINTF_RET(p, remaining, written);
for (i = 0; i < UNIFI_NO_OF_TX_QS; i++)
{
- written = CsrSnprintf(p, remaining,
+ written = scnprintf(p, remaining,
"fh traffic q[%u]: %u waiting, %u free of %u:\n",
i,
CSR_WIFI_HIP_Q_SLOTS_USED(&card->fh_traffic_queue[i]),
@@ -178,58 +178,58 @@ s32 unifi_print_status(card_t *card, char *str, s32 *remain)
UNIFI_SNPRINTF_RET(p, remaining, written);
}
- written = CsrSnprintf(p, remaining, "fh data slots free: %u\n",
+ written = scnprintf(p, remaining, "fh data slots free: %u\n",
card->from_host_data?CardGetFreeFromHostDataSlots(card) : 0);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "From host data slots:");
+ written = scnprintf(p, remaining, "From host data slots:");
UNIFI_SNPRINTF_RET(p, remaining, written);
n = card->config_data.num_fromhost_data_slots;
for (i = 0; i < n && card->from_host_data; i++)
{
- written = CsrSnprintf(p, remaining, " %u",
+ written = scnprintf(p, remaining, " %u",
(u16)card->from_host_data[i].bd.data_length);
UNIFI_SNPRINTF_RET(p, remaining, written);
}
- written = CsrSnprintf(p, remaining, "\n");
+ written = scnprintf(p, remaining, "\n");
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "To host data slots:");
+ written = scnprintf(p, remaining, "To host data slots:");
UNIFI_SNPRINTF_RET(p, remaining, written);
n = card->config_data.num_tohost_data_slots;
for (i = 0; i < n && card->to_host_data; i++)
{
- written = CsrSnprintf(p, remaining, " %u",
+ written = scnprintf(p, remaining, " %u",
(u16)card->to_host_data[i].data_length);
UNIFI_SNPRINTF_RET(p, remaining, written);
}
- written = CsrSnprintf(p, remaining, "\n");
+ written = scnprintf(p, remaining, "\n");
UNIFI_SNPRINTF_RET(p, remaining, written);
#ifdef CSR_UNSAFE_SDIO_ACCESS
- written = CsrSnprintf(p, remaining, "Host State: %s\n", states[card->host_state]);
+ written = scnprintf(p, remaining, "Host State: %s\n", states[card->host_state]);
UNIFI_SNPRINTF_RET(p, remaining, written);
r = unifi_check_io_status(card, &iostate);
if (iostate == 1)
{
- written = CsrSnprintf(p, remaining, "I/O Check: F1 disabled\n");
+ written = scnprintf(p, remaining, "I/O Check: F1 disabled\n");
UNIFI_SNPRINTF_RET(p, remaining, written);
}
else
{
if (iostate == 1)
{
- written = CsrSnprintf(p, remaining, "I/O Check: pending interrupt\n");
+ written = scnprintf(p, remaining, "I/O Check: pending interrupt\n");
UNIFI_SNPRINTF_RET(p, remaining, written);
}
- written = CsrSnprintf(p, remaining, "BH reason interrupt = %d\n",
+ written = scnprintf(p, remaining, "BH reason interrupt = %d\n",
card->bh_reason_unifi);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "BH reason host = %d\n",
+ written = scnprintf(p, remaining, "BH reason host = %d\n",
card->bh_reason_host);
UNIFI_SNPRINTF_RET(p, remaining, written);
@@ -238,26 +238,26 @@ s32 unifi_print_status(card_t *card, char *str, s32 *remain)
r = unifi_read_8_or_16(card, card->sdio_ctrl_addr + 2, &b);
if ((r == CSR_RESULT_SUCCESS) && (!(b & 0x80)))
{
- written = CsrSnprintf(p, remaining, "fhsr: %u (driver thinks is %u)\n",
+ written = scnprintf(p, remaining, "fhsr: %u (driver thinks is %u)\n",
b, card->from_host_signals_r);
UNIFI_SNPRINTF_RET(p, remaining, written);
break;
}
}
iostate = unifi_read_shared_count(card, card->sdio_ctrl_addr + 4);
- written = CsrSnprintf(p, remaining, "thsw: %u (driver thinks is %u)\n",
+ written = scnprintf(p, remaining, "thsw: %u (driver thinks is %u)\n",
iostate, card->to_host_signals_w);
UNIFI_SNPRINTF_RET(p, remaining, written);
}
#endif
- written = CsrSnprintf(p, remaining, "\nStats:\n");
+ written = scnprintf(p, remaining, "\nStats:\n");
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "Total SDIO bytes: R=%lu W=%lu\n",
+ written = scnprintf(p, remaining, "Total SDIO bytes: R=%u W=%u\n",
card->sdio_bytes_read, card->sdio_bytes_written);
UNIFI_SNPRINTF_RET(p, remaining, written);
- written = CsrSnprintf(p, remaining, "Interrupts generated on card: %lu\n",
+ written = scnprintf(p, remaining, "Interrupts generated on card: %u\n",
card->unifi_interrupt_seq);
UNIFI_SNPRINTF_RET(p, remaining, written);
diff --git a/drivers/staging/csr/csr_wifi_hip_unifi.h b/drivers/staging/csr/csr_wifi_hip_unifi.h
index dc3c60b49702..2923e2ef12f2 100644
--- a/drivers/staging/csr/csr_wifi_hip_unifi.h
+++ b/drivers/staging/csr/csr_wifi_hip_unifi.h
@@ -98,7 +98,6 @@ extern "C" {
#include "csr_framework_ext.h" /* from the synergy porting folder */
#include "csr_sdio.h" /* from the synergy porting folder */
#include "csr_macro.h" /* from the synergy porting folder */
-#include "csr_formatted_io.h" /* from the synergy gsp folder */
#include "csr_wifi_result.h"
/* Utility MACROS. Note that UNIFI_MAC_ADDRESS_CMP returns TRUE on success */
diff --git a/drivers/staging/csr/drv.c b/drivers/staging/csr/drv.c
index b2c27f4f03d4..249758076a75 100644
--- a/drivers/staging/csr/drv.c
+++ b/drivers/staging/csr/drv.c
@@ -15,8 +15,6 @@
* ---------------------------------------------------------------------------
*/
-
-
/*
* Porting Notes:
* Part of this file contains an example for how to glue the OS layer
@@ -37,6 +35,7 @@
#include <linux/poll.h>
#include <asm/uaccess.h>
#include <linux/jiffies.h>
+#include <linux/version.h>
#include "csr_wifi_hip_unifiversion.h"
#include "unifi_priv.h"
@@ -124,11 +123,7 @@ static void udi_set_log_filter(ul_client_t *pcli,
/* Mutex to protect access to priv->sme_cli */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
DEFINE_SEMAPHORE(udi_mutex);
-#else
-DECLARE_MUTEX(udi_mutex);
-#endif
s32 CsrHipResultToStatus(CsrResult csrResult)
{
@@ -1980,18 +1975,6 @@ uf_sme_queue_message(unifi_priv_t *priv, u8 *buffer, int length)
} /* uf_sme_queue_message() */
#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
-#define UF_DEVICE_CREATE(_class, _parent, _devno, _priv, _fmt, _args) \
- device_create(_class, _parent, _devno, _priv, _fmt, _args)
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
-#define UF_DEVICE_CREATE(_class, _parent, _devno, _priv, _fmt, _args) \
- device_create_drvdata(_class, _parent, _devno, _priv, _fmt, _args)
-#else
-#define UF_DEVICE_CREATE(_class, _parent, _devno, _priv, _fmt, _args) \
- device_create(_class, _parent, _devno, _fmt, _args)
-#endif
-
/*
****************************************************************************
*
@@ -2009,17 +1992,6 @@ static struct file_operations unifi_fops = {
.poll = unifi_poll,
};
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
-#define UF_DEVICE_CREATE(_class, _parent, _devno, _priv, _fmt, _args) \
- device_create(_class, _parent, _devno, _priv, _fmt, _args)
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
-#define UF_DEVICE_CREATE(_class, _parent, _devno, _priv, _fmt, _args) \
- device_create_drvdata(_class, _parent, _devno, _priv, _fmt, _args)
-#else
-#define UF_DEVICE_CREATE(_class, _parent, _devno, _priv, _fmt, _args) \
- device_create(_class, _parent, _devno, _fmt, _args)
-#endif
-
static dev_t unifi_first_devno;
static struct class *unifi_class;
@@ -2042,11 +2014,11 @@ int uf_create_device_nodes(unifi_priv_t *priv, int bus_id)
}
#ifdef SDIO_EXPORTS_STRUCT_DEVICE
- if (!UF_DEVICE_CREATE(unifi_class, priv->unifi_device,
- devno, priv, "unifi%d", bus_id)) {
+ if (!device_create(unifi_class, priv->unifi_device,
+ devno, priv, "unifi%d", bus_id)) {
#else
- priv->unifi_device = UF_DEVICE_CREATE(unifi_class, NULL,
- devno, priv, "unifi%d", bus_id);
+ priv->unifi_device = device_create(unifi_class, NULL,
+ devno, priv, "unifi%d", bus_id);
if (priv->unifi_device == NULL) {
#endif /* SDIO_EXPORTS_STRUCT_DEVICE */
@@ -2068,13 +2040,13 @@ int uf_create_device_nodes(unifi_priv_t *priv, int bus_id)
return r;
}
- if (!UF_DEVICE_CREATE(unifi_class,
+ if (!device_create(unifi_class,
#ifdef SDIO_EXPORTS_STRUCT_DEVICE
- priv->unifi_device,
+ priv->unifi_device,
#else
- NULL,
+ NULL,
#endif /* SDIO_EXPORTS_STRUCT_DEVICE */
- devno, priv, "unifiudi%d", bus_id)) {
+ devno, priv, "unifiudi%d", bus_id)) {
device_destroy(unifi_class, priv->unifi_cdev.dev);
cdev_del(&priv->unifiudi_cdev);
cdev_del(&priv->unifi_cdev);
diff --git a/drivers/staging/csr/firmware.c b/drivers/staging/csr/firmware.c
index d14e11839618..b6d8a6e52915 100644
--- a/drivers/staging/csr/firmware.c
+++ b/drivers/staging/csr/firmware.c
@@ -286,7 +286,7 @@ uf_run_unifihelper(unifi_priv_t *priv)
unifi_trace(priv, UDBG2, "running %s %s %s\n", argv[0], argv[1], argv[2]);
- r = call_usermodehelper(argv[0], argv, envp, 0);
+ r = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
return r;
#else
@@ -402,9 +402,7 @@ int uf_release_firmware_files(unifi_priv_t *priv)
int uf_release_firmware(unifi_priv_t *priv, struct dlpriv *to_free)
{
if (to_free != NULL) {
- if (to_free->fw_desc != NULL) {
- release_firmware((const struct firmware *)to_free->fw_desc);
- }
+ release_firmware((const struct firmware *)to_free->fw_desc);
to_free->fw_desc = NULL;
to_free->dl_data = NULL;
to_free->dl_len = 0;
diff --git a/drivers/staging/csr/io.c b/drivers/staging/csr/io.c
index e6503d9620a4..caf48e3120ca 100644
--- a/drivers/staging/csr/io.c
+++ b/drivers/staging/csr/io.c
@@ -31,6 +31,7 @@
* ---------------------------------------------------------------------------
*/
#include <linux/proc_fs.h>
+#include <linux/version.h>
#include "csr_wifi_hip_unifi.h"
#include "csr_wifi_hip_unifiversion.h"
@@ -38,7 +39,6 @@
#include "unifiio.h"
#include "unifi_priv.h"
-
/*
* Array of pointers to context structs for unifi devices that are present.
* The index in the array corresponds to the wlan interface number
@@ -70,11 +70,7 @@ static int In_use[MAX_UNIFI_DEVS];
* Mutex to prevent UDI clients to open the character device before the priv
* is created and initialised.
*/
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
DEFINE_SEMAPHORE(Unifi_instance_mutex);
-#else
-DECLARE_MUTEX(Unifi_instance_mutex);
-#endif
/*
* When the device is removed, unregister waits on Unifi_cleanup_wq
* until all the UDI clients release the character device.
@@ -177,21 +173,6 @@ uf_register_netdev(unifi_priv_t *priv, int interfaceTag)
/* The device is registed */
interfacePriv->netdev_registered = 1;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-#ifdef CONFIG_NET_SCHED
- /*
- * IMPORTANT:
- * uf_install_qdisc() holds the network device lock, we can not
- * install the qdisk before the network device is registered.
- */
- r = uf_install_qdisc(priv->netdev[interfaceTag]);
- if (r) {
- unifi_error(priv, "Failed to install qdisc\n");
- return r;
- }
-#endif /* CONFIG_NET_SCHED */
-#endif /* LINUX_VERSION_CODE */
-
#ifdef CSR_SUPPORT_SME
/*
* Register the inet handler; it notifies us for changes in the IP address.
@@ -347,7 +328,7 @@ register_unifi_sdio(CsrSdioFunction *sdio_dev, int bus_id, struct device *dev)
/*
* We use the slot number as unifi device index.
*/
- snprintf(priv->proc_entry_name, 64, "driver/unifi%d", priv->instance);
+ scnprintf(priv->proc_entry_name, 64, "driver/unifi%d", priv->instance);
/*
* The following complex casting is in place in order to eliminate 64-bit compilation warning
* "cast to/from pointer from/to integer of different size"
@@ -669,7 +650,7 @@ unregister_unifi_sdio(int bus_id)
if(interfacePriv->netdev_registered)
{
netif_carrier_off(priv->netdev[interfaceTag]);
- UF_NETIF_TX_STOP_ALL_QUEUES(priv->netdev[interfaceTag]);
+ netif_tx_stop_all_queues(priv->netdev[interfaceTag]);
}
}
@@ -904,54 +885,54 @@ uf_read_proc(char *page, char **start, off_t offset, int count,
orig_p = p;
- written = CsrSnprintf(p, remain, "UniFi SDIO Driver: %s %s %s\n",
+ written = scnprintf(p, remain, "UniFi SDIO Driver: %s %s %s\n",
CSR_WIFI_VERSION, __DATE__, __TIME__);
UNIFI_SNPRINTF_RET(p, remain, written);
#ifdef CSR_SME_USERSPACE
- written = CsrSnprintf(p, remain, "SME: CSR userspace ");
+ written = scnprintf(p, remain, "SME: CSR userspace ");
UNIFI_SNPRINTF_RET(p, remain, written);
#ifdef CSR_SUPPORT_WEXT
- written = CsrSnprintf(p, remain, "with WEXT support\n");
+ written = scnprintf(p, remain, "with WEXT support\n");
#else
- written = CsrSnprintf(p, remain, "\n");
+ written = scnprintf(p, remain, "\n");
#endif /* CSR_SUPPORT_WEXT */
UNIFI_SNPRINTF_RET(p, remain, written);
#endif /* CSR_SME_USERSPACE */
#ifdef CSR_NATIVE_LINUX
- written = CsrSnprintf(p, remain, "SME: native\n");
+ written = scnprintf(p, remain, "SME: native\n");
UNIFI_SNPRINTF_RET(p, remain, written);
#endif
#ifdef CSR_SUPPORT_SME
- written = CsrSnprintf(p, remain,
- "Firmware (ROM) build:%lu, Patch:%lu\n",
+ written = scnprintf(p, remain,
+ "Firmware (ROM) build:%u, Patch:%u\n",
priv->card_info.fw_build,
priv->sme_versions.firmwarePatch);
UNIFI_SNPRINTF_RET(p, remain, written);
#endif
p += unifi_print_status(priv->card, p, &remain);
- written = CsrSnprintf(p, remain, "Last dbg str: %s\n",
+ written = scnprintf(p, remain, "Last dbg str: %s\n",
priv->last_debug_string);
UNIFI_SNPRINTF_RET(p, remain, written);
- written = CsrSnprintf(p, remain, "Last dbg16:");
+ written = scnprintf(p, remain, "Last dbg16:");
UNIFI_SNPRINTF_RET(p, remain, written);
for (i = 0; i < 8; i++) {
- written = CsrSnprintf(p, remain, " %04X",
+ written = scnprintf(p, remain, " %04X",
priv->last_debug_word16[i]);
UNIFI_SNPRINTF_RET(p, remain, written);
}
- written = CsrSnprintf(p, remain, "\n");
+ written = scnprintf(p, remain, "\n");
UNIFI_SNPRINTF_RET(p, remain, written);
- written = CsrSnprintf(p, remain, " ");
+ written = scnprintf(p, remain, " ");
UNIFI_SNPRINTF_RET(p, remain, written);
for (; i < 16; i++) {
- written = CsrSnprintf(p, remain, " %04X",
+ written = scnprintf(p, remain, " %04X",
priv->last_debug_word16[i]);
UNIFI_SNPRINTF_RET(p, remain, written);
}
- written = CsrSnprintf(p, remain, "\n");
+ written = scnprintf(p, remain, "\n");
UNIFI_SNPRINTF_RET(p, remain, written);
*start = page;
diff --git a/drivers/staging/csr/monitor.c b/drivers/staging/csr/monitor.c
index 628782ad641e..7c524a18958e 100644
--- a/drivers/staging/csr/monitor.c
+++ b/drivers/staging/csr/monitor.c
@@ -10,6 +10,7 @@
* ---------------------------------------------------------------------------
*/
+#include <linux/version.h>
#include "unifi_priv.h"
#ifdef UNIFI_SNIFF_ARPHRD
@@ -23,8 +24,6 @@
#define ETH_P_80211_RAW ETH_P_ALL
#endif
-
-
/*
* ---------------------------------------------------------------------------
* uf_start_sniff
@@ -192,11 +191,7 @@ netrx_radiotap(unifi_priv_t *priv,
skb->dev = dev;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
skb->mac_header = skb->data;
-#else
- skb->mac.raw = skb->data;
-#endif
skb->pkt_type = PACKET_OTHERHOST;
skb->protocol = __constant_htons(ETH_P_80211_RAW);
memset(skb->cb, 0, sizeof(skb->cb));
diff --git a/drivers/staging/csr/netdev.c b/drivers/staging/csr/netdev.c
index 1e6e111a8e15..9a52ab408e1a 100644
--- a/drivers/staging/csr/netdev.c
+++ b/drivers/staging/csr/netdev.c
@@ -15,7 +15,6 @@
* ---------------------------------------------------------------------------
*/
-
/*
* Porting Notes:
* This file implements the data plane of the UniFi linux driver.
@@ -48,59 +47,14 @@
#include <linux/etherdevice.h>
#include <linux/mutex.h>
#include <linux/semaphore.h>
-
+#include <linux/version.h>
#include <linux/vmalloc.h>
#include "csr_wifi_hip_unifi.h"
#include "csr_wifi_hip_conversions.h"
#include "unifi_priv.h"
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
-#include <net/iw_handler.h>
-#endif
#include <net/pkt_sched.h>
-/* ALLOW_Q_PAUSE: Pre 2.6.28 kernels do not support multiple driver queues (required for QoS).
- * In order to support QoS in these kernels, multiple queues are implemented in the driver. But since
- * there is only a single queue in the kernel (leading to multiple queues in the driver) there is no possibility
- * of stopping a particular queue in the kernel. Stopping the single kernel queue leads to undesirable starvation
- * of driver queues. One of the proposals is to not stop the kernel queue but to prevent dequeuing from the
- * 'stopped' driver queue. Allow q pause is an experimental implementation of this scheme for pre 2.6.28 kernels.
- * When NOT defined, queues are paused locally in the driver and packets are dequeued for transmission only from the
- * unpaused queues. When Allow q pause is defined the kernel queue is stopped whenever any driver queue is paused.
- */
-#define ALLOW_Q_PAUSE
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
-#ifdef UNIFI_NET_NAME
-#define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \
- do { \
- static char name[8]; \
- sprintf(name, "%s%s", UNIFI_NET_NAME, _name); \
- _dev = alloc_netdev_mq(_size, name, _setup, _num_of_queues); \
- } while (0);
-#else
-#define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \
- do { \
- _dev = alloc_etherdev_mq(_size, _num_of_queues); \
- } while (0);
-#endif /* UNIFI_NET_NAME */
-#else
-#ifdef UNIFI_NET_NAME
-#define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \
- do { \
- static char name[8]; \
- sprintf(name, "%s%s", UNIFI_NET_NAME, _name); \
- _dev = alloc_netdev(_size, name, _setup); \
- } while (0);
-#else
-#define UF_ALLOC_NETDEV(_dev, _size, _name, _setup, _num_of_queues) \
- do { \
- _dev = alloc_etherdev(_size); \
- } while (0);
-#endif /* UNIFI_NET_NAME */
-#endif /* LINUX_VERSION_CODE */
-
-
/* Wext handler is suported only if CSR_SUPPORT_WEXT is defined */
#ifdef CSR_SUPPORT_WEXT
extern struct iw_handler_def unifi_iw_handler_def;
@@ -119,20 +73,8 @@ static int uf_net_open(struct net_device *dev);
static int uf_net_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
static int uf_net_stop(struct net_device *dev);
static struct net_device_stats *uf_net_get_stats(struct net_device *dev);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
static u16 uf_net_select_queue(struct net_device *dev, struct sk_buff *skb);
-#endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
static netdev_tx_t uf_net_xmit(struct sk_buff *skb, struct net_device *dev);
-#else
-static int uf_net_xmit(struct sk_buff *skb, struct net_device *dev);
-#ifndef NETDEV_TX_OK
-#define NETDEV_TX_OK 0
-#endif
-#ifndef NETDEV_TX_BUSY
-#define NETDEV_TX_BUSY 1
-#endif
-#endif
static void uf_set_multicast_list(struct net_device *dev);
@@ -182,62 +124,8 @@ struct uf_tx_packet_data {
unsigned long host_tag;
};
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-static int uf_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd);
-static int uf_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd);
-static struct sk_buff *uf_qdiscop_dequeue(struct Qdisc* qd);
-static void uf_qdiscop_reset(struct Qdisc* qd);
-static void uf_qdiscop_destroy(struct Qdisc* qd);
-static int uf_qdiscop_dump(struct Qdisc *qd, struct sk_buff *skb);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
-static int uf_qdiscop_tune(struct Qdisc *qd, struct nlattr *opt);
-static int uf_qdiscop_init(struct Qdisc *qd, struct nlattr *opt);
-#else
-static int uf_qdiscop_tune(struct Qdisc *qd, struct rtattr *opt);
-static int uf_qdiscop_init(struct Qdisc *qd, struct rtattr *opt);
-#endif
-#endif
-
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-/* queueing discipline operations */
-static struct Qdisc_ops uf_qdisc_ops =
-{
- .next = NULL,
- .cl_ops = NULL,
- .id = "UniFi Qdisc",
- .priv_size = sizeof(struct uf_sched_data),
-
- .enqueue = uf_qdiscop_enqueue,
- .dequeue = uf_qdiscop_dequeue,
- .requeue = uf_qdiscop_requeue,
- .drop = NULL, /* drop not needed since we are always the root qdisc */
-
- .init = uf_qdiscop_init,
- .reset = uf_qdiscop_reset,
- .destroy = uf_qdiscop_destroy,
- .change = uf_qdiscop_tune,
-
- .dump = uf_qdiscop_dump,
-};
-#endif /* LINUX_VERSION_CODE */
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
-#define UF_QDISC_CREATE_DFLT(_dev, _ops, _root)
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
-#define UF_QDISC_CREATE_DFLT(_dev, _ops, _root) \
- qdisc_create_dflt(dev, netdev_get_tx_queue(_dev, 0), _ops, _root)
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
-#define UF_QDISC_CREATE_DFLT(_dev, _ops, _root) \
- qdisc_create_dflt(dev, _ops, _root)
-#else
-#define UF_QDISC_CREATE_DFLT(_dev, _ops, _root) \
- qdisc_create_dflt(dev, _ops)
-#endif /* LINUX_VERSION_CODE */
-
#endif /* CONFIG_NET_SCHED */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
static const struct net_device_ops uf_netdev_ops =
{
.ndo_open = uf_net_open,
@@ -248,7 +136,6 @@ static const struct net_device_ops uf_netdev_ops =
.ndo_set_rx_mode = uf_set_multicast_list,
.ndo_select_queue = uf_net_select_queue,
};
-#endif
static u8 oui_rfc1042[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 };
static u8 oui_8021h[P80211_OUI_LEN] = { 0x00, 0x00, 0xf8 };
@@ -310,7 +197,7 @@ uf_alloc_netdevice(CsrSdioFunction *sdio_dev, int bus_id)
* The RedHat 9 redhat-config-network tool doesn't recognise wlan* devices,
* so use "eth*" (like other wireless extns drivers).
*/
- UF_ALLOC_NETDEV(dev, sizeof(unifi_priv_t)+sizeof(netInterface_priv_t), "%d", ether_setup, UNIFI_TRAFFIC_Q_MAX);
+ dev = alloc_etherdev_mq(sizeof(unifi_priv_t) + sizeof(netInterface_priv_t), UNIFI_TRAFFIC_Q_MAX);
if (dev == NULL) {
return NULL;
@@ -332,22 +219,7 @@ uf_alloc_netdevice(CsrSdioFunction *sdio_dev, int bus_id)
priv->interfacePriv[0] = interfacePriv;
/* Setup / override net_device fields */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
dev->netdev_ops = &uf_netdev_ops;
-#else
- dev->open = uf_net_open;
- dev->stop = uf_net_stop;
- dev->hard_start_xmit = uf_net_xmit;
- dev->do_ioctl = uf_net_ioctl;
-
- /* called by /proc/net/dev */
- dev->get_stats = uf_net_get_stats;
-
- dev->set_multicast_list = uf_set_multicast_list;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
- dev->select_queue = uf_net_select_queue;
-#endif
-#endif
#ifdef CSR_SUPPORT_WEXT
dev->wireless_handlers = &unifi_iw_handler_def;
@@ -440,21 +312,13 @@ uf_alloc_netdevice(CsrSdioFunction *sdio_dev, int bus_id)
interfacePriv->connected = UnifiConnectedUnknown; /* -1 unknown, 0 no, 1 yes */
#ifdef USE_DRIVER_LOCK
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
sema_init(&priv->lock, 1);
-#else
- init_MUTEX(&priv->lock);
-#endif
#endif /* USE_DRIVER_LOCK */
spin_lock_init(&priv->send_signal_lock);
spin_lock_init(&priv->m4_lock);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
sema_init(&priv->ba_mutex, 1);
-#else
- init_MUTEX(&priv->ba_mutex);
-#endif
#if (defined(CSR_WIFI_SECURITY_WAPI_ENABLE) && defined(CSR_WIFI_SECURITY_WAPI_SW_ENCRYPTION))
spin_lock_init(&priv->wapi_lock);
@@ -487,16 +351,8 @@ uf_alloc_netdevice(CsrSdioFunction *sdio_dev, int bus_id)
#endif
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-#ifdef CONFIG_NET_SCHED
- /* Register the qdisc operations */
- register_qdisc(&uf_qdisc_ops);
-#endif /* CONFIG_NET_SCHED */
-#endif /* LINUX_VERSION_CODE */
-
priv->ref_count = 1;
-
priv->amp_client = NULL;
priv->coredump_mode = 0;
priv->ptest_mode = 0;
@@ -566,7 +422,7 @@ uf_alloc_netdevice_for_other_interfaces(unifi_priv_t *priv, u16 interfaceTag)
* The RedHat 9 redhat-config-network tool doesn't recognise wlan* devices,
* so use "eth*" (like other wireless extns drivers).
*/
- UF_ALLOC_NETDEV(dev, sizeof(netInterface_priv_t), "%d", ether_setup, 1);
+ dev = alloc_etherdev_mq(sizeof(netInterface_priv_t), 1);
if (dev == NULL) {
return FALSE;
}
@@ -589,19 +445,7 @@ uf_alloc_netdevice_for_other_interfaces(unifi_priv_t *priv, u16 interfaceTag)
INIT_LIST_HEAD(&interfacePriv->rx_controlled_list);
/* Setup / override net_device fields */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,29)
dev->netdev_ops = &uf_netdev_ops;
-#else
- dev->open = uf_net_open;
- dev->stop = uf_net_stop;
- dev->hard_start_xmit = uf_net_xmit;
- dev->do_ioctl = uf_net_ioctl;
-
- /* called by /proc/net/dev */
- dev->get_stats = uf_net_get_stats;
-
- dev->set_multicast_list = uf_set_multicast_list;
-#endif
#ifdef CSR_SUPPORT_WEXT
dev->wireless_handlers = &unifi_iw_handler_def;
@@ -686,13 +530,6 @@ uf_free_netdevice(unifi_priv_t *priv)
spin_unlock_irqrestore(&priv->wapi_lock, flags);
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-#ifdef CONFIG_NET_SCHED
- /* Unregister the qdisc operations */
- unregister_qdisc(&uf_qdisc_ops);
-#endif /* CONFIG_NET_SCHED */
-#endif /* LINUX_VERSION_CODE */
-
#ifdef CSR_SUPPORT_WEXT
/* Unregister callback for netdevice state changes */
unregister_netdevice_notifier(&uf_netdev_notifier);
@@ -700,10 +537,8 @@ uf_free_netdevice(unifi_priv_t *priv)
#ifdef CSR_SUPPORT_SME
/* Cancel work items and destroy the workqueue */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
cancel_work_sync(&priv->multicast_list_task);
#endif
-#endif
/* Destroy the workqueues. */
flush_workqueue(priv->unifi_workqueue);
destroy_workqueue(priv->unifi_workqueue);
@@ -778,7 +613,7 @@ uf_net_open(struct net_device *dev)
}
#endif
- UF_NETIF_TX_START_ALL_QUEUES(dev);
+ netif_tx_start_all_queues(dev);
func_exit();
return 0;
@@ -808,7 +643,7 @@ uf_net_stop(struct net_device *dev)
func_enter();
#endif
- UF_NETIF_TX_STOP_ALL_QUEUES(dev);
+ netif_tx_stop_all_queues(dev);
func_exit();
return 0;
@@ -957,7 +792,6 @@ get_packet_priority(unifi_priv_t *priv, struct sk_buff *skb, const struct ethhdr
return priority;
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
/*
* ---------------------------------------------------------------------------
* uf_net_select_queue
@@ -1005,7 +839,6 @@ uf_net_select_queue(struct net_device *dev, struct sk_buff *skb)
func_exit();
return (u16)queue;
} /* uf_net_select_queue() */
-#endif
int
skb_add_llc_snap(struct net_device *dev, struct sk_buff *skb, int proto)
@@ -1915,11 +1748,7 @@ send_ma_pkt_request(unifi_priv_t *priv, struct sk_buff *skb, const struct ethhdr
* The controlled port is handled in the qdisc dequeue handler.
* ---------------------------------------------------------------------------
*/
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
static netdev_tx_t
-#else
-static int
-#endif
uf_net_xmit(struct sk_buff *skb, struct net_device *dev)
{
netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev);
@@ -1929,9 +1758,7 @@ uf_net_xmit(struct sk_buff *skb, struct net_device *dev)
int result;
static tx_signal_handler tx_handler;
CSR_PRIORITY priority;
-#if !defined (CONFIG_NET_SCHED) || (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28))
CsrWifiRouterCtrlPortAction port_action;
-#endif /* CONFIG_NET_SCHED */
func_enter();
@@ -1956,11 +1783,6 @@ uf_net_xmit(struct sk_buff *skb, struct net_device *dev)
port = UF_UNCONTROLLED_PORT_Q;
}
-#if defined (CONFIG_NET_SCHED) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28))
- /* Remove the ethernet header */
- skb_pull(skb, ETH_HLEN);
- result = tx_handler(priv, skb, &ehdr, priority);
-#else
/* Uncontrolled port rules apply */
port_action = verify_port(priv
, (((CSR_WIFI_ROUTER_CTRL_MODE_STA == interfacePriv->interfaceMode)||(CSR_WIFI_ROUTER_CTRL_MODE_P2PCLI== interfacePriv->interfaceMode))? interfacePriv->bssid.a: ehdr.h_dest)
@@ -1986,7 +1808,6 @@ uf_net_xmit(struct sk_buff *skb, struct net_device *dev)
func_exit();
return NETDEV_TX_OK;
}
-#endif /* CONFIG_NET_SCHED */
if (result == NETDEV_TX_OK) {
#if (defined(CSR_WIFI_SECURITY_WAPI_ENABLE) && defined(CSR_WIFI_SECURITY_WAPI_SW_ENCRYPTION))
@@ -2059,7 +1880,6 @@ unifi_pause_xmit(void *ospriv, unifi_TrafficQueue queue)
func_enter();
unifi_trace(priv, UDBG2, "Stopping queue %d\n", queue);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
for(i=0;i<CSR_WIFI_NUM_INTERFACES;i++)
{
if (netif_running(priv->netdev[i]))
@@ -2067,24 +1887,6 @@ unifi_pause_xmit(void *ospriv, unifi_TrafficQueue queue)
netif_stop_subqueue(priv->netdev[i], (u16)queue);
}
}
-#else
-#ifdef ALLOW_Q_PAUSE
- unifi_trace(priv, UDBG2, "Stopping netif\n");
- /* stop the traffic from all the interfaces. */
- for(i=0;i<CSR_WIFI_NUM_INTERFACES;i++)
- {
- if (netif_running(priv->netdev[i])) {
- UF_NETIF_TX_STOP_ALL_QUEUES(priv->netdev[i]);
- }
- }
-#else
- if (net_is_tx_q_paused(priv, queue)) {
- unifi_trace(priv, UDBG2, "Queue already stopped\n");
- return;
- }
- net_tx_q_pause(priv, queue);
-#endif
-#endif
#ifdef CSR_SUPPORT_SME
if(queue<=3) {
@@ -2108,7 +1910,6 @@ unifi_restart_xmit(void *ospriv, unifi_TrafficQueue queue)
func_enter();
unifi_trace(priv, UDBG2, "Waking queue %d\n", queue);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
for(i=0;i<CSR_WIFI_NUM_INTERFACES;i++)
{
if (netif_running(priv->netdev[i]))
@@ -2116,25 +1917,6 @@ unifi_restart_xmit(void *ospriv, unifi_TrafficQueue queue)
netif_wake_subqueue(priv->netdev[i], (u16)queue);
}
}
-#else
-#ifdef ALLOW_Q_PAUSE
- /* Need to supply queue number depending on Kernel support */
- /* Resume the traffic from all the interfaces */
- for(i=0;i<CSR_WIFI_NUM_INTERFACES;i++)
- {
- if (netif_running(priv->netdev[i])) {
- UF_NETIF_TX_WAKE_ALL_QUEUES(priv->netdev[i]);
- }
- }
-#else
- if (!(net_is_tx_q_paused(priv, queue))) {
- unifi_trace(priv, UDBG2, "Queue already running\n");
- func_exit();
- return;
- }
- net_tx_q_unpause(priv, queue);
-#endif
-#endif
#ifdef CSR_SUPPORT_SME
if(queue <=3) {
@@ -2349,13 +2131,7 @@ uf_resume_data_plane(unifi_priv_t *priv, int queue,
{
#ifdef CONFIG_NET_SCHED
if (netif_running(priv->netdev[interfaceTag])) {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,28)
netif_tx_schedule_all(priv->netdev[interfaceTag]);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
- netif_schedule_queue(netdev_get_tx_queue(priv->netdev[interfaceTag], 0));
-#else
- netif_schedule(priv->netdev[interfaceTag]);
-#endif /* LINUX_VERSION_CODE */
}
#endif
uf_process_rx_pending_queue(priv, queue, peer_address, 1,interfaceTag);
@@ -2998,19 +2774,13 @@ uf_set_multicast_list(struct net_device *dev)
#else
u8 *mc_list = interfacePriv->mc_list;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34)
struct netdev_hw_addr *mc_addr;
int mc_addr_count;
-#else
- struct dev_mc_list *p; /* Pointer to the addresses structure. */
- int i;
-#endif
if (priv->init_progress != UNIFI_INIT_COMPLETED) {
return;
}
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,34)
mc_addr_count = netdev_mc_count(dev);
unifi_trace(priv, UDBG3,
@@ -3029,25 +2799,6 @@ uf_set_multicast_list(struct net_device *dev)
mc_list += ETH_ALEN;
}
-#else
- unifi_trace(priv, UDBG3,
- "uf_set_multicast_list (count=%d)\n", dev->mc_count);
-
- /* Not enough space? */
- if (dev->mc_count > UNIFI_MAX_MULTICAST_ADDRESSES) {
- return;
- }
-
- /* Store the list to be processed by the work item. */
- interfacePriv->mc_list_count = dev->mc_count;
- p = dev->mc_list;
- for (i = 0; i < dev->mc_count; i++) {
- memcpy(mc_list, p->dmi_addr, ETH_ALEN);
- p = p->next;
- mc_list += ETH_ALEN;
- }
-#endif
-
/* Send a message to the workqueue */
queue_work(priv->unifi_workqueue, &priv->multicast_list_task);
#endif
@@ -3181,375 +2932,6 @@ void uf_net_get_name(struct net_device *dev, char *name, int len)
} /* uf_net_get_name */
-
-
-
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-#ifdef CONFIG_NET_SCHED
-
-/*
- * ---------------------------------------------------------------------------
- * uf_install_qdisc
- *
- * Creates a root qdisc, registers our qdisc handlers and
- * overrides the device's qdisc_sleeping to prevent the system
- * from creating a new one for our network device.
- *
- * Arguments:
- * dev Pointer to the network device.
- *
- * Returns:
- * 0 on success, Linux error code otherwise.
- *
- * Notes:
- * This function holds the qdisk lock so it needs to be called
- * after registering the network device in uf_register_netdev().
- * Also, the qdisc_create_dflt() API has changed in 2.6.20 to
- * include the parentid.
- * ---------------------------------------------------------------------------
- */
-int uf_install_qdisc(struct net_device *dev)
-{
- struct Qdisc *qdisc;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
- struct netdev_queue *queue0;
-#endif /* LINUX_VERSION_CODE */
-
-
- func_enter();
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,27)
- /*
- * check that there is no qdisc currently attached to device
- * this ensures that we will be the root qdisc. (I can't find a better
- * way to test this explicitly)
- */
- if (dev->qdisc_sleeping != &noop_qdisc) {
- func_exit_r(-EFAULT);
- return -EINVAL;
- }
-#endif /* LINUX_VERSION_CODE */
-
- qdisc = UF_QDISC_CREATE_DFLT(dev, &uf_qdisc_ops, TC_H_ROOT);
- if (!qdisc) {
- unifi_error(NULL, "%s: qdisc installation failed\n", dev->name);
- func_exit_r(-EFAULT);
- return -EFAULT;
- }
- unifi_trace(NULL, UDBG5, "%s: parent qdisc=0x%p\n",
- dev->name, qdisc);
-
- qdisc->handle = 0x80020000;
- qdisc->flags = 0x0;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
- queue0 = netdev_get_tx_queue(dev, 0);
- if (queue0 == NULL) {
- unifi_error(NULL, "%s: netdev_get_tx_queue returned no queue\n",
- dev->name);
- func_exit_r(-EFAULT);
- return -EFAULT;
- }
- queue0->qdisc = qdisc;
- queue0->qdisc_sleeping = qdisc;
-#else
- qdisc_lock_tree(dev);
- list_add_tail(&qdisc->list, &dev->qdisc_list);
- dev->qdisc_sleeping = qdisc;
- qdisc_unlock_tree(dev);
-#endif /* LINUX_VERSION_CODE */
-
- func_exit_r(0);
- return 0;
-
-} /* uf_install_qdisc() */
-
-static int uf_qdiscop_enqueue(struct sk_buff *skb, struct Qdisc* qd)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
- netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(qd->dev_queue->dev);
-#else
- netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(qd->dev);
-#endif /* LINUX_VERSION_CODE */
- unifi_priv_t *priv = interfacePriv->privPtr;
- struct uf_sched_data *q = qdisc_priv(qd);
- struct uf_tx_packet_data *pkt_data = (struct uf_tx_packet_data *) skb->cb;
- struct ethhdr ehdr;
- struct Qdisc *qdisc;
- int r, proto;
-
- func_enter();
-
- memcpy(&ehdr, skb->data, ETH_HLEN);
- proto = ntohs(ehdr.h_proto);
-
- /* 802.1x - apply controlled/uncontrolled port rules */
- if ((proto != ETH_P_PAE)
-#ifdef CSR_WIFI_SECURITY_WAPI_ENABLE
- && (proto != ETH_P_WAI)
-#endif
- ) {
- /* queues 0 - 3 */
- pkt_data->priority = get_packet_priority(priv, skb, &ehdr, interfacePriv);
- pkt_data->queue = unifi_frame_priority_to_queue(pkt_data->priority);
- } else {
- pkt_data->queue = UNIFI_TRAFFIC_Q_EAPOL;
- }
-
- qdisc = q->queues[pkt_data->queue];
- r = qdisc->enqueue(skb, qdisc);
- if (r == NET_XMIT_SUCCESS) {
- qd->q.qlen++;
- qd->bstats.bytes += skb->len;
- qd->bstats.packets++;
- func_exit_r(NET_XMIT_SUCCESS);
- return NET_XMIT_SUCCESS;
- }
-
- unifi_error(priv, "uf_qdiscop_enqueue: dropped\n");
- qd->qstats.drops++;
-
- func_exit_r(r);
- return r;
-
-} /* uf_qdiscop_enqueue() */
-
-
-static int uf_qdiscop_requeue(struct sk_buff *skb, struct Qdisc* qd)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
- netInterface_priv_t *interfacePriv = (netInterface_priv_t*)netdev_priv(qd->dev_queue->dev);
-#else
- netInterface_priv_t *interfacePriv = (netInterface_priv_t*)netdev_priv(qd->dev);
-#endif /* LINUX_VERSION_CODE */
- unifi_priv_t *priv = interfacePriv->privPtr;
- struct uf_sched_data *q = qdisc_priv(qd);
- struct uf_tx_packet_data *pkt_data = (struct uf_tx_packet_data *) skb->cb;
- struct Qdisc *qdisc;
- int r;
-
- func_enter();
-
- unifi_trace(priv, UDBG5, "uf_qdiscop_requeue: (q=%d), tag=%u\n",
- pkt_data->queue, pkt_data->host_tag);
-
- /* we recorded which queue to use earlier! */
- qdisc = q->queues[pkt_data->queue];
-
- if ((r = qdisc->ops->requeue(skb, qdisc)) == 0) {
- qd->q.qlen++;
- func_exit_r(0);
- return 0;
- }
-
- unifi_error(priv, "uf_qdiscop_requeue: dropped\n");
- qd->qstats.drops++;
-
- func_exit_r(r);
- return r;
-} /* uf_qdiscop_requeue() */
-
-static struct sk_buff *uf_qdiscop_dequeue(struct Qdisc* qd)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
- netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(qd->dev_queue->dev);
-#else
- netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(qd->dev);
-#endif /* LINUX_VERSION_CODE */
- unifi_priv_t *priv = interfacePriv->privPtr;
- struct uf_sched_data *q = qdisc_priv(qd);
- struct sk_buff *skb;
- struct Qdisc *qdisc;
- int queue, i;
- struct ethhdr ehdr;
- struct uf_tx_packet_data *pkt_data;
- CsrWifiRouterCtrlPortAction port_action;
-
- func_enter();
-
- /* check all the queues */
- for (i = UNIFI_TRAFFIC_Q_MAX - 1; i >= 0; i--) {
-
- if (i != UNIFI_TRAFFIC_Q_EAPOL) {
- queue = priv->prev_queue;
- if (++priv->prev_queue >= UNIFI_TRAFFIC_Q_EAPOL) {
- priv->prev_queue = 0;
- }
- } else {
- queue = i;
- }
-
-#ifndef ALLOW_Q_PAUSE
- /* If queue is paused, do not dequeue */
- if (net_is_tx_q_paused(priv, queue)) {
- unifi_trace(priv, UDBG5,
- "uf_qdiscop_dequeue: tx queue paused (q=%d)\n", queue);
- continue;
- }
-#endif
-
- qdisc = q->queues[queue];
- skb = qdisc->dequeue(qdisc);
- if (skb) {
- /* A packet has been dequeued, decrease the queued packets count */
- qd->q.qlen--;
-
- pkt_data = (struct uf_tx_packet_data *) skb->cb;
-
- /* Check the (un)controlled port status */
- memcpy(&ehdr, skb->data, ETH_HLEN);
-
- port_action = verify_port(priv
- , (((CSR_WIFI_ROUTER_CTRL_MODE_STA == interfacePriv->interfaceMode) ||(CSR_WIFI_ROUTER_CTRL_MODE_P2PCLI == interfacePriv->interfaceMode))? interfacePriv->bssid.a: ehdr.h_dest)
- , (UNIFI_TRAFFIC_Q_EAPOL == queue? UF_UNCONTROLLED_PORT_Q: UF_CONTROLLED_PORT_Q)
- , interfacePriv->InterfaceTag);
-
- /* Dequeue packet if port is open */
- if (port_action == CSR_WIFI_ROUTER_CTRL_PORT_ACTION_8021X_PORT_OPEN) {
- unifi_trace(priv, UDBG5,
- "uf_qdiscop_dequeue: new (q=%d), tag=%u\n",
- queue, pkt_data->host_tag);
-
- func_exit();
- return skb;
- }
-
- /* Discard or block the packet if necessary */
- if (port_action == CSR_WIFI_ROUTER_CTRL_PORT_ACTION_8021X_PORT_CLOSED_DISCARD) {
- unifi_trace(priv, UDBG5,
- "uf_qdiscop_dequeue: drop (q=%d), tag=%u\n",
- queue, pkt_data->host_tag);
- kfree_skb(skb);
- break;
- }
-
- /* We can not send the packet now, put it back to the queue */
- if (qdisc->ops->requeue(skb, qdisc) != 0) {
- unifi_error(priv,
- "uf_qdiscop_dequeue: requeue (q=%d) failed, tag=%u, drop it\n",
- queue, pkt_data->host_tag);
-
- /* Requeue failed, drop the packet */
- kfree_skb(skb);
- break;
- }
- /* We requeued the packet, increase the queued packets count */
- qd->q.qlen++;
-
- unifi_trace(priv, UDBG5,
- "uf_qdiscop_dequeue: skip (q=%d), tag=%u\n",
- queue, pkt_data->host_tag);
- }
- }
-
- func_exit();
- return NULL;
-} /* uf_qdiscop_dequeue() */
-
-
-static void uf_qdiscop_reset(struct Qdisc* qd)
-{
- struct uf_sched_data *q = qdisc_priv(qd);
- int queue;
- func_enter();
-
- for (queue = 0; queue < UNIFI_TRAFFIC_Q_MAX; queue++) {
- qdisc_reset(q->queues[queue]);
- }
- qd->q.qlen = 0;
-
- func_exit();
-} /* uf_qdiscop_reset() */
-
-
-static void uf_qdiscop_destroy(struct Qdisc* qd)
-{
- struct uf_sched_data *q = qdisc_priv(qd);
- int queue;
-
- func_enter();
-
- for (queue=0; queue < UNIFI_TRAFFIC_Q_MAX; queue++) {
- qdisc_destroy(q->queues[queue]);
- q->queues[queue] = &noop_qdisc;
- }
-
- func_exit();
-} /* uf_qdiscop_destroy() */
-
-
-/* called whenever parameters are updated on existing qdisc */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
-static int uf_qdiscop_tune(struct Qdisc *qd, struct nlattr *opt)
-#else
-static int uf_qdiscop_tune(struct Qdisc *qd, struct rtattr *opt)
-#endif
-{
- func_enter();
- func_exit();
- return 0;
-} /* uf_qdiscop_tune() */
-
-
-/* called during initial creation of qdisc on device */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,25)
-static int uf_qdiscop_init(struct Qdisc *qd, struct nlattr *opt)
-#else
-static int uf_qdiscop_init(struct Qdisc *qd, struct rtattr *opt)
-#endif
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
- struct net_device *dev = qd->dev_queue->dev;
-#else
- struct net_device *dev = qd->dev;
-#endif /* LINUX_VERSION_CODE */
- netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev);
- unifi_priv_t *priv = interfacePriv->privPtr;
- struct uf_sched_data *q = qdisc_priv(qd);
- int err = 0, i;
-
- func_enter();
-
- /* make sure we do not mess with the ingress qdisc */
- if (qd->flags & TCQ_F_INGRESS) {
- func_exit();
- return -EINVAL;
- }
-
- /* if options were passed in, set them */
- if (opt) {
- err = uf_qdiscop_tune(qd, opt);
- }
-
- /* create child queues */
- for (i = 0; i < UNIFI_TRAFFIC_Q_MAX; i++) {
- q->queues[i] = UF_QDISC_CREATE_DFLT(dev, &pfifo_qdisc_ops,
- qd->handle);
- if (!q->queues[i]) {
- q->queues[i] = &noop_qdisc;
- unifi_error(priv, "%s child qdisc %i creation failed\n");
- }
-
- unifi_trace(priv, UDBG5, "%s: child qdisc=0x%p\n",
- dev->name, q->queues[i]);
- }
-
- func_exit_r(err);
- return err;
-} /* uf_qdiscop_init() */
-
-
-static int uf_qdiscop_dump(struct Qdisc *qd, struct sk_buff *skb)
-{
- func_enter();
- func_exit_r(skb->len);
- return skb->len;
-} /* uf_qdiscop_dump() */
-
-#endif /* CONFIG_NET_SCHED */
-#endif /* LINUX_VERSION_CODE */
-
#ifdef CSR_SUPPORT_WEXT
/*
@@ -3595,7 +2977,7 @@ uf_netdev_event(struct notifier_block *notif, unsigned long event, void* ptr) {
interfacePriv->wait_netdev_change ? "" : "not");
if (interfacePriv->wait_netdev_change) {
- UF_NETIF_TX_WAKE_ALL_QUEUES(priv->netdev[interfacePriv->InterfaceTag]);
+ netif_tx_wake_all_queues(priv->netdev[interfacePriv->InterfaceTag]);
interfacePriv->connected = UnifiConnected;
interfacePriv->wait_netdev_change = FALSE;
/* Note: passing the broadcast address here will allow anyone to attempt to join our adhoc network */
diff --git a/drivers/staging/csr/sdio_events.c b/drivers/staging/csr/sdio_events.c
index 6892c2e281bc..2a80b9eb0200 100644
--- a/drivers/staging/csr/sdio_events.c
+++ b/drivers/staging/csr/sdio_events.c
@@ -66,7 +66,7 @@ void unifi_suspend(void *ospriv)
unifi_trace(priv, UDBG1, "unifi_suspend: netif_carrier_off");
netif_carrier_off(priv->netdev[interfaceTag]);
}
- UF_NETIF_TX_STOP_ALL_QUEUES(priv->netdev[interfaceTag]);
+ netif_tx_stop_all_queues(priv->netdev[interfaceTag]);
}
}
@@ -119,7 +119,7 @@ void unifi_resume(void *ospriv)
if (interfacePriv->netdev_registered == 1)
{
netif_carrier_on(priv->netdev[interfaceTag]);
- UF_NETIF_TX_START_ALL_QUEUES(priv->netdev[interfaceTag]);
+ netif_tx_start_all_queues(priv->netdev[interfaceTag]);
}
}
diff --git a/drivers/staging/csr/sdio_mmc.c b/drivers/staging/csr/sdio_mmc.c
index d3fd57cdde0b..af3e40bb5010 100644
--- a/drivers/staging/csr/sdio_mmc.c
+++ b/drivers/staging/csr/sdio_mmc.c
@@ -14,7 +14,7 @@
#include <linux/kernel.h>
#include <linux/mutex.h>
#include <linux/gfp.h>
-
+#include <linux/version.h>
#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
@@ -31,7 +31,6 @@ struct wake_lock unifi_sdio_wake_lock; /* wakelock to prevent suspend while resu
static CsrSdioFunctionDriver *sdio_func_drv;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
#ifdef CONFIG_PM
static int uf_sdio_mmc_power_event(struct notifier_block *this, unsigned long event, void *ptr);
#endif
@@ -45,7 +44,6 @@ static int uf_sdio_mmc_power_event(struct notifier_block *this, unsigned long ev
* returning immediately (at least on x86).
*/
static int card_is_powered = 1;
-#endif /* 2.6.32 */
/* MMC uses ENOMEDIUM to indicate card gone away */
@@ -637,7 +635,6 @@ CsrSdioFunctionIdle(CsrSdioFunction *function)
CsrResult
CsrSdioPowerOn(CsrSdioFunction *function)
{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
struct sdio_func *func = (struct sdio_func *)function->priv;
struct mmc_host *host = func->card->host;
@@ -649,7 +646,6 @@ CsrSdioPowerOn(CsrSdioFunction *function)
printk(KERN_INFO "SDIO: Skip power on; card is already powered.\n");
}
_sdio_release_host(func);
-#endif /* 2.6.32 */
return CSR_RESULT_SUCCESS;
} /* CsrSdioPowerOn() */
@@ -667,7 +663,6 @@ CsrSdioPowerOn(CsrSdioFunction *function)
void
CsrSdioPowerOff(CsrSdioFunction *function)
{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
struct sdio_func *func = (struct sdio_func *)function->priv;
struct mmc_host *host = func->card->host;
@@ -679,7 +674,6 @@ CsrSdioPowerOff(CsrSdioFunction *function)
printk(KERN_INFO "SDIO: Skip power off; card is already powered off.\n");
}
_sdio_release_host(func);
-#endif /* 2.6.32 */
} /* CsrSdioPowerOff() */
@@ -845,19 +839,18 @@ uf_glue_sdio_int_handler(struct sdio_func *func)
* Status of the removal.
* ---------------------------------------------------------------------------
*/
-int
-csr_sdio_linux_remove_irq(CsrSdioFunction *function)
+int csr_sdio_linux_remove_irq(CsrSdioFunction *function)
{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int r;
+ struct sdio_func *func = (struct sdio_func *)function->priv;
+ int r;
- unifi_trace(NULL, UDBG1, "csr_sdio_linux_remove_irq\n");
+ unifi_trace(NULL, UDBG1, "csr_sdio_linux_remove_irq\n");
- sdio_claim_host(func);
- r = sdio_release_irq(func);
- sdio_release_host(func);
+ sdio_claim_host(func);
+ r = sdio_release_irq(func);
+ sdio_release_host(func);
- return r;
+ return r;
} /* csr_sdio_linux_remove_irq() */
@@ -876,28 +869,25 @@ csr_sdio_linux_remove_irq(CsrSdioFunction *function)
* Status of the removal.
* ---------------------------------------------------------------------------
*/
-int
-csr_sdio_linux_install_irq(CsrSdioFunction *function)
+int csr_sdio_linux_install_irq(CsrSdioFunction *function)
{
- struct sdio_func *func = (struct sdio_func *)function->priv;
- int r;
+ struct sdio_func *func = (struct sdio_func *)function->priv;
+ int r;
- unifi_trace(NULL, UDBG1, "csr_sdio_linux_install_irq\n");
+ unifi_trace(NULL, UDBG1, "csr_sdio_linux_install_irq\n");
- /* Register our interrupt handle */
- sdio_claim_host(func);
- r = sdio_claim_irq(func, uf_glue_sdio_int_handler);
- sdio_release_host(func);
+ /* Register our interrupt handle */
+ sdio_claim_host(func);
+ r = sdio_claim_irq(func, uf_glue_sdio_int_handler);
+ sdio_release_host(func);
- /* If the interrupt was installed earlier, is fine */
- if (r == -EBUSY) {
- r = 0;
- }
+ /* If the interrupt was installed earlier, is fine */
+ if (r == -EBUSY)
+ r = 0;
- return r;
+ return r;
} /* csr_sdio_linux_install_irq() */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
#ifdef CONFIG_PM
/*
@@ -1023,7 +1013,6 @@ uf_sdio_mmc_power_event(struct notifier_block *this, unsigned long event, void *
}
#endif /* CONFIG_PM */
-#endif /* 2.6.32 */
/*
* ---------------------------------------------------------------------------
@@ -1050,10 +1039,8 @@ uf_glue_sdio_probe(struct sdio_func *func,
/* First of all claim the SDIO driver */
sdio_claim_host(func);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
/* Assume that the card is already powered */
card_is_powered = 1;
-#endif
/* Assumes one card per host, which is true for SDIO */
instance = func->card->host->index;
@@ -1093,14 +1080,12 @@ uf_glue_sdio_probe(struct sdio_func *func,
/* Pass context to the SDIO driver */
sdio_set_drvdata(func, sdio_ctx);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
#ifdef CONFIG_PM
/* Register to get PM events */
if (uf_sdio_mmc_register_pm_notifier(sdio_ctx) == NULL) {
unifi_error(NULL, "%s: Failed to register for PM events\n", __FUNCTION__);
}
#endif
-#endif
/* Register this device with the SDIO function driver */
/* Call the main UniFi driver inserted handler */
@@ -1156,12 +1141,10 @@ uf_glue_sdio_remove(struct sdio_func *func)
sdio_func_drv->removed(sdio_ctx);
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
#ifdef CONFIG_PM
/* Unregister for PM events */
uf_sdio_mmc_unregister_pm_notifier(sdio_ctx);
#endif
-#endif
kfree(sdio_ctx);
@@ -1182,7 +1165,6 @@ static const struct sdio_device_id unifi_ids[] = {
MODULE_DEVICE_TABLE(sdio, unifi_ids);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
#ifdef CONFIG_PM
/*
@@ -1252,16 +1234,13 @@ static struct dev_pm_ops unifi_pm_ops = {
#define UNIFI_PM_OPS NULL
#endif /* CONFIG_PM */
-#endif /* 2.6.32 */
static struct sdio_driver unifi_driver = {
.probe = uf_glue_sdio_probe,
.remove = uf_glue_sdio_remove,
.name = "unifi",
.id_table = unifi_ids,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
.drv.pm = UNIFI_PM_OPS,
-#endif /* 2.6.32 */
};
@@ -1305,12 +1284,10 @@ CsrSdioFunctionDriverRegister(CsrSdioFunctionDriver *sdio_drv)
*/
sdio_func_drv = sdio_drv;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32)
#ifdef CONFIG_PM
/* Initialise PM notifier list */
INIT_LIST_HEAD(&uf_sdio_mmc_pm_notifiers.list);
#endif
-#endif
/* Register ourself with mmc_core */
r = sdio_register_driver(&unifi_driver);
diff --git a/drivers/staging/csr/sme_blocking.c b/drivers/staging/csr/sme_blocking.c
index acf0f0fe3b37..543e8f2c407a 100644
--- a/drivers/staging/csr/sme_blocking.c
+++ b/drivers/staging/csr/sme_blocking.c
@@ -18,10 +18,10 @@
/*
- * This file also contains the implementation of the asyncronous
+ * This file also contains the implementation of the asynchronous
* requests to the SME.
*
- * Before calling an asyncronous SME function, we call sme_init_request()
+ * Before calling an asynchronous SME function, we call sme_init_request()
* which gets hold of the SME semaphore and updates the request status.
* The semaphore makes sure that there is only one pending request to
* the SME at a time.
diff --git a/drivers/staging/csr/sme_native.c b/drivers/staging/csr/sme_native.c
index 229268fd746c..d7a5125d9a8e 100644
--- a/drivers/staging/csr/sme_native.c
+++ b/drivers/staging/csr/sme_native.c
@@ -12,7 +12,7 @@
*/
#include <linux/netdevice.h>
-
+#include <linux/version.h>
#include "unifi_priv.h"
#include "csr_wifi_hip_unifi.h"
#include "csr_wifi_hip_conversions.h"
@@ -24,11 +24,7 @@ uf_sme_init(unifi_priv_t *priv)
{
func_enter();
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
sema_init(&priv->mlme_blocking_mutex, 1);
-#else
- init_MUTEX(&priv->mlme_blocking_mutex);
-#endif
#ifdef CSR_SUPPORT_WEXT
{
diff --git a/drivers/staging/csr/sme_sys.c b/drivers/staging/csr/sme_sys.c
index 99de27e678d2..5b26c41c01f6 100644
--- a/drivers/staging/csr/sme_sys.c
+++ b/drivers/staging/csr/sme_sys.c
@@ -14,6 +14,7 @@
* ---------------------------------------------------------------------------
*/
+#include <linux/version.h>
#include "csr_wifi_hip_unifiversion.h"
#include "unifi_priv.h"
#include "csr_wifi_hip_conversions.h"
@@ -21,7 +22,6 @@
#include "csr_wifi_sme_sef.h"
#endif
-
/*
* This file implements the SME SYS API and contains the following functions:
* CsrWifiRouterCtrlMediaStatusReqHandler()
@@ -192,7 +192,7 @@ void CsrWifiRouterCtrlMediaStatusReqHandler(void* drvpriv, CsrWifiFsmEvent* msg)
#endif
unifi_trace(priv, UDBG1,
"CsrWifiRouterCtrlMediaStatusReqHandler: AP/P2PGO setting netif_carrier_on\n");
- UF_NETIF_TX_WAKE_ALL_QUEUES(priv->netdev[req->interfaceTag]);
+ netif_tx_wake_all_queues(priv->netdev[req->interfaceTag]);
break;
default:
@@ -226,7 +226,7 @@ void CsrWifiRouterCtrlMediaStatusReqHandler(void* drvpriv, CsrWifiFsmEvent* msg)
unifi_trace(priv, UDBG1,
"CsrWifiRouterMediaStatusReqHandler: UnifiConnected && netif_carrier_on\n");
netif_carrier_on(priv->netdev[req->interfaceTag]);
- UF_NETIF_TX_WAKE_ALL_QUEUES(priv->netdev[req->interfaceTag]);
+ netif_tx_wake_all_queues(priv->netdev[req->interfaceTag]);
uf_process_rx_pending_queue(priv, UF_UNCONTROLLED_PORT_Q, broadcast_address, 1, interfacePriv->InterfaceTag);
uf_process_rx_pending_queue(priv, UF_CONTROLLED_PORT_Q, broadcast_address, 1, interfacePriv->InterfaceTag);
}
@@ -869,7 +869,6 @@ wifi_off(unifi_priv_t *priv)
unifi_trace(priv, UDBG1, "wifi_off\n");
/* Destroy the Traffic Analysis Module */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
cancel_work_sync(&priv->ta_ind_work.task);
cancel_work_sync(&priv->ta_sample_ind_work.task);
#ifdef CSR_SUPPORT_WEXT
@@ -884,7 +883,6 @@ wifi_off(unifi_priv_t *priv)
cancel_work_sync(&netpriv->send_m4_ready_task);
}
}
-#endif
flush_workqueue(priv->unifi_workqueue);
/* fw_init parameter can prevent power off UniFi, for debugging */
@@ -955,7 +953,7 @@ void CsrWifiRouterCtrlWifiOffReqHandler(void* drvpriv, CsrWifiFsmEvent* msg)
netInterface_priv_t *interfacePriv = priv->interfacePriv[i];
if (interfacePriv->netdev_registered == 1) {
netif_carrier_off(priv->netdev[i]);
- UF_NETIF_TX_STOP_ALL_QUEUES(priv->netdev[i]);
+ netif_tx_stop_all_queues(priv->netdev[i]);
interfacePriv->connected = UnifiConnectedUnknown;
}
interfacePriv->interfaceMode = 0;
@@ -2123,7 +2121,7 @@ static int peer_add_new_record(unifi_priv_t *priv,CsrWifiRouterCtrlPeerAddReq *r
/* Allocate for the new station record , to avoid race condition would happen between ADD_PEER &
* DEL_PEER the allocation made atomic memory rather than kernel memory
*/
- newRecord = (CsrWifiRouterCtrlStaInfo_t *) kmalloc(sizeof(CsrWifiRouterCtrlStaInfo_t), GFP_ATOMIC);
+ newRecord = kmalloc(sizeof(CsrWifiRouterCtrlStaInfo_t), GFP_ATOMIC);
if (!newRecord) {
unifi_error(priv, "failed to allocate the %d bytes of mem for station record\n",
sizeof(CsrWifiRouterCtrlStaInfo_t));
@@ -2815,12 +2813,11 @@ u8 blockack_session_start(unifi_priv_t *priv,
}
/* create and populate the new BA session structure */
- ba_session_tx = kmalloc(sizeof(ba_session_tx_struct), GFP_KERNEL);
+ ba_session_tx = kzalloc(sizeof(ba_session_tx_struct), GFP_KERNEL);
if (!ba_session_tx) {
unifi_error(priv, "%s: kmalloc failed for ba_session_tx\n", __FUNCTION__);
return FALSE;
}
- memset(ba_session_tx, 0, sizeof(ba_session_tx_struct));
ba_session_tx->interfacePriv = interfacePriv;
ba_session_tx->tID = tID;
@@ -2905,26 +2902,23 @@ u8 blockack_session_start(unifi_priv_t *priv,
return FALSE;
}
- ba_session_rx = kmalloc(sizeof(ba_session_rx_struct), GFP_KERNEL);
+ ba_session_rx = kzalloc(sizeof(ba_session_rx_struct), GFP_KERNEL);
if (!ba_session_rx) {
unifi_error(priv, "%s: kmalloc failed for ba_session_rx\n", __FUNCTION__);
return FALSE;
}
- memset(ba_session_rx, 0, sizeof(ba_session_rx_struct));
ba_session_rx->wind_size = wind_size;
ba_session_rx->start_sn = ba_session_rx->expected_sn = start_sn;
ba_session_rx->trigger_ba_after_ssn = FALSE;
- ba_session_rx->buffer = kmalloc(ba_session_rx->wind_size*sizeof(frame_desc_struct), GFP_KERNEL);
+ ba_session_rx->buffer = kzalloc(ba_session_rx->wind_size*sizeof(frame_desc_struct), GFP_KERNEL);
if (!ba_session_rx->buffer) {
kfree(ba_session_rx);
unifi_error(priv, "%s: kmalloc failed for buffer\n", __FUNCTION__);
return FALSE;
}
- memset(ba_session_rx->buffer, 0, ba_session_rx->wind_size*sizeof(frame_desc_struct));
-
INIT_WORK(&ba_session_rx->send_ba_err_task, uf_send_ba_err_wq);
if (timeout) {
ba_session_rx->timeout = timeout;
diff --git a/drivers/staging/csr/sme_wext.c b/drivers/staging/csr/sme_wext.c
index 7e85907e29a3..b58c0c6b171c 100644
--- a/drivers/staging/csr/sme_wext.c
+++ b/drivers/staging/csr/sme_wext.c
@@ -1191,8 +1191,6 @@ unifi_siwap(struct net_device *dev, struct iw_request_info *info,
netInterface_priv_t *interfacePriv = (netInterface_priv_t *)netdev_priv(dev);
unifi_priv_t *priv = interfacePriv->privPtr;
int err = 0;
- const unsigned char zero_bssid[ETH_ALEN] = {0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00};
func_enter();
@@ -1213,7 +1211,7 @@ unifi_siwap(struct net_device *dev, struct iw_request_info *info,
unifi_trace(priv, UDBG1, "unifi_siwap: asked for %pM\n",
wrqu->ap_addr.sa_data);
- if (!memcmp(wrqu->ap_addr.sa_data, zero_bssid, ETH_ALEN)) {
+ if (is_zero_ether_addr(wrqu->ap_addr.sa_data)) {
priv->ignore_bssid_join = FALSE;
err = sme_mgt_disconnect(priv);
if (err) {
@@ -3043,8 +3041,8 @@ _unifi_siwencodeext(struct net_device *dev, struct iw_request_info *info,
memcpy(sme_key.address.a, ext->addr.sa_data, ETH_ALEN);
if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) {
- unifi_trace(priv, UDBG5, "RSC first 6 bytes = %02X:%02X:%02X:%02X:%02X:%02X\n",
- ext->rx_seq[0], ext->rx_seq[1], ext->rx_seq[2], ext->rx_seq[3], ext->rx_seq[4], ext->rx_seq[5]);
+ unifi_trace(priv, UDBG5, "RSC first 6 bytes = %*phC\n",
+ 6, ext->rx_seq);
/* memcpy((u8*)(&sme_key.keyRsc), ext->rx_seq, 8); */
sme_key.keyRsc[0] = ext->rx_seq[1] << 8 | ext->rx_seq[0];
diff --git a/drivers/staging/csr/ul_int.c b/drivers/staging/csr/ul_int.c
index 46d3507fd8f1..4013d021ebbf 100644
--- a/drivers/staging/csr/ul_int.c
+++ b/drivers/staging/csr/ul_int.c
@@ -12,6 +12,7 @@
*
* ***************************************************************************
*/
+#include <linux/version.h>
#include "csr_wifi_hip_unifi.h"
#include "csr_wifi_hip_conversions.h"
#include "unifi_priv.h"
@@ -44,11 +45,7 @@ ul_init_clients(unifi_priv_t *priv)
int id;
ul_client_t *ul_clients;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
sema_init(&priv->udi_logging_mutex, 1);
-#else
- init_MUTEX(&priv->udi_logging_mutex);
-#endif
priv->logging_client = NULL;
ul_clients = priv->ul_clients;
diff --git a/drivers/staging/csr/unifi_pdu_processing.c b/drivers/staging/csr/unifi_pdu_processing.c
index 7c7e8d49ae42..ae7c8fc94092 100644
--- a/drivers/staging/csr/unifi_pdu_processing.c
+++ b/drivers/staging/csr/unifi_pdu_processing.c
@@ -14,7 +14,7 @@
* ---------------------------------------------------------------------------
*/
-
+#include <linux/version.h>
#include <linux/types.h>
#include <linux/etherdevice.h>
#include <linux/vmalloc.h>
@@ -23,9 +23,6 @@
#include "csr_wifi_hip_conversions.h"
#include "csr_time.h"
#include "unifi_priv.h"
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,13)
-#include <net/iw_handler.h>
-#endif
#include <net/pkt_sched.h>
#ifdef CSR_SUPPORT_SME
diff --git a/drivers/staging/csr/unifi_priv.h b/drivers/staging/csr/unifi_priv.h
index 6d6b46191a1a..aec8e28fb60d 100644
--- a/drivers/staging/csr/unifi_priv.h
+++ b/drivers/staging/csr/unifi_priv.h
@@ -29,9 +29,7 @@
#include <linux/wireless.h>
#include <linux/cdev.h>
#include <linux/kthread.h>
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,19)
#include <linux/freezer.h>
-#endif
#ifdef CSR_WIFI_SUPPORT_MMC_DRIVER
#include <linux/mmc/core.h>
@@ -73,33 +71,6 @@ extern struct wake_lock unifi_sdio_wake_lock;
#include "unifi_clients.h"
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
-#include <linux/workqueue.h>
-
-#undef INIT_WORK
-#define INIT_WORK(_work, _func) \
- do { \
- INIT_LIST_HEAD(&(_work)->entry); \
- (_work)->pending = 0; \
- PREPARE_WORK((_work), (_func), (_work)); \
- init_timer(&(_work)->timer); \
- } while(0)
-
-#endif /* Linux kernel < 2.6.20 */
-
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,27)
-#define UF_NETIF_TX_WAKE_ALL_QUEUES(_netdev) netif_tx_wake_all_queues(_netdev)
-#define UF_NETIF_TX_START_ALL_QUEUES(_netdev) netif_tx_start_all_queues(_netdev)
-#define UF_NETIF_TX_STOP_ALL_QUEUES(_netdev) netif_tx_stop_all_queues(_netdev)
-#else
-#define UF_NETIF_TX_WAKE_ALL_QUEUES(_netdev) netif_wake_queue(_netdev)
-#define UF_NETIF_TX_START_ALL_QUEUES(_netdev) netif_start_queue(_netdev)
-#define UF_NETIF_TX_STOP_ALL_QUEUES(_netdev) netif_stop_queue(_netdev)
-#endif /* Linux kernel >= 2.6.27 */
-
-
#ifdef CSR_NATIVE_LINUX
#include "sme_native/unifi_native.h"
#else
@@ -634,12 +605,10 @@ struct unifi_priv {
spinlock_t wapi_lock;
#endif
-#ifndef ALLOW_Q_PAUSE
/* Array to indicate if a particular Tx queue is paused, this may not be
* required in a multiqueue implementation since we can directly stop kernel
* queues */
u8 tx_q_paused_flag[UNIFI_TRAFFIC_Q_MAX];
-#endif
#ifdef CSR_WIFI_RX_PATH_SPLIT
struct workqueue_struct *rx_workqueue;
@@ -798,12 +767,6 @@ typedef struct netInterface_priv
u8 bcTimSetReqQueued;
} netInterface_priv_t;
-#ifndef ALLOW_Q_PAUSE
-#define net_is_tx_q_paused(priv, q) (priv->tx_q_paused_flag[q])
-#define net_tx_q_unpause(priv, q) (priv->tx_q_paused_flag[q] = 0)
-#define net_tx_q_pause(priv, q) (priv->tx_q_paused_flag[q] = 1)
-#endif
-
#ifdef CSR_SUPPORT_SME
#define routerStartBuffering(priv,queue) priv->routerBufferEnable[(queue)] = TRUE;
#define routerStopBuffering(priv,queue) priv->routerBufferEnable[(queue)] = FALSE;
@@ -1088,9 +1051,6 @@ CsrWifiRouterCtrlStaInfo_t * CsrWifiRouterCtrlGetStationRecordFromHandle(unifi_p
void uf_update_sta_activity(unifi_priv_t *priv, u16 interfaceTag, const u8 *peerMacAddress);
void uf_process_ma_pkt_cfm_for_ap(unifi_priv_t *priv,u16 interfaceTag, const CSR_MA_PACKET_CONFIRM *pkt_cfm);
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
-int uf_install_qdisc(struct net_device *dev);
-#endif
void uf_resume_data_plane(unifi_priv_t *priv, int queue,
CsrWifiMacAddress peer_address,
diff --git a/drivers/staging/csr/unifi_wext.h b/drivers/staging/csr/unifi_wext.h
index 6d7a99595083..6834c43abfbb 100644
--- a/drivers/staging/csr/unifi_wext.h
+++ b/drivers/staging/csr/unifi_wext.h
@@ -16,6 +16,7 @@
#define __LINUX_UNIFI_WEXT_H__ 1
#include <linux/kernel.h>
+#include <linux/version.h>
#include <net/iw_handler.h>
#include "csr_wifi_sme_prim.h"
@@ -70,15 +71,9 @@ uf_iwe_stream_add_point(struct iw_request_info *info, char *start, char *stop,
{
char *new_start;
- new_start = iwe_stream_add_point(
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) || defined (IW_REQUEST_FLAG_COMPAT)
- info,
-#endif
- start, stop, piwe, extra);
+ new_start = iwe_stream_add_point(info, start, stop, piwe, extra);
if (unlikely(new_start == start))
- {
return -E2BIG;
- }
return (new_start - start);
}
@@ -90,14 +85,9 @@ uf_iwe_stream_add_event(struct iw_request_info *info, char *start, char *stop,
{
char *new_start;
- new_start = iwe_stream_add_event(
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) || defined(IW_REQUEST_FLAG_COMPAT)
- info,
-#endif
- start, stop, piwe, len);
- if (unlikely(new_start == start)) {
+ new_start = iwe_stream_add_event(info, start, stop, piwe, len);
+ if (unlikely(new_start == start))
return -E2BIG;
- }
return (new_start - start);
}
@@ -108,14 +98,9 @@ uf_iwe_stream_add_value(struct iw_request_info *info, char *stream, char *start,
{
char *new_start;
- new_start = iwe_stream_add_value(
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) || defined(IW_REQUEST_FLAG_COMPAT)
- info,
-#endif
- stream, start, stop, piwe, len);
- if (unlikely(new_start == start)) {
+ new_start = iwe_stream_add_value(info, stream, start, stop, piwe, len);
+ if (unlikely(new_start == start))
return -E2BIG;
- }
return (new_start - start);
}
diff --git a/drivers/staging/csr/wext_events.c b/drivers/staging/csr/wext_events.c
index d356887ac4c6..9860ea30da25 100644
--- a/drivers/staging/csr/wext_events.c
+++ b/drivers/staging/csr/wext_events.c
@@ -194,11 +194,9 @@ _send_michaelmicfailure_event(struct net_device *dev,
union iwreq_data wrqu;
char buf[128];
- sprintf(buf,
- "MLME-MICHAELMICFAILURE.indication(keyid=%d %scast addr=%02x:%02x:%02x:%02x:%02x:%02x)",
- key_idx, (key_type == CSR_GROUP) ? "broad" : "uni",
- macaddr[0], macaddr[1], macaddr[2],
- macaddr[3], macaddr[4], macaddr[5]);
+ sprintf(buf,
+ "MLME-MICHAELMICFAILURE.indication(keyid=%d %scast addr=%pM)",
+ key_idx, (key_type == CSR_GROUP) ? "broad" : "uni", macaddr);
memset(&wrqu, 0, sizeof(wrqu));
wrqu.data.length = strlen(buf);
wireless_send_event(dev, IWEVCUSTOM, &wrqu, buf);
diff --git a/drivers/staging/cxt1e1/linux.c b/drivers/staging/cxt1e1/linux.c
index 911c0e4375fd..0ff2865edec8 100644
--- a/drivers/staging/cxt1e1/linux.c
+++ b/drivers/staging/cxt1e1/linux.c
@@ -405,7 +405,7 @@ c4_linux_xmit (struct sk_buff * skb, struct net_device * ndev)
priv = hdlc->priv;
rval = musycc_start_xmit (priv->ci, priv->channum, skb);
- return -rval;
+ return rval;
}
static const struct net_device_ops chan_ops = {
@@ -1169,11 +1169,11 @@ cleanup_hdlc (void)
STATIC void __exit
c4_mod_remove (void)
{
- cleanup_hdlc (); /* delete any missed channels */
- cleanup_devs ();
- c4_cleanup ();
- cleanup_ioremap ();
- pr_info("SBE - driver removed.\n");
+ cleanup_hdlc(); /* delete any missed channels */
+ cleanup_devs();
+ c4_cleanup();
+ cleanup_ioremap();
+ pr_info("SBE - driver removed.\n");
}
module_init (c4_mod_init);
diff --git a/drivers/staging/cxt1e1/musycc.c b/drivers/staging/cxt1e1/musycc.c
index 90c0f1e30f65..ba721c604061 100644
--- a/drivers/staging/cxt1e1/musycc.c
+++ b/drivers/staging/cxt1e1/musycc.c
@@ -1761,15 +1761,15 @@ musycc_start_xmit (ci_t * ci, int channum, void *mem_token)
u_int32_t len;
if (!(ch = sd_find_chan (ci, channum)))
- return ENOENT;
+ return -ENOENT;
if (ci->state != C_RUNNING) /* full interrupt processing available */
- return EINVAL;
+ return -EINVAL;
if (ch->state != UP)
- return EINVAL;
+ return -EINVAL;
if (!(ch->status & TX_ENABLED))
- return EROFS; /* how else to flag unwritable state ? */
+ return -EROFS; /* how else to flag unwritable state ? */
#ifdef RLD_TRANS_DEBUGx
if (1 || cxt1e1_log_level >= LOG_MONITOR2)
@@ -1836,7 +1836,7 @@ musycc_start_xmit (ci_t * ci, int channum, void *mem_token)
#if 0
spin_unlock_irqrestore (&ch->ch_txlock, flags);
#endif
- return EBUSY; /* tell user to try again later */
+ return -EBUSY; /* tell user to try again later */
}
/**************************************************/
/** Put the user data into MUSYCC data buffer(s) **/
diff --git a/drivers/staging/dgrp/Kconfig b/drivers/staging/dgrp/Kconfig
new file mode 100644
index 000000000000..39f4bb65ec83
--- /dev/null
+++ b/drivers/staging/dgrp/Kconfig
@@ -0,0 +1,9 @@
+config DGRP
+ tristate "Digi Realport driver"
+ default n
+ depends on SYSFS
+ ---help---
+ Support for Digi Realport devices. These devices allow you to
+ access remote serial ports as if they are local tty devices. This
+ will build the kernel driver, you will still need the userspace
+ component to make your Realport device work.
diff --git a/drivers/staging/dgrp/Makefile b/drivers/staging/dgrp/Makefile
new file mode 100644
index 000000000000..d9c3b88d7162
--- /dev/null
+++ b/drivers/staging/dgrp/Makefile
@@ -0,0 +1,12 @@
+obj-$(CONFIG_DGRP) += dgrp.o
+
+dgrp-y := \
+ dgrp_common.o \
+ dgrp_dpa_ops.o \
+ dgrp_driver.o \
+ dgrp_mon_ops.o \
+ dgrp_net_ops.o \
+ dgrp_ports_ops.o \
+ dgrp_specproc.o \
+ dgrp_tty.o \
+ dgrp_sysfs.o
diff --git a/drivers/staging/dgrp/README b/drivers/staging/dgrp/README
new file mode 100644
index 000000000000..1d8aaee270e8
--- /dev/null
+++ b/drivers/staging/dgrp/README
@@ -0,0 +1,2 @@
+The user space code to work with this driver is located at
+https://github.com/wfp5p/dgrp-utils
diff --git a/drivers/staging/dgrp/TODO b/drivers/staging/dgrp/TODO
new file mode 100644
index 000000000000..3ef2611bc6d7
--- /dev/null
+++ b/drivers/staging/dgrp/TODO
@@ -0,0 +1,13 @@
+- Use configfs for config stuff. This will require changes to the
+ user space code.
+
+- dgrp_send() and dgrp_receive() could use some refactoring
+
+- Don't automatically create CHAN_MAX (64) channel array entries for
+ every device as many devices are going to have much less than 64
+ channels.
+
+- The locking needs to be checked. It seems haphazardly done in most
+ places.
+
+- Check Kconfig dependencies
diff --git a/drivers/staging/dgrp/dgrp_common.c b/drivers/staging/dgrp/dgrp_common.c
new file mode 100644
index 000000000000..fce74e7fb834
--- /dev/null
+++ b/drivers/staging/dgrp/dgrp_common.c
@@ -0,0 +1,200 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ * James Puzzo <jamesp at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, 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.
+ *
+ */
+
+/*
+ *
+ * Filename:
+ *
+ * dgrp_common.c
+ *
+ * Description:
+ *
+ * Definitions of global variables and functions which are either
+ * shared by the tty, mon, and net drivers; or which cross them
+ * functionally (like the poller).
+ *
+ * Author:
+ *
+ * James A. Puzzo
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+
+#include "dgrp_common.h"
+
+/**
+ * dgrp_carrier -- check for carrier change state and act
+ * @ch: struct ch_struct *
+ */
+void dgrp_carrier(struct ch_struct *ch)
+{
+ struct nd_struct *nd;
+
+ int virt_carrier = 0;
+ int phys_carrier = 0;
+
+ /* fix case when the tty has already closed. */
+
+ if (!ch)
+ return;
+ nd = ch->ch_nd;
+ if (!nd)
+ return;
+
+ /*
+ * If we are currently waiting to determine the status of the port,
+ * we don't yet know the state of the modem lines. As a result,
+ * we ignore state changes when we are waiting for the modem lines
+ * to be established. We know, as a result of code in dgrp_net_ops,
+ * that we will be called again immediately following the reception
+ * of the status message with the true modem status flags in it.
+ */
+ if (ch->ch_expect & RR_STATUS)
+ return;
+
+ /*
+ * If CH_HANGUP is set, we gotta keep trying to get all the processes
+ * that have the port open to close the port.
+ * So lets just keep sending a hangup every time we get here.
+ */
+ if ((ch->ch_flag & CH_HANGUP) &&
+ (ch->ch_tun.un_open_count > 0))
+ tty_hangup(ch->ch_tun.un_tty);
+
+ /*
+ * Compute the effective state of both the physical and virtual
+ * senses of carrier.
+ */
+
+ if (ch->ch_s_mlast & DM_CD)
+ phys_carrier = 1;
+
+ if ((ch->ch_s_mlast & DM_CD) ||
+ (ch->ch_digi.digi_flags & DIGI_FORCEDCD) ||
+ (ch->ch_flag & CH_CLOCAL))
+ virt_carrier = 1;
+
+ /*
+ * Test for a VIRTUAL carrier transition to HIGH.
+ *
+ * The CH_HANGUP condition is intended to prevent any action
+ * except for close. As a result, we ignore positive carrier
+ * transitions during CH_HANGUP.
+ */
+ if (((ch->ch_flag & CH_HANGUP) == 0) &&
+ ((ch->ch_flag & CH_VIRT_CD) == 0) &&
+ (virt_carrier == 1)) {
+ /*
+ * When carrier rises, wake any threads waiting
+ * for carrier in the open routine.
+ */
+ nd->nd_tx_work = 1;
+
+ if (waitqueue_active(&ch->ch_flag_wait))
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+
+ /*
+ * Test for a PHYSICAL transition to low, so long as we aren't
+ * currently ignoring physical transitions (which is what "virtual
+ * carrier" indicates).
+ *
+ * The transition of the virtual carrier to low really doesn't
+ * matter... it really only means "ignore carrier state", not
+ * "make pretend that carrier is there".
+ */
+ if ((virt_carrier == 0) &&
+ ((ch->ch_flag & CH_PHYS_CD) != 0) &&
+ (phys_carrier == 0)) {
+ /*
+ * When carrier drops:
+ *
+ * Do a Hard Hangup if that is called for.
+ *
+ * Drop carrier on all open units.
+ *
+ * Flush queues, waking up any task waiting in the
+ * line discipline.
+ *
+ * Send a hangup to the control terminal.
+ *
+ * Enable all select calls.
+ */
+
+ nd->nd_tx_work = 1;
+
+ ch->ch_flag &= ~(CH_LOW | CH_EMPTY | CH_DRAIN | CH_INPUT);
+
+ if (waitqueue_active(&ch->ch_flag_wait))
+ wake_up_interruptible(&ch->ch_flag_wait);
+
+ if (ch->ch_tun.un_open_count > 0)
+ tty_hangup(ch->ch_tun.un_tty);
+
+ if (ch->ch_pun.un_open_count > 0)
+ tty_hangup(ch->ch_pun.un_tty);
+ }
+
+ /*
+ * Make sure that our cached values reflect the current reality.
+ */
+ if (virt_carrier == 1)
+ ch->ch_flag |= CH_VIRT_CD;
+ else
+ ch->ch_flag &= ~CH_VIRT_CD;
+
+ if (phys_carrier == 1)
+ ch->ch_flag |= CH_PHYS_CD;
+ else
+ ch->ch_flag &= ~CH_PHYS_CD;
+
+}
+
+/**
+ * dgrp_chk_perm() -- check permissions for net device
+ * @inode: pointer to inode structure for the net communication device
+ * @op: operation to be tested
+ *
+ * The file permissions and ownerships are tested to determine whether
+ * the operation "op" is permitted on the file pointed to by the inode.
+ * Returns 0 if the operation is permitted, -EACCESS otherwise
+ */
+int dgrp_chk_perm(int mode, int op)
+{
+ if (!current_euid())
+ mode >>= 6;
+ else if (in_egroup_p(0))
+ mode >>= 3;
+
+ if ((mode & op & 0007) == op)
+ return 0;
+
+ if (capable(CAP_SYS_ADMIN))
+ return 0;
+
+ return -EACCES;
+}
+
+/* dgrp_chk_perm wrapper for permission call in struct inode_operations */
+int dgrp_inode_permission(struct inode *inode, int op)
+{
+ return dgrp_chk_perm(inode->i_mode, op);
+}
diff --git a/drivers/staging/dgrp/dgrp_common.h b/drivers/staging/dgrp/dgrp_common.h
new file mode 100644
index 000000000000..05ff338471ac
--- /dev/null
+++ b/drivers/staging/dgrp/dgrp_common.h
@@ -0,0 +1,208 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ * James Puzzo <jamesp at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, 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 __DGRP_COMMON_H
+#define __DGRP_COMMON_H
+
+#define DIGI_VERSION "1.9-29"
+
+#include <linux/fs.h>
+#include <linux/timer.h>
+#include "drp.h"
+
+#define DGRP_TTIME 100
+#define DGRP_RTIME 100
+
+/************************************************************************
+ * All global storage allocation.
+ ************************************************************************/
+
+extern int dgrp_rawreadok; /* Allow raw writing of input */
+extern int dgrp_register_cudevices; /* enable legacy cu devices */
+extern int dgrp_register_prdevices; /* enable transparent print devices */
+extern int dgrp_poll_tick; /* Poll interval - in ms */
+
+extern struct list_head nd_struct_list;
+
+struct dgrp_poll_data {
+ spinlock_t poll_lock;
+ struct timer_list timer;
+ int poll_tick;
+ ulong poll_round; /* Timer rouding factor */
+ long node_active_count;
+};
+
+extern struct dgrp_poll_data dgrp_poll_data;
+extern void dgrp_poll_handler(unsigned long arg);
+
+/* from dgrp_mon_ops.c */
+extern void dgrp_register_mon_hook(struct proc_dir_entry *de);
+
+/* from dgrp_tty.c */
+extern int dgrp_tty_init(struct nd_struct *nd);
+extern void dgrp_tty_uninit(struct nd_struct *nd);
+
+/* from dgrp_ports_ops.c */
+extern void dgrp_register_ports_hook(struct proc_dir_entry *de);
+
+/* from dgrp_net_ops.c */
+extern void dgrp_register_net_hook(struct proc_dir_entry *de);
+
+/* from dgrp_dpa_ops.c */
+extern void dgrp_register_dpa_hook(struct proc_dir_entry *de);
+extern void dgrp_dpa_data(struct nd_struct *, int, u8 *, int);
+
+/* from dgrp_sysfs.c */
+extern void dgrp_create_class_sysfs_files(void);
+extern void dgrp_remove_class_sysfs_files(void);
+
+extern void dgrp_create_node_class_sysfs_files(struct nd_struct *nd);
+extern void dgrp_remove_node_class_sysfs_files(struct nd_struct *nd);
+
+extern void dgrp_create_tty_sysfs(struct un_struct *un, struct device *c);
+extern void dgrp_remove_tty_sysfs(struct device *c);
+
+/* from dgrp_specproc.c */
+/*
+ * The list of DGRP entries with r/w capabilities. These
+ * magic numbers are used for identification purposes.
+ */
+enum {
+ DGRP_CONFIG = 1, /* Configure portservers */
+ DGRP_NETDIR = 2, /* Directory for "net" devices */
+ DGRP_MONDIR = 3, /* Directory for "mon" devices */
+ DGRP_PORTSDIR = 4, /* Directory for "ports" devices */
+ DGRP_INFO = 5, /* Get info. about the running module */
+ DGRP_NODEINFO = 6, /* Get info. about the configured nodes */
+ DGRP_DPADIR = 7, /* Directory for the "dpa" devices */
+};
+
+/*
+ * Directions for proc handlers
+ */
+enum {
+ INBOUND = 1, /* Data being written to kernel */
+ OUTBOUND = 2, /* Data being read from the kernel */
+};
+
+/**
+ * dgrp_proc_entry: structure for dgrp proc dirs
+ * @id: ID number associated with this particular entry. Should be
+ * unique across all of DGRP.
+ * @name: text name associated with the /proc entry
+ * @mode: file access permisssions for the /proc entry
+ * @child: pointer to table describing a subdirectory for this entry
+ * @de: pointer to directory entry for this object once registered. Used
+ * to grab the handle of the object for unregistration
+ * @excl_sem: semaphore to provide exclusive to struct
+ * @excl_cnt: counter of current accesses
+ *
+ * Each entry in a DGRP proc directory is described with a
+ * dgrp_proc_entry structure. A collection of these
+ * entries (in an array) represents the members associated
+ * with a particular /proc directory, and is referred to
+ * as a table. All tables are terminated by an entry with
+ * zeros for every member.
+ */
+struct dgrp_proc_entry {
+ int id; /* Integer identifier */
+ const char *name; /* ASCII identifier */
+ mode_t mode; /* File access permissions */
+ struct dgrp_proc_entry *child; /* Child pointer */
+
+ /* file ops to use, pass NULL to use default */
+ struct file_operations *proc_file_ops;
+
+ struct proc_dir_entry *de; /* proc entry pointer */
+ struct semaphore excl_sem; /* Protects exclusive access var */
+ int excl_cnt; /* Counts number of curr accesses */
+};
+
+extern void dgrp_unregister_proc(void);
+extern void dgrp_register_proc(void);
+
+/*-----------------------------------------------------------------------*
+ *
+ * Declarations for common operations:
+ *
+ * (either used by more than one of net, mon, or tty,
+ * or in interrupt context (i.e. the poller))
+ *
+ *-----------------------------------------------------------------------*/
+
+void dgrp_carrier(struct ch_struct *ch);
+extern int dgrp_inode_permission(struct inode *inode, int op);
+extern int dgrp_chk_perm(int mode, int op);
+
+
+/*
+ * ID manipulation macros (where c1 & c2 are characters, i is
+ * a long integer, and s is a character array of at least three members
+ */
+
+static inline void ID_TO_CHAR(long i, char *s)
+{
+ s[0] = ((i & 0xff00)>>8);
+ s[1] = (i & 0xff);
+ s[2] = 0;
+}
+
+static inline long CHAR_TO_ID(char *s)
+{
+ return ((s[0] & 0xff) << 8) | (s[1] & 0xff);
+}
+
+static inline struct nd_struct *nd_struct_get(long major)
+{
+ struct nd_struct *nd;
+
+ list_for_each_entry(nd, &nd_struct_list, list) {
+ if (major == nd->nd_major)
+ return nd;
+ }
+
+ return NULL;
+}
+
+static inline int nd_struct_add(struct nd_struct *entry)
+{
+ struct nd_struct *ptr;
+
+ ptr = nd_struct_get(entry->nd_major);
+
+ if (ptr)
+ return -EBUSY;
+
+ list_add_tail(&entry->list, &nd_struct_list);
+
+ return 0;
+}
+
+static inline int nd_struct_del(struct nd_struct *entry)
+{
+ struct nd_struct *nd;
+
+ nd = nd_struct_get(entry->nd_major);
+
+ if (!nd)
+ return -ENODEV;
+
+ list_del(&nd->list);
+ return 0;
+}
+
+#endif /* __DGRP_COMMON_H */
diff --git a/drivers/staging/dgrp/dgrp_dpa_ops.c b/drivers/staging/dgrp/dgrp_dpa_ops.c
new file mode 100644
index 000000000000..49e670915e5c
--- /dev/null
+++ b/drivers/staging/dgrp/dgrp_dpa_ops.c
@@ -0,0 +1,556 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ * James Puzzo <jamesp at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, 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.
+ *
+ */
+
+/*
+ *
+ * Filename:
+ *
+ * dgrp_dpa_ops.c
+ *
+ * Description:
+ *
+ * Handle the file operations required for the "dpa" devices.
+ * Includes those functions required to register the "dpa" devices
+ * in "/proc".
+ *
+ * Author:
+ *
+ * James A. Puzzo
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/tty.h>
+#include <linux/poll.h>
+#include <linux/cred.h>
+#include <linux/sched.h>
+#include <linux/ratelimit.h>
+#include <asm/unaligned.h>
+
+#include "dgrp_common.h"
+
+/* File operation declarations */
+static int dgrp_dpa_open(struct inode *, struct file *);
+static int dgrp_dpa_release(struct inode *, struct file *);
+static ssize_t dgrp_dpa_read(struct file *, char __user *, size_t, loff_t *);
+static long dgrp_dpa_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+static unsigned int dgrp_dpa_select(struct file *, struct poll_table_struct *);
+
+static const struct file_operations dpa_ops = {
+ .owner = THIS_MODULE,
+ .read = dgrp_dpa_read,
+ .poll = dgrp_dpa_select,
+ .unlocked_ioctl = dgrp_dpa_ioctl,
+ .open = dgrp_dpa_open,
+ .release = dgrp_dpa_release,
+};
+
+static struct inode_operations dpa_inode_ops = {
+ .permission = dgrp_inode_permission
+};
+
+
+
+struct digi_node {
+ uint nd_state; /* Node state: 1 = up, 0 = down. */
+ uint nd_chan_count; /* Number of channels found */
+ uint nd_tx_byte; /* Tx data count */
+ uint nd_rx_byte; /* RX data count */
+ u8 nd_ps_desc[MAX_DESC_LEN]; /* Description from PS */
+};
+
+#define DIGI_GETNODE (('d'<<8) | 249) /* get board info */
+
+
+struct digi_chan {
+ uint ch_port; /* Port number to get info on */
+ uint ch_open; /* 1 if open, 0 if not */
+ uint ch_txcount; /* TX data count */
+ uint ch_rxcount; /* RX data count */
+ uint ch_s_brate; /* Realport BRATE */
+ uint ch_s_estat; /* Realport ELAST */
+ uint ch_s_cflag; /* Realport CFLAG */
+ uint ch_s_iflag; /* Realport IFLAG */
+ uint ch_s_oflag; /* Realport OFLAG */
+ uint ch_s_xflag; /* Realport XFLAG */
+ uint ch_s_mstat; /* Realport MLAST */
+};
+
+#define DIGI_GETCHAN (('d'<<8) | 248) /* get channel info */
+
+
+struct digi_vpd {
+ int vpd_len;
+ char vpd_data[VPDSIZE];
+};
+
+#define DIGI_GETVPD (('d'<<8) | 246) /* get VPD info */
+
+
+struct digi_debug {
+ int onoff;
+ int port;
+};
+
+#define DIGI_SETDEBUG (('d'<<8) | 247) /* set debug info */
+
+
+void dgrp_register_dpa_hook(struct proc_dir_entry *de)
+{
+ struct nd_struct *node = de->data;
+
+ de->proc_iops = &dpa_inode_ops;
+ de->proc_fops = &dpa_ops;
+
+ node->nd_dpa_de = de;
+ spin_lock_init(&node->nd_dpa_lock);
+}
+
+/*
+ * dgrp_dpa_open -- open the DPA device for a particular PortServer
+ */
+static int dgrp_dpa_open(struct inode *inode, struct file *file)
+{
+ struct nd_struct *nd;
+ int rtn = 0;
+
+ struct proc_dir_entry *de;
+
+ rtn = try_module_get(THIS_MODULE);
+ if (!rtn)
+ return -ENXIO;
+
+ rtn = 0;
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ rtn = -EPERM;
+ goto done;
+ }
+
+ /*
+ * Make sure that the "private_data" field hasn't already been used.
+ */
+ if (file->private_data) {
+ rtn = -EINVAL;
+ goto done;
+ }
+
+ /*
+ * Get the node pointer, and fail if it doesn't exist.
+ */
+ de = PDE(inode);
+ if (!de) {
+ rtn = -ENXIO;
+ goto done;
+ }
+ nd = (struct nd_struct *)de->data;
+ if (!nd) {
+ rtn = -ENXIO;
+ goto done;
+ }
+
+ file->private_data = (void *) nd;
+
+ /*
+ * Allocate the DPA buffer.
+ */
+
+ if (nd->nd_dpa_buf) {
+ rtn = -EBUSY;
+ } else {
+ nd->nd_dpa_buf = kmalloc(DPA_MAX, GFP_KERNEL);
+
+ if (!nd->nd_dpa_buf) {
+ rtn = -ENOMEM;
+ } else {
+ nd->nd_dpa_out = 0;
+ nd->nd_dpa_in = 0;
+ nd->nd_dpa_lbolt = jiffies;
+ }
+ }
+
+done:
+
+ if (rtn)
+ module_put(THIS_MODULE);
+ return rtn;
+}
+
+/*
+ * dgrp_dpa_release -- close the DPA device for a particular PortServer
+ */
+static int dgrp_dpa_release(struct inode *inode, struct file *file)
+{
+ struct nd_struct *nd;
+ u8 *buf;
+ unsigned long lock_flags;
+
+ /*
+ * Get the node pointer, and quit if it doesn't exist.
+ */
+ nd = (struct nd_struct *)(file->private_data);
+ if (!nd)
+ goto done;
+
+ /*
+ * Free the dpa buffer.
+ */
+
+ spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+
+ buf = nd->nd_dpa_buf;
+
+ nd->nd_dpa_buf = NULL;
+ nd->nd_dpa_out = nd->nd_dpa_in;
+
+ /*
+ * Wakeup any thread waiting for buffer space.
+ */
+
+ if (nd->nd_dpa_flag & DPA_WAIT_SPACE) {
+ nd->nd_dpa_flag &= ~DPA_WAIT_SPACE;
+ wake_up_interruptible(&nd->nd_dpa_wqueue);
+ }
+
+ spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+
+ kfree(buf);
+
+done:
+ module_put(THIS_MODULE);
+ file->private_data = NULL;
+ return 0;
+}
+
+/*
+ * dgrp_dpa_read
+ *
+ * Copy data from the monitoring buffer to the user, freeing space
+ * in the monitoring buffer for more messages
+ */
+static ssize_t dgrp_dpa_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct nd_struct *nd;
+ int n;
+ int r;
+ int offset = 0;
+ int res = 0;
+ ssize_t rtn;
+ unsigned long lock_flags;
+
+ /*
+ * Get the node pointer, and quit if it doesn't exist.
+ */
+ nd = (struct nd_struct *)(file->private_data);
+ if (!nd)
+ return -ENXIO;
+
+ /*
+ * Wait for some data to appear in the buffer.
+ */
+
+ spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+
+ for (;;) {
+ n = (nd->nd_dpa_in - nd->nd_dpa_out) & DPA_MASK;
+
+ if (n != 0)
+ break;
+
+ nd->nd_dpa_flag |= DPA_WAIT_DATA;
+
+ spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+
+ /*
+ * Go to sleep waiting until the condition becomes true.
+ */
+ rtn = wait_event_interruptible(nd->nd_dpa_wqueue,
+ ((nd->nd_dpa_flag & DPA_WAIT_DATA) == 0));
+
+ if (rtn)
+ return rtn;
+
+ spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+ }
+
+ /*
+ * Read whatever is there.
+ */
+
+ if (n > count)
+ n = count;
+
+ res = n;
+
+ r = DPA_MAX - nd->nd_dpa_out;
+
+ if (r <= n) {
+
+ spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+ rtn = copy_to_user((void __user *)buf,
+ nd->nd_dpa_buf + nd->nd_dpa_out, r);
+ spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+
+ if (rtn) {
+ rtn = -EFAULT;
+ goto done;
+ }
+
+ nd->nd_dpa_out = 0;
+ n -= r;
+ offset = r;
+ }
+
+ spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+ rtn = copy_to_user((void __user *)buf + offset,
+ nd->nd_dpa_buf + nd->nd_dpa_out, n);
+ spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+
+ if (rtn) {
+ rtn = -EFAULT;
+ goto done;
+ }
+
+ nd->nd_dpa_out += n;
+
+ *ppos += res;
+
+ rtn = res;
+
+ /*
+ * Wakeup any thread waiting for buffer space.
+ */
+
+ n = (nd->nd_dpa_in - nd->nd_dpa_out) & DPA_MASK;
+
+ if (nd->nd_dpa_flag & DPA_WAIT_SPACE &&
+ (DPA_MAX - n) > DPA_HIGH_WATER) {
+ nd->nd_dpa_flag &= ~DPA_WAIT_SPACE;
+ wake_up_interruptible(&nd->nd_dpa_wqueue);
+ }
+
+ done:
+ spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+ return rtn;
+}
+
+static unsigned int dgrp_dpa_select(struct file *file,
+ struct poll_table_struct *table)
+{
+ unsigned int retval = 0;
+ struct nd_struct *nd = file->private_data;
+
+ if (nd->nd_dpa_out != nd->nd_dpa_in)
+ retval |= POLLIN | POLLRDNORM; /* Conditionally readable */
+
+ retval |= POLLOUT | POLLWRNORM; /* Always writeable */
+
+ return retval;
+}
+
+static long dgrp_dpa_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+
+ struct nd_struct *nd;
+ struct digi_chan getchan;
+ struct digi_node getnode;
+ struct ch_struct *ch;
+ struct digi_debug setdebug;
+ struct digi_vpd vpd;
+ unsigned int port;
+ void __user *uarg = (void __user *) arg;
+
+ nd = file->private_data;
+
+ switch (cmd) {
+ case DIGI_GETCHAN:
+ if (copy_from_user(&getchan, uarg, sizeof(struct digi_chan)))
+ return -EFAULT;
+
+ port = getchan.ch_port;
+
+ if (port < 0 || port > nd->nd_chan_count)
+ return -EINVAL;
+
+ ch = nd->nd_chan + port;
+
+ getchan.ch_open = (ch->ch_open_count > 0) ? 1 : 0;
+ getchan.ch_txcount = ch->ch_txcount;
+ getchan.ch_rxcount = ch->ch_rxcount;
+ getchan.ch_s_brate = ch->ch_s_brate;
+ getchan.ch_s_estat = ch->ch_s_elast;
+ getchan.ch_s_cflag = ch->ch_s_cflag;
+ getchan.ch_s_iflag = ch->ch_s_iflag;
+ getchan.ch_s_oflag = ch->ch_s_oflag;
+ getchan.ch_s_xflag = ch->ch_s_xflag;
+ getchan.ch_s_mstat = ch->ch_s_mlast;
+
+ if (copy_to_user(uarg, &getchan, sizeof(struct digi_chan)))
+ return -EFAULT;
+ break;
+
+
+ case DIGI_GETNODE:
+ getnode.nd_state = (nd->nd_state & NS_READY) ? 1 : 0;
+ getnode.nd_chan_count = nd->nd_chan_count;
+ getnode.nd_tx_byte = nd->nd_tx_byte;
+ getnode.nd_rx_byte = nd->nd_rx_byte;
+
+ memset(&getnode.nd_ps_desc, 0, MAX_DESC_LEN);
+ strncpy(getnode.nd_ps_desc, nd->nd_ps_desc, MAX_DESC_LEN);
+
+ if (copy_to_user(uarg, &getnode, sizeof(struct digi_node)))
+ return -EFAULT;
+ break;
+
+
+ case DIGI_SETDEBUG:
+ if (copy_from_user(&setdebug, uarg, sizeof(struct digi_debug)))
+ return -EFAULT;
+
+ nd->nd_dpa_debug = setdebug.onoff;
+ nd->nd_dpa_port = setdebug.port;
+ break;
+
+
+ case DIGI_GETVPD:
+ if (nd->nd_vpd_len > 0) {
+ vpd.vpd_len = nd->nd_vpd_len;
+ memcpy(&vpd.vpd_data, &nd->nd_vpd, nd->nd_vpd_len);
+ } else {
+ vpd.vpd_len = 0;
+ }
+
+ if (copy_to_user(uarg, &vpd, sizeof(struct digi_vpd)))
+ return -EFAULT;
+ break;
+ }
+
+ return 0;
+}
+
+/**
+ * dgrp_dpa() -- send data to the device monitor queue
+ * @nd: pointer to a node structure
+ * @buf: buffer of data to copy to the monitoring buffer
+ * @len: number of bytes to transfer to the buffer
+ *
+ * Called by the net device routines to send data to the device
+ * monitor queue. If the device monitor buffer is too full to
+ * accept the data, it waits until the buffer is ready.
+ */
+static void dgrp_dpa(struct nd_struct *nd, u8 *buf, int nbuf)
+{
+ int n;
+ int r;
+ unsigned long lock_flags;
+
+ /*
+ * Grab DPA lock.
+ */
+ spin_lock_irqsave(&nd->nd_dpa_lock, lock_flags);
+
+ /*
+ * Loop while data remains.
+ */
+ while (nbuf > 0 && nd->nd_dpa_buf != NULL) {
+
+ n = (nd->nd_dpa_out - nd->nd_dpa_in - 1) & DPA_MASK;
+
+ /*
+ * Enforce flow control on the DPA device.
+ */
+ if (n < (DPA_MAX - DPA_HIGH_WATER))
+ nd->nd_dpa_flag |= DPA_WAIT_SPACE;
+
+ /*
+ * This should never happen, as the flow control above
+ * should have stopped things before they got to this point.
+ */
+ if (n == 0) {
+ spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+ return;
+ }
+
+ /*
+ * Copy as much data as will fit.
+ */
+
+ if (n > nbuf)
+ n = nbuf;
+
+ r = DPA_MAX - nd->nd_dpa_in;
+
+ if (r <= n) {
+ memcpy(nd->nd_dpa_buf + nd->nd_dpa_in, buf, r);
+
+ n -= r;
+
+ nd->nd_dpa_in = 0;
+
+ buf += r;
+ nbuf -= r;
+ }
+
+ memcpy(nd->nd_dpa_buf + nd->nd_dpa_in, buf, n);
+
+ nd->nd_dpa_in += n;
+
+ buf += n;
+ nbuf -= n;
+
+ if (nd->nd_dpa_in >= DPA_MAX)
+ pr_info_ratelimited("%s - nd->nd_dpa_in (%i) >= DPA_MAX\n",
+ __func__, nd->nd_dpa_in);
+
+ /*
+ * Wakeup any thread waiting for data
+ */
+ if (nd->nd_dpa_flag & DPA_WAIT_DATA) {
+ nd->nd_dpa_flag &= ~DPA_WAIT_DATA;
+ wake_up_interruptible(&nd->nd_dpa_wqueue);
+ }
+ }
+
+ /*
+ * Release the DPA lock.
+ */
+ spin_unlock_irqrestore(&nd->nd_dpa_lock, lock_flags);
+}
+
+/**
+ * dgrp_monitor_data() -- builds a DPA data packet
+ * @nd: pointer to a node structure
+ * @type: type of message to be logged in the DPA buffer
+ * @buf: buffer of data to be logged in the DPA buffer
+ * @size -- number of bytes in the "buf" buffer
+ */
+void dgrp_dpa_data(struct nd_struct *nd, int type, u8 *buf, int size)
+{
+ u8 header[5];
+
+ header[0] = type;
+
+ put_unaligned_be32(size, header + 1);
+
+ dgrp_dpa(nd, header, sizeof(header));
+ dgrp_dpa(nd, buf, size);
+}
diff --git a/drivers/staging/dgrp/dgrp_driver.c b/drivers/staging/dgrp/dgrp_driver.c
new file mode 100644
index 000000000000..6e4a0ebc0749
--- /dev/null
+++ b/drivers/staging/dgrp/dgrp_driver.c
@@ -0,0 +1,110 @@
+/*
+ *
+ * Copyright 1999-2003 Digi International (www.digi.com)
+ * Jeff Randall
+ * James Puzzo <jamesp at digi dot com>
+ * Scott Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, 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.
+ *
+ */
+
+/*
+ * Driver specific includes
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/init.h>
+
+/*
+ * PortServer includes
+ */
+#include "dgrp_common.h"
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Digi International, http://www.digi.com");
+MODULE_DESCRIPTION("RealPort driver for Digi's ethernet-based serial connectivity product line");
+MODULE_VERSION(DIGI_VERSION);
+
+struct list_head nd_struct_list;
+struct dgrp_poll_data dgrp_poll_data;
+
+int dgrp_rawreadok = 1; /* Bypass flipbuf on input */
+int dgrp_register_cudevices = 1;/* Turn on/off registering legacy cu devices */
+int dgrp_register_prdevices = 1;/* Turn on/off registering transparent print */
+int dgrp_poll_tick = 20; /* Poll interval - in ms */
+
+module_param_named(rawreadok, dgrp_rawreadok, int, 0644);
+MODULE_PARM_DESC(rawreadok, "Bypass flip buffers on input");
+
+module_param_named(register_cudevices, dgrp_register_cudevices, int, 0644);
+MODULE_PARM_DESC(register_cudevices, "Turn on/off registering legacy cu devices");
+
+module_param_named(register_prdevices, dgrp_register_prdevices, int, 0644);
+MODULE_PARM_DESC(register_prdevices, "Turn on/off registering transparent print devices");
+
+module_param_named(pollrate, dgrp_poll_tick, int, 0644);
+MODULE_PARM_DESC(pollrate, "Poll interval in ms");
+
+/* Driver load/unload functions */
+static int dgrp_init_module(void);
+static void dgrp_cleanup_module(void);
+
+module_init(dgrp_init_module);
+module_exit(dgrp_cleanup_module);
+
+/*
+ * init_module()
+ *
+ * Module load. This is where it all starts.
+ */
+static int dgrp_init_module(void)
+{
+ INIT_LIST_HEAD(&nd_struct_list);
+
+ spin_lock_init(&dgrp_poll_data.poll_lock);
+ init_timer(&dgrp_poll_data.timer);
+ dgrp_poll_data.poll_tick = dgrp_poll_tick;
+ dgrp_poll_data.timer.function = dgrp_poll_handler;
+ dgrp_poll_data.timer.data = (unsigned long) &dgrp_poll_data;
+
+ dgrp_create_class_sysfs_files();
+
+ dgrp_register_proc();
+
+ return 0;
+}
+
+
+/*
+ * Module unload. This is where it all ends.
+ */
+static void dgrp_cleanup_module(void)
+{
+ struct nd_struct *nd, *next;
+
+ /*
+ * Attempting to free resources in backwards
+ * order of allocation, in case that helps
+ * memory pool fragmentation.
+ */
+ dgrp_unregister_proc();
+
+ dgrp_remove_class_sysfs_files();
+
+
+ list_for_each_entry_safe(nd, next, &nd_struct_list, list) {
+ dgrp_tty_uninit(nd);
+ kfree(nd);
+ }
+}
diff --git a/drivers/staging/dgrp/dgrp_mon_ops.c b/drivers/staging/dgrp/dgrp_mon_ops.c
new file mode 100644
index 000000000000..268dcb95204b
--- /dev/null
+++ b/drivers/staging/dgrp/dgrp_mon_ops.c
@@ -0,0 +1,346 @@
+/*****************************************************************************
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ * James Puzzo <jamesp at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, 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.
+ *
+ */
+
+/*
+ *
+ * Filename:
+ *
+ * dgrp_mon_ops.c
+ *
+ * Description:
+ *
+ * Handle the file operations required for the "monitor" devices.
+ * Includes those functions required to register the "mon" devices
+ * in "/proc".
+ *
+ * Author:
+ *
+ * James A. Puzzo
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <asm/unaligned.h>
+#include <linux/proc_fs.h>
+
+#include "dgrp_common.h"
+
+/* File operation declarations */
+static int dgrp_mon_open(struct inode *, struct file *);
+static int dgrp_mon_release(struct inode *, struct file *);
+static ssize_t dgrp_mon_read(struct file *, char __user *, size_t, loff_t *);
+static long dgrp_mon_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+
+static const struct file_operations mon_ops = {
+ .owner = THIS_MODULE,
+ .read = dgrp_mon_read,
+ .unlocked_ioctl = dgrp_mon_ioctl,
+ .open = dgrp_mon_open,
+ .release = dgrp_mon_release,
+};
+
+static struct inode_operations mon_inode_ops = {
+ .permission = dgrp_inode_permission
+};
+
+void dgrp_register_mon_hook(struct proc_dir_entry *de)
+{
+ struct nd_struct *node = de->data;
+
+ de->proc_iops = &mon_inode_ops;
+ de->proc_fops = &mon_ops;
+ node->nd_mon_de = de;
+ sema_init(&node->nd_mon_semaphore, 1);
+}
+
+/**
+ * dgrp_mon_open() -- open /proc/dgrp/ports device for a PortServer
+ * @inode: struct inode *
+ * @file: struct file *
+ *
+ * Open function to open the /proc/dgrp/ports device for a PortServer.
+ */
+static int dgrp_mon_open(struct inode *inode, struct file *file)
+{
+ struct nd_struct *nd;
+ struct proc_dir_entry *de;
+ struct timeval tv;
+ uint32_t time;
+ u8 *buf;
+ int rtn;
+
+ rtn = try_module_get(THIS_MODULE);
+ if (!rtn)
+ return -ENXIO;
+
+ rtn = 0;
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ rtn = -EPERM;
+ goto done;
+ }
+
+ /*
+ * Make sure that the "private_data" field hasn't already been used.
+ */
+ if (file->private_data) {
+ rtn = -EINVAL;
+ goto done;
+ }
+
+ /*
+ * Get the node pointer, and fail if it doesn't exist.
+ */
+ de = PDE(inode);
+ if (!de) {
+ rtn = -ENXIO;
+ goto done;
+ }
+
+ nd = (struct nd_struct *)de->data;
+ if (!nd) {
+ rtn = -ENXIO;
+ goto done;
+ }
+
+ file->private_data = (void *) nd;
+
+ /*
+ * Allocate the monitor buffer.
+ */
+
+ /*
+ * Grab the MON lock.
+ */
+ down(&nd->nd_mon_semaphore);
+
+ if (nd->nd_mon_buf) {
+ rtn = -EBUSY;
+ goto done_up;
+ }
+
+ nd->nd_mon_buf = kmalloc(MON_MAX, GFP_KERNEL);
+
+ if (!nd->nd_mon_buf) {
+ rtn = -ENOMEM;
+ goto done_up;
+ }
+
+ /*
+ * Enter an RPDUMP file header into the buffer.
+ */
+
+ buf = nd->nd_mon_buf;
+
+ strcpy(buf, RPDUMP_MAGIC);
+ buf += strlen(buf) + 1;
+
+ do_gettimeofday(&tv);
+
+ /*
+ * tv.tv_sec might be a 64 bit quantity. Pare
+ * it down to 32 bits before attempting to encode
+ * it.
+ */
+ time = (uint32_t) (tv.tv_sec & 0xffffffff);
+
+ put_unaligned_be32(time, buf);
+ put_unaligned_be16(0, buf + 4);
+ buf += 6;
+
+ if (nd->nd_tx_module) {
+ buf[0] = RPDUMP_CLIENT;
+ put_unaligned_be32(0, buf + 1);
+ put_unaligned_be16(1, buf + 5);
+ buf[7] = 0xf0 + nd->nd_tx_module;
+ buf += 8;
+ }
+
+ if (nd->nd_rx_module) {
+ buf[0] = RPDUMP_SERVER;
+ put_unaligned_be32(0, buf + 1);
+ put_unaligned_be16(1, buf + 5);
+ buf[7] = 0xf0 + nd->nd_rx_module;
+ buf += 8;
+ }
+
+ nd->nd_mon_out = 0;
+ nd->nd_mon_in = buf - nd->nd_mon_buf;
+ nd->nd_mon_lbolt = jiffies;
+
+done_up:
+ up(&nd->nd_mon_semaphore);
+
+done:
+ if (rtn)
+ module_put(THIS_MODULE);
+ return rtn;
+}
+
+
+/**
+ * dgrp_mon_release() - Close the MON device for a particular PortServer
+ * @inode: struct inode *
+ * @file: struct file *
+ */
+static int dgrp_mon_release(struct inode *inode, struct file *file)
+{
+ struct nd_struct *nd;
+
+ /*
+ * Get the node pointer, and quit if it doesn't exist.
+ */
+ nd = (struct nd_struct *)(file->private_data);
+ if (!nd)
+ goto done;
+
+ /*
+ * Free the monitor buffer.
+ */
+
+ down(&nd->nd_mon_semaphore);
+
+ kfree(nd->nd_mon_buf);
+ nd->nd_mon_buf = NULL;
+ nd->nd_mon_out = nd->nd_mon_in;
+
+ /*
+ * Wakeup any thread waiting for buffer space.
+ */
+
+ if (nd->nd_mon_flag & MON_WAIT_SPACE) {
+ nd->nd_mon_flag &= ~MON_WAIT_SPACE;
+ wake_up_interruptible(&nd->nd_mon_wqueue);
+ }
+
+ up(&nd->nd_mon_semaphore);
+
+ /*
+ * Make sure there is no thread in the middle of writing a packet.
+ */
+ down(&nd->nd_net_semaphore);
+ up(&nd->nd_net_semaphore);
+
+done:
+ module_put(THIS_MODULE);
+ file->private_data = NULL;
+ return 0;
+}
+
+/**
+ * dgrp_mon_read() -- Copy data from the monitoring buffer to the user
+ */
+static ssize_t dgrp_mon_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct nd_struct *nd;
+ int r;
+ int offset = 0;
+ int res = 0;
+ ssize_t rtn;
+
+ /*
+ * Get the node pointer, and quit if it doesn't exist.
+ */
+ nd = (struct nd_struct *)(file->private_data);
+ if (!nd)
+ return -ENXIO;
+
+ /*
+ * Wait for some data to appear in the buffer.
+ */
+
+ down(&nd->nd_mon_semaphore);
+
+ for (;;) {
+ res = (nd->nd_mon_in - nd->nd_mon_out) & MON_MASK;
+
+ if (res)
+ break;
+
+ nd->nd_mon_flag |= MON_WAIT_DATA;
+
+ up(&nd->nd_mon_semaphore);
+
+ /*
+ * Go to sleep waiting until the condition becomes true.
+ */
+ rtn = wait_event_interruptible(nd->nd_mon_wqueue,
+ ((nd->nd_mon_flag & MON_WAIT_DATA) == 0));
+
+ if (rtn)
+ return rtn;
+
+ down(&nd->nd_mon_semaphore);
+ }
+
+ /*
+ * Read whatever is there.
+ */
+
+ if (res > count)
+ res = count;
+
+ r = MON_MAX - nd->nd_mon_out;
+
+ if (r <= res) {
+ rtn = copy_to_user((void __user *)buf,
+ nd->nd_mon_buf + nd->nd_mon_out, r);
+ if (rtn) {
+ up(&nd->nd_mon_semaphore);
+ return -EFAULT;
+ }
+
+ nd->nd_mon_out = 0;
+ res -= r;
+ offset = r;
+ }
+
+ rtn = copy_to_user((void __user *) buf + offset,
+ nd->nd_mon_buf + nd->nd_mon_out, res);
+ if (rtn) {
+ up(&nd->nd_mon_semaphore);
+ return -EFAULT;
+ }
+
+ nd->nd_mon_out += res;
+
+ *ppos += res;
+
+ up(&nd->nd_mon_semaphore);
+
+ /*
+ * Wakeup any thread waiting for buffer space.
+ */
+
+ if (nd->nd_mon_flag & MON_WAIT_SPACE) {
+ nd->nd_mon_flag &= ~MON_WAIT_SPACE;
+ wake_up_interruptible(&nd->nd_mon_wqueue);
+ }
+
+ return res;
+}
+
+/* ioctl is not valid on monitor device */
+static long dgrp_mon_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ return -EINVAL;
+}
diff --git a/drivers/staging/dgrp/dgrp_net_ops.c b/drivers/staging/dgrp/dgrp_net_ops.c
new file mode 100644
index 000000000000..ab839ea3b44c
--- /dev/null
+++ b/drivers/staging/dgrp/dgrp_net_ops.c
@@ -0,0 +1,3737 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ * James Puzzo <jamesp at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, 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.
+ *
+ */
+
+/*
+ *
+ * Filename:
+ *
+ * dgrp_net_ops.c
+ *
+ * Description:
+ *
+ * Handle the file operations required for the "network" devices.
+ * Includes those functions required to register the "net" devices
+ * in "/proc".
+ *
+ * Author:
+ *
+ * James A. Puzzo
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/spinlock.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/ratelimit.h>
+#include <asm/unaligned.h>
+
+#define MYFLIPLEN TBUF_MAX
+
+#include "dgrp_common.h"
+
+#define TTY_FLIPBUF_SIZE 512
+#define DEVICE_NAME_SIZE 50
+
+/*
+ * Generic helper function declarations
+ */
+static void parity_scan(struct ch_struct *ch, unsigned char *cbuf,
+ unsigned char *fbuf, int *len);
+
+/*
+ * File operation declarations
+ */
+static int dgrp_net_open(struct inode *, struct file *);
+static int dgrp_net_release(struct inode *, struct file *);
+static ssize_t dgrp_net_read(struct file *, char __user *, size_t, loff_t *);
+static ssize_t dgrp_net_write(struct file *, const char __user *, size_t,
+ loff_t *);
+static long dgrp_net_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg);
+static unsigned int dgrp_net_select(struct file *file,
+ struct poll_table_struct *table);
+
+static const struct file_operations net_ops = {
+ .owner = THIS_MODULE,
+ .read = dgrp_net_read,
+ .write = dgrp_net_write,
+ .poll = dgrp_net_select,
+ .unlocked_ioctl = dgrp_net_ioctl,
+ .open = dgrp_net_open,
+ .release = dgrp_net_release,
+};
+
+static struct inode_operations net_inode_ops = {
+ .permission = dgrp_inode_permission
+};
+
+void dgrp_register_net_hook(struct proc_dir_entry *de)
+{
+ struct nd_struct *node = de->data;
+
+ de->proc_iops = &net_inode_ops;
+ de->proc_fops = &net_ops;
+ node->nd_net_de = de;
+ sema_init(&node->nd_net_semaphore, 1);
+ node->nd_state = NS_CLOSED;
+ dgrp_create_node_class_sysfs_files(node);
+}
+
+
+/**
+ * dgrp_dump() -- prints memory for debugging purposes.
+ * @mem: Memory location which should be printed to the console
+ * @len: Number of bytes to be dumped
+ */
+static void dgrp_dump(u8 *mem, int len)
+{
+ int i;
+
+ pr_debug("dgrp dump length = %d, data = ", len);
+ for (i = 0; i < len; ++i)
+ pr_debug("%.2x ", mem[i]);
+ pr_debug("\n");
+}
+
+/**
+ * dgrp_read_data_block() -- Read a data block
+ * @ch: struct ch_struct *
+ * @flipbuf: u8 *
+ * @flipbuf_size: size of flipbuf
+ */
+static void dgrp_read_data_block(struct ch_struct *ch, u8 *flipbuf,
+ int flipbuf_size)
+{
+ int t;
+ int n;
+
+ if (flipbuf_size <= 0)
+ return;
+
+ t = RBUF_MAX - ch->ch_rout;
+ n = flipbuf_size;
+
+ if (n >= t) {
+ memcpy(flipbuf, ch->ch_rbuf + ch->ch_rout, t);
+ flipbuf += t;
+ n -= t;
+ ch->ch_rout = 0;
+ }
+
+ memcpy(flipbuf, ch->ch_rbuf + ch->ch_rout, n);
+ flipbuf += n;
+ ch->ch_rout += n;
+}
+
+
+/**
+ * dgrp_input() -- send data to the line disipline
+ * @ch: pointer to channel struct
+ *
+ * Copys the rbuf to the flipbuf and sends to line discipline.
+ * Sends input buffer data to the line discipline.
+ *
+ * There are several modes to consider here:
+ * rawreadok, tty->real_raw, and IF_PARMRK
+ */
+static void dgrp_input(struct ch_struct *ch)
+{
+ struct nd_struct *nd;
+ struct tty_struct *tty;
+ int remain;
+ int data_len;
+ int len;
+ int flip_len;
+ int tty_count;
+ ulong lock_flags;
+ struct tty_ldisc *ld;
+ u8 *myflipbuf;
+ u8 *myflipflagbuf;
+
+ if (!ch)
+ return;
+
+ nd = ch->ch_nd;
+
+ if (!nd)
+ return;
+
+ spin_lock_irqsave(&nd->nd_lock, lock_flags);
+
+ myflipbuf = nd->nd_inputbuf;
+ myflipflagbuf = nd->nd_inputflagbuf;
+
+ if (!ch->ch_open_count) {
+ ch->ch_rout = ch->ch_rin;
+ goto out;
+ }
+
+ if (ch->ch_tun.un_flag & UN_CLOSING) {
+ ch->ch_rout = ch->ch_rin;
+ goto out;
+ }
+
+ tty = (ch->ch_tun).un_tty;
+
+
+ if (!tty || tty->magic != TTY_MAGIC) {
+ ch->ch_rout = ch->ch_rin;
+ goto out;
+ }
+
+ tty_count = tty->count;
+ if (!tty_count) {
+ ch->ch_rout = ch->ch_rin;
+ goto out;
+ }
+
+ if (tty->closing || test_bit(TTY_CLOSING, &tty->flags)) {
+ ch->ch_rout = ch->ch_rin;
+ goto out;
+ }
+
+ spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+
+ /* Decide how much data we can send into the tty layer */
+ if (dgrp_rawreadok && tty->real_raw)
+ flip_len = MYFLIPLEN;
+ else
+ flip_len = TTY_FLIPBUF_SIZE;
+
+ /* data_len should be the number of chars that we read in */
+ data_len = (ch->ch_rin - ch->ch_rout) & RBUF_MASK;
+ remain = data_len;
+
+ /* len is the amount of data we are going to transfer here */
+ len = min(data_len, flip_len);
+
+ /* take into consideration length of ldisc */
+ len = min(len, (N_TTY_BUF_SIZE - 1) - tty->read_cnt);
+
+ ld = tty_ldisc_ref(tty);
+
+ /*
+ * If we were unable to get a reference to the ld,
+ * don't flush our buffer, and act like the ld doesn't
+ * have any space to put the data right now.
+ */
+ if (!ld) {
+ len = 0;
+ } else if (!ld->ops->receive_buf) {
+ spin_lock_irqsave(&nd->nd_lock, lock_flags);
+ ch->ch_rout = ch->ch_rin;
+ spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+ len = 0;
+ }
+
+ /* Check DPA flow control */
+ if ((nd->nd_dpa_debug) &&
+ (nd->nd_dpa_flag & DPA_WAIT_SPACE) &&
+ (nd->nd_dpa_port == MINOR(tty_devnum(ch->ch_tun.un_tty))))
+ len = 0;
+
+ if ((len) && !(ch->ch_flag & CH_RXSTOP)) {
+
+ dgrp_read_data_block(ch, myflipbuf, len);
+
+ /*
+ * In high performance mode, we don't have to update
+ * flag_buf or any of the counts or pointers into flip buf.
+ */
+ if (!dgrp_rawreadok || !tty->real_raw) {
+ if (I_PARMRK(tty) || I_BRKINT(tty) || I_INPCK(tty))
+ parity_scan(ch, myflipbuf, myflipflagbuf, &len);
+ else
+ memset(myflipflagbuf, TTY_NORMAL, len);
+ }
+
+ if ((nd->nd_dpa_debug) &&
+ (nd->nd_dpa_port == PORT_NUM(MINOR(tty_devnum(tty)))))
+ dgrp_dpa_data(nd, 1, myflipbuf, len);
+
+ /*
+ * If we're doing raw reads, jam it right into the
+ * line disc bypassing the flip buffers.
+ */
+ if (dgrp_rawreadok && tty->real_raw)
+ ld->ops->receive_buf(tty, myflipbuf, NULL, len);
+ else {
+ len = tty_buffer_request_room(tty, len);
+ tty_insert_flip_string_flags(tty, myflipbuf,
+ myflipflagbuf, len);
+
+ /* Tell the tty layer its okay to "eat" the data now */
+ tty_flip_buffer_push(tty);
+ }
+
+ ch->ch_rxcount += len;
+ }
+
+ if (ld)
+ tty_ldisc_deref(ld);
+
+ /*
+ * Wake up any sleepers (maybe dgrp close) that might be waiting
+ * for a channel flag state change.
+ */
+ wake_up_interruptible(&ch->ch_flag_wait);
+ return;
+
+out:
+ spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+}
+
+
+/*
+ * parity_scan
+ *
+ * Loop to inspect each single character or 0xFF escape.
+ *
+ * if PARMRK & ~DOSMODE:
+ * 0xFF 0xFF Normal 0xFF character, escaped
+ * to eliminate confusion.
+ * 0xFF 0x00 0x00 Break
+ * 0xFF 0x00 CC Error character CC.
+ * CC Normal character CC.
+ *
+ * if PARMRK & DOSMODE:
+ * 0xFF 0x18 0x00 Break
+ * 0xFF 0x08 0x00 Framing Error
+ * 0xFF 0x04 0x00 Parity error
+ * 0xFF 0x0C 0x00 Both Framing and Parity error
+ *
+ * TODO: do we need to do the XMODEM, XOFF, XON, XANY processing??
+ * as per protocol
+ */
+static void parity_scan(struct ch_struct *ch, unsigned char *cbuf,
+ unsigned char *fbuf, int *len)
+{
+ int l = *len;
+ int count = 0;
+ int DOS = ((ch->ch_iflag & IF_DOSMODE) == 0 ? 0 : 1);
+ unsigned char *cout; /* character buffer */
+ unsigned char *fout; /* flag buffer */
+ unsigned char *in;
+ unsigned char c;
+
+ in = cbuf;
+ cout = cbuf;
+ fout = fbuf;
+
+ while (l--) {
+ c = *in;
+ in++;
+
+ switch (ch->ch_pscan_state) {
+ default:
+ /* reset to sanity and fall through */
+ ch->ch_pscan_state = 0 ;
+
+ case 0:
+ /* No FF seen yet */
+ if (c == 0xff) /* delete this character from stream */
+ ch->ch_pscan_state = 1;
+ else {
+ *cout++ = c;
+ *fout++ = TTY_NORMAL;
+ count += 1;
+ }
+ break;
+
+ case 1:
+ /* first FF seen */
+ if (c == 0xff) {
+ /* doubled ff, transform to single ff */
+ *cout++ = c;
+ *fout++ = TTY_NORMAL;
+ count += 1;
+ ch->ch_pscan_state = 0;
+ } else {
+ /* save value examination in next state */
+ ch->ch_pscan_savechar = c;
+ ch->ch_pscan_state = 2;
+ }
+ break;
+
+ case 2:
+ /* third character of ff sequence */
+ *cout++ = c;
+ if (DOS) {
+ if (ch->ch_pscan_savechar & 0x10)
+ *fout++ = TTY_BREAK;
+ else if (ch->ch_pscan_savechar & 0x08)
+ *fout++ = TTY_FRAME;
+ else
+ /*
+ * either marked as a parity error,
+ * indeterminate, or not in DOSMODE
+ * call it a parity error
+ */
+ *fout++ = TTY_PARITY;
+ } else {
+ /* case FF XX ?? where XX is not 00 */
+ if (ch->ch_pscan_savechar & 0xff) {
+ /* this should not happen */
+ pr_info("%s: parity_scan: error unexpected byte\n",
+ __func__);
+ *fout++ = TTY_PARITY;
+ }
+ /* case FF 00 XX where XX is not 00 */
+ else if (c == 0xff)
+ *fout++ = TTY_PARITY;
+ /* case FF 00 00 */
+ else
+ *fout++ = TTY_BREAK;
+
+ }
+ count += 1;
+ ch->ch_pscan_state = 0;
+ }
+ }
+ *len = count;
+}
+
+
+/**
+ * dgrp_net_idle() -- Idle the network connection
+ * @nd: pointer to node structure to idle
+ */
+static void dgrp_net_idle(struct nd_struct *nd)
+{
+ struct ch_struct *ch;
+ int i;
+
+ nd->nd_tx_work = 1;
+
+ nd->nd_state = NS_IDLE;
+ nd->nd_flag = 0;
+
+ for (i = nd->nd_seq_out; ; i = (i + 1) & SEQ_MASK) {
+ if (!nd->nd_seq_wait[i]) {
+ nd->nd_seq_wait[i] = 0;
+ wake_up_interruptible(&nd->nd_seq_wque[i]);
+ }
+
+ if (i == nd->nd_seq_in)
+ break;
+ }
+
+ nd->nd_seq_out = nd->nd_seq_in;
+
+ nd->nd_unack = 0;
+ nd->nd_remain = 0;
+
+ nd->nd_tx_module = 0x10;
+ nd->nd_rx_module = 0x00;
+
+ for (i = 0, ch = nd->nd_chan; i < CHAN_MAX; i++, ch++) {
+ ch->ch_state = CS_IDLE;
+
+ ch->ch_otype = 0;
+ ch->ch_otype_waiting = 0;
+ }
+}
+
+/*
+ * Increase the number of channels, waking up any
+ * threads that might be waiting for the channels
+ * to appear.
+ */
+static void increase_channel_count(struct nd_struct *nd, int n)
+{
+ struct ch_struct *ch;
+ struct device *classp;
+ char name[DEVICE_NAME_SIZE];
+ int ret;
+ u8 *buf;
+ int i;
+
+ for (i = nd->nd_chan_count; i < n; ++i) {
+ ch = nd->nd_chan + i;
+
+ /* FIXME: return a useful error instead! */
+ buf = kmalloc(TBUF_MAX, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ if (ch->ch_tbuf)
+ pr_info_ratelimited("%s - ch_tbuf was not NULL\n",
+ __func__);
+
+ ch->ch_tbuf = buf;
+
+ buf = kmalloc(RBUF_MAX, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ if (ch->ch_rbuf)
+ pr_info("%s - ch_rbuf was not NULL\n",
+ __func__);
+ ch->ch_rbuf = buf;
+
+ classp = tty_port_register_device(&ch->port,
+ nd->nd_serial_ttdriver, i,
+ NULL);
+
+ ch->ch_tun.un_sysfs = classp;
+ snprintf(name, DEVICE_NAME_SIZE, "tty_%d", i);
+
+ dgrp_create_tty_sysfs(&ch->ch_tun, classp);
+ ret = sysfs_create_link(&nd->nd_class_dev->kobj,
+ &classp->kobj, name);
+
+ /* NOTE: We don't support "cu" devices anymore,
+ * so you will notice we don't register them
+ * here anymore. */
+ if (dgrp_register_prdevices) {
+ classp = tty_register_device(nd->nd_xprint_ttdriver,
+ i, NULL);
+ ch->ch_pun.un_sysfs = classp;
+ snprintf(name, DEVICE_NAME_SIZE, "pr_%d", i);
+
+ dgrp_create_tty_sysfs(&ch->ch_pun, classp);
+ ret = sysfs_create_link(&nd->nd_class_dev->kobj,
+ &classp->kobj, name);
+ }
+
+ nd->nd_chan_count = i + 1;
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+}
+
+/*
+ * Decrease the number of channels, and wake up any threads that might
+ * be waiting on the channels that vanished.
+ */
+static void decrease_channel_count(struct nd_struct *nd, int n)
+{
+ struct ch_struct *ch;
+ char name[DEVICE_NAME_SIZE];
+ int i;
+
+ for (i = nd->nd_chan_count - 1; i >= n; --i) {
+ ch = nd->nd_chan + i;
+
+ /*
+ * Make any open ports inoperative.
+ */
+ ch->ch_state = CS_IDLE;
+
+ ch->ch_otype = 0;
+ ch->ch_otype_waiting = 0;
+
+ /*
+ * Only "HANGUP" if we care about carrier
+ * transitions and we are already open.
+ */
+ if (ch->ch_open_count != 0) {
+ ch->ch_flag |= CH_HANGUP;
+ dgrp_carrier(ch);
+ }
+
+ /*
+ * Unlike the CH_HANGUP flag above, use another
+ * flag to indicate to the RealPort state machine
+ * that this port has disappeared.
+ */
+ if (ch->ch_open_count != 0)
+ ch->ch_flag |= CH_PORT_GONE;
+
+ wake_up_interruptible(&ch->ch_flag_wait);
+
+ nd->nd_chan_count = i;
+
+ kfree(ch->ch_tbuf);
+ ch->ch_tbuf = NULL;
+
+ kfree(ch->ch_rbuf);
+ ch->ch_rbuf = NULL;
+
+ nd->nd_chan_count = i;
+
+ dgrp_remove_tty_sysfs(ch->ch_tun.un_sysfs);
+ snprintf(name, DEVICE_NAME_SIZE, "tty_%d", i);
+ sysfs_remove_link(&nd->nd_class_dev->kobj, name);
+ tty_unregister_device(nd->nd_serial_ttdriver, i);
+
+ /*
+ * NOTE: We don't support "cu" devices anymore, so don't
+ * unregister them here anymore.
+ */
+
+ if (dgrp_register_prdevices) {
+ dgrp_remove_tty_sysfs(ch->ch_pun.un_sysfs);
+ snprintf(name, DEVICE_NAME_SIZE, "pr_%d", i);
+ sysfs_remove_link(&nd->nd_class_dev->kobj, name);
+ tty_unregister_device(nd->nd_xprint_ttdriver, i);
+ }
+ }
+}
+
+/**
+ * dgrp_chan_count() -- Adjust the node channel count.
+ * @nd: pointer to a node structure
+ * @n: new value for channel count
+ *
+ * Adjusts the node channel count. If new ports have appeared, it tries
+ * to signal those processes that might have been waiting for ports to
+ * appear. If ports have disappeared it tries to signal those processes
+ * that might be hung waiting for a response for the now non-existant port.
+ */
+static void dgrp_chan_count(struct nd_struct *nd, int n)
+{
+ if (n == nd->nd_chan_count)
+ return;
+
+ if (n > nd->nd_chan_count)
+ increase_channel_count(nd, n);
+
+ if (n < nd->nd_chan_count)
+ decrease_channel_count(nd, n);
+}
+
+/**
+ * dgrp_monitor() -- send data to the device monitor queue
+ * @nd: pointer to a node structure
+ * @buf: data to copy to the monitoring buffer
+ * @len: number of bytes to transfer to the buffer
+ *
+ * Called by the net device routines to send data to the device
+ * monitor queue. If the device monitor buffer is too full to
+ * accept the data, it waits until the buffer is ready.
+ */
+static void dgrp_monitor(struct nd_struct *nd, u8 *buf, int len)
+{
+ int n;
+ int r;
+ int rtn;
+
+ /*
+ * Grab monitor lock.
+ */
+ down(&nd->nd_mon_semaphore);
+
+ /*
+ * Loop while data remains.
+ */
+ while ((len > 0) && (nd->nd_mon_buf)) {
+ /*
+ * Determine the amount of available space left in the
+ * buffer. If there's none, wait until some appears.
+ */
+
+ n = (nd->nd_mon_out - nd->nd_mon_in - 1) & MON_MASK;
+
+ if (!n) {
+ nd->nd_mon_flag |= MON_WAIT_SPACE;
+
+ up(&nd->nd_mon_semaphore);
+
+ /*
+ * Go to sleep waiting until the condition becomes true.
+ */
+ rtn = wait_event_interruptible(nd->nd_mon_wqueue,
+ ((nd->nd_mon_flag & MON_WAIT_SPACE) == 0));
+
+/* FIXME: really ignore rtn? */
+
+ /*
+ * We can't exit here if we receive a signal, since
+ * to do so would trash the debug stream.
+ */
+
+ down(&nd->nd_mon_semaphore);
+
+ continue;
+ }
+
+ /*
+ * Copy as much data as will fit.
+ */
+
+ if (n > len)
+ n = len;
+
+ r = MON_MAX - nd->nd_mon_in;
+
+ if (r <= n) {
+ memcpy(nd->nd_mon_buf + nd->nd_mon_in, buf, r);
+
+ n -= r;
+
+ nd->nd_mon_in = 0;
+
+ buf += r;
+ len -= r;
+ }
+
+ memcpy(nd->nd_mon_buf + nd->nd_mon_in, buf, n);
+
+ nd->nd_mon_in += n;
+
+ buf += n;
+ len -= n;
+
+ if (nd->nd_mon_in >= MON_MAX)
+ pr_info_ratelimited("%s - nd_mon_in (%i) >= MON_MAX\n",
+ __func__, nd->nd_mon_in);
+
+ /*
+ * Wakeup any thread waiting for data
+ */
+
+ if (nd->nd_mon_flag & MON_WAIT_DATA) {
+ nd->nd_mon_flag &= ~MON_WAIT_DATA;
+ wake_up_interruptible(&nd->nd_mon_wqueue);
+ }
+ }
+
+ /*
+ * Release the monitor lock.
+ */
+ up(&nd->nd_mon_semaphore);
+}
+
+/**
+ * dgrp_encode_time() -- Encodes rpdump time into a 4-byte quantity.
+ * @nd: pointer to a node structure
+ * @buf: destination buffer
+ *
+ * Encodes "rpdump" time into a 4-byte quantity. Time is measured since
+ * open.
+ */
+static void dgrp_encode_time(struct nd_struct *nd, u8 *buf)
+{
+ ulong t;
+
+ /*
+ * Convert time in HZ since open to time in milliseconds
+ * since open.
+ */
+ t = jiffies - nd->nd_mon_lbolt;
+ t = 1000 * (t / HZ) + 1000 * (t % HZ) / HZ;
+
+ put_unaligned_be32((uint)(t & 0xffffffff), buf);
+}
+
+
+
+/**
+ * dgrp_monitor_message() -- Builds a rpdump style message.
+ * @nd: pointer to a node structure
+ * @message: destination buffer
+ */
+static void dgrp_monitor_message(struct nd_struct *nd, char *message)
+{
+ u8 header[7];
+ int n;
+
+ header[0] = RPDUMP_MESSAGE;
+
+ dgrp_encode_time(nd, header + 1);
+
+ n = strlen(message);
+
+ put_unaligned_be16(n, header + 5);
+
+ dgrp_monitor(nd, header, sizeof(header));
+ dgrp_monitor(nd, (u8 *) message, n);
+}
+
+
+
+/**
+ * dgrp_monitor_reset() -- Note a reset in the monitoring buffer.
+ * @nd: pointer to a node structure
+ */
+static void dgrp_monitor_reset(struct nd_struct *nd)
+{
+ u8 header[5];
+
+ header[0] = RPDUMP_RESET;
+
+ dgrp_encode_time(nd, header + 1);
+
+ dgrp_monitor(nd, header, sizeof(header));
+}
+
+/**
+ * dgrp_monitor_data() -- builds a monitor data packet
+ * @nd: pointer to a node structure
+ * @type: type of message to be logged
+ * @buf: data to be logged
+ * @size: number of bytes in the buffer
+ */
+static void dgrp_monitor_data(struct nd_struct *nd, u8 type, u8 *buf, int size)
+{
+ u8 header[7];
+
+ header[0] = type;
+
+ dgrp_encode_time(nd, header + 1);
+
+ put_unaligned_be16(size, header + 5);
+
+ dgrp_monitor(nd, header, sizeof(header));
+ dgrp_monitor(nd, buf, size);
+}
+
+static int alloc_nd_buffers(struct nd_struct *nd)
+{
+
+ nd->nd_iobuf = NULL;
+ nd->nd_writebuf = NULL;
+ nd->nd_inputbuf = NULL;
+ nd->nd_inputflagbuf = NULL;
+
+ /*
+ * Allocate the network read/write buffer.
+ */
+ nd->nd_iobuf = kzalloc(UIO_MAX + 10, GFP_KERNEL);
+ if (!nd->nd_iobuf)
+ goto out_err;
+
+ /*
+ * Allocate a buffer for doing the copy from user space to
+ * kernel space in the write routines.
+ */
+ nd->nd_writebuf = kzalloc(WRITEBUFLEN, GFP_KERNEL);
+ if (!nd->nd_writebuf)
+ goto out_err;
+
+ /*
+ * Allocate a buffer for doing the copy from kernel space to
+ * tty buffer space in the read routines.
+ */
+ nd->nd_inputbuf = kzalloc(MYFLIPLEN, GFP_KERNEL);
+ if (!nd->nd_inputbuf)
+ goto out_err;
+
+ /*
+ * Allocate a buffer for doing the copy from kernel space to
+ * tty buffer space in the read routines.
+ */
+ nd->nd_inputflagbuf = kzalloc(MYFLIPLEN, GFP_KERNEL);
+ if (!nd->nd_inputflagbuf)
+ goto out_err;
+
+ return 0;
+
+out_err:
+ kfree(nd->nd_iobuf);
+ kfree(nd->nd_writebuf);
+ kfree(nd->nd_inputbuf);
+ kfree(nd->nd_inputflagbuf);
+ return -ENOMEM;
+}
+
+/*
+ * dgrp_net_open() -- Open the NET device for a particular PortServer
+ */
+static int dgrp_net_open(struct inode *inode, struct file *file)
+{
+ struct nd_struct *nd;
+ struct proc_dir_entry *de;
+ ulong lock_flags;
+ int rtn;
+
+ rtn = try_module_get(THIS_MODULE);
+ if (!rtn)
+ return -EAGAIN;
+
+ if (!capable(CAP_SYS_ADMIN)) {
+ rtn = -EPERM;
+ goto done;
+ }
+
+ /*
+ * Make sure that the "private_data" field hasn't already been used.
+ */
+ if (file->private_data) {
+ rtn = -EINVAL;
+ goto done;
+ }
+
+ /*
+ * Get the node pointer, and fail if it doesn't exist.
+ */
+ de = PDE(inode);
+ if (!de) {
+ rtn = -ENXIO;
+ goto done;
+ }
+
+ nd = (struct nd_struct *) de->data;
+ if (!nd) {
+ rtn = -ENXIO;
+ goto done;
+ }
+
+ file->private_data = (void *) nd;
+
+ /*
+ * Grab the NET lock.
+ */
+ down(&nd->nd_net_semaphore);
+
+ if (nd->nd_state != NS_CLOSED) {
+ rtn = -EBUSY;
+ goto unlock;
+ }
+
+ /*
+ * Initialize the link speed parameters.
+ */
+
+ nd->nd_link.lk_fast_rate = UIO_MAX;
+ nd->nd_link.lk_slow_rate = UIO_MAX;
+
+ nd->nd_link.lk_fast_delay = 1000;
+ nd->nd_link.lk_slow_delay = 1000;
+
+ nd->nd_link.lk_header_size = 46;
+
+
+ rtn = alloc_nd_buffers(nd);
+ if (rtn)
+ goto unlock;
+
+ /*
+ * The port is now open, so move it to the IDLE state
+ */
+ dgrp_net_idle(nd);
+
+ nd->nd_tx_time = jiffies;
+
+ /*
+ * If the polling routing is not running, start it running here
+ */
+ spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags);
+
+ if (!dgrp_poll_data.node_active_count) {
+ dgrp_poll_data.node_active_count = 2;
+ dgrp_poll_data.timer.expires = jiffies +
+ dgrp_poll_tick * HZ / 1000;
+ add_timer(&dgrp_poll_data.timer);
+ }
+
+ spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags);
+
+ dgrp_monitor_message(nd, "Net Open");
+
+unlock:
+ /*
+ * Release the NET lock.
+ */
+ up(&nd->nd_net_semaphore);
+
+done:
+ if (rtn)
+ module_put(THIS_MODULE);
+
+ return rtn;
+}
+
+/* dgrp_net_release() -- close the NET device for a particular PortServer */
+static int dgrp_net_release(struct inode *inode, struct file *file)
+{
+ struct nd_struct *nd;
+ ulong lock_flags;
+
+ nd = (struct nd_struct *)(file->private_data);
+ if (!nd)
+ goto done;
+
+/* TODO : historical locking placeholder */
+/*
+ * In the HPUX version of the RealPort driver (which served as a basis
+ * for this driver) this locking code was used. Saved if ever we need
+ * to review the locking under Linux.
+ */
+/* spinlock(&nd->nd_lock); */
+
+
+ /*
+ * Grab the NET lock.
+ */
+ down(&nd->nd_net_semaphore);
+
+ /*
+ * Before "closing" the internal connection, make sure all
+ * ports are "idle".
+ */
+ dgrp_net_idle(nd);
+
+ nd->nd_state = NS_CLOSED;
+ nd->nd_flag = 0;
+
+ /*
+ * TODO ... must the wait queue be reset on close?
+ * should any pending waiters be reset?
+ * Let's decide to assert that the waitq is empty... and see
+ * how soon we break.
+ */
+ if (waitqueue_active(&nd->nd_tx_waitq))
+ pr_info("%s - expected waitqueue_active to be false\n",
+ __func__);
+
+ nd->nd_send = 0;
+
+ kfree(nd->nd_iobuf);
+ nd->nd_iobuf = NULL;
+
+/* TODO : historical locking placeholder */
+/*
+ * In the HPUX version of the RealPort driver (which served as a basis
+ * for this driver) this locking code was used. Saved if ever we need
+ * to review the locking under Linux.
+ */
+/* spinunlock( &nd->nd_lock ); */
+
+
+ kfree(nd->nd_writebuf);
+ nd->nd_writebuf = NULL;
+
+ kfree(nd->nd_inputbuf);
+ nd->nd_inputbuf = NULL;
+
+ kfree(nd->nd_inputflagbuf);
+ nd->nd_inputflagbuf = NULL;
+
+/* TODO : historical locking placeholder */
+/*
+ * In the HPUX version of the RealPort driver (which served as a basis
+ * for this driver) this locking code was used. Saved if ever we need
+ * to review the locking under Linux.
+ */
+/* spinlock(&nd->nd_lock); */
+
+ /*
+ * Set the active port count to zero.
+ */
+ dgrp_chan_count(nd, 0);
+
+/* TODO : historical locking placeholder */
+/*
+ * In the HPUX version of the RealPort driver (which served as a basis
+ * for this driver) this locking code was used. Saved if ever we need
+ * to review the locking under Linux.
+ */
+/* spinunlock(&nd->nd_lock); */
+
+ /*
+ * Release the NET lock.
+ */
+ up(&nd->nd_net_semaphore);
+
+ /*
+ * Cause the poller to stop scheduling itself if this is
+ * the last active node.
+ */
+ spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags);
+
+ if (dgrp_poll_data.node_active_count == 2) {
+ del_timer(&dgrp_poll_data.timer);
+ dgrp_poll_data.node_active_count = 0;
+ }
+
+ spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags);
+
+done:
+ down(&nd->nd_net_semaphore);
+
+ dgrp_monitor_message(nd, "Net Close");
+
+ up(&nd->nd_net_semaphore);
+
+ module_put(THIS_MODULE);
+ file->private_data = NULL;
+ return 0;
+}
+
+/* used in dgrp_send to setup command header */
+static inline u8 *set_cmd_header(u8 *b, u8 port, u8 cmd)
+{
+ *b++ = 0xb0 + (port & 0x0f);
+ *b++ = cmd;
+ return b;
+}
+
+/**
+ * dgrp_send() -- build a packet for transmission to the server
+ * @nd: pointer to a node structure
+ * @tmax: maximum bytes to transmit
+ *
+ * returns number of bytes sent
+ */
+static int dgrp_send(struct nd_struct *nd, long tmax)
+{
+ struct ch_struct *ch = nd->nd_chan;
+ u8 *b;
+ u8 *buf;
+ u8 *mbuf;
+ u8 port;
+ int mod;
+ long send;
+ int maxport;
+ long lastport = -1;
+ ushort rwin;
+ long in;
+ ushort n;
+ long t;
+ long ttotal;
+ long tchan;
+ long tsend;
+ ushort tsafe;
+ long work;
+ long send_sync;
+ long wanted_sync_port = -1;
+ ushort tdata[CHAN_MAX];
+ long used_buffer;
+
+ mbuf = nd->nd_iobuf + UIO_BASE;
+ buf = b = mbuf;
+
+ send_sync = nd->nd_link.lk_slow_rate < UIO_MAX;
+
+ ttotal = 0;
+ tchan = 0;
+
+ memset(tdata, 0, sizeof(tdata));
+
+
+ /*
+ * If there are any outstanding requests to be serviced,
+ * service them here.
+ */
+ if (nd->nd_send & NR_PASSWORD) {
+
+ /*
+ * Send Password response.
+ */
+
+ b[0] = 0xfc;
+ b[1] = 0x20;
+ put_unaligned_be16(strlen(nd->password), b + 2);
+ b += 4;
+ b += strlen(nd->password);
+ nd->nd_send &= ~(NR_PASSWORD);
+ }
+
+
+ /*
+ * Loop over all modules to generate commands, and determine
+ * the amount of data queued for transmit.
+ */
+
+ for (mod = 0, port = 0; port < nd->nd_chan_count; mod++) {
+ /*
+ * If this is not the current module, enter a module select
+ * code in the buffer.
+ */
+
+ if (mod != nd->nd_tx_module)
+ mbuf = ++b;
+
+ /*
+ * Loop to process one module.
+ */
+
+ maxport = port + 16;
+
+ if (maxport > nd->nd_chan_count)
+ maxport = nd->nd_chan_count;
+
+ for (; port < maxport; port++, ch++) {
+ /*
+ * Switch based on channel state.
+ */
+
+ switch (ch->ch_state) {
+ /*
+ * Send requests when the port is closed, and there
+ * are no Open, Close or Cancel requests expected.
+ */
+
+ case CS_IDLE:
+ /*
+ * Wait until any open error code
+ * has been delivered to all
+ * associated ports.
+ */
+
+ if (ch->ch_open_error) {
+ if (ch->ch_wait_count[ch->ch_otype]) {
+ work = 1;
+ break;
+ }
+
+ ch->ch_open_error = 0;
+ }
+
+ /*
+ * Wait until the channel HANGUP flag is reset
+ * before sending the first open. We can only
+ * get to this state after a server disconnect.
+ */
+
+ if ((ch->ch_flag & CH_HANGUP) != 0)
+ break;
+
+ /*
+ * If recovering from a TCP disconnect, or if
+ * there is an immediate open pending, send an
+ * Immediate Open request.
+ */
+ if ((ch->ch_flag & CH_PORT_GONE) ||
+ ch->ch_wait_count[OTYPE_IMMEDIATE] != 0) {
+ b = set_cmd_header(b, port, 10);
+ *b++ = 0;
+
+ ch->ch_state = CS_WAIT_OPEN;
+ ch->ch_otype = OTYPE_IMMEDIATE;
+ break;
+ }
+
+ /*
+ * If there is no Persistent or Incoming Open on the wait
+ * list in the server, and a thread is waiting for a
+ * Persistent or Incoming Open, send a Persistent or Incoming
+ * Open Request.
+ */
+ if (ch->ch_otype_waiting == 0) {
+ if (ch->ch_wait_count[OTYPE_PERSISTENT] != 0) {
+ b = set_cmd_header(b, port, 10);
+ *b++ = 1;
+
+ ch->ch_state = CS_WAIT_OPEN;
+ ch->ch_otype = OTYPE_PERSISTENT;
+ } else if (ch->ch_wait_count[OTYPE_INCOMING] != 0) {
+ b = set_cmd_header(b, port, 10);
+ *b++ = 2;
+
+ ch->ch_state = CS_WAIT_OPEN;
+ ch->ch_otype = OTYPE_INCOMING;
+ }
+ break;
+ }
+
+ /*
+ * If a Persistent or Incoming Open is pending in
+ * the server, but there is no longer an open
+ * thread waiting for it, cancel the request.
+ */
+
+ if (ch->ch_wait_count[ch->ch_otype_waiting] == 0) {
+ b = set_cmd_header(b, port, 10);
+ *b++ = 4;
+
+ ch->ch_state = CS_WAIT_CANCEL;
+ ch->ch_otype = ch->ch_otype_waiting;
+ }
+ break;
+
+ /*
+ * Send port parameter queries.
+ */
+ case CS_SEND_QUERY:
+ /*
+ * Clear out all FEP state that might remain
+ * from the last connection.
+ */
+
+ ch->ch_flag |= CH_PARAM;
+
+ ch->ch_flag &= ~CH_RX_FLUSH;
+
+ ch->ch_expect = 0;
+
+ ch->ch_s_tin = 0;
+ ch->ch_s_tpos = 0;
+ ch->ch_s_tsize = 0;
+ ch->ch_s_treq = 0;
+ ch->ch_s_elast = 0;
+
+ ch->ch_s_rin = 0;
+ ch->ch_s_rwin = 0;
+ ch->ch_s_rsize = 0;
+
+ ch->ch_s_tmax = 0;
+ ch->ch_s_ttime = 0;
+ ch->ch_s_rmax = 0;
+ ch->ch_s_rtime = 0;
+ ch->ch_s_rlow = 0;
+ ch->ch_s_rhigh = 0;
+
+ ch->ch_s_brate = 0;
+ ch->ch_s_iflag = 0;
+ ch->ch_s_cflag = 0;
+ ch->ch_s_oflag = 0;
+ ch->ch_s_xflag = 0;
+
+ ch->ch_s_mout = 0;
+ ch->ch_s_mflow = 0;
+ ch->ch_s_mctrl = 0;
+ ch->ch_s_xon = 0;
+ ch->ch_s_xoff = 0;
+ ch->ch_s_lnext = 0;
+ ch->ch_s_xxon = 0;
+ ch->ch_s_xxoff = 0;
+
+ /* Send Sequence Request */
+ b = set_cmd_header(b, port, 14);
+
+ /* Configure Event Conditions Packet */
+ b = set_cmd_header(b, port, 42);
+ put_unaligned_be16(0x02c0, b);
+ b += 2;
+ *b++ = (DM_DTR | DM_RTS | DM_CTS |
+ DM_DSR | DM_RI | DM_CD);
+
+ /* Send Status Request */
+ b = set_cmd_header(b, port, 16);
+
+ /* Send Buffer Request */
+ b = set_cmd_header(b, port, 20);
+
+ /* Send Port Capability Request */
+ b = set_cmd_header(b, port, 22);
+
+ ch->ch_expect = (RR_SEQUENCE |
+ RR_STATUS |
+ RR_BUFFER |
+ RR_CAPABILITY);
+
+ ch->ch_state = CS_WAIT_QUERY;
+
+ /* Raise modem signals */
+ b = set_cmd_header(b, port, 44);
+
+ if (ch->ch_flag & CH_PORT_GONE)
+ ch->ch_s_mout = ch->ch_mout;
+ else
+ ch->ch_s_mout = ch->ch_mout = DM_DTR | DM_RTS;
+
+ *b++ = ch->ch_mout;
+ *b++ = ch->ch_s_mflow = 0;
+ *b++ = ch->ch_s_mctrl = ch->ch_mctrl = 0;
+
+ if (ch->ch_flag & CH_PORT_GONE)
+ ch->ch_flag &= ~CH_PORT_GONE;
+
+ break;
+
+ /*
+ * Handle normal open and ready mode.
+ */
+
+ case CS_READY:
+
+ /*
+ * If the port is not open, and there are no
+ * no longer any ports requesting an open,
+ * then close the port.
+ */
+
+ if (ch->ch_open_count == 0 &&
+ ch->ch_wait_count[ch->ch_otype] == 0) {
+ goto send_close;
+ }
+
+ /*
+ * Process waiting input.
+ *
+ * If there is no one to read it, discard the data.
+ *
+ * Otherwise if we are not in fastcook mode, or if there is a
+ * fastcook thread waiting for data, send the data to the
+ * line discipline.
+ */
+ if (ch->ch_rin != ch->ch_rout) {
+ if (ch->ch_tun.un_open_count == 0 ||
+ (ch->ch_tun.un_flag & UN_CLOSING) ||
+ (ch->ch_cflag & CF_CREAD) == 0) {
+ ch->ch_rout = ch->ch_rin;
+ } else if ((ch->ch_flag & CH_FAST_READ) == 0 ||
+ ch->ch_inwait != 0) {
+ dgrp_input(ch);
+
+ if (ch->ch_rin != ch->ch_rout)
+ work = 1;
+ }
+ }
+
+ /*
+ * Handle receive flush, and changes to
+ * server port parameters.
+ */
+
+ if (ch->ch_flag & (CH_RX_FLUSH | CH_PARAM)) {
+ /*
+ * If we are in receive flush mode,
+ * and enough data has gone by, reset
+ * receive flush mode.
+ */
+ if (ch->ch_flag & CH_RX_FLUSH) {
+ if (((ch->ch_flush_seq - nd->nd_seq_out) & SEQ_MASK) >
+ ((nd->nd_seq_in - nd->nd_seq_out) & SEQ_MASK))
+ ch->ch_flag &= ~CH_RX_FLUSH;
+ else
+ work = 1;
+ }
+
+ /*
+ * Send TMAX, TTIME.
+ */
+
+ if (ch->ch_s_tmax != ch->ch_tmax ||
+ ch->ch_s_ttime != ch->ch_ttime) {
+ b = set_cmd_header(b, port, 48);
+
+ ch->ch_s_tmax = ch->ch_tmax;
+ ch->ch_s_ttime = ch->ch_ttime;
+
+ put_unaligned_be16(ch->ch_s_tmax,
+ b);
+ b += 2;
+
+ put_unaligned_be16(ch->ch_s_ttime,
+ b);
+ b += 2;
+ }
+
+ /*
+ * Send RLOW, RHIGH.
+ */
+
+ if (ch->ch_s_rlow != ch->ch_rlow ||
+ ch->ch_s_rhigh != ch->ch_rhigh) {
+ b = set_cmd_header(b, port, 45);
+
+ ch->ch_s_rlow = ch->ch_rlow;
+ ch->ch_s_rhigh = ch->ch_rhigh;
+
+ put_unaligned_be16(ch->ch_s_rlow,
+ b);
+ b += 2;
+
+ put_unaligned_be16(ch->ch_s_rhigh,
+ b);
+ b += 2;
+ }
+
+ /*
+ * Send BRATE, CFLAG, IFLAG,
+ * OFLAG, XFLAG.
+ */
+
+ if (ch->ch_s_brate != ch->ch_brate ||
+ ch->ch_s_cflag != ch->ch_cflag ||
+ ch->ch_s_iflag != ch->ch_iflag ||
+ ch->ch_s_oflag != ch->ch_oflag ||
+ ch->ch_s_xflag != ch->ch_xflag) {
+ b = set_cmd_header(b, port, 40);
+
+ ch->ch_s_brate = ch->ch_brate;
+ ch->ch_s_cflag = ch->ch_cflag;
+ ch->ch_s_iflag = ch->ch_iflag;
+ ch->ch_s_oflag = ch->ch_oflag;
+ ch->ch_s_xflag = ch->ch_xflag;
+
+ put_unaligned_be16(ch->ch_s_brate,
+ b);
+ b += 2;
+
+ put_unaligned_be16(ch->ch_s_cflag,
+ b);
+ b += 2;
+
+ put_unaligned_be16(ch->ch_s_iflag,
+ b);
+ b += 2;
+
+ put_unaligned_be16(ch->ch_s_oflag,
+ b);
+ b += 2;
+
+ put_unaligned_be16(ch->ch_s_xflag,
+ b);
+ b += 2;
+ }
+
+ /*
+ * Send MOUT, MFLOW, MCTRL.
+ */
+
+ if (ch->ch_s_mout != ch->ch_mout ||
+ ch->ch_s_mflow != ch->ch_mflow ||
+ ch->ch_s_mctrl != ch->ch_mctrl) {
+ b = set_cmd_header(b, port, 44);
+
+ *b++ = ch->ch_s_mout = ch->ch_mout;
+ *b++ = ch->ch_s_mflow = ch->ch_mflow;
+ *b++ = ch->ch_s_mctrl = ch->ch_mctrl;
+ }
+
+ /*
+ * Send Flow control characters.
+ */
+
+ if (ch->ch_s_xon != ch->ch_xon ||
+ ch->ch_s_xoff != ch->ch_xoff ||
+ ch->ch_s_lnext != ch->ch_lnext ||
+ ch->ch_s_xxon != ch->ch_xxon ||
+ ch->ch_s_xxoff != ch->ch_xxoff) {
+ b = set_cmd_header(b, port, 46);
+
+ *b++ = ch->ch_s_xon = ch->ch_xon;
+ *b++ = ch->ch_s_xoff = ch->ch_xoff;
+ *b++ = ch->ch_s_lnext = ch->ch_lnext;
+ *b++ = ch->ch_s_xxon = ch->ch_xxon;
+ *b++ = ch->ch_s_xxoff = ch->ch_xxoff;
+ }
+
+ /*
+ * Send RMAX, RTIME.
+ */
+
+ if (ch->ch_s_rmax != ch->ch_rmax ||
+ ch->ch_s_rtime != ch->ch_rtime) {
+ b = set_cmd_header(b, port, 47);
+
+ ch->ch_s_rmax = ch->ch_rmax;
+ ch->ch_s_rtime = ch->ch_rtime;
+
+ put_unaligned_be16(ch->ch_s_rmax,
+ b);
+ b += 2;
+
+ put_unaligned_be16(ch->ch_s_rtime,
+ b);
+ b += 2;
+ }
+
+ ch->ch_flag &= ~CH_PARAM;
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+
+
+ /*
+ * Handle action commands.
+ */
+
+ if (ch->ch_send != 0) {
+ /* int send = ch->ch_send & ~ch->ch_expect; */
+ send = ch->ch_send & ~ch->ch_expect;
+
+ /* Send character immediate */
+ if ((send & RR_TX_ICHAR) != 0) {
+ b = set_cmd_header(b, port, 60);
+
+ *b++ = ch->ch_xon;
+ ch->ch_expect |= RR_TX_ICHAR;
+ }
+
+ /* BREAK request */
+ if ((send & RR_TX_BREAK) != 0) {
+ if (ch->ch_break_time != 0) {
+ b = set_cmd_header(b, port, 61);
+ put_unaligned_be16(ch->ch_break_time,
+ b);
+ b += 2;
+
+ ch->ch_expect |= RR_TX_BREAK;
+ ch->ch_break_time = 0;
+ } else {
+ ch->ch_send &= ~RR_TX_BREAK;
+ ch->ch_flag &= ~CH_TX_BREAK;
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+ }
+
+ /*
+ * Flush input/output buffers.
+ */
+
+ if ((send & (RR_RX_FLUSH | RR_TX_FLUSH)) != 0) {
+ b = set_cmd_header(b, port, 62);
+
+ *b++ = ((send & RR_TX_FLUSH) == 0 ? 1 :
+ (send & RR_RX_FLUSH) == 0 ? 2 : 3);
+
+ if (send & RR_RX_FLUSH) {
+ ch->ch_flush_seq = nd->nd_seq_in;
+ ch->ch_flag |= CH_RX_FLUSH;
+ work = 1;
+ send_sync = 1;
+ wanted_sync_port = port;
+ }
+
+ ch->ch_send &= ~(RR_RX_FLUSH | RR_TX_FLUSH);
+ }
+
+ /* Pause input/output */
+ if ((send & (RR_RX_STOP | RR_TX_STOP)) != 0) {
+ b = set_cmd_header(b, port, 63);
+ *b = 0;
+
+ if ((send & RR_TX_STOP) != 0)
+ *b |= EV_OPU;
+
+ if ((send & RR_RX_STOP) != 0)
+ *b |= EV_IPU;
+
+ b++;
+
+ ch->ch_send &= ~(RR_RX_STOP | RR_TX_STOP);
+ }
+
+ /* Start input/output */
+ if ((send & (RR_RX_START | RR_TX_START)) != 0) {
+ b = set_cmd_header(b, port, 64);
+ *b = 0;
+
+ if ((send & RR_TX_START) != 0)
+ *b |= EV_OPU | EV_OPS | EV_OPX;
+
+ if ((send & RR_RX_START) != 0)
+ *b |= EV_IPU | EV_IPS;
+
+ b++;
+
+ ch->ch_send &= ~(RR_RX_START | RR_TX_START);
+ }
+ }
+
+
+ /*
+ * Send a window sequence to acknowledge received data.
+ */
+
+ rwin = (ch->ch_s_rin +
+ ((ch->ch_rout - ch->ch_rin - 1) & RBUF_MASK));
+
+ n = (rwin - ch->ch_s_rwin) & 0xffff;
+
+ if (n >= RBUF_MAX / 4) {
+ b[0] = 0xa0 + (port & 0xf);
+ ch->ch_s_rwin = rwin;
+ put_unaligned_be16(rwin, b + 1);
+ b += 3;
+ }
+
+ /*
+ * If the terminal is waiting on LOW
+ * water or EMPTY, and the condition
+ * is now satisfied, call the line
+ * discipline to put more data in the
+ * buffer.
+ */
+
+ n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+
+ if ((ch->ch_tun.un_flag & (UN_EMPTY|UN_LOW)) != 0) {
+ if ((ch->ch_tun.un_flag & UN_LOW) != 0 ?
+ (n <= TBUF_LOW) :
+ (n == 0 && ch->ch_s_tpos == ch->ch_s_tin)) {
+ ch->ch_tun.un_flag &= ~(UN_EMPTY|UN_LOW);
+
+ if (waitqueue_active(&((ch->ch_tun.un_tty)->write_wait)))
+ wake_up_interruptible(&((ch->ch_tun.un_tty)->write_wait));
+ tty_wakeup(ch->ch_tun.un_tty);
+ n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+ }
+ }
+
+ /*
+ * If the printer is waiting on LOW
+ * water, TIME, EMPTY or PWAIT, and is
+ * now ready to put more data in the
+ * buffer, call the line discipline to
+ * do the job.
+ */
+
+ if (ch->ch_pun.un_open_count &&
+ (ch->ch_pun.un_flag &
+ (UN_EMPTY|UN_TIME|UN_LOW|UN_PWAIT)) != 0) {
+
+ if ((ch->ch_pun.un_flag & UN_LOW) != 0 ?
+ (n <= TBUF_LOW) :
+ (ch->ch_pun.un_flag & UN_TIME) != 0 ?
+ ((jiffies - ch->ch_waketime) >= 0) :
+ (n == 0 && ch->ch_s_tpos == ch->ch_s_tin) &&
+ ((ch->ch_pun.un_flag & UN_EMPTY) != 0 ||
+ ((ch->ch_tun.un_open_count &&
+ ch->ch_tun.un_tty->ops->chars_in_buffer) ?
+ (ch->ch_tun.un_tty->ops->chars_in_buffer)(ch->ch_tun.un_tty) == 0
+ : 1
+ )
+ )) {
+ ch->ch_pun.un_flag &= ~(UN_EMPTY | UN_TIME | UN_LOW | UN_PWAIT);
+
+ if (waitqueue_active(&((ch->ch_pun.un_tty)->write_wait)))
+ wake_up_interruptible(&((ch->ch_pun.un_tty)->write_wait));
+ tty_wakeup(ch->ch_pun.un_tty);
+ n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+
+ } else if ((ch->ch_pun.un_flag & UN_TIME) != 0) {
+ work = 1;
+ }
+ }
+
+
+ /*
+ * Determine the max number of bytes
+ * this port can send, including
+ * packet header overhead.
+ */
+
+ t = ((ch->ch_s_tsize + ch->ch_s_tpos - ch->ch_s_tin) & 0xffff);
+
+ if (n > t)
+ n = t;
+
+ if (n != 0) {
+ n += (n <= 8 ? 1 : n <= 255 ? 2 : 3);
+
+ tdata[tchan++] = n;
+ ttotal += n;
+ }
+ break;
+
+ /*
+ * Close the port.
+ */
+
+send_close:
+ case CS_SEND_CLOSE:
+ b = set_cmd_header(b, port, 10);
+ if (ch->ch_otype == OTYPE_IMMEDIATE)
+ *b++ = 3;
+ else
+ *b++ = 4;
+
+ ch->ch_state = CS_WAIT_CLOSE;
+ break;
+
+ /*
+ * Wait for a previous server request.
+ */
+
+ case CS_WAIT_OPEN:
+ case CS_WAIT_CANCEL:
+ case CS_WAIT_FAIL:
+ case CS_WAIT_QUERY:
+ case CS_WAIT_CLOSE:
+ break;
+
+ default:
+ pr_info("%s - unexpected channel state (%i)\n",
+ __func__, ch->ch_state);
+ }
+ }
+
+ /*
+ * If a module select code is needed, drop one in. If space
+ * was reserved for one, but none is needed, recover the space.
+ */
+
+ if (mod != nd->nd_tx_module) {
+ if (b != mbuf) {
+ mbuf[-1] = 0xf0 | mod;
+ nd->nd_tx_module = mod;
+ } else {
+ b--;
+ }
+ }
+ }
+
+ /*
+ * Adjust "tmax" so that under worst case conditions we do
+ * not overflow either the daemon buffer or the internal
+ * buffer in the loop that follows. Leave a safe area
+ * of 64 bytes so we start getting asserts before we start
+ * losing data or clobbering memory.
+ */
+
+ n = UIO_MAX - UIO_BASE;
+
+ if (tmax > n)
+ tmax = n;
+
+ tmax -= 64;
+
+ tsafe = tmax;
+
+ /*
+ * Allocate space for 5 Module Selects, 1 Sequence Request,
+ * and 1 Set TREQ for each active channel.
+ */
+
+ tmax -= 5 + 3 + 4 * nd->nd_chan_count;
+
+ /*
+ * Further reduce "tmax" to the available transmit credit.
+ * Note that this is a soft constraint; The transmit credit
+ * can go negative for a time and then recover.
+ */
+
+ n = nd->nd_tx_deposit - nd->nd_tx_charge - nd->nd_link.lk_header_size;
+
+ if (tmax > n)
+ tmax = n;
+
+ /*
+ * Finally reduce tmax by the number of bytes already in
+ * the buffer.
+ */
+
+ tmax -= b - buf;
+
+ /*
+ * Suspend data transmit unless every ready channel can send
+ * at least 1 character.
+ */
+ if (tmax < 2 * nd->nd_chan_count) {
+ tsend = 1;
+
+ } else if (tchan > 1 && ttotal > tmax) {
+
+ /*
+ * If transmit is limited by the credit budget, find the
+ * largest number of characters we can send without driving
+ * the credit negative.
+ */
+
+ long tm = tmax;
+ int tc = tchan;
+ int try;
+
+ tsend = tm / tc;
+
+ for (try = 0; try < 3; try++) {
+ int i;
+ int c = 0;
+
+ for (i = 0; i < tc; i++) {
+ if (tsend < tdata[i])
+ tdata[c++] = tdata[i];
+ else
+ tm -= tdata[i];
+ }
+
+ if (c == tc)
+ break;
+
+ tsend = tm / c;
+
+ if (c == 1)
+ break;
+
+ tc = c;
+ }
+
+ tsend = tm / nd->nd_chan_count;
+
+ if (tsend < 2)
+ tsend = 1;
+
+ } else {
+ /*
+ * If no budgetary constraints, or only one channel ready
+ * to send, set the character limit to the remaining
+ * buffer size.
+ */
+
+ tsend = tmax;
+ }
+
+ tsend -= (tsend <= 9) ? 1 : (tsend <= 257) ? 2 : 3;
+
+ /*
+ * Loop over all channels, sending queued data.
+ */
+
+ port = 0;
+ ch = nd->nd_chan;
+ used_buffer = tmax;
+
+ for (mod = 0; port < nd->nd_chan_count; mod++) {
+ /*
+ * If this is not the current module, enter a module select
+ * code in the buffer.
+ */
+
+ if (mod != nd->nd_tx_module)
+ mbuf = ++b;
+
+ /*
+ * Loop to process one module.
+ */
+
+ maxport = port + 16;
+
+ if (maxport > nd->nd_chan_count)
+ maxport = nd->nd_chan_count;
+
+ for (; port < maxport; port++, ch++) {
+ if (ch->ch_state != CS_READY)
+ continue;
+
+ lastport = port;
+
+ n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+
+ /*
+ * If there is data that can be sent, send it.
+ */
+
+ if (n != 0 && used_buffer > 0) {
+ t = (ch->ch_s_tsize + ch->ch_s_tpos - ch->ch_s_tin) & 0xffff;
+
+ if (n > t)
+ n = t;
+
+ if (n > tsend) {
+ work = 1;
+ n = tsend;
+ }
+
+ if (n > used_buffer) {
+ work = 1;
+ n = used_buffer;
+ }
+
+ if (n <= 0)
+ continue;
+
+ /*
+ * Create the correct size transmit header,
+ * depending on the amount of data to transmit.
+ */
+
+ if (n <= 8) {
+
+ b[0] = ((n - 1) << 4) + (port & 0xf);
+ b += 1;
+
+ } else if (n <= 255) {
+
+ b[0] = 0x80 + (port & 0xf);
+ b[1] = n;
+ b += 2;
+
+ } else {
+
+ b[0] = 0x90 + (port & 0xf);
+ put_unaligned_be16(n, b + 1);
+ b += 3;
+ }
+
+ ch->ch_s_tin = (ch->ch_s_tin + n) & 0xffff;
+
+ /*
+ * Copy transmit data to the packet.
+ */
+
+ t = TBUF_MAX - ch->ch_tout;
+
+ if (n >= t) {
+ memcpy(b, ch->ch_tbuf + ch->ch_tout, t);
+ b += t;
+ n -= t;
+ used_buffer -= t;
+ ch->ch_tout = 0;
+ }
+
+ memcpy(b, ch->ch_tbuf + ch->ch_tout, n);
+ b += n;
+ used_buffer -= n;
+ ch->ch_tout += n;
+ n = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+ }
+
+ /*
+ * Wake any terminal unit process waiting in the
+ * dgrp_write routine for low water.
+ */
+
+ if (n > TBUF_LOW)
+ continue;
+
+ if ((ch->ch_flag & CH_LOW) != 0) {
+ ch->ch_flag &= ~CH_LOW;
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+
+ /* selwakeup tty_sel */
+ if (ch->ch_tun.un_open_count) {
+ struct tty_struct *tty = (ch->ch_tun.un_tty);
+
+ if (waitqueue_active(&tty->write_wait))
+ wake_up_interruptible(&tty->write_wait);
+
+ tty_wakeup(tty);
+ }
+
+ if (ch->ch_pun.un_open_count) {
+ struct tty_struct *tty = (ch->ch_pun.un_tty);
+
+ if (waitqueue_active(&tty->write_wait))
+ wake_up_interruptible(&tty->write_wait);
+
+ tty_wakeup(tty);
+ }
+
+ /*
+ * Do EMPTY processing.
+ */
+
+ if (n != 0)
+ continue;
+
+ if ((ch->ch_flag & (CH_EMPTY | CH_DRAIN)) != 0 ||
+ (ch->ch_pun.un_flag & UN_EMPTY) != 0) {
+ /*
+ * If there is still data in the server, ask the server
+ * to notify us when its all gone.
+ */
+
+ if (ch->ch_s_treq != ch->ch_s_tin) {
+ b = set_cmd_header(b, port, 43);
+
+ ch->ch_s_treq = ch->ch_s_tin;
+ put_unaligned_be16(ch->ch_s_treq,
+ b);
+ b += 2;
+ }
+
+ /*
+ * If there is a thread waiting for buffer empty,
+ * and we are truly empty, wake the thread.
+ */
+
+ else if ((ch->ch_flag & CH_EMPTY) != 0 &&
+ (ch->ch_send & RR_TX_BREAK) == 0) {
+ ch->ch_flag &= ~CH_EMPTY;
+
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+ }
+ }
+
+ /*
+ * If a module select code is needed, drop one in. If space
+ * was reserved for one, but none is needed, recover the space.
+ */
+
+ if (mod != nd->nd_tx_module) {
+ if (b != mbuf) {
+ mbuf[-1] = 0xf0 | mod;
+ nd->nd_tx_module = mod;
+ } else {
+ b--;
+ }
+ }
+ }
+
+ /*
+ * Send a synchronization sequence associated with the last open
+ * channel that sent data, and remember the time when the data was
+ * sent.
+ */
+
+ in = nd->nd_seq_in;
+
+ if ((send_sync || nd->nd_seq_wait[in] != 0) && lastport >= 0) {
+ u8 *bb = b;
+
+ /*
+ * Attempt the use the port that really wanted the sync.
+ * This gets around a race condition where the "lastport" is in
+ * the middle of the close() routine, and by the time we
+ * send this command, it will have already acked the close, and
+ * thus not send the sync response.
+ */
+ if (wanted_sync_port >= 0)
+ lastport = wanted_sync_port;
+ /*
+ * Set a flag just in case the port is in the middle of a close,
+ * it will not be permitted to actually close until we get an
+ * sync response, and clear the flag there.
+ */
+ ch = nd->nd_chan + lastport;
+ ch->ch_flag |= CH_WAITING_SYNC;
+
+ mod = lastport >> 4;
+
+ if (mod != nd->nd_tx_module) {
+ bb[0] = 0xf0 + mod;
+ bb += 1;
+
+ nd->nd_tx_module = mod;
+ }
+
+ bb = set_cmd_header(bb, lastport, 12);
+ *bb++ = in;
+
+ nd->nd_seq_size[in] = bb - buf;
+ nd->nd_seq_time[in] = jiffies;
+
+ if (++in >= SEQ_MAX)
+ in = 0;
+
+ if (in != nd->nd_seq_out) {
+ b = bb;
+ nd->nd_seq_in = in;
+ nd->nd_unack += b - buf;
+ }
+ }
+
+ /*
+ * If there are no open ports, a sync cannot be sent.
+ * There is nothing left to wait for anyway, so wake any
+ * thread waiting for an acknowledgement.
+ */
+
+ else if (nd->nd_seq_wait[in] != 0) {
+ nd->nd_seq_wait[in] = 0;
+
+ wake_up_interruptible(&nd->nd_seq_wque[in]);
+ }
+
+ /*
+ * If there is no traffic for an interval of IDLE_MAX, then
+ * send a single byte packet.
+ */
+
+ if (b != buf) {
+ nd->nd_tx_time = jiffies;
+ } else if ((ulong)(jiffies - nd->nd_tx_time) >= IDLE_MAX) {
+ *b++ = 0xf0 | nd->nd_tx_module;
+ nd->nd_tx_time = jiffies;
+ }
+
+ n = b - buf;
+
+ if (n >= tsafe)
+ pr_info("%s - n(%i) >= tsafe(%i)\n",
+ __func__, n, tsafe);
+
+ if (tsend < 0)
+ dgrp_dump(buf, n);
+
+ nd->nd_tx_work = work;
+
+ return n;
+}
+
+/*
+ * dgrp_net_read()
+ * Data to be sent TO the PortServer from the "async." half of the driver.
+ */
+static ssize_t dgrp_net_read(struct file *file, char __user *buf, size_t count,
+ loff_t *ppos)
+{
+ struct nd_struct *nd;
+ long n;
+ u8 *local_buf;
+ u8 *b;
+ ssize_t rtn;
+
+ /*
+ * Get the node pointer, and quit if it doesn't exist.
+ */
+ nd = (struct nd_struct *)(file->private_data);
+ if (!nd)
+ return -ENXIO;
+
+ if (count < UIO_MIN)
+ return -EINVAL;
+
+ /*
+ * Only one read/write operation may be in progress at
+ * any given time.
+ */
+
+ /*
+ * Grab the NET lock.
+ */
+ down(&nd->nd_net_semaphore);
+
+ nd->nd_read_count++;
+
+ nd->nd_tx_ready = 0;
+
+ /*
+ * Determine the effective size of the buffer.
+ */
+
+ if (nd->nd_remain > UIO_BASE)
+ pr_info_ratelimited("%s - nd_remain(%i) > UIO_BASE\n",
+ __func__, nd->nd_remain);
+
+ b = local_buf = nd->nd_iobuf + UIO_BASE;
+
+ /*
+ * Generate data according to the node state.
+ */
+
+ switch (nd->nd_state) {
+ /*
+ * Initialize the connection.
+ */
+
+ case NS_IDLE:
+ if (nd->nd_mon_buf)
+ dgrp_monitor_reset(nd);
+
+ /*
+ * Request a Product ID Packet.
+ */
+
+ b[0] = 0xfb;
+ b[1] = 0x01;
+ b += 2;
+
+ nd->nd_expect |= NR_IDENT;
+
+ /*
+ * Request a Server Capability ID Response.
+ */
+
+ b[0] = 0xfb;
+ b[1] = 0x02;
+ b += 2;
+
+ nd->nd_expect |= NR_CAPABILITY;
+
+ /*
+ * Request a Server VPD Response.
+ */
+
+ b[0] = 0xfb;
+ b[1] = 0x18;
+ b += 2;
+
+ nd->nd_expect |= NR_VPD;
+
+ nd->nd_state = NS_WAIT_QUERY;
+ break;
+
+ /*
+ * We do serious communication with the server only in
+ * the READY state.
+ */
+
+ case NS_READY:
+ b = dgrp_send(nd, count) + local_buf;
+ break;
+
+ /*
+ * Send off an error after receiving a bogus message
+ * from the server.
+ */
+
+ case NS_SEND_ERROR:
+ n = strlen(nd->nd_error);
+
+ b[0] = 0xff;
+ b[1] = n;
+ memcpy(b + 2, nd->nd_error, n);
+ b += 2 + n;
+
+ dgrp_net_idle(nd);
+ /*
+ * Set the active port count to zero.
+ */
+ dgrp_chan_count(nd, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ n = b - local_buf;
+
+ if (n != 0) {
+ nd->nd_send_count++;
+
+ nd->nd_tx_byte += n + nd->nd_link.lk_header_size;
+ nd->nd_tx_charge += n + nd->nd_link.lk_header_size;
+ }
+
+ rtn = copy_to_user((void __user *)buf, local_buf, n);
+ if (rtn) {
+ rtn = -EFAULT;
+ goto done;
+ }
+
+ *ppos += n;
+
+ rtn = n;
+
+ if (nd->nd_mon_buf)
+ dgrp_monitor_data(nd, RPDUMP_CLIENT, local_buf, n);
+
+ /*
+ * Release the NET lock.
+ */
+done:
+ up(&nd->nd_net_semaphore);
+
+ return rtn;
+}
+
+/**
+ * dgrp_receive() -- decode data packets received from the remote PortServer.
+ * @nd: pointer to a node structure
+ */
+static void dgrp_receive(struct nd_struct *nd)
+{
+ struct ch_struct *ch;
+ u8 *buf;
+ u8 *b;
+ u8 *dbuf;
+ char *error;
+ long port;
+ long dlen;
+ long plen;
+ long remain;
+ long n;
+ long mlast;
+ long elast;
+ long mstat;
+ long estat;
+
+ char ID[3];
+
+ nd->nd_tx_time = jiffies;
+
+ ID_TO_CHAR(nd->nd_ID, ID);
+
+ b = buf = nd->nd_iobuf;
+ remain = nd->nd_remain;
+
+ /*
+ * Loop to process Realport protocol packets.
+ */
+
+ while (remain > 0) {
+ int n0 = b[0] >> 4;
+ int n1 = b[0] & 0x0f;
+
+ if (n0 <= 12) {
+ port = (nd->nd_rx_module << 4) + n1;
+
+ if (port >= nd->nd_chan_count) {
+ error = "Improper Port Number";
+ goto prot_error;
+ }
+
+ ch = nd->nd_chan + port;
+ } else {
+ port = -1;
+ ch = NULL;
+ }
+
+ /*
+ * Process by major packet type.
+ */
+
+ switch (n0) {
+
+ /*
+ * Process 1-byte header data packet.
+ */
+
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ dlen = n0 + 1;
+ plen = dlen + 1;
+
+ dbuf = b + 1;
+ goto data;
+
+ /*
+ * Process 2-byte header data packet.
+ */
+
+ case 8:
+ if (remain < 3)
+ goto done;
+
+ dlen = b[1];
+ plen = dlen + 2;
+
+ dbuf = b + 2;
+ goto data;
+
+ /*
+ * Process 3-byte header data packet.
+ */
+
+ case 9:
+ if (remain < 4)
+ goto done;
+
+ dlen = get_unaligned_be16(b + 1);
+ plen = dlen + 3;
+
+ dbuf = b + 3;
+
+ /*
+ * Common packet handling code.
+ */
+
+data:
+ nd->nd_tx_work = 1;
+
+ /*
+ * Otherwise data should appear only when we are
+ * in the CS_READY state.
+ */
+
+ if (ch->ch_state < CS_READY) {
+ error = "Data received before RWIN established";
+ goto prot_error;
+ }
+
+ /*
+ * Assure that the data received is within the
+ * allowable window.
+ */
+
+ n = (ch->ch_s_rwin - ch->ch_s_rin) & 0xffff;
+
+ if (dlen > n) {
+ error = "Receive data overrun";
+ goto prot_error;
+ }
+
+ /*
+ * If we received 3 or less characters,
+ * assume it is a human typing, and set RTIME
+ * to 10 milliseconds.
+ *
+ * If we receive 10 or more characters,
+ * assume its not a human typing, and set RTIME
+ * to 100 milliseconds.
+ */
+
+ if (ch->ch_edelay != DGRP_RTIME) {
+ if (ch->ch_rtime != ch->ch_edelay) {
+ ch->ch_rtime = ch->ch_edelay;
+ ch->ch_flag |= CH_PARAM;
+ }
+ } else if (dlen <= 3) {
+ if (ch->ch_rtime != 10) {
+ ch->ch_rtime = 10;
+ ch->ch_flag |= CH_PARAM;
+ }
+ } else {
+ if (ch->ch_rtime != DGRP_RTIME) {
+ ch->ch_rtime = DGRP_RTIME;
+ ch->ch_flag |= CH_PARAM;
+ }
+ }
+
+ /*
+ * If a portion of the packet is outside the
+ * buffer, shorten the effective length of the
+ * data packet to be the amount of data received.
+ */
+
+ if (remain < plen)
+ dlen -= plen - remain;
+
+ /*
+ * Detect if receive flush is now complete.
+ */
+
+ if ((ch->ch_flag & CH_RX_FLUSH) != 0 &&
+ ((ch->ch_flush_seq - nd->nd_seq_out) & SEQ_MASK) >=
+ ((nd->nd_seq_in - nd->nd_seq_out) & SEQ_MASK)) {
+ ch->ch_flag &= ~CH_RX_FLUSH;
+ }
+
+ /*
+ * If we are ready to receive, move the data into
+ * the receive buffer.
+ */
+
+ ch->ch_s_rin = (ch->ch_s_rin + dlen) & 0xffff;
+
+ if (ch->ch_state == CS_READY &&
+ (ch->ch_tun.un_open_count != 0) &&
+ (ch->ch_tun.un_flag & UN_CLOSING) == 0 &&
+ (ch->ch_cflag & CF_CREAD) != 0 &&
+ (ch->ch_flag & (CH_BAUD0 | CH_RX_FLUSH)) == 0 &&
+ (ch->ch_send & RR_RX_FLUSH) == 0) {
+
+ if (ch->ch_rin + dlen >= RBUF_MAX) {
+ n = RBUF_MAX - ch->ch_rin;
+
+ memcpy(ch->ch_rbuf + ch->ch_rin, dbuf, n);
+
+ ch->ch_rin = 0;
+ dbuf += n;
+ dlen -= n;
+ }
+
+ memcpy(ch->ch_rbuf + ch->ch_rin, dbuf, dlen);
+
+ ch->ch_rin += dlen;
+
+
+ /*
+ * If we are not in fastcook mode, or
+ * if there is a fastcook thread
+ * waiting for data, send the data to
+ * the line discipline.
+ */
+
+ if ((ch->ch_flag & CH_FAST_READ) == 0 ||
+ ch->ch_inwait != 0) {
+ dgrp_input(ch);
+ }
+
+ /*
+ * If there is a read thread waiting
+ * in select, and we are in fastcook
+ * mode, wake him up.
+ */
+
+ if (waitqueue_active(&ch->ch_tun.un_tty->read_wait) &&
+ (ch->ch_flag & CH_FAST_READ) != 0)
+ wake_up_interruptible(&ch->ch_tun.un_tty->read_wait);
+
+ /*
+ * Wake any thread waiting in the
+ * fastcook loop.
+ */
+
+ if ((ch->ch_flag & CH_INPUT) != 0) {
+ ch->ch_flag &= ~CH_INPUT;
+
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+ }
+
+ /*
+ * Fabricate and insert a data packet header to
+ * preceed the remaining data when it comes in.
+ */
+
+ if (remain < plen) {
+ dlen = plen - remain;
+ b = buf;
+
+ b[0] = 0x90 + n1;
+ put_unaligned_be16(dlen, b + 1);
+
+ remain = 3;
+ goto done;
+ }
+ break;
+
+ /*
+ * Handle Window Sequence packets.
+ */
+
+ case 10:
+ plen = 3;
+ if (remain < plen)
+ goto done;
+
+ nd->nd_tx_work = 1;
+
+ {
+ ushort tpos = get_unaligned_be16(b + 1);
+
+ ushort ack = (tpos - ch->ch_s_tpos) & 0xffff;
+ ushort unack = (ch->ch_s_tin - ch->ch_s_tpos) & 0xffff;
+ ushort notify = (ch->ch_s_treq - ch->ch_s_tpos) & 0xffff;
+
+ if (ch->ch_state < CS_READY || ack > unack) {
+ error = "Improper Window Sequence";
+ goto prot_error;
+ }
+
+ ch->ch_s_tpos = tpos;
+
+ if (notify <= ack)
+ ch->ch_s_treq = tpos;
+ }
+ break;
+
+ /*
+ * Handle Command response packets.
+ */
+
+ case 11:
+
+ /*
+ * RealPort engine fix - 03/11/2004
+ *
+ * This check did not used to be here.
+ *
+ * We were using b[1] without verifying that the data
+ * is actually there and valid. On a split packet, it
+ * might not be yet.
+ *
+ * NOTE: I have never actually seen the failure happen
+ * under Linux, but since I have seen it occur
+ * under both Solaris and HP-UX, the assumption
+ * is that it *could* happen here as well...
+ */
+ if (remain < 2)
+ goto done;
+
+
+ switch (b[1]) {
+
+ /*
+ * Handle Open Response.
+ */
+
+ case 11:
+ plen = 6;
+ if (remain < plen)
+ goto done;
+
+ nd->nd_tx_work = 1;
+
+ {
+ int req = b[2];
+ int resp = b[3];
+ port = get_unaligned_be16(b + 4);
+
+ if (port >= nd->nd_chan_count) {
+ error = "Open channel number out of range";
+ goto prot_error;
+ }
+
+ ch = nd->nd_chan + port;
+
+ /*
+ * How we handle an open response depends primarily
+ * on our current channel state.
+ */
+
+ switch (ch->ch_state) {
+ case CS_IDLE:
+
+ /*
+ * Handle a delayed open.
+ */
+
+ if (ch->ch_otype_waiting != 0 &&
+ req == ch->ch_otype_waiting &&
+ resp == 0) {
+ ch->ch_otype = req;
+ ch->ch_otype_waiting = 0;
+ ch->ch_state = CS_SEND_QUERY;
+ break;
+ }
+ goto open_error;
+
+ case CS_WAIT_OPEN:
+
+ /*
+ * Handle the open response.
+ */
+
+ if (req == ch->ch_otype) {
+ switch (resp) {
+
+ /*
+ * On successful response, open the
+ * port and proceed normally.
+ */
+
+ case 0:
+ ch->ch_state = CS_SEND_QUERY;
+ break;
+
+ /*
+ * On a busy response to a persistent open,
+ * remember that the open is pending.
+ */
+
+ case 1:
+ case 2:
+ if (req != OTYPE_IMMEDIATE) {
+ ch->ch_otype_waiting = req;
+ ch->ch_state = CS_IDLE;
+ break;
+ }
+
+ /*
+ * Otherwise the server open failed. If
+ * the Unix port is open, hang it up.
+ */
+
+ default:
+ if (ch->ch_open_count != 0) {
+ ch->ch_flag |= CH_HANGUP;
+ dgrp_carrier(ch);
+ ch->ch_state = CS_IDLE;
+ break;
+ }
+
+ ch->ch_open_error = resp;
+ ch->ch_state = CS_IDLE;
+
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+ break;
+ }
+
+ /*
+ * Handle delayed response arrival preceeding
+ * the open response we are waiting for.
+ */
+
+ if (ch->ch_otype_waiting != 0 &&
+ req == ch->ch_otype_waiting &&
+ resp == 0) {
+ ch->ch_otype = ch->ch_otype_waiting;
+ ch->ch_otype_waiting = 0;
+ ch->ch_state = CS_WAIT_FAIL;
+ break;
+ }
+ goto open_error;
+
+
+ case CS_WAIT_FAIL:
+
+ /*
+ * Handle response to immediate open arriving
+ * after a delayed open success.
+ */
+
+ if (req == OTYPE_IMMEDIATE) {
+ ch->ch_state = CS_SEND_QUERY;
+ break;
+ }
+ goto open_error;
+
+
+ case CS_WAIT_CANCEL:
+ /*
+ * Handle delayed open response arriving before
+ * the cancel response.
+ */
+
+ if (req == ch->ch_otype_waiting &&
+ resp == 0) {
+ ch->ch_otype_waiting = 0;
+ break;
+ }
+
+ /*
+ * Handle cancel response.
+ */
+
+ if (req == 4 && resp == 0) {
+ ch->ch_otype_waiting = 0;
+ ch->ch_state = CS_IDLE;
+ break;
+ }
+ goto open_error;
+
+
+ case CS_WAIT_CLOSE:
+ /*
+ * Handle a successful response to a port
+ * close.
+ */
+
+ if (req >= 3) {
+ ch->ch_state = CS_IDLE;
+ break;
+ }
+ goto open_error;
+
+open_error:
+ default:
+ {
+ error = "Improper Open Response";
+ goto prot_error;
+ }
+ }
+ }
+ break;
+
+ /*
+ * Handle Synchronize Response.
+ */
+
+ case 13:
+ plen = 3;
+ if (remain < plen)
+ goto done;
+ {
+ int seq = b[2];
+ int s;
+
+ /*
+ * If channel was waiting for this sync response,
+ * unset the flag, and wake up anyone waiting
+ * on the event.
+ */
+ if (ch->ch_flag & CH_WAITING_SYNC) {
+ ch->ch_flag &= ~(CH_WAITING_SYNC);
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+
+ if (((seq - nd->nd_seq_out) & SEQ_MASK) >=
+ ((nd->nd_seq_in - nd->nd_seq_out) & SEQ_MASK)) {
+ break;
+ }
+
+ for (s = nd->nd_seq_out;; s = (s + 1) & SEQ_MASK) {
+ if (nd->nd_seq_wait[s] != 0) {
+ nd->nd_seq_wait[s] = 0;
+
+ wake_up_interruptible(&nd->nd_seq_wque[s]);
+ }
+
+ nd->nd_unack -= nd->nd_seq_size[s];
+
+ if (s == seq)
+ break;
+ }
+
+ nd->nd_seq_out = (seq + 1) & SEQ_MASK;
+ }
+ break;
+
+ /*
+ * Handle Sequence Response.
+ */
+
+ case 15:
+ plen = 6;
+ if (remain < plen)
+ goto done;
+
+ {
+ /* Record that we have received the Sequence
+ * Response, but we aren't interested in the
+ * sequence numbers. We were using RIN like it
+ * was ROUT and that was causing problems,
+ * fixed 7-13-2001 David Fries. See comment in
+ * drp.h for ch_s_rin variable.
+ int rin = get_unaligned_be16(b + 2);
+ int tpos = get_unaligned_be16(b + 4);
+ */
+
+ ch->ch_send &= ~RR_SEQUENCE;
+ ch->ch_expect &= ~RR_SEQUENCE;
+ }
+ goto check_query;
+
+ /*
+ * Handle Status Response.
+ */
+
+ case 17:
+ plen = 5;
+ if (remain < plen)
+ goto done;
+
+ {
+ ch->ch_s_elast = get_unaligned_be16(b + 2);
+ ch->ch_s_mlast = b[4];
+
+ ch->ch_expect &= ~RR_STATUS;
+ ch->ch_send &= ~RR_STATUS;
+
+ /*
+ * CH_PHYS_CD is cleared because something _could_ be
+ * waiting for the initial sense of carrier... and if
+ * carrier is high immediately, we want to be sure to
+ * wake them as soon as possible.
+ */
+ ch->ch_flag &= ~CH_PHYS_CD;
+
+ dgrp_carrier(ch);
+ }
+ goto check_query;
+
+ /*
+ * Handle Line Error Response.
+ */
+
+ case 19:
+ plen = 14;
+ if (remain < plen)
+ goto done;
+
+ break;
+
+ /*
+ * Handle Buffer Response.
+ */
+
+ case 21:
+ plen = 6;
+ if (remain < plen)
+ goto done;
+
+ {
+ ch->ch_s_rsize = get_unaligned_be16(b + 2);
+ ch->ch_s_tsize = get_unaligned_be16(b + 4);
+
+ ch->ch_send &= ~RR_BUFFER;
+ ch->ch_expect &= ~RR_BUFFER;
+ }
+ goto check_query;
+
+ /*
+ * Handle Port Capability Response.
+ */
+
+ case 23:
+ plen = 32;
+ if (remain < plen)
+ goto done;
+
+ {
+ ch->ch_send &= ~RR_CAPABILITY;
+ ch->ch_expect &= ~RR_CAPABILITY;
+ }
+
+ /*
+ * When all queries are complete, set those parameters
+ * derived from the query results, then transition
+ * to the READY state.
+ */
+
+check_query:
+ if (ch->ch_state == CS_WAIT_QUERY &&
+ (ch->ch_expect & (RR_SEQUENCE |
+ RR_STATUS |
+ RR_BUFFER |
+ RR_CAPABILITY)) == 0) {
+ ch->ch_tmax = ch->ch_s_tsize / 4;
+
+ if (ch->ch_edelay == DGRP_TTIME)
+ ch->ch_ttime = DGRP_TTIME;
+ else
+ ch->ch_ttime = ch->ch_edelay;
+
+ ch->ch_rmax = ch->ch_s_rsize / 4;
+
+ if (ch->ch_edelay == DGRP_RTIME)
+ ch->ch_rtime = DGRP_RTIME;
+ else
+ ch->ch_rtime = ch->ch_edelay;
+
+ ch->ch_rlow = 2 * ch->ch_s_rsize / 8;
+ ch->ch_rhigh = 6 * ch->ch_s_rsize / 8;
+
+ ch->ch_state = CS_READY;
+
+ nd->nd_tx_work = 1;
+ wake_up_interruptible(&ch->ch_flag_wait);
+
+ }
+ break;
+
+ default:
+ goto decode_error;
+ }
+ break;
+
+ /*
+ * Handle Events.
+ */
+
+ case 12:
+ plen = 4;
+ if (remain < plen)
+ goto done;
+
+ mlast = ch->ch_s_mlast;
+ elast = ch->ch_s_elast;
+
+ mstat = ch->ch_s_mlast = b[1];
+ estat = ch->ch_s_elast = get_unaligned_be16(b + 2);
+
+ /*
+ * Handle modem changes.
+ */
+
+ if (((mstat ^ mlast) & DM_CD) != 0)
+ dgrp_carrier(ch);
+
+
+ /*
+ * Handle received break.
+ */
+
+ if ((estat & ~elast & EV_RXB) != 0 &&
+ (ch->ch_tun.un_open_count != 0) &&
+ I_BRKINT(ch->ch_tun.un_tty) &&
+ !(I_IGNBRK(ch->ch_tun.un_tty))) {
+
+ tty_buffer_request_room(ch->ch_tun.un_tty, 1);
+ tty_insert_flip_char(ch->ch_tun.un_tty, 0, TTY_BREAK);
+ tty_flip_buffer_push(ch->ch_tun.un_tty);
+
+ }
+
+ /*
+ * On transmit break complete, if more break traffic
+ * is waiting then send it. Otherwise wake any threads
+ * waiting for transmitter empty.
+ */
+
+ if ((~estat & elast & EV_TXB) != 0 &&
+ (ch->ch_expect & RR_TX_BREAK) != 0) {
+
+ nd->nd_tx_work = 1;
+
+ ch->ch_expect &= ~RR_TX_BREAK;
+
+ if (ch->ch_break_time != 0) {
+ ch->ch_send |= RR_TX_BREAK;
+ } else {
+ ch->ch_send &= ~RR_TX_BREAK;
+ ch->ch_flag &= ~CH_TX_BREAK;
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+ }
+ break;
+
+ case 13:
+ case 14:
+ error = "Unrecognized command";
+ goto prot_error;
+
+ /*
+ * Decode Special Codes.
+ */
+
+ case 15:
+ switch (n1) {
+ /*
+ * One byte module select.
+ */
+
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ plen = 1;
+ nd->nd_rx_module = n1;
+ break;
+
+ /*
+ * Two byte module select.
+ */
+
+ case 8:
+ plen = 2;
+ if (remain < plen)
+ goto done;
+
+ nd->nd_rx_module = b[1];
+ break;
+
+ /*
+ * ID Request packet.
+ */
+
+ case 11:
+ if (remain < 4)
+ goto done;
+
+ plen = get_unaligned_be16(b + 2);
+
+ if (plen < 12 || plen > 1000) {
+ error = "Response Packet length error";
+ goto prot_error;
+ }
+
+ nd->nd_tx_work = 1;
+
+ switch (b[1]) {
+ /*
+ * Echo packet.
+ */
+
+ case 0:
+ nd->nd_send |= NR_ECHO;
+ break;
+
+ /*
+ * ID Response packet.
+ */
+
+ case 1:
+ nd->nd_send |= NR_IDENT;
+ break;
+
+ /*
+ * ID Response packet.
+ */
+
+ case 32:
+ nd->nd_send |= NR_PASSWORD;
+ break;
+
+ }
+ break;
+
+ /*
+ * Various node-level response packets.
+ */
+
+ case 12:
+ if (remain < 4)
+ goto done;
+
+ plen = get_unaligned_be16(b + 2);
+
+ if (plen < 4 || plen > 1000) {
+ error = "Response Packet length error";
+ goto prot_error;
+ }
+
+ nd->nd_tx_work = 1;
+
+ switch (b[1]) {
+ /*
+ * Echo packet.
+ */
+
+ case 0:
+ nd->nd_expect &= ~NR_ECHO;
+ break;
+
+ /*
+ * Product Response Packet.
+ */
+
+ case 1:
+ {
+ int desclen;
+
+ nd->nd_hw_ver = (b[8] << 8) | b[9];
+ nd->nd_sw_ver = (b[10] << 8) | b[11];
+ nd->nd_hw_id = b[6];
+ desclen = ((plen - 12) > MAX_DESC_LEN) ? MAX_DESC_LEN :
+ plen - 12;
+
+ if (desclen <= 0) {
+ error = "Response Packet desclen error";
+ goto prot_error;
+ }
+
+ strncpy(nd->nd_ps_desc, b + 12, desclen);
+ nd->nd_ps_desc[desclen] = 0;
+ }
+
+ nd->nd_expect &= ~NR_IDENT;
+ break;
+
+ /*
+ * Capability Response Packet.
+ */
+
+ case 2:
+ {
+ int nn = get_unaligned_be16(b + 4);
+
+ if (nn > CHAN_MAX)
+ nn = CHAN_MAX;
+
+ dgrp_chan_count(nd, nn);
+ }
+
+ nd->nd_expect &= ~NR_CAPABILITY;
+ break;
+
+ /*
+ * VPD Response Packet.
+ */
+
+ case 15:
+ /*
+ * NOTE: case 15 is here ONLY because the EtherLite
+ * is broken, and sends a response to 24 back as 15.
+ * To resolve this, the EtherLite firmware is now
+ * fixed to send back 24 correctly, but, for backwards
+ * compatibility, we now have reserved 15 for the
+ * bad EtherLite response to 24 as well.
+ */
+
+ /* Fallthru! */
+
+ case 24:
+
+ /*
+ * If the product doesn't support VPD,
+ * it will send back a null IDRESP,
+ * which is a length of 4 bytes.
+ */
+ if (plen > 4) {
+ memcpy(nd->nd_vpd, b + 4, min(plen - 4, (long) VPDSIZE));
+ nd->nd_vpd_len = min(plen - 4, (long) VPDSIZE);
+ }
+
+ nd->nd_expect &= ~NR_VPD;
+ break;
+
+ default:
+ goto decode_error;
+ }
+
+ if (nd->nd_expect == 0 &&
+ nd->nd_state == NS_WAIT_QUERY) {
+ nd->nd_state = NS_READY;
+ }
+ break;
+
+ /*
+ * Debug packet.
+ */
+
+ case 14:
+ if (remain < 4)
+ goto done;
+
+ plen = get_unaligned_be16(b + 2) + 4;
+
+ if (plen > 1000) {
+ error = "Debug Packet too large";
+ goto prot_error;
+ }
+
+ if (remain < plen)
+ goto done;
+ break;
+
+ /*
+ * Handle reset packet.
+ */
+
+ case 15:
+ if (remain < 2)
+ goto done;
+
+ plen = 2 + b[1];
+
+ if (remain < plen)
+ goto done;
+
+ nd->nd_tx_work = 1;
+
+ n = b[plen];
+ b[plen] = 0;
+
+ b[plen] = n;
+
+ error = "Client Reset Acknowledge";
+ goto prot_error;
+
+ default:
+ goto decode_error;
+ }
+ break;
+
+ default:
+ goto decode_error;
+ }
+
+ b += plen;
+ remain -= plen;
+ }
+
+ /*
+ * When the buffer is exhausted, copy any data left at the
+ * top of the buffer back down to the bottom for the next
+ * read request.
+ */
+
+done:
+ if (remain > 0 && b != buf)
+ memcpy(buf, b, remain);
+
+ nd->nd_remain = remain;
+ return;
+
+/*
+ * Handle a decode error.
+ */
+
+decode_error:
+ error = "Protocol decode error";
+
+/*
+ * Handle a general protocol error.
+ */
+
+prot_error:
+ nd->nd_remain = 0;
+ nd->nd_state = NS_SEND_ERROR;
+ nd->nd_error = error;
+}
+
+/*
+ * dgrp_net_write() -- write data to the network device.
+ *
+ * A zero byte write indicates that the connection to the RealPort
+ * device has been broken.
+ *
+ * A non-zero write indicates data from the RealPort device.
+ */
+static ssize_t dgrp_net_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct nd_struct *nd;
+ ssize_t rtn = 0;
+ long n;
+ long total = 0;
+
+ /*
+ * Get the node pointer, and quit if it doesn't exist.
+ */
+ nd = (struct nd_struct *)(file->private_data);
+ if (!nd)
+ return -ENXIO;
+
+ /*
+ * Grab the NET lock.
+ */
+ down(&nd->nd_net_semaphore);
+
+ nd->nd_write_count++;
+
+ /*
+ * Handle disconnect.
+ */
+
+ if (count == 0) {
+ dgrp_net_idle(nd);
+ /*
+ * Set the active port count to zero.
+ */
+ dgrp_chan_count(nd, 0);
+ goto unlock;
+ }
+
+ /*
+ * Loop to process entire receive packet.
+ */
+
+ while (count > 0) {
+ n = UIO_MAX - nd->nd_remain;
+
+ if (n > count)
+ n = count;
+
+ nd->nd_rx_byte += n + nd->nd_link.lk_header_size;
+
+ rtn = copy_from_user(nd->nd_iobuf + nd->nd_remain,
+ (void __user *) buf + total, n);
+ if (rtn) {
+ rtn = -EFAULT;
+ goto unlock;
+ }
+
+ *ppos += n;
+
+ total += n;
+
+ count -= n;
+
+ if (nd->nd_mon_buf)
+ dgrp_monitor_data(nd, RPDUMP_SERVER,
+ nd->nd_iobuf + nd->nd_remain, n);
+
+ nd->nd_remain += n;
+
+ dgrp_receive(nd);
+ }
+
+ rtn = total;
+
+unlock:
+ /*
+ * Release the NET lock.
+ */
+ up(&nd->nd_net_semaphore);
+
+ return rtn;
+}
+
+
+/*
+ * dgrp_net_select()
+ * Determine whether a device is ready to be read or written to, and
+ * sleep if not.
+ */
+static unsigned int dgrp_net_select(struct file *file,
+ struct poll_table_struct *table)
+{
+ unsigned int retval = 0;
+ struct nd_struct *nd = file->private_data;
+
+ poll_wait(file, &nd->nd_tx_waitq, table);
+
+ if (nd->nd_tx_ready)
+ retval |= POLLIN | POLLRDNORM; /* Conditionally readable */
+
+ retval |= POLLOUT | POLLWRNORM; /* Always writeable */
+
+ return retval;
+}
+
+/*
+ * dgrp_net_ioctl
+ *
+ * Implement those functions which allow the network daemon to control
+ * the network parameters in the driver. The ioctls include ones to
+ * get and set the link speed parameters for the PortServer.
+ */
+static long dgrp_net_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct nd_struct *nd;
+ int rtn = 0;
+ long size = _IOC_SIZE(cmd);
+ struct link_struct link;
+
+ nd = file->private_data;
+
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ rtn = access_ok(VERIFY_WRITE, (void __user *) arg, size);
+ else if (_IOC_DIR(cmd) & _IOC_WRITE)
+ rtn = access_ok(VERIFY_READ, (void __user *) arg, size);
+
+ if (!rtn)
+ return rtn;
+
+ switch (cmd) {
+ case DIGI_SETLINK:
+ if (size != sizeof(struct link_struct))
+ return -EINVAL;
+
+ if (copy_from_user((void *)(&link), (void __user *) arg, size))
+ return -EFAULT;
+
+ if (link.lk_fast_rate < 9600)
+ link.lk_fast_rate = 9600;
+
+ if (link.lk_slow_rate < 2400)
+ link.lk_slow_rate = 2400;
+
+ if (link.lk_fast_rate > 10000000)
+ link.lk_fast_rate = 10000000;
+
+ if (link.lk_slow_rate > link.lk_fast_rate)
+ link.lk_slow_rate = link.lk_fast_rate;
+
+ if (link.lk_fast_delay > 2000)
+ link.lk_fast_delay = 2000;
+
+ if (link.lk_slow_delay > 10000)
+ link.lk_slow_delay = 10000;
+
+ if (link.lk_fast_delay < 60)
+ link.lk_fast_delay = 60;
+
+ if (link.lk_slow_delay < link.lk_fast_delay)
+ link.lk_slow_delay = link.lk_fast_delay;
+
+ if (link.lk_header_size < 2)
+ link.lk_header_size = 2;
+
+ if (link.lk_header_size > 128)
+ link.lk_header_size = 128;
+
+ link.lk_fast_rate /= 8 * 1000 / dgrp_poll_tick;
+ link.lk_slow_rate /= 8 * 1000 / dgrp_poll_tick;
+
+ link.lk_fast_delay /= dgrp_poll_tick;
+ link.lk_slow_delay /= dgrp_poll_tick;
+
+ nd->nd_link = link;
+
+ break;
+
+ case DIGI_GETLINK:
+ if (size != sizeof(struct link_struct))
+ return -EINVAL;
+
+ if (copy_to_user((void __user *)arg, (void *)(&nd->nd_link),
+ size))
+ return -EFAULT;
+
+ break;
+
+ default:
+ return -EINVAL;
+
+ }
+
+ return 0;
+}
+
+/**
+ * dgrp_poll_handler() -- handler for poll timer
+ *
+ * As each timer expires, it determines (a) whether the "transmit"
+ * waiter needs to be woken up, and (b) whether the poller needs to
+ * be rescheduled.
+ */
+void dgrp_poll_handler(unsigned long arg)
+{
+ struct dgrp_poll_data *poll_data;
+ struct nd_struct *nd;
+ struct link_struct *lk;
+ ulong time;
+ ulong poll_time;
+ ulong freq;
+ ulong lock_flags;
+
+ poll_data = (struct dgrp_poll_data *) arg;
+ freq = 1000 / poll_data->poll_tick;
+ poll_data->poll_round += 17;
+
+ if (poll_data->poll_round >= freq)
+ poll_data->poll_round -= freq;
+
+ /*
+ * Loop to process all open nodes.
+ *
+ * For each node, determine the rate at which it should
+ * be transmitting data. Then if the node should wake up
+ * and transmit data now, enable the net receive select
+ * to get the transmit going.
+ */
+
+ list_for_each_entry(nd, &nd_struct_list, list) {
+
+ lk = &nd->nd_link;
+
+ /*
+ * Decrement statistics. These are only for use with
+ * KME, so don't worry that the operations are done
+ * unlocked, and so the results are occassionally wrong.
+ */
+
+ nd->nd_read_count -= (nd->nd_read_count +
+ poll_data->poll_round) / freq;
+ nd->nd_write_count -= (nd->nd_write_count +
+ poll_data->poll_round) / freq;
+ nd->nd_send_count -= (nd->nd_send_count +
+ poll_data->poll_round) / freq;
+ nd->nd_tx_byte -= (nd->nd_tx_byte +
+ poll_data->poll_round) / freq;
+ nd->nd_rx_byte -= (nd->nd_rx_byte +
+ poll_data->poll_round) / freq;
+
+ /*
+ * Wake the daemon to transmit data only when there is
+ * enough byte credit to send data.
+ *
+ * The results are approximate because the operations
+ * are performed unlocked, and we are inspecting
+ * data asynchronously updated elsewhere. The whole
+ * thing is just approximation anyway, so that should
+ * be okay.
+ */
+
+ if (lk->lk_slow_rate >= UIO_MAX) {
+
+ nd->nd_delay = 0;
+ nd->nd_rate = UIO_MAX;
+
+ nd->nd_tx_deposit = nd->nd_tx_charge + 3 * UIO_MAX;
+ nd->nd_tx_credit = 3 * UIO_MAX;
+
+ } else {
+
+ long rate;
+ long delay;
+ long deposit;
+ long charge;
+ long size;
+ long excess;
+
+ long seq_in = nd->nd_seq_in;
+ long seq_out = nd->nd_seq_out;
+
+ /*
+ * If there are no outstanding packets, run at the
+ * fastest rate.
+ */
+
+ if (seq_in == seq_out) {
+ delay = 0;
+ rate = lk->lk_fast_rate;
+ }
+
+ /*
+ * Otherwise compute the transmit rate based on the
+ * delay since the oldest packet.
+ */
+
+ else {
+ /*
+ * The actual delay is computed as the
+ * time since the oldest unacknowledged
+ * packet was sent, minus the time it
+ * took to send that packet to the server.
+ */
+
+ delay = ((jiffies - nd->nd_seq_time[seq_out])
+ - (nd->nd_seq_size[seq_out] /
+ lk->lk_fast_rate));
+
+ /*
+ * If the delay is less than the "fast"
+ * delay, transmit full speed. If greater
+ * than the "slow" delay, transmit at the
+ * "slow" speed. In between, interpolate
+ * between the fast and slow speeds.
+ */
+
+ rate =
+ (delay <= lk->lk_fast_delay ?
+ lk->lk_fast_rate :
+ delay >= lk->lk_slow_delay ?
+ lk->lk_slow_rate :
+ (lk->lk_slow_rate +
+ (lk->lk_slow_delay - delay) *
+ (lk->lk_fast_rate - lk->lk_slow_rate) /
+ (lk->lk_slow_delay - lk->lk_fast_delay)
+ )
+ );
+ }
+
+ nd->nd_delay = delay;
+ nd->nd_rate = rate;
+
+ /*
+ * Increase the transmit credit by depositing the
+ * current transmit rate.
+ */
+
+ deposit = nd->nd_tx_deposit;
+ charge = nd->nd_tx_charge;
+
+ deposit += rate;
+
+ /*
+ * If the available transmit credit becomes too large,
+ * reduce the deposit to correct the value.
+ *
+ * Too large is the max of:
+ * 6 times the header size
+ * 3 times the current transmit rate.
+ */
+
+ size = 2 * nd->nd_link.lk_header_size;
+
+ if (size < rate)
+ size = rate;
+
+ size *= 3;
+
+ excess = deposit - charge - size;
+
+ if (excess > 0)
+ deposit -= excess;
+
+ nd->nd_tx_deposit = deposit;
+ nd->nd_tx_credit = deposit - charge;
+
+ /*
+ * Wake the transmit task only if the transmit credit
+ * is at least 3 times the transmit header size.
+ */
+
+ size = 3 * lk->lk_header_size;
+
+ if (nd->nd_tx_credit < size)
+ continue;
+ }
+
+
+ /*
+ * Enable the READ select to wake the daemon if there
+ * is useful work for the drp_read routine to perform.
+ */
+
+ if (waitqueue_active(&nd->nd_tx_waitq) &&
+ (nd->nd_tx_work != 0 ||
+ (ulong)(jiffies - nd->nd_tx_time) >= IDLE_MAX)) {
+ nd->nd_tx_ready = 1;
+
+ wake_up_interruptible(&nd->nd_tx_waitq);
+
+ /* not needed */
+ /* nd->nd_flag &= ~ND_SELECT; */
+ }
+ }
+
+
+ /*
+ * Schedule ourself back at the nominal wakeup interval.
+ */
+ spin_lock_irqsave(&poll_data->poll_lock, lock_flags);
+
+ poll_data->node_active_count--;
+ if (poll_data->node_active_count > 0) {
+ poll_data->node_active_count++;
+ poll_time = poll_data->timer.expires +
+ poll_data->poll_tick * HZ / 1000;
+
+ time = poll_time - jiffies;
+
+ if (time >= 2 * poll_data->poll_tick)
+ poll_time = jiffies + dgrp_poll_tick * HZ / 1000;
+
+ poll_data->timer.expires = poll_time;
+ add_timer(&poll_data->timer);
+ }
+
+ spin_unlock_irqrestore(&poll_data->poll_lock, lock_flags);
+}
diff --git a/drivers/staging/dgrp/dgrp_ports_ops.c b/drivers/staging/dgrp/dgrp_ports_ops.c
new file mode 100644
index 000000000000..cd1fc2088624
--- /dev/null
+++ b/drivers/staging/dgrp/dgrp_ports_ops.c
@@ -0,0 +1,170 @@
+/*
+ *
+ * Copyright 1999-2000 Digi International (www.digi.com)
+ * James Puzzo <jamesp at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, 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.
+ *
+ * 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.
+ *
+ */
+
+/*
+ *
+ * Filename:
+ *
+ * dgrp_ports_ops.c
+ *
+ * Description:
+ *
+ * Handle the file operations required for the /proc/dgrp/ports/...
+ * devices. Basically gathers tty status for the node and returns it.
+ *
+ * Author:
+ *
+ * James A. Puzzo
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/proc_fs.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+
+#include "dgrp_common.h"
+
+/* File operation declarations */
+static int dgrp_ports_open(struct inode *, struct file *);
+
+static const struct file_operations ports_ops = {
+ .owner = THIS_MODULE,
+ .open = dgrp_ports_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release
+};
+
+static struct inode_operations ports_inode_ops = {
+ .permission = dgrp_inode_permission
+};
+
+
+void dgrp_register_ports_hook(struct proc_dir_entry *de)
+{
+ struct nd_struct *node = de->data;
+
+ de->proc_iops = &ports_inode_ops;
+ de->proc_fops = &ports_ops;
+ node->nd_ports_de = de;
+}
+
+static void *dgrp_ports_seq_start(struct seq_file *seq, loff_t *pos)
+{
+ if (*pos == 0)
+ seq_puts(seq, "#num tty_open pr_open tot_wait MSTAT IFLAG OFLAG CFLAG BPS DIGIFLAGS\n");
+
+ return pos;
+}
+
+static void *dgrp_ports_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+ struct nd_struct *nd = seq->private;
+
+ if (*pos >= nd->nd_chan_count)
+ return NULL;
+
+ *pos += 1;
+
+ return pos;
+}
+
+static void dgrp_ports_seq_stop(struct seq_file *seq, void *v)
+{
+}
+
+static int dgrp_ports_seq_show(struct seq_file *seq, void *v)
+{
+ loff_t *pos = v;
+ struct nd_struct *nd;
+ struct ch_struct *ch;
+ struct un_struct *tun, *pun;
+ unsigned int totcnt;
+
+ nd = seq->private;
+ if (!nd)
+ return 0;
+
+ if (*pos >= nd->nd_chan_count)
+ return 0;
+
+ ch = &nd->nd_chan[*pos];
+ tun = &ch->ch_tun;
+ pun = &ch->ch_pun;
+
+ /*
+ * If port is not open and no one is waiting to
+ * open it, the modem signal values can't be
+ * trusted, and will be zeroed.
+ */
+ totcnt = tun->un_open_count +
+ pun->un_open_count +
+ ch->ch_wait_count[0] +
+ ch->ch_wait_count[1] +
+ ch->ch_wait_count[2];
+
+ seq_printf(seq, "%02d %02d %02d %02d 0x%04X 0x%04X 0x%04X 0x%04X %-6d 0x%04X\n",
+ (int) *pos,
+ tun->un_open_count,
+ pun->un_open_count,
+ ch->ch_wait_count[0] +
+ ch->ch_wait_count[1] +
+ ch->ch_wait_count[2],
+ (totcnt ? ch->ch_s_mlast : 0),
+ ch->ch_s_iflag,
+ ch->ch_s_oflag,
+ ch->ch_s_cflag,
+ (ch->ch_s_brate ? (1843200 / ch->ch_s_brate) : 0),
+ ch->ch_digi.digi_flags);
+
+ return 0;
+}
+
+static const struct seq_operations ports_seq_ops = {
+ .start = dgrp_ports_seq_start,
+ .next = dgrp_ports_seq_next,
+ .stop = dgrp_ports_seq_stop,
+ .show = dgrp_ports_seq_show,
+};
+
+/**
+ * dgrp_ports_open -- open the /proc/dgrp/ports/... device
+ * @inode: struct inode *
+ * @file: struct file *
+ *
+ * Open function to open the /proc/dgrp/ports device for a PortServer.
+ * This is the open function for struct file_operations
+ */
+static int dgrp_ports_open(struct inode *inode, struct file *file)
+{
+ struct seq_file *seq;
+ int rtn;
+
+ rtn = seq_open(file, &ports_seq_ops);
+ if (!rtn) {
+ seq = file->private_data;
+ seq->private = PDE(inode)->data;
+ }
+
+ return rtn;
+}
diff --git a/drivers/staging/dgrp/dgrp_specproc.c b/drivers/staging/dgrp/dgrp_specproc.c
new file mode 100644
index 000000000000..28f5c9ab6b43
--- /dev/null
+++ b/drivers/staging/dgrp/dgrp_specproc.c
@@ -0,0 +1,822 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ * James Puzzo <jamesp at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, 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.
+ *
+ */
+
+/*
+ *
+ * Filename:
+ *
+ * dgrp_specproc.c
+ *
+ * Description:
+ *
+ * Handle the "config" proc entry for the linux realport device driver
+ * and provide slots for the "net" and "mon" devices
+ *
+ * Author:
+ *
+ * James A. Puzzo
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/sched.h>
+#include <linux/cred.h>
+#include <linux/proc_fs.h>
+#include <linux/ctype.h>
+#include <linux/seq_file.h>
+#include <linux/vmalloc.h>
+
+#include "dgrp_common.h"
+
+static struct dgrp_proc_entry dgrp_table[];
+static struct proc_dir_entry *dgrp_proc_dir_entry;
+
+static int dgrp_add_id(long id);
+static int dgrp_remove_nd(struct nd_struct *nd);
+static void unregister_dgrp_device(struct proc_dir_entry *de);
+static void register_dgrp_device(struct nd_struct *node,
+ struct proc_dir_entry *root,
+ void (*register_hook)(struct proc_dir_entry *de));
+
+/* File operation declarations */
+static int dgrp_gen_proc_open(struct inode *, struct file *);
+static int dgrp_gen_proc_close(struct inode *, struct file *);
+static int parse_write_config(char *);
+
+
+static const struct file_operations dgrp_proc_file_ops = {
+ .owner = THIS_MODULE,
+ .open = dgrp_gen_proc_open,
+ .release = dgrp_gen_proc_close,
+};
+
+static struct inode_operations proc_inode_ops = {
+ .permission = dgrp_inode_permission
+};
+
+
+static void register_proc_table(struct dgrp_proc_entry *,
+ struct proc_dir_entry *);
+static void unregister_proc_table(struct dgrp_proc_entry *,
+ struct proc_dir_entry *);
+
+static struct dgrp_proc_entry dgrp_net_table[];
+static struct dgrp_proc_entry dgrp_mon_table[];
+static struct dgrp_proc_entry dgrp_ports_table[];
+static struct dgrp_proc_entry dgrp_dpa_table[];
+
+static ssize_t config_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *pos);
+
+static int nodeinfo_proc_open(struct inode *inode, struct file *file);
+static int info_proc_open(struct inode *inode, struct file *file);
+static int config_proc_open(struct inode *inode, struct file *file);
+
+static struct file_operations config_proc_file_ops = {
+ .owner = THIS_MODULE,
+ .open = config_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+ .write = config_proc_write
+};
+
+static struct file_operations info_proc_file_ops = {
+ .owner = THIS_MODULE,
+ .open = info_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static struct file_operations nodeinfo_proc_file_ops = {
+ .owner = THIS_MODULE,
+ .open = nodeinfo_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static struct dgrp_proc_entry dgrp_table[] = {
+ {
+ .id = DGRP_CONFIG,
+ .name = "config",
+ .mode = 0644,
+ .proc_file_ops = &config_proc_file_ops,
+ },
+ {
+ .id = DGRP_INFO,
+ .name = "info",
+ .mode = 0644,
+ .proc_file_ops = &info_proc_file_ops,
+ },
+ {
+ .id = DGRP_NODEINFO,
+ .name = "nodeinfo",
+ .mode = 0644,
+ .proc_file_ops = &nodeinfo_proc_file_ops,
+ },
+ {
+ .id = DGRP_NETDIR,
+ .name = "net",
+ .mode = 0500,
+ .child = dgrp_net_table
+ },
+ {
+ .id = DGRP_MONDIR,
+ .name = "mon",
+ .mode = 0500,
+ .child = dgrp_mon_table
+ },
+ {
+ .id = DGRP_PORTSDIR,
+ .name = "ports",
+ .mode = 0500,
+ .child = dgrp_ports_table
+ },
+ {
+ .id = DGRP_DPADIR,
+ .name = "dpa",
+ .mode = 0500,
+ .child = dgrp_dpa_table
+ }
+};
+
+static struct proc_dir_entry *net_entry_pointer;
+static struct proc_dir_entry *mon_entry_pointer;
+static struct proc_dir_entry *dpa_entry_pointer;
+static struct proc_dir_entry *ports_entry_pointer;
+
+static struct dgrp_proc_entry dgrp_net_table[] = {
+ {0}
+};
+
+static struct dgrp_proc_entry dgrp_mon_table[] = {
+ {0}
+};
+
+static struct dgrp_proc_entry dgrp_ports_table[] = {
+ {0}
+};
+
+static struct dgrp_proc_entry dgrp_dpa_table[] = {
+ {0}
+};
+
+void dgrp_unregister_proc(void)
+{
+ unregister_proc_table(dgrp_table, dgrp_proc_dir_entry);
+ net_entry_pointer = NULL;
+ mon_entry_pointer = NULL;
+ dpa_entry_pointer = NULL;
+ ports_entry_pointer = NULL;
+
+ if (dgrp_proc_dir_entry) {
+ remove_proc_entry(dgrp_proc_dir_entry->name,
+ dgrp_proc_dir_entry->parent);
+ dgrp_proc_dir_entry = NULL;
+ }
+
+}
+
+void dgrp_register_proc(void)
+{
+ /*
+ * Register /proc/dgrp
+ */
+ dgrp_proc_dir_entry = proc_create("dgrp", S_IFDIR, NULL,
+ &dgrp_proc_file_ops);
+ register_proc_table(dgrp_table, dgrp_proc_dir_entry);
+}
+
+/*
+ * /proc/sys support
+ */
+static int dgrp_proc_match(int len, const char *name, struct proc_dir_entry *de)
+{
+ if (!de || !de->low_ino)
+ return 0;
+ if (de->namelen != len)
+ return 0;
+ return !memcmp(name, de->name, len);
+}
+
+
+/*
+ * Scan the entries in table and add them all to /proc at the position
+ * referred to by "root"
+ */
+static void register_proc_table(struct dgrp_proc_entry *table,
+ struct proc_dir_entry *root)
+{
+ struct proc_dir_entry *de;
+ int len;
+ mode_t mode;
+
+ for (; table->id; table++) {
+ /* Can't do anything without a proc name. */
+ if (!table->name)
+ continue;
+
+ /* Maybe we can't do anything with it... */
+ if (!table->proc_file_ops &&
+ !table->child) {
+ pr_warn("dgrp: Can't register %s\n",
+ table->name);
+ continue;
+ }
+
+ len = strlen(table->name);
+ mode = table->mode;
+
+ de = NULL;
+ if (!table->child)
+ mode |= S_IFREG;
+ else {
+ mode |= S_IFDIR;
+ for (de = root->subdir; de; de = de->next) {
+ if (dgrp_proc_match(len, table->name, de))
+ break;
+ }
+ /* If the subdir exists already, de is non-NULL */
+ }
+
+ if (!de) {
+ de = create_proc_entry(table->name, mode, root);
+ if (!de)
+ continue;
+ de->data = (void *) table;
+ if (!table->child) {
+ de->proc_iops = &proc_inode_ops;
+ if (table->proc_file_ops)
+ de->proc_fops = table->proc_file_ops;
+ else
+ de->proc_fops = &dgrp_proc_file_ops;
+ }
+ }
+ table->de = de;
+ if (de->mode & S_IFDIR)
+ register_proc_table(table->child, de);
+
+ if (table->id == DGRP_NETDIR)
+ net_entry_pointer = de;
+
+ if (table->id == DGRP_MONDIR)
+ mon_entry_pointer = de;
+
+ if (table->id == DGRP_DPADIR)
+ dpa_entry_pointer = de;
+
+ if (table->id == DGRP_PORTSDIR)
+ ports_entry_pointer = de;
+ }
+}
+
+/*
+ * Unregister a /proc sysctl table and any subdirectories.
+ */
+static void unregister_proc_table(struct dgrp_proc_entry *table,
+ struct proc_dir_entry *root)
+{
+ struct proc_dir_entry *de;
+ struct nd_struct *tmp;
+
+ list_for_each_entry(tmp, &nd_struct_list, list) {
+ if ((table == dgrp_net_table) && (tmp->nd_net_de)) {
+ unregister_dgrp_device(tmp->nd_net_de);
+ dgrp_remove_node_class_sysfs_files(tmp);
+ }
+
+ if ((table == dgrp_mon_table) && (tmp->nd_mon_de))
+ unregister_dgrp_device(tmp->nd_mon_de);
+
+ if ((table == dgrp_dpa_table) && (tmp->nd_dpa_de))
+ unregister_dgrp_device(tmp->nd_dpa_de);
+
+ if ((table == dgrp_ports_table) && (tmp->nd_ports_de))
+ unregister_dgrp_device(tmp->nd_ports_de);
+ }
+
+ for (; table->id; table++) {
+ de = table->de;
+
+ if (!de)
+ continue;
+ if (de->mode & S_IFDIR) {
+ if (!table->child) {
+ pr_alert("dgrp: malformed sysctl tree on free\n");
+ continue;
+ }
+ unregister_proc_table(table->child, de);
+
+ /* Don't unregister directories which still have entries */
+ if (de->subdir)
+ continue;
+ }
+
+ /* Don't unregister proc entries that are still being used.. */
+ if ((atomic_read(&de->count)) != 1) {
+ pr_alert("proc entry %s in use, not removing\n",
+ de->name);
+ continue;
+ }
+
+ remove_proc_entry(de->name, de->parent);
+ table->de = NULL;
+ }
+}
+
+static int dgrp_gen_proc_open(struct inode *inode, struct file *file)
+{
+ struct proc_dir_entry *de;
+ struct dgrp_proc_entry *entry;
+ int ret = 0;
+
+ de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode);
+ if (!de || !de->data) {
+ ret = -ENXIO;
+ goto done;
+ }
+
+ entry = (struct dgrp_proc_entry *) de->data;
+ if (!entry) {
+ ret = -ENXIO;
+ goto done;
+ }
+
+ down(&entry->excl_sem);
+
+ if (entry->excl_cnt)
+ ret = -EBUSY;
+ else
+ entry->excl_cnt++;
+
+ up(&entry->excl_sem);
+
+done:
+ return ret;
+}
+
+static int dgrp_gen_proc_close(struct inode *inode, struct file *file)
+{
+ struct proc_dir_entry *de;
+ struct dgrp_proc_entry *entry;
+
+ de = (struct proc_dir_entry *) PDE(file->f_dentry->d_inode);
+ if (!de || !de->data)
+ goto done;
+
+ entry = (struct dgrp_proc_entry *) de->data;
+ if (!entry)
+ goto done;
+
+ down(&entry->excl_sem);
+
+ if (entry->excl_cnt)
+ entry->excl_cnt = 0;
+
+ up(&entry->excl_sem);
+
+done:
+ return 0;
+}
+
+static void *config_proc_start(struct seq_file *m, loff_t *pos)
+{
+ return seq_list_start_head(&nd_struct_list, *pos);
+}
+
+static void *config_proc_next(struct seq_file *p, void *v, loff_t *pos)
+{
+ return seq_list_next(v, &nd_struct_list, pos);
+}
+
+static void config_proc_stop(struct seq_file *m, void *v)
+{
+}
+
+static int config_proc_show(struct seq_file *m, void *v)
+{
+ struct nd_struct *nd;
+ char tmp_id[4];
+
+ if (v == &nd_struct_list) {
+ seq_puts(m, "#-----------------------------------------------------------------------------\n");
+ seq_puts(m, "# Avail\n");
+ seq_puts(m, "# ID Major State Ports\n");
+ return 0;
+ }
+
+ nd = list_entry(v, struct nd_struct, list);
+
+ ID_TO_CHAR(nd->nd_ID, tmp_id);
+
+ seq_printf(m, " %-2.2s %-5ld %-10.10s %-5d\n",
+ tmp_id,
+ nd->nd_major,
+ ND_STATE_STR(nd->nd_state),
+ nd->nd_chan_count);
+
+ return 0;
+}
+
+static const struct seq_operations proc_config_ops = {
+ .start = config_proc_start,
+ .next = config_proc_next,
+ .stop = config_proc_stop,
+ .show = config_proc_show
+};
+
+static int config_proc_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &proc_config_ops);
+}
+
+
+/*
+ * When writing configuration information, each "record" (i.e. each
+ * write) is treated as an independent request. See the "parse"
+ * description for more details.
+ */
+static ssize_t config_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *pos)
+{
+ ssize_t retval;
+ char *inbuf, *sp;
+ char *line, *ldelim;
+
+ if (count > 32768)
+ return -EINVAL;
+
+ inbuf = sp = vzalloc(count + 1);
+ if (!inbuf)
+ return -ENOMEM;
+
+ if (copy_from_user(inbuf, buffer, count)) {
+ retval = -EFAULT;
+ goto done;
+ }
+
+ inbuf[count] = 0;
+
+ ldelim = "\n";
+
+ line = strpbrk(sp, ldelim);
+ while (line) {
+ *line = 0;
+ retval = parse_write_config(sp);
+ if (retval)
+ goto done;
+
+ sp = line + 1;
+ line = strpbrk(sp, ldelim);
+ }
+
+ retval = count;
+done:
+ vfree(inbuf);
+ return retval;
+}
+
+/*
+ * ------------------------------------------------------------------------
+ *
+ * The following are the functions to parse input
+ *
+ * ------------------------------------------------------------------------
+ */
+static inline char *skip_past_ws(const char *str)
+{
+ while ((*str) && !isspace(*str))
+ ++str;
+
+ return skip_spaces(str);
+}
+
+static int parse_id(char **c, char *cID)
+{
+ int tmp = **c;
+
+ if (isalnum(tmp) || (tmp == '_'))
+ cID[0] = tmp;
+ else
+ return -EINVAL;
+
+ (*c)++; tmp = **c;
+
+ if (isalnum(tmp) || (tmp == '_')) {
+ cID[1] = tmp;
+ (*c)++;
+ } else
+ cID[1] = 0;
+
+ return 0;
+}
+
+static int parse_add_config(char *buf)
+{
+ char *c = buf;
+ int retval;
+ char cID[2];
+ long ID;
+
+ c = skip_past_ws(c);
+
+ retval = parse_id(&c, cID);
+ if (retval < 0)
+ return retval;
+
+ ID = CHAR_TO_ID(cID);
+
+ c = skip_past_ws(c);
+
+ return dgrp_add_id(ID);
+}
+
+static int parse_del_config(char *buf)
+{
+ char *c = buf;
+ int retval;
+ struct nd_struct *nd;
+ char cID[2];
+ long ID;
+ long major;
+
+ c = skip_past_ws(c);
+
+ retval = parse_id(&c, cID);
+ if (retval < 0)
+ return retval;
+
+ ID = CHAR_TO_ID(cID);
+
+ c = skip_past_ws(c);
+
+ retval = kstrtol(c, 10, &major);
+ if (retval)
+ return retval;
+
+ nd = nd_struct_get(major);
+ if (!nd)
+ return -EINVAL;
+
+ if ((nd->nd_major != major) || (nd->nd_ID != ID))
+ return -EINVAL;
+
+ return dgrp_remove_nd(nd);
+}
+
+static int parse_chg_config(char *buf)
+{
+ return -EINVAL;
+}
+
+/*
+ * The passed character buffer represents a single configuration request.
+ * If the first character is a "+", it is parsed as a request to add a
+ * PortServer
+ * If the first character is a "-", it is parsed as a request to delete a
+ * PortServer
+ * If the first character is a "*", it is parsed as a request to change a
+ * PortServer
+ * Any other character (including whitespace) causes the record to be
+ * ignored.
+ */
+static int parse_write_config(char *buf)
+{
+ int retval;
+
+ switch (buf[0]) {
+ case '+':
+ retval = parse_add_config(buf);
+ break;
+ case '-':
+ retval = parse_del_config(buf);
+ break;
+ case '*':
+ retval = parse_chg_config(buf);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+
+ return retval;
+}
+
+static int info_proc_show(struct seq_file *m, void *v)
+{
+ seq_printf(m, "version: %s\n", DIGI_VERSION);
+ seq_puts(m, "register_with_sysfs: 1\n");
+ seq_printf(m, "rawreadok: 0x%08x\t(%d)\n",
+ dgrp_rawreadok, dgrp_rawreadok);
+ seq_printf(m, "pollrate: 0x%08x\t(%d)\n",
+ dgrp_poll_tick, dgrp_poll_tick);
+
+ return 0;
+}
+
+static int info_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, info_proc_show, NULL);
+}
+
+
+static void *nodeinfo_start(struct seq_file *m, loff_t *pos)
+{
+ return seq_list_start_head(&nd_struct_list, *pos);
+}
+
+static void *nodeinfo_next(struct seq_file *p, void *v, loff_t *pos)
+{
+ return seq_list_next(v, &nd_struct_list, pos);
+}
+
+static void nodeinfo_stop(struct seq_file *m, void *v)
+{
+}
+
+static int nodeinfo_show(struct seq_file *m, void *v)
+{
+ struct nd_struct *nd;
+ char hwver[8];
+ char swver[8];
+ char tmp_id[4];
+
+ if (v == &nd_struct_list) {
+ seq_puts(m, "#-----------------------------------------------------------------------------\n");
+ seq_puts(m, "# HW HW SW\n");
+ seq_puts(m, "# ID State Version ID Version Description\n");
+ return 0;
+ }
+
+ nd = list_entry(v, struct nd_struct, list);
+
+ ID_TO_CHAR(nd->nd_ID, tmp_id);
+
+ if (nd->nd_state == NS_READY) {
+ sprintf(hwver, "%d.%d", (nd->nd_hw_ver >> 8) & 0xff,
+ nd->nd_hw_ver & 0xff);
+ sprintf(swver, "%d.%d", (nd->nd_sw_ver >> 8) & 0xff,
+ nd->nd_sw_ver & 0xff);
+ seq_printf(m, " %-2.2s %-10.10s %-7.7s %-3d %-7.7s %-35.35s\n",
+ tmp_id,
+ ND_STATE_STR(nd->nd_state),
+ hwver,
+ nd->nd_hw_id,
+ swver,
+ nd->nd_ps_desc);
+
+ } else {
+ seq_printf(m, " %-2.2s %-10.10s\n",
+ tmp_id,
+ ND_STATE_STR(nd->nd_state));
+ }
+
+ return 0;
+}
+
+
+static const struct seq_operations nodeinfo_ops = {
+ .start = nodeinfo_start,
+ .next = nodeinfo_next,
+ .stop = nodeinfo_stop,
+ .show = nodeinfo_show
+};
+
+static int nodeinfo_proc_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &nodeinfo_ops);
+}
+
+/**
+ * dgrp_add_id() -- creates new nd struct and adds it to list
+ * @id: id of device to add
+ */
+static int dgrp_add_id(long id)
+{
+ struct nd_struct *nd;
+ int ret;
+ int i;
+
+ nd = kzalloc(sizeof(struct nd_struct), GFP_KERNEL);
+ if (!nd)
+ return -ENOMEM;
+
+ nd->nd_major = 0;
+ nd->nd_ID = id;
+
+ spin_lock_init(&nd->nd_lock);
+
+ init_waitqueue_head(&nd->nd_tx_waitq);
+ init_waitqueue_head(&nd->nd_mon_wqueue);
+ init_waitqueue_head(&nd->nd_dpa_wqueue);
+ for (i = 0; i < SEQ_MAX; i++)
+ init_waitqueue_head(&nd->nd_seq_wque[i]);
+
+ /* setup the structures to get the major number */
+ ret = dgrp_tty_init(nd);
+ if (ret)
+ goto error_out;
+
+ nd->nd_major = nd->nd_serial_ttdriver->major;
+
+ ret = nd_struct_add(nd);
+ if (ret)
+ goto error_out;
+
+ register_dgrp_device(nd, net_entry_pointer, dgrp_register_net_hook);
+ register_dgrp_device(nd, mon_entry_pointer, dgrp_register_mon_hook);
+ register_dgrp_device(nd, dpa_entry_pointer, dgrp_register_dpa_hook);
+ register_dgrp_device(nd, ports_entry_pointer,
+ dgrp_register_ports_hook);
+
+ return 0;
+
+error_out:
+ kfree(nd);
+ return ret;
+
+}
+
+static int dgrp_remove_nd(struct nd_struct *nd)
+{
+ int ret;
+
+ /* Check to see if the selected structure is in use */
+ if (nd->nd_tty_ref_cnt)
+ return -EBUSY;
+
+ if (nd->nd_net_de) {
+ unregister_dgrp_device(nd->nd_net_de);
+ dgrp_remove_node_class_sysfs_files(nd);
+ }
+
+ if (nd->nd_mon_de)
+ unregister_dgrp_device(nd->nd_mon_de);
+
+ if (nd->nd_ports_de)
+ unregister_dgrp_device(nd->nd_ports_de);
+
+ if (nd->nd_dpa_de)
+ unregister_dgrp_device(nd->nd_dpa_de);
+
+ dgrp_tty_uninit(nd);
+
+ ret = nd_struct_del(nd);
+ if (ret)
+ return ret;
+
+ kfree(nd);
+ return 0;
+}
+
+static void register_dgrp_device(struct nd_struct *node,
+ struct proc_dir_entry *root,
+ void (*register_hook)(struct proc_dir_entry *de))
+{
+ char buf[3];
+ struct proc_dir_entry *de;
+
+ ID_TO_CHAR(node->nd_ID, buf);
+
+ de = create_proc_entry(buf, 0600 | S_IFREG, root);
+ if (!de)
+ return;
+
+ de->data = (void *) node;
+
+ if (register_hook)
+ register_hook(de);
+
+}
+
+static void unregister_dgrp_device(struct proc_dir_entry *de)
+{
+ if (!de)
+ return;
+
+ /* Don't unregister proc entries that are still being used.. */
+ if ((atomic_read(&de->count)) != 1) {
+ pr_alert("%s - proc entry %s in use. Not removing.\n",
+ __func__, de->name);
+ return;
+ }
+
+ remove_proc_entry(de->name, de->parent);
+ de = NULL;
+}
diff --git a/drivers/staging/dgrp/dgrp_sysfs.c b/drivers/staging/dgrp/dgrp_sysfs.c
new file mode 100644
index 000000000000..e5a3c88d016e
--- /dev/null
+++ b/drivers/staging/dgrp/dgrp_sysfs.c
@@ -0,0 +1,555 @@
+/*
+ * Copyright 2004 Digi International (www.digi.com)
+ * Scott H Kilau <Scott_Kilau at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, 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 "dgrp_common.h"
+
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/serial_reg.h>
+#include <linux/pci.h>
+#include <linux/kdev_t.h>
+
+
+#define PORTSERVER_DIVIDEND 1843200
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+#define SERIAL_TYPE_XPRINT 3
+
+
+static struct class *dgrp_class;
+static struct device *dgrp_class_nodes_dev;
+static struct device *dgrp_class_global_settings_dev;
+
+
+static ssize_t dgrp_class_version_show(struct class *class,
+ struct class_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", DIGI_VERSION);
+}
+static CLASS_ATTR(driver_version, 0400, dgrp_class_version_show, NULL);
+
+
+static ssize_t dgrp_class_register_with_sysfs_show(struct device *c,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "1\n");
+}
+static DEVICE_ATTR(register_with_sysfs, 0400,
+ dgrp_class_register_with_sysfs_show, NULL);
+
+
+static ssize_t dgrp_class_rawreadok_show(struct device *c,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", dgrp_rawreadok);
+}
+static ssize_t dgrp_class_rawreadok_store(struct device *c,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ sscanf(buf, "0x%x\n", &dgrp_rawreadok);
+ return count;
+}
+static DEVICE_ATTR(rawreadok, 0600, dgrp_class_rawreadok_show,
+ dgrp_class_rawreadok_store);
+
+
+static ssize_t dgrp_class_pollrate_show(struct device *c,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", dgrp_poll_tick);
+}
+
+static ssize_t dgrp_class_pollrate_store(struct device *c,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ sscanf(buf, "0x%x\n", &dgrp_poll_tick);
+ return count;
+}
+static DEVICE_ATTR(pollrate, 0600, dgrp_class_pollrate_show,
+ dgrp_class_pollrate_store);
+
+static struct attribute *dgrp_sysfs_global_settings_entries[] = {
+ &dev_attr_pollrate.attr,
+ &dev_attr_rawreadok.attr,
+ &dev_attr_register_with_sysfs.attr,
+ NULL
+};
+
+
+static struct attribute_group dgrp_global_settings_attribute_group = {
+ .name = NULL,
+ .attrs = dgrp_sysfs_global_settings_entries,
+};
+
+
+
+void dgrp_create_class_sysfs_files(void)
+{
+ int ret = 0;
+ int max_majors = 1U << (32 - MINORBITS);
+
+ dgrp_class = class_create(THIS_MODULE, "digi_realport");
+ ret = class_create_file(dgrp_class, &class_attr_driver_version);
+
+ dgrp_class_global_settings_dev = device_create(dgrp_class, NULL,
+ MKDEV(0, max_majors + 1), NULL, "driver_settings");
+
+ ret = sysfs_create_group(&dgrp_class_global_settings_dev->kobj,
+ &dgrp_global_settings_attribute_group);
+ if (ret) {
+ pr_alert("%s: failed to create sysfs global settings device attributes.\n",
+ __func__);
+ sysfs_remove_group(&dgrp_class_global_settings_dev->kobj,
+ &dgrp_global_settings_attribute_group);
+ return;
+ }
+
+ dgrp_class_nodes_dev = device_create(dgrp_class, NULL,
+ MKDEV(0, max_majors + 2), NULL, "nodes");
+
+}
+
+
+void dgrp_remove_class_sysfs_files(void)
+{
+ struct nd_struct *nd;
+ int max_majors = 1U << (32 - MINORBITS);
+
+ list_for_each_entry(nd, &nd_struct_list, list)
+ dgrp_remove_node_class_sysfs_files(nd);
+
+ sysfs_remove_group(&dgrp_class_global_settings_dev->kobj,
+ &dgrp_global_settings_attribute_group);
+
+ class_remove_file(dgrp_class, &class_attr_driver_version);
+
+ device_destroy(dgrp_class, MKDEV(0, max_majors + 1));
+ device_destroy(dgrp_class, MKDEV(0, max_majors + 2));
+ class_destroy(dgrp_class);
+}
+
+static ssize_t dgrp_node_state_show(struct device *c,
+ struct device_attribute *attr, char *buf)
+{
+ struct nd_struct *nd;
+
+ if (!c)
+ return 0;
+ nd = (struct nd_struct *) dev_get_drvdata(c);
+ if (!nd)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", ND_STATE_STR(nd->nd_state));
+}
+
+static DEVICE_ATTR(state, 0600, dgrp_node_state_show, NULL);
+
+static ssize_t dgrp_node_description_show(struct device *c,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct nd_struct *nd;
+
+ if (!c)
+ return 0;
+ nd = (struct nd_struct *) dev_get_drvdata(c);
+ if (!nd)
+ return 0;
+
+ if (nd->nd_state == NS_READY && nd->nd_ps_desc)
+ return snprintf(buf, PAGE_SIZE, "%s\n", nd->nd_ps_desc);
+ return 0;
+}
+static DEVICE_ATTR(description_info, 0600, dgrp_node_description_show, NULL);
+
+static ssize_t dgrp_node_hw_version_show(struct device *c,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct nd_struct *nd;
+
+ if (!c)
+ return 0;
+ nd = (struct nd_struct *) dev_get_drvdata(c);
+ if (!nd)
+ return 0;
+
+ if (nd->nd_state == NS_READY)
+ return snprintf(buf, PAGE_SIZE, "%d.%d\n",
+ (nd->nd_hw_ver >> 8) & 0xff,
+ nd->nd_hw_ver & 0xff);
+
+ return 0;
+}
+static DEVICE_ATTR(hw_version_info, 0600, dgrp_node_hw_version_show, NULL);
+
+static ssize_t dgrp_node_hw_id_show(struct device *c,
+ struct device_attribute *attr, char *buf)
+{
+ struct nd_struct *nd;
+
+ if (!c)
+ return 0;
+ nd = (struct nd_struct *) dev_get_drvdata(c);
+ if (!nd)
+ return 0;
+
+
+ if (nd->nd_state == NS_READY)
+ return snprintf(buf, PAGE_SIZE, "%d\n", nd->nd_hw_id);
+ return 0;
+}
+static DEVICE_ATTR(hw_id_info, 0600, dgrp_node_hw_id_show, NULL);
+
+static ssize_t dgrp_node_sw_version_show(struct device *c,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct nd_struct *nd;
+
+ if (!c)
+ return 0;
+
+ nd = (struct nd_struct *) dev_get_drvdata(c);
+ if (!nd)
+ return 0;
+
+ if (nd->nd_state == NS_READY)
+ return snprintf(buf, PAGE_SIZE, "%d.%d\n",
+ (nd->nd_sw_ver >> 8) & 0xff,
+ nd->nd_sw_ver & 0xff);
+
+ return 0;
+}
+static DEVICE_ATTR(sw_version_info, 0600, dgrp_node_sw_version_show, NULL);
+
+
+static struct attribute *dgrp_sysfs_node_entries[] = {
+ &dev_attr_state.attr,
+ &dev_attr_description_info.attr,
+ &dev_attr_hw_version_info.attr,
+ &dev_attr_hw_id_info.attr,
+ &dev_attr_sw_version_info.attr,
+ NULL
+};
+
+
+static struct attribute_group dgrp_node_attribute_group = {
+ .name = NULL,
+ .attrs = dgrp_sysfs_node_entries,
+};
+
+
+void dgrp_create_node_class_sysfs_files(struct nd_struct *nd)
+{
+ int ret;
+ char name[10];
+
+ if (nd->nd_ID)
+ ID_TO_CHAR(nd->nd_ID, name);
+ else
+ sprintf(name, "node%ld", nd->nd_major);
+
+ nd->nd_class_dev = device_create(dgrp_class, dgrp_class_nodes_dev,
+ MKDEV(0, nd->nd_major), NULL, name);
+
+ ret = sysfs_create_group(&nd->nd_class_dev->kobj,
+ &dgrp_node_attribute_group);
+
+ if (ret) {
+ pr_alert("%s: failed to create sysfs node device attributes.\n",
+ __func__);
+ sysfs_remove_group(&nd->nd_class_dev->kobj,
+ &dgrp_node_attribute_group);
+ return;
+ }
+
+ dev_set_drvdata(nd->nd_class_dev, nd);
+
+}
+
+
+void dgrp_remove_node_class_sysfs_files(struct nd_struct *nd)
+{
+ if (nd->nd_class_dev) {
+ sysfs_remove_group(&nd->nd_class_dev->kobj,
+ &dgrp_node_attribute_group);
+
+ device_destroy(dgrp_class, MKDEV(0, nd->nd_major));
+ nd->nd_class_dev = NULL;
+ }
+}
+
+
+
+static ssize_t dgrp_tty_state_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct un_struct *un;
+
+ if (!d)
+ return 0;
+ un = (struct un_struct *) dev_get_drvdata(d);
+ if (!un)
+ return 0;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ un->un_open_count ? "Open" : "Closed");
+}
+static DEVICE_ATTR(state_info, 0600, dgrp_tty_state_show, NULL);
+
+static ssize_t dgrp_tty_baud_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ch_struct *ch;
+ struct un_struct *un;
+
+ if (!d)
+ return 0;
+ un = (struct un_struct *) dev_get_drvdata(d);
+ if (!un)
+ return 0;
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ un->un_open_count ? (PORTSERVER_DIVIDEND / ch->ch_s_brate) : 0);
+}
+static DEVICE_ATTR(baud_info, 0400, dgrp_tty_baud_show, NULL);
+
+
+static ssize_t dgrp_tty_msignals_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ch_struct *ch;
+ struct un_struct *un;
+
+ if (!d)
+ return 0;
+ un = (struct un_struct *) dev_get_drvdata(d);
+ if (!un)
+ return 0;
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+
+ if (ch->ch_open_count) {
+ return snprintf(buf, PAGE_SIZE, "%s %s %s %s %s %s\n",
+ (ch->ch_s_mlast & DM_RTS) ? "RTS" : "",
+ (ch->ch_s_mlast & DM_CTS) ? "CTS" : "",
+ (ch->ch_s_mlast & DM_DTR) ? "DTR" : "",
+ (ch->ch_s_mlast & DM_DSR) ? "DSR" : "",
+ (ch->ch_s_mlast & DM_CD) ? "DCD" : "",
+ (ch->ch_s_mlast & DM_RI) ? "RI" : "");
+ }
+ return 0;
+}
+static DEVICE_ATTR(msignals_info, 0400, dgrp_tty_msignals_show, NULL);
+
+
+static ssize_t dgrp_tty_iflag_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ch_struct *ch;
+ struct un_struct *un;
+
+ if (!d)
+ return 0;
+ un = (struct un_struct *) dev_get_drvdata(d);
+ if (!un)
+ return 0;
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_s_iflag);
+}
+static DEVICE_ATTR(iflag_info, 0600, dgrp_tty_iflag_show, NULL);
+
+
+static ssize_t dgrp_tty_cflag_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ch_struct *ch;
+ struct un_struct *un;
+
+ if (!d)
+ return 0;
+ un = (struct un_struct *) dev_get_drvdata(d);
+ if (!un)
+ return 0;
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_s_cflag);
+}
+static DEVICE_ATTR(cflag_info, 0600, dgrp_tty_cflag_show, NULL);
+
+
+static ssize_t dgrp_tty_oflag_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ch_struct *ch;
+ struct un_struct *un;
+
+ if (!d)
+ return 0;
+ un = (struct un_struct *) dev_get_drvdata(d);
+ if (!un)
+ return 0;
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_s_oflag);
+}
+static DEVICE_ATTR(oflag_info, 0600, dgrp_tty_oflag_show, NULL);
+
+
+static ssize_t dgrp_tty_digi_flag_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ch_struct *ch;
+ struct un_struct *un;
+
+ if (!d)
+ return 0;
+ un = (struct un_struct *) dev_get_drvdata(d);
+ if (!un)
+ return 0;
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%x\n", ch->ch_digi.digi_flags);
+}
+static DEVICE_ATTR(digi_flag_info, 0600, dgrp_tty_digi_flag_show, NULL);
+
+
+static ssize_t dgrp_tty_rxcount_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ch_struct *ch;
+ struct un_struct *un;
+
+ if (!d)
+ return 0;
+ un = (struct un_struct *) dev_get_drvdata(d);
+ if (!un)
+ return 0;
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_rxcount);
+}
+static DEVICE_ATTR(rxcount_info, 0600, dgrp_tty_rxcount_show, NULL);
+
+
+static ssize_t dgrp_tty_txcount_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct ch_struct *ch;
+ struct un_struct *un;
+
+ if (!d)
+ return 0;
+ un = (struct un_struct *) dev_get_drvdata(d);
+ if (!un)
+ return 0;
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+ return snprintf(buf, PAGE_SIZE, "%d\n", ch->ch_txcount);
+}
+static DEVICE_ATTR(txcount_info, 0600, dgrp_tty_txcount_show, NULL);
+
+
+static ssize_t dgrp_tty_name_show(struct device *d,
+ struct device_attribute *attr, char *buf)
+{
+ struct nd_struct *nd;
+ struct ch_struct *ch;
+ struct un_struct *un;
+ char name[10];
+
+ if (!d)
+ return 0;
+ un = (struct un_struct *) dev_get_drvdata(d);
+ if (!un)
+ return 0;
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+ nd = ch->ch_nd;
+ if (!nd)
+ return 0;
+
+ ID_TO_CHAR(nd->nd_ID, name);
+
+ return snprintf(buf, PAGE_SIZE, "%s%s%02d\n",
+ un->un_type == SERIAL_TYPE_XPRINT ? "pr" : "tty",
+ name, ch->ch_portnum);
+}
+static DEVICE_ATTR(custom_name, 0600, dgrp_tty_name_show, NULL);
+
+
+static struct attribute *dgrp_sysfs_tty_entries[] = {
+ &dev_attr_state_info.attr,
+ &dev_attr_baud_info.attr,
+ &dev_attr_msignals_info.attr,
+ &dev_attr_iflag_info.attr,
+ &dev_attr_cflag_info.attr,
+ &dev_attr_oflag_info.attr,
+ &dev_attr_digi_flag_info.attr,
+ &dev_attr_rxcount_info.attr,
+ &dev_attr_txcount_info.attr,
+ &dev_attr_custom_name.attr,
+ NULL
+};
+
+
+static struct attribute_group dgrp_tty_attribute_group = {
+ .name = NULL,
+ .attrs = dgrp_sysfs_tty_entries,
+};
+
+
+void dgrp_create_tty_sysfs(struct un_struct *un, struct device *c)
+{
+ int ret;
+
+ ret = sysfs_create_group(&c->kobj, &dgrp_tty_attribute_group);
+ if (ret) {
+ pr_alert("%s: failed to create sysfs tty device attributes.\n",
+ __func__);
+ sysfs_remove_group(&c->kobj, &dgrp_tty_attribute_group);
+ return;
+ }
+
+ dev_set_drvdata(c, un);
+
+}
+
+
+void dgrp_remove_tty_sysfs(struct device *c)
+{
+ sysfs_remove_group(&c->kobj, &dgrp_tty_attribute_group);
+}
diff --git a/drivers/staging/dgrp/dgrp_tty.c b/drivers/staging/dgrp/dgrp_tty.c
new file mode 100644
index 000000000000..7d7de873870c
--- /dev/null
+++ b/drivers/staging/dgrp/dgrp_tty.c
@@ -0,0 +1,3331 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ * Gene Olson <Gene_Olson at digi dot com>
+ * James Puzzo <jamesp at digi dot com>
+ * Jeff Randall
+ * Scott Kilau <scottk at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, 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.
+ *
+ */
+
+/*
+ *
+ * Filename:
+ *
+ * dgrp_tty.c
+ *
+ * Description:
+ *
+ * This file implements the tty driver functionality for the
+ * RealPort driver software.
+ *
+ * Author:
+ *
+ * James A. Puzzo
+ * Ann-Marie Westgate
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/sched.h>
+
+#include "dgrp_common.h"
+
+#ifndef _POSIX_VDISABLE
+#define _POSIX_VDISABLE ('\0')
+#endif
+
+/*
+ * forward declarations
+ */
+
+static void drp_param(struct ch_struct *);
+static void dgrp_tty_close(struct tty_struct *, struct file *);
+
+/* ioctl helper functions */
+static int set_modem_info(struct ch_struct *, unsigned int, unsigned int *);
+static int get_modem_info(struct ch_struct *, unsigned int *);
+static void dgrp_set_custom_speed(struct ch_struct *, int);
+static int dgrp_tty_digigetedelay(struct tty_struct *, int *);
+static int dgrp_tty_digisetedelay(struct tty_struct *, int *);
+static int dgrp_send_break(struct ch_struct *, int);
+
+static ushort tty_to_ch_flags(struct tty_struct *, char);
+static tcflag_t ch_to_tty_flags(unsigned short, char);
+
+static void dgrp_tty_input_start(struct tty_struct *);
+static void dgrp_tty_input_stop(struct tty_struct *);
+
+static void drp_wmove(struct ch_struct *, int, void*, int);
+
+static int dgrp_tty_open(struct tty_struct *, struct file *);
+static void dgrp_tty_close(struct tty_struct *, struct file *);
+static int dgrp_tty_write(struct tty_struct *, const unsigned char *, int);
+static int dgrp_tty_write_room(struct tty_struct *);
+static void dgrp_tty_flush_buffer(struct tty_struct *);
+static int dgrp_tty_chars_in_buffer(struct tty_struct *);
+static int dgrp_tty_ioctl(struct tty_struct *, unsigned int, unsigned long);
+static void dgrp_tty_set_termios(struct tty_struct *, struct ktermios *);
+static void dgrp_tty_stop(struct tty_struct *);
+static void dgrp_tty_start(struct tty_struct *);
+static void dgrp_tty_throttle(struct tty_struct *);
+static void dgrp_tty_unthrottle(struct tty_struct *);
+static void dgrp_tty_hangup(struct tty_struct *);
+static int dgrp_tty_put_char(struct tty_struct *, unsigned char);
+static int dgrp_tty_tiocmget(struct tty_struct *);
+static int dgrp_tty_tiocmset(struct tty_struct *, unsigned int, unsigned int);
+static int dgrp_tty_send_break(struct tty_struct *, int);
+static void dgrp_tty_send_xchar(struct tty_struct *, char);
+
+/*
+ * tty defines
+ */
+#define SERIAL_TYPE_NORMAL 1
+#define SERIAL_TYPE_CALLOUT 2
+#define SERIAL_TYPE_XPRINT 3
+
+
+/*
+ * tty globals/statics
+ */
+
+
+#define PORTSERVER_DIVIDEND 1843200
+
+/*
+ * Default transparent print information.
+ */
+static struct digi_struct digi_init = {
+ .digi_flags = DIGI_COOK, /* Flags */
+ .digi_maxcps = 100, /* Max CPS */
+ .digi_maxchar = 50, /* Max chars in print queue */
+ .digi_bufsize = 100, /* Printer buffer size */
+ .digi_onlen = 4, /* size of printer on string */
+ .digi_offlen = 4, /* size of printer off string */
+ .digi_onstr = "\033[5i", /* ANSI printer on string */
+ .digi_offstr = "\033[4i", /* ANSI printer off string */
+ .digi_term = "ansi" /* default terminal type */
+};
+
+/*
+ * Define a local default termios struct. All ports will be created
+ * with this termios initially.
+ *
+ * This defines a raw port at 9600 baud, 8 data bits, no parity,
+ * 1 stop bit.
+ */
+static struct ktermios DefaultTermios = {
+ .c_iflag = (ICRNL | IXON),
+ .c_oflag = (OPOST | ONLCR),
+ .c_cflag = (B9600 | CS8 | CREAD | HUPCL | CLOCAL),
+ .c_lflag = (ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL
+ | ECHOKE | IEXTEN),
+ .c_cc = INIT_C_CC,
+ .c_line = 0,
+};
+
+/* Define our tty operations struct */
+static const struct tty_operations dgrp_tty_ops = {
+ .open = dgrp_tty_open,
+ .close = dgrp_tty_close,
+ .write = dgrp_tty_write,
+ .write_room = dgrp_tty_write_room,
+ .flush_buffer = dgrp_tty_flush_buffer,
+ .chars_in_buffer = dgrp_tty_chars_in_buffer,
+ .flush_chars = NULL,
+ .ioctl = dgrp_tty_ioctl,
+ .set_termios = dgrp_tty_set_termios,
+ .stop = dgrp_tty_stop,
+ .start = dgrp_tty_start,
+ .throttle = dgrp_tty_throttle,
+ .unthrottle = dgrp_tty_unthrottle,
+ .hangup = dgrp_tty_hangup,
+ .put_char = dgrp_tty_put_char,
+ .tiocmget = dgrp_tty_tiocmget,
+ .tiocmset = dgrp_tty_tiocmset,
+ .break_ctl = dgrp_tty_send_break,
+ .send_xchar = dgrp_tty_send_xchar
+};
+
+
+static int calc_baud_rate(struct un_struct *un)
+{
+ int i;
+ int brate;
+
+ struct baud_rates {
+ unsigned int rate;
+ unsigned int cflag;
+ };
+
+ static struct baud_rates baud_rates[] = {
+ { 921600, B921600 },
+ { 460800, B460800 },
+ { 230400, B230400 },
+ { 115200, B115200 },
+ { 57600, B57600 },
+ { 38400, B38400 },
+ { 19200, B19200 },
+ { 9600, B9600 },
+ { 4800, B4800 },
+ { 2400, B2400 },
+ { 1200, B1200 },
+ { 600, B600 },
+ { 300, B300 },
+ { 200, B200 },
+ { 150, B150 },
+ { 134, B134 },
+ { 110, B110 },
+ { 75, B75 },
+ { 50, B50 },
+ { 0, B9600 }
+ };
+
+ brate = C_BAUD(un->un_tty);
+
+ for (i = 0; baud_rates[i].rate; i++) {
+ if (baud_rates[i].cflag == brate)
+ break;
+ }
+
+ return baud_rates[i].rate;
+}
+
+static int calc_fastbaud_rate(struct un_struct *un, struct ktermios *uts)
+{
+ int i;
+ int brate;
+
+ ulong bauds[2][16] = {
+ { /* fastbaud*/
+ 0, 57600, 76800, 115200,
+ 131657, 153600, 230400, 460800,
+ 921600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 },
+ { /* fastbaud & CBAUDEX */
+ 0, 57600, 115200, 230400,
+ 460800, 150, 200, 921600,
+ 600, 1200, 1800, 2400,
+ 4800, 9600, 19200, 38400 }
+ };
+
+ brate = C_BAUD(un->un_tty) & 0xff;
+
+ i = (uts->c_cflag & CBAUDEX) ? 1 : 0;
+
+
+ if ((i >= 0) && (i < 2) && (brate >= 0) && (brate < 16))
+ brate = bauds[i][brate];
+ else
+ brate = 0;
+
+ return brate;
+}
+
+/**
+ * drp_param() -- send parameter values to be sent to the node
+ * @ch: channel structure of port to modify
+ *
+ * Interprets the tty and modem changes made by an application
+ * program (by examining the termios structures) and sets up
+ * parameter values to be sent to the node.
+ */
+static void drp_param(struct ch_struct *ch)
+{
+ struct nd_struct *nd;
+ struct un_struct *un;
+ int brate;
+ int mflow;
+ int xflag;
+ int iflag;
+ struct ktermios *tts, *pts, *uts;
+
+ nd = ch->ch_nd;
+
+ /*
+ * If the terminal device is open, use it to set up all tty
+ * modes and functions. Otherwise use the printer device.
+ */
+
+ if (ch->ch_tun.un_open_count) {
+
+ un = &ch->ch_tun;
+ tts = &ch->ch_tun.un_tty->termios;
+
+ /*
+ * If both devices are open, copy critical line
+ * parameters from the tty device to the printer,
+ * so that if the tty is closed, the printer will
+ * continue without disruption.
+ */
+
+ if (ch->ch_pun.un_open_count) {
+
+ pts = &ch->ch_pun.un_tty->termios;
+
+ pts->c_cflag ^=
+ (pts->c_cflag ^ tts->c_cflag) &
+ (CBAUD | CSIZE | CSTOPB | CREAD | PARENB |
+ PARODD | HUPCL | CLOCAL);
+
+ pts->c_iflag ^=
+ (pts->c_iflag ^ tts->c_iflag) &
+ (IGNBRK | BRKINT | IGNPAR | PARMRK | INPCK |
+ ISTRIP | IXON | IXANY | IXOFF);
+
+ pts->c_cc[VSTART] = tts->c_cc[VSTART];
+ pts->c_cc[VSTOP] = tts->c_cc[VSTOP];
+ }
+ } else if (ch->ch_pun.un_open_count == 0) {
+ pr_warn("%s - ch_pun.un_open_count shouldn't be 0\n",
+ __func__);
+ return;
+ } else {
+ un = &ch->ch_pun;
+ }
+
+ uts = &un->un_tty->termios;
+
+ /*
+ * Determine if FAST writes can be performed.
+ */
+
+ if ((ch->ch_digi.digi_flags & DIGI_COOK) != 0 &&
+ (ch->ch_tun.un_open_count != 0) &&
+ !((un->un_tty)->ldisc->ops->flags & LDISC_FLAG_DEFINED) &&
+ !(L_XCASE(un->un_tty))) {
+ ch->ch_flag |= CH_FAST_WRITE;
+ } else {
+ ch->ch_flag &= ~CH_FAST_WRITE;
+ }
+
+ /*
+ * If FAST writes can be performed, and OPOST is on in the
+ * terminal device, do OPOST handling in the server.
+ */
+
+ if ((ch->ch_flag & CH_FAST_WRITE) &&
+ O_OPOST(un->un_tty) != 0) {
+ int oflag = tty_to_ch_flags(un->un_tty, 'o');
+
+ /* add to ch_ocook any processing flags set in the termio */
+ ch->ch_ocook |= oflag & (OF_OLCUC |
+ OF_ONLCR |
+ OF_OCRNL |
+ OF_ONLRET |
+ OF_TABDLY);
+
+ /*
+ * the hpux driver clears any flags set in ch_ocook
+ * from the termios oflag. It is STILL reported though
+ * by a TCGETA
+ */
+
+ oflag = ch_to_tty_flags(ch->ch_ocook, 'o');
+ uts->c_oflag &= ~oflag;
+
+ } else {
+ /* clear the ch->ch_ocook flag */
+ int oflag = ch_to_tty_flags(ch->ch_ocook, 'o');
+ uts->c_oflag |= oflag;
+ ch->ch_ocook = 0;
+ }
+
+ ch->ch_oflag = ch->ch_ocook;
+
+
+ ch->ch_flag &= ~CH_FAST_READ;
+
+ /*
+ * Generate channel flags
+ */
+
+ if (C_BAUD(un->un_tty) == B0) {
+ if (!(ch->ch_flag & CH_BAUD0)) {
+ /* TODO : the HPUX driver flushes line */
+ /* TODO : discipline, I assume I don't have to */
+
+ ch->ch_tout = ch->ch_tin;
+ ch->ch_rout = ch->ch_rin;
+
+ ch->ch_break_time = 0;
+
+ ch->ch_send |= RR_TX_FLUSH | RR_RX_FLUSH;
+
+ ch->ch_mout &= ~(DM_DTR | DM_RTS);
+
+ ch->ch_flag |= CH_BAUD0;
+ }
+ } else if (ch->ch_custom_speed) {
+ ch->ch_brate = PORTSERVER_DIVIDEND / ch->ch_custom_speed ;
+
+ if (ch->ch_flag & CH_BAUD0) {
+ ch->ch_mout |= DM_DTR | DM_RTS;
+
+ ch->ch_flag &= ~CH_BAUD0;
+ }
+ } else {
+ /*
+ * Baud rate mapping.
+ *
+ * If FASTBAUD isn't on, we can scan the new baud rate list
+ * as required.
+ *
+ * However, if FASTBAUD is on, we must go to the old
+ * baud rate mapping that existed many many moons ago,
+ * for compatibility reasons.
+ */
+
+ if (!(ch->ch_digi.digi_flags & DIGI_FAST))
+ brate = calc_baud_rate(un);
+ else
+ brate = calc_fastbaud_rate(un, uts);
+
+ if (brate == 0)
+ brate = 9600;
+
+ ch->ch_brate = PORTSERVER_DIVIDEND / brate;
+
+ if (ch->ch_flag & CH_BAUD0) {
+ ch->ch_mout |= DM_DTR | DM_RTS;
+
+ ch->ch_flag &= ~CH_BAUD0;
+ }
+ }
+
+ /*
+ * Generate channel cflags from the termio.
+ */
+
+ ch->ch_cflag = tty_to_ch_flags(un->un_tty, 'c');
+
+ /*
+ * Generate channel iflags from the termio.
+ */
+
+ iflag = (int) tty_to_ch_flags(un->un_tty, 'i');
+
+ if (START_CHAR(un->un_tty) == _POSIX_VDISABLE ||
+ STOP_CHAR(un->un_tty) == _POSIX_VDISABLE) {
+ iflag &= ~(IF_IXON | IF_IXANY | IF_IXOFF);
+ }
+
+ ch->ch_iflag = iflag;
+
+ /*
+ * Generate flow control characters
+ */
+
+ /*
+ * From the POSIX.1 spec (7.1.2.6): "If {_POSIX_VDISABLE}
+ * is defined for the terminal device file, and the value
+ * of one of the changable special control characters (see
+ * 7.1.1.9) is {_POSIX_VDISABLE}, that function shall be
+ * disabled, that is, no input data shall be recognized as
+ * the disabled special character."
+ *
+ * OK, so we don't ever assign S/DXB XON or XOFF to _POSIX_VDISABLE.
+ */
+
+ if (uts->c_cc[VSTART] != _POSIX_VDISABLE)
+ ch->ch_xon = uts->c_cc[VSTART];
+ if (uts->c_cc[VSTOP] != _POSIX_VDISABLE)
+ ch->ch_xoff = uts->c_cc[VSTOP];
+
+ ch->ch_lnext = (uts->c_cc[VLNEXT] == _POSIX_VDISABLE ? 0 :
+ uts->c_cc[VLNEXT]);
+
+ /*
+ * Also, if either c_cc[START] or c_cc[STOP] is set to
+ * _POSIX_VDISABLE, we can't really do software flow
+ * control--in either direction--so we turn it off as
+ * far as S/DXB is concerned. In essence, if you disable
+ * one, you disable the other too.
+ */
+ if ((uts->c_cc[VSTART] == _POSIX_VDISABLE) ||
+ (uts->c_cc[VSTOP] == _POSIX_VDISABLE))
+ ch->ch_iflag &= ~(IF_IXOFF | IF_IXON);
+
+ /*
+ * Update xflags.
+ */
+
+ xflag = 0;
+
+ if (ch->ch_digi.digi_flags & DIGI_AIXON)
+ xflag = XF_XIXON;
+
+ if ((ch->ch_xxon == _POSIX_VDISABLE) ||
+ (ch->ch_xxoff == _POSIX_VDISABLE))
+ xflag &= ~XF_XIXON;
+
+ ch->ch_xflag = xflag;
+
+
+ /*
+ * Figure effective DCD value.
+ */
+
+ if (C_CLOCAL(un->un_tty))
+ ch->ch_flag |= CH_CLOCAL;
+ else
+ ch->ch_flag &= ~CH_CLOCAL;
+
+ /*
+ * Check modem signals
+ */
+
+ dgrp_carrier(ch);
+
+ /*
+ * Get hardware handshake value.
+ */
+
+ mflow = 0;
+
+ if (C_CRTSCTS(un->un_tty))
+ mflow |= (DM_RTS | DM_CTS);
+
+ if (ch->ch_digi.digi_flags & RTSPACE)
+ mflow |= DM_RTS;
+
+ if (ch->ch_digi.digi_flags & DTRPACE)
+ mflow |= DM_DTR;
+
+ if (ch->ch_digi.digi_flags & CTSPACE)
+ mflow |= DM_CTS;
+
+ if (ch->ch_digi.digi_flags & DSRPACE)
+ mflow |= DM_DSR;
+
+ if (ch->ch_digi.digi_flags & DCDPACE)
+ mflow |= DM_CD;
+
+ if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE)
+ mflow |= DM_RTS_TOGGLE;
+
+ ch->ch_mflow = mflow;
+
+ /*
+ * Send the changes to the server.
+ */
+
+ ch->ch_flag |= CH_PARAM;
+ (ch->ch_nd)->nd_tx_work = 1;
+
+ if (waitqueue_active(&ch->ch_flag_wait))
+ wake_up_interruptible(&ch->ch_flag_wait);
+}
+
+/*
+ * This function is just used as a callback for timeouts
+ * waiting on the ch_sleep flag.
+ */
+static void wake_up_drp_sleep_timer(unsigned long ptr)
+{
+ struct ch_struct *ch = (struct ch_struct *) ptr;
+ if (ch)
+ wake_up(&ch->ch_sleep);
+}
+
+
+/*
+ * Set up our own sleep that can't be cancelled
+ * until our timeout occurs.
+ */
+static void drp_my_sleep(struct ch_struct *ch)
+{
+ struct timer_list drp_wakeup_timer;
+ DECLARE_WAITQUEUE(wait, current);
+
+ /*
+ * First make sure we're ready to receive the wakeup.
+ */
+
+ add_wait_queue(&ch->ch_sleep, &wait);
+ current->state = TASK_UNINTERRUPTIBLE;
+
+ /*
+ * Since we are uninterruptible, set a timer to
+ * unset the uninterruptable state in 1 second.
+ */
+
+ init_timer(&drp_wakeup_timer);
+ drp_wakeup_timer.function = wake_up_drp_sleep_timer;
+ drp_wakeup_timer.data = (unsigned long) ch;
+ drp_wakeup_timer.expires = jiffies + (1 * HZ);
+ add_timer(&drp_wakeup_timer);
+
+ schedule();
+
+ del_timer(&drp_wakeup_timer);
+
+ remove_wait_queue(&ch->ch_sleep, &wait);
+}
+
+/*
+ * dgrp_tty_open()
+ *
+ * returns:
+ * -EBUSY - this is a callout device and the normal device is active
+ * - there is an error in opening the tty
+ * -ENODEV - the channel does not exist
+ * -EAGAIN - we are in the middle of hanging up or closing
+ * - IMMEDIATE_OPEN fails
+ * -ENXIO or -EAGAIN
+ * - if the port is outside physical range
+ * -EINTR - the open is interrupted
+ *
+ */
+static int dgrp_tty_open(struct tty_struct *tty, struct file *file)
+{
+ int retval = 0;
+ struct nd_struct *nd;
+ struct ch_struct *ch;
+ struct un_struct *un;
+ int port;
+ int delay_error;
+ int otype;
+ int unf;
+ int wait_carrier;
+ int category;
+ int counts_were_incremented = 0;
+ ulong lock_flags;
+ DECLARE_WAITQUEUE(wait, current);
+
+ /*
+ * Do some initial checks to see if the node and port exist
+ */
+
+ nd = nd_struct_get(MAJOR(tty_devnum(tty)));
+ port = PORT_NUM(MINOR(tty_devnum(tty)));
+ category = OPEN_CATEGORY(MINOR(tty_devnum(tty)));
+
+ if (!nd)
+ return -ENODEV;
+
+ if (port >= CHAN_MAX)
+ return -ENODEV;
+
+ /*
+ * The channel exists.
+ */
+
+ ch = nd->nd_chan + port;
+
+ un = IS_PRINT(MINOR(tty_devnum(tty))) ? &ch->ch_pun : &ch->ch_tun;
+ un->un_tty = tty;
+ tty->driver_data = un;
+
+ /*
+ * If we are in the middle of hanging up,
+ * then return an error
+ */
+ if (tty_hung_up_p(file)) {
+ retval = ((un->un_flag & UN_HUP_NOTIFY) ?
+ -EAGAIN : -ERESTARTSYS);
+ goto done;
+ }
+
+ /*
+ * If the port is in the middle of closing, then block
+ * until it is done, then try again.
+ */
+ retval = wait_event_interruptible(un->un_close_wait,
+ ((un->un_flag & UN_CLOSING) == 0));
+
+ if (retval)
+ goto done;
+
+ /*
+ * If the port is in the middle of a reopen after a network disconnect,
+ * wait until it is done, then try again.
+ */
+ retval = wait_event_interruptible(ch->ch_flag_wait,
+ ((ch->ch_flag & CH_PORT_GONE) == 0));
+
+ if (retval)
+ goto done;
+
+ /*
+ * If this is a callout device, then just make sure the normal
+ * device isn't being used.
+ */
+
+ if (tty->driver->subtype == SERIAL_TYPE_CALLOUT) {
+ if (un->un_flag & UN_NORMAL_ACTIVE) {
+ retval = -EBUSY;
+ goto done;
+ } else {
+ un->un_flag |= UN_CALLOUT_ACTIVE;
+ }
+ }
+
+ /*
+ * Loop waiting until the open can be successfully completed.
+ */
+
+ spin_lock_irqsave(&nd->nd_lock, lock_flags);
+
+ nd->nd_tx_work = 1;
+
+ for (;;) {
+ wait_carrier = 0;
+
+ /*
+ * Determine the open type from the flags provided.
+ */
+
+ /*
+ * If the port is not enabled, then exit
+ */
+ if (test_bit(TTY_IO_ERROR, &tty->flags)) {
+ /* there was an error in opening the tty */
+ if (un->un_flag & UN_CALLOUT_ACTIVE)
+ retval = -EBUSY;
+ else
+ un->un_flag |= UN_NORMAL_ACTIVE;
+ goto unlock;
+ }
+
+ if (file->f_flags & O_NONBLOCK) {
+
+ /*
+ * if the O_NONBLOCK is set, errors on read and write
+ * must return -EAGAIN immediately and NOT sleep
+ * on the waitqs.
+ */
+ otype = OTYPE_IMMEDIATE;
+ delay_error = -EAGAIN;
+
+ } else if (!OPEN_WAIT_AVAIL(category) ||
+ (file->f_flags & O_NDELAY) != 0) {
+ otype = OTYPE_IMMEDIATE;
+ delay_error = -EBUSY;
+
+ } else if (!OPEN_WAIT_CARRIER(category) ||
+ ((ch->ch_digi.digi_flags & DIGI_FORCEDCD) != 0) ||
+ C_CLOCAL(tty)) {
+ otype = OTYPE_PERSISTENT;
+ delay_error = 0;
+
+ } else {
+ otype = OTYPE_INCOMING;
+ delay_error = 0;
+ }
+
+ /*
+ * Handle port currently outside physical port range.
+ */
+
+ if (port >= nd->nd_chan_count) {
+ if (otype == OTYPE_IMMEDIATE) {
+ retval = (nd->nd_state == NS_READY) ?
+ -ENXIO : -EAGAIN;
+ goto unlock;
+ }
+ }
+
+ /*
+ * Handle port not currently open.
+ */
+
+ else if (ch->ch_open_count == 0) {
+ /*
+ * Return an error when an Incoming Open
+ * response indicates the port is busy.
+ */
+
+ if (ch->ch_open_error != 0 && otype == ch->ch_otype) {
+ retval = (ch->ch_open_error <= 2) ?
+ delay_error : -ENXIO ;
+ goto unlock;
+ }
+
+ /*
+ * Fail any new Immediate open if we do not have
+ * a normal connection to the server.
+ */
+
+ if (nd->nd_state != NS_READY &&
+ otype == OTYPE_IMMEDIATE) {
+ retval = -EAGAIN;
+ goto unlock;
+ }
+
+ /*
+ * If a Realport open of the correct type has
+ * succeeded, complete the open.
+ */
+
+ if (ch->ch_state == CS_READY && ch->ch_otype == otype)
+ break;
+ }
+
+ /*
+ * Handle port already open and active as a device
+ * of same category.
+ */
+
+ else if ((ch->ch_category == category) ||
+ IS_PRINT(MINOR(tty_devnum(tty)))) {
+ /*
+ * Fail if opening the device now would
+ * violate exclusive use.
+ */
+ unf = ch->ch_tun.un_flag | ch->ch_pun.un_flag;
+
+ if ((file->f_flags & O_EXCL) || (unf & UN_EXCL)) {
+ retval = -EBUSY;
+ goto unlock;
+ }
+
+ /*
+ * If the open device is in the hangup state, all
+ * system calls fail except close().
+ */
+
+ /* TODO : check on hangup_p calls */
+
+ if (ch->ch_flag & CH_HANGUP) {
+ retval = -ENXIO;
+ goto unlock;
+ }
+
+ /*
+ * If the port is ready, and carrier is ignored
+ * or present, then complete the open.
+ */
+
+ if (ch->ch_state == CS_READY &&
+ (otype != OTYPE_INCOMING ||
+ ch->ch_flag & CH_VIRT_CD))
+ break;
+
+ wait_carrier = 1;
+ }
+
+ /*
+ * Handle port active with a different category device.
+ */
+
+ else {
+ if (otype == OTYPE_IMMEDIATE) {
+ retval = delay_error;
+ goto unlock;
+ }
+ }
+
+ /*
+ * Wait until conditions change, then take another
+ * try at the open.
+ */
+
+ ch->ch_wait_count[otype]++;
+
+ if (wait_carrier)
+ ch->ch_wait_carrier++;
+
+ /*
+ * Prepare the task to accept the wakeup, then
+ * release our locks and release control.
+ */
+
+ add_wait_queue(&ch->ch_flag_wait, &wait);
+ current->state = TASK_INTERRUPTIBLE;
+
+ spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+
+ /*
+ * Give up control, we'll come back if we're
+ * interrupted or are woken up.
+ */
+ schedule();
+ remove_wait_queue(&ch->ch_flag_wait, &wait);
+
+ spin_lock_irqsave(&nd->nd_lock, lock_flags);
+
+ current->state = TASK_RUNNING;
+
+ ch->ch_wait_count[otype]--;
+
+ if (wait_carrier)
+ ch->ch_wait_carrier--;
+
+ nd->nd_tx_work = 1;
+
+ if (signal_pending(current)) {
+ retval = -EINTR;
+ goto unlock;
+ }
+ } /* end for(;;) */
+
+ /*
+ * The open has succeeded. No turning back.
+ */
+ counts_were_incremented = 1;
+ un->un_open_count++;
+ ch->ch_open_count++;
+
+ /*
+ * Initialize the channel, if it's not already open.
+ */
+
+ if (ch->ch_open_count == 1) {
+ ch->ch_flag = 0;
+ ch->ch_inwait = 0;
+ ch->ch_category = category;
+ ch->ch_pscan_state = 0;
+
+ /* TODO : find out what PS-1 bug Gene was referring to */
+ /* TODO : in the following comment. */
+
+ ch->ch_send = RR_TX_START | RR_RX_START; /* PS-1 bug */
+
+ if (C_CLOCAL(tty) ||
+ ch->ch_s_mlast & DM_CD ||
+ ch->ch_digi.digi_flags & DIGI_FORCEDCD)
+ ch->ch_flag |= CH_VIRT_CD;
+ else if (OPEN_FORCES_CARRIER(category))
+ ch->ch_flag |= CH_VIRT_CD;
+
+ }
+
+ /*
+ * Initialize the unit, if it is not already open.
+ */
+
+ if (un->un_open_count == 1) {
+ /*
+ * Since all terminal options are always sticky in Linux,
+ * we don't need the UN_STICKY flag to be handled specially.
+ */
+ /* clears all the digi flags, leaves serial flags */
+ un->un_flag &= ~UN_DIGI_MASK;
+
+ if (file->f_flags & O_EXCL)
+ un->un_flag |= UN_EXCL;
+
+ /* TODO : include "session" and "pgrp" */
+
+ /*
+ * In Linux, all terminal parameters are intended to be sticky.
+ * as a result, we "remove" the code which once reset the ports
+ * to sane values.
+ */
+
+ drp_param(ch);
+
+ }
+
+ un->un_flag |= UN_INITIALIZED;
+
+ retval = 0;
+
+unlock:
+
+ spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+
+done:
+ /*
+ * Linux does a close for every open, even failed ones!
+ */
+ if (!counts_were_incremented) {
+ un->un_open_count++;
+ ch->ch_open_count++;
+ }
+
+ if (retval)
+ dev_err(tty->dev, "tty open bad return (%i)\n", retval);
+
+ return retval;
+}
+
+
+
+
+/*
+ * dgrp_tty_close() -- close function for tty_operations
+ */
+static void dgrp_tty_close(struct tty_struct *tty, struct file *file)
+{
+ struct ch_struct *ch;
+ struct un_struct *un;
+ struct nd_struct *nd;
+ int tpos;
+ int port;
+ int err = 0;
+ int s = 0;
+ ulong waketime;
+ ulong lock_flags;
+ int sent_printer_offstr = 0;
+
+ port = PORT_NUM(MINOR(tty_devnum(tty)));
+
+ un = tty->driver_data;
+
+ if (!un)
+ return;
+
+ ch = un->un_ch;
+
+ if (!ch)
+ return;
+
+ nd = ch->ch_nd;
+
+ if (!nd)
+ return;
+
+ spin_lock_irqsave(&nd->nd_lock, lock_flags);
+
+
+ /* Used to be on channel basis, now we check on a unit basis. */
+ if (un->un_open_count != 1)
+ goto unlock;
+
+ /*
+ * OK, its the last close on the unit
+ */
+ un->un_flag |= UN_CLOSING;
+
+ /*
+ * Notify the discipline to only process XON/XOFF characters.
+ */
+ tty->closing = 1;
+
+ /*
+ * Wait for output to drain only if this is
+ * the last close against the channel
+ */
+
+ if (ch->ch_open_count == 1) {
+ /*
+ * If its the print device, we need to ensure at all costs that
+ * the offstr will fit. If it won't, flush our tbuf.
+ */
+ if (IS_PRINT(MINOR(tty_devnum(tty))) &&
+ (((ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK) <
+ ch->ch_digi.digi_offlen))
+ ch->ch_tin = ch->ch_tout;
+
+ /*
+ * Turn off the printer. Don't bother checking to see if its
+ * IS_PRINT... Since this is the last close the flag is going
+ * to be cleared, so we MUST make sure the offstr gets inserted
+ * into tbuf.
+ */
+
+ if ((ch->ch_flag & CH_PRON) != 0) {
+ drp_wmove(ch, 0, ch->ch_digi.digi_offstr,
+ ch->ch_digi.digi_offlen);
+ ch->ch_flag &= ~CH_PRON;
+ sent_printer_offstr = 1;
+ }
+ }
+
+ /*
+ * Wait until either the output queue has drained, or we see
+ * absolutely no progress for 15 seconds.
+ */
+
+ tpos = ch->ch_s_tpos;
+
+ waketime = jiffies + 15 * HZ;
+
+ for (;;) {
+
+ /*
+ * Make sure the port still exists.
+ */
+
+ if (port >= nd->nd_chan_count) {
+ err = 1;
+ break;
+ }
+
+ if (signal_pending(current)) {
+ err = 1;
+ break;
+ }
+
+ /*
+ * If the port is idle (not opened on the server), we have
+ * no way of draining/flushing/closing the port on that server.
+ * So break out of loop.
+ */
+ if (ch->ch_state == CS_IDLE)
+ break;
+
+ nd->nd_tx_work = 1;
+
+ /*
+ * Exit if the queues for this unit are empty,
+ * and either the other unit is still open or all
+ * data has drained.
+ */
+
+ if ((un->un_tty)->ops->chars_in_buffer ?
+ ((un->un_tty)->ops->chars_in_buffer)(un->un_tty) == 0 : 1) {
+
+ /*
+ * We don't need to wait for a buffer to drain
+ * if the other unit is open.
+ */
+
+ if (ch->ch_open_count != un->un_open_count)
+ break;
+
+ /*
+ * The wait is complete when all queues are
+ * drained, and any break in progress is complete.
+ */
+
+ if (ch->ch_tin == ch->ch_tout &&
+ ch->ch_s_tin == ch->ch_s_tpos &&
+ (ch->ch_send & RR_TX_BREAK) == 0) {
+ break;
+ }
+ }
+
+ /*
+ * Flush TX data and exit the wait if NDELAY is set,
+ * or this is not a DIGI printer, and the close timeout
+ * expires.
+ */
+
+ if ((file->f_flags & (O_NDELAY | O_NONBLOCK)) ||
+ ((long)(jiffies - waketime) >= 0 &&
+ (ch->ch_digi.digi_flags & DIGI_PRINTER) == 0)) {
+
+ /*
+ * If we sent the printer off string, we cannot
+ * flush our internal buffers, or we might lose
+ * the offstr.
+ */
+ if (!sent_printer_offstr)
+ dgrp_tty_flush_buffer(tty);
+
+ tty_ldisc_flush(tty);
+ break;
+ }
+
+ /*
+ * Otherwise take a short nap.
+ */
+
+ ch->ch_flag |= CH_DRAIN;
+
+ spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+
+ schedule_timeout_interruptible(1);
+ s = signal_pending(current);
+
+ spin_lock_irqsave(&nd->nd_lock, lock_flags);
+
+ if (s) {
+ /*
+ * If we had sent the printer off string, we now have
+ * some problems.
+ *
+ * The system won't let us sleep since we got an error
+ * back from sleep, presumably because the user did
+ * a ctrl-c...
+ * But we need to ensure that the offstr gets sent!
+ * Thus, we have to do something else besides sleeping.
+ * The plan:
+ * 1) Make this task uninterruptable.
+ * 2) Set up a timer to go off in 1 sec.
+ * 3) Act as tho we just got out of the sleep above.
+ *
+ * Thankfully, in the real world, this just
+ * never happens.
+ */
+
+ if (sent_printer_offstr) {
+ spin_unlock_irqrestore(&nd->nd_lock,
+ lock_flags);
+ drp_my_sleep(ch);
+ spin_lock_irqsave(&nd->nd_lock, lock_flags);
+ } else {
+ err = 1;
+ break;
+ }
+ }
+
+ /*
+ * Restart the wait if any progress is seen.
+ */
+
+ if (ch->ch_s_tpos != tpos) {
+ tpos = ch->ch_s_tpos;
+
+ /* TODO: this gives us timeout problems with nist ?? */
+ waketime = jiffies + 15 * HZ;
+ }
+ }
+
+ /*
+ * Close the line discipline
+ */
+
+ /* this is done in tty_io.c */
+ /* if ((un->un_tty)->ldisc.close)
+ * ((un->un_tty)->ldisc.close)(un->un_tty);
+ */
+
+ /* don't do this here */
+ /* un->un_flag = 0; */
+
+ /*
+ * Flush the receive buffer on terminal unit close only.
+ */
+
+ if (!IS_PRINT(MINOR(tty_devnum(tty))))
+ ch->ch_rout = ch->ch_rin;
+
+
+ /*
+ * Don't permit the close to happen until we get any pending
+ * sync request responses.
+ * There could be other ports depending upon the response as well.
+ *
+ * Also, don't permit the close to happen until any parameter
+ * changes have been sent out from the state machine as well.
+ * This is required because of a ditty -a race with -HUPCL
+ * We MUST make sure all channel parameters have been sent to the
+ * Portserver before sending a close.
+ */
+
+ if ((err != 1) && (ch->ch_state != CS_IDLE)) {
+ spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+ s = wait_event_interruptible(ch->ch_flag_wait,
+ ((ch->ch_flag & (CH_WAITING_SYNC | CH_PARAM)) == 0));
+ spin_lock_irqsave(&nd->nd_lock, lock_flags);
+ }
+
+ /*
+ * Cleanup the channel if last unit open.
+ */
+
+ if (ch->ch_open_count == 1) {
+ ch->ch_flag = 0;
+ ch->ch_category = 0;
+ ch->ch_send = 0;
+ ch->ch_expect = 0;
+ ch->ch_tout = ch->ch_tin;
+ /* (un->un_tty)->device = 0; */
+
+ if (ch->ch_state == CS_READY)
+ ch->ch_state = CS_SEND_CLOSE;
+ }
+
+ /*
+ * Send the changes to the server
+ */
+ if (ch->ch_state != CS_IDLE) {
+ ch->ch_flag |= CH_PARAM;
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+
+ nd->nd_tx_work = 1;
+ nd->nd_tx_ready = 1;
+
+unlock:
+ tty->closing = 0;
+
+ if (ch->ch_open_count <= 0)
+ dev_info(tty->dev,
+ "%s - unexpected value for ch->ch_open_count: %i\n",
+ __func__, ch->ch_open_count);
+ else
+ ch->ch_open_count--;
+
+ if (un->un_open_count <= 0)
+ dev_info(tty->dev,
+ "%s - unexpected value for un->un_open_count: %i\n",
+ __func__, un->un_open_count);
+ else
+ un->un_open_count--;
+
+ un->un_flag &= ~(UN_NORMAL_ACTIVE | UN_CALLOUT_ACTIVE | UN_CLOSING);
+ if (waitqueue_active(&un->un_close_wait))
+ wake_up_interruptible(&un->un_close_wait);
+
+ spin_unlock_irqrestore(&nd->nd_lock, lock_flags);
+
+ return;
+
+}
+
+static void drp_wmove(struct ch_struct *ch, int from_user, void *buf, int count)
+{
+ int n;
+ int ret = 0;
+
+ ch->ch_nd->nd_tx_work = 1;
+
+ n = TBUF_MAX - ch->ch_tin;
+
+ if (count >= n) {
+ if (from_user)
+ ret = copy_from_user(ch->ch_tbuf + ch->ch_tin,
+ (void __user *) buf, n);
+ else
+ memcpy(ch->ch_tbuf + ch->ch_tin, buf, n);
+
+ buf = (char *) buf + n;
+ count -= n;
+ ch->ch_tin = 0;
+ }
+
+ if (from_user)
+ ret = copy_from_user(ch->ch_tbuf + ch->ch_tin,
+ (void __user *) buf, count);
+ else
+ memcpy(ch->ch_tbuf + ch->ch_tin, buf, count);
+
+ ch->ch_tin += count;
+}
+
+
+static int dgrp_calculate_txprint_bounds(struct ch_struct *ch, int space,
+ int *un_flag)
+{
+ clock_t tt;
+ clock_t mt;
+ unsigned short tmax = 0;
+
+ /*
+ * If the terminal device is busy, reschedule when
+ * the terminal device becomes idle.
+ */
+
+ if (ch->ch_tun.un_open_count != 0 &&
+ ch->ch_tun.un_tty->ops->chars_in_buffer &&
+ ((ch->ch_tun.un_tty->ops->chars_in_buffer)(ch->ch_tun.un_tty) != 0)) {
+ *un_flag = UN_PWAIT;
+ return 0;
+ }
+
+ /*
+ * Assure that whenever there is printer data in the output
+ * buffer, there always remains enough space after it to
+ * turn the printer off.
+ */
+ space -= ch->ch_digi.digi_offlen;
+
+ if (space <= 0) {
+ *un_flag = UN_EMPTY;
+ return 0;
+ }
+
+ /*
+ * We measure printer CPS speed by incrementing
+ * ch_cpstime by (HZ / digi_maxcps) for every
+ * character we output, restricting output so
+ * that ch_cpstime never exceeds lbolt.
+ *
+ * However if output has not been done for some
+ * time, lbolt will grow to very much larger than
+ * ch_cpstime, which would allow essentially
+ * unlimited amounts of output until ch_cpstime
+ * finally caught up. To avoid this, we adjust
+ * cps_time when necessary so the difference
+ * between lbolt and ch_cpstime never results
+ * in sending more than digi_bufsize characters.
+ *
+ * This nicely models a printer with an internal
+ * buffer of digi_bufsize characters.
+ *
+ * Get the time between lbolt and ch->ch_cpstime;
+ */
+
+ tt = jiffies - ch->ch_cpstime;
+
+ /*
+ * Compute the time required to send digi_bufsize
+ * characters.
+ */
+
+ mt = HZ * ch->ch_digi.digi_bufsize / ch->ch_digi.digi_maxcps;
+
+ /*
+ * Compute the number of characters that can be sent
+ * without violating the time constraint. If the
+ * direct calculation of this number is bigger than
+ * digi_bufsize, limit the number to digi_bufsize,
+ * and adjust cpstime to match.
+ */
+
+ if ((clock_t)(tt + HZ) > (clock_t)(mt + HZ)) {
+ tmax = ch->ch_digi.digi_bufsize;
+ ch->ch_cpstime = jiffies - mt;
+ } else {
+ tmax = ch->ch_digi.digi_maxcps * tt / HZ;
+ }
+
+ /*
+ * If the time constraint now binds, limit the transmit
+ * count accordingly, and tentatively arrange to be
+ * rescheduled based on time.
+ */
+
+ if (tmax < space) {
+ *un_flag = UN_TIME;
+ space = tmax;
+ }
+
+ /*
+ * Compute the total number of characters we can
+ * output before the total number of characters known
+ * to be in the output queue exceeds digi_maxchar.
+ */
+
+ tmax = (ch->ch_digi.digi_maxchar -
+ ((ch->ch_tin - ch->ch_tout) & TBUF_MASK) -
+ ((ch->ch_s_tin - ch->ch_s_tpos) & 0xffff));
+
+
+ /*
+ * If the digi_maxchar constraint now holds, limit
+ * the transmit count accordingly, and arrange to
+ * be rescheduled when the queue becomes empty.
+ */
+
+ if (space > tmax) {
+ *un_flag = UN_EMPTY;
+ space = tmax;
+ }
+
+ if (space <= 0)
+ *un_flag |= UN_EMPTY;
+
+ return space;
+}
+
+
+static int dgrp_tty_write(struct tty_struct *tty,
+ const unsigned char *buf,
+ int count)
+{
+ struct nd_struct *nd;
+ struct un_struct *un;
+ struct ch_struct *ch;
+ int space;
+ int n;
+ int t;
+ int sendcount;
+ int un_flag;
+ ulong lock_flags;
+
+ if (tty == NULL)
+ return 0;
+
+ un = tty->driver_data;
+ if (!un)
+ return 0;
+
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+
+ nd = ch->ch_nd;
+ if (!nd)
+ return 0;
+
+ /*
+ * Ignore the request if the channel is not ready.
+ */
+ if (ch->ch_state != CS_READY)
+ return 0;
+
+ spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags);
+
+ /*
+ * Ignore the request if output is blocked.
+ */
+ if ((un->un_flag & (UN_EMPTY | UN_LOW | UN_TIME | UN_PWAIT)) != 0) {
+ count = 0;
+ goto out;
+ }
+
+ /*
+ * Also ignore the request if DPA has this port open,
+ * and is flow controlled on reading more data.
+ */
+ if (nd->nd_dpa_debug && nd->nd_dpa_flag & DPA_WAIT_SPACE &&
+ nd->nd_dpa_port == MINOR(tty_devnum(ch->ch_tun.un_tty))) {
+ count = 0;
+ goto out;
+ }
+
+ /*
+ * Limit amount we will write to the amount of space
+ * available in the channel buffer.
+ */
+ sendcount = 0;
+
+ space = (ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK;
+
+ /*
+ * Handle the printer device.
+ */
+
+ un_flag = UN_LOW;
+
+ if (IS_PRINT(MINOR(tty_devnum(tty)))) {
+ clock_t tt;
+ clock_t mt;
+ unsigned short tmax = 0;
+
+ /*
+ * If the terminal device is busy, reschedule when
+ * the terminal device becomes idle.
+ */
+
+ if (ch->ch_tun.un_open_count != 0 &&
+ ((ch->ch_tun.un_tty->ops->chars_in_buffer)(ch->ch_tun.un_tty) != 0)) {
+ un->un_flag |= UN_PWAIT;
+ count = 0;
+ goto out;
+ }
+
+ /*
+ * Assure that whenever there is printer data in the output
+ * buffer, there always remains enough space after it to
+ * turn the printer off.
+ */
+ space -= ch->ch_digi.digi_offlen;
+
+ /*
+ * Output the printer on string.
+ */
+
+ if ((ch->ch_flag & CH_PRON) == 0) {
+ space -= ch->ch_digi.digi_onlen;
+
+ if (space < 0) {
+ un->un_flag |= UN_EMPTY;
+ (ch->ch_nd)->nd_tx_work = 1;
+ count = 0;
+ goto out;
+ }
+
+ drp_wmove(ch, 0, ch->ch_digi.digi_onstr,
+ ch->ch_digi.digi_onlen);
+
+ ch->ch_flag |= CH_PRON;
+ }
+
+ /*
+ * We measure printer CPS speed by incrementing
+ * ch_cpstime by (HZ / digi_maxcps) for every
+ * character we output, restricting output so
+ * that ch_cpstime never exceeds lbolt.
+ *
+ * However if output has not been done for some
+ * time, lbolt will grow to very much larger than
+ * ch_cpstime, which would allow essentially
+ * unlimited amounts of output until ch_cpstime
+ * finally caught up. To avoid this, we adjust
+ * cps_time when necessary so the difference
+ * between lbolt and ch_cpstime never results
+ * in sending more than digi_bufsize characters.
+ *
+ * This nicely models a printer with an internal
+ * buffer of digi_bufsize characters.
+ *
+ * Get the time between lbolt and ch->ch_cpstime;
+ */
+
+ tt = jiffies - ch->ch_cpstime;
+
+ /*
+ * Compute the time required to send digi_bufsize
+ * characters.
+ */
+
+ mt = HZ * ch->ch_digi.digi_bufsize / ch->ch_digi.digi_maxcps;
+
+ /*
+ * Compute the number of characters that can be sent
+ * without violating the time constraint. If the
+ * direct calculation of this number is bigger than
+ * digi_bufsize, limit the number to digi_bufsize,
+ * and adjust cpstime to match.
+ */
+
+ if ((clock_t)(tt + HZ) > (clock_t)(mt + HZ)) {
+ tmax = ch->ch_digi.digi_bufsize;
+ ch->ch_cpstime = jiffies - mt;
+ } else {
+ tmax = ch->ch_digi.digi_maxcps * tt / HZ;
+ }
+
+ /*
+ * If the time constraint now binds, limit the transmit
+ * count accordingly, and tentatively arrange to be
+ * rescheduled based on time.
+ */
+
+ if (tmax < space) {
+ space = tmax;
+ un_flag = UN_TIME;
+ }
+
+ /*
+ * Compute the total number of characters we can
+ * output before the total number of characters known
+ * to be in the output queue exceeds digi_maxchar.
+ */
+
+ tmax = (ch->ch_digi.digi_maxchar -
+ ((ch->ch_tin - ch->ch_tout) & TBUF_MASK) -
+ ((ch->ch_s_tin - ch->ch_s_tpos) & 0xffff));
+
+
+ /*
+ * If the digi_maxchar constraint now holds, limit
+ * the transmit count accordingly, and arrange to
+ * be rescheduled when the queue becomes empty.
+ */
+
+ if (space > tmax) {
+ space = tmax;
+ un_flag = UN_EMPTY;
+ }
+
+ }
+ /*
+ * Handle the terminal device.
+ */
+ else {
+
+ /*
+ * If the printer device is on, turn it off.
+ */
+
+ if ((ch->ch_flag & CH_PRON) != 0) {
+
+ space -= ch->ch_digi.digi_offlen;
+
+ drp_wmove(ch, 0, ch->ch_digi.digi_offstr,
+ ch->ch_digi.digi_offlen);
+
+ ch->ch_flag &= ~CH_PRON;
+ }
+ }
+
+ /*
+ * If space is 0 and its because the ch->tbuf
+ * is full, then Linux will handle a callback when queue
+ * space becomes available.
+ * tty_write returns count = 0
+ */
+
+ if (space <= 0) {
+ /* the linux tty_io.c handles this if we return 0 */
+ /* if (fp->flags & O_NONBLOCK) return -EAGAIN; */
+
+ un->un_flag |= UN_EMPTY;
+ (ch->ch_nd)->nd_tx_work = 1;
+ count = 0;
+ goto out;
+ }
+
+ count = min(count, space);
+
+ if (count > 0) {
+
+ un->un_tbusy++;
+
+ /*
+ * Copy the buffer contents to the ch_tbuf
+ * being careful to wrap around the circular queue
+ */
+
+ t = TBUF_MAX - ch->ch_tin;
+ n = count;
+
+ if (n >= t) {
+ memcpy(ch->ch_tbuf + ch->ch_tin, buf, t);
+ if (nd->nd_dpa_debug && nd->nd_dpa_port == PORT_NUM(MINOR(tty_devnum(un->un_tty))))
+ dgrp_dpa_data(nd, 0, (char *) buf, t);
+ buf += t;
+ n -= t;
+ ch->ch_tin = 0;
+ sendcount += n;
+ }
+
+ memcpy(ch->ch_tbuf + ch->ch_tin, buf, n);
+ if (nd->nd_dpa_debug && nd->nd_dpa_port == PORT_NUM(MINOR(tty_devnum(un->un_tty))))
+ dgrp_dpa_data(nd, 0, (char *) buf, n);
+ buf += n;
+ ch->ch_tin += n;
+ sendcount += n;
+
+ un->un_tbusy--;
+ (ch->ch_nd)->nd_tx_work = 1;
+ if (ch->ch_edelay != DGRP_RTIME) {
+ (ch->ch_nd)->nd_tx_ready = 1;
+ wake_up_interruptible(&nd->nd_tx_waitq);
+ }
+ }
+
+ ch->ch_txcount += count;
+
+ if (IS_PRINT(MINOR(tty_devnum(tty)))) {
+
+ /*
+ * Adjust ch_cpstime to account
+ * for the characters just output.
+ */
+
+ if (sendcount > 0) {
+ int cc = HZ * sendcount + ch->ch_cpsrem;
+
+ ch->ch_cpstime += cc / ch->ch_digi.digi_maxcps;
+ ch->ch_cpsrem = cc % ch->ch_digi.digi_maxcps;
+ }
+
+ /*
+ * If we are now waiting on time, schedule ourself
+ * back when we'll be able to send a block of
+ * digi_maxchar characters.
+ */
+
+ if ((un_flag & UN_TIME) != 0) {
+ ch->ch_waketime = (ch->ch_cpstime +
+ (ch->ch_digi.digi_maxchar * HZ /
+ ch->ch_digi.digi_maxcps));
+ }
+ }
+
+ /*
+ * If the printer unit is waiting for completion
+ * of terminal output, get him going again.
+ */
+
+ if ((ch->ch_pun.un_flag & UN_PWAIT) != 0)
+ (ch->ch_nd)->nd_tx_work = 1;
+
+out:
+ spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags);
+
+ return count;
+}
+
+
+/*
+ * Put a character into ch->ch_buf
+ *
+ * - used by the line discipline for OPOST processing
+ */
+
+static int dgrp_tty_put_char(struct tty_struct *tty, unsigned char new_char)
+{
+ struct un_struct *un;
+ struct ch_struct *ch;
+ ulong lock_flags;
+ int space;
+ int retval = 0;
+
+ if (tty == NULL)
+ return 0;
+
+ un = tty->driver_data;
+ if (!un)
+ return 0;
+
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+
+ if (ch->ch_state != CS_READY)
+ return 0;
+
+ spin_lock_irqsave(&dgrp_poll_data.poll_lock, lock_flags);
+
+
+ /*
+ * If space is 0 and its because the ch->tbuf
+ * Warn and dump the character, there isn't anything else
+ * we can do about it. David_Fries@digi.com
+ */
+
+ space = (ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK;
+
+ un->un_tbusy++;
+
+ /*
+ * Output the printer on string if device is TXPrint.
+ */
+ if (IS_PRINT(MINOR(tty_devnum(tty))) && (ch->ch_flag & CH_PRON) == 0) {
+ if (space < ch->ch_digi.digi_onlen) {
+ un->un_tbusy--;
+ goto out;
+ }
+ space -= ch->ch_digi.digi_onlen;
+ drp_wmove(ch, 0, ch->ch_digi.digi_onstr,
+ ch->ch_digi.digi_onlen);
+ ch->ch_flag |= CH_PRON;
+ }
+
+ /*
+ * Output the printer off string if device is NOT TXPrint.
+ */
+
+ if (!IS_PRINT(MINOR(tty_devnum(tty))) &&
+ ((ch->ch_flag & CH_PRON) != 0)) {
+ if (space < ch->ch_digi.digi_offlen) {
+ un->un_tbusy--;
+ goto out;
+ }
+
+ space -= ch->ch_digi.digi_offlen;
+ drp_wmove(ch, 0, ch->ch_digi.digi_offstr,
+ ch->ch_digi.digi_offlen);
+ ch->ch_flag &= ~CH_PRON;
+ }
+
+ if (!space) {
+ un->un_tbusy--;
+ goto out;
+ }
+
+ /*
+ * Copy the character to the ch_tbuf being
+ * careful to wrap around the circular queue
+ */
+ ch->ch_tbuf[ch->ch_tin] = new_char;
+ ch->ch_tin = (1 + ch->ch_tin) & TBUF_MASK;
+
+ if (IS_PRINT(MINOR(tty_devnum(tty)))) {
+
+ /*
+ * Adjust ch_cpstime to account
+ * for the character just output.
+ */
+
+ int cc = HZ + ch->ch_cpsrem;
+
+ ch->ch_cpstime += cc / ch->ch_digi.digi_maxcps;
+ ch->ch_cpsrem = cc % ch->ch_digi.digi_maxcps;
+
+ /*
+ * If we are now waiting on time, schedule ourself
+ * back when we'll be able to send a block of
+ * digi_maxchar characters.
+ */
+
+ ch->ch_waketime = (ch->ch_cpstime +
+ (ch->ch_digi.digi_maxchar * HZ /
+ ch->ch_digi.digi_maxcps));
+ }
+
+
+ un->un_tbusy--;
+ (ch->ch_nd)->nd_tx_work = 1;
+
+ retval = 1;
+out:
+ spin_unlock_irqrestore(&dgrp_poll_data.poll_lock, lock_flags);
+ return retval;
+}
+
+
+
+/*
+ * Flush TX buffer (make in == out)
+ *
+ * check tty_ioctl.c -- this is called after TCOFLUSH
+ */
+static void dgrp_tty_flush_buffer(struct tty_struct *tty)
+{
+ struct un_struct *un;
+ struct ch_struct *ch;
+
+ if (!tty)
+ return;
+ un = tty->driver_data;
+ if (!un)
+ return;
+
+ ch = un->un_ch;
+ if (!ch)
+ return;
+
+ ch->ch_tout = ch->ch_tin;
+ /* do NOT do this here! */
+ /* ch->ch_s_tpos = ch->ch_s_tin = 0; */
+
+ /* send the flush output command now */
+ ch->ch_send |= RR_TX_FLUSH;
+ (ch->ch_nd)->nd_tx_ready = 1;
+ (ch->ch_nd)->nd_tx_work = 1;
+ wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+
+ if (waitqueue_active(&tty->write_wait))
+ wake_up_interruptible(&tty->write_wait);
+
+ tty_wakeup(tty);
+
+}
+
+/*
+ * Return space available in Tx buffer
+ * count = ( ch->ch_tout - ch->ch_tin ) mod (TBUF_MAX - 1)
+ */
+static int dgrp_tty_write_room(struct tty_struct *tty)
+{
+ struct un_struct *un;
+ struct ch_struct *ch;
+ int count;
+
+ if (!tty)
+ return 0;
+
+ un = tty->driver_data;
+ if (!un)
+ return 0;
+
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+
+ count = (ch->ch_tout - ch->ch_tin - 1) & TBUF_MASK;
+
+ /* We *MUST* check this, and return 0 if the Printer Unit cannot
+ * take any more data within its time constraints... If we don't
+ * return 0 and the printer has hit it time constraint, the ld will
+ * call us back doing a put_char, which cannot be rejected!!!
+ */
+ if (IS_PRINT(MINOR(tty_devnum(tty)))) {
+ int un_flag = 0;
+ count = dgrp_calculate_txprint_bounds(ch, count, &un_flag);
+ if (count <= 0)
+ count = 0;
+
+ ch->ch_pun.un_flag |= un_flag;
+ (ch->ch_nd)->nd_tx_work = 1;
+ }
+
+ return count;
+}
+
+/*
+ * Return number of characters that have not been transmitted yet.
+ * chars_in_buffer = ( ch->ch_tin - ch->ch_tout ) mod (TBUF_MAX - 1)
+ * + ( ch->ch_s_tin - ch->ch_s_tout ) mod (0xffff)
+ * = number of characters "in transit"
+ *
+ * Remember that sequence number math is always with a sixteen bit
+ * mask, not the TBUF_MASK.
+ */
+
+static int dgrp_tty_chars_in_buffer(struct tty_struct *tty)
+{
+ struct un_struct *un;
+ struct ch_struct *ch;
+ int count;
+ int count1;
+
+ if (!tty)
+ return 0;
+
+ un = tty->driver_data;
+ if (!un)
+ return 0;
+
+ ch = un->un_ch;
+ if (!ch)
+ return 0;
+
+ count1 = count = (ch->ch_tin - ch->ch_tout) & TBUF_MASK;
+ count += (ch->ch_s_tin - ch->ch_s_tpos) & 0xffff;
+ /* one for tbuf, one for the PS */
+
+ /*
+ * If we are busy transmitting add 1
+ */
+ count += un->un_tbusy;
+
+ return count;
+}
+
+
+/*****************************************************************************
+ *
+ * Helper applications for dgrp_tty_ioctl()
+ *
+ *****************************************************************************
+ */
+
+
+/**
+ * ch_to_tty_flags() -- convert channel flags to termio flags
+ * @ch_flag: Digi channel flags
+ * @flagtype: type of ch_flag (iflag, oflag or cflag)
+ *
+ * take the channel flags of the specified type and return the
+ * corresponding termio flag
+ */
+static tcflag_t ch_to_tty_flags(ushort ch_flag, char flagtype)
+{
+ tcflag_t retval = 0;
+
+ switch (flagtype) {
+ case 'i':
+ retval = ((ch_flag & IF_IGNBRK) ? IGNBRK : 0)
+ | ((ch_flag & IF_BRKINT) ? BRKINT : 0)
+ | ((ch_flag & IF_IGNPAR) ? IGNPAR : 0)
+ | ((ch_flag & IF_PARMRK) ? PARMRK : 0)
+ | ((ch_flag & IF_INPCK) ? INPCK : 0)
+ | ((ch_flag & IF_ISTRIP) ? ISTRIP : 0)
+ | ((ch_flag & IF_IXON) ? IXON : 0)
+ | ((ch_flag & IF_IXANY) ? IXANY : 0)
+ | ((ch_flag & IF_IXOFF) ? IXOFF : 0);
+ break;
+
+ case 'o':
+ retval = ((ch_flag & OF_OLCUC) ? OLCUC : 0)
+ | ((ch_flag & OF_ONLCR) ? ONLCR : 0)
+ | ((ch_flag & OF_OCRNL) ? OCRNL : 0)
+ | ((ch_flag & OF_ONOCR) ? ONOCR : 0)
+ | ((ch_flag & OF_ONLRET) ? ONLRET : 0)
+ /* | ((ch_flag & OF_OTAB3) ? OFILL : 0) */
+ | ((ch_flag & OF_TABDLY) ? TABDLY : 0);
+ break;
+
+ case 'c':
+ retval = ((ch_flag & CF_CSTOPB) ? CSTOPB : 0)
+ | ((ch_flag & CF_CREAD) ? CREAD : 0)
+ | ((ch_flag & CF_PARENB) ? PARENB : 0)
+ | ((ch_flag & CF_PARODD) ? PARODD : 0)
+ | ((ch_flag & CF_HUPCL) ? HUPCL : 0);
+
+ switch (ch_flag & CF_CSIZE) {
+ case CF_CS5:
+ retval |= CS5;
+ break;
+ case CF_CS6:
+ retval |= CS6;
+ break;
+ case CF_CS7:
+ retval |= CS7;
+ break;
+ case CF_CS8:
+ retval |= CS8;
+ break;
+ default:
+ retval |= CS8;
+ break;
+ }
+ break;
+ case 'x':
+ break;
+ case 'l':
+ break;
+ default:
+ return 0;
+ }
+
+ return retval;
+}
+
+
+/**
+ * tty_to_ch_flags() -- convert termio flags to digi channel flags
+ * @tty: pointer to a TTY structure holding flag to be converted
+ * @flagtype: identifies which flag (iflags, oflags, or cflags) should
+ * be converted
+ *
+ * take the termio flag of the specified type and return the
+ * corresponding Digi version of the flags
+ */
+static ushort tty_to_ch_flags(struct tty_struct *tty, char flagtype)
+{
+ ushort retval = 0;
+ tcflag_t tflag = 0;
+
+ switch (flagtype) {
+ case 'i':
+ tflag = tty->termios.c_iflag;
+ retval = (I_IGNBRK(tty) ? IF_IGNBRK : 0)
+ | (I_BRKINT(tty) ? IF_BRKINT : 0)
+ | (I_IGNPAR(tty) ? IF_IGNPAR : 0)
+ | (I_PARMRK(tty) ? IF_PARMRK : 0)
+ | (I_INPCK(tty) ? IF_INPCK : 0)
+ | (I_ISTRIP(tty) ? IF_ISTRIP : 0)
+ | (I_IXON(tty) ? IF_IXON : 0)
+ | (I_IXANY(tty) ? IF_IXANY : 0)
+ | (I_IXOFF(tty) ? IF_IXOFF : 0);
+ break;
+ case 'o':
+ tflag = tty->termios.c_oflag;
+ /*
+ * If OPOST is set, then do the post processing in the
+ * firmware by setting all the processing flags on.
+ * If ~OPOST, then make sure we are not doing any
+ * output processing!!
+ */
+ if (!O_OPOST(tty))
+ retval = 0;
+ else
+ retval = (O_OLCUC(tty) ? OF_OLCUC : 0)
+ | (O_ONLCR(tty) ? OF_ONLCR : 0)
+ | (O_OCRNL(tty) ? OF_OCRNL : 0)
+ | (O_ONOCR(tty) ? OF_ONOCR : 0)
+ | (O_ONLRET(tty) ? OF_ONLRET : 0)
+ /* | (O_OFILL(tty) ? OF_TAB3 : 0) */
+ | (O_TABDLY(tty) ? OF_TABDLY : 0);
+ break;
+ case 'c':
+ tflag = tty->termios.c_cflag;
+ retval = (C_CSTOPB(tty) ? CF_CSTOPB : 0)
+ | (C_CREAD(tty) ? CF_CREAD : 0)
+ | (C_PARENB(tty) ? CF_PARENB : 0)
+ | (C_PARODD(tty) ? CF_PARODD : 0)
+ | (C_HUPCL(tty) ? CF_HUPCL : 0);
+ switch (C_CSIZE(tty)) {
+ case CS8:
+ retval |= CF_CS8;
+ break;
+ case CS7:
+ retval |= CF_CS7;
+ break;
+ case CS6:
+ retval |= CF_CS6;
+ break;
+ case CS5:
+ retval |= CF_CS5;
+ break;
+ default:
+ retval |= CF_CS8;
+ break;
+ }
+ break;
+ case 'x':
+ break;
+ case 'l':
+ break;
+ default:
+ return 0;
+ }
+
+ return retval;
+}
+
+
+static int dgrp_tty_send_break(struct tty_struct *tty, int msec)
+{
+ struct un_struct *un;
+ struct ch_struct *ch;
+ int ret = -EIO;
+
+ if (!tty)
+ return ret;
+
+ un = tty->driver_data;
+ if (!un)
+ return ret;
+
+ ch = un->un_ch;
+ if (!ch)
+ return ret;
+
+ dgrp_send_break(ch, msec);
+ return 0;
+}
+
+
+/*
+ * This routine sends a break character out the serial port.
+ *
+ * duration is in 1/1000's of a second
+ */
+static int dgrp_send_break(struct ch_struct *ch, int msec)
+{
+ ulong x;
+
+ wait_event_interruptible(ch->ch_flag_wait,
+ ((ch->ch_flag & CH_TX_BREAK) == 0));
+ ch->ch_break_time += max(msec, 250);
+ ch->ch_send |= RR_TX_BREAK;
+ ch->ch_flag |= CH_TX_BREAK;
+ (ch->ch_nd)->nd_tx_work = 1;
+
+ x = (msec * HZ) / 1000;
+ wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+
+ return 0;
+}
+
+
+/*
+ * Return modem signals to ld.
+ */
+static int dgrp_tty_tiocmget(struct tty_struct *tty)
+{
+ unsigned int mlast;
+ struct un_struct *un = tty->driver_data;
+ struct ch_struct *ch;
+
+ if (!un)
+ return -ENODEV;
+
+ ch = un->un_ch;
+ if (!ch)
+ return -ENODEV;
+
+ mlast = ((ch->ch_s_mlast & ~(DM_RTS | DM_DTR)) |
+ (ch->ch_mout & (DM_RTS | DM_DTR)));
+
+ /* defined in /usr/include/asm/termios.h */
+ mlast = ((mlast & DM_RTS) ? TIOCM_RTS : 0)
+ | ((mlast & DM_DTR) ? TIOCM_DTR : 0)
+ | ((mlast & DM_CD) ? TIOCM_CAR : 0)
+ | ((mlast & DM_RI) ? TIOCM_RNG : 0)
+ | ((mlast & DM_DSR) ? TIOCM_DSR : 0)
+ | ((mlast & DM_CTS) ? TIOCM_CTS : 0);
+
+ return mlast;
+}
+
+
+/*
+ * Set modem lines
+ */
+static int dgrp_tty_tiocmset(struct tty_struct *tty,
+ unsigned int set, unsigned int clear)
+{
+ ulong lock_flags;
+ struct un_struct *un = tty->driver_data;
+ struct ch_struct *ch;
+
+ if (!un)
+ return -ENODEV;
+
+ ch = un->un_ch;
+ if (!ch)
+ return -ENODEV;
+
+ if (set & TIOCM_RTS)
+ ch->ch_mout |= DM_RTS;
+
+ if (set & TIOCM_DTR)
+ ch->ch_mout |= DM_DTR;
+
+ if (clear & TIOCM_RTS)
+ ch->ch_mout &= ~(DM_RTS);
+
+ if (clear & TIOCM_DTR)
+ ch->ch_mout &= ~(DM_DTR);
+
+ spin_lock_irqsave(&(ch->ch_nd)->nd_lock, lock_flags);
+ ch->ch_flag |= CH_PARAM;
+ (ch->ch_nd)->nd_tx_work = 1;
+ wake_up_interruptible(&ch->ch_flag_wait);
+
+ spin_unlock_irqrestore(&(ch->ch_nd)->nd_lock, lock_flags);
+
+ return 0;
+}
+
+
+
+/*
+ * Get current modem status
+ */
+static int get_modem_info(struct ch_struct *ch, unsigned int *value)
+{
+ unsigned int mlast;
+
+ mlast = ((ch->ch_s_mlast & ~(DM_RTS | DM_DTR)) |
+ (ch->ch_mout & (DM_RTS | DM_DTR)));
+
+ /* defined in /usr/include/asm/termios.h */
+ mlast = ((mlast & DM_RTS) ? TIOCM_RTS : 0)
+ | ((mlast & DM_DTR) ? TIOCM_DTR : 0)
+ | ((mlast & DM_CD) ? TIOCM_CAR : 0)
+ | ((mlast & DM_RI) ? TIOCM_RNG : 0)
+ | ((mlast & DM_DSR) ? TIOCM_DSR : 0)
+ | ((mlast & DM_CTS) ? TIOCM_CTS : 0);
+ put_user(mlast, (unsigned int __user *) value);
+
+ return 0;
+}
+
+/*
+ * Set modem lines
+ */
+static int set_modem_info(struct ch_struct *ch, unsigned int command,
+ unsigned int *value)
+{
+ int error;
+ unsigned int arg;
+ int mval = 0;
+ ulong lock_flags;
+
+ error = access_ok(VERIFY_READ, (void __user *) value, sizeof(int));
+ if (error == 0)
+ return -EFAULT;
+
+ get_user(arg, (unsigned int __user *) value);
+ mval |= ((arg & TIOCM_RTS) ? DM_RTS : 0)
+ | ((arg & TIOCM_DTR) ? DM_DTR : 0);
+
+ switch (command) {
+ case TIOCMBIS: /* set flags */
+ ch->ch_mout |= mval;
+ break;
+ case TIOCMBIC: /* clear flags */
+ ch->ch_mout &= ~mval;
+ break;
+ case TIOCMSET:
+ ch->ch_mout = mval;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(&(ch->ch_nd)->nd_lock, lock_flags);
+
+ ch->ch_flag |= CH_PARAM;
+ (ch->ch_nd)->nd_tx_work = 1;
+ wake_up_interruptible(&ch->ch_flag_wait);
+
+ spin_unlock_irqrestore(&(ch->ch_nd)->nd_lock, lock_flags);
+
+ return 0;
+}
+
+
+/*
+ * Assign the custom baud rate to the channel structure
+ */
+static void dgrp_set_custom_speed(struct ch_struct *ch, int newrate)
+{
+ int testdiv;
+ int testrate_high;
+ int testrate_low;
+
+ int deltahigh, deltalow;
+
+ if (newrate < 0)
+ newrate = 0;
+
+ /*
+ * Since the divisor is stored in a 16-bit integer, we make sure
+ * we don't allow any rates smaller than a 16-bit integer would allow.
+ * And of course, rates above the dividend won't fly.
+ */
+ if (newrate && newrate < ((PORTSERVER_DIVIDEND / 0xFFFF) + 1))
+ newrate = ((PORTSERVER_DIVIDEND / 0xFFFF) + 1);
+ if (newrate && newrate > PORTSERVER_DIVIDEND)
+ newrate = PORTSERVER_DIVIDEND;
+
+ while (newrate > 0) {
+ testdiv = PORTSERVER_DIVIDEND / newrate;
+
+ /*
+ * If we try to figure out what rate the PortServer would use
+ * with the test divisor, it will be either equal or higher
+ * than the requested baud rate. If we then determine the
+ * rate with a divisor one higher, we will get the next lower
+ * supported rate below the requested.
+ */
+ testrate_high = PORTSERVER_DIVIDEND / testdiv;
+ testrate_low = PORTSERVER_DIVIDEND / (testdiv + 1);
+
+ /*
+ * If the rate for the requested divisor is correct, just
+ * use it and be done.
+ */
+ if (testrate_high == newrate)
+ break;
+
+ /*
+ * Otherwise, pick the rate that is closer (i.e. whichever rate
+ * has a smaller delta).
+ */
+ deltahigh = testrate_high - newrate;
+ deltalow = newrate - testrate_low;
+
+ if (deltahigh < deltalow)
+ newrate = testrate_high;
+ else
+ newrate = testrate_low;
+
+ break;
+ }
+
+ ch->ch_custom_speed = newrate;
+
+ drp_param(ch);
+
+ return;
+}
+
+
+/*
+ # dgrp_tty_digiseta()
+ *
+ * Ioctl to set the information from ditty.
+ *
+ * NOTE: DIGI_IXON, DSRPACE, DCDPACE, and DTRPACE are unsupported. JAR 990922
+ */
+static int dgrp_tty_digiseta(struct tty_struct *tty,
+ struct digi_struct *new_info)
+{
+ struct un_struct *un = tty->driver_data;
+ struct ch_struct *ch;
+
+ if (!un)
+ return -ENODEV;
+
+ ch = un->un_ch;
+ if (!ch)
+ return -ENODEV;
+
+ if (copy_from_user(&ch->ch_digi, (void __user *) new_info,
+ sizeof(struct digi_struct)))
+ return -EFAULT;
+
+ if ((ch->ch_digi.digi_flags & RTSPACE) ||
+ (ch->ch_digi.digi_flags & CTSPACE))
+ tty->termios.c_cflag |= CRTSCTS;
+ else
+ tty->termios.c_cflag &= ~CRTSCTS;
+
+ if (ch->ch_digi.digi_maxcps < 1)
+ ch->ch_digi.digi_maxcps = 1;
+
+ if (ch->ch_digi.digi_maxcps > 10000)
+ ch->ch_digi.digi_maxcps = 10000;
+
+ if (ch->ch_digi.digi_bufsize < 10)
+ ch->ch_digi.digi_bufsize = 10;
+
+ if (ch->ch_digi.digi_maxchar < 1)
+ ch->ch_digi.digi_maxchar = 1;
+
+ if (ch->ch_digi.digi_maxchar > ch->ch_digi.digi_bufsize)
+ ch->ch_digi.digi_maxchar = ch->ch_digi.digi_bufsize;
+
+ if (ch->ch_digi.digi_onlen > DIGI_PLEN)
+ ch->ch_digi.digi_onlen = DIGI_PLEN;
+
+ if (ch->ch_digi.digi_offlen > DIGI_PLEN)
+ ch->ch_digi.digi_offlen = DIGI_PLEN;
+
+ /* make the changes now */
+ drp_param(ch);
+
+ return 0;
+}
+
+
+
+/*
+ * dgrp_tty_digigetedelay()
+ *
+ * Ioctl to get the current edelay setting.
+ *
+ *
+ *
+ */
+static int dgrp_tty_digigetedelay(struct tty_struct *tty, int *retinfo)
+{
+ struct un_struct *un;
+ struct ch_struct *ch;
+ int tmp;
+
+ if (!retinfo)
+ return -EFAULT;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -EFAULT;
+
+ un = tty->driver_data;
+
+ if (!un)
+ return -ENODEV;
+
+ ch = un->un_ch;
+ if (!ch)
+ return -ENODEV;
+
+ tmp = ch->ch_edelay;
+
+ if (copy_to_user((void __user *) retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+
+ return 0;
+}
+
+
+/*
+ * dgrp_tty_digisetedelay()
+ *
+ * Ioctl to set the EDELAY setting
+ *
+ */
+static int dgrp_tty_digisetedelay(struct tty_struct *tty, int *new_info)
+{
+ struct un_struct *un;
+ struct ch_struct *ch;
+ int new_digi;
+
+ if (!tty || tty->magic != TTY_MAGIC)
+ return -EFAULT;
+
+ un = tty->driver_data;
+
+ if (!un)
+ return -ENODEV;
+
+ ch = un->un_ch;
+ if (!ch)
+ return -ENODEV;
+
+ if (copy_from_user(&new_digi, (void __user *)new_info, sizeof(int)))
+ return -EFAULT;
+
+ ch->ch_edelay = new_digi;
+
+ /* make the changes now */
+ drp_param(ch);
+
+ return 0;
+}
+
+
+/*
+ * The usual assortment of ioctl's
+ *
+ * note: use tty_check_change to make sure that we are not
+ * changing the state of a terminal when we are not a process
+ * in the forground. See tty_io.c
+ * rc = tty_check_change(tty);
+ * if (rc) return rc;
+ */
+static int dgrp_tty_ioctl(struct tty_struct *tty, unsigned int cmd,
+ unsigned long arg)
+{
+ struct un_struct *un;
+ struct ch_struct *ch;
+ int rc;
+ struct digiflow_struct dflow;
+
+ if (!tty)
+ return -ENODEV;
+
+ un = tty->driver_data;
+ if (!un)
+ return -ENODEV;
+
+ ch = un->un_ch;
+ if (!ch)
+ return -ENODEV;
+
+ switch (cmd) {
+
+ /*
+ * Here are all the standard ioctl's that we MUST implement
+ */
+
+ case TCSBRK:
+ /*
+ * TCSBRK is SVID version: non-zero arg --> no break
+ * this behaviour is exploited by tcdrain().
+ *
+ * According to POSIX.1 spec (7.2.2.1.2) breaks should be
+ * between 0.25 and 0.5 seconds
+ */
+
+ rc = tty_check_change(tty);
+ if (rc)
+ return rc;
+ tty_wait_until_sent(tty, 0);
+
+ if (!arg)
+ rc = dgrp_send_break(ch, 250); /* 1/4 second */
+
+ if (dgrp_tty_chars_in_buffer(tty) != 0)
+ return -EINTR;
+
+ return 0;
+
+ case TCSBRKP:
+ /* support for POSIX tcsendbreak()
+ *
+ * According to POSIX.1 spec (7.2.2.1.2) breaks should be
+ * between 0.25 and 0.5 seconds so we'll ask for something
+ * in the middle: 0.375 seconds.
+ */
+ rc = tty_check_change(tty);
+ if (rc)
+ return rc;
+ tty_wait_until_sent(tty, 0);
+
+ rc = dgrp_send_break(ch, arg ? arg*250 : 250);
+
+ if (dgrp_tty_chars_in_buffer(tty) != 0)
+ return -EINTR;
+ return 0;
+
+ case TIOCSBRK:
+ rc = tty_check_change(tty);
+ if (rc)
+ return rc;
+ tty_wait_until_sent(tty, 0);
+
+ /*
+ * RealPort doesn't support turning on a break unconditionally.
+ * The RealPort device will stop sending a break automatically
+ * after the specified time value that we send in.
+ */
+ rc = dgrp_send_break(ch, 250); /* 1/4 second */
+
+ if (dgrp_tty_chars_in_buffer(tty) != 0)
+ return -EINTR;
+ return 0;
+
+ case TIOCCBRK:
+ /*
+ * RealPort doesn't support turning off a break unconditionally.
+ * The RealPort device will stop sending a break automatically
+ * after the specified time value that was sent when turning on
+ * the break.
+ */
+ return 0;
+
+ case TIOCGSOFTCAR:
+ rc = access_ok(VERIFY_WRITE, (void __user *) arg,
+ sizeof(long));
+ if (rc == 0)
+ return -EFAULT;
+ put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) arg);
+ return 0;
+
+ case TIOCSSOFTCAR:
+ get_user(arg, (unsigned long __user *) arg);
+ tty->termios.c_cflag =
+ ((tty->termios.c_cflag & ~CLOCAL) |
+ (arg ? CLOCAL : 0));
+ return 0;
+
+ case TIOCMGET:
+ rc = access_ok(VERIFY_WRITE, (void __user *) arg,
+ sizeof(unsigned int));
+ if (rc == 0)
+ return -EFAULT;
+ return get_modem_info(ch, (unsigned int *) arg);
+
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ return set_modem_info(ch, cmd, (unsigned int *) arg);
+
+ /*
+ * Here are any additional ioctl's that we want to implement
+ */
+
+ case TCFLSH:
+ /*
+ * The linux tty driver doesn't have a flush
+ * input routine for the driver, assuming all backed
+ * up data is in the line disc. buffers. However,
+ * we all know that's not the case. Here, we
+ * act on the ioctl, but then lie and say we didn't
+ * so the line discipline will process the flush
+ * also.
+ */
+ rc = tty_check_change(tty);
+ if (rc)
+ return rc;
+
+ switch (arg) {
+ case TCIFLUSH:
+ case TCIOFLUSH:
+ /* only flush input if this is the only open unit */
+ if (!IS_PRINT(MINOR(tty_devnum(tty)))) {
+ ch->ch_rout = ch->ch_rin;
+ ch->ch_send |= RR_RX_FLUSH;
+ (ch->ch_nd)->nd_tx_work = 1;
+ (ch->ch_nd)->nd_tx_ready = 1;
+ wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+ }
+ if (arg == TCIFLUSH)
+ break;
+
+ case TCOFLUSH: /* flush output, or the receive buffer */
+ /*
+ * This is handled in the tty_ioctl.c code
+ * calling tty_flush_buffer
+ */
+ break;
+
+ default:
+ /* POSIX.1 says return EINVAL if we got a bad arg */
+ return -EINVAL;
+ }
+ /* pretend we didn't recognize this IOCTL */
+ return -ENOIOCTLCMD;
+
+#ifdef TIOCGETP
+ case TIOCGETP:
+#endif
+ /*****************************************
+ Linux HPUX Function
+ TCSETA TCSETA - set the termios
+ TCSETAF TCSETAF - wait for drain first, then set termios
+ TCSETAW TCSETAW - wait for drain, flush the input queue, then set termios
+ - looking at the tty_ioctl code, these command all call our
+ tty_set_termios at the driver's end, when a TCSETA* is sent,
+ it is expecting the tty to have a termio structure,
+ NOT a termios stucture. These two structures differ in size
+ and the tty_ioctl code does a conversion before processing them both.
+ - we should treat the TCSETAW TCSETAF ioctls the same, and let
+ the tty_ioctl code do the conversion stuff.
+
+ TCSETS
+ TCSETSF (none)
+ TCSETSW
+ - the associated tty structure has a termios structure.
+ *****************************************/
+
+ case TCGETS:
+ case TCGETA:
+ return -ENOIOCTLCMD;
+
+ case TCSETAW:
+ case TCSETAF:
+ case TCSETSF:
+ case TCSETSW:
+ /*
+ * The linux tty driver doesn't have a flush
+ * input routine for the driver, assuming all backed
+ * up data is in the line disc. buffers. However,
+ * we all know that's not the case. Here, we
+ * act on the ioctl, but then lie and say we didn't
+ * so the line discipline will process the flush
+ * also.
+ */
+
+ /*
+ * Also, now that we have TXPrint, we have to check
+ * if this is the TXPrint device and the terminal
+ * device is open. If so, do NOT run check_change,
+ * as the terminal device is ALWAYS the parent.
+ */
+ if (!IS_PRINT(MINOR(tty_devnum(tty))) ||
+ !ch->ch_tun.un_open_count) {
+ rc = tty_check_change(tty);
+ if (rc)
+ return rc;
+ }
+
+ /* wait for all the characters in tbuf to drain */
+ tty_wait_until_sent(tty, 0);
+
+ if ((cmd == TCSETSF) || (cmd == TCSETAF)) {
+ /* flush the contents of the rbuf queue */
+ /* TODO: check if this is print device? */
+ ch->ch_send |= RR_RX_FLUSH;
+ (ch->ch_nd)->nd_tx_ready = 1;
+ (ch->ch_nd)->nd_tx_work = 1;
+ wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+ /* do we need to do this? just to be safe! */
+ ch->ch_rout = ch->ch_rin;
+ }
+
+ /* pretend we didn't recognize this */
+ return -ENOIOCTLCMD;
+
+ case TCXONC:
+ /*
+ * The Linux Line Discipline (LD) would do this for us if we
+ * let it, but we have the special firmware options to do this
+ * the "right way" regardless of hardware or software flow
+ * control so we'll do it outselves instead of letting the LD
+ * do it.
+ */
+ rc = tty_check_change(tty);
+ if (rc)
+ return rc;
+
+ switch (arg) {
+ case TCOON:
+ dgrp_tty_start(tty);
+ return 0;
+ case TCOOFF:
+ dgrp_tty_stop(tty);
+ return 0;
+ case TCION:
+ dgrp_tty_input_start(tty);
+ return 0;
+ case TCIOFF:
+ dgrp_tty_input_stop(tty);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+
+ case DIGI_GETA:
+ /* get information for ditty */
+ if (copy_to_user((struct digi_struct __user *) arg,
+ &ch->ch_digi, sizeof(struct digi_struct)))
+ return -EFAULT;
+ break;
+
+ case DIGI_SETAW:
+ case DIGI_SETAF:
+ /* wait for all the characters in tbuf to drain */
+ tty_wait_until_sent(tty, 0);
+
+ if (cmd == DIGI_SETAF) {
+ /* flush the contents of the rbuf queue */
+ /* send down a packet with RR_RX_FLUSH set */
+ ch->ch_send |= RR_RX_FLUSH;
+ (ch->ch_nd)->nd_tx_ready = 1;
+ (ch->ch_nd)->nd_tx_work = 1;
+ wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+ /* do we need to do this? just to be safe! */
+ ch->ch_rout = ch->ch_rin;
+ }
+
+ /* pretend we didn't recognize this */
+
+ case DIGI_SETA:
+ return dgrp_tty_digiseta(tty, (struct digi_struct *) arg);
+
+ case DIGI_SEDELAY:
+ return dgrp_tty_digisetedelay(tty, (int *) arg);
+
+ case DIGI_GEDELAY:
+ return dgrp_tty_digigetedelay(tty, (int *) arg);
+
+ case DIGI_GETFLOW:
+ case DIGI_GETAFLOW:
+ if (cmd == (DIGI_GETFLOW)) {
+ dflow.startc = tty->termios.c_cc[VSTART];
+ dflow.stopc = tty->termios.c_cc[VSTOP];
+ } else {
+ dflow.startc = ch->ch_xxon;
+ dflow.stopc = ch->ch_xxoff;
+ }
+
+ if (copy_to_user((char __user *)arg, &dflow, sizeof(dflow)))
+ return -EFAULT;
+ break;
+
+ case DIGI_SETFLOW:
+ case DIGI_SETAFLOW:
+
+ if (copy_from_user(&dflow, (char __user *)arg, sizeof(dflow)))
+ return -EFAULT;
+
+ if (cmd == (DIGI_SETFLOW)) {
+ tty->termios.c_cc[VSTART] = dflow.startc;
+ tty->termios.c_cc[VSTOP] = dflow.stopc;
+ } else {
+ ch->ch_xxon = dflow.startc;
+ ch->ch_xxoff = dflow.stopc;
+ }
+ break;
+
+ case DIGI_GETCUSTOMBAUD:
+ rc = access_ok(VERIFY_WRITE, (void __user *) arg, sizeof(int));
+ if (rc == 0)
+ return -EFAULT;
+ put_user(ch->ch_custom_speed, (unsigned int __user *) arg);
+ break;
+
+ case DIGI_SETCUSTOMBAUD:
+ {
+ int new_rate;
+
+ get_user(new_rate, (unsigned int __user *) arg);
+ dgrp_set_custom_speed(ch, new_rate);
+
+ break;
+ }
+
+ default:
+ return -ENOIOCTLCMD;
+ }
+
+ return 0;
+}
+
+/*
+ * This routine allows the tty driver to be notified when
+ * the device's termios setting have changed. Note that we
+ * should be prepared to accept the case where old == NULL
+ * and try to do something rational.
+ *
+ * So we need to make sure that our copies of ch_oflag,
+ * ch_clag, and ch_iflag reflect the tty->termios flags.
+ */
+static void dgrp_tty_set_termios(struct tty_struct *tty, struct ktermios *old)
+{
+ struct ktermios *ts;
+ struct ch_struct *ch;
+ struct un_struct *un;
+
+ /* seems silly, but we have to check all these! */
+ if (!tty)
+ return;
+
+ un = tty->driver_data;
+ if (!un)
+ return;
+
+ ts = &tty->termios;
+
+ ch = un->un_ch;
+ if (!ch)
+ return;
+
+ drp_param(ch);
+
+ /* the CLOCAL flag has just been set */
+ if (!(old->c_cflag & CLOCAL) && C_CLOCAL(tty))
+ wake_up_interruptible(&un->un_open_wait);
+}
+
+
+/*
+ * Throttle receiving data. We just set a bit and stop reading
+ * data out of the channel buffer. It will back up and the
+ * FEP will do whatever is necessary to stop the far end.
+ */
+static void dgrp_tty_throttle(struct tty_struct *tty)
+{
+ struct ch_struct *ch;
+
+ if (!tty)
+ return;
+
+ ch = ((struct un_struct *) tty->driver_data)->un_ch;
+ if (!ch)
+ return;
+
+ ch->ch_flag |= CH_RXSTOP;
+}
+
+
+static void dgrp_tty_unthrottle(struct tty_struct *tty)
+{
+ struct ch_struct *ch;
+
+ if (!tty)
+ return;
+
+ ch = ((struct un_struct *) tty->driver_data)->un_ch;
+ if (!ch)
+ return;
+
+ ch->ch_flag &= ~CH_RXSTOP;
+}
+
+/*
+ * Stop the transmitter
+ */
+static void dgrp_tty_stop(struct tty_struct *tty)
+{
+ struct ch_struct *ch;
+
+ if (!tty)
+ return;
+
+ ch = ((struct un_struct *) tty->driver_data)->un_ch;
+ if (!ch)
+ return;
+
+ ch->ch_send |= RR_TX_STOP;
+ ch->ch_send &= ~RR_TX_START;
+
+ /* make the change NOW! */
+ (ch->ch_nd)->nd_tx_ready = 1;
+ if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq))
+ wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+}
+
+/*
+ * Start the transmitter
+ */
+static void dgrp_tty_start(struct tty_struct *tty)
+{
+ struct ch_struct *ch;
+
+ if (!tty)
+ return;
+
+ ch = ((struct un_struct *) tty->driver_data)->un_ch;
+ if (!ch)
+ return;
+
+ /* TODO: don't do anything if the transmitter is not stopped */
+
+ ch->ch_send |= RR_TX_START;
+ ch->ch_send &= ~RR_TX_STOP;
+
+ /* make the change NOW! */
+ (ch->ch_nd)->nd_tx_ready = 1;
+ (ch->ch_nd)->nd_tx_work = 1;
+ if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq))
+ wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+
+}
+
+/*
+ * Stop the reciever
+ */
+static void dgrp_tty_input_stop(struct tty_struct *tty)
+{
+ struct ch_struct *ch;
+
+ if (!tty)
+ return;
+
+ ch = ((struct un_struct *) tty->driver_data)->un_ch;
+ if (!ch)
+ return;
+
+ ch->ch_send |= RR_RX_STOP;
+ ch->ch_send &= ~RR_RX_START;
+ (ch->ch_nd)->nd_tx_ready = 1;
+ if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq))
+ wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+
+}
+
+
+static void dgrp_tty_send_xchar(struct tty_struct *tty, char c)
+{
+ struct un_struct *un;
+ struct ch_struct *ch;
+
+ if (!tty)
+ return;
+
+ un = tty->driver_data;
+ if (!un)
+ return;
+
+ ch = un->un_ch;
+ if (!ch)
+ return;
+ if (c == STOP_CHAR(tty))
+ ch->ch_send |= RR_RX_STOP;
+ else if (c == START_CHAR(tty))
+ ch->ch_send |= RR_RX_START;
+
+ ch->ch_nd->nd_tx_ready = 1;
+ ch->ch_nd->nd_tx_work = 1;
+
+ return;
+}
+
+
+static void dgrp_tty_input_start(struct tty_struct *tty)
+{
+ struct ch_struct *ch;
+
+ if (!tty)
+ return;
+
+ ch = ((struct un_struct *) tty->driver_data)->un_ch;
+ if (!ch)
+ return;
+
+ ch->ch_send |= RR_RX_START;
+ ch->ch_send &= ~RR_RX_STOP;
+ (ch->ch_nd)->nd_tx_ready = 1;
+ (ch->ch_nd)->nd_tx_work = 1;
+ if (waitqueue_active(&(ch->ch_nd)->nd_tx_waitq))
+ wake_up_interruptible(&(ch->ch_nd)->nd_tx_waitq);
+
+}
+
+
+/*
+ * Hangup the port. Like a close, but don't wait for output
+ * to drain.
+ *
+ * How do we close all the channels that are open?
+ */
+static void dgrp_tty_hangup(struct tty_struct *tty)
+{
+ struct ch_struct *ch;
+ struct nd_struct *nd;
+ struct un_struct *un;
+
+ if (!tty)
+ return;
+
+ un = tty->driver_data;
+ if (!un)
+ return;
+
+ ch = un->un_ch;
+ if (!ch)
+ return;
+
+ nd = ch->ch_nd;
+
+ if (C_HUPCL(tty)) {
+ /* LOWER DTR */
+ ch->ch_mout &= ~DM_DTR;
+ /* Don't do this here */
+ /* ch->ch_flag |= CH_HANGUP; */
+ ch->ch_nd->nd_tx_ready = 1;
+ ch->ch_nd->nd_tx_work = 1;
+ if (waitqueue_active(&ch->ch_flag_wait))
+ wake_up_interruptible(&ch->ch_flag_wait);
+ }
+
+}
+
+/************************************************************************/
+/* */
+/* TTY Initialization/Cleanup Functions */
+/* */
+/************************************************************************/
+
+/*
+ * Uninitialize the TTY portion of the supplied node. Free all
+ * memory and resources associated with this node. Do it in reverse
+ * allocation order: this might possibly result in less fragmentation
+ * of memory, though I don't know this for sure.
+ */
+void
+dgrp_tty_uninit(struct nd_struct *nd)
+{
+ char id[3];
+
+ ID_TO_CHAR(nd->nd_ID, id);
+
+ if (nd->nd_ttdriver_flags & SERIAL_TTDRV_REG) {
+ tty_unregister_driver(nd->nd_serial_ttdriver);
+
+ kfree(nd->nd_serial_ttdriver->ttys);
+ nd->nd_serial_ttdriver->ttys = NULL;
+
+ put_tty_driver(nd->nd_serial_ttdriver);
+ nd->nd_ttdriver_flags &= ~SERIAL_TTDRV_REG;
+ }
+
+ if (nd->nd_ttdriver_flags & CALLOUT_TTDRV_REG) {
+ tty_unregister_driver(nd->nd_callout_ttdriver);
+
+ kfree(nd->nd_callout_ttdriver->ttys);
+ nd->nd_callout_ttdriver->ttys = NULL;
+
+ put_tty_driver(nd->nd_callout_ttdriver);
+ nd->nd_ttdriver_flags &= ~CALLOUT_TTDRV_REG;
+ }
+
+ if (nd->nd_ttdriver_flags & XPRINT_TTDRV_REG) {
+ tty_unregister_driver(nd->nd_xprint_ttdriver);
+
+ kfree(nd->nd_xprint_ttdriver->ttys);
+ nd->nd_xprint_ttdriver->ttys = NULL;
+
+ put_tty_driver(nd->nd_xprint_ttdriver);
+ nd->nd_ttdriver_flags &= ~XPRINT_TTDRV_REG;
+ }
+}
+
+
+
+/*
+ * Initialize the TTY portion of the supplied node.
+ */
+int
+dgrp_tty_init(struct nd_struct *nd)
+{
+ char id[3];
+ int rc;
+ int i;
+
+ ID_TO_CHAR(nd->nd_ID, id);
+
+ /*
+ * Initialize the TTDRIVER structures.
+ */
+
+ nd->nd_serial_ttdriver = alloc_tty_driver(CHAN_MAX);
+ sprintf(nd->nd_serial_name, "tty_dgrp_%s_", id);
+
+ nd->nd_serial_ttdriver->owner = THIS_MODULE;
+ nd->nd_serial_ttdriver->name = nd->nd_serial_name;
+ nd->nd_serial_ttdriver->name_base = 0;
+ nd->nd_serial_ttdriver->major = 0;
+ nd->nd_serial_ttdriver->minor_start = 0;
+ nd->nd_serial_ttdriver->type = TTY_DRIVER_TYPE_SERIAL;
+ nd->nd_serial_ttdriver->subtype = SERIAL_TYPE_NORMAL;
+ nd->nd_serial_ttdriver->init_termios = DefaultTermios;
+ nd->nd_serial_ttdriver->driver_name = "dgrp";
+ nd->nd_serial_ttdriver->flags = (TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV |
+ TTY_DRIVER_HARDWARE_BREAK);
+
+ /* The kernel wants space to store pointers to tty_structs. */
+ nd->nd_serial_ttdriver->ttys =
+ kzalloc(CHAN_MAX * sizeof(struct tty_struct *), GFP_KERNEL);
+ if (!nd->nd_serial_ttdriver->ttys)
+ return -ENOMEM;
+
+ tty_set_operations(nd->nd_serial_ttdriver, &dgrp_tty_ops);
+
+ if (!(nd->nd_ttdriver_flags & SERIAL_TTDRV_REG)) {
+ /*
+ * Register tty devices
+ */
+ rc = tty_register_driver(nd->nd_serial_ttdriver);
+ if (rc < 0) {
+ /*
+ * If errno is EBUSY, this means there are no more
+ * slots available to have us auto-majored.
+ * (Which is currently supported up to 256)
+ *
+ * We can still request majors above 256,
+ * we just have to do it manually.
+ */
+ if (rc == -EBUSY) {
+ int i;
+ int max_majors = 1U << (32 - MINORBITS);
+ for (i = 256; i < max_majors; i++) {
+ nd->nd_serial_ttdriver->major = i;
+ rc = tty_register_driver(nd->nd_serial_ttdriver);
+ if (rc >= 0)
+ break;
+ }
+ /* Really fail now, since we ran out
+ * of majors to try. */
+ if (i == max_majors)
+ return rc;
+
+ } else {
+ return rc;
+ }
+ }
+ nd->nd_ttdriver_flags |= SERIAL_TTDRV_REG;
+ }
+
+ nd->nd_callout_ttdriver = alloc_tty_driver(CHAN_MAX);
+ sprintf(nd->nd_callout_name, "cu_dgrp_%s_", id);
+
+ nd->nd_callout_ttdriver->owner = THIS_MODULE;
+ nd->nd_callout_ttdriver->name = nd->nd_callout_name;
+ nd->nd_callout_ttdriver->name_base = 0;
+ nd->nd_callout_ttdriver->major = nd->nd_serial_ttdriver->major;
+ nd->nd_callout_ttdriver->minor_start = 0x40;
+ nd->nd_callout_ttdriver->type = TTY_DRIVER_TYPE_SERIAL;
+ nd->nd_callout_ttdriver->subtype = SERIAL_TYPE_CALLOUT;
+ nd->nd_callout_ttdriver->init_termios = DefaultTermios;
+ nd->nd_callout_ttdriver->driver_name = "dgrp";
+ nd->nd_callout_ttdriver->flags = (TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV |
+ TTY_DRIVER_HARDWARE_BREAK);
+
+ /* The kernel wants space to store pointers to tty_structs. */
+ nd->nd_callout_ttdriver->ttys =
+ kzalloc(CHAN_MAX * sizeof(struct tty_struct *), GFP_KERNEL);
+ if (!nd->nd_callout_ttdriver->ttys)
+ return -ENOMEM;
+
+ tty_set_operations(nd->nd_callout_ttdriver, &dgrp_tty_ops);
+
+ if (dgrp_register_cudevices) {
+ if (!(nd->nd_ttdriver_flags & CALLOUT_TTDRV_REG)) {
+ /*
+ * Register cu devices
+ */
+ rc = tty_register_driver(nd->nd_callout_ttdriver);
+ if (rc < 0)
+ return rc;
+ nd->nd_ttdriver_flags |= CALLOUT_TTDRV_REG;
+ }
+ }
+
+
+ nd->nd_xprint_ttdriver = alloc_tty_driver(CHAN_MAX);
+ sprintf(nd->nd_xprint_name, "pr_dgrp_%s_", id);
+
+ nd->nd_xprint_ttdriver->owner = THIS_MODULE;
+ nd->nd_xprint_ttdriver->name = nd->nd_xprint_name;
+ nd->nd_xprint_ttdriver->name_base = 0;
+ nd->nd_xprint_ttdriver->major = nd->nd_serial_ttdriver->major;
+ nd->nd_xprint_ttdriver->minor_start = 0x80;
+ nd->nd_xprint_ttdriver->type = TTY_DRIVER_TYPE_SERIAL;
+ nd->nd_xprint_ttdriver->subtype = SERIAL_TYPE_XPRINT;
+ nd->nd_xprint_ttdriver->init_termios = DefaultTermios;
+ nd->nd_xprint_ttdriver->driver_name = "dgrp";
+ nd->nd_xprint_ttdriver->flags = (TTY_DRIVER_REAL_RAW |
+ TTY_DRIVER_DYNAMIC_DEV |
+ TTY_DRIVER_HARDWARE_BREAK);
+
+ /* The kernel wants space to store pointers to tty_structs. */
+ nd->nd_xprint_ttdriver->ttys =
+ kzalloc(CHAN_MAX * sizeof(struct tty_struct *), GFP_KERNEL);
+ if (!nd->nd_xprint_ttdriver->ttys)
+ return -ENOMEM;
+
+ tty_set_operations(nd->nd_xprint_ttdriver, &dgrp_tty_ops);
+
+ if (dgrp_register_prdevices) {
+ if (!(nd->nd_ttdriver_flags & XPRINT_TTDRV_REG)) {
+ /*
+ * Register transparent print devices
+ */
+ rc = tty_register_driver(nd->nd_xprint_ttdriver);
+ if (rc < 0)
+ return rc;
+ nd->nd_ttdriver_flags |= XPRINT_TTDRV_REG;
+ }
+ }
+
+ for (i = 0; i < CHAN_MAX; i++) {
+ struct ch_struct *ch = nd->nd_chan + i;
+
+ ch->ch_nd = nd;
+ ch->ch_digi = digi_init;
+ ch->ch_edelay = 100;
+ ch->ch_custom_speed = 0;
+ ch->ch_portnum = i;
+ ch->ch_tun.un_ch = ch;
+ ch->ch_pun.un_ch = ch;
+ ch->ch_tun.un_type = SERIAL_TYPE_NORMAL;
+ ch->ch_pun.un_type = SERIAL_TYPE_XPRINT;
+
+ init_waitqueue_head(&(ch->ch_flag_wait));
+ init_waitqueue_head(&(ch->ch_sleep));
+
+ init_waitqueue_head(&(ch->ch_tun.un_open_wait));
+ init_waitqueue_head(&(ch->ch_tun.un_close_wait));
+
+ init_waitqueue_head(&(ch->ch_pun.un_open_wait));
+ init_waitqueue_head(&(ch->ch_pun.un_close_wait));
+ tty_port_init(&ch->port);
+ tty_port_init(&ch->port);
+ }
+ return 0;
+}
diff --git a/drivers/staging/dgrp/digirp.h b/drivers/staging/dgrp/digirp.h
new file mode 100644
index 000000000000..33c1394fade7
--- /dev/null
+++ b/drivers/staging/dgrp/digirp.h
@@ -0,0 +1,129 @@
+/************************************************************************
+ * HP-UX Realport Daemon interface file.
+ *
+ * Copyright (C) 1998, by Digi International. All Rights Reserved.
+ ************************************************************************/
+
+#ifndef _DIGIDRP_H
+#define _DIGIDRP_H
+
+/************************************************************************
+ * This file contains defines for the ioctl() interface to
+ * the realport driver. This ioctl() interface is used by the
+ * daemon to set speed setup parameters honored by the driver.
+ ************************************************************************/
+
+struct link_struct {
+ int lk_fast_rate; /* Fast line rate to be used
+ when the delay is less-equal
+ to lk_fast_delay */
+
+ int lk_fast_delay; /* Fast line rate delay in
+ milliseconds */
+
+ int lk_slow_rate; /* Slow line rate to be used when
+ the delay is greater-equal
+ to lk_slow_delay */
+
+ int lk_slow_delay; /* Slow line rate delay in
+ milliseconds */
+
+ int lk_header_size; /* Estimated packet header size
+ when sent across the slowest
+ link. */
+};
+
+#define DIGI_GETLINK _IOW('e', 103, struct link_struct) /* Get link parameters */
+#define DIGI_SETLINK _IOW('e', 104, struct link_struct) /* Set link parameters */
+
+
+/************************************************************************
+ * This module provides application access to special Digi
+ * serial line enhancements which are not standard UNIX(tm) features.
+ ************************************************************************/
+
+struct digiflow_struct {
+ unsigned char startc; /* flow cntl start char */
+ unsigned char stopc; /* flow cntl stop char */
+};
+
+/************************************************************************
+ * Values for digi_flags
+ ************************************************************************/
+#define DIGI_IXON 0x0001 /* Handle IXON in the FEP */
+#define DIGI_FAST 0x0002 /* Fast baud rates */
+#define RTSPACE 0x0004 /* RTS input flow control */
+#define CTSPACE 0x0008 /* CTS output flow control */
+#define DSRPACE 0x0010 /* DSR output flow control */
+#define DCDPACE 0x0020 /* DCD output flow control */
+#define DTRPACE 0x0040 /* DTR input flow control */
+#define DIGI_COOK 0x0080 /* Cooked processing done in FEP */
+#define DIGI_FORCEDCD 0x0100 /* Force carrier */
+#define DIGI_ALTPIN 0x0200 /* Alternate RJ-45 pin config */
+#define DIGI_AIXON 0x0400 /* Aux flow control in fep */
+#define DIGI_PRINTER 0x0800 /* Hold port open for flow cntrl */
+#define DIGI_PP_INPUT 0x1000 /* Change parallel port to input */
+#define DIGI_422 0x4000 /* Change parallel port to input */
+#define DIGI_RTS_TOGGLE 0x8000 /* Support RTS Toggle */
+
+
+/************************************************************************
+ * Values associated with transparent print
+ ************************************************************************/
+#define DIGI_PLEN 8 /* String length */
+#define DIGI_TSIZ 10 /* Terminal string len */
+
+
+/************************************************************************
+ * Structure used with ioctl commands for DIGI parameters.
+ ************************************************************************/
+struct digi_struct {
+ unsigned short digi_flags; /* Flags (see above) */
+ unsigned short digi_maxcps; /* Max printer CPS */
+ unsigned short digi_maxchar; /* Max chars in print queue */
+ unsigned short digi_bufsize; /* Buffer size */
+ unsigned char digi_onlen; /* Length of ON string */
+ unsigned char digi_offlen; /* Length of OFF string */
+ char digi_onstr[DIGI_PLEN]; /* Printer on string */
+ char digi_offstr[DIGI_PLEN]; /* Printer off string */
+ char digi_term[DIGI_TSIZ]; /* terminal string */
+};
+
+/************************************************************************
+ * Ioctl command arguments for DIGI parameters.
+ ************************************************************************/
+/* Read params */
+#define DIGI_GETA _IOR('e', 94, struct digi_struct)
+
+/* Set params */
+#define DIGI_SETA _IOW('e', 95, struct digi_struct)
+
+/* Drain & set params */
+#define DIGI_SETAW _IOW('e', 96, struct digi_struct)
+
+/* Drain, flush & set params */
+#define DIGI_SETAF _IOW('e', 97, struct digi_struct)
+
+/* Get startc/stopc flow control characters */
+#define DIGI_GETFLOW _IOR('e', 99, struct digiflow_struct)
+
+/* Set startc/stopc flow control characters */
+#define DIGI_SETFLOW _IOW('e', 100, struct digiflow_struct)
+
+/* Get Aux. startc/stopc flow control chars */
+#define DIGI_GETAFLOW _IOR('e', 101, struct digiflow_struct)
+
+/* Set Aux. startc/stopc flow control chars */
+#define DIGI_SETAFLOW _IOW('e', 102, struct digiflow_struct)
+
+/* Set integer baud rate */
+#define DIGI_SETCUSTOMBAUD _IOW('e', 106, int)
+
+/* Get integer baud rate */
+#define DIGI_GETCUSTOMBAUD _IOR('e', 107, int)
+
+#define DIGI_GEDELAY _IOR('d', 246, int) /* Get edelay */
+#define DIGI_SEDELAY _IOW('d', 247, int) /* Get edelay */
+
+
+#endif /* _DIGIDRP_H */
diff --git a/drivers/staging/dgrp/drp.h b/drivers/staging/dgrp/drp.h
new file mode 100644
index 000000000000..84a1e7be4899
--- /dev/null
+++ b/drivers/staging/dgrp/drp.h
@@ -0,0 +1,693 @@
+/*
+ *
+ * Copyright 1999 Digi International (www.digi.com)
+ * Gene Olson <gene at digi dot com>
+ * James Puzzo <jamesp at digi dot com>
+ * Scott Kilau <scottk at digi dot 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, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY, 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.
+ *
+ */
+
+/************************************************************************
+ * Master include file for Linux Realport Driver.
+ ************************************************************************/
+
+#ifndef __DRP_H
+#define __DRP_H
+
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/semaphore.h>
+#include <linux/tty.h>
+
+
+#include "digirp.h"
+
+/************************************************************************
+ * Tuning parameters.
+ ************************************************************************/
+
+#define CHAN_MAX 64 /* Max # ports per server */
+
+#define SEQ_MAX 128 /* Max # transmit sequences (2^n) */
+#define SEQ_MASK (SEQ_MAX-1) /* Sequence buffer modulus mask */
+
+#define TBUF_MAX 4096 /* Size of transmit buffer (2^n) */
+#define RBUF_MAX 4096 /* Size of receive buffer (2^n) */
+
+#define TBUF_MASK (TBUF_MAX-1) /* Transmit buffer modulus mask */
+#define RBUF_MASK (RBUF_MAX-1) /* Receive buffer modulus mask */
+
+#define TBUF_LOW 1000 /* Transmit low water mark */
+
+#define UIO_BASE 1000 /* Base for write operations */
+#define UIO_MIN 2000 /* Minimum size application buffer */
+#define UIO_MAX 8100 /* Unix I/O buffer size */
+
+#define MON_MAX 65536 /* Monitor buffer size (2^n) */
+#define MON_MASK (MON_MAX-1) /* Monitor wrap mask */
+
+#define DPA_MAX 65536 /* DPA buffer size (2^n) */
+#define DPA_MASK (DPA_MAX-1) /* DPA wrap mask */
+#define DPA_HIGH_WATER 58000 /* Enforce flow control when
+ * over this amount
+ */
+
+#define IDLE_MAX (20 * HZ) /* Max TCP link idle time */
+
+#define MAX_DESC_LEN 100 /* Maximum length of stored PS
+ * description
+ */
+
+#define WRITEBUFLEN ((4096) + 4) /* 4 extra for alignment play space */
+
+#define VPDSIZE 512
+
+/************************************************************************
+ * Minor device decoding conventions.
+ ************************************************************************
+ *
+ * For Linux, the net and mon devices are handled via "proc", so we
+ * only have to mux the "tty" devices. Since every PortServer will
+ * have an individual major number, the PortServer number does not
+ * need to be encoded, and in fact, does not need to exist.
+ *
+ */
+
+/*
+ * Port device decoding conventions:
+ *
+ * Device 00 - 3f 64 dial-in modem devices. (tty)
+ * Device 40 - 7f 64 dial-out tty devices. (cu)
+ * Device 80 - bf 64 dial-out printer devices.
+ *
+ * IS_PRINT(dev) This is a printer device.
+ *
+ * OPEN_CATEGORY(dev) Specifies the device category. No two
+ * devices of different categories may be open
+ * at the same time.
+ *
+ * The following require the category returned by OPEN_CATEGORY().
+ *
+ * OPEN_WAIT_AVAIL(cat) Waits on open until the device becomes
+ * available. Fails if NDELAY specified.
+ *
+ * OPEN_WAIT_CARRIER(cat) Waits on open if carrier is not present.
+ * Succeeds if NDELAY is given.
+ *
+ * OPEN_FORCES_CARRIER(cat) Carrier is forced high on open.
+ *
+ */
+
+#define PORT_NUM(dev) ((dev) & 0x3f)
+
+#define OPEN_CATEGORY(dev) ((((dev) & 0x80) & 0x40))
+#define IS_PRINT(dev) (((dev) & 0xff) >= 0x80)
+
+#define OPEN_WAIT_AVAIL(cat) (((cat) & 0x40) == 0x000)
+#define OPEN_WAIT_CARRIER(cat) (((cat) & 0x40) == 0x000)
+#define OPEN_FORCES_CARRIER(cat) (((cat) & 0x40) != 0x000)
+
+
+/************************************************************************
+ * Modem signal defines for 16450/16550 compatible FEP.
+ * set in ch_mout, ch_mflow, ch_mlast etc
+ ************************************************************************/
+
+/* TODO : Re-verify that these modem signal definitions are correct */
+
+#define DM_DTR 0x01
+#define DM_RTS 0x02
+#define DM_RTS_TOGGLE 0x04
+
+#define DM_OUT1 0x04
+#define DM_OUT2 0x08
+
+#define DM_CTS 0x10
+#define DM_DSR 0x20
+#define DM_RI 0x40
+#define DM_CD 0x80 /* This is the DCD flag */
+
+
+/************************************************************************
+ * Realport Event Flags.
+ ************************************************************************/
+
+#define EV_OPU 0x0001 /* Ouput paused by client */
+#define EV_OPS 0x0002 /* Output paused by XOFF */
+#define EV_OPX 0x0004 /* Output paused by XXOFF */
+#define EV_OPH 0x0008 /* Output paused by MFLOW */
+#define EV_IPU 0x0010 /* Input paused by client */
+#define EV_IPS 0x0020 /* Input paused by hi/low water */
+#define EV_TXB 0x0040 /* Transmit break pending */
+#define EV_TXI 0x0080 /* Transmit immediate pending */
+#define EV_TXF 0x0100 /* Transmit flow control pending */
+#define EV_RXB 0x0200 /* Break received */
+
+
+/************************************************************************
+ * Realport CFLAGS.
+ ************************************************************************/
+
+#define CF_CS5 0x0000 /* 5 bit characters */
+#define CF_CS6 0x0010 /* 6 bit characters */
+#define CF_CS7 0x0020 /* 7 bit characters */
+#define CF_CS8 0x0030 /* 8 bit characters */
+#define CF_CSIZE 0x0030 /* Character size */
+#define CF_CSTOPB 0x0040 /* Two stop bits */
+#define CF_CREAD 0x0080 /* Enable receiver */
+#define CF_PARENB 0x0100 /* Enable parity */
+#define CF_PARODD 0x0200 /* Odd parity */
+#define CF_HUPCL 0x0400 /* Drop DTR on close */
+
+
+/************************************************************************
+ * Realport XFLAGS.
+ ************************************************************************/
+
+#define XF_XPAR 0x0001 /* Enable Mark/Space Parity */
+#define XF_XMODEM 0x0002 /* Enable in-band modem signalling */
+#define XF_XCASE 0x0004 /* Convert special characters */
+#define XF_XEDATA 0x0008 /* Error data in stream */
+#define XF_XTOSS 0x0010 /* Toss IXANY characters */
+#define XF_XIXON 0x0020 /* xxon/xxoff enable */
+
+
+/************************************************************************
+ * Realport IFLAGS.
+ ************************************************************************/
+
+#define IF_IGNBRK 0x0001 /* Ignore input break */
+#define IF_BRKINT 0x0002 /* Break interrupt */
+#define IF_IGNPAR 0x0004 /* Ignore error characters */
+#define IF_PARMRK 0x0008 /* Error chars marked with 0xff */
+#define IF_INPCK 0x0010 /* Input parity checking enabled */
+#define IF_ISTRIP 0x0020 /* Input chars masked with 0x7F */
+#define IF_IXON 0x0400 /* Output software flow control */
+#define IF_IXANY 0x0800 /* Restart output on any char */
+#define IF_IXOFF 0x1000 /* Input software flow control */
+#define IF_DOSMODE 0x8000 /* 16450-compatible errors */
+
+
+/************************************************************************
+ * Realport OFLAGS.
+ ************************************************************************/
+
+#define OF_OLCUC 0x0002 /* Map lower to upper case */
+#define OF_ONLCR 0x0004 /* Map NL to CR-NL */
+#define OF_OCRNL 0x0008 /* Map CR to NL */
+#define OF_ONOCR 0x0010 /* No CR output at column 0 */
+#define OF_ONLRET 0x0020 /* Assume NL does NL/CR */
+#define OF_TAB3 0x1800 /* Tabs expand to 8 spaces */
+#define OF_TABDLY 0x1800 /* Tab delay */
+
+/************************************************************************
+ * Unit flag definitions for un_flag.
+ ************************************************************************/
+
+/* These are the DIGI unit flags */
+#define UN_EXCL 0x00010000 /* Exclusive open */
+#define UN_STICKY 0x00020000 /* TTY Settings are now sticky */
+#define UN_BUSY 0x00040000 /* Some work this channel */
+#define UN_PWAIT 0x00080000 /* Printer waiting for terminal */
+#define UN_TIME 0x00100000 /* Waiting on time */
+#define UN_EMPTY 0x00200000 /* Waiting output queue empty */
+#define UN_LOW 0x00400000 /* Waiting output low water */
+#define UN_DIGI_MASK 0x00FF0000 /* Waiting output low water */
+
+/*
+ * Definitions for async_struct (and serial_struct) flags field
+ *
+ * these are the ASYNC flags copied from serial.h
+ *
+ */
+#define UN_HUP_NOTIFY 0x0001 /* Notify getty on hangups and
+ * closes on the callout port
+ */
+#define UN_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
+#define UN_SAK 0x0004 /* Secure Attention Key (Orange book) */
+#define UN_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
+
+#define UN_SPD_MASK 0x0030
+#define UN_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
+#define UN_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
+#define UN_SPD_CUST 0x0030 /* Use user-specified divisor */
+
+#define UN_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
+#define UN_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
+
+#define UN_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
+#define UN_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
+#define UN_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
+
+#define UN_FLAGS 0x0FFF /* Possible legal async flags */
+#define UN_USR_MASK 0x0430 /* Legal flags that non-privileged
+ * users can set or reset
+ */
+
+#define UN_INITIALIZED 0x80000000 /* Serial port was initialized */
+#define UN_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
+#define UN_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
+#define UN_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
+#define UN_CLOSING 0x08000000 /* Serial port is closing */
+#define UN_CTS_FLOW 0x04000000 /* Do CTS flow control */
+#define UN_CHECK_CD 0x02000000 /* i.e., CLOCAL */
+#define UN_SHARE_IRQ 0x01000000 /* for multifunction cards */
+
+
+/************************************************************************
+ * Structure for terminal or printer unit. struct un_struct
+ *
+ * Note that in some places the code assumes the "tty_t" is placed
+ * first in the structure.
+ ************************************************************************/
+
+struct un_struct {
+ struct tty_struct *un_tty; /* System TTY struct */
+ struct ch_struct *un_ch; /* Associated channel */
+
+ ushort un_open_count; /* Successful open count */
+ int un_flag; /* Unit flags */
+ ushort un_tbusy; /* Busy transmit count */
+
+ wait_queue_head_t un_open_wait;
+ wait_queue_head_t un_close_wait;
+ ushort un_type;
+ struct device *un_sysfs;
+};
+
+
+/************************************************************************
+ * Channel State Numbers for ch_state.
+ ************************************************************************/
+
+/*
+ * The ordering is important.
+ *
+ * state <= CS_WAIT_CANCEL implies the channel is definitely closed.
+ *
+ * state >= CS_WAIT_FAIL implies the channel is definitely open.
+ *
+ * state >= CS_READY implies data is allowed on the channel.
+ */
+
+enum dgrp_ch_state_t {
+ CS_IDLE = 0, /* Channel is idle */
+ CS_WAIT_OPEN = 1, /* Waiting for Immediate Open Resp */
+ CS_WAIT_CANCEL = 2, /* Waiting for Per/Incom Cancel Resp */
+ CS_WAIT_FAIL = 3, /* Waiting for Immed Open Failure */
+ CS_SEND_QUERY = 4, /* Ready to send Port Query */
+ CS_WAIT_QUERY = 5, /* Waiting for Port Query Response */
+ CS_READY = 6, /* Ready to accept commands and data */
+ CS_SEND_CLOSE = 7, /* Ready to send Close Request */
+ CS_WAIT_CLOSE = 8 /* Waiting for Close Response */
+};
+
+/************************************************************************
+ * Device flag definitions for ch_flag.
+ ************************************************************************/
+
+/*
+ * Note that the state of the two carrier based flags is key. When
+ * we check for carrier state transitions, we look at the current
+ * physical state of the DCD line and compare it with PHYS_CD (which
+ * was the state the last time we checked), and we also determine
+ * a new virtual state (composite of the physical state, FORCEDCD,
+ * CLOCAL, etc.) and compare it with VIRT_CD.
+ *
+ * VIRTUAL transitions high will have the side effect of waking blocked
+ * opens.
+ *
+ * PHYSICAL transitions low will cause hangups to occur _IF_ the virtual
+ * state is also low. We DON'T want to hangup on a PURE virtual drop.
+ */
+
+#define CH_HANGUP 0x00002 /* Server port ready to close */
+
+#define CH_VIRT_CD 0x00004 /* Carrier was virtually present */
+#define CH_PHYS_CD 0x00008 /* Carrier was physically present */
+
+#define CH_CLOCAL 0x00010 /* CLOCAL set in cflags */
+#define CH_BAUD0 0x00020 /* Baud rate zero hangup */
+
+#define CH_FAST_READ 0x00040 /* Fast reads are enabled */
+#define CH_FAST_WRITE 0x00080 /* Fast writes are enabled */
+
+#define CH_PRON 0x00100 /* Printer on string active */
+#define CH_RX_FLUSH 0x00200 /* Flushing receive data */
+#define CH_LOW 0x00400 /* Thread waiting for LOW water */
+#define CH_EMPTY 0x00800 /* Thread waiting for EMPTY */
+#define CH_DRAIN 0x01000 /* Close is waiting to drain */
+#define CH_INPUT 0x02000 /* Thread waiting for INPUT */
+#define CH_RXSTOP 0x04000 /* Stop output to ldisc */
+#define CH_PARAM 0x08000 /* A parameter was updated */
+#define CH_WAITING_SYNC 0x10000 /* A pending sync was assigned
+ * to this port.
+ */
+#define CH_PORT_GONE 0x20000 /* Port has disappeared */
+#define CH_TX_BREAK 0x40000 /* TX Break to be sent,
+ * but has not yet.
+ */
+
+/************************************************************************
+ * Types of Open Requests for ch_otype.
+ ************************************************************************/
+
+#define OTYPE_IMMEDIATE 0 /* Immediate Open */
+#define OTYPE_PERSISTENT 1 /* Persistent Open */
+#define OTYPE_INCOMING 2 /* Incoming Open */
+
+
+/************************************************************************
+ * Request/Response flags.
+ ************************************************************************/
+
+#define RR_SEQUENCE 0x0001 /* Get server RLAST, TIN */
+#define RR_STATUS 0x0002 /* Get server MINT, EINT */
+#define RR_BUFFER 0x0004 /* Get server RSIZE, TSIZE */
+#define RR_CAPABILITY 0x0008 /* Get server port capabilities */
+
+#define RR_TX_FLUSH 0x0040 /* Flush output buffers */
+#define RR_RX_FLUSH 0x0080 /* Flush input buffers */
+
+#define RR_TX_STOP 0x0100 /* Pause output */
+#define RR_RX_STOP 0x0200 /* Pause input */
+#define RR_TX_START 0x0400 /* Start output */
+#define RR_RX_START 0x0800 /* Start input */
+
+#define RR_TX_BREAK 0x1000 /* Send BREAK */
+#define RR_TX_ICHAR 0x2000 /* Send character immediate */
+
+
+/************************************************************************
+ * Channel information structure. struct ch_struct
+ ************************************************************************/
+
+struct ch_struct {
+ struct digi_struct ch_digi; /* Digi variables */
+ int ch_edelay; /* Digi edelay */
+
+ struct tty_port port;
+ struct un_struct ch_tun; /* Terminal unit info */
+ struct un_struct ch_pun; /* Printer unit info */
+
+ struct nd_struct *ch_nd; /* Node pointer */
+ u8 *ch_tbuf; /* Local Transmit Buffer */
+ u8 *ch_rbuf; /* Local Receive Buffer */
+ ulong ch_cpstime; /* Printer CPS time */
+ ulong ch_waketime; /* Printer wake time */
+
+ ulong ch_flag; /* CH_* flags */
+
+ enum dgrp_ch_state_t ch_state; /* CS_* Protocol state */
+ ushort ch_send; /* Bit vector of RR_* requests */
+ ushort ch_expect; /* Bit vector of RR_* responses */
+ ushort ch_wait_carrier; /* Thread count waiting for carrier */
+ ushort ch_wait_count[3]; /* Thread count waiting by otype */
+
+ ushort ch_portnum; /* Port number */
+ ushort ch_open_count; /* Successful open count */
+ ushort ch_category; /* Device category */
+ ushort ch_open_error; /* Last open error number */
+ ushort ch_break_time; /* Pending break request time */
+ ushort ch_cpsrem; /* Printer CPS remainder */
+ ushort ch_ocook; /* Realport fastcook oflags */
+ ushort ch_inwait; /* Thread count in CLIST input */
+
+ ushort ch_tin; /* Local transmit buffer in ptr */
+ ushort ch_tout; /* Local transmit buffer out ptr */
+ ushort ch_s_tin; /* Realport TIN */
+ ushort ch_s_tpos; /* Realport TPOS */
+ ushort ch_s_tsize; /* Realport TSIZE */
+ ushort ch_s_treq; /* Realport TREQ */
+ ushort ch_s_elast; /* Realport ELAST */
+
+ ushort ch_rin; /* Local receive buffer in ptr */
+ ushort ch_rout; /* Local receive buffer out ptr */
+ ushort ch_s_rin; /* Realport RIN */
+ /* David Fries 7-13-2001, ch_s_rin should be renamed ch_s_rout because
+ * the variable we want to represent is the PortServer's ROUT, which is
+ * the sequence number for the next byte the PortServer will send us.
+ * RIN is the sequence number for the next byte the PortServer will
+ * receive from the uart. The port server will send data as long as
+ * ROUT is less than RWIN. What would happen is the port is opened, it
+ * receives data, it gives the value of RIN, we set the RWIN to
+ * RIN+RBUF_MAX-1, it sends us RWIN-ROUT bytes which overflows. ROUT
+ * is set to zero when the port is opened, so we start at zero and
+ * count up as data is received.
+ */
+ ushort ch_s_rwin; /* Realport RWIN */
+ ushort ch_s_rsize; /* Realport RSIZE */
+
+ ushort ch_tmax; /* Local TMAX */
+ ushort ch_ttime; /* Local TTIME */
+ ushort ch_rmax; /* Local RMAX */
+ ushort ch_rtime; /* Local RTIME */
+ ushort ch_rlow; /* Local RLOW */
+ ushort ch_rhigh; /* Local RHIGH */
+
+ ushort ch_s_tmax; /* Realport TMAX */
+ ushort ch_s_ttime; /* Realport TTIME */
+ ushort ch_s_rmax; /* Realport RMAX */
+ ushort ch_s_rtime; /* Realport RTIME */
+ ushort ch_s_rlow; /* Realport RLOW */
+ ushort ch_s_rhigh; /* Realport RHIGH */
+
+ ushort ch_brate; /* Local baud rate */
+ ushort ch_cflag; /* Local tty cflags */
+ ushort ch_iflag; /* Local tty iflags */
+ ushort ch_oflag; /* Local tty oflags */
+ ushort ch_xflag; /* Local tty xflags */
+
+ ushort ch_s_brate; /* Realport BRATE */
+ ushort ch_s_cflag; /* Realport CFLAG */
+ ushort ch_s_iflag; /* Realport IFLAG */
+ ushort ch_s_oflag; /* Realport OFLAG */
+ ushort ch_s_xflag; /* Realport XFLAG */
+
+ u8 ch_otype; /* Open request type */
+ u8 ch_pscan_savechar; /* Last character read by parity scan */
+ u8 ch_pscan_state; /* PScan State based on last 2 chars */
+ u8 ch_otype_waiting; /* Type of open pending in server */
+ u8 ch_flush_seq; /* Receive flush end sequence */
+ u8 ch_s_mlast; /* Realport MLAST */
+
+ u8 ch_mout; /* Local MOUT */
+ u8 ch_mflow; /* Local MFLOW */
+ u8 ch_mctrl; /* Local MCTRL */
+ u8 ch_xon; /* Local XON */
+ u8 ch_xoff; /* Local XOFF */
+ u8 ch_lnext; /* Local LNEXT */
+ u8 ch_xxon; /* Local XXON */
+ u8 ch_xxoff; /* Local XXOFF */
+
+ u8 ch_s_mout; /* Realport MOUT */
+ u8 ch_s_mflow; /* Realport MFLOW */
+ u8 ch_s_mctrl; /* Realport MCTRL */
+ u8 ch_s_xon; /* Realport XON */
+ u8 ch_s_xoff; /* Realport XOFF */
+ u8 ch_s_lnext; /* Realport LNEXT */
+ u8 ch_s_xxon; /* Realport XXON */
+ u8 ch_s_xxoff; /* Realport XXOFF */
+
+ wait_queue_head_t ch_flag_wait; /* Wait queue for ch_flag changes */
+ wait_queue_head_t ch_sleep; /* Wait queue for my_sleep() */
+
+ int ch_custom_speed; /* Realport custom speed */
+ int ch_txcount; /* Running TX count */
+ int ch_rxcount; /* Running RX count */
+};
+
+
+/************************************************************************
+ * Node State definitions.
+ ************************************************************************/
+
+enum dgrp_nd_state_t {
+ NS_CLOSED = 0, /* Network device is closed */
+ NS_IDLE = 1, /* Network connection inactive */
+ NS_SEND_QUERY = 2, /* Send server query */
+ NS_WAIT_QUERY = 3, /* Wait for query response */
+ NS_READY = 4, /* Network ready */
+ NS_SEND_ERROR = 5 /* Must send error hangup */
+};
+
+#define ND_STATE_STR(x) \
+ ((x) == NS_CLOSED ? "CLOSED" : \
+ ((x) == NS_IDLE ? "IDLE" : \
+ ((x) == NS_SEND_QUERY ? "SEND_QUERY" : \
+ ((x) == NS_WAIT_QUERY ? "WAIT_QUERY" : \
+ ((x) == NS_READY ? "READY" : \
+ ((x) == NS_SEND_ERROR ? "SEND_ERROR" : "UNKNOWN"))))))
+
+/************************************************************************
+ * Node Flag definitions.
+ ************************************************************************/
+
+#define ND_SELECT 0x0001 /* Multiple net read selects */
+#define ND_DEB_WAIT 0x0002 /* Debug Device waiting */
+
+
+/************************************************************************
+ * Monitoring flag definitions.
+ ************************************************************************/
+
+#define MON_WAIT_DATA 0x0001 /* Waiting for buffer data */
+#define MON_WAIT_SPACE 0x0002 /* Waiting for buffer space */
+
+/************************************************************************
+ * DPA flag definitions.
+ ************************************************************************/
+
+#define DPA_WAIT_DATA 0x0001 /* Waiting for buffer data */
+#define DPA_WAIT_SPACE 0x0002 /* Waiting for buffer space */
+
+
+/************************************************************************
+ * Definitions taken from Realport Dump.
+ ************************************************************************/
+
+#define RPDUMP_MAGIC "Digi-RealPort-1.0"
+
+#define RPDUMP_MESSAGE 0xE2 /* Descriptive message */
+#define RPDUMP_RESET 0xE7 /* Connection reset */
+#define RPDUMP_CLIENT 0xE8 /* Client data */
+#define RPDUMP_SERVER 0xE9 /* Server data */
+
+
+/************************************************************************
+ * Node request/response definitions.
+ ************************************************************************/
+
+#define NR_ECHO 0x0001 /* Server echo packet */
+#define NR_IDENT 0x0002 /* Server Product ID */
+#define NR_CAPABILITY 0x0004 /* Server Capabilties */
+#define NR_VPD 0x0008 /* Server VPD, if any */
+#define NR_PASSWORD 0x0010 /* Server Password */
+
+/************************************************************************
+ * Registration status of the node's Linux struct tty_driver structures.
+ ************************************************************************/
+#define SERIAL_TTDRV_REG 0x0001 /* nd_serial_ttdriver registered */
+#define CALLOUT_TTDRV_REG 0x0002 /* nd_callout_ttdriver registered */
+#define XPRINT_TTDRV_REG 0x0004 /* nd_xprint_ttdriver registered */
+
+
+/************************************************************************
+ * Node structure. There exists one of these for each associated
+ * realport server.
+ ************************************************************************/
+
+struct nd_struct {
+ struct list_head list;
+ long nd_major; /* Node's major number */
+ long nd_ID; /* Node's ID code */
+
+ char nd_serial_name[50]; /* "tty_dgrp_<id>_" + null */
+ char nd_callout_name[50]; /* "cu_dgrp_<id>_" + null */
+ char nd_xprint_name[50]; /* "pr_dgrp_<id>_" + null */
+
+ char password[16]; /* Password for server, if needed */
+ int nd_tty_ref_cnt; /* Linux tty reference count */
+
+ struct proc_dir_entry *nd_net_de; /* Dir entry for /proc/dgrp/net */
+ struct proc_dir_entry *nd_mon_de; /* Dir entry for /proc/dgrp/mon */
+ struct proc_dir_entry *nd_ports_de; /* Dir entry for /proc/dgrp/ports*/
+ struct proc_dir_entry *nd_dpa_de; /* Dir entry for /proc/dgrp/dpa */
+
+ spinlock_t nd_lock; /* General node lock */
+
+ struct semaphore nd_net_semaphore; /* Net read/write lock */
+ struct semaphore nd_mon_semaphore; /* Monitor buffer lock */
+ spinlock_t nd_dpa_lock; /* DPA buffer lock */
+
+ enum dgrp_nd_state_t nd_state; /* NS_* network state */
+ int nd_chan_count; /* # active channels */
+ int nd_flag; /* Node flags */
+ int nd_send; /* Responses to send */
+ int nd_expect; /* Responses we expect */
+
+ u8 *nd_iobuf; /* Network R/W Buffer */
+ wait_queue_head_t nd_tx_waitq; /* Network select wait queue */
+
+ u8 *nd_inputbuf; /* Input Buffer */
+ u8 *nd_inputflagbuf; /* Input Flags Buffer */
+
+ int nd_tx_deposit; /* Accumulated transmit deposits */
+ int nd_tx_charge; /* Accumulated transmit charges */
+ int nd_tx_credit; /* Current TX credit */
+ int nd_tx_ready; /* Ready to transmit */
+ int nd_tx_work; /* TX work waiting */
+ ulong nd_tx_time; /* Last transmit time */
+ ulong nd_poll_time; /* Next scheduled poll time */
+
+ int nd_delay; /* Current TX delay */
+ int nd_rate; /* Current TX rate */
+ struct link_struct nd_link; /* Link speed params. */
+
+ int nd_seq_in; /* TX seq in ptr */
+ int nd_seq_out; /* TX seq out ptr */
+ int nd_unack; /* Unacknowledged byte count */
+ int nd_remain; /* Remaining receive bytes */
+ int nd_tx_module; /* Current TX module # */
+ int nd_rx_module; /* Current RX module # */
+ char *nd_error; /* Protocol error message */
+
+ int nd_write_count; /* drp_write() call count */
+ int nd_read_count; /* drp_read() count */
+ int nd_send_count; /* TCP message sent */
+ int nd_tx_byte; /* Transmit byte count */
+ int nd_rx_byte; /* Receive byte count */
+
+ ulong nd_mon_lbolt; /* Monitor start time */
+ int nd_mon_flag; /* Monitor flags */
+ int nd_mon_in; /* Monitor in pointer */
+ int nd_mon_out; /* Monitor out pointer */
+ wait_queue_head_t nd_mon_wqueue; /* Monitor wait queue (on flags) */
+ u8 *nd_mon_buf; /* Monitor buffer */
+
+ ulong nd_dpa_lbolt; /* DPA start time */
+ int nd_dpa_flag; /* DPA flags */
+ int nd_dpa_in; /* DPA in pointer */
+ int nd_dpa_out; /* DPA out pointer */
+ wait_queue_head_t nd_dpa_wqueue; /* DPA wait queue (on flags) */
+ u8 *nd_dpa_buf; /* DPA buffer */
+
+ uint nd_dpa_debug;
+ uint nd_dpa_port;
+
+ wait_queue_head_t nd_seq_wque[SEQ_MAX]; /* TX thread wait queues */
+ u8 nd_seq_wait[SEQ_MAX]; /* Transmit thread wait count */
+
+ ushort nd_seq_size[SEQ_MAX]; /* Transmit seq packet size */
+ ulong nd_seq_time[SEQ_MAX]; /* Transmit seq packet time */
+
+ ushort nd_hw_ver; /* HW version returned from PS */
+ ushort nd_sw_ver; /* SW version returned from PS */
+ uint nd_hw_id; /* HW ID returned from PS */
+ u8 nd_ps_desc[MAX_DESC_LEN+1]; /* Description from PS */
+ uint nd_vpd_len; /* VPD len, if any */
+ u8 nd_vpd[VPDSIZE]; /* VPD, if any */
+
+ ulong nd_ttdriver_flags; /* Registration status */
+ struct tty_driver *nd_serial_ttdriver; /* Linux TTYDRIVER structure */
+ struct tty_driver *nd_callout_ttdriver; /* Linux TTYDRIVER structure */
+ struct tty_driver *nd_xprint_ttdriver; /* Linux TTYDRIVER structure */
+
+ u8 *nd_writebuf; /* Used to cache data read
+ * from user
+ */
+ struct ch_struct nd_chan[CHAN_MAX]; /* Channel array */
+ struct device *nd_class_dev; /* Hang our sysfs stuff off of here */
+};
+
+#endif /* __DRP_H */
diff --git a/drivers/staging/et131x/et131x.c b/drivers/staging/et131x/et131x.c
index 029725c89e58..413da0d6b9f6 100644
--- a/drivers/staging/et131x/et131x.c
+++ b/drivers/staging/et131x/et131x.c
@@ -3955,12 +3955,7 @@ static void et131x_hwaddr_init(struct et131x_adapter *adapter)
* EEPROM then we need to generate the last octet and set it on the
* device
*/
- if (adapter->rom_addr[0] == 0x00 &&
- adapter->rom_addr[1] == 0x00 &&
- adapter->rom_addr[2] == 0x00 &&
- adapter->rom_addr[3] == 0x00 &&
- adapter->rom_addr[4] == 0x00 &&
- adapter->rom_addr[5] == 0x00) {
+ if (is_zero_ether_addr(adapter->rom_addr)) {
/*
* We need to randomly generate the last octet so we
* decrease our chances of setting the mac address to
@@ -3995,16 +3990,14 @@ static void et131x_hwaddr_init(struct et131x_adapter *adapter)
static int et131x_pci_init(struct et131x_adapter *adapter,
struct pci_dev *pdev)
{
- int cap = pci_pcie_cap(pdev);
u16 max_payload;
- u16 ctl;
int i, rc;
rc = et131x_init_eeprom(adapter);
if (rc < 0)
goto out;
- if (!cap) {
+ if (!pci_is_pcie(pdev)) {
dev_err(&pdev->dev, "Missing PCIe capabilities\n");
goto err_out;
}
@@ -4012,7 +4005,7 @@ static int et131x_pci_init(struct et131x_adapter *adapter,
/* Let's set up the PORT LOGIC Register. First we need to know what
* the max_payload_size is
*/
- if (pci_read_config_word(pdev, cap + PCI_EXP_DEVCAP, &max_payload)) {
+ if (pcie_capability_read_word(pdev, PCI_EXP_DEVCAP, &max_payload)) {
dev_err(&pdev->dev,
"Could not read PCI config space for Max Payload Size\n");
goto err_out;
@@ -4049,17 +4042,10 @@ static int et131x_pci_init(struct et131x_adapter *adapter,
}
/* Change the max read size to 2k */
- if (pci_read_config_word(pdev, cap + PCI_EXP_DEVCTL, &ctl)) {
+ if (pcie_capability_clear_and_set_word(pdev, PCI_EXP_DEVCTL,
+ PCI_EXP_DEVCTL_READRQ, 0x4 << 12)) {
dev_err(&pdev->dev,
- "Could not read PCI config space for Max read size\n");
- goto err_out;
- }
-
- ctl = (ctl & ~PCI_EXP_DEVCTL_READRQ) | (0x04 << 12);
-
- if (pci_write_config_word(pdev, cap + PCI_EXP_DEVCTL, ctl)) {
- dev_err(&pdev->dev,
- "Could not write PCI config space for Max read size\n");
+ "Couldn't change PCI config space for Max read size\n");
goto err_out;
}
diff --git a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c
index f8b8e71284d9..47cc365c630b 100644
--- a/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c
+++ b/drivers/staging/ft1000/ft1000-pcmcia/ft1000_dnld.c
@@ -94,27 +94,27 @@ void put_request_value(struct net_device *dev, long lvalue);
u16 hdr_checksum(struct pseudo_hdr *pHdr);
struct dsp_file_hdr {
- u32 version_id; // Version ID of this image format.
- u32 package_id; // Package ID of code release.
- u32 build_date; // Date/time stamp when file was built.
- u32 commands_offset; // Offset to attached commands in Pseudo Hdr format.
- u32 loader_offset; // Offset to bootloader code.
- u32 loader_code_address; // Start address of bootloader.
- u32 loader_code_end; // Where bootloader code ends.
+ u32 version_id; /* Version ID of this image format. */
+ u32 package_id; /* Package ID of code release. */
+ u32 build_date; /* Date/time stamp when file was built. */
+ u32 commands_offset; /* Offset to attached commands in Pseudo Hdr format. */
+ u32 loader_offset; /* Offset to bootloader code. */
+ u32 loader_code_address; /* Start address of bootloader. */
+ u32 loader_code_end; /* Where bootloader code ends. */
u32 loader_code_size;
- u32 version_data_offset; // Offset were scrambled version data begins.
- u32 version_data_size; // Size, in words, of scrambled version data.
- u32 nDspImages; // Number of DSP images in file.
+ u32 version_data_offset; /* Offset were scrambled version data begins. */
+ u32 version_data_size; /* Size, in words, of scrambled version data. */
+ u32 nDspImages; /* Number of DSP images in file. */
} __attribute__ ((packed));
struct dsp_image_info {
- u32 coff_date; // Date/time when DSP Coff image was built.
- u32 begin_offset; // Offset in file where image begins.
- u32 end_offset; // Offset in file where image begins.
- u32 run_address; // On chip Start address of DSP code.
- u32 image_size; // Size of image.
- u32 version; // Embedded version # of DSP code.
- unsigned short checksum; // Dsp File checksum
+ u32 coff_date; /* Date/time when DSP Coff image was built. */
+ u32 begin_offset; /* Offset in file where image begins. */
+ u32 end_offset; /* Offset in file where image begins. */
+ u32 run_address; /* On chip Start address of DSP code. */
+ u32 image_size; /* Size of image. */
+ u32 version; /* Embedded version # of DSP code. */
+ unsigned short checksum; /* Dsp File checksum */
unsigned short pad1;
} __attribute__ ((packed));
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c b/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c
index 31929ef5332d..809fa4886961 100644
--- a/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_hw.c
@@ -73,7 +73,7 @@ static int ft1000_control(struct ft1000_device *ft1000dev, unsigned int pipe,
}
ret = usb_control_msg(ft1000dev->dev, pipe, request, requesttype,
- value, index, data, size, LARGE_TIMEOUT);
+ value, index, data, size, timeout);
if (ret > 0)
ret = 0;
@@ -110,7 +110,7 @@ int ft1000_read_register(struct ft1000_device *ft1000dev, u16* Data,
nRegIndx,
Data,
2,
- LARGE_TIMEOUT);
+ USB_CTRL_GET_TIMEOUT);
return ret;
}
@@ -143,7 +143,7 @@ int ft1000_write_register(struct ft1000_device *ft1000dev, u16 value,
nRegIndx,
NULL,
0,
- LARGE_TIMEOUT);
+ USB_CTRL_SET_TIMEOUT);
return ret;
}
@@ -178,7 +178,7 @@ int ft1000_read_dpram32(struct ft1000_device *ft1000dev, u16 indx, u8 *buffer,
indx,
buffer,
cnt,
- LARGE_TIMEOUT);
+ USB_CTRL_GET_TIMEOUT);
return ret;
}
@@ -215,7 +215,7 @@ int ft1000_write_dpram32(struct ft1000_device *ft1000dev, u16 indx, u8 *buffer,
indx,
buffer,
cnt,
- LARGE_TIMEOUT);
+ USB_CTRL_SET_TIMEOUT);
return ret;
}
@@ -255,7 +255,7 @@ int ft1000_read_dpram16(struct ft1000_device *ft1000dev, u16 indx, u8 *buffer,
indx,
buffer,
2,
- LARGE_TIMEOUT);
+ USB_CTRL_GET_TIMEOUT);
return ret;
}
@@ -294,7 +294,7 @@ int ft1000_write_dpram16(struct ft1000_device *ft1000dev, u16 indx, u16 value, u
indx,
NULL,
0,
- LARGE_TIMEOUT);
+ USB_CTRL_SET_TIMEOUT);
return ret;
}
diff --git a/drivers/staging/ft1000/ft1000-usb/ft1000_usb.h b/drivers/staging/ft1000/ft1000-usb/ft1000_usb.h
index 642bb89942f5..2aa6a1c7fd38 100644
--- a/drivers/staging/ft1000/ft1000-usb/ft1000_usb.h
+++ b/drivers/staging/ft1000/ft1000-usb/ft1000_usb.h
@@ -36,8 +36,6 @@ struct app_info_block {
#define FT1000_STATUS_CLOSING 0x01
-#define LARGE_TIMEOUT 5000
-
#define DSPBCMSGID 0x10
/* Electrabuzz specific DPRAM mapping */
diff --git a/drivers/staging/gdm72xx/gdm_qos.c b/drivers/staging/gdm72xx/gdm_qos.c
index 80bde053fbc2..e26c6a8b2627 100644
--- a/drivers/staging/gdm72xx/gdm_qos.c
+++ b/drivers/staging/gdm72xx/gdm_qos.c
@@ -106,8 +106,8 @@ void gdm_qos_init(void *nic_ptr)
for (i = 0 ; i < QOS_MAX; i++) {
INIT_LIST_HEAD(&qcb->qos_list[i]);
- qcb->csr[i].QoSBufCount = 0;
- qcb->csr[i].Enabled = 0;
+ qcb->csr[i].qos_buf_count = 0;
+ qcb->csr[i].enabled = 0;
}
qcb->qos_list_cnt = 0;
@@ -133,8 +133,8 @@ void gdm_qos_release_list(void *nic_ptr)
spin_lock_irqsave(&qcb->qos_lock, flags);
for (i = 0; i < QOS_MAX; i++) {
- qcb->csr[i].QoSBufCount = 0;
- qcb->csr[i].Enabled = 0;
+ qcb->csr[i].qos_buf_count = 0;
+ qcb->csr[i].enabled = 0;
}
qcb->qos_list_cnt = 0;
@@ -153,42 +153,42 @@ static u32 chk_ipv4_rule(struct gdm_wimax_csr_s *csr, u8 *Stream, u8 *port)
{
int i;
- if (csr->ClassifierRuleEnable&IPTYPEOFSERVICE) {
- if (((Stream[1] & csr->IPToSMask) < csr->IPToSLow) ||
- ((Stream[1] & csr->IPToSMask) > csr->IPToSHigh))
+ if (csr->classifier_rule_en&IPTYPEOFSERVICE) {
+ if (((Stream[1] & csr->ip2s_mask) < csr->ip2s_lo) ||
+ ((Stream[1] & csr->ip2s_mask) > csr->ip2s_hi))
return 1;
}
- if (csr->ClassifierRuleEnable&PROTOCOL) {
- if (Stream[9] != csr->Protocol)
+ if (csr->classifier_rule_en&PROTOCOL) {
+ if (Stream[9] != csr->protocol)
return 1;
}
- if (csr->ClassifierRuleEnable&IPMASKEDSRCADDRESS) {
+ if (csr->classifier_rule_en&IPMASKEDSRCADDRESS) {
for (i = 0; i < 4; i++) {
- if ((Stream[12 + i] & csr->IPSrcAddrMask[i]) !=
- (csr->IPSrcAddr[i] & csr->IPSrcAddrMask[i]))
+ if ((Stream[12 + i] & csr->ipsrc_addrmask[i]) !=
+ (csr->ipsrc_addr[i] & csr->ipsrc_addrmask[i]))
return 1;
}
}
- if (csr->ClassifierRuleEnable&IPMASKEDDSTADDRESS) {
+ if (csr->classifier_rule_en&IPMASKEDDSTADDRESS) {
for (i = 0; i < 4; i++) {
- if ((Stream[16 + i] & csr->IPDstAddrMask[i]) !=
- (csr->IPDstAddr[i] & csr->IPDstAddrMask[i]))
+ if ((Stream[16 + i] & csr->ipdst_addrmask[i]) !=
+ (csr->ipdst_addr[i] & csr->ipdst_addrmask[i]))
return 1;
}
}
- if (csr->ClassifierRuleEnable&PROTOCOLSRCPORTRANGE) {
+ if (csr->classifier_rule_en&PROTOCOLSRCPORTRANGE) {
i = ((port[0]<<8)&0xff00)+port[1];
- if ((i < csr->SrcPortLow) || (i > csr->SrcPortHigh))
+ if ((i < csr->srcport_lo) || (i > csr->srcport_hi))
return 1;
}
- if (csr->ClassifierRuleEnable&PROTOCOLDSTPORTRANGE) {
+ if (csr->classifier_rule_en&PROTOCOLDSTPORTRANGE) {
i = ((port[2]<<8)&0xff00)+port[3];
- if ((i < csr->DstPortLow) || (i > csr->DstPortHigh))
+ if ((i < csr->dstport_lo) || (i > csr->dstport_hi))
return 1;
}
@@ -208,8 +208,8 @@ static u32 get_qos_index(struct nic *nic, u8 *iph, u8 *tcpudph)
if (IP_Ver == 4) {
for (i = 0; i < QOS_MAX; i++) {
- if (qcb->csr[i].Enabled) {
- if (qcb->csr[i].ClassifierRuleEnable) {
+ if (qcb->csr[i].enabled) {
+ if (qcb->csr[i].classifier_rule_en) {
if (chk_ipv4_rule(&qcb->csr[i], iph,
tcpudph) == 0)
return i;
@@ -230,14 +230,14 @@ static u32 extract_qos_list(struct nic *nic, struct list_head *head)
INIT_LIST_HEAD(head);
for (i = 0; i < QOS_MAX; i++) {
- if (qcb->csr[i].Enabled) {
- if (qcb->csr[i].QoSBufCount < qcb->qos_limit_size) {
+ if (qcb->csr[i].enabled) {
+ if (qcb->csr[i].qos_buf_count < qcb->qos_limit_size) {
if (!list_empty(&qcb->qos_list[i])) {
entry = list_entry(
qcb->qos_list[i].prev,
struct qos_entry_s, list);
list_move_tail(&entry->list, head);
- qcb->csr[i].QoSBufCount++;
+ qcb->csr[i].qos_buf_count++;
if (!list_empty(&qcb->qos_list[i]))
wprintk("QoS Index(%d) is piled!!\n", i);
@@ -322,8 +322,8 @@ static u32 get_csr(struct qos_cb_s *qcb, u32 SFID, int mode)
if (mode) {
for (i = 0; i < QOS_MAX; i++) {
- if (qcb->csr[i].Enabled == 0) {
- qcb->csr[i].Enabled = 1;
+ if (qcb->csr[i].enabled == 0) {
+ qcb->csr[i].enabled = 1;
qcb->qos_list_cnt++;
return i;
}
@@ -365,7 +365,7 @@ void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
eprintk("QoS ERROR: No SF\n");
return;
}
- qcb->csr[index].QoSBufCount = buf[(i*5)+10];
+ qcb->csr[index].qos_buf_count = buf[(i*5)+10];
}
extract_qos_list(nic, &send_list);
@@ -391,38 +391,38 @@ void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
spin_lock_irqsave(&qcb->qos_lock, flags);
qcb->csr[index].SFID = SFID;
- qcb->csr[index].ClassifierRuleEnable = ((buf[pos++]<<8)&0xff00);
- qcb->csr[index].ClassifierRuleEnable += buf[pos++];
- if (qcb->csr[index].ClassifierRuleEnable == 0)
+ qcb->csr[index].classifier_rule_en = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].classifier_rule_en += buf[pos++];
+ if (qcb->csr[index].classifier_rule_en == 0)
qcb->qos_null_idx = index;
- qcb->csr[index].IPToSMask = buf[pos++];
- qcb->csr[index].IPToSLow = buf[pos++];
- qcb->csr[index].IPToSHigh = buf[pos++];
- qcb->csr[index].Protocol = buf[pos++];
- qcb->csr[index].IPSrcAddrMask[0] = buf[pos++];
- qcb->csr[index].IPSrcAddrMask[1] = buf[pos++];
- qcb->csr[index].IPSrcAddrMask[2] = buf[pos++];
- qcb->csr[index].IPSrcAddrMask[3] = buf[pos++];
- qcb->csr[index].IPSrcAddr[0] = buf[pos++];
- qcb->csr[index].IPSrcAddr[1] = buf[pos++];
- qcb->csr[index].IPSrcAddr[2] = buf[pos++];
- qcb->csr[index].IPSrcAddr[3] = buf[pos++];
- qcb->csr[index].IPDstAddrMask[0] = buf[pos++];
- qcb->csr[index].IPDstAddrMask[1] = buf[pos++];
- qcb->csr[index].IPDstAddrMask[2] = buf[pos++];
- qcb->csr[index].IPDstAddrMask[3] = buf[pos++];
- qcb->csr[index].IPDstAddr[0] = buf[pos++];
- qcb->csr[index].IPDstAddr[1] = buf[pos++];
- qcb->csr[index].IPDstAddr[2] = buf[pos++];
- qcb->csr[index].IPDstAddr[3] = buf[pos++];
- qcb->csr[index].SrcPortLow = ((buf[pos++]<<8)&0xff00);
- qcb->csr[index].SrcPortLow += buf[pos++];
- qcb->csr[index].SrcPortHigh = ((buf[pos++]<<8)&0xff00);
- qcb->csr[index].SrcPortHigh += buf[pos++];
- qcb->csr[index].DstPortLow = ((buf[pos++]<<8)&0xff00);
- qcb->csr[index].DstPortLow += buf[pos++];
- qcb->csr[index].DstPortHigh = ((buf[pos++]<<8)&0xff00);
- qcb->csr[index].DstPortHigh += buf[pos++];
+ qcb->csr[index].ip2s_mask = buf[pos++];
+ qcb->csr[index].ip2s_lo = buf[pos++];
+ qcb->csr[index].ip2s_hi = buf[pos++];
+ qcb->csr[index].protocol = buf[pos++];
+ qcb->csr[index].ipsrc_addrmask[0] = buf[pos++];
+ qcb->csr[index].ipsrc_addrmask[1] = buf[pos++];
+ qcb->csr[index].ipsrc_addrmask[2] = buf[pos++];
+ qcb->csr[index].ipsrc_addrmask[3] = buf[pos++];
+ qcb->csr[index].ipsrc_addr[0] = buf[pos++];
+ qcb->csr[index].ipsrc_addr[1] = buf[pos++];
+ qcb->csr[index].ipsrc_addr[2] = buf[pos++];
+ qcb->csr[index].ipsrc_addr[3] = buf[pos++];
+ qcb->csr[index].ipdst_addrmask[0] = buf[pos++];
+ qcb->csr[index].ipdst_addrmask[1] = buf[pos++];
+ qcb->csr[index].ipdst_addrmask[2] = buf[pos++];
+ qcb->csr[index].ipdst_addrmask[3] = buf[pos++];
+ qcb->csr[index].ipdst_addr[0] = buf[pos++];
+ qcb->csr[index].ipdst_addr[1] = buf[pos++];
+ qcb->csr[index].ipdst_addr[2] = buf[pos++];
+ qcb->csr[index].ipdst_addr[3] = buf[pos++];
+ qcb->csr[index].srcport_lo = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].srcport_lo += buf[pos++];
+ qcb->csr[index].srcport_hi = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].srcport_hi += buf[pos++];
+ qcb->csr[index].dstport_lo = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].dstport_lo += buf[pos++];
+ qcb->csr[index].dstport_hi = ((buf[pos++]<<8)&0xff00);
+ qcb->csr[index].dstport_hi += buf[pos++];
qcb->qos_limit_size = 254/qcb->qos_list_cnt;
spin_unlock_irqrestore(&qcb->qos_lock, flags);
@@ -444,7 +444,7 @@ void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
INIT_LIST_HEAD(&free_list);
spin_lock_irqsave(&qcb->qos_lock, flags);
- qcb->csr[index].Enabled = 0;
+ qcb->csr[index].enabled = 0;
qcb->qos_list_cnt--;
qcb->qos_limit_size = 254/qcb->qos_list_cnt;
diff --git a/drivers/staging/gdm72xx/gdm_qos.h b/drivers/staging/gdm72xx/gdm_qos.h
index 33f2bd4cee32..8f18119d22a9 100644
--- a/drivers/staging/gdm72xx/gdm_qos.h
+++ b/drivers/staging/gdm72xx/gdm_qos.h
@@ -20,18 +20,18 @@
#define BOOLEAN u8
-#define QOS_MAX 16
-#define IPTYPEOFSERVICE 0x8000
-#define PROTOCOL 0x4000
-#define IPMASKEDSRCADDRESS 0x2000
-#define IPMASKEDDSTADDRESS 0x1000
+#define QOS_MAX 16
+#define IPTYPEOFSERVICE 0x8000
+#define PROTOCOL 0x4000
+#define IPMASKEDSRCADDRESS 0x2000
+#define IPMASKEDDSTADDRESS 0x1000
#define PROTOCOLSRCPORTRANGE 0x800
#define PROTOCOLDSTPORTRANGE 0x400
-#define DSTMACADDR 0x200
-#define SRCMACADDR 0x100
-#define ETHERTYPE 0x80
+#define DSTMACADDR 0x200
+#define SRCMACADDR 0x100
+#define ETHERTYPE 0x80
#define IEEE802_1DUSERPRIORITY 0x40
-#define IEEE802_1QVLANID 0x10
+#define IEEE802_1QVLANID 0x10
struct gdm_wimax_csr_s {
/* union{
@@ -51,28 +51,28 @@ struct gdm_wimax_csr_s {
Reserved:5;
} fields;
} */
- BOOLEAN Enabled;
- u32 SFID;
- u8 QoSBufCount;
- u16 ClassifierRuleEnable;
- u8 IPToSLow;
- u8 IPToSHigh;
- u8 IPToSMask;
- u8 Protocol;
- u8 IPSrcAddr[16];
- u8 IPSrcAddrMask[16];
- u8 IPDstAddr[16];
- u8 IPDstAddrMask[16];
- u16 SrcPortLow;
- u16 SrcPortHigh;
- u16 DstPortLow;
- u16 DstPortHigh;
+ BOOLEAN enabled;
+ u32 SFID;
+ u8 qos_buf_count;
+ u16 classifier_rule_en;
+ u8 ip2s_lo;
+ u8 ip2s_hi;
+ u8 ip2s_mask;
+ u8 protocol;
+ u8 ipsrc_addr[16];
+ u8 ipsrc_addrmask[16];
+ u8 ipdst_addr[16];
+ u8 ipdst_addrmask[16];
+ u16 srcport_lo;
+ u16 srcport_hi;
+ u16 dstport_lo;
+ u16 dstport_hi;
};
struct qos_entry_s {
- struct list_head list;
- struct sk_buff *skb;
- struct net_device *dev;
+ struct list_head list;
+ struct sk_buff *skb;
+ struct net_device *dev;
};
@@ -81,7 +81,7 @@ struct qos_cb_s {
u32 qos_list_cnt;
u32 qos_null_idx;
struct gdm_wimax_csr_s csr[QOS_MAX];
- spinlock_t qos_lock;
+ spinlock_t qos_lock;
u32 qos_limit_size;
};
diff --git a/drivers/staging/gdm72xx/gdm_sdio.c b/drivers/staging/gdm72xx/gdm_sdio.c
index 3e43c012ef27..ca38d719a1f8 100644
--- a/drivers/staging/gdm72xx/gdm_sdio.c
+++ b/drivers/staging/gdm72xx/gdm_sdio.c
@@ -60,27 +60,20 @@ static void hexdump(char *title, u8 *data, int len)
static struct sdio_tx *alloc_tx_struct(struct tx_cxt *tx)
{
- struct sdio_tx *t = NULL;
-
- t = kmalloc(sizeof(*t), GFP_ATOMIC);
- if (t == NULL)
- goto out;
+ struct sdio_tx *t = kzalloc(sizeof(*t), GFP_ATOMIC);
- memset(t, 0, sizeof(*t));
+ if (!t)
+ return NULL;
t->buf = kmalloc(TX_BUF_SIZE, GFP_ATOMIC);
- if (t->buf == NULL)
- goto out;
+ if (!t->buf) {
+ kfree(t);
+ return NULL;
+ }
t->tx_cxt = tx;
return t;
-out:
- if (t) {
- kfree(t->buf);
- kfree(t);
- }
- return NULL;
}
static void free_tx_struct(struct sdio_tx *t)
@@ -93,20 +86,12 @@ static void free_tx_struct(struct sdio_tx *t)
static struct sdio_rx *alloc_rx_struct(struct rx_cxt *rx)
{
- struct sdio_rx *r = NULL;
+ struct sdio_rx *r = kzalloc(sizeof(*r), GFP_ATOMIC);
- r = kmalloc(sizeof(*r), GFP_ATOMIC);
- if (r == NULL)
- goto out;
-
- memset(r, 0, sizeof(*r));
-
- r->rx_cxt = rx;
+ if (r)
+ r->rx_cxt = rx;
return r;
-out:
- kfree(r);
- return NULL;
}
static void free_rx_struct(struct sdio_rx *r)
@@ -680,7 +665,7 @@ static int sdio_wimax_probe(struct sdio_func *func,
phy_dev->rcv_func = gdm_sdio_receive;
ret = init_sdio(sdev);
- if (sdev < 0)
+ if (ret < 0)
goto out;
sdev->func = func;
diff --git a/drivers/staging/gdm72xx/gdm_usb.c b/drivers/staging/gdm72xx/gdm_usb.c
index d48d49c145ee..0c9e8958009b 100644
--- a/drivers/staging/gdm72xx/gdm_usb.c
+++ b/drivers/staging/gdm72xx/gdm_usb.c
@@ -26,11 +26,11 @@
MODULE_DEVICE_TABLE(usb, id_table);
-#define TX_BUF_SIZE 2048
+#define TX_BUF_SIZE 2048
#if defined(CONFIG_WIMAX_GDM72XX_WIMAX2)
-#define RX_BUF_SIZE (128*1024) /* For packet aggregation */
+#define RX_BUF_SIZE (128*1024) /* For packet aggregation */
#else
-#define RX_BUF_SIZE 2048
+#define RX_BUF_SIZE 2048
#endif
#define GDM7205_PADDING 256
@@ -39,7 +39,7 @@ MODULE_DEVICE_TABLE(usb, id_table);
#define B2H(x) __be16_to_cpu(x)
#define DB2H(x) __be32_to_cpu(x)
-#define DOWNLOAD_CONF_VALUE 0x21
+#define DOWNLOAD_CONF_VALUE 0x21
#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
@@ -48,7 +48,7 @@ static LIST_HEAD(k_list);
static DEFINE_SPINLOCK(k_lock);
static int k_mode_stop;
-#define K_WAIT_TIME (2 * HZ / 100)
+#define K_WAIT_TIME (2 * HZ / 100)
#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
@@ -73,29 +73,23 @@ static void hexdump(char *title, u8 *data, int len)
static struct usb_tx *alloc_tx_struct(struct tx_cxt *tx)
{
- struct usb_tx *t = NULL;
+ struct usb_tx *t = kzalloc(sizeof(*t), GFP_ATOMIC);
- t = kmalloc(sizeof(*t), GFP_ATOMIC);
- if (t == NULL)
- goto out;
-
- memset(t, 0, sizeof(*t));
+ if (!t)
+ return NULL;
t->urb = usb_alloc_urb(0, GFP_ATOMIC);
t->buf = kmalloc(TX_BUF_SIZE, GFP_ATOMIC);
- if (t->urb == NULL || t->buf == NULL)
- goto out;
-
- t->tx_cxt = tx;
-
- return t;
-out:
- if (t) {
+ if (!t->urb || !t->buf) {
usb_free_urb(t->urb);
kfree(t->buf);
kfree(t);
+ return NULL;
}
- return NULL;
+
+ t->tx_cxt = tx;
+
+ return t;
}
static void free_tx_struct(struct usb_tx *t)
@@ -109,28 +103,22 @@ static void free_tx_struct(struct usb_tx *t)
static struct usb_rx *alloc_rx_struct(struct rx_cxt *rx)
{
- struct usb_rx *r = NULL;
+ struct usb_rx *r = kzalloc(sizeof(*r), GFP_ATOMIC);
- r = kmalloc(sizeof(*r), GFP_ATOMIC);
- if (r == NULL)
- goto out;
-
- memset(r, 0, sizeof(*r));
+ if (!r)
+ return NULL;
r->urb = usb_alloc_urb(0, GFP_ATOMIC);
r->buf = kmalloc(RX_BUF_SIZE, GFP_ATOMIC);
- if (r->urb == NULL || r->buf == NULL)
- goto out;
-
- r->rx_cxt = rx;
- return r;
-out:
- if (r) {
+ if (!r->urb || !r->buf) {
usb_free_urb(r->urb);
kfree(r->buf);
kfree(r);
+ return NULL;
}
- return NULL;
+
+ r->rx_cxt = rx;
+ return r;
}
static void free_rx_struct(struct usb_rx *r)
@@ -180,8 +168,7 @@ static struct usb_rx *get_rx_struct(struct rx_cxt *rx)
}
r = list_entry(rx->free_list.next, struct usb_rx, list);
- list_del(&r->list);
- list_add_tail(&r->list, &rx->used_list);
+ list_move_tail(&r->list, &rx->used_list);
return r;
}
@@ -189,8 +176,7 @@ static struct usb_rx *get_rx_struct(struct rx_cxt *rx)
/* Before this function is called, spin lock should be locked. */
static void put_rx_struct(struct rx_cxt *rx, struct usb_rx *r)
{
- list_del(&r->list);
- list_add(&r->list, &rx->free_list);
+ list_move(&r->list, &rx->free_list);
}
static int init_usb(struct usbwm_dev *udev)
diff --git a/drivers/staging/gdm72xx/gdm_usb.h b/drivers/staging/gdm72xx/gdm_usb.h
index ecb891f6a599..f2c54511bb96 100644
--- a/drivers/staging/gdm72xx/gdm_usb.h
+++ b/drivers/staging/gdm72xx/gdm_usb.h
@@ -18,8 +18,8 @@
#include <linux/usb.h>
#include <linux/list.h>
-#define B_DIFF_DL_DRV (1<<4)
-#define B_DOWNLOAD (1 << 5)
+#define B_DIFF_DL_DRV (1 << 4)
+#define B_DOWNLOAD (1 << 5)
#define MAX_NR_SDU_BUF 64
struct usb_tx {
@@ -29,7 +29,7 @@ struct usb_tx {
#endif
struct tx_cxt *tx_cxt;
- struct urb *urb;
+ struct urb *urb;
u8 *buf;
void (*callback)(void *cb_data);
@@ -44,14 +44,14 @@ struct tx_cxt {
struct list_head pending_list;
#endif
- spinlock_t lock;
+ spinlock_t lock;
};
struct usb_rx {
struct list_head list;
struct rx_cxt *rx_cxt;
- struct urb *urb;
+ struct urb *urb;
u8 *buf;
void (*callback)(void *cb_data, void *data, int len);
@@ -61,7 +61,7 @@ struct usb_rx {
struct rx_cxt {
struct list_head free_list;
struct list_head used_list;
- spinlock_t lock;
+ spinlock_t lock;
};
struct usbwm_dev {
@@ -76,8 +76,8 @@ struct usbwm_dev {
struct list_head list;
#endif
- struct tx_cxt tx;
- struct rx_cxt rx;
+ struct tx_cxt tx;
+ struct rx_cxt rx;
int padding;
};
diff --git a/drivers/staging/gdm72xx/gdm_wimax.c b/drivers/staging/gdm72xx/gdm_wimax.c
index 0716efc1817d..6cb810701a3e 100644
--- a/drivers/staging/gdm72xx/gdm_wimax.c
+++ b/drivers/staging/gdm72xx/gdm_wimax.c
@@ -258,12 +258,16 @@ static int gdm_wimax_event_init(void)
if (!wm_event.ref_cnt) {
wm_event.sock = netlink_init(NETLINK_WIMAX,
gdm_wimax_event_rcv);
- if (wm_event.sock)
- wm_event.ref_cnt++;
- INIT_LIST_HEAD(&wm_event.evtq);
- INIT_LIST_HEAD(&wm_event.freeq);
- INIT_WORK(&wm_event.ws, __gdm_wimax_event_send);
- spin_lock_init(&wm_event.evt_lock);
+ if (wm_event.sock) {
+ INIT_LIST_HEAD(&wm_event.evtq);
+ INIT_LIST_HEAD(&wm_event.freeq);
+ INIT_WORK(&wm_event.ws, __gdm_wimax_event_send);
+ spin_lock_init(&wm_event.evt_lock);
+ }
+ }
+
+ if (wm_event.sock) {
+ wm_event.ref_cnt++;
return 0;
}
diff --git a/drivers/staging/gdm72xx/usb_boot.c b/drivers/staging/gdm72xx/usb_boot.c
index e3dbd5a552ca..0787188728aa 100644
--- a/drivers/staging/gdm72xx/usb_boot.c
+++ b/drivers/staging/gdm72xx/usb_boot.c
@@ -18,25 +18,22 @@
#include <linux/usb.h>
#include <linux/unistd.h>
#include <linux/slab.h>
+#include <linux/firmware.h>
#include <asm/byteorder.h>
#include "gdm_usb.h"
#include "usb_boot.h"
-#define DN_KERNEL_MAGIC_NUMBER 0x10760001
-#define DN_ROOTFS_MAGIC_NUMBER 0x10760002
+#define DN_KERNEL_MAGIC_NUMBER 0x10760001
+#define DN_ROOTFS_MAGIC_NUMBER 0x10760002
-#define DOWNLOAD_SIZE 1024
-
-#define DH2B(x) __cpu_to_be32(x)
-#define DL2H(x) __le32_to_cpu(x)
-
-#define MIN(a, b) ((a) > (b) ? (b) : (a))
+#define DOWNLOAD_SIZE 1024
#define MAX_IMG_CNT 16
-#define UIMG_PATH "/lib/firmware/gdm72xx/gdmuimg.bin"
-#define KERN_PATH "/lib/firmware/gdm72xx/zImage"
-#define FS_PATH "/lib/firmware/gdm72xx/ramdisk.jffs2"
+#define FW_DIR "gdm72xx/"
+#define FW_UIMG "gdmuimg.bin"
+#define FW_KERN "zImage"
+#define FW_FS "ramdisk.jffs2"
struct dn_header {
u32 magic_num;
@@ -44,23 +41,23 @@ struct dn_header {
};
struct img_header {
- u32 magic_code;
- u32 count;
- u32 len;
- u32 offset[MAX_IMG_CNT];
+ u32 magic_code;
+ u32 count;
+ u32 len;
+ u32 offset[MAX_IMG_CNT];
char hostname[32];
char date[32];
};
struct fw_info {
- u32 id;
- u32 len;
- u32 kernel_len;
- u32 rootfs_len;
- u32 kernel_offset;
- u32 rootfs_offset;
- u32 fw_ver;
- u32 mac_ver;
+ u32 id;
+ u32 len;
+ u32 kernel_len;
+ u32 rootfs_len;
+ u32 kernel_offset;
+ u32 rootfs_offset;
+ u32 fw_ver;
+ u32 mac_ver;
char hostname[32];
char userid[16];
char date[32];
@@ -71,7 +68,7 @@ static void array_le32_to_cpu(u32 *arr, int num)
{
int i;
for (i = 0; i < num; i++, arr++)
- *arr = DL2H(*arr);
+ *arr = __le32_to_cpu(*arr);
}
static u8 *tx_buf;
@@ -107,44 +104,37 @@ static int gdm_wibro_recv(struct usb_device *usbdev, void *data, int len)
return 0;
}
-static int download_image(struct usb_device *usbdev, struct file *filp,
- loff_t *pos, u32 img_len, u32 magic_num)
+static int download_image(struct usb_device *usbdev,
+ const struct firmware *firm,
+ loff_t pos, u32 img_len, u32 magic_num)
{
struct dn_header h;
int ret = 0;
u32 size;
- int len, readn;
- size = (img_len + DOWNLOAD_SIZE - 1) & ~(DOWNLOAD_SIZE - 1);
- h.magic_num = DH2B(magic_num);
- h.file_size = DH2B(size);
+ size = ALIGN(img_len, DOWNLOAD_SIZE);
+ h.magic_num = __cpu_to_be32(magic_num);
+ h.file_size = __cpu_to_be32(size);
ret = gdm_wibro_send(usbdev, &h, sizeof(h));
if (ret < 0)
- goto out;
+ return ret;
- readn = 0;
- while ((len = filp->f_op->read(filp, tx_buf, DOWNLOAD_SIZE, pos))) {
+ while (img_len > 0) {
+ if (img_len > DOWNLOAD_SIZE)
+ size = DOWNLOAD_SIZE;
+ else
+ size = img_len; /* the last chunk of data */
- if (len < 0) {
- ret = -1;
- goto out;
- }
- readn += len;
+ memcpy(tx_buf, firm->data + pos, size);
+ ret = gdm_wibro_send(usbdev, tx_buf, size);
- ret = gdm_wibro_send(usbdev, tx_buf, DOWNLOAD_SIZE);
if (ret < 0)
- goto out;
- if (readn >= img_len)
- break;
- }
+ return ret;
- if (readn < img_len) {
- printk(KERN_ERR "gdmwm: Cannot read to the requested size. "
- "Read = %d Requested = %d\n", readn, img_len);
- ret = -EIO;
+ img_len -= size;
+ pos += size;
}
-out:
return ret;
}
@@ -152,14 +142,19 @@ out:
int usb_boot(struct usb_device *usbdev, u16 pid)
{
int i, ret = 0;
- struct file *filp = NULL;
- struct inode *inode = NULL;
- static mm_segment_t fs;
struct img_header hdr;
struct fw_info fw_info;
loff_t pos = 0;
- char *img_name = UIMG_PATH;
- int len;
+ char *img_name = FW_DIR FW_UIMG;
+ const struct firmware *firm;
+
+ ret = request_firmware(&firm, img_name, &usbdev->dev);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "requesting firmware %s failed with error %d\n",
+ img_name, ret);
+ return ret;
+ }
tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL);
if (tx_buf == NULL) {
@@ -167,29 +162,12 @@ int usb_boot(struct usb_device *usbdev, u16 pid)
return -ENOMEM;
}
- fs = get_fs();
- set_fs(get_ds());
-
- filp = filp_open(img_name, O_RDONLY | O_LARGEFILE, 0);
- if (IS_ERR(filp)) {
- printk(KERN_ERR "Can't find %s.\n", img_name);
- ret = PTR_ERR(filp);
- goto restore_fs;
- }
-
- inode = filp->f_dentry->d_inode;
- if (!S_ISREG(inode->i_mode)) {
- printk(KERN_ERR "Invalid file type: %s\n", img_name);
- ret = -EINVAL;
- goto out;
- }
-
- len = filp->f_op->read(filp, (u8 *)&hdr, sizeof(hdr), &pos);
- if (len != sizeof(hdr)) {
+ if (firm->size < sizeof(hdr)) {
printk(KERN_ERR "gdmwm: Cannot read the image info.\n");
ret = -EIO;
goto out;
}
+ memcpy(&hdr, firm->data, sizeof(hdr));
array_le32_to_cpu((u32 *)&hdr, 19);
#if 0
@@ -217,13 +195,12 @@ int usb_boot(struct usb_device *usbdev, u16 pid)
}
pos = hdr.offset[i];
- len = filp->f_op->read(filp, (u8 *)&fw_info, sizeof(fw_info),
- &pos);
- if (len != sizeof(fw_info)) {
+ if (firm->size < sizeof(fw_info) + pos) {
printk(KERN_ERR "gdmwm: Cannot read the FW info.\n");
ret = -EIO;
goto out;
}
+ memcpy(&fw_info, firm->data + pos, sizeof(fw_info));
array_le32_to_cpu((u32 *)&fw_info, 8);
#if 0
@@ -239,14 +216,23 @@ int usb_boot(struct usb_device *usbdev, u16 pid)
continue;
pos = hdr.offset[i] + fw_info.kernel_offset;
- ret = download_image(usbdev, filp, &pos, fw_info.kernel_len,
- DN_KERNEL_MAGIC_NUMBER);
+ if (firm->size < fw_info.kernel_len + pos) {
+ printk(KERN_ERR "gdmwm: Kernel FW is too small.\n");
+ goto out;
+ }
+
+ ret = download_image(usbdev, firm, pos,
+ fw_info.kernel_len, DN_KERNEL_MAGIC_NUMBER);
if (ret < 0)
goto out;
printk(KERN_INFO "GCT: Kernel download success.\n");
pos = hdr.offset[i] + fw_info.rootfs_offset;
- ret = download_image(usbdev, filp, &pos, fw_info.rootfs_len,
+ if (firm->size < fw_info.rootfs_len + pos) {
+ printk(KERN_ERR "gdmwm: Filesystem FW is too small.\n");
+ goto out;
+ }
+ ret = download_image(usbdev, firm, pos, fw_info.rootfs_len,
DN_ROOTFS_MAGIC_NUMBER);
if (ret < 0)
goto out;
@@ -260,10 +246,7 @@ int usb_boot(struct usb_device *usbdev, u16 pid)
ret = -EINVAL;
}
out:
- filp_close(filp, NULL);
-
-restore_fs:
- set_fs(fs);
+ release_firmware(firm);
kfree(tx_buf);
return ret;
}
@@ -293,38 +276,27 @@ out:
return ret;
}
-static int em_download_image(struct usb_device *usbdev, char *path,
+static int em_download_image(struct usb_device *usbdev, const char *img_name,
char *type_string)
{
- struct file *filp;
- struct inode *inode;
- static mm_segment_t fs;
char *buf = NULL;
loff_t pos = 0;
int ret = 0;
- int len, readn = 0;
+ int len;
+ int img_len;
+ const struct firmware *firm;
#if defined(GDM7205_PADDING)
const int pad_size = GDM7205_PADDING;
#else
const int pad_size = 0;
#endif
- fs = get_fs();
- set_fs(get_ds());
-
- filp = filp_open(path, O_RDONLY | O_LARGEFILE, 0);
- if (IS_ERR(filp)) {
- printk(KERN_ERR "Can't find %s.\n", path);
- set_fs(fs);
- ret = -ENOENT;
- goto restore_fs;
- }
-
- inode = filp->f_dentry->d_inode;
- if (!S_ISREG(inode->i_mode)) {
- printk(KERN_ERR "Invalid file type: %s\n", path);
- ret = -EINVAL;
- goto out;
+ ret = request_firmware(&firm, img_name, &usbdev->dev);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "requesting firmware %s failed with error %d\n",
+ img_name, ret);
+ return ret;
}
buf = kmalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
@@ -338,18 +310,28 @@ static int em_download_image(struct usb_device *usbdev, char *path,
if (ret < 0)
goto out;
- while ((len = filp->f_op->read(filp, buf+pad_size, DOWNLOAD_CHUCK,
- &pos))) {
- if (len < 0) {
- ret = -1;
- goto out;
- }
- readn += len;
+ img_len = firm->size;
+
+ if (img_len <= 0) {
+ ret = -1;
+ goto out;
+ }
+ while (img_len > 0) {
+ if (img_len > DOWNLOAD_CHUCK)
+ len = DOWNLOAD_CHUCK;
+ else
+ len = img_len; /* the last chunk of data */
+
+ memcpy(buf+pad_size, firm->data + pos, len);
ret = gdm_wibro_send(usbdev, buf, len+pad_size);
+
if (ret < 0)
goto out;
+ img_len -= DOWNLOAD_CHUCK;
+ pos += DOWNLOAD_CHUCK;
+
ret = em_wait_ack(usbdev, ((len+pad_size) % 512 == 0));
if (ret < 0)
goto out;
@@ -360,11 +342,7 @@ static int em_download_image(struct usb_device *usbdev, char *path,
goto out;
out:
- filp_close(filp, NULL);
-
-restore_fs:
- set_fs(fs);
-
+ release_firmware(firm);
kfree(buf);
return ret;
@@ -382,18 +360,20 @@ static int em_fw_reset(struct usb_device *usbdev)
int usb_emergency(struct usb_device *usbdev)
{
int ret;
+ const char *kern_name = FW_DIR FW_KERN;
+ const char *fs_name = FW_DIR FW_FS;
- ret = em_download_image(usbdev, KERN_PATH, KERNEL_TYPE_STRING);
+ ret = em_download_image(usbdev, kern_name, KERNEL_TYPE_STRING);
if (ret < 0)
- goto out;
+ return ret;
printk(KERN_INFO "GCT Emergency: Kernel download success.\n");
- ret = em_download_image(usbdev, FS_PATH, FS_TYPE_STRING);
+ ret = em_download_image(usbdev, fs_name, FS_TYPE_STRING);
if (ret < 0)
- goto out;
+ return ret;
printk(KERN_INFO "GCT Emergency: Filesystem download success.\n");
ret = em_fw_reset(usbdev);
-out:
+
return ret;
}
diff --git a/drivers/staging/iio/Documentation/generic_buffer.c b/drivers/staging/iio/Documentation/generic_buffer.c
index 827e92de8e30..40d0ecac047f 100644
--- a/drivers/staging/iio/Documentation/generic_buffer.c
+++ b/drivers/staging/iio/Documentation/generic_buffer.c
@@ -104,6 +104,16 @@ void process_scan(char *data,
print2byte(*(uint16_t *)(data + channels[k].location),
&channels[k]);
break;
+ case 4:
+ if (!channels[k].is_signed) {
+ uint32_t val = *(uint32_t *)
+ (data + channels[k].location);
+ printf("%05f ", ((float)val +
+ channels[k].offset)*
+ channels[k].scale);
+
+ }
+ break;
case 8:
if (channels[k].is_signed) {
int64_t val = *(int64_t *)
diff --git a/drivers/staging/iio/Documentation/inkernel.txt b/drivers/staging/iio/Documentation/inkernel.txt
index a05823e955d2..ab528409bba6 100644
--- a/drivers/staging/iio/Documentation/inkernel.txt
+++ b/drivers/staging/iio/Documentation/inkernel.txt
@@ -48,11 +48,11 @@ There are then a number of functions that can be used to get information
about this channel such as it's current reading.
e.g.
-iio_st_read_channel_raw() - get a reading
-iio_st_read_channel_type() - get the type of channel
+iio_read_channel_raw() - get a reading
+iio_get_channel_type() - get the type of channel
There is also provision for retrieving all of the channels associated
with a given consumer. This is useful for generic drivers such as
iio_hwmon where the number and naming of channels is not known by the
-consumer driver. To do this, use iio_st_channel_get_all.
+consumer driver. To do this, use iio_channel_get_all.
diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig
index 04cd6ec1f70f..ca56c75a35fc 100644
--- a/drivers/staging/iio/Kconfig
+++ b/drivers/staging/iio/Kconfig
@@ -1,5 +1,5 @@
#
-# Industrial I/O subsytem configuration
+# Industrial I/O subsystem configuration
#
menu "IIO staging drivers"
depends on IIO
diff --git a/drivers/staging/iio/TODO b/drivers/staging/iio/TODO
index cf3f9489b9da..04c23262f8e2 100644
--- a/drivers/staging/iio/TODO
+++ b/drivers/staging/iio/TODO
@@ -69,5 +69,5 @@ Documentation
1) Lots of cleanup and expansion.
2) Some device require individual docs.
-Contact: Jonathan Cameron <jic23@cam.ac.uk>.
+Contact: Jonathan Cameron <jic23@kernel.org>.
Mailing list: linux-iio@vger.kernel.org
diff --git a/drivers/staging/iio/accel/adis16201_core.c b/drivers/staging/iio/accel/adis16201_core.c
index 204106b72d24..8e37d6e04277 100644
--- a/drivers/staging/iio/accel/adis16201_core.c
+++ b/drivers/staging/iio/accel/adis16201_core.c
@@ -390,7 +390,7 @@ static int adis16201_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-static struct iio_chan_spec adis16201_channels[] = {
+static const struct iio_chan_spec adis16201_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
@@ -565,7 +565,7 @@ error_ret:
return ret;
}
-static int adis16201_remove(struct spi_device *spi)
+static int __devexit adis16201_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
diff --git a/drivers/staging/iio/accel/adis16201_ring.c b/drivers/staging/iio/accel/adis16201_ring.c
index 03fcf6e319db..97c09f0c26ae 100644
--- a/drivers/staging/iio/accel/adis16201_ring.c
+++ b/drivers/staging/iio/accel/adis16201_ring.c
@@ -62,7 +62,6 @@ static irqreturn_t adis16201_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adis16201_state *st = iio_priv(indio_dev);
- struct iio_buffer *ring = indio_dev->buffer;
int i = 0;
s16 *data;
@@ -83,7 +82,7 @@ static irqreturn_t adis16201_trigger_handler(int irq, void *p)
if (indio_dev->scan_timestamp)
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
- ring->access->store_to(ring, (u8 *)data, pf->timestamp);
+ iio_push_to_buffer(indio_dev->buffer, (u8 *)data);
kfree(data);
done:
diff --git a/drivers/staging/iio/accel/adis16203_core.c b/drivers/staging/iio/accel/adis16203_core.c
index 22085e9dfd16..002fa9dfc375 100644
--- a/drivers/staging/iio/accel/adis16203_core.c
+++ b/drivers/staging/iio/accel/adis16203_core.c
@@ -355,7 +355,7 @@ static int adis16203_read_raw(struct iio_dev *indio_dev,
}
}
-static struct iio_chan_spec adis16203_channels[] = {
+static const struct iio_chan_spec adis16203_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
@@ -500,7 +500,7 @@ error_ret:
return ret;
}
-static int adis16203_remove(struct spi_device *spi)
+static int __devexit adis16203_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
diff --git a/drivers/staging/iio/accel/adis16203_ring.c b/drivers/staging/iio/accel/adis16203_ring.c
index c16b2b7323ac..7507e1a04591 100644
--- a/drivers/staging/iio/accel/adis16203_ring.c
+++ b/drivers/staging/iio/accel/adis16203_ring.c
@@ -61,7 +61,6 @@ static irqreturn_t adis16203_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adis16203_state *st = iio_priv(indio_dev);
- struct iio_buffer *ring = indio_dev->buffer;
int i = 0;
s16 *data;
@@ -82,9 +81,7 @@ static irqreturn_t adis16203_trigger_handler(int irq, void *p)
if (indio_dev->scan_timestamp)
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
- ring->access->store_to(ring,
- (u8 *)data,
- pf->timestamp);
+ iio_push_to_buffer(indio_dev->buffer, (u8 *)data);
kfree(data);
done:
diff --git a/drivers/staging/iio/accel/adis16204_core.c b/drivers/staging/iio/accel/adis16204_core.c
index 5f2e5f11c543..05bdb7c2c8e3 100644
--- a/drivers/staging/iio/accel/adis16204_core.c
+++ b/drivers/staging/iio/accel/adis16204_core.c
@@ -397,7 +397,7 @@ static int adis16204_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-static struct iio_chan_spec adis16204_channels[] = {
+static const struct iio_chan_spec adis16204_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1, /* Note was not previously indexed */
@@ -558,7 +558,7 @@ error_ret:
return ret;
}
-static int adis16204_remove(struct spi_device *spi)
+static int __devexit adis16204_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
diff --git a/drivers/staging/iio/accel/adis16204_ring.c b/drivers/staging/iio/accel/adis16204_ring.c
index 1d2b31cc849e..4c976bec986b 100644
--- a/drivers/staging/iio/accel/adis16204_ring.c
+++ b/drivers/staging/iio/accel/adis16204_ring.c
@@ -59,7 +59,6 @@ static irqreturn_t adis16204_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adis16204_state *st = iio_priv(indio_dev);
- struct iio_buffer *ring = indio_dev->buffer;
int i = 0;
s16 *data;
@@ -79,7 +78,7 @@ static irqreturn_t adis16204_trigger_handler(int irq, void *p)
if (indio_dev->scan_timestamp)
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
- ring->access->store_to(ring, (u8 *)data, pf->timestamp);
+ iio_push_to_buffer(indio_dev->buffer, (u8 *)data);
kfree(data);
done:
diff --git a/drivers/staging/iio/accel/adis16209_core.c b/drivers/staging/iio/accel/adis16209_core.c
index 494570508c36..b7333bfe0b2f 100644
--- a/drivers/staging/iio/accel/adis16209_core.c
+++ b/drivers/staging/iio/accel/adis16209_core.c
@@ -390,7 +390,7 @@ static int adis16209_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-static struct iio_chan_spec adis16209_channels[] = {
+static const struct iio_chan_spec adis16209_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
@@ -573,7 +573,7 @@ error_ret:
return ret;
}
-static int adis16209_remove(struct spi_device *spi)
+static int __devexit adis16209_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
diff --git a/drivers/staging/iio/accel/adis16209_ring.c b/drivers/staging/iio/accel/adis16209_ring.c
index 1a4a55c27c7c..f939e29d6c82 100644
--- a/drivers/staging/iio/accel/adis16209_ring.c
+++ b/drivers/staging/iio/accel/adis16209_ring.c
@@ -59,7 +59,6 @@ static irqreturn_t adis16209_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adis16209_state *st = iio_priv(indio_dev);
- struct iio_buffer *ring = indio_dev->buffer;
int i = 0;
s16 *data;
@@ -79,7 +78,7 @@ static irqreturn_t adis16209_trigger_handler(int irq, void *p)
if (indio_dev->scan_timestamp)
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
- ring->access->store_to(ring, (u8 *)data, pf->timestamp);
+ iio_push_to_buffer(indio_dev->buffer, (u8 *)data);
kfree(data);
done:
diff --git a/drivers/staging/iio/accel/adis16220_core.c b/drivers/staging/iio/accel/adis16220_core.c
index 575f1af25d5d..c755089c7117 100644
--- a/drivers/staging/iio/accel/adis16220_core.c
+++ b/drivers/staging/iio/accel/adis16220_core.c
@@ -372,8 +372,7 @@ static ssize_t adis16220_accel_bin_read(struct file *filp, struct kobject *kobj,
loff_t off,
size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj));
return adis16220_capture_buffer_read(indio_dev, buf,
off, count,
@@ -394,8 +393,7 @@ static ssize_t adis16220_adc1_bin_read(struct file *filp, struct kobject *kobj,
char *buf, loff_t off,
size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj));
return adis16220_capture_buffer_read(indio_dev, buf,
off, count,
@@ -416,8 +414,7 @@ static ssize_t adis16220_adc2_bin_read(struct file *filp, struct kobject *kobj,
char *buf, loff_t off,
size_t count)
{
- struct device *dev = container_of(kobj, struct device, kobj);
- struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct iio_dev *indio_dev = dev_to_iio_dev(kobj_to_dev(kobj));
return adis16220_capture_buffer_read(indio_dev, buf,
off, count,
@@ -666,7 +663,7 @@ error_ret:
return ret;
}
-static int adis16220_remove(struct spi_device *spi)
+static int __devexit adis16220_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
diff --git a/drivers/staging/iio/accel/adis16240_core.c b/drivers/staging/iio/accel/adis16240_core.c
index b30b7874ffb0..0fc26a49d681 100644
--- a/drivers/staging/iio/accel/adis16240_core.c
+++ b/drivers/staging/iio/accel/adis16240_core.c
@@ -448,7 +448,7 @@ static int adis16240_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-static struct iio_chan_spec adis16240_channels[] = {
+static const struct iio_chan_spec adis16240_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
@@ -619,7 +619,7 @@ error_ret:
return ret;
}
-static int adis16240_remove(struct spi_device *spi)
+static int __devexit adis16240_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
diff --git a/drivers/staging/iio/accel/adis16240_ring.c b/drivers/staging/iio/accel/adis16240_ring.c
index 360dfed6d4d1..caff8e25e0a2 100644
--- a/drivers/staging/iio/accel/adis16240_ring.c
+++ b/drivers/staging/iio/accel/adis16240_ring.c
@@ -56,7 +56,6 @@ static irqreturn_t adis16240_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adis16240_state *st = iio_priv(indio_dev);
- struct iio_buffer *ring = indio_dev->buffer;
int i = 0;
s16 *data;
@@ -77,7 +76,7 @@ static irqreturn_t adis16240_trigger_handler(int irq, void *p)
if (indio_dev->scan_timestamp)
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
- ring->access->store_to(ring, (u8 *)data, pf->timestamp);
+ iio_push_to_buffer(indio_dev->buffer, (u8 *)data);
kfree(data);
done:
diff --git a/drivers/staging/iio/accel/kxsd9.c b/drivers/staging/iio/accel/kxsd9.c
index 8cf7cd943c90..fdd5fbded660 100644
--- a/drivers/staging/iio/accel/kxsd9.c
+++ b/drivers/staging/iio/accel/kxsd9.c
@@ -2,7 +2,7 @@
* kxsd9.c simple support for the Kionix KXSD9 3D
* accelerometer.
*
- * Copyright (c) 2008-2009 Jonathan Cameron <jic23@cam.ac.uk>
+ * Copyright (c) 2008-2009 Jonathan Cameron <jic23@kernel.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
@@ -186,7 +186,7 @@ error_ret:
.address = KXSD9_REG_##axis, \
}
-static struct iio_chan_spec kxsd9_channels[] = {
+static const struct iio_chan_spec kxsd9_channels[] = {
KXSD9_ACCEL_CHAN(X), KXSD9_ACCEL_CHAN(Y), KXSD9_ACCEL_CHAN(Z),
{
.type = IIO_VOLTAGE,
@@ -286,6 +286,6 @@ static struct spi_driver kxsd9_driver = {
};
module_spi_driver(kxsd9_driver);
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/lis3l02dq.h b/drivers/staging/iio/accel/lis3l02dq.h
index ae5f225b4bb2..f9bcd41f7188 100644
--- a/drivers/staging/iio/accel/lis3l02dq.h
+++ b/drivers/staging/iio/accel/lis3l02dq.h
@@ -2,7 +2,7 @@
* LISL02DQ.h -- support STMicroelectronics LISD02DQ
* 3d 2g Linear Accelerometers via SPI
*
- * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk>
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
*
* Loosely based upon tle62x0.c
*
@@ -28,7 +28,7 @@
/* Control Register (1 of 2) */
#define LIS3L02DQ_REG_CTRL_1_ADDR 0x20
/* Power ctrl - either bit set corresponds to on*/
-#define LIS3L02DQ_REG_CTRL_1_PD_ON 0xC0
+#define LIS3L02DQ_REG_CTRL_1_PD_ON 0xC0
/* Decimation Factor */
#define LIS3L02DQ_DEC_MASK 0x30
@@ -73,14 +73,14 @@
/* Interrupt related stuff */
#define LIS3L02DQ_REG_WAKE_UP_CFG_ADDR 0x23
-/* Switch from or combination fo conditions to and */
+/* Switch from or combination of conditions to and */
#define LIS3L02DQ_REG_WAKE_UP_CFG_BOOLEAN_AND 0x80
/* Latch interrupt request,
* if on ack must be given by reading the ack register */
#define LIS3L02DQ_REG_WAKE_UP_CFG_LATCH_SRC 0x40
-/* Z Interrupt on High (above threshold)*/
+/* Z Interrupt on High (above threshold) */
#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_HIGH 0x20
/* Z Interrupt on Low */
#define LIS3L02DQ_REG_WAKE_UP_CFG_INTERRUPT_Z_LOW 0x10
@@ -117,13 +117,13 @@
#define LIS3L02DQ_REG_STATUS_Y_OVERRUN 0x20
#define LIS3L02DQ_REG_STATUS_X_OVERRUN 0x10
/* XYZ new data available - first is all 3 available? */
-#define LIS3L02DQ_REG_STATUS_XYZ_NEW_DATA 0x08
+#define LIS3L02DQ_REG_STATUS_XYZ_NEW_DATA 0x08
#define LIS3L02DQ_REG_STATUS_Z_NEW_DATA 0x04
#define LIS3L02DQ_REG_STATUS_Y_NEW_DATA 0x02
#define LIS3L02DQ_REG_STATUS_X_NEW_DATA 0x01
/* The accelerometer readings - low and high bytes.
-Form of high byte dependent on justification set in ctrl reg */
+ * Form of high byte dependent on justification set in ctrl reg */
#define LIS3L02DQ_REG_OUT_X_L_ADDR 0x28
#define LIS3L02DQ_REG_OUT_X_H_ADDR 0x29
#define LIS3L02DQ_REG_OUT_Y_L_ADDR 0x2A
@@ -150,9 +150,9 @@ Form of high byte dependent on justification set in ctrl reg */
* struct lis3l02dq_state - device instance specific data
* @us: actual spi_device
* @trig: data ready trigger registered with iio
+ * @buf_lock: mutex to protect tx and rx
* @tx: transmit buffer
* @rx: receive buffer
- * @buf_lock: mutex to protect tx and rx
**/
struct lis3l02dq_state {
struct spi_device *us;
diff --git a/drivers/staging/iio/accel/lis3l02dq_core.c b/drivers/staging/iio/accel/lis3l02dq_core.c
index 9d263484fb86..21b0469f8bc2 100644
--- a/drivers/staging/iio/accel/lis3l02dq_core.c
+++ b/drivers/staging/iio/accel/lis3l02dq_core.c
@@ -2,7 +2,7 @@
* lis3l02dq.c support STMicroelectronics LISD02DQ
* 3d 2g Linear Accelerometers via SPI
*
- * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk>
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.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
@@ -392,7 +392,7 @@ static int lis3l02dq_initial_setup(struct iio_dev *indio_dev)
dev_err(&st->us->dev, "problem with setup control register 1");
goto err_ret;
}
- /* Repeat as sometimes doesn't work first time?*/
+ /* Repeat as sometimes doesn't work first time? */
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_1_ADDR,
val);
@@ -538,7 +538,7 @@ static irqreturn_t lis3l02dq_event_handler(int irq, void *private)
.event_mask = LIS3L02DQ_EVENT_MASK, \
}
-static struct iio_chan_spec lis3l02dq_channels[] = {
+static const struct iio_chan_spec lis3l02dq_channels[] = {
LIS3L02DQ_CHAN(0, IIO_MOD_X),
LIS3L02DQ_CHAN(1, IIO_MOD_Y),
LIS3L02DQ_CHAN(2, IIO_MOD_Z),
@@ -686,7 +686,7 @@ static int __devinit lis3l02dq_probe(struct spi_device *spi)
goto error_ret;
}
st = iio_priv(indio_dev);
- /* this is only used tor removal purposes */
+ /* this is only used for removal purposes */
spi_set_drvdata(spi, indio_dev);
st->us = spi;
@@ -780,21 +780,15 @@ err_ret:
}
/* fixme, confirm ordering in this function */
-static int lis3l02dq_remove(struct spi_device *spi)
+static int __devexit lis3l02dq_remove(struct spi_device *spi)
{
- int ret;
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct lis3l02dq_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
- ret = lis3l02dq_disable_all_events(indio_dev);
- if (ret)
- goto err_ret;
-
- ret = lis3l02dq_stop_device(indio_dev);
- if (ret)
- goto err_ret;
+ lis3l02dq_disable_all_events(indio_dev);
+ lis3l02dq_stop_device(indio_dev);
if (spi->irq && gpio_is_valid(irq_to_gpio(spi->irq)) > 0)
free_irq(st->us->irq, indio_dev);
@@ -804,8 +798,8 @@ static int lis3l02dq_remove(struct spi_device *spi)
lis3l02dq_unconfigure_buffer(indio_dev);
iio_device_free(indio_dev);
-err_ret:
- return ret;
+
+ return 0;
}
static struct spi_driver lis3l02dq_driver = {
@@ -818,7 +812,7 @@ static struct spi_driver lis3l02dq_driver = {
};
module_spi_driver(lis3l02dq_driver);
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("ST LIS3L02DQ Accelerometer SPI driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:lis3l02dq");
diff --git a/drivers/staging/iio/accel/lis3l02dq_ring.c b/drivers/staging/iio/accel/lis3l02dq_ring.c
index f3da59063ed2..fa4190d96247 100644
--- a/drivers/staging/iio/accel/lis3l02dq_ring.c
+++ b/drivers/staging/iio/accel/lis3l02dq_ring.c
@@ -14,7 +14,7 @@
#include "lis3l02dq.h"
/**
- * combine_8_to_16() utility function to munge to u8s into u16
+ * combine_8_to_16() utility function to munge two u8s into u16
**/
static inline u16 combine_8_to_16(u8 lower, u8 upper)
{
@@ -49,7 +49,7 @@ static const u8 read_all_tx_array[] = {
/**
* lis3l02dq_read_all() Reads all channels currently selected
- * @st: device specific state
+ * @indio_dev: IIO device state
* @rx_array: (dma capable) receive array, must be at least
* 4*number of channels
**/
@@ -137,7 +137,6 @@ static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
- struct iio_buffer *buffer = indio_dev->buffer;
int len = 0;
char *data;
@@ -155,7 +154,7 @@ static irqreturn_t lis3l02dq_trigger_handler(int irq, void *p)
if (indio_dev->scan_timestamp)
*(s64 *)((u8 *)data + ALIGN(len, sizeof(s64)))
= pf->timestamp;
- buffer->access->store_to(buffer, (u8 *)data, pf->timestamp);
+ iio_push_to_buffer(indio_dev->buffer, (u8 *)data);
kfree(data);
done:
@@ -172,22 +171,22 @@ __lis3l02dq_write_data_ready_config(struct iio_dev *indio_dev, bool state)
bool currentlyset;
struct lis3l02dq_state *st = iio_priv(indio_dev);
-/* Get the current event mask register */
+ /* Get the current event mask register */
ret = lis3l02dq_spi_read_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
&valold);
if (ret)
goto error_ret;
-/* Find out if data ready is already on */
+ /* Find out if data ready is already on */
currentlyset
= valold & LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
-/* Disable requested */
+ /* Disable requested */
if (!state && currentlyset) {
- /* disable the data ready signal */
+ /* Disable the data ready signal */
valold &= ~LIS3L02DQ_REG_CTRL_2_ENABLE_DATA_READY_GENERATION;
- /* The double write is to overcome a hardware bug?*/
+ /* The double write is to overcome a hardware bug? */
ret = lis3l02dq_spi_write_reg_8(indio_dev,
LIS3L02DQ_REG_CTRL_2_ADDR,
valold);
@@ -199,10 +198,10 @@ __lis3l02dq_write_data_ready_config(struct iio_dev *indio_dev, bool state)
if (ret)
goto error_ret;
st->trigger_on = false;
-/* Enable requested */
+ /* Enable requested */
} else if (state && !currentlyset) {
- /* if not set, enable requested */
- /* first disable all events */
+ /* If not set, enable requested
+ * first disable all events */
ret = lis3l02dq_disable_all_events(indio_dev);
if (ret < 0)
goto error_ret;
@@ -241,7 +240,7 @@ static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig,
if (state == false) {
/*
* A possible quirk with the handler is currently worked around
- * by ensuring outstanding read events are cleared.
+ * by ensuring outstanding read events are cleared.
*/
ret = lis3l02dq_read_all(indio_dev, NULL);
}
@@ -252,7 +251,7 @@ static int lis3l02dq_data_rdy_trigger_set_state(struct iio_trigger *trig,
}
/**
- * lis3l02dq_trig_try_reen() try renabling irq for data rdy trigger
+ * lis3l02dq_trig_try_reen() try reenabling irq for data rdy trigger
* @trig: the datardy trigger
*/
static int lis3l02dq_trig_try_reen(struct iio_trigger *trig)
@@ -261,8 +260,8 @@ static int lis3l02dq_trig_try_reen(struct iio_trigger *trig)
struct lis3l02dq_state *st = iio_priv(indio_dev);
int i;
- /* If gpio still high (or high again) */
- /* In theory possible we will need to do this several times */
+ /* If gpio still high (or high again)
+ * In theory possible we will need to do this several times */
for (i = 0; i < 5; i++)
if (gpio_get_value(irq_to_gpio(st->us->irq)))
lis3l02dq_read_all(indio_dev, NULL);
diff --git a/drivers/staging/iio/accel/sca3000.h b/drivers/staging/iio/accel/sca3000.h
index 131daac90012..c1016c510dae 100644
--- a/drivers/staging/iio/accel/sca3000.h
+++ b/drivers/staging/iio/accel/sca3000.h
@@ -2,7 +2,7 @@
* sca3000.c -- support VTI sca3000 series accelerometers
* via SPI
*
- * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk>
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
*
* Partly based upon tle62x0.c
*
diff --git a/drivers/staging/iio/accel/sca3000_core.c b/drivers/staging/iio/accel/sca3000_core.c
index c218d71abf1f..ffd1697a9db0 100644
--- a/drivers/staging/iio/accel/sca3000_core.c
+++ b/drivers/staging/iio/accel/sca3000_core.c
@@ -5,7 +5,7 @@
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
- * Copyright (c) 2009 Jonathan Cameron <jic23@cam.ac.uk>
+ * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
*
* See industrialio/accels/sca3000.h for comments.
*/
@@ -450,7 +450,7 @@ static IIO_DEVICE_ATTR(revision, S_IRUGO, sca3000_show_rev, NULL, 0);
.event_mask = SCA3000_EVENT_MASK, \
}
-static struct iio_chan_spec sca3000_channels[] = {
+static const struct iio_chan_spec sca3000_channels[] = {
SCA3000_CHAN(0, IIO_MOD_X),
SCA3000_CHAN(1, IIO_MOD_Y),
SCA3000_CHAN(2, IIO_MOD_Z),
@@ -1233,15 +1233,13 @@ error_ret:
return ret;
}
-static int sca3000_remove(struct spi_device *spi)
+static int __devexit sca3000_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct sca3000_state *st = iio_priv(indio_dev);
- int ret;
+
/* Must ensure no interrupts can be generated after this!*/
- ret = sca3000_stop_all_interrupts(st);
- if (ret)
- return ret;
+ sca3000_stop_all_interrupts(st);
if (spi->irq)
free_irq(spi->irq, indio_dev);
iio_device_unregister(indio_dev);
@@ -1272,6 +1270,6 @@ static struct spi_driver sca3000_driver = {
};
module_spi_driver(sca3000_driver);
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("VTI SCA3000 Series Accelerometers SPI driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/accel/sca3000_ring.c b/drivers/staging/iio/accel/sca3000_ring.c
index b7e1a002630a..cbec2f1665e5 100644
--- a/drivers/staging/iio/accel/sca3000_ring.c
+++ b/drivers/staging/iio/accel/sca3000_ring.c
@@ -5,7 +5,7 @@
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
- * Copyright (c) 2009 Jonathan Cameron <jic23@cam.ac.uk>
+ * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
*
*/
diff --git a/drivers/staging/iio/adc/Kconfig b/drivers/staging/iio/adc/Kconfig
index 67711b7d718a..a525143ecbea 100644
--- a/drivers/staging/iio/adc/Kconfig
+++ b/drivers/staging/iio/adc/Kconfig
@@ -68,20 +68,6 @@ config AD799X_RING_BUFFER
Say yes here to include ring buffer support in the AD799X
ADC driver.
-config AD7476
- tristate "Analog Devices AD7475/6/7/8 AD7466/7/8 and AD7495 ADC driver"
- depends on SPI
- select IIO_BUFFER
- select IIO_TRIGGERED_BUFFER
- help
- Say yes here to build support for Analog Devices
- AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, AD7495
- SPI analog to digital converters (ADC).
- If unsure, say N (but it's safe to say "Y").
-
- To compile this driver as a module, choose M here: the
- module will be called ad7476.
-
config AD7887
tristate "Analog Devices AD7887 ADC driver"
depends on SPI
@@ -96,11 +82,12 @@ config AD7887
module will be called ad7887.
config AD7780
- tristate "Analog Devices AD7780 AD7781 ADC driver"
+ tristate "Analog Devices AD7780 and similar ADCs driver"
depends on SPI
depends on GPIOLIB
+ select AD_SIGMA_DELTA
help
- Say yes here to build support for Analog Devices
+ Say yes here to build support for Analog Devices AD7170, AD7171,
AD7780 and AD7781 SPI analog to digital converters (ADC).
If unsure, say N (but it's safe to say "Y").
@@ -108,13 +95,12 @@ config AD7780
module will be called ad7780.
config AD7793
- tristate "Analog Devices AD7792 AD7793 ADC driver"
+ tristate "Analog Devices AD7793 and similar ADCs driver"
depends on SPI
- select IIO_BUFFER
- select IIO_TRIGGERED_BUFFER
+ select AD_SIGMA_DELTA
help
- Say yes here to build support for Analog Devices
- AD7792 and AD7793 SPI analog to digital converters (ADC).
+ Say yes here to build support for Analog Devices AD7785, AD7792, AD7793,
+ AD7794 and AD7795 SPI analog to digital converters (ADC).
If unsure, say N (but it's safe to say "Y").
To compile this driver as a module, choose M here: the
@@ -131,8 +117,7 @@ config AD7816
config AD7192
tristate "Analog Devices AD7190 AD7192 AD7195 ADC driver"
depends on SPI
- select IIO_BUFFER
- select IIO_TRIGGERED_BUFFER
+ select AD_SIGMA_DELTA
help
Say yes here to build support for Analog Devices AD7190,
AD7192 or AD7195 SPI analog to digital converters (ADC).
@@ -200,6 +185,18 @@ config LPC32XX_ADC
activate only one via device tree selection. Provides direct access
via sysfs.
+config MXS_LRADC
+ tristate "Freescale i.MX28 LRADC"
+ depends on ARCH_MXS
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for i.MX28 LRADC convertor
+ built into these chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mxs-lradc.
+
config SPEAR_ADC
tristate "ST SPEAr ADC"
depends on PLAT_SPEAR
diff --git a/drivers/staging/iio/adc/Makefile b/drivers/staging/iio/adc/Makefile
index 14e98b62b70a..62ee02e80cf9 100644
--- a/drivers/staging/iio/adc/Makefile
+++ b/drivers/staging/iio/adc/Makefile
@@ -17,10 +17,6 @@ ad799x-y := ad799x_core.o
ad799x-$(CONFIG_AD799X_RING_BUFFER) += ad799x_ring.o
obj-$(CONFIG_AD799X) += ad799x.o
-ad7476-y := ad7476_core.o
-ad7476-$(CONFIG_IIO_BUFFER) += ad7476_ring.o
-obj-$(CONFIG_AD7476) += ad7476.o
-
ad7887-y := ad7887_core.o
ad7887-$(CONFIG_IIO_BUFFER) += ad7887_ring.o
obj-$(CONFIG_AD7887) += ad7887.o
@@ -38,4 +34,5 @@ obj-$(CONFIG_ADT7310) += adt7310.o
obj-$(CONFIG_ADT7410) += adt7410.o
obj-$(CONFIG_AD7280) += ad7280a.o
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
+obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index 19a064d649e3..aeaa61d49f51 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -23,6 +23,7 @@
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
#include "ad7192.h"
@@ -57,6 +58,7 @@
/* Mode Register Bit Designations (AD7192_REG_MODE) */
#define AD7192_MODE_SEL(x) (((x) & 0x7) << 21) /* Operation Mode Select */
+#define AD7192_MODE_SEL_MASK (0x7 << 21) /* Operation Mode Select Mask */
#define AD7192_MODE_DAT_STA (1 << 20) /* Status Register transmission */
#define AD7192_MODE_CLKSRC(x) (((x) & 0x3) << 18) /* Clock Source Select */
#define AD7192_MODE_SINC3 (1 << 15) /* SINC3 Filter Select */
@@ -91,7 +93,8 @@
#define AD7192_CONF_CHOP (1 << 23) /* CHOP enable */
#define AD7192_CONF_REFSEL (1 << 20) /* REFIN1/REFIN2 Reference Select */
-#define AD7192_CONF_CHAN(x) (((x) & 0xFF) << 8) /* Channel select */
+#define AD7192_CONF_CHAN(x) (((1 << (x)) & 0xFF) << 8) /* Channel select */
+#define AD7192_CONF_CHAN_MASK (0xFF << 8) /* Channel select mask */
#define AD7192_CONF_BURN (1 << 7) /* Burnout current enable */
#define AD7192_CONF_REFDET (1 << 6) /* Reference detect enable */
#define AD7192_CONF_BUF (1 << 4) /* Buffered Mode Enable */
@@ -133,13 +136,7 @@
*/
struct ad7192_state {
- struct spi_device *spi;
- struct iio_trigger *trig;
struct regulator *reg;
- struct ad7192_platform_data *pdata;
- wait_queue_head_t wq_data_avail;
- bool done;
- bool irq_dis;
u16 int_vref_mv;
u32 mclk;
u32 f_order;
@@ -148,178 +145,45 @@ struct ad7192_state {
u32 scale_avail[8][2];
u8 gpocon;
u8 devid;
- /*
- * DMA (thus cache coherency maintenance) requires the
- * transfer buffers to live in their own cache lines.
- */
- u8 data[4] ____cacheline_aligned;
-};
-
-static int __ad7192_write_reg(struct ad7192_state *st, bool locked,
- bool cs_change, unsigned char reg,
- unsigned size, unsigned val)
-{
- u8 *data = st->data;
- struct spi_transfer t = {
- .tx_buf = data,
- .len = size + 1,
- .cs_change = cs_change,
- };
- struct spi_message m;
-
- data[0] = AD7192_COMM_WRITE | AD7192_COMM_ADDR(reg);
-
- switch (size) {
- case 3:
- data[1] = val >> 16;
- data[2] = val >> 8;
- data[3] = val;
- break;
- case 2:
- data[1] = val >> 8;
- data[2] = val;
- break;
- case 1:
- data[1] = val;
- break;
- default:
- return -EINVAL;
- }
-
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
- if (locked)
- return spi_sync_locked(st->spi, &m);
- else
- return spi_sync(st->spi, &m);
-}
+ struct ad_sigma_delta sd;
+};
-static int ad7192_write_reg(struct ad7192_state *st,
- unsigned reg, unsigned size, unsigned val)
+static struct ad7192_state *ad_sigma_delta_to_ad7192(struct ad_sigma_delta *sd)
{
- return __ad7192_write_reg(st, false, false, reg, size, val);
+ return container_of(sd, struct ad7192_state, sd);
}
-static int __ad7192_read_reg(struct ad7192_state *st, bool locked,
- bool cs_change, unsigned char reg,
- int *val, unsigned size)
+static int ad7192_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
{
- u8 *data = st->data;
- int ret;
- struct spi_transfer t[] = {
- {
- .tx_buf = data,
- .len = 1,
- }, {
- .rx_buf = data,
- .len = size,
- .cs_change = cs_change,
- },
- };
- struct spi_message m;
-
- data[0] = AD7192_COMM_READ | AD7192_COMM_ADDR(reg);
-
- spi_message_init(&m);
- spi_message_add_tail(&t[0], &m);
- spi_message_add_tail(&t[1], &m);
-
- if (locked)
- ret = spi_sync_locked(st->spi, &m);
- else
- ret = spi_sync(st->spi, &m);
-
- if (ret < 0)
- return ret;
-
- switch (size) {
- case 3:
- *val = data[0] << 16 | data[1] << 8 | data[2];
- break;
- case 2:
- *val = data[0] << 8 | data[1];
- break;
- case 1:
- *val = data[0];
- break;
- default:
- return -EINVAL;
- }
+ struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd);
- return 0;
-}
+ st->conf &= ~AD7192_CONF_CHAN_MASK;
+ st->conf |= AD7192_CONF_CHAN(channel);
-static int ad7192_read_reg(struct ad7192_state *st,
- unsigned reg, int *val, unsigned size)
-{
- return __ad7192_read_reg(st, 0, 0, reg, val, size);
+ return ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf);
}
-static int ad7192_read(struct ad7192_state *st, unsigned ch,
- unsigned len, int *val)
+static int ad7192_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
{
- int ret;
- st->conf = (st->conf & ~AD7192_CONF_CHAN(-1)) |
- AD7192_CONF_CHAN(1 << ch);
- st->mode = (st->mode & ~AD7192_MODE_SEL(-1)) |
- AD7192_MODE_SEL(AD7192_MODE_SINGLE);
+ struct ad7192_state *st = ad_sigma_delta_to_ad7192(sd);
- ad7192_write_reg(st, AD7192_REG_CONF, 3, st->conf);
+ st->mode &= ~AD7192_MODE_SEL_MASK;
+ st->mode |= AD7192_MODE_SEL(mode);
- spi_bus_lock(st->spi->master);
- st->done = false;
-
- ret = __ad7192_write_reg(st, 1, 1, AD7192_REG_MODE, 3, st->mode);
- if (ret < 0)
- goto out;
-
- st->irq_dis = false;
- enable_irq(st->spi->irq);
- wait_event_interruptible(st->wq_data_avail, st->done);
-
- ret = __ad7192_read_reg(st, 1, 0, AD7192_REG_DATA, val, len);
-out:
- spi_bus_unlock(st->spi->master);
-
- return ret;
+ return ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
}
-static int ad7192_calibrate(struct ad7192_state *st, unsigned mode, unsigned ch)
-{
- int ret;
-
- st->conf = (st->conf & ~AD7192_CONF_CHAN(-1)) |
- AD7192_CONF_CHAN(1 << ch);
- st->mode = (st->mode & ~AD7192_MODE_SEL(-1)) | AD7192_MODE_SEL(mode);
-
- ad7192_write_reg(st, AD7192_REG_CONF, 3, st->conf);
-
- spi_bus_lock(st->spi->master);
- st->done = false;
-
- ret = __ad7192_write_reg(st, 1, 1, AD7192_REG_MODE, 3,
- (st->devid != ID_AD7195) ?
- st->mode | AD7192_MODE_CLKDIV :
- st->mode);
- if (ret < 0)
- goto out;
-
- st->irq_dis = false;
- enable_irq(st->spi->irq);
- wait_event_interruptible(st->wq_data_avail, st->done);
-
- st->mode = (st->mode & ~AD7192_MODE_SEL(-1)) |
- AD7192_MODE_SEL(AD7192_MODE_IDLE);
-
- ret = __ad7192_write_reg(st, 1, 0, AD7192_REG_MODE, 3, st->mode);
-out:
- spi_bus_unlock(st->spi->master);
-
- return ret;
-}
+static const struct ad_sigma_delta_info ad7192_sigma_delta_info = {
+ .set_channel = ad7192_set_channel,
+ .set_mode = ad7192_set_mode,
+ .has_registers = true,
+ .addr_shift = 3,
+ .read_mask = BIT(6),
+};
-static const u8 ad7192_calib_arr[8][2] = {
+static const struct ad_sd_calib_data ad7192_calib_arr[8] = {
{AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN1},
{AD7192_MODE_CAL_INT_FULL, AD7192_CH_AIN1},
{AD7192_MODE_CAL_INT_ZERO, AD7192_CH_AIN2},
@@ -332,45 +196,34 @@ static const u8 ad7192_calib_arr[8][2] = {
static int ad7192_calibrate_all(struct ad7192_state *st)
{
- int i, ret;
-
- for (i = 0; i < ARRAY_SIZE(ad7192_calib_arr); i++) {
- ret = ad7192_calibrate(st, ad7192_calib_arr[i][0],
- ad7192_calib_arr[i][1]);
- if (ret)
- goto out;
- }
-
- return 0;
-out:
- dev_err(&st->spi->dev, "Calibration failed\n");
- return ret;
+ return ad_sd_calibrate_all(&st->sd, ad7192_calib_arr,
+ ARRAY_SIZE(ad7192_calib_arr));
}
-static int ad7192_setup(struct ad7192_state *st)
+static int ad7192_setup(struct ad7192_state *st,
+ const struct ad7192_platform_data *pdata)
{
- struct iio_dev *indio_dev = spi_get_drvdata(st->spi);
- struct ad7192_platform_data *pdata = st->pdata;
+ struct iio_dev *indio_dev = spi_get_drvdata(st->sd.spi);
unsigned long long scale_uv;
int i, ret, id;
u8 ones[6];
/* reset the serial interface */
memset(&ones, 0xFF, 6);
- ret = spi_write(st->spi, &ones, 6);
+ ret = spi_write(st->sd.spi, &ones, 6);
if (ret < 0)
goto out;
msleep(1); /* Wait for at least 500us */
/* write/read test for device presence */
- ret = ad7192_read_reg(st, AD7192_REG_ID, &id, 1);
+ ret = ad_sd_read_reg(&st->sd, AD7192_REG_ID, 1, &id);
if (ret)
goto out;
id &= AD7192_ID_MASK;
if (id != st->devid)
- dev_warn(&st->spi->dev, "device ID query failed (0x%X)\n", id);
+ dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X)\n", id);
switch (pdata->clock_source_sel) {
case AD7192_CLK_EXT_MCLK1_2:
@@ -423,11 +276,11 @@ static int ad7192_setup(struct ad7192_state *st)
if (pdata->burnout_curr_en)
st->conf |= AD7192_CONF_BURN;
- ret = ad7192_write_reg(st, AD7192_REG_MODE, 3, st->mode);
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
if (ret)
goto out;
- ret = ad7192_write_reg(st, AD7192_REG_CONF, 3, st->conf);
+ ret = ad_sd_write_reg(&st->sd, AD7192_REG_CONF, 3, st->conf);
if (ret)
goto out;
@@ -448,181 +301,10 @@ static int ad7192_setup(struct ad7192_state *st)
return 0;
out:
- dev_err(&st->spi->dev, "setup failed\n");
+ dev_err(&st->sd.spi->dev, "setup failed\n");
return ret;
}
-static int ad7192_ring_preenable(struct iio_dev *indio_dev)
-{
- struct ad7192_state *st = iio_priv(indio_dev);
- unsigned channel;
- int ret;
-
- if (bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
- return -EINVAL;
-
- ret = iio_sw_buffer_preenable(indio_dev);
- if (ret < 0)
- return ret;
-
- channel = find_first_bit(indio_dev->active_scan_mask,
- indio_dev->masklength);
-
- st->mode = (st->mode & ~AD7192_MODE_SEL(-1)) |
- AD7192_MODE_SEL(AD7192_MODE_CONT);
- st->conf = (st->conf & ~AD7192_CONF_CHAN(-1)) |
- AD7192_CONF_CHAN(1 << indio_dev->channels[channel].address);
-
- ad7192_write_reg(st, AD7192_REG_CONF, 3, st->conf);
-
- spi_bus_lock(st->spi->master);
- __ad7192_write_reg(st, 1, 1, AD7192_REG_MODE, 3, st->mode);
-
- st->irq_dis = false;
- enable_irq(st->spi->irq);
-
- return 0;
-}
-
-static int ad7192_ring_postdisable(struct iio_dev *indio_dev)
-{
- struct ad7192_state *st = iio_priv(indio_dev);
-
- st->mode = (st->mode & ~AD7192_MODE_SEL(-1)) |
- AD7192_MODE_SEL(AD7192_MODE_IDLE);
-
- st->done = false;
- wait_event_interruptible(st->wq_data_avail, st->done);
-
- if (!st->irq_dis)
- disable_irq_nosync(st->spi->irq);
-
- __ad7192_write_reg(st, 1, 0, AD7192_REG_MODE, 3, st->mode);
-
- return spi_bus_unlock(st->spi->master);
-}
-
-/**
- * ad7192_trigger_handler() bh of trigger launched polling to ring buffer
- **/
-static irqreturn_t ad7192_trigger_handler(int irq, void *p)
-{
- struct iio_poll_func *pf = p;
- struct iio_dev *indio_dev = pf->indio_dev;
- struct iio_buffer *ring = indio_dev->buffer;
- struct ad7192_state *st = iio_priv(indio_dev);
- s64 dat64[2];
- s32 *dat32 = (s32 *)dat64;
-
- if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
- __ad7192_read_reg(st, 1, 1, AD7192_REG_DATA,
- dat32,
- indio_dev->channels[0].scan_type.realbits/8);
-
- /* Guaranteed to be aligned with 8 byte boundary */
- if (indio_dev->scan_timestamp)
- dat64[1] = pf->timestamp;
-
- ring->access->store_to(ring, (u8 *)dat64, pf->timestamp);
-
- iio_trigger_notify_done(indio_dev->trig);
- st->irq_dis = false;
- enable_irq(st->spi->irq);
-
- return IRQ_HANDLED;
-}
-
-static const struct iio_buffer_setup_ops ad7192_ring_setup_ops = {
- .preenable = &ad7192_ring_preenable,
- .postenable = &iio_triggered_buffer_postenable,
- .predisable = &iio_triggered_buffer_predisable,
- .postdisable = &ad7192_ring_postdisable,
- .validate_scan_mask = &iio_validate_scan_mask_onehot,
-};
-
-static int ad7192_register_ring_funcs_and_init(struct iio_dev *indio_dev)
-{
- return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
- &ad7192_trigger_handler, &ad7192_ring_setup_ops);
-}
-
-static void ad7192_ring_cleanup(struct iio_dev *indio_dev)
-{
- iio_triggered_buffer_cleanup(indio_dev);
-}
-
-/**
- * ad7192_data_rdy_trig_poll() the event handler for the data rdy trig
- **/
-static irqreturn_t ad7192_data_rdy_trig_poll(int irq, void *private)
-{
- struct ad7192_state *st = iio_priv(private);
-
- st->done = true;
- wake_up_interruptible(&st->wq_data_avail);
- disable_irq_nosync(irq);
- st->irq_dis = true;
- iio_trigger_poll(st->trig, iio_get_time_ns());
-
- return IRQ_HANDLED;
-}
-
-static struct iio_trigger_ops ad7192_trigger_ops = {
- .owner = THIS_MODULE,
-};
-
-static int ad7192_probe_trigger(struct iio_dev *indio_dev)
-{
- struct ad7192_state *st = iio_priv(indio_dev);
- int ret;
-
- st->trig = iio_trigger_alloc("%s-dev%d",
- spi_get_device_id(st->spi)->name,
- indio_dev->id);
- if (st->trig == NULL) {
- ret = -ENOMEM;
- goto error_ret;
- }
- st->trig->ops = &ad7192_trigger_ops;
- ret = request_irq(st->spi->irq,
- ad7192_data_rdy_trig_poll,
- IRQF_TRIGGER_LOW,
- spi_get_device_id(st->spi)->name,
- indio_dev);
- if (ret)
- goto error_free_trig;
-
- disable_irq_nosync(st->spi->irq);
- st->irq_dis = true;
- st->trig->dev.parent = &st->spi->dev;
- st->trig->private_data = indio_dev;
-
- ret = iio_trigger_register(st->trig);
-
- /* select default trigger */
- indio_dev->trig = st->trig;
- if (ret)
- goto error_free_irq;
-
- return 0;
-
-error_free_irq:
- free_irq(st->spi->irq, indio_dev);
-error_free_trig:
- iio_trigger_free(st->trig);
-error_ret:
- return ret;
-}
-
-static void ad7192_remove_trigger(struct iio_dev *indio_dev)
-{
- struct ad7192_state *st = iio_priv(indio_dev);
-
- iio_trigger_unregister(st->trig);
- free_irq(st->spi->irq, indio_dev);
- iio_trigger_free(st->trig);
-}
-
static ssize_t ad7192_read_frequency(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -664,7 +346,7 @@ static ssize_t ad7192_write_frequency(struct device *dev,
st->mode &= ~AD7192_MODE_RATE(-1);
st->mode |= AD7192_MODE_RATE(div);
- ad7192_write_reg(st, AD7192_REG_MODE, 3, st->mode);
+ ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
out:
mutex_unlock(&indio_dev->mlock);
@@ -676,7 +358,6 @@ static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO,
ad7192_read_frequency,
ad7192_write_frequency);
-
static ssize_t ad7192_show_scale_available(struct device *dev,
struct device_attribute *attr, char *buf)
{
@@ -748,7 +429,7 @@ static ssize_t ad7192_set(struct device *dev,
else
st->gpocon &= ~AD7192_GPOCON_BPDSW;
- ad7192_write_reg(st, AD7192_REG_GPOCON, 1, st->gpocon);
+ ad_sd_write_reg(&st->sd, AD7192_REG_GPOCON, 1, st->gpocon);
break;
case AD7192_REG_MODE:
if (val)
@@ -756,7 +437,7 @@ static ssize_t ad7192_set(struct device *dev,
else
st->mode &= ~AD7192_MODE_ACX;
- ad7192_write_reg(st, AD7192_REG_MODE, 3, st->mode);
+ ad_sd_write_reg(&st->sd, AD7192_REG_MODE, 3, st->mode);
break;
default:
ret = -EINVAL;
@@ -812,27 +493,11 @@ static int ad7192_read_raw(struct iio_dev *indio_dev,
long m)
{
struct ad7192_state *st = iio_priv(indio_dev);
- int ret, smpl = 0;
bool unipolar = !!(st->conf & AD7192_CONF_UNIPOLAR);
switch (m) {
case IIO_CHAN_INFO_RAW:
- mutex_lock(&indio_dev->mlock);
- if (iio_buffer_enabled(indio_dev))
- ret = -EBUSY;
- else
- ret = ad7192_read(st, chan->address,
- chan->scan_type.realbits / 8, &smpl);
- mutex_unlock(&indio_dev->mlock);
-
- if (ret < 0)
- return ret;
-
- *val = (smpl >> chan->scan_type.shift) &
- ((1 << (chan->scan_type.realbits)) - 1);
-
- return IIO_VAL_INT;
-
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_VOLTAGE:
@@ -883,16 +548,16 @@ static int ad7192_write_raw(struct iio_dev *indio_dev,
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
if (val2 == st->scale_avail[i][1]) {
+ ret = 0;
tmp = st->conf;
st->conf &= ~AD7192_CONF_GAIN(-1);
st->conf |= AD7192_CONF_GAIN(i);
-
- if (tmp != st->conf) {
- ad7192_write_reg(st, AD7192_REG_CONF,
- 3, st->conf);
- ad7192_calibrate_all(st);
- }
- ret = 0;
+ if (tmp == st->conf)
+ break;
+ ad_sd_write_reg(&st->sd, AD7192_REG_CONF,
+ 3, st->conf);
+ ad7192_calibrate_all(st);
+ break;
}
break;
default:
@@ -904,15 +569,6 @@ static int ad7192_write_raw(struct iio_dev *indio_dev,
return ret;
}
-static int ad7192_validate_trigger(struct iio_dev *indio_dev,
- struct iio_trigger *trig)
-{
- if (indio_dev->trig != trig)
- return -EINVAL;
-
- return 0;
-}
-
static int ad7192_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
@@ -925,7 +581,7 @@ static const struct iio_info ad7192_info = {
.write_raw = &ad7192_write_raw,
.write_raw_get_fmt = &ad7192_write_raw_get_fmt,
.attrs = &ad7192_attribute_group,
- .validate_trigger = ad7192_validate_trigger,
+ .validate_trigger = ad_sd_validate_trigger,
.driver_module = THIS_MODULE,
};
@@ -934,60 +590,25 @@ static const struct iio_info ad7195_info = {
.write_raw = &ad7192_write_raw,
.write_raw_get_fmt = &ad7192_write_raw_get_fmt,
.attrs = &ad7195_attribute_group,
- .validate_trigger = ad7192_validate_trigger,
+ .validate_trigger = ad_sd_validate_trigger,
.driver_module = THIS_MODULE,
};
-#define AD7192_CHAN_DIFF(_chan, _chan2, _name, _address, _si) \
- { .type = IIO_VOLTAGE, \
- .differential = 1, \
- .indexed = 1, \
- .extend_name = _name, \
- .channel = _chan, \
- .channel2 = _chan2, \
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
- IIO_CHAN_INFO_SCALE_SHARED_BIT | \
- IIO_CHAN_INFO_OFFSET_SHARED_BIT, \
- .address = _address, \
- .scan_index = _si, \
- .scan_type = IIO_ST('u', 24, 32, 0)}
-
-#define AD7192_CHAN(_chan, _address, _si) \
- { .type = IIO_VOLTAGE, \
- .indexed = 1, \
- .channel = _chan, \
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
- IIO_CHAN_INFO_SCALE_SHARED_BIT | \
- IIO_CHAN_INFO_OFFSET_SHARED_BIT, \
- .address = _address, \
- .scan_index = _si, \
- .scan_type = IIO_ST('u', 24, 32, 0)}
-
-#define AD7192_CHAN_TEMP(_chan, _address, _si) \
- { .type = IIO_TEMP, \
- .indexed = 1, \
- .channel = _chan, \
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
- IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
- .address = _address, \
- .scan_index = _si, \
- .scan_type = IIO_ST('u', 24, 32, 0)}
-
-static struct iio_chan_spec ad7192_channels[] = {
- AD7192_CHAN_DIFF(1, 2, NULL, AD7192_CH_AIN1P_AIN2M, 0),
- AD7192_CHAN_DIFF(3, 4, NULL, AD7192_CH_AIN3P_AIN4M, 1),
- AD7192_CHAN_TEMP(0, AD7192_CH_TEMP, 2),
- AD7192_CHAN_DIFF(2, 2, "shorted", AD7192_CH_AIN2P_AIN2M, 3),
- AD7192_CHAN(1, AD7192_CH_AIN1, 4),
- AD7192_CHAN(2, AD7192_CH_AIN2, 5),
- AD7192_CHAN(3, AD7192_CH_AIN3, 6),
- AD7192_CHAN(4, AD7192_CH_AIN4, 7),
+static const struct iio_chan_spec ad7192_channels[] = {
+ AD_SD_DIFF_CHANNEL(0, 1, 2, AD7192_CH_AIN1P_AIN2M, 24, 32, 0),
+ AD_SD_DIFF_CHANNEL(1, 3, 4, AD7192_CH_AIN3P_AIN4M, 24, 32, 0),
+ AD_SD_TEMP_CHANNEL(2, AD7192_CH_TEMP, 24, 32, 0),
+ AD_SD_SHORTED_CHANNEL(3, 2, AD7192_CH_AIN2P_AIN2M, 24, 32, 0),
+ AD_SD_CHANNEL(4, 1, AD7192_CH_AIN1, 24, 32, 0),
+ AD_SD_CHANNEL(5, 2, AD7192_CH_AIN2, 24, 32, 0),
+ AD_SD_CHANNEL(6, 3, AD7192_CH_AIN3, 24, 32, 0),
+ AD_SD_CHANNEL(7, 4, AD7192_CH_AIN4, 24, 32, 0),
IIO_CHAN_SOFT_TIMESTAMP(8),
};
static int __devinit ad7192_probe(struct spi_device *spi)
{
- struct ad7192_platform_data *pdata = spi->dev.platform_data;
+ const struct ad7192_platform_data *pdata = spi->dev.platform_data;
struct ad7192_state *st;
struct iio_dev *indio_dev;
int ret , voltage_uv = 0;
@@ -1017,8 +638,6 @@ static int __devinit ad7192_probe(struct spi_device *spi)
voltage_uv = regulator_get_voltage(st->reg);
}
- st->pdata = pdata;
-
if (pdata && pdata->vref_mv)
st->int_vref_mv = pdata->vref_mv;
else if (voltage_uv)
@@ -1027,7 +646,6 @@ static int __devinit ad7192_probe(struct spi_device *spi)
dev_warn(&spi->dev, "reference voltage undefined\n");
spi_set_drvdata(spi, indio_dev);
- st->spi = spi;
st->devid = spi_get_device_id(spi)->driver_data;
indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi_get_device_id(spi)->name;
@@ -1039,17 +657,13 @@ static int __devinit ad7192_probe(struct spi_device *spi)
else
indio_dev->info = &ad7192_info;
- init_waitqueue_head(&st->wq_data_avail);
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info);
- ret = ad7192_register_ring_funcs_and_init(indio_dev);
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
if (ret)
goto error_disable_reg;
- ret = ad7192_probe_trigger(indio_dev);
- if (ret)
- goto error_ring_cleanup;
-
- ret = ad7192_setup(st);
+ ret = ad7192_setup(st, pdata);
if (ret)
goto error_remove_trigger;
@@ -1059,9 +673,7 @@ static int __devinit ad7192_probe(struct spi_device *spi)
return 0;
error_remove_trigger:
- ad7192_remove_trigger(indio_dev);
-error_ring_cleanup:
- ad7192_ring_cleanup(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
error_disable_reg:
if (!IS_ERR(st->reg))
regulator_disable(st->reg);
@@ -1074,14 +686,13 @@ error_put_reg:
return ret;
}
-static int ad7192_remove(struct spi_device *spi)
+static int __devexit ad7192_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ad7192_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
- ad7192_remove_trigger(indio_dev);
- ad7192_ring_cleanup(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
if (!IS_ERR(st->reg)) {
regulator_disable(st->reg);
diff --git a/drivers/staging/iio/adc/ad7298_core.c b/drivers/staging/iio/adc/ad7298_core.c
index 6141f4a70cfa..4c75114e7d7c 100644
--- a/drivers/staging/iio/adc/ad7298_core.c
+++ b/drivers/staging/iio/adc/ad7298_core.c
@@ -38,7 +38,7 @@
}, \
}
-static struct iio_chan_spec ad7298_channels[] = {
+static const struct iio_chan_spec ad7298_channels[] = {
{
.type = IIO_TEMP,
.indexed = 1,
diff --git a/drivers/staging/iio/adc/ad7298_ring.c b/drivers/staging/iio/adc/ad7298_ring.c
index 506016f01593..c2906a85fedb 100644
--- a/drivers/staging/iio/adc/ad7298_ring.c
+++ b/drivers/staging/iio/adc/ad7298_ring.c
@@ -75,7 +75,6 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7298_state *st = iio_priv(indio_dev);
- struct iio_buffer *ring = indio_dev->buffer;
s64 time_ns = 0;
__u16 buf[16];
int b_sent, i;
@@ -94,7 +93,7 @@ static irqreturn_t ad7298_trigger_handler(int irq, void *p)
indio_dev->masklength); i++)
buf[i] = be16_to_cpu(st->rx_buf[i]);
- indio_dev->buffer->access->store_to(ring, (u8 *)buf, time_ns);
+ iio_push_to_buffer(indio_dev->buffer, (u8 *)buf);
done:
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/staging/iio/adc/ad7476.h b/drivers/staging/iio/adc/ad7476.h
deleted file mode 100644
index b1dd9317fe1f..000000000000
--- a/drivers/staging/iio/adc/ad7476.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * AD7476/5/7/8 (A) SPI ADC driver
- *
- * Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
- */
-#ifndef IIO_ADC_AD7476_H_
-#define IIO_ADC_AD7476_H_
-
-#define RES_MASK(bits) ((1 << (bits)) - 1)
-
-/*
- * TODO: struct ad7476_platform_data needs to go into include/linux/iio
- */
-
-struct ad7476_platform_data {
- u16 vref_mv;
-};
-
-struct ad7476_chip_info {
- u16 int_vref_mv;
- struct iio_chan_spec channel[2];
-};
-
-struct ad7476_state {
- struct spi_device *spi;
- const struct ad7476_chip_info *chip_info;
- struct regulator *reg;
- u16 int_vref_mv;
- struct spi_transfer xfer;
- struct spi_message msg;
- /*
- * DMA (thus cache coherency maintenance) requires the
- * transfer buffers to live in their own cache lines.
- */
- unsigned char data[2] ____cacheline_aligned;
-};
-
-enum ad7476_supported_device_ids {
- ID_AD7466,
- ID_AD7467,
- ID_AD7468,
- ID_AD7475,
- ID_AD7476,
- ID_AD7477,
- ID_AD7478,
- ID_AD7495
-};
-
-#ifdef CONFIG_IIO_BUFFER
-int ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev);
-void ad7476_ring_cleanup(struct iio_dev *indio_dev);
-#else /* CONFIG_IIO_BUFFER */
-
-static inline int
-ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev)
-{
- return 0;
-}
-
-static inline void ad7476_ring_cleanup(struct iio_dev *indio_dev)
-{
-}
-#endif /* CONFIG_IIO_BUFFER */
-#endif /* IIO_ADC_AD7476_H_ */
diff --git a/drivers/staging/iio/adc/ad7476_ring.c b/drivers/staging/iio/adc/ad7476_ring.c
deleted file mode 100644
index d087b21c51f6..000000000000
--- a/drivers/staging/iio/adc/ad7476_ring.c
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright 2010-2012 Analog Devices Inc.
- * Copyright (C) 2008 Jonathan Cameron
- *
- * Licensed under the GPL-2 or later.
- *
- * ad7476_ring.c
- */
-
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/spi/spi.h>
-
-#include <linux/iio/iio.h>
-#include <linux/iio/buffer.h>
-#include <linux/iio/trigger_consumer.h>
-#include <linux/iio/triggered_buffer.h>
-
-#include "ad7476.h"
-
-static irqreturn_t ad7476_trigger_handler(int irq, void *p)
-{
- struct iio_poll_func *pf = p;
- struct iio_dev *indio_dev = pf->indio_dev;
- struct ad7476_state *st = iio_priv(indio_dev);
- s64 time_ns;
- __u8 *rxbuf;
- int b_sent;
-
- rxbuf = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
- if (rxbuf == NULL)
- goto done;
-
- b_sent = spi_read(st->spi, rxbuf,
- st->chip_info->channel[0].scan_type.storagebits / 8);
- if (b_sent < 0)
- goto done;
-
- time_ns = iio_get_time_ns();
-
- if (indio_dev->scan_timestamp)
- memcpy(rxbuf + indio_dev->scan_bytes - sizeof(s64),
- &time_ns, sizeof(time_ns));
-
- indio_dev->buffer->access->store_to(indio_dev->buffer, rxbuf, time_ns);
-done:
- iio_trigger_notify_done(indio_dev->trig);
- kfree(rxbuf);
-
- return IRQ_HANDLED;
-}
-
-int ad7476_register_ring_funcs_and_init(struct iio_dev *indio_dev)
-{
- return iio_triggered_buffer_setup(indio_dev, NULL,
- &ad7476_trigger_handler, NULL);
-}
-
-void ad7476_ring_cleanup(struct iio_dev *indio_dev)
-{
- iio_triggered_buffer_cleanup(indio_dev);
-}
diff --git a/drivers/staging/iio/adc/ad7606.h b/drivers/staging/iio/adc/ad7606.h
index 10f59896597f..9221a74efd18 100644
--- a/drivers/staging/iio/adc/ad7606.h
+++ b/drivers/staging/iio/adc/ad7606.h
@@ -51,7 +51,7 @@ struct ad7606_platform_data {
struct ad7606_chip_info {
const char *name;
u16 int_vref_mv;
- struct iio_chan_spec *channels;
+ const struct iio_chan_spec *channels;
unsigned num_channels;
};
diff --git a/drivers/staging/iio/adc/ad7606_core.c b/drivers/staging/iio/adc/ad7606_core.c
index ccb97fecdea7..bae61cbe9212 100644
--- a/drivers/staging/iio/adc/ad7606_core.c
+++ b/drivers/staging/iio/adc/ad7606_core.c
@@ -241,7 +241,7 @@ static const struct attribute_group ad7606_attribute_group_range = {
.scan_type = IIO_ST('s', 16, 16, 0), \
}
-static struct iio_chan_spec ad7606_8_channels[] = {
+static const struct iio_chan_spec ad7606_8_channels[] = {
AD7606_CHANNEL(0),
AD7606_CHANNEL(1),
AD7606_CHANNEL(2),
@@ -253,7 +253,7 @@ static struct iio_chan_spec ad7606_8_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(8),
};
-static struct iio_chan_spec ad7606_6_channels[] = {
+static const struct iio_chan_spec ad7606_6_channels[] = {
AD7606_CHANNEL(0),
AD7606_CHANNEL(1),
AD7606_CHANNEL(2),
@@ -263,7 +263,7 @@ static struct iio_chan_spec ad7606_6_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(6),
};
-static struct iio_chan_spec ad7606_4_channels[] = {
+static const struct iio_chan_spec ad7606_4_channels[] = {
AD7606_CHANNEL(0),
AD7606_CHANNEL(1),
AD7606_CHANNEL(2),
diff --git a/drivers/staging/iio/adc/ad7606_ring.c b/drivers/staging/iio/adc/ad7606_ring.c
index f15afe47c20d..ba04d0ffd4f4 100644
--- a/drivers/staging/iio/adc/ad7606_ring.c
+++ b/drivers/staging/iio/adc/ad7606_ring.c
@@ -46,7 +46,6 @@ static void ad7606_poll_bh_to_ring(struct work_struct *work_s)
struct ad7606_state *st = container_of(work_s, struct ad7606_state,
poll_work);
struct iio_dev *indio_dev = iio_priv_to_dev(st);
- struct iio_buffer *ring = indio_dev->buffer;
s64 time_ns;
__u8 *buf;
int ret;
@@ -84,7 +83,7 @@ static void ad7606_poll_bh_to_ring(struct work_struct *work_s)
if (indio_dev->scan_timestamp)
*((s64 *)(buf + indio_dev->scan_bytes - sizeof(s64))) = time_ns;
- ring->access->store_to(indio_dev->buffer, buf, time_ns);
+ iio_push_to_buffer(indio_dev->buffer, buf);
done:
gpio_set_value(st->pdata->gpio_convst, 0);
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c
index 19ee49c95de4..0a1328b8657f 100644
--- a/drivers/staging/iio/adc/ad7780.c
+++ b/drivers/staging/iio/adc/ad7780.c
@@ -1,5 +1,5 @@
/*
- * AD7780/AD7781 SPI ADC driver
+ * AD7170/AD7171 and AD7780/AD7781 SPI ADC driver
*
* Copyright 2011 Analog Devices Inc.
*
@@ -20,6 +20,7 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
#include "ad7780.h"
@@ -33,53 +34,53 @@
#define AD7780_PAT0 (1 << 0)
struct ad7780_chip_info {
- struct iio_chan_spec channel;
+ struct iio_chan_spec channel;
+ unsigned int pattern_mask;
+ unsigned int pattern;
};
struct ad7780_state {
- struct spi_device *spi;
const struct ad7780_chip_info *chip_info;
struct regulator *reg;
- struct ad7780_platform_data *pdata;
- wait_queue_head_t wq_data_avail;
- bool done;
+ int powerdown_gpio;
+ unsigned int gain;
u16 int_vref_mv;
- struct spi_transfer xfer;
- struct spi_message msg;
- /*
- * DMA (thus cache coherency maintenance) requires the
- * transfer buffers to live in their own cache lines.
- */
- unsigned int data ____cacheline_aligned;
+
+ struct ad_sigma_delta sd;
};
enum ad7780_supported_device_ids {
+ ID_AD7170,
+ ID_AD7171,
ID_AD7780,
ID_AD7781,
};
-static int ad7780_read(struct ad7780_state *st, int *val)
+static struct ad7780_state *ad_sigma_delta_to_ad7780(struct ad_sigma_delta *sd)
{
- int ret;
-
- spi_bus_lock(st->spi->master);
-
- enable_irq(st->spi->irq);
- st->done = false;
- gpio_set_value(st->pdata->gpio_pdrst, 1);
+ return container_of(sd, struct ad7780_state, sd);
+}
- ret = wait_event_interruptible(st->wq_data_avail, st->done);
- disable_irq_nosync(st->spi->irq);
- if (ret)
- goto out;
+static int ad7780_set_mode(struct ad_sigma_delta *sigma_delta,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta);
+ unsigned val;
+
+ switch (mode) {
+ case AD_SD_MODE_SINGLE:
+ case AD_SD_MODE_CONTINUOUS:
+ val = 1;
+ break;
+ default:
+ val = 0;
+ break;
+ }
- ret = spi_sync_locked(st->spi, &st->msg);
- *val = be32_to_cpu(st->data);
-out:
- gpio_set_value(st->pdata->gpio_pdrst, 0);
- spi_bus_unlock(st->spi->master);
+ if (gpio_is_valid(st->powerdown_gpio))
+ gpio_set_value(st->powerdown_gpio, val);
- return ret;
+ return 0;
}
static int ad7780_read_raw(struct iio_dev *indio_dev,
@@ -89,89 +90,75 @@ static int ad7780_read_raw(struct iio_dev *indio_dev,
long m)
{
struct ad7780_state *st = iio_priv(indio_dev);
- struct iio_chan_spec channel = st->chip_info->channel;
- int ret, smpl = 0;
unsigned long scale_uv;
switch (m) {
case IIO_CHAN_INFO_RAW:
- mutex_lock(&indio_dev->mlock);
- ret = ad7780_read(st, &smpl);
- mutex_unlock(&indio_dev->mlock);
-
- if (ret < 0)
- return ret;
-
- if ((smpl & AD7780_ERR) ||
- !((smpl & AD7780_PAT0) && !(smpl & AD7780_PAT1)))
- return -EIO;
-
- *val = (smpl >> channel.scan_type.shift) &
- ((1 << (channel.scan_type.realbits)) - 1);
- *val -= (1 << (channel.scan_type.realbits - 1));
-
- if (!(smpl & AD7780_GAIN))
- *val *= 128;
-
- return IIO_VAL_INT;
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
case IIO_CHAN_INFO_SCALE:
- scale_uv = (st->int_vref_mv * 100000)
- >> (channel.scan_type.realbits - 1);
+ scale_uv = (st->int_vref_mv * 100000 * st->gain)
+ >> (chan->scan_type.realbits - 1);
*val = scale_uv / 100000;
*val2 = (scale_uv % 100000) * 10;
return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val -= (1 << (chan->scan_type.realbits - 1));
+ return IIO_VAL_INT;
}
+
return -EINVAL;
}
+static int ad7780_postprocess_sample(struct ad_sigma_delta *sigma_delta,
+ unsigned int raw_sample)
+{
+ struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta);
+ const struct ad7780_chip_info *chip_info = st->chip_info;
+
+ if ((raw_sample & AD7780_ERR) ||
+ ((raw_sample & chip_info->pattern_mask) != chip_info->pattern))
+ return -EIO;
+
+ if (raw_sample & AD7780_GAIN)
+ st->gain = 1;
+ else
+ st->gain = 128;
+
+ return 0;
+}
+
+static const struct ad_sigma_delta_info ad7780_sigma_delta_info = {
+ .set_mode = ad7780_set_mode,
+ .postprocess_sample = ad7780_postprocess_sample,
+ .has_registers = false,
+};
+
+#define AD7780_CHANNEL(bits, wordsize) \
+ AD_SD_CHANNEL(1, 0, 0, bits, 32, wordsize - bits)
+
static const struct ad7780_chip_info ad7780_chip_info_tbl[] = {
+ [ID_AD7170] = {
+ .channel = AD7780_CHANNEL(12, 24),
+ .pattern = 0x5,
+ .pattern_mask = 0x7,
+ },
+ [ID_AD7171] = {
+ .channel = AD7780_CHANNEL(16, 24),
+ .pattern = 0x5,
+ .pattern_mask = 0x7,
+ },
[ID_AD7780] = {
- .channel = {
- .type = IIO_VOLTAGE,
- .indexed = 1,
- .channel = 0,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_type = {
- .sign = 'u',
- .realbits = 24,
- .storagebits = 32,
- .shift = 8,
- },
- },
+ .channel = AD7780_CHANNEL(24, 32),
+ .pattern = 0x1,
+ .pattern_mask = 0x3,
},
[ID_AD7781] = {
- .channel = {
- .type = IIO_VOLTAGE,
- .indexed = 1,
- .channel = 0,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_type = {
- .sign = 'u',
- .realbits = 20,
- .storagebits = 32,
- .shift = 12,
- },
- },
+ .channel = AD7780_CHANNEL(20, 32),
+ .pattern = 0x1,
+ .pattern_mask = 0x3,
},
};
-/**
- * Interrupt handler
- */
-static irqreturn_t ad7780_interrupt(int irq, void *dev_id)
-{
- struct ad7780_state *st = dev_id;
-
- st->done = true;
- wake_up_interruptible(&st->wq_data_avail);
-
- return IRQ_HANDLED;
-};
-
static const struct iio_info ad7780_info = {
.read_raw = &ad7780_read_raw,
.driver_module = THIS_MODULE,
@@ -184,16 +171,14 @@ static int __devinit ad7780_probe(struct spi_device *spi)
struct iio_dev *indio_dev;
int ret, voltage_uv = 0;
- if (!pdata) {
- dev_dbg(&spi->dev, "no platform data?\n");
- return -ENODEV;
- }
-
indio_dev = iio_device_alloc(sizeof(*st));
if (indio_dev == NULL)
return -ENOMEM;
st = iio_priv(indio_dev);
+ st->gain = 1;
+
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7780_sigma_delta_info);
st->reg = regulator_get(&spi->dev, "vcc");
if (!IS_ERR(st->reg)) {
@@ -207,8 +192,6 @@ static int __devinit ad7780_probe(struct spi_device *spi)
st->chip_info =
&ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data];
- st->pdata = pdata;
-
if (pdata && pdata->vref_mv)
st->int_vref_mv = pdata->vref_mv;
else if (voltage_uv)
@@ -217,7 +200,6 @@ static int __devinit ad7780_probe(struct spi_device *spi)
dev_warn(&spi->dev, "reference voltage unspecified\n");
spi_set_drvdata(spi, indio_dev);
- st->spi = spi;
indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi_get_device_id(spi)->name;
@@ -226,40 +208,34 @@ static int __devinit ad7780_probe(struct spi_device *spi)
indio_dev->num_channels = 1;
indio_dev->info = &ad7780_info;
- init_waitqueue_head(&st->wq_data_avail);
-
- /* Setup default message */
+ if (pdata && gpio_is_valid(pdata->gpio_pdrst)) {
- st->xfer.rx_buf = &st->data;
- st->xfer.len = st->chip_info->channel.scan_type.storagebits / 8;
-
- spi_message_init(&st->msg);
- spi_message_add_tail(&st->xfer, &st->msg);
-
- ret = gpio_request_one(st->pdata->gpio_pdrst, GPIOF_OUT_INIT_LOW,
+ ret = gpio_request_one(pdata->gpio_pdrst, GPIOF_OUT_INIT_LOW,
"AD7780 /PDRST");
- if (ret) {
- dev_err(&spi->dev, "failed to request GPIO PDRST\n");
- goto error_disable_reg;
+ if (ret) {
+ dev_err(&spi->dev, "failed to request GPIO PDRST\n");
+ goto error_disable_reg;
+ }
+ st->powerdown_gpio = pdata->gpio_pdrst;
+ } else {
+ st->powerdown_gpio = -1;
}
- ret = request_irq(spi->irq, ad7780_interrupt,
- IRQF_TRIGGER_FALLING, spi_get_device_id(spi)->name, st);
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
if (ret)
goto error_free_gpio;
- disable_irq(spi->irq);
-
ret = iio_device_register(indio_dev);
if (ret)
- goto error_free_irq;
+ goto error_cleanup_buffer_and_trigger;
return 0;
-error_free_irq:
- free_irq(spi->irq, st);
+error_cleanup_buffer_and_trigger:
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
error_free_gpio:
- gpio_free(st->pdata->gpio_pdrst);
+ if (pdata && gpio_is_valid(pdata->gpio_pdrst))
+ gpio_free(pdata->gpio_pdrst);
error_disable_reg:
if (!IS_ERR(st->reg))
regulator_disable(st->reg);
@@ -272,14 +248,17 @@ error_put_reg:
return ret;
}
-static int ad7780_remove(struct spi_device *spi)
+static int __devexit ad7780_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ad7780_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
- free_irq(spi->irq, st);
- gpio_free(st->pdata->gpio_pdrst);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+
+ if (gpio_is_valid(st->powerdown_gpio))
+ gpio_free(st->powerdown_gpio);
+
if (!IS_ERR(st->reg)) {
regulator_disable(st->reg);
regulator_put(st->reg);
@@ -290,6 +269,8 @@ static int ad7780_remove(struct spi_device *spi)
}
static const struct spi_device_id ad7780_id[] = {
+ {"ad7170", ID_AD7170},
+ {"ad7171", ID_AD7171},
{"ad7780", ID_AD7780},
{"ad7781", ID_AD7781},
{}
@@ -308,5 +289,5 @@ static struct spi_driver ad7780_driver = {
module_spi_driver(ad7780_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("Analog Devices AD7780/1 ADC");
+MODULE_DESCRIPTION("Analog Devices AD7780 and similar ADCs");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/ad7793.c b/drivers/staging/iio/adc/ad7793.c
index 112e2b7b5bc4..691a7be6f5cb 100644
--- a/drivers/staging/iio/adc/ad7793.c
+++ b/drivers/staging/iio/adc/ad7793.c
@@ -1,5 +1,5 @@
/*
- * AD7792/AD7793 SPI ADC driver
+ * AD7785/AD7792/AD7793/AD7794/AD7795 SPI ADC driver
*
* Copyright 2011-2012 Analog Devices Inc.
*
@@ -24,6 +24,7 @@
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
#include "ad7793.h"
@@ -36,198 +37,65 @@
*/
struct ad7793_chip_info {
- struct iio_chan_spec channel[7];
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
};
struct ad7793_state {
- struct spi_device *spi;
- struct iio_trigger *trig;
const struct ad7793_chip_info *chip_info;
struct regulator *reg;
- struct ad7793_platform_data *pdata;
- wait_queue_head_t wq_data_avail;
- bool done;
- bool irq_dis;
u16 int_vref_mv;
u16 mode;
u16 conf;
u32 scale_avail[8][2];
- /*
- * DMA (thus cache coherency maintenance) requires the
- * transfer buffers to live in their own cache lines.
- */
- u8 data[4] ____cacheline_aligned;
+ struct ad_sigma_delta sd;
+
};
enum ad7793_supported_device_ids {
+ ID_AD7785,
ID_AD7792,
ID_AD7793,
+ ID_AD7794,
+ ID_AD7795,
};
-static int __ad7793_write_reg(struct ad7793_state *st, bool locked,
- bool cs_change, unsigned char reg,
- unsigned size, unsigned val)
-{
- u8 *data = st->data;
- struct spi_transfer t = {
- .tx_buf = data,
- .len = size + 1,
- .cs_change = cs_change,
- };
- struct spi_message m;
-
- data[0] = AD7793_COMM_WRITE | AD7793_COMM_ADDR(reg);
-
- switch (size) {
- case 3:
- data[1] = val >> 16;
- data[2] = val >> 8;
- data[3] = val;
- break;
- case 2:
- data[1] = val >> 8;
- data[2] = val;
- break;
- case 1:
- data[1] = val;
- break;
- default:
- return -EINVAL;
- }
-
- spi_message_init(&m);
- spi_message_add_tail(&t, &m);
-
- if (locked)
- return spi_sync_locked(st->spi, &m);
- else
- return spi_sync(st->spi, &m);
-}
-
-static int ad7793_write_reg(struct ad7793_state *st,
- unsigned reg, unsigned size, unsigned val)
+static struct ad7793_state *ad_sigma_delta_to_ad7793(struct ad_sigma_delta *sd)
{
- return __ad7793_write_reg(st, false, false, reg, size, val);
+ return container_of(sd, struct ad7793_state, sd);
}
-static int __ad7793_read_reg(struct ad7793_state *st, bool locked,
- bool cs_change, unsigned char reg,
- int *val, unsigned size)
+static int ad7793_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
{
- u8 *data = st->data;
- int ret;
- struct spi_transfer t[] = {
- {
- .tx_buf = data,
- .len = 1,
- }, {
- .rx_buf = data,
- .len = size,
- .cs_change = cs_change,
- },
- };
- struct spi_message m;
-
- data[0] = AD7793_COMM_READ | AD7793_COMM_ADDR(reg);
-
- spi_message_init(&m);
- spi_message_add_tail(&t[0], &m);
- spi_message_add_tail(&t[1], &m);
-
- if (locked)
- ret = spi_sync_locked(st->spi, &m);
- else
- ret = spi_sync(st->spi, &m);
-
- if (ret < 0)
- return ret;
-
- switch (size) {
- case 3:
- *val = data[0] << 16 | data[1] << 8 | data[2];
- break;
- case 2:
- *val = data[0] << 8 | data[1];
- break;
- case 1:
- *val = data[0];
- break;
- default:
- return -EINVAL;
- }
+ struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd);
- return 0;
-}
+ st->conf &= ~AD7793_CONF_CHAN_MASK;
+ st->conf |= AD7793_CONF_CHAN(channel);
-static int ad7793_read_reg(struct ad7793_state *st,
- unsigned reg, int *val, unsigned size)
-{
- return __ad7793_read_reg(st, 0, 0, reg, val, size);
+ return ad_sd_write_reg(&st->sd, AD7793_REG_CONF, 2, st->conf);
}
-static int ad7793_read(struct ad7793_state *st, unsigned ch,
- unsigned len, int *val)
+static int ad7793_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
{
- int ret;
- st->conf = (st->conf & ~AD7793_CONF_CHAN(-1)) | AD7793_CONF_CHAN(ch);
- st->mode = (st->mode & ~AD7793_MODE_SEL(-1)) |
- AD7793_MODE_SEL(AD7793_MODE_SINGLE);
-
- ad7793_write_reg(st, AD7793_REG_CONF, sizeof(st->conf), st->conf);
-
- spi_bus_lock(st->spi->master);
- st->done = false;
-
- ret = __ad7793_write_reg(st, 1, 1, AD7793_REG_MODE,
- sizeof(st->mode), st->mode);
- if (ret < 0)
- goto out;
+ struct ad7793_state *st = ad_sigma_delta_to_ad7793(sd);
- st->irq_dis = false;
- enable_irq(st->spi->irq);
- wait_event_interruptible(st->wq_data_avail, st->done);
+ st->mode &= ~AD7793_MODE_SEL_MASK;
+ st->mode |= AD7793_MODE_SEL(mode);
- ret = __ad7793_read_reg(st, 1, 0, AD7793_REG_DATA, val, len);
-out:
- spi_bus_unlock(st->spi->master);
-
- return ret;
+ return ad_sd_write_reg(&st->sd, AD7793_REG_MODE, 2, st->mode);
}
-static int ad7793_calibrate(struct ad7793_state *st, unsigned mode, unsigned ch)
-{
- int ret;
-
- st->conf = (st->conf & ~AD7793_CONF_CHAN(-1)) | AD7793_CONF_CHAN(ch);
- st->mode = (st->mode & ~AD7793_MODE_SEL(-1)) | AD7793_MODE_SEL(mode);
-
- ad7793_write_reg(st, AD7793_REG_CONF, sizeof(st->conf), st->conf);
-
- spi_bus_lock(st->spi->master);
- st->done = false;
-
- ret = __ad7793_write_reg(st, 1, 1, AD7793_REG_MODE,
- sizeof(st->mode), st->mode);
- if (ret < 0)
- goto out;
-
- st->irq_dis = false;
- enable_irq(st->spi->irq);
- wait_event_interruptible(st->wq_data_avail, st->done);
-
- st->mode = (st->mode & ~AD7793_MODE_SEL(-1)) |
- AD7793_MODE_SEL(AD7793_MODE_IDLE);
-
- ret = __ad7793_write_reg(st, 1, 0, AD7793_REG_MODE,
- sizeof(st->mode), st->mode);
-out:
- spi_bus_unlock(st->spi->master);
-
- return ret;
-}
+static const struct ad_sigma_delta_info ad7793_sigma_delta_info = {
+ .set_channel = ad7793_set_channel,
+ .set_mode = ad7793_set_mode,
+ .has_registers = true,
+ .addr_shift = 3,
+ .read_mask = BIT(6),
+};
-static const u8 ad7793_calib_arr[6][2] = {
+static const struct ad_sd_calib_data ad7793_calib_arr[6] = {
{AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN1P_AIN1M},
{AD7793_MODE_CAL_INT_FULL, AD7793_CH_AIN1P_AIN1M},
{AD7793_MODE_CAL_INT_ZERO, AD7793_CH_AIN2P_AIN2M},
@@ -238,59 +106,49 @@ static const u8 ad7793_calib_arr[6][2] = {
static int ad7793_calibrate_all(struct ad7793_state *st)
{
- int i, ret;
-
- for (i = 0; i < ARRAY_SIZE(ad7793_calib_arr); i++) {
- ret = ad7793_calibrate(st, ad7793_calib_arr[i][0],
- ad7793_calib_arr[i][1]);
- if (ret)
- goto out;
- }
-
- return 0;
-out:
- dev_err(&st->spi->dev, "Calibration failed\n");
- return ret;
+ return ad_sd_calibrate_all(&st->sd, ad7793_calib_arr,
+ ARRAY_SIZE(ad7793_calib_arr));
}
-static int ad7793_setup(struct ad7793_state *st)
+static int ad7793_setup(struct iio_dev *indio_dev,
+ const struct ad7793_platform_data *pdata)
{
+ struct ad7793_state *st = iio_priv(indio_dev);
int i, ret = -1;
unsigned long long scale_uv;
u32 id;
/* reset the serial interface */
- ret = spi_write(st->spi, (u8 *)&ret, sizeof(ret));
+ ret = spi_write(st->sd.spi, (u8 *)&ret, sizeof(ret));
if (ret < 0)
goto out;
msleep(1); /* Wait for at least 500us */
/* write/read test for device presence */
- ret = ad7793_read_reg(st, AD7793_REG_ID, &id, 1);
+ ret = ad_sd_read_reg(&st->sd, AD7793_REG_ID, 1, &id);
if (ret)
goto out;
id &= AD7793_ID_MASK;
- if (!((id == AD7792_ID) || (id == AD7793_ID))) {
- dev_err(&st->spi->dev, "device ID query failed\n");
+ if (!((id == AD7792_ID) || (id == AD7793_ID) || (id == AD7795_ID))) {
+ dev_err(&st->sd.spi->dev, "device ID query failed\n");
goto out;
}
- st->mode = (st->pdata->mode & ~AD7793_MODE_SEL(-1)) |
- AD7793_MODE_SEL(AD7793_MODE_IDLE);
- st->conf = st->pdata->conf & ~AD7793_CONF_CHAN(-1);
+ st->mode = pdata->mode;
+ st->conf = pdata->conf;
- ret = ad7793_write_reg(st, AD7793_REG_MODE, sizeof(st->mode), st->mode);
+ ret = ad7793_set_mode(&st->sd, AD_SD_MODE_IDLE);
if (ret)
goto out;
- ret = ad7793_write_reg(st, AD7793_REG_CONF, sizeof(st->conf), st->conf);
+ ret = ad7793_set_channel(&st->sd, 0);
if (ret)
goto out;
- ret = ad7793_write_reg(st, AD7793_REG_IO,
- sizeof(st->pdata->io), st->pdata->io);
+ ret = ad_sd_write_reg(&st->sd, AD7793_REG_IO,
+ sizeof(pdata->io), pdata->io);
if (ret)
goto out;
@@ -301,7 +159,7 @@ static int ad7793_setup(struct ad7793_state *st)
/* Populate available ADC input ranges */
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++) {
scale_uv = ((u64)st->int_vref_mv * 100000000)
- >> (st->chip_info->channel[0].scan_type.realbits -
+ >> (st->chip_info->channels[0].scan_type.realbits -
(!!(st->conf & AD7793_CONF_UNIPOLAR) ? 0 : 1));
scale_uv >>= i;
@@ -311,184 +169,10 @@ static int ad7793_setup(struct ad7793_state *st)
return 0;
out:
- dev_err(&st->spi->dev, "setup failed\n");
+ dev_err(&st->sd.spi->dev, "setup failed\n");
return ret;
}
-static int ad7793_ring_preenable(struct iio_dev *indio_dev)
-{
- struct ad7793_state *st = iio_priv(indio_dev);
- unsigned channel;
- int ret;
-
- if (bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
- return -EINVAL;
- ret = iio_sw_buffer_preenable(indio_dev);
- if (ret < 0)
- return ret;
-
- channel = find_first_bit(indio_dev->active_scan_mask,
- indio_dev->masklength);
-
- st->mode = (st->mode & ~AD7793_MODE_SEL(-1)) |
- AD7793_MODE_SEL(AD7793_MODE_CONT);
- st->conf = (st->conf & ~AD7793_CONF_CHAN(-1)) |
- AD7793_CONF_CHAN(indio_dev->channels[channel].address);
-
- ad7793_write_reg(st, AD7793_REG_CONF, sizeof(st->conf), st->conf);
-
- spi_bus_lock(st->spi->master);
- __ad7793_write_reg(st, 1, 1, AD7793_REG_MODE,
- sizeof(st->mode), st->mode);
-
- st->irq_dis = false;
- enable_irq(st->spi->irq);
-
- return 0;
-}
-
-static int ad7793_ring_postdisable(struct iio_dev *indio_dev)
-{
- struct ad7793_state *st = iio_priv(indio_dev);
-
- st->mode = (st->mode & ~AD7793_MODE_SEL(-1)) |
- AD7793_MODE_SEL(AD7793_MODE_IDLE);
-
- st->done = false;
- wait_event_interruptible(st->wq_data_avail, st->done);
-
- if (!st->irq_dis)
- disable_irq_nosync(st->spi->irq);
-
- __ad7793_write_reg(st, 1, 0, AD7793_REG_MODE,
- sizeof(st->mode), st->mode);
-
- return spi_bus_unlock(st->spi->master);
-}
-
-/**
- * ad7793_trigger_handler() bh of trigger launched polling to ring buffer
- **/
-
-static irqreturn_t ad7793_trigger_handler(int irq, void *p)
-{
- struct iio_poll_func *pf = p;
- struct iio_dev *indio_dev = pf->indio_dev;
- struct iio_buffer *ring = indio_dev->buffer;
- struct ad7793_state *st = iio_priv(indio_dev);
- s64 dat64[2];
- s32 *dat32 = (s32 *)dat64;
-
- if (!bitmap_empty(indio_dev->active_scan_mask, indio_dev->masklength))
- __ad7793_read_reg(st, 1, 1, AD7793_REG_DATA,
- dat32,
- indio_dev->channels[0].scan_type.realbits/8);
-
- /* Guaranteed to be aligned with 8 byte boundary */
- if (indio_dev->scan_timestamp)
- dat64[1] = pf->timestamp;
-
- ring->access->store_to(ring, (u8 *)dat64, pf->timestamp);
-
- iio_trigger_notify_done(indio_dev->trig);
- st->irq_dis = false;
- enable_irq(st->spi->irq);
-
- return IRQ_HANDLED;
-}
-
-static const struct iio_buffer_setup_ops ad7793_ring_setup_ops = {
- .preenable = &ad7793_ring_preenable,
- .postenable = &iio_triggered_buffer_postenable,
- .predisable = &iio_triggered_buffer_predisable,
- .postdisable = &ad7793_ring_postdisable,
- .validate_scan_mask = &iio_validate_scan_mask_onehot,
-};
-
-static int ad7793_register_ring_funcs_and_init(struct iio_dev *indio_dev)
-{
- return iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
- &ad7793_trigger_handler, &ad7793_ring_setup_ops);
-}
-
-static void ad7793_ring_cleanup(struct iio_dev *indio_dev)
-{
- iio_triggered_buffer_cleanup(indio_dev);
-}
-
-/**
- * ad7793_data_rdy_trig_poll() the event handler for the data rdy trig
- **/
-static irqreturn_t ad7793_data_rdy_trig_poll(int irq, void *private)
-{
- struct ad7793_state *st = iio_priv(private);
-
- st->done = true;
- wake_up_interruptible(&st->wq_data_avail);
- disable_irq_nosync(irq);
- st->irq_dis = true;
- iio_trigger_poll(st->trig, iio_get_time_ns());
-
- return IRQ_HANDLED;
-}
-
-static struct iio_trigger_ops ad7793_trigger_ops = {
- .owner = THIS_MODULE,
-};
-
-static int ad7793_probe_trigger(struct iio_dev *indio_dev)
-{
- struct ad7793_state *st = iio_priv(indio_dev);
- int ret;
-
- st->trig = iio_trigger_alloc("%s-dev%d",
- spi_get_device_id(st->spi)->name,
- indio_dev->id);
- if (st->trig == NULL) {
- ret = -ENOMEM;
- goto error_ret;
- }
- st->trig->ops = &ad7793_trigger_ops;
-
- ret = request_irq(st->spi->irq,
- ad7793_data_rdy_trig_poll,
- IRQF_TRIGGER_LOW,
- spi_get_device_id(st->spi)->name,
- indio_dev);
- if (ret)
- goto error_free_trig;
-
- disable_irq_nosync(st->spi->irq);
- st->irq_dis = true;
- st->trig->dev.parent = &st->spi->dev;
- st->trig->private_data = indio_dev;
-
- ret = iio_trigger_register(st->trig);
-
- /* select default trigger */
- indio_dev->trig = st->trig;
- if (ret)
- goto error_free_irq;
-
- return 0;
-
-error_free_irq:
- free_irq(st->spi->irq, indio_dev);
-error_free_trig:
- iio_trigger_free(st->trig);
-error_ret:
- return ret;
-}
-
-static void ad7793_remove_trigger(struct iio_dev *indio_dev)
-{
- struct ad7793_state *st = iio_priv(indio_dev);
-
- iio_trigger_unregister(st->trig);
- free_irq(st->spi->irq, indio_dev);
- iio_trigger_free(st->trig);
-}
-
static const u16 sample_freq_avail[16] = {0, 470, 242, 123, 62, 50, 39, 33, 19,
17, 16, 12, 10, 8, 6, 4};
@@ -531,7 +215,7 @@ static ssize_t ad7793_write_frequency(struct device *dev,
mutex_lock(&indio_dev->mlock);
st->mode &= ~AD7793_MODE_RATE(-1);
st->mode |= AD7793_MODE_RATE(i);
- ad7793_write_reg(st, AD7793_REG_MODE,
+ ad_sd_write_reg(&st->sd, AD7793_REG_MODE,
sizeof(st->mode), st->mode);
mutex_unlock(&indio_dev->mlock);
ret = 0;
@@ -585,26 +269,16 @@ static int ad7793_read_raw(struct iio_dev *indio_dev,
long m)
{
struct ad7793_state *st = iio_priv(indio_dev);
- int ret, smpl = 0;
+ int ret;
unsigned long long scale_uv;
bool unipolar = !!(st->conf & AD7793_CONF_UNIPOLAR);
switch (m) {
case IIO_CHAN_INFO_RAW:
- mutex_lock(&indio_dev->mlock);
- if (iio_buffer_enabled(indio_dev))
- ret = -EBUSY;
- else
- ret = ad7793_read(st, chan->address,
- chan->scan_type.realbits / 8, &smpl);
- mutex_unlock(&indio_dev->mlock);
-
+ ret = ad_sigma_delta_single_conversion(indio_dev, chan, val);
if (ret < 0)
return ret;
- *val = (smpl >> chan->scan_type.shift) &
- ((1 << (chan->scan_type.realbits)) - 1);
-
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
@@ -675,17 +349,18 @@ static int ad7793_write_raw(struct iio_dev *indio_dev,
ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(st->scale_avail); i++)
if (val2 == st->scale_avail[i][1]) {
+ ret = 0;
tmp = st->conf;
st->conf &= ~AD7793_CONF_GAIN(-1);
st->conf |= AD7793_CONF_GAIN(i);
- if (tmp != st->conf) {
- ad7793_write_reg(st, AD7793_REG_CONF,
- sizeof(st->conf),
- st->conf);
- ad7793_calibrate_all(st);
- }
- ret = 0;
+ if (tmp == st->conf)
+ break;
+
+ ad_sd_write_reg(&st->sd, AD7793_REG_CONF,
+ sizeof(st->conf), st->conf);
+ ad7793_calibrate_all(st);
+ break;
}
break;
default:
@@ -696,15 +371,6 @@ static int ad7793_write_raw(struct iio_dev *indio_dev,
return ret;
}
-static int ad7793_validate_trigger(struct iio_dev *indio_dev,
- struct iio_trigger *trig)
-{
- if (indio_dev->trig != trig)
- return -EINVAL;
-
- return 0;
-}
-
static int ad7793_write_raw_get_fmt(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
long mask)
@@ -717,172 +383,67 @@ static const struct iio_info ad7793_info = {
.write_raw = &ad7793_write_raw,
.write_raw_get_fmt = &ad7793_write_raw_get_fmt,
.attrs = &ad7793_attribute_group,
- .validate_trigger = ad7793_validate_trigger,
+ .validate_trigger = ad_sd_validate_trigger,
.driver_module = THIS_MODULE,
};
+#define DECLARE_AD7793_CHANNELS(_name, _b, _sb, _s) \
+const struct iio_chan_spec _name##_channels[] = { \
+ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), (_s)), \
+ AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), (_s)), \
+ AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), (_s)), \
+ AD_SD_SHORTED_CHANNEL(3, 0, AD7793_CH_AIN1M_AIN1M, (_b), (_sb), (_s)), \
+ AD_SD_TEMP_CHANNEL(4, AD7793_CH_TEMP, (_b), (_sb), (_s)), \
+ AD_SD_SUPPLY_CHANNEL(5, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), (_s)), \
+ IIO_CHAN_SOFT_TIMESTAMP(6), \
+}
+
+#define DECLARE_AD7795_CHANNELS(_name, _b, _sb) \
+const struct iio_chan_spec _name##_channels[] = { \
+ AD_SD_DIFF_CHANNEL(0, 0, 0, AD7793_CH_AIN1P_AIN1M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(1, 1, 1, AD7793_CH_AIN2P_AIN2M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(2, 2, 2, AD7793_CH_AIN3P_AIN3M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(3, 3, 3, AD7795_CH_AIN4P_AIN4M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(4, 4, 4, AD7795_CH_AIN5P_AIN5M, (_b), (_sb), 0), \
+ AD_SD_DIFF_CHANNEL(5, 5, 5, AD7795_CH_AIN6P_AIN6M, (_b), (_sb), 0), \
+ AD_SD_SHORTED_CHANNEL(6, 0, AD7795_CH_AIN1M_AIN1M, (_b), (_sb), 0), \
+ AD_SD_TEMP_CHANNEL(7, AD7793_CH_TEMP, (_b), (_sb), 0), \
+ AD_SD_SUPPLY_CHANNEL(8, 3, AD7793_CH_AVDD_MONITOR, (_b), (_sb), 0), \
+ IIO_CHAN_SOFT_TIMESTAMP(9), \
+}
+
+static DECLARE_AD7793_CHANNELS(ad7785, 20, 32, 4);
+static DECLARE_AD7793_CHANNELS(ad7792, 16, 32, 0);
+static DECLARE_AD7793_CHANNELS(ad7793, 24, 32, 0);
+static DECLARE_AD7795_CHANNELS(ad7794, 16, 32);
+static DECLARE_AD7795_CHANNELS(ad7795, 24, 32);
+
static const struct ad7793_chip_info ad7793_chip_info_tbl[] = {
- [ID_AD7793] = {
- .channel[0] = {
- .type = IIO_VOLTAGE,
- .differential = 1,
- .indexed = 1,
- .channel = 0,
- .channel2 = 0,
- .address = AD7793_CH_AIN1P_AIN1M,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_index = 0,
- .scan_type = IIO_ST('u', 24, 32, 0)
- },
- .channel[1] = {
- .type = IIO_VOLTAGE,
- .differential = 1,
- .indexed = 1,
- .channel = 1,
- .channel2 = 1,
- .address = AD7793_CH_AIN2P_AIN2M,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_index = 1,
- .scan_type = IIO_ST('u', 24, 32, 0)
- },
- .channel[2] = {
- .type = IIO_VOLTAGE,
- .differential = 1,
- .indexed = 1,
- .channel = 2,
- .channel2 = 2,
- .address = AD7793_CH_AIN3P_AIN3M,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_index = 2,
- .scan_type = IIO_ST('u', 24, 32, 0)
- },
- .channel[3] = {
- .type = IIO_VOLTAGE,
- .differential = 1,
- .extend_name = "shorted",
- .indexed = 1,
- .channel = 2,
- .channel2 = 2,
- .address = AD7793_CH_AIN1M_AIN1M,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_index = 3,
- .scan_type = IIO_ST('u', 24, 32, 0)
- },
- .channel[4] = {
- .type = IIO_TEMP,
- .indexed = 1,
- .channel = 0,
- .address = AD7793_CH_TEMP,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
- .scan_index = 4,
- .scan_type = IIO_ST('u', 24, 32, 0),
- },
- .channel[5] = {
- .type = IIO_VOLTAGE,
- .extend_name = "supply",
- .indexed = 1,
- .channel = 4,
- .address = AD7793_CH_AVDD_MONITOR,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_index = 5,
- .scan_type = IIO_ST('u', 24, 32, 0),
- },
- .channel[6] = IIO_CHAN_SOFT_TIMESTAMP(6),
+ [ID_AD7785] = {
+ .channels = ad7785_channels,
+ .num_channels = ARRAY_SIZE(ad7785_channels),
},
[ID_AD7792] = {
- .channel[0] = {
- .type = IIO_VOLTAGE,
- .differential = 1,
- .indexed = 1,
- .channel = 0,
- .channel2 = 0,
- .address = AD7793_CH_AIN1P_AIN1M,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_index = 0,
- .scan_type = IIO_ST('u', 16, 32, 0)
- },
- .channel[1] = {
- .type = IIO_VOLTAGE,
- .differential = 1,
- .indexed = 1,
- .channel = 1,
- .channel2 = 1,
- .address = AD7793_CH_AIN2P_AIN2M,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_index = 1,
- .scan_type = IIO_ST('u', 16, 32, 0)
- },
- .channel[2] = {
- .type = IIO_VOLTAGE,
- .differential = 1,
- .indexed = 1,
- .channel = 2,
- .channel2 = 2,
- .address = AD7793_CH_AIN3P_AIN3M,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_index = 2,
- .scan_type = IIO_ST('u', 16, 32, 0)
- },
- .channel[3] = {
- .type = IIO_VOLTAGE,
- .differential = 1,
- .extend_name = "shorted",
- .indexed = 1,
- .channel = 2,
- .channel2 = 2,
- .address = AD7793_CH_AIN1M_AIN1M,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_index = 3,
- .scan_type = IIO_ST('u', 16, 32, 0)
- },
- .channel[4] = {
- .type = IIO_TEMP,
- .indexed = 1,
- .channel = 0,
- .address = AD7793_CH_TEMP,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
- .scan_index = 4,
- .scan_type = IIO_ST('u', 16, 32, 0),
- },
- .channel[5] = {
- .type = IIO_VOLTAGE,
- .extend_name = "supply",
- .indexed = 1,
- .channel = 4,
- .address = AD7793_CH_AVDD_MONITOR,
- .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
- IIO_CHAN_INFO_OFFSET_SHARED_BIT,
- .scan_index = 5,
- .scan_type = IIO_ST('u', 16, 32, 0),
- },
- .channel[6] = IIO_CHAN_SOFT_TIMESTAMP(6),
+ .channels = ad7792_channels,
+ .num_channels = ARRAY_SIZE(ad7792_channels),
+ },
+ [ID_AD7793] = {
+ .channels = ad7793_channels,
+ .num_channels = ARRAY_SIZE(ad7793_channels),
+ },
+ [ID_AD7794] = {
+ .channels = ad7794_channels,
+ .num_channels = ARRAY_SIZE(ad7794_channels),
+ },
+ [ID_AD7795] = {
+ .channels = ad7795_channels,
+ .num_channels = ARRAY_SIZE(ad7795_channels),
},
};
static int __devinit ad7793_probe(struct spi_device *spi)
{
- struct ad7793_platform_data *pdata = spi->dev.platform_data;
+ const struct ad7793_platform_data *pdata = spi->dev.platform_data;
struct ad7793_state *st;
struct iio_dev *indio_dev;
int ret, voltage_uv = 0;
@@ -903,6 +464,8 @@ static int __devinit ad7793_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7793_sigma_delta_info);
+
st->reg = regulator_get(&spi->dev, "vcc");
if (!IS_ERR(st->reg)) {
ret = regulator_enable(st->reg);
@@ -915,8 +478,6 @@ static int __devinit ad7793_probe(struct spi_device *spi)
st->chip_info =
&ad7793_chip_info_tbl[spi_get_device_id(spi)->driver_data];
- st->pdata = pdata;
-
if (pdata && pdata->vref_mv)
st->int_vref_mv = pdata->vref_mv;
else if (voltage_uv)
@@ -925,26 +486,19 @@ static int __devinit ad7793_probe(struct spi_device *spi)
st->int_vref_mv = 1170; /* Build-in ref */
spi_set_drvdata(spi, indio_dev);
- st->spi = spi;
indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = st->chip_info->channel;
- indio_dev->num_channels = 7;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
indio_dev->info = &ad7793_info;
- init_waitqueue_head(&st->wq_data_avail);
-
- ret = ad7793_register_ring_funcs_and_init(indio_dev);
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
if (ret)
goto error_disable_reg;
- ret = ad7793_probe_trigger(indio_dev);
- if (ret)
- goto error_unreg_ring;
-
- ret = ad7793_setup(st);
+ ret = ad7793_setup(indio_dev, pdata);
if (ret)
goto error_remove_trigger;
@@ -955,9 +509,7 @@ static int __devinit ad7793_probe(struct spi_device *spi)
return 0;
error_remove_trigger:
- ad7793_remove_trigger(indio_dev);
-error_unreg_ring:
- ad7793_ring_cleanup(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
error_disable_reg:
if (!IS_ERR(st->reg))
regulator_disable(st->reg);
@@ -970,14 +522,13 @@ error_put_reg:
return ret;
}
-static int ad7793_remove(struct spi_device *spi)
+static int __devexit ad7793_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ad7793_state *st = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
- ad7793_remove_trigger(indio_dev);
- ad7793_ring_cleanup(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
if (!IS_ERR(st->reg)) {
regulator_disable(st->reg);
@@ -990,8 +541,11 @@ static int ad7793_remove(struct spi_device *spi)
}
static const struct spi_device_id ad7793_id[] = {
+ {"ad7785", ID_AD7785},
{"ad7792", ID_AD7792},
{"ad7793", ID_AD7793},
+ {"ad7794", ID_AD7794},
+ {"ad7795", ID_AD7795},
{}
};
MODULE_DEVICE_TABLE(spi, ad7793_id);
@@ -1008,5 +562,5 @@ static struct spi_driver ad7793_driver = {
module_spi_driver(ad7793_driver);
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("Analog Devices AD7792/3 ADC");
+MODULE_DESCRIPTION("Analog Devices AD7793 and simialr ADCs");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/ad7793.h b/drivers/staging/iio/adc/ad7793.h
index 64f7d41dc453..8fdd450a2cd9 100644
--- a/drivers/staging/iio/adc/ad7793.h
+++ b/drivers/staging/iio/adc/ad7793.h
@@ -41,6 +41,7 @@
/* Mode Register Bit Designations (AD7793_REG_MODE) */
#define AD7793_MODE_SEL(x) (((x) & 0x7) << 13) /* Operation Mode Select */
+#define AD7793_MODE_SEL_MASK (0x7 << 13) /* Operation Mode Select mask */
#define AD7793_MODE_CLKSRC(x) (((x) & 0x3) << 6) /* ADC Clock Source Select */
#define AD7793_MODE_RATE(x) ((x) & 0xF) /* Filter Update Rate Select */
@@ -69,7 +70,8 @@
#define AD7793_CONF_GAIN(x) (((x) & 0x7) << 8) /* Gain Select */
#define AD7793_CONF_REFSEL (1 << 7) /* INT/EXT Reference Select */
#define AD7793_CONF_BUF (1 << 4) /* Buffered Mode Enable */
-#define AD7793_CONF_CHAN(x) ((x) & 0x7) /* Channel select */
+#define AD7793_CONF_CHAN(x) ((x) & 0xf) /* Channel select */
+#define AD7793_CONF_CHAN_MASK 0xf /* Channel select mask */
#define AD7793_CH_AIN1P_AIN1M 0 /* AIN1(+) - AIN1(-) */
#define AD7793_CH_AIN2P_AIN2M 1 /* AIN2(+) - AIN2(-) */
@@ -78,9 +80,15 @@
#define AD7793_CH_TEMP 6 /* Temp Sensor */
#define AD7793_CH_AVDD_MONITOR 7 /* AVDD Monitor */
+#define AD7795_CH_AIN4P_AIN4M 4 /* AIN4(+) - AIN4(-) */
+#define AD7795_CH_AIN5P_AIN5M 5 /* AIN5(+) - AIN5(-) */
+#define AD7795_CH_AIN6P_AIN6M 6 /* AIN6(+) - AIN6(-) */
+#define AD7795_CH_AIN1M_AIN1M 8 /* AIN1(-) - AIN1(-) */
+
/* ID Register Bit Designations (AD7793_REG_ID) */
#define AD7792_ID 0xA
#define AD7793_ID 0xB
+#define AD7795_ID 0xF
#define AD7793_ID_MASK 0xF
/* IO (Excitation Current Sources) Register Bit Designations (AD7793_REG_IO) */
diff --git a/drivers/staging/iio/adc/ad7887_core.c b/drivers/staging/iio/adc/ad7887_core.c
index 397b84947155..551790584a12 100644
--- a/drivers/staging/iio/adc/ad7887_core.c
+++ b/drivers/staging/iio/adc/ad7887_core.c
@@ -219,7 +219,7 @@ error_put_reg:
return ret;
}
-static int ad7887_remove(struct spi_device *spi)
+static int __devexit ad7887_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ad7887_state *st = iio_priv(indio_dev);
diff --git a/drivers/staging/iio/adc/ad7887_ring.c b/drivers/staging/iio/adc/ad7887_ring.c
index c76fdb5081c2..b39923bbeedc 100644
--- a/drivers/staging/iio/adc/ad7887_ring.c
+++ b/drivers/staging/iio/adc/ad7887_ring.c
@@ -95,7 +95,7 @@ static irqreturn_t ad7887_trigger_handler(int irq, void *p)
memcpy(buf + indio_dev->scan_bytes - sizeof(s64),
&time_ns, sizeof(time_ns));
- indio_dev->buffer->access->store_to(indio_dev->buffer, buf, time_ns);
+ iio_push_to_buffer(indio_dev->buffer, buf);
done:
kfree(buf);
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/staging/iio/adc/ad799x_ring.c b/drivers/staging/iio/adc/ad799x_ring.c
index 858a685e3889..86026d9b20bc 100644
--- a/drivers/staging/iio/adc/ad799x_ring.c
+++ b/drivers/staging/iio/adc/ad799x_ring.c
@@ -35,7 +35,6 @@ static irqreturn_t ad799x_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct ad799x_state *st = iio_priv(indio_dev);
- struct iio_buffer *ring = indio_dev->buffer;
s64 time_ns;
__u8 *rxbuf;
int b_sent;
@@ -78,7 +77,7 @@ static irqreturn_t ad799x_trigger_handler(int irq, void *p)
memcpy(rxbuf + indio_dev->scan_bytes - sizeof(s64),
&time_ns, sizeof(time_ns));
- ring->access->store_to(indio_dev->buffer, rxbuf, time_ns);
+ iio_push_to_buffer(indio_dev->buffer, rxbuf);
done:
kfree(rxbuf);
out:
diff --git a/drivers/staging/iio/adc/lpc32xx_adc.c b/drivers/staging/iio/adc/lpc32xx_adc.c
index 348d051fc2f8..7e9bd0001cc7 100644
--- a/drivers/staging/iio/adc/lpc32xx_adc.c
+++ b/drivers/staging/iio/adc/lpc32xx_adc.c
@@ -108,7 +108,7 @@ static const struct iio_info lpc32xx_adc_iio_info = {
.scan_index = _index, \
}
-static struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
+static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
LPC32XX_ADC_CHANNEL(0),
LPC32XX_ADC_CHANNEL(1),
LPC32XX_ADC_CHANNEL(2),
diff --git a/drivers/staging/iio/adc/max1363.h b/drivers/staging/iio/adc/max1363.h
index 2cd0112067b2..c746918683f1 100644
--- a/drivers/staging/iio/adc/max1363.h
+++ b/drivers/staging/iio/adc/max1363.h
@@ -100,7 +100,7 @@ enum max1363_modes {
*/
struct max1363_chip_info {
const struct iio_info *info;
- struct iio_chan_spec *channels;
+ const struct iio_chan_spec *channels;
int num_channels;
const enum max1363_modes *mode_list;
enum max1363_modes default_mode;
diff --git a/drivers/staging/iio/adc/max1363_core.c b/drivers/staging/iio/adc/max1363_core.c
index 6799ce23a395..d7b4ffcfa052 100644
--- a/drivers/staging/iio/adc/max1363_core.c
+++ b/drivers/staging/iio/adc/max1363_core.c
@@ -335,12 +335,12 @@ static const enum max1363_modes max1363_mode_list[] = {
IIO_CHAN_SOFT_TIMESTAMP(8) \
}
-static struct iio_chan_spec max1036_channels[] = MAX1363_4X_CHANS(8, 0);
-static struct iio_chan_spec max1136_channels[] = MAX1363_4X_CHANS(10, 0);
-static struct iio_chan_spec max1236_channels[] = MAX1363_4X_CHANS(12, 0);
-static struct iio_chan_spec max1361_channels[] =
+static const struct iio_chan_spec max1036_channels[] = MAX1363_4X_CHANS(8, 0);
+static const struct iio_chan_spec max1136_channels[] = MAX1363_4X_CHANS(10, 0);
+static const struct iio_chan_spec max1236_channels[] = MAX1363_4X_CHANS(12, 0);
+static const struct iio_chan_spec max1361_channels[] =
MAX1363_4X_CHANS(10, MAX1363_EV_M);
-static struct iio_chan_spec max1363_channels[] =
+static const struct iio_chan_spec max1363_channels[] =
MAX1363_4X_CHANS(12, MAX1363_EV_M);
/* Applies to max1236, max1237 */
@@ -392,9 +392,9 @@ static const enum max1363_modes max1238_mode_list[] = {
MAX1363_CHAN_B(11, 10, d11m10, 23, bits, 0), \
IIO_CHAN_SOFT_TIMESTAMP(24) \
}
-static struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8);
-static struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10);
-static struct iio_chan_spec max1238_channels[] = MAX1363_12X_CHANS(12);
+static const struct iio_chan_spec max1038_channels[] = MAX1363_12X_CHANS(8);
+static const struct iio_chan_spec max1138_channels[] = MAX1363_12X_CHANS(10);
+static const struct iio_chan_spec max1238_channels[] = MAX1363_12X_CHANS(12);
static const enum max1363_modes max11607_mode_list[] = {
_s0, _s1, _s2, _s3,
@@ -433,9 +433,9 @@ static const enum max1363_modes max11608_mode_list[] = {
MAX1363_CHAN_B(7, 6, d7m6, 15, bits, 0), \
IIO_CHAN_SOFT_TIMESTAMP(16) \
}
-static struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8);
-static struct iio_chan_spec max11608_channels[] = MAX1363_8X_CHANS(10);
-static struct iio_chan_spec max11614_channels[] = MAX1363_8X_CHANS(12);
+static const struct iio_chan_spec max11602_channels[] = MAX1363_8X_CHANS(8);
+static const struct iio_chan_spec max11608_channels[] = MAX1363_8X_CHANS(10);
+static const struct iio_chan_spec max11614_channels[] = MAX1363_8X_CHANS(12);
static const enum max1363_modes max11644_mode_list[] = {
_s0, _s1, s0to1, d0m1, d1m0,
@@ -449,8 +449,8 @@ static const enum max1363_modes max11644_mode_list[] = {
IIO_CHAN_SOFT_TIMESTAMP(4) \
}
-static struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10);
-static struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12);
+static const struct iio_chan_spec max11646_channels[] = MAX1363_2X_CHANS(10);
+static const struct iio_chan_spec max11644_channels[] = MAX1363_2X_CHANS(12);
enum { max1361,
max1362,
@@ -1367,7 +1367,7 @@ error_out:
return ret;
}
-static int max1363_remove(struct i2c_client *client)
+static int __devexit max1363_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
struct max1363_state *st = iio_priv(indio_dev);
@@ -1434,11 +1434,11 @@ static struct i2c_driver max1363_driver = {
.name = "max1363",
},
.probe = max1363_probe,
- .remove = max1363_remove,
+ .remove = __devexit_p(max1363_remove),
.id_table = max1363_id,
};
module_i2c_driver(max1363_driver);
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Maxim 1363 ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/max1363_ring.c b/drivers/staging/iio/adc/max1363_ring.c
index 774ae1b63550..5f74f3b7671a 100644
--- a/drivers/staging/iio/adc/max1363_ring.c
+++ b/drivers/staging/iio/adc/max1363_ring.c
@@ -80,7 +80,7 @@ static irqreturn_t max1363_trigger_handler(int irq, void *p)
if (indio_dev->scan_timestamp)
memcpy(rxbuf + d_size - sizeof(s64), &time_ns, sizeof(time_ns));
- iio_push_to_buffer(indio_dev->buffer, rxbuf, time_ns);
+ iio_push_to_buffer(indio_dev->buffer, rxbuf);
done_free:
kfree(rxbuf);
diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c
new file mode 100644
index 000000000000..ca7c1fa88e71
--- /dev/null
+++ b/drivers/staging/iio/adc/mxs-lradc.c
@@ -0,0 +1,590 @@
+/*
+ * Freescale i.MX28 LRADC driver
+ *
+ * Copyright (c) 2012 DENX Software Engineering, GmbH.
+ * Marek Vasut <marex@denx.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/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/sysfs.h>
+#include <linux/list.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/stmp_device.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+
+#include <mach/mxs.h>
+#include <mach/common.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define DRIVER_NAME "mxs-lradc"
+
+#define LRADC_MAX_DELAY_CHANS 4
+#define LRADC_MAX_MAPPED_CHANS 8
+#define LRADC_MAX_TOTAL_CHANS 16
+
+#define LRADC_DELAY_TIMER_HZ 2000
+
+/*
+ * Make this runtime configurable if necessary. Currently, if the buffered mode
+ * is enabled, the LRADC takes LRADC_DELAY_TIMER_LOOP samples of data before
+ * triggering IRQ. The sampling happens every (LRADC_DELAY_TIMER_PER / 2000)
+ * seconds. The result is that the samples arrive every 500mS.
+ */
+#define LRADC_DELAY_TIMER_PER 200
+#define LRADC_DELAY_TIMER_LOOP 5
+
+static const char * const mxs_lradc_irq_name[] = {
+ "mxs-lradc-touchscreen",
+ "mxs-lradc-thresh0",
+ "mxs-lradc-thresh1",
+ "mxs-lradc-channel0",
+ "mxs-lradc-channel1",
+ "mxs-lradc-channel2",
+ "mxs-lradc-channel3",
+ "mxs-lradc-channel4",
+ "mxs-lradc-channel5",
+ "mxs-lradc-channel6",
+ "mxs-lradc-channel7",
+ "mxs-lradc-button0",
+ "mxs-lradc-button1",
+};
+
+struct mxs_lradc_chan {
+ uint8_t slot;
+ uint8_t flags;
+};
+
+struct mxs_lradc {
+ struct device *dev;
+ void __iomem *base;
+ int irq[13];
+
+ uint32_t *buffer;
+ struct iio_trigger *trig;
+
+ struct mutex lock;
+
+ uint8_t enable;
+
+ struct completion completion;
+};
+
+#define LRADC_CTRL0 0x00
+#define LRADC_CTRL0_TOUCH_DETECT_ENABLE (1 << 23)
+#define LRADC_CTRL0_TOUCH_SCREEN_TYPE (1 << 22)
+
+#define LRADC_CTRL1 0x10
+#define LRADC_CTRL1_LRADC_IRQ(n) (1 << (n))
+#define LRADC_CTRL1_LRADC_IRQ_MASK 0x1fff
+#define LRADC_CTRL1_LRADC_IRQ_EN(n) (1 << ((n) + 16))
+#define LRADC_CTRL1_LRADC_IRQ_EN_MASK (0x1fff << 16)
+
+#define LRADC_CTRL2 0x20
+#define LRADC_CTRL2_TEMPSENSE_PWD (1 << 15)
+
+#define LRADC_CH(n) (0x50 + (0x10 * (n)))
+#define LRADC_CH_ACCUMULATE (1 << 29)
+#define LRADC_CH_NUM_SAMPLES_MASK (0x1f << 24)
+#define LRADC_CH_NUM_SAMPLES_OFFSET 24
+#define LRADC_CH_VALUE_MASK 0x3ffff
+#define LRADC_CH_VALUE_OFFSET 0
+
+#define LRADC_DELAY(n) (0xd0 + (0x10 * (n)))
+#define LRADC_DELAY_TRIGGER_LRADCS_MASK (0xff << 24)
+#define LRADC_DELAY_TRIGGER_LRADCS_OFFSET 24
+#define LRADC_DELAY_KICK (1 << 20)
+#define LRADC_DELAY_TRIGGER_DELAYS_MASK (0xf << 16)
+#define LRADC_DELAY_TRIGGER_DELAYS_OFFSET 16
+#define LRADC_DELAY_LOOP_COUNT_MASK (0x1f << 11)
+#define LRADC_DELAY_LOOP_COUNT_OFFSET 11
+#define LRADC_DELAY_DELAY_MASK 0x7ff
+#define LRADC_DELAY_DELAY_OFFSET 0
+
+#define LRADC_CTRL4 0x140
+#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4))
+#define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4)
+
+/*
+ * Raw I/O operations
+ */
+static int mxs_lradc_read_raw(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long m)
+{
+ struct mxs_lradc *lradc = iio_priv(iio_dev);
+ int ret;
+
+ if (m != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ /* Check for invalid channel */
+ if (chan->channel > LRADC_MAX_TOTAL_CHANS)
+ return -EINVAL;
+
+ /*
+ * See if there is no buffered operation in progess. If there is, simply
+ * bail out. This can be improved to support both buffered and raw IO at
+ * the same time, yet the code becomes horribly complicated. Therefore I
+ * applied KISS principle here.
+ */
+ ret = mutex_trylock(&lradc->lock);
+ if (!ret)
+ return -EBUSY;
+
+ INIT_COMPLETION(lradc->completion);
+
+ /*
+ * No buffered operation in progress, map the channel and trigger it.
+ * Virtual channel 0 is always used here as the others are always not
+ * used if doing raw sampling.
+ */
+ writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+ writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+
+ writel(chan->channel, lradc->base + LRADC_CTRL4);
+ writel(0, lradc->base + LRADC_CH(0));
+
+ /* Enable the IRQ and start sampling the channel. */
+ writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
+ writel(1 << 0, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_SET);
+
+ /* Wait for completion on the channel, 1 second max. */
+ ret = wait_for_completion_killable_timeout(&lradc->completion, HZ);
+ if (!ret)
+ ret = -ETIMEDOUT;
+ if (ret < 0)
+ goto err;
+
+ /* Read the data. */
+ *val = readl(lradc->base + LRADC_CH(0)) & LRADC_CH_VALUE_MASK;
+ ret = IIO_VAL_INT;
+
+err:
+ writel(LRADC_CTRL1_LRADC_IRQ_EN(0),
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ mutex_unlock(&lradc->lock);
+
+ return ret;
+}
+
+static const struct iio_info mxs_lradc_iio_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = mxs_lradc_read_raw,
+};
+
+/*
+ * IRQ Handling
+ */
+static irqreturn_t mxs_lradc_handle_irq(int irq, void *data)
+{
+ struct iio_dev *iio = data;
+ struct mxs_lradc *lradc = iio_priv(iio);
+ unsigned long reg = readl(lradc->base + LRADC_CTRL1);
+
+ if (!(reg & LRADC_CTRL1_LRADC_IRQ_MASK))
+ return IRQ_NONE;
+
+ /*
+ * Touchscreen IRQ handling code shall probably have priority
+ * and therefore shall be placed here.
+ */
+
+ if (iio_buffer_enabled(iio))
+ iio_trigger_poll(iio->trig, iio_get_time_ns());
+ else if (reg & LRADC_CTRL1_LRADC_IRQ(0))
+ complete(&lradc->completion);
+
+ writel(reg & LRADC_CTRL1_LRADC_IRQ_MASK,
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Trigger handling
+ */
+static irqreturn_t mxs_lradc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *iio = pf->indio_dev;
+ struct mxs_lradc *lradc = iio_priv(iio);
+ struct iio_buffer *buffer = iio->buffer;
+ const uint32_t chan_value = LRADC_CH_ACCUMULATE |
+ ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
+ int i, j = 0;
+
+ for_each_set_bit(i, iio->active_scan_mask, iio->masklength) {
+ lradc->buffer[j] = readl(lradc->base + LRADC_CH(j));
+ writel(chan_value, lradc->base + LRADC_CH(j));
+ lradc->buffer[j] &= LRADC_CH_VALUE_MASK;
+ lradc->buffer[j] /= LRADC_DELAY_TIMER_LOOP;
+ j++;
+ }
+
+ if (iio->scan_timestamp) {
+ s64 *timestamp = (s64 *)((u8 *)lradc->buffer +
+ ALIGN(j, sizeof(s64)));
+ *timestamp = pf->timestamp;
+ }
+
+ iio_push_to_buffer(buffer, (u8 *)lradc->buffer);
+
+ iio_trigger_notify_done(iio->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int mxs_lradc_configure_trigger(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *iio = trig->private_data;
+ struct mxs_lradc *lradc = iio_priv(iio);
+ const uint32_t st = state ? STMP_OFFSET_REG_SET : STMP_OFFSET_REG_CLR;
+
+ writel(LRADC_DELAY_KICK, lradc->base + LRADC_DELAY(0) + st);
+
+ return 0;
+}
+
+static const struct iio_trigger_ops mxs_lradc_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = &mxs_lradc_configure_trigger,
+};
+
+static int mxs_lradc_trigger_init(struct iio_dev *iio)
+{
+ int ret;
+ struct iio_trigger *trig;
+
+ trig = iio_trigger_alloc("%s-dev%i", iio->name, iio->id);
+ if (trig == NULL)
+ return -ENOMEM;
+
+ trig->dev.parent = iio->dev.parent;
+ trig->private_data = iio;
+ trig->ops = &mxs_lradc_trigger_ops;
+
+ ret = iio_trigger_register(trig);
+ if (ret) {
+ iio_trigger_free(trig);
+ return ret;
+ }
+
+ iio->trig = trig;
+
+ return 0;
+}
+
+static void mxs_lradc_trigger_remove(struct iio_dev *iio)
+{
+ iio_trigger_unregister(iio->trig);
+ iio_trigger_free(iio->trig);
+}
+
+static int mxs_lradc_buffer_preenable(struct iio_dev *iio)
+{
+ struct mxs_lradc *lradc = iio_priv(iio);
+ struct iio_buffer *buffer = iio->buffer;
+ int ret = 0, chan, ofs = 0, enable = 0;
+ uint32_t ctrl4 = 0;
+ uint32_t ctrl1_irq = 0;
+ const uint32_t chan_value = LRADC_CH_ACCUMULATE |
+ ((LRADC_DELAY_TIMER_LOOP - 1) << LRADC_CH_NUM_SAMPLES_OFFSET);
+ const int len = bitmap_weight(buffer->scan_mask, LRADC_MAX_TOTAL_CHANS);
+
+ if (!len)
+ return -EINVAL;
+
+ /*
+ * Lock the driver so raw access can not be done during buffered
+ * operation. This simplifies the code a lot.
+ */
+ ret = mutex_trylock(&lradc->lock);
+ if (!ret)
+ return -EBUSY;
+
+ lradc->buffer = kmalloc(len * sizeof(*lradc->buffer), GFP_KERNEL);
+ if (!lradc->buffer) {
+ ret = -ENOMEM;
+ goto err_mem;
+ }
+
+ ret = iio_sw_buffer_preenable(iio);
+ if (ret < 0)
+ goto err_buf;
+
+ writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+ writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+
+ for_each_set_bit(chan, buffer->scan_mask, LRADC_MAX_TOTAL_CHANS) {
+ ctrl4 |= chan << LRADC_CTRL4_LRADCSELECT_OFFSET(ofs);
+ ctrl1_irq |= LRADC_CTRL1_LRADC_IRQ_EN(ofs);
+ writel(chan_value, lradc->base + LRADC_CH(ofs));
+ enable |= 1 << ofs;
+ ofs++;
+ };
+
+ writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
+ lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
+
+ writel(ctrl4, lradc->base + LRADC_CTRL4);
+ writel(ctrl1_irq, lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_SET);
+
+ writel(enable << LRADC_DELAY_TRIGGER_LRADCS_OFFSET,
+ lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_SET);
+
+ return 0;
+
+err_buf:
+ kfree(lradc->buffer);
+err_mem:
+ mutex_unlock(&lradc->lock);
+ return ret;
+}
+
+static int mxs_lradc_buffer_postdisable(struct iio_dev *iio)
+{
+ struct mxs_lradc *lradc = iio_priv(iio);
+
+ writel(LRADC_DELAY_TRIGGER_LRADCS_MASK | LRADC_DELAY_KICK,
+ lradc->base + LRADC_DELAY(0) + STMP_OFFSET_REG_CLR);
+
+ writel(0xff, lradc->base + LRADC_CTRL0 + STMP_OFFSET_REG_CLR);
+ writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ kfree(lradc->buffer);
+ mutex_unlock(&lradc->lock);
+
+ return 0;
+}
+
+static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio,
+ const unsigned long *mask)
+{
+ const int mw = bitmap_weight(mask, iio->masklength);
+
+ return mw <= LRADC_MAX_MAPPED_CHANS;
+}
+
+static const struct iio_buffer_setup_ops mxs_lradc_buffer_ops = {
+ .preenable = &mxs_lradc_buffer_preenable,
+ .postenable = &iio_triggered_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+ .postdisable = &mxs_lradc_buffer_postdisable,
+ .validate_scan_mask = &mxs_lradc_validate_scan_mask,
+};
+
+/*
+ * Driver initialization
+ */
+
+#define MXS_ADC_CHAN(idx, chan_type) { \
+ .type = (chan_type), \
+ .indexed = 1, \
+ .scan_index = (idx), \
+ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT, \
+ .channel = (idx), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 18, \
+ .storagebits = 32, \
+ }, \
+}
+
+static const struct iio_chan_spec mxs_lradc_chan_spec[] = {
+ MXS_ADC_CHAN(0, IIO_VOLTAGE),
+ MXS_ADC_CHAN(1, IIO_VOLTAGE),
+ MXS_ADC_CHAN(2, IIO_VOLTAGE),
+ MXS_ADC_CHAN(3, IIO_VOLTAGE),
+ MXS_ADC_CHAN(4, IIO_VOLTAGE),
+ MXS_ADC_CHAN(5, IIO_VOLTAGE),
+ MXS_ADC_CHAN(6, IIO_VOLTAGE),
+ MXS_ADC_CHAN(7, IIO_VOLTAGE), /* VBATT */
+ MXS_ADC_CHAN(8, IIO_TEMP), /* Temp sense 0 */
+ MXS_ADC_CHAN(9, IIO_TEMP), /* Temp sense 1 */
+ MXS_ADC_CHAN(10, IIO_VOLTAGE), /* VDDIO */
+ MXS_ADC_CHAN(11, IIO_VOLTAGE), /* VTH */
+ MXS_ADC_CHAN(12, IIO_VOLTAGE), /* VDDA */
+ MXS_ADC_CHAN(13, IIO_VOLTAGE), /* VDDD */
+ MXS_ADC_CHAN(14, IIO_VOLTAGE), /* VBG */
+ MXS_ADC_CHAN(15, IIO_VOLTAGE), /* VDD5V */
+};
+
+static void mxs_lradc_hw_init(struct mxs_lradc *lradc)
+{
+ int i;
+ const uint32_t cfg =
+ (LRADC_DELAY_TIMER_PER << LRADC_DELAY_DELAY_OFFSET);
+
+ stmp_reset_block(lradc->base);
+
+ for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
+ writel(cfg | (1 << (LRADC_DELAY_TRIGGER_DELAYS_OFFSET + i)),
+ lradc->base + LRADC_DELAY(i));
+
+ /* Start internal temperature sensing. */
+ writel(0, lradc->base + LRADC_CTRL2);
+}
+
+static void mxs_lradc_hw_stop(struct mxs_lradc *lradc)
+{
+ int i;
+
+ writel(LRADC_CTRL1_LRADC_IRQ_EN_MASK,
+ lradc->base + LRADC_CTRL1 + STMP_OFFSET_REG_CLR);
+
+ for (i = 0; i < LRADC_MAX_DELAY_CHANS; i++)
+ writel(0, lradc->base + LRADC_DELAY(i));
+}
+
+static int __devinit mxs_lradc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mxs_lradc *lradc;
+ struct iio_dev *iio;
+ struct resource *iores;
+ int ret = 0;
+ int i;
+
+ /* Allocate the IIO device. */
+ iio = iio_device_alloc(sizeof(*lradc));
+ if (!iio) {
+ dev_err(dev, "Failed to allocate IIO device\n");
+ return -ENOMEM;
+ }
+
+ lradc = iio_priv(iio);
+
+ /* Grab the memory area */
+ iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ lradc->dev = &pdev->dev;
+ lradc->base = devm_request_and_ioremap(dev, iores);
+ if (!lradc->base) {
+ ret = -EADDRNOTAVAIL;
+ goto err_addr;
+ }
+
+ /* Grab all IRQ sources */
+ for (i = 0; i < 13; i++) {
+ lradc->irq[i] = platform_get_irq(pdev, i);
+ if (lradc->irq[i] < 0) {
+ ret = -EINVAL;
+ goto err_addr;
+ }
+
+ ret = devm_request_irq(dev, lradc->irq[i],
+ mxs_lradc_handle_irq, 0,
+ mxs_lradc_irq_name[i], iio);
+ if (ret)
+ goto err_addr;
+ }
+
+ platform_set_drvdata(pdev, iio);
+
+ init_completion(&lradc->completion);
+ mutex_init(&lradc->lock);
+
+ iio->name = pdev->name;
+ iio->dev.parent = &pdev->dev;
+ iio->info = &mxs_lradc_iio_info;
+ iio->modes = INDIO_DIRECT_MODE;
+ iio->channels = mxs_lradc_chan_spec;
+ iio->num_channels = ARRAY_SIZE(mxs_lradc_chan_spec);
+
+ ret = iio_triggered_buffer_setup(iio, &iio_pollfunc_store_time,
+ &mxs_lradc_trigger_handler,
+ &mxs_lradc_buffer_ops);
+ if (ret)
+ goto err_addr;
+
+ ret = mxs_lradc_trigger_init(iio);
+ if (ret)
+ goto err_trig;
+
+ /* Register IIO device. */
+ ret = iio_device_register(iio);
+ if (ret) {
+ dev_err(dev, "Failed to register IIO device\n");
+ goto err_dev;
+ }
+
+ /* Configure the hardware. */
+ mxs_lradc_hw_init(lradc);
+
+ return 0;
+
+err_dev:
+ mxs_lradc_trigger_remove(iio);
+err_trig:
+ iio_triggered_buffer_cleanup(iio);
+err_addr:
+ iio_device_free(iio);
+ return ret;
+}
+
+static int __devexit mxs_lradc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *iio = platform_get_drvdata(pdev);
+ struct mxs_lradc *lradc = iio_priv(iio);
+
+ mxs_lradc_hw_stop(lradc);
+
+ iio_device_unregister(iio);
+ iio_triggered_buffer_cleanup(iio);
+ mxs_lradc_trigger_remove(iio);
+ iio_device_free(iio);
+
+ return 0;
+}
+
+static const struct of_device_id mxs_lradc_dt_ids[] = {
+ { .compatible = "fsl,imx28-lradc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_lradc_dt_ids);
+
+static struct platform_driver mxs_lradc_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mxs_lradc_dt_ids,
+ },
+ .probe = mxs_lradc_probe,
+ .remove = __devexit_p(mxs_lradc_remove),
+};
+
+module_platform_driver(mxs_lradc_driver);
+
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_DESCRIPTION("Freescale i.MX28 LRADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/adc/spear_adc.c b/drivers/staging/iio/adc/spear_adc.c
index 64d630e6fe29..0b83e2e1f410 100644
--- a/drivers/staging/iio/adc/spear_adc.c
+++ b/drivers/staging/iio/adc/spear_adc.c
@@ -189,7 +189,7 @@ static int spear_read_raw(struct iio_dev *indio_dev,
}, \
}
-static struct iio_chan_spec spear_adc_iio_channels[] = {
+static const struct iio_chan_spec spear_adc_iio_channels[] = {
SPEAR_ADC_CHAN(0),
SPEAR_ADC_CHAN(1),
SPEAR_ADC_CHAN(2),
@@ -330,36 +330,30 @@ static int __devinit spear_adc_probe(struct platform_device *pdev)
goto errout3;
}
- ret = clk_prepare(info->clk);
- if (ret) {
- dev_err(dev, "failed preparing clock\n");
- goto errout4;
- }
-
- ret = clk_enable(info->clk);
+ ret = clk_prepare_enable(info->clk);
if (ret) {
dev_err(dev, "failed enabling clock\n");
- goto errout5;
+ goto errout4;
}
irq = platform_get_irq(pdev, 0);
if ((irq < 0) || (irq >= NR_IRQS)) {
dev_err(dev, "failed getting interrupt resource\n");
ret = -EINVAL;
- goto errout6;
+ goto errout5;
}
ret = devm_request_irq(dev, irq, spear_adc_isr, 0, MOD_NAME, info);
if (ret < 0) {
dev_err(dev, "failed requesting interrupt\n");
- goto errout6;
+ goto errout5;
}
if (of_property_read_u32(np, "sampling-frequency",
&info->sampling_freq)) {
dev_err(dev, "sampling-frequency missing in DT\n");
ret = -EINVAL;
- goto errout6;
+ goto errout5;
}
/*
@@ -389,16 +383,14 @@ static int __devinit spear_adc_probe(struct platform_device *pdev)
ret = iio_device_register(iodev);
if (ret)
- goto errout6;
+ goto errout5;
dev_info(dev, "SPEAR ADC driver loaded, IRQ %d\n", irq);
return 0;
-errout6:
- clk_disable(info->clk);
errout5:
- clk_unprepare(info->clk);
+ clk_disable_unprepare(info->clk);
errout4:
clk_put(info->clk);
errout3:
@@ -416,8 +408,7 @@ static int __devexit spear_adc_remove(struct platform_device *pdev)
iio_device_unregister(iodev);
platform_set_drvdata(pdev, NULL);
- clk_disable(info->clk);
- clk_unprepare(info->clk);
+ clk_disable_unprepare(info->clk);
clk_put(info->clk);
iounmap(info->adc_base_spear6xx);
iio_device_free(iodev);
diff --git a/drivers/staging/iio/gyro/adis16060_core.c b/drivers/staging/iio/gyro/adis16060_core.c
index 9931e2060e1f..87151a7cff04 100644
--- a/drivers/staging/iio/gyro/adis16060_core.c
+++ b/drivers/staging/iio/gyro/adis16060_core.c
@@ -184,7 +184,7 @@ error_ret:
}
/* fixme, confirm ordering in this function */
-static int adis16060_r_remove(struct spi_device *spi)
+static int __devexit adis16060_r_remove(struct spi_device *spi)
{
iio_device_unregister(spi_get_drvdata(spi));
iio_device_free(spi_get_drvdata(spi));
@@ -210,7 +210,7 @@ error_ret:
return ret;
}
-static int adis16060_w_remove(struct spi_device *spi)
+static int __devexit adis16060_w_remove(struct spi_device *spi)
{
return 0;
}
diff --git a/drivers/staging/iio/gyro/adis16080_core.c b/drivers/staging/iio/gyro/adis16080_core.c
index 345e4fa778ba..a73902573f79 100644
--- a/drivers/staging/iio/gyro/adis16080_core.c
+++ b/drivers/staging/iio/gyro/adis16080_core.c
@@ -177,7 +177,7 @@ error_ret:
}
/* fixme, confirm ordering in this function */
-static int adis16080_remove(struct spi_device *spi)
+static int __devexit adis16080_remove(struct spi_device *spi)
{
iio_device_unregister(spi_get_drvdata(spi));
iio_device_free(spi_get_drvdata(spi));
diff --git a/drivers/staging/iio/gyro/adis16130_core.c b/drivers/staging/iio/gyro/adis16130_core.c
index bf61cd0b5bbc..fbf96b0b6ee8 100644
--- a/drivers/staging/iio/gyro/adis16130_core.c
+++ b/drivers/staging/iio/gyro/adis16130_core.c
@@ -154,7 +154,7 @@ error_ret:
}
/* fixme, confirm ordering in this function */
-static int adis16130_remove(struct spi_device *spi)
+static int __devexit adis16130_remove(struct spi_device *spi)
{
iio_device_unregister(spi_get_drvdata(spi));
iio_device_free(spi_get_drvdata(spi));
diff --git a/drivers/staging/iio/gyro/adis16260_core.c b/drivers/staging/iio/gyro/adis16260_core.c
index eb8e9d69efd3..9571c03aa4cc 100644
--- a/drivers/staging/iio/gyro/adis16260_core.c
+++ b/drivers/staging/iio/gyro/adis16260_core.c
@@ -700,24 +700,18 @@ error_ret:
return ret;
}
-static int adis16260_remove(struct spi_device *spi)
+static int __devexit adis16260_remove(struct spi_device *spi)
{
- int ret;
struct iio_dev *indio_dev = spi_get_drvdata(spi);
iio_device_unregister(indio_dev);
-
- ret = adis16260_stop_device(indio_dev);
- if (ret)
- goto err_ret;
-
+ adis16260_stop_device(indio_dev);
adis16260_remove_trigger(indio_dev);
iio_buffer_unregister(indio_dev);
adis16260_unconfigure_ring(indio_dev);
iio_device_free(indio_dev);
-err_ret:
- return ret;
+ return 0;
}
/*
diff --git a/drivers/staging/iio/gyro/adis16260_ring.c b/drivers/staging/iio/gyro/adis16260_ring.c
index eeee8e760e6c..e294cb49736d 100644
--- a/drivers/staging/iio/gyro/adis16260_ring.c
+++ b/drivers/staging/iio/gyro/adis16260_ring.c
@@ -62,7 +62,6 @@ static irqreturn_t adis16260_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct adis16260_state *st = iio_priv(indio_dev);
- struct iio_buffer *ring = indio_dev->buffer;
int i = 0;
s16 *data;
@@ -82,7 +81,7 @@ static irqreturn_t adis16260_trigger_handler(int irq, void *p)
if (indio_dev->scan_timestamp)
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
- ring->access->store_to(ring, (u8 *)data, pf->timestamp);
+ iio_push_to_buffer(indio_dev->buffer, (u8 *)data);
kfree(data);
done:
diff --git a/drivers/staging/iio/gyro/adxrs450_core.c b/drivers/staging/iio/gyro/adxrs450_core.c
index 6513119b1e90..d93527d15917 100644
--- a/drivers/staging/iio/gyro/adxrs450_core.c
+++ b/drivers/staging/iio/gyro/adxrs450_core.c
@@ -409,7 +409,7 @@ error_ret:
return ret;
}
-static int adxrs450_remove(struct spi_device *spi)
+static int __devexit adxrs450_remove(struct spi_device *spi)
{
iio_device_unregister(spi_get_drvdata(spi));
iio_device_free(spi_get_drvdata(spi));
diff --git a/drivers/staging/iio/iio_dummy_evgen.c b/drivers/staging/iio/iio_dummy_evgen.c
index 0cd4fe916bf9..74e24e8aa876 100644
--- a/drivers/staging/iio/iio_dummy_evgen.c
+++ b/drivers/staging/iio/iio_dummy_evgen.c
@@ -216,6 +216,6 @@ static __exit void iio_dummy_evgen_exit(void)
}
module_exit(iio_dummy_evgen_exit);
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("IIO dummy driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/iio_hwmon.c b/drivers/staging/iio/iio_hwmon.c
index 27d27ec9521f..5d491227e01b 100644
--- a/drivers/staging/iio/iio_hwmon.c
+++ b/drivers/staging/iio/iio_hwmon.c
@@ -42,40 +42,17 @@ static ssize_t iio_hwmon_read_val(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- long result;
- int val, ret, scaleint, scalepart;
+ int result;
+ int ret;
struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr);
struct iio_hwmon_state *state = dev_get_drvdata(dev);
- /*
- * No locking between this pair, so theoretically possible
- * the scale has changed.
- */
- ret = iio_read_channel_raw(&state->channels[sattr->index],
- &val);
+ ret = iio_read_channel_processed(&state->channels[sattr->index],
+ &result);
if (ret < 0)
return ret;
- ret = iio_read_channel_scale(&state->channels[sattr->index],
- &scaleint, &scalepart);
- if (ret < 0)
- return ret;
- switch (ret) {
- case IIO_VAL_INT:
- result = val * scaleint;
- break;
- case IIO_VAL_INT_PLUS_MICRO:
- result = (s64)val * (s64)scaleint +
- div_s64((s64)val * (s64)scalepart, 1000000LL);
- break;
- case IIO_VAL_INT_PLUS_NANO:
- result = (s64)val * (s64)scaleint +
- div_s64((s64)val * (s64)scalepart, 1000000000LL);
- break;
- default:
- return -EINVAL;
- }
- return sprintf(buf, "%ld\n", result);
+ return sprintf(buf, "%d\n", result);
}
static void iio_hwmon_free_attrs(struct iio_hwmon_state *st)
@@ -215,18 +192,8 @@ static struct platform_driver __refdata iio_hwmon_driver = {
.remove = __devexit_p(iio_hwmon_remove),
};
-static int iio_inkern_init(void)
-{
- return platform_driver_register(&iio_hwmon_driver);
-}
-module_init(iio_inkern_init);
-
-static void iio_inkern_exit(void)
-{
- platform_driver_unregister(&iio_hwmon_driver);
-}
-module_exit(iio_inkern_exit);
+module_platform_driver(iio_hwmon_driver);
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("IIO to hwmon driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/iio_simple_dummy.c b/drivers/staging/iio/iio_simple_dummy.c
index 155a49a9da7e..dc6c728ea47a 100644
--- a/drivers/staging/iio/iio_simple_dummy.c
+++ b/drivers/staging/iio/iio_simple_dummy.c
@@ -63,7 +63,7 @@ static const struct iio_dummy_accel_calibscale dummy_scales[] = {
* This array of structures tells the IIO core about what the device
* actually provides for a given channel.
*/
-static struct iio_chan_spec iio_dummy_channels[] = {
+static const struct iio_chan_spec iio_dummy_channels[] = {
/* indexed ADC channel in_voltage0_raw etc */
{
.type = IIO_VOLTAGE,
@@ -445,26 +445,20 @@ static int __devinit iio_dummy_probe(int index)
if (ret < 0)
goto error_free_device;
- /* Configure buffered capture support. */
- ret = iio_simple_dummy_configure_buffer(indio_dev);
- if (ret < 0)
- goto error_unregister_events;
-
/*
- * Register the channels with the buffer, but avoid the output
- * channel being registered by reducing the number of channels by 1.
+ * Configure buffered capture support and register the channels with the
+ * buffer, but avoid the output channel being registered by reducing the
+ * number of channels by 1.
*/
- ret = iio_buffer_register(indio_dev, iio_dummy_channels, 5);
+ ret = iio_simple_dummy_configure_buffer(indio_dev, iio_dummy_channels, 5);
if (ret < 0)
- goto error_unconfigure_buffer;
+ goto error_unregister_events;
ret = iio_device_register(indio_dev);
if (ret < 0)
- goto error_unregister_buffer;
+ goto error_unconfigure_buffer;
return 0;
-error_unregister_buffer:
- iio_buffer_unregister(indio_dev);
error_unconfigure_buffer:
iio_simple_dummy_unconfigure_buffer(indio_dev);
error_unregister_events:
@@ -499,7 +493,6 @@ static int iio_dummy_remove(int index)
/* Device specific code to power down etc */
/* Buffered capture related cleanup */
- iio_buffer_unregister(indio_dev);
iio_simple_dummy_unconfigure_buffer(indio_dev);
ret = iio_simple_dummy_events_unregister(indio_dev);
@@ -530,6 +523,7 @@ static __init int iio_dummy_init(void)
instances = 1;
return -EINVAL;
}
+
/* Fake a bus */
iio_dummy_devs = kcalloc(instances, sizeof(*iio_dummy_devs),
GFP_KERNEL);
@@ -558,6 +552,6 @@ static __exit void iio_dummy_exit(void)
}
module_exit(iio_dummy_exit);
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("IIO dummy driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/iio_simple_dummy.h b/drivers/staging/iio/iio_simple_dummy.h
index 53975d916fc9..c9e8702caca4 100644
--- a/drivers/staging/iio/iio_simple_dummy.h
+++ b/drivers/staging/iio/iio_simple_dummy.h
@@ -95,10 +95,12 @@ enum iio_simple_dummy_scan_elements {
};
#ifdef CONFIG_IIO_SIMPLE_DUMMY_BUFFER
-int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev);
+int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *channels, unsigned int num_channels);
void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev);
#else
-static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
+static inline int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *channels, unsigned int num_channels)
{
return 0;
};
diff --git a/drivers/staging/iio/iio_simple_dummy_buffer.c b/drivers/staging/iio/iio_simple_dummy_buffer.c
index bd628de472a9..697d9700db2f 100644
--- a/drivers/staging/iio/iio_simple_dummy_buffer.c
+++ b/drivers/staging/iio/iio_simple_dummy_buffer.c
@@ -87,7 +87,7 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p)
if (indio_dev->scan_timestamp)
*(s64 *)((u8 *)data + ALIGN(len, sizeof(s64)))
= iio_get_time_ns();
- buffer->access->store_to(buffer, (u8 *)data, pf->timestamp);
+ iio_push_to_buffer(buffer, (u8 *)data);
kfree(data);
@@ -126,7 +126,8 @@ static const struct iio_buffer_setup_ops iio_simple_dummy_buffer_setup_ops = {
.predisable = &iio_triggered_buffer_predisable,
};
-int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
+int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *channels, unsigned int num_channels)
{
int ret;
struct iio_buffer *buffer;
@@ -182,8 +183,15 @@ int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev)
* driven by a trigger.
*/
indio_dev->modes |= INDIO_BUFFER_TRIGGERED;
+
+ ret = iio_buffer_register(indio_dev, channels, num_channels);
+ if (ret)
+ goto error_dealloc_pollfunc;
+
return 0;
+error_dealloc_pollfunc:
+ iio_dealloc_pollfunc(indio_dev->pollfunc);
error_free_buffer:
iio_kfifo_free(indio_dev->buffer);
error_ret:
@@ -197,6 +205,7 @@ error_ret:
*/
void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev)
{
+ iio_buffer_unregister(indio_dev);
iio_dealloc_pollfunc(indio_dev->pollfunc);
iio_kfifo_free(indio_dev->buffer);
}
diff --git a/drivers/staging/iio/impedance-analyzer/ad5933.c b/drivers/staging/iio/impedance-analyzer/ad5933.c
index a8e51bc04439..de21d47f33e9 100644
--- a/drivers/staging/iio/impedance-analyzer/ad5933.c
+++ b/drivers/staging/iio/impedance-analyzer/ad5933.c
@@ -108,7 +108,7 @@ static struct ad5933_platform_data ad5933_default_pdata = {
.vref_mv = 3300,
};
-static struct iio_chan_spec ad5933_channels[] = {
+static const struct iio_chan_spec ad5933_channels[] = {
{
.type = IIO_TEMP,
.indexed = 1,
@@ -678,7 +678,7 @@ static void ad5933_work(struct work_struct *work)
buf[0] = be16_to_cpu(buf[0]);
}
/* save datum to the ring */
- ring->access->store_to(ring, (u8 *)buf, iio_get_time_ns());
+ iio_push_to_buffer(ring, (u8 *)buf);
} else {
/* no data available - try again later */
schedule_delayed_work(&st->work, st->poll_time_jiffies);
diff --git a/drivers/staging/iio/imu/adis16400.h b/drivers/staging/iio/imu/adis16400.h
index 9dd9f1459a4d..d59d7ac856a9 100644
--- a/drivers/staging/iio/imu/adis16400.h
+++ b/drivers/staging/iio/imu/adis16400.h
@@ -5,7 +5,7 @@
* 3d 2.5gauss magnetometers via SPI
*
* Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
- * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk>
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
*
* Loosely based upon lis3l02dq.h
*
diff --git a/drivers/staging/iio/imu/adis16400_core.c b/drivers/staging/iio/imu/adis16400_core.c
index a618327e06ed..b302c9ba2712 100644
--- a/drivers/staging/iio/imu/adis16400_core.c
+++ b/drivers/staging/iio/imu/adis16400_core.c
@@ -5,7 +5,7 @@
* 3d Magnetometers via SPI
*
* Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
- * Copyright (c) 2007 Jonathan Cameron <jic23@cam.ac.uk>
+ * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
* Copyright (c) 2011 Analog Devices Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -612,7 +612,7 @@ static int adis16400_read_raw(struct iio_dev *indio_dev,
}
}
-static struct iio_chan_spec adis16400_channels[] = {
+static const struct iio_chan_spec adis16400_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
@@ -742,7 +742,7 @@ static struct iio_chan_spec adis16400_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(12)
};
-static struct iio_chan_spec adis16350_channels[] = {
+static const struct iio_chan_spec adis16350_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
@@ -867,7 +867,7 @@ static struct iio_chan_spec adis16350_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(11)
};
-static struct iio_chan_spec adis16300_channels[] = {
+static const struct iio_chan_spec adis16300_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
@@ -1206,15 +1206,12 @@ error_ret:
}
/* fixme, confirm ordering in this function */
-static int adis16400_remove(struct spi_device *spi)
+static int __devexit adis16400_remove(struct spi_device *spi)
{
- int ret;
struct iio_dev *indio_dev = spi_get_drvdata(spi);
iio_device_unregister(indio_dev);
- ret = adis16400_stop_device(indio_dev);
- if (ret)
- goto err_ret;
+ adis16400_stop_device(indio_dev);
adis16400_remove_trigger(indio_dev);
iio_buffer_unregister(indio_dev);
@@ -1222,9 +1219,6 @@ static int adis16400_remove(struct spi_device *spi)
iio_device_free(indio_dev);
return 0;
-
-err_ret:
- return ret;
}
static const struct spi_device_id adis16400_id[] = {
diff --git a/drivers/staging/iio/imu/adis16400_ring.c b/drivers/staging/iio/imu/adis16400_ring.c
index beec650ddbdb..260bdd1a4681 100644
--- a/drivers/staging/iio/imu/adis16400_ring.c
+++ b/drivers/staging/iio/imu/adis16400_ring.c
@@ -150,7 +150,7 @@ static irqreturn_t adis16400_trigger_handler(int irq, void *p)
/* Guaranteed to be aligned with 8 byte boundary */
if (ring->scan_timestamp)
*((s64 *)(data + ((i + 3)/4)*4)) = pf->timestamp;
- ring->access->store_to(indio_dev->buffer, (u8 *) data, pf->timestamp);
+ iio_push_to_buffer(ring, (u8 *) data);
done:
kfree(data);
diff --git a/drivers/staging/iio/light/isl29018.c b/drivers/staging/iio/light/isl29018.c
index 31d22f5591ca..6ee5567d9813 100644
--- a/drivers/staging/iio/light/isl29018.c
+++ b/drivers/staging/iio/light/isl29018.c
@@ -63,6 +63,7 @@ struct isl29018_chip {
struct regmap *regmap;
struct mutex lock;
unsigned int lux_scale;
+ unsigned int lux_uscale;
unsigned int range;
unsigned int adc_bit;
int prox_scheme;
@@ -145,13 +146,22 @@ static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
static int isl29018_read_lux(struct isl29018_chip *chip, int *lux)
{
int lux_data;
+ unsigned int data_x_range, lux_unshifted;
lux_data = isl29018_read_sensor_input(chip, COMMMAND1_OPMODE_ALS_ONCE);
if (lux_data < 0)
return lux_data;
- *lux = (lux_data * chip->range * chip->lux_scale) >> chip->adc_bit;
+ /* To support fractional scaling, separate the unshifted lux
+ * into two calculations: int scaling and micro-scaling.
+ * lux_uscale ranges from 0-999999, so about 20 bits. Split
+ * the /1,000,000 in two to reduce the risk of over/underflow.
+ */
+ data_x_range = lux_data * chip->range;
+ lux_unshifted = data_x_range * chip->lux_scale;
+ lux_unshifted += data_x_range / 1000 * chip->lux_uscale / 1000;
+ *lux = lux_unshifted >> chip->adc_bit;
return 0;
}
@@ -339,6 +349,8 @@ static int isl29018_write_raw(struct iio_dev *indio_dev,
mutex_lock(&chip->lock);
if (mask == IIO_CHAN_INFO_CALIBSCALE && chan->type == IIO_LIGHT) {
chip->lux_scale = val;
+ /* With no write_raw_get_fmt(), val2 is a MICRO fraction. */
+ chip->lux_uscale = val2;
ret = 0;
}
mutex_unlock(&chip->lock);
@@ -379,7 +391,8 @@ static int isl29018_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_CALIBSCALE:
if (chan->type == IIO_LIGHT) {
*val = chip->lux_scale;
- ret = IIO_VAL_INT;
+ *val2 = chip->lux_uscale;
+ ret = IIO_VAL_INT_PLUS_MICRO;
}
break;
default:
diff --git a/drivers/staging/iio/light/tsl2563.c b/drivers/staging/iio/light/tsl2563.c
index 9d740be43a82..954ca2c172c6 100644
--- a/drivers/staging/iio/light/tsl2563.c
+++ b/drivers/staging/iio/light/tsl2563.c
@@ -805,7 +805,7 @@ fail1:
return err;
}
-static int tsl2563_remove(struct i2c_client *client)
+static int __devexit tsl2563_remove(struct i2c_client *client)
{
struct tsl2563_chip *chip = i2c_get_clientdata(client);
struct iio_dev *indio_dev = iio_priv_to_dev(chip);
diff --git a/drivers/staging/iio/magnetometer/hmc5843.c b/drivers/staging/iio/magnetometer/hmc5843.c
index 6c3e50f7c0d8..10e095486e54 100644
--- a/drivers/staging/iio/magnetometer/hmc5843.c
+++ b/drivers/staging/iio/magnetometer/hmc5843.c
@@ -1,6 +1,6 @@
/* Copyright (C) 2010 Texas Instruments
Author: Shubhrajyoti Datta <shubhrajyoti@ti.com>
- Acknowledgement: Jonathan Cameron <jic23@cam.ac.uk> for valuable inputs.
+ Acknowledgement: Jonathan Cameron <jic23@kernel.org> for valuable inputs.
Support for HMC5883 and HMC5883L by Peter Meerwald <pmeerw@pmeerw.net>.
diff --git a/drivers/staging/iio/meter/ade7753.c b/drivers/staging/iio/meter/ade7753.c
index 3ccff189f258..8b9eceb66b37 100644
--- a/drivers/staging/iio/meter/ade7753.c
+++ b/drivers/staging/iio/meter/ade7753.c
@@ -555,20 +555,15 @@ error_ret:
}
/* fixme, confirm ordering in this function */
-static int ade7753_remove(struct spi_device *spi)
+static int __devexit ade7753_remove(struct spi_device *spi)
{
- int ret;
struct iio_dev *indio_dev = spi_get_drvdata(spi);
iio_device_unregister(indio_dev);
-
- ret = ade7753_stop_device(&(indio_dev->dev));
- if (ret)
- goto err_ret;
-
+ ade7753_stop_device(&indio_dev->dev);
iio_device_free(indio_dev);
-err_ret:
- return ret;
+
+ return 0;
}
static struct spi_driver ade7753_driver = {
diff --git a/drivers/staging/iio/meter/ade7754.c b/drivers/staging/iio/meter/ade7754.c
index abb1e9c8d094..76e0adee96ea 100644
--- a/drivers/staging/iio/meter/ade7754.c
+++ b/drivers/staging/iio/meter/ade7754.c
@@ -577,21 +577,15 @@ error_ret:
}
/* fixme, confirm ordering in this function */
-static int ade7754_remove(struct spi_device *spi)
+static int __devexit ade7754_remove(struct spi_device *spi)
{
- int ret;
struct iio_dev *indio_dev = spi_get_drvdata(spi);
iio_device_unregister(indio_dev);
- ret = ade7754_stop_device(&(indio_dev->dev));
- if (ret)
- goto err_ret;
-
+ ade7754_stop_device(&indio_dev->dev);
iio_device_free(indio_dev);
-err_ret:
- return ret;
-
+ return 0;
}
static struct spi_driver ade7754_driver = {
diff --git a/drivers/staging/iio/meter/ade7758.h b/drivers/staging/iio/meter/ade7758.h
index ec202b4ecfb2..1e11ad5ae5a4 100644
--- a/drivers/staging/iio/meter/ade7758.h
+++ b/drivers/staging/iio/meter/ade7758.h
@@ -122,7 +122,7 @@ struct ade7758_state {
u8 *tx;
u8 *rx;
struct mutex buf_lock;
- struct iio_chan_spec *ade7758_ring_channels;
+ const struct iio_chan_spec *ade7758_ring_channels;
struct spi_transfer ring_xfer[4];
struct spi_message ring_msg;
/*
diff --git a/drivers/staging/iio/meter/ade7758_core.c b/drivers/staging/iio/meter/ade7758_core.c
index 7014a0078446..a0fef77d8e5e 100644
--- a/drivers/staging/iio/meter/ade7758_core.c
+++ b/drivers/staging/iio/meter/ade7758_core.c
@@ -661,7 +661,7 @@ static const struct attribute_group ade7758_attribute_group = {
.attrs = ade7758_attributes,
};
-static struct iio_chan_spec ade7758_channels[] = {
+static const struct iio_chan_spec ade7758_channels[] = {
{
.type = IIO_VOLTAGE,
.indexed = 1,
@@ -962,17 +962,13 @@ error_ret:
return ret;
}
-static int ade7758_remove(struct spi_device *spi)
+static int __devexit ade7758_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ade7758_state *st = iio_priv(indio_dev);
- int ret;
iio_device_unregister(indio_dev);
- ret = ade7758_stop_device(&indio_dev->dev);
- if (ret)
- goto err_ret;
-
+ ade7758_stop_device(&indio_dev->dev);
ade7758_remove_trigger(indio_dev);
ade7758_uninitialize_ring(indio_dev);
ade7758_unconfigure_ring(indio_dev);
@@ -981,8 +977,7 @@ static int ade7758_remove(struct spi_device *spi)
iio_device_free(indio_dev);
-err_ret:
- return ret;
+ return 0;
}
static const struct spi_device_id ade7758_id[] = {
diff --git a/drivers/staging/iio/meter/ade7758_ring.c b/drivers/staging/iio/meter/ade7758_ring.c
index 1ce10b21f4d6..9e49baccf660 100644
--- a/drivers/staging/iio/meter/ade7758_ring.c
+++ b/drivers/staging/iio/meter/ade7758_ring.c
@@ -61,7 +61,6 @@ static irqreturn_t ade7758_trigger_handler(int irq, void *p)
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
- struct iio_buffer *ring = indio_dev->buffer;
struct ade7758_state *st = iio_priv(indio_dev);
s64 dat64[2];
u32 *dat32 = (u32 *)dat64;
@@ -74,7 +73,7 @@ static irqreturn_t ade7758_trigger_handler(int irq, void *p)
if (indio_dev->scan_timestamp)
dat64[1] = pf->timestamp;
- ring->access->store_to(ring, (u8 *)dat64, pf->timestamp);
+ iio_push_to_buffer(indio_dev->buffer, (u8 *)dat64);
iio_trigger_notify_done(indio_dev->trig);
diff --git a/drivers/staging/iio/meter/ade7759.c b/drivers/staging/iio/meter/ade7759.c
index eb0a2a98f388..cb0707cbc347 100644
--- a/drivers/staging/iio/meter/ade7759.c
+++ b/drivers/staging/iio/meter/ade7759.c
@@ -499,20 +499,15 @@ error_ret:
}
/* fixme, confirm ordering in this function */
-static int ade7759_remove(struct spi_device *spi)
+static int __devexit ade7759_remove(struct spi_device *spi)
{
- int ret;
struct iio_dev *indio_dev = spi_get_drvdata(spi);
iio_device_unregister(indio_dev);
- ret = ade7759_stop_device(&(indio_dev->dev));
- if (ret)
- goto err_ret;
-
+ ade7759_stop_device(&indio_dev->dev);
iio_device_free(indio_dev);
-err_ret:
- return ret;
+ return 0;
}
static struct spi_driver ade7759_driver = {
diff --git a/drivers/staging/iio/meter/ade7854-spi.c b/drivers/staging/iio/meter/ade7854-spi.c
index 9fb2f8bfca81..7dae03573428 100644
--- a/drivers/staging/iio/meter/ade7854-spi.c
+++ b/drivers/staging/iio/meter/ade7854-spi.c
@@ -330,7 +330,7 @@ static int __devinit ade7854_spi_probe(struct spi_device *spi)
return 0;
}
-static int ade7854_spi_remove(struct spi_device *spi)
+static int __devexit ade7854_spi_remove(struct spi_device *spi)
{
ade7854_remove(spi_get_drvdata(spi));
diff --git a/drivers/staging/iio/resolver/ad2s1210.c b/drivers/staging/iio/resolver/ad2s1210.c
index f313859476c1..4ba4d05ed423 100644
--- a/drivers/staging/iio/resolver/ad2s1210.c
+++ b/drivers/staging/iio/resolver/ad2s1210.c
@@ -575,7 +575,7 @@ static IIO_DEVICE_ATTR(lot_low_thrd, S_IRUGO | S_IWUSR,
AD2S1210_REG_LOT_LOW_THRD);
-static struct iio_chan_spec ad2s1210_channels[] = {
+static const struct iio_chan_spec ad2s1210_channels[] = {
{
.type = IIO_ANGL,
.indexed = 1,
diff --git a/drivers/staging/iio/ring_hw.h b/drivers/staging/iio/ring_hw.h
index cad8a2ed9b68..39c14a715868 100644
--- a/drivers/staging/iio/ring_hw.h
+++ b/drivers/staging/iio/ring_hw.h
@@ -5,7 +5,7 @@
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation.
*
- * Copyright (c) 2009 Jonathan Cameron <jic23@cam.ac.uk>
+ * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
*
*/
diff --git a/drivers/staging/iio/ring_sw.c b/drivers/staging/iio/ring_sw.c
index f61c8fdaab06..3a45f9a52de8 100644
--- a/drivers/staging/iio/ring_sw.c
+++ b/drivers/staging/iio/ring_sw.c
@@ -65,7 +65,7 @@ static inline void __iio_free_sw_ring_buffer(struct iio_sw_ring_buffer *ring)
/* Lock always held if their is a chance this may be called */
/* Only one of these per ring may run concurrently - enforced by drivers */
static int iio_store_to_sw_ring(struct iio_sw_ring_buffer *ring,
- unsigned char *data, s64 timestamp)
+ unsigned char *data)
{
int ret = 0;
unsigned char *temp_ptr, *change_test_ptr;
@@ -256,11 +256,10 @@ error_ret:
}
static int iio_store_to_sw_rb(struct iio_buffer *r,
- u8 *data,
- s64 timestamp)
+ u8 *data)
{
struct iio_sw_ring_buffer *ring = iio_to_sw_ring(r);
- return iio_store_to_sw_ring(ring, data, timestamp);
+ return iio_store_to_sw_ring(ring, data);
}
static int iio_request_update_sw_rb(struct iio_buffer *r)
diff --git a/drivers/staging/iio/trigger/Kconfig b/drivers/staging/iio/trigger/Kconfig
index b8abf5473ddc..7d3207559265 100644
--- a/drivers/staging/iio/trigger/Kconfig
+++ b/drivers/staging/iio/trigger/Kconfig
@@ -21,6 +21,8 @@ config IIO_GPIO_TRIGGER
config IIO_SYSFS_TRIGGER
tristate "SYSFS trigger"
depends on SYSFS
+ depends on HAVE_IRQ_WORK
+ select IRQ_WORK
help
Provides support for using SYSFS entry as IIO triggers.
If unsure, say N (but it's safe to say "Y").
diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
index ce6a7b1b8860..52062d786f84 100644
--- a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
+++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c
@@ -14,14 +14,18 @@
#include <linux/delay.h>
#include <asm/gptimers.h>
+#include <asm/portmux.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
+#include "iio-trig-bfin-timer.h"
+
struct bfin_timer {
unsigned short id, bit;
unsigned long irqbit;
int irq;
+ int pin;
};
/*
@@ -30,22 +34,22 @@ struct bfin_timer {
*/
static struct bfin_timer iio_bfin_timer_code[MAX_BLACKFIN_GPTIMERS] = {
- {TIMER0_id, TIMER0bit, TIMER_STATUS_TIMIL0, IRQ_TIMER0},
- {TIMER1_id, TIMER1bit, TIMER_STATUS_TIMIL1, IRQ_TIMER1},
- {TIMER2_id, TIMER2bit, TIMER_STATUS_TIMIL2, IRQ_TIMER2},
+ {TIMER0_id, TIMER0bit, TIMER_STATUS_TIMIL0, IRQ_TIMER0, P_TMR0},
+ {TIMER1_id, TIMER1bit, TIMER_STATUS_TIMIL1, IRQ_TIMER1, P_TMR1},
+ {TIMER2_id, TIMER2bit, TIMER_STATUS_TIMIL2, IRQ_TIMER2, P_TMR2},
#if (MAX_BLACKFIN_GPTIMERS > 3)
- {TIMER3_id, TIMER3bit, TIMER_STATUS_TIMIL3, IRQ_TIMER3},
- {TIMER4_id, TIMER4bit, TIMER_STATUS_TIMIL4, IRQ_TIMER4},
- {TIMER5_id, TIMER5bit, TIMER_STATUS_TIMIL5, IRQ_TIMER5},
- {TIMER6_id, TIMER6bit, TIMER_STATUS_TIMIL6, IRQ_TIMER6},
- {TIMER7_id, TIMER7bit, TIMER_STATUS_TIMIL7, IRQ_TIMER7},
+ {TIMER3_id, TIMER3bit, TIMER_STATUS_TIMIL3, IRQ_TIMER3, P_TMR3},
+ {TIMER4_id, TIMER4bit, TIMER_STATUS_TIMIL4, IRQ_TIMER4, P_TMR4},
+ {TIMER5_id, TIMER5bit, TIMER_STATUS_TIMIL5, IRQ_TIMER5, P_TMR5},
+ {TIMER6_id, TIMER6bit, TIMER_STATUS_TIMIL6, IRQ_TIMER6, P_TMR6},
+ {TIMER7_id, TIMER7bit, TIMER_STATUS_TIMIL7, IRQ_TIMER7, P_TMR7},
#endif
#if (MAX_BLACKFIN_GPTIMERS > 8)
- {TIMER8_id, TIMER8bit, TIMER_STATUS_TIMIL8, IRQ_TIMER8},
- {TIMER9_id, TIMER9bit, TIMER_STATUS_TIMIL9, IRQ_TIMER9},
- {TIMER10_id, TIMER10bit, TIMER_STATUS_TIMIL10, IRQ_TIMER10},
+ {TIMER8_id, TIMER8bit, TIMER_STATUS_TIMIL8, IRQ_TIMER8, P_TMR8},
+ {TIMER9_id, TIMER9bit, TIMER_STATUS_TIMIL9, IRQ_TIMER9, P_TMR9},
+ {TIMER10_id, TIMER10bit, TIMER_STATUS_TIMIL10, IRQ_TIMER10, P_TMR10},
#if (MAX_BLACKFIN_GPTIMERS > 11)
- {TIMER11_id, TIMER11bit, TIMER_STATUS_TIMIL11, IRQ_TIMER11},
+ {TIMER11_id, TIMER11bit, TIMER_STATUS_TIMIL11, IRQ_TIMER11, P_TMR11},
#endif
#endif
};
@@ -54,15 +58,33 @@ struct bfin_tmr_state {
struct iio_trigger *trig;
struct bfin_timer *t;
unsigned timer_num;
+ bool output_enable;
+ unsigned int duty;
int irq;
};
+static int iio_bfin_tmr_set_state(struct iio_trigger *trig, bool state)
+{
+ struct bfin_tmr_state *st = trig->private_data;
+
+ if (get_gptimer_period(st->t->id) == 0)
+ return -EINVAL;
+
+ if (state)
+ enable_gptimers(st->t->bit);
+ else
+ disable_gptimers(st->t->bit);
+
+ return 0;
+}
+
static ssize_t iio_bfin_tmr_frequency_store(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct iio_trigger *trig = to_iio_trigger(dev);
struct bfin_tmr_state *st = trig->private_data;
- long val;
+ unsigned long val;
+ bool enabled;
int ret;
ret = strict_strtoul(buf, 10, &val);
@@ -74,20 +96,25 @@ static ssize_t iio_bfin_tmr_frequency_store(struct device *dev,
goto error_ret;
}
- disable_gptimers(st->t->bit);
+ enabled = get_enabled_gptimers() & st->t->bit;
+
+ if (enabled)
+ disable_gptimers(st->t->bit);
if (!val)
goto error_ret;
val = get_sclk() / val;
- if (val <= 4) {
+ if (val <= 4 || val <= st->duty) {
ret = -EINVAL;
goto error_ret;
}
set_gptimer_period(st->t->id, val);
- set_gptimer_pwidth(st->t->id, 1);
- enable_gptimers(st->t->bit);
+ set_gptimer_pwidth(st->t->id, val - st->duty);
+
+ if (enabled)
+ enable_gptimers(st->t->bit);
error_ret:
return ret ? ret : count;
@@ -99,9 +126,15 @@ static ssize_t iio_bfin_tmr_frequency_show(struct device *dev,
{
struct iio_trigger *trig = to_iio_trigger(dev);
struct bfin_tmr_state *st = trig->private_data;
+ unsigned int period = get_gptimer_period(st->t->id);
+ unsigned long val;
+
+ if (period == 0)
+ val = 0;
+ else
+ val = get_sclk() / get_gptimer_period(st->t->id);
- return sprintf(buf, "%lu\n",
- get_sclk() / get_gptimer_period(st->t->id));
+ return sprintf(buf, "%lu\n", val);
}
static DEVICE_ATTR(frequency, S_IRUGO | S_IWUSR, iio_bfin_tmr_frequency_show,
@@ -121,7 +154,6 @@ static const struct attribute_group *iio_bfin_tmr_trigger_attr_groups[] = {
NULL
};
-
static irqreturn_t iio_bfin_tmr_trigger_isr(int irq, void *devid)
{
struct bfin_tmr_state *st = devid;
@@ -145,11 +177,14 @@ static int iio_bfin_tmr_get_number(int irq)
static const struct iio_trigger_ops iio_bfin_tmr_trigger_ops = {
.owner = THIS_MODULE,
+ .set_trigger_state = iio_bfin_tmr_set_state,
};
static int __devinit iio_bfin_tmr_trigger_probe(struct platform_device *pdev)
{
+ struct iio_bfin_timer_trigger_pdata *pdata = pdev->dev.platform_data;
struct bfin_tmr_state *st;
+ unsigned int config;
int ret;
st = kzalloc(sizeof(*st), GFP_KERNEL);
@@ -193,13 +228,43 @@ static int __devinit iio_bfin_tmr_trigger_probe(struct platform_device *pdev)
goto out4;
}
- set_gptimer_config(st->t->id, OUT_DIS | PWM_OUT | PERIOD_CNT | IRQ_ENA);
+ config = PWM_OUT | PERIOD_CNT | IRQ_ENA;
+
+ if (pdata && pdata->output_enable) {
+ unsigned long long val;
+
+ st->output_enable = true;
+
+ ret = peripheral_request(st->t->pin, st->trig->name);
+ if (ret)
+ goto out_free_irq;
+
+ val = (unsigned long long)get_sclk() * pdata->duty_ns;
+ do_div(val, NSEC_PER_SEC);
+ st->duty = val;
+
+ /**
+ * The interrupt will be generated at the end of the period,
+ * since we want the interrupt to be generated at end of the
+ * pulse we invert both polarity and duty cycle, so that the
+ * pulse will be generated directly before the interrupt.
+ */
+ if (pdata->active_low)
+ config |= PULSE_HI;
+ } else {
+ st->duty = 1;
+ config |= OUT_DIS;
+ }
+
+ set_gptimer_config(st->t->id, config);
dev_info(&pdev->dev, "iio trigger Blackfin TMR%d, IRQ-%d",
st->timer_num, st->irq);
platform_set_drvdata(pdev, st);
return 0;
+out_free_irq:
+ free_irq(st->irq, st);
out4:
iio_trigger_unregister(st->trig);
out2:
@@ -215,6 +280,8 @@ static int __devexit iio_bfin_tmr_trigger_remove(struct platform_device *pdev)
struct bfin_tmr_state *st = platform_get_drvdata(pdev);
disable_gptimers(st->t->bit);
+ if (st->output_enable)
+ peripheral_free(st->t->pin);
free_irq(st->irq, st);
iio_trigger_unregister(st->trig);
iio_trigger_put(st->trig);
diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.h b/drivers/staging/iio/trigger/iio-trig-bfin-timer.h
new file mode 100644
index 000000000000..c07321f8d94c
--- /dev/null
+++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.h
@@ -0,0 +1,24 @@
+#ifndef __IIO_BFIN_TIMER_TRIGGER_H__
+#define __IIO_BFIN_TIMER_TRIGGER_H__
+
+/**
+ * struct iio_bfin_timer_trigger_pdata - timer trigger platform data
+ * @output_enable: Enable external trigger pulse generation.
+ * @active_low: Whether the trigger pulse is active low.
+ * @duty_ns: Length of the trigger pulse in nanoseconds.
+ *
+ * This struct is used to configure the output pulse generation of the blackfin
+ * timer trigger. If output_enable is set to true an external trigger signal
+ * will generated on the pin corresponding to the timer. This is useful for
+ * converters which needs an external signal to start conversion. active_low and
+ * duty_ns are used to configure the type of the trigger pulse. If output_enable
+ * is set to false no external trigger pulse will be generated and active_low
+ * and duty_ns are ignored.
+ **/
+struct iio_bfin_timer_trigger_pdata {
+ bool output_enable;
+ bool active_low;
+ unsigned int duty_ns;
+};
+
+#endif
diff --git a/drivers/staging/iio/trigger/iio-trig-gpio.c b/drivers/staging/iio/trigger/iio-trig-gpio.c
index 90b26846fc6b..5ff4d7fa20fa 100644
--- a/drivers/staging/iio/trigger/iio-trig-gpio.c
+++ b/drivers/staging/iio/trigger/iio-trig-gpio.c
@@ -51,7 +51,7 @@ static const struct iio_trigger_ops iio_gpio_trigger_ops = {
.owner = THIS_MODULE,
};
-static int iio_gpio_trigger_probe(struct platform_device *pdev)
+static int __devinit iio_gpio_trigger_probe(struct platform_device *pdev)
{
struct iio_gpio_trigger_info *trig_info;
struct iio_trigger *trig, *trig2;
@@ -130,7 +130,7 @@ error_free_completed_registrations:
return ret;
}
-static int iio_gpio_trigger_remove(struct platform_device *pdev)
+static int __devexit iio_gpio_trigger_remove(struct platform_device *pdev)
{
struct iio_trigger *trig, *trig2;
struct iio_gpio_trigger_info *trig_info;
@@ -153,7 +153,7 @@ static int iio_gpio_trigger_remove(struct platform_device *pdev)
static struct platform_driver iio_gpio_trigger_driver = {
.probe = iio_gpio_trigger_probe,
- .remove = iio_gpio_trigger_remove,
+ .remove = __devexit_p(iio_gpio_trigger_remove),
.driver = {
.name = "iio_gpio_trigger",
.owner = THIS_MODULE,
@@ -162,6 +162,6 @@ static struct platform_driver iio_gpio_trigger_driver = {
module_platform_driver(iio_gpio_trigger_driver);
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Example gpio trigger for the iio subsystem");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c
index 4ceaa18ef9f4..a3de76d70cdc 100644
--- a/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c
+++ b/drivers/staging/iio/trigger/iio-trig-periodic-rtc.c
@@ -101,7 +101,7 @@ static const struct iio_trigger_ops iio_prtc_trigger_ops = {
.set_trigger_state = &iio_trig_periodic_rtc_set_state,
};
-static int iio_trig_periodic_rtc_probe(struct platform_device *dev)
+static int __devinit iio_trig_periodic_rtc_probe(struct platform_device *dev)
{
char **pdata = dev->dev.platform_data;
struct iio_prtc_trigger_info *trig_info;
@@ -167,7 +167,7 @@ error_free_completed_registrations:
return ret;
}
-static int iio_trig_periodic_rtc_remove(struct platform_device *dev)
+static int __devexit iio_trig_periodic_rtc_remove(struct platform_device *dev)
{
struct iio_trigger *trig, *trig2;
struct iio_prtc_trigger_info *trig_info;
@@ -188,7 +188,7 @@ static int iio_trig_periodic_rtc_remove(struct platform_device *dev)
static struct platform_driver iio_trig_periodic_rtc_driver = {
.probe = iio_trig_periodic_rtc_probe,
- .remove = iio_trig_periodic_rtc_remove,
+ .remove = __devexit_p(iio_trig_periodic_rtc_remove),
.driver = {
.name = "iio_prtc_trigger",
.owner = THIS_MODULE,
@@ -197,6 +197,6 @@ static struct platform_driver iio_trig_periodic_rtc_driver = {
module_platform_driver(iio_trig_periodic_rtc_driver);
-MODULE_AUTHOR("Jonathan Cameron <jic23@cam.ac.uk>");
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Periodic realtime clock trigger for the iio subsystem");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/iio/trigger/iio-trig-sysfs.c b/drivers/staging/iio/trigger/iio-trig-sysfs.c
index fee474648108..3bac97224bf4 100644
--- a/drivers/staging/iio/trigger/iio-trig-sysfs.c
+++ b/drivers/staging/iio/trigger/iio-trig-sysfs.c
@@ -10,12 +10,14 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/list.h>
+#include <linux/irq_work.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
struct iio_sysfs_trig {
struct iio_trigger *trig;
+ struct irq_work work;
int id;
struct list_head l;
};
@@ -89,11 +91,21 @@ static struct device iio_sysfs_trig_dev = {
.release = &iio_trigger_sysfs_release,
};
+static void iio_sysfs_trigger_work(struct irq_work *work)
+{
+ struct iio_sysfs_trig *trig = container_of(work, struct iio_sysfs_trig,
+ work);
+
+ iio_trigger_poll(trig->trig, 0);
+}
+
static ssize_t iio_sysfs_trigger_poll(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct iio_trigger *trig = to_iio_trigger(dev);
- iio_trigger_poll_chained(trig, 0);
+ struct iio_sysfs_trig *sysfs_trig = trig->private_data;
+
+ irq_work_queue(&sysfs_trig->work);
return count;
}
@@ -148,6 +160,9 @@ static int iio_sysfs_trigger_probe(int id)
t->trig->dev.groups = iio_sysfs_trigger_attr_groups;
t->trig->ops = &iio_sysfs_trigger_ops;
t->trig->dev.parent = &iio_sysfs_trig_dev;
+ t->trig->private_data = t;
+
+ init_irq_work(&t->work, iio_sysfs_trigger_work);
ret = iio_trigger_register(t->trig);
if (ret)
diff --git a/drivers/staging/imx-drm/Kconfig b/drivers/staging/imx-drm/Kconfig
new file mode 100644
index 000000000000..14b4449df234
--- /dev/null
+++ b/drivers/staging/imx-drm/Kconfig
@@ -0,0 +1,35 @@
+config DRM_IMX
+ tristate "DRM Support for Freescale i.MX"
+ select DRM_KMS_HELPER
+ select DRM_GEM_CMA_HELPER
+ select DRM_KMS_CMA_HELPER
+ depends on DRM && ARCH_MXC
+ help
+ enable i.MX graphics support
+
+config DRM_IMX_FB_HELPER
+ tristate "provide legacy framebuffer /dev/fb0"
+ select DRM_KMS_CMA_HELPER
+ depends on DRM_IMX
+ help
+ The DRM framework can provide a legacy /dev/fb0 framebuffer
+ for your device. This is necessary to get a framebuffer console
+ and also for appplications using the legacy framebuffer API
+
+config DRM_IMX_PARALLEL_DISPLAY
+ tristate "Support for parallel displays"
+ depends on DRM_IMX
+
+config DRM_IMX_IPUV3_CORE
+ tristate "IPUv3 core support"
+ depends on DRM_IMX
+ help
+ Choose this if you have a i.MX5/6 system and want
+ to use the IPU. This option only enables IPU base
+ support.
+
+config DRM_IMX_IPUV3
+ tristate "DRM Support for i.MX IPUv3"
+ depends on DRM_IMX
+ help
+ Choose this if you have a i.MX5 or i.MX6 processor.
diff --git a/drivers/staging/imx-drm/Makefile b/drivers/staging/imx-drm/Makefile
new file mode 100644
index 000000000000..83a9056546e6
--- /dev/null
+++ b/drivers/staging/imx-drm/Makefile
@@ -0,0 +1,9 @@
+
+imxdrm-objs := imx-drm-core.o imx-fb.o
+
+obj-$(CONFIG_DRM_IMX) += imxdrm.o
+
+obj-$(CONFIG_DRM_IMX_PARALLEL_DISPLAY) += parallel-display.o
+obj-$(CONFIG_DRM_IMX_FB_HELPER) += imx-fbdev.o
+obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += ipu-v3/
+obj-$(CONFIG_DRM_IMX_IPUV3) += ipuv3-crtc.o
diff --git a/drivers/staging/imx-drm/TODO b/drivers/staging/imx-drm/TODO
new file mode 100644
index 000000000000..e52adc44e607
--- /dev/null
+++ b/drivers/staging/imx-drm/TODO
@@ -0,0 +1,22 @@
+TODO:
+- get DRM Maintainer review for this code
+- Factor out more code to common helper functions
+- decide where to put the base driver. It is not specific to a subsystem
+ and would be used by DRM/KMS and media/V4L2
+- convert irq driver to irq_domain_add_linear
+
+Missing features (not necessarily for moving out of staging):
+
+- Add KMS plane support for CRTC driver
+- Add LDB (LVDS Display Bridge) support
+- Add i.MX6 HDMI support
+- Add support for IC (Image converter)
+- Add support for CSI (CMOS Sensor interface)
+- Add support for VDIC (Video Deinterlacer)
+
+Many work-in-progress patches for the above features exist. Contact
+Sascha Hauer <kernel@pengutronix.de> if you are interested in working
+on a specific feature.
+
+Please send any patches to Greg Kroah-Hartman <gregkh@linuxfoundation.org> and
+Sascha Hauer <kernel@pengutronix.de>
diff --git a/drivers/staging/imx-drm/imx-drm-core.c b/drivers/staging/imx-drm/imx-drm-core.c
new file mode 100644
index 000000000000..1913199ba16e
--- /dev/null
+++ b/drivers/staging/imx-drm/imx-drm-core.c
@@ -0,0 +1,884 @@
+/*
+ * Freescale i.MX drm driver
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * This program is free software; 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/device.h>
+#include <linux/platform_device.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/fb.h>
+#include <linux/module.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "imx-drm.h"
+
+#define MAX_CRTC 4
+
+struct crtc_cookie {
+ void *cookie;
+ int id;
+ struct list_head list;
+};
+
+struct imx_drm_device {
+ struct drm_device *drm;
+ struct device *dev;
+ struct list_head crtc_list;
+ struct list_head encoder_list;
+ struct list_head connector_list;
+ struct mutex mutex;
+ int references;
+ int pipes;
+ struct drm_fbdev_cma *fbhelper;
+};
+
+struct imx_drm_crtc {
+ struct drm_crtc *crtc;
+ struct list_head list;
+ struct imx_drm_device *imxdrm;
+ int pipe;
+ struct imx_drm_crtc_helper_funcs imx_drm_helper_funcs;
+ struct module *owner;
+ struct crtc_cookie cookie;
+};
+
+struct imx_drm_encoder {
+ struct drm_encoder *encoder;
+ struct list_head list;
+ struct module *owner;
+ struct list_head possible_crtcs;
+};
+
+struct imx_drm_connector {
+ struct drm_connector *connector;
+ struct list_head list;
+ struct module *owner;
+};
+
+static int imx_drm_driver_firstopen(struct drm_device *drm)
+{
+ if (!imx_drm_device_get())
+ return -EINVAL;
+
+ return 0;
+}
+
+static void imx_drm_driver_lastclose(struct drm_device *drm)
+{
+ struct imx_drm_device *imxdrm = drm->dev_private;
+
+ if (imxdrm->fbhelper)
+ drm_fbdev_cma_restore_mode(imxdrm->fbhelper);
+
+ imx_drm_device_put();
+}
+
+static int imx_drm_driver_unload(struct drm_device *drm)
+{
+ struct imx_drm_device *imxdrm = drm->dev_private;
+
+ drm_mode_config_cleanup(imxdrm->drm);
+ drm_kms_helper_poll_fini(imxdrm->drm);
+
+ return 0;
+}
+
+/*
+ * We don't care at all for crtc numbers, but the core expects the
+ * crtcs to be numbered
+ */
+static struct imx_drm_crtc *imx_drm_crtc_by_num(struct imx_drm_device *imxdrm,
+ int num)
+{
+ struct imx_drm_crtc *imx_drm_crtc;
+
+ list_for_each_entry(imx_drm_crtc, &imxdrm->crtc_list, list)
+ if (imx_drm_crtc->pipe == num)
+ return imx_drm_crtc;
+ return NULL;
+}
+
+int imx_drm_crtc_panel_format(struct drm_crtc *crtc, u32 encoder_type,
+ u32 interface_pix_fmt)
+{
+ struct imx_drm_device *imxdrm = crtc->dev->dev_private;
+ struct imx_drm_crtc *imx_crtc;
+ struct imx_drm_crtc_helper_funcs *helper;
+
+ mutex_lock(&imxdrm->mutex);
+
+ list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list)
+ if (imx_crtc->crtc == crtc)
+ goto found;
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return -EINVAL;
+found:
+ mutex_unlock(&imxdrm->mutex);
+
+ helper = &imx_crtc->imx_drm_helper_funcs;
+ if (helper->set_interface_pix_fmt)
+ return helper->set_interface_pix_fmt(crtc,
+ encoder_type, interface_pix_fmt);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_drm_crtc_panel_format);
+
+int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc)
+{
+ return drm_vblank_get(imx_drm_crtc->imxdrm->drm, imx_drm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_get);
+
+void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc)
+{
+ drm_vblank_put(imx_drm_crtc->imxdrm->drm, imx_drm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(imx_drm_crtc_vblank_put);
+
+void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc)
+{
+ drm_handle_vblank(imx_drm_crtc->imxdrm->drm, imx_drm_crtc->pipe);
+}
+EXPORT_SYMBOL_GPL(imx_drm_handle_vblank);
+
+static int imx_drm_enable_vblank(struct drm_device *drm, int crtc)
+{
+ struct imx_drm_device *imxdrm = drm->dev_private;
+ struct imx_drm_crtc *imx_drm_crtc;
+ int ret;
+
+ imx_drm_crtc = imx_drm_crtc_by_num(imxdrm, crtc);
+ if (!imx_drm_crtc)
+ return -EINVAL;
+
+ if (!imx_drm_crtc->imx_drm_helper_funcs.enable_vblank)
+ return -ENOSYS;
+
+ ret = imx_drm_crtc->imx_drm_helper_funcs.enable_vblank(
+ imx_drm_crtc->crtc);
+
+ return ret;
+}
+
+static void imx_drm_disable_vblank(struct drm_device *drm, int crtc)
+{
+ struct imx_drm_device *imxdrm = drm->dev_private;
+ struct imx_drm_crtc *imx_drm_crtc;
+
+ imx_drm_crtc = imx_drm_crtc_by_num(imxdrm, crtc);
+ if (!imx_drm_crtc)
+ return;
+
+ if (!imx_drm_crtc->imx_drm_helper_funcs.disable_vblank)
+ return;
+
+ imx_drm_crtc->imx_drm_helper_funcs.disable_vblank(imx_drm_crtc->crtc);
+}
+
+static const struct file_operations imx_drm_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_gem_cma_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+ .read = drm_read,
+ .llseek = noop_llseek,
+};
+
+static struct imx_drm_device *imx_drm_device;
+
+static struct imx_drm_device *__imx_drm_device(void)
+{
+ return imx_drm_device;
+}
+
+struct drm_device *imx_drm_device_get(void)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_encoder *enc;
+ struct imx_drm_connector *con;
+ struct imx_drm_crtc *crtc;
+
+ mutex_lock(&imxdrm->mutex);
+
+ list_for_each_entry(enc, &imxdrm->encoder_list, list) {
+ if (!try_module_get(enc->owner)) {
+ dev_err(imxdrm->dev, "could not get module %s\n",
+ module_name(enc->owner));
+ goto unwind_enc;
+ }
+ }
+
+ list_for_each_entry(con, &imxdrm->connector_list, list) {
+ if (!try_module_get(con->owner)) {
+ dev_err(imxdrm->dev, "could not get module %s\n",
+ module_name(con->owner));
+ goto unwind_con;
+ }
+ }
+
+ list_for_each_entry(crtc, &imxdrm->crtc_list, list) {
+ if (!try_module_get(crtc->owner)) {
+ dev_err(imxdrm->dev, "could not get module %s\n",
+ module_name(crtc->owner));
+ goto unwind_crtc;
+ }
+ }
+
+ imxdrm->references++;
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return imxdrm->drm;
+
+unwind_crtc:
+ list_for_each_entry_continue_reverse(crtc, &imxdrm->crtc_list, list)
+ module_put(crtc->owner);
+unwind_con:
+ list_for_each_entry_continue_reverse(con, &imxdrm->connector_list, list)
+ module_put(con->owner);
+unwind_enc:
+ list_for_each_entry_continue_reverse(enc, &imxdrm->encoder_list, list)
+ module_put(enc->owner);
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return NULL;
+
+}
+EXPORT_SYMBOL_GPL(imx_drm_device_get);
+
+void imx_drm_device_put(void)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_encoder *enc;
+ struct imx_drm_connector *con;
+ struct imx_drm_crtc *crtc;
+
+ mutex_lock(&imxdrm->mutex);
+
+ list_for_each_entry(crtc, &imxdrm->crtc_list, list)
+ module_put(crtc->owner);
+
+ list_for_each_entry(con, &imxdrm->connector_list, list)
+ module_put(con->owner);
+
+ list_for_each_entry(enc, &imxdrm->encoder_list, list)
+ module_put(enc->owner);
+
+ imxdrm->references--;
+
+ mutex_unlock(&imxdrm->mutex);
+}
+EXPORT_SYMBOL_GPL(imx_drm_device_put);
+
+static int drm_mode_group_reinit(struct drm_device *dev)
+{
+ struct drm_mode_group *group = &dev->primary->mode_group;
+ uint32_t *id_list = group->id_list;
+ int ret;
+
+ ret = drm_mode_group_init_legacy_group(dev, group);
+ if (ret < 0)
+ return ret;
+
+ kfree(id_list);
+ return 0;
+}
+
+/*
+ * register an encoder to the drm core
+ */
+static int imx_drm_encoder_register(struct imx_drm_encoder *imx_drm_encoder)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ INIT_LIST_HEAD(&imx_drm_encoder->possible_crtcs);
+
+ drm_encoder_init(imxdrm->drm, imx_drm_encoder->encoder,
+ imx_drm_encoder->encoder->funcs,
+ imx_drm_encoder->encoder->encoder_type);
+
+ drm_mode_group_reinit(imxdrm->drm);
+
+ return 0;
+}
+
+/*
+ * unregister an encoder from the drm core
+ */
+static void imx_drm_encoder_unregister(struct imx_drm_encoder
+ *imx_drm_encoder)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ drm_encoder_cleanup(imx_drm_encoder->encoder);
+
+ drm_mode_group_reinit(imxdrm->drm);
+}
+
+/*
+ * register a connector to the drm core
+ */
+static int imx_drm_connector_register(
+ struct imx_drm_connector *imx_drm_connector)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ drm_connector_init(imxdrm->drm, imx_drm_connector->connector,
+ imx_drm_connector->connector->funcs,
+ imx_drm_connector->connector->connector_type);
+ drm_mode_group_reinit(imxdrm->drm);
+
+ return drm_sysfs_connector_add(imx_drm_connector->connector);
+}
+
+/*
+ * unregister a connector from the drm core
+ */
+static void imx_drm_connector_unregister(
+ struct imx_drm_connector *imx_drm_connector)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ drm_sysfs_connector_remove(imx_drm_connector->connector);
+ drm_connector_cleanup(imx_drm_connector->connector);
+
+ drm_mode_group_reinit(imxdrm->drm);
+}
+
+/*
+ * register a crtc to the drm core
+ */
+static int imx_drm_crtc_register(struct imx_drm_crtc *imx_drm_crtc)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ int ret;
+
+ drm_crtc_init(imxdrm->drm, imx_drm_crtc->crtc,
+ imx_drm_crtc->imx_drm_helper_funcs.crtc_funcs);
+ ret = drm_mode_crtc_set_gamma_size(imx_drm_crtc->crtc, 256);
+ if (ret)
+ return ret;
+
+ drm_crtc_helper_add(imx_drm_crtc->crtc,
+ imx_drm_crtc->imx_drm_helper_funcs.crtc_helper_funcs);
+
+ drm_mode_group_reinit(imxdrm->drm);
+
+ return 0;
+}
+
+/*
+ * Called by the CRTC driver when all CRTCs are registered. This
+ * puts all the pieces together and initializes the driver.
+ * Once this is called no more CRTCs can be registered since
+ * the drm core has hardcoded the number of crtcs in several
+ * places.
+ */
+static int imx_drm_driver_load(struct drm_device *drm, unsigned long flags)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ int ret;
+
+ imxdrm->drm = drm;
+
+ drm->dev_private = imxdrm;
+
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = 1, we can use the vblank feature.
+ *
+ * P.S. note that we wouldn't use drm irq handler but
+ * just specific driver own one instead because
+ * drm framework supports only one irq handler and
+ * drivers can well take care of their interrupts
+ */
+ drm->irq_enabled = 1;
+
+ drm_mode_config_init(drm);
+ imx_drm_mode_config_init(drm);
+
+ mutex_lock(&imxdrm->mutex);
+
+ drm_kms_helper_poll_init(imxdrm->drm);
+
+ /* setup the grouping for the legacy output */
+ ret = drm_mode_group_init_legacy_group(imxdrm->drm,
+ &imxdrm->drm->primary->mode_group);
+ if (ret)
+ goto err_init;
+
+ ret = drm_vblank_init(imxdrm->drm, MAX_CRTC);
+ if (ret)
+ goto err_init;
+
+ /*
+ * with vblank_disable_allowed = 1, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(after drm_vblank_put function is called)
+ */
+ imxdrm->drm->vblank_disable_allowed = 1;
+
+ ret = 0;
+
+err_init:
+ mutex_unlock(&imxdrm->mutex);
+
+ return ret;
+}
+
+static void imx_drm_update_possible_crtcs(void)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_crtc *imx_drm_crtc;
+ struct imx_drm_encoder *enc;
+ struct crtc_cookie *cookie;
+
+ list_for_each_entry(enc, &imxdrm->encoder_list, list) {
+ u32 possible_crtcs = 0;
+
+ list_for_each_entry(cookie, &enc->possible_crtcs, list) {
+ list_for_each_entry(imx_drm_crtc, &imxdrm->crtc_list, list) {
+ if (imx_drm_crtc->cookie.cookie == cookie->cookie &&
+ imx_drm_crtc->cookie.id == cookie->id) {
+ possible_crtcs |= 1 << imx_drm_crtc->pipe;
+ }
+ }
+ }
+ enc->encoder->possible_crtcs = possible_crtcs;
+ enc->encoder->possible_clones = possible_crtcs;
+ }
+}
+
+/*
+ * imx_drm_add_crtc - add a new crtc
+ *
+ * The return value if !NULL is a cookie for the caller to pass to
+ * imx_drm_remove_crtc later.
+ */
+int imx_drm_add_crtc(struct drm_crtc *crtc,
+ struct imx_drm_crtc **new_crtc,
+ const struct imx_drm_crtc_helper_funcs *imx_drm_helper_funcs,
+ struct module *owner, void *cookie, int id)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_crtc *imx_drm_crtc;
+ const struct drm_crtc_funcs *crtc_funcs;
+ int ret;
+
+ mutex_lock(&imxdrm->mutex);
+
+ if (imxdrm->references) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
+
+ imx_drm_crtc = kzalloc(sizeof(*imx_drm_crtc), GFP_KERNEL);
+ if (!imx_drm_crtc) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ imx_drm_crtc->imx_drm_helper_funcs = *imx_drm_helper_funcs;
+ imx_drm_crtc->pipe = imxdrm->pipes++;
+ imx_drm_crtc->cookie.cookie = cookie;
+ imx_drm_crtc->cookie.id = id;
+
+ crtc_funcs = imx_drm_helper_funcs->crtc_funcs;
+
+ imx_drm_crtc->crtc = crtc;
+ imx_drm_crtc->imxdrm = imxdrm;
+
+ imx_drm_crtc->owner = owner;
+
+ list_add_tail(&imx_drm_crtc->list, &imxdrm->crtc_list);
+
+ *new_crtc = imx_drm_crtc;
+
+ ret = imx_drm_crtc_register(imx_drm_crtc);
+ if (ret)
+ goto err_register;
+
+ imx_drm_update_possible_crtcs();
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return 0;
+
+err_register:
+ kfree(imx_drm_crtc);
+err_alloc:
+err_busy:
+ mutex_unlock(&imxdrm->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_drm_add_crtc);
+
+/*
+ * imx_drm_remove_crtc - remove a crtc
+ */
+int imx_drm_remove_crtc(struct imx_drm_crtc *imx_drm_crtc)
+{
+ struct imx_drm_device *imxdrm = imx_drm_crtc->imxdrm;
+
+ mutex_lock(&imxdrm->mutex);
+
+ drm_crtc_cleanup(imx_drm_crtc->crtc);
+
+ list_del(&imx_drm_crtc->list);
+
+ drm_mode_group_reinit(imxdrm->drm);
+
+ mutex_unlock(&imxdrm->mutex);
+
+ kfree(imx_drm_crtc);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_drm_remove_crtc);
+
+/*
+ * imx_drm_add_encoder - add a new encoder
+ */
+int imx_drm_add_encoder(struct drm_encoder *encoder,
+ struct imx_drm_encoder **newenc, struct module *owner)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_encoder *imx_drm_encoder;
+ int ret;
+
+ mutex_lock(&imxdrm->mutex);
+
+ if (imxdrm->references) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
+
+ imx_drm_encoder = kzalloc(sizeof(*imx_drm_encoder), GFP_KERNEL);
+ if (!imx_drm_encoder) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ imx_drm_encoder->encoder = encoder;
+ imx_drm_encoder->owner = owner;
+
+ ret = imx_drm_encoder_register(imx_drm_encoder);
+ if (ret) {
+ kfree(imx_drm_encoder);
+ ret = -ENOMEM;
+ goto err_register;
+ }
+
+ list_add_tail(&imx_drm_encoder->list, &imxdrm->encoder_list);
+
+ *newenc = imx_drm_encoder;
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return 0;
+
+err_register:
+ kfree(imx_drm_encoder);
+err_alloc:
+err_busy:
+ mutex_unlock(&imxdrm->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_drm_add_encoder);
+
+int imx_drm_encoder_add_possible_crtcs(
+ struct imx_drm_encoder *imx_drm_encoder,
+ struct device_node *np)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct of_phandle_args args;
+ struct crtc_cookie *c;
+ int ret = 0;
+ int i;
+
+ if (!list_empty(&imx_drm_encoder->possible_crtcs))
+ return -EBUSY;
+
+ for (i = 0; !ret; i++) {
+ ret = of_parse_phandle_with_args(np, "crtcs",
+ "#crtc-cells", i, &args);
+ if (ret < 0)
+ break;
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c) {
+ of_node_put(args.np);
+ return -ENOMEM;
+ }
+
+ c->cookie = args.np;
+ c->id = args.args_count > 0 ? args.args[0] : 0;
+
+ of_node_put(args.np);
+
+ mutex_lock(&imxdrm->mutex);
+
+ list_add_tail(&c->list, &imx_drm_encoder->possible_crtcs);
+
+ mutex_unlock(&imxdrm->mutex);
+ }
+
+ imx_drm_update_possible_crtcs();
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_drm_encoder_add_possible_crtcs);
+
+int imx_drm_encoder_get_mux_id(struct imx_drm_encoder *imx_drm_encoder,
+ struct drm_crtc *crtc)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_crtc *imx_crtc;
+ int i = 0;
+
+ mutex_lock(&imxdrm->mutex);
+
+ list_for_each_entry(imx_crtc, &imxdrm->crtc_list, list) {
+ if (imx_crtc->crtc == crtc)
+ goto found;
+ i++;
+ }
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return -EINVAL;
+found:
+ mutex_unlock(&imxdrm->mutex);
+
+ return i;
+}
+
+/*
+ * imx_drm_remove_encoder - remove an encoder
+ */
+int imx_drm_remove_encoder(struct imx_drm_encoder *imx_drm_encoder)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct crtc_cookie *c, *tmp;
+
+ mutex_lock(&imxdrm->mutex);
+
+ imx_drm_encoder_unregister(imx_drm_encoder);
+
+ list_del(&imx_drm_encoder->list);
+
+ list_for_each_entry_safe(c, tmp, &imx_drm_encoder->possible_crtcs,
+ list)
+ kfree(c);
+
+ mutex_unlock(&imxdrm->mutex);
+
+ kfree(imx_drm_encoder);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_drm_remove_encoder);
+
+/*
+ * imx_drm_add_connector - add a connector
+ */
+int imx_drm_add_connector(struct drm_connector *connector,
+ struct imx_drm_connector **new_con,
+ struct module *owner)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+ struct imx_drm_connector *imx_drm_connector;
+ int ret;
+
+ mutex_lock(&imxdrm->mutex);
+
+ if (imxdrm->references) {
+ ret = -EBUSY;
+ goto err_busy;
+ }
+
+ imx_drm_connector = kzalloc(sizeof(*imx_drm_connector), GFP_KERNEL);
+ if (!imx_drm_connector) {
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+
+ imx_drm_connector->connector = connector;
+ imx_drm_connector->owner = owner;
+
+ ret = imx_drm_connector_register(imx_drm_connector);
+ if (ret)
+ goto err_register;
+
+ list_add_tail(&imx_drm_connector->list, &imxdrm->connector_list);
+
+ *new_con = imx_drm_connector;
+
+ mutex_unlock(&imxdrm->mutex);
+
+ return 0;
+
+err_register:
+ kfree(imx_drm_connector);
+err_alloc:
+err_busy:
+ mutex_unlock(&imxdrm->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(imx_drm_add_connector);
+
+void imx_drm_fb_helper_set(struct drm_fbdev_cma *fbdev_helper)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ imxdrm->fbhelper = fbdev_helper;
+}
+EXPORT_SYMBOL_GPL(imx_drm_fb_helper_set);
+
+/*
+ * imx_drm_remove_connector - remove a connector
+ */
+int imx_drm_remove_connector(struct imx_drm_connector *imx_drm_connector)
+{
+ struct imx_drm_device *imxdrm = __imx_drm_device();
+
+ mutex_lock(&imxdrm->mutex);
+
+ imx_drm_connector_unregister(imx_drm_connector);
+
+ list_del(&imx_drm_connector->list);
+
+ mutex_unlock(&imxdrm->mutex);
+
+ kfree(imx_drm_connector);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(imx_drm_remove_connector);
+
+static struct drm_ioctl_desc imx_drm_ioctls[] = {
+ /* none so far */
+};
+
+static struct drm_driver imx_drm_driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM,
+ .load = imx_drm_driver_load,
+ .unload = imx_drm_driver_unload,
+ .firstopen = imx_drm_driver_firstopen,
+ .lastclose = imx_drm_driver_lastclose,
+ .gem_free_object = drm_gem_cma_free_object,
+ .gem_vm_ops = &drm_gem_cma_vm_ops,
+ .dumb_create = drm_gem_cma_dumb_create,
+ .dumb_map_offset = drm_gem_cma_dumb_map_offset,
+ .dumb_destroy = drm_gem_cma_dumb_destroy,
+
+ .get_vblank_counter = drm_vblank_count,
+ .enable_vblank = imx_drm_enable_vblank,
+ .disable_vblank = imx_drm_disable_vblank,
+ .ioctls = imx_drm_ioctls,
+ .num_ioctls = ARRAY_SIZE(imx_drm_ioctls),
+ .fops = &imx_drm_driver_fops,
+ .name = "imx-drm",
+ .desc = "i.MX DRM graphics",
+ .date = "20120507",
+ .major = 1,
+ .minor = 0,
+ .patchlevel = 0,
+};
+
+static int imx_drm_platform_probe(struct platform_device *pdev)
+{
+ imx_drm_device->dev = &pdev->dev;
+
+ return drm_platform_init(&imx_drm_driver, pdev);
+}
+
+static int imx_drm_platform_remove(struct platform_device *pdev)
+{
+ drm_platform_exit(&imx_drm_driver, pdev);
+
+ return 0;
+}
+
+static struct platform_driver imx_drm_pdrv = {
+ .probe = imx_drm_platform_probe,
+ .remove = __devexit_p(imx_drm_platform_remove),
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "imx-drm",
+ },
+};
+
+static struct platform_device *imx_drm_pdev;
+
+static int __init imx_drm_init(void)
+{
+ int ret;
+
+ imx_drm_device = kzalloc(sizeof(*imx_drm_device), GFP_KERNEL);
+ if (!imx_drm_device)
+ return -ENOMEM;
+
+ mutex_init(&imx_drm_device->mutex);
+ INIT_LIST_HEAD(&imx_drm_device->crtc_list);
+ INIT_LIST_HEAD(&imx_drm_device->connector_list);
+ INIT_LIST_HEAD(&imx_drm_device->encoder_list);
+
+ imx_drm_pdev = platform_device_register_simple("imx-drm", -1, NULL, 0);
+ if (!imx_drm_pdev) {
+ ret = -EINVAL;
+ goto err_pdev;
+ }
+
+ imx_drm_pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32),
+
+ ret = platform_driver_register(&imx_drm_pdrv);
+ if (ret)
+ goto err_pdrv;
+
+ return 0;
+
+err_pdrv:
+ platform_device_unregister(imx_drm_pdev);
+err_pdev:
+ kfree(imx_drm_device);
+
+ return ret;
+}
+
+static void __exit imx_drm_exit(void)
+{
+ platform_device_unregister(imx_drm_pdev);
+ platform_driver_unregister(&imx_drm_pdrv);
+
+ kfree(imx_drm_device);
+}
+
+module_init(imx_drm_init);
+module_exit(imx_drm_exit);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION("i.MX drm driver core");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/imx-drm/imx-drm.h b/drivers/staging/imx-drm/imx-drm.h
new file mode 100644
index 000000000000..ae28a490c445
--- /dev/null
+++ b/drivers/staging/imx-drm/imx-drm.h
@@ -0,0 +1,58 @@
+#ifndef _IMX_DRM_H_
+#define _IMX_DRM_H_
+
+struct imx_drm_crtc;
+struct drm_fbdev_cma;
+
+struct imx_drm_crtc_helper_funcs {
+ int (*enable_vblank)(struct drm_crtc *crtc);
+ void (*disable_vblank)(struct drm_crtc *crtc);
+ int (*set_interface_pix_fmt)(struct drm_crtc *crtc, u32 encoder_type,
+ u32 pix_fmt);
+ const struct drm_crtc_helper_funcs *crtc_helper_funcs;
+ const struct drm_crtc_funcs *crtc_funcs;
+};
+
+int imx_drm_add_crtc(struct drm_crtc *crtc,
+ struct imx_drm_crtc **new_crtc,
+ const struct imx_drm_crtc_helper_funcs *imx_helper_funcs,
+ struct module *owner, void *cookie, int id);
+int imx_drm_remove_crtc(struct imx_drm_crtc *);
+int imx_drm_init_drm(struct platform_device *pdev,
+ int preferred_bpp);
+int imx_drm_exit_drm(void);
+
+int imx_drm_crtc_vblank_get(struct imx_drm_crtc *imx_drm_crtc);
+void imx_drm_crtc_vblank_put(struct imx_drm_crtc *imx_drm_crtc);
+void imx_drm_handle_vblank(struct imx_drm_crtc *imx_drm_crtc);
+
+struct imx_drm_encoder;
+int imx_drm_add_encoder(struct drm_encoder *encoder,
+ struct imx_drm_encoder **new_enc,
+ struct module *owner);
+int imx_drm_remove_encoder(struct imx_drm_encoder *);
+
+struct imx_drm_connector;
+int imx_drm_add_connector(struct drm_connector *connector,
+ struct imx_drm_connector **new_con,
+ struct module *owner);
+int imx_drm_remove_connector(struct imx_drm_connector *);
+
+void imx_drm_mode_config_init(struct drm_device *drm);
+
+struct drm_gem_cma_object *imx_drm_fb_get_obj(struct drm_framebuffer *fb);
+
+struct drm_device *imx_drm_device_get(void);
+void imx_drm_device_put(void);
+int imx_drm_crtc_panel_format(struct drm_crtc *crtc, u32 encoder_type,
+ u32 interface_pix_fmt);
+void imx_drm_fb_helper_set(struct drm_fbdev_cma *fbdev_helper);
+
+struct device_node;
+
+int imx_drm_encoder_get_mux_id(struct imx_drm_encoder *imx_drm_encoder,
+ struct drm_crtc *crtc);
+int imx_drm_encoder_add_possible_crtcs(struct imx_drm_encoder *imx_drm_encoder,
+ struct device_node *np);
+
+#endif /* _IMX_DRM_H_ */
diff --git a/drivers/staging/imx-drm/imx-fb.c b/drivers/staging/imx-drm/imx-fb.c
new file mode 100644
index 000000000000..03a7b4e14f67
--- /dev/null
+++ b/drivers/staging/imx-drm/imx-fb.c
@@ -0,0 +1,47 @@
+/*
+ * i.MX drm driver
+ *
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * Based on Samsung Exynos code
+ *
+ * Copyright (c) 2011 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.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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 <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "imx-drm.h"
+
+static struct drm_mode_config_funcs imx_drm_mode_config_funcs = {
+ .fb_create = drm_fb_cma_create,
+};
+
+void imx_drm_mode_config_init(struct drm_device *dev)
+{
+ dev->mode_config.min_width = 64;
+ dev->mode_config.min_height = 64;
+
+ /*
+ * set max width and height as default value(4096x4096).
+ * this value would be used to check framebuffer size limitation
+ * at drm_mode_addfb().
+ */
+ dev->mode_config.max_width = 4096;
+ dev->mode_config.max_height = 4096;
+
+ dev->mode_config.funcs = &imx_drm_mode_config_funcs;
+}
diff --git a/drivers/staging/imx-drm/imx-fbdev.c b/drivers/staging/imx-drm/imx-fbdev.c
new file mode 100644
index 000000000000..8331739c3d08
--- /dev/null
+++ b/drivers/staging/imx-drm/imx-fbdev.c
@@ -0,0 +1,74 @@
+/*
+ * i.MX drm driver
+ *
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * Based on Samsung Exynos code
+ *
+ * Copyright (c) 2011 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.
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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 <drm/drmP.h>
+#include <drm/drm_crtc.h>
+#include <drm/drm_crtc_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "imx-drm.h"
+
+#define MAX_CONNECTOR 4
+#define PREFERRED_BPP 16
+
+static struct drm_fbdev_cma *fbdev_cma;
+
+static int legacyfb_depth = 16;
+
+module_param(legacyfb_depth, int, 0444);
+
+static int __init imx_fb_helper_init(void)
+{
+ struct drm_device *drm = imx_drm_device_get();
+
+ if (!drm)
+ return -EINVAL;
+
+ if (legacyfb_depth != 16 && legacyfb_depth != 32) {
+ pr_warn("i.MX legacyfb: invalid legacyfb_depth setting. defaulting to 16bpp\n");
+ legacyfb_depth = 16;
+ }
+
+ fbdev_cma = drm_fbdev_cma_init(drm, legacyfb_depth,
+ drm->mode_config.num_crtc, MAX_CONNECTOR);
+
+ if (IS_ERR(fbdev_cma)) {
+ imx_drm_device_put();
+ return PTR_ERR(fbdev_cma);
+ }
+
+ imx_drm_fb_helper_set(fbdev_cma);
+
+ return 0;
+}
+
+static void __exit imx_fb_helper_exit(void)
+{
+ imx_drm_fb_helper_set(NULL);
+ drm_fbdev_cma_fini(fbdev_cma);
+ imx_drm_device_put();
+}
+
+late_initcall(imx_fb_helper_init);
+module_exit(imx_fb_helper_exit);
+
+MODULE_DESCRIPTION("Freescale i.MX legacy fb driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/imx-drm/ipu-v3/Makefile b/drivers/staging/imx-drm/ipu-v3/Makefile
new file mode 100644
index 000000000000..28ed72e98a96
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_DRM_IMX_IPUV3_CORE) += imx-ipu-v3.o
+
+imx-ipu-v3-objs := ipu-common.o ipu-dc.o ipu-di.o ipu-dp.o ipu-dmfc.o
diff --git a/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h
new file mode 100644
index 000000000000..74158dd73758
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/imx-ipu-v3.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2005-2009 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU Lesser General
+ * Public License. You may obtain a copy of the GNU Lesser General
+ * Public License Version 2.1 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/lgpl-license.html
+ * http://www.gnu.org/copyleft/lgpl.html
+ */
+
+#ifndef __DRM_IPU_H__
+#define __DRM_IPU_H__
+
+#include <linux/types.h>
+#include <linux/videodev2.h>
+#include <linux/bitmap.h>
+#include <linux/fb.h>
+#include <linux/videodev2.h>
+
+struct ipu_soc;
+
+enum ipuv3_type {
+ IPUV3EX,
+ IPUV3M,
+ IPUV3H,
+};
+
+/*
+ * Bitfield of Display Interface signal polarities.
+ */
+struct ipu_di_signal_cfg {
+ unsigned datamask_en:1;
+ unsigned interlaced:1;
+ unsigned odd_field_first:1;
+ unsigned clksel_en:1;
+ unsigned clkidle_en:1;
+ unsigned data_pol:1; /* true = inverted */
+ unsigned clk_pol:1; /* true = rising edge */
+ unsigned enable_pol:1;
+ unsigned Hsync_pol:1; /* true = active high */
+ unsigned Vsync_pol:1;
+
+ u16 width;
+ u16 height;
+ u32 pixel_fmt;
+ u16 h_start_width;
+ u16 h_sync_width;
+ u16 h_end_width;
+ u16 v_start_width;
+ u16 v_sync_width;
+ u16 v_end_width;
+ u32 v_to_h_sync;
+ unsigned long pixelclock;
+#define IPU_DI_CLKMODE_SYNC (1 << 0)
+#define IPU_DI_CLKMODE_EXT (1 << 1)
+ unsigned long clkflags;
+};
+
+enum ipu_color_space {
+ IPUV3_COLORSPACE_RGB,
+ IPUV3_COLORSPACE_YUV,
+ IPUV3_COLORSPACE_UNKNOWN,
+};
+
+struct ipuv3_channel;
+
+enum ipu_channel_irq {
+ IPU_IRQ_EOF = 0,
+ IPU_IRQ_NFACK = 64,
+ IPU_IRQ_NFB4EOF = 128,
+ IPU_IRQ_EOS = 192,
+};
+
+int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
+ enum ipu_channel_irq irq);
+
+#define IPU_IRQ_DP_SF_START (448 + 2)
+#define IPU_IRQ_DP_SF_END (448 + 3)
+#define IPU_IRQ_BG_SF_END IPU_IRQ_DP_SF_END,
+#define IPU_IRQ_DC_FC_0 (448 + 8)
+#define IPU_IRQ_DC_FC_1 (448 + 9)
+#define IPU_IRQ_DC_FC_2 (448 + 10)
+#define IPU_IRQ_DC_FC_3 (448 + 11)
+#define IPU_IRQ_DC_FC_4 (448 + 12)
+#define IPU_IRQ_DC_FC_6 (448 + 13)
+#define IPU_IRQ_VSYNC_PRE_0 (448 + 14)
+#define IPU_IRQ_VSYNC_PRE_1 (448 + 15)
+
+/*
+ * IPU Image DMA Controller (idmac) functions
+ */
+struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned channel);
+void ipu_idmac_put(struct ipuv3_channel *);
+
+int ipu_idmac_enable_channel(struct ipuv3_channel *channel);
+int ipu_idmac_disable_channel(struct ipuv3_channel *channel);
+
+void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
+ bool doublebuffer);
+void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num);
+
+/*
+ * IPU Display Controller (dc) functions
+ */
+struct ipu_dc;
+struct ipu_di;
+struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel);
+void ipu_dc_put(struct ipu_dc *dc);
+int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
+ u32 pixel_fmt, u32 width);
+void ipu_dc_enable_channel(struct ipu_dc *dc);
+void ipu_dc_disable_channel(struct ipu_dc *dc);
+
+/*
+ * IPU Display Interface (di) functions
+ */
+struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp);
+void ipu_di_put(struct ipu_di *);
+int ipu_di_disable(struct ipu_di *);
+int ipu_di_enable(struct ipu_di *);
+int ipu_di_get_num(struct ipu_di *);
+int ipu_di_init_sync_panel(struct ipu_di *, struct ipu_di_signal_cfg *sig);
+
+/*
+ * IPU Display Multi FIFO Controller (dmfc) functions
+ */
+struct dmfc_channel;
+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc);
+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc);
+int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
+ unsigned long bandwidth_mbs, int burstsize);
+void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc);
+int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width);
+struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipuv3_channel);
+void ipu_dmfc_put(struct dmfc_channel *dmfc);
+
+/*
+ * IPU Display Processor (dp) functions
+ */
+#define IPU_DP_FLOW_SYNC_BG 0
+#define IPU_DP_FLOW_SYNC_FG 1
+#define IPU_DP_FLOW_ASYNC0_BG 2
+#define IPU_DP_FLOW_ASYNC0_FG 3
+#define IPU_DP_FLOW_ASYNC1_BG 4
+#define IPU_DP_FLOW_ASYNC1_FG 5
+
+struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow);
+void ipu_dp_put(struct ipu_dp *);
+int ipu_dp_enable_channel(struct ipu_dp *dp);
+void ipu_dp_disable_channel(struct ipu_dp *dp);
+int ipu_dp_setup_channel(struct ipu_dp *dp,
+ enum ipu_color_space in, enum ipu_color_space out);
+int ipu_dp_set_window_pos(struct ipu_dp *, u16 x_pos, u16 y_pos);
+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable, u8 alpha,
+ bool bg_chan);
+
+#define IPU_CPMEM_WORD(word, ofs, size) ((((word) * 160 + (ofs)) << 8) | (size))
+
+#define IPU_FIELD_UBO IPU_CPMEM_WORD(0, 46, 22)
+#define IPU_FIELD_VBO IPU_CPMEM_WORD(0, 68, 22)
+#define IPU_FIELD_IOX IPU_CPMEM_WORD(0, 90, 4)
+#define IPU_FIELD_RDRW IPU_CPMEM_WORD(0, 94, 1)
+#define IPU_FIELD_SO IPU_CPMEM_WORD(0, 113, 1)
+#define IPU_FIELD_SLY IPU_CPMEM_WORD(1, 102, 14)
+#define IPU_FIELD_SLUV IPU_CPMEM_WORD(1, 128, 14)
+
+#define IPU_FIELD_XV IPU_CPMEM_WORD(0, 0, 10)
+#define IPU_FIELD_YV IPU_CPMEM_WORD(0, 10, 9)
+#define IPU_FIELD_XB IPU_CPMEM_WORD(0, 19, 13)
+#define IPU_FIELD_YB IPU_CPMEM_WORD(0, 32, 12)
+#define IPU_FIELD_NSB_B IPU_CPMEM_WORD(0, 44, 1)
+#define IPU_FIELD_CF IPU_CPMEM_WORD(0, 45, 1)
+#define IPU_FIELD_SX IPU_CPMEM_WORD(0, 46, 12)
+#define IPU_FIELD_SY IPU_CPMEM_WORD(0, 58, 11)
+#define IPU_FIELD_NS IPU_CPMEM_WORD(0, 69, 10)
+#define IPU_FIELD_SDX IPU_CPMEM_WORD(0, 79, 7)
+#define IPU_FIELD_SM IPU_CPMEM_WORD(0, 86, 10)
+#define IPU_FIELD_SCC IPU_CPMEM_WORD(0, 96, 1)
+#define IPU_FIELD_SCE IPU_CPMEM_WORD(0, 97, 1)
+#define IPU_FIELD_SDY IPU_CPMEM_WORD(0, 98, 7)
+#define IPU_FIELD_SDRX IPU_CPMEM_WORD(0, 105, 1)
+#define IPU_FIELD_SDRY IPU_CPMEM_WORD(0, 106, 1)
+#define IPU_FIELD_BPP IPU_CPMEM_WORD(0, 107, 3)
+#define IPU_FIELD_DEC_SEL IPU_CPMEM_WORD(0, 110, 2)
+#define IPU_FIELD_DIM IPU_CPMEM_WORD(0, 112, 1)
+#define IPU_FIELD_BNDM IPU_CPMEM_WORD(0, 114, 3)
+#define IPU_FIELD_BM IPU_CPMEM_WORD(0, 117, 2)
+#define IPU_FIELD_ROT IPU_CPMEM_WORD(0, 119, 1)
+#define IPU_FIELD_HF IPU_CPMEM_WORD(0, 120, 1)
+#define IPU_FIELD_VF IPU_CPMEM_WORD(0, 121, 1)
+#define IPU_FIELD_THE IPU_CPMEM_WORD(0, 122, 1)
+#define IPU_FIELD_CAP IPU_CPMEM_WORD(0, 123, 1)
+#define IPU_FIELD_CAE IPU_CPMEM_WORD(0, 124, 1)
+#define IPU_FIELD_FW IPU_CPMEM_WORD(0, 125, 13)
+#define IPU_FIELD_FH IPU_CPMEM_WORD(0, 138, 12)
+#define IPU_FIELD_EBA0 IPU_CPMEM_WORD(1, 0, 29)
+#define IPU_FIELD_EBA1 IPU_CPMEM_WORD(1, 29, 29)
+#define IPU_FIELD_ILO IPU_CPMEM_WORD(1, 58, 20)
+#define IPU_FIELD_NPB IPU_CPMEM_WORD(1, 78, 7)
+#define IPU_FIELD_PFS IPU_CPMEM_WORD(1, 85, 4)
+#define IPU_FIELD_ALU IPU_CPMEM_WORD(1, 89, 1)
+#define IPU_FIELD_ALBM IPU_CPMEM_WORD(1, 90, 3)
+#define IPU_FIELD_ID IPU_CPMEM_WORD(1, 93, 2)
+#define IPU_FIELD_TH IPU_CPMEM_WORD(1, 95, 7)
+#define IPU_FIELD_SL IPU_CPMEM_WORD(1, 102, 14)
+#define IPU_FIELD_WID0 IPU_CPMEM_WORD(1, 116, 3)
+#define IPU_FIELD_WID1 IPU_CPMEM_WORD(1, 119, 3)
+#define IPU_FIELD_WID2 IPU_CPMEM_WORD(1, 122, 3)
+#define IPU_FIELD_WID3 IPU_CPMEM_WORD(1, 125, 3)
+#define IPU_FIELD_OFS0 IPU_CPMEM_WORD(1, 128, 5)
+#define IPU_FIELD_OFS1 IPU_CPMEM_WORD(1, 133, 5)
+#define IPU_FIELD_OFS2 IPU_CPMEM_WORD(1, 138, 5)
+#define IPU_FIELD_OFS3 IPU_CPMEM_WORD(1, 143, 5)
+#define IPU_FIELD_SXYS IPU_CPMEM_WORD(1, 148, 1)
+#define IPU_FIELD_CRE IPU_CPMEM_WORD(1, 149, 1)
+#define IPU_FIELD_DEC_SEL2 IPU_CPMEM_WORD(1, 150, 1)
+
+struct ipu_cpmem_word {
+ u32 data[5];
+ u32 res[3];
+};
+
+struct ipu_ch_param {
+ struct ipu_cpmem_word word[2];
+};
+
+void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v);
+u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs);
+struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel);
+void ipu_ch_param_dump(struct ipu_ch_param __iomem *p);
+
+static inline void ipu_ch_param_zero(struct ipu_ch_param __iomem *p)
+{
+ int i;
+ void __iomem *base = p;
+
+ for (i = 0; i < sizeof(*p) / sizeof(u32); i++)
+ writel(0, base + i * sizeof(u32));
+}
+
+static inline void ipu_cpmem_set_buffer(struct ipu_ch_param __iomem *p,
+ int bufnum, dma_addr_t buf)
+{
+ if (bufnum)
+ ipu_ch_param_write_field(p, IPU_FIELD_EBA1, buf >> 3);
+ else
+ ipu_ch_param_write_field(p, IPU_FIELD_EBA0, buf >> 3);
+}
+
+static inline void ipu_cpmem_set_resolution(struct ipu_ch_param __iomem *p,
+ int xres, int yres)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_FW, xres - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_FH, yres - 1);
+}
+
+static inline void ipu_cpmem_set_stride(struct ipu_ch_param __iomem *p,
+ int stride)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_SLY, stride - 1);
+}
+
+void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel);
+
+struct ipu_rgb {
+ struct fb_bitfield red;
+ struct fb_bitfield green;
+ struct fb_bitfield blue;
+ struct fb_bitfield transp;
+ int bits_per_pixel;
+};
+
+struct ipu_image {
+ struct v4l2_pix_format pix;
+ struct v4l2_rect rect;
+ dma_addr_t phys;
+};
+
+int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p,
+ int width);
+
+int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *,
+ struct ipu_rgb *rgb);
+
+static inline void ipu_cpmem_interlaced_scan(struct ipu_ch_param *p,
+ int stride)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_SO, 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_ILO, stride / 8);
+ ipu_ch_param_write_field(p, IPU_FIELD_SLY, (stride * 2) - 1);
+};
+
+void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format,
+ int stride, int height);
+void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p,
+ u32 pixel_format, int stride, int u_offset, int v_offset);
+int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 pixelformat);
+int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem,
+ struct ipu_image *image);
+
+enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat);
+
+static inline void ipu_cpmem_set_burstsize(struct ipu_ch_param __iomem *p,
+ int burstsize)
+{
+ ipu_ch_param_write_field(p, IPU_FIELD_NPB, burstsize - 1);
+};
+
+struct ipu_client_platformdata {
+ int di;
+ int dc;
+ int dp;
+ int dmfc;
+ int dma[2];
+};
+
+#endif /* __DRM_IPU_H__ */
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c
new file mode 100644
index 000000000000..f381960f42b0
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c
@@ -0,0 +1,1143 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/export.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/of_device.h>
+#include <asm/mach/irq.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+static inline u32 ipu_cm_read(struct ipu_soc *ipu, unsigned offset)
+{
+ return readl(ipu->cm_reg + offset);
+}
+
+static inline void ipu_cm_write(struct ipu_soc *ipu, u32 value, unsigned offset)
+{
+ writel(value, ipu->cm_reg + offset);
+}
+
+static inline u32 ipu_idmac_read(struct ipu_soc *ipu, unsigned offset)
+{
+ return readl(ipu->idmac_reg + offset);
+}
+
+static inline void ipu_idmac_write(struct ipu_soc *ipu, u32 value,
+ unsigned offset)
+{
+ writel(value, ipu->idmac_reg + offset);
+}
+
+void ipu_srm_dp_sync_update(struct ipu_soc *ipu)
+{
+ u32 val;
+
+ val = ipu_cm_read(ipu, IPU_SRM_PRI2);
+ val |= 0x8;
+ ipu_cm_write(ipu, val, IPU_SRM_PRI2);
+}
+EXPORT_SYMBOL_GPL(ipu_srm_dp_sync_update);
+
+struct ipu_ch_param __iomem *ipu_get_cpmem(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+
+ return ipu->cpmem_base + channel->num;
+}
+EXPORT_SYMBOL_GPL(ipu_get_cpmem);
+
+void ipu_cpmem_set_high_priority(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ struct ipu_ch_param __iomem *p = ipu_get_cpmem(channel);
+ u32 val;
+
+ if (ipu->ipu_type == IPUV3EX)
+ ipu_ch_param_write_field(p, IPU_FIELD_ID, 1);
+
+ val = ipu_idmac_read(ipu, IDMAC_CHA_PRI(channel->num));
+ val |= 1 << (channel->num % 32);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_PRI(channel->num));
+};
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_high_priority);
+
+void ipu_ch_param_write_field(struct ipu_ch_param __iomem *base, u32 wbs, u32 v)
+{
+ u32 bit = (wbs >> 8) % 160;
+ u32 size = wbs & 0xff;
+ u32 word = (wbs >> 8) / 160;
+ u32 i = bit / 32;
+ u32 ofs = bit % 32;
+ u32 mask = (1 << size) - 1;
+ u32 val;
+
+ pr_debug("%s %d %d %d\n", __func__, word, bit , size);
+
+ val = readl(&base->word[word].data[i]);
+ val &= ~(mask << ofs);
+ val |= v << ofs;
+ writel(val, &base->word[word].data[i]);
+
+ if ((bit + size - 1) / 32 > i) {
+ val = readl(&base->word[word].data[i + 1]);
+ val &= ~(mask >> (ofs ? (32 - ofs) : 0));
+ val |= v >> (ofs ? (32 - ofs) : 0);
+ writel(val, &base->word[word].data[i + 1]);
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_ch_param_write_field);
+
+u32 ipu_ch_param_read_field(struct ipu_ch_param __iomem *base, u32 wbs)
+{
+ u32 bit = (wbs >> 8) % 160;
+ u32 size = wbs & 0xff;
+ u32 word = (wbs >> 8) / 160;
+ u32 i = bit / 32;
+ u32 ofs = bit % 32;
+ u32 mask = (1 << size) - 1;
+ u32 val = 0;
+
+ pr_debug("%s %d %d %d\n", __func__, word, bit , size);
+
+ val = (readl(&base->word[word].data[i]) >> ofs) & mask;
+
+ if ((bit + size - 1) / 32 > i) {
+ u32 tmp;
+ tmp = readl(&base->word[word].data[i + 1]);
+ tmp &= mask >> (ofs ? (32 - ofs) : 0);
+ val |= tmp << (ofs ? (32 - ofs) : 0);
+ }
+
+ return val;
+}
+EXPORT_SYMBOL_GPL(ipu_ch_param_read_field);
+
+int ipu_cpmem_set_format_rgb(struct ipu_ch_param __iomem *p,
+ struct ipu_rgb *rgb)
+{
+ int bpp = 0, npb = 0, ro, go, bo, to;
+
+ ro = rgb->bits_per_pixel - rgb->red.length - rgb->red.offset;
+ go = rgb->bits_per_pixel - rgb->green.length - rgb->green.offset;
+ bo = rgb->bits_per_pixel - rgb->blue.length - rgb->blue.offset;
+ to = rgb->bits_per_pixel - rgb->transp.length - rgb->transp.offset;
+
+ ipu_ch_param_write_field(p, IPU_FIELD_WID0, rgb->red.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS0, ro);
+ ipu_ch_param_write_field(p, IPU_FIELD_WID1, rgb->green.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS1, go);
+ ipu_ch_param_write_field(p, IPU_FIELD_WID2, rgb->blue.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS2, bo);
+
+ if (rgb->transp.length) {
+ ipu_ch_param_write_field(p, IPU_FIELD_WID3,
+ rgb->transp.length - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS3, to);
+ } else {
+ ipu_ch_param_write_field(p, IPU_FIELD_WID3, 7);
+ ipu_ch_param_write_field(p, IPU_FIELD_OFS3,
+ rgb->bits_per_pixel);
+ }
+
+ switch (rgb->bits_per_pixel) {
+ case 32:
+ bpp = 0;
+ npb = 15;
+ break;
+ case 24:
+ bpp = 1;
+ npb = 19;
+ break;
+ case 16:
+ bpp = 3;
+ npb = 31;
+ break;
+ case 8:
+ bpp = 5;
+ npb = 63;
+ break;
+ default:
+ return -EINVAL;
+ }
+ ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp);
+ ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb);
+ ipu_ch_param_write_field(p, IPU_FIELD_PFS, 7); /* rgb mode */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_rgb);
+
+int ipu_cpmem_set_format_passthrough(struct ipu_ch_param __iomem *p,
+ int width)
+{
+ int bpp = 0, npb = 0;
+
+ switch (width) {
+ case 32:
+ bpp = 0;
+ npb = 15;
+ break;
+ case 24:
+ bpp = 1;
+ npb = 19;
+ break;
+ case 16:
+ bpp = 3;
+ npb = 31;
+ break;
+ case 8:
+ bpp = 5;
+ npb = 63;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ipu_ch_param_write_field(p, IPU_FIELD_BPP, bpp);
+ ipu_ch_param_write_field(p, IPU_FIELD_NPB, npb);
+ ipu_ch_param_write_field(p, IPU_FIELD_PFS, 6); /* raw mode */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_format_passthrough);
+
+void ipu_cpmem_set_yuv_planar_full(struct ipu_ch_param __iomem *p,
+ u32 pixel_format, int stride, int u_offset, int v_offset)
+{
+ switch (pixel_format) {
+ case V4L2_PIX_FMT_YUV420:
+ ipu_ch_param_write_field(p, IPU_FIELD_SLUV, (stride / 2) - 1);
+ ipu_ch_param_write_field(p, IPU_FIELD_UBO, u_offset / 8);
+ ipu_ch_param_write_field(p, IPU_FIELD_VBO, v_offset / 8);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar_full);
+
+void ipu_cpmem_set_yuv_planar(struct ipu_ch_param __iomem *p, u32 pixel_format,
+ int stride, int height)
+{
+ int u_offset, v_offset;
+ int uv_stride = 0;
+
+ switch (pixel_format) {
+ case V4L2_PIX_FMT_YUV420:
+ uv_stride = stride / 2;
+ u_offset = stride * height;
+ v_offset = u_offset + (uv_stride * height / 2);
+ ipu_cpmem_set_yuv_planar_full(p, V4L2_PIX_FMT_YUV420, stride,
+ u_offset, v_offset);
+ break;
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_yuv_planar);
+
+static struct ipu_rgb def_rgb_32 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 24, .length = 8, },
+ .bits_per_pixel = 32,
+};
+
+static struct ipu_rgb def_bgr_32 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 24, .length = 8, },
+ .bits_per_pixel = 32,
+};
+
+static struct ipu_rgb def_rgb_24 = {
+ .red = { .offset = 0, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 16, .length = 8, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 24,
+};
+
+static struct ipu_rgb def_bgr_24 = {
+ .red = { .offset = 16, .length = 8, },
+ .green = { .offset = 8, .length = 8, },
+ .blue = { .offset = 0, .length = 8, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 24,
+};
+
+static struct ipu_rgb def_rgb_16 = {
+ .red = { .offset = 11, .length = 5, },
+ .green = { .offset = 5, .length = 6, },
+ .blue = { .offset = 0, .length = 5, },
+ .transp = { .offset = 0, .length = 0, },
+ .bits_per_pixel = 16,
+};
+
+#define Y_OFFSET(pix, x, y) ((x) + pix->width * (y))
+#define U_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * (y) / 4) + (x) / 2)
+#define V_OFFSET(pix, x, y) ((pix->width * pix->height) + \
+ (pix->width * pix->height / 4) + \
+ (pix->width * (y) / 4) + (x) / 2)
+
+int ipu_cpmem_set_fmt(struct ipu_ch_param __iomem *cpmem, u32 pixelformat)
+{
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ /* pix format */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 2);
+ /* burst size */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 63);
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ /* bits/pixel */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3);
+ /* pix format */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0xA);
+ /* burst size */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ /* bits/pixel */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_BPP, 3);
+ /* pix format */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_PFS, 0x8);
+ /* burst size */
+ ipu_ch_param_write_field(cpmem, IPU_FIELD_NPB, 31);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ ipu_cpmem_set_format_rgb(cpmem, &def_rgb_32);
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ ipu_cpmem_set_format_rgb(cpmem, &def_rgb_16);
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ ipu_cpmem_set_format_rgb(cpmem, &def_bgr_32);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ ipu_cpmem_set_format_rgb(cpmem, &def_rgb_24);
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ ipu_cpmem_set_format_rgb(cpmem, &def_bgr_24);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_fmt);
+
+int ipu_cpmem_set_image(struct ipu_ch_param __iomem *cpmem,
+ struct ipu_image *image)
+{
+ struct v4l2_pix_format *pix = &image->pix;
+ int y_offset, u_offset, v_offset;
+
+ pr_debug("%s: resolution: %dx%d stride: %d\n",
+ __func__, pix->width, pix->height,
+ pix->bytesperline);
+
+ ipu_cpmem_set_resolution(cpmem, image->rect.width,
+ image->rect.height);
+ ipu_cpmem_set_stride(cpmem, pix->bytesperline);
+
+ ipu_cpmem_set_fmt(cpmem, pix->pixelformat);
+
+ switch (pix->pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ y_offset = Y_OFFSET(pix, image->rect.left, image->rect.top);
+ u_offset = U_OFFSET(pix, image->rect.left,
+ image->rect.top) - y_offset;
+ v_offset = V_OFFSET(pix, image->rect.left,
+ image->rect.top) - y_offset;
+
+ ipu_cpmem_set_yuv_planar_full(cpmem, pix->pixelformat,
+ pix->bytesperline, u_offset, v_offset);
+ ipu_cpmem_set_buffer(cpmem, 0, image->phys + y_offset);
+ break;
+ case V4L2_PIX_FMT_UYVY:
+ ipu_cpmem_set_buffer(cpmem, 0, image->phys +
+ image->rect.left * 2 +
+ image->rect.top * image->pix.bytesperline);
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ ipu_cpmem_set_buffer(cpmem, 0, image->phys +
+ image->rect.left * 4 +
+ image->rect.top * image->pix.bytesperline);
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ ipu_cpmem_set_buffer(cpmem, 0, image->phys +
+ image->rect.left * 2 +
+ image->rect.top * image->pix.bytesperline);
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ ipu_cpmem_set_buffer(cpmem, 0, image->phys +
+ image->rect.left * 3 +
+ image->rect.top * image->pix.bytesperline);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_cpmem_set_image);
+
+enum ipu_color_space ipu_pixelformat_to_colorspace(u32 pixelformat)
+{
+ switch (pixelformat) {
+ case V4L2_PIX_FMT_YUV420:
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_YVYU:
+ return IPUV3_COLORSPACE_YUV;
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32:
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB565:
+ return IPUV3_COLORSPACE_RGB;
+ default:
+ return IPUV3_COLORSPACE_UNKNOWN;
+ }
+}
+EXPORT_SYMBOL_GPL(ipu_pixelformat_to_colorspace);
+
+struct ipuv3_channel *ipu_idmac_get(struct ipu_soc *ipu, unsigned num)
+{
+ struct ipuv3_channel *channel;
+
+ dev_dbg(ipu->dev, "%s %d\n", __func__, num);
+
+ if (num > 63)
+ return ERR_PTR(-ENODEV);
+
+ mutex_lock(&ipu->channel_lock);
+
+ channel = &ipu->channel[num];
+
+ if (channel->busy) {
+ channel = ERR_PTR(-EBUSY);
+ goto out;
+ }
+
+ channel->busy = 1;
+ channel->num = num;
+
+out:
+ mutex_unlock(&ipu->channel_lock);
+
+ return channel;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_get);
+
+void ipu_idmac_put(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+
+ dev_dbg(ipu->dev, "%s %d\n", __func__, channel->num);
+
+ mutex_lock(&ipu->channel_lock);
+
+ channel->busy = 0;
+
+ mutex_unlock(&ipu->channel_lock);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_put);
+
+#define idma_mask(ch) (1 << (ch & 0x1f))
+
+void ipu_idmac_set_double_buffer(struct ipuv3_channel *channel,
+ bool doublebuffer)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ reg = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
+ if (doublebuffer)
+ reg |= idma_mask(channel->num);
+ else
+ reg &= ~idma_mask(channel->num);
+ ipu_cm_write(ipu, reg, IPU_CHA_DB_MODE_SEL(channel->num));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_set_double_buffer);
+
+int ipu_module_enable(struct ipu_soc *ipu, u32 mask)
+{
+ unsigned long lock_flags;
+ u32 val;
+
+ spin_lock_irqsave(&ipu->lock, lock_flags);
+
+ val = ipu_cm_read(ipu, IPU_DISP_GEN);
+
+ if (mask & IPU_CONF_DI0_EN)
+ val |= IPU_DI0_COUNTER_RELEASE;
+ if (mask & IPU_CONF_DI1_EN)
+ val |= IPU_DI1_COUNTER_RELEASE;
+
+ ipu_cm_write(ipu, val, IPU_DISP_GEN);
+
+ val = ipu_cm_read(ipu, IPU_CONF);
+ val |= mask;
+ ipu_cm_write(ipu, val, IPU_CONF);
+
+ spin_unlock_irqrestore(&ipu->lock, lock_flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_module_enable);
+
+int ipu_module_disable(struct ipu_soc *ipu, u32 mask)
+{
+ unsigned long lock_flags;
+ u32 val;
+
+ spin_lock_irqsave(&ipu->lock, lock_flags);
+
+ val = ipu_cm_read(ipu, IPU_CONF);
+ val &= ~mask;
+ ipu_cm_write(ipu, val, IPU_CONF);
+
+ val = ipu_cm_read(ipu, IPU_DISP_GEN);
+
+ if (mask & IPU_CONF_DI0_EN)
+ val &= ~IPU_DI0_COUNTER_RELEASE;
+ if (mask & IPU_CONF_DI1_EN)
+ val &= ~IPU_DI1_COUNTER_RELEASE;
+
+ ipu_cm_write(ipu, val, IPU_DISP_GEN);
+
+ spin_unlock_irqrestore(&ipu->lock, lock_flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_module_disable);
+
+void ipu_idmac_select_buffer(struct ipuv3_channel *channel, u32 buf_num)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ unsigned int chno = channel->num;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ /* Mark buffer as ready. */
+ if (buf_num == 0)
+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF0_RDY(chno));
+ else
+ ipu_cm_write(ipu, idma_mask(chno), IPU_CHA_BUF1_RDY(chno));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_select_buffer);
+
+int ipu_idmac_enable_channel(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
+ val |= idma_mask(channel->num);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_enable_channel);
+
+int ipu_idmac_disable_channel(struct ipuv3_channel *channel)
+{
+ struct ipu_soc *ipu = channel->ipu;
+ u32 val;
+ unsigned long flags;
+ unsigned long timeout;
+
+ timeout = jiffies + msecs_to_jiffies(50);
+ while (ipu_idmac_read(ipu, IDMAC_CHA_BUSY(channel->num)) &
+ idma_mask(channel->num)) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(ipu->dev, "disabling busy idmac channel %d\n",
+ channel->num);
+ break;
+ }
+ cpu_relax();
+ }
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ /* Disable DMA channel(s) */
+ val = ipu_idmac_read(ipu, IDMAC_CHA_EN(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_idmac_write(ipu, val, IDMAC_CHA_EN(channel->num));
+
+ /* Set channel buffers NOT to be ready */
+ ipu_cm_write(ipu, 0xf0000000, IPU_GPR); /* write one to clear */
+
+ if (ipu_cm_read(ipu, IPU_CHA_BUF0_RDY(channel->num)) &
+ idma_mask(channel->num)) {
+ ipu_cm_write(ipu, idma_mask(channel->num),
+ IPU_CHA_BUF0_RDY(channel->num));
+ }
+
+ if (ipu_cm_read(ipu, IPU_CHA_BUF1_RDY(channel->num)) &
+ idma_mask(channel->num)) {
+ ipu_cm_write(ipu, idma_mask(channel->num),
+ IPU_CHA_BUF1_RDY(channel->num));
+ }
+
+ ipu_cm_write(ipu, 0x0, IPU_GPR); /* write one to set */
+
+ /* Reset the double buffer */
+ val = ipu_cm_read(ipu, IPU_CHA_DB_MODE_SEL(channel->num));
+ val &= ~idma_mask(channel->num);
+ ipu_cm_write(ipu, val, IPU_CHA_DB_MODE_SEL(channel->num));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_disable_channel);
+
+static int ipu_reset(struct ipu_soc *ipu)
+{
+ unsigned long timeout;
+
+ ipu_cm_write(ipu, 0x807FFFFF, IPU_MEM_RST);
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+ while (ipu_cm_read(ipu, IPU_MEM_RST) & 0x80000000) {
+ if (time_after(jiffies, timeout))
+ return -ETIME;
+ cpu_relax();
+ }
+
+ mdelay(300);
+
+ return 0;
+}
+
+struct ipu_devtype {
+ const char *name;
+ unsigned long cm_ofs;
+ unsigned long cpmem_ofs;
+ unsigned long srm_ofs;
+ unsigned long tpm_ofs;
+ unsigned long disp0_ofs;
+ unsigned long disp1_ofs;
+ unsigned long dc_tmpl_ofs;
+ unsigned long vdi_ofs;
+ enum ipuv3_type type;
+};
+
+static struct ipu_devtype ipu_type_imx51 = {
+ .name = "IPUv3EX",
+ .cm_ofs = 0x1e000000,
+ .cpmem_ofs = 0x1f000000,
+ .srm_ofs = 0x1f040000,
+ .tpm_ofs = 0x1f060000,
+ .disp0_ofs = 0x1e040000,
+ .disp1_ofs = 0x1e048000,
+ .dc_tmpl_ofs = 0x1f080000,
+ .vdi_ofs = 0x1e068000,
+ .type = IPUV3EX,
+};
+
+static struct ipu_devtype ipu_type_imx53 = {
+ .name = "IPUv3M",
+ .cm_ofs = 0x06000000,
+ .cpmem_ofs = 0x07000000,
+ .srm_ofs = 0x07040000,
+ .tpm_ofs = 0x07060000,
+ .disp0_ofs = 0x06040000,
+ .disp1_ofs = 0x06048000,
+ .dc_tmpl_ofs = 0x07080000,
+ .vdi_ofs = 0x06068000,
+ .type = IPUV3M,
+};
+
+static struct ipu_devtype ipu_type_imx6q = {
+ .name = "IPUv3H",
+ .cm_ofs = 0x00200000,
+ .cpmem_ofs = 0x00300000,
+ .srm_ofs = 0x00340000,
+ .tpm_ofs = 0x00360000,
+ .disp0_ofs = 0x00240000,
+ .disp1_ofs = 0x00248000,
+ .dc_tmpl_ofs = 0x00380000,
+ .vdi_ofs = 0x00268000,
+ .type = IPUV3H,
+};
+
+static const struct of_device_id imx_ipu_dt_ids[] = {
+ { .compatible = "fsl,imx51-ipu", .data = &ipu_type_imx51, },
+ { .compatible = "fsl,imx53-ipu", .data = &ipu_type_imx53, },
+ { .compatible = "fsl,imx6q-ipu", .data = &ipu_type_imx6q, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, imx_ipu_dt_ids);
+
+static int ipu_submodules_init(struct ipu_soc *ipu,
+ struct platform_device *pdev, unsigned long ipu_base,
+ struct clk *ipu_clk)
+{
+ char *unit;
+ int ret;
+ struct device *dev = &pdev->dev;
+ const struct ipu_devtype *devtype = ipu->devtype;
+
+ ret = ipu_di_init(ipu, dev, 0, ipu_base + devtype->disp0_ofs,
+ IPU_CONF_DI0_EN, ipu_clk);
+ if (ret) {
+ unit = "di0";
+ goto err_di_0;
+ }
+
+ ret = ipu_di_init(ipu, dev, 1, ipu_base + devtype->disp1_ofs,
+ IPU_CONF_DI1_EN, ipu_clk);
+ if (ret) {
+ unit = "di1";
+ goto err_di_1;
+ }
+
+ ret = ipu_dc_init(ipu, dev, ipu_base + devtype->cm_ofs +
+ IPU_CM_DC_REG_OFS, ipu_base + devtype->dc_tmpl_ofs);
+ if (ret) {
+ unit = "dc_template";
+ goto err_dc;
+ }
+
+ ret = ipu_dmfc_init(ipu, dev, ipu_base +
+ devtype->cm_ofs + IPU_CM_DMFC_REG_OFS, ipu_clk);
+ if (ret) {
+ unit = "dmfc";
+ goto err_dmfc;
+ }
+
+ ret = ipu_dp_init(ipu, dev, ipu_base + devtype->srm_ofs);
+ if (ret) {
+ unit = "dp";
+ goto err_dp;
+ }
+
+ return 0;
+
+err_dp:
+ ipu_dmfc_exit(ipu);
+err_dmfc:
+ ipu_dc_exit(ipu);
+err_dc:
+ ipu_di_exit(ipu, 1);
+err_di_1:
+ ipu_di_exit(ipu, 0);
+err_di_0:
+ dev_err(&pdev->dev, "init %s failed with %d\n", unit, ret);
+ return ret;
+}
+
+static void ipu_irq_handle(struct ipu_soc *ipu, const int *regs, int num_regs)
+{
+ unsigned long status;
+ int i, bit, irq_base;
+
+ for (i = 0; i < num_regs; i++) {
+
+ status = ipu_cm_read(ipu, IPU_INT_STAT(regs[i]));
+ status &= ipu_cm_read(ipu, IPU_INT_CTRL(regs[i]));
+
+ irq_base = ipu->irq_start + regs[i] * 32;
+ for_each_set_bit(bit, &status, 32)
+ generic_handle_irq(irq_base + bit);
+ }
+}
+
+static void ipu_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
+ const int int_reg[] = { 0, 1, 2, 3, 10, 11, 12, 13, 14};
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ chained_irq_enter(chip, desc);
+
+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
+
+ chained_irq_exit(chip, desc);
+}
+
+static void ipu_err_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ struct ipu_soc *ipu = irq_desc_get_handler_data(desc);
+ const int int_reg[] = { 4, 5, 8, 9};
+ struct irq_chip *chip = irq_get_chip(irq);
+
+ chained_irq_enter(chip, desc);
+
+ ipu_irq_handle(ipu, int_reg, ARRAY_SIZE(int_reg));
+
+ chained_irq_exit(chip, desc);
+}
+
+static void ipu_ack_irq(struct irq_data *d)
+{
+ struct ipu_soc *ipu = irq_data_get_irq_chip_data(d);
+ unsigned int irq = d->irq - ipu->irq_start;
+
+ ipu_cm_write(ipu, 1 << (irq % 32), IPU_INT_STAT(irq / 32));
+}
+
+static void ipu_unmask_irq(struct irq_data *d)
+{
+ struct ipu_soc *ipu = irq_data_get_irq_chip_data(d);
+ unsigned int irq = d->irq - ipu->irq_start;
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ reg = ipu_cm_read(ipu, IPU_INT_CTRL(irq / 32));
+ reg |= 1 << (irq % 32);
+ ipu_cm_write(ipu, reg, IPU_INT_CTRL(irq / 32));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+
+static void ipu_mask_irq(struct irq_data *d)
+{
+ struct ipu_soc *ipu = irq_data_get_irq_chip_data(d);
+ unsigned int irq = d->irq - ipu->irq_start;
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&ipu->lock, flags);
+
+ reg = ipu_cm_read(ipu, IPU_INT_CTRL(irq / 32));
+ reg &= ~(1 << (irq % 32));
+ ipu_cm_write(ipu, reg, IPU_INT_CTRL(irq / 32));
+
+ spin_unlock_irqrestore(&ipu->lock, flags);
+}
+
+static struct irq_chip ipu_irq_chip = {
+ .name = "IPU",
+ .irq_ack = ipu_ack_irq,
+ .irq_mask = ipu_mask_irq,
+ .irq_unmask = ipu_unmask_irq,
+};
+
+int ipu_idmac_channel_irq(struct ipu_soc *ipu, struct ipuv3_channel *channel,
+ enum ipu_channel_irq irq_type)
+{
+ return ipu->irq_start + irq_type + channel->num;
+}
+EXPORT_SYMBOL_GPL(ipu_idmac_channel_irq);
+
+static void ipu_submodules_exit(struct ipu_soc *ipu)
+{
+ ipu_dp_exit(ipu);
+ ipu_dmfc_exit(ipu);
+ ipu_dc_exit(ipu);
+ ipu_di_exit(ipu, 1);
+ ipu_di_exit(ipu, 0);
+}
+
+static int platform_remove_devices_fn(struct device *dev, void *unused)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
+static void platform_device_unregister_children(struct platform_device *pdev)
+{
+ device_for_each_child(&pdev->dev, NULL, platform_remove_devices_fn);
+}
+
+struct ipu_platform_reg {
+ struct ipu_client_platformdata pdata;
+ const char *name;
+};
+
+static const struct ipu_platform_reg client_reg[] = {
+ {
+ .pdata = {
+ .di = 0,
+ .dc = 5,
+ .dp = IPU_DP_FLOW_SYNC_BG,
+ .dma[0] = IPUV3_CHANNEL_MEM_BG_SYNC,
+ .dma[1] = -EINVAL,
+ },
+ .name = "imx-ipuv3-crtc",
+ }, {
+ .pdata = {
+ .di = 1,
+ .dc = 1,
+ .dp = -EINVAL,
+ .dma[0] = IPUV3_CHANNEL_MEM_DC_SYNC,
+ .dma[1] = -EINVAL,
+ },
+ .name = "imx-ipuv3-crtc",
+ },
+};
+
+static int ipu_client_id;
+
+static int ipu_add_subdevice_pdata(struct device *dev,
+ const struct ipu_platform_reg *reg)
+{
+ struct platform_device *pdev;
+
+ pdev = platform_device_register_data(dev, reg->name, ipu_client_id++,
+ &reg->pdata, sizeof(struct ipu_platform_reg));
+
+ return pdev ? 0 : -EINVAL;
+}
+
+static int ipu_add_client_devices(struct ipu_soc *ipu)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(client_reg); i++) {
+ const struct ipu_platform_reg *reg = &client_reg[i];
+ ret = ipu_add_subdevice_pdata(ipu->dev, reg);
+ if (ret)
+ goto err_register;
+ }
+
+ return 0;
+
+err_register:
+ platform_device_unregister_children(to_platform_device(ipu->dev));
+
+ return ret;
+}
+
+static int ipu_irq_init(struct ipu_soc *ipu)
+{
+ int i;
+
+ ipu->irq_start = irq_alloc_descs(-1, 0, IPU_NUM_IRQS, 0);
+ if (ipu->irq_start < 0)
+ return ipu->irq_start;
+
+ for (i = ipu->irq_start; i < ipu->irq_start + IPU_NUM_IRQS; i++) {
+ irq_set_chip_and_handler(i, &ipu_irq_chip, handle_level_irq);
+ set_irq_flags(i, IRQF_VALID);
+ irq_set_chip_data(i, ipu);
+ }
+
+ irq_set_chained_handler(ipu->irq_sync, ipu_irq_handler);
+ irq_set_handler_data(ipu->irq_sync, ipu);
+ irq_set_chained_handler(ipu->irq_err, ipu_err_irq_handler);
+ irq_set_handler_data(ipu->irq_err, ipu);
+
+ return 0;
+}
+
+static void ipu_irq_exit(struct ipu_soc *ipu)
+{
+ int i;
+
+ irq_set_chained_handler(ipu->irq_err, NULL);
+ irq_set_handler_data(ipu->irq_err, NULL);
+ irq_set_chained_handler(ipu->irq_sync, NULL);
+ irq_set_handler_data(ipu->irq_sync, NULL);
+
+ for (i = ipu->irq_start; i < ipu->irq_start + IPU_NUM_IRQS; i++) {
+ set_irq_flags(i, 0);
+ irq_set_chip(i, NULL);
+ irq_set_chip_data(i, NULL);
+ }
+
+ irq_free_descs(ipu->irq_start, IPU_NUM_IRQS);
+}
+
+static int __devinit ipu_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *of_id =
+ of_match_device(imx_ipu_dt_ids, &pdev->dev);
+ struct ipu_soc *ipu;
+ struct resource *res;
+ unsigned long ipu_base;
+ int i, ret, irq_sync, irq_err;
+ const struct ipu_devtype *devtype;
+
+ devtype = of_id->data;
+
+ dev_info(&pdev->dev, "Initializing %s\n", devtype->name);
+
+ irq_sync = platform_get_irq(pdev, 0);
+ irq_err = platform_get_irq(pdev, 1);
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ dev_info(&pdev->dev, "irq_sync: %d irq_err: %d\n",
+ irq_sync, irq_err);
+
+ if (!res || irq_sync < 0 || irq_err < 0)
+ return -ENODEV;
+
+ ipu_base = res->start;
+
+ ipu = devm_kzalloc(&pdev->dev, sizeof(*ipu), GFP_KERNEL);
+ if (!ipu)
+ return -ENODEV;
+
+ for (i = 0; i < 64; i++)
+ ipu->channel[i].ipu = ipu;
+ ipu->devtype = devtype;
+ ipu->ipu_type = devtype->type;
+
+ spin_lock_init(&ipu->lock);
+ mutex_init(&ipu->channel_lock);
+
+ dev_info(&pdev->dev, "cm_reg: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs);
+ dev_info(&pdev->dev, "idmac: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS);
+ dev_info(&pdev->dev, "cpmem: 0x%08lx\n",
+ ipu_base + devtype->cpmem_ofs);
+ dev_info(&pdev->dev, "disp0: 0x%08lx\n",
+ ipu_base + devtype->disp0_ofs);
+ dev_info(&pdev->dev, "disp1: 0x%08lx\n",
+ ipu_base + devtype->disp1_ofs);
+ dev_info(&pdev->dev, "srm: 0x%08lx\n",
+ ipu_base + devtype->srm_ofs);
+ dev_info(&pdev->dev, "tpm: 0x%08lx\n",
+ ipu_base + devtype->tpm_ofs);
+ dev_info(&pdev->dev, "dc: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_DC_REG_OFS);
+ dev_info(&pdev->dev, "ic: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_IC_REG_OFS);
+ dev_info(&pdev->dev, "dmfc: 0x%08lx\n",
+ ipu_base + devtype->cm_ofs + IPU_CM_DMFC_REG_OFS);
+ dev_info(&pdev->dev, "vdi: 0x%08lx\n",
+ ipu_base + devtype->vdi_ofs);
+
+ ipu->cm_reg = devm_ioremap(&pdev->dev,
+ ipu_base + devtype->cm_ofs, PAGE_SIZE);
+ ipu->idmac_reg = devm_ioremap(&pdev->dev,
+ ipu_base + devtype->cm_ofs + IPU_CM_IDMAC_REG_OFS,
+ PAGE_SIZE);
+ ipu->cpmem_base = devm_ioremap(&pdev->dev,
+ ipu_base + devtype->cpmem_ofs, PAGE_SIZE);
+
+ if (!ipu->cm_reg || !ipu->idmac_reg || !ipu->cpmem_base) {
+ ret = -ENOMEM;
+ goto failed_ioremap;
+ }
+
+ ipu->clk = devm_clk_get(&pdev->dev, "bus");
+ if (IS_ERR(ipu->clk)) {
+ ret = PTR_ERR(ipu->clk);
+ dev_err(&pdev->dev, "clk_get failed with %d", ret);
+ goto failed_clk_get;
+ }
+
+ platform_set_drvdata(pdev, ipu);
+
+ clk_prepare_enable(ipu->clk);
+
+ ipu->dev = &pdev->dev;
+ ipu->irq_sync = irq_sync;
+ ipu->irq_err = irq_err;
+
+ ret = ipu_irq_init(ipu);
+ if (ret)
+ goto out_failed_irq;
+
+ ipu_reset(ipu);
+
+ /* Set MCU_T to divide MCU access window into 2 */
+ ipu_cm_write(ipu, 0x00400000L | (IPU_MCU_T_DEFAULT << 18),
+ IPU_DISP_GEN);
+
+ ret = ipu_submodules_init(ipu, pdev, ipu_base, ipu->clk);
+ if (ret)
+ goto failed_submodules_init;
+
+ ret = ipu_add_client_devices(ipu);
+ if (ret) {
+ dev_err(&pdev->dev, "adding client devices failed with %d\n",
+ ret);
+ goto failed_add_clients;
+ }
+
+ return 0;
+
+failed_add_clients:
+ ipu_submodules_exit(ipu);
+failed_submodules_init:
+ ipu_irq_exit(ipu);
+out_failed_irq:
+ clk_disable_unprepare(ipu->clk);
+failed_clk_get:
+failed_ioremap:
+ return ret;
+}
+
+static int __devexit ipu_remove(struct platform_device *pdev)
+{
+ struct ipu_soc *ipu = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ platform_device_unregister_children(pdev);
+ ipu_submodules_exit(ipu);
+ ipu_irq_exit(ipu);
+
+ clk_disable_unprepare(ipu->clk);
+
+ return 0;
+}
+
+static struct platform_driver imx_ipu_driver = {
+ .driver = {
+ .name = "imx-ipuv3",
+ .of_match_table = imx_ipu_dt_ids,
+ },
+ .probe = ipu_probe,
+ .remove = __devexit_p(ipu_remove),
+};
+
+module_platform_driver(imx_ipu_driver);
+
+MODULE_DESCRIPTION("i.MX IPU v3 driver");
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c
new file mode 100644
index 000000000000..93c7579417be
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-dc.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/export.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+#define DC_MAP_CONF_PTR(n) (0x108 + ((n) & ~0x1) * 2)
+#define DC_MAP_CONF_VAL(n) (0x144 + ((n) & ~0x1) * 2)
+
+#define DC_EVT_NF 0
+#define DC_EVT_NL 1
+#define DC_EVT_EOF 2
+#define DC_EVT_NFIELD 3
+#define DC_EVT_EOL 4
+#define DC_EVT_EOFIELD 5
+#define DC_EVT_NEW_ADDR 6
+#define DC_EVT_NEW_CHAN 7
+#define DC_EVT_NEW_DATA 8
+
+#define DC_EVT_NEW_ADDR_W_0 0
+#define DC_EVT_NEW_ADDR_W_1 1
+#define DC_EVT_NEW_CHAN_W_0 2
+#define DC_EVT_NEW_CHAN_W_1 3
+#define DC_EVT_NEW_DATA_W_0 4
+#define DC_EVT_NEW_DATA_W_1 5
+#define DC_EVT_NEW_ADDR_R_0 6
+#define DC_EVT_NEW_ADDR_R_1 7
+#define DC_EVT_NEW_CHAN_R_0 8
+#define DC_EVT_NEW_CHAN_R_1 9
+#define DC_EVT_NEW_DATA_R_0 10
+#define DC_EVT_NEW_DATA_R_1 11
+
+#define DC_WR_CH_CONF 0x0
+#define DC_WR_CH_ADDR 0x4
+#define DC_RL_CH(evt) (8 + ((evt) & ~0x1) * 2)
+
+#define DC_GEN 0xd4
+#define DC_DISP_CONF1(disp) (0xd8 + (disp) * 4)
+#define DC_DISP_CONF2(disp) (0xe8 + (disp) * 4)
+#define DC_STAT 0x1c8
+
+#define WROD(lf) (0x18 | ((lf) << 1))
+#define WRG 0x01
+
+#define SYNC_WAVE 0
+
+#define DC_GEN_SYNC_1_6_SYNC (2 << 1)
+#define DC_GEN_SYNC_PRIORITY_1 (1 << 7)
+
+#define DC_WR_CH_CONF_WORD_SIZE_8 (0 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_16 (1 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_24 (2 << 0)
+#define DC_WR_CH_CONF_WORD_SIZE_32 (3 << 0)
+#define DC_WR_CH_CONF_DISP_ID_PARALLEL(i) (((i) & 0x1) << 3)
+#define DC_WR_CH_CONF_DISP_ID_SERIAL (2 << 3)
+#define DC_WR_CH_CONF_DISP_ID_ASYNC (3 << 4)
+#define DC_WR_CH_CONF_FIELD_MODE (1 << 9)
+#define DC_WR_CH_CONF_PROG_TYPE_NORMAL (4 << 5)
+#define DC_WR_CH_CONF_PROG_TYPE_MASK (7 << 5)
+#define DC_WR_CH_CONF_PROG_DI_ID (1 << 2)
+#define DC_WR_CH_CONF_PROG_DISP_ID(i) (((i) & 0x1) << 3)
+
+#define IPU_DC_NUM_CHANNELS 10
+
+struct ipu_dc_priv;
+
+enum ipu_dc_map {
+ IPU_DC_MAP_RGB24,
+ IPU_DC_MAP_RGB565,
+};
+
+struct ipu_dc {
+ /* The display interface number assigned to this dc channel */
+ unsigned int di;
+ void __iomem *base;
+ struct ipu_dc_priv *priv;
+ int chno;
+ bool in_use;
+};
+
+struct ipu_dc_priv {
+ void __iomem *dc_reg;
+ void __iomem *dc_tmpl_reg;
+ struct ipu_soc *ipu;
+ struct device *dev;
+ struct ipu_dc channels[IPU_DC_NUM_CHANNELS];
+ struct mutex mutex;
+};
+
+static void dc_link_event(struct ipu_dc *dc, int event, int addr, int priority)
+{
+ u32 reg;
+
+ reg = readl(dc->base + DC_RL_CH(event));
+ reg &= ~(0xffff << (16 * (event & 0x1)));
+ reg |= ((addr << 8) | priority) << (16 * (event & 0x1));
+ writel(reg, dc->base + DC_RL_CH(event));
+}
+
+static void dc_write_tmpl(struct ipu_dc *dc, int word, u32 opcode, u32 operand,
+ int map, int wave, int glue, int sync)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ u32 reg;
+ int stop = 1;
+
+ reg = sync | glue << 4 | ++wave << 11 | ++map << 15 | ((operand << 20) & 0xfff00000);
+ writel(reg, priv->dc_tmpl_reg + word * 8);
+ reg = operand >> 12 | opcode << 4 | stop << 9;
+ writel(reg, priv->dc_tmpl_reg + word * 8 + 4);
+}
+
+static int ipu_pixfmt_to_map(u32 fmt)
+{
+ switch (fmt) {
+ case V4L2_PIX_FMT_RGB24:
+ return IPU_DC_MAP_RGB24;
+ case V4L2_PIX_FMT_RGB565:
+ return IPU_DC_MAP_RGB565;
+ default:
+ return -EINVAL;
+ }
+}
+
+int ipu_dc_init_sync(struct ipu_dc *dc, struct ipu_di *di, bool interlaced,
+ u32 pixel_fmt, u32 width)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ u32 reg = 0, map;
+
+ dc->di = ipu_di_get_num(di);
+
+ map = ipu_pixfmt_to_map(pixel_fmt);
+ if (map < 0) {
+ dev_dbg(priv->dev, "IPU_DISP: No MAP\n");
+ return -EINVAL;
+ }
+
+ if (interlaced) {
+ dc_link_event(dc, DC_EVT_NL, 0, 3);
+ dc_link_event(dc, DC_EVT_EOL, 0, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 0, 1);
+
+ /* Init template microcode */
+ dc_write_tmpl(dc, 0, WROD(0), 0, map, SYNC_WAVE, 0, 8);
+ } else {
+ if (dc->di) {
+ dc_link_event(dc, DC_EVT_NL, 2, 3);
+ dc_link_event(dc, DC_EVT_EOL, 3, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 4, 1);
+ /* Init template microcode */
+ dc_write_tmpl(dc, 2, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ dc_write_tmpl(dc, 3, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ dc_write_tmpl(dc, 4, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ } else {
+ dc_link_event(dc, DC_EVT_NL, 5, 3);
+ dc_link_event(dc, DC_EVT_EOL, 6, 2);
+ dc_link_event(dc, DC_EVT_NEW_DATA, 7, 1);
+ /* Init template microcode */
+ dc_write_tmpl(dc, 5, WROD(0), 0, map, SYNC_WAVE, 8, 5);
+ dc_write_tmpl(dc, 6, WROD(0), 0, map, SYNC_WAVE, 4, 5);
+ dc_write_tmpl(dc, 7, WROD(0), 0, map, SYNC_WAVE, 0, 5);
+ }
+ }
+ dc_link_event(dc, DC_EVT_NF, 0, 0);
+ dc_link_event(dc, DC_EVT_NFIELD, 0, 0);
+ dc_link_event(dc, DC_EVT_EOF, 0, 0);
+ dc_link_event(dc, DC_EVT_EOFIELD, 0, 0);
+ dc_link_event(dc, DC_EVT_NEW_CHAN, 0, 0);
+ dc_link_event(dc, DC_EVT_NEW_ADDR, 0, 0);
+
+ reg = readl(dc->base + DC_WR_CH_CONF);
+ if (interlaced)
+ reg |= DC_WR_CH_CONF_FIELD_MODE;
+ else
+ reg &= ~DC_WR_CH_CONF_FIELD_MODE;
+ writel(reg, dc->base + DC_WR_CH_CONF);
+
+ writel(0x0, dc->base + DC_WR_CH_ADDR);
+ writel(width, priv->dc_reg + DC_DISP_CONF2(dc->di));
+
+ ipu_module_enable(priv->ipu, IPU_CONF_DC_EN);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dc_init_sync);
+
+void ipu_dc_enable_channel(struct ipu_dc *dc)
+{
+ int di;
+ u32 reg;
+
+ di = dc->di;
+
+ reg = readl(dc->base + DC_WR_CH_CONF);
+ reg |= DC_WR_CH_CONF_PROG_TYPE_NORMAL;
+ writel(reg, dc->base + DC_WR_CH_CONF);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_enable_channel);
+
+void ipu_dc_disable_channel(struct ipu_dc *dc)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+ u32 val;
+ int irq = 0, timeout = 50;
+
+ if (dc->chno == 1)
+ irq = IPU_IRQ_DC_FC_1;
+ else if (dc->chno == 5)
+ irq = IPU_IRQ_DP_SF_END;
+ else
+ return;
+
+ /* should wait for the interrupt here */
+ mdelay(50);
+
+ if (dc->di == 0)
+ val = 0x00000002;
+ else
+ val = 0x00000020;
+
+ /* Wait for DC triple buffer to empty */
+ while ((readl(priv->dc_reg + DC_STAT) & val) != val) {
+ msleep(2);
+ timeout -= 2;
+ if (timeout <= 0)
+ break;
+ }
+
+ val = readl(dc->base + DC_WR_CH_CONF);
+ val &= ~DC_WR_CH_CONF_PROG_TYPE_MASK;
+ writel(val, dc->base + DC_WR_CH_CONF);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_disable_channel);
+
+static void ipu_dc_map_config(struct ipu_dc_priv *priv, enum ipu_dc_map map,
+ int byte_num, int offset, int mask)
+{
+ int ptr = map * 3 + byte_num;
+ u32 reg;
+
+ reg = readl(priv->dc_reg + DC_MAP_CONF_VAL(ptr));
+ reg &= ~(0xffff << (16 * (ptr & 0x1)));
+ reg |= ((offset << 8) | mask) << (16 * (ptr & 0x1));
+ writel(reg, priv->dc_reg + DC_MAP_CONF_VAL(ptr));
+
+ reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
+ reg &= ~(0x1f << ((16 * (map & 0x1)) + (5 * byte_num)));
+ reg |= ptr << ((16 * (map & 0x1)) + (5 * byte_num));
+ writel(reg, priv->dc_reg + DC_MAP_CONF_PTR(map));
+}
+
+static void ipu_dc_map_clear(struct ipu_dc_priv *priv, int map)
+{
+ u32 reg = readl(priv->dc_reg + DC_MAP_CONF_PTR(map));
+
+ writel(reg & ~(0xffff << (16 * (map & 0x1))),
+ priv->dc_reg + DC_MAP_CONF_PTR(map));
+}
+
+struct ipu_dc *ipu_dc_get(struct ipu_soc *ipu, int channel)
+{
+ struct ipu_dc_priv *priv = ipu->dc_priv;
+ struct ipu_dc *dc;
+
+ if (channel >= IPU_DC_NUM_CHANNELS)
+ return ERR_PTR(-ENODEV);
+
+ dc = &priv->channels[channel];
+
+ mutex_lock(&priv->mutex);
+
+ if (dc->in_use) {
+ mutex_unlock(&priv->mutex);
+ return ERR_PTR(-EBUSY);
+ }
+
+ dc->in_use = 1;
+
+ mutex_unlock(&priv->mutex);
+
+ return dc;
+}
+EXPORT_SYMBOL_GPL(ipu_dc_get);
+
+void ipu_dc_put(struct ipu_dc *dc)
+{
+ struct ipu_dc_priv *priv = dc->priv;
+
+ mutex_lock(&priv->mutex);
+ dc->in_use = 0;
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dc_put);
+
+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev,
+ unsigned long base, unsigned long template_base)
+{
+ struct ipu_dc_priv *priv;
+ static int channel_offsets[] = { 0, 0x1c, 0x38, 0x54, 0x58, 0x5c,
+ 0x78, 0, 0x94, 0xb4};
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ mutex_init(&priv->mutex);
+
+ priv->dev = dev;
+ priv->ipu = ipu;
+ priv->dc_reg = devm_ioremap(dev, base, PAGE_SIZE);
+ priv->dc_tmpl_reg = devm_ioremap(dev, template_base, PAGE_SIZE);
+ if (!priv->dc_reg || !priv->dc_tmpl_reg)
+ return -ENOMEM;
+
+ for (i = 0; i < IPU_DC_NUM_CHANNELS; i++) {
+ priv->channels[i].chno = i;
+ priv->channels[i].priv = priv;
+ priv->channels[i].base = priv->dc_reg + channel_offsets[i];
+ }
+
+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(1) |
+ DC_WR_CH_CONF_PROG_DI_ID,
+ priv->channels[1].base + DC_WR_CH_CONF);
+ writel(DC_WR_CH_CONF_WORD_SIZE_24 | DC_WR_CH_CONF_DISP_ID_PARALLEL(0),
+ priv->channels[5].base + DC_WR_CH_CONF);
+
+ writel(DC_GEN_SYNC_1_6_SYNC | DC_GEN_SYNC_PRIORITY_1, priv->dc_reg + DC_GEN);
+
+ ipu->dc_priv = priv;
+
+ dev_dbg(dev, "DC base: 0x%08lx template base: 0x%08lx\n",
+ base, template_base);
+
+ /* rgb24 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB24);
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 0, 7, 0xff); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 1, 15, 0xff); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB24, 2, 23, 0xff); /* red */
+
+ /* rgb565 */
+ ipu_dc_map_clear(priv, IPU_DC_MAP_RGB565);
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 0, 4, 0xf8); /* blue */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 1, 10, 0xfc); /* green */
+ ipu_dc_map_config(priv, IPU_DC_MAP_RGB565, 2, 15, 0xf8); /* red */
+
+ return 0;
+}
+
+void ipu_dc_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-di.c b/drivers/staging/imx-drm/ipu-v3/ipu-di.c
new file mode 100644
index 000000000000..67d974f7be36
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-di.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/export.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+struct ipu_di {
+ void __iomem *base;
+ int id;
+ u32 module;
+ struct clk *clk_di; /* display input clock */
+ struct clk *clk_ipu; /* IPU bus clock */
+ struct clk *clk_di_pixel; /* resulting pixel clock */
+ struct clk_hw clk_hw_out;
+ char *clk_name;
+ bool inuse;
+ unsigned long clkflags;
+ struct ipu_soc *ipu;
+};
+
+static DEFINE_MUTEX(di_mutex);
+
+struct di_sync_config {
+ int run_count;
+ int run_src;
+ int offset_count;
+ int offset_src;
+ int repeat_count;
+ int cnt_clr_src;
+ int cnt_polarity_gen_en;
+ int cnt_polarity_clr_src;
+ int cnt_polarity_trigger_src;
+ int cnt_up;
+ int cnt_down;
+};
+
+enum di_pins {
+ DI_PIN11 = 0,
+ DI_PIN12 = 1,
+ DI_PIN13 = 2,
+ DI_PIN14 = 3,
+ DI_PIN15 = 4,
+ DI_PIN16 = 5,
+ DI_PIN17 = 6,
+ DI_PIN_CS = 7,
+
+ DI_PIN_SER_CLK = 0,
+ DI_PIN_SER_RS = 1,
+};
+
+enum di_sync_wave {
+ DI_SYNC_NONE = 0,
+ DI_SYNC_CLK = 1,
+ DI_SYNC_INT_HSYNC = 2,
+ DI_SYNC_HSYNC = 3,
+ DI_SYNC_VSYNC = 4,
+ DI_SYNC_DE = 6,
+};
+
+#define SYNC_WAVE 0
+
+#define DI_GENERAL 0x0000
+#define DI_BS_CLKGEN0 0x0004
+#define DI_BS_CLKGEN1 0x0008
+#define DI_SW_GEN0(gen) (0x000c + 4 * ((gen) - 1))
+#define DI_SW_GEN1(gen) (0x0030 + 4 * ((gen) - 1))
+#define DI_STP_REP(gen) (0x0148 + 4 * (((gen) - 1)/2))
+#define DI_SYNC_AS_GEN 0x0054
+#define DI_DW_GEN(gen) (0x0058 + 4 * (gen))
+#define DI_DW_SET(gen, set) (0x0088 + 4 * ((gen) + 0xc * (set)))
+#define DI_SER_CONF 0x015c
+#define DI_SSC 0x0160
+#define DI_POL 0x0164
+#define DI_AW0 0x0168
+#define DI_AW1 0x016c
+#define DI_SCR_CONF 0x0170
+#define DI_STAT 0x0174
+
+#define DI_SW_GEN0_RUN_COUNT(x) ((x) << 19)
+#define DI_SW_GEN0_RUN_SRC(x) ((x) << 16)
+#define DI_SW_GEN0_OFFSET_COUNT(x) ((x) << 3)
+#define DI_SW_GEN0_OFFSET_SRC(x) ((x) << 0)
+
+#define DI_SW_GEN1_CNT_POL_GEN_EN(x) ((x) << 29)
+#define DI_SW_GEN1_CNT_CLR_SRC(x) ((x) << 25)
+#define DI_SW_GEN1_CNT_POL_TRIGGER_SRC(x) ((x) << 12)
+#define DI_SW_GEN1_CNT_POL_CLR_SRC(x) ((x) << 9)
+#define DI_SW_GEN1_CNT_DOWN(x) ((x) << 16)
+#define DI_SW_GEN1_CNT_UP(x) (x)
+#define DI_SW_GEN1_AUTO_RELOAD (0x10000000)
+
+#define DI_DW_GEN_ACCESS_SIZE_OFFSET 24
+#define DI_DW_GEN_COMPONENT_SIZE_OFFSET 16
+
+#define DI_GEN_POLARITY_1 (1 << 0)
+#define DI_GEN_POLARITY_2 (1 << 1)
+#define DI_GEN_POLARITY_3 (1 << 2)
+#define DI_GEN_POLARITY_4 (1 << 3)
+#define DI_GEN_POLARITY_5 (1 << 4)
+#define DI_GEN_POLARITY_6 (1 << 5)
+#define DI_GEN_POLARITY_7 (1 << 6)
+#define DI_GEN_POLARITY_8 (1 << 7)
+#define DI_GEN_POLARITY_DISP_CLK (1 << 17)
+#define DI_GEN_DI_CLK_EXT (1 << 20)
+#define DI_GEN_DI_VSYNC_EXT (1 << 21)
+
+#define DI_POL_DRDY_DATA_POLARITY (1 << 7)
+#define DI_POL_DRDY_POLARITY_15 (1 << 4)
+
+#define DI_VSYNC_SEL_OFFSET 13
+
+static inline u32 ipu_di_read(struct ipu_di *di, unsigned offset)
+{
+ return readl(di->base + offset);
+}
+
+static inline void ipu_di_write(struct ipu_di *di, u32 value, unsigned offset)
+{
+ writel(value, di->base + offset);
+}
+
+static int ipu_di_clk_calc_div(unsigned long inrate, unsigned long outrate)
+{
+ u64 tmp = inrate;
+ int div;
+
+ tmp *= 16;
+
+ do_div(tmp, outrate);
+
+ div = tmp;
+
+ if (div < 0x10)
+ div = 0x10;
+
+#ifdef WTF_IS_THIS
+ /*
+ * Freescale has this in their Kernel. It is neither clear what
+ * it does nor why it does it
+ */
+ if (div & 0x10)
+ div &= ~0x7;
+ else {
+ /* Round up divider if it gets us closer to desired pix clk */
+ if ((div & 0xC) == 0xC) {
+ div += 0x10;
+ div &= ~0xF;
+ }
+ }
+#endif
+ return div;
+}
+
+static unsigned long clk_di_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
+ unsigned long outrate;
+ u32 div = ipu_di_read(di, DI_BS_CLKGEN0);
+
+ if (div < 0x10)
+ div = 0x10;
+
+ outrate = (parent_rate / div) * 16;
+
+ return outrate;
+}
+
+static long clk_di_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
+ unsigned long outrate;
+ int div;
+ u32 val;
+
+ div = ipu_di_clk_calc_div(*prate, rate);
+
+ outrate = (*prate / div) * 16;
+
+ val = ipu_di_read(di, DI_GENERAL);
+
+ if (!(val & DI_GEN_DI_CLK_EXT) && outrate > *prate / 2)
+ outrate = *prate / 2;
+
+ dev_dbg(di->ipu->dev,
+ "%s: inrate: %ld div: 0x%08x outrate: %ld wanted: %ld\n",
+ __func__, *prate, div, outrate, rate);
+
+ return outrate;
+}
+
+static int clk_di_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
+ int div;
+ u32 clkgen0;
+
+ clkgen0 = ipu_di_read(di, DI_BS_CLKGEN0) & ~0xfff;
+
+ div = ipu_di_clk_calc_div(parent_rate, rate);
+
+ ipu_di_write(di, clkgen0 | div, DI_BS_CLKGEN0);
+
+ dev_dbg(di->ipu->dev, "%s: inrate: %ld desired: %ld div: 0x%08x\n",
+ __func__, parent_rate, rate, div);
+ return 0;
+}
+
+static u8 clk_di_get_parent(struct clk_hw *hw)
+{
+ struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
+ u32 val;
+
+ val = ipu_di_read(di, DI_GENERAL);
+
+ return val & DI_GEN_DI_CLK_EXT ? 1 : 0;
+}
+
+static int clk_di_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct ipu_di *di = container_of(hw, struct ipu_di, clk_hw_out);
+ u32 val;
+
+ val = ipu_di_read(di, DI_GENERAL);
+
+ if (index)
+ val |= DI_GEN_DI_CLK_EXT;
+ else
+ val &= ~DI_GEN_DI_CLK_EXT;
+
+ ipu_di_write(di, val, DI_GENERAL);
+
+ return 0;
+}
+
+static struct clk_ops clk_di_ops = {
+ .round_rate = clk_di_round_rate,
+ .set_rate = clk_di_set_rate,
+ .recalc_rate = clk_di_recalc_rate,
+ .set_parent = clk_di_set_parent,
+ .get_parent = clk_di_get_parent,
+};
+
+static void ipu_di_data_wave_config(struct ipu_di *di,
+ int wave_gen,
+ int access_size, int component_size)
+{
+ u32 reg;
+ reg = (access_size << DI_DW_GEN_ACCESS_SIZE_OFFSET) |
+ (component_size << DI_DW_GEN_COMPONENT_SIZE_OFFSET);
+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
+}
+
+static void ipu_di_data_pin_config(struct ipu_di *di, int wave_gen, int di_pin,
+ int set, int up, int down)
+{
+ u32 reg;
+
+ reg = ipu_di_read(di, DI_DW_GEN(wave_gen));
+ reg &= ~(0x3 << (di_pin * 2));
+ reg |= set << (di_pin * 2);
+ ipu_di_write(di, reg, DI_DW_GEN(wave_gen));
+
+ ipu_di_write(di, (down << 16) | up, DI_DW_SET(wave_gen, set));
+}
+
+static void ipu_di_sync_config(struct ipu_di *di, struct di_sync_config *config,
+ int start, int count)
+{
+ u32 reg;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct di_sync_config *c = &config[i];
+ int wave_gen = start + i + 1;
+
+ if ((c->run_count >= 0x1000) || (c->offset_count >= 0x1000) ||
+ (c->repeat_count >= 0x1000) ||
+ (c->cnt_up >= 0x400) ||
+ (c->cnt_down >= 0x400)) {
+ dev_err(di->ipu->dev, "DI%d counters out of range.\n",
+ di->id);
+ return;
+ }
+
+ reg = DI_SW_GEN0_RUN_COUNT(c->run_count) |
+ DI_SW_GEN0_RUN_SRC(c->run_src) |
+ DI_SW_GEN0_OFFSET_COUNT(c->offset_count) |
+ DI_SW_GEN0_OFFSET_SRC(c->offset_src);
+ ipu_di_write(di, reg, DI_SW_GEN0(wave_gen));
+
+ reg = DI_SW_GEN1_CNT_POL_GEN_EN(c->cnt_polarity_gen_en) |
+ DI_SW_GEN1_CNT_CLR_SRC(c->cnt_clr_src) |
+ DI_SW_GEN1_CNT_POL_TRIGGER_SRC(
+ c->cnt_polarity_trigger_src) |
+ DI_SW_GEN1_CNT_POL_CLR_SRC(c->cnt_polarity_clr_src) |
+ DI_SW_GEN1_CNT_DOWN(c->cnt_down) |
+ DI_SW_GEN1_CNT_UP(c->cnt_up);
+
+ /* Enable auto reload */
+ if (c->repeat_count == 0)
+ reg |= DI_SW_GEN1_AUTO_RELOAD;
+
+ ipu_di_write(di, reg, DI_SW_GEN1(wave_gen));
+
+ reg = ipu_di_read(di, DI_STP_REP(wave_gen));
+ reg &= ~(0xffff << (16 * ((wave_gen - 1) & 0x1)));
+ reg |= c->repeat_count << (16 * ((wave_gen - 1) & 0x1));
+ ipu_di_write(di, reg, DI_STP_REP(wave_gen));
+ }
+}
+
+static void ipu_di_sync_config_interlaced(struct ipu_di *di,
+ struct ipu_di_signal_cfg *sig)
+{
+ u32 h_total = sig->width + sig->h_sync_width +
+ sig->h_start_width + sig->h_end_width;
+ u32 v_total = sig->height + sig->v_sync_width +
+ sig->v_start_width + sig->v_end_width;
+ u32 reg;
+ struct di_sync_config cfg[] = {
+ {
+ .run_count = h_total / 2 - 1,
+ .run_src = DI_SYNC_CLK,
+ }, {
+ .run_count = h_total - 11,
+ .run_src = DI_SYNC_CLK,
+ .cnt_down = 4,
+ }, {
+ .run_count = v_total * 2 - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = 1,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = 4,
+ }, {
+ .run_count = v_total / 2 - 1,
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = sig->v_start_width,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = 2,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ }, {
+ .run_src = DI_SYNC_HSYNC,
+ .repeat_count = sig->height / 2,
+ .cnt_clr_src = 4,
+ }, {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_HSYNC,
+ }, {
+ .run_count = v_total / 2 - 1,
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = 9,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = 2,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ }, {
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 5,
+ }, {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .offset_count = v_total / 2,
+ .offset_src = DI_SYNC_INT_HSYNC,
+ .cnt_clr_src = DI_SYNC_HSYNC,
+ .cnt_down = 4,
+ }
+ };
+
+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
+
+ /* set gentime select and tag sel */
+ reg = ipu_di_read(di, DI_SW_GEN1(9));
+ reg &= 0x1FFFFFFF;
+ reg |= (3 - 1) << 29 | 0x00008000;
+ ipu_di_write(di, reg, DI_SW_GEN1(9));
+
+ ipu_di_write(di, v_total / 2 - 1, DI_SCR_CONF);
+}
+
+static void ipu_di_sync_config_noninterlaced(struct ipu_di *di,
+ struct ipu_di_signal_cfg *sig, int div)
+{
+ u32 h_total = sig->width + sig->h_sync_width + sig->h_start_width +
+ sig->h_end_width;
+ u32 v_total = sig->height + sig->v_sync_width + sig->v_start_width +
+ sig->v_end_width;
+ struct di_sync_config cfg[] = {
+ {
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ } , {
+ .run_count = h_total - 1,
+ .run_src = DI_SYNC_CLK,
+ .offset_count = div * sig->v_to_h_sync,
+ .offset_src = DI_SYNC_CLK,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_CLK,
+ .cnt_down = sig->h_sync_width * 2,
+ } , {
+ .run_count = v_total - 1,
+ .run_src = DI_SYNC_INT_HSYNC,
+ .cnt_polarity_gen_en = 1,
+ .cnt_polarity_trigger_src = DI_SYNC_INT_HSYNC,
+ .cnt_down = sig->v_sync_width * 2,
+ } , {
+ .run_src = DI_SYNC_HSYNC,
+ .offset_count = sig->v_sync_width + sig->v_start_width,
+ .offset_src = DI_SYNC_HSYNC,
+ .repeat_count = sig->height,
+ .cnt_clr_src = DI_SYNC_VSYNC,
+ } , {
+ .run_src = DI_SYNC_CLK,
+ .offset_count = sig->h_sync_width + sig->h_start_width,
+ .offset_src = DI_SYNC_CLK,
+ .repeat_count = sig->width,
+ .cnt_clr_src = 5,
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ } , {
+ /* unused */
+ },
+ };
+
+ ipu_di_write(di, v_total - 1, DI_SCR_CONF);
+ ipu_di_sync_config(di, cfg, 0, ARRAY_SIZE(cfg));
+}
+
+int ipu_di_init_sync_panel(struct ipu_di *di, struct ipu_di_signal_cfg *sig)
+{
+ u32 reg;
+ u32 di_gen, vsync_cnt;
+ u32 div;
+ u32 h_total, v_total;
+ int ret;
+ unsigned long round;
+ struct clk *parent;
+
+ dev_dbg(di->ipu->dev, "disp %d: panel size = %d x %d\n",
+ di->id, sig->width, sig->height);
+
+ if ((sig->v_sync_width == 0) || (sig->h_sync_width == 0))
+ return -EINVAL;
+
+ if (sig->clkflags & IPU_DI_CLKMODE_EXT)
+ parent = di->clk_di;
+ else
+ parent = di->clk_ipu;
+
+ ret = clk_set_parent(di->clk_di_pixel, parent);
+ if (ret) {
+ dev_err(di->ipu->dev,
+ "setting pixel clock to parent %s failed with %d\n",
+ __clk_get_name(parent), ret);
+ return ret;
+ }
+
+ if (sig->clkflags & IPU_DI_CLKMODE_SYNC)
+ round = clk_get_rate(parent);
+ else
+ round = clk_round_rate(di->clk_di_pixel, sig->pixelclock);
+
+ ret = clk_set_rate(di->clk_di_pixel, round);
+
+ h_total = sig->width + sig->h_sync_width + sig->h_start_width +
+ sig->h_end_width;
+ v_total = sig->height + sig->v_sync_width + sig->v_start_width +
+ sig->v_end_width;
+
+ mutex_lock(&di_mutex);
+
+ div = ipu_di_read(di, DI_BS_CLKGEN0) & 0xfff;
+ div = div / 16; /* Now divider is integer portion */
+
+ /* Setup pixel clock timing */
+ /* Down time is half of period */
+ ipu_di_write(di, (div << 16), DI_BS_CLKGEN1);
+
+ ipu_di_data_wave_config(di, SYNC_WAVE, div - 1, div - 1);
+ ipu_di_data_pin_config(di, SYNC_WAVE, DI_PIN15, 3, 0, div * 2);
+
+ di_gen = ipu_di_read(di, DI_GENERAL) & DI_GEN_DI_CLK_EXT;
+ di_gen |= DI_GEN_DI_VSYNC_EXT;
+
+ if (sig->interlaced) {
+ ipu_di_sync_config_interlaced(di, sig);
+
+ /* set y_sel = 1 */
+ di_gen |= 0x10000000;
+ di_gen |= DI_GEN_POLARITY_5;
+ di_gen |= DI_GEN_POLARITY_8;
+
+ vsync_cnt = 7;
+
+ if (sig->Hsync_pol)
+ di_gen |= DI_GEN_POLARITY_3;
+ if (sig->Vsync_pol)
+ di_gen |= DI_GEN_POLARITY_2;
+ } else {
+ ipu_di_sync_config_noninterlaced(di, sig, div);
+
+ vsync_cnt = 3;
+
+ if (sig->Hsync_pol)
+ di_gen |= DI_GEN_POLARITY_2;
+ if (sig->Vsync_pol)
+ di_gen |= DI_GEN_POLARITY_3;
+ }
+
+ if (!sig->clk_pol)
+ di_gen |= DI_GEN_POLARITY_DISP_CLK;
+
+ ipu_di_write(di, di_gen, DI_GENERAL);
+
+ ipu_di_write(di, (--vsync_cnt << DI_VSYNC_SEL_OFFSET) | 0x00000002,
+ DI_SYNC_AS_GEN);
+
+ reg = ipu_di_read(di, DI_POL);
+ reg &= ~(DI_POL_DRDY_DATA_POLARITY | DI_POL_DRDY_POLARITY_15);
+
+ if (sig->enable_pol)
+ reg |= DI_POL_DRDY_POLARITY_15;
+ if (sig->data_pol)
+ reg |= DI_POL_DRDY_DATA_POLARITY;
+
+ ipu_di_write(di, reg, DI_POL);
+
+ mutex_unlock(&di_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_init_sync_panel);
+
+int ipu_di_enable(struct ipu_di *di)
+{
+ clk_prepare_enable(di->clk_di_pixel);
+
+ ipu_module_enable(di->ipu, di->module);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_enable);
+
+int ipu_di_disable(struct ipu_di *di)
+{
+ ipu_module_disable(di->ipu, di->module);
+
+ clk_disable_unprepare(di->clk_di_pixel);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_di_disable);
+
+int ipu_di_get_num(struct ipu_di *di)
+{
+ return di->id;
+}
+EXPORT_SYMBOL_GPL(ipu_di_get_num);
+
+static DEFINE_MUTEX(ipu_di_lock);
+
+struct ipu_di *ipu_di_get(struct ipu_soc *ipu, int disp)
+{
+ struct ipu_di *di;
+
+ if (disp > 1)
+ return ERR_PTR(-EINVAL);
+
+ di = ipu->di_priv[disp];
+
+ mutex_lock(&ipu_di_lock);
+
+ if (di->inuse) {
+ di = ERR_PTR(-EBUSY);
+ goto out;
+ }
+
+ di->inuse = true;
+out:
+ mutex_unlock(&ipu_di_lock);
+
+ return di;
+}
+EXPORT_SYMBOL_GPL(ipu_di_get);
+
+void ipu_di_put(struct ipu_di *di)
+{
+ mutex_lock(&ipu_di_lock);
+
+ di->inuse = false;
+
+ mutex_unlock(&ipu_di_lock);
+}
+EXPORT_SYMBOL_GPL(ipu_di_put);
+
+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
+ unsigned long base,
+ u32 module, struct clk *clk_ipu)
+{
+ struct ipu_di *di;
+ int ret;
+ const char *di_parent[2];
+ struct clk_init_data init = {
+ .ops = &clk_di_ops,
+ .num_parents = 2,
+ .flags = 0,
+ };
+
+ if (id > 1)
+ return -ENODEV;
+
+ di = devm_kzalloc(dev, sizeof(*di), GFP_KERNEL);
+ if (!di)
+ return -ENOMEM;
+
+ ipu->di_priv[id] = di;
+
+ di->clk_di = devm_clk_get(dev, id ? "di1" : "di0");
+ if (IS_ERR(di->clk_di))
+ return PTR_ERR(di->clk_di);
+
+ di->module = module;
+ di->id = id;
+ di->clk_ipu = clk_ipu;
+ di->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!di->base)
+ return -ENOMEM;
+
+ di_parent[0] = __clk_get_name(di->clk_ipu);
+ di_parent[1] = __clk_get_name(di->clk_di);
+
+ ipu_di_write(di, 0x10, DI_BS_CLKGEN0);
+
+ init.parent_names = (const char **)&di_parent;
+ di->clk_name = kasprintf(GFP_KERNEL, "%s_di%d_pixel",
+ dev_name(dev), id);
+ if (!di->clk_name)
+ return -ENOMEM;
+
+ init.name = di->clk_name;
+
+ di->clk_hw_out.init = &init;
+ di->clk_di_pixel = clk_register(dev, &di->clk_hw_out);
+
+ if (IS_ERR(di->clk_di_pixel)) {
+ ret = PTR_ERR(di->clk_di_pixel);
+ goto failed_clk_register;
+ }
+
+ dev_info(dev, "DI%d base: 0x%08lx remapped to %p\n",
+ id, base, di->base);
+ di->inuse = false;
+ di->ipu = ipu;
+
+ return 0;
+
+failed_clk_register:
+
+ kfree(di->clk_name);
+
+ return ret;
+}
+
+void ipu_di_exit(struct ipu_soc *ipu, int id)
+{
+ struct ipu_di *di = ipu->di_priv[id];
+
+ clk_unregister(di->clk_di_pixel);
+ kfree(di->clk_name);
+}
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c
new file mode 100644
index 000000000000..91821bc30f41
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-dmfc.c
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/export.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+#define DMFC_RD_CHAN 0x0000
+#define DMFC_WR_CHAN 0x0004
+#define DMFC_WR_CHAN_DEF 0x0008
+#define DMFC_DP_CHAN 0x000c
+#define DMFC_DP_CHAN_DEF 0x0010
+#define DMFC_GENERAL1 0x0014
+#define DMFC_GENERAL2 0x0018
+#define DMFC_IC_CTRL 0x001c
+#define DMFC_STAT 0x0020
+
+#define DMFC_WR_CHAN_1_28 0
+#define DMFC_WR_CHAN_2_41 8
+#define DMFC_WR_CHAN_1C_42 16
+#define DMFC_WR_CHAN_2C_43 24
+
+#define DMFC_DP_CHAN_5B_23 0
+#define DMFC_DP_CHAN_5F_27 8
+#define DMFC_DP_CHAN_6B_24 16
+#define DMFC_DP_CHAN_6F_29 24
+
+#define DMFC_FIFO_SIZE_64 (3 << 3)
+#define DMFC_FIFO_SIZE_128 (2 << 3)
+#define DMFC_FIFO_SIZE_256 (1 << 3)
+#define DMFC_FIFO_SIZE_512 (0 << 3)
+
+#define DMFC_SEGMENT(x) ((x & 0x7) << 0)
+#define DMFC_BURSTSIZE_128 (0 << 6)
+#define DMFC_BURSTSIZE_64 (1 << 6)
+#define DMFC_BURSTSIZE_32 (2 << 6)
+#define DMFC_BURSTSIZE_16 (3 << 6)
+
+struct dmfc_channel_data {
+ int ipu_channel;
+ unsigned long channel_reg;
+ unsigned long shift;
+ unsigned eot_shift;
+ unsigned max_fifo_lines;
+};
+
+static const struct dmfc_channel_data dmfcdata[] = {
+ {
+ .ipu_channel = 23,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_5B_23,
+ .eot_shift = 20,
+ .max_fifo_lines = 3,
+ }, {
+ .ipu_channel = 24,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_6B_24,
+ .eot_shift = 22,
+ .max_fifo_lines = 1,
+ }, {
+ .ipu_channel = 27,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_5F_27,
+ .eot_shift = 21,
+ .max_fifo_lines = 2,
+ }, {
+ .ipu_channel = 28,
+ .channel_reg = DMFC_WR_CHAN,
+ .shift = DMFC_WR_CHAN_1_28,
+ .eot_shift = 16,
+ .max_fifo_lines = 2,
+ }, {
+ .ipu_channel = 29,
+ .channel_reg = DMFC_DP_CHAN,
+ .shift = DMFC_DP_CHAN_6F_29,
+ .eot_shift = 23,
+ .max_fifo_lines = 1,
+ },
+};
+
+#define DMFC_NUM_CHANNELS ARRAY_SIZE(dmfcdata)
+
+struct ipu_dmfc_priv;
+
+struct dmfc_channel {
+ unsigned slots;
+ unsigned slotmask;
+ unsigned segment;
+ int burstsize;
+ struct ipu_soc *ipu;
+ struct ipu_dmfc_priv *priv;
+ const struct dmfc_channel_data *data;
+};
+
+struct ipu_dmfc_priv {
+ struct ipu_soc *ipu;
+ struct device *dev;
+ struct dmfc_channel channels[DMFC_NUM_CHANNELS];
+ struct mutex mutex;
+ unsigned long bandwidth_per_slot;
+ void __iomem *base;
+ int use_count;
+};
+
+int ipu_dmfc_enable_channel(struct dmfc_channel *dmfc)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ mutex_lock(&priv->mutex);
+
+ if (!priv->use_count)
+ ipu_module_enable(priv->ipu, IPU_CONF_DMFC_EN);
+
+ priv->use_count++;
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_enable_channel);
+
+void ipu_dmfc_disable_channel(struct dmfc_channel *dmfc)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+
+ mutex_lock(&priv->mutex);
+
+ priv->use_count--;
+
+ if (!priv->use_count)
+ ipu_module_disable(priv->ipu, IPU_CONF_DMFC_EN);
+
+ if (priv->use_count < 0)
+ priv->use_count = 0;
+
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_disable_channel);
+
+static int ipu_dmfc_setup_channel(struct dmfc_channel *dmfc, int slots,
+ int segment, int burstsize)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ u32 val, field;
+
+ dev_dbg(priv->dev,
+ "dmfc: using %d slots starting from segment %d for IPU channel %d\n",
+ slots, segment, dmfc->data->ipu_channel);
+
+ if (!dmfc)
+ return -EINVAL;
+
+ switch (slots) {
+ case 1:
+ field = DMFC_FIFO_SIZE_64;
+ break;
+ case 2:
+ field = DMFC_FIFO_SIZE_128;
+ break;
+ case 4:
+ field = DMFC_FIFO_SIZE_256;
+ break;
+ case 8:
+ field = DMFC_FIFO_SIZE_512;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (burstsize) {
+ case 16:
+ field |= DMFC_BURSTSIZE_16;
+ break;
+ case 32:
+ field |= DMFC_BURSTSIZE_32;
+ break;
+ case 64:
+ field |= DMFC_BURSTSIZE_64;
+ break;
+ case 128:
+ field |= DMFC_BURSTSIZE_128;
+ break;
+ }
+
+ field |= DMFC_SEGMENT(segment);
+
+ val = readl(priv->base + dmfc->data->channel_reg);
+
+ val &= ~(0xff << dmfc->data->shift);
+ val |= field << dmfc->data->shift;
+
+ writel(val, priv->base + dmfc->data->channel_reg);
+
+ dmfc->slots = slots;
+ dmfc->segment = segment;
+ dmfc->burstsize = burstsize;
+ dmfc->slotmask = ((1 << slots) - 1) << segment;
+
+ return 0;
+}
+
+static int dmfc_bandwidth_to_slots(struct ipu_dmfc_priv *priv,
+ unsigned long bandwidth)
+{
+ int slots = 1;
+
+ while (slots * priv->bandwidth_per_slot < bandwidth)
+ slots *= 2;
+
+ return slots;
+}
+
+static int dmfc_find_slots(struct ipu_dmfc_priv *priv, int slots)
+{
+ unsigned slotmask_need, slotmask_used = 0;
+ int i, segment = 0;
+
+ slotmask_need = (1 << slots) - 1;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ slotmask_used |= priv->channels[i].slotmask;
+
+ while (slotmask_need <= 0xff) {
+ if (!(slotmask_used & slotmask_need))
+ return segment;
+
+ slotmask_need <<= 1;
+ segment++;
+ }
+
+ return -EBUSY;
+}
+
+void ipu_dmfc_free_bandwidth(struct dmfc_channel *dmfc)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ int i;
+
+ dev_dbg(priv->dev, "dmfc: freeing %d slots starting from segment %d\n",
+ dmfc->slots, dmfc->segment);
+
+ mutex_lock(&priv->mutex);
+
+ if (!dmfc->slots)
+ goto out;
+
+ dmfc->slotmask = 0;
+ dmfc->slots = 0;
+ dmfc->segment = 0;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ priv->channels[i].slotmask = 0;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ if (priv->channels[i].slots > 0) {
+ priv->channels[i].segment =
+ dmfc_find_slots(priv, priv->channels[i].slots);
+ priv->channels[i].slotmask =
+ ((1 << priv->channels[i].slots) - 1) <<
+ priv->channels[i].segment;
+ }
+ }
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ if (priv->channels[i].slots > 0)
+ ipu_dmfc_setup_channel(&priv->channels[i],
+ priv->channels[i].slots,
+ priv->channels[i].segment,
+ priv->channels[i].burstsize);
+ }
+out:
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_free_bandwidth);
+
+int ipu_dmfc_alloc_bandwidth(struct dmfc_channel *dmfc,
+ unsigned long bandwidth_pixel_per_second, int burstsize)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ int slots = dmfc_bandwidth_to_slots(priv, bandwidth_pixel_per_second);
+ int segment = 0, ret = 0;
+
+ dev_dbg(priv->dev, "dmfc: trying to allocate %ldMpixel/s for IPU channel %d\n",
+ bandwidth_pixel_per_second / 1000000,
+ dmfc->data->ipu_channel);
+
+ ipu_dmfc_free_bandwidth(dmfc);
+
+ mutex_lock(&priv->mutex);
+
+ if (slots > 8) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ segment = dmfc_find_slots(priv, slots);
+ if (segment < 0) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+ ipu_dmfc_setup_channel(dmfc, slots, segment, burstsize);
+
+out:
+ mutex_unlock(&priv->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_alloc_bandwidth);
+
+int ipu_dmfc_init_channel(struct dmfc_channel *dmfc, int width)
+{
+ struct ipu_dmfc_priv *priv = dmfc->priv;
+ u32 dmfc_gen1;
+
+ dmfc_gen1 = readl(priv->base + DMFC_GENERAL1);
+
+ if ((dmfc->slots * 64 * 4) / width > dmfc->data->max_fifo_lines)
+ dmfc_gen1 |= 1 << dmfc->data->eot_shift;
+ else
+ dmfc_gen1 &= ~(1 << dmfc->data->eot_shift);
+
+ writel(dmfc_gen1, priv->base + DMFC_GENERAL1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_init_channel);
+
+struct dmfc_channel *ipu_dmfc_get(struct ipu_soc *ipu, int ipu_channel)
+{
+ struct ipu_dmfc_priv *priv = ipu->dmfc_priv;
+ int i;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++)
+ if (dmfcdata[i].ipu_channel == ipu_channel)
+ return &priv->channels[i];
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_get);
+
+void ipu_dmfc_put(struct dmfc_channel *dmfc)
+{
+ ipu_dmfc_free_bandwidth(dmfc);
+}
+EXPORT_SYMBOL_GPL(ipu_dmfc_put);
+
+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
+ struct clk *ipu_clk)
+{
+ struct ipu_dmfc_priv *priv;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!priv->base)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->ipu = ipu;
+ mutex_init(&priv->mutex);
+
+ ipu->dmfc_priv = priv;
+
+ for (i = 0; i < DMFC_NUM_CHANNELS; i++) {
+ priv->channels[i].priv = priv;
+ priv->channels[i].ipu = ipu;
+ priv->channels[i].data = &dmfcdata[i];
+ }
+
+ writel(0x0, priv->base + DMFC_WR_CHAN);
+ writel(0x0, priv->base + DMFC_DP_CHAN);
+
+ /*
+ * We have a total bandwidth of clkrate * 4pixel divided
+ * into 8 slots.
+ */
+ priv->bandwidth_per_slot = clk_get_rate(ipu_clk) / 8;
+
+ dev_dbg(dev, "dmfc: 8 slots with %ldMpixel/s bandwidth each\n",
+ priv->bandwidth_per_slot / 1000000);
+
+ writel(0x202020f6, priv->base + DMFC_WR_CHAN_DEF);
+ writel(0x2020f6f6, priv->base + DMFC_DP_CHAN_DEF);
+ writel(0x00000003, priv->base + DMFC_GENERAL1);
+
+ return 0;
+}
+
+void ipu_dmfc_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-dp.c b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c
new file mode 100644
index 000000000000..26aecaf9677f
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-dp.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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/export.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+#include "imx-ipu-v3.h"
+#include "ipu-prv.h"
+
+#define DP_SYNC 0
+#define DP_ASYNC0 0x60
+#define DP_ASYNC1 0xBC
+
+#define DP_COM_CONF 0x0
+#define DP_GRAPH_WIND_CTRL 0x0004
+#define DP_FG_POS 0x0008
+#define DP_CSC_A_0 0x0044
+#define DP_CSC_A_1 0x0048
+#define DP_CSC_A_2 0x004C
+#define DP_CSC_A_3 0x0050
+#define DP_CSC_0 0x0054
+#define DP_CSC_1 0x0058
+
+#define DP_COM_CONF_FG_EN (1 << 0)
+#define DP_COM_CONF_GWSEL (1 << 1)
+#define DP_COM_CONF_GWAM (1 << 2)
+#define DP_COM_CONF_GWCKE (1 << 3)
+#define DP_COM_CONF_CSC_DEF_MASK (3 << 8)
+#define DP_COM_CONF_CSC_DEF_OFFSET 8
+#define DP_COM_CONF_CSC_DEF_FG (3 << 8)
+#define DP_COM_CONF_CSC_DEF_BG (2 << 8)
+#define DP_COM_CONF_CSC_DEF_BOTH (1 << 8)
+
+struct ipu_dp_priv;
+
+struct ipu_dp {
+ u32 flow;
+ bool in_use;
+ bool foreground;
+ enum ipu_color_space in_cs;
+};
+
+struct ipu_flow {
+ struct ipu_dp foreground;
+ struct ipu_dp background;
+ enum ipu_color_space out_cs;
+ void __iomem *base;
+ struct ipu_dp_priv *priv;
+};
+
+struct ipu_dp_priv {
+ struct ipu_soc *ipu;
+ struct device *dev;
+ void __iomem *base;
+ struct ipu_flow flow[3];
+ struct mutex mutex;
+ int use_count;
+};
+
+static u32 ipu_dp_flow_base[] = {DP_SYNC, DP_ASYNC0, DP_ASYNC1};
+
+static inline struct ipu_flow *to_flow(struct ipu_dp *dp)
+{
+ if (dp->foreground)
+ return container_of(dp, struct ipu_flow, foreground);
+ else
+ return container_of(dp, struct ipu_flow, background);
+}
+
+int ipu_dp_set_global_alpha(struct ipu_dp *dp, bool enable,
+ u8 alpha, bool bg_chan)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+ u32 reg;
+
+ mutex_lock(&priv->mutex);
+
+ reg = readl(flow->base + DP_COM_CONF);
+ if (bg_chan)
+ reg &= ~DP_COM_CONF_GWSEL;
+ else
+ reg |= DP_COM_CONF_GWSEL;
+ writel(reg, flow->base + DP_COM_CONF);
+
+ if (enable) {
+ reg = readl(flow->base + DP_GRAPH_WIND_CTRL) & 0x00FFFFFFL;
+ writel(reg | ((u32) alpha << 24),
+ flow->base + DP_GRAPH_WIND_CTRL);
+
+ reg = readl(flow->base + DP_COM_CONF);
+ writel(reg | DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
+ } else {
+ reg = readl(flow->base + DP_COM_CONF);
+ writel(reg & ~DP_COM_CONF_GWAM, flow->base + DP_COM_CONF);
+ }
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_set_global_alpha);
+
+int ipu_dp_set_window_pos(struct ipu_dp *dp, u16 x_pos, u16 y_pos)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ writel((x_pos << 16) | y_pos, flow->base + DP_FG_POS);
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_set_window_pos);
+
+static void ipu_dp_csc_init(struct ipu_flow *flow,
+ enum ipu_color_space in,
+ enum ipu_color_space out,
+ u32 place)
+{
+ u32 reg;
+
+ reg = readl(flow->base + DP_COM_CONF);
+ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+
+ if (in == out) {
+ writel(reg, flow->base + DP_COM_CONF);
+ return;
+ }
+
+ if (in == IPUV3_COLORSPACE_RGB && out == IPUV3_COLORSPACE_YUV) {
+ writel(0x099 | (0x12d << 16), flow->base + DP_CSC_A_0);
+ writel(0x03a | (0x3a9 << 16), flow->base + DP_CSC_A_1);
+ writel(0x356 | (0x100 << 16), flow->base + DP_CSC_A_2);
+ writel(0x100 | (0x329 << 16), flow->base + DP_CSC_A_3);
+ writel(0x3d6 | (0x0000 << 16) | (2 << 30),
+ flow->base + DP_CSC_0);
+ writel(0x200 | (2 << 14) | (0x200 << 16) | (2 << 30),
+ flow->base + DP_CSC_1);
+ } else {
+ writel(0x095 | (0x000 << 16), flow->base + DP_CSC_A_0);
+ writel(0x0cc | (0x095 << 16), flow->base + DP_CSC_A_1);
+ writel(0x3ce | (0x398 << 16), flow->base + DP_CSC_A_2);
+ writel(0x095 | (0x0ff << 16), flow->base + DP_CSC_A_3);
+ writel(0x000 | (0x3e42 << 16) | (1 << 30),
+ flow->base + DP_CSC_0);
+ writel(0x10a | (1 << 14) | (0x3dd6 << 16) | (1 << 30),
+ flow->base + DP_CSC_1);
+ }
+
+ reg |= place;
+
+ writel(reg, flow->base + DP_COM_CONF);
+}
+
+int ipu_dp_setup_channel(struct ipu_dp *dp,
+ enum ipu_color_space in,
+ enum ipu_color_space out)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ mutex_lock(&priv->mutex);
+
+ dp->in_cs = in;
+
+ if (!dp->foreground)
+ flow->out_cs = out;
+
+ if (flow->foreground.in_cs == flow->background.in_cs) {
+ /*
+ * foreground and background are of same colorspace, put
+ * colorspace converter after combining unit.
+ */
+ ipu_dp_csc_init(flow, flow->foreground.in_cs, flow->out_cs,
+ DP_COM_CONF_CSC_DEF_BOTH);
+ } else {
+ if (flow->foreground.in_cs == flow->out_cs)
+ /*
+ * foreground identical to output, apply color
+ * conversion on background
+ */
+ ipu_dp_csc_init(flow, flow->background.in_cs,
+ flow->out_cs, DP_COM_CONF_CSC_DEF_BG);
+ else
+ ipu_dp_csc_init(flow, flow->foreground.in_cs,
+ flow->out_cs, DP_COM_CONF_CSC_DEF_FG);
+ }
+
+ ipu_srm_dp_sync_update(priv->ipu);
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_setup_channel);
+
+int ipu_dp_enable_channel(struct ipu_dp *dp)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ mutex_lock(&priv->mutex);
+
+ if (!priv->use_count)
+ ipu_module_enable(priv->ipu, IPU_CONF_DP_EN);
+
+ priv->use_count++;
+
+ if (dp->foreground) {
+ u32 reg;
+
+ reg = readl(flow->base + DP_COM_CONF);
+ reg |= DP_COM_CONF_FG_EN;
+ writel(reg, flow->base + DP_COM_CONF);
+
+ ipu_srm_dp_sync_update(priv->ipu);
+ }
+
+ mutex_unlock(&priv->mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_enable_channel);
+
+void ipu_dp_disable_channel(struct ipu_dp *dp)
+{
+ struct ipu_flow *flow = to_flow(dp);
+ struct ipu_dp_priv *priv = flow->priv;
+
+ mutex_lock(&priv->mutex);
+
+ priv->use_count--;
+
+ if (dp->foreground) {
+ u32 reg, csc;
+
+ reg = readl(flow->base + DP_COM_CONF);
+ csc = reg & DP_COM_CONF_CSC_DEF_MASK;
+ if (csc == DP_COM_CONF_CSC_DEF_FG)
+ reg &= ~DP_COM_CONF_CSC_DEF_MASK;
+
+ reg &= ~DP_COM_CONF_FG_EN;
+ writel(reg, flow->base + DP_COM_CONF);
+
+ writel(0, flow->base + DP_FG_POS);
+ ipu_srm_dp_sync_update(priv->ipu);
+ }
+
+ if (!priv->use_count)
+ ipu_module_disable(priv->ipu, IPU_CONF_DP_EN);
+
+ if (priv->use_count < 0)
+ priv->use_count = 0;
+
+ mutex_unlock(&priv->mutex);
+}
+EXPORT_SYMBOL_GPL(ipu_dp_disable_channel);
+
+struct ipu_dp *ipu_dp_get(struct ipu_soc *ipu, unsigned int flow)
+{
+ struct ipu_dp_priv *priv = ipu->dp_priv;
+ struct ipu_dp *dp;
+
+ if (flow > 5)
+ return ERR_PTR(-EINVAL);
+
+ if (flow & 1)
+ dp = &priv->flow[flow >> 1].foreground;
+ else
+ dp = &priv->flow[flow >> 1].background;
+
+ if (dp->in_use)
+ return ERR_PTR(-EBUSY);
+
+ dp->in_use = true;
+
+ return dp;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_get);
+
+void ipu_dp_put(struct ipu_dp *dp)
+{
+ dp->in_use = false;
+}
+EXPORT_SYMBOL_GPL(ipu_dp_put);
+
+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base)
+{
+ struct ipu_dp_priv *priv;
+ int i;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ priv->dev = dev;
+ priv->ipu = ipu;
+
+ ipu->dp_priv = priv;
+
+ priv->base = devm_ioremap(dev, base, PAGE_SIZE);
+ if (!priv->base) {
+ kfree(priv);
+ return -ENOMEM;
+ }
+
+ mutex_init(&priv->mutex);
+
+ for (i = 0; i < 3; i++) {
+ priv->flow[i].foreground.foreground = 1;
+ priv->flow[i].base = priv->base + ipu_dp_flow_base[i];
+ priv->flow[i].priv = priv;
+ }
+
+ return 0;
+}
+
+void ipu_dp_exit(struct ipu_soc *ipu)
+{
+}
diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-prv.h b/drivers/staging/imx-drm/ipu-v3/ipu-prv.h
new file mode 100644
index 000000000000..551802863fd5
--- /dev/null
+++ b/drivers/staging/imx-drm/ipu-v3/ipu-prv.h
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 2010 Sascha Hauer <s.hauer@pengutronix.de>
+ * Copyright (C) 2005-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.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT 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 __IPU_PRV_H__
+#define __IPU_PRV_H__
+
+struct ipu_soc;
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+
+#include "imx-ipu-v3.h"
+
+#define IPUV3_CHANNEL_CSI0 0
+#define IPUV3_CHANNEL_CSI1 1
+#define IPUV3_CHANNEL_CSI2 2
+#define IPUV3_CHANNEL_CSI3 3
+#define IPUV3_CHANNEL_MEM_BG_SYNC 23
+#define IPUV3_CHANNEL_MEM_FG_SYNC 27
+#define IPUV3_CHANNEL_MEM_DC_SYNC 28
+#define IPUV3_CHANNEL_MEM_FG_SYNC_ALPHA 31
+#define IPUV3_CHANNEL_MEM_DC_ASYNC 41
+#define IPUV3_CHANNEL_ROT_ENC_MEM 45
+#define IPUV3_CHANNEL_ROT_VF_MEM 46
+#define IPUV3_CHANNEL_ROT_PP_MEM 47
+#define IPUV3_CHANNEL_ROT_ENC_MEM_OUT 48
+#define IPUV3_CHANNEL_ROT_VF_MEM_OUT 49
+#define IPUV3_CHANNEL_ROT_PP_MEM_OUT 50
+#define IPUV3_CHANNEL_MEM_BG_SYNC_ALPHA 51
+
+#define IPU_MCU_T_DEFAULT 8
+#define IPU_CM_IDMAC_REG_OFS 0x00008000
+#define IPU_CM_IC_REG_OFS 0x00020000
+#define IPU_CM_IRT_REG_OFS 0x00028000
+#define IPU_CM_CSI0_REG_OFS 0x00030000
+#define IPU_CM_CSI1_REG_OFS 0x00038000
+#define IPU_CM_SMFC_REG_OFS 0x00050000
+#define IPU_CM_DC_REG_OFS 0x00058000
+#define IPU_CM_DMFC_REG_OFS 0x00060000
+
+/* Register addresses */
+/* IPU Common registers */
+#define IPU_CM_REG(offset) (offset)
+
+#define IPU_CONF IPU_CM_REG(0)
+
+#define IPU_SRM_PRI1 IPU_CM_REG(0x00a0)
+#define IPU_SRM_PRI2 IPU_CM_REG(0x00a4)
+#define IPU_FS_PROC_FLOW1 IPU_CM_REG(0x00a8)
+#define IPU_FS_PROC_FLOW2 IPU_CM_REG(0x00ac)
+#define IPU_FS_PROC_FLOW3 IPU_CM_REG(0x00b0)
+#define IPU_FS_DISP_FLOW1 IPU_CM_REG(0x00b4)
+#define IPU_FS_DISP_FLOW2 IPU_CM_REG(0x00b8)
+#define IPU_SKIP IPU_CM_REG(0x00bc)
+#define IPU_DISP_ALT_CONF IPU_CM_REG(0x00c0)
+#define IPU_DISP_GEN IPU_CM_REG(0x00c4)
+#define IPU_DISP_ALT1 IPU_CM_REG(0x00c8)
+#define IPU_DISP_ALT2 IPU_CM_REG(0x00cc)
+#define IPU_DISP_ALT3 IPU_CM_REG(0x00d0)
+#define IPU_DISP_ALT4 IPU_CM_REG(0x00d4)
+#define IPU_SNOOP IPU_CM_REG(0x00d8)
+#define IPU_MEM_RST IPU_CM_REG(0x00dc)
+#define IPU_PM IPU_CM_REG(0x00e0)
+#define IPU_GPR IPU_CM_REG(0x00e4)
+#define IPU_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0150 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_DB_MODE_SEL(ch) IPU_CM_REG(0x0168 + 4 * ((ch) / 32))
+#define IPU_CHA_CUR_BUF(ch) IPU_CM_REG(0x023C + 4 * ((ch) / 32))
+#define IPU_ALT_CUR_BUF0 IPU_CM_REG(0x0244)
+#define IPU_ALT_CUR_BUF1 IPU_CM_REG(0x0248)
+#define IPU_SRM_STAT IPU_CM_REG(0x024C)
+#define IPU_PROC_TASK_STAT IPU_CM_REG(0x0250)
+#define IPU_DISP_TASK_STAT IPU_CM_REG(0x0254)
+#define IPU_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0268 + 4 * ((ch) / 32))
+#define IPU_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0270 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_BUF0_RDY(ch) IPU_CM_REG(0x0278 + 4 * ((ch) / 32))
+#define IPU_ALT_CHA_BUF1_RDY(ch) IPU_CM_REG(0x0280 + 4 * ((ch) / 32))
+
+#define IPU_INT_CTRL(n) IPU_CM_REG(0x003C + 4 * (n))
+#define IPU_INT_STAT(n) IPU_CM_REG(0x0200 + 4 * (n))
+
+#define IPU_DI0_COUNTER_RELEASE (1 << 24)
+#define IPU_DI1_COUNTER_RELEASE (1 << 25)
+
+#define IPU_IDMAC_REG(offset) (offset)
+
+#define IDMAC_CONF IPU_IDMAC_REG(0x0000)
+#define IDMAC_CHA_EN(ch) IPU_IDMAC_REG(0x0004 + 4 * ((ch) / 32))
+#define IDMAC_SEP_ALPHA IPU_IDMAC_REG(0x000c)
+#define IDMAC_ALT_SEP_ALPHA IPU_IDMAC_REG(0x0010)
+#define IDMAC_CHA_PRI(ch) IPU_IDMAC_REG(0x0014 + 4 * ((ch) / 32))
+#define IDMAC_WM_EN(ch) IPU_IDMAC_REG(0x001c + 4 * ((ch) / 32))
+#define IDMAC_CH_LOCK_EN_1 IPU_IDMAC_REG(0x0024)
+#define IDMAC_CH_LOCK_EN_2 IPU_IDMAC_REG(0x0028)
+#define IDMAC_SUB_ADDR_0 IPU_IDMAC_REG(0x002c)
+#define IDMAC_SUB_ADDR_1 IPU_IDMAC_REG(0x0030)
+#define IDMAC_SUB_ADDR_2 IPU_IDMAC_REG(0x0034)
+#define IDMAC_BAND_EN(ch) IPU_IDMAC_REG(0x0040 + 4 * ((ch) / 32))
+#define IDMAC_CHA_BUSY(ch) IPU_IDMAC_REG(0x0100 + 4 * ((ch) / 32))
+
+#define IPU_NUM_IRQS (32 * 5)
+
+enum ipu_modules {
+ IPU_CONF_CSI0_EN = (1 << 0),
+ IPU_CONF_CSI1_EN = (1 << 1),
+ IPU_CONF_IC_EN = (1 << 2),
+ IPU_CONF_ROT_EN = (1 << 3),
+ IPU_CONF_ISP_EN = (1 << 4),
+ IPU_CONF_DP_EN = (1 << 5),
+ IPU_CONF_DI0_EN = (1 << 6),
+ IPU_CONF_DI1_EN = (1 << 7),
+ IPU_CONF_SMFC_EN = (1 << 8),
+ IPU_CONF_DC_EN = (1 << 9),
+ IPU_CONF_DMFC_EN = (1 << 10),
+
+ IPU_CONF_VDI_EN = (1 << 12),
+
+ IPU_CONF_IDMAC_DIS = (1 << 22),
+
+ IPU_CONF_IC_DMFC_SEL = (1 << 25),
+ IPU_CONF_IC_DMFC_SYNC = (1 << 26),
+ IPU_CONF_VDI_DMFC_SYNC = (1 << 27),
+
+ IPU_CONF_CSI0_DATA_SOURCE = (1 << 28),
+ IPU_CONF_CSI1_DATA_SOURCE = (1 << 29),
+ IPU_CONF_IC_INPUT = (1 << 30),
+ IPU_CONF_CSI_SEL = (1 << 31),
+};
+
+struct ipuv3_channel {
+ unsigned int num;
+
+ bool enabled;
+ bool busy;
+
+ struct ipu_soc *ipu;
+};
+
+struct ipu_dc_priv;
+struct ipu_dmfc_priv;
+struct ipu_di;
+struct ipu_devtype;
+
+struct ipu_soc {
+ struct device *dev;
+ const struct ipu_devtype *devtype;
+ enum ipuv3_type ipu_type;
+ spinlock_t lock;
+ struct mutex channel_lock;
+
+ void __iomem *cm_reg;
+ void __iomem *idmac_reg;
+ struct ipu_ch_param __iomem *cpmem_base;
+
+ int usecount;
+
+ struct clk *clk;
+
+ struct ipuv3_channel channel[64];
+
+ int irq_start;
+ int irq_sync;
+ int irq_err;
+
+ struct ipu_dc_priv *dc_priv;
+ struct ipu_dp_priv *dp_priv;
+ struct ipu_dmfc_priv *dmfc_priv;
+ struct ipu_di *di_priv[2];
+};
+
+void ipu_srm_dp_sync_update(struct ipu_soc *ipu);
+
+int ipu_module_enable(struct ipu_soc *ipu, u32 mask);
+int ipu_module_disable(struct ipu_soc *ipu, u32 mask);
+
+int ipu_di_init(struct ipu_soc *ipu, struct device *dev, int id,
+ unsigned long base, u32 module, struct clk *ipu_clk);
+void ipu_di_exit(struct ipu_soc *ipu, int id);
+
+int ipu_dmfc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
+ struct clk *ipu_clk);
+void ipu_dmfc_exit(struct ipu_soc *ipu);
+
+int ipu_dp_init(struct ipu_soc *ipu, struct device *dev, unsigned long base);
+void ipu_dp_exit(struct ipu_soc *ipu);
+
+int ipu_dc_init(struct ipu_soc *ipu, struct device *dev, unsigned long base,
+ unsigned long template_base);
+void ipu_dc_exit(struct ipu_soc *ipu);
+
+int ipu_cpmem_init(struct ipu_soc *ipu, struct device *dev, unsigned long base);
+void ipu_cpmem_exit(struct ipu_soc *ipu);
+
+#endif /* __IPU_PRV_H__ */
diff --git a/drivers/staging/imx-drm/ipuv3-crtc.c b/drivers/staging/imx-drm/ipuv3-crtc.c
new file mode 100644
index 000000000000..78d3edac75c1
--- /dev/null
+++ b/drivers/staging/imx-drm/ipuv3-crtc.c
@@ -0,0 +1,579 @@
+/*
+ * i.MX IPUv3 Graphics driver
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix
+ *
+ * This program is free software; 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/module.h>
+#include <linux/export.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/fb.h>
+#include <linux/clk.h>
+#include <drm/drm_gem_cma_helper.h>
+#include <drm/drm_fb_cma_helper.h>
+
+#include "ipu-v3/imx-ipu-v3.h"
+#include "imx-drm.h"
+
+#define DRIVER_DESC "i.MX IPUv3 Graphics"
+
+struct ipu_framebuffer {
+ struct drm_framebuffer base;
+ void *virt;
+ dma_addr_t phys;
+ size_t len;
+};
+
+struct ipu_crtc {
+ struct drm_fb_helper fb_helper;
+ struct ipu_framebuffer ifb;
+ int num_crtcs;
+ struct device *dev;
+ struct drm_crtc base;
+ struct imx_drm_crtc *imx_crtc;
+ struct ipuv3_channel *ipu_ch;
+ struct ipu_dc *dc;
+ struct ipu_dp *dp;
+ struct dmfc_channel *dmfc;
+ struct ipu_di *di;
+ int enabled;
+ struct ipu_priv *ipu_priv;
+ struct drm_pending_vblank_event *page_flip_event;
+ struct drm_framebuffer *newfb;
+ int irq;
+ u32 interface_pix_fmt;
+ unsigned long di_clkflags;
+};
+
+#define to_ipu_crtc(x) container_of(x, struct ipu_crtc, base)
+
+static int calc_vref(struct drm_display_mode *mode)
+{
+ unsigned long htotal, vtotal;
+
+ htotal = mode->htotal;
+ vtotal = mode->vtotal;
+
+ if (!htotal || !vtotal)
+ return 60;
+
+ return mode->clock * 1000 / vtotal / htotal;
+}
+
+static int calc_bandwidth(struct drm_display_mode *mode, unsigned int vref)
+{
+ return mode->hdisplay * mode->vdisplay * vref;
+}
+
+static void ipu_fb_enable(struct ipu_crtc *ipu_crtc)
+{
+ if (ipu_crtc->enabled)
+ return;
+
+ ipu_di_enable(ipu_crtc->di);
+ ipu_dmfc_enable_channel(ipu_crtc->dmfc);
+ ipu_idmac_enable_channel(ipu_crtc->ipu_ch);
+ ipu_dc_enable_channel(ipu_crtc->dc);
+ if (ipu_crtc->dp)
+ ipu_dp_enable_channel(ipu_crtc->dp);
+
+ ipu_crtc->enabled = 1;
+}
+
+static void ipu_fb_disable(struct ipu_crtc *ipu_crtc)
+{
+ if (!ipu_crtc->enabled)
+ return;
+
+ if (ipu_crtc->dp)
+ ipu_dp_disable_channel(ipu_crtc->dp);
+ ipu_dc_disable_channel(ipu_crtc->dc);
+ ipu_idmac_disable_channel(ipu_crtc->ipu_ch);
+ ipu_dmfc_disable_channel(ipu_crtc->dmfc);
+ ipu_di_disable(ipu_crtc->di);
+
+ ipu_crtc->enabled = 0;
+}
+
+static void ipu_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ dev_info(ipu_crtc->dev, "%s mode: %d\n", __func__, mode);
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ ipu_fb_enable(ipu_crtc);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ ipu_fb_disable(ipu_crtc);
+ break;
+ }
+}
+
+static int ipu_page_flip(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+ int ret;
+
+ if (ipu_crtc->newfb)
+ return -EBUSY;
+
+ ret = imx_drm_crtc_vblank_get(ipu_crtc->imx_crtc);
+ if (ret) {
+ dev_dbg(ipu_crtc->dev, "failed to acquire vblank counter\n");
+ list_del(&event->base.link);
+
+ return ret;
+ }
+
+ ipu_crtc->newfb = fb;
+ ipu_crtc->page_flip_event = event;
+
+ return 0;
+}
+
+static const struct drm_crtc_funcs ipu_crtc_funcs = {
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = drm_crtc_cleanup,
+ .page_flip = ipu_page_flip,
+};
+
+static int ipu_drm_set_base(struct drm_crtc *crtc, int x, int y)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+ struct drm_gem_cma_object *cma_obj;
+ struct drm_framebuffer *fb = crtc->fb;
+ unsigned long phys;
+
+ cma_obj = drm_fb_cma_get_gem_obj(fb, 0);
+ if (!cma_obj) {
+ DRM_LOG_KMS("entry is null.\n");
+ return -EFAULT;
+ }
+
+ phys = cma_obj->paddr;
+ phys += x * (fb->bits_per_pixel >> 3);
+ phys += y * fb->pitches[0];
+
+ dev_dbg(ipu_crtc->dev, "%s: phys: 0x%lx\n", __func__, phys);
+ dev_dbg(ipu_crtc->dev, "%s: xy: %dx%d\n", __func__, x, y);
+
+ ipu_cpmem_set_stride(ipu_get_cpmem(ipu_crtc->ipu_ch), fb->pitches[0]);
+ ipu_cpmem_set_buffer(ipu_get_cpmem(ipu_crtc->ipu_ch),
+ 0, phys);
+
+ return 0;
+}
+
+static int ipu_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *orig_mode,
+ struct drm_display_mode *mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+ struct drm_framebuffer *fb = ipu_crtc->base.fb;
+ int ret;
+ struct ipu_di_signal_cfg sig_cfg = {};
+ u32 out_pixel_fmt;
+ struct ipu_ch_param __iomem *cpmem = ipu_get_cpmem(ipu_crtc->ipu_ch);
+ int bpp;
+ u32 v4l2_fmt;
+
+ dev_dbg(ipu_crtc->dev, "%s: mode->hdisplay: %d\n", __func__,
+ mode->hdisplay);
+ dev_dbg(ipu_crtc->dev, "%s: mode->vdisplay: %d\n", __func__,
+ mode->vdisplay);
+
+ ipu_ch_param_zero(cpmem);
+
+ switch (fb->pixel_format) {
+ case DRM_FORMAT_XRGB8888:
+ case DRM_FORMAT_ARGB8888:
+ v4l2_fmt = V4L2_PIX_FMT_RGB32;
+ bpp = 32;
+ break;
+ case DRM_FORMAT_RGB565:
+ v4l2_fmt = V4L2_PIX_FMT_RGB565;
+ bpp = 16;
+ break;
+ case DRM_FORMAT_RGB888:
+ v4l2_fmt = V4L2_PIX_FMT_RGB24;
+ bpp = 24;
+ break;
+ default:
+ dev_err(ipu_crtc->dev, "unsupported pixel format 0x%08x\n",
+ fb->pixel_format);
+ return -EINVAL;
+ }
+
+ out_pixel_fmt = ipu_crtc->interface_pix_fmt;
+
+ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
+ sig_cfg.interlaced = 1;
+ if (mode->flags & DRM_MODE_FLAG_PHSYNC)
+ sig_cfg.Hsync_pol = 1;
+ if (mode->flags & DRM_MODE_FLAG_PVSYNC)
+ sig_cfg.Vsync_pol = 1;
+
+ sig_cfg.enable_pol = 1;
+ sig_cfg.clk_pol = 0;
+ sig_cfg.width = mode->hdisplay;
+ sig_cfg.height = mode->vdisplay;
+ sig_cfg.pixel_fmt = out_pixel_fmt;
+ sig_cfg.h_start_width = mode->htotal - mode->hsync_end;
+ sig_cfg.h_sync_width = mode->hsync_end - mode->hsync_start;
+ sig_cfg.h_end_width = mode->hsync_start - mode->hdisplay;
+
+ sig_cfg.v_start_width = mode->vtotal - mode->vsync_end;
+ sig_cfg.v_sync_width = mode->vsync_end - mode->vsync_start;
+ sig_cfg.v_end_width = mode->vsync_start - mode->vdisplay;
+ sig_cfg.pixelclock = mode->clock * 1000;
+ sig_cfg.clkflags = ipu_crtc->di_clkflags;
+
+ sig_cfg.v_to_h_sync = 0;
+
+ if (ipu_crtc->dp) {
+ ret = ipu_dp_setup_channel(ipu_crtc->dp, IPUV3_COLORSPACE_RGB,
+ IPUV3_COLORSPACE_RGB);
+ if (ret) {
+ dev_err(ipu_crtc->dev,
+ "initializing display processor failed with %d\n",
+ ret);
+ return ret;
+ }
+ ipu_dp_set_global_alpha(ipu_crtc->dp, 1, 0, 1);
+ }
+
+ ret = ipu_dc_init_sync(ipu_crtc->dc, ipu_crtc->di, sig_cfg.interlaced,
+ out_pixel_fmt, mode->hdisplay);
+ if (ret) {
+ dev_err(ipu_crtc->dev,
+ "initializing display controller failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_di_init_sync_panel(ipu_crtc->di, &sig_cfg);
+ if (ret) {
+ dev_err(ipu_crtc->dev,
+ "initializing panel failed with %d\n", ret);
+ return ret;
+ }
+
+ ipu_cpmem_set_resolution(cpmem, mode->hdisplay, mode->vdisplay);
+ ipu_cpmem_set_fmt(cpmem, v4l2_fmt);
+ ipu_cpmem_set_high_priority(ipu_crtc->ipu_ch);
+
+ ret = ipu_dmfc_init_channel(ipu_crtc->dmfc, mode->hdisplay);
+ if (ret) {
+ dev_err(ipu_crtc->dev,
+ "initializing dmfc channel failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = ipu_dmfc_alloc_bandwidth(ipu_crtc->dmfc,
+ calc_bandwidth(mode, calc_vref(mode)), 64);
+ if (ret) {
+ dev_err(ipu_crtc->dev,
+ "allocating dmfc bandwidth failed with %d\n",
+ ret);
+ return ret;
+ }
+
+ ipu_drm_set_base(crtc, x, y);
+
+ return 0;
+}
+
+static void ipu_crtc_handle_pageflip(struct ipu_crtc *ipu_crtc)
+{
+ struct drm_pending_vblank_event *e;
+ struct timeval now;
+ unsigned long flags;
+ struct drm_device *drm = ipu_crtc->base.dev;
+
+ spin_lock_irqsave(&drm->event_lock, flags);
+
+ e = ipu_crtc->page_flip_event;
+ if (!e) {
+ spin_unlock_irqrestore(&drm->event_lock, flags);
+ return;
+ }
+
+ do_gettimeofday(&now);
+ e->event.sequence = 0;
+ e->event.tv_sec = now.tv_sec;
+ e->event.tv_usec = now.tv_usec;
+ ipu_crtc->page_flip_event = NULL;
+
+ imx_drm_crtc_vblank_put(ipu_crtc->imx_crtc);
+
+ list_add_tail(&e->base.link, &e->base.file_priv->event_list);
+
+ wake_up_interruptible(&e->base.file_priv->event_wait);
+
+ spin_unlock_irqrestore(&drm->event_lock, flags);
+}
+
+static irqreturn_t ipu_irq_handler(int irq, void *dev_id)
+{
+ struct ipu_crtc *ipu_crtc = dev_id;
+
+ imx_drm_handle_vblank(ipu_crtc->imx_crtc);
+
+ if (ipu_crtc->newfb) {
+ ipu_crtc->base.fb = ipu_crtc->newfb;
+ ipu_crtc->newfb = NULL;
+ ipu_drm_set_base(&ipu_crtc->base, 0, 0);
+ ipu_crtc_handle_pageflip(ipu_crtc);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static bool ipu_crtc_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void ipu_crtc_prepare(struct drm_crtc *crtc)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ ipu_fb_disable(ipu_crtc);
+}
+
+static void ipu_crtc_commit(struct drm_crtc *crtc)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ ipu_fb_enable(ipu_crtc);
+}
+
+static void ipu_crtc_load_lut(struct drm_crtc *crtc)
+{
+}
+
+static struct drm_crtc_helper_funcs ipu_helper_funcs = {
+ .dpms = ipu_crtc_dpms,
+ .mode_fixup = ipu_crtc_mode_fixup,
+ .mode_set = ipu_crtc_mode_set,
+ .prepare = ipu_crtc_prepare,
+ .commit = ipu_crtc_commit,
+ .load_lut = ipu_crtc_load_lut,
+};
+
+static int ipu_enable_vblank(struct drm_crtc *crtc)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ enable_irq(ipu_crtc->irq);
+
+ return 0;
+}
+
+static void ipu_disable_vblank(struct drm_crtc *crtc)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ disable_irq(ipu_crtc->irq);
+}
+
+static int ipu_set_interface_pix_fmt(struct drm_crtc *crtc, u32 encoder_type,
+ u32 pixfmt)
+{
+ struct ipu_crtc *ipu_crtc = to_ipu_crtc(crtc);
+
+ ipu_crtc->interface_pix_fmt = pixfmt;
+
+ switch (encoder_type) {
+ case DRM_MODE_ENCODER_LVDS:
+ ipu_crtc->di_clkflags = IPU_DI_CLKMODE_SYNC |
+ IPU_DI_CLKMODE_EXT;
+ break;
+ case DRM_MODE_ENCODER_NONE:
+ ipu_crtc->di_clkflags = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct imx_drm_crtc_helper_funcs ipu_crtc_helper_funcs = {
+ .enable_vblank = ipu_enable_vblank,
+ .disable_vblank = ipu_disable_vblank,
+ .set_interface_pix_fmt = ipu_set_interface_pix_fmt,
+ .crtc_funcs = &ipu_crtc_funcs,
+ .crtc_helper_funcs = &ipu_helper_funcs,
+};
+
+static void ipu_put_resources(struct ipu_crtc *ipu_crtc)
+{
+ if (!IS_ERR_OR_NULL(ipu_crtc->ipu_ch))
+ ipu_idmac_put(ipu_crtc->ipu_ch);
+ if (!IS_ERR_OR_NULL(ipu_crtc->dmfc))
+ ipu_dmfc_put(ipu_crtc->dmfc);
+ if (!IS_ERR_OR_NULL(ipu_crtc->dp))
+ ipu_dp_put(ipu_crtc->dp);
+ if (!IS_ERR_OR_NULL(ipu_crtc->di))
+ ipu_di_put(ipu_crtc->di);
+}
+
+static int ipu_get_resources(struct ipu_crtc *ipu_crtc,
+ struct ipu_client_platformdata *pdata)
+{
+ struct ipu_soc *ipu = dev_get_drvdata(ipu_crtc->dev->parent);
+ int ret;
+
+ ipu_crtc->ipu_ch = ipu_idmac_get(ipu, pdata->dma[0]);
+ if (IS_ERR_OR_NULL(ipu_crtc->ipu_ch)) {
+ ret = PTR_ERR(ipu_crtc->ipu_ch);
+ goto err_out;
+ }
+
+ ipu_crtc->dc = ipu_dc_get(ipu, pdata->dc);
+ if (IS_ERR(ipu_crtc->dc)) {
+ ret = PTR_ERR(ipu_crtc->dc);
+ goto err_out;
+ }
+
+ ipu_crtc->dmfc = ipu_dmfc_get(ipu, pdata->dma[0]);
+ if (IS_ERR(ipu_crtc->dmfc)) {
+ ret = PTR_ERR(ipu_crtc->dmfc);
+ goto err_out;
+ }
+
+ if (pdata->dp >= 0) {
+ ipu_crtc->dp = ipu_dp_get(ipu, pdata->dp);
+ if (IS_ERR(ipu_crtc->dp)) {
+ ret = PTR_ERR(ipu_crtc->ipu_ch);
+ goto err_out;
+ }
+ }
+
+ ipu_crtc->di = ipu_di_get(ipu, pdata->di);
+ if (IS_ERR(ipu_crtc->di)) {
+ ret = PTR_ERR(ipu_crtc->di);
+ goto err_out;
+ }
+
+ ipu_crtc->irq = ipu_idmac_channel_irq(ipu, ipu_crtc->ipu_ch,
+ IPU_IRQ_EOF);
+ ret = devm_request_irq(ipu_crtc->dev, ipu_crtc->irq, ipu_irq_handler, 0,
+ "imx_drm", ipu_crtc);
+ if (ret < 0) {
+ dev_err(ipu_crtc->dev, "irq request failed with %d.\n", ret);
+ goto err_out;
+ }
+
+ disable_irq(ipu_crtc->irq);
+
+ return 0;
+err_out:
+ ipu_put_resources(ipu_crtc);
+
+ return ret;
+}
+
+static int ipu_crtc_init(struct ipu_crtc *ipu_crtc,
+ struct ipu_client_platformdata *pdata)
+{
+ int ret;
+
+ ret = ipu_get_resources(ipu_crtc, pdata);
+ if (ret) {
+ dev_err(ipu_crtc->dev, "getting resources failed with %d.\n",
+ ret);
+ return ret;
+ }
+
+ ret = imx_drm_add_crtc(&ipu_crtc->base,
+ &ipu_crtc->imx_crtc,
+ &ipu_crtc_helper_funcs, THIS_MODULE,
+ ipu_crtc->dev->parent->of_node, pdata->di);
+ if (ret) {
+ dev_err(ipu_crtc->dev, "adding crtc failed with %d.\n", ret);
+ goto err_put_resources;
+ }
+
+ return 0;
+
+err_put_resources:
+ ipu_put_resources(ipu_crtc);
+
+ return ret;
+}
+
+static int __devinit ipu_drm_probe(struct platform_device *pdev)
+{
+ struct ipu_client_platformdata *pdata = pdev->dev.platform_data;
+ struct ipu_crtc *ipu_crtc;
+ int ret;
+
+ if (!pdata)
+ return -EINVAL;
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+ ipu_crtc = devm_kzalloc(&pdev->dev, sizeof(*ipu_crtc), GFP_KERNEL);
+ if (!ipu_crtc)
+ return -ENOMEM;
+
+ ipu_crtc->dev = &pdev->dev;
+
+ ret = ipu_crtc_init(ipu_crtc, pdata);
+
+ platform_set_drvdata(pdev, ipu_crtc);
+
+ return 0;
+}
+
+static int __devexit ipu_drm_remove(struct platform_device *pdev)
+{
+ struct ipu_crtc *ipu_crtc = platform_get_drvdata(pdev);
+
+ imx_drm_remove_crtc(ipu_crtc->imx_crtc);
+
+ ipu_put_resources(ipu_crtc);
+
+ return 0;
+}
+
+static struct platform_driver ipu_drm_driver = {
+ .driver = {
+ .name = "imx-ipuv3-crtc",
+ },
+ .probe = ipu_drm_probe,
+ .remove = __devexit_p(ipu_drm_remove),
+};
+module_platform_driver(ipu_drm_driver);
+
+MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/imx-drm/parallel-display.c b/drivers/staging/imx-drm/parallel-display.c
new file mode 100644
index 000000000000..9b51d732eefa
--- /dev/null
+++ b/drivers/staging/imx-drm/parallel-display.c
@@ -0,0 +1,261 @@
+/*
+ * i.MX drm driver - parallel display implementation
+ *
+ * Copyright (C) 2012 Sascha Hauer, Pengutronix
+ *
+ * This program is free software; 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/module.h>
+#include <drm/drmP.h>
+#include <drm/drm_fb_helper.h>
+#include <drm/drm_crtc_helper.h>
+#include <linux/videodev2.h>
+
+#include "imx-drm.h"
+
+#define con_to_imxpd(x) container_of(x, struct imx_parallel_display, connector)
+#define enc_to_imxpd(x) container_of(x, struct imx_parallel_display, encoder)
+
+struct imx_parallel_display {
+ struct drm_connector connector;
+ struct imx_drm_connector *imx_drm_connector;
+ struct drm_encoder encoder;
+ struct imx_drm_encoder *imx_drm_encoder;
+ struct device *dev;
+ void *edid;
+ int edid_len;
+ u32 interface_pix_fmt;
+ int mode_valid;
+ struct drm_display_mode mode;
+};
+
+static enum drm_connector_status imx_pd_connector_detect(
+ struct drm_connector *connector, bool force)
+{
+ return connector_status_connected;
+}
+
+static void imx_pd_connector_destroy(struct drm_connector *connector)
+{
+ /* do not free here */
+}
+
+static int imx_pd_connector_get_modes(struct drm_connector *connector)
+{
+ struct imx_parallel_display *imxpd = con_to_imxpd(connector);
+ int num_modes = 0;
+
+ if (imxpd->edid) {
+ drm_mode_connector_update_edid_property(connector, imxpd->edid);
+ num_modes = drm_add_edid_modes(connector, imxpd->edid);
+ }
+
+ if (imxpd->mode_valid) {
+ struct drm_display_mode *mode = drm_mode_create(connector->dev);
+ drm_mode_copy(mode, &imxpd->mode);
+ mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
+ drm_mode_probed_add(connector, mode);
+ num_modes++;
+ }
+
+ return num_modes;
+}
+
+static int imx_pd_connector_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return 0;
+}
+
+static struct drm_encoder *imx_pd_connector_best_encoder(
+ struct drm_connector *connector)
+{
+ struct imx_parallel_display *imxpd = con_to_imxpd(connector);
+
+ return &imxpd->encoder;
+}
+
+static void imx_pd_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static bool imx_pd_encoder_mode_fixup(struct drm_encoder *encoder,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void imx_pd_encoder_prepare(struct drm_encoder *encoder)
+{
+ struct imx_parallel_display *imxpd = enc_to_imxpd(encoder);
+
+ imx_drm_crtc_panel_format(encoder->crtc, DRM_MODE_ENCODER_NONE,
+ imxpd->interface_pix_fmt);
+}
+
+static void imx_pd_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void imx_pd_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void imx_pd_encoder_disable(struct drm_encoder *encoder)
+{
+}
+
+static void imx_pd_encoder_destroy(struct drm_encoder *encoder)
+{
+ /* do not free here */
+}
+
+static struct drm_connector_funcs imx_pd_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .detect = imx_pd_connector_detect,
+ .destroy = imx_pd_connector_destroy,
+};
+
+static struct drm_connector_helper_funcs imx_pd_connector_helper_funcs = {
+ .get_modes = imx_pd_connector_get_modes,
+ .best_encoder = imx_pd_connector_best_encoder,
+ .mode_valid = imx_pd_connector_mode_valid,
+};
+
+static struct drm_encoder_funcs imx_pd_encoder_funcs = {
+ .destroy = imx_pd_encoder_destroy,
+};
+
+static struct drm_encoder_helper_funcs imx_pd_encoder_helper_funcs = {
+ .dpms = imx_pd_encoder_dpms,
+ .mode_fixup = imx_pd_encoder_mode_fixup,
+ .prepare = imx_pd_encoder_prepare,
+ .commit = imx_pd_encoder_commit,
+ .mode_set = imx_pd_encoder_mode_set,
+ .disable = imx_pd_encoder_disable,
+};
+
+static int imx_pd_register(struct imx_parallel_display *imxpd)
+{
+ int ret;
+
+ drm_mode_connector_attach_encoder(&imxpd->connector, &imxpd->encoder);
+
+ imxpd->connector.funcs = &imx_pd_connector_funcs;
+ imxpd->encoder.funcs = &imx_pd_encoder_funcs;
+
+ imxpd->encoder.encoder_type = DRM_MODE_ENCODER_NONE;
+ imxpd->connector.connector_type = DRM_MODE_CONNECTOR_VGA;
+
+ drm_encoder_helper_add(&imxpd->encoder, &imx_pd_encoder_helper_funcs);
+ ret = imx_drm_add_encoder(&imxpd->encoder, &imxpd->imx_drm_encoder,
+ THIS_MODULE);
+ if (ret) {
+ dev_err(imxpd->dev, "adding encoder failed with %d\n", ret);
+ return ret;
+ }
+
+ drm_connector_helper_add(&imxpd->connector,
+ &imx_pd_connector_helper_funcs);
+
+ ret = imx_drm_add_connector(&imxpd->connector,
+ &imxpd->imx_drm_connector, THIS_MODULE);
+ if (ret) {
+ imx_drm_remove_encoder(imxpd->imx_drm_encoder);
+ dev_err(imxpd->dev, "adding connector failed with %d\n", ret);
+ return ret;
+ }
+
+ imxpd->connector.encoder = &imxpd->encoder;
+
+ return 0;
+}
+
+static int __devinit imx_pd_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ const u8 *edidp;
+ struct imx_parallel_display *imxpd;
+ int ret;
+ const char *fmt;
+
+ imxpd = devm_kzalloc(&pdev->dev, sizeof(*imxpd), GFP_KERNEL);
+ if (!imxpd)
+ return -ENOMEM;
+
+ edidp = of_get_property(np, "edid", &imxpd->edid_len);
+ if (edidp)
+ imxpd->edid = kmemdup(edidp, imxpd->edid_len, GFP_KERNEL);
+
+ ret = of_property_read_string(np, "interface-pix-fmt", &fmt);
+ if (!ret) {
+ if (!strcmp(fmt, "rgb24"))
+ imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB24;
+ else if (!strcmp(fmt, "rgb565"))
+ imxpd->interface_pix_fmt = V4L2_PIX_FMT_RGB565;
+ }
+
+ imxpd->dev = &pdev->dev;
+
+ ret = imx_pd_register(imxpd);
+ if (ret)
+ return ret;
+
+ ret = imx_drm_encoder_add_possible_crtcs(imxpd->imx_drm_encoder, np);
+
+ platform_set_drvdata(pdev, imxpd);
+
+ return 0;
+}
+
+static int __devexit imx_pd_remove(struct platform_device *pdev)
+{
+ struct imx_parallel_display *imxpd = platform_get_drvdata(pdev);
+ struct drm_connector *connector = &imxpd->connector;
+ struct drm_encoder *encoder = &imxpd->encoder;
+
+ drm_mode_connector_detach_encoder(connector, encoder);
+
+ imx_drm_remove_connector(imxpd->imx_drm_connector);
+ imx_drm_remove_encoder(imxpd->imx_drm_encoder);
+
+ return 0;
+}
+
+static const struct of_device_id imx_pd_dt_ids[] = {
+ { .compatible = "fsl,imx-parallel-display", },
+ { /* sentinel */ }
+};
+
+static struct platform_driver imx_pd_driver = {
+ .probe = imx_pd_probe,
+ .remove = __devexit_p(imx_pd_remove),
+ .driver = {
+ .of_match_table = imx_pd_dt_ids,
+ .name = "imx-parallel-display",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(imx_pd_driver);
+
+MODULE_DESCRIPTION("i.MX parallel display driver");
+MODULE_AUTHOR("Sascha Hauer, Pengutronix");
+MODULE_LICENSE("GPL");
diff --git a/drivers/staging/ipack/Kconfig b/drivers/staging/ipack/Kconfig
index af321789dddb..4cf47066140c 100644
--- a/drivers/staging/ipack/Kconfig
+++ b/drivers/staging/ipack/Kconfig
@@ -4,6 +4,7 @@
menuconfig IPACK_BUS
tristate "IndustryPack bus support"
+ depends on HAS_IOMEM
---help---
If you say Y here you get support for the IndustryPack Framework
for drivers for many types of boards that support this industrial
diff --git a/drivers/staging/ipack/TODO b/drivers/staging/ipack/TODO
index b21d33ab144a..ffafe6911a77 100644
--- a/drivers/staging/ipack/TODO
+++ b/drivers/staging/ipack/TODO
@@ -12,29 +12,8 @@ operations between the two kind of boards.
TODO
====
-TPCI-200
---------
-
-* It has a linked list with the tpci200 devices it is managing. Get rid of it
- and use driver_for_each_device() instead.
-
-IP-OCTAL
---------
-
-* It has a linked list which saves the devices it is currently
- managing. It should use the driver_for_each_device() function. It is not there
- due to the impossibility of using container_of macro to recover the
- corresponding "struct ipoctal" because the attribute "struct ipack_device" is
- a pointer. This code should be refactored.
-
-Ipack
------
-
-* The structures and API exported can be improved a lot. For example, the
- way to unregistering IP module devices, doing the IP module driver a call to
- remove_device() to notify the carrier driver, or the opposite with the call to
- the ipack_driver_ops' remove() function could be improved.
-
+checkpatch.pl warnings
+cleanup
Contact
=======
diff --git a/drivers/staging/ipack/bridges/tpci200.c b/drivers/staging/ipack/bridges/tpci200.c
index 2b83fa8e550a..bb8aa70281cd 100644
--- a/drivers/staging/ipack/bridges/tpci200.c
+++ b/drivers/staging/ipack/bridges/tpci200.c
@@ -14,38 +14,32 @@
#include <linux/module.h>
#include "tpci200.h"
-static struct ipack_bus_ops tpci200_bus_ops;
-
-/* TPCI200 controls registers */
-static int control_reg[] = {
- TPCI200_CONTROL_A_REG,
- TPCI200_CONTROL_B_REG,
- TPCI200_CONTROL_C_REG,
- TPCI200_CONTROL_D_REG
+static u16 tpci200_status_timeout[] = {
+ TPCI200_A_TIMEOUT,
+ TPCI200_B_TIMEOUT,
+ TPCI200_C_TIMEOUT,
+ TPCI200_D_TIMEOUT,
};
-/* Linked list to save the registered devices */
-static LIST_HEAD(tpci200_list);
-
-static int tpci200_slot_unregister(struct ipack_device *dev);
+static u16 tpci200_status_error[] = {
+ TPCI200_A_ERROR,
+ TPCI200_B_ERROR,
+ TPCI200_C_ERROR,
+ TPCI200_D_ERROR,
+};
static struct tpci200_board *check_slot(struct ipack_device *dev)
{
struct tpci200_board *tpci200;
- int found = 0;
if (dev == NULL)
return NULL;
- list_for_each_entry(tpci200, &tpci200_list, list) {
- if (tpci200->number == dev->bus_nr) {
- found = 1;
- break;
- }
- }
- if (!found) {
- dev_err(&dev->dev, "Carrier not found\n");
+ tpci200 = dev_get_drvdata(dev->bus->parent);
+
+ if (tpci200 == NULL) {
+ dev_info(&dev->dev, "carrier board not found\n");
return NULL;
}
@@ -59,296 +53,190 @@ static struct tpci200_board *check_slot(struct ipack_device *dev)
return tpci200;
}
-static inline unsigned char __tpci200_read8(void __iomem *address,
- unsigned long offset)
-{
- return ioread8(address + (offset^1));
-}
-
-static inline unsigned short __tpci200_read16(void __iomem *address,
- unsigned long offset)
+static void tpci200_clear_mask(struct tpci200_board *tpci200,
+ __le16 __iomem *addr, u16 mask)
{
- return ioread16(address + offset);
+ unsigned long flags;
+ spin_lock_irqsave(&tpci200->regs_lock, flags);
+ iowrite16(ioread16(addr) & (~mask), addr);
+ spin_unlock_irqrestore(&tpci200->regs_lock, flags);
}
-static inline unsigned int __tpci200_read32(void __iomem *address,
- unsigned long offset)
+static void tpci200_set_mask(struct tpci200_board *tpci200,
+ __le16 __iomem *addr, u16 mask)
{
- return swahw32(ioread32(address + offset));
+ unsigned long flags;
+ spin_lock_irqsave(&tpci200->regs_lock, flags);
+ iowrite16(ioread16(addr) | mask, addr);
+ spin_unlock_irqrestore(&tpci200->regs_lock, flags);
}
-static inline void __tpci200_write8(unsigned char value,
- void __iomem *address, unsigned long offset)
+static void tpci200_unregister(struct tpci200_board *tpci200)
{
- iowrite8(value, address+(offset^1));
-}
+ int i;
-static inline void __tpci200_write16(unsigned short value,
- void __iomem *address,
- unsigned long offset)
-{
- iowrite16(value, address+offset);
-}
+ free_irq(tpci200->info->pdev->irq, (void *) tpci200);
-static inline void __tpci200_write32(unsigned int value,
- void __iomem *address,
- unsigned long offset)
-{
- iowrite32(swahw32(value), address+offset);
-}
+ pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs);
+ pci_iounmap(tpci200->info->pdev, tpci200->info->ioidint_space);
+ pci_iounmap(tpci200->info->pdev, tpci200->info->mem8_space);
+ pci_iounmap(tpci200->info->pdev, tpci200->info->cfg_regs);
-static struct ipack_addr_space *get_slot_address_space(struct ipack_device *dev,
- int space)
-{
- struct ipack_addr_space *addr;
+ pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR);
+ pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR);
+ pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR);
+ pci_release_region(tpci200->info->pdev, TPCI200_CFG_MEM_BAR);
- switch (space) {
- case IPACK_IO_SPACE:
- addr = &dev->io_space;
- break;
- case IPACK_ID_SPACE:
- addr = &dev->id_space;
- break;
- case IPACK_MEM_SPACE:
- addr = &dev->mem_space;
- break;
- default:
- dev_err(&dev->dev,
- "Slot [%d:%d] space number %d doesn't exist !\n",
- dev->bus_nr, dev->slot, space);
- return NULL;
- break;
- }
+ pci_disable_device(tpci200->info->pdev);
+ pci_dev_put(tpci200->info->pdev);
- if ((addr->size == 0) || (addr->address == NULL)) {
- dev_err(&dev->dev, "Error, slot space not mapped !\n");
- return NULL;
+ for (i = 0; i < TPCI200_NB_SLOT; i++) {
+ tpci200->slots[i].io_phys.address = NULL;
+ tpci200->slots[i].io_phys.size = 0;
+ tpci200->slots[i].id_phys.address = NULL;
+ tpci200->slots[i].id_phys.size = 0;
+ tpci200->slots[i].int_phys.address = NULL;
+ tpci200->slots[i].int_phys.size = 0;
+ tpci200->slots[i].mem_phys.address = NULL;
+ tpci200->slots[i].mem_phys.size = 0;
}
-
- return addr;
}
-static int tpci200_read8(struct ipack_device *dev, int space,
- unsigned long offset, unsigned char *value)
+static void tpci200_enable_irq(struct tpci200_board *tpci200,
+ int islot)
{
- struct ipack_addr_space *addr;
- struct tpci200_board *tpci200;
-
- tpci200 = check_slot(dev);
- if (tpci200 == NULL)
- return -EINVAL;
-
- addr = get_slot_address_space(dev, space);
- if (addr == NULL)
- return -EINVAL;
-
- if (offset >= addr->size) {
- dev_err(&dev->dev, "Error, slot space offset error !\n");
- return -EFAULT;
- }
-
- *value = __tpci200_read8(addr->address, offset);
-
- return 0;
+ tpci200_set_mask(tpci200,
+ &tpci200->info->interface_regs->control[islot],
+ TPCI200_INT0_EN | TPCI200_INT1_EN);
}
-static int tpci200_read16(struct ipack_device *dev, int space,
- unsigned long offset, unsigned short *value)
+static void tpci200_disable_irq(struct tpci200_board *tpci200,
+ int islot)
{
- struct ipack_addr_space *addr;
- struct tpci200_board *tpci200;
-
- tpci200 = check_slot(dev);
- if (tpci200 == NULL)
- return -EINVAL;
-
- addr = get_slot_address_space(dev, space);
- if (addr == NULL)
- return -EINVAL;
-
- if ((offset+2) >= addr->size) {
- dev_err(&dev->dev, "Error, slot space offset error !\n");
- return -EFAULT;
- }
- *value = __tpci200_read16(addr->address, offset);
-
- return 0;
+ tpci200_clear_mask(tpci200,
+ &tpci200->info->interface_regs->control[islot],
+ TPCI200_INT0_EN | TPCI200_INT1_EN);
}
-static int tpci200_read32(struct ipack_device *dev, int space,
- unsigned long offset, unsigned int *value)
+static irqreturn_t tpci200_slot_irq(struct slot_irq *slot_irq)
{
- struct ipack_addr_space *addr;
- struct tpci200_board *tpci200;
-
- tpci200 = check_slot(dev);
- if (tpci200 == NULL)
- return -EINVAL;
-
- addr = get_slot_address_space(dev, space);
- if (addr == NULL)
- return -EINVAL;
+ irqreturn_t ret;
- if ((offset+4) >= addr->size) {
- dev_err(&dev->dev, "Error, slot space offset error !\n");
- return -EFAULT;
- }
-
- *value = __tpci200_read32(addr->address, offset);
+ if (!slot_irq)
+ return -ENODEV;
+ ret = slot_irq->handler(slot_irq->arg);
- return 0;
+ return ret;
}
-static int tpci200_write8(struct ipack_device *dev, int space,
- unsigned long offset, unsigned char value)
+static irqreturn_t tpci200_interrupt(int irq, void *dev_id)
{
- struct ipack_addr_space *addr;
- struct tpci200_board *tpci200;
+ struct tpci200_board *tpci200 = (struct tpci200_board *) dev_id;
+ struct slot_irq *slot_irq;
+ irqreturn_t ret;
+ u16 status_reg;
+ int i;
- tpci200 = check_slot(dev);
- if (tpci200 == NULL)
- return -EINVAL;
+ /* Read status register */
+ status_reg = ioread16(&tpci200->info->interface_regs->status);
- addr = get_slot_address_space(dev, space);
- if (addr == NULL)
- return -EINVAL;
+ /* Did we cause the interrupt? */
+ if (!(status_reg & TPCI200_SLOT_INT_MASK))
+ return IRQ_NONE;
- if (offset >= addr->size) {
- dev_err(&dev->dev, "Error, slot space offset error !\n");
- return -EFAULT;
+ /* callback to the IRQ handler for the corresponding slot */
+ rcu_read_lock();
+ for (i = 0; i < TPCI200_NB_SLOT; i++) {
+ if (!(status_reg & ((TPCI200_A_INT0 | TPCI200_A_INT1) << (2 * i))))
+ continue;
+ slot_irq = rcu_dereference(tpci200->slots[i].irq);
+ ret = tpci200_slot_irq(slot_irq);
+ if (ret == -ENODEV) {
+ dev_info(&tpci200->info->pdev->dev,
+ "No registered ISR for slot [%d:%d]!. IRQ will be disabled.\n",
+ tpci200->number, i);
+ tpci200_disable_irq(tpci200, i);
+ }
}
+ rcu_read_unlock();
- __tpci200_write8(value, addr->address, offset);
-
- return 0;
+ return IRQ_HANDLED;
}
-static int tpci200_write16(struct ipack_device *dev, int space,
- unsigned long offset, unsigned short value)
+static int tpci200_free_irq(struct ipack_device *dev)
{
- struct ipack_addr_space *addr;
+ struct slot_irq *slot_irq;
struct tpci200_board *tpci200;
tpci200 = check_slot(dev);
if (tpci200 == NULL)
return -EINVAL;
- addr = get_slot_address_space(dev, space);
- if (addr == NULL)
- return -EINVAL;
+ if (mutex_lock_interruptible(&tpci200->mutex))
+ return -ERESTARTSYS;
- if ((offset+2) >= addr->size) {
- dev_err(&dev->dev, "Error, slot space offset error !\n");
- return -EFAULT;
+ if (tpci200->slots[dev->slot].irq == NULL) {
+ mutex_unlock(&tpci200->mutex);
+ return -EINVAL;
}
- __tpci200_write16(value, addr->address, offset);
-
+ tpci200_disable_irq(tpci200, dev->slot);
+ slot_irq = tpci200->slots[dev->slot].irq;
+ /* uninstall handler */
+ RCU_INIT_POINTER(tpci200->slots[dev->slot].irq, NULL);
+ synchronize_rcu();
+ kfree(slot_irq);
+ mutex_unlock(&tpci200->mutex);
return 0;
}
-static int tpci200_write32(struct ipack_device *dev, int space,
- unsigned long offset, unsigned int value)
+static int tpci200_request_irq(struct ipack_device *dev,
+ irqreturn_t (*handler)(void *), void *arg)
{
- struct ipack_addr_space *addr;
+ int res = 0;
+ struct slot_irq *slot_irq;
struct tpci200_board *tpci200;
tpci200 = check_slot(dev);
if (tpci200 == NULL)
return -EINVAL;
- addr = get_slot_address_space(dev, space);
- if (addr == NULL)
- return -EINVAL;
+ if (mutex_lock_interruptible(&tpci200->mutex))
+ return -ERESTARTSYS;
- if ((offset+4) >= addr->size) {
- dev_err(&dev->dev, "Error, slot space offset error !\n");
- return -EFAULT;
+ if (tpci200->slots[dev->slot].irq != NULL) {
+ dev_err(&dev->dev,
+ "Slot [%d:%d] IRQ already registered !\n", dev->bus_nr,
+ dev->slot);
+ res = -EINVAL;
+ goto out_unlock;
}
- __tpci200_write32(value, addr->address, offset);
-
- return 0;
-}
-
-static void tpci200_unregister(struct tpci200_board *tpci200)
-{
- int i;
-
- free_irq(tpci200->info->pdev->irq, (void *) tpci200);
-
- pci_iounmap(tpci200->info->pdev, tpci200->info->interface_regs);
- pci_iounmap(tpci200->info->pdev, tpci200->info->ioidint_space);
- pci_iounmap(tpci200->info->pdev, tpci200->info->mem8_space);
-
- pci_release_region(tpci200->info->pdev, TPCI200_IP_INTERFACE_BAR);
- pci_release_region(tpci200->info->pdev, TPCI200_IO_ID_INT_SPACES_BAR);
- pci_release_region(tpci200->info->pdev, TPCI200_MEM8_SPACE_BAR);
-
- pci_disable_device(tpci200->info->pdev);
- pci_dev_put(tpci200->info->pdev);
-
- for (i = 0; i < TPCI200_NB_SLOT; i++) {
- tpci200->slots[i].io_phys.address = NULL;
- tpci200->slots[i].io_phys.size = 0;
- tpci200->slots[i].id_phys.address = NULL;
- tpci200->slots[i].id_phys.size = 0;
- tpci200->slots[i].mem_phys.address = NULL;
- tpci200->slots[i].mem_phys.size = 0;
+ slot_irq = kzalloc(sizeof(struct slot_irq), GFP_KERNEL);
+ if (slot_irq == NULL) {
+ dev_err(&dev->dev,
+ "Slot [%d:%d] unable to allocate memory for IRQ !\n",
+ dev->bus_nr, dev->slot);
+ res = -ENOMEM;
+ goto out_unlock;
}
-}
-static irqreturn_t tpci200_interrupt(int irq, void *dev_id)
-{
- struct tpci200_board *tpci200 = (struct tpci200_board *) dev_id;
- int i;
- unsigned short status_reg, reg_value;
- unsigned short unhandled_ints = 0;
- irqreturn_t ret = IRQ_NONE;
+ /*
+ * WARNING: Setup Interrupt Vector in the IndustryPack device
+ * before an IRQ request.
+ * Read the User Manual of your IndustryPack device to know
+ * where to write the vector in memory.
+ */
+ slot_irq->handler = handler;
+ slot_irq->arg = arg;
+ slot_irq->holder = dev;
- /* Read status register */
- status_reg = readw(tpci200->info->interface_regs +
- TPCI200_STATUS_REG);
-
- if (status_reg & TPCI200_SLOT_INT_MASK) {
- unhandled_ints = status_reg & TPCI200_SLOT_INT_MASK;
- /* callback to the IRQ handler for the corresponding slot */
- for (i = 0; i < TPCI200_NB_SLOT; i++) {
- if ((tpci200->slots[i].irq != NULL) &&
- (status_reg & ((TPCI200_A_INT0 | TPCI200_A_INT1) << (2*i)))) {
-
- ret = tpci200->slots[i].irq->handler(tpci200->slots[i].irq->arg);
-
- /* Dummy reads */
- readw(tpci200->slots[i].dev->io_space.address +
- 0xC0);
- readw(tpci200->slots[i].dev->io_space.address +
- 0xC2);
-
- unhandled_ints &= ~(((TPCI200_A_INT0 | TPCI200_A_INT1) << (2*i)));
- }
- }
- }
- /* Interrupt not handled are disabled */
- if (unhandled_ints) {
- for (i = 0; i < TPCI200_NB_SLOT; i++) {
- if (unhandled_ints & ((TPCI200_INT0_EN | TPCI200_INT1_EN) << (2*i))) {
- dev_info(&tpci200->slots[i].dev->dev,
- "No registered ISR for slot [%d:%d]!. IRQ will be disabled.\n",
- tpci200->number, i);
- reg_value = readw(
- tpci200->info->interface_regs +
- control_reg[i]);
- reg_value &=
- ~(TPCI200_INT0_EN | TPCI200_INT1_EN);
- writew(reg_value,
- (tpci200->info->interface_regs +
- control_reg[i]));
- }
- }
- }
+ rcu_assign_pointer(tpci200->slots[dev->slot].irq, slot_irq);
+ tpci200_enable_irq(tpci200, dev->slot);
- return ret;
+out_unlock:
+ mutex_unlock(&tpci200->mutex);
+ return res;
}
static int tpci200_register(struct tpci200_board *tpci200)
@@ -398,18 +286,21 @@ static int tpci200_register(struct tpci200_board *tpci200)
/* Map internal tpci200 driver user space */
tpci200->info->interface_regs =
- ioremap(pci_resource_start(tpci200->info->pdev,
+ ioremap_nocache(pci_resource_start(tpci200->info->pdev,
TPCI200_IP_INTERFACE_BAR),
TPCI200_IFACE_SIZE);
tpci200->info->ioidint_space =
- ioremap(pci_resource_start(tpci200->info->pdev,
+ ioremap_nocache(pci_resource_start(tpci200->info->pdev,
TPCI200_IO_ID_INT_SPACES_BAR),
TPCI200_IOIDINT_SIZE);
tpci200->info->mem8_space =
- ioremap(pci_resource_start(tpci200->info->pdev,
+ ioremap_nocache(pci_resource_start(tpci200->info->pdev,
TPCI200_MEM8_SPACE_BAR),
TPCI200_MEM8_SIZE);
+ /* Initialize lock that protects interface_regs */
+ spin_lock_init(&tpci200->regs_lock);
+
ioidint_base = pci_resource_start(tpci200->info->pdev,
TPCI200_IO_ID_INT_SPACES_BAR);
mem_base = pci_resource_start(tpci200->info->pdev,
@@ -437,12 +328,16 @@ static int tpci200_register(struct tpci200_board *tpci200)
TPCI200_ID_SPACE_OFF + TPCI200_ID_SPACE_GAP*i;
tpci200->slots[i].id_phys.size = TPCI200_ID_SPACE_SIZE;
+ tpci200->slots[i].int_phys.address =
+ (void __iomem *)ioidint_base +
+ TPCI200_INT_SPACE_OFF + TPCI200_INT_SPACE_GAP * i;
+ tpci200->slots[i].int_phys.size = TPCI200_INT_SPACE_SIZE;
+
tpci200->slots[i].mem_phys.address =
(void __iomem *)mem_base + TPCI200_MEM8_GAP*i;
tpci200->slots[i].mem_phys.size = TPCI200_MEM8_SIZE;
- writew(slot_ctrl, (tpci200->info->interface_regs +
- control_reg[i]));
+ writew(slot_ctrl, &tpci200->info->interface_regs->control[i]);
}
res = request_irq(tpci200->info->pdev->irq,
@@ -467,70 +362,6 @@ out_disable_pci:
return res;
}
-static int __tpci200_request_irq(struct tpci200_board *tpci200,
- struct ipack_device *dev)
-{
- unsigned short slot_ctrl;
-
- /* Set the default parameters of the slot
- * INT0 enabled, level sensitive
- * INT1 enabled, level sensitive
- * error interrupt disabled
- * timeout interrupt disabled
- * recover time disabled
- * clock rate 8 MHz
- */
- slot_ctrl = TPCI200_INT0_EN | TPCI200_INT1_EN;
- writew(slot_ctrl, (tpci200->info->interface_regs +
- control_reg[dev->slot]));
-
- return 0;
-}
-
-static void __tpci200_free_irq(struct tpci200_board *tpci200,
- struct ipack_device *dev)
-{
- unsigned short slot_ctrl;
-
- /* Set the default parameters of the slot
- * INT0 disabled, level sensitive
- * INT1 disabled, level sensitive
- * error interrupt disabled
- * timeout interrupt disabled
- * recover time disabled
- * clock rate 8 MHz
- */
- slot_ctrl = 0;
- writew(slot_ctrl, (tpci200->info->interface_regs +
- control_reg[dev->slot]));
-}
-
-static int tpci200_free_irq(struct ipack_device *dev)
-{
- struct slot_irq *slot_irq;
- struct tpci200_board *tpci200;
-
- tpci200 = check_slot(dev);
- if (tpci200 == NULL)
- return -EINVAL;
-
- if (mutex_lock_interruptible(&tpci200->mutex))
- return -ERESTARTSYS;
-
- if (tpci200->slots[dev->slot].irq == NULL) {
- mutex_unlock(&tpci200->mutex);
- return -EINVAL;
- }
-
- __tpci200_free_irq(tpci200, dev);
- slot_irq = tpci200->slots[dev->slot].irq;
- tpci200->slots[dev->slot].irq = NULL;
- kfree(slot_irq);
-
- mutex_unlock(&tpci200->mutex);
- return 0;
-}
-
static int tpci200_slot_unmap_space(struct ipack_device *dev, int space)
{
struct ipack_addr_space *virt_addr_space;
@@ -562,6 +393,15 @@ static int tpci200_slot_unmap_space(struct ipack_device *dev, int space)
}
virt_addr_space = &dev->id_space;
break;
+ case IPACK_INT_SPACE:
+ if (dev->int_space.address == NULL) {
+ dev_info(&dev->dev,
+ "Slot [%d:%d] INT space not mapped !\n",
+ dev->bus_nr, dev->slot);
+ goto out_unlock;
+ }
+ virt_addr_space = &dev->int_space;
+ break;
case IPACK_MEM_SPACE:
if (dev->mem_space.address == NULL) {
dev_info(&dev->dev,
@@ -588,29 +428,6 @@ out_unlock:
return 0;
}
-static int tpci200_slot_unregister(struct ipack_device *dev)
-{
- struct tpci200_board *tpci200;
-
- if (dev == NULL)
- return -ENODEV;
-
- tpci200 = check_slot(dev);
- if (tpci200 == NULL)
- return -EINVAL;
-
- tpci200_free_irq(dev);
-
- if (mutex_lock_interruptible(&tpci200->mutex))
- return -ERESTARTSYS;
-
- ipack_device_unregister(dev);
- tpci200->slots[dev->slot].dev = NULL;
- mutex_unlock(&tpci200->mutex);
-
- return 0;
-}
-
static int tpci200_slot_map_space(struct ipack_device *dev,
unsigned int memory_size, int space)
{
@@ -654,6 +471,19 @@ static int tpci200_slot_map_space(struct ipack_device *dev,
phys_address = tpci200->slots[dev->slot].id_phys.address;
size_to_map = tpci200->slots[dev->slot].id_phys.size;
break;
+ case IPACK_INT_SPACE:
+ if (dev->int_space.address != NULL) {
+ dev_err(&dev->dev,
+ "Slot [%d:%d] INT space already mapped !\n",
+ tpci200->number, dev->slot);
+ res = -EINVAL;
+ goto out_unlock;
+ }
+ virt_addr_space = &dev->int_space;
+
+ phys_address = tpci200->slots[dev->slot].int_phys.address;
+ size_to_map = tpci200->slots[dev->slot].int_phys.size;
+ break;
case IPACK_MEM_SPACE:
if (dev->mem_space.address != NULL) {
dev_err(&dev->dev,
@@ -685,85 +515,109 @@ static int tpci200_slot_map_space(struct ipack_device *dev,
virt_addr_space->size = size_to_map;
virt_addr_space->address =
- ioremap((unsigned long)phys_address, size_to_map);
+ ioremap_nocache((unsigned long)phys_address, size_to_map);
out_unlock:
mutex_unlock(&tpci200->mutex);
return res;
}
-static int tpci200_request_irq(struct ipack_device *dev, int vector,
- int (*handler)(void *), void *arg)
+static int tpci200_get_clockrate(struct ipack_device *dev)
{
- int res;
- struct slot_irq *slot_irq;
- struct tpci200_board *tpci200;
+ struct tpci200_board *tpci200 = check_slot(dev);
+ __le16 __iomem *addr;
- tpci200 = check_slot(dev);
- if (tpci200 == NULL)
- return -EINVAL;
+ if (!tpci200)
+ return -ENODEV;
- if (mutex_lock_interruptible(&tpci200->mutex))
- return -ERESTARTSYS;
+ addr = &tpci200->info->interface_regs->control[dev->slot];
+ return (ioread16(addr) & TPCI200_CLK32) ? 32 : 8;
+}
- if (tpci200->slots[dev->slot].irq != NULL) {
- dev_err(&dev->dev,
- "Slot [%d:%d] IRQ already registered !\n", dev->bus_nr,
- dev->slot);
- res = -EINVAL;
- goto out_unlock;
- }
+static int tpci200_set_clockrate(struct ipack_device *dev, int mherz)
+{
+ struct tpci200_board *tpci200 = check_slot(dev);
+ __le16 __iomem *addr;
- slot_irq = kzalloc(sizeof(struct slot_irq), GFP_KERNEL);
- if (slot_irq == NULL) {
- dev_err(&dev->dev,
- "Slot [%d:%d] unable to allocate memory for IRQ !\n",
- dev->bus_nr, dev->slot);
- res = -ENOMEM;
- goto out_unlock;
+ if (!tpci200)
+ return -ENODEV;
+
+ addr = &tpci200->info->interface_regs->control[dev->slot];
+
+ switch (mherz) {
+ case 8:
+ tpci200_clear_mask(tpci200, addr, TPCI200_CLK32);
+ break;
+ case 32:
+ tpci200_set_mask(tpci200, addr, TPCI200_CLK32);
+ break;
+ default:
+ return -EINVAL;
}
+ return 0;
+}
- /*
- * WARNING: Setup Interrupt Vector in the IndustryPack device
- * before an IRQ request.
- * Read the User Manual of your IndustryPack device to know
- * where to write the vector in memory.
- */
- slot_irq->vector = vector;
- slot_irq->handler = handler;
- slot_irq->arg = arg;
+static int tpci200_get_error(struct ipack_device *dev)
+{
+ struct tpci200_board *tpci200 = check_slot(dev);
+ __le16 __iomem *addr;
+ u16 mask;
- tpci200->slots[dev->slot].irq = slot_irq;
- res = __tpci200_request_irq(tpci200, dev);
+ if (!tpci200)
+ return -ENODEV;
-out_unlock:
- mutex_unlock(&tpci200->mutex);
- return res;
+ addr = &tpci200->info->interface_regs->status;
+ mask = tpci200_status_error[dev->slot];
+ return (ioread16(addr) & mask) ? 1 : 0;
}
-static void tpci200_uninstall(struct tpci200_board *tpci200)
+static int tpci200_get_timeout(struct ipack_device *dev)
{
- int i;
+ struct tpci200_board *tpci200 = check_slot(dev);
+ __le16 __iomem *addr;
+ u16 mask;
- for (i = 0; i < TPCI200_NB_SLOT; i++)
- tpci200_slot_unregister(tpci200->slots[i].dev);
+ if (!tpci200)
+ return -ENODEV;
+
+ addr = &tpci200->info->interface_regs->status;
+ mask = tpci200_status_timeout[dev->slot];
+
+ return (ioread16(addr) & mask) ? 1 : 0;
+}
+
+static int tpci200_reset_timeout(struct ipack_device *dev)
+{
+ struct tpci200_board *tpci200 = check_slot(dev);
+ __le16 __iomem *addr;
+ u16 mask;
+
+ if (!tpci200)
+ return -ENODEV;
+
+ addr = &tpci200->info->interface_regs->status;
+ mask = tpci200_status_timeout[dev->slot];
+ iowrite16(mask, addr);
+ return 0;
+}
+
+static void tpci200_uninstall(struct tpci200_board *tpci200)
+{
tpci200_unregister(tpci200);
kfree(tpci200->slots);
}
-static struct ipack_bus_ops tpci200_bus_ops = {
+static const struct ipack_bus_ops tpci200_bus_ops = {
.map_space = tpci200_slot_map_space,
.unmap_space = tpci200_slot_unmap_space,
.request_irq = tpci200_request_irq,
.free_irq = tpci200_free_irq,
- .read8 = tpci200_read8,
- .read16 = tpci200_read16,
- .read32 = tpci200_read32,
- .write8 = tpci200_write8,
- .write16 = tpci200_write16,
- .write32 = tpci200_write32,
- .remove_device = tpci200_slot_unregister,
+ .get_clockrate = tpci200_get_clockrate,
+ .set_clockrate = tpci200_set_clockrate,
+ .get_error = tpci200_get_error,
+ .get_timeout = tpci200_get_timeout,
+ .reset_timeout = tpci200_reset_timeout,
};
static int tpci200_install(struct tpci200_board *tpci200)
@@ -786,11 +640,12 @@ static int tpci200_install(struct tpci200_board *tpci200)
return 0;
}
-static int tpci200_pciprobe(struct pci_dev *pdev,
- const struct pci_device_id *id)
+static int tpci200_pci_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
{
int ret, i;
struct tpci200_board *tpci200;
+ u32 reg32;
tpci200 = kzalloc(sizeof(struct tpci200_board), GFP_KERNEL);
if (!tpci200)
@@ -798,10 +653,40 @@ static int tpci200_pciprobe(struct pci_dev *pdev,
tpci200->info = kzalloc(sizeof(struct tpci200_infos), GFP_KERNEL);
if (!tpci200->info) {
- kfree(tpci200);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out_err_info;
}
+ pci_dev_get(pdev);
+
+ /* Obtain a mapping of the carrier's PCI configuration registers */
+ ret = pci_request_region(pdev, TPCI200_CFG_MEM_BAR,
+ KBUILD_MODNAME " Configuration Memory");
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to allocate PCI Configuration Memory");
+ ret = -EBUSY;
+ goto out_err_pci_request;
+ }
+ tpci200->info->cfg_regs = ioremap_nocache(
+ pci_resource_start(pdev, TPCI200_CFG_MEM_BAR),
+ pci_resource_len(pdev, TPCI200_CFG_MEM_BAR));
+ if (!tpci200->info->cfg_regs) {
+ dev_err(&pdev->dev, "Failed to map PCI Configuration Memory");
+ ret = -EFAULT;
+ goto out_err_ioremap;
+ }
+
+ /* Disable byte swapping for 16 bit IP module access. This will ensure
+ * that the Industrypack big endian byte order is preserved by the
+ * carrier. */
+ reg32 = ioread32(tpci200->info->cfg_regs + LAS1_DESC);
+ reg32 |= 1 << LAS_BIT_BIGENDIAN;
+ iowrite32(reg32, tpci200->info->cfg_regs + LAS1_DESC);
+
+ reg32 = ioread32(tpci200->info->cfg_regs + LAS2_DESC);
+ reg32 |= 1 << LAS_BIT_BIGENDIAN;
+ iowrite32(reg32, tpci200->info->cfg_regs + LAS2_DESC);
+
/* Save struct pci_dev pointer */
tpci200->info->pdev = pdev;
tpci200->info->id_table = (struct pci_device_id *)id;
@@ -809,10 +694,9 @@ static int tpci200_pciprobe(struct pci_dev *pdev,
/* register the device and initialize it */
ret = tpci200_install(tpci200);
if (ret) {
- dev_err(&pdev->dev, "Error during tpci200 install !\n");
- kfree(tpci200->info);
- kfree(tpci200);
- return -ENODEV;
+ dev_err(&pdev->dev, "error during tpci200 install\n");
+ ret = -ENODEV;
+ goto out_err_install;
}
/* Register the carrier in the industry pack bus driver */
@@ -822,48 +706,46 @@ static int tpci200_pciprobe(struct pci_dev *pdev,
if (!tpci200->info->ipack_bus) {
dev_err(&pdev->dev,
"error registering the carrier on ipack driver\n");
- tpci200_uninstall(tpci200);
- kfree(tpci200->info);
- kfree(tpci200);
- return -EFAULT;
+ ret = -EFAULT;
+ goto out_err_bus_register;
}
/* save the bus number given by ipack to logging purpose */
tpci200->number = tpci200->info->ipack_bus->bus_nr;
dev_set_drvdata(&pdev->dev, tpci200);
- /* add the registered device in an internal linked list */
- list_add_tail(&tpci200->list, &tpci200_list);
- /*
- * Give the same IRQ number as the slot number.
- * The TPCI200 has assigned his own two IRQ by PCI bus driver
- */
for (i = 0; i < TPCI200_NB_SLOT; i++)
- tpci200->slots[i].dev =
- ipack_device_register(tpci200->info->ipack_bus, i, i);
+ ipack_device_register(tpci200->info->ipack_bus, i);
+ return 0;
+
+out_err_bus_register:
+ tpci200_uninstall(tpci200);
+out_err_install:
+ iounmap(tpci200->info->cfg_regs);
+out_err_ioremap:
+ pci_release_region(pdev, TPCI200_CFG_MEM_BAR);
+out_err_pci_request:
+ pci_dev_put(pdev);
+ kfree(tpci200->info);
+out_err_info:
+ kfree(tpci200);
return ret;
}
static void __tpci200_pci_remove(struct tpci200_board *tpci200)
{
- tpci200_uninstall(tpci200);
- list_del(&tpci200->list);
ipack_bus_unregister(tpci200->info->ipack_bus);
+ tpci200_uninstall(tpci200);
+
kfree(tpci200->info);
kfree(tpci200);
}
static void __devexit tpci200_pci_remove(struct pci_dev *dev)
{
- struct tpci200_board *tpci200, *next;
+ struct tpci200_board *tpci200 = pci_get_drvdata(dev);
- /* Search the registered device to uninstall it */
- list_for_each_entry_safe(tpci200, next, &tpci200_list, list) {
- if (tpci200->info->pdev == dev) {
- __tpci200_pci_remove(tpci200);
- break;
- }
- }
+ __tpci200_pci_remove(tpci200);
}
static DEFINE_PCI_DEVICE_TABLE(tpci200_idtable) = {
@@ -877,7 +759,7 @@ MODULE_DEVICE_TABLE(pci, tpci200_idtable);
static struct pci_driver tpci200_pci_drv = {
.name = "tpci200",
.id_table = tpci200_idtable,
- .probe = tpci200_pciprobe,
+ .probe = tpci200_pci_probe,
.remove = __devexit_p(tpci200_pci_remove),
};
@@ -888,11 +770,6 @@ static int __init tpci200_drvr_init_module(void)
static void __exit tpci200_drvr_exit_module(void)
{
- struct tpci200_board *tpci200, *next;
-
- list_for_each_entry_safe(tpci200, next, &tpci200_list, list)
- __tpci200_pci_remove(tpci200);
-
pci_unregister_driver(&tpci200_pci_drv);
}
diff --git a/drivers/staging/ipack/bridges/tpci200.h b/drivers/staging/ipack/bridges/tpci200.h
index d04510a89be4..235d1fe4f48c 100644
--- a/drivers/staging/ipack/bridges/tpci200.h
+++ b/drivers/staging/ipack/bridges/tpci200.h
@@ -17,7 +17,6 @@
#include <linux/limits.h>
#include <linux/pci.h>
#include <linux/spinlock.h>
-#include <linux/interrupt.h>
#include <linux/swab.h>
#include <linux/io.h>
@@ -31,18 +30,21 @@
#define TPCI200_SUBVENDOR_ID 0x1498
#define TPCI200_SUBDEVICE_ID 0x300A
+#define TPCI200_CFG_MEM_BAR 0
#define TPCI200_IP_INTERFACE_BAR 2
#define TPCI200_IO_ID_INT_SPACES_BAR 3
#define TPCI200_MEM16_SPACE_BAR 4
#define TPCI200_MEM8_SPACE_BAR 5
-#define TPCI200_REVISION_REG 0x00
-#define TPCI200_CONTROL_A_REG 0x02
-#define TPCI200_CONTROL_B_REG 0x04
-#define TPCI200_CONTROL_C_REG 0x06
-#define TPCI200_CONTROL_D_REG 0x08
-#define TPCI200_RESET_REG 0x0A
-#define TPCI200_STATUS_REG 0x0C
+struct tpci200_regs {
+ __le16 revision;
+ /* writes to control should occur with the mutex held to protect
+ * read-modify-write operations */
+ __le16 control[4];
+ __le16 reset;
+ __le16 status;
+ u8 reserved[242];
+} __packed;
#define TPCI200_IFACE_SIZE 0x100
@@ -62,6 +64,7 @@
#define TPCI200_MEM16_GAP 0x00800000
#define TPCI200_MEM16_SIZE 0x00800000
+/* control field in tpci200_regs */
#define TPCI200_INT0_EN 0x0040
#define TPCI200_INT1_EN 0x0080
#define TPCI200_INT0_EDGE 0x0010
@@ -71,11 +74,13 @@
#define TPCI200_RECOVER_EN 0x0002
#define TPCI200_CLK32 0x0001
+/* reset field in tpci200_regs */
#define TPCI200_A_RESET 0x0001
#define TPCI200_B_RESET 0x0002
#define TPCI200_C_RESET 0x0004
#define TPCI200_D_RESET 0x0008
+/* status field in tpci200_regs */
#define TPCI200_A_TIMEOUT 0x1000
#define TPCI200_B_TIMEOUT 0x2000
#define TPCI200_C_TIMEOUT 0x4000
@@ -97,6 +102,13 @@
#define TPCI200_SLOT_INT_MASK 0x00FF
+/* PCI Configuration registers. The PCI bridge is a PLX Technology PCI9030. */
+#define LAS1_DESC 0x2C
+#define LAS2_DESC 0x30
+
+/* Bits in the LAS?_DESC registers */
+#define LAS_BIT_BIGENDIAN 24
+
#define VME_IOID_SPACE "IOID"
#define VME_MEM_SPACE "MEM"
@@ -108,8 +120,9 @@
*
*/
struct slot_irq {
+ struct ipack_device *holder;
int vector;
- int (*handler)(void *);
+ irqreturn_t (*handler)(void *);
void *arg;
};
@@ -119,14 +132,15 @@ struct slot_irq {
* @irq Slot IRQ infos
* @io_phys IO physical base address register of the slot
* @id_phys ID physical base address register of the slot
+ * @int_phys INT physical base address register of the slot
* @mem_phys MEM physical base address register of the slot
*
*/
struct tpci200_slot {
- struct ipack_device *dev;
struct slot_irq *irq;
struct ipack_addr_space io_phys;
struct ipack_addr_space id_phys;
+ struct ipack_addr_space int_phys;
struct ipack_addr_space mem_phys;
};
@@ -141,15 +155,16 @@ struct tpci200_slot {
struct tpci200_infos {
struct pci_dev *pdev;
struct pci_device_id *id_table;
- void __iomem *interface_regs;
+ struct tpci200_regs __iomem *interface_regs;
void __iomem *ioidint_space;
void __iomem *mem8_space;
+ void __iomem *cfg_regs;
struct ipack_bus_device *ipack_bus;
};
struct tpci200_board {
- struct list_head list;
unsigned int number;
struct mutex mutex;
+ spinlock_t regs_lock;
struct tpci200_slot *slots;
struct tpci200_infos *info;
};
diff --git a/drivers/staging/ipack/devices/ipoctal.c b/drivers/staging/ipack/devices/ipoctal.c
index a68d981c259f..d751edfda839 100644
--- a/drivers/staging/ipack/devices/ipoctal.c
+++ b/drivers/staging/ipack/devices/ipoctal.c
@@ -20,127 +20,68 @@
#include <linux/tty_flip.h>
#include <linux/slab.h>
#include <linux/atomic.h>
+#include <linux/io.h>
#include "../ipack.h"
#include "ipoctal.h"
#include "scc2698.h"
-#define IP_OCTAL_MANUFACTURER_ID 0xF0
-#define IP_OCTAL_232_ID 0x22
-#define IP_OCTAL_422_ID 0x2A
-#define IP_OCTAL_485_ID 0x48
-
#define IP_OCTAL_ID_SPACE_VECTOR 0x41
#define IP_OCTAL_NB_BLOCKS 4
-static struct ipack_driver driver;
static const struct tty_operations ipoctal_fops;
+struct ipoctal_channel {
+ struct ipoctal_stats stats;
+ unsigned int nb_bytes;
+ wait_queue_head_t queue;
+ spinlock_t lock;
+ unsigned int pointer_read;
+ unsigned int pointer_write;
+ atomic_t open;
+ struct tty_port tty_port;
+ union scc2698_channel __iomem *regs;
+ union scc2698_block __iomem *block_regs;
+ unsigned int board_id;
+ unsigned char *board_write;
+ u8 isr_rx_rdy_mask;
+ u8 isr_tx_rdy_mask;
+};
+
struct ipoctal {
- struct list_head list;
struct ipack_device *dev;
unsigned int board_id;
- struct scc2698_channel *chan_regs;
- struct scc2698_block *block_regs;
- struct ipoctal_stats chan_stats[NR_CHANNELS];
- unsigned int nb_bytes[NR_CHANNELS];
- unsigned int count_wr[NR_CHANNELS];
- wait_queue_head_t queue[NR_CHANNELS];
- spinlock_t lock[NR_CHANNELS];
- unsigned int pointer_read[NR_CHANNELS];
- unsigned int pointer_write[NR_CHANNELS];
- atomic_t open[NR_CHANNELS];
+ struct ipoctal_channel channel[NR_CHANNELS];
unsigned char write;
- struct tty_port tty_port[NR_CHANNELS];
struct tty_driver *tty_drv;
};
-/* Linked list to save the registered devices */
-static LIST_HEAD(ipoctal_list);
-
-static inline void ipoctal_write_io_reg(struct ipoctal *ipoctal,
- unsigned char *dest,
- unsigned char value)
-{
- unsigned long offset;
-
- offset = ((void __iomem *) dest) - ipoctal->dev->io_space.address;
- ipoctal->dev->bus->ops->write8(ipoctal->dev, IPACK_IO_SPACE, offset,
- value);
-}
-
-static inline void ipoctal_write_cr_cmd(struct ipoctal *ipoctal,
- unsigned char *dest,
- unsigned char value)
-{
- ipoctal_write_io_reg(ipoctal, dest, value);
-}
-
-static inline unsigned char ipoctal_read_io_reg(struct ipoctal *ipoctal,
- unsigned char *src)
-{
- unsigned long offset;
- unsigned char value;
-
- offset = ((void __iomem *) src) - ipoctal->dev->io_space.address;
- ipoctal->dev->bus->ops->read8(ipoctal->dev, IPACK_IO_SPACE, offset,
- &value);
- return value;
-}
-
-static struct ipoctal *ipoctal_find_board(struct tty_struct *tty)
-{
- struct ipoctal *p;
-
- list_for_each_entry(p, &ipoctal_list, list) {
- if (tty->driver->major == p->tty_drv->major)
- return p;
- }
-
- return NULL;
-}
-
static int ipoctal_port_activate(struct tty_port *port, struct tty_struct *tty)
{
- struct ipoctal *ipoctal;
- int channel = tty->index;
+ struct ipoctal_channel *channel;
- ipoctal = ipoctal_find_board(tty);
+ channel = dev_get_drvdata(tty->dev);
- if (ipoctal == NULL) {
- dev_err(tty->dev, "Device not found. Major %d\n",
- tty->driver->major);
- return -ENODEV;
- }
-
- ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_ENABLE_RX);
+ iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
return 0;
}
static int ipoctal_open(struct tty_struct *tty, struct file *file)
{
- int channel = tty->index;
int res;
- struct ipoctal *ipoctal;
+ struct ipoctal_channel *channel;
- ipoctal = ipoctal_find_board(tty);
-
- if (ipoctal == NULL) {
- dev_err(tty->dev, "Device not found. Major %d\n",
- tty->driver->major);
- return -ENODEV;
- }
+ channel = dev_get_drvdata(tty->dev);
- if (atomic_read(&ipoctal->open[channel]))
+ if (atomic_read(&channel->open))
return -EBUSY;
- tty->driver_data = ipoctal;
+ tty->driver_data = channel;
- res = tty_port_open(&ipoctal->tty_port[channel], tty, file);
+ res = tty_port_open(&channel->tty_port, tty, file);
if (res)
return res;
- atomic_inc(&ipoctal->open[channel]);
+ atomic_inc(&channel->open);
return 0;
}
@@ -154,175 +95,166 @@ static void ipoctal_reset_stats(struct ipoctal_stats *stats)
stats->parity_err = 0;
}
-static void ipoctal_free_channel(struct tty_struct *tty)
+static void ipoctal_free_channel(struct ipoctal_channel *channel)
{
- int channel = tty->index;
- struct ipoctal *ipoctal = tty->driver_data;
-
- if (ipoctal == NULL)
- return;
-
- ipoctal_reset_stats(&ipoctal->chan_stats[channel]);
- ipoctal->pointer_read[channel] = 0;
- ipoctal->pointer_write[channel] = 0;
- ipoctal->nb_bytes[channel] = 0;
+ ipoctal_reset_stats(&channel->stats);
+ channel->pointer_read = 0;
+ channel->pointer_write = 0;
+ channel->nb_bytes = 0;
}
static void ipoctal_close(struct tty_struct *tty, struct file *filp)
{
- int channel = tty->index;
- struct ipoctal *ipoctal = tty->driver_data;
+ struct ipoctal_channel *channel = tty->driver_data;
- tty_port_close(&ipoctal->tty_port[channel], tty, filp);
+ tty_port_close(&channel->tty_port, tty, filp);
- if (atomic_dec_and_test(&ipoctal->open[channel]))
- ipoctal_free_channel(tty);
+ if (atomic_dec_and_test(&channel->open))
+ ipoctal_free_channel(channel);
}
static int ipoctal_get_icount(struct tty_struct *tty,
struct serial_icounter_struct *icount)
{
- struct ipoctal *ipoctal = tty->driver_data;
- int channel = tty->index;
+ struct ipoctal_channel *channel = tty->driver_data;
icount->cts = 0;
icount->dsr = 0;
icount->rng = 0;
icount->dcd = 0;
- icount->rx = ipoctal->chan_stats[channel].rx;
- icount->tx = ipoctal->chan_stats[channel].tx;
- icount->frame = ipoctal->chan_stats[channel].framing_err;
- icount->parity = ipoctal->chan_stats[channel].parity_err;
- icount->brk = ipoctal->chan_stats[channel].rcv_break;
+ icount->rx = channel->stats.rx;
+ icount->tx = channel->stats.tx;
+ icount->frame = channel->stats.framing_err;
+ icount->parity = channel->stats.parity_err;
+ icount->brk = channel->stats.rcv_break;
return 0;
}
-static int ipoctal_irq_handler(void *arg)
+static void ipoctal_irq_rx(struct ipoctal_channel *channel,
+ struct tty_struct *tty, u8 sr)
{
- unsigned int channel;
- unsigned int block;
- unsigned char isr;
- unsigned char sr;
- unsigned char isr_tx_rdy, isr_rx_rdy;
unsigned char value;
- unsigned char flag;
- struct tty_struct *tty;
- struct ipoctal *ipoctal = (struct ipoctal *) arg;
-
- /* Check all channels */
- for (channel = 0; channel < NR_CHANNELS; channel++) {
- /* If there is no client, skip the check */
- if (!atomic_read(&ipoctal->open[channel]))
- continue;
-
- tty = tty_port_tty_get(&ipoctal->tty_port[channel]);
- if (!tty)
- continue;
-
- /*
- * The HW is organized in pair of channels.
- * See which register we need to read from
- */
- block = channel / 2;
- isr = ipoctal_read_io_reg(ipoctal,
- &ipoctal->block_regs[block].u.r.isr);
- sr = ipoctal_read_io_reg(ipoctal,
- &ipoctal->chan_regs[channel].u.r.sr);
-
- if ((channel % 2) == 1) {
- isr_tx_rdy = isr & ISR_TxRDY_B;
- isr_rx_rdy = isr & ISR_RxRDY_FFULL_B;
- } else {
- isr_tx_rdy = isr & ISR_TxRDY_A;
- isr_rx_rdy = isr & ISR_RxRDY_FFULL_A;
+ unsigned char flag = TTY_NORMAL;
+ u8 isr;
+
+ do {
+ value = ioread8(&channel->regs->r.rhr);
+ /* Error: count statistics */
+ if (sr & SR_ERROR) {
+ iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
+
+ if (sr & SR_OVERRUN_ERROR) {
+ channel->stats.overrun_err++;
+ /* Overrun doesn't affect the current character*/
+ tty_insert_flip_char(tty, 0, TTY_OVERRUN);
+ }
+ if (sr & SR_PARITY_ERROR) {
+ channel->stats.parity_err++;
+ flag = TTY_PARITY;
+ }
+ if (sr & SR_FRAMING_ERROR) {
+ channel->stats.framing_err++;
+ flag = TTY_FRAME;
+ }
+ if (sr & SR_RECEIVED_BREAK) {
+ iowrite8(CR_CMD_RESET_BREAK_CHANGE, &channel->regs->w.cr);
+ channel->stats.rcv_break++;
+ flag = TTY_BREAK;
+ }
}
+ tty_insert_flip_char(tty, value, flag);
- /* In case of RS-485, change from TX to RX when finishing TX.
- * Half-duplex.
+ /* Check if there are more characters in RX FIFO
+ * If there are more, the isr register for this channel
+ * has enabled the RxRDY|FFULL bit.
*/
- if ((ipoctal->board_id == IP_OCTAL_485_ID) &&
- (sr & SR_TX_EMPTY) &&
- (ipoctal->nb_bytes[channel] == 0)) {
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->chan_regs[channel].u.w.cr,
- CR_DISABLE_TX);
- ipoctal_write_cr_cmd(ipoctal,
- &ipoctal->chan_regs[channel].u.w.cr,
- CR_CMD_NEGATE_RTSN);
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->chan_regs[channel].u.w.cr,
- CR_ENABLE_RX);
- ipoctal->write = 1;
- wake_up_interruptible(&ipoctal->queue[channel]);
- }
+ isr = ioread8(&channel->block_regs->r.isr);
+ sr = ioread8(&channel->regs->r.sr);
+ } while (isr & channel->isr_rx_rdy_mask);
- /* RX data */
- if (isr_rx_rdy && (sr & SR_RX_READY)) {
- value = ipoctal_read_io_reg(ipoctal,
- &ipoctal->chan_regs[channel].u.r.rhr);
- flag = TTY_NORMAL;
-
- /* Error: count statistics */
- if (sr & SR_ERROR) {
- ipoctal_write_cr_cmd(ipoctal,
- &ipoctal->chan_regs[channel].u.w.cr,
- CR_CMD_RESET_ERR_STATUS);
-
- if (sr & SR_OVERRUN_ERROR) {
- ipoctal->chan_stats[channel].overrun_err++;
- /* Overrun doesn't affect the current character*/
- tty_insert_flip_char(tty, 0, TTY_OVERRUN);
- }
- if (sr & SR_PARITY_ERROR) {
- ipoctal->chan_stats[channel].parity_err++;
- flag = TTY_PARITY;
- }
- if (sr & SR_FRAMING_ERROR) {
- ipoctal->chan_stats[channel].framing_err++;
- flag = TTY_FRAME;
- }
- if (sr & SR_RECEIVED_BREAK) {
- ipoctal->chan_stats[channel].rcv_break++;
- flag = TTY_BREAK;
- }
- }
+ tty_flip_buffer_push(tty);
+}
- tty_insert_flip_char(tty, value, flag);
- }
+static void ipoctal_irq_tx(struct ipoctal_channel *channel)
+{
+ unsigned char value;
+ unsigned int *pointer_write = &channel->pointer_write;
- /* TX of each character */
- if (isr_tx_rdy && (sr & SR_TX_READY)) {
- unsigned int *pointer_write =
- &ipoctal->pointer_write[channel];
+ if (channel->nb_bytes <= 0) {
+ channel->nb_bytes = 0;
+ return;
+ }
- if (ipoctal->nb_bytes[channel] <= 0) {
- ipoctal->nb_bytes[channel] = 0;
- continue;
- }
+ value = channel->tty_port.xmit_buf[*pointer_write];
+ iowrite8(value, &channel->regs->w.thr);
+ channel->stats.tx++;
+ (*pointer_write)++;
+ *pointer_write = *pointer_write % PAGE_SIZE;
+ channel->nb_bytes--;
- value = ipoctal->tty_port[channel].xmit_buf[*pointer_write];
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->chan_regs[channel].u.w.thr,
- value);
- ipoctal->chan_stats[channel].tx++;
- ipoctal->count_wr[channel]++;
- (*pointer_write)++;
- *pointer_write = *pointer_write % PAGE_SIZE;
- ipoctal->nb_bytes[channel]--;
-
- if ((ipoctal->nb_bytes[channel] == 0) &&
- (waitqueue_active(&ipoctal->queue[channel]))) {
-
- if (ipoctal->board_id != IP_OCTAL_485_ID) {
- ipoctal->write = 1;
- wake_up_interruptible(&ipoctal->queue[channel]);
- }
- }
+ if ((channel->nb_bytes == 0) &&
+ (waitqueue_active(&channel->queue))) {
+
+ if (channel->board_id != IPACK1_DEVICE_ID_SBS_OCTAL_485) {
+ *channel->board_write = 1;
+ wake_up_interruptible(&channel->queue);
}
+ }
+}
- tty_flip_buffer_push(tty);
- tty_kref_put(tty);
+static void ipoctal_irq_channel(struct ipoctal_channel *channel)
+{
+ u8 isr, sr;
+ struct tty_struct *tty;
+
+ /* If there is no client, skip the check */
+ if (!atomic_read(&channel->open))
+ return;
+
+ tty = tty_port_tty_get(&channel->tty_port);
+ if (!tty)
+ return;
+ /* The HW is organized in pair of channels. See which register we need
+ * to read from */
+ isr = ioread8(&channel->block_regs->r.isr);
+ sr = ioread8(&channel->regs->r.sr);
+
+ /* In case of RS-485, change from TX to RX when finishing TX.
+ * Half-duplex. */
+ if ((channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) &&
+ (sr & SR_TX_EMPTY) && (channel->nb_bytes == 0)) {
+ iowrite8(CR_DISABLE_TX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_NEGATE_RTSN, &channel->regs->w.cr);
+ iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
+ *channel->board_write = 1;
+ wake_up_interruptible(&channel->queue);
}
+
+ /* RX data */
+ if ((isr & channel->isr_rx_rdy_mask) && (sr & SR_RX_READY))
+ ipoctal_irq_rx(channel, tty, sr);
+
+ /* TX of each character */
+ if ((isr & channel->isr_tx_rdy_mask) && (sr & SR_TX_READY))
+ ipoctal_irq_tx(channel);
+
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+}
+
+static irqreturn_t ipoctal_irq_handler(void *arg)
+{
+ unsigned int i;
+ struct ipoctal *ipoctal = (struct ipoctal *) arg;
+
+ /* Check all channels */
+ for (i = 0; i < NR_CHANNELS; i++)
+ ipoctal_irq_channel(&ipoctal->channel[i]);
+
+ /* Clear the IPack device interrupt */
+ readw(ipoctal->dev->int_space.address + ACK_INT_REQ0);
+ readw(ipoctal->dev->int_space.address + ACK_INT_REQ1);
+
return IRQ_HANDLED;
}
@@ -331,18 +263,15 @@ static int ipoctal_check_model(struct ipack_device *dev, unsigned char *id)
unsigned char manufacturerID;
unsigned char board_id;
- dev->bus->ops->read8(dev, IPACK_ID_SPACE,
- IPACK_IDPROM_OFFSET_MANUFACTURER_ID, &manufacturerID);
- if (manufacturerID != IP_OCTAL_MANUFACTURER_ID)
- return -ENODEV;
-
- dev->bus->ops->read8(dev, IPACK_ID_SPACE,
- IPACK_IDPROM_OFFSET_MODEL, (unsigned char *)&board_id);
+ manufacturerID = ioread8(dev->id_space.address + IPACK_IDPROM_OFFSET_MANUFACTURER_ID);
+ if (manufacturerID != IPACK1_VENDOR_ID_SBS)
+ return -ENODEV;
+ board_id = ioread8(dev->id_space.address + IPACK_IDPROM_OFFSET_MODEL);
switch (board_id) {
- case IP_OCTAL_232_ID:
- case IP_OCTAL_422_ID:
- case IP_OCTAL_485_ID:
+ case IPACK1_DEVICE_ID_SBS_OCTAL_232:
+ case IPACK1_DEVICE_ID_SBS_OCTAL_422:
+ case IPACK1_DEVICE_ID_SBS_OCTAL_485:
*id = board_id;
break;
default:
@@ -358,13 +287,16 @@ static const struct tty_port_operations ipoctal_tty_port_ops = {
};
static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
- unsigned int slot, unsigned int vector)
+ unsigned int slot)
{
int res = 0;
int i;
struct tty_driver *tty;
char name[20];
unsigned char board_id;
+ struct ipoctal_channel *channel;
+ union scc2698_channel __iomem *chan_regs;
+ union scc2698_block __iomem *block_regs;
res = ipoctal->dev->bus->ops->map_space(ipoctal->dev, 0,
IPACK_ID_SPACE);
@@ -392,54 +324,61 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
goto out_unregister_id_space;
}
+ res = ipoctal->dev->bus->ops->map_space(ipoctal->dev, 0,
+ IPACK_INT_SPACE);
+ if (res) {
+ dev_err(&ipoctal->dev->dev,
+ "Unable to map slot [%d:%d] INT space!\n",
+ bus_nr, slot);
+ goto out_unregister_io_space;
+ }
+
res = ipoctal->dev->bus->ops->map_space(ipoctal->dev,
0x8000, IPACK_MEM_SPACE);
if (res) {
dev_err(&ipoctal->dev->dev,
"Unable to map slot [%d:%d] MEM space!\n",
bus_nr, slot);
- goto out_unregister_io_space;
+ goto out_unregister_int_space;
}
/* Save the virtual address to access the registers easily */
- ipoctal->chan_regs =
- (struct scc2698_channel *) ipoctal->dev->io_space.address;
- ipoctal->block_regs =
- (struct scc2698_block *) ipoctal->dev->io_space.address;
+ chan_regs =
+ (union scc2698_channel __iomem *) ipoctal->dev->io_space.address;
+ block_regs =
+ (union scc2698_block __iomem *) ipoctal->dev->io_space.address;
/* Disable RX and TX before touching anything */
for (i = 0; i < NR_CHANNELS ; i++) {
- ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[i].u.w.cr,
- CR_DISABLE_RX | CR_DISABLE_TX);
- ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[i].u.w.cr,
- CR_CMD_RESET_RX);
- ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[i].u.w.cr,
- CR_CMD_RESET_TX);
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->chan_regs[i].u.w.mr,
- MR1_CHRL_8_BITS | MR1_ERROR_CHAR |
- MR1_RxINT_RxRDY); /* mr1 */
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->chan_regs[i].u.w.mr,
- 0); /* mr2 */
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->chan_regs[i].u.w.csr,
- TX_CLK_9600 | RX_CLK_9600);
+ struct ipoctal_channel *channel = &ipoctal->channel[i];
+ channel->regs = chan_regs + i;
+ channel->block_regs = block_regs + (i >> 1);
+ channel->board_write = &ipoctal->write;
+ channel->board_id = ipoctal->board_id;
+ if (i & 1) {
+ channel->isr_tx_rdy_mask = ISR_TxRDY_B;
+ channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_B;
+ } else {
+ channel->isr_tx_rdy_mask = ISR_TxRDY_A;
+ channel->isr_rx_rdy_mask = ISR_RxRDY_FFULL_A;
+ }
+
+ iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
+ iowrite8(MR1_CHRL_8_BITS | MR1_ERROR_CHAR | MR1_RxINT_RxRDY,
+ &channel->regs->w.mr); /* mr1 */
+ iowrite8(0, &channel->regs->w.mr); /* mr2 */
+ iowrite8(TX_CLK_9600 | RX_CLK_9600, &channel->regs->w.csr);
}
for (i = 0; i < IP_OCTAL_NB_BLOCKS; i++) {
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->block_regs[i].u.w.acr,
- ACR_BRG_SET2);
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->block_regs[i].u.w.opcr,
- OPCR_MPP_OUTPUT | OPCR_MPOa_RTSN |
- OPCR_MPOb_RTSN);
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->block_regs[i].u.w.imr,
- IMR_TxRDY_A | IMR_RxRDY_FFULL_A |
- IMR_DELTA_BREAK_A | IMR_TxRDY_B |
- IMR_RxRDY_FFULL_B | IMR_DELTA_BREAK_B);
+ iowrite8(ACR_BRG_SET2, &block_regs[i].w.acr);
+ iowrite8(OPCR_MPP_OUTPUT | OPCR_MPOa_RTSN | OPCR_MPOb_RTSN,
+ &block_regs[i].w.opcr);
+ iowrite8(IMR_TxRDY_A | IMR_RxRDY_FFULL_A | IMR_DELTA_BREAK_A |
+ IMR_TxRDY_B | IMR_RxRDY_FFULL_B | IMR_DELTA_BREAK_B,
+ &block_regs[i].w.imr);
}
/*
@@ -447,10 +386,10 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
* Depending of the carrier these addresses are accesible or not.
* More info in the datasheet.
*/
- ipoctal->dev->bus->ops->request_irq(ipoctal->dev, vector,
+ ipoctal->dev->bus->ops->request_irq(ipoctal->dev,
ipoctal_irq_handler, ipoctal);
- ipoctal->dev->bus->ops->write8(ipoctal->dev, IPACK_MEM_SPACE, 0,
- vector);
+ /* Dummy write */
+ iowrite8(1, ipoctal->dev->mem_space.address + 1);
/* Register the TTY device */
@@ -464,8 +403,8 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
/* Fill struct tty_driver with ipoctal data */
tty->owner = THIS_MODULE;
- tty->driver_name = "ipoctal";
- sprintf(name, "ipoctal.%d.%d.", bus_nr, slot);
+ tty->driver_name = KBUILD_MODNAME;
+ sprintf(name, KBUILD_MODNAME ".%d.%d.", bus_nr, slot);
tty->name = name;
tty->major = 0;
@@ -490,32 +429,40 @@ static int ipoctal_inst_slot(struct ipoctal *ipoctal, unsigned int bus_nr,
ipoctal->tty_drv = tty;
for (i = 0; i < NR_CHANNELS; i++) {
- tty_port_init(&ipoctal->tty_port[i]);
- tty_port_alloc_xmit_buf(&ipoctal->tty_port[i]);
- ipoctal->tty_port[i].ops = &ipoctal_tty_port_ops;
-
- ipoctal_reset_stats(&ipoctal->chan_stats[i]);
- ipoctal->nb_bytes[i] = 0;
- init_waitqueue_head(&ipoctal->queue[i]);
-
- spin_lock_init(&ipoctal->lock[i]);
- ipoctal->pointer_read[i] = 0;
- ipoctal->pointer_write[i] = 0;
- ipoctal->nb_bytes[i] = 0;
- tty_port_register_device(&ipoctal->tty_port[i], tty, i, NULL);
+ struct device *tty_dev;
+
+ channel = &ipoctal->channel[i];
+ tty_port_init(&channel->tty_port);
+ tty_port_alloc_xmit_buf(&channel->tty_port);
+ channel->tty_port.ops = &ipoctal_tty_port_ops;
+
+ ipoctal_reset_stats(&channel->stats);
+ channel->nb_bytes = 0;
+ init_waitqueue_head(&channel->queue);
+
+ spin_lock_init(&channel->lock);
+ channel->pointer_read = 0;
+ channel->pointer_write = 0;
+ tty_dev = tty_port_register_device(&channel->tty_port, tty, i, NULL);
+ if (IS_ERR(tty_dev)) {
+ dev_err(&ipoctal->dev->dev, "Failed to register tty device.\n");
+ continue;
+ }
+ dev_set_drvdata(tty_dev, channel);
/*
* Enable again the RX. TX will be enabled when
* there is something to send
*/
- ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[i].u.w.cr,
- CR_ENABLE_RX);
+ iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
}
return 0;
out_unregister_slot_unmap:
ipoctal->dev->bus->ops->unmap_space(ipoctal->dev, IPACK_ID_SPACE);
+out_unregister_int_space:
+ ipoctal->dev->bus->ops->unmap_space(ipoctal->dev, IPACK_INT_SPACE);
out_unregister_io_space:
ipoctal->dev->bus->ops->unmap_space(ipoctal->dev, IPACK_IO_SPACE);
out_unregister_id_space:
@@ -523,23 +470,22 @@ out_unregister_id_space:
return res;
}
-static inline int ipoctal_copy_write_buffer(struct ipoctal *ipoctal,
- unsigned int channel,
+static inline int ipoctal_copy_write_buffer(struct ipoctal_channel *channel,
const unsigned char *buf,
int count)
{
unsigned long flags;
int i;
- unsigned int *pointer_read = &ipoctal->pointer_read[channel];
+ unsigned int *pointer_read = &channel->pointer_read;
/* Copy the bytes from the user buffer to the internal one */
for (i = 0; i < count; i++) {
- if (i <= (PAGE_SIZE - ipoctal->nb_bytes[channel])) {
- spin_lock_irqsave(&ipoctal->lock[channel], flags);
- ipoctal->tty_port[channel].xmit_buf[*pointer_read] = buf[i];
+ if (i <= (PAGE_SIZE - channel->nb_bytes)) {
+ spin_lock_irqsave(&channel->lock, flags);
+ channel->tty_port.xmit_buf[*pointer_read] = buf[i];
*pointer_read = (*pointer_read + 1) % PAGE_SIZE;
- ipoctal->nb_bytes[channel]++;
- spin_unlock_irqrestore(&ipoctal->lock[channel], flags);
+ channel->nb_bytes++;
+ spin_unlock_irqrestore(&channel->lock, flags);
} else {
break;
}
@@ -547,63 +493,44 @@ static inline int ipoctal_copy_write_buffer(struct ipoctal *ipoctal,
return i;
}
-static int ipoctal_write(struct ipoctal *ipoctal, unsigned int channel,
- const unsigned char *buf, int count)
+static int ipoctal_write_tty(struct tty_struct *tty,
+ const unsigned char *buf, int count)
{
- ipoctal->nb_bytes[channel] = 0;
- ipoctal->count_wr[channel] = 0;
+ struct ipoctal_channel *channel = tty->driver_data;
+ unsigned int char_copied;
- ipoctal_copy_write_buffer(ipoctal, channel, buf, count);
+ char_copied = ipoctal_copy_write_buffer(channel, buf, count);
/* As the IP-OCTAL 485 only supports half duplex, do it manually */
- if (ipoctal->board_id == IP_OCTAL_485_ID) {
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->chan_regs[channel].u.w.cr,
- CR_DISABLE_RX);
- ipoctal_write_cr_cmd(ipoctal,
- &ipoctal->chan_regs[channel].u.w.cr,
- CR_CMD_ASSERT_RTSN);
+ if (channel->board_id == IPACK1_DEVICE_ID_SBS_OCTAL_485) {
+ iowrite8(CR_DISABLE_RX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_ASSERT_RTSN, &channel->regs->w.cr);
}
/*
* Send a packet and then disable TX to avoid failure after several send
* operations
*/
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->chan_regs[channel].u.w.cr,
- CR_ENABLE_TX);
- wait_event_interruptible(ipoctal->queue[channel], ipoctal->write);
- ipoctal_write_io_reg(ipoctal,
- &ipoctal->chan_regs[channel].u.w.cr,
- CR_DISABLE_TX);
-
- ipoctal->write = 0;
- return ipoctal->count_wr[channel];
-}
-
-static int ipoctal_write_tty(struct tty_struct *tty,
- const unsigned char *buf, int count)
-{
- unsigned int channel = tty->index;
- struct ipoctal *ipoctal = tty->driver_data;
+ iowrite8(CR_ENABLE_TX, &channel->regs->w.cr);
+ wait_event_interruptible(channel->queue, *channel->board_write);
+ iowrite8(CR_DISABLE_TX, &channel->regs->w.cr);
- return ipoctal_write(ipoctal, channel, buf, count);
+ *channel->board_write = 0;
+ return char_copied;
}
static int ipoctal_write_room(struct tty_struct *tty)
{
- int channel = tty->index;
- struct ipoctal *ipoctal = tty->driver_data;
+ struct ipoctal_channel *channel = tty->driver_data;
- return PAGE_SIZE - ipoctal->nb_bytes[channel];
+ return PAGE_SIZE - channel->nb_bytes;
}
static int ipoctal_chars_in_buffer(struct tty_struct *tty)
{
- int channel = tty->index;
- struct ipoctal *ipoctal = tty->driver_data;
+ struct ipoctal_channel *channel = tty->driver_data;
- return ipoctal->nb_bytes[channel];
+ return channel->nb_bytes;
}
static void ipoctal_set_termios(struct tty_struct *tty,
@@ -613,23 +540,17 @@ static void ipoctal_set_termios(struct tty_struct *tty,
unsigned char mr1 = 0;
unsigned char mr2 = 0;
unsigned char csr = 0;
- unsigned int channel = tty->index;
- struct ipoctal *ipoctal = tty->driver_data;
+ struct ipoctal_channel *channel = tty->driver_data;
speed_t baud;
cflag = tty->termios.c_cflag;
/* Disable and reset everything before change the setup */
- ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_DISABLE_RX | CR_DISABLE_TX);
- ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_CMD_RESET_RX);
- ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_CMD_RESET_TX);
- ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_CMD_RESET_ERR_STATUS);
- ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_CMD_RESET_MR);
+ iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr);
/* Set Bits per chars */
switch (cflag & CSIZE) {
@@ -666,8 +587,8 @@ static void ipoctal_set_termios(struct tty_struct *tty,
mr2 |= MR2_STOP_BITS_LENGTH_1;
/* Set the flow control */
- switch (ipoctal->board_id) {
- case IP_OCTAL_232_ID:
+ switch (channel->board_id) {
+ case IPACK1_DEVICE_ID_SBS_OCTAL_232:
if (cflag & CRTSCTS) {
mr1 |= MR1_RxRTS_CONTROL_ON;
mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_ON;
@@ -676,11 +597,11 @@ static void ipoctal_set_termios(struct tty_struct *tty,
mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF;
}
break;
- case IP_OCTAL_422_ID:
+ case IPACK1_DEVICE_ID_SBS_OCTAL_422:
mr1 |= MR1_RxRTS_CONTROL_OFF;
mr2 |= MR2_TxRTS_CONTROL_OFF | MR2_CTS_ENABLE_TX_OFF;
break;
- case IP_OCTAL_485_ID:
+ case IPACK1_DEVICE_ID_SBS_OCTAL_485:
mr1 |= MR1_RxRTS_CONTROL_OFF;
mr2 |= MR2_TxRTS_CONTROL_ON | MR2_CTS_ENABLE_TX_OFF;
break;
@@ -742,45 +663,38 @@ static void ipoctal_set_termios(struct tty_struct *tty,
mr1 |= MR1_RxINT_RxRDY;
/* Write the control registers */
- ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.mr, mr1);
- ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.mr, mr2);
- ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.csr, csr);
+ iowrite8(mr1, &channel->regs->w.mr);
+ iowrite8(mr2, &channel->regs->w.mr);
+ iowrite8(csr, &channel->regs->w.csr);
/* Enable again the RX */
- ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_ENABLE_RX);
+ iowrite8(CR_ENABLE_RX, &channel->regs->w.cr);
}
static void ipoctal_hangup(struct tty_struct *tty)
{
unsigned long flags;
- int channel = tty->index;
- struct ipoctal *ipoctal = tty->driver_data;
+ struct ipoctal_channel *channel = tty->driver_data;
- if (ipoctal == NULL)
+ if (channel == NULL)
return;
- spin_lock_irqsave(&ipoctal->lock[channel], flags);
- ipoctal->nb_bytes[channel] = 0;
- ipoctal->pointer_read[channel] = 0;
- ipoctal->pointer_write[channel] = 0;
- spin_unlock_irqrestore(&ipoctal->lock[channel], flags);
-
- tty_port_hangup(&ipoctal->tty_port[channel]);
-
- ipoctal_write_io_reg(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_DISABLE_RX | CR_DISABLE_TX);
- ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_CMD_RESET_RX);
- ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_CMD_RESET_TX);
- ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_CMD_RESET_ERR_STATUS);
- ipoctal_write_cr_cmd(ipoctal, &ipoctal->chan_regs[channel].u.w.cr,
- CR_CMD_RESET_MR);
-
- clear_bit(ASYNCB_INITIALIZED, &ipoctal->tty_port[channel].flags);
- wake_up_interruptible(&ipoctal->tty_port[channel].open_wait);
+ spin_lock_irqsave(&channel->lock, flags);
+ channel->nb_bytes = 0;
+ channel->pointer_read = 0;
+ channel->pointer_write = 0;
+ spin_unlock_irqrestore(&channel->lock, flags);
+
+ tty_port_hangup(&channel->tty_port);
+
+ iowrite8(CR_DISABLE_RX | CR_DISABLE_TX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_RX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_TX, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_ERR_STATUS, &channel->regs->w.cr);
+ iowrite8(CR_CMD_RESET_MR, &channel->regs->w.cr);
+
+ clear_bit(ASYNCB_INITIALIZED, &channel->tty_port.flags);
+ wake_up_interruptible(&channel->tty_port.open_wait);
}
static const struct tty_operations ipoctal_fops = {
@@ -795,27 +709,6 @@ static const struct tty_operations ipoctal_fops = {
.hangup = ipoctal_hangup,
};
-static int ipoctal_match(struct ipack_device *dev)
-{
- int res;
- unsigned char board_id;
-
- if ((!dev->bus->ops) || (!dev->bus->ops->map_space) ||
- (!dev->bus->ops->unmap_space))
- return 0;
-
- res = dev->bus->ops->map_space(dev, 0, IPACK_ID_SPACE);
- if (res)
- return 0;
-
- res = ipoctal_check_model(dev, &board_id);
- dev->bus->ops->unmap_space(dev, IPACK_ID_SPACE);
- if (!res)
- return 1;
-
- return 0;
-}
-
static int ipoctal_probe(struct ipack_device *dev)
{
int res;
@@ -826,11 +719,11 @@ static int ipoctal_probe(struct ipack_device *dev)
return -ENOMEM;
ipoctal->dev = dev;
- res = ipoctal_inst_slot(ipoctal, dev->bus_nr, dev->slot, dev->irq);
+ res = ipoctal_inst_slot(ipoctal, dev->bus_nr, dev->slot);
if (res)
goto out_uninst;
- list_add_tail(&ipoctal->list, &ipoctal_list);
+ dev_set_drvdata(&dev->dev, ipoctal);
return 0;
out_uninst:
@@ -842,46 +735,57 @@ static void __ipoctal_remove(struct ipoctal *ipoctal)
{
int i;
+ ipoctal->dev->bus->ops->free_irq(ipoctal->dev);
+
for (i = 0; i < NR_CHANNELS; i++) {
+ struct ipoctal_channel *channel = &ipoctal->channel[i];
tty_unregister_device(ipoctal->tty_drv, i);
- tty_port_free_xmit_buf(&ipoctal->tty_port[i]);
+ tty_port_free_xmit_buf(&channel->tty_port);
}
tty_unregister_driver(ipoctal->tty_drv);
put_tty_driver(ipoctal->tty_drv);
- list_del(&ipoctal->list);
+ ipoctal->dev->bus->ops->unmap_space(ipoctal->dev, IPACK_MEM_SPACE);
+ ipoctal->dev->bus->ops->unmap_space(ipoctal->dev, IPACK_INT_SPACE);
+ ipoctal->dev->bus->ops->unmap_space(ipoctal->dev, IPACK_IO_SPACE);
+ ipoctal->dev->bus->ops->unmap_space(ipoctal->dev, IPACK_ID_SPACE);
kfree(ipoctal);
}
-static void ipoctal_remove(struct ipack_device *device)
+static void ipoctal_remove(struct ipack_device *idev)
{
- struct ipoctal *ipoctal, *next;
-
- list_for_each_entry_safe(ipoctal, next, &ipoctal_list, list) {
- if (ipoctal->dev == device)
- __ipoctal_remove(ipoctal);
- }
+ __ipoctal_remove(dev_get_drvdata(&idev->dev));
}
-static struct ipack_driver_ops ipoctal_drv_ops = {
- .match = ipoctal_match,
- .probe = ipoctal_probe,
+static DEFINE_IPACK_DEVICE_TABLE(ipoctal_ids) = {
+ { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS,
+ IPACK1_DEVICE_ID_SBS_OCTAL_232) },
+ { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS,
+ IPACK1_DEVICE_ID_SBS_OCTAL_422) },
+ { IPACK_DEVICE(IPACK_ID_VERSION_1, IPACK1_VENDOR_ID_SBS,
+ IPACK1_DEVICE_ID_SBS_OCTAL_485) },
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(ipack, ipoctal_ids);
+
+static const struct ipack_driver_ops ipoctal_drv_ops = {
+ .probe = ipoctal_probe,
.remove = ipoctal_remove,
};
+static struct ipack_driver driver = {
+ .ops = &ipoctal_drv_ops,
+ .id_table = ipoctal_ids,
+};
+
static int __init ipoctal_init(void)
{
- driver.ops = &ipoctal_drv_ops;
return ipack_driver_register(&driver, THIS_MODULE, KBUILD_MODNAME);
}
static void __exit ipoctal_exit(void)
{
- struct ipoctal *p, *next;
-
- list_for_each_entry_safe(p, next, &ipoctal_list, list)
- p->dev->bus->ops->remove_device(p->dev);
-
ipack_driver_unregister(&driver);
}
diff --git a/drivers/staging/ipack/devices/scc2698.h b/drivers/staging/ipack/devices/scc2698.h
index 47f6269985fd..96e8d8c30e14 100644
--- a/drivers/staging/ipack/devices/scc2698.h
+++ b/drivers/staging/ipack/devices/scc2698.h
@@ -15,78 +15,74 @@
#define SCC2698_H_
/*
- * struct scc2698_channel - Channel access to scc2698 IO
+ * union scc2698_channel - Channel access to scc2698 IO
*
* dn value are only spacer.
*
*/
-struct scc2698_channel {
- union {
- struct {
- unsigned char d0, mr; /* Mode register 1/2*/
- unsigned char d1, sr; /* Status register */
- unsigned char d2, r1; /* reserved */
- unsigned char d3, rhr; /* Receive holding register (R) */
- unsigned char junk[8]; /* other crap for block control */
- } r; /* Read access */
- struct {
- unsigned char d0, mr; /* Mode register 1/2 */
- unsigned char d1, csr; /* Clock select register */
- unsigned char d2, cr; /* Command register */
- unsigned char d3, thr; /* Transmit holding register */
- unsigned char junk[8]; /* other crap for block control */
- } w; /* Write access */
- } u;
+union scc2698_channel {
+ struct {
+ u8 d0, mr; /* Mode register 1/2*/
+ u8 d1, sr; /* Status register */
+ u8 d2, r1; /* reserved */
+ u8 d3, rhr; /* Receive holding register (R) */
+ u8 junk[8]; /* other crap for block control */
+ } __packed r; /* Read access */
+ struct {
+ u8 d0, mr; /* Mode register 1/2 */
+ u8 d1, csr; /* Clock select register */
+ u8 d2, cr; /* Command register */
+ u8 d3, thr; /* Transmit holding register */
+ u8 junk[8]; /* other crap for block control */
+ } __packed w; /* Write access */
};
/*
- * struct scc2698_block - Block access to scc2698 IO
+ * union scc2698_block - Block access to scc2698 IO
*
* The scc2698 contain 4 block.
* Each block containt two channel a and b.
* dn value are only spacer.
*
*/
-struct scc2698_block {
- union {
- struct {
- unsigned char d0, mra; /* Mode register 1/2 (a) */
- unsigned char d1, sra; /* Status register (a) */
- unsigned char d2, r1; /* reserved */
- unsigned char d3, rhra; /* Receive holding register (a) */
- unsigned char d4, ipcr; /* Input port change register of block */
- unsigned char d5, isr; /* Interrupt status register of block */
- unsigned char d6, ctur; /* Counter timer upper register of block */
- unsigned char d7, ctlr; /* Counter timer lower register of block */
- unsigned char d8, mrb; /* Mode register 1/2 (b) */
- unsigned char d9, srb; /* Status register (b) */
- unsigned char da, r2; /* reserved */
- unsigned char db, rhrb; /* Receive holding register (b) */
- unsigned char dc, r3; /* reserved */
- unsigned char dd, ip; /* Input port register of block */
- unsigned char de, ctg; /* Start counter timer of block */
- unsigned char df, cts; /* Stop counter timer of block */
- } r; /* Read access */
- struct {
- unsigned char d0, mra; /* Mode register 1/2 (a) */
- unsigned char d1, csra; /* Clock select register (a) */
- unsigned char d2, cra; /* Command register (a) */
- unsigned char d3, thra; /* Transmit holding register (a) */
- unsigned char d4, acr; /* Auxiliary control register of block */
- unsigned char d5, imr; /* Interrupt mask register of block */
- unsigned char d6, ctu; /* Counter timer upper register of block */
- unsigned char d7, ctl; /* Counter timer lower register of block */
- unsigned char d8, mrb; /* Mode register 1/2 (b) */
- unsigned char d9, csrb; /* Clock select register (a) */
- unsigned char da, crb; /* Command register (b) */
- unsigned char db, thrb; /* Transmit holding register (b) */
- unsigned char dc, r1; /* reserved */
- unsigned char dd, opcr; /* Output port configuration register of block */
- unsigned char de, r2; /* reserved */
- unsigned char df, r3; /* reserved */
- } w; /* Write access */
- } u;
-} ;
+union scc2698_block {
+ struct {
+ u8 d0, mra; /* Mode register 1/2 (a) */
+ u8 d1, sra; /* Status register (a) */
+ u8 d2, r1; /* reserved */
+ u8 d3, rhra; /* Receive holding register (a) */
+ u8 d4, ipcr; /* Input port change register of block */
+ u8 d5, isr; /* Interrupt status register of block */
+ u8 d6, ctur; /* Counter timer upper register of block */
+ u8 d7, ctlr; /* Counter timer lower register of block */
+ u8 d8, mrb; /* Mode register 1/2 (b) */
+ u8 d9, srb; /* Status register (b) */
+ u8 da, r2; /* reserved */
+ u8 db, rhrb; /* Receive holding register (b) */
+ u8 dc, r3; /* reserved */
+ u8 dd, ip; /* Input port register of block */
+ u8 de, ctg; /* Start counter timer of block */
+ u8 df, cts; /* Stop counter timer of block */
+ } __packed r; /* Read access */
+ struct {
+ u8 d0, mra; /* Mode register 1/2 (a) */
+ u8 d1, csra; /* Clock select register (a) */
+ u8 d2, cra; /* Command register (a) */
+ u8 d3, thra; /* Transmit holding register (a) */
+ u8 d4, acr; /* Auxiliary control register of block */
+ u8 d5, imr; /* Interrupt mask register of block */
+ u8 d6, ctu; /* Counter timer upper register of block */
+ u8 d7, ctl; /* Counter timer lower register of block */
+ u8 d8, mrb; /* Mode register 1/2 (b) */
+ u8 d9, csrb; /* Clock select register (a) */
+ u8 da, crb; /* Command register (b) */
+ u8 db, thrb; /* Transmit holding register (b) */
+ u8 dc, r1; /* reserved */
+ u8 dd, opcr; /* Output port configuration register of block */
+ u8 de, r2; /* reserved */
+ u8 df, r3; /* reserved */
+ } __packed w; /* Write access */
+};
#define MR1_CHRL_5_BITS (0x0 << 0)
#define MR1_CHRL_6_BITS (0x1 << 0)
@@ -225,4 +221,7 @@ struct scc2698_block {
#define ISR_DELTA_BREAK_B (0x1 << 6)
#define ISR_INPUT_PORT_CHANGE (0x1 << 7)
+#define ACK_INT_REQ0 0
+#define ACK_INT_REQ1 2
+
#endif /* SCC2698_H_ */
diff --git a/drivers/staging/ipack/ipack.c b/drivers/staging/ipack/ipack.c
index c1cd97a4d9ce..d1e0651592a2 100644
--- a/drivers/staging/ipack/ipack.c
+++ b/drivers/staging/ipack/ipack.c
@@ -12,6 +12,7 @@
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/idr.h>
+#include <asm/io.h>
#include "ipack.h"
#define to_ipack_dev(device) container_of(device, struct ipack_device, dev)
@@ -22,55 +23,190 @@ static DEFINE_IDA(ipack_ida);
static void ipack_device_release(struct device *dev)
{
struct ipack_device *device = to_ipack_dev(dev);
+ kfree(device->id);
kfree(device);
}
-static int ipack_bus_match(struct device *device, struct device_driver *driver)
+static inline const struct ipack_device_id *
+ipack_match_one_device(const struct ipack_device_id *id,
+ const struct ipack_device *device)
{
- int ret;
- struct ipack_device *dev = to_ipack_dev(device);
- struct ipack_driver *drv = to_ipack_driver(driver);
+ if ((id->format == IPACK_ANY_FORMAT ||
+ id->format == device->id_format) &&
+ (id->vendor == IPACK_ANY_ID || id->vendor == device->id_vendor) &&
+ (id->device == IPACK_ANY_ID || id->device == device->id_device))
+ return id;
+ return NULL;
+}
- if ((!drv->ops) || (!drv->ops->match))
- return -EINVAL;
+static const struct ipack_device_id *
+ipack_match_id(const struct ipack_device_id *ids, struct ipack_device *idev)
+{
+ if (ids) {
+ while (ids->vendor || ids->device) {
+ if (ipack_match_one_device(ids, idev))
+ return ids;
+ ids++;
+ }
+ }
+ return NULL;
+}
- ret = drv->ops->match(dev);
- if (ret)
- dev->driver = drv;
+static int ipack_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct ipack_device *idev = to_ipack_dev(dev);
+ struct ipack_driver *idrv = to_ipack_driver(drv);
+ const struct ipack_device_id *found_id;
- return ret;
+ found_id = ipack_match_id(idrv->id_table, idev);
+ return found_id ? 1 : 0;
}
static int ipack_bus_probe(struct device *device)
{
struct ipack_device *dev = to_ipack_dev(device);
+ struct ipack_driver *drv = to_ipack_driver(device->driver);
- if (!dev->driver->ops->probe)
+ if (!drv->ops->probe)
return -EINVAL;
- return dev->driver->ops->probe(dev);
+ return drv->ops->probe(dev);
}
static int ipack_bus_remove(struct device *device)
{
struct ipack_device *dev = to_ipack_dev(device);
+ struct ipack_driver *drv = to_ipack_driver(device->driver);
- if (!dev->driver->ops->remove)
+ if (!drv->ops->remove)
return -EINVAL;
- dev->driver->ops->remove(dev);
+ drv->ops->remove(dev);
+ return 0;
+}
+
+#ifdef CONFIG_HOTPLUG
+
+static int ipack_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct ipack_device *idev;
+
+ if (!dev)
+ return -ENODEV;
+
+ idev = to_ipack_dev(dev);
+
+ if (add_uevent_var(env,
+ "MODALIAS=ipack:f%02Xv%08Xd%08X", idev->id_format,
+ idev->id_vendor, idev->id_device))
+ return -ENOMEM;
+
return 0;
}
+#else /* !CONFIG_HOTPLUG */
+
+#define ipack_uevent NULL
+
+#endif /* !CONFIG_HOTPLUG */
+
+#define ipack_device_attr(field, format_string) \
+static ssize_t \
+field##_show(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct ipack_device *idev = to_ipack_dev(dev); \
+ return sprintf(buf, format_string, idev->field); \
+}
+
+static ssize_t id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned int i, c, l, s;
+ struct ipack_device *idev = to_ipack_dev(dev);
+
+
+ switch (idev->id_format) {
+ case IPACK_ID_VERSION_1:
+ l = 0x7; s = 1; break;
+ case IPACK_ID_VERSION_2:
+ l = 0xf; s = 2; break;
+ default:
+ return -EIO;
+ }
+ c = 0;
+ for (i = 0; i < idev->id_avail; i++) {
+ if (i > 0) {
+ if ((i & l) == 0)
+ buf[c++] = '\n';
+ else if ((i & s) == 0)
+ buf[c++] = ' ';
+ }
+ sprintf(&buf[c], "%02x", idev->id[i]);
+ c += 2;
+ }
+ buf[c++] = '\n';
+ return c;
+}
+
+static ssize_t
+id_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ipack_device *idev = to_ipack_dev(dev);
+ switch (idev->id_format) {
+ case IPACK_ID_VERSION_1:
+ return sprintf(buf, "0x%02x\n", idev->id_vendor);
+ case IPACK_ID_VERSION_2:
+ return sprintf(buf, "0x%06x\n", idev->id_vendor);
+ default:
+ return -EIO;
+ }
+}
+
+static ssize_t
+id_device_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct ipack_device *idev = to_ipack_dev(dev);
+ switch (idev->id_format) {
+ case IPACK_ID_VERSION_1:
+ return sprintf(buf, "0x%02x\n", idev->id_device);
+ case IPACK_ID_VERSION_2:
+ return sprintf(buf, "0x%04x\n", idev->id_device);
+ default:
+ return -EIO;
+ }
+}
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct ipack_device *idev = to_ipack_dev(dev);
+
+ return sprintf(buf, "ipac:f%02Xv%08Xd%08X", idev->id_format,
+ idev->id_vendor, idev->id_device);
+}
+
+ipack_device_attr(id_format, "0x%hhu\n");
+
+static struct device_attribute ipack_dev_attrs[] = {
+ __ATTR_RO(id),
+ __ATTR_RO(id_device),
+ __ATTR_RO(id_format),
+ __ATTR_RO(id_vendor),
+ __ATTR_RO(modalias),
+};
+
static struct bus_type ipack_bus_type = {
- .name = "ipack",
- .probe = ipack_bus_probe,
- .match = ipack_bus_match,
- .remove = ipack_bus_remove,
+ .name = "ipack",
+ .probe = ipack_bus_probe,
+ .match = ipack_bus_match,
+ .remove = ipack_bus_remove,
+ .dev_attrs = ipack_dev_attrs,
+ .uevent = ipack_uevent,
};
struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots,
- struct ipack_bus_ops *ops)
+ const struct ipack_bus_ops *ops)
{
int bus_nr;
struct ipack_bus_device *bus;
@@ -93,8 +229,20 @@ struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots,
}
EXPORT_SYMBOL_GPL(ipack_bus_register);
+static int ipack_unregister_bus_member(struct device *dev, void *data)
+{
+ struct ipack_device *idev = to_ipack_dev(dev);
+ struct ipack_bus_device *bus = data;
+
+ if (idev->bus_nr == bus->bus_nr)
+ ipack_device_unregister(idev);
+
+ return 1;
+}
+
int ipack_bus_unregister(struct ipack_bus_device *bus)
{
+ bus_for_each_dev(&ipack_bus_type, NULL, bus, ipack_unregister_bus_member);
ida_simple_remove(&ipack_ida, bus->bus_nr);
kfree(bus);
return 0;
@@ -102,7 +250,7 @@ int ipack_bus_unregister(struct ipack_bus_device *bus)
EXPORT_SYMBOL_GPL(ipack_bus_unregister);
int ipack_driver_register(struct ipack_driver *edrv, struct module *owner,
- char *name)
+ const char *name)
{
edrv->driver.owner = owner;
edrv->driver.name = name;
@@ -117,8 +265,169 @@ void ipack_driver_unregister(struct ipack_driver *edrv)
}
EXPORT_SYMBOL_GPL(ipack_driver_unregister);
+static u16 ipack_crc_byte(u16 crc, u8 c)
+{
+ int i;
+
+ crc ^= c << 8;
+ for (i = 0; i < 8; i++)
+ crc = (crc << 1) ^ ((crc & 0x8000) ? 0x1021 : 0);
+ return crc;
+}
+
+/*
+ * The algorithm in lib/crc-ccitt.c does not seem to apply since it uses the
+ * opposite bit ordering.
+ */
+static u8 ipack_calc_crc1(struct ipack_device *dev)
+{
+ u8 c;
+ u16 crc;
+ unsigned int i;
+
+ crc = 0xffff;
+ for (i = 0; i < dev->id_avail; i++) {
+ c = (i != 11) ? dev->id[i] : 0;
+ crc = ipack_crc_byte(crc, c);
+ }
+ crc = ~crc;
+ return crc & 0xff;
+}
+
+static u16 ipack_calc_crc2(struct ipack_device *dev)
+{
+ u8 c;
+ u16 crc;
+ unsigned int i;
+
+ crc = 0xffff;
+ for (i = 0; i < dev->id_avail; i++) {
+ c = ((i != 0x18) && (i != 0x19)) ? dev->id[i] : 0;
+ crc = ipack_crc_byte(crc, c);
+ }
+ crc = ~crc;
+ return crc;
+}
+
+static void ipack_parse_id1(struct ipack_device *dev)
+{
+ u8 *id = dev->id;
+ u8 crc;
+
+ dev->id_vendor = id[4];
+ dev->id_device = id[5];
+ dev->speed_8mhz = 1;
+ dev->speed_32mhz = (id[7] == 'H');
+ crc = ipack_calc_crc1(dev);
+ dev->id_crc_correct = (crc == id[11]);
+ if (!dev->id_crc_correct) {
+ dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n",
+ id[11], crc);
+ }
+}
+
+static void ipack_parse_id2(struct ipack_device *dev)
+{
+ __be16 *id = (__be16 *) dev->id;
+ u16 flags, crc;
+
+ dev->id_vendor = ((be16_to_cpu(id[3]) & 0xff) << 16)
+ + be16_to_cpu(id[4]);
+ dev->id_device = be16_to_cpu(id[5]);
+ flags = be16_to_cpu(id[10]);
+ dev->speed_8mhz = !!(flags & 2);
+ dev->speed_32mhz = !!(flags & 4);
+ crc = ipack_calc_crc2(dev);
+ dev->id_crc_correct = (crc == be16_to_cpu(id[12]));
+ if (!dev->id_crc_correct) {
+ dev_warn(&dev->dev, "ID CRC invalid found 0x%x, expected 0x%x.\n",
+ id[11], crc);
+ }
+}
+
+static int ipack_device_read_id(struct ipack_device *dev)
+{
+ u8 __iomem *idmem;
+ int i;
+ int ret = 0;
+
+ ret = dev->bus->ops->map_space(dev, 0, IPACK_ID_SPACE);
+ if (ret) {
+ dev_err(&dev->dev, "error mapping memory\n");
+ return ret;
+ }
+ idmem = dev->id_space.address;
+
+ /* Determine ID PROM Data Format. If we find the ids "IPAC" or "IPAH"
+ * we are dealing with a IndustryPack format 1 device. If we detect
+ * "VITA4 " (16 bit big endian formatted) we are dealing with a
+ * IndustryPack format 2 device */
+ if ((ioread8(idmem + 1) == 'I') &&
+ (ioread8(idmem + 3) == 'P') &&
+ (ioread8(idmem + 5) == 'A') &&
+ ((ioread8(idmem + 7) == 'C') ||
+ (ioread8(idmem + 7) == 'H'))) {
+ dev->id_format = IPACK_ID_VERSION_1;
+ dev->id_avail = ioread8(idmem + 0x15);
+ if ((dev->id_avail < 0x0c) || (dev->id_avail > 0x40)) {
+ dev_warn(&dev->dev, "invalid id size");
+ dev->id_avail = 0x0c;
+ }
+ } else if ((ioread8(idmem + 0) == 'I') &&
+ (ioread8(idmem + 1) == 'V') &&
+ (ioread8(idmem + 2) == 'A') &&
+ (ioread8(idmem + 3) == 'T') &&
+ (ioread8(idmem + 4) == ' ') &&
+ (ioread8(idmem + 5) == '4')) {
+ dev->id_format = IPACK_ID_VERSION_2;
+ dev->id_avail = ioread16be(idmem + 0x16);
+ if ((dev->id_avail < 0x1a) || (dev->id_avail > 0x40)) {
+ dev_warn(&dev->dev, "invalid id size");
+ dev->id_avail = 0x1a;
+ }
+ } else {
+ dev->id_format = IPACK_ID_VERSION_INVALID;
+ dev->id_avail = 0;
+ }
+
+ if (!dev->id_avail) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /* Obtain the amount of memory required to store a copy of the complete
+ * ID ROM contents */
+ dev->id = kmalloc(dev->id_avail, GFP_KERNEL);
+ if (!dev->id) {
+ dev_err(&dev->dev, "dev->id alloc failed.\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ for (i = 0; i < dev->id_avail; i++) {
+ if (dev->id_format == IPACK_ID_VERSION_1)
+ dev->id[i] = ioread8(idmem + (i << 1) + 1);
+ else
+ dev->id[i] = ioread8(idmem + i);
+ }
+
+ /* now we can finally work with the copy */
+ switch (dev->id_format) {
+ case IPACK_ID_VERSION_1:
+ ipack_parse_id1(dev);
+ break;
+ case IPACK_ID_VERSION_2:
+ ipack_parse_id2(dev);
+ break;
+ }
+
+out:
+ dev->bus->ops->unmap_space(dev, IPACK_ID_SPACE);
+
+ return ret;
+}
+
struct ipack_device *ipack_device_register(struct ipack_bus_device *bus,
- int slot, int irqv)
+ int slot)
{
int ret;
struct ipack_device *dev;
@@ -132,13 +441,32 @@ struct ipack_device *ipack_device_register(struct ipack_bus_device *bus,
dev->dev.parent = bus->parent;
dev->slot = slot;
dev->bus_nr = bus->bus_nr;
- dev->irq = irqv;
dev->bus = bus;
dev_set_name(&dev->dev,
"ipack-dev.%u.%u", dev->bus_nr, dev->slot);
+ if (bus->ops->set_clockrate(dev, 8))
+ dev_warn(&dev->dev, "failed to switch to 8 MHz operation for reading of device ID.\n");
+ if (bus->ops->reset_timeout(dev))
+ dev_warn(&dev->dev, "failed to reset potential timeout.");
+
+ ret = ipack_device_read_id(dev);
+ if (ret < 0) {
+ dev_err(&dev->dev, "error reading device id section.\n");
+ kfree(dev);
+ return NULL;
+ }
+
+ /* if the device supports 32 MHz operation, use it. */
+ if (dev->speed_32mhz) {
+ ret = bus->ops->set_clockrate(dev, 32);
+ if (ret < 0)
+ dev_err(&dev->dev, "failed to switch to 32 MHz operation.\n");
+ }
+
ret = device_register(&dev->dev);
if (ret < 0) {
+ kfree(dev->id);
kfree(dev);
return NULL;
}
diff --git a/drivers/staging/ipack/ipack.h b/drivers/staging/ipack/ipack.h
index 8bc001e3ca4e..d8e3bb6feac8 100644
--- a/drivers/staging/ipack/ipack.h
+++ b/drivers/staging/ipack/ipack.h
@@ -9,7 +9,11 @@
* Software Foundation; version 2 of the License.
*/
+#include <linux/mod_devicetable.h>
#include <linux/device.h>
+#include <linux/interrupt.h>
+
+#include "ipack_ids.h"
#define IPACK_IDPROM_OFFSET_I 0x01
#define IPACK_IDPROM_OFFSET_P 0x03
@@ -31,6 +35,7 @@ enum ipack_space {
IPACK_IO_SPACE = 0,
IPACK_ID_SPACE = 1,
IPACK_MEM_SPACE = 2,
+ IPACK_INT_SPACE,
};
/**
@@ -49,8 +54,6 @@ struct ipack_addr_space {
*
* @bus_nr: IP bus number where the device is plugged
* @slot: Slot where the device is plugged in the carrier board
- * @irq: IRQ vector
- * @driver: Pointer to the ipack_driver that manages the device
* @bus: ipack_bus_device where the device is plugged to.
* @id_space: Virtual address to ID space.
* @io_space: Virtual address to IO space.
@@ -64,25 +67,30 @@ struct ipack_addr_space {
struct ipack_device {
unsigned int bus_nr;
unsigned int slot;
- unsigned int irq;
- struct ipack_driver *driver;
struct ipack_bus_device *bus;
struct ipack_addr_space id_space;
struct ipack_addr_space io_space;
+ struct ipack_addr_space int_space;
struct ipack_addr_space mem_space;
struct device dev;
+ u8 *id;
+ size_t id_avail;
+ u32 id_vendor;
+ u32 id_device;
+ u8 id_format;
+ unsigned int id_crc_correct:1;
+ unsigned int speed_8mhz:1;
+ unsigned int speed_32mhz:1;
};
/**
* struct ipack_driver_ops -- callbacks to mezzanine driver for installing/removing one device
*
- * @match: Match function
* @probe: Probe function
* @remove: tell the driver that the carrier board wants to remove one device
*/
struct ipack_driver_ops {
- int (*match) (struct ipack_device *dev);
int (*probe) (struct ipack_device *dev);
void (*remove) (struct ipack_device *dev);
};
@@ -95,7 +103,8 @@ struct ipack_driver_ops {
*/
struct ipack_driver {
struct device_driver driver;
- struct ipack_driver_ops *ops;
+ const struct ipack_device_id *id_table;
+ const struct ipack_driver_ops *ops;
};
/**
@@ -105,26 +114,27 @@ struct ipack_driver {
* @unmap_space: unmap IP address space
* @request_irq: request IRQ
* @free_irq: free IRQ
- * @read8: read unsigned char
- * @read16: read unsigned short
- * @read32: read unsigned int
- * @write8: read unsigned char
- * @write16: read unsigned short
- * @write32: read unsigned int
- * @remove_device: tell the bridge module that the device has been removed
+ * @get_clockrate: Returns the clockrate the carrier is currently
+ * communicating with the device at.
+ * @set_clockrate: Sets the clock-rate for carrier / module communication.
+ * Should return -EINVAL if the requested speed is not supported.
+ * @get_error: Returns the error state for the slot the device is attached
+ * to.
+ * @get_timeout: Returns 1 if the communication with the device has
+ * previously timed out.
+ * @reset_timeout: Resets the state returned by get_timeout.
*/
struct ipack_bus_ops {
int (*map_space) (struct ipack_device *dev, unsigned int memory_size, int space);
int (*unmap_space) (struct ipack_device *dev, int space);
- int (*request_irq) (struct ipack_device *dev, int vector, int (*handler)(void *), void *arg);
+ int (*request_irq) (struct ipack_device *dev,
+ irqreturn_t (*handler)(void *), void *arg);
int (*free_irq) (struct ipack_device *dev);
- int (*read8) (struct ipack_device *dev, int space, unsigned long offset, unsigned char *value);
- int (*read16) (struct ipack_device *dev, int space, unsigned long offset, unsigned short *value);
- int (*read32) (struct ipack_device *dev, int space, unsigned long offset, unsigned int *value);
- int (*write8) (struct ipack_device *dev, int space, unsigned long offset, unsigned char value);
- int (*write16) (struct ipack_device *dev, int space, unsigned long offset, unsigned short value);
- int (*write32) (struct ipack_device *dev, int space, unsigned long offset, unsigned int value);
- int (*remove_device) (struct ipack_device *dev);
+ int (*get_clockrate) (struct ipack_device *dev);
+ int (*set_clockrate) (struct ipack_device *dev, int mherz);
+ int (*get_error) (struct ipack_device *dev);
+ int (*get_timeout) (struct ipack_device *dev);
+ int (*reset_timeout) (struct ipack_device *dev);
};
/**
@@ -139,7 +149,7 @@ struct ipack_bus_device {
struct device *parent;
int slots;
int bus_nr;
- struct ipack_bus_ops *ops;
+ const struct ipack_bus_ops *ops;
};
/**
@@ -153,7 +163,7 @@ struct ipack_bus_device {
* available bus device in ipack.
*/
struct ipack_bus_device *ipack_bus_register(struct device *parent, int slots,
- struct ipack_bus_ops *ops);
+ const struct ipack_bus_ops *ops);
/**
* ipack_bus_unregister -- unregister an ipack bus
@@ -166,7 +176,8 @@ int ipack_bus_unregister(struct ipack_bus_device *bus);
* Called by a ipack driver to register itself as a driver
* that can manage ipack devices.
*/
-int ipack_driver_register(struct ipack_driver *edrv, struct module *owner, char *name);
+int ipack_driver_register(struct ipack_driver *edrv, struct module *owner,
+ const char *name);
void ipack_driver_unregister(struct ipack_driver *edrv);
/**
@@ -174,10 +185,33 @@ void ipack_driver_unregister(struct ipack_driver *edrv);
*
* @bus: ipack bus device it is plugged to.
* @slot: slot position in the bus device.
- * @irqv: IRQ vector for the mezzanine.
*
* Register a new ipack device (mezzanine device). The call is done by
* the carrier device driver.
*/
-struct ipack_device *ipack_device_register(struct ipack_bus_device *bus, int slot, int irqv);
+struct ipack_device *ipack_device_register(struct ipack_bus_device *bus, int slot);
void ipack_device_unregister(struct ipack_device *dev);
+
+/**
+ * DEFINE_IPACK_DEVICE_TABLE - macro used to describe a IndustryPack table
+ * @_table: device table name
+ *
+ * This macro is used to create a struct ipack_device_id array (a device table)
+ * in a generic manner.
+ */
+#define DEFINE_IPACK_DEVICE_TABLE(_table) \
+ const struct ipack_device_id _table[] __devinitconst
+
+/**
+ * IPACK_DEVICE - macro used to describe a specific IndustryPack device
+ * @_format: the format version (currently either 1 or 2, 8 bit value)
+ * @vend: the 8 or 24 bit IndustryPack Vendor ID
+ * @dev: the 8 or 16 bit IndustryPack Device ID
+ *
+ * This macro is used to create a struct ipack_device_id that matches a specific
+ * device.
+ */
+#define IPACK_DEVICE(_format, vend, dev) \
+ .format = (_format), \
+ .vendor = (vend), \
+ .device = (dev)
diff --git a/drivers/staging/ipack/ipack_ids.h b/drivers/staging/ipack/ipack_ids.h
new file mode 100644
index 000000000000..8153fee3f2f7
--- /dev/null
+++ b/drivers/staging/ipack/ipack_ids.h
@@ -0,0 +1,32 @@
+/*
+ * IndustryPack Fromat, Vendor and Device IDs.
+ */
+
+/* ID section format versions */
+#define IPACK_ID_VERSION_INVALID 0x00
+#define IPACK_ID_VERSION_1 0x01
+#define IPACK_ID_VERSION_2 0x02
+
+/* Vendors and devices. Sort key: vendor first, device next. */
+#define IPACK1_VENDOR_ID_RESERVED1 0x00
+#define IPACK1_VENDOR_ID_RESERVED2 0xFF
+#define IPACK1_VENDOR_ID_UNREGISTRED01 0x01
+#define IPACK1_VENDOR_ID_UNREGISTRED02 0x02
+#define IPACK1_VENDOR_ID_UNREGISTRED03 0x03
+#define IPACK1_VENDOR_ID_UNREGISTRED04 0x04
+#define IPACK1_VENDOR_ID_UNREGISTRED05 0x05
+#define IPACK1_VENDOR_ID_UNREGISTRED06 0x06
+#define IPACK1_VENDOR_ID_UNREGISTRED07 0x07
+#define IPACK1_VENDOR_ID_UNREGISTRED08 0x08
+#define IPACK1_VENDOR_ID_UNREGISTRED09 0x09
+#define IPACK1_VENDOR_ID_UNREGISTRED10 0x0A
+#define IPACK1_VENDOR_ID_UNREGISTRED11 0x0B
+#define IPACK1_VENDOR_ID_UNREGISTRED12 0x0C
+#define IPACK1_VENDOR_ID_UNREGISTRED13 0x0D
+#define IPACK1_VENDOR_ID_UNREGISTRED14 0x0E
+#define IPACK1_VENDOR_ID_UNREGISTRED15 0x0F
+
+#define IPACK1_VENDOR_ID_SBS 0xF0
+#define IPACK1_DEVICE_ID_SBS_OCTAL_232 0x22
+#define IPACK1_DEVICE_ID_SBS_OCTAL_422 0x2A
+#define IPACK1_DEVICE_ID_SBS_OCTAL_485 0x48
diff --git a/drivers/staging/keucr/smcommon.h b/drivers/staging/keucr/smcommon.h
index 278bdb871129..4d57203b64d8 100644
--- a/drivers/staging/keucr/smcommon.h
+++ b/drivers/staging/keucr/smcommon.h
@@ -25,7 +25,5 @@ Define Difinetion
#define ERR_NoSmartMedia 0x003A /* Medium Not Present */
/***************************************************************************/
-void StringCopy(char *, char *, int);
-int StringCmp(char *, char *, int);
#endif
diff --git a/drivers/staging/keucr/usb.c b/drivers/staging/keucr/usb.c
index 483303402735..55a0b82c6391 100644
--- a/drivers/staging/keucr/usb.c
+++ b/drivers/staging/keucr/usb.c
@@ -320,7 +320,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id)
us->subclass = idesc->bInterfaceSubClass;
us->protocol = idesc->bInterfaceProtocol;
- us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
+ us->fflags = id->driver_info;
us->Power_IsResum = false;
if (us->fflags & US_FL_IGNORE_DEVICE)
diff --git a/drivers/staging/line6/pcm.c b/drivers/staging/line6/pcm.c
index 5e319e3ce685..7fe44a6fd0ed 100644
--- a/drivers/staging/line6/pcm.c
+++ b/drivers/staging/line6/pcm.c
@@ -48,7 +48,13 @@ static ssize_t pcm_set_impulse_volume(struct device *dev,
const char *buf, size_t count)
{
struct snd_line6_pcm *line6pcm = dev2pcm(dev);
- int value = simple_strtoul(buf, NULL, 10);
+ int value;
+ int rv;
+
+ rv = kstrtoint(buf, 10, &value);
+ if (rv < 0)
+ return rv;
+
line6pcm->impulse_volume = value;
if (value > 0)
diff --git a/drivers/staging/line6/variax.c b/drivers/staging/line6/variax.c
index bb99ee4919e7..f97416b1de54 100644
--- a/drivers/staging/line6/variax.c
+++ b/drivers/staging/line6/variax.c
@@ -353,10 +353,10 @@ static ssize_t variax_set_model(struct device *dev,
{
struct usb_line6_variax *variax =
usb_get_intfdata(to_usb_interface(dev));
- unsigned long value;
+ u8 value;
int ret;
- ret = strict_strtoul(buf, 10, &value);
+ ret = kstrtou8(buf, 10, &value);
if (ret)
return ret;
@@ -387,10 +387,10 @@ static ssize_t variax_set_active(struct device *dev,
{
struct usb_line6_variax *variax =
usb_get_intfdata(to_usb_interface(dev));
- unsigned long value;
+ u8 value;
int ret;
- ret = strict_strtoul(buf, 10, &value);
+ ret = kstrtou8(buf, 10, &value);
if (ret)
return ret;
diff --git a/drivers/staging/nvec/Kconfig b/drivers/staging/nvec/Kconfig
index 43048e9ba0d4..1235a7897d04 100644
--- a/drivers/staging/nvec/Kconfig
+++ b/drivers/staging/nvec/Kconfig
@@ -28,7 +28,7 @@ config NVEC_POWER
config NVEC_PAZ00
bool "Support for OEM specific functions on Compal PAZ00 based devices"
- depends on MFD_NVEC && LEDS_CLASS && MACH_PAZ00
+ depends on MFD_NVEC && LEDS_CLASS
help
Say Y to enable control of the yellow side leds on Compal PAZ00 based
devices, e.g. Toshbia AC100 and Dynabooks AZ netbooks.
diff --git a/drivers/staging/nvec/nvec.c b/drivers/staging/nvec/nvec.c
index d0a7e408efe9..24d8eebc1d10 100644
--- a/drivers/staging/nvec/nvec.c
+++ b/drivers/staging/nvec/nvec.c
@@ -264,7 +264,7 @@ int nvec_write_async(struct nvec_chip *nvec, const unsigned char *data,
list_add_tail(&msg->node, &nvec->tx_data);
spin_unlock_irqrestore(&nvec->tx_lock, flags);
- queue_work(nvec->wq, &nvec->tx_work);
+ queue_work(system_nrt_wq, &nvec->tx_work);
return 0;
}
@@ -294,8 +294,10 @@ struct nvec_msg *nvec_write_sync(struct nvec_chip *nvec,
nvec->sync_write_pending = (data[1] << 8) + data[0];
- if (nvec_write_async(nvec, data, size) < 0)
+ if (nvec_write_async(nvec, data, size) < 0) {
+ mutex_unlock(&nvec->sync_write_mutex);
return NULL;
+ }
dev_dbg(nvec->dev, "nvec_sync_write: 0x%04x\n",
nvec->sync_write_pending);
@@ -366,8 +368,7 @@ static void nvec_request_master(struct work_struct *work)
static int parse_msg(struct nvec_chip *nvec, struct nvec_msg *msg)
{
if ((msg->data[0] & 1 << 7) == 0 && msg->data[3]) {
- dev_err(nvec->dev, "ec responded %02x %02x %02x %02x\n",
- msg->data[0], msg->data[1], msg->data[2], msg->data[3]);
+ dev_err(nvec->dev, "ec responded %*ph\n", 4, msg->data);
return -EINVAL;
}
@@ -470,7 +471,7 @@ static void nvec_rx_completed(struct nvec_chip *nvec)
if (!nvec_msg_is_event(nvec->rx))
complete(&nvec->ec_transfer);
- queue_work(nvec->wq, &nvec->rx_work);
+ queue_work(system_nrt_wq, &nvec->rx_work);
}
/**
@@ -737,12 +738,14 @@ static int __devinit tegra_nvec_probe(struct platform_device *pdev)
nvec->gpio = pdata->gpio;
nvec->i2c_addr = pdata->i2c_addr;
} else if (nvec->dev->of_node) {
- nvec->gpio = of_get_named_gpio(nvec->dev->of_node, "request-gpios", 0);
+ nvec->gpio = of_get_named_gpio(nvec->dev->of_node,
+ "request-gpios", 0);
if (nvec->gpio < 0) {
dev_err(&pdev->dev, "no gpio specified");
return -ENODEV;
}
- if (of_property_read_u32(nvec->dev->of_node, "slave-addr", &nvec->i2c_addr)) {
+ if (of_property_read_u32(nvec->dev->of_node,
+ "slave-addr", &nvec->i2c_addr)) {
dev_err(&pdev->dev, "no i2c address specified");
return -ENODEV;
}
@@ -769,7 +772,7 @@ static int __devinit tegra_nvec_probe(struct platform_device *pdev)
return -ENODEV;
}
- i2c_clk = clk_get_sys("tegra-i2c.2", NULL);
+ i2c_clk = clk_get_sys("tegra-i2c.2", "div-clk");
if (IS_ERR(i2c_clk)) {
dev_err(nvec->dev, "failed to get controller clock\n");
return -ENODEV;
@@ -791,13 +794,11 @@ static int __devinit tegra_nvec_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&nvec->tx_data);
INIT_WORK(&nvec->rx_work, nvec_dispatch);
INIT_WORK(&nvec->tx_work, nvec_request_master);
- nvec->wq = alloc_workqueue("nvec", WQ_NON_REENTRANT, 2);
err = devm_gpio_request_one(&pdev->dev, nvec->gpio, GPIOF_OUT_INIT_HIGH,
"nvec gpio");
if (err < 0) {
dev_err(nvec->dev, "couldn't request gpio\n");
- destroy_workqueue(nvec->wq);
return -ENODEV;
}
@@ -805,7 +806,6 @@ static int __devinit tegra_nvec_probe(struct platform_device *pdev)
"nvec", nvec);
if (err) {
dev_err(nvec->dev, "couldn't request irq\n");
- destroy_workqueue(nvec->wq);
return -ENODEV;
}
disable_irq(nvec->irq);
@@ -859,7 +859,8 @@ static int __devexit tegra_nvec_remove(struct platform_device *pdev)
nvec_write_async(nvec, EC_DISABLE_EVENT_REPORTING, 3);
mfd_remove_devices(nvec->dev);
- destroy_workqueue(nvec->wq);
+ cancel_work_sync(&nvec->rx_work);
+ cancel_work_sync(&nvec->tx_work);
return 0;
}
diff --git a/drivers/staging/olpc_dcon/olpc_dcon.c b/drivers/staging/olpc_dcon/olpc_dcon.c
index 2c4bd746715a..d49c32a95690 100644
--- a/drivers/staging/olpc_dcon/olpc_dcon.c
+++ b/drivers/staging/olpc_dcon/olpc_dcon.c
@@ -11,6 +11,7 @@
* License as published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/fb.h>
@@ -72,18 +73,16 @@ static int dcon_hw_init(struct dcon_priv *dcon, int is_init)
ver = dcon_read(dcon, DCON_REG_ID);
if ((ver >> 8) != 0xDC) {
- printk(KERN_ERR "olpc-dcon: DCON ID not 0xDCxx: 0x%04x instead.\n",
- ver);
+ pr_err("DCON ID not 0xDCxx: 0x%04x instead.\n", ver);
rc = -ENXIO;
goto err;
}
if (is_init) {
- printk(KERN_INFO "olpc-dcon: Discovered DCON version %x\n",
- ver & 0xFF);
+ pr_info("Discovered DCON version %x\n", ver & 0xFF);
rc = pdata->init(dcon);
if (rc != 0) {
- printk(KERN_ERR "olpc-dcon: Unable to init.\n");
+ pr_err("Unable to init.\n");
goto err;
}
}
@@ -137,8 +136,7 @@ power_up:
x = 1;
x = olpc_ec_cmd(0x26, (unsigned char *)&x, 1, NULL, 0);
if (x) {
- printk(KERN_WARNING "olpc-dcon: unable to force dcon to power up: %d!\n",
- x);
+ pr_warn("unable to force dcon to power up: %d!\n", x);
return x;
}
msleep(10); /* we'll be conservative */
@@ -151,7 +149,7 @@ power_up:
x = dcon_read(dcon, DCON_REG_ID);
}
if (x < 0) {
- printk(KERN_ERR "olpc-dcon: unable to stabilize dcon's smbus, reasserting power and praying.\n");
+ pr_err("unable to stabilize dcon's smbus, reasserting power and praying.\n");
BUG_ON(olpc_board_at_least(olpc_board(0xc2)));
x = 0;
olpc_ec_cmd(0x26, (unsigned char *)&x, 1, NULL, 0);
@@ -222,8 +220,7 @@ static void dcon_sleep(struct dcon_priv *dcon, bool sleep)
x = 0;
x = olpc_ec_cmd(0x26, (unsigned char *)&x, 1, NULL, 0);
if (x)
- printk(KERN_WARNING "olpc-dcon: unable to force dcon to power down: %d!\n",
- x);
+ pr_warn("unable to force dcon to power down: %d!\n", x);
else
dcon->asleep = sleep;
} else {
@@ -232,8 +229,7 @@ static void dcon_sleep(struct dcon_priv *dcon, bool sleep)
dcon->disp_mode |= MODE_BL_ENABLE;
x = dcon_bus_stabilize(dcon, 1);
if (x)
- printk(KERN_WARNING "olpc-dcon: unable to reinit dcon hardware: %d!\n",
- x);
+ pr_warn("unable to reinit dcon hardware: %d!\n", x);
else
dcon->asleep = sleep;
@@ -304,12 +300,11 @@ static void dcon_source_switch(struct work_struct *work)
switch (source) {
case DCON_SOURCE_CPU:
- printk(KERN_INFO "dcon_source_switch to CPU\n");
+ pr_info("dcon_source_switch to CPU\n");
/* Enable the scanline interrupt bit */
if (dcon_write(dcon, DCON_REG_MODE,
dcon->disp_mode | MODE_SCAN_INT))
- printk(KERN_ERR
- "olpc-dcon: couldn't enable scanline interrupt!\n");
+ pr_err("couldn't enable scanline interrupt!\n");
else {
/* Wait up to one second for the scanline interrupt */
wait_event_timeout(dcon_wait_queue,
@@ -317,11 +312,11 @@ static void dcon_source_switch(struct work_struct *work)
}
if (!dcon->switched)
- printk(KERN_ERR "olpc-dcon: Timeout entering CPU mode; expect a screen glitch.\n");
+ pr_err("Timeout entering CPU mode; expect a screen glitch.\n");
/* Turn off the scanline interrupt */
if (dcon_write(dcon, DCON_REG_MODE, dcon->disp_mode))
- printk(KERN_ERR "olpc-dcon: couldn't disable scanline interrupt!\n");
+ pr_err("couldn't disable scanline interrupt!\n");
/*
* Ideally we'd like to disable interrupts here so that the
@@ -332,7 +327,7 @@ static void dcon_source_switch(struct work_struct *work)
* For now, we just hope..
*/
if (!dcon_blank_fb(dcon, false)) {
- printk(KERN_ERR "olpc-dcon: Failed to enter CPU mode\n");
+ pr_err("Failed to enter CPU mode\n");
dcon->pending_src = DCON_SOURCE_DCON;
return;
}
@@ -341,14 +336,14 @@ static void dcon_source_switch(struct work_struct *work)
pdata->set_dconload(1);
getnstimeofday(&dcon->load_time);
- printk(KERN_INFO "olpc-dcon: The CPU has control\n");
+ pr_info("The CPU has control\n");
break;
case DCON_SOURCE_DCON:
{
int t;
struct timespec delta_t;
- printk(KERN_INFO "dcon_source_switch to DCON\n");
+ pr_info("dcon_source_switch to DCON\n");
add_wait_queue(&dcon_wait_queue, &wait);
set_current_state(TASK_UNINTERRUPTIBLE);
@@ -362,7 +357,7 @@ static void dcon_source_switch(struct work_struct *work)
set_current_state(TASK_RUNNING);
if (!dcon->switched) {
- printk(KERN_ERR "olpc-dcon: Timeout entering DCON mode; expect a screen glitch.\n");
+ pr_err("Timeout entering DCON mode; expect a screen glitch.\n");
} else {
/* sometimes the DCON doesn't follow its own rules,
* and doesn't wait for two vsync pulses before
@@ -378,7 +373,7 @@ static void dcon_source_switch(struct work_struct *work)
delta_t = timespec_sub(dcon->irq_time, dcon->load_time);
if (dcon->switched && delta_t.tv_sec == 0 &&
delta_t.tv_nsec < NSEC_PER_MSEC * 20) {
- printk(KERN_ERR "olpc-dcon: missed loading, retrying\n");
+ pr_err("missed loading, retrying\n");
pdata->set_dconload(1);
mdelay(41);
pdata->set_dconload(0);
@@ -388,7 +383,7 @@ static void dcon_source_switch(struct work_struct *work)
}
dcon_blank_fb(dcon, true);
- printk(KERN_INFO "olpc-dcon: The DCON has control\n");
+ pr_info("The DCON has control\n");
break;
}
default:
@@ -476,7 +471,7 @@ static ssize_t dcon_freeze_store(struct device *dev,
if (ret)
return ret;
- printk(KERN_INFO "dcon_freeze_store: %lu\n", output);
+ pr_info("dcon_freeze_store: %lu\n", output);
switch (output) {
case 0:
@@ -650,7 +645,7 @@ static int dcon_probe(struct i2c_client *client, const struct i2c_device_id *id)
dcon_device = platform_device_alloc("dcon", -1);
if (dcon_device == NULL) {
- printk(KERN_ERR "dcon: Unable to create the DCON device\n");
+ pr_err("Unable to create the DCON device\n");
rc = -ENOMEM;
goto eirq;
}
@@ -658,7 +653,7 @@ static int dcon_probe(struct i2c_client *client, const struct i2c_device_id *id)
platform_set_drvdata(dcon_device, dcon);
if (rc) {
- printk(KERN_ERR "dcon: Unable to add the DCON device\n");
+ pr_err("Unable to add the DCON device\n");
goto edev;
}
@@ -762,7 +757,7 @@ irqreturn_t dcon_interrupt(int irq, void *id)
switch (status & 3) {
case 3:
- printk(KERN_DEBUG "olpc-dcon: DCONLOAD_MISSED interrupt\n");
+ pr_debug("DCONLOAD_MISSED interrupt\n");
break;
case 2: /* switch to DCON mode */
@@ -784,9 +779,9 @@ irqreturn_t dcon_interrupt(int irq, void *id)
dcon->switched = true;
getnstimeofday(&dcon->irq_time);
wake_up(&dcon_wait_queue);
- printk(KERN_DEBUG "olpc-dcon: switching w/ status 0/0\n");
+ pr_debug("switching w/ status 0/0\n");
} else {
- printk(KERN_DEBUG "olpc-dcon: scanline interrupt w/CPU\n");
+ pr_debug("scanline interrupt w/CPU\n");
}
}
diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
index c87fdfac4855..77e8eb5a5abd 100644
--- a/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
+++ b/drivers/staging/olpc_dcon/olpc_dcon_xo_1.c
@@ -10,6 +10,9 @@
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*/
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/cs5535.h>
#include <linux/gpio.h>
#include <linux/delay.h>
@@ -22,23 +25,23 @@ static int dcon_init_xo_1(struct dcon_priv *dcon)
unsigned char lob;
if (gpio_request(OLPC_GPIO_DCON_STAT0, "OLPC-DCON")) {
- printk(KERN_ERR "olpc-dcon: failed to request STAT0 GPIO\n");
+ pr_err("failed to request STAT0 GPIO\n");
return -EIO;
}
if (gpio_request(OLPC_GPIO_DCON_STAT1, "OLPC-DCON")) {
- printk(KERN_ERR "olpc-dcon: failed to request STAT1 GPIO\n");
+ pr_err("failed to request STAT1 GPIO\n");
goto err_gp_stat1;
}
if (gpio_request(OLPC_GPIO_DCON_IRQ, "OLPC-DCON")) {
- printk(KERN_ERR "olpc-dcon: failed to request IRQ GPIO\n");
+ pr_err("failed to request IRQ GPIO\n");
goto err_gp_irq;
}
if (gpio_request(OLPC_GPIO_DCON_LOAD, "OLPC-DCON")) {
- printk(KERN_ERR "olpc-dcon: failed to request LOAD GPIO\n");
+ pr_err("failed to request LOAD GPIO\n");
goto err_gp_load;
}
if (gpio_request(OLPC_GPIO_DCON_BLANK, "OLPC-DCON")) {
- printk(KERN_ERR "olpc-dcon: failed to request BLANK GPIO\n");
+ pr_err("failed to request BLANK GPIO\n");
goto err_gp_blank;
}
@@ -83,7 +86,7 @@ static int dcon_init_xo_1(struct dcon_priv *dcon)
/* Register the interrupt handler */
if (request_irq(DCON_IRQ, &dcon_interrupt, 0, "DCON", dcon)) {
- printk(KERN_ERR "olpc-dcon: failed to request DCON's irq\n");
+ pr_err("failed to request DCON's irq\n");
goto err_req_irq;
}
diff --git a/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c b/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c
index 69415eec425c..352dd3db0132 100644
--- a/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c
+++ b/drivers/staging/olpc_dcon/olpc_dcon_xo_1_5.c
@@ -6,6 +6,8 @@
* License as published by the Free Software Foundation.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/pci.h>
@@ -66,7 +68,7 @@ static int dcon_init_xo_1_5(struct dcon_priv *dcon)
pdev = pci_get_device(PCI_VENDOR_ID_VIA,
PCI_DEVICE_ID_VIA_VX855, NULL);
if (!pdev) {
- printk(KERN_ERR "cannot find VX855 PCI ID\n");
+ pr_err("cannot find VX855 PCI ID\n");
return 1;
}
@@ -104,7 +106,7 @@ static int dcon_init_xo_1_5(struct dcon_priv *dcon)
/* we're sharing the IRQ with ACPI */
irq = acpi_gbl_FADT.sci_interrupt;
if (request_irq(irq, &dcon_interrupt, IRQF_SHARED, "DCON", dcon)) {
- printk(KERN_ERR PREFIX "DCON (IRQ%d) allocation failed\n", irq);
+ pr_err("DCON (IRQ%d) allocation failed\n", irq);
return 1;
}
diff --git a/drivers/staging/omap-thermal/omap-bandgap.c b/drivers/staging/omap-thermal/omap-bandgap.c
index c556abb63a17..368a2e19b2d4 100644
--- a/drivers/staging/omap-thermal/omap-bandgap.c
+++ b/drivers/staging/omap-thermal/omap-bandgap.c
@@ -157,7 +157,7 @@ static int temp_to_adc_conversion(long temp, struct omap_bandgap *bg_ptr, int i,
high = ts_data->adc_end_val - ts_data->adc_start_val;
mid = (high + low) / 2;
- if (temp < bg_ptr->conv_table[high] || temp > bg_ptr->conv_table[high])
+ if (temp < bg_ptr->conv_table[low] || temp > bg_ptr->conv_table[high])
return -EINVAL;
while (low < high) {
@@ -953,12 +953,12 @@ int __devinit omap_bandgap_probe(struct platform_device *pdev)
for (i = 0; i < bg_ptr->conf->sensor_count; i++) {
char *domain;
+ if (bg_ptr->conf->sensors[i].register_cooling)
+ bg_ptr->conf->sensors[i].register_cooling(bg_ptr, i);
+
domain = bg_ptr->conf->sensors[i].domain;
if (bg_ptr->conf->expose_sensor)
bg_ptr->conf->expose_sensor(bg_ptr, i, domain);
-
- if (bg_ptr->conf->sensors[i].register_cooling)
- bg_ptr->conf->sensors[i].register_cooling(bg_ptr, i);
}
/*
@@ -1037,20 +1037,20 @@ static int omap_bandgap_save_ctxt(struct omap_bandgap *bg_ptr)
if (OMAP_BANDGAP_HAS(bg_ptr, MODE_CONFIG))
rval->bg_mode_ctrl = omap_bandgap_readl(bg_ptr,
- tsr->bgap_mode_ctrl);
+ tsr->bgap_mode_ctrl);
if (OMAP_BANDGAP_HAS(bg_ptr, COUNTER))
rval->bg_counter = omap_bandgap_readl(bg_ptr,
- tsr->bgap_counter);
+ tsr->bgap_counter);
if (OMAP_BANDGAP_HAS(bg_ptr, TALERT)) {
rval->bg_threshold = omap_bandgap_readl(bg_ptr,
- tsr->bgap_threshold);
+ tsr->bgap_threshold);
rval->bg_ctrl = omap_bandgap_readl(bg_ptr,
- tsr->bgap_mask_ctrl);
+ tsr->bgap_mask_ctrl);
}
if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG))
rval->tshut_threshold = omap_bandgap_readl(bg_ptr,
- tsr->tshut_threshold);
+ tsr->tshut_threshold);
}
return 0;
@@ -1074,8 +1074,9 @@ static int omap_bandgap_restore_ctxt(struct omap_bandgap *bg_ptr)
if (val == 0) {
if (OMAP_BANDGAP_HAS(bg_ptr, TSHUT_CONFIG))
- omap_bandgap_writel(bg_ptr, rval->tshut_threshold,
- tsr->tshut_threshold);
+ omap_bandgap_writel(bg_ptr,
+ rval->tshut_threshold,
+ tsr->tshut_threshold);
/* Force immediate temperature measurement and update
* of the DTEMP field
*/
diff --git a/drivers/staging/omap-thermal/omap-thermal-common.c b/drivers/staging/omap-thermal/omap-thermal-common.c
index 0675a5e2f7c8..46ee0a9f49d9 100644
--- a/drivers/staging/omap-thermal/omap-thermal-common.c
+++ b/drivers/staging/omap-thermal/omap-thermal-common.c
@@ -77,10 +77,16 @@ static inline int omap_thermal_get_temp(struct thermal_zone_device *thermal,
unsigned long *temp)
{
struct omap_thermal_data *data = thermal->devdata;
- struct omap_bandgap *bg_ptr = data->bg_ptr;
- struct omap_temp_sensor *s = &bg_ptr->conf->sensors[data->sensor_id];
+ struct omap_bandgap *bg_ptr;
+ struct omap_temp_sensor *s;
int ret, tmp, pcb_temp, slope, constant;
+ if (!data)
+ return 0;
+
+ bg_ptr = data->bg_ptr;
+ s = &bg_ptr->conf->sensors[data->sensor_id];
+
ret = omap_bandgap_read_temperature(bg_ptr, data->sensor_id, &tmp);
if (ret)
return ret;
@@ -227,26 +233,44 @@ static struct thermal_zone_device_ops omap_thermal_ops = {
.get_crit_temp = omap_thermal_get_crit_temp,
};
-int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id,
- char *domain)
+static struct omap_thermal_data
+*omap_thermal_build_data(struct omap_bandgap *bg_ptr, int id)
{
struct omap_thermal_data *data;
data = devm_kzalloc(bg_ptr->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
dev_err(bg_ptr->dev, "kzalloc fail\n");
- return -ENOMEM;
+ return NULL;
}
data->sensor_id = id;
data->bg_ptr = bg_ptr;
data->mode = THERMAL_DEVICE_ENABLED;
INIT_WORK(&data->thermal_wq, omap_thermal_work);
+ return data;
+}
+
+int omap_thermal_expose_sensor(struct omap_bandgap *bg_ptr, int id,
+ char *domain)
+{
+ struct omap_thermal_pdata pdata;
+
+ data = omap_bandgap_get_sensor_data(bg_ptr, id);
+
+ if (!data)
+ data = omap_thermal_build_pdata(bg_ptr, id);
+
+ if (!data)
+ return -EINVAL;
+
/* TODO: remove TC1 TC2 */
/* Create thermal zone */
data->omap_thermal = thermal_zone_device_register(domain,
OMAP_TRIP_NUMBER, 0, data, &omap_thermal_ops,
- 0, FAST_TEMP_MONITORING_RATE, 0, 0);
+ 1, 2, /*TODO: remove this when FW allows */
+ FAST_TEMP_MONITORING_RATE,
+ FAST_TEMP_MONITORING_RATE);
if (IS_ERR_OR_NULL(data->omap_thermal)) {
dev_err(bg_ptr->dev, "thermal zone device is NULL\n");
return PTR_ERR(data->omap_thermal);
@@ -333,6 +357,11 @@ int omap_thermal_register_cpu_cooling(struct omap_bandgap *bg_ptr, int id)
int tab_size, ret;
data = omap_bandgap_get_sensor_data(bg_ptr, id);
+ if (!data)
+ data = omap_thermal_build_pdata(bg_ptr, id);
+
+ if (!data)
+ return -EINVAL;
ret = omap_thermal_build_cpufreq_clip(bg_ptr, &tab_ptr, &tab_size);
if (ret < 0) {
@@ -349,6 +378,7 @@ int omap_thermal_register_cpu_cooling(struct omap_bandgap *bg_ptr, int id)
return PTR_ERR(data->cool_dev);
}
bg_ptr->conf->sensors[id].cooling_data.freq_clip_count = tab_size;
+ omap_bandgap_set_sensor_data(bg_ptr, id, data);
return 0;
}
diff --git a/drivers/staging/omap-thermal/omap4-thermal.c b/drivers/staging/omap-thermal/omap4-thermal.c
index fa9dbcd71830..04c02b6c0077 100644
--- a/drivers/staging/omap-thermal/omap4-thermal.c
+++ b/drivers/staging/omap-thermal/omap4-thermal.c
@@ -77,15 +77,15 @@ const struct omap_bandgap_data omap4430_data = {
.remove_sensor = omap_thermal_remove_sensor,
.sensors = {
{
- .registers = &omap4430_mpu_temp_sensor_registers,
- .ts_data = &omap4430_mpu_temp_sensor_data,
- .domain = "cpu",
- .slope = 0,
- .constant = 20000,
- .slope_pcb = 0,
- .constant_pcb = 20000,
- .register_cooling = omap_thermal_register_cpu_cooling,
- .unregister_cooling = omap_thermal_unregister_cpu_cooling,
+ .registers = &omap4430_mpu_temp_sensor_registers,
+ .ts_data = &omap4430_mpu_temp_sensor_data,
+ .domain = "cpu",
+ .slope = 0,
+ .constant = 20000,
+ .slope_pcb = 0,
+ .constant_pcb = 20000,
+ .register_cooling = omap_thermal_register_cpu_cooling,
+ .unregister_cooling = omap_thermal_unregister_cpu_cooling,
},
},
.sensor_count = 1,
@@ -215,15 +215,15 @@ const struct omap_bandgap_data omap4460_data = {
.remove_sensor = omap_thermal_remove_sensor,
.sensors = {
{
- .registers = &omap4460_mpu_temp_sensor_registers,
- .ts_data = &omap4460_mpu_temp_sensor_data,
- .domain = "cpu",
- .slope = OMAP_GRADIENT_SLOPE_4460,
- .constant = OMAP_GRADIENT_CONST_4460,
- .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4460,
- .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4460,
- .register_cooling = omap_thermal_register_cpu_cooling,
- .unregister_cooling = omap_thermal_unregister_cpu_cooling,
+ .registers = &omap4460_mpu_temp_sensor_registers,
+ .ts_data = &omap4460_mpu_temp_sensor_data,
+ .domain = "cpu",
+ .slope = OMAP_GRADIENT_SLOPE_4460,
+ .constant = OMAP_GRADIENT_CONST_4460,
+ .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4460,
+ .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4460,
+ .register_cooling = omap_thermal_register_cpu_cooling,
+ .unregister_cooling = omap_thermal_unregister_cpu_cooling,
},
},
.sensor_count = 1,
@@ -244,15 +244,15 @@ const struct omap_bandgap_data omap4470_data = {
.remove_sensor = omap_thermal_remove_sensor,
.sensors = {
{
- .registers = &omap4460_mpu_temp_sensor_registers,
- .ts_data = &omap4460_mpu_temp_sensor_data,
- .domain = "cpu",
- .slope = OMAP_GRADIENT_SLOPE_4470,
- .constant = OMAP_GRADIENT_CONST_4470,
- .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4470,
- .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4470,
- .register_cooling = omap_thermal_register_cpu_cooling,
- .unregister_cooling = omap_thermal_unregister_cpu_cooling,
+ .registers = &omap4460_mpu_temp_sensor_registers,
+ .ts_data = &omap4460_mpu_temp_sensor_data,
+ .domain = "cpu",
+ .slope = OMAP_GRADIENT_SLOPE_4470,
+ .constant = OMAP_GRADIENT_CONST_4470,
+ .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_4470,
+ .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_4470,
+ .register_cooling = omap_thermal_register_cpu_cooling,
+ .unregister_cooling = omap_thermal_unregister_cpu_cooling,
},
},
.sensor_count = 1,
diff --git a/drivers/staging/omap-thermal/omap5-thermal.c b/drivers/staging/omap-thermal/omap5-thermal.c
index 0658af24a5c7..2f3a498dacd1 100644
--- a/drivers/staging/omap-thermal/omap5-thermal.c
+++ b/drivers/staging/omap-thermal/omap5-thermal.c
@@ -268,29 +268,29 @@ const struct omap_bandgap_data omap5430_data = {
.remove_sensor = omap_thermal_remove_sensor,
.sensors = {
{
- .registers = &omap5430_mpu_temp_sensor_registers,
- .ts_data = &omap5430_mpu_temp_sensor_data,
- .domain = "cpu",
- .register_cooling = omap_thermal_register_cpu_cooling,
- .unregister_cooling = omap_thermal_unregister_cpu_cooling,
- .slope = OMAP_GRADIENT_SLOPE_5430_CPU,
- .constant = OMAP_GRADIENT_CONST_5430_CPU,
- .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU,
- .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_CPU,
+ .registers = &omap5430_mpu_temp_sensor_registers,
+ .ts_data = &omap5430_mpu_temp_sensor_data,
+ .domain = "cpu",
+ .register_cooling = omap_thermal_register_cpu_cooling,
+ .unregister_cooling = omap_thermal_unregister_cpu_cooling,
+ .slope = OMAP_GRADIENT_SLOPE_5430_CPU,
+ .constant = OMAP_GRADIENT_CONST_5430_CPU,
+ .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_CPU,
+ .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_CPU,
},
{
- .registers = &omap5430_gpu_temp_sensor_registers,
- .ts_data = &omap5430_gpu_temp_sensor_data,
- .domain = "gpu",
- .slope = OMAP_GRADIENT_SLOPE_5430_GPU,
- .constant = OMAP_GRADIENT_CONST_5430_GPU,
- .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU,
- .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_GPU,
+ .registers = &omap5430_gpu_temp_sensor_registers,
+ .ts_data = &omap5430_gpu_temp_sensor_data,
+ .domain = "gpu",
+ .slope = OMAP_GRADIENT_SLOPE_5430_GPU,
+ .constant = OMAP_GRADIENT_CONST_5430_GPU,
+ .slope_pcb = OMAP_GRADIENT_SLOPE_W_PCB_5430_GPU,
+ .constant_pcb = OMAP_GRADIENT_CONST_W_PCB_5430_GPU,
},
{
- .registers = &omap5430_core_temp_sensor_registers,
- .ts_data = &omap5430_core_temp_sensor_data,
- .domain = "core",
+ .registers = &omap5430_core_temp_sensor_registers,
+ .ts_data = &omap5430_core_temp_sensor_data,
+ .domain = "core",
},
},
.sensor_count = 3,
diff --git a/drivers/staging/omapdrm/omap_crtc.c b/drivers/staging/omapdrm/omap_crtc.c
index 62e0022561bc..732f2ad34036 100644
--- a/drivers/staging/omapdrm/omap_crtc.c
+++ b/drivers/staging/omapdrm/omap_crtc.c
@@ -155,6 +155,7 @@ static void page_flip_cb(void *arg)
struct drm_crtc *crtc = arg;
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
struct drm_framebuffer *old_fb = omap_crtc->old_fb;
+ struct drm_gem_object *bo;
omap_crtc->old_fb = NULL;
@@ -165,6 +166,9 @@ static void page_flip_cb(void *arg)
* cycle.. for now go for correctness and later figure out speed..
*/
omap_plane_on_endwin(omap_crtc->plane, vblank_cb, crtc);
+
+ bo = omap_framebuffer_bo(crtc->fb, 0);
+ drm_gem_object_unreference_unlocked(bo);
}
static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
@@ -173,6 +177,7 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
{
struct drm_device *dev = crtc->dev;
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ struct drm_gem_object *bo;
DBG("%d -> %d", crtc->fb ? crtc->fb->base.id : -1, fb->base.id);
@@ -185,16 +190,38 @@ static int omap_crtc_page_flip_locked(struct drm_crtc *crtc,
omap_crtc->event = event;
crtc->fb = fb;
- omap_gem_op_async(omap_framebuffer_bo(fb, 0), OMAP_GEM_READ,
- page_flip_cb, crtc);
+ /*
+ * Hold a reference temporarily until the crtc is updated
+ * and takes the reference to the bo. This avoids it
+ * getting freed from under us:
+ */
+ bo = omap_framebuffer_bo(fb, 0);
+ drm_gem_object_reference(bo);
+
+ omap_gem_op_async(bo, OMAP_GEM_READ, page_flip_cb, crtc);
return 0;
}
+static int omap_crtc_set_property(struct drm_crtc *crtc,
+ struct drm_property *property, uint64_t val)
+{
+ struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
+ struct omap_drm_private *priv = crtc->dev->dev_private;
+
+ if (property == priv->rotation_prop) {
+ crtc->invert_dimensions =
+ !!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
+ }
+
+ return omap_plane_set_property(omap_crtc->plane, property, val);
+}
+
static const struct drm_crtc_funcs omap_crtc_funcs = {
.set_config = drm_crtc_helper_set_config,
.destroy = omap_crtc_destroy,
.page_flip = omap_crtc_page_flip_locked,
+ .set_property = omap_crtc_set_property,
};
static const struct drm_crtc_helper_funcs omap_crtc_helper_funcs = {
@@ -231,6 +258,8 @@ struct drm_crtc *omap_crtc_init(struct drm_device *dev,
drm_crtc_init(dev, crtc, &omap_crtc_funcs);
drm_crtc_helper_add(crtc, &omap_crtc_helper_funcs);
+ omap_plane_install_properties(omap_crtc->plane, &crtc->base);
+
return crtc;
fail:
diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.c b/drivers/staging/omapdrm/omap_dmm_tiler.c
index 86197831f63e..3ae39554df18 100644
--- a/drivers/staging/omapdrm/omap_dmm_tiler.c
+++ b/drivers/staging/omapdrm/omap_dmm_tiler.c
@@ -120,7 +120,7 @@ static int wait_status(struct refill_engine *engine, uint32_t wait_mask)
return 0;
}
-irqreturn_t omap_dmm_irq_handler(int irq, void *arg)
+static irqreturn_t omap_dmm_irq_handler(int irq, void *arg)
{
struct dmm *dmm = arg;
uint32_t status = readl(dmm->base + DMM_PAT_IRQSTATUS);
@@ -367,7 +367,7 @@ struct tiler_block *tiler_reserve_1d(size_t size)
int num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (!block)
- return 0;
+ return ERR_PTR(-ENOMEM);
block->fmt = TILFMT_PAGE;
@@ -404,8 +404,26 @@ int tiler_release(struct tiler_block *block)
* Utils
*/
-/* calculate the tiler space address of a pixel in a view orientation */
-static u32 tiler_get_address(u32 orient, enum tiler_fmt fmt, u32 x, u32 y)
+/* calculate the tiler space address of a pixel in a view orientation...
+ * below description copied from the display subsystem section of TRM:
+ *
+ * When the TILER is addressed, the bits:
+ * [28:27] = 0x0 for 8-bit tiled
+ * 0x1 for 16-bit tiled
+ * 0x2 for 32-bit tiled
+ * 0x3 for page mode
+ * [31:29] = 0x0 for 0-degree view
+ * 0x1 for 180-degree view + mirroring
+ * 0x2 for 0-degree view + mirroring
+ * 0x3 for 180-degree view
+ * 0x4 for 270-degree view + mirroring
+ * 0x5 for 270-degree view
+ * 0x6 for 90-degree view
+ * 0x7 for 90-degree view + mirroring
+ * Otherwise the bits indicated the corresponding bit address to access
+ * the SDRAM.
+ */
+static u32 tiler_get_address(enum tiler_fmt fmt, u32 orient, u32 x, u32 y)
{
u32 x_bits, y_bits, tmp, x_mask, y_mask, alignment;
@@ -417,8 +435,11 @@ static u32 tiler_get_address(u32 orient, enum tiler_fmt fmt, u32 x, u32 y)
x_mask = MASK(x_bits);
y_mask = MASK(y_bits);
- if (x < 0 || x > x_mask || y < 0 || y > y_mask)
+ if (x < 0 || x > x_mask || y < 0 || y > y_mask) {
+ DBG("invalid coords: %u < 0 || %u > %u || %u < 0 || %u > %u",
+ x, x, x_mask, y, y, y_mask);
return 0;
+ }
/* account for mirroring */
if (orient & MASK_X_INVERT)
@@ -439,11 +460,22 @@ dma_addr_t tiler_ssptr(struct tiler_block *block)
{
BUG_ON(!validfmt(block->fmt));
- return TILVIEW_8BIT + tiler_get_address(0, block->fmt,
+ return TILVIEW_8BIT + tiler_get_address(block->fmt, 0,
block->area.p0.x * geom[block->fmt].slot_w,
block->area.p0.y * geom[block->fmt].slot_h);
}
+dma_addr_t tiler_tsptr(struct tiler_block *block, uint32_t orient,
+ uint32_t x, uint32_t y)
+{
+ struct tcm_pt *p = &block->area.p0;
+ BUG_ON(!validfmt(block->fmt));
+
+ return tiler_get_address(block->fmt, orient,
+ (p->x * geom[block->fmt].slot_w) + x,
+ (p->y * geom[block->fmt].slot_h) + y);
+}
+
void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h)
{
BUG_ON(!validfmt(fmt));
@@ -451,11 +483,14 @@ void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h)
*h = round_up(*h, geom[fmt].slot_h);
}
-uint32_t tiler_stride(enum tiler_fmt fmt)
+uint32_t tiler_stride(enum tiler_fmt fmt, uint32_t orient)
{
BUG_ON(!validfmt(fmt));
- return 1 << (CONT_WIDTH_BITS + geom[fmt].y_shft);
+ if (orient & MASK_XY_FLIP)
+ return 1 << (CONT_HEIGHT_BITS + geom[fmt].x_shft);
+ else
+ return 1 << (CONT_WIDTH_BITS + geom[fmt].y_shft);
}
size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h)
diff --git a/drivers/staging/omapdrm/omap_dmm_tiler.h b/drivers/staging/omapdrm/omap_dmm_tiler.h
index 7b1052a329e4..740911df5fc3 100644
--- a/drivers/staging/omapdrm/omap_dmm_tiler.h
+++ b/drivers/staging/omapdrm/omap_dmm_tiler.h
@@ -54,7 +54,18 @@ struct tiler_block {
#define TILER_WIDTH (1 << (CONT_WIDTH_BITS - SLOT_WIDTH_BITS))
#define TILER_HEIGHT (1 << (CONT_HEIGHT_BITS - SLOT_HEIGHT_BITS))
-/* tiler space addressing bitfields */
+/*
+Table 15-11. Coding and Description of TILER Orientations
+S Y X Description Alternate description
+0 0 0 0-degree view Natural view
+0 0 1 0-degree view with vertical mirror 180-degree view with horizontal mirror
+0 1 0 0-degree view with horizontal mirror 180-degree view with vertical mirror
+0 1 1 180-degree view
+1 0 0 90-degree view with vertical mirror 270-degree view with horizontal mirror
+1 0 1 270-degree view
+1 1 0 90-degree view
+1 1 1 90-degree view with horizontal mirror 270-degree view with vertical mirror
+ */
#define MASK_XY_FLIP (1 << 31)
#define MASK_Y_INVERT (1 << 30)
#define MASK_X_INVERT (1 << 29)
@@ -90,7 +101,9 @@ int tiler_release(struct tiler_block *block);
/* utilities */
dma_addr_t tiler_ssptr(struct tiler_block *block);
-uint32_t tiler_stride(enum tiler_fmt fmt);
+dma_addr_t tiler_tsptr(struct tiler_block *block, uint32_t orient,
+ uint32_t x, uint32_t y);
+uint32_t tiler_stride(enum tiler_fmt fmt, uint32_t orient);
size_t tiler_size(enum tiler_fmt fmt, uint16_t w, uint16_t h);
size_t tiler_vsize(enum tiler_fmt fmt, uint16_t w, uint16_t h);
void tiler_align(enum tiler_fmt fmt, uint16_t *w, uint16_t *h);
diff --git a/drivers/staging/omapdrm/omap_drv.c b/drivers/staging/omapdrm/omap_drv.c
index 4beab9447ceb..2ec5264dd002 100644
--- a/drivers/staging/omapdrm/omap_drv.c
+++ b/drivers/staging/omapdrm/omap_drv.c
@@ -571,8 +571,7 @@ static int dev_load(struct drm_device *dev, unsigned long flags)
dev->dev_private = priv;
- priv->wq = alloc_workqueue("omapdrm",
- WQ_UNBOUND | WQ_NON_REENTRANT, 1);
+ priv->wq = alloc_ordered_workqueue("omapdrm", 0);
INIT_LIST_HEAD(&priv->obj_list);
@@ -649,6 +648,8 @@ static int dev_firstopen(struct drm_device *dev)
*/
static void dev_lastclose(struct drm_device *dev)
{
+ int i;
+
/* we don't support vga-switcheroo.. so just make sure the fbdev
* mode is active
*/
@@ -657,6 +658,21 @@ static void dev_lastclose(struct drm_device *dev)
DBG("lastclose: dev=%p", dev);
+ /* need to restore default rotation state.. not sure if there is
+ * a cleaner way to restore properties to default state? Maybe
+ * a flag that properties should automatically be restored to
+ * default state on lastclose?
+ */
+ for (i = 0; i < priv->num_crtcs; i++) {
+ drm_object_property_set_value(&priv->crtcs[i]->base,
+ priv->rotation_prop, 0);
+ }
+
+ for (i = 0; i < priv->num_planes; i++) {
+ drm_object_property_set_value(&priv->planes[i]->base,
+ priv->rotation_prop, 0);
+ }
+
ret = drm_fb_helper_restore_fbdev_mode(priv->fbdev);
if (ret)
DBG("failed to restore crtc mode");
@@ -761,7 +777,6 @@ static struct drm_driver omap_drm_driver = {
.irq_postinstall = dev_irq_postinstall,
.irq_uninstall = dev_irq_uninstall,
.irq_handler = dev_irq_handler,
- .reclaim_buffers = drm_core_reclaim_buffers,
#ifdef CONFIG_DEBUG_FS
.debugfs_init = omap_debugfs_init,
.debugfs_cleanup = omap_debugfs_cleanup,
diff --git a/drivers/staging/omapdrm/omap_drv.h b/drivers/staging/omapdrm/omap_drv.h
index 2092a9167d29..9dc72d143ff3 100644
--- a/drivers/staging/omapdrm/omap_drv.h
+++ b/drivers/staging/omapdrm/omap_drv.h
@@ -59,6 +59,27 @@ struct omap_drm_private {
struct list_head obj_list;
bool has_dmm;
+
+ /* properties: */
+ struct drm_property *rotation_prop;
+ struct drm_property *zorder_prop;
+};
+
+/* this should probably be in drm-core to standardize amongst drivers */
+#define DRM_ROTATE_0 0
+#define DRM_ROTATE_90 1
+#define DRM_ROTATE_180 2
+#define DRM_ROTATE_270 3
+#define DRM_REFLECT_X 4
+#define DRM_REFLECT_Y 5
+
+/* parameters which describe (unrotated) coordinates of scanout within a fb: */
+struct omap_drm_window {
+ uint32_t rotation;
+ int32_t crtc_x, crtc_y; /* signed because can be offscreen */
+ uint32_t crtc_w, crtc_h;
+ uint32_t src_x, src_y;
+ uint32_t src_w, src_h;
};
#ifdef CONFIG_DEBUG_FS
@@ -87,6 +108,10 @@ int omap_plane_mode_set(struct drm_plane *plane,
uint32_t src_w, uint32_t src_h);
void omap_plane_on_endwin(struct drm_plane *plane,
void (*fxn)(void *), void *arg);
+void omap_plane_install_properties(struct drm_plane *plane,
+ struct drm_mode_object *obj);
+int omap_plane_set_property(struct drm_plane *plane,
+ struct drm_property *property, uint64_t val);
struct drm_encoder *omap_encoder_init(struct drm_device *dev,
struct omap_overlay_manager *mgr);
@@ -114,8 +139,8 @@ struct drm_gem_object *omap_framebuffer_bo(struct drm_framebuffer *fb, int p);
int omap_framebuffer_replace(struct drm_framebuffer *a,
struct drm_framebuffer *b, void *arg,
void (*unpin)(void *arg, struct drm_gem_object *bo));
-void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y,
- struct omap_overlay_info *info);
+void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
+ struct omap_drm_window *win, struct omap_overlay_info *info);
struct drm_connector *omap_framebuffer_get_next_connector(
struct drm_framebuffer *fb, struct drm_connector *from);
void omap_framebuffer_flush(struct drm_framebuffer *fb,
@@ -157,8 +182,12 @@ int omap_gem_get_pages(struct drm_gem_object *obj, struct page ***pages,
bool remap);
int omap_gem_put_pages(struct drm_gem_object *obj);
uint32_t omap_gem_flags(struct drm_gem_object *obj);
+int omap_gem_rotated_paddr(struct drm_gem_object *obj, uint32_t orient,
+ int x, int y, dma_addr_t *paddr);
uint64_t omap_gem_mmap_offset(struct drm_gem_object *obj);
size_t omap_gem_mmap_size(struct drm_gem_object *obj);
+int omap_gem_tiled_size(struct drm_gem_object *obj, uint16_t *w, uint16_t *h);
+int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient);
struct dma_buf * omap_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *obj, int flags);
diff --git a/drivers/staging/omapdrm/omap_fb.c b/drivers/staging/omapdrm/omap_fb.c
index 74260f043ab1..446801d63007 100644
--- a/drivers/staging/omapdrm/omap_fb.c
+++ b/drivers/staging/omapdrm/omap_fb.c
@@ -18,6 +18,7 @@
*/
#include "omap_drv.h"
+#include "omap_dmm_tiler.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
@@ -137,30 +138,100 @@ static const struct drm_framebuffer_funcs omap_framebuffer_funcs = {
.dirty = omap_framebuffer_dirty,
};
+static uint32_t get_linear_addr(struct plane *plane,
+ const struct format *format, int n, int x, int y)
+{
+ uint32_t offset;
+
+ offset = plane->offset +
+ (x * format->planes[n].stride_bpp) +
+ (y * plane->pitch / format->planes[n].sub_y);
+
+ return plane->paddr + offset;
+}
+
/* update ovl info for scanout, handles cases of multi-planar fb's, etc.
*/
-void omap_framebuffer_update_scanout(struct drm_framebuffer *fb, int x, int y,
- struct omap_overlay_info *info)
+void omap_framebuffer_update_scanout(struct drm_framebuffer *fb,
+ struct omap_drm_window *win, struct omap_overlay_info *info)
{
struct omap_framebuffer *omap_fb = to_omap_framebuffer(fb);
const struct format *format = omap_fb->format;
struct plane *plane = &omap_fb->planes[0];
- unsigned int offset;
+ uint32_t x, y, orient = 0;
+
+ info->color_mode = format->dss_format;
+
+ info->pos_x = win->crtc_x;
+ info->pos_y = win->crtc_y;
+ info->out_width = win->crtc_w;
+ info->out_height = win->crtc_h;
+ info->width = win->src_w;
+ info->height = win->src_h;
+
+ x = win->src_x;
+ y = win->src_y;
+
+ if (omap_gem_flags(plane->bo) & OMAP_BO_TILED) {
+ uint32_t w = win->src_w;
+ uint32_t h = win->src_h;
+
+ switch (win->rotation & 0xf) {
+ default:
+ dev_err(fb->dev->dev, "invalid rotation: %02x",
+ (uint32_t)win->rotation);
+ /* fallthru to default to no rotation */
+ case 0:
+ case BIT(DRM_ROTATE_0):
+ orient = 0;
+ break;
+ case BIT(DRM_ROTATE_90):
+ orient = MASK_XY_FLIP | MASK_X_INVERT;
+ break;
+ case BIT(DRM_ROTATE_180):
+ orient = MASK_X_INVERT | MASK_Y_INVERT;
+ break;
+ case BIT(DRM_ROTATE_270):
+ orient = MASK_XY_FLIP | MASK_Y_INVERT;
+ break;
+ }
- offset = plane->offset +
- (x * format->planes[0].stride_bpp) +
- (y * plane->pitch / format->planes[0].sub_y);
+ if (win->rotation & BIT(DRM_REFLECT_X))
+ orient ^= MASK_X_INVERT;
+
+ if (win->rotation & BIT(DRM_REFLECT_Y))
+ orient ^= MASK_Y_INVERT;
+
+ /* adjust x,y offset for flip/invert: */
+ if (orient & MASK_XY_FLIP)
+ swap(w, h);
+ if (orient & MASK_Y_INVERT)
+ y += h - 1;
+ if (orient & MASK_X_INVERT)
+ x += w - 1;
- info->color_mode = format->dss_format;
- info->paddr = plane->paddr + offset;
- info->screen_width = plane->pitch / format->planes[0].stride_bpp;
+ omap_gem_rotated_paddr(plane->bo, orient, x, y, &info->paddr);
+ info->rotation_type = OMAP_DSS_ROT_TILER;
+ info->screen_width = omap_gem_tiled_stride(plane->bo, orient);
+ } else {
+ info->paddr = get_linear_addr(plane, format, 0, x, y);
+ info->rotation_type = OMAP_DSS_ROT_DMA;
+ info->screen_width = plane->pitch;
+ }
+
+ /* convert to pixels: */
+ info->screen_width /= format->planes[0].stride_bpp;
if (format->dss_format == OMAP_DSS_COLOR_NV12) {
plane = &omap_fb->planes[1];
- offset = plane->offset +
- (x * format->planes[1].stride_bpp) +
- (y * plane->pitch / format->planes[1].sub_y);
- info->p_uv_addr = plane->paddr + offset;
+
+ if (info->rotation_type == OMAP_DSS_ROT_TILER) {
+ WARN_ON(!(omap_gem_flags(plane->bo) & OMAP_BO_TILED));
+ omap_gem_rotated_paddr(plane->bo, orient,
+ x/2, y/2, &info->p_uv_addr);
+ } else {
+ info->p_uv_addr = get_linear_addr(plane, format, 1, x, y);
+ }
} else {
info->p_uv_addr = 0;
}
@@ -377,7 +448,7 @@ struct drm_framebuffer *omap_framebuffer_init(struct drm_device *dev,
size = pitch * mode_cmd->height / format->planes[i].sub_y;
- if (size > (bos[i]->size - mode_cmd->offsets[i])) {
+ if (size > (omap_gem_mmap_size(bos[i]) - mode_cmd->offsets[i])) {
dev_err(dev->dev, "provided buffer object is too small! %d < %d\n",
bos[i]->size - mode_cmd->offsets[i], size);
ret = -EINVAL;
diff --git a/drivers/staging/omapdrm/omap_gem.c b/drivers/staging/omapdrm/omap_gem.c
index 3a0d035a9e03..c8287438e0dc 100644
--- a/drivers/staging/omapdrm/omap_gem.c
+++ b/drivers/staging/omapdrm/omap_gem.c
@@ -226,7 +226,8 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj)
struct drm_device *dev = obj->dev;
struct omap_gem_object *omap_obj = to_omap_bo(obj);
struct page **pages;
- int i, npages = obj->size >> PAGE_SHIFT;
+ int npages = obj->size >> PAGE_SHIFT;
+ int i, ret;
dma_addr_t *addrs;
WARN_ON(omap_obj->pages);
@@ -246,18 +247,32 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj)
*/
if (omap_obj->flags & (OMAP_BO_WC|OMAP_BO_UNCACHED)) {
addrs = kmalloc(npages * sizeof(addrs), GFP_KERNEL);
+ if (!addrs) {
+ ret = -ENOMEM;
+ goto free_pages;
+ }
+
for (i = 0; i < npages; i++) {
addrs[i] = dma_map_page(dev->dev, pages[i],
0, PAGE_SIZE, DMA_BIDIRECTIONAL);
}
} else {
addrs = kzalloc(npages * sizeof(addrs), GFP_KERNEL);
+ if (!addrs) {
+ ret = -ENOMEM;
+ goto free_pages;
+ }
}
omap_obj->addrs = addrs;
omap_obj->pages = pages;
return 0;
+
+free_pages:
+ _drm_gem_put_pages(obj, pages, true, false);
+
+ return ret;
}
/** release backing pages */
@@ -339,6 +354,17 @@ size_t omap_gem_mmap_size(struct drm_gem_object *obj)
return size;
}
+/* get tiled size, returns -EINVAL if not tiled buffer */
+int omap_gem_tiled_size(struct drm_gem_object *obj, uint16_t *w, uint16_t *h)
+{
+ struct omap_gem_object *omap_obj = to_omap_bo(obj);
+ if (omap_obj->flags & OMAP_BO_TILED) {
+ *w = omap_obj->width;
+ *h = omap_obj->height;
+ return 0;
+ }
+ return -EINVAL;
+}
/* Normal handling for the case of faulting in non-tiled buffers */
static int fault_1d(struct drm_gem_object *obj,
@@ -832,6 +858,36 @@ fail:
return ret;
}
+/* Get rotated scanout address (only valid if already pinned), at the
+ * specified orientation and x,y offset from top-left corner of buffer
+ * (only valid for tiled 2d buffers)
+ */
+int omap_gem_rotated_paddr(struct drm_gem_object *obj, uint32_t orient,
+ int x, int y, dma_addr_t *paddr)
+{
+ struct omap_gem_object *omap_obj = to_omap_bo(obj);
+ int ret = -EINVAL;
+
+ mutex_lock(&obj->dev->struct_mutex);
+ if ((omap_obj->paddr_cnt > 0) && omap_obj->block &&
+ (omap_obj->flags & OMAP_BO_TILED)) {
+ *paddr = tiler_tsptr(omap_obj->block, orient, x, y);
+ ret = 0;
+ }
+ mutex_unlock(&obj->dev->struct_mutex);
+ return ret;
+}
+
+/* Get tiler stride for the buffer (only valid for 2d tiled buffers) */
+int omap_gem_tiled_stride(struct drm_gem_object *obj, uint32_t orient)
+{
+ struct omap_gem_object *omap_obj = to_omap_bo(obj);
+ int ret = -EINVAL;
+ if (omap_obj->flags & OMAP_BO_TILED)
+ ret = tiler_stride(gem2fmt(omap_obj->flags), orient);
+ return ret;
+}
+
/* acquire pages when needed (for example, for DMA where physically
* contiguous buffer is not required
*/
@@ -1402,7 +1458,7 @@ void omap_gem_init(struct drm_device *dev)
*/
usergart[i].height = h;
usergart[i].height_shift = ilog2(h);
- usergart[i].stride_pfn = tiler_stride(fmts[i]) >> PAGE_SHIFT;
+ usergart[i].stride_pfn = tiler_stride(fmts[i], 0) >> PAGE_SHIFT;
usergart[i].slot_shift = ilog2((PAGE_SIZE / h) >> i);
for (j = 0; j < NUM_USERGART_ENTRIES; j++) {
struct usergart_entry *entry = &usergart[i].entry[j];
diff --git a/drivers/staging/omapdrm/omap_plane.c b/drivers/staging/omapdrm/omap_plane.c
index 7997be74010d..4bde639dd02c 100644
--- a/drivers/staging/omapdrm/omap_plane.c
+++ b/drivers/staging/omapdrm/omap_plane.c
@@ -20,6 +20,7 @@
#include <linux/kfifo.h>
#include "omap_drv.h"
+#include "omap_dmm_tiler.h"
/* some hackery because omapdss has an 'enum omap_plane' (which would be
* better named omap_plane_id).. and compiler seems unhappy about having
@@ -43,10 +44,9 @@ struct omap_plane {
struct omap_overlay *ovl;
struct omap_overlay_info info;
- /* Source values, converted to integers because we don't support
- * fractional positions:
- */
- unsigned int src_x, src_y;
+ /* position/orientation of scanout within the fb: */
+ struct omap_drm_window win;
+
/* last fb that we pinned: */
struct drm_framebuffer *pinned_fb;
@@ -289,6 +289,7 @@ static void update_scanout(struct drm_plane *plane)
{
struct omap_plane *omap_plane = to_omap_plane(plane);
struct omap_overlay_info *info = &omap_plane->info;
+ struct omap_drm_window *win = &omap_plane->win;
int ret;
ret = update_pin(plane, plane->fb);
@@ -299,11 +300,10 @@ static void update_scanout(struct drm_plane *plane)
return;
}
- omap_framebuffer_update_scanout(plane->fb,
- omap_plane->src_x, omap_plane->src_y, info);
+ omap_framebuffer_update_scanout(plane->fb, win, info);
DBG("%s: %d,%d: %08x %08x (%d)", omap_plane->ovl->name,
- omap_plane->src_x, omap_plane->src_y,
+ win->src_x, win->src_y,
(u32)info->paddr, (u32)info->p_uv_addr,
info->screen_width);
}
@@ -316,21 +316,18 @@ int omap_plane_mode_set(struct drm_plane *plane,
uint32_t src_w, uint32_t src_h)
{
struct omap_plane *omap_plane = to_omap_plane(plane);
+ struct omap_drm_window *win = &omap_plane->win;
+
+ win->crtc_x = crtc_x;
+ win->crtc_y = crtc_y;
+ win->crtc_w = crtc_w;
+ win->crtc_h = crtc_h;
/* src values are in Q16 fixed point, convert to integer: */
- src_x = src_x >> 16;
- src_y = src_y >> 16;
- src_w = src_w >> 16;
- src_h = src_h >> 16;
-
- omap_plane->info.pos_x = crtc_x;
- omap_plane->info.pos_y = crtc_y;
- omap_plane->info.out_width = crtc_w;
- omap_plane->info.out_height = crtc_h;
- omap_plane->info.width = src_w;
- omap_plane->info.height = src_h;
- omap_plane->src_x = src_x;
- omap_plane->src_y = src_y;
+ win->src_x = src_x >> 16;
+ win->src_y = src_y >> 16;
+ win->src_w = src_w >> 16;
+ win->src_h = src_h >> 16;
/* note: this is done after this fxn returns.. but if we need
* to do a commit/update_scanout, etc before this returns we
@@ -359,6 +356,8 @@ static int omap_plane_update(struct drm_plane *plane,
static int omap_plane_disable(struct drm_plane *plane)
{
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+ omap_plane->win.rotation = BIT(DRM_ROTATE_0);
return omap_plane_dpms(plane, DRM_MODE_DPMS_OFF);
}
@@ -409,10 +408,79 @@ void omap_plane_on_endwin(struct drm_plane *plane,
install_irq(plane);
}
+/* helper to install properties which are common to planes and crtcs */
+void omap_plane_install_properties(struct drm_plane *plane,
+ struct drm_mode_object *obj)
+{
+ struct drm_device *dev = plane->dev;
+ struct omap_drm_private *priv = dev->dev_private;
+ struct drm_property *prop;
+
+ prop = priv->rotation_prop;
+ if (!prop) {
+ const struct drm_prop_enum_list props[] = {
+ { DRM_ROTATE_0, "rotate-0" },
+ { DRM_ROTATE_90, "rotate-90" },
+ { DRM_ROTATE_180, "rotate-180" },
+ { DRM_ROTATE_270, "rotate-270" },
+ { DRM_REFLECT_X, "reflect-x" },
+ { DRM_REFLECT_Y, "reflect-y" },
+ };
+ prop = drm_property_create_bitmask(dev, 0, "rotation",
+ props, ARRAY_SIZE(props));
+ if (prop == NULL)
+ return;
+ priv->rotation_prop = prop;
+ }
+ drm_object_attach_property(obj, prop, 0);
+
+ prop = priv->zorder_prop;
+ if (!prop) {
+ prop = drm_property_create_range(dev, 0, "zorder", 0, 3);
+ if (prop == NULL)
+ return;
+ priv->zorder_prop = prop;
+ }
+ drm_object_attach_property(obj, prop, 0);
+}
+
+int omap_plane_set_property(struct drm_plane *plane,
+ struct drm_property *property, uint64_t val)
+{
+ struct omap_plane *omap_plane = to_omap_plane(plane);
+ struct omap_drm_private *priv = plane->dev->dev_private;
+ int ret = -EINVAL;
+
+ if (property == priv->rotation_prop) {
+ struct omap_overlay *ovl = omap_plane->ovl;
+
+ DBG("%s: rotation: %02x", ovl->name, (uint32_t)val);
+ omap_plane->win.rotation = val;
+
+ if (ovl->is_enabled(ovl))
+ ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
+ else
+ ret = 0;
+ } else if (property == priv->zorder_prop) {
+ struct omap_overlay *ovl = omap_plane->ovl;
+
+ DBG("%s: zorder: %d", ovl->name, (uint32_t)val);
+ omap_plane->info.zorder = val;
+
+ if (ovl->is_enabled(ovl))
+ ret = omap_plane_dpms(plane, DRM_MODE_DPMS_ON);
+ else
+ ret = 0;
+ }
+
+ return ret;
+}
+
static const struct drm_plane_funcs omap_plane_funcs = {
.update_plane = omap_plane_update,
.disable_plane = omap_plane_disable,
.destroy = omap_plane_destroy,
+ .set_property = omap_plane_set_property,
};
/* initialize plane */
@@ -455,6 +523,8 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
drm_plane_init(dev, plane, possible_crtcs, &omap_plane_funcs,
omap_plane->formats, omap_plane->nformats, priv);
+ omap_plane_install_properties(plane, &plane->base);
+
/* get our starting configuration, set defaults for parameters
* we don't currently use, etc:
*/
@@ -463,7 +533,6 @@ struct drm_plane *omap_plane_init(struct drm_device *dev,
omap_plane->info.rotation = OMAP_DSS_ROT_0;
omap_plane->info.global_alpha = 0xff;
omap_plane->info.mirror = 0;
- omap_plane->info.mirror = 0;
/* Set defaults depending on whether we are a CRTC or overlay
* layer.
diff --git a/drivers/staging/ozwpan/ozcdev.c b/drivers/staging/ozwpan/ozcdev.c
index 758ce0a8d82e..64913aeb0bac 100644
--- a/drivers/staging/ozwpan/ozcdev.c
+++ b/drivers/staging/ozwpan/ozcdev.c
@@ -104,10 +104,10 @@ ssize_t oz_cdev_read(struct file *filp, char __user *buf, size_t count,
if (pd)
oz_pd_get(pd);
spin_unlock_bh(&g_cdev.lock);
- if (pd == 0)
+ if (pd == NULL)
return -1;
ctx = oz_cdev_claim_ctx(pd);
- if (ctx == 0)
+ if (ctx == NULL)
goto out2;
n = ctx->rd_in - ctx->rd_out;
if (n < 0)
@@ -157,11 +157,11 @@ ssize_t oz_cdev_write(struct file *filp, const char __user *buf, size_t count,
if (pd)
oz_pd_get(pd);
spin_unlock_bh(&g_cdev.lock);
- if (pd == 0)
+ if (pd == NULL)
return -1;
eb = &pd->elt_buff;
ei = oz_elt_info_alloc(eb);
- if (ei == 0) {
+ if (ei == NULL) {
count = 0;
goto out;
}
@@ -410,7 +410,7 @@ int oz_cdev_start(struct oz_pd *pd, int resume)
return 0;
}
ctx = kzalloc(sizeof(struct oz_serial_ctx), GFP_ATOMIC);
- if (ctx == 0)
+ if (ctx == NULL)
return -ENOMEM;
atomic_set(&ctx->ref_count, 1);
ctx->tx_seq_num = 1;
@@ -424,7 +424,7 @@ int oz_cdev_start(struct oz_pd *pd, int resume)
spin_unlock_bh(&pd->app_lock[OZ_APPID_SERIAL-1]);
}
spin_lock(&g_cdev.lock);
- if ((g_cdev.active_pd == 0) &&
+ if ((g_cdev.active_pd == NULL) &&
(memcmp(pd->mac_addr, g_cdev.active_addr, ETH_ALEN) == 0)) {
oz_pd_get(pd);
g_cdev.active_pd = pd;
@@ -477,7 +477,7 @@ void oz_cdev_rx(struct oz_pd *pd, struct oz_elt *elt)
int ix;
ctx = oz_cdev_claim_ctx(pd);
- if (ctx == 0) {
+ if (ctx == NULL) {
oz_trace("Cannot claim serial context.\n");
return;
}
diff --git a/drivers/staging/ozwpan/ozevent.c b/drivers/staging/ozwpan/ozevent.c
index 7f66b4f19b01..a48498bd9b5f 100644
--- a/drivers/staging/ozwpan/ozevent.c
+++ b/drivers/staging/ozwpan/ozevent.c
@@ -14,7 +14,7 @@
#include "ozappif.h"
/*------------------------------------------------------------------------------
* Although the event mask is logically part of the oz_evtdev structure, it is
- * needed outside of this file so define it seperately to avoid the need to
+ * needed outside of this file so define it separately to avoid the need to
* export definition of struct oz_evtdev.
*/
u32 g_evt_mask;
@@ -39,8 +39,8 @@ static struct oz_evtdev g_evtdev;
*/
void oz_event_init(void)
{
- /* Because g_evtdev is static external all fields initally zero so no
- * need to reinitialised those.
+ /* Because g_evtdev is static external all fields initially zero so no
+ * need to reinitialized those.
*/
oz_trace("Event tracing initialized\n");
spin_lock_init(&g_evtdev.lock);
diff --git a/drivers/staging/ozwpan/ozhcd.c b/drivers/staging/ozwpan/ozhcd.c
index 251f07c39a6b..2e087acf1578 100644
--- a/drivers/staging/ozwpan/ozhcd.c
+++ b/drivers/staging/ozwpan/ozhcd.c
@@ -417,6 +417,44 @@ static void oz_ep_free(struct oz_port *port, struct oz_endpoint *ep)
/*------------------------------------------------------------------------------
* Context: softirq
*/
+void oz_complete_buffered_urb(struct oz_port *port, struct oz_endpoint *ep,
+ struct urb *urb)
+{
+ u8 data_len, available_space, copy_len;
+
+ memcpy(&data_len, &ep->buffer[ep->out_ix], sizeof(u8));
+ if (data_len <= urb->transfer_buffer_length)
+ available_space = data_len;
+ else
+ available_space = urb->transfer_buffer_length;
+
+ if (++ep->out_ix == ep->buffer_size)
+ ep->out_ix = 0;
+ copy_len = ep->buffer_size - ep->out_ix;
+ if (copy_len >= available_space)
+ copy_len = available_space;
+ memcpy(urb->transfer_buffer, &ep->buffer[ep->out_ix], copy_len);
+
+ if (copy_len < available_space) {
+ memcpy((urb->transfer_buffer + copy_len), ep->buffer,
+ (available_space - copy_len));
+ ep->out_ix = available_space - copy_len;
+ } else {
+ ep->out_ix += copy_len;
+ }
+ urb->actual_length = available_space;
+ if (ep->out_ix == ep->buffer_size)
+ ep->out_ix = 0;
+
+ ep->buffered_units--;
+ oz_trace("Trying to give back buffered frame of size=%d\n",
+ available_space);
+ oz_complete_urb(port->ozhcd->hcd, urb, 0, 0);
+}
+
+/*------------------------------------------------------------------------------
+ * Context: softirq
+ */
static int oz_enqueue_ep_urb(struct oz_port *port, u8 ep_addr, int in_dir,
struct urb *urb, u8 req_id)
{
@@ -452,6 +490,18 @@ static int oz_enqueue_ep_urb(struct oz_port *port, u8 ep_addr, int in_dir,
ep = port->in_ep[ep_addr];
else
ep = port->out_ep[ep_addr];
+
+ /*For interrupt endpoint check for buffered data
+ * & complete urb
+ */
+ if (((ep->attrib & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)
+ && ep->buffered_units > 0) {
+ oz_free_urb_link(urbl);
+ spin_unlock_bh(&port->ozhcd->hcd_lock);
+ oz_complete_buffered_urb(port, ep, urb);
+ return 0;
+ }
+
if (ep && port->hpd) {
list_add_tail(&urbl->link, &ep->urb_list);
if (!in_dir && ep_addr && (ep->credit < 0)) {
@@ -883,13 +933,14 @@ void oz_hcd_control_cnf(void *hport, u8 req_id, u8 rcode, u8 *data,
} else {
int copy_len;
oz_trace("VENDOR-CLASS - cnf\n");
- if (data_len <= urb->transfer_buffer_length)
- copy_len = data_len;
- else
- copy_len = urb->transfer_buffer_length;
- if (copy_len)
+ if (data_len) {
+ if (data_len <= urb->transfer_buffer_length)
+ copy_len = data_len;
+ else
+ copy_len = urb->transfer_buffer_length;
memcpy(urb->transfer_buffer, data, copy_len);
- urb->actual_length = copy_len;
+ urb->actual_length = copy_len;
+ }
oz_complete_urb(hcd, urb, 0, 0);
}
}
@@ -961,6 +1012,9 @@ void oz_hcd_data_ind(void *hport, u8 endpoint, u8 *data, int data_len)
urb->actual_length = copy_len;
oz_complete_urb(port->ozhcd->hcd, urb, 0, 0);
return;
+ } else {
+ oz_trace("buffering frame as URB is not available\n");
+ oz_hcd_buffer_data(ep, data, data_len);
}
break;
case USB_ENDPOINT_XFER_ISOC:
@@ -1000,7 +1054,7 @@ int oz_hcd_heartbeat(void *hport)
ep = ep_from_link(e);
if (ep->credit < 0)
continue;
- ep->credit += (now - ep->last_jiffies);
+ ep->credit += jiffies_to_msecs(now - ep->last_jiffies);
if (ep->credit > ep->credit_ceiling)
ep->credit = ep->credit_ceiling;
oz_event_log(OZ_EVT_EP_CREDIT, ep->ep_num, 0, 0, ep->credit);
@@ -1009,13 +1063,12 @@ int oz_hcd_heartbeat(void *hport)
urbl = list_first_entry(&ep->urb_list,
struct oz_urb_link, link);
urb = urbl->urb;
- if (ep->credit < urb->number_of_packets)
+ if ((ep->credit + 1) < urb->number_of_packets)
break;
ep->credit -= urb->number_of_packets;
oz_event_log(OZ_EVT_EP_CREDIT, ep->ep_num, 0, 0,
ep->credit);
- list_del(&urbl->link);
- list_add_tail(&urbl->link, &xfr_list);
+ list_move_tail(&urbl->link, &xfr_list);
}
}
spin_unlock_bh(&ozhcd->hcd_lock);
@@ -1052,7 +1105,7 @@ int oz_hcd_heartbeat(void *hport)
}
continue;
}
- ep->credit += (now - ep->last_jiffies);
+ ep->credit += jiffies_to_msecs(now - ep->last_jiffies);
oz_event_log(OZ_EVT_EP_CREDIT, ep->ep_num | USB_DIR_IN,
0, 0, ep->credit);
ep->last_jiffies = now;
@@ -1064,7 +1117,7 @@ int oz_hcd_heartbeat(void *hport)
int len = 0;
int copy_len;
int i;
- if (ep->credit < urb->number_of_packets)
+ if ((ep->credit + 1) < urb->number_of_packets)
break;
if (ep->buffered_units < urb->number_of_packets)
break;
@@ -1096,8 +1149,7 @@ int oz_hcd_heartbeat(void *hport)
urb->error_count = 0;
urb->start_frame = ep->start_frame;
ep->start_frame += urb->number_of_packets;
- list_del(&urbl->link);
- list_add_tail(&urbl->link, &xfr_list);
+ list_move_tail(&urbl->link, &xfr_list);
ep->credit -= urb->number_of_packets;
oz_event_log(OZ_EVT_EP_CREDIT, ep->ep_num | USB_DIR_IN,
0, 0, ep->credit);
@@ -1129,8 +1181,7 @@ int oz_hcd_heartbeat(void *hport)
oz_trace("%ld: Request 0x%p timeout\n",
now, urbl->urb);
urbl->submit_jiffies = now;
- list_del(e);
- list_add_tail(e, &xfr_list);
+ list_move_tail(e, &xfr_list);
}
}
if (!list_empty(&ep->urb_list))
@@ -1167,10 +1218,16 @@ static int oz_build_endpoints_for_interface(struct usb_hcd *hcd,
int buffer_size = 0;
oz_trace("%d bEndpointAddress = %x\n", i, ep_addr);
- if ((ep_addr & USB_ENDPOINT_DIR_MASK) &&
- ((hep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_ISOC)) {
- buffer_size = 24*1024;
+ if (ep_addr & USB_ENDPOINT_DIR_MASK) {
+ switch (hep->desc.bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_ISOC:
+ buffer_size = 24*1024;
+ break;
+ case USB_ENDPOINT_XFER_INT:
+ buffer_size = 128;
+ break;
+ }
}
ep = oz_ep_alloc(mem_flags, buffer_size);
@@ -1247,16 +1304,14 @@ static void oz_clean_endpoints_for_interface(struct usb_hcd *hcd,
port->out_ep[i] = 0;
/* Remove from isoc list if present.
*/
- list_del(e);
- list_add_tail(e, &ep_list);
+ list_move_tail(e, &ep_list);
}
/* Gather IN endpoints.
*/
if ((mask & (1<<(i+OZ_NB_ENDPOINTS))) && port->in_ep[i]) {
e = &port->in_ep[i]->link;
port->in_ep[i] = 0;
- list_del(e);
- list_add_tail(e, &ep_list);
+ list_move_tail(e, &ep_list);
}
}
spin_unlock_bh(&ozhcd->hcd_lock);
@@ -1458,6 +1513,7 @@ static void oz_process_ep0_urb(struct oz_hcd *ozhcd, struct urb *urb,
int data_len = 0;
if ((setup->bRequestType & USB_DIR_IN) == 0)
data_len = wlength;
+ urb->actual_length = data_len;
if (oz_usb_control_req(port->hpd, req_id, setup,
urb->transfer_buffer, data_len)) {
rc = -ENOMEM;
diff --git a/drivers/staging/ozwpan/ozmain.c b/drivers/staging/ozwpan/ozmain.c
index c1ed6b2522ec..ef6c5ab753ee 100644
--- a/drivers/staging/ozwpan/ozmain.c
+++ b/drivers/staging/ozwpan/ozmain.c
@@ -59,6 +59,6 @@ module_exit(ozwpan_exit);
MODULE_AUTHOR("Chris Kelly");
MODULE_DESCRIPTION("Ozmo Devices USB over WiFi hcd driver");
-MODULE_VERSION("1.0.10");
+MODULE_VERSION("1.0.13");
MODULE_LICENSE("GPL");
diff --git a/drivers/staging/ozwpan/ozpd.c b/drivers/staging/ozwpan/ozpd.c
index 6c287ac6eaea..0b3648ce9687 100644
--- a/drivers/staging/ozwpan/ozpd.c
+++ b/drivers/staging/ozwpan/ozpd.c
@@ -24,12 +24,6 @@
/*------------------------------------------------------------------------------
*/
#define OZ_MAX_TX_POOL_SIZE 6
-/* Maximum number of uncompleted isoc frames that can be pending in network.
- */
-#define OZ_MAX_SUBMITTED_ISOC 16
-/* Maximum number of uncompleted isoc frames that can be pending in Tx Queue.
- */
-#define OZ_MAX_TX_QUEUE_ISOC 32
/*------------------------------------------------------------------------------
*/
static struct oz_tx_frame *oz_tx_frame_alloc(struct oz_pd *pd);
@@ -752,8 +746,7 @@ int oz_isoc_stream_create(struct oz_pd *pd, u8 ep_num)
*/
static void oz_isoc_stream_free(struct oz_isoc_stream *st)
{
- if (st->skb)
- kfree_skb(st->skb);
+ kfree_skb(st->skb);
kfree(st);
}
/*------------------------------------------------------------------------------
@@ -854,7 +847,7 @@ int oz_send_isoc_unit(struct oz_pd *pd, u8 ep_num, u8 *data, int len)
if (!(pd->mode & OZ_F_ISOC_ANYTIME)) {
struct oz_tx_frame *isoc_unit = NULL;
int nb = pd->nb_queued_isoc_frames;
- if (nb >= OZ_MAX_TX_QUEUE_ISOC) {
+ if (nb >= pd->isoc_latency) {
oz_trace2(OZ_TRACE_TX_FRAMES,
"Dropping ISOC Unit nb= %d\n",
nb);
diff --git a/drivers/staging/ozwpan/ozpd.h b/drivers/staging/ozwpan/ozpd.h
index ddf1341b4e67..d35b0ea44f67 100644
--- a/drivers/staging/ozwpan/ozpd.h
+++ b/drivers/staging/ozwpan/ozpd.h
@@ -82,6 +82,7 @@ struct oz_pd {
u8 heartbeat_requested;
u8 mode;
u8 ms_per_isoc;
+ unsigned isoc_latency;
unsigned max_stream_buffering;
int nb_queued_frames;
int nb_queued_isoc_frames;
diff --git a/drivers/staging/ozwpan/ozproto.c b/drivers/staging/ozwpan/ozproto.c
index a50ab18a5987..cfb5160d1ebe 100644
--- a/drivers/staging/ozwpan/ozproto.c
+++ b/drivers/staging/ozwpan/ozproto.c
@@ -220,6 +220,19 @@ static struct oz_pd *oz_connect_req(struct oz_pd *cur_pd, struct oz_elt *elt,
pd->ms_per_isoc = body->ms_per_isoc;
if (!pd->ms_per_isoc)
pd->ms_per_isoc = 4;
+
+ switch (body->ms_isoc_latency & OZ_LATENCY_MASK) {
+ case OZ_ONE_MS_LATENCY:
+ pd->isoc_latency = (body->ms_isoc_latency &
+ ~OZ_LATENCY_MASK) / pd->ms_per_isoc;
+ break;
+ case OZ_TEN_MS_LATENCY:
+ pd->isoc_latency = ((body->ms_isoc_latency &
+ ~OZ_LATENCY_MASK) * 10) / pd->ms_per_isoc;
+ break;
+ default:
+ pd->isoc_latency = OZ_MAX_TX_QUEUE_ISOC;
+ }
}
if (body->max_len_div16)
pd->max_tx_size = ((u16)body->max_len_div16)<<4;
diff --git a/drivers/staging/ozwpan/ozproto.h b/drivers/staging/ozwpan/ozproto.h
index 89aea28bd8d5..755a08d0e1ca 100644
--- a/drivers/staging/ozwpan/ozproto.h
+++ b/drivers/staging/ozwpan/ozproto.h
@@ -14,7 +14,7 @@
/* Converts millisecs to jiffies.
*/
-#define oz_ms_to_jiffies(__x) (((__x)*1000)/HZ)
+#define oz_ms_to_jiffies(__x) msecs_to_jiffies(__x)
/* Quantum milliseconds.
*/
@@ -30,6 +30,12 @@
/* Maximun sizes of tx frames. */
#define OZ_MAX_TX_SIZE 1514
+/* Maximum number of uncompleted isoc frames that can be pending in network. */
+#define OZ_MAX_SUBMITTED_ISOC 16
+
+/* Maximum number of uncompleted isoc frames that can be pending in Tx Queue. */
+#define OZ_MAX_TX_QUEUE_ISOC 32
+
/* Application handler functions.
*/
typedef int (*oz_app_init_fn_t)(void);
diff --git a/drivers/staging/ozwpan/ozprotocol.h b/drivers/staging/ozwpan/ozprotocol.h
index 1e4edbeb61cd..17b09b9a5b08 100644
--- a/drivers/staging/ozwpan/ozprotocol.h
+++ b/drivers/staging/ozwpan/ozprotocol.h
@@ -65,6 +65,10 @@ struct oz_hdr {
#define OZ_LAST_PN_HALF_CYCLE 127
+#define OZ_LATENCY_MASK 0xc0
+#define OZ_ONE_MS_LATENCY 0x40
+#define OZ_TEN_MS_LATENCY 0x80
+
/* Connect request data structure.
*/
struct oz_elt_connect_req {
@@ -73,7 +77,7 @@ struct oz_elt_connect_req {
u8 pd_info;
u8 session_id;
u8 presleep;
- u8 resv2;
+ u8 ms_isoc_latency;
u8 host_vendor;
u8 keep_alive;
u16 apps;
diff --git a/drivers/staging/panel/panel.c b/drivers/staging/panel/panel.c
index 39f9982c2708..6e9f7090c451 100644
--- a/drivers/staging/panel/panel.c
+++ b/drivers/staging/panel/panel.c
@@ -137,8 +137,8 @@
#define r_ctr(x) (parport_read_control((x)->port))
#define r_dtr(x) (parport_read_data((x)->port))
#define r_str(x) (parport_read_status((x)->port))
-#define w_ctr(x, y) do { parport_write_control((x)->port, (y)); } while (0)
-#define w_dtr(x, y) do { parport_write_data((x)->port, (y)); } while (0)
+#define w_ctr(x, y) (parport_write_control((x)->port, (y)))
+#define w_dtr(x, y) (parport_write_data((x)->port, (y)))
/* this defines which bits are to be used and which ones to be ignored */
/* logical or of the output bits involved in the scan matrix */
@@ -757,38 +757,38 @@ static void lcd_backlight(int on)
return;
/* The backlight is activated by setting the AUTOFEED line to +5V */
- spin_lock(&pprt_lock);
+ spin_lock_irq(&pprt_lock);
bits.bl = on;
panel_set_bits();
- spin_unlock(&pprt_lock);
+ spin_unlock_irq(&pprt_lock);
}
/* send a command to the LCD panel in serial mode */
static void lcd_write_cmd_s(int cmd)
{
- spin_lock(&pprt_lock);
+ spin_lock_irq(&pprt_lock);
lcd_send_serial(0x1F); /* R/W=W, RS=0 */
lcd_send_serial(cmd & 0x0F);
lcd_send_serial((cmd >> 4) & 0x0F);
udelay(40); /* the shortest command takes at least 40 us */
- spin_unlock(&pprt_lock);
+ spin_unlock_irq(&pprt_lock);
}
/* send data to the LCD panel in serial mode */
static void lcd_write_data_s(int data)
{
- spin_lock(&pprt_lock);
+ spin_lock_irq(&pprt_lock);
lcd_send_serial(0x5F); /* R/W=W, RS=1 */
lcd_send_serial(data & 0x0F);
lcd_send_serial((data >> 4) & 0x0F);
udelay(40); /* the shortest data takes at least 40 us */
- spin_unlock(&pprt_lock);
+ spin_unlock_irq(&pprt_lock);
}
/* send a command to the LCD panel in 8 bits parallel mode */
static void lcd_write_cmd_p8(int cmd)
{
- spin_lock(&pprt_lock);
+ spin_lock_irq(&pprt_lock);
/* present the data to the data port */
w_dtr(pprt, cmd);
udelay(20); /* maintain the data during 20 us before the strobe */
@@ -804,13 +804,13 @@ static void lcd_write_cmd_p8(int cmd)
set_ctrl_bits();
udelay(120); /* the shortest command takes at least 120 us */
- spin_unlock(&pprt_lock);
+ spin_unlock_irq(&pprt_lock);
}
/* send data to the LCD panel in 8 bits parallel mode */
static void lcd_write_data_p8(int data)
{
- spin_lock(&pprt_lock);
+ spin_lock_irq(&pprt_lock);
/* present the data to the data port */
w_dtr(pprt, data);
udelay(20); /* maintain the data during 20 us before the strobe */
@@ -826,27 +826,27 @@ static void lcd_write_data_p8(int data)
set_ctrl_bits();
udelay(45); /* the shortest data takes at least 45 us */
- spin_unlock(&pprt_lock);
+ spin_unlock_irq(&pprt_lock);
}
/* send a command to the TI LCD panel */
static void lcd_write_cmd_tilcd(int cmd)
{
- spin_lock(&pprt_lock);
+ spin_lock_irq(&pprt_lock);
/* present the data to the control port */
w_ctr(pprt, cmd);
udelay(60);
- spin_unlock(&pprt_lock);
+ spin_unlock_irq(&pprt_lock);
}
/* send data to the TI LCD panel */
static void lcd_write_data_tilcd(int data)
{
- spin_lock(&pprt_lock);
+ spin_lock_irq(&pprt_lock);
/* present the data to the data port */
w_dtr(pprt, data);
udelay(60);
- spin_unlock(&pprt_lock);
+ spin_unlock_irq(&pprt_lock);
}
static void lcd_gotoxy(void)
@@ -879,14 +879,14 @@ static void lcd_clear_fast_s(void)
lcd_addr_x = lcd_addr_y = 0;
lcd_gotoxy();
- spin_lock(&pprt_lock);
+ spin_lock_irq(&pprt_lock);
for (pos = 0; pos < lcd_height * lcd_hwidth; pos++) {
lcd_send_serial(0x5F); /* R/W=W, RS=1 */
lcd_send_serial(' ' & 0x0F);
lcd_send_serial((' ' >> 4) & 0x0F);
udelay(40); /* the shortest data takes at least 40 us */
}
- spin_unlock(&pprt_lock);
+ spin_unlock_irq(&pprt_lock);
lcd_addr_x = lcd_addr_y = 0;
lcd_gotoxy();
@@ -899,7 +899,7 @@ static void lcd_clear_fast_p8(void)
lcd_addr_x = lcd_addr_y = 0;
lcd_gotoxy();
- spin_lock(&pprt_lock);
+ spin_lock_irq(&pprt_lock);
for (pos = 0; pos < lcd_height * lcd_hwidth; pos++) {
/* present the data to the data port */
w_dtr(pprt, ' ');
@@ -921,7 +921,7 @@ static void lcd_clear_fast_p8(void)
/* the shortest data takes at least 45 us */
udelay(45);
}
- spin_unlock(&pprt_lock);
+ spin_unlock_irq(&pprt_lock);
lcd_addr_x = lcd_addr_y = 0;
lcd_gotoxy();
@@ -934,14 +934,14 @@ static void lcd_clear_fast_tilcd(void)
lcd_addr_x = lcd_addr_y = 0;
lcd_gotoxy();
- spin_lock(&pprt_lock);
+ spin_lock_irq(&pprt_lock);
for (pos = 0; pos < lcd_height * lcd_hwidth; pos++) {
/* present the data to the data port */
w_dtr(pprt, ' ');
udelay(60);
}
- spin_unlock(&pprt_lock);
+ spin_unlock_irq(&pprt_lock);
lcd_addr_x = lcd_addr_y = 0;
lcd_gotoxy();
@@ -1197,7 +1197,7 @@ static inline int handle_lcd_special_code(void)
break;
}
- /* Check wether one flag was changed */
+ /* Check whether one flag was changed */
if (oldflags != lcd_flags) {
/* check whether one of B,C,D flags were changed */
if ((oldflags ^ lcd_flags) &
@@ -1212,7 +1212,7 @@ static inline int handle_lcd_special_code(void)
lcd_write_cmd(0x30
| ((lcd_flags & LCD_FLAG_F) ? 4 : 0)
| ((lcd_flags & LCD_FLAG_N) ? 8 : 0));
- /* check wether L flag was changed */
+ /* check whether L flag was changed */
else if ((oldflags ^ lcd_flags) & (LCD_FLAG_L)) {
if (lcd_flags & (LCD_FLAG_L))
lcd_backlight(1);
@@ -1886,11 +1886,11 @@ static void panel_process_inputs(void)
static void panel_scan_timer(void)
{
if (keypad_enabled && keypad_initialized) {
- if (spin_trylock(&pprt_lock)) {
+ if (spin_trylock_irq(&pprt_lock)) {
phys_scan_contacts();
/* no need for the parport anymore */
- spin_unlock(&pprt_lock);
+ spin_unlock_irq(&pprt_lock);
}
if (!inputs_stable || phys_curr != phys_prev)
diff --git a/drivers/staging/ramster/Kconfig b/drivers/staging/ramster/Kconfig
index 8349887827dc..843c54101438 100644
--- a/drivers/staging/ramster/Kconfig
+++ b/drivers/staging/ramster/Kconfig
@@ -1,13 +1,30 @@
+config ZCACHE2
+ bool "Dynamic compression of swap pages and clean pagecache pages"
+ depends on CRYPTO=y && SWAP=y && CLEANCACHE && FRONTSWAP && !ZCACHE
+ select CRYPTO_LZO
+ default n
+ help
+ Zcache2 doubles RAM efficiency while providing a significant
+ performance boosts on many workloads. Zcache2 uses
+ compression and an in-kernel implementation of transcendent
+ memory to store clean page cache pages and swap in RAM,
+ providing a noticeable reduction in disk I/O. Zcache2
+ is a complete rewrite of the older zcache; it was intended to
+ be a merge but that has been blocked due to political and
+ technical disagreements. It is intended that they will merge
+ again in the future. Until then, zcache2 is a single-node
+ version of ramster.
+
config RAMSTER
bool "Cross-machine RAM capacity sharing, aka peer-to-peer tmem"
- depends on (CLEANCACHE || FRONTSWAP) && CONFIGFS_FS=y && !ZCACHE && !XVMALLOC && !HIGHMEM && NET
- select LZO_COMPRESS
- select LZO_DECOMPRESS
+ depends on CONFIGFS_FS=y && SYSFS=y && !HIGHMEM && ZCACHE2=y
+ # must ensure struct page is 8-byte aligned
+ select HAVE_ALIGNED_STRUCT_PAGE if !64_BIT
default n
help
RAMster allows RAM on other machines in a cluster to be utilized
dynamically and symmetrically instead of swapping to a local swap
disk, thus improving performance on memory-constrained workloads
while minimizing total RAM across the cluster. RAMster, like
- zcache, compresses swap pages into local RAM, but then remotifies
+ zcache2, compresses swap pages into local RAM, but then remotifies
the compressed pages to another node in the RAMster cluster.
diff --git a/drivers/staging/ramster/Makefile b/drivers/staging/ramster/Makefile
index bcc13c87f996..2d8b9d0a6a8b 100644
--- a/drivers/staging/ramster/Makefile
+++ b/drivers/staging/ramster/Makefile
@@ -1 +1,6 @@
-obj-$(CONFIG_RAMSTER) += zcache-main.o tmem.o r2net.o xvmalloc.o cluster/
+zcache-y := zcache-main.o tmem.o zbud.o
+zcache-$(CONFIG_RAMSTER) += ramster/ramster.o ramster/r2net.o
+zcache-$(CONFIG_RAMSTER) += ramster/nodemanager.o ramster/tcp.o
+zcache-$(CONFIG_RAMSTER) += ramster/heartbeat.o ramster/masklog.o
+
+obj-$(CONFIG_ZCACHE2) += zcache.o
diff --git a/drivers/staging/ramster/TODO b/drivers/staging/ramster/TODO
deleted file mode 100644
index 46fcf0c58acf..000000000000
--- a/drivers/staging/ramster/TODO
+++ /dev/null
@@ -1,13 +0,0 @@
-For this staging driver, RAMster duplicates code from drivers/staging/zcache
-then incorporates changes to the local copy of the code. For V5, it also
-directly incorporates the soon-to-be-removed drivers/staging/zram/xvmalloc.[ch]
-as all testing has been done with xvmalloc rather than the new zsmalloc.
-Before RAMster can be promoted from staging, the zcache and RAMster drivers
-should be either merged or reorganized to separate out common code.
-
-Until V4, RAMster duplicated code from fs/ocfs2/cluster, but this made
-RAMster incompatible with ocfs2 running in the same kernel and included
-lots of code that could be removed. As of V5, the ocfs2 code has been
-mined and made RAMster-specific, made to communicate with a userland
-ramster-tools package rather than ocfs2-tools, and can co-exist with ocfs2
-both in the same kernel and in userland on the same machine.
diff --git a/drivers/staging/ramster/cluster/Makefile b/drivers/staging/ramster/cluster/Makefile
deleted file mode 100644
index 9c6943652c01..000000000000
--- a/drivers/staging/ramster/cluster/Makefile
+++ /dev/null
@@ -1,3 +0,0 @@
-obj-$(CONFIG_RAMSTER) += ramster_nodemanager.o
-
-ramster_nodemanager-objs := heartbeat.o masklog.o nodemanager.o tcp.o
diff --git a/drivers/staging/ramster/ramster.h b/drivers/staging/ramster/ramster.h
index 0c9455e8dcd8..1b71aea2ff62 100644
--- a/drivers/staging/ramster/ramster.h
+++ b/drivers/staging/ramster/ramster.h
@@ -1,118 +1,59 @@
+
/*
- * ramster.h
+ * zcache/ramster.h
*
- * Peer-to-peer transcendent memory
+ * Placeholder to resolve ramster references when !CONFIG_RAMSTER
+ * Real ramster.h lives in ramster subdirectory.
*
* Copyright (c) 2009-2012, Dan Magenheimer, Oracle Corp.
*/
-#ifndef _RAMSTER_H_
-#define _RAMSTER_H_
-
-/*
- * format of remote pampd:
- * bit 0 == intransit
- * bit 1 == is_remote... if this bit is set, then
- * bit 2-9 == remotenode
- * bit 10-22 == size
- * bit 23-30 == cksum
- */
-#define FAKE_PAMPD_INTRANSIT_BITS 1
-#define FAKE_PAMPD_ISREMOTE_BITS 1
-#define FAKE_PAMPD_REMOTENODE_BITS 8
-#define FAKE_PAMPD_REMOTESIZE_BITS 13
-#define FAKE_PAMPD_CHECKSUM_BITS 8
-
-#define FAKE_PAMPD_INTRANSIT_SHIFT 0
-#define FAKE_PAMPD_ISREMOTE_SHIFT (FAKE_PAMPD_INTRANSIT_SHIFT + \
- FAKE_PAMPD_INTRANSIT_BITS)
-#define FAKE_PAMPD_REMOTENODE_SHIFT (FAKE_PAMPD_ISREMOTE_SHIFT + \
- FAKE_PAMPD_ISREMOTE_BITS)
-#define FAKE_PAMPD_REMOTESIZE_SHIFT (FAKE_PAMPD_REMOTENODE_SHIFT + \
- FAKE_PAMPD_REMOTENODE_BITS)
-#define FAKE_PAMPD_CHECKSUM_SHIFT (FAKE_PAMPD_REMOTESIZE_SHIFT + \
- FAKE_PAMPD_REMOTESIZE_BITS)
+#ifndef _ZCACHE_RAMSTER_H_
+#define _ZCACHE_RAMSTER_H_
-#define FAKE_PAMPD_MASK(x) ((1UL << (x)) - 1)
-
-static inline void *pampd_make_remote(int remotenode, size_t size,
- unsigned char cksum)
+#ifdef CONFIG_RAMSTER
+#include "ramster/ramster.h"
+#else
+static inline void ramster_init(bool x, bool y, bool z)
{
- unsigned long fake_pampd = 0;
- fake_pampd |= 1UL << FAKE_PAMPD_ISREMOTE_SHIFT;
- fake_pampd |= ((unsigned long)remotenode &
- FAKE_PAMPD_MASK(FAKE_PAMPD_REMOTENODE_BITS)) <<
- FAKE_PAMPD_REMOTENODE_SHIFT;
- fake_pampd |= ((unsigned long)size &
- FAKE_PAMPD_MASK(FAKE_PAMPD_REMOTESIZE_BITS)) <<
- FAKE_PAMPD_REMOTESIZE_SHIFT;
- fake_pampd |= ((unsigned long)cksum &
- FAKE_PAMPD_MASK(FAKE_PAMPD_CHECKSUM_BITS)) <<
- FAKE_PAMPD_CHECKSUM_SHIFT;
- return (void *)fake_pampd;
}
-static inline unsigned int pampd_remote_node(void *pampd)
+static inline void ramster_register_pamops(struct tmem_pamops *p)
{
- unsigned long fake_pampd = (unsigned long)pampd;
- return (fake_pampd >> FAKE_PAMPD_REMOTENODE_SHIFT) &
- FAKE_PAMPD_MASK(FAKE_PAMPD_REMOTENODE_BITS);
}
-static inline unsigned int pampd_remote_size(void *pampd)
+static inline int ramster_remotify_pageframe(bool b)
{
- unsigned long fake_pampd = (unsigned long)pampd;
- return (fake_pampd >> FAKE_PAMPD_REMOTESIZE_SHIFT) &
- FAKE_PAMPD_MASK(FAKE_PAMPD_REMOTESIZE_BITS);
+ return 0;
}
-static inline unsigned char pampd_remote_cksum(void *pampd)
+static inline void *ramster_pampd_free(void *v, struct tmem_pool *p,
+ struct tmem_oid *o, uint32_t u, bool b)
{
- unsigned long fake_pampd = (unsigned long)pampd;
- return (fake_pampd >> FAKE_PAMPD_CHECKSUM_SHIFT) &
- FAKE_PAMPD_MASK(FAKE_PAMPD_CHECKSUM_BITS);
+ return NULL;
}
-static inline bool pampd_is_remote(void *pampd)
+static inline int ramster_do_preload_flnode(struct tmem_pool *p)
{
- unsigned long fake_pampd = (unsigned long)pampd;
- return (fake_pampd >> FAKE_PAMPD_ISREMOTE_SHIFT) &
- FAKE_PAMPD_MASK(FAKE_PAMPD_ISREMOTE_BITS);
+ return -1;
}
-static inline bool pampd_is_intransit(void *pampd)
+static inline bool pampd_is_remote(void *v)
{
- unsigned long fake_pampd = (unsigned long)pampd;
- return (fake_pampd >> FAKE_PAMPD_INTRANSIT_SHIFT) &
- FAKE_PAMPD_MASK(FAKE_PAMPD_INTRANSIT_BITS);
+ return false;
}
-/* note that it is a BUG for intransit to be set without isremote also set */
-static inline void *pampd_mark_intransit(void *pampd)
+static inline void ramster_count_foreign_pages(bool b, int i)
{
- unsigned long fake_pampd = (unsigned long)pampd;
-
- fake_pampd |= 1UL << FAKE_PAMPD_ISREMOTE_SHIFT;
- fake_pampd |= 1UL << FAKE_PAMPD_INTRANSIT_SHIFT;
- return (void *)fake_pampd;
}
-static inline void *pampd_mask_intransit_and_remote(void *marked_pampd)
+static inline void ramster_cpu_up(int cpu)
{
- unsigned long pampd = (unsigned long)marked_pampd;
-
- pampd &= ~(1UL << FAKE_PAMPD_INTRANSIT_SHIFT);
- pampd &= ~(1UL << FAKE_PAMPD_ISREMOTE_SHIFT);
- return (void *)pampd;
}
-extern int ramster_remote_async_get(struct tmem_xhandle *,
- bool, int, size_t, uint8_t, void *extra);
-extern int ramster_remote_put(struct tmem_xhandle *, char *, size_t,
- bool, int *);
-extern int ramster_remote_flush(struct tmem_xhandle *, int);
-extern int ramster_remote_flush_object(struct tmem_xhandle *, int);
-extern int r2net_register_handlers(void);
-extern int r2net_remote_target_node_set(int);
+static inline void ramster_cpu_down(int cpu)
+{
+}
+#endif
-#endif /* _TMEM_H */
+#endif /* _ZCACHE_RAMSTER_H */
diff --git a/drivers/staging/ramster/cluster/heartbeat.c b/drivers/staging/ramster/ramster/heartbeat.c
index 00209490756e..75d3fe80b055 100644
--- a/drivers/staging/ramster/cluster/heartbeat.c
+++ b/drivers/staging/ramster/ramster/heartbeat.c
@@ -172,8 +172,7 @@ static void r2hb_hb_group_drop_item(struct config_group *group,
struct config_item *item)
{
if (r2hb_global_heartbeat_active()) {
- printk(KERN_NOTICE "ramster: Heartbeat %s "
- "on region %s (%s)\n",
+ pr_notice("ramster: Heartbeat %s on region %s (%s)\n",
"stopped/aborted", config_item_name(item),
"no region");
}
@@ -265,8 +264,7 @@ ssize_t r2hb_hb_group_mode_store(struct r2hb_hb_group *group,
ret = r2hb_global_hearbeat_mode_set(i);
if (!ret)
- printk(KERN_NOTICE "ramster: Heartbeat mode "
- "set to %s\n",
+ pr_notice("ramster: Heartbeat mode set to %s\n",
r2hb_heartbeat_mode_desc[i]);
return count;
}
diff --git a/drivers/staging/ramster/cluster/heartbeat.h b/drivers/staging/ramster/ramster/heartbeat.h
index 6cbc775bd63b..6cbc775bd63b 100644
--- a/drivers/staging/ramster/cluster/heartbeat.h
+++ b/drivers/staging/ramster/ramster/heartbeat.h
diff --git a/drivers/staging/ramster/cluster/masklog.c b/drivers/staging/ramster/ramster/masklog.c
index 1261d8579aae..1261d8579aae 100644
--- a/drivers/staging/ramster/cluster/masklog.c
+++ b/drivers/staging/ramster/ramster/masklog.c
diff --git a/drivers/staging/ramster/cluster/masklog.h b/drivers/staging/ramster/ramster/masklog.h
index 918ae110b699..918ae110b699 100644
--- a/drivers/staging/ramster/cluster/masklog.h
+++ b/drivers/staging/ramster/ramster/masklog.h
diff --git a/drivers/staging/ramster/cluster/nodemanager.c b/drivers/staging/ramster/ramster/nodemanager.c
index de0e5c8da6ea..c0f48158735d 100644
--- a/drivers/staging/ramster/cluster/nodemanager.c
+++ b/drivers/staging/ramster/ramster/nodemanager.c
@@ -528,7 +528,8 @@ static ssize_t r2nm_cluster_attr_idle_timeout_ms_write(
r2net_num_connected_peers());
ret = -EINVAL;
} else if (val <= cluster->cl_keepalive_delay_ms) {
- mlog(ML_NOTICE, "r2net: idle timeout must be larger "
+ mlog(ML_NOTICE,
+ "r2net: idle timeout must be larger "
"than keepalive delay\n");
ret = -EINVAL;
} else {
@@ -563,7 +564,8 @@ static ssize_t r2nm_cluster_attr_keepalive_delay_ms_write(
r2net_num_connected_peers());
ret = -EINVAL;
} else if (val >= cluster->cl_idle_timeout_ms) {
- mlog(ML_NOTICE, "r2net: keepalive delay must be "
+ mlog(ML_NOTICE,
+ "r2net: keepalive delay must be "
"smaller than idle timeout\n");
ret = -EINVAL;
} else {
@@ -612,7 +614,7 @@ static ssize_t r2nm_cluster_attr_fence_method_write(
if (strncasecmp(page, r2nm_fence_method_desc[i], count - 1))
continue;
if (cluster->cl_fence_method != i) {
- printk(KERN_INFO "ramster: Changing fence method to %s\n",
+ pr_info("ramster: Changing fence method to %s\n",
r2nm_fence_method_desc[i]);
cluster->cl_fence_method = i;
}
@@ -967,7 +969,7 @@ static int __init init_r2nm(void)
mutex_init(&r2nm_cluster_group.cs_subsys.su_mutex);
ret = configfs_register_subsystem(&r2nm_cluster_group.cs_subsys);
if (ret) {
- printk(KERN_ERR "nodemanager: Registration returned %d\n", ret);
+ pr_err("nodemanager: Registration returned %d\n", ret);
goto out_callbacks;
}
@@ -988,5 +990,6 @@ out:
MODULE_AUTHOR("Oracle");
MODULE_LICENSE("GPL");
-module_init(init_r2nm)
-module_exit(exit_r2nm)
+/* module_init(init_r2nm) */
+late_initcall(init_r2nm);
+/* module_exit(exit_r2nm) */
diff --git a/drivers/staging/ramster/cluster/nodemanager.h b/drivers/staging/ramster/ramster/nodemanager.h
index 41a04df5842c..41a04df5842c 100644
--- a/drivers/staging/ramster/cluster/nodemanager.h
+++ b/drivers/staging/ramster/ramster/nodemanager.h
diff --git a/drivers/staging/ramster/r2net.c b/drivers/staging/ramster/ramster/r2net.c
index fc830c3ac74b..34818dc65612 100644
--- a/drivers/staging/ramster/r2net.c
+++ b/drivers/staging/ramster/ramster/r2net.c
@@ -1,7 +1,7 @@
/*
* r2net.c
*
- * Copyright (c) 2011, Dan Magenheimer, Oracle Corp.
+ * Copyright (c) 2011-2012, Dan Magenheimer, Oracle Corp.
*
* Ramster_r2net provides an interface between zcache and r2net.
*
@@ -9,10 +9,10 @@
*/
#include <linux/list.h>
-#include "cluster/tcp.h"
-#include "cluster/nodemanager.h"
-#include "tmem.h"
-#include "zcache.h"
+#include "tcp.h"
+#include "nodemanager.h"
+#include "../tmem.h"
+#include "../zcache.h"
#include "ramster.h"
#define RAMSTER_TESTING
@@ -33,7 +33,7 @@ enum {
#define RMSTR_R2NET_MAX_LEN \
(R2NET_MAX_PAYLOAD_BYTES - sizeof(struct tmem_xhandle))
-#include "cluster/tcp_internal.h"
+#include "tcp_internal.h"
static struct r2nm_node *r2net_target_node;
static int r2net_target_nodenum;
@@ -72,8 +72,8 @@ static int ramster_remote_async_get_request_handler(struct r2net_msg *msg,
*(struct tmem_xhandle *)pdata = xh;
pdata += sizeof(struct tmem_xhandle);
local_irq_save(flags);
- found = zcache_get(xh.client_id, xh.pool_id, &xh.oid, xh.index,
- pdata, &size, 1, get_and_free ? 1 : -1);
+ found = zcache_get_page(xh.client_id, xh.pool_id, &xh.oid, xh.index,
+ pdata, &size, true, get_and_free ? 1 : -1);
local_irq_restore(flags);
if (found < 0) {
/* a zero size indicates the get failed */
@@ -98,7 +98,7 @@ static int ramster_remote_async_get_reply_handler(struct r2net_msg *msg,
in += sizeof(struct tmem_xhandle);
datalen -= sizeof(struct tmem_xhandle);
BUG_ON(datalen < 0 || datalen > PAGE_SIZE);
- ret = zcache_localify(xh->pool_id, &xh->oid, xh->index,
+ ret = ramster_localify(xh->pool_id, &xh->oid, xh->index,
in, datalen, xh->extra);
#ifdef RAMSTER_TESTING
if (ret == -EEXIST)
@@ -123,8 +123,8 @@ int ramster_remote_put_handler(struct r2net_msg *msg,
p += sizeof(struct tmem_xhandle);
zcache_autocreate_pool(xh->client_id, xh->pool_id, ephemeral);
local_irq_save(flags);
- ret = zcache_put(xh->client_id, xh->pool_id, &xh->oid, xh->index,
- p, datalen, 1, ephemeral ? 1 : -1);
+ ret = zcache_put_page(xh->client_id, xh->pool_id, &xh->oid, xh->index,
+ p, datalen, true, ephemeral);
local_irq_restore(flags);
return ret;
}
@@ -137,7 +137,8 @@ int ramster_remote_flush_handler(struct r2net_msg *msg,
xh = (struct tmem_xhandle *)p;
p += sizeof(struct tmem_xhandle);
- (void)zcache_flush(xh->client_id, xh->pool_id, &xh->oid, xh->index);
+ (void)zcache_flush_page(xh->client_id, xh->pool_id,
+ &xh->oid, xh->index);
return 0;
}
@@ -153,15 +154,16 @@ int ramster_remote_flobj_handler(struct r2net_msg *msg,
return 0;
}
-int ramster_remote_async_get(struct tmem_xhandle *xh, bool free, int remotenode,
+int r2net_remote_async_get(struct tmem_xhandle *xh, bool free, int remotenode,
size_t expect_size, uint8_t expect_cksum,
void *extra)
{
- int ret = -1, status;
+ int nodenum, ret = -1, status;
struct r2nm_node *node = NULL;
struct kvec vec[1];
size_t veclen = 1;
u32 msg_type;
+ struct r2net_node *nn;
node = r2nm_get_node_by_num(remotenode);
if (node == NULL)
@@ -172,6 +174,21 @@ int ramster_remote_async_get(struct tmem_xhandle *xh, bool free, int remotenode,
xh->extra = extra;
vec[0].iov_len = sizeof(*xh);
vec[0].iov_base = xh;
+
+ node = r2net_target_node;
+ if (!node)
+ goto out;
+
+ nodenum = r2net_target_nodenum;
+
+ r2nm_node_get(node);
+ nn = r2net_nn_from_num(nodenum);
+ if (nn->nn_persistent_error || !nn->nn_sc_valid) {
+ ret = -ENOTCONN;
+ r2nm_node_put(node);
+ goto out;
+ }
+
if (free)
msg_type = RMSTR_TMEM_ASYNC_GET_AND_FREE_REQUEST;
else
@@ -180,8 +197,13 @@ int ramster_remote_async_get(struct tmem_xhandle *xh, bool free, int remotenode,
vec, veclen, remotenode, &status);
r2nm_node_put(node);
if (ret < 0) {
+ if (ret == -ENOTCONN || ret == -EHOSTDOWN)
+ goto out;
+ if (ret == -EAGAIN)
+ goto out;
/* FIXME handle bad message possibilities here? */
- pr_err("UNTESTED ret<0 in ramster_remote_async_get\n");
+ pr_err("UNTESTED ret<0 in ramster_remote_async_get: ret=%d\n",
+ ret);
}
ret = status;
out:
@@ -219,7 +241,7 @@ static void ramster_check_irq_counts(void)
}
#endif
-int ramster_remote_put(struct tmem_xhandle *xh, char *data, size_t size,
+int r2net_remote_put(struct tmem_xhandle *xh, char *data, size_t size,
bool ephemeral, int *remotenode)
{
int nodenum, ret = -1, status;
@@ -227,9 +249,7 @@ int ramster_remote_put(struct tmem_xhandle *xh, char *data, size_t size,
struct kvec vec[2];
size_t veclen = 2;
u32 msg_type;
-#ifdef RAMSTER_TESTING
struct r2net_node *nn;
-#endif
BUG_ON(size > RMSTR_R2NET_MAX_LEN);
xh->client_id = r2nm_this_node(); /* which node is putting */
@@ -237,6 +257,7 @@ int ramster_remote_put(struct tmem_xhandle *xh, char *data, size_t size,
vec[0].iov_base = xh;
vec[1].iov_len = size;
vec[1].iov_base = data;
+
node = r2net_target_node;
if (!node)
goto out;
@@ -245,10 +266,12 @@ int ramster_remote_put(struct tmem_xhandle *xh, char *data, size_t size,
r2nm_node_get(node);
-#ifdef RAMSTER_TESTING
nn = r2net_nn_from_num(nodenum);
- WARN_ON_ONCE(nn->nn_persistent_error || !nn->nn_sc_valid);
-#endif
+ if (nn->nn_persistent_error || !nn->nn_sc_valid) {
+ ret = -ENOTCONN;
+ r2nm_node_put(node);
+ goto out;
+ }
if (ephemeral)
msg_type = RMSTR_TMEM_PUT_EPH;
@@ -261,16 +284,6 @@ int ramster_remote_put(struct tmem_xhandle *xh, char *data, size_t size,
ret = r2net_send_message_vec(msg_type, RMSTR_KEY, vec, veclen,
nodenum, &status);
-#ifdef RAMSTER_TESTING
- if (ret != 0) {
- static unsigned long cnt;
- cnt++;
- if (!(cnt&(cnt-1)))
- pr_err("ramster_remote_put: message failed, ret=%d, cnt=%lu\n",
- ret, cnt);
- ret = -1;
- }
-#endif
if (ret < 0)
ret = -1;
else {
@@ -283,7 +296,7 @@ out:
return ret;
}
-int ramster_remote_flush(struct tmem_xhandle *xh, int remotenode)
+int r2net_remote_flush(struct tmem_xhandle *xh, int remotenode)
{
int ret = -1, status;
struct r2nm_node *node = NULL;
@@ -303,7 +316,7 @@ int ramster_remote_flush(struct tmem_xhandle *xh, int remotenode)
return ret;
}
-int ramster_remote_flush_object(struct tmem_xhandle *xh, int remotenode)
+int r2net_remote_flush_object(struct tmem_xhandle *xh, int remotenode)
{
int ret = -1, status;
struct r2nm_node *node = NULL;
diff --git a/drivers/staging/ramster/ramster/ramster.c b/drivers/staging/ramster/ramster/ramster.c
new file mode 100644
index 000000000000..c06709f39682
--- /dev/null
+++ b/drivers/staging/ramster/ramster/ramster.c
@@ -0,0 +1,985 @@
+/*
+ * ramster.c
+ *
+ * Copyright (c) 2010-2012, Dan Magenheimer, Oracle Corp.
+ *
+ * RAMster implements peer-to-peer transcendent memory, allowing a "cluster" of
+ * kernels to dynamically pool their RAM so that a RAM-hungry workload on one
+ * machine can temporarily and transparently utilize RAM on another machine
+ * which is presumably idle or running a non-RAM-hungry workload.
+ *
+ * RAMster combines a clustering and messaging foundation based on the ocfs2
+ * cluster layer with the in-kernel compression implementation of zcache, and
+ * adds code to glue them together. When a page is "put" to RAMster, it is
+ * compressed and stored locally. Periodically, a thread will "remotify" these
+ * pages by sending them via messages to a remote machine. When the page is
+ * later needed as indicated by a page fault, a "get" is issued. If the data
+ * is local, it is uncompressed and the fault is resolved. If the data is
+ * remote, a message is sent to fetch the data and the faulting thread sleeps;
+ * when the data arrives, the thread awakens, the data is decompressed and
+ * the fault is resolved.
+
+ * As of V5, clusters up to eight nodes are supported; each node can remotify
+ * pages to one specified node, so clusters can be configured as clients to
+ * a "memory server". Some simple policy is in place that will need to be
+ * refined over time. Larger clusters and fault-resistant protocols can also
+ * be added over time.
+ */
+
+#include <linux/module.h>
+#include <linux/cpu.h>
+#include <linux/highmem.h>
+#include <linux/list.h>
+#include <linux/lzo.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <linux/frontswap.h>
+#include "../tmem.h"
+#include "../zcache.h"
+#include "../zbud.h"
+#include "ramster.h"
+#include "ramster_nodemanager.h"
+#include "tcp.h"
+
+#define RAMSTER_TESTING
+
+#ifndef CONFIG_SYSFS
+#error "ramster needs sysfs to define cluster nodes to use"
+#endif
+
+static bool use_cleancache __read_mostly;
+static bool use_frontswap __read_mostly;
+static bool use_frontswap_exclusive_gets __read_mostly;
+
+/* These must be sysfs not debugfs as they are checked/used by userland!! */
+static unsigned long ramster_interface_revision __read_mostly =
+ R2NM_API_VERSION; /* interface revision must match userspace! */
+static unsigned long ramster_pers_remotify_enable __read_mostly;
+static unsigned long ramster_eph_remotify_enable __read_mostly;
+static atomic_t ramster_remote_pers_pages = ATOMIC_INIT(0);
+#define MANUAL_NODES 8
+static bool ramster_nodes_manual_up[MANUAL_NODES] __read_mostly;
+static int ramster_remote_target_nodenum __read_mostly = -1;
+
+/* these counters are made available via debugfs */
+static long ramster_flnodes;
+static atomic_t ramster_flnodes_atomic = ATOMIC_INIT(0);
+static unsigned long ramster_flnodes_max;
+static long ramster_foreign_eph_pages;
+static atomic_t ramster_foreign_eph_pages_atomic = ATOMIC_INIT(0);
+static unsigned long ramster_foreign_eph_pages_max;
+static long ramster_foreign_pers_pages;
+static atomic_t ramster_foreign_pers_pages_atomic = ATOMIC_INIT(0);
+static unsigned long ramster_foreign_pers_pages_max;
+static unsigned long ramster_eph_pages_remoted;
+static unsigned long ramster_pers_pages_remoted;
+static unsigned long ramster_eph_pages_remote_failed;
+static unsigned long ramster_pers_pages_remote_failed;
+static unsigned long ramster_remote_eph_pages_succ_get;
+static unsigned long ramster_remote_pers_pages_succ_get;
+static unsigned long ramster_remote_eph_pages_unsucc_get;
+static unsigned long ramster_remote_pers_pages_unsucc_get;
+static unsigned long ramster_pers_pages_remote_nomem;
+static unsigned long ramster_remote_objects_flushed;
+static unsigned long ramster_remote_object_flushes_failed;
+static unsigned long ramster_remote_pages_flushed;
+static unsigned long ramster_remote_page_flushes_failed;
+/* FIXME frontswap selfshrinking knobs in debugfs? */
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#define zdfs debugfs_create_size_t
+#define zdfs64 debugfs_create_u64
+static int __init ramster_debugfs_init(void)
+{
+ struct dentry *root = debugfs_create_dir("ramster", NULL);
+ if (root == NULL)
+ return -ENXIO;
+
+ zdfs("eph_pages_remoted", S_IRUGO, root, &ramster_eph_pages_remoted);
+ zdfs("pers_pages_remoted", S_IRUGO, root, &ramster_pers_pages_remoted);
+ zdfs("eph_pages_remote_failed", S_IRUGO, root,
+ &ramster_eph_pages_remote_failed);
+ zdfs("pers_pages_remote_failed", S_IRUGO, root,
+ &ramster_pers_pages_remote_failed);
+ zdfs("remote_eph_pages_succ_get", S_IRUGO, root,
+ &ramster_remote_eph_pages_succ_get);
+ zdfs("remote_pers_pages_succ_get", S_IRUGO, root,
+ &ramster_remote_pers_pages_succ_get);
+ zdfs("remote_eph_pages_unsucc_get", S_IRUGO, root,
+ &ramster_remote_eph_pages_unsucc_get);
+ zdfs("remote_pers_pages_unsucc_get", S_IRUGO, root,
+ &ramster_remote_pers_pages_unsucc_get);
+ zdfs("pers_pages_remote_nomem", S_IRUGO, root,
+ &ramster_pers_pages_remote_nomem);
+ zdfs("remote_objects_flushed", S_IRUGO, root,
+ &ramster_remote_objects_flushed);
+ zdfs("remote_pages_flushed", S_IRUGO, root,
+ &ramster_remote_pages_flushed);
+ zdfs("remote_object_flushes_failed", S_IRUGO, root,
+ &ramster_remote_object_flushes_failed);
+ zdfs("remote_page_flushes_failed", S_IRUGO, root,
+ &ramster_remote_page_flushes_failed);
+ zdfs("foreign_eph_pages", S_IRUGO, root,
+ &ramster_foreign_eph_pages);
+ zdfs("foreign_eph_pages_max", S_IRUGO, root,
+ &ramster_foreign_eph_pages_max);
+ zdfs("foreign_pers_pages", S_IRUGO, root,
+ &ramster_foreign_pers_pages);
+ zdfs("foreign_pers_pages_max", S_IRUGO, root,
+ &ramster_foreign_pers_pages_max);
+ return 0;
+}
+#undef zdebugfs
+#undef zdfs64
+#endif
+
+static LIST_HEAD(ramster_rem_op_list);
+static DEFINE_SPINLOCK(ramster_rem_op_list_lock);
+static DEFINE_PER_CPU(struct ramster_preload, ramster_preloads);
+
+static DEFINE_PER_CPU(unsigned char *, ramster_remoteputmem1);
+static DEFINE_PER_CPU(unsigned char *, ramster_remoteputmem2);
+
+static struct kmem_cache *ramster_flnode_cache __read_mostly;
+
+static struct flushlist_node *ramster_flnode_alloc(struct tmem_pool *pool)
+{
+ struct flushlist_node *flnode = NULL;
+ struct ramster_preload *kp;
+
+ kp = &__get_cpu_var(ramster_preloads);
+ flnode = kp->flnode;
+ BUG_ON(flnode == NULL);
+ kp->flnode = NULL;
+ ramster_flnodes = atomic_inc_return(&ramster_flnodes_atomic);
+ if (ramster_flnodes > ramster_flnodes_max)
+ ramster_flnodes_max = ramster_flnodes;
+ return flnode;
+}
+
+/* the "flush list" asynchronously collects pages to remotely flush */
+#define FLUSH_ENTIRE_OBJECT ((uint32_t)-1)
+static void ramster_flnode_free(struct flushlist_node *flnode,
+ struct tmem_pool *pool)
+{
+ int flnodes;
+
+ flnodes = atomic_dec_return(&ramster_flnodes_atomic);
+ BUG_ON(flnodes < 0);
+ kmem_cache_free(ramster_flnode_cache, flnode);
+}
+
+int ramster_do_preload_flnode(struct tmem_pool *pool)
+{
+ struct ramster_preload *kp;
+ struct flushlist_node *flnode;
+ int ret = -ENOMEM;
+
+ BUG_ON(!irqs_disabled());
+ if (unlikely(ramster_flnode_cache == NULL))
+ BUG();
+ kp = &__get_cpu_var(ramster_preloads);
+ flnode = kmem_cache_alloc(ramster_flnode_cache, GFP_ATOMIC);
+ if (unlikely(flnode == NULL) && kp->flnode == NULL)
+ BUG(); /* FIXME handle more gracefully, but how??? */
+ else if (kp->flnode == NULL)
+ kp->flnode = flnode;
+ else
+ kmem_cache_free(ramster_flnode_cache, flnode);
+ return ret;
+}
+
+/*
+ * Called by the message handler after a (still compressed) page has been
+ * fetched from the remote machine in response to an "is_remote" tmem_get
+ * or persistent tmem_localify. For a tmem_get, "extra" is the address of
+ * the page that is to be filled to successfully resolve the tmem_get; for
+ * a (persistent) tmem_localify, "extra" is NULL (as the data is placed only
+ * in the local zcache). "data" points to "size" bytes of (compressed) data
+ * passed in the message. In the case of a persistent remote get, if
+ * pre-allocation was successful (see ramster_repatriate_preload), the page
+ * is placed into both local zcache and at "extra".
+ */
+int ramster_localify(int pool_id, struct tmem_oid *oidp, uint32_t index,
+ char *data, unsigned int size, void *extra)
+{
+ int ret = -ENOENT;
+ unsigned long flags;
+ struct tmem_pool *pool;
+ bool eph, delete = false;
+ void *pampd, *saved_hb;
+ struct tmem_obj *obj;
+
+ pool = zcache_get_pool_by_id(LOCAL_CLIENT, pool_id);
+ if (unlikely(pool == NULL))
+ /* pool doesn't exist anymore */
+ goto out;
+ eph = is_ephemeral(pool);
+ local_irq_save(flags); /* FIXME: maybe only disable softirqs? */
+ pampd = tmem_localify_get_pampd(pool, oidp, index, &obj, &saved_hb);
+ if (pampd == NULL) {
+ /* hmmm... must have been a flush while waiting */
+#ifdef RAMSTER_TESTING
+ pr_err("UNTESTED pampd==NULL in ramster_localify\n");
+#endif
+ if (eph)
+ ramster_remote_eph_pages_unsucc_get++;
+ else
+ ramster_remote_pers_pages_unsucc_get++;
+ obj = NULL;
+ goto finish;
+ } else if (unlikely(!pampd_is_remote(pampd))) {
+ /* hmmm... must have been a dup put while waiting */
+#ifdef RAMSTER_TESTING
+ pr_err("UNTESTED dup while waiting in ramster_localify\n");
+#endif
+ if (eph)
+ ramster_remote_eph_pages_unsucc_get++;
+ else
+ ramster_remote_pers_pages_unsucc_get++;
+ obj = NULL;
+ pampd = NULL;
+ ret = -EEXIST;
+ goto finish;
+ } else if (size == 0) {
+ /* no remote data, delete the local is_remote pampd */
+ pampd = NULL;
+ if (eph)
+ ramster_remote_eph_pages_unsucc_get++;
+ else
+ BUG();
+ delete = true;
+ goto finish;
+ }
+ if (pampd_is_intransit(pampd)) {
+ /*
+ * a pampd is marked intransit if it is remote and space has
+ * been allocated for it locally (note, only happens for
+ * persistent pages, in which case the remote copy is freed)
+ */
+ BUG_ON(eph);
+ pampd = pampd_mask_intransit_and_remote(pampd);
+ zbud_copy_to_zbud(pampd, data, size);
+ } else {
+ /*
+ * setting pampd to NULL tells tmem_localify_finish to leave
+ * pampd alone... meaning it is left pointing to the
+ * remote copy
+ */
+ pampd = NULL;
+ obj = NULL;
+ }
+ /*
+ * but in all cases, we decompress direct-to-memory to complete
+ * the remotify and return success
+ */
+ BUG_ON(extra == NULL);
+ zcache_decompress_to_page(data, size, (struct page *)extra);
+ if (eph)
+ ramster_remote_eph_pages_succ_get++;
+ else
+ ramster_remote_pers_pages_succ_get++;
+ ret = 0;
+finish:
+ tmem_localify_finish(obj, index, pampd, saved_hb, delete);
+ zcache_put_pool(pool);
+ local_irq_restore(flags);
+out:
+ return ret;
+}
+
+void ramster_pampd_new_obj(struct tmem_obj *obj)
+{
+ obj->extra = NULL;
+}
+
+void ramster_pampd_free_obj(struct tmem_pool *pool, struct tmem_obj *obj,
+ bool pool_destroy)
+{
+ struct flushlist_node *flnode;
+
+ BUG_ON(preemptible());
+ if (obj->extra == NULL)
+ return;
+ if (pool_destroy && is_ephemeral(pool))
+ /* FIXME don't bother with remote eph data for now */
+ return;
+ BUG_ON(!pampd_is_remote(obj->extra));
+ flnode = ramster_flnode_alloc(pool);
+ flnode->xh.client_id = pampd_remote_node(obj->extra);
+ flnode->xh.pool_id = pool->pool_id;
+ flnode->xh.oid = obj->oid;
+ flnode->xh.index = FLUSH_ENTIRE_OBJECT;
+ flnode->rem_op.op = RAMSTER_REMOTIFY_FLUSH_OBJ;
+ spin_lock(&ramster_rem_op_list_lock);
+ list_add(&flnode->rem_op.list, &ramster_rem_op_list);
+ spin_unlock(&ramster_rem_op_list_lock);
+}
+
+/*
+ * Called on a remote persistent tmem_get to attempt to preallocate
+ * local storage for the data contained in the remote persistent page.
+ * If successfully preallocated, returns the pampd, marked as remote and
+ * in_transit. Else returns NULL. Note that the appropriate tmem data
+ * structure must be locked.
+ */
+void *ramster_pampd_repatriate_preload(void *pampd, struct tmem_pool *pool,
+ struct tmem_oid *oidp, uint32_t index,
+ bool *intransit)
+{
+ int clen = pampd_remote_size(pampd), c;
+ void *ret_pampd = NULL;
+ unsigned long flags;
+ struct tmem_handle th;
+
+ BUG_ON(!pampd_is_remote(pampd));
+ BUG_ON(is_ephemeral(pool));
+ if (use_frontswap_exclusive_gets)
+ /* don't need local storage */
+ goto out;
+ if (pampd_is_intransit(pampd)) {
+ /*
+ * to avoid multiple allocations (and maybe a memory leak)
+ * don't preallocate if already in the process of being
+ * repatriated
+ */
+ *intransit = true;
+ goto out;
+ }
+ *intransit = false;
+ local_irq_save(flags);
+ th.client_id = pampd_remote_node(pampd);
+ th.pool_id = pool->pool_id;
+ th.oid = *oidp;
+ th.index = index;
+ ret_pampd = zcache_pampd_create(NULL, clen, true, false, &th);
+ if (ret_pampd != NULL) {
+ /*
+ * a pampd is marked intransit if it is remote and space has
+ * been allocated for it locally (note, only happens for
+ * persistent pages, in which case the remote copy is freed)
+ */
+ ret_pampd = pampd_mark_intransit(ret_pampd);
+ c = atomic_dec_return(&ramster_remote_pers_pages);
+ WARN_ON_ONCE(c < 0);
+ } else {
+ ramster_pers_pages_remote_nomem++;
+ }
+ local_irq_restore(flags);
+out:
+ return ret_pampd;
+}
+
+/*
+ * Called on a remote tmem_get to invoke a message to fetch the page.
+ * Might sleep so no tmem locks can be held. "extra" is passed
+ * all the way through the round-trip messaging to ramster_localify.
+ */
+int ramster_pampd_repatriate(void *fake_pampd, void *real_pampd,
+ struct tmem_pool *pool,
+ struct tmem_oid *oid, uint32_t index,
+ bool free, void *extra)
+{
+ struct tmem_xhandle xh;
+ int ret;
+
+ if (pampd_is_intransit(real_pampd))
+ /* have local space pre-reserved, so free remote copy */
+ free = true;
+ xh = tmem_xhandle_fill(LOCAL_CLIENT, pool, oid, index);
+ /* unreliable request/response for now */
+ ret = r2net_remote_async_get(&xh, free,
+ pampd_remote_node(fake_pampd),
+ pampd_remote_size(fake_pampd),
+ pampd_remote_cksum(fake_pampd),
+ extra);
+ return ret;
+}
+
+bool ramster_pampd_is_remote(void *pampd)
+{
+ return pampd_is_remote(pampd);
+}
+
+int ramster_pampd_replace_in_obj(void *new_pampd, struct tmem_obj *obj)
+{
+ int ret = -1;
+
+ if (new_pampd != NULL) {
+ if (obj->extra == NULL)
+ obj->extra = new_pampd;
+ /* enforce that all remote pages in an object reside
+ * in the same node! */
+ else if (pampd_remote_node(new_pampd) !=
+ pampd_remote_node((void *)(obj->extra)))
+ BUG();
+ ret = 0;
+ }
+ return ret;
+}
+
+void *ramster_pampd_free(void *pampd, struct tmem_pool *pool,
+ struct tmem_oid *oid, uint32_t index, bool acct)
+{
+ bool eph = is_ephemeral(pool);
+ void *local_pampd = NULL;
+ int c;
+
+ BUG_ON(preemptible());
+ BUG_ON(!pampd_is_remote(pampd));
+ WARN_ON(acct == false);
+ if (oid == NULL) {
+ /*
+ * a NULL oid means to ignore this pampd free
+ * as the remote freeing will be handled elsewhere
+ */
+ } else if (eph) {
+ /* FIXME remote flush optional but probably good idea */
+ } else if (pampd_is_intransit(pampd)) {
+ /* did a pers remote get_and_free, so just free local */
+ local_pampd = pampd_mask_intransit_and_remote(pampd);
+ } else {
+ struct flushlist_node *flnode =
+ ramster_flnode_alloc(pool);
+
+ flnode->xh.client_id = pampd_remote_node(pampd);
+ flnode->xh.pool_id = pool->pool_id;
+ flnode->xh.oid = *oid;
+ flnode->xh.index = index;
+ flnode->rem_op.op = RAMSTER_REMOTIFY_FLUSH_PAGE;
+ spin_lock(&ramster_rem_op_list_lock);
+ list_add(&flnode->rem_op.list, &ramster_rem_op_list);
+ spin_unlock(&ramster_rem_op_list_lock);
+ c = atomic_dec_return(&ramster_remote_pers_pages);
+ WARN_ON_ONCE(c < 0);
+ }
+ return local_pampd;
+}
+
+void ramster_count_foreign_pages(bool eph, int count)
+{
+ int c;
+
+ BUG_ON(count != 1 && count != -1);
+ if (eph) {
+ if (count > 0) {
+ c = atomic_inc_return(
+ &ramster_foreign_eph_pages_atomic);
+ if (c > ramster_foreign_eph_pages_max)
+ ramster_foreign_eph_pages_max = c;
+ } else {
+ c = atomic_dec_return(&ramster_foreign_eph_pages_atomic);
+ WARN_ON_ONCE(c < 0);
+ }
+ ramster_foreign_eph_pages = c;
+ } else {
+ if (count > 0) {
+ c = atomic_inc_return(
+ &ramster_foreign_pers_pages_atomic);
+ if (c > ramster_foreign_pers_pages_max)
+ ramster_foreign_pers_pages_max = c;
+ } else {
+ c = atomic_dec_return(
+ &ramster_foreign_pers_pages_atomic);
+ WARN_ON_ONCE(c < 0);
+ }
+ ramster_foreign_pers_pages = c;
+ }
+}
+
+/*
+ * For now, just push over a few pages every few seconds to
+ * ensure that it basically works
+ */
+static struct workqueue_struct *ramster_remotify_workqueue;
+static void ramster_remotify_process(struct work_struct *work);
+static DECLARE_DELAYED_WORK(ramster_remotify_worker,
+ ramster_remotify_process);
+
+static void ramster_remotify_queue_delayed_work(unsigned long delay)
+{
+ if (!queue_delayed_work(ramster_remotify_workqueue,
+ &ramster_remotify_worker, delay))
+ pr_err("ramster_remotify: bad workqueue\n");
+}
+
+static void ramster_remote_flush_page(struct flushlist_node *flnode)
+{
+ struct tmem_xhandle *xh;
+ int remotenode, ret;
+
+ preempt_disable();
+ xh = &flnode->xh;
+ remotenode = flnode->xh.client_id;
+ ret = r2net_remote_flush(xh, remotenode);
+ if (ret >= 0)
+ ramster_remote_pages_flushed++;
+ else
+ ramster_remote_page_flushes_failed++;
+ preempt_enable_no_resched();
+ ramster_flnode_free(flnode, NULL);
+}
+
+static void ramster_remote_flush_object(struct flushlist_node *flnode)
+{
+ struct tmem_xhandle *xh;
+ int remotenode, ret;
+
+ preempt_disable();
+ xh = &flnode->xh;
+ remotenode = flnode->xh.client_id;
+ ret = r2net_remote_flush_object(xh, remotenode);
+ if (ret >= 0)
+ ramster_remote_objects_flushed++;
+ else
+ ramster_remote_object_flushes_failed++;
+ preempt_enable_no_resched();
+ ramster_flnode_free(flnode, NULL);
+}
+
+int ramster_remotify_pageframe(bool eph)
+{
+ struct tmem_xhandle xh;
+ unsigned int size;
+ int remotenode, ret, zbuds;
+ struct tmem_pool *pool;
+ unsigned long flags;
+ unsigned char cksum;
+ char *p;
+ int i, j;
+ unsigned char *tmpmem[2];
+ struct tmem_handle th[2];
+ unsigned int zsize[2];
+
+ tmpmem[0] = __get_cpu_var(ramster_remoteputmem1);
+ tmpmem[1] = __get_cpu_var(ramster_remoteputmem2);
+ local_bh_disable();
+ zbuds = zbud_make_zombie_lru(&th[0], &tmpmem[0], &zsize[0], eph);
+ /* now OK to release lock set in caller */
+ local_bh_enable();
+ if (zbuds == 0)
+ goto out;
+ BUG_ON(zbuds > 2);
+ for (i = 0; i < zbuds; i++) {
+ xh.client_id = th[i].client_id;
+ xh.pool_id = th[i].pool_id;
+ xh.oid = th[i].oid;
+ xh.index = th[i].index;
+ size = zsize[i];
+ BUG_ON(size == 0 || size > zbud_max_buddy_size());
+ for (p = tmpmem[i], cksum = 0, j = 0; j < size; j++)
+ cksum += *p++;
+ ret = r2net_remote_put(&xh, tmpmem[i], size, eph, &remotenode);
+ if (ret != 0) {
+ /*
+ * This is some form of a memory leak... if the remote put
+ * fails, there will never be another attempt to remotify
+ * this page. But since we've dropped the zv pointer,
+ * the page may have been freed or the data replaced
+ * so we can't just "put it back" in the remote op list.
+ * Even if we could, not sure where to put it in the list
+ * because there may be flushes that must be strictly
+ * ordered vs the put. So leave this as a FIXME for now.
+ * But count them so we know if it becomes a problem.
+ */
+ if (eph)
+ ramster_eph_pages_remote_failed++;
+ else
+ ramster_pers_pages_remote_failed++;
+ break;
+ } else {
+ if (!eph)
+ atomic_inc(&ramster_remote_pers_pages);
+ }
+ if (eph)
+ ramster_eph_pages_remoted++;
+ else
+ ramster_pers_pages_remoted++;
+ /*
+ * data was successfully remoted so change the local version to
+ * point to the remote node where it landed
+ */
+ local_bh_disable();
+ pool = zcache_get_pool_by_id(LOCAL_CLIENT, xh.pool_id);
+ local_irq_save(flags);
+ (void)tmem_replace(pool, &xh.oid, xh.index,
+ pampd_make_remote(remotenode, size, cksum));
+ local_irq_restore(flags);
+ zcache_put_pool(pool);
+ local_bh_enable();
+ }
+out:
+ return zbuds;
+}
+
+static void zcache_do_remotify_flushes(void)
+{
+ struct ramster_remotify_hdr *rem_op;
+ union remotify_list_node *u;
+
+ while (1) {
+ spin_lock(&ramster_rem_op_list_lock);
+ if (list_empty(&ramster_rem_op_list)) {
+ spin_unlock(&ramster_rem_op_list_lock);
+ goto out;
+ }
+ rem_op = list_first_entry(&ramster_rem_op_list,
+ struct ramster_remotify_hdr, list);
+ list_del_init(&rem_op->list);
+ spin_unlock(&ramster_rem_op_list_lock);
+ u = (union remotify_list_node *)rem_op;
+ switch (rem_op->op) {
+ case RAMSTER_REMOTIFY_FLUSH_PAGE:
+ ramster_remote_flush_page((struct flushlist_node *)u);
+ break;
+ case RAMSTER_REMOTIFY_FLUSH_OBJ:
+ ramster_remote_flush_object((struct flushlist_node *)u);
+ break;
+ default:
+ BUG();
+ }
+ }
+out:
+ return;
+}
+
+static void ramster_remotify_process(struct work_struct *work)
+{
+ static bool remotify_in_progress;
+ int i;
+
+ BUG_ON(irqs_disabled());
+ if (remotify_in_progress)
+ goto requeue;
+ if (ramster_remote_target_nodenum == -1)
+ goto requeue;
+ remotify_in_progress = true;
+ if (use_cleancache && ramster_eph_remotify_enable) {
+ for (i = 0; i < 100; i++) {
+ zcache_do_remotify_flushes();
+ (void)ramster_remotify_pageframe(true);
+ }
+ }
+ if (use_frontswap && ramster_pers_remotify_enable) {
+ for (i = 0; i < 100; i++) {
+ zcache_do_remotify_flushes();
+ (void)ramster_remotify_pageframe(false);
+ }
+ }
+ remotify_in_progress = false;
+requeue:
+ ramster_remotify_queue_delayed_work(HZ);
+}
+
+void __init ramster_remotify_init(void)
+{
+ unsigned long n = 60UL;
+ ramster_remotify_workqueue =
+ create_singlethread_workqueue("ramster_remotify");
+ ramster_remotify_queue_delayed_work(n * HZ);
+}
+
+static ssize_t ramster_manual_node_up_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ int i;
+ char *p = buf;
+ for (i = 0; i < MANUAL_NODES; i++)
+ if (ramster_nodes_manual_up[i])
+ p += sprintf(p, "%d ", i);
+ p += sprintf(p, "\n");
+ return p - buf;
+}
+
+static ssize_t ramster_manual_node_up_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ int err;
+ unsigned long node_num;
+
+ err = kstrtoul(buf, 10, &node_num);
+ if (err) {
+ pr_err("ramster: bad strtoul?\n");
+ return -EINVAL;
+ }
+ if (node_num >= MANUAL_NODES) {
+ pr_err("ramster: bad node_num=%lu?\n", node_num);
+ return -EINVAL;
+ }
+ if (ramster_nodes_manual_up[node_num]) {
+ pr_err("ramster: node %d already up, ignoring\n",
+ (int)node_num);
+ } else {
+ ramster_nodes_manual_up[node_num] = true;
+ r2net_hb_node_up_manual((int)node_num);
+ }
+ return count;
+}
+
+static struct kobj_attribute ramster_manual_node_up_attr = {
+ .attr = { .name = "manual_node_up", .mode = 0644 },
+ .show = ramster_manual_node_up_show,
+ .store = ramster_manual_node_up_store,
+};
+
+static ssize_t ramster_remote_target_nodenum_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ if (ramster_remote_target_nodenum == -1UL)
+ return sprintf(buf, "unset\n");
+ else
+ return sprintf(buf, "%d\n", ramster_remote_target_nodenum);
+}
+
+static ssize_t ramster_remote_target_nodenum_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ int err;
+ unsigned long node_num;
+
+ err = kstrtoul(buf, 10, &node_num);
+ if (err) {
+ pr_err("ramster: bad strtoul?\n");
+ return -EINVAL;
+ } else if (node_num == -1UL) {
+ pr_err("ramster: disabling all remotification, "
+ "data may still reside on remote nodes however\n");
+ return -EINVAL;
+ } else if (node_num >= MANUAL_NODES) {
+ pr_err("ramster: bad node_num=%lu?\n", node_num);
+ return -EINVAL;
+ } else if (!ramster_nodes_manual_up[node_num]) {
+ pr_err("ramster: node %d not up, ignoring setting "
+ "of remotification target\n", (int)node_num);
+ } else if (r2net_remote_target_node_set((int)node_num) >= 0) {
+ pr_info("ramster: node %d set as remotification target\n",
+ (int)node_num);
+ ramster_remote_target_nodenum = (int)node_num;
+ } else {
+ pr_err("ramster: bad num to node node_num=%d?\n",
+ (int)node_num);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static struct kobj_attribute ramster_remote_target_nodenum_attr = {
+ .attr = { .name = "remote_target_nodenum", .mode = 0644 },
+ .show = ramster_remote_target_nodenum_show,
+ .store = ramster_remote_target_nodenum_store,
+};
+
+#define RAMSTER_SYSFS_RO(_name) \
+ static ssize_t ramster_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+ { \
+ return sprintf(buf, "%lu\n", ramster_##_name); \
+ } \
+ static struct kobj_attribute ramster_##_name##_attr = { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .show = ramster_##_name##_show, \
+ }
+
+#define RAMSTER_SYSFS_RW(_name) \
+ static ssize_t ramster_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+ { \
+ return sprintf(buf, "%lu\n", ramster_##_name); \
+ } \
+ static ssize_t ramster_##_name##_store(struct kobject *kobj, \
+ struct kobj_attribute *attr, const char *buf, size_t count) \
+ { \
+ int err; \
+ unsigned long enable; \
+ err = kstrtoul(buf, 10, &enable); \
+ if (err) \
+ return -EINVAL; \
+ ramster_##_name = enable; \
+ return count; \
+ } \
+ static struct kobj_attribute ramster_##_name##_attr = { \
+ .attr = { .name = __stringify(_name), .mode = 0644 }, \
+ .show = ramster_##_name##_show, \
+ .store = ramster_##_name##_store, \
+ }
+
+#define RAMSTER_SYSFS_RO_ATOMIC(_name) \
+ static ssize_t ramster_##_name##_show(struct kobject *kobj, \
+ struct kobj_attribute *attr, char *buf) \
+ { \
+ return sprintf(buf, "%d\n", atomic_read(&ramster_##_name)); \
+ } \
+ static struct kobj_attribute ramster_##_name##_attr = { \
+ .attr = { .name = __stringify(_name), .mode = 0444 }, \
+ .show = ramster_##_name##_show, \
+ }
+
+RAMSTER_SYSFS_RO(interface_revision);
+RAMSTER_SYSFS_RO_ATOMIC(remote_pers_pages);
+RAMSTER_SYSFS_RW(pers_remotify_enable);
+RAMSTER_SYSFS_RW(eph_remotify_enable);
+
+static struct attribute *ramster_attrs[] = {
+ &ramster_interface_revision_attr.attr,
+ &ramster_remote_pers_pages_attr.attr,
+ &ramster_manual_node_up_attr.attr,
+ &ramster_remote_target_nodenum_attr.attr,
+ &ramster_pers_remotify_enable_attr.attr,
+ &ramster_eph_remotify_enable_attr.attr,
+ NULL,
+};
+
+static struct attribute_group ramster_attr_group = {
+ .attrs = ramster_attrs,
+ .name = "ramster",
+};
+
+/*
+ * frontswap selfshrinking
+ */
+
+/* In HZ, controls frequency of worker invocation. */
+static unsigned int selfshrink_interval __read_mostly = 5;
+/* Enable/disable with sysfs. */
+static bool frontswap_selfshrinking __read_mostly;
+
+static void selfshrink_process(struct work_struct *work);
+static DECLARE_DELAYED_WORK(selfshrink_worker, selfshrink_process);
+
+/* Enable/disable with kernel boot option. */
+static bool use_frontswap_selfshrink __initdata = true;
+
+/*
+ * The default values for the following parameters were deemed reasonable
+ * by experimentation, may be workload-dependent, and can all be
+ * adjusted via sysfs.
+ */
+
+/* Control rate for frontswap shrinking. Higher hysteresis is slower. */
+static unsigned int frontswap_hysteresis __read_mostly = 20;
+
+/*
+ * Number of selfshrink worker invocations to wait before observing that
+ * frontswap selfshrinking should commence. Note that selfshrinking does
+ * not use a separate worker thread.
+ */
+static unsigned int frontswap_inertia __read_mostly = 3;
+
+/* Countdown to next invocation of frontswap_shrink() */
+static unsigned long frontswap_inertia_counter;
+
+/*
+ * Invoked by the selfshrink worker thread, uses current number of pages
+ * in frontswap (frontswap_curr_pages()), previous status, and control
+ * values (hysteresis and inertia) to determine if frontswap should be
+ * shrunk and what the new frontswap size should be. Note that
+ * frontswap_shrink is essentially a partial swapoff that immediately
+ * transfers pages from the "swap device" (frontswap) back into kernel
+ * RAM; despite the name, frontswap "shrinking" is very different from
+ * the "shrinker" interface used by the kernel MM subsystem to reclaim
+ * memory.
+ */
+static void frontswap_selfshrink(void)
+{
+ static unsigned long cur_frontswap_pages;
+ static unsigned long last_frontswap_pages;
+ static unsigned long tgt_frontswap_pages;
+
+ last_frontswap_pages = cur_frontswap_pages;
+ cur_frontswap_pages = frontswap_curr_pages();
+ if (!cur_frontswap_pages ||
+ (cur_frontswap_pages > last_frontswap_pages)) {
+ frontswap_inertia_counter = frontswap_inertia;
+ return;
+ }
+ if (frontswap_inertia_counter && --frontswap_inertia_counter)
+ return;
+ if (cur_frontswap_pages <= frontswap_hysteresis)
+ tgt_frontswap_pages = 0;
+ else
+ tgt_frontswap_pages = cur_frontswap_pages -
+ (cur_frontswap_pages / frontswap_hysteresis);
+ frontswap_shrink(tgt_frontswap_pages);
+}
+
+static int __init ramster_nofrontswap_selfshrink_setup(char *s)
+{
+ use_frontswap_selfshrink = false;
+ return 1;
+}
+
+__setup("noselfshrink", ramster_nofrontswap_selfshrink_setup);
+
+static void selfshrink_process(struct work_struct *work)
+{
+ if (frontswap_selfshrinking && frontswap_enabled) {
+ frontswap_selfshrink();
+ schedule_delayed_work(&selfshrink_worker,
+ selfshrink_interval * HZ);
+ }
+}
+
+void ramster_cpu_up(int cpu)
+{
+ unsigned char *p1 = kzalloc(PAGE_SIZE, GFP_KERNEL | __GFP_REPEAT);
+ unsigned char *p2 = kzalloc(PAGE_SIZE, GFP_KERNEL | __GFP_REPEAT);
+ BUG_ON(!p1 || !p2);
+ per_cpu(ramster_remoteputmem1, cpu) = p1;
+ per_cpu(ramster_remoteputmem2, cpu) = p2;
+}
+
+void ramster_cpu_down(int cpu)
+{
+ struct ramster_preload *kp;
+
+ kfree(per_cpu(ramster_remoteputmem1, cpu));
+ per_cpu(ramster_remoteputmem1, cpu) = NULL;
+ kfree(per_cpu(ramster_remoteputmem2, cpu));
+ per_cpu(ramster_remoteputmem2, cpu) = NULL;
+ kp = &per_cpu(ramster_preloads, cpu);
+ if (kp->flnode) {
+ kmem_cache_free(ramster_flnode_cache, kp->flnode);
+ kp->flnode = NULL;
+ }
+}
+
+void ramster_register_pamops(struct tmem_pamops *pamops)
+{
+ pamops->free_obj = ramster_pampd_free_obj;
+ pamops->new_obj = ramster_pampd_new_obj;
+ pamops->replace_in_obj = ramster_pampd_replace_in_obj;
+ pamops->is_remote = ramster_pampd_is_remote;
+ pamops->repatriate = ramster_pampd_repatriate;
+ pamops->repatriate_preload = ramster_pampd_repatriate_preload;
+}
+
+void __init ramster_init(bool cleancache, bool frontswap,
+ bool frontswap_exclusive_gets)
+{
+ int ret = 0;
+
+ if (cleancache)
+ use_cleancache = true;
+ if (frontswap)
+ use_frontswap = true;
+ if (frontswap_exclusive_gets)
+ use_frontswap_exclusive_gets = true;
+ ramster_debugfs_init();
+ ret = sysfs_create_group(mm_kobj, &ramster_attr_group);
+ if (ret)
+ pr_err("ramster: can't create sysfs for ramster\n");
+ (void)r2net_register_handlers();
+ INIT_LIST_HEAD(&ramster_rem_op_list);
+ ramster_flnode_cache = kmem_cache_create("ramster_flnode",
+ sizeof(struct flushlist_node), 0, 0, NULL);
+ frontswap_selfshrinking = use_frontswap_selfshrink;
+ if (frontswap_selfshrinking) {
+ pr_info("ramster: Initializing frontswap selfshrink driver.\n");
+ schedule_delayed_work(&selfshrink_worker,
+ selfshrink_interval * HZ);
+ }
+ ramster_remotify_init();
+}
diff --git a/drivers/staging/ramster/ramster/ramster.h b/drivers/staging/ramster/ramster/ramster.h
new file mode 100644
index 000000000000..12ae56f09ca4
--- /dev/null
+++ b/drivers/staging/ramster/ramster/ramster.h
@@ -0,0 +1,161 @@
+/*
+ * ramster.h
+ *
+ * Peer-to-peer transcendent memory
+ *
+ * Copyright (c) 2009-2012, Dan Magenheimer, Oracle Corp.
+ */
+
+#ifndef _RAMSTER_RAMSTER_H_
+#define _RAMSTER_RAMSTER_H_
+
+#include "../tmem.h"
+
+enum ramster_remotify_op {
+ RAMSTER_REMOTIFY_FLUSH_PAGE,
+ RAMSTER_REMOTIFY_FLUSH_OBJ,
+};
+
+struct ramster_remotify_hdr {
+ enum ramster_remotify_op op;
+ struct list_head list;
+};
+
+struct flushlist_node {
+ struct ramster_remotify_hdr rem_op;
+ struct tmem_xhandle xh;
+};
+
+struct ramster_preload {
+ struct flushlist_node *flnode;
+};
+
+union remotify_list_node {
+ struct ramster_remotify_hdr rem_op;
+ struct {
+ struct ramster_remotify_hdr rem_op;
+ struct tmem_handle th;
+ } zbud_hdr;
+ struct flushlist_node flist;
+};
+
+/*
+ * format of remote pampd:
+ * bit 0 is reserved for zbud (in-page buddy selection)
+ * bit 1 == intransit
+ * bit 2 == is_remote... if this bit is set, then
+ * bit 3-10 == remotenode
+ * bit 11-23 == size
+ * bit 24-31 == cksum
+ */
+#define FAKE_PAMPD_INTRANSIT_BITS 1
+#define FAKE_PAMPD_ISREMOTE_BITS 1
+#define FAKE_PAMPD_REMOTENODE_BITS 8
+#define FAKE_PAMPD_REMOTESIZE_BITS 13
+#define FAKE_PAMPD_CHECKSUM_BITS 8
+
+#define FAKE_PAMPD_INTRANSIT_SHIFT 1
+#define FAKE_PAMPD_ISREMOTE_SHIFT (FAKE_PAMPD_INTRANSIT_SHIFT + \
+ FAKE_PAMPD_INTRANSIT_BITS)
+#define FAKE_PAMPD_REMOTENODE_SHIFT (FAKE_PAMPD_ISREMOTE_SHIFT + \
+ FAKE_PAMPD_ISREMOTE_BITS)
+#define FAKE_PAMPD_REMOTESIZE_SHIFT (FAKE_PAMPD_REMOTENODE_SHIFT + \
+ FAKE_PAMPD_REMOTENODE_BITS)
+#define FAKE_PAMPD_CHECKSUM_SHIFT (FAKE_PAMPD_REMOTESIZE_SHIFT + \
+ FAKE_PAMPD_REMOTESIZE_BITS)
+
+#define FAKE_PAMPD_MASK(x) ((1UL << (x)) - 1)
+
+static inline void *pampd_make_remote(int remotenode, size_t size,
+ unsigned char cksum)
+{
+ unsigned long fake_pampd = 0;
+ fake_pampd |= 1UL << FAKE_PAMPD_ISREMOTE_SHIFT;
+ fake_pampd |= ((unsigned long)remotenode &
+ FAKE_PAMPD_MASK(FAKE_PAMPD_REMOTENODE_BITS)) <<
+ FAKE_PAMPD_REMOTENODE_SHIFT;
+ fake_pampd |= ((unsigned long)size &
+ FAKE_PAMPD_MASK(FAKE_PAMPD_REMOTESIZE_BITS)) <<
+ FAKE_PAMPD_REMOTESIZE_SHIFT;
+ fake_pampd |= ((unsigned long)cksum &
+ FAKE_PAMPD_MASK(FAKE_PAMPD_CHECKSUM_BITS)) <<
+ FAKE_PAMPD_CHECKSUM_SHIFT;
+ return (void *)fake_pampd;
+}
+
+static inline unsigned int pampd_remote_node(void *pampd)
+{
+ unsigned long fake_pampd = (unsigned long)pampd;
+ return (fake_pampd >> FAKE_PAMPD_REMOTENODE_SHIFT) &
+ FAKE_PAMPD_MASK(FAKE_PAMPD_REMOTENODE_BITS);
+}
+
+static inline unsigned int pampd_remote_size(void *pampd)
+{
+ unsigned long fake_pampd = (unsigned long)pampd;
+ return (fake_pampd >> FAKE_PAMPD_REMOTESIZE_SHIFT) &
+ FAKE_PAMPD_MASK(FAKE_PAMPD_REMOTESIZE_BITS);
+}
+
+static inline unsigned char pampd_remote_cksum(void *pampd)
+{
+ unsigned long fake_pampd = (unsigned long)pampd;
+ return (fake_pampd >> FAKE_PAMPD_CHECKSUM_SHIFT) &
+ FAKE_PAMPD_MASK(FAKE_PAMPD_CHECKSUM_BITS);
+}
+
+static inline bool pampd_is_remote(void *pampd)
+{
+ unsigned long fake_pampd = (unsigned long)pampd;
+ return (fake_pampd >> FAKE_PAMPD_ISREMOTE_SHIFT) &
+ FAKE_PAMPD_MASK(FAKE_PAMPD_ISREMOTE_BITS);
+}
+
+static inline bool pampd_is_intransit(void *pampd)
+{
+ unsigned long fake_pampd = (unsigned long)pampd;
+ return (fake_pampd >> FAKE_PAMPD_INTRANSIT_SHIFT) &
+ FAKE_PAMPD_MASK(FAKE_PAMPD_INTRANSIT_BITS);
+}
+
+/* note that it is a BUG for intransit to be set without isremote also set */
+static inline void *pampd_mark_intransit(void *pampd)
+{
+ unsigned long fake_pampd = (unsigned long)pampd;
+
+ fake_pampd |= 1UL << FAKE_PAMPD_ISREMOTE_SHIFT;
+ fake_pampd |= 1UL << FAKE_PAMPD_INTRANSIT_SHIFT;
+ return (void *)fake_pampd;
+}
+
+static inline void *pampd_mask_intransit_and_remote(void *marked_pampd)
+{
+ unsigned long pampd = (unsigned long)marked_pampd;
+
+ pampd &= ~(1UL << FAKE_PAMPD_INTRANSIT_SHIFT);
+ pampd &= ~(1UL << FAKE_PAMPD_ISREMOTE_SHIFT);
+ return (void *)pampd;
+}
+
+extern int r2net_remote_async_get(struct tmem_xhandle *,
+ bool, int, size_t, uint8_t, void *extra);
+extern int r2net_remote_put(struct tmem_xhandle *, char *, size_t,
+ bool, int *);
+extern int r2net_remote_flush(struct tmem_xhandle *, int);
+extern int r2net_remote_flush_object(struct tmem_xhandle *, int);
+extern int r2net_register_handlers(void);
+extern int r2net_remote_target_node_set(int);
+
+extern int ramster_remotify_pageframe(bool);
+extern void ramster_init(bool, bool, bool);
+extern void ramster_register_pamops(struct tmem_pamops *);
+extern int ramster_localify(int, struct tmem_oid *oidp, uint32_t, char *,
+ unsigned int, void *);
+extern void *ramster_pampd_free(void *, struct tmem_pool *, struct tmem_oid *,
+ uint32_t, bool);
+extern void ramster_count_foreign_pages(bool, int);
+extern int ramster_do_preload_flnode(struct tmem_pool *);
+extern void ramster_cpu_up(int);
+extern void ramster_cpu_down(int);
+
+#endif /* _RAMSTER_RAMSTER_H */
diff --git a/drivers/staging/ramster/cluster/ramster_nodemanager.h b/drivers/staging/ramster/ramster/ramster_nodemanager.h
index 49f879d943ab..49f879d943ab 100644
--- a/drivers/staging/ramster/cluster/ramster_nodemanager.h
+++ b/drivers/staging/ramster/ramster/ramster_nodemanager.h
diff --git a/drivers/staging/ramster/cluster/tcp.c b/drivers/staging/ramster/ramster/tcp.c
index d0a07d722b61..aa2a1a763aa4 100644
--- a/drivers/staging/ramster/cluster/tcp.c
+++ b/drivers/staging/ramster/ramster/tcp.c
@@ -541,8 +541,7 @@ static void r2net_set_nn_state(struct r2net_node *nn,
}
if (was_valid && !valid) {
- printk(KERN_NOTICE "ramster: No longer connected to "
- SC_NODEF_FMT "\n",
+ pr_notice("ramster: No longer connected to " SC_NODEF_FMT "\n",
old_sc->sc_node->nd_name, old_sc->sc_node->nd_num,
&old_sc->sc_node->nd_ipv4_address,
ntohs(old_sc->sc_node->nd_ipv4_port));
@@ -551,7 +550,7 @@ static void r2net_set_nn_state(struct r2net_node *nn,
if (!was_valid && valid) {
cancel_delayed_work(&nn->nn_connect_expired);
- printk(KERN_NOTICE "ramster: %s " SC_NODEF_FMT "\n",
+ pr_notice("ramster: %s " SC_NODEF_FMT "\n",
r2nm_this_node() > sc->sc_node->nd_num ?
"Connected to" : "Accepted connection from",
sc->sc_node->nd_name, sc->sc_node->nd_num,
@@ -644,7 +643,7 @@ static void r2net_state_change(struct sock *sk)
r2net_sc_queue_work(sc, &sc->sc_connect_work);
break;
default:
- printk(KERN_INFO "ramster: Connection to "
+ pr_info("ramster: Connection to "
SC_NODEF_FMT " shutdown, state %d\n",
sc->sc_node->nd_name, sc->sc_node->nd_num,
&sc->sc_node->nd_ipv4_address,
@@ -1160,7 +1159,8 @@ int r2net_send_message_vec(u32 msg_type, u32 key, struct kvec *caller_vec,
/* wait on other node's handler */
r2net_set_nst_status_time(&nst);
- wait_event(nsw.ns_wq, r2net_nsw_completed(nn, &nsw));
+ wait_event(nsw.ns_wq, r2net_nsw_completed(nn, &nsw) ||
+ nn->nn_persistent_error || !nn->nn_sc_valid);
r2net_update_send_stats(&nst, sc);
@@ -1325,8 +1325,10 @@ static int r2net_process_message(struct r2net_sock_container *sc,
if (be16_to_cpu(hdr->data_len) > nmh->nh_max_len)
syserr = R2NET_ERR_OVERFLOW;
- if (syserr != R2NET_ERR_NONE)
+ if (syserr != R2NET_ERR_NONE) {
+ pr_err("ramster_r2net, message length problem\n");
goto out_respond;
+ }
r2net_set_func_start_time(sc);
sc->sc_msg_key = be32_to_cpu(hdr->key);
@@ -1393,7 +1395,7 @@ static int r2net_check_handshake(struct r2net_sock_container *sc)
struct r2net_node *nn = r2net_nn_from_num(sc->sc_node->nd_num);
if (hand->protocol_version != cpu_to_be64(R2NET_PROTOCOL_VERSION)) {
- printk(KERN_NOTICE "ramster: " SC_NODEF_FMT " Advertised net "
+ pr_notice("ramster: " SC_NODEF_FMT " Advertised net "
"protocol version %llu but %llu is required. "
"Disconnecting.\n", sc->sc_node->nd_name,
sc->sc_node->nd_num, &sc->sc_node->nd_ipv4_address,
@@ -1413,7 +1415,7 @@ static int r2net_check_handshake(struct r2net_sock_container *sc)
*/
if (be32_to_cpu(hand->r2net_idle_timeout_ms) !=
r2net_idle_timeout()) {
- printk(KERN_NOTICE "ramster: " SC_NODEF_FMT " uses a network "
+ pr_notice("ramster: " SC_NODEF_FMT " uses a network "
"idle timeout of %u ms, but we use %u ms locally. "
"Disconnecting.\n", sc->sc_node->nd_name,
sc->sc_node->nd_num, &sc->sc_node->nd_ipv4_address,
@@ -1426,7 +1428,7 @@ static int r2net_check_handshake(struct r2net_sock_container *sc)
if (be32_to_cpu(hand->r2net_keepalive_delay_ms) !=
r2net_keepalive_delay()) {
- printk(KERN_NOTICE "ramster: " SC_NODEF_FMT " uses a keepalive "
+ pr_notice("ramster: " SC_NODEF_FMT " uses a keepalive "
"delay of %u ms, but we use %u ms locally. "
"Disconnecting.\n", sc->sc_node->nd_name,
sc->sc_node->nd_num, &sc->sc_node->nd_ipv4_address,
@@ -1439,7 +1441,7 @@ static int r2net_check_handshake(struct r2net_sock_container *sc)
if (be32_to_cpu(hand->r2hb_heartbeat_timeout_ms) !=
R2HB_MAX_WRITE_TIMEOUT_MS) {
- printk(KERN_NOTICE "ramster: " SC_NODEF_FMT " uses a heartbeat "
+ pr_notice("ramster: " SC_NODEF_FMT " uses a heartbeat "
"timeout of %u ms, but we use %u ms locally. "
"Disconnecting.\n", sc->sc_node->nd_name,
sc->sc_node->nd_num, &sc->sc_node->nd_ipv4_address,
@@ -1516,6 +1518,7 @@ static int r2net_advance_rx(struct r2net_sock_container *sc)
if (be16_to_cpu(hdr->data_len) >
R2NET_MAX_PAYLOAD_BYTES)
ret = -EOVERFLOW;
+ WARN_ON_ONCE(ret == -EOVERFLOW);
}
}
if (ret <= 0)
@@ -1583,7 +1586,6 @@ static void r2net_rx_until_empty(struct work_struct *work)
/* not permanent so read failed handshake can retry */
r2net_ensure_shutdown(nn, sc, 0);
}
-
sc_put(sc);
}
@@ -1659,6 +1661,7 @@ static void r2net_sc_send_keep_req(struct work_struct *work)
static void r2net_idle_timer(unsigned long data)
{
struct r2net_sock_container *sc = (struct r2net_sock_container *)data;
+ struct r2net_node *nn = r2net_nn_from_num(sc->sc_node->nd_num);
#ifdef CONFIG_DEBUG_FS
unsigned long msecs = ktime_to_ms(ktime_get()) -
ktime_to_ms(sc->sc_tv_timer);
@@ -1666,7 +1669,7 @@ static void r2net_idle_timer(unsigned long data)
unsigned long msecs = r2net_idle_timeout();
#endif
- printk(KERN_NOTICE "ramster: Connection to " SC_NODEF_FMT " has been "
+ pr_notice("ramster: Connection to " SC_NODEF_FMT " has been "
"idle for %lu.%lu secs, shutting it down.\n",
sc->sc_node->nd_name, sc->sc_node->nd_num,
&sc->sc_node->nd_ipv4_address, ntohs(sc->sc_node->nd_ipv4_port),
@@ -1676,13 +1679,8 @@ static void r2net_idle_timer(unsigned long data)
* Initialize the nn_timeout so that the next connection attempt
* will continue in r2net_start_connect.
*/
- /* Avoid spurious shutdowns... not sure if this is still necessary */
- pr_err("ramster_idle_timer, skipping shutdown work\n");
-#if 0
- /* old code used to do these two lines */
atomic_set(&nn->nn_timeout, 1);
r2net_sc_queue_work(sc, &sc->sc_shutdown_work);
-#endif
}
static void r2net_sc_reset_idle_timer(struct r2net_sock_container *sc)
@@ -1807,7 +1805,7 @@ static void r2net_start_connect(struct work_struct *work)
out:
if (ret) {
- printk(KERN_NOTICE "ramster: Connect attempt to " SC_NODEF_FMT
+ pr_notice("ramster: Connect attempt to " SC_NODEF_FMT
" failed with errno %d\n", sc->sc_node->nd_name,
sc->sc_node->nd_num, &sc->sc_node->nd_ipv4_address,
ntohs(sc->sc_node->nd_ipv4_port), ret);
@@ -1833,7 +1831,7 @@ static void r2net_connect_expired(struct work_struct *work)
spin_lock(&nn->nn_lock);
if (!nn->nn_sc_valid) {
- printk(KERN_NOTICE "ramster: No connection established with "
+ pr_notice("ramster: No connection established with "
"node %u after %u.%u seconds, giving up.\n",
r2net_num_from_nn(nn),
r2net_idle_timeout() / 1000,
@@ -1969,7 +1967,7 @@ static int r2net_accept_one(struct socket *sock)
node = r2nm_get_node_by_ip(sin.sin_addr.s_addr);
if (node == NULL) {
- printk(KERN_NOTICE "ramster: Attempt to connect from unknown "
+ pr_notice("ramster: Attempt to connect from unknown "
"node at %pI4:%d\n", &sin.sin_addr.s_addr,
ntohs(sin.sin_port));
ret = -EINVAL;
@@ -1978,7 +1976,7 @@ static int r2net_accept_one(struct socket *sock)
if (r2nm_this_node() >= node->nd_num) {
local_node = r2nm_get_node_by_num(r2nm_this_node());
- printk(KERN_NOTICE "ramster: Unexpected connect attempt seen "
+ pr_notice("ramster: Unexpected connect attempt seen "
"at node '%s' (%u, %pI4:%d) from node '%s' (%u, "
"%pI4:%d)\n", local_node->nd_name, local_node->nd_num,
&(local_node->nd_ipv4_address),
@@ -2008,7 +2006,7 @@ static int r2net_accept_one(struct socket *sock)
ret = 0;
spin_unlock(&nn->nn_lock);
if (ret) {
- printk(KERN_NOTICE "ramster: Attempt to connect from node '%s' "
+ pr_notice("ramster: Attempt to connect from node '%s' "
"at %pI4:%d but it already has an open connection\n",
node->nd_name, &sin.sin_addr.s_addr,
ntohs(sin.sin_port));
@@ -2091,8 +2089,7 @@ static int r2net_open_listening_sock(__be32 addr, __be16 port)
ret = sock_create(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
if (ret < 0) {
- printk(KERN_ERR "ramster: Error %d while creating socket\n",
- ret);
+ pr_err("ramster: Error %d while creating socket\n", ret);
goto out;
}
@@ -2106,17 +2103,17 @@ static int r2net_open_listening_sock(__be32 addr, __be16 port)
r2net_listen_sock = sock;
INIT_WORK(&r2net_listen_work, r2net_accept_many);
- sock->sk->sk_reuse = SK_CAN_REUSE;
+ sock->sk->sk_reuse = /* SK_CAN_REUSE FIXME FOR 3.4 */ 1;
ret = sock->ops->bind(sock, (struct sockaddr *)&sin, sizeof(sin));
if (ret < 0) {
- printk(KERN_ERR "ramster: Error %d while binding socket at "
- "%pI4:%u\n", ret, &addr, ntohs(port));
+ pr_err("ramster: Error %d while binding socket at %pI4:%u\n",
+ ret, &addr, ntohs(port));
goto out;
}
ret = sock->ops->listen(sock, 64);
if (ret < 0)
- printk(KERN_ERR "ramster: Error %d while listening on %pI4:%u\n",
+ pr_err("ramster: Error %d while listening on %pI4:%u\n",
ret, &addr, ntohs(port));
out:
diff --git a/drivers/staging/ramster/cluster/tcp.h b/drivers/staging/ramster/ramster/tcp.h
index 9d05833452b5..9d05833452b5 100644
--- a/drivers/staging/ramster/cluster/tcp.h
+++ b/drivers/staging/ramster/ramster/tcp.h
diff --git a/drivers/staging/ramster/cluster/tcp_internal.h b/drivers/staging/ramster/ramster/tcp_internal.h
index 4d8cc9f96fd2..4d8cc9f96fd2 100644
--- a/drivers/staging/ramster/cluster/tcp_internal.h
+++ b/drivers/staging/ramster/ramster/tcp_internal.h
diff --git a/drivers/staging/ramster/tmem.c b/drivers/staging/ramster/tmem.c
index 8f2f6892d8d3..a2b7e03b6062 100644
--- a/drivers/staging/ramster/tmem.c
+++ b/drivers/staging/ramster/tmem.c
@@ -1,33 +1,43 @@
/*
* In-kernel transcendent memory (generic implementation)
*
- * Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp.
+ * Copyright (c) 2009-2012, Dan Magenheimer, Oracle Corp.
*
* The primary purpose of Transcedent Memory ("tmem") is to map object-oriented
* "handles" (triples containing a pool id, and object id, and an index), to
* pages in a page-accessible memory (PAM). Tmem references the PAM pages via
* an abstract "pampd" (PAM page-descriptor), which can be operated on by a
* set of functions (pamops). Each pampd contains some representation of
- * PAGE_SIZE bytes worth of data. Tmem must support potentially millions of
- * pages and must be able to insert, find, and delete these pages at a
- * potential frequency of thousands per second concurrently across many CPUs,
- * (and, if used with KVM, across many vcpus across many guests).
- * Tmem is tracked with a hierarchy of data structures, organized by
- * the elements in a handle-tuple: pool_id, object_id, and page index.
- * One or more "clients" (e.g. guests) each provide one or more tmem_pools.
- * Each pool, contains a hash table of rb_trees of tmem_objs. Each
- * tmem_obj contains a radix-tree-like tree of pointers, with intermediate
- * nodes called tmem_objnodes. Each leaf pointer in this tree points to
- * a pampd, which is accessible only through a small set of callbacks
- * registered by the PAM implementation (see tmem_register_pamops). Tmem
- * does all memory allocation via a set of callbacks registered by the tmem
- * host implementation (e.g. see tmem_register_hostops).
+ * PAGE_SIZE bytes worth of data. For those familiar with key-value stores,
+ * the tmem handle is a three-level hierarchical key, and the value is always
+ * reconstituted (but not necessarily stored) as PAGE_SIZE bytes and is
+ * referenced in the datastore by the pampd. The hierarchy is required
+ * to ensure that certain invalidation functions can be performed efficiently
+ * (i.e. flush all indexes associated with this object_id, or
+ * flush all objects associated with this pool).
+ *
+ * Tmem must support potentially millions of pages and must be able to insert,
+ * find, and delete these pages at a potential frequency of thousands per
+ * second concurrently across many CPUs, (and, if used with KVM, across many
+ * vcpus across many guests). Tmem is tracked with a hierarchy of data
+ * structures, organized by the elements in the handle-tuple: pool_id,
+ * object_id, and page index. One or more "clients" (e.g. guests) each
+ * provide one or more tmem_pools. Each pool, contains a hash table of
+ * rb_trees of tmem_objs. Each tmem_obj contains a radix-tree-like tree
+ * of pointers, with intermediate nodes called tmem_objnodes. Each leaf
+ * pointer in this tree points to a pampd, which is accessible only through
+ * a small set of callbacks registered by the PAM implementation (see
+ * tmem_register_pamops). Tmem only needs to memory allocation for objs
+ * and objnodes and this is done via a set of callbacks that must be
+ * registered by the tmem host implementation (e.g. see tmem_register_hostops).
*/
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/atomic.h>
+#ifdef CONFIG_RAMSTER
#include <linux/delay.h>
+#endif
#include "tmem.h"
@@ -52,7 +62,7 @@ void tmem_register_hostops(struct tmem_hostops *m)
/*
* A tmem host implementation must use this function to register
- * callbacks for a page-accessible memory (PAM) implementation
+ * callbacks for a page-accessible memory (PAM) implementation.
*/
static struct tmem_pamops tmem_pamops;
@@ -67,42 +77,62 @@ void tmem_register_pamops(struct tmem_pamops *m)
* So an rb_tree is an ideal data structure to manage tmem_objs. But because
* of the potentially huge number of tmem_objs, each pool manages a hashtable
* of rb_trees to reduce search, insert, delete, and rebalancing time.
- * Each hashbucket also has a lock to manage concurrent access.
+ * Each hashbucket also has a lock to manage concurrent access and no
+ * searches, inserts, or deletions can be performed unless the lock is held.
+ * As a result, care must be taken to ensure tmem routines are not called
+ * recursively; the vast majority of the time, a recursive call may work
+ * but a deadlock will occur a small fraction of the time due to the
+ * hashbucket lock.
*
- * The following routines manage tmem_objs. When any tmem_obj is accessed,
- * the hashbucket lock must be held.
+ * The following routines manage tmem_objs. In all of these routines,
+ * the hashbucket lock is already held.
*/
-/* searches for object==oid in pool, returns locked object if found */
-static struct tmem_obj *tmem_obj_find(struct tmem_hashbucket *hb,
- struct tmem_oid *oidp)
+/* Search for object==oid in pool, returns object if found. */
+static struct tmem_obj *__tmem_obj_find(struct tmem_hashbucket *hb,
+ struct tmem_oid *oidp,
+ struct rb_node **parent,
+ struct rb_node ***link)
{
- struct rb_node *rbnode;
- struct tmem_obj *obj;
+ struct rb_node *_parent = NULL, **rbnode;
+ struct tmem_obj *obj = NULL;
- rbnode = hb->obj_rb_root.rb_node;
- while (rbnode) {
- BUG_ON(RB_EMPTY_NODE(rbnode));
- obj = rb_entry(rbnode, struct tmem_obj, rb_tree_node);
+ rbnode = &hb->obj_rb_root.rb_node;
+ while (*rbnode) {
+ BUG_ON(RB_EMPTY_NODE(*rbnode));
+ _parent = *rbnode;
+ obj = rb_entry(*rbnode, struct tmem_obj,
+ rb_tree_node);
switch (tmem_oid_compare(oidp, &obj->oid)) {
case 0: /* equal */
goto out;
case -1:
- rbnode = rbnode->rb_left;
+ rbnode = &(*rbnode)->rb_left;
break;
case 1:
- rbnode = rbnode->rb_right;
+ rbnode = &(*rbnode)->rb_right;
break;
}
}
+
+ if (parent)
+ *parent = _parent;
+ if (link)
+ *link = rbnode;
obj = NULL;
out:
return obj;
}
-static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *);
+static struct tmem_obj *tmem_obj_find(struct tmem_hashbucket *hb,
+ struct tmem_oid *oidp)
+{
+ return __tmem_obj_find(hb, oidp, NULL, NULL);
+}
-/* free an object that has no more pampds in it */
+static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *, bool);
+
+/* Free an object that has no more pampds in it. */
static void tmem_obj_free(struct tmem_obj *obj, struct tmem_hashbucket *hb)
{
struct tmem_pool *pool;
@@ -113,7 +143,7 @@ static void tmem_obj_free(struct tmem_obj *obj, struct tmem_hashbucket *hb)
pool = obj->pool;
BUG_ON(pool == NULL);
if (obj->objnode_tree_root != NULL) /* may be "stump" with no leaves */
- tmem_pampd_destroy_all_in_obj(obj);
+ tmem_pampd_destroy_all_in_obj(obj, false);
BUG_ON(obj->objnode_tree_root != NULL);
BUG_ON((long)obj->objnode_count != 0);
atomic_dec(&pool->obj_count);
@@ -125,15 +155,14 @@ static void tmem_obj_free(struct tmem_obj *obj, struct tmem_hashbucket *hb)
}
/*
- * initialize, and insert an tmem_object_root (called only if find failed)
+ * Initialize, and insert an tmem_object_root (called only if find failed).
*/
static void tmem_obj_init(struct tmem_obj *obj, struct tmem_hashbucket *hb,
struct tmem_pool *pool,
struct tmem_oid *oidp)
{
struct rb_root *root = &hb->obj_rb_root;
- struct rb_node **new = &(root->rb_node), *parent = NULL;
- struct tmem_obj *this;
+ struct rb_node **new = NULL, *parent = NULL;
BUG_ON(pool == NULL);
atomic_inc(&pool->obj_count);
@@ -143,24 +172,15 @@ static void tmem_obj_init(struct tmem_obj *obj, struct tmem_hashbucket *hb,
obj->oid = *oidp;
obj->objnode_count = 0;
obj->pampd_count = 0;
- (*tmem_pamops.new_obj)(obj);
+#ifdef CONFIG_RAMSTER
+ if (tmem_pamops.new_obj != NULL)
+ (*tmem_pamops.new_obj)(obj);
+#endif
SET_SENTINEL(obj, OBJ);
- while (*new) {
- BUG_ON(RB_EMPTY_NODE(*new));
- this = rb_entry(*new, struct tmem_obj, rb_tree_node);
- parent = *new;
- switch (tmem_oid_compare(oidp, &this->oid)) {
- case 0:
- BUG(); /* already present; should never happen! */
- break;
- case -1:
- new = &(*new)->rb_left;
- break;
- case 1:
- new = &(*new)->rb_right;
- break;
- }
- }
+
+ if (__tmem_obj_find(hb, oidp, &parent, &new))
+ BUG();
+
rb_link_node(&obj->rb_tree_node, parent, new);
rb_insert_color(&obj->rb_tree_node, root);
}
@@ -170,7 +190,7 @@ static void tmem_obj_init(struct tmem_obj *obj, struct tmem_hashbucket *hb,
* "ephemeral" vs "persistent". These attributes apply to all tmem_objs
* and all pampds that belong to a tmem_pool. A tmem_pool is created
* or deleted relatively rarely (for example, when a filesystem is
- * mounted or unmounted.
+ * mounted or unmounted).
*/
/* flush all data from a pool and, optionally, free it */
@@ -188,7 +208,7 @@ static void tmem_pool_flush(struct tmem_pool *pool, bool destroy)
while (rbnode != NULL) {
obj = rb_entry(rbnode, struct tmem_obj, rb_tree_node);
rbnode = rb_next(rbnode);
- tmem_pampd_destroy_all_in_obj(obj);
+ tmem_pampd_destroy_all_in_obj(obj, true);
tmem_obj_free(obj, hb);
(*tmem_hostops.obj_free)(obj, pool);
}
@@ -274,7 +294,7 @@ static void tmem_objnode_free(struct tmem_objnode *objnode)
}
/*
- * lookup index in object and return associated pampd (or NULL if not found)
+ * Lookup index in object and return associated pampd (or NULL if not found).
*/
static void **__tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index)
{
@@ -316,6 +336,7 @@ static void *tmem_pampd_lookup_in_obj(struct tmem_obj *obj, uint32_t index)
return slot != NULL ? *slot : NULL;
}
+#ifdef CONFIG_RAMSTER
static void *tmem_pampd_replace_in_obj(struct tmem_obj *obj, uint32_t index,
void *new_pampd, bool no_free)
{
@@ -333,6 +354,7 @@ static void *tmem_pampd_replace_in_obj(struct tmem_obj *obj, uint32_t index,
}
return ret;
}
+#endif
static int tmem_pampd_add_to_obj(struct tmem_obj *obj, uint32_t index,
void *pampd)
@@ -470,7 +492,7 @@ out:
return slot;
}
-/* recursively walk the objnode_tree destroying pampds and objnodes */
+/* Recursively walk the objnode_tree destroying pampds and objnodes. */
static void tmem_objnode_node_destroy(struct tmem_obj *obj,
struct tmem_objnode *objnode,
unsigned int ht)
@@ -495,7 +517,8 @@ static void tmem_objnode_node_destroy(struct tmem_obj *obj,
}
}
-static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *obj)
+static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *obj,
+ bool pool_destroy)
{
if (obj->objnode_tree_root == NULL)
return;
@@ -510,7 +533,10 @@ static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *obj)
obj->objnode_tree_height = 0;
}
obj->objnode_tree_root = NULL;
- (*tmem_pamops.free_obj)(obj->pool, obj);
+#ifdef CONFIG_RAMSTER
+ if (tmem_pamops.free_obj != NULL)
+ (*tmem_pamops.free_obj)(obj->pool, obj, pool_destroy);
+#endif
}
/*
@@ -523,17 +549,16 @@ static void tmem_pampd_destroy_all_in_obj(struct tmem_obj *obj)
*/
/*
- * "Put" a page, e.g. copy a page from the kernel into newly allocated
- * PAM space (if such space is available). Tmem_put is complicated by
- * a corner case: What if a page with matching handle already exists in
- * tmem? To guarantee coherency, one of two actions is necessary: Either
- * the data for the page must be overwritten, or the page must be
- * "flushed" so that the data is not accessible to a subsequent "get".
- * Since these "duplicate puts" are relatively rare, this implementation
- * always flushes for simplicity.
+ * "Put" a page, e.g. associate the passed pampd with the passed handle.
+ * Tmem_put is complicated by a corner case: What if a page with matching
+ * handle already exists in tmem? To guarantee coherency, one of two
+ * actions is necessary: Either the data for the page must be overwritten,
+ * or the page must be "flushed" so that the data is not accessible to a
+ * subsequent "get". Since these "duplicate puts" are relatively rare,
+ * this implementation always flushes for simplicity.
*/
int tmem_put(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index,
- char *data, size_t size, bool raw, int ephemeral)
+ bool raw, void *pampd_to_use)
{
struct tmem_obj *obj = NULL, *objfound = NULL, *objnew = NULL;
void *pampd = NULL, *pampd_del = NULL;
@@ -566,19 +591,17 @@ int tmem_put(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index,
}
BUG_ON(obj == NULL);
BUG_ON(((objnew != obj) && (objfound != obj)) || (objnew == objfound));
- pampd = (*tmem_pamops.create)(data, size, raw, ephemeral,
- obj->pool, &obj->oid, index);
- if (unlikely(pampd == NULL))
- goto free;
+ pampd = pampd_to_use;
+ BUG_ON(pampd_to_use == NULL);
ret = tmem_pampd_add_to_obj(obj, index, pampd);
if (unlikely(ret == -ENOMEM))
/* may have partially built objnode tree ("stump") */
goto delete_and_free;
+ (*tmem_pamops.create_finish)(pampd, is_ephemeral(pool));
goto out;
delete_and_free:
(void)tmem_pampd_delete_from_obj(obj, index);
-free:
if (pampd)
(*tmem_pamops.free)(pampd, pool, NULL, 0, true);
if (objnew) {
@@ -590,6 +613,16 @@ out:
return ret;
}
+#ifdef CONFIG_RAMSTER
+/*
+ * For ramster only: The following routines provide a two-step sequence
+ * to allow the caller to replace a pampd in the tmem data structures with
+ * another pampd. Here, we lookup the passed handle and, if found, return the
+ * associated pampd and object, leaving the hashbucket locked and returning
+ * a reference to it. The caller is expected to immediately call the
+ * matching tmem_localify_finish routine which will handles the replacement
+ * and unlocks the hashbucket.
+ */
void *tmem_localify_get_pampd(struct tmem_pool *pool, struct tmem_oid *oidp,
uint32_t index, struct tmem_obj **ret_obj,
void **saved_hb)
@@ -618,6 +651,7 @@ void tmem_localify_finish(struct tmem_obj *obj, uint32_t index,
if (pampd != NULL) {
BUG_ON(obj == NULL);
(void)tmem_pampd_replace_in_obj(obj, index, pampd, 1);
+ (*tmem_pamops.create_finish)(pampd, is_ephemeral(obj->pool));
} else if (delete) {
BUG_ON(obj == NULL);
(void)tmem_pampd_delete_from_obj(obj, index);
@@ -625,6 +659,9 @@ void tmem_localify_finish(struct tmem_obj *obj, uint32_t index,
spin_unlock(&hb->lock);
}
+/*
+ * For ramster only. Helper function to support asynchronous tmem_get.
+ */
static int tmem_repatriate(void **ppampd, struct tmem_hashbucket *hb,
struct tmem_pool *pool, struct tmem_oid *oidp,
uint32_t index, bool free, char *data)
@@ -633,7 +670,6 @@ static int tmem_repatriate(void **ppampd, struct tmem_hashbucket *hb,
bool intransit = false;
int ret = 0;
-
if (!is_ephemeral(pool))
new_pampd = (*tmem_pamops.repatriate_preload)(
old_pampd, pool, oidp, index, &intransit);
@@ -646,60 +682,91 @@ static int tmem_repatriate(void **ppampd, struct tmem_hashbucket *hb,
if (!intransit)
ret = (*tmem_pamops.repatriate)(old_pampd, new_pampd, pool,
oidp, index, free, data);
+ if (ret == -EAGAIN) {
+ /* rare I think, but should cond_resched()??? */
+ usleep_range(10, 1000);
+ } else if (ret == -ENOTCONN || ret == -EHOSTDOWN) {
+ ret = -1;
+ } else if (ret != 0 && ret != -ENOENT) {
+ ret = -1;
+ }
+ /* note hb->lock has now been unlocked */
+ return ret;
+}
+
+/*
+ * For ramster only. If a page in tmem matches the handle, replace the
+ * page so that any subsequent "get" gets the new page. Returns 0 if
+ * there was a page to replace, else returns -1.
+ */
+int tmem_replace(struct tmem_pool *pool, struct tmem_oid *oidp,
+ uint32_t index, void *new_pampd)
+{
+ struct tmem_obj *obj;
+ int ret = -1;
+ struct tmem_hashbucket *hb;
+
+ hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+ spin_lock(&hb->lock);
+ obj = tmem_obj_find(hb, oidp);
+ if (obj == NULL)
+ goto out;
+ new_pampd = tmem_pampd_replace_in_obj(obj, index, new_pampd, 0);
+ /* if we bug here, pamops wasn't properly set up for ramster */
+ BUG_ON(tmem_pamops.replace_in_obj == NULL);
+ ret = (*tmem_pamops.replace_in_obj)(new_pampd, obj);
+out:
+ spin_unlock(&hb->lock);
return ret;
}
+#endif
/*
- * "Get" a page, e.g. if one can be found, copy the tmem page with the
- * matching handle from PAM space to the kernel. By tmem definition,
- * when a "get" is successful on an ephemeral page, the page is "flushed",
- * and when a "get" is successful on a persistent page, the page is retained
- * in tmem. Note that to preserve
+ * "Get" a page, e.g. if a pampd can be found matching the passed handle,
+ * use a pamops callback to recreated the page from the pampd with the
+ * matching handle. By tmem definition, when a "get" is successful on
+ * an ephemeral page, the page is "flushed", and when a "get" is successful
+ * on a persistent page, the page is retained in tmem. Note that to preserve
* coherency, "get" can never be skipped if tmem contains the data.
* That is, if a get is done with a certain handle and fails, any
* subsequent "get" must also fail (unless of course there is a
* "put" done with the same handle).
-
*/
int tmem_get(struct tmem_pool *pool, struct tmem_oid *oidp, uint32_t index,
- char *data, size_t *size, bool raw, int get_and_free)
+ char *data, size_t *sizep, bool raw, int get_and_free)
{
struct tmem_obj *obj;
- void *pampd;
+ void *pampd = NULL;
bool ephemeral = is_ephemeral(pool);
int ret = -1;
struct tmem_hashbucket *hb;
bool free = (get_and_free == 1) || ((get_and_free == 0) && ephemeral);
- bool lock_held = 0;
+ bool lock_held = false;
void **ppampd;
-again:
- hb = &pool->hashbucket[tmem_oid_hash(oidp)];
- spin_lock(&hb->lock);
- lock_held = 1;
- obj = tmem_obj_find(hb, oidp);
- if (obj == NULL)
- goto out;
- ppampd = __tmem_pampd_lookup_in_obj(obj, index);
- if (ppampd == NULL)
- goto out;
- if (tmem_pamops.is_remote(*ppampd)) {
- ret = tmem_repatriate(ppampd, hb, pool, oidp,
- index, free, data);
- lock_held = 0; /* note hb->lock has been unlocked */
- if (ret == -EAGAIN) {
- /* rare I think, but should cond_resched()??? */
- usleep_range(10, 1000);
- goto again;
- } else if (ret != 0) {
- if (ret != -ENOENT)
- pr_err("UNTESTED case in tmem_get, ret=%d\n",
- ret);
- ret = -1;
+ do {
+ hb = &pool->hashbucket[tmem_oid_hash(oidp)];
+ spin_lock(&hb->lock);
+ lock_held = true;
+ obj = tmem_obj_find(hb, oidp);
+ if (obj == NULL)
+ goto out;
+ ppampd = __tmem_pampd_lookup_in_obj(obj, index);
+ if (ppampd == NULL)
goto out;
+#ifdef CONFIG_RAMSTER
+ if ((tmem_pamops.is_remote != NULL) &&
+ tmem_pamops.is_remote(*ppampd)) {
+ ret = tmem_repatriate(ppampd, hb, pool, oidp,
+ index, free, data);
+ /* tmem_repatriate releases hb->lock */
+ lock_held = false;
+ *sizep = PAGE_SIZE;
+ if (ret != -EAGAIN)
+ goto out;
}
- goto out;
- }
+#endif
+ } while (ret == -EAGAIN);
if (free)
pampd = tmem_pampd_delete_from_obj(obj, index);
else
@@ -715,10 +782,10 @@ again:
}
if (free)
ret = (*tmem_pamops.get_data_and_free)(
- data, size, raw, pampd, pool, oidp, index);
+ data, sizep, raw, pampd, pool, oidp, index);
else
ret = (*tmem_pamops.get_data)(
- data, size, raw, pampd, pool, oidp, index);
+ data, sizep, raw, pampd, pool, oidp, index);
if (ret < 0)
goto out;
ret = 0;
@@ -762,30 +829,6 @@ out:
}
/*
- * If a page in tmem matches the handle, replace the page so that any
- * subsequent "get" gets the new page. Returns the new page if
- * there was a page to replace, else returns NULL.
- */
-int tmem_replace(struct tmem_pool *pool, struct tmem_oid *oidp,
- uint32_t index, void *new_pampd)
-{
- struct tmem_obj *obj;
- int ret = -1;
- struct tmem_hashbucket *hb;
-
- hb = &pool->hashbucket[tmem_oid_hash(oidp)];
- spin_lock(&hb->lock);
- obj = tmem_obj_find(hb, oidp);
- if (obj == NULL)
- goto out;
- new_pampd = tmem_pampd_replace_in_obj(obj, index, new_pampd, 0);
- ret = (*tmem_pamops.replace_in_obj)(new_pampd, obj);
-out:
- spin_unlock(&hb->lock);
- return ret;
-}
-
-/*
* "Flush" all pages in tmem matching this oid.
*/
int tmem_flush_object(struct tmem_pool *pool, struct tmem_oid *oidp)
@@ -799,7 +842,7 @@ int tmem_flush_object(struct tmem_pool *pool, struct tmem_oid *oidp)
obj = tmem_obj_find(hb, oidp);
if (obj == NULL)
goto out;
- tmem_pampd_destroy_all_in_obj(obj);
+ tmem_pampd_destroy_all_in_obj(obj, false);
tmem_obj_free(obj, hb);
(*tmem_hostops.obj_free)(obj, pool);
ret = 0;
diff --git a/drivers/staging/ramster/tmem.h b/drivers/staging/ramster/tmem.h
index 47f1918c8314..adbe5a8f28aa 100644
--- a/drivers/staging/ramster/tmem.h
+++ b/drivers/staging/ramster/tmem.h
@@ -3,23 +3,20 @@
*
* Transcendent memory
*
- * Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp.
+ * Copyright (c) 2009-2012, Dan Magenheimer, Oracle Corp.
*/
#ifndef _TMEM_H_
#define _TMEM_H_
+#include <linux/types.h>
#include <linux/highmem.h>
#include <linux/hash.h>
#include <linux/atomic.h>
/*
- * These are pre-defined by the Xen<->Linux ABI
+ * These are defined by the Xen<->Linux ABI so should remain consistent
*/
-#define TMEM_PUT_PAGE 4
-#define TMEM_GET_PAGE 5
-#define TMEM_FLUSH_PAGE 6
-#define TMEM_FLUSH_OBJECT 7
#define TMEM_POOL_PERSIST 1
#define TMEM_POOL_SHARED 2
#define TMEM_POOL_PRECOMPRESSED 4
@@ -31,7 +28,7 @@
* sentinels have proven very useful for debugging but can be removed
* or disabled before final merge.
*/
-#define SENTINELS
+#undef SENTINELS
#ifdef SENTINELS
#define DECL_SENTINEL uint32_t sentinel;
#define SET_SENTINEL(_x, _y) (_x->sentinel = _y##_SENTINEL)
@@ -46,7 +43,7 @@
#define ASSERT_INVERTED_SENTINEL(_x, _y) do { } while (0)
#endif
-#define ASSERT_SPINLOCK(_l) WARN_ON(!spin_is_locked(_l))
+#define ASSERT_SPINLOCK(_l) lockdep_assert_held(_l)
/*
* A pool is the highest-level data structure managed by tmem and
@@ -88,31 +85,6 @@ struct tmem_oid {
uint64_t oid[3];
};
-struct tmem_xhandle {
- uint8_t client_id;
- uint8_t xh_data_cksum;
- uint16_t xh_data_size;
- uint16_t pool_id;
- struct tmem_oid oid;
- uint32_t index;
- void *extra;
-};
-
-static inline struct tmem_xhandle tmem_xhandle_fill(uint16_t client_id,
- struct tmem_pool *pool,
- struct tmem_oid *oidp,
- uint32_t index)
-{
- struct tmem_xhandle xh;
- xh.client_id = client_id;
- xh.xh_data_cksum = (uint8_t)-1;
- xh.xh_data_size = (uint16_t)-1;
- xh.pool_id = pool->pool_id;
- xh.oid = *oidp;
- xh.index = index;
- return xh;
-}
-
static inline void tmem_oid_set_invalid(struct tmem_oid *oidp)
{
oidp->oid[0] = oidp->oid[1] = oidp->oid[2] = -1UL;
@@ -154,6 +126,34 @@ static inline unsigned tmem_oid_hash(struct tmem_oid *oidp)
TMEM_HASH_BUCKET_BITS);
}
+#ifdef CONFIG_RAMSTER
+struct tmem_xhandle {
+ uint8_t client_id;
+ uint8_t xh_data_cksum;
+ uint16_t xh_data_size;
+ uint16_t pool_id;
+ struct tmem_oid oid;
+ uint32_t index;
+ void *extra;
+};
+
+static inline struct tmem_xhandle tmem_xhandle_fill(uint16_t client_id,
+ struct tmem_pool *pool,
+ struct tmem_oid *oidp,
+ uint32_t index)
+{
+ struct tmem_xhandle xh;
+ xh.client_id = client_id;
+ xh.xh_data_cksum = (uint8_t)-1;
+ xh.xh_data_size = (uint16_t)-1;
+ xh.pool_id = pool->pool_id;
+ xh.oid = *oidp;
+ xh.index = index;
+ return xh;
+}
+#endif
+
+
/*
* A tmem_obj contains an identifier (oid), pointers to the parent
* pool and the rb_tree to which it belongs, counters, and an ordered
@@ -171,11 +171,15 @@ struct tmem_obj {
unsigned int objnode_tree_height;
unsigned long objnode_count;
long pampd_count;
- /* for current design of ramster, all pages belonging to
+#ifdef CONFIG_RAMSTER
+ /*
+ * for current design of ramster, all pages belonging to
* an object reside on the same remotenode and extra is
* used to record the number of the remotenode so a
- * flush-object operation can specify it */
- void *extra; /* for use by pampd implementation */
+ * flush-object operation can specify it
+ */
+ void *extra; /* for private use by pampd implementation */
+#endif
DECL_SENTINEL
};
@@ -193,10 +197,17 @@ struct tmem_objnode {
unsigned int slots_in_use;
};
+struct tmem_handle {
+ struct tmem_oid oid; /* 24 bytes */
+ uint32_t index;
+ uint16_t pool_id;
+ uint16_t client_id;
+};
+
+
/* pampd abstract datatype methods provided by the PAM implementation */
struct tmem_pamops {
- void *(*create)(char *, size_t, bool, int,
- struct tmem_pool *, struct tmem_oid *, uint32_t);
+ void (*create_finish)(void *, bool);
int (*get_data)(char *, size_t *, bool, void *, struct tmem_pool *,
struct tmem_oid *, uint32_t);
int (*get_data_and_free)(char *, size_t *, bool, void *,
@@ -204,14 +215,16 @@ struct tmem_pamops {
uint32_t);
void (*free)(void *, struct tmem_pool *,
struct tmem_oid *, uint32_t, bool);
- void (*free_obj)(struct tmem_pool *, struct tmem_obj *);
- bool (*is_remote)(void *);
+#ifdef CONFIG_RAMSTER
+ void (*new_obj)(struct tmem_obj *);
+ void (*free_obj)(struct tmem_pool *, struct tmem_obj *, bool);
void *(*repatriate_preload)(void *, struct tmem_pool *,
struct tmem_oid *, uint32_t, bool *);
int (*repatriate)(void *, void *, struct tmem_pool *,
struct tmem_oid *, uint32_t, bool, void *);
- void (*new_obj)(struct tmem_obj *);
+ bool (*is_remote)(void *);
int (*replace_in_obj)(void *, struct tmem_obj *);
+#endif
};
extern void tmem_register_pamops(struct tmem_pamops *m);
@@ -226,9 +239,15 @@ extern void tmem_register_hostops(struct tmem_hostops *m);
/* core tmem accessor functions */
extern int tmem_put(struct tmem_pool *, struct tmem_oid *, uint32_t index,
- char *, size_t, bool, int);
+ bool, void *);
extern int tmem_get(struct tmem_pool *, struct tmem_oid *, uint32_t index,
char *, size_t *, bool, int);
+extern int tmem_flush_page(struct tmem_pool *, struct tmem_oid *,
+ uint32_t index);
+extern int tmem_flush_object(struct tmem_pool *, struct tmem_oid *);
+extern int tmem_destroy_pool(struct tmem_pool *);
+extern void tmem_new_pool(struct tmem_pool *, uint32_t);
+#ifdef CONFIG_RAMSTER
extern int tmem_replace(struct tmem_pool *, struct tmem_oid *, uint32_t index,
void *);
extern void *tmem_localify_get_pampd(struct tmem_pool *, struct tmem_oid *,
@@ -236,9 +255,5 @@ extern void *tmem_localify_get_pampd(struct tmem_pool *, struct tmem_oid *,
void **);
extern void tmem_localify_finish(struct tmem_obj *, uint32_t index,
void *, void *, bool);
-extern int tmem_flush_page(struct tmem_pool *, struct tmem_oid *,
- uint32_t index);
-extern int tmem_flush_object(struct tmem_pool *, struct tmem_oid *);
-extern int tmem_destroy_pool(struct tmem_pool *);
-extern void tmem_new_pool(struct tmem_pool *, uint32_t);
+#endif
#endif /* _TMEM_H */
diff --git a/drivers/staging/ramster/xvmalloc.c b/drivers/staging/ramster/xvmalloc.c
deleted file mode 100644
index 44ceb0b823a9..000000000000
--- a/drivers/staging/ramster/xvmalloc.c
+++ /dev/null
@@ -1,509 +0,0 @@
-/*
- * xvmalloc memory allocator
- *
- * Copyright (C) 2008, 2009, 2010 Nitin Gupta
- *
- * This code is released using a dual license strategy: BSD/GPL
- * You can choose the licence that better fits your requirements.
- *
- * Released under the terms of 3-clause BSD License
- * Released under the terms of GNU General Public License Version 2.0
- */
-
-#ifdef CONFIG_ZRAM_DEBUG
-#define DEBUG
-#endif
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/bitops.h>
-#include <linux/errno.h>
-#include <linux/highmem.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-
-#include "xvmalloc.h"
-#include "xvmalloc_int.h"
-
-static void stat_inc(u64 *value)
-{
- *value = *value + 1;
-}
-
-static void stat_dec(u64 *value)
-{
- *value = *value - 1;
-}
-
-static int test_flag(struct block_header *block, enum blockflags flag)
-{
- return block->prev & BIT(flag);
-}
-
-static void set_flag(struct block_header *block, enum blockflags flag)
-{
- block->prev |= BIT(flag);
-}
-
-static void clear_flag(struct block_header *block, enum blockflags flag)
-{
- block->prev &= ~BIT(flag);
-}
-
-/*
- * Given <page, offset> pair, provide a dereferencable pointer.
- * This is called from xv_malloc/xv_free path, so it
- * needs to be fast.
- */
-static void *get_ptr_atomic(struct page *page, u16 offset)
-{
- unsigned char *base;
-
- base = kmap_atomic(page);
- return base + offset;
-}
-
-static void put_ptr_atomic(void *ptr)
-{
- kunmap_atomic(ptr);
-}
-
-static u32 get_blockprev(struct block_header *block)
-{
- return block->prev & PREV_MASK;
-}
-
-static void set_blockprev(struct block_header *block, u16 new_offset)
-{
- block->prev = new_offset | (block->prev & FLAGS_MASK);
-}
-
-static struct block_header *BLOCK_NEXT(struct block_header *block)
-{
- return (struct block_header *)
- ((char *)block + block->size + XV_ALIGN);
-}
-
-/*
- * Get index of free list containing blocks of maximum size
- * which is less than or equal to given size.
- */
-static u32 get_index_for_insert(u32 size)
-{
- if (unlikely(size > XV_MAX_ALLOC_SIZE))
- size = XV_MAX_ALLOC_SIZE;
- size &= ~FL_DELTA_MASK;
- return (size - XV_MIN_ALLOC_SIZE) >> FL_DELTA_SHIFT;
-}
-
-/*
- * Get index of free list having blocks of size greater than
- * or equal to requested size.
- */
-static u32 get_index(u32 size)
-{
- if (unlikely(size < XV_MIN_ALLOC_SIZE))
- size = XV_MIN_ALLOC_SIZE;
- size = ALIGN(size, FL_DELTA);
- return (size - XV_MIN_ALLOC_SIZE) >> FL_DELTA_SHIFT;
-}
-
-/**
- * find_block - find block of at least given size
- * @pool: memory pool to search from
- * @size: size of block required
- * @page: page containing required block
- * @offset: offset within the page where block is located.
- *
- * Searches two level bitmap to locate block of at least
- * the given size. If such a block is found, it provides
- * <page, offset> to identify this block and returns index
- * in freelist where we found this block.
- * Otherwise, returns 0 and <page, offset> params are not touched.
- */
-static u32 find_block(struct xv_pool *pool, u32 size,
- struct page **page, u32 *offset)
-{
- ulong flbitmap, slbitmap;
- u32 flindex, slindex, slbitstart;
-
- /* There are no free blocks in this pool */
- if (!pool->flbitmap)
- return 0;
-
- /* Get freelist index corresponding to this size */
- slindex = get_index(size);
- slbitmap = pool->slbitmap[slindex / BITS_PER_LONG];
- slbitstart = slindex % BITS_PER_LONG;
-
- /*
- * If freelist is not empty at this index, we found the
- * block - head of this list. This is approximate best-fit match.
- */
- if (test_bit(slbitstart, &slbitmap)) {
- *page = pool->freelist[slindex].page;
- *offset = pool->freelist[slindex].offset;
- return slindex;
- }
-
- /*
- * No best-fit found. Search a bit further in bitmap for a free block.
- * Second level bitmap consists of series of 32-bit chunks. Search
- * further in the chunk where we expected a best-fit, starting from
- * index location found above.
- */
- slbitstart++;
- slbitmap >>= slbitstart;
-
- /* Skip this search if we were already at end of this bitmap chunk */
- if ((slbitstart != BITS_PER_LONG) && slbitmap) {
- slindex += __ffs(slbitmap) + 1;
- *page = pool->freelist[slindex].page;
- *offset = pool->freelist[slindex].offset;
- return slindex;
- }
-
- /* Now do a full two-level bitmap search to find next nearest fit */
- flindex = slindex / BITS_PER_LONG;
-
- flbitmap = (pool->flbitmap) >> (flindex + 1);
- if (!flbitmap)
- return 0;
-
- flindex += __ffs(flbitmap) + 1;
- slbitmap = pool->slbitmap[flindex];
- slindex = (flindex * BITS_PER_LONG) + __ffs(slbitmap);
- *page = pool->freelist[slindex].page;
- *offset = pool->freelist[slindex].offset;
-
- return slindex;
-}
-
-/*
- * Insert block at <page, offset> in freelist of given pool.
- * freelist used depends on block size.
- */
-static void insert_block(struct xv_pool *pool, struct page *page, u32 offset,
- struct block_header *block)
-{
- u32 flindex, slindex;
- struct block_header *nextblock;
-
- slindex = get_index_for_insert(block->size);
- flindex = slindex / BITS_PER_LONG;
-
- block->link.prev_page = NULL;
- block->link.prev_offset = 0;
- block->link.next_page = pool->freelist[slindex].page;
- block->link.next_offset = pool->freelist[slindex].offset;
- pool->freelist[slindex].page = page;
- pool->freelist[slindex].offset = offset;
-
- if (block->link.next_page) {
- nextblock = get_ptr_atomic(block->link.next_page,
- block->link.next_offset);
- nextblock->link.prev_page = page;
- nextblock->link.prev_offset = offset;
- put_ptr_atomic(nextblock);
- /* If there was a next page then the free bits are set. */
- return;
- }
-
- __set_bit(slindex % BITS_PER_LONG, &pool->slbitmap[flindex]);
- __set_bit(flindex, &pool->flbitmap);
-}
-
-/*
- * Remove block from freelist. Index 'slindex' identifies the freelist.
- */
-static void remove_block(struct xv_pool *pool, struct page *page, u32 offset,
- struct block_header *block, u32 slindex)
-{
- u32 flindex = slindex / BITS_PER_LONG;
- struct block_header *tmpblock;
-
- if (block->link.prev_page) {
- tmpblock = get_ptr_atomic(block->link.prev_page,
- block->link.prev_offset);
- tmpblock->link.next_page = block->link.next_page;
- tmpblock->link.next_offset = block->link.next_offset;
- put_ptr_atomic(tmpblock);
- }
-
- if (block->link.next_page) {
- tmpblock = get_ptr_atomic(block->link.next_page,
- block->link.next_offset);
- tmpblock->link.prev_page = block->link.prev_page;
- tmpblock->link.prev_offset = block->link.prev_offset;
- put_ptr_atomic(tmpblock);
- }
-
- /* Is this block is at the head of the freelist? */
- if (pool->freelist[slindex].page == page
- && pool->freelist[slindex].offset == offset) {
-
- pool->freelist[slindex].page = block->link.next_page;
- pool->freelist[slindex].offset = block->link.next_offset;
-
- if (pool->freelist[slindex].page) {
- struct block_header *tmpblock;
- tmpblock = get_ptr_atomic(pool->freelist[slindex].page,
- pool->freelist[slindex].offset);
- tmpblock->link.prev_page = NULL;
- tmpblock->link.prev_offset = 0;
- put_ptr_atomic(tmpblock);
- } else {
- /* This freelist bucket is empty */
- __clear_bit(slindex % BITS_PER_LONG,
- &pool->slbitmap[flindex]);
- if (!pool->slbitmap[flindex])
- __clear_bit(flindex, &pool->flbitmap);
- }
- }
-
- block->link.prev_page = NULL;
- block->link.prev_offset = 0;
- block->link.next_page = NULL;
- block->link.next_offset = 0;
-}
-
-/*
- * Allocate a page and add it to freelist of given pool.
- */
-static int grow_pool(struct xv_pool *pool, gfp_t flags)
-{
- struct page *page;
- struct block_header *block;
-
- page = alloc_page(flags);
- if (unlikely(!page))
- return -ENOMEM;
-
- stat_inc(&pool->total_pages);
-
- spin_lock(&pool->lock);
- block = get_ptr_atomic(page, 0);
-
- block->size = PAGE_SIZE - XV_ALIGN;
- set_flag(block, BLOCK_FREE);
- clear_flag(block, PREV_FREE);
- set_blockprev(block, 0);
-
- insert_block(pool, page, 0, block);
-
- put_ptr_atomic(block);
- spin_unlock(&pool->lock);
-
- return 0;
-}
-
-/*
- * Create a memory pool. Allocates freelist, bitmaps and other
- * per-pool metadata.
- */
-struct xv_pool *xv_create_pool(void)
-{
- u32 ovhd_size;
- struct xv_pool *pool;
-
- ovhd_size = roundup(sizeof(*pool), PAGE_SIZE);
- pool = kzalloc(ovhd_size, GFP_KERNEL);
- if (!pool)
- return NULL;
-
- spin_lock_init(&pool->lock);
-
- return pool;
-}
-EXPORT_SYMBOL_GPL(xv_create_pool);
-
-void xv_destroy_pool(struct xv_pool *pool)
-{
- kfree(pool);
-}
-EXPORT_SYMBOL_GPL(xv_destroy_pool);
-
-/**
- * xv_malloc - Allocate block of given size from pool.
- * @pool: pool to allocate from
- * @size: size of block to allocate
- * @page: page no. that holds the object
- * @offset: location of object within page
- *
- * On success, <page, offset> identifies block allocated
- * and 0 is returned. On failure, <page, offset> is set to
- * 0 and -ENOMEM is returned.
- *
- * Allocation requests with size > XV_MAX_ALLOC_SIZE will fail.
- */
-int xv_malloc(struct xv_pool *pool, u32 size, struct page **page,
- u32 *offset, gfp_t flags)
-{
- int error;
- u32 index, tmpsize, origsize, tmpoffset;
- struct block_header *block, *tmpblock;
-
- *page = NULL;
- *offset = 0;
- origsize = size;
-
- if (unlikely(!size || size > XV_MAX_ALLOC_SIZE))
- return -ENOMEM;
-
- size = ALIGN(size, XV_ALIGN);
-
- spin_lock(&pool->lock);
-
- index = find_block(pool, size, page, offset);
-
- if (!*page) {
- spin_unlock(&pool->lock);
- if (flags & GFP_NOWAIT)
- return -ENOMEM;
- error = grow_pool(pool, flags);
- if (unlikely(error))
- return error;
-
- spin_lock(&pool->lock);
- index = find_block(pool, size, page, offset);
- }
-
- if (!*page) {
- spin_unlock(&pool->lock);
- return -ENOMEM;
- }
-
- block = get_ptr_atomic(*page, *offset);
-
- remove_block(pool, *page, *offset, block, index);
-
- /* Split the block if required */
- tmpoffset = *offset + size + XV_ALIGN;
- tmpsize = block->size - size;
- tmpblock = (struct block_header *)((char *)block + size + XV_ALIGN);
- if (tmpsize) {
- tmpblock->size = tmpsize - XV_ALIGN;
- set_flag(tmpblock, BLOCK_FREE);
- clear_flag(tmpblock, PREV_FREE);
-
- set_blockprev(tmpblock, *offset);
- if (tmpblock->size >= XV_MIN_ALLOC_SIZE)
- insert_block(pool, *page, tmpoffset, tmpblock);
-
- if (tmpoffset + XV_ALIGN + tmpblock->size != PAGE_SIZE) {
- tmpblock = BLOCK_NEXT(tmpblock);
- set_blockprev(tmpblock, tmpoffset);
- }
- } else {
- /* This block is exact fit */
- if (tmpoffset != PAGE_SIZE)
- clear_flag(tmpblock, PREV_FREE);
- }
-
- block->size = origsize;
- clear_flag(block, BLOCK_FREE);
-
- put_ptr_atomic(block);
- spin_unlock(&pool->lock);
-
- *offset += XV_ALIGN;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(xv_malloc);
-
-/*
- * Free block identified with <page, offset>
- */
-void xv_free(struct xv_pool *pool, struct page *page, u32 offset)
-{
- void *page_start;
- struct block_header *block, *tmpblock;
-
- offset -= XV_ALIGN;
-
- spin_lock(&pool->lock);
-
- page_start = get_ptr_atomic(page, 0);
- block = (struct block_header *)((char *)page_start + offset);
-
- /* Catch double free bugs */
- BUG_ON(test_flag(block, BLOCK_FREE));
-
- block->size = ALIGN(block->size, XV_ALIGN);
-
- tmpblock = BLOCK_NEXT(block);
- if (offset + block->size + XV_ALIGN == PAGE_SIZE)
- tmpblock = NULL;
-
- /* Merge next block if its free */
- if (tmpblock && test_flag(tmpblock, BLOCK_FREE)) {
- /*
- * Blocks smaller than XV_MIN_ALLOC_SIZE
- * are not inserted in any free list.
- */
- if (tmpblock->size >= XV_MIN_ALLOC_SIZE) {
- remove_block(pool, page,
- offset + block->size + XV_ALIGN, tmpblock,
- get_index_for_insert(tmpblock->size));
- }
- block->size += tmpblock->size + XV_ALIGN;
- }
-
- /* Merge previous block if its free */
- if (test_flag(block, PREV_FREE)) {
- tmpblock = (struct block_header *)((char *)(page_start) +
- get_blockprev(block));
- offset = offset - tmpblock->size - XV_ALIGN;
-
- if (tmpblock->size >= XV_MIN_ALLOC_SIZE)
- remove_block(pool, page, offset, tmpblock,
- get_index_for_insert(tmpblock->size));
-
- tmpblock->size += block->size + XV_ALIGN;
- block = tmpblock;
- }
-
- /* No used objects in this page. Free it. */
- if (block->size == PAGE_SIZE - XV_ALIGN) {
- put_ptr_atomic(page_start);
- spin_unlock(&pool->lock);
-
- __free_page(page);
- stat_dec(&pool->total_pages);
- return;
- }
-
- set_flag(block, BLOCK_FREE);
- if (block->size >= XV_MIN_ALLOC_SIZE)
- insert_block(pool, page, offset, block);
-
- if (offset + block->size + XV_ALIGN != PAGE_SIZE) {
- tmpblock = BLOCK_NEXT(block);
- set_flag(tmpblock, PREV_FREE);
- set_blockprev(tmpblock, offset);
- }
-
- put_ptr_atomic(page_start);
- spin_unlock(&pool->lock);
-}
-EXPORT_SYMBOL_GPL(xv_free);
-
-u32 xv_get_object_size(void *obj)
-{
- struct block_header *blk;
-
- blk = (struct block_header *)((char *)(obj) - XV_ALIGN);
- return blk->size;
-}
-EXPORT_SYMBOL_GPL(xv_get_object_size);
-
-/*
- * Returns total memory used by allocator (userdata + metadata)
- */
-u64 xv_get_total_size_bytes(struct xv_pool *pool)
-{
- return pool->total_pages << PAGE_SHIFT;
-}
-EXPORT_SYMBOL_GPL(xv_get_total_size_bytes);
diff --git a/drivers/staging/ramster/xvmalloc.h b/drivers/staging/ramster/xvmalloc.h
deleted file mode 100644
index 5b1a81aa5faf..000000000000
--- a/drivers/staging/ramster/xvmalloc.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * xvmalloc memory allocator
- *
- * Copyright (C) 2008, 2009, 2010 Nitin Gupta
- *
- * This code is released using a dual license strategy: BSD/GPL
- * You can choose the licence that better fits your requirements.
- *
- * Released under the terms of 3-clause BSD License
- * Released under the terms of GNU General Public License Version 2.0
- */
-
-#ifndef _XV_MALLOC_H_
-#define _XV_MALLOC_H_
-
-#include <linux/types.h>
-
-struct xv_pool;
-
-struct xv_pool *xv_create_pool(void);
-void xv_destroy_pool(struct xv_pool *pool);
-
-int xv_malloc(struct xv_pool *pool, u32 size, struct page **page,
- u32 *offset, gfp_t flags);
-void xv_free(struct xv_pool *pool, struct page *page, u32 offset);
-
-u32 xv_get_object_size(void *obj);
-u64 xv_get_total_size_bytes(struct xv_pool *pool);
-
-#endif
diff --git a/drivers/staging/ramster/xvmalloc_int.h b/drivers/staging/ramster/xvmalloc_int.h
deleted file mode 100644
index b5f1f7febcf6..000000000000
--- a/drivers/staging/ramster/xvmalloc_int.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * xvmalloc memory allocator
- *
- * Copyright (C) 2008, 2009, 2010 Nitin Gupta
- *
- * This code is released using a dual license strategy: BSD/GPL
- * You can choose the licence that better fits your requirements.
- *
- * Released under the terms of 3-clause BSD License
- * Released under the terms of GNU General Public License Version 2.0
- */
-
-#ifndef _XV_MALLOC_INT_H_
-#define _XV_MALLOC_INT_H_
-
-#include <linux/kernel.h>
-#include <linux/types.h>
-
-/* User configurable params */
-
-/* Must be power of two */
-#ifdef CONFIG_64BIT
-#define XV_ALIGN_SHIFT 3
-#else
-#define XV_ALIGN_SHIFT 2
-#endif
-#define XV_ALIGN (1 << XV_ALIGN_SHIFT)
-#define XV_ALIGN_MASK (XV_ALIGN - 1)
-
-/* This must be greater than sizeof(link_free) */
-#define XV_MIN_ALLOC_SIZE 32
-#define XV_MAX_ALLOC_SIZE (PAGE_SIZE - XV_ALIGN)
-
-/*
- * Free lists are separated by FL_DELTA bytes
- * This value is 3 for 4k pages and 4 for 64k pages, for any
- * other page size, a conservative (PAGE_SHIFT - 9) is used.
- */
-#if PAGE_SHIFT == 16
-#define FL_DELTA_SHIFT 4
-#else
-#define FL_DELTA_SHIFT (PAGE_SHIFT - 9)
-#endif
-#define FL_DELTA (1 << FL_DELTA_SHIFT)
-#define FL_DELTA_MASK (FL_DELTA - 1)
-#define NUM_FREE_LISTS ((XV_MAX_ALLOC_SIZE - XV_MIN_ALLOC_SIZE) \
- / FL_DELTA + 1)
-
-#define MAX_FLI DIV_ROUND_UP(NUM_FREE_LISTS, BITS_PER_LONG)
-
-/* End of user params */
-
-enum blockflags {
- BLOCK_FREE,
- PREV_FREE,
- __NR_BLOCKFLAGS,
-};
-
-#define FLAGS_MASK XV_ALIGN_MASK
-#define PREV_MASK (~FLAGS_MASK)
-
-struct freelist_entry {
- struct page *page;
- u16 offset;
- u16 pad;
-};
-
-struct link_free {
- struct page *prev_page;
- struct page *next_page;
- u16 prev_offset;
- u16 next_offset;
-};
-
-struct block_header {
- union {
- /* This common header must be XV_ALIGN bytes */
- u8 common[XV_ALIGN];
- struct {
- u16 size;
- u16 prev;
- };
- };
- struct link_free link;
-};
-
-struct xv_pool {
- ulong flbitmap;
- ulong slbitmap[MAX_FLI];
- u64 total_pages; /* stats */
- struct freelist_entry freelist[NUM_FREE_LISTS];
- spinlock_t lock;
-};
-
-#endif
diff --git a/drivers/staging/ramster/zbud.c b/drivers/staging/ramster/zbud.c
new file mode 100644
index 000000000000..a7c436127aa1
--- /dev/null
+++ b/drivers/staging/ramster/zbud.c
@@ -0,0 +1,1060 @@
+/*
+ * zbud.c - Compression buddies allocator
+ *
+ * Copyright (c) 2010-2012, Dan Magenheimer, Oracle Corp.
+ *
+ * Compression buddies ("zbud") provides for efficiently packing two
+ * (or, possibly in the future, more) compressed pages ("zpages") into
+ * a single "raw" pageframe and for tracking both zpages and pageframes
+ * so that whole pageframes can be easily reclaimed in LRU-like order.
+ * It is designed to be used in conjunction with transcendent memory
+ * ("tmem"); for example separate LRU lists are maintained for persistent
+ * vs. ephemeral pages.
+ *
+ * A zbudpage is an overlay for a struct page and thus each zbudpage
+ * refers to a physical pageframe of RAM. When the caller passes a
+ * struct page from the kernel's page allocator, zbud "transforms" it
+ * to a zbudpage which sets/uses a different set of fields than the
+ * struct-page and thus must "untransform" it back by reinitializing
+ * certain fields before the struct-page can be freed. The fields
+ * of a zbudpage include a page lock for controlling access to the
+ * corresponding pageframe, and there is a size field for each zpage.
+ * Each zbudpage also lives on two linked lists: a "budlist" which is
+ * used to support efficient buddying of zpages; and an "lru" which
+ * is used for reclaiming pageframes in approximately least-recently-used
+ * order.
+ *
+ * A zbudpageframe is a pageframe divided up into aligned 64-byte "chunks"
+ * which contain the compressed data for zero, one, or two zbuds. Contained
+ * with the compressed data is a tmem_handle which is a key to allow
+ * the same data to be found via the tmem interface so the zpage can
+ * be invalidated (for ephemeral pages) or repatriated to the swap cache
+ * (for persistent pages). The contents of a zbudpageframe must never
+ * be accessed without holding the page lock for the corresponding
+ * zbudpage and, to accomodate highmem machines, the contents may
+ * only be examined or changes when kmapped. Thus, when in use, a
+ * kmapped zbudpageframe is referred to in the zbud code as "void *zbpg".
+ *
+ * Note that the term "zbud" refers to the combination of a zpage and
+ * a tmem_handle that is stored as one of possibly two "buddied" zpages;
+ * it also generically refers to this allocator... sorry for any confusion.
+ *
+ * A zbudref is a pointer to a struct zbudpage (which can be cast to a
+ * struct page), with the LSB either cleared or set to indicate, respectively,
+ * the first or second zpage in the zbudpageframe. Since a zbudref can be
+ * cast to a pointer, it is used as the tmem "pampd" pointer and uniquely
+ * references a stored tmem page and so is the only zbud data structure
+ * externally visible to zbud.c/zbud.h.
+ *
+ * Since we wish to reclaim entire pageframes but zpages may be randomly
+ * added and deleted to any given pageframe, we approximate LRU by
+ * promoting a pageframe to MRU when a zpage is added to it, but
+ * leaving it at the current place in the list when a zpage is deleted
+ * from it. As a side effect, zpages that are difficult to buddy (e.g.
+ * very large paages) will be reclaimed faster than average, which seems
+ * reasonable.
+ *
+ * In the current implementation, no more than two zpages may be stored in
+ * any pageframe and no zpage ever crosses a pageframe boundary. While
+ * other zpage allocation mechanisms may allow greater density, this two
+ * zpage-per-pageframe limit both ensures simple reclaim of pageframes
+ * (including garbage collection of references to the contents of those
+ * pageframes from tmem data structures) AND avoids the need for compaction.
+ * With additional complexity, zbud could be modified to support storing
+ * up to three zpages per pageframe or, to handle larger average zpages,
+ * up to three zpages per pair of pageframes, but it is not clear if the
+ * additional complexity would be worth it. So consider it an exercise
+ * for future developers.
+ *
+ * Note also that zbud does no page allocation or freeing. This is so
+ * that the caller has complete control over and, for accounting, visibility
+ * into if/when pages are allocated and freed.
+ *
+ * Finally, note that zbud limits the size of zpages it can store; the
+ * caller must check the zpage size with zbud_max_buddy_size before
+ * storing it, else BUGs will result. User beware.
+ */
+
+#include <linux/module.h>
+#include <linux/highmem.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/pagemap.h>
+#include <linux/atomic.h>
+#include <linux/bug.h>
+#include "tmem.h"
+#include "zcache.h"
+#include "zbud.h"
+
+/*
+ * We need to ensure that a struct zbudpage is never larger than a
+ * struct page. This is checked with a BUG_ON in zbud_init.
+ *
+ * The unevictable field indicates that a zbud is being added to the
+ * zbudpage. Since this is a two-phase process (due to tmem locking),
+ * this field locks the zbudpage against eviction when a zbud match
+ * or creation is in process. Since this addition process may occur
+ * in parallel for two zbuds in one zbudpage, the field is a counter
+ * that must not exceed two.
+ */
+struct zbudpage {
+ union {
+ struct page page;
+ struct {
+ unsigned long space_for_flags;
+ struct {
+ unsigned zbud0_size:12;
+ unsigned zbud1_size:12;
+ unsigned unevictable:2;
+ };
+ struct list_head budlist;
+ struct list_head lru;
+ };
+ };
+};
+
+struct zbudref {
+ union {
+ struct zbudpage *zbudpage;
+ unsigned long zbudref;
+ };
+};
+
+#define CHUNK_SHIFT 6
+#define CHUNK_SIZE (1 << CHUNK_SHIFT)
+#define CHUNK_MASK (~(CHUNK_SIZE-1))
+#define NCHUNKS (PAGE_SIZE >> CHUNK_SHIFT)
+#define MAX_CHUNK (NCHUNKS-1)
+
+/*
+ * The following functions deal with the difference between struct
+ * page and struct zbudpage. Note the hack of using the pageflags
+ * from struct page; this is to avoid duplicating all the complex
+ * pageflag macros.
+ */
+static inline void zbudpage_spin_lock(struct zbudpage *zbudpage)
+{
+ struct page *page = (struct page *)zbudpage;
+
+ while (unlikely(test_and_set_bit_lock(PG_locked, &page->flags))) {
+ do {
+ cpu_relax();
+ } while (test_bit(PG_locked, &page->flags));
+ }
+}
+
+static inline void zbudpage_spin_unlock(struct zbudpage *zbudpage)
+{
+ struct page *page = (struct page *)zbudpage;
+
+ clear_bit(PG_locked, &page->flags);
+}
+
+static inline int zbudpage_spin_trylock(struct zbudpage *zbudpage)
+{
+ return trylock_page((struct page *)zbudpage);
+}
+
+static inline int zbudpage_is_locked(struct zbudpage *zbudpage)
+{
+ return PageLocked((struct page *)zbudpage);
+}
+
+static inline void *kmap_zbudpage_atomic(struct zbudpage *zbudpage)
+{
+ return kmap_atomic((struct page *)zbudpage);
+}
+
+/*
+ * A dying zbudpage is an ephemeral page in the process of being evicted.
+ * Any data contained in the zbudpage is invalid and we are just waiting for
+ * the tmem pampds to be invalidated before freeing the page
+ */
+static inline int zbudpage_is_dying(struct zbudpage *zbudpage)
+{
+ struct page *page = (struct page *)zbudpage;
+
+ return test_bit(PG_reclaim, &page->flags);
+}
+
+static inline void zbudpage_set_dying(struct zbudpage *zbudpage)
+{
+ struct page *page = (struct page *)zbudpage;
+
+ set_bit(PG_reclaim, &page->flags);
+}
+
+static inline void zbudpage_clear_dying(struct zbudpage *zbudpage)
+{
+ struct page *page = (struct page *)zbudpage;
+
+ clear_bit(PG_reclaim, &page->flags);
+}
+
+/*
+ * A zombie zbudpage is a persistent page in the process of being evicted.
+ * The data contained in the zbudpage is valid and we are just waiting for
+ * the tmem pampds to be invalidated before freeing the page
+ */
+static inline int zbudpage_is_zombie(struct zbudpage *zbudpage)
+{
+ struct page *page = (struct page *)zbudpage;
+
+ return test_bit(PG_dirty, &page->flags);
+}
+
+static inline void zbudpage_set_zombie(struct zbudpage *zbudpage)
+{
+ struct page *page = (struct page *)zbudpage;
+
+ set_bit(PG_dirty, &page->flags);
+}
+
+static inline void zbudpage_clear_zombie(struct zbudpage *zbudpage)
+{
+ struct page *page = (struct page *)zbudpage;
+
+ clear_bit(PG_dirty, &page->flags);
+}
+
+static inline void kunmap_zbudpage_atomic(void *zbpg)
+{
+ kunmap_atomic(zbpg);
+}
+
+/*
+ * zbud "translation" and helper functions
+ */
+
+static inline struct zbudpage *zbudref_to_zbudpage(struct zbudref *zref)
+{
+ unsigned long zbud = (unsigned long)zref;
+ zbud &= ~1UL;
+ return (struct zbudpage *)zbud;
+}
+
+static inline struct zbudref *zbudpage_to_zbudref(struct zbudpage *zbudpage,
+ unsigned budnum)
+{
+ unsigned long zbud = (unsigned long)zbudpage;
+ BUG_ON(budnum > 1);
+ zbud |= budnum;
+ return (struct zbudref *)zbud;
+}
+
+static inline int zbudref_budnum(struct zbudref *zbudref)
+{
+ unsigned long zbud = (unsigned long)zbudref;
+ return zbud & 1UL;
+}
+
+static inline unsigned zbud_max_size(void)
+{
+ return MAX_CHUNK << CHUNK_SHIFT;
+}
+
+static inline unsigned zbud_size_to_chunks(unsigned size)
+{
+ BUG_ON(size == 0 || size > zbud_max_size());
+ return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
+}
+
+/* can only be used between kmap_zbudpage_atomic/kunmap_zbudpage_atomic! */
+static inline char *zbud_data(void *zbpg,
+ unsigned budnum, unsigned size)
+{
+ char *p;
+
+ BUG_ON(size == 0 || size > zbud_max_size());
+ p = (char *)zbpg;
+ if (budnum == 1)
+ p += PAGE_SIZE - ((size + CHUNK_SIZE - 1) & CHUNK_MASK);
+ return p;
+}
+
+/*
+ * These are all informative and exposed through debugfs... except for
+ * the arrays... anyone know how to do that? To avoid confusion for
+ * debugfs viewers, some of these should also be atomic_long_t, but
+ * I don't know how to expose atomics via debugfs either...
+ */
+static unsigned long zbud_eph_pageframes;
+static unsigned long zbud_pers_pageframes;
+static unsigned long zbud_eph_zpages;
+static unsigned long zbud_pers_zpages;
+static u64 zbud_eph_zbytes;
+static u64 zbud_pers_zbytes;
+static unsigned long zbud_eph_evicted_pageframes;
+static unsigned long zbud_pers_evicted_pageframes;
+static unsigned long zbud_eph_cumul_zpages;
+static unsigned long zbud_pers_cumul_zpages;
+static u64 zbud_eph_cumul_zbytes;
+static u64 zbud_pers_cumul_zbytes;
+static unsigned long zbud_eph_cumul_chunk_counts[NCHUNKS];
+static unsigned long zbud_pers_cumul_chunk_counts[NCHUNKS];
+static unsigned long zbud_eph_buddied_count;
+static unsigned long zbud_pers_buddied_count;
+static unsigned long zbud_eph_unbuddied_count;
+static unsigned long zbud_pers_unbuddied_count;
+static unsigned long zbud_eph_zombie_count;
+static unsigned long zbud_pers_zombie_count;
+static atomic_t zbud_eph_zombie_atomic;
+static atomic_t zbud_pers_zombie_atomic;
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#define zdfs debugfs_create_size_t
+#define zdfs64 debugfs_create_u64
+static int zbud_debugfs_init(void)
+{
+ struct dentry *root = debugfs_create_dir("zbud", NULL);
+ if (root == NULL)
+ return -ENXIO;
+
+ /*
+ * would be nice to dump the sizes of the unbuddied
+ * arrays, like was done with sysfs, but it doesn't
+ * look like debugfs is flexible enough to do that
+ */
+ zdfs64("eph_zbytes", S_IRUGO, root, &zbud_eph_zbytes);
+ zdfs64("eph_cumul_zbytes", S_IRUGO, root, &zbud_eph_cumul_zbytes);
+ zdfs64("pers_zbytes", S_IRUGO, root, &zbud_pers_zbytes);
+ zdfs64("pers_cumul_zbytes", S_IRUGO, root, &zbud_pers_cumul_zbytes);
+ zdfs("eph_cumul_zpages", S_IRUGO, root, &zbud_eph_cumul_zpages);
+ zdfs("eph_evicted_pageframes", S_IRUGO, root,
+ &zbud_eph_evicted_pageframes);
+ zdfs("eph_zpages", S_IRUGO, root, &zbud_eph_zpages);
+ zdfs("eph_pageframes", S_IRUGO, root, &zbud_eph_pageframes);
+ zdfs("eph_buddied_count", S_IRUGO, root, &zbud_eph_buddied_count);
+ zdfs("eph_unbuddied_count", S_IRUGO, root, &zbud_eph_unbuddied_count);
+ zdfs("pers_cumul_zpages", S_IRUGO, root, &zbud_pers_cumul_zpages);
+ zdfs("pers_evicted_pageframes", S_IRUGO, root,
+ &zbud_pers_evicted_pageframes);
+ zdfs("pers_zpages", S_IRUGO, root, &zbud_pers_zpages);
+ zdfs("pers_pageframes", S_IRUGO, root, &zbud_pers_pageframes);
+ zdfs("pers_buddied_count", S_IRUGO, root, &zbud_pers_buddied_count);
+ zdfs("pers_unbuddied_count", S_IRUGO, root, &zbud_pers_unbuddied_count);
+ zdfs("pers_zombie_count", S_IRUGO, root, &zbud_pers_zombie_count);
+ return 0;
+}
+#undef zdfs
+#undef zdfs64
+#endif
+
+/* protects the buddied list and all unbuddied lists */
+static DEFINE_SPINLOCK(zbud_eph_lists_lock);
+static DEFINE_SPINLOCK(zbud_pers_lists_lock);
+
+struct zbud_unbuddied {
+ struct list_head list;
+ unsigned count;
+};
+
+/* list N contains pages with N chunks USED and NCHUNKS-N unused */
+/* element 0 is never used but optimizing that isn't worth it */
+static struct zbud_unbuddied zbud_eph_unbuddied[NCHUNKS];
+static struct zbud_unbuddied zbud_pers_unbuddied[NCHUNKS];
+static LIST_HEAD(zbud_eph_lru_list);
+static LIST_HEAD(zbud_pers_lru_list);
+static LIST_HEAD(zbud_eph_buddied_list);
+static LIST_HEAD(zbud_pers_buddied_list);
+static LIST_HEAD(zbud_eph_zombie_list);
+static LIST_HEAD(zbud_pers_zombie_list);
+
+/*
+ * Given a struct page, transform it to a zbudpage so that it can be
+ * used by zbud and initialize fields as necessary.
+ */
+static inline struct zbudpage *zbud_init_zbudpage(struct page *page, bool eph)
+{
+ struct zbudpage *zbudpage = (struct zbudpage *)page;
+
+ BUG_ON(page == NULL);
+ INIT_LIST_HEAD(&zbudpage->budlist);
+ INIT_LIST_HEAD(&zbudpage->lru);
+ zbudpage->zbud0_size = 0;
+ zbudpage->zbud1_size = 0;
+ zbudpage->unevictable = 0;
+ if (eph)
+ zbud_eph_pageframes++;
+ else
+ zbud_pers_pageframes++;
+ return zbudpage;
+}
+
+/* "Transform" a zbudpage back to a struct page suitable to free. */
+static inline struct page *zbud_unuse_zbudpage(struct zbudpage *zbudpage,
+ bool eph)
+{
+ struct page *page = (struct page *)zbudpage;
+
+ BUG_ON(!list_empty(&zbudpage->budlist));
+ BUG_ON(!list_empty(&zbudpage->lru));
+ BUG_ON(zbudpage->zbud0_size != 0);
+ BUG_ON(zbudpage->zbud1_size != 0);
+ BUG_ON(!PageLocked(page));
+ BUG_ON(zbudpage->unevictable != 0);
+ BUG_ON(zbudpage_is_dying(zbudpage));
+ BUG_ON(zbudpage_is_zombie(zbudpage));
+ if (eph)
+ zbud_eph_pageframes--;
+ else
+ zbud_pers_pageframes--;
+ zbudpage_spin_unlock(zbudpage);
+ reset_page_mapcount(page);
+ init_page_count(page);
+ page->index = 0;
+ return page;
+}
+
+/* Mark a zbud as unused and do accounting */
+static inline void zbud_unuse_zbud(struct zbudpage *zbudpage,
+ int budnum, bool eph)
+{
+ unsigned size;
+
+ BUG_ON(!zbudpage_is_locked(zbudpage));
+ if (budnum == 0) {
+ size = zbudpage->zbud0_size;
+ zbudpage->zbud0_size = 0;
+ } else {
+ size = zbudpage->zbud1_size;
+ zbudpage->zbud1_size = 0;
+ }
+ if (eph) {
+ zbud_eph_zbytes -= size;
+ zbud_eph_zpages--;
+ } else {
+ zbud_pers_zbytes -= size;
+ zbud_pers_zpages--;
+ }
+}
+
+/*
+ * Given a zbudpage/budnum/size, a tmem handle, and a kmapped pointer
+ * to some data, set up the zbud appropriately including data copying
+ * and accounting. Note that if cdata is NULL, the data copying is
+ * skipped. (This is useful for lazy writes such as for RAMster.)
+ */
+static void zbud_init_zbud(struct zbudpage *zbudpage, struct tmem_handle *th,
+ bool eph, void *cdata,
+ unsigned budnum, unsigned size)
+{
+ char *to;
+ void *zbpg;
+ struct tmem_handle *to_th;
+ unsigned nchunks = zbud_size_to_chunks(size);
+
+ BUG_ON(!zbudpage_is_locked(zbudpage));
+ zbpg = kmap_zbudpage_atomic(zbudpage);
+ to = zbud_data(zbpg, budnum, size);
+ to_th = (struct tmem_handle *)to;
+ to_th->index = th->index;
+ to_th->oid = th->oid;
+ to_th->pool_id = th->pool_id;
+ to_th->client_id = th->client_id;
+ to += sizeof(struct tmem_handle);
+ if (cdata != NULL)
+ memcpy(to, cdata, size - sizeof(struct tmem_handle));
+ kunmap_zbudpage_atomic(zbpg);
+ if (budnum == 0)
+ zbudpage->zbud0_size = size;
+ else
+ zbudpage->zbud1_size = size;
+ if (eph) {
+ zbud_eph_cumul_chunk_counts[nchunks]++;
+ zbud_eph_zpages++;
+ zbud_eph_cumul_zpages++;
+ zbud_eph_zbytes += size;
+ zbud_eph_cumul_zbytes += size;
+ } else {
+ zbud_pers_cumul_chunk_counts[nchunks]++;
+ zbud_pers_zpages++;
+ zbud_pers_cumul_zpages++;
+ zbud_pers_zbytes += size;
+ zbud_pers_cumul_zbytes += size;
+ }
+}
+
+/*
+ * Given a locked dying zbudpage, read out the tmem handles from the data,
+ * unlock the page, then use the handles to tell tmem to flush out its
+ * references
+ */
+static void zbud_evict_tmem(struct zbudpage *zbudpage)
+{
+ int i, j;
+ uint32_t pool_id[2], client_id[2];
+ uint32_t index[2];
+ struct tmem_oid oid[2];
+ struct tmem_pool *pool;
+ void *zbpg;
+ struct tmem_handle *th;
+ unsigned size;
+
+ /* read out the tmem handles from the data and set aside */
+ zbpg = kmap_zbudpage_atomic(zbudpage);
+ for (i = 0, j = 0; i < 2; i++) {
+ size = (i == 0) ? zbudpage->zbud0_size : zbudpage->zbud1_size;
+ if (size) {
+ th = (struct tmem_handle *)zbud_data(zbpg, i, size);
+ client_id[j] = th->client_id;
+ pool_id[j] = th->pool_id;
+ oid[j] = th->oid;
+ index[j] = th->index;
+ j++;
+ zbud_unuse_zbud(zbudpage, i, true);
+ }
+ }
+ kunmap_zbudpage_atomic(zbpg);
+ zbudpage_spin_unlock(zbudpage);
+ /* zbudpage is now an unlocked dying... tell tmem to flush pointers */
+ for (i = 0; i < j; i++) {
+ pool = zcache_get_pool_by_id(client_id[i], pool_id[i]);
+ if (pool != NULL) {
+ tmem_flush_page(pool, &oid[i], index[i]);
+ zcache_put_pool(pool);
+ }
+ }
+}
+
+/*
+ * Externally callable zbud handling routines.
+ */
+
+/*
+ * Return the maximum size compressed page that can be stored (secretly
+ * setting aside space for the tmem handle.
+ */
+unsigned int zbud_max_buddy_size(void)
+{
+ return zbud_max_size() - sizeof(struct tmem_handle);
+}
+
+/*
+ * Given a zbud reference, free the corresponding zbud from all lists,
+ * mark it as unused, do accounting, and if the freeing of the zbud
+ * frees up an entire pageframe, return it to the caller (else NULL).
+ */
+struct page *zbud_free_and_delist(struct zbudref *zref, bool eph,
+ unsigned int *zsize, unsigned int *zpages)
+{
+ unsigned long budnum = zbudref_budnum(zref);
+ struct zbudpage *zbudpage = zbudref_to_zbudpage(zref);
+ struct page *page = NULL;
+ unsigned chunks, bud_size, other_bud_size;
+ spinlock_t *lists_lock =
+ eph ? &zbud_eph_lists_lock : &zbud_pers_lists_lock;
+ struct zbud_unbuddied *unbud =
+ eph ? zbud_eph_unbuddied : zbud_pers_unbuddied;
+
+
+ spin_lock(lists_lock);
+ zbudpage_spin_lock(zbudpage);
+ if (zbudpage_is_dying(zbudpage)) {
+ /* ignore dying zbudpage... see zbud_evict_pageframe_lru() */
+ zbudpage_spin_unlock(zbudpage);
+ spin_unlock(lists_lock);
+ *zpages = 0;
+ *zsize = 0;
+ goto out;
+ }
+ if (budnum == 0) {
+ bud_size = zbudpage->zbud0_size;
+ other_bud_size = zbudpage->zbud1_size;
+ } else {
+ bud_size = zbudpage->zbud1_size;
+ other_bud_size = zbudpage->zbud0_size;
+ }
+ *zsize = bud_size - sizeof(struct tmem_handle);
+ *zpages = 1;
+ zbud_unuse_zbud(zbudpage, budnum, eph);
+ if (other_bud_size == 0) { /* was unbuddied: unlist and free */
+ chunks = zbud_size_to_chunks(bud_size) ;
+ if (zbudpage_is_zombie(zbudpage)) {
+ if (eph)
+ zbud_pers_zombie_count =
+ atomic_dec_return(&zbud_eph_zombie_atomic);
+ else
+ zbud_pers_zombie_count =
+ atomic_dec_return(&zbud_pers_zombie_atomic);
+ zbudpage_clear_zombie(zbudpage);
+ } else {
+ BUG_ON(list_empty(&unbud[chunks].list));
+ list_del_init(&zbudpage->budlist);
+ unbud[chunks].count--;
+ }
+ list_del_init(&zbudpage->lru);
+ spin_unlock(lists_lock);
+ if (eph)
+ zbud_eph_unbuddied_count--;
+ else
+ zbud_pers_unbuddied_count--;
+ page = zbud_unuse_zbudpage(zbudpage, eph);
+ } else { /* was buddied: move remaining buddy to unbuddied list */
+ chunks = zbud_size_to_chunks(other_bud_size) ;
+ if (!zbudpage_is_zombie(zbudpage)) {
+ list_del_init(&zbudpage->budlist);
+ list_add_tail(&zbudpage->budlist, &unbud[chunks].list);
+ unbud[chunks].count++;
+ }
+ if (eph) {
+ zbud_eph_buddied_count--;
+ zbud_eph_unbuddied_count++;
+ } else {
+ zbud_pers_unbuddied_count++;
+ zbud_pers_buddied_count--;
+ }
+ /* don't mess with lru, no need to move it */
+ zbudpage_spin_unlock(zbudpage);
+ spin_unlock(lists_lock);
+ }
+out:
+ return page;
+}
+
+/*
+ * Given a tmem handle, and a kmapped pointer to compressed data of
+ * the given size, try to find an unbuddied zbudpage in which to
+ * create a zbud. If found, put it there, mark the zbudpage unevictable,
+ * and return a zbudref to it. Else return NULL.
+ */
+struct zbudref *zbud_match_prep(struct tmem_handle *th, bool eph,
+ void *cdata, unsigned size)
+{
+ struct zbudpage *zbudpage = NULL, *zbudpage2;
+ unsigned long budnum = 0UL;
+ unsigned nchunks;
+ int i, found_good_buddy = 0;
+ spinlock_t *lists_lock =
+ eph ? &zbud_eph_lists_lock : &zbud_pers_lists_lock;
+ struct zbud_unbuddied *unbud =
+ eph ? zbud_eph_unbuddied : zbud_pers_unbuddied;
+
+ size += sizeof(struct tmem_handle);
+ nchunks = zbud_size_to_chunks(size);
+ for (i = MAX_CHUNK - nchunks + 1; i > 0; i--) {
+ spin_lock(lists_lock);
+ if (!list_empty(&unbud[i].list)) {
+ list_for_each_entry_safe(zbudpage, zbudpage2,
+ &unbud[i].list, budlist) {
+ if (zbudpage_spin_trylock(zbudpage)) {
+ found_good_buddy = i;
+ goto found_unbuddied;
+ }
+ }
+ }
+ spin_unlock(lists_lock);
+ }
+ zbudpage = NULL;
+ goto out;
+
+found_unbuddied:
+ BUG_ON(!zbudpage_is_locked(zbudpage));
+ BUG_ON(!((zbudpage->zbud0_size == 0) ^ (zbudpage->zbud1_size == 0)));
+ if (zbudpage->zbud0_size == 0)
+ budnum = 0UL;
+ else if (zbudpage->zbud1_size == 0)
+ budnum = 1UL;
+ list_del_init(&zbudpage->budlist);
+ if (eph) {
+ list_add_tail(&zbudpage->budlist, &zbud_eph_buddied_list);
+ unbud[found_good_buddy].count--;
+ zbud_eph_unbuddied_count--;
+ zbud_eph_buddied_count++;
+ /* "promote" raw zbudpage to most-recently-used */
+ list_del_init(&zbudpage->lru);
+ list_add_tail(&zbudpage->lru, &zbud_eph_lru_list);
+ } else {
+ list_add_tail(&zbudpage->budlist, &zbud_pers_buddied_list);
+ unbud[found_good_buddy].count--;
+ zbud_pers_unbuddied_count--;
+ zbud_pers_buddied_count++;
+ /* "promote" raw zbudpage to most-recently-used */
+ list_del_init(&zbudpage->lru);
+ list_add_tail(&zbudpage->lru, &zbud_pers_lru_list);
+ }
+ zbud_init_zbud(zbudpage, th, eph, cdata, budnum, size);
+ zbudpage->unevictable++;
+ BUG_ON(zbudpage->unevictable == 3);
+ zbudpage_spin_unlock(zbudpage);
+ spin_unlock(lists_lock);
+out:
+ return zbudpage_to_zbudref(zbudpage, budnum);
+
+}
+
+/*
+ * Given a tmem handle, and a kmapped pointer to compressed data of
+ * the given size, and a newly allocated struct page, create an unevictable
+ * zbud in that new page and return a zbudref to it.
+ */
+struct zbudref *zbud_create_prep(struct tmem_handle *th, bool eph,
+ void *cdata, unsigned size,
+ struct page *newpage)
+{
+ struct zbudpage *zbudpage;
+ unsigned long budnum = 0;
+ unsigned nchunks;
+ spinlock_t *lists_lock =
+ eph ? &zbud_eph_lists_lock : &zbud_pers_lists_lock;
+ struct zbud_unbuddied *unbud =
+ eph ? zbud_eph_unbuddied : zbud_pers_unbuddied;
+
+#if 0
+ /* this may be worth it later to support decompress-in-place? */
+ static unsigned long counter;
+ budnum = counter++ & 1; /* alternate using zbud0 and zbud1 */
+#endif
+
+ if (size > zbud_max_buddy_size())
+ return NULL;
+ if (newpage == NULL)
+ return NULL;
+
+ size += sizeof(struct tmem_handle);
+ nchunks = zbud_size_to_chunks(size) ;
+ spin_lock(lists_lock);
+ zbudpage = zbud_init_zbudpage(newpage, eph);
+ zbudpage_spin_lock(zbudpage);
+ list_add_tail(&zbudpage->budlist, &unbud[nchunks].list);
+ if (eph) {
+ list_add_tail(&zbudpage->lru, &zbud_eph_lru_list);
+ zbud_eph_unbuddied_count++;
+ } else {
+ list_add_tail(&zbudpage->lru, &zbud_pers_lru_list);
+ zbud_pers_unbuddied_count++;
+ }
+ unbud[nchunks].count++;
+ zbud_init_zbud(zbudpage, th, eph, cdata, budnum, size);
+ zbudpage->unevictable++;
+ BUG_ON(zbudpage->unevictable == 3);
+ zbudpage_spin_unlock(zbudpage);
+ spin_unlock(lists_lock);
+ return zbudpage_to_zbudref(zbudpage, budnum);
+}
+
+/*
+ * Finish creation of a zbud by, assuming another zbud isn't being created
+ * in parallel, marking it evictable.
+ */
+void zbud_create_finish(struct zbudref *zref, bool eph)
+{
+ struct zbudpage *zbudpage = zbudref_to_zbudpage(zref);
+ spinlock_t *lists_lock =
+ eph ? &zbud_eph_lists_lock : &zbud_pers_lists_lock;
+
+ spin_lock(lists_lock);
+ zbudpage_spin_lock(zbudpage);
+ BUG_ON(zbudpage_is_dying(zbudpage));
+ zbudpage->unevictable--;
+ BUG_ON((int)zbudpage->unevictable < 0);
+ zbudpage_spin_unlock(zbudpage);
+ spin_unlock(lists_lock);
+}
+
+/*
+ * Given a zbudref and a struct page, decompress the data from
+ * the zbud into the physical page represented by the struct page
+ * by upcalling to zcache_decompress
+ */
+int zbud_decompress(struct page *data_page, struct zbudref *zref, bool eph,
+ void (*decompress)(char *, unsigned int, char *))
+{
+ struct zbudpage *zbudpage = zbudref_to_zbudpage(zref);
+ unsigned long budnum = zbudref_budnum(zref);
+ void *zbpg;
+ char *to_va, *from_va;
+ unsigned size;
+ int ret = -1;
+ spinlock_t *lists_lock =
+ eph ? &zbud_eph_lists_lock : &zbud_pers_lists_lock;
+
+ spin_lock(lists_lock);
+ zbudpage_spin_lock(zbudpage);
+ if (zbudpage_is_dying(zbudpage)) {
+ /* ignore dying zbudpage... see zbud_evict_pageframe_lru() */
+ goto out;
+ }
+ zbpg = kmap_zbudpage_atomic(zbudpage);
+ to_va = kmap_atomic(data_page);
+ if (budnum == 0)
+ size = zbudpage->zbud0_size;
+ else
+ size = zbudpage->zbud1_size;
+ BUG_ON(size == 0 || size > zbud_max_size());
+ from_va = zbud_data(zbpg, budnum, size);
+ from_va += sizeof(struct tmem_handle);
+ size -= sizeof(struct tmem_handle);
+ decompress(from_va, size, to_va);
+ kunmap_atomic(to_va);
+ kunmap_zbudpage_atomic(zbpg);
+ ret = 0;
+out:
+ zbudpage_spin_unlock(zbudpage);
+ spin_unlock(lists_lock);
+ return ret;
+}
+
+/*
+ * Given a zbudref and a kernel pointer, copy the data from
+ * the zbud to the kernel pointer.
+ */
+int zbud_copy_from_zbud(char *to_va, struct zbudref *zref,
+ size_t *sizep, bool eph)
+{
+ struct zbudpage *zbudpage = zbudref_to_zbudpage(zref);
+ unsigned long budnum = zbudref_budnum(zref);
+ void *zbpg;
+ char *from_va;
+ unsigned size;
+ int ret = -1;
+ spinlock_t *lists_lock =
+ eph ? &zbud_eph_lists_lock : &zbud_pers_lists_lock;
+
+ spin_lock(lists_lock);
+ zbudpage_spin_lock(zbudpage);
+ if (zbudpage_is_dying(zbudpage)) {
+ /* ignore dying zbudpage... see zbud_evict_pageframe_lru() */
+ goto out;
+ }
+ zbpg = kmap_zbudpage_atomic(zbudpage);
+ if (budnum == 0)
+ size = zbudpage->zbud0_size;
+ else
+ size = zbudpage->zbud1_size;
+ BUG_ON(size == 0 || size > zbud_max_size());
+ from_va = zbud_data(zbpg, budnum, size);
+ from_va += sizeof(struct tmem_handle);
+ size -= sizeof(struct tmem_handle);
+ *sizep = size;
+ memcpy(to_va, from_va, size);
+
+ kunmap_zbudpage_atomic(zbpg);
+ ret = 0;
+out:
+ zbudpage_spin_unlock(zbudpage);
+ spin_unlock(lists_lock);
+ return ret;
+}
+
+/*
+ * Given a zbudref and a kernel pointer, copy the data from
+ * the kernel pointer to the zbud.
+ */
+int zbud_copy_to_zbud(struct zbudref *zref, char *from_va, bool eph)
+{
+ struct zbudpage *zbudpage = zbudref_to_zbudpage(zref);
+ unsigned long budnum = zbudref_budnum(zref);
+ void *zbpg;
+ char *to_va;
+ unsigned size;
+ int ret = -1;
+ spinlock_t *lists_lock =
+ eph ? &zbud_eph_lists_lock : &zbud_pers_lists_lock;
+
+ spin_lock(lists_lock);
+ zbudpage_spin_lock(zbudpage);
+ if (zbudpage_is_dying(zbudpage)) {
+ /* ignore dying zbudpage... see zbud_evict_pageframe_lru() */
+ goto out;
+ }
+ zbpg = kmap_zbudpage_atomic(zbudpage);
+ if (budnum == 0)
+ size = zbudpage->zbud0_size;
+ else
+ size = zbudpage->zbud1_size;
+ BUG_ON(size == 0 || size > zbud_max_size());
+ to_va = zbud_data(zbpg, budnum, size);
+ to_va += sizeof(struct tmem_handle);
+ size -= sizeof(struct tmem_handle);
+ memcpy(to_va, from_va, size);
+
+ kunmap_zbudpage_atomic(zbpg);
+ ret = 0;
+out:
+ zbudpage_spin_unlock(zbudpage);
+ spin_unlock(lists_lock);
+ return ret;
+}
+
+/*
+ * Choose an ephemeral LRU zbudpage that is evictable (not locked), ensure
+ * there are no references to it remaining, and return the now unused
+ * (and re-init'ed) struct page and the total amount of compressed
+ * data that was evicted.
+ */
+struct page *zbud_evict_pageframe_lru(unsigned int *zsize, unsigned int *zpages)
+{
+ struct zbudpage *zbudpage = NULL, *zbudpage2;
+ struct zbud_unbuddied *unbud = zbud_eph_unbuddied;
+ struct page *page = NULL;
+ bool irqs_disabled = irqs_disabled();
+
+ /*
+ * Since this can be called indirectly from cleancache_put, which
+ * has interrupts disabled, as well as frontswap_put, which does not,
+ * we need to be able to handle both cases, even though it is ugly.
+ */
+ if (irqs_disabled)
+ spin_lock(&zbud_eph_lists_lock);
+ else
+ spin_lock_bh(&zbud_eph_lists_lock);
+ *zsize = 0;
+ if (list_empty(&zbud_eph_lru_list))
+ goto unlock_out;
+ list_for_each_entry_safe(zbudpage, zbudpage2, &zbud_eph_lru_list, lru) {
+ /* skip a locked zbudpage */
+ if (unlikely(!zbudpage_spin_trylock(zbudpage)))
+ continue;
+ /* skip an unevictable zbudpage */
+ if (unlikely(zbudpage->unevictable != 0)) {
+ zbudpage_spin_unlock(zbudpage);
+ continue;
+ }
+ /* got a locked evictable page */
+ goto evict_page;
+
+ }
+unlock_out:
+ /* no unlocked evictable pages, give up */
+ if (irqs_disabled)
+ spin_unlock(&zbud_eph_lists_lock);
+ else
+ spin_unlock_bh(&zbud_eph_lists_lock);
+ goto out;
+
+evict_page:
+ list_del_init(&zbudpage->budlist);
+ list_del_init(&zbudpage->lru);
+ zbudpage_set_dying(zbudpage);
+ /*
+ * the zbudpage is now "dying" and attempts to read, write,
+ * or delete data from it will be ignored
+ */
+ if (zbudpage->zbud0_size != 0 && zbudpage->zbud1_size != 0) {
+ *zsize = zbudpage->zbud0_size + zbudpage->zbud1_size -
+ (2 * sizeof(struct tmem_handle));
+ *zpages = 2;
+ } else if (zbudpage->zbud0_size != 0) {
+ unbud[zbud_size_to_chunks(zbudpage->zbud0_size)].count--;
+ *zsize = zbudpage->zbud0_size - sizeof(struct tmem_handle);
+ *zpages = 1;
+ } else if (zbudpage->zbud1_size != 0) {
+ unbud[zbud_size_to_chunks(zbudpage->zbud1_size)].count--;
+ *zsize = zbudpage->zbud1_size - sizeof(struct tmem_handle);
+ *zpages = 1;
+ } else {
+ BUG();
+ }
+ spin_unlock(&zbud_eph_lists_lock);
+ zbud_eph_evicted_pageframes++;
+ if (*zpages == 1)
+ zbud_eph_unbuddied_count--;
+ else
+ zbud_eph_buddied_count--;
+ zbud_evict_tmem(zbudpage);
+ zbudpage_spin_lock(zbudpage);
+ zbudpage_clear_dying(zbudpage);
+ page = zbud_unuse_zbudpage(zbudpage, true);
+ if (!irqs_disabled)
+ local_bh_enable();
+out:
+ return page;
+}
+
+/*
+ * Choose a persistent LRU zbudpage that is evictable (not locked), zombify it,
+ * read the tmem_handle(s) out of it into the passed array, and return the
+ * number of zbuds. Caller must perform necessary tmem functions and,
+ * indirectly, zbud functions to fetch any valid data and cause the
+ * now-zombified zbudpage to eventually be freed. We track the zombified
+ * zbudpage count so it is possible to observe if there is a leak.
+ FIXME: describe (ramster) case where data pointers are passed in for memcpy
+ */
+unsigned int zbud_make_zombie_lru(struct tmem_handle *th, unsigned char **data,
+ unsigned int *zsize, bool eph)
+{
+ struct zbudpage *zbudpage = NULL, *zbudpag2;
+ struct tmem_handle *thfrom;
+ char *from_va;
+ void *zbpg;
+ unsigned size;
+ int ret = 0, i;
+ spinlock_t *lists_lock =
+ eph ? &zbud_eph_lists_lock : &zbud_pers_lists_lock;
+ struct list_head *lru_list =
+ eph ? &zbud_eph_lru_list : &zbud_pers_lru_list;
+
+ spin_lock_bh(lists_lock);
+ if (list_empty(lru_list))
+ goto out;
+ list_for_each_entry_safe(zbudpage, zbudpag2, lru_list, lru) {
+ /* skip a locked zbudpage */
+ if (unlikely(!zbudpage_spin_trylock(zbudpage)))
+ continue;
+ /* skip an unevictable zbudpage */
+ if (unlikely(zbudpage->unevictable != 0)) {
+ zbudpage_spin_unlock(zbudpage);
+ continue;
+ }
+ /* got a locked evictable page */
+ goto zombify_page;
+ }
+ /* no unlocked evictable pages, give up */
+ goto out;
+
+zombify_page:
+ /* got an unlocked evictable page, zombify it */
+ list_del_init(&zbudpage->budlist);
+ zbudpage_set_zombie(zbudpage);
+ /* FIXME what accounting do I need to do here? */
+ list_del_init(&zbudpage->lru);
+ if (eph) {
+ list_add_tail(&zbudpage->lru, &zbud_eph_zombie_list);
+ zbud_eph_zombie_count =
+ atomic_inc_return(&zbud_eph_zombie_atomic);
+ } else {
+ list_add_tail(&zbudpage->lru, &zbud_pers_zombie_list);
+ zbud_pers_zombie_count =
+ atomic_inc_return(&zbud_pers_zombie_atomic);
+ }
+ /* FIXME what accounting do I need to do here? */
+ zbpg = kmap_zbudpage_atomic(zbudpage);
+ for (i = 0; i < 2; i++) {
+ size = (i == 0) ? zbudpage->zbud0_size : zbudpage->zbud1_size;
+ if (size) {
+ from_va = zbud_data(zbpg, i, size);
+ thfrom = (struct tmem_handle *)from_va;
+ from_va += sizeof(struct tmem_handle);
+ size -= sizeof(struct tmem_handle);
+ if (th != NULL)
+ th[ret] = *thfrom;
+ if (data != NULL)
+ memcpy(data[ret], from_va, size);
+ if (zsize != NULL)
+ *zsize++ = size;
+ ret++;
+ }
+ }
+ kunmap_zbudpage_atomic(zbpg);
+ zbudpage_spin_unlock(zbudpage);
+out:
+ spin_unlock_bh(lists_lock);
+ return ret;
+}
+
+void __init zbud_init(void)
+{
+ int i;
+
+#ifdef CONFIG_DEBUG_FS
+ zbud_debugfs_init();
+#endif
+ BUG_ON((sizeof(struct tmem_handle) * 2 > CHUNK_SIZE));
+ BUG_ON(sizeof(struct zbudpage) > sizeof(struct page));
+ for (i = 0; i < NCHUNKS; i++) {
+ INIT_LIST_HEAD(&zbud_eph_unbuddied[i].list);
+ INIT_LIST_HEAD(&zbud_pers_unbuddied[i].list);
+ }
+}
diff --git a/drivers/staging/ramster/zbud.h b/drivers/staging/ramster/zbud.h
new file mode 100644
index 000000000000..891e8a7d5aa5
--- /dev/null
+++ b/drivers/staging/ramster/zbud.h
@@ -0,0 +1,33 @@
+/*
+ * zbud.h
+ *
+ * Copyright (c) 2010-2012, Dan Magenheimer, Oracle Corp.
+ *
+ */
+
+#ifndef _ZBUD_H_
+#define _ZBUD_H_
+
+#include "tmem.h"
+
+struct zbudref;
+
+extern unsigned int zbud_max_buddy_size(void);
+extern struct zbudref *zbud_match_prep(struct tmem_handle *th, bool eph,
+ void *cdata, unsigned size);
+extern struct zbudref *zbud_create_prep(struct tmem_handle *th, bool eph,
+ void *cdata, unsigned size,
+ struct page *newpage);
+extern void zbud_create_finish(struct zbudref *, bool);
+extern int zbud_decompress(struct page *, struct zbudref *, bool,
+ void (*func)(char *, unsigned int, char *));
+extern int zbud_copy_from_zbud(char *, struct zbudref *, size_t *, bool);
+extern int zbud_copy_to_zbud(struct zbudref *, char *, bool);
+extern struct page *zbud_free_and_delist(struct zbudref *, bool eph,
+ unsigned int *, unsigned int *);
+extern struct page *zbud_evict_pageframe_lru(unsigned int *, unsigned int *);
+extern unsigned int zbud_make_zombie_lru(struct tmem_handle *, unsigned char **,
+ unsigned int *, bool);
+extern void zbud_init(void);
+
+#endif /* _ZBUD_H_ */
diff --git a/drivers/staging/ramster/zcache-main.c b/drivers/staging/ramster/zcache-main.c
index d46764b5aaba..a09dd5cc1cea 100644
--- a/drivers/staging/ramster/zcache-main.c
+++ b/drivers/staging/ramster/zcache-main.c
@@ -5,1392 +5,322 @@
* Copyright (c) 2010,2011, Nitin Gupta
*
* Zcache provides an in-kernel "host implementation" for transcendent memory
- * and, thus indirectly, for cleancache and frontswap. Zcache includes two
- * page-accessible memory [1] interfaces, both utilizing lzo1x compression:
- * 1) "compression buddies" ("zbud") is used for ephemeral pages
- * 2) xvmalloc is used for persistent pages.
- * Xvmalloc (based on the TLSF allocator) has very low fragmentation
- * so maximizes space efficiency, while zbud allows pairs (and potentially,
- * in the future, more than a pair of) compressed pages to be closely linked
- * so that reclaiming can be done via the kernel's physical-page-oriented
- * "shrinker" interface.
- *
- * [1] For a definition of page-accessible memory (aka PAM), see:
- * http://marc.info/?l=linux-mm&m=127811271605009
- * RAMSTER TODO:
- * - handle remotifying of buddied pages (see zbud_remotify_zbpg)
- * - kernel boot params: nocleancache/nofrontswap don't always work?!?
+ * ("tmem") and, thus indirectly, for cleancache and frontswap. Zcache uses
+ * lzo1x compression to improve density and an embedded allocator called
+ * "zbud" which "buddies" two compressed pages semi-optimally in each physical
+ * pageframe. Zbud is integrally tied into tmem to allow pageframes to
+ * be "reclaimed" efficiently.
*/
#include <linux/module.h>
#include <linux/cpu.h>
#include <linux/highmem.h>
#include <linux/list.h>
-#include <linux/lzo.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/math64.h>
-#include "tmem.h"
-#include "zcache.h"
-#include "ramster.h"
-#include "cluster/tcp.h"
+#include <linux/crypto.h>
-#include "xvmalloc.h" /* temporary until change to zsmalloc */
-
-#define RAMSTER_TESTING
-
-#if (!defined(CONFIG_CLEANCACHE) && !defined(CONFIG_FRONTSWAP))
-#error "ramster is useless without CONFIG_CLEANCACHE or CONFIG_FRONTSWAP"
-#endif
-#ifdef CONFIG_CLEANCACHE
#include <linux/cleancache.h>
-#endif
-#ifdef CONFIG_FRONTSWAP
#include <linux/frontswap.h>
-#endif
-
-enum ramster_remotify_op {
- RAMSTER_REMOTIFY_EPH_PUT,
- RAMSTER_REMOTIFY_PERS_PUT,
- RAMSTER_REMOTIFY_FLUSH_PAGE,
- RAMSTER_REMOTIFY_FLUSH_OBJ,
- RAMSTER_INTRANSIT_PERS
-};
-
-struct ramster_remotify_hdr {
- enum ramster_remotify_op op;
- struct list_head list;
-};
-
-#define ZBH_SENTINEL 0x43214321
-#define ZBPG_SENTINEL 0xdeadbeef
-
-#define ZBUD_MAX_BUDS 2
-
-struct zbud_hdr {
- struct ramster_remotify_hdr rem_op;
- uint16_t client_id;
- uint16_t pool_id;
- struct tmem_oid oid;
- uint32_t index;
- uint16_t size; /* compressed size in bytes, zero means unused */
- DECL_SENTINEL
-};
-
-#define ZVH_SENTINEL 0x43214321
-static const int zv_max_page_size = (PAGE_SIZE / 8) * 7;
-
-struct zv_hdr {
- struct ramster_remotify_hdr rem_op;
- uint16_t client_id;
- uint16_t pool_id;
- struct tmem_oid oid;
- uint32_t index;
- DECL_SENTINEL
-};
-
-struct flushlist_node {
- struct ramster_remotify_hdr rem_op;
- struct tmem_xhandle xh;
-};
-
-union {
- struct ramster_remotify_hdr rem_op;
- struct zv_hdr zv;
- struct zbud_hdr zbud;
- struct flushlist_node flist;
-} remotify_list_node;
-
-static LIST_HEAD(zcache_rem_op_list);
-static DEFINE_SPINLOCK(zcache_rem_op_list_lock);
-
-#if 0
-/* this is more aggressive but may cause other problems? */
-#define ZCACHE_GFP_MASK (GFP_ATOMIC | __GFP_NORETRY | __GFP_NOWARN)
+#include "tmem.h"
+#include "zcache.h"
+#include "zbud.h"
+#include "ramster.h"
+#ifdef CONFIG_RAMSTER
+static int ramster_enabled;
#else
-#define ZCACHE_GFP_MASK \
- (__GFP_FS | __GFP_NORETRY | __GFP_NOWARN | __GFP_NOMEMALLOC)
+#define ramster_enabled 0
#endif
-#define MAX_POOLS_PER_CLIENT 16
-
-#define MAX_CLIENTS 16
-#define LOCAL_CLIENT ((uint16_t)-1)
-
-MODULE_LICENSE("GPL");
-
-struct zcache_client {
- struct tmem_pool *tmem_pools[MAX_POOLS_PER_CLIENT];
- struct xv_pool *xvpool;
- bool allocated;
- atomic_t refcount;
-};
-
-static struct zcache_client zcache_host;
-static struct zcache_client zcache_clients[MAX_CLIENTS];
-
-static inline uint16_t get_client_id_from_client(struct zcache_client *cli)
+#ifndef __PG_WAS_ACTIVE
+static inline bool PageWasActive(struct page *page)
{
- BUG_ON(cli == NULL);
- if (cli == &zcache_host)
- return LOCAL_CLIENT;
- return cli - &zcache_clients[0];
+ return true;
}
-static inline bool is_local_client(struct zcache_client *cli)
+static inline void SetPageWasActive(struct page *page)
{
- return cli == &zcache_host;
-}
-
-/**********
- * Compression buddies ("zbud") provides for packing two (or, possibly
- * in the future, more) compressed ephemeral pages into a single "raw"
- * (physical) page and tracking them with data structures so that
- * the raw pages can be easily reclaimed.
- *
- * A zbud page ("zbpg") is an aligned page containing a list_head,
- * a lock, and two "zbud headers". The remainder of the physical
- * page is divided up into aligned 64-byte "chunks" which contain
- * the compressed data for zero, one, or two zbuds. Each zbpg
- * resides on: (1) an "unused list" if it has no zbuds; (2) a
- * "buddied" list if it is fully populated with two zbuds; or
- * (3) one of PAGE_SIZE/64 "unbuddied" lists indexed by how many chunks
- * the one unbuddied zbud uses. The data inside a zbpg cannot be
- * read or written unless the zbpg's lock is held.
- */
-
-struct zbud_page {
- struct list_head bud_list;
- spinlock_t lock;
- struct zbud_hdr buddy[ZBUD_MAX_BUDS];
- DECL_SENTINEL
- /* followed by NUM_CHUNK aligned CHUNK_SIZE-byte chunks */
-};
-
-#define CHUNK_SHIFT 6
-#define CHUNK_SIZE (1 << CHUNK_SHIFT)
-#define CHUNK_MASK (~(CHUNK_SIZE-1))
-#define NCHUNKS (((PAGE_SIZE - sizeof(struct zbud_page)) & \
- CHUNK_MASK) >> CHUNK_SHIFT)
-#define MAX_CHUNK (NCHUNKS-1)
-
-static struct {
- struct list_head list;
- unsigned count;
-} zbud_unbuddied[NCHUNKS];
-/* list N contains pages with N chunks USED and NCHUNKS-N unused */
-/* element 0 is never used but optimizing that isn't worth it */
-static unsigned long zbud_cumul_chunk_counts[NCHUNKS];
-
-struct list_head zbud_buddied_list;
-static unsigned long zcache_zbud_buddied_count;
-
-/* protects the buddied list and all unbuddied lists */
-static DEFINE_SPINLOCK(zbud_budlists_spinlock);
-
-static atomic_t zcache_zbud_curr_raw_pages;
-static atomic_t zcache_zbud_curr_zpages;
-static unsigned long zcache_zbud_curr_zbytes;
-static unsigned long zcache_zbud_cumul_zpages;
-static unsigned long zcache_zbud_cumul_zbytes;
-static unsigned long zcache_compress_poor;
-static unsigned long zcache_policy_percent_exceeded;
-static unsigned long zcache_mean_compress_poor;
-
-/*
- * RAMster counters
- * - Remote pages are pages with a local pampd but the data is remote
- * - Foreign pages are pages stored locally but belonging to another node
- */
-static atomic_t ramster_remote_pers_pages = ATOMIC_INIT(0);
-static unsigned long ramster_pers_remotify_enable;
-static unsigned long ramster_eph_remotify_enable;
-static unsigned long ramster_eph_pages_remoted;
-static unsigned long ramster_eph_pages_remote_failed;
-static unsigned long ramster_pers_pages_remoted;
-static unsigned long ramster_pers_pages_remote_failed;
-static unsigned long ramster_pers_pages_remote_nomem;
-static unsigned long ramster_remote_objects_flushed;
-static unsigned long ramster_remote_object_flushes_failed;
-static unsigned long ramster_remote_pages_flushed;
-static unsigned long ramster_remote_page_flushes_failed;
-static unsigned long ramster_remote_eph_pages_succ_get;
-static unsigned long ramster_remote_pers_pages_succ_get;
-static unsigned long ramster_remote_eph_pages_unsucc_get;
-static unsigned long ramster_remote_pers_pages_unsucc_get;
-static atomic_t ramster_curr_flnode_count = ATOMIC_INIT(0);
-static unsigned long ramster_curr_flnode_count_max;
-static atomic_t ramster_foreign_eph_pampd_count = ATOMIC_INIT(0);
-static unsigned long ramster_foreign_eph_pampd_count_max;
-static atomic_t ramster_foreign_pers_pampd_count = ATOMIC_INIT(0);
-static unsigned long ramster_foreign_pers_pampd_count_max;
-
-/* forward references */
-static void *zcache_get_free_page(void);
-static void zcache_free_page(void *p);
-
-/*
- * zbud helper functions
- */
-
-static inline unsigned zbud_max_buddy_size(void)
-{
- return MAX_CHUNK << CHUNK_SHIFT;
}
+#endif
-static inline unsigned zbud_size_to_chunks(unsigned size)
+#ifdef FRONTSWAP_HAS_EXCLUSIVE_GETS
+static bool frontswap_has_exclusive_gets __read_mostly = true;
+#else
+static bool frontswap_has_exclusive_gets __read_mostly;
+static inline void frontswap_tmem_exclusive_gets(bool b)
{
- BUG_ON(size == 0 || size > zbud_max_buddy_size());
- return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
-}
-
-static inline int zbud_budnum(struct zbud_hdr *zh)
-{
- unsigned offset = (unsigned long)zh & (PAGE_SIZE - 1);
- struct zbud_page *zbpg = NULL;
- unsigned budnum = -1U;
- int i;
-
- for (i = 0; i < ZBUD_MAX_BUDS; i++)
- if (offset == offsetof(typeof(*zbpg), buddy[i])) {
- budnum = i;
- break;
- }
- BUG_ON(budnum == -1U);
- return budnum;
-}
-
-static char *zbud_data(struct zbud_hdr *zh, unsigned size)
-{
- struct zbud_page *zbpg;
- char *p;
- unsigned budnum;
-
- ASSERT_SENTINEL(zh, ZBH);
- budnum = zbud_budnum(zh);
- BUG_ON(size == 0 || size > zbud_max_buddy_size());
- zbpg = container_of(zh, struct zbud_page, buddy[budnum]);
- ASSERT_SPINLOCK(&zbpg->lock);
- p = (char *)zbpg;
- if (budnum == 0)
- p += ((sizeof(struct zbud_page) + CHUNK_SIZE - 1) &
- CHUNK_MASK);
- else if (budnum == 1)
- p += PAGE_SIZE - ((size + CHUNK_SIZE - 1) & CHUNK_MASK);
- return p;
-}
-
-static void zbud_copy_from_pampd(char *data, size_t *size, struct zbud_hdr *zh)
-{
- struct zbud_page *zbpg;
- char *p;
- unsigned budnum;
-
- ASSERT_SENTINEL(zh, ZBH);
- budnum = zbud_budnum(zh);
- zbpg = container_of(zh, struct zbud_page, buddy[budnum]);
- spin_lock(&zbpg->lock);
- BUG_ON(zh->size > *size);
- p = (char *)zbpg;
- if (budnum == 0)
- p += ((sizeof(struct zbud_page) + CHUNK_SIZE - 1) &
- CHUNK_MASK);
- else if (budnum == 1)
- p += PAGE_SIZE - ((zh->size + CHUNK_SIZE - 1) & CHUNK_MASK);
- /* client should be filled in by caller */
- memcpy(data, p, zh->size);
- *size = zh->size;
- spin_unlock(&zbpg->lock);
-}
-
-/*
- * zbud raw page management
- */
-
-static struct zbud_page *zbud_alloc_raw_page(void)
-{
- struct zbud_page *zbpg = NULL;
- struct zbud_hdr *zh0, *zh1;
- zbpg = zcache_get_free_page();
- if (likely(zbpg != NULL)) {
- INIT_LIST_HEAD(&zbpg->bud_list);
- zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1];
- spin_lock_init(&zbpg->lock);
- atomic_inc(&zcache_zbud_curr_raw_pages);
- INIT_LIST_HEAD(&zbpg->bud_list);
- SET_SENTINEL(zbpg, ZBPG);
- zh0->size = 0; zh1->size = 0;
- tmem_oid_set_invalid(&zh0->oid);
- tmem_oid_set_invalid(&zh1->oid);
- }
- return zbpg;
}
+#endif
-static void zbud_free_raw_page(struct zbud_page *zbpg)
-{
- struct zbud_hdr *zh0 = &zbpg->buddy[0], *zh1 = &zbpg->buddy[1];
+static int zcache_enabled __read_mostly;
+static int disable_cleancache __read_mostly;
+static int disable_frontswap __read_mostly;
+static int disable_frontswap_ignore_nonactive __read_mostly;
+static int disable_cleancache_ignore_nonactive __read_mostly;
+static char *namestr __read_mostly = "zcache";
- ASSERT_SENTINEL(zbpg, ZBPG);
- BUG_ON(!list_empty(&zbpg->bud_list));
- ASSERT_SPINLOCK(&zbpg->lock);
- BUG_ON(zh0->size != 0 || tmem_oid_valid(&zh0->oid));
- BUG_ON(zh1->size != 0 || tmem_oid_valid(&zh1->oid));
- INVERT_SENTINEL(zbpg, ZBPG);
- spin_unlock(&zbpg->lock);
- atomic_dec(&zcache_zbud_curr_raw_pages);
- zcache_free_page(zbpg);
-}
+#define ZCACHE_GFP_MASK \
+ (__GFP_FS | __GFP_NORETRY | __GFP_NOWARN | __GFP_NOMEMALLOC)
-/*
- * core zbud handling routines
- */
+MODULE_LICENSE("GPL");
-static unsigned zbud_free(struct zbud_hdr *zh)
-{
- unsigned size;
-
- ASSERT_SENTINEL(zh, ZBH);
- BUG_ON(!tmem_oid_valid(&zh->oid));
- size = zh->size;
- BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size());
- zh->size = 0;
- tmem_oid_set_invalid(&zh->oid);
- INVERT_SENTINEL(zh, ZBH);
- zcache_zbud_curr_zbytes -= size;
- atomic_dec(&zcache_zbud_curr_zpages);
- return size;
-}
-
-static void zbud_free_and_delist(struct zbud_hdr *zh)
-{
- unsigned chunks;
- struct zbud_hdr *zh_other;
- unsigned budnum = zbud_budnum(zh), size;
- struct zbud_page *zbpg =
- container_of(zh, struct zbud_page, buddy[budnum]);
-
- /* FIXME, should be BUG_ON, pool destruction path doesn't disable
- * interrupts tmem_destroy_pool()->tmem_pampd_destroy_all_in_obj()->
- * tmem_objnode_node_destroy()-> zcache_pampd_free() */
- WARN_ON(!irqs_disabled());
- spin_lock(&zbpg->lock);
- if (list_empty(&zbpg->bud_list)) {
- /* ignore zombie page... see zbud_evict_pages() */
- spin_unlock(&zbpg->lock);
- return;
- }
- size = zbud_free(zh);
- ASSERT_SPINLOCK(&zbpg->lock);
- zh_other = &zbpg->buddy[(budnum == 0) ? 1 : 0];
- if (zh_other->size == 0) { /* was unbuddied: unlist and free */
- chunks = zbud_size_to_chunks(size) ;
- spin_lock(&zbud_budlists_spinlock);
- BUG_ON(list_empty(&zbud_unbuddied[chunks].list));
- list_del_init(&zbpg->bud_list);
- zbud_unbuddied[chunks].count--;
- spin_unlock(&zbud_budlists_spinlock);
- zbud_free_raw_page(zbpg);
- } else { /* was buddied: move remaining buddy to unbuddied list */
- chunks = zbud_size_to_chunks(zh_other->size) ;
- spin_lock(&zbud_budlists_spinlock);
- list_del_init(&zbpg->bud_list);
- zcache_zbud_buddied_count--;
- list_add_tail(&zbpg->bud_list, &zbud_unbuddied[chunks].list);
- zbud_unbuddied[chunks].count++;
- spin_unlock(&zbud_budlists_spinlock);
- spin_unlock(&zbpg->lock);
- }
-}
+/* crypto API for zcache */
+#define ZCACHE_COMP_NAME_SZ CRYPTO_MAX_ALG_NAME
+static char zcache_comp_name[ZCACHE_COMP_NAME_SZ] __read_mostly;
+static struct crypto_comp * __percpu *zcache_comp_pcpu_tfms __read_mostly;
-static struct zbud_hdr *zbud_create(uint16_t client_id, uint16_t pool_id,
- struct tmem_oid *oid,
- uint32_t index, struct page *page,
- void *cdata, unsigned size)
-{
- struct zbud_hdr *zh0, *zh1, *zh = NULL;
- struct zbud_page *zbpg = NULL, *ztmp;
- unsigned nchunks;
- char *to;
- int i, found_good_buddy = 0;
-
- nchunks = zbud_size_to_chunks(size) ;
- for (i = MAX_CHUNK - nchunks + 1; i > 0; i--) {
- spin_lock(&zbud_budlists_spinlock);
- if (!list_empty(&zbud_unbuddied[i].list)) {
- list_for_each_entry_safe(zbpg, ztmp,
- &zbud_unbuddied[i].list, bud_list) {
- if (spin_trylock(&zbpg->lock)) {
- found_good_buddy = i;
- goto found_unbuddied;
- }
- }
- }
- spin_unlock(&zbud_budlists_spinlock);
- }
- /* didn't find a good buddy, try allocating a new page */
- zbpg = zbud_alloc_raw_page();
- if (unlikely(zbpg == NULL))
- goto out;
- /* ok, have a page, now compress the data before taking locks */
- spin_lock(&zbud_budlists_spinlock);
- spin_lock(&zbpg->lock);
- list_add_tail(&zbpg->bud_list, &zbud_unbuddied[nchunks].list);
- zbud_unbuddied[nchunks].count++;
- zh = &zbpg->buddy[0];
- goto init_zh;
-
-found_unbuddied:
- ASSERT_SPINLOCK(&zbpg->lock);
- zh0 = &zbpg->buddy[0]; zh1 = &zbpg->buddy[1];
- BUG_ON(!((zh0->size == 0) ^ (zh1->size == 0)));
- if (zh0->size != 0) { /* buddy0 in use, buddy1 is vacant */
- ASSERT_SENTINEL(zh0, ZBH);
- zh = zh1;
- } else if (zh1->size != 0) { /* buddy1 in use, buddy0 is vacant */
- ASSERT_SENTINEL(zh1, ZBH);
- zh = zh0;
- } else
- BUG();
- list_del_init(&zbpg->bud_list);
- zbud_unbuddied[found_good_buddy].count--;
- list_add_tail(&zbpg->bud_list, &zbud_buddied_list);
- zcache_zbud_buddied_count++;
-
-init_zh:
- SET_SENTINEL(zh, ZBH);
- zh->size = size;
- zh->index = index;
- zh->oid = *oid;
- zh->pool_id = pool_id;
- zh->client_id = client_id;
- to = zbud_data(zh, size);
- memcpy(to, cdata, size);
- spin_unlock(&zbpg->lock);
- spin_unlock(&zbud_budlists_spinlock);
- zbud_cumul_chunk_counts[nchunks]++;
- atomic_inc(&zcache_zbud_curr_zpages);
- zcache_zbud_cumul_zpages++;
- zcache_zbud_curr_zbytes += size;
- zcache_zbud_cumul_zbytes += size;
-out:
- return zh;
-}
+enum comp_op {
+ ZCACHE_COMPOP_COMPRESS,
+ ZCACHE_COMPOP_DECOMPRESS
+};
-static int zbud_decompress(struct page *page, struct zbud_hdr *zh)
+static inline int zcache_comp_op(enum comp_op op,
+ const u8 *src, unsigned int slen,
+ u8 *dst, unsigned int *dlen)
{
- struct zbud_page *zbpg;
- unsigned budnum = zbud_budnum(zh);
- size_t out_len = PAGE_SIZE;
- char *to_va, *from_va;
- unsigned size;
- int ret = 0;
+ struct crypto_comp *tfm;
+ int ret = -1;
- zbpg = container_of(zh, struct zbud_page, buddy[budnum]);
- spin_lock(&zbpg->lock);
- if (list_empty(&zbpg->bud_list)) {
- /* ignore zombie page... see zbud_evict_pages() */
+ BUG_ON(!zcache_comp_pcpu_tfms);
+ tfm = *per_cpu_ptr(zcache_comp_pcpu_tfms, get_cpu());
+ BUG_ON(!tfm);
+ switch (op) {
+ case ZCACHE_COMPOP_COMPRESS:
+ ret = crypto_comp_compress(tfm, src, slen, dst, dlen);
+ break;
+ case ZCACHE_COMPOP_DECOMPRESS:
+ ret = crypto_comp_decompress(tfm, src, slen, dst, dlen);
+ break;
+ default:
ret = -EINVAL;
- goto out;
}
- ASSERT_SENTINEL(zh, ZBH);
- BUG_ON(zh->size == 0 || zh->size > zbud_max_buddy_size());
- to_va = kmap_atomic(page);
- size = zh->size;
- from_va = zbud_data(zh, size);
- ret = lzo1x_decompress_safe(from_va, size, to_va, &out_len);
- BUG_ON(ret != LZO_E_OK);
- BUG_ON(out_len != PAGE_SIZE);
- kunmap_atomic(to_va);
-out:
- spin_unlock(&zbpg->lock);
+ put_cpu();
return ret;
}
/*
- * The following routines handle shrinking of ephemeral pages by evicting
- * pages "least valuable" first.
- */
-
-static unsigned long zcache_evicted_raw_pages;
-static unsigned long zcache_evicted_buddied_pages;
-static unsigned long zcache_evicted_unbuddied_pages;
-
-static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id,
- uint16_t poolid);
-static void zcache_put_pool(struct tmem_pool *pool);
-
-/*
- * Flush and free all zbuds in a zbpg, then free the pageframe
- */
-static void zbud_evict_zbpg(struct zbud_page *zbpg)
-{
- struct zbud_hdr *zh;
- int i, j;
- uint32_t pool_id[ZBUD_MAX_BUDS], client_id[ZBUD_MAX_BUDS];
- uint32_t index[ZBUD_MAX_BUDS];
- struct tmem_oid oid[ZBUD_MAX_BUDS];
- struct tmem_pool *pool;
- unsigned long flags;
-
- ASSERT_SPINLOCK(&zbpg->lock);
- for (i = 0, j = 0; i < ZBUD_MAX_BUDS; i++) {
- zh = &zbpg->buddy[i];
- if (zh->size) {
- client_id[j] = zh->client_id;
- pool_id[j] = zh->pool_id;
- oid[j] = zh->oid;
- index[j] = zh->index;
- j++;
- }
- }
- spin_unlock(&zbpg->lock);
- for (i = 0; i < j; i++) {
- pool = zcache_get_pool_by_id(client_id[i], pool_id[i]);
- BUG_ON(pool == NULL);
- local_irq_save(flags);
- /* these flushes should dispose of any local storage */
- tmem_flush_page(pool, &oid[i], index[i]);
- local_irq_restore(flags);
- zcache_put_pool(pool);
- }
-}
-
-/*
- * Free nr pages. This code is funky because we want to hold the locks
- * protecting various lists for as short a time as possible, and in some
- * circumstances the list may change asynchronously when the list lock is
- * not held. In some cases we also trylock not only to avoid waiting on a
- * page in use by another cpu, but also to avoid potential deadlock due to
- * lock inversion.
- */
-static void zbud_evict_pages(int nr)
-{
- struct zbud_page *zbpg;
- int i, newly_unused_pages = 0;
-
-
- /* now try freeing unbuddied pages, starting with least space avail */
- for (i = 0; i < MAX_CHUNK; i++) {
-retry_unbud_list_i:
- spin_lock_bh(&zbud_budlists_spinlock);
- if (list_empty(&zbud_unbuddied[i].list)) {
- spin_unlock_bh(&zbud_budlists_spinlock);
- continue;
- }
- list_for_each_entry(zbpg, &zbud_unbuddied[i].list, bud_list) {
- if (unlikely(!spin_trylock(&zbpg->lock)))
- continue;
- zbud_unbuddied[i].count--;
- spin_unlock(&zbud_budlists_spinlock);
- zcache_evicted_unbuddied_pages++;
- /* want budlists unlocked when doing zbpg eviction */
- zbud_evict_zbpg(zbpg);
- newly_unused_pages++;
- local_bh_enable();
- if (--nr <= 0)
- goto evict_unused;
- goto retry_unbud_list_i;
- }
- spin_unlock_bh(&zbud_budlists_spinlock);
- }
-
- /* as a last resort, free buddied pages */
-retry_bud_list:
- spin_lock_bh(&zbud_budlists_spinlock);
- if (list_empty(&zbud_buddied_list)) {
- spin_unlock_bh(&zbud_budlists_spinlock);
- goto evict_unused;
- }
- list_for_each_entry(zbpg, &zbud_buddied_list, bud_list) {
- if (unlikely(!spin_trylock(&zbpg->lock)))
- continue;
- zcache_zbud_buddied_count--;
- spin_unlock(&zbud_budlists_spinlock);
- zcache_evicted_buddied_pages++;
- /* want budlists unlocked when doing zbpg eviction */
- zbud_evict_zbpg(zbpg);
- newly_unused_pages++;
- local_bh_enable();
- if (--nr <= 0)
- goto evict_unused;
- goto retry_bud_list;
- }
- spin_unlock_bh(&zbud_budlists_spinlock);
-
-evict_unused:
- return;
-}
-
-static DEFINE_PER_CPU(unsigned char *, zcache_remoteputmem);
-
-static int zbud_remotify_zbud(struct tmem_xhandle *xh, char *data,
- size_t size)
-{
- struct tmem_pool *pool;
- int i, remotenode, ret = -1;
- unsigned char cksum, *p;
- unsigned long flags;
-
- for (p = data, cksum = 0, i = 0; i < size; i++)
- cksum += *p;
- ret = ramster_remote_put(xh, data, size, true, &remotenode);
- if (ret == 0) {
- /* data was successfully remoted so change the local version
- * to point to the remote node where it landed */
- pool = zcache_get_pool_by_id(LOCAL_CLIENT, xh->pool_id);
- BUG_ON(pool == NULL);
- local_irq_save(flags);
- /* tmem_replace will also free up any local space */
- (void)tmem_replace(pool, &xh->oid, xh->index,
- pampd_make_remote(remotenode, size, cksum));
- local_irq_restore(flags);
- zcache_put_pool(pool);
- ramster_eph_pages_remoted++;
- ret = 0;
- } else
- ramster_eph_pages_remote_failed++;
- return ret;
-}
-
-static int zbud_remotify_zbpg(struct zbud_page *zbpg)
-{
- struct zbud_hdr *zh1, *zh2 = NULL;
- struct tmem_xhandle xh1, xh2 = { 0 };
- char *data1 = NULL, *data2 = NULL;
- size_t size1 = 0, size2 = 0;
- int ret = 0;
- unsigned char *tmpmem = __get_cpu_var(zcache_remoteputmem);
-
- ASSERT_SPINLOCK(&zbpg->lock);
- if (zbpg->buddy[0].size == 0)
- zh1 = &zbpg->buddy[1];
- else if (zbpg->buddy[1].size == 0)
- zh1 = &zbpg->buddy[0];
- else {
- zh1 = &zbpg->buddy[0];
- zh2 = &zbpg->buddy[1];
- }
- /* don't remotify pages that are already remotified */
- if (zh1->client_id != LOCAL_CLIENT)
- zh1 = NULL;
- if ((zh2 != NULL) && (zh2->client_id != LOCAL_CLIENT))
- zh2 = NULL;
-
- /* copy the data and metadata so can release lock */
- if (zh1 != NULL) {
- xh1.client_id = zh1->client_id;
- xh1.pool_id = zh1->pool_id;
- xh1.oid = zh1->oid;
- xh1.index = zh1->index;
- size1 = zh1->size;
- data1 = zbud_data(zh1, size1);
- memcpy(tmpmem, zbud_data(zh1, size1), size1);
- data1 = tmpmem;
- tmpmem += size1;
- }
- if (zh2 != NULL) {
- xh2.client_id = zh2->client_id;
- xh2.pool_id = zh2->pool_id;
- xh2.oid = zh2->oid;
- xh2.index = zh2->index;
- size2 = zh2->size;
- memcpy(tmpmem, zbud_data(zh2, size2), size2);
- data2 = tmpmem;
- }
- spin_unlock(&zbpg->lock);
- preempt_enable();
-
- /* OK, no locks held anymore, remotify one or both zbuds */
- if (zh1 != NULL)
- ret = zbud_remotify_zbud(&xh1, data1, size1);
- if (zh2 != NULL)
- ret |= zbud_remotify_zbud(&xh2, data2, size2);
- return ret;
-}
-
-void zbud_remotify_pages(int nr)
-{
- struct zbud_page *zbpg;
- int i, ret;
-
- /*
- * for now just try remotifying unbuddied pages, starting with
- * least space avail
- */
- for (i = 0; i < MAX_CHUNK; i++) {
-retry_unbud_list_i:
- preempt_disable(); /* enable in zbud_remotify_zbpg */
- spin_lock_bh(&zbud_budlists_spinlock);
- if (list_empty(&zbud_unbuddied[i].list)) {
- spin_unlock_bh(&zbud_budlists_spinlock);
- preempt_enable();
- continue; /* next i in for loop */
- }
- list_for_each_entry(zbpg, &zbud_unbuddied[i].list, bud_list) {
- if (unlikely(!spin_trylock(&zbpg->lock)))
- continue; /* next list_for_each_entry */
- zbud_unbuddied[i].count--;
- /* want budlists unlocked when doing zbpg remotify */
- spin_unlock_bh(&zbud_budlists_spinlock);
- ret = zbud_remotify_zbpg(zbpg);
- /* preemption is re-enabled in zbud_remotify_zbpg */
- if (ret == 0) {
- if (--nr <= 0)
- goto out;
- goto retry_unbud_list_i;
- }
- /* if fail to remotify any page, quit */
- pr_err("TESTING zbud_remotify_pages failed on page,"
- " trying to re-add\n");
- spin_lock_bh(&zbud_budlists_spinlock);
- spin_lock(&zbpg->lock);
- list_add_tail(&zbpg->bud_list, &zbud_unbuddied[i].list);
- zbud_unbuddied[i].count++;
- spin_unlock(&zbpg->lock);
- spin_unlock_bh(&zbud_budlists_spinlock);
- pr_err("TESTING zbud_remotify_pages failed on page,"
- " finished re-add\n");
- goto out;
- }
- spin_unlock_bh(&zbud_budlists_spinlock);
- preempt_enable();
- }
-
-next_buddied_zbpg:
- preempt_disable(); /* enable in zbud_remotify_zbpg */
- spin_lock_bh(&zbud_budlists_spinlock);
- if (list_empty(&zbud_buddied_list))
- goto unlock_out;
- list_for_each_entry(zbpg, &zbud_buddied_list, bud_list) {
- if (unlikely(!spin_trylock(&zbpg->lock)))
- continue; /* next list_for_each_entry */
- zcache_zbud_buddied_count--;
- /* want budlists unlocked when doing zbpg remotify */
- spin_unlock_bh(&zbud_budlists_spinlock);
- ret = zbud_remotify_zbpg(zbpg);
- /* preemption is re-enabled in zbud_remotify_zbpg */
- if (ret == 0) {
- if (--nr <= 0)
- goto out;
- goto next_buddied_zbpg;
- }
- /* if fail to remotify any page, quit */
- pr_err("TESTING zbud_remotify_pages failed on BUDDIED page,"
- " trying to re-add\n");
- spin_lock_bh(&zbud_budlists_spinlock);
- spin_lock(&zbpg->lock);
- list_add_tail(&zbpg->bud_list, &zbud_buddied_list);
- zcache_zbud_buddied_count++;
- spin_unlock(&zbpg->lock);
- spin_unlock_bh(&zbud_budlists_spinlock);
- pr_err("TESTING zbud_remotify_pages failed on BUDDIED page,"
- " finished re-add\n");
- goto out;
- }
-unlock_out:
- spin_unlock_bh(&zbud_budlists_spinlock);
- preempt_enable();
-out:
- return;
-}
-
-/* the "flush list" asynchronously collects pages to remotely flush */
-#define FLUSH_ENTIRE_OBJECT ((uint32_t)-1)
-static void ramster_flnode_free(struct flushlist_node *,
- struct tmem_pool *);
-
-static void zcache_remote_flush_page(struct flushlist_node *flnode)
-{
- struct tmem_xhandle *xh;
- int remotenode, ret;
-
- preempt_disable();
- xh = &flnode->xh;
- remotenode = flnode->xh.client_id;
- ret = ramster_remote_flush(xh, remotenode);
- if (ret >= 0)
- ramster_remote_pages_flushed++;
- else
- ramster_remote_page_flushes_failed++;
- preempt_enable_no_resched();
- ramster_flnode_free(flnode, NULL);
-}
-
-static void zcache_remote_flush_object(struct flushlist_node *flnode)
-{
- struct tmem_xhandle *xh;
- int remotenode, ret;
-
- preempt_disable();
- xh = &flnode->xh;
- remotenode = flnode->xh.client_id;
- ret = ramster_remote_flush_object(xh, remotenode);
- if (ret >= 0)
- ramster_remote_objects_flushed++;
- else
- ramster_remote_object_flushes_failed++;
- preempt_enable_no_resched();
- ramster_flnode_free(flnode, NULL);
-}
-
-static void zcache_remote_eph_put(struct zbud_hdr *zbud)
-{
- /* FIXME */
-}
-
-static void zcache_remote_pers_put(struct zv_hdr *zv)
-{
- struct tmem_xhandle xh;
- uint16_t size;
- bool ephemeral;
- int remotenode, ret = -1;
- char *data;
- struct tmem_pool *pool;
- unsigned long flags;
- unsigned char cksum;
- char *p;
- int i;
- unsigned char *tmpmem = __get_cpu_var(zcache_remoteputmem);
-
- ASSERT_SENTINEL(zv, ZVH);
- BUG_ON(zv->client_id != LOCAL_CLIENT);
- local_bh_disable();
- xh.client_id = zv->client_id;
- xh.pool_id = zv->pool_id;
- xh.oid = zv->oid;
- xh.index = zv->index;
- size = xv_get_object_size(zv) - sizeof(*zv);
- BUG_ON(size == 0 || size > zv_max_page_size);
- data = (char *)zv + sizeof(*zv);
- for (p = data, cksum = 0, i = 0; i < size; i++)
- cksum += *p;
- memcpy(tmpmem, data, size);
- data = tmpmem;
- pool = zcache_get_pool_by_id(zv->client_id, zv->pool_id);
- ephemeral = is_ephemeral(pool);
- zcache_put_pool(pool);
- /* now OK to release lock set in caller */
- spin_unlock(&zcache_rem_op_list_lock);
- local_bh_enable();
- preempt_disable();
- ret = ramster_remote_put(&xh, data, size, ephemeral, &remotenode);
- preempt_enable_no_resched();
- if (ret != 0) {
- /*
- * This is some form of a memory leak... if the remote put
- * fails, there will never be another attempt to remotify
- * this page. But since we've dropped the zv pointer,
- * the page may have been freed or the data replaced
- * so we can't just "put it back" in the remote op list.
- * Even if we could, not sure where to put it in the list
- * because there may be flushes that must be strictly
- * ordered vs the put. So leave this as a FIXME for now.
- * But count them so we know if it becomes a problem.
- */
- ramster_pers_pages_remote_failed++;
- goto out;
- } else
- atomic_inc(&ramster_remote_pers_pages);
- ramster_pers_pages_remoted++;
- /*
- * data was successfully remoted so change the local version to
- * point to the remote node where it landed
- */
- local_bh_disable();
- pool = zcache_get_pool_by_id(LOCAL_CLIENT, xh.pool_id);
- local_irq_save(flags);
- (void)tmem_replace(pool, &xh.oid, xh.index,
- pampd_make_remote(remotenode, size, cksum));
- local_irq_restore(flags);
- zcache_put_pool(pool);
- local_bh_enable();
-out:
- return;
-}
-
-static void zcache_do_remotify_ops(int nr)
-{
- struct ramster_remotify_hdr *rem_op;
- union remotify_list_node *u;
-
- while (1) {
- if (!nr)
- goto out;
- spin_lock(&zcache_rem_op_list_lock);
- if (list_empty(&zcache_rem_op_list)) {
- spin_unlock(&zcache_rem_op_list_lock);
- goto out;
- }
- rem_op = list_first_entry(&zcache_rem_op_list,
- struct ramster_remotify_hdr, list);
- list_del_init(&rem_op->list);
- if (rem_op->op != RAMSTER_REMOTIFY_PERS_PUT)
- spin_unlock(&zcache_rem_op_list_lock);
- u = (union remotify_list_node *)rem_op;
- switch (rem_op->op) {
- case RAMSTER_REMOTIFY_EPH_PUT:
-BUG();
- zcache_remote_eph_put((struct zbud_hdr *)rem_op);
- break;
- case RAMSTER_REMOTIFY_PERS_PUT:
- zcache_remote_pers_put((struct zv_hdr *)rem_op);
- break;
- case RAMSTER_REMOTIFY_FLUSH_PAGE:
- zcache_remote_flush_page((struct flushlist_node *)u);
- break;
- case RAMSTER_REMOTIFY_FLUSH_OBJ:
- zcache_remote_flush_object((struct flushlist_node *)u);
- break;
- default:
- BUG();
- }
- }
-out:
- return;
-}
-
-/*
- * Communicate interface revision with userspace
- */
-#include "cluster/ramster_nodemanager.h"
-static unsigned long ramster_interface_revision = R2NM_API_VERSION;
-
-/*
- * For now, just push over a few pages every few seconds to
- * ensure that it basically works
- */
-static struct workqueue_struct *ramster_remotify_workqueue;
-static void ramster_remotify_process(struct work_struct *work);
-static DECLARE_DELAYED_WORK(ramster_remotify_worker,
- ramster_remotify_process);
-
-static void ramster_remotify_queue_delayed_work(unsigned long delay)
-{
- if (!queue_delayed_work(ramster_remotify_workqueue,
- &ramster_remotify_worker, delay))
- pr_err("ramster_remotify: bad workqueue\n");
-}
-
-
-static int use_frontswap;
-static int use_cleancache;
-static int ramster_remote_target_nodenum = -1;
-static void ramster_remotify_process(struct work_struct *work)
-{
- static bool remotify_in_progress;
-
- BUG_ON(irqs_disabled());
- if (remotify_in_progress)
- ramster_remotify_queue_delayed_work(HZ);
- else if (ramster_remote_target_nodenum != -1) {
- remotify_in_progress = true;
-#ifdef CONFIG_CLEANCACHE
- if (use_cleancache && ramster_eph_remotify_enable)
- zbud_remotify_pages(5000); /* FIXME is this a good number? */
-#endif
-#ifdef CONFIG_FRONTSWAP
- if (use_frontswap && ramster_pers_remotify_enable)
- zcache_do_remotify_ops(500); /* FIXME is this a good number? */
-#endif
- remotify_in_progress = false;
- ramster_remotify_queue_delayed_work(HZ);
- }
-}
-
-static void ramster_remotify_init(void)
-{
- unsigned long n = 60UL;
- ramster_remotify_workqueue =
- create_singlethread_workqueue("ramster_remotify");
- ramster_remotify_queue_delayed_work(n * HZ);
-}
-
-
-static void zbud_init(void)
-{
- int i;
-
- INIT_LIST_HEAD(&zbud_buddied_list);
- zcache_zbud_buddied_count = 0;
- for (i = 0; i < NCHUNKS; i++) {
- INIT_LIST_HEAD(&zbud_unbuddied[i].list);
- zbud_unbuddied[i].count = 0;
- }
-}
-
-#ifdef CONFIG_SYSFS
-/*
- * These sysfs routines show a nice distribution of how many zbpg's are
- * currently (and have ever been placed) in each unbuddied list. It's fun
- * to watch but can probably go away before final merge.
+ * policy parameters
*/
-static int zbud_show_unbuddied_list_counts(char *buf)
-{
- int i;
- char *p = buf;
-
- for (i = 0; i < NCHUNKS; i++)
- p += sprintf(p, "%u ", zbud_unbuddied[i].count);
- return p - buf;
-}
-
-static int zbud_show_cumul_chunk_counts(char *buf)
-{
- unsigned long i, chunks = 0, total_chunks = 0, sum_total_chunks = 0;
- unsigned long total_chunks_lte_21 = 0, total_chunks_lte_32 = 0;
- unsigned long total_chunks_lte_42 = 0;
- char *p = buf;
-
- for (i = 0; i < NCHUNKS; i++) {
- p += sprintf(p, "%lu ", zbud_cumul_chunk_counts[i]);
- chunks += zbud_cumul_chunk_counts[i];
- total_chunks += zbud_cumul_chunk_counts[i];
- sum_total_chunks += i * zbud_cumul_chunk_counts[i];
- if (i == 21)
- total_chunks_lte_21 = total_chunks;
- if (i == 32)
- total_chunks_lte_32 = total_chunks;
- if (i == 42)
- total_chunks_lte_42 = total_chunks;
- }
- p += sprintf(p, "<=21:%lu <=32:%lu <=42:%lu, mean:%lu\n",
- total_chunks_lte_21, total_chunks_lte_32, total_chunks_lte_42,
- chunks == 0 ? 0 : sum_total_chunks / chunks);
- return p - buf;
-}
-#endif
-/**********
- * This "zv" PAM implementation combines the TLSF-based xvMalloc
- * with lzo1x compression to maximize the amount of data that can
- * be packed into a physical page.
- *
- * Zv represents a PAM page with the index and object (plus a "size" value
- * necessary for decompression) immediately preceding the compressed data.
- */
-
-/* rudimentary policy limits */
-/* total number of persistent pages may not exceed this percentage */
-static unsigned int zv_page_count_policy_percent = 75;
/*
* byte count defining poor compression; pages with greater zsize will be
* rejected
*/
-static unsigned int zv_max_zsize = (PAGE_SIZE / 8) * 7;
+static unsigned int zbud_max_zsize __read_mostly = (PAGE_SIZE / 8) * 7;
/*
* byte count defining poor *mean* compression; pages with greater zsize
* will be rejected until sufficient better-compressed pages are accepted
* driving the mean below this threshold
*/
-static unsigned int zv_max_mean_zsize = (PAGE_SIZE / 8) * 5;
-
-static atomic_t zv_curr_dist_counts[NCHUNKS];
-static atomic_t zv_cumul_dist_counts[NCHUNKS];
+static unsigned int zbud_max_mean_zsize __read_mostly = (PAGE_SIZE / 8) * 5;
-
-static struct zv_hdr *zv_create(struct zcache_client *cli, uint32_t pool_id,
- struct tmem_oid *oid, uint32_t index,
- void *cdata, unsigned clen)
-{
- struct page *page;
- struct zv_hdr *zv = NULL;
- uint32_t offset;
- int alloc_size = clen + sizeof(struct zv_hdr);
- int chunks = (alloc_size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT;
- int ret;
-
- BUG_ON(!irqs_disabled());
- BUG_ON(chunks >= NCHUNKS);
- ret = xv_malloc(cli->xvpool, clen + sizeof(struct zv_hdr),
- &page, &offset, ZCACHE_GFP_MASK);
- if (unlikely(ret))
- goto out;
- atomic_inc(&zv_curr_dist_counts[chunks]);
- atomic_inc(&zv_cumul_dist_counts[chunks]);
- zv = kmap_atomic(page) + offset;
- zv->index = index;
- zv->oid = *oid;
- zv->pool_id = pool_id;
- SET_SENTINEL(zv, ZVH);
- INIT_LIST_HEAD(&zv->rem_op.list);
- zv->client_id = get_client_id_from_client(cli);
- zv->rem_op.op = RAMSTER_REMOTIFY_PERS_PUT;
- if (zv->client_id == LOCAL_CLIENT) {
- spin_lock(&zcache_rem_op_list_lock);
- list_add_tail(&zv->rem_op.list, &zcache_rem_op_list);
- spin_unlock(&zcache_rem_op_list_lock);
- }
- memcpy((char *)zv + sizeof(struct zv_hdr), cdata, clen);
- kunmap_atomic(zv);
-out:
- return zv;
-}
-
-/* similar to zv_create, but just reserve space, no data yet */
-static struct zv_hdr *zv_alloc(struct tmem_pool *pool,
- struct tmem_oid *oid, uint32_t index,
- unsigned clen)
-{
- struct zcache_client *cli = pool->client;
- struct page *page;
- struct zv_hdr *zv = NULL;
- uint32_t offset;
- int ret;
-
- BUG_ON(!irqs_disabled());
- BUG_ON(!is_local_client(pool->client));
- ret = xv_malloc(cli->xvpool, clen + sizeof(struct zv_hdr),
- &page, &offset, ZCACHE_GFP_MASK);
- if (unlikely(ret))
- goto out;
- zv = kmap_atomic(page) + offset;
- SET_SENTINEL(zv, ZVH);
- INIT_LIST_HEAD(&zv->rem_op.list);
- zv->client_id = LOCAL_CLIENT;
- zv->rem_op.op = RAMSTER_INTRANSIT_PERS;
- zv->index = index;
- zv->oid = *oid;
- zv->pool_id = pool->pool_id;
- kunmap_atomic(zv);
-out:
- return zv;
-}
-
-static void zv_free(struct xv_pool *xvpool, struct zv_hdr *zv)
-{
- unsigned long flags;
- struct page *page;
- uint32_t offset;
- uint16_t size = xv_get_object_size(zv);
- int chunks = (size + (CHUNK_SIZE - 1)) >> CHUNK_SHIFT;
-
- ASSERT_SENTINEL(zv, ZVH);
- BUG_ON(chunks >= NCHUNKS);
- atomic_dec(&zv_curr_dist_counts[chunks]);
- size -= sizeof(*zv);
- spin_lock(&zcache_rem_op_list_lock);
- size = xv_get_object_size(zv) - sizeof(*zv);
- BUG_ON(size == 0);
- INVERT_SENTINEL(zv, ZVH);
- if (!list_empty(&zv->rem_op.list))
- list_del_init(&zv->rem_op.list);
- spin_unlock(&zcache_rem_op_list_lock);
- page = virt_to_page(zv);
- offset = (unsigned long)zv & ~PAGE_MASK;
- local_irq_save(flags);
- xv_free(xvpool, page, offset);
- local_irq_restore(flags);
-}
-
-static void zv_decompress(struct page *page, struct zv_hdr *zv)
-{
- size_t clen = PAGE_SIZE;
- char *to_va;
- unsigned size;
- int ret;
-
- ASSERT_SENTINEL(zv, ZVH);
- size = xv_get_object_size(zv) - sizeof(*zv);
- BUG_ON(size == 0);
- to_va = kmap_atomic(page);
- ret = lzo1x_decompress_safe((char *)zv + sizeof(*zv),
- size, to_va, &clen);
- kunmap_atomic(to_va);
- BUG_ON(ret != LZO_E_OK);
- BUG_ON(clen != PAGE_SIZE);
-}
-
-static void zv_copy_from_pampd(char *data, size_t *bufsize, struct zv_hdr *zv)
-{
- unsigned size;
-
- ASSERT_SENTINEL(zv, ZVH);
- size = xv_get_object_size(zv) - sizeof(*zv);
- BUG_ON(size == 0 || size > zv_max_page_size);
- BUG_ON(size > *bufsize);
- memcpy(data, (char *)zv + sizeof(*zv), size);
- *bufsize = size;
-}
-
-static void zv_copy_to_pampd(struct zv_hdr *zv, char *data, size_t size)
-{
- unsigned zv_size;
-
- ASSERT_SENTINEL(zv, ZVH);
- zv_size = xv_get_object_size(zv) - sizeof(*zv);
- BUG_ON(zv_size != size);
- BUG_ON(zv_size == 0 || zv_size > zv_max_page_size);
- memcpy((char *)zv + sizeof(*zv), data, size);
-}
-
-#ifdef CONFIG_SYSFS
/*
- * show a distribution of compression stats for zv pages.
+ * for now, used named slabs so can easily track usage; later can
+ * either just use kmalloc, or perhaps add a slab-like allocator
+ * to more carefully manage total memory utilization
*/
+static struct kmem_cache *zcache_objnode_cache;
+static struct kmem_cache *zcache_obj_cache;
-static int zv_curr_dist_counts_show(char *buf)
-{
- unsigned long i, n, chunks = 0, sum_total_chunks = 0;
- char *p = buf;
-
- for (i = 0; i < NCHUNKS; i++) {
- n = atomic_read(&zv_curr_dist_counts[i]);
- p += sprintf(p, "%lu ", n);
- chunks += n;
- sum_total_chunks += i * n;
- }
- p += sprintf(p, "mean:%lu\n",
- chunks == 0 ? 0 : sum_total_chunks / chunks);
- return p - buf;
-}
-
-static int zv_cumul_dist_counts_show(char *buf)
-{
- unsigned long i, n, chunks = 0, sum_total_chunks = 0;
- char *p = buf;
-
- for (i = 0; i < NCHUNKS; i++) {
- n = atomic_read(&zv_cumul_dist_counts[i]);
- p += sprintf(p, "%lu ", n);
- chunks += n;
- sum_total_chunks += i * n;
- }
- p += sprintf(p, "mean:%lu\n",
- chunks == 0 ? 0 : sum_total_chunks / chunks);
- return p - buf;
-}
+static DEFINE_PER_CPU(struct zcache_preload, zcache_preloads) = { 0, };
-/*
- * setting zv_max_zsize via sysfs causes all persistent (e.g. swap)
- * pages that don't compress to less than this value (including metadata
- * overhead) to be rejected. We don't allow the value to get too close
- * to PAGE_SIZE.
- */
-static ssize_t zv_max_zsize_show(struct kobject *kobj,
- struct kobj_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%u\n", zv_max_zsize);
+/* we try to keep these statistics SMP-consistent */
+static long zcache_obj_count;
+static atomic_t zcache_obj_atomic = ATOMIC_INIT(0);
+static long zcache_obj_count_max;
+static long zcache_objnode_count;
+static atomic_t zcache_objnode_atomic = ATOMIC_INIT(0);
+static long zcache_objnode_count_max;
+static u64 zcache_eph_zbytes;
+static atomic_long_t zcache_eph_zbytes_atomic = ATOMIC_INIT(0);
+static u64 zcache_eph_zbytes_max;
+static u64 zcache_pers_zbytes;
+static atomic_long_t zcache_pers_zbytes_atomic = ATOMIC_INIT(0);
+static u64 zcache_pers_zbytes_max;
+static long zcache_eph_pageframes;
+static atomic_t zcache_eph_pageframes_atomic = ATOMIC_INIT(0);
+static long zcache_eph_pageframes_max;
+static long zcache_pers_pageframes;
+static atomic_t zcache_pers_pageframes_atomic = ATOMIC_INIT(0);
+static long zcache_pers_pageframes_max;
+static long zcache_pageframes_alloced;
+static atomic_t zcache_pageframes_alloced_atomic = ATOMIC_INIT(0);
+static long zcache_pageframes_freed;
+static atomic_t zcache_pageframes_freed_atomic = ATOMIC_INIT(0);
+static long zcache_eph_zpages;
+static atomic_t zcache_eph_zpages_atomic = ATOMIC_INIT(0);
+static long zcache_eph_zpages_max;
+static long zcache_pers_zpages;
+static atomic_t zcache_pers_zpages_atomic = ATOMIC_INIT(0);
+static long zcache_pers_zpages_max;
+
+/* but for the rest of these, counting races are ok */
+static unsigned long zcache_flush_total;
+static unsigned long zcache_flush_found;
+static unsigned long zcache_flobj_total;
+static unsigned long zcache_flobj_found;
+static unsigned long zcache_failed_eph_puts;
+static unsigned long zcache_failed_pers_puts;
+static unsigned long zcache_failed_getfreepages;
+static unsigned long zcache_failed_alloc;
+static unsigned long zcache_put_to_flush;
+static unsigned long zcache_compress_poor;
+static unsigned long zcache_mean_compress_poor;
+static unsigned long zcache_eph_ate_tail;
+static unsigned long zcache_eph_ate_tail_failed;
+static unsigned long zcache_pers_ate_eph;
+static unsigned long zcache_pers_ate_eph_failed;
+static unsigned long zcache_evicted_eph_zpages;
+static unsigned long zcache_evicted_eph_pageframes;
+static unsigned long zcache_last_active_file_pageframes;
+static unsigned long zcache_last_inactive_file_pageframes;
+static unsigned long zcache_last_active_anon_pageframes;
+static unsigned long zcache_last_inactive_anon_pageframes;
+static unsigned long zcache_eph_nonactive_puts_ignored;
+static unsigned long zcache_pers_nonactive_puts_ignored;
+
+#ifdef CONFIG_DEBUG_FS
+#include <linux/debugfs.h>
+#define zdfs debugfs_create_size_t
+#define zdfs64 debugfs_create_u64
+static int zcache_debugfs_init(void)
+{
+ struct dentry *root = debugfs_create_dir("zcache", NULL);
+ if (root == NULL)
+ return -ENXIO;
+
+ zdfs("obj_count", S_IRUGO, root, &zcache_obj_count);
+ zdfs("obj_count_max", S_IRUGO, root, &zcache_obj_count_max);
+ zdfs("objnode_count", S_IRUGO, root, &zcache_objnode_count);
+ zdfs("objnode_count_max", S_IRUGO, root, &zcache_objnode_count_max);
+ zdfs("flush_total", S_IRUGO, root, &zcache_flush_total);
+ zdfs("flush_found", S_IRUGO, root, &zcache_flush_found);
+ zdfs("flobj_total", S_IRUGO, root, &zcache_flobj_total);
+ zdfs("flobj_found", S_IRUGO, root, &zcache_flobj_found);
+ zdfs("failed_eph_puts", S_IRUGO, root, &zcache_failed_eph_puts);
+ zdfs("failed_pers_puts", S_IRUGO, root, &zcache_failed_pers_puts);
+ zdfs("failed_get_free_pages", S_IRUGO, root,
+ &zcache_failed_getfreepages);
+ zdfs("failed_alloc", S_IRUGO, root, &zcache_failed_alloc);
+ zdfs("put_to_flush", S_IRUGO, root, &zcache_put_to_flush);
+ zdfs("compress_poor", S_IRUGO, root, &zcache_compress_poor);
+ zdfs("mean_compress_poor", S_IRUGO, root, &zcache_mean_compress_poor);
+ zdfs("eph_ate_tail", S_IRUGO, root, &zcache_eph_ate_tail);
+ zdfs("eph_ate_tail_failed", S_IRUGO, root, &zcache_eph_ate_tail_failed);
+ zdfs("pers_ate_eph", S_IRUGO, root, &zcache_pers_ate_eph);
+ zdfs("pers_ate_eph_failed", S_IRUGO, root, &zcache_pers_ate_eph_failed);
+ zdfs("evicted_eph_zpages", S_IRUGO, root, &zcache_evicted_eph_zpages);
+ zdfs("evicted_eph_pageframes", S_IRUGO, root,
+ &zcache_evicted_eph_pageframes);
+ zdfs("eph_pageframes", S_IRUGO, root, &zcache_eph_pageframes);
+ zdfs("eph_pageframes_max", S_IRUGO, root, &zcache_eph_pageframes_max);
+ zdfs("pers_pageframes", S_IRUGO, root, &zcache_pers_pageframes);
+ zdfs("pers_pageframes_max", S_IRUGO, root, &zcache_pers_pageframes_max);
+ zdfs("eph_zpages", S_IRUGO, root, &zcache_eph_zpages);
+ zdfs("eph_zpages_max", S_IRUGO, root, &zcache_eph_zpages_max);
+ zdfs("pers_zpages", S_IRUGO, root, &zcache_pers_zpages);
+ zdfs("pers_zpages_max", S_IRUGO, root, &zcache_pers_zpages_max);
+ zdfs("last_active_file_pageframes", S_IRUGO, root,
+ &zcache_last_active_file_pageframes);
+ zdfs("last_inactive_file_pageframes", S_IRUGO, root,
+ &zcache_last_inactive_file_pageframes);
+ zdfs("last_active_anon_pageframes", S_IRUGO, root,
+ &zcache_last_active_anon_pageframes);
+ zdfs("last_inactive_anon_pageframes", S_IRUGO, root,
+ &zcache_last_inactive_anon_pageframes);
+ zdfs("eph_nonactive_puts_ignored", S_IRUGO, root,
+ &zcache_eph_nonactive_puts_ignored);
+ zdfs("pers_nonactive_puts_ignored", S_IRUGO, root,
+ &zcache_pers_nonactive_puts_ignored);
+ zdfs64("eph_zbytes", S_IRUGO, root, &zcache_eph_zbytes);
+ zdfs64("eph_zbytes_max", S_IRUGO, root, &zcache_eph_zbytes_max);
+ zdfs64("pers_zbytes", S_IRUGO, root, &zcache_pers_zbytes);
+ zdfs64("pers_zbytes_max", S_IRUGO, root, &zcache_pers_zbytes_max);
+ return 0;
}
+#undef zdebugfs
+#undef zdfs64
+#endif
-static ssize_t zv_max_zsize_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t count)
-{
- unsigned long val;
- int err;
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- err = kstrtoul(buf, 10, &val);
- if (err || (val == 0) || (val > (PAGE_SIZE / 8) * 7))
- return -EINVAL;
- zv_max_zsize = val;
- return count;
+#define ZCACHE_DEBUG
+#ifdef ZCACHE_DEBUG
+/* developers can call this in case of ooms, e.g. to find memory leaks */
+void zcache_dump(void)
+{
+ pr_info("zcache: obj_count=%lu\n", zcache_obj_count);
+ pr_info("zcache: obj_count_max=%lu\n", zcache_obj_count_max);
+ pr_info("zcache: objnode_count=%lu\n", zcache_objnode_count);
+ pr_info("zcache: objnode_count_max=%lu\n", zcache_objnode_count_max);
+ pr_info("zcache: flush_total=%lu\n", zcache_flush_total);
+ pr_info("zcache: flush_found=%lu\n", zcache_flush_found);
+ pr_info("zcache: flobj_total=%lu\n", zcache_flobj_total);
+ pr_info("zcache: flobj_found=%lu\n", zcache_flobj_found);
+ pr_info("zcache: failed_eph_puts=%lu\n", zcache_failed_eph_puts);
+ pr_info("zcache: failed_pers_puts=%lu\n", zcache_failed_pers_puts);
+ pr_info("zcache: failed_get_free_pages=%lu\n",
+ zcache_failed_getfreepages);
+ pr_info("zcache: failed_alloc=%lu\n", zcache_failed_alloc);
+ pr_info("zcache: put_to_flush=%lu\n", zcache_put_to_flush);
+ pr_info("zcache: compress_poor=%lu\n", zcache_compress_poor);
+ pr_info("zcache: mean_compress_poor=%lu\n",
+ zcache_mean_compress_poor);
+ pr_info("zcache: eph_ate_tail=%lu\n", zcache_eph_ate_tail);
+ pr_info("zcache: eph_ate_tail_failed=%lu\n",
+ zcache_eph_ate_tail_failed);
+ pr_info("zcache: pers_ate_eph=%lu\n", zcache_pers_ate_eph);
+ pr_info("zcache: pers_ate_eph_failed=%lu\n",
+ zcache_pers_ate_eph_failed);
+ pr_info("zcache: evicted_eph_zpages=%lu\n", zcache_evicted_eph_zpages);
+ pr_info("zcache: evicted_eph_pageframes=%lu\n",
+ zcache_evicted_eph_pageframes);
+ pr_info("zcache: eph_pageframes=%lu\n", zcache_eph_pageframes);
+ pr_info("zcache: eph_pageframes_max=%lu\n", zcache_eph_pageframes_max);
+ pr_info("zcache: pers_pageframes=%lu\n", zcache_pers_pageframes);
+ pr_info("zcache: pers_pageframes_max=%lu\n",
+ zcache_pers_pageframes_max);
+ pr_info("zcache: eph_zpages=%lu\n", zcache_eph_zpages);
+ pr_info("zcache: eph_zpages_max=%lu\n", zcache_eph_zpages_max);
+ pr_info("zcache: pers_zpages=%lu\n", zcache_pers_zpages);
+ pr_info("zcache: pers_zpages_max=%lu\n", zcache_pers_zpages_max);
+ pr_info("zcache: eph_zbytes=%llu\n",
+ (unsigned long long)zcache_eph_zbytes);
+ pr_info("zcache: eph_zbytes_max=%llu\n",
+ (unsigned long long)zcache_eph_zbytes_max);
+ pr_info("zcache: pers_zbytes=%llu\n",
+ (unsigned long long)zcache_pers_zbytes);
+ pr_info("zcache: pers_zbytes_max=%llu\n",
+ (unsigned long long)zcache_pers_zbytes_max);
}
+#endif
/*
- * setting zv_max_mean_zsize via sysfs causes all persistent (e.g. swap)
- * pages that don't compress to less than this value (including metadata
- * overhead) to be rejected UNLESS the mean compression is also smaller
- * than this value. In other words, we are load-balancing-by-zsize the
- * accepted pages. Again, we don't allow the value to get too close
- * to PAGE_SIZE.
+ * zcache core code starts here
*/
-static ssize_t zv_max_mean_zsize_show(struct kobject *kobj,
- struct kobj_attribute *attr,
- char *buf)
-{
- return sprintf(buf, "%u\n", zv_max_mean_zsize);
-}
-
-static ssize_t zv_max_mean_zsize_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t count)
-{
- unsigned long val;
- int err;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- err = kstrtoul(buf, 10, &val);
- if (err || (val == 0) || (val > (PAGE_SIZE / 8) * 7))
- return -EINVAL;
- zv_max_mean_zsize = val;
- return count;
-}
+static struct zcache_client zcache_host;
+static struct zcache_client zcache_clients[MAX_CLIENTS];
-/*
- * setting zv_page_count_policy_percent via sysfs sets an upper bound of
- * persistent (e.g. swap) pages that will be retained according to:
- * (zv_page_count_policy_percent * totalram_pages) / 100)
- * when that limit is reached, further puts will be rejected (until
- * some pages have been flushed). Note that, due to compression,
- * this number may exceed 100; it defaults to 75 and we set an
- * arbitrary limit of 150. A poor choice will almost certainly result
- * in OOM's, so this value should only be changed prudently.
- */
-static ssize_t zv_page_count_policy_percent_show(struct kobject *kobj,
- struct kobj_attribute *attr,
- char *buf)
+static inline bool is_local_client(struct zcache_client *cli)
{
- return sprintf(buf, "%u\n", zv_page_count_policy_percent);
+ return cli == &zcache_host;
}
-static ssize_t zv_page_count_policy_percent_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t count)
+static struct zcache_client *zcache_get_client_by_id(uint16_t cli_id)
{
- unsigned long val;
- int err;
+ struct zcache_client *cli = &zcache_host;
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- err = kstrtoul(buf, 10, &val);
- if (err || (val == 0) || (val > 150))
- return -EINVAL;
- zv_page_count_policy_percent = val;
- return count;
+ if (cli_id != LOCAL_CLIENT) {
+ if (cli_id >= MAX_CLIENTS)
+ goto out;
+ cli = &zcache_clients[cli_id];
+ }
+out:
+ return cli;
}
-static struct kobj_attribute zcache_zv_max_zsize_attr = {
- .attr = { .name = "zv_max_zsize", .mode = 0644 },
- .show = zv_max_zsize_show,
- .store = zv_max_zsize_store,
-};
-
-static struct kobj_attribute zcache_zv_max_mean_zsize_attr = {
- .attr = { .name = "zv_max_mean_zsize", .mode = 0644 },
- .show = zv_max_mean_zsize_show,
- .store = zv_max_mean_zsize_store,
-};
-
-static struct kobj_attribute zcache_zv_page_count_policy_percent_attr = {
- .attr = { .name = "zv_page_count_policy_percent",
- .mode = 0644 },
- .show = zv_page_count_policy_percent_show,
- .store = zv_page_count_policy_percent_store,
-};
-#endif
-
-/*
- * zcache core code starts here
- */
-
-/* useful stats not collected by cleancache or frontswap */
-static unsigned long zcache_flush_total;
-static unsigned long zcache_flush_found;
-static unsigned long zcache_flobj_total;
-static unsigned long zcache_flobj_found;
-static unsigned long zcache_failed_eph_puts;
-static unsigned long zcache_nonactive_puts;
-static unsigned long zcache_failed_pers_puts;
-
/*
* Tmem operations assume the poolid implies the invoking client.
* Zcache only has one client (the kernel itself): LOCAL_CLIENT.
@@ -1398,21 +328,16 @@ static unsigned long zcache_failed_pers_puts;
* of zcache would have one client per guest and each client might
* have a poolid==N.
*/
-static struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id, uint16_t poolid)
+struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id, uint16_t poolid)
{
struct tmem_pool *pool = NULL;
struct zcache_client *cli = NULL;
- if (cli_id == LOCAL_CLIENT)
- cli = &zcache_host;
- else {
- if (cli_id >= MAX_CLIENTS)
- goto out;
- cli = &zcache_clients[cli_id];
- if (cli == NULL)
- goto out;
+ cli = zcache_get_client_by_id(cli_id);
+ if (cli == NULL)
+ goto out;
+ if (!is_local_client(cli))
atomic_inc(&cli->refcount);
- }
if (poolid < MAX_POOLS_PER_CLIENT) {
pool = cli->tmem_pools[poolid];
if (pool != NULL)
@@ -1422,7 +347,7 @@ out:
return pool;
}
-static void zcache_put_pool(struct tmem_pool *pool)
+void zcache_put_pool(struct tmem_pool *pool)
{
struct zcache_client *cli = NULL;
@@ -1430,173 +355,26 @@ static void zcache_put_pool(struct tmem_pool *pool)
BUG();
cli = pool->client;
atomic_dec(&pool->refcount);
- atomic_dec(&cli->refcount);
+ if (!is_local_client(cli))
+ atomic_dec(&cli->refcount);
}
int zcache_new_client(uint16_t cli_id)
{
- struct zcache_client *cli = NULL;
+ struct zcache_client *cli;
int ret = -1;
- if (cli_id == LOCAL_CLIENT)
- cli = &zcache_host;
- else if ((unsigned int)cli_id < MAX_CLIENTS)
- cli = &zcache_clients[cli_id];
+ cli = zcache_get_client_by_id(cli_id);
if (cli == NULL)
goto out;
if (cli->allocated)
goto out;
cli->allocated = 1;
-#ifdef CONFIG_FRONTSWAP
- cli->xvpool = xv_create_pool();
- if (cli->xvpool == NULL)
- goto out;
-#endif
- ret = 0;
-out:
- return ret;
-}
-
-/* counters for debugging */
-static unsigned long zcache_failed_get_free_pages;
-static unsigned long zcache_failed_alloc;
-static unsigned long zcache_put_to_flush;
-
-/*
- * for now, used named slabs so can easily track usage; later can
- * either just use kmalloc, or perhaps add a slab-like allocator
- * to more carefully manage total memory utilization
- */
-static struct kmem_cache *zcache_objnode_cache;
-static struct kmem_cache *zcache_obj_cache;
-static struct kmem_cache *ramster_flnode_cache;
-static atomic_t zcache_curr_obj_count = ATOMIC_INIT(0);
-static unsigned long zcache_curr_obj_count_max;
-static atomic_t zcache_curr_objnode_count = ATOMIC_INIT(0);
-static unsigned long zcache_curr_objnode_count_max;
-
-/*
- * to avoid memory allocation recursion (e.g. due to direct reclaim), we
- * preload all necessary data structures so the hostops callbacks never
- * actually do a malloc
- */
-struct zcache_preload {
- void *page;
- struct tmem_obj *obj;
- int nr;
- struct tmem_objnode *objnodes[OBJNODE_TREE_MAX_PATH];
- struct flushlist_node *flnode;
-};
-static DEFINE_PER_CPU(struct zcache_preload, zcache_preloads) = { 0, };
-
-static int zcache_do_preload(struct tmem_pool *pool)
-{
- struct zcache_preload *kp;
- struct tmem_objnode *objnode;
- struct tmem_obj *obj;
- struct flushlist_node *flnode;
- void *page;
- int ret = -ENOMEM;
-
- if (unlikely(zcache_objnode_cache == NULL))
- goto out;
- if (unlikely(zcache_obj_cache == NULL))
- goto out;
- preempt_disable();
- kp = &__get_cpu_var(zcache_preloads);
- while (kp->nr < ARRAY_SIZE(kp->objnodes)) {
- preempt_enable_no_resched();
- objnode = kmem_cache_alloc(zcache_objnode_cache,
- ZCACHE_GFP_MASK);
- if (unlikely(objnode == NULL)) {
- zcache_failed_alloc++;
- goto out;
- }
- preempt_disable();
- kp = &__get_cpu_var(zcache_preloads);
- if (kp->nr < ARRAY_SIZE(kp->objnodes))
- kp->objnodes[kp->nr++] = objnode;
- else
- kmem_cache_free(zcache_objnode_cache, objnode);
- }
- preempt_enable_no_resched();
- obj = kmem_cache_alloc(zcache_obj_cache, ZCACHE_GFP_MASK);
- if (unlikely(obj == NULL)) {
- zcache_failed_alloc++;
- goto out;
- }
- flnode = kmem_cache_alloc(ramster_flnode_cache, ZCACHE_GFP_MASK);
- if (unlikely(flnode == NULL)) {
- zcache_failed_alloc++;
- goto out;
- }
- if (is_ephemeral(pool)) {
- page = (void *)__get_free_page(ZCACHE_GFP_MASK);
- if (unlikely(page == NULL)) {
- zcache_failed_get_free_pages++;
- kmem_cache_free(zcache_obj_cache, obj);
- kmem_cache_free(ramster_flnode_cache, flnode);
- goto out;
- }
- }
- preempt_disable();
- kp = &__get_cpu_var(zcache_preloads);
- if (kp->obj == NULL)
- kp->obj = obj;
- else
- kmem_cache_free(zcache_obj_cache, obj);
- if (kp->flnode == NULL)
- kp->flnode = flnode;
- else
- kmem_cache_free(ramster_flnode_cache, flnode);
- if (is_ephemeral(pool)) {
- if (kp->page == NULL)
- kp->page = page;
- else
- free_page((unsigned long)page);
- }
ret = 0;
out:
return ret;
}
-static int ramster_do_preload_flnode_only(struct tmem_pool *pool)
-{
- struct zcache_preload *kp;
- struct flushlist_node *flnode;
- int ret = -ENOMEM;
-
- BUG_ON(!irqs_disabled());
- if (unlikely(ramster_flnode_cache == NULL))
- BUG();
- kp = &__get_cpu_var(zcache_preloads);
- flnode = kmem_cache_alloc(ramster_flnode_cache, GFP_ATOMIC);
- if (unlikely(flnode == NULL) && kp->flnode == NULL)
- BUG(); /* FIXME handle more gracefully, but how??? */
- else if (kp->flnode == NULL)
- kp->flnode = flnode;
- else
- kmem_cache_free(ramster_flnode_cache, flnode);
- return ret;
-}
-
-static void *zcache_get_free_page(void)
-{
- struct zcache_preload *kp;
- void *page;
-
- kp = &__get_cpu_var(zcache_preloads);
- page = kp->page;
- BUG_ON(page == NULL);
- kp->page = NULL;
- return page;
-}
-
-static void zcache_free_page(void *p)
-{
- free_page((unsigned long)p);
-}
-
/*
* zcache implementation for tmem host ops
*/
@@ -1604,78 +382,56 @@ static void zcache_free_page(void *p)
static struct tmem_objnode *zcache_objnode_alloc(struct tmem_pool *pool)
{
struct tmem_objnode *objnode = NULL;
- unsigned long count;
struct zcache_preload *kp;
+ int i;
kp = &__get_cpu_var(zcache_preloads);
- if (kp->nr <= 0)
- goto out;
- objnode = kp->objnodes[kp->nr - 1];
+ for (i = 0; i < ARRAY_SIZE(kp->objnodes); i++) {
+ objnode = kp->objnodes[i];
+ if (objnode != NULL) {
+ kp->objnodes[i] = NULL;
+ break;
+ }
+ }
BUG_ON(objnode == NULL);
- kp->objnodes[kp->nr - 1] = NULL;
- kp->nr--;
- count = atomic_inc_return(&zcache_curr_objnode_count);
- if (count > zcache_curr_objnode_count_max)
- zcache_curr_objnode_count_max = count;
-out:
+ zcache_objnode_count = atomic_inc_return(&zcache_objnode_atomic);
+ if (zcache_objnode_count > zcache_objnode_count_max)
+ zcache_objnode_count_max = zcache_objnode_count;
return objnode;
}
static void zcache_objnode_free(struct tmem_objnode *objnode,
struct tmem_pool *pool)
{
- atomic_dec(&zcache_curr_objnode_count);
- BUG_ON(atomic_read(&zcache_curr_objnode_count) < 0);
+ zcache_objnode_count =
+ atomic_dec_return(&zcache_objnode_atomic);
+ BUG_ON(zcache_objnode_count < 0);
kmem_cache_free(zcache_objnode_cache, objnode);
}
static struct tmem_obj *zcache_obj_alloc(struct tmem_pool *pool)
{
struct tmem_obj *obj = NULL;
- unsigned long count;
struct zcache_preload *kp;
kp = &__get_cpu_var(zcache_preloads);
obj = kp->obj;
BUG_ON(obj == NULL);
kp->obj = NULL;
- count = atomic_inc_return(&zcache_curr_obj_count);
- if (count > zcache_curr_obj_count_max)
- zcache_curr_obj_count_max = count;
+ zcache_obj_count = atomic_inc_return(&zcache_obj_atomic);
+ if (zcache_obj_count > zcache_obj_count_max)
+ zcache_obj_count_max = zcache_obj_count;
return obj;
}
static void zcache_obj_free(struct tmem_obj *obj, struct tmem_pool *pool)
{
- atomic_dec(&zcache_curr_obj_count);
- BUG_ON(atomic_read(&zcache_curr_obj_count) < 0);
+ zcache_obj_count =
+ atomic_dec_return(&zcache_obj_atomic);
+ BUG_ON(zcache_obj_count < 0);
kmem_cache_free(zcache_obj_cache, obj);
}
-static struct flushlist_node *ramster_flnode_alloc(struct tmem_pool *pool)
-{
- struct flushlist_node *flnode = NULL;
- struct zcache_preload *kp;
- int count;
-
- kp = &__get_cpu_var(zcache_preloads);
- flnode = kp->flnode;
- BUG_ON(flnode == NULL);
- kp->flnode = NULL;
- count = atomic_inc_return(&ramster_curr_flnode_count);
- if (count > ramster_curr_flnode_count_max)
- ramster_curr_flnode_count_max = count;
- return flnode;
-}
-
-static void ramster_flnode_free(struct flushlist_node *flnode,
- struct tmem_pool *pool)
-{
- atomic_dec(&ramster_curr_flnode_count);
- BUG_ON(atomic_read(&ramster_curr_flnode_count) < 0);
- kmem_cache_free(ramster_flnode_cache, flnode);
-}
-
static struct tmem_hostops zcache_hostops = {
.obj_alloc = zcache_obj_alloc,
.obj_free = zcache_obj_free,
@@ -1683,220 +439,363 @@ static struct tmem_hostops zcache_hostops = {
.objnode_free = zcache_objnode_free,
};
-/*
- * zcache implementations for PAM page descriptor ops
- */
+static struct page *zcache_alloc_page(void)
+{
+ struct page *page = alloc_page(ZCACHE_GFP_MASK);
+ if (page != NULL)
+ zcache_pageframes_alloced =
+ atomic_inc_return(&zcache_pageframes_alloced_atomic);
+ return page;
+}
-static inline void dec_and_check(atomic_t *pvar)
+#ifdef FRONTSWAP_HAS_UNUSE
+static void zcache_unacct_page(void)
{
- atomic_dec(pvar);
- /* later when all accounting is fixed, make this a BUG */
- WARN_ON_ONCE(atomic_read(pvar) < 0);
+ zcache_pageframes_freed =
+ atomic_inc_return(&zcache_pageframes_freed_atomic);
}
+#endif
+
+static void zcache_free_page(struct page *page)
+{
+ long curr_pageframes;
+ static long max_pageframes, min_pageframes;
-static atomic_t zcache_curr_eph_pampd_count = ATOMIC_INIT(0);
-static unsigned long zcache_curr_eph_pampd_count_max;
-static atomic_t zcache_curr_pers_pampd_count = ATOMIC_INIT(0);
-static unsigned long zcache_curr_pers_pampd_count_max;
+ if (page == NULL)
+ BUG();
+ __free_page(page);
+ zcache_pageframes_freed =
+ atomic_inc_return(&zcache_pageframes_freed_atomic);
+ curr_pageframes = zcache_pageframes_alloced -
+ atomic_read(&zcache_pageframes_freed_atomic) -
+ atomic_read(&zcache_eph_pageframes_atomic) -
+ atomic_read(&zcache_pers_pageframes_atomic);
+ if (curr_pageframes > max_pageframes)
+ max_pageframes = curr_pageframes;
+ if (curr_pageframes < min_pageframes)
+ min_pageframes = curr_pageframes;
+#ifdef ZCACHE_DEBUG
+ if (curr_pageframes > 2L || curr_pageframes < -2L) {
+ /* pr_info here */
+ }
+#endif
+}
+
+/*
+ * zcache implementations for PAM page descriptor ops
+ */
/* forward reference */
-static int zcache_compress(struct page *from, void **out_va, size_t *out_len);
+static void zcache_compress(struct page *from,
+ void **out_va, unsigned *out_len);
-static int zcache_pampd_eph_create(char *data, size_t size, bool raw,
- struct tmem_pool *pool, struct tmem_oid *oid,
- uint32_t index, void **pampd)
+static struct page *zcache_evict_eph_pageframe(void);
+
+static void *zcache_pampd_eph_create(char *data, size_t size, bool raw,
+ struct tmem_handle *th)
{
- int ret = -1;
- void *cdata = data;
- size_t clen = size;
- struct zcache_client *cli = pool->client;
- uint16_t client_id = get_client_id_from_client(cli);
- struct page *page = NULL;
- unsigned long count;
+ void *pampd = NULL, *cdata = data;
+ unsigned clen = size;
+ struct page *page = (struct page *)(data), *newpage;
if (!raw) {
- page = virt_to_page(data);
- ret = zcache_compress(page, &cdata, &clen);
- if (ret == 0)
- goto out;
- if (clen == 0 || clen > zbud_max_buddy_size()) {
+ zcache_compress(page, &cdata, &clen);
+ if (clen > zbud_max_buddy_size()) {
zcache_compress_poor++;
goto out;
}
+ } else {
+ BUG_ON(clen > zbud_max_buddy_size());
}
- *pampd = (void *)zbud_create(client_id, pool->pool_id, oid,
- index, page, cdata, clen);
- if (*pampd == NULL) {
- ret = -ENOMEM;
+
+ /* look for space via an existing match first */
+ pampd = (void *)zbud_match_prep(th, true, cdata, clen);
+ if (pampd != NULL)
+ goto got_pampd;
+
+ /* no match, now we need to find (or free up) a full page */
+ newpage = zcache_alloc_page();
+ if (newpage != NULL)
+ goto create_in_new_page;
+
+ zcache_failed_getfreepages++;
+ /* can't allocate a page, evict an ephemeral page via LRU */
+ newpage = zcache_evict_eph_pageframe();
+ if (newpage == NULL) {
+ zcache_eph_ate_tail_failed++;
goto out;
}
- ret = 0;
- count = atomic_inc_return(&zcache_curr_eph_pampd_count);
- if (count > zcache_curr_eph_pampd_count_max)
- zcache_curr_eph_pampd_count_max = count;
- if (client_id != LOCAL_CLIENT) {
- count = atomic_inc_return(&ramster_foreign_eph_pampd_count);
- if (count > ramster_foreign_eph_pampd_count_max)
- ramster_foreign_eph_pampd_count_max = count;
- }
+ zcache_eph_ate_tail++;
+
+create_in_new_page:
+ pampd = (void *)zbud_create_prep(th, true, cdata, clen, newpage);
+ BUG_ON(pampd == NULL);
+ zcache_eph_pageframes =
+ atomic_inc_return(&zcache_eph_pageframes_atomic);
+ if (zcache_eph_pageframes > zcache_eph_pageframes_max)
+ zcache_eph_pageframes_max = zcache_eph_pageframes;
+
+got_pampd:
+ zcache_eph_zbytes =
+ atomic_long_add_return(clen, &zcache_eph_zbytes_atomic);
+ if (zcache_eph_zbytes > zcache_eph_zbytes_max)
+ zcache_eph_zbytes_max = zcache_eph_zbytes;
+ zcache_eph_zpages = atomic_inc_return(&zcache_eph_zpages_atomic);
+ if (zcache_eph_zpages > zcache_eph_zpages_max)
+ zcache_eph_zpages_max = zcache_eph_zpages;
+ if (ramster_enabled && raw)
+ ramster_count_foreign_pages(true, 1);
out:
- return ret;
+ return pampd;
}
-static int zcache_pampd_pers_create(char *data, size_t size, bool raw,
- struct tmem_pool *pool, struct tmem_oid *oid,
- uint32_t index, void **pampd)
+static void *zcache_pampd_pers_create(char *data, size_t size, bool raw,
+ struct tmem_handle *th)
{
- int ret = -1;
- void *cdata = data;
- size_t clen = size;
- struct zcache_client *cli = pool->client;
- struct page *page;
- unsigned long count;
- unsigned long zv_mean_zsize;
- struct zv_hdr *zv;
- long curr_pers_pampd_count;
- u64 total_zsize;
-#ifdef RAMSTER_TESTING
- static bool pampd_neg_warned;
-#endif
+ void *pampd = NULL, *cdata = data;
+ unsigned clen = size;
+ struct page *page = (struct page *)(data), *newpage;
+ unsigned long zbud_mean_zsize;
+ unsigned long curr_pers_zpages, total_zsize;
- curr_pers_pampd_count = atomic_read(&zcache_curr_pers_pampd_count) -
- atomic_read(&ramster_remote_pers_pages);
-#ifdef RAMSTER_TESTING
- /* should always be positive, but warn if accounting is off */
- if (!pampd_neg_warned) {
- pr_warn("ramster: bad accounting for curr_pers_pampd_count\n");
- pampd_neg_warned = true;
+ if (data == NULL) {
+ BUG_ON(!ramster_enabled);
+ goto create_pampd;
}
-#endif
- if (curr_pers_pampd_count >
- (zv_page_count_policy_percent * totalram_pages) / 100) {
- zcache_policy_percent_exceeded++;
- goto out;
- }
- if (raw)
- goto ok_to_create;
- page = virt_to_page(data);
- if (zcache_compress(page, &cdata, &clen) == 0)
- goto out;
+ curr_pers_zpages = zcache_pers_zpages;
+/* FIXME CONFIG_RAMSTER... subtract atomic remote_pers_pages here? */
+ if (!raw)
+ zcache_compress(page, &cdata, &clen);
/* reject if compression is too poor */
- if (clen > zv_max_zsize) {
+ if (clen > zbud_max_zsize) {
zcache_compress_poor++;
goto out;
}
/* reject if mean compression is too poor */
- if ((clen > zv_max_mean_zsize) && (curr_pers_pampd_count > 0)) {
- total_zsize = xv_get_total_size_bytes(cli->xvpool);
- zv_mean_zsize = div_u64(total_zsize, curr_pers_pampd_count);
- if (zv_mean_zsize > zv_max_mean_zsize) {
+ if ((clen > zbud_max_mean_zsize) && (curr_pers_zpages > 0)) {
+ total_zsize = zcache_pers_zbytes;
+ if ((long)total_zsize < 0)
+ total_zsize = 0;
+ zbud_mean_zsize = div_u64(total_zsize,
+ curr_pers_zpages);
+ if (zbud_mean_zsize > zbud_max_mean_zsize) {
zcache_mean_compress_poor++;
goto out;
}
}
-ok_to_create:
- *pampd = (void *)zv_create(cli, pool->pool_id, oid, index, cdata, clen);
- if (*pampd == NULL) {
- ret = -ENOMEM;
+
+create_pampd:
+ /* look for space via an existing match first */
+ pampd = (void *)zbud_match_prep(th, false, cdata, clen);
+ if (pampd != NULL)
+ goto got_pampd;
+
+ /* no match, now we need to find (or free up) a full page */
+ newpage = zcache_alloc_page();
+ if (newpage != NULL)
+ goto create_in_new_page;
+ /*
+ * FIXME do the following only if eph is oversized?
+ * if (zcache_eph_pageframes >
+ * (global_page_state(NR_LRU_BASE + LRU_ACTIVE_FILE) +
+ * global_page_state(NR_LRU_BASE + LRU_INACTIVE_FILE)))
+ */
+ zcache_failed_getfreepages++;
+ /* can't allocate a page, evict an ephemeral page via LRU */
+ newpage = zcache_evict_eph_pageframe();
+ if (newpage == NULL) {
+ zcache_pers_ate_eph_failed++;
goto out;
}
- ret = 0;
- count = atomic_inc_return(&zcache_curr_pers_pampd_count);
- if (count > zcache_curr_pers_pampd_count_max)
- zcache_curr_pers_pampd_count_max = count;
- if (is_local_client(cli))
- goto out;
- zv = *(struct zv_hdr **)pampd;
- count = atomic_inc_return(&ramster_foreign_pers_pampd_count);
- if (count > ramster_foreign_pers_pampd_count_max)
- ramster_foreign_pers_pampd_count_max = count;
+ zcache_pers_ate_eph++;
+
+create_in_new_page:
+ pampd = (void *)zbud_create_prep(th, false, cdata, clen, newpage);
+ BUG_ON(pampd == NULL);
+ zcache_pers_pageframes =
+ atomic_inc_return(&zcache_pers_pageframes_atomic);
+ if (zcache_pers_pageframes > zcache_pers_pageframes_max)
+ zcache_pers_pageframes_max = zcache_pers_pageframes;
+
+got_pampd:
+ zcache_pers_zpages = atomic_inc_return(&zcache_pers_zpages_atomic);
+ if (zcache_pers_zpages > zcache_pers_zpages_max)
+ zcache_pers_zpages_max = zcache_pers_zpages;
+ zcache_pers_zbytes =
+ atomic_long_add_return(clen, &zcache_pers_zbytes_atomic);
+ if (zcache_pers_zbytes > zcache_pers_zbytes_max)
+ zcache_pers_zbytes_max = zcache_pers_zbytes;
+ if (ramster_enabled && raw)
+ ramster_count_foreign_pages(false, 1);
out:
- return ret;
+ return pampd;
}
-static void *zcache_pampd_create(char *data, size_t size, bool raw, int eph,
- struct tmem_pool *pool, struct tmem_oid *oid,
- uint32_t index)
+/*
+ * This is called directly from zcache_put_page to pre-allocate space
+ * to store a zpage.
+ */
+void *zcache_pampd_create(char *data, unsigned int size, bool raw,
+ int eph, struct tmem_handle *th)
{
void *pampd = NULL;
- int ret;
- bool ephemeral;
+ struct zcache_preload *kp;
+ struct tmem_objnode *objnode;
+ struct tmem_obj *obj;
+ int i;
- BUG_ON(preemptible());
- ephemeral = (eph == 1) || ((eph == 0) && is_ephemeral(pool));
- if (ephemeral)
- ret = zcache_pampd_eph_create(data, size, raw, pool,
- oid, index, &pampd);
+ BUG_ON(!irqs_disabled());
+ /* pre-allocate per-cpu metadata */
+ BUG_ON(zcache_objnode_cache == NULL);
+ BUG_ON(zcache_obj_cache == NULL);
+ kp = &__get_cpu_var(zcache_preloads);
+ for (i = 0; i < ARRAY_SIZE(kp->objnodes); i++) {
+ objnode = kp->objnodes[i];
+ if (objnode == NULL) {
+ objnode = kmem_cache_alloc(zcache_objnode_cache,
+ ZCACHE_GFP_MASK);
+ if (unlikely(objnode == NULL)) {
+ zcache_failed_alloc++;
+ goto out;
+ }
+ kp->objnodes[i] = objnode;
+ }
+ }
+ if (kp->obj == NULL) {
+ obj = kmem_cache_alloc(zcache_obj_cache, ZCACHE_GFP_MASK);
+ kp->obj = obj;
+ }
+ if (unlikely(kp->obj == NULL)) {
+ zcache_failed_alloc++;
+ goto out;
+ }
+ /*
+ * ok, have all the metadata pre-allocated, now do the data
+ * but since how we allocate the data is dependent on ephemeral
+ * or persistent, we split the call here to different sub-functions
+ */
+ if (eph)
+ pampd = zcache_pampd_eph_create(data, size, raw, th);
else
- ret = zcache_pampd_pers_create(data, size, raw, pool,
- oid, index, &pampd);
- /* FIXME add some counters here for failed creates? */
+ pampd = zcache_pampd_pers_create(data, size, raw, th);
+out:
return pampd;
}
/*
+ * This is a pamops called via tmem_put and is necessary to "finish"
+ * a pampd creation.
+ */
+void zcache_pampd_create_finish(void *pampd, bool eph)
+{
+ zbud_create_finish((struct zbudref *)pampd, eph);
+}
+
+/*
+ * This is passed as a function parameter to zbud_decompress so that
+ * zbud need not be familiar with the details of crypto. It assumes that
+ * the bytes from_va and to_va through from_va+size-1 and to_va+size-1 are
+ * kmapped. It must be successful, else there is a logic bug somewhere.
+ */
+static void zcache_decompress(char *from_va, unsigned int size, char *to_va)
+{
+ int ret;
+ unsigned int outlen = PAGE_SIZE;
+
+ ret = zcache_comp_op(ZCACHE_COMPOP_DECOMPRESS, from_va, size,
+ to_va, &outlen);
+ BUG_ON(ret);
+ BUG_ON(outlen != PAGE_SIZE);
+}
+
+/*
+ * Decompress from the kernel va to a pageframe
+ */
+void zcache_decompress_to_page(char *from_va, unsigned int size,
+ struct page *to_page)
+{
+ char *to_va = kmap_atomic(to_page);
+ zcache_decompress(from_va, size, to_va);
+ kunmap_atomic(to_va);
+}
+
+/*
* fill the pageframe corresponding to the struct page with the data
* from the passed pampd
*/
-static int zcache_pampd_get_data(char *data, size_t *bufsize, bool raw,
+static int zcache_pampd_get_data(char *data, size_t *sizep, bool raw,
void *pampd, struct tmem_pool *pool,
struct tmem_oid *oid, uint32_t index)
{
- int ret = 0;
+ int ret;
+ bool eph = !is_persistent(pool);
BUG_ON(preemptible());
- BUG_ON(is_ephemeral(pool)); /* Fix later for shared pools? */
+ BUG_ON(eph); /* fix later if shared pools get implemented */
BUG_ON(pampd_is_remote(pampd));
if (raw)
- zv_copy_from_pampd(data, bufsize, pampd);
- else
- zv_decompress(virt_to_page(data), pampd);
+ ret = zbud_copy_from_zbud(data, (struct zbudref *)pampd,
+ sizep, eph);
+ else {
+ ret = zbud_decompress((struct page *)(data),
+ (struct zbudref *)pampd, false,
+ zcache_decompress);
+ *sizep = PAGE_SIZE;
+ }
return ret;
}
-static int zcache_pampd_get_data_and_free(char *data, size_t *bufsize, bool raw,
+/*
+ * fill the pageframe corresponding to the struct page with the data
+ * from the passed pampd
+ */
+static int zcache_pampd_get_data_and_free(char *data, size_t *sizep, bool raw,
void *pampd, struct tmem_pool *pool,
struct tmem_oid *oid, uint32_t index)
{
- int ret = 0;
- unsigned long flags;
- struct zcache_client *cli = pool->client;
+ int ret;
+ bool eph = !is_persistent(pool);
+ struct page *page = NULL;
+ unsigned int zsize, zpages;
BUG_ON(preemptible());
BUG_ON(pampd_is_remote(pampd));
- if (is_ephemeral(pool)) {
- local_irq_save(flags);
- if (raw)
- zbud_copy_from_pampd(data, bufsize, pampd);
- else
- ret = zbud_decompress(virt_to_page(data), pampd);
- zbud_free_and_delist((struct zbud_hdr *)pampd);
- local_irq_restore(flags);
- if (!is_local_client(cli))
- dec_and_check(&ramster_foreign_eph_pampd_count);
- dec_and_check(&zcache_curr_eph_pampd_count);
+ if (raw)
+ ret = zbud_copy_from_zbud(data, (struct zbudref *)pampd,
+ sizep, eph);
+ else {
+ ret = zbud_decompress((struct page *)(data),
+ (struct zbudref *)pampd, eph,
+ zcache_decompress);
+ *sizep = PAGE_SIZE;
+ }
+ page = zbud_free_and_delist((struct zbudref *)pampd, eph,
+ &zsize, &zpages);
+ if (eph) {
+ if (page)
+ zcache_eph_pageframes =
+ atomic_dec_return(&zcache_eph_pageframes_atomic);
+ zcache_eph_zpages =
+ atomic_sub_return(zpages, &zcache_eph_zpages_atomic);
+ zcache_eph_zbytes =
+ atomic_long_sub_return(zsize, &zcache_eph_zbytes_atomic);
} else {
- if (is_local_client(cli))
- BUG();
- if (raw)
- zv_copy_from_pampd(data, bufsize, pampd);
- else
- zv_decompress(virt_to_page(data), pampd);
- zv_free(cli->xvpool, pampd);
- if (!is_local_client(cli))
- dec_and_check(&ramster_foreign_pers_pampd_count);
- dec_and_check(&zcache_curr_pers_pampd_count);
- ret = 0;
- }
+ if (page)
+ zcache_pers_pageframes =
+ atomic_dec_return(&zcache_pers_pageframes_atomic);
+ zcache_pers_zpages =
+ atomic_sub_return(zpages, &zcache_pers_zpages_atomic);
+ zcache_pers_zbytes =
+ atomic_long_sub_return(zsize, &zcache_pers_zbytes_atomic);
+ }
+ if (!is_local_client(pool->client))
+ ramster_count_foreign_pages(eph, -1);
+ if (page)
+ zcache_free_page(page);
return ret;
}
-static bool zcache_pampd_is_remote(void *pampd)
-{
- return pampd_is_remote(pampd);
-}
-
/*
* free the pampd and remove it from any zcache lists
* pampd must no longer be pointed to from any tmem data structures!
@@ -1904,362 +803,134 @@ static bool zcache_pampd_is_remote(void *pampd)
static void zcache_pampd_free(void *pampd, struct tmem_pool *pool,
struct tmem_oid *oid, uint32_t index, bool acct)
{
- struct zcache_client *cli = pool->client;
- bool eph = is_ephemeral(pool);
- struct zv_hdr *zv;
+ struct page *page = NULL;
+ unsigned int zsize, zpages;
BUG_ON(preemptible());
if (pampd_is_remote(pampd)) {
- WARN_ON(acct == false);
- if (oid == NULL) {
- /*
- * a NULL oid means to ignore this pampd free
- * as the remote freeing will be handled elsewhere
- */
- } else if (eph) {
- /* FIXME remote flush optional but probably good idea */
- /* FIXME get these working properly again */
- dec_and_check(&zcache_curr_eph_pampd_count);
- } else if (pampd_is_intransit(pampd)) {
- /* did a pers remote get_and_free, so just free local */
- pampd = pampd_mask_intransit_and_remote(pampd);
- goto local_pers;
- } else {
- struct flushlist_node *flnode =
- ramster_flnode_alloc(pool);
-
- flnode->xh.client_id = pampd_remote_node(pampd);
- flnode->xh.pool_id = pool->pool_id;
- flnode->xh.oid = *oid;
- flnode->xh.index = index;
- flnode->rem_op.op = RAMSTER_REMOTIFY_FLUSH_PAGE;
- spin_lock(&zcache_rem_op_list_lock);
- list_add(&flnode->rem_op.list, &zcache_rem_op_list);
- spin_unlock(&zcache_rem_op_list_lock);
- dec_and_check(&zcache_curr_pers_pampd_count);
- dec_and_check(&ramster_remote_pers_pages);
- }
- } else if (eph) {
- zbud_free_and_delist((struct zbud_hdr *)pampd);
- if (!is_local_client(pool->client))
- dec_and_check(&ramster_foreign_eph_pampd_count);
- if (acct)
- /* FIXME get these working properly again */
- dec_and_check(&zcache_curr_eph_pampd_count);
- } else {
-local_pers:
- zv = (struct zv_hdr *)pampd;
- if (!is_local_client(pool->client))
- dec_and_check(&ramster_foreign_pers_pampd_count);
- zv_free(cli->xvpool, zv);
- if (acct)
- /* FIXME get these working properly again */
- dec_and_check(&zcache_curr_pers_pampd_count);
+ BUG_ON(!ramster_enabled);
+ pampd = ramster_pampd_free(pampd, pool, oid, index, acct);
+ if (pampd == NULL)
+ return;
}
-}
-
-static void zcache_pampd_free_obj(struct tmem_pool *pool,
- struct tmem_obj *obj)
-{
- struct flushlist_node *flnode;
-
- BUG_ON(preemptible());
- if (obj->extra == NULL)
- return;
- BUG_ON(!pampd_is_remote(obj->extra));
- flnode = ramster_flnode_alloc(pool);
- flnode->xh.client_id = pampd_remote_node(obj->extra);
- flnode->xh.pool_id = pool->pool_id;
- flnode->xh.oid = obj->oid;
- flnode->xh.index = FLUSH_ENTIRE_OBJECT;
- flnode->rem_op.op = RAMSTER_REMOTIFY_FLUSH_OBJ;
- spin_lock(&zcache_rem_op_list_lock);
- list_add(&flnode->rem_op.list, &zcache_rem_op_list);
- spin_unlock(&zcache_rem_op_list_lock);
-}
-
-void zcache_pampd_new_obj(struct tmem_obj *obj)
-{
- obj->extra = NULL;
-}
-
-int zcache_pampd_replace_in_obj(void *new_pampd, struct tmem_obj *obj)
-{
- int ret = -1;
-
- if (new_pampd != NULL) {
- if (obj->extra == NULL)
- obj->extra = new_pampd;
- /* enforce that all remote pages in an object reside
- * in the same node! */
- else if (pampd_remote_node(new_pampd) !=
- pampd_remote_node((void *)(obj->extra)))
- BUG();
- ret = 0;
- }
- return ret;
-}
-
-/*
- * Called by the message handler after a (still compressed) page has been
- * fetched from the remote machine in response to an "is_remote" tmem_get
- * or persistent tmem_localify. For a tmem_get, "extra" is the address of
- * the page that is to be filled to successfully resolve the tmem_get; for
- * a (persistent) tmem_localify, "extra" is NULL (as the data is placed only
- * in the local zcache). "data" points to "size" bytes of (compressed) data
- * passed in the message. In the case of a persistent remote get, if
- * pre-allocation was successful (see zcache_repatriate_preload), the page
- * is placed into both local zcache and at "extra".
- */
-int zcache_localify(int pool_id, struct tmem_oid *oidp,
- uint32_t index, char *data, size_t size,
- void *extra)
-{
- int ret = -ENOENT;
- unsigned long flags;
- struct tmem_pool *pool;
- bool ephemeral, delete = false;
- size_t clen = PAGE_SIZE;
- void *pampd, *saved_hb;
- struct tmem_obj *obj;
-
- pool = zcache_get_pool_by_id(LOCAL_CLIENT, pool_id);
- if (unlikely(pool == NULL))
- /* pool doesn't exist anymore */
- goto out;
- ephemeral = is_ephemeral(pool);
- local_irq_save(flags); /* FIXME: maybe only disable softirqs? */
- pampd = tmem_localify_get_pampd(pool, oidp, index, &obj, &saved_hb);
- if (pampd == NULL) {
- /* hmmm... must have been a flush while waiting */
-#ifdef RAMSTER_TESTING
- pr_err("UNTESTED pampd==NULL in zcache_localify\n");
-#endif
- if (ephemeral)
- ramster_remote_eph_pages_unsucc_get++;
- else
- ramster_remote_pers_pages_unsucc_get++;
- obj = NULL;
- goto finish;
- } else if (unlikely(!pampd_is_remote(pampd))) {
- /* hmmm... must have been a dup put while waiting */
-#ifdef RAMSTER_TESTING
- pr_err("UNTESTED dup while waiting in zcache_localify\n");
-#endif
- if (ephemeral)
- ramster_remote_eph_pages_unsucc_get++;
- else
- ramster_remote_pers_pages_unsucc_get++;
- obj = NULL;
- pampd = NULL;
- ret = -EEXIST;
- goto finish;
- } else if (size == 0) {
- /* no remote data, delete the local is_remote pampd */
- pampd = NULL;
- if (ephemeral)
- ramster_remote_eph_pages_unsucc_get++;
- else
- BUG();
- delete = true;
- goto finish;
- }
- if (!ephemeral && pampd_is_intransit(pampd)) {
- /* localify to zcache */
- pampd = pampd_mask_intransit_and_remote(pampd);
- zv_copy_to_pampd(pampd, data, size);
+ if (is_ephemeral(pool)) {
+ page = zbud_free_and_delist((struct zbudref *)pampd,
+ true, &zsize, &zpages);
+ if (page)
+ zcache_eph_pageframes =
+ atomic_dec_return(&zcache_eph_pageframes_atomic);
+ zcache_eph_zpages =
+ atomic_sub_return(zpages, &zcache_eph_zpages_atomic);
+ zcache_eph_zbytes =
+ atomic_long_sub_return(zsize, &zcache_eph_zbytes_atomic);
+ /* FIXME CONFIG_RAMSTER... check acct parameter? */
} else {
- pampd = NULL;
- obj = NULL;
- }
- if (extra != NULL) {
- /* decompress direct-to-memory to complete remotify */
- ret = lzo1x_decompress_safe((char *)data, size,
- (char *)extra, &clen);
- BUG_ON(ret != LZO_E_OK);
- BUG_ON(clen != PAGE_SIZE);
- }
- if (ephemeral)
- ramster_remote_eph_pages_succ_get++;
- else
- ramster_remote_pers_pages_succ_get++;
- ret = 0;
-finish:
- tmem_localify_finish(obj, index, pampd, saved_hb, delete);
- zcache_put_pool(pool);
- local_irq_restore(flags);
-out:
- return ret;
-}
-
-/*
- * Called on a remote persistent tmem_get to attempt to preallocate
- * local storage for the data contained in the remote persistent page.
- * If successfully preallocated, returns the pampd, marked as remote and
- * in_transit. Else returns NULL. Note that the appropriate tmem data
- * structure must be locked.
- */
-static void *zcache_pampd_repatriate_preload(void *pampd,
- struct tmem_pool *pool,
- struct tmem_oid *oid,
- uint32_t index,
- bool *intransit)
-{
- int clen = pampd_remote_size(pampd);
- void *ret_pampd = NULL;
- unsigned long flags;
-
- if (!pampd_is_remote(pampd))
- BUG();
- if (is_ephemeral(pool))
- BUG();
- if (pampd_is_intransit(pampd)) {
- /*
- * to avoid multiple allocations (and maybe a memory leak)
- * don't preallocate if already in the process of being
- * repatriated
- */
- *intransit = true;
- goto out;
- }
- *intransit = false;
- local_irq_save(flags);
- ret_pampd = (void *)zv_alloc(pool, oid, index, clen);
- if (ret_pampd != NULL) {
- /*
- * a pampd is marked intransit if it is remote and space has
- * been allocated for it locally (note, only happens for
- * persistent pages, in which case the remote copy is freed)
- */
- ret_pampd = pampd_mark_intransit(ret_pampd);
- dec_and_check(&ramster_remote_pers_pages);
- } else
- ramster_pers_pages_remote_nomem++;
- local_irq_restore(flags);
-out:
- return ret_pampd;
-}
-
-/*
- * Called on a remote tmem_get to invoke a message to fetch the page.
- * Might sleep so no tmem locks can be held. "extra" is passed
- * all the way through the round-trip messaging to zcache_localify.
- */
-static int zcache_pampd_repatriate(void *fake_pampd, void *real_pampd,
- struct tmem_pool *pool,
- struct tmem_oid *oid, uint32_t index,
- bool free, void *extra)
-{
- struct tmem_xhandle xh;
- int ret;
-
- if (pampd_is_intransit(real_pampd))
- /* have local space pre-reserved, so free remote copy */
- free = true;
- xh = tmem_xhandle_fill(LOCAL_CLIENT, pool, oid, index);
- /* unreliable request/response for now */
- ret = ramster_remote_async_get(&xh, free,
- pampd_remote_node(fake_pampd),
- pampd_remote_size(fake_pampd),
- pampd_remote_cksum(fake_pampd),
- extra);
-#ifdef RAMSTER_TESTING
- if (ret != 0 && ret != -ENOENT)
- pr_err("TESTING zcache_pampd_repatriate returns, ret=%d\n",
- ret);
-#endif
- return ret;
+ page = zbud_free_and_delist((struct zbudref *)pampd,
+ false, &zsize, &zpages);
+ if (page)
+ zcache_pers_pageframes =
+ atomic_dec_return(&zcache_pers_pageframes_atomic);
+ zcache_pers_zpages =
+ atomic_sub_return(zpages, &zcache_pers_zpages_atomic);
+ zcache_pers_zbytes =
+ atomic_long_sub_return(zsize, &zcache_pers_zbytes_atomic);
+ }
+ if (!is_local_client(pool->client))
+ ramster_count_foreign_pages(is_ephemeral(pool), -1);
+ if (page)
+ zcache_free_page(page);
}
static struct tmem_pamops zcache_pamops = {
- .create = zcache_pampd_create,
+ .create_finish = zcache_pampd_create_finish,
.get_data = zcache_pampd_get_data,
- .free = zcache_pampd_free,
.get_data_and_free = zcache_pampd_get_data_and_free,
- .free_obj = zcache_pampd_free_obj,
- .is_remote = zcache_pampd_is_remote,
- .repatriate_preload = zcache_pampd_repatriate_preload,
- .repatriate = zcache_pampd_repatriate,
- .new_obj = zcache_pampd_new_obj,
- .replace_in_obj = zcache_pampd_replace_in_obj,
+ .free = zcache_pampd_free,
};
/*
* zcache compression/decompression and related per-cpu stuff
*/
-#define LZO_WORKMEM_BYTES LZO1X_1_MEM_COMPRESS
-#define LZO_DSTMEM_PAGE_ORDER 1
-static DEFINE_PER_CPU(unsigned char *, zcache_workmem);
static DEFINE_PER_CPU(unsigned char *, zcache_dstmem);
+#define ZCACHE_DSTMEM_ORDER 1
-static int zcache_compress(struct page *from, void **out_va, size_t *out_len)
+static void zcache_compress(struct page *from, void **out_va, unsigned *out_len)
{
- int ret = 0;
+ int ret;
unsigned char *dmem = __get_cpu_var(zcache_dstmem);
- unsigned char *wmem = __get_cpu_var(zcache_workmem);
char *from_va;
BUG_ON(!irqs_disabled());
- if (unlikely(dmem == NULL || wmem == NULL))
- goto out; /* no buffer, so can't compress */
+ /* no buffer or no compressor so can't compress */
+ BUG_ON(dmem == NULL);
+ *out_len = PAGE_SIZE << ZCACHE_DSTMEM_ORDER;
from_va = kmap_atomic(from);
mb();
- ret = lzo1x_1_compress(from_va, PAGE_SIZE, dmem, out_len, wmem);
- BUG_ON(ret != LZO_E_OK);
+ ret = zcache_comp_op(ZCACHE_COMPOP_COMPRESS, from_va, PAGE_SIZE, dmem,
+ out_len);
+ BUG_ON(ret);
*out_va = dmem;
kunmap_atomic(from_va);
- ret = 1;
-out:
- return ret;
}
+static int zcache_comp_cpu_up(int cpu)
+{
+ struct crypto_comp *tfm;
+
+ tfm = crypto_alloc_comp(zcache_comp_name, 0, 0);
+ if (IS_ERR(tfm))
+ return NOTIFY_BAD;
+ *per_cpu_ptr(zcache_comp_pcpu_tfms, cpu) = tfm;
+ return NOTIFY_OK;
+}
+
+static void zcache_comp_cpu_down(int cpu)
+{
+ struct crypto_comp *tfm;
+
+ tfm = *per_cpu_ptr(zcache_comp_pcpu_tfms, cpu);
+ crypto_free_comp(tfm);
+ *per_cpu_ptr(zcache_comp_pcpu_tfms, cpu) = NULL;
+}
static int zcache_cpu_notifier(struct notifier_block *nb,
unsigned long action, void *pcpu)
{
- int cpu = (long)pcpu;
+ int ret, i, cpu = (long)pcpu;
struct zcache_preload *kp;
switch (action) {
case CPU_UP_PREPARE:
+ ret = zcache_comp_cpu_up(cpu);
+ if (ret != NOTIFY_OK) {
+ pr_err("%s: can't allocate compressor xform\n",
+ namestr);
+ return ret;
+ }
per_cpu(zcache_dstmem, cpu) = (void *)__get_free_pages(
- GFP_KERNEL | __GFP_REPEAT,
- LZO_DSTMEM_PAGE_ORDER),
- per_cpu(zcache_workmem, cpu) =
- kzalloc(LZO1X_MEM_COMPRESS,
- GFP_KERNEL | __GFP_REPEAT);
- per_cpu(zcache_remoteputmem, cpu) =
- kzalloc(PAGE_SIZE, GFP_KERNEL | __GFP_REPEAT);
+ GFP_KERNEL | __GFP_REPEAT, ZCACHE_DSTMEM_ORDER);
+ if (ramster_enabled)
+ ramster_cpu_up(cpu);
break;
case CPU_DEAD:
case CPU_UP_CANCELED:
- kfree(per_cpu(zcache_remoteputmem, cpu));
- per_cpu(zcache_remoteputmem, cpu) = NULL;
+ zcache_comp_cpu_down(cpu);
free_pages((unsigned long)per_cpu(zcache_dstmem, cpu),
- LZO_DSTMEM_PAGE_ORDER);
+ ZCACHE_DSTMEM_ORDER);
per_cpu(zcache_dstmem, cpu) = NULL;
- kfree(per_cpu(zcache_workmem, cpu));
- per_cpu(zcache_workmem, cpu) = NULL;
kp = &per_cpu(zcache_preloads, cpu);
- while (kp->nr) {
- kmem_cache_free(zcache_objnode_cache,
- kp->objnodes[kp->nr - 1]);
- kp->objnodes[kp->nr - 1] = NULL;
- kp->nr--;
+ for (i = 0; i < ARRAY_SIZE(kp->objnodes); i++) {
+ if (kp->objnodes[i])
+ kmem_cache_free(zcache_objnode_cache,
+ kp->objnodes[i]);
}
if (kp->obj) {
kmem_cache_free(zcache_obj_cache, kp->obj);
kp->obj = NULL;
}
- if (kp->flnode) {
- kmem_cache_free(ramster_flnode_cache, kp->flnode);
- kp->flnode = NULL;
- }
- if (kp->page) {
- free_page((unsigned long)kp->page);
- kp->page = NULL;
- }
+ if (ramster_enabled)
+ ramster_cpu_down(cpu);
break;
default:
break;
@@ -2271,314 +942,104 @@ static struct notifier_block zcache_cpu_notifier_block = {
.notifier_call = zcache_cpu_notifier
};
-#ifdef CONFIG_SYSFS
-#define ZCACHE_SYSFS_RO(_name) \
- static ssize_t zcache_##_name##_show(struct kobject *kobj, \
- struct kobj_attribute *attr, char *buf) \
- { \
- return sprintf(buf, "%lu\n", zcache_##_name); \
- } \
- static struct kobj_attribute zcache_##_name##_attr = { \
- .attr = { .name = __stringify(_name), .mode = 0444 }, \
- .show = zcache_##_name##_show, \
- }
-
-#define ZCACHE_SYSFS_RO_ATOMIC(_name) \
- static ssize_t zcache_##_name##_show(struct kobject *kobj, \
- struct kobj_attribute *attr, char *buf) \
- { \
- return sprintf(buf, "%d\n", atomic_read(&zcache_##_name)); \
- } \
- static struct kobj_attribute zcache_##_name##_attr = { \
- .attr = { .name = __stringify(_name), .mode = 0444 }, \
- .show = zcache_##_name##_show, \
- }
-
-#define ZCACHE_SYSFS_RO_CUSTOM(_name, _func) \
- static ssize_t zcache_##_name##_show(struct kobject *kobj, \
- struct kobj_attribute *attr, char *buf) \
- { \
- return _func(buf); \
- } \
- static struct kobj_attribute zcache_##_name##_attr = { \
- .attr = { .name = __stringify(_name), .mode = 0444 }, \
- .show = zcache_##_name##_show, \
- }
-
-ZCACHE_SYSFS_RO(curr_obj_count_max);
-ZCACHE_SYSFS_RO(curr_objnode_count_max);
-ZCACHE_SYSFS_RO(flush_total);
-ZCACHE_SYSFS_RO(flush_found);
-ZCACHE_SYSFS_RO(flobj_total);
-ZCACHE_SYSFS_RO(flobj_found);
-ZCACHE_SYSFS_RO(failed_eph_puts);
-ZCACHE_SYSFS_RO(nonactive_puts);
-ZCACHE_SYSFS_RO(failed_pers_puts);
-ZCACHE_SYSFS_RO(zbud_curr_zbytes);
-ZCACHE_SYSFS_RO(zbud_cumul_zpages);
-ZCACHE_SYSFS_RO(zbud_cumul_zbytes);
-ZCACHE_SYSFS_RO(zbud_buddied_count);
-ZCACHE_SYSFS_RO(evicted_raw_pages);
-ZCACHE_SYSFS_RO(evicted_unbuddied_pages);
-ZCACHE_SYSFS_RO(evicted_buddied_pages);
-ZCACHE_SYSFS_RO(failed_get_free_pages);
-ZCACHE_SYSFS_RO(failed_alloc);
-ZCACHE_SYSFS_RO(put_to_flush);
-ZCACHE_SYSFS_RO(compress_poor);
-ZCACHE_SYSFS_RO(mean_compress_poor);
-ZCACHE_SYSFS_RO(policy_percent_exceeded);
-ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_raw_pages);
-ZCACHE_SYSFS_RO_ATOMIC(zbud_curr_zpages);
-ZCACHE_SYSFS_RO_ATOMIC(curr_obj_count);
-ZCACHE_SYSFS_RO_ATOMIC(curr_objnode_count);
-ZCACHE_SYSFS_RO_CUSTOM(zbud_unbuddied_list_counts,
- zbud_show_unbuddied_list_counts);
-ZCACHE_SYSFS_RO_CUSTOM(zbud_cumul_chunk_counts,
- zbud_show_cumul_chunk_counts);
-ZCACHE_SYSFS_RO_CUSTOM(zv_curr_dist_counts,
- zv_curr_dist_counts_show);
-ZCACHE_SYSFS_RO_CUSTOM(zv_cumul_dist_counts,
- zv_cumul_dist_counts_show);
-
-static struct attribute *zcache_attrs[] = {
- &zcache_curr_obj_count_attr.attr,
- &zcache_curr_obj_count_max_attr.attr,
- &zcache_curr_objnode_count_attr.attr,
- &zcache_curr_objnode_count_max_attr.attr,
- &zcache_flush_total_attr.attr,
- &zcache_flobj_total_attr.attr,
- &zcache_flush_found_attr.attr,
- &zcache_flobj_found_attr.attr,
- &zcache_failed_eph_puts_attr.attr,
- &zcache_nonactive_puts_attr.attr,
- &zcache_failed_pers_puts_attr.attr,
- &zcache_policy_percent_exceeded_attr.attr,
- &zcache_compress_poor_attr.attr,
- &zcache_mean_compress_poor_attr.attr,
- &zcache_zbud_curr_raw_pages_attr.attr,
- &zcache_zbud_curr_zpages_attr.attr,
- &zcache_zbud_curr_zbytes_attr.attr,
- &zcache_zbud_cumul_zpages_attr.attr,
- &zcache_zbud_cumul_zbytes_attr.attr,
- &zcache_zbud_buddied_count_attr.attr,
- &zcache_evicted_raw_pages_attr.attr,
- &zcache_evicted_unbuddied_pages_attr.attr,
- &zcache_evicted_buddied_pages_attr.attr,
- &zcache_failed_get_free_pages_attr.attr,
- &zcache_failed_alloc_attr.attr,
- &zcache_put_to_flush_attr.attr,
- &zcache_zbud_unbuddied_list_counts_attr.attr,
- &zcache_zbud_cumul_chunk_counts_attr.attr,
- &zcache_zv_curr_dist_counts_attr.attr,
- &zcache_zv_cumul_dist_counts_attr.attr,
- &zcache_zv_max_zsize_attr.attr,
- &zcache_zv_max_mean_zsize_attr.attr,
- &zcache_zv_page_count_policy_percent_attr.attr,
- NULL,
-};
-
-static struct attribute_group zcache_attr_group = {
- .attrs = zcache_attrs,
- .name = "zcache",
-};
-
-#define RAMSTER_SYSFS_RO(_name) \
- static ssize_t ramster_##_name##_show(struct kobject *kobj, \
- struct kobj_attribute *attr, char *buf) \
- { \
- return sprintf(buf, "%lu\n", ramster_##_name); \
- } \
- static struct kobj_attribute ramster_##_name##_attr = { \
- .attr = { .name = __stringify(_name), .mode = 0444 }, \
- .show = ramster_##_name##_show, \
- }
-
-#define RAMSTER_SYSFS_RW(_name) \
- static ssize_t ramster_##_name##_show(struct kobject *kobj, \
- struct kobj_attribute *attr, char *buf) \
- { \
- return sprintf(buf, "%lu\n", ramster_##_name); \
- } \
- static ssize_t ramster_##_name##_store(struct kobject *kobj, \
- struct kobj_attribute *attr, const char *buf, size_t count) \
- { \
- int err; \
- unsigned long enable; \
- err = kstrtoul(buf, 10, &enable); \
- if (err) \
- return -EINVAL; \
- ramster_##_name = enable; \
- return count; \
- } \
- static struct kobj_attribute ramster_##_name##_attr = { \
- .attr = { .name = __stringify(_name), .mode = 0644 }, \
- .show = ramster_##_name##_show, \
- .store = ramster_##_name##_store, \
- }
-
-#define RAMSTER_SYSFS_RO_ATOMIC(_name) \
- static ssize_t ramster_##_name##_show(struct kobject *kobj, \
- struct kobj_attribute *attr, char *buf) \
- { \
- return sprintf(buf, "%d\n", atomic_read(&ramster_##_name)); \
- } \
- static struct kobj_attribute ramster_##_name##_attr = { \
- .attr = { .name = __stringify(_name), .mode = 0444 }, \
- .show = ramster_##_name##_show, \
- }
-
-RAMSTER_SYSFS_RO(interface_revision);
-RAMSTER_SYSFS_RO_ATOMIC(remote_pers_pages);
-RAMSTER_SYSFS_RW(pers_remotify_enable);
-RAMSTER_SYSFS_RW(eph_remotify_enable);
-RAMSTER_SYSFS_RO(eph_pages_remoted);
-RAMSTER_SYSFS_RO(eph_pages_remote_failed);
-RAMSTER_SYSFS_RO(pers_pages_remoted);
-RAMSTER_SYSFS_RO(pers_pages_remote_failed);
-RAMSTER_SYSFS_RO(pers_pages_remote_nomem);
-RAMSTER_SYSFS_RO(remote_pages_flushed);
-RAMSTER_SYSFS_RO(remote_page_flushes_failed);
-RAMSTER_SYSFS_RO(remote_objects_flushed);
-RAMSTER_SYSFS_RO(remote_object_flushes_failed);
-RAMSTER_SYSFS_RO(remote_eph_pages_succ_get);
-RAMSTER_SYSFS_RO(remote_eph_pages_unsucc_get);
-RAMSTER_SYSFS_RO(remote_pers_pages_succ_get);
-RAMSTER_SYSFS_RO(remote_pers_pages_unsucc_get);
-RAMSTER_SYSFS_RO_ATOMIC(foreign_eph_pampd_count);
-RAMSTER_SYSFS_RO(foreign_eph_pampd_count_max);
-RAMSTER_SYSFS_RO_ATOMIC(foreign_pers_pampd_count);
-RAMSTER_SYSFS_RO(foreign_pers_pampd_count_max);
-RAMSTER_SYSFS_RO_ATOMIC(curr_flnode_count);
-RAMSTER_SYSFS_RO(curr_flnode_count_max);
-
-#define MANUAL_NODES 8
-static bool ramster_nodes_manual_up[MANUAL_NODES];
-static ssize_t ramster_manual_node_up_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
-{
- int i;
- char *p = buf;
- for (i = 0; i < MANUAL_NODES; i++)
- if (ramster_nodes_manual_up[i])
- p += sprintf(p, "%d ", i);
- p += sprintf(p, "\n");
- return p - buf;
-}
+/*
+ * The following code interacts with the zbud eviction and zbud
+ * zombify code to access LRU pages
+ */
-static ssize_t ramster_manual_node_up_store(struct kobject *kobj,
- struct kobj_attribute *attr, const char *buf, size_t count)
+static struct page *zcache_evict_eph_pageframe(void)
{
- int err;
- unsigned long node_num;
+ struct page *page;
+ unsigned int zsize = 0, zpages = 0;
- err = kstrtoul(buf, 10, &node_num);
- if (err) {
- pr_err("ramster: bad strtoul?\n");
- return -EINVAL;
- }
- if (node_num >= MANUAL_NODES) {
- pr_err("ramster: bad node_num=%lu?\n", node_num);
- return -EINVAL;
- }
- if (ramster_nodes_manual_up[node_num]) {
- pr_err("ramster: node %d already up, ignoring\n",
- (int)node_num);
- } else {
- ramster_nodes_manual_up[node_num] = true;
- r2net_hb_node_up_manual((int)node_num);
- }
- return count;
+ page = zbud_evict_pageframe_lru(&zsize, &zpages);
+ if (page == NULL)
+ goto out;
+ zcache_eph_zbytes = atomic_long_sub_return(zsize,
+ &zcache_eph_zbytes_atomic);
+ zcache_eph_zpages = atomic_sub_return(zpages,
+ &zcache_eph_zpages_atomic);
+ zcache_evicted_eph_zpages++;
+ zcache_eph_pageframes =
+ atomic_dec_return(&zcache_eph_pageframes_atomic);
+ zcache_evicted_eph_pageframes++;
+out:
+ return page;
}
-static struct kobj_attribute ramster_manual_node_up_attr = {
- .attr = { .name = "manual_node_up", .mode = 0644 },
- .show = ramster_manual_node_up_show,
- .store = ramster_manual_node_up_store,
-};
+#ifdef FRONTSWAP_HAS_UNUSE
+static void unswiz(struct tmem_oid oid, u32 index,
+ unsigned *type, pgoff_t *offset);
-static ssize_t ramster_remote_target_nodenum_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *buf)
+/*
+ * Choose an LRU persistent pageframe and attempt to "unuse" it by
+ * calling frontswap_unuse on both zpages.
+ *
+ * This is work-in-progress.
+ */
+
+static int zcache_frontswap_unuse(void)
{
- if (ramster_remote_target_nodenum == -1UL)
- return sprintf(buf, "unset\n");
- else
- return sprintf(buf, "%d\n", ramster_remote_target_nodenum);
-}
-
-static ssize_t ramster_remote_target_nodenum_store(struct kobject *kobj,
- struct kobj_attribute *attr, const char *buf, size_t count)
-{
- int err;
- unsigned long node_num;
-
- err = kstrtoul(buf, 10, &node_num);
- if (err) {
- pr_err("ramster: bad strtoul?\n");
- return -EINVAL;
- } else if (node_num == -1UL) {
- pr_err("ramster: disabling all remotification, "
- "data may still reside on remote nodes however\n");
- return -EINVAL;
- } else if (node_num >= MANUAL_NODES) {
- pr_err("ramster: bad node_num=%lu?\n", node_num);
- return -EINVAL;
- } else if (!ramster_nodes_manual_up[node_num]) {
- pr_err("ramster: node %d not up, ignoring setting "
- "of remotification target\n", (int)node_num);
- } else if (r2net_remote_target_node_set((int)node_num) >= 0) {
- pr_info("ramster: node %d set as remotification target\n",
- (int)node_num);
- ramster_remote_target_nodenum = (int)node_num;
- } else {
- pr_err("ramster: bad num to node node_num=%d?\n",
- (int)node_num);
- return -EINVAL;
+ struct tmem_handle th[2];
+ int ret = -ENOMEM;
+ int nzbuds, unuse_ret;
+ unsigned type;
+ struct page *newpage1 = NULL, *newpage2 = NULL;
+ struct page *evictpage1 = NULL, *evictpage2 = NULL;
+ pgoff_t offset;
+
+ newpage1 = alloc_page(ZCACHE_GFP_MASK);
+ newpage2 = alloc_page(ZCACHE_GFP_MASK);
+ if (newpage1 == NULL)
+ evictpage1 = zcache_evict_eph_pageframe();
+ if (newpage2 == NULL)
+ evictpage2 = zcache_evict_eph_pageframe();
+ if (evictpage1 == NULL || evictpage2 == NULL)
+ goto free_and_out;
+ /* ok, we have two pages pre-allocated */
+ nzbuds = zbud_make_zombie_lru(&th[0], NULL, NULL, false);
+ if (nzbuds == 0) {
+ ret = -ENOENT;
+ goto free_and_out;
+ }
+ unswiz(th[0].oid, th[0].index, &type, &offset);
+ unuse_ret = frontswap_unuse(type, offset,
+ newpage1 != NULL ? newpage1 : evictpage1,
+ ZCACHE_GFP_MASK);
+ if (unuse_ret != 0)
+ goto free_and_out;
+ else if (evictpage1 != NULL)
+ zcache_unacct_page();
+ newpage1 = NULL;
+ evictpage1 = NULL;
+ if (nzbuds == 2) {
+ unswiz(th[1].oid, th[1].index, &type, &offset);
+ unuse_ret = frontswap_unuse(type, offset,
+ newpage2 != NULL ? newpage2 : evictpage2,
+ ZCACHE_GFP_MASK);
+ if (unuse_ret != 0) {
+ goto free_and_out;
+ } else if (evictpage2 != NULL) {
+ zcache_unacct_page();
+ }
}
- return count;
+ ret = 0;
+ goto out;
+
+free_and_out:
+ if (newpage1 != NULL)
+ __free_page(newpage1);
+ if (newpage2 != NULL)
+ __free_page(newpage2);
+ if (evictpage1 != NULL)
+ zcache_free_page(evictpage1);
+ if (evictpage2 != NULL)
+ zcache_free_page(evictpage2);
+out:
+ return ret;
}
+#endif
-static struct kobj_attribute ramster_remote_target_nodenum_attr = {
- .attr = { .name = "remote_target_nodenum", .mode = 0644 },
- .show = ramster_remote_target_nodenum_show,
- .store = ramster_remote_target_nodenum_store,
-};
-
-
-static struct attribute *ramster_attrs[] = {
- &ramster_interface_revision_attr.attr,
- &ramster_pers_remotify_enable_attr.attr,
- &ramster_eph_remotify_enable_attr.attr,
- &ramster_remote_pers_pages_attr.attr,
- &ramster_eph_pages_remoted_attr.attr,
- &ramster_eph_pages_remote_failed_attr.attr,
- &ramster_pers_pages_remoted_attr.attr,
- &ramster_pers_pages_remote_failed_attr.attr,
- &ramster_pers_pages_remote_nomem_attr.attr,
- &ramster_remote_pages_flushed_attr.attr,
- &ramster_remote_page_flushes_failed_attr.attr,
- &ramster_remote_objects_flushed_attr.attr,
- &ramster_remote_object_flushes_failed_attr.attr,
- &ramster_remote_eph_pages_succ_get_attr.attr,
- &ramster_remote_eph_pages_unsucc_get_attr.attr,
- &ramster_remote_pers_pages_succ_get_attr.attr,
- &ramster_remote_pers_pages_unsucc_get_attr.attr,
- &ramster_foreign_eph_pampd_count_attr.attr,
- &ramster_foreign_eph_pampd_count_max_attr.attr,
- &ramster_foreign_pers_pampd_count_attr.attr,
- &ramster_foreign_pers_pampd_count_max_attr.attr,
- &ramster_curr_flnode_count_attr.attr,
- &ramster_curr_flnode_count_max_attr.attr,
- &ramster_manual_node_up_attr.attr,
- &ramster_remote_target_nodenum_attr.attr,
- NULL,
-};
-
-static struct attribute_group ramster_attr_group = {
- .attrs = ramster_attrs,
- .name = "ramster",
-};
-
-#endif /* CONFIG_SYSFS */
/*
* When zcache is disabled ("frozen"), pools can be created and destroyed,
* but all puts (and thus all other operations that require memory allocation)
@@ -2589,23 +1050,74 @@ static struct attribute_group ramster_attr_group = {
static bool zcache_freeze;
/*
- * zcache shrinker interface (only useful for ephemeral pages, so zbud only)
+ * This zcache shrinker interface reduces the number of ephemeral pageframes
+ * used by zcache to approximately the same as the total number of LRU_FILE
+ * pageframes in use.
*/
static int shrink_zcache_memory(struct shrinker *shrink,
struct shrink_control *sc)
{
+ static bool in_progress;
int ret = -1;
int nr = sc->nr_to_scan;
- gfp_t gfp_mask = sc->gfp_mask;
+ int nr_evict = 0;
+ int nr_unuse = 0;
+ struct page *page;
+#ifdef FRONTSWAP_HAS_UNUSE
+ int unuse_ret;
+#endif
- if (nr >= 0) {
- if (!(gfp_mask & __GFP_FS))
- /* does this case really need to be skipped? */
- goto out;
- zbud_evict_pages(nr);
+ if (nr <= 0)
+ goto skip_evict;
+
+ /* don't allow more than one eviction thread at a time */
+ if (in_progress)
+ goto skip_evict;
+
+ in_progress = true;
+
+ /* we are going to ignore nr, and target a different value */
+ zcache_last_active_file_pageframes =
+ global_page_state(NR_LRU_BASE + LRU_ACTIVE_FILE);
+ zcache_last_inactive_file_pageframes =
+ global_page_state(NR_LRU_BASE + LRU_INACTIVE_FILE);
+ nr_evict = zcache_eph_pageframes - zcache_last_active_file_pageframes +
+ zcache_last_inactive_file_pageframes;
+ while (nr_evict-- > 0) {
+ page = zcache_evict_eph_pageframe();
+ if (page == NULL)
+ break;
+ zcache_free_page(page);
+ }
+
+ zcache_last_active_anon_pageframes =
+ global_page_state(NR_LRU_BASE + LRU_ACTIVE_ANON);
+ zcache_last_inactive_anon_pageframes =
+ global_page_state(NR_LRU_BASE + LRU_INACTIVE_ANON);
+ nr_unuse = zcache_pers_pageframes - zcache_last_active_anon_pageframes +
+ zcache_last_inactive_anon_pageframes;
+#ifdef FRONTSWAP_HAS_UNUSE
+ /* rate limit for testing */
+ if (nr_unuse > 32)
+ nr_unuse = 32;
+ while (nr_unuse-- > 0) {
+ unuse_ret = zcache_frontswap_unuse();
+ if (unuse_ret == -ENOMEM)
+ break;
}
- ret = (int)atomic_read(&zcache_zbud_curr_raw_pages);
-out:
+#endif
+ in_progress = false;
+
+skip_evict:
+ /* resample: has changed, but maybe not all the way yet */
+ zcache_last_active_file_pageframes =
+ global_page_state(NR_LRU_BASE + LRU_ACTIVE_FILE);
+ zcache_last_inactive_file_pageframes =
+ global_page_state(NR_LRU_BASE + LRU_INACTIVE_FILE);
+ ret = zcache_eph_pageframes - zcache_last_active_file_pageframes +
+ zcache_last_inactive_file_pageframes;
+ if (ret < 0)
+ ret = 0;
return ret;
}
@@ -2618,30 +1130,46 @@ static struct shrinker zcache_shrinker = {
* zcache shims between cleancache/frontswap ops and tmem
*/
-int zcache_put(int cli_id, int pool_id, struct tmem_oid *oidp,
- uint32_t index, char *data, size_t size,
- bool raw, int ephemeral)
+/* FIXME rename these core routines to zcache_tmemput etc? */
+int zcache_put_page(int cli_id, int pool_id, struct tmem_oid *oidp,
+ uint32_t index, void *page,
+ unsigned int size, bool raw, int ephemeral)
{
struct tmem_pool *pool;
+ struct tmem_handle th;
int ret = -1;
+ void *pampd = NULL;
BUG_ON(!irqs_disabled());
pool = zcache_get_pool_by_id(cli_id, pool_id);
if (unlikely(pool == NULL))
goto out;
- if (!zcache_freeze && zcache_do_preload(pool) == 0) {
- /* preload does preempt_disable on success */
- ret = tmem_put(pool, oidp, index, data, size, raw, ephemeral);
- if (ret < 0) {
- if (is_ephemeral(pool))
+ if (!zcache_freeze) {
+ ret = 0;
+ th.client_id = cli_id;
+ th.pool_id = pool_id;
+ th.oid = *oidp;
+ th.index = index;
+ pampd = zcache_pampd_create((char *)page, size, raw,
+ ephemeral, &th);
+ if (pampd == NULL) {
+ ret = -ENOMEM;
+ if (ephemeral)
zcache_failed_eph_puts++;
else
zcache_failed_pers_puts++;
+ } else {
+ if (ramster_enabled)
+ ramster_do_preload_flnode(pool);
+ ret = tmem_put(pool, oidp, index, 0, pampd);
+ if (ret < 0)
+ BUG();
}
zcache_put_pool(pool);
- preempt_enable_no_resched();
} else {
zcache_put_to_flush++;
+ if (ramster_enabled)
+ ramster_do_preload_flnode(pool);
if (atomic_read(&pool->obj_count) > 0)
/* the put fails whether the flush succeeds or not */
(void)tmem_flush_page(pool, oidp, index);
@@ -2651,9 +1179,9 @@ out:
return ret;
}
-int zcache_get(int cli_id, int pool_id, struct tmem_oid *oidp,
- uint32_t index, char *data, size_t *sizep,
- bool raw, int get_and_free)
+int zcache_get_page(int cli_id, int pool_id, struct tmem_oid *oidp,
+ uint32_t index, void *page,
+ size_t *sizep, bool raw, int get_and_free)
{
struct tmem_pool *pool;
int ret = -1;
@@ -2667,22 +1195,21 @@ int zcache_get(int cli_id, int pool_id, struct tmem_oid *oidp,
eph = is_ephemeral(pool);
if (likely(pool != NULL)) {
if (atomic_read(&pool->obj_count) > 0)
- ret = tmem_get(pool, oidp, index, data, sizep,
- raw, get_and_free);
+ ret = tmem_get(pool, oidp, index, (char *)(page),
+ sizep, raw, get_and_free);
zcache_put_pool(pool);
}
- WARN_ONCE((!eph && (ret != 0)), "zcache_get fails on persistent pool, "
- "bad things are very likely to happen soon\n");
+ WARN_ONCE((!is_ephemeral(pool) && (ret != 0)),
+ "zcache_get fails on persistent pool, "
+ "bad things are very likely to happen soon\n");
#ifdef RAMSTER_TESTING
if (ret != 0 && ret != -1 && !(ret == -EINVAL && is_ephemeral(pool)))
pr_err("TESTING zcache_get tmem_get returns ret=%d\n", ret);
#endif
- if (ret == -EAGAIN)
- BUG(); /* FIXME... don't need this anymore??? let's ensure */
return ret;
}
-int zcache_flush(int cli_id, int pool_id,
+int zcache_flush_page(int cli_id, int pool_id,
struct tmem_oid *oidp, uint32_t index)
{
struct tmem_pool *pool;
@@ -2692,7 +1219,8 @@ int zcache_flush(int cli_id, int pool_id,
local_irq_save(flags);
zcache_flush_total++;
pool = zcache_get_pool_by_id(cli_id, pool_id);
- ramster_do_preload_flnode_only(pool);
+ if (ramster_enabled)
+ ramster_do_preload_flnode(pool);
if (likely(pool != NULL)) {
if (atomic_read(&pool->obj_count) > 0)
ret = tmem_flush_page(pool, oidp, index);
@@ -2704,7 +1232,8 @@ int zcache_flush(int cli_id, int pool_id,
return ret;
}
-int zcache_flush_object(int cli_id, int pool_id, struct tmem_oid *oidp)
+int zcache_flush_object(int cli_id, int pool_id,
+ struct tmem_oid *oidp)
{
struct tmem_pool *pool;
int ret = -1;
@@ -2713,7 +1242,8 @@ int zcache_flush_object(int cli_id, int pool_id, struct tmem_oid *oidp)
local_irq_save(flags);
zcache_flobj_total++;
pool = zcache_get_pool_by_id(cli_id, pool_id);
- ramster_do_preload_flnode_only(pool);
+ if (ramster_enabled)
+ ramster_do_preload_flnode(pool);
if (likely(pool != NULL)) {
if (atomic_read(&pool->obj_count) > 0)
ret = tmem_flush_object(pool, oidp);
@@ -2725,7 +1255,7 @@ int zcache_flush_object(int cli_id, int pool_id, struct tmem_oid *oidp)
return ret;
}
-int zcache_client_destroy_pool(int cli_id, int pool_id)
+static int zcache_client_destroy_pool(int cli_id, int pool_id)
{
struct tmem_pool *pool = NULL;
struct zcache_client *cli = NULL;
@@ -2752,16 +1282,15 @@ int zcache_client_destroy_pool(int cli_id, int pool_id)
ret = tmem_destroy_pool(pool);
local_bh_enable();
kfree(pool);
- pr_info("ramster: destroyed pool id=%d cli_id=%d\n", pool_id, cli_id);
+ if (cli_id == LOCAL_CLIENT)
+ pr_info("%s: destroyed local pool id=%d\n", namestr, pool_id);
+ else
+ pr_info("%s: destroyed pool id=%d, client=%d\n",
+ namestr, pool_id, cli_id);
out:
return ret;
}
-static int zcache_destroy_pool(int pool_id)
-{
- return zcache_client_destroy_pool(LOCAL_CLIENT, pool_id);
-}
-
int zcache_new_pool(uint16_t cli_id, uint32_t flags)
{
int poolid = -1;
@@ -2777,7 +1306,7 @@ int zcache_new_pool(uint16_t cli_id, uint32_t flags)
atomic_inc(&cli->refcount);
pool = kmalloc(sizeof(struct tmem_pool), GFP_ATOMIC);
if (pool == NULL) {
- pr_info("ramster: pool creation failed: out of memory\n");
+ pr_info("%s: pool creation failed: out of memory\n", namestr);
goto out;
}
@@ -2785,7 +1314,7 @@ int zcache_new_pool(uint16_t cli_id, uint32_t flags)
if (cli->tmem_pools[poolid] == NULL)
break;
if (poolid >= MAX_POOLS_PER_CLIENT) {
- pr_info("ramster: pool creation failed: max exceeded\n");
+ pr_info("%s: pool creation failed: max exceeded\n", namestr);
kfree(pool);
poolid = -1;
goto out;
@@ -2796,11 +1325,11 @@ int zcache_new_pool(uint16_t cli_id, uint32_t flags)
tmem_new_pool(pool, flags);
cli->tmem_pools[poolid] = pool;
if (cli_id == LOCAL_CLIENT)
- pr_info("ramster: created %s tmem pool, id=%d, local client\n",
+ pr_info("%s: created %s local tmem pool, id=%d\n", namestr,
flags & TMEM_POOL_PERSIST ? "persistent" : "ephemeral",
poolid);
else
- pr_info("ramster: created %s tmem pool, id=%d, client=%d\n",
+ pr_info("%s: created %s tmem pool, id=%d, client=%d\n", namestr,
flags & TMEM_POOL_PERSIST ? "persistent" : "ephemeral",
poolid, cli_id);
out:
@@ -2814,30 +1343,37 @@ static int zcache_local_new_pool(uint32_t flags)
return zcache_new_pool(LOCAL_CLIENT, flags);
}
-int zcache_autocreate_pool(int cli_id, int pool_id, bool ephemeral)
+int zcache_autocreate_pool(unsigned int cli_id, unsigned int pool_id, bool eph)
{
struct tmem_pool *pool;
- struct zcache_client *cli = NULL;
- uint32_t flags = ephemeral ? 0 : TMEM_POOL_PERSIST;
+ struct zcache_client *cli;
+ uint32_t flags = eph ? 0 : TMEM_POOL_PERSIST;
int ret = -1;
+ BUG_ON(!ramster_enabled);
if (cli_id == LOCAL_CLIENT)
goto out;
if (pool_id >= MAX_POOLS_PER_CLIENT)
goto out;
- else if ((unsigned int)cli_id < MAX_CLIENTS)
- cli = &zcache_clients[cli_id];
- if ((ephemeral && !use_cleancache) || (!ephemeral && !use_frontswap))
- BUG(); /* FIXME, handle more gracefully later */
+ if (cli_id >= MAX_CLIENTS)
+ goto out;
+
+ cli = &zcache_clients[cli_id];
+ if ((eph && disable_cleancache) || (!eph && disable_frontswap)) {
+ pr_err("zcache_autocreate_pool: pool type disabled\n");
+ goto out;
+ }
if (!cli->allocated) {
- if (zcache_new_client(cli_id))
- BUG(); /* FIXME, handle more gracefully later */
+ if (zcache_new_client(cli_id)) {
+ pr_err("zcache_autocreate_pool: can't create client\n");
+ goto out;
+ }
cli = &zcache_clients[cli_id];
}
atomic_inc(&cli->refcount);
pool = cli->tmem_pools[pool_id];
if (pool != NULL) {
- if (pool->persistent && ephemeral) {
+ if (pool->persistent && eph) {
pr_err("zcache_autocreate_pool: type mismatch\n");
goto out;
}
@@ -2846,7 +1382,7 @@ int zcache_autocreate_pool(int cli_id, int pool_id, bool ephemeral)
}
pool = kmalloc(sizeof(struct tmem_pool), GFP_KERNEL);
if (pool == NULL) {
- pr_info("ramster: pool creation failed: out of memory\n");
+ pr_info("%s: pool creation failed: out of memory\n", namestr);
goto out;
}
atomic_set(&pool->refcount, 0);
@@ -2854,14 +1390,11 @@ int zcache_autocreate_pool(int cli_id, int pool_id, bool ephemeral)
pool->pool_id = pool_id;
tmem_new_pool(pool, flags);
cli->tmem_pools[pool_id] = pool;
- pr_info("ramster: AUTOcreated %s tmem poolid=%d, for remote client=%d\n",
- flags & TMEM_POOL_PERSIST ? "persistent" : "ephemeral",
+ pr_info("%s: AUTOcreated %s tmem poolid=%d, for remote client=%d\n",
+ namestr, flags & TMEM_POOL_PERSIST ? "persistent" : "ephemeral",
pool_id, cli_id);
ret = 0;
out:
- if (cli == NULL)
- BUG(); /* FIXME, handle more gracefully later */
- /* pr_err("zcache_autocreate_pool: failed\n"); */
if (cli != NULL)
atomic_dec(&cli->refcount);
return ret;
@@ -2875,7 +1408,6 @@ out:
* to translate in-kernel semantics to zcache semantics.
*/
-#ifdef CONFIG_CLEANCACHE
static void zcache_cleancache_put_page(int pool_id,
struct cleancache_filekey key,
pgoff_t index, struct page *page)
@@ -2883,18 +1415,13 @@ static void zcache_cleancache_put_page(int pool_id,
u32 ind = (u32) index;
struct tmem_oid oid = *(struct tmem_oid *)&key;
-#ifdef __PG_WAS_ACTIVE
- if (!PageWasActive(page)) {
- zcache_nonactive_puts++;
+ if (!disable_cleancache_ignore_nonactive && !PageWasActive(page)) {
+ zcache_eph_nonactive_puts_ignored++;
return;
}
-#endif
- if (likely(ind == index)) {
- char *kva = page_address(page);
-
- (void)zcache_put(LOCAL_CLIENT, pool_id, &oid, index,
- kva, PAGE_SIZE, 0, 1);
- }
+ if (likely(ind == index))
+ (void)zcache_put_page(LOCAL_CLIENT, pool_id, &oid, index,
+ page, PAGE_SIZE, false, 1);
}
static int zcache_cleancache_get_page(int pool_id,
@@ -2903,21 +1430,16 @@ static int zcache_cleancache_get_page(int pool_id,
{
u32 ind = (u32) index;
struct tmem_oid oid = *(struct tmem_oid *)&key;
+ size_t size;
int ret = -1;
- preempt_disable();
if (likely(ind == index)) {
- char *kva = page_address(page);
- size_t size = PAGE_SIZE;
-
- ret = zcache_get(LOCAL_CLIENT, pool_id, &oid, index,
- kva, &size, 0, 0);
-#ifdef __PG_WAS_ACTIVE
+ ret = zcache_get_page(LOCAL_CLIENT, pool_id, &oid, index,
+ page, &size, false, 0);
+ BUG_ON(ret >= 0 && size != PAGE_SIZE);
if (ret == 0)
SetPageWasActive(page);
-#endif
}
- preempt_enable();
return ret;
}
@@ -2929,7 +1451,7 @@ static void zcache_cleancache_flush_page(int pool_id,
struct tmem_oid oid = *(struct tmem_oid *)&key;
if (likely(ind == index))
- (void)zcache_flush(LOCAL_CLIENT, pool_id, &oid, ind);
+ (void)zcache_flush_page(LOCAL_CLIENT, pool_id, &oid, ind);
}
static void zcache_cleancache_flush_inode(int pool_id,
@@ -2943,7 +1465,7 @@ static void zcache_cleancache_flush_inode(int pool_id,
static void zcache_cleancache_flush_fs(int pool_id)
{
if (pool_id >= 0)
- (void)zcache_destroy_pool(pool_id);
+ (void)zcache_client_destroy_pool(LOCAL_CLIENT, pool_id);
}
static int zcache_cleancache_init_fs(size_t pagesize)
@@ -2980,15 +1502,15 @@ struct cleancache_ops zcache_cleancache_register_ops(void)
return old_ops;
}
-#endif
-#ifdef CONFIG_FRONTSWAP
/* a single tmem poolid is used for all frontswap "types" (swapfiles) */
-static int zcache_frontswap_poolid = -1;
+static int zcache_frontswap_poolid __read_mostly = -1;
/*
* Swizzling increases objects per swaptype, increasing tmem concurrency
* for heavy swaploads. Later, larger nr_cpus -> larger SWIZ_BITS
+ * Setting SWIZ_BITS to 27 basically reconstructs the swap entry from
+ * frontswap_get_page(), but has side-effects. Hence using 8.
*/
#define SWIZ_BITS 8
#define SWIZ_MASK ((1 << SWIZ_BITS) - 1)
@@ -3002,47 +1524,64 @@ static inline struct tmem_oid oswiz(unsigned type, u32 ind)
return oid;
}
-static int zcache_frontswap_store(unsigned type, pgoff_t offset,
- struct page *page)
+#ifdef FRONTSWAP_HAS_UNUSE
+static void unswiz(struct tmem_oid oid, u32 index,
+ unsigned *type, pgoff_t *offset)
+{
+ *type = (unsigned)(oid.oid[0] >> SWIZ_BITS);
+ *offset = (pgoff_t)((index << SWIZ_BITS) |
+ (oid.oid[0] & SWIZ_MASK));
+}
+#endif
+
+static int zcache_frontswap_put_page(unsigned type, pgoff_t offset,
+ struct page *page)
{
u64 ind64 = (u64)offset;
u32 ind = (u32)offset;
struct tmem_oid oid = oswiz(type, ind);
int ret = -1;
unsigned long flags;
- char *kva;
BUG_ON(!PageLocked(page));
+ if (!disable_frontswap_ignore_nonactive && !PageWasActive(page)) {
+ zcache_pers_nonactive_puts_ignored++;
+ ret = -ERANGE;
+ goto out;
+ }
if (likely(ind64 == ind)) {
local_irq_save(flags);
- kva = page_address(page);
- ret = zcache_put(LOCAL_CLIENT, zcache_frontswap_poolid,
- &oid, iswiz(ind), kva, PAGE_SIZE, 0, 0);
+ ret = zcache_put_page(LOCAL_CLIENT, zcache_frontswap_poolid,
+ &oid, iswiz(ind),
+ page, PAGE_SIZE, false, 0);
local_irq_restore(flags);
}
+out:
return ret;
}
/* returns 0 if the page was successfully gotten from frontswap, -1 if
* was not present (should never happen!) */
-static int zcache_frontswap_load(unsigned type, pgoff_t offset,
- struct page *page)
+static int zcache_frontswap_get_page(unsigned type, pgoff_t offset,
+ struct page *page)
{
u64 ind64 = (u64)offset;
u32 ind = (u32)offset;
struct tmem_oid oid = oswiz(type, ind);
- int ret = -1;
+ size_t size;
+ int ret = -1, get_and_free;
- preempt_disable(); /* FIXME, remove this? */
+ if (frontswap_has_exclusive_gets)
+ get_and_free = 1;
+ else
+ get_and_free = -1;
BUG_ON(!PageLocked(page));
if (likely(ind64 == ind)) {
- char *kva = page_address(page);
- size_t size = PAGE_SIZE;
-
- ret = zcache_get(LOCAL_CLIENT, zcache_frontswap_poolid,
- &oid, iswiz(ind), kva, &size, 0, -1);
+ ret = zcache_get_page(LOCAL_CLIENT, zcache_frontswap_poolid,
+ &oid, iswiz(ind),
+ page, &size, false, get_and_free);
+ BUG_ON(ret >= 0 && size != PAGE_SIZE);
}
- preempt_enable(); /* FIXME, remove this? */
return ret;
}
@@ -3054,7 +1593,7 @@ static void zcache_frontswap_flush_page(unsigned type, pgoff_t offset)
struct tmem_oid oid = oswiz(type, ind);
if (likely(ind64 == ind))
- (void)zcache_flush(LOCAL_CLIENT, zcache_frontswap_poolid,
+ (void)zcache_flush_page(LOCAL_CLIENT, zcache_frontswap_poolid,
&oid, iswiz(ind));
}
@@ -3076,12 +1615,12 @@ static void zcache_frontswap_init(unsigned ignored)
/* a single tmem poolid is used for all frontswap "types" (swapfiles) */
if (zcache_frontswap_poolid < 0)
zcache_frontswap_poolid =
- zcache_local_new_pool(TMEM_POOL_PERSIST);
+ zcache_local_new_pool(TMEM_POOL_PERSIST);
}
static struct frontswap_ops zcache_frontswap_ops = {
- .store = zcache_frontswap_store,
- .load = zcache_frontswap_load,
+ .store = zcache_frontswap_put_page,
+ .load = zcache_frontswap_get_page,
.invalidate_page = zcache_frontswap_flush_page,
.invalidate_area = zcache_frontswap_flush_area,
.init = zcache_frontswap_init
@@ -3094,179 +1633,135 @@ struct frontswap_ops zcache_frontswap_register_ops(void)
return old_ops;
}
-#endif
/*
- * frontswap selfshrinking
- */
-
-#ifdef CONFIG_FRONTSWAP
-/* In HZ, controls frequency of worker invocation. */
-static unsigned int selfshrink_interval __read_mostly = 5;
-
-static void selfshrink_process(struct work_struct *work);
-static DECLARE_DELAYED_WORK(selfshrink_worker, selfshrink_process);
-
-/* Enable/disable with sysfs. */
-static bool frontswap_selfshrinking __read_mostly;
-
-/* Enable/disable with kernel boot option. */
-static bool use_frontswap_selfshrink __initdata = true;
-
-/*
- * The default values for the following parameters were deemed reasonable
- * by experimentation, may be workload-dependent, and can all be
- * adjusted via sysfs.
- */
-
-/* Control rate for frontswap shrinking. Higher hysteresis is slower. */
-static unsigned int frontswap_hysteresis __read_mostly = 20;
-
-/*
- * Number of selfshrink worker invocations to wait before observing that
- * frontswap selfshrinking should commence. Note that selfshrinking does
- * not use a separate worker thread.
+ * zcache initialization
+ * NOTE FOR NOW zcache or ramster MUST BE PROVIDED AS A KERNEL BOOT PARAMETER
+ * OR NOTHING HAPPENS!
*/
-static unsigned int frontswap_inertia __read_mostly = 3;
-/* Countdown to next invocation of frontswap_shrink() */
-static unsigned long frontswap_inertia_counter;
-
-/*
- * Invoked by the selfshrink worker thread, uses current number of pages
- * in frontswap (frontswap_curr_pages()), previous status, and control
- * values (hysteresis and inertia) to determine if frontswap should be
- * shrunk and what the new frontswap size should be. Note that
- * frontswap_shrink is essentially a partial swapoff that immediately
- * transfers pages from the "swap device" (frontswap) back into kernel
- * RAM; despite the name, frontswap "shrinking" is very different from
- * the "shrinker" interface used by the kernel MM subsystem to reclaim
- * memory.
- */
-static void frontswap_selfshrink(void)
+static int __init enable_zcache(char *s)
{
- static unsigned long cur_frontswap_pages;
- static unsigned long last_frontswap_pages;
- static unsigned long tgt_frontswap_pages;
-
- last_frontswap_pages = cur_frontswap_pages;
- cur_frontswap_pages = frontswap_curr_pages();
- if (!cur_frontswap_pages ||
- (cur_frontswap_pages > last_frontswap_pages)) {
- frontswap_inertia_counter = frontswap_inertia;
- return;
- }
- if (frontswap_inertia_counter && --frontswap_inertia_counter)
- return;
- if (cur_frontswap_pages <= frontswap_hysteresis)
- tgt_frontswap_pages = 0;
- else
- tgt_frontswap_pages = cur_frontswap_pages -
- (cur_frontswap_pages / frontswap_hysteresis);
- frontswap_shrink(tgt_frontswap_pages);
+ zcache_enabled = 1;
+ return 1;
}
+__setup("zcache", enable_zcache);
-static int __init ramster_nofrontswap_selfshrink_setup(char *s)
+static int __init enable_ramster(char *s)
{
- use_frontswap_selfshrink = false;
+ zcache_enabled = 1;
+#ifdef CONFIG_RAMSTER
+ ramster_enabled = 1;
+#endif
return 1;
}
+__setup("ramster", enable_ramster);
-__setup("noselfshrink", ramster_nofrontswap_selfshrink_setup);
+/* allow independent dynamic disabling of cleancache and frontswap */
-static void selfshrink_process(struct work_struct *work)
+static int __init no_cleancache(char *s)
{
- if (frontswap_selfshrinking && frontswap_enabled) {
- frontswap_selfshrink();
- schedule_delayed_work(&selfshrink_worker,
- selfshrink_interval * HZ);
- }
+ disable_cleancache = 1;
+ return 1;
}
-static int ramster_enabled;
+__setup("nocleancache", no_cleancache);
-static int __init ramster_selfshrink_init(void)
+static int __init no_frontswap(char *s)
{
- frontswap_selfshrinking = ramster_enabled && use_frontswap_selfshrink;
- if (frontswap_selfshrinking)
- pr_info("ramster: Initializing frontswap "
- "selfshrinking driver.\n");
- else
- return -ENODEV;
-
- schedule_delayed_work(&selfshrink_worker, selfshrink_interval * HZ);
-
- return 0;
+ disable_frontswap = 1;
+ return 1;
}
-subsys_initcall(ramster_selfshrink_init);
-#endif
+__setup("nofrontswap", no_frontswap);
-/*
- * zcache initialization
- * NOTE FOR NOW ramster MUST BE PROVIDED AS A KERNEL BOOT PARAMETER OR
- * NOTHING HAPPENS!
- */
+static int __init no_frontswap_exclusive_gets(char *s)
+{
+ frontswap_has_exclusive_gets = false;
+ return 1;
+}
-static int ramster_enabled;
+__setup("nofrontswapexclusivegets", no_frontswap_exclusive_gets);
-static int __init enable_ramster(char *s)
+static int __init no_frontswap_ignore_nonactive(char *s)
{
- ramster_enabled = 1;
+ disable_frontswap_ignore_nonactive = 1;
return 1;
}
-__setup("ramster", enable_ramster);
-/* allow independent dynamic disabling of cleancache and frontswap */
+__setup("nofrontswapignorenonactive", no_frontswap_ignore_nonactive);
-static int use_cleancache = 1;
-
-static int __init no_cleancache(char *s)
+static int __init no_cleancache_ignore_nonactive(char *s)
{
- pr_info("INIT no_cleancache called\n");
- use_cleancache = 0;
+ disable_cleancache_ignore_nonactive = 1;
return 1;
}
-/*
- * FIXME: need to guarantee this gets checked before zcache_init is called
- * What is the correct way to achieve this?
- */
-early_param("nocleancache", no_cleancache);
-
-static int use_frontswap = 1;
+__setup("nocleancacheignorenonactive", no_cleancache_ignore_nonactive);
-static int __init no_frontswap(char *s)
+static int __init enable_zcache_compressor(char *s)
{
- pr_info("INIT no_frontswap called\n");
- use_frontswap = 0;
+ strncpy(zcache_comp_name, s, ZCACHE_COMP_NAME_SZ);
+ zcache_enabled = 1;
return 1;
}
+__setup("zcache=", enable_zcache_compressor);
-__setup("nofrontswap", no_frontswap);
-static int __init zcache_init(void)
+static int __init zcache_comp_init(void)
{
int ret = 0;
-#ifdef CONFIG_SYSFS
- ret = sysfs_create_group(mm_kobj, &zcache_attr_group);
- ret = sysfs_create_group(mm_kobj, &ramster_attr_group);
- if (ret) {
- pr_err("ramster: can't create sysfs\n");
+ /* check crypto algorithm */
+ if (*zcache_comp_name != '\0') {
+ ret = crypto_has_comp(zcache_comp_name, 0, 0);
+ if (!ret)
+ pr_info("zcache: %s not supported\n",
+ zcache_comp_name);
+ }
+ if (!ret)
+ strcpy(zcache_comp_name, "lzo");
+ ret = crypto_has_comp(zcache_comp_name, 0, 0);
+ if (!ret) {
+ ret = 1;
goto out;
}
-#endif /* CONFIG_SYSFS */
-#if defined(CONFIG_CLEANCACHE) || defined(CONFIG_FRONTSWAP)
+ pr_info("zcache: using %s compressor\n", zcache_comp_name);
+
+ /* alloc percpu transforms */
+ ret = 0;
+ zcache_comp_pcpu_tfms = alloc_percpu(struct crypto_comp *);
+ if (!zcache_comp_pcpu_tfms)
+ ret = 1;
+out:
+ return ret;
+}
+
+static int __init zcache_init(void)
+{
+ int ret = 0;
+
if (ramster_enabled) {
+ namestr = "ramster";
+ ramster_register_pamops(&zcache_pamops);
+ }
+#ifdef CONFIG_DEBUG_FS
+ zcache_debugfs_init();
+#endif
+ if (zcache_enabled) {
unsigned int cpu;
- (void)r2net_register_handlers();
tmem_register_hostops(&zcache_hostops);
tmem_register_pamops(&zcache_pamops);
ret = register_cpu_notifier(&zcache_cpu_notifier_block);
if (ret) {
- pr_err("ramster: can't register cpu notifier\n");
+ pr_err("%s: can't register cpu notifier\n", namestr);
+ goto out;
+ }
+ ret = zcache_comp_init();
+ if (ret) {
+ pr_err("%s: compressor initialization failed\n",
+ namestr);
goto out;
}
for_each_online_cpu(cpu) {
@@ -3279,42 +1774,47 @@ static int __init zcache_init(void)
sizeof(struct tmem_objnode), 0, 0, NULL);
zcache_obj_cache = kmem_cache_create("zcache_obj",
sizeof(struct tmem_obj), 0, 0, NULL);
- ramster_flnode_cache = kmem_cache_create("ramster_flnode",
- sizeof(struct flushlist_node), 0, 0, NULL);
-#endif
-#ifdef CONFIG_CLEANCACHE
- pr_info("INIT ramster_enabled=%d use_cleancache=%d\n",
- ramster_enabled, use_cleancache);
- if (ramster_enabled && use_cleancache) {
+ ret = zcache_new_client(LOCAL_CLIENT);
+ if (ret) {
+ pr_err("%s: can't create client\n", namestr);
+ goto out;
+ }
+ zbud_init();
+ if (zcache_enabled && !disable_cleancache) {
struct cleancache_ops old_ops;
- zbud_init();
register_shrinker(&zcache_shrinker);
old_ops = zcache_cleancache_register_ops();
- pr_info("ramster: cleancache enabled using kernel "
- "transcendent memory and compression buddies\n");
+ pr_info("%s: cleancache enabled using kernel transcendent "
+ "memory and compression buddies\n", namestr);
+#ifdef ZCACHE_DEBUG
+ pr_info("%s: cleancache: ignorenonactive = %d\n",
+ namestr, !disable_cleancache_ignore_nonactive);
+#endif
if (old_ops.init_fs != NULL)
- pr_warning("ramster: cleancache_ops overridden");
+ pr_warn("%s: cleancache_ops overridden\n", namestr);
}
-#endif
-#ifdef CONFIG_FRONTSWAP
- pr_info("INIT ramster_enabled=%d use_frontswap=%d\n",
- ramster_enabled, use_frontswap);
- if (ramster_enabled && use_frontswap) {
+ if (zcache_enabled && !disable_frontswap) {
struct frontswap_ops old_ops;
- zcache_new_client(LOCAL_CLIENT);
old_ops = zcache_frontswap_register_ops();
- pr_info("ramster: frontswap enabled using kernel "
- "transcendent memory and xvmalloc\n");
+ if (frontswap_has_exclusive_gets)
+ frontswap_tmem_exclusive_gets(true);
+ pr_info("%s: frontswap enabled using kernel transcendent "
+ "memory and compression buddies\n", namestr);
+#ifdef ZCACHE_DEBUG
+ pr_info("%s: frontswap: excl gets = %d active only = %d\n",
+ namestr, frontswap_has_exclusive_gets,
+ !disable_frontswap_ignore_nonactive);
+#endif
if (old_ops.init != NULL)
- pr_warning("ramster: frontswap_ops overridden");
+ pr_warn("%s: frontswap_ops overridden\n", namestr);
}
- if (ramster_enabled && (use_frontswap || use_cleancache))
- ramster_remotify_init();
-#endif
+ if (ramster_enabled)
+ ramster_init(!disable_cleancache, !disable_frontswap,
+ frontswap_has_exclusive_gets);
out:
return ret;
}
-module_init(zcache_init)
+late_initcall(zcache_init);
diff --git a/drivers/staging/ramster/zcache.h b/drivers/staging/ramster/zcache.h
index 250b121c22e5..81722b33b087 100644
--- a/drivers/staging/ramster/zcache.h
+++ b/drivers/staging/ramster/zcache.h
@@ -1,22 +1,53 @@
+
/*
* zcache.h
*
- * External zcache functions
- *
- * Copyright (c) 2009-2012, Dan Magenheimer, Oracle Corp.
+ * Copyright (c) 2012, Dan Magenheimer, Oracle Corp.
*/
#ifndef _ZCACHE_H_
#define _ZCACHE_H_
-extern int zcache_put(int, int, struct tmem_oid *, uint32_t,
- char *, size_t, bool, int);
-extern int zcache_autocreate_pool(int, int, bool);
-extern int zcache_get(int, int, struct tmem_oid *, uint32_t,
- char *, size_t *, bool, int);
-extern int zcache_flush(int, int, struct tmem_oid *, uint32_t);
+struct zcache_preload {
+ struct tmem_obj *obj;
+ struct tmem_objnode *objnodes[OBJNODE_TREE_MAX_PATH];
+};
+
+struct tmem_pool;
+
+#define MAX_POOLS_PER_CLIENT 16
+
+#define MAX_CLIENTS 16
+#define LOCAL_CLIENT ((uint16_t)-1)
+
+struct zcache_client {
+ struct tmem_pool *tmem_pools[MAX_POOLS_PER_CLIENT];
+ bool allocated;
+ atomic_t refcount;
+};
+
+extern struct tmem_pool *zcache_get_pool_by_id(uint16_t cli_id,
+ uint16_t poolid);
+extern void zcache_put_pool(struct tmem_pool *pool);
+
+extern int zcache_put_page(int, int, struct tmem_oid *,
+ uint32_t, void *,
+ unsigned int, bool, int);
+extern int zcache_get_page(int, int, struct tmem_oid *, uint32_t,
+ void *, size_t *, bool, int);
+extern int zcache_flush_page(int, int, struct tmem_oid *, uint32_t);
extern int zcache_flush_object(int, int, struct tmem_oid *);
-extern int zcache_localify(int, struct tmem_oid *, uint32_t,
- char *, size_t, void *);
+extern void zcache_decompress_to_page(char *, unsigned int, struct page *);
+
+#ifdef CONFIG_RAMSTER
+extern void *zcache_pampd_create(char *, unsigned int, bool, int,
+ struct tmem_handle *);
+int zcache_autocreate_pool(unsigned int cli_id, unsigned int pool_id, bool eph);
+#endif
+
+#define MAX_POOLS_PER_CLIENT 16
+
+#define MAX_CLIENTS 16
+#define LOCAL_CLIENT ((uint16_t)-1)
-#endif /* _ZCACHE_H */
+#endif /* _ZCACHE_H_ */
diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211.h b/drivers/staging/rtl8187se/ieee80211/ieee80211.h
index b94c48b29302..5f5a30223d56 100644
--- a/drivers/staging/rtl8187se/ieee80211/ieee80211.h
+++ b/drivers/staging/rtl8187se/ieee80211/ieee80211.h
@@ -1094,7 +1094,7 @@ struct ieee80211_device {
int (*reset_port)(struct net_device *dev);
- /* Softmac-generated frames (mamagement) are TXed via this
+ /* Softmac-generated frames (management) are TXed via this
* callback if the flag IEEE_SOFTMAC_SINGLE_QUEUE is
* not set. As some cards may have different HW queues that
* one might want to use for data and management frames
diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c
index 8173240dcf7a..00f9af06aca5 100644
--- a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c
+++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac.c
@@ -21,6 +21,7 @@
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
+#include <linux/etherdevice.h>
#include "dot11d.h"
u8 rsn_authen_cipher_suite[16][4] = {
@@ -2110,13 +2111,7 @@ void ieee80211_rtl_stop_queue(struct ieee80211_device *ieee)
inline void ieee80211_randomize_cell(struct ieee80211_device *ieee)
{
- get_random_bytes(ieee->current_network.bssid, ETH_ALEN);
-
- /* an IBSS cell address must have the two less significant
- * bits of the first byte = 2
- */
- ieee->current_network.bssid[0] &= ~0x01;
- ieee->current_network.bssid[0] |= 0x02;
+ random_ether_addr(ieee->current_network.bssid);
}
/* called in user context only */
@@ -2808,9 +2803,7 @@ static int ieee80211_wpa_set_encryption(struct ieee80211_device *ieee,
param->u.crypt.key_len);
return -EINVAL;
}
- if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
- param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
- param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (is_broadcast_ether_addr(param->sta_addr)) {
if (param->u.crypt.idx >= WEP_KEYS)
return -EINVAL;
crypt = &ieee->crypt[param->u.crypt.idx];
diff --git a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c
index 5d204906baf7..1ef8fd612732 100644
--- a/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c
+++ b/drivers/staging/rtl8187se/ieee80211/ieee80211_softmac_wx.c
@@ -14,6 +14,8 @@
*/
+#include <linux/etherdevice.h>
+
#include "ieee80211.h"
/* FIXME: add A freqs */
@@ -131,7 +133,6 @@ int ieee80211_wx_set_wap(struct ieee80211_device *ieee,
{
int ret = 0;
- u8 zero[] = {0,0,0,0,0,0};
unsigned long flags;
short ifup = ieee->proto_started;//dev->flags & IFF_UP;
@@ -161,7 +162,7 @@ int ieee80211_wx_set_wap(struct ieee80211_device *ieee,
spin_lock_irqsave(&ieee->lock, flags);
memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
- ieee->wap_set = memcmp(temp->sa_data, zero,ETH_ALEN)!=0;
+ ieee->wap_set = !is_zero_ether_addr(temp->sa_data);
//printk(" %x:%x:%x:%x:%x:%x\n", ieee->current_network.bssid[0],ieee->current_network.bssid[1],ieee->current_network.bssid[2],ieee->current_network.bssid[3],ieee->current_network.bssid[4],ieee->current_network.bssid[5]);
spin_unlock_irqrestore(&ieee->lock, flags);
diff --git a/drivers/staging/rtl8187se/r8180_core.c b/drivers/staging/rtl8187se/r8180_core.c
index fd22b75aea4f..20e5fb58f52f 100644
--- a/drivers/staging/rtl8187se/r8180_core.c
+++ b/drivers/staging/rtl8187se/r8180_core.c
@@ -2377,7 +2377,7 @@ void rtl8180_wmm_param_update(struct work_struct *work)
u8 u1bAIFS;
u32 u4bAcParam;
pAcParam = (PAC_PARAM)(&AcParam);
- /* Retrieve paramters to update. */
+ /* Retrieve parameters to update. */
u1bAIFS = pAcParam->f.AciAifsn.f.AIFSN * (((mode&IEEE_G) == IEEE_G) ? 9 : 20) + aSifsTime;
u4bAcParam = ((((u32)(pAcParam->f.TXOPLimit))<<AC_PARAM_TXOP_LIMIT_OFFSET)|
(((u32)(pAcParam->f.Ecw.f.ECWmax))<<AC_PARAM_ECW_MAX_OFFSET)|
@@ -2413,7 +2413,7 @@ void rtl8180_wmm_param_update(struct work_struct *work)
u8 u1bAIFS;
u32 u4bAcParam;
- /* Retrieve paramters to update. */
+ /* Retrieve parameters to update. */
eACI = pAcParam->f.AciAifsn.f.ACI;
/* Mode G/A: slotTimeTimer = 9; Mode B: 20 */
u1bAIFS = pAcParam->f.AciAifsn.f.AIFSN * (((mode&IEEE_G) == IEEE_G) ? 9 : 20) + aSifsTime;
diff --git a/drivers/staging/rtl8187se/r8180_hw.h b/drivers/staging/rtl8187se/r8180_hw.h
index 3fca144a56a4..533938123a97 100644
--- a/drivers/staging/rtl8187se/r8180_hw.h
+++ b/drivers/staging/rtl8187se/r8180_hw.h
@@ -554,11 +554,16 @@
/* by amy for power save */
/* by amy for antenna */
#define EEPROM_SW_REVD_OFFSET 0x3f
-/* BIT[8-9] is for SW Antenna Diversity. Only the value EEPROM_SW_AD_ENABLE means enable, other values are diable. */
+
+/* BIT[8-9] is for SW Antenna Diversity.
+ * Only the value EEPROM_SW_AD_ENABLE means enable, other values are disable.
+ */
#define EEPROM_SW_AD_MASK 0x0300
#define EEPROM_SW_AD_ENABLE 0x0100
-/* BIT[10-11] determine if Antenna 1 is the Default Antenna. Only the value EEPROM_DEF_ANT_1 means TRUE, other values are FALSE. */
+/* BIT[10-11] determine if Antenna 1 is the Default Antenna.
+ * Only the value EEPROM_DEF_ANT_1 means TRUE, other values are FALSE.
+ */
#define EEPROM_DEF_ANT_MASK 0x0C00
#define EEPROM_DEF_ANT_1 0x0400
/*by amy for antenna */
diff --git a/drivers/staging/rtl8187se/r8185b_init.c b/drivers/staging/rtl8187se/r8185b_init.c
index 914495783c06..bf343199bd21 100644
--- a/drivers/staging/rtl8187se/r8185b_init.c
+++ b/drivers/staging/rtl8187se/r8185b_init.c
@@ -1008,7 +1008,7 @@ void ActUpdateChannelAccessSetting(struct net_device *dev,
u8 u1bAIFS;
u32 u4bAcParam;
- /* Retrieve paramters to update. */
+ /* Retrieve parameters to update. */
eACI = pAcParam->f.AciAifsn.f.ACI;
u1bAIFS = pAcParam->f.AciAifsn.f.AIFSN * ChnlAccessSetting->SlotTimeTimer + aSifsTime;
u4bAcParam = ((((u32)(pAcParam->f.TXOPLimit)) << AC_PARAM_TXOP_LIMIT_OFFSET) |
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c
index b526fa428679..dd2a96bfcc0c 100644
--- a/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.c
@@ -265,10 +265,11 @@ bool init_firmware(struct net_device *dev)
case FW_SOURCE_IMG_FILE:
{
if (pfirmware->firmware_buf_size[init_step] == 0) {
- const char *fw_name[3] = { "RTL8192E/boot.img",
- "RTL8192E/main.img",
- "RTL8192E/data.img"
- };
+ const char *fw_name[3] = {
+ RTL8192E_BOOT_IMG_FW,
+ RTL8192E_MAIN_IMG_FW,
+ RTL8192E_DATA_IMG_FW
+ };
const struct firmware *fw_entry;
int rc;
rc = request_firmware(&fw_entry,
diff --git a/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h
index caa878833106..06d6abc8345c 100644
--- a/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h
+++ b/drivers/staging/rtl8192e/rtl8192e/r8192E_firmware.h
@@ -23,6 +23,10 @@
#define GET_COMMAND_PACKET_FRAG_THRESHOLD(v) (4*(v/4) - 8)
+#define RTL8192E_BOOT_IMG_FW "RTL8192E/boot.img"
+#define RTL8192E_MAIN_IMG_FW "RTL8192E/main.img"
+#define RTL8192E_DATA_IMG_FW "RTL8192E/data.img"
+
enum firmware_init_step {
FW_INIT_STEP0_BOOT = 0,
FW_INIT_STEP1_MAIN = 1,
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c
index 4f602b227b50..81134d312ee3 100644
--- a/drivers/staging/rtl8192e/rtl8192e/rtl_core.c
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_core.c
@@ -734,8 +734,7 @@ static void rtl8192_prepare_beacon(struct r8192_priv *priv)
ring = &priv->tx_ring[BEACON_QUEUE];
pskb = __skb_dequeue(&ring->queue);
- if (pskb)
- kfree_skb(pskb);
+ kfree_skb(pskb);
pnewskb = rtllib_get_beacon(priv->rtllib);
if (!pnewskb)
@@ -3125,6 +3124,9 @@ MODULE_DESCRIPTION("Linux driver for Realtek RTL819x WiFi cards");
MODULE_AUTHOR(DRV_COPYRIGHT " " DRV_AUTHOR);
MODULE_VERSION(DRV_VERSION);
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(RTL8192E_BOOT_IMG_FW);
+MODULE_FIRMWARE(RTL8192E_MAIN_IMG_FW);
+MODULE_FIRMWARE(RTL8192E_DATA_IMG_FW);
module_param(ifname, charp, S_IRUGO|S_IWUSR);
module_param(hwwep, int, S_IRUGO|S_IWUSR);
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c
index 481b1e4d4913..1853665764a0 100644
--- a/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_dm.c
@@ -202,7 +202,7 @@ static void dm_check_ac_dc_power(struct net_device *dev)
if (priv->ResetProgress == RESET_TYPE_SILENT) {
RT_TRACE((COMP_INIT | COMP_POWER | COMP_RF),
- "GPIOChangeRFWorkItemCallBack(): Silent Reseting!!!!!!!\n");
+ "GPIOChangeRFWorkItemCallBack(): Silent Reset!!!!!!!\n");
return;
}
diff --git a/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
index ddadcc3e4e7c..5abbee37cdca 100644
--- a/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
+++ b/drivers/staging/rtl8192e/rtl8192e/rtl_pci.c
@@ -31,12 +31,10 @@ static void rtl8192_parse_pci_configuration(struct pci_dev *pdev,
struct r8192_priv *priv = (struct r8192_priv *)rtllib_priv(dev);
u8 tmp;
- int pos;
- u8 LinkCtrlReg;
+ u16 LinkCtrlReg;
- pos = pci_find_capability(priv->pdev, PCI_CAP_ID_EXP);
- pci_read_config_byte(priv->pdev, pos + PCI_EXP_LNKCTL, &LinkCtrlReg);
- priv->NdisAdapter.LinkCtrlReg = LinkCtrlReg;
+ pcie_capability_read_word(priv->pdev, PCI_EXP_LNKCTL, &LinkCtrlReg);
+ priv->NdisAdapter.LinkCtrlReg = (u8)LinkCtrlReg;
RT_TRACE(COMP_INIT, "Link Control Register =%x\n",
priv->NdisAdapter.LinkCtrlReg);
diff --git a/drivers/staging/rtl8192e/rtllib.h b/drivers/staging/rtl8192e/rtllib.h
index d7460ae3a765..9ac8d8ea4ae8 100644
--- a/drivers/staging/rtl8192e/rtllib.h
+++ b/drivers/staging/rtl8192e/rtllib.h
@@ -2397,12 +2397,12 @@ struct rtllib_device {
struct rtllib_network *network, u16 type);
int (*is_qos_active)(struct net_device *dev, struct sk_buff *skb);
- /* Softmac-generated frames (mamagement) are TXed via this
+ /* Softmac-generated frames (management) are TXed via this
* callback if the flag IEEE_SOFTMAC_SINGLE_QUEUE is
* not set. As some cards may have different HW queues that
* one might want to use for data and management frames
* the option to have two callbacks might be useful.
- * This fucntion can't sleep.
+ * This function can't sleep.
*/
int (*softmac_hard_start_xmit)(struct sk_buff *skb,
struct net_device *dev);
@@ -2441,9 +2441,9 @@ struct rtllib_device {
* it is called in a work_queue when switching to ad-hoc mode
* or in behalf of iwlist scan when the card is associated
* and root user ask for a scan.
- * the fucntion stop_scan should stop both the syncro and
+ * the function stop_scan should stop both the syncro and
* background scanning and can sleep.
- * The fucntion start_scan should initiate the background
+ * The function start_scan should initiate the background
* scanning and can't sleep.
*/
void (*scan_syncro)(struct net_device *dev);
diff --git a/drivers/staging/rtl8192e/rtllib_softmac.c b/drivers/staging/rtl8192e/rtllib_softmac.c
index a21b4d91a596..4feecec8609c 100644
--- a/drivers/staging/rtl8192e/rtllib_softmac.c
+++ b/drivers/staging/rtl8192e/rtllib_softmac.c
@@ -19,6 +19,7 @@
#include <linux/random.h>
#include <linux/delay.h>
#include <linux/uaccess.h>
+#include <linux/etherdevice.h>
#include "dot11d.h"
short rtllib_is_54g(struct rtllib_network *net)
@@ -266,7 +267,7 @@ inline void softmac_mgmt_xmit(struct sk_buff *skb, struct rtllib_device *ieee)
else
ieee->seq_ctrl[0]++;
- /* check wether the managed packet queued greater than 5 */
+ /* check whether the managed packet queued greater than 5 */
if (!ieee->check_nic_enough_desc(ieee->dev, tcb_desc->queue_index) ||
(skb_queue_len(&ieee->skb_waitQ[tcb_desc->queue_index]) != 0) ||
(ieee->queue_stop)) {
@@ -1687,7 +1688,7 @@ inline void rtllib_softmac_new_net(struct rtllib_device *ieee,
* if the network does broadcast and the user did set essid
* check if essid match
* if the ap is not set, check that the user set the bssid
- * and the network does bradcast and that those two bssid match
+ * and the network does broadcast and that those two bssid match
*/
if ((apset && apmatch &&
((ssidset && ssidbroad && ssidmatch) ||
@@ -1843,7 +1844,7 @@ static short probe_rq_parse(struct rtllib_device *ieee, struct sk_buff *skb,
bssid_match =
(memcmp(header->addr3, ieee->current_network.bssid, ETH_ALEN) != 0) &&
- (memcmp(header->addr3, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0);
+ (!is_broadcast_ether_addr(header->addr3));
if (bssid_match)
return -1;
@@ -2442,7 +2443,7 @@ inline int rtllib_rx_frame_softmac(struct rtllib_device *ieee,
return 0;
}
-/* following are for a simplier TX queue management.
+/* following are for a simpler TX queue management.
* Instead of using netif_[stop/wake]_queue the driver
* will use these two functions (plus a reset one), that
* will internally use the kernel netif_* and takes
@@ -2619,13 +2620,7 @@ void rtllib_wake_all_queues(struct rtllib_device *ieee)
inline void rtllib_randomize_cell(struct rtllib_device *ieee)
{
- get_random_bytes(ieee->current_network.bssid, ETH_ALEN);
-
- /* an IBSS cell address must have the two less significant
- * bits of the first byte = 2
- */
- ieee->current_network.bssid[0] &= ~0x01;
- ieee->current_network.bssid[0] |= 0x02;
+ random_ether_addr(ieee->current_network.bssid);
}
/* called in user context only */
@@ -3361,9 +3356,7 @@ static int rtllib_wpa_set_encryption(struct rtllib_device *ieee,
param->u.crypt.key_len);
return -EINVAL;
}
- if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
- param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
- param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (is_broadcast_ether_addr(param->sta_addr)) {
if (param->u.crypt.idx >= NUM_WEP_KEYS)
return -EINVAL;
crypt = &ieee->crypt_info.crypt[param->u.crypt.idx];
@@ -3411,8 +3404,7 @@ static int rtllib_wpa_set_encryption(struct rtllib_device *ieee,
lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt);
- new_crypt = (struct lib80211_crypt_data *)
- kmalloc(sizeof(*new_crypt), GFP_KERNEL);
+ new_crypt = kmalloc(sizeof(*new_crypt), GFP_KERNEL);
if (new_crypt == NULL) {
ret = -ENOMEM;
goto done;
diff --git a/drivers/staging/rtl8192e/rtllib_softmac_wx.c b/drivers/staging/rtl8192e/rtllib_softmac_wx.c
index 1bb6b52e0f24..740cf85e9d5b 100644
--- a/drivers/staging/rtl8192e/rtllib_softmac_wx.c
+++ b/drivers/staging/rtl8192e/rtllib_softmac_wx.c
@@ -14,6 +14,8 @@
*/
+#include <linux/etherdevice.h>
+
#include "rtllib.h"
#include "dot11d.h"
/* FIXME: add A freqs */
@@ -137,7 +139,6 @@ int rtllib_wx_set_wap(struct rtllib_device *ieee,
{
int ret = 0;
- u8 zero[] = {0, 0, 0, 0, 0, 0};
unsigned long flags;
short ifup = ieee->proto_started;
@@ -157,7 +158,7 @@ int rtllib_wx_set_wap(struct rtllib_device *ieee,
goto out;
}
- if (memcmp(temp->sa_data, zero, ETH_ALEN) == 0) {
+ if (is_zero_ether_addr(temp->sa_data)) {
spin_lock_irqsave(&ieee->lock, flags);
memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
ieee->wap_set = 0;
@@ -177,7 +178,7 @@ int rtllib_wx_set_wap(struct rtllib_device *ieee,
ieee->cannot_notify = false;
memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
- ieee->wap_set = (memcmp(temp->sa_data, zero, ETH_ALEN) != 0);
+ ieee->wap_set = !is_zero_ether_addr(temp->sa_data);
spin_unlock_irqrestore(&ieee->lock, flags);
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211.h b/drivers/staging/rtl8192u/ieee80211/ieee80211.h
index 1c0a1db6420f..13f45c3125ce 100644
--- a/drivers/staging/rtl8192u/ieee80211/ieee80211.h
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211.h
@@ -2114,7 +2114,7 @@ struct ieee80211_device {
struct ieee80211_network * network, u16 type);
int (*is_qos_active) (struct net_device *dev, struct sk_buff *skb);
- /* Softmac-generated frames (mamagement) are TXed via this
+ /* Softmac-generated frames (management) are TXed via this
* callback if the flag IEEE_SOFTMAC_SINGLE_QUEUE is
* not set. As some cards may have different HW queues that
* one might want to use for data and management frames
@@ -2192,7 +2192,7 @@ struct ieee80211_device {
int (*handle_assoc_response) (struct net_device * dev, struct ieee80211_assoc_response_frame * resp, struct ieee80211_network * network);
- /* check whether Tx hw resouce available */
+ /* check whether Tx hw resource available */
short (*check_nic_enough_desc)(struct net_device *dev, int queue_index);
//added by wb for HT related
// void (*SwChnlByTimerHandler)(struct net_device *dev, int channel);
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c
index f6ff8cff313a..7a0707810fd0 100644
--- a/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac.c
@@ -20,6 +20,8 @@
#include <linux/delay.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
+#include <linux/etherdevice.h>
+
#include "dot11d.h"
u8 rsn_authen_cipher_suite[16][4] = {
@@ -269,7 +271,7 @@ inline void softmac_mgmt_xmit(struct sk_buff *skb, struct ieee80211_device *ieee
else
ieee->seq_ctrl[0]++;
- /* check wether the managed packet queued greater than 5 */
+ /* check whether the managed packet queued greater than 5 */
if(!ieee->check_nic_enough_desc(ieee->dev,tcb_desc->queue_index)||\
(skb_queue_len(&ieee->skb_waitQ[tcb_desc->queue_index]) != 0)||\
(ieee->queue_stop) ) {
@@ -1448,7 +1450,7 @@ inline void ieee80211_softmac_new_net(struct ieee80211_device *ieee, struct ieee
( apset && apmatch &&
((ssidset && ssidbroad && ssidmatch) || (ssidbroad && !ssidset) || (!ssidbroad && ssidset)) ) ||
/* if the ap is not set, check that the user set the bssid
- * and the network does bradcast and that those two bssid matches
+ * and the network does broadcast and that those two bssid matches
*/
(!apset && ssidset && ssidbroad && ssidmatch)
){
@@ -2286,13 +2288,7 @@ void ieee80211_stop_queue(struct ieee80211_device *ieee)
inline void ieee80211_randomize_cell(struct ieee80211_device *ieee)
{
- get_random_bytes(ieee->current_network.bssid, ETH_ALEN);
-
- /* an IBSS cell address must have the two less significant
- * bits of the first byte = 2
- */
- ieee->current_network.bssid[0] &= ~0x01;
- ieee->current_network.bssid[0] |= 0x02;
+ random_ether_addr(ieee->current_network.bssid);
}
/* called in user context only */
@@ -2520,7 +2516,7 @@ void ieee80211_associate_retry_wq(struct work_struct *work)
/* until we do not set the state to IEEE80211_NOLINK
* there are no possibility to have someone else trying
- * to start an association procdure (we get here with
+ * to start an association procedure (we get here with
* ieee->state = IEEE80211_ASSOCIATING).
* When we set the state to IEEE80211_NOLINK it is possible
* that the RX path run an attempt to associate, but
@@ -2969,9 +2965,7 @@ static int ieee80211_wpa_set_encryption(struct ieee80211_device *ieee,
param->u.crypt.key_len);
return -EINVAL;
}
- if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
- param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
- param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (is_broadcast_ether_addr(param->sta_addr)) {
if (param->u.crypt.idx >= WEP_KEYS)
return -EINVAL;
crypt = &ieee->crypt[param->u.crypt.idx];
diff --git a/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c b/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c
index cb5a3c32974e..421da8a07697 100644
--- a/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c
+++ b/drivers/staging/rtl8192u/ieee80211/ieee80211_softmac_wx.c
@@ -14,6 +14,8 @@
*/
+#include <linux/etherdevice.h>
+
#include "ieee80211.h"
#include "dot11d.h"
/* FIXME: add A freqs */
@@ -136,7 +138,6 @@ int ieee80211_wx_set_wap(struct ieee80211_device *ieee,
{
int ret = 0;
- u8 zero[] = {0,0,0,0,0,0};
unsigned long flags;
short ifup = ieee->proto_started;//dev->flags & IFF_UP;
@@ -165,7 +166,7 @@ int ieee80211_wx_set_wap(struct ieee80211_device *ieee,
spin_lock_irqsave(&ieee->lock, flags);
memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN);
- ieee->wap_set = memcmp(temp->sa_data, zero,ETH_ALEN)!=0;
+ ieee->wap_set = !is_zero_ether_addr(temp->sa_data);
spin_unlock_irqrestore(&ieee->lock, flags);
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c b/drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c
index 27d083a70eb2..1ebea3daea2d 100644
--- a/drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_BAProc.c
@@ -1,6 +1,6 @@
/********************************************************************************************************************************
* This file is created to process BA Action Frame. According to 802.11 spec, there are 3 BA action types at all. And as BA is
- * related to TS, this part need some struture defined in QOS side code. Also TX RX is going to be resturctured, so how to send
+ * related to TS, this part need some structure defined in QOS side code. Also TX RX is going to be resturctured, so how to send
* ADDBAREQ ADDBARSP and DELBA packet is still on consideration. Temporarily use MANAGE QUEUE instead of Normal Queue.
* WB 2008-05-27
* *****************************************************************************************************************************/
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h b/drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h
index 0b1a1fc09391..a60b39cdb472 100644
--- a/drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_HT.h
@@ -15,7 +15,7 @@
#define HT_OPMODE_MIXED 3
//
-// MIMO Power Save Setings
+// MIMO Power Save Settings
//
#define MIMO_PS_STATIC 0
#define MIMO_PS_DYNAMIC 1
@@ -242,7 +242,7 @@ typedef struct _RT_HIGH_THROUGHPUT{
u8 bEnableHT;
u8 bCurrentHTSupport;
- u8 bRegBW40MHz; // Tx 40MHz channel capablity
+ u8 bRegBW40MHz; // Tx 40MHz channel capability
u8 bCurBW40MHz; // Tx 40MHz channel capability
u8 bRegShortGI40MHz; // Tx Short GI for 40Mhz
diff --git a/drivers/staging/rtl8192u/ieee80211/rtl819x_HTProc.c b/drivers/staging/rtl8192u/ieee80211/rtl819x_HTProc.c
index e88a839b2a91..ebb523904edc 100644
--- a/drivers/staging/rtl8192u/ieee80211/rtl819x_HTProc.c
+++ b/drivers/staging/rtl8192u/ieee80211/rtl819x_HTProc.c
@@ -912,7 +912,7 @@ u8 HTFilterMCSRate( struct ieee80211_device* ieee, u8* pSupportMCS, u8* pOperate
u8 i=0;
- // filter out operational rate set not supported by AP, the lenth of it is 16
+ // filter out operational rate set not supported by AP, the length of it is 16
for(i=0;i<=15;i++){
pOperateMCS[i] = ieee->Regdot11HTOperationalRateSet[i]&pSupportMCS[i];
}
diff --git a/drivers/staging/rtl8192u/r8192U_core.c b/drivers/staging/rtl8192u/r8192U_core.c
index 5981d6658320..5a2fab9fa772 100644
--- a/drivers/staging/rtl8192u/r8192U_core.c
+++ b/drivers/staging/rtl8192u/r8192U_core.c
@@ -2232,24 +2232,15 @@ short rtl8192_usb_initendpoints(struct net_device *dev)
memset(priv->rx_urb, 0, sizeof(struct urb*) * MAX_RX_URB);
priv->pp_rxskb = kcalloc(MAX_RX_URB, sizeof(struct sk_buff *),
GFP_KERNEL);
- if (priv->pp_rxskb == NULL)
- goto destroy;
-
- goto _middle;
-
-
-destroy:
- kfree(priv->pp_rxskb);
- kfree(priv->rx_urb);
-
- priv->pp_rxskb = NULL;
- priv->rx_urb = NULL;
-
- DMESGE("Endpoint Alloc Failure");
- return -ENOMEM;
+ if (!priv->pp_rxskb) {
+ kfree(priv->rx_urb);
+ priv->pp_rxskb = NULL;
+ priv->rx_urb = NULL;
-_middle:
+ DMESGE("Endpoint Alloc Failure");
+ return -ENOMEM;
+ }
printk("End of initendpoints\n");
return 0;
@@ -2808,9 +2799,7 @@ static void rtl8192_init_priv_variable(struct net_device* dev)
(priv->EarlyRxThreshold == 7 ? RCR_ONLYERLPKT:0);
priv->AcmControl = 0;
- priv->pFirmware = kmalloc(sizeof(rt_firmware), GFP_KERNEL);
- if (priv->pFirmware)
- memset(priv->pFirmware, 0, sizeof(rt_firmware));
+ priv->pFirmware = kzalloc(sizeof(rt_firmware), GFP_KERNEL);
/* rx related queue */
skb_queue_head_init(&priv->rx_queue);
diff --git a/drivers/staging/rtl8192u/r819xU_HTType.h b/drivers/staging/rtl8192u/r819xU_HTType.h
index e07f8b17a0d6..6c1d05e1e820 100644
--- a/drivers/staging/rtl8192u/r819xU_HTType.h
+++ b/drivers/staging/rtl8192u/r819xU_HTType.h
@@ -16,7 +16,7 @@
#define HT_OPMODE_MIXED 3
//
-// MIMO Power Save Setings
+// MIMO Power Save Settings
//
#define MIMO_PS_STATIC 0
#define MIMO_PS_DYNAMIC 1
diff --git a/drivers/staging/rtl8192u/r819xU_phyreg.h b/drivers/staging/rtl8192u/r819xU_phyreg.h
index 50f24dce8b16..cca34c05f6a5 100644
--- a/drivers/staging/rtl8192u/r819xU_phyreg.h
+++ b/drivers/staging/rtl8192u/r819xU_phyreg.h
@@ -443,7 +443,7 @@
#define bCCKRxIG 0x7f00
#define bCCKLNAPolarity 0x800000
#define bCCKRx1stGain 0x7f0000
-#define bCCKRFExtend 0x20000000 //CCK Rx inital gain polarity
+#define bCCKRFExtend 0x20000000 //CCK Rx initial gain polarity
#define bCCKRxAGCSatLevel 0x1f000000
#define bCCKRxAGCSatCount 0xe0
#define bCCKRxRFSettle 0x1f //AGCsamp_dly
diff --git a/drivers/staging/rtl8712/drv_types.h b/drivers/staging/rtl8712/drv_types.h
index 62b55663c63a..a074fe810169 100644
--- a/drivers/staging/rtl8712/drv_types.h
+++ b/drivers/staging/rtl8712/drv_types.h
@@ -70,9 +70,7 @@ struct qos_priv {
#include "rtl871x_event.h"
#include "rtl871x_led.h"
-#define SPEC_DEV_ID_NONE BIT(0)
#define SPEC_DEV_ID_DISABLE_HT BIT(1)
-#define SPEC_DEV_ID_ENABLE_PS BIT(2)
struct specific_device_id {
u32 flags;
@@ -127,13 +125,6 @@ struct registry_priv {
u8 wifi_test;
};
-/* For registry parameters */
-#define RGTRY_OFT(field) ((addr_t)FIELD_OFFSET(struct registry_priv, field))
-#define RGTRY_SZ(field) sizeof(((struct registry_priv *)0)->field)
-#define BSSID_OFT(field) ((addr_t)FIELD_OFFSET(struct ndis_wlan_bssid_ex, \
- field))
-#define BSSID_SZ(field) sizeof(((struct ndis_wlan_bssid_ex *)0)->field)
-
struct dvobj_priv {
struct _adapter *padapter;
u32 nr_endpoint;
diff --git a/drivers/staging/rtl8712/ethernet.h b/drivers/staging/rtl8712/ethernet.h
index 882d61b2e959..90954203776d 100644
--- a/drivers/staging/rtl8712/ethernet.h
+++ b/drivers/staging/rtl8712/ethernet.h
@@ -35,14 +35,6 @@
/*!< Is Multicast Address? */
#define RT_ETH_IS_MULTICAST(_pAddr) ((((u8 *)(_pAddr))[0]&0x01) != 0)
-/*!< Is Broadcast Address? */
-#define RT_ETH_IS_BROADCAST(_pAddr) ( \
- ((u8 *)(_pAddr))[0] == 0xff && \
- ((u8 *)(_pAddr))[1] == 0xff && \
- ((u8 *)(_pAddr))[2] == 0xff && \
- ((u8 *)(_pAddr))[3] == 0xff && \
- ((u8 *)(_pAddr))[4] == 0xff && \
- ((u8 *)(_pAddr))[5] == 0xff)
#endif /* #ifndef __INC_ETHERNET_H */
diff --git a/drivers/staging/rtl8712/os_intfs.c b/drivers/staging/rtl8712/os_intfs.c
index 448f00dd68fe..e00f7918d261 100644
--- a/drivers/staging/rtl8712/os_intfs.c
+++ b/drivers/staging/rtl8712/os_intfs.c
@@ -96,7 +96,7 @@ static char *initmac;
/* if wifi_test = 1, driver will disable the turbo mode and pass it to
* firmware private.
*/
-static int wifi_test = 0;
+static int wifi_test;
module_param_string(ifname, ifname, sizeof(ifname), S_IRUGO|S_IWUSR);
module_param(wifi_test, int, 0644);
diff --git a/drivers/staging/rtl8712/rtl8712_recv.c b/drivers/staging/rtl8712/rtl8712_recv.c
index 8e82ce2fee38..c76732cdb183 100644
--- a/drivers/staging/rtl8712/rtl8712_recv.c
+++ b/drivers/staging/rtl8712/rtl8712_recv.c
@@ -374,6 +374,8 @@ static int amsdu_to_msdu(struct _adapter *padapter, union recv_frame *prframe)
a_len -= ETH_HLEN;
/* Allocate new skb for releasing to upper layer */
sub_skb = dev_alloc_skb(nSubframe_Length + 12);
+ if (!sub_skb)
+ break;
skb_reserve(sub_skb, 12);
data_ptr = (u8 *)skb_put(sub_skb, nSubframe_Length);
memcpy(data_ptr, pdata, nSubframe_Length);
@@ -1094,6 +1096,8 @@ static int recvbuf2recvframe(struct _adapter *padapter, struct sk_buff *pskb)
precvframe->u.hdr.rx_end = pkt_copy->data + alloc_sz;
} else {
precvframe->u.hdr.pkt = skb_clone(pskb, GFP_ATOMIC);
+ if (!precvframe->u.hdr.pkt)
+ return _FAIL;
precvframe->u.hdr.rx_head = pbuf;
precvframe->u.hdr.rx_data = pbuf;
precvframe->u.hdr.rx_tail = pbuf;
@@ -1127,6 +1131,9 @@ static void recv_tasklet(void *priv)
recvbuf2recvframe(padapter, pskb);
skb_reset_tail_pointer(pskb);
pskb->len = 0;
- skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb);
+ if (!skb_cloned(pskb))
+ skb_queue_tail(&precvpriv->free_recv_skb_queue, pskb);
+ else
+ consume_skb(pskb);
}
}
diff --git a/drivers/staging/rtl8712/rtl8712_recv.h b/drivers/staging/rtl8712/rtl8712_recv.h
index 8efbd1fa035f..fd9e3fc4c226 100644
--- a/drivers/staging/rtl8712/rtl8712_recv.h
+++ b/drivers/staging/rtl8712/rtl8712_recv.h
@@ -41,7 +41,7 @@
#define RECV_BLK_SZ 512
#define RECV_BLK_CNT 16
#define RECV_BLK_TH RECV_BLK_CNT
-#define MAX_RECVBUF_SZ (30720) /* 30K */
+#define MAX_RECVBUF_SZ 9100
#define RECVBUFF_ALIGN_SZ 512
#define RSVD_ROOM_SZ (0)
/*These definition is used for Rx packet reordering.*/
diff --git a/drivers/staging/rtl8712/rtl8712_xmit.c b/drivers/staging/rtl8712/rtl8712_xmit.c
index 3d23514c0222..4e3f09420c1e 100644
--- a/drivers/staging/rtl8712/rtl8712_xmit.c
+++ b/drivers/staging/rtl8712/rtl8712_xmit.c
@@ -376,7 +376,7 @@ u8 r8712_dump_aggr_xframe(struct xmit_buf *pxmitbuf,
{
struct _adapter *padapter = pxmitframe->padapter;
struct dvobj_priv *pdvobj = (struct dvobj_priv *) &padapter->dvobjpriv;
- struct tx_desc * ptxdesc = (struct tx_desc *)pxmitbuf->pbuf;
+ struct tx_desc *ptxdesc = (struct tx_desc *)pxmitbuf->pbuf;
struct cmd_hdr *pcmd_hdr = (struct cmd_hdr *)
(pxmitbuf->pbuf + TXDESC_SIZE);
u16 total_length = (u16) (ptxdesc->txdw0 & 0xffff);
diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c
index 35e781fca4a0..c9a6a7fbb89c 100644
--- a/drivers/staging/rtl8712/rtl871x_ioctl_linux.c
+++ b/drivers/staging/rtl8712/rtl871x_ioctl_linux.c
@@ -407,9 +407,7 @@ static int wpa_set_encryption(struct net_device *dev, struct ieee_param *param,
if (param_len != (u32)((u8 *) param->u.crypt.key - (u8 *)param) +
param->u.crypt.key_len)
return -EINVAL;
- if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
- param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
- param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (is_broadcast_ether_addr(param->sta_addr)) {
if (param->u.crypt.idx >= WEP_KEYS) {
/* for large key indices, set the default (0) */
param->u.crypt.idx = 0;
diff --git a/drivers/staging/rtl8712/rtl871x_ioctl_set.c b/drivers/staging/rtl8712/rtl871x_ioctl_set.c
index f352b32355a0..d3ab24e34e3d 100644
--- a/drivers/staging/rtl8712/rtl871x_ioctl_set.c
+++ b/drivers/staging/rtl8712/rtl871x_ioctl_set.c
@@ -131,10 +131,7 @@ u8 r8712_set_802_11_bssid(struct _adapter *padapter, u8 *bssid)
u8 status = true;
struct mlme_priv *pmlmepriv = &padapter->mlmepriv;
- if ((bssid[0] == 0x00 && bssid[1] == 0x00 && bssid[2] == 0x00 &&
- bssid[3] == 0x00 && bssid[4] == 0x00 && bssid[5] == 0x00) ||
- (bssid[0] == 0xFF && bssid[1] == 0xFF && bssid[2] == 0xFF &&
- bssid[3] == 0xFF && bssid[4] == 0xFF && bssid[5] == 0xFF)) {
+ if (is_zero_ether_addr(bssid) || is_broadcast_ether_addr(bssid)) {
status = false;
return status;
}
diff --git a/drivers/staging/rtl8712/rtl871x_mlme.c b/drivers/staging/rtl8712/rtl871x_mlme.c
index dc7adc132d12..c51ad9ed4b52 100644
--- a/drivers/staging/rtl8712/rtl871x_mlme.c
+++ b/drivers/staging/rtl8712/rtl871x_mlme.c
@@ -28,6 +28,8 @@
#define _RTL871X_MLME_C_
+#include <linux/etherdevice.h>
+
#include "osdep_service.h"
#include "drv_types.h"
#include "recv_osdep.h"
@@ -146,9 +148,8 @@ static struct wlan_network *_r8712_find_network(struct __queue *scanned_queue,
unsigned long irqL;
struct list_head *phead, *plist;
struct wlan_network *pnetwork = NULL;
- u8 zero_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
- if (!memcmp(zero_addr, addr, ETH_ALEN))
+ if (is_zero_ether_addr(addr))
return NULL;
spin_lock_irqsave(&scanned_queue->lock, irqL);
phead = get_list_head(scanned_queue);
diff --git a/drivers/staging/rtl8712/rtl871x_pwrctrl.h b/drivers/staging/rtl8712/rtl871x_pwrctrl.h
index 6024c4f63d5b..70ff924fba04 100644
--- a/drivers/staging/rtl8712/rtl871x_pwrctrl.h
+++ b/drivers/staging/rtl8712/rtl871x_pwrctrl.h
@@ -30,26 +30,7 @@
#include "drv_types.h"
-#define FW_PWR0 0
-#define FW_PWR1 1
-#define FW_PWR2 2
-#define FW_PWR3 3
-
-
-#define HW_PWR0 7
-#define HW_PWR1 6
-#define HW_PWR2 2
-#define HW_PWR3 0
-#define HW_PWR4 8
-
-#define FW_PWRMSK 0x7
-
-
-#define XMIT_ALIVE BIT(0)
-#define RECV_ALIVE BIT(1)
#define CMD_ALIVE BIT(2)
-#define EVT_ALIVE BIT(3)
-
enum Power_Mgnt {
PS_MODE_ACTIVE = 0 ,
@@ -66,7 +47,6 @@ enum Power_Mgnt {
PS_MODE_NUM
};
-
/*
BIT[2:0] = HW state
BIT[3] = Protocol PS state, 0: register active state,
diff --git a/drivers/staging/rtl8712/rtl871x_recv.c b/drivers/staging/rtl8712/rtl871x_recv.c
index c9d1743e5c5d..23ec684b60e1 100644
--- a/drivers/staging/rtl8712/rtl871x_recv.c
+++ b/drivers/staging/rtl8712/rtl871x_recv.c
@@ -32,6 +32,7 @@
#include <linux/slab.h>
#include <linux/if_ether.h>
#include <linux/kmemleak.h>
+#include <linux/etherdevice.h>
#include "osdep_service.h"
#include "drv_types.h"
@@ -331,8 +332,8 @@ static sint sta2sta_data_frame(struct _adapter *adapter,
return _FAIL;
if ((memcmp(myhwaddr, pattrib->dst, ETH_ALEN)) && (!bmcast))
return _FAIL;
- if (!memcmp(pattrib->bssid, "\x0\x0\x0\x0\x0\x0", ETH_ALEN) ||
- !memcmp(mybssid, "\x0\x0\x0\x0\x0\x0", ETH_ALEN) ||
+ if (is_zero_ether_addr(pattrib->bssid) ||
+ is_zero_ether_addr(mybssid) ||
(memcmp(pattrib->bssid, mybssid, ETH_ALEN)))
return _FAIL;
sta_addr = pattrib->src;
@@ -409,8 +410,8 @@ static sint ap2sta_data_frame(struct _adapter *adapter,
if ((memcmp(myhwaddr, pattrib->dst, ETH_ALEN)) && (!bmcast))
return _FAIL;
/* check BSSID */
- if (!memcmp(pattrib->bssid, "\x0\x0\x0\x0\x0\x0", ETH_ALEN) ||
- !memcmp(mybssid, "\x0\x0\x0\x0\x0\x0", ETH_ALEN) ||
+ if (is_zero_ether_addr(pattrib->bssid) ||
+ is_zero_ether_addr(mybssid) ||
(memcmp(pattrib->bssid, mybssid, ETH_ALEN)))
return _FAIL;
if (bmcast)
diff --git a/drivers/staging/rtl8712/rtl871x_security.c b/drivers/staging/rtl8712/rtl871x_security.c
index 7b92927a04dc..e33fd6db246d 100644
--- a/drivers/staging/rtl8712/rtl871x_security.c
+++ b/drivers/staging/rtl8712/rtl871x_security.c
@@ -58,7 +58,7 @@ struct arc4context {
u8 state[256];
};
-static void arcfour_init(struct arc4context *parc4ctx, u8 * key, u32 key_len)
+static void arcfour_init(struct arc4context *parc4ctx, u8 *key, u32 key_len)
{
u32 t, u;
u32 keyindex;
@@ -288,7 +288,7 @@ static void secmicclear(struct mic_data *pmicdata)
pmicdata->M = 0;
}
-void r8712_secmicsetkey(struct mic_data *pmicdata, u8 * key)
+void r8712_secmicsetkey(struct mic_data *pmicdata, u8 *key)
{
/* Set the key */
pmicdata->K0 = secmicgetuint32(key);
@@ -320,7 +320,7 @@ static void secmicappendbyte(struct mic_data *pmicdata, u8 b)
}
}
-void r8712_secmicappend(struct mic_data *pmicdata, u8 * src, u32 nbytes)
+void r8712_secmicappend(struct mic_data *pmicdata, u8 *src, u32 nbytes)
{
/* This is simple */
while (nbytes > 0) {
@@ -1368,7 +1368,7 @@ u32 r8712_aes_decrypt(struct _adapter *padapter, u8 *precvframe)
precvframe)->u.hdr.attrib;
struct security_priv *psecuritypriv = &padapter->securitypriv;
- pframe = (unsigned char *)((union recv_frame*)precvframe)->
+ pframe = (unsigned char *)((union recv_frame *)precvframe)->
u.hdr.rx_data;
/* 4 start to encrypt each fragment */
if ((prxattrib->encrypt == _AES_)) {
diff --git a/drivers/staging/rtl8712/usb_intf.c b/drivers/staging/rtl8712/usb_intf.c
index c758c40e0c85..6b73843e580a 100644
--- a/drivers/staging/rtl8712/usb_intf.c
+++ b/drivers/staging/rtl8712/usb_intf.c
@@ -37,7 +37,6 @@
#include "recv_osdep.h"
#include "xmit_osdep.h"
#include "rtl8712_efuse.h"
-#include "usb_vendor_req.h"
#include "usb_ops.h"
#include "usb_osintf.h"
diff --git a/drivers/staging/rtl8712/usb_osintf.h b/drivers/staging/rtl8712/usb_osintf.h
index d95797aac37a..609f9210cc46 100644
--- a/drivers/staging/rtl8712/usb_osintf.h
+++ b/drivers/staging/rtl8712/usb_osintf.h
@@ -28,9 +28,6 @@
#include "osdep_service.h"
#include "drv_types.h"
-#include "usb_vendor_req.h"
-
-#define USBD_HALTED(Status) ((u32)(Status) >> 30 == 3)
extern char *r8712_initmac;
diff --git a/drivers/staging/rtl8712/usb_vendor_req.h b/drivers/staging/rtl8712/usb_vendor_req.h
deleted file mode 100644
index 82335a83d0d2..000000000000
--- a/drivers/staging/rtl8712/usb_vendor_req.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/******************************************************************************
- *
- * Copyright(c) 2007 - 2010 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.
- *
- * 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
- *
- * Modifications for inclusion into the Linux staging tree are
- * Copyright(c) 2010 Larry Finger. All rights reserved.
- *
- * Contact information:
- * WLAN FAE <wlanfae@realtek.com>
- * Larry Finger <Larry.Finger@lwfinger.net>
- *
- ******************************************************************************/
-#ifndef _USB_VENDOR_REQUEST_H_
-#define _USB_VENDOR_REQUEST_H_
-
-/*4 Set/Get Register related wIndex/Data */
-#define RT_USB_RESET_MASK_OFF 0
-#define RT_USB_RESET_MASK_ON 1
-#define RT_USB_SLEEP_MASK_OFF 0
-#define RT_USB_SLEEP_MASK_ON 1
-#define RT_USB_LDO_ON 1
-#define RT_USB_LDO_OFF 0
-
-/*4 Set/Get SYSCLK related wValue or Data */
-#define RT_USB_SYSCLK_32KHZ 0
-#define RT_USB_SYSCLK_40MHZ 1
-#define RT_USB_SYSCLK_60MHZ 2
-
-enum RT_USB_BREQUEST {
- RT_USB_SET_REGISTER = 1,
- RT_USB_SET_SYSCLK = 2,
- RT_USB_GET_SYSCLK = 3,
- RT_USB_GET_REGISTER = 4
-};
-
-enum RT_USB_WVALUE {
- RT_USB_RESET_MASK = 1,
- RT_USB_SLEEP_MASK = 2,
- RT_USB_USB_HRCPWM = 3,
- RT_USB_LDO = 4,
- RT_USB_BOOT_TYPE = 5
-};
-
-#endif
-
diff --git a/drivers/staging/rts5139/rts51x_fop.c b/drivers/staging/rts5139/rts51x_fop.c
index e1200fe89579..bf1a9e64e874 100644
--- a/drivers/staging/rts5139/rts51x_fop.c
+++ b/drivers/staging/rts5139/rts51x_fop.c
@@ -79,7 +79,7 @@ static int rts51x_sd_direct_cmnd(struct rts51x_chip *chip,
case 1:
/* Read from card */
- buf = kmalloc(cmnd->buf_len, GFP_KERNEL);
+ buf = kzalloc(cmnd->buf_len, GFP_KERNEL);
if (!buf)
TRACE_RET(chip, STATUS_NOMEM);
diff --git a/drivers/staging/rts5139/trace.h b/drivers/staging/rts5139/trace.h
index 0584b8ab43ca..c9dfb1ea4115 100644
--- a/drivers/staging/rts5139/trace.h
+++ b/drivers/staging/rts5139/trace.h
@@ -93,35 +93,9 @@ do { \
#endif
#ifdef CONFIG_RTS5139_DEBUG
-static inline void rts51x_dump(u8 *buf, int buf_len)
-{
- int i;
- u8 tmp[16] = { 0 };
- u8 *_ptr = buf;
-
- for (i = 0; i < ((buf_len) / 16); i++) {
- RTS51X_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- _ptr[0], _ptr[1], _ptr[2], _ptr[3], _ptr[4],
- _ptr[5], _ptr[6], _ptr[7], _ptr[8], _ptr[9],
- _ptr[10], _ptr[11], _ptr[12], _ptr[13], _ptr[14],
- _ptr[15]);
- _ptr += 16;
- }
- if ((buf_len) % 16) {
- memcpy(tmp, _ptr, (buf_len) % 16);
- _ptr = tmp;
- RTS51X_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- _ptr[0], _ptr[1], _ptr[2], _ptr[3], _ptr[4],
- _ptr[5], _ptr[6], _ptr[7], _ptr[8], _ptr[9],
- _ptr[10], _ptr[11], _ptr[12], _ptr[13], _ptr[14],
- _ptr[15]);
- }
-}
-
-#define RTS51X_DUMP(buf, buf_len) \
- rts51x_dump((u8 *)(buf), (buf_len))
+#define RTS51X_DUMP(buf, buf_len) \
+ print_hex_dump(KERN_DEBUG, RTS51X_TIP, DUMP_PREFIX_NONE, \
+ 16, 1, (buf), (buf_len), false)
#define CATCH_TRIGGER(chip) \
do { \
diff --git a/drivers/staging/rts_pstor/ms.c b/drivers/staging/rts_pstor/ms.c
index 7cc2b53f20d0..16a5c16fb6ab 100644
--- a/drivers/staging/rts_pstor/ms.c
+++ b/drivers/staging/rts_pstor/ms.c
@@ -109,9 +109,8 @@ static int ms_transfer_data(struct rtsx_chip *chip, u8 trans_mode, u8 tpc, u16 s
u8 val, err_code = 0;
enum dma_data_direction dir;
- if (!buf || !buf_len) {
+ if (!buf || !buf_len)
TRACE_RET(chip, STATUS_FAIL);
- }
if (trans_mode == MS_TM_AUTO_READ) {
dir = DMA_FROM_DEVICE;
@@ -151,18 +150,17 @@ static int ms_transfer_data(struct rtsx_chip *chip, u8 trans_mode, u8 tpc, u16 s
use_sg, dir, chip->mspro_timeout);
if (retval < 0) {
ms_set_err_code(chip, err_code);
- if (retval == -ETIMEDOUT) {
+ if (retval == -ETIMEDOUT)
retval = STATUS_TIMEDOUT;
- } else {
+ else
retval = STATUS_FAIL;
- }
+
TRACE_RET(chip, retval);
}
RTSX_READ_REG(chip, MS_TRANS_CFG, &val);
- if (val & (MS_INT_CMDNK | MS_INT_ERR | MS_CRC16_ERR | MS_RDY_TIMEOUT)) {
+ if (val & (MS_INT_CMDNK | MS_INT_ERR | MS_CRC16_ERR | MS_RDY_TIMEOUT))
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -173,9 +171,8 @@ static int ms_write_bytes(struct rtsx_chip *chip,
struct ms_info *ms_card = &(chip->ms_card);
int retval, i;
- if (!data || (data_len < cnt)) {
+ if (!data || (data_len < cnt))
TRACE_RET(chip, STATUS_ERROR);
- }
rtsx_init_cmd(chip);
@@ -183,9 +180,8 @@ static int ms_write_bytes(struct rtsx_chip *chip,
rtsx_add_cmd(chip, WRITE_REG_CMD,
PPBUF_BASE2 + i, 0xFF, data[i]);
}
- if (cnt % 2) {
+ if (cnt % 2)
rtsx_add_cmd(chip, WRITE_REG_CMD, PPBUF_BASE2 + i, 0xFF, 0xFF);
- }
rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TPC, 0xFF, tpc);
rtsx_add_cmd(chip, WRITE_REG_CMD, MS_BYTE_CNT, 0xFF, cnt);
@@ -238,9 +234,8 @@ static int ms_read_bytes(struct rtsx_chip *chip, u8 tpc, u8 cnt, u8 cfg, u8 *dat
int retval, i;
u8 *ptr;
- if (!data) {
+ if (!data)
TRACE_RET(chip, STATUS_ERROR);
- }
rtsx_init_cmd(chip);
@@ -252,14 +247,13 @@ static int ms_read_bytes(struct rtsx_chip *chip, u8 tpc, u8 cnt, u8 cfg, u8 *dat
rtsx_add_cmd(chip, WRITE_REG_CMD, MS_TRANSFER, 0xFF, MS_TRANSFER_START | MS_TM_READ_BYTES);
rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANSFER, MS_TRANSFER_END, MS_TRANSFER_END);
- for (i = 0; i < data_len - 1; i++) {
+ for (i = 0; i < data_len - 1; i++)
rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + i, 0, 0);
- }
- if (data_len % 2) {
+
+ if (data_len % 2)
rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + data_len, 0, 0);
- } else {
+ else
rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + data_len - 1, 0, 0);
- }
retval = rtsx_send_cmd(chip, MS_CARD, 5000);
if (retval < 0) {
@@ -293,9 +287,8 @@ static int ms_read_bytes(struct rtsx_chip *chip, u8 tpc, u8 cnt, u8 cfg, u8 *dat
ptr = rtsx_get_cmd_data(chip) + 1;
- for (i = 0; i < data_len; i++) {
+ for (i = 0; i < data_len; i++)
data[i] = ptr[i];
- }
if ((tpc == PRO_READ_SHORT_DATA) && (data_len == 8)) {
RTSX_DEBUGP("Read format progress:\n");
@@ -343,34 +336,31 @@ static int ms_set_init_para(struct rtsx_chip *chip)
int retval;
if (CHK_HG8BIT(ms_card)) {
- if (chip->asic_code) {
+ if (chip->asic_code)
ms_card->ms_clock = chip->asic_ms_hg_clk;
- } else {
+ else
ms_card->ms_clock = chip->fpga_ms_hg_clk;
- }
+
} else if (CHK_MSPRO(ms_card) || CHK_MS4BIT(ms_card)) {
- if (chip->asic_code) {
+ if (chip->asic_code)
ms_card->ms_clock = chip->asic_ms_4bit_clk;
- } else {
+ else
ms_card->ms_clock = chip->fpga_ms_4bit_clk;
- }
+
} else {
- if (chip->asic_code) {
+ if (chip->asic_code)
ms_card->ms_clock = chip->asic_ms_1bit_clk;
- } else {
+ else
ms_card->ms_clock = chip->fpga_ms_1bit_clk;
- }
}
retval = switch_clock(chip, ms_card->ms_clock);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = select_card(chip, MS_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -381,14 +371,12 @@ static int ms_switch_clock(struct rtsx_chip *chip)
int retval;
retval = select_card(chip, MS_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = switch_clock(chip, ms_card->ms_clock);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -461,9 +449,8 @@ static int ms_pull_ctl_enable(struct rtsx_chip *chip)
}
retval = rtsx_send_cmd(chip, MS_CARD, 100);
- if (retval < 0) {
+ if (retval < 0)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -482,40 +469,37 @@ static int ms_prepare_reset(struct rtsx_chip *chip)
ms_card->pro_under_formatting = 0;
retval = ms_power_off_card3v3(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (!chip->ft2_fast_mode)
wait_timeout(250);
retval = enable_card_clock(chip, MS_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (chip->asic_code) {
retval = ms_pull_ctl_enable(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
RTSX_WRITE_REG(chip, FPGA_PULL_CTL, FPGA_MS_PULL_CTL_BIT | 0x20, 0);
}
if (!chip->ft2_fast_mode) {
retval = card_power_on(chip, MS_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
wait_timeout(150);
#ifdef SUPPORT_OCP
- if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
oc_mask = MS_OC_NOW | MS_OC_EVER;
- } else {
+ else
oc_mask = SD_OC_NOW | SD_OC_EVER;
- }
+
if (chip->ocp_stat & oc_mask) {
RTSX_DEBUGP("Over current, OCPSTAT is 0x%x\n",
chip->ocp_stat);
@@ -539,9 +523,8 @@ static int ms_prepare_reset(struct rtsx_chip *chip)
RTSX_WRITE_REG(chip, CARD_STOP, MS_STOP | MS_CLR_ERR, MS_STOP | MS_CLR_ERR);
retval = ms_set_init_para(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -553,26 +536,23 @@ static int ms_identify_media_type(struct rtsx_chip *chip, int switch_8bit_bus)
u8 val;
retval = ms_set_rw_reg_addr(chip, Pro_StatusReg, 6, SystemParm, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, READ_REG, 6, NO_WAIT_INT);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
break;
- }
}
- if (i == MS_MAX_RETRY_COUNT) {
+ if (i == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_READ_REG(chip, PPBUF_BASE2 + 2, &val);
RTSX_DEBUGP("Type register: 0x%x\n", val);
if (val != 0x01) {
- if (val != 0x02) {
+ if (val != 0x02)
ms_card->check_ms_flow = 1;
- }
+
TRACE_RET(chip, STATUS_FAIL);
}
@@ -587,11 +567,11 @@ static int ms_identify_media_type(struct rtsx_chip *chip, int switch_8bit_bus)
RTSX_DEBUGP("Class register: 0x%x\n", val);
if (val == 0) {
RTSX_READ_REG(chip, PPBUF_BASE2, &val);
- if (val & WRT_PRTCT) {
+ if (val & WRT_PRTCT)
chip->card_wp |= MS_CARD;
- } else {
+ else
chip->card_wp &= ~MS_CARD;
- }
+
} else if ((val == 0x01) || (val == 0x02) || (val == 0x03)) {
chip->card_wp |= MS_CARD;
} else {
@@ -606,11 +586,11 @@ static int ms_identify_media_type(struct rtsx_chip *chip, int switch_8bit_bus)
if (val == 0) {
ms_card->ms_type &= 0x0F;
} else if (val == 7) {
- if (switch_8bit_bus) {
+ if (switch_8bit_bus)
ms_card->ms_type |= MS_HG;
- } else {
+ else
ms_card->ms_type &= 0x0F;
- }
+
} else {
TRACE_RET(chip, STATUS_FAIL);
}
@@ -633,17 +613,15 @@ static int ms_confirm_cpu_startup(struct rtsx_chip *chip)
for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
break;
- }
}
- if (i == MS_MAX_RETRY_COUNT) {
+ if (i == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (k > 100) {
+ if (k > 100)
TRACE_RET(chip, STATUS_FAIL);
- }
+
k++;
wait_timeout(100);
} while (!(val & INT_REG_CED));
@@ -653,16 +631,14 @@ static int ms_confirm_cpu_startup(struct rtsx_chip *chip)
if (retval == STATUS_SUCCESS)
break;
}
- if (i == MS_MAX_RETRY_COUNT) {
+ if (i == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
if (val & INT_REG_ERR) {
- if (val & INT_REG_CMDNK) {
+ if (val & INT_REG_CMDNK)
chip->card_wp |= (MS_CARD);
- } else {
+ else
TRACE_RET(chip, STATUS_FAIL);
- }
}
/* -- end confirm CPU startup */
@@ -681,9 +657,8 @@ static int ms_switch_parallel_bus(struct rtsx_chip *chip)
if (retval == STATUS_SUCCESS)
break;
}
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -698,26 +673,22 @@ static int ms_switch_8bit_bus(struct rtsx_chip *chip)
data[1] = 0;
for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
retval = ms_write_bytes(chip, WRITE_REG, 1, NO_WAIT_INT, data, 2);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
break;
- }
}
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_WRITE_REG(chip, MS_CFG, 0x98, MS_BUS_WIDTH_8 | SAMPLE_TIME_FALLING);
ms_card->ms_type |= MS_8BIT;
retval = ms_set_init_para(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, GET_INT, 1, NO_WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -730,19 +701,16 @@ static int ms_pro_reset_flow(struct rtsx_chip *chip, int switch_8bit_bus)
for (i = 0; i < 3; i++) {
retval = ms_prepare_reset(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_identify_media_type(chip, switch_8bit_bus);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_confirm_cpu_startup(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_switch_parallel_bus(chip);
if (retval != STATUS_SUCCESS) {
@@ -756,18 +724,16 @@ static int ms_pro_reset_flow(struct rtsx_chip *chip, int switch_8bit_bus)
}
}
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
/* Switch MS-PRO into Parallel mode */
RTSX_WRITE_REG(chip, MS_CFG, 0x18, MS_BUS_WIDTH_4);
RTSX_WRITE_REG(chip, MS_CFG, PUSH_TIME_ODD, PUSH_TIME_ODD);
retval = ms_set_init_para(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
/* If MSPro HG Card, We shall try to switch to 8-bit bus */
if (CHK_MSHG(ms_card) && chip->support_ms_8bit && switch_8bit_bus) {
@@ -790,9 +756,8 @@ static int msxc_change_power(struct rtsx_chip *chip, u8 mode)
ms_cleanup_work(chip);
retval = ms_set_rw_reg_addr(chip, 0, 0, Pro_DataCount1, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
buf[0] = 0;
buf[1] = mode;
@@ -802,19 +767,16 @@ static int msxc_change_power(struct rtsx_chip *chip, u8 mode)
buf[5] = 0;
retval = ms_write_bytes(chip, PRO_WRITE_REG , 6, NO_WAIT_INT, buf, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_send_cmd(chip, XC_CHG_POWER, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_READ_REG(chip, MS_TRANS_CFG, buf);
- if (buf[0] & (MS_INT_CMDNK | MS_INT_ERR)) {
+ if (buf[0] & (MS_INT_CMDNK | MS_INT_ERR))
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -836,15 +798,14 @@ static int ms_read_attribute_info(struct rtsx_chip *chip)
#endif
retval = ms_set_rw_reg_addr(chip, Pro_IntReg, 2, Pro_SystemParm, 7);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (CHK_MS8BIT(ms_card)) {
+ if (CHK_MS8BIT(ms_card))
data[0] = PARALLEL_8BIT_IF;
- } else {
+ else
data[0] = PARALLEL_4BIT_IF;
- }
+
data[1] = 0;
data[2] = 0x40;
@@ -856,24 +817,21 @@ static int ms_read_attribute_info(struct rtsx_chip *chip)
for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
retval = ms_write_bytes(chip, PRO_WRITE_REG, 7, NO_WAIT_INT, data, 8);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
break;
- }
}
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
buf = kmalloc(64 * 512, GFP_KERNEL);
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, STATUS_ERROR);
- }
for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
retval = ms_send_cmd(chip, PRO_READ_ATRB, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
continue;
- }
+
retval = rtsx_read_register(chip, MS_TRANS_CFG, &val);
if (retval != STATUS_SUCCESS) {
kfree(buf);
@@ -885,11 +843,10 @@ static int ms_read_attribute_info(struct rtsx_chip *chip)
}
retval = ms_transfer_data(chip, MS_TM_AUTO_READ, PRO_READ_LONG_DATA,
0x40, WAIT_INT, 0, 0, buf, 64 * 512);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
break;
- } else {
+ else
rtsx_clear_ms_error(chip);
- }
}
if (retval != STATUS_SUCCESS) {
kfree(buf);
@@ -963,9 +920,8 @@ static int ms_read_attribute_info(struct rtsx_chip *chip)
}
#ifdef SUPPORT_MSXC
- if (buf[cur_addr_off + 8] == 0x13) {
+ if (buf[cur_addr_off + 8] == 0x13)
ms_card->ms_type |= MS_XC;
- }
#endif
#ifdef SUPPORT_PCGL_1P18
found_sys_info = 1;
@@ -1046,18 +1002,15 @@ static int ms_read_attribute_info(struct rtsx_chip *chip)
#ifdef SUPPORT_MSXC
if (CHK_MSXC(ms_card)) {
- if (class_code != 0x03) {
+ if (class_code != 0x03)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
- if (class_code != 0x02) {
+ if (class_code != 0x02)
TRACE_RET(chip, STATUS_FAIL);
- }
}
#else
- if (class_code != 0x02) {
+ if (class_code != 0x02)
TRACE_RET(chip, STATUS_FAIL);
- }
#endif
if (device_type != 0x00) {
@@ -1069,9 +1022,8 @@ static int ms_read_attribute_info(struct rtsx_chip *chip)
}
}
- if (sub_class & 0xC0) {
+ if (sub_class & 0xC0)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_DEBUGP("class_code: 0x%x, device_type: 0x%x, sub_class: 0x%x\n",
class_code, device_type, sub_class);
@@ -1117,23 +1069,20 @@ Retry:
if (retval != STATUS_SUCCESS) {
if (ms_card->switch_8bit_fail) {
retval = ms_pro_reset_flow(chip, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
TRACE_RET(chip, STATUS_FAIL);
}
}
retval = ms_read_attribute_info(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
#ifdef XC_POWERCLASS
- if (CHK_HG8BIT(ms_card)) {
+ if (CHK_HG8BIT(ms_card))
change_power_class = 0;
- }
if (change_power_class && CHK_MSXC(ms_card)) {
u8 power_class_en = chip->ms_power_class_en;
@@ -1141,11 +1090,10 @@ Retry:
RTSX_DEBUGP("power_class_en = 0x%x\n", power_class_en);
RTSX_DEBUGP("change_power_class = %d\n", change_power_class);
- if (change_power_class) {
+ if (change_power_class)
power_class_en &= (1 << (change_power_class - 1));
- } else {
+ else
power_class_en = 0;
- }
if (power_class_en) {
u8 power_class_mode = (ms_card->raw_sys_info[46] & 0x18) >> 3;
@@ -1165,16 +1113,14 @@ Retry:
#ifdef SUPPORT_MAGIC_GATE
retval = mg_set_tpc_para_sub(chip, 0, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
#endif
- if (CHK_HG8BIT(ms_card)) {
+ if (CHK_HG8BIT(ms_card))
chip->card_bus_width[chip->card2lun[MS_CARD]] = 8;
- } else {
+ else
chip->card_bus_width[chip->card2lun[MS_CARD]] = 4;
- }
return STATUS_SUCCESS;
}
@@ -1185,14 +1131,12 @@ static int ms_read_status_reg(struct rtsx_chip *chip)
u8 val[2];
retval = ms_set_rw_reg_addr(chip, StatusReg0, 2, 0, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_read_bytes(chip, READ_REG, 2, NO_WAIT_INT, val, 2);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (val[1] & (STS_UCDT | STS_UCEX | STS_UCFG)) {
ms_set_err_code(chip, MS_FLASH_READ_ERROR);
@@ -1211,9 +1155,8 @@ static int ms_read_extra_data(struct rtsx_chip *chip,
u8 val, data[10];
retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE, SystemParm, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (CHK_MS4BIT(ms_card)) {
/* Parallel interface */
@@ -1233,9 +1176,8 @@ static int ms_read_extra_data(struct rtsx_chip *chip,
if (retval == STATUS_SUCCESS)
break;
}
- if (i == MS_MAX_RETRY_COUNT) {
+ if (i == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
@@ -1244,15 +1186,14 @@ static int ms_read_extra_data(struct rtsx_chip *chip,
if (retval == STATUS_SUCCESS)
break;
}
- if (i == MS_MAX_RETRY_COUNT) {
+ if (i == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
if (val & INT_REG_CMDNK) {
ms_set_err_code(chip, MS_CMD_NK);
TRACE_RET(chip, STATUS_FAIL);
@@ -1260,20 +1201,18 @@ static int ms_read_extra_data(struct rtsx_chip *chip,
if (val & INT_REG_CED) {
if (val & INT_REG_ERR) {
retval = ms_read_status_reg(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE, SystemParm, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
}
retval = ms_read_bytes(chip, READ_REG, MS_EXTRA_SIZE, NO_WAIT_INT, data, MS_EXTRA_SIZE);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (buf && buf_len) {
if (buf_len > MS_EXTRA_SIZE)
@@ -1291,45 +1230,40 @@ static int ms_write_extra_data(struct rtsx_chip *chip,
int retval, i;
u8 val, data[16];
- if (!buf || (buf_len < MS_EXTRA_SIZE)) {
+ if (!buf || (buf_len < MS_EXTRA_SIZE))
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE, SystemParm, 6 + MS_EXTRA_SIZE);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
data[0] = 0x88;
- } else {
+ else
data[0] = 0x80;
- }
+
data[1] = 0;
data[2] = (u8)(block_addr >> 8);
data[3] = (u8)block_addr;
data[4] = 0x40;
data[5] = page_num;
- for (i = 6; i < MS_EXTRA_SIZE + 6; i++) {
+ for (i = 6; i < MS_EXTRA_SIZE + 6; i++)
data[i] = buf[i - 6];
- }
retval = ms_write_bytes(chip, WRITE_REG , (6+MS_EXTRA_SIZE), NO_WAIT_INT, data, 16);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
if (val & INT_REG_CMDNK) {
ms_set_err_code(chip, MS_CMD_NK);
TRACE_RET(chip, STATUS_FAIL);
@@ -1352,15 +1286,14 @@ static int ms_read_page(struct rtsx_chip *chip, u16 block_addr, u8 page_num)
u8 val, data[6];
retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE, SystemParm, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
data[0] = 0x88;
- } else {
+ else
data[0] = 0x80;
- }
+
data[1] = 0;
data[2] = (u8)(block_addr >> 8);
data[3] = (u8)block_addr;
@@ -1368,20 +1301,18 @@ static int ms_read_page(struct rtsx_chip *chip, u16 block_addr, u8 page_num)
data[5] = page_num;
retval = ms_write_bytes(chip, WRITE_REG , 6, NO_WAIT_INT, data, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
if (val & INT_REG_CMDNK) {
ms_set_err_code(chip, MS_CMD_NK);
TRACE_RET(chip, STATUS_FAIL);
@@ -1394,9 +1325,9 @@ static int ms_read_page(struct rtsx_chip *chip, u16 block_addr, u8 page_num)
TRACE_RET(chip, STATUS_FAIL);
}
retval = ms_read_status_reg(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
- }
+
} else {
if (!(val & INT_REG_BREQ)) {
ms_set_err_code(chip, MS_BREQ_ERROR);
@@ -1406,13 +1337,11 @@ static int ms_read_page(struct rtsx_chip *chip, u16 block_addr, u8 page_num)
}
retval = ms_transfer_tpc(chip, MS_TM_NORMAL_READ, READ_PAGE_DATA, 0, NO_WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (ms_check_err_code(chip, MS_FLASH_WRITE_ERROR)) {
+ if (ms_check_err_code(chip, MS_FLASH_WRITE_ERROR))
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -1425,22 +1354,20 @@ static int ms_set_bad_block(struct rtsx_chip *chip, u16 phy_blk)
u8 val, data[8], extra[MS_EXTRA_SIZE];
retval = ms_read_extra_data(chip, phy_blk, 0, extra, MS_EXTRA_SIZE);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE, SystemParm, 7);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
data[0] = 0x88;
- } else {
+ else
data[0] = 0x80;
- }
+
data[1] = 0;
data[2] = (u8)(phy_blk >> 8);
data[3] = (u8)phy_blk;
@@ -1450,20 +1377,17 @@ static int ms_set_bad_block(struct rtsx_chip *chip, u16 phy_blk)
data[7] = 0xFF;
retval = ms_write_bytes(chip, WRITE_REG , 7, NO_WAIT_INT, data, 7);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (val & INT_REG_CMDNK) {
ms_set_err_code(chip, MS_CMD_NK);
@@ -1488,17 +1412,16 @@ static int ms_erase_block(struct rtsx_chip *chip, u16 phy_blk)
u8 val, data[6];
retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE, SystemParm, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
data[0] = 0x88;
- } else {
+ else
data[0] = 0x80;
- }
+
data[1] = 0;
data[2] = (u8)(phy_blk >> 8);
data[3] = (u8)phy_blk;
@@ -1506,21 +1429,18 @@ static int ms_erase_block(struct rtsx_chip *chip, u16 phy_blk)
data[5] = 0;
retval = ms_write_bytes(chip, WRITE_REG, 6, NO_WAIT_INT, data, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ERASE_RTY:
retval = ms_send_cmd(chip, BLOCK_ERASE, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (val & INT_REG_CMDNK) {
if (i < 3) {
@@ -1546,9 +1466,8 @@ ERASE_RTY:
static void ms_set_page_status(u16 log_blk, u8 type, u8 *extra, int extra_len)
{
- if (!extra || (extra_len < MS_EXTRA_SIZE)) {
+ if (!extra || (extra_len < MS_EXTRA_SIZE))
return;
- }
memset(extra, 0xFF, MS_EXTRA_SIZE);
@@ -1583,9 +1502,8 @@ static int ms_init_page(struct rtsx_chip *chip, u16 phy_blk, u16 log_blk, u8 sta
}
retval = ms_write_extra_data(chip, phy_blk, i, extra, MS_EXTRA_SIZE);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -1603,27 +1521,23 @@ static int ms_copy_page(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
RTSX_DEBUGP("start_page = %d, end_page = %d\n", start_page, end_page);
retval = ms_read_extra_data(chip, new_blk, 0, extra, MS_EXTRA_SIZE);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_read_status_reg(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_READ_REG(chip, PPBUF_BASE2, &val);
if (val & BUF_FULL) {
retval = ms_send_cmd(chip, CLEAR_BUF, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (!(val & INT_REG_CED)) {
ms_set_err_code(chip, MS_FLASH_WRITE_ERROR);
@@ -1640,17 +1554,16 @@ static int ms_copy_page(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
ms_read_extra_data(chip, old_blk, i, extra, MS_EXTRA_SIZE);
retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE, SystemParm, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
data[0] = 0x88;
- } else {
+ else
data[0] = 0x80;
- }
+
data[1] = 0;
data[2] = (u8)(old_blk >> 8);
data[3] = (u8)old_blk;
@@ -1658,20 +1571,17 @@ static int ms_copy_page(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
data[5] = i;
retval = ms_write_bytes(chip, WRITE_REG , 6, NO_WAIT_INT, data, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (val & INT_REG_CMDNK) {
ms_set_err_code(chip, MS_CMD_NK);
@@ -1689,15 +1599,14 @@ static int ms_copy_page(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
}
retval = ms_transfer_tpc(chip, MS_TM_NORMAL_READ, READ_PAGE_DATA, 0, NO_WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (uncorrect_flag) {
ms_set_page_status(log_blk, setPS_NG, extra, MS_EXTRA_SIZE);
- if (i == 0) {
+ if (i == 0)
extra[0] &= 0xEF;
- }
+
ms_write_extra_data(chip, old_blk, i, extra, MS_EXTRA_SIZE);
RTSX_DEBUGP("page %d : extra[0] = 0x%x\n", i, extra[0]);
MS_SET_BAD_BLOCK_FLG(ms_card);
@@ -1710,13 +1619,11 @@ static int ms_copy_page(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
for (rty_cnt = 0; rty_cnt < MS_MAX_RETRY_COUNT; rty_cnt++) {
retval = ms_transfer_tpc(chip, MS_TM_NORMAL_WRITE,
WRITE_PAGE_DATA, 0, NO_WAIT_INT);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
break;
- }
}
- if (rty_cnt == MS_MAX_RETRY_COUNT) {
+ if (rty_cnt == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
}
if (!(val & INT_REG_BREQ)) {
@@ -1730,45 +1637,41 @@ static int ms_copy_page(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
ms_set_err_code(chip, MS_NO_ERROR);
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
data[0] = 0x88;
- } else {
+ else
data[0] = 0x80;
- }
+
data[1] = 0;
data[2] = (u8)(new_blk >> 8);
data[3] = (u8)new_blk;
data[4] = 0x20;
data[5] = i;
- if ((extra[0] & 0x60) != 0x60) {
+ if ((extra[0] & 0x60) != 0x60)
data[6] = extra[0];
- } else {
+ else
data[6] = 0xF8;
- }
+
data[6 + 1] = 0xFF;
data[6 + 2] = (u8)(log_blk >> 8);
data[6 + 3] = (u8)log_blk;
- for (j = 4; j <= MS_EXTRA_SIZE; j++) {
+ for (j = 4; j <= MS_EXTRA_SIZE; j++)
data[6 + j] = 0xFF;
- }
retval = ms_write_bytes(chip, WRITE_REG, (6 + MS_EXTRA_SIZE), NO_WAIT_INT, data, 16);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (val & INT_REG_CMDNK) {
ms_set_err_code(chip, MS_CMD_NK);
@@ -1784,17 +1687,16 @@ static int ms_copy_page(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
if (i == 0) {
retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE, SystemParm, 7);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
data[0] = 0x88;
- } else {
+ else
data[0] = 0x80;
- }
+
data[1] = 0;
data[2] = (u8)(old_blk >> 8);
data[3] = (u8)old_blk;
@@ -1804,20 +1706,17 @@ static int ms_copy_page(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
data[7] = 0xFF;
retval = ms_write_bytes(chip, WRITE_REG, 7, NO_WAIT_INT, data, 8);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (val & INT_REG_CMDNK) {
ms_set_err_code(chip, MS_CMD_NK);
@@ -1848,28 +1747,24 @@ static int reset_ms(struct rtsx_chip *chip)
#endif
retval = ms_prepare_reset(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_card->ms_type |= TYPE_MS;
retval = ms_send_cmd(chip, MS_RESET, NO_WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_read_status_reg(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_READ_REG(chip, PPBUF_BASE2, &val);
- if (val & WRT_PRTCT) {
+ if (val & WRT_PRTCT)
chip->card_wp |= MS_CARD;
- } else {
+ else
chip->card_wp &= ~MS_CARD;
- }
i = 0;
@@ -1913,21 +1808,18 @@ RE_SEARCH:
}
retval = ms_read_page(chip, ms_card->boot_block, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
/* Read MS system information as sys_info */
rtsx_init_cmd(chip);
- for (i = 0; i < 96; i++) {
+ for (i = 0; i < 96; i++)
rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 0x1A0 + i, 0, 0);
- }
retval = rtsx_send_cmd(chip, MS_CARD, 100);
- if (retval < 0) {
+ if (retval < 0)
TRACE_RET(chip, STATUS_FAIL);
- }
ptr = rtsx_get_cmd_data(chip);
memcpy(ms_card->raw_sys_info, ptr, 96);
@@ -1938,21 +1830,18 @@ RE_SEARCH:
rtsx_add_cmd(chip, READ_REG_CMD, HEADER_ID0, 0, 0);
rtsx_add_cmd(chip, READ_REG_CMD, HEADER_ID1, 0, 0);
- for (reg_addr = DISABLED_BLOCK0; reg_addr <= DISABLED_BLOCK3; reg_addr++) {
+ for (reg_addr = DISABLED_BLOCK0; reg_addr <= DISABLED_BLOCK3; reg_addr++)
rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
- }
- for (reg_addr = BLOCK_SIZE_0; reg_addr <= PAGE_SIZE_1; reg_addr++) {
+ for (reg_addr = BLOCK_SIZE_0; reg_addr <= PAGE_SIZE_1; reg_addr++)
rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
- }
rtsx_add_cmd(chip, READ_REG_CMD, MS_Device_Type, 0, 0);
rtsx_add_cmd(chip, READ_REG_CMD, MS_4bit_Support, 0, 0);
retval = rtsx_send_cmd(chip, MS_CARD, 100);
- if (retval < 0) {
+ if (retval < 0)
TRACE_RET(chip, STATUS_FAIL);
- }
ptr = rtsx_get_cmd_data(chip);
@@ -1975,9 +1864,8 @@ RE_SEARCH:
goto RE_SEARCH;
}
- if ((ptr[14] == 1) || (ptr[14] == 3)) {
+ if ((ptr[14] == 1) || (ptr[14] == 3))
chip->card_wp |= MS_CARD;
- }
/* BLOCK_SIZE_0, BLOCK_SIZE_1 */
block_size = ((u16)ptr[6] << 8) | ptr[7];
@@ -2026,17 +1914,15 @@ RE_SEARCH:
/* Switch I/F Mode */
if (ptr[15]) {
retval = ms_set_rw_reg_addr(chip, 0, 0, SystemParm, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_WRITE_REG(chip, PPBUF_BASE2, 0xFF, 0x88);
RTSX_WRITE_REG(chip, PPBUF_BASE2 + 1, 0xFF, 0);
retval = ms_transfer_tpc(chip, MS_TM_WRITE_BYTES, WRITE_REG , 1, NO_WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_WRITE_REG(chip, MS_CFG, 0x58 | MS_NO_CHECK_INT,
MS_BUS_WIDTH_4 | PUSH_TIME_ODD | MS_NO_CHECK_INT);
@@ -2044,11 +1930,10 @@ RE_SEARCH:
ms_card->ms_type |= MS_4BIT;
}
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
chip->card_bus_width[chip->card2lun[MS_CARD]] = 4;
- } else {
+ else
chip->card_bus_width[chip->card2lun[MS_CARD]] = 1;
- }
return STATUS_SUCCESS;
}
@@ -2065,30 +1950,27 @@ static int ms_init_l2p_tbl(struct rtsx_chip *chip)
size = ms_card->segment_cnt * sizeof(struct zone_entry);
ms_card->segment = vzalloc(size);
- if (ms_card->segment == NULL) {
+ if (ms_card->segment == NULL)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_read_page(chip, ms_card->boot_block, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, INIT_FAIL);
- }
reg_addr = PPBUF_BASE2;
for (i = 0; i < (((ms_card->total_block >> 9) * 10) + 1); i++) {
retval = rtsx_read_register(chip, reg_addr++, &val1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, INIT_FAIL);
- }
+
retval = rtsx_read_register(chip, reg_addr++, &val2);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, INIT_FAIL);
- }
defect_block = ((u16)val1 << 8) | val2;
- if (defect_block == 0xFFFF) {
+ if (defect_block == 0xFFFF)
break;
- }
+
seg_no = defect_block / 512;
ms_card->segment[seg_no].defect_list[ms_card->segment[seg_no].disable_count++] = defect_block;
}
@@ -2141,9 +2023,8 @@ static void ms_set_l2p_tbl(struct rtsx_chip *chip, int seg_no, u16 log_off, u16
return;
segment = &(ms_card->segment[seg_no]);
- if (segment->l2p_table) {
+ if (segment->l2p_table)
segment->l2p_table[log_off] = phy_blk;
- }
}
static void ms_set_unused_block(struct rtsx_chip *chip, u16 phy_blk)
@@ -2156,9 +2037,9 @@ static void ms_set_unused_block(struct rtsx_chip *chip, u16 phy_blk)
segment = &(ms_card->segment[seg_no]);
segment->free_table[segment->set_index++] = phy_blk;
- if (segment->set_index >= MS_FREE_TABLE_CNT) {
+ if (segment->set_index >= MS_FREE_TABLE_CNT)
segment->set_index = 0;
- }
+
segment->unused_blk_cnt++;
}
@@ -2175,9 +2056,9 @@ static u16 ms_get_unused_block(struct rtsx_chip *chip, int seg_no)
phy_blk = segment->free_table[segment->get_index];
segment->free_table[segment->get_index++] = 0xFFFF;
- if (segment->get_index >= MS_FREE_TABLE_CNT) {
+ if (segment->get_index >= MS_FREE_TABLE_CNT)
segment->get_index = 0;
- }
+
segment->unused_blk_cnt--;
return phy_blk;
@@ -2199,27 +2080,27 @@ static int ms_arbitrate_l2p(struct rtsx_chip *chip, u16 phy_blk, u16 log_off, u8
if (us1 != us2) {
if (us1 == 0) {
- if (!(chip->card_wp & MS_CARD)) {
+ if (!(chip->card_wp & MS_CARD))
ms_erase_block(chip, tmp_blk);
- }
+
ms_set_unused_block(chip, tmp_blk);
segment->l2p_table[log_off] = phy_blk;
} else {
- if (!(chip->card_wp & MS_CARD)) {
+ if (!(chip->card_wp & MS_CARD))
ms_erase_block(chip, phy_blk);
- }
+
ms_set_unused_block(chip, phy_blk);
}
} else {
if (phy_blk < tmp_blk) {
- if (!(chip->card_wp & MS_CARD)) {
+ if (!(chip->card_wp & MS_CARD))
ms_erase_block(chip, phy_blk);
- }
+
ms_set_unused_block(chip, phy_blk);
} else {
- if (!(chip->card_wp & MS_CARD)) {
+ if (!(chip->card_wp & MS_CARD))
ms_erase_block(chip, tmp_blk);
- }
+
ms_set_unused_block(chip, tmp_blk);
segment->l2p_table[log_off] = phy_blk;
}
@@ -2240,9 +2121,8 @@ static int ms_build_l2p_tbl(struct rtsx_chip *chip, int seg_no)
if (ms_card->segment == NULL) {
retval = ms_init_l2p_tbl(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, retval);
- }
}
if (ms_card->segment[seg_no].build_flag) {
@@ -2250,27 +2130,24 @@ static int ms_build_l2p_tbl(struct rtsx_chip *chip, int seg_no)
return STATUS_SUCCESS;
}
- if (seg_no == 0) {
+ if (seg_no == 0)
table_size = 494;
- } else {
+ else
table_size = 496;
- }
segment = &(ms_card->segment[seg_no]);
if (segment->l2p_table == NULL) {
segment->l2p_table = (u16 *)vmalloc(table_size * 2);
- if (segment->l2p_table == NULL) {
+ if (segment->l2p_table == NULL)
TRACE_GOTO(chip, BUILD_FAIL);
- }
}
memset((u8 *)(segment->l2p_table), 0xff, table_size * 2);
if (segment->free_table == NULL) {
segment->free_table = (u16 *)vmalloc(MS_FREE_TABLE_CNT * 2);
- if (segment->free_table == NULL) {
+ if (segment->free_table == NULL)
TRACE_GOTO(chip, BUILD_FAIL);
- }
}
memset((u8 *)(segment->free_table), 0xff, MS_FREE_TABLE_CNT * 2);
@@ -2368,13 +2245,11 @@ static int ms_build_l2p_tbl(struct rtsx_chip *chip, int seg_no)
/* Logical Address Confirmation Process */
if (seg_no == ms_card->segment_cnt - 1) {
- if (segment->unused_blk_cnt < 2) {
+ if (segment->unused_blk_cnt < 2)
chip->card_wp |= MS_CARD;
- }
} else {
- if (segment->unused_blk_cnt < 1) {
+ if (segment->unused_blk_cnt < 1)
chip->card_wp |= MS_CARD;
- }
}
if (chip->card_wp & MS_CARD)
@@ -2388,9 +2263,9 @@ static int ms_build_l2p_tbl(struct rtsx_chip *chip, int seg_no)
return STATUS_SUCCESS;
}
retval = ms_init_page(chip, phy_blk, log_blk, 0, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, BUILD_FAIL);
- }
+
segment->l2p_table[log_blk-ms_start_idx[seg_no]] = phy_blk;
if (seg_no == ms_card->segment_cnt - 1) {
if (segment->unused_blk_cnt < 2) {
@@ -2419,16 +2294,14 @@ static int ms_build_l2p_tbl(struct rtsx_chip *chip, int seg_no)
phy_blk = ms_get_unused_block(chip, 0);
retval = ms_copy_page(chip, tmp_blk, phy_blk,
log_blk, 0, ms_card->page_off + 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
segment->l2p_table[log_blk] = phy_blk;
retval = ms_set_bad_block(chip, tmp_blk);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
}
}
@@ -2458,14 +2331,12 @@ int reset_ms_card(struct rtsx_chip *chip)
memset(ms_card, 0, sizeof(struct ms_info));
retval = enable_card_clock(chip, MS_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = select_card(chip, MS_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_card->ms_type = 0;
@@ -2473,27 +2344,24 @@ int reset_ms_card(struct rtsx_chip *chip)
if (retval != STATUS_SUCCESS) {
if (ms_card->check_ms_flow) {
retval = reset_ms(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
TRACE_RET(chip, STATUS_FAIL);
}
}
retval = ms_set_init_para(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (!CHK_MSPRO(ms_card)) {
/* Build table for the last segment,
* to check if L2P table block exists, erasing it
*/
retval = ms_build_l2p_tbl(chip, ms_card->total_block / 512 - 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
RTSX_DEBUGP("ms_card->ms_type = 0x%x\n", ms_card->ms_type);
@@ -2520,9 +2388,8 @@ static int mspro_set_rw_cmd(struct rtsx_chip *chip, u32 start_sec, u16 sec_cnt,
if (retval == STATUS_SUCCESS)
break;
}
- if (i == MS_MAX_RETRY_COUNT) {
+ if (i == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -2556,21 +2423,18 @@ static inline int ms_auto_tune_clock(struct rtsx_chip *chip)
RTSX_DEBUGP("--%s--\n", __func__);
if (chip->asic_code) {
- if (ms_card->ms_clock > 30) {
+ if (ms_card->ms_clock > 30)
ms_card->ms_clock -= 20;
- }
} else {
- if (ms_card->ms_clock == CLK_80) {
+ if (ms_card->ms_clock == CLK_80)
ms_card->ms_clock = CLK_60;
- } else if (ms_card->ms_clock == CLK_60) {
+ else if (ms_card->ms_clock == CLK_60)
ms_card->ms_clock = CLK_40;
- }
}
retval = ms_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -2616,15 +2480,13 @@ static int mspro_rw_multi_sector(struct scsi_cmnd *srb, struct rtsx_chip *chip,
}
retval = ms_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
trans_mode = MS_TM_AUTO_READ;
- } else {
+ else
trans_mode = MS_TM_AUTO_WRITE;
- }
RTSX_READ_REG(chip, MS_TRANS_CFG, &val);
@@ -2639,9 +2501,8 @@ static int mspro_rw_multi_sector(struct scsi_cmnd *srb, struct rtsx_chip *chip,
ms_card->total_sec_cnt = 0;
if (val & MS_INT_BREQ) {
retval = ms_send_cmd(chip, PRO_STOP, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
}
@@ -2651,17 +2512,16 @@ static int mspro_rw_multi_sector(struct scsi_cmnd *srb, struct rtsx_chip *chip,
if (!ms_card->seq_mode) {
ms_card->total_sec_cnt = 0;
if (sector_cnt >= SEQ_START_CRITERIA) {
- if ((ms_card->capacity - start_sector) > 0xFE00) {
+ if ((ms_card->capacity - start_sector) > 0xFE00)
count = 0xFE00;
- } else {
+ else
count = (u16)(ms_card->capacity - start_sector);
- }
+
if (count > sector_cnt) {
- if (mode_2k) {
+ if (mode_2k)
ms_card->seq_mode |= MODE_2K_SEQ;
- } else {
+ else
ms_card->seq_mode |= MODE_512_SEQ;
- }
}
} else {
count = sector_cnt;
@@ -2686,9 +2546,9 @@ static int mspro_rw_multi_sector(struct scsi_cmnd *srb, struct rtsx_chip *chip,
TRACE_RET(chip, STATUS_FAIL);
}
- if (val & MS_INT_BREQ) {
+ if (val & MS_INT_BREQ)
ms_send_cmd(chip, PRO_STOP, WAIT_INT);
- }
+
if (val & (MS_CRC16_ERR | MS_RDY_TIMEOUT)) {
RTSX_DEBUGP("MSPro CRC error, tune clock!\n");
chip->rw_need_retry = 1;
@@ -2739,11 +2599,10 @@ static int mspro_read_format_progress(struct rtsx_chip *chip, const int short_da
TRACE_RET(chip, STATUS_FAIL);
}
- if (short_data_len >= 256) {
+ if (short_data_len >= 256)
cnt = 0;
- } else {
+ else
cnt = (u8)short_data_len;
- }
retval = rtsx_write_register(chip, MS_CFG, MS_NO_CHECK_INT, MS_NO_CHECK_INT);
if (retval != STATUS_SUCCESS) {
@@ -2778,9 +2637,8 @@ static int mspro_read_format_progress(struct rtsx_chip *chip, const int short_da
ms_card->format_status = FORMAT_FAIL;
TRACE_RET(chip, STATUS_FAIL);
}
- if (tmp & (MS_INT_CED | MS_INT_CMDNK | MS_INT_BREQ | MS_INT_ERR)) {
+ if (tmp & (MS_INT_CED | MS_INT_CMDNK | MS_INT_BREQ | MS_INT_ERR))
break;
- }
wait_timeout(1);
}
@@ -2843,14 +2701,12 @@ int mspro_format(struct scsi_cmnd *srb, struct rtsx_chip *chip, int short_data_l
RTSX_DEBUGP("--%s--\n", __func__);
retval = ms_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_set_rw_reg_addr(chip, 0x00, 0x00, Pro_TPCParm, 0x01);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
memset(buf, 0, 2);
switch (short_data_len) {
@@ -2874,25 +2730,22 @@ int mspro_format(struct scsi_cmnd *srb, struct rtsx_chip *chip, int short_data_l
if (retval == STATUS_SUCCESS)
break;
}
- if (i == MS_MAX_RETRY_COUNT) {
+ if (i == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (quick_format) {
+ if (quick_format)
para = 0x0000;
- } else {
+ else
para = 0x0001;
- }
+
retval = mspro_set_rw_cmd(chip, 0, para, PRO_FORMAT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_READ_REG(chip, MS_TRANS_CFG, &tmp);
- if (tmp & (MS_INT_CMDNK | MS_INT_ERR)) {
+ if (tmp & (MS_INT_CMDNK | MS_INT_ERR))
TRACE_RET(chip, STATUS_FAIL);
- }
if ((tmp & (MS_INT_BREQ | MS_INT_CED)) == MS_INT_BREQ) {
ms_card->pro_under_formatting = 1;
@@ -2930,15 +2783,14 @@ static int ms_read_multiple_pages(struct rtsx_chip *chip, u16 phy_blk, u16 log_b
}
retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE, SystemParm, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
data[0] = 0x88;
- } else {
+ else
data[0] = 0x80;
- }
+
data[1] = 0;
data[2] = (u8)(phy_blk >> 8);
data[3] = (u8)phy_blk;
@@ -2950,16 +2802,14 @@ static int ms_read_multiple_pages(struct rtsx_chip *chip, u16 phy_blk, u16 log_b
if (retval == STATUS_SUCCESS)
break;
}
- if (i == MS_MAX_RETRY_COUNT) {
+ if (i == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
retval = ms_send_cmd(chip, BLOCK_READ, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ptr = buf;
@@ -2972,9 +2822,9 @@ static int ms_read_multiple_pages(struct rtsx_chip *chip, u16 phy_blk, u16 log_b
}
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
if (val & INT_REG_CMDNK) {
ms_set_err_code(chip, MS_CMD_NK);
TRACE_RET(chip, STATUS_FAIL);
@@ -3006,15 +2856,14 @@ static int ms_read_multiple_pages(struct rtsx_chip *chip, u16 phy_blk, u16 log_b
if (page_addr == (end_page - 1)) {
if (!(val & INT_REG_CED)) {
retval = ms_send_cmd(chip, BLOCK_END, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
if (!(val & INT_REG_CED)) {
ms_set_err_code(chip, MS_FLASH_READ_ERROR);
TRACE_RET(chip, STATUS_FAIL);
@@ -3079,15 +2928,14 @@ static int ms_write_multiple_pages(struct rtsx_chip *chip, u16 old_blk, u16 new_
if (!start_page) {
retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE, SystemParm, 7);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
data[0] = 0x88;
- } else {
+ else
data[0] = 0x80;
- }
+
data[1] = 0;
data[2] = (u8)(old_blk >> 8);
data[3] = (u8)old_blk;
@@ -3097,74 +2945,66 @@ static int ms_write_multiple_pages(struct rtsx_chip *chip, u16 old_blk, u16 new_
data[7] = 0xFF;
retval = ms_write_bytes(chip, WRITE_REG, 7, NO_WAIT_INT, data, 8);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
retval = ms_transfer_tpc(chip, MS_TM_READ_BYTES, GET_INT, 1, NO_WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
retval = ms_set_rw_reg_addr(chip, OverwriteFlag, MS_EXTRA_SIZE, SystemParm, (6 + MS_EXTRA_SIZE));
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ms_set_err_code(chip, MS_NO_ERROR);
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
data[0] = 0x88;
- } else {
+ else
data[0] = 0x80;
- }
+
data[1] = 0;
data[2] = (u8)(new_blk >> 8);
data[3] = (u8)new_blk;
- if ((end_page - start_page) == 1) {
+ if ((end_page - start_page) == 1)
data[4] = 0x20;
- } else {
+ else
data[4] = 0;
- }
+
data[5] = start_page;
data[6] = 0xF8;
data[7] = 0xFF;
data[8] = (u8)(log_blk >> 8);
data[9] = (u8)log_blk;
- for (i = 0x0A; i < 0x10; i++) {
+ for (i = 0x0A; i < 0x10; i++)
data[i] = 0xFF;
- }
for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
retval = ms_write_bytes(chip, WRITE_REG, 6 + MS_EXTRA_SIZE, NO_WAIT_INT, data, 16);
if (retval == STATUS_SUCCESS)
break;
}
- if (i == MS_MAX_RETRY_COUNT) {
+ if (i == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
for (i = 0; i < MS_MAX_RETRY_COUNT; i++) {
retval = ms_send_cmd(chip, BLOCK_WRITE, WAIT_INT);
if (retval == STATUS_SUCCESS)
break;
}
- if (i == MS_MAX_RETRY_COUNT) {
+ if (i == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
ptr = buf;
for (page_addr = start_page; page_addr < end_page; page_addr++) {
@@ -3210,17 +3050,15 @@ static int ms_write_multiple_pages(struct rtsx_chip *chip, u16 old_blk, u16 new_
ms_set_err_code(chip, MS_TO_ERROR);
rtsx_clear_ms_error(chip);
- if (retval == -ETIMEDOUT) {
+ if (retval == -ETIMEDOUT)
TRACE_RET(chip, STATUS_TIMEDOUT);
- } else {
+ else
TRACE_RET(chip, STATUS_FAIL);
- }
}
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if ((end_page - start_page) == 1) {
if (!(val & INT_REG_CED)) {
@@ -3231,15 +3069,13 @@ static int ms_write_multiple_pages(struct rtsx_chip *chip, u16 old_blk, u16 new_
if (page_addr == (end_page - 1)) {
if (!(val & INT_REG_CED)) {
retval = ms_send_cmd(chip, BLOCK_END, WAIT_INT);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
retval = ms_read_bytes(chip, GET_INT, 1, NO_WAIT_INT, &val, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
if ((page_addr == (end_page - 1)) || (page_addr == ms_card->page_off)) {
@@ -3266,9 +3102,8 @@ static int ms_finish_write(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
retval = ms_copy_page(chip, old_blk, new_blk, log_blk,
page_off, ms_card->page_off + 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
seg_no = old_blk >> 9;
@@ -3277,9 +3112,8 @@ static int ms_finish_write(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
ms_set_bad_block(chip, old_blk);
} else {
retval = ms_erase_block(chip, old_blk);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
ms_set_unused_block(chip, old_blk);
- }
}
ms_set_l2p_tbl(chip, seg_no, log_blk - ms_start_idx[seg_no], new_blk);
@@ -3294,9 +3128,8 @@ static int ms_prepare_write(struct rtsx_chip *chip, u16 old_blk, u16 new_blk,
if (start_page) {
retval = ms_copy_page(chip, old_blk, new_blk, log_blk, 0, start_page);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -3311,17 +3144,15 @@ int ms_delay_write(struct rtsx_chip *chip)
if (delay_write->delay_write_flag) {
retval = ms_set_init_para(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
delay_write->delay_write_flag = 0;
retval = ms_finish_write(chip,
delay_write->old_phyblock, delay_write->new_phyblock,
delay_write->logblock, delay_write->pageoff);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -3330,11 +3161,10 @@ int ms_delay_write(struct rtsx_chip *chip)
static inline void ms_rw_fail(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
- if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
- } else {
+ else
set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_WRITE_ERR);
- }
}
static int ms_rw_multi_sector(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 start_sector, u16 sector_cnt)
@@ -3449,11 +3279,11 @@ static int ms_rw_multi_sector(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32
RTSX_DEBUGP("seg_no = %d, old_blk = 0x%x, new_blk = 0x%x\n", seg_no, old_blk, new_blk);
while (total_sec_cnt) {
- if ((start_page + total_sec_cnt) > (ms_card->page_off + 1)) {
+ if ((start_page + total_sec_cnt) > (ms_card->page_off + 1))
end_page = ms_card->page_off + 1;
- } else {
+ else
end_page = start_page + (u8)total_sec_cnt;
- }
+
page_cnt = end_page - start_page;
RTSX_DEBUGP("start_page = %d, end_page = %d, page_cnt = %d\n",
@@ -3482,9 +3312,9 @@ static int ms_rw_multi_sector(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32
if (srb->sc_data_direction == DMA_TO_DEVICE) {
if (end_page == (ms_card->page_off + 1)) {
retval = ms_erase_block(chip, old_blk);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
ms_set_unused_block(chip, old_blk);
- }
+
ms_set_l2p_tbl(chip, seg_no, log_blk - ms_start_idx[seg_no], new_blk);
}
}
@@ -3565,11 +3395,10 @@ int ms_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 start_sector, u16 s
struct ms_info *ms_card = &(chip->ms_card);
int retval;
- if (CHK_MSPRO(ms_card)) {
+ if (CHK_MSPRO(ms_card))
retval = mspro_rw_multi_sector(srb, chip, start_sector, sector_cnt);
- } else {
+ else
retval = ms_rw_multi_sector(srb, chip, start_sector, sector_cnt);
- }
return retval;
}
@@ -3609,14 +3438,12 @@ static int ms_poll_int(struct rtsx_chip *chip)
rtsx_add_cmd(chip, CHECK_REG_CMD, MS_TRANS_CFG, MS_INT_CED, MS_INT_CED);
retval = rtsx_send_cmd(chip, MS_CARD, 5000);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
val = *rtsx_get_cmd_data(chip);
- if (val & MS_INT_ERR) {
+ if (val & MS_INT_ERR)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -3678,9 +3505,8 @@ static int mg_send_ex_cmd(struct rtsx_chip *chip, u8 cmd, u8 entry_num)
if (retval == STATUS_SUCCESS)
break;
}
- if (i == MS_MAX_RETRY_COUNT) {
+ if (i == MS_MAX_RETRY_COUNT)
TRACE_RET(chip, STATUS_FAIL);
- }
if (check_ms_err(chip)) {
rtsx_clear_ms_error(chip);
@@ -3697,14 +3523,13 @@ static int mg_set_tpc_para_sub(struct rtsx_chip *chip, int type, u8 mg_entry_num
RTSX_DEBUGP("--%s--\n", __func__);
- if (type == 0) {
+ if (type == 0)
retval = ms_set_rw_reg_addr(chip, 0, 0, Pro_TPCParm, 1);
- } else {
+ else
retval = ms_set_rw_reg_addr(chip, 0, 0, Pro_DataCount1, 6);
- }
- if (retval != STATUS_SUCCESS) {
+
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
buf[0] = 0;
buf[1] = 0;
@@ -3715,9 +3540,8 @@ static int mg_set_tpc_para_sub(struct rtsx_chip *chip, int type, u8 mg_entry_num
buf[5] = mg_entry_num;
}
retval = ms_write_bytes(chip, PRO_WRITE_REG, (type == 0) ? 1 : 6, NO_WAIT_INT, buf, 6);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -3739,9 +3563,8 @@ int mg_set_leaf_id(struct scsi_cmnd *srb, struct rtsx_chip *chip)
ms_cleanup_work(chip);
retval = ms_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = mg_send_ex_cmd(chip, MG_SET_LID, 0);
if (retval != STATUS_SUCCESS) {
@@ -3751,9 +3574,9 @@ int mg_set_leaf_id(struct scsi_cmnd *srb, struct rtsx_chip *chip)
memset(buf1, 0, 32);
rtsx_stor_get_xfer_buf(buf2, min(12, (int)scsi_bufflen(srb)), srb);
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < 8; i++)
buf1[8+i] = buf2[4+i];
- }
+
retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA, 32, WAIT_INT, buf1, 32);
if (retval != STATUS_SUCCESS) {
set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
@@ -3780,14 +3603,12 @@ int mg_get_local_EKB(struct scsi_cmnd *srb, struct rtsx_chip *chip)
ms_cleanup_work(chip);
retval = ms_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
buf = kmalloc(1540, GFP_KERNEL);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, STATUS_ERROR);
- }
buf[0] = 0x04;
buf[1] = 0x1A;
@@ -3835,9 +3656,8 @@ int mg_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
ms_cleanup_work(chip);
retval = ms_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = mg_send_ex_cmd(chip, MG_GET_ID, 0);
if (retval != STATUS_SUCCESS) {
@@ -3875,12 +3695,12 @@ int mg_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
bufflen = min(12, (int)scsi_bufflen(srb));
rtsx_stor_get_xfer_buf(buf, bufflen, srb);
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < 8; i++)
buf[i] = buf[4+i];
- }
- for (i = 0; i < 24; i++) {
+
+ for (i = 0; i < 24; i++)
buf[8+i] = 0;
- }
+
retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA,
32, WAIT_INT, buf, 32);
if (retval != STATUS_SUCCESS) {
@@ -3911,9 +3731,8 @@ int mg_get_rsp_chg(struct scsi_cmnd *srb, struct rtsx_chip *chip)
ms_cleanup_work(chip);
retval = ms_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = mg_send_ex_cmd(chip, MG_MAKE_RMS, 0);
if (retval != STATUS_SUCCESS) {
@@ -3968,9 +3787,8 @@ int mg_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip)
ms_cleanup_work(chip);
retval = ms_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = mg_send_ex_cmd(chip, MG_MAKE_KSE, 0);
if (retval != STATUS_SUCCESS) {
@@ -3981,12 +3799,12 @@ int mg_rsp(struct scsi_cmnd *srb, struct rtsx_chip *chip)
bufflen = min(12, (int)scsi_bufflen(srb));
rtsx_stor_get_xfer_buf(buf, bufflen, srb);
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < 8; i++)
buf[i] = buf[4+i];
- }
- for (i = 0; i < 24; i++) {
+
+ for (i = 0; i < 24; i++)
buf[8+i] = 0;
- }
+
retval = ms_write_bytes(chip, PRO_WRITE_SHORT_DATA, 32, WAIT_INT, buf, 32);
if (retval != STATUS_SUCCESS) {
set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_AUTHEN);
@@ -4016,14 +3834,12 @@ int mg_get_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip)
ms_cleanup_work(chip);
retval = ms_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
buf = kmalloc(1028, GFP_KERNEL);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, STATUS_ERROR);
- }
buf[0] = 0x04;
buf[1] = 0x02;
@@ -4073,14 +3889,12 @@ int mg_set_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip)
ms_cleanup_work(chip);
retval = ms_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
buf = kmalloc(1028, GFP_KERNEL);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, STATUS_ERROR);
- }
bufflen = min(1028, (int)scsi_bufflen(srb));
rtsx_stor_get_xfer_buf(buf, bufflen, srb);
@@ -4088,11 +3902,10 @@ int mg_set_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip)
retval = mg_send_ex_cmd(chip, MG_SET_IBD, ms_card->mg_entry_num);
if (retval != STATUS_SUCCESS) {
if (ms_card->mg_auth == 0) {
- if ((buf[5] & 0xC0) != 0) {
+ if ((buf[5] & 0xC0) != 0)
set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
- } else {
+ else
set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR);
- }
} else {
set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR);
}
@@ -4121,11 +3934,10 @@ int mg_set_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip)
if ((retval < 0) || check_ms_err(chip)) {
rtsx_clear_ms_error(chip);
if (ms_card->mg_auth == 0) {
- if ((buf[5] & 0xC0) != 0) {
+ if ((buf[5] & 0xC0) != 0)
set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
- } else {
+ else
set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR);
- }
} else {
set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR);
}
@@ -4139,11 +3951,10 @@ int mg_set_ICV(struct scsi_cmnd *srb, struct rtsx_chip *chip)
if ((retval != STATUS_SUCCESS) || check_ms_err(chip)) {
rtsx_clear_ms_error(chip);
if (ms_card->mg_auth == 0) {
- if ((buf[5] & 0xC0) != 0) {
+ if ((buf[5] & 0xC0) != 0)
set_sense_type(chip, lun, SENSE_TYPE_MG_KEY_FAIL_NOT_ESTAB);
- } else {
+ else
set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR);
- }
} else {
set_sense_type(chip, lun, SENSE_TYPE_MG_WRITE_ERR);
}
@@ -4187,14 +3998,13 @@ int ms_power_off_card3v3(struct rtsx_chip *chip)
int retval;
retval = disable_card_clock(chip, MS_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
if (chip->asic_code) {
retval = ms_pull_ctl_disable(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
RTSX_WRITE_REG(chip, FPGA_PULL_CTL,
FPGA_MS_PULL_CTL_BIT | 0x20, FPGA_MS_PULL_CTL_BIT);
@@ -4202,9 +4012,8 @@ int ms_power_off_card3v3(struct rtsx_chip *chip)
RTSX_WRITE_REG(chip, CARD_OE, MS_OUTPUT_EN, 0);
if (!chip->ft2_fast_mode) {
retval = card_power_off(chip, MS_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -4234,9 +4043,8 @@ int release_ms_card(struct rtsx_chip *chip)
#endif
retval = ms_power_off_card3v3(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
diff --git a/drivers/staging/rts_pstor/rtsx.c b/drivers/staging/rts_pstor/rtsx.c
index 5fb05a2edebb..afe9c2e763d7 100644
--- a/drivers/staging/rts_pstor/rtsx.c
+++ b/drivers/staging/rts_pstor/rtsx.c
@@ -20,6 +20,8 @@
* No. 450, Shenhu Road, Suzhou Industry Park, Suzhou, China
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/blkdev.h>
#include <linux/kthread.h>
#include <linux/sched.h>
@@ -170,14 +172,14 @@ static int queuecommand_lck(struct scsi_cmnd *srb,
/* check for state-transition errors */
if (chip->srb != NULL) {
- printk(KERN_ERR "Error in %s: chip->srb = %p\n",
+ dev_err(&dev->pci->dev, "Error in %s: chip->srb = %p\n",
__func__, chip->srb);
return SCSI_MLQUEUE_HOST_BUSY;
}
/* fail the command if we are disconnecting */
if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
- printk(KERN_INFO "Fail command during disconnect\n");
+ dev_info(&dev->pci->dev, "Fail command during disconnect\n");
srb->result = DID_NO_CONNECT << 16;
done(srb);
return 0;
@@ -204,14 +206,14 @@ static int command_abort(struct scsi_cmnd *srb)
struct rtsx_dev *dev = host_to_rtsx(host);
struct rtsx_chip *chip = dev->chip;
- printk(KERN_INFO "%s called\n", __func__);
+ dev_info(&dev->pci->dev, "%s called\n", __func__);
scsi_lock(host);
/* Is this command still active? */
if (chip->srb != srb) {
scsi_unlock(host);
- printk(KERN_INFO "-- nothing to abort\n");
+ dev_info(&dev->pci->dev, "-- nothing to abort\n");
return FAILED;
}
@@ -230,8 +232,9 @@ static int command_abort(struct scsi_cmnd *srb)
static int device_reset(struct scsi_cmnd *srb)
{
int result = 0;
+ struct rtsx_dev *dev = host_to_rtsx(srb->device->host);
- printk(KERN_INFO "%s called\n", __func__);
+ dev_info(&dev->pci->dev, "%s called\n", __func__);
return result < 0 ? FAILED : SUCCESS;
}
@@ -240,8 +243,9 @@ static int device_reset(struct scsi_cmnd *srb)
static int bus_reset(struct scsi_cmnd *srb)
{
int result = 0;
+ struct rtsx_dev *dev = host_to_rtsx(srb->device->host);
- printk(KERN_INFO "%s called\n", __func__);
+ dev_info(&dev->pci->dev, "%s called\n", __func__);
return result < 0 ? FAILED : SUCCESS;
}
@@ -303,14 +307,15 @@ static int rtsx_acquire_irq(struct rtsx_dev *dev)
{
struct rtsx_chip *chip = dev->chip;
- printk(KERN_INFO "%s: chip->msi_en = %d, pci->irq = %d\n",
- __func__, chip->msi_en, dev->pci->irq);
+ dev_info(&dev->pci->dev, "%s: chip->msi_en = %d, pci->irq = %d\n",
+ __func__, chip->msi_en, dev->pci->irq);
if (request_irq(dev->pci->irq, rtsx_interrupt,
chip->msi_en ? 0 : IRQF_SHARED,
CR_DRIVER_NAME, dev)) {
- printk(KERN_ERR "rtsx: unable to grab IRQ %d, "
- "disabling device\n", dev->pci->irq);
+ dev_err(&dev->pci->dev,
+ "rtsx: unable to grab IRQ %d, disabling device\n",
+ dev->pci->irq);
return -1;
}
@@ -347,12 +352,8 @@ static int rtsx_suspend(struct pci_dev *pci, pm_message_t state)
struct rtsx_dev *dev = (struct rtsx_dev *)pci_get_drvdata(pci);
struct rtsx_chip *chip;
- printk(KERN_INFO "Ready to suspend\n");
-
- if (!dev) {
- printk(KERN_ERR "Invalid memory\n");
+ if (!dev)
return 0;
- }
/* lock the device pointers */
mutex_lock(&(dev->dev_mutex));
@@ -386,12 +387,8 @@ static int rtsx_resume(struct pci_dev *pci)
struct rtsx_dev *dev = (struct rtsx_dev *)pci_get_drvdata(pci);
struct rtsx_chip *chip;
- printk(KERN_INFO "Ready to resume\n");
-
- if (!dev) {
- printk(KERN_ERR "Invalid memory\n");
+ if (!dev)
return 0;
- }
chip = dev->chip;
@@ -401,8 +398,9 @@ static int rtsx_resume(struct pci_dev *pci)
pci_set_power_state(pci, PCI_D0);
pci_restore_state(pci);
if (pci_enable_device(pci) < 0) {
- printk(KERN_ERR "%s: pci_enable_device failed, "
- "disabling device\n", CR_DRIVER_NAME);
+ dev_err(&dev->pci->dev,
+ "%s: pci_enable_device failed, disabling device\n",
+ CR_DRIVER_NAME);
/* unlock the device pointers */
mutex_unlock(&dev->dev_mutex);
return -EIO;
@@ -435,12 +433,8 @@ static void rtsx_shutdown(struct pci_dev *pci)
struct rtsx_dev *dev = (struct rtsx_dev *)pci_get_drvdata(pci);
struct rtsx_chip *chip;
- printk(KERN_INFO "Ready to shutdown\n");
-
- if (!dev) {
- printk(KERN_ERR "Invalid memory\n");
+ if (!dev)
return;
- }
chip = dev->chip;
@@ -475,7 +469,7 @@ static int rtsx_control_thread(void *__dev)
/* if the device has disconnected, we are free to exit */
if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
- printk(KERN_INFO "-- rtsx-control exiting\n");
+ dev_info(&dev->pci->dev, "-- rtsx-control exiting\n");
mutex_unlock(&dev->dev_mutex);
break;
}
@@ -495,7 +489,7 @@ static int rtsx_control_thread(void *__dev)
* is UNKNOWN
*/
if (chip->srb->sc_data_direction == DMA_BIDIRECTIONAL) {
- printk(KERN_ERR "UNKNOWN data direction\n");
+ dev_err(&dev->pci->dev, "UNKNOWN data direction\n");
chip->srb->result = DID_ERROR << 16;
}
@@ -503,14 +497,14 @@ static int rtsx_control_thread(void *__dev)
* the maximum known LUN
*/
else if (chip->srb->device->id) {
- printk(KERN_ERR "Bad target number (%d:%d)\n",
+ dev_err(&dev->pci->dev, "Bad target number (%d:%d)\n",
chip->srb->device->id,
chip->srb->device->lun);
chip->srb->result = DID_BAD_TARGET << 16;
}
else if (chip->srb->device->lun > chip->max_lun) {
- printk(KERN_ERR "Bad LUN (%d:%d)\n",
+ dev_err(&dev->pci->dev, "Bad LUN (%d:%d)\n",
chip->srb->device->id,
chip->srb->device->lun);
chip->srb->result = DID_BAD_TARGET << 16;
@@ -534,7 +528,7 @@ static int rtsx_control_thread(void *__dev)
chip->srb->scsi_done(chip->srb);
} else {
SkipForAbort:
- printk(KERN_ERR "scsi command aborted\n");
+ dev_err(&dev->pci->dev, "scsi command aborted\n");
}
if (rtsx_chk_stat(chip, RTSX_STAT_ABORT)) {
@@ -594,7 +588,7 @@ static int rtsx_polling_thread(void *__dev)
/* if the device has disconnected, we are free to exit */
if (rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
- printk(KERN_INFO "-- rtsx-polling exiting\n");
+ dev_info(&dev->pci->dev, "-- rtsx-polling exiting\n");
mutex_unlock(&dev->dev_mutex);
break;
}
@@ -683,13 +677,13 @@ Exit:
/* Release all our dynamic resources */
static void rtsx_release_resources(struct rtsx_dev *dev)
{
- printk(KERN_INFO "-- %s\n", __func__);
+ dev_info(&dev->pci->dev, "-- %s\n", __func__);
/* Tell the control thread to exit. The SCSI host must
* already have been removed so it won't try to queue
* any more commands.
*/
- printk(KERN_INFO "-- sending exit command to thread\n");
+ dev_info(&dev->pci->dev, "-- sending exit command to thread\n");
complete(&dev->cmnd_ready);
if (dev->ctl_thread)
wait_for_completion(&dev->control_exit);
@@ -774,8 +768,9 @@ static int rtsx_scan_thread(void *__dev)
/* Wait for the timeout to expire or for a disconnect */
if (delay_use > 0) {
- printk(KERN_INFO "%s: waiting for device "
- "to settle before scanning\n", CR_DRIVER_NAME);
+ dev_info(&dev->pci->dev,
+ "%s: waiting for device to settle before scanning\n",
+ CR_DRIVER_NAME);
wait_event_interruptible_timeout(dev->delay_wait,
rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT),
delay_use * HZ);
@@ -784,7 +779,8 @@ static int rtsx_scan_thread(void *__dev)
/* If the device is still connected, perform the scanning */
if (!rtsx_chk_stat(chip, RTSX_STAT_DISCONNECT)) {
scsi_scan_host(rtsx_to_host(dev));
- printk(KERN_INFO "%s: device scan complete\n", CR_DRIVER_NAME);
+ dev_info(&dev->pci->dev, "%s: device scan complete\n",
+ CR_DRIVER_NAME);
/* Should we unbind if no devices were detected? */
}
@@ -906,14 +902,14 @@ static int __devinit rtsx_probe(struct pci_dev *pci,
err = pci_enable_device(pci);
if (err < 0) {
- printk(KERN_ERR "PCI enable device failed!\n");
+ dev_err(&pci->dev, "PCI enable device failed!\n");
return err;
}
err = pci_request_regions(pci, CR_DRIVER_NAME);
if (err < 0) {
- printk(KERN_ERR "PCI request regions for %s failed!\n",
- CR_DRIVER_NAME);
+ dev_err(&pci->dev, "PCI request regions for %s failed!\n",
+ CR_DRIVER_NAME);
pci_disable_device(pci);
return err;
}
@@ -924,7 +920,7 @@ static int __devinit rtsx_probe(struct pci_dev *pci,
*/
host = scsi_host_alloc(&rtsx_host_template, sizeof(*dev));
if (!host) {
- printk(KERN_ERR "Unable to allocate the scsi host\n");
+ dev_err(&pci->dev, "Unable to allocate the scsi host\n");
pci_release_regions(pci);
pci_disable_device(pci);
return -ENOMEM;
@@ -949,12 +945,12 @@ static int __devinit rtsx_probe(struct pci_dev *pci,
dev->pci = pci;
dev->irq = -1;
- printk(KERN_INFO "Resource length: 0x%x\n",
- (unsigned int)pci_resource_len(pci, 0));
+ dev_info(&pci->dev, "Resource length: 0x%x\n",
+ (unsigned int)pci_resource_len(pci, 0));
dev->addr = pci_resource_start(pci, 0);
dev->remap_addr = ioremap_nocache(dev->addr, pci_resource_len(pci, 0));
if (dev->remap_addr == NULL) {
- printk(KERN_ERR "ioremap error\n");
+ dev_err(&pci->dev, "ioremap error\n");
err = -ENXIO;
goto errout;
}
@@ -963,13 +959,13 @@ static int __devinit rtsx_probe(struct pci_dev *pci,
* Using "unsigned long" cast here to eliminate gcc warning in
* 64-bit system
*/
- printk(KERN_INFO "Original address: 0x%lx, remapped address: 0x%lx\n",
- (unsigned long)(dev->addr), (unsigned long)(dev->remap_addr));
+ dev_info(&pci->dev, "Original address: 0x%lx, remapped address: 0x%lx\n",
+ (unsigned long)(dev->addr), (unsigned long)(dev->remap_addr));
dev->rtsx_resv_buf = dma_alloc_coherent(&(pci->dev), RTSX_RESV_BUF_LEN,
&(dev->rtsx_resv_buf_addr), GFP_KERNEL);
if (dev->rtsx_resv_buf == NULL) {
- printk(KERN_ERR "alloc dma buffer fail\n");
+ dev_err(&pci->dev, "alloc dma buffer fail\n");
err = -ENXIO;
goto errout;
}
@@ -983,7 +979,7 @@ static int __devinit rtsx_probe(struct pci_dev *pci,
rtsx_init_options(dev->chip);
- printk(KERN_INFO "pci->irq = %d\n", pci->irq);
+ dev_info(&pci->dev, "pci->irq = %d\n", pci->irq);
if (dev->chip->msi_en) {
if (pci_enable_msi(pci) < 0)
@@ -1008,7 +1004,7 @@ static int __devinit rtsx_probe(struct pci_dev *pci,
/* Start up our control thread */
th = kthread_run(rtsx_control_thread, dev, CR_DRIVER_NAME);
if (IS_ERR(th)) {
- printk(KERN_ERR "Unable to start control thread\n");
+ dev_err(&pci->dev, "Unable to start control thread\n");
err = PTR_ERR(th);
goto errout;
}
@@ -1016,14 +1012,14 @@ static int __devinit rtsx_probe(struct pci_dev *pci,
err = scsi_add_host(host, &pci->dev);
if (err) {
- printk(KERN_ERR "Unable to add the scsi host\n");
+ dev_err(&pci->dev, "Unable to add the scsi host\n");
goto errout;
}
/* Start up the thread for delayed SCSI-device scanning */
th = kthread_run(rtsx_scan_thread, dev, "rtsx-scan");
if (IS_ERR(th)) {
- printk(KERN_ERR "Unable to start the device-scanning thread\n");
+ dev_err(&pci->dev, "Unable to start the device-scanning thread\n");
complete(&dev->scanning_done);
quiesce_and_remove_host(dev);
err = PTR_ERR(th);
@@ -1033,7 +1029,7 @@ static int __devinit rtsx_probe(struct pci_dev *pci,
/* Start up the thread for polling thread */
th = kthread_run(rtsx_polling_thread, dev, "rtsx-polling");
if (IS_ERR(th)) {
- printk(KERN_ERR "Unable to start the device-polling thread\n");
+ dev_err(&pci->dev, "Unable to start the device-polling thread\n");
quiesce_and_remove_host(dev);
err = PTR_ERR(th);
goto errout;
@@ -1046,7 +1042,7 @@ static int __devinit rtsx_probe(struct pci_dev *pci,
/* We come here if there are any problems */
errout:
- printk(KERN_ERR "rtsx_probe() failed\n");
+ dev_err(&pci->dev, "rtsx_probe() failed\n");
release_everything(dev);
return err;
@@ -1057,7 +1053,7 @@ static void __devexit rtsx_remove(struct pci_dev *pci)
{
struct rtsx_dev *dev = (struct rtsx_dev *)pci_get_drvdata(pci);
- printk(KERN_INFO "rtsx_remove() called\n");
+ dev_info(&pci->dev, "rtsx_remove() called\n");
quiesce_and_remove_host(dev);
release_everything(dev);
@@ -1090,18 +1086,18 @@ static struct pci_driver driver = {
static int __init rtsx_init(void)
{
- printk(KERN_INFO "Initializing Realtek PCIE storage driver...\n");
+ pr_info("Initializing Realtek PCIE storage driver...\n");
return pci_register_driver(&driver);
}
static void __exit rtsx_exit(void)
{
- printk(KERN_INFO "rtsx_exit() called\n");
+ pr_info("rtsx_exit() called\n");
pci_unregister_driver(&driver);
- printk(KERN_INFO "%s module exit\n", CR_DRIVER_NAME);
+ pr_info("%s module exit\n", CR_DRIVER_NAME);
}
module_init(rtsx_init)
diff --git a/drivers/staging/rts_pstor/rtsx_card.c b/drivers/staging/rts_pstor/rtsx_card.c
index 4f971f2e930a..539aa6a27788 100644
--- a/drivers/staging/rts_pstor/rtsx_card.c
+++ b/drivers/staging/rts_pstor/rtsx_card.c
@@ -309,11 +309,10 @@ static void release_sdio(struct rtsx_chip *chip)
if (chip->chip_insert_with_sdio) {
chip->chip_insert_with_sdio = 0;
- if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_PID(chip, 0x5288))
rtsx_write_register(chip, 0xFE5A, 0x08, 0x00);
- } else {
+ else
rtsx_write_register(chip, 0xFE70, 0x80, 0x00);
- }
}
rtsx_write_register(chip, SDIO_CTRL, SDIO_CD_CTRL, 0);
@@ -379,11 +378,10 @@ void rtsx_reset_cards(struct rtsx_chip *chip)
if (chip->need_reset & XD_CARD) {
chip->card_exist |= XD_CARD;
- if (chip->xd_show_cnt >= MAX_SHOW_CNT) {
+ if (chip->xd_show_cnt >= MAX_SHOW_CNT)
do_reset_xd_card(chip);
- } else {
+ else
chip->xd_show_cnt++;
- }
}
if (CHECK_PID(chip, 0x5288) && CHECK_BARO_PKG(chip, QFN)) {
if (chip->card_exist & XD_CARD) {
@@ -404,11 +402,10 @@ void rtsx_reset_cards(struct rtsx_chip *chip)
if (chip->need_reset & MS_CARD) {
chip->card_exist |= MS_CARD;
- if (chip->ms_show_cnt >= MAX_SHOW_CNT) {
+ if (chip->ms_show_cnt >= MAX_SHOW_CNT)
do_reset_ms_card(chip);
- } else {
+ else
chip->ms_show_cnt++;
- }
}
}
@@ -473,13 +470,12 @@ void card_cd_debounce(struct rtsx_chip *chip, unsigned long *need_reset, unsigne
release_map |= MS_CARD;
}
} else {
- if (chip->int_reg & XD_EXIST) {
+ if (chip->int_reg & XD_EXIST)
reset_map |= XD_CARD;
- } else if (chip->int_reg & SD_EXIST) {
+ else if (chip->int_reg & SD_EXIST)
reset_map |= SD_CARD;
- } else if (chip->int_reg & MS_EXIST) {
+ else if (chip->int_reg & MS_EXIST)
reset_map |= MS_CARD;
- }
}
if (reset_map) {
@@ -489,21 +485,21 @@ void card_cd_debounce(struct rtsx_chip *chip, unsigned long *need_reset, unsigne
for (i = 0; i < (DEBOUNCE_CNT); i++) {
chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
- if (chip->int_reg & XD_EXIST) {
+ if (chip->int_reg & XD_EXIST)
xd_cnt++;
- } else {
+ else
xd_cnt = 0;
- }
- if (chip->int_reg & SD_EXIST) {
+
+ if (chip->int_reg & SD_EXIST)
sd_cnt++;
- } else {
+ else
sd_cnt = 0;
- }
- if (chip->int_reg & MS_EXIST) {
+
+ if (chip->int_reg & MS_EXIST)
ms_cnt++;
- } else {
+ else
ms_cnt = 0;
- }
+
wait_timeout(30);
}
@@ -677,9 +673,8 @@ int switch_ssc_clock(struct rtsx_chip *chip, int clk)
RTSX_DEBUGP("Switch SSC clock to %dMHz (cur_clk = %d)\n", clk, chip->cur_clk);
- if ((clk <= 2) || (N > max_N)) {
+ if ((clk <= 2) || (N > max_N))
TRACE_RET(chip, STATUS_FAIL);
- }
mcu_cnt = (u8)(125/clk + 3);
if (CHECK_PID(chip, 0x5209)) {
@@ -700,32 +695,29 @@ int switch_ssc_clock(struct rtsx_chip *chip, int clk)
if (chip->ssc_en) {
if (CHECK_PID(chip, 0x5209)) {
if (chip->cur_card == SD_CARD) {
- if (CHK_SD_SDR104(sd_card)) {
+ if (CHK_SD_SDR104(sd_card))
ssc_depth = chip->ssc_depth_sd_sdr104;
- } else if (CHK_SD_SDR50(sd_card)) {
+ else if (CHK_SD_SDR50(sd_card))
ssc_depth = chip->ssc_depth_sd_sdr50;
- } else if (CHK_SD_DDR50(sd_card)) {
+ else if (CHK_SD_DDR50(sd_card))
ssc_depth = double_depth(chip->ssc_depth_sd_ddr50);
- } else if (CHK_SD_HS(sd_card)) {
+ else if (CHK_SD_HS(sd_card))
ssc_depth = double_depth(chip->ssc_depth_sd_hs);
- } else if (CHK_MMC_52M(sd_card) || CHK_MMC_DDR52(sd_card)) {
+ else if (CHK_MMC_52M(sd_card) || CHK_MMC_DDR52(sd_card))
ssc_depth = double_depth(chip->ssc_depth_mmc_52m);
- } else {
+ else
ssc_depth = double_depth(chip->ssc_depth_low_speed);
- }
} else if (chip->cur_card == MS_CARD) {
if (CHK_MSPRO(ms_card)) {
- if (CHK_HG8BIT(ms_card)) {
+ if (CHK_HG8BIT(ms_card))
ssc_depth = double_depth(chip->ssc_depth_ms_hg);
- } else {
+ else
ssc_depth = double_depth(chip->ssc_depth_ms_4bit);
- }
} else {
- if (CHK_MS4BIT(ms_card)) {
+ if (CHK_MS4BIT(ms_card))
ssc_depth = double_depth(chip->ssc_depth_ms_4bit);
- } else {
+ else
ssc_depth = double_depth(chip->ssc_depth_low_speed);
- }
}
} else {
ssc_depth = double_depth(chip->ssc_depth_low_speed);
@@ -733,23 +725,23 @@ int switch_ssc_clock(struct rtsx_chip *chip, int clk)
if (ssc_depth) {
if (div == CLK_DIV_2) {
- if (ssc_depth > 1) {
+ if (ssc_depth > 1)
ssc_depth -= 1;
- } else {
+ else
ssc_depth = SSC_DEPTH_4M;
- }
+
} else if (div == CLK_DIV_4) {
- if (ssc_depth > 2) {
+ if (ssc_depth > 2)
ssc_depth -= 2;
- } else {
+ else
ssc_depth = SSC_DEPTH_4M;
- }
+
} else if (div == CLK_DIV_8) {
- if (ssc_depth > 3) {
+ if (ssc_depth > 3)
ssc_depth -= 3;
- } else {
+ else
ssc_depth = SSC_DEPTH_4M;
- }
+
}
}
} else {
@@ -760,11 +752,10 @@ int switch_ssc_clock(struct rtsx_chip *chip, int clk)
ssc_depth = 0;
}
- if (CHECK_PID(chip, 0x5209)) {
+ if (CHECK_PID(chip, 0x5209))
ssc_depth_mask = SSC_DEPTH_MASK;
- } else {
+ else
ssc_depth_mask = 0x03;
- }
RTSX_DEBUGP("ssc_depth = %d\n", ssc_depth);
@@ -781,9 +772,8 @@ int switch_ssc_clock(struct rtsx_chip *chip, int clk)
}
retval = rtsx_send_cmd(chip, 0, WAIT_TIME);
- if (retval < 0) {
+ if (retval < 0)
TRACE_RET(chip, STATUS_ERROR);
- }
udelay(10);
RTSX_WRITE_REG(chip, CLK_CTL, CLK_LOW_FREQ, 0);
@@ -976,25 +966,23 @@ int card_power_on(struct rtsx_chip *chip, u8 card)
rtsx_init_cmd(chip);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PWR_CTL, mask, val1);
- if (CHECK_PID(chip, 0x5209) && (card == SD_CARD)) {
+ if (CHECK_PID(chip, 0x5209) && (card == SD_CARD))
rtsx_add_cmd(chip, WRITE_REG_CMD, PWR_GATE_CTRL, LDO3318_PWR_MASK, LDO_SUSPEND);
- }
+
retval = rtsx_send_cmd(chip, 0, 100);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
udelay(chip->pmos_pwr_on_interval);
rtsx_init_cmd(chip);
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_PWR_CTL, mask, val2);
- if (CHECK_PID(chip, 0x5209) && (card == SD_CARD)) {
+ if (CHECK_PID(chip, 0x5209) && (card == SD_CARD))
rtsx_add_cmd(chip, WRITE_REG_CMD, PWR_GATE_CTRL, LDO3318_PWR_MASK, LDO_ON);
- }
+
retval = rtsx_send_cmd(chip, 0, 100);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -1016,9 +1004,8 @@ int card_power_off(struct rtsx_chip *chip, u8 card)
}
RTSX_WRITE_REG(chip, CARD_PWR_CTL, mask, val);
- if (CHECK_PID(chip, 0x5209) && (card == SD_CARD)) {
+ if (CHECK_PID(chip, 0x5209) && (card == SD_CARD))
RTSX_WRITE_REG(chip, PWR_GATE_CTRL, LDO3318_PWR_MASK, LDO_OFF);
- }
return STATUS_SUCCESS;
}
@@ -1029,9 +1016,8 @@ int card_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 sec_addr, u16 sec
unsigned int lun = SCSI_LUN(srb);
int i;
- if (chip->rw_card[lun] == NULL) {
+ if (chip->rw_card[lun] == NULL)
TRACE_RET(chip, STATUS_FAIL);
- }
for (i = 0; i < 3; i++) {
chip->rw_need_retry = 0;
@@ -1042,9 +1028,9 @@ int card_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 sec_addr, u16 sec
rtsx_release_chip(chip);
TRACE_RET(chip, STATUS_FAIL);
}
- if (detect_card_cd(chip, chip->cur_card) != STATUS_SUCCESS) {
+ if (detect_card_cd(chip, chip->cur_card) != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
if (!chip->rw_need_retry) {
RTSX_DEBUGP("RW fail, but no need to retry\n");
break;
@@ -1066,26 +1052,26 @@ int card_share_mode(struct rtsx_chip *chip, int card)
if (CHECK_PID(chip, 0x5209) || CHECK_PID(chip, 0x5208)) {
mask = CARD_SHARE_MASK;
- if (card == SD_CARD) {
+ if (card == SD_CARD)
value = CARD_SHARE_48_SD;
- } else if (card == MS_CARD) {
+ else if (card == MS_CARD)
value = CARD_SHARE_48_MS;
- } else if (card == XD_CARD) {
+ else if (card == XD_CARD)
value = CARD_SHARE_48_XD;
- } else {
+ else
TRACE_RET(chip, STATUS_FAIL);
- }
+
} else if (CHECK_PID(chip, 0x5288)) {
mask = 0x03;
- if (card == SD_CARD) {
+ if (card == SD_CARD)
value = CARD_SHARE_BAROSSA_SD;
- } else if (card == MS_CARD) {
+ else if (card == MS_CARD)
value = CARD_SHARE_BAROSSA_MS;
- } else if (card == XD_CARD) {
+ else if (card == XD_CARD)
value = CARD_SHARE_BAROSSA_XD;
- } else {
+ else
TRACE_RET(chip, STATUS_FAIL);
- }
+
} else {
TRACE_RET(chip, STATUS_FAIL);
}
@@ -1103,24 +1089,23 @@ int select_card(struct rtsx_chip *chip, int card)
if (chip->cur_card != card) {
u8 mod;
- if (card == SD_CARD) {
+ if (card == SD_CARD)
mod = SD_MOD_SEL;
- } else if (card == MS_CARD) {
+ else if (card == MS_CARD)
mod = MS_MOD_SEL;
- } else if (card == XD_CARD) {
+ else if (card == XD_CARD)
mod = XD_MOD_SEL;
- } else if (card == SPI_CARD) {
+ else if (card == SPI_CARD)
mod = SPI_MOD_SEL;
- } else {
+ else
TRACE_RET(chip, STATUS_FAIL);
- }
+
RTSX_WRITE_REG(chip, CARD_SELECT, 0x07, mod);
chip->cur_card = card;
retval = card_share_mode(chip, card);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -1137,20 +1122,18 @@ void toggle_gpio(struct rtsx_chip *chip, u8 gpio)
void turn_on_led(struct rtsx_chip *chip, u8 gpio)
{
- if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_PID(chip, 0x5288))
rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), (u8)(1 << gpio));
- } else {
+ else
rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), 0);
- }
}
void turn_off_led(struct rtsx_chip *chip, u8 gpio)
{
- if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_PID(chip, 0x5288))
rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), 0);
- } else {
+ else
rtsx_write_register(chip, CARD_GPIO, (u8)(1 << gpio), (u8)(1 << gpio));
- }
}
int detect_card_cd(struct rtsx_chip *chip, int card)
@@ -1169,67 +1152,60 @@ int detect_card_cd(struct rtsx_chip *chip, int card)
}
status = rtsx_readl(chip, RTSX_BIPR);
- if (!(status & card_cd)) {
+ if (!(status & card_cd))
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
int check_card_exist(struct rtsx_chip *chip, unsigned int lun)
{
- if (chip->card_exist & chip->lun2card[lun]) {
+ if (chip->card_exist & chip->lun2card[lun])
return 1;
- }
return 0;
}
int check_card_ready(struct rtsx_chip *chip, unsigned int lun)
{
- if (chip->card_ready & chip->lun2card[lun]) {
+ if (chip->card_ready & chip->lun2card[lun])
return 1;
- }
return 0;
}
int check_card_wp(struct rtsx_chip *chip, unsigned int lun)
{
- if (chip->card_wp & chip->lun2card[lun]) {
+ if (chip->card_wp & chip->lun2card[lun])
return 1;
- }
return 0;
}
int check_card_fail(struct rtsx_chip *chip, unsigned int lun)
{
- if (chip->card_fail & chip->lun2card[lun]) {
+ if (chip->card_fail & chip->lun2card[lun])
return 1;
- }
return 0;
}
int check_card_ejected(struct rtsx_chip *chip, unsigned int lun)
{
- if (chip->card_ejected & chip->lun2card[lun]) {
+ if (chip->card_ejected & chip->lun2card[lun])
return 1;
- }
return 0;
}
u8 get_lun_card(struct rtsx_chip *chip, unsigned int lun)
{
- if ((chip->card_ready & chip->lun2card[lun]) == XD_CARD) {
+ if ((chip->card_ready & chip->lun2card[lun]) == XD_CARD)
return (u8)XD_CARD;
- } else if ((chip->card_ready & chip->lun2card[lun]) == SD_CARD) {
+ else if ((chip->card_ready & chip->lun2card[lun]) == SD_CARD)
return (u8)SD_CARD;
- } else if ((chip->card_ready & chip->lun2card[lun]) == MS_CARD) {
+ else if ((chip->card_ready & chip->lun2card[lun]) == MS_CARD)
return (u8)MS_CARD;
- }
return 0;
}
diff --git a/drivers/staging/rts_pstor/rtsx_chip.c b/drivers/staging/rts_pstor/rtsx_chip.c
index 5452069fbe08..d8e691b99028 100644
--- a/drivers/staging/rts_pstor/rtsx_chip.c
+++ b/drivers/staging/rts_pstor/rtsx_chip.c
@@ -105,11 +105,10 @@ void rtsx_enable_bus_int(struct rtsx_chip *chip)
reg |= DELINK_INT_EN;
#ifdef SUPPORT_OCP
if (CHECK_PID(chip, 0x5209)) {
- if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
reg |= MS_OC_INT_EN | SD_OC_INT_EN;
- } else {
+ else
reg |= SD_OC_INT_EN;
- }
} else {
reg |= OC_INT_EN;
}
@@ -186,20 +185,20 @@ static int rtsx_pre_handle_sdio_new(struct rtsx_chip *chip)
u8 cd_toggle_mask = 0;
RTSX_READ_REG(chip, TLPTISTAT, &tmp);
- if (CHECK_PID(chip, 0x5209)) {
+ if (CHECK_PID(chip, 0x5209))
cd_toggle_mask = 0x10;
- } else {
+ else
cd_toggle_mask = 0x08;
- }
+
if (tmp & cd_toggle_mask) {
/* Disable sdio_bus_auto_switch */
- if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_PID(chip, 0x5288))
RTSX_WRITE_REG(chip, 0xFE5A, 0x08, 0x00);
- } else if (CHECK_PID(chip, 0x5208)) {
+ else if (CHECK_PID(chip, 0x5208))
RTSX_WRITE_REG(chip, 0xFE70, 0x80, 0x00);
- } else {
+ else
RTSX_WRITE_REG(chip, SDIO_CFG, SDIO_BUS_AUTO_SWITCH, 0);
- }
+
RTSX_WRITE_REG(chip, TLPTISTAT, 0xFF, tmp);
chip->need_reset |= SD_CARD;
@@ -208,16 +207,14 @@ static int rtsx_pre_handle_sdio_new(struct rtsx_chip *chip)
if (chip->asic_code) {
retval = sd_pull_ctl_enable(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
RTSX_WRITE_REG(chip, FPGA_PULL_CTL, FPGA_SD_PULL_CTL_BIT | 0x20, 0);
}
retval = card_share_mode(chip, SD_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
/* Enable sdio_bus_auto_switch */
if (CHECK_PID(chip, 0x5288)) {
@@ -232,11 +229,11 @@ static int rtsx_pre_handle_sdio_new(struct rtsx_chip *chip)
chip->sd_io = 1;
}
} else {
- if (CHECK_PID(chip, 0x5209)) {
+ if (CHECK_PID(chip, 0x5209))
RTSX_WRITE_REG(chip, TLPTISTAT, 0x10, 0x10);
- } else {
+ else
RTSX_WRITE_REG(chip, TLPTISTAT, 0x08, 0x08);
- }
+
chip->need_reset |= SD_CARD;
}
@@ -257,48 +254,47 @@ int rtsx_reset_chip(struct rtsx_chip *chip)
/* optimize PHY */
retval = rtsx_write_phy_register(chip, 0x00, 0xB966);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = rtsx_write_phy_register(chip, 0x01, 0x713F);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = rtsx_write_phy_register(chip, 0x03, 0xA549);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = rtsx_write_phy_register(chip, 0x06, 0xB235);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = rtsx_write_phy_register(chip, 0x07, 0xEF40);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = rtsx_write_phy_register(chip, 0x1E, 0xF8EB);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = rtsx_write_phy_register(chip, 0x19, 0xFE6C);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
wait_timeout(1);
retval = rtsx_write_phy_register(chip, 0x0A, 0x05C0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = rtsx_write_cfg_dw(chip, 1, 0x110, 0xFFFF, 0xFFFF);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = rtsx_read_phy_register(chip, 0x08, &val);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
RTSX_DEBUGP("Read from phy 0x08: 0x%04x\n", val);
if (chip->phy_voltage) {
@@ -308,9 +304,9 @@ int rtsx_reset_chip(struct rtsx_chip *chip)
val |= chip->phy_voltage;
RTSX_DEBUGP("Write to phy 0x08: 0x%04x\n", val);
retval = rtsx_write_phy_register(chip, 0x08, val);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
} else {
chip->phy_voltage = (u8)(val & 0x3F);
RTSX_DEBUGP("Default, chip->phy_voltage = 0x%x\n", chip->phy_voltage);
@@ -324,11 +320,11 @@ int rtsx_reset_chip(struct rtsx_chip *chip)
#ifdef SUPPORT_OCP
/* SSC power on, OCD power on */
- if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
RTSX_WRITE_REG(chip, FPDCTL, OC_POWER_DOWN, 0);
- } else {
+ else
RTSX_WRITE_REG(chip, FPDCTL, OC_POWER_DOWN, MS_OC_POWER_DOWN);
- }
+
if (CHECK_PID(chip, 0x5209)) {
RTSX_WRITE_REG(chip, OCPPARA1, SD_OCP_TIME_MASK | MS_OCP_TIME_MASK,
SD_OCP_TIME_800 | MS_OCP_TIME_800);
@@ -352,9 +348,8 @@ int rtsx_reset_chip(struct rtsx_chip *chip)
RTSX_WRITE_REG(chip, FPDCTL, OC_POWER_DOWN, OC_POWER_DOWN);
#endif
- if (!CHECK_PID(chip, 0x5288)) {
+ if (!CHECK_PID(chip, 0x5288))
RTSX_WRITE_REG(chip, CARD_GPIO_DIR, 0xFF, 0x03);
- }
/* Turn off LED */
RTSX_WRITE_REG(chip, CARD_GPIO, 0xFF, 0x03);
@@ -364,9 +359,8 @@ int rtsx_reset_chip(struct rtsx_chip *chip)
/* Card driving select */
RTSX_WRITE_REG(chip, CARD_DRIVE_SEL, 0xFF, chip->card_drive_sel);
- if (CHECK_PID(chip, 0x5209)) {
+ if (CHECK_PID(chip, 0x5209))
RTSX_WRITE_REG(chip, SD30_DRIVE_SEL, 0x07, chip->sd30_drive_sel_3v3);
- }
#ifdef LED_AUTO_BLINK
RTSX_WRITE_REG(chip, CARD_AUTO_BLINK, 0xFF,
@@ -394,36 +388,34 @@ int rtsx_reset_chip(struct rtsx_chip *chip)
if (CHK_SDIO_EXIST(chip)) {
if (CHECK_PID(chip, 0x5209)) {
retval = rtsx_write_cfg_dw(chip, 1, 0xC0, 0xFF, chip->aspm_l0s_l1_en);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
} else if (CHECK_PID(chip, 0x5288)) {
retval = rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFF, chip->aspm_l0s_l1_en);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
}
} else {
- if (CHECK_PID(chip, 0x5208)) {
+ if (CHECK_PID(chip, 0x5208))
RTSX_WRITE_REG(chip, ASPM_FORCE_CTL, 0xFF, 0x3F);
- }
retval = rtsx_write_config_byte(chip, LCTLR, chip->aspm_l0s_l1_en);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
chip->aspm_level[0] = chip->aspm_l0s_l1_en;
if (CHK_SDIO_EXIST(chip)) {
chip->aspm_level[1] = chip->aspm_l0s_l1_en;
- if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_PID(chip, 0x5288))
retval = rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFF, chip->aspm_l0s_l1_en);
- } else {
+ else
retval = rtsx_write_cfg_dw(chip, 1, 0xC0, 0xFF, chip->aspm_l0s_l1_en);
- }
- if (retval != STATUS_SUCCESS) {
+
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
}
chip->aspm_enabled = 1;
@@ -431,49 +423,45 @@ int rtsx_reset_chip(struct rtsx_chip *chip)
} else {
if (chip->asic_code && CHECK_PID(chip, 0x5208)) {
retval = rtsx_write_phy_register(chip, 0x07, 0x0129);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
retval = rtsx_write_config_byte(chip, LCTLR, chip->aspm_l0s_l1_en);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
retval = rtsx_write_config_byte(chip, 0x81, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (CHK_SDIO_EXIST(chip)) {
- if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_PID(chip, 0x5288))
retval = rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFF00, 0x0100);
- } else {
+ else
retval = rtsx_write_cfg_dw(chip, 1, 0xC0, 0xFF00, 0x0100);
- }
- if (retval != STATUS_SUCCESS) {
+
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
}
if (CHECK_PID(chip, 0x5209)) {
retval = rtsx_write_cfg_dw(chip, 0, 0x70C, 0xFF000000, 0x5B);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
if (CHECK_PID(chip, 0x5288)) {
if (!CHK_SDIO_EXIST(chip)) {
retval = rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFFFF, 0x0103);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = rtsx_write_cfg_dw(chip, 2, 0x84, 0xFF, 0x03);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
}
}
@@ -499,29 +487,29 @@ int rtsx_reset_chip(struct rtsx_chip *chip)
if (chip->ic_version >= IC_VER_D) {
u16 reg;
retval = rtsx_read_phy_register(chip, 0x00, &reg);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
reg &= 0xFE7F;
reg |= 0x80;
retval = rtsx_write_phy_register(chip, 0x00, reg);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = rtsx_read_phy_register(chip, 0x1C, &reg);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
reg &= 0xFFF7;
retval = rtsx_write_phy_register(chip, 0x1C, reg);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
}
- if (chip->driver_first_load && (chip->ic_version < IC_VER_C)) {
+ if (chip->driver_first_load && (chip->ic_version < IC_VER_C))
rtsx_calibration(chip);
- }
+
} else {
rtsx_enable_bus_int(chip);
}
@@ -550,18 +538,18 @@ int rtsx_reset_chip(struct rtsx_chip *chip)
RTSX_DEBUGP("In rtsx_reset_chip, chip->int_reg = 0x%x\n", chip->int_reg);
if (chip->int_reg & SD_EXIST) {
#ifdef HW_AUTO_SWITCH_SD_BUS
- if (CHECK_PID(chip, 0x5208) && (chip->ic_version < IC_VER_C)) {
+ if (CHECK_PID(chip, 0x5208) && (chip->ic_version < IC_VER_C))
retval = rtsx_pre_handle_sdio_old(chip);
- } else {
+ else
retval = rtsx_pre_handle_sdio_new(chip);
- }
+
RTSX_DEBUGP("chip->need_reset = 0x%x (rtsx_reset_chip)\n", (unsigned int)(chip->need_reset));
#else /* HW_AUTO_SWITCH_SD_BUS */
retval = rtsx_pre_handle_sdio_old(chip);
#endif /* HW_AUTO_SWITCH_SD_BUS */
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
} else {
chip->sd_io = 0;
RTSX_WRITE_REG(chip, SDIO_CTRL, SDIO_BUS_CTRL | SDIO_CD_CTRL, 0);
@@ -572,9 +560,8 @@ NextCard:
chip->need_reset |= XD_CARD;
if (chip->int_reg & MS_EXIST)
chip->need_reset |= MS_CARD;
- if (chip->int_reg & CARD_EXIST) {
+ if (chip->int_reg & CARD_EXIST)
RTSX_WRITE_REG(chip, SSC_CTL1, SSC_RSTB, SSC_RSTB);
- }
RTSX_DEBUGP("In rtsx_init_chip, chip->need_reset = 0x%x\n", (unsigned int)(chip->need_reset));
@@ -587,9 +574,8 @@ NextCard:
if (chip->remote_wakeup_en && !chip->auto_delink_en) {
RTSX_WRITE_REG(chip, WAKE_SEL_CTL, 0x07, 0x07);
- if (chip->aux_pwr_exist) {
+ if (chip->aux_pwr_exist)
RTSX_WRITE_REG(chip, PME_FORCE_CTL, 0xFF, 0x33);
- }
} else {
RTSX_WRITE_REG(chip, WAKE_SEL_CTL, 0x07, 0x04);
RTSX_WRITE_REG(chip, PME_FORCE_CTL, 0xFF, 0x30);
@@ -598,18 +584,16 @@ NextCard:
if (CHECK_PID(chip, 0x5208) && (chip->ic_version >= IC_VER_D)) {
RTSX_WRITE_REG(chip, PETXCFG, 0x1C, 0x14);
} else if (CHECK_PID(chip, 0x5209)) {
- if (chip->force_clkreq_0) {
+ if (chip->force_clkreq_0)
RTSX_WRITE_REG(chip, PETXCFG, 0x08, 0x08);
- } else {
+ else
RTSX_WRITE_REG(chip, PETXCFG, 0x08, 0x00);
- }
}
if (chip->asic_code && CHECK_PID(chip, 0x5208)) {
retval = rtsx_clr_phy_reg_bit(chip, 0x1C, 2);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
if (chip->ft2_fast_mode) {
@@ -665,11 +649,10 @@ static int rts5209_init(struct rtsx_chip *chip)
u8 val = 0;
val = rtsx_readb(chip, 0x1C);
- if ((val & 0x10) == 0) {
+ if ((val & 0x10) == 0)
chip->asic_code = 1;
- } else {
+ else
chip->asic_code = 0;
- }
chip->ic_version = val & 0x0F;
chip->phy_debug_mode = 0;
@@ -679,9 +662,9 @@ static int rts5209_init(struct rtsx_chip *chip)
chip->ms_power_class_en = 0x03;
retval = rtsx_read_cfg_dw(chip, 0, 0x724, &lval);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
RTSX_DEBUGP("dw in 0x724: 0x%x\n", lval);
val = (u8)lval;
if (!(val & 0x80)) {
@@ -690,17 +673,16 @@ static int rts5209_init(struct rtsx_chip *chip)
else
chip->lun_mode = SD_MS_2LUN;
- if (val & 0x04) {
+ if (val & 0x04)
SET_SDIO_EXIST(chip);
- } else {
+ else
CLR_SDIO_EXIST(chip);
- }
- if (val & 0x02) {
+ if (val & 0x02)
chip->hw_bypass_sd = 0;
- } else {
+ else
chip->hw_bypass_sd = 1;
- }
+
} else {
SET_SDIO_EXIST(chip);
chip->hw_bypass_sd = 0;
@@ -714,24 +696,21 @@ static int rts5209_init(struct rtsx_chip *chip)
val = (u8)(lval >> 8);
clk = (val >> 5) & 0x07;
- if (clk != 0x07) {
+ if (clk != 0x07)
chip->asic_sd_sdr50_clk = 98 - clk * 2;
- }
- if (val & 0x10) {
+ if (val & 0x10)
chip->auto_delink_en = 1;
- } else {
+ else
chip->auto_delink_en = 0;
- }
if (chip->ss_en == 2) {
chip->ss_en = 0;
} else {
- if (val & 0x08) {
+ if (val & 0x08)
chip->ss_en = 1;
- } else {
+ else
chip->ss_en = 0;
- }
}
clk = val & 0x07;
@@ -750,21 +729,21 @@ static int rts5209_init(struct rtsx_chip *chip)
if (clk != 0x03)
chip->asic_sd_ddr50_clk = (48 - clk * 2) * 2;
- if (val & 0x01) {
+ if (val & 0x01)
chip->sdr104_en = 1;
- } else {
+ else
chip->sdr104_en = 0;
- }
- if (val & 0x02) {
+
+ if (val & 0x02)
chip->ddr50_en = 1;
- } else {
+ else
chip->ddr50_en = 0;
- }
- if (val & 0x04) {
+
+ if (val & 0x04)
chip->sdr50_en = 1;
- } else {
+ else
chip->sdr50_en = 0;
- }
+
val = (u8)(lval >> 24);
@@ -772,11 +751,10 @@ static int rts5209_init(struct rtsx_chip *chip)
if (clk != 0x07)
chip->asic_sd_sdr104_clk = 206 - clk * 3;
- if (val & 0x10) {
+ if (val & 0x10)
chip->power_down_in_ss = 1;
- } else {
+ else
chip->power_down_in_ss = 0;
- }
chip->ms_power_class_en = val & 0x03;
}
@@ -786,20 +764,19 @@ static int rts5209_init(struct rtsx_chip *chip)
retval = rtsx_read_pci_cfg_byte(0x00,
0x1C, 0x02, 0x58, &reg58);
- if (retval < 0) {
+ if (retval < 0)
return STATUS_SUCCESS;
- }
+
retval = rtsx_read_pci_cfg_byte(0x00,
0x1C, 0x02, 0x5B, &reg5b);
- if (retval < 0) {
+ if (retval < 0)
return STATUS_SUCCESS;
- }
RTSX_DEBUGP("reg58 = 0x%x, reg5b = 0x%x\n", reg58, reg5b);
- if ((reg58 == 0x00) && (reg5b == 0x01)) {
+ if ((reg58 == 0x00) && (reg5b == 0x01))
chip->auto_delink_en = 0;
- }
+
}
return STATUS_SUCCESS;
@@ -813,24 +790,23 @@ static int rts5208_init(struct rtsx_chip *chip)
RTSX_WRITE_REG(chip, CLK_SEL, 0x03, 0x03);
RTSX_READ_REG(chip, CLK_SEL, &val);
- if (val == 0) {
+ if (val == 0)
chip->asic_code = 1;
- } else {
+ else
chip->asic_code = 0;
- }
if (chip->asic_code) {
retval = rtsx_read_phy_register(chip, 0x1C, &reg);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
RTSX_DEBUGP("Value of phy register 0x1C is 0x%x\n", reg);
chip->ic_version = (reg >> 4) & 0x07;
- if (reg & PHY_DEBUG_MODE) {
+ if (reg & PHY_DEBUG_MODE)
chip->phy_debug_mode = 1;
- } else {
+ else
chip->phy_debug_mode = 0;
- }
+
} else {
RTSX_READ_REG(chip, 0xFE80, &val);
chip->ic_version = val;
@@ -839,33 +815,29 @@ static int rts5208_init(struct rtsx_chip *chip)
RTSX_READ_REG(chip, PDINFO, &val);
RTSX_DEBUGP("PDINFO: 0x%x\n", val);
- if (val & AUX_PWR_DETECTED) {
+ if (val & AUX_PWR_DETECTED)
chip->aux_pwr_exist = 1;
- } else {
+ else
chip->aux_pwr_exist = 0;
- }
RTSX_READ_REG(chip, 0xFE50, &val);
- if (val & 0x01) {
+ if (val & 0x01)
chip->hw_bypass_sd = 1;
- } else {
+ else
chip->hw_bypass_sd = 0;
- }
rtsx_read_config_byte(chip, 0x0E, &val);
- if (val & 0x80) {
+ if (val & 0x80)
SET_SDIO_EXIST(chip);
- } else {
+ else
CLR_SDIO_EXIST(chip);
- }
if (chip->use_hw_setting) {
RTSX_READ_REG(chip, CHANGE_LINK_STATE, &val);
- if (val & 0x80) {
+ if (val & 0x80)
chip->auto_delink_en = 1;
- } else {
+ else
chip->auto_delink_en = 0;
- }
}
return STATUS_SUCCESS;
@@ -879,63 +851,57 @@ static int rts5288_init(struct rtsx_chip *chip)
RTSX_WRITE_REG(chip, CLK_SEL, 0x03, 0x03);
RTSX_READ_REG(chip, CLK_SEL, &val);
- if (val == 0) {
+ if (val == 0)
chip->asic_code = 1;
- } else {
+ else
chip->asic_code = 0;
- }
chip->ic_version = 0;
chip->phy_debug_mode = 0;
RTSX_READ_REG(chip, PDINFO, &val);
RTSX_DEBUGP("PDINFO: 0x%x\n", val);
- if (val & AUX_PWR_DETECTED) {
+ if (val & AUX_PWR_DETECTED)
chip->aux_pwr_exist = 1;
- } else {
+ else
chip->aux_pwr_exist = 0;
- }
RTSX_READ_REG(chip, CARD_SHARE_MODE, &val);
RTSX_DEBUGP("CARD_SHARE_MODE: 0x%x\n", val);
- if (val & 0x04) {
+ if (val & 0x04)
chip->baro_pkg = QFN;
- } else {
+ else
chip->baro_pkg = LQFP;
- }
RTSX_READ_REG(chip, 0xFE5A, &val);
- if (val & 0x10) {
+ if (val & 0x10)
chip->hw_bypass_sd = 1;
- } else {
+ else
chip->hw_bypass_sd = 0;
- }
retval = rtsx_read_cfg_dw(chip, 0, 0x718, &lval);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
max_func = (u8)((lval >> 29) & 0x07);
RTSX_DEBUGP("Max function number: %d\n", max_func);
- if (max_func == 0x02) {
+ if (max_func == 0x02)
SET_SDIO_EXIST(chip);
- } else {
+ else
CLR_SDIO_EXIST(chip);
- }
if (chip->use_hw_setting) {
RTSX_READ_REG(chip, CHANGE_LINK_STATE, &val);
- if (val & 0x80) {
+ if (val & 0x80)
chip->auto_delink_en = 1;
- } else {
+ else
chip->auto_delink_en = 0;
- }
- if (CHECK_BARO_PKG(chip, LQFP)) {
+ if (CHECK_BARO_PKG(chip, LQFP))
chip->lun_mode = SD_MS_1LUN;
- } else {
+ else
chip->lun_mode = DEFAULT_SINGLE;
- }
+
}
return STATUS_SUCCESS;
@@ -990,22 +956,21 @@ int rtsx_init_chip(struct rtsx_chip *chip)
chip->rw_fail_cnt[i] = 0;
}
- if (!check_sd_speed_prior(chip->sd_speed_prior)) {
+ if (!check_sd_speed_prior(chip->sd_speed_prior))
chip->sd_speed_prior = 0x01040203;
- }
+
RTSX_DEBUGP("sd_speed_prior = 0x%08x\n", chip->sd_speed_prior);
- if (!check_sd_current_prior(chip->sd_current_prior)) {
+ if (!check_sd_current_prior(chip->sd_current_prior))
chip->sd_current_prior = 0x00010203;
- }
+
RTSX_DEBUGP("sd_current_prior = 0x%08x\n", chip->sd_current_prior);
- if ((chip->sd_ddr_tx_phase > 31) || (chip->sd_ddr_tx_phase < 0)) {
+ if ((chip->sd_ddr_tx_phase > 31) || (chip->sd_ddr_tx_phase < 0))
chip->sd_ddr_tx_phase = 0;
- }
- if ((chip->mmc_ddr_tx_phase > 31) || (chip->mmc_ddr_tx_phase < 0)) {
+
+ if ((chip->mmc_ddr_tx_phase > 31) || (chip->mmc_ddr_tx_phase < 0))
chip->mmc_ddr_tx_phase = 0;
- }
RTSX_WRITE_REG(chip, FPDCTL, SSC_POWER_DOWN, 0);
wait_timeout(200);
@@ -1014,24 +979,23 @@ int rtsx_init_chip(struct rtsx_chip *chip)
if (CHECK_PID(chip, 0x5209)) {
retval = rts5209_init(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
} else if (CHECK_PID(chip, 0x5208)) {
retval = rts5208_init(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
} else if (CHECK_PID(chip, 0x5288)) {
retval = rts5288_init(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
}
- if (chip->ss_en == 2) {
+ if (chip->ss_en == 2)
chip->ss_en = 0;
- }
RTSX_DEBUGP("chip->asic_code = %d\n", chip->asic_code);
RTSX_DEBUGP("chip->ic_version = 0x%x\n", chip->ic_version);
@@ -1068,9 +1032,8 @@ int rtsx_init_chip(struct rtsx_chip *chip)
}
retval = rtsx_reset_chip(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -1118,19 +1081,19 @@ static void rtsx_monitor_aspm_config(struct rtsx_chip *chip)
chip->aspm_level[1] = reg1;
}
- if ((reg0 & 0x03) && (reg1 & 0x03)) {
+ if ((reg0 & 0x03) && (reg1 & 0x03))
maybe_support_aspm = 1;
- }
+
} else {
- if (reg0 & 0x03) {
+ if (reg0 & 0x03)
maybe_support_aspm = 1;
- }
+
}
if (reg_changed) {
- if (maybe_support_aspm) {
+ if (maybe_support_aspm)
chip->aspm_l0s_l1_en = 0x03;
- }
+
RTSX_DEBUGP("aspm_level[0] = 0x%02x, aspm_level[1] = 0x%02x\n",
chip->aspm_level[0], chip->aspm_level[1]);
@@ -1177,13 +1140,13 @@ void rtsx_polling_func(struct rtsx_chip *chip)
if (chip->ocp_int & MS_OC_INT)
ms_power_off_card3v3(chip);
} else {
- if (chip->card_exist & SD_CARD) {
+ if (chip->card_exist & SD_CARD)
sd_power_off_card3v3(chip);
- } else if (chip->card_exist & MS_CARD) {
+ else if (chip->card_exist & MS_CARD)
ms_power_off_card3v3(chip);
- } else if (chip->card_exist & XD_CARD) {
+ else if (chip->card_exist & XD_CARD)
xd_power_off_card3v3(chip);
- }
+
}
chip->ocp_int = 0;
@@ -1226,9 +1189,9 @@ void rtsx_polling_func(struct rtsx_chip *chip)
if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) {
u32 val;
rtsx_read_cfg_dw(chip, 1, 0x04, &val);
- if (val & 0x07) {
+ if (val & 0x07)
ss_allowed = 0;
- }
+
}
}
} else {
@@ -1284,9 +1247,9 @@ void rtsx_polling_func(struct rtsx_chip *chip)
turn_off_led(chip, LED_GPIO);
- if (chip->auto_power_down && !chip->card_ready && !chip->sd_io) {
+ if (chip->auto_power_down && !chip->card_ready && !chip->sd_io)
rtsx_force_power_down(chip, SSC_PDCTL | OC_PDCTL);
- }
+
}
}
@@ -1299,9 +1262,9 @@ void rtsx_polling_func(struct rtsx_chip *chip)
break;
case RTSX_STAT_IDLE:
- if (chip->sd_io && !chip->sd_int) {
+ if (chip->sd_io && !chip->sd_int)
try_to_switch_sdio_ctrl(chip);
- }
+
rtsx_enable_aspm(chip);
break;
@@ -1313,9 +1276,8 @@ void rtsx_polling_func(struct rtsx_chip *chip)
#ifdef SUPPORT_OCP
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
#ifdef CONFIG_RTS_PSTOR_DEBUG
- if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER | MS_OC_NOW | MS_OC_EVER)) {
+ if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER | MS_OC_NOW | MS_OC_EVER))
RTSX_DEBUGP("Over current, OCPSTAT is 0x%x\n", chip->ocp_stat);
- }
#endif
if (chip->ocp_stat & (SD_OC_NOW | SD_OC_EVER)) {
@@ -1362,28 +1324,27 @@ Delink_Stage:
if (chip->auto_delink_cnt == delink_stage1_cnt) {
rtsx_set_stat(chip, RTSX_STAT_DELINK);
- if (chip->asic_code && CHECK_PID(chip, 0x5208)) {
+ if (chip->asic_code && CHECK_PID(chip, 0x5208))
rtsx_set_phy_reg_bit(chip, 0x1C, 2);
- }
+
if (chip->card_exist) {
RTSX_DEBUGP("False card inserted, do force delink\n");
- if (enter_L1) {
+ if (enter_L1)
rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, 1);
- }
+
rtsx_write_register(chip, CHANGE_LINK_STATE, 0x0A, 0x0A);
- if (enter_L1) {
+ if (enter_L1)
rtsx_enter_L1(chip);
- }
chip->auto_delink_cnt = delink_stage3_cnt + 1;
} else {
RTSX_DEBUGP("No card inserted, do delink\n");
- if (enter_L1) {
+ if (enter_L1)
rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, 1);
- }
+
#ifdef HW_INT_WRITE_CLR
if (CHECK_PID(chip, 0x5209)) {
rtsx_writel(chip, RTSX_BIPR, 0xFFFFFFFF);
@@ -1392,22 +1353,21 @@ Delink_Stage:
#endif
rtsx_write_register(chip, CHANGE_LINK_STATE, 0x02, 0x02);
- if (enter_L1) {
+ if (enter_L1)
rtsx_enter_L1(chip);
- }
+
}
}
if (chip->auto_delink_cnt == delink_stage2_cnt) {
RTSX_DEBUGP("Try to do force delink\n");
- if (enter_L1) {
+ if (enter_L1)
rtsx_exit_L1(chip);
- }
- if (chip->asic_code && CHECK_PID(chip, 0x5208)) {
+ if (chip->asic_code && CHECK_PID(chip, 0x5208))
rtsx_set_phy_reg_bit(chip, 0x1C, 2);
- }
+
rtsx_write_register(chip, CHANGE_LINK_STATE, 0x0A, 0x0A);
}
@@ -1472,9 +1432,9 @@ int rtsx_write_register(struct rtsx_chip *chip, u16 addr, u8 mask, u8 data)
for (i = 0; i < MAX_RW_REG_CNT; i++) {
val = rtsx_readl(chip, RTSX_HAIMR);
if ((val & (1 << 31)) == 0) {
- if (data != (u8)val) {
+ if (data != (u8)val)
TRACE_RET(chip, STATUS_FAIL);
- }
+
return STATUS_SUCCESS;
}
}
@@ -1487,9 +1447,8 @@ int rtsx_read_register(struct rtsx_chip *chip, u16 addr, u8 *data)
u32 val = 2 << 30;
int i;
- if (data) {
+ if (data)
*data = 0;
- }
val |= (u32)(addr & 0x3FFF) << 16;
@@ -1497,18 +1456,15 @@ int rtsx_read_register(struct rtsx_chip *chip, u16 addr, u8 *data)
for (i = 0; i < MAX_RW_REG_CNT; i++) {
val = rtsx_readl(chip, RTSX_HAIMR);
- if ((val & (1 << 31)) == 0) {
+ if ((val & (1 << 31)) == 0)
break;
- }
}
- if (i >= MAX_RW_REG_CNT) {
+ if (i >= MAX_RW_REG_CNT)
TRACE_RET(chip, STATUS_TIMEDOUT);
- }
- if (data) {
+ if (data)
*data = (u8)(val & 0xFF);
- }
return STATUS_SUCCESS;
}
@@ -1537,9 +1493,8 @@ int rtsx_write_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 mask, u3
for (i = 0; i < MAX_RW_REG_CNT; i++) {
RTSX_READ_REG(chip, CFGRWCTL, &tmp);
- if ((tmp & 0x80) == 0) {
+ if ((tmp & 0x80) == 0)
break;
- }
}
}
@@ -1558,9 +1513,8 @@ int rtsx_read_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 *val)
for (i = 0; i < MAX_RW_REG_CNT; i++) {
RTSX_READ_REG(chip, CFGRWCTL, &tmp);
- if ((tmp & 0x80) == 0) {
+ if ((tmp & 0x80) == 0)
break;
- }
}
for (i = 0; i < 4; i++) {
@@ -1568,9 +1522,8 @@ int rtsx_read_cfg_dw(struct rtsx_chip *chip, u8 func_no, u16 addr, u32 *val)
data |= (u32)tmp << (i * 8);
}
- if (val) {
+ if (val)
*val = data;
- }
return STATUS_SUCCESS;
}
@@ -1585,21 +1538,19 @@ int rtsx_write_cfg_seq(struct rtsx_chip *chip, u8 func, u16 addr, u8 *buf, int l
RTSX_DEBUGP("%s\n", __func__);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, STATUS_NOMEM);
- }
- if ((len + offset) % 4) {
+ if ((len + offset) % 4)
dw_len = (len + offset) / 4 + 1;
- } else {
+ else
dw_len = (len + offset) / 4;
- }
+
RTSX_DEBUGP("dw_len = %d\n", dw_len);
data = vzalloc(dw_len * 4);
- if (!data) {
+ if (!data)
TRACE_RET(chip, STATUS_NOMEM);
- }
mask = vzalloc(dw_len * 4);
if (!mask) {
@@ -1645,17 +1596,16 @@ int rtsx_read_cfg_seq(struct rtsx_chip *chip, u8 func, u16 addr, u8 *buf, int le
RTSX_DEBUGP("%s\n", __func__);
- if ((len + offset) % 4) {
+ if ((len + offset) % 4)
dw_len = (len + offset) / 4 + 1;
- } else {
+ else
dw_len = (len + offset) / 4;
- }
+
RTSX_DEBUGP("dw_len = %d\n", dw_len);
data = (u32 *)vmalloc(dw_len * 4);
- if (!data) {
+ if (!data)
TRACE_RET(chip, STATUS_NOMEM);
- }
for (i = 0; i < dw_len; i++) {
retval = rtsx_read_cfg_dw(chip, func, aligned_addr + i * 4, data + i);
@@ -1700,9 +1650,8 @@ int rtsx_write_phy_register(struct rtsx_chip *chip, u8 addr, u16 val)
}
}
- if (!finished) {
+ if (!finished)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -1724,9 +1673,8 @@ int rtsx_read_phy_register(struct rtsx_chip *chip, u8 addr, u16 *val)
}
}
- if (!finished) {
+ if (!finished)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_READ_REG(chip, PHYDATA0, &tmp);
data = tmp;
@@ -1753,9 +1701,8 @@ int rtsx_read_efuse(struct rtsx_chip *chip, u8 addr, u8 *val)
udelay(1);
}
- if (data & 0x80) {
+ if (data & 0x80)
TRACE_RET(chip, STATUS_TIMEDOUT);
- }
RTSX_READ_REG(chip, EFUSE_DATA, &data);
if (val)
@@ -1786,9 +1733,8 @@ int rtsx_write_efuse(struct rtsx_chip *chip, u8 addr, u8 val)
wait_timeout(3);
}
- if (data & 0x80) {
+ if (data & 0x80)
TRACE_RET(chip, STATUS_TIMEDOUT);
- }
wait_timeout(5);
}
@@ -1802,15 +1748,14 @@ int rtsx_clr_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit)
u16 value;
retval = rtsx_read_phy_register(chip, reg, &value);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
if (value & (1 << bit)) {
value &= ~(1 << bit);
retval = rtsx_write_phy_register(chip, reg, value);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -1822,15 +1767,14 @@ int rtsx_set_phy_reg_bit(struct rtsx_chip *chip, u8 reg, u8 bit)
u16 value;
retval = rtsx_read_phy_register(chip, reg, &value);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
if (0 == (value & (1 << bit))) {
value |= (1 << bit);
retval = rtsx_write_phy_register(chip, reg, value);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -1861,11 +1805,11 @@ static void rtsx_handle_pm_dstate(struct rtsx_chip *chip, u8 dstate)
if (CHK_SDIO_EXIST(chip)) {
u8 func_no;
- if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_PID(chip, 0x5288))
func_no = 2;
- } else {
+ else
func_no = 1;
- }
+
rtsx_read_cfg_dw(chip, func_no, 0x84, &ultmp);
RTSX_DEBUGP("pm_dstate of function %d: 0x%x\n", (int)func_no, ultmp);
rtsx_write_cfg_dw(chip, func_no, 0x84, 0xFF, dstate);
@@ -1898,11 +1842,10 @@ void rtsx_enter_ss(struct rtsx_chip *chip)
}
if (CHK_SDIO_EXIST(chip)) {
- if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_PID(chip, 0x5288))
rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFF00, 0x0100);
- } else {
+ else
rtsx_write_cfg_dw(chip, 1, 0xC0, 0xFF00, 0x0100);
- }
}
if (chip->auto_delink_en) {
@@ -1953,11 +1896,11 @@ int rtsx_pre_handle_interrupt(struct rtsx_chip *chip)
u32 ocp_int = 0;
if (CHECK_PID(chip, 0x5209)) {
- if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
ocp_int = MS_OC_INT | SD_OC_INT;
- } else {
+ else
ocp_int = SD_OC_INT;
- }
+
} else {
ocp_int = OC_INT;
}
@@ -1976,9 +1919,8 @@ int rtsx_pre_handle_interrupt(struct rtsx_chip *chip)
chip->int_reg = rtsx_readl(chip, RTSX_BIPR);
#ifdef HW_INT_WRITE_CLR
- if (CHECK_PID(chip, 0x5209)) {
+ if (CHECK_PID(chip, 0x5209))
rtsx_writel(chip, RTSX_BIPR, chip->int_reg);
- }
#endif
if (((chip->int_reg & int_enable) == 0) || (chip->int_reg == 0xFFFFFFFF))
@@ -1988,9 +1930,8 @@ int rtsx_pre_handle_interrupt(struct rtsx_chip *chip)
if (CHECK_PID(chip, 0x5209)) {
u8 val;
rtsx_read_config_byte(chip, 0x05, &val);
- if (val & 0x04) {
+ if (val & 0x04)
return STATUS_FAIL;
- }
}
}
@@ -2107,16 +2048,15 @@ void rtsx_do_before_power_down(struct rtsx_chip *chip, int pm_stat)
RTSX_DEBUGP("Host enter S1\n");
rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, HOST_ENTER_S1);
} else if (pm_stat == PM_S3) {
- if (chip->s3_pwr_off_delay > 0) {
+ if (chip->s3_pwr_off_delay > 0)
wait_timeout(chip->s3_pwr_off_delay);
- }
+
RTSX_DEBUGP("Host enter S3\n");
rtsx_write_register(chip, HOST_SLEEP_STATE, 0x03, HOST_ENTER_S3);
}
- if (chip->do_delink_before_power_down && chip->auto_delink_en) {
+ if (chip->do_delink_before_power_down && chip->auto_delink_en)
rtsx_write_register(chip, CHANGE_LINK_STATE, 0x02, 2);
- }
rtsx_force_power_down(chip, SSC_PDCTL | OC_PDCTL);
@@ -2143,11 +2083,10 @@ void rtsx_enable_aspm(struct rtsx_chip *chip)
if (CHK_SDIO_EXIST(chip)) {
u16 val = chip->aspm_l0s_l1_en | 0x0100;
- if (CHECK_PID(chip, 0x5288)) {
+ if (CHECK_PID(chip, 0x5288))
rtsx_write_cfg_dw(chip, 2, 0xC0, 0xFFFF, val);
- } else {
+ else
rtsx_write_cfg_dw(chip, 1, 0xC0, 0xFFFF, val);
- }
}
}
}
@@ -2167,11 +2106,11 @@ void rtsx_disable_aspm(struct rtsx_chip *chip)
if (chip->asic_code && CHECK_PID(chip, 0x5208))
rtsx_write_phy_register(chip, 0x07, 0x0129);
- if (CHECK_PID(chip, 0x5208)) {
+ if (CHECK_PID(chip, 0x5208))
rtsx_write_register(chip, ASPM_FORCE_CTL, 0xF3, 0x30);
- } else {
+ else
rtsx_write_config_byte(chip, LCTLR, 0x00);
- }
+
wait_timeout(1);
}
}
@@ -2186,9 +2125,8 @@ int rtsx_read_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len)
u16 reg_addr;
u8 *ptr;
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, STATUS_ERROR);
- }
ptr = buf;
reg_addr = PPBUF_BASE2;
@@ -2199,9 +2137,8 @@ int rtsx_read_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len)
rtsx_add_cmd(chip, READ_REG_CMD, reg_addr++, 0, 0);
retval = rtsx_send_cmd(chip, 0, 250);
- if (retval < 0) {
+ if (retval < 0)
TRACE_RET(chip, STATUS_FAIL);
- }
memcpy(ptr, rtsx_get_cmd_data(chip), 256);
ptr += 256;
@@ -2214,9 +2151,8 @@ int rtsx_read_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len)
rtsx_add_cmd(chip, READ_REG_CMD, reg_addr++, 0, 0);
retval = rtsx_send_cmd(chip, 0, 250);
- if (retval < 0) {
+ if (retval < 0)
TRACE_RET(chip, STATUS_FAIL);
- }
}
memcpy(ptr, rtsx_get_cmd_data(chip), buf_len%256);
@@ -2231,9 +2167,8 @@ int rtsx_write_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len)
u16 reg_addr;
u8 *ptr;
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, STATUS_ERROR);
- }
ptr = buf;
reg_addr = PPBUF_BASE2;
@@ -2246,9 +2181,8 @@ int rtsx_write_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len)
}
retval = rtsx_send_cmd(chip, 0, 250);
- if (retval < 0) {
+ if (retval < 0)
TRACE_RET(chip, STATUS_FAIL);
- }
}
if (buf_len%256) {
@@ -2260,9 +2194,8 @@ int rtsx_write_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len)
}
retval = rtsx_send_cmd(chip, 0, 250);
- if (retval < 0) {
+ if (retval < 0)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -2270,9 +2203,8 @@ int rtsx_write_ppbuf(struct rtsx_chip *chip, u8 *buf, int buf_len)
int rtsx_check_chip_exist(struct rtsx_chip *chip)
{
- if (rtsx_readl(chip, 0) == 0xFFFFFFFF) {
+ if (rtsx_readl(chip, 0) == 0xFFFFFFFF)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -2288,17 +2220,15 @@ int rtsx_force_power_on(struct rtsx_chip *chip, u8 ctl)
#ifdef SUPPORT_OCP
if (ctl & OC_PDCTL) {
mask |= SD_OC_POWER_DOWN;
- if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
mask |= MS_OC_POWER_DOWN;
- }
}
#endif
if (mask) {
retval = rtsx_write_register(chip, FPDCTL, mask, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (CHECK_PID(chip, 0x5288))
wait_timeout(200);
@@ -2326,9 +2256,8 @@ int rtsx_force_power_down(struct rtsx_chip *chip, u8 ctl)
if (mask) {
val = mask;
retval = rtsx_write_register(chip, FPDCTL, mask, val);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
diff --git a/drivers/staging/rts_pstor/rtsx_scsi.c b/drivers/staging/rts_pstor/rtsx_scsi.c
index f2e5842d4c90..86c41b3a42a3 100644
--- a/drivers/staging/rts_pstor/rtsx_scsi.c
+++ b/drivers/staging/rts_pstor/rtsx_scsi.c
@@ -135,9 +135,9 @@ void scsi_show_command(struct scsi_cmnd *srb)
default: what = "(unknown command)"; unknown_cmd = 1; break;
}
- if (srb->cmnd[0] != TEST_UNIT_READY) {
+ if (srb->cmnd[0] != TEST_UNIT_READY)
RTSX_DEBUGP("Command %s (%d bytes)\n", what, srb->cmd_len);
- }
+
if (unknown_cmd) {
RTSX_DEBUGP("");
for (i = 0; i < srb->cmd_len && i < 16; i++)
@@ -317,11 +317,11 @@ static int inquiry(struct scsi_cmnd *srb, struct rtsx_chip *chip)
};
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
- if (chip->lun2card[lun] == SD_CARD) {
+ if (chip->lun2card[lun] == SD_CARD)
inquiry_string = inquiry_sd;
- } else {
+ else
inquiry_string = inquiry_ms;
- }
+
} else if (CHECK_LUN_MODE(chip, SD_MS_1LUN)) {
inquiry_string = inquiry_sdms;
} else {
@@ -329,9 +329,8 @@ static int inquiry(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
buf = vmalloc(scsi_bufflen(srb));
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
#ifdef SUPPORT_MAGIC_GATE
if ((chip->mspro_formatter_enable) &&
@@ -340,23 +339,21 @@ static int inquiry(struct scsi_cmnd *srb, struct rtsx_chip *chip)
if (chip->mspro_formatter_enable)
#endif
{
- if (!card || (card == MS_CARD)) {
+ if (!card || (card == MS_CARD))
pro_formatter_flag = 1;
- }
}
if (pro_formatter_flag) {
- if (scsi_bufflen(srb) < 56) {
+ if (scsi_bufflen(srb) < 56)
sendbytes = (unsigned char)(scsi_bufflen(srb));
- } else {
+ else
sendbytes = 56;
- }
+
} else {
- if (scsi_bufflen(srb) < 36) {
+ if (scsi_bufflen(srb) < 36)
sendbytes = (unsigned char)(scsi_bufflen(srb));
- } else {
+ else
sendbytes = 36;
- }
}
if (sendbytes > 8) {
@@ -371,9 +368,8 @@ static int inquiry(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
if (pro_formatter_flag) {
- if (sendbytes > 36) {
+ if (sendbytes > 36)
memcpy(buf + 36, formatter_inquiry_str, sendbytes - 36);
- }
}
scsi_set_resid(srb, 0);
@@ -467,9 +463,8 @@ static int request_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
buf = vmalloc(scsi_bufflen(srb));
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
tmp = (unsigned char *)sense;
memcpy(buf, tmp, scsi_bufflen(srb));
@@ -494,15 +489,15 @@ static void ms_mode_sense(struct rtsx_chip *chip, u8 cmd,
if (cmd == MODE_SENSE) {
sys_info_offset = 8;
- if (data_size > 0x68) {
+ if (data_size > 0x68)
data_size = 0x68;
- }
+
buf[i++] = 0x67; /* Mode Data Length */
} else {
sys_info_offset = 12;
- if (data_size > 0x6C) {
+ if (data_size > 0x6C)
data_size = 0x6C;
- }
+
buf[i++] = 0x00; /* Mode Data Length (MSB) */
buf[i++] = 0x6A; /* Mode Data Length (LSB) */
}
@@ -520,11 +515,11 @@ static void ms_mode_sense(struct rtsx_chip *chip, u8 cmd,
}
/* WP */
- if (check_card_wp(chip, lun)) {
+ if (check_card_wp(chip, lun))
buf[i++] = 0x80;
- } else {
+ else
buf[i++] = 0x00;
- }
+
} else {
buf[i++] = 0x00; /* MediaType */
buf[i++] = 0x00; /* WP */
@@ -545,11 +540,10 @@ static void ms_mode_sense(struct rtsx_chip *chip, u8 cmd,
if (data_size >= 11)
buf[i++] = 0x00; /* No Access Control */
if (data_size >= 12) {
- if (support_format) {
+ if (support_format)
buf[i++] = 0xC0; /* SF, SGM */
- } else {
+ else
buf[i++] = 0x00;
- }
}
} else {
/* The Following Data is the content of "Page 0x20" */
@@ -560,11 +554,10 @@ static void ms_mode_sense(struct rtsx_chip *chip, u8 cmd,
if (data_size >= 7)
buf[i++] = 0x00; /* No Access Control */
if (data_size >= 8) {
- if (support_format) {
+ if (support_format)
buf[i++] = 0xC0; /* SF, SGM */
- } else {
+ else
buf[i++] = 0x00;
- }
}
}
@@ -600,9 +593,8 @@ static int mode_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip)
if ((chip->lun2card[lun] & MS_CARD)) {
if (!card || (card == MS_CARD)) {
dataSize = 108;
- if (chip->mspro_formatter_enable) {
+ if (chip->mspro_formatter_enable)
pro_formatter_flag = 1;
- }
}
}
#else
@@ -615,9 +607,8 @@ static int mode_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip)
#endif
buf = kmalloc(dataSize, GFP_KERNEL);
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
pageCode = srb->cmnd[2] & 0x3f;
@@ -632,11 +623,11 @@ static int mode_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip)
dataSize = 4;
buf[0] = 0x03;
buf[1] = 0x00;
- if (check_card_wp(chip, lun)) {
+ if (check_card_wp(chip, lun))
buf[2] = 0x80;
- } else {
+ else
buf[2] = 0x00;
- }
+
buf[3] = 0x00;
}
} else {
@@ -648,11 +639,10 @@ static int mode_sense(struct scsi_cmnd *srb, struct rtsx_chip *chip)
buf[0] = 0x00;
buf[1] = 0x06;
buf[2] = 0x00;
- if (check_card_wp(chip, lun)) {
+ if (check_card_wp(chip, lun))
buf[3] = 0x80;
- } else {
+ else
buf[3] = 0x00;
- }
buf[4] = 0x00;
buf[5] = 0x00;
buf[6] = 0x00;
@@ -759,11 +749,11 @@ static int read_write(struct scsi_cmnd *srb, struct rtsx_chip *chip)
if (chip->rw_fail_cnt[lun] == 3) {
RTSX_DEBUGP("read/write fail three times in succession\n");
- if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
- } else {
+ else
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
- }
+
TRACE_RET(chip, TRANSPORT_FAILED);
}
@@ -776,9 +766,8 @@ static int read_write(struct scsi_cmnd *srb, struct rtsx_chip *chip)
if (CHECK_PID(chip, 0x5209) && chip->max_payload) {
u8 val = 0x10 | (chip->max_payload << 5);
retval = rtsx_write_cfg_dw(chip, 0, 0x78, 0xFF, val);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
}
}
@@ -789,11 +778,10 @@ static int read_write(struct scsi_cmnd *srb, struct rtsx_chip *chip)
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
} else {
chip->rw_fail_cnt[lun]++;
- if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
- } else {
+ else
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
- }
}
retval = TRANSPORT_FAILED;
TRACE_GOTO(chip, Exit);
@@ -808,9 +796,8 @@ Exit:
if (srb->sc_data_direction == DMA_TO_DEVICE) {
if (CHECK_PID(chip, 0x5209) && chip->max_payload) {
retval = rtsx_write_cfg_dw(chip, 0, 0x78, 0xFF, 0x10);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
}
}
@@ -837,9 +824,8 @@ static int read_format_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip)
buf_len = (scsi_bufflen(srb) > 12) ? 0x14 : 12;
buf = kmalloc(buf_len, GFP_KERNEL);
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
buf[i++] = 0;
buf[i++] = 0;
@@ -864,22 +850,20 @@ static int read_format_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip)
buf[i++] = (unsigned char)(card_size >> 8);
buf[i++] = (unsigned char)card_size;
- if (desc_cnt == 2) {
+ if (desc_cnt == 2)
buf[i++] = 2;
- } else {
+ else
buf[i++] = 0;
- }
} else {
buf[i++] = 0xFF;
buf[i++] = 0xFF;
buf[i++] = 0xFF;
buf[i++] = 0xFF;
- if (desc_cnt == 2) {
+ if (desc_cnt == 2)
buf[i++] = 3;
- } else {
+ else
buf[i++] = 0;
- }
}
buf[i++] = 0x00;
@@ -916,9 +900,8 @@ static int read_capacity(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
buf = kmalloc(8, GFP_KERNEL);
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
card_size = get_card_size(chip, lun);
buf[0] = (unsigned char)((card_size - 1) >> 24);
@@ -956,9 +939,8 @@ static int read_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip)
len = ((u16)srb->cmnd[4] << 8) | srb->cmnd[5];
buf = (u8 *)vmalloc(len);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
retval = rtsx_force_power_on(chip, SSC_PDCTL);
if (retval != STATUS_SUCCESS) {
@@ -1016,9 +998,8 @@ static int write_eeprom(struct scsi_cmnd *srb, struct rtsx_chip *chip)
} else {
len = (unsigned short)min(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)vmalloc(len);
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
rtsx_stor_get_xfer_buf(buf, len, srb);
scsi_set_resid(srb, scsi_bufflen(srb) - len);
@@ -1061,9 +1042,8 @@ static int read_mem(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
buf = (u8 *)vmalloc(len);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
retval = rtsx_force_power_on(chip, SSC_PDCTL);
if (retval != STATUS_SUCCESS) {
@@ -1114,9 +1094,8 @@ static int write_mem(struct scsi_cmnd *srb, struct rtsx_chip *chip)
len = (unsigned short)min(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)vmalloc(len);
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
rtsx_stor_get_xfer_buf(buf, len, srb);
scsi_set_resid(srb, scsi_bufflen(srb) - len);
@@ -1200,16 +1179,15 @@ static int trace_msg_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
clear = srb->cmnd[2];
buf = (unsigned char *)vmalloc(scsi_bufflen(srb));
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
ptr = buf;
- if (chip->trace_msg[chip->msg_idx].valid) {
+ if (chip->trace_msg[chip->msg_idx].valid)
msg_cnt = TRACE_ITEM_CNT;
- } else {
+ else
msg_cnt = chip->msg_idx;
- }
+
*(ptr++) = (u8)(msg_cnt >> 24);
*(ptr++) = (u8)(msg_cnt >> 16);
*(ptr++) = (u8)(msg_cnt >> 8);
@@ -1225,15 +1203,14 @@ static int trace_msg_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
*(ptr++) = (u8)(chip->trace_msg[idx].line >> 8);
*(ptr++) = (u8)(chip->trace_msg[idx].line);
- for (j = 0; j < MSG_FUNC_LEN; j++) {
+ for (j = 0; j < MSG_FUNC_LEN; j++)
*(ptr++) = chip->trace_msg[idx].func[j];
- }
- for (j = 0; j < MSG_FILE_LEN; j++) {
+
+ for (j = 0; j < MSG_FILE_LEN; j++)
*(ptr++) = chip->trace_msg[idx].file[j];
- }
- for (j = 0; j < TIME_VAL_LEN; j++) {
+
+ for (j = 0; j < TIME_VAL_LEN; j++)
*(ptr++) = chip->trace_msg[idx].timeval_buf[j];
- }
}
rtsx_stor_set_xfer_buf(buf, scsi_bufflen(srb), srb);
@@ -1424,20 +1401,19 @@ static int dma_access_ring_buffer(struct scsi_cmnd *srb, struct rtsx_chip *chip)
len = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5];
len = min(len, (u16)scsi_bufflen(srb));
- if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
RTSX_DEBUGP("Read from device\n");
- } else {
+ else
RTSX_DEBUGP("Write to device\n");
- }
retval = rtsx_transfer_data(chip, 0, scsi_sglist(srb), len,
scsi_sg_count(srb), srb->sc_data_direction, 1000);
if (retval < 0) {
- if (srb->sc_data_direction == DMA_FROM_DEVICE) {
+ if (srb->sc_data_direction == DMA_FROM_DEVICE)
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
- } else {
+ else
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
- }
+
TRACE_RET(chip, TRANSPORT_FAILED);
}
scsi_set_resid(srb, 0);
@@ -1462,22 +1438,20 @@ static int get_dev_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
status[0] = (u8)(chip->product_id);
status[1] = chip->ic_version;
- if (chip->auto_delink_en) {
+ if (chip->auto_delink_en)
status[2] = 0x10;
- } else {
+ else
status[2] = 0x00;
- }
status[3] = 20;
status[4] = 10;
status[5] = 05;
status[6] = 21;
- if (chip->card_wp) {
+ if (chip->card_wp)
status[7] = 0x20;
- } else {
+ else
status[7] = 0x00;
- }
#ifdef SUPPORT_OCP
status[8] = 0;
@@ -1489,67 +1463,60 @@ static int get_dev_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
oc_ever_mask = SD_OC_EVER;
}
- if (chip->ocp_stat & oc_now_mask) {
+ if (chip->ocp_stat & oc_now_mask)
status[8] |= 0x02;
- }
- if (chip->ocp_stat & oc_ever_mask) {
+
+ if (chip->ocp_stat & oc_ever_mask)
status[8] |= 0x01;
- }
#endif
if (card == SD_CARD) {
if (CHK_SD(sd_card)) {
if (CHK_SD_HCXC(sd_card)) {
- if (sd_card->capacity > 0x4000000) {
+ if (sd_card->capacity > 0x4000000)
status[0x0E] = 0x02;
- } else {
+ else
status[0x0E] = 0x01;
- }
} else {
status[0x0E] = 0x00;
}
- if (CHK_SD_SDR104(sd_card)) {
+ if (CHK_SD_SDR104(sd_card))
status[0x0F] = 0x03;
- } else if (CHK_SD_DDR50(sd_card)) {
+ else if (CHK_SD_DDR50(sd_card))
status[0x0F] = 0x04;
- } else if (CHK_SD_SDR50(sd_card)) {
+ else if (CHK_SD_SDR50(sd_card))
status[0x0F] = 0x02;
- } else if (CHK_SD_HS(sd_card)) {
+ else if (CHK_SD_HS(sd_card))
status[0x0F] = 0x01;
- } else {
+ else
status[0x0F] = 0x00;
- }
} else {
- if (CHK_MMC_SECTOR_MODE(sd_card)) {
+ if (CHK_MMC_SECTOR_MODE(sd_card))
status[0x0E] = 0x01;
- } else {
+ else
status[0x0E] = 0x00;
- }
- if (CHK_MMC_DDR52(sd_card)) {
+ if (CHK_MMC_DDR52(sd_card))
status[0x0F] = 0x03;
- } else if (CHK_MMC_52M(sd_card)) {
+ else if (CHK_MMC_52M(sd_card))
status[0x0F] = 0x02;
- } else if (CHK_MMC_26M(sd_card)) {
+ else if (CHK_MMC_26M(sd_card))
status[0x0F] = 0x01;
- } else {
+ else
status[0x0F] = 0x00;
- }
}
} else if (card == MS_CARD) {
if (CHK_MSPRO(ms_card)) {
- if (CHK_MSXC(ms_card)) {
+ if (CHK_MSXC(ms_card))
status[0x0E] = 0x01;
- } else {
+ else
status[0x0E] = 0x00;
- }
- if (CHK_HG8BIT(ms_card)) {
+ if (CHK_HG8BIT(ms_card))
status[0x0F] = 0x01;
- } else {
+ else
status[0x0F] = 0x00;
- }
}
}
@@ -1600,37 +1567,35 @@ static int set_chip_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip)
if (phy_debug_mode) {
chip->phy_debug_mode = 1;
retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
rtsx_disable_bus_int(chip);
retval = rtsx_read_phy_register(chip, 0x1C, &reg);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
reg |= 0x0001;
retval = rtsx_write_phy_register(chip, 0x1C, reg);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
} else {
chip->phy_debug_mode = 0;
retval = rtsx_write_register(chip, CDRESUMECTL, 0x77, 0x77);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
rtsx_enable_bus_int(chip);
retval = rtsx_read_phy_register(chip, 0x1C, &reg);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
reg &= 0xFFFE;
retval = rtsx_write_phy_register(chip, 0x1C, reg);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
}
return TRANSPORT_GOOD;
@@ -1737,9 +1702,8 @@ static int read_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip)
if (len) {
buf = (u8 *)vmalloc(len);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
retval = rtsx_force_power_on(chip, SSC_PDCTL);
if (retval != STATUS_SUCCESS) {
@@ -1795,9 +1759,8 @@ static int write_phy_register(struct scsi_cmnd *srb, struct rtsx_chip *chip)
len = (unsigned short)min(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)vmalloc(len);
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
rtsx_stor_get_xfer_buf(buf, len, srb);
scsi_set_resid(srb, scsi_bufflen(srb) - len);
@@ -1886,9 +1849,8 @@ static int read_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
len = ((u16)srb->cmnd[6] << 8) | srb->cmnd[7];
buf = (u8 *)vmalloc(len);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
retval = rtsx_force_power_on(chip, SSC_PDCTL);
if (retval != STATUS_SUCCESS) {
@@ -1934,9 +1896,8 @@ static int write_eeprom2(struct scsi_cmnd *srb, struct rtsx_chip *chip)
len = (unsigned short)min(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)vmalloc(len);
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
rtsx_stor_get_xfer_buf(buf, len, srb);
scsi_set_resid(srb, scsi_bufflen(srb) - len);
@@ -1980,9 +1941,8 @@ static int read_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip)
len = srb->cmnd[5];
buf = (u8 *)vmalloc(len);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
retval = rtsx_force_power_on(chip, SSC_PDCTL);
if (retval != STATUS_SUCCESS) {
@@ -2029,9 +1989,8 @@ static int write_efuse(struct scsi_cmnd *srb, struct rtsx_chip *chip)
len = (u8)min(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)vmalloc(len);
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
rtsx_stor_get_xfer_buf(buf, len, srb);
scsi_set_resid(srb, scsi_bufflen(srb) - len);
@@ -2093,27 +2052,23 @@ Exit:
vfree(buf);
retval = card_power_off(chip, SPI_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
if (chip->asic_code) {
retval = rtsx_write_register(chip, PWR_GATE_CTRL, LDO3318_PWR_MASK, LDO_OFF);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
wait_timeout(600);
retval = rtsx_write_phy_register(chip, 0x08, val);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
retval = rtsx_write_register(chip, PWR_GATE_CTRL, LDO3318_PWR_MASK, LDO_ON);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
}
return result;
@@ -2140,11 +2095,10 @@ static int read_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
RTSX_DEBUGP("%s: func = %d, addr = 0x%x, len = %d\n", __func__, func, addr, len);
- if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) {
+ if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
func_max = 1;
- } else {
+ else
func_max = 0;
- }
if (func > func_max) {
set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
@@ -2152,9 +2106,8 @@ static int read_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
buf = (u8 *)vmalloc(len);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
retval = rtsx_read_cfg_seq(chip, func, addr, buf, len);
if (retval != STATUS_SUCCESS) {
@@ -2193,11 +2146,10 @@ static int write_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
RTSX_DEBUGP("%s: func = %d, addr = 0x%x\n", __func__, func, addr);
- if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) {
+ if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
func_max = 1;
- } else {
+ else
func_max = 0;
- }
if (func > func_max) {
set_sense_type(chip, SCSI_LUN(srb), SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
@@ -2206,9 +2158,8 @@ static int write_cfg_byte(struct scsi_cmnd *srb, struct rtsx_chip *chip)
len = (unsigned short)min(scsi_bufflen(srb), (unsigned int)len);
buf = (u8 *)vmalloc(len);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
rtsx_stor_get_xfer_buf(buf, len, srb);
scsi_set_resid(srb, scsi_bufflen(srb) - len);
@@ -2328,63 +2279,56 @@ static int read_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
rtsx_status[4] = (u8)lun;
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
- if (chip->lun2card[lun] == SD_CARD) {
+ if (chip->lun2card[lun] == SD_CARD)
rtsx_status[5] = 2;
- } else {
+ else
rtsx_status[5] = 3;
- }
} else {
if (chip->card_exist) {
- if (chip->card_exist & XD_CARD) {
+ if (chip->card_exist & XD_CARD)
rtsx_status[5] = 4;
- } else if (chip->card_exist & SD_CARD) {
+ else if (chip->card_exist & SD_CARD)
rtsx_status[5] = 2;
- } else if (chip->card_exist & MS_CARD) {
+ else if (chip->card_exist & MS_CARD)
rtsx_status[5] = 3;
- } else {
+ else
rtsx_status[5] = 7;
- }
} else {
rtsx_status[5] = 7;
}
}
- if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
rtsx_status[6] = 2;
- } else {
+ else
rtsx_status[6] = 1;
- }
rtsx_status[7] = (u8)(chip->product_id);
rtsx_status[8] = chip->ic_version;
- if (check_card_exist(chip, lun)) {
+ if (check_card_exist(chip, lun))
rtsx_status[9] = 1;
- } else {
+ else
rtsx_status[9] = 0;
- }
- if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
+ if (CHECK_LUN_MODE(chip, SD_MS_2LUN))
rtsx_status[10] = 0;
- } else {
+ else
rtsx_status[10] = 1;
- }
if (CHECK_LUN_MODE(chip, SD_MS_2LUN)) {
- if (chip->lun2card[lun] == SD_CARD) {
+ if (chip->lun2card[lun] == SD_CARD)
rtsx_status[11] = SD_CARD;
- } else {
+ else
rtsx_status[11] = MS_CARD;
- }
} else {
rtsx_status[11] = XD_CARD | SD_CARD | MS_CARD;
}
- if (check_card_ready(chip, lun)) {
+ if (check_card_ready(chip, lun))
rtsx_status[12] = 1;
- } else {
+ else
rtsx_status[12] = 0;
- }
if (get_lun_card(chip, lun) == XD_CARD) {
rtsx_status[13] = 0x40;
@@ -2421,29 +2365,26 @@ static int read_status(struct scsi_cmnd *srb, struct rtsx_chip *chip)
} else {
if (CHECK_LUN_MODE(chip, DEFAULT_SINGLE)) {
#ifdef SUPPORT_SDIO
- if (chip->sd_io && chip->sd_int) {
+ if (chip->sd_io && chip->sd_int)
rtsx_status[13] = 0x60;
- } else {
+ else
rtsx_status[13] = 0x70;
- }
#else
rtsx_status[13] = 0x70;
#endif
} else {
- if (chip->lun2card[lun] == SD_CARD) {
+ if (chip->lun2card[lun] == SD_CARD)
rtsx_status[13] = 0x20;
- } else {
+ else
rtsx_status[13] = 0x30;
- }
}
}
rtsx_status[14] = 0x78;
- if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) {
+ if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip))
rtsx_status[15] = 0x83;
- } else {
+ else
rtsx_status[15] = 0x82;
- }
buf_len = min(scsi_bufflen(srb), (unsigned int)sizeof(rtsx_status));
rtsx_stor_set_xfer_buf(rtsx_status, buf_len, srb);
@@ -2482,7 +2423,7 @@ static int spi_vendor_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
unsigned int lun = SCSI_LUN(srb);
u8 gpio_dir;
- if (CHECK_PID(chip, 0x5208) && CHECK_PID(chip, 0x5288)) {
+ if (CHECK_PID(chip, 0x5208) || CHECK_PID(chip, 0x5288)) {
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
TRACE_RET(chip, TRANSPORT_FAILED);
}
@@ -2538,9 +2479,8 @@ static int spi_vendor_cmd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
rtsx_write_register(chip, CARD_GPIO_DIR, 0x07, gpio_dir);
- if (result != STATUS_SUCCESS) {
+ if (result != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
return TRANSPORT_GOOD;
}
@@ -2610,13 +2550,12 @@ void led_shine(struct scsi_cmnd *srb, struct rtsx_chip *chip)
unsigned int lun = SCSI_LUN(srb);
u16 sec_cnt;
- if ((srb->cmnd[0] == READ_10) || (srb->cmnd[0] == WRITE_10)) {
+ if ((srb->cmnd[0] == READ_10) || (srb->cmnd[0] == WRITE_10))
sec_cnt = ((u16)(srb->cmnd[7]) << 8) | srb->cmnd[8];
- } else if ((srb->cmnd[0] == READ_6) || (srb->cmnd[0] == WRITE_6)) {
+ else if ((srb->cmnd[0] == READ_6) || (srb->cmnd[0] == WRITE_6))
sec_cnt = srb->cmnd[4];
- } else {
+ else
return;
- }
if (chip->rw_cap[lun] >= GPIO_TOGGLE_THRESHOLD) {
toggle_gpio(chip, LED_GPIO);
@@ -2659,11 +2598,10 @@ static int ms_format_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
rtsx_set_stat(chip, RTSX_STAT_RUN);
- if (srb->cmnd[8] & 0x01) {
+ if (srb->cmnd[8] & 0x01)
quick_format = 0;
- } else {
+ else
quick_format = 1;
- }
if (!(chip->card_ready & MS_CARD)) {
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
@@ -2724,27 +2662,25 @@ static int get_ms_information(struct scsi_cmnd *srb, struct rtsx_chip *chip)
TRACE_RET(chip, TRANSPORT_FAILED);
}
- if (dev_info_id == 0x15) {
+ if (dev_info_id == 0x15)
buf_len = data_len = 0x3A;
- } else {
+ else
buf_len = data_len = 0x6A;
- }
buf = kmalloc(buf_len, GFP_KERNEL);
- if (!buf) {
+ if (!buf)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
i = 0;
/* GET Memory Stick Media Information Response Header */
buf[i++] = 0x00; /* Data length MSB */
buf[i++] = data_len; /* Data length LSB */
/* Device Information Type Code */
- if (CHK_MSXC(ms_card)) {
+ if (CHK_MSXC(ms_card))
buf[i++] = 0x03;
- } else {
+ else
buf[i++] = 0x02;
- }
+
/* SGM bit */
buf[i++] = 0x01;
/* Reserved */
@@ -2759,11 +2695,11 @@ static int get_ms_information(struct scsi_cmnd *srb, struct rtsx_chip *chip)
/* Device Information ID Number */
buf[i++] = dev_info_id;
/* Device Information Length */
- if (dev_info_id == 0x15) {
+ if (dev_info_id == 0x15)
data_len = 0x31;
- } else {
+ else
data_len = 0x61;
- }
+
buf[i++] = 0x00; /* Data length MSB */
buf[i++] = data_len; /* Data length LSB */
/* Valid Bit */
@@ -2778,11 +2714,10 @@ static int get_ms_information(struct scsi_cmnd *srb, struct rtsx_chip *chip)
rtsx_stor_set_xfer_buf(buf, buf_len, srb);
- if (dev_info_id == 0x15) {
+ if (dev_info_id == 0x15)
scsi_set_resid(srb, scsi_bufflen(srb)-0x3C);
- } else {
+ else
scsi_set_resid(srb, scsi_bufflen(srb)-0x6C);
- }
kfree(buf);
return STATUS_SUCCESS;
@@ -2793,13 +2728,11 @@ static int ms_sp_cmnd(struct scsi_cmnd *srb, struct rtsx_chip *chip)
{
int retval = TRANSPORT_ERROR;
- if (srb->cmnd[2] == MS_FORMAT) {
+ if (srb->cmnd[2] == MS_FORMAT)
retval = ms_format_cmnd(srb, chip);
- }
#ifdef SUPPORT_PCGL_1P18
- else if (srb->cmnd[2] == GET_MS_INFORMATION) {
+ else if (srb->cmnd[2] == GET_MS_INFORMATION)
retval = get_ms_information(srb, chip);
- }
#endif
return retval;
@@ -2912,9 +2845,9 @@ static int mg_report_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
(srb->cmnd[8] == 0x04) &&
(srb->cmnd[9] == 0x1C)) {
retval = mg_get_local_EKB(srb, chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
} else {
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
TRACE_RET(chip, TRANSPORT_FAILED);
@@ -2926,9 +2859,9 @@ static int mg_report_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
(srb->cmnd[8] == 0x00) &&
(srb->cmnd[9] == 0x24)) {
retval = mg_get_rsp_chg(srb, chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
} else {
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
TRACE_RET(chip, TRANSPORT_FAILED);
@@ -2945,9 +2878,9 @@ static int mg_report_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
(srb->cmnd[4] == 0x00) &&
(srb->cmnd[5] < 32)) {
retval = mg_get_ICV(srb, chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
} else {
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
TRACE_RET(chip, TRANSPORT_FAILED);
@@ -3014,9 +2947,9 @@ static int mg_send_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
(srb->cmnd[8] == 0x00) &&
(srb->cmnd[9] == 0x0C)) {
retval = mg_set_leaf_id(srb, chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
} else {
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
TRACE_RET(chip, TRANSPORT_FAILED);
@@ -3028,9 +2961,9 @@ static int mg_send_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
(srb->cmnd[8] == 0x00) &&
(srb->cmnd[9] == 0x0C)) {
retval = mg_chg(srb, chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
} else {
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
TRACE_RET(chip, TRANSPORT_FAILED);
@@ -3042,9 +2975,9 @@ static int mg_send_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
(srb->cmnd[8] == 0x00) &&
(srb->cmnd[9] == 0x0C)) {
retval = mg_rsp(srb, chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
} else {
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
TRACE_RET(chip, TRANSPORT_FAILED);
@@ -3061,9 +2994,9 @@ static int mg_send_key(struct scsi_cmnd *srb, struct rtsx_chip *chip)
(srb->cmnd[4] == 0x00) &&
(srb->cmnd[5] < 32)) {
retval = mg_set_ICV(srb, chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
} else {
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_INVALID_CMD_FIELD);
TRACE_RET(chip, TRANSPORT_FAILED);
diff --git a/drivers/staging/rts_pstor/rtsx_transport.c b/drivers/staging/rts_pstor/rtsx_transport.c
index 54a474235f26..1f9a42480443 100644
--- a/drivers/staging/rts_pstor/rtsx_transport.c
+++ b/drivers/staging/rts_pstor/rtsx_transport.c
@@ -218,9 +218,9 @@ void rtsx_add_cmd(struct rtsx_chip *chip,
val |= (u32)data;
spin_lock_irq(&chip->rtsx->reg_lock);
- if (chip->ci < (HOST_CMDS_BUF_LEN / 4)) {
+ if (chip->ci < (HOST_CMDS_BUF_LEN / 4))
cb[(chip->ci)++] = cpu_to_le32(val);
- }
+
spin_unlock_irq(&chip->rtsx->reg_lock);
}
@@ -244,15 +244,14 @@ int rtsx_send_cmd(struct rtsx_chip *chip, u8 card, int timeout)
long timeleft;
int err = 0;
- if (card == SD_CARD) {
+ if (card == SD_CARD)
rtsx->check_card_cd = SD_EXIST;
- } else if (card == MS_CARD) {
+ else if (card == MS_CARD)
rtsx->check_card_cd = MS_EXIST;
- } else if (card == XD_CARD) {
+ else if (card == XD_CARD)
rtsx->check_card_cd = XD_EXIST;
- } else {
+ else
rtsx->check_card_cd = 0;
- }
spin_lock_irq(&rtsx->reg_lock);
@@ -281,11 +280,11 @@ int rtsx_send_cmd(struct rtsx_chip *chip, u8 card, int timeout)
}
spin_lock_irq(&rtsx->reg_lock);
- if (rtsx->trans_result == TRANS_RESULT_FAIL) {
+ if (rtsx->trans_result == TRANS_RESULT_FAIL)
err = -EIO;
- } else if (rtsx->trans_result == TRANS_RESULT_OK) {
+ else if (rtsx->trans_result == TRANS_RESULT_OK)
err = 0;
- }
+
spin_unlock_irq(&rtsx->reg_lock);
finish_send_cmd:
@@ -341,23 +340,21 @@ static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card,
if ((sg == NULL) || (num_sg <= 0) || !offset || !index)
return -EIO;
- if (dma_dir == DMA_TO_DEVICE) {
+ if (dma_dir == DMA_TO_DEVICE)
dir = HOST_TO_DEVICE;
- } else if (dma_dir == DMA_FROM_DEVICE) {
+ else if (dma_dir == DMA_FROM_DEVICE)
dir = DEVICE_TO_HOST;
- } else {
+ else
return -ENXIO;
- }
- if (card == SD_CARD) {
+ if (card == SD_CARD)
rtsx->check_card_cd = SD_EXIST;
- } else if (card == MS_CARD) {
+ else if (card == MS_CARD)
rtsx->check_card_cd = MS_EXIST;
- } else if (card == XD_CARD) {
+ else if (card == XD_CARD)
rtsx->check_card_cd = XD_EXIST;
- } else {
+ else
rtsx->check_card_cd = 0;
- }
spin_lock_irq(&rtsx->reg_lock);
@@ -405,11 +402,10 @@ static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card,
*offset = 0;
*index = *index + 1;
}
- if ((i == (sg_cnt - 1)) || !resid) {
+ if ((i == (sg_cnt - 1)) || !resid)
option = SG_VALID | SG_END | SG_TRANS_DATA;
- } else {
+ else
option = SG_VALID | SG_TRANS_DATA;
- }
rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option);
@@ -468,11 +464,11 @@ static int rtsx_transfer_sglist_adma_partial(struct rtsx_chip *chip, u8 card,
}
spin_lock_irq(&rtsx->reg_lock);
- if (rtsx->trans_result == TRANS_RESULT_FAIL) {
+ if (rtsx->trans_result == TRANS_RESULT_FAIL)
err = -EIO;
- } else if (rtsx->trans_result == TRANS_RESULT_OK) {
+ else if (rtsx->trans_result == TRANS_RESULT_OK)
err = 0;
- }
+
spin_unlock_irq(&rtsx->reg_lock);
out:
@@ -501,23 +497,21 @@ static int rtsx_transfer_sglist_adma(struct rtsx_chip *chip, u8 card,
if ((sg == NULL) || (num_sg <= 0))
return -EIO;
- if (dma_dir == DMA_TO_DEVICE) {
+ if (dma_dir == DMA_TO_DEVICE)
dir = HOST_TO_DEVICE;
- } else if (dma_dir == DMA_FROM_DEVICE) {
+ else if (dma_dir == DMA_FROM_DEVICE)
dir = DEVICE_TO_HOST;
- } else {
+ else
return -ENXIO;
- }
- if (card == SD_CARD) {
+ if (card == SD_CARD)
rtsx->check_card_cd = SD_EXIST;
- } else if (card == MS_CARD) {
+ else if (card == MS_CARD)
rtsx->check_card_cd = MS_EXIST;
- } else if (card == XD_CARD) {
+ else if (card == XD_CARD)
rtsx->check_card_cd = XD_EXIST;
- } else {
+ else
rtsx->check_card_cd = 0;
- }
spin_lock_irq(&rtsx->reg_lock);
@@ -537,11 +531,10 @@ static int rtsx_transfer_sglist_adma(struct rtsx_chip *chip, u8 card,
u32 val = TRIG_DMA;
int sg_cnt, j;
- if (i == buf_cnt / (HOST_SG_TBL_BUF_LEN / 8)) {
+ if (i == buf_cnt / (HOST_SG_TBL_BUF_LEN / 8))
sg_cnt = buf_cnt % (HOST_SG_TBL_BUF_LEN / 8);
- } else {
+ else
sg_cnt = (HOST_SG_TBL_BUF_LEN / 8);
- }
chip->sgi = 0;
for (j = 0; j < sg_cnt; j++) {
@@ -552,11 +545,10 @@ static int rtsx_transfer_sglist_adma(struct rtsx_chip *chip, u8 card,
RTSX_DEBUGP("DMA addr: 0x%x, Len: 0x%x\n",
(unsigned int)addr, len);
- if (j == (sg_cnt - 1)) {
+ if (j == (sg_cnt - 1))
option = SG_VALID | SG_END | SG_TRANS_DATA;
- } else {
+ else
option = SG_VALID | SG_TRANS_DATA;
- }
rtsx_add_sg_tbl(chip, (u32)addr, (u32)len, option);
@@ -615,11 +607,11 @@ static int rtsx_transfer_sglist_adma(struct rtsx_chip *chip, u8 card,
}
spin_lock_irq(&rtsx->reg_lock);
- if (rtsx->trans_result == TRANS_RESULT_FAIL) {
+ if (rtsx->trans_result == TRANS_RESULT_FAIL)
err = -EIO;
- } else if (rtsx->trans_result == TRANS_RESULT_OK) {
+ else if (rtsx->trans_result == TRANS_RESULT_OK)
err = 0;
- }
+
spin_unlock_irq(&rtsx->reg_lock);
out:
@@ -647,27 +639,25 @@ static int rtsx_transfer_buf(struct rtsx_chip *chip, u8 card, void *buf, size_t
if ((buf == NULL) || (len <= 0))
return -EIO;
- if (dma_dir == DMA_TO_DEVICE) {
+ if (dma_dir == DMA_TO_DEVICE)
dir = HOST_TO_DEVICE;
- } else if (dma_dir == DMA_FROM_DEVICE) {
+ else if (dma_dir == DMA_FROM_DEVICE)
dir = DEVICE_TO_HOST;
- } else {
+ else
return -ENXIO;
- }
addr = dma_map_single(&(rtsx->pci->dev), buf, len, dma_dir);
if (!addr)
return -ENOMEM;
- if (card == SD_CARD) {
+ if (card == SD_CARD)
rtsx->check_card_cd = SD_EXIST;
- } else if (card == MS_CARD) {
+ else if (card == MS_CARD)
rtsx->check_card_cd = MS_EXIST;
- } else if (card == XD_CARD) {
+ else if (card == XD_CARD)
rtsx->check_card_cd = XD_EXIST;
- } else {
+ else
rtsx->check_card_cd = 0;
- }
val |= (u32)(dir & 0x01) << 29;
val |= (u32)(len & 0x00FFFFFF);
@@ -698,11 +688,11 @@ static int rtsx_transfer_buf(struct rtsx_chip *chip, u8 card, void *buf, size_t
}
spin_lock_irq(&rtsx->reg_lock);
- if (rtsx->trans_result == TRANS_RESULT_FAIL) {
+ if (rtsx->trans_result == TRANS_RESULT_FAIL)
err = -EIO;
- } else if (rtsx->trans_result == TRANS_RESULT_OK) {
+ else if (rtsx->trans_result == TRANS_RESULT_OK)
err = 0;
- }
+
spin_unlock_irq(&rtsx->reg_lock);
out:
diff --git a/drivers/staging/rts_pstor/sd.c b/drivers/staging/rts_pstor/sd.c
index 3cc9a489e4e8..c6a581c47cbc 100644
--- a/drivers/staging/rts_pstor/sd.c
+++ b/drivers/staging/rts_pstor/sd.c
@@ -192,9 +192,9 @@ RTY_SEND_CMD:
stat_idx = 16;
} else if (rsp_type != SD_RSP_TYPE_R0) {
- for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4; reg_addr++) {
+ for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4; reg_addr++)
rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
- }
+
stat_idx = 5;
}
@@ -273,9 +273,8 @@ RTY_SEND_CMD:
if ((rsp_type == SD_RSP_TYPE_R1) || (rsp_type == SD_RSP_TYPE_R1b)) {
if ((cmd_idx != SEND_RELATIVE_ADDR) && (cmd_idx != SEND_IF_COND)) {
if (cmd_idx != STOP_TRANSMISSION) {
- if (ptr[1] & 0x80) {
+ if (ptr[1] & 0x80)
TRACE_RET(chip, STATUS_FAIL);
- }
}
#ifdef SUPPORT_SD_LOCK
if (ptr[1] & 0x7D)
@@ -294,11 +293,10 @@ RTY_SEND_CMD:
RTSX_DEBUGP("ptr[3]: 0x%02x\n", ptr[3]);
TRACE_RET(chip, STATUS_FAIL);
}
- if (ptr[3] & 0x01) {
+ if (ptr[3] & 0x01)
sd_card->sd_data_buf_ready = 1;
- } else {
+ else
sd_card->sd_data_buf_ready = 0;
- }
}
}
@@ -322,17 +320,15 @@ static int sd_read_data(struct rtsx_chip *chip,
if (!buf)
buf_len = 0;
- if (buf_len > 512) {
+ if (buf_len > 512)
TRACE_RET(chip, STATUS_FAIL);
- }
rtsx_init_cmd(chip);
if (cmd_len) {
RTSX_DEBUGP("SD/MMC CMD %d\n", cmd[0] - 0x40);
- for (i = 0; i < (cmd_len < 6 ? cmd_len : 6); i++) {
+ for (i = 0; i < (cmd_len < 6 ? cmd_len : 6); i++)
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0 + i, 0xFF, cmd[i]);
- }
}
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, (u8)byte_cnt);
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_H, 0xFF, (u8)(byte_cnt >> 8));
@@ -344,9 +340,9 @@ static int sd_read_data(struct rtsx_chip *chip,
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF,
SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END |
SD_CHECK_CRC7 | SD_RSP_LEN_6);
- if (trans_mode != SD_TM_AUTO_TUNING) {
+ if (trans_mode != SD_TM_AUTO_TUNING)
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, PINGPONG_BUFFER);
- }
+
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_TRANSFER, 0xFF, trans_mode | SD_TRANSFER_START);
rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END, SD_TRANSFER_END);
@@ -362,9 +358,8 @@ static int sd_read_data(struct rtsx_chip *chip,
if (buf && buf_len) {
retval = rtsx_read_ppbuf(chip, buf, buf_len);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -390,9 +385,8 @@ static int sd_write_data(struct rtsx_chip *chip, u8 trans_mode,
if (buf && buf_len) {
retval = rtsx_write_ppbuf(chip, buf, buf_len);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
rtsx_init_cmd(chip);
@@ -450,15 +444,13 @@ static int sd_check_csd(struct rtsx_chip *chip, char check_wp)
break;
}
- if (i == 6) {
+ if (i == 6)
TRACE_RET(chip, STATUS_FAIL);
- }
memcpy(sd_card->raw_csd, rsp + 1, 15);
- if (CHECK_PID(chip, 0x5209)) {
+ if (CHECK_PID(chip, 0x5209))
RTSX_READ_REG(chip, REG_SD_CMD5, sd_card->raw_csd + 15);
- }
RTSX_DEBUGP("CSD Response:\n");
RTSX_DUMP(sd_card->raw_csd, 16);
@@ -469,35 +461,34 @@ static int sd_check_csd(struct rtsx_chip *chip, char check_wp)
trans_speed = rsp[4];
if ((trans_speed & 0x07) == 0x02) {
if ((trans_speed & 0xf8) >= 0x30) {
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = 47;
- } else {
+ else
sd_card->sd_clock = CLK_50;
- }
+
} else if ((trans_speed & 0xf8) == 0x28) {
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = 39;
- } else {
+ else
sd_card->sd_clock = CLK_40;
- }
+
} else if ((trans_speed & 0xf8) == 0x20) {
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = 29;
- } else {
+ else
sd_card->sd_clock = CLK_30;
- }
+
} else if ((trans_speed & 0xf8) >= 0x10) {
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = 23;
- } else {
+ else
sd_card->sd_clock = CLK_20;
- }
+
} else if ((trans_speed & 0x08) >= 0x08) {
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = 19;
- } else {
+ else
sd_card->sd_clock = CLK_20;
- }
} else {
TRACE_RET(chip, STATUS_FAIL);
}
@@ -527,9 +518,9 @@ static int sd_check_csd(struct rtsx_chip *chip, char check_wp)
}
if (check_wp) {
- if (rsp[15] & 0x30) {
+ if (rsp[15] & 0x30)
chip->card_wp |= SD_CARD;
- }
+
RTSX_DEBUGP("CSD WP Status: 0x%x\n", rsp[15]);
}
@@ -568,22 +559,21 @@ static int sd_set_sample_push_timing(struct rtsx_chip *chip)
CRC_FIX_CLK | SD30_VAR_CLK0 | SAMPLE_VAR_CLK1);
RTSX_WRITE_REG(chip, CLK_CTL, CLK_LOW_FREQ, 0);
- if ((chip->sd_ctl & SD_PUSH_POINT_CTL_MASK) == SD_PUSH_POINT_AUTO) {
+ if ((chip->sd_ctl & SD_PUSH_POINT_CTL_MASK) == SD_PUSH_POINT_AUTO)
val = SD20_TX_NEG_EDGE;
- } else if ((chip->sd_ctl & SD_PUSH_POINT_CTL_MASK) == SD_PUSH_POINT_DELAY) {
+ else if ((chip->sd_ctl & SD_PUSH_POINT_CTL_MASK) == SD_PUSH_POINT_DELAY)
val = SD20_TX_14_AHEAD;
- } else {
+ else
val = SD20_TX_NEG_EDGE;
- }
+
RTSX_WRITE_REG(chip, SD_PUSH_POINT_CTL, SD20_TX_SEL_MASK, val);
if ((chip->sd_ctl & SD_SAMPLE_POINT_CTL_MASK) == SD_SAMPLE_POINT_AUTO) {
if (chip->asic_code) {
- if (CHK_SD_HS(sd_card) || CHK_MMC_52M(sd_card)) {
+ if (CHK_SD_HS(sd_card) || CHK_MMC_52M(sd_card))
val = SD20_RX_14_DELAY;
- } else {
+ else
val = SD20_RX_POS_EDGE;
- }
} else {
val = SD20_RX_14_DELAY;
}
@@ -597,32 +587,28 @@ static int sd_set_sample_push_timing(struct rtsx_chip *chip)
} else {
u8 val = 0;
- if ((chip->sd_ctl & SD_PUSH_POINT_CTL_MASK) == SD_PUSH_POINT_DELAY) {
+ if ((chip->sd_ctl & SD_PUSH_POINT_CTL_MASK) == SD_PUSH_POINT_DELAY)
val |= 0x10;
- }
if ((chip->sd_ctl & SD_SAMPLE_POINT_CTL_MASK) == SD_SAMPLE_POINT_AUTO) {
if (chip->asic_code) {
if (CHK_SD_HS(sd_card) || CHK_MMC_52M(sd_card)) {
- if (val & 0x10) {
+ if (val & 0x10)
val |= 0x04;
- } else {
+ else
val |= 0x08;
- }
}
} else {
- if (val & 0x10) {
+ if (val & 0x10)
val |= 0x04;
- } else {
+ else
val |= 0x08;
- }
}
} else if ((chip->sd_ctl & SD_SAMPLE_POINT_CTL_MASK) == SD_SAMPLE_POINT_DELAY) {
- if (val & 0x10) {
+ if (val & 0x10)
val |= 0x04;
- } else {
+ else
val |= 0x08;
- }
}
RTSX_WRITE_REG(chip, REG_SD_CFG1, 0x1C, val);
@@ -636,41 +622,40 @@ static void sd_choose_proper_clock(struct rtsx_chip *chip)
struct sd_info *sd_card = &(chip->sd_card);
if (CHK_SD_SDR104(sd_card)) {
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = chip->asic_sd_sdr104_clk;
- } else {
+ else
sd_card->sd_clock = chip->fpga_sd_sdr104_clk;
- }
+
} else if (CHK_SD_DDR50(sd_card)) {
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = chip->asic_sd_ddr50_clk;
- } else {
+ else
sd_card->sd_clock = chip->fpga_sd_ddr50_clk;
- }
+
} else if (CHK_SD_SDR50(sd_card)) {
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = chip->asic_sd_sdr50_clk;
- } else {
+ else
sd_card->sd_clock = chip->fpga_sd_sdr50_clk;
- }
+
} else if (CHK_SD_HS(sd_card)) {
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = chip->asic_sd_hs_clk;
- } else {
+ else
sd_card->sd_clock = chip->fpga_sd_hs_clk;
- }
+
} else if (CHK_MMC_52M(sd_card) || CHK_MMC_DDR52(sd_card)) {
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = chip->asic_mmc_52m_clk;
- } else {
+ else
sd_card->sd_clock = chip->fpga_mmc_52m_clk;
- }
+
} else if (CHK_MMC_26M(sd_card)) {
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = 48;
- } else {
+ else
sd_card->sd_clock = CLK_50;
- }
}
}
@@ -683,13 +668,12 @@ static int sd_set_clock_divider(struct rtsx_chip *chip, u8 clk_div)
val = clk_div;
} else {
mask = 0x60;
- if (clk_div == SD_CLK_DIVIDE_0) {
+ if (clk_div == SD_CLK_DIVIDE_0)
val = 0x00;
- } else if (clk_div == SD_CLK_DIVIDE_128) {
+ else if (clk_div == SD_CLK_DIVIDE_128)
val = 0x40;
- } else if (clk_div == SD_CLK_DIVIDE_256) {
+ else if (clk_div == SD_CLK_DIVIDE_256)
val = 0x20;
- }
}
RTSX_WRITE_REG(chip, REG_SD_CFG1, mask, val);
@@ -703,16 +687,14 @@ static int sd_set_init_para(struct rtsx_chip *chip)
int retval;
retval = sd_set_sample_push_timing(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
sd_choose_proper_clock(chip);
retval = switch_clock(chip, sd_card->sd_clock);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -735,9 +717,8 @@ int sd_select_card(struct rtsx_chip *chip, int select)
}
retval = sd_send_cmd_get_rsp(chip, cmd_idx, addr, cmd_type, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -750,21 +731,18 @@ static int sd_update_lock_status(struct rtsx_chip *chip)
u8 rsp[5];
retval = sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr, SD_RSP_TYPE_R1, rsp, 5);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (rsp[1] & 0x02) {
+ if (rsp[1] & 0x02)
sd_card->sd_lock_status |= SD_LOCKED;
- } else {
+ else
sd_card->sd_lock_status &= ~SD_LOCKED;
- }
RTSX_DEBUGP("sd_card->sd_lock_status = 0x%x\n", sd_card->sd_lock_status);
- if (rsp[1] & 0x01) {
+ if (rsp[1] & 0x01)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -779,13 +757,11 @@ static int sd_wait_state_data_ready(struct rtsx_chip *chip, u8 state, u8 data_re
for (i = 0; i < polling_cnt; i++) {
retval = sd_send_cmd_get_rsp(chip, SEND_STATUS,
sd_card->sd_addr, SD_RSP_TYPE_R1, rsp, 5);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (((rsp[3] & 0x1E) == state) && ((rsp[3] & 0x01) == data_ready)) {
+ if (((rsp[3] & 0x1E) == state) && ((rsp[3] & 0x01) == data_ready))
return STATUS_SUCCESS;
- }
}
TRACE_RET(chip, STATUS_FAIL);
@@ -798,18 +774,16 @@ static int sd_change_bank_voltage(struct rtsx_chip *chip, u8 voltage)
if (voltage == SD_IO_3V3) {
if (chip->asic_code) {
retval = rtsx_write_phy_register(chip, 0x08, 0x4FC0 | chip->phy_voltage);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
RTSX_WRITE_REG(chip, SD_PAD_CTL, SD_IO_USING_1V8, 0);
}
} else if (voltage == SD_IO_1V8) {
if (chip->asic_code) {
retval = rtsx_write_phy_register(chip, 0x08, 0x4C40 | chip->phy_voltage);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
RTSX_WRITE_REG(chip, SD_PAD_CTL, SD_IO_USING_1V8, SD_IO_USING_1V8);
}
@@ -828,9 +802,8 @@ static int sd_voltage_switch(struct rtsx_chip *chip)
RTSX_WRITE_REG(chip, SD_BUS_STAT, SD_CLK_TOGGLE_EN | SD_CLK_FORCE_STOP, SD_CLK_TOGGLE_EN);
retval = sd_send_cmd_get_rsp(chip, VOLTAGE_SWITCH, 0, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
udelay(chip->sd_voltage_switch_delay);
@@ -842,9 +815,9 @@ static int sd_voltage_switch(struct rtsx_chip *chip)
RTSX_WRITE_REG(chip, SD_BUS_STAT, 0xFF, SD_CLK_FORCE_STOP);
retval = sd_change_bank_voltage(chip, SD_IO_1V8);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
wait_timeout(50);
RTSX_WRITE_REG(chip, SD_BUS_STAT, 0xFF, SD_CLK_TOGGLE_EN);
@@ -893,9 +866,8 @@ static int sd_change_phase(struct rtsx_chip *chip, u8 sample_point, u8 tune_dir)
if (tune_dir == TUNE_RX) {
SD_VP_CTL = SD_VPRX_CTL;
SD_DCMPS_CTL = SD_DCMPS_RX_CTL;
- if (CHK_SD_DDR50(sd_card)) {
+ if (CHK_SD_DDR50(sd_card))
ddr_rx = 1;
- }
} else {
SD_VP_CTL = SD_VPTX_CTL;
SD_DCMPS_CTL = SD_DCMPS_TX_CTL;
@@ -932,23 +904,22 @@ static int sd_change_phase(struct rtsx_chip *chip, u8 sample_point, u8 tune_dir)
rtsx_add_cmd(chip, WRITE_REG_CMD, SD_DCMPS_CTL, DCMPS_CHANGE, DCMPS_CHANGE);
rtsx_add_cmd(chip, CHECK_REG_CMD, SD_DCMPS_CTL, DCMPS_CHANGE_DONE, DCMPS_CHANGE_DONE);
retval = rtsx_send_cmd(chip, SD_CARD, 100);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, Fail);
- }
val = *rtsx_get_cmd_data(chip);
- if (val & DCMPS_ERROR) {
+ if (val & DCMPS_ERROR)
TRACE_GOTO(chip, Fail);
- }
- if ((val & DCMPS_CURRENT_PHASE) != sample_point) {
+
+ if ((val & DCMPS_CURRENT_PHASE) != sample_point)
TRACE_GOTO(chip, Fail);
- }
+
RTSX_WRITE_REG(chip, SD_DCMPS_CTL, DCMPS_CHANGE, 0);
- if (ddr_rx) {
+ if (ddr_rx)
RTSX_WRITE_REG(chip, SD_VP_CTL, PHASE_CHANGE, 0);
- } else {
+ else
RTSX_WRITE_REG(chip, CLK_CTL, CHANGE_CLK, 0);
- }
+
udelay(50);
}
@@ -978,9 +949,8 @@ static int sd_check_spec(struct rtsx_chip *chip, u8 bus_width)
u8 cmd[5], buf[8];
retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
cmd[0] = 0x40 | SEND_SCR;
cmd[1] = 0;
@@ -996,9 +966,8 @@ static int sd_check_spec(struct rtsx_chip *chip, u8 bus_width)
memcpy(sd_card->raw_scr, buf, 8);
- if ((buf[0] & 0x0F) == 0) {
+ if ((buf[0] & 0x0F) == 0)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -1172,13 +1141,12 @@ static int sd_check_switch_mode(struct rtsx_chip *chip, u8 mode,
*/
u16 cc = ((u16)buf[0] << 8) | buf[1];
RTSX_DEBUGP("Maximum current consumption: %dmA\n", cc);
- if ((cc == 0) || (cc > 800)) {
+ if ((cc == 0) || (cc > 800))
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = sd_query_switch_result(chip, func_group, func_to_switch, buf, 64);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if ((cc > 400) || (func_to_switch > CURRENT_LIMIT_400)) {
RTSX_WRITE_REG(chip, OCPPARA2, SD_OCP_THD_MASK, chip->sd_800mA_ocp_thd);
@@ -1192,13 +1160,12 @@ static int sd_check_switch_mode(struct rtsx_chip *chip, u8 mode,
static u8 downgrade_switch_mode(u8 func_group, u8 func_to_switch)
{
if (func_group == SD_FUNC_GROUP_1) {
- if (func_to_switch > HS_SUPPORT) {
+ if (func_to_switch > HS_SUPPORT)
func_to_switch--;
- }
+
} else if (func_group == SD_FUNC_GROUP_4) {
- if (func_to_switch > CURRENT_LIMIT_200) {
+ if (func_to_switch > CURRENT_LIMIT_200)
func_to_switch--;
- }
}
return func_to_switch;
@@ -1241,9 +1208,8 @@ static int sd_check_switch(struct rtsx_chip *chip,
wait_timeout(20);
}
- if (!switch_good) {
+ if (!switch_good)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -1258,9 +1224,8 @@ static int sd_switch_function(struct rtsx_chip *chip, u8 bus_width)
/* Get supported functions */
retval = sd_check_switch_mode(chip, SD_CHECK_MODE,
NO_ARGUMENT, NO_ARGUMENT, bus_width);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
sd_card->func_group1_mask &= ~(sd_card->sd_switch_fail);
@@ -1289,9 +1254,9 @@ static int sd_switch_function(struct rtsx_chip *chip, u8 bus_width)
break;
case HS_SUPPORT:
- if (sd_card->func_group1_mask & HS_SUPPORT_MASK) {
+ if (sd_card->func_group1_mask & HS_SUPPORT_MASK)
func_to_switch = HS_SUPPORT;
- }
+
break;
default:
@@ -1299,9 +1264,9 @@ static int sd_switch_function(struct rtsx_chip *chip, u8 bus_width)
}
- if (func_to_switch) {
+ if (func_to_switch)
break;
- }
+
}
RTSX_DEBUGP("SD_FUNC_GROUP_1: func_to_switch = 0x%02x", func_to_switch);
@@ -1330,23 +1295,21 @@ static int sd_switch_function(struct rtsx_chip *chip, u8 bus_width)
TRACE_RET(chip, STATUS_FAIL);
}
- if (func_to_switch == SDR104_SUPPORT) {
+ if (func_to_switch == SDR104_SUPPORT)
SET_SD_SDR104(sd_card);
- } else if (func_to_switch == DDR50_SUPPORT) {
+ else if (func_to_switch == DDR50_SUPPORT)
SET_SD_DDR50(sd_card);
- } else if (func_to_switch == SDR50_SUPPORT) {
+ else if (func_to_switch == SDR50_SUPPORT)
SET_SD_SDR50(sd_card);
- } else {
+ else
SET_SD_HS(sd_card);
- }
}
if (CHK_SD_DDR50(sd_card)) {
RTSX_WRITE_REG(chip, SD_PUSH_POINT_CTL, 0x06, 0x04);
retval = sd_set_sample_push_timing(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
if (!func_to_switch || (func_to_switch == HS_SUPPORT)) {
@@ -1362,36 +1325,35 @@ static int sd_switch_function(struct rtsx_chip *chip, u8 bus_width)
for (i = 0; i < 4; i++) {
switch ((u8)(chip->sd_current_prior >> (i*8))) {
case CURRENT_LIMIT_800:
- if (sd_card->func_group4_mask & CURRENT_LIMIT_800_MASK) {
+ if (sd_card->func_group4_mask & CURRENT_LIMIT_800_MASK)
func_to_switch = CURRENT_LIMIT_800;
- }
+
break;
case CURRENT_LIMIT_600:
- if (sd_card->func_group4_mask & CURRENT_LIMIT_600_MASK) {
+ if (sd_card->func_group4_mask & CURRENT_LIMIT_600_MASK)
func_to_switch = CURRENT_LIMIT_600;
- }
+
break;
case CURRENT_LIMIT_400:
- if (sd_card->func_group4_mask & CURRENT_LIMIT_400_MASK) {
+ if (sd_card->func_group4_mask & CURRENT_LIMIT_400_MASK)
func_to_switch = CURRENT_LIMIT_400;
- }
+
break;
case CURRENT_LIMIT_200:
- if (sd_card->func_group4_mask & CURRENT_LIMIT_200_MASK) {
+ if (sd_card->func_group4_mask & CURRENT_LIMIT_200_MASK)
func_to_switch = CURRENT_LIMIT_200;
- }
+
break;
default:
continue;
}
- if (func_to_switch != 0xFF) {
+ if (func_to_switch != 0xFF)
break;
- }
}
RTSX_DEBUGP("SD_FUNC_GROUP_4: func_to_switch = 0x%02x", func_to_switch);
@@ -1399,16 +1361,14 @@ static int sd_switch_function(struct rtsx_chip *chip, u8 bus_width)
if (func_to_switch <= CURRENT_LIMIT_800) {
retval = sd_check_switch(chip, SD_FUNC_GROUP_4, func_to_switch, bus_width);
if (retval != STATUS_SUCCESS) {
- if (sd_check_err_code(chip, SD_NO_CARD)) {
+ if (sd_check_err_code(chip, SD_NO_CARD))
TRACE_RET(chip, STATUS_FAIL);
- }
}
RTSX_DEBUGP("Switch current limit finished! (%d)\n", retval);
}
- if (CHK_SD_DDR50(sd_card)) {
+ if (CHK_SD_DDR50(sd_card))
RTSX_WRITE_REG(chip, SD_PUSH_POINT_CTL, 0x06, 0);
- }
return STATUS_SUCCESS;
}
@@ -1438,9 +1398,8 @@ static int sd_sdr_tuning_rx_cmd(struct rtsx_chip *chip, u8 sample_point)
u8 cmd[5];
retval = sd_change_phase(chip, sample_point, TUNE_RX);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
cmd[0] = 0x40 | SEND_TUNING_PATTERN;
cmd[1] = 0;
@@ -1467,16 +1426,14 @@ static int sd_ddr_tuning_rx_cmd(struct rtsx_chip *chip, u8 sample_point)
u8 cmd[5];
retval = sd_change_phase(chip, sample_point, TUNE_RX);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_DEBUGP("sd ddr tuning rx\n");
retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
cmd[0] = 0x40 | SD_STATUS;
cmd[1] = 0;
@@ -1502,18 +1459,16 @@ static int mmc_ddr_tunning_rx_cmd(struct rtsx_chip *chip, u8 sample_point)
int retval;
u8 cmd[5], bus_width;
- if (CHK_MMC_8BIT(sd_card)) {
+ if (CHK_MMC_8BIT(sd_card))
bus_width = SD_BUS_WIDTH_8;
- } else if (CHK_MMC_4BIT(sd_card)) {
+ else if (CHK_MMC_4BIT(sd_card))
bus_width = SD_BUS_WIDTH_4;
- } else {
+ else
bus_width = SD_BUS_WIDTH_1;
- }
retval = sd_change_phase(chip, sample_point, TUNE_RX);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_DEBUGP("mmc ddr tuning rx\n");
@@ -1541,9 +1496,8 @@ static int sd_sdr_tuning_tx_cmd(struct rtsx_chip *chip, u8 sample_point)
int retval;
retval = sd_change_phase(chip, sample_point, TUNE_TX);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_WRITE_REG(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, SD_RSP_80CLK_TIMEOUT_EN);
@@ -1568,26 +1522,23 @@ static int sd_ddr_tuning_tx_cmd(struct rtsx_chip *chip, u8 sample_point)
u8 cmd[5], bus_width;
retval = sd_change_phase(chip, sample_point, TUNE_TX);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (CHK_SD(sd_card)) {
bus_width = SD_BUS_WIDTH_4;
} else {
- if (CHK_MMC_8BIT(sd_card)) {
+ if (CHK_MMC_8BIT(sd_card))
bus_width = SD_BUS_WIDTH_8;
- } else if (CHK_MMC_4BIT(sd_card)) {
+ else if (CHK_MMC_4BIT(sd_card))
bus_width = SD_BUS_WIDTH_4;
- } else {
+ else
bus_width = SD_BUS_WIDTH_1;
- }
}
retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_WRITE_REG(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, SD_RSP_80CLK_TIMEOUT_EN);
@@ -1621,11 +1572,10 @@ static u8 sd_search_final_phase(struct rtsx_chip *chip, u32 phase_map, u8 tune_d
u8 final_phase = 0xFF;
if (phase_map == 0xFFFFFFFF) {
- if (tune_dir == TUNE_RX) {
+ if (tune_dir == TUNE_RX)
final_phase = (u8)chip->sd_default_rx_phase;
- } else {
+ else
final_phase = (u8)chip->sd_default_tx_phase;
- }
goto Search_Finish;
}
@@ -1666,9 +1616,9 @@ static u8 sd_search_final_phase(struct rtsx_chip *chip, u32 phase_map, u8 tune_d
path[0].start = path[cont_path_cnt - 1].start - MAX_PHASE - 1;
path[0].len += path[cont_path_cnt - 1].len;
path[0].mid = path[0].start + path[0].len / 2;
- if (path[0].mid < 0) {
+ if (path[0].mid < 0)
path[0].mid += MAX_PHASE + 1;
- }
+
cont_path_cnt--;
}
@@ -1696,11 +1646,10 @@ static u8 sd_search_final_phase(struct rtsx_chip *chip, u32 phase_map, u8 tune_d
int temp_final_phase =
path[final_path_idx].end - (max_len - (6 + temp_mid));
- if (temp_final_phase < 0) {
+ if (temp_final_phase < 0)
final_phase = (u8)(temp_final_phase + MAX_PHASE + 1);
- } else {
+ else
final_phase = (u8)temp_final_phase;
- }
}
} else if (CHK_SD_SDR50(sd_card)) {
if (max_len > 12) {
@@ -1708,11 +1657,10 @@ static u8 sd_search_final_phase(struct rtsx_chip *chip, u32 phase_map, u8 tune_d
int temp_final_phase =
path[final_path_idx].end - (max_len - (3 + temp_mid));
- if (temp_final_phase < 0) {
+ if (temp_final_phase < 0)
final_phase = (u8)(temp_final_phase + MAX_PHASE + 1);
- } else {
+ else
final_phase = (u8)temp_final_phase;
- }
}
}
}
@@ -1732,17 +1680,16 @@ static int sd_tuning_rx(struct rtsx_chip *chip)
int (*tuning_cmd)(struct rtsx_chip *chip, u8 sample_point);
if (CHK_SD(sd_card)) {
- if (CHK_SD_DDR50(sd_card)) {
+ if (CHK_SD_DDR50(sd_card))
tuning_cmd = sd_ddr_tuning_rx_cmd;
- } else {
+ else
tuning_cmd = sd_sdr_tuning_rx_cmd;
- }
+
} else {
- if (CHK_MMC_DDR52(sd_card)) {
+ if (CHK_MMC_DDR52(sd_card))
tuning_cmd = mmc_ddr_tunning_rx_cmd;
- } else {
+ else
TRACE_RET(chip, STATUS_FAIL);
- }
}
for (i = 0; i < 3; i++) {
@@ -1754,27 +1701,24 @@ static int sd_tuning_rx(struct rtsx_chip *chip)
}
retval = tuning_cmd(chip, (u8)j);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
raw_phase_map[i] |= 1 << j;
- }
}
}
phase_map = raw_phase_map[0] & raw_phase_map[1] & raw_phase_map[2];
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < 3; i++)
RTSX_DEBUGP("RX raw_phase_map[%d] = 0x%08x\n", i, raw_phase_map[i]);
- }
+
RTSX_DEBUGP("RX phase_map = 0x%08x\n", phase_map);
final_phase = sd_search_final_phase(chip, phase_map, TUNE_RX);
- if (final_phase == 0xFF) {
+ if (final_phase == 0xFF)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = sd_change_phase(chip, final_phase, TUNE_RX);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -1799,15 +1743,13 @@ static int sd_ddr_pre_tuning_tx(struct rtsx_chip *chip)
}
retval = sd_change_phase(chip, (u8)i, TUNE_TX);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
continue;
- }
retval = sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
SD_RSP_TYPE_R1, NULL, 0);
- if ((retval == STATUS_SUCCESS) || !sd_check_err_code(chip, SD_RSP_TIMEOUT)) {
+ if ((retval == STATUS_SUCCESS) || !sd_check_err_code(chip, SD_RSP_TIMEOUT))
phase_map |= 1 << i;
- }
}
RTSX_WRITE_REG(chip, SD_CFG3, SD_RSP_80CLK_TIMEOUT_EN, 0);
@@ -1815,14 +1757,12 @@ static int sd_ddr_pre_tuning_tx(struct rtsx_chip *chip)
RTSX_DEBUGP("DDR TX pre tune phase_map = 0x%08x\n", phase_map);
final_phase = sd_search_final_phase(chip, phase_map, TUNE_TX);
- if (final_phase == 0xFF) {
+ if (final_phase == 0xFF)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = sd_change_phase(chip, final_phase, TUNE_TX);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_DEBUGP("DDR TX pre tune phase: %d\n", (int)final_phase);
@@ -1839,17 +1779,16 @@ static int sd_tuning_tx(struct rtsx_chip *chip)
int (*tuning_cmd)(struct rtsx_chip *chip, u8 sample_point);
if (CHK_SD(sd_card)) {
- if (CHK_SD_DDR50(sd_card)) {
+ if (CHK_SD_DDR50(sd_card))
tuning_cmd = sd_ddr_tuning_tx_cmd;
- } else {
+ else
tuning_cmd = sd_sdr_tuning_tx_cmd;
- }
+
} else {
- if (CHK_MMC_DDR52(sd_card)) {
+ if (CHK_MMC_DDR52(sd_card))
tuning_cmd = sd_ddr_tuning_tx_cmd;
- } else {
+ else
TRACE_RET(chip, STATUS_FAIL);
- }
}
for (i = 0; i < 3; i++) {
@@ -1863,27 +1802,24 @@ static int sd_tuning_tx(struct rtsx_chip *chip)
}
retval = tuning_cmd(chip, (u8)j);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
raw_phase_map[i] |= 1 << j;
- }
}
}
phase_map = raw_phase_map[0] & raw_phase_map[1] & raw_phase_map[2];
- for (i = 0; i < 3; i++) {
+ for (i = 0; i < 3; i++)
RTSX_DEBUGP("TX raw_phase_map[%d] = 0x%08x\n", i, raw_phase_map[i]);
- }
+
RTSX_DEBUGP("TX phase_map = 0x%08x\n", phase_map);
final_phase = sd_search_final_phase(chip, phase_map, TUNE_TX);
- if (final_phase == 0xFF) {
+ if (final_phase == 0xFF)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = sd_change_phase(chip, final_phase, TUNE_TX);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -1893,14 +1829,12 @@ static int sd_sdr_tuning(struct rtsx_chip *chip)
int retval;
retval = sd_tuning_tx(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = sd_tuning_rx(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -1911,26 +1845,22 @@ static int sd_ddr_tuning(struct rtsx_chip *chip)
if (!(chip->sd_ctl & SD_DDR_TX_PHASE_SET_BY_USER)) {
retval = sd_ddr_pre_tuning_tx(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
retval = sd_change_phase(chip, (u8)chip->sd_ddr_tx_phase, TUNE_TX);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
retval = sd_tuning_rx(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (!(chip->sd_ctl & SD_DDR_TX_PHASE_SET_BY_USER)) {
retval = sd_tuning_tx(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -1942,26 +1872,22 @@ static int mmc_ddr_tuning(struct rtsx_chip *chip)
if (!(chip->sd_ctl & MMC_DDR_TX_PHASE_SET_BY_USER)) {
retval = sd_ddr_pre_tuning_tx(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
retval = sd_change_phase(chip, (u8)chip->mmc_ddr_tx_phase, TUNE_TX);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
retval = sd_tuning_rx(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (!(chip->sd_ctl & MMC_DDR_TX_PHASE_SET_BY_USER)) {
retval = sd_tuning_tx(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -1974,9 +1900,8 @@ int sd_switch_clock(struct rtsx_chip *chip)
int re_tuning = 0;
retval = select_card(chip, SD_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (CHECK_PID(chip, 0x5209) &&
(CHK_SD30_SPEED(sd_card) || CHK_MMC_DDR52(sd_card))) {
@@ -1987,26 +1912,22 @@ int sd_switch_clock(struct rtsx_chip *chip)
}
retval = switch_clock(chip, sd_card->sd_clock);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (re_tuning) {
if (CHK_SD(sd_card)) {
- if (CHK_SD_DDR50(sd_card)) {
+ if (CHK_SD_DDR50(sd_card))
retval = sd_ddr_tuning(chip);
- } else {
+ else
retval = sd_sdr_tuning(chip);
- }
} else {
- if (CHK_MMC_DDR52(sd_card)) {
+ if (CHK_MMC_DDR52(sd_card))
retval = mmc_ddr_tuning(chip);
- }
}
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
return STATUS_SUCCESS;
@@ -2017,11 +1938,10 @@ static int sd_prepare_reset(struct rtsx_chip *chip)
struct sd_info *sd_card = &(chip->sd_card);
int retval;
- if (chip->asic_code) {
+ if (chip->asic_code)
sd_card->sd_clock = 29;
- } else {
+ else
sd_card->sd_clock = CLK_30;
- }
sd_card->sd_type = 0;
sd_card->seq_mode = 0;
@@ -2037,9 +1957,8 @@ static int sd_prepare_reset(struct rtsx_chip *chip)
chip->sd_io = 0;
retval = sd_set_init_para(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, retval);
- }
if (CHECK_PID(chip, 0x5209)) {
RTSX_WRITE_REG(chip, REG_SD_CFG1, 0xFF,
@@ -2053,9 +1972,8 @@ static int sd_prepare_reset(struct rtsx_chip *chip)
RTSX_WRITE_REG(chip, CARD_STOP, SD_STOP | SD_CLR_ERR, SD_STOP | SD_CLR_ERR);
retval = select_card(chip, SD_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -2122,9 +2040,8 @@ int sd_pull_ctl_enable(struct rtsx_chip *chip)
}
retval = rtsx_send_cmd(chip, SD_CARD, 100);
- if (retval < 0) {
+ if (retval < 0)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -2133,42 +2050,37 @@ static int sd_init_power(struct rtsx_chip *chip)
{
int retval;
- if (CHECK_PID(chip, 0x5209)) {
+ if (CHECK_PID(chip, 0x5209))
RTSX_WRITE_REG(chip, PWR_GATE_CTRL, LDO3318_PWR_MASK, LDO_OFF);
- }
retval = sd_power_off_card3v3(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (!chip->ft2_fast_mode) {
+ if (!chip->ft2_fast_mode)
wait_timeout(250);
- }
retval = enable_card_clock(chip, SD_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (chip->asic_code) {
retval = sd_pull_ctl_enable(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
RTSX_WRITE_REG(chip, FPGA_PULL_CTL, FPGA_SD_PULL_CTL_BIT | 0x20, 0);
}
if (chip->ft2_fast_mode) {
- if (CHECK_PID(chip, 0x5209)) {
+ if (CHECK_PID(chip, 0x5209))
RTSX_WRITE_REG(chip, PWR_GATE_CTRL, LDO3318_PWR_MASK, LDO_ON);
- }
+
} else {
retval = card_power_on(chip, SD_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
wait_timeout(260);
#ifdef SUPPORT_OCP
@@ -2214,13 +2126,12 @@ static int sd_read_lba0(struct rtsx_chip *chip)
if (CHK_SD(sd_card)) {
bus_width = SD_BUS_WIDTH_4;
} else {
- if (CHK_MMC_8BIT(sd_card)) {
+ if (CHK_MMC_8BIT(sd_card))
bus_width = SD_BUS_WIDTH_8;
- } else if (CHK_MMC_4BIT(sd_card)) {
+ else if (CHK_MMC_4BIT(sd_card))
bus_width = SD_BUS_WIDTH_4;
- } else {
+ else
bus_width = SD_BUS_WIDTH_1;
- }
}
retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd,
@@ -2243,9 +2154,8 @@ static int sd_check_wp_state(struct rtsx_chip *chip)
retval = sd_send_cmd_get_rsp(chip, APP_CMD,
sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
cmd[0] = 0x40 | SD_STATUS;
cmd[1] = 0;
@@ -2273,9 +2183,8 @@ static int sd_check_wp_state(struct rtsx_chip *chip)
/* Check SD Machanical Write-Protect Switch */
val = rtsx_readl(chip, RTSX_BIPR);
- if (val & SD_WRITE_PROTECT) {
+ if (val & SD_WRITE_PROTECT)
chip->card_wp |= SD_CARD;
- }
return STATUS_SUCCESS;
}
@@ -2307,14 +2216,12 @@ Switch_Fail:
#endif
retval = sd_prepare_reset(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = sd_dummy_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip) && try_sdio) {
int rty_cnt = 0;
@@ -2348,9 +2255,8 @@ Switch_Fail:
/* Start Initialization Process of SD Card */
RTY_SD_RST:
retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0, SD_RSP_TYPE_R0, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
wait_timeout(20);
@@ -2377,9 +2283,8 @@ RTY_SD_RST:
voltage = SUPPORT_VOLTAGE;
retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0, SD_RSP_TYPE_R0, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
wait_timeout(20);
}
@@ -2393,42 +2298,38 @@ RTY_SD_RST:
}
j++;
- if (j < 3) {
+ if (j < 3)
goto RTY_SD_RST;
- } else {
+ else
TRACE_RET(chip, STATUS_FAIL);
- }
}
retval = sd_send_cmd_get_rsp(chip, SD_APP_OP_COND, voltage, SD_RSP_TYPE_R3, rsp, 5);
if (retval != STATUS_SUCCESS) {
k++;
- if (k < 3) {
+ if (k < 3)
goto RTY_SD_RST;
- } else {
+ else
TRACE_RET(chip, STATUS_FAIL);
- }
}
i++;
wait_timeout(20);
} while (!(rsp[1] & 0x80) && (i < 255));
- if (i == 255) {
+ if (i == 255)
TRACE_RET(chip, STATUS_FAIL);
- }
if (hi_cap_flow) {
- if (rsp[1] & 0x40) {
+ if (rsp[1] & 0x40)
SET_SD_HCXC(sd_card);
- } else {
+ else
CLR_SD_HCXC(sd_card);
- }
- if (CHECK_PID(chip, 0x5209) && CHK_SD_HCXC(sd_card) && !sd20_mode) {
+
+ if (CHECK_PID(chip, 0x5209) && CHK_SD_HCXC(sd_card) && !sd20_mode)
support_1v8 = (rsp[1] & 0x01) ? 1 : 0;
- } else {
+ else
support_1v8 = 0;
- }
} else {
CLR_SD_HCXC(sd_card);
support_1v8 = 0;
@@ -2437,46 +2338,39 @@ RTY_SD_RST:
if (support_1v8) {
retval = sd_voltage_switch(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
retval = sd_send_cmd_get_rsp(chip, ALL_SEND_CID, 0, SD_RSP_TYPE_R2, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
for (i = 0; i < 3; i++) {
retval = sd_send_cmd_get_rsp(chip, SEND_RELATIVE_ADDR, 0, SD_RSP_TYPE_R6, rsp, 5);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
sd_card->sd_addr = (u32)rsp[1] << 24;
sd_card->sd_addr += (u32)rsp[2] << 16;
- if (sd_card->sd_addr) {
+ if (sd_card->sd_addr)
break;
- }
}
retval = sd_check_csd(chip, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = sd_select_card(chip, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
#ifdef SUPPORT_SD_LOCK
SD_UNLOCK_ENTRY:
retval = sd_update_lock_status(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (sd_card->sd_lock_status & SD_LOCKED) {
sd_card->sd_lock_status |= (SD_LOCK_1BIT_MODE | SD_PWD_EXIST);
@@ -2487,23 +2381,21 @@ SD_UNLOCK_ENTRY:
#endif
retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = sd_send_cmd_get_rsp(chip, SET_CLR_CARD_DETECT, 0, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (support_1v8) {
retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = sd_send_cmd_get_rsp(chip, SET_BUS_WIDTH, 2, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
switch_bus_width = SD_BUS_WIDTH_4;
} else {
@@ -2511,14 +2403,12 @@ SD_UNLOCK_ENTRY:
}
retval = sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (!(sd_card->raw_csd[4] & 0x40))
sd_dont_switch = 1;
@@ -2537,9 +2427,9 @@ SD_UNLOCK_ENTRY:
if (retval == STATUS_SUCCESS) {
retval = sd_switch_function(chip, switch_bus_width);
if (retval != STATUS_SUCCESS) {
- if (CHECK_PID(chip, 0x5209)) {
+ if (CHECK_PID(chip, 0x5209))
sd_change_bank_voltage(chip, SD_IO_3V3);
- }
+
sd_init_power(chip);
sd_dont_switch = 1;
try_sdio = 0;
@@ -2548,9 +2438,9 @@ SD_UNLOCK_ENTRY:
}
} else {
if (support_1v8) {
- if (CHECK_PID(chip, 0x5209)) {
+ if (CHECK_PID(chip, 0x5209))
sd_change_bank_voltage(chip, SD_IO_3V3);
- }
+
sd_init_power(chip);
sd_dont_switch = 1;
try_sdio = 0;
@@ -2562,13 +2452,12 @@ SD_UNLOCK_ENTRY:
if (!support_1v8) {
retval = sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
retval = sd_send_cmd_get_rsp(chip, SET_BUS_WIDTH, 2, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
#ifdef SUPPORT_SD_LOCK
@@ -2581,24 +2470,22 @@ SD_UNLOCK_ENTRY:
RTSX_WRITE_REG(chip, SD30_DRIVE_SEL, 0x07, chip->sd30_drive_sel_1v8);
retval = sd_set_init_para(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (CHK_SD_DDR50(sd_card)) {
+ if (CHK_SD_DDR50(sd_card))
retval = sd_ddr_tuning(chip);
- } else {
+ else
retval = sd_sdr_tuning(chip);
- }
if (retval != STATUS_SUCCESS) {
if (sd20_mode) {
TRACE_RET(chip, STATUS_FAIL);
} else {
retval = sd_init_power(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
try_sdio = 0;
sd20_mode = 1;
goto Switch_Fail;
@@ -2609,9 +2496,8 @@ SD_UNLOCK_ENTRY:
if (CHK_SD_DDR50(sd_card)) {
retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
read_lba0 = 0;
- }
}
if (read_lba0) {
@@ -2621,9 +2507,9 @@ SD_UNLOCK_ENTRY:
TRACE_RET(chip, STATUS_FAIL);
} else {
retval = sd_init_power(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
try_sdio = 0;
sd20_mode = 1;
goto Switch_Fail;
@@ -2633,9 +2519,8 @@ SD_UNLOCK_ENTRY:
}
retval = sd_check_wp_state(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
chip->card_bus_width[chip->card2lun[SD_CARD]] = 4;
@@ -2659,9 +2544,8 @@ static int mmc_test_switch_bus(struct rtsx_chip *chip, u8 width)
int len;
retval = sd_send_cmd_get_rsp(chip, BUSTEST_W, 0, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, SWITCH_FAIL);
- }
if (width == MMC_8BIT_BUS) {
buf[0] = 0x55;
@@ -2690,9 +2574,8 @@ static int mmc_test_switch_bus(struct rtsx_chip *chip, u8 width)
rtsx_read_register(chip, REG_SD_STAT1, &val1);
rtsx_read_register(chip, REG_SD_STAT2, &val2);
rtsx_clear_sd_error(chip);
- if ((val1 & 0xE0) || val2) {
+ if ((val1 & 0xE0) || val2)
TRACE_RET(chip, SWITCH_ERR);
- }
} else {
rtsx_clear_sd_error(chip);
rtsx_write_register(chip, REG_SD_CFG3, 0x02, 0);
@@ -2712,11 +2595,10 @@ static int mmc_test_switch_bus(struct rtsx_chip *chip, u8 width)
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CMD0, 0xFF, 0x40 | BUSTEST_R);
- if (width == MMC_8BIT_BUS) {
+ if (width == MMC_8BIT_BUS)
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, 0x08);
- } else {
+ else
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BYTE_CNT_L, 0xFF, 0x04);
- }
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_L, 0xFF, 1);
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_BLOCK_CNT_H, 0xFF, 0);
@@ -2729,9 +2611,8 @@ static int mmc_test_switch_bus(struct rtsx_chip *chip, u8 width)
rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END, SD_TRANSFER_END);
rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2, 0, 0);
- if (width == MMC_8BIT_BUS) {
+ if (width == MMC_8BIT_BUS)
rtsx_add_cmd(chip, READ_REG_CMD, PPBUF_BASE2 + 1, 0, 0);
- }
retval = rtsx_send_cmd(chip, SD_CARD, 100);
if (retval < 0) {
@@ -2747,15 +2628,14 @@ static int mmc_test_switch_bus(struct rtsx_chip *chip, u8 width)
u8 rsp[5];
u32 arg;
- if (CHK_MMC_DDR52(sd_card)) {
+ if (CHK_MMC_DDR52(sd_card))
arg = 0x03B70600;
- } else {
+ else
arg = 0x03B70200;
- }
+
retval = sd_send_cmd_get_rsp(chip, SWITCH, arg, SD_RSP_TYPE_R1b, rsp, 5);
- if ((retval == STATUS_SUCCESS) && !(rsp[4] & MMC_SWITCH_ERR)) {
+ if ((retval == STATUS_SUCCESS) && !(rsp[4] & MMC_SWITCH_ERR))
return SWITCH_SUCCESS;
- }
}
} else {
RTSX_DEBUGP("BUSTEST_R [4bits]: 0x%02x\n", ptr[0]);
@@ -2763,15 +2643,14 @@ static int mmc_test_switch_bus(struct rtsx_chip *chip, u8 width)
u8 rsp[5];
u32 arg;
- if (CHK_MMC_DDR52(sd_card)) {
+ if (CHK_MMC_DDR52(sd_card))
arg = 0x03B70500;
- } else {
+ else
arg = 0x03B70100;
- }
+
retval = sd_send_cmd_get_rsp(chip, SWITCH, arg, SD_RSP_TYPE_R1b, rsp, 5);
- if ((retval == STATUS_SUCCESS) && !(rsp[4] & MMC_SWITCH_ERR)) {
+ if ((retval == STATUS_SUCCESS) && !(rsp[4] & MMC_SWITCH_ERR))
return SWITCH_SUCCESS;
- }
}
}
@@ -2845,11 +2724,10 @@ static int mmc_switch_timing_bus(struct rtsx_chip *chip, int switch_ddr)
card_type_mask = 0x03;
}
#else
- if (chip->sd_ctl & SUPPORT_MMC_DDR_MODE) {
+ if (chip->sd_ctl & SUPPORT_MMC_DDR_MODE)
card_type_mask = 0x07;
- } else {
+ else
card_type_mask = 0x03;
- }
#endif
} else {
card_type_mask = 0x03;
@@ -2859,11 +2737,10 @@ static int mmc_switch_timing_bus(struct rtsx_chip *chip, int switch_ddr)
u8 rsp[5];
if (card_type & 0x04) {
- if (switch_ddr) {
+ if (switch_ddr)
SET_MMC_DDR52(sd_card);
- } else {
+ else
SET_MMC_52M(sd_card);
- }
} else if (card_type & 0x02) {
SET_MMC_52M(sd_card);
} else {
@@ -2872,16 +2749,14 @@ static int mmc_switch_timing_bus(struct rtsx_chip *chip, int switch_ddr)
retval = sd_send_cmd_get_rsp(chip, SWITCH,
0x03B90100, SD_RSP_TYPE_R1b, rsp, 5);
- if ((retval != STATUS_SUCCESS) || (rsp[4] & MMC_SWITCH_ERR)) {
+ if ((retval != STATUS_SUCCESS) || (rsp[4] & MMC_SWITCH_ERR))
CLR_MMC_HS(sd_card);
- }
}
sd_choose_proper_clock(chip);
retval = switch_clock(chip, sd_card->sd_clock);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
/* Test Bus Procedure */
retval = mmc_test_switch_bus(chip, MMC_8BIT_BUS);
@@ -2929,17 +2804,15 @@ static int reset_mmc(struct rtsx_chip *chip)
Switch_Fail:
retval = sd_prepare_reset(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, retval);
- }
SET_MMC(sd_card);
RTY_MMC_RST:
retval = sd_send_cmd_get_rsp(chip, GO_IDLE_STATE, 0, SD_RSP_TYPE_R0, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
do {
if (detect_card_cd(chip, SD_CARD) != STATUS_SUCCESS) {
@@ -2973,56 +2846,47 @@ RTY_MMC_RST:
i++;
} while (!(rsp[1] & 0x80) && (i < 255));
- if (i == 255) {
+ if (i == 255)
TRACE_RET(chip, STATUS_FAIL);
- }
- if ((rsp[1] & 0x60) == 0x40) {
+ if ((rsp[1] & 0x60) == 0x40)
SET_MMC_SECTOR_MODE(sd_card);
- } else {
+ else
CLR_MMC_SECTOR_MODE(sd_card);
- }
retval = sd_send_cmd_get_rsp(chip, ALL_SEND_CID, 0, SD_RSP_TYPE_R2, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
sd_card->sd_addr = 0x00100000;
retval = sd_send_cmd_get_rsp(chip, SET_RELATIVE_ADDR, sd_card->sd_addr, SD_RSP_TYPE_R6, rsp, 5);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = sd_check_csd(chip, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
spec_ver = (sd_card->raw_csd[0] & 0x3C) >> 2;
retval = sd_select_card(chip, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
#ifdef SUPPORT_SD_LOCK
MMC_UNLOCK_ENTRY:
retval = sd_update_lock_status(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
#endif
retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
chip->card_bus_width[chip->card2lun[SD_CARD]] = 1;
@@ -3039,22 +2903,20 @@ MMC_UNLOCK_ENTRY:
}
}
- if (CHK_MMC_SECTOR_MODE(sd_card) && (sd_card->capacity == 0)) {
+ if (CHK_MMC_SECTOR_MODE(sd_card) && (sd_card->capacity == 0))
TRACE_RET(chip, STATUS_FAIL);
- }
if (switch_ddr && CHK_MMC_DDR52(sd_card)) {
retval = sd_set_init_para(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = mmc_ddr_tuning(chip);
if (retval != STATUS_SUCCESS) {
retval = sd_init_power(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
switch_ddr = 0;
TRACE_GOTO(chip, Switch_Fail);
}
@@ -3064,9 +2926,9 @@ MMC_UNLOCK_ENTRY:
retval = sd_read_lba0(chip);
if (retval != STATUS_SUCCESS) {
retval = sd_init_power(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
switch_ddr = 0;
TRACE_GOTO(chip, Switch_Fail);
}
@@ -3082,9 +2944,8 @@ MMC_UNLOCK_ENTRY:
#endif
temp = rtsx_readl(chip, RTSX_BIPR);
- if (temp & SD_WRITE_PROTECT) {
+ if (temp & SD_WRITE_PROTECT)
chip->card_wp |= SD_CARD;
- }
return STATUS_SUCCESS;
}
@@ -3100,36 +2961,31 @@ int reset_sd_card(struct rtsx_chip *chip)
chip->capacity[chip->card2lun[SD_CARD]] = 0;
retval = enable_card_clock(chip, SD_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (chip->ignore_sd && CHK_SDIO_EXIST(chip) && !CHK_SDIO_IGNORED(chip)) {
if (chip->asic_code) {
retval = sd_pull_ctl_enable(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
retval = rtsx_write_register(chip, FPGA_PULL_CTL,
FPGA_SD_PULL_CTL_BIT | 0x20, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
}
retval = card_share_mode(chip, SD_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
chip->sd_io = 1;
TRACE_RET(chip, STATUS_FAIL);
}
retval = sd_init_power(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (chip->sd_ctl & RESET_MMC_FIRST) {
retval = reset_mmc(chip);
@@ -3168,18 +3024,17 @@ int reset_sd_card(struct rtsx_chip *chip)
}
retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
RTSX_WRITE_REG(chip, REG_SD_BYTE_CNT_L, 0xFF, 0);
RTSX_WRITE_REG(chip, REG_SD_BYTE_CNT_H, 0xFF, 2);
chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity;
retval = sd_set_init_para(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_DEBUGP("sd_card->sd_type = 0x%x\n", sd_card->sd_type);
@@ -3205,33 +3060,29 @@ static int reset_mmc_only(struct rtsx_chip *chip)
chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity = 0;
retval = enable_card_clock(chip, SD_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = sd_init_power(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = reset_mmc(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
retval = sd_set_clock_divider(chip, SD_CLK_DIVIDE_0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
+
RTSX_WRITE_REG(chip, REG_SD_BYTE_CNT_L, 0xFF, 0);
RTSX_WRITE_REG(chip, REG_SD_BYTE_CNT_H, 0xFF, 2);
chip->capacity[chip->card2lun[SD_CARD]] = sd_card->capacity;
retval = sd_set_init_para(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_DEBUGP("In reset_mmc_only, sd_card->sd_type = 0x%x\n", sd_card->sd_type);
@@ -3255,9 +3106,8 @@ static int wait_data_buf_ready(struct rtsx_chip *chip)
retval = sd_send_cmd_get_rsp(chip, SEND_STATUS,
sd_card->sd_addr, SD_RSP_TYPE_R1, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (sd_card->sd_data_buf_ready) {
return sd_send_cmd_get_rsp(chip, SEND_STATUS,
@@ -3277,19 +3127,18 @@ void sd_stop_seq_mode(struct rtsx_chip *chip)
if (sd_card->seq_mode) {
retval = sd_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
return;
- }
retval = sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION, 0,
SD_RSP_TYPE_R1b, NULL, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
sd_set_err_code(chip, SD_STS_ERR);
- }
+
retval = sd_wait_state_data_ready(chip, 0x08, 1, 1000);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
sd_set_err_code(chip, SD_STS_ERR);
- }
+
sd_card->seq_mode = 0;
rtsx_write_register(chip, RBCTL, RB_FLUSH, RB_FLUSH);
@@ -3302,9 +3151,8 @@ static inline int sd_auto_tune_clock(struct rtsx_chip *chip)
int retval;
if (chip->asic_code) {
- if (sd_card->sd_clock > 30) {
+ if (sd_card->sd_clock > 30)
sd_card->sd_clock -= 20;
- }
} else {
switch (sd_card->sd_clock) {
case CLK_200:
@@ -3337,9 +3185,8 @@ static inline int sd_auto_tune_clock(struct rtsx_chip *chip)
}
retval = sd_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
return STATUS_SUCCESS;
}
@@ -3377,11 +3224,10 @@ int sd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 start_sector, u16 s
}
}
- if (!CHK_SD_HCXC(sd_card) && !CHK_MMC_SECTOR_MODE(sd_card)) {
+ if (!CHK_SD_HCXC(sd_card) && !CHK_MMC_SECTOR_MODE(sd_card))
data_addr = start_sector << 9;
- } else {
+ else
data_addr = start_sector;
- }
sd_clr_err_code(chip);
@@ -3436,21 +3282,19 @@ int sd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 start_sector, u16 s
rtsx_add_cmd(chip, WRITE_REG_CMD, CARD_DATA_SOURCE, 0x01, RING_BUFFER);
- if (CHK_MMC_8BIT(sd_card)) {
+ if (CHK_MMC_8BIT(sd_card))
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_8);
- } else if (CHK_MMC_4BIT(sd_card) || CHK_SD(sd_card)) {
+ else if (CHK_MMC_4BIT(sd_card) || CHK_SD(sd_card))
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_4);
- } else {
+ else
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_1);
- }
if (sd_card->seq_mode) {
cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END |
SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
if (CHECK_PID(chip, 0x5209)) {
- if (!CHK_SD30_SPEED(sd_card)) {
+ if (!CHK_SD30_SPEED(sd_card))
cfg2 |= SD_NO_CHECK_WAIT_CRC_TO;
- }
}
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, cfg2);
@@ -3480,9 +3324,8 @@ int sd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 start_sector, u16 s
cfg2 = SD_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END |
SD_CHECK_CRC7 | SD_RSP_LEN_6;
if (CHECK_PID(chip, 0x5209)) {
- if (!CHK_SD30_SPEED(sd_card)) {
+ if (!CHK_SD30_SPEED(sd_card))
cfg2 |= SD_NO_CHECK_WAIT_CRC_TO;
- }
}
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, cfg2);
@@ -3523,9 +3366,8 @@ int sd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 start_sector, u16 s
cfg2 = SD_NO_CALCULATE_CRC7 | SD_CHECK_CRC16 | SD_NO_WAIT_BUSY_END |
SD_NO_CHECK_CRC7 | SD_RSP_LEN_0;
if (CHECK_PID(chip, 0x5209)) {
- if (!CHK_SD30_SPEED(sd_card)) {
+ if (!CHK_SD30_SPEED(sd_card))
cfg2 |= SD_NO_CHECK_WAIT_CRC_TO;
- }
}
rtsx_add_cmd(chip, WRITE_REG_CMD, REG_SD_CFG2, 0xFF, cfg2);
@@ -3550,11 +3392,10 @@ int sd_rw(struct scsi_cmnd *srb, struct rtsx_chip *chip, u32 start_sector, u16 s
sd_card->seq_mode = 0;
- if (retval == -ETIMEDOUT) {
+ if (retval == -ETIMEDOUT)
err = STATUS_TIMEDOUT;
- } else {
+ else
err = STATUS_FAIL;
- }
rtsx_read_register(chip, REG_SD_STAT1, &stat);
rtsx_clear_sd_error(chip);
@@ -3640,9 +3481,8 @@ int ext_sd_send_cmd_get_rsp(struct rtsx_chip *chip, u8 cmd_idx,
RTSX_DEBUGP("EXT SD/MMC CMD %d\n", cmd_idx);
- if (rsp_type == SD_RSP_TYPE_R1b) {
+ if (rsp_type == SD_RSP_TYPE_R1b)
timeout = 3000;
- }
RTY_SEND_CMD:
@@ -3662,14 +3502,14 @@ RTY_SEND_CMD:
rtsx_add_cmd(chip, CHECK_REG_CMD, REG_SD_TRANSFER, SD_TRANSFER_END, SD_TRANSFER_END);
if (rsp_type == SD_RSP_TYPE_R2) {
- for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16; reg_addr++) {
+ for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16; reg_addr++)
rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
- }
+
stat_idx = 17;
} else if (rsp_type != SD_RSP_TYPE_R0) {
- for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4; reg_addr++) {
+ for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4; reg_addr++)
rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0, 0);
- }
+
stat_idx = 6;
}
rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_CMD5, 0, 0);
@@ -3683,9 +3523,8 @@ RTY_SEND_CMD:
if (rsp_type & SD_WAIT_BUSY_END) {
retval = sd_check_data0_status(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, retval);
- }
} else {
sd_set_err_code(chip, SD_TO_ERR);
}
@@ -3693,9 +3532,8 @@ RTY_SEND_CMD:
TRACE_RET(chip, STATUS_FAIL);
}
- if (rsp_type == SD_RSP_TYPE_R0) {
+ if (rsp_type == SD_RSP_TYPE_R0)
return STATUS_SUCCESS;
- }
ptr = rtsx_get_cmd_data(chip) + 1;
@@ -3724,9 +3562,8 @@ RTY_SEND_CMD:
if ((cmd_idx == SELECT_CARD) || (cmd_idx == APP_CMD) ||
(cmd_idx == SEND_STATUS) || (cmd_idx == STOP_TRANSMISSION)) {
if ((cmd_idx != STOP_TRANSMISSION) && (special_check == 0)) {
- if (ptr[1] & 0x80) {
+ if (ptr[1] & 0x80)
TRACE_RET(chip, STATUS_FAIL);
- }
}
#ifdef SUPPORT_SD_LOCK
if (ptr[1] & 0x7D)
@@ -3736,26 +3573,23 @@ RTY_SEND_CMD:
{
TRACE_RET(chip, STATUS_FAIL);
}
- if (ptr[2] & 0xF8) {
+ if (ptr[2] & 0xF8)
TRACE_RET(chip, STATUS_FAIL);
- }
if (cmd_idx == SELECT_CARD) {
if (rsp_type == SD_RSP_TYPE_R2) {
- if ((ptr[3] & 0x1E) != 0x04) {
+ if ((ptr[3] & 0x1E) != 0x04)
TRACE_RET(chip, STATUS_FAIL);
- }
+
} else if (rsp_type == SD_RSP_TYPE_R0) {
- if ((ptr[3] & 0x1E) != 0x03) {
+ if ((ptr[3] & 0x1E) != 0x03)
TRACE_RET(chip, STATUS_FAIL);
- }
}
}
}
- if (rsp && rsp_len) {
+ if (rsp && rsp_len)
memcpy(rsp, ptr, rsp_len);
- }
return STATUS_SUCCESS;
}
@@ -3765,29 +3599,27 @@ int ext_sd_get_rsp(struct rtsx_chip *chip, int len, u8 *rsp, u8 rsp_type)
int retval, rsp_len;
u16 reg_addr;
- if (rsp_type == SD_RSP_TYPE_R0) {
+ if (rsp_type == SD_RSP_TYPE_R0)
return STATUS_SUCCESS;
- }
rtsx_init_cmd(chip);
if (rsp_type == SD_RSP_TYPE_R2) {
- for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16; reg_addr++) {
+ for (reg_addr = PPBUF_BASE2; reg_addr < PPBUF_BASE2 + 16; reg_addr++)
rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0xFF, 0);
- }
+
rsp_len = 17;
} else if (rsp_type != SD_RSP_TYPE_R0) {
- for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4; reg_addr++) {
+ for (reg_addr = REG_SD_CMD0; reg_addr <= REG_SD_CMD4; reg_addr++)
rtsx_add_cmd(chip, READ_REG_CMD, reg_addr, 0xFF, 0);
- }
+
rsp_len = 6;
}
rtsx_add_cmd(chip, READ_REG_CMD, REG_SD_CMD5, 0xFF, 0);
retval = rtsx_send_cmd(chip, SD_CARD, 100);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (rsp) {
int min_len = (rsp_len < len) ? rsp_len : len;
@@ -3858,9 +3690,8 @@ int sd_pass_thru_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
buf[5] = (1 == CHK_SD(sd_card)) ? 0x01 : 0x02;
- if (chip->card_wp & SD_CARD) {
+ if (chip->card_wp & SD_CARD)
buf[5] |= 0x80;
- }
buf[6] = (u8)(sd_card->sd_addr >> 16);
buf[7] = (u8)(sd_card->sd_addr >> 24);
@@ -3875,9 +3706,8 @@ int sd_pass_thru_mode(struct scsi_cmnd *srb, struct rtsx_chip *chip)
static inline int get_rsp_type(struct scsi_cmnd *srb, u8 *rsp_type, int *rsp_len)
{
- if (!rsp_type || !rsp_len) {
+ if (!rsp_type || !rsp_len)
return STATUS_FAIL;
- }
switch (srb->cmnd[10]) {
case 0x03:
@@ -3927,9 +3757,8 @@ int sd_execute_no_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
retval = sd_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
if (sd_card->pre_cmd_err) {
sd_card->pre_cmd_err = 0;
@@ -3938,12 +3767,11 @@ int sd_execute_no_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
cmd_idx = srb->cmnd[2] & 0x3F;
- if (srb->cmnd[1] & 0x02) {
+ if (srb->cmnd[1] & 0x02)
standby = 1;
- }
- if (srb->cmnd[1] & 0x01) {
+
+ if (srb->cmnd[1] & 0x01)
acmd = 1;
- }
arg = ((u32)srb->cmnd[3] << 24) | ((u32)srb->cmnd[4] << 16) |
((u32)srb->cmnd[5] << 8) | srb->cmnd[6];
@@ -3956,64 +3784,56 @@ int sd_execute_no_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
sd_card->last_rsp_type = rsp_type;
retval = sd_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
#ifdef SUPPORT_SD_LOCK
if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) {
if (CHK_MMC_8BIT(sd_card)) {
retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_8);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
} else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) {
retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_4);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
}
}
#else
retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_4);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
#endif
if (standby) {
retval = sd_select_card(chip, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Cmd_Failed);
- }
}
if (acmd) {
retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
SD_RSP_TYPE_R1, NULL, 0, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Cmd_Failed);
- }
}
retval = ext_sd_send_cmd_get_rsp(chip, cmd_idx, arg, rsp_type,
sd_card->rsp, rsp_len, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Cmd_Failed);
- }
if (standby) {
retval = sd_select_card(chip, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Cmd_Failed);
- }
}
#ifdef SUPPORT_SD_LOCK
retval = sd_update_lock_status(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Cmd_Failed);
- }
#endif
scsi_set_resid(srb, 0);
@@ -4024,9 +3844,8 @@ SD_Execute_Cmd_Failed:
set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
release_sd_card(chip);
do_reset_sd_card(chip);
- if (!(chip->card_ready & SD_CARD)) {
+ if (!(chip->card_ready & SD_CARD))
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
- }
TRACE_RET(chip, TRANSPORT_FAILED);
}
@@ -4053,20 +3872,18 @@ int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
retval = sd_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
cmd_idx = srb->cmnd[2] & 0x3F;
- if (srb->cmnd[1] & 0x04) {
+ if (srb->cmnd[1] & 0x04)
send_cmd12 = 1;
- }
- if (srb->cmnd[1] & 0x02) {
+
+ if (srb->cmnd[1] & 0x02)
standby = 1;
- }
- if (srb->cmnd[1] & 0x01) {
+
+ if (srb->cmnd[1] & 0x01)
acmd = 1;
- }
data_len = ((u32)srb->cmnd[7] << 16) | ((u32)srb->cmnd[8] << 8) | srb->cmnd[9];
@@ -4078,19 +3895,17 @@ int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
sd_card->last_rsp_type = rsp_type;
retval = sd_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
#ifdef SUPPORT_SD_LOCK
if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) {
- if (CHK_MMC_8BIT(sd_card)) {
+ if (CHK_MMC_8BIT(sd_card))
bus_width = SD_BUS_WIDTH_8;
- } else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) {
+ else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card))
bus_width = SD_BUS_WIDTH_4;
- } else {
+ else
bus_width = SD_BUS_WIDTH_1;
- }
} else {
bus_width = SD_BUS_WIDTH_4;
}
@@ -4102,24 +3917,21 @@ int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
if (data_len < 512) {
retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, data_len,
SD_RSP_TYPE_R1, NULL, 0, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
- }
}
if (standby) {
retval = sd_select_card(chip, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
- }
}
if (acmd) {
retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
SD_RSP_TYPE_R1, NULL, 0, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
- }
}
if (data_len <= 512) {
@@ -4138,9 +3950,8 @@ int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
cmd[4] = srb->cmnd[6];
buf = kmalloc(data_len, GFP_KERNEL);
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
retval = sd_read_data(chip, SD_TM_NORMAL_READ, cmd, 5, byte_cnt,
blk_cnt, bus_width, buf, data_len, 2000);
@@ -4195,56 +4006,48 @@ int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
retval = ext_sd_get_rsp(chip, rsp_len, sd_card->rsp, rsp_type);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
- }
if (standby) {
retval = sd_select_card(chip, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
- }
}
if (send_cmd12) {
retval = ext_sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION,
0, SD_RSP_TYPE_R1b, NULL, 0, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
- }
}
if (data_len < 512) {
retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200,
SD_RSP_TYPE_R1, NULL, 0, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
- }
retval = rtsx_write_register(chip, SD_BYTE_CNT_H, 0xFF, 0x02);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
- }
+
retval = rtsx_write_register(chip, SD_BYTE_CNT_L, 0xFF, 0x00);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
- }
}
- if ((srb->cmnd[1] & 0x02) || (srb->cmnd[1] & 0x04)) {
+ if ((srb->cmnd[1] & 0x02) || (srb->cmnd[1] & 0x04))
cmd13_checkbit = 1;
- }
for (i = 0; i < 3; i++) {
retval = ext_sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
SD_RSP_TYPE_R1, NULL, 0, cmd13_checkbit);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
break;
- }
}
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Read_Cmd_Failed);
- }
scsi_set_resid(srb, 0);
return TRANSPORT_GOOD;
@@ -4252,14 +4055,13 @@ int sd_execute_read_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
SD_Execute_Read_Cmd_Failed:
sd_card->pre_cmd_err = 1;
set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
- if (read_err) {
+ if (read_err)
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_UNRECOVER_READ_ERR);
- }
+
release_sd_card(chip);
do_reset_sd_card(chip);
- if (!(chip->card_ready & SD_CARD)) {
+ if (!(chip->card_ready & SD_CARD))
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
- }
TRACE_RET(chip, TRANSPORT_FAILED);
}
@@ -4291,20 +4093,18 @@ int sd_execute_write_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
retval = sd_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
cmd_idx = srb->cmnd[2] & 0x3F;
- if (srb->cmnd[1] & 0x04) {
+ if (srb->cmnd[1] & 0x04)
send_cmd12 = 1;
- }
- if (srb->cmnd[1] & 0x02) {
+
+ if (srb->cmnd[1] & 0x02)
standby = 1;
- }
- if (srb->cmnd[1] & 0x01) {
+
+ if (srb->cmnd[1] & 0x01)
acmd = 1;
- }
data_len = ((u32)srb->cmnd[7] << 16) | ((u32)srb->cmnd[8] << 8) | srb->cmnd[9];
arg = ((u32)srb->cmnd[3] << 24) | ((u32)srb->cmnd[4] << 16) |
@@ -4325,75 +4125,66 @@ int sd_execute_write_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
sd_card->last_rsp_type = rsp_type;
retval = sd_switch_clock(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
#ifdef SUPPORT_SD_LOCK
if ((sd_card->sd_lock_status & SD_LOCK_1BIT_MODE) == 0) {
if (CHK_MMC_8BIT(sd_card)) {
retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_8);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
+
} else if (CHK_SD(sd_card) || CHK_MMC_4BIT(sd_card)) {
retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_4);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
}
}
#else
retval = rtsx_write_register(chip, REG_SD_CFG1, 0x03, SD_BUS_WIDTH_4);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, TRANSPORT_FAILED);
- }
#endif
if (data_len < 512) {
retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, data_len,
SD_RSP_TYPE_R1, NULL, 0, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
- }
}
if (standby) {
retval = sd_select_card(chip, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
- }
}
if (acmd) {
retval = ext_sd_send_cmd_get_rsp(chip, APP_CMD, sd_card->sd_addr,
SD_RSP_TYPE_R1, NULL, 0, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
- }
}
retval = ext_sd_send_cmd_get_rsp(chip, cmd_idx, arg, rsp_type,
sd_card->rsp, rsp_len, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
- }
if (data_len <= 512) {
u16 i;
u8 *buf;
buf = kmalloc(data_len, GFP_KERNEL);
- if (buf == NULL) {
+ if (buf == NULL)
TRACE_RET(chip, TRANSPORT_ERROR);
- }
rtsx_stor_get_xfer_buf(buf, data_len, srb);
#ifdef SUPPORT_SD_LOCK
- if (cmd_idx == LOCK_UNLOCK) {
+ if (cmd_idx == LOCK_UNLOCK)
lock_cmd_type = buf[0] & 0x0F;
- }
#endif
if (data_len > 256) {
@@ -4485,11 +4276,11 @@ int sd_execute_write_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
}
rtsx_init_cmd(chip);
- if (CHECK_PID(chip, 0x5209)) {
+ if (CHECK_PID(chip, 0x5209))
rtsx_add_cmd(chip, CHECK_REG_CMD, SD_BUS_STAT, SD_DAT0_STATUS, SD_DAT0_STATUS);
- } else {
+ else
rtsx_add_cmd(chip, CHECK_REG_CMD, 0xFD30, 0x02, 0x02);
- }
+
rtsx_send_cmd(chip, SD_CARD, 250);
retval = sd_update_lock_status(chip);
@@ -4502,61 +4293,53 @@ int sd_execute_write_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
if (standby) {
retval = sd_select_card(chip, 1);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
- }
}
if (send_cmd12) {
retval = ext_sd_send_cmd_get_rsp(chip, STOP_TRANSMISSION,
0, SD_RSP_TYPE_R1b, NULL, 0, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
- }
}
if (data_len < 512) {
retval = ext_sd_send_cmd_get_rsp(chip, SET_BLOCKLEN, 0x200,
SD_RSP_TYPE_R1, NULL, 0, 0);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
- }
retval = rtsx_write_register(chip, SD_BYTE_CNT_H, 0xFF, 0x02);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
- }
+
rtsx_write_register(chip, SD_BYTE_CNT_L, 0xFF, 0x00);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
- }
}
- if ((srb->cmnd[1] & 0x02) || (srb->cmnd[1] & 0x04)) {
+ if ((srb->cmnd[1] & 0x02) || (srb->cmnd[1] & 0x04))
cmd13_checkbit = 1;
- }
for (i = 0; i < 3; i++) {
retval = ext_sd_send_cmd_get_rsp(chip, SEND_STATUS, sd_card->sd_addr,
SD_RSP_TYPE_R1, NULL, 0, cmd13_checkbit);
- if (retval == STATUS_SUCCESS) {
+ if (retval == STATUS_SUCCESS)
break;
- }
}
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_GOTO(chip, SD_Execute_Write_Cmd_Failed);
- }
#ifdef SUPPORT_SD_LOCK
if (cmd_idx == LOCK_UNLOCK) {
if (!lock_cmd_fail) {
RTSX_DEBUGP("lock_cmd_type = 0x%x\n", lock_cmd_type);
- if (lock_cmd_type & SD_CLR_PWD) {
+ if (lock_cmd_type & SD_CLR_PWD)
sd_card->sd_lock_status &= ~SD_PWD_EXIST;
- }
- if (lock_cmd_type & SD_SET_PWD) {
+
+ if (lock_cmd_type & SD_SET_PWD)
sd_card->sd_lock_status |= SD_PWD_EXIST;
- }
}
RTSX_DEBUGP("sd_lock_state = 0x%x, sd_card->sd_lock_status = 0x%x\n",
@@ -4593,14 +4376,13 @@ int sd_execute_write_data(struct scsi_cmnd *srb, struct rtsx_chip *chip)
SD_Execute_Write_Cmd_Failed:
sd_card->pre_cmd_err = 1;
set_sense_type(chip, lun, SENSE_TYPE_NO_SENSE);
- if (write_err) {
+ if (write_err)
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_WRITE_ERR);
- }
+
release_sd_card(chip);
do_reset_sd_card(chip);
- if (!(chip->card_ready & SD_CARD)) {
+ if (!(chip->card_ready & SD_CARD))
set_sense_type(chip, lun, SENSE_TYPE_MEDIA_NOT_PRESENT);
- }
TRACE_RET(chip, TRANSPORT_FAILED);
}
@@ -4670,9 +4452,8 @@ int sd_hw_rst(struct scsi_cmnd *srb, struct rtsx_chip *chip)
switch (srb->cmnd[1] & 0x0F) {
case 0:
#ifdef SUPPORT_SD_LOCK
- if (0x64 == srb->cmnd[9]) {
+ if (0x64 == srb->cmnd[9])
sd_card->sd_lock_status |= SD_SDR_RST;
- }
#endif
retval = reset_sd_card(chip);
if (retval != STATUS_SUCCESS) {
@@ -4723,26 +4504,23 @@ int sd_power_off_card3v3(struct rtsx_chip *chip)
int retval;
retval = disable_card_clock(chip, SD_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
RTSX_WRITE_REG(chip, CARD_OE, SD_OUTPUT_EN, 0);
if (!chip->ft2_fast_mode) {
retval = card_power_off(chip, SD_CARD);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
wait_timeout(50);
}
if (chip->asic_code) {
retval = sd_pull_ctl_disable(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
} else {
RTSX_WRITE_REG(chip, FPGA_PULL_CTL,
FPGA_SD_PULL_CTL_BIT | 0x20, FPGA_SD_PULL_CTL_BIT);
@@ -4774,19 +4552,16 @@ int release_sd_card(struct rtsx_chip *chip)
memset(sd_card->raw_scr, 0, 8);
retval = sd_power_off_card3v3(chip);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
if (CHECK_PID(chip, 0x5209)) {
retval = sd_change_bank_voltage(chip, SD_IO_3V3);
- if (retval != STATUS_SUCCESS) {
+ if (retval != STATUS_SUCCESS)
TRACE_RET(chip, STATUS_FAIL);
- }
- if (CHK_SD30_SPEED(sd_card)) {
+ if (CHK_SD30_SPEED(sd_card))
RTSX_WRITE_REG(chip, SD30_DRIVE_SEL, 0x07, chip->sd30_drive_sel_3v3);
- }
RTSX_WRITE_REG(chip, OCPPARA2, SD_OCP_THD_MASK, chip->sd_400mA_ocp_thd);
}
diff --git a/drivers/staging/rts_pstor/trace.h b/drivers/staging/rts_pstor/trace.h
index bc83b49a4eb4..cf60a1b872b3 100644
--- a/drivers/staging/rts_pstor/trace.h
+++ b/drivers/staging/rts_pstor/trace.h
@@ -83,33 +83,9 @@ do { \
#endif
#ifdef CONFIG_RTS_PSTOR_DEBUG
-static inline void rtsx_dump(u8 *buf, int buf_len)
-{
- int i;
- u8 tmp[16] = {0};
- u8 *_ptr = buf;
-
- for (i = 0; i < ((buf_len)/16); i++) {
- RTSX_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- _ptr[0], _ptr[1], _ptr[2], _ptr[3], _ptr[4], _ptr[5],
- _ptr[6], _ptr[7], _ptr[8], _ptr[9], _ptr[10], _ptr[11],
- _ptr[12], _ptr[13], _ptr[14], _ptr[15]);
- _ptr += 16;
- }
- if ((buf_len) % 16) {
- memcpy(tmp, _ptr, (buf_len) % 16);
- _ptr = tmp;
- RTSX_DEBUGP("%02x %02x %02x %02x %02x %02x %02x %02x "
- "%02x %02x %02x %02x %02x %02x %02x %02x\n",
- _ptr[0], _ptr[1], _ptr[2], _ptr[3], _ptr[4], _ptr[5],
- _ptr[6], _ptr[7], _ptr[8], _ptr[9], _ptr[10], _ptr[11],
- _ptr[12], _ptr[13], _ptr[14], _ptr[15]);
- }
-}
-
-#define RTSX_DUMP(buf, buf_len) rtsx_dump((u8 *)(buf), (buf_len))
-
+#define RTSX_DUMP(buf, buf_len) \
+ print_hex_dump(KERN_DEBUG, RTSX_STOR, DUMP_PREFIX_NONE, \
+ 16, 1, (buf), (buf_len), false)
#else
#define RTSX_DUMP(buf, buf_len)
#endif
diff --git a/drivers/staging/sbe-2t3e3/2t3e3.h b/drivers/staging/sbe-2t3e3/2t3e3.h
index 383f2cfc1ad2..ccad049c1122 100644
--- a/drivers/staging/sbe-2t3e3/2t3e3.h
+++ b/drivers/staging/sbe-2t3e3/2t3e3.h
@@ -789,7 +789,6 @@ void dc_restart(struct channel *);
void dc_receiver_onoff(struct channel *, u32);
void dc_transmitter_onoff(struct channel *, u32);
void dc_set_loopback(struct channel *, u32);
-u32 dc_init_descriptor_list(struct channel *);
void dc_clear_descriptor_list(struct channel *);
void dc_drop_descriptor_list(struct channel *);
void dc_set_output_port(struct channel *);
diff --git a/drivers/staging/sbe-2t3e3/dc.c b/drivers/staging/sbe-2t3e3/dc.c
index 9e81d9036a33..daadd6ea4978 100644
--- a/drivers/staging/sbe-2t3e3/dc.c
+++ b/drivers/staging/sbe-2t3e3/dc.c
@@ -17,6 +17,8 @@
#include "2t3e3.h"
#include "ctrl.h"
+static int dc_init_descriptor_list(struct channel *sc);
+
void dc_init(struct channel *sc)
{
u32 val;
@@ -307,7 +309,7 @@ void dc_set_loopback(struct channel *sc, u32 mode)
SBE_2T3E3_21143_VAL_FULL_DUPLEX_MODE);
}
-u32 dc_init_descriptor_list(struct channel *sc)
+static int dc_init_descriptor_list(struct channel *sc)
{
u32 i, j;
struct sk_buff *m;
@@ -317,7 +319,7 @@ u32 dc_init_descriptor_list(struct channel *sc)
sizeof(t3e3_rx_desc_t), GFP_KERNEL);
if (sc->ether.rx_ring == NULL) {
dev_err(&sc->pdev->dev, "SBE 2T3E3: no buffer space for RX ring\n");
- return ENOMEM;
+ return -ENOMEM;
}
if (sc->ether.tx_ring == NULL)
@@ -327,7 +329,7 @@ u32 dc_init_descriptor_list(struct channel *sc)
kfree(sc->ether.rx_ring);
sc->ether.rx_ring = NULL;
dev_err(&sc->pdev->dev, "SBE 2T3E3: no buffer space for RX ring\n");
- return ENOMEM;
+ return -ENOMEM;
}
@@ -351,7 +353,7 @@ u32 dc_init_descriptor_list(struct channel *sc)
sc->ether.tx_ring = NULL;
dev_err(&sc->pdev->dev, "SBE 2T3E3: token_alloc err:"
" no buffer space for RX ring\n");
- return ENOBUFS;
+ return -ENOBUFS;
}
sc->ether.rx_data[i] = m;
}
diff --git a/drivers/staging/sbe-2t3e3/module.c b/drivers/staging/sbe-2t3e3/module.c
index cd778b3a02b2..8adb17816ad9 100644
--- a/drivers/staging/sbe-2t3e3/module.c
+++ b/drivers/staging/sbe-2t3e3/module.c
@@ -67,6 +67,7 @@ static int __devinit t3e3_init_channel(struct channel *channel, struct pci_dev *
dev = alloc_hdlcdev(channel);
if (!dev) {
printk(KERN_ERR "SBE 2T3E3" ": Out of memory\n");
+ err = -ENOMEM;
goto free_regions;
}
@@ -82,8 +83,9 @@ static int __devinit t3e3_init_channel(struct channel *channel, struct pci_dev *
else
channel->h.slot = 0;
- if (setup_device(dev, channel))
- goto free_regions;
+ err = setup_device(dev, channel);
+ if (err)
+ goto free_dev;
pci_read_config_dword(channel->pdev, 0x40, &val); /* mask sleep mode */
pci_write_config_dword(channel->pdev, 0x40, val & 0x3FFFFFFF);
@@ -92,14 +94,19 @@ static int __devinit t3e3_init_channel(struct channel *channel, struct pci_dev *
pci_read_config_dword(channel->pdev, PCI_COMMAND, &channel->h.command);
t3e3_init(channel);
- if (request_irq(dev->irq, &t3e3_intr, IRQF_SHARED, dev->name, dev)) {
+ err = request_irq(dev->irq, &t3e3_intr, IRQF_SHARED, dev->name, dev);
+ if (err) {
printk(KERN_WARNING "%s: could not get irq: %d\n", dev->name, dev->irq);
- goto free_regions;
+ goto unregister_dev;
}
pci_set_drvdata(pdev, channel);
return 0;
+unregister_dev:
+ unregister_hdlc_device(dev);
+free_dev:
+ free_netdev(dev);
free_regions:
pci_release_regions(pdev);
disable:
diff --git a/drivers/staging/sbe-2t3e3/netdev.c b/drivers/staging/sbe-2t3e3/netdev.c
index c7b5e8bb04ff..180c96327b9a 100644
--- a/drivers/staging/sbe-2t3e3/netdev.c
+++ b/drivers/staging/sbe-2t3e3/netdev.c
@@ -21,13 +21,13 @@
#include <linux/interrupt.h>
#include "2t3e3.h"
-int t3e3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+static int t3e3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
struct channel *sc = dev_to_priv(dev);
int cmd_2t3e3, len, rlen;
t3e3_param_t param;
t3e3_resp_t resp;
- void *data = ifr->ifr_data + sizeof(cmd_2t3e3) + sizeof(len);
+ void __user *data = ifr->ifr_data + sizeof(cmd_2t3e3) + sizeof(len);
if (cmd == SIOCWANDEV)
return hdlc_ioctl(dev, ifr, cmd);
@@ -82,7 +82,7 @@ static struct net_device_stats* t3e3_get_stats(struct net_device *dev)
return nstats;
}
-int t3e3_open(struct net_device *dev)
+static int t3e3_open(struct net_device *dev)
{
struct channel *sc = dev_to_priv(dev);
int ret = hdlc_open(dev);
@@ -97,7 +97,7 @@ int t3e3_open(struct net_device *dev)
return 0;
}
-int t3e3_close(struct net_device *dev)
+static int t3e3_close(struct net_device *dev)
{
struct channel *sc = dev_to_priv(dev);
hdlc_close(dev);
diff --git a/drivers/staging/sep/sep_main.c b/drivers/staging/sep/sep_main.c
index ca8946acba60..a414e52dd082 100644
--- a/drivers/staging/sep/sep_main.c
+++ b/drivers/staging/sep/sep_main.c
@@ -719,7 +719,7 @@ static int sep_mmap(struct file *filp, struct vm_area_struct *vma)
if (remap_pfn_range(vma, vma->vm_start, bus_addr >> PAGE_SHIFT,
vma->vm_end - vma->vm_start, vma->vm_page_prot)) {
- dev_dbg(&sep->pdev->dev, "[PID%d] remap_page_range failed\n",
+ dev_dbg(&sep->pdev->dev, "[PID%d] remap_pfn_range failed\n",
current->pid);
error = -EAGAIN;
goto end_function_with_error;
diff --git a/drivers/staging/serqt_usb2/serqt_usb2.c b/drivers/staging/serqt_usb2/serqt_usb2.c
index c90de969be8f..099bc69ca00c 100644
--- a/drivers/staging/serqt_usb2/serqt_usb2.c
+++ b/drivers/staging/serqt_usb2/serqt_usb2.c
@@ -16,8 +16,6 @@
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
-static bool debug;
-
/* Version Information */
#define DRIVER_VERSION "v2.14"
#define DRIVER_AUTHOR "Tim Gobeli, Quatech, Inc"
@@ -184,11 +182,11 @@ static int port_paranoia_check(struct usb_serial_port *port,
const char *function)
{
if (!port) {
- dbg("%s - port == NULL", function);
+ pr_debug("%s - port == NULL", function);
return -1;
}
if (!port->serial) {
- dbg("%s - port->serial == NULL\n", function);
+ pr_debug("%s - port->serial == NULL\n", function);
return -1;
}
@@ -199,12 +197,12 @@ static int serial_paranoia_check(struct usb_serial *serial,
const char *function)
{
if (!serial) {
- dbg("%s - serial == NULL\n", function);
+ pr_debug("%s - serial == NULL\n", function);
return -1;
}
if (!serial->type) {
- dbg("%s - serial->type == NULL!", function);
+ pr_debug("%s - serial->type == NULL!", function);
return -1;
}
@@ -247,7 +245,6 @@ static void ProcessLineStatus(struct quatech_port *qt_port,
qt_port->shadowLSR =
line_status & (SERIAL_LSR_OE | SERIAL_LSR_PE | SERIAL_LSR_FE |
SERIAL_LSR_BI);
- return;
}
static void ProcessModemStatus(struct quatech_port *qt_port,
@@ -256,7 +253,6 @@ static void ProcessModemStatus(struct quatech_port *qt_port,
qt_port->shadowMSR = modem_status;
wake_up_interruptible(&qt_port->wait);
- return;
}
static void ProcessRxChar(struct tty_struct *tty, struct usb_serial_port *port,
@@ -276,7 +272,7 @@ static void qt_write_bulk_callback(struct urb *urb)
status = urb->status;
if (status) {
- dbg("nonzero write bulk status received:%d\n", status);
+ dev_dbg(&urb->dev->dev, "nonzero write bulk status received:%d\n", status);
return;
}
@@ -309,8 +305,8 @@ static void qt_read_bulk_callback(struct urb *urb)
if (urb->status) {
qt_port->ReadBulkStopped = 1;
- dbg("%s - nonzero write bulk status received: %d\n",
- __func__, urb->status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero write bulk status received: %d\n",
+ __func__, urb->status);
return;
}
@@ -325,21 +321,19 @@ static void qt_read_bulk_callback(struct urb *urb)
/* index = MINOR(port->tty->device) - serial->minor; */
index = tty->index - serial->minor;
- dbg("%s - port->RxHolding = %d\n", __func__, qt_port->RxHolding);
+ dev_dbg(&port->dev, "%s - port->RxHolding = %d\n", __func__, qt_port->RxHolding);
if (port_paranoia_check(port, __func__) != 0) {
- dbg("%s - port_paranoia_check, exiting\n", __func__);
qt_port->ReadBulkStopped = 1;
goto exit;
}
- if (!serial) {
- dbg("%s - bad serial pointer, exiting\n", __func__);
+ if (!serial)
goto exit;
- }
+
if (qt_port->closePending == 1) {
/* Were closing , stop reading */
- dbg("%s - (qt_port->closepending == 1\n", __func__);
+ dev_dbg(&port->dev, "%s - (qt_port->closepending == 1\n", __func__);
qt_port->ReadBulkStopped = 1;
goto exit;
}
@@ -357,8 +351,8 @@ static void qt_read_bulk_callback(struct urb *urb)
if (urb->status) {
qt_port->ReadBulkStopped = 1;
- dbg("%s - nonzero read bulk status received: %d\n",
- __func__, urb->status);
+ dev_dbg(&port->dev, "%s - nonzero read bulk status received: %d\n",
+ __func__, urb->status);
goto exit;
}
@@ -373,7 +367,7 @@ static void qt_read_bulk_callback(struct urb *urb)
case 0x00:
/* line status change 4th byte must follow */
if (i > (RxCount - 4)) {
- dbg("Illegal escape seuences in received data\n");
+ dev_dbg(&port->dev, "Illegal escape seuences in received data\n");
break;
}
ProcessLineStatus(qt_port, data[i + 3]);
@@ -383,9 +377,9 @@ static void qt_read_bulk_callback(struct urb *urb)
case 0x01:
/* Modem status status change 4th byte must follow */
- dbg("Modem status status.\n");
+ dev_dbg(&port->dev, "Modem status status.\n");
if (i > (RxCount - 4)) {
- dbg("Illegal escape sequences in received data\n");
+ dev_dbg(&port->dev, "Illegal escape sequences in received data\n");
break;
}
ProcessModemStatus(qt_port,
@@ -394,7 +388,7 @@ static void qt_read_bulk_callback(struct urb *urb)
flag = 1;
break;
case 0xff:
- dbg("No status sequence.\n");
+ dev_dbg(&port->dev, "No status sequence.\n");
if (tty) {
ProcessRxChar(tty, port, data[i]);
@@ -423,8 +417,8 @@ static void qt_read_bulk_callback(struct urb *urb)
qt_read_bulk_callback, port);
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result)
- dbg("%s - failed resubmitting read urb, error %d",
- __func__, result);
+ dev_dbg(&port->dev, "%s - failed resubmitting read urb, error %d",
+ __func__, result);
else {
if (RxCount) {
tty_flip_buffer_push(tty);
@@ -519,7 +513,6 @@ static int qt_set_device(struct usb_serial *serial,
PortSettings += ((__u16) (device_data->porta));
length = sizeof(struct qt_get_device_data);
- dbg("%s - PortSettings = 0x%x\n", __func__, PortSettings);
result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
QT_SET_GET_DEVICE, 0x40, PortSettings,
@@ -693,6 +686,7 @@ static int BoxDisable_SW_FlowCtrl(struct usb_serial *serial, __u16 index)
static int qt_startup(struct usb_serial *serial)
{
+ struct device *dev = &serial->dev->dev;
struct usb_serial_port *port;
struct quatech_port *qt_port;
struct qt_get_device_data DeviceData;
@@ -704,8 +698,6 @@ static int qt_startup(struct usb_serial *serial)
port = serial->port[i];
qt_port = kzalloc(sizeof(*qt_port), GFP_KERNEL);
if (!qt_port) {
- dbg("%s: kmalloc for quatech_port (%d) failed!.",
- __func__, i);
for (--i; i >= 0; i--) {
port = serial->port[i];
kfree(usb_get_serial_port_data(port));
@@ -720,25 +712,23 @@ static int qt_startup(struct usb_serial *serial)
}
status = qt_get_device(serial, &DeviceData);
- if (status < 0) {
- dbg(__FILE__ "box_get_device failed");
+ if (status < 0)
goto startup_error;
- }
- dbg(__FILE__ "DeviceData.portb = 0x%x", DeviceData.portb);
+ dev_dbg(dev, "DeviceData.portb = 0x%x\n", DeviceData.portb);
DeviceData.portb &= ~FULLPWRBIT;
- dbg(__FILE__ "Changing DeviceData.portb to 0x%x", DeviceData.portb);
+ dev_dbg(dev, "Changing DeviceData.portb to 0x%x\n", DeviceData.portb);
status = qt_set_device(serial, &DeviceData);
if (status < 0) {
- dbg(__FILE__ "qt_set_device failed\n");
+ dev_dbg(dev, "qt_set_device failed\n");
goto startup_error;
}
status = qt_get_device(serial, &DeviceData);
if (status < 0) {
- dbg(__FILE__ "qt_get_device failed");
+ dev_dbg(dev, "qt_get_device failed\n");
goto startup_error;
}
@@ -782,29 +772,27 @@ static int qt_startup(struct usb_serial *serial)
status = BoxSetPrebufferLevel(serial); /* sets to default value */
if (status < 0) {
- dbg(__FILE__ "BoxSetPrebufferLevel failed\n");
+ dev_dbg(dev, "BoxSetPrebufferLevel failed\n");
goto startup_error;
}
status = BoxSetATC(serial, ATC_DISABLED);
if (status < 0) {
- dbg(__FILE__ "BoxSetATC failed\n");
+ dev_dbg(dev, "BoxSetATC failed\n");
goto startup_error;
}
- dbg(__FILE__ "DeviceData.portb = 0x%x", DeviceData.portb);
+ dev_dbg(dev, "DeviceData.portb = 0x%x\n", DeviceData.portb);
DeviceData.portb |= NEXT_BOARD_POWER_BIT;
- dbg(__FILE__ "Changing DeviceData.portb to 0x%x", DeviceData.portb);
+ dev_dbg(dev, "Changing DeviceData.portb to 0x%x\n", DeviceData.portb);
status = qt_set_device(serial, &DeviceData);
if (status < 0) {
- dbg(__FILE__ "qt_set_device failed\n");
+ dev_dbg(dev, "qt_set_device failed\n");
goto startup_error;
}
- dbg("Exit Success %s\n", __func__);
-
return 0;
startup_error:
@@ -815,8 +803,6 @@ startup_error:
usb_set_serial_port_data(port, NULL);
}
- dbg("Exit fail %s\n", __func__);
-
return -EIO;
}
@@ -871,10 +857,10 @@ static int qt_open(struct tty_struct *tty,
/* Port specific setups */
result = qt_open_channel(serial, port->number, &ChannelData);
if (result < 0) {
- dbg(__FILE__ "qt_open_channel failed\n");
+ dev_dbg(&port->dev, "qt_open_channel failed\n");
return result;
}
- dbg(__FILE__ "qt_open_channel completed.\n");
+ dev_dbg(&port->dev, "qt_open_channel completed.\n");
/* FIXME: are these needed? Does it even do anything useful? */
quatech_port->shadowLSR = ChannelData.line_status &
@@ -886,10 +872,10 @@ static int qt_open(struct tty_struct *tty,
/* Set Baud rate to default and turn off (default)flow control here */
result = qt_setuart(serial, port->number, DEFAULT_DIVISOR, DEFAULT_LCR);
if (result < 0) {
- dbg(__FILE__ "qt_setuart failed\n");
+ dev_dbg(&port->dev, "qt_setuart failed\n");
return result;
}
- dbg(__FILE__ "qt_setuart completed.\n");
+ dev_dbg(&port->dev, "qt_setuart completed.\n");
/*
* Put this here to make it responsive to stty and defaults set by
@@ -924,12 +910,12 @@ static int qt_open(struct tty_struct *tty,
}
- dbg("port number is %d\n", port->number);
- dbg("serial number is %d\n", port->serial->minor);
- dbg("Bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
- dbg("BulkOut endpoint is %d\n", port->bulk_out_endpointAddress);
- dbg("Interrupt endpoint is %d\n", port->interrupt_in_endpointAddress);
- dbg("port's number in the device is %d\n", quatech_port->port_num);
+ dev_dbg(&port->dev, "port number is %d\n", port->number);
+ dev_dbg(&port->dev, "serial number is %d\n", port->serial->minor);
+ dev_dbg(&port->dev, "Bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
+ dev_dbg(&port->dev, "BulkOut endpoint is %d\n", port->bulk_out_endpointAddress);
+ dev_dbg(&port->dev, "Interrupt endpoint is %d\n", port->interrupt_in_endpointAddress);
+ dev_dbg(&port->dev, "port's number in the device is %d\n", quatech_port->port_num);
quatech_port->read_urb = port->read_urb;
/* set up our bulk in urb */
@@ -942,7 +928,7 @@ static int qt_open(struct tty_struct *tty,
quatech_port->read_urb->transfer_buffer_length,
qt_read_bulk_callback, quatech_port);
- dbg("qt_open: bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
+ dev_dbg(&port->dev, "qt_open: bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
quatech_port->read_urb_busy = true;
result = usb_submit_urb(quatech_port->read_urb, GFP_KERNEL);
if (result) {
@@ -976,8 +962,6 @@ static int qt_chars_in_buffer(struct tty_struct *tty)
chars = port->write_urb->transfer_buffer_length;
}
- dbg("%s - returns %d\n", __func__, chars);
-
return chars;
}
@@ -999,7 +983,7 @@ static void qt_block_until_empty(struct tty_struct *tty,
wait--;
if (wait == 0) {
- dbg("%s - TIMEOUT", __func__);
+ dev_dbg(&qt_port->port->dev, "%s - TIMEOUT", __func__);
return;
} else {
wait = 30;
@@ -1037,17 +1021,15 @@ static void qt_close(struct usb_serial_port *port)
/* Close uart channel */
status = qt_close_channel(serial, index);
if (status < 0)
- dbg("%s - port %d qt_close_channel failed.\n",
- __func__, port->number);
+ dev_dbg(&port->dev, "%s - port %d qt_close_channel failed.\n", __func__, port->number);
port0->open_ports--;
- dbg("qt_num_open_ports in close%d:in port%d\n",
- port0->open_ports, port->number);
+ dev_dbg(&port->dev, "qt_num_open_ports in close%d:in port%d\n", port0->open_ports, port->number);
if (port0->open_ports == 0) {
if (serial->port[0]->interrupt_in_urb) {
- dbg("%s", "Shutdown interrupt_in_urb\n");
+ dev_dbg(&port->dev, "%s", "Shutdown interrupt_in_urb\n");
usb_kill_urb(serial->port[0]->interrupt_in_urb);
}
@@ -1071,14 +1053,14 @@ static int qt_write(struct tty_struct *tty, struct usb_serial_port *port,
return -ENODEV;
if (count == 0) {
- dbg("%s - write request of 0 bytes\n", __func__);
+ dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
return 0;
}
/* only do something if we have a bulk out endpoint */
if (serial->num_bulk_out) {
if (port->write_urb->status == -EINPROGRESS) {
- dbg("%s - already writing\n", __func__);
+ dev_dbg(&port->dev, "%s - already writing\n", __func__);
return 0;
}
@@ -1098,8 +1080,8 @@ static int qt_write(struct tty_struct *tty, struct usb_serial_port *port,
/* send the data out the bulk port */
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result)
- dbg("%s - failed submitting write urb, error %d\n",
- __func__, result);
+ dev_dbg(&port->dev, "%s - failed submitting write urb, error %d\n",
+ __func__, result);
else
result = count;
@@ -1118,10 +1100,8 @@ static int qt_write_room(struct tty_struct *tty)
int retval = -EINVAL;
- if (port_paranoia_check(port, __func__)) {
- dbg("%s", "Invalid port\n");
+ if (port_paranoia_check(port, __func__))
return -1;
- }
serial = get_usb_serial(port, __func__);
@@ -1150,7 +1130,7 @@ static int qt_ioctl(struct tty_struct *tty,
struct usb_serial *serial = get_usb_serial(port, __func__);
unsigned int index;
- dbg("%s cmd 0x%04x", __func__, cmd);
+ dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
index = tty->index - serial->minor;
@@ -1183,7 +1163,7 @@ static int qt_ioctl(struct tty_struct *tty,
return 0;
}
- dbg("%s -No ioctl for that one. port = %d\n", __func__, port->number);
+ dev_dbg(&port->dev, "%s -No ioctl for that one. port = %d\n", __func__, port->number);
return -ENOIOCTLCMD;
}
@@ -1230,7 +1210,7 @@ static void qt_set_termios(struct tty_struct *tty,
else
new_LCR |= SERIAL_ONE_STOPB;
- dbg("%s - 4\n", __func__);
+ dev_dbg(&port->dev, "%s - 4\n", __func__);
/* Thats the LCR stuff, go ahead and set it */
baud = tty_get_baud_rate(tty);
@@ -1238,7 +1218,7 @@ static void qt_set_termios(struct tty_struct *tty,
/* pick a default, any default... */
baud = 9600;
- dbg("%s - got baud = %d\n", __func__, baud);
+ dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud);
divisor = MAX_BAUD_RATE / baud;
remainder = MAX_BAUD_RATE % baud;
@@ -1252,30 +1232,28 @@ static void qt_set_termios(struct tty_struct *tty,
status =
qt_setuart(port->serial, index, (unsigned short)divisor, new_LCR);
if (status < 0) {
- dbg(__FILE__ "qt_setuart failed\n");
+ dev_dbg(&port->dev, "qt_setuart failed\n");
return;
}
/* Now determine flow control */
if (cflag & CRTSCTS) {
- dbg("%s - Enabling HW flow control port %d\n", __func__,
- port->number);
+ dev_dbg(&port->dev, "%s - Enabling HW flow control port %d\n", __func__, port->number);
/* Enable RTS/CTS flow control */
status = BoxSetHW_FlowCtrl(port->serial, index, 1);
if (status < 0) {
- dbg(__FILE__ "BoxSetHW_FlowCtrl failed\n");
+ dev_dbg(&port->dev, "BoxSetHW_FlowCtrl failed\n");
return;
}
} else {
/* Disable RTS/CTS flow control */
- dbg("%s - disabling HW flow control port %d\n", __func__,
- port->number);
+ dev_dbg(&port->dev, "%s - disabling HW flow control port %d\n", __func__, port->number);
status = BoxSetHW_FlowCtrl(port->serial, index, 0);
if (status < 0) {
- dbg(__FILE__ "BoxSetHW_FlowCtrl failed\n");
+ dev_dbg(&port->dev, "BoxSetHW_FlowCtrl failed\n");
return;
}
@@ -1290,13 +1268,13 @@ static void qt_set_termios(struct tty_struct *tty,
BoxSetSW_FlowCtrl(port->serial, index, stop_char,
start_char);
if (status < 0)
- dbg(__FILE__ "BoxSetSW_FlowCtrl (enabled) failed\n");
+ dev_dbg(&port->dev, "BoxSetSW_FlowCtrl (enabled) failed\n");
} else {
/* disable SW flow control */
status = BoxDisable_SW_FlowCtrl(port->serial, index);
if (status < 0)
- dbg(__FILE__ "BoxSetSW_FlowCtrl (diabling) failed\n");
+ dev_dbg(&port->dev, "BoxSetSW_FlowCtrl (diabling) failed\n");
}
termios->c_cflag &= ~CMSPAR;
@@ -1410,7 +1388,7 @@ static int qt_tiocmget(struct tty_struct *tty)
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = get_usb_serial(port, __func__);
struct quatech_port *qt_port = qt_get_port_private(port);
- int retval = -ENODEV;
+ int retval;
if (!serial)
return -ENODEV;
@@ -1428,7 +1406,7 @@ static int qt_tiocmset(struct tty_struct *tty,
struct usb_serial_port *port = tty->driver_data;
struct usb_serial *serial = get_usb_serial(port, __func__);
struct quatech_port *qt_port = qt_get_port_private(port);
- int retval = -ENODEV;
+ int retval;
if (!serial)
return -ENODEV;
@@ -1456,7 +1434,6 @@ static void qt_throttle(struct tty_struct *tty)
qt_port->RxHolding = 1;
mutex_unlock(&qt_port->lock);
- return;
}
static void qt_unthrottle(struct tty_struct *tty)
@@ -1474,10 +1451,10 @@ static void qt_unthrottle(struct tty_struct *tty)
mutex_lock(&qt_port->lock);
if (qt_port->RxHolding == 1) {
- dbg("%s -qt_port->RxHolding == 1\n", __func__);
+ dev_dbg(&port->dev, "%s -qt_port->RxHolding == 1\n", __func__);
qt_port->RxHolding = 0;
- dbg("%s - qt_port->RxHolding = 0\n", __func__);
+ dev_dbg(&port->dev, "%s - qt_port->RxHolding = 0\n", __func__);
/* if we have a bulk endpoint, start it up */
if ((serial->num_bulk_in) && (qt_port->ReadBulkStopped == 1)) {
@@ -1497,19 +1474,12 @@ static void qt_unthrottle(struct tty_struct *tty)
}
}
mutex_unlock(&qt_port->lock);
- return;
-
}
static int qt_calc_num_ports(struct usb_serial *serial)
{
int num_ports;
- dbg("numberofendpoints: %d\n",
- (int)serial->interface->cur_altsetting->desc.bNumEndpoints);
- dbg("numberofendpoints: %d\n",
- (int)serial->interface->altsetting->desc.bNumEndpoints);
-
num_ports =
(serial->interface->cur_altsetting->desc.bNumEndpoints - 1) / 2;
@@ -1550,6 +1520,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/staging/silicom/Kconfig b/drivers/staging/silicom/Kconfig
new file mode 100644
index 000000000000..eda2e7d73645
--- /dev/null
+++ b/drivers/staging/silicom/Kconfig
@@ -0,0 +1,46 @@
+#
+# Silicom device configuration
+#
+
+config NET_VENDOR_SILICOM
+ bool "Silicom devices"
+ default y
+ depends on PCI
+ ---help---
+ If you have a network card (Ethernet) belonging to this class,
+ say Y.
+
+ Note that the answer to this question does not directly affect
+ the kernel: saying N will just case the configurator to skip all
+ the questions regarding Silicom chipsets. If you say Y, you will be asked
+ for your specific chipset/driver in the following questions.
+
+if NET_VENDOR_SILICOM
+
+config SBYPASS
+ tristate "Silicom BypassCTL library support"
+ depends on PCI && NET
+ depends on m
+ ---help---
+ If you have a network (Ethernet) controller of this type, say Y
+
+ To compile this driver as a module, choose M here. The module
+ will be called bypass.
+
+config BPCTL
+ tristate "Silicom BypassCTL net support"
+ depends on PCI && NET
+ depends on m
+ select SBYPASS
+ select NET_CORE
+ select MII
+ ---help---
+ If you have a network (Ethernet) controller of this type, say Y
+ or M and read the Ethernet-HOWTO, available from
+ <http://www.tldp.org/docs.html#howto>.
+
+ To compile this driver as a module, choose M here. The module
+ will be called bpctl_mod.
+
+
+endif # NET_VENDOR_SILICOM
diff --git a/drivers/staging/silicom/Makefile b/drivers/staging/silicom/Makefile
new file mode 100644
index 000000000000..80e6d12d156b
--- /dev/null
+++ b/drivers/staging/silicom/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the Bypass network device drivers.
+#
+
+obj-$(CONFIG_BPCTL) += bpctl_mod.o
+obj-$(CONFIG_SBYPASS) += bypasslib/
+
+
+bpctl_mod-objs := bp_mod.o bp_proc.o
diff --git a/drivers/staging/silicom/README b/drivers/staging/silicom/README
new file mode 100644
index 000000000000..ae970b37fdc6
--- /dev/null
+++ b/drivers/staging/silicom/README
@@ -0,0 +1,14 @@
+
+Theory of Operation:
+
+The Silicom Bypass Network Interface Cards (NICs) are network cards with paired ports (2 or 4).
+The pairs either act as a "wire" allowing the network packets to pass or insert the device in
+between the two ports. When paired with the on-board hardware watchdog or other failsafe,
+they provide high availability for the network in the face of software outages or maintenance.
+
+The software requirements are for a kernel level driver that interfaces with the bypass and watchdog,
+as well as for control software. User control can be either the provided standalone executable
+(/bin/bpctl) or the API exposed by the Silicom library.
+
+
+
diff --git a/drivers/staging/silicom/TODO b/drivers/staging/silicom/TODO
new file mode 100644
index 000000000000..09d07b0ea9c0
--- /dev/null
+++ b/drivers/staging/silicom/TODO
@@ -0,0 +1,8 @@
+TODO:
+ - checkpatch.pl cleanups
+ - locking audit
+ - single module with all functionality
+ - userland
+ - fix monolithic build.
+
+
diff --git a/drivers/staging/silicom/bits.h b/drivers/staging/silicom/bits.h
new file mode 100644
index 000000000000..8c411d0d4ecd
--- /dev/null
+++ b/drivers/staging/silicom/bits.h
@@ -0,0 +1,56 @@
+/******************************************************************************/
+/* */
+/* Broadcom BCM5700 Linux Network Driver, Copyright (c) 2000 - 2004 Broadcom */
+/* Corporation. */
+/* All rights reserved. */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, located in the file LICENSE. */
+/* */
+/* History: */
+/* 02/25/00 Hav Khauv Initial version. */
+/******************************************************************************/
+
+#ifndef BITS_H
+#define BITS_H
+
+/******************************************************************************/
+/* Bit Mask definitions */
+/******************************************************************************/
+
+#define BIT_NONE 0x00
+#define BIT_0 0x01
+#define BIT_1 0x02
+#define BIT_2 0x04
+#define BIT_3 0x08
+#define BIT_4 0x10
+#define BIT_5 0x20
+#define BIT_6 0x40
+#define BIT_7 0x80
+#define BIT_8 0x0100
+#define BIT_9 0x0200
+#define BIT_10 0x0400
+#define BIT_11 0x0800
+#define BIT_12 0x1000
+#define BIT_13 0x2000
+#define BIT_14 0x4000
+#define BIT_15 0x8000
+#define BIT_16 0x010000
+#define BIT_17 0x020000
+#define BIT_18 0x040000
+#define BIT_19 0x080000
+#define BIT_20 0x100000
+#define BIT_21 0x200000
+#define BIT_22 0x400000
+#define BIT_23 0x800000
+#define BIT_24 0x01000000
+#define BIT_25 0x02000000
+#define BIT_26 0x04000000
+#define BIT_27 0x08000000
+#define BIT_28 0x10000000
+#define BIT_29 0x20000000
+#define BIT_30 0x40000000
+#define BIT_31 0x80000000
+
+#endif /* BITS_H */
diff --git a/drivers/staging/silicom/bp_ioctl.h b/drivers/staging/silicom/bp_ioctl.h
new file mode 100644
index 000000000000..57de34a69e8e
--- /dev/null
+++ b/drivers/staging/silicom/bp_ioctl.h
@@ -0,0 +1,140 @@
+/******************************************************************************/
+/* */
+/* Silicom Bypass Control Utility, Copyright (c) 2005-2007 Silicom */
+/* 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, located in the file LICENSE. */
+/* */
+/* */
+/******************************************************************************/
+
+#ifndef BP_IOCTL_H
+#define BP_IOCTL_H
+
+#define BP_CAP 0x01 /* BIT_0 */
+#define BP_STATUS_CAP 0x02
+#define BP_STATUS_CHANGE_CAP 0x04
+#define SW_CTL_CAP 0x08
+#define BP_DIS_CAP 0x10
+#define BP_DIS_STATUS_CAP 0x20
+#define STD_NIC_CAP 0x40
+#define BP_PWOFF_ON_CAP 0x80
+#define BP_PWOFF_OFF_CAP 0x0100
+#define BP_PWOFF_CTL_CAP 0x0200
+#define BP_PWUP_ON_CAP 0x0400
+#define BP_PWUP_OFF_CAP 0x0800
+#define BP_PWUP_CTL_CAP 0x1000
+#define WD_CTL_CAP 0x2000
+#define WD_STATUS_CAP 0x4000
+#define WD_TIMEOUT_CAP 0x8000
+#define TX_CTL_CAP 0x10000
+#define TX_STATUS_CAP 0x20000
+#define TAP_CAP 0x40000
+#define TAP_STATUS_CAP 0x80000
+#define TAP_STATUS_CHANGE_CAP 0x100000
+#define TAP_DIS_CAP 0x200000
+#define TAP_DIS_STATUS_CAP 0x400000
+#define TAP_PWUP_ON_CAP 0x800000
+#define TAP_PWUP_OFF_CAP 0x1000000
+#define TAP_PWUP_CTL_CAP 0x2000000
+#define NIC_CAP_NEG 0x4000000
+#define TPL_CAP 0x8000000
+#define DISC_CAP 0x10000000
+#define DISC_DIS_CAP 0x20000000
+#define DISC_PWUP_CTL_CAP 0x40000000
+
+#define TPL2_CAP_EX 0x01
+#define DISC_PORT_CAP_EX 0x02
+
+#define WD_MIN_TIME_MASK(val) (val & 0xf)
+#define WD_STEP_COUNT_MASK(val) ((val & 0xf) << 5)
+#define WDT_STEP_TIME 0x10 /* BIT_4 */
+
+#define WD_MIN_TIME_GET(desc) (desc & 0xf)
+#define WD_STEP_COUNT_GET(desc) ((desc>>5) & 0xf)
+
+typedef enum {
+ IF_SCAN,
+ GET_DEV_NUM,
+ IS_BYPASS,
+ GET_BYPASS_SLAVE,
+ GET_BYPASS_CAPS,
+ GET_WD_SET_CAPS,
+ SET_BYPASS,
+ GET_BYPASS,
+ GET_BYPASS_CHANGE,
+ SET_BYPASS_WD,
+ GET_BYPASS_WD,
+ GET_WD_EXPIRE_TIME,
+ RESET_BYPASS_WD_TIMER,
+ SET_DIS_BYPASS,
+ GET_DIS_BYPASS,
+ SET_BYPASS_PWOFF,
+ GET_BYPASS_PWOFF,
+ SET_BYPASS_PWUP,
+ GET_BYPASS_PWUP,
+ SET_STD_NIC,
+ GET_STD_NIC,
+ SET_TX,
+ GET_TX,
+ SET_TAP,
+ GET_TAP,
+ GET_TAP_CHANGE,
+ SET_DIS_TAP,
+ GET_DIS_TAP,
+ SET_TAP_PWUP,
+ GET_TAP_PWUP,
+ SET_WD_EXP_MODE,
+ GET_WD_EXP_MODE,
+ SET_WD_AUTORESET,
+ GET_WD_AUTORESET,
+ SET_TPL,
+ GET_TPL,
+ SET_DISC,
+ GET_DISC,
+ GET_DISC_CHANGE,
+ SET_DIS_DISC,
+ GET_DIS_DISC,
+ SET_DISC_PWUP,
+ GET_DISC_PWUP,
+ GET_BYPASS_INFO = 100,
+ GET_BP_WAIT_AT_PWUP,
+ SET_BP_WAIT_AT_PWUP,
+ GET_BP_HW_RESET,
+ SET_BP_HW_RESET,
+ SET_DISC_PORT,
+ GET_DISC_PORT,
+ SET_DISC_PORT_PWUP,
+ GET_DISC_PORT_PWUP,
+ SET_BP_FORCE_LINK,
+ GET_BP_FORCE_LINK,
+#ifdef BP_SELF_TEST
+ SET_BP_SELF_TEST = 200,
+ GET_BP_SELF_TEST,
+#endif
+
+} CMND_TYPE_SD;
+
+/*
+* The major device number. We can't rely on dynamic
+* registration any more, because ioctls need to know
+* it.
+*/
+
+#define MAGIC_NUM 'J'
+
+/* for passing single values */
+struct bpctl_cmd {
+ int status;
+ int data[8];
+ int in_param[8];
+ int out_param[8];
+};
+
+#define IOCTL_TX_MSG(cmd) _IOWR(MAGIC_NUM, cmd, struct bpctl_cmd)
+
+#define DEVICE_NAME "bpctl"
+
+#endif
diff --git a/drivers/staging/silicom/bp_mod.c b/drivers/staging/silicom/bp_mod.c
new file mode 100644
index 000000000000..3cfd0516adfa
--- /dev/null
+++ b/drivers/staging/silicom/bp_mod.c
@@ -0,0 +1,8931 @@
+/******************************************************************************/
+/* */
+/* Bypass Control utility, Copyright (c) 2005-20011 Silicom */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, located in the file LICENSE. */
+/* Copyright(c) 2007 - 2009 Intel Corporation. All rights reserved. */
+/* */
+/* */
+/******************************************************************************/
+#include <linux/version.h>
+
+#include <linux/kernel.h> /* We're doing kernel work */
+#include <linux/module.h> /* Specifically, a module */
+#include <linux/fs.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+#include <linux/rcupdate.h>
+#include <linux/etherdevice.h>
+
+#include <linux/uaccess.h> /* for get_user and put_user */
+#include <linux/sched.h>
+#include <linux/ethtool.h>
+#include <linux/proc_fs.h>
+
+#include "bp_ioctl.h"
+#include "bp_mod.h"
+#include "bypass.h"
+#include "libbp_sd.h"
+
+#define SUCCESS 0
+#define BP_MOD_VER "9.0.4"
+#define BP_MOD_DESCR "Silicom Bypass-SD Control driver"
+#define BP_SYNC_FLAG 1
+
+static int Device_Open = 0;
+static int major_num = 0;
+
+MODULE_AUTHOR("Anna Lukin, annal@silicom.co.il");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(BP_MOD_DESCR);
+MODULE_VERSION(BP_MOD_VER);
+spinlock_t bpvm_lock;
+
+#define lock_bpctl() \
+if (down_interruptible(&bpctl_sema)) { \
+ return -ERESTARTSYS; \
+} \
+
+#define unlock_bpctl() \
+ up(&bpctl_sema);
+
+/* Media Types */
+typedef enum {
+ bp_copper = 0,
+ bp_fiber,
+ bp_cx4,
+ bp_none,
+} bp_media_type;
+
+struct pfs_unit_sd {
+ struct proc_dir_entry *proc_entry;
+ char proc_name[32];
+};
+
+struct bypass_pfs_sd {
+ char dir_name[32];
+ struct proc_dir_entry *bypass_entry;
+ struct pfs_unit_sd bypass_info;
+ struct pfs_unit_sd bypass_slave;
+ struct pfs_unit_sd bypass_caps;
+ struct pfs_unit_sd wd_set_caps;
+ struct pfs_unit_sd bypass;
+ struct pfs_unit_sd bypass_change;
+ struct pfs_unit_sd bypass_wd;
+ struct pfs_unit_sd wd_expire_time;
+ struct pfs_unit_sd reset_bypass_wd;
+ struct pfs_unit_sd dis_bypass;
+ struct pfs_unit_sd bypass_pwup;
+ struct pfs_unit_sd bypass_pwoff;
+ struct pfs_unit_sd std_nic;
+ struct pfs_unit_sd tap;
+ struct pfs_unit_sd dis_tap;
+ struct pfs_unit_sd tap_pwup;
+ struct pfs_unit_sd tap_change;
+ struct pfs_unit_sd wd_exp_mode;
+ struct pfs_unit_sd wd_autoreset;
+ struct pfs_unit_sd tpl;
+
+};
+
+typedef struct _bpctl_dev {
+ char *name;
+ char *desc;
+ struct pci_dev *pdev; /* PCI device */
+ struct net_device *ndev; /* net device */
+ unsigned long mem_map;
+ uint8_t bus;
+ uint8_t slot;
+ uint8_t func;
+ u_int32_t device;
+ u_int32_t vendor;
+ u_int32_t subvendor;
+ u_int32_t subdevice;
+ int ifindex;
+ uint32_t bp_caps;
+ uint32_t bp_caps_ex;
+ uint8_t bp_fw_ver;
+ int bp_ext_ver;
+ int wdt_status;
+ unsigned long bypass_wdt_on_time;
+ uint32_t bypass_timer_interval;
+ struct timer_list bp_timer;
+ uint32_t reset_time;
+ uint8_t bp_status_un;
+ atomic_t wdt_busy;
+ bp_media_type media_type;
+ int bp_tpl_flag;
+ struct timer_list bp_tpl_timer;
+ spinlock_t bypass_wr_lock;
+ int bp_10g;
+ int bp_10gb;
+ int bp_fiber5;
+ int bp_10g9;
+ int bp_i80;
+ int bp_540;
+ int (*hard_start_xmit_save) (struct sk_buff *skb,
+ struct net_device *dev);
+ const struct net_device_ops *old_ops;
+ struct net_device_ops new_ops;
+ int bp_self_test_flag;
+ char *bp_tx_data;
+ struct bypass_pfs_sd bypass_pfs_set;
+
+} bpctl_dev_t;
+
+static bpctl_dev_t *bpctl_dev_arr;
+
+static struct semaphore bpctl_sema;
+static int device_num = 0;
+
+static int get_dev_idx(int ifindex);
+static bpctl_dev_t *get_master_port_fn(bpctl_dev_t *pbpctl_dev);
+static int disc_status(bpctl_dev_t *pbpctl_dev);
+static int bypass_status(bpctl_dev_t *pbpctl_dev);
+static int wdt_timer(bpctl_dev_t *pbpctl_dev, int *time_left);
+static bpctl_dev_t *get_status_port_fn(bpctl_dev_t *pbpctl_dev);
+static void if_scan_init(void);
+
+int bypass_proc_create_dev_sd(bpctl_dev_t *pbp_device_block);
+int bypass_proc_remove_dev_sd(bpctl_dev_t *pbp_device_block);
+int bp_proc_create(void);
+
+int is_bypass_fn(bpctl_dev_t *pbpctl_dev);
+int get_dev_idx_bsf(int bus, int slot, int func);
+
+static unsigned long str_to_hex(char *p);
+static int bp_device_event(struct notifier_block *unused,
+ unsigned long event, void *ptr)
+{
+ struct net_device *dev = ptr;
+ static bpctl_dev_t *pbpctl_dev = NULL, *pbpctl_dev_m = NULL;
+ int dev_num = 0, ret = 0, ret_d = 0, time_left = 0;
+ /* printk("BP_PROC_SUPPORT event =%d %s %d\n", event,dev->name, dev->ifindex ); */
+ /* return NOTIFY_DONE; */
+ if (!dev)
+ return NOTIFY_DONE;
+ if (event == NETDEV_REGISTER) {
+ {
+ struct ethtool_drvinfo drvinfo;
+ char cbuf[32];
+ char *buf = NULL;
+ char res[10];
+ int i = 0, ifindex, idx_dev = 0;
+ int bus = 0, slot = 0, func = 0;
+ ifindex = dev->ifindex;
+
+ memset(res, 0, 10);
+ memset(&drvinfo, 0, sizeof(struct ethtool_drvinfo));
+
+ if (dev->ethtool_ops && dev->ethtool_ops->get_drvinfo) {
+ memset(&drvinfo, 0, sizeof(drvinfo));
+ dev->ethtool_ops->get_drvinfo(dev, &drvinfo);
+ } else
+ return NOTIFY_DONE;
+ if (!drvinfo.bus_info)
+ return NOTIFY_DONE;
+ if (!strcmp(drvinfo.bus_info, "N/A"))
+ return NOTIFY_DONE;
+ memcpy(&cbuf, drvinfo.bus_info, 32);
+ buf = &cbuf[0];
+
+ while (*buf++ != ':') ;
+ for (i = 0; i < 10; i++, buf++) {
+ if (*buf == ':')
+ break;
+ res[i] = *buf;
+
+ }
+ buf++;
+ bus = str_to_hex(res);
+ memset(res, 0, 10);
+
+ for (i = 0; i < 10; i++, buf++) {
+ if (*buf == '.')
+ break;
+ res[i] = *buf;
+
+ }
+ buf++;
+ slot = str_to_hex(res);
+ func = str_to_hex(buf);
+ idx_dev = get_dev_idx_bsf(bus, slot, func);
+
+ if (idx_dev != -1) {
+
+ bpctl_dev_arr[idx_dev].ifindex = ifindex;
+ bpctl_dev_arr[idx_dev].ndev = dev;
+
+ bypass_proc_remove_dev_sd(&bpctl_dev_arr
+ [idx_dev]);
+ bypass_proc_create_dev_sd(&bpctl_dev_arr
+ [idx_dev]);
+
+ }
+
+ }
+ return NOTIFY_DONE;
+
+ }
+ if (event == NETDEV_UNREGISTER) {
+ int idx_dev = 0;
+ for (idx_dev = 0;
+ ((bpctl_dev_arr[idx_dev].pdev != NULL)
+ && (idx_dev < device_num)); idx_dev++) {
+ if (bpctl_dev_arr[idx_dev].ndev == dev) {
+ bypass_proc_remove_dev_sd(&bpctl_dev_arr
+ [idx_dev]);
+ bpctl_dev_arr[idx_dev].ndev = NULL;
+
+ return NOTIFY_DONE;
+
+ }
+
+ }
+ return NOTIFY_DONE;
+ }
+ if (event == NETDEV_CHANGENAME) {
+ int idx_dev = 0;
+ for (idx_dev = 0;
+ ((bpctl_dev_arr[idx_dev].pdev != NULL)
+ && (idx_dev < device_num)); idx_dev++) {
+ if (bpctl_dev_arr[idx_dev].ndev == dev) {
+ bypass_proc_remove_dev_sd(&bpctl_dev_arr
+ [idx_dev]);
+ bypass_proc_create_dev_sd(&bpctl_dev_arr
+ [idx_dev]);
+
+ return NOTIFY_DONE;
+
+ }
+
+ }
+ return NOTIFY_DONE;
+
+ }
+
+ switch (event) {
+
+ case NETDEV_CHANGE:{
+ if (netif_carrier_ok(dev))
+ return NOTIFY_DONE;
+
+ if (((dev_num = get_dev_idx(dev->ifindex)) == -1) ||
+ (!(pbpctl_dev = &bpctl_dev_arr[dev_num])))
+ return NOTIFY_DONE;
+
+ if ((is_bypass_fn(pbpctl_dev)) == 1)
+ pbpctl_dev_m = pbpctl_dev;
+ else
+ pbpctl_dev_m = get_master_port_fn(pbpctl_dev);
+ if (!pbpctl_dev_m)
+ return NOTIFY_DONE;
+ ret = bypass_status(pbpctl_dev_m);
+ if (ret == 1)
+ printk("bpmod: %s is in the Bypass mode now",
+ dev->name);
+ ret_d = disc_status(pbpctl_dev_m);
+ if (ret_d == 1)
+ printk
+ ("bpmod: %s is in the Disconnect mode now",
+ dev->name);
+ if (ret || ret_d) {
+ wdt_timer(pbpctl_dev_m, &time_left);
+ if (time_left == -1)
+ printk("; WDT has expired");
+ printk(".\n");
+
+ }
+ return NOTIFY_DONE;
+
+ }
+
+ default:
+ return NOTIFY_DONE;
+
+ }
+ return NOTIFY_DONE;
+
+}
+
+static struct notifier_block bp_notifier_block = {
+ .notifier_call = bp_device_event,
+};
+
+static int device_open(struct inode *inode, struct file *file)
+{
+#ifdef DEBUG
+ printk("device_open(%p)\n", file);
+#endif
+ Device_Open++;
+/*
+* Initialize the message
+*/
+ return SUCCESS;
+}
+
+static int device_release(struct inode *inode, struct file *file)
+{
+#ifdef DEBUG
+ printk("device_release(%p,%p)\n", inode, file);
+#endif
+ Device_Open--;
+ return SUCCESS;
+}
+
+int is_bypass_fn(bpctl_dev_t *pbpctl_dev);
+int wdt_time_left(bpctl_dev_t *pbpctl_dev);
+
+static void write_pulse(bpctl_dev_t *pbpctl_dev,
+ unsigned int ctrl_ext,
+ unsigned char value, unsigned char len)
+{
+ unsigned char ctrl_val = 0;
+ unsigned int i = len;
+ unsigned int ctrl = 0;
+ bpctl_dev_t *pbpctl_dev_c = NULL;
+
+ if (pbpctl_dev->bp_i80)
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ if (pbpctl_dev->bp_540)
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+
+ if (pbpctl_dev->bp_10g9) {
+ if (!(pbpctl_dev_c = get_status_port_fn(pbpctl_dev)))
+ return;
+ ctrl = BP10G_READ_REG(pbpctl_dev_c, ESDP);
+ }
+
+ while (i--) {
+ ctrl_val = (value >> i) & 0x1;
+ if (ctrl_val) {
+ if (pbpctl_dev->bp_10g9) {
+
+ /* To start management : MCLK 1, MDIO 1, output */
+ /* DATA 1 CLK 1 */
+ /*BP10G_WRITE_REG(pbpctl_dev, I2CCTL, (ctrl_ext|BP10G_MCLK_DATA_OUT9|BP10G_MDIO_DATA_OUT9)); */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ ctrl_ext |
+ BP10G_MDIO_DATA_OUT9);
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ (ctrl | BP10G_MCLK_DATA_OUT9 |
+ BP10G_MCLK_DIR_OUT9));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, (ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DATA5
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA5));
+
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, (ctrl_ext |
+ BPCTLI_CTRL_EXT_MDIO_DIR80
+ |
+ BPCTLI_CTRL_EXT_MDIO_DATA80));
+
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, (ctrl |
+ BPCTLI_CTRL_EXT_MCLK_DIR80
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA80));
+
+ } else if (pbpctl_dev->bp_540) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP, (ctrl |
+ BP540_MDIO_DIR
+ |
+ BP540_MDIO_DATA
+ |
+ BP540_MCLK_DIR
+ |
+ BP540_MCLK_DATA));
+
+ } else if (pbpctl_dev->bp_10gb) {
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_SET |
+ BP10GB_MCLK_SET) &
+ ~(BP10GB_MCLK_DIR |
+ BP10GB_MDIO_DIR |
+ BP10GB_MDIO_CLR |
+ BP10GB_MCLK_CLR));
+
+ } else if (!pbpctl_dev->bp_10g)
+ /* To start management : MCLK 1, MDIO 1, output */
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ (ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR |
+ BPCTLI_CTRL_EXT_MDIO_DIR |
+ BPCTLI_CTRL_EXT_MDIO_DATA |
+ BPCTLI_CTRL_EXT_MCLK_DATA));
+ else {
+
+ /* To start management : MCLK 1, MDIO 1, output*/
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ (ctrl_ext | BP10G_MCLK_DATA_OUT
+ | BP10G_MDIO_DATA_OUT));
+
+ }
+
+ usec_delay(PULSE_TIME);
+ if (pbpctl_dev->bp_10g9) {
+
+ /*BP10G_WRITE_REG(pbpctl_dev, I2CCTL, ((ctrl_ext|BP10G_MDIO_DATA_OUT9)&~(BP10G_MCLK_DATA_OUT9))); */
+ /* DATA 1 CLK 0 */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ ctrl_ext |
+ BP10G_MDIO_DATA_OUT9);
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ (ctrl | BP10G_MCLK_DIR_OUT9) &
+ ~BP10G_MCLK_DATA_OUT9);
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5 |
+ BPCTLI_CTRL_EXT_MDIO_DIR5 |
+ BPCTLI_CTRL_EXT_MDIO_DATA5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MCLK_DATA5)));
+
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, (ctrl_ext |
+ BPCTLI_CTRL_EXT_MDIO_DIR80
+ |
+ BPCTLI_CTRL_EXT_MDIO_DATA80));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl |
+ BPCTLI_CTRL_EXT_MCLK_DIR80)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MCLK_DATA80)));
+
+ } else if (pbpctl_dev->bp_540) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ (ctrl | BP540_MDIO_DIR |
+ BP540_MDIO_DATA |
+ BP540_MCLK_DIR) &
+ ~(BP540_MCLK_DATA));
+
+ } else if (pbpctl_dev->bp_10gb) {
+
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_SET |
+ BP10GB_MCLK_CLR) &
+ ~(BP10GB_MCLK_DIR |
+ BP10GB_MDIO_DIR |
+ BP10GB_MDIO_CLR |
+ BP10GB_MCLK_SET));
+
+ } else if (!pbpctl_dev->bp_10g)
+
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR |
+ BPCTLI_CTRL_EXT_MDIO_DIR |
+ BPCTLI_CTRL_EXT_MDIO_DATA)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MCLK_DATA)));
+ else {
+
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ ((ctrl_ext |
+ BP10G_MDIO_DATA_OUT) &
+ ~(BP10G_MCLK_DATA_OUT)));
+ }
+
+ usec_delay(PULSE_TIME);
+
+ } else {
+ if (pbpctl_dev->bp_10g9) {
+ /* DATA 0 CLK 1 */
+ /*BP10G_WRITE_REG(pbpctl_dev, I2CCTL, ((ctrl_ext|BP10G_MCLK_DATA_OUT9)&~BP10G_MDIO_DATA_OUT9)); */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ (ctrl_ext &
+ ~BP10G_MDIO_DATA_OUT9));
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ (ctrl | BP10G_MCLK_DATA_OUT9 |
+ BP10G_MCLK_DIR_OUT9));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5 |
+ BPCTLI_CTRL_EXT_MDIO_DIR5 |
+ BPCTLI_CTRL_EXT_MCLK_DATA5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA5)));
+
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MDIO_DIR80)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA80)));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ (ctrl |
+ BPCTLI_CTRL_EXT_MCLK_DIR80 |
+ BPCTLI_CTRL_EXT_MCLK_DATA80));
+
+ } else if (pbpctl_dev->bp_540) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ ((ctrl | BP540_MCLK_DIR |
+ BP540_MCLK_DATA |
+ BP540_MDIO_DIR) &
+ ~(BP540_MDIO_DATA)));
+
+ } else if (pbpctl_dev->bp_10gb) {
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_CLR |
+ BP10GB_MCLK_SET) &
+ ~(BP10GB_MCLK_DIR |
+ BP10GB_MDIO_DIR |
+ BP10GB_MDIO_SET |
+ BP10GB_MCLK_CLR));
+
+ } else if (!pbpctl_dev->bp_10g)
+
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR |
+ BPCTLI_CTRL_EXT_MDIO_DIR |
+ BPCTLI_CTRL_EXT_MCLK_DATA)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA)));
+ else {
+
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ ((ctrl_ext |
+ BP10G_MCLK_DATA_OUT) &
+ ~BP10G_MDIO_DATA_OUT));
+
+ }
+ usec_delay(PULSE_TIME);
+ if (pbpctl_dev->bp_10g9) {
+ /* DATA 0 CLK 0 */
+ /*BP10G_WRITE_REG(pbpctl_dev, I2CCTL, (ctrl_ext&~(BP10G_MCLK_DATA_OUT9|BP10G_MDIO_DATA_OUT9))); */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ (ctrl_ext &
+ ~BP10G_MDIO_DATA_OUT9));
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ ((ctrl | BP10G_MCLK_DIR_OUT9) &
+ ~(BP10G_MCLK_DATA_OUT9)));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5 |
+ BPCTLI_CTRL_EXT_MDIO_DIR5)
+ &
+ ~(BPCTLI_CTRL_EXT_MCLK_DATA5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DATA5)));
+
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MDIO_DIR80)
+ &
+ ~BPCTLI_CTRL_EXT_MDIO_DATA80));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl |
+ BPCTLI_CTRL_EXT_MCLK_DIR80)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MCLK_DATA80)));
+
+ } else if (pbpctl_dev->bp_540) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ ((ctrl | BP540_MCLK_DIR |
+ BP540_MDIO_DIR) &
+ ~(BP540_MDIO_DATA |
+ BP540_MCLK_DATA)));
+ } else if (pbpctl_dev->bp_10gb) {
+
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_CLR |
+ BP10GB_MCLK_CLR) &
+ ~(BP10GB_MCLK_DIR |
+ BP10GB_MDIO_DIR |
+ BP10GB_MDIO_SET |
+ BP10GB_MCLK_SET));
+
+ } else if (!pbpctl_dev->bp_10g)
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR |
+ BPCTLI_CTRL_EXT_MDIO_DIR) &
+ ~(BPCTLI_CTRL_EXT_MCLK_DATA
+ |
+ BPCTLI_CTRL_EXT_MDIO_DATA)));
+ else {
+
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ (ctrl_ext &
+ ~(BP10G_MCLK_DATA_OUT |
+ BP10G_MDIO_DATA_OUT)));
+ }
+
+ usec_delay(PULSE_TIME);
+ }
+
+ }
+}
+
+static int read_pulse(bpctl_dev_t *pbpctl_dev, unsigned int ctrl_ext,
+ unsigned char len)
+{
+ unsigned char ctrl_val = 0;
+ unsigned int i = len;
+ unsigned int ctrl = 0;
+ bpctl_dev_t *pbpctl_dev_c = NULL;
+
+ if (pbpctl_dev->bp_i80)
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ if (pbpctl_dev->bp_540)
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+ if (pbpctl_dev->bp_10g9) {
+ if (!(pbpctl_dev_c = get_status_port_fn(pbpctl_dev)))
+ return -1;
+ ctrl = BP10G_READ_REG(pbpctl_dev_c, ESDP);
+ }
+
+
+ while (i--) {
+ if (pbpctl_dev->bp_10g9) {
+ /*BP10G_WRITE_REG(pbpctl_dev, I2CCTL, ((ctrl_ext|BP10G_MDIO_DATA_OUT9)&~BP10G_MCLK_DATA_OUT9)); */
+ /* DATA ? CLK 0 */
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ ((ctrl | BP10G_MCLK_DIR_OUT9) &
+ ~(BP10G_MCLK_DATA_OUT9)));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DIR5
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA5)));
+
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ (ctrl_ext &
+ ~BPCTLI_CTRL_EXT_MDIO_DIR80));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl | BPCTLI_CTRL_EXT_MCLK_DIR80)
+ & ~(BPCTLI_CTRL_EXT_MCLK_DATA80)));
+
+ } else if (pbpctl_dev->bp_540) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ ((ctrl | BP540_MCLK_DIR) &
+ ~(BP540_MDIO_DIR | BP540_MCLK_DATA)));
+
+ } else if (pbpctl_dev->bp_10gb) {
+
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_DIR |
+ BP10GB_MCLK_CLR) & ~(BP10GB_MCLK_DIR |
+ BP10GB_MDIO_CLR |
+ BP10GB_MDIO_SET |
+ BP10GB_MCLK_SET));
+
+ } else if (!pbpctl_dev->bp_10g)
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DIR
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA)));
+ else {
+
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP, ((ctrl_ext | BP10G_MDIO_DATA_OUT) & ~BP10G_MCLK_DATA_OUT)); /* ? */
+ /* printk("0x28=0x%x\n",BP10G_READ_REG(pbpctl_dev,EODSDP);); */
+
+ }
+
+ usec_delay(PULSE_TIME);
+ if (pbpctl_dev->bp_10g9) {
+ /*BP10G_WRITE_REG(pbpctl_dev, I2CCTL, (ctrl_ext|BP10G_MCLK_DATA_OUT9|BP10G_MDIO_DATA_OUT9)); */
+ /* DATA ? CLK 1 */
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ (ctrl | BP10G_MCLK_DATA_OUT9 |
+ BP10G_MCLK_DIR_OUT9));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DIR5)));
+
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ (ctrl_ext &
+ ~(BPCTLI_CTRL_EXT_MDIO_DIR80)));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ (ctrl | BPCTLI_CTRL_EXT_MCLK_DIR80 |
+ BPCTLI_CTRL_EXT_MCLK_DATA80));
+
+ } else if (pbpctl_dev->bp_540) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ ((ctrl | BP540_MCLK_DIR |
+ BP540_MCLK_DATA) &
+ ~(BP540_MDIO_DIR)));
+
+ } else if (pbpctl_dev->bp_10gb) {
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_DIR |
+ BP10GB_MCLK_SET) & ~(BP10GB_MCLK_DIR |
+ BP10GB_MDIO_CLR |
+ BP10GB_MDIO_SET |
+ BP10GB_MCLK_CLR));
+
+ } else if (!pbpctl_dev->bp_10g)
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DIR)));
+ else {
+
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ (ctrl_ext | BP10G_MCLK_DATA_OUT |
+ BP10G_MDIO_DATA_OUT));
+
+ }
+ if (pbpctl_dev->bp_10g9) {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, I2CCTL);
+
+ } else if ((pbpctl_dev->bp_fiber5) || (pbpctl_dev->bp_i80)) {
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL);
+ } else if (pbpctl_dev->bp_540) {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, ESDP);
+ } else if (pbpctl_dev->bp_10gb)
+ ctrl_ext = BP10GB_READ_REG(pbpctl_dev, MISC_REG_SPIO);
+
+ else if (!pbpctl_dev->bp_10g)
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ else
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, EODSDP);
+
+ usec_delay(PULSE_TIME);
+ if (pbpctl_dev->bp_10g9) {
+ if (ctrl_ext & BP10G_MDIO_DATA_IN9)
+ ctrl_val |= 1 << i;
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ if (ctrl_ext & BPCTLI_CTRL_EXT_MDIO_DATA5)
+ ctrl_val |= 1 << i;
+ } else if (pbpctl_dev->bp_i80) {
+ if (ctrl_ext & BPCTLI_CTRL_EXT_MDIO_DATA80)
+ ctrl_val |= 1 << i;
+ } else if (pbpctl_dev->bp_540) {
+ if (ctrl_ext & BP540_MDIO_DATA)
+ ctrl_val |= 1 << i;
+ } else if (pbpctl_dev->bp_10gb) {
+ if (ctrl_ext & BP10GB_MDIO_DATA)
+ ctrl_val |= 1 << i;
+
+ } else if (!pbpctl_dev->bp_10g) {
+
+ if (ctrl_ext & BPCTLI_CTRL_EXT_MDIO_DATA)
+ ctrl_val |= 1 << i;
+ } else {
+
+ if (ctrl_ext & BP10G_MDIO_DATA_IN)
+ ctrl_val |= 1 << i;
+ }
+
+ }
+
+ return ctrl_val;
+}
+
+static void write_reg(bpctl_dev_t *pbpctl_dev, unsigned char value,
+ unsigned char addr)
+{
+ uint32_t ctrl_ext = 0, ctrl = 0;
+ bpctl_dev_t *pbpctl_dev_c = NULL;
+ unsigned long flags;
+ if (pbpctl_dev->bp_10g9) {
+ if (!(pbpctl_dev_c = get_status_port_fn(pbpctl_dev)))
+ return;
+ }
+ if ((pbpctl_dev->wdt_status == WDT_STATUS_EN) &&
+ (pbpctl_dev->bp_ext_ver < PXG4BPFI_VER))
+ wdt_time_left(pbpctl_dev);
+
+#ifdef BP_SYNC_FLAG
+ spin_lock_irqsave(&pbpctl_dev->bypass_wr_lock, flags);
+#else
+ atomic_set(&pbpctl_dev->wdt_busy, 1);
+#endif
+ if (pbpctl_dev->bp_10g9) {
+
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, I2CCTL);
+ ctrl = BP10G_READ_REG(pbpctl_dev_c, ESDP);
+ /* DATA 0 CLK 0 */
+ /* BP10G_WRITE_REG(pbpctl_dev, I2CCTL, (ctrl_ext&~(BP10G_MCLK_DATA_OUT9|BP10G_MDIO_DATA_OUT9))); */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ (ctrl_ext & ~BP10G_MDIO_DATA_OUT9));
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ ((ctrl | BP10G_MCLK_DIR_OUT9) &
+ ~(BP10G_MCLK_DATA_OUT9)));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA5
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA5)));
+ } else if (pbpctl_dev->bp_i80) {
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL);
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MDIO_DIR80)
+ &
+ ~BPCTLI_CTRL_EXT_MDIO_DATA80));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl | BPCTLI_CTRL_EXT_MCLK_DIR80) &
+ ~BPCTLI_CTRL_EXT_MCLK_DATA80));
+
+ } else if (pbpctl_dev->bp_540) {
+ ctrl = ctrl_ext = BP10G_READ_REG(pbpctl_dev, ESDP);
+ BP10G_WRITE_REG(pbpctl_dev, ESDP, ((ctrl |
+ BP540_MDIO_DIR |
+ BP540_MCLK_DIR) &
+ ~(BP540_MDIO_DATA |
+ BP540_MCLK_DATA)));
+
+ } else if (pbpctl_dev->bp_10gb) {
+ ctrl_ext = BP10GB_READ_REG(pbpctl_dev, MISC_REG_SPIO);
+
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_CLR | BP10GB_MCLK_CLR)
+ & ~(BP10GB_MCLK_DIR | BP10GB_MDIO_DIR |
+ BP10GB_MDIO_SET | BP10GB_MCLK_SET));
+
+ } else if (!pbpctl_dev->bp_10g) {
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA)));
+ } else {
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, EODSDP);
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ (ctrl_ext &
+ ~(BP10G_MCLK_DATA_OUT | BP10G_MDIO_DATA_OUT)));
+ }
+ usec_delay(CMND_INTERVAL);
+
+ /*send sync cmd */
+ write_pulse(pbpctl_dev, ctrl_ext, SYNC_CMD_VAL, SYNC_CMD_LEN);
+ /*send wr cmd */
+ write_pulse(pbpctl_dev, ctrl_ext, WR_CMD_VAL, WR_CMD_LEN);
+ write_pulse(pbpctl_dev, ctrl_ext, addr, ADDR_CMD_LEN);
+
+ /*write data */
+ write_pulse(pbpctl_dev, ctrl_ext, value, WR_DATA_LEN);
+ if (pbpctl_dev->bp_10g9) {
+ /*BP10G_WRITE_REG(pbpctl_dev, I2CCTL, (ctrl_ext&~(BP10G_MCLK_DATA_OUT9|BP10G_MDIO_DATA_OUT9))); */
+ /* DATA 0 CLK 0 */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ (ctrl_ext & ~BP10G_MDIO_DATA_OUT9));
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ ((ctrl | BP10G_MCLK_DIR_OUT9) &
+ ~(BP10G_MCLK_DATA_OUT9)));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA5
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA5)));
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MDIO_DIR80)
+ &
+ ~BPCTLI_CTRL_EXT_MDIO_DATA80));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl | BPCTLI_CTRL_EXT_MCLK_DIR80) &
+ ~BPCTLI_CTRL_EXT_MCLK_DATA80));
+ } else if (pbpctl_dev->bp_540) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP, ((ctrl |
+ BP540_MDIO_DIR |
+ BP540_MCLK_DIR) &
+ ~(BP540_MDIO_DATA |
+ BP540_MCLK_DATA)));
+ } else if (pbpctl_dev->bp_10gb) {
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_CLR | BP10GB_MCLK_CLR)
+ & ~(BP10GB_MCLK_DIR | BP10GB_MDIO_DIR |
+ BP10GB_MDIO_SET | BP10GB_MCLK_SET));
+
+ } else if (!pbpctl_dev->bp_10g)
+
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA)));
+ else {
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ (ctrl_ext &
+ ~(BP10G_MCLK_DATA_OUT | BP10G_MDIO_DATA_OUT)));
+
+ }
+
+ usec_delay(CMND_INTERVAL * 4);
+
+ if ((pbpctl_dev->wdt_status == WDT_STATUS_EN) &&
+ (pbpctl_dev->bp_ext_ver < PXG4BPFI_VER) && (addr == CMND_REG_ADDR))
+ pbpctl_dev->bypass_wdt_on_time = jiffies;
+#ifdef BP_SYNC_FLAG
+ spin_unlock_irqrestore(&pbpctl_dev->bypass_wr_lock, flags);
+#else
+ atomic_set(&pbpctl_dev->wdt_busy, 0);
+#endif
+
+}
+
+static void write_data(bpctl_dev_t *pbpctl_dev, unsigned char value)
+{
+ write_reg(pbpctl_dev, value, CMND_REG_ADDR);
+}
+
+static int read_reg(bpctl_dev_t *pbpctl_dev, unsigned char addr)
+{
+ uint32_t ctrl_ext = 0, ctrl = 0, ctrl_value = 0;
+ bpctl_dev_t *pbpctl_dev_c = NULL;
+
+#ifdef BP_SYNC_FLAG
+ unsigned long flags;
+ spin_lock_irqsave(&pbpctl_dev->bypass_wr_lock, flags);
+#else
+ atomic_set(&pbpctl_dev->wdt_busy, 1);
+#endif
+ if (pbpctl_dev->bp_10g9) {
+ if (!(pbpctl_dev_c = get_status_port_fn(pbpctl_dev)))
+ return -1;
+ }
+
+ if (pbpctl_dev->bp_10g9) {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, I2CCTL);
+ ctrl = BP10G_READ_REG(pbpctl_dev_c, ESDP);
+
+ /* BP10G_WRITE_REG(pbpctl_dev, I2CCTL, (ctrl_ext&~(BP10G_MCLK_DATA_OUT9|BP10G_MDIO_DATA_OUT9))); */
+ /* DATA 0 CLK 0 */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ (ctrl_ext & ~BP10G_MDIO_DATA_OUT9));
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ ((ctrl | BP10G_MCLK_DIR_OUT9) &
+ ~(BP10G_MCLK_DATA_OUT9)));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL);
+
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA5
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA5)));
+ } else if (pbpctl_dev->bp_i80) {
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL);
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MDIO_DIR80)
+ &
+ ~BPCTLI_CTRL_EXT_MDIO_DATA80));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl | BPCTLI_CTRL_EXT_MCLK_DIR80) &
+ ~BPCTLI_CTRL_EXT_MCLK_DATA80));
+ } else if (pbpctl_dev->bp_540) {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, ESDP);
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+
+ BP10G_WRITE_REG(pbpctl_dev, ESDP, ((ctrl | BP540_MCLK_DIR |
+ BP540_MDIO_DIR) &
+ ~(BP540_MDIO_DATA |
+ BP540_MCLK_DATA)));
+ } else if (pbpctl_dev->bp_10gb) {
+ ctrl_ext = BP10GB_READ_REG(pbpctl_dev, MISC_REG_SPIO);
+
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_CLR | BP10GB_MCLK_CLR)
+ & ~(BP10GB_MCLK_DIR | BP10GB_MDIO_DIR |
+ BP10GB_MDIO_SET | BP10GB_MCLK_SET));
+#if 0
+
+ /*BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO, (ctrl_ext | BP10GB_MCLK_DIR | BP10GB_MDIO_DIR|
+ BP10GB_MCLK_CLR|BP10GB_MDIO_CLR));
+ ctrl_ext = BP10GB_READ_REG(pbpctl_dev, MISC_REG_SPIO);
+ printk("1reg=%x\n", ctrl_ext); */
+
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO, ((ctrl_ext |
+ BP10GB_MCLK_SET |
+ BP10GB_MDIO_CLR))
+ & ~(BP10GB_MCLK_CLR | BP10GB_MDIO_SET |
+ BP10GB_MCLK_DIR | BP10GB_MDIO_DIR));
+
+ /* bnx2x_set_spio(pbpctl_dev, 5, MISC_REGISTERS_SPIO_OUTPUT_LOW);
+ bnx2x_set_spio(pbpctl_dev, 4, MISC_REGISTERS_SPIO_OUTPUT_LOW);
+ bnx2x_set_spio(pbpctl_dev, 4, MISC_REGISTERS_SPIO_INPUT_HI_Z); */
+
+ ctrl_ext = BP10GB_READ_REG(pbpctl_dev, MISC_REG_SPIO);
+
+ printk("2reg=%x\n", ctrl_ext);
+
+#ifdef BP_SYNC_FLAG
+ spin_unlock_irqrestore(&pbpctl_dev->bypass_wr_lock, flags);
+#else
+ atomic_set(&pbpctl_dev->wdt_busy, 0);
+#endif
+
+ return 0;
+
+#endif
+
+ } else if (!pbpctl_dev->bp_10g) {
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA)));
+ } else {
+
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, EODSDP);
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ (ctrl_ext &
+ ~(BP10G_MCLK_DATA_OUT | BP10G_MDIO_DATA_OUT)));
+
+ }
+
+ usec_delay(CMND_INTERVAL);
+
+ /*send sync cmd */
+ write_pulse(pbpctl_dev, ctrl_ext, SYNC_CMD_VAL, SYNC_CMD_LEN);
+ /*send rd cmd */
+ write_pulse(pbpctl_dev, ctrl_ext, RD_CMD_VAL, RD_CMD_LEN);
+ /*send addr */
+ write_pulse(pbpctl_dev, ctrl_ext, addr, ADDR_CMD_LEN);
+ /*read data */
+ /* zero */
+ if (pbpctl_dev->bp_10g9) {
+ /* DATA 0 CLK 1 */
+ /*BP10G_WRITE_REG(pbpctl_dev, I2CCTL, (ctrl_ext|BP10G_MCLK_DATA_OUT9|BP10G_MDIO_DATA_OUT9)); */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ (ctrl_ext | BP10G_MDIO_DATA_OUT9));
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ (ctrl | BP10G_MCLK_DATA_OUT9 |
+ BP10G_MCLK_DIR_OUT9));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DIR5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DATA5)));
+
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ (ctrl_ext &
+ ~(BPCTLI_CTRL_EXT_MDIO_DATA80 |
+ BPCTLI_CTRL_EXT_MDIO_DIR80)));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ (ctrl | BPCTLI_CTRL_EXT_MCLK_DIR80 |
+ BPCTLI_CTRL_EXT_MCLK_DATA80));
+
+ } else if (pbpctl_dev->bp_540) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ (((ctrl | BP540_MDIO_DIR | BP540_MCLK_DIR |
+ BP540_MCLK_DATA) & ~BP540_MDIO_DATA)));
+
+ } else if (pbpctl_dev->bp_10gb) {
+
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_DIR | BP10GB_MCLK_SET)
+ & ~(BP10GB_MCLK_DIR | BP10GB_MDIO_SET |
+ BP10GB_MDIO_CLR | BP10GB_MCLK_CLR));
+
+ } else if (!pbpctl_dev->bp_10g)
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DIR
+ |
+ BPCTLI_CTRL_EXT_MDIO_DATA)));
+ else {
+
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ (ctrl_ext | BP10G_MCLK_DATA_OUT |
+ BP10G_MDIO_DATA_OUT));
+
+
+ }
+ usec_delay(PULSE_TIME);
+
+ ctrl_value = read_pulse(pbpctl_dev, ctrl_ext, RD_DATA_LEN);
+
+ if (pbpctl_dev->bp_10g9) {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, I2CCTL);
+ ctrl = BP10G_READ_REG(pbpctl_dev_c, ESDP);
+
+ /* BP10G_WRITE_REG(pbpctl_dev, I2CCTL, (ctrl_ext&~(BP10G_MCLK_DATA_OUT9|BP10G_MDIO_DATA_OUT9))); */
+ /* DATA 0 CLK 0 */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ (ctrl_ext & ~BP10G_MDIO_DATA_OUT9));
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ ((ctrl | BP10G_MCLK_DIR_OUT9) &
+ ~(BP10G_MCLK_DATA_OUT9)));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA5
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA5)));
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MDIO_DIR80)
+ &
+ ~BPCTLI_CTRL_EXT_MDIO_DATA80));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl | BPCTLI_CTRL_EXT_MCLK_DIR80) &
+ ~BPCTLI_CTRL_EXT_MCLK_DATA80));
+
+ } else if (pbpctl_dev->bp_540) {
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+ BP10G_WRITE_REG(pbpctl_dev, ESDP, ((ctrl | BP540_MCLK_DIR |
+ BP540_MDIO_DIR) &
+ ~(BP540_MDIO_DATA |
+ BP540_MCLK_DATA)));
+
+ } else if (pbpctl_dev->bp_10gb) {
+ ctrl_ext = BP10GB_READ_REG(pbpctl_dev, MISC_REG_SPIO);
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_CLR | BP10GB_MCLK_CLR)
+ & ~(BP10GB_MCLK_DIR | BP10GB_MDIO_DIR |
+ BP10GB_MDIO_SET | BP10GB_MCLK_SET));
+
+ } else if (!pbpctl_dev->bp_10g) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA)));
+ } else {
+
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, EODSDP);
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ (ctrl_ext &
+ ~(BP10G_MCLK_DATA_OUT | BP10G_MDIO_DATA_OUT)));
+
+ }
+
+ usec_delay(CMND_INTERVAL * 4);
+#ifdef BP_SYNC_FLAG
+ spin_unlock_irqrestore(&pbpctl_dev->bypass_wr_lock, flags);
+#else
+ atomic_set(&pbpctl_dev->wdt_busy, 0);
+#endif
+
+ return ctrl_value;
+}
+
+static int wdt_pulse(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t ctrl_ext = 0, ctrl = 0;
+ bpctl_dev_t *pbpctl_dev_c = NULL;
+
+#ifdef BP_SYNC_FLAG
+ unsigned long flags;
+
+ spin_lock_irqsave(&pbpctl_dev->bypass_wr_lock, flags);
+#else
+
+ if ((atomic_read(&pbpctl_dev->wdt_busy)) == 1)
+ return -1;
+#endif
+ if (pbpctl_dev->bp_10g9) {
+ if (!(pbpctl_dev_c = get_status_port_fn(pbpctl_dev)))
+ return -1;
+ }
+
+ if (pbpctl_dev->bp_10g9) {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, I2CCTL);
+ ctrl = BP10G_READ_REG(pbpctl_dev_c, ESDP);
+
+ /* BP10G_WRITE_REG(pbpctl_dev, I2CCTL, (ctrl_ext&~(BP10G_MCLK_DATA_OUT9|BP10G_MDIO_DATA_OUT9))); */
+ /* DATA 0 CLK 0 */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ (ctrl_ext & ~BP10G_MDIO_DATA_OUT9));
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ ((ctrl | BP10G_MCLK_DIR_OUT9) &
+ ~(BP10G_MCLK_DATA_OUT9)));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA5
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA5)));
+ } else if (pbpctl_dev->bp_i80) {
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL);
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MDIO_DIR80)
+ &
+ ~BPCTLI_CTRL_EXT_MDIO_DATA80));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl | BPCTLI_CTRL_EXT_MCLK_DIR80) &
+ ~BPCTLI_CTRL_EXT_MCLK_DATA80));
+ } else if (pbpctl_dev->bp_540) {
+ ctrl_ext = ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+ BP10G_WRITE_REG(pbpctl_dev, ESDP, ((ctrl | BP540_MCLK_DIR |
+ BP540_MDIO_DIR) &
+ ~(BP540_MDIO_DATA |
+ BP540_MCLK_DATA)));
+ } else if (pbpctl_dev->bp_10gb) {
+ ctrl_ext = BP10GB_READ_REG(pbpctl_dev, MISC_REG_SPIO);
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_CLR | BP10GB_MCLK_CLR)
+ & ~(BP10GB_MCLK_DIR | BP10GB_MDIO_DIR |
+ BP10GB_MDIO_SET | BP10GB_MCLK_SET));
+
+ } else if (!pbpctl_dev->bp_10g) {
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA)));
+ } else {
+
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev, EODSDP);
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ (ctrl_ext &
+ ~(BP10G_MCLK_DATA_OUT | BP10G_MDIO_DATA_OUT)));
+
+ }
+ if (pbpctl_dev->bp_10g9) {
+ /* BP10G_WRITE_REG(pbpctl_dev, I2CCTL, ((ctrl_ext|BP10G_MCLK_DATA_OUT9)&~BP10G_MDIO_DATA_OUT9)); */
+ /* DATA 0 CLK 1 */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ (ctrl_ext & ~BP10G_MDIO_DATA_OUT9));
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ (ctrl | BP10G_MCLK_DATA_OUT9 |
+ BP10G_MCLK_DIR_OUT9));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR5
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA5)));
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MDIO_DIR80)
+ &
+ ~BPCTLI_CTRL_EXT_MDIO_DATA80));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ (ctrl | BPCTLI_CTRL_EXT_MCLK_DIR80 |
+ BPCTLI_CTRL_EXT_MCLK_DATA80));
+
+ } else if (pbpctl_dev->bp_540) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP, ((ctrl |
+ BP540_MDIO_DIR |
+ BP540_MCLK_DIR |
+ BP540_MCLK_DATA) &
+ ~BP540_MDIO_DATA));
+
+ } else if (pbpctl_dev->bp_10gb) {
+ ctrl_ext = BP10GB_READ_REG(pbpctl_dev, MISC_REG_SPIO);
+
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_CLR | BP10GB_MCLK_SET)
+ & ~(BP10GB_MCLK_DIR | BP10GB_MDIO_DIR |
+ BP10GB_MDIO_SET | BP10GB_MCLK_CLR));
+
+ } else if (!pbpctl_dev->bp_10g)
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR
+ |
+ BPCTLI_CTRL_EXT_MCLK_DATA)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MDIO_DATA)));
+ else {
+
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ ((ctrl_ext | BP10G_MCLK_DATA_OUT) &
+ ~BP10G_MDIO_DATA_OUT));
+
+ }
+
+ usec_delay(WDT_INTERVAL);
+ if (pbpctl_dev->bp_10g9) {
+ /* BP10G_WRITE_REG(pbpctl_dev, I2CCTL, (ctrl_ext&~(BP10G_MCLK_DATA_OUT9|BP10G_MDIO_DATA_OUT9))); */
+ /* DATA 0 CLK 0 */
+ BP10G_WRITE_REG(pbpctl_dev, I2CCTL,
+ (ctrl_ext & ~BP10G_MDIO_DATA_OUT9));
+ BP10G_WRITE_REG(pbpctl_dev_c, ESDP,
+ ((ctrl | BP10G_MCLK_DIR_OUT9) &
+ ~(BP10G_MCLK_DATA_OUT9)));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR5)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MCLK_DATA5
+ |
+ BPCTLI_CTRL_EXT_MDIO_DATA5)));
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MDIO_DIR80)
+ &
+ ~BPCTLI_CTRL_EXT_MDIO_DATA80));
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl | BPCTLI_CTRL_EXT_MCLK_DIR80) &
+ ~BPCTLI_CTRL_EXT_MCLK_DATA80));
+
+ } else if (pbpctl_dev->bp_540) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP, ((ctrl | BP540_MCLK_DIR |
+ BP540_MDIO_DIR) &
+ ~(BP540_MDIO_DATA |
+ BP540_MCLK_DATA)));
+
+ } else if (pbpctl_dev->bp_10gb) {
+ ctrl_ext = BP10GB_READ_REG(pbpctl_dev, MISC_REG_SPIO);
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_SPIO,
+ (ctrl_ext | BP10GB_MDIO_CLR | BP10GB_MCLK_CLR)
+ & ~(BP10GB_MCLK_DIR | BP10GB_MDIO_DIR |
+ BP10GB_MDIO_SET | BP10GB_MCLK_SET));
+
+ } else if (!pbpctl_dev->bp_10g)
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MCLK_DATA
+ |
+ BPCTLI_CTRL_EXT_MDIO_DATA)));
+ else {
+
+ BP10G_WRITE_REG(pbpctl_dev, EODSDP,
+ (ctrl_ext &
+ ~(BP10G_MCLK_DATA_OUT | BP10G_MDIO_DATA_OUT)));
+ }
+ if ((pbpctl_dev->wdt_status == WDT_STATUS_EN) /*&&
+ (pbpctl_dev->bp_ext_ver<PXG4BPFI_VER) */ )
+ pbpctl_dev->bypass_wdt_on_time = jiffies;
+#ifdef BP_SYNC_FLAG
+ spin_unlock_irqrestore(&pbpctl_dev->bypass_wr_lock, flags);
+#endif
+ usec_delay(CMND_INTERVAL * 4);
+ return 0;
+}
+
+static void data_pulse(bpctl_dev_t *pbpctl_dev, unsigned char value)
+{
+
+ uint32_t ctrl_ext = 0;
+#ifdef BP_SYNC_FLAG
+ unsigned long flags;
+#endif
+ wdt_time_left(pbpctl_dev);
+#ifdef BP_SYNC_FLAG
+ spin_lock_irqsave(&pbpctl_dev->bypass_wr_lock, flags);
+#else
+ atomic_set(&pbpctl_dev->wdt_busy, 1);
+#endif
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_SDP6_DIR |
+ BPCTLI_CTRL_EXT_SDP7_DIR) &
+ ~(BPCTLI_CTRL_EXT_SDP6_DATA |
+ BPCTLI_CTRL_EXT_SDP7_DATA)));
+
+ usec_delay(INIT_CMND_INTERVAL);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_SDP6_DIR |
+ BPCTLI_CTRL_EXT_SDP7_DIR |
+ BPCTLI_CTRL_EXT_SDP6_DATA) &
+ ~
+ (BPCTLI_CTRL_EXT_SDP7_DATA)));
+ usec_delay(INIT_CMND_INTERVAL);
+
+ while (value) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ctrl_ext |
+ BPCTLI_CTRL_EXT_SDP6_DIR |
+ BPCTLI_CTRL_EXT_SDP7_DIR |
+ BPCTLI_CTRL_EXT_SDP6_DATA |
+ BPCTLI_CTRL_EXT_SDP7_DATA);
+ usec_delay(PULSE_INTERVAL);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_SDP6_DIR
+ |
+ BPCTLI_CTRL_EXT_SDP7_DIR
+ |
+ BPCTLI_CTRL_EXT_SDP6_DATA)
+ &
+ ~BPCTLI_CTRL_EXT_SDP7_DATA));
+ usec_delay(PULSE_INTERVAL);
+ value--;
+
+ }
+ usec_delay(INIT_CMND_INTERVAL - PULSE_INTERVAL);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_SDP6_DIR |
+ BPCTLI_CTRL_EXT_SDP7_DIR) &
+ ~(BPCTLI_CTRL_EXT_SDP6_DATA |
+ BPCTLI_CTRL_EXT_SDP7_DATA)));
+ usec_delay(WDT_TIME_CNT);
+ if (pbpctl_dev->wdt_status == WDT_STATUS_EN)
+ pbpctl_dev->bypass_wdt_on_time = jiffies;
+#ifdef BP_SYNC_FLAG
+ spin_unlock_irqrestore(&pbpctl_dev->bypass_wr_lock, flags);
+#else
+ atomic_set(&pbpctl_dev->wdt_busy, 0);
+#endif
+
+}
+
+static int send_wdt_pulse(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t ctrl_ext = 0;
+
+#ifdef BP_SYNC_FLAG
+ unsigned long flags;
+
+ spin_lock_irqsave(&pbpctl_dev->bypass_wr_lock, flags);
+#else
+
+ if ((atomic_read(&pbpctl_dev->wdt_busy)) == 1)
+ return -1;
+#endif
+ wdt_time_left(pbpctl_dev);
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ctrl_ext | /* 1 */
+ BPCTLI_CTRL_EXT_SDP7_DIR |
+ BPCTLI_CTRL_EXT_SDP7_DATA);
+ usec_delay(PULSE_INTERVAL);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext | /* 0 */
+ BPCTLI_CTRL_EXT_SDP7_DIR) &
+ ~BPCTLI_CTRL_EXT_SDP7_DATA));
+
+ usec_delay(PULSE_INTERVAL);
+ if (pbpctl_dev->wdt_status == WDT_STATUS_EN)
+ pbpctl_dev->bypass_wdt_on_time = jiffies;
+#ifdef BP_SYNC_FLAG
+ spin_unlock_irqrestore(&pbpctl_dev->bypass_wr_lock, flags);
+#endif
+
+ return 0;
+}
+
+void send_bypass_clear_pulse(bpctl_dev_t *pbpctl_dev, unsigned int value)
+{
+ uint32_t ctrl_ext = 0;
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext | /* 0 */
+ BPCTLI_CTRL_EXT_SDP6_DIR) &
+ ~BPCTLI_CTRL_EXT_SDP6_DATA));
+
+ usec_delay(PULSE_INTERVAL);
+ while (value) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ctrl_ext | /* 1 */
+ BPCTLI_CTRL_EXT_SDP6_DIR |
+ BPCTLI_CTRL_EXT_SDP6_DATA);
+ usec_delay(PULSE_INTERVAL);
+ value--;
+ }
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext | /* 0 */
+ BPCTLI_CTRL_EXT_SDP6_DIR) &
+ ~BPCTLI_CTRL_EXT_SDP6_DATA));
+ usec_delay(PULSE_INTERVAL);
+}
+
+/* #endif OLD_FW */
+#ifdef BYPASS_DEBUG
+
+int pulse_set_fn(bpctl_dev_t *pbpctl_dev, unsigned int counter)
+{
+ uint32_t ctrl_ext = 0;
+
+ if (!pbpctl_dev)
+ return -1;
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ write_pulse_1(pbpctl_dev, ctrl_ext, counter, counter);
+
+ pbpctl_dev->bypass_wdt_status = 0;
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ write_pulse_1(pbpctl_dev, ctrl_ext, counter, counter);
+ } else {
+ wdt_time_left(pbpctl_dev);
+ if (pbpctl_dev->wdt_status == WDT_STATUS_EN) {
+ pbpctl_dev->wdt_status = 0;
+ data_pulse(pbpctl_dev, counter);
+ pbpctl_dev->wdt_status = WDT_STATUS_EN;
+ pbpctl_dev->bypass_wdt_on_time = jiffies;
+
+ } else
+ data_pulse(pbpctl_dev, counter);
+ }
+
+ return 0;
+}
+
+int zero_set_fn(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t ctrl_ext = 0, ctrl_value = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ printk("zero_set");
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_MCLK_DIR)
+ &
+ ~
+ (BPCTLI_CTRL_EXT_MCLK_DATA
+ |
+ BPCTLI_CTRL_EXT_MDIO_DIR
+ |
+ BPCTLI_CTRL_EXT_MDIO_DATA)));
+
+ }
+ return ctrl_value;
+}
+
+int pulse_get2_fn(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t ctrl_ext = 0, ctrl_value = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ printk("pulse_get_fn\n");
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ ctrl_value = read_pulse_2(pbpctl_dev, ctrl_ext);
+ printk("read:%d\n", ctrl_value);
+ }
+ return ctrl_value;
+}
+
+int pulse_get1_fn(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t ctrl_ext = 0, ctrl_value = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+
+ printk("pulse_get_fn\n");
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ ctrl_value = read_pulse_1(pbpctl_dev, ctrl_ext);
+ printk("read:%d\n", ctrl_value);
+ }
+ return ctrl_value;
+}
+
+int gpio6_set_fn(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t ctrl_ext = 0;
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ctrl_ext |
+ BPCTLI_CTRL_EXT_SDP6_DIR |
+ BPCTLI_CTRL_EXT_SDP6_DATA);
+ return 0;
+}
+
+int gpio7_set_fn(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t ctrl_ext = 0;
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ctrl_ext |
+ BPCTLI_CTRL_EXT_SDP7_DIR |
+ BPCTLI_CTRL_EXT_SDP7_DATA);
+ return 0;
+}
+
+int gpio7_clear_fn(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t ctrl_ext = 0;
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_SDP7_DIR) &
+ ~BPCTLI_CTRL_EXT_SDP7_DATA));
+ return 0;
+}
+
+int gpio6_clear_fn(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t ctrl_ext = 0;
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, ((ctrl_ext |
+ BPCTLI_CTRL_EXT_SDP6_DIR) &
+ ~BPCTLI_CTRL_EXT_SDP6_DATA));
+ return 0;
+}
+#endif /*BYPASS_DEBUG */
+
+static bpctl_dev_t *get_status_port_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int idx_dev = 0;
+
+ if (pbpctl_dev == NULL)
+ return NULL;
+
+ if ((pbpctl_dev->func == 0) || (pbpctl_dev->func == 2)) {
+ for (idx_dev = 0;
+ ((bpctl_dev_arr[idx_dev].pdev != NULL)
+ && (idx_dev < device_num)); idx_dev++) {
+ if ((bpctl_dev_arr[idx_dev].bus == pbpctl_dev->bus)
+ && (bpctl_dev_arr[idx_dev].slot == pbpctl_dev->slot)
+ && ((bpctl_dev_arr[idx_dev].func == 1)
+ && (pbpctl_dev->func == 0))) {
+
+ return &(bpctl_dev_arr[idx_dev]);
+ }
+ if ((bpctl_dev_arr[idx_dev].bus == pbpctl_dev->bus) &&
+ (bpctl_dev_arr[idx_dev].slot == pbpctl_dev->slot) &&
+ ((bpctl_dev_arr[idx_dev].func == 3)
+ && (pbpctl_dev->func == 2))) {
+
+ return &(bpctl_dev_arr[idx_dev]);
+ }
+ }
+ }
+ return NULL;
+}
+
+static bpctl_dev_t *get_master_port_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int idx_dev = 0;
+
+ if (pbpctl_dev == NULL)
+ return NULL;
+
+ if ((pbpctl_dev->func == 1) || (pbpctl_dev->func == 3)) {
+ for (idx_dev = 0;
+ ((bpctl_dev_arr[idx_dev].pdev != NULL)
+ && (idx_dev < device_num)); idx_dev++) {
+ if ((bpctl_dev_arr[idx_dev].bus == pbpctl_dev->bus)
+ && (bpctl_dev_arr[idx_dev].slot == pbpctl_dev->slot)
+ && ((bpctl_dev_arr[idx_dev].func == 0)
+ && (pbpctl_dev->func == 1))) {
+
+ return &(bpctl_dev_arr[idx_dev]);
+ }
+ if ((bpctl_dev_arr[idx_dev].bus == pbpctl_dev->bus) &&
+ (bpctl_dev_arr[idx_dev].slot == pbpctl_dev->slot) &&
+ ((bpctl_dev_arr[idx_dev].func == 2)
+ && (pbpctl_dev->func == 3))) {
+
+ return &(bpctl_dev_arr[idx_dev]);
+ }
+ }
+ }
+ return NULL;
+}
+
+/**************************************/
+/**************INTEL API***************/
+/**************************************/
+
+static void write_data_port_int(bpctl_dev_t *pbpctl_dev,
+ unsigned char ctrl_value)
+{
+ uint32_t value;
+
+ value = BPCTL_READ_REG(pbpctl_dev, CTRL);
+/* Make SDP0 Pin Directonality to Output */
+ value |= BPCTLI_CTRL_SDP0_DIR;
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, value);
+
+ value &= ~BPCTLI_CTRL_SDP0_DATA;
+ value |= ((ctrl_value & 0x1) << BPCTLI_CTRL_SDP0_SHIFT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL, value);
+
+ value = (BPCTL_READ_REG(pbpctl_dev, CTRL_EXT));
+/* Make SDP2 Pin Directonality to Output */
+ value |= BPCTLI_CTRL_EXT_SDP6_DIR;
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, value);
+
+ value &= ~BPCTLI_CTRL_EXT_SDP6_DATA;
+ value |= (((ctrl_value & 0x2) >> 1) << BPCTLI_CTRL_EXT_SDP6_SHIFT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT, value);
+
+}
+
+static int write_data_int(bpctl_dev_t *pbpctl_dev, unsigned char value)
+{
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+
+ if (!(pbpctl_dev_b = get_status_port_fn(pbpctl_dev)))
+ return -1;
+ atomic_set(&pbpctl_dev->wdt_busy, 1);
+ write_data_port_int(pbpctl_dev, value & 0x3);
+ write_data_port_int(pbpctl_dev_b, ((value & 0xc) >> 2));
+ atomic_set(&pbpctl_dev->wdt_busy, 0);
+
+ return 0;
+}
+
+static int wdt_pulse_int(bpctl_dev_t *pbpctl_dev)
+{
+
+ if ((atomic_read(&pbpctl_dev->wdt_busy)) == 1)
+ return -1;
+
+ if ((write_data_int(pbpctl_dev, RESET_WDT_INT)) < 0)
+ return -1;
+ msec_delay_bp(CMND_INTERVAL_INT);
+ if ((write_data_int(pbpctl_dev, CMND_OFF_INT)) < 0)
+ return -1;
+ msec_delay_bp(CMND_INTERVAL_INT);
+
+ if (pbpctl_dev->wdt_status == WDT_STATUS_EN)
+ pbpctl_dev->bypass_wdt_on_time = jiffies;
+
+ return 0;
+}
+
+/*************************************/
+/************* COMMANDS **************/
+/*************************************/
+
+/* CMND_ON 0x4 (100)*/
+int cmnd_on(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice))
+ return 0;
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER)
+ write_data(pbpctl_dev, CMND_ON);
+ else
+ data_pulse(pbpctl_dev, CMND_ON);
+ ret = 0;
+ }
+ return ret;
+}
+
+/* CMND_OFF 0x2 (10)*/
+int cmnd_off(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice)) {
+ write_data_int(pbpctl_dev, CMND_OFF_INT);
+ msec_delay_bp(CMND_INTERVAL_INT);
+ } else if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER)
+ write_data(pbpctl_dev, CMND_OFF);
+ else
+ data_pulse(pbpctl_dev, CMND_OFF);
+ ret = 0;
+ };
+ return ret;
+}
+
+/* BYPASS_ON (0xa)*/
+int bypass_on(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+
+ if (pbpctl_dev->bp_caps & BP_CAP) {
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice)) {
+ write_data_int(pbpctl_dev, BYPASS_ON_INT);
+ msec_delay_bp(BYPASS_DELAY_INT);
+ pbpctl_dev->bp_status_un = 0;
+ } else if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ write_data(pbpctl_dev, BYPASS_ON);
+ if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER)
+ msec_delay_bp(LATCH_DELAY);
+ } else
+ data_pulse(pbpctl_dev, BYPASS_ON);
+ ret = 0;
+ };
+ return ret;
+}
+
+/* BYPASS_OFF (0x8 111)*/
+int bypass_off(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+
+ if (pbpctl_dev->bp_caps & BP_CAP) {
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice)) {
+ write_data_int(pbpctl_dev, DIS_BYPASS_CAP_INT);
+ msec_delay_bp(BYPASS_DELAY_INT);
+ write_data_int(pbpctl_dev, PWROFF_BYPASS_ON_INT);
+ msec_delay_bp(BYPASS_DELAY_INT);
+ pbpctl_dev->bp_status_un = 0;
+ } else if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ write_data(pbpctl_dev, BYPASS_OFF);
+ if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER)
+ msec_delay_bp(LATCH_DELAY);
+ } else
+ data_pulse(pbpctl_dev, BYPASS_OFF);
+ ret = 0;
+ }
+ return ret;
+}
+
+/* TAP_OFF (0x9)*/
+int tap_off(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+ if ((pbpctl_dev->bp_caps & TAP_CAP)
+ && (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER)) {
+ write_data(pbpctl_dev, TAP_OFF);
+ msec_delay_bp(LATCH_DELAY);
+ ret = 0;
+ };
+ return ret;
+}
+
+/* TAP_ON (0xb)*/
+int tap_on(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+ if ((pbpctl_dev->bp_caps & TAP_CAP)
+ && (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER)) {
+ write_data(pbpctl_dev, TAP_ON);
+ msec_delay_bp(LATCH_DELAY);
+ ret = 0;
+ };
+ return ret;
+}
+
+/* DISC_OFF (0x9)*/
+int disc_off(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ if ((pbpctl_dev->bp_caps & DISC_CAP) && (pbpctl_dev->bp_ext_ver >= 0x8)) {
+ write_data(pbpctl_dev, DISC_OFF);
+ msec_delay_bp(LATCH_DELAY);
+ } else
+ ret = BP_NOT_CAP;
+ return ret;
+}
+
+/* DISC_ON (0xb)*/
+int disc_on(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ if ((pbpctl_dev->bp_caps & DISC_CAP) && (pbpctl_dev->bp_ext_ver >= 0x8)) {
+ write_data(pbpctl_dev, /*DISC_ON */ 0x85);
+ msec_delay_bp(LATCH_DELAY);
+ } else
+ ret = BP_NOT_CAP;
+ return ret;
+}
+
+/* DISC_PORT_ON */
+int disc_port_on(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ bpctl_dev_t *pbpctl_dev_m;
+
+ if ((is_bypass_fn(pbpctl_dev)) == 1)
+ pbpctl_dev_m = pbpctl_dev;
+ else
+ pbpctl_dev_m = get_master_port_fn(pbpctl_dev);
+ if (pbpctl_dev_m == NULL)
+ return BP_NOT_CAP;
+
+ if (pbpctl_dev_m->bp_caps_ex & DISC_PORT_CAP_EX) {
+ if (is_bypass_fn(pbpctl_dev) == 1) {
+
+ write_data(pbpctl_dev_m, TX_DISA);
+ } else {
+
+ write_data(pbpctl_dev_m, TX_DISB);
+ }
+
+ msec_delay_bp(LATCH_DELAY);
+
+ }
+ return ret;
+}
+
+/* DISC_PORT_OFF */
+int disc_port_off(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ bpctl_dev_t *pbpctl_dev_m;
+
+ if ((is_bypass_fn(pbpctl_dev)) == 1)
+ pbpctl_dev_m = pbpctl_dev;
+ else
+ pbpctl_dev_m = get_master_port_fn(pbpctl_dev);
+ if (pbpctl_dev_m == NULL)
+ return BP_NOT_CAP;
+
+ if (pbpctl_dev_m->bp_caps_ex & DISC_PORT_CAP_EX) {
+ if (is_bypass_fn(pbpctl_dev) == 1)
+ write_data(pbpctl_dev_m, TX_ENA);
+ else
+ write_data(pbpctl_dev_m, TX_ENB);
+
+ msec_delay_bp(LATCH_DELAY);
+
+ }
+ return ret;
+}
+
+/*TWO_PORT_LINK_HW_EN (0xe)*/
+int tpl_hw_on(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0, ctrl = 0;
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+
+ if (!(pbpctl_dev_b = get_status_port_fn(pbpctl_dev)))
+ return BP_NOT_CAP;
+
+ if (pbpctl_dev->bp_caps_ex & TPL2_CAP_EX) {
+ cmnd_on(pbpctl_dev);
+ write_data(pbpctl_dev, TPL2_ON);
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+ cmnd_off(pbpctl_dev);
+ return ret;
+ }
+
+ if (TPL_IF_SERIES(pbpctl_dev->subdevice)) {
+ ctrl = BPCTL_READ_REG(pbpctl_dev_b, CTRL);
+ BPCTL_BP_WRITE_REG(pbpctl_dev_b, CTRL,
+ ((ctrl | BPCTLI_CTRL_SWDPIO0) &
+ ~BPCTLI_CTRL_SWDPIN0));
+ } else
+ ret = BP_NOT_CAP;
+ return ret;
+}
+
+/*TWO_PORT_LINK_HW_DIS (0xc)*/
+int tpl_hw_off(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0, ctrl = 0;
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+
+ if (!(pbpctl_dev_b = get_status_port_fn(pbpctl_dev)))
+ return BP_NOT_CAP;
+ if (pbpctl_dev->bp_caps_ex & TPL2_CAP_EX) {
+ cmnd_on(pbpctl_dev);
+ write_data(pbpctl_dev, TPL2_OFF);
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+ cmnd_off(pbpctl_dev);
+ return ret;
+ }
+ if (TPL_IF_SERIES(pbpctl_dev->subdevice)) {
+ ctrl = BPCTL_READ_REG(pbpctl_dev_b, CTRL);
+ BPCTL_BP_WRITE_REG(pbpctl_dev_b, CTRL,
+ (ctrl | BPCTLI_CTRL_SWDPIO0 |
+ BPCTLI_CTRL_SWDPIN0));
+ } else
+ ret = BP_NOT_CAP;
+ return ret;
+}
+
+/* WDT_OFF (0x6 110)*/
+int wdt_off(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice)) {
+ bypass_off(pbpctl_dev);
+ } else if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER)
+ write_data(pbpctl_dev, WDT_OFF);
+ else
+ data_pulse(pbpctl_dev, WDT_OFF);
+ pbpctl_dev->wdt_status = WDT_STATUS_DIS;
+ ret = 0;
+ };
+ return ret;
+}
+
+/* WDT_ON (0x10)*/
+
+/***Global***/
+static unsigned int
+ wdt_val_array[] = { 1000, 1500, 2000, 3000, 4000, 8000, 16000, 32000, 0 };
+
+int wdt_on(bpctl_dev_t *pbpctl_dev, unsigned int timeout)
+{
+
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+ unsigned int pulse = 0, temp_value = 0, temp_cnt = 0;
+ pbpctl_dev->wdt_status = 0;
+
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice)) {
+ for (; wdt_val_array[temp_cnt]; temp_cnt++)
+ if (timeout <= wdt_val_array[temp_cnt])
+ break;
+
+ if (!wdt_val_array[temp_cnt])
+ temp_cnt--;
+
+ timeout = wdt_val_array[temp_cnt];
+ temp_cnt += 0x7;
+
+ write_data_int(pbpctl_dev, DIS_BYPASS_CAP_INT);
+ msec_delay_bp(BYPASS_DELAY_INT);
+ pbpctl_dev->bp_status_un = 0;
+ write_data_int(pbpctl_dev, temp_cnt);
+ pbpctl_dev->bypass_wdt_on_time = jiffies;
+ msec_delay_bp(CMND_INTERVAL_INT);
+ pbpctl_dev->bypass_timer_interval = timeout;
+ } else {
+ timeout =
+ (timeout <
+ TIMEOUT_UNIT ? TIMEOUT_UNIT : (timeout >
+ WDT_TIMEOUT_MAX ?
+ WDT_TIMEOUT_MAX :
+ timeout));
+ temp_value = timeout / 100;
+ while ((temp_value >>= 1))
+ temp_cnt++;
+ if (timeout > ((1 << temp_cnt) * 100))
+ temp_cnt++;
+ pbpctl_dev->bypass_wdt_on_time = jiffies;
+ pulse = (WDT_ON | temp_cnt);
+ if (pbpctl_dev->bp_ext_ver == OLD_IF_VER)
+ data_pulse(pbpctl_dev, pulse);
+ else
+ write_data(pbpctl_dev, pulse);
+ pbpctl_dev->bypass_timer_interval =
+ (1 << temp_cnt) * 100;
+ }
+ pbpctl_dev->wdt_status = WDT_STATUS_EN;
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+void bp75_put_hw_semaphore_generic(bpctl_dev_t *pbpctl_dev)
+{
+ u32 swsm;
+
+ swsm = BPCTL_READ_REG(pbpctl_dev, SWSM);
+
+ swsm &= ~(BPCTLI_SWSM_SMBI | BPCTLI_SWSM_SWESMBI);
+
+ BPCTL_WRITE_REG(pbpctl_dev, SWSM, swsm);
+}
+
+s32 bp75_get_hw_semaphore_generic(bpctl_dev_t *pbpctl_dev)
+{
+ u32 swsm;
+ s32 ret_val = 0;
+ s32 timeout = 8192 + 1;
+ s32 i = 0;
+
+ /* Get the SW semaphore */
+ while (i < timeout) {
+ swsm = BPCTL_READ_REG(pbpctl_dev, SWSM);
+ if (!(swsm & BPCTLI_SWSM_SMBI))
+ break;
+
+ usec_delay(50);
+ i++;
+ }
+
+ if (i == timeout) {
+ printk
+ ("bpctl_mod: Driver can't access device - SMBI bit is set.\n");
+ ret_val = -1;
+ goto out;
+ }
+
+ /* Get the FW semaphore. */
+ for (i = 0; i < timeout; i++) {
+ swsm = BPCTL_READ_REG(pbpctl_dev, SWSM);
+ BPCTL_WRITE_REG(pbpctl_dev, SWSM, swsm | BPCTLI_SWSM_SWESMBI);
+
+ /* Semaphore acquired if bit latched */
+ if (BPCTL_READ_REG(pbpctl_dev, SWSM) & BPCTLI_SWSM_SWESMBI)
+ break;
+
+ usec_delay(50);
+ }
+
+ if (i == timeout) {
+ /* Release semaphores */
+ bp75_put_hw_semaphore_generic(pbpctl_dev);
+ printk("bpctl_mod: Driver can't access the NVM\n");
+ ret_val = -1;
+ goto out;
+ }
+
+ out:
+ return ret_val;
+}
+
+static void bp75_release_phy(bpctl_dev_t *pbpctl_dev)
+{
+ u16 mask = BPCTLI_SWFW_PHY0_SM;
+ u32 swfw_sync;
+
+ if ((pbpctl_dev->func == 1) || (pbpctl_dev->func == 3))
+ mask = BPCTLI_SWFW_PHY1_SM;
+
+ while (bp75_get_hw_semaphore_generic(pbpctl_dev) != 0) ;
+ /* Empty */
+
+ swfw_sync = BPCTL_READ_REG(pbpctl_dev, SW_FW_SYNC);
+ swfw_sync &= ~mask;
+ BPCTL_WRITE_REG(pbpctl_dev, SW_FW_SYNC, swfw_sync);
+
+ bp75_put_hw_semaphore_generic(pbpctl_dev);
+}
+
+static s32 bp75_acquire_phy(bpctl_dev_t *pbpctl_dev)
+{
+ u16 mask = BPCTLI_SWFW_PHY0_SM;
+ u32 swfw_sync;
+ u32 swmask;
+ u32 fwmask;
+ s32 ret_val = 0;
+ s32 i = 0, timeout = 200;
+
+ if ((pbpctl_dev->func == 1) || (pbpctl_dev->func == 3))
+ mask = BPCTLI_SWFW_PHY1_SM;
+
+ swmask = mask;
+ fwmask = mask << 16;
+
+ while (i < timeout) {
+ if (bp75_get_hw_semaphore_generic(pbpctl_dev)) {
+ ret_val = -1;
+ goto out;
+ }
+
+ swfw_sync = BPCTL_READ_REG(pbpctl_dev, SW_FW_SYNC);
+ if (!(swfw_sync & (fwmask | swmask)))
+ break;
+
+ bp75_put_hw_semaphore_generic(pbpctl_dev);
+ mdelay(5);
+ i++;
+ }
+
+ if (i == timeout) {
+ printk
+ ("bpctl_mod: Driver can't access resource, SW_FW_SYNC timeout.\n");
+ ret_val = -1;
+ goto out;
+ }
+
+ swfw_sync |= swmask;
+ BPCTL_WRITE_REG(pbpctl_dev, SW_FW_SYNC, swfw_sync);
+
+ bp75_put_hw_semaphore_generic(pbpctl_dev);
+
+ out:
+ return ret_val;
+}
+
+s32 bp75_read_phy_reg_mdic(bpctl_dev_t *pbpctl_dev, u32 offset, u16 *data)
+{
+ u32 i, mdic = 0;
+ s32 ret_val = 0;
+ u32 phy_addr = 1;
+
+ mdic = ((offset << BPCTLI_MDIC_REG_SHIFT) |
+ (phy_addr << BPCTLI_MDIC_PHY_SHIFT) | (BPCTLI_MDIC_OP_READ));
+
+ BPCTL_WRITE_REG(pbpctl_dev, MDIC, mdic);
+
+ for (i = 0; i < (BPCTLI_GEN_POLL_TIMEOUT * 3); i++) {
+ usec_delay(50);
+ mdic = BPCTL_READ_REG(pbpctl_dev, MDIC);
+ if (mdic & BPCTLI_MDIC_READY)
+ break;
+ }
+ if (!(mdic & BPCTLI_MDIC_READY)) {
+ printk("bpctl_mod: MDI Read did not complete\n");
+ ret_val = -1;
+ goto out;
+ }
+ if (mdic & BPCTLI_MDIC_ERROR) {
+ printk("bpctl_mod: MDI Error\n");
+ ret_val = -1;
+ goto out;
+ }
+ *data = (u16) mdic;
+
+ out:
+ return ret_val;
+}
+
+s32 bp75_write_phy_reg_mdic(bpctl_dev_t *pbpctl_dev, u32 offset, u16 data)
+{
+ u32 i, mdic = 0;
+ s32 ret_val = 0;
+ u32 phy_addr = 1;
+
+ mdic = (((u32) data) |
+ (offset << BPCTLI_MDIC_REG_SHIFT) |
+ (phy_addr << BPCTLI_MDIC_PHY_SHIFT) | (BPCTLI_MDIC_OP_WRITE));
+
+ BPCTL_WRITE_REG(pbpctl_dev, MDIC, mdic);
+
+ for (i = 0; i < (BPCTLI_GEN_POLL_TIMEOUT * 3); i++) {
+ usec_delay(50);
+ mdic = BPCTL_READ_REG(pbpctl_dev, MDIC);
+ if (mdic & BPCTLI_MDIC_READY)
+ break;
+ }
+ if (!(mdic & BPCTLI_MDIC_READY)) {
+ printk("bpctl_mod: MDI Write did not complete\n");
+ ret_val = -1;
+ goto out;
+ }
+ if (mdic & BPCTLI_MDIC_ERROR) {
+ printk("bpctl_mod: MDI Error\n");
+ ret_val = -1;
+ goto out;
+ }
+
+ out:
+ return ret_val;
+}
+
+static s32 bp75_read_phy_reg(bpctl_dev_t *pbpctl_dev, u32 offset, u16 *data)
+{
+ s32 ret_val = 0;
+
+ ret_val = bp75_acquire_phy(pbpctl_dev);
+ if (ret_val)
+ goto out;
+
+ if (offset > BPCTLI_MAX_PHY_MULTI_PAGE_REG) {
+ ret_val = bp75_write_phy_reg_mdic(pbpctl_dev,
+ BPCTLI_IGP01E1000_PHY_PAGE_SELECT,
+ (u16) offset);
+ if (ret_val)
+ goto release;
+ }
+
+ ret_val =
+ bp75_read_phy_reg_mdic(pbpctl_dev,
+ BPCTLI_MAX_PHY_REG_ADDRESS & offset, data);
+
+ release:
+ bp75_release_phy(pbpctl_dev);
+ out:
+ return ret_val;
+}
+
+static s32 bp75_write_phy_reg(bpctl_dev_t *pbpctl_dev, u32 offset, u16 data)
+{
+ s32 ret_val = 0;
+
+ ret_val = bp75_acquire_phy(pbpctl_dev);
+ if (ret_val)
+ goto out;
+
+ if (offset > BPCTLI_MAX_PHY_MULTI_PAGE_REG) {
+ ret_val = bp75_write_phy_reg_mdic(pbpctl_dev,
+ BPCTLI_IGP01E1000_PHY_PAGE_SELECT,
+ (u16) offset);
+ if (ret_val)
+ goto release;
+ }
+
+ ret_val =
+ bp75_write_phy_reg_mdic(pbpctl_dev,
+ BPCTLI_MAX_PHY_REG_ADDRESS & offset, data);
+
+ release:
+ bp75_release_phy(pbpctl_dev);
+
+ out:
+ return ret_val;
+}
+
+/* SET_TX (non-Bypass command :)) */
+static int set_tx(bpctl_dev_t *pbpctl_dev, int tx_state)
+{
+ int ret = 0, ctrl = 0;
+ bpctl_dev_t *pbpctl_dev_m;
+ if ((is_bypass_fn(pbpctl_dev)) == 1)
+ pbpctl_dev_m = pbpctl_dev;
+ else
+ pbpctl_dev_m = get_master_port_fn(pbpctl_dev);
+ if (pbpctl_dev_m == NULL)
+ return BP_NOT_CAP;
+ if (pbpctl_dev_m->bp_caps_ex & DISC_PORT_CAP_EX) {
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL);
+ if (!tx_state) {
+ if (pbpctl_dev->bp_540) {
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ (ctrl | BP10G_SDP1_DIR |
+ BP10G_SDP1_DATA));
+
+ } else {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ (ctrl | BPCTLI_CTRL_SDP1_DIR
+ | BPCTLI_CTRL_SWDPIN1));
+ }
+ } else {
+ if (pbpctl_dev->bp_540) {
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ ((ctrl | BP10G_SDP1_DIR) &
+ ~BP10G_SDP1_DATA));
+ } else {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ ((ctrl |
+ BPCTLI_CTRL_SDP1_DIR) &
+ ~BPCTLI_CTRL_SWDPIN1));
+ }
+ return ret;
+
+ }
+ } else if (pbpctl_dev->bp_caps & TX_CTL_CAP) {
+ if (PEG5_IF_SERIES(pbpctl_dev->subdevice)) {
+ if (tx_state) {
+ uint16_t mii_reg;
+ if (!
+ (ret =
+ bp75_read_phy_reg(pbpctl_dev,
+ BPCTLI_PHY_CONTROL,
+ &mii_reg))) {
+ if (mii_reg & BPCTLI_MII_CR_POWER_DOWN) {
+ ret =
+ bp75_write_phy_reg
+ (pbpctl_dev,
+ BPCTLI_PHY_CONTROL,
+ mii_reg &
+ ~BPCTLI_MII_CR_POWER_DOWN);
+ }
+ }
+ } else {
+ uint16_t mii_reg;
+ if (!
+ (ret =
+ bp75_read_phy_reg(pbpctl_dev,
+ BPCTLI_PHY_CONTROL,
+ &mii_reg))) {
+
+ mii_reg |= BPCTLI_MII_CR_POWER_DOWN;
+ ret =
+ bp75_write_phy_reg(pbpctl_dev,
+ BPCTLI_PHY_CONTROL,
+ mii_reg);
+ }
+ }
+
+ }
+ if (pbpctl_dev->bp_fiber5) {
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+
+ } else if (pbpctl_dev->bp_10gb)
+ ctrl = BP10GB_READ_REG(pbpctl_dev, MISC_REG_GPIO);
+
+ else if (!pbpctl_dev->bp_10g)
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL);
+ else
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+
+ if (!tx_state)
+ if (pbpctl_dev->bp_10g9) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ (ctrl | BP10G_SDP3_DATA |
+ BP10G_SDP3_DIR));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ (ctrl |
+ BPCTLI_CTRL_EXT_SDP6_DIR |
+ BPCTLI_CTRL_EXT_SDP6_DATA));
+
+ } else if (pbpctl_dev->bp_10gb) {
+ if ((pbpctl_dev->func == 1)
+ || (pbpctl_dev->func == 3))
+ BP10GB_WRITE_REG(pbpctl_dev,
+ MISC_REG_GPIO,
+ (ctrl |
+ BP10GB_GPIO0_SET_P1) &
+ ~(BP10GB_GPIO0_CLR_P1 |
+ BP10GB_GPIO0_OE_P1));
+ else
+ BP10GB_WRITE_REG(pbpctl_dev,
+ MISC_REG_GPIO,
+ (ctrl |
+ BP10GB_GPIO0_OE_P0 |
+ BP10GB_GPIO0_SET_P0));
+
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ (ctrl | BPCTLI_CTRL_SDP1_DIR
+ | BPCTLI_CTRL_SWDPIN1));
+
+ } else if (pbpctl_dev->bp_540) {
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ (ctrl | BP10G_SDP1_DIR |
+ BP10G_SDP1_DATA));
+
+ }
+
+ else if (!pbpctl_dev->bp_10g)
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ (ctrl | BPCTLI_CTRL_SWDPIO0 |
+ BPCTLI_CTRL_SWDPIN0));
+
+ else
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ (ctrl | BP10G_SDP0_DATA |
+ BP10G_SDP0_DIR));
+
+ else {
+ if (pbpctl_dev->bp_10g9) {
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ ((ctrl | BP10G_SDP3_DIR) &
+ ~BP10G_SDP3_DATA));
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL_EXT,
+ ((ctrl |
+ BPCTLI_CTRL_EXT_SDP6_DIR) &
+ ~BPCTLI_CTRL_EXT_SDP6_DATA));
+
+ } else if (pbpctl_dev->bp_10gb) {
+ if ((bpctl_dev_arr->func == 1)
+ || (bpctl_dev_arr->func == 3))
+ BP10GB_WRITE_REG(pbpctl_dev,
+ MISC_REG_GPIO,
+ (ctrl |
+ BP10GB_GPIO0_CLR_P1) &
+ ~(BP10GB_GPIO0_SET_P1 |
+ BP10GB_GPIO0_OE_P1));
+ else
+ BP10GB_WRITE_REG(pbpctl_dev,
+ MISC_REG_GPIO,
+ (ctrl |
+ BP10GB_GPIO0_OE_P0 |
+ BP10GB_GPIO0_CLR_P0));
+
+ } else if (pbpctl_dev->bp_i80) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ ((ctrl |
+ BPCTLI_CTRL_SDP1_DIR) &
+ ~BPCTLI_CTRL_SWDPIN1));
+ } else if (pbpctl_dev->bp_540) {
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ ((ctrl | BP10G_SDP1_DIR) &
+ ~BP10G_SDP1_DATA));
+ }
+
+ else if (!pbpctl_dev->bp_10g) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ ((ctrl | BPCTLI_CTRL_SWDPIO0)
+ & ~BPCTLI_CTRL_SWDPIN0));
+ if (!PEGF_IF_SERIES(pbpctl_dev->subdevice)) {
+ BPCTL_BP_WRITE_REG(pbpctl_dev, CTRL,
+ (ctrl &
+ ~
+ (BPCTLI_CTRL_SDP0_DATA
+ |
+ BPCTLI_CTRL_SDP0_DIR)));
+ }
+ } else
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ ((ctrl | BP10G_SDP0_DIR) &
+ ~BP10G_SDP0_DATA));
+
+ }
+
+ } else
+ ret = BP_NOT_CAP;
+ return ret;
+
+}
+
+/* SET_FORCE_LINK (non-Bypass command :)) */
+static int set_bp_force_link(bpctl_dev_t *pbpctl_dev, int tx_state)
+{
+ int ret = 0, ctrl = 0;
+
+ if (DBI_IF_SERIES(pbpctl_dev->subdevice)) {
+
+ if ((pbpctl_dev->bp_10g) || (pbpctl_dev->bp_10g9)) {
+
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL);
+ if (!tx_state)
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ ctrl & ~BP10G_SDP1_DIR);
+ else
+ BP10G_WRITE_REG(pbpctl_dev, ESDP,
+ ((ctrl | BP10G_SDP1_DIR) &
+ ~BP10G_SDP1_DATA));
+ return ret;
+ }
+
+ }
+ return BP_NOT_CAP;
+}
+
+/*RESET_CONT 0x20 */
+int reset_cont(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice))
+ return BP_NOT_CAP;
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER)
+ write_data(pbpctl_dev, RESET_CONT);
+ else
+ data_pulse(pbpctl_dev, RESET_CONT);
+ ret = 0;
+ };
+ return ret;
+}
+
+/*DIS_BYPASS_CAP 0x22 */
+int dis_bypass_cap(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & BP_DIS_CAP) {
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice)) {
+ write_data_int(pbpctl_dev, DIS_BYPASS_CAP_INT);
+ msec_delay_bp(BYPASS_DELAY_INT);
+ } else {
+ write_data(pbpctl_dev, BYPASS_OFF);
+ msec_delay_bp(LATCH_DELAY);
+ write_data(pbpctl_dev, DIS_BYPASS_CAP);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ }
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+/*EN_BYPASS_CAP 0x24 */
+int en_bypass_cap(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & BP_DIS_CAP) {
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice)) {
+ write_data_int(pbpctl_dev, PWROFF_BYPASS_ON_INT);
+ msec_delay_bp(BYPASS_DELAY_INT);
+ } else {
+ write_data(pbpctl_dev, EN_BYPASS_CAP);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ }
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+/* BYPASS_STATE_PWRON 0x26*/
+int bypass_state_pwron(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & BP_PWUP_CTL_CAP) {
+ write_data(pbpctl_dev, BYPASS_STATE_PWRON);
+ if (pbpctl_dev->bp_ext_ver == PXG2BPI_VER)
+ msec_delay_bp(DFLT_PWRON_DELAY);
+ else
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+/* NORMAL_STATE_PWRON 0x28*/
+int normal_state_pwron(bpctl_dev_t *pbpctl_dev)
+{
+ if ((pbpctl_dev->bp_caps & BP_PWUP_CTL_CAP)
+ || (pbpctl_dev->bp_caps & TAP_PWUP_CTL_CAP)) {
+ write_data(pbpctl_dev, NORMAL_STATE_PWRON);
+ if (pbpctl_dev->bp_ext_ver == PXG2BPI_VER)
+ msec_delay_bp(DFLT_PWRON_DELAY);
+ else
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+/* BYPASS_STATE_PWROFF 0x27*/
+int bypass_state_pwroff(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & BP_PWOFF_CTL_CAP) {
+ write_data(pbpctl_dev, BYPASS_STATE_PWROFF);
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+/* NORMAL_STATE_PWROFF 0x29*/
+int normal_state_pwroff(bpctl_dev_t *pbpctl_dev)
+{
+ if ((pbpctl_dev->bp_caps & BP_PWOFF_CTL_CAP)) {
+ write_data(pbpctl_dev, NORMAL_STATE_PWROFF);
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+/*TAP_STATE_PWRON 0x2a*/
+int tap_state_pwron(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & TAP_PWUP_CTL_CAP) {
+ write_data(pbpctl_dev, TAP_STATE_PWRON);
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+/*DIS_TAP_CAP 0x2c*/
+int dis_tap_cap(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & TAP_DIS_CAP) {
+ write_data(pbpctl_dev, DIS_TAP_CAP);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+/*EN_TAP_CAP 0x2e*/
+int en_tap_cap(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & TAP_DIS_CAP) {
+ write_data(pbpctl_dev, EN_TAP_CAP);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+/*DISC_STATE_PWRON 0x2a*/
+int disc_state_pwron(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & DISC_PWUP_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= 0x8) {
+ write_data(pbpctl_dev, DISC_STATE_PWRON);
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+ return BP_OK;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+/*DIS_DISC_CAP 0x2c*/
+int dis_disc_cap(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & DISC_DIS_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= 0x8) {
+ write_data(pbpctl_dev, DIS_DISC_CAP);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ return BP_OK;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+/*DISC_STATE_PWRON 0x2a*/
+int disc_port_state_pwron(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ bpctl_dev_t *pbpctl_dev_m;
+
+ return BP_NOT_CAP;
+
+ if ((is_bypass_fn(pbpctl_dev)) == 1)
+ pbpctl_dev_m = pbpctl_dev;
+ else
+ pbpctl_dev_m = get_master_port_fn(pbpctl_dev);
+ if (pbpctl_dev_m == NULL)
+ return BP_NOT_CAP;
+
+ if (pbpctl_dev_m->bp_caps_ex & DISC_PORT_CAP_EX) {
+ if (is_bypass_fn(pbpctl_dev) == 1)
+ write_data(pbpctl_dev_m, TX_DISA_PWRUP);
+ else
+ write_data(pbpctl_dev_m, TX_DISB_PWRUP);
+
+ msec_delay_bp(LATCH_DELAY);
+
+ }
+ return ret;
+}
+
+int normal_port_state_pwron(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ bpctl_dev_t *pbpctl_dev_m;
+ return BP_NOT_CAP;
+
+ if ((is_bypass_fn(pbpctl_dev)) == 1)
+ pbpctl_dev_m = pbpctl_dev;
+ else
+ pbpctl_dev_m = get_master_port_fn(pbpctl_dev);
+ if (pbpctl_dev_m == NULL)
+ return BP_NOT_CAP;
+
+ if (pbpctl_dev_m->bp_caps_ex & DISC_PORT_CAP_EX) {
+ if (is_bypass_fn(pbpctl_dev) == 1)
+ write_data(pbpctl_dev_m, TX_ENA_PWRUP);
+ else
+ write_data(pbpctl_dev_m, TX_ENB_PWRUP);
+
+ msec_delay_bp(LATCH_DELAY);
+
+ }
+ return ret;
+}
+
+/*EN_TAP_CAP 0x2e*/
+int en_disc_cap(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & DISC_DIS_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= 0x8) {
+ write_data(pbpctl_dev, EN_DISC_CAP);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ return BP_OK;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int std_nic_on(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & STD_NIC_CAP) {
+
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice)) {
+ write_data_int(pbpctl_dev, DIS_BYPASS_CAP_INT);
+ msec_delay_bp(BYPASS_DELAY_INT);
+ pbpctl_dev->bp_status_un = 0;
+ return BP_OK;
+ }
+
+ if (pbpctl_dev->bp_ext_ver >= 0x8) {
+ write_data(pbpctl_dev, STD_NIC_ON);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ return BP_OK;
+
+ }
+
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ wdt_off(pbpctl_dev);
+
+ if (pbpctl_dev->bp_caps & BP_CAP) {
+ write_data(pbpctl_dev, BYPASS_OFF);
+ msec_delay_bp(LATCH_DELAY);
+ }
+
+ if (pbpctl_dev->bp_caps & TAP_CAP) {
+ write_data(pbpctl_dev, TAP_OFF);
+ msec_delay_bp(LATCH_DELAY);
+ }
+
+ write_data(pbpctl_dev, NORMAL_STATE_PWRON);
+ if (pbpctl_dev->bp_ext_ver == PXG2BPI_VER)
+ msec_delay_bp(DFLT_PWRON_DELAY);
+ else
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+
+ if (pbpctl_dev->bp_caps & BP_DIS_CAP) {
+ write_data(pbpctl_dev, DIS_BYPASS_CAP);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ }
+
+ if (pbpctl_dev->bp_caps & TAP_DIS_CAP) {
+ write_data(pbpctl_dev, DIS_TAP_CAP);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+
+ }
+ return 0;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int std_nic_off(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & STD_NIC_CAP) {
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice)) {
+ write_data_int(pbpctl_dev, PWROFF_BYPASS_ON_INT);
+ msec_delay_bp(BYPASS_DELAY_INT);
+ return BP_OK;
+ }
+ if (pbpctl_dev->bp_ext_ver >= 0x8) {
+ write_data(pbpctl_dev, STD_NIC_OFF);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ return BP_OK;
+
+ }
+
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+
+ if (pbpctl_dev->bp_caps & TAP_PWUP_CTL_CAP) {
+ write_data(pbpctl_dev, TAP_STATE_PWRON);
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+ }
+
+ if (pbpctl_dev->bp_caps & BP_PWUP_CTL_CAP) {
+ write_data(pbpctl_dev, BYPASS_STATE_PWRON);
+ if (pbpctl_dev->bp_ext_ver > PXG2BPI_VER)
+ msec_delay_bp(LATCH_DELAY +
+ EEPROM_WR_DELAY);
+ else
+ msec_delay_bp(DFLT_PWRON_DELAY);
+ }
+
+ if (pbpctl_dev->bp_caps & TAP_DIS_CAP) {
+ write_data(pbpctl_dev, EN_TAP_CAP);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ }
+ if (pbpctl_dev->bp_caps & DISC_DIS_CAP) {
+ write_data(pbpctl_dev, EN_DISC_CAP);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ }
+
+ if (pbpctl_dev->bp_caps & BP_DIS_CAP) {
+ write_data(pbpctl_dev, EN_BYPASS_CAP);
+ msec_delay_bp(BYPASS_CAP_DELAY);
+ }
+
+ return 0;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int wdt_time_left(bpctl_dev_t *pbpctl_dev)
+{
+
+ /* unsigned long curr_time=((long long)(jiffies*1000))/HZ, delta_time=0,wdt_on_time=((long long)(pbpctl_dev->bypass_wdt_on_time*1000))/HZ; */
+ unsigned long curr_time = jiffies, delta_time = 0, wdt_on_time =
+ pbpctl_dev->bypass_wdt_on_time, delta_time_msec = 0;
+ int time_left = 0;
+
+ switch (pbpctl_dev->wdt_status) {
+ case WDT_STATUS_DIS:
+ time_left = 0;
+ break;
+ case WDT_STATUS_EN:
+ delta_time =
+ (curr_time >=
+ wdt_on_time) ? (curr_time - wdt_on_time) : (~wdt_on_time +
+ curr_time);
+ delta_time_msec = jiffies_to_msecs(delta_time);
+ time_left = pbpctl_dev->bypass_timer_interval - delta_time_msec;
+ if (time_left < 0) {
+ time_left = -1;
+ pbpctl_dev->wdt_status = WDT_STATUS_EXP;
+ }
+ break;
+ case WDT_STATUS_EXP:
+ time_left = -1;
+ break;
+ }
+
+ return time_left;
+}
+
+static int wdt_timer(bpctl_dev_t *pbpctl_dev, int *time_left)
+{
+ int ret = 0;
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+ {
+ if (pbpctl_dev->wdt_status == WDT_STATUS_UNKNOWN)
+ ret = BP_NOT_CAP;
+ else
+ *time_left = wdt_time_left(pbpctl_dev);
+ }
+
+ } else
+ ret = BP_NOT_CAP;
+ return ret;
+}
+
+static int wdt_timer_reload(bpctl_dev_t *pbpctl_dev)
+{
+
+ int ret = 0;
+
+ if ((pbpctl_dev->bp_caps & WD_CTL_CAP) &&
+ (pbpctl_dev->wdt_status != WDT_STATUS_UNKNOWN)) {
+ if (pbpctl_dev->wdt_status == WDT_STATUS_DIS)
+ return 0;
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER)
+ ret = wdt_pulse(pbpctl_dev);
+ else if (INTEL_IF_SERIES(pbpctl_dev->subdevice))
+ ret = wdt_pulse_int(pbpctl_dev);
+ else
+ ret = send_wdt_pulse(pbpctl_dev);
+ /* if (ret==-1)
+ mod_timer(&pbpctl_dev->bp_timer, jiffies+1);*/
+ return 1;
+ }
+ return BP_NOT_CAP;
+}
+
+static void wd_reset_timer(unsigned long param)
+{
+ bpctl_dev_t *pbpctl_dev = (bpctl_dev_t *) param;
+#ifdef BP_SELF_TEST
+ struct sk_buff *skb_tmp;
+#endif
+
+ if ((pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) &&
+ ((atomic_read(&pbpctl_dev->wdt_busy)) == 1)) {
+ mod_timer(&pbpctl_dev->bp_timer, jiffies + 1);
+ return;
+ }
+#ifdef BP_SELF_TEST
+
+ if (pbpctl_dev->bp_self_test_flag == 1) {
+ skb_tmp = dev_alloc_skb(BPTEST_DATA_LEN + 2);
+ if ((skb_tmp) && (pbpctl_dev->ndev) && (pbpctl_dev->bp_tx_data)) {
+ memcpy(skb_put(skb_tmp, BPTEST_DATA_LEN),
+ pbpctl_dev->bp_tx_data, BPTEST_DATA_LEN);
+ skb_tmp->dev = pbpctl_dev->ndev;
+ skb_tmp->protocol =
+ eth_type_trans(skb_tmp, pbpctl_dev->ndev);
+ skb_tmp->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_receive_skb(skb_tmp);
+ goto bp_timer_reload;
+ return;
+ }
+ }
+#endif
+
+ wdt_timer_reload(pbpctl_dev);
+#ifdef BP_SELF_TEST
+ bp_timer_reload:
+#endif
+ if (pbpctl_dev->reset_time) {
+ mod_timer(&pbpctl_dev->bp_timer,
+ jiffies + (HZ * pbpctl_dev->reset_time) / 1000);
+ }
+}
+
+/*WAIT_AT_PWRUP 0x80 */
+int bp_wait_at_pwup_en(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= BP_FW_EXT_VER8) {
+ write_data(pbpctl_dev, BP_WAIT_AT_PWUP_EN);
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+
+ return BP_OK;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+/*DIS_WAIT_AT_PWRUP 0x81 */
+int bp_wait_at_pwup_dis(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+
+ if (pbpctl_dev->bp_ext_ver >= BP_FW_EXT_VER8) {
+ write_data(pbpctl_dev, BP_WAIT_AT_PWUP_DIS);
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+
+ return BP_OK;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+/*EN_HW_RESET 0x82 */
+
+int bp_hw_reset_en(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= BP_FW_EXT_VER8) {
+ write_data(pbpctl_dev, BP_HW_RESET_EN);
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+
+ return BP_OK;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+/*DIS_HW_RESET 0x83 */
+
+int bp_hw_reset_dis(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= BP_FW_EXT_VER8) {
+ write_data(pbpctl_dev, BP_HW_RESET_DIS);
+ msec_delay_bp(LATCH_DELAY + EEPROM_WR_DELAY);
+
+ return BP_OK;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+
+int wdt_exp_mode(bpctl_dev_t *pbpctl_dev, int mode)
+{
+ uint32_t status_reg = 0, status_reg1 = 0;
+
+ if ((pbpctl_dev->bp_caps & (TAP_STATUS_CAP | DISC_CAP)) &&
+ (pbpctl_dev->bp_caps & BP_CAP)) {
+ if (pbpctl_dev->bp_ext_ver >= PXE2TBPI_VER) {
+
+ if ((pbpctl_dev->bp_ext_ver >= 0x8) &&
+ (mode == 2) && (pbpctl_dev->bp_caps & DISC_CAP)) {
+ status_reg1 =
+ read_reg(pbpctl_dev, STATUS_DISC_REG_ADDR);
+ if (!(status_reg1 & WDTE_DISC_BPN_MASK))
+ write_reg(pbpctl_dev,
+ status_reg1 |
+ WDTE_DISC_BPN_MASK,
+ STATUS_DISC_REG_ADDR);
+ return BP_OK;
+ }
+ }
+ status_reg = read_reg(pbpctl_dev, STATUS_TAP_REG_ADDR);
+
+ if ((mode == 0) && (pbpctl_dev->bp_caps & BP_CAP)) {
+ if (pbpctl_dev->bp_ext_ver >= 0x8) {
+ status_reg1 =
+ read_reg(pbpctl_dev, STATUS_DISC_REG_ADDR);
+ if (status_reg1 & WDTE_DISC_BPN_MASK)
+ write_reg(pbpctl_dev,
+ status_reg1 &
+ ~WDTE_DISC_BPN_MASK,
+ STATUS_DISC_REG_ADDR);
+ }
+ if (status_reg & WDTE_TAP_BPN_MASK)
+ write_reg(pbpctl_dev,
+ status_reg & ~WDTE_TAP_BPN_MASK,
+ STATUS_TAP_REG_ADDR);
+ return BP_OK;
+
+ } else if ((mode == 1) && (pbpctl_dev->bp_caps & TAP_CAP)) {
+ if (!(status_reg & WDTE_TAP_BPN_MASK))
+ write_reg(pbpctl_dev,
+ status_reg | WDTE_TAP_BPN_MASK,
+ STATUS_TAP_REG_ADDR);
+ /*else return BP_NOT_CAP; */
+ return BP_OK;
+ }
+
+ }
+ return BP_NOT_CAP;
+}
+
+int bypass_fw_ver(bpctl_dev_t *pbpctl_dev)
+{
+ if (is_bypass_fn(pbpctl_dev))
+ return read_reg(pbpctl_dev, VER_REG_ADDR);
+ else
+ return BP_NOT_CAP;
+}
+
+int bypass_sign_check(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (is_bypass_fn(pbpctl_dev))
+ return (((read_reg(pbpctl_dev, PIC_SIGN_REG_ADDR)) ==
+ PIC_SIGN_VALUE) ? 1 : 0);
+ else
+ return BP_NOT_CAP;
+}
+
+static int tx_status(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t ctrl = 0;
+ bpctl_dev_t *pbpctl_dev_m;
+ if ((is_bypass_fn(pbpctl_dev)) == 1)
+ pbpctl_dev_m = pbpctl_dev;
+ else
+ pbpctl_dev_m = get_master_port_fn(pbpctl_dev);
+ if (pbpctl_dev_m == NULL)
+ return BP_NOT_CAP;
+ if (pbpctl_dev_m->bp_caps_ex & DISC_PORT_CAP_EX) {
+
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL);
+ if (pbpctl_dev->bp_i80)
+ return ((ctrl & BPCTLI_CTRL_SWDPIN1) != 0 ? 0 : 1);
+ if (pbpctl_dev->bp_540) {
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+
+ return ((ctrl & BP10G_SDP1_DATA) != 0 ? 0 : 1);
+ }
+
+ }
+
+ if (pbpctl_dev->bp_caps & TX_CTL_CAP) {
+ if (PEG5_IF_SERIES(pbpctl_dev->subdevice)) {
+ uint16_t mii_reg;
+ if (!
+ (bp75_read_phy_reg
+ (pbpctl_dev, BPCTLI_PHY_CONTROL, &mii_reg))) {
+ if (mii_reg & BPCTLI_MII_CR_POWER_DOWN)
+ return 0;
+
+ else
+ return 1;
+ }
+ return -1;
+ }
+
+ if (pbpctl_dev->bp_10g9) {
+ return ((BP10G_READ_REG(pbpctl_dev, ESDP) &
+ BP10G_SDP3_DATA) != 0 ? 0 : 1);
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ if (ctrl & BPCTLI_CTRL_EXT_SDP6_DATA)
+ return 0;
+ return 1;
+ } else if (pbpctl_dev->bp_10gb) {
+ ctrl = BP10GB_READ_REG(pbpctl_dev, MISC_REG_GPIO);
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_GPIO,
+ (ctrl | BP10GB_GPIO0_OE_P1) &
+ ~(BP10GB_GPIO0_SET_P1 |
+ BP10GB_GPIO0_CLR_P1));
+
+ if ((pbpctl_dev->func == 1) || (pbpctl_dev->func == 3))
+ return (((BP10GB_READ_REG
+ (pbpctl_dev,
+ MISC_REG_GPIO)) & BP10GB_GPIO0_P1) !=
+ 0 ? 0 : 1);
+ else
+ return (((BP10GB_READ_REG
+ (pbpctl_dev,
+ MISC_REG_GPIO)) & BP10GB_GPIO0_P0) !=
+ 0 ? 0 : 1);
+ }
+
+ if (!pbpctl_dev->bp_10g) {
+
+ ctrl = BPCTL_READ_REG(pbpctl_dev, CTRL);
+ if (pbpctl_dev->bp_i80)
+ return ((ctrl & BPCTLI_CTRL_SWDPIN1) !=
+ 0 ? 0 : 1);
+ if (pbpctl_dev->bp_540) {
+ ctrl = BP10G_READ_REG(pbpctl_dev, ESDP);
+
+ return ((ctrl & BP10G_SDP1_DATA) != 0 ? 0 : 1);
+ }
+
+ return ((ctrl & BPCTLI_CTRL_SWDPIN0) != 0 ? 0 : 1);
+ } else
+ return ((BP10G_READ_REG(pbpctl_dev, ESDP) &
+ BP10G_SDP0_DATA) != 0 ? 0 : 1);
+
+ }
+ return BP_NOT_CAP;
+}
+
+static int bp_force_link_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (DBI_IF_SERIES(pbpctl_dev->subdevice)) {
+
+ if ((pbpctl_dev->bp_10g) || (pbpctl_dev->bp_10g9)) {
+ return ((BP10G_READ_REG(pbpctl_dev, ESDP) &
+ BP10G_SDP1_DIR) != 0 ? 1 : 0);
+
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int bypass_from_last_read(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t ctrl_ext = 0;
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+
+ if ((pbpctl_dev->bp_caps & SW_CTL_CAP)
+ && (pbpctl_dev_b = get_status_port_fn(pbpctl_dev))) {
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev_b, CTRL_EXT);
+ BPCTL_BP_WRITE_REG(pbpctl_dev_b, CTRL_EXT,
+ (ctrl_ext & ~BPCTLI_CTRL_EXT_SDP7_DIR));
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev_b, CTRL_EXT);
+ if (ctrl_ext & BPCTLI_CTRL_EXT_SDP7_DATA)
+ return 0;
+ return 1;
+ } else
+ return BP_NOT_CAP;
+}
+
+int bypass_status_clear(bpctl_dev_t *pbpctl_dev)
+{
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+
+ if ((pbpctl_dev->bp_caps & SW_CTL_CAP)
+ && (pbpctl_dev_b = get_status_port_fn(pbpctl_dev))) {
+
+ send_bypass_clear_pulse(pbpctl_dev_b, 1);
+ return 0;
+ } else
+ return BP_NOT_CAP;
+}
+
+int bypass_flag_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ if ((pbpctl_dev->bp_caps & BP_CAP)) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ return ((((read_reg(pbpctl_dev, STATUS_REG_ADDR)) &
+ BYPASS_FLAG_MASK) ==
+ BYPASS_FLAG_MASK) ? 1 : 0);
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int bypass_flag_status_clear(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & BP_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ uint32_t status_reg = 0;
+ status_reg = read_reg(pbpctl_dev, STATUS_REG_ADDR);
+ write_reg(pbpctl_dev, status_reg & ~BYPASS_FLAG_MASK,
+ STATUS_REG_ADDR);
+ return 0;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int bypass_change_status(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+
+ if (pbpctl_dev->bp_caps & BP_STATUS_CHANGE_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= 0x8) {
+ ret = bypass_flag_status(pbpctl_dev);
+ bypass_flag_status_clear(pbpctl_dev);
+ } else if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ ret = bypass_flag_status(pbpctl_dev);
+ bypass_flag_status_clear(pbpctl_dev);
+ } else {
+ ret = bypass_from_last_read(pbpctl_dev);
+ bypass_status_clear(pbpctl_dev);
+ }
+ }
+ return ret;
+}
+
+int bypass_off_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & BP_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ return ((((read_reg(pbpctl_dev, STATUS_REG_ADDR)) &
+ BYPASS_OFF_MASK) == BYPASS_OFF_MASK) ? 1 : 0);
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+static int bypass_status(bpctl_dev_t *pbpctl_dev)
+{
+ u32 ctrl_ext = 0;
+ if (pbpctl_dev->bp_caps & BP_CAP) {
+
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+
+ if (!(pbpctl_dev_b = get_status_port_fn(pbpctl_dev)))
+ return BP_NOT_CAP;
+
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice)) {
+
+ if (!pbpctl_dev->bp_status_un)
+ return (((BPCTL_READ_REG
+ (pbpctl_dev_b,
+ CTRL_EXT)) &
+ BPCTLI_CTRL_EXT_SDP7_DATA) !=
+ 0 ? 1 : 0);
+ else
+ return BP_NOT_CAP;
+ }
+ if (pbpctl_dev->bp_ext_ver >= 0x8) {
+
+ if (pbpctl_dev->bp_10g9) {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev_b, I2CCTL);
+ BP10G_WRITE_REG(pbpctl_dev_b, I2CCTL,
+ (ctrl_ext | BP10G_I2C_CLK_OUT));
+ return ((BP10G_READ_REG(pbpctl_dev_b, I2CCTL) &
+ BP10G_I2C_CLK_IN) != 0 ? 0 : 1);
+
+ } else if (pbpctl_dev->bp_540) {
+ return (((BP10G_READ_REG(pbpctl_dev_b, ESDP)) &
+ BP10G_SDP0_DATA) != 0 ? 0 : 1);
+ }
+
+ else if ((pbpctl_dev->bp_fiber5)
+ || (pbpctl_dev->bp_i80)) {
+ return (((BPCTL_READ_REG(pbpctl_dev_b, CTRL)) &
+ BPCTLI_CTRL_SWDPIN0) != 0 ? 0 : 1);
+ } else if (pbpctl_dev->bp_10gb) {
+ ctrl_ext =
+ BP10GB_READ_REG(pbpctl_dev, MISC_REG_GPIO);
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_GPIO,
+ (ctrl_ext | BP10GB_GPIO3_OE_P0)
+ & ~(BP10GB_GPIO3_SET_P0 |
+ BP10GB_GPIO3_CLR_P0));
+
+ return (((BP10GB_READ_REG
+ (pbpctl_dev,
+ MISC_REG_GPIO)) & BP10GB_GPIO3_P0) !=
+ 0 ? 0 : 1);
+ }
+
+ else if (!pbpctl_dev->bp_10g)
+ return (((BPCTL_READ_REG
+ (pbpctl_dev_b,
+ CTRL_EXT)) &
+ BPCTLI_CTRL_EXT_SDP7_DATA) !=
+ 0 ? 0 : 1);
+
+ else {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev_b, EODSDP);
+ BP10G_WRITE_REG(pbpctl_dev_b, EODSDP,
+ (ctrl_ext |
+ BP10G_SDP7_DATA_OUT));
+ return ((BP10G_READ_REG(pbpctl_dev_b, EODSDP) &
+ BP10G_SDP7_DATA_IN) != 0 ? 0 : 1);
+ }
+
+ } else if (pbpctl_dev->media_type == bp_copper) {
+
+ return (((BPCTL_READ_REG(pbpctl_dev_b, CTRL)) &
+ BPCTLI_CTRL_SWDPIN1) != 0 ? 1 : 0);
+ } else {
+ if ((bypass_status_clear(pbpctl_dev)) >= 0)
+ return bypass_from_last_read(pbpctl_dev);
+ }
+
+ }
+ return BP_NOT_CAP;
+}
+
+int default_pwron_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (pbpctl_dev->bp_caps & BP_PWUP_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ return ((((read_reg
+ (pbpctl_dev,
+ STATUS_REG_ADDR)) & DFLT_PWRON_MASK)
+ == DFLT_PWRON_MASK) ? 0 : 1);
+ }
+ } /*else if ((!pbpctl_dev->bp_caps&BP_DIS_CAP)&&
+ (pbpctl_dev->bp_caps&BP_PWUP_ON_CAP))
+ return 1; */
+ }
+ return BP_NOT_CAP;
+}
+
+static int default_pwroff_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ /*if ((!pbpctl_dev->bp_caps&BP_DIS_CAP)&&
+ (pbpctl_dev->bp_caps&BP_PWOFF_ON_CAP))
+ return 1; */
+ if ((pbpctl_dev->bp_caps & SW_CTL_CAP)
+ && (pbpctl_dev->bp_caps & BP_PWOFF_CTL_CAP)) {
+ return ((((read_reg(pbpctl_dev, STATUS_REG_ADDR)) &
+ DFLT_PWROFF_MASK) == DFLT_PWROFF_MASK) ? 0 : 1);
+ }
+ return BP_NOT_CAP;
+}
+
+int dis_bypass_cap_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & BP_DIS_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ return ((((read_reg(pbpctl_dev, STATUS_REG_ADDR)) &
+ DIS_BYPASS_CAP_MASK) ==
+ DIS_BYPASS_CAP_MASK) ? 1 : 0);
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int cmd_en_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ return ((((read_reg(pbpctl_dev, STATUS_REG_ADDR)) &
+ CMND_EN_MASK) == CMND_EN_MASK) ? 1 : 0);
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int wdt_en_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ return ((((read_reg(pbpctl_dev, STATUS_REG_ADDR)) &
+ WDT_EN_MASK) == WDT_EN_MASK) ? 1 : 0);
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int wdt_programmed(bpctl_dev_t *pbpctl_dev, int *timeout)
+{
+ int ret = 0;
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ if ((read_reg(pbpctl_dev, STATUS_REG_ADDR)) &
+ WDT_EN_MASK) {
+ u8 wdt_val;
+ wdt_val = read_reg(pbpctl_dev, WDT_REG_ADDR);
+ *timeout = (1 << wdt_val) * 100;
+ } else
+ *timeout = 0;
+ } else {
+ int curr_wdt_status = pbpctl_dev->wdt_status;
+ if (curr_wdt_status == WDT_STATUS_UNKNOWN)
+ *timeout = -1;
+ else
+ *timeout =
+ curr_wdt_status ==
+ 0 ? 0 : pbpctl_dev->bypass_timer_interval;
+ };
+ } else
+ ret = BP_NOT_CAP;
+ return ret;
+}
+
+int bypass_support(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER) {
+ ret =
+ ((((read_reg(pbpctl_dev, PRODUCT_CAP_REG_ADDR)) &
+ BYPASS_SUPPORT_MASK) ==
+ BYPASS_SUPPORT_MASK) ? 1 : 0);
+ } else if (pbpctl_dev->bp_ext_ver == PXG2BPI_VER)
+ ret = 1;
+ } else
+ ret = BP_NOT_CAP;
+ return ret;
+}
+
+int tap_support(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER) {
+ ret =
+ ((((read_reg(pbpctl_dev, PRODUCT_CAP_REG_ADDR)) &
+ TAP_SUPPORT_MASK) == TAP_SUPPORT_MASK) ? 1 : 0);
+ } else if (pbpctl_dev->bp_ext_ver == PXG2BPI_VER)
+ ret = 0;
+ } else
+ ret = BP_NOT_CAP;
+ return ret;
+}
+
+int normal_support(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER) {
+ ret =
+ ((((read_reg(pbpctl_dev, PRODUCT_CAP_REG_ADDR)) &
+ NORMAL_UNSUPPORT_MASK) ==
+ NORMAL_UNSUPPORT_MASK) ? 0 : 1);
+ } else
+ ret = 1;
+ };
+ return ret;
+}
+
+int get_bp_prod_caps(bpctl_dev_t *pbpctl_dev)
+{
+ if ((pbpctl_dev->bp_caps & SW_CTL_CAP) &&
+ (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER))
+ return read_reg(pbpctl_dev, PRODUCT_CAP_REG_ADDR);
+ return BP_NOT_CAP;
+
+}
+
+int tap_flag_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & TAP_STATUS_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER)
+ return ((((read_reg(pbpctl_dev, STATUS_TAP_REG_ADDR)) &
+ TAP_FLAG_MASK) == TAP_FLAG_MASK) ? 1 : 0);
+
+ }
+ return BP_NOT_CAP;
+}
+
+int tap_flag_status_clear(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t status_reg = 0;
+ if (pbpctl_dev->bp_caps & TAP_STATUS_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER) {
+ status_reg = read_reg(pbpctl_dev, STATUS_TAP_REG_ADDR);
+ write_reg(pbpctl_dev, status_reg & ~TAP_FLAG_MASK,
+ STATUS_TAP_REG_ADDR);
+ return 0;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int tap_change_status(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+ if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER) {
+ if (pbpctl_dev->bp_caps & TAP_CAP) {
+ if (pbpctl_dev->bp_caps & BP_CAP) {
+ ret = tap_flag_status(pbpctl_dev);
+ tap_flag_status_clear(pbpctl_dev);
+ } else {
+ ret = bypass_from_last_read(pbpctl_dev);
+ bypass_status_clear(pbpctl_dev);
+ }
+ }
+ }
+ return ret;
+}
+
+int tap_off_status(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & TAP_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER)
+ return ((((read_reg(pbpctl_dev, STATUS_TAP_REG_ADDR)) &
+ TAP_OFF_MASK) == TAP_OFF_MASK) ? 1 : 0);
+ }
+ return BP_NOT_CAP;
+}
+
+int tap_status(bpctl_dev_t *pbpctl_dev)
+{
+ u32 ctrl_ext = 0;
+
+ if (pbpctl_dev->bp_caps & TAP_CAP) {
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+
+ if (!(pbpctl_dev_b = get_status_port_fn(pbpctl_dev)))
+ return BP_NOT_CAP;
+
+ if (pbpctl_dev->bp_ext_ver >= 0x8) {
+ if (!pbpctl_dev->bp_10g)
+ return (((BPCTL_READ_REG
+ (pbpctl_dev_b,
+ CTRL_EXT)) &
+ BPCTLI_CTRL_EXT_SDP6_DATA) !=
+ 0 ? 0 : 1);
+ else {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev_b, EODSDP);
+ BP10G_WRITE_REG(pbpctl_dev_b, EODSDP,
+ (ctrl_ext |
+ BP10G_SDP6_DATA_OUT));
+ return ((BP10G_READ_REG(pbpctl_dev_b, EODSDP) &
+ BP10G_SDP6_DATA_IN) != 0 ? 0 : 1);
+ }
+
+ } else if (pbpctl_dev->media_type == bp_copper)
+ return (((BPCTL_READ_REG(pbpctl_dev, CTRL)) &
+ BPCTLI_CTRL_SWDPIN0) != 0 ? 1 : 0);
+ else {
+ if ((bypass_status_clear(pbpctl_dev)) >= 0)
+ return bypass_from_last_read(pbpctl_dev);
+ }
+
+ }
+ return BP_NOT_CAP;
+}
+
+int default_pwron_tap_status(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & TAP_PWUP_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER)
+ return ((((read_reg(pbpctl_dev, STATUS_TAP_REG_ADDR)) &
+ DFLT_PWRON_TAP_MASK) ==
+ DFLT_PWRON_TAP_MASK) ? 1 : 0);
+ }
+ return BP_NOT_CAP;
+}
+
+int dis_tap_cap_status(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & TAP_PWUP_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER)
+ return ((((read_reg(pbpctl_dev, STATUS_TAP_REG_ADDR)) &
+ DIS_TAP_CAP_MASK) ==
+ DIS_TAP_CAP_MASK) ? 1 : 0);
+ }
+ return BP_NOT_CAP;
+}
+
+int disc_flag_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & DISC_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= 0x8)
+ return ((((read_reg(pbpctl_dev, STATUS_DISC_REG_ADDR)) &
+ DISC_FLAG_MASK) == DISC_FLAG_MASK) ? 1 : 0);
+
+ }
+ return BP_NOT_CAP;
+}
+
+int disc_flag_status_clear(bpctl_dev_t *pbpctl_dev)
+{
+ uint32_t status_reg = 0;
+ if (pbpctl_dev->bp_caps & DISC_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= 0x8) {
+ status_reg = read_reg(pbpctl_dev, STATUS_DISC_REG_ADDR);
+ write_reg(pbpctl_dev, status_reg & ~DISC_FLAG_MASK,
+ STATUS_DISC_REG_ADDR);
+ return BP_OK;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int disc_change_status(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+ if (pbpctl_dev->bp_caps & DISC_CAP) {
+ ret = disc_flag_status(pbpctl_dev);
+ disc_flag_status_clear(pbpctl_dev);
+ return ret;
+ }
+ return BP_NOT_CAP;
+}
+
+int disc_off_status(bpctl_dev_t *pbpctl_dev)
+{
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+ u32 ctrl_ext = 0;
+
+ if (pbpctl_dev->bp_caps & DISC_CAP) {
+ if (!(pbpctl_dev_b = get_status_port_fn(pbpctl_dev)))
+ return BP_NOT_CAP;
+ if (DISCF_IF_SERIES(pbpctl_dev->subdevice))
+ return ((((read_reg(pbpctl_dev, STATUS_DISC_REG_ADDR)) &
+ DISC_OFF_MASK) == DISC_OFF_MASK) ? 1 : 0);
+
+ if (pbpctl_dev->bp_i80) {
+ return (((BPCTL_READ_REG(pbpctl_dev_b, CTRL_EXT)) &
+ BPCTLI_CTRL_EXT_SDP6_DATA) != 0 ? 1 : 0);
+
+ }
+ if (pbpctl_dev->bp_540) {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev_b, ESDP);
+ return ((BP10G_READ_REG(pbpctl_dev_b, ESDP) &
+ BP10G_SDP2_DATA) != 0 ? 1 : 0);
+
+ }
+ if (pbpctl_dev->media_type == bp_copper) {
+
+#if 0
+ return ((((read_reg(pbpctl_dev, STATUS_DISC_REG_ADDR)) &
+ DISC_OFF_MASK) == DISC_OFF_MASK) ? 1 : 0);
+#endif
+ if (!pbpctl_dev->bp_10g)
+ return (((BPCTL_READ_REG(pbpctl_dev_b, CTRL)) &
+ BPCTLI_CTRL_SWDPIN1) != 0 ? 1 : 0);
+ else
+ return ((BP10G_READ_REG(pbpctl_dev_b, ESDP) &
+ BP10G_SDP1_DATA) != 0 ? 1 : 0);
+
+ } else {
+
+ if (pbpctl_dev->bp_10g9) {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev_b, I2CCTL);
+ BP10G_WRITE_REG(pbpctl_dev_b, I2CCTL,
+ (ctrl_ext |
+ BP10G_I2C_DATA_OUT));
+ return ((BP10G_READ_REG(pbpctl_dev_b, I2CCTL) &
+ BP10G_I2C_DATA_IN) != 0 ? 1 : 0);
+
+ } else if (pbpctl_dev->bp_fiber5) {
+ return (((BPCTL_READ_REG(pbpctl_dev_b, CTRL)) &
+ BPCTLI_CTRL_SWDPIN1) != 0 ? 1 : 0);
+ } else if (pbpctl_dev->bp_10gb) {
+ ctrl_ext =
+ BP10GB_READ_REG(pbpctl_dev, MISC_REG_GPIO);
+ BP10GB_WRITE_REG(pbpctl_dev, MISC_REG_GPIO,
+ (ctrl_ext | BP10GB_GPIO3_OE_P1)
+ & ~(BP10GB_GPIO3_SET_P1 |
+ BP10GB_GPIO3_CLR_P1));
+
+ return (((BP10GB_READ_REG
+ (pbpctl_dev,
+ MISC_REG_GPIO)) & BP10GB_GPIO3_P1) !=
+ 0 ? 1 : 0);
+ }
+ if (!pbpctl_dev->bp_10g) {
+
+ return (((BPCTL_READ_REG
+ (pbpctl_dev_b,
+ CTRL_EXT)) &
+ BPCTLI_CTRL_EXT_SDP6_DATA) !=
+ 0 ? 1 : 0);
+ } else {
+ ctrl_ext = BP10G_READ_REG(pbpctl_dev_b, EODSDP);
+ BP10G_WRITE_REG(pbpctl_dev_b, EODSDP,
+ (ctrl_ext |
+ BP10G_SDP6_DATA_OUT));
+ return (((BP10G_READ_REG(pbpctl_dev_b, EODSDP))
+ & BP10G_SDP6_DATA_IN) != 0 ? 1 : 0);
+ }
+
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+static int disc_status(bpctl_dev_t *pbpctl_dev)
+{
+ int ctrl = 0;
+ if (pbpctl_dev->bp_caps & DISC_CAP) {
+
+ if ((ctrl = disc_off_status(pbpctl_dev)) < 0)
+ return ctrl;
+ return ((ctrl == 0) ? 1 : 0);
+
+ }
+ return BP_NOT_CAP;
+}
+
+int default_pwron_disc_status(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & DISC_PWUP_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= 0x8)
+ return ((((read_reg(pbpctl_dev, STATUS_DISC_REG_ADDR)) &
+ DFLT_PWRON_DISC_MASK) ==
+ DFLT_PWRON_DISC_MASK) ? 1 : 0);
+ }
+ return BP_NOT_CAP;
+}
+
+int dis_disc_cap_status(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & DIS_DISC_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= 0x8)
+ return ((((read_reg(pbpctl_dev, STATUS_DISC_REG_ADDR)) &
+ DIS_DISC_CAP_MASK) ==
+ DIS_DISC_CAP_MASK) ? 1 : 0);
+ }
+ return BP_NOT_CAP;
+}
+
+int disc_port_status(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+ bpctl_dev_t *pbpctl_dev_m;
+
+ if ((is_bypass_fn(pbpctl_dev)) == 1)
+ pbpctl_dev_m = pbpctl_dev;
+ else
+ pbpctl_dev_m = get_master_port_fn(pbpctl_dev);
+ if (pbpctl_dev_m == NULL)
+ return BP_NOT_CAP;
+
+ if (pbpctl_dev_m->bp_caps_ex & DISC_PORT_CAP_EX) {
+ if (is_bypass_fn(pbpctl_dev) == 1) {
+ return ((((read_reg(pbpctl_dev, STATUS_TAP_REG_ADDR)) &
+ TX_DISA_MASK) == TX_DISA_MASK) ? 1 : 0);
+ } else
+ return ((((read_reg(pbpctl_dev, STATUS_TAP_REG_ADDR)) &
+ TX_DISB_MASK) == TX_DISB_MASK) ? 1 : 0);
+
+ }
+ return ret;
+}
+
+int default_pwron_disc_port_status(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+ bpctl_dev_t *pbpctl_dev_m;
+
+ if ((is_bypass_fn(pbpctl_dev)) == 1)
+ pbpctl_dev_m = pbpctl_dev;
+ else
+ pbpctl_dev_m = get_master_port_fn(pbpctl_dev);
+ if (pbpctl_dev_m == NULL)
+ return BP_NOT_CAP;
+
+ if (pbpctl_dev_m->bp_caps_ex & DISC_PORT_CAP_EX) {
+ if (is_bypass_fn(pbpctl_dev) == 1)
+ return ret;
+ /* return((((read_reg(pbpctl_dev,STATUS_TAP_REG_ADDR)) & TX_DISA_MASK)==TX_DISA_MASK)?1:0); */
+ else
+ return ret;
+ /* return((((read_reg(pbpctl_dev,STATUS_TAP_REG_ADDR)) & TX_DISA_MASK)==TX_DISA_MASK)?1:0); */
+
+ }
+ return ret;
+}
+
+int wdt_exp_mode_status(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver <= PXG2BPI_VER)
+ return 0; /* bypass mode */
+ else if (pbpctl_dev->bp_ext_ver == PXG2TBPI_VER)
+ return 1; /* tap mode */
+ else if (pbpctl_dev->bp_ext_ver >= PXE2TBPI_VER) {
+ if (pbpctl_dev->bp_ext_ver >= 0x8) {
+ if (((read_reg
+ (pbpctl_dev,
+ STATUS_DISC_REG_ADDR)) &
+ WDTE_DISC_BPN_MASK) == WDTE_DISC_BPN_MASK)
+ return 2;
+ }
+ return ((((read_reg(pbpctl_dev, STATUS_TAP_REG_ADDR)) &
+ WDTE_TAP_BPN_MASK) ==
+ WDTE_TAP_BPN_MASK) ? 1 : 0);
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+int tpl2_flag_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps_ex & TPL2_CAP_EX) {
+ return ((((read_reg(pbpctl_dev, STATUS_DISC_REG_ADDR)) &
+ TPL2_FLAG_MASK) == TPL2_FLAG_MASK) ? 1 : 0);
+
+ }
+ return BP_NOT_CAP;
+}
+
+int tpl_hw_status(bpctl_dev_t *pbpctl_dev)
+{
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+
+ if (!(pbpctl_dev_b = get_status_port_fn(pbpctl_dev)))
+ return BP_NOT_CAP;
+
+ if (TPL_IF_SERIES(pbpctl_dev->subdevice))
+ return (((BPCTL_READ_REG(pbpctl_dev, CTRL)) &
+ BPCTLI_CTRL_SWDPIN0) != 0 ? 1 : 0);
+ return BP_NOT_CAP;
+}
+
+
+int bp_wait_at_pwup_status(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ if (pbpctl_dev->bp_ext_ver >= 0x8)
+ return ((((read_reg(pbpctl_dev, CONT_CONFIG_REG_ADDR)) &
+ WAIT_AT_PWUP_MASK) ==
+ WAIT_AT_PWUP_MASK) ? 1 : 0);
+ }
+ return BP_NOT_CAP;
+}
+
+int bp_hw_reset_status(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+
+ if (pbpctl_dev->bp_ext_ver >= 0x8)
+ return ((((read_reg(pbpctl_dev, CONT_CONFIG_REG_ADDR)) &
+ EN_HW_RESET_MASK) ==
+ EN_HW_RESET_MASK) ? 1 : 0);
+ }
+ return BP_NOT_CAP;
+}
+
+
+int std_nic_status(bpctl_dev_t *pbpctl_dev)
+{
+ int status_val = 0;
+
+ if (pbpctl_dev->bp_caps & STD_NIC_CAP) {
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice))
+ return BP_NOT_CAP;
+ if (pbpctl_dev->bp_ext_ver >= BP_FW_EXT_VER8) {
+ return ((((read_reg(pbpctl_dev, STATUS_DISC_REG_ADDR)) &
+ STD_NIC_ON_MASK) == STD_NIC_ON_MASK) ? 1 : 0);
+ }
+
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ if (pbpctl_dev->bp_caps & BP_CAP) {
+ status_val =
+ read_reg(pbpctl_dev, STATUS_REG_ADDR);
+ if (((!(status_val & WDT_EN_MASK))
+ && ((status_val & STD_NIC_MASK) ==
+ STD_NIC_MASK)))
+ status_val = 1;
+ else
+ return 0;
+ }
+ if (pbpctl_dev->bp_caps & TAP_CAP) {
+ status_val =
+ read_reg(pbpctl_dev, STATUS_TAP_REG_ADDR);
+ if ((status_val & STD_NIC_TAP_MASK) ==
+ STD_NIC_TAP_MASK)
+ status_val = 1;
+ else
+ return 0;
+ }
+ if (pbpctl_dev->bp_caps & TAP_CAP) {
+ if ((disc_off_status(pbpctl_dev)))
+ status_val = 1;
+ else
+ return 0;
+ }
+
+ return status_val;
+ }
+ }
+ return BP_NOT_CAP;
+}
+
+/******************************************************/
+/**************SW_INIT*********************************/
+/******************************************************/
+void bypass_caps_init(bpctl_dev_t *pbpctl_dev)
+{
+ u_int32_t ctrl_ext = 0;
+ bpctl_dev_t *pbpctl_dev_m = NULL;
+
+#ifdef BYPASS_DEBUG
+ int ret = 0;
+ if (!(INTEL_IF_SERIES(adapter->bp_device_block.subdevice))) {
+ ret = read_reg(pbpctl_dev, VER_REG_ADDR);
+ printk("VER_REG reg1=%x\n", ret);
+ ret = read_reg(pbpctl_dev, PRODUCT_CAP_REG_ADDR);
+ printk("PRODUCT_CAP reg=%x\n", ret);
+ ret = read_reg(pbpctl_dev, STATUS_TAP_REG_ADDR);
+ printk("STATUS_TAP reg1=%x\n", ret);
+ ret = read_reg(pbpctl_dev, 0x7);
+ printk("SIG_REG reg1=%x\n", ret);
+ ret = read_reg(pbpctl_dev, STATUS_REG_ADDR);
+ printk("STATUS_REG_ADDR=%x\n", ret);
+ ret = read_reg(pbpctl_dev, WDT_REG_ADDR);
+ printk("WDT_REG_ADDR=%x\n", ret);
+ ret = read_reg(pbpctl_dev, TMRL_REG_ADDR);
+ printk("TMRL_REG_ADDR=%x\n", ret);
+ ret = read_reg(pbpctl_dev, TMRH_REG_ADDR);
+ printk("TMRH_REG_ADDR=%x\n", ret);
+ }
+#endif
+ if ((pbpctl_dev->bp_fiber5) || (pbpctl_dev->bp_10g9)) {
+ pbpctl_dev->media_type = bp_fiber;
+ } else if (pbpctl_dev->bp_10gb) {
+ if (BP10GB_CX4_SERIES(pbpctl_dev->subdevice))
+ pbpctl_dev->media_type = bp_cx4;
+ else
+ pbpctl_dev->media_type = bp_fiber;
+
+ }
+
+ else if (pbpctl_dev->bp_540)
+ pbpctl_dev->media_type = bp_none;
+ else if (!pbpctl_dev->bp_10g) {
+
+ ctrl_ext = BPCTL_READ_REG(pbpctl_dev, CTRL_EXT);
+ if ((ctrl_ext & BPCTLI_CTRL_EXT_LINK_MODE_MASK) == 0x0)
+ pbpctl_dev->media_type = bp_copper;
+ else
+ pbpctl_dev->media_type = bp_fiber;
+
+ } else {
+ if (BP10G_CX4_SERIES(pbpctl_dev->subdevice))
+ pbpctl_dev->media_type = bp_cx4;
+ else
+ pbpctl_dev->media_type = bp_fiber;
+ }
+
+ if (is_bypass_fn(pbpctl_dev)) {
+
+ pbpctl_dev->bp_caps |= BP_PWOFF_ON_CAP;
+ if (pbpctl_dev->media_type == bp_fiber)
+ pbpctl_dev->bp_caps |=
+ (TX_CTL_CAP | TX_STATUS_CAP | TPL_CAP);
+
+ if (TPL_IF_SERIES(pbpctl_dev->subdevice)) {
+ pbpctl_dev->bp_caps |= TPL_CAP;
+ }
+
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice)) {
+ pbpctl_dev->bp_caps |=
+ (BP_CAP | BP_STATUS_CAP | SW_CTL_CAP |
+ BP_PWUP_ON_CAP | BP_PWUP_OFF_CAP | BP_PWOFF_OFF_CAP
+ | WD_CTL_CAP | WD_STATUS_CAP | STD_NIC_CAP |
+ WD_TIMEOUT_CAP);
+
+ pbpctl_dev->bp_ext_ver = OLD_IF_VER;
+ return;
+ }
+
+ if ((pbpctl_dev->bp_fw_ver == 0xff) &&
+ OLD_IF_SERIES(pbpctl_dev->subdevice)) {
+
+ pbpctl_dev->bp_caps |=
+ (BP_CAP | BP_STATUS_CAP | BP_STATUS_CHANGE_CAP |
+ SW_CTL_CAP | BP_PWUP_ON_CAP | WD_CTL_CAP |
+ WD_STATUS_CAP | WD_TIMEOUT_CAP);
+
+ pbpctl_dev->bp_ext_ver = OLD_IF_VER;
+ return;
+ }
+
+ else {
+ switch (pbpctl_dev->bp_fw_ver) {
+ case BP_FW_VER_A0:
+ case BP_FW_VER_A1:{
+ pbpctl_dev->bp_ext_ver =
+ (pbpctl_dev->
+ bp_fw_ver & EXT_VER_MASK);
+ break;
+ }
+ default:{
+ if ((bypass_sign_check(pbpctl_dev)) !=
+ 1) {
+ pbpctl_dev->bp_caps = 0;
+ return;
+ }
+ pbpctl_dev->bp_ext_ver =
+ (pbpctl_dev->
+ bp_fw_ver & EXT_VER_MASK);
+ }
+ }
+ }
+
+ if (pbpctl_dev->bp_ext_ver == PXG2BPI_VER)
+ pbpctl_dev->bp_caps |=
+ (BP_CAP | BP_STATUS_CAP | BP_STATUS_CHANGE_CAP |
+ SW_CTL_CAP | BP_DIS_CAP | BP_DIS_STATUS_CAP |
+ BP_PWUP_ON_CAP | BP_PWUP_OFF_CAP | BP_PWUP_CTL_CAP
+ | WD_CTL_CAP | STD_NIC_CAP | WD_STATUS_CAP |
+ WD_TIMEOUT_CAP);
+ else if (pbpctl_dev->bp_ext_ver >= PXG2TBPI_VER) {
+ int cap_reg;
+
+ pbpctl_dev->bp_caps |=
+ (SW_CTL_CAP | WD_CTL_CAP | WD_STATUS_CAP |
+ WD_TIMEOUT_CAP);
+ cap_reg = get_bp_prod_caps(pbpctl_dev);
+
+ if ((cap_reg & NORMAL_UNSUPPORT_MASK) ==
+ NORMAL_UNSUPPORT_MASK)
+ pbpctl_dev->bp_caps |= NIC_CAP_NEG;
+ else
+ pbpctl_dev->bp_caps |= STD_NIC_CAP;
+
+ if ((normal_support(pbpctl_dev)) == 1)
+
+ pbpctl_dev->bp_caps |= STD_NIC_CAP;
+
+ else
+ pbpctl_dev->bp_caps |= NIC_CAP_NEG;
+ if ((cap_reg & BYPASS_SUPPORT_MASK) ==
+ BYPASS_SUPPORT_MASK) {
+ pbpctl_dev->bp_caps |=
+ (BP_CAP | BP_STATUS_CAP |
+ BP_STATUS_CHANGE_CAP | BP_DIS_CAP |
+ BP_DIS_STATUS_CAP | BP_PWUP_ON_CAP |
+ BP_PWUP_OFF_CAP | BP_PWUP_CTL_CAP);
+ if (pbpctl_dev->bp_ext_ver >= BP_FW_EXT_VER7)
+ pbpctl_dev->bp_caps |=
+ BP_PWOFF_ON_CAP | BP_PWOFF_OFF_CAP |
+ BP_PWOFF_CTL_CAP;
+ }
+ if ((cap_reg & TAP_SUPPORT_MASK) == TAP_SUPPORT_MASK) {
+ pbpctl_dev->bp_caps |=
+ (TAP_CAP | TAP_STATUS_CAP |
+ TAP_STATUS_CHANGE_CAP | TAP_DIS_CAP |
+ TAP_DIS_STATUS_CAP | TAP_PWUP_ON_CAP |
+ TAP_PWUP_OFF_CAP | TAP_PWUP_CTL_CAP);
+ }
+ if (pbpctl_dev->bp_ext_ver >= BP_FW_EXT_VER8) {
+ if ((cap_reg & DISC_SUPPORT_MASK) ==
+ DISC_SUPPORT_MASK)
+ pbpctl_dev->bp_caps |=
+ (DISC_CAP | DISC_DIS_CAP |
+ DISC_PWUP_CTL_CAP);
+ if ((cap_reg & TPL2_SUPPORT_MASK) ==
+ TPL2_SUPPORT_MASK) {
+ pbpctl_dev->bp_caps_ex |= TPL2_CAP_EX;
+ pbpctl_dev->bp_caps |= TPL_CAP;
+ pbpctl_dev->bp_tpl_flag =
+ tpl2_flag_status(pbpctl_dev);
+ }
+
+ }
+
+ if (pbpctl_dev->bp_ext_ver >= BP_FW_EXT_VER9) {
+ if ((cap_reg & DISC_PORT_SUPPORT_MASK) ==
+ DISC_PORT_SUPPORT_MASK) {
+ pbpctl_dev->bp_caps_ex |=
+ DISC_PORT_CAP_EX;
+ pbpctl_dev->bp_caps |=
+ (TX_CTL_CAP | TX_STATUS_CAP);
+ }
+
+ }
+
+ }
+ if (pbpctl_dev->bp_ext_ver >= PXG2BPI_VER) {
+ if ((read_reg(pbpctl_dev, STATUS_REG_ADDR)) &
+ WDT_EN_MASK)
+ pbpctl_dev->wdt_status = WDT_STATUS_EN;
+ else
+ pbpctl_dev->wdt_status = WDT_STATUS_DIS;
+ }
+
+ } else if ((P2BPFI_IF_SERIES(pbpctl_dev->subdevice)) ||
+ (PEGF5_IF_SERIES(pbpctl_dev->subdevice)) ||
+ (PEGF80_IF_SERIES(pbpctl_dev->subdevice)) ||
+ (BP10G9_IF_SERIES(pbpctl_dev->subdevice))) {
+ pbpctl_dev->bp_caps |= (TX_CTL_CAP | TX_STATUS_CAP);
+ }
+ if ((pbpctl_dev->subdevice & 0xa00) == 0xa00)
+ pbpctl_dev->bp_caps |= (TX_CTL_CAP | TX_STATUS_CAP);
+ if (PEG5_IF_SERIES(pbpctl_dev->subdevice))
+ pbpctl_dev->bp_caps |= (TX_CTL_CAP | TX_STATUS_CAP);
+
+ if (BP10GB_IF_SERIES(pbpctl_dev->subdevice)) {
+ pbpctl_dev->bp_caps &= ~(TX_CTL_CAP | TX_STATUS_CAP);
+ }
+ pbpctl_dev_m = get_master_port_fn(pbpctl_dev);
+ if (pbpctl_dev_m != NULL) {
+ int cap_reg = 0;
+ if (pbpctl_dev_m->bp_ext_ver >= 0x9) {
+ cap_reg = get_bp_prod_caps(pbpctl_dev_m);
+ if ((cap_reg & DISC_PORT_SUPPORT_MASK) ==
+ DISC_PORT_SUPPORT_MASK)
+ pbpctl_dev->bp_caps |=
+ (TX_CTL_CAP | TX_STATUS_CAP);
+ pbpctl_dev->bp_caps_ex |= DISC_PORT_CAP_EX;
+ }
+ }
+}
+
+int bypass_off_init(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+
+ if ((ret = cmnd_on(pbpctl_dev)) < 0)
+ return ret;
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice))
+ return dis_bypass_cap(pbpctl_dev);
+ wdt_off(pbpctl_dev);
+ if (pbpctl_dev->bp_caps & BP_CAP)
+ bypass_off(pbpctl_dev);
+ if (pbpctl_dev->bp_caps & TAP_CAP)
+ tap_off(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+ return 0;
+}
+
+void remove_bypass_wd_auto(bpctl_dev_t *pbpctl_dev)
+{
+#ifdef BP_SELF_TEST
+ bpctl_dev_t *pbpctl_dev_sl = NULL;
+#endif
+
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+
+ del_timer_sync(&pbpctl_dev->bp_timer);
+#ifdef BP_SELF_TEST
+ pbpctl_dev_sl = get_status_port_fn(pbpctl_dev);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
+ if (pbpctl_dev_sl && (pbpctl_dev_sl->ndev)
+ && (pbpctl_dev_sl->ndev->hard_start_xmit)
+ && (pbpctl_dev_sl->hard_start_xmit_save)) {
+ rtnl_lock();
+ pbpctl_dev_sl->ndev->hard_start_xmit =
+ pbpctl_dev_sl->hard_start_xmit_save;
+ rtnl_unlock();
+ }
+#else
+ if (pbpctl_dev_sl && (pbpctl_dev_sl->ndev)) {
+ if ((pbpctl_dev_sl->ndev->netdev_ops)
+ && (pbpctl_dev_sl->old_ops)) {
+ rtnl_lock();
+ pbpctl_dev_sl->ndev->netdev_ops =
+ pbpctl_dev_sl->old_ops;
+ pbpctl_dev_sl->old_ops = NULL;
+
+ rtnl_unlock();
+
+ }
+
+ }
+
+#endif
+#endif
+ }
+
+}
+
+int init_bypass_wd_auto(bpctl_dev_t *pbpctl_dev)
+{
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+ init_timer(&pbpctl_dev->bp_timer);
+ pbpctl_dev->bp_timer.function = &wd_reset_timer;
+ pbpctl_dev->bp_timer.data = (unsigned long)pbpctl_dev;
+ return 1;
+ }
+ return BP_NOT_CAP;
+}
+
+#ifdef BP_SELF_TEST
+int bp_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ bpctl_dev_t *pbpctl_dev = NULL, *pbpctl_dev_m = NULL;
+ int idx_dev = 0;
+ struct ethhdr *eth = (struct ethhdr *)skb->data;
+
+ for (idx_dev = 0;
+ ((bpctl_dev_arr[idx_dev].ndev != NULL) && (idx_dev < device_num));
+ idx_dev++) {
+ if (bpctl_dev_arr[idx_dev].ndev == dev) {
+ pbpctl_dev = &bpctl_dev_arr[idx_dev];
+ break;
+ }
+ }
+ if (!pbpctl_dev)
+ return 1;
+ if ((htons(ETH_P_BPTEST) == eth->h_proto)) {
+
+ pbpctl_dev_m = get_master_port_fn(pbpctl_dev);
+ if (pbpctl_dev_m) {
+
+ if (bypass_status(pbpctl_dev_m)) {
+ cmnd_on(pbpctl_dev_m);
+ bypass_off(pbpctl_dev_m);
+ cmnd_off(pbpctl_dev_m);
+ }
+ wdt_timer_reload(pbpctl_dev_m);
+ }
+ dev_kfree_skb_irq(skb);
+ return 0;
+ }
+ return pbpctl_dev->hard_start_xmit_save(skb, dev);
+}
+#endif
+
+int set_bypass_wd_auto(bpctl_dev_t *pbpctl_dev, unsigned int param)
+{
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+ if (pbpctl_dev->reset_time != param) {
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice))
+ pbpctl_dev->reset_time =
+ (param <
+ WDT_AUTO_MIN_INT) ? WDT_AUTO_MIN_INT :
+ param;
+ else
+ pbpctl_dev->reset_time = param;
+ if (param)
+ mod_timer(&pbpctl_dev->bp_timer, jiffies);
+ }
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+int get_bypass_wd_auto(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+ return pbpctl_dev->reset_time;
+ }
+ return BP_NOT_CAP;
+}
+
+#ifdef BP_SELF_TEST
+
+int set_bp_self_test(bpctl_dev_t *pbpctl_dev, unsigned int param)
+{
+ bpctl_dev_t *pbpctl_dev_sl = NULL;
+
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+ pbpctl_dev->bp_self_test_flag = param == 0 ? 0 : 1;
+ pbpctl_dev_sl = get_status_port_fn(pbpctl_dev);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,31))
+ if ((pbpctl_dev_sl->ndev) &&
+ (pbpctl_dev_sl->ndev->hard_start_xmit)) {
+ rtnl_lock();
+ if (pbpctl_dev->bp_self_test_flag == 1) {
+
+ pbpctl_dev_sl->hard_start_xmit_save =
+ pbpctl_dev_sl->ndev->hard_start_xmit;
+ pbpctl_dev_sl->ndev->hard_start_xmit =
+ bp_hard_start_xmit;
+ } else if (pbpctl_dev_sl->hard_start_xmit_save) {
+ pbpctl_dev_sl->ndev->hard_start_xmit =
+ pbpctl_dev_sl->hard_start_xmit_save;
+ }
+ rtnl_unlock();
+ }
+#else
+ if ((pbpctl_dev_sl->ndev) && (pbpctl_dev_sl->ndev->netdev_ops)) {
+ rtnl_lock();
+ if (pbpctl_dev->bp_self_test_flag == 1) {
+
+ pbpctl_dev_sl->old_ops =
+ pbpctl_dev_sl->ndev->netdev_ops;
+ pbpctl_dev_sl->new_ops =
+ *pbpctl_dev_sl->old_ops;
+ pbpctl_dev_sl->new_ops.ndo_start_xmit =
+ bp_hard_start_xmit;
+ pbpctl_dev_sl->ndev->netdev_ops =
+ &pbpctl_dev_sl->new_ops;
+
+ } else if (pbpctl_dev_sl->old_ops) {
+ pbpctl_dev_sl->ndev->netdev_ops =
+ pbpctl_dev_sl->old_ops;
+ pbpctl_dev_sl->old_ops = NULL;
+ }
+ rtnl_unlock();
+ }
+#endif
+
+ set_bypass_wd_auto(pbpctl_dev, param);
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+int get_bp_self_test(bpctl_dev_t *pbpctl_dev)
+{
+
+ if (pbpctl_dev->bp_caps & WD_CTL_CAP) {
+ if (pbpctl_dev->bp_self_test_flag == 1)
+ return pbpctl_dev->reset_time;
+ else
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+#endif
+
+/**************************************************************/
+/************************* API ********************************/
+/**************************************************************/
+
+int is_bypass_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return (((pbpctl_dev->func == 0) || (pbpctl_dev->func == 2)) ? 1 : 0);
+}
+
+int set_bypass_fn(bpctl_dev_t *pbpctl_dev, int bypass_mode)
+{
+ int ret = 0;
+
+ if (!(pbpctl_dev->bp_caps & BP_CAP))
+ return BP_NOT_CAP;
+ if ((ret = cmnd_on(pbpctl_dev)) < 0)
+ return ret;
+ if (!bypass_mode)
+ ret = bypass_off(pbpctl_dev);
+ else
+ ret = bypass_on(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+
+ return ret;
+}
+
+int get_bypass_fn(bpctl_dev_t *pbpctl_dev)
+{
+ return bypass_status(pbpctl_dev);
+}
+
+int get_bypass_change_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return bypass_change_status(pbpctl_dev);
+}
+
+int set_dis_bypass_fn(bpctl_dev_t *pbpctl_dev, int dis_param)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (!(pbpctl_dev->bp_caps & BP_DIS_CAP))
+ return BP_NOT_CAP;
+ if ((ret = cmnd_on(pbpctl_dev)) < 0)
+ return ret;
+ if (dis_param)
+ ret = dis_bypass_cap(pbpctl_dev);
+ else
+ ret = en_bypass_cap(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+ return ret;
+}
+
+int get_dis_bypass_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return dis_bypass_cap_status(pbpctl_dev);
+}
+
+int set_bypass_pwoff_fn(bpctl_dev_t *pbpctl_dev, int bypass_mode)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (!(pbpctl_dev->bp_caps & BP_PWOFF_CTL_CAP))
+ return BP_NOT_CAP;
+ if ((ret = cmnd_on(pbpctl_dev)) < 0)
+ return ret;
+ if (bypass_mode)
+ ret = bypass_state_pwroff(pbpctl_dev);
+ else
+ ret = normal_state_pwroff(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+ return ret;
+}
+
+int get_bypass_pwoff_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return default_pwroff_status(pbpctl_dev);
+}
+
+int set_bypass_pwup_fn(bpctl_dev_t *pbpctl_dev, int bypass_mode)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (!(pbpctl_dev->bp_caps & BP_PWUP_CTL_CAP))
+ return BP_NOT_CAP;
+ if ((ret = cmnd_on(pbpctl_dev)) < 0)
+ return ret;
+ if (bypass_mode)
+ ret = bypass_state_pwron(pbpctl_dev);
+ else
+ ret = normal_state_pwron(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+ return ret;
+}
+
+int get_bypass_pwup_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return default_pwron_status(pbpctl_dev);
+}
+
+int set_bypass_wd_fn(bpctl_dev_t *pbpctl_dev, int timeout)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (!(pbpctl_dev->bp_caps & WD_CTL_CAP))
+ return BP_NOT_CAP;
+
+ if ((ret = cmnd_on(pbpctl_dev)) < 0)
+ return ret;
+ if (!timeout)
+ ret = wdt_off(pbpctl_dev);
+ else {
+ wdt_on(pbpctl_dev, timeout);
+ ret = pbpctl_dev->bypass_timer_interval;
+ }
+ cmnd_off(pbpctl_dev);
+ return ret;
+}
+
+int get_bypass_wd_fn(bpctl_dev_t *pbpctl_dev, int *timeout)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return wdt_programmed(pbpctl_dev, timeout);
+}
+
+int get_wd_expire_time_fn(bpctl_dev_t *pbpctl_dev, int *time_left)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return wdt_timer(pbpctl_dev, time_left);
+}
+
+int reset_bypass_wd_timer_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return wdt_timer_reload(pbpctl_dev);
+}
+
+int get_wd_set_caps_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int bp_status = 0;
+
+ unsigned int step_value = TIMEOUT_MAX_STEP + 1, bit_cnt = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (INTEL_IF_SERIES(pbpctl_dev->subdevice))
+ return BP_NOT_CAP;
+
+ while ((step_value >>= 1))
+ bit_cnt++;
+
+ if (is_bypass_fn(pbpctl_dev)) {
+ bp_status =
+ WD_STEP_COUNT_MASK(bit_cnt) | WDT_STEP_TIME |
+ WD_MIN_TIME_MASK(TIMEOUT_UNIT / 100);
+ } else
+ return -1;
+
+ return bp_status;
+}
+
+int set_std_nic_fn(bpctl_dev_t *pbpctl_dev, int nic_mode)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (!(pbpctl_dev->bp_caps & STD_NIC_CAP))
+ return BP_NOT_CAP;
+
+ if ((ret = cmnd_on(pbpctl_dev)) < 0)
+ return ret;
+ if (nic_mode)
+ ret = std_nic_on(pbpctl_dev);
+ else
+ ret = std_nic_off(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+ return ret;
+}
+
+int get_std_nic_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return std_nic_status(pbpctl_dev);
+}
+
+int set_tap_fn(bpctl_dev_t *pbpctl_dev, int tap_mode)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((pbpctl_dev->bp_caps & TAP_CAP) && ((cmnd_on(pbpctl_dev)) >= 0)) {
+ if (!tap_mode)
+ tap_off(pbpctl_dev);
+ else
+ tap_on(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+int get_tap_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return tap_status(pbpctl_dev);
+}
+
+int set_tap_pwup_fn(bpctl_dev_t *pbpctl_dev, int tap_mode)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((pbpctl_dev->bp_caps & TAP_PWUP_CTL_CAP)
+ && ((cmnd_on(pbpctl_dev)) >= 0)) {
+ if (tap_mode)
+ ret = tap_state_pwron(pbpctl_dev);
+ else
+ ret = normal_state_pwron(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+ } else
+ ret = BP_NOT_CAP;
+ return ret;
+}
+
+int get_tap_pwup_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((ret = default_pwron_tap_status(pbpctl_dev)) < 0)
+ return ret;
+ return ((ret == 0) ? 1 : 0);
+}
+
+int get_tap_change_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return tap_change_status(pbpctl_dev);
+}
+
+int set_dis_tap_fn(bpctl_dev_t *pbpctl_dev, int dis_param)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((pbpctl_dev->bp_caps & TAP_DIS_CAP) && ((cmnd_on(pbpctl_dev)) >= 0)) {
+ if (dis_param)
+ ret = dis_tap_cap(pbpctl_dev);
+ else
+ ret = en_tap_cap(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+ return ret;
+ } else
+ return BP_NOT_CAP;
+}
+
+int get_dis_tap_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return dis_tap_cap_status(pbpctl_dev);
+}
+
+int set_disc_fn(bpctl_dev_t *pbpctl_dev, int disc_mode)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((pbpctl_dev->bp_caps & DISC_CAP) && ((cmnd_on(pbpctl_dev)) >= 0)) {
+ if (!disc_mode)
+ disc_off(pbpctl_dev);
+ else
+ disc_on(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+
+ return BP_OK;
+ }
+ return BP_NOT_CAP;
+}
+
+int get_disc_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ ret = disc_status(pbpctl_dev);
+
+ return ret;
+}
+
+int set_disc_pwup_fn(bpctl_dev_t *pbpctl_dev, int disc_mode)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((pbpctl_dev->bp_caps & DISC_PWUP_CTL_CAP)
+ && ((cmnd_on(pbpctl_dev)) >= 0)) {
+ if (disc_mode)
+ ret = disc_state_pwron(pbpctl_dev);
+ else
+ ret = normal_state_pwron(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+ } else
+ ret = BP_NOT_CAP;
+ return ret;
+}
+
+int get_disc_pwup_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ ret = default_pwron_disc_status(pbpctl_dev);
+ return (ret == 0 ? 1 : (ret < 0 ? BP_NOT_CAP : 0));
+}
+
+int get_disc_change_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ ret = disc_change_status(pbpctl_dev);
+ return ret;
+}
+
+int set_dis_disc_fn(bpctl_dev_t *pbpctl_dev, int dis_param)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((pbpctl_dev->bp_caps & DISC_DIS_CAP)
+ && ((cmnd_on(pbpctl_dev)) >= 0)) {
+ if (dis_param)
+ ret = dis_disc_cap(pbpctl_dev);
+ else
+ ret = en_disc_cap(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+ return ret;
+ } else
+ return BP_NOT_CAP;
+}
+
+int get_dis_disc_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ ret = dis_disc_cap_status(pbpctl_dev);
+
+ return ret;
+}
+
+int set_disc_port_fn(bpctl_dev_t *pbpctl_dev, int disc_mode)
+{
+ int ret = BP_NOT_CAP;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (!disc_mode)
+ ret = disc_port_off(pbpctl_dev);
+ else
+ ret = disc_port_on(pbpctl_dev);
+
+ return ret;
+}
+
+int get_disc_port_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return disc_port_status(pbpctl_dev);
+}
+
+int set_disc_port_pwup_fn(bpctl_dev_t *pbpctl_dev, int disc_mode)
+{
+ int ret = BP_NOT_CAP;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (!disc_mode)
+ ret = normal_port_state_pwron(pbpctl_dev);
+ else
+ ret = disc_port_state_pwron(pbpctl_dev);
+
+ return ret;
+}
+
+int get_disc_port_pwup_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((ret = default_pwron_disc_port_status(pbpctl_dev)) < 0)
+ return ret;
+ return ((ret == 0) ? 1 : 0);
+}
+
+int get_wd_exp_mode_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return wdt_exp_mode_status(pbpctl_dev);
+}
+
+int set_wd_exp_mode_fn(bpctl_dev_t *pbpctl_dev, int param)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return wdt_exp_mode(pbpctl_dev, param);
+}
+
+int reset_cont_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((ret = cmnd_on(pbpctl_dev)) < 0)
+ return ret;
+ return reset_cont(pbpctl_dev);
+}
+
+int set_tx_fn(bpctl_dev_t *pbpctl_dev, int tx_state)
+{
+
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((pbpctl_dev->bp_caps & TPL_CAP) &&
+ (pbpctl_dev->bp_caps & SW_CTL_CAP)) {
+ if ((pbpctl_dev->bp_tpl_flag))
+ return BP_NOT_CAP;
+ } else if ((pbpctl_dev_b = get_master_port_fn(pbpctl_dev))) {
+ if ((pbpctl_dev_b->bp_caps & TPL_CAP) &&
+ (pbpctl_dev_b->bp_tpl_flag))
+ return BP_NOT_CAP;
+ }
+ return set_tx(pbpctl_dev, tx_state);
+}
+
+int set_bp_force_link_fn(int dev_num, int tx_state)
+{
+ static bpctl_dev_t *bpctl_dev_curr;
+
+ if ((dev_num < 0) || (dev_num > device_num)
+ || (bpctl_dev_arr[dev_num].pdev == NULL))
+ return -1;
+ bpctl_dev_curr = &bpctl_dev_arr[dev_num];
+
+ return set_bp_force_link(bpctl_dev_curr, tx_state);
+}
+
+int set_wd_autoreset_fn(bpctl_dev_t *pbpctl_dev, int param)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return set_bypass_wd_auto(pbpctl_dev, param);
+}
+
+int get_wd_autoreset_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return get_bypass_wd_auto(pbpctl_dev);
+}
+
+#ifdef BP_SELF_TEST
+int set_bp_self_test_fn(bpctl_dev_t *pbpctl_dev, int param)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return set_bp_self_test(pbpctl_dev, param);
+}
+
+int get_bp_self_test_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return get_bp_self_test(pbpctl_dev);
+}
+
+#endif
+
+int get_bypass_caps_fn(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ return pbpctl_dev->bp_caps;
+
+}
+
+int get_bypass_slave_fn(bpctl_dev_t *pbpctl_dev, bpctl_dev_t **pbpctl_dev_out)
+{
+ int idx_dev = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((pbpctl_dev->func == 0) || (pbpctl_dev->func == 2)) {
+ for (idx_dev = 0;
+ ((bpctl_dev_arr[idx_dev].pdev != NULL)
+ && (idx_dev < device_num)); idx_dev++) {
+ if ((bpctl_dev_arr[idx_dev].bus == pbpctl_dev->bus)
+ && (bpctl_dev_arr[idx_dev].slot ==
+ pbpctl_dev->slot)) {
+ if ((pbpctl_dev->func == 0)
+ && (bpctl_dev_arr[idx_dev].func == 1)) {
+ *pbpctl_dev_out =
+ &bpctl_dev_arr[idx_dev];
+ return 1;
+ }
+ if ((pbpctl_dev->func == 2) &&
+ (bpctl_dev_arr[idx_dev].func == 3)) {
+ *pbpctl_dev_out =
+ &bpctl_dev_arr[idx_dev];
+ return 1;
+ }
+ }
+ }
+ return -1;
+ } else
+ return 0;
+}
+
+int is_bypass(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((pbpctl_dev->func == 0) || (pbpctl_dev->func == 2))
+ return 1;
+ else
+ return 0;
+}
+
+int get_tx_fn(bpctl_dev_t *pbpctl_dev)
+{
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+ if (!pbpctl_dev)
+ return -1;
+
+ if ((pbpctl_dev->bp_caps & TPL_CAP) &&
+ (pbpctl_dev->bp_caps & SW_CTL_CAP)) {
+ if ((pbpctl_dev->bp_tpl_flag))
+ return BP_NOT_CAP;
+ } else if ((pbpctl_dev_b = get_master_port_fn(pbpctl_dev))) {
+ if ((pbpctl_dev_b->bp_caps & TPL_CAP) &&
+ (pbpctl_dev_b->bp_tpl_flag))
+ return BP_NOT_CAP;
+ }
+ return tx_status(pbpctl_dev);
+}
+
+int get_bp_force_link_fn(int dev_num)
+{
+ static bpctl_dev_t *bpctl_dev_curr;
+
+ if ((dev_num < 0) || (dev_num > device_num)
+ || (bpctl_dev_arr[dev_num].pdev == NULL))
+ return -1;
+ bpctl_dev_curr = &bpctl_dev_arr[dev_num];
+
+ return bp_force_link_status(bpctl_dev_curr);
+}
+
+static int get_bypass_link_status(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ if (pbpctl_dev->media_type == bp_fiber)
+ return ((BPCTL_READ_REG(pbpctl_dev, CTRL) &
+ BPCTLI_CTRL_SWDPIN1));
+ else
+ return ((BPCTL_READ_REG(pbpctl_dev, STATUS) &
+ BPCTLI_STATUS_LU));
+
+}
+
+static void bp_tpl_timer_fn(unsigned long param)
+{
+ bpctl_dev_t *pbpctl_dev = (bpctl_dev_t *) param;
+ uint32_t link1, link2;
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+
+ if (!(pbpctl_dev_b = get_status_port_fn(pbpctl_dev)))
+ return;
+
+ if (!pbpctl_dev->bp_tpl_flag) {
+ set_tx(pbpctl_dev_b, 1);
+ set_tx(pbpctl_dev, 1);
+ return;
+ }
+ link1 = get_bypass_link_status(pbpctl_dev);
+
+ link2 = get_bypass_link_status(pbpctl_dev_b);
+ if ((link1) && (tx_status(pbpctl_dev))) {
+ if ((!link2) && (tx_status(pbpctl_dev_b))) {
+ set_tx(pbpctl_dev, 0);
+ } else if (!tx_status(pbpctl_dev_b)) {
+ set_tx(pbpctl_dev_b, 1);
+ }
+ } else if ((!link1) && (tx_status(pbpctl_dev))) {
+ if ((link2) && (tx_status(pbpctl_dev_b))) {
+ set_tx(pbpctl_dev_b, 0);
+ }
+ } else if ((link1) && (!tx_status(pbpctl_dev))) {
+ if ((link2) && (tx_status(pbpctl_dev_b))) {
+ set_tx(pbpctl_dev, 1);
+ }
+ } else if ((!link1) && (!tx_status(pbpctl_dev))) {
+ if ((link2) && (tx_status(pbpctl_dev_b))) {
+ set_tx(pbpctl_dev, 1);
+ }
+ }
+
+ mod_timer(&pbpctl_dev->bp_tpl_timer, jiffies + BP_LINK_MON_DELAY * HZ);
+}
+
+void remove_bypass_tpl_auto(bpctl_dev_t *pbpctl_dev)
+{
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+ if (!pbpctl_dev)
+ return;
+ pbpctl_dev_b = get_status_port_fn(pbpctl_dev);
+
+ if (pbpctl_dev->bp_caps & TPL_CAP) {
+ del_timer_sync(&pbpctl_dev->bp_tpl_timer);
+ pbpctl_dev->bp_tpl_flag = 0;
+ pbpctl_dev_b = get_status_port_fn(pbpctl_dev);
+ if (pbpctl_dev_b)
+ set_tx(pbpctl_dev_b, 1);
+ set_tx(pbpctl_dev, 1);
+ }
+ return;
+}
+
+int init_bypass_tpl_auto(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+ if (pbpctl_dev->bp_caps & TPL_CAP) {
+ init_timer(&pbpctl_dev->bp_tpl_timer);
+ pbpctl_dev->bp_tpl_timer.function = &bp_tpl_timer_fn;
+ pbpctl_dev->bp_tpl_timer.data = (unsigned long)pbpctl_dev;
+ return BP_OK;
+ }
+ return BP_NOT_CAP;
+}
+
+int set_bypass_tpl_auto(bpctl_dev_t *pbpctl_dev, unsigned int param)
+{
+ if (!pbpctl_dev)
+ return -1;
+ if (pbpctl_dev->bp_caps & TPL_CAP) {
+ if ((param) && (!pbpctl_dev->bp_tpl_flag)) {
+ pbpctl_dev->bp_tpl_flag = param;
+ mod_timer(&pbpctl_dev->bp_tpl_timer, jiffies + 1);
+ return BP_OK;
+ };
+ if ((!param) && (pbpctl_dev->bp_tpl_flag))
+ remove_bypass_tpl_auto(pbpctl_dev);
+
+ return BP_OK;
+ }
+ return BP_NOT_CAP;
+}
+
+int get_bypass_tpl_auto(bpctl_dev_t *pbpctl_dev)
+{
+ if (!pbpctl_dev)
+ return -1;
+ if (pbpctl_dev->bp_caps & TPL_CAP) {
+ return pbpctl_dev->bp_tpl_flag;
+ }
+ return BP_NOT_CAP;
+}
+
+int set_tpl_fn(bpctl_dev_t *pbpctl_dev, int tpl_mode)
+{
+
+ bpctl_dev_t *pbpctl_dev_b = NULL;
+ if (!pbpctl_dev)
+ return -1;
+
+ pbpctl_dev_b = get_status_port_fn(pbpctl_dev);
+
+ if (pbpctl_dev->bp_caps & TPL_CAP) {
+ if (tpl_mode) {
+ if ((pbpctl_dev_b = get_status_port_fn(pbpctl_dev)))
+ set_tx(pbpctl_dev_b, 1);
+ set_tx(pbpctl_dev, 1);
+ }
+ if ((TPL_IF_SERIES(pbpctl_dev->subdevice)) ||
+ (pbpctl_dev->bp_caps_ex & TPL2_CAP_EX)) {
+ pbpctl_dev->bp_tpl_flag = tpl_mode;
+ if (!tpl_mode)
+ tpl_hw_off(pbpctl_dev);
+ else
+ tpl_hw_on(pbpctl_dev);
+ } else
+ set_bypass_tpl_auto(pbpctl_dev, tpl_mode);
+ return 0;
+ }
+ return BP_NOT_CAP;
+}
+
+int get_tpl_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = BP_NOT_CAP;
+ if (!pbpctl_dev)
+ return -1;
+
+ if (pbpctl_dev->bp_caps & TPL_CAP) {
+ if (pbpctl_dev->bp_caps_ex & TPL2_CAP_EX)
+ return tpl2_flag_status(pbpctl_dev);
+ ret = pbpctl_dev->bp_tpl_flag;
+ }
+ return ret;
+}
+
+int set_bp_wait_at_pwup_fn(bpctl_dev_t *pbpctl_dev, int tap_mode)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ /* bp_lock(pbp_device_block); */
+ cmnd_on(pbpctl_dev);
+ if (!tap_mode)
+ bp_wait_at_pwup_dis(pbpctl_dev);
+ else
+ bp_wait_at_pwup_en(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+
+ /* bp_unlock(pbp_device_block); */
+ return BP_OK;
+ }
+ return BP_NOT_CAP;
+}
+
+int get_bp_wait_at_pwup_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ /* bp_lock(pbp_device_block); */
+ ret = bp_wait_at_pwup_status(pbpctl_dev);
+ /* bp_unlock(pbp_device_block); */
+
+ return ret;
+}
+
+int set_bp_hw_reset_fn(bpctl_dev_t *pbpctl_dev, int tap_mode)
+{
+ if (!pbpctl_dev)
+ return -1;
+
+ if (pbpctl_dev->bp_caps & SW_CTL_CAP) {
+ /* bp_lock(pbp_device_block); */
+ cmnd_on(pbpctl_dev);
+
+ if (!tap_mode)
+ bp_hw_reset_dis(pbpctl_dev);
+ else
+ bp_hw_reset_en(pbpctl_dev);
+ cmnd_off(pbpctl_dev);
+ /* bp_unlock(pbp_device_block); */
+ return BP_OK;
+ }
+ return BP_NOT_CAP;
+}
+
+int get_bp_hw_reset_fn(bpctl_dev_t *pbpctl_dev)
+{
+ int ret = 0;
+ if (!pbpctl_dev)
+ return -1;
+
+ /* bp_lock(pbp_device_block); */
+ ret = bp_hw_reset_status(pbpctl_dev);
+
+ /* bp_unlock(pbp_device_block); */
+
+ return ret;
+}
+
+
+int get_bypass_info_fn(bpctl_dev_t *pbpctl_dev, char *dev_name,
+ char *add_param)
+{
+ if (!pbpctl_dev)
+ return -1;
+ if (!is_bypass_fn(pbpctl_dev))
+ return -1;
+ strcpy(dev_name, pbpctl_dev->name);
+ *add_param = pbpctl_dev->bp_fw_ver;
+ return 0;
+}
+
+int get_dev_idx_bsf(int bus, int slot, int func)
+{
+ int idx_dev = 0;
+ for (idx_dev = 0;
+ ((bpctl_dev_arr[idx_dev].pdev != NULL) && (idx_dev < device_num));
+ idx_dev++) {
+ if ((bus == bpctl_dev_arr[idx_dev].bus)
+ && (slot == bpctl_dev_arr[idx_dev].slot)
+ && (func == bpctl_dev_arr[idx_dev].func))
+
+ return idx_dev;
+ }
+ return -1;
+}
+
+static void str_low(char *str)
+{
+ int i;
+
+ for (i = 0; i < strlen(str); i++)
+ if ((str[i] >= 65) && (str[i] <= 90))
+ str[i] += 32;
+}
+
+static unsigned long str_to_hex(char *p)
+{
+ unsigned long hex = 0;
+ unsigned long length = strlen(p), shift = 0;
+ unsigned char dig = 0;
+
+ str_low(p);
+ length = strlen(p);
+
+ if (length == 0)
+ return 0;
+
+ do {
+ dig = p[--length];
+ dig = dig < 'a' ? (dig - '0') : (dig - 'a' + 0xa);
+ hex |= (dig << shift);
+ shift += 4;
+ } while (length);
+ return hex;
+}
+
+static int get_dev_idx(int ifindex)
+{
+ int idx_dev = 0;
+
+ for (idx_dev = 0;
+ ((bpctl_dev_arr[idx_dev].pdev != NULL) && (idx_dev < device_num));
+ idx_dev++) {
+ if (ifindex == bpctl_dev_arr[idx_dev].ifindex)
+ return idx_dev;
+ }
+
+ return -1;
+}
+
+static bpctl_dev_t *get_dev_idx_p(int ifindex)
+{
+ int idx_dev = 0;
+
+ for (idx_dev = 0;
+ ((bpctl_dev_arr[idx_dev].pdev != NULL) && (idx_dev < device_num));
+ idx_dev++) {
+ if (ifindex == bpctl_dev_arr[idx_dev].ifindex)
+ return &bpctl_dev_arr[idx_dev];
+ }
+
+ return NULL;
+}
+
+static void if_scan_init(void)
+{
+ int idx_dev = 0;
+ struct net_device *dev;
+ int ifindex;
+ /* rcu_read_lock(); */
+ /* rtnl_lock(); */
+ /* rcu_read_lock(); */
+#if 1
+#if (LINUX_VERSION_CODE >= 0x020618)
+ for_each_netdev(&init_net, dev)
+#elif (LINUX_VERSION_CODE >= 0x20616)
+ for_each_netdev(dev)
+#else
+ for (dev = dev_base; dev; dev = dev->next)
+#endif
+ {
+
+ struct ethtool_drvinfo drvinfo;
+ char cbuf[32];
+ char *buf = NULL;
+ char res[10];
+ int i = 0;
+ int bus = 0, slot = 0, func = 0;
+ ifindex = dev->ifindex;
+
+ memset(res, 0, 10);
+ memset(&drvinfo, 0, sizeof(struct ethtool_drvinfo));
+
+ if (dev->ethtool_ops && dev->ethtool_ops->get_drvinfo) {
+ memset(&drvinfo, 0, sizeof(drvinfo));
+ dev->ethtool_ops->get_drvinfo(dev, &drvinfo);
+ } else
+ continue;
+ if (!drvinfo.bus_info)
+ continue;
+ if (!strcmp(drvinfo.bus_info, "N/A"))
+ continue;
+ memcpy(&cbuf, drvinfo.bus_info, 32);
+ buf = &cbuf[0];
+
+ while (*buf++ != ':') ;
+ for (i = 0; i < 10; i++, buf++) {
+ if (*buf == ':')
+ break;
+ res[i] = *buf;
+
+ }
+ buf++;
+ bus = str_to_hex(res);
+ memset(res, 0, 10);
+
+ for (i = 0; i < 10; i++, buf++) {
+ if (*buf == '.')
+ break;
+ res[i] = *buf;
+
+ }
+ buf++;
+ slot = str_to_hex(res);
+ func = str_to_hex(buf);
+ idx_dev = get_dev_idx_bsf(bus, slot, func);
+
+ if (idx_dev != -1) {
+
+ bpctl_dev_arr[idx_dev].ifindex = ifindex;
+ bpctl_dev_arr[idx_dev].ndev = dev;
+
+ }
+
+ }
+#endif
+ /* rtnl_unlock(); */
+ /* rcu_read_unlock(); */
+
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30))
+static int device_ioctl(struct inode *inode, /* see include/linux/fs.h */
+ struct file *file, /* ditto */
+ unsigned int ioctl_num, /* number and param for ioctl */
+ unsigned long ioctl_param)
+#else
+static long device_ioctl(struct file *file, /* ditto */
+ unsigned int ioctl_num, /* number and param for ioctl */
+ unsigned long ioctl_param)
+#endif
+{
+ struct bpctl_cmd bpctl_cmd;
+ int dev_idx = 0;
+ bpctl_dev_t *pbpctl_dev_out;
+ void __user *argp = (void __user *)ioctl_param;
+ int ret = 0;
+ unsigned long flags;
+
+ static bpctl_dev_t *pbpctl_dev;
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30))
+ /* lock_kernel(); */
+#endif
+ lock_bpctl();
+ /* local_irq_save(flags); */
+ /* if(!spin_trylock_irqsave(&bpvm_lock)){
+ local_irq_restore(flags);
+ unlock_bpctl();
+ unlock_kernel();
+ return -1;
+ } */
+ /* spin_lock_irqsave(&bpvm_lock, flags); */
+
+/*
+* Switch according to the ioctl called
+*/
+ if (ioctl_num == IOCTL_TX_MSG(IF_SCAN)) {
+ if_scan_init();
+ ret = SUCCESS;
+ goto bp_exit;
+ }
+ if (copy_from_user(&bpctl_cmd, argp, sizeof(struct bpctl_cmd))) {
+
+ ret = -EFAULT;
+ goto bp_exit;
+ }
+
+ if (ioctl_num == IOCTL_TX_MSG(GET_DEV_NUM)) {
+ bpctl_cmd.out_param[0] = device_num;
+ if (copy_to_user
+ (argp, (void *)&bpctl_cmd, sizeof(struct bpctl_cmd))) {
+ ret = -EFAULT;
+ goto bp_exit;
+ }
+ ret = SUCCESS;
+ goto bp_exit;
+
+ }
+ /* lock_bpctl(); */
+ /* preempt_disable(); */
+ local_irq_save(flags);
+ if (!spin_trylock(&bpvm_lock)) {
+ local_irq_restore(flags);
+ unlock_bpctl();
+ return -1;
+ }
+
+/* preempt_disable();
+ rcu_read_lock();
+ spin_lock_irqsave(&bpvm_lock, flags);
+*/
+ if ((bpctl_cmd.in_param[5]) ||
+ (bpctl_cmd.in_param[6]) || (bpctl_cmd.in_param[7]))
+ dev_idx = get_dev_idx_bsf(bpctl_cmd.in_param[5],
+ bpctl_cmd.in_param[6],
+ bpctl_cmd.in_param[7]);
+ else if (bpctl_cmd.in_param[1] == 0)
+ dev_idx = bpctl_cmd.in_param[0];
+ else
+ dev_idx = get_dev_idx(bpctl_cmd.in_param[1]);
+
+ if (dev_idx < 0 || dev_idx > device_num) {
+ /* unlock_bpctl();
+ preempt_enable(); */
+ ret = -EOPNOTSUPP;
+ /* preempt_enable();
+ rcu_read_unlock(); */
+ spin_unlock_irqrestore(&bpvm_lock, flags);
+ goto bp_exit;
+ }
+
+ bpctl_cmd.out_param[0] = bpctl_dev_arr[dev_idx].bus;
+ bpctl_cmd.out_param[1] = bpctl_dev_arr[dev_idx].slot;
+ bpctl_cmd.out_param[2] = bpctl_dev_arr[dev_idx].func;
+ bpctl_cmd.out_param[3] = bpctl_dev_arr[dev_idx].ifindex;
+
+ if ((bpctl_dev_arr[dev_idx].bp_10gb)
+ && (!(bpctl_dev_arr[dev_idx].ifindex))) {
+ printk("Please load network driver for %s adapter!\n",
+ bpctl_dev_arr[dev_idx].name);
+ bpctl_cmd.status = -1;
+ ret = SUCCESS;
+ /* preempt_enable(); */
+ /* rcu_read_unlock(); */
+ spin_unlock_irqrestore(&bpvm_lock, flags);
+ goto bp_exit;
+
+ }
+ if ((bpctl_dev_arr[dev_idx].bp_10gb) && (bpctl_dev_arr[dev_idx].ndev)) {
+ if (!(bpctl_dev_arr[dev_idx].ndev->flags & IFF_UP)) {
+ if (!(bpctl_dev_arr[dev_idx].ndev->flags & IFF_UP)) {
+ printk
+ ("Please bring up network interfaces for %s adapter!\n",
+ bpctl_dev_arr[dev_idx].name);
+ bpctl_cmd.status = -1;
+ ret = SUCCESS;
+ /* preempt_enable(); */
+ /* rcu_read_unlock(); */
+ spin_unlock_irqrestore(&bpvm_lock, flags);
+ goto bp_exit;
+ }
+
+ }
+ }
+
+ if ((dev_idx < 0) || (dev_idx > device_num)
+ || (bpctl_dev_arr[dev_idx].pdev == NULL)) {
+ bpctl_cmd.status = -1;
+ goto bpcmd_exit;
+ }
+
+ pbpctl_dev = &bpctl_dev_arr[dev_idx];
+
+ switch (ioctl_num) {
+ case IOCTL_TX_MSG(SET_BYPASS_PWOFF):
+ bpctl_cmd.status =
+ set_bypass_pwoff_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_BYPASS_PWOFF):
+ bpctl_cmd.status = get_bypass_pwoff_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(SET_BYPASS_PWUP):
+ bpctl_cmd.status =
+ set_bypass_pwup_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_BYPASS_PWUP):
+ bpctl_cmd.status = get_bypass_pwup_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(SET_BYPASS_WD):
+ bpctl_cmd.status =
+ set_bypass_wd_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_BYPASS_WD):
+ bpctl_cmd.status =
+ get_bypass_wd_fn(pbpctl_dev, (int *)&(bpctl_cmd.data[0]));
+ break;
+
+ case IOCTL_TX_MSG(GET_WD_EXPIRE_TIME):
+ bpctl_cmd.status =
+ get_wd_expire_time_fn(pbpctl_dev,
+ (int *)&(bpctl_cmd.data[0]));
+ break;
+
+ case IOCTL_TX_MSG(RESET_BYPASS_WD_TIMER):
+ bpctl_cmd.status = reset_bypass_wd_timer_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(GET_WD_SET_CAPS):
+ bpctl_cmd.status = get_wd_set_caps_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(SET_STD_NIC):
+ bpctl_cmd.status =
+ set_std_nic_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_STD_NIC):
+ bpctl_cmd.status = get_std_nic_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(SET_TAP):
+ bpctl_cmd.status =
+ set_tap_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_TAP):
+ bpctl_cmd.status = get_tap_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(GET_TAP_CHANGE):
+ bpctl_cmd.status = get_tap_change_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(SET_DIS_TAP):
+ bpctl_cmd.status =
+ set_dis_tap_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_DIS_TAP):
+ bpctl_cmd.status = get_dis_tap_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(SET_TAP_PWUP):
+ bpctl_cmd.status =
+ set_tap_pwup_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_TAP_PWUP):
+ bpctl_cmd.status = get_tap_pwup_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(SET_WD_EXP_MODE):
+ bpctl_cmd.status =
+ set_wd_exp_mode_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_WD_EXP_MODE):
+ bpctl_cmd.status = get_wd_exp_mode_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(GET_DIS_BYPASS):
+ bpctl_cmd.status = get_dis_bypass_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(SET_DIS_BYPASS):
+ bpctl_cmd.status =
+ set_dis_bypass_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_BYPASS_CHANGE):
+ bpctl_cmd.status = get_bypass_change_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(GET_BYPASS):
+ bpctl_cmd.status = get_bypass_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(SET_BYPASS):
+ bpctl_cmd.status =
+ set_bypass_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_BYPASS_CAPS):
+ bpctl_cmd.status = get_bypass_caps_fn(pbpctl_dev);
+ /*preempt_enable(); */
+ /*rcu_read_unlock();*/
+ spin_unlock_irqrestore(&bpvm_lock, flags);
+ if (copy_to_user
+ (argp, (void *)&bpctl_cmd, sizeof(struct bpctl_cmd))) {
+ /*unlock_bpctl(); */
+ /*preempt_enable(); */
+ ret = -EFAULT;
+ goto bp_exit;
+ }
+ goto bp_exit;
+
+ case IOCTL_TX_MSG(GET_BYPASS_SLAVE):
+ bpctl_cmd.status =
+ get_bypass_slave_fn(pbpctl_dev, &pbpctl_dev_out);
+ if (bpctl_cmd.status == 1) {
+ bpctl_cmd.out_param[4] = pbpctl_dev_out->bus;
+ bpctl_cmd.out_param[5] = pbpctl_dev_out->slot;
+ bpctl_cmd.out_param[6] = pbpctl_dev_out->func;
+ bpctl_cmd.out_param[7] = pbpctl_dev_out->ifindex;
+ }
+ break;
+
+ case IOCTL_TX_MSG(IS_BYPASS):
+ bpctl_cmd.status = is_bypass(pbpctl_dev);
+ break;
+ case IOCTL_TX_MSG(SET_TX):
+ bpctl_cmd.status = set_tx_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+ case IOCTL_TX_MSG(GET_TX):
+ bpctl_cmd.status = get_tx_fn(pbpctl_dev);
+ break;
+ case IOCTL_TX_MSG(SET_WD_AUTORESET):
+ bpctl_cmd.status =
+ set_wd_autoreset_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+
+ break;
+ case IOCTL_TX_MSG(GET_WD_AUTORESET):
+
+ bpctl_cmd.status = get_wd_autoreset_fn(pbpctl_dev);
+ break;
+ case IOCTL_TX_MSG(SET_DISC):
+ bpctl_cmd.status =
+ set_disc_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+ case IOCTL_TX_MSG(GET_DISC):
+ bpctl_cmd.status = get_disc_fn(pbpctl_dev);
+ break;
+ case IOCTL_TX_MSG(GET_DISC_CHANGE):
+ bpctl_cmd.status = get_disc_change_fn(pbpctl_dev);
+ break;
+ case IOCTL_TX_MSG(SET_DIS_DISC):
+ bpctl_cmd.status =
+ set_dis_disc_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+ case IOCTL_TX_MSG(GET_DIS_DISC):
+ bpctl_cmd.status = get_dis_disc_fn(pbpctl_dev);
+ break;
+ case IOCTL_TX_MSG(SET_DISC_PWUP):
+ bpctl_cmd.status =
+ set_disc_pwup_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+ case IOCTL_TX_MSG(GET_DISC_PWUP):
+ bpctl_cmd.status = get_disc_pwup_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(GET_BYPASS_INFO):
+
+ bpctl_cmd.status =
+ get_bypass_info_fn(pbpctl_dev, (char *)&bpctl_cmd.data,
+ (char *)&bpctl_cmd.out_param[4]);
+ break;
+
+ case IOCTL_TX_MSG(SET_TPL):
+ bpctl_cmd.status =
+ set_tpl_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_TPL):
+ bpctl_cmd.status = get_tpl_fn(pbpctl_dev);
+ break;
+ case IOCTL_TX_MSG(SET_BP_WAIT_AT_PWUP):
+ bpctl_cmd.status =
+ set_bp_wait_at_pwup_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_BP_WAIT_AT_PWUP):
+ bpctl_cmd.status = get_bp_wait_at_pwup_fn(pbpctl_dev);
+ break;
+ case IOCTL_TX_MSG(SET_BP_HW_RESET):
+ bpctl_cmd.status =
+ set_bp_hw_reset_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_BP_HW_RESET):
+ bpctl_cmd.status = get_bp_hw_reset_fn(pbpctl_dev);
+ break;
+#ifdef BP_SELF_TEST
+ case IOCTL_TX_MSG(SET_BP_SELF_TEST):
+ bpctl_cmd.status =
+ set_bp_self_test_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+
+ break;
+ case IOCTL_TX_MSG(GET_BP_SELF_TEST):
+ bpctl_cmd.status = get_bp_self_test_fn(pbpctl_dev);
+ break;
+
+#endif
+#if 0
+ case IOCTL_TX_MSG(SET_DISC_PORT):
+ bpctl_cmd.status =
+ set_disc_port_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_DISC_PORT):
+ bpctl_cmd.status = get_disc_port_fn(pbpctl_dev);
+ break;
+
+ case IOCTL_TX_MSG(SET_DISC_PORT_PWUP):
+ bpctl_cmd.status =
+ set_disc_port_pwup_fn(pbpctl_dev, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_DISC_PORT_PWUP):
+ bpctl_cmd.status = get_disc_port_pwup_fn(pbpctl_dev);
+ break;
+#endif
+ case IOCTL_TX_MSG(SET_BP_FORCE_LINK):
+ bpctl_cmd.status =
+ set_bp_force_link_fn(dev_idx, bpctl_cmd.in_param[2]);
+ break;
+
+ case IOCTL_TX_MSG(GET_BP_FORCE_LINK):
+ bpctl_cmd.status = get_bp_force_link_fn(dev_idx);
+ break;
+
+ default:
+ /* unlock_bpctl(); */
+
+ ret = -EOPNOTSUPP;
+ /* preempt_enable(); */
+ /* rcu_read_unlock();*/
+ spin_unlock_irqrestore(&bpvm_lock, flags);
+ goto bp_exit;
+ }
+ /* unlock_bpctl(); */
+ /* preempt_enable(); */
+ bpcmd_exit:
+ /* rcu_read_unlock(); */
+ spin_unlock_irqrestore(&bpvm_lock, flags);
+ if (copy_to_user(argp, (void *)&bpctl_cmd, sizeof(struct bpctl_cmd)))
+ ret = -EFAULT;
+ ret = SUCCESS;
+ bp_exit:
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30))
+ /* unlock_kernel(); */
+#endif
+ /* spin_unlock_irqrestore(&bpvm_lock, flags); */
+ unlock_bpctl();
+ /* unlock_kernel(); */
+ return ret;
+}
+
+struct file_operations Fops = {
+ .owner = THIS_MODULE,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30))
+ .ioctl = device_ioctl,
+#else
+ .unlocked_ioctl = device_ioctl,
+#endif
+
+ .open = device_open,
+ .release = device_release, /* a.k.a. close */
+};
+
+#ifndef PCI_DEVICE
+#define PCI_DEVICE(vend,dev) \
+ .vendor = (vend), .device = (dev), \
+ .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID
+#endif
+
+#define SILICOM_E1000BP_ETHERNET_DEVICE(device_id) {\
+ PCI_DEVICE(SILICOM_VID, device_id)}
+
+typedef enum {
+ PXG2BPFI,
+ PXG2BPFIL,
+ PXG2BPFILX,
+ PXG2BPFILLX,
+ PXGBPI,
+ PXGBPIG,
+ PXG2TBFI,
+ PXG4BPI,
+ PXG4BPFI,
+ PEG4BPI,
+ PEG2BPI,
+ PEG4BPIN,
+ PEG2BPFI,
+ PEG2BPFILX,
+ PMCXG2BPFI,
+ PMCXG2BPFIN,
+ PEG4BPII,
+ PEG4BPFII,
+ PXG4BPFILX,
+ PMCXG2BPIN,
+ PMCXG4BPIN,
+ PXG2BISC1,
+ PEG2TBFI,
+ PXG2TBI,
+ PXG4BPFID,
+ PEG4BPFI,
+ PEG4BPIPT,
+ PXG6BPI,
+ PEG4BPIL,
+ PMCXG2BPIN2,
+ PMCXG4BPIN2,
+ PMCX2BPI,
+ PEG2BPFID,
+ PEG2BPFIDLX,
+ PMCX4BPI,
+ MEG2BPFILN,
+ MEG2BPFINX,
+ PEG4BPFILX,
+ PE10G2BPISR,
+ PE10G2BPILR,
+ MHIO8AD,
+ PE10G2BPICX4,
+ PEG2BPI5,
+ PEG6BPI,
+ PEG4BPFI5,
+ PEG4BPFI5LX,
+ MEG2BPFILXLN,
+ PEG2BPIX1,
+ MEG2BPFILXNX,
+ XE10G2BPIT,
+ XE10G2BPICX4,
+ XE10G2BPISR,
+ XE10G2BPILR,
+ PEG4BPIIO,
+ XE10G2BPIXR,
+ PE10GDBISR,
+ PE10GDBILR,
+ PEG2BISC6,
+ PEG6BPIFC,
+ PE10G2BPTCX4,
+ PE10G2BPTSR,
+ PE10G2BPTLR,
+ PE10G2BPTT,
+ PEG4BPI6,
+ PEG4BPFI6,
+ PEG4BPFI6LX,
+ PEG4BPFI6ZX,
+ PEG2BPI6,
+ PEG2BPFI6,
+ PEG2BPFI6LX,
+ PEG2BPFI6ZX,
+ PEG2BPFI6FLXM,
+ PEG4BPI6FC,
+ PEG4BPFI6FC,
+ PEG4BPFI6FCLX,
+ PEG4BPFI6FCZX,
+ PEG6BPI6,
+ PEG2BPI6SC6,
+ MEG2BPI6,
+ XEG2BPI6,
+ MEG4BPI6,
+ PEG2BPFI5,
+ PEG2BPFI5LX,
+ PXEG4BPFI,
+ M1EG2BPI6,
+ M1EG2BPFI6,
+ M1EG2BPFI6LX,
+ M1EG2BPFI6ZX,
+ M1EG4BPI6,
+ M1EG4BPFI6,
+ M1EG4BPFI6LX,
+ M1EG4BPFI6ZX,
+ M1EG6BPI6,
+ M1E2G4BPi80,
+ M1E2G4BPFi80,
+ M1E2G4BPFi80LX,
+ M1E2G4BPFi80ZX,
+ PE210G2SPI9,
+ M1E10G2BPI9CX4,
+ M1E10G2BPI9SR,
+ M1E10G2BPI9LR,
+ M1E10G2BPI9T,
+ PE210G2BPI9CX4,
+ PE210G2BPI9SR,
+ PE210G2BPI9LR,
+ PE210G2BPI9T,
+ M2EG2BPFI6,
+ M2EG2BPFI6LX,
+ M2EG2BPFI6ZX,
+ M2EG4BPI6,
+ M2EG4BPFI6,
+ M2EG4BPFI6LX,
+ M2EG4BPFI6ZX,
+ M2EG6BPI6,
+ PEG2DBI6,
+ PEG2DBFI6,
+ PEG2DBFI6LX,
+ PEG2DBFI6ZX,
+ PE2G4BPi80,
+ PE2G4BPFi80,
+ PE2G4BPFi80LX,
+ PE2G4BPFi80ZX,
+ PE2G4BPi80L,
+ M6E2G8BPi80A,
+
+ PE2G2BPi35,
+ PAC1200BPi35,
+ PE2G2BPFi35,
+ PE2G2BPFi35LX,
+ PE2G2BPFi35ZX,
+ PE2G4BPi35,
+ PE2G4BPi35L,
+ PE2G4BPFi35,
+ PE2G4BPFi35LX,
+ PE2G4BPFi35ZX,
+
+ PE2G6BPi35,
+ PE2G6BPi35CX,
+
+ PE2G2BPi80,
+ PE2G2BPFi80,
+ PE2G2BPFi80LX,
+ PE2G2BPFi80ZX,
+ M2E10G2BPI9CX4,
+ M2E10G2BPI9SR,
+ M2E10G2BPI9LR,
+ M2E10G2BPI9T,
+ M6E2G8BPi80,
+ PE210G2DBi9SR,
+ PE210G2DBi9SRRB,
+ PE210G2DBi9LR,
+ PE210G2DBi9LRRB,
+ PE310G4DBi940SR,
+ PE310G4BPi9T,
+ PE310G4BPi9SR,
+ PE310G4BPi9LR,
+ PE210G2BPi40,
+} board_t;
+
+typedef struct _bpmod_info_t {
+ unsigned int vendor;
+ unsigned int device;
+ unsigned int subvendor;
+ unsigned int subdevice;
+ unsigned int index;
+ char *bp_name;
+
+} bpmod_info_t;
+
+typedef struct _dev_desc {
+ char *name;
+} dev_desc_t;
+
+dev_desc_t dev_desc[] = {
+ {"Silicom Bypass PXG2BPFI-SD series adapter"},
+ {"Silicom Bypass PXG2BPFIL-SD series adapter"},
+ {"Silicom Bypass PXG2BPFILX-SD series adapter"},
+ {"Silicom Bypass PXG2BPFILLX-SD series adapter"},
+ {"Silicom Bypass PXG2BPI-SD series adapter"},
+ {"Silicom Bypass PXG2BPIG-SD series adapter"},
+ {"Silicom Bypass PXG2TBFI-SD series adapter"},
+ {"Silicom Bypass PXG4BPI-SD series adapter"},
+ {"Silicom Bypass PXG4BPFI-SD series adapter"},
+ {"Silicom Bypass PEG4BPI-SD series adapter"},
+ {"Silicom Bypass PEG2BPI-SD series adapter"},
+ {"Silicom Bypass PEG4BPIN-SD series adapter"},
+ {"Silicom Bypass PEG2BPFI-SD series adapter"},
+ {"Silicom Bypass PEG2BPFI-LX-SD series adapter"},
+ {"Silicom Bypass PMCX2BPFI-SD series adapter"},
+ {"Silicom Bypass PMCX2BPFI-N series adapter"},
+ {"Intel Bypass PEG2BPII series adapter"},
+ {"Intel Bypass PEG2BPFII series adapter"},
+ {"Silicom Bypass PXG4BPFILX-SD series adapter"},
+ {"Silicom Bypass PMCX2BPI-N series adapter"},
+ {"Silicom Bypass PMCX4BPI-N series adapter"},
+ {"Silicom Bypass PXG2BISC1-SD series adapter"},
+ {"Silicom Bypass PEG2TBFI-SD series adapter"},
+ {"Silicom Bypass PXG2TBI-SD series adapter"},
+ {"Silicom Bypass PXG4BPFID-SD series adapter"},
+ {"Silicom Bypass PEG4BPFI-SD series adapter"},
+ {"Silicom Bypass PEG4BPIPT-SD series adapter"},
+ {"Silicom Bypass PXG6BPI-SD series adapter"},
+ {"Silicom Bypass PEG4BPIL-SD series adapter"},
+ {"Silicom Bypass PMCX2BPI-N2 series adapter"},
+ {"Silicom Bypass PMCX4BPI-N2 series adapter"},
+ {"Silicom Bypass PMCX2BPI-SD series adapter"},
+ {"Silicom Bypass PEG2BPFID-SD series adapter"},
+ {"Silicom Bypass PEG2BPFIDLX-SD series adapter"},
+ {"Silicom Bypass PMCX4BPI-SD series adapter"},
+ {"Silicom Bypass MEG2BPFILN-SD series adapter"},
+ {"Silicom Bypass MEG2BPFINX-SD series adapter"},
+ {"Silicom Bypass PEG4BPFILX-SD series adapter"},
+ {"Silicom Bypass PE10G2BPISR-SD series adapter"},
+ {"Silicom Bypass PE10G2BPILR-SD series adapter"},
+ {"Silicom Bypass MHIO8AD-SD series adapter"},
+ {"Silicom Bypass PE10G2BPICX4-SD series adapter"},
+ {"Silicom Bypass PEG2BPI5-SD series adapter"},
+ {"Silicom Bypass PEG6BPI5-SD series adapter"},
+ {"Silicom Bypass PEG4BPFI5-SD series adapter"},
+ {"Silicom Bypass PEG4BPFI5LX-SD series adapter"},
+ {"Silicom Bypass MEG2BPFILXLN-SD series adapter"},
+ {"Silicom Bypass PEG2BPIX1-SD series adapter"},
+ {"Silicom Bypass MEG2BPFILXNX-SD series adapter"},
+ {"Silicom Bypass XE10G2BPIT-SD series adapter"},
+ {"Silicom Bypass XE10G2BPICX4-SD series adapter"},
+ {"Silicom Bypass XE10G2BPISR-SD series adapter"},
+ {"Silicom Bypass XE10G2BPILR-SD series adapter"},
+ {"Intel Bypass PEG2BPFII0 series adapter"},
+ {"Silicom Bypass XE10G2BPIXR series adapter"},
+ {"Silicom Bypass PE10G2DBISR series adapter"},
+ {"Silicom Bypass PEG2BI5SC6 series adapter"},
+ {"Silicom Bypass PEG6BPI5FC series adapter"},
+
+ {"Silicom Bypass PE10G2BPTCX4 series adapter"},
+ {"Silicom Bypass PE10G2BPTSR series adapter"},
+ {"Silicom Bypass PE10G2BPTLR series adapter"},
+ {"Silicom Bypass PE10G2BPTT series adapter"},
+ {"Silicom Bypass PEG4BPI6 series adapter"},
+ {"Silicom Bypass PEG4BPFI6 series adapter"},
+ {"Silicom Bypass PEG4BPFI6LX series adapter"},
+ {"Silicom Bypass PEG4BPFI6ZX series adapter"},
+ {"Silicom Bypass PEG2BPI6 series adapter"},
+ {"Silicom Bypass PEG2BPFI6 series adapter"},
+ {"Silicom Bypass PEG2BPFI6LX series adapter"},
+ {"Silicom Bypass PEG2BPFI6ZX series adapter"},
+ {"Silicom Bypass PEG2BPFI6FLXM series adapter"},
+ {"Silicom Bypass PEG4BPI6FC series adapter"},
+ {"Silicom Bypass PEG4BPFI6FC series adapter"},
+ {"Silicom Bypass PEG4BPFI6FCLX series adapter"},
+ {"Silicom Bypass PEG4BPFI6FCZX series adapter"},
+ {"Silicom Bypass PEG6BPI6 series adapter"},
+ {"Silicom Bypass PEG2BPI6SC6 series adapter"},
+ {"Silicom Bypass MEG2BPI6 series adapter"},
+ {"Silicom Bypass XEG2BPI6 series adapter"},
+ {"Silicom Bypass MEG4BPI6 series adapter"},
+ {"Silicom Bypass PEG2BPFI5-SD series adapter"},
+ {"Silicom Bypass PEG2BPFI5LX-SD series adapter"},
+ {"Silicom Bypass PXEG4BPFI-SD series adapter"},
+ {"Silicom Bypass MxEG2BPI6 series adapter"},
+ {"Silicom Bypass MxEG2BPFI6 series adapter"},
+ {"Silicom Bypass MxEG2BPFI6LX series adapter"},
+ {"Silicom Bypass MxEG2BPFI6ZX series adapter"},
+ {"Silicom Bypass MxEG4BPI6 series adapter"},
+ {"Silicom Bypass MxEG4BPFI6 series adapter"},
+ {"Silicom Bypass MxEG4BPFI6LX series adapter"},
+ {"Silicom Bypass MxEG4BPFI6ZX series adapter"},
+ {"Silicom Bypass MxEG6BPI6 series adapter"},
+ {"Silicom Bypass MxE2G4BPi80 series adapter"},
+ {"Silicom Bypass MxE2G4BPFi80 series adapter"},
+ {"Silicom Bypass MxE2G4BPFi80LX series adapter"},
+ {"Silicom Bypass MxE2G4BPFi80ZX series adapter"},
+
+ {"Silicom Bypass PE210G2SPI9 series adapter"},
+
+ {"Silicom Bypass MxE210G2BPI9CX4 series adapter"},
+ {"Silicom Bypass MxE210G2BPI9SR series adapter"},
+ {"Silicom Bypass MxE210G2BPI9LR series adapter"},
+ {"Silicom Bypass MxE210G2BPI9T series adapter"},
+
+ {"Silicom Bypass PE210G2BPI9CX4 series adapter"},
+ {"Silicom Bypass PE210G2BPI9SR series adapter"},
+ {"Silicom Bypass PE210G2BPI9LR series adapter"},
+ {"Silicom Bypass PE210G2BPI9T series adapter"},
+
+ {"Silicom Bypass M2EG2BPFI6 series adapter"},
+ {"Silicom Bypass M2EG2BPFI6LX series adapter"},
+ {"Silicom Bypass M2EG2BPFI6ZX series adapter"},
+ {"Silicom Bypass M2EG4BPI6 series adapter"},
+ {"Silicom Bypass M2EG4BPFI6 series adapter"},
+ {"Silicom Bypass M2EG4BPFI6LX series adapter"},
+ {"Silicom Bypass M2EG4BPFI6ZX series adapter"},
+ {"Silicom Bypass M2EG6BPI6 series adapter"},
+
+ {"Silicom Bypass PEG2DBI6 series adapter"},
+ {"Silicom Bypass PEG2DBFI6 series adapter"},
+ {"Silicom Bypass PEG2DBFI6LX series adapter"},
+ {"Silicom Bypass PEG2DBFI6ZX series adapter"},
+
+ {"Silicom Bypass PE2G4BPi80 series adapter"},
+ {"Silicom Bypass PE2G4BPFi80 series adapter"},
+ {"Silicom Bypass PE2G4BPFi80LX series adapter"},
+ {"Silicom Bypass PE2G4BPFi80ZX series adapter"},
+
+ {"Silicom Bypass PE2G4BPi80L series adapter"},
+ {"Silicom Bypass MxE2G8BPi80A series adapter"},
+
+ {"Silicom Bypass PE2G2BPi35 series adapter"},
+ {"Silicom Bypass PAC1200BPi35 series adapter"},
+ {"Silicom Bypass PE2G2BPFi35 series adapter"},
+ {"Silicom Bypass PE2G2BPFi35LX series adapter"},
+ {"Silicom Bypass PE2G2BPFi35ZX series adapter"},
+
+ {"Silicom Bypass PE2G4BPi35 series adapter"},
+ {"Silicom Bypass PE2G4BPi35L series adapter"},
+ {"Silicom Bypass PE2G4BPFi35 series adapter"},
+ {"Silicom Bypass PE2G4BPFi35LX series adapter"},
+ {"Silicom Bypass PE2G4BPFi35ZX series adapter"},
+
+ {"Silicom Bypass PE2G6BPi35 series adapter"},
+ {"Silicom Bypass PE2G6BPi35CX series adapter"},
+
+ {"Silicom Bypass PE2G2BPi80 series adapter"},
+ {"Silicom Bypass PE2G2BPFi80 series adapter"},
+ {"Silicom Bypass PE2G2BPFi80LX series adapter"},
+ {"Silicom Bypass PE2G2BPFi80ZX series adapter"},
+
+ {"Silicom Bypass M2E10G2BPI9CX4 series adapter"},
+ {"Silicom Bypass M2E10G2BPI9SR series adapter"},
+ {"Silicom Bypass M2E10G2BPI9LR series adapter"},
+ {"Silicom Bypass M2E10G2BPI9T series adapter"},
+ {"Silicom Bypass MxE2G8BPi80 series adapter"},
+ {"Silicom Bypass PE210G2DBi9SR series adapter"},
+ {"Silicom Bypass PE210G2DBi9SRRB series adapter"},
+ {"Silicom Bypass PE210G2DBi9LR series adapter"},
+ {"Silicom Bypass PE210G2DBi9LRRB series adapter"},
+ {"Silicom Bypass PE310G4DBi9-SR series adapter"},
+ {"Silicom Bypass PE310G4BPi9T series adapter"},
+ {"Silicom Bypass PE310G4BPi9SR series adapter"},
+ {"Silicom Bypass PE310G4BPi9LR series adapter"},
+ {"Silicom Bypass PE210G2BPi40T series adapter"},
+ {0},
+};
+
+static bpmod_info_t tx_ctl_pci_tbl[] = {
+ {0x8086, 0x107a, SILICOM_SVID, SILICOM_PXG2BPFI_SSID, PXG2BPFI,
+ "PXG2BPFI-SD"},
+ {0x8086, 0x107a, SILICOM_SVID, SILICOM_PXG2BPFIL_SSID, PXG2BPFIL,
+ "PXG2BPFIL-SD"},
+ {0x8086, 0x107a, SILICOM_SVID, SILICOM_PXG2BPFILX_SSID, PXG2BPFILX,
+ "PXG2BPFILX-SD"},
+ {0x8086, 0x107a, SILICOM_SVID, SILICOM_PXG2BPFILLX_SSID, PXG2BPFILLX,
+ "PXG2BPFILLXSD"},
+ {0x8086, 0x1010, SILICOM_SVID, SILICOM_PXGBPI_SSID, PXGBPI,
+ "PXG2BPI-SD"},
+ {0x8086, 0x1079, SILICOM_SVID, SILICOM_PXGBPIG_SSID, PXGBPIG,
+ "PXG2BPIG-SD"},
+ {0x8086, 0x107a, SILICOM_SVID, SILICOM_PXG2TBFI_SSID, PXG2TBFI,
+ "PXG2TBFI-SD"},
+ {0x8086, 0x1079, SILICOM_SVID, SILICOM_PXG4BPI_SSID, PXG4BPI,
+ "PXG4BPI-SD"},
+ {0x8086, 0x107a, SILICOM_SVID, SILICOM_PXG4BPFI_SSID, PXG4BPFI,
+ "PXG4BPFI-SD"},
+ {0x8086, 0x107a, SILICOM_SVID, SILICOM_PXG4BPFILX_SSID, PXG4BPFILX,
+ "PXG4BPFILX-SD"},
+ {0x8086, 0x1079, SILICOM_SVID, SILICOM_PEG4BPI_SSID, PEG4BPI,
+ "PEXG4BPI-SD"},
+ {0x8086, 0x105e, SILICOM_SVID, SILICOM_PEG2BPI_SSID, PEG2BPI,
+ "PEG2BPI-SD"},
+ {0x8086, 0x105e, SILICOM_SVID, SILICOM_PEG4BPIN_SSID, PEG4BPIN,
+ "PEG4BPI-SD"},
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_PEG2BPFI_SSID, PEG2BPFI,
+ "PEG2BPFI-SD"},
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_PEG2BPFILX_SSID, PEG2BPFILX,
+ "PEG2BPFILX-SD"},
+ {0x8086, 0x107a, SILICOM_SVID, SILICOM_PMCXG2BPFI_SSID, PMCXG2BPFI,
+ "PMCX2BPFI-SD"},
+ {0x8086, 0x107a, NOKIA_PMCXG2BPFIN_SVID, NOKIA_PMCXG2BPFIN_SSID,
+ PMCXG2BPFIN, "PMCX2BPFI-N"},
+ {0x8086, INTEL_PEG4BPII_PID, 0x8086, INTEL_PEG4BPII_SSID, PEG4BPII,
+ "PEG4BPII"},
+ {0x8086, INTEL_PEG4BPIIO_PID, 0x8086, INTEL_PEG4BPIIO_SSID, PEG4BPIIO,
+ "PEG4BPII0"},
+ {0x8086, INTEL_PEG4BPFII_PID, 0x8086, INTEL_PEG4BPFII_SSID, PEG4BPFII,
+ "PEG4BPFII"},
+ {0x8086, 0x1079, NOKIA_PMCXG2BPFIN_SVID, NOKIA_PMCXG2BPIN_SSID,
+ PMCXG2BPIN, "PMCX2BPI-N"},
+ {0x8086, 0x1079, NOKIA_PMCXG2BPFIN_SVID, NOKIA_PMCXG4BPIN_SSID,
+ PMCXG4BPIN, "PMCX4BPI-N"},
+ {0x8086, 0x1079, SILICOM_SVID, SILICOM_PXG2BISC1_SSID, PXG2BISC1,
+ "PXG2BISC1-SD"},
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_PEG2TBFI_SSID, PEG2TBFI,
+ "PEG2TBFI-SD"},
+ {0x8086, 0x1079, SILICOM_SVID, SILICOM_PXG2TBI_SSID, PXG2TBI,
+ "PXG2TBI-SD"},
+ {0x8086, 0x107a, SILICOM_SVID, SILICOM_PXG4BPFID_SSID, PXG4BPFID,
+ "PXG4BPFID-SD"},
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_PEG4BPFI_SSID, PEG4BPFI,
+ "PEG4BPFI-SD"},
+ {0x8086, 0x105e, SILICOM_SVID, SILICOM_PEG4BPIPT_SSID, PEG4BPIPT,
+ "PEG4BPIPT-SD"},
+ {0x8086, 0x1079, SILICOM_SVID, SILICOM_PXG6BPI_SSID, PXG6BPI,
+ "PXG6BPI-SD"},
+ {0x8086, 0x10a7, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG4BPIL_SSID /*PCI_ANY_ID */ , PEG4BPIL, "PEG4BPIL-SD"},
+ {0x8086, 0x1079, NOKIA_PMCXG2BPFIN_SVID, NOKIA_PMCXG2BPIN2_SSID,
+ PMCXG2BPIN2, "PMCX2BPI-N2"},
+ {0x8086, 0x1079, NOKIA_PMCXG2BPFIN_SVID, NOKIA_PMCXG4BPIN2_SSID,
+ PMCXG4BPIN2, "PMCX4BPI-N2"},
+ {0x8086, 0x1079, SILICOM_SVID, SILICOM_PMCX2BPI_SSID, PMCX2BPI,
+ "PMCX2BPI-SD"},
+ {0x8086, 0x1079, SILICOM_SVID, SILICOM_PMCX4BPI_SSID, PMCX4BPI,
+ "PMCX4BPI-SD"},
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_PEG2BPFID_SSID, PEG2BPFID,
+ "PEG2BPFID-SD"},
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_PEG2BPFIDLX_SSID, PEG2BPFIDLX,
+ "PEG2BPFIDLXSD"},
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_MEG2BPFILN_SSID, MEG2BPFILN,
+ "MEG2BPFILN-SD"},
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_MEG2BPFINX_SSID, MEG2BPFINX,
+ "MEG2BPFINX-SD"},
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_PEG4BPFILX_SSID, PEG4BPFILX,
+ "PEG4BPFILX-SD"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID, SILICOM_PE10G2BPISR_SSID,
+ PE10G2BPISR, "PE10G2BPISR"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID, SILICOM_PE10G2BPILR_SSID,
+ PE10G2BPILR, "PE10G2BPILR"},
+ {0x8086, 0x10a9, SILICOM_SVID, SILICOM_MHIO8AD_SSID, MHIO8AD,
+ "MHIO8AD-SD"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID, SILICOM_PE10G2BPICX4_SSID,
+ PE10G2BPISR, "PE10G2BPICX4"},
+ {0x8086, 0x10a7, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2BPI5_SSID /*PCI_ANY_ID */ , PEG2BPI5, "PEG2BPI5-SD"},
+ {0x8086, 0x10a7, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG6BPI_SSID /*PCI_ANY_ID */ , PEG6BPI, "PEG6BPI5"},
+ {0x8086, 0x10a9, SILICOM_SVID /*PCI_ANY_ID */ , SILICOM_PEG4BPFI5_SSID,
+ PEG4BPFI5, "PEG4BPFI5"},
+ {0x8086, 0x10a9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG4BPFI5LX_SSID, PEG4BPFI5LX, "PEG4BPFI5LX"},
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_MEG2BPFILXLN_SSID, MEG2BPFILXLN,
+ "MEG2BPFILXLN"},
+ {0x8086, 0x105e, SILICOM_SVID, SILICOM_PEG2BPIX1_SSID, PEG2BPIX1,
+ "PEG2BPIX1-SD"},
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_MEG2BPFILXNX_SSID, MEG2BPFILXNX,
+ "MEG2BPFILXNX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID, SILICOM_XE10G2BPIT_SSID, XE10G2BPIT,
+ "XE10G2BPIT"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID, SILICOM_XE10G2BPICX4_SSID,
+ XE10G2BPICX4, "XE10G2BPICX4"},
+ {0x8086, 0x10C6, SILICOM_SVID, SILICOM_XE10G2BPISR_SSID, XE10G2BPISR,
+ "XE10G2BPISR"},
+ {0x8086, 0x10C6, SILICOM_SVID, SILICOM_XE10G2BPILR_SSID, XE10G2BPILR,
+ "XE10G2BPILR"},
+ {0x8086, 0x10C6, NOKIA_XE10G2BPIXR_SVID, NOKIA_XE10G2BPIXR_SSID,
+ XE10G2BPIXR, "XE10G2BPIXR"},
+ {0x8086, 0x10C6, SILICOM_SVID, SILICOM_PE10GDBISR_SSID, PE10GDBISR,
+ "PE10G2DBISR"},
+ {0x8086, 0x10C6, SILICOM_SVID, SILICOM_PE10GDBILR_SSID, PE10GDBILR,
+ "PE10G2DBILR"},
+ {0x8086, 0x10a7, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2BISC6_SSID /*PCI_ANY_ID */ , PEG2BISC6, "PEG2BI5SC6"},
+ {0x8086, 0x10a7, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG6BPIFC_SSID /*PCI_ANY_ID */ , PEG6BPIFC, "PEG6BPI5FC"},
+
+ {BROADCOM_VID, BROADCOM_PE10G2_PID, SILICOM_SVID,
+ SILICOM_PE10G2BPTCX4_SSID, PE10G2BPTCX4, "PE10G2BPTCX4"},
+ {BROADCOM_VID, BROADCOM_PE10G2_PID, SILICOM_SVID,
+ SILICOM_PE10G2BPTSR_SSID, PE10G2BPTSR, "PE10G2BPTSR"},
+ {BROADCOM_VID, BROADCOM_PE10G2_PID, SILICOM_SVID,
+ SILICOM_PE10G2BPTLR_SSID, PE10G2BPTLR, "PE10G2BPTLR"},
+ {BROADCOM_VID, BROADCOM_PE10G2_PID, SILICOM_SVID,
+ SILICOM_PE10G2BPTT_SSID, PE10G2BPTT, "PE10G2BPTT"},
+
+ /* {BROADCOM_VID, BROADCOM_PE10G2_PID, PCI_ANY_ID, PCI_ANY_ID, PE10G2BPTCX4, "PE10G2BPTCX4"}, */
+
+ {0x8086, 0x10c9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG4BPI6_SSID /*PCI_ANY_ID */ , PEG4BPI6, "PEG4BPI6"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG4BPFI6_SSID /*PCI_ANY_ID */ , PEG4BPFI6, "PEG4BPFI6"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG4BPFI6LX_SSID /*PCI_ANY_ID */ , PEG4BPFI6LX, "PEG4BPFI6LX"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG4BPFI6ZX_SSID /*PCI_ANY_ID */ , PEG4BPFI6ZX, "PEG4BPFI6ZX"},
+ {0x8086, 0x10c9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2BPI6_SSID /*PCI_ANY_ID */ , PEG2BPI6, "PEG2BPI6"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2BPFI6_SSID /*PCI_ANY_ID */ , PEG2BPFI6, "PEG2BPFI6"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2BPFI6LX_SSID /*PCI_ANY_ID */ , PEG2BPFI6LX, "PEG2BPFI6LX"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2BPFI6ZX_SSID /*PCI_ANY_ID */ , PEG2BPFI6ZX, "PEG2BPFI6ZX"},
+ {0x8086, 0x10e7, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2BPFI6FLXM_SSID /*PCI_ANY_ID */ , PEG2BPFI6FLXM,
+ "PEG2BPFI6FLXM"},
+ {0x8086, 0x10c9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG4BPI6FC_SSID /*PCI_ANY_ID */ , PEG4BPI6FC, "PEG4BPI6FC"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG4BPFI6FC_SSID /*PCI_ANY_ID */ , PEG4BPFI6FC, "PEG4BPFI6FC"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG4BPFI6FCLX_SSID /*PCI_ANY_ID */ , PEG4BPFI6FCLX,
+ "PEG4BPFI6FCLX"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG4BPFI6FCZX_SSID /*PCI_ANY_ID */ , PEG4BPFI6FCZX,
+ "PEG4BPFI6FCZX"},
+ {0x8086, 0x10c9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG6BPI6_SSID /*PCI_ANY_ID */ , PEG6BPI6, "PEG6BPI6"},
+ {0x8086, 0x10c9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2BPI6SC6_SSID /*PCI_ANY_ID */ , PEG2BPI6SC6,
+ "PEG6BPI62SC6"},
+ {0x8086, 0x10c9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_MEG2BPI6_SSID /*PCI_ANY_ID */ , MEG2BPI6, "MEG2BPI6"},
+ {0x8086, 0x10c9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_XEG2BPI6_SSID /*PCI_ANY_ID */ , XEG2BPI6, "XEG2BPI6"},
+ {0x8086, 0x10c9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_MEG4BPI6_SSID /*PCI_ANY_ID */ , MEG4BPI6, "MEG4BPI6"},
+
+ {0x8086, 0x10a9, SILICOM_SVID /*PCI_ANY_ID */ , SILICOM_PEG2BPFI5_SSID,
+ PEG2BPFI5, "PEG2BPFI5"},
+ {0x8086, 0x10a9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2BPFI5LX_SSID, PEG2BPFI5LX, "PEG2BPFI5LX"},
+
+ {0x8086, 0x105f, SILICOM_SVID, SILICOM_PXEG4BPFI_SSID, PXEG4BPFI,
+ "PXEG4BPFI-SD"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1EG2BPI6_SSID /*PCI_ANY_ID */ , M1EG2BPI6, "MxEG2BPI6"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1EG2BPFI6_SSID /*PCI_ANY_ID */ , M1EG2BPFI6, "MxEG2BPFI6"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1EG2BPFI6LX_SSID /*PCI_ANY_ID */ , M1EG2BPFI6LX,
+ "MxEG2BPFI6LX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1EG2BPFI6ZX_SSID /*PCI_ANY_ID */ , M1EG2BPFI6ZX,
+ "MxEG2BPFI6ZX"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1EG4BPI6_SSID /*PCI_ANY_ID */ , M1EG4BPI6, "MxEG4BPI6"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1EG4BPFI6_SSID /*PCI_ANY_ID */ , M1EG4BPFI6, "MxEG4BPFI6"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1EG4BPFI6LX_SSID /*PCI_ANY_ID */ , M1EG4BPFI6LX,
+ "MxEG4BPFI6LX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1EG4BPFI6ZX_SSID /*PCI_ANY_ID */ , M1EG4BPFI6ZX,
+ "MxEG4BPFI6ZX"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1EG6BPI6_SSID /*PCI_ANY_ID */ , M1EG6BPI6, "MxEG6BPI6"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1E2G4BPi80_SSID /*PCI_ANY_ID */ , M1E2G4BPi80, "MxE2G4BPi80"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1E2G4BPFi80_SSID /*PCI_ANY_ID */ , M1E2G4BPFi80,
+ "MxE2G4BPFi80"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1E2G4BPFi80LX_SSID /*PCI_ANY_ID */ , M1E2G4BPFi80LX,
+ "MxE2G4BPFi80LX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1E2G4BPFi80ZX_SSID /*PCI_ANY_ID */ , M1E2G4BPFi80ZX,
+ "MxE2G4BPFi80ZX"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2EG2BPFI6_SSID /*PCI_ANY_ID */ , M2EG2BPFI6, "M2EG2BPFI6"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2EG2BPFI6LX_SSID /*PCI_ANY_ID */ , M2EG2BPFI6LX,
+ "M2EG2BPFI6LX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2EG2BPFI6ZX_SSID /*PCI_ANY_ID */ , M2EG2BPFI6ZX,
+ "M2EG2BPFI6ZX"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2EG4BPI6_SSID /*PCI_ANY_ID */ , M2EG4BPI6, "M2EG4BPI6"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2EG4BPFI6_SSID /*PCI_ANY_ID */ , M2EG4BPFI6, "M2EG4BPFI6"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2EG4BPFI6LX_SSID /*PCI_ANY_ID */ , M2EG4BPFI6LX,
+ "M2EG4BPFI6LX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2EG4BPFI6ZX_SSID /*PCI_ANY_ID */ , M2EG4BPFI6ZX,
+ "M2EG4BPFI6ZX"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2EG6BPI6_SSID /*PCI_ANY_ID */ , M2EG6BPI6, "M2EG6BPI6"},
+
+ {0x8086, 0x10c9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2DBI6_SSID /*PCI_ANY_ID */ , PEG2DBI6, "PEG2DBI6"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2DBFI6_SSID /*PCI_ANY_ID */ , PEG2DBFI6, "PEG2DBFI6"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2DBFI6LX_SSID /*PCI_ANY_ID */ , PEG2DBFI6LX, "PEG2DBFI6LX"},
+ {0x8086, 0x10e6, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PEG2DBFI6ZX_SSID /*PCI_ANY_ID */ , PEG2DBFI6ZX, "PEG2DBFI6ZX"},
+
+ {0x8086, 0x10F9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE210G2DBi9SR_SSID, PE210G2DBi9SR, "PE210G2DBi9SR"},
+ {0x8086, 0x10F9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE210G2DBi9LR_SSID, PE210G2DBi9LR, "PE210G2DBi9LR"},
+ {0x8086, 0x10F9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE310G4DBi940SR_SSID, PE310G4DBi940SR, "PE310G4DBi9SR"},
+
+ {0x8086, 0x10Fb, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE310G4BPi9T_SSID, PE310G4BPi9T, "PE310G4BPi9T"},
+ {0x8086, 0x10Fb, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE310G4BPi9SR_SSID, PE310G4BPi9SR, "PE310G4BPi9SR"},
+ {0x8086, 0x10Fb, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE310G4BPi9LR_SSID, PE310G4BPi9LR, "PE310G4BPi9LR"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G4BPi80_SSID /*PCI_ANY_ID */ , PE2G4BPi80, "PE2G4BPi80"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G4BPFi80_SSID /*PCI_ANY_ID */ , PE2G4BPFi80, "PE2G4BPFi80"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G4BPFi80LX_SSID /*PCI_ANY_ID */ , PE2G4BPFi80LX,
+ "PE2G4BPFi80LX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G4BPFi80ZX_SSID /*PCI_ANY_ID */ , PE2G4BPFi80ZX,
+ "PE2G4BPFi80ZX"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G4BPi80L_SSID /*PCI_ANY_ID */ , PE2G4BPi80L, "PE2G4BPi80L"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M6E2G8BPi80A_SSID /*PCI_ANY_ID */ , M6E2G8BPi80A,
+ "MxE2G8BPi80A"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G2BPi35_SSID /*PCI_ANY_ID */ , PE2G2BPi35, "PE2G2BPi35"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PAC1200BPi35_SSID /*PCI_ANY_ID */ , PAC1200BPi35,
+ "PAC1200BPi35"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G2BPFi35_SSID /*PCI_ANY_ID */ , PE2G2BPFi35, "PE2G2BPFi35"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G2BPFi35LX_SSID /*PCI_ANY_ID */ , PE2G2BPFi35LX,
+ "PE2G2BPFi35LX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G2BPFi35ZX_SSID /*PCI_ANY_ID */ , PE2G2BPFi35ZX,
+ "PE2G2BPFi35ZX"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G4BPi35_SSID /*PCI_ANY_ID */ , PE2G4BPi35, "PE2G4BPi35"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G4BPi35L_SSID /*PCI_ANY_ID */ , PE2G4BPi35L, "PE2G4BPi35L"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G4BPFi35_SSID /*PCI_ANY_ID */ , PE2G4BPFi35, "PE2G4BPFi35"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G4BPFi35LX_SSID /*PCI_ANY_ID */ , PE2G4BPFi35LX,
+ "PE2G4BPFi35LX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G4BPFi35ZX_SSID /*PCI_ANY_ID */ , PE2G4BPFi35ZX,
+ "PE2G4BPFi35ZX"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G6BPi35_SSID /*PCI_ANY_ID */ , PE2G6BPi35, "PE2G6BPi35"},
+
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaa0, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaa1, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaa2, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaa3, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaa4, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaa5, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaa6, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaa7, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaa8, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaa9, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaaa, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaab, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaac, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaad, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaae, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaaf, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xab0, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xab1, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xab2, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xab3, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xab4, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xab5, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xab6, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xab7, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xab8, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xab9, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xaba, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xabb, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xabc, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xabd, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xabe, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ , 0xabf, PE2G6BPi35CX,
+ "PE2G6BPi35CX"},
+
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G2BPi80_SSID /*PCI_ANY_ID */ , PE2G2BPi80, "PE2G2BPi80"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G2BPFi80_SSID /*PCI_ANY_ID */ , PE2G2BPFi80, "PE2G2BPFi80"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G2BPFi80LX_SSID /*PCI_ANY_ID */ , PE2G2BPFi80LX,
+ "PE2G2BPFi80LX"},
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE2G2BPFi80ZX_SSID /*PCI_ANY_ID */ , PE2G2BPFi80ZX,
+ "PE2G2BPFi80ZX"},
+
+ {0x8086, 0x10c9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_MEG2BPI6_SSID /*PCI_ANY_ID */ , MEG2BPI6, "MEG2BPI6"},
+ {0x8086, 0x10c9, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_XEG2BPI6_SSID /*PCI_ANY_ID */ , XEG2BPI6, "XEG2BPI6"},
+
+#if 0
+ {0x8086, 0x10fb, 0x8086, INTEL_PE210G2SPI9_SSID, PE210G2SPI9,
+ "PE210G2SPI9"},
+#endif
+ {0x8086, 0x10fb, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1E10G2BPI9CX4_SSID /*PCI_ANY_ID */ , M1E10G2BPI9CX4,
+ "MxE210G2BPI9CX4"},
+ {0x8086, 0x10fb, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1E10G2BPI9SR_SSID /*PCI_ANY_ID */ , M1E10G2BPI9SR,
+ "MxE210G2BPI9SR"},
+ {0x8086, 0x10fb, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1E10G2BPI9LR_SSID /*PCI_ANY_ID */ , M1E10G2BPI9LR,
+ "MxE210G2BPI9LR"},
+ {0x8086, 0x10fb, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M1E10G2BPI9T_SSID /*PCI_ANY_ID */ , M1E10G2BPI9T,
+ "MxE210G2BPI9T"},
+
+ {0x8086, 0x10fb, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2E10G2BPI9CX4_SSID /*PCI_ANY_ID */ , M2E10G2BPI9CX4,
+ "M2E10G2BPI9CX4"},
+ {0x8086, 0x10fb, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2E10G2BPI9SR_SSID /*PCI_ANY_ID */ , M2E10G2BPI9SR,
+ "M2E10G2BPI9SR"},
+ {0x8086, 0x10fb, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2E10G2BPI9LR_SSID /*PCI_ANY_ID */ , M2E10G2BPI9LR,
+ "M2E10G2BPI9LR"},
+ {0x8086, 0x10fb, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M2E10G2BPI9T_SSID /*PCI_ANY_ID */ , M2E10G2BPI9T,
+ "M2E10G2BPI9T"},
+
+ {0x8086, 0x10fb, SILICOM_SVID, SILICOM_PE210G2BPI9CX4_SSID,
+ PE210G2BPI9CX4, "PE210G2BPI9CX4"},
+ {0x8086, 0x10fb, SILICOM_SVID, SILICOM_PE210G2BPI9SR_SSID,
+ PE210G2BPI9SR, "PE210G2BPI9SR"},
+ {0x8086, 0x10fb, SILICOM_SVID, SILICOM_PE210G2BPI9LR_SSID,
+ PE210G2BPI9LR, "PE210G2BPI9LR"},
+ {0x8086, 0x10fb, SILICOM_SVID, SILICOM_PE210G2BPI9T_SSID, PE210G2BPI9T,
+ "PE210G2BPI9T"},
+
+#if 0
+ {0x1374, 0x2c, SILICOM_SVID, SILICOM_PXG4BPI_SSID, PXG4BPI,
+ "PXG4BPI-SD"},
+
+ {0x1374, 0x2d, SILICOM_SVID, SILICOM_PXG4BPFI_SSID, PXG4BPFI,
+ "PXG4BPFI-SD"},
+
+ {0x1374, 0x3f, SILICOM_SVID, SILICOM_PXG2TBI_SSID, PXG2TBI,
+ "PXG2TBI-SD"},
+
+ {0x1374, 0x3d, SILICOM_SVID, SILICOM_PXG2BISC1_SSID, PXG2BISC1,
+ "PXG2BISC1-SD"},
+
+ {0x1374, 0x40, SILICOM_SVID, SILICOM_PEG4BPFI_SSID, PEG4BPFI,
+ "PEG4BPFI-SD"},
+
+#ifdef BP_SELF_TEST
+ {0x1374, 0x28, SILICOM_SVID, 0x28, PXGBPI, "PXG2BPI-SD"},
+#endif
+#endif
+ {0x8086, PCI_ANY_ID, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_M6E2G8BPi80_SSID /*PCI_ANY_ID */ , M6E2G8BPi80, "MxE2G8BPi80"},
+ {0x8086, 0x1528, SILICOM_SVID /*PCI_ANY_ID */ ,
+ SILICOM_PE210G2BPi40_SSID /*PCI_ANY_ID */ , PE210G2BPi40,
+ "PE210G2BPi40T"},
+
+ /* required last entry */
+ {0,}
+};
+
+/*
+* Initialize the module - Register the character device
+*/
+
+static int __init bypass_init_module(void)
+{
+ int ret_val, idx, idx_dev = 0;
+ struct pci_dev *pdev1 = NULL;
+ unsigned long mmio_start, mmio_len;
+
+ printk(BP_MOD_DESCR " v" BP_MOD_VER "\n");
+ ret_val = register_chrdev(major_num, DEVICE_NAME, &Fops);
+ if (ret_val < 0) {
+ printk("%s failed with %d\n", DEVICE_NAME, ret_val);
+ return ret_val;
+ }
+ major_num = ret_val; /* dynamic */
+ for (idx = 0; tx_ctl_pci_tbl[idx].vendor; idx++) {
+ while ((pdev1 = pci_get_subsys(tx_ctl_pci_tbl[idx].vendor,
+ tx_ctl_pci_tbl[idx].device,
+ tx_ctl_pci_tbl[idx].subvendor,
+ tx_ctl_pci_tbl[idx].subdevice,
+ pdev1))) {
+
+ device_num++;
+ }
+ }
+ if (!device_num) {
+ printk("No such device\n");
+ unregister_chrdev(major_num, DEVICE_NAME);
+ return -1;
+ }
+
+ bpctl_dev_arr = kmalloc((device_num) * sizeof(bpctl_dev_t), GFP_KERNEL);
+
+ if (!bpctl_dev_arr) {
+ printk("Allocation error\n");
+ unregister_chrdev(major_num, DEVICE_NAME);
+ return -1;
+ }
+ memset(bpctl_dev_arr, 0, ((device_num) * sizeof(bpctl_dev_t)));
+
+ pdev1 = NULL;
+ for (idx = 0; tx_ctl_pci_tbl[idx].vendor; idx++) {
+ while ((pdev1 = pci_get_subsys(tx_ctl_pci_tbl[idx].vendor,
+ tx_ctl_pci_tbl[idx].device,
+ tx_ctl_pci_tbl[idx].subvendor,
+ tx_ctl_pci_tbl[idx].subdevice,
+ pdev1))) {
+ bpctl_dev_arr[idx_dev].pdev = pdev1;
+
+ mmio_start = pci_resource_start(pdev1, 0);
+ mmio_len = pci_resource_len(pdev1, 0);
+
+ bpctl_dev_arr[idx_dev].desc =
+ dev_desc[tx_ctl_pci_tbl[idx].index].name;
+ bpctl_dev_arr[idx_dev].name =
+ tx_ctl_pci_tbl[idx].bp_name;
+ bpctl_dev_arr[idx_dev].device =
+ tx_ctl_pci_tbl[idx].device;
+ bpctl_dev_arr[idx_dev].vendor =
+ tx_ctl_pci_tbl[idx].vendor;
+ bpctl_dev_arr[idx_dev].subdevice =
+ tx_ctl_pci_tbl[idx].subdevice;
+ bpctl_dev_arr[idx_dev].subvendor =
+ tx_ctl_pci_tbl[idx].subvendor;
+ /* bpctl_dev_arr[idx_dev].pdev=pdev1; */
+ bpctl_dev_arr[idx_dev].func = PCI_FUNC(pdev1->devfn);
+ bpctl_dev_arr[idx_dev].slot = PCI_SLOT(pdev1->devfn);
+ bpctl_dev_arr[idx_dev].bus = pdev1->bus->number;
+ bpctl_dev_arr[idx_dev].mem_map =
+ (unsigned long)ioremap(mmio_start, mmio_len);
+#ifdef BP_SYNC_FLAG
+ spin_lock_init(&bpctl_dev_arr[idx_dev].bypass_wr_lock);
+#endif
+ if (BP10G9_IF_SERIES(bpctl_dev_arr[idx_dev].subdevice))
+ bpctl_dev_arr[idx_dev].bp_10g9 = 1;
+ if (BP10G_IF_SERIES(bpctl_dev_arr[idx_dev].subdevice))
+ bpctl_dev_arr[idx_dev].bp_10g = 1;
+ if (PEG540_IF_SERIES(bpctl_dev_arr[idx_dev].subdevice)) {
+
+ bpctl_dev_arr[idx_dev].bp_540 = 1;
+ }
+ if (PEGF5_IF_SERIES(bpctl_dev_arr[idx_dev].subdevice))
+ bpctl_dev_arr[idx_dev].bp_fiber5 = 1;
+ if (PEG80_IF_SERIES(bpctl_dev_arr[idx_dev].subdevice))
+ bpctl_dev_arr[idx_dev].bp_i80 = 1;
+ if (PEGF80_IF_SERIES(bpctl_dev_arr[idx_dev].subdevice))
+ bpctl_dev_arr[idx_dev].bp_i80 = 1;
+ if ((bpctl_dev_arr[idx_dev].subdevice & 0xa00) == 0xa00)
+ bpctl_dev_arr[idx_dev].bp_i80 = 1;
+ if (BP10GB_IF_SERIES(bpctl_dev_arr[idx_dev].subdevice)) {
+ if (bpctl_dev_arr[idx_dev].ifindex == 0) {
+ unregister_chrdev(major_num,
+ DEVICE_NAME);
+ printk
+ ("Please load network driver for %s adapter!\n",
+ bpctl_dev_arr[idx_dev].name);
+ return -1;
+ }
+
+ if (bpctl_dev_arr[idx_dev].ndev) {
+ if (!
+ (bpctl_dev_arr[idx_dev].ndev->
+ flags & IFF_UP)) {
+ if (!
+ (bpctl_dev_arr[idx_dev].
+ ndev->flags & IFF_UP)) {
+ unregister_chrdev
+ (major_num,
+ DEVICE_NAME);
+ printk
+ ("Please bring up network interfaces for %s adapter!\n",
+ bpctl_dev_arr
+ [idx_dev].name);
+ return -1;
+ }
+
+ }
+ }
+ bpctl_dev_arr[idx_dev].bp_10gb = 1;
+ }
+
+ if (!bpctl_dev_arr[idx_dev].bp_10g9) {
+
+ if (is_bypass_fn(&bpctl_dev_arr[idx_dev])) {
+ printk(KERN_INFO "%s found, ",
+ bpctl_dev_arr[idx_dev].name);
+ if ((OLD_IF_SERIES
+ (bpctl_dev_arr[idx_dev].subdevice))
+ ||
+ (INTEL_IF_SERIES
+ (bpctl_dev_arr[idx_dev].
+ subdevice)))
+ bpctl_dev_arr[idx_dev].
+ bp_fw_ver = 0xff;
+ else
+ bpctl_dev_arr[idx_dev].
+ bp_fw_ver =
+ bypass_fw_ver(&bpctl_dev_arr
+ [idx_dev]);
+ if ((bpctl_dev_arr[idx_dev].bp_10gb ==
+ 1)
+ && (bpctl_dev_arr[idx_dev].
+ bp_fw_ver == 0xff)) {
+ int cnt = 100;
+ while (cnt--) {
+ iounmap((void
+ *)
+ (bpctl_dev_arr
+ [idx_dev].
+ mem_map));
+ mmio_start =
+ pci_resource_start
+ (pdev1, 0);
+ mmio_len =
+ pci_resource_len
+ (pdev1, 0);
+
+ bpctl_dev_arr[idx_dev].
+ mem_map =
+ (unsigned long)
+ ioremap(mmio_start,
+ mmio_len);
+
+ bpctl_dev_arr[idx_dev].
+ bp_fw_ver =
+ bypass_fw_ver
+ (&bpctl_dev_arr
+ [idx_dev]);
+ if (bpctl_dev_arr
+ [idx_dev].
+ bp_fw_ver == 0xa8)
+ break;
+
+ }
+ }
+ /* bpctl_dev_arr[idx_dev].bp_fw_ver=0xa8; */
+ printk("firmware version: 0x%x\n",
+ bpctl_dev_arr[idx_dev].
+ bp_fw_ver);
+ }
+ bpctl_dev_arr[idx_dev].wdt_status =
+ WDT_STATUS_UNKNOWN;
+ bpctl_dev_arr[idx_dev].reset_time = 0;
+ atomic_set(&bpctl_dev_arr[idx_dev].wdt_busy, 0);
+ bpctl_dev_arr[idx_dev].bp_status_un = 1;
+
+ bypass_caps_init(&bpctl_dev_arr[idx_dev]);
+
+ init_bypass_wd_auto(&bpctl_dev_arr[idx_dev]);
+ init_bypass_tpl_auto(&bpctl_dev_arr[idx_dev]);
+ if (NOKIA_SERIES
+ (bpctl_dev_arr[idx_dev].subdevice))
+ reset_cont(&bpctl_dev_arr[idx_dev]);
+ }
+#ifdef BP_SELF_TEST
+ if ((bpctl_dev_arr[idx_dev].bp_tx_data =
+ kmalloc(BPTEST_DATA_LEN, GFP_KERNEL))) {
+
+ memset(bpctl_dev_arr[idx_dev].bp_tx_data, 0x0,
+ BPTEST_DATA_LEN);
+
+ memset(bpctl_dev_arr[idx_dev].bp_tx_data, 0xff,
+ 6);
+ memset(bpctl_dev_arr[idx_dev].bp_tx_data + 6,
+ 0x0, 1);
+ memset(bpctl_dev_arr[idx_dev].bp_tx_data + 7,
+ 0xaa, 5);
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,9))
+ bpctl_dev_arr[idx_dev].bp_tx_data[12] =
+ (ETH_P_BPTEST >> 8) & 0xff;
+ bpctl_dev_arr[idx_dev].bp_tx_data[13] =
+ ETH_P_BPTEST & 0xff;
+#else
+ *(__be16 *) (bpctl_dev_arr[idx_dev].bp_tx_data +
+ 12) = htons(ETH_P_BPTEST);
+#endif
+
+ } else
+ printk("bp_ctl: Memory allocation error!\n");
+#endif
+ idx_dev++;
+
+ }
+ }
+ if_scan_init();
+
+ sema_init(&bpctl_sema, 1);
+ spin_lock_init(&bpvm_lock);
+ {
+
+ bpctl_dev_t *pbpctl_dev_c = NULL;
+ for (idx_dev = 0;
+ ((bpctl_dev_arr[idx_dev].pdev != NULL)
+ && (idx_dev < device_num)); idx_dev++) {
+ if (bpctl_dev_arr[idx_dev].bp_10g9) {
+ pbpctl_dev_c =
+ get_status_port_fn(&bpctl_dev_arr[idx_dev]);
+ if (is_bypass_fn(&bpctl_dev_arr[idx_dev])) {
+ printk(KERN_INFO "%s found, ",
+ bpctl_dev_arr[idx_dev].name);
+ bpctl_dev_arr[idx_dev].bp_fw_ver =
+ bypass_fw_ver(&bpctl_dev_arr
+ [idx_dev]);
+ printk("firmware version: 0x%x\n",
+ bpctl_dev_arr[idx_dev].
+ bp_fw_ver);
+
+ }
+ bpctl_dev_arr[idx_dev].wdt_status =
+ WDT_STATUS_UNKNOWN;
+ bpctl_dev_arr[idx_dev].reset_time = 0;
+ atomic_set(&bpctl_dev_arr[idx_dev].wdt_busy, 0);
+ bpctl_dev_arr[idx_dev].bp_status_un = 1;
+
+ bypass_caps_init(&bpctl_dev_arr[idx_dev]);
+
+ init_bypass_wd_auto(&bpctl_dev_arr[idx_dev]);
+ init_bypass_tpl_auto(&bpctl_dev_arr[idx_dev]);
+
+ }
+
+ }
+ }
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ inter_module_register("is_bypass_sd", THIS_MODULE, &is_bypass_sd);
+ inter_module_register("get_bypass_slave_sd", THIS_MODULE,
+ &get_bypass_slave_sd);
+ inter_module_register("get_bypass_caps_sd", THIS_MODULE,
+ &get_bypass_caps_sd);
+ inter_module_register("get_wd_set_caps_sd", THIS_MODULE,
+ &get_wd_set_caps_sd);
+ inter_module_register("set_bypass_sd", THIS_MODULE, &set_bypass_sd);
+ inter_module_register("get_bypass_sd", THIS_MODULE, &get_bypass_sd);
+ inter_module_register("get_bypass_change_sd", THIS_MODULE,
+ &get_bypass_change_sd);
+ inter_module_register("set_dis_bypass_sd", THIS_MODULE,
+ &set_dis_bypass_sd);
+ inter_module_register("get_dis_bypass_sd", THIS_MODULE,
+ &get_dis_bypass_sd);
+ inter_module_register("set_bypass_pwoff_sd", THIS_MODULE,
+ &set_bypass_pwoff_sd);
+ inter_module_register("get_bypass_pwoff_sd", THIS_MODULE,
+ &get_bypass_pwoff_sd);
+ inter_module_register("set_bypass_pwup_sd", THIS_MODULE,
+ &set_bypass_pwup_sd);
+ inter_module_register("get_bypass_pwup_sd", THIS_MODULE,
+ &get_bypass_pwup_sd);
+ inter_module_register("get_bypass_wd_sd", THIS_MODULE,
+ &get_bypass_wd_sd);
+ inter_module_register("set_bypass_wd_sd", THIS_MODULE,
+ &set_bypass_wd_sd);
+ inter_module_register("get_wd_expire_time_sd", THIS_MODULE,
+ &get_wd_expire_time_sd);
+ inter_module_register("reset_bypass_wd_timer_sd", THIS_MODULE,
+ &reset_bypass_wd_timer_sd);
+ inter_module_register("set_std_nic_sd", THIS_MODULE, &set_std_nic_sd);
+ inter_module_register("get_std_nic_sd", THIS_MODULE, &get_std_nic_sd);
+ inter_module_register("set_tx_sd", THIS_MODULE, &set_tx_sd);
+ inter_module_register("get_tx_sd", THIS_MODULE, &get_tx_sd);
+ inter_module_register("set_tpl_sd", THIS_MODULE, &set_tpl_sd);
+ inter_module_register("get_tpl_sd", THIS_MODULE, &get_tpl_sd);
+
+ inter_module_register("set_bp_hw_reset_sd", THIS_MODULE,
+ &set_bp_hw_reset_sd);
+ inter_module_register("get_bp_hw_reset_sd", THIS_MODULE,
+ &get_bp_hw_reset_sd);
+
+ inter_module_register("set_tap_sd", THIS_MODULE, &set_tap_sd);
+ inter_module_register("get_tap_sd", THIS_MODULE, &get_tap_sd);
+ inter_module_register("get_tap_change_sd", THIS_MODULE,
+ &get_tap_change_sd);
+ inter_module_register("set_dis_tap_sd", THIS_MODULE, &set_dis_tap_sd);
+ inter_module_register("get_dis_tap_sd", THIS_MODULE, &get_dis_tap_sd);
+ inter_module_register("set_tap_pwup_sd", THIS_MODULE, &set_tap_pwup_sd);
+ inter_module_register("get_tap_pwup_sd", THIS_MODULE, &get_tap_pwup_sd);
+ inter_module_register("set_bp_disc_sd", THIS_MODULE, &set_bp_disc_sd);
+ inter_module_register("get_bp_disc_sd", THIS_MODULE, &get_bp_disc_sd);
+ inter_module_register("get_bp_disc_change_sd", THIS_MODULE,
+ &get_bp_disc_change_sd);
+ inter_module_register("set_bp_dis_disc_sd", THIS_MODULE,
+ &set_bp_dis_disc_sd);
+ inter_module_register("get_bp_dis_disc_sd", THIS_MODULE,
+ &get_bp_dis_disc_sd);
+ inter_module_register("set_bp_disc_pwup_sd", THIS_MODULE,
+ &set_bp_disc_pwup_sd);
+ inter_module_register("get_bp_disc_pwup_sd", THIS_MODULE,
+ &get_bp_disc_pwup_sd);
+ inter_module_register("set_wd_exp_mode_sd", THIS_MODULE,
+ &set_wd_exp_mode_sd);
+ inter_module_register("get_wd_exp_mode_sd", THIS_MODULE,
+ &get_wd_exp_mode_sd);
+ inter_module_register("set_wd_autoreset_sd", THIS_MODULE,
+ &set_wd_autoreset_sd);
+ inter_module_register("get_wd_autoreset_sd", THIS_MODULE,
+ &get_wd_autoreset_sd);
+ inter_module_register("get_bypass_info_sd", THIS_MODULE,
+ &get_bypass_info_sd);
+ inter_module_register("bp_if_scan_sd", THIS_MODULE, &bp_if_scan_sd);
+
+#endif
+ register_netdevice_notifier(&bp_notifier_block);
+#ifdef BP_PROC_SUPPORT
+ {
+ int i = 0;
+ /* unsigned long flags; */
+ /* rcu_read_lock(); */
+ bp_proc_create();
+ for (i = 0; i < device_num; i++) {
+ if (bpctl_dev_arr[i].ifindex) {
+ /* spin_lock_irqsave(&bpvm_lock, flags); */
+ bypass_proc_remove_dev_sd(&bpctl_dev_arr[i]);
+ bypass_proc_create_dev_sd(&bpctl_dev_arr[i]);
+ /* spin_unlock_irqrestore(&bpvm_lock, flags); */
+ }
+
+ }
+ /* rcu_read_unlock(); */
+ }
+#endif
+
+ return 0;
+}
+
+/*
+* Cleanup - unregister the appropriate file from /proc
+*/
+static void __exit bypass_cleanup_module(void)
+{
+ int i;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23))
+ int ret;
+#endif
+ unregister_netdevice_notifier(&bp_notifier_block);
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0))
+ inter_module_unregister("is_bypass_sd");
+ inter_module_unregister("get_bypass_slave_sd");
+ inter_module_unregister("get_bypass_caps_sd");
+ inter_module_unregister("get_wd_set_caps_sd");
+ inter_module_unregister("set_bypass_sd");
+ inter_module_unregister("get_bypass_sd");
+ inter_module_unregister("get_bypass_change_sd");
+ inter_module_unregister("set_dis_bypass_sd");
+ inter_module_unregister("get_dis_bypass_sd");
+ inter_module_unregister("set_bypass_pwoff_sd");
+ inter_module_unregister("get_bypass_pwoff_sd");
+ inter_module_unregister("set_bypass_pwup_sd");
+ inter_module_unregister("get_bypass_pwup_sd");
+ inter_module_unregister("set_bypass_wd_sd");
+ inter_module_unregister("get_bypass_wd_sd");
+ inter_module_unregister("get_wd_expire_time_sd");
+ inter_module_unregister("reset_bypass_wd_timer_sd");
+ inter_module_unregister("set_std_nic_sd");
+ inter_module_unregister("get_std_nic_sd");
+ inter_module_unregister("set_tx_sd");
+ inter_module_unregister("get_tx_sd");
+ inter_module_unregister("set_tpl_sd");
+ inter_module_unregister("get_tpl_sd");
+ inter_module_unregister("set_tap_sd");
+ inter_module_unregister("get_tap_sd");
+ inter_module_unregister("get_tap_change_sd");
+ inter_module_unregister("set_dis_tap_sd");
+ inter_module_unregister("get_dis_tap_sd");
+ inter_module_unregister("set_tap_pwup_sd");
+ inter_module_unregister("get_tap_pwup_sd");
+ inter_module_unregister("set_bp_disc_sd");
+ inter_module_unregister("get_bp_disc_sd");
+ inter_module_unregister("get_bp_disc_change_sd");
+ inter_module_unregister("set_bp_dis_disc_sd");
+ inter_module_unregister("get_bp_dis_disc_sd");
+ inter_module_unregister("set_bp_disc_pwup_sd");
+ inter_module_unregister("get_bp_disc_pwup_sd");
+ inter_module_unregister("set_wd_exp_mode_sd");
+ inter_module_unregister("get_wd_exp_mode_sd");
+ inter_module_unregister("set_wd_autoreset_sd");
+ inter_module_unregister("get_wd_autoreset_sd");
+ inter_module_unregister("get_bypass_info_sd");
+ inter_module_unregister("bp_if_scan_sd");
+
+#endif
+
+ for (i = 0; i < device_num; i++) {
+ /* unsigned long flags; */
+#ifdef BP_PROC_SUPPORT
+/* spin_lock_irqsave(&bpvm_lock, flags);
+ rcu_read_lock(); */
+ bypass_proc_remove_dev_sd(&bpctl_dev_arr[i]);
+/* spin_unlock_irqrestore(&bpvm_lock, flags);
+ rcu_read_unlock(); */
+#endif
+ remove_bypass_wd_auto(&bpctl_dev_arr[i]);
+ bpctl_dev_arr[i].reset_time = 0;
+
+ remove_bypass_tpl_auto(&bpctl_dev_arr[i]);
+ }
+
+ /* unmap all devices */
+ for (i = 0; i < device_num; i++) {
+#ifdef BP_SELF_TEST
+ if (bpctl_dev_arr[i].bp_tx_data)
+ kfree(bpctl_dev_arr[i].bp_tx_data);
+#endif
+ iounmap((void *)(bpctl_dev_arr[i].mem_map));
+ }
+
+ /* free all devices space */
+ if (bpctl_dev_arr)
+ kfree(bpctl_dev_arr);
+
+/*
+* Unregister the device
+*/
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,23))
+ ret = unregister_chrdev(major_num, DEVICE_NAME);
+/*
+* If there's an error, report it
+*/
+ if (ret < 0)
+ printk("Error in module_unregister_chrdev: %d\n", ret);
+#else
+ unregister_chrdev(major_num, DEVICE_NAME);
+
+#endif
+}
+
+module_init(bypass_init_module);
+module_exit(bypass_cleanup_module);
+
+int is_bypass_sd(int ifindex)
+{
+ return is_bypass(get_dev_idx_p(ifindex));
+}
+
+int set_bypass_sd(int ifindex, int bypass_mode)
+{
+
+ return set_bypass_fn(get_dev_idx_p(ifindex), bypass_mode);
+}
+
+int get_bypass_sd(int ifindex)
+{
+
+ return get_bypass_fn(get_dev_idx_p(ifindex));
+}
+
+int get_bypass_change_sd(int ifindex)
+{
+
+ return get_bypass_change_fn(get_dev_idx_p(ifindex));
+}
+
+int set_dis_bypass_sd(int ifindex, int dis_param)
+{
+ return set_dis_bypass_fn(get_dev_idx_p(ifindex), dis_param);
+}
+
+int get_dis_bypass_sd(int ifindex)
+{
+
+ return get_dis_bypass_fn(get_dev_idx_p(ifindex));
+}
+
+int set_bypass_pwoff_sd(int ifindex, int bypass_mode)
+{
+ return set_bypass_pwoff_fn(get_dev_idx_p(ifindex), bypass_mode);
+
+}
+
+int get_bypass_pwoff_sd(int ifindex)
+{
+ return get_bypass_pwoff_fn(get_dev_idx_p(ifindex));
+
+}
+
+int set_bypass_pwup_sd(int ifindex, int bypass_mode)
+{
+ return set_bypass_pwup_fn(get_dev_idx_p(ifindex), bypass_mode);
+
+}
+
+int get_bypass_pwup_sd(int ifindex)
+{
+ return get_bypass_pwup_fn(get_dev_idx_p(ifindex));
+
+}
+
+int set_bypass_wd_sd(int if_index, int ms_timeout, int *ms_timeout_set)
+{
+ if ((is_bypass(get_dev_idx_p(if_index))) <= 0)
+ return BP_NOT_CAP;
+ *ms_timeout_set = set_bypass_wd_fn(get_dev_idx_p(if_index), ms_timeout);
+ return 0;
+}
+
+int get_bypass_wd_sd(int ifindex, int *timeout)
+{
+ return get_bypass_wd_fn(get_dev_idx_p(ifindex), timeout);
+
+}
+
+int get_wd_expire_time_sd(int ifindex, int *time_left)
+{
+ return get_wd_expire_time_fn(get_dev_idx_p(ifindex), time_left);
+}
+
+int reset_bypass_wd_timer_sd(int ifindex)
+{
+ return reset_bypass_wd_timer_fn(get_dev_idx_p(ifindex));
+
+}
+
+int get_wd_set_caps_sd(int ifindex)
+{
+ return get_wd_set_caps_fn(get_dev_idx_p(ifindex));
+
+}
+
+int set_std_nic_sd(int ifindex, int nic_mode)
+{
+ return set_std_nic_fn(get_dev_idx_p(ifindex), nic_mode);
+
+}
+
+int get_std_nic_sd(int ifindex)
+{
+ return get_std_nic_fn(get_dev_idx_p(ifindex));
+
+}
+
+int set_tap_sd(int ifindex, int tap_mode)
+{
+ return set_tap_fn(get_dev_idx_p(ifindex), tap_mode);
+
+}
+
+int get_tap_sd(int ifindex)
+{
+ return get_tap_fn(get_dev_idx_p(ifindex));
+
+}
+
+int set_tap_pwup_sd(int ifindex, int tap_mode)
+{
+ return set_tap_pwup_fn(get_dev_idx_p(ifindex), tap_mode);
+
+}
+
+int get_tap_pwup_sd(int ifindex)
+{
+ return get_tap_pwup_fn(get_dev_idx_p(ifindex));
+
+}
+
+int get_tap_change_sd(int ifindex)
+{
+ return get_tap_change_fn(get_dev_idx_p(ifindex));
+
+}
+
+int set_dis_tap_sd(int ifindex, int dis_param)
+{
+ return set_dis_tap_fn(get_dev_idx_p(ifindex), dis_param);
+
+}
+
+int get_dis_tap_sd(int ifindex)
+{
+ return get_dis_tap_fn(get_dev_idx_p(ifindex));
+
+}
+
+int set_bp_disc_sd(int ifindex, int disc_mode)
+{
+ return set_disc_fn(get_dev_idx_p(ifindex), disc_mode);
+
+}
+
+int get_bp_disc_sd(int ifindex)
+{
+ return get_disc_fn(get_dev_idx_p(ifindex));
+
+}
+
+int set_bp_disc_pwup_sd(int ifindex, int disc_mode)
+{
+ return set_disc_pwup_fn(get_dev_idx_p(ifindex), disc_mode);
+
+}
+
+int get_bp_disc_pwup_sd(int ifindex)
+{
+ return get_disc_pwup_fn(get_dev_idx_p(ifindex));
+
+}
+
+int get_bp_disc_change_sd(int ifindex)
+{
+ return get_disc_change_fn(get_dev_idx_p(ifindex));
+
+}
+
+int set_bp_dis_disc_sd(int ifindex, int dis_param)
+{
+ return set_dis_disc_fn(get_dev_idx_p(ifindex), dis_param);
+
+}
+
+int get_bp_dis_disc_sd(int ifindex)
+{
+ return get_dis_disc_fn(get_dev_idx_p(ifindex));
+
+}
+
+int get_wd_exp_mode_sd(int ifindex)
+{
+ return get_wd_exp_mode_fn(get_dev_idx_p(ifindex));
+}
+
+int set_wd_exp_mode_sd(int ifindex, int param)
+{
+ return set_wd_exp_mode_fn(get_dev_idx_p(ifindex), param);
+
+}
+
+int reset_cont_sd(int ifindex)
+{
+ return reset_cont_fn(get_dev_idx_p(ifindex));
+
+}
+
+int set_tx_sd(int ifindex, int tx_state)
+{
+ return set_tx_fn(get_dev_idx_p(ifindex), tx_state);
+
+}
+
+int set_tpl_sd(int ifindex, int tpl_state)
+{
+ return set_tpl_fn(get_dev_idx_p(ifindex), tpl_state);
+
+}
+
+int set_bp_hw_reset_sd(int ifindex, int status)
+{
+ return set_bp_hw_reset_fn(get_dev_idx_p(ifindex), status);
+
+}
+
+int set_wd_autoreset_sd(int ifindex, int param)
+{
+ return set_wd_autoreset_fn(get_dev_idx_p(ifindex), param);
+
+}
+
+int get_wd_autoreset_sd(int ifindex)
+{
+ return get_wd_autoreset_fn(get_dev_idx_p(ifindex));
+
+}
+
+int get_bypass_caps_sd(int ifindex)
+{
+ return get_bypass_caps_fn(get_dev_idx_p(ifindex));
+}
+
+int get_bypass_slave_sd(int ifindex)
+{
+ bpctl_dev_t *pbpctl_dev_out;
+ int ret = get_bypass_slave_fn(get_dev_idx_p(ifindex), &pbpctl_dev_out);
+ if (ret == 1)
+ return pbpctl_dev_out->ifindex;
+ return -1;
+
+}
+
+int get_tx_sd(int ifindex)
+{
+ return get_tx_fn(get_dev_idx_p(ifindex));
+
+}
+
+int get_tpl_sd(int ifindex)
+{
+ return get_tpl_fn(get_dev_idx_p(ifindex));
+
+}
+
+int get_bp_hw_reset_sd(int ifindex)
+{
+ return get_bp_hw_reset_fn(get_dev_idx_p(ifindex));
+
+}
+
+int get_bypass_info_sd(int ifindex, struct bp_info *bp_info)
+{
+ return get_bypass_info_fn(get_dev_idx_p(ifindex), bp_info->prod_name, &bp_info->fw_ver);
+}
+
+int bp_if_scan_sd(void)
+{
+ if_scan_init();
+ return 0;
+}
+
+EXPORT_SYMBOL_NOVERS(is_bypass_sd);
+EXPORT_SYMBOL_NOVERS(get_bypass_slave_sd);
+EXPORT_SYMBOL_NOVERS(get_bypass_caps_sd);
+EXPORT_SYMBOL_NOVERS(get_wd_set_caps_sd);
+EXPORT_SYMBOL_NOVERS(set_bypass_sd);
+EXPORT_SYMBOL_NOVERS(get_bypass_sd);
+EXPORT_SYMBOL_NOVERS(get_bypass_change_sd);
+EXPORT_SYMBOL_NOVERS(set_dis_bypass_sd);
+EXPORT_SYMBOL_NOVERS(get_dis_bypass_sd);
+EXPORT_SYMBOL_NOVERS(set_bypass_pwoff_sd);
+EXPORT_SYMBOL_NOVERS(get_bypass_pwoff_sd);
+EXPORT_SYMBOL_NOVERS(set_bypass_pwup_sd);
+EXPORT_SYMBOL_NOVERS(get_bypass_pwup_sd);
+EXPORT_SYMBOL_NOVERS(set_bypass_wd_sd);
+EXPORT_SYMBOL_NOVERS(get_bypass_wd_sd);
+EXPORT_SYMBOL_NOVERS(get_wd_expire_time_sd);
+EXPORT_SYMBOL_NOVERS(reset_bypass_wd_timer_sd);
+EXPORT_SYMBOL_NOVERS(set_std_nic_sd);
+EXPORT_SYMBOL_NOVERS(get_std_nic_sd);
+EXPORT_SYMBOL_NOVERS(set_tx_sd);
+EXPORT_SYMBOL_NOVERS(get_tx_sd);
+EXPORT_SYMBOL_NOVERS(set_tpl_sd);
+EXPORT_SYMBOL_NOVERS(get_tpl_sd);
+EXPORT_SYMBOL_NOVERS(set_bp_hw_reset_sd);
+EXPORT_SYMBOL_NOVERS(get_bp_hw_reset_sd);
+EXPORT_SYMBOL_NOVERS(set_tap_sd);
+EXPORT_SYMBOL_NOVERS(get_tap_sd);
+EXPORT_SYMBOL_NOVERS(get_tap_change_sd);
+EXPORT_SYMBOL_NOVERS(set_dis_tap_sd);
+EXPORT_SYMBOL_NOVERS(get_dis_tap_sd);
+EXPORT_SYMBOL_NOVERS(set_tap_pwup_sd);
+EXPORT_SYMBOL_NOVERS(get_tap_pwup_sd);
+EXPORT_SYMBOL_NOVERS(set_wd_exp_mode_sd);
+EXPORT_SYMBOL_NOVERS(get_wd_exp_mode_sd);
+EXPORT_SYMBOL_NOVERS(set_wd_autoreset_sd);
+EXPORT_SYMBOL_NOVERS(get_wd_autoreset_sd);
+EXPORT_SYMBOL_NOVERS(set_bp_disc_sd);
+EXPORT_SYMBOL_NOVERS(get_bp_disc_sd);
+EXPORT_SYMBOL_NOVERS(get_bp_disc_change_sd);
+EXPORT_SYMBOL_NOVERS(set_bp_dis_disc_sd);
+EXPORT_SYMBOL_NOVERS(get_bp_dis_disc_sd);
+EXPORT_SYMBOL_NOVERS(set_bp_disc_pwup_sd);
+EXPORT_SYMBOL_NOVERS(get_bp_disc_pwup_sd);
+EXPORT_SYMBOL_NOVERS(get_bypass_info_sd);
+EXPORT_SYMBOL_NOVERS(bp_if_scan_sd);
+
+#define BP_PROC_DIR "bypass"
+
+#define GPIO6_SET_ENTRY_SD "gpio6_set"
+#define GPIO6_CLEAR_ENTRY_SD "gpio6_clear"
+
+#define GPIO7_SET_ENTRY_SD "gpio7_set"
+#define GPIO7_CLEAR_ENTRY_SD "gpio7_clear"
+
+#define PULSE_SET_ENTRY_SD "pulse_set"
+#define ZERO_SET_ENTRY_SD "zero_set"
+#define PULSE_GET1_ENTRY_SD "pulse_get1"
+#define PULSE_GET2_ENTRY_SD "pulse_get2"
+
+#define CMND_ON_ENTRY_SD "cmnd_on"
+#define CMND_OFF_ENTRY_SD "cmnd_off"
+#define RESET_CONT_ENTRY_SD "reset_cont"
+
+ /*COMMANDS*/
+#define BYPASS_INFO_ENTRY_SD "bypass_info"
+#define BYPASS_SLAVE_ENTRY_SD "bypass_slave"
+#define BYPASS_CAPS_ENTRY_SD "bypass_caps"
+#define WD_SET_CAPS_ENTRY_SD "wd_set_caps"
+#define BYPASS_ENTRY_SD "bypass"
+#define BYPASS_CHANGE_ENTRY_SD "bypass_change"
+#define BYPASS_WD_ENTRY_SD "bypass_wd"
+#define WD_EXPIRE_TIME_ENTRY_SD "wd_expire_time"
+#define RESET_BYPASS_WD_ENTRY_SD "reset_bypass_wd"
+#define DIS_BYPASS_ENTRY_SD "dis_bypass"
+#define BYPASS_PWUP_ENTRY_SD "bypass_pwup"
+#define BYPASS_PWOFF_ENTRY_SD "bypass_pwoff"
+#define STD_NIC_ENTRY_SD "std_nic"
+#define STD_NIC_ENTRY_SD "std_nic"
+#define TAP_ENTRY_SD "tap"
+#define TAP_CHANGE_ENTRY_SD "tap_change"
+#define DIS_TAP_ENTRY_SD "dis_tap"
+#define TAP_PWUP_ENTRY_SD "tap_pwup"
+#define TWO_PORT_LINK_ENTRY_SD "two_port_link"
+#define WD_EXP_MODE_ENTRY_SD "wd_exp_mode"
+#define WD_AUTORESET_ENTRY_SD "wd_autoreset"
+#define TPL_ENTRY_SD "tpl"
+#define WAIT_AT_PWUP_ENTRY_SD "wait_at_pwup"
+#define HW_RESET_ENTRY_SD "hw_reset"
+#define DISC_ENTRY_SD "disc"
+#define DISC_CHANGE_ENTRY_SD "disc_change"
+#define DIS_DISC_ENTRY_SD "dis_disc"
+#define DISC_PWUP_ENTRY_SD "disc_pwup"
+static struct proc_dir_entry *bp_procfs_dir;
+
+static struct proc_dir_entry *proc_getdir(char *name,
+ struct proc_dir_entry *proc_dir)
+{
+ struct proc_dir_entry *pde = proc_dir;
+
+ for (pde = pde->subdir; pde; pde = pde->next) {
+ if (pde->namelen && (strcmp(name, pde->name) == 0)) {
+ /* directory exists */
+ break;
+ }
+ }
+ if (pde == (struct proc_dir_entry *)0) {
+ /* create the directory */
+#if (LINUX_VERSION_CODE > 0x20300)
+ pde = proc_mkdir(name, proc_dir);
+#else
+ pde = create_proc_entry(name, S_IFDIR, proc_dir);
+#endif
+ if (pde == (struct proc_dir_entry *)0) {
+
+ return pde;
+ }
+ }
+
+ return pde;
+}
+
+int bp_proc_create(void)
+{
+ bp_procfs_dir = proc_getdir(BP_PROC_DIR, init_net.proc_net);
+ if (bp_procfs_dir == (struct proc_dir_entry *)0) {
+ printk(KERN_DEBUG
+ "Could not create procfs nicinfo directory %s\n",
+ BP_PROC_DIR);
+ return -1;
+ }
+ return 0;
+}
+
+int
+bypass_proc_create_entry_sd(struct pfs_unit_sd *pfs_unit_curr,
+ char *proc_name,
+ write_proc_t *write_proc,
+ read_proc_t *read_proc,
+ struct proc_dir_entry *parent_pfs, void *data)
+{
+ strcpy(pfs_unit_curr->proc_name, proc_name);
+ pfs_unit_curr->proc_entry = create_proc_entry(pfs_unit_curr->proc_name,
+ S_IFREG | S_IRUSR |
+ S_IWUSR | S_IRGRP |
+ S_IROTH, parent_pfs);
+ if (pfs_unit_curr->proc_entry == NULL)
+ return -1;
+
+ pfs_unit_curr->proc_entry->read_proc = read_proc;
+ pfs_unit_curr->proc_entry->write_proc = write_proc;
+ pfs_unit_curr->proc_entry->data = data;
+
+ return 0;
+
+}
+
+int
+get_bypass_info_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+ int len = 0;
+
+ len += sprintf(page, "Name\t\t\t%s\n", pbp_device_block->name);
+ len +=
+ sprintf(page + len, "Firmware version\t0x%x\n",
+ pbp_device_block->bp_fw_ver);
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_bypass_slave_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0;
+ bpctl_dev_t *pbp_device_block_slave = NULL;
+ int idx_dev = 0;
+ struct net_device *net_slave_dev = NULL;
+
+ if ((pbp_device_block->func == 0) || (pbp_device_block->func == 2)) {
+ for (idx_dev = 0;
+ ((bpctl_dev_arr[idx_dev].pdev != NULL)
+ && (idx_dev < device_num)); idx_dev++) {
+ if ((bpctl_dev_arr[idx_dev].bus ==
+ pbp_device_block->bus)
+ && (bpctl_dev_arr[idx_dev].slot ==
+ pbp_device_block->slot)) {
+ if ((pbp_device_block->func == 0)
+ && (bpctl_dev_arr[idx_dev].func == 1)) {
+ pbp_device_block_slave =
+ &bpctl_dev_arr[idx_dev];
+ break;
+ }
+ if ((pbp_device_block->func == 2) &&
+ (bpctl_dev_arr[idx_dev].func == 3)) {
+ pbp_device_block_slave =
+ &bpctl_dev_arr[idx_dev];
+ break;
+ }
+ }
+ }
+ } else
+ pbp_device_block_slave = pbp_device_block;
+ if (!pbp_device_block_slave) {
+ len = sprintf(page, "fail\n");
+ *eof = 1;
+ return len;
+ }
+ net_slave_dev = pbp_device_block_slave->ndev;
+ if (net_slave_dev) {
+ if (net_slave_dev)
+ len = sprintf(page, "%s\n", net_slave_dev->name);
+ else
+ len = sprintf(page, "fail\n");
+
+ }
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_bypass_caps_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bypass_caps_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "-1\n");
+ else
+ len = sprintf(page, "0x%x\n", ret);
+ *eof = 1;
+ return len;
+
+}
+
+int
+get_wd_set_caps_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_wd_set_caps_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "-1\n");
+ else
+ len = sprintf(page, "0x%x\n", ret);
+ *eof = 1;
+ return len;
+}
+
+int
+set_bypass_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ bypass_param = 0;
+
+ set_bypass_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+set_tap_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_tap_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+set_disc_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_disc_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+get_bypass_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bypass_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_tap_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_tap_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_disc_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_disc_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_bypass_change_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bypass_change_fn(pbp_device_block);
+ if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "fail\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_tap_change_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_tap_change_fn(pbp_device_block);
+ if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "fail\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_disc_change_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_disc_change_fn(pbp_device_block);
+ if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "fail\n");
+
+ *eof = 1;
+ return len;
+}
+
+#define isdigit(c) (c >= '0' && c <= '9')
+__inline static int atoi(char **s)
+{
+ int i = 0;
+ while (isdigit(**s))
+ i = i * 10 + *((*s)++) - '0';
+ return i;
+}
+
+int
+set_bypass_wd_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+ int timeout;
+ int ret;
+
+ ret = kstrtoint_from_user(buffer, count, 10, &timeout);
+ if (ret)
+ return ret;
+ set_bypass_wd_fn(pbp_device_block, timeout);
+
+ return count;
+}
+
+int
+get_bypass_wd_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0, timeout = 0;
+
+ ret = get_bypass_wd_fn(pbp_device_block, &timeout);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (timeout == -1)
+ len = sprintf(page, "unknown\n");
+ else if (timeout == 0)
+ len = sprintf(page, "disable\n");
+ else
+ len = sprintf(page, "%d\n", timeout);
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_wd_expire_time_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0, timeout = 0;
+
+ ret = get_wd_expire_time_fn(pbp_device_block, &timeout);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (timeout == -1)
+ len = sprintf(page, "expire\n");
+ else if (timeout == 0)
+ len = sprintf(page, "disable\n");
+
+ else
+ len = sprintf(page, "%d\n", timeout);
+ *eof = 1;
+ return len;
+}
+
+int
+get_tpl_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_tpl_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+#ifdef PMC_FIX_FLAG
+int
+get_wait_at_pwup_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bp_wait_at_pwup_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_hw_reset_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bp_hw_reset_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+#endif /*PMC_WAIT_FLAG */
+
+int
+reset_bypass_wd_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = reset_bypass_wd_timer_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "disable\n");
+ else if (ret == 1)
+ len = sprintf(page, "success\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+set_dis_bypass_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ bypass_param = 0;
+
+ set_dis_bypass_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+set_dis_tap_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_dis_tap_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+set_dis_disc_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_dis_disc_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+get_dis_bypass_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_dis_bypass_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_dis_tap_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_dis_tap_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_dis_disc_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_dis_disc_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+set_bypass_pwup_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ bypass_param = 0;
+
+ set_bypass_pwup_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+set_bypass_pwoff_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ bypass_param = 0;
+
+ set_bypass_pwoff_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+set_tap_pwup_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_tap_pwup_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+set_disc_pwup_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_disc_pwup_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+get_bypass_pwup_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bypass_pwup_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_bypass_pwoff_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bypass_pwoff_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_tap_pwup_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_tap_pwup_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_disc_pwup_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_disc_pwup_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+set_std_nic_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (count >= sizeof(kbuf))
+ return -EINVAL;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ bypass_param = 0;
+
+ set_std_nic_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+get_std_nic_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_std_nic_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_wd_exp_mode_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_wd_exp_mode_fn(pbp_device_block);
+ if (ret == 1)
+ len = sprintf(page, "tap\n");
+ else if (ret == 0)
+ len = sprintf(page, "bypass\n");
+ else if (ret == 2)
+ len = sprintf(page, "disc\n");
+
+ else
+ len = sprintf(page, "fail\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+set_wd_exp_mode_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "tap") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "bypass") == 0)
+ bypass_param = 0;
+ else if (strcmp(kbuf, "disc") == 0)
+ bypass_param = 2;
+
+ set_wd_exp_mode_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+get_wd_autoreset_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_wd_autoreset_fn(pbp_device_block);
+ if (ret >= 0)
+ len = sprintf(page, "%d\n", ret);
+ else
+ len = sprintf(page, "fail\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+set_wd_autoreset_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+ int timeout;
+ int ret;
+
+ ret = kstrtoint_from_user(buffer, count, 10, &timeout);
+ if (ret)
+ return ret;
+ set_wd_autoreset_fn(pbp_device_block, timeout);
+
+ return count;
+}
+
+int
+set_tpl_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tpl_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tpl_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tpl_param = 0;
+
+ set_tpl_fn(pbp_device_block, tpl_param);
+
+ return count;
+}
+
+#ifdef PMC_FIX_FLAG
+int
+set_wait_at_pwup_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tpl_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tpl_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tpl_param = 0;
+
+ set_bp_wait_at_pwup_fn(pbp_device_block, tpl_param);
+
+ return count;
+}
+
+int
+set_hw_reset_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tpl_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tpl_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tpl_param = 0;
+
+ set_bp_hw_reset_fn(pbp_device_block, tpl_param);
+
+ return count;
+}
+
+#endif /*PMC_FIX_FLAG */
+
+int bypass_proc_create_dev_sd(bpctl_dev_t *pbp_device_block)
+{
+ struct bypass_pfs_sd *current_pfs = &(pbp_device_block->bypass_pfs_set);
+ static struct proc_dir_entry *procfs_dir = NULL;
+ int ret = 0;
+
+ if (!pbp_device_block->ndev)
+ return -1;
+ sprintf(current_pfs->dir_name, "bypass_%s",
+ pbp_device_block->ndev->name);
+
+ if (!bp_procfs_dir)
+ return -1;
+
+ /* create device proc dir */
+ procfs_dir = proc_getdir(current_pfs->dir_name, bp_procfs_dir);
+ if (procfs_dir == 0) {
+ printk(KERN_DEBUG "Could not create procfs directory %s\n",
+ current_pfs->dir_name);
+ return -1;
+ }
+ current_pfs->bypass_entry = procfs_dir;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_info), BYPASS_INFO_ENTRY_SD, NULL, /* write */
+ get_bypass_info_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (pbp_device_block->bp_caps & SW_CTL_CAP) {
+
+ /* Create set param proc's */
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_slave), BYPASS_SLAVE_ENTRY_SD, NULL, /* write */
+ get_bypass_slave_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_caps), BYPASS_CAPS_ENTRY_SD, NULL, /* write */
+ get_bypass_caps_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->wd_set_caps), WD_SET_CAPS_ENTRY_SD, NULL, /* write */
+ get_wd_set_caps_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_wd), BYPASS_WD_ENTRY_SD, set_bypass_wd_pfs, /* write */
+ get_bypass_wd_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->wd_expire_time), WD_EXPIRE_TIME_ENTRY_SD, NULL, /* write */
+ get_wd_expire_time_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->reset_bypass_wd), RESET_BYPASS_WD_ENTRY_SD, NULL, /* write */
+ reset_bypass_wd_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->std_nic), STD_NIC_ENTRY_SD, set_std_nic_pfs, /* write */
+ get_std_nic_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (pbp_device_block->bp_caps & BP_CAP) {
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass), BYPASS_ENTRY_SD, set_bypass_pfs, /* write */
+ get_bypass_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->dis_bypass), DIS_BYPASS_ENTRY_SD, set_dis_bypass_pfs, /* write */
+ get_dis_bypass_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_pwup), BYPASS_PWUP_ENTRY_SD, set_bypass_pwup_pfs, /* write */
+ get_bypass_pwup_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_pwoff), BYPASS_PWOFF_ENTRY_SD, set_bypass_pwoff_pfs, /* write */
+ get_bypass_pwoff_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_change), BYPASS_CHANGE_ENTRY_SD, NULL, /* write */
+ get_bypass_change_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+ }
+
+ if (pbp_device_block->bp_caps & TAP_CAP) {
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap), TAP_ENTRY_SD, set_tap_pfs, /* write */
+ get_tap_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->dis_tap), DIS_TAP_ENTRY_SD, set_dis_tap_pfs, /* write */
+ get_dis_tap_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap_pwup), TAP_PWUP_ENTRY_SD, set_tap_pwup_pfs, /* write */
+ get_tap_pwup_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap_change), TAP_CHANGE_ENTRY_SD, NULL, /* write */
+ get_tap_change_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+ }
+ if (pbp_device_block->bp_caps & DISC_CAP) {
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap), DISC_ENTRY_SD, set_disc_pfs, /* write */
+ get_disc_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+#if 1
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->dis_tap), DIS_DISC_ENTRY_SD, set_dis_disc_pfs, /* write */
+ get_dis_disc_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+#endif
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap_pwup), DISC_PWUP_ENTRY_SD, set_disc_pwup_pfs, /* write */
+ get_disc_pwup_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap_change), DISC_CHANGE_ENTRY_SD, NULL, /* write */
+ get_disc_change_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+ }
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->wd_exp_mode), WD_EXP_MODE_ENTRY_SD, set_wd_exp_mode_pfs, /* write */
+ get_wd_exp_mode_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->wd_autoreset), WD_AUTORESET_ENTRY_SD, set_wd_autoreset_pfs, /* write */
+ get_wd_autoreset_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+ if (bypass_proc_create_entry_sd(&(current_pfs->tpl), TPL_ENTRY_SD, set_tpl_pfs, /* write */
+ get_tpl_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+#ifdef PMC_FIX_FLAG
+ if (bypass_proc_create_entry_sd(&(current_pfs->tpl), WAIT_AT_PWUP_ENTRY_SD, set_wait_at_pwup_pfs, /* write */
+ get_wait_at_pwup_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+ if (bypass_proc_create_entry_sd(&(current_pfs->tpl), HW_RESET_ENTRY_SD, set_hw_reset_pfs, /* write */
+ get_hw_reset_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+#endif
+
+ }
+ if (ret < 0)
+ printk(KERN_DEBUG "Create proc entry failed\n");
+
+ return ret;
+}
+
+int bypass_proc_remove_dev_sd(bpctl_dev_t *pbp_device_block)
+{
+
+ struct bypass_pfs_sd *current_pfs = &pbp_device_block->bypass_pfs_set;
+ struct proc_dir_entry *pde = current_pfs->bypass_entry, *pde_curr =
+ NULL;
+ char name[256];
+
+ if (!pde)
+ return 0;
+ for (pde = pde->subdir; pde;) {
+ strcpy(name, pde->name);
+ pde_curr = pde;
+ pde = pde->next;
+ remove_proc_entry(name, current_pfs->bypass_entry);
+ }
+ if (!pde)
+ remove_proc_entry(current_pfs->dir_name, bp_procfs_dir);
+ current_pfs->bypass_entry = NULL;
+
+ return 0;
+}
diff --git a/drivers/staging/silicom/bp_mod.h b/drivers/staging/silicom/bp_mod.h
new file mode 100644
index 000000000000..b8275f5611fa
--- /dev/null
+++ b/drivers/staging/silicom/bp_mod.h
@@ -0,0 +1,704 @@
+/******************************************************************************/
+/* */
+/* Bypass Control utility, Copyright (c) 2005 Silicom */
+/* */
+/* This program is free software; you can redistribute it and/or modify */
+/* it under the terms of the GNU General Public License as published by */
+/* the Free Software Foundation, located in the file LICENSE. */
+/* */
+/* */
+/* bp_mod.h */
+/* */
+/******************************************************************************/
+
+#ifndef BP_MOD_H
+#define BP_MOD_H
+#include "bits.h"
+
+#define EXPORT_SYMBOL_NOVERS EXPORT_SYMBOL
+
+#define usec_delay(x) udelay(x)
+#ifndef msec_delay_bp
+#define msec_delay_bp(x) \
+do { \
+ int i; \
+ if (1) { \
+ for (i = 0; i < 1000; i++) { \
+ udelay(x) ; \
+ } \
+ } else { \
+ msleep(x); \
+ } \
+} while (0)
+
+#endif
+
+#include <linux/param.h>
+
+#ifndef jiffies_to_msecs
+#define jiffies_to_msecs(x) _kc_jiffies_to_msecs(x)
+static inline unsigned int jiffies_to_msecs(const unsigned long j)
+{
+#if HZ <= 1000 && !(1000 % HZ)
+ return (1000 / HZ) * j;
+#elif HZ > 1000 && !(HZ % 1000)
+ return (j + (HZ / 1000) - 1) / (HZ / 1000);
+#else
+ return (j * 1000) / HZ;
+#endif
+}
+#endif
+
+#define SILICOM_VID 0x1374
+#define SILICOM_SVID 0x1374
+
+#define SILICOM_PXG2BPFI_SSID 0x0026
+#define SILICOM_PXG2BPFILX_SSID 0x0027
+#define SILICOM_PXGBPI_SSID 0x0028
+#define SILICOM_PXGBPIG_SSID 0x0029
+#define SILICOM_PXG2TBFI_SSID 0x002a
+#define SILICOM_PXG4BPI_SSID 0x002c
+#define SILICOM_PXG4BPFI_SSID 0x002d
+#define SILICOM_PXG4BPFILX_SSID 0x002e
+#define SILICOM_PXG2BPFIL_SSID 0x002F
+#define SILICOM_PXG2BPFILLX_SSID 0x0030
+#define SILICOM_PEG4BPI_SSID 0x0031
+#define SILICOM_PEG2BPI_SSID 0x0037
+#define SILICOM_PEG4BPIN_SSID 0x0038
+#define SILICOM_PEG2BPFI_SSID 0x0039
+#define SILICOM_PEG2BPFILX_SSID 0x003A
+#define SILICOM_PMCXG2BPFI_SSID 0x003B
+#define NOKIA_PMCXG2BPFIN_SSID 0x0510
+#define NOKIA_PMCXG2BPIN_SSID 0x0513
+#define NOKIA_PMCXG4BPIN_SSID 0x0514
+#define NOKIA_PMCXG2BPFIN_SVID 0x13B8
+#define NOKIA_PMCXG2BPIN2_SSID 0x0515
+#define NOKIA_PMCXG4BPIN2_SSID 0x0516
+#define SILICOM_PMCX2BPI_SSID 0x041
+#define SILICOM_PMCX4BPI_SSID 0x042
+#define SILICOM_PXG2BISC1_SSID 0x003d
+#define SILICOM_PEG2TBFI_SSID 0x003E
+#define SILICOM_PXG2TBI_SSID 0x003f
+#define SILICOM_PXG4BPFID_SSID 0x0043
+#define SILICOM_PEG4BPFI_SSID 0x0040
+#define SILICOM_PEG4BPIPT_SSID 0x0044
+#define SILICOM_PXG6BPI_SSID 0x0045
+#define SILICOM_PEG4BPIL_SSID 0x0046
+#define SILICOM_PEG2BPI5_SSID 0x0052
+#define SILICOM_PEG6BPI_SSID 0x0053
+#define SILICOM_PEG4BPFI5_SSID 0x0050
+#define SILICOM_PEG4BPFI5LX_SSID 0x0051
+#define SILICOM_PEG2BISC6_SSID 0x54
+
+#define SILICOM_PEG6BPIFC_SSID 0x55
+
+#define SILICOM_PEG2BPFI5_SSID 0x0056
+#define SILICOM_PEG2BPFI5LX_SSID 0x0057
+
+#define SILICOM_PXEG4BPFI_SSID 0x0058
+
+#define SILICOM_PEG2BPFID_SSID 0x0047
+#define SILICOM_PEG2BPFIDLX_SSID 0x004C
+#define SILICOM_MEG2BPFILN_SSID 0x0048
+#define SILICOM_MEG2BPFINX_SSID 0x0049
+#define SILICOM_PEG4BPFILX_SSID 0x004A
+#define SILICOM_MHIO8AD_SSID 0x004F
+
+#define SILICOM_MEG2BPFILXLN_SSID 0x004b
+#define SILICOM_PEG2BPIX1_SSID 0x004d
+#define SILICOM_MEG2BPFILXNX_SSID 0x004e
+
+#define SILICOM_PE10G2BPISR_SSID 0x0102
+#define SILICOM_PE10G2BPILR_SSID 0x0103
+#define SILICOM_PE10G2BPICX4_SSID 0x0101
+
+#define SILICOM_XE10G2BPILR_SSID 0x0163
+#define SILICOM_XE10G2BPISR_SSID 0x0162
+#define SILICOM_XE10G2BPICX4_SSID 0x0161
+#define SILICOM_XE10G2BPIT_SSID 0x0160
+
+#define SILICOM_PE10GDBISR_SSID 0x0181
+#define SILICOM_PE10GDBILR_SSID 0x0182
+
+#define SILICOM_PE210G2DBi9SR_SSID 0x0188
+#define SILICOM_PE210G2DBi9SRRB_SSID 0x0188
+#define SILICOM_PE210G2DBi9LR_SSID 0x0189
+#define SILICOM_PE210G2DBi9LRRB_SSID 0x0189
+#define SILICOM_PE310G4DBi940SR_SSID 0x018C
+
+#define SILICOM_PE310G4BPi9T_SSID 0x130
+#define SILICOM_PE310G4BPi9SR_SSID 0x132
+#define SILICOM_PE310G4BPi9LR_SSID 0x133
+
+#define NOKIA_XE10G2BPIXR_SVID 0x13B8
+#define NOKIA_XE10G2BPIXR_SSID 0x051C
+
+#define INTEL_PEG4BPII_PID 0x10A0
+#define INTEL_PEG4BPFII_PID 0x10A1
+#define INTEL_PEG4BPII_SSID 0x11A0
+#define INTEL_PEG4BPFII_SSID 0x11A1
+
+#define INTEL_PEG4BPIIO_SSID 0x10A0
+#define INTEL_PEG4BPIIO_PID 0x105e
+
+#define BROADCOM_VID 0x14e4
+#define BROADCOM_PE10G2_PID 0x164e
+
+#define SILICOM_PE10G2BPTCX4_SSID 0x0141
+#define SILICOM_PE10G2BPTSR_SSID 0x0142
+#define SILICOM_PE10G2BPTLR_SSID 0x0143
+#define SILICOM_PE10G2BPTT_SSID 0x0140
+
+#define SILICOM_PEG4BPI6_SSID 0x0320
+#define SILICOM_PEG4BPFI6_SSID 0x0321
+#define SILICOM_PEG4BPFI6LX_SSID 0x0322
+#define SILICOM_PEG4BPFI6ZX_SSID 0x0323
+
+#define SILICOM_PEG2BPI6_SSID 0x0300
+#define SILICOM_PEG2BPFI6_SSID 0x0301
+#define SILICOM_PEG2BPFI6LX_SSID 0x0302
+#define SILICOM_PEG2BPFI6ZX_SSID 0x0303
+#define SILICOM_PEG2BPFI6FLXM_SSID 0x0304
+
+#define SILICOM_PEG2DBI6_SSID 0x0308
+#define SILICOM_PEG2DBFI6_SSID 0x0309
+#define SILICOM_PEG2DBFI6LX_SSID 0x030A
+#define SILICOM_PEG2DBFI6ZX_SSID 0x030B
+
+#define SILICOM_MEG2BPI6_SSID 0x0310
+#define SILICOM_XEG2BPI6_SSID 0x0318
+#define SILICOM_PEG4BPI6FC_SSID 0x0328
+#define SILICOM_PEG4BPFI6FC_SSID 0x0329
+#define SILICOM_PEG4BPFI6FCLX_SSID 0x032A
+#define SILICOM_PEG4BPFI6FCZX_SSID 0x032B
+
+#define SILICOM_PEG6BPI6_SSID 0x0340
+
+#define SILICOM_PEG2BPI6SC6_SSID 0x0360
+
+#define SILICOM_MEG2BPI6_SSID 0x0310
+#define SILICOM_XEG2BPI6_SSID 0x0318
+#define SILICOM_MEG4BPI6_SSID 0x0330
+
+#define SILICOM_PE2G4BPi80L_SSID 0x0380
+
+#define SILICOM_M6E2G8BPi80A_SSID 0x0474
+
+#define SILICOM_PE2G4BPi35_SSID 0x03d8
+
+#define SILICOM_PE2G4BPFi80_SSID 0x0381
+#define SILICOM_PE2G4BPFi80LX_SSID 0x0382
+#define SILICOM_PE2G4BPFi80ZX_SSID 0x0383
+
+#define SILICOM_PE2G4BPi80_SSID 0x0388
+
+#define SILICOM_PE2G2BPi80_SSID 0x0390
+#define SILICOM_PE2G2BPFi80_SSID 0x0391
+#define SILICOM_PE2G2BPFi80LX_SSID 0x0392
+#define SILICOM_PE2G2BPFi80ZX_SSID 0x0393
+
+#define SILICOM_PE2G4BPi35L_SSID 0x03D0
+#define SILICOM_PE2G4BPFi35_SSID 0x03D1
+#define SILICOM_PE2G4BPFi35LX_SSID 0x03D2
+#define SILICOM_PE2G4BPFi35ZX_SSID 0x03D3
+
+#define SILICOM_PE2G2BPi35_SSID 0x03c0
+#define SILICOM_PAC1200BPi35_SSID 0x03cc
+#define SILICOM_PE2G2BPFi35_SSID 0x03C1
+#define SILICOM_PE2G2BPFi35LX_SSID 0x03C2
+#define SILICOM_PE2G2BPFi35ZX_SSID 0x03C3
+
+#define SILICOM_PE2G6BPi35_SSID 0x03E0
+#define SILICOM_PE2G6BPi35CX_SSID 0x0AA0
+
+#define INTEL_PE210G2SPI9_SSID 0x00C
+
+#define SILICOM_M1EG2BPI6_SSID 0x400
+
+#define SILICOM_M1EG2BPFI6_SSID 0x0401
+#define SILICOM_M1EG2BPFI6LX_SSID 0x0402
+#define SILICOM_M1EG2BPFI6ZX_SSID 0x0403
+
+#define SILICOM_M1EG4BPI6_SSID 0x0420
+
+#define SILICOM_M1EG4BPFI6_SSID 0x0421
+#define SILICOM_M1EG4BPFI6LX_SSID 0x0422
+#define SILICOM_M1EG4BPFI6ZX_SSID 0x0423
+
+#define SILICOM_M1EG6BPI6_SSID 0x0440
+
+#define SILICOM_M1E2G4BPi80_SSID 0x0460
+#define SILICOM_M1E2G4BPFi80_SSID 0x0461
+#define SILICOM_M1E2G4BPFi80LX_SSID 0x0462
+#define SILICOM_M1E2G4BPFi80ZX_SSID 0x0463
+
+#define SILICOM_M6E2G8BPi80_SSID 0x0470
+#define SILICOM_PE210G2BPi40_SSID 0x01a0
+
+#define PEG540_IF_SERIES(pid) \
+ ((pid == SILICOM_PE210G2BPi40_SSID))
+
+#define OLD_IF_SERIES(pid)\
+ ((pid == SILICOM_PXG2BPFI_SSID) || \
+ (pid == SILICOM_PXG2BPFILX_SSID))
+
+#define P2BPFI_IF_SERIES(pid) \
+ ((pid == SILICOM_PXG2BPFI_SSID) || \
+ (pid == SILICOM_PXG2BPFILX_SSID) || \
+ (pid == SILICOM_PEG2BPFI_SSID) || \
+ (pid == SILICOM_PEG2BPFID_SSID) || \
+ (pid == SILICOM_PEG2BPFIDLX_SSID) || \
+ (pid == SILICOM_MEG2BPFILN_SSID) || \
+ (pid == SILICOM_MEG2BPFINX_SSID) || \
+ (pid == SILICOM_PEG4BPFILX_SSID) || \
+ (pid == SILICOM_PEG4BPFI_SSID) || \
+ (pid == SILICOM_PXEG4BPFI_SSID) || \
+ (pid == SILICOM_PXG4BPFID_SSID) || \
+ (pid == SILICOM_PEG2TBFI_SSID) || \
+ (pid == SILICOM_PE10G2BPISR_SSID) || \
+ (pid == SILICOM_PE10G2BPILR_SSID) || \
+ (pid == SILICOM_PEG2BPFILX_SSID) || \
+ (pid == SILICOM_PMCXG2BPFI_SSID) || \
+ (pid == SILICOM_MHIO8AD_SSID) || \
+ (pid == SILICOM_PEG4BPFI5LX_SSID) || \
+ (pid == SILICOM_PEG4BPFI5_SSID) || \
+ (pid == SILICOM_PEG4BPFI6FC_SSID) || \
+ (pid == SILICOM_PEG4BPFI6FCLX_SSID) || \
+ (pid == SILICOM_PEG4BPFI6FCZX_SSID) || \
+ (pid == NOKIA_PMCXG2BPFIN_SSID) || \
+ (pid == SILICOM_MEG2BPFILXLN_SSID) || \
+ (pid == SILICOM_MEG2BPFILXNX_SSID) || \
+ (pid == SILICOM_XE10G2BPIT_SSID) || \
+ (pid == SILICOM_XE10G2BPICX4_SSID) || \
+ (pid == SILICOM_XE10G2BPISR_SSID) || \
+ (pid == NOKIA_XE10G2BPIXR_SSID) || \
+ (pid == SILICOM_PE10GDBISR_SSID) || \
+ (pid == SILICOM_PE10GDBILR_SSID) || \
+ (pid == SILICOM_XE10G2BPILR_SSID))
+
+#define INTEL_IF_SERIES(pid) \
+ ((pid == INTEL_PEG4BPII_SSID) || \
+ (pid == INTEL_PEG4BPIIO_SSID) || \
+ (pid == INTEL_PEG4BPFII_SSID))
+
+#define NOKIA_SERIES(pid) \
+ ((pid == NOKIA_PMCXG2BPIN_SSID) || \
+ (pid == NOKIA_PMCXG4BPIN_SSID) || \
+ (pid == SILICOM_PMCX4BPI_SSID) || \
+ (pid == NOKIA_PMCXG2BPFIN_SSID) || \
+ (pid == SILICOM_PMCXG2BPFI_SSID) || \
+ (pid == NOKIA_PMCXG2BPIN2_SSID) || \
+ (pid == NOKIA_PMCXG4BPIN2_SSID) || \
+ (pid == SILICOM_PMCX2BPI_SSID))
+
+#define DISCF_IF_SERIES(pid) \
+ (pid == SILICOM_PEG2TBFI_SSID)
+
+#define PEGF_IF_SERIES(pid) \
+ ((pid == SILICOM_PEG2BPFI_SSID) || \
+ (pid == SILICOM_PEG2BPFID_SSID) || \
+ (pid == SILICOM_PEG2BPFIDLX_SSID) || \
+ (pid == SILICOM_PEG2BPFILX_SSID) || \
+ (pid == SILICOM_PEG4BPFI_SSID) || \
+ (pid == SILICOM_PXEG4BPFI_SSID) || \
+ (pid == SILICOM_MEG2BPFILN_SSID) || \
+ (pid == SILICOM_MEG2BPFINX_SSID) || \
+ (pid == SILICOM_PEG4BPFILX_SSID) || \
+ (pid == SILICOM_PEG2TBFI_SSID) || \
+ (pid == SILICOM_MEG2BPFILXLN_SSID) || \
+ (pid == SILICOM_MEG2BPFILXNX_SSID))
+
+#define TPL_IF_SERIES(pid) \
+ ((pid == SILICOM_PXG2BPFIL_SSID) || \
+ (pid == SILICOM_PXG2BPFILLX_SSID) || \
+ (pid == SILICOM_PXG2TBFI_SSID) || \
+ (pid == SILICOM_PXG4BPFID_SSID) || \
+ (pid == SILICOM_PXG4BPFI_SSID))
+
+#define BP10G_IF_SERIES(pid) \
+ ((pid == SILICOM_PE10G2BPISR_SSID) || \
+ (pid == SILICOM_PE10G2BPICX4_SSID) || \
+ (pid == SILICOM_PE10G2BPILR_SSID) || \
+ (pid == SILICOM_XE10G2BPIT_SSID) || \
+ (pid == SILICOM_XE10G2BPICX4_SSID) || \
+ (pid == SILICOM_XE10G2BPISR_SSID) || \
+ (pid == NOKIA_XE10G2BPIXR_SSID) || \
+ (pid == SILICOM_PE10GDBISR_SSID) || \
+ (pid == SILICOM_PE10GDBILR_SSID) || \
+ (pid == SILICOM_XE10G2BPILR_SSID))
+
+#define BP10GB_IF_SERIES(pid) \
+ ((pid == SILICOM_PE10G2BPTCX4_SSID) || \
+ (pid == SILICOM_PE10G2BPTSR_SSID) || \
+ (pid == SILICOM_PE10G2BPTLR_SSID) || \
+ (pid == SILICOM_PE10G2BPTT_SSID))
+
+#define BP10G_CX4_SERIES(pid) \
+ (pid == SILICOM_PE10G2BPICX4_SSID)
+
+#define BP10GB_CX4_SERIES(pid) \
+ (pid == SILICOM_PE10G2BPTCX4_SSID)
+
+#define SILICOM_M2EG2BPFI6_SSID 0x0501
+#define SILICOM_M2EG2BPFI6LX_SSID 0x0502
+#define SILICOM_M2EG2BPFI6ZX_SSID 0x0503
+#define SILICOM_M2EG4BPI6_SSID 0x0520
+
+#define SILICOM_M2EG4BPFI6_SSID 0x0521
+#define SILICOM_M2EG4BPFI6LX_SSID 0x0522
+#define SILICOM_M2EG4BPFI6ZX_SSID 0x0523
+
+#define SILICOM_M2EG6BPI6_SSID 0x0540
+
+#define SILICOM_M1E10G2BPI9CX4_SSID 0x481
+#define SILICOM_M1E10G2BPI9SR_SSID 0x482
+#define SILICOM_M1E10G2BPI9LR_SSID 0x483
+#define SILICOM_M1E10G2BPI9T_SSID 0x480
+
+#define SILICOM_M2E10G2BPI9CX4_SSID 0x581
+#define SILICOM_M2E10G2BPI9SR_SSID 0x582
+#define SILICOM_M2E10G2BPI9LR_SSID 0x583
+#define SILICOM_M2E10G2BPI9T_SSID 0x580
+
+#define SILICOM_PE210G2BPI9CX4_SSID 0x121
+#define SILICOM_PE210G2BPI9SR_SSID 0x122
+#define SILICOM_PE210G2BPI9LR_SSID 0x123
+#define SILICOM_PE210G2BPI9T_SSID 0x120
+
+#define DBI_IF_SERIES(pid) \
+ ((pid == SILICOM_PE10GDBISR_SSID) || \
+ (pid == SILICOM_PE10GDBILR_SSID) || \
+ (pid == SILICOM_XE10G2BPILR_SSID) || \
+ (pid == SILICOM_PE210G2DBi9LR_SSID))
+
+#define PEGF5_IF_SERIES(pid) \
+ ((pid == SILICOM_PEG2BPFI5_SSID) || \
+ (pid == SILICOM_PEG2BPFI5LX_SSID) || \
+ (pid == SILICOM_PEG4BPFI6_SSID) || \
+ (pid == SILICOM_PEG4BPFI6LX_SSID) || \
+ (pid == SILICOM_PEG4BPFI6ZX_SSID) || \
+ (pid == SILICOM_PEG2BPFI6_SSID) || \
+ (pid == SILICOM_PEG2BPFI6LX_SSID) || \
+ (pid == SILICOM_PEG2BPFI6ZX_SSID) || \
+ (pid == SILICOM_PEG2BPFI6FLXM_SSID) || \
+ (pid == SILICOM_PEG2DBFI6_SSID) || \
+ (pid == SILICOM_PEG2DBFI6LX_SSID) || \
+ (pid == SILICOM_PEG2DBFI6ZX_SSID) || \
+ (pid == SILICOM_PEG4BPI6FC_SSID) || \
+ (pid == SILICOM_PEG4BPFI6FCLX_SSID) || \
+ (pid == SILICOM_PEG4BPI6FC_SSID) || \
+ (pid == SILICOM_M1EG2BPFI6_SSID) || \
+ (pid == SILICOM_M1EG2BPFI6LX_SSID) || \
+ (pid == SILICOM_M1EG2BPFI6ZX_SSID) || \
+ (pid == SILICOM_M1EG4BPFI6_SSID) || \
+ (pid == SILICOM_M1EG4BPFI6LX_SSID) || \
+ (pid == SILICOM_M1EG4BPFI6ZX_SSID) || \
+ (pid == SILICOM_M2EG2BPFI6_SSID) || \
+ (pid == SILICOM_M2EG2BPFI6LX_SSID) || \
+ (pid == SILICOM_M2EG2BPFI6ZX_SSID) || \
+ (pid == SILICOM_M2EG4BPFI6_SSID) || \
+ (pid == SILICOM_M2EG4BPFI6LX_SSID) || \
+ (pid == SILICOM_M2EG4BPFI6ZX_SSID) || \
+ (pid == SILICOM_PEG4BPFI6FCZX_SSID))
+
+#define PEG5_IF_SERIES(pid) \
+ ((pid == SILICOM_PEG4BPI6_SSID) || \
+ (pid == SILICOM_PEG2BPI6_SSID) || \
+ (pid == SILICOM_PEG4BPI6FC_SSID) || \
+ (pid == SILICOM_PEG6BPI6_SSID) || \
+ (pid == SILICOM_PEG2BPI6SC6_SSID) || \
+ (pid == SILICOM_MEG2BPI6_SSID) || \
+ (pid == SILICOM_XEG2BPI6_SSID) || \
+ (pid == SILICOM_MEG4BPI6_SSID) || \
+ (pid == SILICOM_M1EG2BPI6_SSID) || \
+ (pid == SILICOM_M1EG4BPI6_SSID) || \
+ (pid == SILICOM_M1EG6BPI6_SSID) || \
+ (pid == SILICOM_PEG6BPI_SSID) || \
+ (pid == SILICOM_PEG4BPIL_SSID) || \
+ (pid == SILICOM_PEG2BISC6_SSID) || \
+ (pid == SILICOM_PEG2BPI5_SSID))
+
+#define PEG80_IF_SERIES(pid) \
+ ((pid == SILICOM_M1E2G4BPi80_SSID) || \
+ (pid == SILICOM_M6E2G8BPi80_SSID) || \
+ (pid == SILICOM_PE2G4BPi80L_SSID) || \
+ (pid == SILICOM_M6E2G8BPi80A_SSID) || \
+ (pid == SILICOM_PE2G2BPi35_SSID) || \
+ (pid == SILICOM_PAC1200BPi35_SSID) || \
+ (pid == SILICOM_PE2G4BPi35_SSID) || \
+ (pid == SILICOM_PE2G4BPi35L_SSID) || \
+ (pid == SILICOM_PE2G6BPi35_SSID) || \
+ (pid == SILICOM_PE2G2BPi80_SSID) || \
+ (pid == SILICOM_PE2G4BPi80_SSID) || \
+ (pid == SILICOM_PE2G4BPFi80_SSID) || \
+ (pid == SILICOM_PE2G4BPFi80LX_SSID) || \
+ (pid == SILICOM_PE2G4BPFi80ZX_SSID) || \
+ (pid == SILICOM_PE2G4BPFi80ZX_SSID) || \
+ (pid == SILICOM_PE2G2BPFi80_SSID) || \
+ (pid == SILICOM_PE2G2BPFi80LX_SSID) || \
+ (pid == SILICOM_PE2G2BPFi80ZX_SSID) || \
+ (pid == SILICOM_PE2G2BPFi35_SSID) || \
+ (pid == SILICOM_PE2G2BPFi35LX_SSID) || \
+ (pid == SILICOM_PE2G2BPFi35ZX_SSID) || \
+ (pid == SILICOM_PE2G4BPFi35_SSID) || \
+ (pid == SILICOM_PE2G4BPFi35LX_SSID) || \
+ (pid == SILICOM_PE2G4BPFi35ZX_SSID))
+
+#define PEGF80_IF_SERIES(pid) \
+ ((pid == SILICOM_PE2G4BPFi80_SSID) || \
+ (pid == SILICOM_PE2G4BPFi80LX_SSID) || \
+ (pid == SILICOM_PE2G4BPFi80ZX_SSID) || \
+ (pid == SILICOM_PE2G4BPFi80ZX_SSID) || \
+ (pid == SILICOM_M1E2G4BPFi80_SSID) || \
+ (pid == SILICOM_M1E2G4BPFi80LX_SSID) || \
+ (pid == SILICOM_M1E2G4BPFi80ZX_SSID) || \
+ (pid == SILICOM_PE2G2BPFi80_SSID) || \
+ (pid == SILICOM_PE2G2BPFi80LX_SSID) || \
+ (pid == SILICOM_PE2G2BPFi80ZX_SSID) || \
+ (pid == SILICOM_PE2G2BPFi35_SSID) || \
+ (pid == SILICOM_PE2G2BPFi35LX_SSID) || \
+ (pid == SILICOM_PE2G2BPFi35ZX_SSID) || \
+ (pid == SILICOM_PE2G4BPFi35_SSID) || \
+ (pid == SILICOM_PE2G4BPFi35LX_SSID) || \
+ (pid == SILICOM_PE2G4BPFi35ZX_SSID))
+
+#define BP10G9_IF_SERIES(pid) \
+ ((pid == INTEL_PE210G2SPI9_SSID) || \
+ (pid == SILICOM_M1E10G2BPI9CX4_SSID) || \
+ (pid == SILICOM_M1E10G2BPI9SR_SSID) || \
+ (pid == SILICOM_M1E10G2BPI9LR_SSID) || \
+ (pid == SILICOM_M1E10G2BPI9T_SSID) || \
+ (pid == SILICOM_M2E10G2BPI9CX4_SSID) || \
+ (pid == SILICOM_M2E10G2BPI9SR_SSID) || \
+ (pid == SILICOM_M2E10G2BPI9LR_SSID) || \
+ (pid == SILICOM_M2E10G2BPI9T_SSID) || \
+ (pid == SILICOM_PE210G2BPI9CX4_SSID) || \
+ (pid == SILICOM_PE210G2BPI9SR_SSID) || \
+ (pid == SILICOM_PE210G2BPI9LR_SSID) || \
+ (pid == SILICOM_PE210G2DBi9SR_SSID) || \
+ (pid == SILICOM_PE210G2DBi9SRRB_SSID) || \
+ (pid == SILICOM_PE210G2DBi9LR_SSID) || \
+ (pid == SILICOM_PE210G2DBi9LRRB_SSID) || \
+ (pid == SILICOM_PE310G4DBi940SR_SSID) || \
+ (pid == SILICOM_PEG2BISC6_SSID) || \
+ (pid == SILICOM_PE310G4BPi9T_SSID) || \
+ (pid == SILICOM_PE310G4BPi9SR_SSID) || \
+ (pid == SILICOM_PE310G4BPi9LR_SSID) || \
+ (pid == SILICOM_PE210G2BPI9T_SSID))
+
+/*******************************************************/
+/* 1G INTERFACE ****************************************/
+/*******************************************************/
+
+/* Intel Registers */
+#define BPCTLI_CTRL 0x00000
+#define BPCTLI_CTRL_SWDPIO0 0x00400000
+#define BPCTLI_CTRL_SWDPIN0 0x00040000
+
+#define BPCTLI_CTRL_EXT 0x00018 /* Extended Device Control - RW */
+#define BPCTLI_STATUS 0x00008 /* Device Status - RO */
+
+/* HW related */
+#define BPCTLI_CTRL_EXT_SDP6_DATA 0x00000040 /* Value of SW Defineable Pin 6 */
+#define BPCTLI_CTRL_EXT_SDP7_DATA 0x00000080 /* Value of SW Defineable Pin 7 */
+#define BPCTLI_CTRL_SDP0_DATA 0x00040000 /* SWDPIN 0 value */
+#define BPCTLI_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */
+#define BPCTLI_CTRL_EXT_SDP7_DIR 0x00000800 /* Direction of SDP7 0=in 1=out */
+#define BPCTLI_CTRL_SDP0_DIR 0x00400000 /* SDP0 Input or output */
+#define BPCTLI_CTRL_SWDPIN1 0x00080000
+#define BPCTLI_CTRL_SDP1_DIR 0x00800000
+
+#define BPCTLI_STATUS_LU 0x00000002 /* Link up.0=no,1=link */
+
+#define BPCTLI_CTRL_SDP0_SHIFT 18
+#define BPCTLI_CTRL_EXT_SDP6_SHIFT 6
+
+#define BPCTLI_STATUS_TBIMODE 0x00000020
+#define BPCTLI_CTRL_EXT_LINK_MODE_PCIE_SERDES 0x00C00000
+#define BPCTLI_CTRL_EXT_LINK_MODE_MASK 0x00C00000
+
+#define BPCTLI_CTRL_EXT_MCLK_DIR BPCTLI_CTRL_EXT_SDP7_DIR
+#define BPCTLI_CTRL_EXT_MCLK_DATA BPCTLI_CTRL_EXT_SDP7_DATA
+#define BPCTLI_CTRL_EXT_MDIO_DIR BPCTLI_CTRL_EXT_SDP6_DIR
+#define BPCTLI_CTRL_EXT_MDIO_DATA BPCTLI_CTRL_EXT_SDP6_DATA
+
+#define BPCTLI_CTRL_EXT_MCLK_DIR5 BPCTLI_CTRL_SDP1_DIR
+#define BPCTLI_CTRL_EXT_MCLK_DATA5 BPCTLI_CTRL_SWDPIN1
+#define BPCTLI_CTRL_EXT_MCLK_DIR80 BPCTLI_CTRL_EXT_SDP6_DIR
+#define BPCTLI_CTRL_EXT_MCLK_DATA80 BPCTLI_CTRL_EXT_SDP6_DATA
+#define BPCTLI_CTRL_EXT_MDIO_DIR5 BPCTLI_CTRL_SWDPIO0
+#define BPCTLI_CTRL_EXT_MDIO_DATA5 BPCTLI_CTRL_SWDPIN0
+#define BPCTLI_CTRL_EXT_MDIO_DIR80 BPCTLI_CTRL_SWDPIO0
+#define BPCTLI_CTRL_EXT_MDIO_DATA80 BPCTLI_CTRL_SWDPIN0
+
+#define BPCTL_WRITE_REG(a, reg, value) \
+ (writel((value), (void *)(((a)->mem_map) + BPCTLI_##reg)))
+
+#define BPCTL_READ_REG(a, reg) ( \
+ readl((void *)((a)->mem_map) + BPCTLI_##reg))
+
+#define BPCTL_WRITE_FLUSH(a) BPCTL_READ_REG(a, STATUS)
+
+#define BPCTL_BP_WRITE_REG(a, reg, value) ({ \
+ BPCTL_WRITE_REG(a, reg, value); \
+ BPCTL_WRITE_FLUSH(a); })
+
+/**************************************************************/
+/************** 82575 Interface********************************/
+/**************************************************************/
+
+#define BPCTLI_MII_CR_POWER_DOWN 0x0800
+#define BPCTLI_PHY_CONTROL 0x00 /* Control Register */
+#define BPCTLI_MDIC 0x00020 /* MDI Control - RW */
+#define BPCTLI_IGP01E1000_PHY_PAGE_SELECT 0x1F /* Page Select */
+#define BPCTLI_MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */
+
+#define BPCTLI_MDIC_DATA_MASK 0x0000FFFF
+#define BPCTLI_MDIC_REG_MASK 0x001F0000
+#define BPCTLI_MDIC_REG_SHIFT 16
+#define BPCTLI_MDIC_PHY_MASK 0x03E00000
+#define BPCTLI_MDIC_PHY_SHIFT 21
+#define BPCTLI_MDIC_OP_WRITE 0x04000000
+#define BPCTLI_MDIC_OP_READ 0x08000000
+#define BPCTLI_MDIC_READY 0x10000000
+#define BPCTLI_MDIC_INT_EN 0x20000000
+#define BPCTLI_MDIC_ERROR 0x40000000
+
+#define BPCTLI_SWFW_PHY0_SM 0x02
+#define BPCTLI_SWFW_PHY1_SM 0x04
+
+#define BPCTLI_SW_FW_SYNC 0x05B5C /* Software-Firmware Synchronization - RW */
+
+#define BPCTLI_SWSM 0x05B50 /* SW Semaphore */
+#define BPCTLI_FWSM 0x05B54 /* FW Semaphore */
+
+#define BPCTLI_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */
+#define BPCTLI_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */
+#define BPCTLI_MAX_PHY_MULTI_PAGE_REG 0xF
+#define BPCTLI_GEN_POLL_TIMEOUT 640
+
+/********************************************************/
+
+/********************************************************/
+/* 10G INTERFACE ****************************************/
+/********************************************************/
+
+#define BP10G_I2CCTL 0x28
+
+/* I2CCTL Bit Masks */
+#define BP10G_I2C_CLK_IN 0x00000001
+#define BP10G_I2C_CLK_OUT 0x00000002
+#define BP10G_I2C_DATA_IN 0x00000004
+#define BP10G_I2C_DATA_OUT 0x00000008
+
+#define BP10G_ESDP 0x20
+
+#define BP10G_SDP0_DIR 0x100
+#define BP10G_SDP1_DIR 0x200
+#define BP10G_SDP3_DIR 0x800
+#define BP10G_SDP4_DIR BIT_12
+#define BP10G_SDP5_DIR 0x2000
+#define BP10G_SDP0_DATA 0x001
+#define BP10G_SDP1_DATA 0x002
+#define BP10G_SDP3_DATA 0x008
+#define BP10G_SDP4_DATA 0x010
+#define BP10G_SDP5_DATA 0x020
+
+#define BP10G_SDP2_DIR 0x400
+#define BP10G_SDP2_DATA 0x4
+
+#define BP10G_EODSDP 0x28
+
+#define BP10G_SDP6_DATA_IN 0x001
+#define BP10G_SDP6_DATA_OUT 0x002
+
+#define BP10G_SDP7_DATA_IN 0x004
+#define BP10G_SDP7_DATA_OUT 0x008
+
+#define BP10G_MCLK_DATA_OUT BP10G_SDP7_DATA_OUT
+#define BP10G_MDIO_DATA_OUT BP10G_SDP6_DATA_OUT
+#define BP10G_MDIO_DATA_IN BP10G_SDP6_DATA_IN
+
+#define BP10G_MDIO_DATA /*BP10G_SDP5_DATA*/ BP10G_SDP3_DATA
+#define BP10G_MDIO_DIR /*BP10G_SDP5_DIR*/ BP10G_SDP3_DATA
+
+/*#define BP10G_MCLK_DATA_OUT9 BP10G_I2C_CLK_OUT
+#define BP10G_MDIO_DATA_OUT9 BP10G_I2C_DATA_OUT*/
+
+ /*#define BP10G_MCLK_DATA_OUT9*//*BP10G_I2C_DATA_OUT */
+#define BP10G_MDIO_DATA_OUT9 BP10G_I2C_DATA_OUT /*BP10G_I2C_CLK_OUT */
+
+/* VIA EOSDP ! */
+#define BP10G_MCLK_DATA_OUT9 BP10G_SDP4_DATA
+#define BP10G_MCLK_DIR_OUT9 BP10G_SDP4_DIR
+
+/*#define BP10G_MDIO_DATA_IN9 BP10G_I2C_DATA_IN*/
+
+#define BP10G_MDIO_DATA_IN9 BP10G_I2C_DATA_IN /*BP10G_I2C_CLK_IN */
+
+#define BP540_MDIO_DATA /*BP10G_SDP5_DATA*/ BP10G_SDP0_DATA
+#define BP540_MDIO_DIR /*BP10G_SDP5_DIR*/ BP10G_SDP0_DIR
+#define BP540_MCLK_DATA BP10G_SDP2_DATA
+#define BP540_MCLK_DIR BP10G_SDP2_DIR
+
+#define BP10G_WRITE_REG(a, reg, value) \
+ (writel((value), (void *)(((a)->mem_map) + BP10G_##reg)))
+
+#define BP10G_READ_REG(a, reg) ( \
+ readl((void *)((a)->mem_map) + BP10G_##reg))
+
+/*****BROADCOM*******************************************/
+
+#define BP10GB_MISC_REG_GPIO 0xa490
+#define BP10GB_GPIO3_P0 BIT_3
+#define BP10GB_GPIO3_P1 BIT_7
+
+#define BP10GB_GPIO3_SET_P0 BIT_11
+#define BP10GB_GPIO3_CLR_P0 BIT_19
+#define BP10GB_GPIO3_OE_P0 BIT_27
+
+#define BP10GB_GPIO3_SET_P1 BIT_15
+#define BP10GB_GPIO3_CLR_P1 BIT_23
+#define BP10GB_GPIO3_OE_P1 BIT_31
+
+#define BP10GB_GPIO0_P1 0x10
+#define BP10GB_GPIO0_P0 0x1
+#define BP10GB_GPIO0_CLR_P0 0x10000
+#define BP10GB_GPIO0_CLR_P1 0x100000
+#define BP10GB_GPIO0_SET_P0 0x100
+#define BP10GB_GPIO0_SET_P1 0x1000
+
+#define BP10GB_GPIO0_OE_P1 0x10000000
+#define BP10GB_GPIO0_OE_P0 0x1000000
+
+#define BP10GB_MISC_REG_SPIO 0xa4fc
+#define BP10GB_GPIO4_OE BIT_28
+#define BP10GB_GPIO5_OE BIT_29
+#define BP10GB_GPIO4_CLR BIT_20
+#define BP10GB_GPIO5_CLR BIT_21
+#define BP10GB_GPIO4_SET BIT_12
+#define BP10GB_GPIO5_SET BIT_13
+#define BP10GB_GPIO4 BIT_4
+#define BP10GB_GPIO5 BIT_5
+
+#define BP10GB_MCLK_DIR BP10GB_GPIO5_OE
+#define BP10GB_MDIO_DIR BP10GB_GPIO4_OE
+
+#define BP10GB_MCLK_DATA BP10GB_GPIO5
+#define BP10GB_MDIO_DATA BP10GB_GPIO4
+
+#define BP10GB_MCLK_SET BP10GB_GPIO5_SET
+#define BP10GB_MDIO_SET BP10GB_GPIO4_SET
+
+#define BP10GB_MCLK_CLR BP10GB_GPIO5_CLR
+#define BP10GB_MDIO_CLR BP10GB_GPIO4_CLR
+
+#define BP10GB_WRITE_REG(a, reg, value) \
+ (writel((value), (void *)(((a)->mem_map) + BP10GB_##reg)))
+
+#define BP10GB_READ_REG(a, reg) ( \
+ readl((void *)((a)->mem_map) + BP10GB_##reg))
+
+#endif
+
+int bp_proc_create(void);
diff --git a/drivers/staging/silicom/bp_proc.c b/drivers/staging/silicom/bp_proc.c
new file mode 100644
index 000000000000..6ad4b27472e4
--- /dev/null
+++ b/drivers/staging/silicom/bp_proc.c
@@ -0,0 +1,1350 @@
+/******************************************************************************/
+/* */
+/* Copyright (c) 2004-2006 Silicom, 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 as published by */
+/* the Free Software Foundation, located in the file LICENSE. */
+/* */
+/* */
+/******************************************************************************/
+
+#include <linux/version.h>
+#if defined(CONFIG_SMP) && ! defined(__SMP__)
+#define __SMP__
+#endif
+
+#include <linux/proc_fs.h>
+#include <linux/netdevice.h>
+#include <asm/uaccess.h>
+//#include <linux/smp_lock.h>
+#include "bp_mod.h"
+
+#define BP_PROC_DIR "bypass"
+//#define BYPASS_SUPPORT "bypass"
+
+#ifdef BYPASS_SUPPORT
+
+#define GPIO6_SET_ENTRY_SD "gpio6_set"
+#define GPIO6_CLEAR_ENTRY_SD "gpio6_clear"
+
+#define GPIO7_SET_ENTRY_SD "gpio7_set"
+#define GPIO7_CLEAR_ENTRY_SD "gpio7_clear"
+
+#define PULSE_SET_ENTRY_SD "pulse_set"
+#define ZERO_SET_ENTRY_SD "zero_set"
+#define PULSE_GET1_ENTRY_SD "pulse_get1"
+#define PULSE_GET2_ENTRY_SD "pulse_get2"
+
+#define CMND_ON_ENTRY_SD "cmnd_on"
+#define CMND_OFF_ENTRY_SD "cmnd_off"
+#define RESET_CONT_ENTRY_SD "reset_cont"
+
+ /*COMMANDS*/
+#define BYPASS_INFO_ENTRY_SD "bypass_info"
+#define BYPASS_SLAVE_ENTRY_SD "bypass_slave"
+#define BYPASS_CAPS_ENTRY_SD "bypass_caps"
+#define WD_SET_CAPS_ENTRY_SD "wd_set_caps"
+#define BYPASS_ENTRY_SD "bypass"
+#define BYPASS_CHANGE_ENTRY_SD "bypass_change"
+#define BYPASS_WD_ENTRY_SD "bypass_wd"
+#define WD_EXPIRE_TIME_ENTRY_SD "wd_expire_time"
+#define RESET_BYPASS_WD_ENTRY_SD "reset_bypass_wd"
+#define DIS_BYPASS_ENTRY_SD "dis_bypass"
+#define BYPASS_PWUP_ENTRY_SD "bypass_pwup"
+#define BYPASS_PWOFF_ENTRY_SD "bypass_pwoff"
+#define STD_NIC_ENTRY_SD "std_nic"
+#define STD_NIC_ENTRY_SD "std_nic"
+#define TAP_ENTRY_SD "tap"
+#define TAP_CHANGE_ENTRY_SD "tap_change"
+#define DIS_TAP_ENTRY_SD "dis_tap"
+#define TAP_PWUP_ENTRY_SD "tap_pwup"
+#define TWO_PORT_LINK_ENTRY_SD "two_port_link"
+#define WD_EXP_MODE_ENTRY_SD "wd_exp_mode"
+#define WD_AUTORESET_ENTRY_SD "wd_autoreset"
+#define TPL_ENTRY_SD "tpl"
+#define WAIT_AT_PWUP_ENTRY_SD "wait_at_pwup"
+#define HW_RESET_ENTRY_SD "hw_reset"
+#define DISC_ENTRY_SD "disc"
+#define DISC_CHANGE_ENTRY_SD "disc_change"
+#define DIS_DISC_ENTRY_SD "dis_disc"
+#define DISC_PWUP_ENTRY_SD "disc_pwup"
+#endif //bypass_support
+static struct proc_dir_entry *bp_procfs_dir;
+
+static struct proc_dir_entry *proc_getdir(char *name,
+ struct proc_dir_entry *proc_dir)
+{
+ struct proc_dir_entry *pde = proc_dir;
+ for (pde = pde->subdir; pde; pde = pde->next) {
+ if (pde->namelen && (strcmp(name, pde->name) == 0)) {
+ /* directory exists */
+ break;
+ }
+ }
+ if (pde == (struct proc_dir_entry *)0) {
+ /* create the directory */
+ pde = create_proc_entry(name, S_IFDIR, proc_dir);
+ if (pde == (struct proc_dir_entry *)0) {
+ return (pde);
+ }
+ }
+ return (pde);
+}
+
+#ifdef BYPASS_SUPPORT
+
+int
+bypass_proc_create_entry_sd(struct pfs_unit *pfs_unit_curr,
+ char *proc_name,
+ write_proc_t * write_proc,
+ read_proc_t * read_proc,
+ struct proc_dir_entry *parent_pfs, void *data)
+{
+ strcpy(pfs_unit_curr->proc_name, proc_name);
+ pfs_unit_curr->proc_entry = create_proc_entry(pfs_unit_curr->proc_name,
+ S_IFREG | S_IRUSR |
+ S_IWUSR | S_IRGRP |
+ S_IROTH, parent_pfs);
+ if (pfs_unit_curr->proc_entry == 0) {
+
+ return -1;
+ }
+
+ pfs_unit_curr->proc_entry->read_proc = read_proc;
+ pfs_unit_curr->proc_entry->write_proc = write_proc;
+ pfs_unit_curr->proc_entry->data = data;
+
+ return 0;
+
+}
+
+int
+get_bypass_info_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+ int len = 0;
+
+ len += sprintf(page, "Name\t\t\t%s\n", pbp_device_block->bp_name);
+ len +=
+ sprintf(page + len, "Firmware version\t0x%x\n",
+ pbp_device_block->bp_fw_ver);
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_bypass_slave_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ struct pci_dev *pci_slave_dev = pbp_device_block->bp_slave;
+ struct net_device *net_slave_dev;
+ int len = 0;
+
+ if (is_bypass_fn(pbp_device_block)) {
+ net_slave_dev = pci_get_drvdata(pci_slave_dev);
+ if (net_slave_dev)
+ len = sprintf(page, "%s\n", net_slave_dev->name);
+ else
+ len = sprintf(page, "fail\n");
+ } else
+ len = sprintf(page, "fail\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_bypass_caps_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bypass_caps_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "-1\n");
+ else
+ len = sprintf(page, "0x%x\n", ret);
+ *eof = 1;
+ return len;
+
+}
+
+int
+get_wd_set_caps_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_wd_set_caps_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "-1\n");
+ else
+ len = sprintf(page, "0x%x\n", ret);
+ *eof = 1;
+ return len;
+}
+
+int
+set_bypass_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ bypass_param = 0;
+
+ set_bypass_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+set_tap_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_tap_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+set_disc_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_disc_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+get_bypass_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bypass_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_tap_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_tap_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_disc_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_disc_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_bypass_change_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bypass_change_fn(pbp_device_block);
+ if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "fail\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_tap_change_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_tap_change_fn(pbp_device_block);
+ if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "fail\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_disc_change_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_disc_change_fn(pbp_device_block);
+ if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "fail\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+set_bypass_wd_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ unsigned int timeout = 0;
+ char *timeout_ptr = kbuf;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ timeout_ptr = kbuf;
+ timeout = atoi(&timeout_ptr);
+
+ set_bypass_wd_fn(pbp_device_block, timeout);
+
+ return count;
+}
+
+int
+get_bypass_wd_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0, timeout = 0;
+
+ ret = get_bypass_wd_fn(pbp_device_block, &timeout);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (timeout == -1)
+ len = sprintf(page, "unknown\n");
+ else if (timeout == 0)
+ len = sprintf(page, "disable\n");
+ else
+ len = sprintf(page, "%d\n", timeout);
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_wd_expire_time_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0, timeout = 0;
+
+ ret = get_wd_expire_time_fn(pbp_device_block, &timeout);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (timeout == -1)
+ len = sprintf(page, "expire\n");
+ else if (timeout == 0)
+ len = sprintf(page, "disable\n");
+
+ else
+ len = sprintf(page, "%d\n", timeout);
+ *eof = 1;
+ return len;
+}
+
+int
+get_tpl_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_tpl_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+#ifdef PMC_FIX_FLAG
+int
+get_wait_at_pwup_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bp_wait_at_pwup_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_hw_reset_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bp_hw_reset_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 1)
+ len = sprintf(page, "on\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+
+ *eof = 1;
+ return len;
+}
+
+#endif /*PMC_WAIT_FLAG */
+
+int
+reset_bypass_wd_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = reset_bypass_wd_timer_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "disable\n");
+ else if (ret == 1)
+ len = sprintf(page, "success\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+set_dis_bypass_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ bypass_param = 0;
+
+ set_dis_bypass_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+set_dis_tap_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_dis_tap_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+set_dis_disc_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_dis_disc_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+get_dis_bypass_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_dis_bypass_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_dis_tap_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_dis_tap_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_dis_disc_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_dis_disc_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+set_bypass_pwup_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ bypass_param = 0;
+
+ set_bypass_pwup_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+set_bypass_pwoff_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ bypass_param = 0;
+
+ set_bypass_pwoff_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+set_tap_pwup_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_tap_pwup_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+set_disc_pwup_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tap_param = 0, length = 0;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tap_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tap_param = 0;
+
+ set_disc_pwup_fn(pbp_device_block, tap_param);
+
+ return count;
+}
+
+int
+get_bypass_pwup_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bypass_pwup_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_bypass_pwoff_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_bypass_pwoff_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_tap_pwup_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_tap_pwup_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_disc_pwup_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_disc_pwup_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+set_std_nic_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ bypass_param = 0;
+
+ set_std_nic_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+get_std_nic_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_std_nic_fn(pbp_device_block);
+ if (ret == BP_NOT_CAP)
+ len = sprintf(page, "fail\n");
+ else if (ret == 0)
+ len = sprintf(page, "off\n");
+ else
+ len = sprintf(page, "on\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+get_wd_exp_mode_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_wd_exp_mode_fn(pbp_device_block);
+ if (ret == 1)
+ len = sprintf(page, "tap\n");
+ else if (ret == 0)
+ len = sprintf(page, "bypass\n");
+ else if (ret == 2)
+ len = sprintf(page, "disc\n");
+
+ else
+ len = sprintf(page, "fail\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+set_wd_exp_mode_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int bypass_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "tap") == 0)
+ bypass_param = 1;
+ else if (strcmp(kbuf, "bypass") == 0)
+ bypass_param = 0;
+ else if (strcmp(kbuf, "disc") == 0)
+ bypass_param = 2;
+
+ set_wd_exp_mode_fn(pbp_device_block, bypass_param);
+
+ return count;
+}
+
+int
+get_wd_autoreset_pfs(char *page, char **start, off_t off, int count,
+ int *eof, void *data)
+{
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int len = 0, ret = 0;
+
+ ret = get_wd_autoreset_fn(pbp_device_block);
+ if (ret >= 0)
+ len = sprintf(page, "%d\n", ret);
+ else
+ len = sprintf(page, "fail\n");
+
+ *eof = 1;
+ return len;
+}
+
+int
+set_wd_autoreset_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+ u32 timeout = 0;
+ char *timeout_ptr = kbuf;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ timeout_ptr = kbuf;
+ timeout = atoi(&timeout_ptr);
+
+ set_wd_autoreset_fn(pbp_device_block, timeout);
+
+ return count;
+}
+
+int
+set_tpl_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tpl_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tpl_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tpl_param = 0;
+
+ set_tpl_fn(pbp_device_block, tpl_param);
+
+ return count;
+}
+
+#ifdef PMC_FIX_FLAG
+int
+set_wait_at_pwup_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tpl_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tpl_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tpl_param = 0;
+
+ set_bp_wait_at_pwup_fn(pbp_device_block, tpl_param);
+
+ return count;
+}
+
+int
+set_hw_reset_pfs(struct file *file, const char *buffer,
+ unsigned long count, void *data)
+{
+
+ char kbuf[256];
+ bpctl_dev_t *pbp_device_block = (bpctl_dev_t *) data;
+
+ int tpl_param = 0, length = 0;
+
+ if (count > (sizeof(kbuf) - 1))
+ return -1;
+
+ if (copy_from_user(&kbuf, buffer, count)) {
+ return -1;
+ }
+
+ kbuf[count] = '\0';
+ length = strlen(kbuf);
+ if (kbuf[length - 1] == '\n')
+ kbuf[--length] = '\0';
+
+ if (strcmp(kbuf, "on") == 0)
+ tpl_param = 1;
+ else if (strcmp(kbuf, "off") == 0)
+ tpl_param = 0;
+
+ set_bp_hw_reset_fn(pbp_device_block, tpl_param);
+
+ return count;
+}
+
+#endif /*PMC_FIX_FLAG */
+
+int bypass_proc_create_dev_sd(bpctl_dev_t * pbp_device_block)
+{
+ struct bypass_pfs_sd *current_pfs = &(pbp_device_block->bypass_pfs_set);
+ static struct proc_dir_entry *procfs_dir = NULL;
+ int ret = 0;
+
+ sprintf(current_pfs->dir_name, "bypass_%s", dev->name);
+
+ if (!bp_procfs_dir)
+ return -1;
+
+ /* create device proc dir */
+ procfs_dir = proc_getdir(current_pfs->dir_name, bp_procfs_dir);
+ if (procfs_dir == 0) {
+ printk(KERN_DEBUG "Could not create procfs directory %s\n",
+ current_pfs->dir_name);
+ return -1;
+ }
+ current_pfs->bypass_entry = procfs_dir;
+
+ if (bypass_proc_create_entry(&(current_pfs->bypass_info), BYPASS_INFO_ENTRY_SD, NULL, /* write */
+ get_bypass_info_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (pbp_device_block->bp_caps & SW_CTL_CAP) {
+
+ /* Create set param proc's */
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_slave), BYPASS_SLAVE_ENTRY_SD, NULL, /* write */
+ get_bypass_slave_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_caps), BYPASS_CAPS_ENTRY_SD, NULL, /* write */
+ get_bypass_caps_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->wd_set_caps), WD_SET_CAPS_ENTRY_SD, NULL, /* write */
+ get_wd_set_caps_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_wd), BYPASS_WD_ENTRY_SD, set_bypass_wd_pfs, /* write */
+ get_bypass_wd_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->wd_expire_time), WD_EXPIRE_TIME_ENTRY_SD, NULL, /* write */
+ get_wd_expire_time_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->reset_bypass_wd), RESET_BYPASS_WD_ENTRY_SD, NULL, /* write */
+ reset_bypass_wd_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->std_nic), STD_NIC_ENTRY_SD, set_std_nic_pfs, /* write */
+ get_std_nic_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (pbp_device_block->bp_caps & BP_CAP) {
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass), BYPASS_ENTRY_SD, set_bypass_pfs, /* write */
+ get_bypass_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->dis_bypass), DIS_BYPASS_ENTRY_SD, set_dis_bypass_pfs, /* write */
+ get_dis_bypass_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_pwup), BYPASS_PWUP_ENTRY_SD, set_bypass_pwup_pfs, /* write */
+ get_bypass_pwup_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_pwoff), BYPASS_PWOFF_ENTRY_SD, set_bypass_pwoff_pfs, /* write */
+ get_bypass_pwoff_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->bypass_change), BYPASS_CHANGE_ENTRY_SD, NULL, /* write */
+ get_bypass_change_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+ }
+
+ if (pbp_device_block->bp_caps & TAP_CAP) {
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap), TAP_ENTRY_SD, set_tap_pfs, /* write */
+ get_tap_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->dis_tap), DIS_TAP_ENTRY_SD, set_dis_tap_pfs, /* write */
+ get_dis_tap_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap_pwup), TAP_PWUP_ENTRY_SD, set_tap_pwup_pfs, /* write */
+ get_tap_pwup_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap_change), TAP_CHANGE_ENTRY_SD, NULL, /* write */
+ get_tap_change_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+ }
+ if (pbp_device_block->bp_caps & DISC_CAP) {
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap), DISC_ENTRY_SD, set_disc_pfs, /* write */
+ get_disc_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+#if 1
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->dis_tap), DIS_DISC_ENTRY_SD, set_dis_disc_pfs, /* write */
+ get_dis_disc_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+#endif
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap_pwup), DISC_PWUP_ENTRY_SD, set_disc_pwup_pfs, /* write */
+ get_disc_pwup_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->tap_change), DISC_CHANGE_ENTRY_SD, NULL, /* write */
+ get_disc_change_pfs, /* read */
+ procfs_dir,
+ pbp_device_block))
+ ret = -1;
+ }
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->wd_exp_mode), WD_EXP_MODE_ENTRY_SD, set_wd_exp_mode_pfs, /* write */
+ get_wd_exp_mode_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+ if (bypass_proc_create_entry_sd(&(current_pfs->wd_autoreset), WD_AUTORESET_ENTRY_SD, set_wd_autoreset_pfs, /* write */
+ get_wd_autoreset_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+ if (bypass_proc_create_entry_sd(&(current_pfs->tpl), TPL_ENTRY_SD, set_tpl_pfs, /* write */
+ get_tpl_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+#ifdef PMC_FIX_FLAG
+ if (bypass_proc_create_entry_sd(&(current_pfs->tpl), WAIT_AT_PWUP_ENTRY_SD, set_wait_at_pwup_pfs, /* write */
+ get_wait_at_pwup_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+ if (bypass_proc_create_entry_sd(&(current_pfs->tpl), HW_RESET_ENTRY_SD, set_hw_reset_pfs, /* write */
+ get_hw_reset_pfs, /* read */
+ procfs_dir, pbp_device_block))
+ ret = -1;
+
+#endif
+
+ }
+ if (ret < 0)
+ printk(KERN_DEBUG "Create proc entry failed\n");
+
+ return ret;
+}
+
+int bypass_proc_remove_dev_sd(bpctl_dev_t * pbp_device_block)
+{
+
+ struct bypass_pfs_sd *current_pfs = &pbp_device_block->bypass_pfs_set;
+ struct proc_dir_entry *pde = current_pfs->bypass_entry, *pde_curr =
+ NULL;
+ char name[256];
+
+ for (pde = pde->subdir; pde;) {
+ strcpy(name, pde->name);
+ pde_curr = pde;
+ pde = pde->next;
+ remove_proc_entry(name, current_pfs->bypass_entry);
+ }
+ if (!pde)
+ remove_proc_entry(current_pfs->dir_name, bp_procfs_dir);
+
+ return 0;
+}
+
+#endif /* BYPASS_SUPPORT */
diff --git a/drivers/staging/silicom/bypass.h b/drivers/staging/silicom/bypass.h
new file mode 100644
index 000000000000..08fa7a0fc8d8
--- /dev/null
+++ b/drivers/staging/silicom/bypass.h
@@ -0,0 +1,202 @@
+/******************************************************************************/
+/* */
+/* Bypass Control utility, Copyright (c) 2005 Silicom */
+/* 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, located in the file LICENSE. */
+/* */
+/* */
+/******************************************************************************/
+
+#ifndef BYPASS_H
+#define BYPASS_H
+
+/* Bypass related */
+
+#define SYNC_CMD_VAL 2 /* 10b */
+#define SYNC_CMD_LEN 2
+
+#define WR_CMD_VAL 2 /* 10b */
+#define WR_CMD_LEN 2
+
+#define RD_CMD_VAL 1 /* 10b */
+#define RD_CMD_LEN 2
+
+#define ADDR_CMD_LEN 4
+
+#define WR_DATA_LEN 8
+#define RD_DATA_LEN 8
+
+#define PIC_SIGN_REG_ADDR 0x7
+#define PIC_SIGN_VALUE 0xcd
+
+#define STATUS_REG_ADDR 0
+#define WDT_EN_MASK 0x01 /* BIT_0 */
+#define CMND_EN_MASK 0x02 /* BIT_1 */
+#define DIS_BYPASS_CAP_MASK 0x04 /* BIT_2 Bypass Cap is disable*/
+#define DFLT_PWRON_MASK 0x08 /* BIT_3 */
+#define BYPASS_OFF_MASK 0x10 /* BIT_4 */
+#define BYPASS_FLAG_MASK 0x20 /* BIT_5 */
+#define STD_NIC_MASK (DIS_BYPASS_CAP_MASK | BYPASS_OFF_MASK | DFLT_PWRON_MASK)
+#define WD_EXP_FLAG_MASK 0x40 /* BIT_6 */
+#define DFLT_PWROFF_MASK 0x80 /* BIT_7 */
+#define STD_NIC_PWOFF_MASK (DIS_BYPASS_CAP_MASK | BYPASS_OFF_MASK | DFLT_PWRON_MASK | DFLT_PWROFF_MASK)
+
+#define PRODUCT_CAP_REG_ADDR 0x5
+#define BYPASS_SUPPORT_MASK 0x01 /* BIT_0 */
+#define TAP_SUPPORT_MASK 0x02 /* BIT_1 */
+#define NORMAL_UNSUPPORT_MASK 0x04 /* BIT_2 */
+#define DISC_SUPPORT_MASK 0x08 /* BIT_3 */
+#define TPL2_SUPPORT_MASK 0x10 /* BIT_4 */
+#define DISC_PORT_SUPPORT_MASK 0x20 /* BIT_5 */
+
+#define STATUS_TAP_REG_ADDR 0x6
+#define WDTE_TAP_BPN_MASK 0x01 /* BIT_1 1 when wdt expired -> TAP, 0 - Bypass */
+#define DIS_TAP_CAP_MASK 0x04 /* BIT_2 TAP Cap is disable*/
+#define DFLT_PWRON_TAP_MASK 0x08 /* BIT_3 */
+#define TAP_OFF_MASK 0x10 /* BIT_4 */
+#define TAP_FLAG_MASK 0x20 /* BIT_5 */
+#define TX_DISA_MASK 0x40
+#define TX_DISB_MASK 0x80
+
+#define STD_NIC_TAP_MASK (DIS_TAP_CAP_MASK | TAP_OFF_MASK | DFLT_PWRON_TAP_MASK)
+
+#define STATUS_DISC_REG_ADDR 13
+#define WDTE_DISC_BPN_MASK 0x01 /* BIT_0 1 when wdt expired -> TAP, 0 - Bypass */
+#define STD_NIC_ON_MASK 0x02 /* BIT_1 */
+#define DIS_DISC_CAP_MASK 0x04 /* BIT_2 TAP Cap is disable*/
+#define DFLT_PWRON_DISC_MASK 0x08 /* BIT_3 */
+#define DISC_OFF_MASK 0x10 /* BIT_4 */
+#define DISC_FLAG_MASK 0x20 /* BIT_5 */
+#define TPL2_FLAG_MASK 0x40 /* BIT_6 */
+#define STD_NIC_DISC_MASK DIS_DISC_CAP_MASK
+
+#define CONT_CONFIG_REG_ADDR 12
+#define EN_HW_RESET_MASK 0x2 /* BIT_1 */
+#define WAIT_AT_PWUP_MASK 0x1 /* BIT_0 */
+
+#define VER_REG_ADDR 0x1
+#define BP_FW_VER_A0 0xa0
+#define BP_FW_VER_A1 0xa1
+
+#define INT_VER_MASK 0xf0
+#define EXT_VER_MASK 0xf
+/* */
+#define PXG2BPI_VER 0x0
+#define PXG2TBPI_VER 0x1
+#define PXE2TBPI_VER 0x2
+#define PXG4BPFI_VER 0x4
+#define BP_FW_EXT_VER7 0x6
+#define BP_FW_EXT_VER8 0x8
+#define BP_FW_EXT_VER9 0x9
+
+#define OLD_IF_VER -1
+
+#define CMND_REG_ADDR 10 /* 1010b */
+#define WDT_REG_ADDR 4
+#define TMRL_REG_ADDR 2
+#define TMRH_REG_ADDR 3
+
+/* NEW_FW */
+#define WDT_INTERVAL 1 /* 5 //8 */
+#define WDT_CMND_INTERVAL 200 /* 50 */
+#define CMND_INTERVAL 200 /* 100 usec */
+#define PULSE_TIME 100
+
+/* OLD_FW */
+#define INIT_CMND_INTERVAL 40
+#define PULSE_INTERVAL 5
+#define WDT_TIME_CNT 3
+
+/* Intel Commands */
+
+#define CMND_OFF_INT 0xf
+#define PWROFF_BYPASS_ON_INT 0x5
+#define BYPASS_ON_INT 0x6
+#define DIS_BYPASS_CAP_INT 0x4
+#define RESET_WDT_INT 0x1
+
+/* Intel timing */
+
+#define BYPASS_DELAY_INT 4 /* msec */
+#define CMND_INTERVAL_INT 2 /* msec */
+
+/* Silicom Commands */
+#define CMND_ON 0x4
+#define CMND_OFF 0x2
+#define BYPASS_ON 0xa
+#define BYPASS_OFF 0x8
+#define PORT_LINK_EN 0xe
+#define PORT_LINK_DIS 0xc
+#define WDT_ON 0x10 /* 0x1f (11111) - max */
+#define TIMEOUT_UNIT 100
+#define TIMEOUT_MAX_STEP 15
+#define WDT_TIMEOUT_MIN 100 /* msec */
+#define WDT_TIMEOUT_MAX 3276800 /* msec */
+#define WDT_AUTO_MIN_INT 500
+#define WDT_TIMEOUT_DEF WDT_TIMEOUT_MIN
+#define WDT_OFF 0x6
+#define WDT_RELOAD 0x9
+#define RESET_CONT 0x20
+#define DIS_BYPASS_CAP 0x22
+#define EN_BYPASS_CAP 0x24
+#define BYPASS_STATE_PWRON 0x26
+#define NORMAL_STATE_PWRON 0x28
+#define BYPASS_STATE_PWROFF 0x27
+#define NORMAL_STATE_PWROFF 0x29
+#define TAP_ON 0xb
+#define TAP_OFF 0x9
+#define TAP_STATE_PWRON 0x2a
+#define DIS_TAP_CAP 0x2c
+#define EN_TAP_CAP 0x2e
+#define STD_NIC_OFF 0x86
+#define STD_NIC_ON 0x84
+#define DISC_ON 0x85
+#define DISC_OFF 0x8a
+#define DISC_STATE_PWRON 0x87
+#define DIS_DISC_CAP 0x88
+#define EN_DISC_CAP 0x89
+#define TPL2_ON 0x8c
+#define TPL2_OFF 0x8b
+#define BP_WAIT_AT_PWUP_EN 0x80
+#define BP_WAIT_AT_PWUP_DIS 0x81
+#define BP_HW_RESET_EN 0x82
+#define BP_HW_RESET_DIS 0x83
+
+#define TX_DISA 0x8d
+#define TX_DISB 0x8e
+#define TX_ENA 0xA0
+#define TX_ENB 0xA1
+
+#define TX_DISA_PWRUP 0xA2
+#define TX_DISB_PWRUP 0xA3
+#define TX_ENA_PWRUP 0xA4
+#define TX_ENB_PWRUP 0xA5
+
+#define BYPASS_CAP_DELAY 21 /* msec */
+#define DFLT_PWRON_DELAY 10 /* msec */
+#define LATCH_DELAY 13 /* msec */
+#define EEPROM_WR_DELAY 8 /* msec */
+
+#define BP_LINK_MON_DELAY 4 /* sec */
+
+#define BP_FW_EXT_VER0 0xa0
+#define BP_FW_EXT_VER1 0xa1
+#define BP_FW_EXT_VER2 0xb1
+
+#define BP_OK 0
+#define BP_NOT_CAP -1
+#define WDT_STATUS_EXP -2
+#define WDT_STATUS_UNKNOWN -1
+#define WDT_STATUS_EN 1
+#define WDT_STATUS_DIS 0
+
+#ifdef BP_SELF_TEST
+#define ETH_P_BPTEST 0xabba
+
+#define BPTEST_DATA_LEN 60
+#endif
+
+#endif /* BYPASS_H */
diff --git a/drivers/staging/silicom/bypasslib/Makefile b/drivers/staging/silicom/bypasslib/Makefile
new file mode 100644
index 000000000000..80e8b9bc9357
--- /dev/null
+++ b/drivers/staging/silicom/bypasslib/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for the Bypass network device drivers.
+#
+
+obj-$(CONFIG_SBYPASS) += bypass.o
+
diff --git a/drivers/staging/silicom/bypasslib/bp_ioctl.h b/drivers/staging/silicom/bypasslib/bp_ioctl.h
new file mode 100644
index 000000000000..040c6fa8d5ad
--- /dev/null
+++ b/drivers/staging/silicom/bypasslib/bp_ioctl.h
@@ -0,0 +1,198 @@
+/******************************************************************************/
+/* */
+/* bypass library, Copyright (c) 2004-2006 Silicom, Ltd */
+/* 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, located in the file LICENSE. */
+/* */
+/* */
+/* */
+/******************************************************************************/
+
+#ifndef BP_IOCTL_H
+#define BP_IOCTL_H
+
+#define BP_CAP 0x01 //BIT_0
+#define BP_STATUS_CAP 0x02 //BIT_1
+#define BP_STATUS_CHANGE_CAP 0x04 //BIT_2
+#define SW_CTL_CAP 0x08 //BIT_3
+#define BP_DIS_CAP 0x10 //BIT_4
+#define BP_DIS_STATUS_CAP 0x20 //BIT_5
+#define STD_NIC_CAP 0x40 //BIT_6
+#define BP_PWOFF_ON_CAP 0x80 //BIT_7
+#define BP_PWOFF_OFF_CAP 0x0100 //BIT_8
+#define BP_PWOFF_CTL_CAP 0x0200 //BIT_9
+#define BP_PWUP_ON_CAP 0x0400 //BIT_10
+#define BP_PWUP_OFF_CAP 0x0800 //BIT_11
+#define BP_PWUP_CTL_CAP 0x1000 //BIT_12
+#define WD_CTL_CAP 0x2000 //BIT_13
+#define WD_STATUS_CAP 0x4000 //BIT_14
+#define WD_TIMEOUT_CAP 0x8000 //BIT_15
+#define TX_CTL_CAP 0x10000 //BIT_16
+#define TX_STATUS_CAP 0x20000 //BIT_17
+#define TAP_CAP 0x40000 //BIT_18
+#define TAP_STATUS_CAP 0x80000 //BIT_19
+#define TAP_STATUS_CHANGE_CAP 0x100000 //BIT_20
+#define TAP_DIS_CAP 0x200000 //BIT_21
+#define TAP_DIS_STATUS_CAP 0x400000 //BIT_22
+#define TAP_PWUP_ON_CAP 0x800000 //BIT_23
+#define TAP_PWUP_OFF_CAP 0x1000000 //BIT 24
+#define TAP_PWUP_CTL_CAP 0x2000000 //BIT 25
+#define NIC_CAP_NEG 0x4000000 //BIT 26
+#define TPL_CAP 0x8000000 //BIT 27
+#define DISC_CAP 0x10000000 //BIT 28
+#define DISC_DIS_CAP 0x20000000 //BIT 29
+#define DISC_PWUP_CTL_CAP 0x40000000 //BIT 30
+
+#define WD_MIN_TIME_MASK(val) (val & 0xf)
+#define WD_STEP_COUNT_MASK(val) ((val & 0xf) << 5)
+#define WDT_STEP_TIME 0x10 //BIT_4
+
+#define WD_MIN_TIME_GET(desc) (desc & 0xf)
+#define WD_STEP_COUNT_GET(desc) (desc>>5) & 0xf
+
+typedef enum {
+ IS_BYPASS = 1,
+ GET_BYPASS_SLAVE,
+ GET_BYPASS_CAPS,
+ GET_WD_SET_CAPS,
+ SET_BYPASS,
+ GET_BYPASS,
+ GET_BYPASS_CHANGE,
+ SET_BYPASS_WD,
+ GET_BYPASS_WD,
+ GET_WD_EXPIRE_TIME,
+ RESET_BYPASS_WD_TIMER,
+ SET_DIS_BYPASS,
+ GET_DIS_BYPASS,
+ SET_BYPASS_PWOFF,
+ GET_BYPASS_PWOFF,
+ SET_BYPASS_PWUP,
+ GET_BYPASS_PWUP,
+ SET_STD_NIC,
+ GET_STD_NIC,
+ SET_TX,
+ GET_TX,
+ SET_TAP,
+ GET_TAP,
+ GET_TAP_CHANGE,
+ SET_DIS_TAP,
+ GET_DIS_TAP,
+ SET_TAP_PWUP,
+ GET_TAP_PWUP,
+ SET_WD_EXP_MODE,
+ GET_WD_EXP_MODE,
+ SET_WD_AUTORESET,
+ GET_WD_AUTORESET,
+ SET_TPL,
+ GET_TPL,
+ SET_DISC,
+ GET_DISC,
+ GET_DISC_CHANGE,
+ SET_DIS_DISC,
+ GET_DIS_DISC,
+ SET_DISC_PWUP,
+ GET_DISC_PWUP,
+
+ GET_BYPASS_INFO = 100,
+ GET_BP_WAIT_AT_PWUP,
+ SET_BP_WAIT_AT_PWUP,
+ GET_BP_HW_RESET,
+ SET_BP_HW_RESET,
+} CMND_TYPE;
+
+typedef enum {
+ IF_SCAN_SD,
+ GET_DEV_NUM_SD,
+ IS_BYPASS_SD,
+ GET_BYPASS_SLAVE_SD,
+ GET_BYPASS_CAPS_SD,
+ GET_WD_SET_CAPS_SD,
+ SET_BYPASS_SD,
+ GET_BYPASS_SD,
+ GET_BYPASS_CHANGE_SD,
+ SET_BYPASS_WD_SD,
+ GET_BYPASS_WD_SD,
+ GET_WD_EXPIRE_TIME_SD,
+ RESET_BYPASS_WD_TIMER_SD,
+ SET_DIS_BYPASS_SD,
+ GET_DIS_BYPASS_SD,
+ SET_BYPASS_PWOFF_SD,
+ GET_BYPASS_PWOFF_SD,
+ SET_BYPASS_PWUP_SD,
+ GET_BYPASS_PWUP_SD,
+ SET_STD_NIC_SD,
+ GET_STD_NIC_SD,
+ SET_TX_SD,
+ GET_TX_SD,
+ SET_TAP_SD,
+ GET_TAP_SD,
+ GET_TAP_CHANGE_SD,
+ SET_DIS_TAP_SD,
+ GET_DIS_TAP_SD,
+ SET_TAP_PWUP_SD,
+ GET_TAP_PWUP_SD,
+ SET_WD_EXP_MODE_SD,
+ GET_WD_EXP_MODE_SD,
+ SET_WD_AUTORESET_SD,
+ GET_WD_AUTORESET_SD,
+ SET_TPL_SD,
+ GET_TPL_SD,
+ SET_DISC_SD,
+ GET_DISC_SD,
+ GET_DISC_CHANGE_SD,
+ SET_DIS_DISC_SD,
+ GET_DIS_DISC_SD,
+ SET_DISC_PWUP_SD,
+ GET_DISC_PWUP_SD,
+
+ GET_BYPASS_INFO_SD = 100,
+ GET_BP_WAIT_AT_PWUP_SD,
+ SET_BP_WAIT_AT_PWUP_SD,
+ GET_BP_HW_RESET_SD,
+ SET_BP_HW_RESET_SD,
+
+} CMND_TYPE_SD;
+
+#define SIOCGIFBYPASS SIOCDEVPRIVATE+10
+
+struct bp_info {
+ char prod_name[14];
+ unsigned char fw_ver;
+};
+
+/* for passing single values */
+struct if_bypass {
+ char if_name[IFNAMSIZ];
+ int cmd;
+ int data;
+};
+struct if_bypass_info {
+ char if_name[IFNAMSIZ];
+ char cmd;
+ struct bp_info bp_info;
+};
+
+/*
+* The major device number. We can't rely on dynamic
+* registration any more, because ioctls need to know
+* it.
+*/
+
+#define MAGIC_NUM 'J'
+
+/* for passing single values */
+struct bpctl_cmd {
+ int status;
+ int data[8];
+ int in_param[8];
+ int out_param[8];
+};
+
+#define IOCTL_TX_MSG(cmd) _IOWR(MAGIC_NUM, cmd, struct bpctl_cmd)
+
+#define DEVICE_NAME "bpctl"
+
+#endif
diff --git a/drivers/staging/silicom/bypasslib/bplibk.h b/drivers/staging/silicom/bypasslib/bplibk.h
new file mode 100644
index 000000000000..a1c85eec02f0
--- /dev/null
+++ b/drivers/staging/silicom/bypasslib/bplibk.h
@@ -0,0 +1,47 @@
+/******************************************************************************/
+/* */
+/* bypass library, Copyright (c) 2004 Silicom, 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, located in the file LICENSE. */
+/* */
+/* */
+/* bplib.h */
+/* */
+/******************************************************************************/
+#ifndef BYPASS_H
+#define BYPASS_H
+
+#include "bp_ioctl.h"
+#include "libbp_sd.h"
+
+#define IF_NAME "eth"
+#define SILICOM_VID 0x1374
+#define SILICOM_BP_PID_MIN 0x24
+#define SILICOM_BP_PID_MAX 0x5f
+#define INTEL_PEG4BPII_PID 0x10a0
+#define INTEL_PEG4BPFII_PID 0x10a1
+
+#define PEGII_IF_SERIES(vid, pid) \
+ ((vid==0x8086)&& \
+ ((pid==INTEL_PEG4BPII_PID)|| \
+ (pid==INTEL_PEG4BPFII_PID)))
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10))
+#define pci_get_class pci_find_class
+
+#define pci_get_device pci_find_device
+
+#endif
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,6,10))
+#define EXPORT_SYMBOL_NOVERS EXPORT_SYMBOL
+#endif
+
+#ifdef BP_VENDOR_SUPPORT
+char *bp_desc_array[] =
+ { "e1000bp", "e1000bpe", "slcm5700", "bnx2xbp", "ixgbp", "ixgbpe", NULL };
+#endif
+
+#endif
diff --git a/drivers/staging/silicom/bypasslib/bypass.c b/drivers/staging/silicom/bypasslib/bypass.c
new file mode 100644
index 000000000000..527829d58133
--- /dev/null
+++ b/drivers/staging/silicom/bypasslib/bypass.c
@@ -0,0 +1,529 @@
+/******************************************************************************/
+/* */
+/* bypass library, Copyright (c) 2004-2007 Silicom, 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, located in the file LICENSE. */
+/* */
+/* */
+/* bypass.c */
+/* */
+/******************************************************************************/
+
+#include <linux/version.h>
+#if defined(CONFIG_SMP) && ! defined(__SMP__)
+#define __SMP__
+#endif
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <asm/unistd.h>
+
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include <linux/netdevice.h> // struct device, and other headers
+#include <linux/kernel_stat.h>
+#include <linux/pci.h>
+#include <linux/rtnetlink.h>
+#include <linux/ethtool.h>
+
+#include <net/net_namespace.h>
+
+#include "bplibk.h"
+
+#define MOD_NAME "bypass"
+
+#define VERSION "\n"MOD_NAME" version 9.0.4\n"
+
+MODULE_AUTHOR("www.silicom.co.il");
+
+MODULE_LICENSE("GPL");
+
+int init_lib_module(void);
+void cleanup_lib_module(void);
+
+static int do_cmd(struct net_device *dev, struct ifreq *ifr, int cmd, int *data)
+{
+ int ret = -1;
+ struct if_bypass *bypass_cb;
+ static int (*ioctl) (struct net_device *, struct ifreq *, int);
+
+ bypass_cb = (struct if_bypass *)ifr;
+ bypass_cb->cmd = cmd;
+ bypass_cb->data = *data;
+ if ((dev->netdev_ops) && (ioctl = dev->netdev_ops->ndo_do_ioctl)) {
+ ret = ioctl(dev, ifr, SIOCGIFBYPASS);
+ *data = bypass_cb->data;
+ }
+
+ return ret;
+}
+
+static int doit(int cmd, int if_index, int *data)
+{
+ struct ifreq ifr;
+ int ret = -1;
+ struct net_device *dev;
+ struct net_device *n;
+ for_each_netdev_safe(&init_net, dev, n) {
+
+ if (dev->ifindex == if_index) {
+ ret = do_cmd(dev, &ifr, cmd, data);
+ if (ret < 0)
+ ret = -1;
+
+ }
+ }
+
+ return ret;
+}
+
+#define bp_symbol_get(fn_name) symbol_get(fn_name)
+#define bp_symbol_put(fn_name) symbol_put(fn_name)
+
+#define SET_BPLIB_INT_FN(fn_name, arg_type, arg, ret) \
+ ({ int (* fn_ex)(arg_type)=NULL; \
+ fn_ex=bp_symbol_get(fn_name##_sd); \
+ if(fn_ex) { \
+ ret= fn_ex(arg); \
+ bp_symbol_put(fn_name##_sd); \
+ } else ret=-1; \
+ })
+
+#define SET_BPLIB_INT_FN2(fn_name, arg_type, arg, arg_type1, arg1, ret) \
+ ({ int (* fn_ex)(arg_type,arg_type1)=NULL; \
+ fn_ex=bp_symbol_get(fn_name##_sd); \
+ if(fn_ex) { \
+ ret= fn_ex(arg,arg1); \
+ bp_symbol_put(fn_name##_sd); \
+ } else ret=-1; \
+ })
+#define SET_BPLIB_INT_FN3(fn_name, arg_type, arg, arg_type1, arg1,arg_type2, arg2, ret) \
+ ({ int (* fn_ex)(arg_type,arg_type1, arg_type2)=NULL; \
+ fn_ex=bp_symbol_get(fn_name##_sd); \
+ if(fn_ex) { \
+ ret= fn_ex(arg,arg1,arg2); \
+ bp_symbol_put(fn_name##_sd); \
+ } else ret=-1; \
+ })
+
+#define DO_BPLIB_GET_ARG_FN(fn_name,ioctl_val, if_index) \
+ ({ int data, ret=0; \
+ if(is_dev_sd(if_index)){ \
+ SET_BPLIB_INT_FN(fn_name, int, if_index, ret); \
+ return ret; \
+ } \
+ return doit(ioctl_val,if_index, &data); \
+ })
+
+#define DO_BPLIB_SET_ARG_FN(fn_name,ioctl_val,if_index,arg) \
+ ({ int data, ret=0; \
+ if(is_dev_sd(if_index)){ \
+ SET_BPLIB_INT_FN2(fn_name, int, if_index, int, arg, ret); \
+ return ret; \
+ } \
+ data=arg; \
+ return doit(ioctl_val,if_index, &data); \
+ })
+
+static int is_dev_sd(int if_index)
+{
+ int ret = 0;
+ SET_BPLIB_INT_FN(is_bypass, int, if_index, ret);
+ return (ret >= 0 ? 1 : 0);
+}
+
+int is_bypass_dev(int if_index)
+{
+ struct pci_dev *pdev = NULL;
+ struct net_device *dev = NULL;
+ struct ifreq ifr;
+ int ret = 0, data = 0;
+
+ while ((pdev = pci_get_class(PCI_CLASS_NETWORK_ETHERNET << 8, pdev))) {
+ if ((dev = pci_get_drvdata(pdev)) != NULL)
+ if (((dev = pci_get_drvdata(pdev)) != NULL) &&
+ (dev->ifindex == if_index)) {
+ if ((pdev->vendor == SILICOM_VID) &&
+ (pdev->device >= SILICOM_BP_PID_MIN) &&
+ (pdev->device <= SILICOM_BP_PID_MAX))
+ goto send_cmd;
+#if defined(BP_VENDOR_SUPPORT) && defined(ETHTOOL_GDRVINFO)
+ else {
+ struct ethtool_drvinfo info;
+ const struct ethtool_ops *ops =
+ dev->ethtool_ops;
+ int k = 0;
+
+ if (ops->get_drvinfo) {
+ memset(&info, 0, sizeof(info));
+ info.cmd = ETHTOOL_GDRVINFO;
+ ops->get_drvinfo(dev, &info);
+ for (; bp_desc_array[k]; k++)
+ if (!
+ (strcmp
+ (bp_desc_array[k],
+ info.driver)))
+ goto send_cmd;
+
+ }
+
+ }
+#endif
+ return -1;
+ }
+ }
+ send_cmd:
+ ret = do_cmd(dev, &ifr, IS_BYPASS, &data);
+ return (ret < 0 ? -1 : ret);
+}
+
+int is_bypass(int if_index)
+{
+ int ret = 0;
+ SET_BPLIB_INT_FN(is_bypass, int, if_index, ret);
+
+ if (ret < 0)
+ return is_bypass_dev(if_index);
+ return ret;
+}
+
+int get_bypass_slave(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_bypass_slave, GET_BYPASS_SLAVE, if_index);
+}
+
+int get_bypass_caps(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_bypass_caps, GET_BYPASS_CAPS, if_index);
+}
+
+int get_wd_set_caps(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_wd_set_caps, GET_WD_SET_CAPS, if_index);
+}
+
+int set_bypass(int if_index, int bypass_mode)
+{
+ DO_BPLIB_SET_ARG_FN(set_bypass, SET_BYPASS, if_index, bypass_mode);
+}
+
+int get_bypass(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_bypass, GET_BYPASS, if_index);
+}
+
+int get_bypass_change(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_bypass_change, GET_BYPASS_CHANGE, if_index);
+}
+
+int set_dis_bypass(int if_index, int dis_bypass)
+{
+ DO_BPLIB_SET_ARG_FN(set_dis_bypass, SET_DIS_BYPASS, if_index,
+ dis_bypass);
+}
+
+int get_dis_bypass(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_dis_bypass, GET_DIS_BYPASS, if_index);
+}
+
+int set_bypass_pwoff(int if_index, int bypass_mode)
+{
+ DO_BPLIB_SET_ARG_FN(set_bypass_pwoff, SET_BYPASS_PWOFF, if_index,
+ bypass_mode);
+}
+
+int get_bypass_pwoff(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_bypass_pwoff, GET_BYPASS_PWOFF, if_index);
+}
+
+int set_bypass_pwup(int if_index, int bypass_mode)
+{
+ DO_BPLIB_SET_ARG_FN(set_bypass_pwup, SET_BYPASS_PWUP, if_index,
+ bypass_mode);
+}
+
+int get_bypass_pwup(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_bypass_pwup, GET_BYPASS_PWUP, if_index);
+}
+
+int set_bypass_wd(int if_index, int ms_timeout, int *ms_timeout_set)
+{
+ int data = ms_timeout, ret = 0;
+ if (is_dev_sd(if_index))
+ SET_BPLIB_INT_FN3(set_bypass_wd, int, if_index, int, ms_timeout,
+ int *, ms_timeout_set, ret);
+ else {
+ ret = doit(SET_BYPASS_WD, if_index, &data);
+ if (ret > 0) {
+ *ms_timeout_set = ret;
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+int get_bypass_wd(int if_index, int *ms_timeout_set)
+{
+ int *data = ms_timeout_set, ret = 0;
+ if (is_dev_sd(if_index))
+ SET_BPLIB_INT_FN2(get_bypass_wd, int, if_index, int *,
+ ms_timeout_set, ret);
+ else
+ ret = doit(GET_BYPASS_WD, if_index, data);
+ return ret;
+}
+
+int get_wd_expire_time(int if_index, int *ms_time_left)
+{
+ int *data = ms_time_left, ret = 0;
+ if (is_dev_sd(if_index))
+ SET_BPLIB_INT_FN2(get_wd_expire_time, int, if_index, int *,
+ ms_time_left, ret);
+ else {
+ ret = doit(GET_WD_EXPIRE_TIME, if_index, data);
+ if ((ret == 0) && (*data != 0))
+ ret = 1;
+ }
+ return ret;
+}
+
+int reset_bypass_wd_timer(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(reset_bypass_wd_timer, RESET_BYPASS_WD_TIMER,
+ if_index);
+}
+
+int set_std_nic(int if_index, int bypass_mode)
+{
+ DO_BPLIB_SET_ARG_FN(set_std_nic, SET_STD_NIC, if_index, bypass_mode);
+}
+
+int get_std_nic(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_std_nic, GET_STD_NIC, if_index);
+}
+
+int set_tx(int if_index, int tx_state)
+{
+ DO_BPLIB_SET_ARG_FN(set_tx, SET_TX, if_index, tx_state);
+}
+
+int get_tx(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_tx, GET_TX, if_index);
+}
+
+int set_tap(int if_index, int tap_mode)
+{
+ DO_BPLIB_SET_ARG_FN(set_tap, SET_TAP, if_index, tap_mode);
+}
+
+int get_tap(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_tap, GET_TAP, if_index);
+}
+
+int get_tap_change(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_tap_change, GET_TAP_CHANGE, if_index);
+}
+
+int set_dis_tap(int if_index, int dis_tap)
+{
+ DO_BPLIB_SET_ARG_FN(set_dis_tap, SET_DIS_TAP, if_index, dis_tap);
+}
+
+int get_dis_tap(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_dis_tap, GET_DIS_TAP, if_index);
+}
+
+int set_tap_pwup(int if_index, int tap_mode)
+{
+ DO_BPLIB_SET_ARG_FN(set_tap_pwup, SET_TAP_PWUP, if_index, tap_mode);
+}
+
+int get_tap_pwup(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_tap_pwup, GET_TAP_PWUP, if_index);
+}
+
+int set_bp_disc(int if_index, int disc_mode)
+{
+ DO_BPLIB_SET_ARG_FN(set_bp_disc, SET_DISC, if_index, disc_mode);
+}
+
+int get_bp_disc(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_bp_disc, GET_DISC, if_index);
+}
+
+int get_bp_disc_change(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_bp_disc_change, GET_DISC_CHANGE, if_index);
+}
+
+int set_bp_dis_disc(int if_index, int dis_disc)
+{
+ DO_BPLIB_SET_ARG_FN(set_bp_dis_disc, SET_DIS_DISC, if_index, dis_disc);
+}
+
+int get_bp_dis_disc(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_bp_dis_disc, GET_DIS_DISC, if_index);
+}
+
+int set_bp_disc_pwup(int if_index, int disc_mode)
+{
+ DO_BPLIB_SET_ARG_FN(set_bp_disc_pwup, SET_DISC_PWUP, if_index,
+ disc_mode);
+}
+
+int get_bp_disc_pwup(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_bp_disc_pwup, GET_DISC_PWUP, if_index);
+}
+
+int set_wd_exp_mode(int if_index, int mode)
+{
+ DO_BPLIB_SET_ARG_FN(set_wd_exp_mode, SET_WD_EXP_MODE, if_index, mode);
+}
+
+int get_wd_exp_mode(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_wd_exp_mode, GET_WD_EXP_MODE, if_index);
+}
+
+int set_wd_autoreset(int if_index, int time)
+{
+ DO_BPLIB_SET_ARG_FN(set_wd_autoreset, SET_WD_AUTORESET, if_index, time);
+}
+
+int get_wd_autoreset(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_wd_autoreset, GET_WD_AUTORESET, if_index);
+}
+
+int set_tpl(int if_index, int tpl_mode)
+{
+ DO_BPLIB_SET_ARG_FN(set_tpl, SET_TPL, if_index, tpl_mode);
+}
+
+int get_tpl(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_tpl, GET_TPL, if_index);
+}
+
+int set_bp_hw_reset(int if_index, int mode)
+{
+ DO_BPLIB_SET_ARG_FN(set_tpl, SET_BP_HW_RESET, if_index, mode);
+}
+
+int get_bp_hw_reset(int if_index)
+{
+ DO_BPLIB_GET_ARG_FN(get_tpl, GET_BP_HW_RESET, if_index);
+}
+
+int get_bypass_info(int if_index, struct bp_info *bp_info)
+{
+ int ret = 0;
+ if (is_dev_sd(if_index)) {
+ SET_BPLIB_INT_FN2(get_bypass_info, int, if_index,
+ struct bp_info *, bp_info, ret);
+ } else {
+ static int (*ioctl) (struct net_device *, struct ifreq *, int);
+ struct net_device *dev;
+
+ struct net_device *n;
+ for_each_netdev_safe(&init_net, dev, n) {
+ if (dev->ifindex == if_index) {
+ struct if_bypass_info *bypass_cb;
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ bypass_cb = (struct if_bypass_info *)&ifr;
+ bypass_cb->cmd = GET_BYPASS_INFO;
+
+ if ((dev->netdev_ops) &&
+ (ioctl = dev->netdev_ops->ndo_do_ioctl)) {
+ ret = ioctl(dev, &ifr, SIOCGIFBYPASS);
+ }
+
+ else
+ ret = -1;
+ if (ret == 0)
+ memcpy(bp_info, &bypass_cb->bp_info,
+ sizeof(struct bp_info));
+ ret = (ret < 0 ? -1 : 0);
+ break;
+ }
+ }
+ }
+ return ret;
+}
+
+int init_lib_module()
+{
+
+ printk(VERSION);
+ return 0;
+}
+
+void cleanup_lib_module()
+{
+}
+
+EXPORT_SYMBOL_NOVERS(is_bypass);
+EXPORT_SYMBOL_NOVERS(get_bypass_slave);
+EXPORT_SYMBOL_NOVERS(get_bypass_caps);
+EXPORT_SYMBOL_NOVERS(get_wd_set_caps);
+EXPORT_SYMBOL_NOVERS(set_bypass);
+EXPORT_SYMBOL_NOVERS(get_bypass);
+EXPORT_SYMBOL_NOVERS(get_bypass_change);
+EXPORT_SYMBOL_NOVERS(set_dis_bypass);
+EXPORT_SYMBOL_NOVERS(get_dis_bypass);
+EXPORT_SYMBOL_NOVERS(set_bypass_pwoff);
+EXPORT_SYMBOL_NOVERS(get_bypass_pwoff);
+EXPORT_SYMBOL_NOVERS(set_bypass_pwup);
+EXPORT_SYMBOL_NOVERS(get_bypass_pwup);
+EXPORT_SYMBOL_NOVERS(set_bypass_wd);
+EXPORT_SYMBOL_NOVERS(get_bypass_wd);
+EXPORT_SYMBOL_NOVERS(get_wd_expire_time);
+EXPORT_SYMBOL_NOVERS(reset_bypass_wd_timer);
+EXPORT_SYMBOL_NOVERS(set_std_nic);
+EXPORT_SYMBOL_NOVERS(get_std_nic);
+EXPORT_SYMBOL_NOVERS(set_tx);
+EXPORT_SYMBOL_NOVERS(get_tx);
+EXPORT_SYMBOL_NOVERS(set_tap);
+EXPORT_SYMBOL_NOVERS(get_tap);
+EXPORT_SYMBOL_NOVERS(get_tap_change);
+EXPORT_SYMBOL_NOVERS(set_dis_tap);
+EXPORT_SYMBOL_NOVERS(get_dis_tap);
+EXPORT_SYMBOL_NOVERS(set_tap_pwup);
+EXPORT_SYMBOL_NOVERS(get_tap_pwup);
+EXPORT_SYMBOL_NOVERS(set_bp_disc);
+EXPORT_SYMBOL_NOVERS(get_bp_disc);
+EXPORT_SYMBOL_NOVERS(get_bp_disc_change);
+EXPORT_SYMBOL_NOVERS(set_bp_dis_disc);
+EXPORT_SYMBOL_NOVERS(get_bp_dis_disc);
+EXPORT_SYMBOL_NOVERS(set_bp_disc_pwup);
+EXPORT_SYMBOL_NOVERS(get_bp_disc_pwup);
+EXPORT_SYMBOL_NOVERS(set_wd_exp_mode);
+EXPORT_SYMBOL_NOVERS(get_wd_exp_mode);
+EXPORT_SYMBOL_NOVERS(set_wd_autoreset);
+EXPORT_SYMBOL_NOVERS(get_wd_autoreset);
+EXPORT_SYMBOL_NOVERS(set_tpl);
+EXPORT_SYMBOL_NOVERS(get_tpl);
+EXPORT_SYMBOL_NOVERS(set_bp_hw_reset);
+EXPORT_SYMBOL_NOVERS(get_bp_hw_reset);
+EXPORT_SYMBOL_NOVERS(get_bypass_info);
+
+module_init(init_lib_module);
+module_exit(cleanup_lib_module);
diff --git a/drivers/staging/silicom/bypasslib/libbp_sd.h b/drivers/staging/silicom/bypasslib/libbp_sd.h
new file mode 100644
index 000000000000..3b4f8364ed18
--- /dev/null
+++ b/drivers/staging/silicom/bypasslib/libbp_sd.h
@@ -0,0 +1,509 @@
+/******************************************************************************/
+/* */
+/* bypass library, Copyright (c) 2004 Silicom, Ltd */
+/* 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, located in the file LICENSE. */
+/* */
+/* Ver 1.0.0 */
+/* */
+/* libbypass.h */
+/* */
+/******************************************************************************/
+
+/**
+ * is_bypass - check if device is a Bypass controlling device
+ * @if_index: network device index
+ *
+ * Output:
+ * 1 - if device is bypass controlling device,
+ * 0 - if device is bypass slave device
+ * -1 - device not support Bypass
+ **/
+int is_bypass_sd(int if_index);
+
+/**
+ * get_bypass_slave - get second port participate in the Bypass pair
+ * @if_index: network device index
+ *
+ * Output:
+ * network device index of the slave device
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int get_bypass_slave_sd(int if_index);
+
+/**
+ * get_bypass_caps - get second port participate in the Bypass pair
+ * @if_index: network device index
+ *
+ * Output:
+ * flags word on success;flag word is a 32-bit mask word with each bit defines different
+ * capability as described bellow.
+ * Value of 1 for supporting this feature. 0 for not supporting this feature.
+ * -1 - on failure (if the device is not capable of the operation or not a Bypass device)
+ * Bit feature description
+ *
+ * 0 BP_CAP The interface is Bypass capable in general
+ *
+ * 1 BP_STATUS_CAP The interface can report of the current Bypass mode
+ *
+ * 2 BP_STATUS_CHANGE_CAP The interface can report on a change to bypass mode from
+ * the last time the mode was defined
+ *
+ * 3 SW_CTL_CAP The interface is Software controlled capable for bypass/non bypass modes.
+ *
+ * 4 BP_DIS_CAP The interface is capable of disabling the Bypass mode at all times.
+ * This mode will retain its mode even during power loss and also after
+ * power recovery. This will overcome on any bypass operation due to
+ * watchdog timeout or set bypass command.
+ *
+ * 5 BP_DIS_STATUS_CAP The interface can report of the current DIS_BP_CAP
+ *
+ * 6 STD_NIC_CAP The interface is capable to be configured to operate as standard, non Bypass,
+ * NIC interface (have direct connection to interfaces at all power modes)
+ *
+ * 7 BP_PWOFF_NO_CAP The interface can be in Bypass mode at power off state
+ *
+ * 8 BP_PWOFF_OFF_CAP The interface can disconnect the Bypass mode at power off state without
+ * effecting all the other states of operation
+ *
+ * 9 BP_PWOFF_CTL_CAP The behavior of the Bypass mode at Power-off state can be controlled by
+ * software without effecting any other state
+ *
+ *10 BP_PWUP_ON_CAP The interface can be in Bypass mode when power is turned on
+ * (until the system take control of the bypass functionality)
+ *
+ *11 BP_PWUP_OFF_CAP The interface can disconnect from Bypass mode when power is turned on
+ * (until the system take control of the bypass functionality)
+ *
+ *12 BP_PWUP_CTL_CAP The behavior of the Bypass mode at Power-up can be controlled by software
+ *
+ *13 WD_CTL_CAP The interface has watchdog capabilities to turn to Bypass mode when not reset
+ * for defined period of time.
+ *
+ *14 WD_STATUS_CAP The interface can report on the watchdog status (Active/inactive)
+ *
+ *15 WD_TIMEOUT_CAP The interface can report the time left till watchdog triggers to Bypass mode.
+ *
+ *16-31 RESERVED
+ *
+ * **/
+int get_bypass_caps_sd(int if_index);
+
+/**
+ * get_wd_set_caps - Obtain watchdog timer setting capabilities
+ * @if_index: network device index
+ *
+ * Output:
+ *
+ * Set of numbers defining the various parameters of the watchdog capable
+ * to be set to as described bellow.
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ *
+ * Bit feature description
+ *
+ * 0-3 WD_MIN_TIME The interface WD minimal time period in 100mS units
+ *
+ * 4 WD_STEP_TIME The steps of the WD timer in
+ * 0 - for linear steps (WD_MIN_TIME * X)
+ * 1 - for multiply by 2 from previous step (WD_MIN_TIME * 2^X)
+ *
+ * 5-8 WD_STEP_COUNT Number of steps the WD timer supports in 2^X
+ * (X bit available for defining the value)
+ *
+ *
+ *
+ **/
+int get_wd_set_caps_sd(int if_index);
+
+/**
+ * set_bypass - set Bypass state
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: bypass mode (1=on, 0=off)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int set_bypass_sd(int if_index, int bypass_mode);
+
+/**
+ * get_bypass - Get Bypass mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int get_bypass_sd(int if_index);
+
+/**
+ * get_bypass_change - Get change of Bypass mode state from last status check
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int get_bypass_change_sd(int if_index);
+
+/**
+ * set_dis_bypass - Set Disable Bypass mode
+ * @if_index: network device index of the controlling device
+ * @dis_bypass: disable bypass(1=dis, 0=en)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int set_dis_bypass_sd(int if_index, int dis_bypass);
+
+/**
+ * get_dis_bypass - Get Disable Bypass mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (normal Bypass mode/ Disable bypass)
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int get_dis_bypass_sd(int if_index);
+
+/**
+ * set_bypass_pwoff - Set Bypass mode at power-off state
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: bypass mode setting at power off state (1=BP en, 0=BP Dis)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int set_bypass_pwoff_sd(int if_index, int bypass_mode);
+
+/**
+ * get_bypass_pwoff - Get Bypass mode state at power-off state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (Disable bypass at power off state / normal Bypass mode)
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int get_bypass_pwoff_sd(int if_index);
+
+/**
+ * set_bypass_pwup - Set Bypass mode at power-up state
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: bypass mode setting at power up state (1=BP en, 0=BP Dis)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int set_bypass_pwup_sd(int if_index, int bypass_mode);
+
+/**
+ * get_bypass_pwup - Get Bypass mode state at power-up state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (Disable bypass at power up state / normal Bypass mode)
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int get_bypass_pwup_sd(int if_index);
+
+/**
+ * set_bypass_wd - Set watchdog state
+ * @if_index: network device index of the controlling device
+ * @ms_timeout: requested timeout (in ms units), 0 for disabling the watchdog timer
+ * @ms_timeout_set(output): requested timeout (in ms units),
+ * that the adapter supports and will be used by the watchdog
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int set_bypass_wd_sd(int if_index, int ms_timeout, int *ms_timeout_set);
+
+/**
+ * get_bypass_wd - Get watchdog state
+ * @if_index: network device index of the controlling device
+ * @ms_timeout (output): WDT timeout (in ms units),
+ * -1 for unknown wdt status
+ * 0 if WDT is disabled
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int get_bypass_wd_sd(int if_index, int *ms_timeout_set);
+
+/**
+ * get_wd_expire_time - Get watchdog expire
+ * @if_index: network device index of the controlling device
+ * @ms_time_left (output): time left till watchdog time expire,
+ * -1 if WDT has expired
+ * 0 if WDT is disabled
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device or unknown wdt status)
+ **/
+int get_wd_expire_time_sd(int if_index, int *ms_time_left);
+
+/**
+ * reset_bypass_wd_timer - Reset watchdog timer
+ * @if_index: network device index of the controlling device
+ *
+ * Output:
+ * 1 - on success
+ * 0 - watchdog is not configured
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device or unknown wdt status)
+ **/
+int reset_bypass_wd_timer_sd(int if_index);
+
+/**
+ * set_std_nic - Standard NIC mode of operation
+ * @if_index: network device index of the controlling device
+ * @nic_mode: 0/1 (Default Bypass mode / Standard NIC mode)
+ *
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int set_std_nic_sd(int if_index, int nic_mode);
+
+/**
+ * get_std_nic - Get Standard NIC mode setting
+ * @if_index: network device index of the controlling device
+ *
+ * Output:
+ * 0/1 (Default Bypass mode / Standard NIC mode) on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int get_std_nic_sd(int if_index);
+
+/**
+ * set_tx - set transmitter enable/disable
+ * @if_index: network device index of the controlling device
+ * @tx_state: 0/1 (Transmit Disable / Transmit Enable)
+ *
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation )
+ **/
+int set_tx_sd(int if_index, int tx_state);
+
+/**
+ * get_std_nic - get transmitter state (disable / enable)
+ * @if_index: network device index of the controlling device
+ *
+ * Output:
+ * 0/1 (ransmit Disable / Transmit Enable) on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass)
+ **/
+int get_tx_sd(int if_index);
+
+/**
+ * set_tap - set TAP state
+ * @if_index: network device index of the controlling device
+ * @tap_mode: 1 tap mode , 0 normal nic mode
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device not support TAP or it's a slave device)
+ **/
+int set_tap_sd(int if_index, int tap_mode);
+
+/**
+ * get_tap - Get TAP mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support TAP or it's a slave device)
+ **/
+int get_tap_sd(int if_index);
+
+/**
+ * get_tap_change - Get change of TAP mode state from last status check
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support TAP or it's a slave device)
+ **/
+int get_tap_change_sd(int if_index);
+
+/**
+ * set_dis_tap - Set Disable TAP mode
+ * @if_index: network device index of the controlling device
+ * @dis_tap: disable tap(1=dis, 0=en)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support TAP
+ * or it's a slave device)
+ **/
+int set_dis_tap_sd(int if_index, int dis_tap);
+
+/**
+ * get_dis_tap - Get Disable TAP mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (normal TAP mode/ Disable TAP)
+ * -1 - on failure (device is not capable of the operation ordevice not support TAP
+ * or it's a slave device)
+ **/
+int get_dis_tap_sd(int if_index);
+
+/**
+ * set_tap_pwup - Set TAP mode at power-up state
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: tap mode setting at power up state (1=TAP en, 0=TAP Dis)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support TAP
+ * or it's a slave device)
+ **/
+int set_tap_pwup_sd(int if_index, int tap_mode);
+
+/**
+ * get_tap_pwup - Get TAP mode state at power-up state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (Disable TAP at power up state / normal TAP mode)
+ * -1 - on failure (device is not capable of the operation ordevice not support TAP
+ * or it's a slave device)
+ **/
+int get_tap_pwup_sd(int if_index);
+
+/**
+ * set_bp_disc - set Disconnect state
+ * @if_index: network device index of the controlling device
+ * @tap_mode: 1 disc mode , 0 non-disc mode
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device not support Disconnect or it's a slave device)
+ **/
+int set_bp_disc_sd(int if_index, int disc_mode);
+
+/**
+ * get_bp_disc - Get Disconnect mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support Disconnect or it's a slave device)
+ **/
+int get_bp_disc_sd(int if_index);
+
+/**
+ * get_bp_disc_change - Get change of Disconnect mode state from last status check
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support Disconnect or it's a slave device)
+ **/
+int get_bp_disc_change_sd(int if_index);
+
+/**
+ * set_bp_dis_disc - Set Disable Disconnect mode
+ * @if_index: network device index of the controlling device
+ * @dis_tap: disable tap(1=dis, 0=en)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable ofthe operation ordevice not support Disconnect
+ * or it's a slave device)
+ **/
+int set_bp_dis_disc_sd(int if_index, int dis_disc);
+
+/**
+ * get_dis_tap - Get Disable Disconnect mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (normal Disconnect mode/ Disable Disconnect)
+ * -1 - on failure (device is not capable of the operation ordevice not support Disconnect
+ * or it's a slave device)
+ **/
+int get_bp_dis_disc_sd(int if_index);
+
+/**
+ * set_bp_disc_pwup - Set Disconnect mode at power-up state
+ * @if_index: network device index of the controlling device
+ * @disc_mode: tap mode setting at power up state (1=Disc en, 0=Disc Dis)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Disconnect
+ * or it's a slave device)
+ **/
+int set_bp_disc_pwup_sd(int if_index, int disc_mode);
+
+/**
+ * get_bp_disc_pwup - Get Disconnect mode state at power-up state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (Disable Disconnect at power up state / normal Disconnect mode)
+ * -1 - on failure (device is not capable of the operation ordevice not support TAP
+ * or it's a slave device)
+ **/
+int get_bp_disc_pwup_sd(int if_index);
+
+/**
+ * set_wd_exp_mode - Set adapter state when WDT expired.
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: adapter mode (1=tap mode, 0=bypass mode)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int set_wd_exp_mode_sd(int if_index, int bypass_mode);
+
+/**
+ * get_wd_exp_mode - Get adapter state when WDT expired.
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (bypass/tap) on success
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int get_wd_exp_mode_sd(int if_index);
+
+/**
+ * set_wd_autoreset - reset WDT periodically.
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: adapter mode (1=tap mode, 0=bypass mode)
+ * Output:
+ * 1 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device or unknown wdt status)
+ **/
+int set_wd_autoreset_sd(int if_index, int time);
+
+/**
+ * set_wd_autoreset - reset WDT periodically.
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: adapter mode (1=tap mode, 0=bypass mode)
+ * Output:
+ * 1 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device or unknown wdt status)
+ **/
+int get_wd_autoreset_sd(int if_index);
+
+/**
+ * set_tpl - set TPL state
+ * @if_index: network device index of the controlling device
+ * @tpl_mode: 1 tpl mode , 0 normal nic mode
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device not support TPL)
+ **/
+int set_tpl_sd(int if_index, int tpl_mode);
+
+/**
+ * get_tpl - Get TPL mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support TPL or it's a slave device)
+ **/
+int get_tpl_sd(int if_index);
+
+int get_bypass_info_sd(int if_index, struct bp_info *bp_info);
+int bp_if_scan_sd(void);
+/*int get_dev_num_sd(void);*/
diff --git a/drivers/staging/silicom/libbp_sd.h b/drivers/staging/silicom/libbp_sd.h
new file mode 100644
index 000000000000..065277f81c78
--- /dev/null
+++ b/drivers/staging/silicom/libbp_sd.h
@@ -0,0 +1,550 @@
+/******************************************************************************/
+/* */
+/* bypass library, Copyright (c) 2004 Silicom, Ltd */
+/* 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, located in the file LICENSE. */
+/* */
+/* Ver 1.0.0 */
+/* */
+/* libbypass.h */
+/* */
+/******************************************************************************/
+
+#define BP_CAP 0x01 /* BIT_0 */
+#define BP_STATUS_CAP 0x02
+#define BP_STATUS_CHANGE_CAP 0x04
+#define SW_CTL_CAP 0x08
+#define BP_DIS_CAP 0x10
+#define BP_DIS_STATUS_CAP 0x20
+#define STD_NIC_CAP 0x40
+#define BP_PWOFF_ON_CAP 0x80
+#define BP_PWOFF_OFF_CAP 0x0100
+#define BP_PWOFF_CTL_CAP 0x0200
+#define BP_PWUP_ON_CAP 0x0400
+#define BP_PWUP_OFF_CAP 0x0800
+#define BP_PWUP_CTL_CAP 0x1000
+#define WD_CTL_CAP 0x2000
+#define WD_STATUS_CAP 0x4000
+#define WD_TIMEOUT_CAP 0x8000
+#define TX_CTL_CAP 0x10000
+#define TX_STATUS_CAP 0x20000
+#define TAP_CAP 0x40000
+#define TAP_STATUS_CAP 0x80000
+#define TAP_STATUS_CHANGE_CAP 0x100000
+#define TAP_DIS_CAP 0x200000
+#define TAP_DIS_STATUS_CAP 0x400000
+#define TAP_PWUP_ON_CAP 0x800000
+#define TAP_PWUP_OFF_CAP 0x1000000
+#define TAP_PWUP_CTL_CAP 0x2000000
+#define NIC_CAP_NEG 0x4000000 /* BIT 26 */
+
+#define WD_MIN_TIME_GET(desc) (desc & 0xf)
+#define WDT_STEP_TIME 0x10
+
+struct bp_info {
+ char prod_name[14];
+ unsigned char fw_ver;
+};
+
+/**
+ * is_bypass - check if device is a Bypass controlling device
+ * @if_index: network device index
+ *
+ * Output:
+ * 1 - if device is bypass controlling device,
+ * 0 - if device is bypass slave device
+ * -1 - device not support Bypass
+ **/
+int is_bypass_sd(int if_index);
+
+/**
+ * get_bypass_slave - get second port participate in the Bypass pair
+ * @if_index: network device index
+ *
+ * Output:
+ * network device index of the slave device
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int get_bypass_slave_sd(int if_index);
+
+/**
+ * get_bypass_caps - get second port participate in the Bypass pair
+ * @if_index: network device index
+ *
+ * Output:
+ * flags word on success;flag word is a 32-bit mask word with each bit defines different
+ * capability as described bellow.
+ * Value of 1 for supporting this feature. 0 for not supporting this feature.
+ * -1 - on failure (if the device is not capable of the operation or not a Bypass device)
+ * Bit feature description
+ *
+ * 0 BP_CAP The interface is Bypass capable in general
+ *
+ * 1 BP_STATUS_CAP The interface can report of the current Bypass mode
+ *
+ * 2 BP_STATUS_CHANGE_CAP The interface can report on a change to bypass mode from
+ * the last time the mode was defined
+ *
+ * 3 SW_CTL_CAP The interface is Software controlled capable for bypass/non bypass modes.
+ *
+ * 4 BP_DIS_CAP The interface is capable of disabling the Bypass mode at all times.
+ * This mode will retain its mode even during power loss and also after
+ * power recovery. This will overcome on any bypass operation due to
+ * watchdog timeout or set bypass command.
+ *
+ * 5 BP_DIS_STATUS_CAP The interface can report of the current DIS_BP_CAP
+ *
+ * 6 STD_NIC_CAP The interface is capable to be configured to operate as standard, non Bypass,
+ * NIC interface (have direct connection to interfaces at all power modes)
+ *
+ * 7 BP_PWOFF_NO_CAP The interface can be in Bypass mode at power off state
+ *
+ * 8 BP_PWOFF_OFF_CAP The interface can disconnect the Bypass mode at power off state without
+ * effecting all the other states of operation
+ *
+ * 9 BP_PWOFF_CTL_CAP The behavior of the Bypass mode at Power-off state can be controlled by
+ * software without effecting any other state
+ *
+ *10 BP_PWUP_ON_CAP The interface can be in Bypass mode when power is turned on
+ * (until the system take control of the bypass functionality)
+ *
+ *11 BP_PWUP_OFF_CAP The interface can disconnect from Bypass mode when power is turned on
+ * (until the system take control of the bypass functionality)
+ *
+ *12 BP_PWUP_CTL_CAP The behavior of the Bypass mode at Power-up can be controlled by software
+ *
+ *13 WD_CTL_CAP The interface has watchdog capabilities to turn to Bypass mode when not reset
+ * for defined period of time.
+ *
+ *14 WD_STATUS_CAP The interface can report on the watchdog status (Active/inactive)
+ *
+ *15 WD_TIMEOUT_CAP The interface can report the time left till watchdog triggers to Bypass mode.
+ *
+ *16-31 RESERVED
+ *
+ * **/
+int get_bypass_caps_sd(int if_index);
+
+/**
+ * get_wd_set_caps - Obtain watchdog timer setting capabilities
+ * @if_index: network device index
+ *
+ * Output:
+ *
+ * Set of numbers defining the various parameters of the watchdog capable
+ * to be set to as described bellow.
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ *
+ * Bit feature description
+ *
+ * 0-3 WD_MIN_TIME The interface WD minimal time period in 100mS units
+ *
+ * 4 WD_STEP_TIME The steps of the WD timer in
+ * 0 - for linear steps (WD_MIN_TIME * X)
+ * 1 - for multiply by 2 from previous step (WD_MIN_TIME * 2^X)
+ *
+ * 5-8 WD_STEP_COUNT Number of steps the WD timer supports in 2^X
+ * (X bit available for defining the value)
+ *
+ *
+ *
+ **/
+int get_wd_set_caps_sd(int if_index);
+
+/**
+ * set_bypass - set Bypass state
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: bypass mode (1=on, 0=off)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int set_bypass_sd(int if_index, int bypass_mode);
+
+/**
+ * get_bypass - Get Bypass mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int get_bypass_sd(int if_index);
+
+/**
+ * get_bypass_change - Get change of Bypass mode state from last status check
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int get_bypass_change_sd(int if_index);
+
+/**
+ * set_dis_bypass - Set Disable Bypass mode
+ * @if_index: network device index of the controlling device
+ * @dis_bypass: disable bypass(1=dis, 0=en)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int set_dis_bypass_sd(int if_index, int dis_bypass);
+
+/**
+ * get_dis_bypass - Get Disable Bypass mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (normal Bypass mode/ Disable bypass)
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int get_dis_bypass_sd(int if_index);
+
+/**
+ * set_bypass_pwoff - Set Bypass mode at power-off state
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: bypass mode setting at power off state (1=BP en, 0=BP Dis)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int set_bypass_pwoff_sd(int if_index, int bypass_mode);
+
+/**
+ * get_bypass_pwoff - Get Bypass mode state at power-off state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (Disable bypass at power off state / normal Bypass mode)
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int get_bypass_pwoff_sd(int if_index);
+
+/**
+ * set_bypass_pwup - Set Bypass mode at power-up state
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: bypass mode setting at power up state (1=BP en, 0=BP Dis)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int set_bypass_pwup_sd(int if_index, int bypass_mode);
+
+/**
+ * get_bypass_pwup - Get Bypass mode state at power-up state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (Disable bypass at power up state / normal Bypass mode)
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int get_bypass_pwup_sd(int if_index);
+
+/**
+ * set_bypass_wd - Set watchdog state
+ * @if_index: network device index of the controlling device
+ * @ms_timeout: requested timeout (in ms units), 0 for disabling the watchdog timer
+ * @ms_timeout_set(output): requested timeout (in ms units),
+ * that the adapter supports and will be used by the watchdog
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int set_bypass_wd_sd(int if_index, int ms_timeout, int *ms_timeout_set);
+
+/**
+ * get_bypass_wd - Get watchdog state
+ * @if_index: network device index of the controlling device
+ * @ms_timeout (output): WDT timeout (in ms units),
+ * -1 for unknown wdt status
+ * 0 if WDT is disabled
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int get_bypass_wd_sd(int if_index, int *ms_timeout_set);
+
+/**
+ * get_wd_expire_time - Get watchdog expire
+ * @if_index: network device index of the controlling device
+ * @ms_time_left (output): time left till watchdog time expire,
+ * -1 if WDT has expired
+ * 0 if WDT is disabled
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device or unknown wdt status)
+ **/
+int get_wd_expire_time_sd(int if_index, int *ms_time_left);
+
+/**
+ * reset_bypass_wd_timer - Reset watchdog timer
+ * @if_index: network device index of the controlling device
+ *
+ * Output:
+ * 1 - on success
+ * 0 - watchdog is not configured
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device or unknown wdt status)
+ **/
+int reset_bypass_wd_timer_sd(int if_index);
+
+/**
+ * set_std_nic - Standard NIC mode of operation
+ * @if_index: network device index of the controlling device
+ * @nic_mode: 0/1 (Default Bypass mode / Standard NIC mode)
+ *
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int set_std_nic_sd(int if_index, int nic_mode);
+
+/**
+ * get_std_nic - Get Standard NIC mode setting
+ * @if_index: network device index of the controlling device
+ *
+ * Output:
+ * 0/1 (Default Bypass mode / Standard NIC mode) on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device)
+ **/
+int get_std_nic_sd(int if_index);
+
+/**
+ * set_tx - set transmitter enable/disable
+ * @if_index: network device index of the controlling device
+ * @tx_state: 0/1 (Transmit Disable / Transmit Enable)
+ *
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation )
+ **/
+int set_tx_sd(int if_index, int tx_state);
+
+/**
+ * get_tx - get transmitter state (disable / enable)
+ * @if_index: network device index of the controlling device
+ *
+ * Output:
+ * 0/1 (ransmit Disable / Transmit Enable) on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass)
+ **/
+int get_tx_sd(int if_index);
+
+/**
+ * set_tpl - set TPL enable/disable
+ * @if_index: network device index of the controlling device
+ * @tx_state: 0/1 (TPL Disable / TPL Enable)
+ *
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation )
+ **/
+int set_tpl_sd(int if_index, int tpl_state);
+
+/**
+ * get_tpl - get TPL state (disable / enable)
+ * @if_index: network device index of the controlling device
+ *
+ * Output:
+ * 0/1 (TPL Disable / TPL Enable) on success
+ * -1 - on failure (device is not capable of the operation)
+ **/
+int get_tpl_sd(int if_index);
+
+int get_bp_hw_reset_sd(int if_index);
+
+int set_bp_hw_reset_sd(int if_index, int status);
+
+/**
+ * set_tap - set TAP state
+ * @if_index: network device index of the controlling device
+ * @tap_mode: 1 tap mode , 0 normal nic mode
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device not support TAP or it's a slave device)
+ **/
+int set_tap_sd(int if_index, int tap_mode);
+
+/**
+ * get_tap - Get TAP mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support TAP or it's a slave device)
+ **/
+int get_tap_sd(int if_index);
+
+/**
+ * get_tap_change - Get change of TAP mode state from last status check
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support TAP or it's a slave device)
+ **/
+int get_tap_change_sd(int if_index);
+
+/**
+ * set_dis_tap - Set Disable TAP mode
+ * @if_index: network device index of the controlling device
+ * @dis_tap: disable tap(1=dis, 0=en)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support TAP
+ * or it's a slave device)
+ **/
+int set_dis_tap_sd(int if_index, int dis_tap);
+
+/**
+ * get_dis_tap - Get Disable TAP mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (normal TAP mode/ Disable TAP)
+ * -1 - on failure (device is not capable of the operation ordevice not support TAP
+ * or it's a slave device)
+ **/
+int get_dis_tap_sd(int if_index);
+
+/**
+ * set_tap_pwup - Set TAP mode at power-up state
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: tap mode setting at power up state (1=TAP en, 0=TAP Dis)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support TAP
+ * or it's a slave device)
+ **/
+int set_tap_pwup_sd(int if_index, int tap_mode);
+
+/**
+ * get_tap_pwup - Get TAP mode state at power-up state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (Disable TAP at power up state / normal TAP mode)
+ * -1 - on failure (device is not capable of the operation ordevice not support TAP
+ * or it's a slave device)
+ **/
+int get_tap_pwup_sd(int if_index);
+
+/**
+ * set_wd_exp_mode - Set adapter state when WDT expired.
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: adapter mode (1=tap mode, 0=bypass mode)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int set_wd_exp_mode_sd(int if_index, int bypass_mode);
+
+/**
+ * get_wd_exp_mode - Get adapter state when WDT expired.
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (bypass/tap) on success
+ * -1 - on failure (device not support Bypass or it's a slave device)
+ **/
+int get_wd_exp_mode_sd(int if_index);
+
+/**
+ * set_wd_autoreset - reset WDT periodically.
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: adapter mode (1=tap mode, 0=bypass mode)
+ * Output:
+ * 1 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device or unknown wdt status)
+ **/
+int set_wd_autoreset_sd(int if_index, int time);
+
+/**
+ * set_wd_autoreset - reset WDT periodically.
+ * @if_index: network device index of the controlling device
+ * @bypass_mode: adapter mode (1=tap mode, 0=bypass mode)
+ * Output:
+ * 1 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support Bypass
+ * or it's a slave device or unknown wdt status)
+ **/
+int get_wd_autoreset_sd(int if_index);
+/**
+ * set_disc - set DISC state
+ * @if_index: network device index of the controlling device
+ * @tap_mode: 1 DISC mode , 0 normal nic mode
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device not support disconnect or it's a slave device)
+ **/
+int set_bp_disc_sd(int if_index, int disc_mode);
+
+/**
+ * get_disc - Get disc mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support disconnect or it's a slave device)
+ **/
+int get_bp_disc_sd(int if_index);
+
+/**
+ * get_disc_change - Get change of DISC mode state from last status check
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - (off/on) on success
+ * -1 - on failure (device not support disconnect or it's a slave device)
+ **/
+int get_bp_disc_change_sd(int if_index);
+
+/**
+ * set_dis_disc - Set Disable DISC mode
+ * @if_index: network device index of the controlling device
+ * @dis_disc: disable disconnect(1=dis, 0=en)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support DISC
+ * or it's a slave device)
+ **/
+int set_bp_dis_disc_sd(int if_index, int dis_disc);
+
+/**
+ * get_dis_disc - Get Disable DISC mode state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (normal DISC mode/ Disable DISC)
+ * -1 - on failure (device is not capable of the operation ordevice not support TAP
+ * or it's a slave device)
+ **/
+int get_bp_dis_disc_sd(int if_index);
+
+/**
+ * set_disc_pwup - Set DISC mode at power-up state
+ * @if_index: network device index of the controlling device
+ * @disc_mode: DISC mode setting at power up state (1= en, 0= Dis)
+ * Output:
+ * 0 - on success
+ * -1 - on failure (device is not capable of the operation ordevice not support DISC
+ * or it's a slave device)
+ **/
+int set_bp_disc_pwup_sd(int if_index, int disc_mode);
+
+/**
+ * get_disc_pwup - Get DISC mode state at power-up state
+ * @if_index: network device index of the controlling device
+ * Output:
+ * 0/1 - on success (Disable DISC at power up state / normal DISC mode)
+ * -1 - on failure (device is not capable of the operation ordevice not support DISC
+ * or it's a slave device)
+ **/
+int get_bp_disc_pwup_sd(int if_index);
+
+int get_bypass_info_sd(int if_index, struct bp_info *bp_info);
+int bp_if_scan_sd(void);
+/*int get_dev_num_sd(void);*/
diff --git a/drivers/staging/slicoss/slicoss.c b/drivers/staging/slicoss/slicoss.c
index 56829fc032ff..cd920dad85cd 100644
--- a/drivers/staging/slicoss/slicoss.c
+++ b/drivers/staging/slicoss/slicoss.c
@@ -514,8 +514,7 @@ static int slic_card_download_gbrcv(struct adapter *adapter)
file = "slicoss/gbrcvucode.sys";
break;
default:
- ASSERT(0);
- break;
+ return -ENOENT;
}
ret = request_firmware(&fw, file, &adapter->pcidev->dev);
@@ -529,15 +528,16 @@ static int slic_card_download_gbrcv(struct adapter *adapter)
index += 4;
switch (adapter->devid) {
case SLIC_2GB_DEVICE_ID:
- if (rcvucodelen != OasisRcvUCodeLen)
+ if (rcvucodelen != OasisRcvUCodeLen) {
+ release_firmware(fw);
return -EINVAL;
+ }
break;
case SLIC_1GB_DEVICE_ID:
- if (rcvucodelen != GBRcvUCodeLen)
+ if (rcvucodelen != GBRcvUCodeLen) {
+ release_firmware(fw);
return -EINVAL;
- break;
- default:
- ASSERT(0);
+ }
break;
}
/* start download */
@@ -2552,7 +2552,6 @@ static void slic_mcast_set_list(struct net_device *dev)
if (status == 0)
slic_mcast_set_mask(adapter);
}
- return;
}
#define XMIT_FAIL_LINK_STATE 1
@@ -3132,7 +3131,6 @@ static int slic_entry_open(struct net_device *dev)
{
struct adapter *adapter = netdev_priv(dev);
struct sliccard *card = adapter->card;
- u32 locked = 0;
int status;
ASSERT(adapter);
@@ -3142,7 +3140,6 @@ static int slic_entry_open(struct net_device *dev)
spin_lock_irqsave(&slic_global.driver_lock.lock,
slic_global.driver_lock.flags);
- locked = 1;
if (!adapter->activated) {
card->adapters_activated++;
slic_global.num_slic_ports_active++;
@@ -3156,23 +3153,15 @@ static int slic_entry_open(struct net_device *dev)
slic_global.num_slic_ports_active--;
adapter->activated = 0;
}
- if (locked) {
- spin_unlock_irqrestore(&slic_global.driver_lock.lock,
- slic_global.driver_lock.flags);
- locked = 0;
- }
- return status;
+ goto spin_unlock;
}
if (!card->master)
card->master = adapter;
- if (locked) {
- spin_unlock_irqrestore(&slic_global.driver_lock.lock,
- slic_global.driver_lock.flags);
- locked = 0;
- }
-
- return 0;
+spin_unlock:
+ spin_unlock_irqrestore(&slic_global.driver_lock.lock,
+ slic_global.driver_lock.flags);
+ return status;
}
static void slic_card_cleanup(struct sliccard *card)
@@ -3712,9 +3701,8 @@ static void slic_init_adapter(struct net_device *netdev,
phys_shmem);
ASSERT(adapter->pshmem);
- memset(adapter->pshmem, 0, sizeof(struct slic_shmem));
-
- return;
+ if (adapter->pshmem)
+ memset(adapter->pshmem, 0, sizeof(struct slic_shmem));
}
static const struct net_device_ops slic_netdev_ops = {
diff --git a/drivers/staging/sm7xxfb/sm7xx.h b/drivers/staging/sm7xxfb/sm7xx.h
index 333f33c3dc66..85998615b801 100644
--- a/drivers/staging/sm7xxfb/sm7xx.h
+++ b/drivers/staging/sm7xxfb/sm7xx.h
@@ -29,7 +29,7 @@
#define dac_reg (0x3c8)
#define dac_val (0x3c9)
-extern char __iomem *smtc_RegBaseAddress;
+extern void __iomem *smtc_RegBaseAddress;
#define smtc_mmiowb(dat, reg) writeb(dat, smtc_RegBaseAddress + reg)
#define smtc_mmioww(dat, reg) writew(dat, smtc_RegBaseAddress + reg)
#define smtc_mmiowl(dat, reg) writel(dat, smtc_RegBaseAddress + reg)
diff --git a/drivers/staging/sm7xxfb/sm7xxfb.c b/drivers/staging/sm7xxfb/sm7xxfb.c
index 1c1780c70fbb..f27182d4dea6 100644
--- a/drivers/staging/sm7xxfb/sm7xxfb.c
+++ b/drivers/staging/sm7xxfb/sm7xxfb.c
@@ -43,11 +43,11 @@ struct smtcfb_info {
u16 chip_id;
u8 chip_rev_id;
- unsigned char __iomem *m_pMMIO;
- char __iomem *m_pLFB;
- char *m_pDPR;
- char *m_pVPR;
- char *m_pCPR;
+ void __iomem *lfb; /* linear frame buffer */
+ void __iomem *dp_regs; /* drawing processor control regs */
+ void __iomem *vp_regs; /* video processor control regs */
+ void __iomem *cp_regs; /* capture processor control regs */
+ void __iomem *mmio; /* memory map IO port */
u_int width;
u_int height;
@@ -56,8 +56,7 @@ struct smtcfb_info {
u32 colreg[17];
};
-char __iomem *smtc_RegBaseAddress; /* Memory Map IO starting address */
-char __iomem *smtc_VRAMBaseAddress; /* video memory starting address */
+void __iomem *smtc_RegBaseAddress; /* Memory Map IO starting address */
static struct fb_var_screeninfo smtcfb_var = {
.xres = 1024,
@@ -72,14 +71,20 @@ static struct fb_var_screeninfo smtcfb_var = {
.height = -1,
.width = -1,
.vmode = FB_VMODE_NONINTERLACED,
+ .nonstd = 0,
+ .accel_flags = FB_ACCELF_TEXT,
};
static struct fb_fix_screeninfo smtcfb_fix = {
- .id = "sm712fb",
+ .id = "smXXXfb",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_TRUECOLOR,
.line_length = 800 * 3,
.accel = FB_ACCEL_SMI_LYNX,
+ .type_aux = 0,
+ .xpanstep = 0,
+ .ypanstep = 0,
+ .ywrapstep = 0,
};
struct vesa_mode {
@@ -545,28 +550,28 @@ static void sm7xx_set_timing(struct smtcfb_info *sfb)
smtc_mmiowb(0x67, 0x3c2);
/* set VPR registers */
- writel(0x0, sfb->m_pVPR + 0x0C);
- writel(0x0, sfb->m_pVPR + 0x40);
+ writel(0x0, sfb->vp_regs + 0x0C);
+ writel(0x0, sfb->vp_regs + 0x40);
/* set data width */
m_nScreenStride =
(sfb->width * sfb->fb.var.bits_per_pixel) / 64;
switch (sfb->fb.var.bits_per_pixel) {
case 8:
- writel(0x0, sfb->m_pVPR + 0x0);
+ writel(0x0, sfb->vp_regs + 0x0);
break;
case 16:
- writel(0x00020000, sfb->m_pVPR + 0x0);
+ writel(0x00020000, sfb->vp_regs + 0x0);
break;
case 24:
- writel(0x00040000, sfb->m_pVPR + 0x0);
+ writel(0x00040000, sfb->vp_regs + 0x0);
break;
case 32:
- writel(0x00030000, sfb->m_pVPR + 0x0);
+ writel(0x00030000, sfb->vp_regs + 0x0);
break;
}
writel((u32) (((m_nScreenStride + 2) << 16) | m_nScreenStride),
- sfb->m_pVPR + 0x10);
+ sfb->vp_regs + 0x10);
}
@@ -673,9 +678,9 @@ static struct fb_ops smtcfb_ops = {
};
/*
- * alloc struct smtcfb_info and assign the default value
+ * alloc struct smtcfb_info and assign default values
*/
-static struct smtcfb_info *smtc_alloc_fb_info(struct pci_dev *pdev, char *name)
+static struct smtcfb_info *smtc_alloc_fb_info(struct pci_dev *pdev)
{
struct smtcfb_info *sfb;
@@ -686,32 +691,12 @@ static struct smtcfb_info *smtc_alloc_fb_info(struct pci_dev *pdev, char *name)
sfb->pdev = pdev;
- /* init sfb->fb with default value */
-
- sfb->fb.flags = FBINFO_FLAG_DEFAULT;
- sfb->fb.fbops = &smtcfb_ops;
-
- sfb->fb.fix = smtcfb_fix;
- strcpy(sfb->fb.fix.id, name);
-
- sfb->fb.fix.type = FB_TYPE_PACKED_PIXELS;
- sfb->fb.fix.type_aux = 0;
- sfb->fb.fix.xpanstep = 0;
- sfb->fb.fix.ypanstep = 0;
- sfb->fb.fix.ywrapstep = 0;
- sfb->fb.fix.accel = FB_ACCEL_SMI_LYNX;
-
- sfb->fb.var = smtcfb_var;
- sfb->fb.var.nonstd = 0;
- sfb->fb.var.activate = FB_ACTIVATE_NOW;
- sfb->fb.var.height = -1;
- sfb->fb.var.width = -1;
- sfb->fb.var.accel_flags = FB_ACCELF_TEXT;
- sfb->fb.var.vmode = FB_VMODE_NONINTERLACED;
-
- sfb->fb.pseudo_palette = sfb->colreg;
-
- sfb->fb.par = sfb;
+ sfb->fb.flags = FBINFO_FLAG_DEFAULT;
+ sfb->fb.fbops = &smtcfb_ops;
+ sfb->fb.fix = smtcfb_fix;
+ sfb->fb.var = smtcfb_var;
+ sfb->fb.pseudo_palette = sfb->colreg;
+ sfb->fb.par = sfb;
return sfb;
}
@@ -751,7 +736,7 @@ static int smtc_map_smem(struct smtcfb_info *sfb,
sfb->fb.fix.smem_len = smem_len;
- sfb->fb.screen_base = smtc_VRAMBaseAddress;
+ sfb->fb.screen_base = sfb->lfb;
if (!sfb->fb.screen_base) {
dev_err(&pdev->dev,
@@ -788,9 +773,8 @@ static int __devinit smtcfb_pci_probe(struct pci_dev *pdev,
{
struct smtcfb_info *sfb;
u_long smem_size = 0x00800000; /* default 8MB */
- char name[16];
int err;
- unsigned long pFramebufferPhysical;
+ unsigned long mmio_base;
dev_info(&pdev->dev, "Silicon Motion display driver.");
@@ -798,7 +782,9 @@ static int __devinit smtcfb_pci_probe(struct pci_dev *pdev,
if (err)
return err;
- sfb = smtc_alloc_fb_info(pdev, name);
+ sprintf(smtcfb_fix.id, "sm%Xfb", ent->device);
+
+ sfb = smtc_alloc_fb_info(pdev);
if (!sfb) {
err = -ENOMEM;
@@ -806,7 +792,6 @@ static int __devinit smtcfb_pci_probe(struct pci_dev *pdev,
}
sfb->chip_id = ent->device;
- sprintf(name, "sm%Xfb", sfb->chip_id);
pci_set_drvdata(pdev, sfb);
@@ -829,33 +814,28 @@ static int __devinit smtcfb_pci_probe(struct pci_dev *pdev,
sfb->fb.var.bits_per_pixel = (smtc_scr_info.lfb_depth = 32);
#endif
/* Map address and memory detection */
- pFramebufferPhysical = pci_resource_start(pdev, 0);
+ mmio_base = pci_resource_start(pdev, 0);
pci_read_config_byte(pdev, PCI_REVISION_ID, &sfb->chip_rev_id);
switch (sfb->chip_id) {
case 0x710:
case 0x712:
- sfb->fb.fix.mmio_start = pFramebufferPhysical + 0x00400000;
+ sfb->fb.fix.mmio_start = mmio_base + 0x00400000;
sfb->fb.fix.mmio_len = 0x00400000;
smem_size = SM712_VIDEOMEMORYSIZE;
#ifdef __BIG_ENDIAN
- sfb->m_pLFB = (smtc_VRAMBaseAddress =
- ioremap(pFramebufferPhysical, 0x00c00000));
+ sfb->lfb = ioremap(mmio_base, 0x00c00000);
#else
- sfb->m_pLFB = (smtc_VRAMBaseAddress =
- ioremap(pFramebufferPhysical, 0x00800000));
+ sfb->lfb = ioremap(mmio_base, 0x00800000);
#endif
- sfb->m_pMMIO = (smtc_RegBaseAddress =
- smtc_VRAMBaseAddress + 0x00700000);
- sfb->m_pDPR = smtc_VRAMBaseAddress + 0x00408000;
- sfb->m_pVPR = sfb->m_pLFB + 0x0040c000;
+ sfb->mmio = (smtc_RegBaseAddress =
+ sfb->lfb + 0x00700000);
+ sfb->dp_regs = sfb->lfb + 0x00408000;
+ sfb->vp_regs = sfb->lfb + 0x0040c000;
#ifdef __BIG_ENDIAN
if (sfb->fb.var.bits_per_pixel == 32) {
- smtc_VRAMBaseAddress += 0x800000;
- sfb->m_pLFB += 0x800000;
- dev_info(&pdev->dev,
- "smtc_VRAMBaseAddress=%p sfb->m_pLFB=%p",
- smtc_VRAMBaseAddress, sfb->m_pLFB);
+ sfb->lfb += 0x800000;
+ dev_info(&pdev->dev, "sfb->lfb=%p", sfb->lfb);
}
#endif
if (!smtc_RegBaseAddress) {
@@ -879,15 +859,14 @@ static int __devinit smtcfb_pci_probe(struct pci_dev *pdev,
#endif
break;
case 0x720:
- sfb->fb.fix.mmio_start = pFramebufferPhysical;
+ sfb->fb.fix.mmio_start = mmio_base;
sfb->fb.fix.mmio_len = 0x00200000;
smem_size = SM722_VIDEOMEMORYSIZE;
- sfb->m_pDPR = ioremap(pFramebufferPhysical, 0x00a00000);
- sfb->m_pLFB = (smtc_VRAMBaseAddress =
- sfb->m_pDPR + 0x00200000);
- sfb->m_pMMIO = (smtc_RegBaseAddress =
- sfb->m_pDPR + 0x000c0000);
- sfb->m_pVPR = sfb->m_pDPR + 0x800;
+ sfb->dp_regs = ioremap(mmio_base, 0x00a00000);
+ sfb->lfb = sfb->dp_regs + 0x00200000;
+ sfb->mmio = (smtc_RegBaseAddress =
+ sfb->dp_regs + 0x000c0000);
+ sfb->vp_regs = sfb->dp_regs + 0x800;
smtc_seqw(0x62, 0xff);
smtc_seqw(0x6a, 0x0d);
diff --git a/drivers/staging/speakup/i18n.c b/drivers/staging/speakup/i18n.c
index ca01734d13c5..7c1658b971dc 100644
--- a/drivers/staging/speakup/i18n.c
+++ b/drivers/staging/speakup/i18n.c
@@ -555,6 +555,7 @@ ssize_t msg_set(enum msg_index_t index, char *text, size_t length)
&& index <= MSG_FORMATTED_END)
&& !fmt_validate(speakup_default_msgs[index],
newstr)) {
+ kfree(newstr);
return -EINVAL;
}
spk_lock(flags);
diff --git a/drivers/staging/speakup/speakup_soft.c b/drivers/staging/speakup/speakup_soft.c
index 42cdafeea35e..e2f5c81e7548 100644
--- a/drivers/staging/speakup/speakup_soft.c
+++ b/drivers/staging/speakup/speakup_soft.c
@@ -40,13 +40,13 @@ static int softsynth_is_alive(struct spk_synth *synth);
static unsigned char get_index(void);
static struct miscdevice synth_device;
-static int initialized;
+static int init_pos;
static int misc_registered;
static struct var_t vars[] = {
{ CAPS_START, .u.s = {"\x01+3p" } },
{ CAPS_STOP, .u.s = {"\x01-3p" } },
- { RATE, .u.n = {"\x01%ds", 5, 0, 9, 0, 0, NULL } },
+ { RATE, .u.n = {"\x01%ds", 2, 0, 9, 0, 0, NULL } },
{ PITCH, .u.n = {"\x01%dp", 5, 0, 9, 0, 0, NULL } },
{ VOL, .u.n = {"\x01%dv", 5, 0, 9, 0, 0, NULL } },
{ TONE, .u.n = {"\x01%dx", 1, 0, 2, 0, 0, NULL } },
@@ -194,7 +194,7 @@ static int softsynth_close(struct inode *inode, struct file *fp)
unsigned long flags;
spk_lock(flags);
synth_soft.alive = 0;
- initialized = 0;
+ init_pos = 0;
spk_unlock(flags);
/* Make sure we let applications go before leaving */
speakup_start_ttys();
@@ -239,13 +239,8 @@ static ssize_t softsynth_read(struct file *fp, char *buf, size_t count,
ch = '\x18';
} else if (synth_buffer_empty()) {
break;
- } else if (!initialized) {
- if (*init) {
- ch = *init;
- init++;
- } else {
- initialized = 1;
- }
+ } else if (init[init_pos]) {
+ ch = init[init_pos++];
} else {
ch = synth_buffer_getc();
}
diff --git a/drivers/staging/telephony/ixj.c b/drivers/staging/telephony/ixj.c
index b303c9192e17..1cfa0b07d725 100644
--- a/drivers/staging/telephony/ixj.c
+++ b/drivers/staging/telephony/ixj.c
@@ -7057,7 +7057,7 @@ static int ixj_selfprobe(IXJ *j)
printk(KERN_INFO "Enable Line Monitor\n");
if (ixjdebug & 0x0002)
- printk(KERN_INFO "Set Line Monitor to Asyncronous Mode\n");
+ printk(KERN_INFO "Set Line Monitor to Asynchronous Mode\n");
if (ixj_WriteDSPCommand(0x7E01, j)) /* Asynchronous Line Monitor */
return -1;
@@ -7068,7 +7068,7 @@ static int ixj_selfprobe(IXJ *j)
if (ixj_WriteDSPCommand(0x5151, j)) /* Enable DTMF detection */
return -1;
- if (ixj_WriteDSPCommand(0x6E01, j)) /* Set Asyncronous Tone Generation */
+ if (ixj_WriteDSPCommand(0x6E01, j)) /* Set Asynchronous Tone Generation */
return -1;
set_rec_depth(j, 2); /* Set Record Channel Limit to 2 frames */
diff --git a/drivers/staging/tidspbridge/Documentation/error-codes b/drivers/staging/tidspbridge/Documentation/error-codes
index 12826e2a3aaa..ad73cba058eb 100644
--- a/drivers/staging/tidspbridge/Documentation/error-codes
+++ b/drivers/staging/tidspbridge/Documentation/error-codes
@@ -69,7 +69,7 @@ The error codes used by this driver are:
Invalid pointer or handler.
[EEXIST]
- Attempted to create a channel manager when one already exists.
+ Attempted to create a channel manager when one already exists.
[EINVAL]
Invalid argument.
diff --git a/drivers/staging/tidspbridge/core/_tiomap.h b/drivers/staging/tidspbridge/core/_tiomap.h
index 7cb587103975..543a127c7d4d 100644
--- a/drivers/staging/tidspbridge/core/_tiomap.h
+++ b/drivers/staging/tidspbridge/core/_tiomap.h
@@ -219,7 +219,7 @@ static const struct map_l4_peripheral l4_peripheral_table[] = {
/* MBX_PM_MAX_RESOURCES: CORE 2 Clock Resources. */
#define MBX_CORE2_RESOURCES 1
-/* MBX_PM_MAX_RESOURCES: TOTAL Clock Reosurces. */
+/* MBX_PM_MAX_RESOURCES: TOTAL Clock Resources. */
#define MBX_PM_MAX_RESOURCES 11
/* Power Management Commands */
diff --git a/drivers/staging/tidspbridge/core/chnl_sm.c b/drivers/staging/tidspbridge/core/chnl_sm.c
index e0c7e4c470c8..16fa3462fbbe 100644
--- a/drivers/staging/tidspbridge/core/chnl_sm.c
+++ b/drivers/staging/tidspbridge/core/chnl_sm.c
@@ -20,7 +20,7 @@
* The lower edge functions must be implemented by the Bridge driver
* writer, and are declared in chnl_sm.h.
*
- * Care is taken in this code to prevent simulataneous access to channel
+ * Care is taken in this code to prevent simultaneous access to channel
* queues from
* 1. Threads.
* 2. io_dpc(), scheduled from the io_isr() as an event.
@@ -34,7 +34,7 @@
* Channel Invariant:
* There is an important invariant condition which must be maintained per
* channel outside of bridge_chnl_get_ioc() and IO_Dispatch(), violation of
- * which may cause timeouts and/or failure offunction sync_wait_on_event.
+ * which may cause timeouts and/or failure of function sync_wait_on_event.
* This invariant condition is:
*
* list_empty(&pchnl->io_completions) ==> pchnl->sync_event is reset
@@ -94,7 +94,7 @@ int bridge_chnl_add_io_req(struct chnl_object *chnl_obj, void *host_buf,
struct dev_object *dev_obj;
u8 dw_state;
bool is_eos;
- struct chnl_mgr *chnl_mgr_obj = pchnl->chnl_mgr_obj;
+ struct chnl_mgr *chnl_mgr_obj;
u8 *host_sys_buf = NULL;
bool sched_dpc = false;
u16 mb_val = 0;
@@ -153,6 +153,7 @@ func_cont:
* If DPC is scheduled in process context (iosm_schedule) and any
* non-mailbox interrupt occurs, that DPC will run and break CS. Hence
* we disable ALL DPCs. We will try to disable ONLY IO DPC later. */
+ chnl_mgr_obj = pchnl->chnl_mgr_obj;
spin_lock_bh(&chnl_mgr_obj->chnl_mgr_lock);
omap_mbox_disable_irq(dev_ctxt->mbox, IRQ_RX);
if (pchnl->chnl_type == CHNL_PCPY) {
@@ -602,7 +603,7 @@ int bridge_chnl_get_ioc(struct chnl_object *chnl_obj, u32 timeout,
/* Since DSPStream_Reclaim() does not take a timeout
* parameter, we pass the stream's timeout value to
* bridge_chnl_get_ioc. We cannot determine whether or not
- * we have waited in User mode. Since the stream's timeout
+ * we have waited in user mode. Since the stream's timeout
* value may be non-zero, we still have to set the event.
* Therefore, this optimization is taken out.
*
diff --git a/drivers/staging/tidspbridge/core/dsp-clock.c b/drivers/staging/tidspbridge/core/dsp-clock.c
index 7d056bd1eaad..b647207928b1 100644
--- a/drivers/staging/tidspbridge/core/dsp-clock.c
+++ b/drivers/staging/tidspbridge/core/dsp-clock.c
@@ -16,6 +16,8 @@
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
+#define L4_34XX_BASE 0x48000000
+
#include <linux/types.h>
/* ----------------------------------- Host OS */
diff --git a/drivers/staging/tidspbridge/core/io_sm.c b/drivers/staging/tidspbridge/core/io_sm.c
index 480a3845a24c..e322fb7aebe1 100644
--- a/drivers/staging/tidspbridge/core/io_sm.c
+++ b/drivers/staging/tidspbridge/core/io_sm.c
@@ -837,8 +837,8 @@ static void io_dispatch_pm(struct io_mgr *pio_mgr)
/*
* ======== io_dpc ========
* Deferred procedure call for shared memory channel driver ISR. Carries
- * out the dispatch of I/O as a non-preemptible event.It can only be
- * pre-empted by an ISR.
+ * out the dispatch of I/O as a non-preemptible event. It can only be
+ * pre-empted by an ISR.
*/
void io_dpc(unsigned long ref_data)
{
@@ -877,7 +877,7 @@ void io_dpc(unsigned long ref_data)
pio_mgr->intr_val);
}
}
- /* Proc-copy chanel dispatch */
+ /* Proc-copy channel dispatch */
input_chnl(pio_mgr, NULL, IO_SERVICE);
output_chnl(pio_mgr, NULL, IO_SERVICE);
@@ -938,7 +938,7 @@ int io_mbox_msg(struct notifier_block *self, unsigned long len, void *msg)
/*
* ======== io_request_chnl ========
* Purpose:
- * Request chanenel I/O from the DSP. Sets flags in shared memory, then
+ * Request channel I/O from the DSP. Sets flags in shared memory, then
* interrupts the DSP.
*/
void io_request_chnl(struct io_mgr *io_manager, struct chnl_object *pchnl,
@@ -2208,7 +2208,7 @@ void dump_dl_modules(struct bridge_dev_context *bridge_context)
module_struct->num_sects);
/*
- * The section name strings start immedialty following
+ * The section name strings start immediately following
* the array of dll_sect structures.
*/
sect_str = (char *) &module_struct->
diff --git a/drivers/staging/tidspbridge/core/sync.c b/drivers/staging/tidspbridge/core/sync.c
index 995986a9d03b..7bb550acaf4a 100644
--- a/drivers/staging/tidspbridge/core/sync.c
+++ b/drivers/staging/tidspbridge/core/sync.c
@@ -49,7 +49,7 @@ void sync_set_event(struct sync_object *event)
* @timeout timeout on waiting for the evetns.
* @pu_index index of the event set.
*
- * This functios will wait until any of the array element is set or until
+ * These functions will wait until any of the array element is set or until
* timeout. In case of success the function will return 0 and
* @pu_index will store the index of the array element set or in case
* of timeout the function will return -ETIME or in case of
diff --git a/drivers/staging/tidspbridge/core/tiomap3430.c b/drivers/staging/tidspbridge/core/tiomap3430.c
index 7bf55c40944e..066a3ceec65e 100644
--- a/drivers/staging/tidspbridge/core/tiomap3430.c
+++ b/drivers/staging/tidspbridge/core/tiomap3430.c
@@ -328,7 +328,7 @@ static int bridge_brd_read(struct bridge_dev_context *dev_ctxt,
ul_num_bytes, mem_type);
return status;
}
- /* copy the data from DSP memory, */
+ /* copy the data from DSP memory */
memcpy(host_buff, (void *)(dsp_base_addr + offset), ul_num_bytes);
return status;
}
@@ -415,10 +415,10 @@ static int bridge_brd_start(struct bridge_dev_context *dev_ctxt,
/* Assert RST1 i.e only the RST only for DSP megacell */
if (!status) {
/*
- * XXX: ioremapping MUST be removed once ctrl
+ * XXX: OMAP343X_CTRL_BASE ioremapping MUST be removed once ctrl
* function is made available.
*/
- void __iomem *ctrl = ioremap(OMAP343X_CTRL_BASE, SZ_4K);
+ void __iomem *ctrl = ioremap(0x48002000, SZ_4K);
if (!ctrl)
return -ENOMEM;
@@ -1745,7 +1745,7 @@ static int mem_map_vmalloc(struct bridge_dev_context *dev_context,
pa_next = page_to_phys(page[0]);
while (!status && (i < num_pages)) {
/*
- * Reuse pa_next from the previous iteraion to avoid
+ * Reuse pa_next from the previous iteration to avoid
* an extra va2pa call
*/
pa_curr = pa_next;
diff --git a/drivers/staging/tidspbridge/core/tiomap3430_pwr.c b/drivers/staging/tidspbridge/core/tiomap3430_pwr.c
index 55675b7b9b66..dafa6d9b2948 100644
--- a/drivers/staging/tidspbridge/core/tiomap3430_pwr.c
+++ b/drivers/staging/tidspbridge/core/tiomap3430_pwr.c
@@ -356,7 +356,7 @@ int pre_scale_dsp(struct bridge_dev_context *dev_context, void *pargs)
dev_dbg(bridge, "OPP: %s IVA in sleep. No message to DSP\n");
return 0;
} else if ((dev_context->brd_state == BRD_RUNNING)) {
- /* Send a prenotificatio to DSP */
+ /* Send a prenotification to DSP */
dev_dbg(bridge, "OPP: %s sent notification to DSP\n", __func__);
sm_interrupt_dsp(dev_context, MBX_PM_SETPOINT_PRENOTIFY);
return 0;
diff --git a/drivers/staging/tidspbridge/core/wdt.c b/drivers/staging/tidspbridge/core/wdt.c
index 453ef748bf45..1dce36fb828f 100644
--- a/drivers/staging/tidspbridge/core/wdt.c
+++ b/drivers/staging/tidspbridge/core/wdt.c
@@ -25,6 +25,7 @@
#include <dspbridge/host_os.h>
+#define OMAP34XX_WDT3_BASE (0x49000000 + 0x30000)
#define INT_34XX_WDT3_IRQ (36 + NR_IRQS)
static struct dsp_wdt_setting dsp_wdt;
@@ -61,9 +62,9 @@ int dsp_wdt_init(void)
dsp_wdt.fclk = clk_get(NULL, "wdt3_fck");
- if (dsp_wdt.fclk) {
+ if (!IS_ERR(dsp_wdt.fclk)) {
dsp_wdt.iclk = clk_get(NULL, "wdt3_ick");
- if (!dsp_wdt.iclk) {
+ if (IS_ERR(dsp_wdt.iclk)) {
clk_put(dsp_wdt.fclk);
dsp_wdt.fclk = NULL;
ret = -EFAULT;
diff --git a/drivers/staging/tidspbridge/dynload/tramp.c b/drivers/staging/tidspbridge/dynload/tramp.c
index 60d22ea47055..404af1895980 100644
--- a/drivers/staging/tidspbridge/dynload/tramp.c
+++ b/drivers/staging/tidspbridge/dynload/tramp.c
@@ -81,7 +81,7 @@ static u8 priv_h2a(u8 value)
* Description: Generate a trampoline symbol name (ASCII) using the value
* of the symbol. This places the new name into the user buffer.
* The name is fixed in length and of the form: __$dbTR__xxxxxxxx
- * (where "xxxxxxxx" is the hex value.
+ * (where "xxxxxxxx" is the hex value).
*/
static void priv_tramp_sym_gen_name(u32 value, char *dst)
{
@@ -414,7 +414,7 @@ static int priv_tramp_sym_finalize(struct dload_state *dlthis)
/* Copy the symbol contents into the flat table */
*new_sym = cur_sym->sym_info;
- /* Now finaize the symbol. If it is in the tramp
+ /* Now finalize the symbol. If it is in the tramp
* section, we need to adjust for the section start.
* If it is external then we don't need to adjust at
* all.
@@ -773,7 +773,7 @@ static int priv_img_pkt_dup(struct dload_state *dlthis,
int ret_val = 0;
struct tramp_img_dup_relo *dup_relo = NULL;
- /* Determinne if this image packet is already being tracked in the
+ /* Determine if this image packet is already being tracked in the
dup list for other trampolines. */
dup_pkt = priv_dup_find(dlthis, secnn, image_offset);
@@ -998,7 +998,7 @@ int dload_tramp_generate(struct dload_state *dlthis, s16 secnn,
/*
* Function: dload_tramp_pkt_update
* Description: Update the duplicate copy of this image packet, which the
- * trampoline layer is already tracking. This is call is critical
+ * trampoline layer is already tracking. This call is critical
* to make if trampolines were generated anywhere within the
* packet and first pass relo continued on the remainder. The
* trampoline layer needs the updates image data so when 2nd
diff --git a/drivers/staging/tidspbridge/gen/uuidutil.c b/drivers/staging/tidspbridge/gen/uuidutil.c
index b44656cf7858..b7d8313d1acb 100644
--- a/drivers/staging/tidspbridge/gen/uuidutil.c
+++ b/drivers/staging/tidspbridge/gen/uuidutil.c
@@ -26,27 +26,6 @@
/* ----------------------------------- This */
#include <dspbridge/uuidutil.h>
-/*
- * ======== uuid_uuid_to_string ========
- * Purpose:
- * Converts a struct dsp_uuid to a string.
- * Note: snprintf format specifier is:
- * %[flags] [width] [.precision] [{h | l | I64 | L}]type
- */
-void uuid_uuid_to_string(struct dsp_uuid *uuid_obj, char *sz_uuid,
- s32 size)
-{
- s32 i; /* return result from snprintf. */
-
- i = snprintf(sz_uuid, size,
- "%.8X_%.4X_%.4X_%.2X%.2X_%.2X%.2X%.2X%.2X%.2X%.2X",
- uuid_obj->data1, uuid_obj->data2, uuid_obj->data3,
- uuid_obj->data4, uuid_obj->data5,
- uuid_obj->data6[0], uuid_obj->data6[1],
- uuid_obj->data6[2], uuid_obj->data6[3],
- uuid_obj->data6[4], uuid_obj->data6[5]);
-}
-
static s32 uuid_hex_to_bin(char *buf, s32 len)
{
s32 i;
diff --git a/drivers/staging/tidspbridge/hw/hw_mmu.c b/drivers/staging/tidspbridge/hw/hw_mmu.c
index 8a93d55ca596..71cb82293649 100644
--- a/drivers/staging/tidspbridge/hw/hw_mmu.c
+++ b/drivers/staging/tidspbridge/hw/hw_mmu.c
@@ -61,7 +61,7 @@ enum hw_mmu_page_size_t {
* Type : hw_status
* Description : 0 -- No errors occurred
* RET_BAD_NULL_PARAM -- A Pointer
- * Paramater was set to NULL
+ * Parameter was set to NULL
*
* PURPOSE: : Flush the TLB entry pointed by the
* lock counter register
@@ -103,7 +103,7 @@ static hw_status mmu_flush_entry(const void __iomem *base_address);
*
* Type : hw_status
* Description : 0 -- No errors occurred
- * RET_BAD_NULL_PARAM -- A Pointer Paramater
+ * RET_BAD_NULL_PARAM -- A Pointer Parameter
* was set to NULL
* RET_PARAM_OUT_OF_RANGE -- Input Parameter out
* of Range
@@ -148,7 +148,7 @@ static hw_status mmu_set_cam_entry(const void __iomem *base_address,
*
* Type : hw_status
* Description : 0 -- No errors occurred
- * RET_BAD_NULL_PARAM -- A Pointer Paramater
+ * RET_BAD_NULL_PARAM -- A Pointer Parameter
* was set to NULL
* RET_PARAM_OUT_OF_RANGE -- Input Parameter
* out of Range
diff --git a/drivers/staging/tidspbridge/include/dspbridge/dspioctl.h b/drivers/staging/tidspbridge/include/dspbridge/dspioctl.h
index 0c7ec04448f1..0fcda1978921 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/dspioctl.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/dspioctl.h
@@ -51,7 +51,7 @@
#define BRDIOCTL_POSTSCALE_NOTIFY (BRDIOCTL_PWRCONTROL + 0xA)
#define BRDIOCTL_CONSTRAINT_REQUEST (BRDIOCTL_PWRCONTROL + 0xB)
-/* Number of actual DSP-MMU TLB entrries */
+/* Number of actual DSP-MMU TLB entries */
#define BRDIOCTL_NUMOFMMUTLB 32
struct bridge_ioctl_extproc {
diff --git a/drivers/staging/tidspbridge/include/dspbridge/mbx_sh.h b/drivers/staging/tidspbridge/include/dspbridge/mbx_sh.h
index 7424c888d637..d4cb3948baba 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/mbx_sh.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/mbx_sh.h
@@ -22,7 +22,7 @@
* mailbox interrupt's cmd value received. The class value are defined
* as a bit (10 thru 15) being set.
*
- * Note: Only 16 bits of each is used. Other 16 bit data reg available.
+ * Note: Only 16 bits of each is used. Other 16 bit data reg available.
*
* 16 bit Mbx bit defns:
*
diff --git a/drivers/staging/tidspbridge/include/dspbridge/node.h b/drivers/staging/tidspbridge/include/dspbridge/node.h
index 7397b7a12f7a..68ed74a86c95 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/node.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/node.h
@@ -220,7 +220,7 @@ extern int node_create_mgr(struct node_mgr **node_man,
* Parameters:
* noderes: Node resource info handle returned from
* node_allocate().
- * pr_ctxt: Poninter to process context data.
+ * pr_ctxt: Pointer to process context data.
* Returns:
* 0: Success.
* -EFAULT: Invalid hnode.
diff --git a/drivers/staging/tidspbridge/include/dspbridge/ntfy.h b/drivers/staging/tidspbridge/include/dspbridge/ntfy.h
index cbc8819c61cc..6bb94d20e99a 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/ntfy.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/ntfy.h
@@ -78,7 +78,7 @@ static inline void ntfy_init(struct ntfy_object *no)
* ntfy_delete() - delete list of nofy events registered.
* @ntfy_obj: Pointer to the ntfy object structure.
*
- * This function is used to remove all the notify events registered.
+ * This function is used to remove all the notify events registered.
* unregister function is not needed in this function, to unregister
* a ntfy_event please look at ntfy_register function.
*
diff --git a/drivers/staging/tidspbridge/include/dspbridge/proc.h b/drivers/staging/tidspbridge/include/dspbridge/proc.h
index a82380ebc041..851b356d7a51 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/proc.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/proc.h
@@ -263,7 +263,7 @@ extern int proc_get_processor_id(void *proc, u32 * proc_id);
* Returns:
* 0 : Success.
* -EFAULT : Invalid processor handle.
- * -EPERM : General failure while retireving processor trace
+ * -EPERM : General failure while retrieving processor trace
* Buffer.
* Requires:
* pbuf is not NULL
diff --git a/drivers/staging/tidspbridge/include/dspbridge/strm.h b/drivers/staging/tidspbridge/include/dspbridge/strm.h
index dacf0c234fd1..97aee4c63d24 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/strm.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/strm.h
@@ -203,7 +203,7 @@ extern int strm_issue(struct strm_object *stream_obj, u8 * pbuf,
* index: Stream index.
* pattr: Pointer to structure containing attributes to be
* applied to stream. Cannot be NULL.
- * strmres: Location to store stream resuorce info handle on output.
+ * strmres: Location to store stream resource info handle on output.
* Returns:
* 0: Success.
* -EFAULT: Invalid hnode.
diff --git a/drivers/staging/tidspbridge/include/dspbridge/sync.h b/drivers/staging/tidspbridge/include/dspbridge/sync.h
index b1e75eb8847c..58a0d5c5543d 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/sync.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/sync.h
@@ -78,7 +78,7 @@ void sync_set_event(struct sync_object *event);
* @event: events to wait for it.
* @timeout timeout on waiting for the evetn.
*
- * This functios will wait until @event is set or until timeout. In case of
+ * This function will wait until @event is set or until timeout. In case of
* success the function will return 0 and
* in case of timeout the function will return -ETIME
* in case of signal the function will return -ERESTARTSYS
@@ -106,7 +106,7 @@ static inline int sync_wait_on_event(struct sync_object *event,
* @timeout timeout on waiting for the evetns.
* @pu_index index of the event set.
*
- * This functios will wait until any of the array element is set or until
+ * This function will wait until any of the array element is set or until
* timeout. In case of success the function will return 0 and
* @pu_index will store the index of the array element set and in case
* of timeout the function will return -ETIME.
diff --git a/drivers/staging/tidspbridge/include/dspbridge/uuidutil.h b/drivers/staging/tidspbridge/include/dspbridge/uuidutil.h
index 9a994753e9ba..414bf71d652d 100644
--- a/drivers/staging/tidspbridge/include/dspbridge/uuidutil.h
+++ b/drivers/staging/tidspbridge/include/dspbridge/uuidutil.h
@@ -22,26 +22,6 @@
#define MAXUUIDLEN 37
/*
- * ======== uuid_uuid_to_string ========
- * Purpose:
- * Converts a dsp_uuid to an ANSI string.
- * Parameters:
- * uuid_obj: Pointer to a dsp_uuid object.
- * sz_uuid: Pointer to a buffer to receive a NULL-terminated UUID
- * string.
- * size: Maximum size of the sz_uuid string.
- * Returns:
- * Requires:
- * uuid_obj & sz_uuid are non-NULL values.
- * Ensures:
- * Lenghth of sz_uuid is less than MAXUUIDLEN.
- * Details:
- * UUID string limit currently set at MAXUUIDLEN.
- */
-void uuid_uuid_to_string(struct dsp_uuid *uuid_obj, char *sz_uuid,
- s32 size);
-
-/*
* ======== uuid_uuid_from_string ========
* Purpose:
* Converts an ANSI string to a dsp_uuid.
diff --git a/drivers/staging/tidspbridge/rmgr/dbdcd.c b/drivers/staging/tidspbridge/rmgr/dbdcd.c
index 12a1d34b3954..9d52c3cb92f0 100644
--- a/drivers/staging/tidspbridge/rmgr/dbdcd.c
+++ b/drivers/staging/tidspbridge/rmgr/dbdcd.c
@@ -346,11 +346,13 @@ int dcd_get_object_def(struct dcd_manager *hdcd_mgr,
struct dcd_manager *dcd_mgr_obj = hdcd_mgr; /* ptr to DCD mgr */
struct cod_libraryobj *lib = NULL;
int status = 0;
+ int len;
u32 ul_addr = 0; /* Used by cod_get_section */
u32 ul_len = 0; /* Used by cod_get_section */
u32 dw_buf_size; /* Used by REG functions */
char sz_reg_key[DCD_MAXPATHLENGTH];
char *sz_uuid; /*[MAXUUIDLEN]; */
+ char *tmp;
struct dcd_key_elem *dcd_key = NULL;
char sz_sect_name[MAXUUIDLEN + 2]; /* ".[UUID]\0" */
char *psz_coff_buf;
@@ -395,7 +397,7 @@ int dcd_get_object_def(struct dcd_manager *hdcd_mgr,
}
/* Create UUID value to set in registry. */
- uuid_uuid_to_string(obj_uuid, sz_uuid, MAXUUIDLEN);
+ snprintf(sz_uuid, MAXUUIDLEN, "%pUL", obj_uuid);
if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH)
strncat(sz_reg_key, sz_uuid, MAXUUIDLEN);
@@ -429,12 +431,27 @@ int dcd_get_object_def(struct dcd_manager *hdcd_mgr,
}
/* Ensure sz_uuid + 1 is not greater than sizeof sz_sect_name. */
+ len = strlen(sz_uuid);
+ if (len + 1 > sizeof(sz_sect_name)) {
+ status = -EPERM;
+ goto func_end;
+ }
/* Create section name based on node UUID. A period is
* pre-pended to the UUID string to form the section name.
* I.e. ".24BC8D90_BB45_11d4_B756_006008BDB66F" */
+
+ len -= 4; /* uuid has 4 delimiters '-' */
+ tmp = sz_uuid;
+
strncpy(sz_sect_name, ".", 2);
- strncat(sz_sect_name, sz_uuid, strlen(sz_uuid));
+ do {
+ char *uuid = strsep(&tmp, "-");
+ if (!uuid)
+ break;
+ len -= strlen(uuid);
+ strncat(sz_sect_name, uuid, strlen(uuid) + 1);
+ } while (len && strncat(sz_sect_name, "_", 2));
/* Get section information. */
status = cod_get_section(lib, sz_sect_name, &ul_addr, &ul_len);
@@ -463,7 +480,7 @@ int dcd_get_object_def(struct dcd_manager *hdcd_mgr,
status = cod_read_section(lib, sz_sect_name, psz_coff_buf, ul_len);
#endif
if (!status) {
- /* Compres DSP buffer to conform to PC format. */
+ /* Compress DSP buffer to conform to PC format. */
if (strstr(dcd_key->path, "iva") == NULL) {
compress_buf(psz_coff_buf, ul_len, DSPWORDSIZE);
} else {
@@ -666,7 +683,7 @@ int dcd_get_library_name(struct dcd_manager *hdcd_mgr,
status = -EPERM;
}
/* Create UUID value to find match in registry. */
- uuid_uuid_to_string(uuid_obj, sz_uuid, MAXUUIDLEN);
+ snprintf(sz_uuid, MAXUUIDLEN, "%pUL", uuid_obj);
if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH)
strncat(sz_reg_key, sz_uuid, MAXUUIDLEN);
else
@@ -706,7 +723,7 @@ int dcd_get_library_name(struct dcd_manager *hdcd_mgr,
} else {
status = -EPERM;
}
- uuid_uuid_to_string(uuid_obj, sz_uuid, MAXUUIDLEN);
+ snprintf(sz_uuid, MAXUUIDLEN, "%pUL", uuid_obj);
if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH)
strncat(sz_reg_key, sz_uuid, MAXUUIDLEN);
else
@@ -797,7 +814,7 @@ int dcd_register_object(struct dsp_uuid *uuid_obj,
status = -EPERM;
/* Create UUID value to set in registry. */
- uuid_uuid_to_string(uuid_obj, sz_uuid, MAXUUIDLEN);
+ snprintf(sz_uuid, MAXUUIDLEN, "%pUL", uuid_obj);
if ((strlen(sz_reg_key) + MAXUUIDLEN) < DCD_MAXPATHLENGTH)
strncat(sz_reg_key, sz_uuid, MAXUUIDLEN);
else
diff --git a/drivers/staging/tidspbridge/rmgr/drv_interface.c b/drivers/staging/tidspbridge/rmgr/drv_interface.c
index 49c9b662392f..bddea1d3b2c3 100644
--- a/drivers/staging/tidspbridge/rmgr/drv_interface.c
+++ b/drivers/staging/tidspbridge/rmgr/drv_interface.c
@@ -613,16 +613,6 @@ static struct platform_driver bridge_driver = {
#endif
};
-static int __init bridge_init(void)
-{
- return platform_driver_register(&bridge_driver);
-}
-
-static void __exit bridge_exit(void)
-{
- platform_driver_unregister(&bridge_driver);
-}
-
/* To remove all process resources before removing the process from the
* process context list */
int drv_remove_all_resources(void *process_ctxt)
@@ -636,6 +626,4 @@ int drv_remove_all_resources(void *process_ctxt)
return status;
}
-/* Bridge driver initialization and de-initialization functions */
-module_init(bridge_init);
-module_exit(bridge_exit);
+module_platform_driver(bridge_driver);
diff --git a/drivers/staging/tidspbridge/rmgr/dspdrv.c b/drivers/staging/tidspbridge/rmgr/dspdrv.c
index dc767b183cdf..d460f5823c6b 100644
--- a/drivers/staging/tidspbridge/rmgr/dspdrv.c
+++ b/drivers/staging/tidspbridge/rmgr/dspdrv.c
@@ -72,7 +72,7 @@ u32 dsp_init(u32 *init_status)
/* Unwind whatever was loaded */
if (status) {
- /* irrespective of the status of dev_remove_device we conitinue
+ /* irrespective of the status of dev_remove_device we continue
* unloading. Get the Driver Object iterate through and remove.
* Reset the status to E_FAIL to avoid going through
* api_init_complete2. */
@@ -92,7 +92,7 @@ u32 dsp_init(u32 *init_status)
func_cont:
/* Attempt to Start the Board */
if (!status) {
- /* BRD_AutoStart could fail if the dsp execuetable is not the
+ /* BRD_AutoStart could fail if the dsp executable is not the
* correct one. We should not propagate that error
* into the device loader. */
(void)api_init_complete2();
diff --git a/drivers/staging/tidspbridge/rmgr/mgr.c b/drivers/staging/tidspbridge/rmgr/mgr.c
index 8a1e9287cff6..b32ba0ad2a07 100644
--- a/drivers/staging/tidspbridge/rmgr/mgr.c
+++ b/drivers/staging/tidspbridge/rmgr/mgr.c
@@ -262,8 +262,8 @@ int mgr_enum_processor_info(u32 processor_id,
IVAPROCTYPE_ARM7)
proc_detect = true;
}
- /* User applciatiuons aonly check for chip type, so
- * this clumsy overwrite */
+ /* User applications only check for chip type, so
+ * this is a clumsy overwrite */
processor_info->processor_type = DSPTYPE64;
} else {
dev_dbg(bridge, "%s: Failed to get DCD processor info "
diff --git a/drivers/staging/tidspbridge/rmgr/nldr.c b/drivers/staging/tidspbridge/rmgr/nldr.c
index 30d5480fcdcc..6309221b64a5 100644
--- a/drivers/staging/tidspbridge/rmgr/nldr.c
+++ b/drivers/staging/tidspbridge/rmgr/nldr.c
@@ -898,7 +898,7 @@ static int add_ovly_info(void *handle, struct dbll_sect_info *sect_info,
nldr_obj->ovly_table[i].execute_sects++;
} else {
- /* Put in "other" sectins */
+ /* Put in "other" sections */
status =
add_ovly_sect(nldr_obj,
&nldr_obj->
diff --git a/drivers/staging/tidspbridge/rmgr/node.c b/drivers/staging/tidspbridge/rmgr/node.c
index 7fb426c5251c..c2fc6137c770 100644
--- a/drivers/staging/tidspbridge/rmgr/node.c
+++ b/drivers/staging/tidspbridge/rmgr/node.c
@@ -1613,7 +1613,7 @@ int node_get_attr(struct node_object *hnode,
return -EFAULT;
hnode_mgr = hnode->node_mgr;
- /* Enter hnode_mgr critical section (since we're accessing
+ /* Enter hnode_mgr critical section since we're accessing
* data that could be changed by node_change_priority() and
* node_connect(). */
mutex_lock(&hnode_mgr->node_mgr_lock);
@@ -2714,8 +2714,7 @@ static int get_node_props(struct dcd_manager *hdcd_mgr,
hnode->ntype = node_type = pndb_props->ntype;
/* Create UUID value to set in registry. */
- uuid_uuid_to_string((struct dsp_uuid *)node_uuid, sz_uuid,
- MAXUUIDLEN);
+ snprintf(sz_uuid, MAXUUIDLEN, "%pUL", node_uuid);
dev_dbg(bridge, "(node) UUID: %s\n", sz_uuid);
/* Fill in message args that come from NDB */
diff --git a/drivers/staging/tidspbridge/rmgr/proc.c b/drivers/staging/tidspbridge/rmgr/proc.c
index 7e4f12f6be42..5e43938ab7fa 100644
--- a/drivers/staging/tidspbridge/rmgr/proc.c
+++ b/drivers/staging/tidspbridge/rmgr/proc.c
@@ -300,7 +300,7 @@ proc_attach(u32 processor_id,
if (status)
goto func_end;
- /* If we made it this far, create the Proceesor object: */
+ /* If we made it this far, create the Processor object: */
p_proc_object = kzalloc(sizeof(struct proc_object), GFP_KERNEL);
/* Fill out the Processor Object: */
if (p_proc_object == NULL) {
diff --git a/drivers/staging/usbip/Kconfig b/drivers/staging/usbip/Kconfig
index dd13c0220681..199b1d4c0b85 100644
--- a/drivers/staging/usbip/Kconfig
+++ b/drivers/staging/usbip/Kconfig
@@ -1,6 +1,6 @@
config USBIP_CORE
- tristate "USB/IP support (EXPERIMENTAL)"
- depends on USB && NET && EXPERIMENTAL
+ tristate "USB/IP support"
+ depends on USB && NET
default N
---help---
This enables pushing USB packets over IP to allow remote
diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c
index 92ced35e6b7f..c8d79a7f0e0e 100644
--- a/drivers/staging/usbip/stub_dev.c
+++ b/drivers/staging/usbip/stub_dev.c
@@ -187,10 +187,14 @@ static void stub_shutdown_connection(struct usbip_device *ud)
}
/* 1. stop threads */
- if (ud->tcp_rx)
+ if (ud->tcp_rx) {
kthread_stop_put(ud->tcp_rx);
- if (ud->tcp_tx)
+ ud->tcp_rx = NULL;
+ }
+ if (ud->tcp_tx) {
kthread_stop_put(ud->tcp_tx);
+ ud->tcp_tx = NULL;
+ }
/*
* 2. close the socket
diff --git a/drivers/staging/usbip/stub_rx.c b/drivers/staging/usbip/stub_rx.c
index 1d5b3fc62160..694cfd7596f3 100644
--- a/drivers/staging/usbip/stub_rx.c
+++ b/drivers/staging/usbip/stub_rx.c
@@ -155,7 +155,7 @@ static int tweak_set_configuration_cmd(struct urb *urb)
* eventually reassigned to the device as far as driver matching
* condition is kept.
*
- * Unfortunatelly, an existing usbip connection will be dropped
+ * Unfortunately, an existing usbip connection will be dropped
* due to this driver unbinding. So, skip here.
* A user may need to set a special configuration value before
* exporting the device.
diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
index 70f230269329..57f11f9cd8a5 100644
--- a/drivers/staging/usbip/usbip_common.c
+++ b/drivers/staging/usbip/usbip_common.c
@@ -22,7 +22,9 @@
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/stat.h>
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <net/sock.h>
#include "usbip_common.h"
@@ -36,6 +38,8 @@ unsigned long usbip_debug_flag = 0xffffffff;
unsigned long usbip_debug_flag;
#endif
EXPORT_SYMBOL_GPL(usbip_debug_flag);
+module_param(usbip_debug_flag, ulong, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(usbip_debug_flag, "debug flags (defined in usbip_common.h)");
/* FIXME */
struct device_attribute dev_attr_usbip_debug;
@@ -157,8 +161,7 @@ static void usbip_dump_usb_device(struct usb_device *udev)
dev_dbg(dev, "have_langid %d, string_langid %d\n",
udev->have_langid, udev->string_langid);
- dev_dbg(dev, "maxchild %d, children %p\n",
- udev->maxchild, udev->children);
+ dev_dbg(dev, "maxchild %d\n", udev->maxchild);
}
static void usbip_dump_request_type(__u8 rt)
diff --git a/drivers/staging/usbip/userspace/configure.ac b/drivers/staging/usbip/userspace/configure.ac
index bf5cf49cb554..43e641e5ac06 100644
--- a/drivers/staging/usbip/userspace/configure.ac
+++ b/drivers/staging/usbip/userspace/configure.ac
@@ -56,11 +56,11 @@ AC_ARG_WITH([tcp-wrappers],
[AS_HELP_STRING([--with-tcp-wrappers],
[use the libwrap (TCP wrappers) library])],
dnl [ACTION-IF-GIVEN]
- [saved_LIBS="$LIBS"
- if test "$withval" = "yes"; then
+ [if test "$withval" = "yes"; then
AC_MSG_RESULT([yes])
AC_MSG_CHECKING([for hosts_access in -lwrap])
- LIBS="-lwrap $LIBS"
+ saved_LIBS="$LIBS"
+ LIBS="-lwrap $saved_LIBS"
AC_TRY_LINK(
[int hosts_access(); int allow_severity, deny_severity;],
[hosts_access()],
@@ -69,9 +69,9 @@ AC_ARG_WITH([tcp-wrappers],
[use tcp wrapper]) wrap_LIB="-lwrap"],
[AC_MSG_RESULT([not found]); exit 1])
else
- AC_MSG_RESULT([no])
- fi
- LIBS="$saved_LIBS"],
+ AC_MSG_RESULT([no]);
+ LIBS="$saved_LIBS"
+ fi],
dnl [ACTION-IF-NOT-GIVEN]
[AC_MSG_RESULT([(default)])
AC_MSG_CHECKING([for hosts_access in -lwrap])
diff --git a/drivers/staging/usbip/userspace/doc/usbip.8 b/drivers/staging/usbip/userspace/doc/usbip.8
index 1653bb2cd7d1..6e0d74503792 100644
--- a/drivers/staging/usbip/userspace/doc/usbip.8
+++ b/drivers/staging/usbip/userspace/doc/usbip.8
@@ -3,69 +3,87 @@
usbip \- manage USB/IP devices
.SH SYNOPSIS
.B usbip
-[\fIoptions\fR]
+[\foptions\R] <\fIcommand\fR> <\fIargs\fR>
.SH DESCRIPTION
-Devices exported by USB/IP servers can be listed, attached and
-detached using this program.
+On a USB/IP server, devices can be listed, bound, and unbound using
+this program. On a USB/IP client, devices exported by USB/IP servers
+can be listed, attached and detached.
.SH OPTIONS
.HP
-\fB\-a\fR, \fB\-\-attach\fR <host> <bus_id>
+\fB\-\-debug\fR
.IP
-Attach a remote USB device.
+Print debugging information.
+.PP
+
+.HP
+\fB\-\-log\fR
+.IP
+Log to syslog.
+.PP
+
+.SH COMMANDS
+.HP
+\fBversion\fR
+.IP
+Show version and exit.
.PP
.HP
-\fB\-x\fR, \fB\-\-attachall\fR <host>
+\fBhelp\fR [\fIcommand\fR]
.IP
-Attach all remote USB devices on the specific host.
+Print the program help message, or help on a specific command, and
+then exit.
.PP
.HP
-\fB\-d\fR, \fB\-\-detach\fR <ports>
+\fBattach\fR \-\-host=<\fIhost\fR> \-\-busid=<\fIbus_id\fR>
+.IP
+Attach a remote USB device.
+.PP
+
+.HP
+\fBdetach\fR \-\-port=<\fIport\fR>
.IP
Detach an imported USB device.
.PP
.HP
-\fB\-l\fR, \fB\-\-list\fR <hosts>
+\fBbind\fR \-\-busid=<\fIbusid\fR>
.IP
-List exported USB devices.
+Make a device exportable.
.PP
.HP
-\fB\-p\fR, \fB\-\-port\fR
+\fBunbind\fR \-\-busid=<\fIbusid\fR>
.IP
-List virtual USB port status.
+Stop exporting a device so it can be used by a local driver.
.PP
.HP
-\fB\-D\fR, \fB\-\-debug\fR
+\fBlist\fR \-\-remote=<\fIhost\fR>
.IP
-Print debugging information.
+List USB devices exported by a remote host.
.PP
.HP
-\fB\-v\fR, \fB\-\-version\fR
+\fBlist\fR \-\-local
.IP
-Show version.
+List local USB devices.
.PP
+
.SH EXAMPLES
- client:# usbip --list server
+ client:# usbip list --remote=server
- List exportable usb devices on the server.
- client:# usbip --attach server 1-2
+ client:# usbip attach --host=server --busid=1-2
- Connect the remote USB device.
- client:# usbip --port
- - Show virtual port status.
-
- client:# usbip --detach 0
+ client:# usbip detach --port=0
- Detach the usb device.
.SH "SEE ALSO"
-\fBusbipd\fP\fB(8)\fB\fP,
-\fBusbip_attach_driver\fP\fB(8)\fB\fP
+\fBusbipd\fP\fB(8)\fB\fP
diff --git a/drivers/staging/usbip/userspace/doc/usbip_bind_driver.8 b/drivers/staging/usbip/userspace/doc/usbip_bind_driver.8
deleted file mode 100644
index d43bbd6be934..000000000000
--- a/drivers/staging/usbip/userspace/doc/usbip_bind_driver.8
+++ /dev/null
@@ -1,42 +0,0 @@
-.TH USBIP_BIND_DRIVER "8" "February 2009" "usbip" "System Administration Utilities"
-.SH NAME
-usbip_bind_driver \- change driver binding for USB/IP
-
-.SH SYNOPSIS
-.B usbip_bind_driver
-[\fIoptions\fR]
-
-.SH DESCRIPTION
-Driver bindings for USB devices can be changed using
-this program. It is used to export and unexport USB
-devices over USB/IP.
-
-.SH OPTIONS
-.TP
-\fB\-u\fR, \fB\-\-usbip\fR <busid>
-Make a device exportable
-.TP
-\fB\-o\fR, \fB\-\-other\fR <busid>
-Use a device by a local driver
-.TP
-\fB\-l\fR, \fB\-\-list\fR
-Print usb devices and their drivers
-.TP
-\fB\-L\fR, \fB\-\-list2\fR
-Print usb devices and their drivers in parseable mode
-
-.SH EXAMPLES
-
- server:# usbip_bind_driver --list
- - List driver assignments for usb devices.
-
- server:# usbip_bind_driver --usbip 1-2
- - Bind usbip-host.ko to the device of busid 1-2.
- - A usb device 1-2 is now exportable to other hosts!
-
- server:# usbip_bind_driver --other 1-2
- - Shutdown exporting and use the device locally.
-
-.SH "SEE ALSO"
-\fBusbip\fP\fB(8)\fB\fP,
-\fBusbipd\fP\fB(8)\fB\fP
diff --git a/drivers/staging/usbip/userspace/doc/usbipd.8 b/drivers/staging/usbip/userspace/doc/usbipd.8
index 006559f1df25..d896936f1780 100644
--- a/drivers/staging/usbip/userspace/doc/usbipd.8
+++ b/drivers/staging/usbip/userspace/doc/usbipd.8
@@ -10,7 +10,7 @@ usbipd \- USB/IP server daemon
provides USB/IP clients access to exported USB devices.
Devices have to explicitly be exported using
-.B usbip_bind_driver
+.B usbip bind
before usbipd makes them available to other hosts.
The daemon accepts connections from USB/IP clients
@@ -29,6 +29,11 @@ Run as a daemon process.
Print debugging information.
.PP
+\fB\-h\fR, \fB\-\-help\fR
+.IP
+Print the program help message and exit.
+.PP
+
.HP
\fB\-v\fR, \fB\-\-version\fR
.IP
@@ -48,15 +53,14 @@ USB/IP client can connect and use exported devices.
server:# usbipd -D
- Start usbip daemon.
- server:# usbip_bind_driver --list
+ server:# usbip list --local
- List driver assignments for usb devices.
- server:# usbip_bind_driver --usbip 1-2
+ server:# usbip bind --busid=1-2
- Bind usbip-host.ko to the device of busid 1-2.
- A usb device 1-2 is now exportable to other hosts!
- - Use 'usbip_bind_driver --other 1-2' when you want to shutdown exporting and use the device locally.
+ - Use 'usbip unbind --busid=1-2' when you want to shutdown exporting and use the device locally.
.SH "SEE ALSO"
-\fBusbip\fP\fB(8)\fB\fP,
-\fBusbip_attach_driver\fP\fB(8)\fB\fP
+\fBusbip\fP\fB(8)\fB\fP
diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c
index 12a9a5fbc797..620d1beb4587 100644
--- a/drivers/staging/usbip/vhci_hcd.c
+++ b/drivers/staging/usbip/vhci_hcd.c
@@ -220,7 +220,7 @@ static int vhci_hub_status(struct usb_hcd *hcd, char *buf)
pr_info("changed %d\n", changed);
- if (hcd->state == HC_STATE_SUSPENDED)
+ if ((hcd->state == HC_STATE_SUSPENDED) && (changed == 1))
usb_hcd_resume_root_hub(hcd);
done:
@@ -749,6 +749,7 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
{
struct vhci_unlink *unlink, *tmp;
+ spin_lock(&the_controller->lock);
spin_lock(&vdev->priv_lock);
list_for_each_entry_safe(unlink, tmp, &vdev->unlink_tx, list) {
@@ -757,9 +758,12 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
kfree(unlink);
}
- list_for_each_entry_safe(unlink, tmp, &vdev->unlink_rx, list) {
+ while (!list_empty(&vdev->unlink_rx)) {
struct urb *urb;
+ unlink = list_first_entry(&vdev->unlink_rx, struct vhci_unlink,
+ list);
+
/* give back URB of unanswered unlink request */
pr_info("unlink cleanup rx %lu\n", unlink->unlink_seqnum);
@@ -774,18 +778,24 @@ static void vhci_device_unlink_cleanup(struct vhci_device *vdev)
urb->status = -ENODEV;
- spin_lock(&the_controller->lock);
usb_hcd_unlink_urb_from_ep(vhci_to_hcd(the_controller), urb);
+
+ list_del(&unlink->list);
+
+ spin_unlock(&vdev->priv_lock);
spin_unlock(&the_controller->lock);
usb_hcd_giveback_urb(vhci_to_hcd(the_controller), urb,
urb->status);
- list_del(&unlink->list);
+ spin_lock(&the_controller->lock);
+ spin_lock(&vdev->priv_lock);
+
kfree(unlink);
}
spin_unlock(&vdev->priv_lock);
+ spin_unlock(&the_controller->lock);
}
/*
@@ -804,11 +814,14 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
}
/* kill threads related to this sdev, if v.c. exists */
- if (vdev->ud.tcp_rx)
+ if (vdev->ud.tcp_rx) {
kthread_stop_put(vdev->ud.tcp_rx);
- if (vdev->ud.tcp_tx)
+ vdev->ud.tcp_rx = NULL;
+ }
+ if (vdev->ud.tcp_tx) {
kthread_stop_put(vdev->ud.tcp_tx);
-
+ vdev->ud.tcp_tx = NULL;
+ }
pr_info("stop threads\n");
/* active connection is closed */
@@ -828,11 +841,11 @@ static void vhci_shutdown_connection(struct usbip_device *ud)
* disable endpoints. pending urbs are unlinked(dequeued).
*
* NOTE: After calling rh_port_disconnect(), the USB device drivers of a
- * deteched device should release used urbs in a cleanup function(i.e.
+ * detached device should release used urbs in a cleanup function (i.e.
* xxx_disconnect()). Therefore, vhci_hcd does not need to release
* pushed urbs and their private data in this function.
*
- * NOTE: vhci_dequeue() must be considered carefully. When shutdowning
+ * NOTE: vhci_dequeue() must be considered carefully. When shutting down
* a connection, vhci_shutdown_connection() expects vhci_dequeue()
* gives back pushed urbs and frees their private data by request of
* the cleanup function of a USB driver. When unlinking a urb with an
diff --git a/drivers/staging/vme/devices/vme_pio2_core.c b/drivers/staging/vme/devices/vme_pio2_core.c
index 4bf8e05ac312..dad8281915bf 100644
--- a/drivers/staging/vme/devices/vme_pio2_core.c
+++ b/drivers/staging/vme/devices/vme_pio2_core.c
@@ -10,6 +10,8 @@
* option) any later version.
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/types.h>
@@ -163,15 +165,13 @@ static int __init pio2_init(void)
int retval = 0;
if (bus_num == 0) {
- printk(KERN_ERR "%s: No cards, skipping registration\n",
- driver_name);
+ pr_err("No cards, skipping registration\n");
goto err_nocard;
}
if (bus_num > PIO2_CARDS_MAX) {
- printk(KERN_ERR
- "%s: Driver only able to handle %d PIO2 Cards\n",
- driver_name, PIO2_CARDS_MAX);
+ pr_err("Driver only able to handle %d PIO2 Cards\n",
+ PIO2_CARDS_MAX);
bus_num = PIO2_CARDS_MAX;
}
diff --git a/drivers/staging/vme/devices/vme_user.c b/drivers/staging/vme/devices/vme_user.c
index e25645e226e3..c3f94f311ca7 100644
--- a/drivers/staging/vme/devices/vme_user.c
+++ b/drivers/staging/vme/devices/vme_user.c
@@ -64,13 +64,13 @@ static unsigned int bus_num;
*
* However the VME driver at http://www.vmelinux.org/ is rather old and doesn't
* even support the tsi148 chipset (which has 8 master and 8 slave windows).
- * We'll run with this or now as far as possible, however it probably makes
+ * We'll run with this for now as far as possible, however it probably makes
* sense to get rid of the old mappings and just do everything dynamically.
*
* So for now, we'll restrict the driver to providing 4 masters and 4 slaves as
* defined above and try to support at least some of the interface from
- * http://www.vmelinux.org/ as an alternative drive can be written providing a
- * saner interface later.
+ * http://www.vmelinux.org/ as an alternative the driver can be written
+ * providing a saner interface later.
*
* The vmelinux.org driver never supported slave images, the devices reserved
* for slaves were repurposed to support all 8 master images on the UniverseII!
@@ -242,7 +242,7 @@ static ssize_t resource_to_user(int minor, char __user *buf, size_t count,
}
/*
- * We are going ot alloc a page during init per window for small transfers.
+ * We are going to alloc a page during init per window for small transfers.
* Small transfers will go user space -> buffer -> VME. Larger (more than a
* page) transfers will lock the user space buffer into memory and then
* transfer the data directly from the user space buffers out to VME.
@@ -396,7 +396,7 @@ static ssize_t vme_user_write(struct file *file, const char __user *buf,
default:
retval = -EINVAL;
}
-
+
mutex_unlock(&image[minor].mutex);
if (retval > 0)
diff --git a/drivers/staging/vt6655/80211mgr.h b/drivers/staging/vt6655/80211mgr.h
index 3bdab3f56f1c..65780f28db41 100644
--- a/drivers/staging/vt6655/80211mgr.h
+++ b/drivers/staging/vt6655/80211mgr.h
@@ -186,7 +186,7 @@
//
-// Cipher Suite Selectors defiened in 802.11i
+// Cipher Suite Selectors defined in 802.11i
//
#define WLAN_11i_CSS_USE_GROUP 0
#define WLAN_11i_CSS_WEP40 1
diff --git a/drivers/staging/vt6655/baseband.c b/drivers/staging/vt6655/baseband.c
index e7b93a21e3b2..8d2c6a789ab2 100644
--- a/drivers/staging/vt6655/baseband.c
+++ b/drivers/staging/vt6655/baseband.c
@@ -28,8 +28,8 @@
* Functions:
* BBuGetFrameTime - Calculate data frame transmitting time
* BBvCaculateParameter - Caculate PhyLength, PhyService and Phy Signal parameter for baseband Tx
- * BBbReadEmbeded - Embeded read baseband register via MAC
- * BBbWriteEmbeded - Embeded write baseband register via MAC
+ * BBbReadEmbedded - Embedded read baseband register via MAC
+ * BBbWriteEmbedded - Embedded write baseband register via MAC
* BBbIsRegBitsOn - Test if baseband register bits on
* BBbIsRegBitsOff - Test if baseband register bits off
* BBbVT3253Init - VIA VT3253 baseband chip init code
@@ -40,7 +40,7 @@
* Revision History:
* 06-10-2003 Bryan YC Fan: Re-write codes to support VT3253 spec.
* 08-07-2003 Bryan YC Fan: Add MAXIM2827/2825 and RFMD2959 support.
- * 08-26-2003 Kyle Hsu : Modify BBuGetFrameTime() and BBvCaculateParameter().
+ * 08-26-2003 Kyle Hsu : Modify BBuGetFrameTime() and BBvCalculateParameter().
* cancel the setting of MAC_REG_SOFTPWRCTL on BBbVT3253Init().
* Add the comments.
* 09-01-2003 Bryan YC Fan: RF & BB tables updated.
@@ -1826,7 +1826,7 @@ BBuGetFrameTime (
}
/*
- * Description: Caculate Length, Service, and Signal fields of Phy for Tx
+ * Description: Calculate Length, Service, and Signal fields of Phy for Tx
*
* Parameters:
* In:
@@ -1842,7 +1842,7 @@ BBuGetFrameTime (
*
*/
void
-BBvCaculateParameter (
+BBvCalculateParameter (
PSDevice pDevice,
unsigned int cbFrameLength,
unsigned short wRate,
@@ -2001,7 +2001,7 @@ BBvCaculateParameter (
}
/*
- * Description: Read a byte from BASEBAND, by embeded programming
+ * Description: Read a byte from BASEBAND, by embedded programming
*
* Parameters:
* In:
@@ -2013,7 +2013,7 @@ BBvCaculateParameter (
* Return Value: true if succeeded; false if failed.
*
*/
-bool BBbReadEmbeded (unsigned long dwIoBase, unsigned char byBBAddr, unsigned char *pbyData)
+bool BBbReadEmbedded (unsigned long dwIoBase, unsigned char byBBAddr, unsigned char *pbyData)
{
unsigned short ww;
unsigned char byValue;
@@ -2043,7 +2043,7 @@ bool BBbReadEmbeded (unsigned long dwIoBase, unsigned char byBBAddr, unsigned ch
/*
- * Description: Write a Byte to BASEBAND, by embeded programming
+ * Description: Write a Byte to BASEBAND, by embedded programming
*
* Parameters:
* In:
@@ -2056,7 +2056,7 @@ bool BBbReadEmbeded (unsigned long dwIoBase, unsigned char byBBAddr, unsigned ch
* Return Value: true if succeeded; false if failed.
*
*/
-bool BBbWriteEmbeded (unsigned long dwIoBase, unsigned char byBBAddr, unsigned char byData)
+bool BBbWriteEmbedded (unsigned long dwIoBase, unsigned char byBBAddr, unsigned char byData)
{
unsigned short ww;
unsigned char byValue;
@@ -2102,7 +2102,7 @@ bool BBbIsRegBitsOn (unsigned long dwIoBase, unsigned char byBBAddr, unsigned ch
{
unsigned char byOrgData;
- BBbReadEmbeded(dwIoBase, byBBAddr, &byOrgData);
+ BBbReadEmbedded(dwIoBase, byBBAddr, &byOrgData);
return (byOrgData & byTestBits) == byTestBits;
}
@@ -2125,7 +2125,7 @@ bool BBbIsRegBitsOff (unsigned long dwIoBase, unsigned char byBBAddr, unsigned c
{
unsigned char byOrgData;
- BBbReadEmbeded(dwIoBase, byBBAddr, &byOrgData);
+ BBbReadEmbedded(dwIoBase, byBBAddr, &byOrgData);
return (byOrgData & byTestBits) == 0;
}
@@ -2155,14 +2155,14 @@ bool BBbVT3253Init (PSDevice pDevice)
if (byRFType == RF_RFMD2959) {
if (byLocalID <= REV_ID_VT3253_A1) {
for (ii = 0; ii < CB_VT3253_INIT_FOR_RFMD; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253InitTab_RFMD[ii][0],byVT3253InitTab_RFMD[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253InitTab_RFMD[ii][0],byVT3253InitTab_RFMD[ii][1]);
}
} else {
for (ii = 0; ii < CB_VT3253B0_INIT_FOR_RFMD; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_RFMD[ii][0],byVT3253B0_RFMD[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_RFMD[ii][0],byVT3253B0_RFMD[ii][1]);
}
for (ii = 0; ii < CB_VT3253B0_AGC_FOR_RFMD2959; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_AGC4_RFMD2959[ii][0],byVT3253B0_AGC4_RFMD2959[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_AGC4_RFMD2959[ii][0],byVT3253B0_AGC4_RFMD2959[ii][1]);
}
VNSvOutPortD(dwIoBase + MAC_REG_ITRTMSET, 0x23);
MACvRegBitsOn(dwIoBase, MAC_REG_PAPEDELAY, BIT0);
@@ -2177,10 +2177,10 @@ bool BBbVT3253Init (PSDevice pDevice)
pDevice->ldBmThreshold[3] = 0;
} else if ((byRFType == RF_AIROHA) || (byRFType == RF_AL2230S) ) {
for (ii = 0; ii < CB_VT3253B0_INIT_FOR_AIROHA2230; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_AIROHA2230[ii][0],byVT3253B0_AIROHA2230[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_AIROHA2230[ii][0],byVT3253B0_AIROHA2230[ii][1]);
}
for (ii = 0; ii < CB_VT3253B0_AGC; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_AGC[ii][0],byVT3253B0_AGC[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_AGC[ii][0],byVT3253B0_AGC[ii][1]);
}
pDevice->abyBBVGA[0] = 0x1C;
pDevice->abyBBVGA[1] = 0x10;
@@ -2192,10 +2192,10 @@ bool BBbVT3253Init (PSDevice pDevice)
pDevice->ldBmThreshold[3] = 0;
} else if (byRFType == RF_UW2451) {
for (ii = 0; ii < CB_VT3253B0_INIT_FOR_UW2451; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_UW2451[ii][0],byVT3253B0_UW2451[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_UW2451[ii][0],byVT3253B0_UW2451[ii][1]);
}
for (ii = 0; ii < CB_VT3253B0_AGC; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_AGC[ii][0],byVT3253B0_AGC[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_AGC[ii][0],byVT3253B0_AGC[ii][1]);
}
VNSvOutPortB(dwIoBase + MAC_REG_ITRTMSET, 0x23);
MACvRegBitsOn(dwIoBase, MAC_REG_PAPEDELAY, BIT0);
@@ -2210,28 +2210,28 @@ bool BBbVT3253Init (PSDevice pDevice)
pDevice->ldBmThreshold[3] = 0;
} else if (byRFType == RF_UW2452) {
for (ii = 0; ii < CB_VT3253B0_INIT_FOR_UW2451; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_UW2451[ii][0],byVT3253B0_UW2451[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_UW2451[ii][0],byVT3253B0_UW2451[ii][1]);
}
// Init ANT B select,TX Config CR09 = 0x61->0x45, 0x45->0x41(VC1/VC2 define, make the ANT_A, ANT_B inverted)
- //bResult &= BBbWriteEmbeded(dwIoBase,0x09,0x41);
+ //bResult &= BBbWriteEmbedded(dwIoBase,0x09,0x41);
// Init ANT B select,RX Config CR10 = 0x28->0x2A, 0x2A->0x28(VC1/VC2 define, make the ANT_A, ANT_B inverted)
- //bResult &= BBbWriteEmbeded(dwIoBase,0x0a,0x28);
+ //bResult &= BBbWriteEmbedded(dwIoBase,0x0a,0x28);
// Select VC1/VC2, CR215 = 0x02->0x06
- bResult &= BBbWriteEmbeded(dwIoBase,0xd7,0x06);
+ bResult &= BBbWriteEmbedded(dwIoBase,0xd7,0x06);
//{{RobertYu:20050125, request by Jack
- bResult &= BBbWriteEmbeded(dwIoBase,0x90,0x20);
- bResult &= BBbWriteEmbeded(dwIoBase,0x97,0xeb);
+ bResult &= BBbWriteEmbedded(dwIoBase,0x90,0x20);
+ bResult &= BBbWriteEmbedded(dwIoBase,0x97,0xeb);
//}}
//{{RobertYu:20050221, request by Jack
- bResult &= BBbWriteEmbeded(dwIoBase,0xa6,0x00);
- bResult &= BBbWriteEmbeded(dwIoBase,0xa8,0x30);
+ bResult &= BBbWriteEmbedded(dwIoBase,0xa6,0x00);
+ bResult &= BBbWriteEmbedded(dwIoBase,0xa8,0x30);
//}}
- bResult &= BBbWriteEmbeded(dwIoBase,0xb0,0x58);
+ bResult &= BBbWriteEmbedded(dwIoBase,0xb0,0x58);
for (ii = 0; ii < CB_VT3253B0_AGC; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_AGC[ii][0],byVT3253B0_AGC[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_AGC[ii][0],byVT3253B0_AGC[ii][1]);
}
//VNSvOutPortB(dwIoBase + MAC_REG_ITRTMSET, 0x23); // RobertYu: 20050104, 20050131 disable PA_Delay
//MACvRegBitsOn(dwIoBase, MAC_REG_PAPEDELAY, BIT0); // RobertYu: 20050104, 20050131 disable PA_Delay
@@ -2248,10 +2248,10 @@ bool BBbVT3253Init (PSDevice pDevice)
} else if (byRFType == RF_VT3226) {
for (ii = 0; ii < CB_VT3253B0_INIT_FOR_AIROHA2230; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_AIROHA2230[ii][0],byVT3253B0_AIROHA2230[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_AIROHA2230[ii][0],byVT3253B0_AIROHA2230[ii][1]);
}
for (ii = 0; ii < CB_VT3253B0_AGC; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_AGC[ii][0],byVT3253B0_AGC[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_AGC[ii][0],byVT3253B0_AGC[ii][1]);
}
pDevice->abyBBVGA[0] = 0x1C;
pDevice->abyBBVGA[1] = 0x10;
@@ -2266,20 +2266,20 @@ bool BBbVT3253Init (PSDevice pDevice)
//{{ RobertYu: 20050104
} else if (byRFType == RF_AIROHA7230) {
for (ii = 0; ii < CB_VT3253B0_INIT_FOR_AIROHA2230; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_AIROHA2230[ii][0],byVT3253B0_AIROHA2230[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_AIROHA2230[ii][0],byVT3253B0_AIROHA2230[ii][1]);
}
//{{ RobertYu:20050223, request by JerryChung
// Init ANT B select,TX Config CR09 = 0x61->0x45, 0x45->0x41(VC1/VC2 define, make the ANT_A, ANT_B inverted)
- //bResult &= BBbWriteEmbeded(dwIoBase,0x09,0x41);
+ //bResult &= BBbWriteEmbedded(dwIoBase,0x09,0x41);
// Init ANT B select,RX Config CR10 = 0x28->0x2A, 0x2A->0x28(VC1/VC2 define, make the ANT_A, ANT_B inverted)
- //bResult &= BBbWriteEmbeded(dwIoBase,0x0a,0x28);
+ //bResult &= BBbWriteEmbedded(dwIoBase,0x0a,0x28);
// Select VC1/VC2, CR215 = 0x02->0x06
- bResult &= BBbWriteEmbeded(dwIoBase,0xd7,0x06);
+ bResult &= BBbWriteEmbedded(dwIoBase,0xd7,0x06);
//}}
for (ii = 0; ii < CB_VT3253B0_AGC; ii++) {
- bResult &= BBbWriteEmbeded(dwIoBase,byVT3253B0_AGC[ii][0],byVT3253B0_AGC[ii][1]);
+ bResult &= BBbWriteEmbedded(dwIoBase,byVT3253B0_AGC[ii][0],byVT3253B0_AGC[ii][1]);
}
pDevice->abyBBVGA[0] = 0x1C;
pDevice->abyBBVGA[1] = 0x10;
@@ -2297,8 +2297,8 @@ bool BBbVT3253Init (PSDevice pDevice)
}
if (byLocalID > REV_ID_VT3253_A1) {
- BBbWriteEmbeded(dwIoBase, 0x04, 0x7F);
- BBbWriteEmbeded(dwIoBase, 0x0D, 0x01);
+ BBbWriteEmbedded(dwIoBase, 0x04, 0x7F);
+ BBbWriteEmbedded(dwIoBase, 0x0D, 0x01);
}
return bResult;
@@ -2324,7 +2324,7 @@ void BBvReadAllRegs (unsigned long dwIoBase, unsigned char *pbyBBRegs)
int ii;
unsigned char byBase = 1;
for (ii = 0; ii < BB_MAX_CONTEXT_SIZE; ii++) {
- BBbReadEmbeded(dwIoBase, (unsigned char)(ii*byBase), pbyBBRegs);
+ BBbReadEmbedded(dwIoBase, (unsigned char)(ii*byBase), pbyBBRegs);
pbyBBRegs += byBase;
}
}
@@ -2350,39 +2350,39 @@ void BBvLoopbackOn (PSDevice pDevice)
unsigned long dwIoBase = pDevice->PortOffset;
//CR C9 = 0x00
- BBbReadEmbeded(dwIoBase, 0xC9, &pDevice->byBBCRc9);//CR201
- BBbWriteEmbeded(dwIoBase, 0xC9, 0);
- BBbReadEmbeded(dwIoBase, 0x4D, &pDevice->byBBCR4d);//CR77
- BBbWriteEmbeded(dwIoBase, 0x4D, 0x90);
+ BBbReadEmbedded(dwIoBase, 0xC9, &pDevice->byBBCRc9);//CR201
+ BBbWriteEmbedded(dwIoBase, 0xC9, 0);
+ BBbReadEmbedded(dwIoBase, 0x4D, &pDevice->byBBCR4d);//CR77
+ BBbWriteEmbedded(dwIoBase, 0x4D, 0x90);
//CR 88 = 0x02(CCK), 0x03(OFDM)
- BBbReadEmbeded(dwIoBase, 0x88, &pDevice->byBBCR88);//CR136
+ BBbReadEmbedded(dwIoBase, 0x88, &pDevice->byBBCR88);//CR136
if (pDevice->uConnectionRate <= RATE_11M) { //CCK
// Enable internal digital loopback: CR33 |= 0000 0001
- BBbReadEmbeded(dwIoBase, 0x21, &byData);//CR33
- BBbWriteEmbeded(dwIoBase, 0x21, (unsigned char)(byData | 0x01));//CR33
+ BBbReadEmbedded(dwIoBase, 0x21, &byData);//CR33
+ BBbWriteEmbedded(dwIoBase, 0x21, (unsigned char)(byData | 0x01));//CR33
// CR154 = 0x00
- BBbWriteEmbeded(dwIoBase, 0x9A, 0); //CR154
+ BBbWriteEmbedded(dwIoBase, 0x9A, 0); //CR154
- BBbWriteEmbeded(dwIoBase, 0x88, 0x02);//CR239
+ BBbWriteEmbedded(dwIoBase, 0x88, 0x02);//CR239
}
else { //OFDM
// Enable internal digital loopback:CR154 |= 0000 0001
- BBbReadEmbeded(dwIoBase, 0x9A, &byData);//CR154
- BBbWriteEmbeded(dwIoBase, 0x9A, (unsigned char)(byData | 0x01));//CR154
+ BBbReadEmbedded(dwIoBase, 0x9A, &byData);//CR154
+ BBbWriteEmbedded(dwIoBase, 0x9A, (unsigned char)(byData | 0x01));//CR154
// CR33 = 0x00
- BBbWriteEmbeded(dwIoBase, 0x21, 0); //CR33
+ BBbWriteEmbedded(dwIoBase, 0x21, 0); //CR33
- BBbWriteEmbeded(dwIoBase, 0x88, 0x03);//CR239
+ BBbWriteEmbedded(dwIoBase, 0x88, 0x03);//CR239
}
//CR14 = 0x00
- BBbWriteEmbeded(dwIoBase, 0x0E, 0);//CR14
+ BBbWriteEmbedded(dwIoBase, 0x0E, 0);//CR14
// Disable TX_IQUN
- BBbReadEmbeded(pDevice->PortOffset, 0x09, &pDevice->byBBCR09);
- BBbWriteEmbeded(pDevice->PortOffset, 0x09, (unsigned char)(pDevice->byBBCR09 & 0xDE));
+ BBbReadEmbedded(pDevice->PortOffset, 0x09, &pDevice->byBBCR09);
+ BBbWriteEmbedded(pDevice->PortOffset, 0x09, (unsigned char)(pDevice->byBBCR09 & 0xDE));
}
/*
@@ -2403,22 +2403,22 @@ void BBvLoopbackOff (PSDevice pDevice)
unsigned char byData;
unsigned long dwIoBase = pDevice->PortOffset;
- BBbWriteEmbeded(dwIoBase, 0xC9, pDevice->byBBCRc9);//CR201
- BBbWriteEmbeded(dwIoBase, 0x88, pDevice->byBBCR88);//CR136
- BBbWriteEmbeded(dwIoBase, 0x09, pDevice->byBBCR09);//CR136
- BBbWriteEmbeded(dwIoBase, 0x4D, pDevice->byBBCR4d);//CR77
+ BBbWriteEmbedded(dwIoBase, 0xC9, pDevice->byBBCRc9);//CR201
+ BBbWriteEmbedded(dwIoBase, 0x88, pDevice->byBBCR88);//CR136
+ BBbWriteEmbedded(dwIoBase, 0x09, pDevice->byBBCR09);//CR136
+ BBbWriteEmbedded(dwIoBase, 0x4D, pDevice->byBBCR4d);//CR77
if (pDevice->uConnectionRate <= RATE_11M) { // CCK
// Set the CR33 Bit2 to disable internal Loopback.
- BBbReadEmbeded(dwIoBase, 0x21, &byData);//CR33
- BBbWriteEmbeded(dwIoBase, 0x21, (unsigned char)(byData & 0xFE));//CR33
+ BBbReadEmbedded(dwIoBase, 0x21, &byData);//CR33
+ BBbWriteEmbedded(dwIoBase, 0x21, (unsigned char)(byData & 0xFE));//CR33
}
else { // OFDM
- BBbReadEmbeded(dwIoBase, 0x9A, &byData);//CR154
- BBbWriteEmbeded(dwIoBase, 0x9A, (unsigned char)(byData & 0xFE));//CR154
+ BBbReadEmbedded(dwIoBase, 0x9A, &byData);//CR154
+ BBbWriteEmbedded(dwIoBase, 0x9A, (unsigned char)(byData & 0xFE));//CR154
}
- BBbReadEmbeded(dwIoBase, 0x0E, &byData);//CR14
- BBbWriteEmbeded(dwIoBase, 0x0E, (unsigned char)(byData | 0x80));//CR14
+ BBbReadEmbedded(dwIoBase, 0x0E, &byData);//CR14
+ BBbWriteEmbedded(dwIoBase, 0x0E, (unsigned char)(byData | 0x80));//CR14
}
@@ -2442,7 +2442,7 @@ BBvSetShortSlotTime (PSDevice pDevice)
unsigned char byBBRxConf=0;
unsigned char byBBVGA=0;
- BBbReadEmbeded(pDevice->PortOffset, 0x0A, &byBBRxConf);//CR10
+ BBbReadEmbedded(pDevice->PortOffset, 0x0A, &byBBRxConf);//CR10
if (pDevice->bShortSlotTime) {
byBBRxConf &= 0xDF;//1101 1111
@@ -2451,12 +2451,12 @@ BBvSetShortSlotTime (PSDevice pDevice)
}
// patch for 3253B0 Baseband with Cardbus module
- BBbReadEmbeded(pDevice->PortOffset, 0xE7, &byBBVGA);
+ BBbReadEmbedded(pDevice->PortOffset, 0xE7, &byBBVGA);
if (byBBVGA == pDevice->abyBBVGA[0]) {
byBBRxConf |= 0x20;//0010 0000
}
- BBbWriteEmbeded(pDevice->PortOffset, 0x0A, byBBRxConf);//CR10
+ BBbWriteEmbedded(pDevice->PortOffset, 0x0A, byBBRxConf);//CR10
}
@@ -2464,9 +2464,9 @@ void BBvSetVGAGainOffset(PSDevice pDevice, unsigned char byData)
{
unsigned char byBBRxConf=0;
- BBbWriteEmbeded(pDevice->PortOffset, 0xE7, byData);
+ BBbWriteEmbedded(pDevice->PortOffset, 0xE7, byData);
- BBbReadEmbeded(pDevice->PortOffset, 0x0A, &byBBRxConf);//CR10
+ BBbReadEmbedded(pDevice->PortOffset, 0x0A, &byBBRxConf);//CR10
// patch for 3253B0 Baseband with Cardbus module
if (byData == pDevice->abyBBVGA[0]) {
byBBRxConf |= 0x20;//0010 0000
@@ -2476,7 +2476,7 @@ void BBvSetVGAGainOffset(PSDevice pDevice, unsigned char byData)
byBBRxConf |= 0x20;//0010 0000
}
pDevice->byBBVGACurrent = byData;
- BBbWriteEmbeded(pDevice->PortOffset, 0x0A, byBBRxConf);//CR10
+ BBbWriteEmbedded(pDevice->PortOffset, 0x0A, byBBRxConf);//CR10
}
@@ -2495,10 +2495,10 @@ void BBvSetVGAGainOffset(PSDevice pDevice, unsigned char byData)
void
BBvSoftwareReset (unsigned long dwIoBase)
{
- BBbWriteEmbeded(dwIoBase, 0x50, 0x40);
- BBbWriteEmbeded(dwIoBase, 0x50, 0);
- BBbWriteEmbeded(dwIoBase, 0x9C, 0x01);
- BBbWriteEmbeded(dwIoBase, 0x9C, 0);
+ BBbWriteEmbedded(dwIoBase, 0x50, 0x40);
+ BBbWriteEmbedded(dwIoBase, 0x50, 0);
+ BBbWriteEmbedded(dwIoBase, 0x9C, 0x01);
+ BBbWriteEmbedded(dwIoBase, 0x9C, 0);
}
/*
@@ -2518,9 +2518,9 @@ BBvPowerSaveModeON (unsigned long dwIoBase)
{
unsigned char byOrgData;
- BBbReadEmbeded(dwIoBase, 0x0D, &byOrgData);
+ BBbReadEmbedded(dwIoBase, 0x0D, &byOrgData);
byOrgData |= BIT0;
- BBbWriteEmbeded(dwIoBase, 0x0D, byOrgData);
+ BBbWriteEmbedded(dwIoBase, 0x0D, byOrgData);
}
/*
@@ -2540,9 +2540,9 @@ BBvPowerSaveModeOFF (unsigned long dwIoBase)
{
unsigned char byOrgData;
- BBbReadEmbeded(dwIoBase, 0x0D, &byOrgData);
+ BBbReadEmbedded(dwIoBase, 0x0D, &byOrgData);
byOrgData &= ~(BIT0);
- BBbWriteEmbeded(dwIoBase, 0x0D, byOrgData);
+ BBbWriteEmbedded(dwIoBase, 0x0D, byOrgData);
}
/*
@@ -2567,7 +2567,7 @@ BBvSetTxAntennaMode (unsigned long dwIoBase, unsigned char byAntennaMode)
#ifdef PLICE_DEBUG
//printk("Enter BBvSetTxAntennaMode\n");
#endif
- BBbReadEmbeded(dwIoBase, 0x09, &byBBTxConf);//CR09
+ BBbReadEmbedded(dwIoBase, 0x09, &byBBTxConf);//CR09
if (byAntennaMode == ANT_DIVERSITY) {
// bit 1 is diversity
byBBTxConf |= 0x02;
@@ -2581,7 +2581,7 @@ BBvSetTxAntennaMode (unsigned long dwIoBase, unsigned char byAntennaMode)
byBBTxConf &= 0xFD; // 1111 1101
byBBTxConf |= 0x04;
}
- BBbWriteEmbeded(dwIoBase, 0x09, byBBTxConf);//CR09
+ BBbWriteEmbedded(dwIoBase, 0x09, byBBTxConf);//CR09
}
@@ -2606,7 +2606,7 @@ BBvSetRxAntennaMode (unsigned long dwIoBase, unsigned char byAntennaMode)
{
unsigned char byBBRxConf;
- BBbReadEmbeded(dwIoBase, 0x0A, &byBBRxConf);//CR10
+ BBbReadEmbedded(dwIoBase, 0x0A, &byBBRxConf);//CR10
if (byAntennaMode == ANT_DIVERSITY) {
byBBRxConf |= 0x01;
@@ -2616,7 +2616,7 @@ BBvSetRxAntennaMode (unsigned long dwIoBase, unsigned char byAntennaMode)
byBBRxConf &= 0xFE; // 1111 1110
byBBRxConf |= 0x02;
}
- BBbWriteEmbeded(dwIoBase, 0x0A, byBBRxConf);//CR10
+ BBbWriteEmbedded(dwIoBase, 0x0A, byBBRxConf);//CR10
}
@@ -2635,15 +2635,15 @@ BBvSetRxAntennaMode (unsigned long dwIoBase, unsigned char byAntennaMode)
void
BBvSetDeepSleep (unsigned long dwIoBase, unsigned char byLocalID)
{
- BBbWriteEmbeded(dwIoBase, 0x0C, 0x17);//CR12
- BBbWriteEmbeded(dwIoBase, 0x0D, 0xB9);//CR13
+ BBbWriteEmbedded(dwIoBase, 0x0C, 0x17);//CR12
+ BBbWriteEmbedded(dwIoBase, 0x0D, 0xB9);//CR13
}
void
BBvExitDeepSleep (unsigned long dwIoBase, unsigned char byLocalID)
{
- BBbWriteEmbeded(dwIoBase, 0x0C, 0x00);//CR12
- BBbWriteEmbeded(dwIoBase, 0x0D, 0x01);//CR13
+ BBbWriteEmbedded(dwIoBase, 0x0C, 0x00);//CR12
+ BBbWriteEmbedded(dwIoBase, 0x0D, 0x01);//CR13
}
diff --git a/drivers/staging/vt6655/baseband.h b/drivers/staging/vt6655/baseband.h
index be2d68909490..9b5bc9c58d9f 100644
--- a/drivers/staging/vt6655/baseband.h
+++ b/drivers/staging/vt6655/baseband.h
@@ -73,12 +73,12 @@
#define BBvClearFOE(dwIoBase) \
{ \
- BBbWriteEmbeded(dwIoBase, 0xB1, 0); \
+ BBbWriteEmbedded(dwIoBase, 0xB1, 0); \
}
#define BBvSetFOE(dwIoBase) \
{ \
- BBbWriteEmbeded(dwIoBase, 0xB1, 0x0C); \
+ BBbWriteEmbedded(dwIoBase, 0xB1, 0x0C); \
}
@@ -97,7 +97,7 @@ BBuGetFrameTime(
);
void
-BBvCaculateParameter (
+BBvCalculateParameter (
PSDevice pDevice,
unsigned int cbFrameLength,
unsigned short wRate,
@@ -107,8 +107,8 @@ BBvCaculateParameter (
unsigned char *pbyPhySgn
);
-bool BBbReadEmbeded(unsigned long dwIoBase, unsigned char byBBAddr, unsigned char *pbyData);
-bool BBbWriteEmbeded(unsigned long dwIoBase, unsigned char byBBAddr, unsigned char byData);
+bool BBbReadEmbedded(unsigned long dwIoBase, unsigned char byBBAddr, unsigned char *pbyData);
+bool BBbWriteEmbedded(unsigned long dwIoBase, unsigned char byBBAddr, unsigned char byData);
void BBvReadAllRegs(unsigned long dwIoBase, unsigned char *pbyBBRegs);
void BBvLoopbackOn(PSDevice pDevice);
diff --git a/drivers/staging/vt6655/bssdb.c b/drivers/staging/vt6655/bssdb.c
index fcffa4f0f4e3..fe57fb880a8f 100644
--- a/drivers/staging/vt6655/bssdb.c
+++ b/drivers/staging/vt6655/bssdb.c
@@ -784,8 +784,8 @@ BSSDBbIsSTAInNodeDB(void *pMgmtObject, unsigned char *abyDstAddr,
/*+
*
* Routine Description:
- * Find an empty node and allocated; if no empty found,
- * instand used of most inactive one.
+ * Find an empty node and allocat it; if there is no empty node,
+ * then use the most inactive one.
*
* Return Value:
* None
diff --git a/drivers/staging/vt6655/card.c b/drivers/staging/vt6655/card.c
index 2721e0798496..319ca482f003 100644
--- a/drivers/staging/vt6655/card.c
+++ b/drivers/staging/vt6655/card.c
@@ -28,9 +28,9 @@
* CARDbIsOFDMinBasicRate - Check if any OFDM rate is in BasicRateSet
* CARDvSetLoopbackMode - Set Loopback mode
* CARDbSoftwareReset - Sortware reset NIC
- * CARDqGetTSFOffset - Caculate TSFOffset
+ * CARDqGetTSFOffset - Calculate TSFOffset
* CARDbGetCurrentTSF - Read Current NIC TSF counter
- * CARDqGetNextTBTT - Caculate Next Beacon TSF counter
+ * CARDqGetNextTBTT - Calculate Next Beacon TSF counter
* CARDvSetFirstNextTBTT - Set NIC Beacon time
* CARDvUpdateNextTBTT - Sync. NIC Beacon time
* CARDbRadioPowerOff - Turn Off NIC Radio Power
@@ -100,7 +100,7 @@ const unsigned short cwRXBCNTSFOff[MAX_RATE] =
static
void
-s_vCaculateOFDMRParameter(
+s_vCalculateOFDMRParameter(
unsigned char byRate,
CARD_PHY_TYPE ePHYType,
unsigned char *pbyTxRate,
@@ -111,7 +111,7 @@ s_vCaculateOFDMRParameter(
/*--------------------- Export Functions --------------------------*/
/*
- * Description: Caculate TxRate and RsvTime fields for RSPINF in OFDM mode.
+ * Description: Calculate TxRate and RsvTime fields for RSPINF in OFDM mode.
*
* Parameters:
* In:
@@ -126,7 +126,7 @@ s_vCaculateOFDMRParameter(
*/
static
void
-s_vCaculateOFDMRParameter (
+s_vCalculateOFDMRParameter (
unsigned char byRate,
CARD_PHY_TYPE ePHYType,
unsigned char *pbyTxRate,
@@ -251,7 +251,7 @@ s_vSetRSPINF (PSDevice pDevice, CARD_PHY_TYPE ePHYType, void *pvSupportRateIEs,
MACvSelectPage1(pDevice->PortOffset);
//RSPINF_b_1
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
VNTWIFIbyGetACKTxRate(RATE_1M, pvSupportRateIEs, pvExtSupportRateIEs),
PK_TYPE_11B,
@@ -262,7 +262,7 @@ s_vSetRSPINF (PSDevice pDevice, CARD_PHY_TYPE ePHYType, void *pvSupportRateIEs,
VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_1, MAKEDWORD(wLen,MAKEWORD(bySignal,byServ)));
///RSPINF_b_2
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
VNTWIFIbyGetACKTxRate(RATE_2M, pvSupportRateIEs, pvExtSupportRateIEs),
PK_TYPE_11B,
@@ -273,7 +273,7 @@ s_vSetRSPINF (PSDevice pDevice, CARD_PHY_TYPE ePHYType, void *pvSupportRateIEs,
VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_2, MAKEDWORD(wLen,MAKEWORD(bySignal,byServ)));
//RSPINF_b_5
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
VNTWIFIbyGetACKTxRate(RATE_5M, pvSupportRateIEs, pvExtSupportRateIEs),
PK_TYPE_11B,
@@ -284,7 +284,7 @@ s_vSetRSPINF (PSDevice pDevice, CARD_PHY_TYPE ePHYType, void *pvSupportRateIEs,
VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_5, MAKEDWORD(wLen,MAKEWORD(bySignal,byServ)));
//RSPINF_b_11
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
VNTWIFIbyGetACKTxRate(RATE_11M, pvSupportRateIEs, pvExtSupportRateIEs),
PK_TYPE_11B,
@@ -295,51 +295,51 @@ s_vSetRSPINF (PSDevice pDevice, CARD_PHY_TYPE ePHYType, void *pvSupportRateIEs,
VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_11, MAKEDWORD(wLen,MAKEWORD(bySignal,byServ)));
//RSPINF_a_6
- s_vCaculateOFDMRParameter(RATE_6M,
+ s_vCalculateOFDMRParameter(RATE_6M,
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_6, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_9
- s_vCaculateOFDMRParameter(RATE_9M,
+ s_vCalculateOFDMRParameter(RATE_9M,
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_9, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_12
- s_vCaculateOFDMRParameter(RATE_12M,
+ s_vCalculateOFDMRParameter(RATE_12M,
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_12, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_18
- s_vCaculateOFDMRParameter(RATE_18M,
+ s_vCalculateOFDMRParameter(RATE_18M,
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_18, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_24
- s_vCaculateOFDMRParameter(RATE_24M,
+ s_vCalculateOFDMRParameter(RATE_24M,
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_24, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_36
- s_vCaculateOFDMRParameter(
+ s_vCalculateOFDMRParameter(
VNTWIFIbyGetACKTxRate(RATE_36M, pvSupportRateIEs, pvExtSupportRateIEs),
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_36, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_48
- s_vCaculateOFDMRParameter(
+ s_vCalculateOFDMRParameter(
VNTWIFIbyGetACKTxRate(RATE_48M, pvSupportRateIEs, pvExtSupportRateIEs),
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_48, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_54
- s_vCaculateOFDMRParameter(
+ s_vCalculateOFDMRParameter(
VNTWIFIbyGetACKTxRate(RATE_54M, pvSupportRateIEs, pvExtSupportRateIEs),
ePHYType,
&byTxRate,
@@ -461,22 +461,22 @@ bool CARDbSetPhyParameter (void *pDeviceHandler, CARD_PHY_TYPE ePHYType, unsigne
pDevice->abyBBVGA[0] = 0x20;
pDevice->abyBBVGA[2] = 0x10;
pDevice->abyBBVGA[3] = 0x10;
- BBbReadEmbeded(pDevice->PortOffset, 0xE7, &byData);
+ BBbReadEmbedded(pDevice->PortOffset, 0xE7, &byData);
if (byData == 0x1C) {
- BBbWriteEmbeded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
+ BBbWriteEmbedded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
}
} else if (pDevice->byRFType == RF_UW2452) {
MACvSetBBType(pDevice->PortOffset, BB_TYPE_11A);
pDevice->abyBBVGA[0] = 0x18;
- BBbReadEmbeded(pDevice->PortOffset, 0xE7, &byData);
+ BBbReadEmbedded(pDevice->PortOffset, 0xE7, &byData);
if (byData == 0x14) {
- BBbWriteEmbeded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
- BBbWriteEmbeded(pDevice->PortOffset, 0xE1, 0x57);
+ BBbWriteEmbedded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
+ BBbWriteEmbedded(pDevice->PortOffset, 0xE1, 0x57);
}
} else {
MACvSetBBType(pDevice->PortOffset, BB_TYPE_11A);
}
- BBbWriteEmbeded(pDevice->PortOffset, 0x88, 0x03);
+ BBbWriteEmbedded(pDevice->PortOffset, 0x88, 0x03);
bySlot = C_SLOT_SHORT;
bySIFS = C_SIFS_A;
byDIFS = C_SIFS_A + 2*C_SLOT_SHORT;
@@ -490,19 +490,19 @@ bool CARDbSetPhyParameter (void *pDeviceHandler, CARD_PHY_TYPE ePHYType, unsigne
pDevice->abyBBVGA[0] = 0x1C;
pDevice->abyBBVGA[2] = 0x00;
pDevice->abyBBVGA[3] = 0x00;
- BBbReadEmbeded(pDevice->PortOffset, 0xE7, &byData);
+ BBbReadEmbedded(pDevice->PortOffset, 0xE7, &byData);
if (byData == 0x20) {
- BBbWriteEmbeded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
+ BBbWriteEmbedded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
}
} else if (pDevice->byRFType == RF_UW2452) {
pDevice->abyBBVGA[0] = 0x14;
- BBbReadEmbeded(pDevice->PortOffset, 0xE7, &byData);
+ BBbReadEmbedded(pDevice->PortOffset, 0xE7, &byData);
if (byData == 0x18) {
- BBbWriteEmbeded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
- BBbWriteEmbeded(pDevice->PortOffset, 0xE1, 0xD3);
+ BBbWriteEmbedded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
+ BBbWriteEmbedded(pDevice->PortOffset, 0xE1, 0xD3);
}
}
- BBbWriteEmbeded(pDevice->PortOffset, 0x88, 0x02);
+ BBbWriteEmbedded(pDevice->PortOffset, 0x88, 0x02);
bySlot = C_SLOT_LONG;
bySIFS = C_SIFS_BG;
byDIFS = C_SIFS_BG + 2*C_SLOT_LONG;
@@ -517,19 +517,19 @@ bool CARDbSetPhyParameter (void *pDeviceHandler, CARD_PHY_TYPE ePHYType, unsigne
pDevice->abyBBVGA[0] = 0x1C;
pDevice->abyBBVGA[2] = 0x00;
pDevice->abyBBVGA[3] = 0x00;
- BBbReadEmbeded(pDevice->PortOffset, 0xE7, &byData);
+ BBbReadEmbedded(pDevice->PortOffset, 0xE7, &byData);
if (byData == 0x20) {
- BBbWriteEmbeded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
+ BBbWriteEmbedded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
}
} else if (pDevice->byRFType == RF_UW2452) {
pDevice->abyBBVGA[0] = 0x14;
- BBbReadEmbeded(pDevice->PortOffset, 0xE7, &byData);
+ BBbReadEmbedded(pDevice->PortOffset, 0xE7, &byData);
if (byData == 0x18) {
- BBbWriteEmbeded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
- BBbWriteEmbeded(pDevice->PortOffset, 0xE1, 0xD3);
+ BBbWriteEmbedded(pDevice->PortOffset, 0xE7, pDevice->abyBBVGA[0]);
+ BBbWriteEmbedded(pDevice->PortOffset, 0xE1, 0xD3);
}
}
- BBbWriteEmbeded(pDevice->PortOffset, 0x88, 0x08);
+ BBbWriteEmbedded(pDevice->PortOffset, 0x88, 0x08);
bySIFS = C_SIFS_BG;
if(VNTWIFIbIsShortSlotTime(wCapInfo)) {
bySlot = C_SLOT_SHORT;
@@ -1354,7 +1354,8 @@ CARDbSetQuiet (
/*
*
* Description:
- * Do Quiet, It will called by either ISR (after start) or VNTWIFI (before start) so do not need SPINLOCK
+ * Do Quiet, It will be called by either ISR(after start)
+ * or VNTWIFI(before start) so we do not need a SPINLOCK
*
* Parameters:
* In:
@@ -1738,7 +1739,7 @@ void CARDvSetRSPINF (void *pDeviceHandler, CARD_PHY_TYPE ePHYType)
MACvSelectPage1(pDevice->PortOffset);
//RSPINF_b_1
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
CARDwGetCCKControlRate((void *)pDevice, RATE_1M),
PK_TYPE_11B,
@@ -1749,7 +1750,7 @@ void CARDvSetRSPINF (void *pDeviceHandler, CARD_PHY_TYPE ePHYType)
VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_1, MAKEDWORD(wLen,MAKEWORD(bySignal,byServ)));
///RSPINF_b_2
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
CARDwGetCCKControlRate((void *)pDevice, RATE_2M),
PK_TYPE_11B,
@@ -1760,7 +1761,7 @@ void CARDvSetRSPINF (void *pDeviceHandler, CARD_PHY_TYPE ePHYType)
VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_2, MAKEDWORD(wLen,MAKEWORD(bySignal,byServ)));
//RSPINF_b_5
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
CARDwGetCCKControlRate((void *)pDevice, RATE_5M),
PK_TYPE_11B,
@@ -1771,7 +1772,7 @@ void CARDvSetRSPINF (void *pDeviceHandler, CARD_PHY_TYPE ePHYType)
VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_5, MAKEDWORD(wLen,MAKEWORD(bySignal,byServ)));
//RSPINF_b_11
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
CARDwGetCCKControlRate((void *)pDevice, RATE_11M),
PK_TYPE_11B,
@@ -1782,56 +1783,56 @@ void CARDvSetRSPINF (void *pDeviceHandler, CARD_PHY_TYPE ePHYType)
VNSvOutPortD(pDevice->PortOffset + MAC_REG_RSPINF_B_11, MAKEDWORD(wLen,MAKEWORD(bySignal,byServ)));
//RSPINF_a_6
- s_vCaculateOFDMRParameter(RATE_6M,
+ s_vCalculateOFDMRParameter(RATE_6M,
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_6, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_9
- s_vCaculateOFDMRParameter(RATE_9M,
+ s_vCalculateOFDMRParameter(RATE_9M,
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_9, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_12
- s_vCaculateOFDMRParameter(RATE_12M,
+ s_vCalculateOFDMRParameter(RATE_12M,
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_12, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_18
- s_vCaculateOFDMRParameter(RATE_18M,
+ s_vCalculateOFDMRParameter(RATE_18M,
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_18, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_24
- s_vCaculateOFDMRParameter(RATE_24M,
+ s_vCalculateOFDMRParameter(RATE_24M,
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_24, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_36
- s_vCaculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_36M),
+ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_36M),
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_36, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_48
- s_vCaculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_48M),
+ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_48M),
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_48, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_54
- s_vCaculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_54M),
+ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_54M),
ePHYType,
&byTxRate,
&byRsvTime);
VNSvOutPortW(pDevice->PortOffset + MAC_REG_RSPINF_A_54, MAKEWORD(byTxRate,byRsvTime));
//RSPINF_a_72
- s_vCaculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_54M),
+ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)pDevice, RATE_54M),
ePHYType,
&byTxRate,
&byRsvTime);
@@ -2041,7 +2042,7 @@ bool CARDbSoftwareReset (void *pDeviceHandler)
/*
- * Description: Caculate TSF offset of two TSF input
+ * Description: Calculate TSF offset of two TSF input
* Get TSF Offset from RxBCN's TSF and local TSF
*
* Parameters:
diff --git a/drivers/staging/vt6655/datarate.c b/drivers/staging/vt6655/datarate.c
index efbb8f45f728..b86ec1b6d187 100644
--- a/drivers/staging/vt6655/datarate.c
+++ b/drivers/staging/vt6655/datarate.c
@@ -126,7 +126,7 @@ DATARATEbyGetRateIdx (
/*+
*
* Routine Description:
- * Rate fallback Algorithm Implementaion
+ * Rate fallback Algorithm Implementation
*
* Parameters:
* In:
diff --git a/drivers/staging/vt6655/device.h b/drivers/staging/vt6655/device.h
index c5e6b98d3e4e..e54e00bc5665 100644
--- a/drivers/staging/vt6655/device.h
+++ b/drivers/staging/vt6655/device.h
@@ -327,7 +327,7 @@ typedef struct tagSDeFragControlBlock
//flags for driver status
#define DEVICE_FLAGS_OPENED 0x00010000UL
#define DEVICE_FLAGS_WOL_ENABLED 0x00080000UL
-//flags for capbilities
+//flags for capabilities
#define DEVICE_FLAGS_TX_ALIGN 0x01000000UL
#define DEVICE_FLAGS_HAVE_CAM 0x02000000UL
#define DEVICE_FLAGS_FLOW_CTRL 0x04000000UL
@@ -567,7 +567,7 @@ typedef struct __device_info {
bool bPrvActive4RadioOFF;
bool bGPIOBlockRead;
- // Beacon releated
+ // Beacon related
unsigned short wSeqCounter;
unsigned short wBCNBufLen;
bool bBeaconBufReady;
diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c
index 89d1c22695a0..9e3b3f2bbe53 100644
--- a/drivers/staging/vt6655/device_main.c
+++ b/drivers/staging/vt6655/device_main.c
@@ -290,7 +290,7 @@ DEFINE_PCI_DEVICE_TABLE(vt6655_pci_id_table) = {
static int vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent);
-static bool vt6655_init_info(struct pci_dev* pcid, PSDevice* ppDevice, PCHIP_INFO);
+static void vt6655_init_info(struct pci_dev* pcid, PSDevice* ppDevice, PCHIP_INFO);
static void device_free_info(PSDevice pDevice);
static bool device_get_pci_info(PSDevice, struct pci_dev* pcid);
static void device_print_info(PSDevice pDevice);
@@ -347,21 +347,22 @@ static int Config_FileGetParameter(unsigned char *string,
-static char* get_chip_name(int chip_id) {
- int i;
- for (i=0;chip_info_table[i].name!=NULL;i++)
- if (chip_info_table[i].chip_id==chip_id)
- break;
- return chip_info_table[i].name;
+static char* get_chip_name(int chip_id)
+{
+ int i;
+ for (i = 0; chip_info_table[i].name != NULL; i++)
+ if (chip_info_table[i].chip_id == chip_id)
+ break;
+ return chip_info_table[i].name;
}
static void __devexit vt6655_remove(struct pci_dev *pcid)
{
- PSDevice pDevice=pci_get_drvdata(pcid);
+ PSDevice pDevice = pci_get_drvdata(pcid);
- if (pDevice==NULL)
- return;
- device_free_info(pDevice);
+ if (pDevice == NULL)
+ return;
+ device_free_info(pDevice);
}
@@ -397,31 +398,29 @@ device_set_bool_opt(unsigned int *opt, int val,bool def,u32 flag, char* name,cha
}
}
*/
-static void
-device_get_options(PSDevice pDevice, int index, char* devname) {
-
- POPTIONS pOpts = &(pDevice->sOpts);
- pOpts->nRxDescs0=RX_DESC_DEF0;
- pOpts->nRxDescs1=RX_DESC_DEF1;
- pOpts->nTxDescs[0]=TX_DESC_DEF0;
- pOpts->nTxDescs[1]=TX_DESC_DEF1;
-pOpts->flags|=DEVICE_FLAGS_IP_ALIGN;
- pOpts->int_works=INT_WORKS_DEF;
- pOpts->rts_thresh=RTS_THRESH_DEF;
- pOpts->frag_thresh=FRAG_THRESH_DEF;
- pOpts->data_rate=DATA_RATE_DEF;
- pOpts->channel_num=CHANNEL_DEF;
-
-pOpts->flags|=DEVICE_FLAGS_PREAMBLE_TYPE;
-pOpts->flags|=DEVICE_FLAGS_OP_MODE;
-//pOpts->flags|=DEVICE_FLAGS_PS_MODE;
- pOpts->short_retry=SHORT_RETRY_DEF;
- pOpts->long_retry=LONG_RETRY_DEF;
- pOpts->bbp_type=BBP_TYPE_DEF;
-pOpts->flags|=DEVICE_FLAGS_80211h_MODE;
-pOpts->flags|=DEVICE_FLAGS_DiversityANT;
-
-
+static void device_get_options(PSDevice pDevice, int index, char* devname)
+{
+ POPTIONS pOpts = &(pDevice->sOpts);
+
+ pOpts->nRxDescs0 = RX_DESC_DEF0;
+ pOpts->nRxDescs1 = RX_DESC_DEF1;
+ pOpts->nTxDescs[0] = TX_DESC_DEF0;
+ pOpts->nTxDescs[1] = TX_DESC_DEF1;
+ pOpts->flags |= DEVICE_FLAGS_IP_ALIGN;
+ pOpts->int_works = INT_WORKS_DEF;
+ pOpts->rts_thresh = RTS_THRESH_DEF;
+ pOpts->frag_thresh = FRAG_THRESH_DEF;
+ pOpts->data_rate = DATA_RATE_DEF;
+ pOpts->channel_num = CHANNEL_DEF;
+
+ pOpts->flags |= DEVICE_FLAGS_PREAMBLE_TYPE;
+ pOpts->flags |= DEVICE_FLAGS_OP_MODE;
+ //pOpts->flags|=DEVICE_FLAGS_PS_MODE;
+ pOpts->short_retry = SHORT_RETRY_DEF;
+ pOpts->long_retry = LONG_RETRY_DEF;
+ pOpts->bbp_type = BBP_TYPE_DEF;
+ pOpts->flags |= DEVICE_FLAGS_80211h_MODE;
+ pOpts->flags |= DEVICE_FLAGS_DiversityANT;
}
static void
@@ -518,7 +517,7 @@ static void s_vCompleteCurrentMeasure (PSDevice pDevice, unsigned char byResult)
//
-// Initialiation of MAC & BBP registers
+// Initialisation of MAC & BBP registers
//
static void device_init_registers(PSDevice pDevice, DEVICE_INIT_TYPE InitType)
@@ -894,18 +893,15 @@ static bool device_release_WPADEV(PSDevice pDevice)
return true;
}
-
static const struct net_device_ops device_netdev_ops = {
- .ndo_open = device_open,
- .ndo_stop = device_close,
- .ndo_do_ioctl = device_ioctl,
- .ndo_get_stats = device_get_stats,
- .ndo_start_xmit = device_xmit,
- .ndo_set_rx_mode = device_set_multi,
+ .ndo_open = device_open,
+ .ndo_stop = device_close,
+ .ndo_do_ioctl = device_ioctl,
+ .ndo_get_stats = device_get_stats,
+ .ndo_start_xmit = device_xmit,
+ .ndo_set_rx_mode = device_set_multi,
};
-
-
static int __devinit
vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent)
{
@@ -926,7 +922,7 @@ vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent)
if (dev == NULL) {
printk(KERN_ERR DEVICE_NAME ": allocate net device failed \n");
- return -ENODEV;
+ return -ENOMEM;
}
// Chain it all together
@@ -939,9 +935,7 @@ vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent)
bFirst=false;
}
- if (!vt6655_init_info(pcid, &pDevice, pChip_info)) {
- return -ENOMEM;
- }
+ vt6655_init_info(pcid, &pDevice, pChip_info);
pDevice->dev = dev;
pDevice->next_module = root_device_dev;
root_device_dev = dev;
@@ -1064,7 +1058,7 @@ vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent)
//Mask out the options cannot be set to the chip
pDevice->sOpts.flags &= pChip_info->flags;
- //Enable the chip specified capbilities
+ //Enable the chip specified capabilities
pDevice->flags = pDevice->sOpts.flags | (pChip_info->flags & 0xFF000000UL);
pDevice->tx_80211 = device_dma0_tx_80211;
pDevice->sMgmtObj.pAdapter = (void *)pDevice;
@@ -1105,7 +1099,7 @@ static void device_print_info(PSDevice pDevice)
}
-static bool __devinit vt6655_init_info(struct pci_dev* pcid, PSDevice* ppDevice,
+static void __devinit vt6655_init_info(struct pci_dev* pcid, PSDevice* ppDevice,
PCHIP_INFO pChip_info) {
PSDevice p;
@@ -1129,8 +1123,6 @@ static bool __devinit vt6655_init_info(struct pci_dev* pcid, PSDevice* ppDevice,
(*ppDevice)->multicast_limit =32;
spin_lock_init(&((*ppDevice)->lock));
-
- return true;
}
static bool device_get_pci_info(PSDevice pDevice, struct pci_dev* pcid) {
@@ -1678,7 +1670,7 @@ static int device_tx_srv(PSDevice pDevice, unsigned int uIdx) {
uFrameSize = pTD->pTDInfo->dwReqCount - uFIFOHeaderSize;
pTxBufHead = (PSTxBufHead) (pTD->pTDInfo->buf);
// Update the statistics based on the Transmit status
- // now, we DO'NT check TSR0_CDH
+ // now, we DONT check TSR0_CDH
STAvUpdateTDStatCounter(&pDevice->scStatistic,
byTsr0, byTsr1,
@@ -2660,7 +2652,7 @@ static irqreturn_t device_intr(int irq, void *dev_instance) {
(pDevice->byLocalID != REV_ID_VT3253_B0) &&
(pDevice->bBSSIDFilter == true)) {
// update RSSI
- //BBbReadEmbeded(pDevice->PortOffset, 0x3E, &byRSSI);
+ //BBbReadEmbedded(pDevice->PortOffset, 0x3E, &byRSSI);
//pDevice->uCurrRSSI = byRSSI;
}
*/
diff --git a/drivers/staging/vt6655/dpc.c b/drivers/staging/vt6655/dpc.c
index e8a71ba4b92c..373e9e4fc87d 100644
--- a/drivers/staging/vt6655/dpc.c
+++ b/drivers/staging/vt6655/dpc.c
@@ -718,7 +718,7 @@ device_receive_frame (
if ((*pbyRSSI != 0) &&
(pMgmt->pCurrBSS!=NULL)) {
RFvRSSITodBm(pDevice, *pbyRSSI, &ldBm);
- // Moniter if RSSI is too strong.
+ // Monitor if RSSI is too strong.
pMgmt->pCurrBSS->byRSSIStatCnt++;
pMgmt->pCurrBSS->byRSSIStatCnt %= RSSI_STAT_COUNT;
pMgmt->pCurrBSS->ldBmAverage[pMgmt->pCurrBSS->byRSSIStatCnt] = ldBm;
diff --git a/drivers/staging/vt6655/hostap.c b/drivers/staging/vt6655/hostap.c
index 6ac6f452b261..67b1b88b1b89 100644
--- a/drivers/staging/vt6655/hostap.c
+++ b/drivers/staging/vt6655/hostap.c
@@ -495,9 +495,7 @@ static int hostap_set_encryption(PSDevice pDevice,
return -EINVAL;
}
- if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
- param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
- param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (is_broadcast_ether_addr(param->sta_addr)) {
if (param->u.crypt.idx >= MAX_GROUP_KEY)
return -EINVAL;
iNodeIndex = 0;
@@ -716,9 +714,7 @@ static int hostap_get_encryption(PSDevice pDevice,
param->u.crypt.err = 0;
- if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
- param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
- param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (is_broadcast_ether_addr(param->sta_addr)) {
iNodeIndex = 0;
} else {
if (BSSDBbIsSTAInNodeDB(pMgmt, param->sta_addr, &iNodeIndex) == false) {
diff --git a/drivers/staging/vt6655/ioctl.c b/drivers/staging/vt6655/ioctl.c
index ef197efab049..afed6e33dfc7 100644
--- a/drivers/staging/vt6655/ioctl.c
+++ b/drivers/staging/vt6655/ioctl.c
@@ -111,7 +111,7 @@ int private_ioctl(PSDevice pDevice, struct ifreq *rq)
break;
case WLAN_CMD_ZONETYPE_SET:
- /* mike add :cann't support. */
+ /* mike add :can't support. */
result = -EOPNOTSUPP;
break;
@@ -539,11 +539,8 @@ int private_ioctl(PSDevice pDevice, struct ifreq *rq)
pMgmt->abyIBSSSuppRates[3] |= BIT7;
}
- DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Support Rate= %x %x %x %x\n",
- pMgmt->abyIBSSSuppRates[2],
- pMgmt->abyIBSSSuppRates[3],
- pMgmt->abyIBSSSuppRates[4],
- pMgmt->abyIBSSSuppRates[5]);
+ DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Support Rate= %*ph\n",
+ 4, pMgmt->abyIBSSSuppRates + 2);
netif_stop_queue(pDevice->dev);
spin_lock_irq(&pDevice->lock);
diff --git a/drivers/staging/vt6655/iwctl.c b/drivers/staging/vt6655/iwctl.c
index 77aad7f5ae76..5cdda8dab854 100644
--- a/drivers/staging/vt6655/iwctl.c
+++ b/drivers/staging/vt6655/iwctl.c
@@ -358,7 +358,7 @@ int iwctl_giwscan(struct net_device *dev,
/*
- * Wireless Handler : set frequence or channel
+ * Wireless Handler : set frequency or channel
*/
int iwctl_siwfreq(struct net_device *dev,
@@ -404,7 +404,7 @@ int iwctl_siwfreq(struct net_device *dev,
}
/*
- * Wireless Handler : get frequence or channel
+ * Wireless Handler : get frequency or channel
*/
int iwctl_giwfreq(struct net_device *dev,
@@ -1346,7 +1346,7 @@ if((wrq->flags & IW_ENCODE_DISABLED)==0){
}else if(index>0){
//when the length is 0 the request only changes the default transmit key index
- //check the new key has a non zero lenget
+ //check the new key if it has a non zero length
if(pDevice->bEncryptionEnable==false)
{
rc = -EINVAL;
diff --git a/drivers/staging/vt6655/key.c b/drivers/staging/vt6655/key.c
index 774b0d4a7e06..194fedc715fa 100644
--- a/drivers/staging/vt6655/key.c
+++ b/drivers/staging/vt6655/key.c
@@ -372,7 +372,7 @@ bool KeybRemoveKey (
int i;
if (is_broadcast_ether_addr(pbyBSSID)) {
- // dealte all key
+ // delete all keys
if ((dwKeyIndex & PAIRWISE_KEY) != 0) {
for (i=0;i<MAX_KEY_TABLE;i++) {
pTable->KeyTable[i].PairwiseKey.bKeyValid = false;
diff --git a/drivers/staging/vt6655/mac.c b/drivers/staging/vt6655/mac.c
index f8d1651341f8..30c261579412 100644
--- a/drivers/staging/vt6655/mac.c
+++ b/drivers/staging/vt6655/mac.c
@@ -56,9 +56,9 @@
* MACbSafeStop - Stop MAC function
* MACbShutdown - Shut down MAC
* MACvInitialize - Initialize MAC
- * MACvSetCurrRxDescAddr - Set Rx Descriptos Address
- * MACvSetCurrTx0DescAddr - Set Tx0 Descriptos Address
- * MACvSetCurrTx1DescAddr - Set Tx1 Descriptos Address
+ * MACvSetCurrRxDescAddr - Set Rx Descriptors Address
+ * MACvSetCurrTx0DescAddr - Set Tx0 Descriptors Address
+ * MACvSetCurrTx1DescAddr - Set Tx1 Descriptors Address
* MACvTimer0MicroSDelay - Micro Second Delay Loop by MAC
*
* Revision History:
@@ -1498,7 +1498,7 @@ int ii;
wOffset += (uKeyIdx * 4);
for (ii=0;ii<4;ii++) {
- // alway push 128 bits
+ // always push 128 bits
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"3.(%d) wOffset: %d, Data: %lX\n", ii, wOffset+ii, *pdwKey);
VNSvOutPortW(dwIoBase + MAC_REG_MISCFFNDEX, wOffset+ii);
VNSvOutPortD(dwIoBase + MAC_REG_MISCFFDATA, *pdwKey++);
@@ -1567,7 +1567,7 @@ int ii;
wOffset++;
wOffset++;
wOffset += (uKeyIdx * 4);
- // alway push 128 bits
+ // always push 128 bits
for (ii=0; ii<3; ii++) {
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"(%d) wOffset: %d, Data: %lX\n", ii, wOffset+ii, *pdwKey);
VNSvOutPortW(dwIoBase + MAC_REG_MISCFFNDEX, wOffset+ii);
@@ -1696,7 +1696,7 @@ int ii;
wOffset += (uKeyIdx * 4);
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"1. wOffset: %d, Data: %lX, idx:%d\n", wOffset, *pdwKey, uKeyIdx);
- // alway push 128 bits
+ // always push 128 bits
for (ii=0; ii<4; ii++) {
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"2.(%d) wOffset: %d, Data: %lX\n", ii, wOffset+ii, *pdwKey);
VNSvOutPortW(dwIoBase + MAC_REG_MISCFFNDEX, wOffset+ii);
diff --git a/drivers/staging/vt6655/mac.h b/drivers/staging/vt6655/mac.h
index e3ccfee90268..adfb366f4901 100644
--- a/drivers/staging/vt6655/mac.h
+++ b/drivers/staging/vt6655/mac.h
@@ -586,7 +586,7 @@
#define PKT_TYPE_NONE 0x00 // turn off receiver
#define PKT_TYPE_ALL_MULTICAST 0x80
#define PKT_TYPE_PROMISCUOUS 0x40
-#define PKT_TYPE_DIRECTED 0x20 // obselete, directed address is always accepted
+#define PKT_TYPE_DIRECTED 0x20 // obsolete, directed address is always accepted
#define PKT_TYPE_BROADCAST 0x10
#define PKT_TYPE_MULTICAST 0x08
#define PKT_TYPE_ERROR_WPA 0x04
diff --git a/drivers/staging/vt6655/mib.c b/drivers/staging/vt6655/mib.c
index 1b91a8370954..63ae4adddf2f 100644
--- a/drivers/staging/vt6655/mib.c
+++ b/drivers/staging/vt6655/mib.c
@@ -191,7 +191,7 @@ void STAvUpdateRDStatCounter (PSStatCounter pStatistic,
pStatistic->ullRsrOK++;
if (cbFrameLength >= ETH_ALEN) {
- // update counters in case that successful transmit
+ // update counters in case of successful transmit
if (byRSR & RSR_ADDRBROAD) {
pStatistic->ullRxBroadcastFrames++;
pStatistic->ullRxBroadcastBytes += (unsigned long long) cbFrameLength;
diff --git a/drivers/staging/vt6655/power.c b/drivers/staging/vt6655/power.c
index 4c0b02e8f0b5..661d534304cc 100644
--- a/drivers/staging/vt6655/power.c
+++ b/drivers/staging/vt6655/power.c
@@ -207,7 +207,7 @@ PSbConsiderPowerDown(
if (pDevice->bCmdRunning)
return false;
- // Froce PSEN on
+ // Force PSEN on
MACvRegBitsOn(pDevice->PortOffset, MAC_REG_PSCTL, PSCTL_PSEN);
// check if all TD are empty,
diff --git a/drivers/staging/vt6655/rf.c b/drivers/staging/vt6655/rf.c
index aa696650b86c..aaa231aaf4e9 100644
--- a/drivers/staging/vt6655/rf.c
+++ b/drivers/staging/vt6655/rf.c
@@ -26,7 +26,7 @@
* Date: Feb. 19, 2004
*
* Functions:
- * IFRFbWriteEmbeded - Embeded write RF register via MAC
+ * IFRFbWriteEmbedded - Embedded write RF register via MAC
*
* Revision History:
*
@@ -453,18 +453,18 @@ bool s_bAL7230Init (unsigned long dwIoBase)
BBvPowerSaveModeOFF(dwIoBase); //RobertYu:20050106, have DC value for Calibration
for (ii = 0; ii < CB_AL7230_INIT_SEQ; ii++)
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTable[ii]);
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTable[ii]);
// PLL On
MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3);
//Calibration
MACvTimer0MicroSDelay(dwIoBase, 150);//150us
- bResult &= IFRFbWriteEmbeded(dwIoBase, (0x9ABA8F00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW)); //TXDCOC:active, RCK:diable
+ bResult &= IFRFbWriteEmbedded(dwIoBase, (0x9ABA8F00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW)); //TXDCOC:active, RCK:disable
MACvTimer0MicroSDelay(dwIoBase, 30);//30us
- bResult &= IFRFbWriteEmbeded(dwIoBase, (0x3ABA8F00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW)); //TXDCOC:diable, RCK:active
+ bResult &= IFRFbWriteEmbedded(dwIoBase, (0x3ABA8F00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW)); //TXDCOC:disable, RCK:active
MACvTimer0MicroSDelay(dwIoBase, 30);//30us
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTable[CB_AL7230_INIT_SEQ-1]); //TXDCOC:diable, RCK:diable
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTable[CB_AL7230_INIT_SEQ-1]); //TXDCOC:disable, RCK:disable
MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, (SOFTPWRCTL_SWPE3 |
SOFTPWRCTL_SWPE2 |
@@ -490,9 +490,9 @@ bool s_bAL7230SelectChannel (unsigned long dwIoBase, unsigned char byChannel)
// PLLON Off
MACvWordRegBitsOff(dwIoBase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3);
- bResult &= IFRFbWriteEmbeded (dwIoBase, dwAL7230ChannelTable0[byChannel-1]); //Reg0
- bResult &= IFRFbWriteEmbeded (dwIoBase, dwAL7230ChannelTable1[byChannel-1]); //Reg1
- bResult &= IFRFbWriteEmbeded (dwIoBase, dwAL7230ChannelTable2[byChannel-1]); //Reg4
+ bResult &= IFRFbWriteEmbedded (dwIoBase, dwAL7230ChannelTable0[byChannel-1]); //Reg0
+ bResult &= IFRFbWriteEmbedded (dwIoBase, dwAL7230ChannelTable1[byChannel-1]); //Reg1
+ bResult &= IFRFbWriteEmbedded (dwIoBase, dwAL7230ChannelTable2[byChannel-1]); //Reg4
// PLLOn On
MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3);
@@ -574,7 +574,7 @@ bool s_bAL7230SelectChannel (unsigned long dwIoBase, unsigned char byChannel)
/*--------------------- Export Functions --------------------------*/
/*
- * Description: Write to IF/RF, by embeded programming
+ * Description: Write to IF/RF, by embedded programming
*
* Parameters:
* In:
@@ -586,7 +586,7 @@ bool s_bAL7230SelectChannel (unsigned long dwIoBase, unsigned char byChannel)
* Return Value: true if succeeded; false if failed.
*
*/
-bool IFRFbWriteEmbeded (unsigned long dwIoBase, unsigned long dwData)
+bool IFRFbWriteEmbedded (unsigned long dwIoBase, unsigned long dwData)
{
unsigned short ww;
unsigned long dwValue;
@@ -669,11 +669,11 @@ bool RFbAL2230Init (unsigned long dwIoBase)
//patch abnormal AL2230 frequency output
//2008-8-21 chester <add>
- IFRFbWriteEmbeded(dwIoBase, (0x07168700+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW));
+ IFRFbWriteEmbedded(dwIoBase, (0x07168700+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW));
for (ii = 0; ii < CB_AL2230_INIT_SEQ; ii++)
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL2230InitTable[ii]);
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL2230InitTable[ii]);
//2008-8-21 chester <add>
MACvTimer0MicroSDelay(dwIoBase, 30); //delay 30 us
@@ -681,11 +681,11 @@ MACvTimer0MicroSDelay(dwIoBase, 30); //delay 30 us
MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3);
MACvTimer0MicroSDelay(dwIoBase, 150);//150us
- bResult &= IFRFbWriteEmbeded(dwIoBase, (0x00d80f00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW));
+ bResult &= IFRFbWriteEmbedded(dwIoBase, (0x00d80f00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW));
MACvTimer0MicroSDelay(dwIoBase, 30);//30us
- bResult &= IFRFbWriteEmbeded(dwIoBase, (0x00780f00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW));
+ bResult &= IFRFbWriteEmbedded(dwIoBase, (0x00780f00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW));
MACvTimer0MicroSDelay(dwIoBase, 30);//30us
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL2230InitTable[CB_AL2230_INIT_SEQ-1]);
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL2230InitTable[CB_AL2230_INIT_SEQ-1]);
MACvWordRegBitsOn(dwIoBase, MAC_REG_SOFTPWRCTL, (SOFTPWRCTL_SWPE3 |
SOFTPWRCTL_SWPE2 |
@@ -704,8 +704,8 @@ bool RFbAL2230SelectChannel (unsigned long dwIoBase, unsigned char byChannel)
bResult = true;
- bResult &= IFRFbWriteEmbeded (dwIoBase, dwAL2230ChannelTable0[byChannel-1]);
- bResult &= IFRFbWriteEmbeded (dwIoBase, dwAL2230ChannelTable1[byChannel-1]);
+ bResult &= IFRFbWriteEmbedded (dwIoBase, dwAL2230ChannelTable0[byChannel-1]);
+ bResult &= IFRFbWriteEmbedded (dwIoBase, dwAL2230ChannelTable1[byChannel-1]);
// Set Channel[7] = 0 to tell H/W channel is changing now.
VNSvOutPortB(dwIoBase + MAC_REG_CHANNEL, (byChannel & 0x7F));
@@ -817,7 +817,7 @@ bool bResult = true;
switch (pDevice->byRFType) {
case RF_AIROHA7230 :
- bResult = IFRFbWriteEmbeded (pDevice->PortOffset, 0x1ABAEF00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult = IFRFbWriteEmbedded (pDevice->PortOffset, 0x1ABAEF00+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW);
break;
default :
bResult = true;
@@ -1072,23 +1072,23 @@ unsigned long dwMax7230Pwr = 0;
switch (pDevice->byRFType) {
case RF_AIROHA :
- bResult &= IFRFbWriteEmbeded(pDevice->PortOffset, dwAL2230PowerTable[byPwr]);
+ bResult &= IFRFbWriteEmbedded(pDevice->PortOffset, dwAL2230PowerTable[byPwr]);
if (uRATE <= RATE_11M) {
- bResult &= IFRFbWriteEmbeded(pDevice->PortOffset, 0x0001B400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice->PortOffset, 0x0001B400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
} else {
- bResult &= IFRFbWriteEmbeded(pDevice->PortOffset, 0x0005A400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice->PortOffset, 0x0005A400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
}
break;
case RF_AL2230S :
- bResult &= IFRFbWriteEmbeded(pDevice->PortOffset, dwAL2230PowerTable[byPwr]);
+ bResult &= IFRFbWriteEmbedded(pDevice->PortOffset, dwAL2230PowerTable[byPwr]);
if (uRATE <= RATE_11M) {
- bResult &= IFRFbWriteEmbeded(pDevice->PortOffset, 0x040C1400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
- bResult &= IFRFbWriteEmbeded(pDevice->PortOffset, 0x00299B00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice->PortOffset, 0x040C1400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice->PortOffset, 0x00299B00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
}else {
- bResult &= IFRFbWriteEmbeded(pDevice->PortOffset, 0x0005A400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
- bResult &= IFRFbWriteEmbeded(pDevice->PortOffset, 0x00099B00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice->PortOffset, 0x0005A400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice->PortOffset, 0x00099B00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
}
break;
@@ -1098,7 +1098,7 @@ unsigned long dwMax7230Pwr = 0;
dwMax7230Pwr = 0x080C0B00 | ( (byPwr) << 12 ) |
(BY_AL7230_REG_LEN << 3 ) | IFREGCTL_REGW;
- bResult &= IFRFbWriteEmbeded(pDevice->PortOffset, dwMax7230Pwr);
+ bResult &= IFRFbWriteEmbedded(pDevice->PortOffset, dwMax7230Pwr);
break;
@@ -1166,24 +1166,24 @@ bool RFbAL7230SelectChannelPostProcess (unsigned long dwIoBase, unsigned char by
if( (byOldChannel <= CB_MAX_CHANNEL_24G) && (byNewChannel > CB_MAX_CHANNEL_24G) )
{
// Change from 2.4G to 5G
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTableAMode[2]); //Reg2
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTableAMode[3]); //Reg3
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTableAMode[5]); //Reg5
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTableAMode[7]); //Reg7
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTableAMode[10]);//Reg10
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTableAMode[12]);//Reg12
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTableAMode[15]);//Reg15
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTableAMode[2]); //Reg2
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTableAMode[3]); //Reg3
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTableAMode[5]); //Reg5
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTableAMode[7]); //Reg7
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTableAMode[10]);//Reg10
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTableAMode[12]);//Reg12
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTableAMode[15]);//Reg15
}
else if( (byOldChannel > CB_MAX_CHANNEL_24G) && (byNewChannel <= CB_MAX_CHANNEL_24G) )
{
// change from 5G to 2.4G
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTable[2]); //Reg2
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTable[3]); //Reg3
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTable[5]); //Reg5
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTable[7]); //Reg7
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTable[10]);//Reg10
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTable[12]);//Reg12
- bResult &= IFRFbWriteEmbeded(dwIoBase, dwAL7230InitTable[15]);//Reg15
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTable[2]); //Reg2
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTable[3]); //Reg3
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTable[5]); //Reg5
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTable[7]); //Reg7
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTable[10]);//Reg10
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTable[12]);//Reg12
+ bResult &= IFRFbWriteEmbedded(dwIoBase, dwAL7230InitTable[15]);//Reg15
}
return bResult;
diff --git a/drivers/staging/vt6655/rf.h b/drivers/staging/vt6655/rf.h
index 73f09693ee7a..1da0fdeb2e1c 100644
--- a/drivers/staging/vt6655/rf.h
+++ b/drivers/staging/vt6655/rf.h
@@ -75,7 +75,7 @@
/*--------------------- Export Functions --------------------------*/
-bool IFRFbWriteEmbeded(unsigned long dwIoBase, unsigned long dwData);
+bool IFRFbWriteEmbedded(unsigned long dwIoBase, unsigned long dwData);
bool RFbSelectChannel(unsigned long dwIoBase, unsigned char byRFType, unsigned char byChannel);
bool RFbInit (
PSDevice pDevice
diff --git a/drivers/staging/vt6655/rxtx.c b/drivers/staging/vt6655/rxtx.c
index 6935b37d5444..4972e57845c2 100644
--- a/drivers/staging/vt6655/rxtx.c
+++ b/drivers/staging/vt6655/rxtx.c
@@ -27,7 +27,7 @@
* Functions:
* s_vGenerateTxParameter - Generate tx dma required parameter.
* vGenerateMACHeader - Translate 802.3 to 802.11 header
- * cbGetFragCount - Caculate fragment number count
+ * cbGetFragCount - Calculate fragment number count
* csBeacon_xmit - beacon tx function
* csMgmt_xmit - management tx function
* s_cbFillTxBufHead - fulfill tx dma buffer header
@@ -733,11 +733,11 @@ s_uFillDataHead (
if (byFBOption == AUTO_FB_NONE) {
PSTxDataHead_g pBuf = (PSTxDataHead_g)pTxDataHead;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField_a), (unsigned char *)&(pBuf->bySignalField_a)
);
pBuf->wTransmitLength_a = cpu_to_le16(wLen);
- BBvCaculateParameter(pDevice, cbFrameLength, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, cbFrameLength, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField_b), (unsigned char *)&(pBuf->bySignalField_b)
);
pBuf->wTransmitLength_b = cpu_to_le16(wLen);
@@ -759,11 +759,11 @@ s_uFillDataHead (
// Auto Fallback
PSTxDataHead_g_FB pBuf = (PSTxDataHead_g_FB)pTxDataHead;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField_a), (unsigned char *)&(pBuf->bySignalField_a)
);
pBuf->wTransmitLength_a = cpu_to_le16(wLen);
- BBvCaculateParameter(pDevice, cbFrameLength, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, cbFrameLength, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField_b), (unsigned char *)&(pBuf->bySignalField_b)
);
pBuf->wTransmitLength_b = cpu_to_le16(wLen);
@@ -788,7 +788,7 @@ s_uFillDataHead (
// Auto Fallback
PSTxDataHead_a_FB pBuf = (PSTxDataHead_a_FB)pTxDataHead;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField), (unsigned char *)&(pBuf->bySignalField)
);
pBuf->wTransmitLength = cpu_to_le16(wLen);
@@ -805,7 +805,7 @@ s_uFillDataHead (
} else {
PSTxDataHead_ab pBuf = (PSTxDataHead_ab)pTxDataHead;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField), (unsigned char *)&(pBuf->bySignalField)
);
pBuf->wTransmitLength = cpu_to_le16(wLen);
@@ -823,7 +823,7 @@ s_uFillDataHead (
else {
PSTxDataHead_ab pBuf = (PSTxDataHead_ab)pTxDataHead;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField), (unsigned char *)&(pBuf->bySignalField)
);
pBuf->wTransmitLength = cpu_to_le16(wLen);
@@ -871,11 +871,11 @@ s_vFillRTSHead (
if (byFBOption == AUTO_FB_NONE) {
PSRTS_g pBuf = (PSRTS_g)pvRTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField_b), (unsigned char *)&(pBuf->bySignalField_b)
);
pBuf->wTransmitLength_b = cpu_to_le16(wLen);
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField_a), (unsigned char *)&(pBuf->bySignalField_a)
);
pBuf->wTransmitLength_a = cpu_to_le16(wLen);
@@ -904,11 +904,11 @@ s_vFillRTSHead (
else {
PSRTS_g_FB pBuf = (PSRTS_g_FB)pvRTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField_b), (unsigned char *)&(pBuf->bySignalField_b)
);
pBuf->wTransmitLength_b = cpu_to_le16(wLen);
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField_a), (unsigned char *)&(pBuf->bySignalField_a)
);
pBuf->wTransmitLength_a = cpu_to_le16(wLen);
@@ -946,7 +946,7 @@ s_vFillRTSHead (
if (byFBOption == AUTO_FB_NONE) {
PSRTS_ab pBuf = (PSRTS_ab)pvRTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField), (unsigned char *)&(pBuf->bySignalField)
);
pBuf->wTransmitLength = cpu_to_le16(wLen);
@@ -975,7 +975,7 @@ s_vFillRTSHead (
else {
PSRTS_a_FB pBuf = (PSRTS_a_FB)pvRTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField), (unsigned char *)&(pBuf->bySignalField)
);
pBuf->wTransmitLength = cpu_to_le16(wLen);
@@ -1005,7 +1005,7 @@ s_vFillRTSHead (
else if (byPktType == PK_TYPE_11B) {
PSRTS_ab pBuf = (PSRTS_ab)pvRTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField), (unsigned char *)&(pBuf->bySignalField)
);
pBuf->wTransmitLength = cpu_to_le16(wLen);
@@ -1065,7 +1065,7 @@ s_vFillCTSHead (
// Auto Fall back
PSCTS_FB pBuf = (PSCTS_FB)pvCTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uCTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, uCTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField_b), (unsigned char *)&(pBuf->bySignalField_b)
);
@@ -1092,7 +1092,7 @@ s_vFillCTSHead (
} else { //if (byFBOption != AUTO_FB_NONE && uDMAIdx != TYPE_ATIMDMA && uDMAIdx != TYPE_BEACONDMA)
PSCTS pBuf = (PSCTS)pvCTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uCTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, uCTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(unsigned short *)&(wLen), (unsigned char *)&(pBuf->byServiceField_b), (unsigned char *)&(pBuf->bySignalField_b)
);
pBuf->wTransmitLength_b = cpu_to_le16(wLen);
@@ -2568,7 +2568,7 @@ CMD_STATUS csMgmt_xmit(PSDevice pDevice, PSTxMgmtPacket pPacket) {
if (bIsPSPOLL) {
// The MAC will automatically replace the Duration-field of MAC header by Duration-field
// of FIFO control header.
- // This will cause AID-field of PS-POLL packet be incorrect (Because PS-POLL's AID field is
+ // This will cause AID-field of PS-POLL packet to be incorrect (Because PS-POLL's AID field is
// in the same place of other packet's Duration-field).
// And it will cause Cisco-AP to issue Disassociation-packet
if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) {
@@ -2664,7 +2664,7 @@ CMD_STATUS csBeacon_xmit(PSDevice pDevice, PSTxMgmtPacket pPacket) {
wCurrentRate, false, 0, 0, 1, AUTO_FB_NONE));
}
- BBvCaculateParameter(pDevice, cbFrameSize, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameSize, wCurrentRate, byPktType,
(unsigned short *)&(wLen), (unsigned char *)&(pTxDataHead->byServiceField), (unsigned char *)&(pTxDataHead->bySignalField)
);
pTxDataHead->wTransmitLength = cpu_to_le16(wLen);
@@ -2860,7 +2860,7 @@ vDMA0_tx_80211(PSDevice pDevice, struct sk_buff *skb, unsigned char *pbMPDU, un
// SetPower will cause error power TX state for OFDM Date packet in TX buffer.
// 2004.11.11 Kyle -- Using OFDM power to tx MngPkt will decrease the connection capability.
- // And cmd timer will wait data pkt TX finish before scanning so it's OK
+ // And cmd timer will wait data pkt TX to finish before scanning so it's OK
// to set power here.
if (pDevice->pMgmt->eScanState != WMAC_NO_SCANNING) {
RFbSetPower(pDevice, wCurrentRate, pDevice->byCurrentCh);
@@ -2957,7 +2957,7 @@ vDMA0_tx_80211(PSDevice pDevice, struct sk_buff *skb, unsigned char *pbMPDU, un
pTxBufHead->wFragCtl |= cpu_to_le16((unsigned short)cbMacHdLen << 10);
// Notes:
- // Although spec says MMPDU can be fragmented; In most case,
+ // Although spec says MMPDU can be fragmented; In most casses,
// no one will send a MMPDU under fragmentation. With RTS may occur.
pDevice->bAES = false; //Set FRAGCTL_WEPTYP
diff --git a/drivers/staging/vt6655/tcrc.c b/drivers/staging/vt6655/tcrc.c
index f9c28bf8a6af..1313c4cd0860 100644
--- a/drivers/staging/vt6655/tcrc.c
+++ b/drivers/staging/vt6655/tcrc.c
@@ -18,7 +18,7 @@
*
* File: tcrc.c
*
- * Purpose: Implement functions to caculate CRC
+ * Purpose: Implement functions to calculate CRC
*
* Author: Tevin Chen
*
diff --git a/drivers/staging/vt6655/tcrc.h b/drivers/staging/vt6655/tcrc.h
index d0449855beb1..a2044212d1c9 100644
--- a/drivers/staging/vt6655/tcrc.h
+++ b/drivers/staging/vt6655/tcrc.h
@@ -18,7 +18,7 @@
*
* File: tcrc.h
*
- * Purpose: Implement functions to caculate CRC
+ * Purpose: Implement functions to calculate CRC
*
* Author: Tevin Chen
*
diff --git a/drivers/staging/vt6655/tether.c b/drivers/staging/vt6655/tether.c
index 1cf8508e407d..28554bf75549 100644
--- a/drivers/staging/vt6655/tether.c
+++ b/drivers/staging/vt6655/tether.c
@@ -25,7 +25,7 @@
* Date: May 21, 1996
*
* Functions:
- * ETHbyGetHashIndexByCrc32 - Caculate multicast hash value by CRC32
+ * ETHbyGetHashIndexByCrc32 - Calculate multicast hash value by CRC32
* ETHbIsBufferCrc32Ok - Check CRC value of the buffer if Ok or not
*
* Revision History:
@@ -50,7 +50,7 @@
/*
- * Description: Caculate multicast hash value by CRC32
+ * Description: Calculate multicast hash value by CRC32
*
* Parameters:
* In:
diff --git a/drivers/staging/vt6655/tkip.c b/drivers/staging/vt6655/tkip.c
index ed3eac17ae8d..f141ba1cbb9b 100644
--- a/drivers/staging/vt6655/tkip.c
+++ b/drivers/staging/vt6655/tkip.c
@@ -170,7 +170,7 @@ unsigned int rotr1(unsigned int a)
/*
- * Description: Caculate RC4Key fom TK, TA, and TSC
+ * Description: Calculate RC4Key fom TK, TA, and TSC
*
* Parameters:
* In:
diff --git a/drivers/staging/vt6655/vntwifi.c b/drivers/staging/vt6655/vntwifi.c
index d645ecd89417..62c44b87d310 100644
--- a/drivers/staging/vt6655/vntwifi.c
+++ b/drivers/staging/vt6655/vntwifi.c
@@ -60,7 +60,7 @@
* Parameters:
* In:
* pMgmtHandle - pointer to management object
- * eOPMode - Opreation Mode
+ * eOPMode - Operation Mode
* Out:
* none
*
diff --git a/drivers/staging/vt6655/wcmd.c b/drivers/staging/vt6655/wcmd.c
index 7b5b99c8cf14..94bd1fc42c93 100644
--- a/drivers/staging/vt6655/wcmd.c
+++ b/drivers/staging/vt6655/wcmd.c
@@ -121,7 +121,7 @@ vAdHocBeaconStop(PSDevice pDevice)
/*
* temporarily stop Beacon packet for AdHoc Server
- * if all of the following coditions are met:
+ * if all of the following conditions are met:
* (1) STA is in AdHoc mode
* (2) VT3253 is programmed as automatic Beacon Transmitting
* (3) One of the following conditions is met
@@ -812,8 +812,8 @@ printk("chester-abyDesireSSID=%s\n",((PWLAN_IE_SSID)pMgmt->abyDesireSSID)->abySS
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "sta ps tx fail \n");
}
pMgmt->sNodeDBTable[ii].wEnQueueCnt--;
- // check if sta ps enable, wait next pspoll
- // if sta ps disable, send all pending buffers.
+ // check if sta ps enabled, and wait next pspoll.
+ // if sta ps disable, then send all pending buffers.
if (pMgmt->sNodeDBTable[ii].bPSEnable)
break;
}
diff --git a/drivers/staging/vt6655/wmgr.c b/drivers/staging/vt6655/wmgr.c
index c46d51908ac0..b6f99ecbbeb5 100644
--- a/drivers/staging/vt6655/wmgr.c
+++ b/drivers/staging/vt6655/wmgr.c
@@ -26,8 +26,8 @@
* Date: May 8, 2002
*
* Functions:
- * nsMgrObjectInitial - Initialize Management Objet data structure
- * vMgrObjectReset - Reset Management Objet data structure
+ * nsMgrObjectInitial - Initialize Management Object data structure
+ * vMgrObjectReset - Reset Management Object data structure
* vMgrAssocBeginSta - Start associate function
* vMgrReAssocBeginSta - Start reassociate function
* vMgrDisassocBeginSta - Start disassociate function
@@ -54,7 +54,7 @@
* bMgrPrepareBeaconToSend - Prepare Beacon frame
* s_vMgrLogStatus - Log 802.11 Status
* vMgrRxManagePacket - Rcv management frame dispatch function
- * s_vMgrFormatTIM- Assember TIM field of beacon
+ * s_vMgrFormatTIM- Assembler TIM field of beacon
* vMgrTimerInit- Initial 1-sec and command call back funtions
*
* Revision History:
@@ -425,7 +425,7 @@ vMgrTimerInit(
/*+
*
* Routine Description:
- * Reset the management object structure.
+ * Reset the management object structure.
*
* Return Value:
* None.
@@ -1287,14 +1287,14 @@ s_vMgrRxAuthentication(
vMgrDecodeAuthen(&sFrame);
switch (cpu_to_le16((*(sFrame.pwAuthSequence )))){
case 1:
- //AP funciton
+ //AP function
s_vMgrRxAuthenSequence_1(pDevice,pMgmt, &sFrame);
break;
case 2:
s_vMgrRxAuthenSequence_2(pDevice, pMgmt, &sFrame);
break;
case 3:
- //AP funciton
+ //AP function
s_vMgrRxAuthenSequence_3(pDevice, pMgmt, &sFrame);
break;
case 4:
@@ -1923,7 +1923,7 @@ s_vMgrRxBeacon(
byIEChannel = sFrame.pDSParms->byCurrChannel;
}
if (byCurrChannel != byIEChannel) {
- // adjust channel info. bcs we rcv adjcent channel pakckets
+ // adjust channel info. bcs we rcv adjacent channel packets
bChannelHit = false;
byCurrChannel = byIEChannel;
}
@@ -2081,7 +2081,7 @@ if(ChannelExceedZoneType(pDevice,byCurrChannel)==true)
}
}
//
- // Basic Rate Set may change dynamiclly
+ // Basic Rate Set may change dynamically
//
if (pBSSList->eNetworkTypeInUse == PHY_TYPE_11B) {
uRateLen = WLAN_RATES_MAXLEN_11B;
@@ -2134,7 +2134,7 @@ if(ChannelExceedZoneType(pDevice,byCurrChannel)==true)
}
// DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"Beacon 2 \n");
- // check if CF field exisit
+ // check if CF field exists
if (WLAN_GET_CAP_INFO_ESS(*sFrame.pwCapInfo)) {
if (sFrame.pCFParms->wCFPDurRemaining > 0) {
// TODO: deal with CFP period to set NAV
@@ -2244,7 +2244,7 @@ if(ChannelExceedZoneType(pDevice,byCurrChannel)==true)
if (pMgmt->sNodeDBTable[0].uInActiveCount != 0)
pMgmt->sNodeDBTable[0].uInActiveCount = 0;
- // adhoc mode:TSF updated only when beacon larger then local TSF
+ // adhoc mode:TSF updated only when beacon larger than local TSF
if (bTSFLargeDiff && bTSFOffsetPostive &&
(pMgmt->eCurrState == WMAC_STATE_JOINTED))
bUpdateTSF = true;
@@ -2252,7 +2252,7 @@ if(ChannelExceedZoneType(pDevice,byCurrChannel)==true)
// During dpc, already in spinlocked.
if (BSSDBbIsSTAInNodeDB(pMgmt, sFrame.pHdr->sA3.abyAddr2, &uNodeIndex)) {
- // Update the STA, (Techically the Beacons of all the IBSS nodes
+ // Update the STA, (Technically the Beacons of all the IBSS nodes
// should be identical, but that's not happening in practice.
pMgmt->abyCurrSuppRates[1] = RATEuSetIE((PWLAN_IE_SUPP_RATES)sFrame.pSuppRates,
(PWLAN_IE_SUPP_RATES)pMgmt->abyCurrSuppRates,
@@ -2305,7 +2305,7 @@ if(ChannelExceedZoneType(pDevice,byCurrChannel)==true)
*/
}
- // if other stations jointed, indicate connect to upper layer..
+ // if other stations joined, indicate connection to upper layer..
if (pMgmt->eCurrState == WMAC_STATE_STARTED) {
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Current IBSS State: [Started]........to: [Jointed] \n");
pMgmt->eCurrState = WMAC_STATE_JOINTED;
@@ -3081,8 +3081,8 @@ s_vMgrSynchBSS (
// }
// }
// if( uSameBssidNum>=2) { //we only check AP in hidden sssid mode
- if ((pMgmt->eAuthenMode == WMAC_AUTH_WPAPSK) || //networkmanager 0.7.0 does not give the pairwise-key selsection,
- (pMgmt->eAuthenMode == WMAC_AUTH_WPA2PSK)) { // so we need re-selsect it according to real pairwise-key info.
+ if ((pMgmt->eAuthenMode == WMAC_AUTH_WPAPSK) || //networkmanager 0.7.0 does not give the pairwise-key selection,
+ (pMgmt->eAuthenMode == WMAC_AUTH_WPA2PSK)) { // so we need re-select it according to real pairwise-key info.
if(pCurr->bWPAValid == true) { //WPA-PSK
pMgmt->eAuthenMode = WMAC_AUTH_WPAPSK;
if(pCurr->abyPKType[0] == WPA_TKIP) {
@@ -3193,7 +3193,7 @@ s_vMgrFormatTIM(
*
*
* Return Value:
- * PTR to frame; or NULL on allocation failue
+ * PTR to frame; or NULL on allocation failure
*
-*/
@@ -3310,7 +3310,7 @@ s_MgrMakeBeacon(
*((unsigned short *)(sFrame.pBuf + sFrame.len + sFrame.pRSNWPA->len))=0;
sFrame.pRSNWPA->len +=2;
- // RSN Capabilites
+ // RSN Capabilities
*((unsigned short *)(sFrame.pBuf + sFrame.len + sFrame.pRSNWPA->len))=0;
sFrame.pRSNWPA->len +=2;
sFrame.len += sFrame.pRSNWPA->len + WLAN_IEHDR_LEN;
@@ -3420,7 +3420,7 @@ s_MgrMakeBeacon(
*
*
* Return Value:
- * PTR to frame; or NULL on allocation failue
+ * PTR to frame; or NULL on allocation failure
*
-*/
@@ -3611,7 +3611,7 @@ s_MgrMakeProbeResponse(
*
*
* Return Value:
- * A ptr to frame or NULL on allocation failue
+ * A ptr to frame or NULL on allocation failure
*
-*/
@@ -3652,7 +3652,7 @@ s_MgrMakeAssocRequest(
memcpy( sFrame.pHdr->sA3.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN);
memcpy( sFrame.pHdr->sA3.abyAddr3, pMgmt->abyCurrBSSID, WLAN_BSSID_LEN);
- // Set the capibility and listen interval
+ // Set the capability and listen interval
*(sFrame.pwCapInfo) = cpu_to_le16(wCurrCapInfo);
*(sFrame.pwListenInterval) = cpu_to_le16(wListenInterval);
@@ -3762,7 +3762,7 @@ s_MgrMakeAssocRequest(
sFrame.pRSNWPA->len +=6;
- // RSN Capabilites
+ // RSN Capabilities
*pbyRSN++=0x00;
*pbyRSN++=0x00;
@@ -3831,7 +3831,7 @@ s_MgrMakeAssocRequest(
}
sFrame.pRSN->len +=6;
- // RSN Capabilites
+ // RSN Capabilities
if (pMgmt->pCurrBSS->sRSNCapObj.bRSNCapExist == true) {
memcpy(&sFrame.pRSN->abyRSN[16], &pMgmt->pCurrBSS->sRSNCapObj.wRSNCap, 2);
} else {
@@ -3886,7 +3886,7 @@ s_MgrMakeAssocRequest(
*
*
* Return Value:
- * A ptr to frame or NULL on allocation failue
+ * A ptr to frame or NULL on allocation failure
*
-*/
@@ -3929,7 +3929,7 @@ s_MgrMakeReAssocRequest(
memcpy( sFrame.pHdr->sA3.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN);
memcpy( sFrame.pHdr->sA3.abyAddr3, pMgmt->abyCurrBSSID, WLAN_BSSID_LEN);
- /* Set the capibility and listen interval */
+ /* Set the capability and listen interval */
*(sFrame.pwCapInfo) = cpu_to_le16(wCurrCapInfo);
*(sFrame.pwListenInterval) = cpu_to_le16(wListenInterval);
@@ -4019,7 +4019,7 @@ s_MgrMakeReAssocRequest(
sFrame.pRSNWPA->len +=6;
- // RSN Capabilites
+ // RSN Capabilities
*pbyRSN++=0x00;
*pbyRSN++=0x00;
sFrame.pRSNWPA->len +=2;
@@ -4087,7 +4087,7 @@ s_MgrMakeReAssocRequest(
}
sFrame.pRSN->len +=6;
- // RSN Capabilites
+ // RSN Capabilities
if (pMgmt->pCurrBSS->sRSNCapObj.bRSNCapExist == true) {
memcpy(&sFrame.pRSN->abyRSN[16], &pMgmt->pCurrBSS->sRSNCapObj.wRSNCap, 2);
} else {
@@ -4138,7 +4138,7 @@ s_MgrMakeReAssocRequest(
*
*
* Return Value:
- * PTR to frame; or NULL on allocation failue
+ * PTR to frame; or NULL on allocation failure
*
-*/
@@ -4212,7 +4212,7 @@ s_MgrMakeAssocResponse(
*
*
* Return Value:
- * PTR to frame; or NULL on allocation failue
+ * PTR to frame; or NULL on allocation failure
*
-*/
@@ -4333,7 +4333,7 @@ s_vMgrRxProbeResponse(
byIEChannel = sFrame.pDSParms->byCurrChannel;
}
if (byCurrChannel != byIEChannel) {
- // adjust channel info. bcs we rcv adjcent channel pakckets
+ // adjust channel info. bcs we rcv adjacent channel packets
bChannelHit = false;
byCurrChannel = byIEChannel;
}
diff --git a/drivers/staging/vt6655/wmgr.h b/drivers/staging/vt6655/wmgr.h
index e3ae562f521a..bfa67ae5d40a 100644
--- a/drivers/staging/vt6655/wmgr.h
+++ b/drivers/staging/vt6655/wmgr.h
@@ -286,7 +286,7 @@ typedef struct tagSMgmtObject
CMD_STATE eCommandState;
unsigned int uScanChannel;
- // Desire joinning BSS vars
+ // Desire joining BSS vars
unsigned char abyDesireSSID[WLAN_IEHDR_LEN + WLAN_SSID_MAXLEN + 1];
unsigned char abyDesireBSSID[WLAN_BSSID_LEN];
@@ -310,7 +310,7 @@ typedef struct tagSMgmtObject
unsigned int uScanEndCh;
unsigned short wScanSteps;
unsigned int uScanBSSType;
- // Desire scannig vars
+ // Desire scanning vars
unsigned char abyScanSSID[WLAN_IEHDR_LEN + WLAN_SSID_MAXLEN + 1];
unsigned char abyScanBSSID[WLAN_BSSID_LEN];
diff --git a/drivers/staging/vt6655/wpa.c b/drivers/staging/vt6655/wpa.c
index 0afb9fe0379a..4412fe9396a4 100644
--- a/drivers/staging/vt6655/wpa.c
+++ b/drivers/staging/vt6655/wpa.c
@@ -229,7 +229,7 @@ WPA_ParseRSN (
* Parameters:
* In:
* byCmd - Search type
- * byEncrypt- Encrcypt Type
+ * byEncrypt- Encrypt Type
* pBSSList - BSS list
* Out:
* none
diff --git a/drivers/staging/vt6655/wpa2.c b/drivers/staging/vt6655/wpa2.c
index 744799cfe832..884db1abe123 100644
--- a/drivers/staging/vt6655/wpa2.c
+++ b/drivers/staging/vt6655/wpa2.c
@@ -175,16 +175,16 @@ WPA2vParseRSN (
pBSSNode->abyCSSPK[j++] = WLAN_11i_CSS_USE_GROUP;
bUseGK = true;
} else if ( !memcmp(pbyOUI, abyOUIWEP40, 4)) {
- // Invialid CSS, continue to parsing
+ // Invalid CSS, continue to parsing
} else if ( !memcmp(pbyOUI, abyOUITKIP, 4)) {
if (pBSSNode->byCSSGK != WLAN_11i_CSS_CCMP)
pBSSNode->abyCSSPK[j++] = WLAN_11i_CSS_TKIP;
else
- ; // Invialid CSS, continue to parsing
+ ; // Invalid CSS, continue to parsing
} else if ( !memcmp(pbyOUI, abyOUICCMP, 4)) {
pBSSNode->abyCSSPK[j++] = WLAN_11i_CSS_CCMP;
} else if ( !memcmp(pbyOUI, abyOUIWEP104, 4)) {
- // Invialid CSS, continue to parsing
+ // Invalid CSS, continue to parsing
} else {
// any vendor checks here
pBSSNode->abyCSSPK[j++] = WLAN_11i_CSS_UNKNOWN;
@@ -329,7 +329,7 @@ WPA2uSetIEs(
}
pRSNIEs->len +=6;
- // RSN Capabilites
+ // RSN Capabilities
if (pMgmt->pCurrBSS->sRSNCapObj.bRSNCapExist == true) {
memcpy(&pRSNIEs->abyRSN[16], &pMgmt->pCurrBSS->sRSNCapObj.wRSNCap, 2);
} else {
diff --git a/drivers/staging/vt6655/wpactl.c b/drivers/staging/vt6655/wpactl.c
index 732ba88dc796..2b6ae1e403bf 100644
--- a/drivers/staging/vt6655/wpactl.c
+++ b/drivers/staging/vt6655/wpactl.c
@@ -77,7 +77,7 @@ static void wpadev_setup(struct net_device *dev)
/*
* Description:
- * register netdev for wpa supplicant deamon
+ * register netdev for wpa supplicant daemon
*
* Parameters:
* In:
@@ -164,7 +164,7 @@ static int wpa_release_wpadev(PSDevice pDevice)
/*
* Description:
- * Set enable/disable dev for wpa supplicant deamon
+ * Set enable/disable dev for wpa supplicant daemon
*
* Parameters:
* In:
@@ -847,7 +847,7 @@ else
if(!bWepEnabled) pDevice->eEncryptionStatus = Ndis802_11EncryptionDisabled;
else pDevice->eEncryptionStatus = Ndis802_11Encryption1Enabled;
//pMgmt->eAuthenMode = WMAC_AUTH_OPEN;
- //pMgmt->bShareKeyAlgorithm = false; //20080717-06,<Modify> by chester//Fix Open mode, WEP encrytion
+ //pMgmt->bShareKeyAlgorithm = false; //20080717-06,<Modify> by chester//Fix Open mode, WEP encryption
}
//mike save old encryption status
pDevice->eOldEncryptionStatus = pDevice->eEncryptionStatus;
diff --git a/drivers/staging/vt6655/wroute.c b/drivers/staging/vt6655/wroute.c
index 66e2eeae628b..82e93cb82053 100644
--- a/drivers/staging/vt6655/wroute.c
+++ b/drivers/staging/vt6655/wroute.c
@@ -18,7 +18,7 @@
*
* File: wroute.c
*
- * Purpose: handle WMAC frame relay & filterring
+ * Purpose: handle WMAC frame relay & filtering
*
* Author: Lyndon Chen
*
diff --git a/drivers/staging/vt6656/80211mgr.h b/drivers/staging/vt6656/80211mgr.h
index 515b9c1d4d10..e5db73be0e71 100644
--- a/drivers/staging/vt6656/80211mgr.h
+++ b/drivers/staging/vt6656/80211mgr.h
@@ -191,7 +191,7 @@
//
-// Cipher Suite Selectors defiened in 802.11i
+// Cipher Suite Selectors defined in 802.11i
//
#define WLAN_11i_CSS_USE_GROUP 0
#define WLAN_11i_CSS_WEP40 1
@@ -720,7 +720,7 @@ typedef struct tagWLAN_FR_AUTHEN {
} WLAN_FR_AUTHEN, *PWLAN_FR_AUTHEN;
-// Deauthenication
+// Deauthentication
typedef struct tagWLAN_FR_DEAUTHEN {
unsigned int uType;
diff --git a/drivers/staging/vt6656/baseband.c b/drivers/staging/vt6656/baseband.c
index 06f27f624db4..385501595b4d 100644
--- a/drivers/staging/vt6656/baseband.c
+++ b/drivers/staging/vt6656/baseband.c
@@ -27,7 +27,7 @@
*
* Functions:
* BBuGetFrameTime - Calculate data frame transmitting time
- * BBvCaculateParameter - Caculate PhyLength, PhyService and Phy Signal parameter for baseband Tx
+ * BBvCalculateParameter - Calculate PhyLength, PhyService and Phy Signal parameter for baseband Tx
* BBbVT3184Init - VIA VT3184 baseband chip init code
* BBvLoopbackOn - Turn on BaseBand Loopback mode
* BBvLoopbackOff - Turn off BaseBand Loopback mode
@@ -741,7 +741,7 @@ BBuGetFrameTime (
}
/*
- * Description: Caculate Length, Service, and Signal fields of Phy for Tx
+ * Description: Calculate Length, Service, and Signal fields of Phy for Tx
*
* Parameters:
* In:
@@ -757,7 +757,7 @@ BBuGetFrameTime (
*
*/
void
-BBvCaculateParameter (
+BBvCalculateParameter (
PSDevice pDevice,
unsigned int cbFrameLength,
WORD wRate,
diff --git a/drivers/staging/vt6656/baseband.h b/drivers/staging/vt6656/baseband.h
index 8db8cd07d5f5..844d5a8b13e5 100644
--- a/drivers/staging/vt6656/baseband.h
+++ b/drivers/staging/vt6656/baseband.h
@@ -104,7 +104,7 @@ BBuGetFrameTime(
WORD wRate
);
-void BBvCaculateParameter(PSDevice pDevice,
+void BBvCalculateParameter(PSDevice pDevice,
unsigned int cbFrameLength,
WORD wRate,
BYTE byPacketType,
diff --git a/drivers/staging/vt6656/bssdb.c b/drivers/staging/vt6656/bssdb.c
index 099936771e69..2ac066df8340 100644
--- a/drivers/staging/vt6656/bssdb.c
+++ b/drivers/staging/vt6656/bssdb.c
@@ -226,7 +226,7 @@ PKnownBSS BSSpSearchBSSList(void *hDeviceContext,
if (pSelect == NULL) {
pSelect = pCurrBSS;
} else {
- // compare RSSI, select signal strong one
+ // compare RSSI, select the strongest signal
if (pCurrBSS->uRSSI < pSelect->uRSSI) {
pSelect = pCurrBSS;
}
@@ -274,9 +274,9 @@ void BSSvClearBSSList(void *hDeviceContext, BOOL bKeepCurrBSSID)
if (pMgmt->sBSSList[ii].bActive &&
!compare_ether_addr(pMgmt->sBSSList[ii].abyBSSID,
pMgmt->abyCurrBSSID)) {
- //mike mark: there are two same BSSID in list if that AP is in hidden ssid mode,one 's SSID is null,
- // but other's is obvious, so if it acssociate with your STA exactly,you must keep two
- // of them!!!!!!!!!
+ //mike mark: there are two BSSID's in list. If that AP is in hidden ssid mode, one SSID is null,
+ // but other's might not be obvious, so if it associate's with your STA,
+ // you must keep the two of them!!
// bKeepCurrBSSID = FALSE;
continue;
}
@@ -489,7 +489,7 @@ BOOL BSSbInsertToBSSList(void *hDeviceContext,
}
if (pDevice->bUpdateBBVGA) {
- // Moniter if RSSI is too strong.
+ // Monitor if RSSI is too strong.
pBSSList->byRSSIStatCnt = 0;
RFvRSSITodBm(pDevice, (BYTE)(pRxPacket->uRSSI), &pBSSList->ldBmMAX);
pBSSList->ldBmAverage[0] = pBSSList->ldBmMAX;
@@ -621,7 +621,7 @@ BOOL BSSbUpdateToBSSList(void *hDeviceContext,
if (pRxPacket->uRSSI != 0) {
RFvRSSITodBm(pDevice, (BYTE)(pRxPacket->uRSSI), &ldBm);
- // Moniter if RSSI is too strong.
+ // Monitor if RSSI is too strong.
pBSSList->byRSSIStatCnt++;
pBSSList->byRSSIStatCnt %= RSSI_STAT_COUNT;
pBSSList->ldBmAverage[pBSSList->byRSSIStatCnt] = ldBm;
@@ -687,8 +687,8 @@ BOOL BSSbIsSTAInNodeDB(void *hDeviceContext,
/*+
*
* Routine Description:
- * Find an empty node and allocated; if no empty found,
- * instand used of most inactive one.
+ * Find an empty node and allocate it; if no empty node
+ * is found, then use the most inactive one.
*
* Return Value:
* None
@@ -718,7 +718,7 @@ void BSSvCreateOneNode(void *hDeviceContext, unsigned int *puNodeIndex)
}
}
- // if not found replace uInActiveCount is largest one.
+ // if not found replace uInActiveCount with the largest one.
if ( ii == (MAX_NODE_NUM + 1)) {
*puNodeIndex = SelectIndex;
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Replace inactive node = %d\n", SelectIndex);
diff --git a/drivers/staging/vt6656/card.c b/drivers/staging/vt6656/card.c
index e3ddc0b55317..826520b03383 100644
--- a/drivers/staging/vt6656/card.c
+++ b/drivers/staging/vt6656/card.c
@@ -28,9 +28,9 @@
* CARDbIsOFDMinBasicRate - Check if any OFDM rate is in BasicRateSet
* CARDvSetLoopbackMode - Set Loopback mode
* CARDbSoftwareReset - Sortware reset NIC
- * CARDqGetTSFOffset - Caculate TSFOffset
+ * CARDqGetTSFOffset - Calculate TSFOffset
* CARDbGetCurrentTSF - Read Current NIC TSF counter
- * CARDqGetNextTBTT - Caculate Next Beacon TSF counter
+ * CARDqGetNextTBTT - Calculate Next Beacon TSF counter
* CARDvSetFirstNextTBTT - Set NIC Beacon time
* CARDvUpdateNextTBTT - Sync. NIC Beacon time
* CARDbRadioPowerOff - Turn Off NIC Radio Power
@@ -40,7 +40,7 @@
*
* Revision History:
* 06-10-2003 Bryan YC Fan: Re-write codes to support VT3253 spec.
- * 08-26-2003 Kyle Hsu: Modify the defination type of dwIoBase.
+ * 08-26-2003 Kyle Hsu: Modify the definition type of dwIoBase.
* 09-01-2003 Bryan YC Fan: Add vUpdateIFS().
*
*/
@@ -200,7 +200,7 @@ static WORD swGetOFDMControlRate(void *pDeviceHandler, WORD wRateIdx)
}
/*
- * Description: Caculate TxRate and RsvTime fields for RSPINF in OFDM mode.
+ * Description: Calculate TxRate and RsvTime fields for RSPINF in OFDM mode.
*
* Parameters:
* In:
@@ -214,7 +214,7 @@ static WORD swGetOFDMControlRate(void *pDeviceHandler, WORD wRateIdx)
*
*/
void
-CARDvCaculateOFDMRParameter (
+CARDvCalculateOFDMRParameter (
WORD wRate,
BYTE byBBType,
PBYTE pbyTxRate,
@@ -337,7 +337,7 @@ void CARDvSetRSPINF(void *pDeviceHandler, BYTE byBBType)
int i;
//RSPINF_b_1
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
swGetCCKControlRate(pDevice, RATE_1M),
PK_TYPE_11B,
@@ -347,7 +347,7 @@ void CARDvSetRSPINF(void *pDeviceHandler, BYTE byBBType)
);
///RSPINF_b_2
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
swGetCCKControlRate(pDevice, RATE_2M),
PK_TYPE_11B,
@@ -357,7 +357,7 @@ void CARDvSetRSPINF(void *pDeviceHandler, BYTE byBBType)
);
//RSPINF_b_5
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
swGetCCKControlRate(pDevice, RATE_5M),
PK_TYPE_11B,
@@ -367,7 +367,7 @@ void CARDvSetRSPINF(void *pDeviceHandler, BYTE byBBType)
);
//RSPINF_b_11
- BBvCaculateParameter(pDevice,
+ BBvCalculateParameter(pDevice,
14,
swGetCCKControlRate(pDevice, RATE_11M),
PK_TYPE_11B,
@@ -377,55 +377,55 @@ void CARDvSetRSPINF(void *pDeviceHandler, BYTE byBBType)
);
//RSPINF_a_6
- CARDvCaculateOFDMRParameter (RATE_6M,
+ CARDvCalculateOFDMRParameter (RATE_6M,
byBBType,
&abyTxRate[0],
&abyRsvTime[0]);
//RSPINF_a_9
- CARDvCaculateOFDMRParameter (RATE_9M,
+ CARDvCalculateOFDMRParameter (RATE_9M,
byBBType,
&abyTxRate[1],
&abyRsvTime[1]);
//RSPINF_a_12
- CARDvCaculateOFDMRParameter (RATE_12M,
+ CARDvCalculateOFDMRParameter (RATE_12M,
byBBType,
&abyTxRate[2],
&abyRsvTime[2]);
//RSPINF_a_18
- CARDvCaculateOFDMRParameter (RATE_18M,
+ CARDvCalculateOFDMRParameter (RATE_18M,
byBBType,
&abyTxRate[3],
&abyRsvTime[3]);
//RSPINF_a_24
- CARDvCaculateOFDMRParameter (RATE_24M,
+ CARDvCalculateOFDMRParameter (RATE_24M,
byBBType,
&abyTxRate[4],
&abyRsvTime[4]);
//RSPINF_a_36
- CARDvCaculateOFDMRParameter (swGetOFDMControlRate(pDevice, RATE_36M),
+ CARDvCalculateOFDMRParameter (swGetOFDMControlRate(pDevice, RATE_36M),
byBBType,
&abyTxRate[5],
&abyRsvTime[5]);
//RSPINF_a_48
- CARDvCaculateOFDMRParameter (swGetOFDMControlRate(pDevice, RATE_48M),
+ CARDvCalculateOFDMRParameter (swGetOFDMControlRate(pDevice, RATE_48M),
byBBType,
&abyTxRate[6],
&abyRsvTime[6]);
//RSPINF_a_54
- CARDvCaculateOFDMRParameter (swGetOFDMControlRate(pDevice, RATE_54M),
+ CARDvCalculateOFDMRParameter (swGetOFDMControlRate(pDevice, RATE_54M),
byBBType,
&abyTxRate[7],
&abyRsvTime[7]);
//RSPINF_a_72
- CARDvCaculateOFDMRParameter (swGetOFDMControlRate(pDevice, RATE_54M),
+ CARDvCalculateOFDMRParameter (swGetOFDMControlRate(pDevice, RATE_54M),
byBBType,
&abyTxRate[8],
&abyRsvTime[8]);
@@ -640,7 +640,7 @@ BYTE CARDbyGetPktType(void *pDeviceHandler)
/*
- * Description: Caculate TSF offset of two TSF input
+ * Description: Calculate TSF offset of two TSF input
* Get TSF Offset from RxBCN's TSF and local TSF
*
* Parameters:
diff --git a/drivers/staging/vt6656/device.h b/drivers/staging/vt6656/device.h
index 171dd68cf5b2..6370d1039103 100644
--- a/drivers/staging/vt6656/device.h
+++ b/drivers/staging/vt6656/device.h
@@ -478,7 +478,7 @@ typedef struct __device_info {
unsigned int cbTD;
//
- // Variables to track resources for the Interript In Pipe
+ // Variables to track resources for the Interrupt In Pipe
//
INT_BUFFER intBuf;
BOOL fKillEventPollingThread;
diff --git a/drivers/staging/vt6656/dpc.c b/drivers/staging/vt6656/dpc.c
index 3aa895ec6507..28edf9e7efcb 100644
--- a/drivers/staging/vt6656/dpc.c
+++ b/drivers/staging/vt6656/dpc.c
@@ -748,7 +748,7 @@ RXbBulkInProcessData (
if ((*pbyRSSI != 0) &&
(pMgmt->pCurrBSS!=NULL)) {
RFvRSSITodBm(pDevice, *pbyRSSI, &ldBm);
- // Moniter if RSSI is too strong.
+ // Monitor if RSSI is too strong.
pMgmt->pCurrBSS->byRSSIStatCnt++;
pMgmt->pCurrBSS->byRSSIStatCnt %= RSSI_STAT_COUNT;
pMgmt->pCurrBSS->ldBmAverage[pMgmt->pCurrBSS->byRSSIStatCnt] = ldBm;
diff --git a/drivers/staging/vt6656/hostap.c b/drivers/staging/vt6656/hostap.c
index 682002a5b8d7..0a73d4060ee1 100644
--- a/drivers/staging/vt6656/hostap.c
+++ b/drivers/staging/vt6656/hostap.c
@@ -18,7 +18,7 @@
*
* File: hostap.c
*
- * Purpose: handle hostap deamon ioctl input/out functions
+ * Purpose: handle hostap daemon ioctl input/out functions
*
* Author: Lyndon Chen
*
@@ -48,7 +48,7 @@ static int msglevel =MSG_LEVEL_INFO;
/*
* Description:
- * register net_device (AP) for hostap deamon
+ * register net_device (AP) for hostap daemon
*
* Parameters:
* In:
@@ -176,7 +176,7 @@ int vt6656_hostap_set_hostapd(PSDevice pDevice, int val, int rtnl_locked)
/*
* Description:
- * remove station function supported for hostap deamon
+ * remove station function supported for hostap daemon
*
* Parameters:
* In:
@@ -204,7 +204,7 @@ static int hostap_remove_sta(PSDevice pDevice,
/*
* Description:
- * add a station from hostap deamon
+ * add a station from hostap daemon
*
* Parameters:
* In:
@@ -439,9 +439,7 @@ static int hostap_set_encryption(PSDevice pDevice,
return -EINVAL;
}
- if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
- param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
- param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (is_broadcast_ether_addr(param->sta_addr)) {
if (param->u.crypt.idx >= MAX_GROUP_KEY)
return -EINVAL;
iNodeIndex = 0;
@@ -663,9 +661,7 @@ static int hostap_get_encryption(PSDevice pDevice,
param->u.crypt.err = 0;
- if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff &&
- param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff &&
- param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) {
+ if (is_broadcast_ether_addr(param->sta_addr)) {
iNodeIndex = 0;
} else {
if (BSSbIsSTAInNodeDB(pDevice, param->sta_addr, &iNodeIndex) == FALSE) {
@@ -686,7 +682,7 @@ static int hostap_get_encryption(PSDevice pDevice,
/*
* Description:
- * vt6656_hostap_ioctl main function supported for hostap deamon.
+ * vt6656_hostap_ioctl main function supported for hostap daemon.
*
* Parameters:
* In:
@@ -732,8 +728,8 @@ int vt6656_hostap_ioctl(PSDevice pDevice, struct iw_point *p)
break;
case VIAWGET_HOSTAPD_SET_ASSOC_AP_ADDR:
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "VIAWGET_HOSTAPD_SET_ASSOC_AP_ADDR \n");
- return -EOPNOTSUPP;
- break;
+ ret = -EOPNOTSUPP;
+ goto out;
case VIAWGET_HOSTAPD_FLUSH:
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "VIAWGET_HOSTAPD_FLUSH \n");
spin_lock_irq(&pDevice->lock);
@@ -777,13 +773,13 @@ int vt6656_hostap_ioctl(PSDevice pDevice, struct iw_point *p)
case VIAWGET_HOSTAPD_STA_CLEAR_STATS:
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "VIAWGET_HOSTAPD_STA_CLEAR_STATS \n");
- return -EOPNOTSUPP;
-
+ ret = -EOPNOTSUPP;
+ goto out;
default:
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "vt6656_hostap_ioctl: unknown cmd=%d\n",
(int)param->cmd);
- return -EOPNOTSUPP;
- break;
+ ret = -EOPNOTSUPP;
+ goto out;
}
diff --git a/drivers/staging/vt6656/int.c b/drivers/staging/vt6656/int.c
index eba4b5061cf7..bba31caae036 100644
--- a/drivers/staging/vt6656/int.c
+++ b/drivers/staging/vt6656/int.c
@@ -149,7 +149,7 @@ void INTnsProcessData(PSDevice pDevice)
pMgmt->sNodeDBTable[0].bRxPSPoll =
FALSE;
} else if (pMgmt->byDTIMCount == 0) {
- /* check if mutltcast tx bufferring */
+ /* check if multicast tx buffering */
pMgmt->byDTIMCount =
pMgmt->byDTIMPeriod-1;
pMgmt->sNodeDBTable[0].bRxPSPoll = TRUE;
diff --git a/drivers/staging/vt6656/ioctl.c b/drivers/staging/vt6656/ioctl.c
index 5b9a84f95185..b6af5f691128 100644
--- a/drivers/staging/vt6656/ioctl.c
+++ b/drivers/staging/vt6656/ioctl.c
@@ -526,11 +526,8 @@ int private_ioctl(PSDevice pDevice, struct ifreq *rq)
pMgmt->abyIBSSSuppRates[3] |= BIT7;
}
- DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Support Rate= %x %x %x %x\n",
- pMgmt->abyIBSSSuppRates[2],
- pMgmt->abyIBSSSuppRates[3],
- pMgmt->abyIBSSSuppRates[4],
- pMgmt->abyIBSSSuppRates[5]);
+ DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Support Rate= %*ph\n",
+ 4, pMgmt->abyIBSSSuppRates + 2);
netif_stop_queue(pDevice->dev);
spin_lock_irq(&pDevice->lock);
@@ -620,7 +617,7 @@ int private_ioctl(PSDevice pDevice, struct ifreq *rq)
result = -EFAULT;
break;
}
- /* for some AP maybe good authenticate */
+ /* for some AP's maybe a good authentication */
if (wpa_Result.key_mgmt == 0x20)
pMgmt->Cisco_cckm = 1;
else
@@ -644,7 +641,7 @@ int private_ioctl(PSDevice pDevice, struct ifreq *rq)
break;
default:
- DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Private command not support..\n");
+ DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Private command not supported..\n");
}
return result;
diff --git a/drivers/staging/vt6656/iwctl.c b/drivers/staging/vt6656/iwctl.c
index 8b9894b1146a..8f198749ca51 100644
--- a/drivers/staging/vt6656/iwctl.c
+++ b/drivers/staging/vt6656/iwctl.c
@@ -674,7 +674,7 @@ int iwctl_giwaplist(struct net_device *dev, struct iw_request_info *info,
jj++;
}
- wrq->flags = 1; // Should be define'd
+ wrq->flags = 1; // Should be defined
wrq->length = jj;
memcpy(extra, sock, sizeof(struct sockaddr) * jj);
memcpy(extra + sizeof(struct sockaddr) * jj, qual, sizeof(struct iw_quality) * jj);
diff --git a/drivers/staging/vt6656/key.c b/drivers/staging/vt6656/key.c
index ee62a06a75f4..a61fcb9591aa 100644
--- a/drivers/staging/vt6656/key.c
+++ b/drivers/staging/vt6656/key.c
@@ -403,7 +403,7 @@ BOOL KeybRemoveKey(
BOOL bReturnValue = FALSE;
if (is_broadcast_ether_addr(pbyBSSID)) {
- // dealte all key
+ // delete all keys
if ((dwKeyIndex & PAIRWISE_KEY) != 0) {
for (i=0;i<MAX_KEY_TABLE;i++) {
pTable->KeyTable[i].PairwiseKey.bKeyValid = FALSE;
@@ -618,7 +618,7 @@ BOOL KeybGetTransmitKey(PSKeyManagement pTable, PBYTE pbyBSSID, DWORD dwKeyType,
/*
- * Description: Check Pairewise Key
+ * Description: Check Pairwise Key
*
* Parameters:
* In:
diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c
index d536756549e6..ad422dea702b 100644
--- a/drivers/staging/vt6656/main_usb.c
+++ b/drivers/staging/vt6656/main_usb.c
@@ -29,14 +29,14 @@
* vt6656_probe - module initial (insmod) driver entry
* device_remove1 - module remove entry
* device_open - allocate dma/descripter resource & initial mac/bbp function
- * device_xmit - asynchrous data tx function
+ * device_xmit - asynchronous data tx function
* device_set_multi - set mac filter
* device_ioctl - ioctl entry
- * device_close - shutdown mac/bbp & free dma/descripter resource
+ * device_close - shutdown mac/bbp & free dma/descriptor resource
* device_alloc_frag_buf - rx fragement pre-allocated function
* device_free_tx_bufs - free tx buffer function
* device_dma0_tx_80211- tx 802.11 frame via dma0
- * device_dma0_xmit- tx PS bufferred frame via dma0
+ * device_dma0_xmit- tx PS buffered frame via dma0
* device_init_registers- initial MAC & BBP & RF internal registers.
* device_init_rings- initial tx/rx ring buffer
* device_init_defrag_cb- initial & allocate de-fragement buffer.
@@ -316,7 +316,7 @@ static void device_init_diversity_timer(PSDevice pDevice)
//
-// Initialiation of MAC & BBP registers
+// Initialization of MAC & BBP registers
//
static BOOL device_init_registers(PSDevice pDevice, DEVICE_INIT_TYPE InitType)
@@ -639,7 +639,7 @@ static BOOL device_release_WPADEV(PSDevice pDevice)
viawget_wpa_header *wpahdr;
int ii=0;
// wait_queue_head_t Set_wait;
- //send device close to wpa_supplicnat layer
+ //send device close to wpa_supplicant layer
if (pDevice->bWPADEVUp==TRUE) {
wpahdr = (viawget_wpa_header *)pDevice->skb->data;
wpahdr->type = VIAWGET_DEVICECLOSE_MSG;
@@ -1010,7 +1010,7 @@ static int device_open(struct net_device *dev) {
}
if (device_init_defrag_cb(pDevice)== FALSE) {
- DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO " Initial defragement cb fail \n");
+ DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO " Initial defragment cb fail \n");
goto free_rx_tx;
}
@@ -1296,7 +1296,7 @@ static inline u32 ether_crc(int length, unsigned char *data)
return crc;
}
-//find out the start position of str2 from str1
+//find out the start position of str2 from str1
static unsigned char *kstrstr(const unsigned char *str1,
const unsigned char *str2) {
int str1_len = strlen(str1);
@@ -1345,7 +1345,7 @@ static int Config_FileGetParameter(unsigned char *string,
}
memset(buf2,0,100);
- memcpy(buf2,start_p,end_p-start_p); //get the tartget line
+ memcpy(buf2,start_p,end_p-start_p); //get the target line
buf2[end_p-start_p]='\0';
//find value
@@ -1396,7 +1396,7 @@ static unsigned char *Config_FileOperation(PSDevice pDevice)
}
if(!(filp->f_op) || !(filp->f_op->read) ||!(filp->f_op->write)) {
- printk("file %s cann't readable or writable?\n",config_path);
+ printk("file %s is not read or writeable?\n",config_path);
result = -1;
goto error1;
}
@@ -1969,7 +1969,7 @@ static int device_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) {
default:
rc = -EOPNOTSUPP;
- DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Ioctl command not support..%x\n", cmd);
+ DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "Ioctl command not supported..%x\n", cmd);
}
diff --git a/drivers/staging/vt6656/power.c b/drivers/staging/vt6656/power.c
index b3136773b5da..ab3a55462056 100644
--- a/drivers/staging/vt6656/power.c
+++ b/drivers/staging/vt6656/power.c
@@ -19,7 +19,7 @@
*
* File: power.c
*
- * Purpose: Handles 802.11 power management functions
+ * Purpose: Handles 802.11 power management functions
*
* Author: Lyndon Chen
*
diff --git a/drivers/staging/vt6656/rf.c b/drivers/staging/vt6656/rf.c
index 3fd0478a9a54..593cdc713b0e 100644
--- a/drivers/staging/vt6656/rf.c
+++ b/drivers/staging/vt6656/rf.c
@@ -26,7 +26,7 @@
* Date: Feb. 19, 2004
*
* Functions:
- * IFRFbWriteEmbeded - Embeded write RF register via MAC
+ * IFRFbWriteEmbedded - Embedded write RF register via MAC
*
* Revision History:
*
@@ -711,7 +711,7 @@ const BYTE RFaby11aChannelIndex[200] = {
/*--------------------- Export Functions --------------------------*/
/*
- * Description: Write to IF/RF, by embeded programming
+ * Description: Write to IF/RF, by embedded programming
*
* Parameters:
* In:
@@ -722,7 +722,7 @@ const BYTE RFaby11aChannelIndex[200] = {
* Return Value: TRUE if succeeded; FALSE if failed.
*
*/
-BOOL IFRFbWriteEmbeded (PSDevice pDevice, DWORD dwData)
+BOOL IFRFbWriteEmbedded (PSDevice pDevice, DWORD dwData)
{
BYTE pbyData[4];
@@ -828,23 +828,23 @@ BOOL bResult = TRUE;
case RF_AL2230 :
if (pDevice->byCurPwr >= AL2230_PWR_IDX_LEN)
return FALSE;
- bResult &= IFRFbWriteEmbeded(pDevice, dwAL2230PowerTable[pDevice->byCurPwr]);
+ bResult &= IFRFbWriteEmbedded(pDevice, dwAL2230PowerTable[pDevice->byCurPwr]);
if (uRATE <= RATE_11M)
- bResult &= IFRFbWriteEmbeded(pDevice, 0x0001B400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x0001B400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
else
- bResult &= IFRFbWriteEmbeded(pDevice, 0x0005A400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x0005A400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
break;
case RF_AL2230S :
if (pDevice->byCurPwr >= AL2230_PWR_IDX_LEN)
return FALSE;
- bResult &= IFRFbWriteEmbeded(pDevice, dwAL2230PowerTable[pDevice->byCurPwr]);
+ bResult &= IFRFbWriteEmbedded(pDevice, dwAL2230PowerTable[pDevice->byCurPwr]);
if (uRATE <= RATE_11M) {
- bResult &= IFRFbWriteEmbeded(pDevice, 0x040C1400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
- bResult &= IFRFbWriteEmbeded(pDevice, 0x00299B00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x040C1400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x00299B00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
}else {
- bResult &= IFRFbWriteEmbeded(pDevice, 0x0005A400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
- bResult &= IFRFbWriteEmbeded(pDevice, 0x00099B00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x0005A400+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x00099B00+(BY_AL2230_REG_LEN<<3)+IFREGCTL_REGW);
}
break;
@@ -854,10 +854,10 @@ BOOL bResult = TRUE;
DWORD dwMax7230Pwr;
if (uRATE <= RATE_11M) { //RobertYu:20060426, for better 11b mask
- bResult &= IFRFbWriteEmbeded(pDevice, 0x111BB900+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x111BB900+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW);
}
else {
- bResult &= IFRFbWriteEmbeded(pDevice, 0x221BB900+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x221BB900+(BY_AL7230_REG_LEN<<3)+IFREGCTL_REGW);
}
if (pDevice->byCurPwr > AL7230_PWR_IDX_LEN) return FALSE;
@@ -866,7 +866,7 @@ BOOL bResult = TRUE;
dwMax7230Pwr = 0x080C0B00 | ( (pDevice->byCurPwr) << 12 ) |
(BY_AL7230_REG_LEN << 3 ) | IFREGCTL_REGW;
- bResult &= IFRFbWriteEmbeded(pDevice, dwMax7230Pwr);
+ bResult &= IFRFbWriteEmbedded(pDevice, dwMax7230Pwr);
break;
}
break;
@@ -879,7 +879,7 @@ BOOL bResult = TRUE;
return FALSE;
dwVT3226Pwr = ((0x3F-pDevice->byCurPwr) << 20 ) | ( 0x17 << 8 ) /* Reg7 */ |
(BY_VT3226_REG_LEN << 3 ) | IFREGCTL_REGW;
- bResult &= IFRFbWriteEmbeded(pDevice, dwVT3226Pwr);
+ bResult &= IFRFbWriteEmbedded(pDevice, dwVT3226Pwr);
break;
}
@@ -894,27 +894,27 @@ BOOL bResult = TRUE;
dwVT3226Pwr = ((0x3F-pDevice->byCurPwr) << 20 ) | ( 0xE07 << 8 ) /* Reg7 */ | //RobertYu:20060420, TWIF 1.10
(BY_VT3226_REG_LEN << 3 ) | IFREGCTL_REGW;
- bResult &= IFRFbWriteEmbeded(pDevice, dwVT3226Pwr);
+ bResult &= IFRFbWriteEmbedded(pDevice, dwVT3226Pwr);
- bResult &= IFRFbWriteEmbeded(pDevice, 0x03C6A200+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW);
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x03C6A200+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW);
if (pDevice->sMgmtObj.eScanState != WMAC_NO_SCANNING) {
// scanning, the channel number is pDevice->uScanChannel
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"@@@@ RFbRawSetPower> 11B mode uCurrChannel[%d]\n", pDevice->sMgmtObj.uScanChannel);
- bResult &= IFRFbWriteEmbeded(pDevice, dwVT3226D0LoCurrentTable[pDevice->sMgmtObj.uScanChannel-1]); //RobertYu:20060420, sometimes didn't change channel just set power with different rate
+ bResult &= IFRFbWriteEmbedded(pDevice, dwVT3226D0LoCurrentTable[pDevice->sMgmtObj.uScanChannel-1]); //RobertYu:20060420, sometimes didn't change channel just set power with different rate
} else {
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"@@@@ RFbRawSetPower> 11B mode uCurrChannel[%d]\n", pDevice->sMgmtObj.uCurrChannel);
- bResult &= IFRFbWriteEmbeded(pDevice, dwVT3226D0LoCurrentTable[pDevice->sMgmtObj.uCurrChannel-1]); //RobertYu:20060420, sometimes didn't change channel just set power with different rate
+ bResult &= IFRFbWriteEmbedded(pDevice, dwVT3226D0LoCurrentTable[pDevice->sMgmtObj.uCurrChannel-1]); //RobertYu:20060420, sometimes didn't change channel just set power with different rate
}
- bResult &= IFRFbWriteEmbeded(pDevice, 0x015C0800+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW); //RobertYu:20060420, ok now, new switching power (mini-pci can have bigger power consumption)
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x015C0800+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW); //RobertYu:20060420, ok now, new switching power (mini-pci can have bigger power consumption)
} else {
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO"@@@@ RFbRawSetPower> 11G mode\n");
dwVT3226Pwr = ((0x3F-pDevice->byCurPwr) << 20 ) | ( 0x7 << 8 ) /* Reg7 */ | //RobertYu:20060420, TWIF 1.10
(BY_VT3226_REG_LEN << 3 ) | IFREGCTL_REGW;
- bResult &= IFRFbWriteEmbeded(pDevice, dwVT3226Pwr);
- bResult &= IFRFbWriteEmbeded(pDevice, 0x00C6A200+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW); //RobertYu:20060327
- bResult &= IFRFbWriteEmbeded(pDevice, 0x016BC600+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW); //RobertYu:20060111
- bResult &= IFRFbWriteEmbeded(pDevice, 0x00900800+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW); //RobertYu:20060111
+ bResult &= IFRFbWriteEmbedded(pDevice, dwVT3226Pwr);
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x00C6A200+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW); //RobertYu:20060327
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x016BC600+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW); //RobertYu:20060111
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x00900800+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW); //RobertYu:20060111
}
break;
}
@@ -929,7 +929,7 @@ BOOL bResult = TRUE;
dwVT3342Pwr = ((0x3F-pDevice->byCurPwr) << 20 ) | ( 0x27 << 8 ) /* Reg7 */ |
(BY_VT3342_REG_LEN << 3 ) | IFREGCTL_REGW;
- bResult &= IFRFbWriteEmbeded(pDevice, dwVT3342Pwr);
+ bResult &= IFRFbWriteEmbedded(pDevice, dwVT3342Pwr);
break;
}
@@ -1048,7 +1048,7 @@ BYTE abyArray[256];
wLength1,
abyArray
);
- //Channle Table 0
+ //Channel Table 0
wValue = 0;
while ( wLength2 > 0 ) {
@@ -1106,7 +1106,7 @@ BYTE abyArray[256];
wLength1,
abyArray);
- //Channle Table 0
+ //Channel Table 0
wValue = 0;
while ( wLength2 > 0 ) {
@@ -1141,9 +1141,9 @@ BOOL s_bVT3226D0_11bLoCurrentAdjust(
bResult = TRUE;
if( b11bMode )
- bResult &= IFRFbWriteEmbeded(pDevice, dwVT3226D0LoCurrentTable[byChannel-1]);
+ bResult &= IFRFbWriteEmbedded(pDevice, dwVT3226D0LoCurrentTable[byChannel-1]);
else
- bResult &= IFRFbWriteEmbeded(pDevice, 0x016BC600+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW); //RobertYu:20060412
+ bResult &= IFRFbWriteEmbedded(pDevice, 0x016BC600+(BY_VT3226_REG_LEN<<3)+IFREGCTL_REGW); //RobertYu:20060412
return bResult;
}
diff --git a/drivers/staging/vt6656/rf.h b/drivers/staging/vt6656/rf.h
index f5ba8fd7f816..72eb27ac436b 100644
--- a/drivers/staging/vt6656/rf.h
+++ b/drivers/staging/vt6656/rf.h
@@ -63,7 +63,7 @@
extern const BYTE RFaby11aChannelIndex[200];
/*--------------------- Export Functions --------------------------*/
-BOOL IFRFbWriteEmbeded(PSDevice pDevice, DWORD dwData);
+BOOL IFRFbWriteEmbedded(PSDevice pDevice, DWORD dwData);
BOOL RFbSetPower(PSDevice pDevice, unsigned int uRATE, unsigned int uCH);
BOOL RFbRawSetPower(
diff --git a/drivers/staging/vt6656/rxtx.c b/drivers/staging/vt6656/rxtx.c
index b6e04e7b629b..339083879883 100644
--- a/drivers/staging/vt6656/rxtx.c
+++ b/drivers/staging/vt6656/rxtx.c
@@ -841,7 +841,7 @@ s_uFillDataHead (
if ((uDMAIdx == TYPE_ATIMDMA) || (uDMAIdx == TYPE_BEACONDMA)) {
PSTxDataHead_ab pBuf = (PSTxDataHead_ab) pTxDataHead;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
(PWORD)&(pBuf->wTransmitLength), (PBYTE)&(pBuf->byServiceField), (PBYTE)&(pBuf->bySignalField)
);
//Get Duration and TimeStampOff
@@ -858,10 +858,10 @@ s_uFillDataHead (
if (byFBOption == AUTO_FB_NONE) {
PSTxDataHead_g pBuf = (PSTxDataHead_g)pTxDataHead;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
(PWORD)&(pBuf->wTransmitLength_a), (PBYTE)&(pBuf->byServiceField_a), (PBYTE)&(pBuf->bySignalField_a)
);
- BBvCaculateParameter(pDevice, cbFrameLength, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, cbFrameLength, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(PWORD)&(pBuf->wTransmitLength_b), (PBYTE)&(pBuf->byServiceField_b), (PBYTE)&(pBuf->bySignalField_b)
);
//Get Duration and TimeStamp
@@ -881,10 +881,10 @@ s_uFillDataHead (
// Auto Fallback
PSTxDataHead_g_FB pBuf = (PSTxDataHead_g_FB)pTxDataHead;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
(PWORD)&(pBuf->wTransmitLength_a), (PBYTE)&(pBuf->byServiceField_a), (PBYTE)&(pBuf->bySignalField_a)
);
- BBvCaculateParameter(pDevice, cbFrameLength, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, cbFrameLength, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(PWORD)&(pBuf->wTransmitLength_b), (PBYTE)&(pBuf->byServiceField_b), (PBYTE)&(pBuf->bySignalField_b)
);
//Get Duration and TimeStamp
@@ -907,7 +907,7 @@ s_uFillDataHead (
// Auto Fallback
PSTxDataHead_a_FB pBuf = (PSTxDataHead_a_FB)pTxDataHead;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
(PWORD)&(pBuf->wTransmitLength), (PBYTE)&(pBuf->byServiceField), (PBYTE)&(pBuf->bySignalField)
);
//Get Duration and TimeStampOff
@@ -924,7 +924,7 @@ s_uFillDataHead (
} else {
PSTxDataHead_ab pBuf = (PSTxDataHead_ab)pTxDataHead;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
(PWORD)&(pBuf->wTransmitLength), (PBYTE)&(pBuf->byServiceField), (PBYTE)&(pBuf->bySignalField)
);
//Get Duration and TimeStampOff
@@ -942,7 +942,7 @@ s_uFillDataHead (
else if (byPktType == PK_TYPE_11B) {
PSTxDataHead_ab pBuf = (PSTxDataHead_ab)pTxDataHead;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
+ BBvCalculateParameter(pDevice, cbFrameLength, wCurrentRate, byPktType,
(PWORD)&(pBuf->wTransmitLength), (PBYTE)&(pBuf->byServiceField), (PBYTE)&(pBuf->bySignalField)
);
//Get Duration and TimeStampOff
@@ -987,17 +987,17 @@ s_vFillRTSHead (
uRTSFrameLen -= 4;
}
- // Note: So far RTSHead dosen't appear in ATIM & Beacom DMA, so we don't need to take them into account.
+ // Note: So far RTSHead doesn't appear in ATIM & Beacom DMA, so we don't need to take them into account.
// Otherwise, we need to modified codes for them.
if (byPktType == PK_TYPE_11GB || byPktType == PK_TYPE_11GA) {
if (byFBOption == AUTO_FB_NONE) {
PSRTS_g pBuf = (PSRTS_g)pvRTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(PWORD)&(wLen), (PBYTE)&(pBuf->byServiceField_b), (PBYTE)&(pBuf->bySignalField_b)
);
pBuf->wTransmitLength_b = cpu_to_le16(wLen);
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
(PWORD)&(wLen), (PBYTE)&(pBuf->byServiceField_a), (PBYTE)&(pBuf->bySignalField_a)
);
pBuf->wTransmitLength_a = cpu_to_le16(wLen);
@@ -1035,11 +1035,11 @@ s_vFillRTSHead (
else {
PSRTS_g_FB pBuf = (PSRTS_g_FB)pvRTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(PWORD)&(wLen), (PBYTE)&(pBuf->byServiceField_b), (PBYTE)&(pBuf->bySignalField_b)
);
pBuf->wTransmitLength_b = cpu_to_le16(wLen);
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
(PWORD)&(wLen), (PBYTE)&(pBuf->byServiceField_a), (PBYTE)&(pBuf->bySignalField_a)
);
pBuf->wTransmitLength_a = cpu_to_le16(wLen);
@@ -1084,7 +1084,7 @@ s_vFillRTSHead (
if (byFBOption == AUTO_FB_NONE) {
PSRTS_ab pBuf = (PSRTS_ab)pvRTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
(PWORD)&(wLen), (PBYTE)&(pBuf->byServiceField), (PBYTE)&(pBuf->bySignalField)
);
pBuf->wTransmitLength = cpu_to_le16(wLen);
@@ -1119,7 +1119,7 @@ s_vFillRTSHead (
else {
PSRTS_a_FB pBuf = (PSRTS_a_FB)pvRTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopOFDMBasicRate, byPktType,
(PWORD)&(wLen), (PBYTE)&(pBuf->byServiceField), (PBYTE)&(pBuf->bySignalField)
);
pBuf->wTransmitLength = cpu_to_le16(wLen);
@@ -1155,7 +1155,7 @@ s_vFillRTSHead (
else if (byPktType == PK_TYPE_11B) {
PSRTS_ab pBuf = (PSRTS_ab)pvRTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, uRTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(PWORD)&(wLen), (PBYTE)&(pBuf->byServiceField), (PBYTE)&(pBuf->bySignalField)
);
pBuf->wTransmitLength = cpu_to_le16(wLen);
@@ -1221,7 +1221,7 @@ s_vFillCTSHead (
// Auto Fall back
PSCTS_FB pBuf = (PSCTS_FB)pvCTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uCTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, uCTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(PWORD)&(wLen), (PBYTE)&(pBuf->byServiceField_b), (PBYTE)&(pBuf->bySignalField_b)
);
pBuf->wTransmitLength_b = cpu_to_le16(wLen);
@@ -1246,7 +1246,7 @@ s_vFillCTSHead (
} else { //if (byFBOption != AUTO_FB_NONE && uDMAIdx != TYPE_ATIMDMA && uDMAIdx != TYPE_BEACONDMA)
PSCTS pBuf = (PSCTS)pvCTS;
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, uCTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, uCTSFrameLen, pDevice->byTopCCKBasicRate, PK_TYPE_11B,
(PWORD)&(wLen), (PBYTE)&(pBuf->byServiceField_b), (PBYTE)&(pBuf->bySignalField_b)
);
pBuf->wTransmitLength_b = cpu_to_le16(wLen);
@@ -1827,7 +1827,7 @@ s_bPacketToWirelessUsb(
*
* Parameters:
* In:
- * pDevice - Pointer to adpater
+ * pDevice - Pointer to adapter
* dwTxBufferAddr - Transmit Buffer
* pPacket - Packet from upper layer
* cbPacketSize - Transmit Data Length
@@ -2198,7 +2198,7 @@ CMD_STATUS csMgmt_xmit(
if (bIsPSPOLL) {
// The MAC will automatically replace the Duration-field of MAC header by Duration-field
- // of FIFO control header.
+ // of FIFO control header.
// This will cause AID-field of PS-POLL packet be incorrect (Because PS-POLL's AID field is
// in the same place of other packet's Duration-field).
// And it will cause Cisco-AP to issue Disassociation-packet
@@ -2272,7 +2272,7 @@ csBeacon_xmit(
wCurrentRate = RATE_6M;
pTxDataHead = (PSTxDataHead_ab) (pbyTxBufferAddr + wTxBufSize);
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameSize, wCurrentRate, PK_TYPE_11A,
+ BBvCalculateParameter(pDevice, cbFrameSize, wCurrentRate, PK_TYPE_11A,
(PWORD)&(pTxDataHead->wTransmitLength), (PBYTE)&(pTxDataHead->byServiceField), (PBYTE)&(pTxDataHead->bySignalField)
);
//Get Duration and TimeStampOff
@@ -2285,7 +2285,7 @@ csBeacon_xmit(
pTxBufHead->wFIFOCtl |= FIFOCTL_11B;
pTxDataHead = (PSTxDataHead_ab) (pbyTxBufferAddr + wTxBufSize);
//Get SignalField,ServiceField,Length
- BBvCaculateParameter(pDevice, cbFrameSize, wCurrentRate, PK_TYPE_11B,
+ BBvCalculateParameter(pDevice, cbFrameSize, wCurrentRate, PK_TYPE_11B,
(PWORD)&(pTxDataHead->wTransmitLength), (PBYTE)&(pTxDataHead->byServiceField), (PBYTE)&(pTxDataHead->bySignalField)
);
//Get Duration and TimeStampOff
@@ -2473,7 +2473,7 @@ vDMA0_tx_80211(PSDevice pDevice, struct sk_buff *skb) {
cbMacHdLen = WLAN_HDR_ADDR3_LEN;
}
- // hostapd deamon ext support rate patch
+ // hostapd daemon ext support rate patch
if (WLAN_GET_FC_FSTYPE(p80211Header->sA4.wFrameCtl) == WLAN_FSTYPE_ASSOCRESP) {
if (((PWLAN_IE_SUPP_RATES)pMgmt->abyCurrSuppRates)->len != 0) {
@@ -2591,7 +2591,7 @@ vDMA0_tx_80211(PSDevice pDevice, struct sk_buff *skb) {
pMACHeader->wFrameCtl &= cpu_to_le16(0xfffc);
memcpy(pbyPayloadHead, (skb->data + cbMacHdLen), cbFrameBodySize);
- // replace support rate, patch for hostapd deamon( only support 11M)
+ // replace support rate, patch for hostapd daemon( only support 11M)
if (WLAN_GET_FC_FSTYPE(p80211Header->sA4.wFrameCtl) == WLAN_FSTYPE_ASSOCRESP) {
if (cbExtSuppRate != 0) {
if (((PWLAN_IE_SUPP_RATES)pMgmt->abyCurrSuppRates)->len != 0)
@@ -2770,7 +2770,7 @@ int nsDMA_tx_packet(PSDevice pDevice, unsigned int uDMAIdx, struct sk_buff *skb)
pMgmt->abyPSTxMap[0] |= byMask[0];
return 0;
}
- // muticast/broadcast data rate
+ // multicast/broadcast data rate
if (pDevice->byBBType != BB_TYPE_11A)
pDevice->wCurrentRate = RATE_2M;
@@ -2855,7 +2855,7 @@ int nsDMA_tx_packet(PSDevice pDevice, unsigned int uDMAIdx, struct sk_buff *skb)
}
PRINT_K("Authentication completed!!\n");
}
- else if((Key_info & BIT3) && (Descriptor_type==2) && //RSN pairse-key challenge
+ else if((Key_info & BIT3) && (Descriptor_type==2) && //RSN pairwise-key challenge
(Key_info & BIT8) && (Key_info & BIT9)) {
pDevice->fWPA_Authened = TRUE;
PRINT_K("WPA2 Authentication completed!!\n");
diff --git a/drivers/staging/vt6656/tcrc.c b/drivers/staging/vt6656/tcrc.c
index e25021e850a0..2237eeb5ec5b 100644
--- a/drivers/staging/vt6656/tcrc.c
+++ b/drivers/staging/vt6656/tcrc.c
@@ -18,7 +18,7 @@
*
* File: tcrc.c
*
- * Purpose: Implement functions to caculate CRC
+ * Purpose: Implement functions to calculate CRC
*
* Author: Tevin Chen
*
diff --git a/drivers/staging/vt6656/tcrc.h b/drivers/staging/vt6656/tcrc.h
index 4dfd01e477a4..dc54bd8fc4fc 100644
--- a/drivers/staging/vt6656/tcrc.h
+++ b/drivers/staging/vt6656/tcrc.h
@@ -18,7 +18,7 @@
*
* File: tcrc.h
*
- * Purpose: Implement functions to caculate CRC
+ * Purpose: Implement functions to calculate CRC
*
* Author: Tevin Chen
*
diff --git a/drivers/staging/vt6656/tether.c b/drivers/staging/vt6656/tether.c
index 4f368f174b21..083b2153a271 100644
--- a/drivers/staging/vt6656/tether.c
+++ b/drivers/staging/vt6656/tether.c
@@ -25,7 +25,7 @@
* Date: May 21, 1996
*
* Functions:
- * ETHbyGetHashIndexByCrc32 - Caculate multicast hash value by CRC32
+ * ETHbyGetHashIndexByCrc32 - Calculate multicast hash value by CRC32
* ETHbIsBufferCrc32Ok - Check CRC value of the buffer if Ok or not
*
* Revision History:
@@ -50,7 +50,7 @@
/*
- * Description: Caculate multicast hash value by CRC32
+ * Description: Calculate multicast hash value by CRC32
*
* Parameters:
* In:
diff --git a/drivers/staging/vt6656/tkip.c b/drivers/staging/vt6656/tkip.c
index 0715636cb9cb..003123e550f6 100644
--- a/drivers/staging/vt6656/tkip.c
+++ b/drivers/staging/vt6656/tkip.c
@@ -168,7 +168,7 @@ static unsigned int rotr1(unsigned int a)
/*
- * Description: Caculate RC4Key fom TK, TA, and TSC
+ * Description: Calculate RC4Key fom TK, TA, and TSC
*
* Parameters:
* In:
diff --git a/drivers/staging/vt6656/wcmd.c b/drivers/staging/vt6656/wcmd.c
index 9d2caa819f47..586fbe1627f7 100644
--- a/drivers/staging/vt6656/wcmd.c
+++ b/drivers/staging/vt6656/wcmd.c
@@ -263,7 +263,7 @@ s_vProbeChannel(
*
*
* Return Value:
- * A ptr to Tx frame or NULL on allocation failue
+ * A ptr to Tx frame or NULL on allocation failure
*
-*/
@@ -751,7 +751,7 @@ void vRunCommand(void *hDeviceContext)
pDevice->nTxDataTimeCout = 0;
}
else {
- // printk("mike:-->First time triger TimerTxData InSleep\n");
+ // printk("mike:-->First time trigger TimerTxData InSleep\n");
}
pDevice->IsTxDataTrigger = TRUE;
add_timer(&pDevice->sTimerTxData);
@@ -794,7 +794,7 @@ void vRunCommand(void *hDeviceContext)
DBG_PRT(MSG_LEVEL_DEBUG,
KERN_INFO "vMgrCreateOwnIBSS fail!\n");
}
- // alway turn off unicast bit
+ // always turn off unicast bit
MACvRegBitsOff(pDevice, MAC_REG_RCR, RCR_UNICAST);
pDevice->byRxMode &= ~RCR_UNICAST;
DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "wcmd: rx_mode = %x\n", pDevice->byRxMode );
@@ -946,7 +946,7 @@ void vRunCommand(void *hDeviceContext)
pItemSSID = (PWLAN_IE_SSID)pMgmt->abyCurrSSID;
pItemSSID->len = 0;
memset(pItemSSID->abySSID, 0, WLAN_SSID_MAXLEN);
- //clear dessire SSID
+ //clear desired SSID
pItemSSID = (PWLAN_IE_SSID)pMgmt->abyDesireSSID;
pItemSSID->len = 0;
memset(pItemSSID->abySSID, 0, WLAN_SSID_MAXLEN);
diff --git a/drivers/staging/vt6656/wctl.c b/drivers/staging/vt6656/wctl.c
index c231ae7176f5..9249263b2da8 100644
--- a/drivers/staging/vt6656/wctl.c
+++ b/drivers/staging/vt6656/wctl.c
@@ -89,7 +89,7 @@ BOOL WCTLbIsDuplicate (PSCache pCache, PS802_11Header pMACHeader)
ADD_ONE_WITH_WRAP_AROUND(uIndex, DUPLICATE_RX_CACHE_LENGTH);
}
}
- /* Not fount in cache - insert */
+ /* Not found in cache - insert */
pCacheEntry = &pCache->asCacheEntry[pCache->uInPtr];
pCacheEntry->wFmSequence = pMACHeader->wSeqCtl;
memcpy(&(pCacheEntry->abyAddr2[0]), &(pMACHeader->abyAddr2[0]), ETH_ALEN);
diff --git a/drivers/staging/vt6656/wmgr.c b/drivers/staging/vt6656/wmgr.c
index f08e2d15c7b3..7db6a8d3508b 100644
--- a/drivers/staging/vt6656/wmgr.c
+++ b/drivers/staging/vt6656/wmgr.c
@@ -27,7 +27,7 @@
*
* Functions:
* nsMgrObjectInitial - Initialize Management Objet data structure
- * vMgrObjectReset - Reset Management Objet data structure
+ * vMgrObjectReset - Reset Management Object data structure
* vMgrAssocBeginSta - Start associate function
* vMgrReAssocBeginSta - Start reassociate function
* vMgrDisassocBeginSta - Start disassociate function
@@ -54,7 +54,7 @@
* bMgrPrepareBeaconToSend - Prepare Beacon frame
* s_vMgrLogStatus - Log 802.11 Status
* vMgrRxManagePacket - Rcv management frame dispatch function
- * s_vMgrFormatTIM- Assember TIM field of beacon
+ * s_vMgrFormatTIM- Assembler TIM field of beacon
* vMgrTimerInit- Initial 1-sec and command call back funtions
*
* Revision History:
@@ -2032,7 +2032,7 @@ if(ChannelExceedZoneType(pDevice,byCurrChannel)==TRUE)
}
//
- // Preamble may change dynamiclly
+ // Preamble may change dynamically
//
byOldPreambleType = pDevice->byPreambleType;
if (WLAN_GET_CAP_INFO_SHORTPREAMBLE(pBSSList->wCapInfo)) {
@@ -2044,7 +2044,7 @@ if(ChannelExceedZoneType(pDevice,byCurrChannel)==TRUE)
if (pDevice->byPreambleType != byOldPreambleType)
CARDvSetRSPINF(pDevice, (BYTE)pDevice->byBBType);
//
- // Basic Rate Set may change dynamiclly
+ // Basic Rate Set may change dynamically
//
if (pBSSList->eNetworkTypeInUse == PHY_TYPE_11B) {
uRateLen = WLAN_RATES_MAXLEN_11B;
@@ -2188,7 +2188,7 @@ if(ChannelExceedZoneType(pDevice,byCurrChannel)==TRUE)
// During dpc, already in spinlocked.
if (BSSbIsSTAInNodeDB(pDevice, sFrame.pHdr->sA3.abyAddr2, &uNodeIndex)) {
- // Update the STA, (Techically the Beacons of all the IBSS nodes
+ // Update the STA, (Technically the Beacons of all the IBSS nodes
// should be identical, but that's not happening in practice.
pMgmt->abyCurrSuppRates[1] = RATEuSetIE((PWLAN_IE_SUPP_RATES)sFrame.pSuppRates,
(PWLAN_IE_SUPP_RATES)pMgmt->abyCurrSuppRates,
@@ -2722,7 +2722,7 @@ void vMgrJoinBSSBegin(void *hDeviceContext, PCMD_STATUS pStatus)
memcpy(pDevice->abyBSSID, pCurr->abyBSSID, WLAN_BSSID_LEN);
// Add current BSS to Candidate list
- // This should only works for WPA2 BSS, and WPA2 BSS check must be done before.
+ // This should only work for WPA2 BSS, and WPA2 BSS check must be done before.
if (pMgmt->eAuthenMode == WMAC_AUTH_WPA2) {
BOOL bResult = bAdd_PMKID_Candidate((void *) pDevice,
pMgmt->abyCurrBSSID,
@@ -3181,7 +3181,7 @@ s_vMgrFormatTIM(
*
*
* Return Value:
- * PTR to frame; or NULL on allocation failue
+ * PTR to frame; or NULL on allocation failure
*
-*/
@@ -3353,7 +3353,7 @@ s_MgrMakeBeacon(
*
*
* Return Value:
- * PTR to frame; or NULL on allocation failue
+ * PTR to frame; or NULL on allocation failure
*
-*/
@@ -3528,7 +3528,7 @@ s_MgrMakeAssocRequest(
memcpy( sFrame.pHdr->sA3.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN);
memcpy( sFrame.pHdr->sA3.abyAddr3, pMgmt->abyCurrBSSID, WLAN_BSSID_LEN);
- // Set the capibility and listen interval
+ // Set the capability and listen interval
*(sFrame.pwCapInfo) = cpu_to_le16(wCurrCapInfo);
*(sFrame.pwListenInterval) = cpu_to_le16(wListenInterval);
@@ -3749,7 +3749,7 @@ s_MgrMakeAssocRequest(
*
*
* Return Value:
- * A ptr to frame or NULL on allocation failue
+ * A ptr to frame or NULL on allocation failure
*
-*/
@@ -3792,7 +3792,7 @@ s_MgrMakeReAssocRequest(
memcpy( sFrame.pHdr->sA3.abyAddr2, pMgmt->abyMACAddr, WLAN_ADDR_LEN);
memcpy( sFrame.pHdr->sA3.abyAddr3, pMgmt->abyCurrBSSID, WLAN_BSSID_LEN);
- /* Set the capibility and listen interval */
+ /* Set the capability and listen interval */
*(sFrame.pwCapInfo) = cpu_to_le16(wCurrCapInfo);
*(sFrame.pwListenInterval) = cpu_to_le16(wListenInterval);
@@ -4004,7 +4004,7 @@ s_MgrMakeReAssocRequest(
*
*
* Return Value:
- * PTR to frame; or NULL on allocation failue
+ * PTR to frame; or NULL on allocation failure
*
-*/
@@ -4077,7 +4077,7 @@ s_MgrMakeAssocResponse(
*
*
* Return Value:
- * PTR to frame; or NULL on allocation failue
+ * PTR to frame; or NULL on allocation failure
*
-*/
diff --git a/drivers/staging/vt6656/wpa.c b/drivers/staging/vt6656/wpa.c
index b16d4ddc117b..f6429a26ae0f 100644
--- a/drivers/staging/vt6656/wpa.c
+++ b/drivers/staging/vt6656/wpa.c
@@ -231,7 +231,7 @@ WPA_ParseRSN(
* Parameters:
* In:
* byCmd - Search type
- * byEncrypt- Encrcypt Type
+ * byEncrypt- Encrypt Type
* pBSSList - BSS list
* Out:
* none
diff --git a/drivers/staging/vt6656/wpa2.c b/drivers/staging/vt6656/wpa2.c
index d4f3f7530ee4..c0926976627d 100644
--- a/drivers/staging/vt6656/wpa2.c
+++ b/drivers/staging/vt6656/wpa2.c
@@ -174,16 +174,16 @@ WPA2vParseRSN (
pBSSNode->abyCSSPK[j++] = WLAN_11i_CSS_USE_GROUP;
bUseGK = TRUE;
} else if ( !memcmp(pbyOUI, abyOUIWEP40, 4)) {
- // Invialid CSS, continue to parsing
+ // Invalid CSS, continue parsing
} else if ( !memcmp(pbyOUI, abyOUITKIP, 4)) {
if (pBSSNode->byCSSGK != WLAN_11i_CSS_CCMP)
pBSSNode->abyCSSPK[j++] = WLAN_11i_CSS_TKIP;
else
- ; // Invialid CSS, continue to parsing
+ ; // Invalid CSS, continue parsing
} else if ( !memcmp(pbyOUI, abyOUICCMP, 4)) {
pBSSNode->abyCSSPK[j++] = WLAN_11i_CSS_CCMP;
} else if ( !memcmp(pbyOUI, abyOUIWEP104, 4)) {
- // Invialid CSS, continue to parsing
+ // Invalid CSS, continue parsing
} else {
// any vendor checks here
pBSSNode->abyCSSPK[j++] = WLAN_11i_CSS_UNKNOWN;
diff --git a/drivers/staging/vt6656/wpactl.c b/drivers/staging/vt6656/wpactl.c
index 5435e8205b2c..3e65aa132011 100644
--- a/drivers/staging/vt6656/wpactl.c
+++ b/drivers/staging/vt6656/wpactl.c
@@ -74,7 +74,7 @@ static void wpadev_setup(struct net_device *dev)
/*
* Description:
- * register netdev for wpa supplicant deamon
+ * register netdev for wpa supplicant daemon
*
* Parameters:
* In:
@@ -154,7 +154,7 @@ static int wpa_release_wpadev(PSDevice pDevice)
/*
* Description:
- * Set enable/disable dev for wpa supplicant deamon
+ * Set enable/disable dev for wpa supplicant daemon
*
* Parameters:
* In:
@@ -326,7 +326,7 @@ int wpa_set_wpadev(PSDevice pDevice, int val)
if ((byKeyDecMode == KEY_CTL_TKIP) &&
(param->u.wpa_key.key_len != MAX_KEY_LEN)) {
// TKIP Key must be 256 bits
- DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "return- TKIP Key must be 256 bits!\n");
+ DBG_PRT(MSG_LEVEL_DEBUG, KERN_INFO "return - TKIP Key must be 256 bits!\n");
return -EINVAL;
}
// Check AES key length
diff --git a/drivers/staging/winbond/Kconfig b/drivers/staging/winbond/Kconfig
index 132671d96d0d..a29f60836b77 100644
--- a/drivers/staging/winbond/Kconfig
+++ b/drivers/staging/winbond/Kconfig
@@ -1,6 +1,6 @@
config W35UND
tristate "IS89C35 WLAN USB driver"
- depends on MAC80211 && WLAN && USB && EXPERIMENTAL
+ depends on MAC80211 && WLAN && USB
default n
---help---
This is highly experimental driver for Winbond WIFI card.
diff --git a/drivers/staging/winbond/localpara.h b/drivers/staging/winbond/localpara.h
index d7980575bed1..84effc47d79d 100644
--- a/drivers/staging/winbond/localpara.h
+++ b/drivers/staging/winbond/localpara.h
@@ -137,7 +137,7 @@ struct wb_local_para {
u8 iPowerSaveMode; /* 0 indicates on, 1 indicates off */
u8 ATIMmode;
u8 ExcludeUnencrypted;
- /* Unit ime count for the decision to enter PS mode */
+ /* Unit time count for the decision to enter PS mode */
u16 CheckCountForPS;
u8 boHasTxActivity;/* tx activity has occurred */
u8 boMacPsValid; /* Power save mode obtained from H/W is valid or not */
@@ -187,7 +187,7 @@ struct wb_local_para {
u8 reserved7[3];
struct chan_info CurrentChan; /* Current channel no. and channel band. It may be changed by scanning. */
- u8 boHandover; /* Roaming, Hnadover to other AP. */
+ u8 boHandover; /* Roaming, Handover to other AP. */
u8 boCCAbusy;
u16 CWMax; /* It may not be the real value that H/W used */
diff --git a/drivers/staging/winbond/mds.c b/drivers/staging/winbond/mds.c
index c9f0e8f856a0..1b8b8ace39e0 100644
--- a/drivers/staging/winbond/mds.c
+++ b/drivers/staging/winbond/mds.c
@@ -569,7 +569,7 @@ Mds_SendComplete(struct wbsoft_priv *adapter, struct T02_descriptor *pT02)
unsigned char SendOK = true;
u8 RetryCount, TxRate;
- if (pT02->T02_IgnoreResult) /* Don't care the result */
+ if (pT02->T02_IgnoreResult) /* Don't care about the result */
return;
if (pT02->T02_IsLastMpdu) {
/* TODO: DTO -- get the retry count and fragment count */
diff --git a/drivers/staging/winbond/mto.c b/drivers/staging/winbond/mto.c
index 1b52ebd4b011..560c0ab617d1 100644
--- a/drivers/staging/winbond/mto.c
+++ b/drivers/staging/winbond/mto.c
@@ -23,7 +23,7 @@
#include "core.h"
/* Declare SQ3 to rate and fragmentation threshold table */
-/* Declare fragmentation thresholds table */
+/* Declare fragmentation threshold table */
#define MTO_MAX_FRAG_TH_LEVELS 5
#define MTO_MAX_DATA_RATE_LEVELS 12
diff --git a/drivers/staging/winbond/phy_calibration.c b/drivers/staging/winbond/phy_calibration.c
index 77a3fff708c6..cabae3466704 100644
--- a/drivers/staging/winbond/phy_calibration.c
+++ b/drivers/staging/winbond/phy_calibration.c
@@ -399,7 +399,7 @@ void _rxadc_dc_offset_cancellation_winbond(struct hw_data *phw_data, u32 frequen
val |= MASK_ADC_DC_CAL_STR;
hw_set_dxx_reg(phw_data, REG_MODE_CTRL, val);
- /* e. The result are shown in "adc_dc_cal_i[8:0] and adc_dc_cal_q[8:0]" */
+ /* e. The results are shown in "adc_dc_cal_i[8:0] and adc_dc_cal_q[8:0]" */
#ifdef _DEBUG
hw_get_dxx_reg(phw_data, REG_OFFSET_READ, &val);
PHY_DEBUG(("[CAL] REG_OFFSET_READ = 0x%08X\n", val));
@@ -720,7 +720,7 @@ u8 _tx_iq_calibration_loop_winbond(struct hw_data *phw_data,
for (capture_time = 0; capture_time < 10; capture_time++) {
/*
* a. Set iqcal_mode[1:0] to 0x2 and set "calib_start" to 0x1 to
- * enable "IQ alibration Mode II"
+ * enable "IQ calibration Mode II"
*/
reg_mode_ctrl &= ~(MASK_IQCAL_TONE_SEL|MASK_IQCAL_MODE);
reg_mode_ctrl &= ~MASK_IQCAL_MODE;
@@ -750,7 +750,7 @@ u8 _tx_iq_calibration_loop_winbond(struct hw_data *phw_data,
/*
* d. Set iqcal_mode[1:0] to 0x3 and set "calib_start" to 0x1 to
- * enable "IQ alibration Mode II"
+ * enable "IQ calibration Mode II"
*/
/* hw_get_dxx_reg(phw_data, REG_MODE_CTRL, &val); */
hw_get_dxx_reg(phw_data, REG_MODE_CTRL, &reg_mode_ctrl);
@@ -980,7 +980,7 @@ void _tx_iq_calibration_winbond(struct hw_data *phw_data)
phy_set_rf_data(phw_data, 0, (0<<24)|0xFDF1C0);
/* ; [BB-chip]: Calibration (6f).Send test pattern */
/* ; [BB-chip]: Calibration (6g). Search RXGCL optimal value */
- /* ; [BB-chip]: Calibration (6h). Caculate TX-path IQ imbalance and setting TX path IQ compensation table */
+ /* ; [BB-chip]: Calibration (6h). Calculate TX-path IQ imbalance and setting TX path IQ compensation table */
/* phy_set_rf_data(phw_data, 3, (3<<24)|0x025586); */
msleep(30); /* 20060612.1.a 30ms delay. Add the follow 2 lines */
@@ -1373,7 +1373,7 @@ u8 _rx_iq_calibration_loop_winbond(struct hw_data *phw_data, u16 factor, u32 fre
/***************************************************************/
void _rx_iq_calibration_winbond(struct hw_data *phw_data, u32 frequency)
{
-/* figo 20050523 marked this flag for can't compile for relesase */
+/* figo 20050523 marked this flag for can't compile for release */
#ifdef _DEBUG
s32 rx_cal_reg[4];
u32 val;
@@ -1397,7 +1397,7 @@ void _rx_iq_calibration_winbond(struct hw_data *phw_data, u32 frequency)
/* ; [BB-chip]: Calibration (7f). Send test pattern */
/* ; [BB-chip]: Calibration (7g). Search RXGCL optimal value */
- /* ; [BB-chip]: Calibration (7h). Caculate RX-path IQ imbalance and setting RX path IQ compensation table */
+ /* ; [BB-chip]: Calibration (7h). Calculate RX-path IQ imbalance and setting RX path IQ compensation table */
result = _rx_iq_calibration_loop_winbond(phw_data, 12589, frequency);
@@ -1454,7 +1454,7 @@ void phy_calibration_winbond(struct hw_data *phw_data, u32 frequency)
_rxadc_dc_offset_cancellation_winbond(phw_data, frequency);
/* _txidac_dc_offset_cancellation_winbond(phw_data); */
- /* _txqdac_dc_offset_cacellation_winbond(phw_data); */
+ /* _txqdac_dc_offset_cancellation_winbond(phw_data); */
_tx_iq_calibration_winbond(phw_data);
_rx_iq_calibration_winbond(phw_data, frequency);
diff --git a/drivers/staging/winbond/reg.c b/drivers/staging/winbond/reg.c
index 1b38d6d225c9..5ecf9a121e78 100644
--- a/drivers/staging/winbond/reg.c
+++ b/drivers/staging/winbond/reg.c
@@ -693,7 +693,7 @@ u32 w89rf242_rf_data[] = {
(0x0E << 24) | 0x5557DC, /* 1555F ; IBSC (0x0E) -- IRLNA & IRLNB (PTAT & Const current)=01/01; FA5976B_1.3F */
(0x10 << 24) | 0x000C20, /* 00030 ; TMODA (0x10) -- LNA_gain_step=0011 ; LNA=15/16dB */
(0x11 << 24) | 0x0C0022, /* 03000 ; TMODB (0x11) -- Turn ON RX-Q path Test Switch; To improve IQ path group delay (FA5976A_1.3C) */
- (0x12 << 24) | 0x000024 /* TMODC (0x12) -- Turn OFF Tempearure sensor */
+ (0x12 << 24) | 0x000024 /* TMODC (0x12) -- Turn OFF Temperature sensor */
};
u32 w89rf242_channel_data_24[][2] = {
diff --git a/drivers/staging/winbond/sme_api.h b/drivers/staging/winbond/sme_api.h
index 8f4596c9e9b3..652ae7085a5f 100644
--- a/drivers/staging/winbond/sme_api.h
+++ b/drivers/staging/winbond/sme_api.h
@@ -12,7 +12,6 @@
#include "localpara.h"
/****************** CONSTANT AND MACRO SECTION ******************************/
-#define _INLINE __inline
#define MEDIA_STATE_DISCONNECTED 0
#define MEDIA_STATE_CONNECTED 1
@@ -26,17 +25,17 @@
/* OID_802_11_BSSID */
s8 sme_get_bssid(void *pcore_data, u8 *pbssid);
-s8 sme_get_desired_bssid(void *pcore_data, u8 *pbssid); /* Not use */
+s8 sme_get_desired_bssid(void *pcore_data, u8 *pbssid); /* Unused */
s8 sme_set_desired_bssid(void *pcore_data, u8 *pbssid);
/* OID_802_11_SSID */
s8 sme_get_ssid(void *pcore_data, u8 *pssid, u8 *pssid_len);
-s8 sme_get_desired_ssid(void *pcore_data, u8 *pssid, u8 *pssid_len);/* Not use */
+s8 sme_get_desired_ssid(void *pcore_data, u8 *pssid, u8 *pssid_len);/* Unused */
s8 sme_set_desired_ssid(void *pcore_data, u8 *pssid, u8 ssid_len);
/* OID_802_11_INFRASTRUCTURE_MODE */
s8 sme_get_bss_type(void *pcore_data, u8 *pbss_type);
-s8 sme_get_desired_bss_type(void *pcore_data, u8 *pbss_type); /* Not use */
+s8 sme_get_desired_bss_type(void *pcore_data, u8 *pbss_type); /* Unused */
s8 sme_set_desired_bss_type(void *pcore_data, u8 bss_type);
/* OID_802_11_FRAGMENTATION_THRESHOLD */
@@ -107,7 +106,7 @@ s8 sme_set_bssid_list_scan(void *pcore_data, void *pscan_para);
s8 sme_set_reload_defaults(void *pcore_data, u8 reload_type);
-/*------------------------- none-standard ----------------------------------*/
+/*------------------------- non-standard ----------------------------------*/
s8 sme_get_connect_status(void *pcore_data, u8 *pstatus);
/*--------------------------------------------------------------------------*/
@@ -138,7 +137,7 @@ s8 sme_set_txrate_policy(void *pcore_data, u8 policy);
s8 sme_get_txrate_policy(void *pcore_data, u8 *policy);
s8 sme_get_cwmin_value(void *pcore_data, u8 *cwmin);
s8 sme_get_cwmax_value(void *pcore_data, u16 *cwmax);
-s8 sme_get_ms_radio_mode(void *pcore_data, u8 * pMsRadioOff);
+s8 sme_get_ms_radio_mode(void *pcore_data, u8 *pMsRadioOff);
s8 sme_set_ms_radio_mode(void *pcore_data, u8 boMsRadioOff);
void sme_get_tx_power_level(void *pcore_data, u32 *TxPower);
diff --git a/drivers/staging/winbond/wb35reg.c b/drivers/staging/winbond/wb35reg.c
index da595f16f634..1bff7d1c9a77 100644
--- a/drivers/staging/winbond/wb35reg.c
+++ b/drivers/staging/winbond/wb35reg.c
@@ -217,7 +217,7 @@ unsigned char Wb35Reg_Write(struct hw_data *pHwData, u16 RegisterNo, u32 Registe
* This command will be executed with a user defined value. When it completes,
* this value is useful. For example, hal_set_current_channel will use it.
* true : read command process successfully
- * false : register not support
+ * false : register not supported
*/
unsigned char Wb35Reg_WriteWithCallbackValue(struct hw_data *pHwData,
u16 RegisterNo,
@@ -631,7 +631,7 @@ unsigned char Wb35Reg_initial(struct hw_data *pHwData)
* CardComputeCrc --
*
* Description:
- * Runs the AUTODIN II CRC algorithm on buffer Buffer of length, Length.
+ * Runs the AUTODIN II CRC algorithm on the buffers Buffer length.
*
* Arguments:
* Buffer - the input buffer
diff --git a/drivers/staging/winbond/wb35tx.c b/drivers/staging/winbond/wb35tx.c
index 5df39d46cda4..30a77ccfe480 100644
--- a/drivers/staging/winbond/wb35tx.c
+++ b/drivers/staging/winbond/wb35tx.c
@@ -1,13 +1,13 @@
-//============================================================================
-// Copyright (c) 1996-2002 Winbond Electronic Corporation
-//
-// Module Name:
-// Wb35Tx.c
-//
-// Abstract:
-// Processing the Tx message and put into down layer
-//
-//============================================================================
+/*
+ * Copyright (c) 1996-2002 Winbond Electronic Corporation
+ *
+ * Module Name:
+ * Wb35Tx.c
+ *
+ * Abstract:
+ * Processing the Tx message and put into down layer
+ *
+ */
#include <linux/usb.h>
#include <linux/gfp.h>
@@ -15,7 +15,7 @@
#include "mds_f.h"
unsigned char
-Wb35Tx_get_tx_buffer(struct hw_data * pHwData, u8 **pBuffer)
+Wb35Tx_get_tx_buffer(struct hw_data *pHwData, u8 **pBuffer)
{
struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
@@ -25,28 +25,29 @@ Wb35Tx_get_tx_buffer(struct hw_data * pHwData, u8 **pBuffer)
static void Wb35Tx(struct wbsoft_priv *adapter);
-static void Wb35Tx_complete(struct urb * pUrb)
+static void Wb35Tx_complete(struct urb *pUrb)
{
struct wbsoft_priv *adapter = pUrb->context;
- struct hw_data * pHwData = &adapter->sHwData;
+ struct hw_data *pHwData = &adapter->sHwData;
struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
struct wb35_mds *pMds = &adapter->Mds;
printk("wb35: tx complete\n");
- // Variable setting
+ /* Variable setting */
pWb35Tx->EP4vm_state = VM_COMPLETED;
- pWb35Tx->EP4VM_status = pUrb->status; //Store the last result of Irp
- pMds->TxOwner[ pWb35Tx->TxSendIndex ] = 0;// Set the owner. Free the owner bit always.
+ pWb35Tx->EP4VM_status = pUrb->status; /* Store the last result of Irp */
+ /* Set the owner. Free the owner bit always. */
+ pMds->TxOwner[pWb35Tx->TxSendIndex] = 0;
pWb35Tx->TxSendIndex++;
pWb35Tx->TxSendIndex %= MAX_USB_TX_BUFFER_NUMBER;
- if (pHwData->SurpriseRemove) // Let WbWlanHalt to handle surprise remove
+ if (pHwData->SurpriseRemove) /* Let WbWlanHalt handle surprise remove */
goto error;
if (pWb35Tx->tx_halt)
goto error;
- // The URB is completed, check the result
+ /* The URB is completed, check the result */
if (pWb35Tx->EP4VM_status != 0) {
printk("URB submission failed\n");
pWb35Tx->EP4vm_state = VM_STOP;
@@ -64,43 +65,42 @@ error:
static void Wb35Tx(struct wbsoft_priv *adapter)
{
- struct hw_data * pHwData = &adapter->sHwData;
+ struct hw_data *pHwData = &adapter->sHwData;
struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
u8 *pTxBufferAddress;
struct wb35_mds *pMds = &adapter->Mds;
- struct urb * pUrb = (struct urb *)pWb35Tx->Tx4Urb;
- int retv;
+ struct urb *pUrb = (struct urb *)pWb35Tx->Tx4Urb;
+ int retv;
u32 SendIndex;
-
if (pHwData->SurpriseRemove)
goto cleanup;
if (pWb35Tx->tx_halt)
goto cleanup;
- // Ownership checking
+ /* Ownership checking */
SendIndex = pWb35Tx->TxSendIndex;
- if (!pMds->TxOwner[SendIndex]) //No more data need to be sent, return immediately
+ /* No more data need to be sent, return immediately */
+ if (!pMds->TxOwner[SendIndex])
goto cleanup;
pTxBufferAddress = pWb35Tx->TxBuffer[SendIndex];
- //
- // Issuing URB
- //
+
+ /* Issuing URB */
usb_fill_bulk_urb(pUrb, pHwData->udev,
usb_sndbulkpipe(pHwData->udev, 4),
- pTxBufferAddress, pMds->TxBufferSize[ SendIndex ],
+ pTxBufferAddress, pMds->TxBufferSize[SendIndex],
Wb35Tx_complete, adapter);
pWb35Tx->EP4vm_state = VM_RUNNING;
retv = usb_submit_urb(pUrb, GFP_ATOMIC);
- if (retv<0) {
+ if (retv < 0) {
printk("EP4 Tx Irp sending error\n");
goto cleanup;
}
- // Check if driver needs issue Irp for EP2
+ /* Check if driver needs issue Irp for EP2 */
pWb35Tx->TxFillCount += pMds->TxCountInBuffer[SendIndex];
if (pWb35Tx->TxFillCount > 12)
Wb35Tx_EP2VM_start(adapter);
@@ -115,10 +115,10 @@ static void Wb35Tx(struct wbsoft_priv *adapter)
void Wb35Tx_start(struct wbsoft_priv *adapter)
{
- struct hw_data * pHwData = &adapter->sHwData;
+ struct hw_data *pHwData = &adapter->sHwData;
struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
- // Allow only one thread to run into function
+ /* Allow only one thread to run into function */
if (atomic_inc_return(&pWb35Tx->TxFireCounter) == 1) {
pWb35Tx->EP4vm_state = VM_RUNNING;
Wb35Tx(adapter);
@@ -126,7 +126,7 @@ void Wb35Tx_start(struct wbsoft_priv *adapter)
atomic_dec(&pWb35Tx->TxFireCounter);
}
-unsigned char Wb35Tx_initial(struct hw_data * pHwData)
+unsigned char Wb35Tx_initial(struct hw_data *pHwData)
{
struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
@@ -135,54 +135,50 @@ unsigned char Wb35Tx_initial(struct hw_data * pHwData)
return false;
pWb35Tx->Tx2Urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!pWb35Tx->Tx2Urb)
- {
- usb_free_urb( pWb35Tx->Tx4Urb );
+ if (!pWb35Tx->Tx2Urb) {
+ usb_free_urb(pWb35Tx->Tx4Urb);
return false;
}
return true;
}
-//======================================================
-void Wb35Tx_stop(struct hw_data * pHwData)
+void Wb35Tx_stop(struct hw_data *pHwData)
{
struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
- // Trying to canceling the Trp of EP2
+ /* Try to cancel the Trp of EP2 */
if (pWb35Tx->EP2vm_state == VM_RUNNING)
- usb_unlink_urb( pWb35Tx->Tx2Urb ); // Only use unlink, let Wb35Tx_destrot to free them
+ /* Only use unlink, let Wb35Tx_destroy free them */
+ usb_unlink_urb(pWb35Tx->Tx2Urb);
pr_debug("EP2 Tx stop\n");
- // Trying to canceling the Irp of EP4
+ /* Try to cancel the Irp of EP4 */
if (pWb35Tx->EP4vm_state == VM_RUNNING)
- usb_unlink_urb( pWb35Tx->Tx4Urb ); // Only use unlink, let Wb35Tx_destrot to free them
+ /* Only use unlink, let Wb35Tx_destroy free them */
+ usb_unlink_urb(pWb35Tx->Tx4Urb);
pr_debug("EP4 Tx stop\n");
}
-//======================================================
-void Wb35Tx_destroy(struct hw_data * pHwData)
+void Wb35Tx_destroy(struct hw_data *pHwData)
{
struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
- // Wait for VM stop
+ /* Wait for VM stop */
do {
- msleep(10); // Delay for waiting function enter 940623.1.a
- } while( (pWb35Tx->EP2vm_state != VM_STOP) && (pWb35Tx->EP4vm_state != VM_STOP) );
- msleep(10); // Delay for waiting function enter 940623.1.b
-
- if (pWb35Tx->Tx4Urb)
- usb_free_urb( pWb35Tx->Tx4Urb );
+ msleep(10); /* Delay for waiting function enter 940623.1.a */
+ } while ((pWb35Tx->EP2vm_state != VM_STOP) && (pWb35Tx->EP4vm_state != VM_STOP));
+ msleep(10); /* Delay for waiting function enter 940623.1.b */
- if (pWb35Tx->Tx2Urb)
- usb_free_urb( pWb35Tx->Tx2Urb );
+ usb_free_urb(pWb35Tx->Tx4Urb);
+ usb_free_urb(pWb35Tx->Tx2Urb);
pr_debug("Wb35Tx_destroy OK\n");
}
void Wb35Tx_CurrentTime(struct wbsoft_priv *adapter, u32 TimeCount)
{
- struct hw_data * pHwData = &adapter->sHwData;
+ struct hw_data *pHwData = &adapter->sHwData;
struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
unsigned char Trigger = false;
@@ -199,46 +195,45 @@ void Wb35Tx_CurrentTime(struct wbsoft_priv *adapter, u32 TimeCount)
static void Wb35Tx_EP2VM(struct wbsoft_priv *adapter);
-static void Wb35Tx_EP2VM_complete(struct urb * pUrb)
+static void Wb35Tx_EP2VM_complete(struct urb *pUrb)
{
struct wbsoft_priv *adapter = pUrb->context;
- struct hw_data * pHwData = &adapter->sHwData;
+ struct hw_data *pHwData = &adapter->sHwData;
struct T02_descriptor T02, TSTATUS;
struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
- u32 * pltmp = (u32 *)pWb35Tx->EP2_buf;
+ u32 *pltmp = (u32 *)pWb35Tx->EP2_buf;
u32 i;
u16 InterruptInLength;
-
- // Variable setting
+ /* Variable setting */
pWb35Tx->EP2vm_state = VM_COMPLETED;
pWb35Tx->EP2VM_status = pUrb->status;
- // For Linux 2.4. Interrupt will always trigger
- if (pHwData->SurpriseRemove) // Let WbWlanHalt to handle surprise remove
+ /* For Linux 2.4. Interrupt will always trigger */
+ if (pHwData->SurpriseRemove) /* Let WbWlanHalt handle surprise remove */
goto error;
if (pWb35Tx->tx_halt)
goto error;
- //The Urb is completed, check the result
+ /* The Urb is completed, check the result */
if (pWb35Tx->EP2VM_status != 0) {
printk("EP2 IoCompleteRoutine return error\n");
- pWb35Tx->EP2vm_state= VM_STOP;
+ pWb35Tx->EP2vm_state = VM_STOP;
goto error;
}
- // Update the Tx result
+ /* Update the Tx result */
InterruptInLength = pUrb->actual_length;
- // Modify for minimum memory access and DWORD alignment.
- T02.value = cpu_to_le32(pltmp[0]) >> 8; // [31:8] -> [24:0]
- InterruptInLength -= 1;// 20051221.1.c Modify the follow for more stable
- InterruptInLength >>= 2; // InterruptInLength/4
+ /* Modify for minimum memory access and DWORD alignment. */
+ T02.value = cpu_to_le32(pltmp[0]) >> 8; /* [31:8] -> [24:0] */
+ InterruptInLength -= 1; /* 20051221.1.c Modify the follow for more stable */
+ InterruptInLength >>= 2; /* InterruptInLength/4 */
for (i = 1; i <= InterruptInLength; i++) {
T02.value |= ((cpu_to_le32(pltmp[i]) & 0xff) << 24);
- TSTATUS.value = T02.value; //20061009 anson's endian
- Mds_SendComplete( adapter, &TSTATUS );
+ TSTATUS.value = T02.value; /* 20061009 anson's endian */
+ Mds_SendComplete(adapter, &TSTATUS);
T02.value = cpu_to_le32(pltmp[i]) >> 8;
}
@@ -250,10 +245,10 @@ error:
static void Wb35Tx_EP2VM(struct wbsoft_priv *adapter)
{
- struct hw_data * pHwData = &adapter->sHwData;
+ struct hw_data *pHwData = &adapter->sHwData;
struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
- struct urb * pUrb = (struct urb *)pWb35Tx->Tx2Urb;
- u32 * pltmp = (u32 *)pWb35Tx->EP2_buf;
+ struct urb *pUrb = (struct urb *)pWb35Tx->Tx2Urb;
+ u32 *pltmp = (u32 *)pWb35Tx->EP2_buf;
int retv;
if (pHwData->SurpriseRemove)
@@ -262,11 +257,10 @@ static void Wb35Tx_EP2VM(struct wbsoft_priv *adapter)
if (pWb35Tx->tx_halt)
goto error;
- //
- // Issuing URB
- //
- usb_fill_int_urb( pUrb, pHwData->udev, usb_rcvintpipe(pHwData->udev,2),
- pltmp, MAX_INTERRUPT_LENGTH, Wb35Tx_EP2VM_complete, adapter, 32);
+ /* Issuing URB */
+ usb_fill_int_urb(pUrb, pHwData->udev, usb_rcvintpipe(pHwData->udev, 2),
+ pltmp, MAX_INTERRUPT_LENGTH, Wb35Tx_EP2VM_complete,
+ adapter, 32);
pWb35Tx->EP2vm_state = VM_RUNNING;
retv = usb_submit_urb(pUrb, GFP_ATOMIC);
@@ -284,10 +278,10 @@ error:
void Wb35Tx_EP2VM_start(struct wbsoft_priv *adapter)
{
- struct hw_data * pHwData = &adapter->sHwData;
+ struct hw_data *pHwData = &adapter->sHwData;
struct wb35_tx *pWb35Tx = &pHwData->Wb35Tx;
- // Allow only one thread to run into function
+ /* Allow only one thread to run into function */
if (atomic_inc_return(&pWb35Tx->TxResultCount) == 1) {
pWb35Tx->EP2vm_state = VM_RUNNING;
Wb35Tx_EP2VM(adapter);
diff --git a/drivers/staging/winbond/wb35tx_s.h b/drivers/staging/winbond/wb35tx_s.h
index f70f43395591..715f87d6ac5b 100644
--- a/drivers/staging/winbond/wb35tx_s.h
+++ b/drivers/staging/winbond/wb35tx_s.h
@@ -3,45 +3,37 @@
#include "mds_s.h"
-//====================================
-// IS89C35 Tx related definition
-//====================================
-#define TX_INTERFACE 0 // Interface 1
-#define TX_PIPE 3 // endpoint 4
-#define TX_INTERRUPT 1 // endpoint 2
-#define MAX_INTERRUPT_LENGTH 64 // It must be 64 for EP2 hardware
-
-
-
-//====================================
-// Internal variable for module
-//====================================
-
+/* IS89C35 Tx related definition */
+#define TX_INTERFACE 0 /* Interface 1 */
+#define TX_PIPE 3 /* Endpoint 4 */
+#define TX_INTERRUPT 1 /* Endpoint 2 */
+#define MAX_INTERRUPT_LENGTH 64 /* It must be 64 for EP2 hardware */
+/* Internal variable for module */
struct wb35_tx {
- // For Tx buffer
+ /* For Tx buffer */
u8 TxBuffer[ MAX_USB_TX_BUFFER_NUMBER ][ MAX_USB_TX_BUFFER ];
- // For Interrupt pipe
+ /* For Interrupt pipe */
u8 EP2_buf[MAX_INTERRUPT_LENGTH];
- atomic_t TxResultCount;// For thread control of EP2 931130.4.m
- atomic_t TxFireCounter;// For thread control of EP4 931130.4.n
- u32 ByteTransfer;
+ atomic_t TxResultCount; /* For thread control of EP2 931130.4.m */
+ atomic_t TxFireCounter; /* For thread control of EP4 931130.4.n */
+ u32 ByteTransfer;
- u32 TxSendIndex;// The next index of Mds array to be sent
- u32 EP2vm_state; // for EP2vm state
- u32 EP4vm_state; // for EP4vm state
- u32 tx_halt; // Stopping VM
+ u32 TxSendIndex; /* The next index of Mds array to be sent */
+ u32 EP2vm_state; /* for EP2vm state */
+ u32 EP4vm_state; /* for EP4vm state */
+ u32 tx_halt; /* Stopping VM */
- struct urb * Tx4Urb;
- struct urb * Tx2Urb;
+ struct urb *Tx4Urb;
+ struct urb *Tx2Urb;
int EP2VM_status;
int EP4VM_status;
- u32 TxFillCount; // 20060928
- u32 TxTimer; // 20060928 Add if sending packet not great than 13
+ u32 TxFillCount; /* 20060928 */
+ u32 TxTimer; /* 20060928 Add if sending packet is greater than 13 */
};
#endif
diff --git a/drivers/staging/wlags49_h2/README.ubuntu b/drivers/staging/wlags49_h2/README.ubuntu
index 5f1cfb8fd427..bfad7dc7725f 100644
--- a/drivers/staging/wlags49_h2/README.ubuntu
+++ b/drivers/staging/wlags49_h2/README.ubuntu
@@ -46,12 +46,12 @@ If you have a card using the HERMES II.5 chip you have to make
changes to the Makefile and uncomment -DHERMES25. This will build
driver wlags49_h25_cs.
-Note: You can detemine the type with command "pccardctrl info"
+Note: You can determine the type with command "pccardctrl info"
MANIFID: 0156,0002 = HERMES - not supported by this driver
MANIFID: 0156,0003 = HERMES II (Wireless B)
MANIFID: 0156,0004 = HERMES II.5 (Wireless B/G)
-After succesfull compile type command
+After successful compile type command
sudo make install
@@ -81,7 +81,7 @@ The linux driver files (wl_xxxx.c) are changed in the following ways:
- Addaptations of Andrey Borzenkov applied to 7.22 source
- Alterations to avoid most HCF_ASSERTs
-- Switching interrupts off and on in the HCF
--- Bugfixes, things that were aparently wrong like reporting link status
+-- Bugfixes, things that were apparently wrong like reporting link status
change which checked a variable that was not changed in HCF anymore.
-- Used on WEP but setting keys via SIOCSIWENCODEEXT was not supported
-- Recovery actions added
@@ -93,7 +93,7 @@ have to "open" the device first to get a handle and after "close" no
changed; the former ioctl functions are now called before "open" and
after "close", which was not expected. One of the problems was enable/
disable of interrupts in the HCF. Interrupt handling starts at "open"
-so if a former "ioctl" routinge is called before "open" or after "close"
+so if a former "ioctl" routine is called before "open" or after "close"
then nothing should be done with interrupt switching in the HCF. Once
this was solved most HCF_ASSERTS went away.
@@ -120,8 +120,8 @@ include the man page. Even though setting parameters on the module
does not work anymore but it provides some information about all the
settings.
-I have not have personal contact with Agere, but others have. Agere
-agreed to make their software available under the BSD licence.
+I have no personal contact with Agere, but others have. Agere
+agreed to make their software available under the BSD license.
This driver is based on the 7.22 version.
The following was mailed by Agere to Andrey Borzenkov about this:
diff --git a/drivers/staging/wlags49_h2/TODO b/drivers/staging/wlags49_h2/TODO
index 94032b6ac2b5..f1a45611b236 100644
--- a/drivers/staging/wlags49_h2/TODO
+++ b/drivers/staging/wlags49_h2/TODO
@@ -1,4 +1,4 @@
-First of all, the best thing would be that this driver becomes obsolte by
+First of all, the best thing would be that this driver becomes obsolete by
adding support for Hermes II and Hermes II.5 cards to the existing orinoco
driver. The orinoco driver currently only supports Hermes I based cards.
Since this will not happen by magic and has not happened until now this
@@ -10,11 +10,11 @@ list.
TODO:
- verify against a Hermes II.5 card
- - verify with WPA encription (both with H2 and H2.5 cards)
+ - verify with WPA encryption (both with H2 and H2.5 cards)
- sometimes the card does not initialize correctly, retry mechanisms
- are build in to catch most cases but not all
+ are built in to catch most cases but not all
- once the driver runs it is very stable, but I have the impression
- some the crittical sections take to long
+ that some of the critical sections take some time.
- the driver is split into a Hermes II and a Hermes II.5 part, it
would be nice to handle both with one module instead of two
- review by the wireless developer community
@@ -25,7 +25,7 @@ TODO:
DONE:
- verified against a Hermes II card (Thomson Speedtouch 110 PCMCIA
card)
- - verified with WEP encription
+ - verified with WEP encryption
Please send any patches or complaints about this driver to Greg
Kroah-Hartman <greg@kroah.com> and Cc: Henk de Groot <pe1dnn@amsat.org>
diff --git a/drivers/staging/wlags49_h2/hcf.c b/drivers/staging/wlags49_h2/hcf.c
index 423544634ae5..4aac26d486f1 100644
--- a/drivers/staging/wlags49_h2/hcf.c
+++ b/drivers/staging/wlags49_h2/hcf.c
@@ -508,7 +508,7 @@ HCF_STATIC hcf_16* BASED xxxx[ ] = {
* - HCF_ACT_INT_FORCE_ON enable interrupt generation by WaveLAN NIC
* - HCF_ACT_INT_OFF disable interrupt generation by WaveLAN NIC
* - HCF_ACT_INT_ON compensate 1 HCF_ACT_INT_OFF, enable interrupt generation if balance reached
- * - HCF_ACT_PRS_SCAN Hermes Probe Respons Scan (F102) command
+ * - HCF_ACT_PRS_SCAN Hermes Probe Response Scan (F102) command
* - HCF_ACT_RX_ACK acknowledge non-DMA receiver to Hermes
* - HCF_ACT_SCAN Hermes Inquire Scan (F101) command (non-WARP only)
* - HCF_ACT_SLEEP DDS Sleep request
@@ -571,7 +571,7 @@ HCF_STATIC hcf_16* BASED xxxx[ ] = {
* The F/W is wokenup by the HCF when the NIC Interrupts mode are disabled, i.e. at the first HCF_ACT_INT_OFF
* after going into sleep.
*
- * The following Miscellanuous actions are defined:
+ * The following Miscellaneous actions are defined:
*
* o HCF_ACT_RX_ACK: Receiver Acknowledgement (non-DMA, non-USB mode only)
* Acking the receiver, frees the NIC memory used to hold the Rx frame and allows the F/W to
@@ -579,7 +579,7 @@ HCF_STATIC hcf_16* BASED xxxx[ ] = {
* If the MSF does not need access (any longer) to the current frame, e.g. because it is rejected based on the
* look ahead or copied to another buffer, the receiver may be acked. Acking earlier is assumed to have the
* potential of improving the performance.
- * If the MSF does not explitly ack te receiver, the acking is done implicitly if:
+ * If the MSF does not explicitly ack the receiver, the acking is done implicitly if:
* - the received frame fits in the look ahead buffer, by the hcf_service_nic call that reported the Rx frame
* - if not in the above step, by hcf_rcv_msg (assuming hcf_rcv_msg is called)
* - if neither of the above implicit acks nor an explicit ack by the MSF, by the first hcf_service_nic after
@@ -591,9 +591,9 @@ HCF_STATIC hcf_16* BASED xxxx[ ] = {
* The Inquire Tallies command requests the F/W to provide its current set of tallies.
* See also hcf_get_info with CFG_TALLIES as parameter.
*
- * o HCF_ACT_PRS_SCAN: Inquire Probe Respons Scan command
+ * o HCF_ACT_PRS_SCAN: Inquire Probe Response Scan command
* This command is only operational if the F/W is enabled.
- * The Probe Respons Scan command starts a scan sequence.
+ * The Probe Response Scan command starts a scan sequence.
* The HCF puts the result of this action in an MSF defined buffer (see CFG_RID_LOG_STRCT).
*
* o HCF_ACT_SCAN: Inquire Scan command
@@ -606,7 +606,7 @@ HCF_STATIC hcf_16* BASED xxxx[ ] = {
* - NIC interrupts are not disabled while required by parameter action.
* - an invalid code is specified in parameter action.
* - HCF_ACT_INT_ON commands outnumber the HCF_ACT_INT_OFF commands.
- * - reentrancy, may be caused by calling hcf_functions without adequate protection against NIC interrupts or
+ * - reentrancy, may be caused by calling hcf_functions without adequate protection against NIC interrupts or
* multi-threading
*
* - Since the HCF does not maintain status information relative to the F/W enabled state, it is not asserted
@@ -625,7 +625,7 @@ HCF_STATIC hcf_16* BASED xxxx[ ] = {
* change in HREG_EV_STAT matching a bit in HREG_INT_EN, i.e. not if invoked as result of another device
* generating an interrupt on the shared interrupt line.
* Note 1: it has been observed that under certain adverse conditions on certain platforms the writing of
- * HREG_INT_EN can apparently fail, therefor it is paramount that HREG_INT_EN is written again with 0 for
+ * HREG_INT_EN can apparently fail, therefore it is paramount that HREG_INT_EN is written again with 0 for
* each and every call to HCF_ACT_INT_OFF.
* Note 2: it has been observed that under certain H/W & S/W architectures this logic is called when there is
* no NIC at all. To cater for this, the value of HREG_INT_EN is validated. If the unused bit 0x0100 is set,
@@ -902,7 +902,7 @@ hcf_action( IFBP ifbp, hcf_16 action )
* - A command other than Continue, Enable, Disable, Connect or Disconnect is given.
* - An invalid combination of the subfields is given or a bit outside the subfields is given.
* - any return code besides HCF_SUCCESS.
- * - reentrancy, may be caused by calling a hcf_function without adequate protection against NIC interrupts or
+ * - reentrancy, may be caused by calling a hcf_function without adequate protection against NIC interrupts or
* multi-threading
*
*.DIAGRAM
@@ -1030,7 +1030,7 @@ hcf_cntl( IFBP ifbp, hcf_16 cmd )
* hcf_connect passes the MSF-defined location of the IFB to the HCF and grants or revokes access right for the
* HCF to the IFB. Revoking is done by specifying HCF_DISCONNECT rather than an I/O address for the parameter
* io_base. Every call of hcf_connect in "connect" mode, must eventually be followed by a call of hcf_connect
- * in "disconnect" mode. Clalling hcf_connect in "connect"/"disconnect" mode can not be nested.
+ * in "disconnect" mode. Calling hcf_connect in "connect"/"disconnect" mode can not be nested.
* The IFB address must be used as a handle with all subsequent HCF-function calls and the HCF uses the IFB
* address as a handle when it performs a call(back) of an MSF-function (i.e. msf_assert).
*
@@ -1058,7 +1058,7 @@ hcf_cntl( IFBP ifbp, hcf_16 cmd )
* specification for S/W reset
* Note 2: it turns out that on some H/W constellations, the clock to access the EEProm is not lowered
* to an appropriate frequency by HREG_IO_SRESET. By giving an HCMD_INI first, this problem is worked around.
- *2b: Experimentally it is determined over a wide range of F/W versions that waiting for the for Cmd bit in
+ *2b: Experimentally it is determined over a wide range of F/W versions that are waiting for the for Cmd bit in
* Ev register gives a workable strategy. The available documentation does not give much clues.
*4: clear and initialize the IFB
* The HCF house keeping info is designed such that zero is the appropriate initial value for as much as
diff --git a/drivers/staging/wlags49_h2/hcfcfg.h b/drivers/staging/wlags49_h2/hcfcfg.h
index 39fb4d326f65..869b5c343a86 100644
--- a/drivers/staging/wlags49_h2/hcfcfg.h
+++ b/drivers/staging/wlags49_h2/hcfcfg.h
@@ -21,7 +21,7 @@
* hcfcfg.tpl list all #defines which must be specified to:
* adjust the HCF functions defined in HCF.C to the characteristics of a specific environment
* o maximum sizes for messages
-* o Endianess
+* o Endianness
* Compiler specific macros
* o port I/O macros
* o type definitions
diff --git a/drivers/staging/wlags49_h2/hcfdef.h b/drivers/staging/wlags49_h2/hcfdef.h
index 30744e194a23..74c0f713c57e 100644
--- a/drivers/staging/wlags49_h2/hcfdef.h
+++ b/drivers/staging/wlags49_h2/hcfdef.h
@@ -652,15 +652,15 @@ err: you used an invalid bitmask;
#if 0 //get compiler going
#if HCF_EX_INT_TICK != HREG_EV_TICK
;? out dated checking
-err: someone redefined these macros while the implemenation assumes they are equal;
+err: someone redefined these macros while the implementation assumes they are equal;
#endif
#if HCF_EX_INT_TX_OK != HFS_TX_CNTL_TX_OK || HFS_TX_CNTL_TX_OK != HREG_EV_TX_OK
;? out dated checking
-err: someone redefined these macros while the implemenation assumes they are equal;
+err: someone redefined these macros while the implementation assumes they are equal;
#endif
#if HCF_EX_INT_TX_EX != HFS_TX_CNTL_TX_EX || HFS_TX_CNTL_TX_EX != HREG_EV_TX_EX
;? out dated checking
-err: someone redefined these macros while the implemenation assumes they are equal;
+err: someone redefined these macros while the implementation assumes they are equal;
#endif
#endif // 0 get compiler going
diff --git a/drivers/staging/wlags49_h2/mdd.h b/drivers/staging/wlags49_h2/mdd.h
index 5c3515f31a16..0c914971c1ef 100644
--- a/drivers/staging/wlags49_h2/mdd.h
+++ b/drivers/staging/wlags49_h2/mdd.h
@@ -652,7 +652,7 @@ XX1( CFG_SCAN, SCAN_RS_STRCT, scan_result[32] ) /*Scan results *
#define CFG_CNF_WDS_ADDR6 0xFC16 //[AP] Port 6 MAC Adrs of corresponding WDS Link node
#define CFG_CNF_PM_MCAST_BUF 0xFC17 //[AP] Switch for PM buffereing of Multicast Messages
#define CFG_CNF_MCAST_PM_BUF CFG_CNF_PM_MCAST_BUF //name does not match H-II spec
-#define CFG_CNF_REJECT_ANY 0xFC18 //[AP] Switch for PM buffereing of Multicast Messages
+#define CFG_CNF_REJECT_ANY 0xFC18 //[AP] Switch for PM buffering of Multicast Messages
#define CFG_CNF_ENCRYPTION 0xFC20 //select en/de-cryption of Tx/Rx messages
#define CFG_CNF_AUTHENTICATION 0xFC21 //[STA] selects Authentication algorithm
@@ -844,13 +844,13 @@ XX1( CFG_SCAN, SCAN_RS_STRCT, scan_result[32] ) /*Scan results *
#define HCF_SUCCESS 0x00 // OK
-#define HCF_ERR_TIME_OUT 0x04 // Expected Hermes event did not occure in expected time
+#define HCF_ERR_TIME_OUT 0x04 // Expected Hermes event did not occur in expected time
#define HCF_ERR_NO_NIC 0x05 /* card not found (usually yanked away during hcfio_in_string
* Also: card is either absent or disabled while it should be neither */
#define HCF_ERR_LEN 0x08 /* buffer size insufficient
* - IFB_ConfigTable too small
* - hcf_get_info buffer has a size of 0 or 1 or less than needed
- * to accomodate all data
+ * to accommodate all data
* - hcf_put_info: CFG_DLNV_DATA exceeds intermediate
* buffer size */
#define HCF_ERR_INCOMP_PRI 0x09 // primary functions are not compatible
@@ -1004,7 +1004,7 @@ XX1( CFG_SCAN, SCAN_RS_STRCT, scan_result[32] ) /*Scan results *
#define CFG_CURRENT_LINK_STATUS 0x090B //Latest link status got through 0xF200 LinkEvent
/*============================================================ INFORMATION FRAMES =========================*/
-#define CFG_INFO_FRAME_MIN 0xF000 //lowest value representing an Informatio Frame
+#define CFG_INFO_FRAME_MIN 0xF000 //lowest value representing an Information Frame
#define CFG_TALLIES 0xF100 //Communications Tallies
#define CFG_SCAN 0xF101 //Scan results
diff --git a/drivers/staging/wlags49_h2/sta_h2.c b/drivers/staging/wlags49_h2/sta_h2.c
index f9a38523724c..00dffe2ed8f1 100644
--- a/drivers/staging/wlags49_h2/sta_h2.c
+++ b/drivers/staging/wlags49_h2/sta_h2.c
@@ -26,7 +26,7 @@
#include "hcfcfg.h" // to get hcf_16 etc defined as well as
- // possible settings which inluence mdd.h or dhf.h
+ // possible settings which influence mdd.h or dhf.h
#include "mdd.h" //to get COMP_ID_STA etc defined
#include "dhf.h" //used to be "fhfmem.h", to get memblock,plugrecord,
diff --git a/drivers/staging/wlags49_h2/sta_h25.c b/drivers/staging/wlags49_h2/sta_h25.c
index 86ca1cdd8497..5b6f670d8ef2 100644
--- a/drivers/staging/wlags49_h2/sta_h25.c
+++ b/drivers/staging/wlags49_h2/sta_h25.c
@@ -25,7 +25,7 @@
#include "hcfcfg.h" // to get hcf_16 etc defined as well as
- // possible settings which inluence mdd.h or dhf.h
+ // possible settings which influence mdd.h or dhf.h
#include "mdd.h" //to get COMP_ID_STA etc defined
#include "dhf.h" //used to be "fhfmem.h", to get memblock,plugrecord,
diff --git a/drivers/staging/wlags49_h2/wl_enc.h b/drivers/staging/wlags49_h2/wl_enc.h
index 46629f3b112b..1804611276b8 100644
--- a/drivers/staging/wlags49_h2/wl_enc.h
+++ b/drivers/staging/wlags49_h2/wl_enc.h
@@ -106,7 +106,7 @@ ENCSTRCT, *PENCSTRCT;
/*******************************************************************************
- * function prrottypes
+ * function prototypes
******************************************************************************/
int wl_wep_code( char *szCrypt, char *szDest, void *Data, int nLen );
diff --git a/drivers/staging/wlags49_h2/wl_if.h b/drivers/staging/wlags49_h2/wl_if.h
index 6a26130f5a3a..6d66dabf032c 100644
--- a/drivers/staging/wlags49_h2/wl_if.h
+++ b/drivers/staging/wlags49_h2/wl_if.h
@@ -95,7 +95,7 @@
// Manufacture ID: 0156,0003
// Lowest measurment for noise floor seen is value 54
// Highest signal strength in close proximity to the AP seen is value 118
-// Very good must be arround 100 (otherwise its never "full scale"
+// Very good must be around 100 (otherwise its never "full scale"
// All other constants are derrived from these. This makes the signal gauge
// work for me...
#define HCF_MIN_SIGNAL_LEVEL 54
diff --git a/drivers/staging/wlags49_h2/wl_internal.h b/drivers/staging/wlags49_h2/wl_internal.h
index 553601f48873..b23078164149 100644
--- a/drivers/staging/wlags49_h2/wl_internal.h
+++ b/drivers/staging/wlags49_h2/wl_internal.h
@@ -11,7 +11,7 @@
*
*------------------------------------------------------------------------------
*
- * Header for defintions and macros internal to the drvier.
+ * Header for definitions and macros internal to the drvier.
*
*------------------------------------------------------------------------------
*
@@ -838,7 +838,7 @@ typedef struct dma_strct
DESC_STRCT *rx_packet[NUM_RX_DESC];
DESC_STRCT *rx_reclaim_desc, *tx_reclaim_desc; // Descriptors for host-reclaim purposes (see HCF)
int tx_rsc_ind; // DMA Tx resource indicator is maintained in the MSF, not in the HCF
- int rx_rsc_ind; // Also added rx rsource indicator so that cleanup can be performed if alloc fails
+ int rx_rsc_ind; // Also added rx resource indicator so that cleanup can be performed if alloc fails
int status;
} DMA_STRCT;
diff --git a/drivers/staging/wlags49_h2/wl_main.c b/drivers/staging/wlags49_h2/wl_main.c
index 204107829577..f5f120a62460 100644
--- a/drivers/staging/wlags49_h2/wl_main.c
+++ b/drivers/staging/wlags49_h2/wl_main.c
@@ -63,7 +63,7 @@
* constant definitions
******************************************************************************/
-/* Allow support for calling system fcns to access F/W iamge file */
+/* Allow support for calling system fcns to access F/W image file */
#define __KERNEL_SYSCALLS__
/*******************************************************************************
@@ -128,7 +128,7 @@
#include <wl_pci.h>
#endif /* BUS_PCI */
/*******************************************************************************
- * macro defintions
+ * macro definitions
******************************************************************************/
#define VALID_PARAM(C) \
{ \
@@ -1163,7 +1163,7 @@ int rc;
CNV_INT_TO_LITTLE( lp->hcfCtx.IFB_FWIdentity.version_major ),
CNV_INT_TO_LITTLE( lp->hcfCtx.IFB_FWIdentity.version_minor ));
- /* now we wil get the MAC address of the card */
+ /* now we will get the MAC address of the card */
lp->ltvRecord.len = 4;
if ( CNV_INT_TO_LITTLE( lp->hcfCtx.IFB_FWIdentity.comp_id ) == COMP_ID_FW_AP ) {
lp->ltvRecord.typ = CFG_NIC_MAC_ADDR;
@@ -1374,7 +1374,7 @@ int wl_put_ltv_init( struct wl_private *lp )
lp->ltvRecord.len = 2;
lp->ltvRecord.typ = CFG_CNTL_OPT;
- /* The Card Services build must ALWAYS configure for 16-bit I/O. PCI or
+ /* The Card Services build must ALWAYS be configured for 16-bit I/O. PCI or
CardBus can be set to either 16/32 bit I/O, or Bus Master DMA, but only
for Hermes-2.5 */
#ifdef BUS_PCMCIA
@@ -1627,7 +1627,7 @@ int wl_put_ltv( struct wl_private *lp )
lp->ltvRecord.u.u16[0] = CNV_INT_TO_LITTLE( lp->TxRateControl[0] );
#endif // WARP
-//;?skip temporarily to see whether the RID or something else is the probelm hcf_status = hcf_put_info( &lp->hcfCtx, (LTVP)&( lp->ltvRecord ));
+//;?skip temporarily to see whether the RID or something else is the problem hcf_status = hcf_put_info( &lp->hcfCtx, (LTVP)&( lp->ltvRecord ));
DBG_TRACE( DbgInfo, "CFG_TX_RATE_CNTL 2.4GHz : 0x%04x\n",
lp->TxRateControl[0] );
@@ -2474,7 +2474,7 @@ void wl_resume(struct net_device *dev)
*
* DESCRIPTION:
*
- * This function perfroms a check on the device and calls wl_remove() if
+ * This function performs a check on the device and calls wl_remove() if
* necessary. This function can be used for all bus types, but exists mostly
* for the benefit of the Card Services driver, as there are times when
* wl_remove() does not get called.
@@ -2596,7 +2596,7 @@ int wl_enable( struct wl_private *lp )
lp->portState = WVLAN_PORT_STATE_ENABLED; //;?bad mnemonic, NIC iso PORT
#ifdef ENABLE_DMA
if ( lp->use_dma ) {
- wl_pci_dma_hcf_supply( lp ); //;?always succes?
+ wl_pci_dma_hcf_supply( lp ); //;?always successful?
}
#endif
}
@@ -2874,7 +2874,7 @@ int wl_mbx( struct wl_private *lp )
* DESCRIPTION:
*
* This function will perform the tedious task of endian translating all
- * fields withtin a mailbox message which need translating.
+ * fields within a mailbox message which need translating.
*
* PARAMETERS:
*
@@ -2989,7 +2989,7 @@ void wl_endian_translate_mailbox( ltv_t *ltv )
*
* DESCRIPTION:
*
- * This function will process the mailbox data.
+ * This function processes the mailbox data.
*
* PARAMETERS:
*
diff --git a/drivers/staging/wlags49_h2/wl_netdev.c b/drivers/staging/wlags49_h2/wl_netdev.c
index 824b85232353..fb421407e106 100644
--- a/drivers/staging/wlags49_h2/wl_netdev.c
+++ b/drivers/staging/wlags49_h2/wl_netdev.c
@@ -652,7 +652,6 @@ void wl_tx_timeout( struct net_device *dev )
wl_unlock( lp, &flags );
DBG_LEAVE( DbgInfo );
- return;
} // wl_tx_timeout
/*============================================================================*/
@@ -836,8 +835,7 @@ int wl_tx( struct sk_buff *skb, struct net_device *dev, int port )
txF->frame.port = port;
/* Move the frame to the txQ */
/* NOTE: Here's where we would do priority queueing */
- list_del( &( txF->node ));
- list_add( &( txF->node ), &( lp->txQ[0] ));
+ list_move(&(txF->node), &(lp->txQ[0]));
lp->txQ_count++;
if( lp->txQ_count >= DEFAULT_NUM_TX_FRAMES ) {
@@ -1252,7 +1250,7 @@ struct net_device * wl_device_alloc( void )
netif_stop_queue( dev );
- /* Allocate virutal devices for WDS support if needed */
+ /* Allocate virtual devices for WDS support if needed */
WL_WDS_DEVICE_ALLOC( lp );
DBG_LEAVE( DbgInfo );
@@ -1292,7 +1290,6 @@ void wl_device_dealloc( struct net_device *dev )
free_netdev( dev );
DBG_LEAVE( DbgInfo );
- return;
} // wl_device_dealloc
/*============================================================================*/
@@ -1547,7 +1544,6 @@ void wl_wds_device_alloc( struct wl_private *lp )
WL_WDS_NETIF_STOP_QUEUE( lp );
DBG_LEAVE( DbgInfo );
- return;
} // wl_wds_device_alloc
/*============================================================================*/
@@ -1593,7 +1589,6 @@ void wl_wds_device_dealloc( struct wl_private *lp )
}
DBG_LEAVE( DbgInfo );
- return;
} // wl_wds_device_dealloc
/*============================================================================*/
@@ -1604,7 +1599,7 @@ void wl_wds_device_dealloc( struct wl_private *lp )
* DESCRIPTION:
*
* Used to start the netif queues of all the "virtual" network devices
- * which repesent the WDS ports.
+ * which represent the WDS ports.
*
* PARAMETERS:
*
@@ -1629,8 +1624,6 @@ void wl_wds_netif_start_queue( struct wl_private *lp )
}
}
}
-
- return;
} // wl_wds_netif_start_queue
/*============================================================================*/
@@ -1641,7 +1634,7 @@ void wl_wds_netif_start_queue( struct wl_private *lp )
* DESCRIPTION:
*
* Used to stop the netif queues of all the "virtual" network devices
- * which repesent the WDS ports.
+ * which represent the WDS ports.
*
* PARAMETERS:
*
@@ -1666,8 +1659,6 @@ void wl_wds_netif_stop_queue( struct wl_private *lp )
}
}
}
-
- return;
} // wl_wds_netif_stop_queue
/*============================================================================*/
@@ -1678,7 +1669,7 @@ void wl_wds_netif_stop_queue( struct wl_private *lp )
* DESCRIPTION:
*
* Used to wake the netif queues of all the "virtual" network devices
- * which repesent the WDS ports.
+ * which represent the WDS ports.
*
* PARAMETERS:
*
@@ -1703,8 +1694,6 @@ void wl_wds_netif_wake_queue( struct wl_private *lp )
}
}
}
-
- return;
} // wl_wds_netif_wake_queue
/*============================================================================*/
@@ -1715,7 +1704,7 @@ void wl_wds_netif_wake_queue( struct wl_private *lp )
* DESCRIPTION:
*
* Used to signal the network layer that carrier is present on all of the
- * "virtual" network devices which repesent the WDS ports.
+ * "virtual" network devices which represent the WDS ports.
*
* PARAMETERS:
*
@@ -1738,8 +1727,6 @@ void wl_wds_netif_carrier_on( struct wl_private *lp )
}
}
}
-
- return;
} // wl_wds_netif_carrier_on
/*============================================================================*/
@@ -1750,7 +1737,7 @@ void wl_wds_netif_carrier_on( struct wl_private *lp )
* DESCRIPTION:
*
* Used to signal the network layer that carrier is NOT present on all of
- * the "virtual" network devices which repesent the WDS ports.
+ * the "virtual" network devices which represent the WDS ports.
*
* PARAMETERS:
*
@@ -1763,18 +1750,15 @@ void wl_wds_netif_carrier_on( struct wl_private *lp )
******************************************************************************/
void wl_wds_netif_carrier_off( struct wl_private *lp )
{
- int count;
- /*------------------------------------------------------------------------*/
+ int count;
- if( lp != NULL ) {
- for( count = 0; count < NUM_WDS_PORTS; count++ ) {
- if( lp->wds_port[count].is_registered ) {
- netif_carrier_off( lp->wds_port[count].dev );
- }
- }
- }
+ if(lp != NULL) {
+ for(count = 0; count < NUM_WDS_PORTS; count++) {
+ if(lp->wds_port[count].is_registered)
+ netif_carrier_off(lp->wds_port[count].dev);
+ }
+ }
- return;
} // wl_wds_netif_carrier_off
/*============================================================================*/
@@ -1810,22 +1794,19 @@ int wl_send_dma( struct wl_private *lp, struct sk_buff *skb, int port )
DBG_FUNC( "wl_send_dma" );
- if( lp == NULL )
- {
+ if( lp == NULL ) {
DBG_ERROR( DbgInfo, "Private adapter struct is NULL\n" );
return FALSE;
}
- if( lp->dev == NULL )
- {
+ if( lp->dev == NULL ) {
DBG_ERROR( DbgInfo, "net_device struct in wl_private is NULL\n" );
return FALSE;
}
/* AGAIN, ALL THE QUEUEING DONE HERE IN I/O MODE IS NOT PERFORMED */
- if( skb == NULL )
- {
+ if( skb == NULL ) {
DBG_WARNING (DbgInfo, "Nothing to send.\n");
return FALSE;
}
@@ -1835,8 +1816,7 @@ int wl_send_dma( struct wl_private *lp, struct sk_buff *skb, int port )
/* Get a free descriptor */
desc = wl_pci_dma_get_tx_packet( lp );
- if( desc == NULL )
- {
+ if( desc == NULL ) {
if( lp->netif_queue_on == TRUE ) {
netif_stop_queue( lp->dev );
WL_WDS_NETIF_STOP_QUEUE( lp );
@@ -1852,8 +1832,7 @@ int wl_send_dma( struct wl_private *lp, struct sk_buff *skb, int port )
desc_next = desc->next_desc_addr;
- if( desc_next->buf_addr == NULL )
- {
+ if( desc_next->buf_addr == NULL ) {
DBG_ERROR( DbgInfo, "DMA descriptor buf_addr is NULL\n" );
return FALSE;
}
diff --git a/drivers/staging/wlags49_h2/wl_pci.c b/drivers/staging/wlags49_h2/wl_pci.c
index 0b31b01bd490..a09c3ac793a2 100644
--- a/drivers/staging/wlags49_h2/wl_pci.c
+++ b/drivers/staging/wlags49_h2/wl_pci.c
@@ -223,7 +223,7 @@ int wl_adapter_init_module( void )
******************************************************************************/
void wl_adapter_cleanup_module( void )
{
- //;?how comes wl_adapter_cleanup_module is located in a seemingly pci specific module
+ //;?how come wl_adapter_cleanup_module is located in a seemingly pci specific module
DBG_FUNC( "wl_adapter_cleanup_module" );
DBG_ENTER( DbgInfo );
@@ -385,7 +385,7 @@ int wl_adapter_is_open( struct net_device *dev )
* DESCRIPTION:
*
* Registered in the pci_driver structure, this function is called when the
- * PCI subsystem finds a new PCI device which matches the infomation contained
+ * PCI subsystem finds a new PCI device which matches the information contained
* in the pci_device_id table.
*
* PARAMETERS:
@@ -424,7 +424,7 @@ int __devinit wl_pci_probe( struct pci_dev *pdev,
* DESCRIPTION:
*
* Registered in the pci_driver structure, this function is called when the
- * PCI subsystem detects that a PCI device which matches the infomation
+ * PCI subsystem detects that a PCI device which matches the information
* contained in the pci_device_id table has been removed.
*
* PARAMETERS:
@@ -899,7 +899,7 @@ int wl_pci_dma_free_tx_packet( struct pci_dev *pdev, struct wl_private *lp,
* DESCRIPTION:
*
* Allocates a single Rx packet, consisting of two descriptors and one
- * contiguous buffer. THe buffer starts with the hermes-specific header.
+ * contiguous buffer. The buffer starts with the hermes-specific header.
* One descriptor points at the start, the other at offset 0x3a of the
* buffer.
*
diff --git a/drivers/staging/wlags49_h2/wl_priv.c b/drivers/staging/wlags49_h2/wl_priv.c
index f30e5ee4bca3..87e1e4123126 100644
--- a/drivers/staging/wlags49_h2/wl_priv.c
+++ b/drivers/staging/wlags49_h2/wl_priv.c
@@ -225,7 +225,7 @@ int wvlan_uil_connect( struct uilreq *urq, struct wl_private *lp )
*
* DESCRIPTION:
*
- * Disonnect from the UIL after a request has been completed.
+ * Disconnect from the UIL after a request has been completed.
*
* PARAMETERS:
*
diff --git a/drivers/staging/wlags49_h2/wl_profile.c b/drivers/staging/wlags49_h2/wl_profile.c
index 0e49272bc7a8..beabf5916df7 100644
--- a/drivers/staging/wlags49_h2/wl_profile.c
+++ b/drivers/staging/wlags49_h2/wl_profile.c
@@ -910,7 +910,7 @@ int parse_mac_address(char *value, u_char *byte_array)
memset(byte_field, '\0', 3);
while (value[value_offset] != '\0') {
- /* Skip over the colon chars seperating the bytes, if they exist */
+ /* Skip over the colon chars separating the bytes, if they exist */
if (value[value_offset] == ':') {
value_offset++;
continue;
diff --git a/drivers/staging/wlags49_h2/wl_version.h b/drivers/staging/wlags49_h2/wl_version.h
index 3deacfac9d25..037b5266428c 100644
--- a/drivers/staging/wlags49_h2/wl_version.h
+++ b/drivers/staging/wlags49_h2/wl_version.h
@@ -152,7 +152,7 @@ err: define bus type;
/*******************************************************************************
- * bus architechture specific defines, includes, etc.
+ * bus architecture specific defines, includes, etc.
******************************************************************************/
/*
* There doesn't seem to be a difference for PCMCIA and PCI anymore, at least
diff --git a/drivers/staging/wlags49_h2/wl_wext.c b/drivers/staging/wlags49_h2/wl_wext.c
index 7ff0a108da13..f553366cccc5 100644
--- a/drivers/staging/wlags49_h2/wl_wext.c
+++ b/drivers/staging/wlags49_h2/wl_wext.c
@@ -62,6 +62,7 @@
#include <linux/if_arp.h>
#include <linux/ioport.h>
#include <linux/delay.h>
+#include <linux/etherdevice.h>
#include <asm/uaccess.h>
#include <debug.h>
@@ -173,7 +174,7 @@ static int hermes_clear_tkip_keys(ltv_t *ltv, u16 key_idx, u8 *addr)
switch (key_idx) {
case 0:
- if (memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) {
+ if (!is_broadcast_ether_addr(addr)) {
ltv->len = 7;
ltv->typ = CFG_REMOVE_TKIP_MAPPED_KEY;
memcpy(&ltv->u.u8[0], addr, ETH_ALEN);
@@ -605,7 +606,7 @@ retry:
if (retries < 10) {
retries++;
- /* Holding the lock too long, make a gap to allow other processes */
+ /* Holding the lock too long, makes a gap to allow other processes */
wl_unlock(lp, &flags);
wl_lock( lp, &flags );
@@ -617,7 +618,7 @@ retry:
goto out_unlock;
}
- /* Holding the lock too long, make a gap to allow other processes */
+ /* Holding the lock too long, makes a gap to allow other processes */
wl_unlock(lp, &flags);
wl_lock( lp, &flags );
@@ -630,7 +631,7 @@ retry:
}
}
- /* Holding the lock too long, make a gap to allow other processes */
+ /* Holding the lock too long, makes a gap to allow other processes */
wl_unlock(lp, &flags);
wl_lock( lp, &flags );
@@ -693,7 +694,7 @@ retry:
/* Encryption */
- /* Holding the lock too long, make a gap to allow other processes */
+ /* Holding the lock too long, makes a gap to allow other processes */
wl_unlock(lp, &flags);
wl_lock( lp, &flags );
@@ -720,7 +721,7 @@ retry:
// Retry Limits and Lifetime - NOT SUPPORTED
- /* Holding the lock too long, make a gap to allow other processes */
+ /* Holding the lock too long, makes a gap to allow other processes */
wl_unlock(lp, &flags);
wl_lock( lp, &flags );
@@ -2595,7 +2596,7 @@ static int wireless_set_scan(struct net_device *dev, struct iw_request_info *inf
int retries = 0;
/*------------------------------------------------------------------------*/
- //;? Note: shows results as trace, retruns always 0 unless BUSY
+ //;? Note: shows results as trace, returns always 0 unless BUSY
DBG_FUNC( "wireless_set_scan" );
DBG_ENTER( DbgInfo );
@@ -2645,7 +2646,7 @@ retry:
DBG_TRACE( DbgInfo, "CFG_SCAN_CHANNEL result : 0x%x\n", status );
- // Holding the lock too long, make a gap to allow other processes
+ // Holding the lock too long, makes a gap to allow other processes
wl_unlock(lp, &flags);
wl_lock( lp, &flags );
@@ -2656,7 +2657,7 @@ retry:
DBG_TRACE( DbgInfo, "Reset card to recover, attempt: %d\n", retries );
wl_reset( dev );
- // Holding the lock too long, make a gap to allow other processes
+ // Holding the lock too long, makes a gap to allow other processes
wl_unlock(lp, &flags);
wl_lock( lp, &flags );
@@ -2673,7 +2674,7 @@ retry:
status = hcf_put_info( &( lp->hcfCtx ), (LTVP)&( lp->ltvRecord ));
- // Holding the lock too long, make a gap to allow other processes
+ // Holding the lock too long, makes a gap to allow other processes
wl_unlock(lp, &flags);
wl_lock( lp, &flags );
@@ -2681,7 +2682,7 @@ retry:
/* Initiate the scan */
/* NOTE: Using HCF_ACT_SCAN has been removed, as using HCF_ACT_ACS_SCAN to
- retrieve probe responses must always be used to support WPA */
+ retrieve probe response must always be used to support WPA */
status = hcf_action( &( lp->hcfCtx ), HCF_ACT_ACS_SCAN );
if( status == HCF_SUCCESS ) {
@@ -3054,7 +3055,7 @@ static void flush_tx(struct wl_private *lp)
* Make sure that there is no data queued up in the firmware
* before setting the TKIP keys. If this check is not
* performed, some data may be sent out with incorrect MIC
- * and cause synchronizarion errors with the AP
+ * and cause synchronization errors with the AP
*/
/* Check every 1ms for 100ms */
for (count = 0; count < 100; count++) {
@@ -3654,7 +3655,7 @@ void wl_wext_event_ap( struct net_device *dev )
this event BEFORE sending the association event, as there are timing
issues with the hostap supplicant. The supplicant will attempt to process
an EAPOL-Key frame from an AP before receiving this information, which
- is required properly process the said frame. */
+ is required for a proper processed frame. */
wl_wext_event_assoc_ie( dev );
/* Get the BSSID */
diff --git a/drivers/staging/wlags49_h25/Kconfig b/drivers/staging/wlags49_h25/Kconfig
index bf5664a51cd4..dd8dc4d62c64 100644
--- a/drivers/staging/wlags49_h25/Kconfig
+++ b/drivers/staging/wlags49_h25/Kconfig
@@ -8,4 +8,4 @@ config WLAGS49_H25
Driver for wireless cards using Agere's HERMES II.5 chipset
which are identified with Manufacture ID: 0156,0004
The software is a modified version of wl_lkm_722_abg.tar.gz
- from the Agere Systems website, addapted for Ubuntu 9.04.
+ from the Agere Systems website, adapted for Ubuntu 9.04.
diff --git a/drivers/staging/wlags49_h25/TODO b/drivers/staging/wlags49_h25/TODO
index 94032b6ac2b5..ec71ad3245e4 100644
--- a/drivers/staging/wlags49_h25/TODO
+++ b/drivers/staging/wlags49_h25/TODO
@@ -1,4 +1,4 @@
-First of all, the best thing would be that this driver becomes obsolte by
+First of all, the best thing would be that this driver becomes obsolete by
adding support for Hermes II and Hermes II.5 cards to the existing orinoco
driver. The orinoco driver currently only supports Hermes I based cards.
Since this will not happen by magic and has not happened until now this
@@ -10,11 +10,11 @@ list.
TODO:
- verify against a Hermes II.5 card
- - verify with WPA encription (both with H2 and H2.5 cards)
+ - verify with WPA encryption (both with H2 and H2.5 cards)
- sometimes the card does not initialize correctly, retry mechanisms
are build in to catch most cases but not all
- once the driver runs it is very stable, but I have the impression
- some the crittical sections take to long
+ some the critical sections take to long
- the driver is split into a Hermes II and a Hermes II.5 part, it
would be nice to handle both with one module instead of two
- review by the wireless developer community
@@ -25,7 +25,7 @@ TODO:
DONE:
- verified against a Hermes II card (Thomson Speedtouch 110 PCMCIA
card)
- - verified with WEP encription
+ - verified with WEP encryption
Please send any patches or complaints about this driver to Greg
Kroah-Hartman <greg@kroah.com> and Cc: Henk de Groot <pe1dnn@amsat.org>
diff --git a/drivers/staging/wlan-ng/cfg80211.c b/drivers/staging/wlan-ng/cfg80211.c
index 0970127344e6..18c06a59c091 100644
--- a/drivers/staging/wlan-ng/cfg80211.c
+++ b/drivers/staging/wlan-ng/cfg80211.c
@@ -1,7 +1,7 @@
/* cfg80211 Interface for prism2_usb module */
-/* Prism2 channell/frequency/bitrate declarations */
+/* Prism2 channel/frequency/bitrate declarations */
static const struct ieee80211_channel prism2_channels[] = {
{ .center_freq = 2412 },
{ .center_freq = 2417 },
@@ -329,9 +329,9 @@ int prism2_get_station(struct wiphy *wiphy, struct net_device *dev,
int prism2_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
{
- struct net_device *dev = request->wdev->netdev;
+ struct net_device *dev;
struct prism2_wiphy_private *priv = wiphy_priv(wiphy);
- wlandevice_t *wlandev = dev->ml_priv;
+ wlandevice_t *wlandev;
struct p80211msg_dot11req_scan msg1;
struct p80211msg_dot11req_scan_results msg2;
struct cfg80211_bss *bss;
@@ -345,6 +345,9 @@ int prism2_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
if (!request)
return -EINVAL;
+ dev = request->wdev->netdev;
+ wlandev = dev->ml_priv;
+
if (priv->scan_request && priv->scan_request != request)
return -EBUSY;
@@ -499,7 +502,7 @@ int prism2_connect(struct wiphy *wiphy, struct net_device *dev,
goto exit;
}
- /* Set the authorisation */
+ /* Set the authorization */
if ((sme->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) ||
((sme->auth_type == NL80211_AUTHTYPE_AUTOMATIC) && !is_wep))
msg_join.authtype.data = P80211ENUM_authalg_opensystem;
diff --git a/drivers/staging/wlan-ng/hfa384x_usb.c b/drivers/staging/wlan-ng/hfa384x_usb.c
index 7843dfdaa3cf..f180c3d8b012 100644
--- a/drivers/staging/wlan-ng/hfa384x_usb.c
+++ b/drivers/staging/wlan-ng/hfa384x_usb.c
@@ -2140,11 +2140,7 @@ exit_proc:
----------------------------------------------------------------*/
int hfa384x_drvr_getconfig(hfa384x_t *hw, u16 rid, void *buf, u16 len)
{
- int result;
-
- result = hfa384x_dorrid_wait(hw, rid, buf, len);
-
- return result;
+ return hfa384x_dorrid_wait(hw, rid, buf, len);
}
/*----------------------------------------------------------------
@@ -3790,7 +3786,7 @@ static void hfa384x_ctlxout_callback(struct urb *urb)
#endif
if ((urb->status == -ESHUTDOWN) ||
(urb->status == -ENODEV) || (hw == NULL))
- goto done;
+ return;
retry:
spin_lock_irqsave(&hw->ctlxq.lock, flags);
@@ -3803,7 +3799,7 @@ retry:
*/
if (list_empty(&hw->ctlxq.active)) {
spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
- goto done;
+ return;
}
/*
@@ -3886,9 +3882,6 @@ delresp:
if (run_queue)
hfa384x_usbctlxq_run(hw);
-
-done:
- ;
}
/*----------------------------------------------------------------
@@ -3985,15 +3978,10 @@ static void hfa384x_usbctlx_resptimerfn(unsigned long data)
if (unlocked_usbctlx_cancel_async(hw, ctlx) == 0) {
spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
hfa384x_usbctlxq_run(hw);
- goto done;
+ return;
}
}
-
spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
-
-done:
- ;
-
}
/*----------------------------------------------------------------
@@ -4057,23 +4045,20 @@ static void hfa384x_usb_throttlefn(unsigned long data)
static int hfa384x_usbctlx_submit(hfa384x_t *hw, hfa384x_usbctlx_t *ctlx)
{
unsigned long flags;
- int ret;
spin_lock_irqsave(&hw->ctlxq.lock, flags);
if (hw->wlandev->hwremoved) {
spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
- ret = -ENODEV;
- } else {
- ctlx->state = CTLX_PENDING;
- list_add_tail(&ctlx->list, &hw->ctlxq.pending);
-
- spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
- hfa384x_usbctlxq_run(hw);
- ret = 0;
+ return -ENODEV;
}
- return ret;
+ ctlx->state = CTLX_PENDING;
+ list_add_tail(&ctlx->list, &hw->ctlxq.pending);
+ spin_unlock_irqrestore(&hw->ctlxq.lock, flags);
+ hfa384x_usbctlxq_run(hw);
+
+ return 0;
}
/*----------------------------------------------------------------
diff --git a/drivers/staging/wlan-ng/p80211conv.c b/drivers/staging/wlan-ng/p80211conv.c
index f53a27a2e3fe..3df753b51e89 100644
--- a/drivers/staging/wlan-ng/p80211conv.c
+++ b/drivers/staging/wlan-ng/p80211conv.c
@@ -559,17 +559,17 @@ void p80211skb_rxmeta_detach(struct sk_buff *skb)
/* Sanity checks */
if (skb == NULL) { /* bad skb */
pr_debug("Called w/ null skb.\n");
- goto exit;
+ return;
}
frmmeta = P80211SKB_FRMMETA(skb);
if (frmmeta == NULL) { /* no magic */
pr_debug("Called w/ bad frmmeta magic.\n");
- goto exit;
+ return;
}
rxmeta = frmmeta->rx;
if (rxmeta == NULL) { /* bad meta ptr */
pr_debug("Called w/ bad rxmeta ptr.\n");
- goto exit;
+ return;
}
/* Free rxmeta */
@@ -577,8 +577,6 @@ void p80211skb_rxmeta_detach(struct sk_buff *skb)
/* Clear skb->cb */
memset(skb->cb, 0, sizeof(skb->cb));
-exit:
- return;
}
/*----------------------------------------------------------------
@@ -660,5 +658,4 @@ void p80211skb_free(struct wlandevice *wlandev, struct sk_buff *skb)
else
printk(KERN_ERR "Freeing an skb (%p) w/ no frmmeta.\n", skb);
dev_kfree_skb(skb);
- return;
}
diff --git a/drivers/staging/wlan-ng/p80211netdev.c b/drivers/staging/wlan-ng/p80211netdev.c
index 0f51b4ab3631..750330f064f9 100644
--- a/drivers/staging/wlan-ng/p80211netdev.c
+++ b/drivers/staging/wlan-ng/p80211netdev.c
@@ -240,10 +240,7 @@ void p80211netdev_rx(wlandevice_t *wlandev, struct sk_buff *skb)
{
/* Enqueue for post-irq processing */
skb_queue_tail(&wlandev->nsd_rxq, skb);
-
tasklet_schedule(&wlandev->rx_bh);
-
- return;
}
/*----------------------------------------------------------------
@@ -464,7 +461,7 @@ failed:
/*----------------------------------------------------------------
* p80211knetdev_set_multicast_list
*
-* Called from higher lavers whenever there's a need to set/clear
+* Called from higher layers whenever there's a need to set/clear
* promiscuous mode or rewrite the multicast list.
*
* Arguments:
@@ -644,7 +641,7 @@ static int p80211knetdev_set_mac_address(netdevice_t *dev, void *addr)
p80211item_unk392_t *mibattr;
p80211item_pstr6_t *macaddr;
p80211item_uint32_t *resultcode;
- int result = 0;
+ int result;
/* If we're running, we don't allow MAC address changes */
if (netif_running(dev))
@@ -806,15 +803,13 @@ int wlan_setup(wlandevice_t *wlandev, struct device *physdev)
* Arguments:
* wlandev ptr to the wlandev structure for the
* interface.
-* Returns:
-* zero on success, non-zero otherwise.
* Call Context:
* Should be process thread. We'll assume it might be
* interrupt though. When we add support for statically
* compiled drivers, this function will be called in the
* context of the kernel startup code.
----------------------------------------------------------------*/
-int wlan_unsetup(wlandevice_t *wlandev)
+void wlan_unsetup(wlandevice_t *wlandev)
{
struct wireless_dev *wdev;
@@ -827,8 +822,6 @@ int wlan_unsetup(wlandevice_t *wlandev)
free_netdev(wlandev->netdev);
wlandev->netdev = NULL;
}
-
- return 0;
}
/*----------------------------------------------------------------
@@ -852,13 +845,7 @@ int wlan_unsetup(wlandevice_t *wlandev)
----------------------------------------------------------------*/
int register_wlandev(wlandevice_t *wlandev)
{
- int i = 0;
-
- i = register_netdev(wlandev->netdev);
- if (i)
- return i;
-
- return 0;
+ return register_netdev(wlandev->netdev);
}
/*----------------------------------------------------------------
diff --git a/drivers/staging/wlan-ng/p80211netdev.h b/drivers/staging/wlan-ng/p80211netdev.h
index 85884176b661..2fecca2302f4 100644
--- a/drivers/staging/wlan-ng/p80211netdev.h
+++ b/drivers/staging/wlan-ng/p80211netdev.h
@@ -235,7 +235,7 @@ int wep_encrypt(wlandevice_t *wlandev, u8 *buf, u8 *dst, u32 len, int keynum,
u8 *iv, u8 *icv);
int wlan_setup(wlandevice_t *wlandev, struct device *physdev);
-int wlan_unsetup(wlandevice_t *wlandev);
+void wlan_unsetup(wlandevice_t *wlandev);
int register_wlandev(wlandevice_t *wlandev);
int unregister_wlandev(wlandevice_t *wlandev);
void p80211netdev_rx(wlandevice_t *wlandev, struct sk_buff *skb);
diff --git a/drivers/staging/wlan-ng/p80211req.c b/drivers/staging/wlan-ng/p80211req.c
index 179194e7d2aa..cdfd808d6854 100644
--- a/drivers/staging/wlan-ng/p80211req.c
+++ b/drivers/staging/wlan-ng/p80211req.c
@@ -73,7 +73,7 @@
#include "p80211req.h"
static void p80211req_handlemsg(wlandevice_t *wlandev, struct p80211msg *msg);
-static int p80211req_mibset_mibget(wlandevice_t *wlandev,
+static void p80211req_mibset_mibget(wlandevice_t *wlandev,
struct p80211msg_dot11req_mibget *mib_msg,
int isget);
@@ -155,32 +155,29 @@ static void p80211req_handlemsg(wlandevice_t *wlandev, struct p80211msg *msg)
switch (msg->msgcode) {
case DIDmsg_lnxreq_hostwep:{
- struct p80211msg_lnxreq_hostwep *req =
- (struct p80211msg_lnxreq_hostwep *) msg;
- wlandev->hostwep &=
- ~(HOSTWEP_DECRYPT | HOSTWEP_ENCRYPT);
- if (req->decrypt.data == P80211ENUM_truth_true)
- wlandev->hostwep |= HOSTWEP_DECRYPT;
- if (req->encrypt.data == P80211ENUM_truth_true)
- wlandev->hostwep |= HOSTWEP_ENCRYPT;
-
- break;
- }
+ struct p80211msg_lnxreq_hostwep *req =
+ (struct p80211msg_lnxreq_hostwep *) msg;
+ wlandev->hostwep &=
+ ~(HOSTWEP_DECRYPT | HOSTWEP_ENCRYPT);
+ if (req->decrypt.data == P80211ENUM_truth_true)
+ wlandev->hostwep |= HOSTWEP_DECRYPT;
+ if (req->encrypt.data == P80211ENUM_truth_true)
+ wlandev->hostwep |= HOSTWEP_ENCRYPT;
+
+ break;
+ }
case DIDmsg_dot11req_mibget:
case DIDmsg_dot11req_mibset:{
- int isget = (msg->msgcode == DIDmsg_dot11req_mibget);
- struct p80211msg_dot11req_mibget *mib_msg =
- (struct p80211msg_dot11req_mibget *) msg;
- p80211req_mibset_mibget(wlandev, mib_msg, isget);
- }
- default:
- ;
+ int isget = (msg->msgcode == DIDmsg_dot11req_mibget);
+ struct p80211msg_dot11req_mibget *mib_msg =
+ (struct p80211msg_dot11req_mibget *) msg;
+ p80211req_mibset_mibget(wlandev, mib_msg, isget);
+ break;
+ }
} /* switch msg->msgcode */
-
- return;
}
-static int p80211req_mibset_mibget(wlandevice_t *wlandev,
+static void p80211req_mibset_mibget(wlandevice_t *wlandev,
struct p80211msg_dot11req_mibget *mib_msg,
int isget)
{
@@ -190,76 +187,65 @@ static int p80211req_mibset_mibget(wlandevice_t *wlandev,
switch (mibitem->did) {
case DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey0:{
- if (!isget)
- wep_change_key(wlandev, 0, key, pstr->len);
- break;
- }
+ if (!isget)
+ wep_change_key(wlandev, 0, key, pstr->len);
+ break;
+ }
case DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey1:{
- if (!isget)
- wep_change_key(wlandev, 1, key, pstr->len);
- break;
- }
+ if (!isget)
+ wep_change_key(wlandev, 1, key, pstr->len);
+ break;
+ }
case DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey2:{
- if (!isget)
- wep_change_key(wlandev, 2, key, pstr->len);
- break;
- }
+ if (!isget)
+ wep_change_key(wlandev, 2, key, pstr->len);
+ break;
+ }
case DIDmib_dot11smt_dot11WEPDefaultKeysTable_dot11WEPDefaultKey3:{
- if (!isget)
- wep_change_key(wlandev, 3, key, pstr->len);
- break;
- }
+ if (!isget)
+ wep_change_key(wlandev, 3, key, pstr->len);
+ break;
+ }
case DIDmib_dot11smt_dot11PrivacyTable_dot11WEPDefaultKeyID:{
- u32 *data = (u32 *) mibitem->data;
+ u32 *data = (u32 *) mibitem->data;
- if (isget) {
- *data =
- wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK;
- } else {
- wlandev->hostwep &= ~(HOSTWEP_DEFAULTKEY_MASK);
-
- wlandev->hostwep |=
- (*data & HOSTWEP_DEFAULTKEY_MASK);
- }
- break;
+ if (isget) {
+ *data = wlandev->hostwep & HOSTWEP_DEFAULTKEY_MASK;
+ } else {
+ wlandev->hostwep &= ~(HOSTWEP_DEFAULTKEY_MASK);
+ wlandev->hostwep |= (*data & HOSTWEP_DEFAULTKEY_MASK);
}
+ break;
+ }
case DIDmib_dot11smt_dot11PrivacyTable_dot11PrivacyInvoked:{
- u32 *data = (u32 *) mibitem->data;
-
- if (isget) {
- if (wlandev->hostwep & HOSTWEP_PRIVACYINVOKED)
- *data = P80211ENUM_truth_true;
- else
- *data = P80211ENUM_truth_false;
- } else {
- wlandev->hostwep &= ~(HOSTWEP_PRIVACYINVOKED);
- if (*data == P80211ENUM_truth_true)
- wlandev->hostwep |=
- HOSTWEP_PRIVACYINVOKED;
- }
- break;
+ u32 *data = (u32 *) mibitem->data;
+
+ if (isget) {
+ if (wlandev->hostwep & HOSTWEP_PRIVACYINVOKED)
+ *data = P80211ENUM_truth_true;
+ else
+ *data = P80211ENUM_truth_false;
+ } else {
+ wlandev->hostwep &= ~(HOSTWEP_PRIVACYINVOKED);
+ if (*data == P80211ENUM_truth_true)
+ wlandev->hostwep |= HOSTWEP_PRIVACYINVOKED;
}
+ break;
+ }
case DIDmib_dot11smt_dot11PrivacyTable_dot11ExcludeUnencrypted:{
- u32 *data = (u32 *) mibitem->data;
-
- if (isget) {
- if (wlandev->hostwep &
- HOSTWEP_EXCLUDEUNENCRYPTED)
- *data = P80211ENUM_truth_true;
- else
- *data = P80211ENUM_truth_false;
- } else {
- wlandev->hostwep &=
- ~(HOSTWEP_EXCLUDEUNENCRYPTED);
- if (*data == P80211ENUM_truth_true)
- wlandev->hostwep |=
- HOSTWEP_EXCLUDEUNENCRYPTED;
- }
- break;
+ u32 *data = (u32 *) mibitem->data;
+
+ if (isget) {
+ if (wlandev->hostwep & HOSTWEP_EXCLUDEUNENCRYPTED)
+ *data = P80211ENUM_truth_true;
+ else
+ *data = P80211ENUM_truth_false;
+ } else {
+ wlandev->hostwep &= ~(HOSTWEP_EXCLUDEUNENCRYPTED);
+ if (*data == P80211ENUM_truth_true)
+ wlandev->hostwep |= HOSTWEP_EXCLUDEUNENCRYPTED;
}
- default:
- ;
+ break;
+ }
}
-
- return 0;
}
diff --git a/drivers/staging/wlan-ng/p80211types.h b/drivers/staging/wlan-ng/p80211types.h
index f043090ef86d..8cb4fc6448a0 100644
--- a/drivers/staging/wlan-ng/p80211types.h
+++ b/drivers/staging/wlan-ng/p80211types.h
@@ -197,7 +197,7 @@
P80211DID_LSB_ACCESS)
/*----------------------------------------------------------------*/
-/* The following structure types are used for the represenation */
+/* The following structure types are used for the representation */
/* of ENUMint type metadata. */
typedef struct p80211enumpair {
diff --git a/drivers/staging/wlan-ng/p80211wep.c b/drivers/staging/wlan-ng/p80211wep.c
index 80c2d3b672bb..77e50a4aa7e9 100644
--- a/drivers/staging/wlan-ng/p80211wep.c
+++ b/drivers/staging/wlan-ng/p80211wep.c
@@ -134,10 +134,8 @@ int wep_change_key(wlandevice_t *wlandev, int keynum, u8 *key, int keylen)
return -1;
#ifdef WEP_DEBUG
- printk(KERN_DEBUG
- "WEP key %d len %d = %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x\n",
- keynum, keylen, key[0], key[1], key[2], key[3], key[4], key[5],
- key[6], key[7]);
+ printk(KERN_DEBUG "WEP key %d len %d = %*phC\n", keynum, keylen,
+ 8, key);
#endif
wlandev->wep_keylens[keynum] = keylen;
@@ -184,10 +182,8 @@ int wep_decrypt(wlandevice_t *wlandev, u8 *buf, u32 len, int key_override,
keylen += 3; /* add in IV bytes */
#ifdef WEP_DEBUG
- printk(KERN_DEBUG
- "D %d: %02x %02x %02x (%d %d) %02x:%02x:%02x:%02x:%02x\n", len,
- key[0], key[1], key[2], keyidx, keylen, key[3], key[4], key[5],
- key[6], key[7]);
+ printk(KERN_DEBUG "D %d: %*ph (%d %d) %*phC\n", len, 3, key,
+ keyidx, keylen, 5, key + 3);
#endif
/* set up the RC4 state */
@@ -263,10 +259,8 @@ int wep_encrypt(wlandevice_t *wlandev, u8 *buf, u8 *dst, u32 len, int keynum,
keylen += 3; /* add in IV bytes */
#ifdef WEP_DEBUG
- printk(KERN_DEBUG
- "E %d (%d/%d %d) %02x %02x %02x %02x:%02x:%02x:%02x:%02x\n", len,
- iv[3], keynum, keylen, key[0], key[1], key[2], key[3], key[4],
- key[5], key[6], key[7]);
+ printk(KERN_DEBUG "E %d (%d/%d %d) %*ph %*phC\n", len,
+ iv[3], keynum, keylen, 3, key, 5, key + 3);
#endif
/* set up the RC4 state */
diff --git a/drivers/staging/wlan-ng/prism2fw.c b/drivers/staging/wlan-ng/prism2fw.c
index 66c9aa972310..0dfd2a4933ef 100644
--- a/drivers/staging/wlan-ng/prism2fw.c
+++ b/drivers/staging/wlan-ng/prism2fw.c
@@ -123,27 +123,27 @@ struct imgchunk {
/* s-record image processing */
/* Data records */
-unsigned int ns3data;
-struct s3datarec s3data[S3DATA_MAX];
+static unsigned int ns3data;
+static struct s3datarec s3data[S3DATA_MAX];
/* Plug records */
-unsigned int ns3plug;
-struct s3plugrec s3plug[S3PLUG_MAX];
+static unsigned int ns3plug;
+static struct s3plugrec s3plug[S3PLUG_MAX];
/* CRC records */
-unsigned int ns3crc;
-struct s3crcrec s3crc[S3CRC_MAX];
+static unsigned int ns3crc;
+static struct s3crcrec s3crc[S3CRC_MAX];
/* Info records */
-unsigned int ns3info;
-struct s3inforec s3info[S3INFO_MAX];
+static unsigned int ns3info;
+static struct s3inforec s3info[S3INFO_MAX];
/* S7 record (there _better_ be only one) */
-u32 startaddr;
+static u32 startaddr;
/* Load image chunks */
-unsigned int nfchunks;
-struct imgchunk fchunk[CHUNKS_MAX];
+static unsigned int nfchunks;
+static struct imgchunk fchunk[CHUNKS_MAX];
/* Note that for the following pdrec_t arrays, the len and code */
/* fields are stored in HOST byte order. The mkpdrlist() function */
@@ -151,11 +151,11 @@ struct imgchunk fchunk[CHUNKS_MAX];
/*----------------------------------------------------------------*/
/* PDA, built from [card|newfile]+[addfile1+addfile2...] */
-struct pda pda;
-hfa384x_compident_t nicid;
-hfa384x_caplevel_t rfid;
-hfa384x_caplevel_t macid;
-hfa384x_caplevel_t priid;
+static struct pda pda;
+static hfa384x_compident_t nicid;
+static hfa384x_caplevel_t rfid;
+static hfa384x_caplevel_t macid;
+static hfa384x_caplevel_t priid;
/*================================================================*/
/* Local Function Declarations */
@@ -237,7 +237,7 @@ int prism2_fwtry(struct usb_device *udev, wlandevice_t *wlandev)
* 0 - success
* ~0 - failure
----------------------------------------------------------------*/
-int prism2_fwapply(const struct ihex_binrec *rfptr, wlandevice_t *wlandev)
+static int prism2_fwapply(const struct ihex_binrec *rfptr, wlandevice_t *wlandev)
{
signed int result = 0;
struct p80211msg_dot11req_mibget getmsg;
@@ -376,7 +376,7 @@ int prism2_fwapply(const struct ihex_binrec *rfptr, wlandevice_t *wlandev)
* 0 success
* ~0 failure
----------------------------------------------------------------*/
-int crcimage(struct imgchunk *fchunk, unsigned int nfchunks,
+static int crcimage(struct imgchunk *fchunk, unsigned int nfchunks,
struct s3crcrec *s3crc, unsigned int ns3crc)
{
int result = 0;
@@ -440,7 +440,7 @@ int crcimage(struct imgchunk *fchunk, unsigned int nfchunks,
* Returns:
* nothing
----------------------------------------------------------------*/
-void free_chunks(struct imgchunk *fchunk, unsigned int *nfchunks)
+static void free_chunks(struct imgchunk *fchunk, unsigned int *nfchunks)
{
int i;
for (i = 0; i < *nfchunks; i++)
@@ -462,7 +462,7 @@ void free_chunks(struct imgchunk *fchunk, unsigned int *nfchunks)
* Returns:
* nothing
----------------------------------------------------------------*/
-void free_srecs(void)
+static void free_srecs(void)
{
ns3data = 0;
memset(s3data, 0, sizeof(s3data));
@@ -489,7 +489,7 @@ void free_srecs(void)
* 0 - success
* ~0 - failure (probably an errno)
----------------------------------------------------------------*/
-int mkimage(struct imgchunk *clist, unsigned int *ccnt)
+static int mkimage(struct imgchunk *clist, unsigned int *ccnt)
{
int result = 0;
int i;
@@ -582,7 +582,7 @@ int mkimage(struct imgchunk *clist, unsigned int *ccnt)
* 0 - success
* ~0 - failure (probably an errno)
----------------------------------------------------------------*/
-int mkpdrlist(struct pda *pda)
+static int mkpdrlist(struct pda *pda)
{
int result = 0;
u16 *pda16 = (u16 *) pda->buf;
@@ -656,7 +656,7 @@ int mkpdrlist(struct pda *pda)
* 0 success
* ~0 failure
----------------------------------------------------------------*/
-int plugimage(struct imgchunk *fchunk, unsigned int nfchunks,
+static int plugimage(struct imgchunk *fchunk, unsigned int nfchunks,
struct s3plugrec *s3plug, unsigned int ns3plug, struct pda *pda)
{
int result = 0;
@@ -764,7 +764,7 @@ int plugimage(struct imgchunk *fchunk, unsigned int nfchunks,
* 0 - success
* ~0 - failure (probably an errno)
----------------------------------------------------------------*/
-int read_cardpda(struct pda *pda, wlandevice_t *wlandev)
+static int read_cardpda(struct pda *pda, wlandevice_t *wlandev)
{
int result = 0;
struct p80211msg_p2req_readpda msg;
@@ -806,7 +806,7 @@ int read_cardpda(struct pda *pda, wlandevice_t *wlandev)
*
* Note also that the start address record, originally an S7 record in
* the srec file, is expected in the fw file to be like a data record but
-* with a certain address to make it identiable.
+* with a certain address to make it identifiable.
*
* Here's the SREC format that the fw should have come from:
* S[37]nnaaaaaaaaddd...dddcc
@@ -854,7 +854,7 @@ int read_cardpda(struct pda *pda, wlandevice_t *wlandev)
* 0 - success
* ~0 - failure (probably an errno)
----------------------------------------------------------------*/
-int read_fwfile(const struct ihex_binrec *record)
+static int read_fwfile(const struct ihex_binrec *record)
{
int i;
int rcnt = 0;
@@ -978,7 +978,7 @@ int read_fwfile(const struct ihex_binrec *record)
* 0 success
* ~0 failure
----------------------------------------------------------------*/
-int writeimage(wlandevice_t *wlandev, struct imgchunk *fchunk,
+static int writeimage(wlandevice_t *wlandev, struct imgchunk *fchunk,
unsigned int nfchunks)
{
int result = 0;
@@ -1130,7 +1130,7 @@ free_result:
return result;
}
-int validate_identity(void)
+static int validate_identity(void)
{
int i;
int result = 1;
diff --git a/drivers/staging/wlan-ng/prism2sta.c b/drivers/staging/wlan-ng/prism2sta.c
index 1dfd9aa5e9fe..8d2277bb898f 100644
--- a/drivers/staging/wlan-ng/prism2sta.c
+++ b/drivers/staging/wlan-ng/prism2sta.c
@@ -242,7 +242,6 @@ static int prism2sta_close(wlandevice_t *wlandev)
----------------------------------------------------------------*/
static void prism2sta_reset(wlandevice_t *wlandev)
{
- return;
}
/*----------------------------------------------------------------
@@ -988,7 +987,6 @@ static void prism2sta_inf_handover(wlandevice_t *wlandev,
hfa384x_InfFrame_t *inf)
{
pr_debug("received infoframe:HANDOVER (unhandled)\n");
- return;
}
/*----------------------------------------------------------------
@@ -1035,8 +1033,6 @@ static void prism2sta_inf_tallies(wlandevice_t *wlandev,
for (i = 0; i < cnt; i++, dst++, src16++)
*dst += le16_to_cpu(*src16);
}
-
- return;
}
/*----------------------------------------------------------------
@@ -1093,8 +1089,6 @@ static void prism2sta_inf_scanresults(wlandevice_t *wlandev,
printk(KERN_ERR "setconfig(joinreq) failed, result=%d\n",
result);
}
-
- return;
}
/*----------------------------------------------------------------
@@ -1194,7 +1188,6 @@ static void prism2sta_inf_chinforesults(wlandevice_t *wlandev,
atomic_set(&hw->channel_info.done, 2);
hw->channel_info.count = n;
- return;
}
void prism2sta_processing_defer(struct work_struct *data)
@@ -1218,7 +1211,7 @@ void prism2sta_processing_defer(struct work_struct *data)
/* Now let's handle the linkstatus stuff */
if (hw->link_status == hw->link_status_new)
- goto failed;
+ return;
hw->link_status = hw->link_status_new;
@@ -1272,7 +1265,7 @@ void prism2sta_processing_defer(struct work_struct *data)
pr_debug
("getconfig(0x%02x) failed, result = %d\n",
HFA384x_RID_CURRENTBSSID, result);
- goto failed;
+ return;
}
result = hfa384x_drvr_getconfig(hw,
@@ -1282,7 +1275,7 @@ void prism2sta_processing_defer(struct work_struct *data)
pr_debug
("getconfig(0x%02x) failed, result = %d\n",
HFA384x_RID_CURRENTSSID, result);
- goto failed;
+ return;
}
prism2mgmt_bytestr2pstr((hfa384x_bytestr_t *) &ssid,
(p80211pstrd_t *) &
@@ -1296,7 +1289,7 @@ void prism2sta_processing_defer(struct work_struct *data)
pr_debug
("getconfig(0x%02x) failed, result = %d\n",
HFA384x_RID_PORTSTATUS, result);
- goto failed;
+ return;
}
wlandev->macmode =
(portstatus == HFA384x_PSTATUS_CONN_IBSS) ?
@@ -1355,7 +1348,7 @@ void prism2sta_processing_defer(struct work_struct *data)
if (result) {
pr_debug("getconfig(0x%02x) failed, result = %d\n",
HFA384x_RID_CURRENTBSSID, result);
- goto failed;
+ return;
}
result = hfa384x_drvr_getconfig(hw,
@@ -1364,7 +1357,7 @@ void prism2sta_processing_defer(struct work_struct *data)
if (result) {
pr_debug("getconfig(0x%02x) failed, result = %d\n",
HFA384x_RID_CURRENTSSID, result);
- goto failed;
+ return;
}
prism2mgmt_bytestr2pstr((hfa384x_bytestr_t *) &ssid,
(p80211pstrd_t *) &wlandev->ssid);
@@ -1443,14 +1436,10 @@ void prism2sta_processing_defer(struct work_struct *data)
/* This is bad, IO port problems? */
printk(KERN_WARNING
"unknown linkstatus=0x%02x\n", hw->link_status);
- goto failed;
- break;
+ return;
}
wlandev->linkstatus = (hw->link_status == HFA384x_LINK_CONNECTED);
-
-failed:
- return;
}
/*----------------------------------------------------------------
@@ -1478,8 +1467,6 @@ static void prism2sta_inf_linkstatus(wlandevice_t *wlandev,
hw->link_status_new = le16_to_cpu(inf->info.linkstatus.linkstatus);
schedule_work(&hw->link_bh);
-
- return;
}
/*----------------------------------------------------------------
@@ -1540,8 +1527,6 @@ static void prism2sta_inf_assocstatus(wlandevice_t *wlandev,
printk(KERN_WARNING
"authfail assocstatus info frame received for authenticated station.\n");
}
-
- return;
}
/*----------------------------------------------------------------
@@ -1731,7 +1716,6 @@ static void prism2sta_inf_authreq_defer(wlandevice_t *wlandev,
"setconfig(authenticatestation) failed, result=%d\n",
result);
}
- return;
}
/*----------------------------------------------------------------
@@ -1758,8 +1742,6 @@ static void prism2sta_inf_psusercnt(wlandevice_t *wlandev,
hfa384x_t *hw = (hfa384x_t *) wlandev->priv;
hw->psusercount = le16_to_cpu(inf->info.psusercnt.usercnt);
-
- return;
}
/*----------------------------------------------------------------
@@ -1825,7 +1807,6 @@ void prism2sta_ev_info(wlandevice_t *wlandev, hfa384x_InfFrame_t *inf)
"Unknown info type=0x%02x\n", inf->infotype);
break;
}
- return;
}
/*----------------------------------------------------------------
@@ -1850,8 +1831,6 @@ void prism2sta_ev_info(wlandevice_t *wlandev, hfa384x_InfFrame_t *inf)
void prism2sta_ev_txexc(wlandevice_t *wlandev, u16 status)
{
pr_debug("TxExc status=0x%x.\n", status);
-
- return;
}
/*----------------------------------------------------------------
@@ -1875,7 +1854,6 @@ void prism2sta_ev_tx(wlandevice_t *wlandev, u16 status)
pr_debug("Tx Complete, status=0x%04x\n", status);
/* update linux network stats */
wlandev->linux_stats.tx_packets++;
- return;
}
/*----------------------------------------------------------------
@@ -1897,7 +1875,6 @@ void prism2sta_ev_tx(wlandevice_t *wlandev, u16 status)
void prism2sta_ev_rx(wlandevice_t *wlandev, struct sk_buff *skb)
{
p80211netdev_rx(wlandev, skb);
- return;
}
/*----------------------------------------------------------------
@@ -1919,7 +1896,6 @@ void prism2sta_ev_rx(wlandevice_t *wlandev, struct sk_buff *skb)
void prism2sta_ev_alloc(wlandevice_t *wlandev)
{
netif_wake_queue(wlandev->netdev);
- return;
}
/*----------------------------------------------------------------
@@ -1988,12 +1964,12 @@ void prism2sta_commsqual_defer(struct work_struct *data)
int result = 0;
if (hw->wlandev->hwremoved)
- goto done;
+ return;
/* we don't care if we're in AP mode */
if ((wlandev->macmode == WLAN_MACMODE_NONE) ||
(wlandev->macmode == WLAN_MACMODE_ESS_AP)) {
- goto done;
+ return;
}
/* It only makes sense to poll these in non-IBSS */
@@ -2004,7 +1980,7 @@ void prism2sta_commsqual_defer(struct work_struct *data)
if (result) {
printk(KERN_ERR "error fetching commsqual\n");
- goto done;
+ return;
}
pr_debug("commsqual %d %d %d\n",
@@ -2021,7 +1997,7 @@ void prism2sta_commsqual_defer(struct work_struct *data)
if (result) {
pr_debug("get signal rate failed, result = %d\n",
result);
- goto done;
+ return;
}
switch (mibitem->data) {
@@ -2048,7 +2024,7 @@ void prism2sta_commsqual_defer(struct work_struct *data)
if (result) {
pr_debug("getconfig(0x%02x) failed, result = %d\n",
HFA384x_RID_CURRENTBSSID, result);
- goto done;
+ return;
}
result = hfa384x_drvr_getconfig(hw,
@@ -2057,16 +2033,13 @@ void prism2sta_commsqual_defer(struct work_struct *data)
if (result) {
pr_debug("getconfig(0x%02x) failed, result = %d\n",
HFA384x_RID_CURRENTSSID, result);
- goto done;
+ return;
}
prism2mgmt_bytestr2pstr((hfa384x_bytestr_t *) &ssid,
(p80211pstrd_t *) &wlandev->ssid);
/* Reschedule timer */
mod_timer(&hw->commsqual_timer, jiffies + HZ);
-
-done:
- ;
}
void prism2sta_commsqual_timer(unsigned long data)
diff --git a/drivers/staging/xgifb/XGI_main_26.c b/drivers/staging/xgifb/XGI_main_26.c
index 64ffd70eb7dc..f775c5453845 100644
--- a/drivers/staging/xgifb/XGI_main_26.c
+++ b/drivers/staging/xgifb/XGI_main_26.c
@@ -6,6 +6,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <linux/sizes.h>
#include <linux/module.h>
#ifdef CONFIG_MTRR
@@ -329,6 +330,7 @@ static int XGIfb_validate_mode(struct xgifb_video_info *xgifb_info, int myindex)
{
u16 xres, yres;
struct xgi_hw_device_info *hw_info = &xgifb_info->hw_info;
+ unsigned long required_mem;
if (xgifb_info->chip == XG21) {
if (xgifb_info->display2 == XGIFB_DISP_LCD) {
@@ -345,13 +347,13 @@ static int XGIfb_validate_mode(struct xgifb_video_info *xgifb_info, int myindex)
}
}
- return myindex;
+ goto check_memory;
}
/* FIXME: for now, all is valid on XG27 */
if (xgifb_info->chip == XG27)
- return myindex;
+ goto check_memory;
if (!(XGIbios_mode[myindex].chipset & MD_XGI315))
return -1;
@@ -539,6 +541,12 @@ static int XGIfb_validate_mode(struct xgifb_video_info *xgifb_info, int myindex)
case XGIFB_DISP_NONE:
break;
}
+
+check_memory:
+ required_mem = XGIbios_mode[myindex].xres * XGIbios_mode[myindex].yres *
+ XGIbios_mode[myindex].bpp / 8;
+ if (required_mem > xgifb_info->video_size)
+ return -1;
return myindex;
}
@@ -913,17 +921,10 @@ static void XGIfb_post_setmode(struct xgifb_video_info *xgifb_info)
}
if ((filter >= 0) && (filter <= 7)) {
- pr_debug("FilterTable[%d]-%d: %02x %02x %02x %02x\n",
+ pr_debug("FilterTable[%d]-%d: %*ph\n",
filter_tb, filter,
- XGI_TV_filter[filter_tb].
- filter[filter][0],
- XGI_TV_filter[filter_tb].
- filter[filter][1],
- XGI_TV_filter[filter_tb].
- filter[filter][2],
- XGI_TV_filter[filter_tb].
- filter[filter][3]
- );
+ 4, XGI_TV_filter[filter_tb].
+ filter[filter]);
xgifb_reg_set(
XGIPART2,
0x35,
@@ -1404,11 +1405,10 @@ static int XGIfb_pan_display(struct fb_var_screeninfo *var,
if (var->yoffset < 0 || var->yoffset >= info->var.yres_virtual
|| var->xoffset)
return -EINVAL;
- } else {
- if (var->xoffset + info->var.xres > info->var.xres_virtual
+ } else if (var->xoffset + info->var.xres > info->var.xres_virtual
|| var->yoffset + info->var.yres
- > info->var.yres_virtual)
- return -EINVAL;
+ > info->var.yres_virtual) {
+ return -EINVAL;
}
err = XGIfb_pan_var(var, info);
if (err < 0)
@@ -1471,6 +1471,9 @@ static int XGIfb_get_dram_size(struct xgifb_video_info *xgifb_info)
xgifb_reg_set(XGISR, IND_SIS_DRAM_SIZE, 0x51);
reg = xgifb_reg_get(XGISR, IND_SIS_DRAM_SIZE);
+ if (!reg)
+ return -1;
+
switch ((reg & XGI_DRAM_SIZE_MASK) >> 4) {
case XGI_DRAM_SIZE_1MB:
xgifb_info->video_size = 0x100000;
@@ -1701,6 +1704,7 @@ static int __devinit xgifb_probe(struct pci_dev *pdev,
struct fb_info *fb_info;
struct xgifb_video_info *xgifb_info;
struct xgi_hw_device_info *hw_info;
+ unsigned long video_size_max;
fb_info = framebuffer_alloc(sizeof(*xgifb_info), &pdev->dev);
if (!fb_info)
@@ -1721,6 +1725,7 @@ static int __devinit xgifb_probe(struct pci_dev *pdev,
xgifb_info->subsysvendor = pdev->subsystem_vendor;
xgifb_info->subsysdevice = pdev->subsystem_device;
+ video_size_max = pci_resource_len(pdev, 0);
xgifb_info->video_base = pci_resource_start(pdev, 0);
xgifb_info->mmio_base = pci_resource_start(pdev, 1);
xgifb_info->mmio_size = pci_resource_len(pdev, 1);
@@ -1777,10 +1782,10 @@ static int __devinit xgifb_probe(struct pci_dev *pdev,
hw_info->jChipType = xgifb_info->chip;
if (XGIfb_get_dram_size(xgifb_info)) {
- dev_err(&pdev->dev,
- "Fatal error: Unable to determine RAM size.\n");
- ret = -ENODEV;
- goto error_disable;
+ xgifb_info->video_size = min_t(unsigned long, video_size_max,
+ SZ_16M);
+ } else if (xgifb_info->video_size > video_size_max) {
+ xgifb_info->video_size = video_size_max;
}
/* Enable PCI_LINEAR_ADDRESSING and MMIO_ENABLE */
diff --git a/drivers/staging/xgifb/vb_def.h b/drivers/staging/xgifb/vb_def.h
index 69078d933a47..77137e4452a0 100644
--- a/drivers/staging/xgifb/vb_def.h
+++ b/drivers/staging/xgifb/vb_def.h
@@ -3,9 +3,7 @@
#include "../../video/sis/initdef.h"
#define VB_XGI301C 0x0020 /* for 301C */
-#define VB_YPbPr1080i 0x03
-#define LVDSCRT1Len 15
#define SupportCRT2in301C 0x0100 /* for 301C */
#define SetCHTVOverScan 0x8000
@@ -22,15 +20,6 @@
#define XGI_CRT2_PORT_00 (0x00 - 0x030)
-/* =============================================================
- for 310
-============================================================== */
-#define ModeSoftSetting 0x04
-
-/* ---------------- SetMode Stack */
-#define CRT1Len 15
-#define VCLKLen 4
-
#define SupportAllCRT2 0x0078
#define NoSupportTV 0x0070
#define NoSupportHiVisionTV 0x0060
@@ -115,16 +104,6 @@
#define ActiveHiTV 0x08
#define ActiveYPbPr 0x10
-/* --------------------------------------------------------- */
-/* translated from asm code 301def.h */
-/* */
-/* --------------------------------------------------------- */
-#define LVDSCRT1Len_H 8
-#define LVDSCRT1Len_V 7
-#define LCDDesDataLen 6
-#define LVDSDesDataLen2 8
-#define LCDDesDataLen2 8
-
#define NTSC1024x768HT 1908
#define YPbPrTV525iHT 1716 /* YPbPr */
diff --git a/drivers/staging/xgifb/vb_init.c b/drivers/staging/xgifb/vb_init.c
index 80dba6a425ba..7739dbd9f029 100644
--- a/drivers/staging/xgifb/vb_init.c
+++ b/drivers/staging/xgifb/vb_init.c
@@ -1269,7 +1269,7 @@ static unsigned char GetXG27FPBits(struct vb_device_info *pVBInfo)
if (temp <= 2)
temp &= 0x03;
else
- temp = ((temp & 0x04) >> 1) || ((~temp) & 0x01);
+ temp = ((temp & 0x04) >> 1) | ((~temp) & 0x01);
xgifb_reg_set(pVBInfo->P3d4, 0x4A, CR4A);
@@ -1299,8 +1299,6 @@ unsigned char XGIInitNew(struct pci_dev *pdev)
outb(0x67, (pVBInfo->BaseAddr + 0x12)); /* 3c2 <- 67 ,ynlai */
- pVBInfo->ISXPDOS = 0;
-
pVBInfo->P3c4 = pVBInfo->BaseAddr + 0x14;
pVBInfo->P3d4 = pVBInfo->BaseAddr + 0x24;
pVBInfo->P3c0 = pVBInfo->BaseAddr + 0x10;
@@ -1494,7 +1492,6 @@ unsigned char XGIInitNew(struct pci_dev *pdev)
XGINew_SetModeScratch(HwDeviceExtension, pVBInfo);
xgifb_reg_set(pVBInfo->P3d4, 0x8c, 0x87);
- xgifb_reg_set(pVBInfo->P3c4, 0x14, 0x31);
return 1;
} /* end of init */
diff --git a/drivers/staging/xgifb/vb_setmode.c b/drivers/staging/xgifb/vb_setmode.c
index e81149fc66e3..e95a1655a6ce 100644
--- a/drivers/staging/xgifb/vb_setmode.c
+++ b/drivers/staging/xgifb/vb_setmode.c
@@ -23,20 +23,18 @@ static const unsigned short XGINew_VGA_DAC[] = {
void InitTo330Pointer(unsigned char ChipType, struct vb_device_info *pVBInfo)
{
- pVBInfo->StandTable = (struct SiS_StandTable_S *) &XGI330_StandTable;
- pVBInfo->EModeIDTable = (struct XGI_ExtStruct *) XGI330_EModeIDTable;
- pVBInfo->RefIndex = (struct XGI_Ext2Struct *) XGI330_RefIndex;
- pVBInfo->XGINEWUB_CRT1Table
- = (struct XGI_CRT1TableStruct *) XGI_CRT1Table;
-
- pVBInfo->MCLKData = (struct SiS_MCLKData *) XGI340New_MCLKData;
- pVBInfo->ECLKData = (struct XGI_ECLKDataStruct *) XGI340_ECLKData;
- pVBInfo->VCLKData = (struct SiS_VCLKData *) XGI_VCLKData;
- pVBInfo->VBVCLKData = (struct SiS_VBVCLKData *) XGI_VBVCLKData;
+ pVBInfo->StandTable = &XGI330_StandTable;
+ pVBInfo->EModeIDTable = XGI330_EModeIDTable;
+ pVBInfo->RefIndex = XGI330_RefIndex;
+ pVBInfo->XGINEWUB_CRT1Table = XGI_CRT1Table;
+
+ pVBInfo->MCLKData = XGI340New_MCLKData;
+ pVBInfo->ECLKData = XGI340_ECLKData;
+ pVBInfo->VCLKData = XGI_VCLKData;
+ pVBInfo->VBVCLKData = XGI_VBVCLKData;
pVBInfo->ScreenOffset = XGI330_ScreenOffset;
- pVBInfo->StResInfo = (struct SiS_StResInfo_S *) XGI330_StResInfo;
- pVBInfo->ModeResInfo
- = (struct SiS_ModeResInfo_S *) XGI330_ModeResInfo;
+ pVBInfo->StResInfo = XGI330_StResInfo;
+ pVBInfo->ModeResInfo = XGI330_ModeResInfo;
pVBInfo->LCDResInfo = 0;
pVBInfo->LCDTypeInfo = 0;
@@ -56,24 +54,9 @@ void InitTo330Pointer(unsigned char ChipType, struct vb_device_info *pVBInfo)
pVBInfo->SR21 = 0xa3;
pVBInfo->SR22 = 0xfb;
- pVBInfo->NTSCTiming = XGI330_NTSCTiming;
- pVBInfo->PALTiming = XGI330_PALTiming;
- pVBInfo->HiTVExtTiming = XGI330_HiTVExtTiming;
- pVBInfo->HiTVSt1Timing = XGI330_HiTVSt1Timing;
- pVBInfo->HiTVSt2Timing = XGI330_HiTVSt2Timing;
- pVBInfo->HiTVTextTiming = XGI330_HiTVTextTiming;
- pVBInfo->YPbPr750pTiming = XGI330_YPbPr750pTiming;
- pVBInfo->YPbPr525pTiming = XGI330_YPbPr525pTiming;
- pVBInfo->YPbPr525iTiming = XGI330_YPbPr525iTiming;
- pVBInfo->HiTVGroup3Data = XGI330_HiTVGroup3Data;
- pVBInfo->HiTVGroup3Simu = XGI330_HiTVGroup3Simu;
- pVBInfo->HiTVGroup3Text = XGI330_HiTVGroup3Text;
- pVBInfo->Ren525pGroup3 = XGI330_Ren525pGroup3;
- pVBInfo->Ren750pGroup3 = XGI330_Ren750pGroup3;
-
- pVBInfo->TimingH = (struct XGI_TimingHStruct *) XGI_TimingH;
- pVBInfo->TimingV = (struct XGI_TimingVStruct *) XGI_TimingV;
- pVBInfo->UpdateCRT1 = (struct XGI_XG21CRT1Struct *) XGI_UpdateCRT1Table;
+ pVBInfo->TimingH = XGI_TimingH;
+ pVBInfo->TimingV = XGI_TimingV;
+ pVBInfo->UpdateCRT1 = XGI_UpdateCRT1Table;
/* 310 customization related */
if ((pVBInfo->VBType & VB_SIS301LV) || (pVBInfo->VBType & VB_SIS302LV))
@@ -86,8 +69,7 @@ void InitTo330Pointer(unsigned char ChipType, struct vb_device_info *pVBInfo)
if (ChipType == XG27) {
unsigned char temp;
- pVBInfo->MCLKData
- = (struct SiS_MCLKData *) XGI27New_MCLKData;
+ pVBInfo->MCLKData = XGI27New_MCLKData;
pVBInfo->CR40 = XGI27_cr41;
pVBInfo->XGINew_CR97 = 0xc1;
pVBInfo->SR15 = XG27_SR13;
@@ -116,11 +98,9 @@ static void XGI_SetSeqRegs(unsigned short ModeNo,
i = XGI_SetCRT2ToLCDA;
if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA) {
tempah |= 0x01;
- } else {
- if (pVBInfo->VBInfo & (SetCRT2ToTV | SetCRT2ToLCD)) {
- if (pVBInfo->VBInfo & SetInSlaveMode)
- tempah |= 0x01;
- }
+ } else if (pVBInfo->VBInfo & (SetCRT2ToTV | SetCRT2ToLCD)) {
+ if (pVBInfo->VBInfo & SetInSlaveMode)
+ tempah |= 0x01;
}
tempah |= 0x20; /* screen off */
@@ -165,10 +145,9 @@ static void XGI_SetATTRegs(unsigned short ModeNo,
if ((modeflag & Charx8Dot) && i == 0x13) { /* ifndef Dot9 */
if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA) {
ARdata = 0;
- } else {
- if ((pVBInfo->VBInfo &
+ } else if ((pVBInfo->VBInfo &
(SetCRT2ToTV | SetCRT2ToLCD)) &&
- (pVBInfo->VBInfo & SetInSlaveMode))
+ (pVBInfo->VBInfo & SetInSlaveMode)) {
ARdata = 0;
}
}
@@ -258,59 +237,45 @@ static unsigned char XGI_AjustCRT2Rate(unsigned short ModeNo,
}
if (pVBInfo->VBInfo & SetCRT2ToHiVision) { /* for HiTV */
- if ((pVBInfo->VBType & VB_SIS301LV) &&
- (pVBInfo->VBExtInfo == VB_YPbPr1080i)) {
- tempax |= SupportYPbPr750p;
- if ((pVBInfo->VBInfo & SetInSlaveMode) &&
- ((resinfo == 3) ||
- (resinfo == 4) ||
- (resinfo > 7)))
+ tempax |= SupportHiVision;
+ if ((pVBInfo->VBInfo & SetInSlaveMode) &&
+ ((resinfo == 4) ||
+ (resinfo == 3 &&
+ (pVBInfo->SetFlag & TVSimuMode)) ||
+ (resinfo > 7)))
return 0;
- } else {
- tempax |= SupportHiVision;
- if ((pVBInfo->VBInfo & SetInSlaveMode) &&
- ((resinfo == 4) ||
- (resinfo == 3 &&
- (pVBInfo->SetFlag & TVSimuMode)) ||
- (resinfo > 7)))
- return 0;
- }
- } else {
- if (pVBInfo->VBInfo & (SetCRT2ToAVIDEO |
+ } else if (pVBInfo->VBInfo & (SetCRT2ToAVIDEO |
SetCRT2ToSVIDEO |
SetCRT2ToSCART |
SetCRT2ToYPbPr525750 |
SetCRT2ToHiVision)) {
- tempax |= SupportTV;
-
- if (pVBInfo->VBType & (VB_SIS301B |
- VB_SIS302B |
- VB_SIS301LV |
- VB_SIS302LV |
- VB_XGI301C))
- tempax |= SupportTV1024;
-
- if (!(pVBInfo->VBInfo & TVSetPAL) &&
- (modeflag & NoSupportSimuTV) &&
- (pVBInfo->VBInfo & SetInSlaveMode) &&
- (!(pVBInfo->VBInfo & SetNotSimuMode)))
- return 0;
- }
+ tempax |= SupportTV;
+
+ if (pVBInfo->VBType & (VB_SIS301B |
+ VB_SIS302B |
+ VB_SIS301LV |
+ VB_SIS302LV |
+ VB_XGI301C))
+ tempax |= SupportTV1024;
+
+ if (!(pVBInfo->VBInfo & TVSetPAL) &&
+ (modeflag & NoSupportSimuTV) &&
+ (pVBInfo->VBInfo & SetInSlaveMode) &&
+ (!(pVBInfo->VBInfo & SetNotSimuMode)))
+ return 0;
}
- } else { /* for LVDS */
- if (pVBInfo->VBInfo & SetCRT2ToLCD) {
- tempax |= SupportLCD;
+ } else if (pVBInfo->VBInfo & SetCRT2ToLCD) { /* for LVDS */
+ tempax |= SupportLCD;
- if (resinfo > 0x08)
- return 0; /* 1024x768 */
+ if (resinfo > 0x08)
+ return 0; /* 1024x768 */
- if (pVBInfo->LCDResInfo < Panel_1024x768) {
- if (resinfo > 0x07)
- return 0; /* 800x600 */
+ if (pVBInfo->LCDResInfo < Panel_1024x768) {
+ if (resinfo > 0x07)
+ return 0; /* 800x600 */
- if (resinfo == 0x04)
- return 0; /* 512x384 */
- }
+ if (resinfo == 0x04)
+ return 0; /* 512x384 */
}
}
@@ -969,13 +934,8 @@ static unsigned short XGI_GetVCLK2Ptr(unsigned short ModeNo,
}
/* 301lv */
- if ((pVBInfo->VBType & VB_SIS301LV) &&
- !(pVBInfo->VBExtInfo == VB_YPbPr1080i)) {
- if (pVBInfo->VBExtInfo == YPbPr750p)
- VCLKIndex = XGI_YPbPr750pVCLK;
- else if (pVBInfo->VBExtInfo == YPbPr525p)
- VCLKIndex = YPbPr525pVCLK;
- else if (pVBInfo->SetFlag & RPLLDIV2XO)
+ if (pVBInfo->VBType & VB_SIS301LV) {
+ if (pVBInfo->SetFlag & RPLLDIV2XO)
VCLKIndex = YPbPr525iVCLK_2;
else
VCLKIndex = YPbPr525iVCLK;
@@ -991,13 +951,11 @@ static unsigned short XGI_GetVCLK2Ptr(unsigned short ModeNo,
Ext_CRTVCLK;
VCLKIndex &= IndexMask;
}
- } else { /* LVDS */
- if ((pVBInfo->LCDResInfo == Panel_800x600) ||
- (pVBInfo->LCDResInfo == Panel_320x480))
- VCLKIndex = VCLK40; /* LVDSXlat1VCLK */
- else
- VCLKIndex = VCLK65_315 + 2; /* LVDSXlat2VCLK,
- LVDSXlat3VCLK */
+ } else if ((pVBInfo->LCDResInfo == Panel_800x600) ||
+ (pVBInfo->LCDResInfo == Panel_320x480)) { /* LVDS */
+ VCLKIndex = VCLK40; /* LVDSXlat1VCLK */
+ } else {
+ VCLKIndex = VCLK65_315 + 2; /* LVDSXlat2VCLK, LVDSXlat3VCLK */
}
return VCLKIndex;
@@ -1352,7 +1310,7 @@ static void *XGI_GetLcdPtr(unsigned short BX, unsigned short ModeNo,
unsigned short RefreshRateTableIndex,
struct vb_device_info *pVBInfo)
{
- unsigned short i, tempdx, tempcx, tempbx, tempal, modeflag, table;
+ unsigned short i, tempdx, tempbx, tempal, modeflag, table;
struct XGI330_LCDDataTablStruct *tempdi = NULL;
@@ -1377,15 +1335,6 @@ static void *XGI_GetLcdPtr(unsigned short BX, unsigned short ModeNo,
tempal = (tempal & 0x0f);
}
- tempcx = LCDLenList[tempbx];
-
- if (pVBInfo->LCDInfo & EnableScalingLCD) { /* ScaleLCD */
- if ((tempbx == 5) || (tempbx) == 7)
- tempcx = LCDDesDataLen2;
- else if ((tempbx == 3) || (tempbx == 8))
- tempcx = LVDSDesDataLen2;
- }
-
switch (tempbx) {
case 0:
case 1:
@@ -1403,14 +1352,6 @@ static void *XGI_GetLcdPtr(unsigned short BX, unsigned short ModeNo,
case 5:
tempdi = XGI_LCDDesDataTable;
break;
- case 6:
- tempdi = XGI_EPLCHLCDRegPtr;
- break;
- case 7:
- case 8:
- case 9:
- tempdi = NULL;
- break;
default:
break;
}
@@ -1764,62 +1705,20 @@ static void *XGI_GetLcdPtr(unsigned short BX, unsigned short ModeNo,
default:
break;
}
- } else if (table == 6) {
- switch (tempdi[i].DATAPTR) {
- case 0:
- return &XGI_CH7017LV1024x768[tempal];
- break;
- case 1:
- return &XGI_CH7017LV1400x1050[tempal];
- break;
- default:
- break;
- }
}
return NULL;
}
-static void *XGI_GetTVPtr(unsigned short BX, unsigned short ModeNo,
+static struct SiS_TVData const *XGI_GetTVPtr(unsigned short ModeNo,
unsigned short ModeIdIndex,
unsigned short RefreshRateTableIndex,
struct vb_device_info *pVBInfo)
{
- unsigned short i, tempdx, tempbx, tempal, modeflag, table;
- struct XGI330_TVDataTablStruct *tempdi = NULL;
+ unsigned short i, tempdx, tempal, modeflag;
- tempbx = BX;
modeflag = pVBInfo->EModeIDTable[ModeIdIndex].Ext_ModeFlag;
tempal = pVBInfo->RefIndex[RefreshRateTableIndex].Ext_CRT2CRTC;
tempal = tempal & 0x3f;
- table = tempbx;
-
- switch (tempbx) {
- case 0:
- tempdi = NULL;
- break;
- case 1:
- tempdi = NULL;
- break;
- case 2:
- case 6:
- tempdi = xgifb_chrontel_tv;
- break;
- case 3:
- tempdi = NULL;
- break;
- case 4:
- tempdi = XGI_TVDataTable;
- break;
- case 5:
- tempdi = NULL;
- break;
- default:
- break;
- }
-
- if (tempdi == NULL) /* OEMUtil */
- return NULL;
-
tempdx = pVBInfo->TVInfo;
if (pVBInfo->VBInfo & SetInSlaveMode)
@@ -1830,78 +1729,14 @@ static void *XGI_GetTVPtr(unsigned short BX, unsigned short ModeNo,
i = 0;
- while (tempdi[i].MASK != 0xffff) {
- if ((tempdx & tempdi[i].MASK) == tempdi[i].CAP)
+ while (XGI_TVDataTable[i].MASK != 0xffff) {
+ if ((tempdx & XGI_TVDataTable[i].MASK) ==
+ XGI_TVDataTable[i].CAP)
break;
i++;
}
- if (table == 0x04) {
- switch (tempdi[i].DATAPTR) {
- case 0:
- return &XGI_ExtPALData[tempal];
- break;
- case 1:
- return &XGI_ExtNTSCData[tempal];
- break;
- case 2:
- return &XGI_StPALData[tempal];
- break;
- case 3:
- return &XGI_StNTSCData[tempal];
- break;
- case 4:
- return &XGI_ExtHiTVData[tempal];
- break;
- case 5:
- return &XGI_St2HiTVData[tempal];
- break;
- case 6:
- return &XGI_ExtYPbPr525iData[tempal];
- break;
- case 7:
- return &XGI_ExtYPbPr525pData[tempal];
- break;
- case 8:
- return &XGI_ExtYPbPr750pData[tempal];
- break;
- case 9:
- return &XGI_StYPbPr525iData[tempal];
- break;
- case 10:
- return &XGI_StYPbPr525pData[tempal];
- break;
- case 11:
- return &XGI_StYPbPr750pData[tempal];
- break;
- case 12: /* avoid system hang */
- return &XGI_ExtNTSCData[tempal];
- break;
- case 13:
- return &XGI_St1HiTVData[tempal];
- break;
- default:
- break;
- }
- } else if (table == 0x02) {
- switch (tempdi[i].DATAPTR) {
- case 0:
- return &XGI_CHTVUNTSCData[tempal];
- break;
- case 1:
- return &XGI_CHTVONTSCData[tempal];
- break;
- case 2:
- return &XGI_CHTVUPALData[tempal];
- break;
- case 3:
- return &XGI_CHTVOPALData[tempal];
- break;
- default:
- break;
- }
- }
- return NULL;
+ return &XGI_TVDataTable[i].DATAPTR[tempal];
}
static void XGI_GetLVDSData(unsigned short ModeNo, unsigned short ModeIdIndex,
@@ -1914,9 +1749,8 @@ static void XGI_GetLVDSData(unsigned short ModeNo, unsigned short ModeIdIndex,
tempbx = 2;
if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) {
- LCDPtr = (struct SiS_LVDSData *)XGI_GetLcdPtr(tempbx,
- ModeNo, ModeIdIndex, RefreshRateTableIndex,
- pVBInfo);
+ LCDPtr = XGI_GetLcdPtr(tempbx, ModeNo, ModeIdIndex,
+ RefreshRateTableIndex, pVBInfo);
pVBInfo->VGAHT = LCDPtr->VGAHT;
pVBInfo->VGAVT = LCDPtr->VGAVT;
pVBInfo->HT = LCDPtr->LCDHT;
@@ -1962,11 +1796,8 @@ static void XGI_ModCRT1Regs(unsigned short ModeNo, unsigned short ModeIdIndex,
tempbx = 0;
if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) {
- LCDPtr = (struct XGI_LVDSCRT1HDataStruct *)
- XGI_GetLcdPtr(tempbx, ModeNo,
- ModeIdIndex,
- RefreshRateTableIndex,
- pVBInfo);
+ LCDPtr = XGI_GetLcdPtr(tempbx, ModeNo, ModeIdIndex,
+ RefreshRateTableIndex, pVBInfo);
for (i = 0; i < 8; i++)
pVBInfo->TimingH[0].data[i] = LCDPtr[0].Reg[i];
@@ -1977,13 +1808,8 @@ static void XGI_ModCRT1Regs(unsigned short ModeNo, unsigned short ModeIdIndex,
tempbx = 1;
if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) {
- LCDPtr1 = (struct XGI_LVDSCRT1VDataStruct *)
- XGI_GetLcdPtr(
- tempbx,
- ModeNo,
- ModeIdIndex,
- RefreshRateTableIndex,
- pVBInfo);
+ LCDPtr1 = XGI_GetLcdPtr(tempbx, ModeNo, ModeIdIndex,
+ RefreshRateTableIndex, pVBInfo);
for (i = 0; i < 7; i++)
pVBInfo->TimingV[0].data[i] = LCDPtr1[0].Reg[i];
}
@@ -2075,23 +1901,11 @@ static void XGI_SetLVDSRegs(unsigned short ModeNo, unsigned short ModeIdIndex,
modeflag = pVBInfo->EModeIDTable[ModeIdIndex].Ext_ModeFlag;
tempbx = 3;
if (pVBInfo->LCDInfo & EnableScalingLCD)
- LCDPtr1 =
- (struct XGI330_LCDDataDesStruct2 *)
- XGI_GetLcdPtr(
- tempbx,
- ModeNo,
- ModeIdIndex,
- RefreshRateTableIndex,
- pVBInfo);
+ LCDPtr1 = XGI_GetLcdPtr(tempbx, ModeNo, ModeIdIndex,
+ RefreshRateTableIndex, pVBInfo);
else
- LCDPtr =
- (struct XGI_LCDDesStruct *)
- XGI_GetLcdPtr(
- tempbx,
- ModeNo,
- ModeIdIndex,
- RefreshRateTableIndex,
- pVBInfo);
+ LCDPtr = XGI_GetLcdPtr(tempbx, ModeNo, ModeIdIndex,
+ RefreshRateTableIndex, pVBInfo);
XGI_GetLCDSync(&tempax, &tempbx, pVBInfo);
push1 = tempbx;
@@ -2438,8 +2252,8 @@ static void XGI_GetVCLKLen(unsigned char tempal, unsigned char *di_0,
| VB_SIS301LV | VB_SIS302LV | VB_XGI301C)) {
if ((!(pVBInfo->VBInfo & XGI_SetCRT2ToLCDA)) &&
(pVBInfo->SetFlag & ProgrammingCRT2)) {
- *di_0 = (unsigned char) XGI_VBVCLKData[tempal].SR2B;
- *di_1 = XGI_VBVCLKData[tempal].SR2C;
+ *di_0 = XGI_VBVCLKData[tempal].Part4_A;
+ *di_1 = XGI_VBVCLKData[tempal].Part4_B;
}
} else {
*di_0 = XGI_VCLKData[tempal].SR2B;
@@ -2634,21 +2448,16 @@ static void XGI_GetVBInfo(unsigned short ModeNo, unsigned short ModeIdIndex,
temp = xgifb_reg_get(pVBInfo->P3d4, 0x38);
- if (pVBInfo->IF_DEF_LCDA == 1) {
-
- if (((HwDeviceExtension->jChipType >= XG20) ||
- (HwDeviceExtension->jChipType >= XG40)) &&
- (pVBInfo->IF_DEF_LVDS == 0)) {
- if (pVBInfo->VBType &
- (VB_SIS302B |
- VB_SIS301LV |
- VB_SIS302LV |
- VB_XGI301C)) {
- if (temp & EnableDualEdge) {
- tempbx |= SetCRT2ToDualEdge;
- if (temp & SetToLCDA)
- tempbx |= XGI_SetCRT2ToLCDA;
- }
+ if (pVBInfo->IF_DEF_LVDS == 0) {
+ if (pVBInfo->VBType &
+ (VB_SIS302B |
+ VB_SIS301LV |
+ VB_SIS302LV |
+ VB_XGI301C)) {
+ if (temp & EnableDualEdge) {
+ tempbx |= SetCRT2ToDualEdge;
+ if (temp & SetToLCDA)
+ tempbx |= XGI_SetCRT2ToLCDA;
}
}
}
@@ -2687,11 +2496,10 @@ static void XGI_GetVBInfo(unsigned short ModeNo, unsigned short ModeIdIndex,
temp = 0x09FC;
else
temp = 0x097C;
+ } else if (pVBInfo->IF_DEF_HiVision == 1) {
+ temp = 0x01FC;
} else {
- if (pVBInfo->IF_DEF_HiVision == 1)
- temp = 0x01FC;
- else
- temp = 0x017C;
+ temp = 0x017C;
}
} else { /* 3nd party chip */
temp = SetCRT2ToLCD;
@@ -2702,19 +2510,17 @@ static void XGI_GetVBInfo(unsigned short ModeNo, unsigned short ModeIdIndex,
tempbx = 0;
}
- if (pVBInfo->IF_DEF_LCDA == 1) { /* Select Display Device */
- if (!(pVBInfo->VBType & VB_NoLCD)) {
- if (tempbx & XGI_SetCRT2ToLCDA) {
- if (tempbx & SetSimuScanMode)
- tempbx &= (~(SetCRT2ToLCD |
- SetCRT2ToRAMDAC |
- SwitchCRT2));
- else
- tempbx &= (~(SetCRT2ToLCD |
- SetCRT2ToRAMDAC |
- SetCRT2ToTV |
- SwitchCRT2));
- }
+ if (!(pVBInfo->VBType & VB_NoLCD)) {
+ if (tempbx & XGI_SetCRT2ToLCDA) {
+ if (tempbx & SetSimuScanMode)
+ tempbx &= (~(SetCRT2ToLCD |
+ SetCRT2ToRAMDAC |
+ SwitchCRT2));
+ else
+ tempbx &= (~(SetCRT2ToLCD |
+ SetCRT2ToRAMDAC |
+ SetCRT2ToTV |
+ SwitchCRT2));
}
}
@@ -2777,11 +2583,9 @@ static void XGI_GetVBInfo(unsigned short ModeNo, unsigned short ModeIdIndex,
if (!(tempbx & DisableCRT2Display)) {
if ((!(tempbx & DriverMode)) ||
(!(modeflag & CRT2Mode))) {
- if (pVBInfo->IF_DEF_LCDA == 1) {
- if (!(tempbx & XGI_SetCRT2ToLCDA))
- tempbx |= (SetInSlaveMode |
- SetSimuScanMode);
- }
+ if (!(tempbx & XGI_SetCRT2ToLCDA))
+ tempbx |= (SetInSlaveMode |
+ SetSimuScanMode);
}
/* LCD+TV can't support in slave mode
@@ -2867,19 +2671,17 @@ static void XGI_GetTVInfo(unsigned short ModeNo, unsigned short ModeIdIndex,
if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
if (pVBInfo->VBInfo & SetInSlaveMode)
tempbx &= (~RPLLDIV2XO);
- } else {
- if (tempbx &
- (TVSetYPbPr525p | TVSetYPbPr750p))
+ } else if (tempbx &
+ (TVSetYPbPr525p | TVSetYPbPr750p)) {
tempbx &= (~RPLLDIV2XO);
- else if (!(pVBInfo->VBType &
+ } else if (!(pVBInfo->VBType &
(VB_SIS301B |
VB_SIS302B |
VB_SIS301LV |
VB_SIS302LV |
VB_XGI301C))) {
- if (tempbx & TVSimuMode)
- tempbx &= (~RPLLDIV2XO);
- }
+ if (tempbx & TVSimuMode)
+ tempbx &= (~RPLLDIV2XO);
}
}
}
@@ -2960,20 +2762,6 @@ static unsigned char XGI_GetLCDInfo(unsigned short ModeNo,
tempbx |= SetLCDtoNonExpanding;
}
- if (pVBInfo->IF_DEF_ExpLink == 1) {
- if (modeflag & HalfDCLK) {
- if (!(tempbx & SetLCDtoNonExpanding)) {
- tempbx |= XGI_EnableLVDSDDA;
- } else {
- if (pVBInfo->LCDResInfo == Panel_1024x768) {
- if (resinfo == 4) {/* 512x384 */
- tempbx |= XGI_EnableLVDSDDA;
- }
- }
- }
- }
- }
-
if (pVBInfo->VBInfo & SetInSlaveMode) {
if (pVBInfo->VBInfo & SetNotSimuMode)
tempbx |= XGI_LCDVESATiming;
@@ -3122,33 +2910,6 @@ static void XGI_XG27BLSignalVDD(unsigned short tempbh, unsigned short tempbl,
xgifb_reg_and_or(pVBInfo->P3d4, 0x48, ~tempbh, tempbl);
}
-/* --------------------------------------------------------------------- */
-/* Function : XGI_XG21SetPanelDelay */
-/* Input : */
-/* Output : */
-/* Description : */
-/* I/P : bl : 1 ; T1 : the duration between CPL on and signal on */
-/* : bl : 2 ; T2 : the duration signal on and Vdd on */
-/* : bl : 3 ; T3 : the duration between CPL off and signal off */
-/* : bl : 4 ; T4 : the duration signal off and Vdd off */
-/* --------------------------------------------------------------------- */
-static void XGI_XG21SetPanelDelay(struct xgifb_video_info *xgifb_info,
- unsigned short tempbl,
- struct vb_device_info *pVBInfo)
-{
- if (tempbl == 1)
- mdelay(xgifb_info->lvds_data.PSC_S1);
-
- if (tempbl == 2)
- mdelay(xgifb_info->lvds_data.PSC_S2);
-
- if (tempbl == 3)
- mdelay(xgifb_info->lvds_data.PSC_S3);
-
- if (tempbl == 4)
- mdelay(xgifb_info->lvds_data.PSC_S4);
-}
-
static void XGI_DisplayOn(struct xgifb_video_info *xgifb_info,
struct xgi_hw_device_info *pXGIHWDE,
struct vb_device_info *pVBInfo)
@@ -3160,12 +2921,12 @@ static void XGI_DisplayOn(struct xgifb_video_info *xgifb_info,
if (!(XGI_XG21GetPSCValue(pVBInfo) & 0x1)) {
/* LVDS VDD on */
XGI_XG21BLSignalVDD(0x01, 0x01, pVBInfo);
- XGI_XG21SetPanelDelay(xgifb_info, 2, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S2);
}
if (!(XGI_XG21GetPSCValue(pVBInfo) & 0x20))
/* LVDS signal on */
XGI_XG21BLSignalVDD(0x20, 0x20, pVBInfo);
- XGI_XG21SetPanelDelay(xgifb_info, 3, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S3);
/* LVDS backlight on */
XGI_XG21BLSignalVDD(0x02, 0x02, pVBInfo);
} else {
@@ -3180,12 +2941,12 @@ static void XGI_DisplayOn(struct xgifb_video_info *xgifb_info,
if (!(XGI_XG27GetPSCValue(pVBInfo) & 0x1)) {
/* LVDS VDD on */
XGI_XG27BLSignalVDD(0x01, 0x01, pVBInfo);
- XGI_XG21SetPanelDelay(xgifb_info, 2, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S2);
}
if (!(XGI_XG27GetPSCValue(pVBInfo) & 0x20))
/* LVDS signal on */
XGI_XG27BLSignalVDD(0x20, 0x20, pVBInfo);
- XGI_XG21SetPanelDelay(xgifb_info, 3, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S3);
/* LVDS backlight on */
XGI_XG27BLSignalVDD(0x02, 0x02, pVBInfo);
} else {
@@ -3205,7 +2966,7 @@ void XGI_DisplayOff(struct xgifb_video_info *xgifb_info,
if (pVBInfo->IF_DEF_LVDS == 1) {
/* LVDS backlight off */
XGI_XG21BLSignalVDD(0x02, 0x00, pVBInfo);
- XGI_XG21SetPanelDelay(xgifb_info, 3, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S3);
} else {
/* DVO/DVI signal off */
XGI_XG21BLSignalVDD(0x20, 0x00, pVBInfo);
@@ -3216,7 +2977,7 @@ void XGI_DisplayOff(struct xgifb_video_info *xgifb_info,
if ((XGI_XG27GetPSCValue(pVBInfo) & 0x2)) {
/* LVDS backlight off */
XGI_XG27BLSignalVDD(0x02, 0x00, pVBInfo);
- XGI_XG21SetPanelDelay(xgifb_info, 3, pVBInfo);
+ mdelay(xgifb_info->lvds_data.PSC_S3);
}
if (pVBInfo->IF_DEF_LVDS == 0)
@@ -3378,7 +3139,6 @@ static void XGI_GetCRT2Data(unsigned short ModeNo, unsigned short ModeIdIndex,
unsigned short tempax = 0, tempbx, modeflag, resinfo;
struct SiS_LCDData *LCDPtr = NULL;
- struct SiS_TVData *TVPtr = NULL;
/* si+Ext_ResInfo */
modeflag = pVBInfo->EModeIDTable[ModeIdIndex].Ext_ModeFlag;
@@ -3395,9 +3155,8 @@ static void XGI_GetCRT2Data(unsigned short ModeNo, unsigned short ModeIdIndex,
tempbx = 4;
if (pVBInfo->VBInfo & (SetCRT2ToLCD | XGI_SetCRT2ToLCDA)) {
- LCDPtr = (struct SiS_LCDData *) XGI_GetLcdPtr(tempbx,
- ModeNo, ModeIdIndex, RefreshRateTableIndex,
- pVBInfo);
+ LCDPtr = XGI_GetLcdPtr(tempbx, ModeNo, ModeIdIndex,
+ RefreshRateTableIndex, pVBInfo);
pVBInfo->RVBHCMAX = LCDPtr->RVBHCMAX;
pVBInfo->RVBHCFACT = LCDPtr->RVBHCFACT;
@@ -3479,10 +3238,10 @@ static void XGI_GetCRT2Data(unsigned short ModeNo, unsigned short ModeIdIndex,
}
if (pVBInfo->VBInfo & (SetCRT2ToTV)) {
- tempbx = 4;
- TVPtr = (struct SiS_TVData *) XGI_GetTVPtr(tempbx,
- ModeNo, ModeIdIndex, RefreshRateTableIndex,
- pVBInfo);
+ struct SiS_TVData const *TVPtr;
+
+ TVPtr = XGI_GetTVPtr(ModeNo, ModeIdIndex, RefreshRateTableIndex,
+ pVBInfo);
pVBInfo->RVBHCMAX = TVPtr->RVBHCMAX;
pVBInfo->RVBHCFACT = TVPtr->RVBHCFACT;
@@ -3882,16 +3641,9 @@ static void XGI_SetLockRegs(unsigned short ModeNo, unsigned short ModeIdIndex,
| VB_SIS302LV | VB_XGI301C)))
temp += 2;
- if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
- if (pVBInfo->VBType & VB_SIS301LV) {
- if (pVBInfo->VBExtInfo == VB_YPbPr1080i) {
- if (resinfo == 7)
- temp -= 2;
- }
- } else if (resinfo == 7) {
+ if ((pVBInfo->VBInfo & SetCRT2ToHiVision) &&
+ !(pVBInfo->VBType & VB_SIS301LV) && (resinfo == 7))
temp -= 2;
- }
- }
}
/* 0x05 Horizontal Display Start */
@@ -4061,18 +3813,16 @@ static void XGI_SetLockRegs(unsigned short ModeNo, unsigned short ModeIdIndex,
} else {
tempbx -= 10;
}
- } else {
- if (pVBInfo->TVInfo & TVSimuMode) {
- if (pVBInfo->TVInfo & TVSetPAL) {
- if (pVBInfo->VBType & VB_SIS301LV) {
- if (!(pVBInfo->TVInfo &
- (TVSetYPbPr525p |
- TVSetYPbPr750p |
- TVSetHiVision)))
- tempbx += 40;
- } else {
+ } else if (pVBInfo->TVInfo & TVSimuMode) {
+ if (pVBInfo->TVInfo & TVSetPAL) {
+ if (pVBInfo->VBType & VB_SIS301LV) {
+ if (!(pVBInfo->TVInfo &
+ (TVSetYPbPr525p |
+ TVSetYPbPr750p |
+ TVSetHiVision)))
tempbx += 40;
- }
+ } else {
+ tempbx += 40;
}
}
}
@@ -4154,7 +3904,7 @@ static void XGI_SetGroup2(unsigned short ModeNo, unsigned short ModeIdIndex,
{
unsigned short i, j, tempax, tempbx, tempcx, temp, push1, push2,
modeflag, resinfo, crt2crtc;
- unsigned char *TimingPoint;
+ unsigned char const *TimingPoint;
unsigned long longtemp, tempeax, tempebx, temp2, tempecx;
@@ -4186,33 +3936,33 @@ static void XGI_SetGroup2(unsigned short ModeNo, unsigned short ModeIdIndex,
tempax = (tempax & 0xff00) >> 8;
xgifb_reg_set(pVBInfo->Part2Port, 0x0, tempax);
- TimingPoint = pVBInfo->NTSCTiming;
+ TimingPoint = XGI330_NTSCTiming;
if (pVBInfo->TVInfo & TVSetPAL)
- TimingPoint = pVBInfo->PALTiming;
+ TimingPoint = XGI330_PALTiming;
if (pVBInfo->VBInfo & SetCRT2ToHiVision) {
- TimingPoint = pVBInfo->HiTVExtTiming;
+ TimingPoint = XGI330_HiTVExtTiming;
if (pVBInfo->VBInfo & SetInSlaveMode)
- TimingPoint = pVBInfo->HiTVSt2Timing;
+ TimingPoint = XGI330_HiTVSt2Timing;
if (pVBInfo->SetFlag & TVSimuMode)
- TimingPoint = pVBInfo->HiTVSt1Timing;
+ TimingPoint = XGI330_HiTVSt1Timing;
if (!(modeflag & Charx8Dot))
- TimingPoint = pVBInfo->HiTVTextTiming;
+ TimingPoint = XGI330_HiTVTextTiming;
}
if (pVBInfo->VBInfo & SetCRT2ToYPbPr525750) {
if (pVBInfo->TVInfo & TVSetYPbPr525i)
- TimingPoint = pVBInfo->YPbPr525iTiming;
+ TimingPoint = XGI330_YPbPr525iTiming;
if (pVBInfo->TVInfo & TVSetYPbPr525p)
- TimingPoint = pVBInfo->YPbPr525pTiming;
+ TimingPoint = XGI330_YPbPr525pTiming;
if (pVBInfo->TVInfo & TVSetYPbPr750p)
- TimingPoint = pVBInfo->YPbPr750pTiming;
+ TimingPoint = XGI330_YPbPr750pTiming;
}
for (i = 0x01, j = 0; i <= 0x2D; i++, j++)
@@ -4385,11 +4135,9 @@ static void XGI_SetGroup2(unsigned short ModeNo, unsigned short ModeIdIndex,
temp += 1;
}
}
- } else {
- if (pVBInfo->VBInfo & SetInSlaveMode) {
- if (ModeNo == 0x2f)
- temp += 1;
- }
+ } else if (pVBInfo->VBInfo & SetInSlaveMode) {
+ if (ModeNo == 0x2f)
+ temp += 1;
}
}
@@ -4644,8 +4392,8 @@ static void XGI_SetLCDRegs(unsigned short ModeNo, unsigned short ModeIdIndex,
/* Customized LCDB Des no add */
tempbx = 5;
- LCDBDesPtr = (struct XGI_LCDDesStruct *) XGI_GetLcdPtr(tempbx, ModeNo,
- ModeIdIndex, RefreshRateTableIndex, pVBInfo);
+ LCDBDesPtr = XGI_GetLcdPtr(tempbx, ModeNo, ModeIdIndex,
+ RefreshRateTableIndex, pVBInfo);
tempah = pVBInfo->LCDResInfo;
tempah &= PanelResInfo;
@@ -4876,7 +4624,7 @@ static void XGI_SetGroup3(unsigned short ModeNo, unsigned short ModeIdIndex,
struct vb_device_info *pVBInfo)
{
unsigned short i;
- unsigned char *tempdi;
+ unsigned char const *tempdi;
unsigned short modeflag;
/* si+Ext_ResInfo */
@@ -4905,18 +4653,18 @@ static void XGI_SetGroup3(unsigned short ModeNo, unsigned short ModeIdIndex,
if (pVBInfo->TVInfo & TVSetYPbPr525i)
return;
- tempdi = pVBInfo->HiTVGroup3Data;
+ tempdi = XGI330_HiTVGroup3Data;
if (pVBInfo->SetFlag & TVSimuMode) {
- tempdi = pVBInfo->HiTVGroup3Simu;
+ tempdi = XGI330_HiTVGroup3Simu;
if (!(modeflag & Charx8Dot))
- tempdi = pVBInfo->HiTVGroup3Text;
+ tempdi = XGI330_HiTVGroup3Text;
}
if (pVBInfo->TVInfo & TVSetYPbPr525p)
- tempdi = pVBInfo->Ren525pGroup3;
+ tempdi = XGI330_Ren525pGroup3;
if (pVBInfo->TVInfo & TVSetYPbPr750p)
- tempdi = pVBInfo->Ren750pGroup3;
+ tempdi = XGI330_Ren750pGroup3;
for (i = 0; i <= 0x3E; i++)
xgifb_reg_set(pVBInfo->Part3Port, i, tempdi[i]);
@@ -5054,13 +4802,11 @@ static void XGI_SetGroup4(unsigned short ModeNo, unsigned short ModeIdIndex,
if (pVBInfo->VBInfo & SetCRT2ToLCD) {
if (tempax > 800)
tempax -= 800;
- } else {
- if (pVBInfo->VGAHDE > 800) {
- if (pVBInfo->VGAHDE == 1024)
- tempax = (tempax * 25 / 32) - 1;
- else
- tempax = (tempax * 20 / 32) - 1;
- }
+ } else if (pVBInfo->VGAHDE > 800) {
+ if (pVBInfo->VGAHDE == 1024)
+ tempax = (tempax * 25 / 32) - 1;
+ else
+ tempax = (tempax * 20 / 32) - 1;
}
tempax -= 1;
@@ -5101,9 +4847,7 @@ static void XGI_SetGroup4(unsigned short ModeNo, unsigned short ModeIdIndex,
}
/* end 301b */
- if (pVBInfo->ISXPDOS == 0)
- XGI_SetCRT2VCLK(ModeNo, ModeIdIndex, RefreshRateTableIndex,
- pVBInfo);
+ XGI_SetCRT2VCLK(ModeNo, ModeIdIndex, RefreshRateTableIndex, pVBInfo);
}
static void XGINew_EnableCRT2(struct vb_device_info *pVBInfo)
@@ -6414,12 +6158,10 @@ static void XGI_EnableBridge(struct xgifb_video_info *xgifb_info,
if (pVBInfo->SetFlag & EnableChA) {
/* Power on */
xgifb_reg_set(pVBInfo->Part1Port, 0x1E, 0x20);
- } else {
- if (pVBInfo->VBInfo & SetCRT2ToDualEdge) {
- /* Power on */
- xgifb_reg_set(pVBInfo->Part1Port,
- 0x1E, 0x20);
- }
+ } else if (pVBInfo->VBInfo & SetCRT2ToDualEdge) {
+ /* Power on */
+ xgifb_reg_set(pVBInfo->Part1Port,
+ 0x1E, 0x20);
}
}
@@ -6607,7 +6349,6 @@ unsigned char XGISetModeNew(struct xgifb_video_info *xgifb_info,
struct vb_device_info *pVBInfo = &VBINF;
pVBInfo->BaseAddr = xgifb_info->vga_base;
pVBInfo->IF_DEF_LVDS = 0;
- pVBInfo->IF_DEF_LCDA = 1;
if (HwDeviceExtension->jChipType >= XG20) {
pVBInfo->IF_DEF_YPbPr = 0;
@@ -6678,16 +6419,14 @@ unsigned char XGISetModeNew(struct xgifb_video_info *xgifb_info,
XGI_SetLCDAGroup(ModeNo, ModeIdIndex,
HwDeviceExtension, pVBInfo);
}
- } else {
- if (!(pVBInfo->VBInfo & SwitchCRT2)) {
- XGI_SetCRT1Group(xgifb_info,
- HwDeviceExtension, ModeNo,
- ModeIdIndex, pVBInfo);
- if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA) {
- XGI_SetLCDAGroup(ModeNo, ModeIdIndex,
- HwDeviceExtension,
- pVBInfo);
- }
+ } else if (!(pVBInfo->VBInfo & SwitchCRT2)) {
+ XGI_SetCRT1Group(xgifb_info,
+ HwDeviceExtension, ModeNo,
+ ModeIdIndex, pVBInfo);
+ if (pVBInfo->VBInfo & XGI_SetCRT2ToLCDA) {
+ XGI_SetLCDAGroup(ModeNo, ModeIdIndex,
+ HwDeviceExtension,
+ pVBInfo);
}
}
diff --git a/drivers/staging/xgifb/vb_struct.h b/drivers/staging/xgifb/vb_struct.h
index 22c8eb9810d6..70158c2c68af 100644
--- a/drivers/staging/xgifb/vb_struct.h
+++ b/drivers/staging/xgifb/vb_struct.h
@@ -43,13 +43,6 @@ struct XGI_LCDDesStruct {
unsigned short LCDVRS;
};
-struct XGI_LCDDataTablStruct {
- unsigned char PANELID;
- unsigned short MASK;
- unsigned short CAP;
- unsigned short DATAPTR;
-};
-
struct XGI330_LCDDataDesStruct2 {
unsigned short LCDHDES;
unsigned short LCDHRS;
@@ -59,19 +52,6 @@ struct XGI330_LCDDataDesStruct2 {
unsigned short LCDVSync;
};
-
-struct XGI330_TVDataStruct {
- unsigned short RVBHCMAX;
- unsigned short RVBHCFACT;
- unsigned short VGAHT;
- unsigned short VGAVT;
- unsigned short TVHDE;
- unsigned short TVVDE;
- unsigned short RVBHRS;
- unsigned char FlickerMode;
- unsigned short HALFRVBHRS;
-};
-
struct XGI330_LCDDataTablStruct {
unsigned char PANELID;
unsigned short MASK;
@@ -82,7 +62,7 @@ struct XGI330_LCDDataTablStruct {
struct XGI330_TVDataTablStruct {
unsigned short MASK;
unsigned short CAP;
- unsigned short DATAPTR;
+ struct SiS_TVData const *DATAPTR;
};
@@ -137,10 +117,10 @@ struct XGI21_LVDSCapStruct {
unsigned short LVDSVSYNC;
unsigned char VCLKData1;
unsigned char VCLKData2;
- unsigned char PSC_S1;
- unsigned char PSC_S2;
- unsigned char PSC_S3;
- unsigned char PSC_S4;
+ unsigned char PSC_S1; /* Duration between CPL on and signal on */
+ unsigned char PSC_S2; /* Duration signal on and Vdd on */
+ unsigned char PSC_S3; /* Duration between CPL off and signal off */
+ unsigned char PSC_S4; /* Duration signal off and Vdd off */
unsigned char PSC_S5;
};
@@ -155,7 +135,6 @@ struct XGI301C_Tap4TimingStruct {
};
struct vb_device_info {
- unsigned char ISXPDOS;
unsigned long P3c4, P3d4, P3c0, P3ce, P3c2, P3cc;
unsigned long P3ca, P3c6, P3c7, P3c8, P3c9, P3da;
unsigned long Part0Port, Part1Port, Part2Port;
@@ -168,12 +147,10 @@ struct vb_device_info {
unsigned short ModeType;
unsigned short IF_DEF_LVDS, IF_DEF_TRUMPION, IF_DEF_DSTN;
unsigned short IF_DEF_CRT2Monitor;
- unsigned short IF_DEF_LCDA, IF_DEF_YPbPr;
- unsigned short IF_DEF_ExpLink;
+ unsigned short IF_DEF_YPbPr;
unsigned short IF_DEF_HiVision;
unsigned short LCDResInfo, LCDTypeInfo, VBType;/*301b*/
unsigned short VBInfo, TVInfo, LCDInfo;
- unsigned short VBExtInfo;/*301lv*/
unsigned short SetFlag;
unsigned short NewFlickerMode;
unsigned short SelectCRT2Rate;
@@ -197,20 +174,6 @@ struct vb_device_info {
struct SiS_MCLKData *MCLKData;
struct XGI_ECLKDataStruct *ECLKData;
- unsigned char *NTSCTiming;
- unsigned char *PALTiming;
- unsigned char *HiTVExtTiming;
- unsigned char *HiTVSt1Timing;
- unsigned char *HiTVSt2Timing;
- unsigned char *HiTVTextTiming;
- unsigned char *YPbPr750pTiming;
- unsigned char *YPbPr525pTiming;
- unsigned char *YPbPr525iTiming;
- unsigned char *HiTVGroup3Data;
- unsigned char *HiTVGroup3Simu;
- unsigned char *HiTVGroup3Text;
- unsigned char *Ren525pGroup3;
- unsigned char *Ren750pGroup3;
unsigned char *ScreenOffset;
unsigned char *pXGINew_DRAMTypeDefinition;
unsigned char XGINew_CR97;
diff --git a/drivers/staging/xgifb/vb_table.h b/drivers/staging/xgifb/vb_table.h
index 1c168461411d..180aae042cea 100644
--- a/drivers/staging/xgifb/vb_table.h
+++ b/drivers/staging/xgifb/vb_table.h
@@ -403,13 +403,6 @@ static struct XGI_CRT1TableStruct XGI_CRT1Table[] = {
0x03, 0xDE, 0xC0, 0x84, 0xBF, 0x04, 0x90} } /* 0x47 */
};
-static unsigned char XGI_CH7017LV1024x768[] = {
- 0x60, 0x02, 0x00, 0x07, 0x40, 0xED,
- 0xA3, 0xC8, 0xC7, 0xAC, 0xE0, 0x02};
-static unsigned char XGI_CH7017LV1400x1050[] = {
- 0x60, 0x03, 0x11, 0x00, 0x40, 0xE3,
- 0xAD, 0xDB, 0xF6, 0xAC, 0xE0, 0x02};
-
/*add for new UNIVGABIOS*/
static struct SiS_LCDData XGI_StLCD1024x768Data[] = {
{62, 25, 800, 546, 1344, 806},
@@ -525,18 +518,7 @@ static struct SiS_LCDData XGI_StLCD1600x1200Data[] = {
{1, 1, 2160, 1250, 2160, 1250} /* 09 (1600x1200) */
};
-static struct SiS_LCDData XGI_CetLCD1400x1050Data[] = {
- {1, 1, 1688, 1066, 1688, 1066}, /* 00 (320x200,320x400,
- 640x200,640x400) */
- {1, 1, 1688, 1066, 1688, 1066}, /* 01 (320x350,640x350) */
- {1, 1, 1688, 1066, 1688, 1066}, /* 02 (360x400,720x400) */
- {1, 1, 1688, 1066, 1688, 1066}, /* 03 (720x350) */
- {1, 1, 1688, 1066, 1688, 1066}, /* 04 (640x480x60Hz) */
- {1, 1, 1688, 1066, 1688, 1066}, /* 05 (800x600x60Hz) */
- {1, 1, 1688, 1066, 1688, 1066}, /* 06 (1024x768x60Hz) */
- {1, 1, 1688, 1066, 1688, 1066}, /* 07 (1280x1024x60Hz) */
- {1, 1, 1688, 1066, 1688, 1066} /* 08 (1400x1050x60Hz) */
-};
+#define XGI_CetLCD1400x1050Data XGI_CetLCD1280x1024Data
static struct SiS_LCDData XGI_NoScalingData[] = {
{1, 1, 800, 449, 800, 449},
@@ -583,17 +565,7 @@ static struct SiS_LCDData xgifb_lcd_1280x1024x75[] = {
{1, 1, 1688, 1066, 1688, 1066} /* ; 07 (1280x1024x75Hz) */
};
-static struct SiS_LCDData XGI_CetLCD1280x1024x75Data[] = {
- {1, 1, 1688, 1066, 1688, 1066}, /* ; 00 (320x200,320x400,
- 640x200,640x400) */
- {1, 1, 1688, 1066, 1688, 1066}, /* ; 01 (320x350,640x350) */
- {1, 1, 1688, 1066, 1688, 1066}, /* ; 02 (360x400,720x400) */
- {1, 1, 1688, 1066, 1688, 1066}, /* ; 03 (720x350) */
- {1, 1, 1688, 1066, 1688, 1066}, /* ; 04 (640x480x75Hz) */
- {1, 1, 1688, 1066, 1688, 1066}, /* ; 05 (800x600x75Hz) */
- {1, 1, 1688, 1066, 1688, 1066}, /* ; 06 (1024x768x75Hz) */
- {1, 1, 1688, 1066, 1688, 1066} /* ; 07 (1280x1024x75Hz) */
-};
+#define XGI_CetLCD1280x1024x75Data XGI_CetLCD1280x1024Data
static struct SiS_LCDData XGI_NoScalingDatax75[] = {
{1, 1, 800, 449, 800, 449}, /* ; 00 (320x200, 320x400,
@@ -903,7 +875,7 @@ static struct XGI330_LCDDataDesStruct2 XGI_NoScalingDesDatax75[] = {
{9, 1337, 0, 771, 112, 6} /* ; 0A (1280x768x60Hz) */
};
-static struct XGI330_TVDataStruct XGI_StPALData[] = {
+static const struct SiS_TVData XGI_StPALData[] = {
{1, 1, 864, 525, 1270, 400, 100, 0, 760},
{1, 1, 864, 525, 1270, 350, 100, 0, 760},
{1, 1, 864, 525, 1270, 400, 0, 0, 720},
@@ -912,7 +884,7 @@ static struct XGI330_TVDataStruct XGI_StPALData[] = {
{1, 1, 864, 525, 1270, 600, 50, 0, 0}
};
-static struct XGI330_TVDataStruct XGI_ExtPALData[] = {
+static const struct SiS_TVData XGI_ExtPALData[] = {
{2, 1, 1080, 463, 1270, 500, 50, 0, 50},
{15, 7, 1152, 413, 1270, 500, 50, 0, 50},
{2, 1, 1080, 463, 1270, 500, 50, 0, 50},
@@ -923,7 +895,7 @@ static struct XGI330_TVDataStruct XGI_ExtPALData[] = {
{3, 2, 1080, 619, 1270, 540, 438, 0, 438}
};
-static struct XGI330_TVDataStruct XGI_StNTSCData[] = {
+static const struct SiS_TVData XGI_StNTSCData[] = {
{1, 1, 858, 525, 1270, 400, 50, 0, 760},
{1, 1, 858, 525, 1270, 350, 50, 0, 640},
{1, 1, 858, 525, 1270, 400, 0, 0, 720},
@@ -931,7 +903,7 @@ static struct XGI330_TVDataStruct XGI_StNTSCData[] = {
{1, 1, 858, 525, 1270, 480, 0, 0, 760}
};
-static struct XGI330_TVDataStruct XGI_ExtNTSCData[] = {
+static const struct SiS_TVData XGI_ExtNTSCData[] = {
{9, 5, 1001, 453, 1270, 420, 171, 0, 171},
{12, 5, 858, 403, 1270, 420, 171, 0, 171},
{9, 5, 1001, 453, 1270, 420, 171, 0, 171},
@@ -943,7 +915,7 @@ static struct XGI330_TVDataStruct XGI_ExtNTSCData[] = {
{3, 2, 1001, 533, 1270, 420, 0, 0, 0}
};
-static struct XGI330_TVDataStruct XGI_St1HiTVData[] = {
+static const struct SiS_TVData XGI_St1HiTVData[] = {
{1, 1, 892, 563, 690, 800, 0, 0, 0}, /* 00 (320x200,320x400,
640x200,640x400) */
{1, 1, 892, 563, 690, 700, 0, 0, 0}, /* 01 (320x350,640x350) */
@@ -953,7 +925,7 @@ static struct XGI330_TVDataStruct XGI_St1HiTVData[] = {
{8, 5, 1050, 683, 1648, 960, 0x150, 1, 0} /* 05 (400x300,800x600) */
};
-static struct XGI330_TVDataStruct XGI_St2HiTVData[] = {
+static const struct SiS_TVData XGI_St2HiTVData[] = {
{3, 1, 840, 483, 1648, 960, 0x032, 0, 0}, /* 00 (320x200,320x400,
640x200,640x400) */
{1, 1, 892, 563, 690, 700, 0, 0, 0}, /* 01 (320x350,640x350) */
@@ -963,7 +935,7 @@ static struct XGI330_TVDataStruct XGI_St2HiTVData[] = {
{8, 5, 1050, 683, 1648, 960, 0x17C, 1, 0} /* 05 (400x300,800x600) */
};
-static struct XGI330_TVDataStruct XGI_ExtHiTVData[] = {
+static const struct SiS_TVData XGI_ExtHiTVData[] = {
{6, 1, 840, 563, 1632, 960, 0, 0, 0}, /* 00 (320x200,320x400,
640x200,640x400) */
{3, 1, 960, 563, 1632, 960, 0, 0, 0}, /* 01 (320x350,640x350) */
@@ -978,7 +950,7 @@ static struct XGI330_TVDataStruct XGI_ExtHiTVData[] = {
{8, 5, 1750, 803, 1648, 960, 0x128, 0, 0} /* 0A (1280x720) */
};
-static struct XGI330_TVDataStruct XGI_ExtYPbPr525iData[] = {
+static const struct SiS_TVData XGI_ExtYPbPr525iData[] = {
{ 9, 5, 1001, 453, 1270, 420, 171, 0, 171},
{ 12, 5, 858, 403, 1270, 420, 171, 0, 171},
{ 9, 5, 1001, 453, 1270, 420, 171, 0, 171},
@@ -990,7 +962,7 @@ static struct XGI330_TVDataStruct XGI_ExtYPbPr525iData[] = {
{ 3, 2, 1001, 533, 1250, 420, 0, 0, 0}
};
-static struct XGI330_TVDataStruct XGI_StYPbPr525iData[] = {
+static const struct SiS_TVData XGI_StYPbPr525iData[] = {
{1, 1, 858, 525, 1270, 400, 50, 0, 760},
{1, 1, 858, 525, 1270, 350, 50, 0, 640},
{1, 1, 858, 525, 1270, 400, 0, 0, 720},
@@ -998,7 +970,7 @@ static struct XGI330_TVDataStruct XGI_StYPbPr525iData[] = {
{1, 1, 858, 525, 1270, 480, 0, 0, 760},
};
-static struct XGI330_TVDataStruct XGI_ExtYPbPr525pData[] = {
+static const struct SiS_TVData XGI_ExtYPbPr525pData[] = {
{ 9, 5, 1001, 453, 1270, 420, 171, 0, 171},
{ 12, 5, 858, 403, 1270, 420, 171, 0, 171},
{ 9, 5, 1001, 453, 1270, 420, 171, 0, 171},
@@ -1010,7 +982,7 @@ static struct XGI330_TVDataStruct XGI_ExtYPbPr525pData[] = {
{ 3, 2, 1001, 533, 1270, 420, 0, 0, 0}
};
-static struct XGI330_TVDataStruct XGI_StYPbPr525pData[] = {
+static const struct SiS_TVData XGI_StYPbPr525pData[] = {
{1, 1, 1716, 525, 1270, 400, 50, 0, 760},
{1, 1, 1716, 525, 1270, 350, 50, 0, 640},
{1, 1, 1716, 525, 1270, 400, 0, 0, 720},
@@ -1018,7 +990,7 @@ static struct XGI330_TVDataStruct XGI_StYPbPr525pData[] = {
{1, 1, 1716, 525, 1270, 480, 0, 0, 760},
};
-static struct XGI330_TVDataStruct XGI_ExtYPbPr750pData[] = {
+static const struct SiS_TVData XGI_ExtYPbPr750pData[] = {
{ 3, 1, 935, 470, 1130, 680, 50, 0, 0}, /* 00 (320x200,320x400,
640x200,640x400) */
{24, 7, 935, 420, 1130, 680, 50, 0, 0}, /* 01 (320x350,640x350) */
@@ -1033,7 +1005,7 @@ static struct XGI330_TVDataStruct XGI_ExtYPbPr750pData[] = {
{10, 9, 1320, 830, 1130, 640, 50, 0, 0}
};
-static struct XGI330_TVDataStruct XGI_StYPbPr750pData[] = {
+static const struct SiS_TVData XGI_StYPbPr750pData[] = {
{1, 1, 1650, 750, 1280, 400, 50, 0, 760},
{1, 1, 1650, 750, 1280, 350, 50, 0, 640},
{1, 1, 1650, 750, 1280, 400, 0, 0, 720},
@@ -1041,7 +1013,7 @@ static struct XGI330_TVDataStruct XGI_StYPbPr750pData[] = {
{1, 1, 1650, 750, 1280, 480, 0, 0, 760},
};
-static unsigned char XGI330_NTSCTiming[] = {
+static const unsigned char XGI330_NTSCTiming[] = {
0x17, 0x1d, 0x03, 0x09, 0x05, 0x06, 0x0c, 0x0c,
0x94, 0x49, 0x01, 0x0a, 0x06, 0x0d, 0x04, 0x0a,
0x06, 0x14, 0x0d, 0x04, 0x0a, 0x00, 0x85, 0x1b,
@@ -1052,7 +1024,7 @@ static unsigned char XGI330_NTSCTiming[] = {
0x00, 0x40, 0x44, 0x00, 0xdb, 0x02, 0x3b, 0x00
};
-static unsigned char XGI330_PALTiming[] = {
+static const unsigned char XGI330_PALTiming[] = {
0x21, 0x5A, 0x35, 0x6e, 0x04, 0x38, 0x3d, 0x70,
0x94, 0x49, 0x01, 0x12, 0x06, 0x3e, 0x35, 0x6d,
0x06, 0x14, 0x3e, 0x35, 0x6d, 0x00, 0x45, 0x2b,
@@ -1063,7 +1035,7 @@ static unsigned char XGI330_PALTiming[] = {
0x00, 0x40, 0x3e, 0x00, 0xe1, 0x02, 0x28, 0x00
};
-static unsigned char XGI330_HiTVExtTiming[] = {
+static const unsigned char XGI330_HiTVExtTiming[] = {
0x2D, 0x60, 0x2C, 0x5F, 0x08, 0x31, 0x3A, 0x64,
0x28, 0x02, 0x01, 0x3D, 0x06, 0x3E, 0x35, 0x6D,
0x06, 0x14, 0x3E, 0x35, 0x6D, 0x00, 0xC5, 0x3F,
@@ -1075,7 +1047,7 @@ static unsigned char XGI330_HiTVExtTiming[] = {
0x27, 0x00, 0xfc, 0xff, 0x6a, 0x00
};
-static unsigned char XGI330_HiTVSt1Timing[] = {
+static const unsigned char XGI330_HiTVSt1Timing[] = {
0x32, 0x65, 0x2C, 0x5F, 0x08, 0x31, 0x3A, 0x65,
0x28, 0x02, 0x01, 0x3D, 0x06, 0x3E, 0x35, 0x6D,
0x06, 0x14, 0x3E, 0x35, 0x6D, 0x00, 0xC5, 0x3F,
@@ -1087,7 +1059,7 @@ static unsigned char XGI330_HiTVSt1Timing[] = {
0x0E, 0x00, 0xfc, 0xff, 0x2d, 0x00
};
-static unsigned char XGI330_HiTVSt2Timing[] = {
+static const unsigned char XGI330_HiTVSt2Timing[] = {
0x32, 0x65, 0x2C, 0x5F, 0x08, 0x31, 0x3A, 0x64,
0x28, 0x02, 0x01, 0x3D, 0x06, 0x3E, 0x35, 0x6D,
0x06, 0x14, 0x3E, 0x35, 0x6D, 0x00, 0xC5, 0x3F,
@@ -1099,7 +1071,7 @@ static unsigned char XGI330_HiTVSt2Timing[] = {
0x27, 0x00, 0xFC, 0xff, 0x6a, 0x00
};
-static unsigned char XGI330_HiTVTextTiming[] = {
+static const unsigned char XGI330_HiTVTextTiming[] = {
0x32, 0x65, 0x2C, 0x5F, 0x08, 0x31, 0x3A, 0x65,
0x28, 0x02, 0x01, 0x3D, 0x06, 0x3E, 0x35, 0x6D,
0x06, 0x14, 0x3E, 0x35, 0x6D, 0x00, 0xC5, 0x3F,
@@ -1111,7 +1083,7 @@ static unsigned char XGI330_HiTVTextTiming[] = {
0x11, 0x00, 0xFC, 0xFF, 0x32, 0x00
};
-static unsigned char XGI330_YPbPr750pTiming[] = {
+static const unsigned char XGI330_YPbPr750pTiming[] = {
0x30, 0x1d, 0xe8, 0x09, 0x09, 0xed, 0x0c, 0x0c,
0x98, 0x0a, 0x01, 0x0c, 0x06, 0x0d, 0x04, 0x0a,
0x06, 0x14, 0x0d, 0x04, 0x0a, 0x00, 0x85, 0x3f,
@@ -1123,7 +1095,7 @@ static unsigned char XGI330_YPbPr750pTiming[] = {
0x11, 0x00, 0xfc, 0xff, 0x32, 0x00
};
-static unsigned char XGI330_YPbPr525pTiming[] = {
+static const unsigned char XGI330_YPbPr525pTiming[] = {
0x3E, 0x11, 0x06, 0x09, 0x0b, 0x0c, 0x0c, 0x0c,
0x98, 0x0a, 0x01, 0x0d, 0x06, 0x0d, 0x04, 0x0a,
0x06, 0x14, 0x0d, 0x04, 0x0a, 0x00, 0x85, 0x3f,
@@ -1135,7 +1107,7 @@ static unsigned char XGI330_YPbPr525pTiming[] = {
0x11, 0x00, 0xFC, 0xFF, 0x32, 0x00
};
-static unsigned char XGI330_YPbPr525iTiming[] = {
+static const unsigned char XGI330_YPbPr525iTiming[] = {
0x1B, 0x21, 0x03, 0x09, 0x05, 0x06, 0x0C, 0x0C,
0x94, 0x49, 0x01, 0x0A, 0x06, 0x0D, 0x04, 0x0A,
0x06, 0x14, 0x0D, 0x04, 0x0A, 0x00, 0x85, 0x1B,
@@ -1147,7 +1119,7 @@ static unsigned char XGI330_YPbPr525iTiming[] = {
0x44, 0x00, 0xDB, 0x02, 0x3B, 0x00
};
-static unsigned char XGI330_HiTVGroup3Data[] = {
+static const unsigned char XGI330_HiTVGroup3Data[] = {
0x00, 0x1A, 0x22, 0x63, 0x62, 0x22, 0x08, 0x5F,
0x05, 0x21, 0xB2, 0xB2, 0x55, 0x77, 0x2A, 0xA6,
0x25, 0x2F, 0x47, 0xFA, 0xC8, 0xFF, 0x8E, 0x20,
@@ -1158,7 +1130,7 @@ static unsigned char XGI330_HiTVGroup3Data[] = {
0x18, 0x05, 0x18, 0x05, 0x4C, 0xA8, 0x01
};
-static unsigned char XGI330_HiTVGroup3Simu[] = {
+static const unsigned char XGI330_HiTVGroup3Simu[] = {
0x00, 0x1A, 0x22, 0x63, 0x62, 0x22, 0x08, 0x95,
0xDB, 0x20, 0xB8, 0xB8, 0x55, 0x47, 0x2A, 0xA6,
0x25, 0x2F, 0x47, 0xFA, 0xC8, 0xFF, 0x8E, 0x20,
@@ -1169,7 +1141,7 @@ static unsigned char XGI330_HiTVGroup3Simu[] = {
0x18, 0x05, 0x18, 0x05, 0x4C, 0xA8, 0x01
};
-static unsigned char XGI330_HiTVGroup3Text[] = {
+static const unsigned char XGI330_HiTVGroup3Text[] = {
0x00, 0x1A, 0x22, 0x63, 0x62, 0x22, 0x08, 0xA7,
0xF5, 0x20, 0xCE, 0xCE, 0x55, 0x47, 0x2A, 0xA6,
0x25, 0x2F, 0x47, 0xFA, 0xC8, 0xFF, 0x8E, 0x20,
@@ -1180,7 +1152,7 @@ static unsigned char XGI330_HiTVGroup3Text[] = {
0x18, 0x05, 0x18, 0x05, 0x4C, 0xA8, 0x01
};
-static unsigned char XGI330_Ren525pGroup3[] = {
+static const unsigned char XGI330_Ren525pGroup3[] = {
0x00, 0x14, 0x15, 0x25, 0x55, 0x15, 0x0b, 0x13,
0xB1, 0x41, 0x62, 0x62, 0xFF, 0xF4, 0x45, 0xa6,
0x25, 0x2F, 0x67, 0xF6, 0xbf, 0xFF, 0x8E, 0x20,
@@ -1191,7 +1163,7 @@ static unsigned char XGI330_Ren525pGroup3[] = {
0x1a, 0x1F, 0x25, 0x2a, 0x4C, 0xAA, 0x01
};
-static unsigned char XGI330_Ren750pGroup3[] = {
+static const unsigned char XGI330_Ren750pGroup3[] = {
0x00, 0x14, 0x15, 0x25, 0x55, 0x15, 0x0b, 0x7a,
0x54, 0x41, 0xE7, 0xE7, 0xFF, 0xF4, 0x45, 0xa6,
0x25, 0x2F, 0x67, 0xF6, 0xbf, 0xFF, 0x8E, 0x20,
@@ -1236,17 +1208,7 @@ static struct SiS_LVDSData XGI_LVDS1280x1024Data_1[] = {
{1688, 1066, 1688, 1066}
};
-static struct SiS_LVDSData XGI_LVDS1280x1024Data_2[] = {
- {1344, 806, 1344, 806},
- {1344, 806, 1344, 806},
- {1344, 806, 1344, 806},
- {1344, 806, 1344, 806},
- {1344, 806, 1344, 806},
- {1344, 806, 1344, 806},
- {1344, 806, 1344, 806},
- {800, 449, 1280, 801},
- {800, 525, 1280, 813}
-};
+#define XGI_LVDS1280x1024Data_2 XGI_LVDS1024x768Data_2
static struct SiS_LVDSData XGI_LVDS1400x1050Data_1[] = {
{928, 416, 1688, 1066},
@@ -1532,42 +1494,6 @@ static struct XGI330_LCDDataDesStruct2 XGI_LVDSNoScalingDesDatax75[] = {
{0, 1328, 0, 771, 112, 6} /* ; 0A (1280x768x75Hz) */
};
-static struct SiS_LVDSData XGI_CHTVUNTSCData[] = {
- { 840, 600, 840, 600},
- { 840, 600, 840, 600},
- { 840, 600, 840, 600},
- { 840, 600, 840, 600},
- { 784, 600, 784, 600},
- {1064, 750, 1064, 750}
-};
-
-static struct SiS_LVDSData XGI_CHTVONTSCData[] = {
- { 840, 525, 840, 525},
- { 840, 525, 840, 525},
- { 840, 525, 840, 525},
- { 840, 525, 840, 525},
- { 784, 525, 784, 525},
- {1040, 700, 1040, 700}
-};
-
-static struct SiS_LVDSData XGI_CHTVUPALData[] = {
- {1008, 625, 1008, 625},
- {1008, 625, 1008, 625},
- {1008, 625, 1008, 625},
- {1008, 625, 1008, 625},
- { 840, 750, 840, 750},
- { 936, 836, 936, 836}
-};
-
-static struct SiS_LVDSData XGI_CHTVOPALData[] = {
- {1008, 625, 1008, 625},
- {1008, 625, 1008, 625},
- {1008, 625, 1008, 625},
- {1008, 625, 1008, 625},
- {840, 625, 840, 625},
- {960, 750, 960, 750}
-};
-
/* CR00,CR02,CR03,CR04,CR05,SR0B,SR0C,SR0E */
static struct XGI_LVDSCRT1HDataStruct XGI_LVDSCRT11024x768_1_H[] = {
{ {0x4B, 0x27, 0x8F, 0x32, 0x1B, 0x00, 0x45, 0x00} }, /* 00 (320x) */
@@ -1933,49 +1859,21 @@ static struct XGI330_LCDDataTablStruct XGI_EPLLCDDesDataPtr[] = {
{0xFF, 0x0000, 0x0000, 0}
};
-static struct XGI330_LCDDataTablStruct XGI_EPLCHLCDRegPtr[] = {
- {Panel_1024x768, 0x0000, 0x0000, 0}, /* XGI_CH7017LV1024x768 */
- {Panel_1400x1050, 0x0000, 0x0000, 1}, /* XGI_CH7017LV1400x1050 */
- {0xFF, 0x0000, 0x0000, 0}
-};
-
-static struct XGI330_TVDataTablStruct XGI_TVDataTable[] = {
- {0x09E1, 0x0001, 0}, /* XGI_ExtPALData */
- {0x09E1, 0x0000, 1}, /* XGI_ExtNTSCData */
- {0x09E1, 0x0801, 2}, /* XGI_StPALData */
- {0x09E1, 0x0800, 3}, /* XGI_StNTSCData */
- {0x49E0, 0x0100, 4}, /* XGI_ExtHiTVData */
- {0x49E0, 0x4100, 5}, /* XGI_St2HiTVData */
- {0x49E0, 0x4900, 13}, /* XGI_St1HiTVData */
- {0x09E0, 0x0020, 6}, /* XGI_ExtYPbPr525iData */
- {0x09E0, 0x0040, 7}, /* XGI_ExtYPbPr525pData */
- {0x09E0, 0x0080, 8}, /* XGI_ExtYPbPr750pData */
- {0x09E0, 0x0820, 9}, /* XGI_StYPbPr525iData */
- {0x09E0, 0x0840, 10}, /* XGI_StYPbPr525pData */
- {0x09E0, 0x0880, 11}, /* XGI_StYPbPr750pData */
- {0xffff, 0x0000, 12} /* END */
-};
-
-/* Chrontel 7017 TV List */
-static struct XGI330_TVDataTablStruct xgifb_chrontel_tv[] = {
- {0x0011, 0x0000, 0}, /* UNTSC */
- {0x0011, 0x0010, 1}, /* ONTSC */
- {0x0011, 0x0001, 2}, /* UPAL */
- {0x0011, 0x0011, 3}, /* OPAL */
- {0xFFFF, 0x0000, 4}
-};
-
-static unsigned short LCDLenList[] = {
- LVDSCRT1Len_H,
- LVDSCRT1Len_V,
- LVDSDataLen,
- LCDDesDataLen,
- LCDDataLen,
- LCDDesDataLen,
- 0,
- LCDDesDataLen,
- LCDDesDataLen,
- 0
+static const struct XGI330_TVDataTablStruct XGI_TVDataTable[] = {
+ {0x09E1, 0x0001, XGI_ExtPALData},
+ {0x09E1, 0x0000, XGI_ExtNTSCData},
+ {0x09E1, 0x0801, XGI_StPALData},
+ {0x09E1, 0x0800, XGI_StNTSCData},
+ {0x49E0, 0x0100, XGI_ExtHiTVData},
+ {0x49E0, 0x4100, XGI_St2HiTVData},
+ {0x49E0, 0x4900, XGI_St1HiTVData},
+ {0x09E0, 0x0020, XGI_ExtYPbPr525iData},
+ {0x09E0, 0x0040, XGI_ExtYPbPr525pData},
+ {0x09E0, 0x0080, XGI_ExtYPbPr750pData},
+ {0x09E0, 0x0820, XGI_StYPbPr525iData},
+ {0x09E0, 0x0840, XGI_StYPbPr525pData},
+ {0x09E0, 0x0880, XGI_StYPbPr750pData},
+ {0xffff, 0x0000, XGI_ExtNTSCData},
};
/* Dual link only */
@@ -2336,7 +2234,7 @@ static struct SiS_VCLKData XGI_VCLKData[] = {
{0xFF, 0x00, 0} /* End mark */
};
-static struct SiS_VCLKData XGI_VBVCLKData[] = {
+static struct SiS_VBVCLKData XGI_VBVCLKData[] = {
{0x1B, 0xE1, 25}, /* 00 (25.175MHz) */
{0x4E, 0xE4, 28}, /* 01 (28.322MHz) */
{0x57, 0xE4, 31}, /* 02 (31.500MHz) */
diff --git a/drivers/staging/zcache/tmem.c b/drivers/staging/zcache/tmem.c
index eaa90213457b..56c8e606ad1c 100644
--- a/drivers/staging/zcache/tmem.c
+++ b/drivers/staging/zcache/tmem.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2009-2011, Dan Magenheimer, Oracle Corp.
*
- * The primary purpose of Transcedent Memory ("tmem") is to map object-oriented
+ * The primary purpose of Transcendent Memory ("tmem") is to map object-oriented
* "handles" (triples containing a pool id, and object id, and an index), to
* pages in a page-accessible memory (PAM). Tmem references the PAM pages via
* an abstract "pampd" (PAM page-descriptor), which can be operated on by a
diff --git a/drivers/staging/zsmalloc/zsmalloc-main.c b/drivers/staging/zsmalloc/zsmalloc-main.c
index 8b0bcb626a7f..09a9d35d436f 100644
--- a/drivers/staging/zsmalloc/zsmalloc-main.c
+++ b/drivers/staging/zsmalloc/zsmalloc-main.c
@@ -75,9 +75,140 @@
#include <linux/cpumask.h>
#include <linux/cpu.h>
#include <linux/vmalloc.h>
+#include <linux/hardirq.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
#include "zsmalloc.h"
-#include "zsmalloc_int.h"
+
+/*
+ * This must be power of 2 and greater than of equal to sizeof(link_free).
+ * These two conditions ensure that any 'struct link_free' itself doesn't
+ * span more than 1 page which avoids complex case of mapping 2 pages simply
+ * to restore link_free pointer values.
+ */
+#define ZS_ALIGN 8
+
+/*
+ * A single 'zspage' is composed of up to 2^N discontiguous 0-order (single)
+ * pages. ZS_MAX_ZSPAGE_ORDER defines upper limit on N.
+ */
+#define ZS_MAX_ZSPAGE_ORDER 2
+#define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER)
+
+/*
+ * Object location (<PFN>, <obj_idx>) is encoded as
+ * as single (void *) handle value.
+ *
+ * Note that object index <obj_idx> is relative to system
+ * page <PFN> it is stored in, so for each sub-page belonging
+ * to a zspage, obj_idx starts with 0.
+ *
+ * This is made more complicated by various memory models and PAE.
+ */
+
+#ifndef MAX_PHYSMEM_BITS
+#ifdef CONFIG_HIGHMEM64G
+#define MAX_PHYSMEM_BITS 36
+#else /* !CONFIG_HIGHMEM64G */
+/*
+ * If this definition of MAX_PHYSMEM_BITS is used, OBJ_INDEX_BITS will just
+ * be PAGE_SHIFT
+ */
+#define MAX_PHYSMEM_BITS BITS_PER_LONG
+#endif
+#endif
+#define _PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT)
+#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS)
+#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1)
+
+#define MAX(a, b) ((a) >= (b) ? (a) : (b))
+/* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */
+#define ZS_MIN_ALLOC_SIZE \
+ MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS))
+#define ZS_MAX_ALLOC_SIZE PAGE_SIZE
+
+/*
+ * On systems with 4K page size, this gives 254 size classes! There is a
+ * trader-off here:
+ * - Large number of size classes is potentially wasteful as free page are
+ * spread across these classes
+ * - Small number of size classes causes large internal fragmentation
+ * - Probably its better to use specific size classes (empirically
+ * determined). NOTE: all those class sizes must be set as multiple of
+ * ZS_ALIGN to make sure link_free itself never has to span 2 pages.
+ *
+ * ZS_MIN_ALLOC_SIZE and ZS_SIZE_CLASS_DELTA must be multiple of ZS_ALIGN
+ * (reason above)
+ */
+#define ZS_SIZE_CLASS_DELTA 16
+#define ZS_SIZE_CLASSES ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / \
+ ZS_SIZE_CLASS_DELTA + 1)
+
+/*
+ * We do not maintain any list for completely empty or full pages
+ */
+enum fullness_group {
+ ZS_ALMOST_FULL,
+ ZS_ALMOST_EMPTY,
+ _ZS_NR_FULLNESS_GROUPS,
+
+ ZS_EMPTY,
+ ZS_FULL
+};
+
+/*
+ * We assign a page to ZS_ALMOST_EMPTY fullness group when:
+ * n <= N / f, where
+ * n = number of allocated objects
+ * N = total number of objects zspage can store
+ * f = 1/fullness_threshold_frac
+ *
+ * Similarly, we assign zspage to:
+ * ZS_ALMOST_FULL when n > N / f
+ * ZS_EMPTY when n == 0
+ * ZS_FULL when n == N
+ *
+ * (see: fix_fullness_group())
+ */
+static const int fullness_threshold_frac = 4;
+
+struct size_class {
+ /*
+ * Size of objects stored in this class. Must be multiple
+ * of ZS_ALIGN.
+ */
+ int size;
+ unsigned int index;
+
+ /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */
+ int pages_per_zspage;
+
+ spinlock_t lock;
+
+ /* stats */
+ u64 pages_allocated;
+
+ struct page *fullness_list[_ZS_NR_FULLNESS_GROUPS];
+};
+
+/*
+ * Placed within free objects to form a singly linked list.
+ * For every zspage, first_page->freelist gives head of this list.
+ *
+ * This must be power of 2 and less than or equal to ZS_ALIGN
+ */
+struct link_free {
+ /* Handle of next free chunk (encodes <PFN, obj_idx>) */
+ void *next;
+};
+
+struct zs_pool {
+ struct size_class size_class[ZS_SIZE_CLASSES];
+
+ gfp_t flags; /* allocation flags used when growing pool */
+ const char *name;
+};
/*
* A zspage's class index and fullness group
@@ -88,6 +219,30 @@
#define CLASS_IDX_MASK ((1 << CLASS_IDX_BITS) - 1)
#define FULLNESS_MASK ((1 << FULLNESS_BITS) - 1)
+/*
+ * By default, zsmalloc uses a copy-based object mapping method to access
+ * allocations that span two pages. However, if a particular architecture
+ * 1) Implements local_flush_tlb_kernel_range() and 2) Performs VM mapping
+ * faster than copying, then it should be added here so that
+ * USE_PGTABLE_MAPPING is defined. This causes zsmalloc to use page table
+ * mapping rather than copying
+ * for object mapping.
+*/
+#if defined(CONFIG_ARM)
+#define USE_PGTABLE_MAPPING
+#endif
+
+struct mapping_area {
+#ifdef USE_PGTABLE_MAPPING
+ struct vm_struct *vm; /* vm area for mapping object that span pages */
+#else
+ char *vm_buf; /* copy buffer for objects that span pages */
+#endif
+ char *vm_addr; /* address of kmap_atomic()'ed pages */
+ enum zs_mapmode vm_mm; /* mapping mode */
+};
+
+
/* per-cpu VM mapping areas for zspage accesses that cross page boundaries */
static DEFINE_PER_CPU(struct mapping_area, zs_map_area);
@@ -470,16 +625,83 @@ static struct page *find_get_zspage(struct size_class *class)
return page;
}
-static void zs_copy_map_object(char *buf, struct page *firstpage,
- int off, int size)
+#ifdef USE_PGTABLE_MAPPING
+static inline int __zs_cpu_up(struct mapping_area *area)
+{
+ /*
+ * Make sure we don't leak memory if a cpu UP notification
+ * and zs_init() race and both call zs_cpu_up() on the same cpu
+ */
+ if (area->vm)
+ return 0;
+ area->vm = alloc_vm_area(PAGE_SIZE * 2, NULL);
+ if (!area->vm)
+ return -ENOMEM;
+ return 0;
+}
+
+static inline void __zs_cpu_down(struct mapping_area *area)
+{
+ if (area->vm)
+ free_vm_area(area->vm);
+ area->vm = NULL;
+}
+
+static inline void *__zs_map_object(struct mapping_area *area,
+ struct page *pages[2], int off, int size)
+{
+ BUG_ON(map_vm_area(area->vm, PAGE_KERNEL, &pages));
+ area->vm_addr = area->vm->addr;
+ return area->vm_addr + off;
+}
+
+static inline void __zs_unmap_object(struct mapping_area *area,
+ struct page *pages[2], int off, int size)
+{
+ unsigned long addr = (unsigned long)area->vm_addr;
+ unsigned long end = addr + (PAGE_SIZE * 2);
+
+ flush_cache_vunmap(addr, end);
+ unmap_kernel_range_noflush(addr, PAGE_SIZE * 2);
+ local_flush_tlb_kernel_range(addr, end);
+}
+
+#else /* USE_PGTABLE_MAPPING */
+
+static inline int __zs_cpu_up(struct mapping_area *area)
+{
+ /*
+ * Make sure we don't leak memory if a cpu UP notification
+ * and zs_init() race and both call zs_cpu_up() on the same cpu
+ */
+ if (area->vm_buf)
+ return 0;
+ area->vm_buf = (char *)__get_free_page(GFP_KERNEL);
+ if (!area->vm_buf)
+ return -ENOMEM;
+ return 0;
+}
+
+static inline void __zs_cpu_down(struct mapping_area *area)
+{
+ if (area->vm_buf)
+ free_page((unsigned long)area->vm_buf);
+ area->vm_buf = NULL;
+}
+
+static void *__zs_map_object(struct mapping_area *area,
+ struct page *pages[2], int off, int size)
{
- struct page *pages[2];
int sizes[2];
void *addr;
+ char *buf = area->vm_buf;
- pages[0] = firstpage;
- pages[1] = get_next_page(firstpage);
- BUG_ON(!pages[1]);
+ /* disable page faults to match kmap_atomic() return conditions */
+ pagefault_disable();
+
+ /* no read fastpath */
+ if (area->vm_mm == ZS_MM_WO)
+ goto out;
sizes[0] = PAGE_SIZE - off;
sizes[1] = size - sizes[0];
@@ -491,18 +713,20 @@ static void zs_copy_map_object(char *buf, struct page *firstpage,
addr = kmap_atomic(pages[1]);
memcpy(buf + sizes[0], addr, sizes[1]);
kunmap_atomic(addr);
+out:
+ return area->vm_buf;
}
-static void zs_copy_unmap_object(char *buf, struct page *firstpage,
- int off, int size)
+static void __zs_unmap_object(struct mapping_area *area,
+ struct page *pages[2], int off, int size)
{
- struct page *pages[2];
int sizes[2];
void *addr;
+ char *buf = area->vm_buf;
- pages[0] = firstpage;
- pages[1] = get_next_page(firstpage);
- BUG_ON(!pages[1]);
+ /* no write fastpath */
+ if (area->vm_mm == ZS_MM_RO)
+ goto out;
sizes[0] = PAGE_SIZE - off;
sizes[1] = size - sizes[0];
@@ -514,34 +738,31 @@ static void zs_copy_unmap_object(char *buf, struct page *firstpage,
addr = kmap_atomic(pages[1]);
memcpy(addr, buf + sizes[0], sizes[1]);
kunmap_atomic(addr);
+
+out:
+ /* enable page faults to match kunmap_atomic() return conditions */
+ pagefault_enable();
}
+#endif /* USE_PGTABLE_MAPPING */
+
static int zs_cpu_notifier(struct notifier_block *nb, unsigned long action,
void *pcpu)
{
- int cpu = (long)pcpu;
+ int ret, cpu = (long)pcpu;
struct mapping_area *area;
switch (action) {
case CPU_UP_PREPARE:
area = &per_cpu(zs_map_area, cpu);
- /*
- * Make sure we don't leak memory if a cpu UP notification
- * and zs_init() race and both call zs_cpu_up() on the same cpu
- */
- if (area->vm_buf)
- return 0;
- area->vm_buf = (char *)__get_free_page(GFP_KERNEL);
- if (!area->vm_buf)
- return -ENOMEM;
- return 0;
+ ret = __zs_cpu_up(area);
+ if (ret)
+ return notifier_from_errno(ret);
break;
case CPU_DEAD:
case CPU_UP_CANCELED:
area = &per_cpu(zs_map_area, cpu);
- if (area->vm_buf)
- free_page((unsigned long)area->vm_buf);
- area->vm_buf = NULL;
+ __zs_cpu_down(area);
break;
}
@@ -758,28 +979,36 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle,
enum fullness_group fg;
struct size_class *class;
struct mapping_area *area;
+ struct page *pages[2];
BUG_ON(!handle);
+ /*
+ * Because we use per-cpu mapping areas shared among the
+ * pools/users, we can't allow mapping in interrupt context
+ * because it can corrupt another users mappings.
+ */
+ BUG_ON(in_interrupt());
+
obj_handle_to_location(handle, &page, &obj_idx);
get_zspage_mapping(get_first_page(page), &class_idx, &fg);
class = &pool->size_class[class_idx];
off = obj_idx_to_offset(page, obj_idx, class->size);
area = &get_cpu_var(zs_map_area);
+ area->vm_mm = mm;
if (off + class->size <= PAGE_SIZE) {
/* this object is contained entirely within a page */
area->vm_addr = kmap_atomic(page);
return area->vm_addr + off;
}
- /* disable page faults to match kmap_atomic() return conditions */
- pagefault_disable();
+ /* this object spans two pages */
+ pages[0] = page;
+ pages[1] = get_next_page(page);
+ BUG_ON(!pages[1]);
- if (mm != ZS_MM_WO)
- zs_copy_map_object(area->vm_buf, page, off, class->size);
- area->vm_addr = NULL;
- return area->vm_buf;
+ return __zs_map_object(area, pages, off, class->size);
}
EXPORT_SYMBOL_GPL(zs_map_object);
@@ -793,17 +1022,6 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
struct size_class *class;
struct mapping_area *area;
- area = &__get_cpu_var(zs_map_area);
- /* single-page object fastpath */
- if (area->vm_addr) {
- kunmap_atomic(area->vm_addr);
- goto out;
- }
-
- /* no write fastpath */
- if (area->vm_mm == ZS_MM_RO)
- goto pfenable;
-
BUG_ON(!handle);
obj_handle_to_location(handle, &page, &obj_idx);
@@ -811,12 +1029,18 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
class = &pool->size_class[class_idx];
off = obj_idx_to_offset(page, obj_idx, class->size);
- zs_copy_unmap_object(area->vm_buf, page, off, class->size);
+ area = &__get_cpu_var(zs_map_area);
+ if (off + class->size <= PAGE_SIZE)
+ kunmap_atomic(area->vm_addr);
+ else {
+ struct page *pages[2];
-pfenable:
- /* enable page faults to match kunmap_atomic() return conditions */
- pagefault_enable();
-out:
+ pages[0] = page;
+ pages[1] = get_next_page(page);
+ BUG_ON(!pages[1]);
+
+ __zs_unmap_object(area, pages, off, class->size);
+ }
put_cpu_var(zs_map_area);
}
EXPORT_SYMBOL_GPL(zs_unmap_object);
diff --git a/drivers/staging/zsmalloc/zsmalloc_int.h b/drivers/staging/zsmalloc/zsmalloc_int.h
deleted file mode 100644
index 528051767733..000000000000
--- a/drivers/staging/zsmalloc/zsmalloc_int.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * zsmalloc memory allocator
- *
- * Copyright (C) 2011 Nitin Gupta
- *
- * This code is released using a dual license strategy: BSD/GPL
- * You can choose the license that better fits your requirements.
- *
- * Released under the terms of 3-clause BSD License
- * Released under the terms of GNU General Public License Version 2.0
- */
-
-#ifndef _ZS_MALLOC_INT_H_
-#define _ZS_MALLOC_INT_H_
-
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/types.h>
-
-/*
- * This must be power of 2 and greater than of equal to sizeof(link_free).
- * These two conditions ensure that any 'struct link_free' itself doesn't
- * span more than 1 page which avoids complex case of mapping 2 pages simply
- * to restore link_free pointer values.
- */
-#define ZS_ALIGN 8
-
-/*
- * A single 'zspage' is composed of up to 2^N discontiguous 0-order (single)
- * pages. ZS_MAX_ZSPAGE_ORDER defines upper limit on N.
- */
-#define ZS_MAX_ZSPAGE_ORDER 2
-#define ZS_MAX_PAGES_PER_ZSPAGE (_AC(1, UL) << ZS_MAX_ZSPAGE_ORDER)
-
-/*
- * Object location (<PFN>, <obj_idx>) is encoded as
- * as single (void *) handle value.
- *
- * Note that object index <obj_idx> is relative to system
- * page <PFN> it is stored in, so for each sub-page belonging
- * to a zspage, obj_idx starts with 0.
- *
- * This is made more complicated by various memory models and PAE.
- */
-
-#ifndef MAX_PHYSMEM_BITS
-#ifdef CONFIG_HIGHMEM64G
-#define MAX_PHYSMEM_BITS 36
-#else /* !CONFIG_HIGHMEM64G */
-/*
- * If this definition of MAX_PHYSMEM_BITS is used, OBJ_INDEX_BITS will just
- * be PAGE_SHIFT
- */
-#define MAX_PHYSMEM_BITS BITS_PER_LONG
-#endif
-#endif
-#define _PFN_BITS (MAX_PHYSMEM_BITS - PAGE_SHIFT)
-#define OBJ_INDEX_BITS (BITS_PER_LONG - _PFN_BITS)
-#define OBJ_INDEX_MASK ((_AC(1, UL) << OBJ_INDEX_BITS) - 1)
-
-#define MAX(a, b) ((a) >= (b) ? (a) : (b))
-/* ZS_MIN_ALLOC_SIZE must be multiple of ZS_ALIGN */
-#define ZS_MIN_ALLOC_SIZE \
- MAX(32, (ZS_MAX_PAGES_PER_ZSPAGE << PAGE_SHIFT >> OBJ_INDEX_BITS))
-#define ZS_MAX_ALLOC_SIZE PAGE_SIZE
-
-/*
- * On systems with 4K page size, this gives 254 size classes! There is a
- * trader-off here:
- * - Large number of size classes is potentially wasteful as free page are
- * spread across these classes
- * - Small number of size classes causes large internal fragmentation
- * - Probably its better to use specific size classes (empirically
- * determined). NOTE: all those class sizes must be set as multiple of
- * ZS_ALIGN to make sure link_free itself never has to span 2 pages.
- *
- * ZS_MIN_ALLOC_SIZE and ZS_SIZE_CLASS_DELTA must be multiple of ZS_ALIGN
- * (reason above)
- */
-#define ZS_SIZE_CLASS_DELTA 16
-#define ZS_SIZE_CLASSES ((ZS_MAX_ALLOC_SIZE - ZS_MIN_ALLOC_SIZE) / \
- ZS_SIZE_CLASS_DELTA + 1)
-
-/*
- * We do not maintain any list for completely empty or full pages
- */
-enum fullness_group {
- ZS_ALMOST_FULL,
- ZS_ALMOST_EMPTY,
- _ZS_NR_FULLNESS_GROUPS,
-
- ZS_EMPTY,
- ZS_FULL
-};
-
-/*
- * We assign a page to ZS_ALMOST_EMPTY fullness group when:
- * n <= N / f, where
- * n = number of allocated objects
- * N = total number of objects zspage can store
- * f = 1/fullness_threshold_frac
- *
- * Similarly, we assign zspage to:
- * ZS_ALMOST_FULL when n > N / f
- * ZS_EMPTY when n == 0
- * ZS_FULL when n == N
- *
- * (see: fix_fullness_group())
- */
-static const int fullness_threshold_frac = 4;
-
-struct mapping_area {
- char *vm_buf; /* copy buffer for objects that span pages */
- char *vm_addr; /* address of kmap_atomic()'ed pages */
- enum zs_mapmode vm_mm; /* mapping mode */
-};
-
-struct size_class {
- /*
- * Size of objects stored in this class. Must be multiple
- * of ZS_ALIGN.
- */
- int size;
- unsigned int index;
-
- /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */
- int pages_per_zspage;
-
- spinlock_t lock;
-
- /* stats */
- u64 pages_allocated;
-
- struct page *fullness_list[_ZS_NR_FULLNESS_GROUPS];
-};
-
-/*
- * Placed within free objects to form a singly linked list.
- * For every zspage, first_page->freelist gives head of this list.
- *
- * This must be power of 2 and less than or equal to ZS_ALIGN
- */
-struct link_free {
- /* Handle of next free chunk (encodes <PFN, obj_idx>) */
- void *next;
-};
-
-struct zs_pool {
- struct size_class size_class[ZS_SIZE_CLASSES];
-
- gfp_t flags; /* allocation flags used when growing pool */
- const char *name;
-};
-
-#endif
diff --git a/drivers/target/iscsi/iscsi_target_parameters.c b/drivers/target/iscsi/iscsi_target_parameters.c
index 0c4760fabfc0..240f7aa76ed1 100644
--- a/drivers/target/iscsi/iscsi_target_parameters.c
+++ b/drivers/target/iscsi/iscsi_target_parameters.c
@@ -662,7 +662,7 @@ int iscsi_extract_key_value(char *textbuf, char **key, char **value)
{
*value = strchr(textbuf, '=');
if (!*value) {
- pr_err("Unable to locate \"=\" seperator for key,"
+ pr_err("Unable to locate \"=\" separator for key,"
" ignoring request.\n");
return -1;
}
@@ -1269,7 +1269,7 @@ static int iscsi_check_value(struct iscsi_param *param, char *value)
comma_ptr = strchr(value, ',');
if (comma_ptr && !IS_TYPE_VALUE_LIST(param)) {
- pr_err("Detected value seperator \",\", but"
+ pr_err("Detected value separator \",\", but"
" key \"%s\" does not allow a value list,"
" protocol error.\n", param->name);
return -1;
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 830cd62d8492..d8e05eeab232 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig
@@ -214,8 +214,8 @@ config CYCLADES
If you haven't heard about it, it's safe to say N.
config CYZ_INTR
- bool "Cyclades-Z interrupt mode operation (EXPERIMENTAL)"
- depends on EXPERIMENTAL && CYCLADES
+ bool "Cyclades-Z interrupt mode operation"
+ depends on CYCLADES
help
The Cyclades-Z family of multiport cards allows 2 (two) driver op
modes: polling and interrupt. In polling mode, the driver will check
@@ -285,7 +285,7 @@ config SYNCLINK_GT
config NOZOMI
tristate "HSDPA Broadband Wireless Data Card - Globe Trotter"
- depends on PCI && EXPERIMENTAL
+ depends on PCI
help
If you have a HSDPA driver Broadband Wireless Data Card -
Globe Trotter PCMCIA card, say Y here.
@@ -294,7 +294,7 @@ config NOZOMI
will be called nozomi.
config ISI
- tristate "Multi-Tech multiport card support (EXPERIMENTAL)"
+ tristate "Multi-Tech multiport card support"
depends on SERIAL_NONSTANDARD && PCI
select FW_LOADER
help
@@ -317,7 +317,6 @@ config N_HDLC
config N_GSM
tristate "GSM MUX line discipline support (EXPERIMENTAL)"
- depends on EXPERIMENTAL
depends on NET
help
This line discipline provides support for the GSM MUX protocol and
diff --git a/drivers/tty/hvc/Kconfig b/drivers/tty/hvc/Kconfig
index 0282a83f51fb..f47b734c6a7a 100644
--- a/drivers/tty/hvc/Kconfig
+++ b/drivers/tty/hvc/Kconfig
@@ -76,7 +76,7 @@ config HVC_XEN_FRONTEND
config HVC_UDBG
bool "udbg based fake hypervisor console"
- depends on PPC && EXPERIMENTAL
+ depends on PPC
select HVC_DRIVER
default n
help
diff --git a/drivers/tty/hvc/hvc_console.c b/drivers/tty/hvc/hvc_console.c
index 7f80f15681cd..4a652999380f 100644
--- a/drivers/tty/hvc/hvc_console.c
+++ b/drivers/tty/hvc/hvc_console.c
@@ -558,7 +558,7 @@ static int hvc_write_room(struct tty_struct *tty)
struct hvc_struct *hp = tty->driver_data;
if (!hp)
- return -1;
+ return 0;
return hp->outbuf_size - hp->n_outbuf;
}
diff --git a/drivers/tty/ipwireless/network.c b/drivers/tty/ipwireless/network.c
index 57c8b481113f..d2af155dec8b 100644
--- a/drivers/tty/ipwireless/network.c
+++ b/drivers/tty/ipwireless/network.c
@@ -274,7 +274,12 @@ static void do_go_online(struct work_struct *work_go_online)
network->xaccm[0] = ~0U;
network->xaccm[3] = 0x60000000U;
network->raccm = ~0U;
- ppp_register_channel(channel);
+ if (ppp_register_channel(channel) < 0) {
+ printk(KERN_ERR IPWIRELESS_PCCARD_NAME
+ ": unable to register PPP channel\n");
+ kfree(channel);
+ return;
+ }
spin_lock_irqsave(&network->lock, flags);
network->ppp_channel = channel;
}
diff --git a/drivers/tty/n_gsm.c b/drivers/tty/n_gsm.c
index 3e210a430fb3..1e8e8ce55959 100644
--- a/drivers/tty/n_gsm.c
+++ b/drivers/tty/n_gsm.c
@@ -488,7 +488,7 @@ static void gsm_print_packet(const char *hdr, int addr, int cr,
default:
if (!(control & 0x01)) {
pr_cont("I N(S)%d N(R)%d",
- (control & 0x0E) >> 1, (control & 0xE) >> 5);
+ (control & 0x0E) >> 1, (control & 0xE0) >> 5);
} else switch (control & 0x0F) {
case RR:
pr_cont("RR(%d)", (control & 0xE0) >> 5);
diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c
index 2bace847eb39..a82b39939a9c 100644
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -628,6 +628,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
index = devpts_new_index(inode);
if (index < 0) {
retval = index;
+ mutex_unlock(&devpts_mutex);
goto err_file;
}
@@ -667,7 +668,6 @@ out:
mutex_unlock(&tty_mutex);
devpts_kill_index(inode, index);
err_file:
- mutex_unlock(&devpts_mutex);
tty_free_file(filp);
return retval;
}
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index d4e0b07cb130..3ba4234592bc 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -290,6 +290,9 @@ static const struct serial8250_config uart_config[] = {
UART_FCR_R_TRIG_00 | UART_FCR_T_TRIG_00,
.flags = UART_CAP_FIFO,
},
+ [PORT_8250_CIR] = {
+ .name = "CIR port"
+ }
};
/* Uart divisor latch read */
@@ -1037,6 +1040,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
unsigned char save_lcr, save_mcr;
struct uart_port *port = &up->port;
unsigned long flags;
+ unsigned int old_capabilities;
if (!port->iobase && !port->mapbase && !port->membase)
return;
@@ -1087,6 +1091,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
/*
* We failed; there's nothing here
*/
+ spin_unlock_irqrestore(&port->lock, flags);
DEBUG_AUTOCONF("IER test failed (%02x, %02x) ",
scratch2, scratch3);
goto out;
@@ -1110,6 +1115,7 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
status1 = serial_in(up, UART_MSR) & 0xF0;
serial_out(up, UART_MCR, save_mcr);
if (status1 != 0x90) {
+ spin_unlock_irqrestore(&port->lock, flags);
DEBUG_AUTOCONF("LOOP test failed (%02x) ",
status1);
goto out;
@@ -1132,8 +1138,6 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
serial_out(up, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(up, UART_IIR) >> 6;
- DEBUG_AUTOCONF("iir=%d ", scratch);
-
switch (scratch) {
case 0:
autoconfig_8250(up);
@@ -1167,19 +1171,13 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
serial_out(up, UART_LCR, save_lcr);
- if (up->capabilities != uart_config[port->type].flags) {
- printk(KERN_WARNING
- "ttyS%d: detected caps %08x should be %08x\n",
- serial_index(port), up->capabilities,
- uart_config[port->type].flags);
- }
-
port->fifosize = uart_config[up->port.type].fifo_size;
+ old_capabilities = up->capabilities;
up->capabilities = uart_config[port->type].flags;
up->tx_loadsz = uart_config[port->type].tx_loadsz;
if (port->type == PORT_UNKNOWN)
- goto out;
+ goto out_lock;
/*
* Reset the UART.
@@ -1196,8 +1194,16 @@ static void autoconfig(struct uart_8250_port *up, unsigned int probeflags)
else
serial_out(up, UART_IER, 0);
- out:
+out_lock:
spin_unlock_irqrestore(&port->lock, flags);
+ if (up->capabilities != old_capabilities) {
+ printk(KERN_WARNING
+ "ttyS%d: detected caps %08x should be %08x\n",
+ serial_index(port), old_capabilities,
+ up->capabilities);
+ }
+out:
+ DEBUG_AUTOCONF("iir=%d ", scratch);
DEBUG_AUTOCONF("type=%s\n", uart_config[port->type].name);
}
@@ -1897,6 +1903,9 @@ static int serial8250_startup(struct uart_port *port)
unsigned char lsr, iir;
int retval;
+ if (port->type == PORT_8250_CIR)
+ return -ENODEV;
+
port->fifosize = uart_config[up->port.type].fifo_size;
up->tx_loadsz = uart_config[up->port.type].tx_loadsz;
up->capabilities = uart_config[up->port.type].flags;
@@ -2554,7 +2563,10 @@ static int serial8250_request_port(struct uart_port *port)
{
struct uart_8250_port *up =
container_of(port, struct uart_8250_port, port);
- int ret = 0;
+ int ret;
+
+ if (port->type == PORT_8250_CIR)
+ return -ENODEV;
ret = serial8250_request_std_resource(up);
if (ret == 0 && port->type == PORT_RSA) {
@@ -2573,6 +2585,9 @@ static void serial8250_config_port(struct uart_port *port, int flags)
int probeflags = PROBE_ANY;
int ret;
+ if (port->type == PORT_8250_CIR)
+ return;
+
/*
* Find the region that we can probe for. This in turn
* tells us whether we can probe for the type of port.
@@ -2672,6 +2687,9 @@ static void __init serial8250_isa_init_ports(void)
return;
first = 0;
+ if (nr_uarts > UART_NR)
+ nr_uarts = UART_NR;
+
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
struct uart_port *port = &up->port;
@@ -2681,6 +2699,7 @@ static void __init serial8250_isa_init_ports(void)
init_timer(&up->timer);
up->timer.function = serial8250_timeout;
+ up->cur_iotype = 0xFF;
/*
* ALPHA_KLUDGE_MCR needs to be killed.
@@ -2732,13 +2751,9 @@ serial8250_register_ports(struct uart_driver *drv, struct device *dev)
for (i = 0; i < nr_uarts; i++) {
struct uart_8250_port *up = &serial8250_ports[i];
- up->cur_iotype = 0xFF;
- }
- serial8250_isa_init_ports();
-
- for (i = 0; i < nr_uarts; i++) {
- struct uart_8250_port *up = &serial8250_ports[i];
+ if (up->port.dev)
+ continue;
up->port.dev = dev;
@@ -2863,9 +2878,6 @@ static struct console serial8250_console = {
static int __init serial8250_console_init(void)
{
- if (nr_uarts > UART_NR)
- nr_uarts = UART_NR;
-
serial8250_isa_init_ports();
register_console(&serial8250_console);
return 0;
@@ -3147,8 +3159,9 @@ int serial8250_register_8250_port(struct uart_8250_port *up)
mutex_lock(&serial_mutex);
uart = serial8250_find_match_or_unused(&up->port);
- if (uart) {
- uart_remove_one_port(&serial8250_reg, &uart->port);
+ if (uart && uart->port.type != PORT_8250_CIR) {
+ if (uart->port.dev)
+ uart_remove_one_port(&serial8250_reg, &uart->port);
uart->port.iobase = up->port.iobase;
uart->port.membase = up->port.membase;
@@ -3232,8 +3245,7 @@ static int __init serial8250_init(void)
{
int ret;
- if (nr_uarts > UART_NR)
- nr_uarts = UART_NR;
+ serial8250_isa_init_ports();
printk(KERN_INFO "Serial: 8250/16550 driver, "
"%d ports, IRQ sharing %sabled\n", nr_uarts,
@@ -3248,11 +3260,15 @@ static int __init serial8250_init(void)
if (ret)
goto out;
+ ret = serial8250_pnp_init();
+ if (ret)
+ goto unreg_uart_drv;
+
serial8250_isa_devs = platform_device_alloc("serial8250",
PLAT8250_DEV_LEGACY);
if (!serial8250_isa_devs) {
ret = -ENOMEM;
- goto unreg_uart_drv;
+ goto unreg_pnp;
}
ret = platform_device_add(serial8250_isa_devs);
@@ -3268,6 +3284,8 @@ static int __init serial8250_init(void)
platform_device_del(serial8250_isa_devs);
put_dev:
platform_device_put(serial8250_isa_devs);
+unreg_pnp:
+ serial8250_pnp_exit();
unreg_uart_drv:
#ifdef CONFIG_SPARC
sunserial_unregister_minors(&serial8250_reg, UART_NR);
@@ -3292,6 +3310,8 @@ static void __exit serial8250_exit(void)
platform_driver_unregister(&serial8250_isa_driver);
platform_device_unregister(isa_dev);
+ serial8250_pnp_exit();
+
#ifdef CONFIG_SPARC
sunserial_unregister_minors(&serial8250_reg, UART_NR);
#else
diff --git a/drivers/tty/serial/8250/8250.h b/drivers/tty/serial/8250/8250.h
index 0c5e908df0b5..5a76f9c8d36b 100644
--- a/drivers/tty/serial/8250/8250.h
+++ b/drivers/tty/serial/8250/8250.h
@@ -97,3 +97,12 @@ static inline void serial_dl_write(struct uart_8250_port *up, int value)
#else
#define ALPHA_KLUDGE_MCR 0
#endif
+
+#ifdef CONFIG_SERIAL_8250_PNP
+int serial8250_pnp_init(void);
+void serial8250_pnp_exit(void);
+#else
+static inline int serial8250_pnp_init(void) { return 0; }
+static inline void serial8250_pnp_exit(void) { }
+#endif
+
diff --git a/drivers/tty/serial/8250/8250_pci.c b/drivers/tty/serial/8250/8250_pci.c
index fdab80a4e063..17b7d26abf41 100644
--- a/drivers/tty/serial/8250/8250_pci.c
+++ b/drivers/tty/serial/8250/8250_pci.c
@@ -1181,6 +1181,8 @@ pci_wch_ch353_setup(struct serial_private *priv,
#define PCI_SUBDEVICE_ID_OCTPRO422 0x0208
#define PCI_SUBDEVICE_ID_POCTAL232 0x0308
#define PCI_SUBDEVICE_ID_POCTAL422 0x0408
+#define PCI_SUBDEVICE_ID_SIIG_DUAL_00 0x2500
+#define PCI_SUBDEVICE_ID_SIIG_DUAL_30 0x2530
#define PCI_VENDOR_ID_ADVANTECH 0x13fe
#define PCI_DEVICE_ID_INTEL_CE4100_UART 0x2e66
#define PCI_DEVICE_ID_ADVANTECH_PCI3620 0x3620
@@ -3286,8 +3288,11 @@ static struct pci_device_id serial_pci_tbl[] = {
* For now just used the hex ID 0x950a.
*/
{ PCI_VENDOR_ID_OXSEMI, 0x950a,
- PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL, 0, 0,
- pbn_b0_2_115200 },
+ PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_00,
+ 0, 0, pbn_b0_2_115200 },
+ { PCI_VENDOR_ID_OXSEMI, 0x950a,
+ PCI_SUBVENDOR_ID_SIIG, PCI_SUBDEVICE_ID_SIIG_DUAL_30,
+ 0, 0, pbn_b0_2_115200 },
{ PCI_VENDOR_ID_OXSEMI, 0x950a,
PCI_ANY_ID, PCI_ANY_ID, 0, 0,
pbn_b0_2_1130000 },
@@ -4309,7 +4314,7 @@ static void serial8250_io_resume(struct pci_dev *dev)
pciserial_resume_ports(priv);
}
-static struct pci_error_handlers serial8250_err_handler = {
+static const struct pci_error_handlers serial8250_err_handler = {
.error_detected = serial8250_io_error_detected,
.slot_reset = serial8250_io_slot_reset,
.resume = serial8250_io_resume,
diff --git a/drivers/tty/serial/8250/8250_pnp.c b/drivers/tty/serial/8250/8250_pnp.c
index fde5aa60d51e..f8ee25001dd0 100644
--- a/drivers/tty/serial/8250/8250_pnp.c
+++ b/drivers/tty/serial/8250/8250_pnp.c
@@ -1,5 +1,5 @@
/*
- * Probe module for 8250/16550-type ISAPNP serial ports.
+ * Probe for 8250/16550-type ISAPNP serial ports.
*
* Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o.
*
@@ -25,7 +25,7 @@
#include "8250.h"
#define UNKNOWN_DEV 0x3000
-
+#define CIR_PORT 0x0800
static const struct pnp_device_id pnp_dev_table[] = {
/* Archtek America Corp. */
@@ -362,6 +362,9 @@ static const struct pnp_device_id pnp_dev_table[] = {
{ "PNPCXXX", UNKNOWN_DEV },
/* More unknown PnP modems */
{ "PNPDXXX", UNKNOWN_DEV },
+ /* Winbond CIR port, should not be probed. We should keep track
+ of it to prevent the legacy serial driver from probing it */
+ { "WEC1022", CIR_PORT },
{ "", 0 }
};
@@ -409,7 +412,7 @@ static int __devinit check_resources(struct pnp_dev *dev)
* PnP modems, alternatively we must hardcode all modems in pnp_devices[]
* table.
*/
-static int __devinit serial_pnp_guess_board(struct pnp_dev *dev, int *flags)
+static int __devinit serial_pnp_guess_board(struct pnp_dev *dev)
{
if (!(check_name(pnp_dev_name(dev)) ||
(dev->card && check_name(dev->card->name))))
@@ -428,7 +431,7 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
int ret, line, flags = dev_id->driver_data;
if (flags & UNKNOWN_DEV) {
- ret = serial_pnp_guess_board(dev, &flags);
+ ret = serial_pnp_guess_board(dev);
if (ret < 0)
return ret;
}
@@ -436,7 +439,10 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
memset(&uart, 0, sizeof(uart));
if (pnp_irq_valid(dev, 0))
uart.port.irq = pnp_irq(dev, 0);
- if (pnp_port_valid(dev, 0)) {
+ if ((flags & CIR_PORT) && pnp_port_valid(dev, 2)) {
+ uart.port.iobase = pnp_port_start(dev, 2);
+ uart.port.iotype = UPIO_PORT;
+ } else if (pnp_port_valid(dev, 0)) {
uart.port.iobase = pnp_port_start(dev, 0);
uart.port.iotype = UPIO_PORT;
} else if (pnp_mem_valid(dev, 0)) {
@@ -451,6 +457,10 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
"Setup PNP port: port %x, mem 0x%lx, irq %d, type %d\n",
uart.port.iobase, uart.port.mapbase, uart.port.irq, uart.port.iotype);
#endif
+ if (flags & CIR_PORT) {
+ uart.port.flags |= UPF_FIXED_PORT | UPF_FIXED_TYPE;
+ uart.port.type = PORT_8250_CIR;
+ }
uart.port.flags |= UPF_SKIP_TEST | UPF_BOOT_AUTOCONF;
if (pnp_irq_flags(dev, 0) & IORESOURCE_IRQ_SHAREABLE)
@@ -459,7 +469,7 @@ serial_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
uart.port.dev = &dev->dev;
line = serial8250_register_8250_port(&uart);
- if (line < 0)
+ if (line < 0 || (flags & CIR_PORT))
return -ENODEV;
pnp_set_drvdata(dev, (void *)((long)line + 1));
@@ -507,18 +517,13 @@ static struct pnp_driver serial_pnp_driver = {
.id_table = pnp_dev_table,
};
-static int __init serial8250_pnp_init(void)
+int serial8250_pnp_init(void)
{
return pnp_register_driver(&serial_pnp_driver);
}
-static void __exit serial8250_pnp_exit(void)
+void serial8250_pnp_exit(void)
{
pnp_unregister_driver(&serial_pnp_driver);
}
-module_init(serial8250_pnp_init);
-module_exit(serial8250_pnp_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("Generic 8250/16x50 PnP serial driver");
diff --git a/drivers/tty/serial/8250/Kconfig b/drivers/tty/serial/8250/Kconfig
index a27dd0569bd7..f3d283f2e3aa 100644
--- a/drivers/tty/serial/8250/Kconfig
+++ b/drivers/tty/serial/8250/Kconfig
@@ -33,6 +33,14 @@ config SERIAL_8250
Most people will say Y or M here, so that they can use serial mice,
modems and similar devices connecting to the standard serial ports.
+config SERIAL_8250_PNP
+ bool "8250/16550 PNP device support" if EXPERT
+ depends on SERIAL_8250 && PNP
+ default y
+ ---help---
+ This builds standard PNP serial support. You may be able to
+ disable this feature if you only need legacy serial support.
+
config SERIAL_8250_CONSOLE
bool "Console on 8250/16550 and compatible serial port"
depends on SERIAL_8250=y
@@ -85,14 +93,6 @@ config SERIAL_8250_PCI
disable this feature if you only need legacy serial support.
Saves about 9K.
-config SERIAL_8250_PNP
- tristate "8250/16550 PNP device support" if EXPERT
- depends on SERIAL_8250 && PNP
- default SERIAL_8250
- help
- This builds standard PNP serial support. You may be able to
- disable this feature if you only need legacy serial support.
-
config SERIAL_8250_HP300
tristate
depends on SERIAL_8250 && HP300
diff --git a/drivers/tty/serial/8250/Makefile b/drivers/tty/serial/8250/Makefile
index d7533c7d2c1a..108fe7fe13e2 100644
--- a/drivers/tty/serial/8250/Makefile
+++ b/drivers/tty/serial/8250/Makefile
@@ -2,8 +2,9 @@
# Makefile for the 8250 serial device drivers.
#
-obj-$(CONFIG_SERIAL_8250) += 8250.o
-obj-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
+obj-$(CONFIG_SERIAL_8250) += 8250_core.o
+8250_core-y := 8250.o
+8250_core-$(CONFIG_SERIAL_8250_PNP) += 8250_pnp.o
obj-$(CONFIG_SERIAL_8250_GSC) += 8250_gsc.o
obj-$(CONFIG_SERIAL_8250_PCI) += 8250_pci.o
obj-$(CONFIG_SERIAL_8250_HP300) += 8250_hp300.o
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 26907cf25744..233fbaaf2559 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -141,6 +141,25 @@ config SERIAL_ATMEL_TTYAT
Say Y if you have an external 8250/16C550 UART. If unsure, say N.
+config SERIAL_KGDB_NMI
+ bool "Serial console over KGDB NMI debugger port"
+ depends on KGDB_SERIAL_CONSOLE
+ help
+ This special driver allows you to temporary use NMI debugger port
+ as a normal console (assuming that the port is attached to KGDB).
+
+ Unlike KDB's disable_nmi command, with this driver you are always
+ able to go back to the debugger using KGDB escape sequence ($3#33).
+ This is because this console driver processes the input in NMI
+ context, and thus is able to intercept the magic sequence.
+
+ Note that since the console interprets input and uses polling
+ communication methods, for things like PPP you still must fully
+ detach debugger port from the KGDB NMI (i.e. disable_nmi), and
+ use raw console.
+
+ If unsure, say N.
+
config SERIAL_KS8695
bool "Micrel KS8695 (Centaur) serial port support"
depends on ARCH_KS8695
@@ -693,7 +712,7 @@ config SERIAL_SH_SCI_CONSOLE
config SERIAL_SH_SCI_DMA
bool "DMA support"
- depends on SERIAL_SH_SCI && SH_DMAE && EXPERIMENTAL
+ depends on SERIAL_SH_SCI && SH_DMAE
config SERIAL_PNX8XXX
bool "Enable PNX8XXX SoCs' UART Support"
@@ -1304,7 +1323,7 @@ config SERIAL_ALTERA_UART_CONSOLE
config SERIAL_IFX6X60
tristate "SPI protocol driver for Infineon 6x60 modem (EXPERIMENTAL)"
- depends on GPIOLIB && SPI && EXPERIMENTAL
+ depends on GPIOLIB && SPI
help
Support for the IFX6x60 modem devices on Intel MID platforms.
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index ce88667cfd17..4f694dafa719 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_SERIAL_MSM_HS) += msm_serial_hs.o
obj-$(CONFIG_SERIAL_NETX) += netx-serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o
obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o
+obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o
obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o
obj-$(CONFIG_SERIAL_OMAP) += omap-serial.o
obj-$(CONFIG_SERIAL_ALTERA_UART) += altera_uart.o
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index cede93876649..d7e1edec50b5 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -1284,11 +1284,40 @@ static void pl011_break_ctl(struct uart_port *port, int break_state)
}
#ifdef CONFIG_CONSOLE_POLL
+
+static void pl011_quiesce_irqs(struct uart_port *port)
+{
+ struct uart_amba_port *uap = (struct uart_amba_port *)port;
+ unsigned char __iomem *regs = uap->port.membase;
+
+ writew(readw(regs + UART011_MIS), regs + UART011_ICR);
+ /*
+ * There is no way to clear TXIM as this is "ready to transmit IRQ", so
+ * we simply mask it. start_tx() will unmask it.
+ *
+ * Note we can race with start_tx(), and if the race happens, the
+ * polling user might get another interrupt just after we clear it.
+ * But it should be OK and can happen even w/o the race, e.g.
+ * controller immediately got some new data and raised the IRQ.
+ *
+ * And whoever uses polling routines assumes that it manages the device
+ * (including tx queue), so we're also fine with start_tx()'s caller
+ * side.
+ */
+ writew(readw(regs + UART011_IMSC) & ~UART011_TXIM, regs + UART011_IMSC);
+}
+
static int pl011_get_poll_char(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
unsigned int status;
+ /*
+ * The caller might need IRQs lowered, e.g. if used with KDB NMI
+ * debugger.
+ */
+ pl011_quiesce_irqs(port);
+
status = readw(uap->port.membase + UART01x_FR);
if (status & UART01x_FR_RXFE)
return NO_POLL_CHAR;
@@ -1309,10 +1338,9 @@ static void pl011_put_poll_char(struct uart_port *port,
#endif /* CONFIG_CONSOLE_POLL */
-static int pl011_startup(struct uart_port *port)
+static int pl011_hwinit(struct uart_port *port)
{
struct uart_amba_port *uap = (struct uart_amba_port *)port;
- unsigned int cr;
int retval;
/* Optionaly enable pins to be muxed in and configured */
@@ -1337,6 +1365,37 @@ static int pl011_startup(struct uart_port *port)
UART011_RTIS | UART011_RXIS, uap->port.membase + UART011_ICR);
/*
+ * Save interrupts enable mask, and enable RX interrupts in case if
+ * the interrupt is used for NMI entry.
+ */
+ uap->im = readw(uap->port.membase + UART011_IMSC);
+ writew(UART011_RTIM | UART011_RXIM, uap->port.membase + UART011_IMSC);
+
+ if (uap->port.dev->platform_data) {
+ struct amba_pl011_data *plat;
+
+ plat = uap->port.dev->platform_data;
+ if (plat->init)
+ plat->init();
+ }
+ return 0;
+ out:
+ return retval;
+}
+
+static int pl011_startup(struct uart_port *port)
+{
+ struct uart_amba_port *uap = (struct uart_amba_port *)port;
+ unsigned int cr;
+ int retval;
+
+ retval = pl011_hwinit(port);
+ if (retval)
+ goto clk_dis;
+
+ writew(uap->im, uap->port.membase + UART011_IMSC);
+
+ /*
* Allocate the IRQ
*/
retval = request_irq(uap->port.irq, pl011_int, 0, "uart-pl011", uap);
@@ -1395,19 +1454,10 @@ static int pl011_startup(struct uart_port *port)
writew(uap->im, uap->port.membase + UART011_IMSC);
spin_unlock_irq(&uap->port.lock);
- if (uap->port.dev->platform_data) {
- struct amba_pl011_data *plat;
-
- plat = uap->port.dev->platform_data;
- if (plat->init)
- plat->init();
- }
-
return 0;
clk_dis:
clk_disable_unprepare(uap->clk);
- out:
return retval;
}
@@ -1595,13 +1645,26 @@ pl011_set_termios(struct uart_port *port, struct ktermios *termios,
old_cr &= ~ST_UART011_CR_OVSFACT;
}
+ /*
+ * Workaround for the ST Micro oversampling variants to
+ * increase the bitrate slightly, by lowering the divisor,
+ * to avoid delayed sampling of start bit at high speeds,
+ * else we see data corruption.
+ */
+ if (uap->vendor->oversampling) {
+ if ((baud >= 3000000) && (baud < 3250000) && (quot > 1))
+ quot -= 1;
+ else if ((baud > 3250000) && (quot > 2))
+ quot -= 2;
+ }
/* Set baud rate */
writew(quot & 0x3f, port->membase + UART011_FBRD);
writew(quot >> 6, port->membase + UART011_IBRD);
/*
* ----------v----------v----------v----------v-----
- * NOTE: MUST BE WRITTEN AFTER UARTLCR_M & UARTLCR_L
+ * NOTE: lcrh_tx and lcrh_rx MUST BE WRITTEN AFTER
+ * UART011_FBRD & UART011_IBRD.
* ----------^----------^----------^----------^-----
*/
writew(lcr_h, port->membase + uap->lcrh_rx);
@@ -1688,6 +1751,7 @@ static struct uart_ops amba_pl011_pops = {
.config_port = pl011_config_port,
.verify_port = pl011_verify_port,
#ifdef CONFIG_CONSOLE_POLL
+ .poll_init = pl011_hwinit,
.poll_get_char = pl011_get_poll_char,
.poll_put_char = pl011_put_poll_char,
#endif
diff --git a/drivers/tty/serial/cpm_uart/cpm_uart_core.c b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
index b418947b7107..d0dd9194cecc 100644
--- a/drivers/tty/serial/cpm_uart/cpm_uart_core.c
+++ b/drivers/tty/serial/cpm_uart/cpm_uart_core.c
@@ -71,7 +71,7 @@ static void cpm_uart_initbd(struct uart_cpm_port *pinfo);
/**************************************************************/
-#define HW_BUF_SPD_THRESHOLD 9600
+#define HW_BUF_SPD_THRESHOLD 2400
/*
* Check, if transmit buffers are processed
@@ -417,6 +417,7 @@ static int cpm_uart_startup(struct uart_port *port)
clrbits32(&pinfo->sccp->scc_gsmrl, SCC_GSMRL_ENR);
clrbits16(&pinfo->sccp->scc_sccm, UART_SCCM_RX);
}
+ cpm_uart_initbd(pinfo);
cpm_line_cr_cmd(pinfo, CPM_CR_INIT_TRX);
}
/* Install interrupt handler. */
@@ -500,16 +501,28 @@ static void cpm_uart_set_termios(struct uart_port *port,
struct uart_cpm_port *pinfo = (struct uart_cpm_port *)port;
smc_t __iomem *smcp = pinfo->smcp;
scc_t __iomem *sccp = pinfo->sccp;
+ int maxidl;
pr_debug("CPM uart[%d]:set_termios\n", port->line);
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
- if (baud <= HW_BUF_SPD_THRESHOLD ||
+ if (baud < HW_BUF_SPD_THRESHOLD ||
(pinfo->port.state && pinfo->port.state->port.tty->low_latency))
pinfo->rx_fifosize = 1;
else
pinfo->rx_fifosize = RX_BUF_SIZE;
+ /* MAXIDL is the timeout after which a receive buffer is closed
+ * when not full if no more characters are received.
+ * We calculate it from the baudrate so that the duration is
+ * always the same at standard rates: about 4ms.
+ */
+ maxidl = baud / 2400;
+ if (maxidl < 1)
+ maxidl = 1;
+ if (maxidl > 0x10)
+ maxidl = 0x10;
+
/* Character length programmed into the mode register is the
* sum of: 1 start bit, number of data bits, 0 or 1 parity bit,
* 1 or 2 stop bits, minus 1.
@@ -610,6 +623,7 @@ static void cpm_uart_set_termios(struct uart_port *port,
* SMC/SCC receiver is disabled.
*/
out_be16(&pinfo->smcup->smc_mrblr, pinfo->rx_fifosize);
+ out_be16(&pinfo->smcup->smc_maxidl, maxidl);
/* Set the mode register. We want to keep a copy of the
* enables, because we want to put them back if they were
@@ -622,6 +636,7 @@ static void cpm_uart_set_termios(struct uart_port *port,
SMCMR_SM_UART | prev_mode);
} else {
out_be16(&pinfo->sccup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
+ out_be16(&pinfo->sccup->scc_maxidl, maxidl);
out_be16(&sccp->scc_psmr, (sbits << 12) | scval);
}
@@ -798,7 +813,7 @@ static void cpm_uart_init_scc(struct uart_cpm_port *pinfo)
cpm_set_scc_fcr(sup);
out_be16(&sup->scc_genscc.scc_mrblr, pinfo->rx_fifosize);
- out_be16(&sup->scc_maxidl, pinfo->rx_fifosize);
+ out_be16(&sup->scc_maxidl, 0x10);
out_be16(&sup->scc_brkcr, 1);
out_be16(&sup->scc_parec, 0);
out_be16(&sup->scc_frmec, 0);
@@ -872,7 +887,7 @@ static void cpm_uart_init_smc(struct uart_cpm_port *pinfo)
/* Using idle character time requires some additional tuning. */
out_be16(&up->smc_mrblr, pinfo->rx_fifosize);
- out_be16(&up->smc_maxidl, pinfo->rx_fifosize);
+ out_be16(&up->smc_maxidl, 0x10);
out_be16(&up->smc_brklen, 0);
out_be16(&up->smc_brkec, 0);
out_be16(&up->smc_brkcr, 1);
diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c
index 2a093a42512f..59819121fe9b 100644
--- a/drivers/tty/serial/imx.c
+++ b/drivers/tty/serial/imx.c
@@ -1373,8 +1373,7 @@ static int serial_imx_suspend(struct platform_device *dev, pm_message_t state)
val |= UCR3_AWAKEN;
writel(val, sport->port.membase + UCR3);
- if (sport)
- uart_suspend_port(&imx_reg, &sport->port);
+ uart_suspend_port(&imx_reg, &sport->port);
return 0;
}
@@ -1389,8 +1388,7 @@ static int serial_imx_resume(struct platform_device *dev)
val &= ~UCR3_AWAKEN;
writel(val, sport->port.membase + UCR3);
- if (sport)
- uart_resume_port(&imx_reg, &sport->port);
+ uart_resume_port(&imx_reg, &sport->port);
return 0;
}
@@ -1540,7 +1538,7 @@ static int serial_imx_probe(struct platform_device *pdev)
ret = uart_add_one_port(&imx_reg, &sport->port);
if (ret)
goto deinit;
- platform_set_drvdata(pdev, &sport->port);
+ platform_set_drvdata(pdev, sport);
return 0;
deinit:
diff --git a/drivers/tty/serial/jsm/jsm_driver.c b/drivers/tty/serial/jsm/jsm_driver.c
index 7545fe1b9925..5ab3c3b595e4 100644
--- a/drivers/tty/serial/jsm/jsm_driver.c
+++ b/drivers/tty/serial/jsm/jsm_driver.c
@@ -54,7 +54,7 @@ static pci_ers_result_t jsm_io_error_detected(struct pci_dev *pdev,
static pci_ers_result_t jsm_io_slot_reset(struct pci_dev *pdev);
static void jsm_io_resume(struct pci_dev *pdev);
-static struct pci_error_handlers jsm_err_handler = {
+static const struct pci_error_handlers jsm_err_handler = {
.error_detected = jsm_io_error_detected,
.slot_reset = jsm_io_slot_reset,
.resume = jsm_io_resume,
diff --git a/drivers/tty/serial/kgdb_nmi.c b/drivers/tty/serial/kgdb_nmi.c
new file mode 100644
index 000000000000..d185247ba1aa
--- /dev/null
+++ b/drivers/tty/serial/kgdb_nmi.c
@@ -0,0 +1,402 @@
+/*
+ * KGDB NMI serial console
+ *
+ * Copyright 2010 Google, Inc.
+ * Arve Hjønnevåg <arve@android.com>
+ * Colin Cross <ccross@android.com>
+ * Copyright 2012 Linaro Ltd.
+ * Anton Vorontsov <anton.vorontsov@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/kernel.h>
+#include <linux/module.h>
+#include <linux/compiler.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/atomic.h>
+#include <linux/console.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/tick.h>
+#include <linux/kfifo.h>
+#include <linux/kgdb.h>
+#include <linux/kdb.h>
+
+static int kgdb_nmi_knock = 1;
+module_param_named(knock, kgdb_nmi_knock, int, 0600);
+MODULE_PARM_DESC(knock, "if set to 1 (default), the special '$3#33' command " \
+ "must be used to enter the debugger; when set to 0, " \
+ "hitting return key is enough to enter the debugger; " \
+ "when set to -1, the debugger is entered immediately " \
+ "upon NMI");
+
+static char *kgdb_nmi_magic = "$3#33";
+module_param_named(magic, kgdb_nmi_magic, charp, 0600);
+MODULE_PARM_DESC(magic, "magic sequence to enter NMI debugger (default $3#33)");
+
+static bool kgdb_nmi_tty_enabled;
+
+static void kgdb_nmi_console_write(struct console *co, const char *s, uint c)
+{
+ int i;
+
+ if (!kgdb_nmi_tty_enabled || atomic_read(&kgdb_active) >= 0)
+ return;
+
+ for (i = 0; i < c; i++)
+ dbg_io_ops->write_char(s[i]);
+}
+
+static struct tty_driver *kgdb_nmi_tty_driver;
+
+static struct tty_driver *kgdb_nmi_console_device(struct console *co, int *idx)
+{
+ *idx = co->index;
+ return kgdb_nmi_tty_driver;
+}
+
+static struct console kgdb_nmi_console = {
+ .name = "ttyNMI",
+ .write = kgdb_nmi_console_write,
+ .device = kgdb_nmi_console_device,
+ .flags = CON_PRINTBUFFER | CON_ANYTIME | CON_ENABLED,
+ .index = -1,
+};
+
+/*
+ * This is usually the maximum rate on debug ports. We make fifo large enough
+ * to make copy-pasting to the terminal usable.
+ */
+#define KGDB_NMI_BAUD 115200
+#define KGDB_NMI_FIFO_SIZE roundup_pow_of_two(KGDB_NMI_BAUD / 8 / HZ)
+
+struct kgdb_nmi_tty_priv {
+ struct tty_port port;
+ struct tasklet_struct tlet;
+ STRUCT_KFIFO(char, KGDB_NMI_FIFO_SIZE) fifo;
+};
+
+static struct kgdb_nmi_tty_priv *kgdb_nmi_port_to_priv(struct tty_port *port)
+{
+ return container_of(port, struct kgdb_nmi_tty_priv, port);
+}
+
+/*
+ * Our debugging console is polled in a tasklet, so we'll check for input
+ * every tick. In HZ-less mode, we should program the next tick. We have
+ * to use the lowlevel stuff as no locks should be grabbed.
+ */
+#ifdef CONFIG_HIGH_RES_TIMERS
+static void kgdb_tty_poke(void)
+{
+ tick_program_event(ktime_get(), 0);
+}
+#else
+static inline void kgdb_tty_poke(void) {}
+#endif
+
+static struct tty_port *kgdb_nmi_port;
+
+static void kgdb_tty_recv(int ch)
+{
+ struct kgdb_nmi_tty_priv *priv;
+ char c = ch;
+
+ if (!kgdb_nmi_port || ch < 0)
+ return;
+ /*
+ * Can't use port->tty->driver_data as tty might be not there. Tasklet
+ * will check for tty and will get the ref, but here we don't have to
+ * do that, and actually, we can't: we're in NMI context, no locks are
+ * possible.
+ */
+ priv = kgdb_nmi_port_to_priv(kgdb_nmi_port);
+ kfifo_in(&priv->fifo, &c, 1);
+ kgdb_tty_poke();
+}
+
+static int kgdb_nmi_poll_one_knock(void)
+{
+ static int n;
+ int c = -1;
+ const char *magic = kgdb_nmi_magic;
+ size_t m = strlen(magic);
+ bool printch = 0;
+
+ c = dbg_io_ops->read_char();
+ if (c == NO_POLL_CHAR)
+ return c;
+
+ if (!kgdb_nmi_knock && (c == '\r' || c == '\n')) {
+ return 1;
+ } else if (c == magic[n]) {
+ n = (n + 1) % m;
+ if (!n)
+ return 1;
+ printch = 1;
+ } else {
+ n = 0;
+ }
+
+ if (kgdb_nmi_tty_enabled) {
+ kgdb_tty_recv(c);
+ return 0;
+ }
+
+ if (printch) {
+ kdb_printf("%c", c);
+ return 0;
+ }
+
+ kdb_printf("\r%s %s to enter the debugger> %*s",
+ kgdb_nmi_knock ? "Type" : "Hit",
+ kgdb_nmi_knock ? magic : "<return>", (int)m, "");
+ while (m--)
+ kdb_printf("\b");
+ return 0;
+}
+
+/**
+ * kgdb_nmi_poll_knock - Check if it is time to enter the debugger
+ *
+ * "Serial ports are often noisy, especially when muxed over another port (we
+ * often use serial over the headset connector). Noise on the async command
+ * line just causes characters that are ignored, on a command line that blocked
+ * execution noise would be catastrophic." -- Colin Cross
+ *
+ * So, this function implements KGDB/KDB knocking on the serial line: we won't
+ * enter the debugger until we receive a known magic phrase (which is actually
+ * "$3#33", known as "escape to KDB" command. There is also a relaxed variant
+ * of knocking, i.e. just pressing the return key is enough to enter the
+ * debugger. And if knocking is disabled, the function always returns 1.
+ */
+bool kgdb_nmi_poll_knock(void)
+{
+ if (kgdb_nmi_knock < 0)
+ return 1;
+
+ while (1) {
+ int ret;
+
+ ret = kgdb_nmi_poll_one_knock();
+ if (ret == NO_POLL_CHAR)
+ return 0;
+ else if (ret == 1)
+ break;
+ }
+ return 1;
+}
+
+/*
+ * The tasklet is cheap, it does not cause wakeups when reschedules itself,
+ * instead it waits for the next tick.
+ */
+static void kgdb_nmi_tty_receiver(unsigned long data)
+{
+ struct kgdb_nmi_tty_priv *priv = (void *)data;
+ struct tty_struct *tty;
+ char ch;
+
+ tasklet_schedule(&priv->tlet);
+
+ if (likely(!kgdb_nmi_tty_enabled || !kfifo_len(&priv->fifo)))
+ return;
+
+ /* Port is there, but tty might be hung up, check. */
+ tty = tty_port_tty_get(kgdb_nmi_port);
+ if (!tty)
+ return;
+
+ while (kfifo_out(&priv->fifo, &ch, 1))
+ tty_insert_flip_char(priv->port.tty, ch, TTY_NORMAL);
+ tty_flip_buffer_push(priv->port.tty);
+
+ tty_kref_put(tty);
+}
+
+static int kgdb_nmi_tty_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ kgdb_nmi_port = port;
+ tasklet_schedule(&priv->tlet);
+ return 0;
+}
+
+static void kgdb_nmi_tty_shutdown(struct tty_port *port)
+{
+ struct kgdb_nmi_tty_priv *priv = port->tty->driver_data;
+
+ tasklet_kill(&priv->tlet);
+ kgdb_nmi_port = NULL;
+}
+
+static const struct tty_port_operations kgdb_nmi_tty_port_ops = {
+ .activate = kgdb_nmi_tty_activate,
+ .shutdown = kgdb_nmi_tty_shutdown,
+};
+
+static int kgdb_nmi_tty_install(struct tty_driver *drv, struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv;
+ int ret;
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ INIT_KFIFO(priv->fifo);
+ tasklet_init(&priv->tlet, kgdb_nmi_tty_receiver, (unsigned long)priv);
+ tty_port_init(&priv->port);
+ priv->port.ops = &kgdb_nmi_tty_port_ops;
+ tty->driver_data = priv;
+
+ ret = tty_port_install(&priv->port, drv, tty);
+ if (ret) {
+ pr_err("%s: can't install tty port: %d\n", __func__, ret);
+ goto err;
+ }
+ return 0;
+err:
+ kfree(priv);
+ return ret;
+}
+
+static void kgdb_nmi_tty_cleanup(struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ tty->driver_data = NULL;
+ kfree(priv);
+}
+
+static int kgdb_nmi_tty_open(struct tty_struct *tty, struct file *file)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ return tty_port_open(&priv->port, tty, file);
+}
+
+static void kgdb_nmi_tty_close(struct tty_struct *tty, struct file *file)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ tty_port_close(&priv->port, tty, file);
+}
+
+static void kgdb_nmi_tty_hangup(struct tty_struct *tty)
+{
+ struct kgdb_nmi_tty_priv *priv = tty->driver_data;
+
+ tty_port_hangup(&priv->port);
+}
+
+static int kgdb_nmi_tty_write_room(struct tty_struct *tty)
+{
+ /* Actually, we can handle any amount as we use polled writes. */
+ return 2048;
+}
+
+static int kgdb_nmi_tty_write(struct tty_struct *tty, const unchar *buf, int c)
+{
+ int i;
+
+ for (i = 0; i < c; i++)
+ dbg_io_ops->write_char(buf[i]);
+ return c;
+}
+
+static const struct tty_operations kgdb_nmi_tty_ops = {
+ .open = kgdb_nmi_tty_open,
+ .close = kgdb_nmi_tty_close,
+ .install = kgdb_nmi_tty_install,
+ .cleanup = kgdb_nmi_tty_cleanup,
+ .hangup = kgdb_nmi_tty_hangup,
+ .write_room = kgdb_nmi_tty_write_room,
+ .write = kgdb_nmi_tty_write,
+};
+
+static int kgdb_nmi_enable_console(int argc, const char *argv[])
+{
+ kgdb_nmi_tty_enabled = !(argc == 1 && !strcmp(argv[1], "off"));
+ return 0;
+}
+
+int kgdb_register_nmi_console(void)
+{
+ int ret;
+
+ if (!arch_kgdb_ops.enable_nmi)
+ return 0;
+
+ kgdb_nmi_tty_driver = alloc_tty_driver(1);
+ if (!kgdb_nmi_tty_driver) {
+ pr_err("%s: cannot allocate tty\n", __func__);
+ return -ENOMEM;
+ }
+ kgdb_nmi_tty_driver->driver_name = "ttyNMI";
+ kgdb_nmi_tty_driver->name = "ttyNMI";
+ kgdb_nmi_tty_driver->num = 1;
+ kgdb_nmi_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ kgdb_nmi_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ kgdb_nmi_tty_driver->flags = TTY_DRIVER_REAL_RAW;
+ kgdb_nmi_tty_driver->init_termios = tty_std_termios;
+ tty_termios_encode_baud_rate(&kgdb_nmi_tty_driver->init_termios,
+ KGDB_NMI_BAUD, KGDB_NMI_BAUD);
+ tty_set_operations(kgdb_nmi_tty_driver, &kgdb_nmi_tty_ops);
+
+ ret = tty_register_driver(kgdb_nmi_tty_driver);
+ if (ret) {
+ pr_err("%s: can't register tty driver: %d\n", __func__, ret);
+ goto err_drv_reg;
+ }
+
+ ret = kdb_register("nmi_console", kgdb_nmi_enable_console, "[off]",
+ "switch to Linux NMI console", 0);
+ if (ret) {
+ pr_err("%s: can't register kdb command: %d\n", __func__, ret);
+ goto err_kdb_reg;
+ }
+
+ register_console(&kgdb_nmi_console);
+ arch_kgdb_ops.enable_nmi(1);
+
+ return 0;
+err_kdb_reg:
+ tty_unregister_driver(kgdb_nmi_tty_driver);
+err_drv_reg:
+ put_tty_driver(kgdb_nmi_tty_driver);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(kgdb_register_nmi_console);
+
+int kgdb_unregister_nmi_console(void)
+{
+ int ret;
+
+ if (!arch_kgdb_ops.enable_nmi)
+ return 0;
+ arch_kgdb_ops.enable_nmi(0);
+
+ kdb_unregister("nmi_console");
+
+ ret = unregister_console(&kgdb_nmi_console);
+ if (ret)
+ return ret;
+
+ ret = tty_unregister_driver(kgdb_nmi_tty_driver);
+ if (ret)
+ return ret;
+ put_tty_driver(kgdb_nmi_tty_driver);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(kgdb_unregister_nmi_console);
diff --git a/drivers/tty/serial/kgdboc.c b/drivers/tty/serial/kgdboc.c
index 2b42a01a81c6..3f63d834cbc9 100644
--- a/drivers/tty/serial/kgdboc.c
+++ b/drivers/tty/serial/kgdboc.c
@@ -145,6 +145,8 @@ __setup("kgdboc=", kgdboc_option_setup);
static void cleanup_kgdboc(void)
{
+ if (kgdb_unregister_nmi_console())
+ return;
kgdboc_unregister_kbd();
if (configured == 1)
kgdb_unregister_io_module(&kgdboc_io_ops);
@@ -198,11 +200,18 @@ do_register:
if (err)
goto noconfig;
+ err = kgdb_register_nmi_console();
+ if (err)
+ goto nmi_con_failed;
+
configured = 1;
return 0;
+nmi_con_failed:
+ kgdb_unregister_io_module(&kgdboc_io_ops);
noconfig:
+ kgdboc_unregister_kbd();
config[0] = 0;
configured = 0;
cleanup_kgdboc();
diff --git a/drivers/tty/serial/max3100.c b/drivers/tty/serial/max3100.c
index 46043c2521ce..0f24486be532 100644
--- a/drivers/tty/serial/max3100.c
+++ b/drivers/tty/serial/max3100.c
@@ -827,14 +827,16 @@ static int __devexit max3100_remove(struct spi_device *spi)
/* find out the index for the chip we are removing */
for (i = 0; i < MAX_MAX3100; i++)
- if (max3100s[i] == s)
+ if (max3100s[i] == s) {
+ dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i);
+ uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port);
+ kfree(max3100s[i]);
+ max3100s[i] = NULL;
break;
+ }
- dev_dbg(&spi->dev, "%s: removing port %d\n", __func__, i);
- uart_remove_one_port(&max3100_uart_driver, &max3100s[i]->port);
- kfree(max3100s[i]);
- max3100s[i] = NULL;
-
+ WARN_ON(i == MAX_MAX3100);
+
/* check if this is the last chip we have */
for (i = 0; i < MAX_MAX3100; i++)
if (max3100s[i]) {
diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c
index f19d04ed8586..8cf577008ad7 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -598,7 +598,7 @@ static struct psc_ops mpc512x_psc_ops = {
};
#endif
-static struct psc_ops *psc_ops;
+static const struct psc_ops *psc_ops;
/* ======================================================================== */
/* UART operations */
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 68984136bfb1..6db3baa39a97 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -781,6 +781,7 @@ out_free_irq:
auart_port[pdev->id] = NULL;
free_irq(s->irq, s);
out_free_clk:
+ put_device(s->dev);
clk_put(s->clk);
out_free:
kfree(s);
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index f175385bb304..ccc2f35adff1 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -334,6 +334,10 @@ static unsigned int check_modem_status(struct uart_omap_port *up)
static void serial_omap_rlsi(struct uart_omap_port *up, unsigned int lsr)
{
unsigned int flag;
+ unsigned char ch = 0;
+
+ if (likely(lsr & UART_LSR_DR))
+ ch = serial_in(up, UART_RX);
up->port.icount.rx++;
flag = TTY_NORMAL;
@@ -1222,10 +1226,8 @@ static int serial_omap_suspend(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
- if (up) {
- uart_suspend_port(&serial_omap_reg, &up->port);
- flush_work_sync(&up->qos_work);
- }
+ uart_suspend_port(&serial_omap_reg, &up->port);
+ flush_work_sync(&up->qos_work);
return 0;
}
@@ -1234,8 +1236,8 @@ static int serial_omap_resume(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
- if (up)
- uart_resume_port(&serial_omap_reg, &up->port);
+ uart_resume_port(&serial_omap_reg, &up->port);
+
return 0;
}
#endif
@@ -1553,17 +1555,14 @@ static int serial_omap_runtime_suspend(struct device *dev)
static int serial_omap_runtime_resume(struct device *dev)
{
struct uart_omap_port *up = dev_get_drvdata(dev);
- struct omap_uart_port_info *pdata = dev->platform_data;
- if (up && pdata) {
- u32 loss_cnt = serial_omap_get_context_loss_count(up);
+ u32 loss_cnt = serial_omap_get_context_loss_count(up);
- if (up->context_loss_cnt != loss_cnt)
- serial_omap_restore_context(up);
+ if (up->context_loss_cnt != loss_cnt)
+ serial_omap_restore_context(up);
- up->latency = up->calc_latency;
- schedule_work(&up->qos_work);
- }
+ up->latency = up->calc_latency;
+ schedule_work(&up->qos_work);
return 0;
}
diff --git a/drivers/tty/serial/samsung.c b/drivers/tty/serial/samsung.c
index bdaa06f3ab69..7f04717176aa 100644
--- a/drivers/tty/serial/samsung.c
+++ b/drivers/tty/serial/samsung.c
@@ -876,11 +876,24 @@ s3c24xx_serial_verify_port(struct uart_port *port, struct serial_struct *ser)
static struct console s3c24xx_serial_console;
+static int __init s3c24xx_serial_console_init(void)
+{
+ register_console(&s3c24xx_serial_console);
+ return 0;
+}
+console_initcall(s3c24xx_serial_console_init);
+
#define S3C24XX_SERIAL_CONSOLE &s3c24xx_serial_console
#else
#define S3C24XX_SERIAL_CONSOLE NULL
#endif
+#ifdef CONFIG_CONSOLE_POLL
+static int s3c24xx_serial_get_poll_char(struct uart_port *port);
+static void s3c24xx_serial_put_poll_char(struct uart_port *port,
+ unsigned char c);
+#endif
+
static struct uart_ops s3c24xx_serial_ops = {
.pm = s3c24xx_serial_pm,
.tx_empty = s3c24xx_serial_tx_empty,
@@ -899,6 +912,10 @@ static struct uart_ops s3c24xx_serial_ops = {
.request_port = s3c24xx_serial_request_port,
.config_port = s3c24xx_serial_config_port,
.verify_port = s3c24xx_serial_verify_port,
+#ifdef CONFIG_CONSOLE_POLL
+ .poll_get_char = s3c24xx_serial_get_poll_char,
+ .poll_put_char = s3c24xx_serial_put_poll_char,
+#endif
};
static struct uart_driver s3c24xx_uart_drv = {
@@ -1316,6 +1333,36 @@ s3c24xx_serial_console_txrdy(struct uart_port *port, unsigned int ufcon)
return (utrstat & S3C2410_UTRSTAT_TXE) ? 1 : 0;
}
+#ifdef CONFIG_CONSOLE_POLL
+/*
+ * Console polling routines for writing and reading from the uart while
+ * in an interrupt or debug context.
+ */
+
+static int s3c24xx_serial_get_poll_char(struct uart_port *port)
+{
+ struct s3c24xx_uart_port *ourport = to_ourport(port);
+ unsigned int ufstat;
+
+ ufstat = rd_regl(port, S3C2410_UFSTAT);
+ if (s3c24xx_serial_rx_fifocnt(ourport, ufstat) == 0)
+ return NO_POLL_CHAR;
+
+ return rd_regb(port, S3C2410_URXH);
+}
+
+static void s3c24xx_serial_put_poll_char(struct uart_port *port,
+ unsigned char c)
+{
+ unsigned int ufcon = rd_regl(cons_uart, S3C2410_UFCON);
+
+ while (!s3c24xx_serial_console_txrdy(port, ufcon))
+ cpu_relax();
+ wr_regb(cons_uart, S3C2410_UTXH, c);
+}
+
+#endif /* CONFIG_CONSOLE_POLL */
+
static void
s3c24xx_serial_console_putchar(struct uart_port *port, int ch)
{
@@ -1684,7 +1731,7 @@ static int __init s3c24xx_serial_modinit(void)
ret = uart_register_driver(&s3c24xx_uart_drv);
if (ret < 0) {
pr_err("Failed to register Samsung UART driver\n");
- return -1;
+ return ret;
}
return platform_driver_register(&samsung_serial_driver);
diff --git a/drivers/tty/serial/sccnxp.c b/drivers/tty/serial/sccnxp.c
index 05d767cf82a7..b7086d004f5f 100644
--- a/drivers/tty/serial/sccnxp.c
+++ b/drivers/tty/serial/sccnxp.c
@@ -213,7 +213,7 @@ const struct baud_table baud_std[] = {
{ 0, 0, 0, 0 }
};
-static void sccnxp_set_baud(struct uart_port *port, int baud)
+static int sccnxp_set_baud(struct uart_port *port, int baud)
{
struct sccnxp_port *s = dev_get_drvdata(port->dev);
int div_std, tmp_baud, bestbaud = baud, besterr = -1;
@@ -244,8 +244,11 @@ static void sccnxp_set_baud(struct uart_port *port, int baud)
sccnxp_port_write(port, SCCNXP_ACR_REG, acr | ACR_TIMER_MODE);
sccnxp_port_write(port, SCCNXP_CSR_REG, (csr << 4) | csr);
- dev_dbg(port->dev, "Baudrate desired: %i, calculated: %i\n",
- baud, bestbaud);
+ if (baud != bestbaud)
+ dev_dbg(port->dev, "Baudrate desired: %i, calculated: %i\n",
+ baud, bestbaud);
+
+ return bestbaud;
}
static void sccnxp_enable_irq(struct uart_port *port, int mask)
@@ -528,7 +531,6 @@ static void sccnxp_set_termios(struct uart_port *port,
/* Mask termios capabilities we don't support */
termios->c_cflag &= ~CMSPAR;
- termios->c_iflag &= ~(IXON | IXOFF | IXANY);
/* Disable RX & TX, reset break condition, status and FIFOs */
sccnxp_port_write(port, SCCNXP_CR_REG, CR_CMD_RX_RESET |
@@ -548,8 +550,8 @@ static void sccnxp_set_termios(struct uart_port *port,
case CS7:
mr1 = MR1_BITS_7;
break;
- default:
case CS8:
+ default:
mr1 = MR1_BITS_8;
break;
}
@@ -587,11 +589,14 @@ static void sccnxp_set_termios(struct uart_port *port,
baud = uart_get_baud_rate(port, termios, old, 50,
(s->flags & SCCNXP_HAVE_MR0) ?
230400 : 38400);
- sccnxp_set_baud(port, baud);
+ baud = sccnxp_set_baud(port, baud);
/* Update timeout according to new baud rate */
uart_update_timeout(port, termios->c_cflag, baud);
+ if (tty_termios_baud_rate(termios))
+ tty_termios_encode_baud_rate(termios, baud, baud);
+
/* Enable RX & TX */
sccnxp_port_write(port, SCCNXP_CR_REG, CR_RX_ENABLE | CR_TX_ENABLE);
diff --git a/drivers/tty/serial/serial_core.c b/drivers/tty/serial/serial_core.c
index 046279ce3e8d..0fcfd98a9566 100644
--- a/drivers/tty/serial/serial_core.c
+++ b/drivers/tty/serial/serial_core.c
@@ -2129,6 +2129,7 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options)
int bits = 8;
int parity = 'n';
int flow = 'n';
+ int ret;
if (!state || !state->uart_port)
return -1;
@@ -2137,6 +2138,22 @@ static int uart_poll_init(struct tty_driver *driver, int line, char *options)
if (!(port->ops->poll_get_char && port->ops->poll_put_char))
return -1;
+ if (port->ops->poll_init) {
+ struct tty_port *tport = &state->port;
+
+ ret = 0;
+ mutex_lock(&tport->mutex);
+ /*
+ * We don't set ASYNCB_INITIALIZED as we only initialized the
+ * hw, e.g. state->xmit is still uninitialized.
+ */
+ if (!test_bit(ASYNCB_INITIALIZED, &tport->flags))
+ ret = port->ops->poll_init(port);
+ mutex_unlock(&tport->mutex);
+ if (ret)
+ return ret;
+ }
+
if (options) {
uart_parse_options(options, &baud, &parity, &bits, &flow);
return uart_set_options(port, NULL, baud, parity, bits, flow);
@@ -2501,9 +2518,12 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
{
struct uart_state *state = uport->state;
struct tty_port *port = &state->port;
- struct tty_ldisc *ld = tty_ldisc_ref(port->tty);
+ struct tty_ldisc *ld = NULL;
struct pps_event_time ts;
+ struct tty_struct *tty = port->tty;
+ if (tty)
+ ld = tty_ldisc_ref(tty);
if (ld && ld->ops->dcd_change)
pps_get_ts(&ts);
@@ -2516,12 +2536,12 @@ void uart_handle_dcd_change(struct uart_port *uport, unsigned int status)
if (port->flags & ASYNC_CHECK_CD) {
if (status)
wake_up_interruptible(&port->open_wait);
- else if (port->tty)
- tty_hangup(port->tty);
+ else if (tty)
+ tty_hangup(tty);
}
if (ld && ld->ops->dcd_change)
- ld->ops->dcd_change(port->tty, status, &ts);
+ ld->ops->dcd_change(tty, status, &ts);
if (ld)
tty_ldisc_deref(ld);
}
diff --git a/drivers/tty/serial/serial_ks8695.c b/drivers/tty/serial/serial_ks8695.c
index 7c13639c597e..9bd004f9da89 100644
--- a/drivers/tty/serial/serial_ks8695.c
+++ b/drivers/tty/serial/serial_ks8695.c
@@ -548,8 +548,8 @@ static struct uart_ops ks8695uart_pops = {
static struct uart_port ks8695uart_ports[SERIAL_KS8695_NR] = {
{
- .membase = (void *) KS8695_UART_VA,
- .mapbase = KS8695_UART_VA,
+ .membase = KS8695_UART_VA,
+ .mapbase = KS8695_UART_PA,
.iotype = SERIAL_IO_MEM,
.irq = KS8695_IRQ_UART_TX,
.uartclk = KS8695_CLOCK_RATE * 16,
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 2be006fb3da0..205d4cf4a063 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -34,6 +34,7 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
/*
* UART Register offsets
@@ -76,6 +77,8 @@
#define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT)
#define TX_FIFO_INTS (TXFAE | TXFE | TXUDR)
+#define VT8500_MAX_PORTS 6
+
struct vt8500_port {
struct uart_port uart;
char name[16];
@@ -83,6 +86,13 @@ struct vt8500_port {
unsigned int ier;
};
+/*
+ * we use this variable to keep track of which ports
+ * have been allocated as we can't use pdev->id in
+ * devicetree
+ */
+static unsigned long vt8500_ports_in_use;
+
static inline void vt8500_write(struct uart_port *port, unsigned int val,
unsigned int off)
{
@@ -431,7 +441,7 @@ static int vt8500_verify_port(struct uart_port *port,
return 0;
}
-static struct vt8500_port *vt8500_uart_ports[4];
+static struct vt8500_port *vt8500_uart_ports[VT8500_MAX_PORTS];
static struct uart_driver vt8500_uart_driver;
#ifdef CONFIG_SERIAL_VT8500_CONSOLE
@@ -548,7 +558,9 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
{
struct vt8500_port *vt8500_port;
struct resource *mmres, *irqres;
+ struct device_node *np = pdev->dev.of_node;
int ret;
+ int port;
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
@@ -559,16 +571,46 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
if (!vt8500_port)
return -ENOMEM;
+ if (np)
+ port = of_alias_get_id(np, "serial");
+ if (port > VT8500_MAX_PORTS)
+ port = -1;
+ else
+ port = -1;
+
+ if (port < 0) {
+ /* calculate the port id */
+ port = find_first_zero_bit(&vt8500_ports_in_use,
+ sizeof(vt8500_ports_in_use));
+ }
+
+ if (port > VT8500_MAX_PORTS)
+ return -ENODEV;
+
+ /* reserve the port id */
+ if (test_and_set_bit(port, &vt8500_ports_in_use)) {
+ /* port already in use - shouldn't really happen */
+ return -EBUSY;
+ }
+
vt8500_port->uart.type = PORT_VT8500;
vt8500_port->uart.iotype = UPIO_MEM;
vt8500_port->uart.mapbase = mmres->start;
vt8500_port->uart.irq = irqres->start;
vt8500_port->uart.fifosize = 16;
vt8500_port->uart.ops = &vt8500_uart_pops;
- vt8500_port->uart.line = pdev->id;
+ vt8500_port->uart.line = port;
vt8500_port->uart.dev = &pdev->dev;
vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
- vt8500_port->uart.uartclk = 24000000;
+
+ vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0);
+ if (vt8500_port->clk) {
+ vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk);
+ } else {
+ /* use the default of 24Mhz if not specified and warn */
+ pr_warn("%s: serial clock source not specified\n", __func__);
+ vt8500_port->uart.uartclk = 24000000;
+ }
snprintf(vt8500_port->name, sizeof(vt8500_port->name),
"VT8500 UART%d", pdev->id);
@@ -579,7 +621,7 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
goto err;
}
- vt8500_uart_ports[pdev->id] = vt8500_port;
+ vt8500_uart_ports[port] = vt8500_port;
uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart);
@@ -603,12 +645,18 @@ static int __devexit vt8500_serial_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id wmt_dt_ids[] = {
+ { .compatible = "via,vt8500-uart", },
+ {}
+};
+
static struct platform_driver vt8500_platform_driver = {
.probe = vt8500_serial_probe,
.remove = __devexit_p(vt8500_serial_remove),
.driver = {
.name = "vt8500_serial",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(wmt_dt_ids),
},
};
@@ -642,4 +690,4 @@ module_exit(vt8500_serial_exit);
MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
MODULE_DESCRIPTION("Driver for vt8500 serial device");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 7de2285d9fa9..4c90b510d016 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -47,6 +47,7 @@ config USB_ARCH_HAS_EHCI
default y if SPARC_LEON
default y if ARCH_MMP
default y if MACH_LOONGSON1
+ default y if PLAT_ORION
default PCI
# some non-PCI HCDs implement xHCI
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index d7e422dc0ef7..e1f8b2c973fe 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -307,6 +307,34 @@ enum {
#define FW_GET_BYTE(p) (*((__u8 *) (p)))
#define FW_DIR "ueagle-atm/"
+#define EAGLE_FIRMWARE FW_DIR "eagle.fw"
+#define ADI930_FIRMWARE FW_DIR "adi930.fw"
+#define EAGLE_I_FIRMWARE FW_DIR "eagleI.fw"
+#define EAGLE_II_FIRMWARE FW_DIR "eagleII.fw"
+#define EAGLE_III_FIRMWARE FW_DIR "eagleIII.fw"
+#define EAGLE_IV_FIRMWARE FW_DIR "eagleIV.fw"
+
+#define DSP4I_FIRMWARE FW_DIR "DSP4i.bin"
+#define DSP4P_FIRMWARE FW_DIR "DSP4p.bin"
+#define DSP9I_FIRMWARE FW_DIR "DSP9i.bin"
+#define DSP9P_FIRMWARE FW_DIR "DSP9p.bin"
+#define DSPEI_FIRMWARE FW_DIR "DSPei.bin"
+#define DSPEP_FIRMWARE FW_DIR "DSPep.bin"
+#define FPGA930_FIRMWARE FW_DIR "930-fpga.bin"
+
+#define CMV4P_FIRMWARE FW_DIR "CMV4p.bin"
+#define CMV4PV2_FIRMWARE FW_DIR "CMV4p.bin.v2"
+#define CMV4I_FIRMWARE FW_DIR "CMV4i.bin"
+#define CMV4IV2_FIRMWARE FW_DIR "CMV4i.bin.v2"
+#define CMV9P_FIRMWARE FW_DIR "CMV9p.bin"
+#define CMV9PV2_FIRMWARE FW_DIR "CMV9p.bin.v2"
+#define CMV9I_FIRMWARE FW_DIR "CMV9i.bin"
+#define CMV9IV2_FIRMWARE FW_DIR "CMV9i.bin.v2"
+#define CMVEP_FIRMWARE FW_DIR "CMVep.bin"
+#define CMVEPV2_FIRMWARE FW_DIR "CMVep.bin.v2"
+#define CMVEI_FIRMWARE FW_DIR "CMVei.bin"
+#define CMVEIV2_FIRMWARE FW_DIR "CMVei.bin.v2"
+
#define UEA_FW_NAME_MAX 30
#define NB_MODEM 4
@@ -694,26 +722,26 @@ err:
static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
{
int ret;
- char *fw_name = FW_DIR "eagle.fw";
+ char *fw_name = EAGLE_FIRMWARE;
uea_enters(usb);
uea_info(usb, "pre-firmware device, uploading firmware\n");
switch (ver) {
case ADI930:
- fw_name = FW_DIR "adi930.fw";
+ fw_name = ADI930_FIRMWARE;
break;
case EAGLE_I:
- fw_name = FW_DIR "eagleI.fw";
+ fw_name = EAGLE_I_FIRMWARE;
break;
case EAGLE_II:
- fw_name = FW_DIR "eagleII.fw";
+ fw_name = EAGLE_II_FIRMWARE;
break;
case EAGLE_III:
- fw_name = FW_DIR "eagleIII.fw";
+ fw_name = EAGLE_III_FIRMWARE;
break;
case EAGLE_IV:
- fw_name = FW_DIR "eagleIV.fw";
+ fw_name = EAGLE_IV_FIRMWARE;
break;
}
@@ -869,19 +897,19 @@ static int request_dsp(struct uea_softc *sc)
if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
if (IS_ISDN(sc))
- dsp_name = FW_DIR "DSP4i.bin";
+ dsp_name = DSP4I_FIRMWARE;
else
- dsp_name = FW_DIR "DSP4p.bin";
+ dsp_name = DSP4P_FIRMWARE;
} else if (UEA_CHIP_VERSION(sc) == ADI930) {
if (IS_ISDN(sc))
- dsp_name = FW_DIR "DSP9i.bin";
+ dsp_name = DSP9I_FIRMWARE;
else
- dsp_name = FW_DIR "DSP9p.bin";
+ dsp_name = DSP9P_FIRMWARE;
} else {
if (IS_ISDN(sc))
- dsp_name = FW_DIR "DSPei.bin";
+ dsp_name = DSPEI_FIRMWARE;
else
- dsp_name = FW_DIR "DSPep.bin";
+ dsp_name = DSPEP_FIRMWARE;
}
ret = request_firmware(&sc->dsp_firm, dsp_name, &sc->usb_dev->dev);
@@ -1925,7 +1953,7 @@ static int load_XILINX_firmware(struct uea_softc *sc)
int ret, size, u, ln;
const u8 *pfw;
u8 value;
- char *fw_name = FW_DIR "930-fpga.bin";
+ char *fw_name = FPGA930_FIRMWARE;
uea_enters(INS_TO_USBDEV(sc));
@@ -2753,3 +2781,28 @@ module_usb_driver(uea_driver);
MODULE_AUTHOR("Damien Bergamini/Matthieu Castet/Stanislaw W. Gruszka");
MODULE_DESCRIPTION("ADI 930/Eagle USB ADSL Modem driver");
MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(EAGLE_FIRMWARE);
+MODULE_FIRMWARE(ADI930_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_I_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_II_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_III_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_IV_FIRMWARE);
+MODULE_FIRMWARE(DSP4I_FIRMWARE);
+MODULE_FIRMWARE(DSP4P_FIRMWARE);
+MODULE_FIRMWARE(DSP9I_FIRMWARE);
+MODULE_FIRMWARE(DSP9P_FIRMWARE);
+MODULE_FIRMWARE(DSPEI_FIRMWARE);
+MODULE_FIRMWARE(DSPEP_FIRMWARE);
+MODULE_FIRMWARE(FPGA930_FIRMWARE);
+MODULE_FIRMWARE(CMV4P_FIRMWARE);
+MODULE_FIRMWARE(CMV4PV2_FIRMWARE);
+MODULE_FIRMWARE(CMV4I_FIRMWARE);
+MODULE_FIRMWARE(CMV4IV2_FIRMWARE);
+MODULE_FIRMWARE(CMV9P_FIRMWARE);
+MODULE_FIRMWARE(CMV9PV2_FIRMWARE);
+MODULE_FIRMWARE(CMV9I_FIRMWARE);
+MODULE_FIRMWARE(CMV9IV2_FIRMWARE);
+MODULE_FIRMWARE(CMVEP_FIRMWARE);
+MODULE_FIRMWARE(CMVEPV2_FIRMWARE);
+MODULE_FIRMWARE(CMVEI_FIRMWARE);
+MODULE_FIRMWARE(CMVEIV2_FIRMWARE);
diff --git a/drivers/usb/atm/usbatm.c b/drivers/usb/atm/usbatm.c
index ee62b3576f94..35f10bfe15db 100644
--- a/drivers/usb/atm/usbatm.c
+++ b/drivers/usb/atm/usbatm.c
@@ -84,7 +84,7 @@
#include <linux/ratelimit.h>
#ifdef VERBOSE_DEBUG
-static int usbatm_print_packet(const unsigned char *data, int len);
+static int usbatm_print_packet(struct usbatm_data *instance, const unsigned char *data, int len);
#define PACKETDEBUG(arg...) usbatm_print_packet(arg)
#define vdbg(arg...) dev_dbg(arg)
#else
@@ -230,8 +230,8 @@ static int usbatm_submit_urb(struct urb *urb)
struct usbatm_channel *channel = urb->context;
int ret;
- vdbg("%s: submitting urb 0x%p, size %u",
- __func__, urb, urb->transfer_buffer_length);
+ /* vdbg("%s: submitting urb 0x%p, size %u",
+ __func__, urb, urb->transfer_buffer_length); */
ret = usb_submit_urb(urb, GFP_ATOMIC);
if (ret) {
@@ -261,8 +261,8 @@ static void usbatm_complete(struct urb *urb)
unsigned long flags;
int status = urb->status;
- vdbg("%s: urb 0x%p, status %d, actual_length %d",
- __func__, urb, status, urb->actual_length);
+ /* vdbg("%s: urb 0x%p, status %d, actual_length %d",
+ __func__, urb, status, urb->actual_length); */
/* usually in_interrupt(), but not always */
spin_lock_irqsave(&channel->lock, flags);
@@ -311,7 +311,7 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
int vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
u8 pti = ((source[3] & 0xe) >> 1);
- vdbg("%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti);
+ vdbg(&instance->usb_intf->dev, "%s: vpi %hd, vci %d, pti %d", __func__, vpi, vci, pti);
if ((vci != instance->cached_vci) || (vpi != instance->cached_vpi)) {
instance->cached_vpi = vpi;
@@ -381,7 +381,9 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
goto out;
}
- vdbg("%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", __func__, length, pdu_length, vcc);
+ vdbg(&instance->usb_intf->dev,
+ "%s: got packet (length: %u, pdu_length: %u, vcc: 0x%p)",
+ __func__, length, pdu_length, vcc);
if (!(skb = dev_alloc_skb(length))) {
if (printk_ratelimit())
@@ -391,7 +393,9 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
goto out;
}
- vdbg("%s: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", __func__, skb, skb->truesize);
+ vdbg(&instance->usb_intf->dev,
+ "%s: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)",
+ __func__, skb, skb->truesize);
if (!atm_charge(vcc, skb->truesize)) {
atm_rldbg(instance, "%s: failed atm_charge (skb->truesize: %u)!\n",
@@ -405,10 +409,11 @@ static void usbatm_extract_one_cell(struct usbatm_data *instance, unsigned char
length);
__skb_put(skb, length);
- vdbg("%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
+ vdbg(&instance->usb_intf->dev,
+ "%s: sending skb 0x%p, skb->len %u, skb->truesize %u",
__func__, skb, skb->len, skb->truesize);
- PACKETDEBUG(skb->data, skb->len);
+ PACKETDEBUG(instance, skb->data, skb->len);
vcc->push(vcc, skb);
@@ -474,7 +479,8 @@ static unsigned int usbatm_write_cells(struct usbatm_data *instance,
unsigned int bytes_written;
unsigned int stride = instance->tx_channel.stride;
- vdbg("%s: skb->len=%d, avail_space=%u", __func__, skb->len, avail_space);
+ vdbg(&instance->usb_intf->dev, "%s: skb->len=%d, avail_space=%u",
+ __func__, skb->len, avail_space);
UDSL_ASSERT(instance, !(avail_space % stride));
for (bytes_written = 0; bytes_written < avail_space && ctrl->len;
@@ -534,7 +540,8 @@ static void usbatm_rx_process(unsigned long data)
struct urb *urb;
while ((urb = usbatm_pop_urb(&instance->rx_channel))) {
- vdbg("%s: processing urb 0x%p", __func__, urb);
+ vdbg(&instance->usb_intf->dev,
+ "%s: processing urb 0x%p", __func__, urb);
if (usb_pipeisoc(urb->pipe)) {
unsigned char *merge_start = NULL;
@@ -608,7 +615,8 @@ static void usbatm_tx_process(unsigned long data)
buffer + bytes_written,
buf_size - bytes_written);
- vdbg("%s: wrote %u bytes from skb 0x%p to urb 0x%p",
+ vdbg(&instance->usb_intf->dev,
+ "%s: wrote %u bytes from skb 0x%p to urb 0x%p",
__func__, bytes_written, skb, urb);
if (!UDSL_SKB(skb)->len) {
@@ -664,7 +672,8 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
struct usbatm_control *ctrl = UDSL_SKB(skb);
int err;
- vdbg("%s called (skb 0x%p, len %u)", __func__, skb, skb->len);
+ vdbg(&instance->usb_intf->dev, "%s called (skb 0x%p, len %u)", __func__,
+ skb, skb->len);
/* racy disconnection check - fine */
if (!instance || instance->disconnected) {
@@ -688,7 +697,7 @@ static int usbatm_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
goto fail;
}
- PACKETDEBUG(skb->data, skb->len);
+ PACKETDEBUG(instance, skb->data, skb->len);
/* initialize the control block */
ctrl->atm.vcc = vcc;
@@ -1202,7 +1211,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
if (i >= num_rcv_urbs)
list_add_tail(&urb->urb_list, &channel->list);
- vdbg("%s: alloced buffer 0x%p buf size %u urb 0x%p",
+ vdbg(&intf->dev, "%s: alloced buffer 0x%p buf size %u urb 0x%p",
__func__, urb->transfer_buffer, urb->transfer_buffer_length, urb);
}
@@ -1359,7 +1368,8 @@ MODULE_VERSION(DRIVER_VERSION);
************/
#ifdef VERBOSE_DEBUG
-static int usbatm_print_packet(const unsigned char *data, int len)
+static int usbatm_print_packet(struct usbatm_data *instance,
+ const unsigned char *data, int len)
{
unsigned char buffer[256];
int i = 0, j = 0;
@@ -1369,7 +1379,7 @@ static int usbatm_print_packet(const unsigned char *data, int len)
sprintf(buffer, "%.3d :", i);
for (j = 0; (j < 16) && (i < len); j++, i++)
sprintf(buffer, "%s %2.2x", buffer, data[i]);
- dbg("%s", buffer);
+ dev_dbg(&instance->usb_intf->dev, "%s", buffer);
}
return i;
}
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index 47e499c9c0b6..1ea932a13685 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -13,7 +13,6 @@ if USB_CHIPIDEA
config USB_CHIPIDEA_UDC
bool "ChipIdea device controller"
depends on USB_GADGET=y || USB_GADGET=USB_CHIPIDEA
- select USB_GADGET_DUALSPEED
help
Say Y here to enable device controller functionality of the
ChipIdea driver.
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
index 5c66d9c330ca..d92ca325b104 100644
--- a/drivers/usb/chipidea/Makefile
+++ b/drivers/usb/chipidea/Makefile
@@ -1,3 +1,5 @@
+ccflags-$(CONFIG_USB_CHIPIDEA_DEBUG) := -DDEBUG
+
obj-$(CONFIG_USB_CHIPIDEA) += ci_hdrc.o
ci_hdrc-y := core.o
@@ -15,5 +17,5 @@ ifneq ($(CONFIG_PCI),)
endif
ifneq ($(CONFIG_OF_DEVICE),)
- obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o
+ obj-$(CONFIG_USB_CHIPIDEA) += ci13xxx_imx.o usbmisc_imx6q.o
endif
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index d738603a2757..e25d1263da13 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -139,6 +139,7 @@ struct ci13xxx {
enum ci_role role;
bool is_otg;
struct work_struct work;
+ struct work_struct vbus_work;
struct workqueue_struct *wq;
struct dma_pool *qh_pool;
diff --git a/drivers/usb/chipidea/ci13xxx_imx.c b/drivers/usb/chipidea/ci13xxx_imx.c
index ef60d06835d0..0f5ca4bea17f 100644
--- a/drivers/usb/chipidea/ci13xxx_imx.c
+++ b/drivers/usb/chipidea/ci13xxx_imx.c
@@ -20,8 +20,10 @@
#include <linux/usb/chipidea.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
+#include <linux/pinctrl/consumer.h>
#include "ci.h"
+#include "ci13xxx_imx.h"
#define pdev_to_phy(pdev) \
((struct usb_phy *)platform_get_drvdata(pdev))
@@ -34,6 +36,55 @@ struct ci13xxx_imx_data {
struct regulator *reg_vbus;
};
+static const struct usbmisc_ops *usbmisc_ops;
+
+/* Common functions shared by usbmisc drivers */
+
+int usbmisc_set_ops(const struct usbmisc_ops *ops)
+{
+ if (usbmisc_ops)
+ return -EBUSY;
+
+ usbmisc_ops = ops;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usbmisc_set_ops);
+
+void usbmisc_unset_ops(const struct usbmisc_ops *ops)
+{
+ usbmisc_ops = NULL;
+}
+EXPORT_SYMBOL_GPL(usbmisc_unset_ops);
+
+int usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev)
+{
+ struct device_node *np = dev->of_node;
+ struct of_phandle_args args;
+ int ret;
+
+ usbdev->dev = dev;
+
+ ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
+ 0, &args);
+ if (ret) {
+ dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
+ ret);
+ memset(usbdev, 0, sizeof(*usbdev));
+ return ret;
+ }
+ usbdev->index = args.args[0];
+ of_node_put(args.np);
+
+ if (of_find_property(np, "disable-over-current", NULL))
+ usbdev->disable_oc = 1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(usbmisc_get_init_data);
+
+/* End of common functions shared by usbmisc drivers*/
+
static struct ci13xxx_platform_data ci13xxx_imx_platdata __devinitdata = {
.name = "ci13xxx_imx",
.flags = CI13XXX_REQUIRE_TRANSCEIVER |
@@ -49,8 +100,13 @@ static int __devinit ci13xxx_imx_probe(struct platform_device *pdev)
struct device_node *phy_np;
struct resource *res;
struct regulator *reg_vbus;
+ struct pinctrl *pinctrl;
int ret;
+ if (of_find_property(pdev->dev.of_node, "fsl,usbmisc", NULL)
+ && !usbmisc_ops)
+ return -EPROBE_DEFER;
+
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
dev_err(&pdev->dev, "Failed to allocate CI13xxx-IMX data!\n");
@@ -63,6 +119,11 @@ static int __devinit ci13xxx_imx_probe(struct platform_device *pdev)
return -ENOENT;
}
+ pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+ if (IS_ERR(pinctrl))
+ dev_warn(&pdev->dev, "pinctrl get/select failed, err=%ld\n",
+ PTR_ERR(pinctrl));
+
data->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(data->clk)) {
dev_err(&pdev->dev,
@@ -120,6 +181,16 @@ static int __devinit ci13xxx_imx_probe(struct platform_device *pdev)
*pdev->dev.dma_mask = DMA_BIT_MASK(32);
dma_set_coherent_mask(&pdev->dev, *pdev->dev.dma_mask);
}
+
+ if (usbmisc_ops && usbmisc_ops->init) {
+ ret = usbmisc_ops->init(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "usbmisc init failed, ret=%d\n", ret);
+ goto err;
+ }
+ }
+
plat_ci = ci13xxx_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
&ci13xxx_imx_platdata);
diff --git a/drivers/usb/chipidea/ci13xxx_imx.h b/drivers/usb/chipidea/ci13xxx_imx.h
new file mode 100644
index 000000000000..2e88accb3d67
--- /dev/null
+++ b/drivers/usb/chipidea/ci13xxx_imx.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2012 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
+ */
+
+/* Used to set SoC specific callbacks */
+struct usbmisc_ops {
+ /* It's called once when probe a usb device */
+ int (*init)(struct device *dev);
+};
+
+struct usbmisc_usb_device {
+ struct device *dev; /* usb controller device */
+ int index;
+
+ int disable_oc:1; /* over current detect disabled */
+};
+
+int usbmisc_set_ops(const struct usbmisc_ops *ops);
+void usbmisc_unset_ops(const struct usbmisc_ops *ops);
+int
+usbmisc_get_init_data(struct device *dev, struct usbmisc_usb_device *usbdev);
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 1083585fad00..f69d029b4607 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -273,14 +273,13 @@ static void ci_role_work(struct work_struct *work)
struct ci13xxx *ci = container_of(work, struct ci13xxx, work);
enum ci_role role = ci_otg_role(ci);
- hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS);
-
if (role != ci->role) {
dev_dbg(ci->dev, "switching from %s to %s\n",
ci_role(ci)->name, ci->roles[role]->name);
ci_role_stop(ci);
ci_role_start(ci, role);
+ enable_irq(ci->irq);
}
}
@@ -320,17 +319,22 @@ static irqreturn_t ci_irq(int irq, void *data)
{
struct ci13xxx *ci = data;
irqreturn_t ret = IRQ_NONE;
+ u32 otgsc = 0;
+
+ if (ci->is_otg)
+ otgsc = hw_read(ci, OP_OTGSC, ~0);
- if (ci->is_otg) {
- u32 sts = hw_read(ci, OP_OTGSC, ~0);
+ if (ci->role != CI_ROLE_END)
+ ret = ci_role(ci)->irq(ci);
- if (sts & OTGSC_IDIS) {
- queue_work(ci->wq, &ci->work);
- ret = IRQ_HANDLED;
- }
+ if (ci->is_otg && (otgsc & OTGSC_IDIS)) {
+ hw_write(ci, OP_OTGSC, OTGSC_IDIS, OTGSC_IDIS);
+ disable_irq_nosync(ci->irq);
+ queue_work(ci->wq, &ci->work);
+ ret = IRQ_HANDLED;
}
- return ci->role == CI_ROLE_END ? ret : ci_role(ci)->irq(ci);
+ return ret;
}
static DEFINE_IDA(ci_ida);
@@ -462,6 +466,8 @@ static int __devinit ci_hdrc_probe(struct platform_device *pdev)
if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
ci->is_otg = true;
+ /* ID pin needs 1ms debouce time, we delay 2ms for safe */
+ mdelay(2);
ci->role = ci_otg_role(ci);
} else {
ci->role = ci->roles[CI_ROLE_HOST]
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index d214448b677e..2f45bba8561d 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -305,6 +305,18 @@ static u32 hw_test_and_clear_intr_active(struct ci13xxx *ci)
return reg;
}
+static void hw_enable_vbus_intr(struct ci13xxx *ci)
+{
+ hw_write(ci, OP_OTGSC, OTGSC_AVVIS, OTGSC_AVVIS);
+ hw_write(ci, OP_OTGSC, OTGSC_AVVIE, OTGSC_AVVIE);
+ queue_work(ci->wq, &ci->vbus_work);
+}
+
+static void hw_disable_vbus_intr(struct ci13xxx *ci)
+{
+ hw_write(ci, OP_OTGSC, OTGSC_AVVIE, 0);
+}
+
/**
* hw_test_and_clear_setup_guard: test & clear setup guard (execute without
* interruption)
@@ -371,6 +383,16 @@ static int hw_usb_reset(struct ci13xxx *ci)
return 0;
}
+static void vbus_work(struct work_struct *work)
+{
+ struct ci13xxx *ci = container_of(work, struct ci13xxx, vbus_work);
+
+ if (hw_read(ci, OP_OTGSC, OTGSC_AVV))
+ usb_gadget_vbus_connect(&ci->gadget);
+ else
+ usb_gadget_vbus_disconnect(&ci->gadget);
+}
+
/******************************************************************************
* UTIL block
*****************************************************************************/
@@ -1370,6 +1392,7 @@ static int ci13xxx_vbus_session(struct usb_gadget *_gadget, int is_active)
if (is_active) {
pm_runtime_get_sync(&_gadget->dev);
hw_device_reset(ci, USBMODE_CM_DC);
+ hw_enable_vbus_intr(ci);
hw_device_state(ci, ci->ep0out->qh.dma);
} else {
hw_device_state(ci, 0);
@@ -1544,8 +1567,10 @@ static int ci13xxx_start(struct usb_gadget *gadget,
pm_runtime_get_sync(&ci->gadget.dev);
if (ci->platdata->flags & CI13XXX_PULLUP_ON_VBUS) {
if (ci->vbus_active) {
- if (ci->platdata->flags & CI13XXX_REGS_SHARED)
+ if (ci->platdata->flags & CI13XXX_REGS_SHARED) {
hw_device_reset(ci, USBMODE_CM_DC);
+ hw_enable_vbus_intr(ci);
+ }
} else {
pm_runtime_put_sync(&ci->gadget.dev);
goto done;
@@ -1651,6 +1676,13 @@ static irqreturn_t udc_irq(struct ci13xxx *ci)
} else {
retval = IRQ_NONE;
}
+
+ intr = hw_read(ci, OP_OTGSC, ~0);
+ hw_write(ci, OP_OTGSC, ~0, intr);
+
+ if (intr & (OTGSC_AVVIE & OTGSC_AVVIS))
+ queue_work(ci->wq, &ci->vbus_work);
+
spin_unlock(&ci->lock);
return retval;
@@ -1726,6 +1758,7 @@ static int udc_start(struct ci13xxx *ci)
retval = hw_device_reset(ci, USBMODE_CM_DC);
if (retval)
goto put_transceiver;
+ hw_enable_vbus_intr(ci);
}
retval = device_register(&ci->gadget.dev);
@@ -1788,6 +1821,9 @@ static void udc_stop(struct ci13xxx *ci)
if (ci == NULL)
return;
+ hw_disable_vbus_intr(ci);
+ cancel_work_sync(&ci->vbus_work);
+
usb_del_gadget_udc(&ci->gadget);
destroy_eps(ci);
@@ -1828,6 +1864,7 @@ int ci_hdrc_gadget_init(struct ci13xxx *ci)
rdrv->irq = udc_irq;
rdrv->name = "gadget";
ci->roles[CI_ROLE_GADGET] = rdrv;
+ INIT_WORK(&ci->vbus_work, vbus_work);
return 0;
}
diff --git a/drivers/usb/chipidea/usbmisc_imx6q.c b/drivers/usb/chipidea/usbmisc_imx6q.c
new file mode 100644
index 000000000000..416e3fc58fd0
--- /dev/null
+++ b/drivers/usb/chipidea/usbmisc_imx6q.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2012 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/module.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include "ci13xxx_imx.h"
+
+#define USB_DEV_MAX 4
+
+#define BM_OVER_CUR_DIS BIT(7)
+
+struct imx6q_usbmisc {
+ void __iomem *base;
+ spinlock_t lock;
+ struct clk *clk;
+ struct usbmisc_usb_device usbdev[USB_DEV_MAX];
+};
+
+static struct imx6q_usbmisc *usbmisc;
+
+static struct usbmisc_usb_device *get_usbdev(struct device *dev)
+{
+ int i, ret;
+
+ for (i = 0; i < USB_DEV_MAX; i++) {
+ if (usbmisc->usbdev[i].dev == dev)
+ return &usbmisc->usbdev[i];
+ else if (!usbmisc->usbdev[i].dev)
+ break;
+ }
+
+ if (i >= USB_DEV_MAX)
+ return ERR_PTR(-EBUSY);
+
+ ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]);
+ if (ret)
+ return ERR_PTR(ret);
+
+ return &usbmisc->usbdev[i];
+}
+
+static int usbmisc_imx6q_init(struct device *dev)
+{
+
+ struct usbmisc_usb_device *usbdev;
+ unsigned long flags;
+ u32 reg;
+
+ usbdev = get_usbdev(dev);
+ if (IS_ERR(usbdev))
+ return PTR_ERR(usbdev);
+
+ if (usbdev->disable_oc) {
+ spin_lock_irqsave(&usbmisc->lock, flags);
+ reg = readl(usbmisc->base + usbdev->index * 4);
+ writel(reg | BM_OVER_CUR_DIS,
+ usbmisc->base + usbdev->index * 4);
+ spin_unlock_irqrestore(&usbmisc->lock, flags);
+ }
+
+ return 0;
+}
+
+static const struct usbmisc_ops imx6q_usbmisc_ops = {
+ .init = usbmisc_imx6q_init,
+};
+
+static const struct of_device_id usbmisc_imx6q_dt_ids[] = {
+ { .compatible = "fsl,imx6q-usbmisc"},
+ { /* sentinel */ }
+};
+
+static int __devinit usbmisc_imx6q_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ struct imx6q_usbmisc *data;
+ int ret;
+
+ if (usbmisc)
+ return -EBUSY;
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ spin_lock_init(&data->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->base = devm_request_and_ioremap(&pdev->dev, res);
+ if (!data->base)
+ return -EADDRNOTAVAIL;
+
+ data->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(data->clk)) {
+ dev_err(&pdev->dev,
+ "failed to get clock, err=%ld\n", PTR_ERR(data->clk));
+ return PTR_ERR(data->clk);
+ }
+
+ ret = clk_prepare_enable(data->clk);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "clk_prepare_enable failed, err=%d\n", ret);
+ return ret;
+ }
+
+ ret = usbmisc_set_ops(&imx6q_usbmisc_ops);
+ if (ret) {
+ clk_disable_unprepare(data->clk);
+ return ret;
+ }
+
+ usbmisc = data;
+
+ return 0;
+}
+
+static int __devexit usbmisc_imx6q_remove(struct platform_device *pdev)
+{
+ usbmisc_unset_ops(&imx6q_usbmisc_ops);
+ clk_disable_unprepare(usbmisc->clk);
+ return 0;
+}
+
+static struct platform_driver usbmisc_imx6q_driver = {
+ .probe = usbmisc_imx6q_probe,
+ .remove = __devexit_p(usbmisc_imx6q_remove),
+ .driver = {
+ .name = "usbmisc_imx6q",
+ .owner = THIS_MODULE,
+ .of_match_table = usbmisc_imx6q_dt_ids,
+ },
+};
+
+int __init usbmisc_imx6q_drv_init(void)
+{
+ return platform_driver_register(&usbmisc_imx6q_driver);
+}
+subsys_initcall(usbmisc_imx6q_drv_init);
+
+void __exit usbmisc_imx6q_drv_exit(void)
+{
+ platform_driver_unregister(&usbmisc_imx6q_driver);
+}
+module_exit(usbmisc_imx6q_drv_exit);
+
+MODULE_ALIAS("platform:usbmisc-imx6q");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("driver for imx6q usb non-core registers");
+MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index ff7b5a8d501c..36f2be4def2f 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -39,7 +39,6 @@
#include <linux/serial.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
-#include <linux/serial.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index 9981984b365b..f70c1a1694ad 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -56,7 +56,7 @@ config USB_SUSPEND
config USB_OTG
bool "OTG support"
- depends on USB && EXPERIMENTAL
+ depends on USB
depends on USB_SUSPEND
default n
help
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index f4bdd0ce8d56..7199adccf444 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -702,6 +702,8 @@ int usb_get_configuration(struct usb_device *dev)
if (result < 0) {
dev_err(ddev, "unable to read config index %d "
"descriptor/%s: %d\n", cfgno, "start", result);
+ if (result != -EPIPE)
+ goto err;
dev_err(ddev, "chopping to %d config(s)\n", cfgno);
dev->descriptor.bNumConfigurations = cfgno;
break;
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index d95696584762..f460de31acee 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -496,6 +496,7 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
char *pages_start, *data_end, *speed;
unsigned int length;
ssize_t total_written = 0;
+ struct usb_device *childdev = NULL;
/* don't bother with anything else if we're not writing any data */
if (*nbytes <= 0)
@@ -589,14 +590,12 @@ static ssize_t usb_device_dump(char __user **buffer, size_t *nbytes,
free_pages((unsigned long)pages_start, 1);
/* Now look at all of this device's children. */
- for (chix = 0; chix < usbdev->maxchild; chix++) {
- struct usb_device *childdev = usbdev->children[chix];
-
+ usb_hub_for_each_child(usbdev, chix, childdev) {
if (childdev) {
usb_lock_device(childdev);
ret = usb_device_dump(buffer, nbytes, skip_bytes,
file_offset, childdev, bus,
- level + 1, chix, ++cnt);
+ level + 1, chix - 1, ++cnt);
usb_unlock_device(childdev);
if (ret == -EFAULT)
return total_written;
@@ -624,7 +623,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
/* print devices for all busses */
list_for_each_entry(bus, &usb_bus_list, bus_list) {
/* recurse through all children of the root hub */
- if (!bus->root_hub)
+ if (!bus_to_hcd(bus)->rh_registered)
continue;
usb_lock_device(bus->root_hub);
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos,
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index ebb8a9de8b5f..e0356cb859b5 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1928,6 +1928,38 @@ static int proc_get_capabilities(struct dev_state *ps, void __user *arg)
return 0;
}
+static int proc_disconnect_claim(struct dev_state *ps, void __user *arg)
+{
+ struct usbdevfs_disconnect_claim dc;
+ struct usb_interface *intf;
+
+ if (copy_from_user(&dc, arg, sizeof(dc)))
+ return -EFAULT;
+
+ intf = usb_ifnum_to_if(ps->dev, dc.interface);
+ if (!intf)
+ return -EINVAL;
+
+ if (intf->dev.driver) {
+ struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+
+ if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) &&
+ strncmp(dc.driver, intf->dev.driver->name,
+ sizeof(dc.driver)) != 0)
+ return -EBUSY;
+
+ if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER) &&
+ strncmp(dc.driver, intf->dev.driver->name,
+ sizeof(dc.driver)) == 0)
+ return -EBUSY;
+
+ dev_dbg(&intf->dev, "disconnect by usbfs\n");
+ usb_driver_release_interface(driver, intf);
+ }
+
+ return claimintf(ps, dc.interface);
+}
+
/*
* NOTE: All requests here that have interface numbers as parameters
* are assuming that somehow the configuration has been prevented from
@@ -2101,6 +2133,9 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
case USBDEVFS_GET_CAPABILITIES:
ret = proc_get_capabilities(ps, p);
break;
+ case USBDEVFS_DISCONNECT_CLAIM:
+ ret = proc_disconnect_claim(ps, p);
+ break;
}
usb_unlock_device(dev);
if (ret >= 0)
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 445455a4429b..ddd820d25288 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -125,10 +125,9 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
{
struct usb_dynid *dynid, *n;
struct usb_driver *usb_driver = to_usb_driver(driver);
- u32 idVendor = 0;
- u32 idProduct = 0;
- int fields = 0;
- int retval = 0;
+ u32 idVendor;
+ u32 idProduct;
+ int fields;
fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
if (fields < 2)
@@ -141,14 +140,10 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count)
(id->idProduct == idProduct)) {
list_del(&dynid->node);
kfree(dynid);
- retval = 0;
break;
}
}
spin_unlock(&usb_driver->dynids.lock);
-
- if (retval)
- return retval;
return count;
}
static DRIVER_ATTR(remove_id, S_IRUGO | S_IWUSR, show_dynids, store_remove_id);
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index db7fe50c23d4..68cc6532e749 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -24,10 +24,6 @@ struct ep_device {
#define to_ep_device(_dev) \
container_of(_dev, struct ep_device, dev)
-struct device_type usb_ep_device_type = {
- .name = "usb_endpoint",
-};
-
struct ep_attribute {
struct attribute attr;
ssize_t (*show)(struct usb_device *,
@@ -172,6 +168,11 @@ static void ep_device_release(struct device *dev)
kfree(ep_dev);
}
+struct device_type usb_ep_device_type = {
+ .name = "usb_endpoint",
+ .release = ep_device_release,
+};
+
int usb_create_ep_devs(struct device *parent,
struct usb_host_endpoint *endpoint,
struct usb_device *udev)
@@ -190,7 +191,6 @@ int usb_create_ep_devs(struct device *parent,
ep_dev->dev.groups = ep_dev_groups;
ep_dev->dev.type = &usb_ep_device_type;
ep_dev->dev.parent = parent;
- ep_dev->dev.release = ep_device_release;
dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
retval = device_register(&ep_dev->dev);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index bc84106ac057..1e741bca0265 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -22,6 +22,7 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/bcd.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
@@ -123,9 +124,8 @@ static inline int is_root_hub(struct usb_device *udev)
*/
/*-------------------------------------------------------------------------*/
-
-#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff)
-#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff)
+#define KERNEL_REL bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff))
+#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
/* usb 3.0 root hub device descriptor */
static const u8 usb3_rh_dev_descriptor[18] = {
@@ -1011,10 +1011,7 @@ static int register_root_hub(struct usb_hcd *hcd)
if (retval) {
dev_err (parent_dev, "can't register root hub for %s, %d\n",
dev_name(&usb_dev->dev), retval);
- }
- mutex_unlock(&usb_bus_list_lock);
-
- if (retval == 0) {
+ } else {
spin_lock_irq (&hcd_root_hub_lock);
hcd->rh_registered = 1;
spin_unlock_irq (&hcd_root_hub_lock);
@@ -1023,6 +1020,7 @@ static int register_root_hub(struct usb_hcd *hcd)
if (HCD_DEAD(hcd))
usb_hc_died (hcd); /* This time clean up */
}
+ mutex_unlock(&usb_bus_list_lock);
return retval;
}
@@ -2153,15 +2151,8 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
- unsigned long flags;
irqreturn_t rc;
- /* IRQF_DISABLED doesn't work correctly with shared IRQs
- * when the first handler doesn't use it. So let's just
- * assume it's never used.
- */
- local_irq_save(flags);
-
if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
rc = IRQ_NONE;
else if (hcd->driver->irq(hcd) == IRQ_NONE)
@@ -2169,7 +2160,6 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
else
rc = IRQ_HANDLED;
- local_irq_restore(flags);
return rc;
}
EXPORT_SYMBOL_GPL(usb_hcd_irq);
@@ -2357,14 +2347,6 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
int retval;
if (hcd->driver->irq) {
-
- /* IRQF_DISABLED doesn't work as advertised when used together
- * with IRQF_SHARED. As usb_hcd_irq() will always disable
- * interrupts we can remove it here.
- */
- if (irqflags & IRQF_SHARED)
- irqflags &= ~IRQF_DISABLED;
-
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
hcd->driver->description, hcd->self.busnum);
retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 128a804c42f4..673ee4696262 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -39,6 +39,13 @@
#endif
#endif
+struct usb_port {
+ struct usb_device *child;
+ struct device dev;
+ struct dev_state *port_owner;
+ enum usb_port_connect_type connect_type;
+};
+
struct usb_hub {
struct device *intfdev; /* the "interface" device */
struct usb_device *hdev;
@@ -83,7 +90,7 @@ struct usb_hub {
u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds;
struct delayed_work init_work;
- struct dev_state **port_owners;
+ struct usb_port **ports;
};
static inline int hub_is_superspeed(struct usb_device *hdev)
@@ -156,6 +163,8 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100
+#define to_usb_port(_dev) \
+ container_of(_dev, struct usb_port, dev)
static int usb_reset_and_verify_device(struct usb_device *udev);
@@ -174,7 +183,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus)
/* Note that hdev or one of its children must be locked! */
static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
{
- if (!hdev || !hdev->actconfig)
+ if (!hdev || !hdev->actconfig || !hdev->maxchild)
return NULL;
return usb_get_intfdata(hdev->actconfig->interface[0]);
}
@@ -869,8 +878,8 @@ static int hub_port_disable(struct usb_hub *hub, int port1, int set_state)
struct usb_device *hdev = hub->hdev;
int ret = 0;
- if (hdev->children[port1-1] && set_state)
- usb_set_device_state(hdev->children[port1-1],
+ if (hub->ports[port1 - 1]->child && set_state)
+ usb_set_device_state(hub->ports[port1 - 1]->child,
USB_STATE_NOTATTACHED);
if (!hub->error && !hub_is_superspeed(hub->hdev))
ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
@@ -1026,7 +1035,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
* which ports need attention.
*/
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
- struct usb_device *udev = hdev->children[port1-1];
+ struct usb_device *udev = hub->ports[port1 - 1]->child;
u16 portstatus, portchange;
portstatus = portchange = 0;
@@ -1191,8 +1200,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
if (type != HUB_SUSPEND) {
/* Disconnect all the children */
for (i = 0; i < hdev->maxchild; ++i) {
- if (hdev->children[i])
- usb_disconnect(&hdev->children[i]);
+ if (hub->ports[i]->child)
+ usb_disconnect(&hub->ports[i]->child);
}
}
@@ -1222,6 +1231,52 @@ static int hub_post_reset(struct usb_interface *intf)
return 0;
}
+static void usb_port_device_release(struct device *dev)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+
+ kfree(port_dev);
+}
+
+static void usb_hub_remove_port_device(struct usb_hub *hub,
+ int port1)
+{
+ device_unregister(&hub->ports[port1 - 1]->dev);
+}
+
+struct device_type usb_port_device_type = {
+ .name = "usb_port",
+ .release = usb_port_device_release,
+};
+
+static int usb_hub_create_port_device(struct usb_hub *hub,
+ int port1)
+{
+ struct usb_port *port_dev = NULL;
+ int retval;
+
+ port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
+ if (!port_dev) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ hub->ports[port1 - 1] = port_dev;
+ port_dev->dev.parent = hub->intfdev;
+ port_dev->dev.type = &usb_port_device_type;
+ dev_set_name(&port_dev->dev, "port%d", port1);
+
+ retval = device_register(&port_dev->dev);
+ if (retval)
+ goto error_register;
+ return 0;
+
+error_register:
+ put_device(&port_dev->dev);
+exit:
+ return retval;
+}
+
static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
@@ -1231,7 +1286,7 @@ static int hub_configure(struct usb_hub *hub,
u16 hubstatus, hubchange;
u16 wHubCharacteristics;
unsigned int pipe;
- int maxp, ret;
+ int maxp, ret, i;
char *message = "out of memory";
hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
@@ -1271,11 +1326,9 @@ static int hub_configure(struct usb_hub *hub,
dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
(hdev->maxchild == 1) ? "" : "s");
- hdev->children = kzalloc(hdev->maxchild *
- sizeof(struct usb_device *), GFP_KERNEL);
- hub->port_owners = kzalloc(hdev->maxchild * sizeof(struct dev_state *),
- GFP_KERNEL);
- if (!hdev->children || !hub->port_owners) {
+ hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *),
+ GFP_KERNEL);
+ if (!hub->ports) {
ret = -ENOMEM;
goto fail;
}
@@ -1484,6 +1537,11 @@ static int hub_configure(struct usb_hub *hub,
if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
+ for (i = 0; i < hdev->maxchild; i++)
+ if (usb_hub_create_port_device(hub, i + 1) < 0)
+ dev_err(hub->intfdev,
+ "couldn't create port%d device.\n", i + 1);
+
hub_activate(hub, HUB_INIT);
return 0;
@@ -1508,6 +1566,7 @@ static void hub_disconnect(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
struct usb_device *hdev = interface_to_usbdev(intf);
+ int i;
/* Take the hub off the event list and don't let it be added again */
spin_lock_irq(&hub_event_lock);
@@ -1523,14 +1582,16 @@ static void hub_disconnect(struct usb_interface *intf)
hub_quiesce(hub, HUB_DISCONNECT);
usb_set_intfdata (intf, NULL);
+
+ for (i = 0; i < hdev->maxchild; i++)
+ usb_hub_remove_port_device(hub, i + 1);
hub->hdev->maxchild = 0;
if (hub->hdev->speed == USB_SPEED_HIGH)
highspeed_hubs--;
usb_free_urb(hub->urb);
- kfree(hdev->children);
- kfree(hub->port_owners);
+ kfree(hub->ports);
kfree(hub->descriptor);
kfree(hub->status);
kfree(hub->buffer);
@@ -1617,6 +1678,7 @@ static int
hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
{
struct usb_device *hdev = interface_to_usbdev (intf);
+ struct usb_hub *hub = hdev_to_hub(hdev);
/* assert ifno == 0 (part of hub spec) */
switch (code) {
@@ -1630,11 +1692,11 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
else {
info->nports = hdev->maxchild;
for (i = 0; i < info->nports; i++) {
- if (hdev->children[i] == NULL)
+ if (hub->ports[i]->child == NULL)
info->port[i] = 0;
else
info->port[i] =
- hdev->children[i]->devnum;
+ hub->ports[i]->child->devnum;
}
}
spin_unlock_irq(&device_state_lock);
@@ -1662,7 +1724,7 @@ static int find_port_owner(struct usb_device *hdev, unsigned port1,
/* This assumes that devices not managed by the hub driver
* will always have maxchild equal to 0.
*/
- *ppowner = &(hdev_to_hub(hdev)->port_owners[port1 - 1]);
+ *ppowner = &(hdev_to_hub(hdev)->ports[port1 - 1]->port_owner);
return 0;
}
@@ -1699,16 +1761,14 @@ int usb_hub_release_port(struct usb_device *hdev, unsigned port1,
void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner)
{
+ struct usb_hub *hub = hdev_to_hub(hdev);
int n;
- struct dev_state **powner;
- n = find_port_owner(hdev, 1, &powner);
- if (n == 0) {
- for (; n < hdev->maxchild; (++n, ++powner)) {
- if (*powner == owner)
- *powner = NULL;
- }
+ for (n = 0; n < hdev->maxchild; n++) {
+ if (hub->ports[n]->port_owner == owner)
+ hub->ports[n]->port_owner = NULL;
}
+
}
/* The caller must hold udev's lock */
@@ -1719,17 +1779,17 @@ bool usb_device_is_owned(struct usb_device *udev)
if (udev->state == USB_STATE_NOTATTACHED || !udev->parent)
return false;
hub = hdev_to_hub(udev->parent);
- return !!hub->port_owners[udev->portnum - 1];
+ return !!hub->ports[udev->portnum - 1]->port_owner;
}
-
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{
+ struct usb_hub *hub = hdev_to_hub(udev);
int i;
for (i = 0; i < udev->maxchild; ++i) {
- if (udev->children[i])
- recursively_mark_NOTATTACHED(udev->children[i]);
+ if (hub->ports[i]->child)
+ recursively_mark_NOTATTACHED(hub->ports[i]->child);
}
if (udev->state == USB_STATE_SUSPENDED)
udev->active_duration -= jiffies;
@@ -1893,6 +1953,7 @@ static void hub_free_dev(struct usb_device *udev)
void usb_disconnect(struct usb_device **pdev)
{
struct usb_device *udev = *pdev;
+ struct usb_hub *hub = hdev_to_hub(udev);
int i;
/* mark the device as inactive, so any further urb submissions for
@@ -1907,8 +1968,8 @@ void usb_disconnect(struct usb_device **pdev)
/* Free up all the children before we remove this device */
for (i = 0; i < udev->maxchild; i++) {
- if (udev->children[i])
- usb_disconnect(&udev->children[i]);
+ if (hub->ports[i]->child)
+ usb_disconnect(&hub->ports[i]->child);
}
/* deallocate hcd/hardware state ... nuking all pending urbs and
@@ -2113,7 +2174,8 @@ static void set_usb_port_removable(struct usb_device *udev)
return;
if (hub_is_superspeed(hdev)) {
- if (hub->descriptor->u.ss.DeviceRemovable & (1 << port))
+ if (le16_to_cpu(hub->descriptor->u.ss.DeviceRemovable)
+ & (1 << port))
removable = false;
} else {
if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8)))
@@ -3072,7 +3134,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
struct usb_device *udev;
- udev = hdev->children [port1-1];
+ udev = hub->ports[port1 - 1]->child;
if (udev && udev->can_submit) {
dev_warn(&intf->dev, "port %d nyet suspended\n", port1);
if (PMSG_IS_AUTO(msg))
@@ -3999,7 +4061,7 @@ hub_power_remaining (struct usb_hub *hub)
remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent;
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
- struct usb_device *udev = hdev->children[port1 - 1];
+ struct usb_device *udev = hub->ports[port1 - 1]->child;
int delta;
if (!udev)
@@ -4063,7 +4125,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
#endif
/* Try to resuscitate an existing device */
- udev = hdev->children[port1-1];
+ udev = hub->ports[port1 - 1]->child;
if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
udev->state != USB_STATE_NOTATTACHED) {
usb_lock_device(udev);
@@ -4092,7 +4154,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
/* Disconnect any existing devices under this port */
if (udev)
- usb_disconnect(&hdev->children[port1-1]);
+ usb_disconnect(&hub->ports[port1 - 1]->child);
clear_bit(port1, hub->change_bits);
/* We can forget about a "removed" device when there's a physical
@@ -4228,7 +4290,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
if (hdev->state == USB_STATE_NOTATTACHED)
status = -ENOTCONN;
else
- hdev->children[port1-1] = udev;
+ hub->ports[port1 - 1]->child = udev;
spin_unlock_irq(&device_state_lock);
/* Run it through the hoops (find a driver, etc) */
@@ -4236,7 +4298,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
status = usb_new_device(udev);
if (status) {
spin_lock_irq(&device_state_lock);
- hdev->children[port1-1] = NULL;
+ hub->ports[port1 - 1]->child = NULL;
spin_unlock_irq(&device_state_lock);
}
}
@@ -4282,7 +4344,7 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port,
int ret;
hdev = hub->hdev;
- udev = hdev->children[port-1];
+ udev = hub->ports[port - 1]->child;
if (!hub_is_superspeed(hdev)) {
if (!(portchange & USB_PORT_STAT_C_SUSPEND))
return 0;
@@ -4436,7 +4498,7 @@ static void hub_events(void)
*/
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
- && hdev->children[i-1]) {
+ && hub->ports[i - 1]->child) {
dev_err (hub_dev,
"port %i "
"disabled by hub (EMI?), "
@@ -4993,3 +5055,75 @@ void usb_queue_reset_device(struct usb_interface *iface)
schedule_work(&iface->reset_ws);
}
EXPORT_SYMBOL_GPL(usb_queue_reset_device);
+
+/**
+ * usb_hub_find_child - Get the pointer of child device
+ * attached to the port which is specified by @port1.
+ * @hdev: USB device belonging to the usb hub
+ * @port1: port num to indicate which port the child device
+ * is attached to.
+ *
+ * USB drivers call this function to get hub's child device
+ * pointer.
+ *
+ * Return NULL if input param is invalid and
+ * child's usb_device pointer if non-NULL.
+ */
+struct usb_device *usb_hub_find_child(struct usb_device *hdev,
+ int port1)
+{
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ if (port1 < 1 || port1 > hdev->maxchild)
+ return NULL;
+ return hub->ports[port1 - 1]->child;
+}
+EXPORT_SYMBOL_GPL(usb_hub_find_child);
+
+/**
+ * usb_set_hub_port_connect_type - set hub port connect type.
+ * @hdev: USB device belonging to the usb hub
+ * @port1: port num of the port
+ * @type: connect type of the port
+ */
+void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
+ enum usb_port_connect_type type)
+{
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ hub->ports[port1 - 1]->connect_type = type;
+}
+
+/**
+ * usb_get_hub_port_connect_type - Get the port's connect type
+ * @hdev: USB device belonging to the usb hub
+ * @port1: port num of the port
+ *
+ * Return connect type of the port and if input params are
+ * invalid, return USB_PORT_CONNECT_TYPE_UNKNOWN.
+ */
+enum usb_port_connect_type
+usb_get_hub_port_connect_type(struct usb_device *hdev, int port1)
+{
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ return hub->ports[port1 - 1]->connect_type;
+}
+
+#ifdef CONFIG_ACPI
+/**
+ * usb_get_hub_port_acpi_handle - Get the usb port's acpi handle
+ * @hdev: USB device belonging to the usb hub
+ * @port1: port num of the port
+ *
+ * Return port's acpi handle if successful, NULL if params are
+ * invaild.
+ */
+acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
+ int port1)
+{
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
+}
+#endif
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 0ab7da2283e3..1ed5afd91e6d 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -146,8 +146,6 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
dr->wIndex = cpu_to_le16(index);
dr->wLength = cpu_to_le16(size);
- /* dbg("usb_control_msg"); */
-
ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
kfree(dr);
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index e77a8e8eaa23..fdefd9c7f7af 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -209,7 +209,7 @@ void usb_detect_quirks(struct usb_device *udev)
* for all devices. It will affect things like hub resets
* and EMF-related port disables.
*/
- if (!(udev->quirks & USB_QUIRK_RESET_MORPHS))
+ if (!(udev->quirks & USB_QUIRK_RESET))
udev->persist_enabled = 1;
#endif /* CONFIG_PM */
}
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 682e8256b95d..818e4a024d0d 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -196,7 +196,7 @@ show_avoid_reset_quirk(struct device *dev, struct device_attribute *attr, char *
struct usb_device *udev;
udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET_MORPHS));
+ return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET));
}
static ssize_t
@@ -204,15 +204,15 @@ set_avoid_reset_quirk(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
- int config;
+ int val;
- if (sscanf(buf, "%d", &config) != 1 || config < 0 || config > 1)
+ if (sscanf(buf, "%d", &val) != 1 || val < 0 || val > 1)
return -EINVAL;
usb_lock_device(udev);
- if (config)
- udev->quirks |= USB_QUIRK_RESET_MORPHS;
+ if (val)
+ udev->quirks |= USB_QUIRK_RESET;
else
- udev->quirks &= ~USB_QUIRK_RESET_MORPHS;
+ udev->quirks &= ~USB_QUIRK_RESET;
usb_unlock_device(udev);
return count;
}
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 8947b203d5a4..0ef7d42d8abe 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -19,20 +19,91 @@
#include "usb.h"
-static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle)
+/**
+ * usb_acpi_power_manageable - check whether usb port has
+ * acpi power resource.
+ * @hdev: USB device belonging to the usb hub
+ * @index: port index based zero
+ *
+ * Return true if the port has acpi power resource and false if no.
+ */
+bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
+{
+ acpi_handle port_handle;
+ int port1 = index + 1;
+
+ port_handle = usb_get_hub_port_acpi_handle(hdev,
+ port1);
+ if (port_handle)
+ return acpi_bus_power_manageable(port_handle);
+ else
+ return false;
+}
+EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
+
+/**
+ * usb_acpi_set_power_state - control usb port's power via acpi power
+ * resource
+ * @hdev: USB device belonging to the usb hub
+ * @index: port index based zero
+ * @enable: power state expected to be set
+ *
+ * Notice to use usb_acpi_power_manageable() to check whether the usb port
+ * has acpi power resource before invoking this function.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
+{
+ acpi_handle port_handle;
+ unsigned char state;
+ int port1 = index + 1;
+ int error = -EINVAL;
+
+ port_handle = (acpi_handle)usb_get_hub_port_acpi_handle(hdev,
+ port1);
+ if (!port_handle)
+ return error;
+
+ if (enable)
+ state = ACPI_STATE_D0;
+ else
+ state = ACPI_STATE_D3_COLD;
+
+ error = acpi_bus_set_power(port_handle, state);
+ if (!error)
+ dev_dbg(&hdev->dev, "The power of hub port %d was set to %d\n",
+ port1, enable);
+ else
+ dev_dbg(&hdev->dev, "The power of hub port failed to be set\n");
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
+
+static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
+ acpi_handle handle, int port1)
{
acpi_status status;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *upc;
+ struct acpi_pld pld;
int ret = 0;
- status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
-
+ /*
+ * Accoding to ACPI Spec 9.13. PLD indicates whether usb port is
+ * user visible and _UPC indicates whether it is connectable. If
+ * the port was visible and connectable, it could be freely connected
+ * and disconnected with USB devices. If no visible and connectable,
+ * a usb device is directly hard-wired to the port. If no visible and
+ * no connectable, the port would be not used.
+ */
+ status = acpi_get_physical_device_location(handle, &pld);
if (ACPI_FAILURE(status))
return -ENODEV;
+ status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
upc = buffer.pointer;
-
if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
|| upc->package.count != 4) {
ret = -EINVAL;
@@ -40,69 +111,107 @@ static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle)
}
if (upc->package.elements[0].integer.value)
- udev->removable = USB_DEVICE_REMOVABLE;
- else
- udev->removable = USB_DEVICE_FIXED;
+ if (pld.user_visible)
+ usb_set_hub_port_connect_type(hdev, port1,
+ USB_PORT_CONNECT_TYPE_HOT_PLUG);
+ else
+ usb_set_hub_port_connect_type(hdev, port1,
+ USB_PORT_CONNECT_TYPE_HARD_WIRED);
+ else if (!pld.user_visible)
+ usb_set_hub_port_connect_type(hdev, port1, USB_PORT_NOT_USED);
out:
kfree(upc);
return ret;
}
-static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle)
-{
- acpi_status status;
- struct acpi_pld pld;
-
- status = acpi_get_physical_device_location(handle, &pld);
-
- if (ACPI_FAILURE(status))
- return -ENODEV;
-
- if (pld.user_visible)
- udev->removable = USB_DEVICE_REMOVABLE;
- else
- udev->removable = USB_DEVICE_FIXED;
-
- return 0;
-}
-
static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
{
struct usb_device *udev;
- struct device *parent;
acpi_handle *parent_handle;
-
- if (!is_usb_device(dev))
- return -ENODEV;
-
- udev = to_usb_device(dev);
- parent = dev->parent;
- parent_handle = DEVICE_ACPI_HANDLE(parent);
-
- if (!parent_handle)
- return -ENODEV;
-
- *handle = acpi_get_child(parent_handle, udev->portnum);
-
- if (!*handle)
- return -ENODEV;
+ int port_num;
/*
- * PLD will tell us whether a port is removable to the user or
- * not. If we don't get an answer from PLD (it's not present
- * or it's malformed) then try to infer it from UPC. If a
- * device isn't connectable then it's probably not removable.
+ * In the ACPI DSDT table, only usb root hub and usb ports are
+ * acpi device nodes. The hierarchy like following.
+ * Device (EHC1)
+ * Device (HUBN)
+ * Device (PR01)
+ * Device (PR11)
+ * Device (PR12)
+ * Device (PR13)
+ * ...
+ * So all binding process is divided into two parts. binding
+ * root hub and usb ports.
*/
- if (usb_acpi_check_pld(udev, *handle) != 0)
- usb_acpi_check_upc(udev, *handle);
+ if (is_usb_device(dev)) {
+ udev = to_usb_device(dev);
+ if (udev->parent) {
+ enum usb_port_connect_type type;
+
+ /*
+ * According usb port's connect type to set usb device's
+ * removability.
+ */
+ type = usb_get_hub_port_connect_type(udev->parent,
+ udev->portnum);
+ switch (type) {
+ case USB_PORT_CONNECT_TYPE_HOT_PLUG:
+ udev->removable = USB_DEVICE_REMOVABLE;
+ break;
+ case USB_PORT_CONNECT_TYPE_HARD_WIRED:
+ udev->removable = USB_DEVICE_FIXED;
+ break;
+ default:
+ udev->removable = USB_DEVICE_REMOVABLE_UNKNOWN;
+ break;
+ }
+
+ return -ENODEV;
+ }
+
+ /* root hub's parent is the usb hcd. */
+ parent_handle = DEVICE_ACPI_HANDLE(dev->parent);
+ *handle = acpi_get_child(parent_handle, udev->portnum);
+ if (!*handle)
+ return -ENODEV;
+ return 0;
+ } else if (is_usb_port(dev)) {
+ sscanf(dev_name(dev), "port%d", &port_num);
+ /* Get the struct usb_device point of port's hub */
+ udev = to_usb_device(dev->parent->parent);
+
+ /*
+ * The root hub ports' parent is the root hub. The non-root-hub
+ * ports' parent is the parent hub port which the hub is
+ * connected to.
+ */
+ if (!udev->parent) {
+ *handle = acpi_get_child(DEVICE_ACPI_HANDLE(&udev->dev),
+ port_num);
+ if (!*handle)
+ return -ENODEV;
+ } else {
+ parent_handle =
+ usb_get_hub_port_acpi_handle(udev->parent,
+ udev->portnum);
+ if (!parent_handle)
+ return -ENODEV;
+
+ *handle = acpi_get_child(parent_handle, port_num);
+ if (!*handle)
+ return -ENODEV;
+ }
+ usb_acpi_check_port_connect_type(udev, *handle, port_num);
+ } else
+ return -ENODEV;
return 0;
}
static struct acpi_bus_type usb_acpi_bus = {
.bus = &usb_bus_type,
- .find_bridge = NULL,
+ .find_bridge = usb_acpi_find_device,
.find_device = usb_acpi_find_device,
};
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index acb103c5c391..1c528c1bf0be 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -1,4 +1,5 @@
#include <linux/pm.h>
+#include <linux/acpi.h>
struct dev_state;
@@ -115,6 +116,7 @@ extern struct bus_type usb_bus_type;
extern struct device_type usb_device_type;
extern struct device_type usb_if_device_type;
extern struct device_type usb_ep_device_type;
+extern struct device_type usb_port_device_type;
extern struct usb_device_driver usb_generic_driver;
static inline int is_usb_device(const struct device *dev)
@@ -132,6 +134,11 @@ static inline int is_usb_endpoint(const struct device *dev)
return dev->type == &usb_ep_device_type;
}
+static inline int is_usb_port(const struct device *dev)
+{
+ return dev->type == &usb_port_device_type;
+}
+
/* Do the same for device drivers and interface drivers. */
static inline int is_usb_device_driver(struct device_driver *drv)
@@ -162,10 +169,16 @@ extern void usb_notify_add_device(struct usb_device *udev);
extern void usb_notify_remove_device(struct usb_device *udev);
extern void usb_notify_add_bus(struct usb_bus *ubus);
extern void usb_notify_remove_bus(struct usb_bus *ubus);
+extern enum usb_port_connect_type
+ usb_get_hub_port_connect_type(struct usb_device *hdev, int port1);
+extern void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
+ enum usb_port_connect_type type);
#ifdef CONFIG_ACPI
extern int usb_acpi_register(void);
extern void usb_acpi_unregister(void);
+extern acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
+ int port1);
#else
static inline int usb_acpi_register(void) { return 0; };
static inline void usb_acpi_unregister(void) { };
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index d13c60f42139..f6a6e070c2ac 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -2,8 +2,6 @@ config USB_DWC3
tristate "DesignWare USB3 DRD Core Support"
depends on (USB && USB_GADGET)
select USB_OTG_UTILS
- select USB_GADGET_DUALSPEED
- select USB_GADGET_SUPERSPEED
select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
help
Say Y or M here if your system has a Dual Role SuperSpeed
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index a68ff53124dc..b415c0c859d3 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -50,6 +50,7 @@
#include <linux/dma-mapping.h>
#include <linux/of.h>
+#include <linux/usb/otg.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -99,6 +100,7 @@ void dwc3_put_device_id(int id)
ret = test_bit(id, dwc3_devs);
WARN(!ret, "dwc3: ID %d not in use\n", id);
+ smp_mb__before_clear_bit();
clear_bit(id, dwc3_devs);
}
EXPORT_SYMBOL_GPL(dwc3_put_device_id);
@@ -136,6 +138,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST;
dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+ usb_phy_init(dwc->usb2_phy);
+ usb_phy_init(dwc->usb3_phy);
mdelay(100);
/* Clear USB3 PHY reset */
@@ -464,12 +468,24 @@ static int __devinit dwc3_probe(struct platform_device *pdev)
return -ENOMEM;
}
- regs = devm_ioremap(dev, res->start, resource_size(res));
+ regs = devm_ioremap_nocache(dev, res->start, resource_size(res));
if (!regs) {
dev_err(dev, "ioremap failed\n");
return -ENOMEM;
}
+ dwc->usb2_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+ if (IS_ERR_OR_NULL(dwc->usb2_phy)) {
+ dev_err(dev, "no usb2 phy configured\n");
+ return -EPROBE_DEFER;
+ }
+
+ dwc->usb3_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB3);
+ if (IS_ERR_OR_NULL(dwc->usb3_phy)) {
+ dev_err(dev, "no usb3 phy configured\n");
+ return -EPROBE_DEFER;
+ }
+
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 151eca876dfd..243affc93431 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -457,7 +457,6 @@ enum dwc3_phy {
enum dwc3_ep0_next {
DWC3_EP0_UNKNOWN = 0,
DWC3_EP0_COMPLETE,
- DWC3_EP0_NRDY_SETUP,
DWC3_EP0_NRDY_DATA,
DWC3_EP0_NRDY_STATUS,
};
@@ -624,6 +623,8 @@ struct dwc3_scratchpad_array {
* @maximum_speed: maximum speed requested (mainly for testing purposes)
* @revision: revision register contents
* @mode: mode of operation
+ * @usb2_phy: pointer to USB2 PHY
+ * @usb3_phy: pointer to USB3 PHY
* @is_selfpowered: true when we are selfpowered
* @three_stage_setup: set if we perform a three phase setup
* @ep0_bounced: true when we used bounce buffer
@@ -667,6 +668,9 @@ struct dwc3 {
struct usb_gadget gadget;
struct usb_gadget_driver *gadget_driver;
+ struct usb_phy *usb2_phy;
+ struct usb_phy *usb3_phy;
+
void __iomem *regs;
size_t regs_size;
@@ -779,7 +783,6 @@ struct dwc3_event_depevt {
#define DEPEVT_STREAMEVT_NOTFOUND 2
/* Control-only Status */
-#define DEPEVT_STATUS_CONTROL_SETUP 0
#define DEPEVT_STATUS_CONTROL_DATA 1
#define DEPEVT_STATUS_CONTROL_STATUS 2
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index b8f00389fa34..ca6597853f90 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -19,16 +19,74 @@
#include <linux/platform_data/dwc3-exynos.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include "core.h"
struct dwc3_exynos {
struct platform_device *dwc3;
+ struct platform_device *usb2_phy;
+ struct platform_device *usb3_phy;
struct device *dev;
struct clk *clk;
};
+static int __devinit dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
+{
+ struct nop_usb_xceiv_platform_data pdata;
+ struct platform_device *pdev;
+ int ret;
+
+ memset(&pdata, 0x00, sizeof(pdata));
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 0);
+ if (!pdev)
+ return -ENOMEM;
+
+ exynos->usb2_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB2;
+
+ ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err1;
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 1);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ exynos->usb3_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB3;
+
+ ret = platform_device_add_data(exynos->usb3_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(exynos->usb2_phy);
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(exynos->usb3_phy);
+ if (ret)
+ goto err3;
+
+ return 0;
+
+err3:
+ platform_device_del(exynos->usb2_phy);
+
+err2:
+ platform_device_put(exynos->usb3_phy);
+
+err1:
+ platform_device_put(exynos->usb2_phy);
+
+ return ret;
+}
+
static int __devinit dwc3_exynos_probe(struct platform_device *pdev)
{
struct dwc3_exynos_data *pdata = pdev->dev.platform_data;
@@ -51,6 +109,12 @@ static int __devinit dwc3_exynos_probe(struct platform_device *pdev)
if (devid < 0)
goto err1;
+ ret = dwc3_exynos_register_phys(exynos);
+ if (ret) {
+ dev_err(&pdev->dev, "couldn't register PHYs\n");
+ goto err1;
+ }
+
dwc3 = platform_device_alloc("dwc3", devid);
if (!dwc3) {
dev_err(&pdev->dev, "couldn't allocate dwc3 device\n");
@@ -120,6 +184,8 @@ static int __devexit dwc3_exynos_remove(struct platform_device *pdev)
struct dwc3_exynos_data *pdata = pdev->dev.platform_data;
platform_device_unregister(exynos->dwc3);
+ platform_device_unregister(exynos->usb2_phy);
+ platform_device_unregister(exynos->usb3_phy);
dwc3_put_device_id(exynos->dwc3->id);
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 479dc047da3a..ee57a10d90d0 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -48,6 +48,9 @@
#include <linux/io.h>
#include <linux/of.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/nop-usb-xceiv.h>
+
#include "core.h"
/*
@@ -131,6 +134,8 @@ struct dwc3_omap {
spinlock_t lock;
struct platform_device *dwc3;
+ struct platform_device *usb2_phy;
+ struct platform_device *usb3_phy;
struct device *dev;
int irq;
@@ -152,6 +157,59 @@ static inline void dwc3_omap_writel(void __iomem *base, u32 offset, u32 value)
writel(value, base + offset);
}
+static int __devinit dwc3_omap_register_phys(struct dwc3_omap *omap)
+{
+ struct nop_usb_xceiv_platform_data pdata;
+ struct platform_device *pdev;
+ int ret;
+
+ memset(&pdata, 0x00, sizeof(pdata));
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 0);
+ if (!pdev)
+ return -ENOMEM;
+
+ omap->usb2_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB2;
+
+ ret = platform_device_add_data(omap->usb2_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err1;
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 1);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ omap->usb3_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB3;
+
+ ret = platform_device_add_data(omap->usb3_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(omap->usb2_phy);
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(omap->usb3_phy);
+ if (ret)
+ goto err3;
+
+ return 0;
+
+err3:
+ platform_device_del(omap->usb2_phy);
+
+err2:
+ platform_device_put(omap->usb3_phy);
+
+err1:
+ platform_device_put(omap->usb2_phy);
+
+ return ret;
+}
static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
{
@@ -251,6 +309,12 @@ static int __devinit dwc3_omap_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ ret = dwc3_omap_register_phys(omap);
+ if (ret) {
+ dev_err(dev, "couldn't register PHYs\n");
+ return ret;
+ }
+
devid = dwc3_get_device_id();
if (devid < 0)
return -ENODEV;
@@ -371,6 +435,8 @@ static int __devexit dwc3_omap_remove(struct platform_device *pdev)
struct dwc3_omap *omap = platform_get_drvdata(pdev);
platform_device_unregister(omap->dwc3);
+ platform_device_unregister(omap->usb2_phy);
+ platform_device_unregister(omap->usb3_phy);
dwc3_put_device_id(omap->dwc3->id);
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index a9ca9adba391..94f550e37f98 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -42,6 +42,9 @@
#include <linux/pci.h>
#include <linux/platform_device.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/nop-usb-xceiv.h>
+
#include "core.h"
/* FIXME define these in <linux/pci_ids.h> */
@@ -51,8 +54,64 @@
struct dwc3_pci {
struct device *dev;
struct platform_device *dwc3;
+ struct platform_device *usb2_phy;
+ struct platform_device *usb3_phy;
};
+static int __devinit dwc3_pci_register_phys(struct dwc3_pci *glue)
+{
+ struct nop_usb_xceiv_platform_data pdata;
+ struct platform_device *pdev;
+ int ret;
+
+ memset(&pdata, 0x00, sizeof(pdata));
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 0);
+ if (!pdev)
+ return -ENOMEM;
+
+ glue->usb2_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB2;
+
+ ret = platform_device_add_data(glue->usb2_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err1;
+
+ pdev = platform_device_alloc("nop_usb_xceiv", 1);
+ if (!pdev) {
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ glue->usb3_phy = pdev;
+ pdata.type = USB_PHY_TYPE_USB3;
+
+ ret = platform_device_add_data(glue->usb3_phy, &pdata, sizeof(pdata));
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(glue->usb2_phy);
+ if (ret)
+ goto err2;
+
+ ret = platform_device_add(glue->usb3_phy);
+ if (ret)
+ goto err3;
+
+ return 0;
+
+err3:
+ platform_device_del(glue->usb2_phy);
+
+err2:
+ platform_device_put(glue->usb3_phy);
+
+err1:
+ platform_device_put(glue->usb2_phy);
+
+ return ret;
+}
+
static int __devinit dwc3_pci_probe(struct pci_dev *pci,
const struct pci_device_id *id)
{
@@ -80,6 +139,12 @@ static int __devinit dwc3_pci_probe(struct pci_dev *pci,
pci_set_power_state(pci, PCI_D0);
pci_set_master(pci);
+ ret = dwc3_pci_register_phys(glue);
+ if (ret) {
+ dev_err(dev, "couldn't register PHYs\n");
+ return ret;
+ }
+
devid = dwc3_get_device_id();
if (devid < 0) {
ret = -ENOMEM;
@@ -144,6 +209,8 @@ static void __devexit dwc3_pci_remove(struct pci_dev *pci)
{
struct dwc3_pci *glue = pci_get_drvdata(pci);
+ platform_device_unregister(glue->usb2_phy);
+ platform_device_unregister(glue->usb3_phy);
dwc3_put_device_id(glue->dwc3->id);
platform_device_unregister(glue->dwc3);
pci_set_drvdata(pci, NULL);
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index e4d5ca86b9da..d7da073a23fe 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -125,7 +125,6 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
struct dwc3_request *req)
{
struct dwc3 *dwc = dep->dwc;
- int ret = 0;
req->request.actual = 0;
req->request.status = -EINPROGRESS;
@@ -156,16 +155,72 @@ static int __dwc3_gadget_ep0_queue(struct dwc3_ep *dep,
dep->flags &= ~(DWC3_EP_PENDING_REQUEST |
DWC3_EP0_DIR_IN);
- } else if (dwc->delayed_status) {
+
+ return 0;
+ }
+
+ /*
+ * In case gadget driver asked us to delay the STATUS phase,
+ * handle it here.
+ */
+ if (dwc->delayed_status) {
+ unsigned direction;
+
+ direction = !dwc->ep0_expect_in;
dwc->delayed_status = false;
if (dwc->ep0state == EP0_STATUS_PHASE)
- __dwc3_ep0_do_control_status(dwc, dwc->eps[1]);
+ __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
else
dev_dbg(dwc->dev, "too early for delayed status\n");
+
+ return 0;
}
- return ret;
+ /*
+ * Unfortunately we have uncovered a limitation wrt the Data Phase.
+ *
+ * Section 9.4 says we can wait for the XferNotReady(DATA) event to
+ * come before issueing Start Transfer command, but if we do, we will
+ * miss situations where the host starts another SETUP phase instead of
+ * the DATA phase. Such cases happen at least on TD.7.6 of the Link
+ * Layer Compliance Suite.
+ *
+ * The problem surfaces due to the fact that in case of back-to-back
+ * SETUP packets there will be no XferNotReady(DATA) generated and we
+ * will be stuck waiting for XferNotReady(DATA) forever.
+ *
+ * By looking at tables 9-13 and 9-14 of the Databook, we can see that
+ * it tells us to start Data Phase right away. It also mentions that if
+ * we receive a SETUP phase instead of the DATA phase, core will issue
+ * XferComplete for the DATA phase, before actually initiating it in
+ * the wire, with the TRB's status set to "SETUP_PENDING". Such status
+ * can only be used to print some debugging logs, as the core expects
+ * us to go through to the STATUS phase and start a CONTROL_STATUS TRB,
+ * just so it completes right away, without transferring anything and,
+ * only then, we can go back to the SETUP phase.
+ *
+ * Because of this scenario, SNPS decided to change the programming
+ * model of control transfers and support on-demand transfers only for
+ * the STATUS phase. To fix the issue we have now, we will always wait
+ * for gadget driver to queue the DATA phase's struct usb_request, then
+ * start it right away.
+ *
+ * If we're actually in a 2-stage transfer, we will wait for
+ * XferNotReady(STATUS).
+ */
+ if (dwc->three_stage_setup) {
+ unsigned direction;
+
+ direction = dwc->ep0_expect_in;
+ dwc->ep0state = EP0_DATA_PHASE;
+
+ __dwc3_ep0_do_control_data(dwc, dwc->eps[direction], req);
+
+ dep->flags &= ~DWC3_EP0_DIR_IN;
+ }
+
+ return 0;
}
int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
@@ -207,9 +262,14 @@ out:
static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
{
- struct dwc3_ep *dep = dwc->eps[0];
+ struct dwc3_ep *dep;
+
+ /* reinitialize physical ep1 */
+ dep = dwc->eps[1];
+ dep->flags = DWC3_EP_ENABLED;
/* stall is always issued on EP0 */
+ dep = dwc->eps[0];
__dwc3_gadget_ep_set_halt(dep, 1);
dep->flags = DWC3_EP_ENABLED;
dwc->delayed_status = false;
@@ -698,6 +758,7 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
struct dwc3_trb *trb;
struct dwc3_ep *ep0;
u32 transferred;
+ u32 status;
u32 length;
u8 epnum;
@@ -710,6 +771,17 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
ur = &r->request;
trb = dwc->ep0_trb;
+
+ status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+ if (status == DWC3_TRBSTS_SETUP_PENDING) {
+ dev_dbg(dwc->dev, "Setup Pending received\n");
+
+ if (r)
+ dwc3_gadget_giveback(ep0, r, -ECONNRESET);
+
+ return;
+ }
+
length = trb->size & DWC3_TRB_SIZE_MASK;
if (dwc->ep0_bounced) {
@@ -745,8 +817,11 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
{
struct dwc3_request *r;
struct dwc3_ep *dep;
+ struct dwc3_trb *trb;
+ u32 status;
dep = dwc->eps[0];
+ trb = dwc->ep0_trb;
if (!list_empty(&dep->request_list)) {
r = next_request(&dep->request_list);
@@ -766,6 +841,10 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
}
}
+ status = DWC3_TRB_SIZE_TRBSTS(trb->size);
+ if (status == DWC3_TRBSTS_SETUP_PENDING)
+ dev_dbg(dwc->dev, "Setup Pending received\n");
+
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
}
@@ -799,12 +878,6 @@ static void dwc3_ep0_xfer_complete(struct dwc3 *dwc,
}
}
-static void dwc3_ep0_do_control_setup(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
-{
- dwc3_ep0_out_start(dwc);
-}
-
static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
struct dwc3_ep *dep, struct dwc3_request *req)
{
@@ -857,29 +930,6 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc,
WARN_ON(ret < 0);
}
-static void dwc3_ep0_do_control_data(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
-{
- struct dwc3_ep *dep;
- struct dwc3_request *req;
-
- dep = dwc->eps[0];
-
- if (list_empty(&dep->request_list)) {
- dev_vdbg(dwc->dev, "pending request for EP0 Data phase\n");
- dep->flags |= DWC3_EP_PENDING_REQUEST;
-
- if (event->endpoint_number)
- dep->flags |= DWC3_EP0_DIR_IN;
- return;
- }
-
- req = next_request(&dep->request_list);
- dep = dwc->eps[event->endpoint_number];
-
- __dwc3_ep0_do_control_data(dwc, dep, req);
-}
-
static int dwc3_ep0_start_control_status(struct dwc3_ep *dep)
{
struct dwc3 *dwc = dep->dwc;
@@ -911,100 +961,61 @@ static void dwc3_ep0_do_control_status(struct dwc3 *dwc,
__dwc3_ep0_do_control_status(dwc, dep);
}
-static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
- const struct dwc3_event_depevt *event)
+static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
{
- dwc->setup_packet_pending = true;
-
- /*
- * This part is very tricky: If we have just handled
- * XferNotReady(Setup) and we're now expecting a
- * XferComplete but, instead, we receive another
- * XferNotReady(Setup), we should STALL and restart
- * the state machine.
- *
- * In all other cases, we just continue waiting
- * for the XferComplete event.
- *
- * We are a little bit unsafe here because we're
- * not trying to ensure that last event was, indeed,
- * XferNotReady(Setup).
- *
- * Still, we don't expect any condition where that
- * should happen and, even if it does, it would be
- * another error condition.
- */
- if (dwc->ep0_next_event == DWC3_EP0_COMPLETE) {
- switch (event->status) {
- case DEPEVT_STATUS_CONTROL_SETUP:
- dev_vdbg(dwc->dev, "Unexpected XferNotReady(Setup)\n");
- dwc3_ep0_stall_and_restart(dwc);
- break;
- case DEPEVT_STATUS_CONTROL_DATA:
- /* FALLTHROUGH */
- case DEPEVT_STATUS_CONTROL_STATUS:
- /* FALLTHROUGH */
- default:
- dev_vdbg(dwc->dev, "waiting for XferComplete\n");
- }
+ struct dwc3_gadget_ep_cmd_params params;
+ u32 cmd;
+ int ret;
+ if (!dep->resource_index)
return;
- }
-
- switch (event->status) {
- case DEPEVT_STATUS_CONTROL_SETUP:
- dev_vdbg(dwc->dev, "Control Setup\n");
- dwc->ep0state = EP0_SETUP_PHASE;
+ cmd = DWC3_DEPCMD_ENDTRANSFER;
+ cmd |= DWC3_DEPCMD_CMDIOC;
+ cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
+ memset(&params, 0, sizeof(params));
+ ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
+ WARN_ON_ONCE(ret);
+ dep->resource_index = 0;
+}
- dwc3_ep0_do_control_setup(dwc, event);
- break;
+static void dwc3_ep0_xfernotready(struct dwc3 *dwc,
+ const struct dwc3_event_depevt *event)
+{
+ dwc->setup_packet_pending = true;
+ switch (event->status) {
case DEPEVT_STATUS_CONTROL_DATA:
dev_vdbg(dwc->dev, "Control Data\n");
- dwc->ep0state = EP0_DATA_PHASE;
-
- if (dwc->ep0_next_event != DWC3_EP0_NRDY_DATA) {
- dev_vdbg(dwc->dev, "Expected %d got %d\n",
- dwc->ep0_next_event,
- DWC3_EP0_NRDY_DATA);
-
- dwc3_ep0_stall_and_restart(dwc);
- return;
- }
-
/*
- * One of the possible error cases is when Host _does_
- * request for Data Phase, but it does so on the wrong
- * direction.
+ * We already have a DATA transfer in the controller's cache,
+ * if we receive a XferNotReady(DATA) we will ignore it, unless
+ * it's for the wrong direction.
*
- * Here, we already know ep0_next_event is DATA (see above),
- * so we only need to check for direction.
+ * In that case, we must issue END_TRANSFER command to the Data
+ * Phase we already have started and issue SetStall on the
+ * control endpoint.
*/
if (dwc->ep0_expect_in != event->endpoint_number) {
+ struct dwc3_ep *dep = dwc->eps[dwc->ep0_expect_in];
+
dev_vdbg(dwc->dev, "Wrong direction for Data phase\n");
+ dwc3_ep0_end_control_data(dwc, dep);
dwc3_ep0_stall_and_restart(dwc);
return;
}
- dwc3_ep0_do_control_data(dwc, event);
break;
case DEPEVT_STATUS_CONTROL_STATUS:
+ if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS)
+ return;
+
dev_vdbg(dwc->dev, "Control Status\n");
dwc->ep0state = EP0_STATUS_PHASE;
- if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) {
- dev_vdbg(dwc->dev, "Expected %d got %d\n",
- dwc->ep0_next_event,
- DWC3_EP0_NRDY_STATUS);
-
- dwc3_ep0_stall_and_restart(dwc);
- return;
- }
-
if (dwc->delayed_status) {
WARN_ON_ONCE(event->endpoint_number != 1);
dev_vdbg(dwc->dev, "Mass Storage delayed status\n");
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index c2813c2b005a..c9e729a4bf65 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -434,15 +434,25 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
const struct usb_endpoint_descriptor *desc,
- const struct usb_ss_ep_comp_descriptor *comp_desc)
+ const struct usb_ss_ep_comp_descriptor *comp_desc,
+ bool ignore)
{
struct dwc3_gadget_ep_cmd_params params;
memset(&params, 0x00, sizeof(params));
params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc))
- | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc))
- | DWC3_DEPCFG_BURST_SIZE(dep->endpoint.maxburst - 1);
+ | DWC3_DEPCFG_MAX_PACKET_SIZE(usb_endpoint_maxp(desc));
+
+ /* Burst size is only needed in SuperSpeed mode */
+ if (dwc->gadget.speed == USB_SPEED_SUPER) {
+ u32 burst = dep->endpoint.maxburst - 1;
+
+ params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst);
+ }
+
+ if (ignore)
+ params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
| DWC3_DEPCFG_XFER_NOT_READY_EN;
@@ -501,7 +511,8 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
*/
static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
const struct usb_endpoint_descriptor *desc,
- const struct usb_ss_ep_comp_descriptor *comp_desc)
+ const struct usb_ss_ep_comp_descriptor *comp_desc,
+ bool ignore)
{
struct dwc3 *dwc = dep->dwc;
u32 reg;
@@ -513,7 +524,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
return ret;
}
- ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc);
+ ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore);
if (ret)
return ret;
@@ -561,27 +572,7 @@ static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
if (!list_empty(&dep->req_queued)) {
dwc3_stop_active_transfer(dwc, dep->number);
- /*
- * NOTICE: We are violating what the Databook says about the
- * EndTransfer command. Ideally we would _always_ wait for the
- * EndTransfer Command Completion IRQ, but that's causing too
- * much trouble synchronizing between us and gadget driver.
- *
- * We have discussed this with the IP Provider and it was
- * suggested to giveback all requests here, but give HW some
- * extra time to synchronize with the interconnect. We're using
- * an arbitraty 100us delay for that.
- *
- * Note also that a similar handling was tested by Synopsys
- * (thanks a lot Paul) and nothing bad has come out of it.
- * In short, what we're doing is:
- *
- * - Issue EndTransfer WITH CMDIOC bit set
- * - Wait 100us
- * - giveback all requests to gadget driver
- */
- udelay(100);
-
+ /* - giveback all requests to gadget driver */
while (!list_empty(&dep->req_queued)) {
req = next_request(&dep->req_queued);
@@ -660,6 +651,12 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
dep = to_dwc3_ep(ep);
dwc = dep->dwc;
+ if (dep->flags & DWC3_EP_ENABLED) {
+ dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
+ dep->name);
+ return 0;
+ }
+
switch (usb_endpoint_type(desc)) {
case USB_ENDPOINT_XFER_CONTROL:
strlcat(dep->name, "-control", sizeof(dep->name));
@@ -677,16 +674,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
dev_err(dwc->dev, "invalid endpoint transfer type\n");
}
- if (dep->flags & DWC3_EP_ENABLED) {
- dev_WARN_ONCE(dwc->dev, true, "%s is already enabled\n",
- dep->name);
- return 0;
- }
-
dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
spin_lock_irqsave(&dwc->lock, flags);
- ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc);
+ ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false);
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1105,12 +1096,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
}
ret = __dwc3_gadget_kick_transfer(dep, 0, true);
- if (ret && ret != -EBUSY) {
- struct dwc3 *dwc = dep->dwc;
-
+ if (ret && ret != -EBUSY)
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
- }
}
/*
@@ -1119,16 +1107,14 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
* core may not see the modified TRB(s).
*/
if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
- (dep->flags & DWC3_EP_BUSY)) {
+ (dep->flags & DWC3_EP_BUSY) &&
+ !(dep->flags & DWC3_EP_MISSED_ISOC)) {
WARN_ON_ONCE(!dep->resource_index);
ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index,
false);
- if (ret && ret != -EBUSY) {
- struct dwc3 *dwc = dep->dwc;
-
+ if (ret && ret != -EBUSY)
dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
dep->name);
- }
}
/*
@@ -1533,14 +1519,14 @@ static int dwc3_gadget_start(struct usb_gadget *g,
dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err0;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
goto err1;
@@ -1765,7 +1751,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
int i;
for (i = 0; i < DWC3_ENDPOINTS_NUM; i++) {
- struct dwc3_ep *dep = dwc->eps[i];
+ dep = dwc->eps[i];
if (!(dep->flags & DWC3_EP_ENABLED))
continue;
@@ -1892,6 +1878,25 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
if (!dep->resource_index)
return;
+ /*
+ * NOTICE: We are violating what the Databook says about the
+ * EndTransfer command. Ideally we would _always_ wait for the
+ * EndTransfer Command Completion IRQ, but that's causing too
+ * much trouble synchronizing between us and gadget driver.
+ *
+ * We have discussed this with the IP Provider and it was
+ * suggested to giveback all requests here, but give HW some
+ * extra time to synchronize with the interconnect. We're using
+ * an arbitraty 100us delay for that.
+ *
+ * Note also that a similar handling was tested by Synopsys
+ * (thanks a lot Paul) and nothing bad has come out of it.
+ * In short, what we're doing is:
+ *
+ * - Issue EndTransfer WITH CMDIOC bit set
+ * - Wait 100us
+ */
+
cmd = DWC3_DEPCMD_ENDTRANSFER;
cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
@@ -1899,6 +1904,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
WARN_ON_ONCE(ret);
dep->resource_index = 0;
+
+ udelay(100);
}
static void dwc3_stop_active_transfers(struct dwc3 *dwc)
@@ -2156,14 +2163,14 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
}
dep = dwc->eps[0];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
}
dep = dwc->eps[1];
- ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL);
+ ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
if (ret) {
dev_err(dwc->dev, "failed to enable %s\n", dep->name);
return;
diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c
index 89dcf155d57e..e426ad626d74 100644
--- a/drivers/usb/early/ehci-dbgp.c
+++ b/drivers/usb/early/ehci-dbgp.c
@@ -491,7 +491,7 @@ static int ehci_wait_for_port(int port);
* Return -ENODEV for any general failure
* Return -EIO if wait for port fails
*/
-int dbgp_external_startup(void)
+static int _dbgp_external_startup(void)
{
int devnum;
struct usb_debug_descriptor dbgp_desc;
@@ -613,6 +613,11 @@ err:
goto try_again;
return -ENODEV;
}
+
+int dbgp_external_startup(struct usb_hcd *hcd)
+{
+ return xen_dbgp_external_startup(hcd) ?: _dbgp_external_startup();
+}
EXPORT_SYMBOL_GPL(dbgp_external_startup);
static int ehci_reset_port(int port)
@@ -804,7 +809,7 @@ try_next_port:
dbgp_ehci_status("ehci skip - already configured");
}
- ret = dbgp_external_startup();
+ ret = _dbgp_external_startup();
if (ret == -EIO)
goto next_debug_port;
@@ -934,7 +939,7 @@ static void early_dbgp_write(struct console *con, const char *str, u32 n)
ctrl = readl(&ehci_debug->control);
if (!(ctrl & DBGP_ENABLED)) {
dbgp_not_safe = 1;
- dbgp_external_startup();
+ _dbgp_external_startup();
} else {
cmd |= CMD_RUN;
writel(cmd, &ehci_regs->command);
@@ -974,10 +979,14 @@ struct console early_dbgp_console = {
.index = -1,
};
-int dbgp_reset_prep(void)
+int dbgp_reset_prep(struct usb_hcd *hcd)
{
+ int ret = xen_dbgp_reset_prep(hcd);
u32 ctrl;
+ if (ret)
+ return ret;
+
dbgp_not_safe = 1;
if (!ehci_debug)
return 0;
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 51ab5fd5d468..dfb51a45496c 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -154,16 +154,25 @@ config USB_LPC32XX
config USB_ATMEL_USBA
tristate "Atmel USBA"
- select USB_GADGET_DUALSPEED
depends on AVR32 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
USBA is the integrated high-speed USB Device controller on
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
+config USB_BCM63XX_UDC
+ tristate "Broadcom BCM63xx Peripheral Controller"
+ depends on BCM63XX
+ help
+ Many Broadcom BCM63xx chipsets (such as the BCM6328) have a
+ high speed USB Device Port with support for four fixed endpoints
+ (plus endpoint zero).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "bcm63xx_udc".
+
config USB_FSL_USB2
tristate "Freescale Highspeed USB DR Peripheral Controller"
depends on FSL_SOC || ARCH_MXC
- select USB_GADGET_DUALSPEED
select USB_FSL_MPH_DR_OF if OF
help
Some of Freescale PowerPC and i.MX processors have a High Speed
@@ -179,7 +188,6 @@ config USB_FSL_USB2
config USB_FUSB300
tristate "Faraday FUSB300 USB Peripheral Controller"
depends on !PHYS_ADDR_T_64BIT
- select USB_GADGET_DUALSPEED
help
Faraday usb device controller FUSB300 driver
@@ -227,7 +235,6 @@ config USB_PXA25X_SMALL
config USB_R8A66597
tristate "Renesas R8A66597 USB Peripheral Controller"
- select USB_GADGET_DUALSPEED
help
R8A66597 is a discrete USB host and peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
@@ -240,7 +247,6 @@ config USB_R8A66597
config USB_RENESAS_USBHS_UDC
tristate 'Renesas USBHS controller'
depends on USB_RENESAS_USBHS
- select USB_GADGET_DUALSPEED
help
Renesas USBHS is a discrete USB host and peripheral controller chip
that supports both full and high speed USB 2.0 data transfers.
@@ -268,7 +274,6 @@ config USB_PXA27X
config USB_S3C_HSOTG
tristate "S3C HS/OtG USB Device controller"
depends on S3C_DEV_USB_HSOTG
- select USB_GADGET_DUALSPEED
help
The Samsung S3C64XX USB2.0 high-speed gadget controller
integrated into the S3C64XX series SoC.
@@ -305,7 +310,6 @@ config USB_S3C2410_DEBUG
config USB_S3C_HSUDC
tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
depends on ARCH_S3C24XX
- select USB_GADGET_DUALSPEED
help
Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
integrated with dual speed USB 2.0 device controller. It has
@@ -315,7 +319,6 @@ config USB_S3C_HSUDC
config USB_MV_UDC
tristate "Marvell USB2.0 Device Controller"
- select USB_GADGET_DUALSPEED
help
Marvell Socs (including PXA and MMP series) include a high speed
USB2.0 OTG controller, which can be configured as high speed or
@@ -338,14 +341,12 @@ config USB_MV_U3D
config USB_GADGET_MUSB_HDRC
tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)"
depends on USB_MUSB_HDRC
- select USB_GADGET_DUALSPEED
help
This OTG-capable silicon IP is used in dual designs including
the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
config USB_M66592
tristate "Renesas M66592 USB Peripheral Controller"
- select USB_GADGET_DUALSPEED
help
M66592 is a discrete USB peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
@@ -362,7 +363,6 @@ config USB_M66592
config USB_AMD5536UDC
tristate "AMD5536 UDC"
depends on PCI
- select USB_GADGET_DUALSPEED
help
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
It is a USB Highspeed DMA capable USB device controller. Beside ep0
@@ -389,7 +389,6 @@ config USB_FSL_QE
config USB_NET2272
tristate "PLX NET2272"
- select USB_GADGET_DUALSPEED
help
PLX NET2272 is a USB peripheral controller which supports
both full and high speed USB 2.0 data transfers.
@@ -413,7 +412,6 @@ config USB_NET2272_DMA
config USB_NET2280
tristate "NetChip 228x"
depends on PCI
- select USB_GADGET_DUALSPEED
help
NetChip 2280 / 2282 is a PCI based USB peripheral controller which
supports both full and high speed USB 2.0 data transfers.
@@ -443,7 +441,6 @@ config USB_GOKU
config USB_EG20T
tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
depends on PCI
- select USB_GADGET_DUALSPEED
help
This is a USB device driver for EG20T PCH.
EG20T PCH is the platform controller hub that is used in Intel's
@@ -470,8 +467,6 @@ config USB_EG20T
config USB_DUMMY_HCD
tristate "Dummy HCD (DEVELOPMENT)"
depends on USB=y || (USB=m && USB_GADGET=m)
- select USB_GADGET_DUALSPEED
- select USB_GADGET_SUPERSPEED
help
This host controller driver emulates USB, looping all data transfer
requests back to a USB "gadget driver" in the same host. The host
@@ -496,18 +491,15 @@ config USB_DUMMY_HCD
endmenu
-# Selected by UDC drivers that support high-speed operation.
-config USB_GADGET_DUALSPEED
- bool
-
-# Selected by UDC drivers that support super-speed opperation
-config USB_GADGET_SUPERSPEED
- bool
- depends on USB_GADGET_DUALSPEED
-
#
# USB Gadget Drivers
#
+
+# composite based drivers
+config USB_LIBCOMPOSITE
+ tristate
+ depends on USB_GADGET
+
choice
tristate "USB Gadget Drivers"
default USB_ETH
@@ -531,6 +523,7 @@ choice
config USB_ZERO
tristate "Gadget Zero (DEVELOPMENT)"
+ select USB_LIBCOMPOSITE
help
Gadget Zero is a two-configuration device. It either sinks and
sources bulk data; or it loops back a configurable number of
@@ -564,8 +557,9 @@ config USB_ZERO_HNPTEST
one serve as the USB host instead (in the "B-Host" role).
config USB_AUDIO
- tristate "Audio Gadget (EXPERIMENTAL)"
+ tristate "Audio Gadget"
depends on SND
+ select USB_LIBCOMPOSITE
select SND_PCM
help
This Gadget Audio driver is compatible with USB Audio Class
@@ -594,6 +588,7 @@ config GADGET_UAC1
config USB_ETH
tristate "Ethernet Gadget (with CDC Ethernet support)"
depends on NET
+ select USB_LIBCOMPOSITE
select CRC32
help
This driver implements Ethernet style communication, in one of
@@ -629,6 +624,7 @@ config USB_ETH
config USB_ETH_RNDIS
bool "RNDIS support"
depends on USB_ETH
+ select USB_LIBCOMPOSITE
default y
help
Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
@@ -647,6 +643,7 @@ config USB_ETH_RNDIS
config USB_ETH_EEM
bool "Ethernet Emulation Model (EEM) support"
depends on USB_ETH
+ select USB_LIBCOMPOSITE
default n
help
CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
@@ -663,6 +660,7 @@ config USB_ETH_EEM
config USB_G_NCM
tristate "Network Control Model (NCM) support"
depends on NET
+ select USB_LIBCOMPOSITE
select CRC32
help
This driver implements USB CDC NCM subclass standard. NCM is
@@ -674,8 +672,7 @@ config USB_G_NCM
dynamically linked module called "g_ncm".
config USB_GADGETFS
- tristate "Gadget Filesystem (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ tristate "Gadget Filesystem"
help
This driver provides a filesystem based API that lets user mode
programs implement a single-configuration USB device, including
@@ -683,15 +680,12 @@ config USB_GADGETFS
All endpoints, transfer speeds, and transfer types supported by
the hardware are available, through read() and write() calls.
- Currently, this option is still labelled as EXPERIMENTAL because
- of existing race conditions in the underlying in-kernel AIO core.
-
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "gadgetfs".
config USB_FUNCTIONFS
- tristate "Function Filesystem (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ tristate "Function Filesystem"
+ select USB_LIBCOMPOSITE
select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
help
The Function Filesystem (FunctionFS) lets one create USB
@@ -755,6 +749,7 @@ config USB_FILE_STORAGE_TEST
config USB_MASS_STORAGE
tristate "Mass Storage Gadget"
depends on BLOCK
+ select USB_LIBCOMPOSITE
help
The Mass Storage Gadget acts as a USB Mass Storage disk drive.
As its storage repository it can use a regular file or a block
@@ -770,6 +765,7 @@ config USB_MASS_STORAGE
config USB_GADGET_TARGET
tristate "USB Gadget Target Fabric Module"
depends on TARGET_CORE
+ select USB_LIBCOMPOSITE
help
This fabric is an USB gadget. Two USB protocols are supported that is
BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
@@ -779,6 +775,7 @@ config USB_GADGET_TARGET
config USB_G_SERIAL
tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
+ select USB_LIBCOMPOSITE
help
The Serial Gadget talks to the Linux-USB generic serial driver.
This driver supports a CDC-ACM module option, which can be used
@@ -797,8 +794,9 @@ config USB_G_SERIAL
make MS-Windows work with CDC ACM.
config USB_MIDI_GADGET
- tristate "MIDI Gadget (EXPERIMENTAL)"
- depends on SND && EXPERIMENTAL
+ tristate "MIDI Gadget"
+ depends on SND
+ select USB_LIBCOMPOSITE
select SND_RAWMIDI
help
The MIDI Gadget acts as a USB Audio device, with one MIDI
@@ -812,6 +810,7 @@ config USB_MIDI_GADGET
config USB_G_PRINTER
tristate "Printer Gadget"
+ select USB_LIBCOMPOSITE
help
The Printer Gadget channels data between the USB host and a
userspace program driving the print engine. The user space
@@ -828,6 +827,7 @@ config USB_G_PRINTER
config USB_CDC_COMPOSITE
tristate "CDC Composite Device (Ethernet and ACM)"
depends on NET
+ select USB_LIBCOMPOSITE
help
This driver provides two functions in one configuration:
a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
@@ -842,6 +842,7 @@ config USB_CDC_COMPOSITE
config USB_G_NOKIA
tristate "Nokia composite gadget"
depends on PHONET
+ select USB_LIBCOMPOSITE
help
The Nokia composite gadget provides support for acm, obex
and phonet in only one composite gadget driver.
@@ -852,6 +853,7 @@ config USB_G_NOKIA
config USB_G_ACM_MS
tristate "CDC Composite Device (ACM and mass storage)"
depends on BLOCK
+ select USB_LIBCOMPOSITE
help
This driver provides two functions in one configuration:
a mass storage, and a CDC ACM (serial port) link.
@@ -860,9 +862,10 @@ config USB_G_ACM_MS
dynamically linked module called "g_acm_ms".
config USB_G_MULTI
- tristate "Multifunction Composite Gadget (EXPERIMENTAL)"
+ tristate "Multifunction Composite Gadget"
depends on BLOCK && NET
select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
+ select USB_LIBCOMPOSITE
help
The Multifunction Composite Gadget provides Ethernet (RNDIS
and/or CDC Ethernet), mass storage and ACM serial link
@@ -903,6 +906,7 @@ config USB_G_MULTI_CDC
config USB_G_HID
tristate "HID Gadget"
+ select USB_LIBCOMPOSITE
help
The HID gadget driver provides generic emulation of USB
Human Interface Devices (HID).
@@ -913,8 +917,10 @@ config USB_G_HID
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_hid".
+# Standalone / single function gadgets
config USB_G_DBGP
tristate "EHCI Debug Device Gadget"
+ select USB_LIBCOMPOSITE
help
This gadget emulates an EHCI Debug device. This is useful when you want
to interact with an EHCI Debug Port.
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 3fd8cd09d2c1..307be5fa824c 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -4,6 +4,9 @@
ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
obj-$(CONFIG_USB_GADGET) += udc-core.o
+obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
+libcomposite-y := usbstring.o config.o epautoconf.o
+libcomposite-y += composite.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2272) += net2272.o
obj-$(CONFIG_USB_NET2280) += net2280.o
@@ -16,6 +19,7 @@ obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
obj-$(CONFIG_USB_AT91) += at91_udc.o
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
+obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
fsl_usb2_udc-y := fsl_udc_core.o
fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c
index 75b8a691fd00..5a7f289805ff 100644
--- a/drivers/usb/gadget/acm_ms.c
+++ b/drivers/usb/gadget/acm_ms.c
@@ -15,7 +15,7 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
+#include <linux/module.h>
#include "u_serial.h"
@@ -41,15 +41,12 @@
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
#include "u_serial.c"
#include "f_acm.c"
#include "f_mass_storage.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
@@ -89,17 +86,11 @@ static const struct usb_descriptor_header *otg_desc[] = {
NULL,
};
-
/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -157,7 +148,6 @@ static struct usb_configuration acm_ms_config_driver = {
static int __init acm_ms_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
void *retp;
@@ -174,44 +164,22 @@ static int __init acm_ms_bind(struct usb_composite_dev *cdev)
goto fail0;
}
- /* set bcdDevice */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0) {
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- } else {
- WARNING(cdev, "controller '%s' not recognized; trying %s\n",
- gadget->name,
- acm_ms_config_driver.label);
- device_desc.bcdDevice =
- cpu_to_le16(0x0300 | 0x0099);
- }
-
/*
* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
-
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail1;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail1;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
if (status < 0)
goto fail1;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
DRIVER_DESC);
fsg_common_put(&fsg_common);
@@ -232,11 +200,12 @@ static int __exit acm_ms_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static struct usb_composite_driver acm_ms_driver = {
+static __refdata struct usb_composite_driver acm_ms_driver = {
.name = "g_acm_ms",
.dev = &device_desc,
.max_speed = USB_SPEED_SUPER,
.strings = dev_strings,
+ .bind = acm_ms_bind,
.unbind = __exit_p(acm_ms_unbind),
};
@@ -246,7 +215,7 @@ MODULE_LICENSE("GPL v2");
static int __init init(void)
{
- return usb_composite_probe(&acm_ms_driver, acm_ms_bind);
+ return usb_composite_probe(&acm_ms_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index 187d21181cd5..fc0ec5e0d58e 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/amd5536udc.c
@@ -1401,7 +1401,7 @@ static int udc_wakeup(struct usb_gadget *gadget)
}
static int amd5536_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int amd5536_stop(struct usb_gadget_driver *driver);
/* gadget operations */
static const struct usb_gadget_ops udc_ops = {
@@ -1914,7 +1914,7 @@ static int setup_ep0(struct udc *dev)
/* Called by gadget driver to register itself */
static int amd5536_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct udc *dev = udc;
int retval;
@@ -1932,7 +1932,7 @@ static int amd5536_start(struct usb_gadget_driver *driver,
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
- retval = bind(&dev->gadget);
+ retval = bind(&dev->gadget, driver);
/* Some gadget drivers use both ep0 directions.
* NOTE: to gadget driver, ep0 is just one endpoint...
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index 1e35963bd4ed..89d90b5fb787 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -469,7 +469,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
const struct usb_endpoint_descriptor *desc)
{
struct at91_ep *ep = container_of(_ep, struct at91_ep, ep);
- struct at91_udc *udc = ep->udc;
+ struct at91_udc *udc;
u16 maxpacket;
u32 tmp;
unsigned long flags;
@@ -483,6 +483,7 @@ static int at91_ep_enable(struct usb_ep *_ep,
return -EINVAL;
}
+ udc = ep->udc;
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
DBG("bogus device state\n");
return -ESHUTDOWN;
@@ -1699,7 +1700,7 @@ static int __devinit at91udc_probe(struct platform_device *pdev)
int retval;
struct resource *res;
- if (!dev->platform_data) {
+ if (!dev->platform_data && !pdev->dev.of_node) {
/* small (so we copy it) but critical! */
DBG("missing platform_data\n");
return -ENODEV;
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c
index 98899244860e..231b0efe8fdc 100644
--- a/drivers/usb/gadget/audio.c
+++ b/drivers/usb/gadget/audio.c
@@ -12,35 +12,21 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
-#include <linux/utsname.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+#include "gadget_chips.h"
#define DRIVER_DESC "Linux USB Audio Gadget"
#define DRIVER_VERSION "Feb 2, 2012"
-/*-------------------------------------------------------------------------*/
-
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
+USB_GADGET_COMPOSITE_OPTIONS();
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -149,39 +135,18 @@ static struct usb_configuration audio_config_driver = {
static int __init audio_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
int status;
- gcnum = usb_gadget_controller_number(cdev->gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else {
- ERROR(cdev, "controller '%s' not recognized; trying %s\n",
- cdev->gadget->name,
- audio_config_driver.label);
- device_desc.bcdDevice =
- __constant_cpu_to_le16(0x0300 | 0x0099);
- }
-
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- cdev->gadget->name);
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
status = usb_add_config(cdev, &audio_config_driver, audio_do_config);
if (status < 0)
goto fail;
+ usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
return 0;
@@ -198,17 +163,18 @@ static int __exit audio_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static struct usb_composite_driver audio_driver = {
+static __refdata struct usb_composite_driver audio_driver = {
.name = "g_audio",
.dev = &device_desc,
.strings = audio_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = audio_bind,
.unbind = __exit_p(audio_unbind),
};
static int __init init(void)
{
- return usb_composite_probe(&audio_driver, audio_bind);
+ return usb_composite_probe(&audio_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c
new file mode 100644
index 000000000000..9ca792224cd4
--- /dev/null
+++ b/drivers/usb/gadget/bcm63xx_udc.c
@@ -0,0 +1,2464 @@
+/*
+ * bcm63xx_udc.c -- BCM63xx UDC high/full speed USB device controller
+ *
+ * Copyright (C) 2012 Kevin Cernekee <cernekee@gmail.com>
+ * Copyright (C) 2012 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/bitops.h>
+#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/compiler.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kconfig.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_iudma.h>
+#include <bcm63xx_dev_usb_usbd.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+
+#define DRV_MODULE_NAME "bcm63xx_udc"
+
+static const char bcm63xx_ep0name[] = "ep0";
+static const char *const bcm63xx_ep_name[] = {
+ bcm63xx_ep0name,
+ "ep1in-bulk", "ep2out-bulk", "ep3in-int", "ep4out-int",
+};
+
+static bool use_fullspeed;
+module_param(use_fullspeed, bool, S_IRUGO);
+MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
+
+/*
+ * RX IRQ coalescing options:
+ *
+ * false (default) - one IRQ per DATAx packet. Slow but reliable. The
+ * driver is able to pass the "testusb" suite and recover from conditions like:
+ *
+ * 1) Device queues up a 2048-byte RX IUDMA transaction on an OUT bulk ep
+ * 2) Host sends 512 bytes of data
+ * 3) Host decides to reconfigure the device and sends SET_INTERFACE
+ * 4) Device shuts down the endpoint and cancels the RX transaction
+ *
+ * true - one IRQ per transfer, for transfers <= 2048B. Generates
+ * considerably fewer IRQs, but error recovery is less robust. Does not
+ * reliably pass "testusb".
+ *
+ * TX always uses coalescing, because we can cancel partially complete TX
+ * transfers by repeatedly flushing the FIFO. The hardware doesn't allow
+ * this on RX.
+ */
+static bool irq_coalesce;
+module_param(irq_coalesce, bool, S_IRUGO);
+MODULE_PARM_DESC(irq_coalesce, "take one IRQ per RX transfer");
+
+#define BCM63XX_NUM_EP 5
+#define BCM63XX_NUM_IUDMA 6
+#define BCM63XX_NUM_FIFO_PAIRS 3
+
+#define IUDMA_RESET_TIMEOUT_US 10000
+
+#define IUDMA_EP0_RXCHAN 0
+#define IUDMA_EP0_TXCHAN 1
+
+#define IUDMA_MAX_FRAGMENT 2048
+#define BCM63XX_MAX_CTRL_PKT 64
+
+#define BCMEP_CTRL 0x00
+#define BCMEP_ISOC 0x01
+#define BCMEP_BULK 0x02
+#define BCMEP_INTR 0x03
+
+#define BCMEP_OUT 0x00
+#define BCMEP_IN 0x01
+
+#define BCM63XX_SPD_FULL 1
+#define BCM63XX_SPD_HIGH 0
+
+#define IUDMA_DMAC_OFFSET 0x200
+#define IUDMA_DMAS_OFFSET 0x400
+
+enum bcm63xx_ep0_state {
+ EP0_REQUEUE,
+ EP0_IDLE,
+ EP0_IN_DATA_PHASE_SETUP,
+ EP0_IN_DATA_PHASE_COMPLETE,
+ EP0_OUT_DATA_PHASE_SETUP,
+ EP0_OUT_DATA_PHASE_COMPLETE,
+ EP0_OUT_STATUS_PHASE,
+ EP0_IN_FAKE_STATUS_PHASE,
+ EP0_SHUTDOWN,
+};
+
+static const char __maybe_unused bcm63xx_ep0_state_names[][32] = {
+ "REQUEUE",
+ "IDLE",
+ "IN_DATA_PHASE_SETUP",
+ "IN_DATA_PHASE_COMPLETE",
+ "OUT_DATA_PHASE_SETUP",
+ "OUT_DATA_PHASE_COMPLETE",
+ "OUT_STATUS_PHASE",
+ "IN_FAKE_STATUS_PHASE",
+ "SHUTDOWN",
+};
+
+/**
+ * struct iudma_ch_cfg - Static configuration for an IUDMA channel.
+ * @ep_num: USB endpoint number.
+ * @n_bds: Number of buffer descriptors in the ring.
+ * @ep_type: Endpoint type (control, bulk, interrupt).
+ * @dir: Direction (in, out).
+ * @n_fifo_slots: Number of FIFO entries to allocate for this channel.
+ * @max_pkt_hs: Maximum packet size in high speed mode.
+ * @max_pkt_fs: Maximum packet size in full speed mode.
+ */
+struct iudma_ch_cfg {
+ int ep_num;
+ int n_bds;
+ int ep_type;
+ int dir;
+ int n_fifo_slots;
+ int max_pkt_hs;
+ int max_pkt_fs;
+};
+
+static const struct iudma_ch_cfg iudma_defaults[] = {
+
+ /* This controller was designed to support a CDC/RNDIS application.
+ It may be possible to reconfigure some of the endpoints, but
+ the hardware limitations (FIFO sizing and number of DMA channels)
+ may significantly impact flexibility and/or stability. Change
+ these values at your own risk.
+
+ ep_num ep_type n_fifo_slots max_pkt_fs
+ idx | n_bds | dir | max_pkt_hs |
+ | | | | | | | | */
+ [0] = { -1, 4, BCMEP_CTRL, BCMEP_OUT, 32, 64, 64 },
+ [1] = { 0, 4, BCMEP_CTRL, BCMEP_OUT, 32, 64, 64 },
+ [2] = { 2, 16, BCMEP_BULK, BCMEP_OUT, 128, 512, 64 },
+ [3] = { 1, 16, BCMEP_BULK, BCMEP_IN, 128, 512, 64 },
+ [4] = { 4, 4, BCMEP_INTR, BCMEP_OUT, 32, 64, 64 },
+ [5] = { 3, 4, BCMEP_INTR, BCMEP_IN, 32, 64, 64 },
+};
+
+struct bcm63xx_udc;
+
+/**
+ * struct iudma_ch - Represents the current state of a single IUDMA channel.
+ * @ch_idx: IUDMA channel index (0 to BCM63XX_NUM_IUDMA-1).
+ * @ep_num: USB endpoint number. -1 for ep0 RX.
+ * @enabled: Whether bcm63xx_ep_enable() has been called.
+ * @max_pkt: "Chunk size" on the USB interface. Based on interface speed.
+ * @is_tx: true for TX, false for RX.
+ * @bep: Pointer to the associated endpoint. NULL for ep0 RX.
+ * @udc: Reference to the device controller.
+ * @read_bd: Next buffer descriptor to reap from the hardware.
+ * @write_bd: Next BD available for a new packet.
+ * @end_bd: Points to the final BD in the ring.
+ * @n_bds_used: Number of BD entries currently occupied.
+ * @bd_ring: Base pointer to the BD ring.
+ * @bd_ring_dma: Physical (DMA) address of bd_ring.
+ * @n_bds: Total number of BDs in the ring.
+ *
+ * ep0 has two IUDMA channels (IUDMA_EP0_RXCHAN and IUDMA_EP0_TXCHAN), as it is
+ * bidirectional. The "struct usb_ep" associated with ep0 is for TX (IN)
+ * only.
+ *
+ * Each bulk/intr endpoint has a single IUDMA channel and a single
+ * struct usb_ep.
+ */
+struct iudma_ch {
+ unsigned int ch_idx;
+ int ep_num;
+ bool enabled;
+ int max_pkt;
+ bool is_tx;
+ struct bcm63xx_ep *bep;
+ struct bcm63xx_udc *udc;
+
+ struct bcm_enet_desc *read_bd;
+ struct bcm_enet_desc *write_bd;
+ struct bcm_enet_desc *end_bd;
+ int n_bds_used;
+
+ struct bcm_enet_desc *bd_ring;
+ dma_addr_t bd_ring_dma;
+ unsigned int n_bds;
+};
+
+/**
+ * struct bcm63xx_ep - Internal (driver) state of a single endpoint.
+ * @ep_num: USB endpoint number.
+ * @iudma: Pointer to IUDMA channel state.
+ * @ep: USB gadget layer representation of the EP.
+ * @udc: Reference to the device controller.
+ * @queue: Linked list of outstanding requests for this EP.
+ * @halted: 1 if the EP is stalled; 0 otherwise.
+ */
+struct bcm63xx_ep {
+ unsigned int ep_num;
+ struct iudma_ch *iudma;
+ struct usb_ep ep;
+ struct bcm63xx_udc *udc;
+ struct list_head queue;
+ unsigned halted:1;
+};
+
+/**
+ * struct bcm63xx_req - Internal (driver) state of a single request.
+ * @queue: Links back to the EP's request list.
+ * @req: USB gadget layer representation of the request.
+ * @offset: Current byte offset into the data buffer (next byte to queue).
+ * @bd_bytes: Number of data bytes in outstanding BD entries.
+ * @iudma: IUDMA channel used for the request.
+ */
+struct bcm63xx_req {
+ struct list_head queue; /* ep's requests */
+ struct usb_request req;
+ unsigned int offset;
+ unsigned int bd_bytes;
+ struct iudma_ch *iudma;
+};
+
+/**
+ * struct bcm63xx_udc - Driver/hardware private context.
+ * @lock: Spinlock to mediate access to this struct, and (most) HW regs.
+ * @dev: Generic Linux device structure.
+ * @pd: Platform data (board/port info).
+ * @usbd_clk: Clock descriptor for the USB device block.
+ * @usbh_clk: Clock descriptor for the USB host block.
+ * @gadget: USB slave device.
+ * @driver: Driver for USB slave devices.
+ * @usbd_regs: Base address of the USBD/USB20D block.
+ * @iudma_regs: Base address of the USBD's associated IUDMA block.
+ * @bep: Array of endpoints, including ep0.
+ * @iudma: Array of all IUDMA channels used by this controller.
+ * @cfg: USB configuration number, from SET_CONFIGURATION wValue.
+ * @iface: USB interface number, from SET_INTERFACE wIndex.
+ * @alt_iface: USB alt interface number, from SET_INTERFACE wValue.
+ * @ep0_ctrl_req: Request object for bcm63xx_udc-initiated ep0 transactions.
+ * @ep0_ctrl_buf: Data buffer for ep0_ctrl_req.
+ * @ep0state: Current state of the ep0 state machine.
+ * @ep0_wq: Workqueue struct used to wake up the ep0 state machine.
+ * @wedgemap: Bitmap of wedged endpoints.
+ * @ep0_req_reset: USB reset is pending.
+ * @ep0_req_set_cfg: Need to spoof a SET_CONFIGURATION packet.
+ * @ep0_req_set_iface: Need to spoof a SET_INTERFACE packet.
+ * @ep0_req_shutdown: Driver is shutting down; requesting ep0 to halt activity.
+ * @ep0_req_completed: ep0 request has completed; worker has not seen it yet.
+ * @ep0_reply: Pending reply from gadget driver.
+ * @ep0_request: Outstanding ep0 request.
+ * @debugfs_root: debugfs directory: /sys/kernel/debug/<DRV_MODULE_NAME>.
+ * @debugfs_usbd: debugfs file "usbd" for controller state.
+ * @debugfs_iudma: debugfs file "usbd" for IUDMA state.
+ */
+struct bcm63xx_udc {
+ spinlock_t lock;
+
+ struct device *dev;
+ struct bcm63xx_usbd_platform_data *pd;
+ struct clk *usbd_clk;
+ struct clk *usbh_clk;
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+
+ void __iomem *usbd_regs;
+ void __iomem *iudma_regs;
+
+ struct bcm63xx_ep bep[BCM63XX_NUM_EP];
+ struct iudma_ch iudma[BCM63XX_NUM_IUDMA];
+
+ int cfg;
+ int iface;
+ int alt_iface;
+
+ struct bcm63xx_req ep0_ctrl_req;
+ u8 *ep0_ctrl_buf;
+
+ int ep0state;
+ struct work_struct ep0_wq;
+
+ unsigned long wedgemap;
+
+ unsigned ep0_req_reset:1;
+ unsigned ep0_req_set_cfg:1;
+ unsigned ep0_req_set_iface:1;
+ unsigned ep0_req_shutdown:1;
+
+ unsigned ep0_req_completed:1;
+ struct usb_request *ep0_reply;
+ struct usb_request *ep0_request;
+
+ struct dentry *debugfs_root;
+ struct dentry *debugfs_usbd;
+ struct dentry *debugfs_iudma;
+};
+
+static const struct usb_ep_ops bcm63xx_udc_ep_ops;
+
+/***********************************************************************
+ * Convenience functions
+ ***********************************************************************/
+
+static inline struct bcm63xx_udc *gadget_to_udc(struct usb_gadget *g)
+{
+ return container_of(g, struct bcm63xx_udc, gadget);
+}
+
+static inline struct bcm63xx_ep *our_ep(struct usb_ep *ep)
+{
+ return container_of(ep, struct bcm63xx_ep, ep);
+}
+
+static inline struct bcm63xx_req *our_req(struct usb_request *req)
+{
+ return container_of(req, struct bcm63xx_req, req);
+}
+
+static inline u32 usbd_readl(struct bcm63xx_udc *udc, u32 off)
+{
+ return bcm_readl(udc->usbd_regs + off);
+}
+
+static inline void usbd_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
+{
+ bcm_writel(val, udc->usbd_regs + off);
+}
+
+static inline u32 usb_dma_readl(struct bcm63xx_udc *udc, u32 off)
+{
+ return bcm_readl(udc->iudma_regs + off);
+}
+
+static inline void usb_dma_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
+{
+ bcm_writel(val, udc->iudma_regs + off);
+}
+
+static inline u32 usb_dmac_readl(struct bcm63xx_udc *udc, u32 off)
+{
+ return bcm_readl(udc->iudma_regs + IUDMA_DMAC_OFFSET + off);
+}
+
+static inline void usb_dmac_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
+{
+ bcm_writel(val, udc->iudma_regs + IUDMA_DMAC_OFFSET + off);
+}
+
+static inline u32 usb_dmas_readl(struct bcm63xx_udc *udc, u32 off)
+{
+ return bcm_readl(udc->iudma_regs + IUDMA_DMAS_OFFSET + off);
+}
+
+static inline void usb_dmas_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
+{
+ bcm_writel(val, udc->iudma_regs + IUDMA_DMAS_OFFSET + off);
+}
+
+static inline void set_clocks(struct bcm63xx_udc *udc, bool is_enabled)
+{
+ if (is_enabled) {
+ clk_enable(udc->usbh_clk);
+ clk_enable(udc->usbd_clk);
+ udelay(10);
+ } else {
+ clk_disable(udc->usbd_clk);
+ clk_disable(udc->usbh_clk);
+ }
+}
+
+/***********************************************************************
+ * Low-level IUDMA / FIFO operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_ep_dma_select - Helper function to set up the init_sel signal.
+ * @udc: Reference to the device controller.
+ * @idx: Desired init_sel value.
+ *
+ * The "init_sel" signal is used as a selection index for both endpoints
+ * and IUDMA channels. Since these do not map 1:1, the use of this signal
+ * depends on the context.
+ */
+static void bcm63xx_ep_dma_select(struct bcm63xx_udc *udc, int idx)
+{
+ u32 val = usbd_readl(udc, USBD_CONTROL_REG);
+
+ val &= ~USBD_CONTROL_INIT_SEL_MASK;
+ val |= idx << USBD_CONTROL_INIT_SEL_SHIFT;
+ usbd_writel(udc, val, USBD_CONTROL_REG);
+}
+
+/**
+ * bcm63xx_set_stall - Enable/disable stall on one endpoint.
+ * @udc: Reference to the device controller.
+ * @bep: Endpoint on which to operate.
+ * @is_stalled: true to enable stall, false to disable.
+ *
+ * See notes in bcm63xx_update_wedge() regarding automatic clearing of
+ * halt/stall conditions.
+ */
+static void bcm63xx_set_stall(struct bcm63xx_udc *udc, struct bcm63xx_ep *bep,
+ bool is_stalled)
+{
+ u32 val;
+
+ val = USBD_STALL_UPDATE_MASK |
+ (is_stalled ? USBD_STALL_ENABLE_MASK : 0) |
+ (bep->ep_num << USBD_STALL_EPNUM_SHIFT);
+ usbd_writel(udc, val, USBD_STALL_REG);
+}
+
+/**
+ * bcm63xx_fifo_setup - (Re)initialize FIFO boundaries and settings.
+ * @udc: Reference to the device controller.
+ *
+ * These parameters depend on the USB link speed. Settings are
+ * per-IUDMA-channel-pair.
+ */
+static void bcm63xx_fifo_setup(struct bcm63xx_udc *udc)
+{
+ int is_hs = udc->gadget.speed == USB_SPEED_HIGH;
+ u32 i, val, rx_fifo_slot, tx_fifo_slot;
+
+ /* set up FIFO boundaries and packet sizes; this is done in pairs */
+ rx_fifo_slot = tx_fifo_slot = 0;
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i += 2) {
+ const struct iudma_ch_cfg *rx_cfg = &iudma_defaults[i];
+ const struct iudma_ch_cfg *tx_cfg = &iudma_defaults[i + 1];
+
+ bcm63xx_ep_dma_select(udc, i >> 1);
+
+ val = (rx_fifo_slot << USBD_RXFIFO_CONFIG_START_SHIFT) |
+ ((rx_fifo_slot + rx_cfg->n_fifo_slots - 1) <<
+ USBD_RXFIFO_CONFIG_END_SHIFT);
+ rx_fifo_slot += rx_cfg->n_fifo_slots;
+ usbd_writel(udc, val, USBD_RXFIFO_CONFIG_REG);
+ usbd_writel(udc,
+ is_hs ? rx_cfg->max_pkt_hs : rx_cfg->max_pkt_fs,
+ USBD_RXFIFO_EPSIZE_REG);
+
+ val = (tx_fifo_slot << USBD_TXFIFO_CONFIG_START_SHIFT) |
+ ((tx_fifo_slot + tx_cfg->n_fifo_slots - 1) <<
+ USBD_TXFIFO_CONFIG_END_SHIFT);
+ tx_fifo_slot += tx_cfg->n_fifo_slots;
+ usbd_writel(udc, val, USBD_TXFIFO_CONFIG_REG);
+ usbd_writel(udc,
+ is_hs ? tx_cfg->max_pkt_hs : tx_cfg->max_pkt_fs,
+ USBD_TXFIFO_EPSIZE_REG);
+
+ usbd_readl(udc, USBD_TXFIFO_EPSIZE_REG);
+ }
+}
+
+/**
+ * bcm63xx_fifo_reset_ep - Flush a single endpoint's FIFO.
+ * @udc: Reference to the device controller.
+ * @ep_num: Endpoint number.
+ */
+static void bcm63xx_fifo_reset_ep(struct bcm63xx_udc *udc, int ep_num)
+{
+ u32 val;
+
+ bcm63xx_ep_dma_select(udc, ep_num);
+
+ val = usbd_readl(udc, USBD_CONTROL_REG);
+ val |= USBD_CONTROL_FIFO_RESET_MASK;
+ usbd_writel(udc, val, USBD_CONTROL_REG);
+ usbd_readl(udc, USBD_CONTROL_REG);
+}
+
+/**
+ * bcm63xx_fifo_reset - Flush all hardware FIFOs.
+ * @udc: Reference to the device controller.
+ */
+static void bcm63xx_fifo_reset(struct bcm63xx_udc *udc)
+{
+ int i;
+
+ for (i = 0; i < BCM63XX_NUM_FIFO_PAIRS; i++)
+ bcm63xx_fifo_reset_ep(udc, i);
+}
+
+/**
+ * bcm63xx_ep_init - Initial (one-time) endpoint initialization.
+ * @udc: Reference to the device controller.
+ */
+static void bcm63xx_ep_init(struct bcm63xx_udc *udc)
+{
+ u32 i, val;
+
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+ const struct iudma_ch_cfg *cfg = &iudma_defaults[i];
+
+ if (cfg->ep_num < 0)
+ continue;
+
+ bcm63xx_ep_dma_select(udc, cfg->ep_num);
+ val = (cfg->ep_type << USBD_EPNUM_TYPEMAP_TYPE_SHIFT) |
+ ((i >> 1) << USBD_EPNUM_TYPEMAP_DMA_CH_SHIFT);
+ usbd_writel(udc, val, USBD_EPNUM_TYPEMAP_REG);
+ }
+}
+
+/**
+ * bcm63xx_ep_setup - Configure per-endpoint settings.
+ * @udc: Reference to the device controller.
+ *
+ * This needs to be rerun if the speed/cfg/intf/altintf changes.
+ */
+static void bcm63xx_ep_setup(struct bcm63xx_udc *udc)
+{
+ u32 val, i;
+
+ usbd_writel(udc, USBD_CSR_SETUPADDR_DEF, USBD_CSR_SETUPADDR_REG);
+
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+ const struct iudma_ch_cfg *cfg = &iudma_defaults[i];
+ int max_pkt = udc->gadget.speed == USB_SPEED_HIGH ?
+ cfg->max_pkt_hs : cfg->max_pkt_fs;
+ int idx = cfg->ep_num;
+
+ udc->iudma[i].max_pkt = max_pkt;
+
+ if (idx < 0)
+ continue;
+ udc->bep[idx].ep.maxpacket = max_pkt;
+
+ val = (idx << USBD_CSR_EP_LOG_SHIFT) |
+ (cfg->dir << USBD_CSR_EP_DIR_SHIFT) |
+ (cfg->ep_type << USBD_CSR_EP_TYPE_SHIFT) |
+ (udc->cfg << USBD_CSR_EP_CFG_SHIFT) |
+ (udc->iface << USBD_CSR_EP_IFACE_SHIFT) |
+ (udc->alt_iface << USBD_CSR_EP_ALTIFACE_SHIFT) |
+ (max_pkt << USBD_CSR_EP_MAXPKT_SHIFT);
+ usbd_writel(udc, val, USBD_CSR_EP_REG(idx));
+ }
+}
+
+/**
+ * iudma_write - Queue a single IUDMA transaction.
+ * @udc: Reference to the device controller.
+ * @iudma: IUDMA channel to use.
+ * @breq: Request containing the transaction data.
+ *
+ * For RX IUDMA, this will queue a single buffer descriptor, as RX IUDMA
+ * does not honor SOP/EOP so the handling of multiple buffers is ambiguous.
+ * So iudma_write() may be called several times to fulfill a single
+ * usb_request.
+ *
+ * For TX IUDMA, this can queue multiple buffer descriptors if needed.
+ */
+static void iudma_write(struct bcm63xx_udc *udc, struct iudma_ch *iudma,
+ struct bcm63xx_req *breq)
+{
+ int first_bd = 1, last_bd = 0, extra_zero_pkt = 0;
+ unsigned int bytes_left = breq->req.length - breq->offset;
+ const int max_bd_bytes = !irq_coalesce && !iudma->is_tx ?
+ iudma->max_pkt : IUDMA_MAX_FRAGMENT;
+
+ iudma->n_bds_used = 0;
+ breq->bd_bytes = 0;
+ breq->iudma = iudma;
+
+ if ((bytes_left % iudma->max_pkt == 0) && bytes_left && breq->req.zero)
+ extra_zero_pkt = 1;
+
+ do {
+ struct bcm_enet_desc *d = iudma->write_bd;
+ u32 dmaflags = 0;
+ unsigned int n_bytes;
+
+ if (d == iudma->end_bd) {
+ dmaflags |= DMADESC_WRAP_MASK;
+ iudma->write_bd = iudma->bd_ring;
+ } else {
+ iudma->write_bd++;
+ }
+ iudma->n_bds_used++;
+
+ n_bytes = min_t(int, bytes_left, max_bd_bytes);
+ if (n_bytes)
+ dmaflags |= n_bytes << DMADESC_LENGTH_SHIFT;
+ else
+ dmaflags |= (1 << DMADESC_LENGTH_SHIFT) |
+ DMADESC_USB_ZERO_MASK;
+
+ dmaflags |= DMADESC_OWNER_MASK;
+ if (first_bd) {
+ dmaflags |= DMADESC_SOP_MASK;
+ first_bd = 0;
+ }
+
+ /*
+ * extra_zero_pkt forces one more iteration through the loop
+ * after all data is queued up, to send the zero packet
+ */
+ if (extra_zero_pkt && !bytes_left)
+ extra_zero_pkt = 0;
+
+ if (!iudma->is_tx || iudma->n_bds_used == iudma->n_bds ||
+ (n_bytes == bytes_left && !extra_zero_pkt)) {
+ last_bd = 1;
+ dmaflags |= DMADESC_EOP_MASK;
+ }
+
+ d->address = breq->req.dma + breq->offset;
+ mb();
+ d->len_stat = dmaflags;
+
+ breq->offset += n_bytes;
+ breq->bd_bytes += n_bytes;
+ bytes_left -= n_bytes;
+ } while (!last_bd);
+
+ usb_dmac_writel(udc, ENETDMAC_CHANCFG_EN_MASK,
+ ENETDMAC_CHANCFG_REG(iudma->ch_idx));
+}
+
+/**
+ * iudma_read - Check for IUDMA buffer completion.
+ * @udc: Reference to the device controller.
+ * @iudma: IUDMA channel to use.
+ *
+ * This checks to see if ALL of the outstanding BDs on the DMA channel
+ * have been filled. If so, it returns the actual transfer length;
+ * otherwise it returns -EBUSY.
+ */
+static int iudma_read(struct bcm63xx_udc *udc, struct iudma_ch *iudma)
+{
+ int i, actual_len = 0;
+ struct bcm_enet_desc *d = iudma->read_bd;
+
+ if (!iudma->n_bds_used)
+ return -EINVAL;
+
+ for (i = 0; i < iudma->n_bds_used; i++) {
+ u32 dmaflags;
+
+ dmaflags = d->len_stat;
+
+ if (dmaflags & DMADESC_OWNER_MASK)
+ return -EBUSY;
+
+ actual_len += (dmaflags & DMADESC_LENGTH_MASK) >>
+ DMADESC_LENGTH_SHIFT;
+ if (d == iudma->end_bd)
+ d = iudma->bd_ring;
+ else
+ d++;
+ }
+
+ iudma->read_bd = d;
+ iudma->n_bds_used = 0;
+ return actual_len;
+}
+
+/**
+ * iudma_reset_channel - Stop DMA on a single channel.
+ * @udc: Reference to the device controller.
+ * @iudma: IUDMA channel to reset.
+ */
+static void iudma_reset_channel(struct bcm63xx_udc *udc, struct iudma_ch *iudma)
+{
+ int timeout = IUDMA_RESET_TIMEOUT_US;
+ struct bcm_enet_desc *d;
+ int ch_idx = iudma->ch_idx;
+
+ if (!iudma->is_tx)
+ bcm63xx_fifo_reset_ep(udc, max(0, iudma->ep_num));
+
+ /* stop DMA, then wait for the hardware to wrap up */
+ usb_dmac_writel(udc, 0, ENETDMAC_CHANCFG_REG(ch_idx));
+
+ while (usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG(ch_idx)) &
+ ENETDMAC_CHANCFG_EN_MASK) {
+ udelay(1);
+
+ /* repeatedly flush the FIFO data until the BD completes */
+ if (iudma->is_tx && iudma->ep_num >= 0)
+ bcm63xx_fifo_reset_ep(udc, iudma->ep_num);
+
+ if (!timeout--) {
+ dev_err(udc->dev, "can't reset IUDMA channel %d\n",
+ ch_idx);
+ break;
+ }
+ if (timeout == IUDMA_RESET_TIMEOUT_US / 2) {
+ dev_warn(udc->dev, "forcibly halting IUDMA channel %d\n",
+ ch_idx);
+ usb_dmac_writel(udc, ENETDMAC_CHANCFG_BUFHALT_MASK,
+ ENETDMAC_CHANCFG_REG(ch_idx));
+ }
+ }
+ usb_dmac_writel(udc, ~0, ENETDMAC_IR_REG(ch_idx));
+
+ /* don't leave "live" HW-owned entries for the next guy to step on */
+ for (d = iudma->bd_ring; d <= iudma->end_bd; d++)
+ d->len_stat = 0;
+ mb();
+
+ iudma->read_bd = iudma->write_bd = iudma->bd_ring;
+ iudma->n_bds_used = 0;
+
+ /* set up IRQs, UBUS burst size, and BD base for this channel */
+ usb_dmac_writel(udc, ENETDMAC_IR_BUFDONE_MASK,
+ ENETDMAC_IRMASK_REG(ch_idx));
+ usb_dmac_writel(udc, 8, ENETDMAC_MAXBURST_REG(ch_idx));
+
+ usb_dmas_writel(udc, iudma->bd_ring_dma, ENETDMAS_RSTART_REG(ch_idx));
+ usb_dmas_writel(udc, 0, ENETDMAS_SRAM2_REG(ch_idx));
+}
+
+/**
+ * iudma_init_channel - One-time IUDMA channel initialization.
+ * @udc: Reference to the device controller.
+ * @ch_idx: Channel to initialize.
+ */
+static int iudma_init_channel(struct bcm63xx_udc *udc, unsigned int ch_idx)
+{
+ struct iudma_ch *iudma = &udc->iudma[ch_idx];
+ const struct iudma_ch_cfg *cfg = &iudma_defaults[ch_idx];
+ unsigned int n_bds = cfg->n_bds;
+ struct bcm63xx_ep *bep = NULL;
+
+ iudma->ep_num = cfg->ep_num;
+ iudma->ch_idx = ch_idx;
+ iudma->is_tx = !!(ch_idx & 0x01);
+ if (iudma->ep_num >= 0) {
+ bep = &udc->bep[iudma->ep_num];
+ bep->iudma = iudma;
+ INIT_LIST_HEAD(&bep->queue);
+ }
+
+ iudma->bep = bep;
+ iudma->udc = udc;
+
+ /* ep0 is always active; others are controlled by the gadget driver */
+ if (iudma->ep_num <= 0)
+ iudma->enabled = true;
+
+ iudma->n_bds = n_bds;
+ iudma->bd_ring = dmam_alloc_coherent(udc->dev,
+ n_bds * sizeof(struct bcm_enet_desc),
+ &iudma->bd_ring_dma, GFP_KERNEL);
+ if (!iudma->bd_ring)
+ return -ENOMEM;
+ iudma->end_bd = &iudma->bd_ring[n_bds - 1];
+
+ return 0;
+}
+
+/**
+ * iudma_init - One-time initialization of all IUDMA channels.
+ * @udc: Reference to the device controller.
+ *
+ * Enable DMA, flush channels, and enable global IUDMA IRQs.
+ */
+static int iudma_init(struct bcm63xx_udc *udc)
+{
+ int i, rc;
+
+ usb_dma_writel(udc, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG);
+
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+ rc = iudma_init_channel(udc, i);
+ if (rc)
+ return rc;
+ iudma_reset_channel(udc, &udc->iudma[i]);
+ }
+
+ usb_dma_writel(udc, BIT(BCM63XX_NUM_IUDMA)-1, ENETDMA_GLB_IRQMASK_REG);
+ return 0;
+}
+
+/**
+ * iudma_uninit - Uninitialize IUDMA channels.
+ * @udc: Reference to the device controller.
+ *
+ * Kill global IUDMA IRQs, flush channels, and kill DMA.
+ */
+static void iudma_uninit(struct bcm63xx_udc *udc)
+{
+ int i;
+
+ usb_dma_writel(udc, 0, ENETDMA_GLB_IRQMASK_REG);
+
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i++)
+ iudma_reset_channel(udc, &udc->iudma[i]);
+
+ usb_dma_writel(udc, 0, ENETDMA_CFG_REG);
+}
+
+/***********************************************************************
+ * Other low-level USBD operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_set_ctrl_irqs - Mask/unmask control path interrupts.
+ * @udc: Reference to the device controller.
+ * @enable_irqs: true to enable, false to disable.
+ */
+static void bcm63xx_set_ctrl_irqs(struct bcm63xx_udc *udc, bool enable_irqs)
+{
+ u32 val;
+
+ usbd_writel(udc, 0, USBD_STATUS_REG);
+
+ val = BIT(USBD_EVENT_IRQ_USB_RESET) |
+ BIT(USBD_EVENT_IRQ_SETUP) |
+ BIT(USBD_EVENT_IRQ_SETCFG) |
+ BIT(USBD_EVENT_IRQ_SETINTF) |
+ BIT(USBD_EVENT_IRQ_USB_LINK);
+ usbd_writel(udc, enable_irqs ? val : 0, USBD_EVENT_IRQ_MASK_REG);
+ usbd_writel(udc, val, USBD_EVENT_IRQ_STATUS_REG);
+}
+
+/**
+ * bcm63xx_select_phy_mode - Select between USB device and host mode.
+ * @udc: Reference to the device controller.
+ * @is_device: true for device, false for host.
+ *
+ * This should probably be reworked to use the drivers/usb/otg
+ * infrastructure.
+ *
+ * By default, the AFE/pullups are disabled in device mode, until
+ * bcm63xx_select_pullup() is called.
+ */
+static void bcm63xx_select_phy_mode(struct bcm63xx_udc *udc, bool is_device)
+{
+ u32 val, portmask = BIT(udc->pd->port_no);
+
+ if (BCMCPU_IS_6328()) {
+ /* configure pinmux to sense VBUS signal */
+ val = bcm_gpio_readl(GPIO_PINMUX_OTHR_REG);
+ val &= ~GPIO_PINMUX_OTHR_6328_USB_MASK;
+ val |= is_device ? GPIO_PINMUX_OTHR_6328_USB_DEV :
+ GPIO_PINMUX_OTHR_6328_USB_HOST;
+ bcm_gpio_writel(val, GPIO_PINMUX_OTHR_REG);
+ }
+
+ val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG);
+ if (is_device) {
+ val |= (portmask << USBH_PRIV_UTMI_CTL_HOSTB_SHIFT);
+ val |= (portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+ } else {
+ val &= ~(portmask << USBH_PRIV_UTMI_CTL_HOSTB_SHIFT);
+ val &= ~(portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+ }
+ bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_UTMI_CTL_6368_REG);
+
+ val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_SWAP_6368_REG);
+ if (is_device)
+ val |= USBH_PRIV_SWAP_USBD_MASK;
+ else
+ val &= ~USBH_PRIV_SWAP_USBD_MASK;
+ bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_SWAP_6368_REG);
+}
+
+/**
+ * bcm63xx_select_pullup - Enable/disable the pullup on D+
+ * @udc: Reference to the device controller.
+ * @is_on: true to enable the pullup, false to disable.
+ *
+ * If the pullup is active, the host will sense a FS/HS device connected to
+ * the port. If the pullup is inactive, the host will think the USB
+ * device has been disconnected.
+ */
+static void bcm63xx_select_pullup(struct bcm63xx_udc *udc, bool is_on)
+{
+ u32 val, portmask = BIT(udc->pd->port_no);
+
+ val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG);
+ if (is_on)
+ val &= ~(portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+ else
+ val |= (portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+ bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_UTMI_CTL_6368_REG);
+}
+
+/**
+ * bcm63xx_uninit_udc_hw - Shut down the hardware prior to driver removal.
+ * @udc: Reference to the device controller.
+ *
+ * This just masks the IUDMA IRQs and releases the clocks. It is assumed
+ * that bcm63xx_udc_stop() has already run, and the clocks are stopped.
+ */
+static void bcm63xx_uninit_udc_hw(struct bcm63xx_udc *udc)
+{
+ set_clocks(udc, true);
+ iudma_uninit(udc);
+ set_clocks(udc, false);
+
+ clk_put(udc->usbd_clk);
+ clk_put(udc->usbh_clk);
+}
+
+/**
+ * bcm63xx_init_udc_hw - Initialize the controller hardware and data structures.
+ * @udc: Reference to the device controller.
+ */
+static int bcm63xx_init_udc_hw(struct bcm63xx_udc *udc)
+{
+ int i, rc = 0;
+ u32 val;
+
+ udc->ep0_ctrl_buf = devm_kzalloc(udc->dev, BCM63XX_MAX_CTRL_PKT,
+ GFP_KERNEL);
+ if (!udc->ep0_ctrl_buf)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+ for (i = 0; i < BCM63XX_NUM_EP; i++) {
+ struct bcm63xx_ep *bep = &udc->bep[i];
+
+ bep->ep.name = bcm63xx_ep_name[i];
+ bep->ep_num = i;
+ bep->ep.ops = &bcm63xx_udc_ep_ops;
+ list_add_tail(&bep->ep.ep_list, &udc->gadget.ep_list);
+ bep->halted = 0;
+ bep->ep.maxpacket = BCM63XX_MAX_CTRL_PKT;
+ bep->udc = udc;
+ bep->ep.desc = NULL;
+ INIT_LIST_HEAD(&bep->queue);
+ }
+
+ udc->gadget.ep0 = &udc->bep[0].ep;
+ list_del(&udc->bep[0].ep.ep_list);
+
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->ep0state = EP0_SHUTDOWN;
+
+ udc->usbh_clk = clk_get(udc->dev, "usbh");
+ if (IS_ERR(udc->usbh_clk))
+ return -EIO;
+
+ udc->usbd_clk = clk_get(udc->dev, "usbd");
+ if (IS_ERR(udc->usbd_clk)) {
+ clk_put(udc->usbh_clk);
+ return -EIO;
+ }
+
+ set_clocks(udc, true);
+
+ val = USBD_CONTROL_AUTO_CSRS_MASK |
+ USBD_CONTROL_DONE_CSRS_MASK |
+ (irq_coalesce ? USBD_CONTROL_RXZSCFG_MASK : 0);
+ usbd_writel(udc, val, USBD_CONTROL_REG);
+
+ val = USBD_STRAPS_APP_SELF_PWR_MASK |
+ USBD_STRAPS_APP_RAM_IF_MASK |
+ USBD_STRAPS_APP_CSRPRGSUP_MASK |
+ USBD_STRAPS_APP_8BITPHY_MASK |
+ USBD_STRAPS_APP_RMTWKUP_MASK;
+
+ if (udc->gadget.max_speed == USB_SPEED_HIGH)
+ val |= (BCM63XX_SPD_HIGH << USBD_STRAPS_SPEED_SHIFT);
+ else
+ val |= (BCM63XX_SPD_FULL << USBD_STRAPS_SPEED_SHIFT);
+ usbd_writel(udc, val, USBD_STRAPS_REG);
+
+ bcm63xx_set_ctrl_irqs(udc, false);
+
+ usbd_writel(udc, 0, USBD_EVENT_IRQ_CFG_LO_REG);
+
+ val = USBD_EVENT_IRQ_CFG_FALLING(USBD_EVENT_IRQ_ENUM_ON) |
+ USBD_EVENT_IRQ_CFG_FALLING(USBD_EVENT_IRQ_SET_CSRS);
+ usbd_writel(udc, val, USBD_EVENT_IRQ_CFG_HI_REG);
+
+ rc = iudma_init(udc);
+ set_clocks(udc, false);
+ if (rc)
+ bcm63xx_uninit_udc_hw(udc);
+
+ return 0;
+}
+
+/***********************************************************************
+ * Standard EP gadget operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_ep_enable - Enable one endpoint.
+ * @ep: Endpoint to enable.
+ * @desc: Contains max packet, direction, etc.
+ *
+ * Most of the endpoint parameters are fixed in this controller, so there
+ * isn't much for this function to do.
+ */
+static int bcm63xx_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ struct iudma_ch *iudma = bep->iudma;
+ unsigned long flags;
+
+ if (!ep || !desc || ep->name == bcm63xx_ep0name)
+ return -EINVAL;
+
+ if (!udc->driver)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (iudma->enabled) {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return -EINVAL;
+ }
+
+ iudma->enabled = true;
+ BUG_ON(!list_empty(&bep->queue));
+
+ iudma_reset_channel(udc, iudma);
+
+ bep->halted = 0;
+ bcm63xx_set_stall(udc, bep, false);
+ clear_bit(bep->ep_num, &udc->wedgemap);
+
+ ep->desc = desc;
+ ep->maxpacket = usb_endpoint_maxp(desc);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/**
+ * bcm63xx_ep_disable - Disable one endpoint.
+ * @ep: Endpoint to disable.
+ */
+static int bcm63xx_ep_disable(struct usb_ep *ep)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ struct iudma_ch *iudma = bep->iudma;
+ struct list_head *pos, *n;
+ unsigned long flags;
+
+ if (!ep || !ep->desc)
+ return -EINVAL;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (!iudma->enabled) {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return -EINVAL;
+ }
+ iudma->enabled = false;
+
+ iudma_reset_channel(udc, iudma);
+
+ if (!list_empty(&bep->queue)) {
+ list_for_each_safe(pos, n, &bep->queue) {
+ struct bcm63xx_req *breq =
+ list_entry(pos, struct bcm63xx_req, queue);
+
+ usb_gadget_unmap_request(&udc->gadget, &breq->req,
+ iudma->is_tx);
+ list_del(&breq->queue);
+ breq->req.status = -ESHUTDOWN;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ breq->req.complete(&iudma->bep->ep, &breq->req);
+ spin_lock_irqsave(&udc->lock, flags);
+ }
+ }
+ ep->desc = NULL;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/**
+ * bcm63xx_udc_alloc_request - Allocate a new request.
+ * @ep: Endpoint associated with the request.
+ * @mem_flags: Flags to pass to kzalloc().
+ */
+static struct usb_request *bcm63xx_udc_alloc_request(struct usb_ep *ep,
+ gfp_t mem_flags)
+{
+ struct bcm63xx_req *breq;
+
+ breq = kzalloc(sizeof(*breq), mem_flags);
+ if (!breq)
+ return NULL;
+ return &breq->req;
+}
+
+/**
+ * bcm63xx_udc_free_request - Free a request.
+ * @ep: Endpoint associated with the request.
+ * @req: Request to free.
+ */
+static void bcm63xx_udc_free_request(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct bcm63xx_req *breq = our_req(req);
+ kfree(breq);
+}
+
+/**
+ * bcm63xx_udc_queue - Queue up a new request.
+ * @ep: Endpoint associated with the request.
+ * @req: Request to add.
+ * @mem_flags: Unused.
+ *
+ * If the queue is empty, start this request immediately. Otherwise, add
+ * it to the list.
+ *
+ * ep0 replies are sent through this function from the gadget driver, but
+ * they are treated differently because they need to be handled by the ep0
+ * state machine. (Sometimes they are replies to control requests that
+ * were spoofed by this driver, and so they shouldn't be transmitted at all.)
+ */
+static int bcm63xx_udc_queue(struct usb_ep *ep, struct usb_request *req,
+ gfp_t mem_flags)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ struct bcm63xx_req *breq = our_req(req);
+ unsigned long flags;
+ int rc = 0;
+
+ if (unlikely(!req || !req->complete || !req->buf || !ep))
+ return -EINVAL;
+
+ req->actual = 0;
+ req->status = 0;
+ breq->offset = 0;
+
+ if (bep == &udc->bep[0]) {
+ /* only one reply per request, please */
+ if (udc->ep0_reply)
+ return -EINVAL;
+
+ udc->ep0_reply = req;
+ schedule_work(&udc->ep0_wq);
+ return 0;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (!bep->iudma->enabled) {
+ rc = -ESHUTDOWN;
+ goto out;
+ }
+
+ rc = usb_gadget_map_request(&udc->gadget, req, bep->iudma->is_tx);
+ if (rc == 0) {
+ list_add_tail(&breq->queue, &bep->queue);
+ if (list_is_singular(&bep->queue))
+ iudma_write(udc, bep->iudma, breq);
+ }
+
+out:
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return rc;
+}
+
+/**
+ * bcm63xx_udc_dequeue - Remove a pending request from the queue.
+ * @ep: Endpoint associated with the request.
+ * @req: Request to remove.
+ *
+ * If the request is not at the head of the queue, this is easy - just nuke
+ * it. If the request is at the head of the queue, we'll need to stop the
+ * DMA transaction and then queue up the successor.
+ */
+static int bcm63xx_udc_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ struct bcm63xx_req *breq = our_req(req), *cur;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (list_empty(&bep->queue)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ cur = list_first_entry(&bep->queue, struct bcm63xx_req, queue);
+ usb_gadget_unmap_request(&udc->gadget, &breq->req, bep->iudma->is_tx);
+
+ if (breq == cur) {
+ iudma_reset_channel(udc, bep->iudma);
+ list_del(&breq->queue);
+
+ if (!list_empty(&bep->queue)) {
+ struct bcm63xx_req *next;
+
+ next = list_first_entry(&bep->queue,
+ struct bcm63xx_req, queue);
+ iudma_write(udc, bep->iudma, next);
+ }
+ } else {
+ list_del(&breq->queue);
+ }
+
+out:
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ req->status = -ESHUTDOWN;
+ req->complete(ep, req);
+
+ return rc;
+}
+
+/**
+ * bcm63xx_udc_set_halt - Enable/disable STALL flag in the hardware.
+ * @ep: Endpoint to halt.
+ * @value: Zero to clear halt; nonzero to set halt.
+ *
+ * See comments in bcm63xx_update_wedge().
+ */
+static int bcm63xx_udc_set_halt(struct usb_ep *ep, int value)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ bcm63xx_set_stall(udc, bep, !!value);
+ bep->halted = value;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * bcm63xx_udc_set_wedge - Stall the endpoint until the next reset.
+ * @ep: Endpoint to wedge.
+ *
+ * See comments in bcm63xx_update_wedge().
+ */
+static int bcm63xx_udc_set_wedge(struct usb_ep *ep)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ set_bit(bep->ep_num, &udc->wedgemap);
+ bcm63xx_set_stall(udc, bep, true);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static const struct usb_ep_ops bcm63xx_udc_ep_ops = {
+ .enable = bcm63xx_ep_enable,
+ .disable = bcm63xx_ep_disable,
+
+ .alloc_request = bcm63xx_udc_alloc_request,
+ .free_request = bcm63xx_udc_free_request,
+
+ .queue = bcm63xx_udc_queue,
+ .dequeue = bcm63xx_udc_dequeue,
+
+ .set_halt = bcm63xx_udc_set_halt,
+ .set_wedge = bcm63xx_udc_set_wedge,
+};
+
+/***********************************************************************
+ * EP0 handling
+ ***********************************************************************/
+
+/**
+ * bcm63xx_ep0_setup_callback - Drop spinlock to invoke ->setup callback.
+ * @udc: Reference to the device controller.
+ * @ctrl: 8-byte SETUP request.
+ */
+static int bcm63xx_ep0_setup_callback(struct bcm63xx_udc *udc,
+ struct usb_ctrlrequest *ctrl)
+{
+ int rc;
+
+ spin_unlock_irq(&udc->lock);
+ rc = udc->driver->setup(&udc->gadget, ctrl);
+ spin_lock_irq(&udc->lock);
+ return rc;
+}
+
+/**
+ * bcm63xx_ep0_spoof_set_cfg - Synthesize a SET_CONFIGURATION request.
+ * @udc: Reference to the device controller.
+ *
+ * Many standard requests are handled automatically in the hardware, but
+ * we still need to pass them to the gadget driver so that it can
+ * reconfigure the interfaces/endpoints if necessary.
+ *
+ * Unfortunately we are not able to send a STALL response if the host
+ * requests an invalid configuration. If this happens, we'll have to be
+ * content with printing a warning.
+ */
+static int bcm63xx_ep0_spoof_set_cfg(struct bcm63xx_udc *udc)
+{
+ struct usb_ctrlrequest ctrl;
+ int rc;
+
+ ctrl.bRequestType = USB_DIR_OUT | USB_RECIP_DEVICE;
+ ctrl.bRequest = USB_REQ_SET_CONFIGURATION;
+ ctrl.wValue = cpu_to_le16(udc->cfg);
+ ctrl.wIndex = 0;
+ ctrl.wLength = 0;
+
+ rc = bcm63xx_ep0_setup_callback(udc, &ctrl);
+ if (rc < 0) {
+ dev_warn_ratelimited(udc->dev,
+ "hardware auto-acked bad SET_CONFIGURATION(%d) request\n",
+ udc->cfg);
+ }
+ return rc;
+}
+
+/**
+ * bcm63xx_ep0_spoof_set_iface - Synthesize a SET_INTERFACE request.
+ * @udc: Reference to the device controller.
+ */
+static int bcm63xx_ep0_spoof_set_iface(struct bcm63xx_udc *udc)
+{
+ struct usb_ctrlrequest ctrl;
+ int rc;
+
+ ctrl.bRequestType = USB_DIR_OUT | USB_RECIP_INTERFACE;
+ ctrl.bRequest = USB_REQ_SET_INTERFACE;
+ ctrl.wValue = cpu_to_le16(udc->alt_iface);
+ ctrl.wIndex = cpu_to_le16(udc->iface);
+ ctrl.wLength = 0;
+
+ rc = bcm63xx_ep0_setup_callback(udc, &ctrl);
+ if (rc < 0) {
+ dev_warn_ratelimited(udc->dev,
+ "hardware auto-acked bad SET_INTERFACE(%d,%d) request\n",
+ udc->iface, udc->alt_iface);
+ }
+ return rc;
+}
+
+/**
+ * bcm63xx_ep0_map_write - dma_map and iudma_write a single request.
+ * @udc: Reference to the device controller.
+ * @ch_idx: IUDMA channel number.
+ * @req: USB gadget layer representation of the request.
+ */
+static void bcm63xx_ep0_map_write(struct bcm63xx_udc *udc, int ch_idx,
+ struct usb_request *req)
+{
+ struct bcm63xx_req *breq = our_req(req);
+ struct iudma_ch *iudma = &udc->iudma[ch_idx];
+
+ BUG_ON(udc->ep0_request);
+ udc->ep0_request = req;
+
+ req->actual = 0;
+ breq->offset = 0;
+ usb_gadget_map_request(&udc->gadget, req, iudma->is_tx);
+ iudma_write(udc, iudma, breq);
+}
+
+/**
+ * bcm63xx_ep0_complete - Set completion status and "stage" the callback.
+ * @udc: Reference to the device controller.
+ * @req: USB gadget layer representation of the request.
+ * @status: Status to return to the gadget driver.
+ */
+static void bcm63xx_ep0_complete(struct bcm63xx_udc *udc,
+ struct usb_request *req, int status)
+{
+ req->status = status;
+ if (status)
+ req->actual = 0;
+ if (req->complete) {
+ spin_unlock_irq(&udc->lock);
+ req->complete(&udc->bep[0].ep, req);
+ spin_lock_irq(&udc->lock);
+ }
+}
+
+/**
+ * bcm63xx_ep0_nuke_reply - Abort request from the gadget driver due to
+ * reset/shutdown.
+ * @udc: Reference to the device controller.
+ * @is_tx: Nonzero for TX (IN), zero for RX (OUT).
+ */
+static void bcm63xx_ep0_nuke_reply(struct bcm63xx_udc *udc, int is_tx)
+{
+ struct usb_request *req = udc->ep0_reply;
+
+ udc->ep0_reply = NULL;
+ usb_gadget_unmap_request(&udc->gadget, req, is_tx);
+ if (udc->ep0_request == req) {
+ udc->ep0_req_completed = 0;
+ udc->ep0_request = NULL;
+ }
+ bcm63xx_ep0_complete(udc, req, -ESHUTDOWN);
+}
+
+/**
+ * bcm63xx_ep0_read_complete - Close out the pending ep0 request; return
+ * transfer len.
+ * @udc: Reference to the device controller.
+ */
+static int bcm63xx_ep0_read_complete(struct bcm63xx_udc *udc)
+{
+ struct usb_request *req = udc->ep0_request;
+
+ udc->ep0_req_completed = 0;
+ udc->ep0_request = NULL;
+
+ return req->actual;
+}
+
+/**
+ * bcm63xx_ep0_internal_request - Helper function to submit an ep0 request.
+ * @udc: Reference to the device controller.
+ * @ch_idx: IUDMA channel number.
+ * @length: Number of bytes to TX/RX.
+ *
+ * Used for simple transfers performed by the ep0 worker. This will always
+ * use ep0_ctrl_req / ep0_ctrl_buf.
+ */
+static void bcm63xx_ep0_internal_request(struct bcm63xx_udc *udc, int ch_idx,
+ int length)
+{
+ struct usb_request *req = &udc->ep0_ctrl_req.req;
+
+ req->buf = udc->ep0_ctrl_buf;
+ req->length = length;
+ req->complete = NULL;
+
+ bcm63xx_ep0_map_write(udc, ch_idx, req);
+}
+
+/**
+ * bcm63xx_ep0_do_setup - Parse new SETUP packet and decide how to handle it.
+ * @udc: Reference to the device controller.
+ *
+ * EP0_IDLE probably shouldn't ever happen. EP0_REQUEUE means we're ready
+ * for the next packet. Anything else means the transaction requires multiple
+ * stages of handling.
+ */
+static enum bcm63xx_ep0_state bcm63xx_ep0_do_setup(struct bcm63xx_udc *udc)
+{
+ int rc;
+ struct usb_ctrlrequest *ctrl = (void *)udc->ep0_ctrl_buf;
+
+ rc = bcm63xx_ep0_read_complete(udc);
+
+ if (rc < 0) {
+ dev_err(udc->dev, "missing SETUP packet\n");
+ return EP0_IDLE;
+ }
+
+ /*
+ * Handle 0-byte IN STATUS acknowledgement. The hardware doesn't
+ * ALWAYS deliver these 100% of the time, so if we happen to see one,
+ * just throw it away.
+ */
+ if (rc == 0)
+ return EP0_REQUEUE;
+
+ /* Drop malformed SETUP packets */
+ if (rc != sizeof(*ctrl)) {
+ dev_warn_ratelimited(udc->dev,
+ "malformed SETUP packet (%d bytes)\n", rc);
+ return EP0_REQUEUE;
+ }
+
+ /* Process new SETUP packet arriving on ep0 */
+ rc = bcm63xx_ep0_setup_callback(udc, ctrl);
+ if (rc < 0) {
+ bcm63xx_set_stall(udc, &udc->bep[0], true);
+ return EP0_REQUEUE;
+ }
+
+ if (!ctrl->wLength)
+ return EP0_REQUEUE;
+ else if (ctrl->bRequestType & USB_DIR_IN)
+ return EP0_IN_DATA_PHASE_SETUP;
+ else
+ return EP0_OUT_DATA_PHASE_SETUP;
+}
+
+/**
+ * bcm63xx_ep0_do_idle - Check for outstanding requests if ep0 is idle.
+ * @udc: Reference to the device controller.
+ *
+ * In state EP0_IDLE, the RX descriptor is either pending, or has been
+ * filled with a SETUP packet from the host. This function handles new
+ * SETUP packets, control IRQ events (which can generate fake SETUP packets),
+ * and reset/shutdown events.
+ *
+ * Returns 0 if work was done; -EAGAIN if nothing to do.
+ */
+static int bcm63xx_ep0_do_idle(struct bcm63xx_udc *udc)
+{
+ if (udc->ep0_req_reset) {
+ udc->ep0_req_reset = 0;
+ } else if (udc->ep0_req_set_cfg) {
+ udc->ep0_req_set_cfg = 0;
+ if (bcm63xx_ep0_spoof_set_cfg(udc) >= 0)
+ udc->ep0state = EP0_IN_FAKE_STATUS_PHASE;
+ } else if (udc->ep0_req_set_iface) {
+ udc->ep0_req_set_iface = 0;
+ if (bcm63xx_ep0_spoof_set_iface(udc) >= 0)
+ udc->ep0state = EP0_IN_FAKE_STATUS_PHASE;
+ } else if (udc->ep0_req_completed) {
+ udc->ep0state = bcm63xx_ep0_do_setup(udc);
+ return udc->ep0state == EP0_IDLE ? -EAGAIN : 0;
+ } else if (udc->ep0_req_shutdown) {
+ udc->ep0_req_shutdown = 0;
+ udc->ep0_req_completed = 0;
+ udc->ep0_request = NULL;
+ iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_RXCHAN]);
+ usb_gadget_unmap_request(&udc->gadget,
+ &udc->ep0_ctrl_req.req, 0);
+
+ /* bcm63xx_udc_pullup() is waiting for this */
+ mb();
+ udc->ep0state = EP0_SHUTDOWN;
+ } else if (udc->ep0_reply) {
+ /*
+ * This could happen if a USB RESET shows up during an ep0
+ * transaction (especially if a laggy driver like gadgetfs
+ * is in use).
+ */
+ dev_warn(udc->dev, "nuking unexpected reply\n");
+ bcm63xx_ep0_nuke_reply(udc, 0);
+ } else {
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/**
+ * bcm63xx_ep0_one_round - Handle the current ep0 state.
+ * @udc: Reference to the device controller.
+ *
+ * Returns 0 if work was done; -EAGAIN if nothing to do.
+ */
+static int bcm63xx_ep0_one_round(struct bcm63xx_udc *udc)
+{
+ enum bcm63xx_ep0_state ep0state = udc->ep0state;
+ bool shutdown = udc->ep0_req_reset || udc->ep0_req_shutdown;
+
+ switch (udc->ep0state) {
+ case EP0_REQUEUE:
+ /* set up descriptor to receive SETUP packet */
+ bcm63xx_ep0_internal_request(udc, IUDMA_EP0_RXCHAN,
+ BCM63XX_MAX_CTRL_PKT);
+ ep0state = EP0_IDLE;
+ break;
+ case EP0_IDLE:
+ return bcm63xx_ep0_do_idle(udc);
+ case EP0_IN_DATA_PHASE_SETUP:
+ /*
+ * Normal case: TX request is in ep0_reply (queued by the
+ * callback), or will be queued shortly. When it's here,
+ * send it to the HW and go to EP0_IN_DATA_PHASE_COMPLETE.
+ *
+ * Shutdown case: Stop waiting for the reply. Just
+ * REQUEUE->IDLE. The gadget driver is NOT expected to
+ * queue anything else now.
+ */
+ if (udc->ep0_reply) {
+ bcm63xx_ep0_map_write(udc, IUDMA_EP0_TXCHAN,
+ udc->ep0_reply);
+ ep0state = EP0_IN_DATA_PHASE_COMPLETE;
+ } else if (shutdown) {
+ ep0state = EP0_REQUEUE;
+ }
+ break;
+ case EP0_IN_DATA_PHASE_COMPLETE: {
+ /*
+ * Normal case: TX packet (ep0_reply) is in flight; wait for
+ * it to finish, then go back to REQUEUE->IDLE.
+ *
+ * Shutdown case: Reset the TX channel, send -ESHUTDOWN
+ * completion to the gadget driver, then REQUEUE->IDLE.
+ */
+ if (udc->ep0_req_completed) {
+ udc->ep0_reply = NULL;
+ bcm63xx_ep0_read_complete(udc);
+ /*
+ * the "ack" sometimes gets eaten (see
+ * bcm63xx_ep0_do_idle)
+ */
+ ep0state = EP0_REQUEUE;
+ } else if (shutdown) {
+ iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_TXCHAN]);
+ bcm63xx_ep0_nuke_reply(udc, 1);
+ ep0state = EP0_REQUEUE;
+ }
+ break;
+ }
+ case EP0_OUT_DATA_PHASE_SETUP:
+ /* Similar behavior to EP0_IN_DATA_PHASE_SETUP */
+ if (udc->ep0_reply) {
+ bcm63xx_ep0_map_write(udc, IUDMA_EP0_RXCHAN,
+ udc->ep0_reply);
+ ep0state = EP0_OUT_DATA_PHASE_COMPLETE;
+ } else if (shutdown) {
+ ep0state = EP0_REQUEUE;
+ }
+ break;
+ case EP0_OUT_DATA_PHASE_COMPLETE: {
+ /* Similar behavior to EP0_IN_DATA_PHASE_COMPLETE */
+ if (udc->ep0_req_completed) {
+ udc->ep0_reply = NULL;
+ bcm63xx_ep0_read_complete(udc);
+
+ /* send 0-byte ack to host */
+ bcm63xx_ep0_internal_request(udc, IUDMA_EP0_TXCHAN, 0);
+ ep0state = EP0_OUT_STATUS_PHASE;
+ } else if (shutdown) {
+ iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_RXCHAN]);
+ bcm63xx_ep0_nuke_reply(udc, 0);
+ ep0state = EP0_REQUEUE;
+ }
+ break;
+ }
+ case EP0_OUT_STATUS_PHASE:
+ /*
+ * Normal case: 0-byte OUT ack packet is in flight; wait
+ * for it to finish, then go back to REQUEUE->IDLE.
+ *
+ * Shutdown case: just cancel the transmission. Don't bother
+ * calling the completion, because it originated from this
+ * function anyway. Then go back to REQUEUE->IDLE.
+ */
+ if (udc->ep0_req_completed) {
+ bcm63xx_ep0_read_complete(udc);
+ ep0state = EP0_REQUEUE;
+ } else if (shutdown) {
+ iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_TXCHAN]);
+ udc->ep0_request = NULL;
+ ep0state = EP0_REQUEUE;
+ }
+ break;
+ case EP0_IN_FAKE_STATUS_PHASE: {
+ /*
+ * Normal case: we spoofed a SETUP packet and are now
+ * waiting for the gadget driver to send a 0-byte reply.
+ * This doesn't actually get sent to the HW because the
+ * HW has already sent its own reply. Once we get the
+ * response, return to IDLE.
+ *
+ * Shutdown case: return to IDLE immediately.
+ *
+ * Note that the ep0 RX descriptor has remained queued
+ * (and possibly unfilled) during this entire transaction.
+ * The HW datapath (IUDMA) never even sees SET_CONFIGURATION
+ * or SET_INTERFACE transactions.
+ */
+ struct usb_request *r = udc->ep0_reply;
+
+ if (!r) {
+ if (shutdown)
+ ep0state = EP0_IDLE;
+ break;
+ }
+
+ bcm63xx_ep0_complete(udc, r, 0);
+ udc->ep0_reply = NULL;
+ ep0state = EP0_IDLE;
+ break;
+ }
+ case EP0_SHUTDOWN:
+ break;
+ }
+
+ if (udc->ep0state == ep0state)
+ return -EAGAIN;
+
+ udc->ep0state = ep0state;
+ return 0;
+}
+
+/**
+ * bcm63xx_ep0_process - ep0 worker thread / state machine.
+ * @w: Workqueue struct.
+ *
+ * bcm63xx_ep0_process is triggered any time an event occurs on ep0. It
+ * is used to synchronize ep0 events and ensure that both HW and SW events
+ * occur in a well-defined order. When the ep0 IUDMA queues are idle, it may
+ * synthesize SET_CONFIGURATION / SET_INTERFACE requests that were consumed
+ * by the USBD hardware.
+ *
+ * The worker function will continue iterating around the state machine
+ * until there is nothing left to do. Usually "nothing left to do" means
+ * that we're waiting for a new event from the hardware.
+ */
+static void bcm63xx_ep0_process(struct work_struct *w)
+{
+ struct bcm63xx_udc *udc = container_of(w, struct bcm63xx_udc, ep0_wq);
+ spin_lock_irq(&udc->lock);
+ while (bcm63xx_ep0_one_round(udc) == 0)
+ ;
+ spin_unlock_irq(&udc->lock);
+}
+
+/***********************************************************************
+ * Standard UDC gadget operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_udc_get_frame - Read current SOF frame number from the HW.
+ * @gadget: USB slave device.
+ */
+static int bcm63xx_udc_get_frame(struct usb_gadget *gadget)
+{
+ struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+
+ return (usbd_readl(udc, USBD_STATUS_REG) &
+ USBD_STATUS_SOF_MASK) >> USBD_STATUS_SOF_SHIFT;
+}
+
+/**
+ * bcm63xx_udc_pullup - Enable/disable pullup on D+ line.
+ * @gadget: USB slave device.
+ * @is_on: 0 to disable pullup, 1 to enable.
+ *
+ * See notes in bcm63xx_select_pullup().
+ */
+static int bcm63xx_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+ unsigned long flags;
+ int i, rc = -EINVAL;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (is_on && udc->ep0state == EP0_SHUTDOWN) {
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->ep0state = EP0_REQUEUE;
+ bcm63xx_fifo_setup(udc);
+ bcm63xx_fifo_reset(udc);
+ bcm63xx_ep_setup(udc);
+
+ bitmap_zero(&udc->wedgemap, BCM63XX_NUM_EP);
+ for (i = 0; i < BCM63XX_NUM_EP; i++)
+ bcm63xx_set_stall(udc, &udc->bep[i], false);
+
+ bcm63xx_set_ctrl_irqs(udc, true);
+ bcm63xx_select_pullup(gadget_to_udc(gadget), true);
+ rc = 0;
+ } else if (!is_on && udc->ep0state != EP0_SHUTDOWN) {
+ bcm63xx_select_pullup(gadget_to_udc(gadget), false);
+
+ udc->ep0_req_shutdown = 1;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ while (1) {
+ schedule_work(&udc->ep0_wq);
+ if (udc->ep0state == EP0_SHUTDOWN)
+ break;
+ msleep(50);
+ }
+ bcm63xx_set_ctrl_irqs(udc, false);
+ cancel_work_sync(&udc->ep0_wq);
+ return 0;
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return rc;
+}
+
+/**
+ * bcm63xx_udc_start - Start the controller.
+ * @gadget: USB slave device.
+ * @driver: Driver for USB slave devices.
+ */
+static int bcm63xx_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+ unsigned long flags;
+
+ if (!driver || driver->max_speed < USB_SPEED_HIGH ||
+ !driver->setup)
+ return -EINVAL;
+ if (!udc)
+ return -ENODEV;
+ if (udc->driver)
+ return -EBUSY;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ set_clocks(udc, true);
+ bcm63xx_fifo_setup(udc);
+ bcm63xx_ep_init(udc);
+ bcm63xx_ep_setup(udc);
+ bcm63xx_fifo_reset(udc);
+ bcm63xx_select_phy_mode(udc, true);
+
+ udc->driver = driver;
+ driver->driver.bus = NULL;
+ udc->gadget.dev.driver = &driver->driver;
+ udc->gadget.dev.of_node = udc->dev->of_node;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * bcm63xx_udc_stop - Shut down the controller.
+ * @gadget: USB slave device.
+ * @driver: Driver for USB slave devices.
+ */
+static int bcm63xx_udc_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ udc->driver = NULL;
+ udc->gadget.dev.driver = NULL;
+
+ /*
+ * If we switch the PHY too abruptly after dropping D+, the host
+ * will often complain:
+ *
+ * hub 1-0:1.0: port 1 disabled by hub (EMI?), re-enabling...
+ */
+ msleep(100);
+
+ bcm63xx_select_phy_mode(udc, false);
+ set_clocks(udc, false);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops bcm63xx_udc_ops = {
+ .get_frame = bcm63xx_udc_get_frame,
+ .pullup = bcm63xx_udc_pullup,
+ .udc_start = bcm63xx_udc_start,
+ .udc_stop = bcm63xx_udc_stop,
+};
+
+/***********************************************************************
+ * IRQ handling
+ ***********************************************************************/
+
+/**
+ * bcm63xx_update_cfg_iface - Read current configuration/interface settings.
+ * @udc: Reference to the device controller.
+ *
+ * This controller intercepts SET_CONFIGURATION and SET_INTERFACE messages.
+ * The driver never sees the raw control packets coming in on the ep0
+ * IUDMA channel, but at least we get an interrupt event to tell us that
+ * new values are waiting in the USBD_STATUS register.
+ */
+static void bcm63xx_update_cfg_iface(struct bcm63xx_udc *udc)
+{
+ u32 reg = usbd_readl(udc, USBD_STATUS_REG);
+
+ udc->cfg = (reg & USBD_STATUS_CFG_MASK) >> USBD_STATUS_CFG_SHIFT;
+ udc->iface = (reg & USBD_STATUS_INTF_MASK) >> USBD_STATUS_INTF_SHIFT;
+ udc->alt_iface = (reg & USBD_STATUS_ALTINTF_MASK) >>
+ USBD_STATUS_ALTINTF_SHIFT;
+ bcm63xx_ep_setup(udc);
+}
+
+/**
+ * bcm63xx_update_link_speed - Check to see if the link speed has changed.
+ * @udc: Reference to the device controller.
+ *
+ * The link speed update coincides with a SETUP IRQ. Returns 1 if the
+ * speed has changed, so that the caller can update the endpoint settings.
+ */
+static int bcm63xx_update_link_speed(struct bcm63xx_udc *udc)
+{
+ u32 reg = usbd_readl(udc, USBD_STATUS_REG);
+ enum usb_device_speed oldspeed = udc->gadget.speed;
+
+ switch ((reg & USBD_STATUS_SPD_MASK) >> USBD_STATUS_SPD_SHIFT) {
+ case BCM63XX_SPD_HIGH:
+ udc->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case BCM63XX_SPD_FULL:
+ udc->gadget.speed = USB_SPEED_FULL;
+ break;
+ default:
+ /* this should never happen */
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ dev_err(udc->dev,
+ "received SETUP packet with invalid link speed\n");
+ return 0;
+ }
+
+ if (udc->gadget.speed != oldspeed) {
+ dev_info(udc->dev, "link up, %s-speed mode\n",
+ udc->gadget.speed == USB_SPEED_HIGH ? "high" : "full");
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * bcm63xx_update_wedge - Iterate through wedged endpoints.
+ * @udc: Reference to the device controller.
+ * @new_status: true to "refresh" wedge status; false to clear it.
+ *
+ * On a SETUP interrupt, we need to manually "refresh" the wedge status
+ * because the controller hardware is designed to automatically clear
+ * stalls in response to a CLEAR_FEATURE request from the host.
+ *
+ * On a RESET interrupt, we do want to restore all wedged endpoints.
+ */
+static void bcm63xx_update_wedge(struct bcm63xx_udc *udc, bool new_status)
+{
+ int i;
+
+ for_each_set_bit(i, &udc->wedgemap, BCM63XX_NUM_EP) {
+ bcm63xx_set_stall(udc, &udc->bep[i], new_status);
+ if (!new_status)
+ clear_bit(i, &udc->wedgemap);
+ }
+}
+
+/**
+ * bcm63xx_udc_ctrl_isr - ISR for control path events (USBD).
+ * @irq: IRQ number (unused).
+ * @dev_id: Reference to the device controller.
+ *
+ * This is where we handle link (VBUS) down, USB reset, speed changes,
+ * SET_CONFIGURATION, and SET_INTERFACE events.
+ */
+static irqreturn_t bcm63xx_udc_ctrl_isr(int irq, void *dev_id)
+{
+ struct bcm63xx_udc *udc = dev_id;
+ u32 stat;
+ bool disconnected = false;
+
+ stat = usbd_readl(udc, USBD_EVENT_IRQ_STATUS_REG) &
+ usbd_readl(udc, USBD_EVENT_IRQ_MASK_REG);
+
+ usbd_writel(udc, stat, USBD_EVENT_IRQ_STATUS_REG);
+
+ spin_lock(&udc->lock);
+ if (stat & BIT(USBD_EVENT_IRQ_USB_LINK)) {
+ /* VBUS toggled */
+
+ if (!(usbd_readl(udc, USBD_EVENTS_REG) &
+ USBD_EVENTS_USB_LINK_MASK) &&
+ udc->gadget.speed != USB_SPEED_UNKNOWN)
+ dev_info(udc->dev, "link down\n");
+
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ disconnected = true;
+ }
+ if (stat & BIT(USBD_EVENT_IRQ_USB_RESET)) {
+ bcm63xx_fifo_setup(udc);
+ bcm63xx_fifo_reset(udc);
+ bcm63xx_ep_setup(udc);
+
+ bcm63xx_update_wedge(udc, false);
+
+ udc->ep0_req_reset = 1;
+ schedule_work(&udc->ep0_wq);
+ disconnected = true;
+ }
+ if (stat & BIT(USBD_EVENT_IRQ_SETUP)) {
+ if (bcm63xx_update_link_speed(udc)) {
+ bcm63xx_fifo_setup(udc);
+ bcm63xx_ep_setup(udc);
+ }
+ bcm63xx_update_wedge(udc, true);
+ }
+ if (stat & BIT(USBD_EVENT_IRQ_SETCFG)) {
+ bcm63xx_update_cfg_iface(udc);
+ udc->ep0_req_set_cfg = 1;
+ schedule_work(&udc->ep0_wq);
+ }
+ if (stat & BIT(USBD_EVENT_IRQ_SETINTF)) {
+ bcm63xx_update_cfg_iface(udc);
+ udc->ep0_req_set_iface = 1;
+ schedule_work(&udc->ep0_wq);
+ }
+ spin_unlock(&udc->lock);
+
+ if (disconnected && udc->driver)
+ udc->driver->disconnect(&udc->gadget);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * bcm63xx_udc_data_isr - ISR for data path events (IUDMA).
+ * @irq: IRQ number (unused).
+ * @dev_id: Reference to the IUDMA channel that generated the interrupt.
+ *
+ * For the two ep0 channels, we have special handling that triggers the
+ * ep0 worker thread. For normal bulk/intr channels, either queue up
+ * the next buffer descriptor for the transaction (incomplete transaction),
+ * or invoke the completion callback (complete transactions).
+ */
+static irqreturn_t bcm63xx_udc_data_isr(int irq, void *dev_id)
+{
+ struct iudma_ch *iudma = dev_id;
+ struct bcm63xx_udc *udc = iudma->udc;
+ struct bcm63xx_ep *bep;
+ struct usb_request *req = NULL;
+ struct bcm63xx_req *breq = NULL;
+ int rc;
+ bool is_done = false;
+
+ spin_lock(&udc->lock);
+
+ usb_dmac_writel(udc, ENETDMAC_IR_BUFDONE_MASK,
+ ENETDMAC_IR_REG(iudma->ch_idx));
+ bep = iudma->bep;
+ rc = iudma_read(udc, iudma);
+
+ /* special handling for EP0 RX (0) and TX (1) */
+ if (iudma->ch_idx == IUDMA_EP0_RXCHAN ||
+ iudma->ch_idx == IUDMA_EP0_TXCHAN) {
+ req = udc->ep0_request;
+ breq = our_req(req);
+
+ /* a single request could require multiple submissions */
+ if (rc >= 0) {
+ req->actual += rc;
+
+ if (req->actual >= req->length || breq->bd_bytes > rc) {
+ udc->ep0_req_completed = 1;
+ is_done = true;
+ schedule_work(&udc->ep0_wq);
+
+ /* "actual" on a ZLP is 1 byte */
+ req->actual = min(req->actual, req->length);
+ } else {
+ /* queue up the next BD (same request) */
+ iudma_write(udc, iudma, breq);
+ }
+ }
+ } else if (!list_empty(&bep->queue)) {
+ breq = list_first_entry(&bep->queue, struct bcm63xx_req, queue);
+ req = &breq->req;
+
+ if (rc >= 0) {
+ req->actual += rc;
+
+ if (req->actual >= req->length || breq->bd_bytes > rc) {
+ is_done = true;
+ list_del(&breq->queue);
+
+ req->actual = min(req->actual, req->length);
+
+ if (!list_empty(&bep->queue)) {
+ struct bcm63xx_req *next;
+
+ next = list_first_entry(&bep->queue,
+ struct bcm63xx_req, queue);
+ iudma_write(udc, iudma, next);
+ }
+ } else {
+ iudma_write(udc, iudma, breq);
+ }
+ }
+ }
+ spin_unlock(&udc->lock);
+
+ if (is_done) {
+ usb_gadget_unmap_request(&udc->gadget, req, iudma->is_tx);
+ if (req->complete)
+ req->complete(&bep->ep, req);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/***********************************************************************
+ * Debug filesystem
+ ***********************************************************************/
+
+/*
+ * bcm63xx_usbd_dbg_show - Show USBD controller state.
+ * @s: seq_file to which the information will be written.
+ * @p: Unused.
+ *
+ * This file nominally shows up as /sys/kernel/debug/bcm63xx_udc/usbd
+ */
+static int bcm63xx_usbd_dbg_show(struct seq_file *s, void *p)
+{
+ struct bcm63xx_udc *udc = s->private;
+
+ if (!udc->driver)
+ return -ENODEV;
+
+ seq_printf(s, "ep0 state: %s\n",
+ bcm63xx_ep0_state_names[udc->ep0state]);
+ seq_printf(s, " pending requests: %s%s%s%s%s%s%s\n",
+ udc->ep0_req_reset ? "reset " : "",
+ udc->ep0_req_set_cfg ? "set_cfg " : "",
+ udc->ep0_req_set_iface ? "set_iface " : "",
+ udc->ep0_req_shutdown ? "shutdown " : "",
+ udc->ep0_request ? "pending " : "",
+ udc->ep0_req_completed ? "completed " : "",
+ udc->ep0_reply ? "reply " : "");
+ seq_printf(s, "cfg: %d; iface: %d; alt_iface: %d\n",
+ udc->cfg, udc->iface, udc->alt_iface);
+ seq_printf(s, "regs:\n");
+ seq_printf(s, " control: %08x; straps: %08x; status: %08x\n",
+ usbd_readl(udc, USBD_CONTROL_REG),
+ usbd_readl(udc, USBD_STRAPS_REG),
+ usbd_readl(udc, USBD_STATUS_REG));
+ seq_printf(s, " events: %08x; stall: %08x\n",
+ usbd_readl(udc, USBD_EVENTS_REG),
+ usbd_readl(udc, USBD_STALL_REG));
+
+ return 0;
+}
+
+/*
+ * bcm63xx_iudma_dbg_show - Show IUDMA status and descriptors.
+ * @s: seq_file to which the information will be written.
+ * @p: Unused.
+ *
+ * This file nominally shows up as /sys/kernel/debug/bcm63xx_udc/iudma
+ */
+static int bcm63xx_iudma_dbg_show(struct seq_file *s, void *p)
+{
+ struct bcm63xx_udc *udc = s->private;
+ int ch_idx, i;
+ u32 sram2, sram3;
+
+ if (!udc->driver)
+ return -ENODEV;
+
+ for (ch_idx = 0; ch_idx < BCM63XX_NUM_IUDMA; ch_idx++) {
+ struct iudma_ch *iudma = &udc->iudma[ch_idx];
+ struct list_head *pos;
+
+ seq_printf(s, "IUDMA channel %d -- ", ch_idx);
+ switch (iudma_defaults[ch_idx].ep_type) {
+ case BCMEP_CTRL:
+ seq_printf(s, "control");
+ break;
+ case BCMEP_BULK:
+ seq_printf(s, "bulk");
+ break;
+ case BCMEP_INTR:
+ seq_printf(s, "interrupt");
+ break;
+ }
+ seq_printf(s, ch_idx & 0x01 ? " tx" : " rx");
+ seq_printf(s, " [ep%d]:\n",
+ max_t(int, iudma_defaults[ch_idx].ep_num, 0));
+ seq_printf(s, " cfg: %08x; irqstat: %08x; irqmask: %08x; maxburst: %08x\n",
+ usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG(ch_idx)),
+ usb_dmac_readl(udc, ENETDMAC_IR_REG(ch_idx)),
+ usb_dmac_readl(udc, ENETDMAC_IRMASK_REG(ch_idx)),
+ usb_dmac_readl(udc, ENETDMAC_MAXBURST_REG(ch_idx)));
+
+ sram2 = usb_dmas_readl(udc, ENETDMAS_SRAM2_REG(ch_idx));
+ sram3 = usb_dmas_readl(udc, ENETDMAS_SRAM3_REG(ch_idx));
+ seq_printf(s, " base: %08x; index: %04x_%04x; desc: %04x_%04x %08x\n",
+ usb_dmas_readl(udc, ENETDMAS_RSTART_REG(ch_idx)),
+ sram2 >> 16, sram2 & 0xffff,
+ sram3 >> 16, sram3 & 0xffff,
+ usb_dmas_readl(udc, ENETDMAS_SRAM4_REG(ch_idx)));
+ seq_printf(s, " desc: %d/%d used", iudma->n_bds_used,
+ iudma->n_bds);
+
+ if (iudma->bep) {
+ i = 0;
+ list_for_each(pos, &iudma->bep->queue)
+ i++;
+ seq_printf(s, "; %d queued\n", i);
+ } else {
+ seq_printf(s, "\n");
+ }
+
+ for (i = 0; i < iudma->n_bds; i++) {
+ struct bcm_enet_desc *d = &iudma->bd_ring[i];
+
+ seq_printf(s, " %03x (%02x): len_stat: %04x_%04x; pa %08x",
+ i * sizeof(*d), i,
+ d->len_stat >> 16, d->len_stat & 0xffff,
+ d->address);
+ if (d == iudma->read_bd)
+ seq_printf(s, " <<RD");
+ if (d == iudma->write_bd)
+ seq_printf(s, " <<WR");
+ seq_printf(s, "\n");
+ }
+
+ seq_printf(s, "\n");
+ }
+
+ return 0;
+}
+
+static int bcm63xx_usbd_dbg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, bcm63xx_usbd_dbg_show, inode->i_private);
+}
+
+static int bcm63xx_iudma_dbg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, bcm63xx_iudma_dbg_show, inode->i_private);
+}
+
+static const struct file_operations usbd_dbg_fops = {
+ .owner = THIS_MODULE,
+ .open = bcm63xx_usbd_dbg_open,
+ .llseek = seq_lseek,
+ .read = seq_read,
+ .release = single_release,
+};
+
+static const struct file_operations iudma_dbg_fops = {
+ .owner = THIS_MODULE,
+ .open = bcm63xx_iudma_dbg_open,
+ .llseek = seq_lseek,
+ .read = seq_read,
+ .release = single_release,
+};
+
+
+/**
+ * bcm63xx_udc_init_debugfs - Create debugfs entries.
+ * @udc: Reference to the device controller.
+ */
+static void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc)
+{
+ struct dentry *root, *usbd, *iudma;
+
+ if (!IS_ENABLED(CONFIG_USB_GADGET_DEBUG_FS))
+ return;
+
+ root = debugfs_create_dir(udc->gadget.name, NULL);
+ if (IS_ERR(root) || !root)
+ goto err_root;
+
+ usbd = debugfs_create_file("usbd", 0400, root, udc,
+ &usbd_dbg_fops);
+ if (!usbd)
+ goto err_usbd;
+ iudma = debugfs_create_file("iudma", 0400, root, udc,
+ &iudma_dbg_fops);
+ if (!iudma)
+ goto err_iudma;
+
+ udc->debugfs_root = root;
+ udc->debugfs_usbd = usbd;
+ udc->debugfs_iudma = iudma;
+ return;
+err_iudma:
+ debugfs_remove(usbd);
+err_usbd:
+ debugfs_remove(root);
+err_root:
+ dev_err(udc->dev, "debugfs is not available\n");
+}
+
+/**
+ * bcm63xx_udc_cleanup_debugfs - Remove debugfs entries.
+ * @udc: Reference to the device controller.
+ *
+ * debugfs_remove() is safe to call with a NULL argument.
+ */
+static void bcm63xx_udc_cleanup_debugfs(struct bcm63xx_udc *udc)
+{
+ debugfs_remove(udc->debugfs_iudma);
+ debugfs_remove(udc->debugfs_usbd);
+ debugfs_remove(udc->debugfs_root);
+ udc->debugfs_iudma = NULL;
+ udc->debugfs_usbd = NULL;
+ udc->debugfs_root = NULL;
+}
+
+/***********************************************************************
+ * Driver init/exit
+ ***********************************************************************/
+
+/**
+ * bcm63xx_udc_gadget_release - Called from device_release().
+ * @dev: Unused.
+ *
+ * We get a warning if this function doesn't exist, but it's empty because
+ * we don't have to free any of the memory allocated with the devm_* APIs.
+ */
+static void bcm63xx_udc_gadget_release(struct device *dev)
+{
+}
+
+/**
+ * bcm63xx_udc_probe - Initialize a new instance of the UDC.
+ * @pdev: Platform device struct from the bcm63xx BSP code.
+ *
+ * Note that platform data is required, because pd.port_no varies from chip
+ * to chip and is used to switch the correct USB port to device mode.
+ */
+static int __devinit bcm63xx_udc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct bcm63xx_usbd_platform_data *pd = dev->platform_data;
+ struct bcm63xx_udc *udc;
+ struct resource *res;
+ int rc = -ENOMEM, i, irq;
+
+ udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
+ if (!udc) {
+ dev_err(dev, "cannot allocate memory\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, udc);
+ udc->dev = dev;
+ udc->pd = pd;
+
+ if (!pd) {
+ dev_err(dev, "missing platform data\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "error finding USBD resource\n");
+ return -ENXIO;
+ }
+ udc->usbd_regs = devm_request_and_ioremap(dev, res);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(dev, "error finding IUDMA resource\n");
+ return -ENXIO;
+ }
+ udc->iudma_regs = devm_request_and_ioremap(dev, res);
+
+ if (!udc->usbd_regs || !udc->iudma_regs) {
+ dev_err(dev, "error requesting resources\n");
+ return -ENXIO;
+ }
+
+ spin_lock_init(&udc->lock);
+ INIT_WORK(&udc->ep0_wq, bcm63xx_ep0_process);
+ dev_set_name(&udc->gadget.dev, "gadget");
+
+ udc->gadget.ops = &bcm63xx_udc_ops;
+ udc->gadget.name = dev_name(dev);
+ udc->gadget.dev.parent = dev;
+ udc->gadget.dev.release = bcm63xx_udc_gadget_release;
+ udc->gadget.dev.dma_mask = dev->dma_mask;
+
+ if (!pd->use_fullspeed && !use_fullspeed)
+ udc->gadget.max_speed = USB_SPEED_HIGH;
+ else
+ udc->gadget.max_speed = USB_SPEED_FULL;
+
+ /* request clocks, allocate buffers, and clear any pending IRQs */
+ rc = bcm63xx_init_udc_hw(udc);
+ if (rc)
+ return rc;
+
+ rc = -ENXIO;
+
+ /* IRQ resource #0: control interrupt (VBUS, speed, etc.) */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "missing IRQ resource #0\n");
+ goto out_uninit;
+ }
+ if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0,
+ dev_name(dev), udc) < 0) {
+ dev_err(dev, "error requesting IRQ #%d\n", irq);
+ goto out_uninit;
+ }
+
+ /* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+ irq = platform_get_irq(pdev, i + 1);
+ if (irq < 0) {
+ dev_err(dev, "missing IRQ resource #%d\n", i + 1);
+ goto out_uninit;
+ }
+ if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0,
+ dev_name(dev), &udc->iudma[i]) < 0) {
+ dev_err(dev, "error requesting IRQ #%d\n", irq);
+ goto out_uninit;
+ }
+ }
+
+ rc = device_register(&udc->gadget.dev);
+ if (rc)
+ goto out_uninit;
+
+ bcm63xx_udc_init_debugfs(udc);
+ rc = usb_add_gadget_udc(dev, &udc->gadget);
+ if (!rc)
+ return 0;
+
+ bcm63xx_udc_cleanup_debugfs(udc);
+ device_unregister(&udc->gadget.dev);
+out_uninit:
+ bcm63xx_uninit_udc_hw(udc);
+ return rc;
+}
+
+/**
+ * bcm63xx_udc_remove - Remove the device from the system.
+ * @pdev: Platform device struct from the bcm63xx BSP code.
+ */
+static int __devexit bcm63xx_udc_remove(struct platform_device *pdev)
+{
+ struct bcm63xx_udc *udc = platform_get_drvdata(pdev);
+
+ bcm63xx_udc_cleanup_debugfs(udc);
+ usb_del_gadget_udc(&udc->gadget);
+ device_unregister(&udc->gadget.dev);
+ BUG_ON(udc->driver);
+
+ platform_set_drvdata(pdev, NULL);
+ bcm63xx_uninit_udc_hw(udc);
+
+ return 0;
+}
+
+static struct platform_driver bcm63xx_udc_driver = {
+ .probe = bcm63xx_udc_probe,
+ .remove = __devexit_p(bcm63xx_udc_remove),
+ .driver = {
+ .name = DRV_MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(bcm63xx_udc_driver);
+
+MODULE_DESCRIPTION("BCM63xx USB Peripheral Controller");
+MODULE_AUTHOR("Kevin Cernekee <cernekee@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_MODULE_NAME);
diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c
index 725550f06fab..1e4bb77f00bb 100644
--- a/drivers/usb/gadget/cdc2.c
+++ b/drivers/usb/gadget/cdc2.c
@@ -11,7 +11,6 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/module.h>
#include "u_ether.h"
@@ -34,6 +33,7 @@
#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
/*
* Kbuild is not very cooperative with respect to linking separately
@@ -43,10 +43,6 @@
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
#include "u_serial.c"
#include "f_acm.c"
#include "f_ecm.c"
@@ -92,15 +88,10 @@ static const struct usb_descriptor_header *otg_desc[] = {
/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -152,7 +143,6 @@ static struct usb_configuration cdc_config_driver = {
static int __init cdc_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
@@ -172,47 +162,22 @@ static int __init cdc_bind(struct usb_composite_dev *cdev)
if (status < 0)
goto fail0;
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else {
- /* We assume that can_support_ecm() tells the truth;
- * but if the controller isn't recognized at all then
- * that assumption is a bit more likely to be wrong.
- */
- WARNING(cdev, "controller '%s' not recognized; trying %s\n",
- gadget->name,
- cdc_config_driver.label);
- device_desc.bcdDevice =
- cpu_to_le16(0x0300 | 0x0099);
- }
-
-
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail1;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail1;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &cdc_config_driver, cdc_do_config);
if (status < 0)
goto fail1;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
DRIVER_DESC);
@@ -232,11 +197,12 @@ static int __exit cdc_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static struct usb_composite_driver cdc_driver = {
+static __refdata struct usb_composite_driver cdc_driver = {
.name = "g_cdc",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = cdc_bind,
.unbind = __exit_p(cdc_unbind),
};
@@ -246,7 +212,7 @@ MODULE_LICENSE("GPL");
static int __init init(void)
{
- return usb_composite_probe(&cdc_driver, cdc_bind);
+ return usb_composite_probe(&cdc_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 3f72110da1b0..957f973dd96a 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -28,44 +28,6 @@
* with the relevant device-wide data.
*/
-/* big enough to hold our biggest descriptor */
-#define USB_BUFSIZ 1024
-
-static struct usb_composite_driver *composite;
-static int (*composite_gadget_bind)(struct usb_composite_dev *cdev);
-
-/* Some systems will need runtime overrides for the product identifiers
- * published in the device descriptor, either numbers or strings or both.
- * String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
- */
-
-static ushort idVendor;
-module_param(idVendor, ushort, 0644);
-MODULE_PARM_DESC(idVendor, "USB Vendor ID");
-
-static ushort idProduct;
-module_param(idProduct, ushort, 0644);
-MODULE_PARM_DESC(idProduct, "USB Product ID");
-
-static ushort bcdDevice;
-module_param(bcdDevice, ushort, 0644);
-MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
-
-static char *iManufacturer;
-module_param(iManufacturer, charp, 0644);
-MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
-
-static char *iProduct;
-module_param(iProduct, charp, 0644);
-MODULE_PARM_DESC(iProduct, "USB Product string");
-
-static char *iSerialNumber;
-module_param(iSerialNumber, charp, 0644);
-MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
-
-static char composite_manufacturer[50];
-
-/*-------------------------------------------------------------------------*/
/**
* next_ep_desc() - advance to the next EP descriptor
* @t: currect pointer within descriptor array
@@ -192,6 +154,7 @@ ep_found:
}
return 0;
}
+EXPORT_SYMBOL_GPL(config_ep_by_speed);
/**
* usb_add_function() - add a function to a configuration
@@ -250,6 +213,7 @@ done:
function->name, function, value);
return value;
}
+EXPORT_SYMBOL_GPL(usb_add_function);
/**
* usb_function_deactivate - prevent function and gadget enumeration
@@ -286,6 +250,7 @@ int usb_function_deactivate(struct usb_function *function)
spin_unlock_irqrestore(&cdev->lock, flags);
return status;
}
+EXPORT_SYMBOL_GPL(usb_function_deactivate);
/**
* usb_function_activate - allow function and gadget enumeration
@@ -300,9 +265,10 @@ int usb_function_deactivate(struct usb_function *function)
int usb_function_activate(struct usb_function *function)
{
struct usb_composite_dev *cdev = function->config->cdev;
+ unsigned long flags;
int status = 0;
- spin_lock(&cdev->lock);
+ spin_lock_irqsave(&cdev->lock, flags);
if (WARN_ON(cdev->deactivations == 0))
status = -EINVAL;
@@ -312,9 +278,10 @@ int usb_function_activate(struct usb_function *function)
status = usb_gadget_connect(cdev->gadget);
}
- spin_unlock(&cdev->lock);
+ spin_unlock_irqrestore(&cdev->lock, flags);
return status;
}
+EXPORT_SYMBOL_GPL(usb_function_activate);
/**
* usb_interface_id() - allocate an unused interface ID
@@ -351,16 +318,18 @@ int usb_interface_id(struct usb_configuration *config,
}
return -ENODEV;
}
+EXPORT_SYMBOL_GPL(usb_interface_id);
static int config_buf(struct usb_configuration *config,
enum usb_device_speed speed, void *buf, u8 type)
{
struct usb_config_descriptor *c = buf;
void *next = buf + USB_DT_CONFIG_SIZE;
- int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
+ int len;
struct usb_function *f;
int status;
+ len = USB_COMP_EP0_BUFSIZ - USB_DT_CONFIG_SIZE;
/* write the config descriptor */
c = buf;
c->bLength = USB_DT_CONFIG_SIZE;
@@ -790,6 +759,7 @@ done:
config->bConfigurationValue, status);
return status;
}
+EXPORT_SYMBOL_GPL(usb_add_config);
static void remove_config(struct usb_composite_dev *cdev,
struct usb_configuration *config)
@@ -889,10 +859,10 @@ static int lookup_string(
static int get_string(struct usb_composite_dev *cdev,
void *buf, u16 language, int id)
{
+ struct usb_composite_driver *composite = cdev->driver;
struct usb_configuration *c;
struct usb_function *f;
int len;
- const char *str;
/* Yes, not only is USB's I18N support probably more than most
* folk will ever care about ... also, it's all supported here.
@@ -932,26 +902,6 @@ static int get_string(struct usb_composite_dev *cdev,
return s->bLength;
}
- /* Otherwise, look up and return a specified string. First
- * check if the string has not been overridden.
- */
- if (cdev->manufacturer_override == id)
- str = iManufacturer ?: composite->iManufacturer ?:
- composite_manufacturer;
- else if (cdev->product_override == id)
- str = iProduct ?: composite->iProduct;
- else if (cdev->serial_override == id)
- str = iSerialNumber ?: composite->iSerialNumber;
- else
- str = NULL;
- if (str) {
- struct usb_gadget_strings strings = {
- .language = language,
- .strings = &(struct usb_string) { 0xff, str }
- };
- return usb_gadget_get_string(&strings, 0xff, buf);
- }
-
/* String IDs are device-scoped, so we look up each string
* table we're told about. These lookups are infrequent;
* simpler-is-better here.
@@ -1003,6 +953,7 @@ int usb_string_id(struct usb_composite_dev *cdev)
}
return -ENODEV;
}
+EXPORT_SYMBOL_GPL(usb_string_id);
/**
* usb_string_ids() - allocate unused string IDs in batch
@@ -1034,6 +985,7 @@ int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
return 0;
}
+EXPORT_SYMBOL_GPL(usb_string_ids_tab);
/**
* usb_string_ids_n() - allocate unused string IDs in batch
@@ -1062,7 +1014,7 @@ int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)
c->next_string_id += n;
return next + 1;
}
-
+EXPORT_SYMBOL_GPL(usb_string_ids_n);
/*-------------------------------------------------------------------------*/
@@ -1359,8 +1311,8 @@ static void composite_disconnect(struct usb_gadget *gadget)
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->config)
reset_config(cdev);
- if (composite->disconnect)
- composite->disconnect(cdev);
+ if (cdev->driver->disconnect)
+ cdev->driver->disconnect(cdev);
spin_unlock_irqrestore(&cdev->lock, flags);
}
@@ -1396,35 +1348,67 @@ composite_unbind(struct usb_gadget *gadget)
struct usb_configuration, list);
remove_config(cdev, c);
}
- if (composite->unbind)
- composite->unbind(cdev);
+ if (cdev->driver->unbind)
+ cdev->driver->unbind(cdev);
if (cdev->req) {
kfree(cdev->req->buf);
usb_ep_free_request(gadget->ep0, cdev->req);
}
device_remove_file(&gadget->dev, &dev_attr_suspended);
+ kfree(cdev->def_manufacturer);
kfree(cdev);
set_gadget_data(gadget, NULL);
- composite = NULL;
}
-static u8 override_id(struct usb_composite_dev *cdev, u8 *desc)
+static void update_unchanged_dev_desc(struct usb_device_descriptor *new,
+ const struct usb_device_descriptor *old)
{
- if (!*desc) {
- int ret = usb_string_id(cdev);
- if (unlikely(ret < 0))
- WARNING(cdev, "failed to override string ID\n");
- else
- *desc = ret;
- }
+ __le16 idVendor;
+ __le16 idProduct;
+ __le16 bcdDevice;
+ u8 iSerialNumber;
+ u8 iManufacturer;
+ u8 iProduct;
- return *desc;
+ /*
+ * these variables may have been set in
+ * usb_composite_overwrite_options()
+ */
+ idVendor = new->idVendor;
+ idProduct = new->idProduct;
+ bcdDevice = new->bcdDevice;
+ iSerialNumber = new->iSerialNumber;
+ iManufacturer = new->iManufacturer;
+ iProduct = new->iProduct;
+
+ *new = *old;
+ if (idVendor)
+ new->idVendor = idVendor;
+ if (idProduct)
+ new->idProduct = idProduct;
+ if (bcdDevice)
+ new->bcdDevice = bcdDevice;
+ else
+ new->bcdDevice = cpu_to_le16(get_default_bcdDevice());
+ if (iSerialNumber)
+ new->iSerialNumber = iSerialNumber;
+ if (iManufacturer)
+ new->iManufacturer = iManufacturer;
+ if (iProduct)
+ new->iProduct = iProduct;
}
-static int composite_bind(struct usb_gadget *gadget)
+static struct usb_composite_driver *to_cdriver(struct usb_gadget_driver *gdrv)
+{
+ return container_of(gdrv, struct usb_composite_driver, gadget_driver);
+}
+
+static int composite_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *gdriver)
{
struct usb_composite_dev *cdev;
+ struct usb_composite_driver *composite = to_cdriver(gdriver);
int status = -ENOMEM;
cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
@@ -1440,13 +1424,12 @@ static int composite_bind(struct usb_gadget *gadget)
cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!cdev->req)
goto fail;
- cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
+ cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);
if (!cdev->req->buf)
goto fail;
cdev->req->complete = composite_setup_complete;
gadget->ep0->driver_data = cdev;
- cdev->bufsiz = USB_BUFSIZ;
cdev->driver = composite;
/*
@@ -1467,49 +1450,11 @@ static int composite_bind(struct usb_gadget *gadget)
* serial number), register function drivers, potentially update
* power state and consumption, etc
*/
- status = composite_gadget_bind(cdev);
+ status = composite->bind(cdev);
if (status < 0)
goto fail;
- cdev->desc = *composite->dev;
-
- /* standardized runtime overrides for device ID data */
- if (idVendor)
- cdev->desc.idVendor = cpu_to_le16(idVendor);
- else
- idVendor = le16_to_cpu(cdev->desc.idVendor);
- if (idProduct)
- cdev->desc.idProduct = cpu_to_le16(idProduct);
- else
- idProduct = le16_to_cpu(cdev->desc.idProduct);
- if (bcdDevice)
- cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
- else
- bcdDevice = le16_to_cpu(cdev->desc.bcdDevice);
-
- /* string overrides */
- if (iManufacturer || !cdev->desc.iManufacturer) {
- if (!iManufacturer && !composite->iManufacturer &&
- !*composite_manufacturer)
- snprintf(composite_manufacturer,
- sizeof composite_manufacturer,
- "%s %s with %s",
- init_utsname()->sysname,
- init_utsname()->release,
- gadget->name);
-
- cdev->manufacturer_override =
- override_id(cdev, &cdev->desc.iManufacturer);
- }
-
- if (iProduct || (!cdev->desc.iProduct && composite->iProduct))
- cdev->product_override =
- override_id(cdev, &cdev->desc.iProduct);
-
- if (iSerialNumber ||
- (!cdev->desc.iSerialNumber && composite->iSerialNumber))
- cdev->serial_override =
- override_id(cdev, &cdev->desc.iSerialNumber);
+ update_unchanged_dev_desc(&cdev->desc, composite->dev);
/* has userspace failed to provide a serial number? */
if (composite->needs_serial && !cdev->desc.iSerialNumber)
@@ -1546,8 +1491,8 @@ composite_suspend(struct usb_gadget *gadget)
f->suspend(f);
}
}
- if (composite->suspend)
- composite->suspend(cdev);
+ if (cdev->driver->suspend)
+ cdev->driver->suspend(cdev);
cdev->suspended = 1;
@@ -1565,8 +1510,8 @@ composite_resume(struct usb_gadget *gadget)
* suspend/resume callbacks?
*/
DBG(cdev, "resume\n");
- if (composite->resume)
- composite->resume(cdev);
+ if (cdev->driver->resume)
+ cdev->driver->resume(cdev);
if (cdev->config) {
list_for_each_entry(f, &cdev->config->functions, list) {
if (f->resume)
@@ -1584,13 +1529,8 @@ composite_resume(struct usb_gadget *gadget)
/*-------------------------------------------------------------------------*/
-static struct usb_gadget_driver composite_driver = {
-#ifdef CONFIG_USB_GADGET_SUPERSPEED
- .max_speed = USB_SPEED_SUPER,
-#else
- .max_speed = USB_SPEED_HIGH,
-#endif
-
+static const struct usb_gadget_driver composite_driver_template = {
+ .bind = composite_bind,
.unbind = composite_unbind,
.setup = composite_setup,
@@ -1623,25 +1563,26 @@ static struct usb_gadget_driver composite_driver = {
* while it was binding. That would usually be done in order to wait for
* some userspace participation.
*/
-int usb_composite_probe(struct usb_composite_driver *driver,
- int (*bind)(struct usb_composite_dev *cdev))
+int usb_composite_probe(struct usb_composite_driver *driver)
{
- if (!driver || !driver->dev || !bind || composite)
+ struct usb_gadget_driver *gadget_driver;
+
+ if (!driver || !driver->dev || !driver->bind)
return -EINVAL;
if (!driver->name)
driver->name = "composite";
- if (!driver->iProduct)
- driver->iProduct = driver->name;
- composite_driver.function = (char *) driver->name;
- composite_driver.driver.name = driver->name;
- composite_driver.max_speed =
- min_t(u8, composite_driver.max_speed, driver->max_speed);
- composite = driver;
- composite_gadget_bind = bind;
-
- return usb_gadget_probe_driver(&composite_driver, composite_bind);
+
+ driver->gadget_driver = composite_driver_template;
+ gadget_driver = &driver->gadget_driver;
+
+ gadget_driver->function = (char *) driver->name;
+ gadget_driver->driver.name = driver->name;
+ gadget_driver->max_speed = driver->max_speed;
+
+ return usb_gadget_probe_driver(gadget_driver);
}
+EXPORT_SYMBOL_GPL(usb_composite_probe);
/**
* usb_composite_unregister() - unregister a composite driver
@@ -1652,10 +1593,9 @@ int usb_composite_probe(struct usb_composite_driver *driver,
*/
void usb_composite_unregister(struct usb_composite_driver *driver)
{
- if (composite != driver)
- return;
- usb_gadget_unregister_driver(&composite_driver);
+ usb_gadget_unregister_driver(&driver->gadget_driver);
}
+EXPORT_SYMBOL_GPL(usb_composite_unregister);
/**
* usb_composite_setup_continue() - Continue with the control transfer
@@ -1692,4 +1632,60 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
spin_unlock_irqrestore(&cdev->lock, flags);
}
+EXPORT_SYMBOL_GPL(usb_composite_setup_continue);
+
+static char *composite_default_mfr(struct usb_gadget *gadget)
+{
+ char *mfr;
+ int len;
+
+ len = snprintf(NULL, 0, "%s %s with %s", init_utsname()->sysname,
+ init_utsname()->release, gadget->name);
+ len++;
+ mfr = kmalloc(len, GFP_KERNEL);
+ if (!mfr)
+ return NULL;
+ snprintf(mfr, len, "%s %s with %s", init_utsname()->sysname,
+ init_utsname()->release, gadget->name);
+ return mfr;
+}
+
+void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
+ struct usb_composite_overwrite *covr)
+{
+ struct usb_device_descriptor *desc = &cdev->desc;
+ struct usb_gadget_strings *gstr = cdev->driver->strings[0];
+ struct usb_string *dev_str = gstr->strings;
+
+ if (covr->idVendor)
+ desc->idVendor = cpu_to_le16(covr->idVendor);
+
+ if (covr->idProduct)
+ desc->idProduct = cpu_to_le16(covr->idProduct);
+
+ if (covr->bcdDevice)
+ desc->bcdDevice = cpu_to_le16(covr->bcdDevice);
+
+ if (covr->serial_number) {
+ desc->iSerialNumber = dev_str[USB_GADGET_SERIAL_IDX].id;
+ dev_str[USB_GADGET_SERIAL_IDX].s = covr->serial_number;
+ }
+ if (covr->manufacturer) {
+ desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id;
+ dev_str[USB_GADGET_MANUFACTURER_IDX].s = covr->manufacturer;
+
+ } else if (!strlen(dev_str[USB_GADGET_MANUFACTURER_IDX].s)) {
+ desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id;
+ cdev->def_manufacturer = composite_default_mfr(cdev->gadget);
+ dev_str[USB_GADGET_MANUFACTURER_IDX].s = cdev->def_manufacturer;
+ }
+
+ if (covr->product) {
+ desc->iProduct = dev_str[USB_GADGET_PRODUCT_IDX].id;
+ dev_str[USB_GADGET_PRODUCT_IDX].s = covr->product;
+ }
+}
+EXPORT_SYMBOL_GPL(usb_composite_overwrite_options);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index 7542a72ce51a..e3a98929d346 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -12,6 +12,7 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/device.h>
@@ -53,7 +54,7 @@ usb_descriptor_fillbuf(void *buf, unsigned buflen,
}
return dest - (u8 *)buf;
}
-
+EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf);
/**
* usb_gadget_config_buf - builts a complete configuration descriptor
@@ -106,6 +107,7 @@ int usb_gadget_config_buf(
cp->bmAttributes |= USB_CONFIG_ATT_ONE;
return len;
}
+EXPORT_SYMBOL_GPL(usb_gadget_config_buf);
/**
* usb_copy_descriptors - copy a vector of USB descriptors
@@ -155,4 +157,4 @@ usb_copy_descriptors(struct usb_descriptor_header **src)
return ret;
}
-
+EXPORT_SYMBOL_GPL(usb_copy_descriptors);
diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c
index 19d7bb0df75a..87d165028162 100644
--- a/drivers/usb/gadget/dbgp.c
+++ b/drivers/usb/gadget/dbgp.c
@@ -13,9 +13,6 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-/* See comments in "zero.c" */
-#include "epautoconf.c"
-
#ifdef CONFIG_USB_G_DBGP_SERIAL
#include "u_serial.c"
#endif
@@ -292,7 +289,8 @@ fail_1:
return -ENODEV;
}
-static int __init dbgp_bind(struct usb_gadget *gadget)
+static int __init dbgp_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
int err, stp;
@@ -402,9 +400,10 @@ fail:
return err;
}
-static struct usb_gadget_driver dbgp_driver = {
+static __refdata struct usb_gadget_driver dbgp_driver = {
.function = "dbgp",
.max_speed = USB_SPEED_HIGH,
+ .bind = dbgp_bind,
.unbind = dbgp_unbind,
.setup = dbgp_setup,
.disconnect = dbgp_disconnect,
@@ -416,7 +415,7 @@ static struct usb_gadget_driver dbgp_driver = {
static int __init dbgp_init(void)
{
- return usb_gadget_probe_driver(&dbgp_driver, dbgp_bind);
+ return usb_gadget_probe_driver(&dbgp_driver);
}
static void __exit dbgp_exit(void)
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index afdbb1cbf5d9..0f7541be28f3 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -909,6 +909,7 @@ static int dummy_udc_start(struct usb_gadget *g,
dum->devstatus = 0;
dum->driver = driver;
+ dum->gadget.dev.driver = &driver->driver;
dev_dbg(udc_dev(dum), "binding gadget driver '%s'\n",
driver->driver.name);
return 0;
@@ -923,6 +924,7 @@ static int dummy_udc_stop(struct usb_gadget *g,
dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n",
driver->driver.name);
+ dum->gadget.dev.driver = NULL;
dum->driver = NULL;
return 0;
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 51f3d42f5a64..a777f7bd11b4 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -10,6 +10,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/device.h>
@@ -22,17 +23,6 @@
#include "gadget_chips.h"
-
-/* we must assign addresses for configurable endpoints (like net2280) */
-static unsigned epnum;
-
-// #define MANY_ENDPOINTS
-#ifdef MANY_ENDPOINTS
-/* more than 15 configurable endpoints */
-static unsigned in_epnum;
-#endif
-
-
/*
* This should work with endpoints from controller drivers sharing the
* same endpoint naming convention. By example:
@@ -176,16 +166,14 @@ ep_matches (
if (isdigit (ep->name [2])) {
u8 num = simple_strtoul (&ep->name [2], NULL, 10);
desc->bEndpointAddress |= num;
-#ifdef MANY_ENDPOINTS
} else if (desc->bEndpointAddress & USB_DIR_IN) {
- if (++in_epnum > 15)
+ if (++gadget->in_epnum > 15)
return 0;
- desc->bEndpointAddress = USB_DIR_IN | in_epnum;
-#endif
+ desc->bEndpointAddress = USB_DIR_IN | gadget->in_epnum;
} else {
- if (++epnum > 15)
+ if (++gadget->out_epnum > 15)
return 0;
- desc->bEndpointAddress |= epnum;
+ desc->bEndpointAddress |= gadget->out_epnum;
}
/* report (variable) full speed bulk maxpacket */
@@ -328,6 +316,7 @@ found_ep:
ep->comp_desc = NULL;
return ep;
}
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig_ss);
/**
* usb_ep_autoconfig() - choose an endpoint matching the
@@ -367,7 +356,7 @@ struct usb_ep *usb_ep_autoconfig(
{
return usb_ep_autoconfig_ss(gadget, desc, NULL);
}
-
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig);
/**
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
@@ -385,9 +374,7 @@ void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
ep->driver_data = NULL;
}
-#ifdef MANY_ENDPOINTS
- in_epnum = 0;
-#endif
- epnum = 0;
+ gadget->in_epnum = 0;
+ gadget->out_epnum = 0;
}
-
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig_reset);
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index a28f6ffcd0f3..18c3f423706e 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -14,8 +14,6 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
-#include <linux/utsname.h>
-
#if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
@@ -102,11 +100,6 @@ static inline bool has_rndis(void)
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_ecm.c"
#include "f_subset.c"
#ifdef USB_ETH_RNDIS
@@ -117,6 +110,7 @@ static inline bool has_rndis(void)
#include "u_ether.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
@@ -195,17 +189,10 @@ static const struct usb_descriptor_header *otg_desc[] = {
NULL,
};
-
-/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = PREFIX DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -288,7 +275,6 @@ static struct usb_configuration eth_config_driver = {
static int __init eth_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
@@ -323,42 +309,15 @@ static int __init eth_bind(struct usb_composite_dev *cdev)
device_desc.bNumConfigurations = 2;
}
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else {
- /* We assume that can_support_ecm() tells the truth;
- * but if the controller isn't recognized at all then
- * that assumption is a bit more likely to be wrong.
- */
- dev_warn(&gadget->dev,
- "controller '%s' not recognized; trying %s\n",
- gadget->name,
- eth_config_driver.label);
- device_desc.bcdDevice =
- cpu_to_le16(0x0300 | 0x0099);
- }
-
-
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration(s); RNDIS first, if it's used */
if (has_rndis()) {
@@ -372,6 +331,7 @@ static int __init eth_bind(struct usb_composite_dev *cdev)
if (status < 0)
goto fail;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
DRIVER_DESC);
@@ -388,11 +348,12 @@ static int __exit eth_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static struct usb_composite_driver eth_driver = {
+static __refdata struct usb_composite_driver eth_driver = {
.name = "g_ether",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
+ .bind = eth_bind,
.unbind = __exit_p(eth_unbind),
};
@@ -402,7 +363,7 @@ MODULE_LICENSE("GPL");
static int __init init(void)
{
- return usb_composite_probe(&eth_driver, eth_bind);
+ return usb_composite_probe(&eth_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 30b908f2a53d..95bc94f8e570 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -897,10 +897,7 @@ ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
return -ENOMEM;
/* export host's Ethernet address in CDC format */
- snprintf(ecm->ethaddr, sizeof ecm->ethaddr,
- "%02X%02X%02X%02X%02X%02X",
- ethaddr[0], ethaddr[1], ethaddr[2],
- ethaddr[3], ethaddr[4], ethaddr[5]);
+ snprintf(ecm->ethaddr, sizeof ecm->ethaddr, "%pm", ethaddr);
ecm_string_defs[1].s = ecm->ethaddr;
ecm->port.cdc_filter = DEFAULT_FILTER;
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c
index 16a8b1c15c62..511e527178e2 100644
--- a/drivers/usb/gadget/f_hid.c
+++ b/drivers/usb/gadget/f_hid.c
@@ -10,7 +10,6 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/module.h>
#include <linux/hid.h>
#include <linux/cdev.h>
@@ -18,6 +17,7 @@
#include <linux/poll.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
+#include <linux/sched.h>
#include <linux/usb/g_hid.h>
static int major, minors;
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 4f1142efa6d1..3a7668bde3ef 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -213,7 +213,6 @@
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/freezer.h>
-#include <linux/utsname.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -349,7 +348,6 @@ struct fsg_config {
const char *vendor_name; /* 8 characters or less */
const char *product_name; /* 16 characters or less */
- u16 release;
char can_stall;
};
@@ -2773,18 +2771,7 @@ buffhds_first_it:
bh->next = common->buffhds;
/* Prepare inquiryString */
- if (cfg->release != 0xffff) {
- i = cfg->release;
- } else {
- i = usb_gadget_controller_number(gadget);
- if (i >= 0) {
- i = 0x0300 + i;
- } else {
- WARNING(common, "controller '%s' not recognized\n",
- gadget->name);
- i = 0x0399;
- }
- }
+ i = get_default_bcdDevice();
snprintf(common->inquiry_string, sizeof common->inquiry_string,
"%-8s%-16s%04x", cfg->vendor_name ?: "Linux",
/* Assume product name dependent on the first LUN */
@@ -3110,7 +3097,6 @@ fsg_config_from_params(struct fsg_config *cfg,
/* Let MSF use defaults */
cfg->vendor_name = 0;
cfg->product_name = 0;
- cfg->release = 0xffff;
cfg->ops = NULL;
cfg->private_data = NULL;
diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c
index 2f7e8f2930cc..8ed1259fe80d 100644
--- a/drivers/usb/gadget/f_midi.c
+++ b/drivers/usb/gadget/f_midi.c
@@ -21,7 +21,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/utsname.h>
#include <linux/device.h>
#include <sound/core.h>
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c
index aab8eded045b..b651b529c67f 100644
--- a/drivers/usb/gadget/f_ncm.c
+++ b/drivers/usb/gadget/f_ncm.c
@@ -1346,10 +1346,7 @@ int __init ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
return -ENOMEM;
/* export host's Ethernet address in CDC format */
- snprintf(ncm->ethaddr, sizeof ncm->ethaddr,
- "%02X%02X%02X%02X%02X%02X",
- ethaddr[0], ethaddr[1], ethaddr[2],
- ethaddr[3], ethaddr[4], ethaddr[5]);
+ snprintf(ncm->ethaddr, sizeof ncm->ethaddr, "%pm", ethaddr);
ncm_string_defs[1].s = ncm->ethaddr;
spin_lock_init(&ncm->lock);
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c
index 5c1b68b63c98..3c126fde6e7e 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/f_sourcesink.c
@@ -795,7 +795,7 @@ static int sourcesink_setup(struct usb_configuration *c,
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
- req->length = USB_BUFSIZ;
+ req->length = USB_COMP_EP0_BUFSIZ;
/* composite driver infrastructure handles everything except
* the two control test requests.
diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c
index 21ab474aca07..4060c0bd9785 100644
--- a/drivers/usb/gadget/f_subset.c
+++ b/drivers/usb/gadget/f_subset.c
@@ -436,10 +436,7 @@ int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
return -ENOMEM;
/* export host's Ethernet address in CDC format */
- snprintf(geth->ethaddr, sizeof geth->ethaddr,
- "%02X%02X%02X%02X%02X%02X",
- ethaddr[0], ethaddr[1], ethaddr[2],
- ethaddr[3], ethaddr[4], ethaddr[5]);
+ snprintf(geth->ethaddr, sizeof geth->ethaddr, "%pm", ethaddr);
geth_string_defs[1].s = geth->ethaddr;
geth->port.cdc_filter = DEFAULT_FILTER;
diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c
index e7cc4de93e33..d3c6cffccb72 100644
--- a/drivers/usb/gadget/f_uac2.c
+++ b/drivers/usb/gadget/f_uac2.c
@@ -463,7 +463,7 @@ snd_fail:
return err;
}
-static int __devexit snd_uac2_remove(struct platform_device *pdev)
+static int snd_uac2_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index a896d73f7a93..3f7d640b6758 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -251,26 +251,12 @@
#include <linux/freezer.h>
#include <linux/utsname.h>
+#include <linux/usb/composite.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "gadget_chips.h"
-
-
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
-/*-------------------------------------------------------------------------*/
-
#define DRIVER_DESC "File-backed Storage Gadget"
#define DRIVER_NAME "g_file_storage"
#define DRIVER_VERSION "1 September 2010"
@@ -3213,7 +3199,6 @@ static void /* __init_or_exit */ fsg_unbind(struct usb_gadget *gadget)
static int __init check_parameters(struct fsg_dev *fsg)
{
int prot;
- int gcnum;
/* Store the default values */
mod_data.transport_type = USB_PR_BULK;
@@ -3228,16 +3213,8 @@ static int __init check_parameters(struct fsg_dev *fsg)
if (gadget_is_at91(fsg->gadget))
mod_data.can_stall = 0;
- if (mod_data.release == 0xffff) { // Parameter wasn't set
- gcnum = usb_gadget_controller_number(fsg->gadget);
- if (gcnum >= 0)
- mod_data.release = 0x0300 + gcnum;
- else {
- WARNING(fsg, "controller '%s' not recognized\n",
- fsg->gadget->name);
- mod_data.release = 0x0399;
- }
- }
+ if (mod_data.release == 0xffff)
+ mod_data.release = get_default_bcdDevice();
prot = simple_strtol(mod_data.protocol_parm, NULL, 0);
@@ -3331,7 +3308,8 @@ static int __init check_parameters(struct fsg_dev *fsg)
}
-static int __init fsg_bind(struct usb_gadget *gadget)
+static int __init fsg_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
struct fsg_dev *fsg = the_fsg;
int rc;
@@ -3603,9 +3581,10 @@ static void fsg_resume(struct usb_gadget *gadget)
/*-------------------------------------------------------------------------*/
-static struct usb_gadget_driver fsg_driver = {
+static __refdata struct usb_gadget_driver fsg_driver = {
.max_speed = USB_SPEED_SUPER,
.function = (char *) fsg_string_product,
+ .bind = fsg_bind,
.unbind = fsg_unbind,
.disconnect = fsg_disconnect,
.setup = fsg_setup,
@@ -3653,7 +3632,8 @@ static int __init fsg_init(void)
if ((rc = fsg_alloc()) != 0)
return rc;
fsg = the_fsg;
- if ((rc = usb_gadget_probe_driver(&fsg_driver, fsg_bind)) != 0)
+ rc = usb_gadget_probe_driver(&fsg_driver);
+ if (rc != 0)
kref_put(&fsg->ref, fsg_release);
return rc;
}
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index 3def828f85e7..6ae70cba0c4a 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -1255,7 +1255,7 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on)
}
static int fsl_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int fsl_stop(struct usb_gadget_driver *driver);
/* defined in gadget.h */
static struct usb_gadget_ops fsl_gadget_ops = {
@@ -1951,7 +1951,7 @@ static irqreturn_t fsl_udc_irq(int irq, void *_udc)
* Called by initialization code of gadget drivers
*----------------------------------------------------------------*/
static int fsl_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
int retval = -ENODEV;
unsigned long flags = 0;
@@ -1976,7 +1976,7 @@ static int fsl_start(struct usb_gadget_driver *driver,
spin_unlock_irqrestore(&udc_controller->lock, flags);
/* bind udc driver to gadget driver */
- retval = bind(&udc_controller->gadget);
+ retval = bind(&udc_controller->gadget, driver);
if (retval) {
VDBG("bind to %s --> %d", driver->driver.name, retval);
udc_controller->gadget.dev.driver = NULL;
diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c
index cdd94540e1cd..72cd5e6719db 100644
--- a/drivers/usb/gadget/fusb300_udc.c
+++ b/drivers/usb/gadget/fusb300_udc.c
@@ -1311,7 +1311,7 @@ static void init_controller(struct fusb300 *fusb300)
static struct fusb300 *the_controller;
static int fusb300_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct fusb300 *fusb300 = the_controller;
int retval;
@@ -1339,7 +1339,7 @@ static int fusb300_udc_start(struct usb_gadget_driver *driver,
goto error;
}
- retval = bind(&fusb300->gadget);
+ retval = bind(&fusb300->gadget, driver);
if (retval) {
pr_err("bind to driver error (%d)\n", retval);
device_del(&fusb300->gadget.dev);
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index d3ace9002a6a..3953dd4d7186 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -13,7 +13,6 @@
#define pr_fmt(fmt) "g_ffs: " fmt
#include <linux/module.h>
-#include <linux/utsname.h>
/*
* kbuild is not very cooperative with respect to linking separately
@@ -22,12 +21,6 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
# if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
@@ -76,6 +69,8 @@ struct gfs_ffs_obj {
struct ffs_data *ffs_data;
};
+USB_GADGET_COMPOSITE_OPTIONS();
+
static struct usb_device_descriptor gfs_dev_desc = {
.bLength = sizeof gfs_dev_desc,
.bDescriptorType = USB_DT_DEVICE,
@@ -117,6 +112,9 @@ static const struct usb_descriptor_header *gfs_otg_desc[] = {
/* String IDs are assigned dynamically */
static struct usb_string gfs_strings[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
{ .s = "FunctionFS + RNDIS" },
#endif
@@ -163,13 +161,13 @@ static int gfs_bind(struct usb_composite_dev *cdev);
static int gfs_unbind(struct usb_composite_dev *cdev);
static int gfs_do_config(struct usb_configuration *c);
-static struct usb_composite_driver gfs_driver = {
+static __refdata struct usb_composite_driver gfs_driver = {
.name = DRIVER_NAME,
.dev = &gfs_dev_desc,
.strings = gfs_dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = gfs_bind,
.unbind = gfs_unbind,
- .iProduct = DRIVER_DESC,
};
static DEFINE_MUTEX(gfs_lock);
@@ -268,7 +266,7 @@ static int functionfs_ready_callback(struct ffs_data *ffs)
}
gfs_registered = true;
- ret = usb_composite_probe(&gfs_driver, gfs_bind);
+ ret = usb_composite_probe(&gfs_driver);
if (unlikely(ret < 0))
gfs_registered = false;
@@ -357,6 +355,7 @@ static int gfs_bind(struct usb_composite_dev *cdev)
ret = usb_string_ids_tab(cdev, gfs_strings);
if (unlikely(ret < 0))
goto error;
+ gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id;
for (i = func_num; --i; ) {
ret = functionfs_bind(ffs_tab[i].ffs_data, cdev);
@@ -369,9 +368,10 @@ static int gfs_bind(struct usb_composite_dev *cdev)
for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
struct gfs_configuration *c = gfs_configurations + i;
+ int sid = USB_GADGET_FIRST_AVAIL_IDX + i;
- c->c.label = gfs_strings[i].s;
- c->c.iConfiguration = gfs_strings[i].id;
+ c->c.label = gfs_strings[sid].s;
+ c->c.iConfiguration = gfs_strings[sid].id;
c->c.bConfigurationValue = 1 + i;
c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER;
@@ -379,7 +379,7 @@ static int gfs_bind(struct usb_composite_dev *cdev)
if (unlikely(ret < 0))
goto error_unbind;
}
-
+ usb_composite_overwrite_options(cdev, &coverwrite);
return 0;
error_unbind:
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index b8b3a3411218..bcd04bc66b98 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -15,6 +15,8 @@
#ifndef __GADGET_CHIPS_H
#define __GADGET_CHIPS_H
+#include <linux/usb/gadget.h>
+
/*
* NOTICE: the entries below are alphabetical and should be kept
* that way.
@@ -25,106 +27,12 @@
* If you have forgotten the alphabetical order let VIM/EMACS
* do that for you.
*/
-#define gadget_is_amd5536udc(g) (!strcmp("amd5536udc", (g)->name))
#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name))
-#define gadget_is_atmel_usba(g) (!strcmp("atmel_usba_udc", (g)->name))
-#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
-#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
-#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name))
-#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name))
-#define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name))
-#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name))
#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
-#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name))
-#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name))
-#define gadget_is_lpc32xx(g) (!strcmp("lpc32xx_udc", (g)->name))
-#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name))
#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
-#define gadget_is_net2272(g) (!strcmp("net2272", (g)->name))
#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name))
-#define gadget_is_omap(g) (!strcmp("omap_udc", (g)->name))
-#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name))
#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name))
#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name))
-#define gadget_is_r8a66597(g) (!strcmp("r8a66597_udc", (g)->name))
-#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name))
-#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name))
-#define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name))
-#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
-
-/**
- * usb_gadget_controller_number - support bcdDevice id convention
- * @gadget: the controller being driven
- *
- * Return a 2-digit BCD value associated with the peripheral controller,
- * suitable for use as part of a bcdDevice value, or a negative error code.
- *
- * NOTE: this convention is purely optional, and has no meaning in terms of
- * any USB specification. If you want to use a different convention in your
- * gadget driver firmware -- maybe a more formal revision ID -- feel free.
- *
- * Hosts see these bcdDevice numbers, and are allowed (but not encouraged!)
- * to change their behavior accordingly. For example it might help avoiding
- * some chip bug.
- */
-static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
-{
- if (gadget_is_net2280(gadget))
- return 0x01;
- else if (gadget_is_dummy(gadget))
- return 0x02;
- else if (gadget_is_pxa(gadget))
- return 0x03;
- else if (gadget_is_goku(gadget))
- return 0x06;
- else if (gadget_is_omap(gadget))
- return 0x08;
- else if (gadget_is_pxa27x(gadget))
- return 0x11;
- else if (gadget_is_s3c2410(gadget))
- return 0x12;
- else if (gadget_is_at91(gadget))
- return 0x13;
- else if (gadget_is_imx(gadget))
- return 0x14;
- else if (gadget_is_musbhdrc(gadget))
- return 0x16;
- else if (gadget_is_atmel_usba(gadget))
- return 0x18;
- else if (gadget_is_fsl_usb2(gadget))
- return 0x19;
- else if (gadget_is_amd5536udc(gadget))
- return 0x20;
- else if (gadget_is_m66592(gadget))
- return 0x21;
- else if (gadget_is_fsl_qe(gadget))
- return 0x22;
- else if (gadget_is_ci13xxx_pci(gadget))
- return 0x23;
- else if (gadget_is_langwell(gadget))
- return 0x24;
- else if (gadget_is_r8a66597(gadget))
- return 0x25;
- else if (gadget_is_s3c_hsotg(gadget))
- return 0x26;
- else if (gadget_is_pch(gadget))
- return 0x27;
- else if (gadget_is_ci13xxx_msm(gadget))
- return 0x28;
- else if (gadget_is_renesas_usbhs(gadget))
- return 0x29;
- else if (gadget_is_s3c_hsudc(gadget))
- return 0x30;
- else if (gadget_is_net2272(gadget))
- return 0x31;
- else if (gadget_is_dwc3(gadget))
- return 0x32;
- else if (gadget_is_lpc32xx(gadget))
- return 0x33;
-
- return -ENOENT;
-}
-
/**
* gadget_supports_altsettings - return true if altsettings work
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index 681bd038b1d8..881aab86ae99 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -22,7 +22,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/utsname.h>
#include <linux/module.h>
#include <linux/device.h>
@@ -31,16 +30,13 @@
#include <sound/rawmidi.h>
#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/usb/audio.h>
#include <linux/usb/midi.h>
#include "gadget_chips.h"
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
#include "f_midi.c"
/*-------------------------------------------------------------------------*/
@@ -51,6 +47,8 @@ MODULE_LICENSE("GPL v2");
static const char shortname[] = "g_midi";
static const char longname[] = "MIDI Gadget";
+USB_GADGET_COMPOSITE_OPTIONS();
+
static int index = SNDRV_DEFAULT_IDX1;
module_param(index, int, S_IRUGO);
MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");
@@ -85,9 +83,7 @@ MODULE_PARM_DESC(out_ports, "Number of MIDI output ports");
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_DESCRIPTION_IDX 2
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
@@ -102,8 +98,9 @@ static struct usb_device_descriptor device_desc = {
};
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = "Grey Innovation",
- [STRING_PRODUCT_IDX].s = "MIDI Gadget",
+ [USB_GADGET_MANUFACTURER_IDX].s = "Grey Innovation",
+ [USB_GADGET_PRODUCT_IDX].s = "MIDI Gadget",
+ [USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = "MIDI",
{ } /* end of list */
};
@@ -140,61 +137,35 @@ static int __init midi_bind_config(struct usb_configuration *c)
static int __init midi_bind(struct usb_composite_dev *cdev)
{
- struct usb_gadget *gadget = cdev->gadget;
- int gcnum, status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- return status;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
+ int status;
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
return status;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
-
- /* config description */
- status = usb_string_id(cdev);
- if (status < 0)
- return status;
- strings_dev[STRING_DESCRIPTION_IDX].id = status;
-
- midi_config.iConfiguration = status;
-
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum < 0) {
- /* gmidi is so simple (no altsettings) that
- * it SHOULD NOT have problems with bulk-capable hardware.
- * so warn about unrecognized controllers, don't panic.
- */
- pr_warning("%s: controller '%s' not recognized\n",
- __func__, gadget->name);
- device_desc.bcdDevice = cpu_to_le16(0x9999);
- } else {
- device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
- }
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id;
status = usb_add_config(cdev, &midi_config, midi_bind_config);
if (status < 0)
return status;
-
+ usb_composite_overwrite_options(cdev, &coverwrite);
pr_info("%s\n", longname);
return 0;
}
-static struct usb_composite_driver midi_driver = {
+static __refdata struct usb_composite_driver midi_driver = {
.name = (char *) longname,
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = midi_bind,
.unbind = __exit_p(midi_unbind),
};
static int __init midi_init(void)
{
- return usb_composite_probe(&midi_driver, midi_bind);
+ return usb_composite_probe(&midi_driver);
}
module_init(midi_init);
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index 9fd7886cfa9a..51037cb78604 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -994,7 +994,7 @@ static int goku_get_frame(struct usb_gadget *_gadget)
}
static int goku_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int goku_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops goku_ops = {
@@ -1348,7 +1348,7 @@ static struct goku_udc *the_controller;
* the driver might get unbound.
*/
static int goku_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct goku_udc *dev = the_controller;
int retval;
@@ -1368,7 +1368,7 @@ static int goku_start(struct usb_gadget_driver *driver,
driver->driver.bus = NULL;
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
- retval = bind(&dev->gadget);
+ retval = bind(&dev->gadget, driver);
if (retval) {
DBG(dev, "bind to driver %s --> error %d\n",
driver->driver.name, retval);
diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c
index 3493adf064f5..74130f6c12c0 100644
--- a/drivers/usb/gadget/hid.c
+++ b/drivers/usb/gadget/hid.c
@@ -15,7 +15,10 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+#include "gadget_chips.h"
#define DRIVER_DESC "HID Gadget"
#define DRIVER_VERSION "2010/03/16"
@@ -33,12 +36,6 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_hid.c"
@@ -50,6 +47,7 @@ struct hidg_func_node {
static LIST_HEAD(hidg_func_list);
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
@@ -92,15 +90,10 @@ static const struct usb_descriptor_header *otg_desc[] = {
/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -150,7 +143,7 @@ static int __init hid_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct list_head *tmp;
- int status, gcnum, funcs = 0;
+ int status, funcs = 0;
list_for_each(tmp, &hidg_func_list)
funcs++;
@@ -163,38 +156,22 @@ static int __init hid_bind(struct usb_composite_dev *cdev)
if (status < 0)
return status;
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else
- device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
-
-
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
- if (status < 0)
- return status;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
return status;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &config_driver, do_config);
if (status < 0)
return status;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
return 0;
@@ -242,11 +219,12 @@ static int __devexit hidg_plat_driver_remove(struct platform_device *pdev)
/****************************** Some noise ******************************/
-static struct usb_composite_driver hidg_driver = {
+static __refdata struct usb_composite_driver hidg_driver = {
.name = "g_hid",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = hid_bind,
.unbind = __exit_p(hid_unbind),
};
@@ -272,7 +250,7 @@ static int __init hidg_init(void)
if (status < 0)
return status;
- status = usb_composite_probe(&hidg_driver, hid_bind);
+ status = usb_composite_probe(&hidg_driver);
if (status < 0)
platform_driver_unregister(&hidg_plat_driver);
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index e58b16442971..4bb6d53f2de3 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -828,7 +828,6 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
-#ifdef CONFIG_USB_GADGET_DUALSPEED
case USB_SPEED_HIGH:
/* fails if caller didn't provide that descriptor... */
ep->desc = &data->hs_desc;
@@ -836,7 +835,6 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
-#endif
default:
DBG(data->dev, "unconnected, %s init abandoned\n",
data->name);
@@ -1324,7 +1322,6 @@ static const struct file_operations ep0_io_operations = {
* Unrecognized ep0 requests may be handled in user space.
*/
-#ifdef CONFIG_USB_GADGET_DUALSPEED
static void make_qualifier (struct dev_data *dev)
{
struct usb_qualifier_descriptor qual;
@@ -1347,7 +1344,6 @@ static void make_qualifier (struct dev_data *dev)
memcpy (dev->rbuf, &qual, sizeof qual);
}
-#endif
static int
config_buf (struct dev_data *dev, u8 type, unsigned index)
@@ -1427,7 +1423,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
dev->dev->bMaxPacketSize0 = dev->gadget->ep0->maxpacket;
req->buf = dev->dev;
break;
-#ifdef CONFIG_USB_GADGET_DUALSPEED
case USB_DT_DEVICE_QUALIFIER:
if (!dev->hs_config)
break;
@@ -1437,7 +1432,6 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
break;
case USB_DT_OTHER_SPEED_CONFIG:
// FALLTHROUGH
-#endif
case USB_DT_CONFIG:
value = config_buf (dev,
w_value >> 8,
@@ -1685,8 +1679,8 @@ gadgetfs_unbind (struct usb_gadget *gadget)
static struct dev_data *the_device;
-static int
-gadgetfs_bind (struct usb_gadget *gadget)
+static int gadgetfs_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
struct dev_data *dev = the_device;
@@ -1763,12 +1757,8 @@ gadgetfs_suspend (struct usb_gadget *gadget)
}
static struct usb_gadget_driver gadgetfs_driver = {
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- .max_speed = USB_SPEED_HIGH,
-#else
- .max_speed = USB_SPEED_FULL,
-#endif
.function = (char *) driver_desc,
+ .bind = gadgetfs_bind,
.unbind = gadgetfs_unbind,
.setup = gadgetfs_setup,
.disconnect = gadgetfs_disconnect,
@@ -1783,7 +1773,8 @@ static struct usb_gadget_driver gadgetfs_driver = {
static void gadgetfs_nop(struct usb_gadget *arg) { }
-static int gadgetfs_probe (struct usb_gadget *gadget)
+static int gadgetfs_probe(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
CHIP = gadget->name;
return -EISNAM;
@@ -1791,6 +1782,7 @@ static int gadgetfs_probe (struct usb_gadget *gadget)
static struct usb_gadget_driver probe_driver = {
.max_speed = USB_SPEED_HIGH,
+ .bind = gadgetfs_probe,
.unbind = gadgetfs_nop,
.setup = (void *)gadgetfs_nop,
.disconnect = gadgetfs_nop,
@@ -1900,7 +1892,12 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
/* triggers gadgetfs_bind(); then we can enumerate. */
spin_unlock_irq (&dev->lock);
- value = usb_gadget_probe_driver(&gadgetfs_driver, gadgetfs_bind);
+ if (dev->hs_config)
+ gadgetfs_driver.max_speed = USB_SPEED_HIGH;
+ else
+ gadgetfs_driver.max_speed = USB_SPEED_FULL;
+
+ value = usb_gadget_probe_driver(&gadgetfs_driver);
if (value != 0) {
kfree (dev->buf);
dev->buf = NULL;
@@ -2039,7 +2036,7 @@ gadgetfs_fill_super (struct super_block *sb, void *opts, int silent)
return -ESRCH;
/* fake probe to determine $CHIP */
- (void) usb_gadget_probe_driver(&probe_driver, gadgetfs_probe);
+ usb_gadget_probe_driver(&probe_driver);
if (!CHIP)
return -ENODEV;
diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c
index f1ec99e69cb7..f696fb9b136d 100644
--- a/drivers/usb/gadget/lpc32xx_udc.c
+++ b/drivers/usb/gadget/lpc32xx_udc.c
@@ -141,8 +141,6 @@ struct lpc32xx_ep {
u32 totalints;
bool wedge;
-
- const struct usb_endpoint_descriptor *desc;
};
/*
@@ -556,10 +554,8 @@ static int proc_udc_show(struct seq_file *s, void *unused)
if (udc->enabled && udc->vbus) {
proc_ep_show(s, &udc->ep[0]);
- list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
- if (ep->desc)
- proc_ep_show(s, ep);
- }
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list)
+ proc_ep_show(s, ep);
}
spin_unlock_irqrestore(&udc->lock, flags);
@@ -1453,7 +1449,6 @@ static void udc_reinit(struct lpc32xx_udc *udc)
if (i != 0)
list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
- ep->desc = NULL;
ep->ep.maxpacket = ep->maxpacket;
INIT_LIST_HEAD(&ep->queue);
ep->req_pending = 0;
@@ -1515,7 +1510,7 @@ static void nuke(struct lpc32xx_ep *ep, int status)
done(ep, req, status);
}
- if (ep->desc && status == -ESHUTDOWN) {
+ if (status == -ESHUTDOWN) {
uda_disable_hwepint(ep->udc, ep->hwep_num);
udc_disable_hwep(ep->udc, ep->hwep_num);
}
@@ -1658,9 +1653,6 @@ static int lpc32xx_ep_disable(struct usb_ep *_ep)
nuke(ep, -ESHUTDOWN);
- /* restore the endpoint's pristine config */
- ep->desc = NULL;
-
/* Clear all DMA statuses for this EP */
udc_ep_dma_disable(udc, ep->hwep_num);
writel(1 << ep->hwep_num, USBD_EOTINTCLR(udc->udp_baseaddr));
@@ -1696,7 +1688,7 @@ static int lpc32xx_ep_enable(struct usb_ep *_ep,
unsigned long flags;
/* Verify EP data */
- if ((!_ep) || (!ep) || (!desc) || (ep->desc) ||
+ if ((!_ep) || (!ep) || (!desc) ||
(desc->bDescriptorType != USB_DT_ENDPOINT)) {
dev_dbg(udc->dev, "bad ep or descriptor\n");
return -EINVAL;
@@ -1754,7 +1746,6 @@ static int lpc32xx_ep_enable(struct usb_ep *_ep,
/* Initialize endpoint to match the selected descriptor */
ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
- ep->desc = desc;
ep->ep.maxpacket = maxpacket;
/* Map hardware endpoint from base and direction */
@@ -1837,7 +1828,7 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep,
udc = ep->udc;
- if (!_ep || (!ep->desc && ep->hwep_num_base != 0)) {
+ if (!_ep) {
dev_dbg(udc->dev, "invalid ep\n");
return -EINVAL;
}
@@ -1976,7 +1967,7 @@ static int lpc32xx_ep_set_halt(struct usb_ep *_ep, int value)
struct lpc32xx_udc *udc = ep->udc;
unsigned long flags;
- if ((!ep) || (ep->desc == NULL) || (ep->hwep_num <= 1))
+ if ((!ep) || (ep->hwep_num <= 1))
return -EINVAL;
/* Don't halt an IN EP */
@@ -2262,7 +2253,7 @@ static int udc_get_status(struct lpc32xx_udc *udc, u16 reqtype, u16 wIndex)
case USB_RECIP_ENDPOINT:
tmp = wIndex & USB_ENDPOINT_NUMBER_MASK;
ep = &udc->ep[tmp];
- if ((tmp == 0) || (tmp >= NUM_ENDPOINTS) || (tmp && !ep->desc))
+ if ((tmp == 0) || (tmp >= NUM_ENDPOINTS))
return -EOPNOTSUPP;
if (wIndex & USB_DIR_IN) {
@@ -2599,9 +2590,8 @@ static int lpc32xx_pullup(struct usb_gadget *gadget, int is_on)
return 0;
}
-static int lpc32xx_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int lpc32xx_stop(struct usb_gadget_driver *driver);
+static int lpc32xx_start(struct usb_gadget *, struct usb_gadget_driver *);
+static int lpc32xx_stop(struct usb_gadget *, struct usb_gadget_driver *);
static const struct usb_gadget_ops lpc32xx_udc_ops = {
.get_frame = lpc32xx_get_frame,
@@ -2609,8 +2599,8 @@ static const struct usb_gadget_ops lpc32xx_udc_ops = {
.set_selfpowered = lpc32xx_set_selfpowered,
.vbus_session = lpc32xx_vbus_session,
.pullup = lpc32xx_pullup,
- .start = lpc32xx_start,
- .stop = lpc32xx_stop,
+ .udc_start = lpc32xx_start,
+ .udc_stop = lpc32xx_stop,
};
static void nop_release(struct device *dev)
@@ -2618,10 +2608,9 @@ static void nop_release(struct device *dev)
/* nothing to free */
}
-static struct lpc32xx_udc controller = {
+static const struct lpc32xx_udc controller_template = {
.gadget = {
.ops = &lpc32xx_udc_ops,
- .ep0 = &controller.ep[0].ep,
.name = driver_name,
.dev = {
.init_name = "gadget",
@@ -2633,7 +2622,6 @@ static struct lpc32xx_udc controller = {
.name = "ep0",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 0,
.hwep_num = 0, /* Can be 0 or 1, has special handling */
@@ -2645,7 +2633,6 @@ static struct lpc32xx_udc controller = {
.name = "ep1-int",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 2,
.hwep_num = 0, /* 2 or 3, will be set later */
@@ -2657,7 +2644,6 @@ static struct lpc32xx_udc controller = {
.name = "ep2-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 4,
.hwep_num = 0, /* 4 or 5, will be set later */
@@ -2669,7 +2655,6 @@ static struct lpc32xx_udc controller = {
.name = "ep3-iso",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 1023,
.hwep_num_base = 6,
.hwep_num = 0, /* 6 or 7, will be set later */
@@ -2681,7 +2666,6 @@ static struct lpc32xx_udc controller = {
.name = "ep4-int",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 8,
.hwep_num = 0, /* 8 or 9, will be set later */
@@ -2693,7 +2677,6 @@ static struct lpc32xx_udc controller = {
.name = "ep5-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 10,
.hwep_num = 0, /* 10 or 11, will be set later */
@@ -2705,7 +2688,6 @@ static struct lpc32xx_udc controller = {
.name = "ep6-iso",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 1023,
.hwep_num_base = 12,
.hwep_num = 0, /* 12 or 13, will be set later */
@@ -2717,7 +2699,6 @@ static struct lpc32xx_udc controller = {
.name = "ep7-int",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 14,
.hwep_num = 0,
@@ -2729,7 +2710,6 @@ static struct lpc32xx_udc controller = {
.name = "ep8-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 16,
.hwep_num = 0,
@@ -2741,7 +2721,6 @@ static struct lpc32xx_udc controller = {
.name = "ep9-iso",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 1023,
.hwep_num_base = 18,
.hwep_num = 0,
@@ -2753,7 +2732,6 @@ static struct lpc32xx_udc controller = {
.name = "ep10-int",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 20,
.hwep_num = 0,
@@ -2765,7 +2743,6 @@ static struct lpc32xx_udc controller = {
.name = "ep11-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 22,
.hwep_num = 0,
@@ -2777,7 +2754,6 @@ static struct lpc32xx_udc controller = {
.name = "ep12-iso",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 1023,
.hwep_num_base = 24,
.hwep_num = 0,
@@ -2789,7 +2765,6 @@ static struct lpc32xx_udc controller = {
.name = "ep13-int",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 26,
.hwep_num = 0,
@@ -2801,7 +2776,6 @@ static struct lpc32xx_udc controller = {
.name = "ep14-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 28,
.hwep_num = 0,
@@ -2813,7 +2787,6 @@ static struct lpc32xx_udc controller = {
.name = "ep15-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 1023,
.hwep_num_base = 30,
.hwep_num = 0,
@@ -2987,14 +2960,13 @@ static irqreturn_t lpc32xx_usb_vbus_irq(int irq, void *_udc)
return IRQ_HANDLED;
}
-static int lpc32xx_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int lpc32xx_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
- struct lpc32xx_udc *udc = &controller;
- int retval, i;
+ struct lpc32xx_udc *udc = to_udc(gadget);
+ int i;
- if (!driver || driver->max_speed < USB_SPEED_FULL ||
- !bind || !driver->setup) {
+ if (!driver || driver->max_speed < USB_SPEED_FULL || !driver->setup) {
dev_err(udc->dev, "bad parameter.\n");
return -EINVAL;
}
@@ -3011,18 +2983,6 @@ static int lpc32xx_start(struct usb_gadget_driver *driver,
udc->selfpowered = 1;
udc->vbus = 0;
- retval = bind(&udc->gadget);
- if (retval) {
- dev_err(udc->dev, "bind() returned %d\n", retval);
- udc->enabled = 0;
- udc->selfpowered = 0;
- udc->driver = NULL;
- udc->gadget.dev.driver = NULL;
- return retval;
- }
-
- dev_dbg(udc->dev, "bound to %s\n", driver->driver.name);
-
/* Force VBUS process once to check for cable insertion */
udc->last_vbus = udc->vbus = 0;
schedule_work(&udc->vbus_job);
@@ -3034,22 +2994,19 @@ static int lpc32xx_start(struct usb_gadget_driver *driver,
return 0;
}
-static int lpc32xx_stop(struct usb_gadget_driver *driver)
+static int lpc32xx_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
int i;
- struct lpc32xx_udc *udc = &controller;
+ struct lpc32xx_udc *udc = to_udc(gadget);
- if (!driver || driver != udc->driver || !driver->unbind)
+ if (!driver || driver != udc->driver)
return -EINVAL;
- /* Disable USB pullup */
- isp1301_pullup_enable(udc, 0, 1);
-
for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++)
disable_irq(udc->udp_irq[i]);
if (udc->clocked) {
-
spin_lock(&udc->lock);
stop_activity(udc);
spin_unlock(&udc->lock);
@@ -3069,20 +3026,16 @@ static int lpc32xx_stop(struct usb_gadget_driver *driver)
}
udc->enabled = 0;
- pullup(udc, 0);
-
- driver->unbind(&udc->gadget);
udc->gadget.dev.driver = NULL;
udc->driver = NULL;
- dev_dbg(udc->dev, "unbound from %s\n", driver->driver.name);
return 0;
}
static void lpc32xx_udc_shutdown(struct platform_device *dev)
{
/* Force disconnect on reboot */
- struct lpc32xx_udc *udc = &controller;
+ struct lpc32xx_udc *udc = platform_get_drvdata(dev);
pullup(udc, 0);
}
@@ -3120,12 +3073,21 @@ static u64 lpc32xx_usbd_dmamask = ~(u32) 0x7F;
static int __init lpc32xx_udc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct lpc32xx_udc *udc = &controller;
+ struct lpc32xx_udc *udc;
int retval, i;
struct resource *res;
dma_addr_t dma_handle;
struct device_node *isp1301_node;
+ udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ return -ENOMEM;
+
+ memcpy(udc, &controller_template, sizeof(*udc));
+ for (i = 0; i <= 15; i++)
+ udc->ep[i].udc = udc;
+ udc->gadget.ep0 = &udc->ep[0].ep;
+
/* init software state */
udc->gadget.dev.parent = dev;
udc->pdev = pdev;
@@ -3140,8 +3102,10 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev)
}
udc->isp1301_i2c_client = isp1301_get_client(isp1301_node);
- if (!udc->isp1301_i2c_client)
- return -EPROBE_DEFER;
+ if (!udc->isp1301_i2c_client) {
+ retval = -EPROBE_DEFER;
+ goto phy_fail;
+ }
dev_info(udc->dev, "ISP1301 I2C device at address 0x%x\n",
udc->isp1301_i2c_client->addr);
@@ -3160,8 +3124,10 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev)
* IORESOURCE_IRQ, USB transceiver interrupt number
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENXIO;
+ if (!res) {
+ retval = -ENXIO;
+ goto resource_fail;
+ }
spin_lock_init(&udc->lock);
@@ -3171,7 +3137,8 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev)
if (udc->udp_irq[i] < 0) {
dev_err(udc->dev,
"irq resource %d not available!\n", i);
- return udc->udp_irq[i];
+ retval = udc->udp_irq[i];
+ goto irq_fail;
}
}
@@ -3179,7 +3146,8 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev)
udc->io_p_size = resource_size(res);
if (!request_mem_region(udc->io_p_start, udc->io_p_size, driver_name)) {
dev_err(udc->dev, "someone's using UDC memory\n");
- return -EBUSY;
+ retval = -EBUSY;
+ goto request_mem_region_fail;
}
udc->udp_baseaddr = ioremap(udc->io_p_start, udc->io_p_size);
@@ -3208,7 +3176,7 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev)
udc->usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg");
if (IS_ERR(udc->usb_otg_clk)) {
dev_err(udc->dev, "failed to acquire USB otg clock\n");
- retval = PTR_ERR(udc->usb_slv_clk);
+ retval = PTR_ERR(udc->usb_otg_clk);
goto usb_otg_clk_get_fail;
}
@@ -3376,7 +3344,11 @@ pll_get_fail:
io_map_fail:
release_mem_region(udc->io_p_start, udc->io_p_size);
dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval);
-
+request_mem_region_fail:
+irq_fail:
+resource_fail:
+phy_fail:
+ kfree(udc);
return retval;
}
@@ -3414,6 +3386,7 @@ static int __devexit lpc32xx_udc_remove(struct platform_device *pdev)
clk_put(udc->usb_pll_clk);
iounmap(udc->udp_baseaddr);
release_mem_region(udc->io_p_start, udc->io_p_size);
+ kfree(udc);
return 0;
}
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
index cf6bd626f3fe..b6401f1b56ce 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -1466,7 +1466,7 @@ static struct usb_ep_ops m66592_ep_ops = {
static struct m66592 *the_controller;
static int m66592_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct m66592 *m66592 = the_controller;
int retval;
@@ -1492,7 +1492,7 @@ static int m66592_start(struct usb_gadget_driver *driver,
goto error;
}
- retval = bind(&m66592->gadget);
+ retval = bind(&m66592->gadget, driver);
if (retval) {
pr_err("bind to driver error (%d)\n", retval);
device_del(&m66592->gadget.dev);
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
index 1f376eba31f6..080e577773d5 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/mass_storage.c
@@ -29,9 +29,8 @@
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/usb/ch9.h>
-
+#include <linux/module.h>
/*-------------------------------------------------------------------------*/
@@ -47,14 +46,10 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
#include "f_mass_storage.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor msg_device_desc = {
.bLength = sizeof msg_device_desc,
@@ -85,6 +80,22 @@ static const struct usb_descriptor_header *otg_desc[] = {
NULL,
};
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
/****************************** Configurations ******************************/
@@ -143,10 +154,15 @@ static int __init msg_bind(struct usb_composite_dev *cdev)
{
int status;
- status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
return status;
+ msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
+ if (status < 0)
+ return status;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&cdev->gadget->dev,
DRIVER_DESC ", version: " DRIVER_VERSION "\n");
set_bit(0, &msg_registered);
@@ -156,12 +172,13 @@ static int __init msg_bind(struct usb_composite_dev *cdev)
/****************************** Some noise ******************************/
-static struct usb_composite_driver msg_driver = {
+static __refdata struct usb_composite_driver msg_driver = {
.name = "g_mass_storage",
.dev = &msg_device_desc,
- .iProduct = DRIVER_DESC,
.max_speed = USB_SPEED_SUPER,
.needs_serial = 1,
+ .strings = dev_strings,
+ .bind = msg_bind,
};
MODULE_DESCRIPTION(DRIVER_DESC);
@@ -170,7 +187,7 @@ MODULE_LICENSE("GPL");
static int __init msg_init(void)
{
- return usb_composite_probe(&msg_driver, msg_bind);
+ return usb_composite_probe(&msg_driver);
}
module_init(msg_init);
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index c37fb33a3d1b..88472bf7dbb7 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -14,10 +14,8 @@
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/module.h>
-
#if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
#endif
@@ -42,12 +40,6 @@ MODULE_LICENSE("GPL");
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_mass_storage.c"
#include "u_serial.c"
@@ -61,7 +53,7 @@ MODULE_LICENSE("GPL");
#endif
#include "u_ether.c"
-
+USB_GADGET_COMPOSITE_OPTIONS();
/***************************** Device Descriptor ****************************/
@@ -112,21 +104,16 @@ static const struct usb_descriptor_header *otg_desc[] = {
enum {
-#ifdef CONFIG_USB_G_MULTI_RNDIS
- MULTI_STRING_RNDIS_CONFIG_IDX,
-#endif
-#ifdef CONFIG_USB_G_MULTI_CDC
+ MULTI_STRING_RNDIS_CONFIG_IDX = USB_GADGET_FIRST_AVAIL_IDX,
MULTI_STRING_CDC_CONFIG_IDX,
-#endif
};
static struct usb_string strings_dev[] = {
-#ifdef CONFIG_USB_G_MULTI_RNDIS
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
[MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS",
-#endif
-#ifdef CONFIG_USB_G_MULTI_CDC
[MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM",
-#endif
{ } /* end of list */
};
@@ -260,7 +247,7 @@ static int cdc_config_register(struct usb_composite_dev *cdev)
static int __ref multi_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
- int status, gcnum;
+ int status;
if (!can_support_ecm(cdev->gadget)) {
dev_err(&gadget->dev, "controller '%s' not usable\n",
@@ -288,19 +275,11 @@ static int __ref multi_bind(struct usb_composite_dev *cdev)
}
}
- /* set bcdDevice */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0) {
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- } else {
- WARNING(cdev, "controller '%s' not recognized\n", gadget->name);
- device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
- }
-
/* allocate string IDs */
status = usb_string_ids_tab(cdev, strings_dev);
if (unlikely(status < 0))
goto fail2;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register configurations */
status = rndis_config_register(cdev);
@@ -310,6 +289,7 @@ static int __ref multi_bind(struct usb_composite_dev *cdev)
status = cdc_config_register(cdev);
if (unlikely(status < 0))
goto fail2;
+ usb_composite_overwrite_options(cdev, &coverwrite);
/* we're done */
dev_info(&gadget->dev, DRIVER_DESC "\n");
@@ -338,20 +318,20 @@ static int __exit multi_unbind(struct usb_composite_dev *cdev)
/****************************** Some noise ******************************/
-static struct usb_composite_driver multi_driver = {
+static __refdata struct usb_composite_driver multi_driver = {
.name = "g_multi",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = multi_bind,
.unbind = __exit_p(multi_unbind),
- .iProduct = DRIVER_DESC,
.needs_serial = 1,
};
static int __init multi_init(void)
{
- return usb_composite_probe(&multi_driver, multi_bind);
+ return usb_composite_probe(&multi_driver);
}
module_init(multi_init);
diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c
index 75db2c306cea..ea45224f78c8 100644
--- a/drivers/usb/gadget/mv_udc_core.c
+++ b/drivers/usb/gadget/mv_udc_core.c
@@ -51,9 +51,8 @@
#define EPSTATUS_TIMEOUT 10000
#define PRIME_TIMEOUT 10000
#define READSAFE_TIMEOUT 1000
-#define DTD_TIMEOUT 1000
-#define LOOPS_USEC_SHIFT 4
+#define LOOPS_USEC_SHIFT 1
#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT)
#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT)
@@ -64,7 +63,6 @@ static const char driver_desc[] = DRIVER_DESC;
/* controller device global variable */
static struct mv_udc *the_controller;
-int mv_usb_otgsc;
static void nuke(struct mv_ep *ep, int status);
static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver);
@@ -357,17 +355,24 @@ done:
return retval;
}
-
static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
dma_addr_t *dma, int *is_last)
{
- u32 temp;
struct mv_dtd *dtd;
struct mv_udc *udc;
+ struct mv_dqh *dqh;
+ u32 temp, mult = 0;
/* how big will this transfer be? */
- *length = min(req->req.length - req->req.actual,
- (unsigned)EP_MAX_LENGTH_TRANSFER);
+ if (usb_endpoint_xfer_isoc(req->ep->ep.desc)) {
+ dqh = req->ep->dqh;
+ mult = (dqh->max_packet_length >> EP_QUEUE_HEAD_MULT_POS)
+ & 0x3;
+ *length = min(req->req.length - req->req.actual,
+ (unsigned)(mult * req->ep->ep.maxpacket));
+ } else
+ *length = min(req->req.length - req->req.actual,
+ (unsigned)EP_MAX_LENGTH_TRANSFER);
udc = req->ep->udc;
@@ -375,7 +380,7 @@ static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
* Be careful that no _GFP_HIGHMEM is set,
* or we can not use dma_to_virt
*/
- dtd = dma_pool_alloc(udc->dtd_pool, GFP_KERNEL, dma);
+ dtd = dma_pool_alloc(udc->dtd_pool, GFP_ATOMIC, dma);
if (dtd == NULL)
return dtd;
@@ -409,6 +414,8 @@ static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
if (*is_last && !req->req.no_interrupt)
temp |= DTD_IOC;
+ temp |= mult << 10;
+
dtd->size_ioc_sts = temp;
mb();
@@ -708,6 +715,7 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
struct mv_req *req = container_of(_req, struct mv_req, req);
struct mv_udc *udc = ep->udc;
unsigned long flags;
+ int retval;
/* catch various bogus parameters */
if (!_req || !req->req.complete || !req->req.buf
@@ -719,10 +727,6 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
dev_err(&udc->dev->dev, "%s, bad ep", __func__);
return -EINVAL;
}
- if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
- if (req->req.length > ep->ep.maxpacket)
- return -EMSGSIZE;
- }
udc = ep->udc;
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
@@ -755,15 +759,17 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
/* build dtds and push them to device queue */
if (!req_to_dtd(req)) {
- int retval;
retval = queue_dtd(ep, req);
if (retval) {
spin_unlock_irqrestore(&udc->lock, flags);
- return retval;
+ dev_err(&udc->dev->dev, "Failed to queue dtd\n");
+ goto err_unmap_dma;
}
} else {
spin_unlock_irqrestore(&udc->lock, flags);
- return -ENOMEM;
+ dev_err(&udc->dev->dev, "Failed to dma_pool_alloc\n");
+ retval = -ENOMEM;
+ goto err_unmap_dma;
}
/* Update ep0 state */
@@ -775,6 +781,22 @@ mv_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
+
+err_unmap_dma:
+ if (req->mapped) {
+ dma_unmap_single(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ((ep_dir(ep) == EP_DIR_IN) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE));
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ } else
+ dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ((ep_dir(ep) == EP_DIR_IN) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE));
+
+ return retval;
}
static void mv_prime_ep(struct mv_ep *ep, struct mv_req *req)
@@ -1065,7 +1087,7 @@ static int udc_reset(struct mv_udc *udc)
tmp |= USBMODE_CTRL_MODE_DEVICE;
/* turn setup lockout off, require setup tripwire in usbcmd */
- tmp |= USBMODE_SETUP_LOCK_OFF | USBMODE_STREAM_DISABLE;
+ tmp |= USBMODE_SETUP_LOCK_OFF;
writel(tmp, &udc->op_regs->usbmode);
@@ -1199,12 +1221,16 @@ static int mv_udc_vbus_session(struct usb_gadget *gadget, int is_active)
udc_start(udc);
}
} else if (udc->driver && udc->softconnect) {
+ if (!udc->active)
+ goto out;
+
/* stop all the transfer in queue*/
stop_activity(udc, udc->driver);
udc_stop(udc);
mv_udc_disable(udc);
}
+out:
spin_unlock_irqrestore(&udc->lock, flags);
return retval;
}
@@ -1243,7 +1269,7 @@ static int mv_udc_pullup(struct usb_gadget *gadget, int is_on)
}
static int mv_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int mv_udc_stop(struct usb_gadget_driver *driver);
/* device controller usb_gadget_ops structure */
static const struct usb_gadget_ops mv_ops = {
@@ -1348,7 +1374,7 @@ static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver)
}
static int mv_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct mv_udc *udc = the_controller;
int retval = 0;
@@ -1373,7 +1399,7 @@ static int mv_udc_start(struct usb_gadget_driver *driver,
spin_unlock_irqrestore(&udc->lock, flags);
- retval = bind(&udc->gadget);
+ retval = bind(&udc->gadget, driver);
if (retval) {
dev_err(&udc->dev->dev, "bind to driver %s --> %d\n",
driver->driver.name, retval);
@@ -1499,15 +1525,17 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
}
/* prime the data phase */
- if (!req_to_dtd(req))
+ if (!req_to_dtd(req)) {
retval = queue_dtd(ep, req);
- else{ /* no mem */
+ if (retval) {
+ dev_err(&udc->dev->dev,
+ "Failed to queue dtd when prime status\n");
+ goto out;
+ }
+ } else{ /* no mem */
retval = -ENOMEM;
- goto out;
- }
-
- if (retval) {
- dev_err(&udc->dev->dev, "response error on GET_STATUS request\n");
+ dev_err(&udc->dev->dev,
+ "Failed to dma_pool_alloc when prime status\n");
goto out;
}
@@ -1515,6 +1543,15 @@ udc_prime_status(struct mv_udc *udc, u8 direction, u16 status, bool empty)
return 0;
out:
+ if (req->mapped) {
+ dma_unmap_single(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ((ep_dir(ep) == EP_DIR_IN) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE));
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ }
+
return retval;
}
@@ -2468,9 +2505,11 @@ static void mv_udc_shutdown(struct platform_device *dev)
u32 mode;
/* reset controller mode to IDLE */
+ mv_udc_enable(udc);
mode = readl(&udc->op_regs->usbmode);
mode &= ~3;
writel(mode, &udc->op_regs->usbmode);
+ mv_udc_disable(udc);
}
static struct platform_driver udc_driver = {
diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c
index 89530034dff1..a22ad9af0565 100644
--- a/drivers/usb/gadget/ncm.c
+++ b/drivers/usb/gadget/ncm.c
@@ -20,8 +20,8 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
-#include <linux/utsname.h>
-
+#include <linux/module.h>
+#include <linux/usb/composite.h>
#include "u_ether.h"
@@ -36,11 +36,6 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_ncm.c"
#include "u_ether.c"
@@ -57,6 +52,7 @@
#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
@@ -97,17 +93,11 @@ static const struct usb_descriptor_header *otg_desc[] = {
NULL,
};
-
/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -149,7 +139,6 @@ static struct usb_configuration ncm_config_driver = {
static int __init gncm_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
@@ -158,48 +147,22 @@ static int __init gncm_bind(struct usb_composite_dev *cdev)
if (status < 0)
return status;
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else {
- /* We assume that can_support_ecm() tells the truth;
- * but if the controller isn't recognized at all then
- * that assumption is a bit more likely to be wrong.
- */
- dev_warn(&gadget->dev,
- "controller '%s' not recognized; trying %s\n",
- gadget->name,
- ncm_config_driver.label);
- device_desc.bcdDevice =
- cpu_to_le16(0x0300 | 0x0099);
- }
-
-
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
status = usb_add_config(cdev, &ncm_config_driver,
ncm_do_config);
if (status < 0)
goto fail;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s\n", DRIVER_DESC);
return 0;
@@ -215,11 +178,12 @@ static int __exit gncm_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static struct usb_composite_driver ncm_driver = {
+static __refdata struct usb_composite_driver ncm_driver = {
.name = "g_ncm",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = gncm_bind,
.unbind = __exit_p(gncm_unbind),
};
@@ -229,7 +193,7 @@ MODULE_LICENSE("GPL");
static int __init init(void)
{
- return usb_composite_probe(&ncm_driver, gncm_bind);
+ return usb_composite_probe(&ncm_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c
index c7fb7723c014..661600abace8 100644
--- a/drivers/usb/gadget/nokia.c
+++ b/drivers/usb/gadget/nokia.c
@@ -16,7 +16,6 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/device.h>
#include "u_serial.h"
@@ -38,11 +37,6 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "u_serial.c"
#include "f_acm.c"
#include "f_ecm.c"
@@ -52,23 +46,23 @@
#include "u_ether.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
#define NOKIA_VENDOR_ID 0x0421 /* Nokia */
#define NOKIA_PRODUCT_ID 0x01c8 /* Nokia Gadget */
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_DESCRIPTION_IDX 2
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static char manufacturer_nokia[] = "Nokia";
static const char product_nokia[] = NOKIA_LONG_NAME;
static const char description_nokia[] = "PC-Suite Configuration";
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer_nokia,
- [STRING_PRODUCT_IDX].s = NOKIA_LONG_NAME,
+ [USB_GADGET_MANUFACTURER_IDX].s = manufacturer_nokia,
+ [USB_GADGET_PRODUCT_IDX].s = NOKIA_LONG_NAME,
+ [USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = description_nokia,
{ } /* end of list */
};
@@ -90,6 +84,7 @@ static struct usb_device_descriptor device_desc = {
.bDeviceClass = USB_CLASS_COMM,
.idVendor = __constant_cpu_to_le16(NOKIA_VENDOR_ID),
.idProduct = __constant_cpu_to_le16(NOKIA_PRODUCT_ID),
+ .bcdDevice = cpu_to_le16(NOKIA_VERSION_NUM),
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
.bNumConfigurations = 1,
@@ -151,7 +146,6 @@ static struct usb_configuration nokia_config_100ma_driver = {
static int __init nokia_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
@@ -167,41 +161,17 @@ static int __init nokia_bind(struct usb_composite_dev *cdev)
if (status < 0)
goto err_ether;
- status = usb_string_id(cdev);
- if (status < 0)
- goto err_usb;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
-
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto err_usb;
- strings_dev[STRING_PRODUCT_IDX].id = status;
-
- device_desc.iProduct = status;
-
- /* config description */
- status = usb_string_id(cdev);
- if (status < 0)
- goto err_usb;
- strings_dev[STRING_DESCRIPTION_IDX].id = status;
-
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ status = strings_dev[STRING_DESCRIPTION_IDX].id;
nokia_config_500ma_driver.iConfiguration = status;
nokia_config_100ma_driver.iConfiguration = status;
- /* set up other descriptors */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(NOKIA_VERSION_NUM);
- else {
- /* this should only work with hw that supports altsettings
- * and several endpoints, anything else, panic.
- */
- pr_err("nokia_bind: controller '%s' not recognized\n",
- gadget->name);
+ if (!gadget_supports_altsettings(gadget))
goto err_usb;
- }
/* finally register the configuration */
status = usb_add_config(cdev, &nokia_config_500ma_driver,
@@ -214,6 +184,7 @@ static int __init nokia_bind(struct usb_composite_dev *cdev)
if (status < 0)
goto err_usb;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s\n", NOKIA_LONG_NAME);
return 0;
@@ -237,17 +208,18 @@ static int __exit nokia_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static struct usb_composite_driver nokia_driver = {
+static __refdata struct usb_composite_driver nokia_driver = {
.name = "g_nokia",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = nokia_bind,
.unbind = __exit_p(nokia_unbind),
};
static int __init nokia_init(void)
{
- return usb_composite_probe(&nokia_driver, nokia_bind);
+ return usb_composite_probe(&nokia_driver);
}
module_init(nokia_init);
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index f9132ada53b5..2a4749c3eb3f 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -1308,7 +1308,7 @@ static int omap_pullup(struct usb_gadget *gadget, int is_on)
}
static int omap_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int omap_udc_stop(struct usb_gadget_driver *driver);
static struct usb_gadget_ops omap_gadget_ops = {
@@ -2040,7 +2040,7 @@ static inline int machine_without_vbus_sense(void)
}
static int omap_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
int status = -ENODEV;
struct omap_ep *ep;
@@ -2082,7 +2082,7 @@ static int omap_udc_start(struct usb_gadget_driver *driver,
if (udc->dc_clk != NULL)
omap_udc_enable_clock(1);
- status = bind(&udc->gadget);
+ status = bind(&udc->gadget, driver);
if (status) {
DBG("bind to %s --> %d\n", driver->driver.name, status);
udc->gadget.dev.driver = NULL;
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index f4fb71c9ae08..6490c0040e3a 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -1236,7 +1236,7 @@ static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
}
static int pch_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int pch_udc_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops pch_udc_ops = {
.get_frame = pch_udc_pcd_get_frame,
@@ -2982,7 +2982,7 @@ static int init_dma_pools(struct pch_udc_dev *dev)
}
static int pch_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct pch_udc_dev *dev = pch_udc;
int retval;
@@ -3006,7 +3006,7 @@ static int pch_udc_start(struct usb_gadget_driver *driver,
dev->gadget.dev.driver = &driver->driver;
/* Invoke the bind routine of the gadget driver */
- retval = bind(&dev->gadget);
+ retval = bind(&dev->gadget, driver);
if (retval) {
dev_err(&dev->pdev->dev, "%s: binding to %s returning %d\n",
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c
index f1f9290a2f47..e156e3f26727 100644
--- a/drivers/usb/gadget/printer.c
+++ b/drivers/usb/gadget/printer.c
@@ -22,7 +22,6 @@
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
-#include <linux/utsname.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
@@ -38,25 +37,13 @@
#include <asm/unaligned.h>
#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/usb/g_printer.h>
#include "gadget_chips.h"
-
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
-/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
#define DRIVER_DESC "Printer Gadget"
#define DRIVER_VERSION "2007 OCT 06"
@@ -120,8 +107,7 @@ static struct printer_dev usb_printer_gadget;
* parameters are in UTF-8 (superset of ASCII's 7 bit characters).
*/
-static char *iSerialNum;
-module_param(iSerialNum, charp, S_IRUGO);
+module_param_named(iSerialNum, coverwrite.serial_number, charp, S_IRUGO);
MODULE_PARM_DESC(iSerialNum, "1");
static char *iPNPstring;
@@ -141,18 +127,10 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
* descriptors are built on demand.
*/
-#define STRING_MANUFACTURER 1
-#define STRING_PRODUCT 2
-#define STRING_SERIALNUM 3
-
/* holds our biggest descriptor */
#define USB_DESC_BUFSIZE 256
#define USB_BUFSIZE 8192
-/* This device advertises one configuration. */
-#define DEV_CONFIG_VALUE 1
-#define PRINTER_INTERFACE 0
-
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
@@ -162,16 +140,12 @@ static struct usb_device_descriptor device_desc = {
.bDeviceProtocol = 0,
.idVendor = cpu_to_le16(PRINTER_VENDOR_NUM),
.idProduct = cpu_to_le16(PRINTER_PRODUCT_NUM),
- .iManufacturer = STRING_MANUFACTURER,
- .iProduct = STRING_PRODUCT,
- .iSerialNumber = STRING_SERIALNUM,
.bNumConfigurations = 1
};
static struct usb_interface_descriptor intf_desc = {
.bLength = sizeof intf_desc,
.bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = PRINTER_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_PRINTER,
.bInterfaceSubClass = 1, /* Printer Sub-Class */
@@ -252,7 +226,6 @@ static const struct usb_descriptor_header *otg_desc[] = {
/* descriptors that are built on-demand */
-static char manufacturer [50];
static char product_desc [40] = DRIVER_DESC;
static char serial_num [40] = "1";
static char pnp_string [1024] =
@@ -260,9 +233,9 @@ static char pnp_string [1024] =
/* static strings, in UTF-8 */
static struct usb_string strings [] = {
- { STRING_MANUFACTURER, manufacturer, },
- { STRING_PRODUCT, product_desc, },
- { STRING_SERIALNUM, serial_num, },
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = product_desc,
+ [USB_GADGET_SERIAL_IDX].s = serial_num,
{ } /* end of list */
};
@@ -871,25 +844,13 @@ static int set_interface(struct printer_dev *dev, unsigned number)
int result = 0;
/* Free the current interface */
- switch (dev->interface) {
- case PRINTER_INTERFACE:
- printer_reset_interface(dev);
- break;
- }
+ printer_reset_interface(dev);
- switch (number) {
- case PRINTER_INTERFACE:
- result = set_printer_interface(dev);
- if (result) {
- printer_reset_interface(dev);
- } else {
- dev->interface = PRINTER_INTERFACE;
- }
- break;
- default:
- result = -EINVAL;
- /* FALL THROUGH */
- }
+ result = set_printer_interface(dev);
+ if (result)
+ printer_reset_interface(dev);
+ else
+ dev->interface = number;
if (!result)
INFO(dev, "Using interface %x\n", number);
@@ -972,7 +933,7 @@ static int printer_func_setup(struct usb_function *f,
switch (ctrl->bRequest) {
case 0: /* Get the IEEE-1284 PNP String */
/* Only one printer interface is supported. */
- if ((wIndex>>8) != PRINTER_INTERFACE)
+ if ((wIndex>>8) != dev->interface)
break;
value = (pnp_string[0]<<8)|pnp_string[1];
@@ -983,7 +944,7 @@ static int printer_func_setup(struct usb_function *f,
case 1: /* Get Port Status */
/* Only one printer interface is supported. */
- if (wIndex != PRINTER_INTERFACE)
+ if (wIndex != dev->interface)
break;
*(u8 *)req->buf = dev->printer_status;
@@ -992,7 +953,7 @@ static int printer_func_setup(struct usb_function *f,
case 2: /* Soft Reset */
/* Only one printer interface is supported. */
- if (wIndex != PRINTER_INTERFACE)
+ if (wIndex != dev->interface)
break;
printer_soft_reset(dev);
@@ -1020,6 +981,37 @@ unknown:
static int __init printer_func_bind(struct usb_configuration *c,
struct usb_function *f)
{
+ struct printer_dev *dev = container_of(f, struct printer_dev, function);
+ struct usb_composite_dev *cdev = c->cdev;
+ struct usb_ep *in_ep, *out_ep;
+ int id;
+
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ intf_desc.bInterfaceNumber = id;
+
+ /* all we really need is bulk IN/OUT */
+ in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
+ if (!in_ep) {
+autoconf_fail:
+ dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
+ cdev->gadget->name);
+ return -ENODEV;
+ }
+ in_ep->driver_data = in_ep; /* claim */
+
+ out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
+ if (!out_ep)
+ goto autoconf_fail;
+ out_ep->driver_data = out_ep; /* claim */
+
+ /* assumes that all endpoints are dual-speed */
+ hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
+ hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
+
+ dev->in_ep = in_ep;
+ dev->out_ep = out_ep;
return 0;
}
@@ -1035,7 +1027,8 @@ static int printer_func_set_alt(struct usb_function *f,
int ret = -ENOTSUPP;
if (!alt)
- ret = set_interface(dev, PRINTER_INTERFACE);
+ ret = set_interface(dev, intf);
+
return ret;
}
@@ -1107,13 +1100,13 @@ static int __init printer_bind_config(struct usb_configuration *c)
{
struct usb_gadget *gadget = c->cdev->gadget;
struct printer_dev *dev;
- struct usb_ep *in_ep, *out_ep;
int status = -ENOMEM;
- int gcnum;
size_t len;
u32 i;
struct usb_request *req;
+ usb_ep_autoconfig_reset(gadget);
+
dev = &usb_printer_gadget;
dev->function.name = shortname;
@@ -1125,6 +1118,10 @@ static int __init printer_bind_config(struct usb_configuration *c)
dev->function.set_alt = printer_func_set_alt;
dev->function.disable = printer_func_disable;
+ status = usb_add_function(c, &dev->function);
+ if (status)
+ return status;
+
/* Setup the sysfs files for the printer gadget. */
dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno,
NULL, "g_printer");
@@ -1145,23 +1142,6 @@ static int __init printer_bind_config(struct usb_configuration *c)
goto fail;
}
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0) {
- device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
- } else {
- dev_warn(&gadget->dev, "controller '%s' not recognized\n",
- gadget->name);
- /* unrecognized, but safe unless bulk is REALLY quirky */
- device_desc.bcdDevice =
- cpu_to_le16(0xFFFF);
- }
- snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
-
- if (iSerialNum)
- strlcpy(serial_num, iSerialNum, sizeof serial_num);
-
if (iPNPstring)
strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2);
@@ -1169,26 +1149,6 @@ static int __init printer_bind_config(struct usb_configuration *c)
pnp_string[0] = (len >> 8) & 0xFF;
pnp_string[1] = len & 0xFF;
- /* all we really need is bulk IN/OUT */
- usb_ep_autoconfig_reset(gadget);
- in_ep = usb_ep_autoconfig(gadget, &fs_ep_in_desc);
- if (!in_ep) {
-autoconf_fail:
- dev_err(&gadget->dev, "can't autoconfigure on %s\n",
- gadget->name);
- return -ENODEV;
- }
- in_ep->driver_data = in_ep; /* claim */
-
- out_ep = usb_ep_autoconfig(gadget, &fs_ep_out_desc);
- if (!out_ep)
- goto autoconf_fail;
- out_ep->driver_data = out_ep; /* claim */
-
- /* assumes that all endpoints are dual-speed */
- hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
- hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
-
usb_gadget_set_selfpowered(gadget);
if (gadget->is_otg) {
@@ -1215,9 +1175,6 @@ autoconf_fail:
dev->current_rx_bytes = 0;
dev->current_rx_buf = NULL;
- dev->in_ep = in_ep;
- dev->out_ep = out_ep;
-
for (i = 0; i < QLEN; i++) {
req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
if (!req) {
@@ -1250,8 +1207,6 @@ autoconf_fail:
dev->gadget = gadget;
INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
- INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, out_ep->name,
- in_ep->name);
return 0;
fail:
@@ -1266,14 +1221,28 @@ static int printer_unbind(struct usb_composite_dev *cdev)
static int __init printer_bind(struct usb_composite_dev *cdev)
{
- return usb_add_config(cdev, &printer_cfg_driver, printer_bind_config);
+ int ret;
+
+ ret = usb_string_ids_tab(cdev, strings);
+ if (ret < 0)
+ return ret;
+ device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
+ device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
+
+ ret = usb_add_config(cdev, &printer_cfg_driver, printer_bind_config);
+ if (ret)
+ return ret;
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ return ret;
}
-static struct usb_composite_driver printer_driver = {
+static __refdata struct usb_composite_driver printer_driver = {
.name = shortname,
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = printer_bind,
.unbind = printer_unbind,
};
@@ -1297,7 +1266,7 @@ init(void)
return status;
}
- status = usb_composite_probe(&printer_driver, printer_bind);
+ status = usb_composite_probe(&printer_driver);
if (status) {
class_destroy(usb_gadget_class);
unregister_chrdev_region(g_printer_devno, 1);
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index 907ad3ecb341..8efbf08c3561 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -33,7 +33,6 @@
#include <linux/dma-mapping.h>
#include <linux/irq.h>
#include <linux/clk.h>
-#include <linux/err.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/io.h>
@@ -1000,7 +999,7 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
}
static int pxa25x_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int pxa25x_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops pxa25x_udc_ops = {
@@ -1258,7 +1257,7 @@ static void udc_enable (struct pxa25x_udc *dev)
* the driver might get unbound.
*/
static int pxa25x_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct pxa25x_udc *dev = the_controller;
int retval;
@@ -1286,7 +1285,7 @@ fail:
dev->gadget.dev.driver = NULL;
return retval;
}
- retval = bind(&dev->gadget);
+ retval = bind(&dev->gadget, driver);
if (retval) {
DMSG("bind to driver %s --> error %d\n",
driver->driver.name, retval);
diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h
index 861f4df6ea22..2eca1e71fecd 100644
--- a/drivers/usb/gadget/pxa25x_udc.h
+++ b/drivers/usb/gadget/pxa25x_udc.h
@@ -225,7 +225,7 @@ dump_state(struct pxa25x_udc *dev)
dev->stats.read.bytes, dev->stats.read.ops);
for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) {
- if (dev->ep [i].desc == NULL)
+ if (dev->ep[i].ep.desc == NULL)
continue;
DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs);
}
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
index 7a8713cda945..2b3b01d5f403 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -1672,7 +1672,7 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
}
static int pxa27x_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int pxa27x_udc_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops pxa_udc_ops = {
@@ -1803,7 +1803,7 @@ static void udc_enable(struct pxa_udc *udc)
* Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise
*/
static int pxa27x_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct pxa_udc *udc = the_controller;
int retval;
@@ -1826,7 +1826,7 @@ static int pxa27x_udc_start(struct usb_gadget_driver *driver,
dev_err(udc->dev, "device_add error %d\n", retval);
goto add_fail;
}
- retval = bind(&udc->gadget);
+ retval = bind(&udc->gadget, driver);
if (retval) {
dev_err(udc->dev, "bind to driver %s --> error %d\n",
driver->driver.name, retval);
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index b35babed6fcb..e4192b887de9 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -863,26 +863,8 @@ int rndis_msg_parser(u8 configNr, u8 *buf)
*/
pr_warning("%s: unknown RNDIS message 0x%08X len %d\n",
__func__, MsgType, MsgLength);
- {
- unsigned i;
- for (i = 0; i < MsgLength; i += 16) {
- pr_debug("%03d: "
- " %02x %02x %02x %02x"
- " %02x %02x %02x %02x"
- " %02x %02x %02x %02x"
- " %02x %02x %02x %02x"
- "\n",
- i,
- buf[i], buf [i+1],
- buf[i+2], buf[i+3],
- buf[i+4], buf [i+5],
- buf[i+6], buf[i+7],
- buf[i+8], buf [i+9],
- buf[i+10], buf[i+11],
- buf[i+12], buf [i+13],
- buf[i+14], buf[i+15]);
- }
- }
+ print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET,
+ buf, MsgLength);
break;
}
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index 0bb617e1dda2..6f696ee8b817 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -2197,7 +2197,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
/* issue soft reset */
writel(GRSTCTL_CSftRst, hsotg->regs + GRSTCTL);
- timeout = 1000;
+ timeout = 10000;
do {
grstctl = readl(hsotg->regs + GRSTCTL);
} while ((grstctl & GRSTCTL_CSftRst) && timeout-- > 0);
@@ -2207,7 +2207,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
return -EINVAL;
}
- timeout = 1000;
+ timeout = 10000;
while (1) {
u32 grstctl = readl(hsotg->regs + GRSTCTL);
@@ -3516,7 +3516,7 @@ static int __devinit s3c_hsotg_probe(struct platform_device *pdev)
hsotg->dev = dev;
hsotg->plat = plat;
- hsotg->clk = clk_get(&pdev->dev, "otg");
+ hsotg->clk = devm_clk_get(&pdev->dev, "otg");
if (IS_ERR(hsotg->clk)) {
dev_err(dev, "cannot get otg clock\n");
return PTR_ERR(hsotg->clk);
@@ -3667,7 +3667,6 @@ err_supplies:
err_clk:
clk_disable_unprepare(hsotg->clk);
- clk_put(hsotg->clk);
return ret;
}
@@ -3693,7 +3692,6 @@ static int __devexit s3c_hsotg_remove(struct platform_device *pdev)
regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
clk_disable_unprepare(hsotg->clk);
- clk_put(hsotg->clk);
device_unregister(&hsotg->gadget.dev);
return 0;
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
index e26a4e7ed2bf..d8e785d4ad59 100644
--- a/drivers/usb/gadget/s3c-hsudc.c
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -135,7 +135,6 @@ struct s3c_hsudc_req {
* @dev: The device reference used by probe function.
* @lock: Lock to synchronize the usage of Endpoints (EP's are indexed).
* @regs: Remapped base address of controller's register space.
- * @mem_rsrc: Device memory resource used for remapping device register space.
* irq: IRQ number used by the controller.
* uclk: Reference to the controller clock.
* ep0state: Current state of EP0.
@@ -150,7 +149,6 @@ struct s3c_hsudc {
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsudc_supply_names)];
spinlock_t lock;
void __iomem *regs;
- struct resource *mem_rsrc;
int irq;
struct clk *uclk;
int ep0state;
@@ -835,9 +833,9 @@ static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep,
{
struct s3c_hsudc_req *hsreq;
- hsreq = kzalloc(sizeof *hsreq, gfp_flags);
+ hsreq = kzalloc(sizeof(*hsreq), gfp_flags);
if (!hsreq)
- return 0;
+ return NULL;
INIT_LIST_HEAD(&hsreq->queue);
return &hsreq->req;
@@ -906,16 +904,16 @@ static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
csr = readl((u32)hsudc->regs + offset);
if (!(csr & S3C_ESR_TX_SUCCESS) &&
(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
- hsreq = 0;
+ hsreq = NULL;
} else {
csr = readl((u32)hsudc->regs + offset);
if ((csr & S3C_ESR_RX_SUCCESS)
&& (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
- hsreq = 0;
+ hsreq = NULL;
}
}
- if (hsreq != 0)
+ if (hsreq)
list_add_tail(&hsreq->queue, &hsep->queue);
spin_unlock_irqrestore(&hsudc->lock, flags);
@@ -1271,7 +1269,7 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data;
int ret, i;
- hsudc = kzalloc(sizeof(struct s3c_hsudc) +
+ hsudc = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsudc) +
sizeof(struct s3c_hsudc_ep) * pd->epnum,
GFP_KERNEL);
if (!hsudc) {
@@ -1296,25 +1294,12 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "unable to obtain driver resource data\n");
- ret = -ENODEV;
- goto err_res;
- }
-
- hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res),
- dev_name(&pdev->dev));
- if (!hsudc->mem_rsrc) {
- dev_err(dev, "failed to reserve register area\n");
- ret = -ENODEV;
- goto err_res;
- }
- hsudc->regs = ioremap(res->start, resource_size(res));
+ hsudc->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hsudc->regs) {
dev_err(dev, "error mapping device register area\n");
ret = -EBUSY;
- goto err_remap;
+ goto err_res;
}
spin_lock_init(&hsudc->lock);
@@ -1337,21 +1322,22 @@ static int __devinit s3c_hsudc_probe(struct platform_device *pdev)
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(dev, "unable to obtain IRQ number\n");
- goto err_irq;
+ goto err_res;
}
hsudc->irq = ret;
- ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
+ ret = devm_request_irq(&pdev->dev, hsudc->irq, s3c_hsudc_irq, 0,
+ driver_name, hsudc);
if (ret < 0) {
dev_err(dev, "irq request failed\n");
- goto err_irq;
+ goto err_res;
}
- hsudc->uclk = clk_get(&pdev->dev, "usb-device");
+ hsudc->uclk = devm_clk_get(&pdev->dev, "usb-device");
if (IS_ERR(hsudc->uclk)) {
dev_err(dev, "failed to find usb-device clock source\n");
ret = PTR_ERR(hsudc->uclk);
- goto err_clk;
+ goto err_res;
}
clk_enable(hsudc->uclk);
@@ -1377,21 +1363,12 @@ err_add_udc:
device_unregister(&hsudc->gadget.dev);
err_add_device:
clk_disable(hsudc->uclk);
- clk_put(hsudc->uclk);
-err_clk:
- free_irq(hsudc->irq, hsudc);
-err_irq:
- iounmap(hsudc->regs);
-
-err_remap:
- release_mem_region(res->start, resource_size(res));
err_res:
if (!IS_ERR_OR_NULL(hsudc->transceiver))
usb_put_phy(hsudc->transceiver);
regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
err_supplies:
- kfree(hsudc);
return ret;
}
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index f006045fc44c..a2fa6e16d019 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -12,6 +12,8 @@
* (at your option) any later version.
*/
+#define pr_fmt(fmt) "s3c2410_udc: " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
@@ -27,6 +29,7 @@
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/prefetch.h>
+#include <linux/io.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
@@ -35,7 +38,6 @@
#include <linux/usb/gadget.h>
#include <asm/byteorder.h>
-#include <asm/io.h>
#include <asm/irq.h>
#include <asm/unaligned.h>
#include <mach/irqs.h>
@@ -115,7 +117,7 @@ static int dprintk(int level, const char *fmt, ...)
sizeof(printk_buf)-len, fmt, args);
va_end(args);
- return printk(KERN_DEBUG "%s", printk_buf);
+ return pr_debug("%s", printk_buf);
}
#else
static int dprintk(int level, const char *fmt, ...)
@@ -125,10 +127,10 @@ static int dprintk(int level, const char *fmt, ...)
#endif
static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
{
- u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg;
+ u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg;
u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
- u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2;
- u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2;
+ u32 ep1_i_csr1, ep1_i_csr2, ep1_o_csr1, ep1_o_csr2;
+ u32 ep2_i_csr1, ep2_i_csr2, ep2_o_csr1, ep2_o_csr2;
addr_reg = udc_read(S3C2410_UDC_FUNC_ADDR_REG);
pwr_reg = udc_read(S3C2410_UDC_PWR_REG);
@@ -164,10 +166,10 @@ static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
"EP2_I_CSR2 : 0x%04X\n"
"EP2_O_CSR1 : 0x%04X\n"
"EP2_O_CSR2 : 0x%04X\n",
- addr_reg,pwr_reg,ep_int_reg,usb_int_reg,
+ addr_reg, pwr_reg, ep_int_reg, usb_int_reg,
ep_int_en_reg, usb_int_en_reg, ep0_csr,
- ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2,
- ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2
+ ep1_i_csr1, ep1_i_csr2, ep1_o_csr1, ep1_o_csr2,
+ ep2_i_csr1, ep2_i_csr2, ep2_o_csr1, ep2_o_csr2
);
return 0;
@@ -230,7 +232,7 @@ static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base)
{
udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
- udc_writeb(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY
+ udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY
| S3C2410_UDC_EP0_CSR_DE),
S3C2410_UDC_EP0_CSR_REG);
}
@@ -263,7 +265,7 @@ static void s3c2410_udc_done(struct s3c2410_ep *ep,
list_del_init(&req->queue);
- if (likely (req->req.status == -EINPROGRESS))
+ if (likely(req->req.status == -EINPROGRESS))
req->req.status = status;
else
status = req->req.status;
@@ -280,9 +282,9 @@ static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
if (&ep->queue == NULL)
return;
- while (!list_empty (&ep->queue)) {
+ while (!list_empty(&ep->queue)) {
struct s3c2410_request *req;
- req = list_entry (ep->queue.next, struct s3c2410_request,
+ req = list_entry(ep->queue.next, struct s3c2410_request,
queue);
s3c2410_udc_done(ep, req, status);
}
@@ -389,10 +391,10 @@ static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep,
if (idx == 0) {
/* Reset signal => no need to say 'data sent' */
- if (! (udc_read(S3C2410_UDC_USB_INT_REG)
+ if (!(udc_read(S3C2410_UDC_USB_INT_REG)
& S3C2410_UDC_USBINT_RESET))
s3c2410_udc_set_ep0_de_in(base_addr);
- ep->dev->ep0state=EP0_IDLE;
+ ep->dev->ep0state = EP0_IDLE;
} else {
udc_write(idx, S3C2410_UDC_INDEX_REG);
ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
@@ -406,7 +408,7 @@ static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep,
} else {
if (idx == 0) {
/* Reset signal => no need to say 'data sent' */
- if (! (udc_read(S3C2410_UDC_USB_INT_REG)
+ if (!(udc_read(S3C2410_UDC_USB_INT_REG)
& S3C2410_UDC_USBINT_RESET))
s3c2410_udc_set_ep0_ipr(base_addr);
} else {
@@ -442,7 +444,7 @@ static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep,
u8 *buf;
u32 ep_csr;
unsigned bufferspace;
- int is_last=1;
+ int is_last = 1;
unsigned avail;
int fifo_count = 0;
u32 idx;
@@ -510,7 +512,7 @@ static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep,
/* Only ep0 debug messages are interesting */
if (idx == 0)
dprintk(DEBUG_VERBOSE, "%s fifo count : %d [last %d]\n",
- __func__, fifo_count,is_last);
+ __func__, fifo_count, is_last);
if (is_last) {
if (idx == 0) {
@@ -542,7 +544,7 @@ static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep,
static int s3c2410_udc_read_fifo_crq(struct usb_ctrlrequest *crq)
{
- unsigned char *outbuf = (unsigned char*)crq;
+ unsigned char *outbuf = (unsigned char *)crq;
int bytes_read = 0;
udc_write(0, S3C2410_UDC_INDEX_REG);
@@ -648,7 +650,7 @@ static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev,
switch (crq->bRequest) {
case USB_REQ_SET_CONFIGURATION:
- dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n");
+ dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ...\n");
if (crq->bRequestType == USB_RECIP_DEVICE) {
dev->req_config = 1;
@@ -657,7 +659,7 @@ static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev,
break;
case USB_REQ_SET_INTERFACE:
- dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n");
+ dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ...\n");
if (crq->bRequestType == USB_RECIP_INTERFACE) {
dev->req_config = 1;
@@ -666,7 +668,7 @@ static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev,
break;
case USB_REQ_SET_ADDRESS:
- dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n");
+ dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ...\n");
if (crq->bRequestType == USB_RECIP_DEVICE) {
tmp = crq->wValue & 0x7F;
@@ -679,13 +681,12 @@ static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev,
break;
case USB_REQ_GET_STATUS:
- dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n");
+ dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ...\n");
s3c2410_udc_clear_ep0_opr(base_addr);
if (dev->req_std) {
- if (!s3c2410_udc_get_status(dev, crq)) {
+ if (!s3c2410_udc_get_status(dev, crq))
return;
- }
}
break;
@@ -750,7 +751,7 @@ static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev,
/* deferred i/o == no response yet */
} else if (dev->req_pending) {
dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n");
- dev->req_pending=0;
+ dev->req_pending = 0;
}
dprintk(DEBUG_VERBOSE, "ep0state %s\n", ep0states[dev->ep0state]);
@@ -801,16 +802,14 @@ static void s3c2410_udc_handle_ep0(struct s3c2410_udc *dev)
case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
- if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) {
+ if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req)
s3c2410_udc_write_fifo(ep, req);
- }
break;
case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */
dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
- if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) {
- s3c2410_udc_read_fifo(ep,req);
- }
+ if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req)
+ s3c2410_udc_read_fifo(ep, req);
break;
case EP0_END_XFER:
@@ -836,7 +835,7 @@ static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
u32 ep_csr1;
u32 idx;
- if (likely (!list_empty(&ep->queue)))
+ if (likely(!list_empty(&ep->queue)))
req = list_entry(ep->queue.next,
struct s3c2410_request, queue);
else
@@ -858,9 +857,8 @@ static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
return;
}
- if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) {
- s3c2410_udc_write_fifo(ep,req);
- }
+ if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
+ s3c2410_udc_write_fifo(ep, req);
} else {
udc_write(idx, S3C2410_UDC_INDEX_REG);
ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);
@@ -873,9 +871,8 @@ static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
return;
}
- if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) {
- s3c2410_udc_read_fifo(ep,req);
- }
+ if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
+ s3c2410_udc_read_fifo(ep, req);
}
}
@@ -1057,7 +1054,7 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
struct s3c2410_ep *ep;
u32 max, tmp;
unsigned long flags;
- u32 csr1,csr2;
+ u32 csr1, csr2;
u32 int_en_reg;
ep = to_s3c2410_ep(_ep);
@@ -1073,7 +1070,7 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
max = usb_endpoint_maxp(desc) & 0x1fff;
- local_irq_save (flags);
+ local_irq_save(flags);
_ep->maxpacket = max & 0x7ff;
ep->ep.desc = desc;
ep->halted = 0;
@@ -1117,11 +1114,11 @@ static int s3c2410_udc_ep_enable(struct usb_ep *_ep,
/* print some debug message */
tmp = desc->bEndpointAddress;
- dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
- _ep->name,ep->num, tmp,
+ dprintk(DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
+ _ep->name, ep->num, tmp,
desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
- local_irq_restore (flags);
+ local_irq_restore(flags);
s3c2410_udc_set_halt(_ep, 0);
return 0;
@@ -1149,7 +1146,7 @@ static int s3c2410_udc_ep_disable(struct usb_ep *_ep)
ep->ep.desc = NULL;
ep->halted = 1;
- s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN);
+ s3c2410_udc_nuke(ep->dev, ep, -ESHUTDOWN);
/* disable irqs */
int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
@@ -1170,16 +1167,16 @@ s3c2410_udc_alloc_request(struct usb_ep *_ep, gfp_t mem_flags)
{
struct s3c2410_request *req;
- dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags);
+ dprintk(DEBUG_VERBOSE, "%s(%p,%d)\n", __func__, _ep, mem_flags);
if (!_ep)
return NULL;
- req = kzalloc (sizeof(struct s3c2410_request), mem_flags);
+ req = kzalloc(sizeof(struct s3c2410_request), mem_flags);
if (!req)
return NULL;
- INIT_LIST_HEAD (&req->queue);
+ INIT_LIST_HEAD(&req->queue);
return &req->req;
}
@@ -1197,7 +1194,7 @@ s3c2410_udc_free_request(struct usb_ep *_ep, struct usb_request *_req)
if (!ep || !_req || (!ep->ep.desc && _ep->name != ep0name))
return;
- WARN_ON (!list_empty (&req->queue));
+ WARN_ON(!list_empty(&req->queue));
kfree(req);
}
@@ -1220,12 +1217,12 @@ static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
}
dev = ep->dev;
- if (unlikely (!dev->driver
+ if (unlikely(!dev->driver
|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
return -ESHUTDOWN;
}
- local_irq_save (flags);
+ local_irq_save(flags);
if (unlikely(!_req || !_req->complete
|| !_req->buf || !list_empty(&req->queue))) {
@@ -1233,7 +1230,7 @@ static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
else {
dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n",
- __func__, !_req->complete,!_req->buf,
+ __func__, !_req->complete, !_req->buf,
!list_empty(&req->queue));
}
@@ -1299,7 +1296,7 @@ static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req,
}
/* pio or dma irq handler advances the queue. */
- if (likely (req != 0))
+ if (likely(req))
list_add_tail(&req->queue, &ep->queue);
local_irq_restore(flags);
@@ -1329,11 +1326,11 @@ static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
udc = to_s3c2410_udc(ep->gadget);
- local_irq_save (flags);
+ local_irq_save(flags);
- list_for_each_entry (req, &ep->queue, queue) {
+ list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req) {
- list_del_init (&req->queue);
+ list_del_init(&req->queue);
_req->status = -ECONNRESET;
retval = 0;
break;
@@ -1348,7 +1345,7 @@ static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
s3c2410_udc_done(ep, req, -ECONNRESET);
}
- local_irq_restore (flags);
+ local_irq_restore(flags);
return retval;
}
@@ -1367,7 +1364,7 @@ static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value)
return -EINVAL;
}
- local_irq_save (flags);
+ local_irq_save(flags);
idx = ep->bEndpointAddress & 0x7F;
@@ -1376,7 +1373,7 @@ static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value)
s3c2410_udc_set_ep0_de_out(base_addr);
} else {
udc_write(idx, S3C2410_UDC_INDEX_REG);
- ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN)
+ ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN)
? S3C2410_UDC_IN_CSR1_REG
: S3C2410_UDC_OUT_CSR1_REG);
@@ -1404,7 +1401,7 @@ static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value)
}
ep->halted = value ? 1 : 0;
- local_irq_restore (flags);
+ local_irq_restore(flags);
return 0;
}
@@ -1484,9 +1481,9 @@ static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on)
}
s3c2410_udc_disable(udc);
}
- }
- else
+ } else {
return -EOPNOTSUPP;
+ }
return 0;
}
@@ -1542,7 +1539,7 @@ static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
}
static int s3c2410_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int s3c2410_udc_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops s3c2410_ops = {
@@ -1617,20 +1614,20 @@ static void s3c2410_udc_reinit(struct s3c2410_udc *dev)
u32 i;
/* device/ep0 records init */
- INIT_LIST_HEAD (&dev->gadget.ep_list);
- INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
dev->ep0state = EP0_IDLE;
for (i = 0; i < S3C2410_ENDPOINTS; i++) {
struct s3c2410_ep *ep = &dev->ep[i];
if (i != 0)
- list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
+ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
ep->dev = dev;
ep->ep.desc = NULL;
ep->halted = 0;
- INIT_LIST_HEAD (&ep->queue);
+ INIT_LIST_HEAD(&ep->queue);
}
}
@@ -1668,7 +1665,7 @@ static void s3c2410_udc_enable(struct s3c2410_udc *dev)
}
static int s3c2410_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct s3c2410_udc *udc = the_controller;
int retval;
@@ -1683,13 +1680,13 @@ static int s3c2410_udc_start(struct usb_gadget_driver *driver,
return -EBUSY;
if (!bind || !driver->setup || driver->max_speed < USB_SPEED_FULL) {
- printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",
+ dev_err(&udc->gadget.dev, "Invalid driver: bind %p setup %p speed %d\n",
bind, driver->setup, driver->max_speed);
return -EINVAL;
}
#if defined(MODULE)
if (!driver->unbind) {
- printk(KERN_ERR "Invalid driver: no unbind method\n");
+ dev_err(&udc->gadget.dev, "Invalid driver: no unbind method\n");
return -EINVAL;
}
#endif
@@ -1699,15 +1696,17 @@ static int s3c2410_udc_start(struct usb_gadget_driver *driver,
udc->gadget.dev.driver = &driver->driver;
/* Bind the driver */
- if ((retval = device_add(&udc->gadget.dev)) != 0) {
- printk(KERN_ERR "Error in device_add() : %d\n",retval);
+ retval = device_add(&udc->gadget.dev);
+ if (retval) {
+ dev_err(&udc->gadget.dev, "Error in device_add() : %d\n", retval);
goto register_error;
}
dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n",
driver->driver.name);
- if ((retval = bind(&udc->gadget)) != 0) {
+ retval = bind(&udc->gadget, driver);
+ if (retval) {
device_del(&udc->gadget.dev);
goto register_error;
}
@@ -1865,7 +1864,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
}
- spin_lock_init (&udc->lock);
+ spin_lock_init(&udc->lock);
udc_info = pdev->dev.platform_data;
rsrc_start = S3C2410_PA_USBDEV;
@@ -2028,7 +2027,8 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM
-static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
+static int
+s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
{
s3c2410_udc_command(S3C2410_UDC_P_DISABLE);
@@ -2073,7 +2073,7 @@ static int __init udc_init(void)
s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);
if (IS_ERR(s3c2410_udc_debugfs_root)) {
- printk(KERN_ERR "%s: debugfs dir creation failed %ld\n",
+ pr_err("%s: debugfs dir creation failed %ld\n",
gadget_name, PTR_ERR(s3c2410_udc_debugfs_root));
s3c2410_udc_debugfs_root = NULL;
}
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index 665c07422c26..44752f531e85 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -11,7 +11,6 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
@@ -37,17 +36,13 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_acm.c"
#include "f_obex.c"
#include "f_serial.c"
#include "u_serial.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
/* Thanks to NetChip Technologies for donating this product ID.
*
@@ -61,15 +56,12 @@
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_DESCRIPTION_IDX 2
-
-static char manufacturer[50];
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = GS_VERSION_NAME,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME,
+ [USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */,
{ } /* end of list */
};
@@ -94,7 +86,7 @@ static struct usb_device_descriptor device_desc = {
/* .bMaxPacketSize0 = f(hardware) */
.idVendor = cpu_to_le16(GS_VENDOR_ID),
/* .idProduct = f(use_acm) */
- /* .bcdDevice = f(hardware) */
+ .bcdDevice = cpu_to_le16(GS_VERSION_NUM),
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
.bNumConfigurations = 1,
@@ -162,8 +154,6 @@ static struct usb_configuration serial_config_driver = {
static int __init gs_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
- struct usb_gadget *gadget = cdev->gadget;
int status;
status = gserial_setup(cdev->gadget, n_ports);
@@ -174,50 +164,14 @@ static int __init gs_bind(struct usb_composite_dev *cdev)
* contents can be overridden by the composite_dev glue.
*/
- /* device description: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
-
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail;
- strings_dev[STRING_PRODUCT_IDX].id = status;
-
- device_desc.iProduct = status;
-
- /* config description */
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail;
- strings_dev[STRING_DESCRIPTION_IDX].id = status;
-
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ status = strings_dev[STRING_DESCRIPTION_IDX].id;
serial_config_driver.iConfiguration = status;
- /* set up other descriptors */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum);
- else {
- /* this is so simple (for now, no altsettings) that it
- * SHOULD NOT have problems with bulk-capable hardware.
- * so warn about unrcognized controllers -- don't panic.
- *
- * things like configuration and altsetting numbering
- * can need hardware-specific attention though.
- */
- pr_warning("gs_bind: controller '%s' not recognized\n",
- gadget->name);
- device_desc.bcdDevice =
- cpu_to_le16(GS_VERSION_NUM | 0x0099);
- }
-
if (gadget_is_otg(cdev->gadget)) {
serial_config_driver.descriptors = otg_desc;
serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
@@ -229,6 +183,7 @@ static int __init gs_bind(struct usb_composite_dev *cdev)
if (status < 0)
goto fail;
+ usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "%s\n", GS_VERSION_NAME);
return 0;
@@ -238,11 +193,12 @@ fail:
return status;
}
-static struct usb_composite_driver gserial_driver = {
+static __refdata struct usb_composite_driver gserial_driver = {
.name = "g_serial",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
+ .bind = gs_bind,
};
static int __init init(void)
@@ -271,7 +227,7 @@ static int __init init(void)
}
strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label;
- return usb_composite_probe(&gserial_driver, gs_bind);
+ return usb_composite_probe(&gserial_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c
index 5444866e13ef..eaa1005377fc 100644
--- a/drivers/usb/gadget/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/tcm_usb_gadget.c
@@ -25,13 +25,10 @@
#include <target/configfs_macros.h>
#include <asm/unaligned.h>
-#include "usbstring.c"
-#include "epautoconf.c"
-#include "config.c"
-#include "composite.c"
-
#include "tcm_usb_gadget.h"
+USB_GADGET_COMPOSITE_OPTIONS();
+
static struct target_fabric_configfs *usbg_fabric_configfs;
static inline struct f_uas *to_f_uas(struct usb_function *f)
@@ -1977,7 +1974,6 @@ static struct usb_interface_descriptor bot_intf_desc = {
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
.bInterfaceSubClass = USB_SC_SCSI,
.bInterfaceProtocol = USB_PR_BULK,
- .iInterface = USB_G_STR_INT_UAS,
};
static struct usb_interface_descriptor uasp_intf_desc = {
@@ -1988,7 +1984,6 @@ static struct usb_interface_descriptor uasp_intf_desc = {
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
.bInterfaceSubClass = USB_SC_SCSI,
.bInterfaceProtocol = USB_PR_UAS,
- .iInterface = USB_G_STR_INT_BBB,
};
static struct usb_endpoint_descriptor uasp_bi_desc = {
@@ -2209,20 +2204,16 @@ static struct usb_device_descriptor usbg_device_desc = {
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.idVendor = cpu_to_le16(UAS_VENDOR_ID),
.idProduct = cpu_to_le16(UAS_PRODUCT_ID),
- .iManufacturer = USB_G_STR_MANUFACTOR,
- .iProduct = USB_G_STR_PRODUCT,
- .iSerialNumber = USB_G_STR_SERIAL,
-
.bNumConfigurations = 1,
};
static struct usb_string usbg_us_strings[] = {
- { USB_G_STR_MANUFACTOR, "Target Manufactor"},
- { USB_G_STR_PRODUCT, "Target Product"},
- { USB_G_STR_SERIAL, "000000000001"},
- { USB_G_STR_CONFIG, "default config"},
- { USB_G_STR_INT_UAS, "USB Attached SCSI"},
- { USB_G_STR_INT_BBB, "Bulk Only Transport"},
+ [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufactor",
+ [USB_GADGET_PRODUCT_IDX].s = "Target Product",
+ [USB_GADGET_SERIAL_IDX].s = "000000000001",
+ [USB_G_STR_CONFIG].s = "default config",
+ [USB_G_STR_INT_UAS].s = "USB Attached SCSI",
+ [USB_G_STR_INT_BBB].s = "Bulk Only Transport",
{ },
};
@@ -2244,7 +2235,6 @@ static int guas_unbind(struct usb_composite_dev *cdev)
static struct usb_configuration usbg_config_driver = {
.label = "Linux Target",
.bConfigurationValue = 1,
- .iConfiguration = USB_G_STR_CONFIG,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
@@ -2417,6 +2407,9 @@ static int usbg_cfg_bind(struct usb_configuration *c)
fu->function.disable = usbg_disable;
fu->tpg = the_only_tpg_I_currently_have;
+ bot_intf_desc.iInterface = usbg_us_strings[USB_G_STR_INT_BBB].id;
+ uasp_intf_desc.iInterface = usbg_us_strings[USB_G_STR_INT_UAS].id;
+
ret = usb_add_function(c, &fu->function);
if (ret)
goto err;
@@ -2431,22 +2424,38 @@ static int usb_target_bind(struct usb_composite_dev *cdev)
{
int ret;
+ ret = usb_string_ids_tab(cdev, usbg_us_strings);
+ if (ret)
+ return ret;
+
+ usbg_device_desc.iManufacturer =
+ usbg_us_strings[USB_GADGET_MANUFACTURER_IDX].id;
+ usbg_device_desc.iProduct = usbg_us_strings[USB_GADGET_PRODUCT_IDX].id;
+ usbg_device_desc.iSerialNumber =
+ usbg_us_strings[USB_GADGET_SERIAL_IDX].id;
+ usbg_config_driver.iConfiguration =
+ usbg_us_strings[USB_G_STR_CONFIG].id;
+
ret = usb_add_config(cdev, &usbg_config_driver,
usbg_cfg_bind);
+ if (ret)
+ return ret;
+ usb_composite_overwrite_options(cdev, &coverwrite);
return 0;
}
-static struct usb_composite_driver usbg_driver = {
+static __refdata struct usb_composite_driver usbg_driver = {
.name = "g_target",
.dev = &usbg_device_desc,
.strings = usbg_strings,
.max_speed = USB_SPEED_SUPER,
+ .bind = usb_target_bind,
.unbind = guas_unbind,
};
static int usbg_attach(struct usbg_tpg *tpg)
{
- return usb_composite_probe(&usbg_driver, usb_target_bind);
+ return usb_composite_probe(&usbg_driver);
}
static void usbg_detach(struct usbg_tpg *tpg)
diff --git a/drivers/usb/gadget/tcm_usb_gadget.h b/drivers/usb/gadget/tcm_usb_gadget.h
index bb18999a9a8d..8289219925b8 100644
--- a/drivers/usb/gadget/tcm_usb_gadget.h
+++ b/drivers/usb/gadget/tcm_usb_gadget.h
@@ -16,12 +16,11 @@
#define UASP_SS_EP_COMP_LOG_STREAMS 4
#define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS)
-#define USB_G_STR_MANUFACTOR 1
-#define USB_G_STR_PRODUCT 2
-#define USB_G_STR_SERIAL 3
-#define USB_G_STR_CONFIG 4
-#define USB_G_STR_INT_UAS 5
-#define USB_G_STR_INT_BBB 6
+enum {
+ USB_G_STR_CONFIG = USB_GADGET_FIRST_AVAIL_IDX,
+ USB_G_STR_INT_UAS,
+ USB_G_STR_INT_BBB,
+};
#define USB_G_ALT_INT_BBB 0
#define USB_G_ALT_INT_UAS 1
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 0e5230926154..b9c46900c2c1 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -14,6 +14,7 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/gfp.h>
#include <linux/device.h>
#include <linux/ctype.h>
@@ -83,17 +84,10 @@ struct eth_dev {
#define DEFAULT_QLEN 2 /* double buffering by default */
-
-#ifdef CONFIG_USB_GADGET_DUALSPEED
-
static unsigned qmult = 5;
module_param(qmult, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");
-#else /* full speed (low speed doesn't do bulk) */
-#define qmult 1
-#endif
-
/* for dual-speed hardware, use deeper queues at high/super speed */
static inline int qlen(struct usb_gadget *gadget)
{
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index e5e44f8cde9a..f3cd9690b101 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -118,7 +118,7 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request);
*/
static inline int usb_gadget_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
return gadget->ops->start(driver, bind);
}
@@ -262,8 +262,8 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
if (udc_is_newstyle(udc)) {
- udc->driver->disconnect(udc->gadget);
usb_gadget_disconnect(udc->gadget);
+ udc->driver->disconnect(udc->gadget);
udc->driver->unbind(udc->gadget);
usb_gadget_udc_stop(udc->gadget, udc->driver);
} else {
@@ -311,13 +311,12 @@ EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
/* ------------------------------------------------------------------------- */
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
int ret;
- if (!driver || !bind || !driver->setup)
+ if (!driver || !driver->bind || !driver->setup)
return -EINVAL;
mutex_lock(&udc_lock);
@@ -339,7 +338,7 @@ found:
udc->dev.driver = &driver->driver;
if (udc_is_newstyle(udc)) {
- ret = bind(udc->gadget);
+ ret = driver->bind(udc->gadget, driver);
if (ret)
goto err1;
ret = usb_gadget_udc_start(udc->gadget, driver);
@@ -350,7 +349,7 @@ found:
usb_gadget_connect(udc->gadget);
} else {
- ret = usb_gadget_start(udc->gadget, driver, bind);
+ ret = usb_gadget_start(udc->gadget, driver, driver->bind);
if (ret)
goto err1;
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c
index 4d25b9009edf..1f49fce0f0b7 100644
--- a/drivers/usb/gadget/usbstring.c
+++ b/drivers/usb/gadget/usbstring.c
@@ -9,6 +9,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/device.h>
@@ -68,4 +69,4 @@ usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
buf [1] = USB_DT_STRING;
return buf [0];
}
-
+EXPORT_SYMBOL_GPL(usb_gadget_get_string);
diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c
index 120e134e805e..69cf5c2cd335 100644
--- a/drivers/usb/gadget/webcam.c
+++ b/drivers/usb/gadget/webcam.c
@@ -23,16 +23,12 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "uvc_queue.c"
#include "uvc_video.c"
#include "uvc_v4l2.c"
#include "f_uvc.c"
+USB_GADGET_COMPOSITE_OPTIONS();
/* --------------------------------------------------------------------------
* Device descriptor
*/
@@ -47,13 +43,12 @@ static char webcam_config_label[] = "Video";
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_DESCRIPTION_IDX 2
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_string webcam_strings[] = {
- [STRING_MANUFACTURER_IDX].s = webcam_vendor_label,
- [STRING_PRODUCT_IDX].s = webcam_product_label,
+ [USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label,
+ [USB_GADGET_PRODUCT_IDX].s = webcam_product_label,
+ [USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = webcam_config_label,
{ }
};
@@ -358,26 +353,22 @@ webcam_bind(struct usb_composite_dev *cdev)
/* Allocate string descriptor numbers ... note that string contents
* can be overridden by the composite_dev glue.
*/
- if ((ret = usb_string_id(cdev)) < 0)
- goto error;
- webcam_strings[STRING_MANUFACTURER_IDX].id = ret;
- webcam_device_descriptor.iManufacturer = ret;
-
- if ((ret = usb_string_id(cdev)) < 0)
- goto error;
- webcam_strings[STRING_PRODUCT_IDX].id = ret;
- webcam_device_descriptor.iProduct = ret;
-
- if ((ret = usb_string_id(cdev)) < 0)
+ ret = usb_string_ids_tab(cdev, webcam_strings);
+ if (ret < 0)
goto error;
- webcam_strings[STRING_DESCRIPTION_IDX].id = ret;
- webcam_config_driver.iConfiguration = ret;
+ webcam_device_descriptor.iManufacturer =
+ webcam_strings[USB_GADGET_MANUFACTURER_IDX].id;
+ webcam_device_descriptor.iProduct =
+ webcam_strings[USB_GADGET_PRODUCT_IDX].id;
+ webcam_config_driver.iConfiguration =
+ webcam_strings[STRING_DESCRIPTION_IDX].id;
/* Register our configuration. */
if ((ret = usb_add_config(cdev, &webcam_config_driver,
webcam_config_bind)) < 0)
goto error;
+ usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "Webcam Video Gadget\n");
return 0;
@@ -390,18 +381,19 @@ error:
* Driver
*/
-static struct usb_composite_driver webcam_driver = {
+static __refdata struct usb_composite_driver webcam_driver = {
.name = "g_webcam",
.dev = &webcam_device_descriptor,
.strings = webcam_device_strings,
.max_speed = USB_SPEED_SUPER,
+ .bind = webcam_bind,
.unbind = webcam_unbind,
};
static int __init
webcam_init(void)
{
- return usb_composite_probe(&webcam_driver, webcam_bind);
+ return usb_composite_probe(&webcam_driver);
}
static void __exit
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 12ad516ada77..6bf4c0611365 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -42,7 +42,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/utsname.h>
#include <linux/device.h>
#include "g_zero.h"
@@ -58,15 +57,11 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_sourcesink.c"
#include "f_loopback.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
#define DRIVER_VERSION "Cinco de Mayo 2008"
@@ -141,20 +136,13 @@ const struct usb_descriptor_header *otg_desc[] = {
#endif
/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_SERIAL_IDX 2
-
-static char manufacturer[50];
-
/* default serial number takes at least two packets */
static char serial[] = "0123456789.0123456789.0123456789";
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = longname,
- [STRING_SERIAL_IDX].s = serial,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = longname,
+ [USB_GADGET_SERIAL_IDX].s = serial,
{ } /* end of list */
};
@@ -265,30 +253,18 @@ static void zero_resume(struct usb_composite_dev *cdev)
static int __init zero_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
- struct usb_gadget *gadget = cdev->gadget;
- int id;
+ int status;
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
- id = usb_string_id(cdev);
- if (id < 0)
- return id;
- strings_dev[STRING_MANUFACTURER_IDX].id = id;
- device_desc.iManufacturer = id;
-
- id = usb_string_id(cdev);
- if (id < 0)
- return id;
- strings_dev[STRING_PRODUCT_IDX].id = id;
- device_desc.iProduct = id;
-
- id = usb_string_id(cdev);
- if (id < 0)
- return id;
- strings_dev[STRING_SERIAL_IDX].id = id;
- device_desc.iSerialNumber = id;
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ return status;
+
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id;
setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);
@@ -303,28 +279,10 @@ static int __init zero_bind(struct usb_composite_dev *cdev)
loopback_add(cdev, autoresume != 0);
}
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
- else {
- /* gadget zero is so simple (for now, no altsettings) that
- * it SHOULD NOT have problems with bulk-capable hardware.
- * so just warn about unrcognized controllers -- don't panic.
- *
- * things like configuration and altsetting numbering
- * can need hardware-specific attention though.
- */
- pr_warning("%s: controller '%s' not recognized\n",
- longname, gadget->name);
- device_desc.bcdDevice = cpu_to_le16(0x9999);
- }
+ usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
-
return 0;
}
@@ -334,11 +292,12 @@ static int zero_unbind(struct usb_composite_dev *cdev)
return 0;
}
-static struct usb_composite_driver zero_driver = {
+static __refdata struct usb_composite_driver zero_driver = {
.name = "zero",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
+ .bind = zero_bind,
.unbind = zero_unbind,
.suspend = zero_suspend,
.resume = zero_resume,
@@ -349,7 +308,7 @@ MODULE_LICENSE("GPL");
static int __init init(void)
{
- return usb_composite_probe(&zero_driver, zero_bind);
+ return usb_composite_probe(&zero_driver);
}
module_init(init);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 276add2358a1..3f1431d37e1c 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -18,8 +18,8 @@ config USB_C67X00_HCD
module will be called c67x00.
config USB_XHCI_HCD
- tristate "xHCI HCD (USB 3.0) support (EXPERIMENTAL)"
- depends on USB && USB_ARCH_HAS_XHCI && EXPERIMENTAL
+ tristate "xHCI HCD (USB 3.0) support"
+ depends on USB && USB_ARCH_HAS_XHCI
---help---
The eXtensible Host Controller Interface (xHCI) is standard for USB 3.0
"SuperSpeed" host controller hardware.
@@ -262,7 +262,7 @@ config USB_ISP116X_HCD
config USB_ISP1760_HCD
tristate "ISP 1760 HCD support"
- depends on USB && EXPERIMENTAL
+ depends on USB
---help---
The ISP1760 chip is a USB 2.0 host controller.
@@ -292,7 +292,7 @@ config USB_OHCI_HCD
depends on USB && USB_ARCH_HAS_OHCI
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
select USB_OTG_UTILS if ARCH_OMAP
- select USB_ISP1301 if ARCH_LPC32XX
+ depends on USB_ISP1301 || !ARCH_LPC32XX
---help---
The Open Host Controller Interface (OHCI) is a standard for accessing
USB 1.1 host controller hardware. It does more in hardware than Intel's
@@ -376,7 +376,7 @@ config USB_OHCI_HCD_PCI
config USB_OHCI_HCD_SSB
bool "OHCI support for Broadcom SSB OHCI core (DEPRECATED)"
- depends on USB_OHCI_HCD && (SSB = y || SSB = USB_OHCI_HCD) && EXPERIMENTAL
+ depends on USB_OHCI_HCD && (SSB = y || SSB = USB_OHCI_HCD)
select USB_HCD_SSB
select USB_OHCI_HCD_PLATFORM
default n
@@ -414,21 +414,21 @@ config USB_CNS3XXX_OHCI
config USB_OHCI_HCD_PLATFORM
bool "Generic OHCI driver for a platform device"
- depends on USB_OHCI_HCD && EXPERIMENTAL
+ depends on USB_OHCI_HCD
default n
---help---
Adds an OHCI host driver for a generic platform device, which
- provieds a memory space and an irq.
+ provides a memory space and an irq.
If unsure, say N.
config USB_EHCI_HCD_PLATFORM
bool "Generic EHCI driver for a platform device"
- depends on USB_EHCI_HCD && EXPERIMENTAL
+ depends on USB_EHCI_HCD
default n
---help---
Adds an EHCI host driver for a generic platform device, which
- provieds a memory space and an irq.
+ provides a memory space and an irq.
If unsure, say N.
@@ -450,7 +450,7 @@ config USB_OHCI_LITTLE_ENDIAN
config USB_UHCI_HCD
tristate "UHCI HCD (most Intel and VIA) support"
- depends on USB && (PCI || SPARC_LEON)
+ depends on USB && (PCI || SPARC_LEON || ARCH_VT8500)
---help---
The Universal Host Controller Interface is a standard by Intel for
accessing the USB hardware in the PC (which is also called the USB
@@ -468,7 +468,15 @@ config USB_UHCI_HCD
config USB_UHCI_SUPPORT_NON_PCI_HC
bool
depends on USB_UHCI_HCD
- default y if SPARC_LEON
+ default y if (SPARC_LEON || ARCH_VT8500)
+
+config USB_UHCI_PLATFORM
+ bool "Generic UHCI Platform Driver support"
+ depends on USB_UHCI_SUPPORT_NON_PCI_HC
+ default y if ARCH_VT8500
+ ---help---
+ Enable support for generic UHCI platform devices that require no
+ additional configuration.
config USB_UHCI_BIG_ENDIAN_MMIO
bool
@@ -583,8 +591,7 @@ config USB_RENESAS_USBHS_HCD
module will be called renesas-usbhs.
config USB_WHCI_HCD
- tristate "Wireless USB Host Controller Interface (WHCI) driver (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ tristate "Wireless USB Host Controller Interface (WHCI) driver"
depends on PCI && USB && UWB
select USB_WUSB
select UWB_WHCI
@@ -596,8 +603,7 @@ config USB_WHCI_HCD
will be called "whci-hcd".
config USB_HWA_HCD
- tristate "Host Wire Adapter (HWA) driver (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ tristate "Host Wire Adapter (HWA) driver"
depends on USB && UWB
select USB_WUSB
select UWB_HWA
@@ -648,7 +654,7 @@ config USB_OCTEON2_COMMON
config USB_HCD_BCMA
tristate "BCMA usb host driver"
- depends on BCMA && EXPERIMENTAL
+ depends on BCMA
select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
help
@@ -660,7 +666,7 @@ config USB_HCD_BCMA
config USB_HCD_SSB
tristate "SSB usb host driver"
- depends on SSB && EXPERIMENTAL
+ depends on SSB
select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
select USB_EHCI_HCD_PLATFORM if USB_EHCI_HCD
help
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index a47e2cffaaf8..411bb74152eb 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -150,31 +150,24 @@ static int __devinit ehci_atmel_drv_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description)) {
- dev_dbg(&pdev->dev, "controller already in use\n");
- retval = -EBUSY;
- goto fail_request_resource;
- }
-
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (hcd->regs == NULL) {
dev_dbg(&pdev->dev, "error mapping memory\n");
retval = -EFAULT;
- goto fail_ioremap;
+ goto fail_request_resource;
}
- iclk = clk_get(&pdev->dev, "ehci_clk");
+ iclk = devm_clk_get(&pdev->dev, "ehci_clk");
if (IS_ERR(iclk)) {
dev_err(&pdev->dev, "Error getting interface clock\n");
retval = -ENOENT;
- goto fail_get_iclk;
+ goto fail_request_resource;
}
- fclk = clk_get(&pdev->dev, "uhpck");
+ fclk = devm_clk_get(&pdev->dev, "uhpck");
if (IS_ERR(fclk)) {
dev_err(&pdev->dev, "Error getting function clock\n");
retval = -ENOENT;
- goto fail_get_fclk;
+ goto fail_request_resource;
}
atmel_start_ehci(pdev);
@@ -187,13 +180,6 @@ static int __devinit ehci_atmel_drv_probe(struct platform_device *pdev)
fail_add_hcd:
atmel_stop_ehci(pdev);
- clk_put(fclk);
-fail_get_fclk:
- clk_put(iclk);
-fail_get_iclk:
- iounmap(hcd->regs);
-fail_ioremap:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
fail_request_resource:
usb_put_hcd(hcd);
fail_create_hcd:
@@ -209,13 +195,9 @@ static int __devexit ehci_atmel_drv_remove(struct platform_device *pdev)
ehci_shutdown(hcd);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
atmel_stop_ehci(pdev);
- clk_put(fclk);
- clk_put(iclk);
fclk = iclk = NULL;
return 0;
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
index cba10d625a5d..65c945eb4144 100644
--- a/drivers/usb/host/ehci-au1xxx.c
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -98,23 +98,17 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- pr_debug("request_mem_region failed");
- ret = -EBUSY;
- goto err1;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hcd->regs) {
- pr_debug("ioremap failed");
+ pr_debug("devm_request_and_ioremap failed");
ret = -ENOMEM;
- goto err2;
+ goto err1;
}
if (alchemy_usb_control(ALCHEMY_USB_EHCI0, 1)) {
printk(KERN_INFO "%s: controller init failed!\n", pdev->name);
ret = -ENODEV;
- goto err3;
+ goto err1;
}
ret = usb_add_hcd(hcd, pdev->resource[1].start,
@@ -125,10 +119,6 @@ static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
}
alchemy_usb_control(ALCHEMY_USB_EHCI0, 0);
-err3:
- iounmap(hcd->regs);
-err2:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return ret;
@@ -140,8 +130,6 @@ static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
usb_remove_hcd(hcd);
alchemy_usb_control(ALCHEMY_USB_EHCI0, 0);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/usb/host/ehci-cns3xxx.c b/drivers/usb/host/ehci-cns3xxx.c
index caaa3e5be334..d91708d2e729 100644
--- a/drivers/usb/host/ehci-cns3xxx.c
+++ b/drivers/usb/host/ehci-cns3xxx.c
@@ -105,27 +105,17 @@ static int cns3xxx_ehci_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description)) {
- dev_dbg(dev, "controller already in use\n");
- retval = -EBUSY;
- goto err1;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (hcd->regs == NULL) {
dev_dbg(dev, "error mapping memory\n");
retval = -EFAULT;
- goto err2;
+ goto err1;
}
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval == 0)
return retval;
- iounmap(hcd->regs);
-err2:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
@@ -137,8 +127,6 @@ static int cns3xxx_ehci_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
/*
* EHCI and OHCI share the same clock and power,
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index f0c00de035ef..1599806e3d47 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -653,10 +653,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
seen [seen_count++].qh = p.qh;
} else
temp = 0;
- if (p.qh) {
- tag = Q_NEXT_TYPE(ehci, hw->hw_next);
- p = p.qh->qh_next;
- }
+ tag = Q_NEXT_TYPE(ehci, hw->hw_next);
+ p = p.qh->qh_next;
break;
case Q_TYPE_FSTN:
temp = scnprintf (next, size,
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index b7451b29c5ac..9bfde82078ec 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -210,11 +210,11 @@ static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
usb_put_hcd(hcd);
}
-static void ehci_fsl_setup_phy(struct usb_hcd *hcd,
+static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
enum fsl_usb2_phy_modes phy_mode,
unsigned int port_offset)
{
- u32 portsc, temp;
+ u32 portsc;
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
void __iomem *non_ehci = hcd->regs;
struct device *dev = hcd->self.controller;
@@ -232,9 +232,15 @@ static void ehci_fsl_setup_phy(struct usb_hcd *hcd,
case FSL_USB2_PHY_ULPI:
if (pdata->controller_ver) {
/* controller version 1.6 or above */
- temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
- out_be32(non_ehci + FSL_SOC_USB_CTRL, temp |
- USB_CTRL_USB_EN | ULPI_PHY_CLK_SEL);
+ setbits32(non_ehci + FSL_SOC_USB_CTRL,
+ ULPI_PHY_CLK_SEL);
+ /*
+ * Due to controller issue of PHY_CLK_VALID in ULPI
+ * mode, we set USB_CTRL_USB_EN before checking
+ * PHY_CLK_VALID, otherwise PHY_CLK_VALID doesn't work.
+ */
+ clrsetbits_be32(non_ehci + FSL_SOC_USB_CTRL,
+ UTMI_PHY_EN, USB_CTRL_USB_EN);
}
portsc |= PORT_PTS_ULPI;
break;
@@ -247,9 +253,7 @@ static void ehci_fsl_setup_phy(struct usb_hcd *hcd,
case FSL_USB2_PHY_UTMI:
if (pdata->controller_ver) {
/* controller version 1.6 or above */
- temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
- out_be32(non_ehci + FSL_SOC_USB_CTRL, temp |
- UTMI_PHY_EN | USB_CTRL_USB_EN);
+ setbits32(non_ehci + FSL_SOC_USB_CTRL, UTMI_PHY_EN);
mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI PHY CLK to
become stable - 10ms*/
}
@@ -262,23 +266,33 @@ static void ehci_fsl_setup_phy(struct usb_hcd *hcd,
case FSL_USB2_PHY_NONE:
break;
}
+
+ if (pdata->controller_ver && (phy_mode == FSL_USB2_PHY_ULPI)) {
+ /* check PHY_CLK_VALID to get phy clk valid */
+ if (!spin_event_timeout(in_be32(non_ehci + FSL_SOC_USB_CTRL) &
+ PHY_CLK_VALID, FSL_USB_PHY_CLK_TIMEOUT, 0)) {
+ printk(KERN_WARNING "fsl-ehci: USB PHY clock invalid\n");
+ return -EINVAL;
+ }
+ }
+
ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]);
+
+ if (phy_mode != FSL_USB2_PHY_ULPI)
+ setbits32(non_ehci + FSL_SOC_USB_CTRL, USB_CTRL_USB_EN);
+
+ return 0;
}
-static void ehci_fsl_usb_setup(struct ehci_hcd *ehci)
+static int ehci_fsl_usb_setup(struct ehci_hcd *ehci)
{
struct usb_hcd *hcd = ehci_to_hcd(ehci);
struct fsl_usb2_platform_data *pdata;
void __iomem *non_ehci = hcd->regs;
- u32 temp;
pdata = hcd->self.controller->platform_data;
- /* Enable PHY interface in the control reg. */
if (pdata->have_sysif_regs) {
- temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
- out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
-
/*
* Turn on cache snooping hardware, since some PowerPC platforms
* wholly rely on hardware to deal with cache coherent
@@ -293,7 +307,8 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci)
if ((pdata->operating_mode == FSL_USB2_DR_HOST) ||
(pdata->operating_mode == FSL_USB2_DR_OTG))
- ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0);
+ if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0))
+ return -EINVAL;
if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
unsigned int chip, rev, svr;
@@ -307,9 +322,12 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci)
ehci->has_fsl_port_bug = 1;
if (pdata->port_enables & FSL_USB2_PORT0_ENABLED)
- ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0);
+ if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0))
+ return -EINVAL;
+
if (pdata->port_enables & FSL_USB2_PORT1_ENABLED)
- ehci_fsl_setup_phy(hcd, pdata->phy_mode, 1);
+ if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 1))
+ return -EINVAL;
}
if (pdata->have_sysif_regs) {
@@ -322,12 +340,15 @@ static void ehci_fsl_usb_setup(struct ehci_hcd *ehci)
#endif
out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
}
+
+ return 0;
}
/* called after powerup, by probe or system-pm "wakeup" */
static int ehci_fsl_reinit(struct ehci_hcd *ehci)
{
- ehci_fsl_usb_setup(ehci);
+ if (ehci_fsl_usb_setup(ehci))
+ return -EINVAL;
ehci_port_power(ehci, 0);
return 0;
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
index 88403684d10b..dbd292e9f0a7 100644
--- a/drivers/usb/host/ehci-fsl.h
+++ b/drivers/usb/host/ehci-fsl.h
@@ -61,4 +61,5 @@
#define PLL_RESET (1<<8)
#define UTMI_PHY_EN (1<<9)
#define ULPI_PHY_CLK_SEL (1<<10)
+#define PHY_CLK_VALID (1<<17)
#endif /* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c
index 22ca45c079a4..3180cb3624d9 100644
--- a/drivers/usb/host/ehci-grlib.c
+++ b/drivers/usb/host/ehci-grlib.c
@@ -127,12 +127,6 @@ static int __devinit ehci_hcd_grlib_probe(struct platform_device *op)
hcd->rsrc_start = res.start;
hcd->rsrc_len = resource_size(&res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__);
- rv = -EBUSY;
- goto err_rmr;
- }
-
irq = irq_of_parse_and_map(dn, 0);
if (irq == NO_IRQ) {
printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__);
@@ -140,9 +134,9 @@ static int __devinit ehci_hcd_grlib_probe(struct platform_device *op)
goto err_irq;
}
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&op->dev, &res);
if (!hcd->regs) {
- printk(KERN_ERR "%s: ioremap failed\n", __FILE__);
+ pr_err("%s: devm_request_and_ioremap failed\n", __FILE__);
rv = -ENOMEM;
goto err_ioremap;
}
@@ -161,17 +155,13 @@ static int __devinit ehci_hcd_grlib_probe(struct platform_device *op)
rv = usb_add_hcd(hcd, irq, 0);
if (rv)
- goto err_ehci;
+ goto err_ioremap;
return 0;
-err_ehci:
- iounmap(hcd->regs);
err_ioremap:
irq_dispose_mapping(irq);
err_irq:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-err_rmr:
usb_put_hcd(hcd);
return rv;
@@ -188,9 +178,7 @@ static int ehci_hcd_grlib_remove(struct platform_device *op)
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
irq_dispose_mapping(hcd->irq);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index b05c6865b610..6bf6c42481e8 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -228,7 +228,7 @@ static int ehci_reset (struct ehci_hcd *ehci)
/* If the EHCI debug controller is active, special care must be
* taken before and after a host controller reset */
- if (ehci->debug && !dbgp_reset_prep())
+ if (ehci->debug && !dbgp_reset_prep(ehci_to_hcd(ehci)))
ehci->debug = NULL;
command |= CMD_RESET;
@@ -251,7 +251,7 @@ static int ehci_reset (struct ehci_hcd *ehci)
tdi_reset (ehci);
if (ehci->debug)
- dbgp_external_startup();
+ dbgp_external_startup(ehci_to_hcd(ehci));
ehci->port_c_suspend = ehci->suspended_ports =
ehci->resuming_ports = 0;
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index c7880223738a..914ce9370e70 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -353,10 +353,10 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
goto shutdown;
if (unlikely(ehci->debug)) {
- if (!dbgp_reset_prep())
+ if (!dbgp_reset_prep(hcd))
ehci->debug = NULL;
else
- dbgp_external_startup();
+ dbgp_external_startup(hcd);
}
/* Ideally and we've got a real resume here, and no port's power
diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c
index 488d401942e9..f224c0a48bed 100644
--- a/drivers/usb/host/ehci-ixp4xx.c
+++ b/drivers/usb/host/ehci-ixp4xx.c
@@ -98,30 +98,19 @@ static int ixp4xx_ehci_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description)) {
- dev_dbg(&pdev->dev, "controller already in use\n");
- retval = -EBUSY;
- goto fail_request_resource;
- }
-
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (hcd->regs == NULL) {
dev_dbg(&pdev->dev, "error mapping memory\n");
retval = -EFAULT;
- goto fail_ioremap;
+ goto fail_request_resource;
}
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval)
- goto fail_add_hcd;
+ goto fail_request_resource;
return retval;
-fail_add_hcd:
- iounmap(hcd->regs);
-fail_ioremap:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
fail_request_resource:
usb_put_hcd(hcd);
fail_create_hcd:
@@ -134,8 +123,6 @@ static int ixp4xx_ehci_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/ehci-ls1x.c b/drivers/usb/host/ehci-ls1x.c
index a283e59709d6..ca759652626b 100644
--- a/drivers/usb/host/ehci-ls1x.c
+++ b/drivers/usb/host/ehci-ls1x.c
@@ -106,29 +106,19 @@ static int ehci_hcd_ls1x_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- dev_dbg(&pdev->dev, "controller already in use\n");
- ret = -EBUSY;
- goto err_put_hcd;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (hcd->regs == NULL) {
dev_dbg(&pdev->dev, "error mapping memory\n");
ret = -EFAULT;
- goto err_release_region;
+ goto err_put_hcd;
}
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret)
- goto err_iounmap;
+ goto err_put_hcd;
return ret;
-err_iounmap:
- iounmap(hcd->regs);
-err_release_region:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_put_hcd:
usb_put_hcd(hcd);
return ret;
@@ -139,8 +129,6 @@ static int ehci_hcd_ls1x_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 17dd9e94001e..4af4dc5b618c 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -133,7 +133,7 @@ static int ehci_msm_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
@@ -145,17 +145,17 @@ static int ehci_msm_probe(struct platform_device *pdev)
* powering up VBUS, mapping of registers address space and power
* management.
*/
- phy = usb_get_phy(USB_PHY_TYPE_USB2);
+ phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(phy)) {
dev_err(&pdev->dev, "unable to find transceiver\n");
ret = -ENODEV;
- goto unmap;
+ goto put_hcd;
}
ret = otg_set_host(phy->otg, &hcd->self);
if (ret < 0) {
dev_err(&pdev->dev, "unable to register with transceiver\n");
- goto put_transceiver;
+ goto put_hcd;
}
device_init_wakeup(&pdev->dev, 1);
@@ -168,10 +168,6 @@ static int ehci_msm_probe(struct platform_device *pdev)
return 0;
-put_transceiver:
- usb_put_phy(phy);
-unmap:
- iounmap(hcd->regs);
put_hcd:
usb_put_hcd(hcd);
@@ -187,7 +183,6 @@ static int __devexit ehci_msm_remove(struct platform_device *pdev)
pm_runtime_set_suspended(&pdev->dev);
otg_set_host(phy->otg, NULL);
- usb_put_phy(phy);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c
index f6df1ccc9617..f7bfc0b898b9 100644
--- a/drivers/usb/host/ehci-mv.c
+++ b/drivers/usb/host/ehci-mv.c
@@ -161,7 +161,7 @@ static int mv_ehci_probe(struct platform_device *pdev)
return -ENOMEM;
size = sizeof(*ehci_mv) + sizeof(struct clk *) * pdata->clknum;
- ehci_mv = kzalloc(size, GFP_KERNEL);
+ ehci_mv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
if (ehci_mv == NULL) {
dev_err(&pdev->dev, "cannot allocate ehci_hcd_mv\n");
retval = -ENOMEM;
@@ -175,12 +175,12 @@ static int mv_ehci_probe(struct platform_device *pdev)
ehci_mv->clknum = pdata->clknum;
for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) {
ehci_mv->clk[clk_i] =
- clk_get(&pdev->dev, pdata->clkname[clk_i]);
+ devm_clk_get(&pdev->dev, pdata->clkname[clk_i]);
if (IS_ERR(ehci_mv->clk[clk_i])) {
dev_err(&pdev->dev, "error get clck \"%s\"\n",
pdata->clkname[clk_i]);
retval = PTR_ERR(ehci_mv->clk[clk_i]);
- goto err_put_clk;
+ goto err_clear_drvdata;
}
}
@@ -188,34 +188,36 @@ static int mv_ehci_probe(struct platform_device *pdev)
if (r == NULL) {
dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
retval = -ENODEV;
- goto err_put_clk;
+ goto err_clear_drvdata;
}
- ehci_mv->phy_regs = ioremap(r->start, resource_size(r));
+ ehci_mv->phy_regs = devm_ioremap(&pdev->dev, r->start,
+ resource_size(r));
if (ehci_mv->phy_regs == 0) {
dev_err(&pdev->dev, "failed to map phy I/O memory\n");
retval = -EFAULT;
- goto err_put_clk;
+ goto err_clear_drvdata;
}
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs");
if (!r) {
dev_err(&pdev->dev, "no I/O memory resource defined\n");
retval = -ENODEV;
- goto err_iounmap_phyreg;
+ goto err_clear_drvdata;
}
- ehci_mv->cap_regs = ioremap(r->start, resource_size(r));
+ ehci_mv->cap_regs = devm_ioremap(&pdev->dev, r->start,
+ resource_size(r));
if (ehci_mv->cap_regs == NULL) {
dev_err(&pdev->dev, "failed to map I/O memory\n");
retval = -EFAULT;
- goto err_iounmap_phyreg;
+ goto err_clear_drvdata;
}
retval = mv_ehci_enable(ehci_mv);
if (retval) {
dev_err(&pdev->dev, "init phy error %d\n", retval);
- goto err_iounmap_capreg;
+ goto err_clear_drvdata;
}
offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK;
@@ -239,7 +241,7 @@ static int mv_ehci_probe(struct platform_device *pdev)
ehci_mv->mode = pdata->mode;
if (ehci_mv->mode == MV_USB_MODE_OTG) {
#ifdef CONFIG_USB_OTG_UTILS
- ehci_mv->otg = usb_get_phy(USB_PHY_TYPE_USB2);
+ ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(ehci_mv->otg)) {
dev_err(&pdev->dev,
"unable to find transceiver\n");
@@ -252,7 +254,7 @@ static int mv_ehci_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"unable to register with transceiver\n");
retval = -ENODEV;
- goto err_put_transceiver;
+ goto err_disable_clk;
}
/* otg will enable clock before use as host */
mv_ehci_disable(ehci_mv);
@@ -286,22 +288,10 @@ static int mv_ehci_probe(struct platform_device *pdev)
err_set_vbus:
if (pdata->set_vbus)
pdata->set_vbus(0);
-#ifdef CONFIG_USB_OTG_UTILS
-err_put_transceiver:
- if (!IS_ERR_OR_NULL(ehci_mv->otg))
- usb_put_phy(ehci_mv->otg);
-#endif
err_disable_clk:
mv_ehci_disable(ehci_mv);
-err_iounmap_capreg:
- iounmap(ehci_mv->cap_regs);
-err_iounmap_phyreg:
- iounmap(ehci_mv->phy_regs);
-err_put_clk:
- for (clk_i--; clk_i >= 0; clk_i--)
- clk_put(ehci_mv->clk[clk_i]);
+err_clear_drvdata:
platform_set_drvdata(pdev, NULL);
- kfree(ehci_mv);
err_put_hcd:
usb_put_hcd(hcd);
@@ -317,10 +307,8 @@ static int mv_ehci_remove(struct platform_device *pdev)
if (hcd->rh_registered)
usb_remove_hcd(hcd);
- if (!IS_ERR_OR_NULL(ehci_mv->otg)) {
+ if (!IS_ERR_OR_NULL(ehci_mv->otg))
otg_set_host(ehci_mv->otg->otg, NULL);
- usb_put_phy(ehci_mv->otg);
- }
if (ehci_mv->mode == MV_USB_MODE_HOST) {
if (ehci_mv->pdata->set_vbus)
@@ -329,15 +317,8 @@ static int mv_ehci_remove(struct platform_device *pdev)
mv_ehci_disable(ehci_mv);
}
- iounmap(ehci_mv->cap_regs);
- iounmap(ehci_mv->phy_regs);
-
- for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++)
- clk_put(ehci_mv->clk[clk_i]);
-
platform_set_drvdata(pdev, NULL);
- kfree(ehci_mv);
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c
index a6e2ea4ef8fd..4a08fc0b27c9 100644
--- a/drivers/usb/host/ehci-mxc.c
+++ b/drivers/usb/host/ehci-mxc.c
@@ -121,7 +121,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
if (!hcd)
return -ENOMEM;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto err_alloc;
@@ -131,34 +131,28 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
if (!res) {
dev_err(dev, "Found HC with no register addr. Check setup!\n");
ret = -ENODEV;
- goto err_get_resource;
+ goto err_alloc;
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- dev_dbg(dev, "controller already in use\n");
- ret = -EBUSY;
- goto err_request_mem;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hcd->regs) {
dev_err(dev, "error mapping memory\n");
ret = -EFAULT;
- goto err_ioremap;
+ goto err_alloc;
}
/* enable clocks */
- priv->usbclk = clk_get(dev, "ipg");
+ priv->usbclk = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(priv->usbclk)) {
ret = PTR_ERR(priv->usbclk);
- goto err_clk;
+ goto err_alloc;
}
clk_prepare_enable(priv->usbclk);
- priv->ahbclk = clk_get(dev, "ahb");
+ priv->ahbclk = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(priv->ahbclk)) {
ret = PTR_ERR(priv->ahbclk);
goto err_clk_ahb;
@@ -166,7 +160,7 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
clk_prepare_enable(priv->ahbclk);
/* "dr" device has its own clock on i.MX51 */
- priv->phyclk = clk_get(dev, "phy");
+ priv->phyclk = devm_clk_get(&pdev->dev, "phy");
if (IS_ERR(priv->phyclk))
priv->phyclk = NULL;
if (priv->phyclk)
@@ -245,23 +239,12 @@ err_add:
if (pdata && pdata->exit)
pdata->exit(pdev);
err_init:
- if (priv->phyclk) {
+ if (priv->phyclk)
clk_disable_unprepare(priv->phyclk);
- clk_put(priv->phyclk);
- }
clk_disable_unprepare(priv->ahbclk);
- clk_put(priv->ahbclk);
err_clk_ahb:
clk_disable_unprepare(priv->usbclk);
- clk_put(priv->usbclk);
-err_clk:
- iounmap(hcd->regs);
-err_ioremap:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-err_request_mem:
-err_get_resource:
- kfree(priv);
err_alloc:
usb_put_hcd(hcd);
return ret;
@@ -280,22 +263,14 @@ static int __exit ehci_mxc_drv_remove(struct platform_device *pdev)
usb_phy_shutdown(pdata->otg);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
platform_set_drvdata(pdev, NULL);
clk_disable_unprepare(priv->usbclk);
- clk_put(priv->usbclk);
clk_disable_unprepare(priv->ahbclk);
- clk_put(priv->ahbclk);
- if (priv->phyclk) {
+ if (priv->phyclk)
clk_disable_unprepare(priv->phyclk);
- clk_put(priv->phyclk);
- }
-
- kfree(priv);
return 0;
}
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index 4b1d896d5a22..764e0100b6f4 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -82,10 +82,14 @@ static int __devinit ehci_platform_probe(struct platform_device *dev)
{
struct usb_hcd *hcd;
struct resource *res_mem;
+ struct usb_ehci_pdata *pdata = dev->dev.platform_data;
int irq;
int err = -ENOMEM;
- BUG_ON(!dev->dev.platform_data);
+ if (!pdata) {
+ WARN_ON(1);
+ return -ENODEV;
+ }
if (usb_disabled())
return -ENODEV;
@@ -101,10 +105,18 @@ static int __devinit ehci_platform_probe(struct platform_device *dev)
return -ENXIO;
}
+ if (pdata->power_on) {
+ err = pdata->power_on(dev);
+ if (err < 0)
+ return err;
+ }
+
hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
dev_name(&dev->dev));
- if (!hcd)
- return -ENOMEM;
+ if (!hcd) {
+ err = -ENOMEM;
+ goto err_power;
+ }
hcd->rsrc_start = res_mem->start;
hcd->rsrc_len = resource_size(res_mem);
@@ -116,8 +128,10 @@ static int __devinit ehci_platform_probe(struct platform_device *dev)
}
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs)
+ if (!hcd->regs) {
+ err = -ENOMEM;
goto err_release_region;
+ }
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err)
goto err_iounmap;
@@ -132,12 +146,17 @@ err_release_region:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_put_hcd:
usb_put_hcd(hcd);
+err_power:
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
return err;
}
static int __devexit ehci_platform_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev->dev.platform_data;
usb_remove_hcd(hcd);
iounmap(hcd->regs);
@@ -145,6 +164,9 @@ static int __devexit ehci_platform_remove(struct platform_device *dev)
usb_put_hcd(hcd);
platform_set_drvdata(dev, NULL);
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
return 0;
}
@@ -153,14 +175,32 @@ static int __devexit ehci_platform_remove(struct platform_device *dev)
static int ehci_platform_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev->platform_data;
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
bool do_wakeup = device_may_wakeup(dev);
+ int ret;
+
+ ret = ehci_suspend(hcd, do_wakeup);
- return ehci_suspend(hcd, do_wakeup);
+ if (pdata->power_suspend)
+ pdata->power_suspend(pdev);
+
+ return ret;
}
static int ehci_platform_resume(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev->platform_data;
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+
+ if (pdata->power_on) {
+ int err = pdata->power_on(pdev);
+ if (err < 0)
+ return err;
+ }
ehci_resume(hcd, false);
return 0;
diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c
index bbbe89dfd886..fa937d05a02b 100644
--- a/drivers/usb/host/ehci-ppc-of.c
+++ b/drivers/usb/host/ehci-ppc-of.c
@@ -114,12 +114,6 @@ static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op)
hcd->rsrc_start = res.start;
hcd->rsrc_len = resource_size(&res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__);
- rv = -EBUSY;
- goto err_rmr;
- }
-
irq = irq_of_parse_and_map(dn, 0);
if (irq == NO_IRQ) {
printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__);
@@ -127,9 +121,9 @@ static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op)
goto err_irq;
}
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&op->dev, &res);
if (!hcd->regs) {
- printk(KERN_ERR "%s: ioremap failed\n", __FILE__);
+ pr_err("%s: devm_request_and_ioremap failed\n", __FILE__);
rv = -ENOMEM;
goto err_ioremap;
}
@@ -139,8 +133,10 @@ static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op)
if (np != NULL) {
/* claim we really affected by usb23 erratum */
if (!of_address_to_resource(np, 0, &res))
- ehci->ohci_hcctrl_reg = ioremap(res.start +
- OHCI_HCCTRL_OFFSET, OHCI_HCCTRL_LEN);
+ ehci->ohci_hcctrl_reg =
+ devm_ioremap(&op->dev,
+ res.start + OHCI_HCCTRL_OFFSET,
+ OHCI_HCCTRL_LEN);
else
pr_debug("%s: no ohci offset in fdt\n", __FILE__);
if (!ehci->ohci_hcctrl_reg) {
@@ -169,19 +165,13 @@ static int __devinit ehci_hcd_ppc_of_probe(struct platform_device *op)
rv = usb_add_hcd(hcd, irq, 0);
if (rv)
- goto err_ehci;
+ goto err_ioremap;
return 0;
-err_ehci:
- if (ehci->has_amcc_usb23)
- iounmap(ehci->ohci_hcctrl_reg);
- iounmap(hcd->regs);
err_ioremap:
irq_dispose_mapping(irq);
err_irq:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-err_rmr:
usb_put_hcd(hcd);
return rv;
@@ -202,9 +192,7 @@ static int ehci_hcd_ppc_of_remove(struct platform_device *op)
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
irq_dispose_mapping(hcd->irq);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
/* use request_mem_region to test if the ohci driver is loaded. if so
* ensure the ohci core is operational.
@@ -222,8 +210,6 @@ static int ehci_hcd_ppc_of_remove(struct platform_device *op)
pr_debug("%s: no ohci offset in fdt\n", __FILE__);
of_node_put(np);
}
-
- iounmap(ehci->ohci_hcctrl_reg);
}
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
index dfb14c7a61e2..85b74be202eb 100644
--- a/drivers/usb/host/ehci-s5p.c
+++ b/drivers/usb/host/ehci-s5p.c
@@ -128,7 +128,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
}
s5p_ehci->hcd = hcd;
- s5p_ehci->clk = clk_get(&pdev->dev, "usbhost");
+ s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
if (IS_ERR(s5p_ehci->clk)) {
dev_err(&pdev->dev, "Failed to get usbhost clock\n");
@@ -138,7 +138,7 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
err = clk_enable(s5p_ehci->clk);
if (err)
- goto fail_clken;
+ goto fail_clk;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
@@ -184,8 +184,6 @@ static int __devinit s5p_ehci_probe(struct platform_device *pdev)
fail_io:
clk_disable(s5p_ehci->clk);
-fail_clken:
- clk_put(s5p_ehci->clk);
fail_clk:
usb_put_hcd(hcd);
return err;
@@ -203,7 +201,6 @@ static int __devexit s5p_ehci_remove(struct platform_device *pdev)
pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
clk_disable(s5p_ehci->clk);
- clk_put(s5p_ehci->clk);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c
index 0c9e43cfaff5..efad02d947f2 100644
--- a/drivers/usb/host/ehci-sead3.c
+++ b/drivers/usb/host/ehci-sead3.c
@@ -112,17 +112,11 @@ static int ehci_hcd_sead3_drv_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- pr_debug("request_mem_region failed");
- ret = -EBUSY;
- goto err1;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hcd->regs) {
pr_debug("ioremap failed");
ret = -ENOMEM;
- goto err2;
+ goto err1;
}
/* Root hub has integrated TT. */
@@ -135,9 +129,6 @@ static int ehci_hcd_sead3_drv_probe(struct platform_device *pdev)
return ret;
}
- iounmap(hcd->regs);
-err2:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return ret;
@@ -148,8 +139,6 @@ static int ehci_hcd_sead3_drv_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c
index b3f1e3650da0..6081e1ed3ac9 100644
--- a/drivers/usb/host/ehci-sh.c
+++ b/drivers/usb/host/ehci-sh.c
@@ -125,33 +125,27 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description)) {
- dev_dbg(&pdev->dev, "controller already in use\n");
- ret = -EBUSY;
- goto fail_request_resource;
- }
-
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (hcd->regs == NULL) {
dev_dbg(&pdev->dev, "error mapping memory\n");
ret = -ENXIO;
- goto fail_ioremap;
+ goto fail_request_resource;
}
- priv = kmalloc(sizeof(struct ehci_sh_priv), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ehci_sh_priv),
+ GFP_KERNEL);
if (!priv) {
dev_dbg(&pdev->dev, "error allocating priv data\n");
ret = -ENOMEM;
- goto fail_alloc;
+ goto fail_request_resource;
}
/* These are optional, we don't care if they fail */
- priv->fclk = clk_get(&pdev->dev, "usb_fck");
+ priv->fclk = devm_clk_get(&pdev->dev, "usb_fck");
if (IS_ERR(priv->fclk))
priv->fclk = NULL;
- priv->iclk = clk_get(&pdev->dev, "usb_ick");
+ priv->iclk = devm_clk_get(&pdev->dev, "usb_ick");
if (IS_ERR(priv->iclk))
priv->iclk = NULL;
@@ -176,14 +170,6 @@ fail_add_hcd:
clk_disable(priv->iclk);
clk_disable(priv->fclk);
- clk_put(priv->iclk);
- clk_put(priv->fclk);
-
- kfree(priv);
-fail_alloc:
- iounmap(hcd->regs);
-fail_ioremap:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
fail_request_resource:
usb_put_hcd(hcd);
fail_create_hcd:
@@ -198,19 +184,12 @@ static int __exit ehci_hcd_sh_remove(struct platform_device *pdev)
struct usb_hcd *hcd = priv->hcd;
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
platform_set_drvdata(pdev, NULL);
clk_disable(priv->fclk);
clk_disable(priv->iclk);
- clk_put(priv->fclk);
- clk_put(priv->iclk);
-
- kfree(priv);
-
return 0;
}
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 26dedb30ad0b..6223d1757848 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -27,7 +27,7 @@
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
-#include <mach/usb_phy.h>
+#include <linux/usb/tegra_usb_phy.h>
#include <mach/iomap.h>
#define TEGRA_USB_DMA_ALIGN 32
@@ -49,7 +49,7 @@ static void tegra_ehci_power_up(struct usb_hcd *hcd)
clk_prepare_enable(tegra->emc_clk);
clk_prepare_enable(tegra->clk);
- tegra_usb_phy_power_on(tegra->phy);
+ usb_phy_set_suspend(&tegra->phy->u_phy, 0);
tegra->host_resumed = 1;
}
@@ -58,7 +58,7 @@ static void tegra_ehci_power_down(struct usb_hcd *hcd)
struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
tegra->host_resumed = 0;
- tegra_usb_phy_power_off(tegra->phy);
+ usb_phy_set_suspend(&tegra->phy->u_phy, 1);
clk_disable_unprepare(tegra->clk);
clk_disable_unprepare(tegra->emc_clk);
}
@@ -634,7 +634,8 @@ static int tegra_ehci_probe(struct platform_device *pdev)
setup_vbus_gpio(pdev, pdata);
- tegra = kzalloc(sizeof(struct tegra_ehci_hcd), GFP_KERNEL);
+ tegra = devm_kzalloc(&pdev->dev, sizeof(struct tegra_ehci_hcd),
+ GFP_KERNEL);
if (!tegra)
return -ENOMEM;
@@ -642,13 +643,12 @@ static int tegra_ehci_probe(struct platform_device *pdev)
dev_name(&pdev->dev));
if (!hcd) {
dev_err(&pdev->dev, "Unable to create HCD\n");
- err = -ENOMEM;
- goto fail_hcd;
+ return -ENOMEM;
}
platform_set_drvdata(pdev, tegra);
- tegra->clk = clk_get(&pdev->dev, NULL);
+ tegra->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(tegra->clk)) {
dev_err(&pdev->dev, "Can't get ehci clock\n");
err = PTR_ERR(tegra->clk);
@@ -657,9 +657,9 @@ static int tegra_ehci_probe(struct platform_device *pdev)
err = clk_prepare_enable(tegra->clk);
if (err)
- goto fail_clken;
+ goto fail_clk;
- tegra->emc_clk = clk_get(&pdev->dev, "emc");
+ tegra->emc_clk = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(tegra->emc_clk)) {
dev_err(&pdev->dev, "Can't get emc clock\n");
err = PTR_ERR(tegra->emc_clk);
@@ -677,7 +677,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- hcd->regs = ioremap(res->start, resource_size(res));
+ hcd->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!hcd->regs) {
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
err = -ENOMEM;
@@ -702,7 +702,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
default:
err = -ENODEV;
dev_err(&pdev->dev, "unknown usb instance\n");
- goto fail_phy;
+ goto fail_io;
}
}
@@ -712,10 +712,12 @@ static int tegra_ehci_probe(struct platform_device *pdev)
if (IS_ERR(tegra->phy)) {
dev_err(&pdev->dev, "Failed to open USB phy\n");
err = -ENXIO;
- goto fail_phy;
+ goto fail_io;
}
- err = tegra_usb_phy_power_on(tegra->phy);
+ usb_phy_init(&tegra->phy->u_phy);
+
+ err = usb_phy_set_suspend(&tegra->phy->u_phy, 0);
if (err) {
dev_err(&pdev->dev, "Failed to power on the phy\n");
goto fail;
@@ -733,7 +735,8 @@ static int tegra_ehci_probe(struct platform_device *pdev)
#ifdef CONFIG_USB_OTG_UTILS
if (pdata->operating_mode == TEGRA_USB_OTG) {
- tegra->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+ tegra->transceiver =
+ devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (!IS_ERR_OR_NULL(tegra->transceiver))
otg_set_host(tegra->transceiver->otg, &hcd->self);
}
@@ -757,25 +760,16 @@ static int tegra_ehci_probe(struct platform_device *pdev)
fail:
#ifdef CONFIG_USB_OTG_UTILS
- if (!IS_ERR_OR_NULL(tegra->transceiver)) {
+ if (!IS_ERR_OR_NULL(tegra->transceiver))
otg_set_host(tegra->transceiver->otg, NULL);
- usb_put_phy(tegra->transceiver);
- }
#endif
- tegra_usb_phy_close(tegra->phy);
-fail_phy:
- iounmap(hcd->regs);
+ usb_phy_shutdown(&tegra->phy->u_phy);
fail_io:
clk_disable_unprepare(tegra->emc_clk);
- clk_put(tegra->emc_clk);
fail_emc_clk:
clk_disable_unprepare(tegra->clk);
-fail_clken:
- clk_put(tegra->clk);
fail_clk:
usb_put_hcd(hcd);
-fail_hcd:
- kfree(tegra);
return err;
}
@@ -792,26 +786,19 @@ static int tegra_ehci_remove(struct platform_device *pdev)
pm_runtime_put_noidle(&pdev->dev);
#ifdef CONFIG_USB_OTG_UTILS
- if (!IS_ERR_OR_NULL(tegra->transceiver)) {
+ if (!IS_ERR_OR_NULL(tegra->transceiver))
otg_set_host(tegra->transceiver->otg, NULL);
- usb_put_phy(tegra->transceiver);
- }
#endif
usb_remove_hcd(hcd);
-
- tegra_usb_phy_close(tegra->phy);
- iounmap(hcd->regs);
-
usb_put_hcd(hcd);
+ usb_phy_shutdown(&tegra->phy->u_phy);
+
clk_disable_unprepare(tegra->clk);
- clk_put(tegra->clk);
clk_disable_unprepare(tegra->emc_clk);
- clk_put(tegra->emc_clk);
- kfree(tegra);
return 0;
}
diff --git a/drivers/usb/host/ehci-timer.c b/drivers/usb/host/ehci-timer.c
index eb896a2c8f2e..20dbdcbe9b0f 100644
--- a/drivers/usb/host/ehci-timer.c
+++ b/drivers/usb/host/ehci-timer.c
@@ -118,7 +118,8 @@ static void ehci_poll_ASS(struct ehci_hcd *ehci)
ehci_enable_event(ehci, EHCI_HRTIMER_POLL_ASS, true);
return;
}
- ehci_warn(ehci, "Waited too long for the async schedule status, giving up\n");
+ ehci_dbg(ehci, "Waited too long for the async schedule status (%x/%x), giving up\n",
+ want, actual);
}
ehci->ASS_poll_count = 0;
@@ -163,7 +164,8 @@ static void ehci_poll_PSS(struct ehci_hcd *ehci)
ehci_enable_event(ehci, EHCI_HRTIMER_POLL_PSS, true);
return;
}
- ehci_warn(ehci, "Waited too long for the periodic schedule status, giving up\n");
+ ehci_dbg(ehci, "Waited too long for the periodic schedule status (%x/%x), giving up\n",
+ want, actual);
}
ehci->PSS_poll_count = 0;
diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c
index 4d147c4e33f5..96722bfebc84 100644
--- a/drivers/usb/host/ehci-vt8500.c
+++ b/drivers/usb/host/ehci-vt8500.c
@@ -16,6 +16,7 @@
*
*/
+#include <linux/of.h>
#include <linux/platform_device.h>
static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
@@ -106,17 +107,11 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev)
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- pr_debug("request_mem_region failed");
- ret = -EBUSY;
- goto err1;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hcd->regs) {
pr_debug("ioremap failed");
ret = -ENOMEM;
- goto err2;
+ goto err1;
}
ehci = hcd_to_ehci(hcd);
@@ -129,9 +124,6 @@ static int vt8500_ehci_drv_probe(struct platform_device *pdev)
return ret;
}
- iounmap(hcd->regs);
-err2:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return ret;
@@ -142,14 +134,18 @@ static int vt8500_ehci_drv_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
platform_set_drvdata(pdev, NULL);
return 0;
}
+static const struct of_device_id vt8500_ehci_ids[] = {
+ { .compatible = "via,vt8500-ehci", },
+ { .compatible = "wm,prizm-ehci", },
+ {}
+};
+
static struct platform_driver vt8500_ehci_driver = {
.probe = vt8500_ehci_drv_probe,
.remove = vt8500_ehci_drv_remove,
@@ -157,7 +153,9 @@ static struct platform_driver vt8500_ehci_driver = {
.driver = {
.name = "vt8500-ehci",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(vt8500_ehci_ids),
}
};
MODULE_ALIAS("platform:vt8500-ehci");
+MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c
index 39f24fa37ebe..6a3f921a5d76 100644
--- a/drivers/usb/host/ehci-xilinx-of.c
+++ b/drivers/usb/host/ehci-xilinx-of.c
@@ -152,12 +152,6 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op)
hcd->rsrc_start = res.start;
hcd->rsrc_len = resource_size(&res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__);
- rv = -EBUSY;
- goto err_rmr;
- }
-
irq = irq_of_parse_and_map(dn, 0);
if (!irq) {
printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__);
@@ -165,11 +159,11 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op)
goto err_irq;
}
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&op->dev, &res);
if (!hcd->regs) {
- printk(KERN_ERR "%s: ioremap failed\n", __FILE__);
+ pr_err("%s: devm_request_and_ioremap failed\n", __FILE__);
rv = -ENOMEM;
- goto err_ioremap;
+ goto err_irq;
}
ehci = hcd_to_ehci(hcd);
@@ -200,12 +194,7 @@ static int __devinit ehci_hcd_xilinx_of_probe(struct platform_device *op)
if (rv == 0)
return 0;
- iounmap(hcd->regs);
-
-err_ioremap:
err_irq:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-err_rmr:
usb_put_hcd(hcd);
return rv;
@@ -227,9 +216,6 @@ static int ehci_hcd_xilinx_of_remove(struct platform_device *op)
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c
index 2dc8a40e39d7..8f18538e0ff7 100644
--- a/drivers/usb/host/fhci-sched.c
+++ b/drivers/usb/host/fhci-sched.c
@@ -261,8 +261,7 @@ static void move_head_to_tail(struct list_head *list)
struct list_head *node = list->next;
if (!list_empty(list)) {
- list_del(node);
- list_add_tail(node, list);
+ list_move_tail(node, list);
}
}
diff --git a/drivers/usb/host/fsl-mph-dr-of.c b/drivers/usb/host/fsl-mph-dr-of.c
index 22ff6b3a676f..1e771292383f 100644
--- a/drivers/usb/host/fsl-mph-dr-of.c
+++ b/drivers/usb/host/fsl-mph-dr-of.c
@@ -133,6 +133,8 @@ static int usb_get_ver_info(struct device_node *np)
ver = FSL_USB_VER_1_6;
else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.2"))
ver = FSL_USB_VER_2_2;
+ else if (of_device_is_compatible(np, "fsl-usb2-dr-v2.4"))
+ ver = FSL_USB_VER_2_4;
else /* for previous controller versions */
ver = FSL_USB_VER_OLD;
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index aaa8d2bce217..0bf72f943b00 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -467,7 +467,8 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
/* From the GPIO notifying the over-current situation, find
* out the corresponding port */
at91_for_each_port(port) {
- if (gpio_to_irq(pdata->overcurrent_pin[port]) == irq) {
+ if (gpio_is_valid(pdata->overcurrent_pin[port]) &&
+ gpio_to_irq(pdata->overcurrent_pin[port]) == irq) {
gpio = pdata->overcurrent_pin[port];
break;
}
diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c
index 119966603d8d..e068f034cb9b 100644
--- a/drivers/usb/host/ohci-nxp.c
+++ b/drivers/usb/host/ohci-nxp.c
@@ -278,7 +278,7 @@ static int __devinit usb_hcd_nxp_probe(struct platform_device *pdev)
usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg");
if (IS_ERR(usb_otg_clk)) {
dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n");
- ret = PTR_ERR(usb_dev_clk);
+ ret = PTR_ERR(usb_otg_clk);
goto out6;
}
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index 670c7059c9ae..e24ec9f79164 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -83,10 +83,14 @@ static int __devinit ohci_platform_probe(struct platform_device *dev)
{
struct usb_hcd *hcd;
struct resource *res_mem;
+ struct usb_ohci_pdata *pdata = dev->dev.platform_data;
int irq;
int err = -ENOMEM;
- BUG_ON(!dev->dev.platform_data);
+ if (!pdata) {
+ WARN_ON(1);
+ return -ENODEV;
+ }
if (usb_disabled())
return -ENODEV;
@@ -103,10 +107,18 @@ static int __devinit ohci_platform_probe(struct platform_device *dev)
return -ENXIO;
}
+ if (pdata->power_on) {
+ err = pdata->power_on(dev);
+ if (err < 0)
+ return err;
+ }
+
hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
dev_name(&dev->dev));
- if (!hcd)
- return -ENOMEM;
+ if (!hcd) {
+ err = -ENOMEM;
+ goto err_power;
+ }
hcd->rsrc_start = res_mem->start;
hcd->rsrc_len = resource_size(res_mem);
@@ -118,8 +130,10 @@ static int __devinit ohci_platform_probe(struct platform_device *dev)
}
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs)
+ if (!hcd->regs) {
+ err = -ENOMEM;
goto err_release_region;
+ }
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err)
goto err_iounmap;
@@ -134,12 +148,17 @@ err_release_region:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_put_hcd:
usb_put_hcd(hcd);
+err_power:
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
return err;
}
static int __devexit ohci_platform_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct usb_ohci_pdata *pdata = dev->dev.platform_data;
usb_remove_hcd(hcd);
iounmap(hcd->regs);
@@ -147,6 +166,9 @@ static int __devexit ohci_platform_remove(struct platform_device *dev)
usb_put_hcd(hcd);
platform_set_drvdata(dev, NULL);
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
return 0;
}
@@ -154,12 +176,28 @@ static int __devexit ohci_platform_remove(struct platform_device *dev)
static int ohci_platform_suspend(struct device *dev)
{
+ struct usb_ohci_pdata *pdata = dev->platform_data;
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+
+ if (pdata->power_suspend)
+ pdata->power_suspend(pdev);
+
return 0;
}
static int ohci_platform_resume(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ohci_pdata *pdata = dev->platform_data;
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+
+ if (pdata->power_on) {
+ int err = pdata->power_on(pdev);
+ if (err < 0)
+ return err;
+ }
ohci_finish_controller_resume(hcd);
return 0;
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index 955c410d59b6..2bf11440b010 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -23,6 +23,8 @@
#include <linux/signal.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
#include <mach/hardware.h>
#include <linux/platform_data/usb-ohci-pxa27x.h>
#include <linux/platform_data/usb-pxa3xx-ulpi.h>
@@ -272,6 +274,67 @@ static void pxa27x_stop_hc(struct pxa27x_ohci *ohci, struct device *dev)
clk_disable_unprepare(ohci->clk);
}
+#ifdef CONFIG_OF
+static const struct of_device_id pxa_ohci_dt_ids[] = {
+ { .compatible = "marvell,pxa-ohci" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, pxa_ohci_dt_ids);
+
+static u64 pxa_ohci_dma_mask = DMA_BIT_MASK(32);
+
+static int __devinit ohci_pxa_of_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pxaohci_platform_data *pdata;
+ u32 tmp;
+
+ if (!np)
+ return 0;
+
+ /* Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &pxa_ohci_dma_mask;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (of_get_property(np, "marvell,enable-port1", NULL))
+ pdata->flags |= ENABLE_PORT1;
+ if (of_get_property(np, "marvell,enable-port2", NULL))
+ pdata->flags |= ENABLE_PORT2;
+ if (of_get_property(np, "marvell,enable-port3", NULL))
+ pdata->flags |= ENABLE_PORT3;
+ if (of_get_property(np, "marvell,port-sense-low", NULL))
+ pdata->flags |= POWER_SENSE_LOW;
+ if (of_get_property(np, "marvell,power-control-low", NULL))
+ pdata->flags |= POWER_CONTROL_LOW;
+ if (of_get_property(np, "marvell,no-oc-protection", NULL))
+ pdata->flags |= NO_OC_PROTECTION;
+ if (of_get_property(np, "marvell,oc-mode-perport", NULL))
+ pdata->flags |= OC_MODE_PERPORT;
+ if (!of_property_read_u32(np, "marvell,power-on-delay", &tmp))
+ pdata->power_on_delay = tmp;
+ if (!of_property_read_u32(np, "marvell,port-mode", &tmp))
+ pdata->port_mode = tmp;
+ if (!of_property_read_u32(np, "marvell,power-budget", &tmp))
+ pdata->power_budget = tmp;
+
+ pdev->dev.platform_data = pdata;
+
+ return 0;
+}
+#else
+static int __devinit ohci_pxa_of_init(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
/*-------------------------------------------------------------------------*/
@@ -297,6 +360,10 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
struct resource *r;
struct clk *usb_clk;
+ retval = ohci_pxa_of_init(pdev);
+ if (retval)
+ return retval;
+
inf = pdev->dev.platform_data;
if (!inf)
@@ -544,6 +611,7 @@ static struct platform_driver ohci_hcd_pxa27x_driver = {
.driver = {
.name = "pxa27x-ohci",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pxa_ohci_dt_ids),
#ifdef CONFIG_PM
.pm = &ohci_hcd_pxa27x_pm_ops,
#endif
diff --git a/drivers/usb/host/ohci-xls.c b/drivers/usb/host/ohci-xls.c
index 41e378f17c66..84201cd1a472 100644
--- a/drivers/usb/host/ohci-xls.c
+++ b/drivers/usb/host/ohci-xls.c
@@ -56,7 +56,7 @@ static int ohci_xls_probe_internal(const struct hc_driver *driver,
goto err3;
}
- retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval != 0)
goto err4;
return retval;
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 4c634eb56358..fcc09e5ec0ad 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -2029,15 +2029,14 @@ static int r8a66597_get_frame(struct usb_hcd *hcd)
static void collect_usb_address_map(struct usb_device *udev, unsigned long *map)
{
int chix;
+ struct usb_device *childdev;
if (udev->state == USB_STATE_CONFIGURED &&
udev->parent && udev->parent->devnum > 1 &&
udev->parent->descriptor.bDeviceClass == USB_CLASS_HUB)
map[udev->devnum/32] |= (1 << (udev->devnum % 32));
- for (chix = 0; chix < udev->maxchild; chix++) {
- struct usb_device *childdev = udev->children[chix];
-
+ usb_hub_for_each_child(udev, chix, childdev) {
if (childdev)
collect_usb_address_map(childdev, map);
}
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 91ce1c02e617..619b05f42d4f 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -156,7 +156,7 @@ static void setup_packet(
writeb(SL_SETUP /* | ep->epnum */, data_reg);
writeb(usb_pipedevice(urb->pipe), data_reg);
- /* always OUT/data0 */ ;
+ /* always OUT/data0 */
sl811_write(sl811, bank + SL11H_HOSTCTLREG,
control | SL11H_HCTLMASK_OUT);
ep->length = 0;
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index e4db350602b8..4b9e9aba2665 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -846,6 +846,11 @@ static const char hcd_name[] = "uhci_hcd";
#define PLATFORM_DRIVER uhci_grlib_driver
#endif
+#ifdef CONFIG_USB_UHCI_PLATFORM
+#include "uhci-platform.c"
+#define PLATFORM_DRIVER uhci_platform_driver
+#endif
+
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER)
#error "missing bus glue for uhci-hcd"
#endif
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
new file mode 100644
index 000000000000..e4780491df4a
--- /dev/null
+++ b/drivers/usb/host/uhci-platform.c
@@ -0,0 +1,157 @@
+/*
+ * Generic UHCI HCD (Host Controller Driver) for Platform Devices
+ *
+ * Copyright (c) 2011 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This file is based on uhci-grlib.c
+ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
+ */
+
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+static int uhci_platform_init(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ uhci->rh_numports = uhci_count_ports(hcd);
+
+ /* Set up pointers to to generic functions */
+ uhci->reset_hc = uhci_generic_reset_hc;
+ uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc;
+
+ /* No special actions need to be taken for the functions below */
+ uhci->configure_hc = NULL;
+ uhci->resume_detect_interrupts_are_broken = NULL;
+ uhci->global_suspend_mode_is_broken = NULL;
+
+ /* Reset if the controller isn't already safely quiescent. */
+ check_and_reset_hc(uhci);
+ return 0;
+}
+
+static const struct hc_driver uhci_platform_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Generic UHCI Host Controller",
+ .hcd_priv_size = sizeof(struct uhci_hcd),
+
+ /* Generic hardware linkage */
+ .irq = uhci_irq,
+ .flags = HCD_MEMORY | HCD_USB11,
+
+ /* Basic lifecycle operations */
+ .reset = uhci_platform_init,
+ .start = uhci_start,
+#ifdef CONFIG_PM
+ .pci_suspend = NULL,
+ .pci_resume = NULL,
+ .bus_suspend = uhci_rh_suspend,
+ .bus_resume = uhci_rh_resume,
+#endif
+ .stop = uhci_stop,
+
+ .urb_enqueue = uhci_urb_enqueue,
+ .urb_dequeue = uhci_urb_dequeue,
+
+ .endpoint_disable = uhci_hcd_endpoint_disable,
+ .get_frame_number = uhci_hcd_get_frame_number,
+
+ .hub_status_data = uhci_hub_status_data,
+ .hub_control = uhci_hub_control,
+};
+
+
+static int __devinit uhci_hcd_platform_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct uhci_hcd *uhci;
+ struct resource *res;
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ hcd = usb_create_hcd(&uhci_platform_hc_driver, &pdev->dev,
+ pdev->name);
+ if (!hcd)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ pr_err("%s: request_mem_region failed\n", __func__);
+ ret = -EBUSY;
+ goto err_rmr;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ pr_err("%s: ioremap failed\n", __func__);
+ ret = -ENOMEM;
+ goto err_irq;
+ }
+ uhci = hcd_to_uhci(hcd);
+
+ uhci->regs = hcd->regs;
+
+ ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED |
+ IRQF_SHARED);
+ if (ret)
+ goto err_uhci;
+
+ return 0;
+
+err_uhci:
+ iounmap(hcd->regs);
+err_irq:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err_rmr:
+ usb_put_hcd(hcd);
+
+ return ret;
+}
+
+static int uhci_hcd_platform_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+/* Make sure the controller is quiescent and that we're not using it
+ * any more. This is mainly for the benefit of programs which, like kexec,
+ * expect the hardware to be idle: not doing DMA or generating IRQs.
+ *
+ * This routine may be called in a damaged or failing kernel. Hence we
+ * do not acquire the spinlock before shutting down the controller.
+ */
+static void uhci_hcd_platform_shutdown(struct platform_device *op)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
+
+ uhci_hc_died(hcd_to_uhci(hcd));
+}
+
+static const struct of_device_id platform_uhci_ids[] = {
+ { .compatible = "platform-uhci", },
+ {}
+};
+
+static struct platform_driver uhci_platform_driver = {
+ .probe = uhci_hcd_platform_probe,
+ .remove = uhci_hcd_platform_remove,
+ .shutdown = uhci_hcd_platform_shutdown,
+ .driver = {
+ .name = "platform-uhci",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(platform_uhci_ids),
+ },
+};
diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c
index 1e141f755b26..c3a647816af0 100644
--- a/drivers/usb/host/whci/hcd.c
+++ b/drivers/usb/host/whci/hcd.c
@@ -238,16 +238,16 @@ static struct hc_driver whc_hc_driver = {
static int whc_probe(struct umc_dev *umc)
{
- int ret = -ENOMEM;
+ int ret;
struct usb_hcd *usb_hcd;
- struct wusbhc *wusbhc = NULL;
- struct whc *whc = NULL;
+ struct wusbhc *wusbhc;
+ struct whc *whc;
struct device *dev = &umc->dev;
usb_hcd = usb_create_hcd(&whc_hc_driver, dev, "whci");
if (usb_hcd == NULL) {
dev_err(dev, "unable to create hcd\n");
- goto error;
+ return -ENOMEM;
}
usb_hcd->wireless = 1;
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c
index 76083ae92138..dc31c425ce01 100644
--- a/drivers/usb/host/whci/qset.c
+++ b/drivers/usb/host/whci/qset.c
@@ -436,7 +436,7 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u
int i;
int ntds = 0;
struct whc_std *std = NULL;
- struct whc_page_list_entry *entry;
+ struct whc_page_list_entry *new_pl_virt;
dma_addr_t prev_end = 0;
size_t pl_len;
int p = 0;
@@ -508,12 +508,15 @@ static int qset_add_urb_sg(struct whc *whc, struct whc_qset *qset, struct urb *u
pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
- std->pl_virt = krealloc(std->pl_virt, pl_len, mem_flags);
- if (std->pl_virt == NULL) {
+ new_pl_virt = krealloc(std->pl_virt, pl_len, mem_flags);
+ if (new_pl_virt == NULL) {
+ kfree(std->pl_virt);
+ std->pl_virt = NULL;
return -ENOMEM;
}
+ std->pl_virt = new_pl_virt;
- for (;p < std->num_pointers; p++, entry++) {
+ for (;p < std->num_pointers; p++) {
std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr);
dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1);
}
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index d5eb357aa5c4..aa90ad4d4fd5 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -29,7 +29,7 @@
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
PORT_RC | PORT_PLC | PORT_PE)
-/* usb 1.1 root hub device descriptor */
+/* USB 3.0 BOS descriptor and a capability descriptor, combined */
static u8 usb_bos_descriptor [] = {
USB_DT_BOS_SIZE, /* __u8 bLength, 5 bytes */
USB_DT_BOS, /* __u8 bDescriptorType */
@@ -422,7 +422,7 @@ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
xhci_writel(xhci, temp, port_array[port_id]);
}
-void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
+static void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
__le32 __iomem **port_array, int port_id, u16 wake_mask)
{
u32 temp;
@@ -808,6 +808,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
temp = xhci_readl(xhci, port_array[wIndex]);
xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp);
+
+ temp = usb_acpi_power_manageable(hcd->self.root_hub,
+ wIndex);
+ if (temp)
+ usb_acpi_set_power_state(hcd->self.root_hub,
+ wIndex, true);
break;
case USB_PORT_FEAT_RESET:
temp = (temp | PORT_RESET);
@@ -907,6 +913,16 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
xhci_disable_port(hcd, xhci, wIndex,
port_array[wIndex], temp);
break;
+ case USB_PORT_FEAT_POWER:
+ xhci_writel(xhci, temp & ~PORT_POWER,
+ port_array[wIndex]);
+
+ temp = usb_acpi_power_manageable(hcd->self.root_hub,
+ wIndex);
+ if (temp)
+ usb_acpi_set_power_state(hcd->self.root_hub,
+ wIndex, false);
+ break;
default:
goto error;
}
diff --git a/drivers/usb/host/xhci-mem.c b/drivers/usb/host/xhci-mem.c
index 77689bd64cac..487bc083dead 100644
--- a/drivers/usb/host/xhci-mem.c
+++ b/drivers/usb/host/xhci-mem.c
@@ -1772,6 +1772,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
{
struct pci_dev *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
struct dev_info *dev_info, *next;
+ struct xhci_cd *cur_cd, *next_cd;
unsigned long flags;
int size;
int i, j, num_ports;
@@ -1795,6 +1796,11 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
xhci_ring_free(xhci, xhci->cmd_ring);
xhci->cmd_ring = NULL;
xhci_dbg(xhci, "Freed command ring\n");
+ list_for_each_entry_safe(cur_cd, next_cd,
+ &xhci->cancel_cmd_list, cancel_cmd_list) {
+ list_del(&cur_cd->cancel_cmd_list);
+ kfree(cur_cd);
+ }
for (i = 1; i < MAX_HC_SLOTS; ++i)
xhci_free_virt_device(xhci, i);
@@ -2340,6 +2346,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
xhci->cmd_ring = xhci_ring_alloc(xhci, 1, 1, TYPE_COMMAND, flags);
if (!xhci->cmd_ring)
goto fail;
+ INIT_LIST_HEAD(&xhci->cancel_cmd_list);
xhci_dbg(xhci, "Allocated command ring at %p\n", xhci->cmd_ring);
xhci_dbg(xhci, "First segment DMA is 0x%llx\n",
(unsigned long long)xhci->cmd_ring->first_seg->dma);
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 9bfd4ca1153c..8345d7c23061 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -103,6 +103,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
* PPT chipsets.
*/
xhci->quirks |= XHCI_SPURIOUS_REBOOT;
+ xhci->quirks |= XHCI_AVOID_BEI;
}
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 643c2f3f3e73..c6ebb176dc4f 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -280,12 +280,123 @@ static inline int room_on_ring(struct xhci_hcd *xhci, struct xhci_ring *ring,
/* Ring the host controller doorbell after placing a command on the ring */
void xhci_ring_cmd_db(struct xhci_hcd *xhci)
{
+ if (!(xhci->cmd_ring_state & CMD_RING_STATE_RUNNING))
+ return;
+
xhci_dbg(xhci, "// Ding dong!\n");
xhci_writel(xhci, DB_VALUE_HOST, &xhci->dba->doorbell[0]);
/* Flush PCI posted writes */
xhci_readl(xhci, &xhci->dba->doorbell[0]);
}
+static int xhci_abort_cmd_ring(struct xhci_hcd *xhci)
+{
+ u64 temp_64;
+ int ret;
+
+ xhci_dbg(xhci, "Abort command ring\n");
+
+ if (!(xhci->cmd_ring_state & CMD_RING_STATE_RUNNING)) {
+ xhci_dbg(xhci, "The command ring isn't running, "
+ "Have the command ring been stopped?\n");
+ return 0;
+ }
+
+ temp_64 = xhci_read_64(xhci, &xhci->op_regs->cmd_ring);
+ if (!(temp_64 & CMD_RING_RUNNING)) {
+ xhci_dbg(xhci, "Command ring had been stopped\n");
+ return 0;
+ }
+ xhci->cmd_ring_state = CMD_RING_STATE_ABORTED;
+ xhci_write_64(xhci, temp_64 | CMD_RING_ABORT,
+ &xhci->op_regs->cmd_ring);
+
+ /* Section 4.6.1.2 of xHCI 1.0 spec says software should
+ * time the completion od all xHCI commands, including
+ * the Command Abort operation. If software doesn't see
+ * CRR negated in a timely manner (e.g. longer than 5
+ * seconds), then it should assume that the there are
+ * larger problems with the xHC and assert HCRST.
+ */
+ ret = handshake(xhci, &xhci->op_regs->cmd_ring,
+ CMD_RING_RUNNING, 0, 5 * 1000 * 1000);
+ if (ret < 0) {
+ xhci_err(xhci, "Stopped the command ring failed, "
+ "maybe the host is dead\n");
+ xhci->xhc_state |= XHCI_STATE_DYING;
+ xhci_quiesce(xhci);
+ xhci_halt(xhci);
+ return -ESHUTDOWN;
+ }
+
+ return 0;
+}
+
+static int xhci_queue_cd(struct xhci_hcd *xhci,
+ struct xhci_command *command,
+ union xhci_trb *cmd_trb)
+{
+ struct xhci_cd *cd;
+ cd = kzalloc(sizeof(struct xhci_cd), GFP_ATOMIC);
+ if (!cd)
+ return -ENOMEM;
+ INIT_LIST_HEAD(&cd->cancel_cmd_list);
+
+ cd->command = command;
+ cd->cmd_trb = cmd_trb;
+ list_add_tail(&cd->cancel_cmd_list, &xhci->cancel_cmd_list);
+
+ return 0;
+}
+
+/*
+ * Cancel the command which has issue.
+ *
+ * Some commands may hang due to waiting for acknowledgement from
+ * usb device. It is outside of the xHC's ability to control and
+ * will cause the command ring is blocked. When it occurs software
+ * should intervene to recover the command ring.
+ * See Section 4.6.1.1 and 4.6.1.2
+ */
+int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
+ union xhci_trb *cmd_trb)
+{
+ int retval = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&xhci->lock, flags);
+
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_warn(xhci, "Abort the command ring,"
+ " but the xHCI is dead.\n");
+ retval = -ESHUTDOWN;
+ goto fail;
+ }
+
+ /* queue the cmd desriptor to cancel_cmd_list */
+ retval = xhci_queue_cd(xhci, command, cmd_trb);
+ if (retval) {
+ xhci_warn(xhci, "Queuing command descriptor failed.\n");
+ goto fail;
+ }
+
+ /* abort command ring */
+ retval = xhci_abort_cmd_ring(xhci);
+ if (retval) {
+ xhci_err(xhci, "Abort command ring failed\n");
+ if (unlikely(retval == -ESHUTDOWN)) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ usb_hc_died(xhci_to_hcd(xhci)->primary_hcd);
+ xhci_dbg(xhci, "xHCI host controller is dead.\n");
+ return retval;
+ }
+ }
+
+fail:
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return retval;
+}
+
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci,
unsigned int slot_id,
unsigned int ep_index,
@@ -1059,6 +1170,20 @@ static void handle_reset_ep_completion(struct xhci_hcd *xhci,
}
}
+/* Complete the command and detele it from the devcie's command queue.
+ */
+static void xhci_complete_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
+ struct xhci_command *command, u32 status)
+{
+ command->status = status;
+ list_del(&command->cmd_list);
+ if (command->completion)
+ complete(command->completion);
+ else
+ xhci_free_command(xhci, command);
+}
+
+
/* Check to see if a command in the device's command queue matches this one.
* Signal the completion or free the command, and return 1. Return 0 if the
* completed command isn't at the head of the command list.
@@ -1077,15 +1202,144 @@ static int handle_cmd_in_cmd_wait_list(struct xhci_hcd *xhci,
if (xhci->cmd_ring->dequeue != command->command_trb)
return 0;
- command->status = GET_COMP_CODE(le32_to_cpu(event->status));
- list_del(&command->cmd_list);
- if (command->completion)
- complete(command->completion);
- else
- xhci_free_command(xhci, command);
+ xhci_complete_cmd_in_cmd_wait_list(xhci, command,
+ GET_COMP_CODE(le32_to_cpu(event->status)));
return 1;
}
+/*
+ * Finding the command trb need to be cancelled and modifying it to
+ * NO OP command. And if the command is in device's command wait
+ * list, finishing and freeing it.
+ *
+ * If we can't find the command trb, we think it had already been
+ * executed.
+ */
+static void xhci_cmd_to_noop(struct xhci_hcd *xhci, struct xhci_cd *cur_cd)
+{
+ struct xhci_segment *cur_seg;
+ union xhci_trb *cmd_trb;
+ u32 cycle_state;
+
+ if (xhci->cmd_ring->dequeue == xhci->cmd_ring->enqueue)
+ return;
+
+ /* find the current segment of command ring */
+ cur_seg = find_trb_seg(xhci->cmd_ring->first_seg,
+ xhci->cmd_ring->dequeue, &cycle_state);
+
+ /* find the command trb matched by cd from command ring */
+ for (cmd_trb = xhci->cmd_ring->dequeue;
+ cmd_trb != xhci->cmd_ring->enqueue;
+ next_trb(xhci, xhci->cmd_ring, &cur_seg, &cmd_trb)) {
+ /* If the trb is link trb, continue */
+ if (TRB_TYPE_LINK_LE32(cmd_trb->generic.field[3]))
+ continue;
+
+ if (cur_cd->cmd_trb == cmd_trb) {
+
+ /* If the command in device's command list, we should
+ * finish it and free the command structure.
+ */
+ if (cur_cd->command)
+ xhci_complete_cmd_in_cmd_wait_list(xhci,
+ cur_cd->command, COMP_CMD_STOP);
+
+ /* get cycle state from the origin command trb */
+ cycle_state = le32_to_cpu(cmd_trb->generic.field[3])
+ & TRB_CYCLE;
+
+ /* modify the command trb to NO OP command */
+ cmd_trb->generic.field[0] = 0;
+ cmd_trb->generic.field[1] = 0;
+ cmd_trb->generic.field[2] = 0;
+ cmd_trb->generic.field[3] = cpu_to_le32(
+ TRB_TYPE(TRB_CMD_NOOP) | cycle_state);
+ break;
+ }
+ }
+}
+
+static void xhci_cancel_cmd_in_cd_list(struct xhci_hcd *xhci)
+{
+ struct xhci_cd *cur_cd, *next_cd;
+
+ if (list_empty(&xhci->cancel_cmd_list))
+ return;
+
+ list_for_each_entry_safe(cur_cd, next_cd,
+ &xhci->cancel_cmd_list, cancel_cmd_list) {
+ xhci_cmd_to_noop(xhci, cur_cd);
+ list_del(&cur_cd->cancel_cmd_list);
+ kfree(cur_cd);
+ }
+}
+
+/*
+ * traversing the cancel_cmd_list. If the command descriptor according
+ * to cmd_trb is found, the function free it and return 1, otherwise
+ * return 0.
+ */
+static int xhci_search_cmd_trb_in_cd_list(struct xhci_hcd *xhci,
+ union xhci_trb *cmd_trb)
+{
+ struct xhci_cd *cur_cd, *next_cd;
+
+ if (list_empty(&xhci->cancel_cmd_list))
+ return 0;
+
+ list_for_each_entry_safe(cur_cd, next_cd,
+ &xhci->cancel_cmd_list, cancel_cmd_list) {
+ if (cur_cd->cmd_trb == cmd_trb) {
+ if (cur_cd->command)
+ xhci_complete_cmd_in_cmd_wait_list(xhci,
+ cur_cd->command, COMP_CMD_STOP);
+ list_del(&cur_cd->cancel_cmd_list);
+ kfree(cur_cd);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * If the cmd_trb_comp_code is COMP_CMD_ABORT, we just check whether the
+ * trb pointed by the command ring dequeue pointer is the trb we want to
+ * cancel or not. And if the cmd_trb_comp_code is COMP_CMD_STOP, we will
+ * traverse the cancel_cmd_list to trun the all of the commands according
+ * to command descriptor to NO-OP trb.
+ */
+static int handle_stopped_cmd_ring(struct xhci_hcd *xhci,
+ int cmd_trb_comp_code)
+{
+ int cur_trb_is_good = 0;
+
+ /* Searching the cmd trb pointed by the command ring dequeue
+ * pointer in command descriptor list. If it is found, free it.
+ */
+ cur_trb_is_good = xhci_search_cmd_trb_in_cd_list(xhci,
+ xhci->cmd_ring->dequeue);
+
+ if (cmd_trb_comp_code == COMP_CMD_ABORT)
+ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
+ else if (cmd_trb_comp_code == COMP_CMD_STOP) {
+ /* traversing the cancel_cmd_list and canceling
+ * the command according to command descriptor
+ */
+ xhci_cancel_cmd_in_cd_list(xhci);
+
+ xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
+ /*
+ * ring command ring doorbell again to restart the
+ * command ring
+ */
+ if (xhci->cmd_ring->dequeue != xhci->cmd_ring->enqueue)
+ xhci_ring_cmd_db(xhci);
+ }
+ return cur_trb_is_good;
+}
+
static void handle_cmd_completion(struct xhci_hcd *xhci,
struct xhci_event_cmd *event)
{
@@ -1111,6 +1365,22 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
xhci->error_bitmask |= 1 << 5;
return;
}
+
+ if ((GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_ABORT) ||
+ (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_STOP)) {
+ /* If the return value is 0, we think the trb pointed by
+ * command ring dequeue pointer is a good trb. The good
+ * trb means we don't want to cancel the trb, but it have
+ * been stopped by host. So we should handle it normally.
+ * Otherwise, driver should invoke inc_deq() and return.
+ */
+ if (handle_stopped_cmd_ring(xhci,
+ GET_COMP_CODE(le32_to_cpu(event->status)))) {
+ inc_deq(xhci, xhci->cmd_ring);
+ return;
+ }
+ }
+
switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])
& TRB_TYPE_BITMASK) {
case TRB_TYPE(TRB_ENABLE_SLOT):
@@ -2003,6 +2273,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
*/
static int handle_tx_event(struct xhci_hcd *xhci,
struct xhci_transfer_event *event)
+ __releases(&xhci->lock)
+ __acquires(&xhci->lock)
{
struct xhci_virt_device *xdev;
struct xhci_virt_ep *ep;
@@ -2580,7 +2852,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
xhci_err(xhci, "Ring expansion failed\n");
return -ENOMEM;
}
- };
+ }
if (enqueue_is_link_trb(ep_ring)) {
struct xhci_ring *ring = ep_ring;
@@ -3400,7 +3672,9 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
} else {
td->last_trb = ep_ring->enqueue;
field |= TRB_IOC;
- if (xhci->hci_version == 0x100) {
+ if (xhci->hci_version == 0x100 &&
+ !(xhci->quirks &
+ XHCI_AVOID_BEI)) {
/* Set BEI bit except for the last td */
if (i < num_tds - 1)
field |= TRB_BEI;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 6ece0ed288d4..8d7fcbbe6ade 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -52,7 +52,7 @@ MODULE_PARM_DESC(link_quirk, "Don't clear the chain bit on a link TRB");
* handshake done). There are two failure modes: "usec" have passed (major
* hardware flakeout), or the register reads as all-ones (hardware removed).
*/
-static int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
+int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
u32 mask, u32 done, int usec)
{
u32 result;
@@ -105,9 +105,10 @@ int xhci_halt(struct xhci_hcd *xhci)
ret = handshake(xhci, &xhci->op_regs->status,
STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC);
- if (!ret)
+ if (!ret) {
xhci->xhc_state |= XHCI_STATE_HALTED;
- else
+ xhci->cmd_ring_state = CMD_RING_STATE_STOPPED;
+ } else
xhci_warn(xhci, "Host not halted after %u microseconds.\n",
XHCI_MAX_HALT_USEC);
return ret;
@@ -470,6 +471,8 @@ static bool compliance_mode_recovery_timer_quirk_check(void)
dmi_product_name = dmi_get_system_info(DMI_PRODUCT_NAME);
dmi_sys_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+ if (!dmi_product_name || !dmi_sys_vendor)
+ return false;
if (!(strstr(dmi_sys_vendor, "Hewlett-Packard")))
return false;
@@ -581,6 +584,7 @@ static int xhci_run_finished(struct xhci_hcd *xhci)
return -ENODEV;
}
xhci->shared_hcd->state = HC_STATE_RUNNING;
+ xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
if (xhci->quirks & XHCI_NEC_HOST)
xhci_ring_cmd_db(xhci);
@@ -886,7 +890,7 @@ int xhci_suspend(struct xhci_hcd *xhci)
command &= ~CMD_RUN;
xhci_writel(xhci, command, &xhci->op_regs->command);
if (handshake(xhci, &xhci->op_regs->status,
- STS_HALT, STS_HALT, 100*100)) {
+ STS_HALT, STS_HALT, XHCI_MAX_HALT_USEC)) {
xhci_warn(xhci, "WARN: xHC CMD_RUN timeout\n");
spin_unlock_irq(&xhci->lock);
return -ETIMEDOUT;
@@ -1948,7 +1952,7 @@ static void xhci_finish_resource_reservation(struct xhci_hcd *xhci,
xhci->num_active_eps);
}
-unsigned int xhci_get_block_size(struct usb_device *udev)
+static unsigned int xhci_get_block_size(struct usb_device *udev)
{
switch (udev->speed) {
case USB_SPEED_LOW:
@@ -1966,7 +1970,8 @@ unsigned int xhci_get_block_size(struct usb_device *udev)
}
}
-unsigned int xhci_get_largest_overhead(struct xhci_interval_bw *interval_bw)
+static unsigned int
+xhci_get_largest_overhead(struct xhci_interval_bw *interval_bw)
{
if (interval_bw->overhead[LS_OVERHEAD_TYPE])
return LS_OVERHEAD;
@@ -2521,6 +2526,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
struct completion *cmd_completion;
u32 *cmd_status;
struct xhci_virt_device *virt_dev;
+ union xhci_trb *cmd_trb;
spin_lock_irqsave(&xhci->lock, flags);
virt_dev = xhci->devs[udev->slot_id];
@@ -2566,6 +2572,7 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
}
init_completion(cmd_completion);
+ cmd_trb = xhci->cmd_ring->dequeue;
if (!ctx_change)
ret = xhci_queue_configure_endpoint(xhci, in_ctx->dma,
udev->slot_id, must_succeed);
@@ -2587,14 +2594,17 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
/* Wait for the configure endpoint command to complete */
timeleft = wait_for_completion_interruptible_timeout(
cmd_completion,
- USB_CTRL_SET_TIMEOUT);
+ XHCI_CMD_DEFAULT_TIMEOUT);
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for %s command\n",
timeleft == 0 ? "Timeout" : "Signal",
ctx_change == 0 ?
"configure endpoint" :
"evaluate context");
- /* FIXME cancel the configure endpoint command */
+ /* cancel the configure endpoint command */
+ ret = xhci_cancel_cmd(xhci, command, cmd_trb);
+ if (ret < 0)
+ return ret;
return -ETIME;
}
@@ -3543,8 +3553,10 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
unsigned long flags;
int timeleft;
int ret;
+ union xhci_trb *cmd_trb;
spin_lock_irqsave(&xhci->lock, flags);
+ cmd_trb = xhci->cmd_ring->dequeue;
ret = xhci_queue_slot_control(xhci, TRB_ENABLE_SLOT, 0);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
@@ -3556,12 +3568,12 @@ int xhci_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
/* XXX: how much time for xHC slot assignment? */
timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
- USB_CTRL_SET_TIMEOUT);
+ XHCI_CMD_DEFAULT_TIMEOUT);
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for a slot\n",
timeleft == 0 ? "Timeout" : "Signal");
- /* FIXME cancel the enable slot request */
- return 0;
+ /* cancel the enable slot request */
+ return xhci_cancel_cmd(xhci, NULL, cmd_trb);
}
if (!xhci->slot_id) {
@@ -3622,6 +3634,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
struct xhci_slot_ctx *slot_ctx;
struct xhci_input_control_ctx *ctrl_ctx;
u64 temp_64;
+ union xhci_trb *cmd_trb;
if (!udev->slot_id) {
xhci_dbg(xhci, "Bad Slot ID %d\n", udev->slot_id);
@@ -3660,6 +3673,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
xhci_dbg_ctx(xhci, virt_dev->in_ctx, 2);
spin_lock_irqsave(&xhci->lock, flags);
+ cmd_trb = xhci->cmd_ring->dequeue;
ret = xhci_queue_address_device(xhci, virt_dev->in_ctx->dma,
udev->slot_id);
if (ret) {
@@ -3672,7 +3686,7 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
/* ctrl tx can take up to 5 sec; XXX: need more time for xHC? */
timeleft = wait_for_completion_interruptible_timeout(&xhci->addr_dev,
- USB_CTRL_SET_TIMEOUT);
+ XHCI_CMD_DEFAULT_TIMEOUT);
/* FIXME: From section 4.3.4: "Software shall be responsible for timing
* the SetAddress() "recovery interval" required by USB and aborting the
* command on a timeout.
@@ -3680,7 +3694,10 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
if (timeleft <= 0) {
xhci_warn(xhci, "%s while waiting for address device command\n",
timeleft == 0 ? "Timeout" : "Signal");
- /* FIXME cancel the address device command */
+ /* cancel the address device command */
+ ret = xhci_cancel_cmd(xhci, NULL, cmd_trb);
+ if (ret < 0)
+ return ret;
return -ETIME;
}
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 1a05908c6673..53df4e70ca07 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1256,6 +1256,16 @@ struct xhci_td {
union xhci_trb *last_trb;
};
+/* xHCI command default timeout value */
+#define XHCI_CMD_DEFAULT_TIMEOUT (5 * HZ)
+
+/* command descriptor */
+struct xhci_cd {
+ struct list_head cancel_cmd_list;
+ struct xhci_command *command;
+ union xhci_trb *cmd_trb;
+};
+
struct xhci_dequeue_state {
struct xhci_segment *new_deq_seg;
union xhci_trb *new_deq_ptr;
@@ -1421,6 +1431,11 @@ struct xhci_hcd {
/* data structures */
struct xhci_device_context_array *dcbaa;
struct xhci_ring *cmd_ring;
+ unsigned int cmd_ring_state;
+#define CMD_RING_STATE_RUNNING (1 << 0)
+#define CMD_RING_STATE_ABORTED (1 << 1)
+#define CMD_RING_STATE_STOPPED (1 << 2)
+ struct list_head cancel_cmd_list;
unsigned int cmd_ring_reserved_trbs;
struct xhci_ring *event_ring;
struct xhci_erst erst;
@@ -1496,6 +1511,7 @@ struct xhci_hcd {
#define XHCI_INTEL_HOST (1 << 12)
#define XHCI_SPURIOUS_REBOOT (1 << 13)
#define XHCI_COMP_MODE_QUIRK (1 << 14)
+#define XHCI_AVOID_BEI (1 << 15)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1704,6 +1720,8 @@ static inline void xhci_unregister_plat(void)
/* xHCI host controller glue */
typedef void (*xhci_get_quirks_t)(struct device *, struct xhci_hcd *);
+int handshake(struct xhci_hcd *xhci, void __iomem *ptr,
+ u32 mask, u32 done, int usec);
void xhci_quiesce(struct xhci_hcd *xhci);
int xhci_halt(struct xhci_hcd *xhci);
int xhci_reset(struct xhci_hcd *xhci);
@@ -1794,6 +1812,8 @@ void xhci_queue_config_ep_quirk(struct xhci_hcd *xhci,
unsigned int slot_id, unsigned int ep_index,
struct xhci_dequeue_state *deq_state);
void xhci_stop_endpoint_command_watchdog(unsigned long arg);
+int xhci_cancel_cmd(struct xhci_hcd *xhci, struct xhci_command *command,
+ union xhci_trb *cmd_trb);
void xhci_ring_ep_doorbell(struct xhci_hcd *xhci, unsigned int slot_id,
unsigned int ep_index, unsigned int stream_id);
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 1bfcd02ebeb5..a8f05239350e 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -244,3 +244,8 @@ config USB_YUREX
To compile this driver as a module, choose M here: the
module will be called yurex.
+config USB_EZUSB_FX2
+ tristate "Functions for loading firmware on EZUSB chips"
+ help
+ Say Y here if you need EZUSB device support.
+ (Cypress FX/FX2/FX2LP microcontrollers)
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 796ce7ebccc8..3e99a643294b 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_USB_CYPRESS_CY7C63) += cypress_cy7c63.o
obj-$(CONFIG_USB_CYTHERM) += cytherm.o
obj-$(CONFIG_USB_EMI26) += emi26.o
obj-$(CONFIG_USB_EMI62) += emi62.o
+obj-$(CONFIG_USB_EZUSB_FX2) += ezusb.o
obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
obj-$(CONFIG_USB_IOWARRIOR) += iowarrior.o
diff --git a/drivers/usb/misc/ezusb.c b/drivers/usb/misc/ezusb.c
new file mode 100644
index 000000000000..4223d761223d
--- /dev/null
+++ b/drivers/usb/misc/ezusb.c
@@ -0,0 +1,160 @@
+/*
+ * EZ-USB specific functions used by some of the USB to Serial drivers.
+ *
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.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/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/firmware.h>
+#include <linux/ihex.h>
+
+struct ezusb_fx_type {
+ /* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */
+ unsigned short cpucs_reg;
+ unsigned short max_internal_adress;
+};
+
+struct ezusb_fx_type ezusb_fx1 = {
+ .cpucs_reg = 0x7F92,
+ .max_internal_adress = 0x1B3F,
+};
+
+struct ezusb_fx_type ezusb_fx2 = {
+ .cpucs_reg = 0xE600,
+ .max_internal_adress = 0x3FFF,
+};
+
+/* Commands for writing to memory */
+#define WRITE_INT_RAM 0xA0
+#define WRITE_EXT_RAM 0xA3
+
+int ezusb_writememory(struct usb_device *dev, int address,
+ unsigned char *data, int length, __u8 request)
+{
+ int result;
+ unsigned char *transfer_buffer;
+
+ if (!dev)
+ return -ENODEV;
+
+ transfer_buffer = kmemdup(data, length, GFP_KERNEL);
+ if (!transfer_buffer) {
+ dev_err(&dev->dev, "%s - kmalloc(%d) failed.\n",
+ __func__, length);
+ return -ENOMEM;
+ }
+ result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ address, 0, transfer_buffer, length, 3000);
+
+ kfree(transfer_buffer);
+ return result;
+}
+EXPORT_SYMBOL_GPL(ezusb_writememory);
+
+int ezusb_set_reset(struct usb_device *dev, unsigned short cpucs_reg,
+ unsigned char reset_bit)
+{
+ int response = ezusb_writememory(dev, cpucs_reg, &reset_bit, 1, WRITE_INT_RAM);
+ if (response < 0)
+ dev_err(&dev->dev, "%s-%d failed: %d\n",
+ __func__, reset_bit, response);
+ return response;
+}
+
+int ezusb_fx1_set_reset(struct usb_device *dev, unsigned char reset_bit)
+{
+ return ezusb_set_reset(dev, ezusb_fx1.cpucs_reg, reset_bit);
+}
+EXPORT_SYMBOL_GPL(ezusb_fx1_set_reset);
+
+int ezusb_fx2_set_reset(struct usb_device *dev, unsigned char reset_bit)
+{
+ return ezusb_set_reset(dev, ezusb_fx2.cpucs_reg, reset_bit);
+}
+EXPORT_SYMBOL_GPL(ezusb_fx2_set_reset);
+
+static int ezusb_ihex_firmware_download(struct usb_device *dev,
+ struct ezusb_fx_type fx,
+ const char *firmware_path)
+{
+ int ret = -ENOENT;
+ const struct firmware *firmware = NULL;
+ const struct ihex_binrec *record;
+
+ if (request_ihex_firmware(&firmware, firmware_path,
+ &dev->dev)) {
+ dev_err(&dev->dev,
+ "%s - request \"%s\" failed\n",
+ __func__, firmware_path);
+ goto out;
+ }
+
+ ret = ezusb_set_reset(dev, fx.cpucs_reg, 0);
+ if (ret < 0)
+ goto out;
+
+ record = (const struct ihex_binrec *)firmware->data;
+ for (; record; record = ihex_next_binrec(record)) {
+ if (be32_to_cpu(record->addr) > fx.max_internal_adress) {
+ ret = ezusb_writememory(dev, be32_to_cpu(record->addr),
+ (unsigned char *)record->data,
+ be16_to_cpu(record->len), WRITE_EXT_RAM);
+ if (ret < 0) {
+ dev_err(&dev->dev, "%s - ezusb_writememory "
+ "failed writing internal memory "
+ "(%d %04X %p %d)\n", __func__, ret,
+ be32_to_cpu(record->addr), record->data,
+ be16_to_cpu(record->len));
+ goto out;
+ }
+ }
+ }
+
+ ret = ezusb_set_reset(dev, fx.cpucs_reg, 1);
+ if (ret < 0)
+ goto out;
+ record = (const struct ihex_binrec *)firmware->data;
+ for (; record; record = ihex_next_binrec(record)) {
+ if (be32_to_cpu(record->addr) <= fx.max_internal_adress) {
+ ret = ezusb_writememory(dev, be32_to_cpu(record->addr),
+ (unsigned char *)record->data,
+ be16_to_cpu(record->len), WRITE_INT_RAM);
+ if (ret < 0) {
+ dev_err(&dev->dev, "%s - ezusb_writememory "
+ "failed writing external memory "
+ "(%d %04X %p %d)\n", __func__, ret,
+ be32_to_cpu(record->addr), record->data,
+ be16_to_cpu(record->len));
+ goto out;
+ }
+ }
+ }
+ ret = ezusb_set_reset(dev, fx.cpucs_reg, 0);
+out:
+ release_firmware(firmware);
+ return ret;
+}
+
+int ezusb_fx1_ihex_firmware_download(struct usb_device *dev,
+ const char *firmware_path)
+{
+ return ezusb_ihex_firmware_download(dev, ezusb_fx1, firmware_path);
+}
+EXPORT_SYMBOL_GPL(ezusb_fx1_ihex_firmware_download);
+
+int ezusb_fx2_ihex_firmware_download(struct usb_device *dev,
+ const char *firmware_path)
+{
+ return ezusb_ihex_firmware_download(dev, ezusb_fx2, firmware_path);
+}
+EXPORT_SYMBOL_GPL(ezusb_fx2_ihex_firmware_download);
+
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index a2702cbfe804..80894791c020 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -868,9 +868,6 @@ static int tower_probe (struct usb_interface *interface, const struct usb_device
dbg(2, "%s: enter", __func__);
- if (udev == NULL)
- dev_info(&interface->dev, "udev is NULL.\n");
-
/* allocate memory for our device state and initialize it */
dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL);
diff --git a/drivers/usb/misc/rio500.c b/drivers/usb/misc/rio500.c
index 1084124c4a44..b9b356a9dd11 100644
--- a/drivers/usb/misc/rio500.c
+++ b/drivers/usb/misc/rio500.c
@@ -338,7 +338,7 @@ write_rio(struct file *file, const char __user *buffer,
thistime -= partial;
} else
break;
- };
+ }
if (result) {
dev_err(&rio->rio_dev->dev, "Write Whoops - %x\n",
result);
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 6259f0d99324..23a0b7f0892d 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -12,7 +12,6 @@ config USB_MUSB_HDRC
select TWL4030_USB if MACH_OMAP_3430SDP
select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
select USB_OTG_UTILS
- select USB_GADGET_DUALSPEED
help
Say Y here if your system has a dual role high speed USB
controller based on the Mentor Graphics silicon IP. Then
@@ -20,7 +19,7 @@ config USB_MUSB_HDRC
it's being used with, including the USB peripheral role,
or the USB host role, or both.
- Texas Instruments familiies using this IP include DaVinci
+ Texas Instruments families using this IP include DaVinci
(35x, 644x ...), OMAP 243x, OMAP 3, and TUSB 6010.
Analog Devices parts using this IP include Blackfin BF54x,
diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c
index 7a95ab87ac00..457f25e62c51 100644
--- a/drivers/usb/musb/am35x.c
+++ b/drivers/usb/musb/am35x.c
@@ -33,6 +33,7 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <plat/usb.h>
@@ -107,9 +108,8 @@ static void am35x_musb_enable(struct musb *musb)
musb_writel(reg_base, CORE_INTR_MASK_SET_REG, AM35X_INTR_USB_MASK);
/* Force the DRVVBUS IRQ so we can start polling for ID change. */
- if (is_otg_enabled(musb))
- musb_writel(reg_base, CORE_INTR_SRC_SET_REG,
- AM35X_INTR_DRVVBUS << AM35X_INTR_USB_SHIFT);
+ musb_writel(reg_base, CORE_INTR_SRC_SET_REG,
+ AM35X_INTR_DRVVBUS << AM35X_INTR_USB_SHIFT);
}
/*
@@ -173,9 +173,6 @@ static void otg_timer(unsigned long _musb)
MUSB_INTR_VBUSERROR << AM35X_INTR_USB_SHIFT);
break;
case OTG_STATE_B_IDLE:
- if (!is_peripheral_enabled(musb))
- break;
-
devctl = musb_readb(mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
@@ -192,9 +189,6 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout)
{
static unsigned long last_timer;
- if (!is_otg_enabled(musb))
- return;
-
if (timeout == 0)
timeout = jiffies + msecs_to_jiffies(3);
@@ -271,8 +265,7 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci)
u8 devctl = musb_readb(mregs, MUSB_DEVCTL);
int err;
- err = is_host_enabled(musb) && (musb->int_usb &
- MUSB_INTR_VBUSERROR);
+ err = musb->int_usb & MUSB_INTR_VBUSERROR;
if (err) {
/*
* The Mentor core doesn't debounce VBUS as needed
@@ -289,7 +282,7 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci)
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
WARNING("VBUS error workaround (delay coming)\n");
- } else if (is_host_enabled(musb) && drvvbus) {
+ } else if (drvvbus) {
MUSB_HST_MODE(musb);
otg->default_a = 1;
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
@@ -326,7 +319,7 @@ eoi:
}
/* Poll for ID change */
- if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE)
+ if (musb->xceiv->state == OTG_STATE_B_IDLE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
spin_unlock_irqrestore(&musb->lock, flags);
@@ -369,8 +362,7 @@ static int am35x_musb_init(struct musb *musb)
if (IS_ERR_OR_NULL(musb->xceiv))
return -ENODEV;
- if (is_host_enabled(musb))
- setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
+ setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
/* Reset the musb */
if (data->reset)
@@ -400,8 +392,7 @@ static int am35x_musb_exit(struct musb *musb)
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
- if (is_host_enabled(musb))
- del_timer_sync(&otg_workaround);
+ del_timer_sync(&otg_workaround);
/* Shutdown the on-chip PHY and its PLL. */
if (data->set_phy_power)
@@ -468,6 +459,7 @@ static int __devinit am35x_probe(struct platform_device *pdev)
struct clk *clk;
int ret = -ENOMEM;
+ int musbid;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
@@ -475,38 +467,47 @@ static int __devinit am35x_probe(struct platform_device *pdev)
goto err0;
}
- musb = platform_device_alloc("musb-hdrc", -1);
+ /* get the musb id */
+ musbid = musb_get_id(&pdev->dev, GFP_KERNEL);
+ if (musbid < 0) {
+ dev_err(&pdev->dev, "failed to allocate musb id\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ musb = platform_device_alloc("musb-hdrc", musbid);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err1;
+ goto err2;
}
phy_clk = clk_get(&pdev->dev, "fck");
if (IS_ERR(phy_clk)) {
dev_err(&pdev->dev, "failed to get PHY clock\n");
ret = PTR_ERR(phy_clk);
- goto err2;
+ goto err3;
}
clk = clk_get(&pdev->dev, "ick");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
ret = PTR_ERR(clk);
- goto err3;
+ goto err4;
}
ret = clk_enable(phy_clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable PHY clock\n");
- goto err4;
+ goto err5;
}
ret = clk_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock\n");
- goto err5;
+ goto err6;
}
+ musb->id = musbid;
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &am35x_dmamask;
musb->dev.coherent_dma_mask = am35x_dmamask;
@@ -524,38 +525,41 @@ static int __devinit am35x_probe(struct platform_device *pdev)
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err6;
+ goto err7;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err6;
+ goto err7;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err6;
+ goto err7;
}
return 0;
-err6:
+err7:
clk_disable(clk);
-err5:
+err6:
clk_disable(phy_clk);
-err4:
+err5:
clk_put(clk);
-err3:
+err4:
clk_put(phy_clk);
-err2:
+err3:
platform_device_put(musb);
+err2:
+ musb_put_id(&pdev->dev, musbid);
+
err1:
kfree(glue);
@@ -567,6 +571,7 @@ static int __devexit am35x_remove(struct platform_device *pdev)
{
struct am35x_glue *glue = platform_get_drvdata(pdev);
+ musb_put_id(&pdev->dev, glue->musb->id);
platform_device_del(glue->musb);
platform_device_put(glue->musb);
clk_disable(glue->clk);
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index 428e6aa3e78a..e8cff9bb9d23 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -19,6 +19,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/prefetch.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <asm/cacheflush.h>
@@ -184,8 +185,8 @@ static irqreturn_t blackfin_interrupt(int irq, void *__hci)
}
/* Start sampling ID pin, when plug is removed from MUSB */
- if ((is_otg_enabled(musb) && (musb->xceiv->state == OTG_STATE_B_IDLE
- || musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) ||
+ if ((musb->xceiv->state == OTG_STATE_B_IDLE
+ || musb->xceiv->state == OTG_STATE_A_WAIT_BCON) ||
(musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb))) {
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
musb->a_wait_bcon = TIMER_DELAY;
@@ -228,18 +229,13 @@ static void musb_conn_timer_handler(unsigned long _musb)
val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR;
musb_writeb(musb->mregs, MUSB_INTRUSB, val);
- if (is_otg_enabled(musb))
- musb->xceiv->state = OTG_STATE_B_IDLE;
- else
- musb_writeb(musb->mregs, MUSB_POWER, MUSB_POWER_HSENAB);
+ musb->xceiv->state = OTG_STATE_B_IDLE;
}
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
break;
case OTG_STATE_B_IDLE:
-
- if (!is_peripheral_enabled(musb))
- break;
- /* Start a new session. It seems that MUSB needs taking
+ /*
+ * Start a new session. It seems that MUSB needs taking
* some time to recognize the type of the plug inserted?
*/
val = musb_readw(musb->mregs, MUSB_DEVCTL);
@@ -295,10 +291,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
static void bfin_musb_enable(struct musb *musb)
{
- if (!is_otg_enabled(musb) && is_host_enabled(musb)) {
- mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
- musb->a_wait_bcon = TIMER_DELAY;
- }
+ /* REVISIT is this really correct ? */
}
static void bfin_musb_disable(struct musb *musb)
@@ -323,12 +316,6 @@ static int bfin_musb_set_power(struct usb_phy *x, unsigned mA)
return 0;
}
-static void bfin_musb_try_idle(struct musb *musb, unsigned long timeout)
-{
- if (!is_otg_enabled(musb) && is_host_enabled(musb))
- mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
-}
-
static int bfin_musb_vbus_status(struct musb *musb)
{
return 0;
@@ -424,12 +411,10 @@ static int bfin_musb_init(struct musb *musb)
bfin_musb_reg_init(musb);
- if (is_host_enabled(musb)) {
- setup_timer(&musb_conn_timer,
- musb_conn_timer_handler, (unsigned long) musb);
- }
- if (is_peripheral_enabled(musb))
- musb->xceiv->set_power = bfin_musb_set_power;
+ setup_timer(&musb_conn_timer, musb_conn_timer_handler,
+ (unsigned long) musb);
+
+ musb->xceiv->set_power = bfin_musb_set_power;
musb->isr = blackfin_interrupt;
musb->double_buffer_not_ok = true;
@@ -454,7 +439,6 @@ static const struct musb_platform_ops bfin_ops = {
.disable = bfin_musb_disable,
.set_mode = bfin_musb_set_mode,
- .try_idle = bfin_musb_try_idle,
.vbus_status = bfin_musb_vbus_status,
.set_vbus = bfin_musb_set_vbus,
@@ -471,6 +455,7 @@ static int __devinit bfin_probe(struct platform_device *pdev)
struct bfin_glue *glue;
int ret = -ENOMEM;
+ int musbid;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
@@ -478,12 +463,21 @@ static int __devinit bfin_probe(struct platform_device *pdev)
goto err0;
}
- musb = platform_device_alloc("musb-hdrc", -1);
+ /* get the musb id */
+ musbid = musb_get_id(&pdev->dev, GFP_KERNEL);
+ if (musbid < 0) {
+ dev_err(&pdev->dev, "failed to allocate musb id\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ musb = platform_device_alloc("musb-hdrc", musbid);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err1;
+ goto err2;
}
+ musb->id = musbid;
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &bfin_dmamask;
musb->dev.coherent_dma_mask = bfin_dmamask;
@@ -499,26 +493,29 @@ static int __devinit bfin_probe(struct platform_device *pdev)
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err2;
+ goto err3;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err2;
+ goto err3;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err2;
+ goto err3;
}
return 0;
-err2:
+err3:
platform_device_put(musb);
+err2:
+ musb_put_id(&pdev->dev, musbid);
+
err1:
kfree(glue);
@@ -530,6 +527,7 @@ static int __devexit bfin_remove(struct platform_device *pdev)
{
struct bfin_glue *glue = platform_get_drvdata(pdev);
+ musb_put_id(&pdev->dev, glue->musb->id);
platform_device_del(glue->musb);
platform_device_put(glue->musb);
kfree(glue);
diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c
index 8637c1f69fc3..e19da82b4782 100644
--- a/drivers/usb/musb/cppi_dma.c
+++ b/drivers/usb/musb/cppi_dma.c
@@ -1316,7 +1316,7 @@ irqreturn_t cppi_interrupt(int irq, void *dev_id)
}
/* Instantiate a software object representing a DMA controller. */
-struct dma_controller *__init
+struct dma_controller *__devinit
dma_controller_create(struct musb *musb, void __iomem *mregs)
{
struct cppi *controller;
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index 15a262754150..8bc44b76eec2 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -33,6 +33,7 @@
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <mach/da8xx.h>
#include <linux/platform_data/usb-davinci.h>
@@ -155,9 +156,8 @@ static void da8xx_musb_enable(struct musb *musb)
musb_writel(reg_base, DA8XX_USB_INTR_MASK_SET_REG, mask);
/* Force the DRVVBUS IRQ so we can start polling for ID change. */
- if (is_otg_enabled(musb))
- musb_writel(reg_base, DA8XX_USB_INTR_SRC_SET_REG,
- DA8XX_INTR_DRVVBUS << DA8XX_INTR_USB_SHIFT);
+ musb_writel(reg_base, DA8XX_USB_INTR_SRC_SET_REG,
+ DA8XX_INTR_DRVVBUS << DA8XX_INTR_USB_SHIFT);
}
/**
@@ -231,9 +231,6 @@ static void otg_timer(unsigned long _musb)
MUSB_INTR_VBUSERROR << DA8XX_INTR_USB_SHIFT);
break;
case OTG_STATE_B_IDLE:
- if (!is_peripheral_enabled(musb))
- break;
-
/*
* There's no ID-changed IRQ, so we have no good way to tell
* when to switch to the A-Default state machine (by setting
@@ -263,9 +260,6 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
{
static unsigned long last_timer;
- if (!is_otg_enabled(musb))
- return;
-
if (timeout == 0)
timeout = jiffies + msecs_to_jiffies(3);
@@ -333,8 +327,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
u8 devctl = musb_readb(mregs, MUSB_DEVCTL);
int err;
- err = is_host_enabled(musb) && (musb->int_usb &
- MUSB_INTR_VBUSERROR);
+ err = musb->int_usb & USB_INTR_VBUSERROR;
if (err) {
/*
* The Mentor core doesn't debounce VBUS as needed
@@ -351,7 +344,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
WARNING("VBUS error workaround (delay coming)\n");
- } else if (is_host_enabled(musb) && drvvbus) {
+ } else if (drvvbus) {
MUSB_HST_MODE(musb);
otg->default_a = 1;
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
@@ -382,7 +375,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
musb_writel(reg_base, DA8XX_USB_END_OF_INTR_REG, 0);
/* Poll for ID change */
- if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE)
+ if (musb->xceiv->state == OTG_STATE_B_IDLE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
spin_unlock_irqrestore(&musb->lock, flags);
@@ -430,8 +423,7 @@ static int da8xx_musb_init(struct musb *musb)
if (IS_ERR_OR_NULL(musb->xceiv))
goto fail;
- if (is_host_enabled(musb))
- setup_timer(&otg_workaround, otg_timer, (unsigned long)musb);
+ setup_timer(&otg_workaround, otg_timer, (unsigned long)musb);
/* Reset the controller */
musb_writel(reg_base, DA8XX_USB_CTRL_REG, DA8XX_SOFT_RESET_MASK);
@@ -454,8 +446,7 @@ fail:
static int da8xx_musb_exit(struct musb *musb)
{
- if (is_host_enabled(musb))
- del_timer_sync(&otg_workaround);
+ del_timer_sync(&otg_workaround);
phy_off();
@@ -489,6 +480,7 @@ static int __devinit da8xx_probe(struct platform_device *pdev)
struct clk *clk;
int ret = -ENOMEM;
+ int musbid;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
@@ -496,25 +488,34 @@ static int __devinit da8xx_probe(struct platform_device *pdev)
goto err0;
}
- musb = platform_device_alloc("musb-hdrc", -1);
+ /* get the musb id */
+ musbid = musb_get_id(&pdev->dev, GFP_KERNEL);
+ if (musbid < 0) {
+ dev_err(&pdev->dev, "failed to allocate musb id\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ musb = platform_device_alloc("musb-hdrc", musbid);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err1;
+ goto err2;
}
clk = clk_get(&pdev->dev, "usb20");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
ret = PTR_ERR(clk);
- goto err2;
+ goto err3;
}
ret = clk_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock\n");
- goto err3;
+ goto err4;
}
+ musb->id = musbid;
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &da8xx_dmamask;
musb->dev.coherent_dma_mask = da8xx_dmamask;
@@ -531,32 +532,35 @@ static int __devinit da8xx_probe(struct platform_device *pdev)
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err4;
+ goto err5;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err4;
+ goto err5;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err4;
+ goto err5;
}
return 0;
-err4:
+err5:
clk_disable(clk);
-err3:
+err4:
clk_put(clk);
-err2:
+err3:
platform_device_put(musb);
+err2:
+ musb_put_id(&pdev->dev, musbid);
+
err1:
kfree(glue);
@@ -568,6 +572,7 @@ static int __devexit da8xx_remove(struct platform_device *pdev)
{
struct da8xx_glue *glue = platform_get_drvdata(pdev);
+ musb_put_id(&pdev->dev, glue->musb->id);
platform_device_del(glue->musb);
platform_device_put(glue->musb);
clk_disable(glue->clk);
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
index 472c8b42d38b..606bfd00cde6 100644
--- a/drivers/usb/musb/davinci.c
+++ b/drivers/usb/musb/davinci.c
@@ -33,6 +33,7 @@
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <mach/cputype.h>
#include <mach/hardware.h>
@@ -115,8 +116,7 @@ static void davinci_musb_enable(struct musb *musb)
dma_off = 0;
/* force a DRVVBUS irq so we can start polling for ID change */
- if (is_otg_enabled(musb))
- musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
+ musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
DAVINCI_INTR_DRVVBUS << DAVINCI_USB_USBINT_SHIFT);
}
@@ -234,10 +234,8 @@ static void otg_timer(unsigned long _musb)
MUSB_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT);
break;
case OTG_STATE_B_IDLE:
- if (!is_peripheral_enabled(musb))
- break;
-
- /* There's no ID-changed IRQ, so we have no good way to tell
+ /*
+ * There's no ID-changed IRQ, so we have no good way to tell
* when to switch to the A-Default state machine (by setting
* the DEVCTL.SESSION flag).
*
@@ -315,8 +313,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci)
u8 devctl = musb_readb(mregs, MUSB_DEVCTL);
int err = musb->int_usb & MUSB_INTR_VBUSERROR;
- err = is_host_enabled(musb)
- && (musb->int_usb & MUSB_INTR_VBUSERROR);
+ err = musb->int_usb & MUSB_INTR_VBUSERROR;
if (err) {
/* The Mentor core doesn't debounce VBUS as needed
* to cope with device connect current spikes. This
@@ -332,7 +329,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci)
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
WARNING("VBUS error workaround (delay coming)\n");
- } else if (is_host_enabled(musb) && drvvbus) {
+ } else if (drvvbus) {
MUSB_HST_MODE(musb);
otg->default_a = 1;
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
@@ -365,8 +362,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci)
musb_writel(tibase, DAVINCI_USB_EOI_REG, 0);
/* poll for ID change */
- if (is_otg_enabled(musb)
- && musb->xceiv->state == OTG_STATE_B_IDLE)
+ if (musb->xceiv->state == OTG_STATE_B_IDLE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
spin_unlock_irqrestore(&musb->lock, flags);
@@ -397,8 +393,7 @@ static int davinci_musb_init(struct musb *musb)
if (revision == 0)
goto fail;
- if (is_host_enabled(musb))
- setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
+ setup_timer(&otg_workaround, otg_timer, (unsigned long) musb);
davinci_musb_source_power(musb, 0, 1);
@@ -419,12 +414,7 @@ static int davinci_musb_init(struct musb *musb)
if (cpu_is_davinci_dm355()) {
u32 deepsleep = __raw_readl(DM355_DEEPSLEEP);
- if (is_host_enabled(musb)) {
- deepsleep &= ~DRVVBUS_OVERRIDE;
- } else {
- deepsleep &= ~DRVVBUS_FORCE;
- deepsleep |= DRVVBUS_OVERRIDE;
- }
+ deepsleep &= ~DRVVBUS_FORCE;
__raw_writel(deepsleep, DM355_DEEPSLEEP);
}
@@ -453,8 +443,7 @@ unregister:
static int davinci_musb_exit(struct musb *musb)
{
- if (is_host_enabled(musb))
- del_timer_sync(&otg_workaround);
+ del_timer_sync(&otg_workaround);
/* force VBUS off */
if (cpu_is_davinci_dm355()) {
@@ -468,7 +457,7 @@ static int davinci_musb_exit(struct musb *musb)
davinci_musb_source_power(musb, 0 /*off*/, 1);
/* delay, to avoid problems with module reload */
- if (is_host_enabled(musb) && musb->xceiv->otg->default_a) {
+ if (musb->xceiv->otg->default_a) {
int maxdelay = 30;
u8 devctl, warn = 0;
@@ -523,6 +512,7 @@ static int __devinit davinci_probe(struct platform_device *pdev)
struct clk *clk;
int ret = -ENOMEM;
+ int musbid;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
@@ -530,25 +520,34 @@ static int __devinit davinci_probe(struct platform_device *pdev)
goto err0;
}
- musb = platform_device_alloc("musb-hdrc", -1);
+ /* get the musb id */
+ musbid = musb_get_id(&pdev->dev, GFP_KERNEL);
+ if (musbid < 0) {
+ dev_err(&pdev->dev, "failed to allocate musb id\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ musb = platform_device_alloc("musb-hdrc", musbid);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err1;
+ goto err2;
}
clk = clk_get(&pdev->dev, "usb");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
ret = PTR_ERR(clk);
- goto err2;
+ goto err3;
}
ret = clk_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock\n");
- goto err3;
+ goto err4;
}
+ musb->id = musbid;
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &davinci_dmamask;
musb->dev.coherent_dma_mask = davinci_dmamask;
@@ -565,32 +564,35 @@ static int __devinit davinci_probe(struct platform_device *pdev)
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err4;
+ goto err5;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err4;
+ goto err5;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err4;
+ goto err5;
}
return 0;
-err4:
+err5:
clk_disable(clk);
-err3:
+err4:
clk_put(clk);
-err2:
+err3:
platform_device_put(musb);
+err2:
+ musb_put_id(&pdev->dev, musbid);
+
err1:
kfree(glue);
@@ -602,6 +604,7 @@ static int __devexit davinci_remove(struct platform_device *pdev)
{
struct davinci_glue *glue = platform_get_drvdata(pdev);
+ musb_put_id(&pdev->dev, glue->musb->id);
platform_device_del(glue->musb);
platform_device_put(glue->musb);
clk_disable(glue->clk);
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 26f1befb4896..bb56a0e8b23b 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -99,6 +99,8 @@
#include <linux/prefetch.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/idr.h>
+#include <linux/dma-mapping.h>
#include "musb_core.h"
@@ -114,6 +116,7 @@
#define MUSB_DRIVER_NAME "musb-hdrc"
const char musb_driver_name[] = MUSB_DRIVER_NAME;
+static DEFINE_IDA(musb_ida);
MODULE_DESCRIPTION(DRIVER_INFO);
MODULE_AUTHOR(DRIVER_AUTHOR);
@@ -130,6 +133,35 @@ static inline struct musb *dev_to_musb(struct device *dev)
/*-------------------------------------------------------------------------*/
+int musb_get_id(struct device *dev, gfp_t gfp_mask)
+{
+ int ret;
+ int id;
+
+ ret = ida_pre_get(&musb_ida, gfp_mask);
+ if (!ret) {
+ dev_err(dev, "failed to reserve resource for id\n");
+ return -ENOMEM;
+ }
+
+ ret = ida_get_new(&musb_ida, &id);
+ if (ret < 0) {
+ dev_err(dev, "failed to allocate a new id\n");
+ return ret;
+ }
+
+ return id;
+}
+EXPORT_SYMBOL_GPL(musb_get_id);
+
+void musb_put_id(struct device *dev, int id)
+{
+
+ dev_dbg(dev, "removing id %d\n", id);
+ ida_remove(&musb_ida, id);
+}
+EXPORT_SYMBOL_GPL(musb_put_id);
+
#ifndef CONFIG_BLACKFIN
static int musb_ulpi_read(struct usb_phy *phy, u32 offset)
{
@@ -234,6 +266,9 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
struct musb *musb = hw_ep->musb;
void __iomem *fifo = hw_ep->fifo;
+ if (unlikely(len == 0))
+ return;
+
prefetch((u8 *)src);
dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n",
@@ -276,6 +311,9 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
struct musb *musb = hw_ep->musb;
void __iomem *fifo = hw_ep->fifo;
+ if (unlikely(len == 0))
+ return;
+
dev_dbg(musb->controller, "%cX ep%d fifo %p count %d buf %p\n",
'R', hw_ep->epnum, fifo, len, dst);
@@ -348,7 +386,7 @@ void musb_load_testpacket(struct musb *musb)
/*
* Handles OTG hnp timeouts, such as b_ase0_brst
*/
-void musb_otg_timer_func(unsigned long data)
+static void musb_otg_timer_func(unsigned long data)
{
struct musb *musb = (struct musb *)data;
unsigned long flags;
@@ -643,8 +681,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
break;
case OTG_STATE_B_PERIPHERAL:
musb_g_suspend(musb);
- musb->is_active = is_otg_enabled(musb)
- && otg->gadget->b_hnp_enable;
+ musb->is_active = otg->gadget->b_hnp_enable;
if (musb->is_active) {
musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n");
@@ -660,8 +697,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
break;
case OTG_STATE_A_HOST:
musb->xceiv->state = OTG_STATE_A_SUSPEND;
- musb->is_active = is_otg_enabled(musb)
- && otg->host->b_hnp_enable;
+ musb->is_active = otg->host->b_hnp_enable;
break;
case OTG_STATE_B_HOST:
/* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
@@ -749,7 +785,7 @@ b_host:
case OTG_STATE_A_SUSPEND:
usb_hcd_resume_root_hub(musb_to_hcd(musb));
musb_root_disconnect(musb);
- if (musb->a_wait_bcon != 0 && is_otg_enabled(musb))
+ if (musb->a_wait_bcon != 0)
musb_platform_try_idle(musb, jiffies
+ msecs_to_jiffies(musb->a_wait_bcon));
break;
@@ -787,7 +823,7 @@ b_host:
*/
if (int_usb & MUSB_INTR_RESET) {
handled = IRQ_HANDLED;
- if (is_host_capable() && (devctl & MUSB_DEVCTL_HM) != 0) {
+ if ((devctl & MUSB_DEVCTL_HM) != 0) {
/*
* Looks like non-HS BABBLE can be ignored, but
* HS BABBLE is an error condition. For HS the solution
@@ -801,7 +837,7 @@ b_host:
ERR("Stopping host session -- babble\n");
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
}
- } else if (is_peripheral_capable()) {
+ } else {
dev_dbg(musb->controller, "BUS RESET as %s\n",
otg_state_string(musb->xceiv->state));
switch (musb->xceiv->state) {
@@ -925,25 +961,16 @@ void musb_start(struct musb *musb)
devctl = musb_readb(regs, MUSB_DEVCTL);
devctl &= ~MUSB_DEVCTL_SESSION;
- if (is_otg_enabled(musb)) {
- /* session started after:
- * (a) ID-grounded irq, host mode;
- * (b) vbus present/connect IRQ, peripheral mode;
- * (c) peripheral initiates, using SRP
- */
- if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
- musb->is_active = 1;
- else
- devctl |= MUSB_DEVCTL_SESSION;
-
- } else if (is_host_enabled(musb)) {
- /* assume ID pin is hard-wired to ground */
+ /* session started after:
+ * (a) ID-grounded irq, host mode;
+ * (b) vbus present/connect IRQ, peripheral mode;
+ * (c) peripheral initiates, using SRP
+ */
+ if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
+ musb->is_active = 1;
+ else
devctl |= MUSB_DEVCTL_SESSION;
- } else /* peripheral is enabled */ {
- if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
- musb->is_active = 1;
- }
musb_platform_enable(musb);
musb_writeb(regs, MUSB_DEVCTL, devctl);
}
@@ -1007,8 +1034,6 @@ static void musb_shutdown(struct platform_device *pdev)
musb_generic_disable(musb);
spin_unlock_irqrestore(&musb->lock, flags);
- if (!is_otg_enabled(musb) && is_host_enabled(musb))
- usb_remove_hcd(musb_to_hcd(musb));
musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
musb_platform_exit(musb);
@@ -1302,7 +1327,7 @@ done:
if (offset < 0) {
pr_debug("%s: mem overrun, ep %d\n",
musb_driver_name, epn);
- return -EINVAL;
+ return offset;
}
epn++;
musb->nr_endpoints = max(epn, musb->nr_endpoints);
@@ -1330,7 +1355,7 @@ static int __devinit ep_config_from_hw(struct musb *musb)
{
u8 epnum = 0;
struct musb_hw_ep *hw_ep;
- void *mbase = musb->mregs;
+ void __iomem *mbase = musb->mregs;
int ret = 0;
dev_dbg(musb->controller, "<== static silicon ep config\n");
@@ -1571,13 +1596,10 @@ irqreturn_t musb_interrupt(struct musb *musb)
/* musb_ep_select(musb->mregs, ep_num); */
/* REVISIT just retval = ep->rx_irq(...) */
retval = IRQ_HANDLED;
- if (devctl & MUSB_DEVCTL_HM) {
- if (is_host_capable())
- musb_host_rx(musb, ep_num);
- } else {
- if (is_peripheral_capable())
- musb_g_rx(musb, ep_num);
- }
+ if (devctl & MUSB_DEVCTL_HM)
+ musb_host_rx(musb, ep_num);
+ else
+ musb_g_rx(musb, ep_num);
}
reg >>= 1;
@@ -1592,13 +1614,10 @@ irqreturn_t musb_interrupt(struct musb *musb)
/* musb_ep_select(musb->mregs, ep_num); */
/* REVISIT just retval |= ep->tx_irq(...) */
retval = IRQ_HANDLED;
- if (devctl & MUSB_DEVCTL_HM) {
- if (is_host_capable())
- musb_host_tx(musb, ep_num);
- } else {
- if (is_peripheral_capable())
- musb_g_tx(musb, ep_num);
- }
+ if (devctl & MUSB_DEVCTL_HM)
+ musb_host_tx(musb, ep_num);
+ else
+ musb_g_tx(musb, ep_num);
}
reg >>= 1;
ep_num++;
@@ -1634,22 +1653,16 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
} else {
/* endpoints 1..15 */
if (transmit) {
- if (devctl & MUSB_DEVCTL_HM) {
- if (is_host_capable())
- musb_host_tx(musb, epnum);
- } else {
- if (is_peripheral_capable())
- musb_g_tx(musb, epnum);
- }
+ if (devctl & MUSB_DEVCTL_HM)
+ musb_host_tx(musb, epnum);
+ else
+ musb_g_tx(musb, epnum);
} else {
/* receive */
- if (devctl & MUSB_DEVCTL_HM) {
- if (is_host_capable())
- musb_host_rx(musb, epnum);
- } else {
- if (is_peripheral_capable())
- musb_g_rx(musb, epnum);
- }
+ if (devctl & MUSB_DEVCTL_HM)
+ musb_host_rx(musb, epnum);
+ else
+ musb_g_rx(musb, epnum);
}
}
}
@@ -1785,10 +1798,9 @@ static const struct attribute_group musb_attr_group = {
static void musb_irq_work(struct work_struct *data)
{
struct musb *musb = container_of(data, struct musb, irq_work);
- static int old_state;
- if (musb->xceiv->state != old_state) {
- old_state = musb->xceiv->state;
+ if (musb->xceiv->state != musb->xceiv_old_state) {
+ musb->xceiv_old_state = musb->xceiv->state;
sysfs_notify(&musb->controller->kobj, NULL, "mode");
}
}
@@ -1862,15 +1874,15 @@ static void musb_free(struct musb *musb)
dma_controller_destroy(c);
}
- kfree(musb);
+ usb_put_hcd(musb_to_hcd(musb));
}
/*
* Perform generic per-controller initialization.
*
- * @pDevice: the controller (already clocked, etc)
- * @nIrq: irq
- * @mregs: virtual address of controller registers,
+ * @dev: the controller (already clocked, etc)
+ * @nIrq: IRQ number
+ * @ctrl: virtual address of controller registers,
* not yet corrected for platform-specific offsets
*/
static int __devinit
@@ -1879,6 +1891,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
int status;
struct musb *musb;
struct musb_hdrc_platform_data *plat = dev->platform_data;
+ struct usb_hcd *hcd;
/* The driver might handle more features than the board; OK.
* Fail when the board needs a feature that's not enabled.
@@ -1901,7 +1914,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
pm_runtime_enable(musb->controller);
spin_lock_init(&musb->lock);
- musb->board_mode = plat->mode;
musb->board_set_power = plat->set_power;
musb->min_power = plat->min_power;
musb->ops = plat->platform_ops;
@@ -1972,7 +1984,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
goto fail3;
}
musb->nIrq = nIrq;
-/* FIXME this handles wakeup irqs wrong */
+ /* FIXME this handles wakeup irqs wrong */
if (enable_irq_wake(nIrq) == 0) {
musb->irq_wake = 1;
device_init_wakeup(dev, 1);
@@ -1981,58 +1993,25 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
}
/* host side needs more setup */
- if (is_host_enabled(musb)) {
- struct usb_hcd *hcd = musb_to_hcd(musb);
-
- otg_set_host(musb->xceiv->otg, &hcd->self);
-
- if (is_otg_enabled(musb))
- hcd->self.otg_port = 1;
- musb->xceiv->otg->host = &hcd->self;
- hcd->power_budget = 2 * (plat->power ? : 250);
-
- /* program PHY to use external vBus if required */
- if (plat->extvbus) {
- u8 busctl = musb_read_ulpi_buscontrol(musb->mregs);
- busctl |= MUSB_ULPI_USE_EXTVBUS;
- musb_write_ulpi_buscontrol(musb->mregs, busctl);
- }
+ hcd = musb_to_hcd(musb);
+ otg_set_host(musb->xceiv->otg, &hcd->self);
+ hcd->self.otg_port = 1;
+ musb->xceiv->otg->host = &hcd->self;
+ hcd->power_budget = 2 * (plat->power ? : 250);
+
+ /* program PHY to use external vBus if required */
+ if (plat->extvbus) {
+ u8 busctl = musb_read_ulpi_buscontrol(musb->mregs);
+ busctl |= MUSB_ULPI_USE_EXTVBUS;
+ musb_write_ulpi_buscontrol(musb->mregs, busctl);
}
- /* For the host-only role, we can activate right away.
- * (We expect the ID pin to be forcibly grounded!!)
- * Otherwise, wait till the gadget driver hooks up.
- */
- if (!is_otg_enabled(musb) && is_host_enabled(musb)) {
- struct usb_hcd *hcd = musb_to_hcd(musb);
-
- MUSB_HST_MODE(musb);
- musb->xceiv->otg->default_a = 1;
- musb->xceiv->state = OTG_STATE_A_IDLE;
-
- status = usb_add_hcd(musb_to_hcd(musb), 0, 0);
-
- hcd->self.uses_pio_for_control = 1;
- dev_dbg(musb->controller, "%s mode, status %d, devctl %02x %c\n",
- "HOST", status,
- musb_readb(musb->mregs, MUSB_DEVCTL),
- (musb_readb(musb->mregs, MUSB_DEVCTL)
- & MUSB_DEVCTL_BDEVICE
- ? 'B' : 'A'));
-
- } else /* peripheral is enabled */ {
- MUSB_DEV_MODE(musb);
- musb->xceiv->otg->default_a = 0;
- musb->xceiv->state = OTG_STATE_B_IDLE;
-
- status = musb_gadget_setup(musb);
+ MUSB_DEV_MODE(musb);
+ musb->xceiv->otg->default_a = 0;
+ musb->xceiv->state = OTG_STATE_B_IDLE;
- dev_dbg(musb->controller, "%s mode, status %d, dev%02x\n",
- is_otg_enabled(musb) ? "OTG" : "PERIPHERAL",
- status,
- musb_readb(musb->mregs, MUSB_DEVCTL));
+ status = musb_gadget_setup(musb);
- }
if (status < 0)
goto fail3;
@@ -2048,28 +2027,13 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
pm_runtime_put(musb->controller);
- dev_info(dev, "USB %s mode controller at %p using %s, IRQ %d\n",
- ({char *s;
- switch (musb->board_mode) {
- case MUSB_HOST: s = "Host"; break;
- case MUSB_PERIPHERAL: s = "Peripheral"; break;
- default: s = "OTG"; break;
- }; s; }),
- ctrl,
- (is_dma_capable() && musb->dma_controller)
- ? "DMA" : "PIO",
- musb->nIrq);
-
return 0;
fail5:
musb_exit_debugfs(musb);
fail4:
- if (!is_otg_enabled(musb) && is_host_enabled(musb))
- usb_remove_hcd(musb_to_hcd(musb));
- else
- musb_gadget_cleanup(musb);
+ musb_gadget_cleanup(musb);
fail3:
pm_runtime_put_sync(musb->controller);
@@ -2096,11 +2060,6 @@ fail0:
/* all implementations (PCI bridge to FPGA, VLYNQ, etc) should just
* bridge to a platform device; this driver then suffices.
*/
-
-#ifndef CONFIG_MUSB_PIO_ONLY
-static u64 *orig_dma_mask;
-#endif
-
static int __devinit musb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -2119,10 +2078,6 @@ static int __devinit musb_probe(struct platform_device *pdev)
return -ENOMEM;
}
-#ifndef CONFIG_MUSB_PIO_ONLY
- /* clobbered by use_dma=n */
- orig_dma_mask = dev->dma_mask;
-#endif
status = musb_init_controller(dev, irq, base);
if (status < 0)
iounmap(base);
@@ -2132,7 +2087,8 @@ static int __devinit musb_probe(struct platform_device *pdev)
static int __devexit musb_remove(struct platform_device *pdev)
{
- struct musb *musb = dev_to_musb(&pdev->dev);
+ struct device *dev = &pdev->dev;
+ struct musb *musb = dev_to_musb(dev);
void __iomem *ctrl_base = musb->ctrl_base;
/* this gets called on rmmod.
@@ -2145,9 +2101,9 @@ static int __devexit musb_remove(struct platform_device *pdev)
musb_free(musb);
iounmap(ctrl_base);
- device_init_wakeup(&pdev->dev, 0);
+ device_init_wakeup(dev, 0);
#ifndef CONFIG_MUSB_PIO_ONLY
- pdev->dev.dma_mask = orig_dma_mask;
+ dma_set_mask(dev, *dev->parent->dma_mask);
#endif
return 0;
}
@@ -2160,11 +2116,9 @@ static void musb_save_context(struct musb *musb)
void __iomem *musb_base = musb->mregs;
void __iomem *epio;
- if (is_host_enabled(musb)) {
- musb->context.frame = musb_readw(musb_base, MUSB_FRAME);
- musb->context.testmode = musb_readb(musb_base, MUSB_TESTMODE);
- musb->context.busctl = musb_read_ulpi_buscontrol(musb->mregs);
- }
+ musb->context.frame = musb_readw(musb_base, MUSB_FRAME);
+ musb->context.testmode = musb_readb(musb_base, MUSB_TESTMODE);
+ musb->context.busctl = musb_read_ulpi_buscontrol(musb->mregs);
musb->context.power = musb_readb(musb_base, MUSB_POWER);
musb->context.intrtxe = musb_readw(musb_base, MUSB_INTRTXE);
musb->context.intrrxe = musb_readw(musb_base, MUSB_INTRRXE);
@@ -2203,30 +2157,29 @@ static void musb_save_context(struct musb *musb)
musb->context.index_regs[i].rxfifosz =
musb_read_rxfifosz(musb_base);
}
- if (is_host_enabled(musb)) {
- musb->context.index_regs[i].txtype =
- musb_readb(epio, MUSB_TXTYPE);
- musb->context.index_regs[i].txinterval =
- musb_readb(epio, MUSB_TXINTERVAL);
- musb->context.index_regs[i].rxtype =
- musb_readb(epio, MUSB_RXTYPE);
- musb->context.index_regs[i].rxinterval =
- musb_readb(epio, MUSB_RXINTERVAL);
-
- musb->context.index_regs[i].txfunaddr =
- musb_read_txfunaddr(musb_base, i);
- musb->context.index_regs[i].txhubaddr =
- musb_read_txhubaddr(musb_base, i);
- musb->context.index_regs[i].txhubport =
- musb_read_txhubport(musb_base, i);
-
- musb->context.index_regs[i].rxfunaddr =
- musb_read_rxfunaddr(musb_base, i);
- musb->context.index_regs[i].rxhubaddr =
- musb_read_rxhubaddr(musb_base, i);
- musb->context.index_regs[i].rxhubport =
- musb_read_rxhubport(musb_base, i);
- }
+
+ musb->context.index_regs[i].txtype =
+ musb_readb(epio, MUSB_TXTYPE);
+ musb->context.index_regs[i].txinterval =
+ musb_readb(epio, MUSB_TXINTERVAL);
+ musb->context.index_regs[i].rxtype =
+ musb_readb(epio, MUSB_RXTYPE);
+ musb->context.index_regs[i].rxinterval =
+ musb_readb(epio, MUSB_RXINTERVAL);
+
+ musb->context.index_regs[i].txfunaddr =
+ musb_read_txfunaddr(musb_base, i);
+ musb->context.index_regs[i].txhubaddr =
+ musb_read_txhubaddr(musb_base, i);
+ musb->context.index_regs[i].txhubport =
+ musb_read_txhubport(musb_base, i);
+
+ musb->context.index_regs[i].rxfunaddr =
+ musb_read_rxfunaddr(musb_base, i);
+ musb->context.index_regs[i].rxhubaddr =
+ musb_read_rxhubaddr(musb_base, i);
+ musb->context.index_regs[i].rxhubport =
+ musb_read_rxhubport(musb_base, i);
}
}
@@ -2237,11 +2190,9 @@ static void musb_restore_context(struct musb *musb)
void __iomem *ep_target_regs;
void __iomem *epio;
- if (is_host_enabled(musb)) {
- musb_writew(musb_base, MUSB_FRAME, musb->context.frame);
- musb_writeb(musb_base, MUSB_TESTMODE, musb->context.testmode);
- musb_write_ulpi_buscontrol(musb->mregs, musb->context.busctl);
- }
+ musb_writew(musb_base, MUSB_FRAME, musb->context.frame);
+ musb_writeb(musb_base, MUSB_TESTMODE, musb->context.testmode);
+ musb_write_ulpi_buscontrol(musb->mregs, musb->context.busctl);
musb_writeb(musb_base, MUSB_POWER, musb->context.power);
musb_writew(musb_base, MUSB_INTRTXE, musb->context.intrtxe);
musb_writew(musb_base, MUSB_INTRRXE, musb->context.intrrxe);
@@ -2280,33 +2231,31 @@ static void musb_restore_context(struct musb *musb)
musb->context.index_regs[i].rxfifoadd);
}
- if (is_host_enabled(musb)) {
- musb_writeb(epio, MUSB_TXTYPE,
+ musb_writeb(epio, MUSB_TXTYPE,
musb->context.index_regs[i].txtype);
- musb_writeb(epio, MUSB_TXINTERVAL,
+ musb_writeb(epio, MUSB_TXINTERVAL,
musb->context.index_regs[i].txinterval);
- musb_writeb(epio, MUSB_RXTYPE,
+ musb_writeb(epio, MUSB_RXTYPE,
musb->context.index_regs[i].rxtype);
- musb_writeb(epio, MUSB_RXINTERVAL,
+ musb_writeb(epio, MUSB_RXINTERVAL,
- musb->context.index_regs[i].rxinterval);
- musb_write_txfunaddr(musb_base, i,
+ musb->context.index_regs[i].rxinterval);
+ musb_write_txfunaddr(musb_base, i,
musb->context.index_regs[i].txfunaddr);
- musb_write_txhubaddr(musb_base, i,
+ musb_write_txhubaddr(musb_base, i,
musb->context.index_regs[i].txhubaddr);
- musb_write_txhubport(musb_base, i,
+ musb_write_txhubport(musb_base, i,
musb->context.index_regs[i].txhubport);
- ep_target_regs =
- musb_read_target_reg_base(i, musb_base);
+ ep_target_regs =
+ musb_read_target_reg_base(i, musb_base);
- musb_write_rxfunaddr(ep_target_regs,
+ musb_write_rxfunaddr(ep_target_regs,
musb->context.index_regs[i].rxfunaddr);
- musb_write_rxhubaddr(ep_target_regs,
+ musb_write_rxhubaddr(ep_target_regs,
musb->context.index_regs[i].rxhubaddr);
- musb_write_rxhubport(ep_target_regs,
+ musb_write_rxhubport(ep_target_regs,
musb->context.index_regs[i].rxhubport);
- }
}
musb_writeb(musb_base, MUSB_INDEX, musb->context.index);
}
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 586105b55a7c..c158aacd6de8 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -71,10 +71,6 @@ struct musb_ep;
#include <linux/usb/hcd.h>
#include "musb_host.h"
-#define is_peripheral_enabled(musb) ((musb)->board_mode != MUSB_HOST)
-#define is_host_enabled(musb) ((musb)->board_mode != MUSB_PERIPHERAL)
-#define is_otg_enabled(musb) ((musb)->board_mode == MUSB_OTG)
-
/* NOTE: otg and peripheral-only state machines start at B_IDLE.
* OTG or host-only go to A_IDLE when ID is sensed.
*/
@@ -88,8 +84,6 @@ struct musb_ep;
/****************************** PERIPHERAL ROLE *****************************/
-#define is_peripheral_capable() (1)
-
extern irqreturn_t musb_g_ep0_irq(struct musb *);
extern void musb_g_tx(struct musb *, u8);
extern void musb_g_rx(struct musb *, u8);
@@ -101,8 +95,6 @@ extern void musb_g_disconnect(struct musb *);
/****************************** HOST ROLE ***********************************/
-#define is_host_capable() (1)
-
extern irqreturn_t musb_h_ep0_irq(struct musb *);
extern void musb_host_tx(struct musb *, u8);
extern void musb_host_rx(struct musb *, u8);
@@ -376,7 +368,6 @@ struct musb {
u16 epmask;
u8 nr_endpoints;
- u8 board_mode; /* enum musb_mode */
int (*board_set_power)(int state);
u8 min_power; /* vbus for periph, in mA/2 */
@@ -446,6 +437,10 @@ struct musb {
#ifdef MUSB_CONFIG_PROC_FS
struct proc_dir_entry *proc_entry;
#endif
+ int xceiv_old_state;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs_root;
+#endif
};
static inline struct musb *gadget_to_musb(struct usb_gadget *g)
@@ -484,7 +479,7 @@ static inline void musb_configure_ep0(struct musb *musb)
static inline int musb_read_fifosize(struct musb *musb,
struct musb_hw_ep *hw_ep, u8 epnum)
{
- void *mbase = musb->mregs;
+ void __iomem *mbase = musb->mregs;
u8 reg = 0;
/* read from core using indexed model */
@@ -526,6 +521,8 @@ extern const char musb_driver_name[];
extern void musb_start(struct musb *musb);
extern void musb_stop(struct musb *musb);
+extern int musb_get_id(struct device *dev, gfp_t gfp_mask);
+extern void musb_put_id(struct device *dev, int id);
extern void musb_write_fifo(struct musb_hw_ep *ep, u16 len, const u8 *src);
extern void musb_read_fifo(struct musb_hw_ep *ep, u16 len, u8 *dst);
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index 40a37c91cc10..1d6e8af94c06 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -103,8 +103,6 @@ static const struct musb_register_map musb_regmap[] = {
{ } /* Terminating Entry */
};
-static struct dentry *musb_debugfs_root;
-
static int musb_regdump_show(struct seq_file *s, void *unused)
{
struct musb *musb = s->private;
@@ -241,7 +239,7 @@ int __devinit musb_init_debugfs(struct musb *musb)
struct dentry *file;
int ret;
- root = debugfs_create_dir("musb", NULL);
+ root = debugfs_create_dir(dev_name(musb->controller), NULL);
if (!root) {
ret = -ENOMEM;
goto err0;
@@ -261,7 +259,7 @@ int __devinit musb_init_debugfs(struct musb *musb)
goto err1;
}
- musb_debugfs_root = root;
+ musb->debugfs_root = root;
return 0;
@@ -274,5 +272,5 @@ err0:
void /* __init_or_exit */ musb_exit_debugfs(struct musb *musb)
{
- debugfs_remove_recursive(musb_debugfs_root);
+ debugfs_remove_recursive(musb->debugfs_root);
}
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 3a97c4e2d4f5..24d39210d4ab 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -178,7 +178,7 @@ struct dma_controller {
extern void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit);
-extern struct dma_controller *__init
+extern struct dma_controller *__devinit
dma_controller_create(struct musb *, void __iomem *);
extern void dma_controller_destroy(struct dma_controller *);
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 494772fc9e23..444346e1e10d 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -31,11 +31,13 @@
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/of.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/module.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -45,6 +47,10 @@
#include "musb_core.h"
+#ifdef CONFIG_OF
+static const struct of_device_id musb_dsps_of_match[];
+#endif
+
/**
* avoid using musb_readx()/musb_writex() as glue layer should not be
* dependent on musb core layer symbols.
@@ -105,6 +111,8 @@ struct dsps_musb_wrapper {
/* miscellaneous stuff */
u32 musb_core_offset;
u8 poll_seconds;
+ /* number of musb instances */
+ u8 instances;
};
/**
@@ -112,9 +120,10 @@ struct dsps_musb_wrapper {
*/
struct dsps_glue {
struct device *dev;
- struct platform_device *musb; /* child musb pdev */
+ struct platform_device *musb[2]; /* child musb pdev */
const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */
- struct timer_list timer; /* otg_workaround timer */
+ struct timer_list timer[2]; /* otg_workaround timer */
+ unsigned long last_timer[2]; /* last timer data for each instance */
};
/**
@@ -137,9 +146,8 @@ static void dsps_musb_enable(struct musb *musb)
dsps_writel(reg_base, wrp->epintr_set, epmask);
dsps_writel(reg_base, wrp->coreintr_set, coremask);
/* Force the DRVVBUS IRQ so we can start polling for ID change. */
- if (is_otg_enabled(musb))
- dsps_writel(reg_base, wrp->coreintr_set,
- (1 << wrp->drvvbus) << wrp->usb_shift);
+ dsps_writel(reg_base, wrp->coreintr_set,
+ (1 << wrp->drvvbus) << wrp->usb_shift);
}
/**
@@ -165,8 +173,8 @@ static void otg_timer(unsigned long _musb)
struct musb *musb = (void *)_musb;
void __iomem *mregs = musb->mregs;
struct device *dev = musb->controller;
- struct platform_device *pdev = to_platform_device(dev->parent);
- struct dsps_glue *glue = platform_get_drvdata(pdev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dsps_glue *glue = dev_get_drvdata(dev->parent);
const struct dsps_musb_wrapper *wrp = glue->wrp;
u8 devctl;
unsigned long flags;
@@ -200,12 +208,9 @@ static void otg_timer(unsigned long _musb)
MUSB_INTR_VBUSERROR << wrp->usb_shift);
break;
case OTG_STATE_B_IDLE:
- if (!is_peripheral_enabled(musb))
- break;
-
devctl = dsps_readb(mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE)
- mod_timer(&glue->timer,
+ mod_timer(&glue->timer[pdev->id],
jiffies + wrp->poll_seconds * HZ);
else
musb->xceiv->state = OTG_STATE_A_IDLE;
@@ -219,12 +224,8 @@ static void otg_timer(unsigned long _musb)
static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
{
struct device *dev = musb->controller;
- struct platform_device *pdev = to_platform_device(dev->parent);
- struct dsps_glue *glue = platform_get_drvdata(pdev);
- static unsigned long last_timer;
-
- if (!is_otg_enabled(musb))
- return;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dsps_glue *glue = dev_get_drvdata(dev->parent);
if (timeout == 0)
timeout = jiffies + msecs_to_jiffies(3);
@@ -234,22 +235,23 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
otg_state_string(musb->xceiv->state));
- del_timer(&glue->timer);
- last_timer = jiffies;
+ del_timer(&glue->timer[pdev->id]);
+ glue->last_timer[pdev->id] = jiffies;
return;
}
- if (time_after(last_timer, timeout) && timer_pending(&glue->timer)) {
+ if (time_after(glue->last_timer[pdev->id], timeout) &&
+ timer_pending(&glue->timer[pdev->id])) {
dev_dbg(musb->controller,
"Longer idle timer already pending, ignoring...\n");
return;
}
- last_timer = timeout;
+ glue->last_timer[pdev->id] = timeout;
dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
otg_state_string(musb->xceiv->state),
jiffies_to_msecs(timeout - jiffies));
- mod_timer(&glue->timer, timeout);
+ mod_timer(&glue->timer[pdev->id], timeout);
}
static irqreturn_t dsps_interrupt(int irq, void *hci)
@@ -257,8 +259,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
struct musb *musb = hci;
void __iomem *reg_base = musb->ctrl_base;
struct device *dev = musb->controller;
- struct platform_device *pdev = to_platform_device(dev->parent);
- struct dsps_glue *glue = platform_get_drvdata(pdev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dsps_glue *glue = dev_get_drvdata(dev->parent);
const struct dsps_musb_wrapper *wrp = glue->wrp;
unsigned long flags;
irqreturn_t ret = IRQ_NONE;
@@ -293,7 +295,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
* value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
* Also, DRVVBUS pulses for SRP (but not at 5V) ...
*/
- if ((usbintr & MUSB_INTR_BABBLE) && is_host_enabled(musb))
+ if (usbintr & MUSB_INTR_BABBLE)
pr_info("CAUTION: musb: Babble Interrupt Occured\n");
if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
@@ -302,8 +304,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
u8 devctl = dsps_readb(mregs, MUSB_DEVCTL);
int err;
- err = is_host_enabled(musb) && (musb->int_usb &
- MUSB_INTR_VBUSERROR);
+ err = musb->int_usb & MUSB_INTR_VBUSERROR;
if (err) {
/*
* The Mentor core doesn't debounce VBUS as needed
@@ -318,15 +319,15 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
- mod_timer(&glue->timer,
+ mod_timer(&glue->timer[pdev->id],
jiffies + wrp->poll_seconds * HZ);
WARNING("VBUS error workaround (delay coming)\n");
- } else if (is_host_enabled(musb) && drvvbus) {
+ } else if (drvvbus) {
musb->is_active = 1;
MUSB_HST_MODE(musb);
musb->xceiv->otg->default_a = 1;
musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
- del_timer(&glue->timer);
+ del_timer(&glue->timer[pdev->id]);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
@@ -352,8 +353,9 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
dsps_writel(reg_base, wrp->eoi, 1);
/* Poll for ID change */
- if (is_otg_enabled(musb) && musb->xceiv->state == OTG_STATE_B_IDLE)
- mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
+ if (musb->xceiv->state == OTG_STATE_B_IDLE)
+ mod_timer(&glue->timer[pdev->id],
+ jiffies + wrp->poll_seconds * HZ);
spin_unlock_irqrestore(&musb->lock, flags);
@@ -364,8 +366,8 @@ static int dsps_musb_init(struct musb *musb)
{
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *plat = dev->platform_data;
- struct platform_device *pdev = to_platform_device(dev->parent);
- struct dsps_glue *glue = platform_get_drvdata(pdev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dsps_glue *glue = dev_get_drvdata(dev->parent);
const struct dsps_musb_wrapper *wrp = glue->wrp;
struct omap_musb_board_data *data = plat->board_data;
void __iomem *reg_base = musb->ctrl_base;
@@ -375,8 +377,7 @@ static int dsps_musb_init(struct musb *musb)
/* mentor core register starts at offset of 0x400 from musb base */
musb->mregs += wrp->musb_core_offset;
- /* NOP driver needs change if supporting dual instance */
- usb_nop_xceiv_register();
+ /* Get the NOP PHY */
musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(musb->xceiv))
return -ENODEV;
@@ -388,8 +389,7 @@ static int dsps_musb_init(struct musb *musb)
goto err0;
}
- if (is_host_enabled(musb))
- setup_timer(&glue->timer, otg_timer, (unsigned long) musb);
+ setup_timer(&glue->timer[pdev->id], otg_timer, (unsigned long) musb);
/* Reset the musb */
dsps_writel(reg_base, wrp->control, (1 << wrp->reset));
@@ -420,11 +420,10 @@ static int dsps_musb_exit(struct musb *musb)
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *plat = dev->platform_data;
struct omap_musb_board_data *data = plat->board_data;
- struct platform_device *pdev = to_platform_device(dev->parent);
- struct dsps_glue *glue = platform_get_drvdata(pdev);
+ struct platform_device *pdev = to_platform_device(dev);
+ struct dsps_glue *glue = dev_get_drvdata(dev->parent);
- if (is_host_enabled(musb))
- del_timer_sync(&glue->timer);
+ del_timer_sync(&glue->timer[pdev->id]);
/* Shutdown the on-chip PHY and its PLL. */
if (data->set_phy_power)
@@ -454,11 +453,13 @@ static int __devinit dsps_create_musb_pdev(struct dsps_glue *glue, u8 id)
struct device *dev = glue->dev;
struct platform_device *pdev = to_platform_device(dev);
struct musb_hdrc_platform_data *pdata = dev->platform_data;
+ struct device_node *np = pdev->dev.of_node;
+ struct musb_hdrc_config *config;
struct platform_device *musb;
struct resource *res;
struct resource resources[2];
char res_name[10];
- int ret;
+ int ret, musbid;
/* get memory resource */
sprintf(res_name, "musb%d", id);
@@ -483,62 +484,107 @@ static int __devinit dsps_create_musb_pdev(struct dsps_glue *glue, u8 id)
resources[1] = *res;
resources[1].name = "mc";
+ /* get the musb id */
+ musbid = musb_get_id(dev, GFP_KERNEL);
+ if (musbid < 0) {
+ dev_err(dev, "failed to allocate musb id\n");
+ ret = -ENOMEM;
+ goto err0;
+ }
/* allocate the child platform device */
- musb = platform_device_alloc("musb-hdrc", -1);
+ musb = platform_device_alloc("musb-hdrc", musbid);
if (!musb) {
dev_err(dev, "failed to allocate musb device\n");
ret = -ENOMEM;
- goto err0;
+ goto err1;
}
+ musb->id = musbid;
musb->dev.parent = dev;
musb->dev.dma_mask = &musb_dmamask;
musb->dev.coherent_dma_mask = musb_dmamask;
- glue->musb = musb;
-
- pdata->platform_ops = &dsps_ops;
+ glue->musb[id] = musb;
ret = platform_device_add_resources(musb, resources, 2);
if (ret) {
dev_err(dev, "failed to add resources\n");
- goto err1;
+ goto err2;
}
+ if (np) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev,
+ "failed to allocate musb platfrom data\n");
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
+ if (!config) {
+ dev_err(&pdev->dev,
+ "failed to allocate musb hdrc config\n");
+ goto err2;
+ }
+
+ of_property_read_u32(np, "num-eps", (u32 *)&config->num_eps);
+ of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits);
+ sprintf(res_name, "port%d-mode", id);
+ of_property_read_u32(np, res_name, (u32 *)&pdata->mode);
+ of_property_read_u32(np, "power", (u32 *)&pdata->power);
+ config->multipoint = of_property_read_bool(np, "multipoint");
+
+ pdata->config = config;
+ }
+
+ pdata->platform_ops = &dsps_ops;
+
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(dev, "failed to add platform_data\n");
- goto err1;
+ goto err2;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(dev, "failed to register musb device\n");
- goto err1;
+ goto err2;
}
return 0;
-err1:
+err2:
platform_device_put(musb);
+err1:
+ musb_put_id(dev, musbid);
err0:
return ret;
}
-static void __devexit dsps_delete_musb_pdev(struct dsps_glue *glue)
+static void dsps_delete_musb_pdev(struct dsps_glue *glue, u8 id)
{
- platform_device_del(glue->musb);
- platform_device_put(glue->musb);
+ musb_put_id(glue->dev, glue->musb[id]->id);
+ platform_device_del(glue->musb[id]);
+ platform_device_put(glue->musb[id]);
}
static int __devinit dsps_probe(struct platform_device *pdev)
{
- const struct platform_device_id *id = platform_get_device_id(pdev);
- const struct dsps_musb_wrapper *wrp =
- (struct dsps_musb_wrapper *)id->driver_data;
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *match;
+ const struct dsps_musb_wrapper *wrp;
struct dsps_glue *glue;
struct resource *iomem;
- int ret;
+ int ret, i;
+
+ match = of_match_node(musb_dsps_of_match, np);
+ if (!match) {
+ dev_err(&pdev->dev, "fail to get matching of_match struct\n");
+ ret = -EINVAL;
+ goto err0;
+ }
+ wrp = match->data;
/* allocate glue */
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
@@ -575,11 +621,16 @@ static int __devinit dsps_probe(struct platform_device *pdev)
goto err2;
}
- /* create the child platform device for first instances of musb */
- ret = dsps_create_musb_pdev(glue, 0);
- if (ret != 0) {
- dev_err(&pdev->dev, "failed to create child pdev\n");
- goto err3;
+ /* create the child platform device for all instances of musb */
+ for (i = 0; i < wrp->instances ; i++) {
+ ret = dsps_create_musb_pdev(glue, i);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "failed to create child pdev\n");
+ /* release resources of previously created instances */
+ for (i--; i >= 0 ; i--)
+ dsps_delete_musb_pdev(glue, i);
+ goto err3;
+ }
}
return 0;
@@ -597,9 +648,12 @@ err0:
static int __devexit dsps_remove(struct platform_device *pdev)
{
struct dsps_glue *glue = platform_get_drvdata(pdev);
+ const struct dsps_musb_wrapper *wrp = glue->wrp;
+ int i;
/* delete the child platform device */
- dsps_delete_musb_pdev(glue);
+ for (i = 0; i < wrp->instances ; i++)
+ dsps_delete_musb_pdev(glue, i);
/* disable usbss clocks */
pm_runtime_put(&pdev->dev);
@@ -665,6 +719,7 @@ static const struct dsps_musb_wrapper ti81xx_driver_data __devinitconst = {
.rxep_bitmap = (0xfffe << 16),
.musb_core_offset = 0x400,
.poll_seconds = 2,
+ .instances = 2,
};
static const struct platform_device_id musb_dsps_id_table[] __devinitconst = {
@@ -676,13 +731,14 @@ static const struct platform_device_id musb_dsps_id_table[] __devinitconst = {
};
MODULE_DEVICE_TABLE(platform, musb_dsps_id_table);
+#ifdef CONFIG_OF
static const struct of_device_id musb_dsps_of_match[] __devinitconst = {
- { .compatible = "musb-ti81xx", },
- { .compatible = "ti,ti81xx-musb", },
- { .compatible = "ti,am335x-musb", },
+ { .compatible = "ti,musb-am33xx",
+ .data = (void *) &ti81xx_driver_data, },
{ },
};
MODULE_DEVICE_TABLE(of, musb_dsps_of_match);
+#endif
static struct platform_driver dsps_usbss_driver = {
.probe = dsps_probe,
@@ -690,7 +746,7 @@ static struct platform_driver dsps_usbss_driver = {
.driver = {
.name = "musb-dsps",
.pm = &dsps_pm_ops,
- .of_match_table = musb_dsps_of_match,
+ .of_match_table = of_match_ptr(musb_dsps_of_match),
},
.id_table = musb_dsps_id_table,
};
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index f7194cf65aba..d0b87e7b4abf 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -373,7 +373,7 @@ static void txstate(struct musb *musb, struct musb_request *req)
request_size = min_t(size_t, request->length - request->actual,
musb_ep->dma->max_len);
- use_dma = (request->dma != DMA_ADDR_INVALID);
+ use_dma = (request->dma != DMA_ADDR_INVALID && request_size);
/* MUSB_TXCSR_P_ISO is still set correctly */
@@ -644,8 +644,8 @@ static void rxstate(struct musb *musb, struct musb_request *req)
struct usb_request *request = &req->request;
struct musb_ep *musb_ep;
void __iomem *epio = musb->endpoints[epnum].regs;
- unsigned fifo_count = 0;
- u16 len;
+ unsigned len = 0;
+ u16 fifo_count;
u16 csr = musb_readw(epio, MUSB_RXCSR);
struct musb_hw_ep *hw_ep = &musb->endpoints[epnum];
u8 use_mode_1;
@@ -655,7 +655,7 @@ static void rxstate(struct musb *musb, struct musb_request *req)
else
musb_ep = &hw_ep->ep_out;
- len = musb_ep->packet_sz;
+ fifo_count = musb_ep->packet_sz;
/* Check if EP is disabled */
if (!musb_ep->desc) {
@@ -704,15 +704,14 @@ static void rxstate(struct musb *musb, struct musb_request *req)
}
if (csr & MUSB_RXCSR_RXPKTRDY) {
- len = musb_readw(epio, MUSB_RXCOUNT);
+ fifo_count = musb_readw(epio, MUSB_RXCOUNT);
/*
- * Enable Mode 1 on RX transfers only when short_not_ok flag
- * is set. Currently short_not_ok flag is set only from
- * file_storage and f_mass_storage drivers
+ * use mode 1 only if we expect data of at least ep packet_sz
+ * and have not yet received a short packet
*/
-
- if (request->short_not_ok && len == musb_ep->packet_sz)
+ if ((request->length - request->actual >= musb_ep->packet_sz) &&
+ (fifo_count >= musb_ep->packet_sz))
use_mode_1 = 1;
else
use_mode_1 = 0;
@@ -723,31 +722,11 @@ static void rxstate(struct musb *musb, struct musb_request *req)
struct dma_controller *c;
struct dma_channel *channel;
int use_dma = 0;
+ int transfer_size;
c = musb->dma_controller;
channel = musb_ep->dma;
- /* We use DMA Req mode 0 in rx_csr, and DMA controller operates in
- * mode 0 only. So we do not get endpoint interrupts due to DMA
- * completion. We only get interrupts from DMA controller.
- *
- * We could operate in DMA mode 1 if we knew the size of the tranfer
- * in advance. For mass storage class, request->length = what the host
- * sends, so that'd work. But for pretty much everything else,
- * request->length is routinely more than what the host sends. For
- * most these gadgets, end of is signified either by a short packet,
- * or filling the last byte of the buffer. (Sending extra data in
- * that last pckate should trigger an overflow fault.) But in mode 1,
- * we don't get DMA completion interrupt for short packets.
- *
- * Theoretically, we could enable DMAReq irq (MUSB_RXCSR_DMAMODE = 1),
- * to get endpoint interrupt on every DMA req, but that didn't seem
- * to work reliably.
- *
- * REVISIT an updated g_file_storage can set req->short_not_ok, which
- * then becomes usable as a runtime "use mode 1" hint...
- */
-
/* Experimental: Mode1 works with mass storage use cases */
if (use_mode_1) {
csr |= MUSB_RXCSR_AUTOCLEAR;
@@ -764,35 +743,30 @@ static void rxstate(struct musb *musb, struct musb_request *req)
csr | MUSB_RXCSR_DMAMODE);
musb_writew(epio, MUSB_RXCSR, csr);
+ transfer_size = min(request->length - request->actual,
+ channel->max_len);
+ musb_ep->dma->desired_mode = 1;
+
} else {
if (!musb_ep->hb_mult &&
musb_ep->hw_ep->rx_double_buffered)
csr |= MUSB_RXCSR_AUTOCLEAR;
csr |= MUSB_RXCSR_DMAENAB;
musb_writew(epio, MUSB_RXCSR, csr);
- }
- if (request->actual < request->length) {
- int transfer_size = 0;
- if (use_mode_1) {
- transfer_size = min(request->length - request->actual,
- channel->max_len);
- musb_ep->dma->desired_mode = 1;
- } else {
- transfer_size = min(request->length - request->actual,
- (unsigned)len);
- musb_ep->dma->desired_mode = 0;
- }
-
- use_dma = c->channel_program(
- channel,
- musb_ep->packet_sz,
- channel->desired_mode,
- request->dma
- + request->actual,
- transfer_size);
+ transfer_size = min(request->length - request->actual,
+ (unsigned)fifo_count);
+ musb_ep->dma->desired_mode = 0;
}
+ use_dma = c->channel_program(
+ channel,
+ musb_ep->packet_sz,
+ channel->desired_mode,
+ request->dma
+ + request->actual,
+ transfer_size);
+
if (use_dma)
return;
}
@@ -808,8 +782,8 @@ static void rxstate(struct musb *musb, struct musb_request *req)
channel = musb_ep->dma;
/* In case first packet is short */
- if (len < musb_ep->packet_sz)
- transfer_size = len;
+ if (fifo_count < musb_ep->packet_sz)
+ transfer_size = fifo_count;
else if (request->short_not_ok)
transfer_size = min(request->length -
request->actual,
@@ -817,7 +791,7 @@ static void rxstate(struct musb *musb, struct musb_request *req)
else
transfer_size = min(request->length -
request->actual,
- (unsigned)len);
+ (unsigned)fifo_count);
csr &= ~MUSB_RXCSR_DMAMODE;
csr |= (MUSB_RXCSR_DMAENAB |
@@ -845,10 +819,10 @@ static void rxstate(struct musb *musb, struct musb_request *req)
}
#endif /* Mentor's DMA */
- fifo_count = request->length - request->actual;
+ len = request->length - request->actual;
dev_dbg(musb->controller, "%s OUT/RX pio fifo %d/%d, maxpacket %d\n",
musb_ep->end_point.name,
- len, fifo_count,
+ fifo_count, len,
musb_ep->packet_sz);
fifo_count = min_t(unsigned, len, fifo_count);
@@ -901,7 +875,8 @@ static void rxstate(struct musb *musb, struct musb_request *req)
}
/* reach the end or short packet detected */
- if (request->actual == request->length || len < musb_ep->packet_sz)
+ if (request->actual == request->length ||
+ fifo_count < musb_ep->packet_sz)
musb_g_giveback(musb_ep, request, 0);
}
@@ -1885,8 +1860,7 @@ int __devinit musb_gadget_setup(struct musb *musb)
musb->g.dev.release = musb_gadget_release;
musb->g.name = musb_driver_name;
- if (is_otg_enabled(musb))
- musb->g.is_otg = 1;
+ musb->g.is_otg = 1;
musb_g_init_endpoints(musb);
@@ -1932,11 +1906,14 @@ static int musb_gadget_start(struct usb_gadget *g,
{
struct musb *musb = gadget_to_musb(g);
struct usb_otg *otg = musb->xceiv->otg;
+ struct usb_hcd *hcd = musb_to_hcd(musb);
unsigned long flags;
- int retval = -EINVAL;
+ int retval = 0;
- if (driver->max_speed < USB_SPEED_HIGH)
- goto err0;
+ if (driver->max_speed < USB_SPEED_HIGH) {
+ retval = -EINVAL;
+ goto err;
+ }
pm_runtime_get_sync(musb->controller);
@@ -1950,49 +1927,30 @@ static int musb_gadget_start(struct usb_gadget *g,
otg_set_peripheral(otg, &musb->g);
musb->xceiv->state = OTG_STATE_B_IDLE;
-
- /*
- * FIXME this ignores the softconnect flag. Drivers are
- * allowed hold the peripheral inactive until for example
- * userspace hooks up printer hardware or DSP codecs, so
- * hosts only see fully functional devices.
- */
-
- if (!is_otg_enabled(musb))
- musb_start(musb);
-
spin_unlock_irqrestore(&musb->lock, flags);
- if (is_otg_enabled(musb)) {
- struct usb_hcd *hcd = musb_to_hcd(musb);
-
- dev_dbg(musb->controller, "OTG startup...\n");
+ /* REVISIT: funcall to other code, which also
+ * handles power budgeting ... this way also
+ * ensures HdrcStart is indirectly called.
+ */
+ retval = usb_add_hcd(hcd, 0, 0);
+ if (retval < 0) {
+ dev_dbg(musb->controller, "add_hcd failed, %d\n", retval);
+ goto err;
+ }
- /* REVISIT: funcall to other code, which also
- * handles power budgeting ... this way also
- * ensures HdrcStart is indirectly called.
- */
- retval = usb_add_hcd(musb_to_hcd(musb), 0, 0);
- if (retval < 0) {
- dev_dbg(musb->controller, "add_hcd failed, %d\n", retval);
- goto err2;
- }
+ if ((musb->xceiv->last_event == USB_EVENT_ID)
+ && otg->set_vbus)
+ otg_set_vbus(otg, 1);
- if ((musb->xceiv->last_event == USB_EVENT_ID)
- && otg->set_vbus)
- otg_set_vbus(otg, 1);
+ hcd->self.uses_pio_for_control = 1;
- hcd->self.uses_pio_for_control = 1;
- }
if (musb->xceiv->last_event == USB_EVENT_NONE)
pm_runtime_put(musb->controller);
return 0;
-err2:
- if (!is_otg_enabled(musb))
- musb_stop(musb);
-err0:
+err:
return retval;
}
@@ -2070,16 +2028,12 @@ static int musb_gadget_stop(struct usb_gadget *g,
musb_platform_try_idle(musb, 0);
spin_unlock_irqrestore(&musb->lock, flags);
- if (is_otg_enabled(musb)) {
- usb_remove_hcd(musb_to_hcd(musb));
- /* FIXME we need to be able to register another
- * gadget driver here and have everything work;
- * that currently misbehaves.
- */
- }
-
- if (!is_otg_enabled(musb))
- musb_stop(musb);
+ usb_remove_hcd(musb_to_hcd(musb));
+ /*
+ * FIXME we need to be able to register another
+ * gadget driver here and have everything work;
+ * that currently misbehaves.
+ */
pm_runtime_put(musb->controller);
@@ -2241,13 +2195,11 @@ __acquires(musb->lock)
if (devctl & MUSB_DEVCTL_BDEVICE) {
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
musb->g.is_a_peripheral = 0;
- } else if (is_otg_enabled(musb)) {
+ } else {
musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
musb->g.is_a_peripheral = 1;
- } else
- WARN_ON(1);
+ }
/* start with default limits on VBUS power draw */
- (void) musb_gadget_vbus_draw(&musb->g,
- is_otg_enabled(musb) ? 8 : 100);
+ (void) musb_gadget_vbus_draw(&musb->g, 8);
}
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 1ae378d5fc6f..3df6a76b851d 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -693,6 +693,8 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
void __iomem *epio = hw_ep->regs;
struct musb_qh *qh = musb_ep_get_qh(hw_ep, !is_out);
u16 packet_sz = qh->maxpacket;
+ u8 use_dma = 1;
+ u16 csr;
dev_dbg(musb->controller, "%s hw%d urb %p spd%d dev%d ep%d%s "
"h_addr%02x h_port%02x bytes %d\n",
@@ -704,9 +706,17 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
musb_ep_select(mbase, epnum);
+ if (is_out && !len) {
+ use_dma = 0;
+ csr = musb_readw(epio, MUSB_TXCSR);
+ csr &= ~MUSB_TXCSR_DMAENAB;
+ musb_writew(epio, MUSB_TXCSR, csr);
+ hw_ep->tx_channel = NULL;
+ }
+
/* candidate for DMA? */
dma_controller = musb->dma_controller;
- if (is_dma_capable() && epnum && dma_controller) {
+ if (use_dma && is_dma_capable() && epnum && dma_controller) {
dma_channel = is_out ? hw_ep->tx_channel : hw_ep->rx_channel;
if (!dma_channel) {
dma_channel = dma_controller->channel_alloc(
@@ -813,9 +823,28 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
if (load_count) {
/* PIO to load FIFO */
qh->segsize = load_count;
- musb_write_fifo(hw_ep, load_count, buf);
+ if (!buf) {
+ sg_miter_start(&qh->sg_miter, urb->sg, 1,
+ SG_MITER_ATOMIC
+ | SG_MITER_FROM_SG);
+ if (!sg_miter_next(&qh->sg_miter)) {
+ dev_err(musb->controller,
+ "error: sg"
+ "list empty\n");
+ sg_miter_stop(&qh->sg_miter);
+ goto finish;
+ }
+ buf = qh->sg_miter.addr + urb->sg->offset +
+ urb->actual_length;
+ load_count = min_t(u32, load_count,
+ qh->sg_miter.length);
+ musb_write_fifo(hw_ep, load_count, buf);
+ qh->sg_miter.consumed = load_count;
+ sg_miter_stop(&qh->sg_miter);
+ } else
+ musb_write_fifo(hw_ep, load_count, buf);
}
-
+finish:
/* re-enable interrupt */
musb_writew(mbase, MUSB_INTRTXE, int_txe);
@@ -882,6 +911,73 @@ static void musb_ep_program(struct musb *musb, u8 epnum,
}
}
+/* Schedule next QH from musb->in_bulk/out_bulk and move the current qh to
+ * the end; avoids starvation for other endpoints.
+ */
+static void musb_bulk_nak_timeout(struct musb *musb, struct musb_hw_ep *ep,
+ int is_in)
+{
+ struct dma_channel *dma;
+ struct urb *urb;
+ void __iomem *mbase = musb->mregs;
+ void __iomem *epio = ep->regs;
+ struct musb_qh *cur_qh, *next_qh;
+ u16 rx_csr, tx_csr;
+
+ musb_ep_select(mbase, ep->epnum);
+ if (is_in) {
+ dma = is_dma_capable() ? ep->rx_channel : NULL;
+
+ /* clear nak timeout bit */
+ rx_csr = musb_readw(epio, MUSB_RXCSR);
+ rx_csr |= MUSB_RXCSR_H_WZC_BITS;
+ rx_csr &= ~MUSB_RXCSR_DATAERROR;
+ musb_writew(epio, MUSB_RXCSR, rx_csr);
+
+ cur_qh = first_qh(&musb->in_bulk);
+ } else {
+ dma = is_dma_capable() ? ep->tx_channel : NULL;
+
+ /* clear nak timeout bit */
+ tx_csr = musb_readw(epio, MUSB_TXCSR);
+ tx_csr |= MUSB_TXCSR_H_WZC_BITS;
+ tx_csr &= ~MUSB_TXCSR_H_NAKTIMEOUT;
+ musb_writew(epio, MUSB_TXCSR, tx_csr);
+
+ cur_qh = first_qh(&musb->out_bulk);
+ }
+ if (cur_qh) {
+ urb = next_urb(cur_qh);
+ if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
+ dma->status = MUSB_DMA_STATUS_CORE_ABORT;
+ musb->dma_controller->channel_abort(dma);
+ urb->actual_length += dma->actual_len;
+ dma->actual_len = 0L;
+ }
+ musb_save_toggle(cur_qh, is_in, urb);
+
+ if (is_in) {
+ /* move cur_qh to end of queue */
+ list_move_tail(&cur_qh->ring, &musb->in_bulk);
+
+ /* get the next qh from musb->in_bulk */
+ next_qh = first_qh(&musb->in_bulk);
+
+ /* set rx_reinit and schedule the next qh */
+ ep->rx_reinit = 1;
+ } else {
+ /* move cur_qh to end of queue */
+ list_move_tail(&cur_qh->ring, &musb->out_bulk);
+
+ /* get the next qh from musb->out_bulk */
+ next_qh = first_qh(&musb->out_bulk);
+
+ /* set tx_reinit and schedule the next qh */
+ ep->tx_reinit = 1;
+ }
+ musb_start_urb(musb, is_in, next_qh);
+ }
+}
/*
* Service the default endpoint (ep0) as host.
@@ -1116,6 +1212,7 @@ void musb_host_tx(struct musb *musb, u8 epnum)
void __iomem *mbase = musb->mregs;
struct dma_channel *dma;
bool transfer_pending = false;
+ static bool use_sg;
musb_ep_select(mbase, epnum);
tx_csr = musb_readw(epio, MUSB_TXCSR);
@@ -1146,23 +1243,31 @@ void musb_host_tx(struct musb *musb, u8 epnum)
status = -ETIMEDOUT;
} else if (tx_csr & MUSB_TXCSR_H_NAKTIMEOUT) {
- dev_dbg(musb->controller, "TX end=%d device not responding\n", epnum);
-
- /* NOTE: this code path would be a good place to PAUSE a
- * transfer, if there's some other (nonperiodic) tx urb
- * that could use this fifo. (dma complicates it...)
- * That's already done for bulk RX transfers.
- *
- * if (bulk && qh->ring.next != &musb->out_bulk), then
- * we have a candidate... NAKing is *NOT* an error
- */
- musb_ep_select(mbase, epnum);
- musb_writew(epio, MUSB_TXCSR,
- MUSB_TXCSR_H_WZC_BITS
- | MUSB_TXCSR_TXPKTRDY);
- return;
+ if (USB_ENDPOINT_XFER_BULK == qh->type && qh->mux == 1
+ && !list_is_singular(&musb->out_bulk)) {
+ dev_dbg(musb->controller,
+ "NAK timeout on TX%d ep\n", epnum);
+ musb_bulk_nak_timeout(musb, hw_ep, 0);
+ } else {
+ dev_dbg(musb->controller,
+ "TX end=%d device not responding\n", epnum);
+ /* NOTE: this code path would be a good place to PAUSE a
+ * transfer, if there's some other (nonperiodic) tx urb
+ * that could use this fifo. (dma complicates it...)
+ * That's already done for bulk RX transfers.
+ *
+ * if (bulk && qh->ring.next != &musb->out_bulk), then
+ * we have a candidate... NAKing is *NOT* an error
+ */
+ musb_ep_select(mbase, epnum);
+ musb_writew(epio, MUSB_TXCSR,
+ MUSB_TXCSR_H_WZC_BITS
+ | MUSB_TXCSR_TXPKTRDY);
+ }
+ return;
}
+done:
if (status) {
if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
dma->status = MUSB_DMA_STATUS_CORE_ABORT;
@@ -1332,9 +1437,38 @@ void musb_host_tx(struct musb *musb, u8 epnum)
length = qh->maxpacket;
/* Unmap the buffer so that CPU can use it */
usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb);
- musb_write_fifo(hw_ep, length, urb->transfer_buffer + offset);
+
+ /*
+ * We need to map sg if the transfer_buffer is
+ * NULL.
+ */
+ if (!urb->transfer_buffer)
+ use_sg = true;
+
+ if (use_sg) {
+ /* sg_miter_start is already done in musb_ep_program */
+ if (!sg_miter_next(&qh->sg_miter)) {
+ dev_err(musb->controller, "error: sg list empty\n");
+ sg_miter_stop(&qh->sg_miter);
+ status = -EINVAL;
+ goto done;
+ }
+ urb->transfer_buffer = qh->sg_miter.addr;
+ length = min_t(u32, length, qh->sg_miter.length);
+ musb_write_fifo(hw_ep, length, urb->transfer_buffer);
+ qh->sg_miter.consumed = length;
+ sg_miter_stop(&qh->sg_miter);
+ } else {
+ musb_write_fifo(hw_ep, length, urb->transfer_buffer + offset);
+ }
+
qh->segsize = length;
+ if (use_sg) {
+ if (offset + length >= urb->transfer_buffer_length)
+ use_sg = false;
+ }
+
musb_ep_select(mbase, epnum);
musb_writew(epio, MUSB_TXCSR,
MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY);
@@ -1380,50 +1514,6 @@ void musb_host_tx(struct musb *musb, u8 epnum)
#endif
-/* Schedule next QH from musb->in_bulk and move the current qh to
- * the end; avoids starvation for other endpoints.
- */
-static void musb_bulk_rx_nak_timeout(struct musb *musb, struct musb_hw_ep *ep)
-{
- struct dma_channel *dma;
- struct urb *urb;
- void __iomem *mbase = musb->mregs;
- void __iomem *epio = ep->regs;
- struct musb_qh *cur_qh, *next_qh;
- u16 rx_csr;
-
- musb_ep_select(mbase, ep->epnum);
- dma = is_dma_capable() ? ep->rx_channel : NULL;
-
- /* clear nak timeout bit */
- rx_csr = musb_readw(epio, MUSB_RXCSR);
- rx_csr |= MUSB_RXCSR_H_WZC_BITS;
- rx_csr &= ~MUSB_RXCSR_DATAERROR;
- musb_writew(epio, MUSB_RXCSR, rx_csr);
-
- cur_qh = first_qh(&musb->in_bulk);
- if (cur_qh) {
- urb = next_urb(cur_qh);
- if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
- dma->status = MUSB_DMA_STATUS_CORE_ABORT;
- musb->dma_controller->channel_abort(dma);
- urb->actual_length += dma->actual_len;
- dma->actual_len = 0L;
- }
- musb_save_toggle(cur_qh, 1, urb);
-
- /* move cur_qh to end of queue */
- list_move_tail(&cur_qh->ring, &musb->in_bulk);
-
- /* get the next qh from musb->in_bulk */
- next_qh = first_qh(&musb->in_bulk);
-
- /* set rx_reinit and schedule the next qh */
- ep->rx_reinit = 1;
- musb_start_urb(musb, 1, next_qh);
- }
-}
-
/*
* Service an RX interrupt for the given IN endpoint; docs cover bulk, iso,
* and high-bandwidth IN transfer cases.
@@ -1442,6 +1532,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
bool done = false;
u32 status;
struct dma_channel *dma;
+ static bool use_sg;
+ unsigned int sg_flags = SG_MITER_ATOMIC | SG_MITER_TO_SG;
musb_ep_select(mbase, epnum);
@@ -1500,7 +1592,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
if (usb_pipebulk(urb->pipe)
&& qh->mux == 1
&& !list_is_singular(&musb->in_bulk)) {
- musb_bulk_rx_nak_timeout(musb, hw_ep);
+ musb_bulk_nak_timeout(musb, hw_ep, 1);
return;
}
musb_ep_select(mbase, epnum);
@@ -1756,10 +1848,43 @@ void musb_host_rx(struct musb *musb, u8 epnum)
#endif /* Mentor DMA */
if (!dma) {
+ unsigned int received_len;
+
/* Unmap the buffer so that CPU can use it */
usb_hcd_unmap_urb_for_dma(musb_to_hcd(musb), urb);
- done = musb_host_packet_rx(musb, urb,
- epnum, iso_err);
+
+ /*
+ * We need to map sg if the transfer_buffer is
+ * NULL.
+ */
+ if (!urb->transfer_buffer) {
+ use_sg = true;
+ sg_miter_start(&qh->sg_miter, urb->sg, 1,
+ sg_flags);
+ }
+
+ if (use_sg) {
+ if (!sg_miter_next(&qh->sg_miter)) {
+ dev_err(musb->controller, "error: sg list empty\n");
+ sg_miter_stop(&qh->sg_miter);
+ status = -EINVAL;
+ done = true;
+ goto finish;
+ }
+ urb->transfer_buffer = qh->sg_miter.addr;
+ received_len = urb->actual_length;
+ qh->offset = 0x0;
+ done = musb_host_packet_rx(musb, urb, epnum,
+ iso_err);
+ /* Calculate the number of bytes received */
+ received_len = urb->actual_length -
+ received_len;
+ qh->sg_miter.consumed = received_len;
+ sg_miter_stop(&qh->sg_miter);
+ } else {
+ done = musb_host_packet_rx(musb, urb,
+ epnum, iso_err);
+ }
dev_dbg(musb->controller, "read %spacket\n", done ? "last " : "");
}
}
@@ -1768,6 +1893,9 @@ finish:
urb->actual_length += xfer_len;
qh->offset += xfer_len;
if (done) {
+ if (use_sg)
+ use_sg = false;
+
if (urb->status == -EINPROGRESS)
urb->status = status;
musb_advance_schedule(musb, urb, hw_ep, USB_DIR_IN);
@@ -1863,14 +1991,14 @@ static int musb_schedule(
else
head = &musb->out_bulk;
- /* Enable bulk RX NAK timeout scheme when bulk requests are
+ /* Enable bulk RX/TX NAK timeout scheme when bulk requests are
* multiplexed. This scheme doen't work in high speed to full
* speed scenario as NAK interrupts are not coming from a
* full speed device connected to a high speed device.
* NAK timeout interval is 8 (128 uframe or 16ms) for HS and
* 4 (8 frame or 8ms) for FS device.
*/
- if (is_in && qh->dev)
+ if (qh->dev)
qh->intv_reg =
(USB_SPEED_HIGH == qh->dev->speed) ? 8 : 4;
goto success;
diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h
index 622d09fb9aba..5a9c8feec10c 100644
--- a/drivers/usb/musb/musb_host.h
+++ b/drivers/usb/musb/musb_host.h
@@ -35,6 +35,8 @@
#ifndef _MUSB_HOST_H
#define _MUSB_HOST_H
+#include <linux/scatterlist.h>
+
static inline struct usb_hcd *musb_to_hcd(struct musb *musb)
{
return container_of((void *) musb, struct usb_hcd, hcd_priv);
@@ -71,6 +73,7 @@ struct musb_qh {
u16 maxpacket;
u16 frame; /* for periodic schedule */
unsigned iso_idx; /* in urb->iso_frame_desc[] */
+ struct sg_mapping_iter sg_miter; /* for highmem in PIO mode */
};
/* map from control or bulk queue head to the first qh on that ring */
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 22ec3e379980..f70579154ded 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -81,8 +81,7 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
switch (musb->xceiv->state) {
case OTG_STATE_A_HOST:
musb->xceiv->state = OTG_STATE_A_SUSPEND;
- musb->is_active = is_otg_enabled(musb)
- && otg->host->b_hnp_enable;
+ musb->is_active = otg->host->b_hnp_enable;
if (musb->is_active)
mod_timer(&musb->otg_timer, jiffies
+ msecs_to_jiffies(
@@ -91,8 +90,7 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
break;
case OTG_STATE_B_HOST:
musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
- musb->is_active = is_otg_enabled(musb)
- && otg->host->b_hnp_enable;
+ musb->is_active = otg->host->b_hnp_enable;
musb_platform_try_idle(musb, 0);
break;
default:
@@ -190,8 +188,7 @@ void musb_root_disconnect(struct musb *musb)
switch (musb->xceiv->state) {
case OTG_STATE_A_SUSPEND:
- if (is_otg_enabled(musb)
- && otg->host->b_hnp_enable) {
+ if (otg->host->b_hnp_enable) {
musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
musb->g.is_a_peripheral = 1;
break;
@@ -273,7 +270,7 @@ int musb_hub_control(
musb_port_suspend(musb, false);
break;
case USB_PORT_FEAT_POWER:
- if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
+ if (!hcd->self.is_b_host)
musb_platform_set_vbus(musb, 0);
break;
case USB_PORT_FEAT_C_CONNECTION:
@@ -369,7 +366,7 @@ int musb_hub_control(
* initialization logic, e.g. for OTG, or change any
* logic relating to VBUS power-up.
*/
- if (!(is_otg_enabled(musb) && hcd->self.is_b_host))
+ if (!hcd->self.is_b_host)
musb_start(musb);
break;
case USB_PORT_FEAT_RESET:
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index c1be687e00ec..0fc6ca6bc60a 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -380,7 +380,7 @@ void dma_controller_destroy(struct dma_controller *c)
kfree(controller);
}
-struct dma_controller *__init
+struct dma_controller *__devinit
dma_controller_create(struct musb *musb, void __iomem *base)
{
struct musb_dma_controller *controller;
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index 5fdb9da8dd56..a538fe17a966 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -30,10 +30,12 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/io.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <linux/err.h>
+#include <linux/delay.h>
#include <linux/usb/musb-omap.h>
#include "musb_core.h"
@@ -44,6 +46,7 @@ struct omap2430_glue {
struct platform_device *musb;
enum omap_musb_vbus_id_status status;
struct work_struct omap_musb_mailbox_work;
+ u32 __iomem *control_otghs;
};
#define glue_to_musb(g) platform_get_drvdata(g->musb)
@@ -51,6 +54,26 @@ struct omap2430_glue *_glue;
static struct timer_list musb_idle_timer;
+/**
+ * omap4_usb_phy_mailbox - write to usb otg mailbox
+ * @glue: struct omap2430_glue *
+ * @val: the value to be written to the mailbox
+ *
+ * On detection of a device (ID pin is grounded), this API should be called
+ * to set AVALID, VBUSVALID and ID pin is grounded.
+ *
+ * When OMAP is connected to a host (OMAP in device mode), this API
+ * is called to set AVALID, VBUSVALID and ID pin in high impedance.
+ *
+ * XXX: This function will be removed once we have a seperate driver for
+ * control module
+ */
+static void omap4_usb_phy_mailbox(struct omap2430_glue *glue, u32 val)
+{
+ if (glue->control_otghs)
+ writel(val, glue->control_otghs);
+}
+
static void musb_do_idle(unsigned long _musb)
{
struct musb *musb = (void *)_musb;
@@ -140,7 +163,6 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
struct usb_otg *otg = musb->xceiv->otg;
u8 devctl;
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
- int ret = 1;
/* HDRC controls CPEN, but beware current surges during device
* connect. They can trigger transient overcurrent conditions
* that must be ignored.
@@ -150,6 +172,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
if (is_on) {
if (musb->xceiv->state == OTG_STATE_A_IDLE) {
+ int loops = 100;
/* start the session */
devctl |= MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
@@ -159,17 +182,18 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
*/
while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) {
+ mdelay(5);
cpu_relax();
- if (time_after(jiffies, timeout)) {
+ if (time_after(jiffies, timeout)
+ || loops-- <= 0) {
dev_err(musb->controller,
"configured as A device timeout");
- ret = -EINVAL;
break;
}
}
- if (ret && otg->set_vbus)
+ if (otg->set_vbus)
otg_set_vbus(otg, 1);
} else {
musb->is_active = 1;
@@ -245,6 +269,7 @@ EXPORT_SYMBOL_GPL(omap_musb_mailbox);
static void omap_musb_set_mailbox(struct omap2430_glue *glue)
{
+ u32 val;
struct musb *musb = glue_to_musb(glue);
struct device *dev = musb->controller;
struct musb_hdrc_platform_data *pdata = dev->platform_data;
@@ -258,9 +283,10 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
otg->default_a = true;
musb->xceiv->state = OTG_STATE_A_IDLE;
musb->xceiv->last_event = USB_EVENT_ID;
- if (!is_otg_enabled(musb) || musb->gadget_driver) {
+ if (musb->gadget_driver) {
pm_runtime_get_sync(dev);
- usb_phy_init(musb->xceiv);
+ val = AVALID | VBUSVALID;
+ omap4_usb_phy_mailbox(glue, val);
omap2430_musb_set_vbus(musb, 1);
}
break;
@@ -273,7 +299,8 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
musb->xceiv->last_event = USB_EVENT_VBUS;
if (musb->gadget_driver)
pm_runtime_get_sync(dev);
- usb_phy_init(musb->xceiv);
+ val = IDDIG | AVALID | VBUSVALID;
+ omap4_usb_phy_mailbox(glue, val);
break;
case OMAP_MUSB_ID_FLOAT:
@@ -281,17 +308,17 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
dev_dbg(dev, "VBUS Disconnect\n");
musb->xceiv->last_event = USB_EVENT_NONE;
- if (is_otg_enabled(musb) || is_peripheral_enabled(musb))
- if (musb->gadget_driver) {
- pm_runtime_mark_last_busy(dev);
- pm_runtime_put_autosuspend(dev);
- }
+ if (musb->gadget_driver) {
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
if (data->interface_type == MUSB_INTERFACE_UTMI) {
if (musb->xceiv->otg->set_vbus)
otg_set_vbus(musb->xceiv->otg, 0);
}
- usb_phy_shutdown(musb->xceiv);
+ val = SESSEND | IDDIG;
+ omap4_usb_phy_mailbox(glue, val);
break;
default:
dev_dbg(dev, "ID float\n");
@@ -366,6 +393,7 @@ err1:
static void omap2430_musb_enable(struct musb *musb)
{
u8 devctl;
+ u32 val;
unsigned long timeout = jiffies + msecs_to_jiffies(1000);
struct device *dev = musb->controller;
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
@@ -375,7 +403,8 @@ static void omap2430_musb_enable(struct musb *musb)
switch (glue->status) {
case OMAP_MUSB_ID_GROUND:
- usb_phy_init(musb->xceiv);
+ val = AVALID | VBUSVALID;
+ omap4_usb_phy_mailbox(glue, val);
if (data->interface_type != MUSB_INTERFACE_UTMI)
break;
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
@@ -394,7 +423,8 @@ static void omap2430_musb_enable(struct musb *musb)
break;
case OMAP_MUSB_VBUS_VALID:
- usb_phy_init(musb->xceiv);
+ val = IDDIG | AVALID | VBUSVALID;
+ omap4_usb_phy_mailbox(glue, val);
break;
default:
@@ -404,11 +434,14 @@ static void omap2430_musb_enable(struct musb *musb)
static void omap2430_musb_disable(struct musb *musb)
{
+ u32 val;
struct device *dev = musb->controller;
struct omap2430_glue *glue = dev_get_drvdata(dev->parent);
- if (glue->status != OMAP_MUSB_UNKNOWN)
- usb_phy_shutdown(musb->xceiv);
+ if (glue->status != OMAP_MUSB_UNKNOWN) {
+ val = SESSEND | IDDIG;
+ omap4_usb_phy_mailbox(glue, val);
+ }
}
static int omap2430_musb_exit(struct musb *musb)
@@ -438,9 +471,14 @@ static u64 omap2430_dmamask = DMA_BIT_MASK(32);
static int __devinit omap2430_probe(struct platform_device *pdev)
{
struct musb_hdrc_platform_data *pdata = pdev->dev.platform_data;
+ struct omap_musb_board_data *data;
struct platform_device *musb;
struct omap2430_glue *glue;
+ struct device_node *np = pdev->dev.of_node;
+ struct musb_hdrc_config *config;
+ struct resource *res;
int ret = -ENOMEM;
+ int musbid;
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
@@ -448,12 +486,21 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
goto err0;
}
- musb = platform_device_alloc("musb-hdrc", -1);
+ /* get the musb id */
+ musbid = musb_get_id(&pdev->dev, GFP_KERNEL);
+ if (musbid < 0) {
+ dev_err(&pdev->dev, "failed to allocate musb id\n");
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ musb = platform_device_alloc("musb-hdrc", musbid);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err0;
+ goto err1;
}
+ musb->id = musbid;
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &omap2430_dmamask;
musb->dev.coherent_dma_mask = omap2430_dmamask;
@@ -462,6 +509,48 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
glue->musb = musb;
glue->status = OMAP_MUSB_UNKNOWN;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ glue->control_otghs = devm_request_and_ioremap(&pdev->dev, res);
+ if (glue->control_otghs == NULL)
+ dev_dbg(&pdev->dev, "Failed to obtain control memory\n");
+
+ if (np) {
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev,
+ "failed to allocate musb platfrom data\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&pdev->dev,
+ "failed to allocate musb board data\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
+ if (!data) {
+ dev_err(&pdev->dev,
+ "failed to allocate musb hdrc config\n");
+ goto err1;
+ }
+
+ of_property_read_u32(np, "mode", (u32 *)&pdata->mode);
+ of_property_read_u32(np, "interface_type",
+ (u32 *)&data->interface_type);
+ of_property_read_u32(np, "num_eps", (u32 *)&config->num_eps);
+ of_property_read_u32(np, "ram_bits", (u32 *)&config->ram_bits);
+ of_property_read_u32(np, "power", (u32 *)&pdata->power);
+ config->multipoint = of_property_read_bool(np, "multipoint");
+
+ pdata->board_data = data;
+ pdata->config = config;
+ }
+
pdata->platform_ops = &omap2430_ops;
platform_set_drvdata(pdev, glue);
@@ -478,13 +567,13 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err1;
+ goto err2;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err1;
+ goto err2;
}
pm_runtime_enable(&pdev->dev);
@@ -492,14 +581,17 @@ static int __devinit omap2430_probe(struct platform_device *pdev)
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err1;
+ goto err2;
}
return 0;
-err1:
+err2:
platform_device_put(musb);
+err1:
+ musb_put_id(&pdev->dev, musbid);
+
err0:
return ret;
}
@@ -509,8 +601,8 @@ static int __devexit omap2430_remove(struct platform_device *pdev)
struct omap2430_glue *glue = platform_get_drvdata(pdev);
cancel_work_sync(&glue->omap_musb_mailbox_work);
- platform_device_del(glue->musb);
- platform_device_put(glue->musb);
+ musb_put_id(&pdev->dev, glue->musb->id);
+ platform_device_unregister(glue->musb);
return 0;
}
@@ -559,12 +651,26 @@ static struct dev_pm_ops omap2430_pm_ops = {
#define DEV_PM_OPS NULL
#endif
+#ifdef CONFIG_OF
+static const struct of_device_id omap2430_id_table[] = {
+ {
+ .compatible = "ti,omap4-musb"
+ },
+ {
+ .compatible = "ti,omap3-musb"
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, omap2430_id_table);
+#endif
+
static struct platform_driver omap2430_driver = {
.probe = omap2430_probe,
.remove = __devexit_p(omap2430_remove),
.driver = {
.name = "musb-omap2430",
.pm = DEV_PM_OPS,
+ .of_match_table = of_match_ptr(omap2430_id_table),
},
};
diff --git a/drivers/usb/musb/omap2430.h b/drivers/usb/musb/omap2430.h
index 40b3c02ae9f0..b85f3973e78c 100644
--- a/drivers/usb/musb/omap2430.h
+++ b/drivers/usb/musb/omap2430.h
@@ -49,4 +49,13 @@
#define OTG_FORCESTDBY 0x414
# define ENABLEFORCE (1 << 0)
+/*
+ * Control Module bit definitions
+ * XXX: Will be removed once we have a driver for control module.
+ */
+#define AVALID BIT(0)
+#define BVALID BIT(1)
+#define VBUSVALID BIT(2)
+#define SESSEND BIT(3)
+#define IDDIG BIT(4)
#endif /* __MUSB_OMAP243X_H__ */
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index 341625442377..dc4d75ea13ad 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -24,6 +24,7 @@
#include <linux/irq.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include "musb_core.h"
@@ -153,7 +154,7 @@ tusb_fifo_write_unaligned(void __iomem *fifo, const u8 *buf, u16 len)
}
static inline void tusb_fifo_read_unaligned(void __iomem *fifo,
- void __iomem *buf, u16 len)
+ void *buf, u16 len)
{
u32 val;
int i;
@@ -437,14 +438,13 @@ static void musb_do_idle(unsigned long _musb)
if (is_host_active(musb) && (musb->port1_status >> 16))
goto done;
- if (is_peripheral_enabled(musb) && !musb->gadget_driver) {
+ if (!musb->gadget_driver) {
wakeups = 0;
} else {
wakeups = TUSB_PRCM_WHOSTDISCON
| TUSB_PRCM_WBUS
| TUSB_PRCM_WVBUS;
- if (is_otg_enabled(musb))
- wakeups |= TUSB_PRCM_WID;
+ wakeups |= TUSB_PRCM_WID;
}
tusb_allow_idle(musb, wakeups);
}
@@ -582,21 +582,12 @@ static void tusb_musb_set_vbus(struct musb *musb, int is_on)
*
* Note that if a mini-A cable is plugged in the ID line will stay down as
* the weak ID pull-up is not able to pull the ID up.
- *
- * REVISIT: It would be possible to add support for changing between host
- * and peripheral modes in non-OTG configurations by reconfiguring hardware
- * and then setting musb->board_mode. For now, only support OTG mode.
*/
static int tusb_musb_set_mode(struct musb *musb, u8 musb_mode)
{
void __iomem *tbase = musb->ctrl_base;
u32 otg_stat, phy_otg_ctrl, phy_otg_ena, dev_conf;
- if (musb->board_mode != MUSB_OTG) {
- ERR("Changing mode currently only supported in OTG mode\n");
- return -EINVAL;
- }
-
otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
phy_otg_ctrl = musb_readl(tbase, TUSB_PHY_OTG_CTRL);
phy_otg_ena = musb_readl(tbase, TUSB_PHY_OTG_CTRL_ENABLE);
@@ -652,10 +643,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
if ((int_src & TUSB_INT_SRC_ID_STATUS_CHNG)) {
int default_a;
- if (is_otg_enabled(musb))
- default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS);
- else
- default_a = is_host_enabled(musb);
+ default_a = !(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS);
dev_dbg(musb->controller, "Default-%c\n", default_a ? 'A' : 'B');
otg->default_a = default_a;
tusb_musb_set_vbus(musb, default_a);
@@ -669,8 +657,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
if (int_src & TUSB_INT_SRC_VBUS_SENSE_CHNG) {
/* B-dev state machine: no vbus ~= disconnect */
- if ((is_otg_enabled(musb) && !otg->default_a)
- || !is_host_enabled(musb)) {
+ if (!otg->default_a) {
/* ? musb_root_disconnect(musb); */
musb->port1_status &=
~(USB_PORT_STAT_CONNECTION
@@ -1119,10 +1106,8 @@ static int tusb_musb_init(struct musb *musb)
}
musb->isr = tusb_musb_interrupt;
- if (is_peripheral_enabled(musb)) {
- musb->xceiv->set_power = tusb_draw_power;
- the_musb = musb;
- }
+ musb->xceiv->set_power = tusb_draw_power;
+ the_musb = musb;
setup_timer(&musb_idle_timer, musb_do_idle, (unsigned long) musb);
@@ -1175,6 +1160,7 @@ static int __devinit tusb_probe(struct platform_device *pdev)
struct tusb6010_glue *glue;
int ret = -ENOMEM;
+ int musbid;
glue = kzalloc(sizeof(*glue), GFP_KERNEL);
if (!glue) {
@@ -1182,12 +1168,21 @@ static int __devinit tusb_probe(struct platform_device *pdev)
goto err0;
}
- musb = platform_device_alloc("musb-hdrc", -1);
+ /* get the musb id */
+ musbid = musb_get_id(&pdev->dev, GFP_KERNEL);
+ if (musbid < 0) {
+ dev_err(&pdev->dev, "failed to allocate musb id\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ musb = platform_device_alloc("musb-hdrc", musbid);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err1;
+ goto err2;
}
+ musb->id = musbid;
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &tusb_dmamask;
musb->dev.coherent_dma_mask = tusb_dmamask;
@@ -1203,26 +1198,29 @@ static int __devinit tusb_probe(struct platform_device *pdev)
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err2;
+ goto err3;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err2;
+ goto err3;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err2;
+ goto err3;
}
return 0;
-err2:
+err3:
platform_device_put(musb);
+err2:
+ musb_put_id(&pdev->dev, musbid);
+
err1:
kfree(glue);
@@ -1234,6 +1232,7 @@ static int __devexit tusb_remove(struct platform_device *pdev)
{
struct tusb6010_glue *glue = platform_get_drvdata(pdev);
+ musb_put_id(&pdev->dev, glue->musb->id);
platform_device_del(glue->musb);
platform_device_put(glue->musb);
kfree(glue);
diff --git a/drivers/usb/musb/tusb6010_omap.c b/drivers/usb/musb/tusb6010_omap.c
index 89f0709f8935..7a62b95dac24 100644
--- a/drivers/usb/musb/tusb6010_omap.c
+++ b/drivers/usb/musb/tusb6010_omap.c
@@ -661,7 +661,7 @@ void dma_controller_destroy(struct dma_controller *c)
kfree(tusb_dma);
}
-struct dma_controller *__init
+struct dma_controller *__devinit
dma_controller_create(struct musb *musb, void __iomem *base)
{
void __iomem *tbase = musb->ctrl_base;
diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index a8c0fadce1b0..d62a91fedc22 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -74,25 +74,34 @@ static int __devinit ux500_probe(struct platform_device *pdev)
goto err0;
}
- musb = platform_device_alloc("musb-hdrc", -1);
+ /* get the musb id */
+ musbid = musb_get_id(&pdev->dev, GFP_KERNEL);
+ if (musbid < 0) {
+ dev_err(&pdev->dev, "failed to allocate musb id\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ musb = platform_device_alloc("musb-hdrc", musbid);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err1;
+ goto err2;
}
clk = clk_get(&pdev->dev, "usb");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
ret = PTR_ERR(clk);
- goto err2;
+ goto err3;
}
ret = clk_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock\n");
- goto err3;
+ goto err4;
}
+ musb->id = musbid;
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = pdev->dev.dma_mask;
musb->dev.coherent_dma_mask = pdev->dev.coherent_dma_mask;
@@ -109,32 +118,35 @@ static int __devinit ux500_probe(struct platform_device *pdev)
pdev->num_resources);
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err4;
+ goto err5;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err4;
+ goto err5;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err4;
+ goto err5;
}
return 0;
-err4:
+err5:
clk_disable(clk);
-err3:
+err4:
clk_put(clk);
-err2:
+err3:
platform_device_put(musb);
+err2:
+ musb_put_id(&pdev->dev, musbid);
+
err1:
kfree(glue);
@@ -146,6 +158,7 @@ static int __devexit ux500_remove(struct platform_device *pdev)
{
struct ux500_glue *glue = platform_get_drvdata(pdev);
+ musb_put_id(&pdev->dev, glue->musb->id);
platform_device_del(glue->musb);
platform_device_put(glue->musb);
clk_disable(glue->clk);
diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c
index f82246d2fd16..f1059e725ea8 100644
--- a/drivers/usb/musb/ux500_dma.c
+++ b/drivers/usb/musb/ux500_dma.c
@@ -364,7 +364,7 @@ void dma_controller_destroy(struct dma_controller *c)
kfree(controller);
}
-struct dma_controller *__init
+struct dma_controller *__devinit
dma_controller_create(struct musb *musb, void __iomem *base)
{
struct ux500_dma_controller *controller;
diff --git a/drivers/usb/otg/Kconfig b/drivers/usb/otg/Kconfig
index 13fd1ddf742f..d8c8a42bff3e 100644
--- a/drivers/usb/otg/Kconfig
+++ b/drivers/usb/otg/Kconfig
@@ -68,7 +68,7 @@ config TWL4030_USB
config TWL6030_USB
tristate "TWL6030 USB Transceiver Driver"
- depends on TWL4030_CORE
+ depends on TWL4030_CORE && OMAP_USB2
select USB_OTG_UTILS
help
Enable this to support the USB OTG transceiver on TWL6030
diff --git a/drivers/usb/otg/fsl_otg.c b/drivers/usb/otg/fsl_otg.c
index 23c798cb2d7f..c19d1d7173a9 100644
--- a/drivers/usb/otg/fsl_otg.c
+++ b/drivers/usb/otg/fsl_otg.c
@@ -544,9 +544,13 @@ int fsl_otg_start_gadget(struct otg_fsm *fsm, int on)
*/
static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
{
- struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ struct fsl_otg *otg_dev;
+
+ if (!otg)
+ return -ENODEV;
- if (!otg || otg_dev != fsl_otg_dev)
+ otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ if (otg_dev != fsl_otg_dev)
return -ENODEV;
otg->host = host;
@@ -590,12 +594,15 @@ static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
static int fsl_otg_set_peripheral(struct usb_otg *otg,
struct usb_gadget *gadget)
{
- struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ struct fsl_otg *otg_dev;
+ if (!otg)
+ return -ENODEV;
+
+ otg_dev = container_of(otg->phy, struct fsl_otg, phy);
VDBG("otg_dev 0x%x\n", (int)otg_dev);
VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev);
-
- if (!otg || otg_dev != fsl_otg_dev)
+ if (otg_dev != fsl_otg_dev)
return -ENODEV;
if (!gadget) {
@@ -660,10 +667,13 @@ static void fsl_otg_event(struct work_struct *work)
/* B-device start SRP */
static int fsl_otg_start_srp(struct usb_otg *otg)
{
- struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ struct fsl_otg *otg_dev;
+
+ if (!otg || otg->phy->state != OTG_STATE_B_IDLE)
+ return -ENODEV;
- if (!otg || otg_dev != fsl_otg_dev
- || otg->phy->state != OTG_STATE_B_IDLE)
+ otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ if (otg_dev != fsl_otg_dev)
return -ENODEV;
otg_dev->fsm.b_bus_req = 1;
@@ -675,9 +685,13 @@ static int fsl_otg_start_srp(struct usb_otg *otg)
/* A_host suspend will call this function to start hnp */
static int fsl_otg_start_hnp(struct usb_otg *otg)
{
- struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ struct fsl_otg *otg_dev;
+
+ if (!otg)
+ return -ENODEV;
- if (!otg || otg_dev != fsl_otg_dev)
+ otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ if (otg_dev != fsl_otg_dev)
return -ENODEV;
DBG("start_hnp...n");
diff --git a/drivers/usb/otg/mxs-phy.c b/drivers/usb/otg/mxs-phy.c
index c1a67cb8e244..88db976647cf 100644
--- a/drivers/usb/otg/mxs-phy.c
+++ b/drivers/usb/otg/mxs-phy.c
@@ -20,6 +20,7 @@
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/io.h>
+#include <linux/workqueue.h>
#define DRIVER_NAME "mxs_phy"
@@ -34,9 +35,16 @@
#define BM_USBPHY_CTRL_ENUTMILEVEL2 BIT(14)
#define BM_USBPHY_CTRL_ENHOSTDISCONDETECT BIT(1)
+/*
+ * Amount of delay in miliseconds to safely enable ENHOSTDISCONDETECT bit
+ * so that connection and reset processing can be completed for the root hub.
+ */
+#define MXY_PHY_ENHOSTDISCONDETECT_DELAY 250
+
struct mxs_phy {
struct usb_phy phy;
struct clk *clk;
+ struct delayed_work enhostdiscondetect_work;
};
#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
@@ -62,6 +70,7 @@ static int mxs_phy_init(struct usb_phy *phy)
clk_prepare_enable(mxs_phy->clk);
mxs_phy_hw_init(mxs_phy);
+ INIT_DELAYED_WORK(&mxs_phy->enhostdiscondetect_work, NULL);
return 0;
}
@@ -76,13 +85,34 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
clk_disable_unprepare(mxs_phy->clk);
}
+static void mxs_phy_enhostdiscondetect_delay(struct work_struct *ws)
+{
+ struct mxs_phy *mxs_phy = container_of(ws, struct mxs_phy,
+ enhostdiscondetect_work.work);
+
+ /* Enable HOSTDISCONDETECT after delay. */
+ dev_dbg(mxs_phy->phy.dev, "Setting ENHOSTDISCONDETECT\n");
+ writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
+ mxs_phy->phy.io_priv + HW_USBPHY_CTRL_SET);
+}
+
static int mxs_phy_on_connect(struct usb_phy *phy, int port)
{
+ struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+
dev_dbg(phy->dev, "Connect on port %d\n", port);
- mxs_phy_hw_init(to_mxs_phy(phy));
- writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
- phy->io_priv + HW_USBPHY_CTRL_SET);
+ mxs_phy_hw_init(mxs_phy);
+
+ /*
+ * Delay enabling ENHOSTDISCONDETECT so that connection and
+ * reset processing can be completed for the root hub.
+ */
+ dev_dbg(phy->dev, "Delaying setting ENHOSTDISCONDETECT\n");
+ PREPARE_DELAYED_WORK(&mxs_phy->enhostdiscondetect_work,
+ mxs_phy_enhostdiscondetect_delay);
+ schedule_delayed_work(&mxs_phy->enhostdiscondetect_work,
+ msecs_to_jiffies(MXY_PHY_ENHOSTDISCONDETECT_DELAY));
return 0;
}
@@ -91,6 +121,8 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy, int port)
{
dev_dbg(phy->dev, "Disconnect on port %d\n", port);
+ /* No need to delay before clearing ENHOSTDISCONDETECT. */
+ dev_dbg(phy->dev, "Clearing ENHOSTDISCONDETECT\n");
writel_relaxed(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
phy->io_priv + HW_USBPHY_CTRL_CLR);
diff --git a/drivers/usb/otg/nop-usb-xceiv.c b/drivers/usb/otg/nop-usb-xceiv.c
index 803f958f4133..e52e35e7adaf 100644
--- a/drivers/usb/otg/nop-usb-xceiv.c
+++ b/drivers/usb/otg/nop-usb-xceiv.c
@@ -30,6 +30,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/usb/otg.h>
+#include <linux/usb/nop-usb-xceiv.h>
#include <linux/slab.h>
struct nop_usb_xceiv {
@@ -94,7 +95,9 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host)
static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev)
{
+ struct nop_usb_xceiv_platform_data *pdata = pdev->dev.platform_data;
struct nop_usb_xceiv *nop;
+ enum usb_phy_type type = USB_PHY_TYPE_USB2;
int err;
nop = kzalloc(sizeof *nop, GFP_KERNEL);
@@ -107,6 +110,9 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ if (pdata)
+ type = pdata->type;
+
nop->dev = &pdev->dev;
nop->phy.dev = nop->dev;
nop->phy.label = "nop-xceiv";
@@ -117,7 +123,7 @@ static int __devinit nop_usb_xceiv_probe(struct platform_device *pdev)
nop->phy.otg->set_host = nop_set_host;
nop->phy.otg->set_peripheral = nop_set_peripheral;
- err = usb_add_phy(&nop->phy, USB_PHY_TYPE_USB2);
+ err = usb_add_phy(&nop->phy, type);
if (err) {
dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
err);
diff --git a/drivers/usb/otg/otg.c b/drivers/usb/otg/otg.c
index 1bf60a22595c..a30c04115115 100644
--- a/drivers/usb/otg/otg.c
+++ b/drivers/usb/otg/otg.c
@@ -159,7 +159,7 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
unsigned long flags;
struct usb_phy *phy;
- if (x && x->type != USB_PHY_TYPE_UNDEFINED) {
+ if (x->type != USB_PHY_TYPE_UNDEFINED) {
dev_err(x->dev, "not accepting initialized PHY %s\n", x->label);
return -EINVAL;
}
diff --git a/drivers/usb/otg/twl4030-usb.c b/drivers/usb/otg/twl4030-usb.c
index 523cad5bfea9..f0d2e7530cfe 100644
--- a/drivers/usb/otg/twl4030-usb.c
+++ b/drivers/usb/otg/twl4030-usb.c
@@ -585,23 +585,28 @@ static int __devinit twl4030_usb_probe(struct platform_device *pdev)
struct twl4030_usb *twl;
int status, err;
struct usb_otg *otg;
-
- if (!pdata) {
- dev_dbg(&pdev->dev, "platform_data not available\n");
- return -EINVAL;
- }
+ struct device_node *np = pdev->dev.of_node;
twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
if (!twl)
return -ENOMEM;
+ if (np)
+ of_property_read_u32(np, "usb_mode",
+ (enum twl4030_usb_mode *)&twl->usb_mode);
+ else if (pdata)
+ twl->usb_mode = pdata->usb_mode;
+ else {
+ dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
+ return -EINVAL;
+ }
+
otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL);
if (!otg)
return -ENOMEM;
twl->dev = &pdev->dev;
twl->irq = platform_get_irq(pdev, 0);
- twl->usb_mode = pdata->usb_mode;
twl->vbus_supplied = false;
twl->asleep = 1;
twl->linkstat = OMAP_MUSB_UNKNOWN;
@@ -690,12 +695,21 @@ static int __exit twl4030_usb_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id twl4030_usb_id_table[] = {
+ { .compatible = "ti,twl4030-usb" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, twl4030_usb_id_table);
+#endif
+
static struct platform_driver twl4030_usb_driver = {
.probe = twl4030_usb_probe,
.remove = __exit_p(twl4030_usb_remove),
.driver = {
.name = "twl4030_usb",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(twl4030_usb_id_table),
},
};
diff --git a/drivers/usb/otg/twl6030-usb.c b/drivers/usb/otg/twl6030-usb.c
index 6907d8df7a27..fcadef7864f1 100644
--- a/drivers/usb/otg/twl6030-usb.c
+++ b/drivers/usb/otg/twl6030-usb.c
@@ -25,8 +25,9 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
-#include <linux/usb/otg.h>
#include <linux/usb/musb-omap.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/usb/omap_usb.h>
#include <linux/i2c/twl.h>
#include <linux/regulator/consumer.h>
#include <linux/err.h>
@@ -87,7 +88,7 @@
#define VBUS_DET BIT(2)
struct twl6030_usb {
- struct usb_phy phy;
+ struct phy_companion comparator;
struct device *dev;
/* for vbus reporting with irqs disabled */
@@ -104,10 +105,10 @@ struct twl6030_usb {
u8 asleep;
bool irq_enabled;
bool vbus_enable;
- unsigned long features;
+ const char *regulator;
};
-#define phy_to_twl(x) container_of((x), struct twl6030_usb, phy)
+#define comparator_to_twl(x) container_of((x), struct twl6030_usb, comparator)
/*-------------------------------------------------------------------------*/
@@ -137,50 +138,9 @@ static inline u8 twl6030_readb(struct twl6030_usb *twl, u8 module, u8 address)
return ret;
}
-static int twl6030_phy_init(struct usb_phy *x)
+static int twl6030_start_srp(struct phy_companion *comparator)
{
- struct twl6030_usb *twl;
- struct device *dev;
- struct twl4030_usb_data *pdata;
-
- twl = phy_to_twl(x);
- dev = twl->dev;
- pdata = dev->platform_data;
-
- if (twl->linkstat == OMAP_MUSB_ID_GROUND)
- pdata->phy_power(twl->dev, 1, 1);
- else
- pdata->phy_power(twl->dev, 0, 1);
-
- return 0;
-}
-
-static void twl6030_phy_shutdown(struct usb_phy *x)
-{
- struct twl6030_usb *twl;
- struct device *dev;
- struct twl4030_usb_data *pdata;
-
- twl = phy_to_twl(x);
- dev = twl->dev;
- pdata = dev->platform_data;
- pdata->phy_power(twl->dev, 0, 0);
-}
-
-static int twl6030_phy_suspend(struct usb_phy *x, int suspend)
-{
- struct twl6030_usb *twl = phy_to_twl(x);
- struct device *dev = twl->dev;
- struct twl4030_usb_data *pdata = dev->platform_data;
-
- pdata->phy_suspend(dev, suspend);
-
- return 0;
-}
-
-static int twl6030_start_srp(struct usb_otg *otg)
-{
- struct twl6030_usb *twl = phy_to_twl(otg->phy);
+ struct twl6030_usb *twl = comparator_to_twl(comparator);
twl6030_writeb(twl, TWL_MODULE_USB, 0x24, USB_VBUS_CTRL_SET);
twl6030_writeb(twl, TWL_MODULE_USB, 0x84, USB_VBUS_CTRL_SET);
@@ -193,13 +153,6 @@ static int twl6030_start_srp(struct usb_otg *otg)
static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
{
- char *regulator_name;
-
- if (twl->features & TWL6025_SUBCLASS)
- regulator_name = "ldousb";
- else
- regulator_name = "vusb";
-
/* Set to OTG_REV 1.3 and turn on the ID_WAKEUP_COMP */
twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x1, TWL6030_BACKUP_REG);
@@ -209,7 +162,7 @@ static int twl6030_usb_ldo_init(struct twl6030_usb *twl)
/* Program MISC2 register and set bit VUSB_IN_VBAT */
twl6030_writeb(twl, TWL6030_MODULE_ID0 , 0x10, TWL6030_MISC2);
- twl->usb3v3 = regulator_get(twl->dev, regulator_name);
+ twl->usb3v3 = regulator_get(twl->dev, twl->regulator);
if (IS_ERR(twl->usb3v3))
return -ENODEV;
@@ -313,23 +266,8 @@ static irqreturn_t twl6030_usbotg_irq(int irq, void *_twl)
return IRQ_HANDLED;
}
-static int twl6030_set_peripheral(struct usb_otg *otg,
- struct usb_gadget *gadget)
-{
- if (!otg)
- return -ENODEV;
-
- otg->gadget = gadget;
- if (!gadget)
- otg->phy->state = OTG_STATE_UNDEFINED;
-
- return 0;
-}
-
-static int twl6030_enable_irq(struct usb_phy *x)
+static int twl6030_enable_irq(struct twl6030_usb *twl)
{
- struct twl6030_usb *twl = phy_to_twl(x);
-
twl6030_writeb(twl, TWL_MODULE_USB, 0x1, USB_ID_INT_EN_HI_SET);
twl6030_interrupt_unmask(0x05, REG_INT_MSK_LINE_C);
twl6030_interrupt_unmask(0x05, REG_INT_MSK_STS_C);
@@ -362,9 +300,9 @@ static void otg_set_vbus_work(struct work_struct *data)
CHARGERUSB_CTRL1);
}
-static int twl6030_set_vbus(struct usb_otg *otg, bool enabled)
+static int twl6030_set_vbus(struct phy_companion *comparator, bool enabled)
{
- struct twl6030_usb *twl = phy_to_twl(otg->phy);
+ struct twl6030_usb *twl = comparator_to_twl(comparator);
twl->vbus_enable = enabled;
schedule_work(&twl->set_vbus_work);
@@ -372,52 +310,44 @@ static int twl6030_set_vbus(struct usb_otg *otg, bool enabled)
return 0;
}
-static int twl6030_set_host(struct usb_otg *otg, struct usb_bus *host)
-{
- if (!otg)
- return -ENODEV;
-
- otg->host = host;
- if (!host)
- otg->phy->state = OTG_STATE_UNDEFINED;
- return 0;
-}
-
static int __devinit twl6030_usb_probe(struct platform_device *pdev)
{
+ u32 ret;
struct twl6030_usb *twl;
int status, err;
- struct twl4030_usb_data *pdata;
- struct usb_otg *otg;
- struct device *dev = &pdev->dev;
- pdata = dev->platform_data;
+ struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct twl4030_usb_data *pdata = dev->platform_data;
twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL);
if (!twl)
return -ENOMEM;
- otg = devm_kzalloc(dev, sizeof *otg, GFP_KERNEL);
- if (!otg)
- return -ENOMEM;
-
twl->dev = &pdev->dev;
twl->irq1 = platform_get_irq(pdev, 0);
twl->irq2 = platform_get_irq(pdev, 1);
- twl->features = pdata->features;
twl->linkstat = OMAP_MUSB_UNKNOWN;
- twl->phy.dev = twl->dev;
- twl->phy.label = "twl6030";
- twl->phy.otg = otg;
- twl->phy.init = twl6030_phy_init;
- twl->phy.shutdown = twl6030_phy_shutdown;
- twl->phy.set_suspend = twl6030_phy_suspend;
+ twl->comparator.set_vbus = twl6030_set_vbus;
+ twl->comparator.start_srp = twl6030_start_srp;
+
+ ret = omap_usb2_set_comparator(&twl->comparator);
+ if (ret == -ENODEV) {
+ dev_info(&pdev->dev, "phy not ready, deferring probe");
+ return -EPROBE_DEFER;
+ }
- otg->phy = &twl->phy;
- otg->set_host = twl6030_set_host;
- otg->set_peripheral = twl6030_set_peripheral;
- otg->set_vbus = twl6030_set_vbus;
- otg->start_srp = twl6030_start_srp;
+ if (np) {
+ twl->regulator = "usb";
+ } else if (pdata) {
+ if (pdata->features & TWL6025_SUBCLASS)
+ twl->regulator = "ldousb";
+ else
+ twl->regulator = "vusb";
+ } else {
+ dev_err(&pdev->dev, "twl6030 initialized without pdata\n");
+ return -EINVAL;
+ }
/* init spinlock for workqueue */
spin_lock_init(&twl->lock);
@@ -427,7 +357,6 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "ldo init failed\n");
return err;
}
- usb_add_phy(&twl->phy, USB_PHY_TYPE_USB2);
platform_set_drvdata(pdev, twl);
if (device_create_file(&pdev->dev, &dev_attr_vbus))
@@ -458,9 +387,7 @@ static int __devinit twl6030_usb_probe(struct platform_device *pdev)
}
twl->asleep = 0;
- pdata->phy_init(dev);
- twl6030_phy_suspend(&twl->phy, 0);
- twl6030_enable_irq(&twl->phy);
+ twl6030_enable_irq(twl);
dev_info(&pdev->dev, "Initialized TWL6030 USB module\n");
return 0;
@@ -470,10 +397,6 @@ static int __exit twl6030_usb_remove(struct platform_device *pdev)
{
struct twl6030_usb *twl = platform_get_drvdata(pdev);
- struct twl4030_usb_data *pdata;
- struct device *dev = &pdev->dev;
- pdata = dev->platform_data;
-
twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
REG_INT_MSK_LINE_C);
twl6030_interrupt_mask(TWL6030_USBOTG_INT_MASK,
@@ -481,19 +404,27 @@ static int __exit twl6030_usb_remove(struct platform_device *pdev)
free_irq(twl->irq1, twl);
free_irq(twl->irq2, twl);
regulator_put(twl->usb3v3);
- pdata->phy_exit(twl->dev);
device_remove_file(twl->dev, &dev_attr_vbus);
cancel_work_sync(&twl->set_vbus_work);
return 0;
}
+#ifdef CONFIG_OF
+static const struct of_device_id twl6030_usb_id_table[] = {
+ { .compatible = "ti,twl6030-usb" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, twl6030_usb_id_table);
+#endif
+
static struct platform_driver twl6030_usb_driver = {
.probe = twl6030_usb_probe,
.remove = __exit_p(twl6030_usb_remove),
.driver = {
.name = "twl6030_usb",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(twl6030_usb_id_table),
},
};
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index e7cf84f0751a..63c339b3e676 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -4,6 +4,15 @@
comment "USB Physical Layer drivers"
depends on USB || USB_GADGET
+config OMAP_USB2
+ tristate "OMAP USB2 PHY Driver"
+ select USB_OTG_UTILS
+ help
+ Enable this to support the transceiver that is part of SOC. This
+ driver takes care of all the PHY functionality apart from comparator.
+ The USB OTG controller communicates with the comparator using this
+ driver.
+
config USB_ISP1301
tristate "NXP ISP1301 USB transceiver support"
depends on USB || USB_GADGET
@@ -15,3 +24,11 @@ config USB_ISP1301
To compile this driver as a module, choose M here: the
module will be called isp1301.
+
+config MV_U3D_PHY
+ bool "Marvell USB 3.0 PHY controller Driver"
+ depends on USB_MV_U3D
+ select USB_OTG_UTILS
+ help
+ Enable this to support Marvell USB 3.0 phy controller for Marvell
+ SoC.
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index eca095b1a890..b069f29f1225 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -4,4 +4,7 @@
ccflags-$(CONFIG_USB_DEBUG) := -DDEBUG
+obj-$(CONFIG_OMAP_USB2) += omap-usb2.o
obj-$(CONFIG_USB_ISP1301) += isp1301.o
+obj-$(CONFIG_MV_U3D_PHY) += mv_u3d_phy.o
+obj-$(CONFIG_USB_EHCI_TEGRA) += tegra_usb_phy.o
diff --git a/drivers/usb/phy/isp1301.c b/drivers/usb/phy/isp1301.c
index b19f4932a037..18dbf7e37607 100644
--- a/drivers/usb/phy/isp1301.c
+++ b/drivers/usb/phy/isp1301.c
@@ -15,12 +15,6 @@
#define DRV_NAME "isp1301"
-#define ISP1301_I2C_ADDR 0x2C
-
-static const unsigned short normal_i2c[] = {
- ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END
-};
-
static const struct i2c_device_id isp1301_id[] = {
{ "isp1301", 0 },
{ }
diff --git a/drivers/usb/phy/mv_u3d_phy.c b/drivers/usb/phy/mv_u3d_phy.c
new file mode 100644
index 000000000000..9f1c5d3c60ec
--- /dev/null
+++ b/drivers/usb/phy/mv_u3d_phy.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+#include <linux/platform_data/mv_usb.h>
+
+#include "mv_u3d_phy.h"
+
+/*
+ * struct mv_u3d_phy - transceiver driver state
+ * @phy: transceiver structure
+ * @dev: The parent device supplied to the probe function
+ * @clk: usb phy clock
+ * @base: usb phy register memory base
+ */
+struct mv_u3d_phy {
+ struct usb_phy phy;
+ struct mv_usb_platform_data *plat;
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *base;
+};
+
+static u32 mv_u3d_phy_read(void __iomem *base, u32 reg)
+{
+ void __iomem *addr, *data;
+
+ addr = base;
+ data = base + 0x4;
+
+ writel_relaxed(reg, addr);
+ return readl_relaxed(data);
+}
+
+static void mv_u3d_phy_set(void __iomem *base, u32 reg, u32 value)
+{
+ void __iomem *addr, *data;
+ u32 tmp;
+
+ addr = base;
+ data = base + 0x4;
+
+ writel_relaxed(reg, addr);
+ tmp = readl_relaxed(data);
+ tmp |= value;
+ writel_relaxed(tmp, data);
+}
+
+static void mv_u3d_phy_clear(void __iomem *base, u32 reg, u32 value)
+{
+ void __iomem *addr, *data;
+ u32 tmp;
+
+ addr = base;
+ data = base + 0x4;
+
+ writel_relaxed(reg, addr);
+ tmp = readl_relaxed(data);
+ tmp &= ~value;
+ writel_relaxed(tmp, data);
+}
+
+static void mv_u3d_phy_write(void __iomem *base, u32 reg, u32 value)
+{
+ void __iomem *addr, *data;
+
+ addr = base;
+ data = base + 0x4;
+
+ writel_relaxed(reg, addr);
+ writel_relaxed(value, data);
+}
+
+void mv_u3d_phy_shutdown(struct usb_phy *phy)
+{
+ struct mv_u3d_phy *mv_u3d_phy;
+ void __iomem *base;
+ u32 val;
+
+ mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy);
+ base = mv_u3d_phy->base;
+
+ /* Power down Reference Analog current, bit 15
+ * Power down PLL, bit 14
+ * Power down Receiver, bit 13
+ * Power down Transmitter, bit 12
+ * of USB3_POWER_PLL_CONTROL register
+ */
+ val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL);
+ val &= ~(USB3_POWER_PLL_CONTROL_PU);
+ mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val);
+
+ if (mv_u3d_phy->clk)
+ clk_disable(mv_u3d_phy->clk);
+}
+
+static int mv_u3d_phy_init(struct usb_phy *phy)
+{
+ struct mv_u3d_phy *mv_u3d_phy;
+ void __iomem *base;
+ u32 val, count;
+
+ /* enable usb3 phy */
+ mv_u3d_phy = container_of(phy, struct mv_u3d_phy, phy);
+
+ if (mv_u3d_phy->clk)
+ clk_enable(mv_u3d_phy->clk);
+
+ base = mv_u3d_phy->base;
+
+ val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL);
+ val &= ~(USB3_POWER_PLL_CONTROL_PU_MASK);
+ val |= 0xF << USB3_POWER_PLL_CONTROL_PU_SHIFT;
+ mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val);
+ udelay(100);
+
+ mv_u3d_phy_write(base, USB3_RESET_CONTROL,
+ USB3_RESET_CONTROL_RESET_PIPE);
+ udelay(100);
+
+ mv_u3d_phy_write(base, USB3_RESET_CONTROL,
+ USB3_RESET_CONTROL_RESET_PIPE
+ | USB3_RESET_CONTROL_RESET_PHY);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_POWER_PLL_CONTROL);
+ val &= ~(USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK
+ | USB3_POWER_PLL_CONTROL_PHY_MODE_MASK);
+ val |= (USB3_PLL_25MHZ << USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT)
+ | (0x5 << USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT);
+ mv_u3d_phy_write(base, USB3_POWER_PLL_CONTROL, val);
+ udelay(100);
+
+ mv_u3d_phy_clear(base, USB3_KVCO_CALI_CONTROL,
+ USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_SQUELCH_FFE);
+ val &= ~(USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK
+ | USB3_SQUELCH_FFE_FFE_RES_SEL_MASK
+ | USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK);
+ val |= ((0xD << USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT)
+ | (0x7 << USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT)
+ | (0x8 << USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT));
+ mv_u3d_phy_write(base, USB3_SQUELCH_FFE, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_GEN1_SET0);
+ val &= ~USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK;
+ val |= 1 << USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT;
+ mv_u3d_phy_write(base, USB3_GEN1_SET0, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_GEN2_SET0);
+ val &= ~(USB3_GEN2_SET0_G2_TX_AMP_MASK
+ | USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK
+ | USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK);
+ val |= ((0x14 << USB3_GEN2_SET0_G2_TX_AMP_SHIFT)
+ | (1 << USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT)
+ | (0xA << USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT)
+ | (1 << USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT));
+ mv_u3d_phy_write(base, USB3_GEN2_SET0, val);
+ udelay(100);
+
+ mv_u3d_phy_read(base, USB3_TX_EMPPH);
+ val &= ~(USB3_TX_EMPPH_AMP_MASK
+ | USB3_TX_EMPPH_EN_MASK
+ | USB3_TX_EMPPH_AMP_FORCE_MASK
+ | USB3_TX_EMPPH_PAR1_MASK
+ | USB3_TX_EMPPH_PAR2_MASK);
+ val |= ((0xB << USB3_TX_EMPPH_AMP_SHIFT)
+ | (1 << USB3_TX_EMPPH_EN_SHIFT)
+ | (1 << USB3_TX_EMPPH_AMP_FORCE_SHIFT)
+ | (0x1C << USB3_TX_EMPPH_PAR1_SHIFT)
+ | (1 << USB3_TX_EMPPH_PAR2_SHIFT));
+
+ mv_u3d_phy_write(base, USB3_TX_EMPPH, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_GEN2_SET1);
+ val &= ~(USB3_GEN2_SET1_G2_RX_SELMUPI_MASK
+ | USB3_GEN2_SET1_G2_RX_SELMUPF_MASK
+ | USB3_GEN2_SET1_G2_RX_SELMUFI_MASK
+ | USB3_GEN2_SET1_G2_RX_SELMUFF_MASK);
+ val |= ((1 << USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT)
+ | (1 << USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT)
+ | (1 << USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT)
+ | (1 << USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT));
+ mv_u3d_phy_write(base, USB3_GEN2_SET1, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_DIGITAL_LOOPBACK_EN);
+ val &= ~USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK;
+ val |= 1 << USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT;
+ mv_u3d_phy_write(base, USB3_DIGITAL_LOOPBACK_EN, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_IMPEDANCE_TX_SSC);
+ val &= ~USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK;
+ val |= 0xC << USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT;
+ mv_u3d_phy_write(base, USB3_IMPEDANCE_TX_SSC, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_IMPEDANCE_CALI_CTRL);
+ val &= ~USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK;
+ val |= 0x4 << USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT;
+ mv_u3d_phy_write(base, USB3_IMPEDANCE_CALI_CTRL, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_PHY_ISOLATION_MODE);
+ val &= ~(USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK
+ | USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK
+ | USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK);
+ val |= ((1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT)
+ | (1 << USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT));
+ mv_u3d_phy_write(base, USB3_PHY_ISOLATION_MODE, val);
+ udelay(100);
+
+ val = mv_u3d_phy_read(base, USB3_TXDETRX);
+ val &= ~(USB3_TXDETRX_VTHSEL_MASK);
+ val |= 0x1 << USB3_TXDETRX_VTHSEL_SHIFT;
+ mv_u3d_phy_write(base, USB3_TXDETRX, val);
+ udelay(100);
+
+ dev_dbg(mv_u3d_phy->dev, "start calibration\n");
+
+calstart:
+ /* Perform Manual Calibration */
+ mv_u3d_phy_set(base, USB3_KVCO_CALI_CONTROL,
+ 1 << USB3_KVCO_CALI_CONTROL_CAL_START_SHIFT);
+
+ mdelay(1);
+
+ count = 0;
+ while (1) {
+ val = mv_u3d_phy_read(base, USB3_KVCO_CALI_CONTROL);
+ if (val & (1 << USB3_KVCO_CALI_CONTROL_CAL_DONE_SHIFT))
+ break;
+ else if (count > 50) {
+ dev_dbg(mv_u3d_phy->dev, "calibration failure, retry...\n");
+ goto calstart;
+ }
+ count++;
+ mdelay(1);
+ }
+
+ /* active PIPE interface */
+ mv_u3d_phy_write(base, USB3_PIPE_SM_CTRL,
+ 1 << USB3_PIPE_SM_CTRL_PHY_INIT_DONE);
+
+ return 0;
+}
+
+static int __devinit mv_u3d_phy_probe(struct platform_device *pdev)
+{
+ struct mv_u3d_phy *mv_u3d_phy;
+ struct mv_usb_platform_data *pdata;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ void __iomem *phy_base;
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "%s: no platform data defined\n", __func__);
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing mem resource\n");
+ return -ENODEV;
+ }
+
+ phy_base = devm_request_and_ioremap(dev, res);
+ if (!phy_base) {
+ dev_err(dev, "%s: register mapping failed\n", __func__);
+ return -ENXIO;
+ }
+
+ mv_u3d_phy = devm_kzalloc(dev, sizeof(*mv_u3d_phy), GFP_KERNEL);
+ if (!mv_u3d_phy)
+ return -ENOMEM;
+
+ mv_u3d_phy->dev = &pdev->dev;
+ mv_u3d_phy->plat = pdata;
+ mv_u3d_phy->base = phy_base;
+ mv_u3d_phy->phy.dev = mv_u3d_phy->dev;
+ mv_u3d_phy->phy.label = "mv-u3d-phy";
+ mv_u3d_phy->phy.init = mv_u3d_phy_init;
+ mv_u3d_phy->phy.shutdown = mv_u3d_phy_shutdown;
+
+ ret = usb_add_phy(&mv_u3d_phy->phy, USB_PHY_TYPE_USB3);
+ if (ret)
+ goto err;
+
+ if (!mv_u3d_phy->clk)
+ mv_u3d_phy->clk = clk_get(mv_u3d_phy->dev, "u3dphy");
+
+ platform_set_drvdata(pdev, mv_u3d_phy);
+
+ dev_info(&pdev->dev, "Initialized Marvell USB 3.0 PHY\n");
+err:
+ return ret;
+}
+
+static int __exit mv_u3d_phy_remove(struct platform_device *pdev)
+{
+ struct mv_u3d_phy *mv_u3d_phy = platform_get_drvdata(pdev);
+
+ usb_remove_phy(&mv_u3d_phy->phy);
+
+ if (mv_u3d_phy->clk) {
+ clk_put(mv_u3d_phy->clk);
+ mv_u3d_phy->clk = NULL;
+ }
+
+ return 0;
+}
+
+static struct platform_driver mv_u3d_phy_driver = {
+ .probe = mv_u3d_phy_probe,
+ .remove = __devexit_p(mv_u3d_phy_remove),
+ .driver = {
+ .name = "mv-u3d-phy",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(mv_u3d_phy_driver);
+MODULE_DESCRIPTION("Marvell USB 3.0 PHY controller");
+MODULE_AUTHOR("Yu Xu <yuxu@marvell.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mv-u3d-phy");
diff --git a/drivers/usb/phy/mv_u3d_phy.h b/drivers/usb/phy/mv_u3d_phy.h
new file mode 100644
index 000000000000..2a658cb9a527
--- /dev/null
+++ b/drivers/usb/phy/mv_u3d_phy.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#ifndef __MV_U3D_PHY_H
+#define __MV_U3D_PHY_H
+
+#define USB3_POWER_PLL_CONTROL 0x1
+#define USB3_KVCO_CALI_CONTROL 0x2
+#define USB3_IMPEDANCE_CALI_CTRL 0x3
+#define USB3_IMPEDANCE_TX_SSC 0x4
+#define USB3_SQUELCH_FFE 0x6
+#define USB3_GEN1_SET0 0xD
+#define USB3_GEN2_SET0 0xF
+#define USB3_GEN2_SET1 0x10
+#define USB3_DIGITAL_LOOPBACK_EN 0x23
+#define USB3_PHY_ISOLATION_MODE 0x26
+#define USB3_TXDETRX 0x48
+#define USB3_TX_EMPPH 0x5E
+#define USB3_RESET_CONTROL 0x90
+#define USB3_PIPE_SM_CTRL 0x91
+
+#define USB3_RESET_CONTROL_RESET_PIPE 0x1
+#define USB3_RESET_CONTROL_RESET_PHY 0x2
+
+#define USB3_POWER_PLL_CONTROL_REF_FREF_SEL_MASK (0x1F << 0)
+#define USB3_POWER_PLL_CONTROL_REF_FREF_SEL_SHIFT 0
+#define USB3_PLL_25MHZ 0x2
+#define USB3_PLL_26MHZ 0x5
+#define USB3_POWER_PLL_CONTROL_PHY_MODE_MASK (0x7 << 5)
+#define USB3_POWER_PLL_CONTROL_PHY_MODE_SHIFT 5
+#define USB3_POWER_PLL_CONTROL_PU_MASK (0xF << 12)
+#define USB3_POWER_PLL_CONTROL_PU_SHIFT 12
+#define USB3_POWER_PLL_CONTROL_PU (0xF << 12)
+
+#define USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_MASK (0x1 << 12)
+#define USB3_KVCO_CALI_CONTROL_USE_MAX_PLL_RATE_SHIFT 12
+#define USB3_KVCO_CALI_CONTROL_CAL_DONE_SHIFT 14
+#define USB3_KVCO_CALI_CONTROL_CAL_START_SHIFT 15
+
+#define USB3_SQUELCH_FFE_FFE_CAP_SEL_MASK 0xF
+#define USB3_SQUELCH_FFE_FFE_CAP_SEL_SHIFT 0
+#define USB3_SQUELCH_FFE_FFE_RES_SEL_MASK (0x7 << 4)
+#define USB3_SQUELCH_FFE_FFE_RES_SEL_SHIFT 4
+#define USB3_SQUELCH_FFE_SQ_THRESH_IN_MASK (0x1F << 8)
+#define USB3_SQUELCH_FFE_SQ_THRESH_IN_SHIFT 8
+
+#define USB3_GEN1_SET0_G1_TX_SLEW_CTRL_EN_MASK (0x1 << 15)
+#define USB3_GEN1_SET0_G1_TX_EMPH_EN_SHIFT 11
+
+#define USB3_GEN2_SET0_G2_TX_AMP_MASK (0x1F << 1)
+#define USB3_GEN2_SET0_G2_TX_AMP_SHIFT 1
+#define USB3_GEN2_SET0_G2_TX_AMP_ADJ_SHIFT 6
+#define USB3_GEN2_SET0_G2_TX_EMPH_AMP_MASK (0xF << 7)
+#define USB3_GEN2_SET0_G2_TX_EMPH_AMP_SHIFT 7
+#define USB3_GEN2_SET0_G2_TX_EMPH_EN_MASK (0x1 << 11)
+#define USB3_GEN2_SET0_G2_TX_EMPH_EN_SHIFT 11
+#define USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_MASK (0x1 << 15)
+#define USB3_GEN2_SET0_G2_TX_SLEW_CTRL_EN_SHIFT 15
+
+#define USB3_GEN2_SET1_G2_RX_SELMUPI_MASK (0x7 << 0)
+#define USB3_GEN2_SET1_G2_RX_SELMUPI_SHIFT 0
+#define USB3_GEN2_SET1_G2_RX_SELMUPF_MASK (0x7 << 3)
+#define USB3_GEN2_SET1_G2_RX_SELMUPF_SHIFT 3
+#define USB3_GEN2_SET1_G2_RX_SELMUFI_MASK (0x3 << 6)
+#define USB3_GEN2_SET1_G2_RX_SELMUFI_SHIFT 6
+#define USB3_GEN2_SET1_G2_RX_SELMUFF_MASK (0x3 << 8)
+#define USB3_GEN2_SET1_G2_RX_SELMUFF_SHIFT 8
+
+#define USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_MASK (0x3 << 10)
+#define USB3_DIGITAL_LOOPBACK_EN_SEL_BITS_SHIFT 10
+
+#define USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_MASK (0x7 << 12)
+#define USB3_IMPEDANCE_CALI_CTRL_IMP_CAL_THR_SHIFT 12
+
+#define USB3_IMPEDANCE_TX_SSC_SSC_AMP_MASK (0x3F << 0)
+#define USB3_IMPEDANCE_TX_SSC_SSC_AMP_SHIFT 0
+
+#define USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_MASK 0xF
+#define USB3_PHY_ISOLATION_MODE_PHY_GEN_RX_SHIFT 0
+#define USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_MASK (0xF << 4)
+#define USB3_PHY_ISOLATION_MODE_PHY_GEN_TX_SHIFT 4
+#define USB3_PHY_ISOLATION_MODE_TX_DRV_IDLE_MASK (0x1 << 8)
+
+#define USB3_TXDETRX_VTHSEL_MASK (0x3 << 4)
+#define USB3_TXDETRX_VTHSEL_SHIFT 4
+
+#define USB3_TX_EMPPH_AMP_MASK (0xF << 0)
+#define USB3_TX_EMPPH_AMP_SHIFT 0
+#define USB3_TX_EMPPH_EN_MASK (0x1 << 6)
+#define USB3_TX_EMPPH_EN_SHIFT 6
+#define USB3_TX_EMPPH_AMP_FORCE_MASK (0x1 << 7)
+#define USB3_TX_EMPPH_AMP_FORCE_SHIFT 7
+#define USB3_TX_EMPPH_PAR1_MASK (0x1F << 8)
+#define USB3_TX_EMPPH_PAR1_SHIFT 8
+#define USB3_TX_EMPPH_PAR2_MASK (0x1 << 13)
+#define USB3_TX_EMPPH_PAR2_SHIFT 13
+
+#define USB3_PIPE_SM_CTRL_PHY_INIT_DONE 15
+
+#endif /* __MV_U3D_PHY_H */
diff --git a/drivers/usb/phy/omap-usb2.c b/drivers/usb/phy/omap-usb2.c
new file mode 100644
index 000000000000..15ab3d6f2e8c
--- /dev/null
+++ b/drivers/usb/phy/omap-usb2.c
@@ -0,0 +1,271 @@
+/*
+ * omap-usb2.c - USB PHY, talking to musb controller in OMAP.
+ *
+ * Copyright (C) 2012 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/usb/omap_usb.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+
+/**
+ * omap_usb2_set_comparator - links the comparator present in the sytem with
+ * this phy
+ * @comparator - the companion phy(comparator) for this phy
+ *
+ * The phy companion driver should call this API passing the phy_companion
+ * filled with set_vbus and start_srp to be used by usb phy.
+ *
+ * For use by phy companion driver
+ */
+int omap_usb2_set_comparator(struct phy_companion *comparator)
+{
+ struct omap_usb *phy;
+ struct usb_phy *x = usb_get_phy(USB_PHY_TYPE_USB2);
+
+ if (IS_ERR(x))
+ return -ENODEV;
+
+ phy = phy_to_omapusb(x);
+ phy->comparator = comparator;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
+
+/**
+ * omap_usb_phy_power - power on/off the phy using control module reg
+ * @phy: struct omap_usb *
+ * @on: 0 or 1, based on powering on or off the PHY
+ *
+ * XXX: Remove this function once control module driver gets merged
+ */
+static void omap_usb_phy_power(struct omap_usb *phy, int on)
+{
+ u32 val;
+
+ if (on) {
+ val = readl(phy->control_dev);
+ if (val & PHY_PD) {
+ writel(~PHY_PD, phy->control_dev);
+ /* XXX: add proper documentation for this delay */
+ mdelay(200);
+ }
+ } else {
+ writel(PHY_PD, phy->control_dev);
+ }
+}
+
+static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
+{
+ struct omap_usb *phy = phy_to_omapusb(otg->phy);
+
+ if (!phy->comparator)
+ return -ENODEV;
+
+ return phy->comparator->set_vbus(phy->comparator, enabled);
+}
+
+static int omap_usb_start_srp(struct usb_otg *otg)
+{
+ struct omap_usb *phy = phy_to_omapusb(otg->phy);
+
+ if (!phy->comparator)
+ return -ENODEV;
+
+ return phy->comparator->start_srp(phy->comparator);
+}
+
+static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+ struct usb_phy *phy = otg->phy;
+
+ otg->host = host;
+ if (!host)
+ phy->state = OTG_STATE_UNDEFINED;
+
+ return 0;
+}
+
+static int omap_usb_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ struct usb_phy *phy = otg->phy;
+
+ otg->gadget = gadget;
+ if (!gadget)
+ phy->state = OTG_STATE_UNDEFINED;
+
+ return 0;
+}
+
+static int omap_usb2_suspend(struct usb_phy *x, int suspend)
+{
+ u32 ret;
+ struct omap_usb *phy = phy_to_omapusb(x);
+
+ if (suspend && !phy->is_suspended) {
+ omap_usb_phy_power(phy, 0);
+ pm_runtime_put_sync(phy->dev);
+ phy->is_suspended = 1;
+ } else if (!suspend && phy->is_suspended) {
+ ret = pm_runtime_get_sync(phy->dev);
+ if (ret < 0) {
+ dev_err(phy->dev, "get_sync failed with err %d\n",
+ ret);
+ return ret;
+ }
+ omap_usb_phy_power(phy, 1);
+ phy->is_suspended = 0;
+ }
+
+ return 0;
+}
+
+static int __devinit omap_usb2_probe(struct platform_device *pdev)
+{
+ struct omap_usb *phy;
+ struct usb_otg *otg;
+ struct resource *res;
+
+ phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+ if (!phy) {
+ dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
+ return -ENOMEM;
+ }
+
+ otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
+ if (!otg) {
+ dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n");
+ return -ENOMEM;
+ }
+
+ phy->dev = &pdev->dev;
+
+ phy->phy.dev = phy->dev;
+ phy->phy.label = "omap-usb2";
+ phy->phy.set_suspend = omap_usb2_suspend;
+ phy->phy.otg = otg;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+ phy->control_dev = devm_request_and_ioremap(&pdev->dev, res);
+ if (phy->control_dev == NULL) {
+ dev_err(&pdev->dev, "Failed to obtain io memory\n");
+ return -ENXIO;
+ }
+
+ phy->is_suspended = 1;
+ omap_usb_phy_power(phy, 0);
+
+ otg->set_host = omap_usb_set_host;
+ otg->set_peripheral = omap_usb_set_peripheral;
+ otg->set_vbus = omap_usb_set_vbus;
+ otg->start_srp = omap_usb_start_srp;
+ otg->phy = &phy->phy;
+
+ phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
+ if (IS_ERR(phy->wkupclk)) {
+ dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
+ return PTR_ERR(phy->wkupclk);
+ }
+ clk_prepare(phy->wkupclk);
+
+ usb_add_phy(&phy->phy, USB_PHY_TYPE_USB2);
+
+ platform_set_drvdata(pdev, phy);
+
+ pm_runtime_enable(phy->dev);
+
+ return 0;
+}
+
+static int __devexit omap_usb2_remove(struct platform_device *pdev)
+{
+ struct omap_usb *phy = platform_get_drvdata(pdev);
+
+ clk_unprepare(phy->wkupclk);
+ usb_remove_phy(&phy->phy);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int omap_usb2_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_usb *phy = platform_get_drvdata(pdev);
+
+ clk_disable(phy->wkupclk);
+
+ return 0;
+}
+
+static int omap_usb2_runtime_resume(struct device *dev)
+{
+ u32 ret = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_usb *phy = platform_get_drvdata(pdev);
+
+ ret = clk_enable(phy->wkupclk);
+ if (ret < 0)
+ dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
+
+ return ret;
+}
+
+static const struct dev_pm_ops omap_usb2_pm_ops = {
+ SET_RUNTIME_PM_OPS(omap_usb2_runtime_suspend, omap_usb2_runtime_resume,
+ NULL)
+};
+
+#define DEV_PM_OPS (&omap_usb2_pm_ops)
+#else
+#define DEV_PM_OPS NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id omap_usb2_id_table[] = {
+ { .compatible = "ti,omap-usb2" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
+#endif
+
+static struct platform_driver omap_usb2_driver = {
+ .probe = omap_usb2_probe,
+ .remove = __devexit_p(omap_usb2_remove),
+ .driver = {
+ .name = "omap-usb2",
+ .owner = THIS_MODULE,
+ .pm = DEV_PM_OPS,
+ .of_match_table = of_match_ptr(omap_usb2_id_table),
+ },
+};
+
+module_platform_driver(omap_usb2_driver);
+
+MODULE_ALIAS("platform: omap_usb2");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP USB2 phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/arch/arm/mach-tegra/usb_phy.c b/drivers/usb/phy/tegra_usb_phy.c
index 022b33a05c3a..987116f9efcd 100644
--- a/arch/arm/mach-tegra/usb_phy.c
+++ b/drivers/usb/phy/tegra_usb_phy.c
@@ -1,6 +1,4 @@
/*
- * arch/arm/mach-tegra/usb_phy.c
- *
* Copyright (C) 2010 Google, Inc.
*
* Author:
@@ -30,8 +28,7 @@
#include <linux/usb/otg.h>
#include <linux/usb/ulpi.h>
#include <asm/mach-types.h>
-#include <mach/gpio-tegra.h>
-#include <mach/usb_phy.h>
+#include <linux/usb/tegra_usb_phy.h>
#include <mach/iomap.h>
#define ULPI_VIEWPORT 0x170
@@ -482,7 +479,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy)
return 0;
}
-static void utmi_phy_power_off(struct tegra_usb_phy *phy)
+static int utmi_phy_power_off(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
@@ -514,7 +511,7 @@ static void utmi_phy_power_off(struct tegra_usb_phy *phy)
UTMIP_FORCE_PDDR_POWERDOWN;
writel(val, base + UTMIP_XCVR_CFG1);
- utmip_pad_power_off(phy);
+ return utmip_pad_power_off(phy);
}
static void utmi_phy_preresume(struct tegra_usb_phy *phy)
@@ -638,7 +635,7 @@ static int ulpi_phy_power_on(struct tegra_usb_phy *phy)
return 0;
}
-static void ulpi_phy_power_off(struct tegra_usb_phy *phy)
+static int ulpi_phy_power_off(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
@@ -651,15 +648,92 @@ static void ulpi_phy_power_off(struct tegra_usb_phy *phy)
val &= ~(USB_PORTSC1_WKOC | USB_PORTSC1_WKDS | USB_PORTSC1_WKCN);
writel(val, base + USB_PORTSC1);
- gpio_direction_output(config->reset_gpio, 0);
clk_disable(phy->clk);
+ return gpio_direction_output(config->reset_gpio, 0);
+}
+
+static int tegra_phy_init(struct usb_phy *x)
+{
+ struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+ struct tegra_ulpi_config *ulpi_config;
+ int err;
+
+ if (phy_is_ulpi(phy)) {
+ ulpi_config = phy->config;
+ phy->clk = clk_get_sys(NULL, ulpi_config->clk);
+ if (IS_ERR(phy->clk)) {
+ pr_err("%s: can't get ulpi clock\n", __func__);
+ err = -ENXIO;
+ goto err1;
+ }
+ if (!gpio_is_valid(ulpi_config->reset_gpio))
+ ulpi_config->reset_gpio =
+ of_get_named_gpio(phy->dev->of_node,
+ "nvidia,phy-reset-gpio", 0);
+ if (!gpio_is_valid(ulpi_config->reset_gpio)) {
+ pr_err("%s: invalid reset gpio: %d\n", __func__,
+ ulpi_config->reset_gpio);
+ err = -EINVAL;
+ goto err1;
+ }
+ gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b");
+ gpio_direction_output(ulpi_config->reset_gpio, 0);
+ phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
+ phy->ulpi->io_priv = phy->regs + ULPI_VIEWPORT;
+ } else {
+ err = utmip_pad_open(phy);
+ if (err < 0)
+ goto err1;
+ }
+ return 0;
+err1:
+ clk_disable_unprepare(phy->pll_u);
+ clk_put(phy->pll_u);
+ return err;
+}
+
+static void tegra_usb_phy_close(struct usb_phy *x)
+{
+ struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+
+ if (phy_is_ulpi(phy))
+ clk_put(phy->clk);
+ else
+ utmip_pad_close(phy);
+ clk_disable_unprepare(phy->pll_u);
+ clk_put(phy->pll_u);
+ kfree(phy);
+}
+
+static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
+{
+ if (phy_is_ulpi(phy))
+ return ulpi_phy_power_on(phy);
+ else
+ return utmi_phy_power_on(phy);
+}
+
+static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
+{
+ if (phy_is_ulpi(phy))
+ return ulpi_phy_power_off(phy);
+ else
+ return utmi_phy_power_off(phy);
+}
+
+static int tegra_usb_phy_suspend(struct usb_phy *x, int suspend)
+{
+ struct tegra_usb_phy *phy = container_of(x, struct tegra_usb_phy, u_phy);
+ if (suspend)
+ return tegra_usb_phy_power_off(phy);
+ else
+ return tegra_usb_phy_power_on(phy);
}
struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode)
{
struct tegra_usb_phy *phy;
- struct tegra_ulpi_config *ulpi_config;
unsigned long parent_rate;
int i;
int err;
@@ -672,6 +746,7 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
phy->regs = regs;
phy->config = config;
phy->mode = phy_mode;
+ phy->dev = dev;
if (!phy->config) {
if (phy_is_ulpi(phy)) {
@@ -704,33 +779,9 @@ struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
goto err1;
}
- if (phy_is_ulpi(phy)) {
- ulpi_config = config;
- phy->clk = clk_get_sys(NULL, ulpi_config->clk);
- if (IS_ERR(phy->clk)) {
- pr_err("%s: can't get ulpi clock\n", __func__);
- err = -ENXIO;
- goto err1;
- }
- if (!gpio_is_valid(ulpi_config->reset_gpio))
- ulpi_config->reset_gpio =
- of_get_named_gpio(dev->of_node,
- "nvidia,phy-reset-gpio", 0);
- if (!gpio_is_valid(ulpi_config->reset_gpio)) {
- pr_err("%s: invalid reset gpio: %d\n", __func__,
- ulpi_config->reset_gpio);
- err = -EINVAL;
- goto err1;
- }
- gpio_request(ulpi_config->reset_gpio, "ulpi_phy_reset_b");
- gpio_direction_output(ulpi_config->reset_gpio, 0);
- phy->ulpi = otg_ulpi_create(&ulpi_viewport_access_ops, 0);
- phy->ulpi->io_priv = regs + ULPI_VIEWPORT;
- } else {
- err = utmip_pad_open(phy);
- if (err < 0)
- goto err1;
- }
+ phy->u_phy.init = tegra_phy_init;
+ phy->u_phy.shutdown = tegra_usb_phy_close;
+ phy->u_phy.set_suspend = tegra_usb_phy_suspend;
return phy;
@@ -743,24 +794,6 @@ err0:
}
EXPORT_SYMBOL_GPL(tegra_usb_phy_open);
-int tegra_usb_phy_power_on(struct tegra_usb_phy *phy)
-{
- if (phy_is_ulpi(phy))
- return ulpi_phy_power_on(phy);
- else
- return utmi_phy_power_on(phy);
-}
-EXPORT_SYMBOL_GPL(tegra_usb_phy_power_on);
-
-void tegra_usb_phy_power_off(struct tegra_usb_phy *phy)
-{
- if (phy_is_ulpi(phy))
- ulpi_phy_power_off(phy);
- else
- utmi_phy_power_off(phy);
-}
-EXPORT_SYMBOL_GPL(tegra_usb_phy_power_off);
-
void tegra_usb_phy_preresume(struct tegra_usb_phy *phy)
{
if (!phy_is_ulpi(phy))
@@ -803,15 +836,3 @@ void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy)
utmi_phy_clk_enable(phy);
}
EXPORT_SYMBOL_GPL(tegra_usb_phy_clk_enable);
-
-void tegra_usb_phy_close(struct tegra_usb_phy *phy)
-{
- if (phy_is_ulpi(phy))
- clk_put(phy->clk);
- else
- utmip_pad_close(phy);
- clk_disable_unprepare(phy->pll_u);
- clk_put(phy->pll_u);
- kfree(phy);
-}
-EXPORT_SYMBOL_GPL(tegra_usb_phy_close);
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 681da06170c2..072edc1cc55f 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -432,17 +432,16 @@ static int usbhs_probe(struct platform_device *pdev)
}
/* usb private data */
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
dev_err(&pdev->dev, "Could not allocate priv\n");
return -ENOMEM;
}
- priv->base = ioremap_nocache(res->start, resource_size(res));
+ priv->base = devm_request_and_ioremap(&pdev->dev, res);
if (!priv->base) {
dev_err(&pdev->dev, "ioremap error.\n");
- ret = -ENOMEM;
- goto probe_end_kfree;
+ return -ENOMEM;
}
/*
@@ -485,7 +484,7 @@ static int usbhs_probe(struct platform_device *pdev)
/* call pipe and module init */
ret = usbhs_pipe_probe(priv);
if (ret < 0)
- goto probe_end_iounmap;
+ return ret;
ret = usbhs_fifo_probe(priv);
if (ret < 0)
@@ -546,10 +545,6 @@ probe_end_fifo_exit:
usbhs_fifo_remove(priv);
probe_end_pipe_exit:
usbhs_pipe_remove(priv);
-probe_end_iounmap:
- iounmap(priv->base);
-probe_end_kfree:
- kfree(priv);
dev_info(&pdev->dev, "probe failed\n");
@@ -576,8 +571,6 @@ static int __devexit usbhs_remove(struct platform_device *pdev)
usbhs_mod_remove(priv);
usbhs_fifo_remove(priv);
usbhs_pipe_remove(priv);
- iounmap(priv->base);
- kfree(priv);
return 0;
}
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
index 82a628f96c03..35c5208f3249 100644
--- a/drivers/usb/renesas_usbhs/mod.c
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -209,14 +209,18 @@ int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state)
return (int)irq_state->intsts0 & CTSQ_MASK;
}
-static void usbhs_status_get_each_irq(struct usbhs_priv *priv,
- struct usbhs_irq_state *state)
+static int usbhs_status_get_each_irq(struct usbhs_priv *priv,
+ struct usbhs_irq_state *state)
{
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+ u16 intenb0, intenb1;
state->intsts0 = usbhs_read(priv, INTSTS0);
state->intsts1 = usbhs_read(priv, INTSTS1);
+ intenb0 = usbhs_read(priv, INTENB0);
+ intenb1 = usbhs_read(priv, INTENB1);
+
/* mask */
if (mod) {
state->brdysts = usbhs_read(priv, BRDYSTS);
@@ -226,6 +230,20 @@ static void usbhs_status_get_each_irq(struct usbhs_priv *priv,
state->bempsts &= mod->irq_bempsts;
state->brdysts &= mod->irq_brdysts;
}
+
+ /*
+ * Check whether the irq enable registers and the irq status are set
+ * when IRQF_SHARED is set.
+ */
+ if (priv->irqflags & IRQF_SHARED) {
+ if (!(intenb0 & state->intsts0) &&
+ !(intenb1 & state->intsts1) &&
+ !(state->bempsts) &&
+ !(state->brdysts))
+ return -EIO;
+ }
+
+ return 0;
}
/*
@@ -238,7 +256,8 @@ static irqreturn_t usbhs_interrupt(int irq, void *data)
struct usbhs_priv *priv = data;
struct usbhs_irq_state irq_state;
- usbhs_status_get_each_irq(priv, &irq_state);
+ if (usbhs_status_get_each_irq(priv, &irq_state) < 0)
+ return IRQ_NONE;
/*
* clear interrupt
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index 325d2910f9f9..76f462241738 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -42,11 +42,6 @@ config USB_SERIAL_CONSOLE
If unsure, say N.
-config USB_EZUSB
- bool "Functions for loading firmware on EZUSB chips"
- help
- Say Y here if you need EZUSB device support.
-
config USB_SERIAL_GENERIC
bool "USB Generic Serial Driver"
help
@@ -94,7 +89,7 @@ config USB_SERIAL_CH341
config USB_SERIAL_WHITEHEAT
tristate "USB ConnectTech WhiteHEAT Serial Driver"
- select USB_EZUSB
+ select USB_EZUSB_FX2
help
Say Y here if you want to use a ConnectTech WhiteHEAT 4 port
USB to serial converter device.
@@ -281,7 +276,7 @@ config USB_SERIAL_IUU
config USB_SERIAL_KEYSPAN_PDA
tristate "USB Keyspan PDA Single Port Serial Driver"
- select USB_EZUSB
+ select USB_EZUSB_FX2
help
Say Y here if you want to use a Keyspan PDA single port USB to
serial converter device. This driver makes use of firmware
@@ -292,7 +287,7 @@ config USB_SERIAL_KEYSPAN_PDA
config USB_SERIAL_KEYSPAN
tristate "USB Keyspan USA-xxx Serial Driver"
- select USB_EZUSB
+ select USB_EZUSB_FX2
---help---
Say Y here if you want to use Keyspan USB to serial converter
devices. This driver makes use of Keyspan's official firmware
@@ -596,7 +591,7 @@ config USB_SERIAL_CYBERJACK
config USB_SERIAL_XIRCOM
tristate "USB Xircom / Entregra Single Port Serial Driver"
- select USB_EZUSB
+ select USB_EZUSB_FX2
help
Say Y here if you want to use a Xircom or Entregra single port USB to
serial converter device. This driver makes use of firmware
@@ -660,6 +655,14 @@ config USB_SERIAL_ZIO
To compile this driver as a module, choose M here: the
module will be called zio.
+config USB_SERIAL_ZTE
+ tristate "ZTE USB serial driver"
+ help
+ Say Y here if you want to use a ZTE USB to serial device.
+
+ To compile this driver as a module, choose M here: the
+ module will be called zte.
+
config USB_SERIAL_SSU100
tristate "USB Quatech SSU-100 Single Port Serial Driver"
help
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 1dc483a8bfc7..3b3e7308d476 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -9,7 +9,6 @@ obj-$(CONFIG_USB_SERIAL) += usbserial.o
usbserial-y := usb-serial.o generic.o bus.o
usbserial-$(CONFIG_USB_SERIAL_CONSOLE) += console.o
-usbserial-$(CONFIG_USB_EZUSB) += ezusb.o
obj-$(CONFIG_USB_SERIAL_AIRCABLE) += aircable.o
obj-$(CONFIG_USB_SERIAL_ARK3116) += ark3116.o
@@ -63,3 +62,4 @@ obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o
obj-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda.o
obj-$(CONFIG_USB_SERIAL_VIVOPAY_SERIAL) += vivopay-serial.o
obj-$(CONFIG_USB_SERIAL_ZIO) += zio.o
+obj-$(CONFIG_USB_SERIAL_ZTE) += zte_ev.o
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
index d634e6635632..54e1bb6372e7 100644
--- a/drivers/usb/serial/aircable.c
+++ b/drivers/usb/serial/aircable.c
@@ -52,8 +52,6 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
-static bool debug;
-
/* Vendor and Product ID */
#define AIRCABLE_VID 0x16CA
#define AIRCABLE_USB_PID 0x1502
@@ -196,6 +194,3 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 3b98fb733362..cf2522c397d3 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -37,7 +37,6 @@
#include <linux/mutex.h>
#include <linux/spinlock.h>
-static bool debug;
/*
* Version information
*/
@@ -650,8 +649,7 @@ static void ark3116_read_int_callback(struct urb *urb)
/*
* Not sure what this data meant...
*/
- usb_serial_debug_data(debug, &port->dev,
- __func__,
+ usb_serial_debug_data(&port->dev, __func__,
urb->actual_length,
urb->transfer_buffer);
break;
@@ -750,9 +748,6 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debug");
-
/*
* The following describes what I learned from studying the old
* ark3116.c driver, disassembling the windows driver, and some lucky
diff --git a/drivers/usb/serial/belkin_sa.c b/drivers/usb/serial/belkin_sa.c
index a46df73ee96e..99449424193f 100644
--- a/drivers/usb/serial/belkin_sa.c
+++ b/drivers/usb/serial/belkin_sa.c
@@ -37,8 +37,6 @@
#include <linux/usb/serial.h>
#include "belkin_sa.h"
-static bool debug;
-
/*
* Version Information
*/
@@ -206,8 +204,7 @@ static void belkin_sa_read_int_callback(struct urb *urb)
goto exit;
}
- usb_serial_debug_data(debug, &port->dev, __func__,
- urb->actual_length, data);
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
/* Handle known interrupt data */
/* ignore data[0] and data[1] */
@@ -515,6 +512,3 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/ch341.c b/drivers/usb/serial/ch341.c
index cabd1b15ddce..e9c7046ae355 100644
--- a/drivers/usb/serial/ch341.c
+++ b/drivers/usb/serial/ch341.c
@@ -70,8 +70,6 @@
#define CH341_NBREAK_BITS_REG2 0x40
-static bool debug;
-
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x4348, 0x5523) },
{ USB_DEVICE(0x1a86, 0x7523) },
@@ -93,8 +91,9 @@ static int ch341_control_out(struct usb_device *dev, u8 request,
u16 value, u16 index)
{
int r;
- dbg("ch341_control_out(%02x,%02x,%04x,%04x)", USB_DIR_OUT|0x40,
- (int)request, (int)value, (int)index);
+
+ dev_dbg(&dev->dev, "ch341_control_out(%02x,%02x,%04x,%04x)\n",
+ USB_DIR_OUT|0x40, (int)request, (int)value, (int)index);
r = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
@@ -108,8 +107,10 @@ static int ch341_control_in(struct usb_device *dev,
char *buf, unsigned bufsize)
{
int r;
- dbg("ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)", USB_DIR_IN|0x40,
- (int)request, (int)value, (int)index, buf, (int)bufsize);
+
+ dev_dbg(&dev->dev, "ch341_control_in(%02x,%02x,%04x,%04x,%p,%u)\n",
+ USB_DIR_IN|0x40, (int)request, (int)value, (int)index, buf,
+ (int)bufsize);
r = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), request,
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
@@ -320,7 +321,7 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
if (r)
goto out;
- dbg("%s - submitting interrupt urb", __func__);
+ dev_dbg(&port->dev, "%s - submitting interrupt urb", __func__);
r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (r) {
dev_err(&port->dev, "%s - failed submitting interrupt urb,"
@@ -390,19 +391,19 @@ static void ch341_break_ctl(struct tty_struct *tty, int break_state)
__func__, r);
goto out;
}
- dbg("%s - initial ch341 break register contents - reg1: %x, reg2: %x",
- __func__, break_reg[0], break_reg[1]);
+ dev_dbg(&port->dev, "%s - initial ch341 break register contents - reg1: %x, reg2: %x\n",
+ __func__, break_reg[0], break_reg[1]);
if (break_state != 0) {
- dbg("%s - Enter break state requested", __func__);
+ dev_dbg(&port->dev, "%s - Enter break state requested\n", __func__);
break_reg[0] &= ~CH341_NBREAK_BITS_REG1;
break_reg[1] &= ~CH341_NBREAK_BITS_REG2;
} else {
- dbg("%s - Leave break state requested", __func__);
+ dev_dbg(&port->dev, "%s - Leave break state requested\n", __func__);
break_reg[0] |= CH341_NBREAK_BITS_REG1;
break_reg[1] |= CH341_NBREAK_BITS_REG2;
}
- dbg("%s - New ch341 break register contents - reg1: %x, reg2: %x",
- __func__, break_reg[0], break_reg[1]);
+ dev_dbg(&port->dev, "%s - New ch341 break register contents - reg1: %x, reg2: %x\n",
+ __func__, break_reg[0], break_reg[1]);
reg_contents = get_unaligned_le16(break_reg);
r = ch341_control_out(port->serial->dev, CH341_REQ_WRITE_REG,
ch341_break_reg, reg_contents);
@@ -451,16 +452,16 @@ static void ch341_read_int_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __func__,
- urb->status);
+ dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
+ __func__, urb->status);
return;
default:
- dbg("%s - nonzero urb status received: %d", __func__,
- urb->status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n",
+ __func__, urb->status);
goto exit;
}
- usb_serial_debug_data(debug, &port->dev, __func__,
+ usb_serial_debug_data(&port->dev, __func__,
urb->actual_length, urb->transfer_buffer);
if (actual_length >= 4) {
@@ -536,15 +537,16 @@ static int ch341_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s (%d) cmd = 0x%04x", __func__, port->number, cmd);
+
+ dev_dbg(&port->dev, "%s (%d) cmd = 0x%04x\n", __func__, port->number, cmd);
switch (cmd) {
case TIOCMIWAIT:
- dbg("%s (%d) TIOCMIWAIT", __func__, port->number);
+ dev_dbg(&port->dev, "%s (%d) TIOCMIWAIT\n", __func__, port->number);
return wait_modem_info(port, arg);
default:
- dbg("%s not supported = 0x%04x", __func__, cmd);
+ dev_dbg(&port->dev, "%s not supported = 0x%04x\n", __func__, cmd);
break;
}
@@ -572,7 +574,7 @@ static int ch341_tiocmget(struct tty_struct *tty)
| ((status & CH341_BIT_RI) ? TIOCM_RI : 0)
| ((status & CH341_BIT_DCD) ? TIOCM_CD : 0);
- dbg("%s - result = %x", __func__, result);
+ dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
return result;
}
@@ -617,6 +619,3 @@ static struct usb_serial_driver * const serial_drivers[] = {
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/console.c b/drivers/usb/serial/console.c
index 9a564286bfd7..5f3bcd31e204 100644
--- a/drivers/usb/serial/console.c
+++ b/drivers/usb/serial/console.c
@@ -11,6 +11,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -20,8 +22,6 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
-static int debug;
-
struct usbcons_info {
int magic;
int break_flag;
@@ -68,8 +68,6 @@ static int usb_console_setup(struct console *co, char *options)
struct tty_struct *tty = NULL;
struct ktermios dummy;
- dbg("%s", __func__);
-
if (options) {
baud = simple_strtoul(options, NULL, 10);
s = options;
@@ -113,8 +111,7 @@ static int usb_console_setup(struct console *co, char *options)
serial = usb_serial_get_by_index(co->index);
if (serial == NULL) {
/* no device is connected yet, sorry :( */
- printk(KERN_ERR "No USB device connected to ttyUSB%i\n",
- co->index);
+ pr_err("No USB device connected to ttyUSB%i\n", co->index);
return -ENODEV;
}
@@ -213,10 +210,10 @@ static void usb_console_write(struct console *co,
if (count == 0)
return;
- dbg("%s - port %d, %d byte(s)", __func__, port->number, count);
+ pr_debug("%s - port %d, %d byte(s)\n", __func__, port->number, count);
if (!port->port.console) {
- dbg("%s - port not opened", __func__);
+ pr_debug("%s - port not opened\n", __func__);
return;
}
@@ -237,7 +234,7 @@ static void usb_console_write(struct console *co,
retval = serial->type->write(NULL, port, buf, i);
else
retval = usb_serial_generic_write(NULL, port, buf, i);
- dbg("%s - return value : %d", __func__, retval);
+ pr_debug("%s - return value : %d\n", __func__, retval);
if (lf) {
/* append CR after LF */
unsigned char cr = 13;
@@ -247,7 +244,7 @@ static void usb_console_write(struct console *co,
else
retval = usb_serial_generic_write(NULL,
port, &cr, 1);
- dbg("%s - return value : %d", __func__, retval);
+ pr_debug("%s - return value : %d\n", __func__, retval);
}
buf += i;
count -= i;
@@ -284,10 +281,8 @@ void usb_serial_console_disconnect(struct usb_serial *serial)
}
}
-void usb_serial_console_init(int serial_debug, int minor)
+void usb_serial_console_init(int minor)
{
- debug = serial_debug;
-
if (minor == 0) {
/*
* Call register_console() if this is the first device plugged
@@ -302,7 +297,7 @@ void usb_serial_console_init(int serial_debug, int minor)
* register_console). console_write() is called immediately
* from register_console iff CON_PRINTBUFFER is set in flags.
*/
- dbg("registering the USB serial console.");
+ pr_debug("registering the USB serial console.\n");
register_console(&usbcons);
}
}
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index ba5e07e188a0..28af5acc3360 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -52,8 +52,6 @@ static int cp210x_startup(struct usb_serial *);
static void cp210x_release(struct usb_serial *);
static void cp210x_dtr_rts(struct usb_serial_port *p, int on);
-static bool debug;
-
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x045B, 0x0053) }, /* Renesas RX610 RX-Stick */
{ USB_DEVICE(0x0471, 0x066A) }, /* AKTAKOM ACE-1001 cable */
@@ -304,9 +302,8 @@ static int cp210x_get_config(struct usb_serial_port *port, u8 request,
kfree(buf);
if (result != size) {
- dbg("%s - Unable to send config request, "
- "request=0x%x size=%d result=%d",
- __func__, request, size, result);
+ dev_dbg(&port->dev, "%s - Unable to send config request, request=0x%x size=%d result=%d\n",
+ __func__, request, size, result);
if (result > 0)
result = -EPROTO;
@@ -361,9 +358,8 @@ static int cp210x_set_config(struct usb_serial_port *port, u8 request,
kfree(buf);
if ((size > 2 && result != size) || result < 0) {
- dbg("%s - Unable to send request, "
- "request=0x%x size=%d result=%d",
- __func__, request, size, result);
+ dev_dbg(&port->dev, "%s - Unable to send request, request=0x%x size=%d result=%d\n",
+ __func__, request, size, result);
if (result > 0)
result = -EPROTO;
@@ -487,13 +483,14 @@ static void cp210x_get_termios(struct tty_struct *tty,
static void cp210x_get_termios_port(struct usb_serial_port *port,
unsigned int *cflagp, unsigned int *baudp)
{
+ struct device *dev = &port->dev;
unsigned int cflag, modem_ctl[4];
unsigned int baud;
unsigned int bits;
cp210x_get_config(port, CP210X_GET_BAUDRATE, &baud, 4);
- dbg("%s - baud rate = %d", __func__, baud);
+ dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud);
*baudp = baud;
cflag = *cflagp;
@@ -502,31 +499,30 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
cflag &= ~CSIZE;
switch (bits & BITS_DATA_MASK) {
case BITS_DATA_5:
- dbg("%s - data bits = 5", __func__);
+ dev_dbg(dev, "%s - data bits = 5\n", __func__);
cflag |= CS5;
break;
case BITS_DATA_6:
- dbg("%s - data bits = 6", __func__);
+ dev_dbg(dev, "%s - data bits = 6\n", __func__);
cflag |= CS6;
break;
case BITS_DATA_7:
- dbg("%s - data bits = 7", __func__);
+ dev_dbg(dev, "%s - data bits = 7\n", __func__);
cflag |= CS7;
break;
case BITS_DATA_8:
- dbg("%s - data bits = 8", __func__);
+ dev_dbg(dev, "%s - data bits = 8\n", __func__);
cflag |= CS8;
break;
case BITS_DATA_9:
- dbg("%s - data bits = 9 (not supported, using 8 data bits)",
- __func__);
+ dev_dbg(dev, "%s - data bits = 9 (not supported, using 8 data bits)\n", __func__);
cflag |= CS8;
bits &= ~BITS_DATA_MASK;
bits |= BITS_DATA_8;
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
default:
- dbg("%s - Unknown number of data bits, using 8", __func__);
+ dev_dbg(dev, "%s - Unknown number of data bits, using 8\n", __func__);
cflag |= CS8;
bits &= ~BITS_DATA_MASK;
bits |= BITS_DATA_8;
@@ -536,29 +532,29 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
switch (bits & BITS_PARITY_MASK) {
case BITS_PARITY_NONE:
- dbg("%s - parity = NONE", __func__);
+ dev_dbg(dev, "%s - parity = NONE\n", __func__);
cflag &= ~PARENB;
break;
case BITS_PARITY_ODD:
- dbg("%s - parity = ODD", __func__);
+ dev_dbg(dev, "%s - parity = ODD\n", __func__);
cflag |= (PARENB|PARODD);
break;
case BITS_PARITY_EVEN:
- dbg("%s - parity = EVEN", __func__);
+ dev_dbg(dev, "%s - parity = EVEN\n", __func__);
cflag &= ~PARODD;
cflag |= PARENB;
break;
case BITS_PARITY_MARK:
- dbg("%s - parity = MARK", __func__);
+ dev_dbg(dev, "%s - parity = MARK\n", __func__);
cflag |= (PARENB|PARODD|CMSPAR);
break;
case BITS_PARITY_SPACE:
- dbg("%s - parity = SPACE", __func__);
+ dev_dbg(dev, "%s - parity = SPACE\n", __func__);
cflag &= ~PARODD;
cflag |= (PARENB|CMSPAR);
break;
default:
- dbg("%s - Unknown parity mode, disabling parity", __func__);
+ dev_dbg(dev, "%s - Unknown parity mode, disabling parity\n", __func__);
cflag &= ~PARENB;
bits &= ~BITS_PARITY_MASK;
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
@@ -568,21 +564,19 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
cflag &= ~CSTOPB;
switch (bits & BITS_STOP_MASK) {
case BITS_STOP_1:
- dbg("%s - stop bits = 1", __func__);
+ dev_dbg(dev, "%s - stop bits = 1\n", __func__);
break;
case BITS_STOP_1_5:
- dbg("%s - stop bits = 1.5 (not supported, using 1 stop bit)",
- __func__);
+ dev_dbg(dev, "%s - stop bits = 1.5 (not supported, using 1 stop bit)\n", __func__);
bits &= ~BITS_STOP_MASK;
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
case BITS_STOP_2:
- dbg("%s - stop bits = 2", __func__);
+ dev_dbg(dev, "%s - stop bits = 2\n", __func__);
cflag |= CSTOPB;
break;
default:
- dbg("%s - Unknown number of stop bits, using 1 stop bit",
- __func__);
+ dev_dbg(dev, "%s - Unknown number of stop bits, using 1 stop bit\n", __func__);
bits &= ~BITS_STOP_MASK;
cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2);
break;
@@ -590,10 +584,10 @@ static void cp210x_get_termios_port(struct usb_serial_port *port,
cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
if (modem_ctl[0] & 0x0008) {
- dbg("%s - flow control = CRTSCTS", __func__);
+ dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__);
cflag |= CRTSCTS;
} else {
- dbg("%s - flow control = NONE", __func__);
+ dev_dbg(dev, "%s - flow control = NONE\n", __func__);
cflag &= ~CRTSCTS;
}
@@ -640,7 +634,7 @@ static void cp210x_change_speed(struct tty_struct *tty,
*/
baud = cp210x_quantise_baudrate(baud);
- dbg("%s - setting baud rate to %u", __func__, baud);
+ dev_dbg(&port->dev, "%s - setting baud rate to %u\n", __func__, baud);
if (cp210x_set_config(port, CP210X_SET_BAUDRATE, &baud,
sizeof(baud))) {
dev_warn(&port->dev, "failed to set baud rate to %u\n", baud);
@@ -656,11 +650,12 @@ static void cp210x_change_speed(struct tty_struct *tty,
static void cp210x_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
+ struct device *dev = &port->dev;
unsigned int cflag, old_cflag;
unsigned int bits;
unsigned int modem_ctl[4];
- dbg("%s - port %d", __func__, port->number);
+ dev_dbg(dev, "%s - port %d\n", __func__, port->number);
if (!tty)
return;
@@ -678,34 +673,31 @@ static void cp210x_set_termios(struct tty_struct *tty,
switch (cflag & CSIZE) {
case CS5:
bits |= BITS_DATA_5;
- dbg("%s - data bits = 5", __func__);
+ dev_dbg(dev, "%s - data bits = 5\n", __func__);
break;
case CS6:
bits |= BITS_DATA_6;
- dbg("%s - data bits = 6", __func__);
+ dev_dbg(dev, "%s - data bits = 6\n", __func__);
break;
case CS7:
bits |= BITS_DATA_7;
- dbg("%s - data bits = 7", __func__);
+ dev_dbg(dev, "%s - data bits = 7\n", __func__);
break;
case CS8:
bits |= BITS_DATA_8;
- dbg("%s - data bits = 8", __func__);
+ dev_dbg(dev, "%s - data bits = 8\n", __func__);
break;
/*case CS9:
bits |= BITS_DATA_9;
- dbg("%s - data bits = 9", __func__);
+ dev_dbg(dev, "%s - data bits = 9\n", __func__);
break;*/
default:
- dbg("cp210x driver does not "
- "support the number of bits requested,"
- " using 8 bit mode");
+ dev_dbg(dev, "cp210x driver does not support the number of bits requested, using 8 bit mode\n");
bits |= BITS_DATA_8;
break;
}
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
- dbg("Number of data bits requested "
- "not supported by device");
+ dev_dbg(dev, "Number of data bits requested not supported by device\n");
}
if ((cflag & (PARENB|PARODD|CMSPAR)) !=
@@ -714,25 +706,25 @@ static void cp210x_set_termios(struct tty_struct *tty,
bits &= ~BITS_PARITY_MASK;
if (cflag & PARENB) {
if (cflag & CMSPAR) {
- if (cflag & PARODD) {
- bits |= BITS_PARITY_MARK;
- dbg("%s - parity = MARK", __func__);
- } else {
- bits |= BITS_PARITY_SPACE;
- dbg("%s - parity = SPACE", __func__);
- }
+ if (cflag & PARODD) {
+ bits |= BITS_PARITY_MARK;
+ dev_dbg(dev, "%s - parity = MARK\n", __func__);
+ } else {
+ bits |= BITS_PARITY_SPACE;
+ dev_dbg(dev, "%s - parity = SPACE\n", __func__);
+ }
} else {
- if (cflag & PARODD) {
- bits |= BITS_PARITY_ODD;
- dbg("%s - parity = ODD", __func__);
- } else {
- bits |= BITS_PARITY_EVEN;
- dbg("%s - parity = EVEN", __func__);
- }
+ if (cflag & PARODD) {
+ bits |= BITS_PARITY_ODD;
+ dev_dbg(dev, "%s - parity = ODD\n", __func__);
+ } else {
+ bits |= BITS_PARITY_EVEN;
+ dev_dbg(dev, "%s - parity = EVEN\n", __func__);
+ }
}
}
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
- dbg("Parity mode not supported by device");
+ dev_dbg(dev, "Parity mode not supported by device\n");
}
if ((cflag & CSTOPB) != (old_cflag & CSTOPB)) {
@@ -740,37 +732,36 @@ static void cp210x_set_termios(struct tty_struct *tty,
bits &= ~BITS_STOP_MASK;
if (cflag & CSTOPB) {
bits |= BITS_STOP_2;
- dbg("%s - stop bits = 2", __func__);
+ dev_dbg(dev, "%s - stop bits = 2\n", __func__);
} else {
bits |= BITS_STOP_1;
- dbg("%s - stop bits = 1", __func__);
+ dev_dbg(dev, "%s - stop bits = 1\n", __func__);
}
if (cp210x_set_config(port, CP210X_SET_LINE_CTL, &bits, 2))
- dbg("Number of stop bits requested "
- "not supported by device");
+ dev_dbg(dev, "Number of stop bits requested not supported by device\n");
}
if ((cflag & CRTSCTS) != (old_cflag & CRTSCTS)) {
cp210x_get_config(port, CP210X_GET_FLOW, modem_ctl, 16);
- dbg("%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x",
- __func__, modem_ctl[0], modem_ctl[1],
- modem_ctl[2], modem_ctl[3]);
+ dev_dbg(dev, "%s - read modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x\n",
+ __func__, modem_ctl[0], modem_ctl[1],
+ modem_ctl[2], modem_ctl[3]);
if (cflag & CRTSCTS) {
modem_ctl[0] &= ~0x7B;
modem_ctl[0] |= 0x09;
modem_ctl[1] = 0x80;
- dbg("%s - flow control = CRTSCTS", __func__);
+ dev_dbg(dev, "%s - flow control = CRTSCTS\n", __func__);
} else {
modem_ctl[0] &= ~0x7B;
modem_ctl[0] |= 0x01;
modem_ctl[1] |= 0x40;
- dbg("%s - flow control = NONE", __func__);
+ dev_dbg(dev, "%s - flow control = NONE\n", __func__);
}
- dbg("%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x",
- __func__, modem_ctl[0], modem_ctl[1],
- modem_ctl[2], modem_ctl[3]);
+ dev_dbg(dev, "%s - write modem controls = 0x%.4x 0x%.4x 0x%.4x 0x%.4x\n",
+ __func__, modem_ctl[0], modem_ctl[1],
+ modem_ctl[2], modem_ctl[3]);
cp210x_set_config(port, CP210X_SET_FLOW, modem_ctl, 16);
}
@@ -805,7 +796,7 @@ static int cp210x_tiocmset_port(struct usb_serial_port *port,
control |= CONTROL_WRITE_DTR;
}
- dbg("%s - control = 0x%.4x", __func__, control);
+ dev_dbg(&port->dev, "%s - control = 0x%.4x\n", __func__, control);
return cp210x_set_config(port, CP210X_SET_MHS, &control, 2);
}
@@ -833,7 +824,7 @@ static int cp210x_tiocmget (struct tty_struct *tty)
|((control & CONTROL_RING)? TIOCM_RI : 0)
|((control & CONTROL_DCD) ? TIOCM_CD : 0);
- dbg("%s - control = 0x%.2x", __func__, control);
+ dev_dbg(&port->dev, "%s - control = 0x%.2x\n", __func__, control);
return result;
}
@@ -847,8 +838,8 @@ static void cp210x_break_ctl (struct tty_struct *tty, int break_state)
state = BREAK_OFF;
else
state = BREAK_ON;
- dbg("%s - turning break %s", __func__,
- state == BREAK_OFF ? "off" : "on");
+ dev_dbg(&port->dev, "%s - turning break %s\n", __func__,
+ state == BREAK_OFF ? "off" : "on");
cp210x_set_config(port, CP210X_SET_BREAK, &state, 2);
}
@@ -865,7 +856,6 @@ static int cp210x_startup(struct usb_serial *serial)
if (!port_priv)
return -ENOMEM;
- memset(port_priv, 0x00, sizeof(*port_priv));
port_priv->bInterfaceNumber =
serial->interface->cur_altsetting->desc.bInterfaceNumber;
@@ -892,6 +882,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable verbose debugging messages");
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 3aa0b530f68e..2a7aecc72237 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -43,8 +43,6 @@
#define CYBERJACK_LOCAL_BUF_SIZE 32
-static bool debug;
-
/*
* Version Information
*/
@@ -136,7 +134,8 @@ static int cyberjack_startup(struct usb_serial *serial)
if (result)
dev_err(&serial->dev->dev,
"usb_submit_urb(read int) failed\n");
- dbg("%s - usb_submit_urb(int urb)", __func__);
+ dev_dbg(&serial->dev->dev, "%s - usb_submit_urb(int urb)\n",
+ __func__);
}
return 0;
@@ -167,7 +166,7 @@ static int cyberjack_open(struct tty_struct *tty,
unsigned long flags;
int result = 0;
- dbg("%s - usb_clear_halt", __func__);
+ dev_dbg(&port->dev, "%s - usb_clear_halt\n", __func__);
usb_clear_halt(port->serial->dev, port->write_urb->pipe);
priv = usb_get_serial_port_data(port);
@@ -192,18 +191,19 @@ static void cyberjack_close(struct usb_serial_port *port)
static int cyberjack_write(struct tty_struct *tty,
struct usb_serial_port *port, const unsigned char *buf, int count)
{
+ struct device *dev = &port->dev;
struct cyberjack_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
int result;
int wrexpected;
if (count == 0) {
- dbg("%s - write request of 0 bytes", __func__);
+ dev_dbg(dev, "%s - write request of 0 bytes\n", __func__);
return 0;
}
if (!test_and_clear_bit(0, &port->write_urbs_free)) {
- dbg("%s - already writing", __func__);
+ dev_dbg(dev, "%s - already writing\n", __func__);
return 0;
}
@@ -220,13 +220,12 @@ static int cyberjack_write(struct tty_struct *tty,
/* Copy data */
memcpy(priv->wrbuf + priv->wrfilled, buf, count);
- usb_serial_debug_data(debug, &port->dev, __func__, count,
- priv->wrbuf + priv->wrfilled);
+ usb_serial_debug_data(dev, __func__, count, priv->wrbuf + priv->wrfilled);
priv->wrfilled += count;
if (priv->wrfilled >= 3) {
wrexpected = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3;
- dbg("%s - expected data: %d", __func__, wrexpected);
+ dev_dbg(dev, "%s - expected data: %d\n", __func__, wrexpected);
} else
wrexpected = sizeof(priv->wrbuf);
@@ -234,7 +233,7 @@ static int cyberjack_write(struct tty_struct *tty,
/* We have enough data to begin transmission */
int length;
- dbg("%s - transmitting data (frame 1)", __func__);
+ dev_dbg(dev, "%s - transmitting data (frame 1)\n", __func__);
length = (wrexpected > port->bulk_out_size) ?
port->bulk_out_size : wrexpected;
@@ -258,11 +257,11 @@ static int cyberjack_write(struct tty_struct *tty,
return 0;
}
- dbg("%s - priv->wrsent=%d", __func__, priv->wrsent);
- dbg("%s - priv->wrfilled=%d", __func__, priv->wrfilled);
+ dev_dbg(dev, "%s - priv->wrsent=%d\n", __func__, priv->wrsent);
+ dev_dbg(dev, "%s - priv->wrfilled=%d\n", __func__, priv->wrfilled);
if (priv->wrsent >= priv->wrfilled) {
- dbg("%s - buffer cleaned", __func__);
+ dev_dbg(dev, "%s - buffer cleaned\n", __func__);
memset(priv->wrbuf, 0, sizeof(priv->wrbuf));
priv->wrfilled = 0;
priv->wrsent = 0;
@@ -284,6 +283,7 @@ static void cyberjack_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct cyberjack_private *priv = usb_get_serial_port_data(port);
+ struct device *dev = &port->dev;
unsigned char *data = urb->transfer_buffer;
int status = urb->status;
int result;
@@ -292,8 +292,7 @@ static void cyberjack_read_int_callback(struct urb *urb)
if (status)
return;
- usb_serial_debug_data(debug, &port->dev, __func__,
- urb->actual_length, data);
+ usb_serial_debug_data(dev, __func__, urb->actual_length, data);
/* React only to interrupts signaling a bulk_in transfer */
if (urb->actual_length == 4 && data[0] == 0x01) {
@@ -307,7 +306,7 @@ static void cyberjack_read_int_callback(struct urb *urb)
old_rdtodo = priv->rdtodo;
if (old_rdtodo + size < old_rdtodo) {
- dbg("To many bulk_in urbs to do.");
+ dev_dbg(dev, "To many bulk_in urbs to do.\n");
spin_unlock(&priv->lock);
goto resubmit;
}
@@ -315,17 +314,16 @@ static void cyberjack_read_int_callback(struct urb *urb)
/* "+=" is probably more fault tollerant than "=" */
priv->rdtodo += size;
- dbg("%s - rdtodo: %d", __func__, priv->rdtodo);
+ dev_dbg(dev, "%s - rdtodo: %d\n", __func__, priv->rdtodo);
spin_unlock(&priv->lock);
if (!old_rdtodo) {
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result)
- dev_err(&port->dev, "%s - failed resubmitting "
- "read urb, error %d\n",
+ dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
__func__, result);
- dbg("%s - usb_submit_urb(read urb)", __func__);
+ dev_dbg(dev, "%s - usb_submit_urb(read urb)\n", __func__);
}
}
@@ -333,30 +331,30 @@ resubmit:
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
if (result)
dev_err(&port->dev, "usb_submit_urb(read int) failed\n");
- dbg("%s - usb_submit_urb(int urb)", __func__);
+ dev_dbg(dev, "%s - usb_submit_urb(int urb)\n", __func__);
}
static void cyberjack_read_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct cyberjack_private *priv = usb_get_serial_port_data(port);
+ struct device *dev = &port->dev;
struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
short todo;
int result;
int status = urb->status;
- usb_serial_debug_data(debug, &port->dev, __func__,
- urb->actual_length, data);
+ usb_serial_debug_data(dev, __func__, urb->actual_length, data);
if (status) {
- dbg("%s - nonzero read bulk status received: %d",
- __func__, status);
+ dev_dbg(dev, "%s - nonzero read bulk status received: %d\n",
+ __func__, status);
return;
}
tty = tty_port_tty_get(&port->port);
if (!tty) {
- dbg("%s - ignoring since device not open", __func__);
+ dev_dbg(dev, "%s - ignoring since device not open\n", __func__);
return;
}
if (urb->actual_length) {
@@ -376,15 +374,15 @@ static void cyberjack_read_bulk_callback(struct urb *urb)
spin_unlock(&priv->lock);
- dbg("%s - rdtodo: %d", __func__, todo);
+ dev_dbg(dev, "%s - rdtodo: %d\n", __func__, todo);
/* Continue to read if we have still urbs to do. */
if (todo /* || (urb->actual_length==port->bulk_in_endpointAddress)*/) {
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (result)
- dev_err(&port->dev, "%s - failed resubmitting read "
- "urb, error %d\n", __func__, result);
- dbg("%s - usb_submit_urb(read urb)", __func__);
+ dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
+ __func__, result);
+ dev_dbg(dev, "%s - usb_submit_urb(read urb)\n", __func__);
}
}
@@ -392,12 +390,13 @@ static void cyberjack_write_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct cyberjack_private *priv = usb_get_serial_port_data(port);
+ struct device *dev = &port->dev;
int status = urb->status;
set_bit(0, &port->write_urbs_free);
if (status) {
- dbg("%s - nonzero write bulk status received: %d",
- __func__, status);
+ dev_dbg(dev, "%s - nonzero write bulk status received: %d\n",
+ __func__, status);
return;
}
@@ -407,7 +406,7 @@ static void cyberjack_write_bulk_callback(struct urb *urb)
if (priv->wrfilled) {
int length, blksize, result;
- dbg("%s - transmitting data (frame n)", __func__);
+ dev_dbg(dev, "%s - transmitting data (frame n)\n", __func__);
length = ((priv->wrfilled - priv->wrsent) > port->bulk_out_size) ?
port->bulk_out_size : (priv->wrfilled - priv->wrsent);
@@ -422,8 +421,7 @@ static void cyberjack_write_bulk_callback(struct urb *urb)
/* send the data out the bulk port */
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result) {
- dev_err(&port->dev,
- "%s - failed submitting write urb, error %d\n",
+ dev_err(dev, "%s - failed submitting write urb, error %d\n",
__func__, result);
/* Throw away data. No better idea what to do with it. */
priv->wrfilled = 0;
@@ -431,14 +429,14 @@ static void cyberjack_write_bulk_callback(struct urb *urb)
goto exit;
}
- dbg("%s - priv->wrsent=%d", __func__, priv->wrsent);
- dbg("%s - priv->wrfilled=%d", __func__, priv->wrfilled);
+ dev_dbg(dev, "%s - priv->wrsent=%d\n", __func__, priv->wrsent);
+ dev_dbg(dev, "%s - priv->wrfilled=%d\n", __func__, priv->wrfilled);
blksize = ((int)priv->wrbuf[2]<<8)+priv->wrbuf[1]+3;
if (priv->wrsent >= priv->wrfilled ||
priv->wrsent >= blksize) {
- dbg("%s - buffer cleaned", __func__);
+ dev_dbg(dev, "%s - buffer cleaned\n", __func__);
memset(priv->wrbuf, 0, sizeof(priv->wrbuf));
priv->wrfilled = 0;
priv->wrsent = 0;
@@ -456,6 +454,3 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index be34f153e566..1befce21e173 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -46,7 +46,6 @@
#include "cypress_m8.h"
-static bool debug;
static bool stats;
static int interval;
static bool unstable_bauds;
@@ -263,8 +262,9 @@ static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate)
* safest speed for a part like that.
*/
if (new_rate > 4800) {
- dbg("%s - failed setting baud rate, device incapable "
- "speed %d", __func__, new_rate);
+ dev_dbg(&port->dev,
+ "%s - failed setting baud rate, device incapable speed %d\n",
+ __func__, new_rate);
return -1;
}
}
@@ -274,8 +274,9 @@ static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate)
/* 300 and 600 baud rates are supported under
* the generic firmware, but are not used with
* NMEA and SiRF protocols */
- dbg("%s - failed setting baud rate, unsupported speed "
- "of %d on Earthmate GPS", __func__, new_rate);
+ dev_dbg(&port->dev,
+ "%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS",
+ __func__, new_rate);
return -1;
}
break;
@@ -294,6 +295,7 @@ static int cypress_serial_control(struct tty_struct *tty,
{
int new_baudrate = 0, retval = 0, tries = 0;
struct cypress_private *priv;
+ struct device *dev = &port->dev;
u8 *feature_buffer;
const unsigned int feature_len = 5;
unsigned long flags;
@@ -312,16 +314,16 @@ static int cypress_serial_control(struct tty_struct *tty,
/* 0 means 'Hang up' so doesn't change the true bit rate */
new_baudrate = priv->baud_rate;
if (baud_rate && baud_rate != priv->baud_rate) {
- dbg("%s - baud rate is changing", __func__);
+ dev_dbg(dev, "%s - baud rate is changing\n", __func__);
retval = analyze_baud_rate(port, baud_rate);
if (retval >= 0) {
new_baudrate = retval;
- dbg("%s - New baud rate set to %d",
- __func__, new_baudrate);
+ dev_dbg(dev, "%s - New baud rate set to %d\n",
+ __func__, new_baudrate);
}
}
- dbg("%s - baud rate is being sent as %d",
- __func__, new_baudrate);
+ dev_dbg(dev, "%s - baud rate is being sent as %d\n", __func__,
+ new_baudrate);
/* fill the feature_buffer with new configuration */
put_unaligned_le32(new_baudrate, feature_buffer);
@@ -333,9 +335,8 @@ static int cypress_serial_control(struct tty_struct *tty,
/* 1 bit gap */
feature_buffer[4] |= (reset << 7); /* assign reset at end of byte, 1 bit space */
- dbg("%s - device is being sent this feature report:",
- __func__);
- dbg("%s - %02X - %02X - %02X - %02X - %02X", __func__,
+ dev_dbg(dev, "%s - device is being sent this feature report:\n", __func__);
+ dev_dbg(dev, "%s - %02X - %02X - %02X - %02X - %02X\n", __func__,
feature_buffer[0], feature_buffer[1],
feature_buffer[2], feature_buffer[3],
feature_buffer[4]);
@@ -355,8 +356,8 @@ static int cypress_serial_control(struct tty_struct *tty,
retval != -ENODEV);
if (retval != feature_len) {
- dev_err(&port->dev, "%s - failed sending serial "
- "line settings - %d\n", __func__, retval);
+ dev_err(dev, "%s - failed sending serial line settings - %d\n",
+ __func__, retval);
cypress_set_dead(port);
} else {
spin_lock_irqsave(&priv->lock, flags);
@@ -377,7 +378,7 @@ static int cypress_serial_control(struct tty_struct *tty,
retval = -ENOTTY;
goto out;
}
- dbg("%s - retreiving serial line settings", __func__);
+ dev_dbg(dev, "%s - retreiving serial line settings\n", __func__);
do {
retval = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
@@ -392,8 +393,8 @@ static int cypress_serial_control(struct tty_struct *tty,
&& retval != -ENODEV);
if (retval != feature_len) {
- dev_err(&port->dev, "%s - failed to retrieve serial "
- "line settings - %d\n", __func__, retval);
+ dev_err(dev, "%s - failed to retrieve serial line settings - %d\n",
+ __func__, retval);
cypress_set_dead(port);
goto out;
} else {
@@ -474,14 +475,14 @@ static int generic_startup(struct usb_serial *serial)
if (interval > 0) {
priv->write_urb_interval = interval;
priv->read_urb_interval = interval;
- dbg("%s - port %d read & write intervals forced to %d",
- __func__, port->number, interval);
+ dev_dbg(&port->dev, "%s - read & write intervals forced to %d\n",
+ __func__, interval);
} else {
priv->write_urb_interval = port->interrupt_out_urb->interval;
priv->read_urb_interval = port->interrupt_in_urb->interval;
- dbg("%s - port %d intervals: read=%d write=%d",
- __func__, port->number,
- priv->read_urb_interval, priv->write_urb_interval);
+ dev_dbg(&port->dev, "%s - intervals: read=%d write=%d\n",
+ __func__, priv->read_urb_interval,
+ priv->write_urb_interval);
}
usb_set_serial_port_data(port, priv);
@@ -495,8 +496,7 @@ static int cypress_earthmate_startup(struct usb_serial *serial)
struct usb_serial_port *port = serial->port[0];
if (generic_startup(serial)) {
- dbg("%s - Failed setting up port %d", __func__,
- port->number);
+ dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
return 1;
}
@@ -511,8 +511,9 @@ static int cypress_earthmate_startup(struct usb_serial *serial)
handle GET_CONFIG requests; everything they've
produced since that time crashes if this command is
attempted :-( */
- dbg("%s - Marking this device as unsafe for GET_CONFIG "
- "commands", __func__);
+ dev_dbg(&port->dev,
+ "%s - Marking this device as unsafe for GET_CONFIG commands\n",
+ __func__);
priv->get_cfg_unsafe = !0;
}
@@ -523,14 +524,14 @@ static int cypress_earthmate_startup(struct usb_serial *serial)
static int cypress_hidcom_startup(struct usb_serial *serial)
{
struct cypress_private *priv;
+ struct usb_serial_port *port = serial->port[0];
if (generic_startup(serial)) {
- dbg("%s - Failed setting up port %d", __func__,
- serial->port[0]->number);
+ dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
return 1;
}
- priv = usb_get_serial_port_data(serial->port[0]);
+ priv = usb_get_serial_port_data(port);
priv->chiptype = CT_CYPHIDCOM;
return 0;
@@ -540,14 +541,14 @@ static int cypress_hidcom_startup(struct usb_serial *serial)
static int cypress_ca42v2_startup(struct usb_serial *serial)
{
struct cypress_private *priv;
+ struct usb_serial_port *port = serial->port[0];
if (generic_startup(serial)) {
- dbg("%s - Failed setting up port %d", __func__,
- serial->port[0]->number);
+ dev_dbg(&port->dev, "%s - Failed setting up port\n", __func__);
return 1;
}
- priv = usb_get_serial_port_data(serial->port[0]);
+ priv = usb_get_serial_port_data(port);
priv->chiptype = CT_CA42V2;
return 0;
@@ -649,7 +650,7 @@ static void cypress_close(struct usb_serial_port *port)
kfifo_reset_out(&priv->write_fifo);
spin_unlock_irqrestore(&priv->lock, flags);
- dbg("%s - stopping urbs", __func__);
+ dev_dbg(&port->dev, "%s - stopping urbs\n", __func__);
usb_kill_urb(port->interrupt_in_urb);
usb_kill_urb(port->interrupt_out_urb);
@@ -665,7 +666,7 @@ static int cypress_write(struct tty_struct *tty, struct usb_serial_port *port,
{
struct cypress_private *priv = usb_get_serial_port_data(port);
- dbg("%s - port %d, %d bytes", __func__, port->number, count);
+ dev_dbg(&port->dev, "%s - port %d, %d bytes\n", __func__, port->number, count);
/* line control commands, which need to be executed immediately,
are not put into the buffer for obvious reasons.
@@ -691,17 +692,18 @@ static void cypress_send(struct usb_serial_port *port)
{
int count = 0, result, offset, actual_size;
struct cypress_private *priv = usb_get_serial_port_data(port);
+ struct device *dev = &port->dev;
unsigned long flags;
if (!priv->comm_is_ok)
return;
- dbg("%s - interrupt out size is %d", __func__,
- port->interrupt_out_size);
+ dev_dbg(dev, "%s - interrupt out size is %d\n", __func__,
+ port->interrupt_out_size);
spin_lock_irqsave(&priv->lock, flags);
if (priv->write_urb_in_use) {
- dbg("%s - can't write, urb in use", __func__);
+ dev_dbg(dev, "%s - can't write, urb in use\n", __func__);
spin_unlock_irqrestore(&priv->lock, flags);
return;
}
@@ -731,7 +733,7 @@ static void cypress_send(struct usb_serial_port *port)
if (priv->cmd_ctrl) {
priv->cmd_count++;
- dbg("%s - line control command being issued", __func__);
+ dev_dbg(dev, "%s - line control command being issued\n", __func__);
spin_unlock_irqrestore(&priv->lock, flags);
goto send;
} else
@@ -753,7 +755,7 @@ static void cypress_send(struct usb_serial_port *port)
port->interrupt_out_buffer[0] |= count;
}
- dbg("%s - count is %d", __func__, count);
+ dev_dbg(dev, "%s - count is %d\n", __func__, count);
send:
spin_lock_irqsave(&priv->lock, flags);
@@ -766,9 +768,8 @@ send:
actual_size = count +
(priv->pkt_fmt == packet_format_1 ? 2 : 1);
- usb_serial_debug_data(debug, &port->dev, __func__,
- port->interrupt_out_size,
- port->interrupt_out_urb->transfer_buffer);
+ usb_serial_debug_data(dev, __func__, port->interrupt_out_size,
+ port->interrupt_out_urb->transfer_buffer);
usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
@@ -807,7 +808,7 @@ static int cypress_write_room(struct tty_struct *tty)
room = kfifo_avail(&priv->write_fifo);
spin_unlock_irqrestore(&priv->lock, flags);
- dbg("%s - returns %d", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
return room;
}
@@ -832,7 +833,7 @@ static int cypress_tiocmget(struct tty_struct *tty)
| ((status & UART_RI) ? TIOCM_RI : 0)
| ((status & UART_CD) ? TIOCM_CD : 0);
- dbg("%s - result = %x", __func__, result);
+ dev_dbg(&port->dev, "%s - result = %x\n", __func__, result);
return result;
}
@@ -867,7 +868,7 @@ static int cypress_ioctl(struct tty_struct *tty,
struct usb_serial_port *port = tty->driver_data;
struct cypress_private *priv = usb_get_serial_port_data(port);
- dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd);
+ dev_dbg(&port->dev, "%s - port %d, cmd 0x%.4x\n", __func__, port->number, cmd);
switch (cmd) {
/* This code comes from drivers/char/serial.c and ftdi_sio.c */
@@ -902,7 +903,7 @@ static int cypress_ioctl(struct tty_struct *tty,
default:
break;
}
- dbg("%s - arg not supported - it was 0x%04x - check include/asm/ioctls.h", __func__, cmd);
+ dev_dbg(&port->dev, "%s - arg not supported - it was 0x%04x - check include/asm/ioctls.h\n", __func__, cmd);
return -ENOIOCTLCMD;
} /* cypress_ioctl */
@@ -911,6 +912,7 @@ static void cypress_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct cypress_private *priv = usb_get_serial_port_data(port);
+ struct device *dev = &port->dev;
int data_bits, stop_bits, parity_type, parity_enable;
unsigned cflag, iflag;
unsigned long flags;
@@ -984,23 +986,21 @@ static void cypress_set_termios(struct tty_struct *tty,
data_bits = 3;
break;
default:
- dev_err(&port->dev, "%s - CSIZE was set, but not CS5-CS8\n",
- __func__);
+ dev_err(dev, "%s - CSIZE was set, but not CS5-CS8\n", __func__);
data_bits = 3;
}
spin_lock_irqsave(&priv->lock, flags);
oldlines = priv->line_control;
if ((cflag & CBAUD) == B0) {
/* drop dtr and rts */
- dbg("%s - dropping the lines, baud rate 0bps", __func__);
+ dev_dbg(dev, "%s - dropping the lines, baud rate 0bps\n", __func__);
priv->line_control &= ~(CONTROL_DTR | CONTROL_RTS);
} else
priv->line_control = (CONTROL_DTR | CONTROL_RTS);
spin_unlock_irqrestore(&priv->lock, flags);
- dbg("%s - sending %d stop_bits, %d parity_enable, %d parity_type, "
- "%d data_bits (+5)", __func__, stop_bits,
- parity_enable, parity_type, data_bits);
+ dev_dbg(dev, "%s - sending %d stop_bits, %d parity_enable, %d parity_type, %d data_bits (+5)\n",
+ __func__, stop_bits, parity_enable, parity_type, data_bits);
cypress_serial_control(tty, port, tty_get_baud_rate(tty),
data_bits, stop_bits,
@@ -1017,8 +1017,7 @@ static void cypress_set_termios(struct tty_struct *tty,
spin_lock_irqsave(&priv->lock, flags);
if (priv->chiptype == CT_EARTHMATE && priv->baud_rate == 4800) {
- dbg("Using custom termios settings for a baud rate of "
- "4800bps.");
+ dev_dbg(dev, "Using custom termios settings for a baud rate of 4800bps.\n");
/* define custom termios settings for NMEA protocol */
tty->termios.c_iflag /* input modes - */
@@ -1067,7 +1066,7 @@ static int cypress_chars_in_buffer(struct tty_struct *tty)
chars = kfifo_len(&priv->write_fifo);
spin_unlock_irqrestore(&priv->lock, flags);
- dbg("%s - returns %d", __func__, chars);
+ dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
return chars;
}
@@ -1112,6 +1111,7 @@ static void cypress_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct cypress_private *priv = usb_get_serial_port_data(port);
+ struct device *dev = &urb->dev->dev;
struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
unsigned long flags;
@@ -1135,16 +1135,15 @@ static void cypress_read_int_callback(struct urb *urb)
/* FALLS THROUGH */
default:
/* something ugly is going on... */
- dev_err(&urb->dev->dev,
- "%s - unexpected nonzero read status received: %d\n",
- __func__, status);
+ dev_err(dev, "%s - unexpected nonzero read status received: %d\n",
+ __func__, status);
cypress_set_dead(port);
return;
}
spin_lock_irqsave(&priv->lock, flags);
if (priv->rx_flags & THROTTLED) {
- dbg("%s - now throttling", __func__);
+ dev_dbg(dev, "%s - now throttling\n", __func__);
priv->rx_flags |= ACTUALLY_THROTTLED;
spin_unlock_irqrestore(&priv->lock, flags);
return;
@@ -1153,7 +1152,7 @@ static void cypress_read_int_callback(struct urb *urb)
tty = tty_port_tty_get(&port->port);
if (!tty) {
- dbg("%s - bad tty pointer - exiting", __func__);
+ dev_dbg(dev, "%s - bad tty pointer - exiting\n", __func__);
return;
}
@@ -1180,13 +1179,13 @@ static void cypress_read_int_callback(struct urb *urb)
}
spin_unlock_irqrestore(&priv->lock, flags);
if (result < bytes) {
- dbg("%s - wrong packet size - received %d bytes but packet "
- "said %d bytes", __func__, result, bytes);
+ dev_dbg(dev,
+ "%s - wrong packet size - received %d bytes but packet said %d bytes\n",
+ __func__, result, bytes);
goto continue_read;
}
- usb_serial_debug_data(debug, &port->dev, __func__,
- urb->actual_length, data);
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
spin_lock_irqsave(&priv->lock, flags);
/* check to see if status has changed */
@@ -1202,7 +1201,7 @@ static void cypress_read_int_callback(struct urb *urb)
* though */
if (tty && !(tty->termios.c_cflag & CLOCAL) &&
!(priv->current_status & UART_CD)) {
- dbg("%s - calling hangup", __func__);
+ dev_dbg(dev, "%s - calling hangup\n", __func__);
tty_hangup(tty);
goto continue_read;
}
@@ -1215,7 +1214,7 @@ static void cypress_read_int_callback(struct urb *urb)
if (priv->current_status & CYP_ERROR) {
spin_unlock_irqrestore(&priv->lock, flags);
tty_flag = TTY_PARITY;
- dbg("%s - Parity Error detected", __func__);
+ dev_dbg(dev, "%s - Parity Error detected\n", __func__);
} else
spin_unlock_irqrestore(&priv->lock, flags);
@@ -1246,9 +1245,8 @@ continue_read:
priv->read_urb_interval);
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
if (result && result != -EPERM) {
- dev_err(&urb->dev->dev, "%s - failed resubmitting "
- "read urb, error %d\n", __func__,
- result);
+ dev_err(dev, "%s - failed resubmitting read urb, error %d\n",
+ __func__, result);
cypress_set_dead(port);
}
}
@@ -1259,6 +1257,7 @@ static void cypress_write_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct cypress_private *priv = usb_get_serial_port_data(port);
+ struct device *dev = &urb->dev->dev;
int result;
int status = urb->status;
@@ -1270,8 +1269,8 @@ static void cypress_write_int_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d",
- __func__, status);
+ dev_dbg(dev, "%s - urb shutting down with status: %d\n",
+ __func__, status);
priv->write_urb_in_use = 0;
return;
case -EPIPE: /* no break needed; clear halt and resubmit */
@@ -1279,21 +1278,19 @@ static void cypress_write_int_callback(struct urb *urb)
break;
usb_clear_halt(port->serial->dev, 0x02);
/* error in the urb, so we have to resubmit it */
- dbg("%s - nonzero write bulk status received: %d",
+ dev_dbg(dev, "%s - nonzero write bulk status received: %d\n",
__func__, status);
port->interrupt_out_urb->transfer_buffer_length = 1;
result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
if (!result)
return;
- dev_err(&urb->dev->dev,
- "%s - failed resubmitting write urb, error %d\n",
- __func__, result);
+ dev_err(dev, "%s - failed resubmitting write urb, error %d\n",
+ __func__, result);
cypress_set_dead(port);
break;
default:
- dev_err(&urb->dev->dev,
- "%s - unexpected nonzero write status received: %d\n",
- __func__, status);
+ dev_err(dev, "%s - unexpected nonzero write status received: %d\n",
+ __func__, status);
cypress_set_dead(port);
break;
}
@@ -1310,8 +1307,6 @@ MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
module_param(stats, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(stats, "Enable statistics or not");
module_param(interval, int, S_IRUGO | S_IWUSR);
diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c
index afd9d2ec577b..c86f68c6b078 100644
--- a/drivers/usb/serial/digi_acceleport.c
+++ b/drivers/usb/serial/digi_acceleport.c
@@ -249,10 +249,6 @@ static int digi_read_inb_callback(struct urb *urb);
static int digi_read_oob_callback(struct urb *urb);
-/* Statics */
-
-static bool debug;
-
static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_2_ID) },
{ USB_DEVICE(DIGI_VENDOR_ID, DIGI_4_ID) },
@@ -404,14 +400,15 @@ static void digi_wakeup_write(struct usb_serial_port *port)
static int digi_write_oob_command(struct usb_serial_port *port,
unsigned char *buf, int count, int interruptible)
{
-
int ret = 0;
int len;
struct usb_serial_port *oob_port = (struct usb_serial_port *)((struct digi_serial *)(usb_get_serial_data(port->serial)))->ds_oob_port;
struct digi_port *oob_priv = usb_get_serial_port_data(oob_port);
unsigned long flags = 0;
- dbg("digi_write_oob_command: TOP: port=%d, count=%d", oob_priv->dp_port_num, count);
+ dev_dbg(&port->dev,
+ "digi_write_oob_command: TOP: port=%d, count=%d\n",
+ oob_priv->dp_port_num, count);
spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
while (count > 0) {
@@ -467,7 +464,7 @@ static int digi_write_inb_command(struct usb_serial_port *port,
unsigned char *data = port->write_urb->transfer_buffer;
unsigned long flags = 0;
- dbg("digi_write_inb_command: TOP: port=%d, count=%d",
+ dev_dbg(&port->dev, "digi_write_inb_command: TOP: port=%d, count=%d\n",
priv->dp_port_num, count);
if (timeout)
@@ -549,7 +546,8 @@ static int digi_set_modem_signals(struct usb_serial_port *port,
unsigned long flags = 0;
- dbg("digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x",
+ dev_dbg(&port->dev,
+ "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x\n",
port_priv->dp_port_num, modem_signals);
spin_lock_irqsave(&oob_priv->dp_port_lock, flags);
@@ -687,6 +685,7 @@ static void digi_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct digi_port *priv = usb_get_serial_port_data(port);
+ struct device *dev = &port->dev;
unsigned int iflag = tty->termios.c_iflag;
unsigned int cflag = tty->termios.c_cflag;
unsigned int old_iflag = old_termios->c_iflag;
@@ -697,7 +696,9 @@ static void digi_set_termios(struct tty_struct *tty,
int i = 0;
speed_t baud;
- dbg("digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", priv->dp_port_num, iflag, old_iflag, cflag, old_cflag);
+ dev_dbg(dev,
+ "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x\n",
+ priv->dp_port_num, iflag, old_iflag, cflag, old_cflag);
/* set baud rate */
baud = tty_get_baud_rate(tty);
@@ -773,7 +774,8 @@ static void digi_set_termios(struct tty_struct *tty,
case CS7: arg = DIGI_WORD_SIZE_7; break;
case CS8: arg = DIGI_WORD_SIZE_8; break;
default:
- dbg("digi_set_termios: can't handle word size %d",
+ dev_dbg(dev,
+ "digi_set_termios: can't handle word size %d\n",
(cflag&CSIZE));
break;
}
@@ -866,7 +868,7 @@ static void digi_set_termios(struct tty_struct *tty,
}
ret = digi_write_oob_command(port, buf, i, 1);
if (ret != 0)
- dbg("digi_set_termios: write oob failed, ret=%d", ret);
+ dev_dbg(dev, "digi_set_termios: write oob failed, ret=%d\n", ret);
tty_encode_baud_rate(tty, baud, baud);
}
@@ -922,7 +924,8 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
unsigned char *data = port->write_urb->transfer_buffer;
unsigned long flags = 0;
- dbg("digi_write: TOP: port=%d, count=%d, in_interrupt=%ld",
+ dev_dbg(&port->dev,
+ "digi_write: TOP: port=%d, count=%d, in_interrupt=%ld\n",
priv->dp_port_num, count, in_interrupt());
/* copy user data (which can sleep) before getting spin lock */
@@ -981,7 +984,7 @@ static int digi_write(struct tty_struct *tty, struct usb_serial_port *port,
dev_err_console(port,
"%s: usb_submit_urb failed, ret=%d, port=%d\n",
__func__, ret, priv->dp_port_num);
- dbg("digi_write: returning %d", ret);
+ dev_dbg(&port->dev, "digi_write: returning %d\n", ret);
return ret;
}
@@ -1012,7 +1015,7 @@ static void digi_write_bulk_callback(struct urb *urb)
/* handle oob callback */
if (priv->dp_port_num == serial_priv->ds_oob_port_num) {
- dbg("digi_write_bulk_callback: oob callback");
+ dev_dbg(&port->dev, "digi_write_bulk_callback: oob callback\n");
spin_lock(&priv->dp_port_lock);
priv->dp_write_urb_in_use = 0;
wake_up_interruptible(&port->write_wait);
@@ -1066,7 +1069,7 @@ static int digi_write_room(struct tty_struct *tty)
room = port->bulk_out_size - 2 - priv->dp_out_buf_len;
spin_unlock_irqrestore(&priv->dp_port_lock, flags);
- dbg("digi_write_room: port=%d, room=%d", priv->dp_port_num, room);
+ dev_dbg(&port->dev, "digi_write_room: port=%d, room=%d\n", priv->dp_port_num, room);
return room;
}
@@ -1077,12 +1080,12 @@ static int digi_chars_in_buffer(struct tty_struct *tty)
struct digi_port *priv = usb_get_serial_port_data(port);
if (priv->dp_write_urb_in_use) {
- dbg("digi_chars_in_buffer: port=%d, chars=%d",
+ dev_dbg(&port->dev, "digi_chars_in_buffer: port=%d, chars=%d\n",
priv->dp_port_num, port->bulk_out_size - 2);
/* return(port->bulk_out_size - 2); */
return 256;
} else {
- dbg("digi_chars_in_buffer: port=%d, chars=%d",
+ dev_dbg(&port->dev, "digi_chars_in_buffer: port=%d, chars=%d\n",
priv->dp_port_num, priv->dp_out_buf_len);
return priv->dp_out_buf_len;
}
@@ -1120,7 +1123,7 @@ static int digi_open(struct tty_struct *tty, struct usb_serial_port *port)
ret = digi_write_oob_command(port, buf, 8, 1);
if (ret != 0)
- dbg("digi_open: write oob failed, ret=%d", ret);
+ dev_dbg(&port->dev, "digi_open: write oob failed, ret=%d\n", ret);
/* set termios settings */
if (tty) {
@@ -1180,7 +1183,7 @@ static void digi_close(struct usb_serial_port *port)
ret = digi_write_oob_command(port, buf, 20, 0);
if (ret != 0)
- dbg("digi_close: write oob failed, ret=%d", ret);
+ dev_dbg(&port->dev, "digi_close: write oob failed, ret=%d\n", ret);
/* wait for final commands on oob port to complete */
prepare_to_wait(&priv->dp_flush_wait, &wait,
@@ -1448,9 +1451,9 @@ static int digi_read_inb_callback(struct urb *urb)
tty_kref_put(tty);
if (opcode == DIGI_CMD_RECEIVE_DISABLE)
- dbg("%s: got RECEIVE_DISABLE", __func__);
+ dev_dbg(&port->dev, "%s: got RECEIVE_DISABLE\n", __func__);
else if (opcode != DIGI_CMD_RECEIVE_DATA)
- dbg("%s: unknown opcode: %d", __func__, opcode);
+ dev_dbg(&port->dev, "%s: unknown opcode: %d\n", __func__, opcode);
return throttled ? 1 : 0;
@@ -1484,7 +1487,7 @@ static int digi_read_oob_callback(struct urb *urb)
status = ((unsigned char *)urb->transfer_buffer)[i++];
val = ((unsigned char *)urb->transfer_buffer)[i++];
- dbg("digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d",
+ dev_dbg(&port->dev, "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d\n",
opcode, line, status, val);
if (status != 0 || line >= serial->type->num_ports)
@@ -1552,6 +1555,3 @@ module_usb_serial_driver(serial_drivers, id_table_combined);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/empeg.c b/drivers/usb/serial/empeg.c
index 34e86383090a..43ede4a1e12c 100644
--- a/drivers/usb/serial/empeg.c
+++ b/drivers/usb/serial/empeg.c
@@ -28,8 +28,6 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
-static bool debug;
-
/*
* Version Information
*/
@@ -134,6 +132,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/ezusb.c b/drivers/usb/serial/ezusb.c
deleted file mode 100644
index 800e8eb60003..000000000000
--- a/drivers/usb/serial/ezusb.c
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * EZ-USB specific functions used by some of the USB to Serial drivers.
- *
- * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.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/errno.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/tty.h>
-#include <linux/module.h>
-#include <linux/usb.h>
-#include <linux/usb/serial.h>
-
-/* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */
-#define CPUCS_REG 0x7F92
-
-int ezusb_writememory(struct usb_serial *serial, int address,
- unsigned char *data, int length, __u8 request)
-{
- int result;
- unsigned char *transfer_buffer;
-
- if (!serial->dev) {
- printk(KERN_ERR "ezusb: %s - no physical device present, "
- "failing.\n", __func__);
- return -ENODEV;
- }
-
- transfer_buffer = kmemdup(data, length, GFP_KERNEL);
- if (!transfer_buffer) {
- dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n",
- __func__, length);
- return -ENOMEM;
- }
- result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- request, 0x40, address, 0, transfer_buffer, length, 3000);
- kfree(transfer_buffer);
- return result;
-}
-EXPORT_SYMBOL_GPL(ezusb_writememory);
-
-int ezusb_set_reset(struct usb_serial *serial, unsigned char reset_bit)
-{
- int response;
-
- response = ezusb_writememory(serial, CPUCS_REG, &reset_bit, 1, 0xa0);
- if (response < 0)
- dev_err(&serial->dev->dev, "%s- %d failed\n",
- __func__, reset_bit);
- return response;
-}
-EXPORT_SYMBOL_GPL(ezusb_set_reset);
-
diff --git a/drivers/usb/serial/f81232.c b/drivers/usb/serial/f81232.c
index 79451ee12ca0..244477107e2f 100644
--- a/drivers/usb/serial/f81232.c
+++ b/drivers/usb/serial/f81232.c
@@ -25,8 +25,6 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
-static bool debug;
-
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1934, 0x0706) },
{ } /* Terminating entry */
@@ -85,7 +83,7 @@ static void f81232_read_int_callback(struct urb *urb)
goto exit;
}
- usb_serial_debug_data(debug, &port->dev, __func__,
+ usb_serial_debug_data(&port->dev, __func__,
urb->actual_length, urb->transfer_buffer);
f81232_update_line_status(port, data, actual_length);
@@ -389,7 +387,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver");
MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org");
MODULE_LICENSE("GPL v2");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
-
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 0c8d1c226273..be845873e23d 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -48,14 +48,9 @@
#include "ftdi_sio.h"
#include "ftdi_sio_ids.h"
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.6.0"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Bill Ryder <bryder@sgi.com>, Kuba Ober <kuba@mareimbrium.org>, Andreas Mohr, Johan Hovold <jhovold@gmail.com>"
#define DRIVER_DESC "USB FTDI Serial Converters Driver"
-static bool debug;
static __u16 vendor = FTDI_VID;
static __u16 product;
@@ -584,6 +579,8 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_IBS_PEDO_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_IBS_PROD_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_TAVIR_STK500_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_TIAO_UMPA_PID),
+ .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
/*
* ELV devices:
*/
@@ -1063,11 +1060,12 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,
unsigned int clear)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct device *dev = &port->dev;
unsigned urb_value;
int rv;
if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
- dbg("%s - DTR|RTS not being set|cleared", __func__);
+ dev_dbg(dev, "%s - DTR|RTS not being set|cleared\n", __func__);
return 0; /* no change */
}
@@ -1088,18 +1086,14 @@ static int update_mctrl(struct usb_serial_port *port, unsigned int set,
urb_value, priv->interface,
NULL, 0, WDR_TIMEOUT);
if (rv < 0) {
- dbg("%s Error from MODEM_CTRL urb: DTR %s, RTS %s",
- __func__,
- (set & TIOCM_DTR) ? "HIGH" :
- (clear & TIOCM_DTR) ? "LOW" : "unchanged",
- (set & TIOCM_RTS) ? "HIGH" :
- (clear & TIOCM_RTS) ? "LOW" : "unchanged");
+ dev_dbg(dev, "%s Error from MODEM_CTRL urb: DTR %s, RTS %s\n",
+ __func__,
+ (set & TIOCM_DTR) ? "HIGH" : (clear & TIOCM_DTR) ? "LOW" : "unchanged",
+ (set & TIOCM_RTS) ? "HIGH" : (clear & TIOCM_RTS) ? "LOW" : "unchanged");
} else {
- dbg("%s - DTR %s, RTS %s", __func__,
- (set & TIOCM_DTR) ? "HIGH" :
- (clear & TIOCM_DTR) ? "LOW" : "unchanged",
- (set & TIOCM_RTS) ? "HIGH" :
- (clear & TIOCM_RTS) ? "LOW" : "unchanged");
+ dev_dbg(dev, "%s - DTR %s, RTS %s\n", __func__,
+ (set & TIOCM_DTR) ? "HIGH" : (clear & TIOCM_DTR) ? "LOW" : "unchanged",
+ (set & TIOCM_RTS) ? "HIGH" : (clear & TIOCM_RTS) ? "LOW" : "unchanged");
/* FIXME: locking on last_dtr_rts */
priv->last_dtr_rts = (priv->last_dtr_rts & ~clear) | set;
}
@@ -1111,6 +1105,7 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
struct usb_serial_port *port)
{
struct ftdi_private *priv = usb_get_serial_port_data(port);
+ struct device *dev = &port->dev;
__u32 div_value = 0;
int div_okay = 1;
int baud;
@@ -1146,7 +1141,7 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
alt_speed hack */
baud = tty_get_baud_rate(tty);
- dbg("%s - tty_get_baud_rate reports speed %d", __func__, baud);
+ dev_dbg(dev, "%s - tty_get_baud_rate reports speed %d\n", __func__, baud);
/* 2. Observe async-compatible custom_divisor hack, update baudrate
if needed */
@@ -1155,8 +1150,8 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
((priv->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) &&
(priv->custom_divisor)) {
baud = priv->baud_base / priv->custom_divisor;
- dbg("%s - custom divisor %d sets baud rate to %d",
- __func__, priv->custom_divisor, baud);
+ dev_dbg(dev, "%s - custom divisor %d sets baud rate to %d\n",
+ __func__, priv->custom_divisor, baud);
}
/* 3. Convert baudrate to device-specific divisor */
@@ -1178,8 +1173,8 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
case 115200: div_value = ftdi_sio_b115200; break;
} /* baud */
if (div_value == 0) {
- dbg("%s - Baudrate (%d) requested is not supported",
- __func__, baud);
+ dev_dbg(dev, "%s - Baudrate (%d) requested is not supported\n",
+ __func__, baud);
div_value = ftdi_sio_b9600;
baud = 9600;
div_okay = 0;
@@ -1189,7 +1184,7 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
if (baud <= 3000000) {
div_value = ftdi_232am_baud_to_divisor(baud);
} else {
- dbg("%s - Baud rate too high!", __func__);
+ dev_dbg(dev, "%s - Baud rate too high!\n", __func__);
baud = 9600;
div_value = ftdi_232am_baud_to_divisor(9600);
div_okay = 0;
@@ -1212,7 +1207,7 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
}
div_value = ftdi_232bm_baud_to_divisor(baud);
} else {
- dbg("%s - Baud rate too high!", __func__);
+ dev_dbg(dev, "%s - Baud rate too high!\n", __func__);
div_value = ftdi_232bm_baud_to_divisor(9600);
div_okay = 0;
baud = 9600;
@@ -1226,7 +1221,7 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
} else if (baud < 1200) {
div_value = ftdi_232bm_baud_to_divisor(baud);
} else {
- dbg("%s - Baud rate too high!", __func__);
+ dev_dbg(dev, "%s - Baud rate too high!\n", __func__);
div_value = ftdi_232bm_baud_to_divisor(9600);
div_okay = 0;
baud = 9600;
@@ -1235,7 +1230,7 @@ static __u32 get_ftdi_divisor(struct tty_struct *tty,
} /* priv->chip_type */
if (div_okay) {
- dbg("%s - Baud rate set to %d (divisor 0x%lX) on chip %s",
+ dev_dbg(dev, "%s - Baud rate set to %d (divisor 0x%lX) on chip %s\n",
__func__, baud, (unsigned long)div_value,
ftdi_chip_name[priv->chip_type]);
}
@@ -1281,7 +1276,7 @@ static int write_latency_timer(struct usb_serial_port *port)
if (priv->flags & ASYNC_LOW_LATENCY)
l = 1;
- dbg("%s: setting latency timer = %i", __func__, l);
+ dev_dbg(&port->dev, "%s: setting latency timer = %i\n", __func__, l);
rv = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
@@ -1436,8 +1431,8 @@ static void ftdi_determine_type(struct usb_serial_port *port)
version = le16_to_cpu(udev->descriptor.bcdDevice);
interfaces = udev->actconfig->desc.bNumInterfaces;
- dbg("%s: bcdDevice = 0x%x, bNumInterfaces = %u", __func__,
- version, interfaces);
+ dev_dbg(&port->dev, "%s: bcdDevice = 0x%x, bNumInterfaces = %u\n", __func__,
+ version, interfaces);
if (interfaces > 1) {
int inter;
@@ -1467,8 +1462,9 @@ static void ftdi_determine_type(struct usb_serial_port *port)
/* BM-type devices have a bug where bcdDevice gets set
* to 0x200 when iSerialNumber is 0. */
if (version < 0x500) {
- dbg("%s: something fishy - bcdDevice too low for multi-interface device",
- __func__);
+ dev_dbg(&port->dev,
+ "%s: something fishy - bcdDevice too low for multi-interface device\n",
+ __func__);
}
} else if (version < 0x200) {
/* Old device. Assume it's the original SIO. */
@@ -1582,7 +1578,7 @@ static ssize_t store_event_char(struct device *dev,
int v = simple_strtoul(valbuf, NULL, 10);
int rv;
- dbg("%s: setting event char = %i", __func__, v);
+ dev_dbg(&port->dev, "%s: setting event char = %i\n", __func__, v);
rv = usb_control_msg(udev,
usb_sndctrlpipe(udev, 0),
@@ -1591,7 +1587,7 @@ static ssize_t store_event_char(struct device *dev,
v, priv->interface,
NULL, 0, WDR_TIMEOUT);
if (rv < 0) {
- dbg("Unable to write event character: %i", rv);
+ dev_dbg(&port->dev, "Unable to write event character: %i\n", rv);
return -EIO;
}
@@ -1610,7 +1606,7 @@ static int create_sysfs_attrs(struct usb_serial_port *port)
/* XXX I've no idea if the original SIO supports the event_char
* sysfs parameter, so I'm playing it safe. */
if (priv->chip_type != SIO) {
- dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]);
+ dev_dbg(&port->dev, "sysfs attributes for %s\n", ftdi_chip_name[priv->chip_type]);
retval = device_create_file(&port->dev, &dev_attr_event_char);
if ((!retval) &&
(priv->chip_type == FT232BM ||
@@ -1750,8 +1746,8 @@ static int ftdi_NDI_device_setup(struct usb_serial *serial)
if (latency > 99)
latency = 99;
- dbg("%s setting NDI device latency to %d", __func__, latency);
- dev_info(&udev->dev, "NDI device with a latency value of %d", latency);
+ dev_dbg(&udev->dev, "%s setting NDI device latency to %d\n", __func__, latency);
+ dev_info(&udev->dev, "NDI device with a latency value of %d\n", latency);
/* FIXME: errors are not returned */
usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
@@ -1969,7 +1965,7 @@ static int ftdi_process_packet(struct tty_struct *tty,
char *ch;
if (len < 2) {
- dbg("malformed packet");
+ dev_dbg(&port->dev, "malformed packet\n");
return 0;
}
@@ -2084,12 +2080,12 @@ static void ftdi_break_ctl(struct tty_struct *tty, int break_state)
FTDI_SIO_SET_DATA_REQUEST_TYPE,
urb_value , priv->interface,
NULL, 0, WDR_TIMEOUT) < 0) {
- dev_err(&port->dev, "%s FAILED to enable/disable break state "
- "(state was %d)\n", __func__, break_state);
+ dev_err(&port->dev, "%s FAILED to enable/disable break state (state was %d)\n",
+ __func__, break_state);
}
- dbg("%s break state is %d - urb is %d", __func__,
- break_state, urb_value);
+ dev_dbg(&port->dev, "%s break state is %d - urb is %d\n", __func__,
+ break_state, urb_value);
}
@@ -2101,6 +2097,7 @@ static void ftdi_set_termios(struct tty_struct *tty,
struct usb_serial_port *port, struct ktermios *old_termios)
{
struct usb_device *dev = port->serial->dev;
+ struct device *ddev = &port->dev;
struct ftdi_private *priv = usb_get_serial_port_data(port);
struct ktermios *termios = &tty->termios;
unsigned int cflag = termios->c_cflag;
@@ -2114,20 +2111,20 @@ static void ftdi_set_termios(struct tty_struct *tty,
/* Force baud rate if this device requires it, unless it is set to
B0. */
if (priv->force_baud && ((termios->c_cflag & CBAUD) != B0)) {
- dbg("%s: forcing baud rate for this device", __func__);
+ dev_dbg(ddev, "%s: forcing baud rate for this device\n", __func__);
tty_encode_baud_rate(tty, priv->force_baud,
priv->force_baud);
}
/* Force RTS-CTS if this device requires it. */
if (priv->force_rtscts) {
- dbg("%s: forcing rtscts for this device", __func__);
+ dev_dbg(ddev, "%s: forcing rtscts for this device\n", __func__);
termios->c_cflag |= CRTSCTS;
}
cflag = termios->c_cflag;
- if (old_termios == 0)
+ if (!old_termios)
goto no_skip;
if (old_termios->c_cflag == termios->c_cflag
@@ -2163,10 +2160,16 @@ no_skip:
}
if (cflag & CSIZE) {
switch (cflag & CSIZE) {
- case CS7: urb_value |= 7; dbg("Setting CS7"); break;
- case CS8: urb_value |= 8; dbg("Setting CS8"); break;
+ case CS7:
+ urb_value |= 7;
+ dev_dbg(ddev, "Setting CS7\n");
+ break;
+ case CS8:
+ urb_value |= 8;
+ dev_dbg(ddev, "Setting CS8\n");
+ break;
default:
- dev_err(&port->dev, "CSIZE was set but not CS7-CS8\n");
+ dev_err(ddev, "CSIZE was set but not CS7-CS8\n");
}
}
@@ -2179,8 +2182,8 @@ no_skip:
FTDI_SIO_SET_DATA_REQUEST_TYPE,
urb_value , priv->interface,
NULL, 0, WDR_SHORT_TIMEOUT) < 0) {
- dev_err(&port->dev, "%s FAILED to set "
- "databits/stopbits/parity\n", __func__);
+ dev_err(ddev, "%s FAILED to set databits/stopbits/parity\n",
+ __func__);
}
/* Now do the baudrate */
@@ -2192,8 +2195,7 @@ no_data_parity_stop_changes:
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
0, priv->interface,
NULL, 0, WDR_TIMEOUT) < 0) {
- dev_err(&port->dev,
- "%s error from disable flowcontrol urb\n",
+ dev_err(ddev, "%s error from disable flowcontrol urb\n",
__func__);
}
/* Drop RTS and DTR */
@@ -2202,8 +2204,7 @@ no_data_parity_stop_changes:
/* set the baudrate determined before */
mutex_lock(&priv->cfg_lock);
if (change_speed(tty, port))
- dev_err(&port->dev, "%s urb failed to set baudrate\n",
- __func__);
+ dev_err(ddev, "%s urb failed to set baudrate\n", __func__);
mutex_unlock(&priv->cfg_lock);
/* Ensure RTS and DTR are raised when baudrate changed from 0 */
if (!old_termios || (old_termios->c_cflag & CBAUD) == B0)
@@ -2214,17 +2215,15 @@ no_data_parity_stop_changes:
/* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */
no_c_cflag_changes:
if (cflag & CRTSCTS) {
- dbg("%s Setting to CRTSCTS flow control", __func__);
+ dev_dbg(ddev, "%s Setting to CRTSCTS flow control\n", __func__);
if (usb_control_msg(dev,
usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
0 , (FTDI_SIO_RTS_CTS_HS | priv->interface),
NULL, 0, WDR_TIMEOUT) < 0) {
- dev_err(&port->dev,
- "urb failed to set to rts/cts flow control\n");
+ dev_err(ddev, "urb failed to set to rts/cts flow control\n");
}
-
} else {
/*
* Xon/Xoff code
@@ -2234,8 +2233,8 @@ no_c_cflag_changes:
* code is executed.
*/
if (iflag & IXOFF) {
- dbg("%s request to enable xonxoff iflag=%04x",
- __func__, iflag);
+ dev_dbg(ddev, "%s request to enable xonxoff iflag=%04x\n",
+ __func__, iflag);
/* Try to enable the XON/XOFF on the ftdi_sio
* Set the vstart and vstop -- could have been done up
* above where a lot of other dereferencing is done but
@@ -2260,18 +2259,16 @@ no_c_cflag_changes:
/* else clause to only run if cflag ! CRTSCTS and iflag
* ! XOFF. CHECKME Assuming XON/XOFF handled by tty
* stack - not by device */
- dbg("%s Turning off hardware flow control", __func__);
+ dev_dbg(ddev, "%s Turning off hardware flow control\n", __func__);
if (usb_control_msg(dev,
usb_sndctrlpipe(dev, 0),
FTDI_SIO_SET_FLOW_CTRL_REQUEST,
FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE,
0, priv->interface,
NULL, 0, WDR_TIMEOUT) < 0) {
- dev_err(&port->dev,
- "urb failed to clear flow control\n");
+ dev_err(ddev, "urb failed to clear flow control\n");
}
}
-
}
}
@@ -2365,7 +2362,7 @@ static int ftdi_ioctl(struct tty_struct *tty,
struct async_icount cnow;
struct async_icount cprev;
- dbg("%s cmd 0x%04x", __func__, cmd);
+ dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
/* Based on code from acm.c and others */
switch (cmd) {
@@ -2413,14 +2410,13 @@ static int ftdi_ioctl(struct tty_struct *tty,
/* This is not necessarily an error - turns out the higher layers
* will do some ioctls themselves (see comment above)
*/
- dbg("%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h", __func__, cmd);
+ dev_dbg(&port->dev, "%s arg not supported - it was 0x%04x - check /usr/include/asm/ioctls.h\n",
+ __func__, cmd);
return -ENOIOCTLCMD;
}
static int __init ftdi_init(void)
{
- int retval;
-
if (vendor > 0 && product > 0) {
/* Add user specified VID/PID to reserved element of table. */
int i;
@@ -2430,11 +2426,7 @@ static int __init ftdi_init(void)
id_table_combined[i].idVendor = vendor;
id_table_combined[i].idProduct = product;
}
- retval = usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, id_table_combined);
- if (retval == 0)
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
- DRIVER_DESC "\n");
- return retval;
+ return usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, id_table_combined);
}
static void __exit ftdi_exit(void)
@@ -2450,8 +2442,6 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
module_param(vendor, ushort, 0);
MODULE_PARM_DESC(vendor, "User specified vendor ID (default="
__MODULE_STRING(FTDI_VID)")");
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 41fe5826100c..57c12ef6625e 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -517,6 +517,11 @@
*/
#define FTDI_TAVIR_STK500_PID 0xFA33 /* STK500 AVR programmer */
+/*
+ * TIAO product ids (FTDI_VID)
+ * http://www.tiaowiki.com/w/Main_Page
+ */
+#define FTDI_TIAO_UMPA_PID 0x8a98 /* TIAO/DIYGADGET USB Multi-Protocol Adapter */
/********************************/
diff --git a/drivers/usb/serial/funsoft.c b/drivers/usb/serial/funsoft.c
index 235707961ca3..9362f8fd2385 100644
--- a/drivers/usb/serial/funsoft.c
+++ b/drivers/usb/serial/funsoft.c
@@ -16,8 +16,6 @@
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
-static bool debug;
-
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1404, 0xcddc) },
{ },
@@ -40,6 +38,3 @@ static struct usb_serial_driver * const serial_drivers[] = {
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index 346c15a51066..3ee92648c02d 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -41,9 +41,6 @@
/* the mode to be set when the port ist opened */
static int initial_mode = 1;
-/* debug flag */
-static bool debug;
-
#define GARMIN_VENDOR_ID 0x091E
/*
@@ -258,10 +255,7 @@ static void send_to_tty(struct usb_serial_port *port,
struct tty_struct *tty = tty_port_tty_get(&port->port);
if (tty && actual_length) {
-
- usb_serial_debug_data(debug, &port->dev,
- __func__, actual_length, data);
-
+ usb_serial_debug_data(&port->dev, __func__, actual_length, data);
tty_insert_flip_string(tty, data, actual_length);
tty_flip_buffer_push(tty);
}
@@ -303,8 +297,9 @@ static int pkt_add(struct garmin_data *garmin_data_p,
state = garmin_data_p->state;
spin_unlock_irqrestore(&garmin_data_p->lock, flags);
- dbg("%s - added: pkt: %d - %d bytes",
- __func__, pkt->seq, data_length);
+ dev_dbg(&garmin_data_p->port->dev,
+ "%s - added: pkt: %d - %d bytes\n", __func__,
+ pkt->seq, data_length);
/* in serial mode, if someone is waiting for data from
the device, convert and send the next packet to tty. */
@@ -359,7 +354,8 @@ static int gsp_send_ack(struct garmin_data *garmin_data_p, __u8 pkt_id)
__u8 *ptr = pkt;
unsigned l = 0;
- dbg("%s - pkt-id: 0x%X.", __func__, 0xFF & pkt_id);
+ dev_dbg(&garmin_data_p->port->dev, "%s - pkt-id: 0x%X.\n", __func__,
+ 0xFF & pkt_id);
*ptr++ = DLE;
*ptr++ = ACK;
@@ -399,20 +395,20 @@ static int gsp_send_ack(struct garmin_data *garmin_data_p, __u8 pkt_id)
*/
static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count)
{
+ struct device *dev = &garmin_data_p->port->dev;
unsigned long flags;
const __u8 *recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET;
__le32 *usbdata = (__le32 *) garmin_data_p->inbuffer;
-
int cksum = 0;
int n = 0;
int pktid = recpkt[0];
int size = recpkt[1];
- usb_serial_debug_data(debug, &garmin_data_p->port->dev,
- __func__, count-GSP_INITIAL_OFFSET, recpkt);
+ usb_serial_debug_data(&garmin_data_p->port->dev, __func__,
+ count-GSP_INITIAL_OFFSET, recpkt);
if (size != (count-GSP_INITIAL_OFFSET-3)) {
- dbg("%s - invalid size, expected %d bytes, got %d",
+ dev_dbg(dev, "%s - invalid size, expected %d bytes, got %d\n",
__func__, size, (count-GSP_INITIAL_OFFSET-3));
return -EINVPKT;
}
@@ -422,8 +418,8 @@ static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count)
/* sanity check, remove after test ... */
if ((__u8 *)&(usbdata[3]) != recpkt) {
- dbg("%s - ptr mismatch %p - %p",
- __func__, &(usbdata[4]), recpkt);
+ dev_dbg(dev, "%s - ptr mismatch %p - %p\n", __func__,
+ &(usbdata[4]), recpkt);
return -EINVPKT;
}
@@ -433,7 +429,7 @@ static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count)
}
if ((0xff & (cksum + *recpkt)) != 0) {
- dbg("%s - invalid checksum, expected %02x, got %02x",
+ dev_dbg(dev, "%s - invalid checksum, expected %02x, got %02x\n",
__func__, 0xff & -cksum, 0xff & *recpkt);
return -EINVPKT;
}
@@ -480,6 +476,7 @@ static int gsp_rec_packet(struct garmin_data *garmin_data_p, int count)
static int gsp_receive(struct garmin_data *garmin_data_p,
const unsigned char *buf, int count)
{
+ struct device *dev = &garmin_data_p->port->dev;
unsigned long flags;
int offs = 0;
int ack_or_nak_seen = 0;
@@ -500,7 +497,7 @@ static int gsp_receive(struct garmin_data *garmin_data_p,
skip = garmin_data_p->flags & FLAGS_GSP_SKIP;
spin_unlock_irqrestore(&garmin_data_p->lock, flags);
- /* dbg("%s - dle=%d skip=%d size=%d count=%d",
+ /* dev_dbg(dev, "%s - dle=%d skip=%d size=%d count=%d\n",
__func__, dleSeen, skip, size, count); */
if (size == 0)
@@ -530,12 +527,12 @@ static int gsp_receive(struct garmin_data *garmin_data_p,
if (data == ACK) {
ack_or_nak_seen = ACK;
- dbg("ACK packet complete.");
+ dev_dbg(dev, "ACK packet complete.\n");
} else if (data == NAK) {
ack_or_nak_seen = NAK;
- dbg("NAK packet complete.");
+ dev_dbg(dev, "NAK packet complete.\n");
} else {
- dbg("packet complete - id=0x%X.",
+ dev_dbg(dev, "packet complete - id=0x%X.\n",
0xFF & data);
gsp_rec_packet(garmin_data_p, size);
}
@@ -557,7 +554,7 @@ static int gsp_receive(struct garmin_data *garmin_data_p,
}
if (size >= GPS_IN_BUFSIZ) {
- dbg("%s - packet too large.", __func__);
+ dev_dbg(dev, "%s - packet too large.\n", __func__);
skip = 1;
size = GSP_INITIAL_OFFSET;
dleSeen = 0;
@@ -602,6 +599,7 @@ static int gsp_receive(struct garmin_data *garmin_data_p,
static int gsp_send(struct garmin_data *garmin_data_p,
const unsigned char *buf, int count)
{
+ struct device *dev = &garmin_data_p->port->dev;
const unsigned char *src;
unsigned char *dst;
int pktid = 0;
@@ -610,12 +608,12 @@ static int gsp_send(struct garmin_data *garmin_data_p,
int i = 0;
int k;
- dbg("%s - state %d - %d bytes.", __func__,
- garmin_data_p->state, count);
+ dev_dbg(dev, "%s - state %d - %d bytes.\n", __func__,
+ garmin_data_p->state, count);
k = garmin_data_p->outsize;
if ((k+count) > GPS_OUT_BUFSIZ) {
- dbg("packet too large");
+ dev_dbg(dev, "packet too large\n");
garmin_data_p->outsize = 0;
return -4;
}
@@ -634,28 +632,28 @@ static int gsp_send(struct garmin_data *garmin_data_p,
return 0;
}
- dbg("%s - %d bytes in buffer, %d bytes in pkt.", __func__, k, i);
+ dev_dbg(dev, "%s - %d bytes in buffer, %d bytes in pkt.\n", __func__, k, i);
/* garmin_data_p->outbuffer now contains a complete packet */
- usb_serial_debug_data(debug, &garmin_data_p->port->dev,
- __func__, k, garmin_data_p->outbuffer);
+ usb_serial_debug_data(&garmin_data_p->port->dev, __func__, k,
+ garmin_data_p->outbuffer);
garmin_data_p->outsize = 0;
if (GARMIN_LAYERID_APPL != getLayerId(garmin_data_p->outbuffer)) {
- dbg("not an application packet (%d)",
+ dev_dbg(dev, "not an application packet (%d)\n",
getLayerId(garmin_data_p->outbuffer));
return -1;
}
if (pktid > 255) {
- dbg("packet-id %d too large", pktid);
+ dev_dbg(dev, "packet-id %d too large\n", pktid);
return -2;
}
if (datalen > 255) {
- dbg("packet-size %d too large", datalen);
+ dev_dbg(dev, "packet-size %d too large\n", datalen);
return -3;
}
@@ -722,7 +720,7 @@ static int gsp_next_packet(struct garmin_data *garmin_data_p)
struct garmin_packet *pkt = NULL;
while ((pkt = pkt_pop(garmin_data_p)) != NULL) {
- dbg("%s - next pkt: %d", __func__, pkt->seq);
+ dev_dbg(&garmin_data_p->port->dev, "%s - next pkt: %d\n", __func__, pkt->seq);
result = gsp_send(garmin_data_p, pkt->data, pkt->size);
if (result > 0) {
kfree(pkt);
@@ -768,7 +766,9 @@ static int nat_receive(struct garmin_data *garmin_data_p,
if (len >= GPS_IN_BUFSIZ) {
/* seems to be an invalid packet, ignore rest
of input */
- dbg("%s - packet size too large: %d", __func__, len);
+ dev_dbg(&garmin_data_p->port->dev,
+ "%s - packet size too large: %d\n",
+ __func__, len);
garmin_data_p->insize = 0;
count = 0;
result = -EINVPKT;
@@ -849,10 +849,10 @@ static int process_resetdev_request(struct usb_serial_port *port)
spin_unlock_irqrestore(&garmin_data_p->lock, flags);
usb_kill_urb(port->interrupt_in_urb);
- dbg("%s - usb_reset_device", __func__);
+ dev_dbg(&port->dev, "%s - usb_reset_device\n", __func__);
status = usb_reset_device(port->serial->dev);
if (status)
- dbg("%s - usb_reset_device failed: %d",
+ dev_dbg(&port->dev, "%s - usb_reset_device failed: %d\n",
__func__, status);
return status;
}
@@ -889,7 +889,7 @@ static int garmin_init_session(struct usb_serial_port *port)
if (status == 0) {
usb_kill_urb(port->interrupt_in_urb);
- dbg("%s - adding interrupt input", __func__);
+ dev_dbg(&serial->dev->dev, "%s - adding interrupt input\n", __func__);
status = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (status)
dev_err(&serial->dev->dev,
@@ -902,7 +902,7 @@ static int garmin_init_session(struct usb_serial_port *port)
* gpsbabel/jeeps/gpslibusb.c gusb_reset_toggles()
*/
if (status == 0) {
- dbg("%s - starting session ...", __func__);
+ dev_dbg(&serial->dev->dev, "%s - starting session ...\n", __func__);
garmin_data_p->state = STATE_ACTIVE;
for (i = 0; i < 3; i++) {
@@ -952,8 +952,8 @@ static void garmin_close(struct usb_serial_port *port)
struct usb_serial *serial = port->serial;
struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
- dbg("%s - port %d - mode=%d state=%d flags=0x%X", __func__,
- port->number, garmin_data_p->mode,
+ dev_dbg(&port->dev, "%s - port %d - mode=%d state=%d flags=0x%X\n",
+ __func__, port->number, garmin_data_p->mode,
garmin_data_p->state, garmin_data_p->flags);
if (!serial)
@@ -1032,7 +1032,7 @@ static int garmin_write_bulk(struct usb_serial_port *port,
memcpy(buffer, buf, count);
- usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
+ usb_serial_debug_data(&port->dev, __func__, count, buffer);
usb_fill_bulk_urb(urb, serial->dev,
usb_sndbulkpipe(serial->dev,
@@ -1073,11 +1073,12 @@ static int garmin_write_bulk(struct usb_serial_port *port,
static int garmin_write(struct tty_struct *tty, struct usb_serial_port *port,
const unsigned char *buf, int count)
{
+ struct device *dev = &port->dev;
int pktid, pktsiz, len;
struct garmin_data *garmin_data_p = usb_get_serial_port_data(port);
__le32 *privpkt = (__le32 *)garmin_data_p->privpkt;
- usb_serial_debug_data(debug, &port->dev, __func__, count, buf);
+ usb_serial_debug_data(dev, __func__, count, buf);
if (garmin_data_p->state == STATE_RESET)
return -EIO;
@@ -1097,27 +1098,18 @@ static int garmin_write(struct tty_struct *tty, struct usb_serial_port *port,
&& GARMIN_LAYERID_PRIVATE ==
getLayerId(garmin_data_p->privpkt)) {
- dbg("%s - processing private request %d",
+ dev_dbg(dev, "%s - processing private request %d\n",
__func__, pktid);
/* drop all unfinished transfers */
garmin_clear(garmin_data_p);
switch (pktid) {
-
- case PRIV_PKTID_SET_DEBUG:
- if (pktsiz != 4)
- return -EINVPKT;
- debug = __le32_to_cpu(privpkt[3]);
- dbg("%s - debug level set to 0x%X",
- __func__, debug);
- break;
-
case PRIV_PKTID_SET_MODE:
if (pktsiz != 4)
return -EINVPKT;
garmin_data_p->mode = __le32_to_cpu(privpkt[3]);
- dbg("%s - mode set to %d",
+ dev_dbg(dev, "%s - mode set to %d\n",
__func__, garmin_data_p->mode);
break;
@@ -1133,7 +1125,7 @@ static int garmin_write(struct tty_struct *tty, struct usb_serial_port *port,
if (pktsiz != 4)
return -EINVPKT;
initial_mode = __le32_to_cpu(privpkt[3]);
- dbg("%s - initial_mode set to %d",
+ dev_dbg(dev, "%s - initial_mode set to %d\n",
__func__,
garmin_data_p->mode);
break;
@@ -1169,7 +1161,7 @@ static void garmin_read_process(struct garmin_data *garmin_data_p,
if (garmin_data_p->flags & FLAGS_DROP_DATA) {
/* abort-transfer cmd is actice */
- dbg("%s - pkt dropped", __func__);
+ dev_dbg(&garmin_data_p->port->dev, "%s - pkt dropped\n", __func__);
} else if (garmin_data_p->state != STATE_DISCONNECTED &&
garmin_data_p->state != STATE_RESET) {
@@ -1178,7 +1170,7 @@ static void garmin_read_process(struct garmin_data *garmin_data_p,
send it directly to the tty port */
if (garmin_data_p->flags & FLAGS_QUEUING) {
pkt_add(garmin_data_p, data, data_length);
- } else if (bulk_data ||
+ } else if (bulk_data ||
getLayerId(data) == GARMIN_LAYERID_APPL) {
spin_lock_irqsave(&garmin_data_p->lock, flags);
@@ -1208,18 +1200,17 @@ static void garmin_read_bulk_callback(struct urb *urb)
int retval;
if (!serial) {
- dbg("%s - bad serial pointer, exiting", __func__);
+ dev_dbg(&urb->dev->dev, "%s - bad serial pointer, exiting\n", __func__);
return;
}
if (status) {
- dbg("%s - nonzero read bulk status received: %d",
+ dev_dbg(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n",
__func__, status);
return;
}
- usb_serial_debug_data(debug, &port->dev,
- __func__, urb->actual_length, data);
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
garmin_read_process(garmin_data_p, data, urb->actual_length, 1);
@@ -1239,11 +1230,11 @@ static void garmin_read_bulk_callback(struct urb *urb)
retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (retval)
dev_err(&port->dev,
- "%s - failed resubmitting read urb, "
- "error %d\n", __func__, retval);
+ "%s - failed resubmitting read urb, error %d\n",
+ __func__, retval);
}
} else {
- dbg("%s - end of bulk data", __func__);
+ dev_dbg(&port->dev, "%s - end of bulk data\n", __func__);
spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags &= ~FLAGS_BULK_IN_ACTIVE;
spin_unlock_irqrestore(&garmin_data_p->lock, flags);
@@ -1268,23 +1259,23 @@ static void garmin_read_int_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d",
+ dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d",
+ dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n",
__func__, status);
return;
}
- usb_serial_debug_data(debug, &port->dev, __func__,
- urb->actual_length, urb->transfer_buffer);
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
+ urb->transfer_buffer);
if (urb->actual_length == sizeof(GARMIN_BULK_IN_AVAIL_REPLY) &&
0 == memcmp(data, GARMIN_BULK_IN_AVAIL_REPLY,
sizeof(GARMIN_BULK_IN_AVAIL_REPLY))) {
- dbg("%s - bulk data available.", __func__);
+ dev_dbg(&port->dev, "%s - bulk data available.\n", __func__);
if (0 == (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) {
@@ -1319,7 +1310,7 @@ static void garmin_read_int_callback(struct urb *urb)
garmin_data_p->serial_num = __le32_to_cpup(
(__le32 *)(data+GARMIN_PKTHDR_LENGTH));
- dbg("%s - start-of-session reply seen - serial %u.",
+ dev_dbg(&port->dev, "%s - start-of-session reply seen - serial %u.\n",
__func__, garmin_data_p->serial_num);
}
@@ -1495,7 +1486,5 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-module_param(debug, bool, S_IWUSR | S_IRUGO);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
module_param(initial_mode, int, S_IRUGO);
MODULE_PARM_DESC(initial_mode, "Initial mode");
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 9b026bf7afef..296612153ea2 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -24,8 +24,6 @@
#include <linux/kfifo.h>
#include <linux/serial.h>
-static int debug;
-
#ifdef CONFIG_USB_SERIAL_GENERIC
static __u16 vendor = 0x05f9;
@@ -60,11 +58,10 @@ static struct usb_serial_driver * const serial_drivers[] = {
#endif
-int usb_serial_generic_register(int _debug)
+int usb_serial_generic_register(void)
{
int retval = 0;
- debug = _debug;
#ifdef CONFIG_USB_SERIAL_GENERIC
generic_device_ids[0].idVendor = vendor;
generic_device_ids[0].idProduct = product;
@@ -171,8 +168,7 @@ retry:
urb->transfer_buffer,
port->bulk_out_size);
urb->transfer_buffer_length = count;
- usb_serial_debug_data(debug, &port->dev, __func__, count,
- urb->transfer_buffer);
+ usb_serial_debug_data(&port->dev, __func__, count, urb->transfer_buffer);
spin_lock_irqsave(&port->lock, flags);
port->tx_bytes += count;
spin_unlock_irqrestore(&port->lock, flags);
@@ -365,8 +361,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb)
return;
}
- usb_serial_debug_data(debug, &port->dev, __func__,
- urb->actual_length, data);
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
port->serial->type->process_read_urb(urb);
/* Throttle the device if requested by tty */
diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c
index f435575c4e6e..8e6faaf3580c 100644
--- a/drivers/usb/serial/io_edgeport.c
+++ b/drivers/usb/serial/io_edgeport.c
@@ -190,9 +190,6 @@ static const struct divisor_table_entry divisor_table[] = {
{ 230400, 1},
};
-/* local variables */
-static bool debug;
-
/* Number of outstanding Command Write Urbs */
static atomic_t CmdUrbs = ATOMIC_INIT(0);
@@ -244,7 +241,7 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData,
__u8 lsr, __u8 data);
static int send_iosp_ext_cmd(struct edgeport_port *edge_port, __u8 command,
__u8 param);
-static int calc_baud_rate_divisor(int baud_rate, int *divisor);
+static int calc_baud_rate_divisor(struct device *dev, int baud_rate, int *divisor);
static int send_cmd_write_baud_rate(struct edgeport_port *edge_port,
int baudRate);
static void change_port_settings(struct tty_struct *tty,
@@ -286,6 +283,7 @@ static void unicode_to_ascii(char *string, int buflen,
************************************************************************/
static void update_edgeport_E2PROM(struct edgeport_serial *edge_serial)
{
+ struct device *dev = &edge_serial->serial->dev->dev;
__u32 BootCurVer;
__u32 BootNewVer;
__u8 BootMajorVersion;
@@ -311,7 +309,7 @@ static void update_edgeport_E2PROM(struct edgeport_serial *edge_serial)
response = request_ihex_firmware(&fw, fw_name,
&edge_serial->serial->dev->dev);
if (response) {
- printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
+ dev_err(dev, "Failed to load image \"%s\" err %d\n",
fw_name, response);
return;
}
@@ -330,20 +328,20 @@ static void update_edgeport_E2PROM(struct edgeport_serial *edge_serial)
(BootMinorVersion << 16) +
BootBuildNumber;
- dbg("Current Boot Image version %d.%d.%d",
+ dev_dbg(dev, "Current Boot Image version %d.%d.%d\n",
edge_serial->boot_descriptor.MajorVersion,
edge_serial->boot_descriptor.MinorVersion,
le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
if (BootNewVer > BootCurVer) {
- dbg("**Update Boot Image from %d.%d.%d to %d.%d.%d",
+ dev_dbg(dev, "**Update Boot Image from %d.%d.%d to %d.%d.%d\n",
edge_serial->boot_descriptor.MajorVersion,
edge_serial->boot_descriptor.MinorVersion,
le16_to_cpu(edge_serial->boot_descriptor.BuildNumber),
BootMajorVersion, BootMinorVersion, BootBuildNumber);
- dbg("Downloading new Boot Image");
+ dev_dbg(dev, "Downloading new Boot Image\n");
for (rec = ihex_next_binrec(rec); rec;
rec = ihex_next_binrec(rec)) {
@@ -362,7 +360,7 @@ static void update_edgeport_E2PROM(struct edgeport_serial *edge_serial)
}
}
} else {
- dbg("Boot Image -- already up to date");
+ dev_dbg(dev, "Boot Image -- already up to date\n");
}
release_firmware(fw);
}
@@ -379,7 +377,7 @@ static int get_string_desc(struct usb_device *dev, int Id,
struct usb_string_descriptor StringDesc;
struct usb_string_descriptor *pStringDesc;
- dbg("%s - USB String ID = %d", __func__, Id);
+ dev_dbg(&dev->dev, "%s - USB String ID = %d\n", __func__, Id);
if (!usb_get_descriptor(dev, USB_DT_STRING, Id, &StringDesc,
sizeof(StringDesc)))
@@ -400,34 +398,39 @@ static int get_string_desc(struct usb_device *dev, int Id,
}
#endif
-static void dump_product_info(struct edgeport_product_info *product_info)
+static void dump_product_info(struct edgeport_serial *edge_serial,
+ struct edgeport_product_info *product_info)
{
+ struct device *dev = &edge_serial->serial->dev->dev;
+
/* Dump Product Info structure */
- dbg("**Product Information:");
- dbg(" ProductId %x", product_info->ProductId);
- dbg(" NumPorts %d", product_info->NumPorts);
- dbg(" ProdInfoVer %d", product_info->ProdInfoVer);
- dbg(" IsServer %d", product_info->IsServer);
- dbg(" IsRS232 %d", product_info->IsRS232);
- dbg(" IsRS422 %d", product_info->IsRS422);
- dbg(" IsRS485 %d", product_info->IsRS485);
- dbg(" RomSize %d", product_info->RomSize);
- dbg(" RamSize %d", product_info->RamSize);
- dbg(" CpuRev %x", product_info->CpuRev);
- dbg(" BoardRev %x", product_info->BoardRev);
- dbg(" BootMajorVersion %d.%d.%d", product_info->BootMajorVersion,
- product_info->BootMinorVersion,
- le16_to_cpu(product_info->BootBuildNumber));
- dbg(" FirmwareMajorVersion %d.%d.%d",
- product_info->FirmwareMajorVersion,
- product_info->FirmwareMinorVersion,
- le16_to_cpu(product_info->FirmwareBuildNumber));
- dbg(" ManufactureDescDate %d/%d/%d",
- product_info->ManufactureDescDate[0],
- product_info->ManufactureDescDate[1],
- product_info->ManufactureDescDate[2]+1900);
- dbg(" iDownloadFile 0x%x", product_info->iDownloadFile);
- dbg(" EpicVer %d", product_info->EpicVer);
+ dev_dbg(dev, "**Product Information:\n");
+ dev_dbg(dev, " ProductId %x\n", product_info->ProductId);
+ dev_dbg(dev, " NumPorts %d\n", product_info->NumPorts);
+ dev_dbg(dev, " ProdInfoVer %d\n", product_info->ProdInfoVer);
+ dev_dbg(dev, " IsServer %d\n", product_info->IsServer);
+ dev_dbg(dev, " IsRS232 %d\n", product_info->IsRS232);
+ dev_dbg(dev, " IsRS422 %d\n", product_info->IsRS422);
+ dev_dbg(dev, " IsRS485 %d\n", product_info->IsRS485);
+ dev_dbg(dev, " RomSize %d\n", product_info->RomSize);
+ dev_dbg(dev, " RamSize %d\n", product_info->RamSize);
+ dev_dbg(dev, " CpuRev %x\n", product_info->CpuRev);
+ dev_dbg(dev, " BoardRev %x\n", product_info->BoardRev);
+ dev_dbg(dev, " BootMajorVersion %d.%d.%d\n",
+ product_info->BootMajorVersion,
+ product_info->BootMinorVersion,
+ le16_to_cpu(product_info->BootBuildNumber));
+ dev_dbg(dev, " FirmwareMajorVersion %d.%d.%d\n",
+ product_info->FirmwareMajorVersion,
+ product_info->FirmwareMinorVersion,
+ le16_to_cpu(product_info->FirmwareBuildNumber));
+ dev_dbg(dev, " ManufactureDescDate %d/%d/%d\n",
+ product_info->ManufactureDescDate[0],
+ product_info->ManufactureDescDate[1],
+ product_info->ManufactureDescDate[2]+1900);
+ dev_dbg(dev, " iDownloadFile 0x%x\n",
+ product_info->iDownloadFile);
+ dev_dbg(dev, " EpicVer %d\n", product_info->EpicVer);
}
static void get_product_info(struct edgeport_serial *edge_serial)
@@ -462,7 +465,7 @@ static void get_product_info(struct edgeport_serial *edge_serial)
product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_80251;
else
product_info->iDownloadFile = EDGE_DOWNLOAD_FILE_I930;
-
+
/* Determine Product type and set appropriate flags */
switch (DEVICE_ID_FROM_USB_PRODUCT_ID(product_info->ProductId)) {
case ION_DEVICE_ID_EDGEPORT_COMPATIBLE:
@@ -490,7 +493,7 @@ static void get_product_info(struct edgeport_serial *edge_serial)
break;
}
- dump_product_info(product_info);
+ dump_product_info(edge_serial, product_info);
}
static int get_epic_descriptor(struct edgeport_serial *ep)
@@ -500,6 +503,7 @@ static int get_epic_descriptor(struct edgeport_serial *ep)
struct edgeport_product_info *product_info = &ep->product_info;
struct edge_compatibility_descriptor *epic = &ep->epic_descriptor;
struct edge_compatibility_bits *bits;
+ struct device *dev = &serial->dev->dev;
ep->is_epic = 0;
result = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
@@ -509,8 +513,6 @@ static int get_epic_descriptor(struct edgeport_serial *ep)
sizeof(struct edge_compatibility_descriptor),
300);
- dbg("%s result = %d", __func__, result);
-
if (result > 0) {
ep->is_epic = 1;
memset(product_info, 0, sizeof(struct edgeport_product_info));
@@ -524,23 +526,23 @@ static int get_epic_descriptor(struct edgeport_serial *ep)
product_info->EpicVer = epic->EpicVer;
product_info->Epic = epic->Supports;
product_info->ProductId = ION_DEVICE_ID_EDGEPORT_COMPATIBLE;
- dump_product_info(product_info);
+ dump_product_info(ep, product_info);
bits = &ep->epic_descriptor.Supports;
- dbg("**EPIC descriptor:");
- dbg(" VendEnableSuspend: %s", bits->VendEnableSuspend ? "TRUE": "FALSE");
- dbg(" IOSPOpen : %s", bits->IOSPOpen ? "TRUE": "FALSE");
- dbg(" IOSPClose : %s", bits->IOSPClose ? "TRUE": "FALSE");
- dbg(" IOSPChase : %s", bits->IOSPChase ? "TRUE": "FALSE");
- dbg(" IOSPSetRxFlow : %s", bits->IOSPSetRxFlow ? "TRUE": "FALSE");
- dbg(" IOSPSetTxFlow : %s", bits->IOSPSetTxFlow ? "TRUE": "FALSE");
- dbg(" IOSPSetXChar : %s", bits->IOSPSetXChar ? "TRUE": "FALSE");
- dbg(" IOSPRxCheck : %s", bits->IOSPRxCheck ? "TRUE": "FALSE");
- dbg(" IOSPSetClrBreak : %s", bits->IOSPSetClrBreak ? "TRUE": "FALSE");
- dbg(" IOSPWriteMCR : %s", bits->IOSPWriteMCR ? "TRUE": "FALSE");
- dbg(" IOSPWriteLCR : %s", bits->IOSPWriteLCR ? "TRUE": "FALSE");
- dbg(" IOSPSetBaudRate : %s", bits->IOSPSetBaudRate ? "TRUE": "FALSE");
- dbg(" TrueEdgeport : %s", bits->TrueEdgeport ? "TRUE": "FALSE");
+ dev_dbg(dev, "**EPIC descriptor:\n");
+ dev_dbg(dev, " VendEnableSuspend: %s\n", bits->VendEnableSuspend ? "TRUE": "FALSE");
+ dev_dbg(dev, " IOSPOpen : %s\n", bits->IOSPOpen ? "TRUE": "FALSE");
+ dev_dbg(dev, " IOSPClose : %s\n", bits->IOSPClose ? "TRUE": "FALSE");
+ dev_dbg(dev, " IOSPChase : %s\n", bits->IOSPChase ? "TRUE": "FALSE");
+ dev_dbg(dev, " IOSPSetRxFlow : %s\n", bits->IOSPSetRxFlow ? "TRUE": "FALSE");
+ dev_dbg(dev, " IOSPSetTxFlow : %s\n", bits->IOSPSetTxFlow ? "TRUE": "FALSE");
+ dev_dbg(dev, " IOSPSetXChar : %s\n", bits->IOSPSetXChar ? "TRUE": "FALSE");
+ dev_dbg(dev, " IOSPRxCheck : %s\n", bits->IOSPRxCheck ? "TRUE": "FALSE");
+ dev_dbg(dev, " IOSPSetClrBreak : %s\n", bits->IOSPSetClrBreak ? "TRUE": "FALSE");
+ dev_dbg(dev, " IOSPWriteMCR : %s\n", bits->IOSPWriteMCR ? "TRUE": "FALSE");
+ dev_dbg(dev, " IOSPWriteLCR : %s\n", bits->IOSPWriteLCR ? "TRUE": "FALSE");
+ dev_dbg(dev, " IOSPSetBaudRate : %s\n", bits->IOSPSetBaudRate ? "TRUE": "FALSE");
+ dev_dbg(dev, " TrueEdgeport : %s\n", bits->TrueEdgeport ? "TRUE": "FALSE");
}
return result;
@@ -561,7 +563,8 @@ static int get_epic_descriptor(struct edgeport_serial *ep)
*****************************************************************************/
static void edge_interrupt_callback(struct urb *urb)
{
- struct edgeport_serial *edge_serial = urb->context;
+ struct edgeport_serial *edge_serial = urb->context;
+ struct device *dev;
struct edgeport_port *edge_port;
struct usb_serial_port *port;
struct tty_struct *tty;
@@ -574,8 +577,6 @@ static void edge_interrupt_callback(struct urb *urb)
int result;
int status = urb->status;
- dbg("%s", __func__);
-
switch (status) {
case 0:
/* success */
@@ -584,36 +585,42 @@ static void edge_interrupt_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d",
- __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d", __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
goto exit;
}
+ dev = &edge_serial->serial->dev->dev;
+
/* process this interrupt-read even if there are no ports open */
if (length) {
- usb_serial_debug_data(debug, &edge_serial->serial->dev->dev,
- __func__, length, data);
+ usb_serial_debug_data(dev, __func__, length, data);
if (length > 1) {
bytes_avail = data[0] | (data[1] << 8);
if (bytes_avail) {
spin_lock(&edge_serial->es_lock);
edge_serial->rxBytesAvail += bytes_avail;
- dbg("%s - bytes_avail=%d, rxBytesAvail=%d, read_in_progress=%d", __func__, bytes_avail, edge_serial->rxBytesAvail, edge_serial->read_in_progress);
+ dev_dbg(dev,
+ "%s - bytes_avail=%d, rxBytesAvail=%d, read_in_progress=%d\n",
+ __func__, bytes_avail,
+ edge_serial->rxBytesAvail,
+ edge_serial->read_in_progress);
if (edge_serial->rxBytesAvail > 0 &&
!edge_serial->read_in_progress) {
- dbg("%s - posting a read", __func__);
+ dev_dbg(dev, "%s - posting a read\n", __func__);
edge_serial->read_in_progress = true;
/* we have pending bytes on the
bulk in pipe, send a request */
result = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
if (result) {
- dev_err(&edge_serial->serial->dev->dev, "%s - usb_submit_urb(read bulk) failed with result = %d\n", __func__, result);
+ dev_err(dev,
+ "%s - usb_submit_urb(read bulk) failed with result = %d\n",
+ __func__, result);
edge_serial->read_in_progress = false;
}
}
@@ -633,9 +640,9 @@ static void edge_interrupt_callback(struct urb *urb)
spin_lock(&edge_port->ep_lock);
edge_port->txCredits += txCredits;
spin_unlock(&edge_port->ep_lock);
- dbg("%s - txcredits for port%d = %d",
- __func__, portNumber,
- edge_port->txCredits);
+ dev_dbg(dev, "%s - txcredits for port%d = %d\n",
+ __func__, portNumber,
+ edge_port->txCredits);
/* tell the tty driver that something
has changed */
@@ -673,49 +680,48 @@ exit:
static void edge_bulk_in_callback(struct urb *urb)
{
struct edgeport_serial *edge_serial = urb->context;
+ struct device *dev;
unsigned char *data = urb->transfer_buffer;
int retval;
__u16 raw_data_length;
int status = urb->status;
- dbg("%s", __func__);
-
if (status) {
- dbg("%s - nonzero read bulk status received: %d",
- __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n",
+ __func__, status);
edge_serial->read_in_progress = false;
return;
}
if (urb->actual_length == 0) {
- dbg("%s - read bulk callback with no data", __func__);
+ dev_dbg(&urb->dev->dev, "%s - read bulk callback with no data\n", __func__);
edge_serial->read_in_progress = false;
return;
}
+ dev = &edge_serial->serial->dev->dev;
raw_data_length = urb->actual_length;
- usb_serial_debug_data(debug, &edge_serial->serial->dev->dev,
- __func__, raw_data_length, data);
+ usb_serial_debug_data(dev, __func__, raw_data_length, data);
spin_lock(&edge_serial->es_lock);
/* decrement our rxBytes available by the number that we just got */
edge_serial->rxBytesAvail -= raw_data_length;
- dbg("%s - Received = %d, rxBytesAvail %d", __func__,
- raw_data_length, edge_serial->rxBytesAvail);
+ dev_dbg(dev, "%s - Received = %d, rxBytesAvail %d\n", __func__,
+ raw_data_length, edge_serial->rxBytesAvail);
process_rcvd_data(edge_serial, data, urb->actual_length);
/* check to see if there's any more data for us to read */
if (edge_serial->rxBytesAvail > 0) {
- dbg("%s - posting a read", __func__);
+ dev_dbg(dev, "%s - posting a read\n", __func__);
retval = usb_submit_urb(edge_serial->read_urb, GFP_ATOMIC);
if (retval) {
- dev_err(&urb->dev->dev,
- "%s - usb_submit_urb(read bulk) failed, "
- "retval = %d\n", __func__, retval);
+ dev_err(dev,
+ "%s - usb_submit_urb(read bulk) failed, retval = %d\n",
+ __func__, retval);
edge_serial->read_in_progress = false;
}
} else {
@@ -737,11 +743,10 @@ static void edge_bulk_out_data_callback(struct urb *urb)
struct tty_struct *tty;
int status = urb->status;
- dbg("%s", __func__);
-
if (status) {
- dbg("%s - nonzero write bulk status received: %d",
- __func__, status);
+ dev_dbg(&urb->dev->dev,
+ "%s - nonzero write bulk status received: %d\n",
+ __func__, status);
}
tty = tty_port_tty_get(&edge_port->port->port);
@@ -773,11 +778,9 @@ static void edge_bulk_out_cmd_callback(struct urb *urb)
struct tty_struct *tty;
int status = urb->status;
- dbg("%s", __func__);
-
atomic_dec(&CmdUrbs);
- dbg("%s - FREE URB %p (outstanding %d)", __func__,
- urb, atomic_read(&CmdUrbs));
+ dev_dbg(&urb->dev->dev, "%s - FREE URB %p (outstanding %d)\n",
+ __func__, urb, atomic_read(&CmdUrbs));
/* clean up the transfer buffer */
@@ -787,8 +790,9 @@ static void edge_bulk_out_cmd_callback(struct urb *urb)
usb_free_urb(urb);
if (status) {
- dbg("%s - nonzero write bulk status received: %d",
- __func__, status);
+ dev_dbg(&urb->dev->dev,
+ "%s - nonzero write bulk status received: %d\n",
+ __func__, status);
return;
}
@@ -819,12 +823,11 @@ static void edge_bulk_out_cmd_callback(struct urb *urb)
static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
+ struct device *dev = &port->dev;
struct usb_serial *serial;
struct edgeport_serial *edge_serial;
int response;
- dbg("%s - port %d", __func__, port->number);
-
if (edge_port == NULL)
return -ENODEV;
@@ -875,9 +878,8 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
response = usb_submit_urb(edge_serial->interrupt_read_urb,
GFP_KERNEL);
if (response) {
- dev_err(&port->dev,
- "%s - Error %d submitting control urb\n",
- __func__, response);
+ dev_err(dev, "%s - Error %d submitting control urb\n",
+ __func__, response);
}
}
@@ -902,8 +904,7 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
response = send_iosp_ext_cmd(edge_port, IOSP_CMD_OPEN_PORT, 0);
if (response < 0) {
- dev_err(&port->dev, "%s - error sending open port command\n",
- __func__);
+ dev_err(dev, "%s - error sending open port command\n", __func__);
edge_port->openPending = false;
return -ENODEV;
}
@@ -914,7 +915,7 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
if (!edge_port->open) {
/* open timed out */
- dbg("%s - open timedout", __func__);
+ dev_dbg(dev, "%s - open timedout\n", __func__);
edge_port->openPending = false;
return -ENODEV;
}
@@ -927,7 +928,7 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
edge_port->txfifo.fifo = kmalloc(edge_port->maxTxCredits, GFP_KERNEL);
if (!edge_port->txfifo.fifo) {
- dbg("%s - no memory", __func__);
+ dev_dbg(dev, "%s - no memory\n", __func__);
edge_close(port);
return -ENOMEM;
}
@@ -937,15 +938,13 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
edge_port->write_in_progress = false;
if (!edge_port->write_urb) {
- dbg("%s - no memory", __func__);
+ dev_dbg(dev, "%s - no memory\n", __func__);
edge_close(port);
return -ENOMEM;
}
- dbg("%s(%d) - Initialize TX fifo to %d bytes",
- __func__, port->number, edge_port->maxTxCredits);
-
- dbg("%s exited", __func__);
+ dev_dbg(dev, "%s(%d) - Initialize TX fifo to %d bytes\n",
+ __func__, port->number, edge_port->maxTxCredits);
return 0;
}
@@ -963,6 +962,7 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
************************************************************************/
static void block_until_chase_response(struct edgeport_port *edge_port)
{
+ struct device *dev = &edge_port->port->dev;
DEFINE_WAIT(wait);
__u16 lastCredits;
int timeout = 1*HZ;
@@ -974,11 +974,11 @@ static void block_until_chase_response(struct edgeport_port *edge_port)
/* Did we get our Chase response */
if (!edge_port->chaseResponsePending) {
- dbg("%s - Got Chase Response", __func__);
+ dev_dbg(dev, "%s - Got Chase Response\n", __func__);
/* did we get all of our credit back? */
if (edge_port->txCredits == edge_port->maxTxCredits) {
- dbg("%s - Got all credits", __func__);
+ dev_dbg(dev, "%s - Got all credits\n", __func__);
return;
}
}
@@ -994,12 +994,12 @@ static void block_until_chase_response(struct edgeport_port *edge_port)
loop--;
if (loop == 0) {
edge_port->chaseResponsePending = false;
- dbg("%s - Chase TIMEOUT", __func__);
+ dev_dbg(dev, "%s - Chase TIMEOUT\n", __func__);
return;
}
} else {
/* Reset timeout value back to 10 seconds */
- dbg("%s - Last %d, Current %d", __func__,
+ dev_dbg(dev, "%s - Last %d, Current %d\n", __func__,
lastCredits, edge_port->txCredits);
loop = 10;
}
@@ -1019,6 +1019,7 @@ static void block_until_chase_response(struct edgeport_port *edge_port)
************************************************************************/
static void block_until_tx_empty(struct edgeport_port *edge_port)
{
+ struct device *dev = &edge_port->port->dev;
DEFINE_WAIT(wait);
struct TxFifo *fifo = &edge_port->txfifo;
__u32 lastCount;
@@ -1031,7 +1032,7 @@ static void block_until_tx_empty(struct edgeport_port *edge_port)
/* Is the Edgeport Buffer empty? */
if (lastCount == 0) {
- dbg("%s - TX Buffer Empty", __func__);
+ dev_dbg(dev, "%s - TX Buffer Empty\n", __func__);
return;
}
@@ -1041,13 +1042,13 @@ static void block_until_tx_empty(struct edgeport_port *edge_port)
schedule_timeout(timeout);
finish_wait(&edge_port->wait_chase, &wait);
- dbg("%s wait", __func__);
+ dev_dbg(dev, "%s wait\n", __func__);
if (lastCount == fifo->count) {
/* No activity.. count down. */
loop--;
if (loop == 0) {
- dbg("%s - TIMEOUT", __func__);
+ dev_dbg(dev, "%s - TIMEOUT\n", __func__);
return;
}
} else {
@@ -1068,8 +1069,6 @@ static void edge_close(struct usb_serial_port *port)
struct edgeport_port *edge_port;
int status;
- dbg("%s - port %d", __func__, port->number);
-
edge_serial = usb_get_serial_data(port->serial);
edge_port = usb_get_serial_port_data(port);
if (edge_serial == NULL || edge_port == NULL)
@@ -1086,7 +1085,7 @@ static void edge_close(struct usb_serial_port *port)
/* flush and chase */
edge_port->chaseResponsePending = true;
- dbg("%s - Sending IOSP_CMD_CHASE_PORT", __func__);
+ dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CHASE_PORT\n", __func__);
status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0);
if (status == 0)
/* block until chase finished */
@@ -1099,7 +1098,7 @@ static void edge_close(struct usb_serial_port *port)
((edge_serial->is_epic) &&
(edge_serial->epic_descriptor.Supports.IOSPClose))) {
/* close the port */
- dbg("%s - Sending IOSP_CMD_CLOSE_PORT", __func__);
+ dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLOSE_PORT\n", __func__);
send_iosp_ext_cmd(edge_port, IOSP_CMD_CLOSE_PORT, 0);
}
@@ -1119,8 +1118,6 @@ static void edge_close(struct usb_serial_port *port)
}
kfree(edge_port->txfifo.fifo);
edge_port->txfifo.fifo = NULL;
-
- dbg("%s exited", __func__);
}
/*****************************************************************************
@@ -1141,8 +1138,6 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
int secondhalf;
unsigned long flags;
- dbg("%s - port %d", __func__, port->number);
-
if (edge_port == NULL)
return -ENODEV;
@@ -1155,14 +1150,14 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
copySize = min((unsigned int)count,
(edge_port->txCredits - fifo->count));
- dbg("%s(%d) of %d byte(s) Fifo room %d -- will copy %d bytes",
- __func__, port->number, count,
+ dev_dbg(&port->dev, "%s(%d) of %d byte(s) Fifo room %d -- will copy %d bytes\n",
+ __func__, port->number, count,
edge_port->txCredits - fifo->count, copySize);
/* catch writes of 0 bytes which the tty driver likes to give us,
and when txCredits is empty */
if (copySize == 0) {
- dbg("%s - copySize = Zero", __func__);
+ dev_dbg(&port->dev, "%s - copySize = Zero\n", __func__);
goto finish_write;
}
@@ -1175,13 +1170,12 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
*/
bytesleft = fifo->size - fifo->head;
firsthalf = min(bytesleft, copySize);
- dbg("%s - copy %d bytes of %d into fifo ", __func__,
- firsthalf, bytesleft);
+ dev_dbg(&port->dev, "%s - copy %d bytes of %d into fifo \n", __func__,
+ firsthalf, bytesleft);
/* now copy our data */
memcpy(&fifo->fifo[fifo->head], data, firsthalf);
- usb_serial_debug_data(debug, &port->dev, __func__,
- firsthalf, &fifo->fifo[fifo->head]);
+ usb_serial_debug_data(&port->dev, __func__, firsthalf, &fifo->fifo[fifo->head]);
/* update the index and size */
fifo->head += firsthalf;
@@ -1194,10 +1188,9 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
secondhalf = copySize-firsthalf;
if (secondhalf) {
- dbg("%s - copy rest of data %d", __func__, secondhalf);
+ dev_dbg(&port->dev, "%s - copy rest of data %d\n", __func__, secondhalf);
memcpy(&fifo->fifo[fifo->head], &data[firsthalf], secondhalf);
- usb_serial_debug_data(debug, &port->dev, __func__,
- secondhalf, &fifo->fifo[fifo->head]);
+ usb_serial_debug_data(&port->dev, __func__, secondhalf, &fifo->fifo[fifo->head]);
/* update the index and size */
fifo->count += secondhalf;
fifo->head += secondhalf;
@@ -1212,8 +1205,8 @@ finish_write:
send_more_port_data((struct edgeport_serial *)
usb_get_serial_data(port->serial), edge_port);
- dbg("%s wrote %d byte(s) TxCredits %d, Fifo %d", __func__,
- copySize, edge_port->txCredits, fifo->count);
+ dev_dbg(&port->dev, "%s wrote %d byte(s) TxCredits %d, Fifo %d\n",
+ __func__, copySize, edge_port->txCredits, fifo->count);
return copySize;
}
@@ -1236,6 +1229,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial,
struct edgeport_port *edge_port)
{
struct TxFifo *fifo = &edge_port->txfifo;
+ struct device *dev = &edge_port->port->dev;
struct urb *urb;
unsigned char *buffer;
int status;
@@ -1245,16 +1239,14 @@ static void send_more_port_data(struct edgeport_serial *edge_serial,
int secondhalf;
unsigned long flags;
- dbg("%s(%d)", __func__, edge_port->port->number);
-
spin_lock_irqsave(&edge_port->ep_lock, flags);
if (edge_port->write_in_progress ||
!edge_port->open ||
(fifo->count == 0)) {
- dbg("%s(%d) EXIT - fifo %d, PendingWrite = %d",
- __func__, edge_port->port->number,
- fifo->count, edge_port->write_in_progress);
+ dev_dbg(dev, "%s(%d) EXIT - fifo %d, PendingWrite = %d\n",
+ __func__, edge_port->port->number,
+ fifo->count, edge_port->write_in_progress);
goto exit_send;
}
@@ -1266,7 +1258,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial,
* it's better to wait for more credits so we can do a larger write.
*/
if (edge_port->txCredits < EDGE_FW_GET_TX_CREDITS_SEND_THRESHOLD(edge_port->maxTxCredits, EDGE_FW_BULK_MAX_PACKET_SIZE)) {
- dbg("%s(%d) Not enough credit - fifo %d TxCredit %d",
+ dev_dbg(dev, "%s(%d) Not enough credit - fifo %d TxCredit %d\n",
__func__, edge_port->port->number, fifo->count,
edge_port->txCredits);
goto exit_send;
@@ -1315,8 +1307,7 @@ static void send_more_port_data(struct edgeport_serial *edge_serial,
}
if (count)
- usb_serial_debug_data(debug, &edge_port->port->dev,
- __func__, count, &buffer[2]);
+ usb_serial_debug_data(&edge_port->port->dev, __func__, count, &buffer[2]);
/* fill up the urb with all of our data and submit it */
usb_fill_bulk_urb(urb, edge_serial->serial->dev,
@@ -1341,8 +1332,8 @@ static void send_more_port_data(struct edgeport_serial *edge_serial,
edge_port->txCredits += count;
edge_port->icount.tx -= count;
}
- dbg("%s wrote %d byte(s) TxCredit %d, Fifo %d",
- __func__, count, edge_port->txCredits, fifo->count);
+ dev_dbg(dev, "%s wrote %d byte(s) TxCredit %d, Fifo %d\n",
+ __func__, count, edge_port->txCredits, fifo->count);
exit_send:
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
@@ -1363,17 +1354,13 @@ static int edge_write_room(struct tty_struct *tty)
int room;
unsigned long flags;
- dbg("%s", __func__);
-
if (edge_port == NULL)
return 0;
if (edge_port->closePending)
return 0;
- dbg("%s - port %d", __func__, port->number);
-
if (!edge_port->open) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return 0;
}
@@ -1382,7 +1369,7 @@ static int edge_write_room(struct tty_struct *tty)
room = edge_port->txCredits - edge_port->txfifo.count;
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
- dbg("%s - returns %d", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
return room;
}
@@ -1403,15 +1390,13 @@ static int edge_chars_in_buffer(struct tty_struct *tty)
int num_chars;
unsigned long flags;
- dbg("%s", __func__);
-
if (edge_port == NULL)
return 0;
if (edge_port->closePending)
return 0;
if (!edge_port->open) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return 0;
}
@@ -1420,8 +1405,8 @@ static int edge_chars_in_buffer(struct tty_struct *tty)
edge_port->txfifo.count;
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
if (num_chars) {
- dbg("%s(port %d) - returns %d", __func__,
- port->number, num_chars);
+ dev_dbg(&port->dev, "%s(port %d) - returns %d\n", __func__,
+ port->number, num_chars);
}
return num_chars;
@@ -1439,13 +1424,11 @@ static void edge_throttle(struct tty_struct *tty)
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status;
- dbg("%s - port %d", __func__, port->number);
-
if (edge_port == NULL)
return;
if (!edge_port->open) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
}
@@ -1479,13 +1462,11 @@ static void edge_unthrottle(struct tty_struct *tty)
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status;
- dbg("%s - port %d", __func__, port->number);
-
if (edge_port == NULL)
return;
if (!edge_port->open) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
}
@@ -1517,18 +1498,14 @@ static void edge_set_termios(struct tty_struct *tty,
unsigned int cflag;
cflag = tty->termios.c_cflag;
- dbg("%s - clfag %08x iflag %08x", __func__,
- tty->termios.c_cflag, tty->termios.c_iflag);
- dbg("%s - old clfag %08x old iflag %08x", __func__,
- old_termios->c_cflag, old_termios->c_iflag);
-
- dbg("%s - port %d", __func__, port->number);
+ dev_dbg(&port->dev, "%s - clfag %08x iflag %08x\n", __func__, tty->termios.c_cflag, tty->termios.c_iflag);
+ dev_dbg(&port->dev, "%s - old clfag %08x old iflag %08x\n", __func__, old_termios->c_cflag, old_termios->c_iflag);
if (edge_port == NULL)
return;
if (!edge_port->open) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
}
@@ -1556,7 +1533,7 @@ static int get_lsr_info(struct edgeport_port *edge_port,
spin_lock_irqsave(&edge_port->ep_lock, flags);
if (edge_port->maxTxCredits == edge_port->txCredits &&
edge_port->txfifo.count == 0) {
- dbg("%s -- Empty", __func__);
+ dev_dbg(&edge_port->port->dev, "%s -- Empty\n", __func__);
result = TIOCSER_TEMT;
}
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
@@ -1573,8 +1550,6 @@ static int edge_tiocmset(struct tty_struct *tty,
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
unsigned int mcr;
- dbg("%s - port %d", __func__, port->number);
-
mcr = edge_port->shadowMCR;
if (set & TIOCM_RTS)
mcr |= MCR_RTS;
@@ -1605,8 +1580,6 @@ static int edge_tiocmget(struct tty_struct *tty)
unsigned int msr;
unsigned int mcr;
- dbg("%s - port %d", __func__, port->number);
-
msr = edge_port->shadowMSR;
mcr = edge_port->shadowMCR;
result = ((mcr & MCR_DTR) ? TIOCM_DTR: 0) /* 0x002 */
@@ -1616,9 +1589,6 @@ static int edge_tiocmget(struct tty_struct *tty)
| ((msr & EDGEPORT_MSR_RI) ? TIOCM_RI: 0) /* 0x080 */
| ((msr & EDGEPORT_MSR_DSR) ? TIOCM_DSR: 0); /* 0x100 */
-
- dbg("%s -- %x", __func__, result);
-
return result;
}
@@ -1642,8 +1612,8 @@ static int edge_get_icount(struct tty_struct *tty,
icount->brk = cnow.brk;
icount->buf_overrun = cnow.buf_overrun;
- dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d",
- __func__, port->number, icount->rx, icount->tx);
+ dev_dbg(&port->dev, "%s (%d) TIOCGICOUNT RX=%d, TX=%d\n", __func__,
+ port->number, icount->rx, icount->tx);
return 0;
}
@@ -1686,19 +1656,19 @@ static int edge_ioctl(struct tty_struct *tty,
struct async_icount cnow;
struct async_icount cprev;
- dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+ dev_dbg(&port->dev, "%s - port %d, cmd = 0x%x\n", __func__, port->number, cmd);
switch (cmd) {
case TIOCSERGETLSR:
- dbg("%s (%d) TIOCSERGETLSR", __func__, port->number);
+ dev_dbg(&port->dev, "%s (%d) TIOCSERGETLSR\n", __func__, port->number);
return get_lsr_info(edge_port, (unsigned int __user *) arg);
case TIOCGSERIAL:
- dbg("%s (%d) TIOCGSERIAL", __func__, port->number);
+ dev_dbg(&port->dev, "%s (%d) TIOCGSERIAL\n", __func__, port->number);
return get_serial_info(edge_port, (struct serial_struct __user *) arg);
case TIOCMIWAIT:
- dbg("%s (%d) TIOCMIWAIT", __func__, port->number);
+ dev_dbg(&port->dev, "%s (%d) TIOCMIWAIT\n", __func__, port->number);
cprev = edge_port->icount;
while (1) {
prepare_to_wait(&edge_port->delta_msr_wait,
@@ -1745,7 +1715,7 @@ static void edge_break(struct tty_struct *tty, int break_state)
/* flush and chase */
edge_port->chaseResponsePending = true;
- dbg("%s - Sending IOSP_CMD_CHASE_PORT", __func__);
+ dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CHASE_PORT\n", __func__);
status = send_iosp_ext_cmd(edge_port, IOSP_CMD_CHASE_PORT, 0);
if (status == 0) {
/* block until chase finished */
@@ -1759,16 +1729,16 @@ static void edge_break(struct tty_struct *tty, int break_state)
((edge_serial->is_epic) &&
(edge_serial->epic_descriptor.Supports.IOSPSetClrBreak))) {
if (break_state == -1) {
- dbg("%s - Sending IOSP_CMD_SET_BREAK", __func__);
+ dev_dbg(&port->dev, "%s - Sending IOSP_CMD_SET_BREAK\n", __func__);
status = send_iosp_ext_cmd(edge_port,
IOSP_CMD_SET_BREAK, 0);
} else {
- dbg("%s - Sending IOSP_CMD_CLEAR_BREAK", __func__);
+ dev_dbg(&port->dev, "%s - Sending IOSP_CMD_CLEAR_BREAK\n", __func__);
status = send_iosp_ext_cmd(edge_port,
IOSP_CMD_CLEAR_BREAK, 0);
}
if (status)
- dbg("%s - error sending break set/clear command.",
+ dev_dbg(&port->dev, "%s - error sending break set/clear command.\n",
__func__);
}
}
@@ -1781,20 +1751,19 @@ static void edge_break(struct tty_struct *tty, int break_state)
static void process_rcvd_data(struct edgeport_serial *edge_serial,
unsigned char *buffer, __u16 bufferLength)
{
+ struct device *dev = &edge_serial->serial->dev->dev;
struct usb_serial_port *port;
struct edgeport_port *edge_port;
struct tty_struct *tty;
__u16 lastBufferLength;
__u16 rxLen;
- dbg("%s", __func__);
-
lastBufferLength = bufferLength + 1;
while (bufferLength > 0) {
/* failsafe incase we get a message that we don't understand */
if (lastBufferLength == bufferLength) {
- dbg("%s - stuck in loop, exiting it.", __func__);
+ dev_dbg(dev, "%s - stuck in loop, exiting it.\n", __func__);
break;
}
lastBufferLength = bufferLength;
@@ -1815,8 +1784,8 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial,
++buffer;
--bufferLength;
- dbg("%s - Hdr1=%02X Hdr2=%02X", __func__,
- edge_serial->rxHeader1, edge_serial->rxHeader2);
+ dev_dbg(dev, "%s - Hdr1=%02X Hdr2=%02X\n", __func__,
+ edge_serial->rxHeader1, edge_serial->rxHeader2);
/* Process depending on whether this header is
* data or status */
@@ -1855,10 +1824,10 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial,
IOSP_GET_HDR_DATA_LEN(
edge_serial->rxHeader1,
edge_serial->rxHeader2);
- dbg("%s - Data for Port %u Len %u",
- __func__,
- edge_serial->rxPort,
- edge_serial->rxBytesRemaining);
+ dev_dbg(dev, "%s - Data for Port %u Len %u\n",
+ __func__,
+ edge_serial->rxPort,
+ edge_serial->rxBytesRemaining);
/* ASSERT(DevExt->RxPort < DevExt->NumPorts);
* ASSERT(DevExt->RxBytesRemaining <
@@ -1896,7 +1865,7 @@ static void process_rcvd_data(struct edgeport_serial *edge_serial,
tty = tty_port_tty_get(
&edge_port->port->port);
if (tty) {
- dbg("%s - Sending %d bytes to TTY for port %d",
+ dev_dbg(dev, "%s - Sending %d bytes to TTY for port %d\n",
__func__, rxLen, edge_serial->rxPort);
edge_tty_recv(&edge_serial->serial->dev->dev, tty, buffer, rxLen);
tty_kref_put(tty);
@@ -1935,6 +1904,7 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,
struct usb_serial_port *port;
struct edgeport_port *edge_port;
struct tty_struct *tty;
+ struct device *dev;
__u8 code = edge_serial->rxStatusCode;
/* switch the port pointer to the one being currently talked about */
@@ -1946,16 +1916,15 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,
__func__, edge_serial->rxPort);
return;
}
-
- dbg("%s - port %d", __func__, edge_serial->rxPort);
+ dev = &port->dev;
if (code == IOSP_EXT_STATUS) {
switch (byte2) {
case IOSP_EXT_STATUS_CHASE_RSP:
/* we want to do EXT status regardless of port
* open/closed */
- dbg("%s - Port %u EXT CHASE_RSP Data = %02x",
- __func__, edge_serial->rxPort, byte3);
+ dev_dbg(dev, "%s - Port %u EXT CHASE_RSP Data = %02x\n",
+ __func__, edge_serial->rxPort, byte3);
/* Currently, the only EXT_STATUS is Chase, so process
* here instead of one more call to one more subroutine
* If/when more EXT_STATUS, there'll be more work to do
@@ -1970,7 +1939,8 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,
return;
case IOSP_EXT_STATUS_RX_CHECK_RSP:
- dbg("%s ========== Port %u CHECK_RSP Sequence = %02x =============", __func__, edge_serial->rxPort, byte3);
+ dev_dbg(dev, "%s ========== Port %u CHECK_RSP Sequence = %02x =============\n",
+ __func__, edge_serial->rxPort, byte3);
/* Port->RxCheckRsp = true; */
return;
}
@@ -1979,7 +1949,8 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,
if (code == IOSP_STATUS_OPEN_RSP) {
edge_port->txCredits = GET_TX_BUFFER_SIZE(byte3);
edge_port->maxTxCredits = edge_port->txCredits;
- dbg("%s - Port %u Open Response Initial MSR = %02x TxBufferSize = %d", __func__, edge_serial->rxPort, byte2, edge_port->txCredits);
+ dev_dbg(dev, "%s - Port %u Open Response Initial MSR = %02x TxBufferSize = %d\n",
+ __func__, edge_serial->rxPort, byte2, edge_port->txCredits);
handle_new_msr(edge_port, byte2);
/* send the current line settings to the port so we are
@@ -2008,27 +1979,27 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,
switch (code) {
/* Not currently sent by Edgeport */
case IOSP_STATUS_LSR:
- dbg("%s - Port %u LSR Status = %02x",
- __func__, edge_serial->rxPort, byte2);
+ dev_dbg(dev, "%s - Port %u LSR Status = %02x\n",
+ __func__, edge_serial->rxPort, byte2);
handle_new_lsr(edge_port, false, byte2, 0);
break;
case IOSP_STATUS_LSR_DATA:
- dbg("%s - Port %u LSR Status = %02x, Data = %02x",
- __func__, edge_serial->rxPort, byte2, byte3);
+ dev_dbg(dev, "%s - Port %u LSR Status = %02x, Data = %02x\n",
+ __func__, edge_serial->rxPort, byte2, byte3);
/* byte2 is LSR Register */
/* byte3 is broken data byte */
handle_new_lsr(edge_port, true, byte2, byte3);
break;
/*
* case IOSP_EXT_4_STATUS:
- * dbg("%s - Port %u LSR Status = %02x Data = %02x",
+ * dev_dbg(dev, "%s - Port %u LSR Status = %02x Data = %02x\n",
* __func__, edge_serial->rxPort, byte2, byte3);
* break;
*/
case IOSP_STATUS_MSR:
- dbg("%s - Port %u MSR Status = %02x",
- __func__, edge_serial->rxPort, byte2);
+ dev_dbg(dev, "%s - Port %u MSR Status = %02x\n",
+ __func__, edge_serial->rxPort, byte2);
/*
* Process this new modem status and generate appropriate
* events, etc, based on the new status. This routine
@@ -2038,7 +2009,7 @@ static void process_rcvd_status(struct edgeport_serial *edge_serial,
break;
default:
- dbg("%s - Unrecognized IOSP status code %u", __func__, code);
+ dev_dbg(dev, "%s - Unrecognized IOSP status code %u\n", __func__, code);
break;
}
}
@@ -2073,8 +2044,6 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 newMsr)
{
struct async_icount *icount;
- dbg("%s %02x", __func__, newMsr);
-
if (newMsr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR |
EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) {
icount = &edge_port->icount;
@@ -2107,8 +2076,6 @@ static void handle_new_lsr(struct edgeport_port *edge_port, __u8 lsrData,
(LSR_OVER_ERR | LSR_PAR_ERR | LSR_FRM_ERR | LSR_BREAK));
struct async_icount *icount;
- dbg("%s - %02x", __func__, newLsr);
-
edge_port->shadowLSR = lsr;
if (newLsr & LSR_BREAK) {
@@ -2156,7 +2123,7 @@ static int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
__u16 current_length;
unsigned char *transfer_buffer;
- dbg("%s - %x, %x, %d", __func__, extAddr, addr, length);
+ dev_dbg(&serial->dev->dev, "%s - %x, %x, %d\n", __func__, extAddr, addr, length);
transfer_buffer = kmalloc(64, GFP_KERNEL);
if (!transfer_buffer) {
@@ -2173,8 +2140,7 @@ static int sram_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
else
current_length = length;
-/* dbg("%s - writing %x, %x, %d", __func__,
- extAddr, addr, current_length); */
+/* dev_dbg(&serial->dev->dev, "%s - writing %x, %x, %d\n", __func__, extAddr, addr, current_length); */
memcpy(transfer_buffer, data, current_length);
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
@@ -2207,8 +2173,6 @@ static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
__u16 current_length;
unsigned char *transfer_buffer;
-/* dbg("%s - %x, %x, %d", __func__, extAddr, addr, length); */
-
transfer_buffer = kmalloc(64, GFP_KERNEL);
if (!transfer_buffer) {
dev_err(&serial->dev->dev, "%s - kmalloc(%d) failed.\n",
@@ -2223,8 +2187,6 @@ static int rom_write(struct usb_serial *serial, __u16 extAddr, __u16 addr,
current_length = 64;
else
current_length = length;
-/* dbg("%s - writing %x, %x, %d", __func__,
- extAddr, addr, current_length); */
memcpy(transfer_buffer, data, current_length);
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
@@ -2257,8 +2219,6 @@ static int rom_read(struct usb_serial *serial, __u16 extAddr,
__u16 current_length;
unsigned char *transfer_buffer;
- dbg("%s - %x, %x, %d", __func__, extAddr, addr, length);
-
transfer_buffer = kmalloc(64, GFP_KERNEL);
if (!transfer_buffer) {
dev_err(&serial->dev->dev,
@@ -2273,8 +2233,6 @@ static int rom_read(struct usb_serial *serial, __u16 extAddr,
current_length = 64;
else
current_length = length;
-/* dbg("%s - %x, %x, %d", __func__,
- extAddr, addr, current_length); */
result = usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
USB_REQUEST_ION_READ_ROM,
@@ -2305,8 +2263,6 @@ static int send_iosp_ext_cmd(struct edgeport_port *edge_port,
int length = 0;
int status = 0;
- dbg("%s - %d, %d", __func__, command, param);
-
buffer = kmalloc(10, GFP_ATOMIC);
if (!buffer) {
dev_err(&edge_port->port->dev,
@@ -2339,11 +2295,11 @@ static int write_cmd_usb(struct edgeport_port *edge_port,
{
struct edgeport_serial *edge_serial =
usb_get_serial_data(edge_port->port->serial);
+ struct device *dev = &edge_port->port->dev;
int status = 0;
struct urb *urb;
- usb_serial_debug_data(debug, &edge_port->port->dev,
- __func__, length, buffer);
+ usb_serial_debug_data(dev, __func__, length, buffer);
/* Allocate our next urb */
urb = usb_alloc_urb(0, GFP_ATOMIC);
@@ -2351,8 +2307,8 @@ static int write_cmd_usb(struct edgeport_port *edge_port,
return -ENOMEM;
atomic_inc(&CmdUrbs);
- dbg("%s - ALLOCATE URB %p (outstanding %d)",
- __func__, urb, atomic_read(&CmdUrbs));
+ dev_dbg(dev, "%s - ALLOCATE URB %p (outstanding %d)\n",
+ __func__, urb, atomic_read(&CmdUrbs));
usb_fill_bulk_urb(urb, edge_serial->serial->dev,
usb_sndbulkpipe(edge_serial->serial->dev,
@@ -2364,9 +2320,8 @@ static int write_cmd_usb(struct edgeport_port *edge_port,
if (status) {
/* something went wrong */
- dev_err(&edge_port->port->dev,
- "%s - usb_submit_urb(write command) failed, status = %d\n",
- __func__, status);
+ dev_err(dev, "%s - usb_submit_urb(write command) failed, status = %d\n",
+ __func__, status);
usb_kill_urb(urb);
usb_free_urb(urb);
atomic_dec(&CmdUrbs);
@@ -2378,7 +2333,7 @@ static int write_cmd_usb(struct edgeport_port *edge_port,
if (edge_port->commandPending) {
/* command timed out */
- dbg("%s - command timed out", __func__);
+ dev_dbg(dev, "%s - command timed out\n", __func__);
status = -EINVAL;
}
#endif
@@ -2396,6 +2351,7 @@ static int send_cmd_write_baud_rate(struct edgeport_port *edge_port,
{
struct edgeport_serial *edge_serial =
usb_get_serial_data(edge_port->port->serial);
+ struct device *dev = &edge_port->port->dev;
unsigned char *cmdBuffer;
unsigned char *currCmd;
int cmdLen = 0;
@@ -2406,26 +2362,24 @@ static int send_cmd_write_baud_rate(struct edgeport_port *edge_port,
if (edge_serial->is_epic &&
!edge_serial->epic_descriptor.Supports.IOSPSetBaudRate) {
- dbg("SendCmdWriteBaudRate - NOT Setting baud rate for port = %d, baud = %d",
- edge_port->port->number, baudRate);
+ dev_dbg(dev, "SendCmdWriteBaudRate - NOT Setting baud rate for port = %d, baud = %d\n",
+ edge_port->port->number, baudRate);
return 0;
}
- dbg("%s - port = %d, baud = %d", __func__,
- edge_port->port->number, baudRate);
+ dev_dbg(dev, "%s - port = %d, baud = %d\n", __func__,
+ edge_port->port->number, baudRate);
- status = calc_baud_rate_divisor(baudRate, &divisor);
+ status = calc_baud_rate_divisor(dev, baudRate, &divisor);
if (status) {
- dev_err(&edge_port->port->dev, "%s - bad baud rate\n",
- __func__);
+ dev_err(dev, "%s - bad baud rate\n", __func__);
return status;
}
/* Alloc memory for the string of commands. */
cmdBuffer = kmalloc(0x100, GFP_ATOMIC);
if (!cmdBuffer) {
- dev_err(&edge_port->port->dev,
- "%s - kmalloc(%d) failed.\n", __func__, 0x100);
+ dev_err(dev, "%s - kmalloc(%d) failed.\n", __func__, 0x100);
return -ENOMEM;
}
currCmd = cmdBuffer;
@@ -2456,14 +2410,11 @@ static int send_cmd_write_baud_rate(struct edgeport_port *edge_port,
* this function calculates the proper baud rate divisor for the specified
* baud rate.
*****************************************************************************/
-static int calc_baud_rate_divisor(int baudrate, int *divisor)
+static int calc_baud_rate_divisor(struct device *dev, int baudrate, int *divisor)
{
int i;
__u16 custom;
-
- dbg("%s - %d", __func__, baudrate);
-
for (i = 0; i < ARRAY_SIZE(divisor_table); i++) {
if (divisor_table[i].BaudRate == baudrate) {
*divisor = divisor_table[i].Divisor;
@@ -2480,7 +2431,7 @@ static int calc_baud_rate_divisor(int baudrate, int *divisor)
*divisor = custom;
- dbg("%s - Baud %d = %d", __func__, baudrate, custom);
+ dev_dbg(dev, "%s - Baud %d = %d\n", __func__, baudrate, custom);
return 0;
}
@@ -2497,25 +2448,26 @@ static int send_cmd_write_uart_register(struct edgeport_port *edge_port,
{
struct edgeport_serial *edge_serial =
usb_get_serial_data(edge_port->port->serial);
+ struct device *dev = &edge_port->port->dev;
unsigned char *cmdBuffer;
unsigned char *currCmd;
unsigned long cmdLen = 0;
int status;
- dbg("%s - write to %s register 0x%02x",
- (regNum == MCR) ? "MCR" : "LCR", __func__, regValue);
+ dev_dbg(dev, "%s - write to %s register 0x%02x\n",
+ (regNum == MCR) ? "MCR" : "LCR", __func__, regValue);
if (edge_serial->is_epic &&
!edge_serial->epic_descriptor.Supports.IOSPWriteMCR &&
regNum == MCR) {
- dbg("SendCmdWriteUartReg - Not writing to MCR Register");
+ dev_dbg(dev, "SendCmdWriteUartReg - Not writing to MCR Register\n");
return 0;
}
if (edge_serial->is_epic &&
!edge_serial->epic_descriptor.Supports.IOSPWriteLCR &&
regNum == LCR) {
- dbg("SendCmdWriteUartReg - Not writing to LCR Register");
+ dev_dbg(dev, "SendCmdWriteUartReg - Not writing to LCR Register\n");
return 0;
}
@@ -2550,6 +2502,7 @@ static int send_cmd_write_uart_register(struct edgeport_port *edge_port,
static void change_port_settings(struct tty_struct *tty,
struct edgeport_port *edge_port, struct ktermios *old_termios)
{
+ struct device *dev = &edge_port->port->dev;
struct edgeport_serial *edge_serial =
usb_get_serial_data(edge_port->port->serial);
int baud;
@@ -2562,11 +2515,11 @@ static void change_port_settings(struct tty_struct *tty,
__u8 txFlow;
int status;
- dbg("%s - port %d", __func__, edge_port->port->number);
+ dev_dbg(dev, "%s - port %d\n", __func__, edge_port->port->number);
if (!edge_port->open &&
!edge_port->openPending) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(dev, "%s - port not opened\n", __func__);
return;
}
@@ -2575,20 +2528,20 @@ static void change_port_settings(struct tty_struct *tty,
switch (cflag & CSIZE) {
case CS5:
lData = LCR_BITS_5; mask = 0x1f;
- dbg("%s - data bits = 5", __func__);
+ dev_dbg(dev, "%s - data bits = 5\n", __func__);
break;
case CS6:
lData = LCR_BITS_6; mask = 0x3f;
- dbg("%s - data bits = 6", __func__);
+ dev_dbg(dev, "%s - data bits = 6\n", __func__);
break;
case CS7:
lData = LCR_BITS_7; mask = 0x7f;
- dbg("%s - data bits = 7", __func__);
+ dev_dbg(dev, "%s - data bits = 7\n", __func__);
break;
default:
case CS8:
lData = LCR_BITS_8;
- dbg("%s - data bits = 8", __func__);
+ dev_dbg(dev, "%s - data bits = 8\n", __func__);
break;
}
@@ -2597,28 +2550,28 @@ static void change_port_settings(struct tty_struct *tty,
if (cflag & CMSPAR) {
if (cflag & PARODD) {
lParity = LCR_PAR_MARK;
- dbg("%s - parity = mark", __func__);
+ dev_dbg(dev, "%s - parity = mark\n", __func__);
} else {
lParity = LCR_PAR_SPACE;
- dbg("%s - parity = space", __func__);
+ dev_dbg(dev, "%s - parity = space\n", __func__);
}
} else if (cflag & PARODD) {
lParity = LCR_PAR_ODD;
- dbg("%s - parity = odd", __func__);
+ dev_dbg(dev, "%s - parity = odd\n", __func__);
} else {
lParity = LCR_PAR_EVEN;
- dbg("%s - parity = even", __func__);
+ dev_dbg(dev, "%s - parity = even\n", __func__);
}
} else {
- dbg("%s - parity = none", __func__);
+ dev_dbg(dev, "%s - parity = none\n", __func__);
}
if (cflag & CSTOPB) {
lStop = LCR_STOP_2;
- dbg("%s - stop bits = 2", __func__);
+ dev_dbg(dev, "%s - stop bits = 2\n", __func__);
} else {
lStop = LCR_STOP_1;
- dbg("%s - stop bits = 1", __func__);
+ dev_dbg(dev, "%s - stop bits = 1\n", __func__);
}
/* figure out the flow control settings */
@@ -2626,9 +2579,9 @@ static void change_port_settings(struct tty_struct *tty,
if (cflag & CRTSCTS) {
rxFlow |= IOSP_RX_FLOW_RTS;
txFlow |= IOSP_TX_FLOW_CTS;
- dbg("%s - RTS/CTS is enabled", __func__);
+ dev_dbg(dev, "%s - RTS/CTS is enabled\n", __func__);
} else {
- dbg("%s - RTS/CTS is disabled", __func__);
+ dev_dbg(dev, "%s - RTS/CTS is disabled\n", __func__);
}
/* if we are implementing XON/XOFF, set the start and stop character
@@ -2649,19 +2602,19 @@ static void change_port_settings(struct tty_struct *tty,
/* if we are implementing INBOUND XON/XOFF */
if (I_IXOFF(tty)) {
rxFlow |= IOSP_RX_FLOW_XON_XOFF;
- dbg("%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
- __func__, start_char, stop_char);
+ dev_dbg(dev, "%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
+ __func__, start_char, stop_char);
} else {
- dbg("%s - INBOUND XON/XOFF is disabled", __func__);
+ dev_dbg(dev, "%s - INBOUND XON/XOFF is disabled\n", __func__);
}
/* if we are implementing OUTBOUND XON/XOFF */
if (I_IXON(tty)) {
txFlow |= IOSP_TX_FLOW_XON_XOFF;
- dbg("%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
- __func__, start_char, stop_char);
+ dev_dbg(dev, "%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
+ __func__, start_char, stop_char);
} else {
- dbg("%s - OUTBOUND XON/XOFF is disabled", __func__);
+ dev_dbg(dev, "%s - OUTBOUND XON/XOFF is disabled\n", __func__);
}
}
@@ -2704,7 +2657,7 @@ static void change_port_settings(struct tty_struct *tty,
baud = 9600;
}
- dbg("%s - baud rate = %d", __func__, baud);
+ dev_dbg(dev, "%s - baud rate = %d\n", __func__, baud);
status = send_cmd_write_baud_rate(edge_port, baud);
if (status == -1) {
/* Speed change was not possible - put back the old speed */
@@ -2746,9 +2699,10 @@ static void unicode_to_ascii(char *string, int buflen,
****************************************************************************/
static void get_manufacturing_desc(struct edgeport_serial *edge_serial)
{
+ struct device *dev = &edge_serial->serial->dev->dev;
int response;
- dbg("getting manufacturer descriptor");
+ dev_dbg(dev, "getting manufacturer descriptor\n");
response = rom_read(edge_serial->serial,
(EDGE_MANUF_DESC_ADDR & 0xffff0000) >> 16,
@@ -2757,42 +2711,41 @@ static void get_manufacturing_desc(struct edgeport_serial *edge_serial)
(__u8 *)(&edge_serial->manuf_descriptor));
if (response < 1)
- dev_err(&edge_serial->serial->dev->dev,
- "error in getting manufacturer descriptor\n");
+ dev_err(dev, "error in getting manufacturer descriptor\n");
else {
char string[30];
- dbg("**Manufacturer Descriptor");
- dbg(" RomSize: %dK",
+ dev_dbg(dev, "**Manufacturer Descriptor\n");
+ dev_dbg(dev, " RomSize: %dK\n",
edge_serial->manuf_descriptor.RomSize);
- dbg(" RamSize: %dK",
+ dev_dbg(dev, " RamSize: %dK\n",
edge_serial->manuf_descriptor.RamSize);
- dbg(" CpuRev: %d",
+ dev_dbg(dev, " CpuRev: %d\n",
edge_serial->manuf_descriptor.CpuRev);
- dbg(" BoardRev: %d",
+ dev_dbg(dev, " BoardRev: %d\n",
edge_serial->manuf_descriptor.BoardRev);
- dbg(" NumPorts: %d",
+ dev_dbg(dev, " NumPorts: %d\n",
edge_serial->manuf_descriptor.NumPorts);
- dbg(" DescDate: %d/%d/%d",
+ dev_dbg(dev, " DescDate: %d/%d/%d\n",
edge_serial->manuf_descriptor.DescDate[0],
edge_serial->manuf_descriptor.DescDate[1],
edge_serial->manuf_descriptor.DescDate[2]+1900);
unicode_to_ascii(string, sizeof(string),
edge_serial->manuf_descriptor.SerialNumber,
edge_serial->manuf_descriptor.SerNumLength/2);
- dbg(" SerialNumber: %s", string);
+ dev_dbg(dev, " SerialNumber: %s\n", string);
unicode_to_ascii(string, sizeof(string),
edge_serial->manuf_descriptor.AssemblyNumber,
edge_serial->manuf_descriptor.AssemblyNumLength/2);
- dbg(" AssemblyNumber: %s", string);
+ dev_dbg(dev, " AssemblyNumber: %s\n", string);
unicode_to_ascii(string, sizeof(string),
edge_serial->manuf_descriptor.OemAssyNumber,
edge_serial->manuf_descriptor.OemAssyNumLength/2);
- dbg(" OemAssyNumber: %s", string);
- dbg(" UartType: %d",
+ dev_dbg(dev, " OemAssyNumber: %s\n", string);
+ dev_dbg(dev, " UartType: %d\n",
edge_serial->manuf_descriptor.UartType);
- dbg(" IonPid: %d",
+ dev_dbg(dev, " IonPid: %d\n",
edge_serial->manuf_descriptor.IonPid);
- dbg(" IonConfig: %d",
+ dev_dbg(dev, " IonConfig: %d\n",
edge_serial->manuf_descriptor.IonConfig);
}
}
@@ -2805,9 +2758,10 @@ static void get_manufacturing_desc(struct edgeport_serial *edge_serial)
****************************************************************************/
static void get_boot_desc(struct edgeport_serial *edge_serial)
{
+ struct device *dev = &edge_serial->serial->dev->dev;
int response;
- dbg("getting boot descriptor");
+ dev_dbg(dev, "getting boot descriptor\n");
response = rom_read(edge_serial->serial,
(EDGE_BOOT_DESC_ADDR & 0xffff0000) >> 16,
@@ -2816,23 +2770,22 @@ static void get_boot_desc(struct edgeport_serial *edge_serial)
(__u8 *)(&edge_serial->boot_descriptor));
if (response < 1)
- dev_err(&edge_serial->serial->dev->dev,
- "error in getting boot descriptor\n");
+ dev_err(dev, "error in getting boot descriptor\n");
else {
- dbg("**Boot Descriptor:");
- dbg(" BootCodeLength: %d",
- le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength));
- dbg(" MajorVersion: %d",
+ dev_dbg(dev, "**Boot Descriptor:\n");
+ dev_dbg(dev, " BootCodeLength: %d\n",
+ le16_to_cpu(edge_serial->boot_descriptor.BootCodeLength));
+ dev_dbg(dev, " MajorVersion: %d\n",
edge_serial->boot_descriptor.MajorVersion);
- dbg(" MinorVersion: %d",
+ dev_dbg(dev, " MinorVersion: %d\n",
edge_serial->boot_descriptor.MinorVersion);
- dbg(" BuildNumber: %d",
+ dev_dbg(dev, " BuildNumber: %d\n",
le16_to_cpu(edge_serial->boot_descriptor.BuildNumber));
- dbg(" Capabilities: 0x%x",
+ dev_dbg(dev, " Capabilities: 0x%x\n",
le16_to_cpu(edge_serial->boot_descriptor.Capabilities));
- dbg(" UConfig0: %d",
+ dev_dbg(dev, " UConfig0: %d\n",
edge_serial->boot_descriptor.UConfig0);
- dbg(" UConfig1: %d",
+ dev_dbg(dev, " UConfig1: %d\n",
edge_serial->boot_descriptor.UConfig1);
}
}
@@ -2844,6 +2797,7 @@ static void get_boot_desc(struct edgeport_serial *edge_serial)
****************************************************************************/
static void load_application_firmware(struct edgeport_serial *edge_serial)
{
+ struct device *dev = &edge_serial->serial->dev->dev;
const struct ihex_binrec *rec;
const struct firmware *fw;
const char *fw_name;
@@ -2864,7 +2818,7 @@ static void load_application_firmware(struct edgeport_serial *edge_serial)
break;
case EDGE_DOWNLOAD_FILE_NONE:
- dbg("No download file specified, skipping download");
+ dev_dbg(dev, "No download file specified, skipping download\n");
return;
default:
@@ -2874,7 +2828,7 @@ static void load_application_firmware(struct edgeport_serial *edge_serial)
response = request_ihex_firmware(&fw, fw_name,
&edge_serial->serial->dev->dev);
if (response) {
- printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
+ dev_err(dev, "Failed to load image \"%s\" err %d\n",
fw_name, response);
return;
}
@@ -2882,7 +2836,7 @@ static void load_application_firmware(struct edgeport_serial *edge_serial)
rec = (const struct ihex_binrec *)fw->data;
build = (rec->data[2] << 8) | rec->data[3];
- dbg("%s %d.%d.%d", fw_info, rec->data[0], rec->data[1], build);
+ dev_dbg(dev, "%s %d.%d.%d\n", fw_info, rec->data[0], rec->data[1], build);
edge_serial->product_info.FirmwareMajorVersion = rec->data[0];
edge_serial->product_info.FirmwareMinorVersion = rec->data[1];
@@ -2905,10 +2859,10 @@ static void load_application_firmware(struct edgeport_serial *edge_serial)
}
}
- dbg("sending exec_dl_code");
- response = usb_control_msg (edge_serial->serial->dev,
- usb_sndctrlpipe(edge_serial->serial->dev, 0),
- USB_REQUEST_ION_EXEC_DL_CODE,
+ dev_dbg(dev, "sending exec_dl_code\n");
+ response = usb_control_msg (edge_serial->serial->dev,
+ usb_sndctrlpipe(edge_serial->serial->dev, 0),
+ USB_REQUEST_ION_EXEC_DL_CODE,
0x40, 0x4000, 0x0001, NULL, 0, 3000);
release_firmware(fw);
@@ -2923,6 +2877,7 @@ static int edge_startup(struct usb_serial *serial)
struct edgeport_serial *edge_serial;
struct edgeport_port *edge_port;
struct usb_device *dev;
+ struct device *ddev = &serial->dev->dev;
int i, j;
int response;
bool interrupt_in_found;
@@ -2974,32 +2929,31 @@ static int edge_startup(struct usb_serial *serial)
/* serial->num_ports = serial->product_info.NumPorts; */
if ((!edge_serial->is_epic) &&
(edge_serial->product_info.NumPorts != serial->num_ports)) {
- dev_warn(&serial->dev->dev, "Device Reported %d serial ports "
- "vs. core thinking we have %d ports, email "
- "greg@kroah.com this information.\n",
+ dev_warn(ddev,
+ "Device Reported %d serial ports vs. core thinking we have %d ports, email greg@kroah.com this information.\n",
edge_serial->product_info.NumPorts,
serial->num_ports);
}
- dbg("%s - time 1 %ld", __func__, jiffies);
+ dev_dbg(ddev, "%s - time 1 %ld\n", __func__, jiffies);
/* If not an EPiC device */
if (!edge_serial->is_epic) {
/* now load the application firmware into this device */
load_application_firmware(edge_serial);
- dbg("%s - time 2 %ld", __func__, jiffies);
+ dev_dbg(ddev, "%s - time 2 %ld\n", __func__, jiffies);
/* Check current Edgeport EEPROM and update if necessary */
update_edgeport_E2PROM(edge_serial);
- dbg("%s - time 3 %ld", __func__, jiffies);
+ dev_dbg(ddev, "%s - time 3 %ld\n", __func__, jiffies);
/* set the configuration to use #1 */
-/* dbg("set_configuration 1"); */
+/* dev_dbg(ddev, "set_configuration 1\n"); */
/* usb_set_configuration (dev, 1); */
}
- dbg(" FirmwareMajorVersion %d.%d.%d",
+ dev_dbg(ddev, " FirmwareMajorVersion %d.%d.%d\n",
edge_serial->product_info.FirmwareMajorVersion,
edge_serial->product_info.FirmwareMinorVersion,
le16_to_cpu(edge_serial->product_info.FirmwareBuildNumber));
@@ -3011,8 +2965,7 @@ static int edge_startup(struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
edge_port = kzalloc(sizeof(struct edgeport_port), GFP_KERNEL);
if (edge_port == NULL) {
- dev_err(&serial->dev->dev, "%s - Out of memory\n",
- __func__);
+ dev_err(ddev, "%s - Out of memory\n", __func__);
for (j = 0; j < i; ++j) {
kfree(usb_get_serial_port_data(serial->port[j]));
usb_set_serial_port_data(serial->port[j],
@@ -3044,19 +2997,19 @@ static int edge_startup(struct usb_serial *serial)
if (!interrupt_in_found &&
(usb_endpoint_is_int_in(endpoint))) {
/* we found a interrupt in endpoint */
- dbg("found interrupt in");
+ dev_dbg(ddev, "found interrupt in\n");
/* not set up yet, so do it now */
edge_serial->interrupt_read_urb =
usb_alloc_urb(0, GFP_KERNEL);
if (!edge_serial->interrupt_read_urb) {
- dev_err(&dev->dev, "out of memory\n");
+ dev_err(ddev, "out of memory\n");
return -ENOMEM;
}
edge_serial->interrupt_in_buffer =
kmalloc(buffer_size, GFP_KERNEL);
if (!edge_serial->interrupt_in_buffer) {
- dev_err(&dev->dev, "out of memory\n");
+ dev_err(ddev, "out of memory\n");
usb_free_urb(edge_serial->interrupt_read_urb);
return -ENOMEM;
}
@@ -3081,13 +3034,13 @@ static int edge_startup(struct usb_serial *serial)
if (!bulk_in_found &&
(usb_endpoint_is_bulk_in(endpoint))) {
/* we found a bulk in endpoint */
- dbg("found bulk in");
+ dev_dbg(ddev, "found bulk in\n");
/* not set up yet, so do it now */
edge_serial->read_urb =
usb_alloc_urb(0, GFP_KERNEL);
if (!edge_serial->read_urb) {
- dev_err(&dev->dev, "out of memory\n");
+ dev_err(ddev, "out of memory\n");
return -ENOMEM;
}
edge_serial->bulk_in_buffer =
@@ -3114,7 +3067,7 @@ static int edge_startup(struct usb_serial *serial)
if (!bulk_out_found &&
(usb_endpoint_is_bulk_out(endpoint))) {
/* we found a bulk out endpoint */
- dbg("found bulk out");
+ dev_dbg(ddev, "found bulk out\n");
edge_serial->bulk_out_endpoint =
endpoint->bEndpointAddress;
bulk_out_found = true;
@@ -3122,8 +3075,7 @@ static int edge_startup(struct usb_serial *serial)
}
if (!interrupt_in_found || !bulk_in_found || !bulk_out_found) {
- dev_err(&dev->dev, "Error - the proper endpoints "
- "were not found!\n");
+ dev_err(ddev, "Error - the proper endpoints were not found!\n");
return -ENODEV;
}
@@ -3132,8 +3084,7 @@ static int edge_startup(struct usb_serial *serial)
response = usb_submit_urb(edge_serial->interrupt_read_urb,
GFP_KERNEL);
if (response)
- dev_err(&dev->dev,
- "%s - Error %d submitting control urb\n",
+ dev_err(ddev, "%s - Error %d submitting control urb\n",
__func__, response);
}
return response;
@@ -3148,8 +3099,6 @@ static void edge_disconnect(struct usb_serial *serial)
{
struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
- dbg("%s", __func__);
-
/* stop reads and writes on all ports */
/* free up our endpoint stuff */
if (edge_serial->is_epic) {
@@ -3173,8 +3122,6 @@ static void edge_release(struct usb_serial *serial)
struct edgeport_serial *edge_serial = usb_get_serial_data(serial);
int i;
- dbg("%s", __func__);
-
for (i = 0; i < serial->num_ports; ++i)
kfree(usb_get_serial_port_data(serial->port[i]));
@@ -3190,6 +3137,3 @@ MODULE_FIRMWARE("edgeport/boot.fw");
MODULE_FIRMWARE("edgeport/boot2.fw");
MODULE_FIRMWARE("edgeport/down.fw");
MODULE_FIRMWARE("edgeport/down2.fw");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/io_ti.c b/drivers/usb/serial/io_ti.c
index 765978ae752e..a2209cd45093 100644
--- a/drivers/usb/serial/io_ti.c
+++ b/drivers/usb/serial/io_ti.c
@@ -201,8 +201,6 @@ static unsigned char OperationalMajorVersion;
static unsigned char OperationalMinorVersion;
static unsigned short OperationalBuildNumber;
-static bool debug;
-
static int closing_wait = EDGE_CLOSING_WAIT;
static bool ignore_cpu_rev;
static int default_uart_mode; /* RS232 */
@@ -233,8 +231,8 @@ static int ti_vread_sync(struct usb_device *dev, __u8 request,
if (status < 0)
return status;
if (status != size) {
- dbg("%s - wanted to write %d, but only wrote %d",
- __func__, size, status);
+ dev_dbg(&dev->dev, "%s - wanted to write %d, but only wrote %d\n",
+ __func__, size, status);
return -ECOMM;
}
return 0;
@@ -251,8 +249,8 @@ static int ti_vsend_sync(struct usb_device *dev, __u8 request,
if (status < 0)
return status;
if (status != size) {
- dbg("%s - wanted to write %d, but only wrote %d",
- __func__, size, status);
+ dev_dbg(&dev->dev, "%s - wanted to write %d, but only wrote %d\n",
+ __func__, size, status);
return -ECOMM;
}
return 0;
@@ -270,7 +268,7 @@ static int purge_port(struct usb_serial_port *port, __u16 mask)
{
int port_number = port->number - port->serial->minor;
- dbg("%s - port %d, mask %x", __func__, port_number, mask);
+ dev_dbg(&port->dev, "%s - port %d, mask %x\n", __func__, port_number, mask);
return send_cmd(port->serial->dev,
UMPC_PURGE_PORT,
@@ -295,7 +293,7 @@ static int read_download_mem(struct usb_device *dev, int start_address,
__u8 read_length;
__be16 be_start_address;
- dbg("%s - @ %x for %d", __func__, start_address, length);
+ dev_dbg(&dev->dev, "%s - @ %x for %d\n", __func__, start_address, length);
/* Read in blocks of 64 bytes
* (TI firmware can't handle more than 64 byte reads)
@@ -307,8 +305,7 @@ static int read_download_mem(struct usb_device *dev, int start_address,
read_length = (__u8)length;
if (read_length > 1) {
- dbg("%s - @ %x for %d", __func__,
- start_address, read_length);
+ dev_dbg(&dev->dev, "%s - @ %x for %d\n", __func__, start_address, read_length);
}
be_start_address = cpu_to_be16(start_address);
status = ti_vread_sync(dev, UMPC_MEMORY_READ,
@@ -317,13 +314,12 @@ static int read_download_mem(struct usb_device *dev, int start_address,
buffer, read_length);
if (status) {
- dbg("%s - ERROR %x", __func__, status);
+ dev_dbg(&dev->dev, "%s - ERROR %x\n", __func__, status);
return status;
}
if (read_length > 1)
- usb_serial_debug_data(debug, &dev->dev, __func__,
- read_length, buffer);
+ usb_serial_debug_data(&dev->dev, __func__, read_length, buffer);
/* Update pointers/length */
start_address += read_length;
@@ -353,15 +349,14 @@ static int read_boot_mem(struct edgeport_serial *serial,
UMPC_MEMORY_READ, serial->TI_I2C_Type,
(__u16)(start_address+i), &buffer[i], 0x01);
if (status) {
- dbg("%s - ERROR %x", __func__, status);
+ dev_dbg(&serial->serial->dev->dev, "%s - ERROR %x\n", __func__, status);
return status;
}
}
- dbg("%s - start_address = %x, length = %d",
- __func__, start_address, length);
- usb_serial_debug_data(debug, &serial->serial->dev->dev,
- __func__, length, buffer);
+ dev_dbg(&serial->serial->dev->dev, "%s - start_address = %x, length = %d\n",
+ __func__, start_address, length);
+ usb_serial_debug_data(&serial->serial->dev->dev, __func__, length, buffer);
serial->TiReadI2C = 1;
@@ -398,10 +393,8 @@ static int write_boot_mem(struct edgeport_serial *serial,
return status;
}
- dbg("%s - start_sddr = %x, length = %d",
- __func__, start_address, length);
- usb_serial_debug_data(debug, &serial->serial->dev->dev,
- __func__, length, buffer);
+ dev_dbg(&serial->serial->dev->dev, "%s - start_sddr = %x, length = %d\n", __func__, start_address, length);
+ usb_serial_debug_data(&serial->serial->dev->dev, __func__, length, buffer);
return status;
}
@@ -411,6 +404,7 @@ static int write_boot_mem(struct edgeport_serial *serial,
static int write_i2c_mem(struct edgeport_serial *serial,
int start_address, int length, __u8 address_type, __u8 *buffer)
{
+ struct device *dev = &serial->serial->dev->dev;
int status = 0;
int write_length;
__be16 be_start_address;
@@ -424,10 +418,9 @@ static int write_i2c_mem(struct edgeport_serial *serial,
if (write_length > length)
write_length = length;
- dbg("%s - BytesInFirstPage Addr = %x, length = %d",
- __func__, start_address, write_length);
- usb_serial_debug_data(debug, &serial->serial->dev->dev,
- __func__, write_length, buffer);
+ dev_dbg(dev, "%s - BytesInFirstPage Addr = %x, length = %d\n",
+ __func__, start_address, write_length);
+ usb_serial_debug_data(dev, __func__, write_length, buffer);
/* Write first page */
be_start_address = cpu_to_be16(start_address);
@@ -436,7 +429,7 @@ static int write_i2c_mem(struct edgeport_serial *serial,
(__force __u16)be_start_address,
buffer, write_length);
if (status) {
- dbg("%s - ERROR %d", __func__, status);
+ dev_dbg(dev, "%s - ERROR %d\n", __func__, status);
return status;
}
@@ -452,10 +445,9 @@ static int write_i2c_mem(struct edgeport_serial *serial,
else
write_length = length;
- dbg("%s - Page Write Addr = %x, length = %d",
- __func__, start_address, write_length);
- usb_serial_debug_data(debug, &serial->serial->dev->dev,
- __func__, write_length, buffer);
+ dev_dbg(dev, "%s - Page Write Addr = %x, length = %d\n",
+ __func__, start_address, write_length);
+ usb_serial_debug_data(dev, __func__, write_length, buffer);
/* Write next page */
be_start_address = cpu_to_be16(start_address);
@@ -464,8 +456,7 @@ static int write_i2c_mem(struct edgeport_serial *serial,
(__force __u16)be_start_address,
buffer, write_length);
if (status) {
- dev_err(&serial->serial->dev->dev, "%s - ERROR %d\n",
- __func__, status);
+ dev_err(dev, "%s - ERROR %d\n", __func__, status);
return status;
}
@@ -508,7 +499,7 @@ static int tx_active(struct edgeport_port *port)
if (status)
goto exit_is_tx_active;
- dbg("%s - XByteCount 0x%X", __func__, oedb->XByteCount);
+ dev_dbg(&port->port->dev, "%s - XByteCount 0x%X\n", __func__, oedb->XByteCount);
/* and the LSR */
status = read_ram(port->port->serial->dev,
@@ -516,7 +507,7 @@ static int tx_active(struct edgeport_port *port)
if (status)
goto exit_is_tx_active;
- dbg("%s - LSR = 0x%X", __func__, *lsr);
+ dev_dbg(&port->port->dev, "%s - LSR = 0x%X\n", __func__, *lsr);
/* If either buffer has data or we are transmitting then return TRUE */
if ((oedb->XByteCount & 0x80) != 0)
@@ -527,7 +518,7 @@ static int tx_active(struct edgeport_port *port)
/* We return Not Active if we get any kind of error */
exit_is_tx_active:
- dbg("%s - return %d", __func__, bytes_left);
+ dev_dbg(&port->port->dev, "%s - return %d\n", __func__, bytes_left);
kfree(lsr);
kfree(oedb);
@@ -599,14 +590,13 @@ static int choose_config(struct usb_device *dev)
* configuration # 1, which is Config Descriptor 0.
*/
- dbg("%s - Number of Interfaces = %d",
- __func__, dev->config->desc.bNumInterfaces);
- dbg("%s - MAX Power = %d",
- __func__, dev->config->desc.bMaxPower * 2);
+ dev_dbg(&dev->dev, "%s - Number of Interfaces = %d\n",
+ __func__, dev->config->desc.bNumInterfaces);
+ dev_dbg(&dev->dev, "%s - MAX Power = %d\n",
+ __func__, dev->config->desc.bMaxPower * 2);
if (dev->config->desc.bNumInterfaces != 1) {
- dev_err(&dev->dev, "%s - bNumInterfaces is not 1, ERROR!\n",
- __func__);
+ dev_err(&dev->dev, "%s - bNumInterfaces is not 1, ERROR!\n", __func__);
return -ENODEV;
}
@@ -684,7 +674,7 @@ static int valid_csum(struct ti_i2c_desc *rom_desc, __u8 *buffer)
cs = (__u8)(cs + buffer[i]);
if (cs != rom_desc->CheckSum) {
- dbg("%s - Mismatch %x - %x", __func__, rom_desc->CheckSum, cs);
+ pr_debug("%s - Mismatch %x - %x", __func__, rom_desc->CheckSum, cs);
return -EINVAL;
}
return 0;
@@ -736,11 +726,11 @@ static int check_i2c_image(struct edgeport_serial *serial)
if ((start_address + sizeof(struct ti_i2c_desc) +
rom_desc->Size) > TI_MAX_I2C_SIZE) {
status = -ENODEV;
- dbg("%s - structure too big, erroring out.", __func__);
+ dev_dbg(dev, "%s - structure too big, erroring out.\n", __func__);
break;
}
- dbg("%s Type = 0x%x", __func__, rom_desc->Type);
+ dev_dbg(dev, "%s Type = 0x%x\n", __func__, rom_desc->Type);
/* Skip type 2 record */
ttype = rom_desc->Type & 0x0f;
@@ -779,18 +769,18 @@ static int get_manuf_info(struct edgeport_serial *serial, __u8 *buffer)
int start_address;
struct ti_i2c_desc *rom_desc;
struct edge_ti_manuf_descriptor *desc;
+ struct device *dev = &serial->serial->dev->dev;
rom_desc = kmalloc(sizeof(*rom_desc), GFP_KERNEL);
if (!rom_desc) {
- dev_err(&serial->serial->dev->dev, "%s - out of memory\n",
- __func__);
+ dev_err(dev, "%s - out of memory\n", __func__);
return -ENOMEM;
}
start_address = get_descriptor_addr(serial, I2C_DESC_TYPE_ION,
rom_desc);
if (!start_address) {
- dbg("%s - Edge Descriptor not found in I2C", __func__);
+ dev_dbg(dev, "%s - Edge Descriptor not found in I2C\n", __func__);
status = -ENODEV;
goto exit;
}
@@ -804,12 +794,12 @@ static int get_manuf_info(struct edgeport_serial *serial, __u8 *buffer)
status = valid_csum(rom_desc, buffer);
desc = (struct edge_ti_manuf_descriptor *)buffer;
- dbg("%s - IonConfig 0x%x", __func__, desc->IonConfig);
- dbg("%s - Version %d", __func__, desc->Version);
- dbg("%s - Cpu/Board 0x%x", __func__, desc->CpuRev_BoardRev);
- dbg("%s - NumPorts %d", __func__, desc->NumPorts);
- dbg("%s - NumVirtualPorts %d", __func__, desc->NumVirtualPorts);
- dbg("%s - TotalPorts %d", __func__, desc->TotalPorts);
+ dev_dbg(dev, "%s - IonConfig 0x%x\n", __func__, desc->IonConfig);
+ dev_dbg(dev, "%s - Version %d\n", __func__, desc->Version);
+ dev_dbg(dev, "%s - Cpu/Board 0x%x\n", __func__, desc->CpuRev_BoardRev);
+ dev_dbg(dev, "%s - NumPorts %d\n", __func__, desc->NumPorts);
+ dev_dbg(dev, "%s - NumVirtualPorts %d\n", __func__, desc->NumVirtualPorts);
+ dev_dbg(dev, "%s - TotalPorts %d\n", __func__, desc->TotalPorts);
exit:
kfree(rom_desc);
@@ -855,8 +845,8 @@ static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
err = request_firmware(&fw, fw_name, dev);
if (err) {
- printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
- fw_name, err);
+ dev_err(dev, "Failed to load image \"%s\" err %d\n",
+ fw_name, err);
kfree(buffer);
return err;
}
@@ -903,13 +893,13 @@ static int build_i2c_fw_hdr(__u8 *header, struct device *dev)
/* Try to figure out what type of I2c we have */
static int i2c_type_bootmode(struct edgeport_serial *serial)
{
+ struct device *dev = &serial->serial->dev->dev;
int status;
u8 *data;
data = kmalloc(1, GFP_KERNEL);
if (!data) {
- dev_err(&serial->serial->dev->dev,
- "%s - out of memory\n", __func__);
+ dev_err(dev, "%s - out of memory\n", __func__);
return -ENOMEM;
}
@@ -917,11 +907,11 @@ static int i2c_type_bootmode(struct edgeport_serial *serial)
status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ,
DTK_ADDR_SPACE_I2C_TYPE_II, 0, data, 0x01);
if (status)
- dbg("%s - read 2 status error = %d", __func__, status);
+ dev_dbg(dev, "%s - read 2 status error = %d\n", __func__, status);
else
- dbg("%s - read 2 data = 0x%x", __func__, *data);
+ dev_dbg(dev, "%s - read 2 data = 0x%x\n", __func__, *data);
if ((!status) && (*data == UMP5152 || *data == UMP3410)) {
- dbg("%s - ROM_TYPE_II", __func__);
+ dev_dbg(dev, "%s - ROM_TYPE_II\n", __func__);
serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
goto out;
}
@@ -930,16 +920,16 @@ static int i2c_type_bootmode(struct edgeport_serial *serial)
status = ti_vread_sync(serial->serial->dev, UMPC_MEMORY_READ,
DTK_ADDR_SPACE_I2C_TYPE_III, 0, data, 0x01);
if (status)
- dbg("%s - read 3 status error = %d", __func__, status);
+ dev_dbg(dev, "%s - read 3 status error = %d\n", __func__, status);
else
- dbg("%s - read 2 data = 0x%x", __func__, *data);
+ dev_dbg(dev, "%s - read 2 data = 0x%x\n", __func__, *data);
if ((!status) && (*data == UMP5152 || *data == UMP3410)) {
- dbg("%s - ROM_TYPE_III", __func__);
+ dev_dbg(dev, "%s - ROM_TYPE_III\n", __func__);
serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_III;
goto out;
}
- dbg("%s - Unknown", __func__);
+ dev_dbg(dev, "%s - Unknown\n", __func__);
serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
status = -ENODEV;
out:
@@ -1050,11 +1040,11 @@ static int download_fw(struct edgeport_serial *serial)
if (serial->product_info.TiMode == TI_MODE_DOWNLOAD) {
struct ti_i2c_desc *rom_desc;
- dbg("%s - RUNNING IN DOWNLOAD MODE", __func__);
+ dev_dbg(dev, "%s - RUNNING IN DOWNLOAD MODE\n", __func__);
status = check_i2c_image(serial);
if (status) {
- dbg("%s - DOWNLOAD MODE -- BAD I2C", __func__);
+ dev_dbg(dev, "%s - DOWNLOAD MODE -- BAD I2C\n", __func__);
return status;
}
@@ -1074,7 +1064,7 @@ static int download_fw(struct edgeport_serial *serial)
/* Check version number of ION descriptor */
if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) {
- dbg("%s - Wrong CPU Rev %d (Must be 2)",
+ dev_dbg(dev, "%s - Wrong CPU Rev %d (Must be 2)\n",
__func__, ti_cpu_rev(ti_manuf_desc));
kfree(ti_manuf_desc);
return -EINVAL;
@@ -1094,8 +1084,7 @@ static int download_fw(struct edgeport_serial *serial)
struct ti_i2c_firmware_rec *firmware_version;
u8 *record;
- dbg("%s - Found Type FIRMWARE (Type 2) record",
- __func__);
+ dev_dbg(dev, "%s - Found Type FIRMWARE (Type 2) record\n", __func__);
firmware_version = kmalloc(sizeof(*firmware_version),
GFP_KERNEL);
@@ -1127,22 +1116,21 @@ static int download_fw(struct edgeport_serial *serial)
download_new_ver = (OperationalMajorVersion << 8) +
(OperationalMinorVersion);
- dbg("%s - >> FW Versions Device %d.%d Driver %d.%d",
- __func__,
- firmware_version->Ver_Major,
- firmware_version->Ver_Minor,
- OperationalMajorVersion,
- OperationalMinorVersion);
+ dev_dbg(dev, "%s - >> FW Versions Device %d.%d Driver %d.%d\n",
+ __func__, firmware_version->Ver_Major,
+ firmware_version->Ver_Minor,
+ OperationalMajorVersion,
+ OperationalMinorVersion);
/* Check if we have an old version in the I2C and
update if necessary */
if (download_cur_ver < download_new_ver) {
- dbg("%s - Update I2C dld from %d.%d to %d.%d",
- __func__,
- firmware_version->Ver_Major,
- firmware_version->Ver_Minor,
- OperationalMajorVersion,
- OperationalMinorVersion);
+ dev_dbg(dev, "%s - Update I2C dld from %d.%d to %d.%d\n",
+ __func__,
+ firmware_version->Ver_Major,
+ firmware_version->Ver_Minor,
+ OperationalMajorVersion,
+ OperationalMinorVersion);
record = kmalloc(1, GFP_KERNEL);
if (!record) {
@@ -1196,9 +1184,7 @@ static int download_fw(struct edgeport_serial *serial)
}
if (*record != I2C_DESC_TYPE_FIRMWARE_BLANK) {
- dev_err(dev,
- "%s - error resetting device\n",
- __func__);
+ dev_err(dev, "%s - error resetting device\n", __func__);
kfree(record);
kfree(firmware_version);
kfree(rom_desc);
@@ -1206,15 +1192,14 @@ static int download_fw(struct edgeport_serial *serial)
return -ENODEV;
}
- dbg("%s - HARDWARE RESET", __func__);
+ dev_dbg(dev, "%s - HARDWARE RESET\n", __func__);
/* Reset UMP -- Back to BOOT MODE */
status = ti_vsend_sync(serial->serial->dev,
UMPC_HARDWARE_RESET,
0, 0, NULL, 0);
- dbg("%s - HARDWARE RESET return %d",
- __func__, status);
+ dev_dbg(dev, "%s - HARDWARE RESET return %d\n", __func__, status);
/* return an error on purpose. */
kfree(record);
@@ -1249,8 +1234,7 @@ static int download_fw(struct edgeport_serial *serial)
return -ENOMEM;
}
- dbg("%s - Found Type BLANK FIRMWARE (Type F2) record",
- __func__);
+ dev_dbg(dev, "%s - Found Type BLANK FIRMWARE (Type F2) record\n", __func__);
/*
* In order to update the I2C firmware we must change
@@ -1292,7 +1276,7 @@ static int download_fw(struct edgeport_serial *serial)
HEADER_SIZE, vheader);
if (status) {
- dbg("%s - can't read header back", __func__);
+ dev_dbg(dev, "%s - can't read header back\n", __func__);
kfree(vheader);
kfree(header);
kfree(rom_desc);
@@ -1300,8 +1284,7 @@ static int download_fw(struct edgeport_serial *serial)
return status;
}
if (memcmp(vheader, header, HEADER_SIZE)) {
- dbg("%s - write download record failed",
- __func__);
+ dev_dbg(dev, "%s - write download record failed\n", __func__);
kfree(vheader);
kfree(header);
kfree(rom_desc);
@@ -1312,13 +1295,13 @@ static int download_fw(struct edgeport_serial *serial)
kfree(vheader);
kfree(header);
- dbg("%s - Start firmware update", __func__);
+ dev_dbg(dev, "%s - Start firmware update\n", __func__);
/* Tell firmware to copy download image into I2C */
status = ti_vsend_sync(serial->serial->dev,
UMPC_COPY_DNLD_TO_I2C, 0, 0, NULL, 0);
- dbg("%s - Update complete 0x%x", __func__, status);
+ dev_dbg(dev, "%s - Update complete 0x%x\n", __func__, status);
if (status) {
dev_err(dev,
"%s - UMPC_COPY_DNLD_TO_I2C failed\n",
@@ -1338,7 +1321,7 @@ static int download_fw(struct edgeport_serial *serial)
/********************************************************************/
/* Boot Mode */
/********************************************************************/
- dbg("%s - RUNNING IN BOOT MODE", __func__);
+ dev_dbg(dev, "%s - RUNNING IN BOOT MODE\n", __func__);
/* Configure the TI device so we can use the BULK pipes for download */
status = config_boot_dev(serial->serial->dev);
@@ -1347,8 +1330,8 @@ static int download_fw(struct edgeport_serial *serial)
if (le16_to_cpu(serial->serial->dev->descriptor.idVendor)
!= USB_VENDOR_ID_ION) {
- dbg("%s - VID = 0x%x", __func__,
- le16_to_cpu(serial->serial->dev->descriptor.idVendor));
+ dev_dbg(dev, "%s - VID = 0x%x\n", __func__,
+ le16_to_cpu(serial->serial->dev->descriptor.idVendor));
serial->TI_I2C_Type = DTK_ADDR_SPACE_I2C_TYPE_II;
goto stayinbootmode;
}
@@ -1385,8 +1368,8 @@ static int download_fw(struct edgeport_serial *serial)
/* Check for version 2 */
if (!ignore_cpu_rev && ti_cpu_rev(ti_manuf_desc) < 2) {
- dbg("%s - Wrong CPU Rev %d (Must be 2)",
- __func__, ti_cpu_rev(ti_manuf_desc));
+ dev_dbg(dev, "%s - Wrong CPU Rev %d (Must be 2)\n",
+ __func__, ti_cpu_rev(ti_manuf_desc));
kfree(ti_manuf_desc);
goto stayinbootmode;
}
@@ -1421,8 +1404,8 @@ static int download_fw(struct edgeport_serial *serial)
err = request_firmware(&fw, fw_name, dev);
if (err) {
- printk(KERN_ERR "Failed to load image \"%s\" err %d\n",
- fw_name, err);
+ dev_err(dev, "Failed to load image \"%s\" err %d\n",
+ fw_name, err);
kfree(buffer);
return err;
}
@@ -1442,23 +1425,20 @@ static int download_fw(struct edgeport_serial *serial)
header->CheckSum = cs;
/* Download the operational code */
- dbg("%s - Downloading operational code image (TI UMP)",
- __func__);
+ dev_dbg(dev, "%s - Downloading operational code image (TI UMP)\n", __func__);
status = download_code(serial, buffer, buffer_size);
kfree(buffer);
if (status) {
- dbg("%s - Error downloading operational code image",
- __func__);
+ dev_dbg(dev, "%s - Error downloading operational code image\n", __func__);
return status;
}
/* Device will reboot */
serial->product_info.TiMode = TI_MODE_TRANSITIONING;
- dbg("%s - Download successful -- Device rebooting...",
- __func__);
+ dev_dbg(dev, "%s - Download successful -- Device rebooting...\n", __func__);
/* return an error on purpose */
return -ENODEV;
@@ -1466,7 +1446,7 @@ static int download_fw(struct edgeport_serial *serial)
stayinbootmode:
/* Eprom is invalid or blank stay in boot mode */
- dbg("%s - STAYING IN BOOT MODE", __func__);
+ dev_dbg(dev, "%s - STAYING IN BOOT MODE\n", __func__);
serial->product_info.TiMode = TI_MODE_BOOT;
return 0;
@@ -1487,7 +1467,7 @@ static int restore_mcr(struct edgeport_port *port, __u8 mcr)
{
int status = 0;
- dbg("%s - %x", __func__, mcr);
+ dev_dbg(&port->port->dev, "%s - %x\n", __func__, mcr);
status = ti_do_config(port, UMPC_SET_CLR_DTR, mcr & MCR_DTR);
if (status)
@@ -1524,7 +1504,7 @@ static void handle_new_msr(struct edgeport_port *edge_port, __u8 msr)
struct async_icount *icount;
struct tty_struct *tty;
- dbg("%s - %02x", __func__, msr);
+ dev_dbg(&edge_port->port->dev, "%s - %02x\n", __func__, msr);
if (msr & (EDGEPORT_MSR_DELTA_CTS | EDGEPORT_MSR_DELTA_DSR |
EDGEPORT_MSR_DELTA_RI | EDGEPORT_MSR_DELTA_CD)) {
@@ -1566,7 +1546,7 @@ static void handle_new_lsr(struct edgeport_port *edge_port, int lsr_data,
LSR_FRM_ERR | LSR_BREAK));
struct tty_struct *tty;
- dbg("%s - %02x", __func__, new_lsr);
+ dev_dbg(&edge_port->port->dev, "%s - %02x\n", __func__, new_lsr);
edge_port->shadow_lsr = lsr;
@@ -1604,6 +1584,7 @@ static void edge_interrupt_callback(struct urb *urb)
struct edgeport_serial *edge_serial = urb->context;
struct usb_serial_port *port;
struct edgeport_port *edge_port;
+ struct device *dev;
unsigned char *data = urb->transfer_buffer;
int length = urb->actual_length;
int port_number;
@@ -1613,8 +1594,6 @@ static void edge_interrupt_callback(struct urb *urb)
__u8 msr;
int status = urb->status;
- dbg("%s", __func__);
-
switch (status) {
case 0:
/* success */
@@ -1623,7 +1602,7 @@ static void edge_interrupt_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d",
+ dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
@@ -1633,27 +1612,26 @@ static void edge_interrupt_callback(struct urb *urb)
}
if (!length) {
- dbg("%s - no data in urb", __func__);
+ dev_dbg(&urb->dev->dev, "%s - no data in urb\n", __func__);
goto exit;
}
- usb_serial_debug_data(debug, &edge_serial->serial->dev->dev,
- __func__, length, data);
+ dev = &edge_serial->serial->dev->dev;
+ usb_serial_debug_data(dev, __func__, length, data);
if (length != 2) {
- dbg("%s - expecting packet of size 2, got %d",
- __func__, length);
+ dev_dbg(dev, "%s - expecting packet of size 2, got %d\n", __func__, length);
goto exit;
}
port_number = TIUMP_GET_PORT_FROM_CODE(data[0]);
function = TIUMP_GET_FUNC_FROM_CODE(data[0]);
- dbg("%s - port_number %d, function %d, info 0x%x",
- __func__, port_number, function, data[1]);
+ dev_dbg(dev, "%s - port_number %d, function %d, info 0x%x\n", __func__,
+ port_number, function, data[1]);
port = edge_serial->serial->port[port_number];
edge_port = usb_get_serial_port_data(port);
if (!edge_port) {
- dbg("%s - edge_port not found", __func__);
+ dev_dbg(dev, "%s - edge_port not found\n", __func__);
return;
}
switch (function) {
@@ -1662,13 +1640,13 @@ static void edge_interrupt_callback(struct urb *urb)
if (lsr & UMP_UART_LSR_DATA_MASK) {
/* Save the LSR event for bulk read
completion routine */
- dbg("%s - LSR Event Port %u LSR Status = %02x",
- __func__, port_number, lsr);
+ dev_dbg(dev, "%s - LSR Event Port %u LSR Status = %02x\n",
+ __func__, port_number, lsr);
edge_port->lsr_event = 1;
edge_port->lsr_mask = lsr;
} else {
- dbg("%s - ===== Port %d LSR Status = %02x ======",
- __func__, port_number, lsr);
+ dev_dbg(dev, "%s - ===== Port %d LSR Status = %02x ======\n",
+ __func__, port_number, lsr);
handle_new_lsr(edge_port, 0, lsr, 0);
}
break;
@@ -1676,8 +1654,8 @@ static void edge_interrupt_callback(struct urb *urb)
case TIUMP_INTERRUPT_CODE_MSR: /* MSR */
/* Copy MSR from UMP */
msr = data[1];
- dbg("%s - ===== Port %u MSR Status = %02x ======",
- __func__, port_number, msr);
+ dev_dbg(dev, "%s - ===== Port %u MSR Status = %02x ======\n",
+ __func__, port_number, msr);
handle_new_msr(edge_port, msr);
break;
@@ -1700,14 +1678,13 @@ exit:
static void edge_bulk_in_callback(struct urb *urb)
{
struct edgeport_port *edge_port = urb->context;
+ struct device *dev = &edge_port->port->dev;
unsigned char *data = urb->transfer_buffer;
struct tty_struct *tty;
int retval = 0;
int port_number;
int status = urb->status;
- dbg("%s", __func__);
-
switch (status) {
case 0:
/* success */
@@ -1716,13 +1693,10 @@ static void edge_bulk_in_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d",
- __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status);
return;
default:
- dev_err(&urb->dev->dev,
- "%s - nonzero read bulk status received: %d\n",
- __func__, status);
+ dev_err(&urb->dev->dev, "%s - nonzero read bulk status received: %d\n", __func__, status);
}
if (status == -EPIPE)
@@ -1737,8 +1711,8 @@ static void edge_bulk_in_callback(struct urb *urb)
if (edge_port->lsr_event) {
edge_port->lsr_event = 0;
- dbg("%s ===== Port %u LSR Status = %02x, Data = %02x ======",
- __func__, port_number, edge_port->lsr_mask, *data);
+ dev_dbg(dev, "%s ===== Port %u LSR Status = %02x, Data = %02x ======\n",
+ __func__, port_number, edge_port->lsr_mask, *data);
handle_new_lsr(edge_port, 1, edge_port->lsr_mask, *data);
/* Adjust buffer length/pointer */
--urb->actual_length;
@@ -1747,14 +1721,12 @@ static void edge_bulk_in_callback(struct urb *urb)
tty = tty_port_tty_get(&edge_port->port->port);
if (tty && urb->actual_length) {
- usb_serial_debug_data(debug, &edge_port->port->dev,
- __func__, urb->actual_length, data);
+ usb_serial_debug_data(dev, __func__, urb->actual_length, data);
if (edge_port->close_pending)
- dbg("%s - close pending, dropping data on the floor",
+ dev_dbg(dev, "%s - close pending, dropping data on the floor\n",
__func__);
else
- edge_tty_recv(&edge_port->port->dev, tty, data,
- urb->actual_length);
+ edge_tty_recv(dev, tty, data, urb->actual_length);
edge_port->icount.rx += urb->actual_length;
}
tty_kref_put(tty);
@@ -1769,9 +1741,7 @@ exit:
spin_unlock(&edge_port->ep_lock);
if (retval)
- dev_err(&urb->dev->dev,
- "%s - usb_submit_urb failed with result %d\n",
- __func__, retval);
+ dev_err(dev, "%s - usb_submit_urb failed with result %d\n", __func__, retval);
}
static void edge_tty_recv(struct device *dev, struct tty_struct *tty,
@@ -1793,8 +1763,6 @@ static void edge_bulk_out_callback(struct urb *urb)
int status = urb->status;
struct tty_struct *tty;
- dbg("%s - port %d", __func__, port->number);
-
edge_port->ep_write_urb_in_use = 0;
switch (status) {
@@ -1805,7 +1773,7 @@ static void edge_bulk_out_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d",
+ dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
__func__, status);
return;
default:
@@ -1830,8 +1798,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
u16 open_settings;
u8 transaction_timeout;
- dbg("%s - port %d", __func__, port->number);
-
if (edge_port == NULL)
return -ENODEV;
@@ -1850,9 +1816,8 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
return -ENODEV;
}
- dbg("%s - port_number = %d, uart_base = %04x, dma_address = %04x",
- __func__, port_number, edge_port->uart_base,
- edge_port->dma_address);
+ dev_dbg(&port->dev, "%s - port_number = %d, uart_base = %04x, dma_address = %04x\n",
+ __func__, port_number, edge_port->uart_base, edge_port->dma_address);
dev = port->serial->dev;
@@ -1885,7 +1850,7 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
UMP_PIPE_TRANS_TIMEOUT_ENA |
(transaction_timeout << 2));
- dbg("%s - Sending UMPC_OPEN_PORT", __func__);
+ dev_dbg(&port->dev, "%s - Sending UMPC_OPEN_PORT\n", __func__);
/* Tell TI to open and start the port */
status = send_cmd(dev, UMPC_OPEN_PORT,
@@ -1924,11 +1889,11 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
return status;
}
- dbg("ShadowMSR 0x%X", edge_port->shadow_msr);
+ dev_dbg(&port->dev, "ShadowMSR 0x%X\n", edge_port->shadow_msr);
/* Set Initial MCR */
edge_port->shadow_mcr = MCR_RTS | MCR_DTR;
- dbg("ShadowMCR 0x%X", edge_port->shadow_mcr);
+ dev_dbg(&port->dev, "ShadowMCR 0x%X\n", edge_port->shadow_mcr);
edge_serial = edge_port->edge_serial;
if (mutex_lock_interruptible(&edge_serial->es_lock))
@@ -1980,8 +1945,6 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port)
++edge_serial->num_ports_open;
- dbg("%s - exited", __func__);
-
goto release_es_lock;
unlink_int_urb:
@@ -1999,8 +1962,6 @@ static void edge_close(struct usb_serial_port *port)
struct usb_serial *serial = port->serial;
int port_number;
- dbg("%s - port %d", __func__, port->number);
-
edge_serial = usb_get_serial_data(port->serial);
edge_port = usb_get_serial_port_data(port);
if (edge_serial == NULL || edge_port == NULL)
@@ -2019,7 +1980,7 @@ static void edge_close(struct usb_serial_port *port)
/* assuming we can still talk to the device,
* send a close port command to it */
- dbg("%s - send umpc_close_port", __func__);
+ dev_dbg(&port->dev, "%s - send umpc_close_port\n", __func__);
port_number = port->number - port->serial->minor;
mutex_lock(&serial->disc_mutex);
@@ -2042,8 +2003,6 @@ static void edge_close(struct usb_serial_port *port)
}
mutex_unlock(&edge_serial->es_lock);
edge_port->close_pending = 0;
-
- dbg("%s - exited", __func__);
}
static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
@@ -2051,10 +2010,8 @@ static int edge_write(struct tty_struct *tty, struct usb_serial_port *port,
{
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
- dbg("%s - port %d", __func__, port->number);
-
if (count == 0) {
- dbg("%s - write request of 0 bytes", __func__);
+ dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
return 0;
}
@@ -2077,9 +2034,6 @@ static void edge_send(struct tty_struct *tty)
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
unsigned long flags;
-
- dbg("%s - port %d", __func__, port->number);
-
spin_lock_irqsave(&edge_port->ep_lock, flags);
if (edge_port->ep_write_urb_in_use) {
@@ -2100,8 +2054,7 @@ static void edge_send(struct tty_struct *tty)
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
- usb_serial_debug_data(debug, &port->dev, __func__, count,
- port->write_urb->transfer_buffer);
+ usb_serial_debug_data(&port->dev, __func__, count, port->write_urb->transfer_buffer);
/* set up our urb */
port->write_urb->transfer_buffer_length = count;
@@ -2130,8 +2083,6 @@ static int edge_write_room(struct tty_struct *tty)
int room = 0;
unsigned long flags;
- dbg("%s - port %d", __func__, port->number);
-
if (edge_port == NULL)
return 0;
if (edge_port->close_pending == 1)
@@ -2141,7 +2092,7 @@ static int edge_write_room(struct tty_struct *tty)
room = kfifo_avail(&edge_port->write_fifo);
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
- dbg("%s - returns %d", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
return room;
}
@@ -2152,8 +2103,6 @@ static int edge_chars_in_buffer(struct tty_struct *tty)
int chars = 0;
unsigned long flags;
- dbg("%s - port %d", __func__, port->number);
-
if (edge_port == NULL)
return 0;
if (edge_port->close_pending == 1)
@@ -2163,7 +2112,7 @@ static int edge_chars_in_buffer(struct tty_struct *tty)
chars = kfifo_len(&edge_port->write_fifo);
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
- dbg("%s - returns %d", __func__, chars);
+ dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
return chars;
}
@@ -2173,8 +2122,6 @@ static void edge_throttle(struct tty_struct *tty)
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status;
- dbg("%s - port %d", __func__, port->number);
-
if (edge_port == NULL)
return;
@@ -2200,8 +2147,6 @@ static void edge_unthrottle(struct tty_struct *tty)
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
int status;
- dbg("%s - port %d", __func__, port->number);
-
if (edge_port == NULL)
return;
@@ -2261,6 +2206,7 @@ static int restart_read(struct edgeport_port *edge_port)
static void change_port_settings(struct tty_struct *tty,
struct edgeport_port *edge_port, struct ktermios *old_termios)
{
+ struct device *dev = &edge_port->port->dev;
struct ump_uart_config *config;
int baud;
unsigned cflag;
@@ -2268,13 +2214,12 @@ static void change_port_settings(struct tty_struct *tty,
int port_number = edge_port->port->number -
edge_port->port->serial->minor;
- dbg("%s - port %d", __func__, edge_port->port->number);
+ dev_dbg(dev, "%s - port %d\n", __func__, edge_port->port->number);
config = kmalloc (sizeof (*config), GFP_KERNEL);
if (!config) {
tty->termios = *old_termios;
- dev_err(&edge_port->port->dev, "%s - out of memory\n",
- __func__);
+ dev_err(dev, "%s - out of memory\n", __func__);
return;
}
@@ -2290,20 +2235,20 @@ static void change_port_settings(struct tty_struct *tty,
switch (cflag & CSIZE) {
case CS5:
config->bDataBits = UMP_UART_CHAR5BITS;
- dbg("%s - data bits = 5", __func__);
+ dev_dbg(dev, "%s - data bits = 5\n", __func__);
break;
case CS6:
config->bDataBits = UMP_UART_CHAR6BITS;
- dbg("%s - data bits = 6", __func__);
+ dev_dbg(dev, "%s - data bits = 6\n", __func__);
break;
case CS7:
config->bDataBits = UMP_UART_CHAR7BITS;
- dbg("%s - data bits = 7", __func__);
+ dev_dbg(dev, "%s - data bits = 7\n", __func__);
break;
default:
case CS8:
config->bDataBits = UMP_UART_CHAR8BITS;
- dbg("%s - data bits = 8", __func__);
+ dev_dbg(dev, "%s - data bits = 8\n", __func__);
break;
}
@@ -2311,32 +2256,32 @@ static void change_port_settings(struct tty_struct *tty,
if (cflag & PARODD) {
config->wFlags |= UMP_MASK_UART_FLAGS_PARITY;
config->bParity = UMP_UART_ODDPARITY;
- dbg("%s - parity = odd", __func__);
+ dev_dbg(dev, "%s - parity = odd\n", __func__);
} else {
config->wFlags |= UMP_MASK_UART_FLAGS_PARITY;
config->bParity = UMP_UART_EVENPARITY;
- dbg("%s - parity = even", __func__);
+ dev_dbg(dev, "%s - parity = even\n", __func__);
}
} else {
config->bParity = UMP_UART_NOPARITY;
- dbg("%s - parity = none", __func__);
+ dev_dbg(dev, "%s - parity = none\n", __func__);
}
if (cflag & CSTOPB) {
config->bStopBits = UMP_UART_STOPBIT2;
- dbg("%s - stop bits = 2", __func__);
+ dev_dbg(dev, "%s - stop bits = 2\n", __func__);
} else {
config->bStopBits = UMP_UART_STOPBIT1;
- dbg("%s - stop bits = 1", __func__);
+ dev_dbg(dev, "%s - stop bits = 1\n", __func__);
}
/* figure out the flow control settings */
if (cflag & CRTSCTS) {
config->wFlags |= UMP_MASK_UART_FLAGS_OUT_X_CTS_FLOW;
config->wFlags |= UMP_MASK_UART_FLAGS_RTS_FLOW;
- dbg("%s - RTS/CTS is enabled", __func__);
+ dev_dbg(dev, "%s - RTS/CTS is enabled\n", __func__);
} else {
- dbg("%s - RTS/CTS is disabled", __func__);
+ dev_dbg(dev, "%s - RTS/CTS is disabled\n", __func__);
tty->hw_stopped = 0;
restart_read(edge_port);
}
@@ -2349,18 +2294,18 @@ static void change_port_settings(struct tty_struct *tty,
/* if we are implementing INBOUND XON/XOFF */
if (I_IXOFF(tty)) {
config->wFlags |= UMP_MASK_UART_FLAGS_IN_X;
- dbg("%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
- __func__, config->cXon, config->cXoff);
+ dev_dbg(dev, "%s - INBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
+ __func__, config->cXon, config->cXoff);
} else
- dbg("%s - INBOUND XON/XOFF is disabled", __func__);
+ dev_dbg(dev, "%s - INBOUND XON/XOFF is disabled\n", __func__);
/* if we are implementing OUTBOUND XON/XOFF */
if (I_IXON(tty)) {
config->wFlags |= UMP_MASK_UART_FLAGS_OUT_X;
- dbg("%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x",
- __func__, config->cXon, config->cXoff);
+ dev_dbg(dev, "%s - OUTBOUND XON/XOFF is enabled, XON = %2x, XOFF = %2x\n",
+ __func__, config->cXon, config->cXoff);
} else
- dbg("%s - OUTBOUND XON/XOFF is disabled", __func__);
+ dev_dbg(dev, "%s - OUTBOUND XON/XOFF is disabled\n", __func__);
tty->termios.c_cflag &= ~CMSPAR;
@@ -2377,17 +2322,16 @@ static void change_port_settings(struct tty_struct *tty,
/* FIXME: Recompute actual baud from divisor here */
- dbg("%s - baud rate = %d, wBaudRate = %d", __func__, baud,
- config->wBaudRate);
+ dev_dbg(dev, "%s - baud rate = %d, wBaudRate = %d\n", __func__, baud, config->wBaudRate);
- dbg("wBaudRate: %d", (int)(461550L / config->wBaudRate));
- dbg("wFlags: 0x%x", config->wFlags);
- dbg("bDataBits: %d", config->bDataBits);
- dbg("bParity: %d", config->bParity);
- dbg("bStopBits: %d", config->bStopBits);
- dbg("cXon: %d", config->cXon);
- dbg("cXoff: %d", config->cXoff);
- dbg("bUartMode: %d", config->bUartMode);
+ dev_dbg(dev, "wBaudRate: %d\n", (int)(461550L / config->wBaudRate));
+ dev_dbg(dev, "wFlags: 0x%x\n", config->wFlags);
+ dev_dbg(dev, "bDataBits: %d\n", config->bDataBits);
+ dev_dbg(dev, "bParity: %d\n", config->bParity);
+ dev_dbg(dev, "bStopBits: %d\n", config->bStopBits);
+ dev_dbg(dev, "cXon: %d\n", config->cXon);
+ dev_dbg(dev, "cXoff: %d\n", config->cXoff);
+ dev_dbg(dev, "bUartMode: %d\n", config->bUartMode);
/* move the word values into big endian mode */
cpu_to_be16s(&config->wFlags);
@@ -2397,8 +2341,8 @@ static void change_port_settings(struct tty_struct *tty,
(__u8)(UMPM_UART1_PORT + port_number),
0, (__u8 *)config, sizeof(*config));
if (status)
- dbg("%s - error %d when trying to write config to device",
- __func__, status);
+ dev_dbg(dev, "%s - error %d when trying to write config to device\n",
+ __func__, status);
kfree(config);
}
@@ -2410,11 +2354,11 @@ static void edge_set_termios(struct tty_struct *tty,
cflag = tty->termios.c_cflag;
- dbg("%s - clfag %08x iflag %08x", __func__,
- tty->termios.c_cflag, tty->termios.c_iflag);
- dbg("%s - old clfag %08x old iflag %08x", __func__,
- old_termios->c_cflag, old_termios->c_iflag);
- dbg("%s - port %d", __func__, port->number);
+ dev_dbg(&port->dev, "%s - clfag %08x iflag %08x\n", __func__,
+ tty->termios.c_cflag, tty->termios.c_iflag);
+ dev_dbg(&port->dev, "%s - old clfag %08x old iflag %08x\n", __func__,
+ old_termios->c_cflag, old_termios->c_iflag);
+ dev_dbg(&port->dev, "%s - port %d\n", __func__, port->number);
if (edge_port == NULL)
return;
@@ -2430,8 +2374,6 @@ static int edge_tiocmset(struct tty_struct *tty,
unsigned int mcr;
unsigned long flags;
- dbg("%s - port %d", __func__, port->number);
-
spin_lock_irqsave(&edge_port->ep_lock, flags);
mcr = edge_port->shadow_mcr;
if (set & TIOCM_RTS)
@@ -2464,8 +2406,6 @@ static int edge_tiocmget(struct tty_struct *tty)
unsigned int mcr;
unsigned long flags;
- dbg("%s - port %d", __func__, port->number);
-
spin_lock_irqsave(&edge_port->ep_lock, flags);
msr = edge_port->shadow_msr;
@@ -2478,7 +2418,7 @@ static int edge_tiocmget(struct tty_struct *tty)
| ((msr & EDGEPORT_MSR_DSR) ? TIOCM_DSR: 0); /* 0x100 */
- dbg("%s -- %x", __func__, result);
+ dev_dbg(&port->dev, "%s -- %x\n", __func__, result);
spin_unlock_irqrestore(&edge_port->ep_lock, flags);
return result;
@@ -2538,15 +2478,15 @@ static int edge_ioctl(struct tty_struct *tty,
struct async_icount cnow;
struct async_icount cprev;
- dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+ dev_dbg(&port->dev, "%s - port %d, cmd = 0x%x\n", __func__, port->number, cmd);
switch (cmd) {
case TIOCGSERIAL:
- dbg("%s - (%d) TIOCGSERIAL", __func__, port->number);
+ dev_dbg(&port->dev, "%s - TIOCGSERIAL\n", __func__);
return get_serial_info(edge_port,
(struct serial_struct __user *) arg);
case TIOCMIWAIT:
- dbg("%s - (%d) TIOCMIWAIT", __func__, port->number);
+ dev_dbg(&port->dev, "%s - TIOCMIWAIT\n", __func__);
cprev = edge_port->icount;
while (1) {
interruptible_sleep_on(&edge_port->delta_msr_wait);
@@ -2578,8 +2518,6 @@ static void edge_break(struct tty_struct *tty, int break_state)
int status;
int bv = 0; /* Off */
- dbg("%s - state = %d", __func__, break_state);
-
/* chase the port close */
chase_port(edge_port, 0, 0);
@@ -2587,8 +2525,8 @@ static void edge_break(struct tty_struct *tty, int break_state)
bv = 1; /* On */
status = ti_do_config(edge_port, UMPC_SET_CLR_BREAK, bv);
if (status)
- dbg("%s - error %d sending break set/clear command.",
- __func__, status);
+ dev_dbg(&port->dev, "%s - error %d sending break set/clear command.\n",
+ __func__, status);
}
static int edge_startup(struct usb_serial *serial)
@@ -2655,7 +2593,6 @@ cleanup:
static void edge_disconnect(struct usb_serial *serial)
{
- dbg("%s", __func__);
}
static void edge_release(struct usb_serial *serial)
@@ -2663,8 +2600,6 @@ static void edge_release(struct usb_serial *serial)
int i;
struct edgeport_port *edge_port;
- dbg("%s", __func__);
-
for (i = 0; i < serial->num_ports; ++i) {
edge_port = usb_get_serial_port_data(serial->port[i]);
kfifo_free(&edge_port->write_fifo);
@@ -2692,7 +2627,7 @@ static ssize_t store_uart_mode(struct device *dev,
struct edgeport_port *edge_port = usb_get_serial_port_data(port);
unsigned int v = simple_strtoul(valbuf, NULL, 0);
- dbg("%s: setting uart_mode = %d", __func__, v);
+ dev_dbg(dev, "%s: setting uart_mode = %d\n", __func__, v);
if (v < 256)
edge_port->bUartMode = v;
@@ -2789,9 +2724,6 @@ MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("edgeport/down3.bin");
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
-
module_param(closing_wait, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(closing_wait, "Maximum wait for data to drain, in .01 secs");
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index c85a7eb87d4e..1068bf22e27e 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -33,7 +33,6 @@
#define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
#define DRIVER_DESC "USB PocketPC PDA driver"
-static bool debug;
static int connect_retries = KP_RETRIES;
static int initial_wait;
@@ -616,9 +615,6 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
-
module_param(connect_retries, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(connect_retries,
"Maximum number of connect retries (one second each)");
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index 2cb30c535839..20a132ec39e2 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -138,11 +138,10 @@ static const struct usb_device_id id_table[] = {
};
MODULE_DEVICE_TABLE(usb, id_table);
-static bool debug;
-
static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port)
{
- struct usb_device *dev = port->serial->dev;
+ struct usb_device *udev = port->serial->dev;
+ struct device *dev = &port->dev;
u8 buf_flow_static[16] = IPW_BYTES_FLOWINIT;
u8 *buf_flow_init;
int result;
@@ -154,8 +153,8 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port)
/* --1: Tell the modem to initialize (we think) From sniffs this is
* always the first thing that gets sent to the modem during
* opening of the device */
- dbg("%s: Sending SIO_INIT (we guess)", __func__);
- result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ dev_dbg(dev, "%s: Sending SIO_INIT (we guess)\n", __func__);
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_INIT,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
0,
@@ -164,22 +163,19 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port)
0,
100000);
if (result < 0)
- dev_err(&port->dev,
- "Init of modem failed (error = %d)\n", result);
+ dev_err(dev, "Init of modem failed (error = %d)\n", result);
/* reset the bulk pipes */
- usb_clear_halt(dev,
- usb_rcvbulkpipe(dev, port->bulk_in_endpointAddress));
- usb_clear_halt(dev,
- usb_sndbulkpipe(dev, port->bulk_out_endpointAddress));
+ usb_clear_halt(udev, usb_rcvbulkpipe(udev, port->bulk_in_endpointAddress));
+ usb_clear_halt(udev, usb_sndbulkpipe(udev, port->bulk_out_endpointAddress));
/*--2: Start reading from the device */
- dbg("%s: setting up bulk read callback", __func__);
+ dev_dbg(dev, "%s: setting up bulk read callback\n", __func__);
usb_wwan_open(tty, port);
/*--3: Tell the modem to open the floodgates on the rx bulk channel */
- dbg("%s:asking modem for RxRead (RXBULK_ON)", __func__);
- result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ dev_dbg(dev, "%s:asking modem for RxRead (RXBULK_ON)\n", __func__);
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_RXCTL,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
IPW_RXBULK_ON,
@@ -188,12 +184,11 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port)
0,
100000);
if (result < 0)
- dev_err(&port->dev,
- "Enabling bulk RxRead failed (error = %d)\n", result);
+ dev_err(dev, "Enabling bulk RxRead failed (error = %d)\n", result);
/*--4: setup the initial flowcontrol */
- dbg("%s:setting init flowcontrol (%s)", __func__, buf_flow_init);
- result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ dev_dbg(dev, "%s:setting init flowcontrol (%s)\n", __func__, buf_flow_init);
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_HANDFLOW,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
0,
@@ -202,8 +197,7 @@ static int ipw_open(struct tty_struct *tty, struct usb_serial_port *port)
0x10,
200000);
if (result < 0)
- dev_err(&port->dev,
- "initial flowcontrol failed (error = %d)\n", result);
+ dev_err(dev, "initial flowcontrol failed (error = %d)\n", result);
kfree(buf_flow_init);
return 0;
@@ -233,12 +227,13 @@ static void ipw_release(struct usb_serial *serial)
static void ipw_dtr_rts(struct usb_serial_port *port, int on)
{
- struct usb_device *dev = port->serial->dev;
+ struct usb_device *udev = port->serial->dev;
+ struct device *dev = &port->dev;
int result;
- dbg("%s: on = %d", __func__, on);
+ dev_dbg(dev, "%s: on = %d\n", __func__, on);
- result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_SET_PIN,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
on ? IPW_PIN_SETDTR : IPW_PIN_CLRDTR,
@@ -247,10 +242,9 @@ static void ipw_dtr_rts(struct usb_serial_port *port, int on)
0,
200000);
if (result < 0)
- dev_err(&port->dev, "setting dtr failed (error = %d)\n",
- result);
+ dev_err(dev, "setting dtr failed (error = %d)\n", result);
- result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_SET_PIN, USB_TYPE_VENDOR |
USB_RECIP_INTERFACE | USB_DIR_OUT,
on ? IPW_PIN_SETRTS : IPW_PIN_CLRRTS,
@@ -259,18 +253,18 @@ static void ipw_dtr_rts(struct usb_serial_port *port, int on)
0,
200000);
if (result < 0)
- dev_err(&port->dev, "setting rts failed (error = %d)\n",
- result);
+ dev_err(dev, "setting rts failed (error = %d)\n", result);
}
static void ipw_close(struct usb_serial_port *port)
{
- struct usb_device *dev = port->serial->dev;
+ struct usb_device *udev = port->serial->dev;
+ struct device *dev = &port->dev;
int result;
/*--3: purge */
- dbg("%s:sending purge", __func__);
- result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ dev_dbg(dev, "%s:sending purge\n", __func__);
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_PURGE, USB_TYPE_VENDOR |
USB_RECIP_INTERFACE | USB_DIR_OUT,
0x03,
@@ -279,12 +273,12 @@ static void ipw_close(struct usb_serial_port *port)
0,
200000);
if (result < 0)
- dev_err(&port->dev, "purge failed (error = %d)\n", result);
+ dev_err(dev, "purge failed (error = %d)\n", result);
/* send RXBULK_off (tell modem to stop transmitting bulk data on
rx chan) */
- result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
IPW_SIO_RXCTL,
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
IPW_RXBULK_OFF,
@@ -294,8 +288,7 @@ static void ipw_close(struct usb_serial_port *port)
100000);
if (result < 0)
- dev_err(&port->dev,
- "Disabling bulk RxRead failed (error = %d)\n", result);
+ dev_err(dev, "Disabling bulk RxRead failed (error = %d)\n", result);
usb_wwan_close(port);
}
@@ -328,6 +321,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 5a96692b12a2..e24e2d4f4c1b 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -38,15 +38,9 @@
#include <linux/usb/serial.h>
#include <linux/usb/irda.h>
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v0.5"
#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Johan Hovold <jhovold@gmail.com>"
#define DRIVER_DESC "USB IR Dongle driver"
-static bool debug;
-
/* if overridden by the user, then use their value for the size of the read and
* write urbs */
static int buffer_size;
@@ -430,18 +424,12 @@ err_buf:
static int __init ir_init(void)
{
- int retval;
-
if (buffer_size) {
ir_device.bulk_in_size = buffer_size;
ir_device.bulk_out_size = buffer_size;
}
- retval = usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, ir_id_table);
- if (retval == 0)
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
- DRIVER_DESC "\n");
- return retval;
+ return usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, ir_id_table);
}
static void __exit ir_exit(void)
@@ -457,8 +445,6 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
module_param(xbof, int, 0);
MODULE_PARM_DESC(xbof, "Force specific number of XBOFs");
module_param(buffer_size, int, 0);
diff --git a/drivers/usb/serial/iuu_phoenix.c b/drivers/usb/serial/iuu_phoenix.c
index bf3864045c18..01da3ea36e89 100644
--- a/drivers/usb/serial/iuu_phoenix.c
+++ b/drivers/usb/serial/iuu_phoenix.c
@@ -32,13 +32,6 @@
#include "iuu_phoenix.h"
#include <linux/random.h>
-
-#ifdef CONFIG_USB_SERIAL_DEBUG
-static bool debug = 1;
-#else
-static bool debug;
-#endif
-
/*
* Version Information
*/
@@ -72,7 +65,6 @@ struct iuu_private {
u8 *writebuf; /* buffer for writing to device */
int writelen; /* num of byte to write to device */
u8 *buf; /* used for initialize speed */
- u8 *dbgbuf; /* debug buffer */
u8 len;
int vcc; /* vcc (either 3 or 5 V) */
u32 baud;
@@ -84,32 +76,31 @@ struct iuu_private {
static void iuu_free_buf(struct iuu_private *priv)
{
kfree(priv->buf);
- kfree(priv->dbgbuf);
kfree(priv->writebuf);
}
-static int iuu_alloc_buf(struct iuu_private *priv)
+static int iuu_alloc_buf(struct usb_serial *serial, struct iuu_private *priv)
{
priv->buf = kzalloc(256, GFP_KERNEL);
- priv->dbgbuf = kzalloc(256, GFP_KERNEL);
priv->writebuf = kzalloc(256, GFP_KERNEL);
- if (!priv->buf || !priv->dbgbuf || !priv->writebuf) {
+ if (!priv->buf || !priv->writebuf) {
iuu_free_buf(priv);
- dbg("%s problem allocation buffer", __func__);
+ dev_dbg(&serial->dev->dev, "%s problem allocation buffer\n", __func__);
return -ENOMEM;
}
- dbg("%s - Privates buffers allocation success", __func__);
+ dev_dbg(&serial->dev->dev, "%s - Privates buffers allocation success\n", __func__);
return 0;
}
static int iuu_startup(struct usb_serial *serial)
{
struct iuu_private *priv;
+
priv = kzalloc(sizeof(struct iuu_private), GFP_KERNEL);
- dbg("%s- priv allocation success", __func__);
+ dev_dbg(&serial->dev->dev, "%s- priv allocation success\n", __func__);
if (!priv)
return -ENOMEM;
- if (iuu_alloc_buf(priv)) {
+ if (iuu_alloc_buf(serial, priv)) {
kfree(priv);
return -ENOMEM;
}
@@ -130,13 +121,13 @@ static void iuu_release(struct usb_serial *serial)
if (priv) {
iuu_free_buf(priv);
- dbg("%s - I will free all", __func__);
+ dev_dbg(&port->dev, "%s - I will free all\n", __func__);
usb_set_serial_port_data(port, NULL);
- dbg("%s - priv is not anymore in port structure", __func__);
+ dev_dbg(&port->dev, "%s - priv is not anymore in port structure\n", __func__);
kfree(priv);
- dbg("%s priv is now kfree", __func__);
+ dev_dbg(&port->dev, "%s priv is now kfree\n", __func__);
}
}
@@ -148,13 +139,13 @@ static int iuu_tiocmset(struct tty_struct *tty,
unsigned long flags;
/* FIXME: locking on tiomstatus */
- dbg("%s (%d) msg : SET = 0x%04x, CLEAR = 0x%04x ", __func__,
- port->number, set, clear);
+ dev_dbg(&port->dev, "%s msg : SET = 0x%04x, CLEAR = 0x%04x\n",
+ __func__, set, clear);
spin_lock_irqsave(&priv->lock, flags);
if ((set & TIOCM_RTS) && !(priv->tiostatus == TIOCM_RTS)) {
- dbg("%s TIOCMSET RESET called !!!", __func__);
+ dev_dbg(&port->dev, "%s TIOCMSET RESET called !!!\n", __func__);
priv->reset = 1;
}
if (set & TIOCM_RTS)
@@ -190,7 +181,7 @@ static void iuu_rxcmd(struct urb *urb)
int status = urb->status;
if (status) {
- dbg("%s - status = %d", __func__, status);
+ dev_dbg(&port->dev, "%s - status = %d\n", __func__, status);
/* error stop all */
return;
}
@@ -244,13 +235,13 @@ static void iuu_update_status_callback(struct urb *urb)
int status = urb->status;
if (status) {
- dbg("%s - status = %d", __func__, status);
+ dev_dbg(&port->dev, "%s - status = %d\n", __func__, status);
/* error stop all */
return;
}
st = urb->transfer_buffer;
- dbg("%s - enter", __func__);
+ dev_dbg(&port->dev, "%s - enter\n", __func__);
if (urb->actual_length == 1) {
switch (st[0]) {
case 0x1:
@@ -272,7 +263,7 @@ static void iuu_status_callback(struct urb *urb)
int result;
int status = urb->status;
- dbg("%s - status = %d", __func__, status);
+ dev_dbg(&port->dev, "%s - status = %d\n", __func__, status);
usb_fill_bulk_urb(port->read_urb, port->serial->dev,
usb_rcvbulkpipe(port->serial->dev,
port->bulk_in_endpointAddress),
@@ -311,9 +302,9 @@ static int bulk_immediate(struct usb_serial_port *port, u8 *buf, u8 count)
count, &actual, HZ * 1);
if (status != IUU_OPERATION_OK)
- dbg("%s - error = %2x", __func__, status);
+ dev_dbg(&port->dev, "%s - error = %2x\n", __func__, status);
else
- dbg("%s - write OK !", __func__);
+ dev_dbg(&port->dev, "%s - write OK !\n", __func__);
return status;
}
@@ -331,9 +322,9 @@ static int read_immediate(struct usb_serial_port *port, u8 *buf, u8 count)
count, &actual, HZ * 1);
if (status != IUU_OPERATION_OK)
- dbg("%s - error = %2x", __func__, status);
+ dev_dbg(&port->dev, "%s - error = %2x\n", __func__, status);
else
- dbg("%s - read OK !", __func__);
+ dev_dbg(&port->dev, "%s - read OK !\n", __func__);
return status;
}
@@ -357,9 +348,9 @@ static int iuu_led(struct usb_serial_port *port, unsigned int R,
status = bulk_immediate(port, buf, 8);
kfree(buf);
if (status != IUU_OPERATION_OK)
- dbg("%s - led error status = %2x", __func__, status);
+ dev_dbg(&port->dev, "%s - led error status = %2x\n", __func__, status);
else
- dbg("%s - led OK !", __func__);
+ dev_dbg(&port->dev, "%s - led OK !\n", __func__);
return IUU_OPERATION_OK;
}
@@ -445,7 +436,7 @@ static int iuu_clk(struct usb_serial_port *port, int dwFrq)
status = bulk_immediate(port, (u8 *) priv->buf, Count);
if (status != 0) {
- dbg("%s - write error ", __func__);
+ dev_dbg(&port->dev, "%s - write error\n", __func__);
return status;
}
} else if (frq == 3579000) {
@@ -554,12 +545,13 @@ static int iuu_clk(struct usb_serial_port *port, int dwFrq)
status = bulk_immediate(port, (u8 *) priv->buf, Count);
if (status != IUU_OPERATION_OK)
- dbg("%s - write error ", __func__);
+ dev_dbg(&port->dev, "%s - write error\n", __func__);
return status;
}
static int iuu_uart_flush(struct usb_serial_port *port)
{
+ struct device *dev = &port->dev;
int i;
int status;
u8 rxcmd = IUU_UART_RX;
@@ -571,27 +563,26 @@ static int iuu_uart_flush(struct usb_serial_port *port)
for (i = 0; i < 2; i++) {
status = bulk_immediate(port, &rxcmd, 1);
if (status != IUU_OPERATION_OK) {
- dbg("%s - uart_flush_write error", __func__);
+ dev_dbg(dev, "%s - uart_flush_write error\n", __func__);
return status;
}
status = read_immediate(port, &priv->len, 1);
if (status != IUU_OPERATION_OK) {
- dbg("%s - uart_flush_read error", __func__);
+ dev_dbg(dev, "%s - uart_flush_read error\n", __func__);
return status;
}
if (priv->len > 0) {
- dbg("%s - uart_flush datalen is : %i ", __func__,
- priv->len);
+ dev_dbg(dev, "%s - uart_flush datalen is : %i\n", __func__, priv->len);
status = read_immediate(port, priv->buf, priv->len);
if (status != IUU_OPERATION_OK) {
- dbg("%s - uart_flush_read error", __func__);
+ dev_dbg(dev, "%s - uart_flush_read error\n", __func__);
return status;
}
}
}
- dbg("%s - uart_flush_read OK!", __func__);
+ dev_dbg(dev, "%s - uart_flush_read OK!\n", __func__);
iuu_led(port, 0, 0xF000, 0, 0xFF);
return status;
}
@@ -610,10 +601,10 @@ static void read_buf_callback(struct urb *urb)
return;
}
- dbg("%s - %i chars to write", __func__, urb->actual_length);
+ dev_dbg(&port->dev, "%s - %i chars to write\n", __func__, urb->actual_length);
tty = tty_port_tty_get(&port->port);
if (data == NULL)
- dbg("%s - data is NULL !!!", __func__);
+ dev_dbg(&port->dev, "%s - data is NULL !!!\n", __func__);
if (tty && urb->actual_length && data) {
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
@@ -627,7 +618,6 @@ static int iuu_bulk_write(struct usb_serial_port *port)
struct iuu_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
int result;
- int i;
int buf_len;
char *buf_ptr = port->write_urb->transfer_buffer;
@@ -640,14 +630,8 @@ static int iuu_bulk_write(struct usb_serial_port *port)
buf_len = priv->writelen;
priv->writelen = 0;
spin_unlock_irqrestore(&priv->lock, flags);
- if (debug == 1) {
- for (i = 0; i < buf_len; i++)
- sprintf(priv->dbgbuf + i*2 ,
- "%02X", priv->writebuf[i]);
- priv->dbgbuf[buf_len+i*2] = 0;
- dbg("%s - writing %i chars : %s", __func__,
- buf_len, priv->dbgbuf);
- }
+ dev_dbg(&port->dev, "%s - writing %i chars : %*ph\n", __func__,
+ buf_len, buf_len, buf_ptr);
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
port->bulk_out_endpointAddress),
@@ -683,18 +667,18 @@ static void iuu_uart_read_callback(struct urb *urb)
priv->poll++;
if (status) {
- dbg("%s - status = %d", __func__, status);
+ dev_dbg(&port->dev, "%s - status = %d\n", __func__, status);
/* error stop all */
return;
}
if (data == NULL)
- dbg("%s - data is NULL !!!", __func__);
+ dev_dbg(&port->dev, "%s - data is NULL !!!\n", __func__);
if (urb->actual_length == 1 && data != NULL)
len = (int) data[0];
if (urb->actual_length > 1) {
- dbg("%s - urb->actual_length = %i", __func__,
+ dev_dbg(&port->dev, "%s - urb->actual_length = %i\n", __func__,
urb->actual_length);
error = 1;
return;
@@ -702,7 +686,7 @@ static void iuu_uart_read_callback(struct urb *urb)
/* if len > 0 call readbuf */
if (len > 0 && error == 0) {
- dbg("%s - call read buf - len to read is %i ",
+ dev_dbg(&port->dev, "%s - call read buf - len to read is %i\n",
__func__, len);
status = iuu_read_buf(port, len);
return;
@@ -729,7 +713,7 @@ static void iuu_uart_read_callback(struct urb *urb)
}
spin_unlock_irqrestore(&priv->lock, flags);
/* if nothing to write call again rxcmd */
- dbg("%s - rxcmd recall", __func__);
+ dev_dbg(&port->dev, "%s - rxcmd recall\n", __func__);
iuu_led_activity_off(urb);
}
@@ -769,7 +753,7 @@ static void read_rxcmd_callback(struct urb *urb)
port->read_urb->transfer_buffer, 256,
iuu_uart_read_callback, port);
result = usb_submit_urb(port->read_urb, GFP_ATOMIC);
- dbg("%s - submit result = %d", __func__, result);
+ dev_dbg(&port->dev, "%s - submit result = %d\n", __func__, result);
}
static int iuu_uart_on(struct usb_serial_port *port)
@@ -789,13 +773,13 @@ static int iuu_uart_on(struct usb_serial_port *port)
status = bulk_immediate(port, buf, 4);
if (status != IUU_OPERATION_OK) {
- dbg("%s - uart_on error", __func__);
+ dev_dbg(&port->dev, "%s - uart_on error\n", __func__);
goto uart_enable_failed;
}
/* iuu_reset() the card after iuu_uart_on() */
status = iuu_uart_flush(port);
if (status != IUU_OPERATION_OK)
- dbg("%s - uart_flush error", __func__);
+ dev_dbg(&port->dev, "%s - uart_flush error\n", __func__);
uart_enable_failed:
kfree(buf);
return status;
@@ -813,7 +797,7 @@ static int iuu_uart_off(struct usb_serial_port *port)
status = bulk_immediate(port, buf, 1);
if (status != IUU_OPERATION_OK)
- dbg("%s - uart_off error", __func__);
+ dev_dbg(&port->dev, "%s - uart_off error\n", __func__);
kfree(buf);
return status;
@@ -830,7 +814,7 @@ static int iuu_uart_baud(struct usb_serial_port *port, u32 baud_base,
u8 T1reload = 0;
unsigned int T1FrekvensHZ = 0;
- dbg("%s - enter baud_base=%d", __func__, baud_base);
+ dev_dbg(&port->dev, "%s - enter baud_base=%d\n", __func__, baud_base);
dataout = kmalloc(sizeof(u8) * 5, GFP_KERNEL);
if (!dataout)
@@ -911,7 +895,7 @@ static int iuu_uart_baud(struct usb_serial_port *port, u32 baud_base,
status = bulk_immediate(port, dataout, DataCount);
if (status != IUU_OPERATION_OK)
- dbg("%s - uart_off error", __func__);
+ dev_dbg(&port->dev, "%s - uart_off error\n", __func__);
kfree(dataout);
return status;
}
@@ -932,7 +916,7 @@ static void iuu_set_termios(struct tty_struct *tty,
/* Just use the ospeed. ispeed should be the same. */
baud = tty->termios.c_ospeed;
- dbg("%s - enter c_ospeed or baud=%d", __func__, baud);
+ dev_dbg(&port->dev, "%s - enter c_ospeed or baud=%d\n", __func__, baud);
/* compute the parity parameter */
parity = 0;
@@ -983,7 +967,7 @@ static void iuu_close(struct usb_serial_port *port)
if (serial->dev) {
/* free writebuf */
/* shutdown our urbs */
- dbg("%s - shutting down urbs", __func__);
+ dev_dbg(&port->dev, "%s - shutting down urbs\n", __func__);
usb_kill_urb(port->write_urb);
usb_kill_urb(port->read_urb);
usb_kill_urb(port->interrupt_in_urb);
@@ -1006,6 +990,7 @@ static void iuu_init_termios(struct tty_struct *tty)
static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
+ struct device *dev = &port->dev;
u8 *buf;
int result;
int baud;
@@ -1017,7 +1002,7 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
/* Re-encode speed */
tty_encode_baud_rate(tty, baud, baud);
- dbg("%s - port %d, baud %d", __func__, port->number, baud);
+ dev_dbg(dev, "%s - baud %d\n", __func__, baud);
usb_clear_halt(serial->dev, port->write_urb->pipe);
usb_clear_halt(serial->dev, port->read_urb->pipe);
@@ -1032,14 +1017,14 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
result = usb_control_msg(port->serial->dev, \
usb_rcvctrlpipe(port->serial->dev, 0), \
b, a, c, d, buf, 1, 1000); \
- dbg("0x%x:0x%x:0x%x:0x%x %d - %x", a, b, c, d, result, \
+ dev_dbg(dev, "0x%x:0x%x:0x%x:0x%x %d - %x\n", a, b, c, d, result, \
buf[0]); } while (0);
#define SOUP(a, b, c, d) do { \
result = usb_control_msg(port->serial->dev, \
usb_sndctrlpipe(port->serial->dev, 0), \
b, a, c, d, NULL, 0, 1000); \
- dbg("0x%x:0x%x:0x%x:0x%x %d", a, b, c, d, result); } while (0)
+ dev_dbg(dev, "0x%x:0x%x:0x%x:0x%x %d\n", a, b, c, d, result); } while (0)
/* This is not UART related but IUU USB driver related or something */
/* like that. Basically no IUU will accept any commands from the USB */
@@ -1119,7 +1104,7 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
iuu_uart_flush(port);
- dbg("%s - initialization done", __func__);
+ dev_dbg(dev, "%s - initialization done\n", __func__);
memset(port->write_urb->transfer_buffer, IUU_UART_RX, 1);
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
@@ -1129,11 +1114,10 @@ static int iuu_open(struct tty_struct *tty, struct usb_serial_port *port)
read_rxcmd_callback, port);
result = usb_submit_urb(port->write_urb, GFP_KERNEL);
if (result) {
- dev_err(&port->dev, "%s - failed submitting read urb,"
- " error %d\n", __func__, result);
+ dev_err(dev, "%s - failed submitting read urb, error %d\n", __func__, result);
iuu_close(port);
} else {
- dbg("%s - rxcmd OK", __func__);
+ dev_dbg(dev, "%s - rxcmd OK\n", __func__);
}
return result;
@@ -1159,9 +1143,9 @@ static int iuu_vcc_set(struct usb_serial_port *port, unsigned int vcc)
kfree(buf);
if (status != IUU_OPERATION_OK)
- dbg("%s - vcc error status = %2x", __func__, status);
+ dev_dbg(&port->dev, "%s - vcc error status = %2x\n", __func__, status);
else
- dbg("%s - vcc OK !", __func__);
+ dev_dbg(&port->dev, "%s - vcc OK !\n", __func__);
return status;
}
@@ -1192,7 +1176,7 @@ static ssize_t store_vcc_mode(struct device *dev,
goto fail_store_vcc_mode;
}
- dbg("%s: setting vcc_mode = %ld", __func__, v);
+ dev_dbg(dev, "%s: setting vcc_mode = %ld", __func__, v);
if ((v != 3) && (v != 5)) {
dev_err(dev, "%s - vcc_mode %ld is invalid\n", __func__, v);
@@ -1257,8 +1241,6 @@ MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_VERSION(DRIVER_VERSION);
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
module_param(xmas, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(xmas, "Xmas colors enabled or not");
diff --git a/drivers/usb/serial/keyspan.c b/drivers/usb/serial/keyspan.c
index 7bcbb47e1449..29c943d737d0 100644
--- a/drivers/usb/serial/keyspan.c
+++ b/drivers/usb/serial/keyspan.c
@@ -38,15 +38,12 @@
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/spinlock.h>
-#include <linux/firmware.h>
-#include <linux/ihex.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
+#include <linux/usb/ezusb.h>
#include "keyspan.h"
-static bool debug;
-
/*
* Version Information
*/
@@ -165,7 +162,7 @@ static void keyspan_set_termios(struct tty_struct *tty,
so other rates can be generated if desired. */
baud_rate = tty_get_baud_rate(tty);
/* If no match or invalid, don't change */
- if (d_details->calculate_baud_rate(baud_rate, d_details->baudclk,
+ if (d_details->calculate_baud_rate(port, baud_rate, d_details->baudclk,
NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) {
/* FIXME - more to do here to ensure rate changes cleanly */
/* FIXME - calcuate exact rate from divisor ? */
@@ -241,8 +238,8 @@ static int keyspan_write(struct tty_struct *tty,
dataOffset = 1;
}
- dbg("%s - for port %d (%d chars), flip=%d",
- __func__, port->number, count, p_priv->out_flip);
+ dev_dbg(&port->dev, "%s - for port %d (%d chars), flip=%d\n",
+ __func__, port->number, count, p_priv->out_flip);
for (left = count; left > 0; left -= todo) {
todo = left;
@@ -255,11 +252,11 @@ static int keyspan_write(struct tty_struct *tty,
this_urb = p_priv->out_urbs[flip];
if (this_urb == NULL) {
/* no bulk out, so return 0 bytes written */
- dbg("%s - no output urb :(", __func__);
+ dev_dbg(&port->dev, "%s - no output urb :(\n", __func__);
return count;
}
- dbg("%s - endpoint %d flip %d",
+ dev_dbg(&port->dev, "%s - endpoint %d flip %d\n",
__func__, usb_pipeendpoint(this_urb->pipe), flip);
if (this_urb->status == -EINPROGRESS) {
@@ -282,7 +279,7 @@ static int keyspan_write(struct tty_struct *tty,
err = usb_submit_urb(this_urb, GFP_ATOMIC);
if (err != 0)
- dbg("usb_submit_urb(write bulk) failed (%d)", err);
+ dev_dbg(&port->dev, "usb_submit_urb(write bulk) failed (%d)\n", err);
p_priv->tx_start_time[flip] = jiffies;
/* Flip for next time if usa26 or usa28 interface
@@ -305,8 +302,8 @@ static void usa26_indat_callback(struct urb *urb)
endpoint = usb_pipeendpoint(urb->pipe);
if (status) {
- dbg("%s - nonzero status: %x on endpoint %d.",
- __func__, status, endpoint);
+ dev_dbg(&urb->dev->dev,"%s - nonzero status: %x on endpoint %d.\n",
+ __func__, status, endpoint);
return;
}
@@ -325,7 +322,7 @@ static void usa26_indat_callback(struct urb *urb)
tty_insert_flip_char(tty, data[i], err);
} else {
/* some bytes had errors, every byte has status */
- dbg("%s - RX error!!!!", __func__);
+ dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__);
for (i = 0; i + 1 < urb->actual_length; i += 2) {
int stat = data[i], flag = 0;
if (stat & RXERROR_OVERRUN)
@@ -345,7 +342,7 @@ static void usa26_indat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+ dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
}
/* Outdat handling is common for all devices */
@@ -356,7 +353,7 @@ static void usa2x_outdat_callback(struct urb *urb)
port = urb->context;
p_priv = usb_get_serial_port_data(port);
- dbg("%s - urb %d", __func__, urb == p_priv->out_urbs[1]);
+ dev_dbg(&port->dev, "%s - urb %d\n", __func__, urb == p_priv->out_urbs[1]);
usb_serial_port_softint(port);
}
@@ -374,7 +371,7 @@ static void usa26_outcont_callback(struct urb *urb)
p_priv = usb_get_serial_port_data(port);
if (p_priv->resend_cont) {
- dbg("%s - sending setup", __func__);
+ dev_dbg(&port->dev, "%s - sending setup\n", __func__);
keyspan_usa26_send_setup(port->serial, port,
p_priv->resend_cont - 1);
}
@@ -394,20 +391,22 @@ static void usa26_instat_callback(struct urb *urb)
serial = urb->context;
if (status) {
- dbg("%s - nonzero status: %x", __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
return;
}
if (urb->actual_length != 9) {
- dbg("%s - %d byte report??", __func__, urb->actual_length);
+ dev_dbg(&urb->dev->dev, "%s - %d byte report??\n", __func__, urb->actual_length);
goto exit;
}
msg = (struct keyspan_usa26_portStatusMessage *)data;
#if 0
- dbg("%s - port status: port %d cts %d dcd %d dsr %d ri %d toff %d txoff %d rxen %d cr %d",
- __func__, msg->port, msg->hskia_cts, msg->gpia_dcd, msg->dsr, msg->ri, msg->_txOff,
- msg->_txXoff, msg->rxEnabled, msg->controlResponse);
+ dev_dbg(&urb->dev->dev,
+ "%s - port status: port %d cts %d dcd %d dsr %d ri %d toff %d txoff %d rxen %d cr %d",
+ __func__, msg->port, msg->hskia_cts, msg->gpia_dcd, msg->dsr,
+ msg->ri, msg->_txOff, msg->_txXoff, msg->rxEnabled,
+ msg->controlResponse);
#endif
/* Now do something useful with the data */
@@ -415,7 +414,7 @@ static void usa26_instat_callback(struct urb *urb)
/* Check port number from message and retrieve private data */
if (msg->port >= serial->num_ports) {
- dbg("%s - Unexpected port number %d", __func__, msg->port);
+ dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n", __func__, msg->port);
goto exit;
}
port = serial->port[msg->port];
@@ -438,7 +437,7 @@ static void usa26_instat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+ dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
exit: ;
}
@@ -465,8 +464,8 @@ static void usa28_indat_callback(struct urb *urb)
do {
if (status) {
- dbg("%s - nonzero status: %x on endpoint %d.",
- __func__, status, usb_pipeendpoint(urb->pipe));
+ dev_dbg(&urb->dev->dev, "%s - nonzero status: %x on endpoint %d.\n",
+ __func__, status, usb_pipeendpoint(urb->pipe));
return;
}
@@ -484,7 +483,7 @@ static void usa28_indat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - resubmit read urb failed. (%d)",
+ dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n",
__func__, err);
p_priv->in_flip ^= 1;
@@ -505,7 +504,7 @@ static void usa28_outcont_callback(struct urb *urb)
p_priv = usb_get_serial_port_data(port);
if (p_priv->resend_cont) {
- dbg("%s - sending setup", __func__);
+ dev_dbg(&port->dev, "%s - sending setup\n", __func__);
keyspan_usa28_send_setup(port->serial, port,
p_priv->resend_cont - 1);
}
@@ -526,25 +525,28 @@ static void usa28_instat_callback(struct urb *urb)
serial = urb->context;
if (status) {
- dbg("%s - nonzero status: %x", __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
return;
}
if (urb->actual_length != sizeof(struct keyspan_usa28_portStatusMessage)) {
- dbg("%s - bad length %d", __func__, urb->actual_length);
+ dev_dbg(&urb->dev->dev, "%s - bad length %d\n", __func__, urb->actual_length);
goto exit;
}
- /*dbg("%s %x %x %x %x %x %x %x %x %x %x %x %x", __func__
- data[0], data[1], data[2], data[3], data[4], data[5],
- data[6], data[7], data[8], data[9], data[10], data[11]);*/
+ /*
+ dev_dbg(&urb->dev->dev,
+ "%s %x %x %x %x %x %x %x %x %x %x %x %x", __func__,
+ data[0], data[1], data[2], data[3], data[4], data[5],
+ data[6], data[7], data[8], data[9], data[10], data[11]);
+ */
/* Now do something useful with the data */
msg = (struct keyspan_usa28_portStatusMessage *)data;
/* Check port number from message and retrieve private data */
if (msg->port >= serial->num_ports) {
- dbg("%s - Unexpected port number %d", __func__, msg->port);
+ dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n", __func__, msg->port);
goto exit;
}
port = serial->port[msg->port];
@@ -567,7 +569,7 @@ static void usa28_instat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+ dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
exit: ;
}
@@ -589,7 +591,7 @@ static void usa49_glocont_callback(struct urb *urb)
p_priv = usb_get_serial_port_data(port);
if (p_priv->resend_cont) {
- dbg("%s - sending setup", __func__);
+ dev_dbg(&port->dev, "%s - sending setup\n", __func__);
keyspan_usa49_send_setup(serial, port,
p_priv->resend_cont - 1);
break;
@@ -613,27 +615,29 @@ static void usa49_instat_callback(struct urb *urb)
serial = urb->context;
if (status) {
- dbg("%s - nonzero status: %x", __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
return;
}
if (urb->actual_length !=
sizeof(struct keyspan_usa49_portStatusMessage)) {
- dbg("%s - bad length %d", __func__, urb->actual_length);
+ dev_dbg(&urb->dev->dev, "%s - bad length %d\n", __func__, urb->actual_length);
goto exit;
}
- /*dbg(" %x %x %x %x %x %x %x %x %x %x %x", __func__,
- data[0], data[1], data[2], data[3], data[4], data[5],
- data[6], data[7], data[8], data[9], data[10]);*/
+ /*
+ dev_dbg(&urb->dev->dev, "%s: %x %x %x %x %x %x %x %x %x %x %x",
+ __func__, data[0], data[1], data[2], data[3], data[4],
+ data[5], data[6], data[7], data[8], data[9], data[10]);
+ */
/* Now do something useful with the data */
msg = (struct keyspan_usa49_portStatusMessage *)data;
/* Check port number from message and retrieve private data */
if (msg->portNumber >= serial->num_ports) {
- dbg("%s - Unexpected port number %d",
- __func__, msg->portNumber);
+ dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n",
+ __func__, msg->portNumber);
goto exit;
}
port = serial->port[msg->portNumber];
@@ -656,7 +660,7 @@ static void usa49_instat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+ dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
exit: ;
}
@@ -676,8 +680,8 @@ static void usa49_indat_callback(struct urb *urb)
endpoint = usb_pipeendpoint(urb->pipe);
if (status) {
- dbg("%s - nonzero status: %x on endpoint %d.", __func__,
- status, endpoint);
+ dev_dbg(&urb->dev->dev, "%s - nonzero status: %x on endpoint %d.\n",
+ __func__, status, endpoint);
return;
}
@@ -710,7 +714,7 @@ static void usa49_indat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+ dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
}
static void usa49wg_indat_callback(struct urb *urb)
@@ -725,7 +729,7 @@ static void usa49wg_indat_callback(struct urb *urb)
serial = urb->context;
if (status) {
- dbg("%s - nonzero status: %x", __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
return;
}
@@ -738,7 +742,7 @@ static void usa49wg_indat_callback(struct urb *urb)
/* Check port number from message*/
if (data[i] >= serial->num_ports) {
- dbg("%s - Unexpected port number %d",
+ dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n",
__func__, data[i]);
return;
}
@@ -778,7 +782,7 @@ static void usa49wg_indat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+ dev_dbg(&urb->dev->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
}
/* not used, usa-49 doesn't have per-port control endpoints */
@@ -799,7 +803,7 @@ static void usa90_indat_callback(struct urb *urb)
endpoint = usb_pipeendpoint(urb->pipe);
if (status) {
- dbg("%s - nonzero status: %x on endpoint %d.",
+ dev_dbg(&urb->dev->dev, "%s - nonzero status: %x on endpoint %d.\n",
__func__, status, endpoint);
return;
}
@@ -828,7 +832,7 @@ static void usa90_indat_callback(struct urb *urb)
err);
} else {
/* some bytes had errors, every byte has status */
- dbg("%s - RX error!!!!", __func__);
+ dev_dbg(&port->dev, "%s - RX error!!!!\n", __func__);
for (i = 0; i + 1 < urb->actual_length; i += 2) {
int stat = data[i], flag = 0;
if (stat & RXERROR_OVERRUN)
@@ -850,7 +854,7 @@ static void usa90_indat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+ dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
}
@@ -868,11 +872,11 @@ static void usa90_instat_callback(struct urb *urb)
serial = urb->context;
if (status) {
- dbg("%s - nonzero status: %x", __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
return;
}
if (urb->actual_length < 14) {
- dbg("%s - %d byte report??", __func__, urb->actual_length);
+ dev_dbg(&urb->dev->dev, "%s - %d byte report??\n", __func__, urb->actual_length);
goto exit;
}
@@ -900,7 +904,7 @@ static void usa90_instat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+ dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
exit:
;
}
@@ -914,7 +918,7 @@ static void usa90_outcont_callback(struct urb *urb)
p_priv = usb_get_serial_port_data(port);
if (p_priv->resend_cont) {
- dbg("%s - sending setup", __func__);
+ dev_dbg(&urb->dev->dev, "%s - sending setup\n", __func__);
keyspan_usa90_send_setup(port->serial, port,
p_priv->resend_cont - 1);
}
@@ -935,13 +939,13 @@ static void usa67_instat_callback(struct urb *urb)
serial = urb->context;
if (status) {
- dbg("%s - nonzero status: %x", __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero status: %x\n", __func__, status);
return;
}
if (urb->actual_length !=
sizeof(struct keyspan_usa67_portStatusMessage)) {
- dbg("%s - bad length %d", __func__, urb->actual_length);
+ dev_dbg(&urb->dev->dev, "%s - bad length %d\n", __func__, urb->actual_length);
return;
}
@@ -951,7 +955,7 @@ static void usa67_instat_callback(struct urb *urb)
/* Check port number from message and retrieve private data */
if (msg->port >= serial->num_ports) {
- dbg("%s - Unexpected port number %d", __func__, msg->port);
+ dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n", __func__, msg->port);
return;
}
@@ -973,7 +977,7 @@ static void usa67_instat_callback(struct urb *urb)
/* Resubmit urb so we continue receiving */
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - resubmit read urb failed. (%d)", __func__, err);
+ dev_dbg(&port->dev, "%s - resubmit read urb failed. (%d)\n", __func__, err);
}
static void usa67_glocont_callback(struct urb *urb)
@@ -989,7 +993,7 @@ static void usa67_glocont_callback(struct urb *urb)
p_priv = usb_get_serial_port_data(port);
if (p_priv->resend_cont) {
- dbg("%s - sending setup", __func__);
+ dev_dbg(&port->dev, "%s - sending setup\n", __func__);
keyspan_usa67_send_setup(serial, port,
p_priv->resend_cont - 1);
break;
@@ -1068,8 +1072,7 @@ static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port)
usb_clear_halt(urb->dev, urb->pipe);
err = usb_submit_urb(urb, GFP_KERNEL);
if (err != 0)
- dbg("%s - submit urb %d failed (%d)",
- __func__, i, err);
+ dev_dbg(&port->dev, "%s - submit urb %d failed (%d)\n", __func__, i, err);
}
/* Reset low level data toggle on out endpoints */
@@ -1092,7 +1095,7 @@ static int keyspan_open(struct tty_struct *tty, struct usb_serial_port *port)
baud_rate = tty_get_baud_rate(tty);
/* If no match or invalid, leave as default */
if (baud_rate >= 0
- && d_details->calculate_baud_rate(baud_rate, d_details->baudclk,
+ && d_details->calculate_baud_rate(port, baud_rate, d_details->baudclk,
NULL, NULL, NULL, device_port) == KEYSPAN_BAUD_RATE_OK) {
p_priv->baud = baud_rate;
}
@@ -1142,7 +1145,7 @@ static void keyspan_close(struct usb_serial_port *port)
}
/*while (p_priv->outcont_urb->status == -EINPROGRESS) {
- dbg("%s - urb in progress", __func__);
+ dev_dbg(&port->dev, "%s - urb in progress\n", __func__);
}*/
p_priv->out_flip = 0;
@@ -1162,18 +1165,15 @@ static void keyspan_close(struct usb_serial_port *port)
/* download the firmware to a pre-renumeration device */
static int keyspan_fake_startup(struct usb_serial *serial)
{
- int response;
- const struct ihex_binrec *record;
- char *fw_name;
- const struct firmware *fw;
+ char *fw_name;
- dbg("Keyspan startup version %04x product %04x",
- le16_to_cpu(serial->dev->descriptor.bcdDevice),
- le16_to_cpu(serial->dev->descriptor.idProduct));
+ dev_dbg(&serial->dev->dev, "Keyspan startup version %04x product %04x\n",
+ le16_to_cpu(serial->dev->descriptor.bcdDevice),
+ le16_to_cpu(serial->dev->descriptor.idProduct));
if ((le16_to_cpu(serial->dev->descriptor.bcdDevice) & 0x8000)
!= 0x8000) {
- dbg("Firmware already loaded. Quitting.");
+ dev_dbg(&serial->dev->dev, "Firmware already loaded. Quitting.\n");
return 1;
}
@@ -1233,34 +1233,16 @@ static int keyspan_fake_startup(struct usb_serial *serial)
return 1;
}
- if (request_ihex_firmware(&fw, fw_name, &serial->dev->dev)) {
- dev_err(&serial->dev->dev, "Required keyspan firmware image (%s) unavailable.\n", fw_name);
- return 1;
- }
-
- dbg("Uploading Keyspan %s firmware.", fw_name);
+ dev_dbg(&serial->dev->dev, "Uploading Keyspan %s firmware.\n", fw_name);
- /* download the firmware image */
- response = ezusb_set_reset(serial, 1);
-
- record = (const struct ihex_binrec *)fw->data;
-
- while (record) {
- response = ezusb_writememory(serial, be32_to_cpu(record->addr),
- (unsigned char *)record->data,
- be16_to_cpu(record->len), 0xa0);
- if (response < 0) {
- dev_err(&serial->dev->dev, "ezusb_writememory failed for Keyspan firmware (%d %04X %p %d)\n",
- response, be32_to_cpu(record->addr),
- record->data, be16_to_cpu(record->len));
- break;
- }
- record = ihex_next_binrec(record);
+ if (ezusb_fx1_ihex_firmware_download(serial->dev, fw_name) < 0) {
+ dev_err(&serial->dev->dev, "failed to load firmware \"%s\"\n",
+ fw_name);
+ return -ENOENT;
}
- release_firmware(fw);
- /* bring device out of reset. Renumeration will occur in a
- moment and the new device will bind to the real driver */
- response = ezusb_set_reset(serial, 0);
+
+ /* after downloading firmware Renumeration will occur in a
+ moment and the new device will bind to the real driver */
/* we don't want this device to have a driver assigned to it. */
return 1;
@@ -1296,10 +1278,10 @@ static struct urb *keyspan_setup_urb(struct usb_serial *serial, int endpoint,
if (endpoint == -1)
return NULL; /* endpoint not needed */
- dbg("%s - alloc for endpoint %d.", __func__, endpoint);
+ dev_dbg(&serial->interface->dev, "%s - alloc for endpoint %d.\n", __func__, endpoint);
urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
if (urb == NULL) {
- dbg("%s - alloc for endpoint %d failed.", __func__, endpoint);
+ dev_dbg(&serial->interface->dev, "%s - alloc for endpoint %d failed.\n", __func__, endpoint);
return NULL;
}
@@ -1332,7 +1314,7 @@ static struct urb *keyspan_setup_urb(struct usb_serial *serial, int endpoint,
return NULL;
}
- dbg("%s - using urb %p for %s endpoint %x",
+ dev_dbg(&serial->interface->dev, "%s - using urb %p for %s endpoint %x\n",
__func__, urb, ep_type_name, endpoint);
return urb;
}
@@ -1464,14 +1446,15 @@ static void keyspan_setup_urbs(struct usb_serial *serial)
}
/* usa19 function doesn't require prescaler */
-static int keyspan_usa19_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
+static int keyspan_usa19_calc_baud(struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk, u8 *rate_hi,
u8 *rate_low, u8 *prescaler, int portnum)
{
u32 b16, /* baud rate times 16 (actual rate used internally) */
div, /* divisor */
cnt; /* inverse of divisor (programmed into 8051) */
- dbg("%s - %d.", __func__, baud_rate);
+ dev_dbg(&port->dev, "%s - %d.\n", __func__, baud_rate);
/* prevent divide by zero... */
b16 = baud_rate * 16L;
@@ -1498,19 +1481,20 @@ static int keyspan_usa19_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
if (rate_hi)
*rate_hi = (u8) ((cnt >> 8) & 0xff);
if (rate_low && rate_hi)
- dbg("%s - %d %02x %02x.",
+ dev_dbg(&port->dev, "%s - %d %02x %02x.\n",
__func__, baud_rate, *rate_hi, *rate_low);
return KEYSPAN_BAUD_RATE_OK;
}
/* usa19hs function doesn't require prescaler */
-static int keyspan_usa19hs_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
- u8 *rate_low, u8 *prescaler, int portnum)
+static int keyspan_usa19hs_calc_baud(struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk, u8 *rate_hi,
+ u8 *rate_low, u8 *prescaler, int portnum)
{
u32 b16, /* baud rate times 16 (actual rate used internally) */
div; /* divisor */
- dbg("%s - %d.", __func__, baud_rate);
+ dev_dbg(&port->dev, "%s - %d.\n", __func__, baud_rate);
/* prevent divide by zero... */
b16 = baud_rate * 16L;
@@ -1533,13 +1517,14 @@ static int keyspan_usa19hs_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
*rate_hi = (u8) ((div >> 8) & 0xff);
if (rate_low && rate_hi)
- dbg("%s - %d %02x %02x.",
+ dev_dbg(&port->dev, "%s - %d %02x %02x.\n",
__func__, baud_rate, *rate_hi, *rate_low);
return KEYSPAN_BAUD_RATE_OK;
}
-static int keyspan_usa19w_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
+static int keyspan_usa19w_calc_baud(struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk, u8 *rate_hi,
u8 *rate_low, u8 *prescaler, int portnum)
{
u32 b16, /* baud rate times 16 (actual rate used internally) */
@@ -1551,7 +1536,7 @@ static int keyspan_usa19w_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
u8 best_prescaler;
int i;
- dbg("%s - %d.", __func__, baud_rate);
+ dev_dbg(&port->dev, "%s - %d.\n", __func__, baud_rate);
/* prevent divide by zero */
b16 = baud_rate * 16L;
@@ -1596,20 +1581,21 @@ static int keyspan_usa19w_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
*rate_hi = (u8) ((div >> 8) & 0xff);
if (prescaler) {
*prescaler = best_prescaler;
- /* dbg("%s - %d %d", __func__, *prescaler, div); */
+ /* dev_dbg(&port->dev, "%s - %d %d\n", __func__, *prescaler, div); */
}
return KEYSPAN_BAUD_RATE_OK;
}
/* USA-28 supports different maximum baud rates on each port */
-static int keyspan_usa28_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
- u8 *rate_low, u8 *prescaler, int portnum)
+static int keyspan_usa28_calc_baud(struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk, u8 *rate_hi,
+ u8 *rate_low, u8 *prescaler, int portnum)
{
u32 b16, /* baud rate times 16 (actual rate used internally) */
div, /* divisor */
cnt; /* inverse of divisor (programmed into 8051) */
- dbg("%s - %d.", __func__, baud_rate);
+ dev_dbg(&port->dev, "%s - %d.\n", __func__, baud_rate);
/* prevent divide by zero */
b16 = baud_rate * 16L;
@@ -1642,7 +1628,7 @@ static int keyspan_usa28_calc_baud(u32 baud_rate, u32 baudclk, u8 *rate_hi,
*rate_low = (u8) (cnt & 0xff);
if (rate_hi)
*rate_hi = (u8) ((cnt >> 8) & 0xff);
- dbg("%s - %d OK.", __func__, baud_rate);
+ dev_dbg(&port->dev, "%s - %d OK.\n", __func__, baud_rate);
return KEYSPAN_BAUD_RATE_OK;
}
@@ -1658,7 +1644,7 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
struct urb *this_urb;
int device_port, err;
- dbg("%s reset=%d", __func__, reset_port);
+ dev_dbg(&port->dev, "%s reset=%d\n", __func__, reset_port);
s_priv = usb_get_serial_data(serial);
p_priv = usb_get_serial_port_data(port);
@@ -1668,11 +1654,11 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
outcont_urb = d_details->outcont_endpoints[port->number];
this_urb = p_priv->outcont_urb;
- dbg("%s - endpoint %d", __func__, usb_pipeendpoint(this_urb->pipe));
+ dev_dbg(&port->dev, "%s - endpoint %d\n", __func__, usb_pipeendpoint(this_urb->pipe));
/* Make sure we have an urb then send the message */
if (this_urb == NULL) {
- dbg("%s - oops no urb.", __func__);
+ dev_dbg(&port->dev, "%s - oops no urb.\n", __func__);
return -1;
}
@@ -1681,7 +1667,7 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
if ((reset_port + 1) > p_priv->resend_cont)
p_priv->resend_cont = reset_port + 1;
if (this_urb->status == -EINPROGRESS) {
- /* dbg("%s - already writing", __func__); */
+ /* dev_dbg(&port->dev, "%s - already writing\n", __func__); */
mdelay(5);
return -1;
}
@@ -1692,11 +1678,11 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
if (p_priv->old_baud != p_priv->baud) {
p_priv->old_baud = p_priv->baud;
msg.setClocking = 0xff;
- if (d_details->calculate_baud_rate
- (p_priv->baud, d_details->baudclk, &msg.baudHi,
- &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE) {
- dbg("%s - Invalid baud rate %d requested, using 9600.",
- __func__, p_priv->baud);
+ if (d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
+ &msg.baudHi, &msg.baudLo, &msg.prescaler,
+ device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+ dev_dbg(&port->dev, "%s - Invalid baud rate %d requested, using 9600.\n",
+ __func__, p_priv->baud);
msg.baudLo = 0;
msg.baudHi = 125; /* Values for 9600 baud */
msg.prescaler = 10;
@@ -1790,12 +1776,12 @@ static int keyspan_usa26_send_setup(struct usb_serial *serial,
err = usb_submit_urb(this_urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - usb_submit_urb(setup) failed (%d)", __func__, err);
+ dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed (%d)\n", __func__, err);
#if 0
else {
- dbg("%s - usb_submit_urb(%d) OK %d bytes (end %d)", __func__
- outcont_urb, this_urb->transfer_buffer_length,
- usb_pipeendpoint(this_urb->pipe));
+ dev_dbg(&port->dev, "%s - usb_submit_urb(%d) OK %d bytes (end %d)\n", __func__
+ outcont_urb, this_urb->transfer_buffer_length,
+ usb_pipeendpoint(this_urb->pipe));
}
#endif
@@ -1821,7 +1807,7 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial,
/* only do something if we have a bulk out endpoint */
this_urb = p_priv->outcont_urb;
if (this_urb == NULL) {
- dbg("%s - oops no urb.", __func__);
+ dev_dbg(&port->dev, "%s - oops no urb.\n", __func__);
return -1;
}
@@ -1830,7 +1816,7 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial,
if ((reset_port + 1) > p_priv->resend_cont)
p_priv->resend_cont = reset_port + 1;
if (this_urb->status == -EINPROGRESS) {
- dbg("%s already writing", __func__);
+ dev_dbg(&port->dev, "%s already writing\n", __func__);
mdelay(5);
return -1;
}
@@ -1838,9 +1824,10 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial,
memset(&msg, 0, sizeof(struct keyspan_usa28_portControlMessage));
msg.setBaudRate = 1;
- if (d_details->calculate_baud_rate(p_priv->baud, d_details->baudclk,
- &msg.baudHi, &msg.baudLo, NULL, device_port) == KEYSPAN_INVALID_BAUD_RATE) {
- dbg("%s - Invalid baud rate requested %d.",
+ if (d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
+ &msg.baudHi, &msg.baudLo, NULL,
+ device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+ dev_dbg(&port->dev, "%s - Invalid baud rate requested %d.\n",
__func__, p_priv->baud);
msg.baudLo = 0xff;
msg.baudHi = 0xb2; /* Values for 9600 baud */
@@ -1915,10 +1902,10 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial,
err = usb_submit_urb(this_urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - usb_submit_urb(setup) failed", __func__);
+ dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed\n", __func__);
#if 0
else {
- dbg("%s - usb_submit_urb(setup) OK %d bytes", __func__,
+ dev_dbg(&port->dev, "%s - usb_submit_urb(setup) OK %d bytes\n", __func__,
this_urb->transfer_buffer_length);
}
#endif
@@ -1949,13 +1936,13 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
/* Make sure we have an urb then send the message */
if (this_urb == NULL) {
- dbg("%s - oops no urb for port %d.", __func__, port->number);
+ dev_dbg(&port->dev, "%s - oops no urb for port %d.\n", __func__, port->number);
return -1;
}
- dbg("%s - endpoint %d port %d (%d)",
- __func__, usb_pipeendpoint(this_urb->pipe),
- port->number, device_port);
+ dev_dbg(&port->dev, "%s - endpoint %d port %d (%d)\n",
+ __func__, usb_pipeendpoint(this_urb->pipe),
+ port->number, device_port);
/* Save reset port val for resend.
Don't overwrite resend for open/close condition. */
@@ -1963,7 +1950,7 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
p_priv->resend_cont = reset_port + 1;
if (this_urb->status == -EINPROGRESS) {
- /* dbg("%s - already writing", __func__); */
+ /* dev_dbg(&port->dev, "%s - already writing\n", __func__); */
mdelay(5);
return -1;
}
@@ -1977,11 +1964,11 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
if (p_priv->old_baud != p_priv->baud) {
p_priv->old_baud = p_priv->baud;
msg.setClocking = 0xff;
- if (d_details->calculate_baud_rate
- (p_priv->baud, d_details->baudclk, &msg.baudHi,
- &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE) {
- dbg("%s - Invalid baud rate %d requested, using 9600.",
- __func__, p_priv->baud);
+ if (d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
+ &msg.baudHi, &msg.baudLo, &msg.prescaler,
+ device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+ dev_dbg(&port->dev, "%s - Invalid baud rate %d requested, using 9600.\n",
+ __func__, p_priv->baud);
msg.baudLo = 0;
msg.baudHi = 125; /* Values for 9600 baud */
msg.prescaler = 10;
@@ -2100,12 +2087,12 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
}
err = usb_submit_urb(this_urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - usb_submit_urb(setup) failed (%d)", __func__, err);
+ dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed (%d)\n", __func__, err);
#if 0
else {
- dbg("%s - usb_submit_urb(%d) OK %d bytes (end %d)", __func__,
- outcont_urb, this_urb->transfer_buffer_length,
- usb_pipeendpoint(this_urb->pipe));
+ dev_dbg(&port->dev, "%s - usb_submit_urb(%d) OK %d bytes (end %d)\n", __func__,
+ outcont_urb, this_urb->transfer_buffer_length,
+ usb_pipeendpoint(this_urb->pipe));
}
#endif
@@ -2131,7 +2118,7 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial,
/* only do something if we have a bulk out endpoint */
this_urb = p_priv->outcont_urb;
if (this_urb == NULL) {
- dbg("%s - oops no urb.", __func__);
+ dev_dbg(&port->dev, "%s - oops no urb.\n", __func__);
return -1;
}
@@ -2140,7 +2127,7 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial,
if ((reset_port + 1) > p_priv->resend_cont)
p_priv->resend_cont = reset_port + 1;
if (this_urb->status == -EINPROGRESS) {
- dbg("%s already writing", __func__);
+ dev_dbg(&port->dev, "%s already writing\n", __func__);
mdelay(5);
return -1;
}
@@ -2151,13 +2138,12 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial,
if (p_priv->old_baud != p_priv->baud) {
p_priv->old_baud = p_priv->baud;
msg.setClocking = 0x01;
- if (d_details->calculate_baud_rate
- (p_priv->baud, d_details->baudclk, &msg.baudHi,
- &msg.baudLo, &prescaler, 0) == KEYSPAN_INVALID_BAUD_RATE) {
- dbg("%s - Invalid baud rate %d requested, using 9600.",
- __func__, p_priv->baud);
+ if (d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
+ &msg.baudHi, &msg.baudLo, &prescaler, 0) == KEYSPAN_INVALID_BAUD_RATE) {
+ dev_dbg(&port->dev, "%s - Invalid baud rate %d requested, using 9600.\n",
+ __func__, p_priv->baud);
p_priv->baud = 9600;
- d_details->calculate_baud_rate(p_priv->baud, d_details->baudclk,
+ d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
&msg.baudHi, &msg.baudLo, &prescaler, 0);
}
msg.setRxMode = 1;
@@ -2239,7 +2225,7 @@ static int keyspan_usa90_send_setup(struct usb_serial *serial,
err = usb_submit_urb(this_urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - usb_submit_urb(setup) failed (%d)", __func__, err);
+ dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed (%d)\n", __func__, err);
return 0;
}
@@ -2265,7 +2251,7 @@ static int keyspan_usa67_send_setup(struct usb_serial *serial,
/* Make sure we have an urb then send the message */
if (this_urb == NULL) {
- dbg("%s - oops no urb for port %d.", __func__,
+ dev_dbg(&port->dev, "%s - oops no urb for port %d.\n", __func__,
port->number);
return -1;
}
@@ -2275,7 +2261,7 @@ static int keyspan_usa67_send_setup(struct usb_serial *serial,
if ((reset_port + 1) > p_priv->resend_cont)
p_priv->resend_cont = reset_port + 1;
if (this_urb->status == -EINPROGRESS) {
- /* dbg("%s - already writing", __func__); */
+ /* dev_dbg(&port->dev, "%s - already writing\n", __func__); */
mdelay(5);
return -1;
}
@@ -2288,11 +2274,11 @@ static int keyspan_usa67_send_setup(struct usb_serial *serial,
if (p_priv->old_baud != p_priv->baud) {
p_priv->old_baud = p_priv->baud;
msg.setClocking = 0xff;
- if (d_details->calculate_baud_rate
- (p_priv->baud, d_details->baudclk, &msg.baudHi,
- &msg.baudLo, &msg.prescaler, device_port) == KEYSPAN_INVALID_BAUD_RATE) {
- dbg("%s - Invalid baud rate %d requested, using 9600.",
- __func__, p_priv->baud);
+ if (d_details->calculate_baud_rate(port, p_priv->baud, d_details->baudclk,
+ &msg.baudHi, &msg.baudLo, &msg.prescaler,
+ device_port) == KEYSPAN_INVALID_BAUD_RATE) {
+ dev_dbg(&port->dev, "%s - Invalid baud rate %d requested, using 9600.\n",
+ __func__, p_priv->baud);
msg.baudLo = 0;
msg.baudHi = 125; /* Values for 9600 baud */
msg.prescaler = 10;
@@ -2383,8 +2369,7 @@ static int keyspan_usa67_send_setup(struct usb_serial *serial,
err = usb_submit_urb(this_urb, GFP_ATOMIC);
if (err != 0)
- dbg("%s - usb_submit_urb(setup) failed (%d)", __func__,
- err);
+ dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed (%d)\n", __func__, err);
return 0;
}
@@ -2440,8 +2425,7 @@ static int keyspan_startup(struct usb_serial *serial)
/* Setup private data for serial driver */
s_priv = kzalloc(sizeof(struct keyspan_serial_private), GFP_KERNEL);
if (!s_priv) {
- dbg("%s - kmalloc for keyspan_serial_private failed.",
- __func__);
+ dev_dbg(&serial->dev->dev, "%s - kmalloc for keyspan_serial_private failed.\n", __func__);
return -ENOMEM;
}
@@ -2454,7 +2438,7 @@ static int keyspan_startup(struct usb_serial *serial)
p_priv = kzalloc(sizeof(struct keyspan_port_private),
GFP_KERNEL);
if (!p_priv) {
- dbg("%s - kmalloc for keyspan_port_private (%d) failed!.", __func__, i);
+ dev_dbg(&port->dev, "%s - kmalloc for keyspan_port_private (%d) failed!.\n", __func__, i);
return 1;
}
p_priv->device_details = d_details;
@@ -2466,14 +2450,12 @@ static int keyspan_startup(struct usb_serial *serial)
if (s_priv->instat_urb != NULL) {
err = usb_submit_urb(s_priv->instat_urb, GFP_KERNEL);
if (err != 0)
- dbg("%s - submit instat urb failed %d", __func__,
- err);
+ dev_dbg(&serial->dev->dev, "%s - submit instat urb failed %d\n", __func__, err);
}
if (s_priv->indat_urb != NULL) {
err = usb_submit_urb(s_priv->indat_urb, GFP_KERNEL);
if (err != 0)
- dbg("%s - submit indat urb failed %d", __func__,
- err);
+ dev_dbg(&serial->dev->dev, "%s - submit indat urb failed %d\n", __func__, err);
}
return 0;
@@ -2527,10 +2509,8 @@ static void keyspan_release(struct usb_serial *serial)
s_priv = usb_get_serial_data(serial);
- /* dbg("Freeing serial->private."); */
kfree(s_priv);
- /* dbg("Freeing port->private."); */
/* Now free per port private data */
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
@@ -2554,7 +2534,3 @@ MODULE_FIRMWARE("keyspan/usa18x.fw");
MODULE_FIRMWARE("keyspan/usa19w.fw");
MODULE_FIRMWARE("keyspan/usa49w.fw");
MODULE_FIRMWARE("keyspan/usa49wlc.fw");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
-
diff --git a/drivers/usb/serial/keyspan.h b/drivers/usb/serial/keyspan.h
index fe1c5d91692c..0a8a40b5711e 100644
--- a/drivers/usb/serial/keyspan.h
+++ b/drivers/usb/serial/keyspan.h
@@ -64,19 +64,23 @@ static int keyspan_tiocmset (struct tty_struct *tty,
unsigned int clear);
static int keyspan_fake_startup (struct usb_serial *serial);
-static int keyspan_usa19_calc_baud (u32 baud_rate, u32 baudclk,
+static int keyspan_usa19_calc_baud (struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk,
u8 *rate_hi, u8 *rate_low,
u8 *prescaler, int portnum);
-static int keyspan_usa19w_calc_baud (u32 baud_rate, u32 baudclk,
+static int keyspan_usa19w_calc_baud (struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk,
u8 *rate_hi, u8 *rate_low,
u8 *prescaler, int portnum);
-static int keyspan_usa28_calc_baud (u32 baud_rate, u32 baudclk,
+static int keyspan_usa28_calc_baud (struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk,
u8 *rate_hi, u8 *rate_low,
u8 *prescaler, int portnum);
-static int keyspan_usa19hs_calc_baud (u32 baud_rate, u32 baudclk,
+static int keyspan_usa19hs_calc_baud (struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk,
u8 *rate_hi, u8 *rate_low,
u8 *prescaler, int portnum);
@@ -188,8 +192,9 @@ struct keyspan_device_details {
/* Endpoint used for global control functions */
int glocont_endpoint;
- int (*calculate_baud_rate) (u32 baud_rate, u32 baudclk,
- u8 *rate_hi, u8 *rate_low, u8 *prescaler, int portnum);
+ int (*calculate_baud_rate) (struct usb_serial_port *port,
+ u32 baud_rate, u32 baudclk,
+ u8 *rate_hi, u8 *rate_low, u8 *prescaler, int portnum);
u32 baudclk;
};
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index dcada8615fcf..ca43ecb4a2bd 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -25,13 +25,10 @@
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
-#include <linux/firmware.h>
-#include <linux/ihex.h>
#include <linux/uaccess.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
-
-static bool debug;
+#include <linux/usb/ezusb.h>
/* make a simple define to handle if we are compiling keyspan_pda or xircom support */
#if defined(CONFIG_USB_SERIAL_KEYSPAN_PDA) || defined(CONFIG_USB_SERIAL_KEYSPAN_PDA_MODULE)
@@ -137,8 +134,8 @@ static void keyspan_pda_request_unthrottle(struct work_struct *work)
0,
2000);
if (result < 0)
- dbg("%s - error %d from usb_control_msg",
- __func__, result);
+ dev_dbg(&serial->dev->dev, "%s - error %d from usb_control_msg\n",
+ __func__, result);
}
@@ -160,12 +157,10 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d",
- __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n", __func__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d",
- __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
goto exit;
}
@@ -183,7 +178,7 @@ static void keyspan_pda_rx_interrupt(struct urb *urb)
break;
case 1:
/* status interrupt */
- dbg(" rx int, d1=%d, d2=%d", data[1], data[2]);
+ dev_dbg(&port->dev, "rx int, d1=%d, d2=%d\n", data[1], data[2]);
switch (data[1]) {
case 1: /* modemline change */
break;
@@ -229,7 +224,7 @@ static void keyspan_pda_rx_unthrottle(struct tty_struct *tty)
/* just restart the receive interrupt URB */
if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL))
- dbg(" usb_submit_urb(read urb) failed");
+ dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n");
}
@@ -308,8 +303,8 @@ static void keyspan_pda_break_ctl(struct tty_struct *tty, int break_state)
USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT,
value, 0, NULL, 0, 2000);
if (result < 0)
- dbg("%s - error %d from usb_control_msg",
- __func__, result);
+ dev_dbg(&port->dev, "%s - error %d from usb_control_msg\n",
+ __func__, result);
/* there is something funky about this.. the TCSBRK that 'cu' performs
ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4
seconds apart, but it feels like the break sent isn't as long as it
@@ -347,7 +342,7 @@ static void keyspan_pda_set_termios(struct tty_struct *tty,
speed = keyspan_pda_setbaud(serial, speed);
if (speed == 0) {
- dbg("can't handle requested baud rate");
+ dev_dbg(&port->dev, "can't handle requested baud rate\n");
/* It hasn't changed so.. */
speed = tty_termios_baud_rate(old_termios);
}
@@ -459,7 +454,7 @@ static int keyspan_pda_write(struct tty_struct *tty,
Block if we can't write anything at all, otherwise write as much as
we can. */
if (count == 0) {
- dbg(" write request of 0 bytes");
+ dev_dbg(&port->dev, "write request of 0 bytes\n");
return 0;
}
@@ -505,16 +500,16 @@ static int keyspan_pda_write(struct tty_struct *tty,
1,
2000);
if (rc > 0) {
- dbg(" roomquery says %d", *room);
+ dev_dbg(&port->dev, "roomquery says %d\n", *room);
priv->tx_room = *room;
}
kfree(room);
if (rc < 0) {
- dbg(" roomquery failed");
+ dev_dbg(&port->dev, "roomquery failed\n");
goto exit;
}
if (rc == 0) {
- dbg(" roomquery returned 0 bytes");
+ dev_dbg(&port->dev, "roomquery returned 0 bytes\n");
rc = -EIO; /* device didn't return any data */
goto exit;
}
@@ -536,7 +531,7 @@ static int keyspan_pda_write(struct tty_struct *tty,
rc = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (rc) {
- dbg(" usb_submit_urb(write bulk) failed");
+ dev_dbg(&port->dev, "usb_submit_urb(write bulk) failed\n");
goto exit;
}
} else {
@@ -639,11 +634,11 @@ static int keyspan_pda_open(struct tty_struct *tty,
1,
2000);
if (rc < 0) {
- dbg("%s - roomquery failed", __func__);
+ dev_dbg(&port->dev, "%s - roomquery failed\n", __func__);
goto error;
}
if (rc == 0) {
- dbg("%s - roomquery returned 0 bytes", __func__);
+ dev_dbg(&port->dev, "%s - roomquery returned 0 bytes\n", __func__);
rc = -EIO;
goto error;
}
@@ -654,7 +649,7 @@ static int keyspan_pda_open(struct tty_struct *tty,
/*Start reading from the device*/
rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (rc) {
- dbg("%s - usb_submit_urb(read int) failed", __func__);
+ dev_dbg(&port->dev, "%s - usb_submit_urb(read int) failed\n", __func__);
goto error;
}
error:
@@ -678,11 +673,9 @@ static int keyspan_pda_fake_startup(struct usb_serial *serial)
{
int response;
const char *fw_name;
- const struct ihex_binrec *record;
- const struct firmware *fw;
/* download the firmware here ... */
- response = ezusb_set_reset(serial, 1);
+ response = ezusb_fx1_set_reset(serial->dev, 1);
if (0) { ; }
#ifdef KEYSPAN
@@ -699,30 +692,15 @@ static int keyspan_pda_fake_startup(struct usb_serial *serial)
__func__);
return -ENODEV;
}
- if (request_ihex_firmware(&fw, fw_name, &serial->dev->dev)) {
+
+ if (ezusb_fx1_ihex_firmware_download(serial->dev, fw_name) < 0) {
dev_err(&serial->dev->dev, "failed to load firmware \"%s\"\n",
fw_name);
return -ENOENT;
}
- record = (const struct ihex_binrec *)fw->data;
-
- while (record) {
- response = ezusb_writememory(serial, be32_to_cpu(record->addr),
- (unsigned char *)record->data,
- be16_to_cpu(record->len), 0xa0);
- if (response < 0) {
- dev_err(&serial->dev->dev, "ezusb_writememory failed "
- "for Keyspan PDA firmware (%d %04X %p %d)\n",
- response, be32_to_cpu(record->addr),
- record->data, be16_to_cpu(record->len));
- break;
- }
- record = ihex_next_binrec(record);
- }
- release_firmware(fw);
- /* bring device out of reset. Renumeration will occur in a moment
- and the new device will bind to the real driver */
- response = ezusb_set_reset(serial, 0);
+
+ /* after downloading firmware Renumeration will occur in a
+ moment and the new device will bind to the real driver */
/* we want this device to fail to have a driver assigned to it. */
return 1;
@@ -828,6 +806,3 @@ module_usb_serial_driver(serial_drivers, id_table_combined);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/kl5kusb105.c b/drivers/usb/serial/kl5kusb105.c
index def9ad258715..3f6d7376c02d 100644
--- a/drivers/usb/serial/kl5kusb105.c
+++ b/drivers/usb/serial/kl5kusb105.c
@@ -49,8 +49,6 @@
#include <linux/usb/serial.h>
#include "kl5kusb105.h"
-static bool debug;
-
/*
* Version Information
*/
@@ -239,7 +237,9 @@ static int klsi_105_startup(struct usb_serial *serial)
priv = kmalloc(sizeof(struct klsi_105_private),
GFP_KERNEL);
if (!priv) {
- dbg("%skmalloc for klsi_105_private failed.", __func__);
+ dev_dbg(&serial->interface->dev,
+ "%s - kmalloc for klsi_105_private failed.\n",
+ __func__);
i--;
goto err_cleanup;
}
@@ -344,14 +344,14 @@ static int klsi_105_open(struct tty_struct *tty, struct usb_serial_port *port)
dev_err(&port->dev, "Enabling read failed (error = %d)\n", rc);
retval = rc;
} else
- dbg("%s - enabled reading", __func__);
+ dev_dbg(&port->dev, "%s - enabled reading\n", __func__);
rc = klsi_105_get_line_state(port, &line_state);
if (rc >= 0) {
spin_lock_irqsave(&priv->lock, flags);
priv->line_state = line_state;
spin_unlock_irqrestore(&priv->lock, flags);
- dbg("%s - read line state 0x%lx", __func__, line_state);
+ dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
retval = 0;
} else
retval = rc;
@@ -421,7 +421,7 @@ static void klsi_105_process_read_urb(struct urb *urb)
return;
if (urb->actual_length <= KLSI_HDR_LEN) {
- dbg("%s - malformed packet", __func__);
+ dev_dbg(&port->dev, "%s - malformed packet\n", __func__);
return;
}
@@ -431,7 +431,7 @@ static void klsi_105_process_read_urb(struct urb *urb)
len = get_unaligned_le16(data);
if (len > urb->actual_length - KLSI_HDR_LEN) {
- dbg("%s - packet length mismatch", __func__);
+ dev_dbg(&port->dev, "%s - packet length mismatch\n", __func__);
len = urb->actual_length - KLSI_HDR_LEN;
}
@@ -445,6 +445,7 @@ static void klsi_105_set_termios(struct tty_struct *tty,
struct ktermios *old_termios)
{
struct klsi_105_private *priv = usb_get_serial_port_data(port);
+ struct device *dev = &port->dev;
unsigned int iflag = tty->termios.c_iflag;
unsigned int old_iflag = old_termios->c_iflag;
unsigned int cflag = tty->termios.c_cflag;
@@ -455,8 +456,7 @@ static void klsi_105_set_termios(struct tty_struct *tty,
cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
if (!cfg) {
- dev_err(&port->dev, "%s - out of memory for config buffer.\n",
- __func__);
+ dev_err(dev, "%s - out of memory for config buffer.\n", __func__);
return;
}
@@ -471,7 +471,7 @@ static void klsi_105_set_termios(struct tty_struct *tty,
if ((cflag & CBAUD) != (old_cflag & CBAUD)) {
/* reassert DTR and (maybe) RTS on transition from B0 */
if ((old_cflag & CBAUD) == B0) {
- dbg("%s: baud was B0", __func__);
+ dev_dbg(dev, "%s: baud was B0\n", __func__);
#if 0
priv->control_state |= TIOCM_DTR;
/* don't set RTS if using hardware flow control */
@@ -509,14 +509,13 @@ static void klsi_105_set_termios(struct tty_struct *tty,
priv->cfg.baudrate = kl5kusb105a_sio_b115200;
break;
default:
- dbg("KLSI USB->Serial converter:"
- " unsupported baudrate request, using default of 9600");
- priv->cfg.baudrate = kl5kusb105a_sio_b9600;
+ dev_dbg(dev, "KLSI USB->Serial converter: unsupported baudrate request, using default of 9600");
+ priv->cfg.baudrate = kl5kusb105a_sio_b9600;
baud = 9600;
break;
}
if ((cflag & CBAUD) == B0) {
- dbg("%s: baud is B0", __func__);
+ dev_dbg(dev, "%s: baud is B0\n", __func__);
/* Drop RTS and DTR */
/* maybe this should be simulated by sending read
* disable and read enable messages?
@@ -533,11 +532,11 @@ static void klsi_105_set_termios(struct tty_struct *tty,
/* set the number of data bits */
switch (cflag & CSIZE) {
case CS5:
- dbg("%s - 5 bits/byte not supported", __func__);
+ dev_dbg(dev, "%s - 5 bits/byte not supported\n", __func__);
spin_unlock_irqrestore(&priv->lock, flags);
goto err;
case CS6:
- dbg("%s - 6 bits/byte not supported", __func__);
+ dev_dbg(dev, "%s - 6 bits/byte not supported\n", __func__);
spin_unlock_irqrestore(&priv->lock, flags);
goto err;
case CS7:
@@ -547,8 +546,7 @@ static void klsi_105_set_termios(struct tty_struct *tty,
priv->cfg.databits = kl5kusb105a_dtb_8;
break;
default:
- dev_err(&port->dev,
- "CSIZE was not CS5-CS8, using default of 8\n");
+ dev_err(dev, "CSIZE was not CS5-CS8, using default of 8\n");
priv->cfg.databits = kl5kusb105a_dtb_8;
break;
}
@@ -616,7 +614,7 @@ static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
(struct mct_u232_private *)port->private;
unsigned char lcr = priv->last_lcr;
- dbg("%sstate=%d", __func__, break_state);
+ dev_dbg(&port->dev, "%s - state=%d\n", __func__, break_state);
/* LOCKING */
if (break_state)
@@ -645,7 +643,7 @@ static int klsi_105_tiocmget(struct tty_struct *tty)
spin_lock_irqsave(&priv->lock, flags);
priv->line_state = line_state;
spin_unlock_irqrestore(&priv->lock, flags);
- dbg("%s - read line state 0x%lx", __func__, line_state);
+ dev_dbg(&port->dev, "%s - read line state 0x%lx\n", __func__, line_state);
return (int)line_state;
}
@@ -681,6 +679,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "enable extensive debugging messages");
diff --git a/drivers/usb/serial/kobil_sct.c b/drivers/usb/serial/kobil_sct.c
index bf5c74965d34..5c4d2fbd4e11 100644
--- a/drivers/usb/serial/kobil_sct.c
+++ b/drivers/usb/serial/kobil_sct.c
@@ -38,8 +38,6 @@
#include <linux/ioctl.h>
#include "kobil_sct.h"
-static bool debug;
-
/* Version Information */
#define DRIVER_VERSION "21/05/2004"
#define DRIVER_AUTHOR "KOBIL Systems GmbH - http://www.kobil.com"
@@ -139,17 +137,16 @@ static int kobil_startup(struct usb_serial *serial)
switch (priv->device_type) {
case KOBIL_ADAPTER_B_PRODUCT_ID:
- printk(KERN_DEBUG "KOBIL B1 PRO / KAAN PRO detected\n");
+ dev_dbg(&serial->dev->dev, "KOBIL B1 PRO / KAAN PRO detected\n");
break;
case KOBIL_ADAPTER_K_PRODUCT_ID:
- printk(KERN_DEBUG
- "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n");
+ dev_dbg(&serial->dev->dev, "KOBIL KAAN Standard Plus / SecOVID Reader Plus detected\n");
break;
case KOBIL_USBTWIN_PRODUCT_ID:
- printk(KERN_DEBUG "KOBIL USBTWIN detected\n");
+ dev_dbg(&serial->dev->dev, "KOBIL USBTWIN detected\n");
break;
case KOBIL_KAAN_SIM_PRODUCT_ID:
- printk(KERN_DEBUG "KOBIL KAAN SIM detected\n");
+ dev_dbg(&serial->dev->dev, "KOBIL KAAN SIM detected\n");
break;
}
usb_set_serial_port_data(serial->port[0], priv);
@@ -164,13 +161,15 @@ static int kobil_startup(struct usb_serial *serial)
for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
endpoint = &altsetting->endpoint[i];
if (usb_endpoint_is_int_out(&endpoint->desc)) {
- dbg("%s Found interrupt out endpoint. Address: %d",
+ dev_dbg(&serial->dev->dev,
+ "%s Found interrupt out endpoint. Address: %d\n",
__func__, endpoint->desc.bEndpointAddress);
priv->write_int_endpoint_address =
endpoint->desc.bEndpointAddress;
}
if (usb_endpoint_is_int_in(&endpoint->desc)) {
- dbg("%s Found interrupt in endpoint. Address: %d",
+ dev_dbg(&serial->dev->dev,
+ "%s Found interrupt in endpoint. Address: %d\n",
__func__, endpoint->desc.bEndpointAddress);
priv->read_int_endpoint_address =
endpoint->desc.bEndpointAddress;
@@ -200,6 +199,7 @@ static void kobil_init_termios(struct tty_struct *tty)
static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
{
+ struct device *dev = &port->dev;
int result = 0;
struct kobil_private *priv;
unsigned char *transfer_buffer;
@@ -215,12 +215,10 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
/* allocate write_urb */
if (!port->write_urb) {
- dbg("%s - port %d Allocating port->write_urb",
- __func__, port->number);
+ dev_dbg(dev, "%s - Allocating port->write_urb\n", __func__);
port->write_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!port->write_urb) {
- dbg("%s - port %d usb_alloc_urb failed",
- __func__, port->number);
+ dev_dbg(dev, "%s - usb_alloc_urb failed\n", __func__);
kfree(transfer_buffer);
return -ENOMEM;
}
@@ -247,10 +245,9 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
transfer_buffer_length,
KOBIL_TIMEOUT
);
- dbg("%s - port %d Send get_HW_version URB returns: %i",
- __func__, port->number, result);
- dbg("Harware version: %i.%i.%i",
- transfer_buffer[0], transfer_buffer[1], transfer_buffer[2]);
+ dev_dbg(dev, "%s - Send get_HW_version URB returns: %i\n", __func__, result);
+ dev_dbg(dev, "Harware version: %i.%i.%i\n", transfer_buffer[0],
+ transfer_buffer[1], transfer_buffer[2]);
/* get firmware version */
result = usb_control_msg(port->serial->dev,
@@ -263,10 +260,9 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
transfer_buffer_length,
KOBIL_TIMEOUT
);
- dbg("%s - port %d Send get_FW_version URB returns: %i",
- __func__, port->number, result);
- dbg("Firmware version: %i.%i.%i",
- transfer_buffer[0], transfer_buffer[1], transfer_buffer[2]);
+ dev_dbg(dev, "%s - Send get_FW_version URB returns: %i\n", __func__, result);
+ dev_dbg(dev, "Firmware version: %i.%i.%i\n", transfer_buffer[0],
+ transfer_buffer[1], transfer_buffer[2]);
if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
@@ -282,8 +278,7 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
0,
KOBIL_TIMEOUT
);
- dbg("%s - port %d Send set_baudrate URB returns: %i",
- __func__, port->number, result);
+ dev_dbg(dev, "%s - Send set_baudrate URB returns: %i\n", __func__, result);
/* reset all queues */
result = usb_control_msg(port->serial->dev,
@@ -296,16 +291,14 @@ static int kobil_open(struct tty_struct *tty, struct usb_serial_port *port)
0,
KOBIL_TIMEOUT
);
- dbg("%s - port %d Send reset_all_queues URB returns: %i",
- __func__, port->number, result);
+ dev_dbg(dev, "%s - Send reset_all_queues URB returns: %i\n", __func__, result);
}
if (priv->device_type == KOBIL_USBTWIN_PRODUCT_ID ||
priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID ||
priv->device_type == KOBIL_KAAN_SIM_PRODUCT_ID) {
/* start reading (Adapter B 'cause PNP string) */
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
- dbg("%s - port %d Send read URB returns: %i",
- __func__, port->number, result);
+ dev_dbg(dev, "%s - Send read URB returns: %i\n", __func__, result);
}
kfree(transfer_buffer);
@@ -333,11 +326,9 @@ static void kobil_read_int_callback(struct urb *urb)
struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
int status = urb->status;
-/* char *dbg_data; */
if (status) {
- dbg("%s - port %d Read int status not zero: %d",
- __func__, port->number, status);
+ dev_dbg(&port->dev, "%s - Read int status not zero: %d\n", __func__, status);
return;
}
@@ -346,6 +337,8 @@ static void kobil_read_int_callback(struct urb *urb)
/* BEGIN DEBUG */
/*
+ char *dbg_data;
+
dbg_data = kzalloc((3 * purb->actual_length + 10)
* sizeof(char), GFP_KERNEL);
if (! dbg_data) {
@@ -354,7 +347,7 @@ static void kobil_read_int_callback(struct urb *urb)
for (i = 0; i < purb->actual_length; i++) {
sprintf(dbg_data +3*i, "%02X ", data[i]);
}
- dbg(" <-- %s", dbg_data);
+ dev_dbg(&port->dev, " <-- %s\n", dbg_data);
kfree(dbg_data);
*/
/* END DEBUG */
@@ -365,8 +358,7 @@ static void kobil_read_int_callback(struct urb *urb)
tty_kref_put(tty);
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
- dbg("%s - port %d Send read URB returns: %i",
- __func__, port->number, result);
+ dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
}
@@ -384,22 +376,20 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
struct kobil_private *priv;
if (count == 0) {
- dbg("%s - port %d write request of 0 bytes",
- __func__, port->number);
+ dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
return 0;
}
priv = usb_get_serial_port_data(port);
if (count > (KOBIL_BUF_LENGTH - priv->filled)) {
- dbg("%s - port %d Error: write request bigger than buffer size", __func__, port->number);
+ dev_dbg(&port->dev, "%s - Error: write request bigger than buffer size\n", __func__);
return -ENOMEM;
}
/* Copy data to buffer */
memcpy(priv->buf + priv->filled, buf, count);
- usb_serial_debug_data(debug, &port->dev, __func__, count,
- priv->buf + priv->filled);
+ usb_serial_debug_data(&port->dev, __func__, count, priv->buf + priv->filled);
priv->filled = priv->filled + count;
/* only send complete block. TWIN, KAAN SIM and adapter K
@@ -432,8 +422,7 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
priv->cur_pos = priv->cur_pos + length;
result = usb_submit_urb(port->write_urb, GFP_NOIO);
- dbg("%s - port %d Send write URB returns: %i",
- __func__, port->number, result);
+ dev_dbg(&port->dev, "%s - Send write URB returns: %i\n", __func__, result);
todo = priv->filled - priv->cur_pos;
if (todo > 0)
@@ -448,8 +437,7 @@ static int kobil_write(struct tty_struct *tty, struct usb_serial_port *port,
priv->device_type == KOBIL_ADAPTER_K_PRODUCT_ID) {
result = usb_submit_urb(port->interrupt_in_urb,
GFP_NOIO);
- dbg("%s - port %d Send read URB returns: %i",
- __func__, port->number, result);
+ dev_dbg(&port->dev, "%s - Send read URB returns: %i\n", __func__, result);
}
}
return count;
@@ -493,8 +481,8 @@ static int kobil_tiocmget(struct tty_struct *tty)
transfer_buffer_length,
KOBIL_TIMEOUT);
- dbg("%s - port %d Send get_status_line_state URB returns: %i. Statusline: %02x",
- __func__, port->number, result, transfer_buffer[0]);
+ dev_dbg(&port->dev, "%s - Send get_status_line_state URB returns: %i. Statusline: %02x\n",
+ __func__, result, transfer_buffer[0]);
result = 0;
if ((transfer_buffer[0] & SUSBCR_GSL_DSR) != 0)
@@ -507,6 +495,7 @@ static int kobil_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
+ struct device *dev = &port->dev;
struct kobil_private *priv;
int result;
int dtr = 0;
@@ -538,11 +527,9 @@ static int kobil_tiocmset(struct tty_struct *tty,
if (priv->device_type == KOBIL_ADAPTER_B_PRODUCT_ID) {
if (dtr != 0)
- dbg("%s - port %d Setting DTR",
- __func__, port->number);
+ dev_dbg(dev, "%s - Setting DTR\n", __func__);
else
- dbg("%s - port %d Clearing DTR",
- __func__, port->number);
+ dev_dbg(dev, "%s - Clearing DTR\n", __func__);
result = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
SUSBCRequest_SetStatusLinesOrQueues,
@@ -554,11 +541,9 @@ static int kobil_tiocmset(struct tty_struct *tty,
KOBIL_TIMEOUT);
} else {
if (rts != 0)
- dbg("%s - port %d Setting RTS",
- __func__, port->number);
+ dev_dbg(dev, "%s - Setting RTS\n", __func__);
else
- dbg("%s - port %d Clearing RTS",
- __func__, port->number);
+ dev_dbg(dev, "%s - Clearing RTS\n", __func__);
result = usb_control_msg(port->serial->dev,
usb_rcvctrlpipe(port->serial->dev, 0),
SUSBCRequest_SetStatusLinesOrQueues,
@@ -569,8 +554,7 @@ static int kobil_tiocmset(struct tty_struct *tty,
0,
KOBIL_TIMEOUT);
}
- dbg("%s - port %d Send set_status_line URB returns: %i",
- __func__, port->number, result);
+ dev_dbg(dev, "%s - Send set_status_line URB returns: %i\n", __func__, result);
kfree(transfer_buffer);
return (result < 0) ? result : 0;
}
@@ -658,7 +642,8 @@ static int kobil_ioctl(struct tty_struct *tty,
KOBIL_TIMEOUT
);
- dbg("%s - port %d Send reset_all_queues (FLUSH) URB returns: %i", __func__, port->number, result);
+ dev_dbg(&port->dev,
+ "%s - Send reset_all_queues (FLUSH) URB returns: %i", __func__, result);
kfree(transfer_buffer);
return (result < 0) ? -EIO: 0;
default:
@@ -671,6 +656,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/mct_u232.c b/drivers/usb/serial/mct_u232.c
index df98cffdba65..f3947712e137 100644
--- a/drivers/usb/serial/mct_u232.c
+++ b/drivers/usb/serial/mct_u232.c
@@ -45,8 +45,6 @@
#define DRIVER_AUTHOR "Wolfgang Grandegger <wolfgang@ces.ch>"
#define DRIVER_DESC "Magic Control Technology USB-RS232 converter driver"
-static bool debug;
-
/*
* Function prototypes
*/
@@ -214,7 +212,7 @@ static int mct_u232_set_baud_rate(struct tty_struct *tty,
value, rc);
else
tty_encode_baud_rate(tty, speed, speed);
- dbg("set_baud_rate: value: 0x%x, divisor: 0x%x", value, divisor);
+ dev_dbg(&port->dev, "set_baud_rate: value: 0x%x, divisor: 0x%x\n", value, divisor);
/* Mimic the MCT-supplied Windows driver (version 1.21P.0104), which
always sends two extra USB 'device request' messages after the
@@ -247,8 +245,8 @@ static int mct_u232_set_baud_rate(struct tty_struct *tty,
if (port && C_CRTSCTS(tty))
cts_enable_byte = 1;
- dbg("set_baud_rate: send second control message, data = %02X",
- cts_enable_byte);
+ dev_dbg(&port->dev, "set_baud_rate: send second control message, data = %02X\n",
+ cts_enable_byte);
buf[0] = cts_enable_byte;
rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
MCT_U232_SET_CTS_REQUEST,
@@ -263,7 +261,8 @@ static int mct_u232_set_baud_rate(struct tty_struct *tty,
return rc;
} /* mct_u232_set_baud_rate */
-static int mct_u232_set_line_ctrl(struct usb_serial *serial, unsigned char lcr)
+static int mct_u232_set_line_ctrl(struct usb_serial_port *port,
+ unsigned char lcr)
{
int rc;
unsigned char *buf;
@@ -273,20 +272,19 @@ static int mct_u232_set_line_ctrl(struct usb_serial *serial, unsigned char lcr)
return -ENOMEM;
buf[0] = lcr;
- rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
MCT_U232_SET_LINE_CTRL_REQUEST,
MCT_U232_SET_REQUEST_TYPE,
0, 0, buf, MCT_U232_SET_LINE_CTRL_SIZE,
WDR_TIMEOUT);
if (rc < 0)
- dev_err(&serial->dev->dev,
- "Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc);
- dbg("set_line_ctrl: 0x%x", lcr);
+ dev_err(&port->dev, "Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc);
+ dev_dbg(&port->dev, "set_line_ctrl: 0x%x\n", lcr);
kfree(buf);
return rc;
} /* mct_u232_set_line_ctrl */
-static int mct_u232_set_modem_ctrl(struct usb_serial *serial,
+static int mct_u232_set_modem_ctrl(struct usb_serial_port *port,
unsigned int control_state)
{
int rc;
@@ -304,25 +302,24 @@ static int mct_u232_set_modem_ctrl(struct usb_serial *serial,
mcr |= MCT_U232_MCR_RTS;
buf[0] = mcr;
- rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ rc = usb_control_msg(port->serial->dev, usb_sndctrlpipe(port->serial->dev, 0),
MCT_U232_SET_MODEM_CTRL_REQUEST,
MCT_U232_SET_REQUEST_TYPE,
0, 0, buf, MCT_U232_SET_MODEM_CTRL_SIZE,
WDR_TIMEOUT);
kfree(buf);
- dbg("set_modem_ctrl: state=0x%x ==> mcr=0x%x", control_state, mcr);
+ dev_dbg(&port->dev, "set_modem_ctrl: state=0x%x ==> mcr=0x%x\n", control_state, mcr);
if (rc < 0) {
- dev_err(&serial->dev->dev,
- "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);
+ dev_err(&port->dev, "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);
return rc;
}
return 0;
} /* mct_u232_set_modem_ctrl */
-static int mct_u232_get_modem_stat(struct usb_serial *serial,
- unsigned char *msr)
+static int mct_u232_get_modem_stat(struct usb_serial_port *port,
+ unsigned char *msr)
{
int rc;
unsigned char *buf;
@@ -332,19 +329,18 @@ static int mct_u232_get_modem_stat(struct usb_serial *serial,
*msr = 0;
return -ENOMEM;
}
- rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ rc = usb_control_msg(port->serial->dev, usb_rcvctrlpipe(port->serial->dev, 0),
MCT_U232_GET_MODEM_STAT_REQUEST,
MCT_U232_GET_REQUEST_TYPE,
0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE,
WDR_TIMEOUT);
if (rc < 0) {
- dev_err(&serial->dev->dev,
- "Get MODEM STATus failed (error = %d)\n", rc);
+ dev_err(&port->dev, "Get MODEM STATus failed (error = %d)\n", rc);
*msr = 0;
} else {
*msr = buf[0];
}
- dbg("get_modem_stat: 0x%x", *msr);
+ dev_dbg(&port->dev, "get_modem_stat: 0x%x\n", *msr);
kfree(buf);
return rc;
} /* mct_u232_get_modem_stat */
@@ -363,8 +359,8 @@ static void mct_u232_msr_to_icount(struct async_icount *icount,
icount->dcd++;
} /* mct_u232_msr_to_icount */
-static void mct_u232_msr_to_state(unsigned int *control_state,
- unsigned char msr)
+static void mct_u232_msr_to_state(struct usb_serial_port *port,
+ unsigned int *control_state, unsigned char msr)
{
/* Translate Control Line states */
if (msr & MCT_U232_MSR_DSR)
@@ -383,7 +379,7 @@ static void mct_u232_msr_to_state(unsigned int *control_state,
*control_state |= TIOCM_CD;
else
*control_state &= ~TIOCM_CD;
- dbg("msr_to_state: msr=0x%x ==> state=0x%x", msr, *control_state);
+ dev_dbg(&port->dev, "msr_to_state: msr=0x%x ==> state=0x%x\n", msr, *control_state);
} /* mct_u232_msr_to_state */
/*
@@ -465,14 +461,14 @@ static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port)
control_state = priv->control_state;
last_lcr = priv->last_lcr;
spin_unlock_irqrestore(&priv->lock, flags);
- mct_u232_set_modem_ctrl(serial, control_state);
- mct_u232_set_line_ctrl(serial, last_lcr);
+ mct_u232_set_modem_ctrl(port, control_state);
+ mct_u232_set_line_ctrl(port, last_lcr);
/* Read modem status and update control state */
- mct_u232_get_modem_stat(serial, &last_msr);
+ mct_u232_get_modem_stat(port, &last_msr);
spin_lock_irqsave(&priv->lock, flags);
priv->last_msr = last_msr;
- mct_u232_msr_to_state(&priv->control_state, priv->last_msr);
+ mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
spin_unlock_irqrestore(&priv->lock, flags);
retval = usb_submit_urb(port->read_urb, GFP_KERNEL);
@@ -512,7 +508,7 @@ static void mct_u232_dtr_rts(struct usb_serial_port *port, int on)
priv->control_state &= ~(TIOCM_DTR | TIOCM_RTS);
control_state = priv->control_state;
spin_unlock_irq(&priv->lock);
- mct_u232_set_modem_ctrl(port->serial, control_state);
+ mct_u232_set_modem_ctrl(port, control_state);
}
mutex_unlock(&port->serial->disc_mutex);
}
@@ -532,7 +528,6 @@ static void mct_u232_read_int_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
- struct usb_serial *serial = port->serial;
struct tty_struct *tty;
unsigned char *data = urb->transfer_buffer;
int retval;
@@ -547,22 +542,16 @@ static void mct_u232_read_int_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d",
- __func__, status);
+ dev_dbg(&port->dev, "%s - urb shutting down with status: %d\n",
+ __func__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d",
- __func__, status);
+ dev_dbg(&port->dev, "%s - nonzero urb status received: %d\n",
+ __func__, status);
goto exit;
}
- if (!serial) {
- dbg("%s - bad serial pointer, exiting", __func__);
- return;
- }
-
- usb_serial_debug_data(debug, &port->dev, __func__,
- urb->actual_length, data);
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
/*
* Work-a-round: handle the 'usual' bulk-in pipe here
@@ -588,7 +577,7 @@ static void mct_u232_read_int_callback(struct urb *urb)
priv->last_msr = data[MCT_U232_MSR_INDEX];
/* Record Control Line states */
- mct_u232_msr_to_state(&priv->control_state, priv->last_msr);
+ mct_u232_msr_to_state(port, &priv->control_state, priv->last_msr);
mct_u232_msr_to_icount(&priv->icount, priv->last_msr);
@@ -656,18 +645,18 @@ static void mct_u232_set_termios(struct tty_struct *tty,
/* reassert DTR and RTS on transition from B0 */
if ((old_cflag & CBAUD) == B0) {
- dbg("%s: baud was B0", __func__);
+ dev_dbg(&port->dev, "%s: baud was B0\n", __func__);
control_state |= TIOCM_DTR | TIOCM_RTS;
- mct_u232_set_modem_ctrl(serial, control_state);
+ mct_u232_set_modem_ctrl(port, control_state);
}
mct_u232_set_baud_rate(tty, serial, port, tty_get_baud_rate(tty));
if ((cflag & CBAUD) == B0) {
- dbg("%s: baud is B0", __func__);
+ dev_dbg(&port->dev, "%s: baud is B0\n", __func__);
/* Drop RTS and DTR */
control_state &= ~(TIOCM_DTR | TIOCM_RTS);
- mct_u232_set_modem_ctrl(serial, control_state);
+ mct_u232_set_modem_ctrl(port, control_state);
}
/*
@@ -704,7 +693,7 @@ static void mct_u232_set_termios(struct tty_struct *tty,
last_lcr |= (cflag & CSTOPB) ?
MCT_U232_STOP_BITS_2 : MCT_U232_STOP_BITS_1;
- mct_u232_set_line_ctrl(serial, last_lcr);
+ mct_u232_set_line_ctrl(port, last_lcr);
/* save off the modified port settings */
spin_lock_irqsave(&priv->lock, flags);
@@ -716,7 +705,6 @@ static void mct_u232_set_termios(struct tty_struct *tty,
static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
- struct usb_serial *serial = port->serial;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
unsigned char lcr;
unsigned long flags;
@@ -728,7 +716,7 @@ static void mct_u232_break_ctl(struct tty_struct *tty, int break_state)
lcr |= MCT_U232_SET_BREAK;
spin_unlock_irqrestore(&priv->lock, flags);
- mct_u232_set_line_ctrl(serial, lcr);
+ mct_u232_set_line_ctrl(port, lcr);
} /* mct_u232_break_ctl */
@@ -750,7 +738,6 @@ static int mct_u232_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
- struct usb_serial *serial = port->serial;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
unsigned int control_state;
unsigned long flags;
@@ -769,7 +756,7 @@ static int mct_u232_tiocmset(struct tty_struct *tty,
priv->control_state = control_state;
spin_unlock_irqrestore(&priv->lock, flags);
- return mct_u232_set_modem_ctrl(serial, control_state);
+ return mct_u232_set_modem_ctrl(port, control_state);
}
static void mct_u232_throttle(struct tty_struct *tty)
@@ -784,7 +771,7 @@ static void mct_u232_throttle(struct tty_struct *tty)
priv->control_state &= ~TIOCM_RTS;
control_state = priv->control_state;
spin_unlock_irq(&priv->lock);
- (void) mct_u232_set_modem_ctrl(port->serial, control_state);
+ mct_u232_set_modem_ctrl(port, control_state);
} else {
spin_unlock_irq(&priv->lock);
}
@@ -802,7 +789,7 @@ static void mct_u232_unthrottle(struct tty_struct *tty)
priv->control_state |= TIOCM_RTS;
control_state = priv->control_state;
spin_unlock_irq(&priv->lock);
- (void) mct_u232_set_modem_ctrl(port->serial, control_state);
+ mct_u232_set_modem_ctrl(port, control_state);
} else {
spin_unlock_irq(&priv->lock);
}
@@ -817,13 +804,13 @@ static int mct_u232_ioctl(struct tty_struct *tty,
struct async_icount cnow, cprev;
unsigned long flags;
- dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+ dev_dbg(&port->dev, "%s - cmd = 0x%x\n", __func__, cmd);
switch (cmd) {
case TIOCMIWAIT:
- dbg("%s (%d) TIOCMIWAIT", __func__, port->number);
+ dev_dbg(&port->dev, "%s TIOCMIWAIT", __func__);
spin_lock_irqsave(&mct_u232_port->lock, flags);
cprev = mct_u232_port->icount;
@@ -879,8 +866,8 @@ static int mct_u232_get_icount(struct tty_struct *tty,
spin_unlock_irqrestore(&mct_u232_port->lock, flags);
- dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d",
- __func__, port->number, icount->rx, icount->tx);
+ dev_dbg(&port->dev, "%s TIOCGICOUNT RX=%d, TX=%d\n",
+ __func__, icount->rx, icount->tx);
return 0;
}
@@ -889,6 +876,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/metro-usb.c b/drivers/usb/serial/metro-usb.c
index 2b0627b5fe2c..0b257ddffbdb 100644
--- a/drivers/usb/serial/metro-usb.c
+++ b/drivers/usb/serial/metro-usb.c
@@ -52,9 +52,6 @@ static struct usb_device_id id_table[] = {
};
MODULE_DEVICE_TABLE(usb, id_table);
-/* Input parameter constants. */
-static bool debug;
-
/* UNI-Directional mode commands for device configure */
#define UNI_CMD_OPEN 0x80
#define UNI_CMD_CLOSE 0xFF
@@ -436,7 +433,3 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Philip Nicastro");
MODULE_AUTHOR("Aleksey Babahin <tamerlan311@gmail.com>");
MODULE_DESCRIPTION(DRIVER_DESC);
-
-/* Module input parameters */
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Print debug info (bool 1=on, 0=off)");
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index 012f67b2e4cc..1bf1ad066666 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -71,8 +71,6 @@ struct moschip_port {
struct urb *write_urb_pool[NUM_URBS];
};
-static bool debug;
-
static struct usb_serial_driver moschip7720_2port_driver;
#define USB_VENDOR_ID_MOSCHIP 0x9710
@@ -281,16 +279,19 @@ static void send_deferred_urbs(unsigned long _mos_parport)
int ret_val;
unsigned long flags;
struct mos7715_parport *mos_parport = (void *)_mos_parport;
- struct urbtracker *urbtrack;
+ struct urbtracker *urbtrack, *tmp;
struct list_head *cursor, *next;
+ struct device *dev;
/* if release function ran, game over */
if (unlikely(mos_parport->serial == NULL))
return;
+ dev = &mos_parport->serial->dev->dev;
+
/* try again to get the mutex */
if (!mutex_trylock(&mos_parport->serial->disc_mutex)) {
- dbg("%s: rescheduling tasklet", __func__);
+ dev_dbg(dev, "%s: rescheduling tasklet\n", __func__);
tasklet_schedule(&mos_parport->urb_tasklet);
return;
}
@@ -305,20 +306,19 @@ static void send_deferred_urbs(unsigned long _mos_parport)
if (list_empty(&mos_parport->deferred_urbs)) {
spin_unlock_irqrestore(&mos_parport->listlock, flags);
mutex_unlock(&mos_parport->serial->disc_mutex);
- dbg("%s: deferred_urbs list empty", __func__);
+ dev_dbg(dev, "%s: deferred_urbs list empty\n", __func__);
return;
}
/* move contents of deferred_urbs list to active_urbs list and submit */
list_for_each_safe(cursor, next, &mos_parport->deferred_urbs)
list_move_tail(cursor, &mos_parport->active_urbs);
- list_for_each_entry(urbtrack, &mos_parport->active_urbs,
+ list_for_each_entry_safe(urbtrack, tmp, &mos_parport->active_urbs,
urblist_entry) {
ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC);
- dbg("%s: urb submitted", __func__);
+ dev_dbg(dev, "%s: urb submitted\n", __func__);
if (ret_val) {
- dev_err(&mos_parport->serial->dev->dev,
- "usb_submit_urb() failed: %d", ret_val);
+ dev_err(dev, "usb_submit_urb() failed: %d\n", ret_val);
list_del(&urbtrack->urblist_entry);
kref_put(&urbtrack->ref_count, destroy_urbtracker);
}
@@ -334,7 +334,7 @@ static void async_complete(struct urb *urb)
int status = urb->status;
if (unlikely(status))
- dbg("%s - nonzero urb status received: %d", __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n", __func__, status);
/* remove the urbtracker from the active_urbs list */
spin_lock(&urbtrack->mos_parport->listlock);
@@ -389,7 +389,7 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport,
&mos_parport->deferred_urbs);
spin_unlock_irqrestore(&mos_parport->listlock, flags);
tasklet_schedule(&mos_parport->urb_tasklet);
- dbg("tasklet scheduled");
+ dev_dbg(&usbdev->dev, "tasklet scheduled");
return 0;
}
@@ -690,7 +690,7 @@ static int mos7715_parport_init(struct usb_serial *serial)
/* allocate and initialize parallel port control struct */
mos_parport = kzalloc(sizeof(struct mos7715_parport), GFP_KERNEL);
if (mos_parport == NULL) {
- dbg("mos7715_parport_init: kzalloc failed");
+ dev_dbg(&serial->dev->dev, "%s: kzalloc failed\n", __func__);
return -ENOMEM;
}
mos_parport->msg_pending = false;
@@ -743,6 +743,7 @@ static void mos7720_interrupt_callback(struct urb *urb)
int result;
int length;
int status = urb->status;
+ struct device *dev = &urb->dev->dev;
__u8 *data;
__u8 sp1;
__u8 sp2;
@@ -755,12 +756,10 @@ static void mos7720_interrupt_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __func__,
- status);
+ dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d", __func__,
- status);
+ dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
goto exit;
}
@@ -777,7 +776,7 @@ static void mos7720_interrupt_callback(struct urb *urb)
* oneukum 2007-03-14 */
if (unlikely(length != 4)) {
- dbg("Wrong data !!!");
+ dev_dbg(dev, "Wrong data !!!\n");
return;
}
@@ -786,31 +785,29 @@ static void mos7720_interrupt_callback(struct urb *urb)
if ((sp1 | sp2) & 0x01) {
/* No Interrupt Pending in both the ports */
- dbg("No Interrupt !!!");
+ dev_dbg(dev, "No Interrupt !!!\n");
} else {
switch (sp1 & 0x0f) {
case SERIAL_IIR_RLS:
- dbg("Serial Port 1: Receiver status error or address "
- "bit detected in 9-bit mode\n");
+ dev_dbg(dev, "Serial Port 1: Receiver status error or address bit detected in 9-bit mode\n");
break;
case SERIAL_IIR_CTI:
- dbg("Serial Port 1: Receiver time out");
+ dev_dbg(dev, "Serial Port 1: Receiver time out\n");
break;
case SERIAL_IIR_MS:
- /* dbg("Serial Port 1: Modem status change"); */
+ /* dev_dbg(dev, "Serial Port 1: Modem status change\n"); */
break;
}
switch (sp2 & 0x0f) {
case SERIAL_IIR_RLS:
- dbg("Serial Port 2: Receiver status error or address "
- "bit detected in 9-bit mode");
+ dev_dbg(dev, "Serial Port 2: Receiver status error or address bit detected in 9-bit mode\n");
break;
case SERIAL_IIR_CTI:
- dbg("Serial Port 2: Receiver time out");
+ dev_dbg(dev, "Serial Port 2: Receiver time out\n");
break;
case SERIAL_IIR_MS:
- /* dbg("Serial Port 2: Modem status change"); */
+ /* dev_dbg(dev, "Serial Port 2: Modem status change\n"); */
break;
}
}
@@ -818,9 +815,7 @@ static void mos7720_interrupt_callback(struct urb *urb)
exit:
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
- dev_err(&urb->dev->dev,
- "%s - Error %d submitting control urb\n",
- __func__, result);
+ dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result);
}
/*
@@ -833,6 +828,7 @@ static void mos7715_interrupt_callback(struct urb *urb)
int result;
int length;
int status = urb->status;
+ struct device *dev = &urb->dev->dev;
__u8 *data;
__u8 iir;
@@ -845,12 +841,10 @@ static void mos7715_interrupt_callback(struct urb *urb)
case -ESHUTDOWN:
case -ENODEV:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __func__,
- status);
+ dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d", __func__,
- status);
+ dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
goto exit;
}
@@ -864,7 +858,7 @@ static void mos7715_interrupt_callback(struct urb *urb)
* Byte 4: FIFO status for both */
if (unlikely(length != 4)) {
- dbg("Wrong data !!!");
+ dev_dbg(dev, "Wrong data !!!\n");
return;
}
@@ -872,14 +866,13 @@ static void mos7715_interrupt_callback(struct urb *urb)
if (!(iir & 0x01)) { /* serial port interrupt pending */
switch (iir & 0x0f) {
case SERIAL_IIR_RLS:
- dbg("Serial Port: Receiver status error or address "
- "bit detected in 9-bit mode\n");
+ dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n\n");
break;
case SERIAL_IIR_CTI:
- dbg("Serial Port: Receiver time out");
+ dev_dbg(dev, "Serial Port: Receiver time out\n");
break;
case SERIAL_IIR_MS:
- /* dbg("Serial Port: Modem status change"); */
+ /* dev_dbg(dev, "Serial Port: Modem status change\n"); */
break;
}
}
@@ -897,9 +890,7 @@ static void mos7715_interrupt_callback(struct urb *urb)
exit:
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
- dev_err(&urb->dev->dev,
- "%s - Error %d submitting control urb\n",
- __func__, result);
+ dev_err(dev, "%s - Error %d submitting control urb\n", __func__, result);
}
/*
@@ -916,13 +907,13 @@ static void mos7720_bulk_in_callback(struct urb *urb)
int status = urb->status;
if (status) {
- dbg("nonzero read bulk status received: %d", status);
+ dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status);
return;
}
port = urb->context;
- dbg("Entering...%s", __func__);
+ dev_dbg(&port->dev, "Entering...%s\n", __func__);
data = urb->transfer_buffer;
@@ -936,8 +927,7 @@ static void mos7720_bulk_in_callback(struct urb *urb)
if (port->read_urb->status != -EINPROGRESS) {
retval = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (retval)
- dbg("usb_submit_urb(read bulk) failed, retval = %d",
- retval);
+ dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, retval = %d\n", retval);
}
}
@@ -953,13 +943,13 @@ static void mos7720_bulk_out_data_callback(struct urb *urb)
int status = urb->status;
if (status) {
- dbg("nonzero write bulk status received:%d", status);
+ dev_dbg(&urb->dev->dev, "nonzero write bulk status received:%d\n", status);
return;
}
mos7720_port = urb->context;
if (!mos7720_port) {
- dbg("NULL mos7720_port pointer");
+ dev_dbg(&urb->dev->dev, "NULL mos7720_port pointer\n");
return ;
}
@@ -1061,9 +1051,7 @@ static int mos7720_open(struct tty_struct *tty, struct usb_serial_port *port)
port_number = port->number - port->serial->minor;
read_mos_reg(serial, port_number, LSR, &data);
- dbg("SS::%p LSR:%x", mos7720_port, data);
-
- dbg("Check:Sending Command ..........");
+ dev_dbg(&port->dev, "SS::%p LSR:%x\n", mos7720_port, data);
write_mos_reg(serial, dummy, SP1_REG, 0x02);
write_mos_reg(serial, dummy, SP2_REG, 0x02);
@@ -1122,20 +1110,16 @@ static int mos7720_chars_in_buffer(struct tty_struct *tty)
int chars = 0;
struct moschip_port *mos7720_port;
- dbg("%s:entering ...........", __func__);
-
mos7720_port = usb_get_serial_port_data(port);
- if (mos7720_port == NULL) {
- dbg("%s:leaving ...........", __func__);
+ if (mos7720_port == NULL)
return 0;
- }
for (i = 0; i < NUM_URBS; ++i) {
if (mos7720_port->write_urb_pool[i] &&
mos7720_port->write_urb_pool[i]->status == -EINPROGRESS)
chars += URB_TRANSFER_BUFFER_SIZE;
}
- dbg("%s - returns %d", __func__, chars);
+ dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
return chars;
}
@@ -1145,8 +1129,6 @@ static void mos7720_close(struct usb_serial_port *port)
struct moschip_port *mos7720_port;
int j;
- dbg("mos7720_close:entering...");
-
serial = port->serial;
mos7720_port = usb_get_serial_port_data(port);
@@ -1166,9 +1148,7 @@ static void mos7720_close(struct usb_serial_port *port)
/* While closing port, shutdown all bulk read, write *
* and interrupt read if they exists, otherwise nop */
- dbg("Shutdown bulk write");
usb_kill_urb(port->write_urb);
- dbg("Shutdown bulk read");
usb_kill_urb(port->read_urb);
mutex_lock(&serial->disc_mutex);
@@ -1182,8 +1162,6 @@ static void mos7720_close(struct usb_serial_port *port)
}
mutex_unlock(&serial->disc_mutex);
mos7720_port->open = 0;
-
- dbg("Leaving %s", __func__);
}
static void mos7720_break(struct tty_struct *tty, int break_state)
@@ -1193,8 +1171,6 @@ static void mos7720_break(struct tty_struct *tty, int break_state)
struct usb_serial *serial;
struct moschip_port *mos7720_port;
- dbg("Entering %s", __func__);
-
serial = port->serial;
mos7720_port = usb_get_serial_port_data(port);
@@ -1225,13 +1201,9 @@ static int mos7720_write_room(struct tty_struct *tty)
int room = 0;
int i;
- dbg("%s:entering ...........", __func__);
-
mos7720_port = usb_get_serial_port_data(port);
- if (mos7720_port == NULL) {
- dbg("%s:leaving ...........", __func__);
+ if (mos7720_port == NULL)
return -ENODEV;
- }
/* FIXME: Locking */
for (i = 0; i < NUM_URBS; ++i) {
@@ -1240,7 +1212,7 @@ static int mos7720_write_room(struct tty_struct *tty)
room += URB_TRANSFER_BUFFER_SIZE;
}
- dbg("%s - returns %d", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
return room;
}
@@ -1257,15 +1229,11 @@ static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port,
struct urb *urb;
const unsigned char *current_position = data;
- dbg("%s:entering ...........", __func__);
-
serial = port->serial;
mos7720_port = usb_get_serial_port_data(port);
- if (mos7720_port == NULL) {
- dbg("mos7720_port is NULL");
+ if (mos7720_port == NULL)
return -ENODEV;
- }
/* try to find a free urb in the list */
urb = NULL;
@@ -1274,13 +1242,13 @@ static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port,
if (mos7720_port->write_urb_pool[i] &&
mos7720_port->write_urb_pool[i]->status != -EINPROGRESS) {
urb = mos7720_port->write_urb_pool[i];
- dbg("URB:%d", i);
+ dev_dbg(&port->dev, "URB:%d\n", i);
break;
}
}
if (urb == NULL) {
- dbg("%s - no more free urbs", __func__);
+ dev_dbg(&port->dev, "%s - no more free urbs\n", __func__);
goto exit;
}
@@ -1296,7 +1264,7 @@ static int mos7720_write(struct tty_struct *tty, struct usb_serial_port *port,
transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
memcpy(urb->transfer_buffer, current_position, transfer_size);
- usb_serial_debug_data(debug, &port->dev, __func__, transfer_size,
+ usb_serial_debug_data(&port->dev, __func__, transfer_size,
urb->transfer_buffer);
/* fill urb with data and submit */
@@ -1326,20 +1294,16 @@ static void mos7720_throttle(struct tty_struct *tty)
struct moschip_port *mos7720_port;
int status;
- dbg("%s- port %d", __func__, port->number);
-
mos7720_port = usb_get_serial_port_data(port);
if (mos7720_port == NULL)
return;
if (!mos7720_port->open) {
- dbg("port not opened");
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
}
- dbg("%s: Entering ..........", __func__);
-
/* if we are implementing XON/XOFF, send the stop character */
if (I_IXOFF(tty)) {
unsigned char stop_char = STOP_CHAR(tty);
@@ -1368,12 +1332,10 @@ static void mos7720_unthrottle(struct tty_struct *tty)
return;
if (!mos7720_port->open) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
}
- dbg("%s: Entering ..........", __func__);
-
/* if we are implementing XON/XOFF, send the start character */
if (I_IXOFF(tty)) {
unsigned char start_char = START_CHAR(tty);
@@ -1409,7 +1371,7 @@ static int set_higher_rates(struct moschip_port *mos7720_port,
/***********************************************
* Init Sequence for higher rates
***********************************************/
- dbg("Sending Setting Commands ..........");
+ dev_dbg(&port->dev, "Sending Setting Commands ..........\n");
port_number = port->number - port->serial->minor;
write_mos_reg(serial, port_number, IER, 0x00);
@@ -1478,7 +1440,7 @@ static struct divisor_table_entry divisor_table[] = {
* this function calculates the proper baud rate divisor for the specified
* baud rate.
*****************************************************************************/
-static int calc_baud_rate_divisor(int baudrate, int *divisor)
+static int calc_baud_rate_divisor(struct usb_serial_port *port, int baudrate, int *divisor)
{
int i;
__u16 custom;
@@ -1486,7 +1448,7 @@ static int calc_baud_rate_divisor(int baudrate, int *divisor)
__u16 round;
- dbg("%s - %d", __func__, baudrate);
+ dev_dbg(&port->dev, "%s - %d\n", __func__, baudrate);
for (i = 0; i < ARRAY_SIZE(divisor_table); i++) {
if (divisor_table[i].baudrate == baudrate) {
@@ -1508,11 +1470,11 @@ static int calc_baud_rate_divisor(int baudrate, int *divisor)
custom++;
*divisor = custom;
- dbg("Baud %d = %d", baudrate, custom);
+ dev_dbg(&port->dev, "Baud %d = %d\n", baudrate, custom);
return 0;
}
- dbg("Baud calculation Failed...");
+ dev_dbg(&port->dev, "Baud calculation Failed...\n");
return -EINVAL;
}
@@ -1536,13 +1498,11 @@ static int send_cmd_write_baud_rate(struct moschip_port *mos7720_port,
port = mos7720_port->port;
serial = port->serial;
- dbg("%s: Entering ..........", __func__);
-
number = port->number - port->serial->minor;
- dbg("%s - port = %d, baud = %d", __func__, port->number, baudrate);
+ dev_dbg(&port->dev, "%s - baud = %d\n", __func__, baudrate);
/* Calculate the Divisor */
- status = calc_baud_rate_divisor(baudrate, &divisor);
+ status = calc_baud_rate_divisor(port, baudrate, &divisor);
if (status) {
dev_err(&port->dev, "%s - bad baud rate\n", __func__);
return status;
@@ -1591,15 +1551,11 @@ static void change_port_settings(struct tty_struct *tty,
serial = port->serial;
port_number = port->number - port->serial->minor;
- dbg("%s - port %d", __func__, port->number);
-
if (!mos7720_port->open) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
}
- dbg("%s: Entering ..........", __func__);
-
lData = UART_LCR_WLEN8;
lStop = 0x00; /* 1 stop bit */
lParity = 0x00; /* No parity */
@@ -1633,14 +1589,14 @@ static void change_port_settings(struct tty_struct *tty,
if (cflag & PARENB) {
if (cflag & PARODD) {
lParity = UART_LCR_PARITY;
- dbg("%s - parity = odd", __func__);
+ dev_dbg(&port->dev, "%s - parity = odd\n", __func__);
} else {
lParity = (UART_LCR_EPAR | UART_LCR_PARITY);
- dbg("%s - parity = even", __func__);
+ dev_dbg(&port->dev, "%s - parity = even\n", __func__);
}
} else {
- dbg("%s - parity = none", __func__);
+ dev_dbg(&port->dev, "%s - parity = none\n", __func__);
}
if (cflag & CMSPAR)
@@ -1649,10 +1605,10 @@ static void change_port_settings(struct tty_struct *tty,
/* Change the Stop bit */
if (cflag & CSTOPB) {
lStop = UART_LCR_STOP;
- dbg("%s - stop bits = 2", __func__);
+ dev_dbg(&port->dev, "%s - stop bits = 2\n", __func__);
} else {
lStop = 0x00;
- dbg("%s - stop bits = 1", __func__);
+ dev_dbg(&port->dev, "%s - stop bits = 1\n", __func__);
}
#define LCR_BITS_MASK 0x03 /* Mask for bits/char field */
@@ -1698,7 +1654,7 @@ static void change_port_settings(struct tty_struct *tty,
baud = tty_get_baud_rate(tty);
if (!baud) {
/* pick a default, any default... */
- dbg("Picked default baud...");
+ dev_dbg(&port->dev, "Picked default baud...\n");
baud = 9600;
}
@@ -1709,7 +1665,7 @@ static void change_port_settings(struct tty_struct *tty,
return;
}
- dbg("%s - baud rate = %d", __func__, baud);
+ dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud);
status = send_cmd_write_baud_rate(mos7720_port, baud);
/* FIXME: needs to write actual resulting baud back not just
blindly do so */
@@ -1721,8 +1677,7 @@ static void change_port_settings(struct tty_struct *tty,
if (port->read_urb->status != -EINPROGRESS) {
status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (status)
- dbg("usb_submit_urb(read bulk) failed, status = %d",
- status);
+ dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status);
}
}
@@ -1747,23 +1702,19 @@ static void mos7720_set_termios(struct tty_struct *tty,
return;
if (!mos7720_port->open) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
}
- dbg("%s\n", "setting termios - ASPIRE");
+ dev_dbg(&port->dev, "setting termios - ASPIRE\n");
cflag = tty->termios.c_cflag;
- dbg("%s - cflag %08x iflag %08x", __func__,
- tty->termios.c_cflag,
- RELEVANT_IFLAG(tty->termios.c_iflag));
+ dev_dbg(&port->dev, "%s - cflag %08x iflag %08x\n", __func__,
+ tty->termios.c_cflag, RELEVANT_IFLAG(tty->termios.c_iflag));
- dbg("%s - old cflag %08x old iflag %08x", __func__,
- old_termios->c_cflag,
- RELEVANT_IFLAG(old_termios->c_iflag));
-
- dbg("%s - port %d", __func__, port->number);
+ dev_dbg(&port->dev, "%s - old cflag %08x old iflag %08x\n", __func__,
+ old_termios->c_cflag, RELEVANT_IFLAG(old_termios->c_iflag));
/* change the port settings to the new ones specified */
change_port_settings(tty, mos7720_port, old_termios);
@@ -1771,8 +1722,7 @@ static void mos7720_set_termios(struct tty_struct *tty,
if (port->read_urb->status != -EINPROGRESS) {
status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (status)
- dbg("usb_submit_urb(read bulk) failed, status = %d",
- status);
+ dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n", status);
}
}
@@ -1800,7 +1750,7 @@ static int get_lsr_info(struct tty_struct *tty,
read_mos_reg(port->serial, port_number, LSR, &data);
if ((data & (UART_LSR_TEMT | UART_LSR_THRE))
== (UART_LSR_TEMT | UART_LSR_THRE)) {
- dbg("%s -- Empty", __func__);
+ dev_dbg(&port->dev, "%s -- Empty\n", __func__);
result = TIOCSER_TEMT;
}
}
@@ -1817,8 +1767,6 @@ static int mos7720_tiocmget(struct tty_struct *tty)
unsigned int mcr ;
unsigned int msr ;
- dbg("%s - port %d", __func__, port->number);
-
mcr = mos7720_port->shadowMCR;
msr = mos7720_port->shadowMSR;
@@ -1829,8 +1777,6 @@ static int mos7720_tiocmget(struct tty_struct *tty)
| ((msr & UART_MSR_RI) ? TIOCM_RI : 0) /* 0x080 */
| ((msr & UART_MSR_DSR) ? TIOCM_DSR : 0); /* 0x100 */
- dbg("%s -- %x", __func__, result);
-
return result;
}
@@ -1840,8 +1786,6 @@ static int mos7720_tiocmset(struct tty_struct *tty,
struct usb_serial_port *port = tty->driver_data;
struct moschip_port *mos7720_port = usb_get_serial_port_data(port);
unsigned int mcr ;
- dbg("%s - port %d", __func__, port->number);
- dbg("he was at tiocmset");
mcr = mos7720_port->shadowMCR;
@@ -1888,8 +1832,8 @@ static int mos7720_get_icount(struct tty_struct *tty,
icount->brk = cnow.brk;
icount->buf_overrun = cnow.buf_overrun;
- dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __func__,
- port->number, icount->rx, icount->tx);
+ dev_dbg(&port->dev, "%s TIOCGICOUNT RX=%d, TX=%d\n", __func__,
+ icount->rx, icount->tx);
return 0;
}
@@ -1975,29 +1919,28 @@ static int mos7720_ioctl(struct tty_struct *tty,
if (mos7720_port == NULL)
return -ENODEV;
- dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+ dev_dbg(&port->dev, "%s - cmd = 0x%x", __func__, cmd);
switch (cmd) {
case TIOCSERGETLSR:
- dbg("%s (%d) TIOCSERGETLSR", __func__, port->number);
+ dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
return get_lsr_info(tty, mos7720_port,
(unsigned int __user *)arg);
/* FIXME: These should be using the mode methods */
case TIOCMBIS:
case TIOCMBIC:
- dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET",
- __func__, port->number);
+ dev_dbg(&port->dev, "%s TIOCMSET/TIOCMBIC/TIOCMSET\n", __func__);
return set_modem_info(mos7720_port, cmd,
(unsigned int __user *)arg);
case TIOCGSERIAL:
- dbg("%s (%d) TIOCGSERIAL", __func__, port->number);
+ dev_dbg(&port->dev, "%s TIOCGSERIAL\n", __func__);
return get_serial_info(mos7720_port,
(struct serial_struct __user *)arg);
case TIOCMIWAIT:
- dbg("%s (%d) TIOCMIWAIT", __func__, port->number);
+ dev_dbg(&port->dev, "%s TIOCMIWAIT\n", __func__);
cprev = mos7720_port->icount;
while (1) {
if (signal_pending(current))
@@ -2030,13 +1973,6 @@ static int mos7720_startup(struct usb_serial *serial)
u16 product;
int ret_val;
- dbg("%s: Entering ..........", __func__);
-
- if (!serial) {
- dbg("Invalid Handler");
- return -ENODEV;
- }
-
product = le16_to_cpu(serial->dev->descriptor.idProduct);
dev = serial->dev;
@@ -2081,8 +2017,8 @@ static int mos7720_startup(struct usb_serial *serial)
mos7720_port->port = serial->port[i];
usb_set_serial_port_data(serial->port[i], mos7720_port);
- dbg("port number is %d", serial->port[i]->number);
- dbg("serial number is %d", serial->minor);
+ dev_dbg(&dev->dev, "port number is %d\n", serial->port[i]->number);
+ dev_dbg(&dev->dev, "serial number is %d\n", serial->minor);
}
@@ -2106,7 +2042,7 @@ static int mos7720_startup(struct usb_serial *serial)
#endif
/* LSR For Port 1 */
read_mos_reg(serial, 0, LSR, &data);
- dbg("LSR:%x", data);
+ dev_dbg(&dev->dev, "LSR:%x\n", data);
return 0;
}
@@ -2195,6 +2131,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 402c32d7accb..d6d4eeca8c68 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -252,8 +252,6 @@ struct moschip_port {
struct timer_list led_timer2; /* Timer for LED off */
};
-static bool debug;
-
/*
* mos7840_set_reg_sync
* To set the Control register by calling usb_fill_control_urb function
@@ -265,7 +263,7 @@ static int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg,
{
struct usb_device *dev = port->serial->dev;
val = val & 0x00ff;
- dbg("mos7840_set_reg_sync offset is %x, value %x", reg, val);
+ dev_dbg(&port->dev, "mos7840_set_reg_sync offset is %x, value %x\n", reg, val);
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
MCS_WR_RTYPE, val, reg, NULL, 0,
@@ -293,7 +291,7 @@ static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg,
MCS_RD_RTYPE, 0, reg, buf, VENDOR_READ_LENGTH,
MOS_WDR_TIMEOUT);
*val = buf[0];
- dbg("mos7840_get_reg_sync offset is %x, return val %x", reg, *val);
+ dev_dbg(&port->dev, "%s offset is %x, return val %x\n", __func__, reg, *val);
kfree(buf);
return ret;
@@ -316,21 +314,16 @@ static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg,
if (port->serial->num_ports == 4) {
val |= (((__u16) port->number -
(__u16) (port->serial->minor)) + 1) << 8;
- dbg("mos7840_set_uart_reg application number is %x", val);
} else {
if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
val |= (((__u16) port->number -
(__u16) (port->serial->minor)) + 1) << 8;
- dbg("mos7840_set_uart_reg application number is %x",
- val);
} else {
- val |=
- (((__u16) port->number -
+ val |= (((__u16) port->number -
(__u16) (port->serial->minor)) + 2) << 8;
- dbg("mos7840_set_uart_reg application number is %x",
- val);
}
}
+ dev_dbg(&port->dev, "%s application number is %x\n", __func__, val);
return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
MCS_WR_RTYPE, val, reg, NULL, 0,
MOS_WDR_TIMEOUT);
@@ -354,27 +347,21 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
if (!buf)
return -ENOMEM;
- /* dbg("application number is %4x",
- (((__u16)port->number - (__u16)(port->serial->minor))+1)<<8); */
/* Wval is same as application number */
if (port->serial->num_ports == 4) {
Wval =
(((__u16) port->number - (__u16) (port->serial->minor)) +
1) << 8;
- dbg("mos7840_get_uart_reg application number is %x", Wval);
} else {
if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
Wval = (((__u16) port->number -
(__u16) (port->serial->minor)) + 1) << 8;
- dbg("mos7840_get_uart_reg application number is %x",
- Wval);
} else {
Wval = (((__u16) port->number -
(__u16) (port->serial->minor)) + 2) << 8;
- dbg("mos7840_get_uart_reg application number is %x",
- Wval);
}
}
+ dev_dbg(&port->dev, "%s application number is %x\n", __func__, Wval);
ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
MCS_RD_RTYPE, Wval, reg, buf, VENDOR_READ_LENGTH,
MOS_WDR_TIMEOUT);
@@ -384,14 +371,13 @@ static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
return ret;
}
-static void mos7840_dump_serial_port(struct moschip_port *mos7840_port)
+static void mos7840_dump_serial_port(struct usb_serial_port *port,
+ struct moschip_port *mos7840_port)
{
- dbg("***************************************");
- dbg("SpRegOffset is %2x", mos7840_port->SpRegOffset);
- dbg("ControlRegOffset is %2x", mos7840_port->ControlRegOffset);
- dbg("DCRRegOffset is %2x", mos7840_port->DcrRegOffset);
- dbg("***************************************");
+ dev_dbg(&port->dev, "SpRegOffset is %2x\n", mos7840_port->SpRegOffset);
+ dev_dbg(&port->dev, "ControlRegOffset is %2x\n", mos7840_port->ControlRegOffset);
+ dev_dbg(&port->dev, "DCRRegOffset is %2x\n", mos7840_port->DcrRegOffset);
}
@@ -450,8 +436,6 @@ static void mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
{
struct async_icount *icount;
- dbg("%s - %02x", __func__, new_lsr);
-
if (new_lsr & SERIAL_LSR_BI) {
/*
* Parity and Framing errors only count if they
@@ -492,6 +476,7 @@ static void mos7840_control_callback(struct urb *urb)
{
unsigned char *data;
struct moschip_port *mos7840_port;
+ struct device *dev = &urb->dev->dev;
__u8 regval = 0x0;
int result = 0;
int status = urb->status;
@@ -506,21 +491,19 @@ static void mos7840_control_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __func__,
- status);
+ dev_dbg(dev, "%s - urb shutting down with status: %d\n", __func__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d", __func__,
- status);
+ dev_dbg(dev, "%s - nonzero urb status received: %d\n", __func__, status);
goto exit;
}
- dbg("%s urb buffer size is %d", __func__, urb->actual_length);
- dbg("%s mos7840_port->MsrLsr is %d port %d", __func__,
- mos7840_port->MsrLsr, mos7840_port->port_num);
+ dev_dbg(dev, "%s urb buffer size is %d\n", __func__, urb->actual_length);
+ dev_dbg(dev, "%s mos7840_port->MsrLsr is %d port %d\n", __func__,
+ mos7840_port->MsrLsr, mos7840_port->port_num);
data = urb->transfer_buffer;
regval = (__u8) data[0];
- dbg("%s data is %x", __func__, regval);
+ dev_dbg(dev, "%s data is %x\n", __func__, regval);
if (mos7840_port->MsrLsr == 0)
mos7840_handle_new_msr(mos7840_port, regval);
else if (mos7840_port->MsrLsr == 1)
@@ -532,8 +515,7 @@ exit:
result = usb_submit_urb(mos7840_port->int_urb, GFP_ATOMIC);
spin_unlock(&mos7840_port->pool_lock);
if (result) {
- dev_err(&urb->dev->dev,
- "%s - Error %d submitting interrupt urb\n",
+ dev_err(dev, "%s - Error %d submitting interrupt urb\n",
__func__, result);
}
}
@@ -570,12 +552,12 @@ static void mos7840_set_led_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* This urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __func__,
- urb->status);
+ dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d",
+ __func__, urb->status);
break;
default:
- dbg("%s - nonzero urb status received: %d", __func__,
- urb->status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d",
+ __func__, urb->status);
}
}
@@ -650,12 +632,12 @@ static void mos7840_interrupt_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __func__,
- status);
+ dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d\n",
+ __func__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d", __func__,
- status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d\n",
+ __func__, status);
goto exit;
}
@@ -672,7 +654,7 @@ static void mos7840_interrupt_callback(struct urb *urb)
* Byte 5 FIFO status for both */
if (length && length > 5) {
- dbg("%s", "Wrong data !!!");
+ dev_dbg(&urb->dev->dev, "%s", "Wrong data !!!\n");
return;
}
@@ -689,17 +671,17 @@ static void mos7840_interrupt_callback(struct urb *urb)
(__u16) (serial->minor)) + 1) << 8;
if (mos7840_port->open) {
if (sp[i] & 0x01) {
- dbg("SP%d No Interrupt !!!", i);
+ dev_dbg(&urb->dev->dev, "SP%d No Interrupt !!!\n", i);
} else {
switch (sp[i] & 0x0f) {
case SERIAL_IIR_RLS:
- dbg("Serial Port %d: Receiver status error or ", i);
- dbg("address bit detected in 9-bit mode");
+ dev_dbg(&urb->dev->dev, "Serial Port %d: Receiver status error or \n", i);
+ dev_dbg(&urb->dev->dev, "address bit detected in 9-bit mode\n");
mos7840_port->MsrLsr = 1;
wreg = LINE_STATUS_REGISTER;
break;
case SERIAL_IIR_MS:
- dbg("Serial Port %d: Modem status change", i);
+ dev_dbg(&urb->dev->dev, "Serial Port %d: Modem status change\n", i);
mos7840_port->MsrLsr = 0;
wreg = MODEM_STATUS_REGISTER;
break;
@@ -731,11 +713,11 @@ static int mos7840_port_paranoia_check(struct usb_serial_port *port,
const char *function)
{
if (!port) {
- dbg("%s - port == NULL", function);
+ pr_debug("%s - port == NULL\n", function);
return -1;
}
if (!port->serial) {
- dbg("%s - port->serial == NULL", function);
+ pr_debug("%s - port->serial == NULL\n", function);
return -1;
}
@@ -747,11 +729,11 @@ static int mos7840_serial_paranoia_check(struct usb_serial *serial,
const char *function)
{
if (!serial) {
- dbg("%s - serial == NULL", function);
+ pr_debug("%s - serial == NULL\n", function);
return -1;
}
if (!serial->type) {
- dbg("%s - serial->type == NULL!", function);
+ pr_debug("%s - serial->type == NULL!\n", function);
return -1;
}
@@ -790,49 +772,44 @@ static void mos7840_bulk_in_callback(struct urb *urb)
int status = urb->status;
mos7840_port = urb->context;
- if (!mos7840_port) {
- dbg("%s", "NULL mos7840_port pointer");
+ if (!mos7840_port)
return;
- }
if (status) {
- dbg("nonzero read bulk status received: %d", status);
+ dev_dbg(&urb->dev->dev, "nonzero read bulk status received: %d\n", status);
mos7840_port->read_urb_busy = false;
return;
}
- port = (struct usb_serial_port *)mos7840_port->port;
+ port = mos7840_port->port;
if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Port Paranoia failed");
mos7840_port->read_urb_busy = false;
return;
}
serial = mos7840_get_usb_serial(port, __func__);
if (!serial) {
- dbg("%s", "Bad serial pointer");
mos7840_port->read_urb_busy = false;
return;
}
data = urb->transfer_buffer;
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
if (urb->actual_length) {
tty = tty_port_tty_get(&mos7840_port->port->port);
if (tty) {
tty_insert_flip_string(tty, data, urb->actual_length);
- dbg(" %s ", data);
tty_flip_buffer_push(tty);
tty_kref_put(tty);
}
mos7840_port->icount.rx += urb->actual_length;
smp_wmb();
- dbg("mos7840_port->icount.rx is %d:",
- mos7840_port->icount.rx);
+ dev_dbg(&port->dev, "mos7840_port->icount.rx is %d:\n", mos7840_port->icount.rx);
}
if (!mos7840_port->read_urb) {
- dbg("%s", "URB KILLED !!!");
+ dev_dbg(&port->dev, "%s", "URB KILLED !!!\n");
mos7840_port->read_urb_busy = false;
return;
}
@@ -850,7 +827,7 @@ static void mos7840_bulk_in_callback(struct urb *urb)
retval = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
if (retval) {
- dbg("usb_submit_urb(read bulk) failed, retval = %d", retval);
+ dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, retval = %d\n", retval);
mos7840_port->read_urb_busy = false;
}
}
@@ -864,11 +841,13 @@ static void mos7840_bulk_in_callback(struct urb *urb)
static void mos7840_bulk_out_data_callback(struct urb *urb)
{
struct moschip_port *mos7840_port;
+ struct usb_serial_port *port;
struct tty_struct *tty;
int status = urb->status;
int i;
mos7840_port = urb->context;
+ port = mos7840_port->port;
spin_lock(&mos7840_port->pool_lock);
for (i = 0; i < NUM_URBS; i++) {
if (urb == mos7840_port->write_urb_pool[i]) {
@@ -879,16 +858,14 @@ static void mos7840_bulk_out_data_callback(struct urb *urb)
spin_unlock(&mos7840_port->pool_lock);
if (status) {
- dbg("nonzero write bulk status received:%d", status);
+ dev_dbg(&port->dev, "nonzero write bulk status received:%d\n", status);
return;
}
- if (mos7840_port_paranoia_check(mos7840_port->port, __func__)) {
- dbg("%s", "Port Paranoia failed");
+ if (mos7840_port_paranoia_check(port, __func__))
return;
- }
- tty = tty_port_tty_get(&mos7840_port->port->port);
+ tty = tty_port_tty_get(&port->port);
if (tty && mos7840_port->open)
tty_wakeup(tty);
tty_kref_put(tty);
@@ -929,17 +906,13 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
struct moschip_port *mos7840_port;
struct moschip_port *port0;
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Port Paranoia failed");
+ if (mos7840_port_paranoia_check(port, __func__))
return -ENODEV;
- }
serial = port->serial;
- if (mos7840_serial_paranoia_check(serial, __func__)) {
- dbg("%s", "Serial Paranoia failed");
+ if (mos7840_serial_paranoia_check(serial, __func__))
return -ENODEV;
- }
mos7840_port = mos7840_get_port_private(port);
port0 = mos7840_get_port_private(serial->port[0]);
@@ -990,20 +963,20 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
Data = 0x0;
status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
if (status < 0) {
- dbg("Reading Spreg failed");
+ dev_dbg(&port->dev, "Reading Spreg failed\n");
return -1;
}
Data |= 0x80;
status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
if (status < 0) {
- dbg("writing Spreg failed");
+ dev_dbg(&port->dev, "writing Spreg failed\n");
return -1;
}
Data &= ~0x80;
status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
if (status < 0) {
- dbg("writing Spreg failed");
+ dev_dbg(&port->dev, "writing Spreg failed\n");
return -1;
}
/* End of block to be checked */
@@ -1012,7 +985,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
status = mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset,
&Data);
if (status < 0) {
- dbg("Reading Controlreg failed");
+ dev_dbg(&port->dev, "Reading Controlreg failed\n");
return -1;
}
Data |= 0x08; /* Driver done bit */
@@ -1020,7 +993,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
status = mos7840_set_reg_sync(port,
mos7840_port->ControlRegOffset, Data);
if (status < 0) {
- dbg("writing Controlreg failed");
+ dev_dbg(&port->dev, "writing Controlreg failed\n");
return -1;
}
/* do register settings here */
@@ -1031,21 +1004,21 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
Data = 0x00;
status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
if (status < 0) {
- dbg("disabling interrupts failed");
+ dev_dbg(&port->dev, "disabling interrupts failed\n");
return -1;
}
/* Set FIFO_CONTROL_REGISTER to the default value */
Data = 0x00;
status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
if (status < 0) {
- dbg("Writing FIFO_CONTROL_REGISTER failed");
+ dev_dbg(&port->dev, "Writing FIFO_CONTROL_REGISTER failed\n");
return -1;
}
Data = 0xcf;
status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
if (status < 0) {
- dbg("Writing FIFO_CONTROL_REGISTER failed");
+ dev_dbg(&port->dev, "Writing FIFO_CONTROL_REGISTER failed\n");
return -1;
}
@@ -1142,12 +1115,12 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
* (can't set it up in mos7840_startup as the *
* structures were not set up at that time.) */
- dbg("port number is %d", port->number);
- dbg("serial number is %d", port->serial->minor);
- dbg("Bulkin endpoint is %d", port->bulk_in_endpointAddress);
- dbg("BulkOut endpoint is %d", port->bulk_out_endpointAddress);
- dbg("Interrupt endpoint is %d", port->interrupt_in_endpointAddress);
- dbg("port's number in the device is %d", mos7840_port->port_num);
+ dev_dbg(&port->dev, "port number is %d\n", port->number);
+ dev_dbg(&port->dev, "serial number is %d\n", port->serial->minor);
+ dev_dbg(&port->dev, "Bulkin endpoint is %d\n", port->bulk_in_endpointAddress);
+ dev_dbg(&port->dev, "BulkOut endpoint is %d\n", port->bulk_out_endpointAddress);
+ dev_dbg(&port->dev, "Interrupt endpoint is %d\n", port->interrupt_in_endpointAddress);
+ dev_dbg(&port->dev, "port's number in the device is %d\n", mos7840_port->port_num);
mos7840_port->read_urb = port->read_urb;
/* set up our bulk in urb */
@@ -1171,8 +1144,7 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
mos7840_bulk_in_callback, mos7840_port);
}
- dbg("mos7840_open: bulkin endpoint is %d",
- port->bulk_in_endpointAddress);
+ dev_dbg(&port->dev, "%s: bulkin endpoint is %d\n", __func__, port->bulk_in_endpointAddress);
mos7840_port->read_urb_busy = true;
response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
if (response) {
@@ -1197,9 +1169,6 @@ static int mos7840_open(struct tty_struct *tty, struct usb_serial_port *port)
mos7840_port->icount.tx = 0;
mos7840_port->icount.rx = 0;
- dbg("usb_serial serial:%p mos7840_port:%p\n usb_serial_port port:%p",
- serial, mos7840_port, port);
-
return 0;
}
@@ -1221,10 +1190,8 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty)
unsigned long flags;
struct moschip_port *mos7840_port;
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Invalid port");
+ if (mos7840_port_paranoia_check(port, __func__))
return 0;
- }
mos7840_port = mos7840_get_port_private(port);
if (mos7840_port == NULL)
@@ -1238,7 +1205,7 @@ static int mos7840_chars_in_buffer(struct tty_struct *tty)
}
}
spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
- dbg("%s - returns %d", __func__, chars);
+ dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
return chars;
}
@@ -1256,16 +1223,12 @@ static void mos7840_close(struct usb_serial_port *port)
int j;
__u16 Data;
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Port Paranoia failed");
+ if (mos7840_port_paranoia_check(port, __func__))
return;
- }
serial = mos7840_get_usb_serial(port, __func__);
- if (!serial) {
- dbg("%s", "Serial Paranoia failed");
+ if (!serial)
return;
- }
mos7840_port = mos7840_get_port_private(port);
port0 = mos7840_get_port_private(serial->port[0]);
@@ -1291,27 +1254,26 @@ static void mos7840_close(struct usb_serial_port *port)
* and interrupt read if they exists */
if (serial->dev) {
if (mos7840_port->write_urb) {
- dbg("%s", "Shutdown bulk write");
+ dev_dbg(&port->dev, "%s", "Shutdown bulk write\n");
usb_kill_urb(mos7840_port->write_urb);
}
if (mos7840_port->read_urb) {
- dbg("%s", "Shutdown bulk read");
+ dev_dbg(&port->dev, "%s", "Shutdown bulk read\n");
usb_kill_urb(mos7840_port->read_urb);
mos7840_port->read_urb_busy = false;
}
if ((&mos7840_port->control_urb)) {
- dbg("%s", "Shutdown control read");
+ dev_dbg(&port->dev, "%s", "Shutdown control read\n");
/*/ usb_kill_urb (mos7840_port->control_urb); */
}
}
/* if(mos7840_port->ctrl_buf != NULL) */
/* kfree(mos7840_port->ctrl_buf); */
port0->open_ports--;
- dbg("mos7840_num_open_ports in close%d:in port%d",
- port0->open_ports, port->number);
+ dev_dbg(&port->dev, "%s in close%d:in port%d\n", __func__, port0->open_ports, port->number);
if (port0->open_ports == 0) {
if (serial->port[0]->interrupt_in_urb) {
- dbg("%s", "Shutdown interrupt_in_urb");
+ dev_dbg(&port->dev, "Shutdown interrupt_in_urb\n");
usb_kill_urb(serial->port[0]->interrupt_in_urb);
}
}
@@ -1363,7 +1325,7 @@ static void mos7840_block_until_chase_response(struct tty_struct *tty,
/* No activity.. count down section */
wait--;
if (wait == 0) {
- dbg("%s - TIMEOUT", __func__);
+ dev_dbg(&mos7840_port->port->dev, "%s - TIMEOUT\n", __func__);
return;
} else {
/* Reset timeout value back to seconds */
@@ -1384,16 +1346,12 @@ static void mos7840_break(struct tty_struct *tty, int break_state)
struct usb_serial *serial;
struct moschip_port *mos7840_port;
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Port Paranoia failed");
+ if (mos7840_port_paranoia_check(port, __func__))
return;
- }
serial = mos7840_get_usb_serial(port, __func__);
- if (!serial) {
- dbg("%s", "Serial Paranoia failed");
+ if (!serial)
return;
- }
mos7840_port = mos7840_get_port_private(port);
@@ -1411,8 +1369,7 @@ static void mos7840_break(struct tty_struct *tty, int break_state)
/* FIXME: no locking on shadowLCR anywhere in driver */
mos7840_port->shadowLCR = data;
- dbg("mcs7840_break mos7840_port->shadowLCR is %x",
- mos7840_port->shadowLCR);
+ dev_dbg(&port->dev, "%s mos7840_port->shadowLCR is %x\n", __func__, mos7840_port->shadowLCR);
mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER,
mos7840_port->shadowLCR);
}
@@ -1433,17 +1390,12 @@ static int mos7840_write_room(struct tty_struct *tty)
unsigned long flags;
struct moschip_port *mos7840_port;
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Invalid port");
- dbg("%s", " mos7840_write_room:leaving ...........");
+ if (mos7840_port_paranoia_check(port, __func__))
return -1;
- }
mos7840_port = mos7840_get_port_private(port);
- if (mos7840_port == NULL) {
- dbg("%s", "mos7840_break:leaving ...........");
+ if (mos7840_port == NULL)
return -1;
- }
spin_lock_irqsave(&mos7840_port->pool_lock, flags);
for (i = 0; i < NUM_URBS; ++i) {
@@ -1453,7 +1405,7 @@ static int mos7840_write_room(struct tty_struct *tty)
spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
room = (room == 0) ? 0 : room - URB_TRANSFER_BUFFER_SIZE + 1;
- dbg("%s - returns %d", __func__, room);
+ dev_dbg(&mos7840_port->port->dev, "%s - returns %d\n", __func__, room);
return room;
}
@@ -1486,9 +1438,8 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
Data = 0x00;
status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
mos7840_port->shadowLCR = Data;
- dbg("mos7840_write: LINE_CONTROL_REGISTER is %x", Data);
- dbg("mos7840_write: mos7840_port->shadowLCR is %x",
- mos7840_port->shadowLCR);
+ dev_dbg(&port->dev, "%s: LINE_CONTROL_REGISTER is %x\n", __func__, Data);
+ dev_dbg(&port->dev, "%s: mos7840_port->shadowLCR is %x\n", __func__, mos7840_port->shadowLCR);
/* Data = 0x03; */
/* status = mos7840_set_uart_reg(port,LINE_CONTROL_REGISTER,Data); */
@@ -1501,34 +1452,27 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
/* status = mos7840_set_uart_reg(port,DIVISOR_LATCH_LSB,Data); */
Data = 0x00;
status = mos7840_get_uart_reg(port, DIVISOR_LATCH_LSB, &Data);
- dbg("mos7840_write:DLL value is %x", Data);
+ dev_dbg(&port->dev, "%s: DLL value is %x\n", __func__, Data);
Data = 0x0;
status = mos7840_get_uart_reg(port, DIVISOR_LATCH_MSB, &Data);
- dbg("mos7840_write:DLM value is %x", Data);
+ dev_dbg(&port->dev, "%s: DLM value is %x\n", __func__, Data);
Data = Data & ~SERIAL_LCR_DLAB;
- dbg("mos7840_write: mos7840_port->shadowLCR is %x",
- mos7840_port->shadowLCR);
+ dev_dbg(&port->dev, "%s: mos7840_port->shadowLCR is %x\n", __func__, mos7840_port->shadowLCR);
status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
#endif
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Port Paranoia failed");
+ if (mos7840_port_paranoia_check(port, __func__))
return -1;
- }
serial = port->serial;
- if (mos7840_serial_paranoia_check(serial, __func__)) {
- dbg("%s", "Serial Paranoia failed");
+ if (mos7840_serial_paranoia_check(serial, __func__))
return -1;
- }
mos7840_port = mos7840_get_port_private(port);
- if (mos7840_port == NULL) {
- dbg("%s", "mos7840_port is NULL");
+ if (mos7840_port == NULL)
return -1;
- }
/* try to find a free urb in the list */
urb = NULL;
@@ -1538,14 +1482,14 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
if (!mos7840_port->busy[i]) {
mos7840_port->busy[i] = 1;
urb = mos7840_port->write_urb_pool[i];
- dbg("URB:%d", i);
+ dev_dbg(&port->dev, "URB:%d\n", i);
break;
}
}
spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
if (urb == NULL) {
- dbg("%s - no more free urbs", __func__);
+ dev_dbg(&port->dev, "%s - no more free urbs\n", __func__);
goto exit;
}
@@ -1585,7 +1529,7 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
}
data1 = urb->transfer_buffer;
- dbg("bulkout endpoint is %d", port->bulk_out_endpointAddress);
+ dev_dbg(&port->dev, "bulkout endpoint is %d\n", port->bulk_out_endpointAddress);
/* Turn on LED */
if (mos7840_port->has_led && !mos7840_port->led_flag) {
@@ -1608,7 +1552,7 @@ static int mos7840_write(struct tty_struct *tty, struct usb_serial_port *port,
bytes_sent = transfer_size;
mos7840_port->icount.tx += transfer_size;
smp_wmb();
- dbg("mos7840_port->icount.tx is %d:", mos7840_port->icount.tx);
+ dev_dbg(&port->dev, "mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx);
exit:
return bytes_sent;
@@ -1626,12 +1570,8 @@ static void mos7840_throttle(struct tty_struct *tty)
struct moschip_port *mos7840_port;
int status;
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Invalid port");
+ if (mos7840_port_paranoia_check(port, __func__))
return;
- }
-
- dbg("- port %d", port->number);
mos7840_port = mos7840_get_port_private(port);
@@ -1639,7 +1579,7 @@ static void mos7840_throttle(struct tty_struct *tty)
return;
if (!mos7840_port->open) {
- dbg("%s", "port not opened");
+ dev_dbg(&port->dev, "%s", "port not opened\n");
return;
}
@@ -1672,16 +1612,14 @@ static void mos7840_unthrottle(struct tty_struct *tty)
int status;
struct moschip_port *mos7840_port = mos7840_get_port_private(port);
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Invalid port");
+ if (mos7840_port_paranoia_check(port, __func__))
return;
- }
if (mos7840_port == NULL)
return;
if (!mos7840_port->open) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
}
@@ -1726,7 +1664,7 @@ static int mos7840_tiocmget(struct tty_struct *tty)
| ((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0)
| ((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0);
- dbg("%s - 0x%04X", __func__, result);
+ dev_dbg(&port->dev, "%s - 0x%04X\n", __func__, result);
return result;
}
@@ -1764,7 +1702,7 @@ static int mos7840_tiocmset(struct tty_struct *tty,
status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mcr);
if (status < 0) {
- dbg("setting MODEM_CONTROL_REGISTER Failed");
+ dev_dbg(&port->dev, "setting MODEM_CONTROL_REGISTER Failed\n");
return status;
}
@@ -1776,10 +1714,11 @@ static int mos7840_tiocmset(struct tty_struct *tty,
* this function calculates the proper baud rate divisor for the specified
* baud rate.
*****************************************************************************/
-static int mos7840_calc_baud_rate_divisor(int baudRate, int *divisor,
+static int mos7840_calc_baud_rate_divisor(struct usb_serial_port *port,
+ int baudRate, int *divisor,
__u16 *clk_sel_val)
{
- dbg("%s - %d", __func__, baudRate);
+ dev_dbg(&port->dev, "%s - %d\n", __func__, baudRate);
if (baudRate <= 115200) {
*divisor = 115200 / baudRate;
@@ -1832,11 +1771,11 @@ static int mos7840_calc_baud_rate_divisor(int baudRate, int *divisor,
custom++;
*divisor = custom;
- dbg(" Baud %d = %d", baudrate, custom);
+ dev_dbg(&port->dev, " Baud %d = %d\n", baudrate, custom);
return 0;
}
- dbg("%s", " Baud calculation Failed...");
+ dev_dbg(&port->dev, "%s", " Baud calculation Failed...\n");
return -1;
#endif
}
@@ -1860,21 +1799,17 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
if (mos7840_port == NULL)
return -1;
- port = (struct usb_serial_port *)mos7840_port->port;
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Invalid port");
+ port = mos7840_port->port;
+ if (mos7840_port_paranoia_check(port, __func__))
return -1;
- }
- if (mos7840_serial_paranoia_check(port->serial, __func__)) {
- dbg("%s", "Invalid Serial");
+ if (mos7840_serial_paranoia_check(port->serial, __func__))
return -1;
- }
number = mos7840_port->port->number - mos7840_port->port->serial->minor;
- dbg("%s - port = %d, baud = %d", __func__,
- mos7840_port->port->number, baudRate);
+ dev_dbg(&port->dev, "%s - port = %d, baud = %d\n", __func__,
+ mos7840_port->port->number, baudRate);
/* reset clk_uart_sel in spregOffset */
if (baudRate > 115200) {
#ifdef HW_flow_control
@@ -1885,7 +1820,7 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
Data);
if (status < 0) {
- dbg("Writing spreg failed in set_serial_baud");
+ dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
return -1;
}
#endif
@@ -1898,7 +1833,7 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
Data);
if (status < 0) {
- dbg("Writing spreg failed in set_serial_baud");
+ dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
return -1;
}
#endif
@@ -1908,19 +1843,19 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
if (1) { /* baudRate <= 115200) */
clk_sel_val = 0x0;
Data = 0x0;
- status = mos7840_calc_baud_rate_divisor(baudRate, &divisor,
+ status = mos7840_calc_baud_rate_divisor(port, baudRate, &divisor,
&clk_sel_val);
status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset,
&Data);
if (status < 0) {
- dbg("reading spreg failed in set_serial_baud");
+ dev_dbg(&port->dev, "reading spreg failed in set_serial_baud\n");
return -1;
}
Data = (Data & 0x8f) | clk_sel_val;
status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset,
Data);
if (status < 0) {
- dbg("Writing spreg failed in set_serial_baud");
+ dev_dbg(&port->dev, "Writing spreg failed in set_serial_baud\n");
return -1;
}
/* Calculate the Divisor */
@@ -1936,11 +1871,11 @@ static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
/* Write the divisor */
Data = (unsigned char)(divisor & 0xff);
- dbg("set_serial_baud Value to write DLL is %x", Data);
+ dev_dbg(&port->dev, "set_serial_baud Value to write DLL is %x\n", Data);
mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
Data = (unsigned char)((divisor & 0xff00) >> 8);
- dbg("set_serial_baud Value to write DLM is %x", Data);
+ dev_dbg(&port->dev, "set_serial_baud Value to write DLM is %x\n", Data);
mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
/* Disable access to divisor latch */
@@ -1975,24 +1910,18 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
if (mos7840_port == NULL)
return;
- port = (struct usb_serial_port *)mos7840_port->port;
+ port = mos7840_port->port;
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Invalid port");
+ if (mos7840_port_paranoia_check(port, __func__))
return;
- }
- if (mos7840_serial_paranoia_check(port->serial, __func__)) {
- dbg("%s", "Invalid Serial");
+ if (mos7840_serial_paranoia_check(port->serial, __func__))
return;
- }
serial = port->serial;
- dbg("%s - port %d", __func__, mos7840_port->port->number);
-
if (!mos7840_port->open) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
}
@@ -2027,14 +1956,14 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
if (cflag & PARENB) {
if (cflag & PARODD) {
lParity = LCR_PAR_ODD;
- dbg("%s - parity = odd", __func__);
+ dev_dbg(&port->dev, "%s - parity = odd\n", __func__);
} else {
lParity = LCR_PAR_EVEN;
- dbg("%s - parity = even", __func__);
+ dev_dbg(&port->dev, "%s - parity = even\n", __func__);
}
} else {
- dbg("%s - parity = none", __func__);
+ dev_dbg(&port->dev, "%s - parity = none\n", __func__);
}
if (cflag & CMSPAR)
@@ -2043,10 +1972,10 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
/* Change the Stop bit */
if (cflag & CSTOPB) {
lStop = LCR_STOP_2;
- dbg("%s - stop bits = 2", __func__);
+ dev_dbg(&port->dev, "%s - stop bits = 2\n", __func__);
} else {
lStop = LCR_STOP_1;
- dbg("%s - stop bits = 1", __func__);
+ dev_dbg(&port->dev, "%s - stop bits = 1\n", __func__);
}
/* Update the LCR with the correct value */
@@ -2054,8 +1983,8 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
mos7840_port->shadowLCR |= (lData | lParity | lStop);
- dbg("mos7840_change_port_settings mos7840_port->shadowLCR is %x",
- mos7840_port->shadowLCR);
+ dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is %x\n", __func__,
+ mos7840_port->shadowLCR);
/* Disable Interrupts */
Data = 0x00;
mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
@@ -2096,11 +2025,11 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
if (!baud) {
/* pick a default, any default... */
- dbg("%s", "Picked default baud...");
+ dev_dbg(&port->dev, "%s", "Picked default baud...\n");
baud = 9600;
}
- dbg("%s - baud rate = %d", __func__, baud);
+ dev_dbg(&port->dev, "%s - baud rate = %d\n", __func__, baud);
status = mos7840_send_cmd_write_baud_rate(mos7840_port, baud);
/* Enable Interrupts */
@@ -2111,15 +2040,15 @@ static void mos7840_change_port_settings(struct tty_struct *tty,
mos7840_port->read_urb_busy = true;
status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
if (status) {
- dbg("usb_submit_urb(read bulk) failed, status = %d",
+ dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n",
status);
mos7840_port->read_urb_busy = false;
}
}
wake_up(&mos7840_port->delta_msr_wait);
mos7840_port->delta_msr_cond = 1;
- dbg("mos7840_change_port_settings mos7840_port->shadowLCR is End %x",
- mos7840_port->shadowLCR);
+ dev_dbg(&port->dev, "%s - mos7840_port->shadowLCR is End %x\n", __func__,
+ mos7840_port->shadowLCR);
}
/*****************************************************************************
@@ -2137,17 +2066,13 @@ static void mos7840_set_termios(struct tty_struct *tty,
struct usb_serial *serial;
struct moschip_port *mos7840_port;
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Invalid port");
+ if (mos7840_port_paranoia_check(port, __func__))
return;
- }
serial = port->serial;
- if (mos7840_serial_paranoia_check(serial, __func__)) {
- dbg("%s", "Invalid Serial");
+ if (mos7840_serial_paranoia_check(serial, __func__))
return;
- }
mos7840_port = mos7840_get_port_private(port);
@@ -2155,26 +2080,26 @@ static void mos7840_set_termios(struct tty_struct *tty,
return;
if (!mos7840_port->open) {
- dbg("%s - port not opened", __func__);
+ dev_dbg(&port->dev, "%s - port not opened\n", __func__);
return;
}
- dbg("%s", "setting termios - ");
+ dev_dbg(&port->dev, "%s", "setting termios - \n");
cflag = tty->termios.c_cflag;
- dbg("%s - clfag %08x iflag %08x", __func__,
- tty->termios.c_cflag, RELEVANT_IFLAG(tty->termios.c_iflag));
- dbg("%s - old clfag %08x old iflag %08x", __func__,
- old_termios->c_cflag, RELEVANT_IFLAG(old_termios->c_iflag));
- dbg("%s - port %d", __func__, port->number);
+ dev_dbg(&port->dev, "%s - clfag %08x iflag %08x\n", __func__,
+ tty->termios.c_cflag, RELEVANT_IFLAG(tty->termios.c_iflag));
+ dev_dbg(&port->dev, "%s - old clfag %08x old iflag %08x\n", __func__,
+ old_termios->c_cflag, RELEVANT_IFLAG(old_termios->c_iflag));
+ dev_dbg(&port->dev, "%s - port %d\n", __func__, port->number);
/* change the port settings to the new ones specified */
mos7840_change_port_settings(tty, mos7840_port, old_termios);
if (!mos7840_port->read_urb) {
- dbg("%s", "URB KILLED !!!!!");
+ dev_dbg(&port->dev, "%s", "URB KILLED !!!!!\n");
return;
}
@@ -2182,7 +2107,7 @@ static void mos7840_set_termios(struct tty_struct *tty,
mos7840_port->read_urb_busy = true;
status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
if (status) {
- dbg("usb_submit_urb(read bulk) failed, status = %d",
+ dev_dbg(&port->dev, "usb_submit_urb(read bulk) failed, status = %d\n",
status);
mos7840_port->read_urb_busy = false;
}
@@ -2207,10 +2132,8 @@ static int mos7840_get_lsr_info(struct tty_struct *tty,
unsigned int result = 0;
count = mos7840_chars_in_buffer(tty);
- if (count == 0) {
- dbg("%s -- Empty", __func__);
+ if (count == 0)
result = TIOCSER_TEMT;
- }
if (copy_to_user(value, &result, sizeof(int)))
return -EFAULT;
@@ -2273,8 +2196,8 @@ static int mos7840_get_icount(struct tty_struct *tty,
icount->brk = cnow.brk;
icount->buf_overrun = cnow.buf_overrun;
- dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __func__,
- port->number, icount->rx, icount->tx);
+ dev_dbg(&port->dev, "%s TIOCGICOUNT RX=%d, TX=%d\n", __func__,
+ icount->rx, icount->tx);
return 0;
}
@@ -2293,35 +2216,33 @@ static int mos7840_ioctl(struct tty_struct *tty,
struct async_icount cnow;
struct async_icount cprev;
- if (mos7840_port_paranoia_check(port, __func__)) {
- dbg("%s", "Invalid port");
+ if (mos7840_port_paranoia_check(port, __func__))
return -1;
- }
mos7840_port = mos7840_get_port_private(port);
if (mos7840_port == NULL)
return -1;
- dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+ dev_dbg(&port->dev, "%s - cmd = 0x%x\n", __func__, cmd);
switch (cmd) {
/* return number of bytes available */
case TIOCSERGETLSR:
- dbg("%s (%d) TIOCSERGETLSR", __func__, port->number);
+ dev_dbg(&port->dev, "%s TIOCSERGETLSR\n", __func__);
return mos7840_get_lsr_info(tty, argp);
case TIOCGSERIAL:
- dbg("%s (%d) TIOCGSERIAL", __func__, port->number);
+ dev_dbg(&port->dev, "%s TIOCGSERIAL\n", __func__);
return mos7840_get_serial_info(mos7840_port, argp);
case TIOCSSERIAL:
- dbg("%s (%d) TIOCSSERIAL", __func__, port->number);
+ dev_dbg(&port->dev, "%s TIOCSSERIAL\n", __func__);
break;
case TIOCMIWAIT:
- dbg("%s (%d) TIOCMIWAIT", __func__, port->number);
+ dev_dbg(&port->dev, "%s TIOCMIWAIT\n", __func__);
cprev = mos7840_port->icount;
while (1) {
/* interruptible_sleep_on(&mos7840_port->delta_msr_wait); */
@@ -2437,11 +2358,6 @@ static int mos7840_startup(struct usb_serial *serial)
int i, status;
__u16 Data;
- if (!serial) {
- dbg("%s", "Invalid Handler");
- return -1;
- }
-
dev = serial->dev;
/* we set up the pointers to the endpoints in the mos7840_open *
@@ -2449,7 +2365,7 @@ static int mos7840_startup(struct usb_serial *serial)
/* set up port private structures */
for (i = 0; i < serial->num_ports; ++i) {
- dbg ("mos7840_startup: configuring port %d............", i);
+ dev_dbg(&dev->dev, "mos7840_startup: configuring port %d............\n", i);
mos7840_port = kzalloc(sizeof(struct moschip_port), GFP_KERNEL);
if (mos7840_port == NULL) {
dev_err(&dev->dev, "%s - Out of memory\n", __func__);
@@ -2470,10 +2386,10 @@ static int mos7840_startup(struct usb_serial *serial)
* usb-serial.c:get_free_serial() and cannot therefore be used
* to index device instances */
mos7840_port->port_num = i + 1;
- dbg ("serial->port[i]->number = %d", serial->port[i]->number);
- dbg ("serial->port[i]->serial->minor = %d", serial->port[i]->serial->minor);
- dbg ("mos7840_port->port_num = %d", mos7840_port->port_num);
- dbg ("serial->minor = %d", serial->minor);
+ dev_dbg(&dev->dev, "serial->port[i]->number = %d\n", serial->port[i]->number);
+ dev_dbg(&dev->dev, "serial->port[i]->serial->minor = %d\n", serial->port[i]->serial->minor);
+ dev_dbg(&dev->dev, "mos7840_port->port_num = %d\n", mos7840_port->port_num);
+ dev_dbg(&dev->dev, "serial->minor = %d\n", serial->minor);
if (mos7840_port->port_num == 1) {
mos7840_port->SpRegOffset = 0x0;
@@ -2500,18 +2416,17 @@ static int mos7840_startup(struct usb_serial *serial)
mos7840_port->ControlRegOffset = 0xd;
mos7840_port->DcrRegOffset = 0x1c;
}
- mos7840_dump_serial_port(mos7840_port);
+ mos7840_dump_serial_port(serial->port[i], mos7840_port);
mos7840_set_port_private(serial->port[i], mos7840_port);
/* enable rx_disable bit in control register */
status = mos7840_get_reg_sync(serial->port[i],
mos7840_port->ControlRegOffset, &Data);
if (status < 0) {
- dbg("Reading ControlReg failed status-0x%x", status);
+ dev_dbg(&dev->dev, "Reading ControlReg failed status-0x%x\n", status);
break;
} else
- dbg("ControlReg Reading success val is %x, status%d",
- Data, status);
+ dev_dbg(&dev->dev, "ControlReg Reading success val is %x, status%d\n", Data, status);
Data |= 0x08; /* setting driver done bit */
Data |= 0x04; /* sp1_bit to have cts change reflect in
modem status reg */
@@ -2520,11 +2435,10 @@ static int mos7840_startup(struct usb_serial *serial)
status = mos7840_set_reg_sync(serial->port[i],
mos7840_port->ControlRegOffset, Data);
if (status < 0) {
- dbg("Writing ControlReg failed(rx_disable) status-0x%x", status);
+ dev_dbg(&dev->dev, "Writing ControlReg failed(rx_disable) status-0x%x\n", status);
break;
} else
- dbg("ControlReg Writing success(rx_disable) status%d",
- status);
+ dev_dbg(&dev->dev, "ControlReg Writing success(rx_disable) status%d\n", status);
/* Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2
and 0x24 in DCR3 */
@@ -2532,61 +2446,57 @@ static int mos7840_startup(struct usb_serial *serial)
status = mos7840_set_reg_sync(serial->port[i],
(__u16) (mos7840_port->DcrRegOffset + 0), Data);
if (status < 0) {
- dbg("Writing DCR0 failed status-0x%x", status);
+ dev_dbg(&dev->dev, "Writing DCR0 failed status-0x%x\n", status);
break;
} else
- dbg("DCR0 Writing success status%d", status);
+ dev_dbg(&dev->dev, "DCR0 Writing success status%d\n", status);
Data = 0x05;
status = mos7840_set_reg_sync(serial->port[i],
(__u16) (mos7840_port->DcrRegOffset + 1), Data);
if (status < 0) {
- dbg("Writing DCR1 failed status-0x%x", status);
+ dev_dbg(&dev->dev, "Writing DCR1 failed status-0x%x\n", status);
break;
} else
- dbg("DCR1 Writing success status%d", status);
+ dev_dbg(&dev->dev, "DCR1 Writing success status%d\n", status);
Data = 0x24;
status = mos7840_set_reg_sync(serial->port[i],
(__u16) (mos7840_port->DcrRegOffset + 2), Data);
if (status < 0) {
- dbg("Writing DCR2 failed status-0x%x", status);
+ dev_dbg(&dev->dev, "Writing DCR2 failed status-0x%x\n", status);
break;
} else
- dbg("DCR2 Writing success status%d", status);
+ dev_dbg(&dev->dev, "DCR2 Writing success status%d\n", status);
/* write values in clkstart0x0 and clkmulti 0x20 */
Data = 0x0;
status = mos7840_set_reg_sync(serial->port[i],
CLK_START_VALUE_REGISTER, Data);
if (status < 0) {
- dbg("Writing CLK_START_VALUE_REGISTER failed status-0x%x", status);
+ dev_dbg(&dev->dev, "Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status);
break;
} else
- dbg("CLK_START_VALUE_REGISTER Writing success status%d", status);
+ dev_dbg(&dev->dev, "CLK_START_VALUE_REGISTER Writing success status%d\n", status);
Data = 0x20;
status = mos7840_set_reg_sync(serial->port[i],
CLK_MULTI_REGISTER, Data);
if (status < 0) {
- dbg("Writing CLK_MULTI_REGISTER failed status-0x%x",
- status);
+ dev_dbg(&dev->dev, "Writing CLK_MULTI_REGISTER failed status-0x%x\n", status);
goto error;
} else
- dbg("CLK_MULTI_REGISTER Writing success status%d",
- status);
+ dev_dbg(&dev->dev, "CLK_MULTI_REGISTER Writing success status%d\n", status);
/* write value 0x0 to scratchpad register */
Data = 0x00;
status = mos7840_set_uart_reg(serial->port[i],
SCRATCH_PAD_REGISTER, Data);
if (status < 0) {
- dbg("Writing SCRATCH_PAD_REGISTER failed status-0x%x",
- status);
+ dev_dbg(&dev->dev, "Writing SCRATCH_PAD_REGISTER failed status-0x%x\n", status);
break;
} else
- dbg("SCRATCH_PAD_REGISTER Writing success status%d",
- status);
+ dev_dbg(&dev->dev, "SCRATCH_PAD_REGISTER Writing success status%d\n", status);
/* Zero Length flag register */
if ((mos7840_port->port_num != 1)
@@ -2596,31 +2506,25 @@ static int mos7840_startup(struct usb_serial *serial)
status = mos7840_set_reg_sync(serial->port[i],
(__u16) (ZLP_REG1 +
((__u16)mos7840_port->port_num)), Data);
- dbg("ZLIP offset %x",
- (__u16) (ZLP_REG1 +
- ((__u16) mos7840_port->port_num)));
+ dev_dbg(&dev->dev, "ZLIP offset %x\n",
+ (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num)));
if (status < 0) {
- dbg("Writing ZLP_REG%d failed status-0x%x",
- i + 2, status);
+ dev_dbg(&dev->dev, "Writing ZLP_REG%d failed status-0x%x\n", i + 2, status);
break;
} else
- dbg("ZLP_REG%d Writing success status%d",
- i + 2, status);
+ dev_dbg(&dev->dev, "ZLP_REG%d Writing success status%d\n", i + 2, status);
} else {
Data = 0xff;
status = mos7840_set_reg_sync(serial->port[i],
(__u16) (ZLP_REG1 +
((__u16)mos7840_port->port_num) - 0x1), Data);
- dbg("ZLIP offset %x",
- (__u16) (ZLP_REG1 +
- ((__u16) mos7840_port->port_num) - 0x1));
+ dev_dbg(&dev->dev, "ZLIP offset %x\n",
+ (__u16)(ZLP_REG1 + ((__u16) mos7840_port->port_num) - 0x1));
if (status < 0) {
- dbg("Writing ZLP_REG%d failed status-0x%x",
- i + 1, status);
+ dev_dbg(&dev->dev, "Writing ZLP_REG%d failed status-0x%x\n", i + 1, status);
break;
} else
- dbg("ZLP_REG%d Writing success status%d",
- i + 1, status);
+ dev_dbg(&dev->dev, "ZLP_REG%d Writing success status%d\n", i + 1, status);
}
mos7840_port->control_urb = usb_alloc_urb(0, GFP_KERNEL);
@@ -2661,16 +2565,15 @@ static int mos7840_startup(struct usb_serial *serial)
MODEM_CONTROL_REGISTER, 0x0300);
}
}
- dbg ("mos7840_startup: all ports configured...........");
/* Zero Length flag enable */
Data = 0x0f;
status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data);
if (status < 0) {
- dbg("Writing ZLP_REG5 failed status-0x%x", status);
+ dev_dbg(&dev->dev, "Writing ZLP_REG5 failed status-0x%x\n", status);
goto error;
} else
- dbg("ZLP_REG5 Writing success status%d", status);
+ dev_dbg(&dev->dev, "ZLP_REG5 Writing success status%d\n", status);
/* setting configuration feature to one */
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
@@ -2700,11 +2603,6 @@ static void mos7840_disconnect(struct usb_serial *serial)
unsigned long flags;
struct moschip_port *mos7840_port;
- if (!serial) {
- dbg("%s", "Invalid Handler");
- return;
- }
-
/* check for the ports to be closed,close the ports and disconnect */
/* free private structure allocated for serial port *
@@ -2712,7 +2610,6 @@ static void mos7840_disconnect(struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
mos7840_port = mos7840_get_port_private(serial->port[i]);
- dbg ("mos7840_port %d = %p", i, mos7840_port);
if (mos7840_port) {
spin_lock_irqsave(&mos7840_port->pool_lock, flags);
mos7840_port->zombie = 1;
@@ -2732,11 +2629,6 @@ static void mos7840_release(struct usb_serial *serial)
int i;
struct moschip_port *mos7840_port;
- if (!serial) {
- dbg("%s", "Invalid Handler");
- return;
- }
-
/* check for the ports to be closed,close the ports and disconnect */
/* free private structure allocated for serial port *
@@ -2744,7 +2636,6 @@ static void mos7840_release(struct usb_serial *serial)
for (i = 0; i < serial->num_ports; ++i) {
mos7840_port = mos7840_get_port_private(serial->port[i]);
- dbg("mos7840_port %d = %p", i, mos7840_port);
if (mos7840_port) {
if (mos7840_port->has_led) {
/* Turn off LED */
@@ -2801,6 +2692,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/navman.c b/drivers/usb/serial/navman.c
index d95452cc076d..1566f8f500ae 100644
--- a/drivers/usb/serial/navman.c
+++ b/drivers/usb/serial/navman.c
@@ -21,8 +21,6 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
-static bool debug;
-
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x0a99, 0x0001) }, /* Talon Technology device */
{ USB_DEVICE(0x0df7, 0x0900) }, /* Mobile Action i-gotU */
@@ -55,8 +53,7 @@ static void navman_read_int_callback(struct urb *urb)
goto exit;
}
- usb_serial_debug_data(debug, &port->dev, __func__,
- urb->actual_length, data);
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
tty = tty_port_tty_get(&port->port);
if (tty && urb->actual_length) {
@@ -123,6 +120,3 @@ static struct usb_serial_driver * const serial_drivers[] = {
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index 6f3d7051c7f4..6def58b79382 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -23,8 +23,6 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
-static bool debug;
-
/*
* Version Information
*/
@@ -164,31 +162,21 @@ static void omninet_read_bulk_callback(struct urb *urb)
struct omninet_header *header = (struct omninet_header *) &data[0];
int status = urb->status;
int result;
- int i;
if (status) {
- dbg("%s - nonzero read bulk status received: %d",
- __func__, status);
+ dev_dbg(&port->dev, "%s - nonzero read bulk status received: %d\n",
+ __func__, status);
return;
}
- if (debug && header->oh_xxx != 0x30) {
- if (urb->actual_length) {
- printk(KERN_DEBUG "%s: omninet_read %d: ",
- __FILE__, header->oh_len);
- for (i = 0; i < (header->oh_len +
- OMNINET_HEADERLEN); i++)
- printk("%.2x ", data[i]);
- printk("\n");
- }
- }
-
if (urb->actual_length && header->oh_len) {
struct tty_struct *tty = tty_port_tty_get(&port->port);
- tty_insert_flip_string(tty, data + OMNINET_DATAOFFSET,
+ if (tty) {
+ tty_insert_flip_string(tty, data + OMNINET_DATAOFFSET,
header->oh_len);
- tty_flip_buffer_push(tty);
- tty_kref_put(tty);
+ tty_flip_buffer_push(tty);
+ tty_kref_put(tty);
+ }
}
/* Continue trying to always read */
@@ -212,12 +200,12 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
int result;
if (count == 0) {
- dbg("%s - write request of 0 bytes", __func__);
+ dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
return 0;
}
if (!test_and_clear_bit(0, &port->write_urbs_free)) {
- dbg("%s - already writing", __func__);
+ dev_dbg(&port->dev, "%s - already writing\n", __func__);
return 0;
}
@@ -226,8 +214,8 @@ static int omninet_write(struct tty_struct *tty, struct usb_serial_port *port,
memcpy(wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET,
buf, count);
- usb_serial_debug_data(debug, &port->dev, __func__, count,
- wport->write_urb->transfer_buffer);
+ usb_serial_debug_data(&port->dev, __func__, count,
+ wport->write_urb->transfer_buffer);
header->oh_seq = od->od_outseq++;
header->oh_len = count;
@@ -261,7 +249,7 @@ static int omninet_write_room(struct tty_struct *tty)
if (test_bit(0, &wport->write_urbs_free))
room = wport->bulk_out_size - OMNINET_HEADERLEN;
- dbg("%s - returns %d", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
return room;
}
@@ -275,8 +263,8 @@ static void omninet_write_bulk_callback(struct urb *urb)
set_bit(0, &port->write_urbs_free);
if (status) {
- dbg("%s - nonzero write bulk status received: %d",
- __func__, status);
+ dev_dbg(&port->dev, "%s - nonzero write bulk status received: %d\n",
+ __func__, status);
return;
}
@@ -304,6 +292,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c
index 02cb1b7f6559..41b1647306eb 100644
--- a/drivers/usb/serial/opticon.c
+++ b/drivers/usb/serial/opticon.c
@@ -32,8 +32,6 @@
* an examples of 1D barcode types are EAN, UPC, Code39, IATA etc.. */
#define DRIVER_DESC "Opticon USB barcode to serial driver (1D)"
-static bool debug;
-
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x065a, 0x0009) },
{ },
@@ -78,17 +76,16 @@ static void opticon_read_bulk_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d",
- __func__, status);
+ dev_dbg(&priv->udev->dev, "%s - urb shutting down with status: %d\n",
+ __func__, status);
return;
default:
- dbg("%s - nonzero urb status received: %d",
- __func__, status);
+ dev_dbg(&priv->udev->dev, "%s - nonzero urb status received: %d\n",
+ __func__, status);
goto exit;
}
- usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length,
- data);
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
if (urb->actual_length > 2) {
data_length = urb->actual_length - 2;
@@ -229,8 +226,8 @@ static void opticon_write_control_callback(struct urb *urb)
kfree(urb->setup_packet);
if (status)
- dbg("%s - nonzero write bulk status received: %d",
- __func__, status);
+ dev_dbg(&priv->udev->dev, "%s - nonzero write bulk status received: %d\n",
+ __func__, status);
spin_lock_irqsave(&priv->lock, flags);
--priv->outstanding_urbs;
@@ -253,7 +250,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
spin_lock_irqsave(&priv->lock, flags);
if (priv->outstanding_urbs > URB_UPPER_LIMIT) {
spin_unlock_irqrestore(&priv->lock, flags);
- dbg("%s - write limit hit", __func__);
+ dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
return 0;
}
priv->outstanding_urbs++;
@@ -276,7 +273,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port,
memcpy(buffer, buf, count);
- usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
+ usb_serial_debug_data(&port->dev, __func__, count, buffer);
/* The conncected devices do not have a bulk write endpoint,
* to transmit data to de barcode device the control endpoint is used */
@@ -338,7 +335,7 @@ static int opticon_write_room(struct tty_struct *tty)
spin_lock_irqsave(&priv->lock, flags);
if (priv->outstanding_urbs > URB_UPPER_LIMIT * 2 / 3) {
spin_unlock_irqrestore(&priv->lock, flags);
- dbg("%s - write limit hit", __func__);
+ dev_dbg(&port->dev, "%s - write limit hit\n", __func__);
return 0;
}
spin_unlock_irqrestore(&priv->lock, flags);
@@ -394,7 +391,7 @@ static int opticon_tiocmget(struct tty_struct *tty)
result |= TIOCM_CTS;
spin_unlock_irqrestore(&priv->lock, flags);
- dbg("%s - %x", __func__, result);
+ dev_dbg(&port->dev, "%s - %x\n", __func__, result);
return result;
}
@@ -466,7 +463,7 @@ static int opticon_ioctl(struct tty_struct *tty,
struct usb_serial_port *port = tty->driver_data;
struct opticon_private *priv = usb_get_serial_data(port->serial);
- dbg("%s - port %d, cmd = 0x%x", __func__, port->number, cmd);
+ dev_dbg(&port->dev, "%s - port %d, cmd = 0x%x\n", __func__, port->number, cmd);
switch (cmd) {
case TIOCGSERIAL:
@@ -612,6 +609,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 5ce88d1bc6f1..30cff03e9f01 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -870,7 +870,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0153, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0155, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0156, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0157, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf5_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0158, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0159, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x0161, 0xff, 0xff, 0xff) },
@@ -1257,8 +1258,6 @@ static struct usb_serial_driver * const serial_drivers[] = {
&option_1port_device, NULL
};
-static bool debug;
-
struct option_private {
u8 bInterfaceNumber;
};
@@ -1369,18 +1368,19 @@ static void option_instat_callback(struct urb *urb)
{
int err;
int status = urb->status;
- struct usb_serial_port *port = urb->context;
+ struct usb_serial_port *port = urb->context;
+ struct device *dev = &port->dev;
struct usb_wwan_port_private *portdata =
usb_get_serial_port_data(port);
- dbg("%s: urb %p port %p has data %p", __func__, urb, port, portdata);
+ dev_dbg(dev, "%s: urb %p port %p has data %p\n", __func__, urb, port, portdata);
if (status == 0) {
struct usb_ctrlrequest *req_pkt =
(struct usb_ctrlrequest *)urb->transfer_buffer;
if (!req_pkt) {
- dbg("%s: NULL req_pkt", __func__);
+ dev_dbg(dev, "%s: NULL req_pkt\n", __func__);
return;
}
if ((req_pkt->bRequestType == 0xA1) &&
@@ -1390,7 +1390,7 @@ static void option_instat_callback(struct urb *urb)
urb->transfer_buffer +
sizeof(struct usb_ctrlrequest));
- dbg("%s: signal x%x", __func__, signals);
+ dev_dbg(dev, "%s: signal x%x\n", __func__, signals);
old_dcd_state = portdata->dcd_state;
portdata->cts_state = 1;
@@ -1406,17 +1406,17 @@ static void option_instat_callback(struct urb *urb)
tty_kref_put(tty);
}
} else {
- dbg("%s: type %x req %x", __func__,
+ dev_dbg(dev, "%s: type %x req %x\n", __func__,
req_pkt->bRequestType, req_pkt->bRequest);
}
} else
- dev_err(&port->dev, "%s: error %d\n", __func__, status);
+ dev_err(dev, "%s: error %d\n", __func__, status);
/* Resubmit urb so we continue receiving IRQ data */
if (status != -ESHUTDOWN && status != -ENOENT) {
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err)
- dbg("%s: resubmit intr urb failed. (%d)",
+ dev_dbg(dev, "%s: resubmit intr urb failed. (%d)\n",
__func__, err);
}
}
@@ -1450,6 +1450,3 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug messages");
diff --git a/drivers/usb/serial/oti6858.c b/drivers/usb/serial/oti6858.c
index 9f555560bfbf..933241f03fd8 100644
--- a/drivers/usb/serial/oti6858.c
+++ b/drivers/usb/serial/oti6858.c
@@ -66,8 +66,6 @@ static const struct usb_device_id id_table[] = {
MODULE_DEVICE_TABLE(usb, id_table);
-static bool debug;
-
/* requests */
#define OTI6858_REQ_GET_STATUS (USB_DIR_IN | USB_TYPE_VENDOR | 0x00)
#define OTI6858_REQ_T_GET_STATUS 0x01
@@ -256,11 +254,11 @@ static void setup_line(struct work_struct *work)
priv->setup_done = 1;
spin_unlock_irqrestore(&priv->lock, flags);
- dbg("%s(): submitting interrupt urb", __func__);
+ dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result != 0) {
- dev_err(&port->dev, "%s(): usb_submit_urb() failed"
- " with error %d\n", __func__, result);
+ dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n",
+ __func__, result);
}
}
@@ -310,11 +308,11 @@ static void send_data(struct work_struct *work)
if (count == 0) {
priv->flags.write_urb_in_use = 0;
- dbg("%s(): submitting interrupt urb", __func__);
+ dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
result = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
if (result != 0) {
- dev_err(&port->dev, "%s(): usb_submit_urb() failed"
- " with error %d\n", __func__, result);
+ dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n",
+ __func__, result);
}
return;
}
@@ -325,8 +323,8 @@ static void send_data(struct work_struct *work)
port->write_urb->transfer_buffer_length = count;
result = usb_submit_urb(port->write_urb, GFP_NOIO);
if (result != 0) {
- dev_err_console(port, "%s(): usb_submit_urb() failed"
- " with error %d\n", __func__, result);
+ dev_err_console(port, "%s(): usb_submit_urb() failed with error %d\n",
+ __func__, result);
priv->flags.write_urb_in_use = 0;
}
@@ -420,10 +418,8 @@ static void oti6858_set_termios(struct tty_struct *tty,
__le16 divisor;
int br;
- if (!tty) {
- dbg("%s(): no tty structures", __func__);
+ if (!tty)
return;
- }
cflag = tty->termios.c_cflag;
@@ -560,11 +556,11 @@ static int oti6858_open(struct tty_struct *tty, struct usb_serial_port *port)
spin_unlock_irqrestore(&priv->lock, flags);
kfree(buf);
- dbg("%s(): submitting interrupt urb", __func__);
+ dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result != 0) {
- dev_err(&port->dev, "%s(): usb_submit_urb() failed"
- " with error %d\n", __func__, result);
+ dev_err(&port->dev, "%s(): usb_submit_urb() failed with error %d\n",
+ __func__, result);
oti6858_close(port);
return result;
}
@@ -586,14 +582,14 @@ static void oti6858_close(struct usb_serial_port *port)
kfifo_reset_out(&port->write_fifo);
spin_unlock_irqrestore(&port->lock, flags);
- dbg("%s(): after buf_clear()", __func__);
+ dev_dbg(&port->dev, "%s(): after buf_clear()\n", __func__);
/* cancel scheduled setup */
cancel_delayed_work_sync(&priv->delayed_setup_work);
cancel_delayed_work_sync(&priv->delayed_write_work);
/* shutdown our urbs */
- dbg("%s(): shutting down urbs", __func__);
+ dev_dbg(&port->dev, "%s(): shutting down urbs\n", __func__);
usb_kill_urb(port->write_urb);
usb_kill_urb(port->read_urb);
usb_kill_urb(port->interrupt_in_urb);
@@ -607,8 +603,8 @@ static int oti6858_tiocmset(struct tty_struct *tty,
unsigned long flags;
u8 control;
- dbg("%s(port = %d, set = 0x%08x, clear = 0x%08x)",
- __func__, port->number, set, clear);
+ dev_dbg(&port->dev, "%s(set = 0x%08x, clear = 0x%08x)\n",
+ __func__, set, clear);
/* FIXME: check if this is correct (active high/low) */
spin_lock_irqsave(&priv->lock, flags);
@@ -655,7 +651,7 @@ static int oti6858_tiocmget(struct tty_struct *tty)
if ((pin_state & PIN_DCD) != 0)
result |= TIOCM_CD;
- dbg("%s() = 0x%08x", __func__, result);
+ dev_dbg(&port->dev, "%s() = 0x%08x\n", __func__, result);
return result;
}
@@ -700,15 +696,14 @@ static int oti6858_ioctl(struct tty_struct *tty,
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s(port = %d, cmd = 0x%04x, arg = 0x%08lx)",
- __func__, port->number, cmd, arg);
+ dev_dbg(&port->dev, "%s(cmd = 0x%04x, arg = 0x%08lx)\n", __func__, cmd, arg);
switch (cmd) {
case TIOCMIWAIT:
- dbg("%s(): TIOCMIWAIT", __func__);
+ dev_dbg(&port->dev, "%s(): TIOCMIWAIT\n", __func__);
return wait_modem_info(port, arg);
default:
- dbg("%s(): 0x%04x not supported", __func__, cmd);
+ dev_dbg(&port->dev, "%s(): 0x%04x not supported\n", __func__, cmd);
break;
}
return -ENOIOCTLCMD;
@@ -738,12 +733,12 @@ static void oti6858_read_int_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s(): urb shutting down with status: %d",
- __func__, status);
+ dev_dbg(&urb->dev->dev, "%s(): urb shutting down with status: %d\n",
+ __func__, status);
return;
default:
- dbg("%s(): nonzero urb status received: %d",
- __func__, status);
+ dev_dbg(&urb->dev->dev, "%s(): nonzero urb status received: %d\n",
+ __func__, status);
break;
}
@@ -759,8 +754,7 @@ static void oti6858_read_int_callback(struct urb *urb)
priv->transient = 4;
priv->setup_done = 0;
resubmit = 0;
- dbg("%s(): scheduling setup_line()",
- __func__);
+ dev_dbg(&port->dev, "%s(): scheduling setup_line()\n", __func__);
schedule_delayed_work(&priv->delayed_setup_work, 0);
}
}
@@ -774,8 +768,7 @@ static void oti6858_read_int_callback(struct urb *urb)
priv->transient = 4;
priv->setup_done = 0;
resubmit = 0;
- dbg("%s(): scheduling setup_line()",
- __func__);
+ dev_dbg(&port->dev, "%s(): scheduling setup_line()\n", __func__);
schedule_delayed_work(&priv->delayed_setup_work, 0);
}
}
@@ -826,7 +819,7 @@ static void oti6858_read_int_callback(struct urb *urb)
if (resubmit) {
int result;
-/* dbg("%s(): submitting interrupt urb", __func__); */
+/* dev_dbg(&urb->dev->dev, "%s(): submitting interrupt urb\n", __func__); */
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result != 0) {
dev_err(&urb->dev->dev,
@@ -851,7 +844,7 @@ static void oti6858_read_bulk_callback(struct urb *urb)
spin_unlock_irqrestore(&priv->lock, flags);
if (status != 0) {
- dbg("%s(): unable to handle the error, exiting", __func__);
+ dev_dbg(&urb->dev->dev, "%s(): unable to handle the error, exiting\n", __func__);
return;
}
@@ -885,15 +878,13 @@ static void oti6858_write_bulk_callback(struct urb *urb)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s(): urb shutting down with status: %d",
- __func__, status);
+ dev_dbg(&urb->dev->dev, "%s(): urb shutting down with status: %d\n", __func__, status);
priv->flags.write_urb_in_use = 0;
return;
default:
/* error in the urb, so we have to resubmit it */
- dbg("%s(): nonzero write bulk status received: %d",
- __func__, status);
- dbg("%s(): overflow in write", __func__);
+ dev_dbg(&urb->dev->dev, "%s(): nonzero write bulk status received: %d\n", __func__, status);
+ dev_dbg(&urb->dev->dev, "%s(): overflow in write\n", __func__);
port->write_urb->transfer_buffer_length = 1;
result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
@@ -908,7 +899,7 @@ static void oti6858_write_bulk_callback(struct urb *urb)
priv->flags.write_urb_in_use = 0;
/* schedule the interrupt urb if we are still open */
- dbg("%s(): submitting interrupt urb", __func__);
+ dev_dbg(&port->dev, "%s(): submitting interrupt urb\n", __func__);
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
if (result != 0) {
dev_err(&port->dev, "%s(): failed submitting int urb,"
@@ -922,7 +913,3 @@ MODULE_DESCRIPTION(OTI6858_DESCRIPTION);
MODULE_AUTHOR(OTI6858_AUTHOR);
MODULE_VERSION(OTI6858_VERSION);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "enable debug output");
-
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 2b9108a8ea64..892ebdc7a364 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -36,8 +36,6 @@
*/
#define DRIVER_DESC "Prolific PL2303 USB to serial adaptor driver"
-static bool debug;
-
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID) },
{ USB_DEVICE(PL2303_VENDOR_ID, PL2303_PRODUCT_ID_RSAQ2) },
@@ -741,7 +739,7 @@ static void pl2303_read_int_callback(struct urb *urb)
goto exit;
}
- usb_serial_debug_data(debug, &port->dev, __func__,
+ usb_serial_debug_data(&port->dev, __func__,
urb->actual_length, urb->transfer_buffer);
pl2303_update_line_status(port, data, actual_length);
@@ -839,7 +837,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
-
diff --git a/drivers/usb/serial/qcaux.c b/drivers/usb/serial/qcaux.c
index a4edc7ee9c8a..9b1b96f2d095 100644
--- a/drivers/usb/serial/qcaux.c
+++ b/drivers/usb/serial/qcaux.c
@@ -36,8 +36,6 @@
#define UTSTARCOM_PRODUCT_UM175_V1 0x3712
#define UTSTARCOM_PRODUCT_UM175_V2 0x3714
#define UTSTARCOM_PRODUCT_UM175_ALLTEL 0x3715
-#define PANTECH_PRODUCT_UML190_VZW 0x3716
-#define PANTECH_PRODUCT_UML290_VZW 0x3718
/* CMOTECH devices */
#define CMOTECH_VENDOR_ID 0x16d8
@@ -68,11 +66,9 @@ static struct usb_device_id id_table[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(LG_VENDOR_ID, LG_PRODUCT_VX4400_6000, 0xff, 0xff, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(SANYO_VENDOR_ID, SANYO_PRODUCT_KATANA_LX, 0xff, 0xff, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(SAMSUNG_VENDOR_ID, SAMSUNG_PRODUCT_U520, 0xff, 0x00, 0x00) },
- { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML190_VZW, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML190_VZW, 0xff, 0xfe, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xfd, 0xff) }, /* NMEA */
- { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xfe, 0xff) }, /* WMC */
- { USB_DEVICE_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, PANTECH_PRODUCT_UML290_VZW, 0xff, 0xff, 0xff) }, /* DIAG */
+ { USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xfd, 0xff) }, /* NMEA */
+ { USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xfe, 0xff) }, /* WMC */
+ { USB_VENDOR_AND_INTERFACE_INFO(UTSTARCOM_VENDOR_ID, 0xff, 0xff, 0xff) }, /* DIAG */
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index bfd50779f0c9..c3ddb65c05f2 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -22,8 +22,6 @@
#define DRIVER_AUTHOR "Qualcomm Inc"
#define DRIVER_DESC "Qualcomm USB Serial driver"
-static bool debug;
-
#define DEVICE_G1K(v, p) \
USB_DEVICE(v, p), .driver_info = 1
@@ -305,6 +303,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL v2");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
index 7df9cdb053ed..2cdfdcc90b37 100644
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -27,8 +27,6 @@
#include <linux/serial_reg.h>
#include <linux/uaccess.h>
-static bool debug;
-
/* default urb timeout for usb operations */
#define QT2_USB_TIMEOUT USB_CTRL_SET_TIMEOUT
@@ -1089,7 +1087,7 @@ static int qt2_write(struct tty_struct *tty,
data = write_urb->transfer_buffer;
spin_lock_irqsave(&port_priv->urb_lock, flags);
if (port_priv->urb_in_use == true) {
- printk(KERN_INFO "qt2_write - urb is in use\n");
+ dev_err(&port->dev, "qt2_write - urb is in use\n");
goto write_out;
}
@@ -1146,6 +1144,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index 36e9d9fc0618..c949ce6ef0c6 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -62,6 +62,7 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h>
#include <linux/errno.h>
@@ -81,11 +82,9 @@
#define CONFIG_USB_SERIAL_SAFE_PADDED 0
#endif
-static bool debug;
static bool safe = 1;
static bool padded = CONFIG_USB_SERIAL_SAFE_PADDED;
-#define DRIVER_VERSION "v0.1"
#define DRIVER_AUTHOR "sl@lineo.com, tbr@lineo.com, Johan Hovold <jhovold@gmail.com>"
#define DRIVER_DESC "USB Safe Encapsulated Serial"
@@ -100,9 +99,6 @@ MODULE_PARM_DESC(vendor, "User specified USB idVendor (required)");
module_param(product, ushort, 0);
MODULE_PARM_DESC(product, "User specified USB idProduct (required)");
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
-
module_param(safe, bool, 0);
MODULE_PARM_DESC(safe, "Turn Safe Encapsulation On/Off");
@@ -315,13 +311,9 @@ static int __init safe_init(void)
{
int i;
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
- DRIVER_DESC "\n");
-
/* if we have vendor / product parameters patch them into id list */
if (vendor || product) {
- printk(KERN_INFO KBUILD_MODNAME ": vendor: %x product: %x\n",
- vendor, product);
+ pr_info("vendor: %x product: %x\n", vendor, product);
for (i = 0; i < ARRAY_SIZE(id_table); i++) {
if (!id_table[i].idVendor && !id_table[i].idProduct) {
diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c
index b14ebbd73567..01d882cf3775 100644
--- a/drivers/usb/serial/sierra.c
+++ b/drivers/usb/serial/sierra.c
@@ -46,7 +46,6 @@
allocations > PAGE_SIZE and the number of packets in a page
is an integer 512 is the largest possible packet on EHCI */
-static bool debug;
static bool nmea;
/* Used in interface blacklisting */
@@ -518,7 +517,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port,
memcpy(buffer, buf, writesize);
- usb_serial_debug_data(debug, &port->dev, __func__, writesize, buffer);
+ usb_serial_debug_data(&port->dev, __func__, writesize, buffer);
usb_fill_bulk_urb(urb, serial->dev,
usb_sndbulkpipe(serial->dev,
@@ -595,8 +594,8 @@ static void sierra_indat_callback(struct urb *urb)
tty_flip_buffer_push(tty);
tty_kref_put(tty);
- usb_serial_debug_data(debug, &port->dev,
- __func__, urb->actual_length, data);
+ usb_serial_debug_data(&port->dev, __func__,
+ urb->actual_length, data);
}
} else {
dev_dbg(&port->dev, "%s: empty read urb"
@@ -765,7 +764,6 @@ static struct urb *sierra_setup_urb(struct usb_serial *serial, int endpoint,
usb_sndbulkpipe(serial->dev, endpoint) | dir,
buf, len, callback, ctx);
- /* debug */
dev_dbg(&serial->dev->dev, "%s %c u : %p d:%p\n", __func__,
dir == USB_DIR_IN ? 'i' : 'o', urb, buf);
} else {
@@ -1082,6 +1080,3 @@ MODULE_LICENSE("GPL");
module_param(nmea, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(nmea, "NMEA streaming");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug messages");
diff --git a/drivers/usb/serial/spcp8x5.c b/drivers/usb/serial/spcp8x5.c
index ab68a4d74d61..9716efe92955 100644
--- a/drivers/usb/serial/spcp8x5.c
+++ b/drivers/usb/serial/spcp8x5.c
@@ -33,8 +33,6 @@
#define DRIVER_VERSION "v0.10"
#define DRIVER_DESC "SPCP8x5 USB to serial adaptor driver"
-static bool debug;
-
#define SPCP8x5_007_VID 0x04FC
#define SPCP8x5_007_PID 0x0201
#define SPCP8x5_008_VID 0x04fc
@@ -665,6 +663,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
index cf2d30cf7588..015810b3785b 100644
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -46,8 +46,6 @@
#define FULLPWRBIT 0x00000080
#define NEXT_BOARD_POWER_BIT 0x00000004
-static bool debug;
-
/* Version Information */
#define DRIVER_VERSION "v0.1"
#define DRIVER_DESC "Quatech SSU-100 USB to Serial Driver"
@@ -135,7 +133,7 @@ static inline int update_mctrl(struct usb_device *dev, unsigned int set,
int result;
if (((set | clear) & (TIOCM_DTR | TIOCM_RTS)) == 0) {
- dbg("%s - DTR|RTS not being set|cleared", __func__);
+ dev_dbg(&dev->dev, "%s - DTR|RTS not being set|cleared\n", __func__);
return 0; /* no change */
}
@@ -148,7 +146,7 @@ static inline int update_mctrl(struct usb_device *dev, unsigned int set,
result = ssu100_setregister(dev, 0, UART_MCR, urb_value);
if (result < 0)
- dbg("%s Error from MODEM_CTRL urb", __func__);
+ dev_dbg(&dev->dev, "%s Error from MODEM_CTRL urb\n", __func__);
return result;
}
@@ -164,7 +162,7 @@ static int ssu100_initdevice(struct usb_device *dev)
result = ssu100_getdevice(dev, data);
if (result < 0) {
- dbg("%s - get_device failed %i", __func__, result);
+ dev_dbg(&dev->dev, "%s - get_device failed %i\n", __func__, result);
goto out;
}
@@ -172,25 +170,25 @@ static int ssu100_initdevice(struct usb_device *dev)
result = ssu100_setdevice(dev, data);
if (result < 0) {
- dbg("%s - setdevice failed %i", __func__, result);
+ dev_dbg(&dev->dev, "%s - setdevice failed %i\n", __func__, result);
goto out;
}
result = ssu100_control_msg(dev, QT_GET_SET_PREBUF_TRIG_LVL, 128, 0);
if (result < 0) {
- dbg("%s - set prebuffer level failed %i", __func__, result);
+ dev_dbg(&dev->dev, "%s - set prebuffer level failed %i\n", __func__, result);
goto out;
}
result = ssu100_control_msg(dev, QT_SET_ATF, ATC_DISABLED, 0);
if (result < 0) {
- dbg("%s - set ATFprebuffer level failed %i", __func__, result);
+ dev_dbg(&dev->dev, "%s - set ATFprebuffer level failed %i\n", __func__, result);
goto out;
}
result = ssu100_getdevice(dev, data);
if (result < 0) {
- dbg("%s - get_device failed %i", __func__, result);
+ dev_dbg(&dev->dev, "%s - get_device failed %i\n", __func__, result);
goto out;
}
@@ -201,7 +199,7 @@ static int ssu100_initdevice(struct usb_device *dev)
result = ssu100_setdevice(dev, data);
if (result < 0) {
- dbg("%s - setdevice failed %i", __func__, result);
+ dev_dbg(&dev->dev, "%s - setdevice failed %i\n", __func__, result);
goto out;
}
@@ -249,7 +247,7 @@ static void ssu100_set_termios(struct tty_struct *tty,
if (!baud)
baud = 9600;
- dbg("%s - got baud = %d\n", __func__, baud);
+ dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud);
divisor = MAX_BAUD_RATE / baud;
@@ -261,7 +259,7 @@ static void ssu100_set_termios(struct tty_struct *tty,
result = ssu100_control_msg(dev, QT_GET_SET_UART, divisor, urb_value);
if (result < 0)
- dbg("%s - set uart failed", __func__);
+ dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
if (cflag & CRTSCTS)
result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
@@ -270,7 +268,7 @@ static void ssu100_set_termios(struct tty_struct *tty,
result = ssu100_control_msg(dev, QT_HW_FLOW_CONTROL_MASK,
0, 0);
if (result < 0)
- dbg("%s - set HW flow control failed", __func__);
+ dev_dbg(&port->dev, "%s - set HW flow control failed\n", __func__);
if (I_IXOFF(tty) || I_IXON(tty)) {
u16 x = ((u16)(START_CHAR(tty) << 8) | (u16)(STOP_CHAR(tty)));
@@ -282,7 +280,7 @@ static void ssu100_set_termios(struct tty_struct *tty,
0, 0);
if (result < 0)
- dbg("%s - set SW flow control failed", __func__);
+ dev_dbg(&port->dev, "%s - set SW flow control failed\n", __func__);
}
@@ -304,7 +302,7 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
QT_TRANSFER_IN, 0x01,
0, data, 2, 300);
if (result < 0) {
- dbg("%s - open failed %i", __func__, result);
+ dev_dbg(&port->dev, "%s - open failed %i\n", __func__, result);
kfree(data);
return result;
}
@@ -319,7 +317,7 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
/* set to 9600 */
result = ssu100_control_msg(dev, QT_GET_SET_UART, 0x30, 0x0300);
if (result < 0)
- dbg("%s - set uart failed", __func__);
+ dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
if (tty)
ssu100_set_termios(tty, port, &tty->termios);
@@ -423,7 +421,7 @@ static int ssu100_ioctl(struct tty_struct *tty,
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s cmd 0x%04x", __func__, cmd);
+ dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
switch (cmd) {
case TIOCGSERIAL:
@@ -437,7 +435,7 @@ static int ssu100_ioctl(struct tty_struct *tty,
break;
}
- dbg("%s arg not supported", __func__);
+ dev_dbg(&port->dev, "%s arg not supported\n", __func__);
return -ENOIOCTLCMD;
}
@@ -668,6 +666,3 @@ module_usb_serial_driver(serial_drivers, id_table);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/symbolserial.c b/drivers/usb/serial/symbolserial.c
index e53d2aac35c5..701fffa8431f 100644
--- a/drivers/usb/serial/symbolserial.c
+++ b/drivers/usb/serial/symbolserial.c
@@ -20,8 +20,6 @@
#include <linux/usb/serial.h>
#include <linux/uaccess.h>
-static bool debug;
-
static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x05e0, 0x0600) },
{ },
@@ -71,8 +69,7 @@ static void symbol_int_callback(struct urb *urb)
goto exit;
}
- usb_serial_debug_data(debug, &port->dev, __func__, urb->actual_length,
- data);
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
if (urb->actual_length > 1) {
data_length = urb->actual_length - 1;
@@ -292,6 +289,3 @@ static struct usb_serial_driver * const serial_drivers[] = {
module_usb_serial_driver(serial_drivers, id_table);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/ti_usb_3410_5052.c b/drivers/usb/serial/ti_usb_3410_5052.c
index f502a16aac21..6f49392cda5b 100644
--- a/drivers/usb/serial/ti_usb_3410_5052.c
+++ b/drivers/usb/serial/ti_usb_3410_5052.c
@@ -40,7 +40,6 @@
/* Defines */
-#define TI_DRIVER_VERSION "v0.10"
#define TI_DRIVER_AUTHOR "Al Borchers <alborchers@steinerpoint.com>"
#define TI_DRIVER_DESC "TI USB 3410/5052 Serial Driver"
@@ -141,8 +140,8 @@ static int ti_command_out_sync(struct ti_device *tdev, __u8 command,
static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
__u16 moduleid, __u16 value, __u8 *data, int size);
-static int ti_write_byte(struct ti_device *tdev, unsigned long addr,
- __u8 mask, __u8 byte);
+static int ti_write_byte(struct usb_serial_port *port, struct ti_device *tdev,
+ unsigned long addr, __u8 mask, __u8 byte);
static int ti_download_firmware(struct ti_device *tdev);
@@ -150,7 +149,6 @@ static int ti_download_firmware(struct ti_device *tdev);
/* Data */
/* module parameters */
-static bool debug;
static int closing_wait = TI_DEFAULT_CLOSING_WAIT;
static ushort vendor_3410[TI_EXTRA_VID_PID_COUNT];
static unsigned int vendor_3410_count;
@@ -277,7 +275,6 @@ static struct usb_serial_driver * const serial_drivers[] = {
MODULE_AUTHOR(TI_DRIVER_AUTHOR);
MODULE_DESCRIPTION(TI_DRIVER_DESC);
-MODULE_VERSION(TI_DRIVER_VERSION);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("ti_3410.fw");
@@ -288,9 +285,6 @@ MODULE_FIRMWARE("mts_edge.fw");
MODULE_FIRMWARE("mts_mt9234mu.fw");
MODULE_FIRMWARE("mts_mt9234zba.fw");
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Enable debugging, 0=no, 1=yes");
-
module_param(closing_wait, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(closing_wait,
"Maximum wait for data to drain in close, in .01 secs, default is 4000");
@@ -316,7 +310,6 @@ MODULE_DEVICE_TABLE(usb, ti_id_table_combined);
static int __init ti_init(void)
{
int i, j, c;
- int ret;
/* insert extra vendor and product ids */
c = ARRAY_SIZE(ti_id_table_combined) - 2 * TI_EXTRA_VID_PID_COUNT - 1;
@@ -339,11 +332,7 @@ static int __init ti_init(void)
ti_id_table_combined[c].match_flags = USB_DEVICE_ID_MATCH_DEVICE;
}
- ret = usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, ti_id_table_combined);
- if (ret == 0)
- printk(KERN_INFO KBUILD_MODNAME ": " TI_DRIVER_VERSION ":"
- TI_DRIVER_DESC "\n");
- return ret;
+ return usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME, ti_id_table_combined);
}
static void __exit ti_exit(void)
@@ -364,10 +353,11 @@ static int ti_startup(struct usb_serial *serial)
int i;
- dbg("%s - product 0x%4X, num configurations %d, configuration value %d",
- __func__, le16_to_cpu(dev->descriptor.idProduct),
- dev->descriptor.bNumConfigurations,
- dev->actconfig->desc.bConfigurationValue);
+ dev_dbg(&dev->dev,
+ "%s - product 0x%4X, num configurations %d, configuration value %d",
+ __func__, le16_to_cpu(dev->descriptor.idProduct),
+ dev->descriptor.bNumConfigurations,
+ dev->actconfig->desc.bConfigurationValue);
/* create device structure */
tdev = kzalloc(sizeof(struct ti_device), GFP_KERNEL);
@@ -382,8 +372,8 @@ static int ti_startup(struct usb_serial *serial)
/* determine device type */
if (usb_match_id(serial->interface, ti_id_table_3410))
tdev->td_is_3410 = 1;
- dbg("%s - device type is %s", __func__,
- tdev->td_is_3410 ? "3410" : "5052");
+ dev_dbg(&dev->dev, "%s - device type is %s\n", __func__,
+ tdev->td_is_3410 ? "3410" : "5052");
/* if we have only 1 configuration, download firmware */
if (dev->descriptor.bNumConfigurations == 1) {
@@ -501,20 +491,17 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
/* start interrupt urb the first time a port is opened on this device */
if (tdev->td_open_port_count == 0) {
- dbg("%s - start interrupt in urb", __func__);
+ dev_dbg(&port->dev, "%s - start interrupt in urb\n", __func__);
urb = tdev->td_serial->port[0]->interrupt_in_urb;
if (!urb) {
- dev_err(&port->dev, "%s - no interrupt urb\n",
- __func__);
+ dev_err(&port->dev, "%s - no interrupt urb\n", __func__);
status = -EINVAL;
goto release_lock;
}
urb->context = tdev;
status = usb_submit_urb(urb, GFP_KERNEL);
if (status) {
- dev_err(&port->dev,
- "%s - submit interrupt urb failed, %d\n",
- __func__, status);
+ dev_err(&port->dev, "%s - submit interrupt urb failed, %d\n", __func__, status);
goto release_lock;
}
}
@@ -522,16 +509,16 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
if (tty)
ti_set_termios(tty, port, &tty->termios);
- dbg("%s - sending TI_OPEN_PORT", __func__);
+ dev_dbg(&port->dev, "%s - sending TI_OPEN_PORT\n", __func__);
status = ti_command_out_sync(tdev, TI_OPEN_PORT,
(__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0);
if (status) {
dev_err(&port->dev, "%s - cannot send open command, %d\n",
- __func__, status);
+ __func__, status);
goto unlink_int_urb;
}
- dbg("%s - sending TI_START_PORT", __func__);
+ dev_dbg(&port->dev, "%s - sending TI_START_PORT\n", __func__);
status = ti_command_out_sync(tdev, TI_START_PORT,
(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
if (status) {
@@ -540,7 +527,7 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
goto unlink_int_urb;
}
- dbg("%s - sending TI_PURGE_PORT", __func__);
+ dev_dbg(&port->dev, "%s - sending TI_PURGE_PORT\n", __func__);
status = ti_command_out_sync(tdev, TI_PURGE_PORT,
(__u8)(TI_UART1_PORT + port_number), TI_PURGE_INPUT, NULL, 0);
if (status) {
@@ -564,7 +551,7 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
if (tty)
ti_set_termios(tty, port, &tty->termios);
- dbg("%s - sending TI_OPEN_PORT (2)", __func__);
+ dev_dbg(&port->dev, "%s - sending TI_OPEN_PORT (2)\n", __func__);
status = ti_command_out_sync(tdev, TI_OPEN_PORT,
(__u8)(TI_UART1_PORT + port_number), open_settings, NULL, 0);
if (status) {
@@ -573,7 +560,7 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
goto unlink_int_urb;
}
- dbg("%s - sending TI_START_PORT (2)", __func__);
+ dev_dbg(&port->dev, "%s - sending TI_START_PORT (2)\n", __func__);
status = ti_command_out_sync(tdev, TI_START_PORT,
(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
if (status) {
@@ -583,7 +570,7 @@ static int ti_open(struct tty_struct *tty, struct usb_serial_port *port)
}
/* start read urb */
- dbg("%s - start read urb", __func__);
+ dev_dbg(&port->dev, "%s - start read urb\n", __func__);
urb = port->read_urb;
if (!urb) {
dev_err(&port->dev, "%s - no read urb\n", __func__);
@@ -609,7 +596,7 @@ unlink_int_urb:
usb_kill_urb(port->serial->port[0]->interrupt_in_urb);
release_lock:
mutex_unlock(&tdev->td_open_close_lock);
- dbg("%s - exit %d", __func__, status);
+ dev_dbg(&port->dev, "%s - exit %d\n", __func__, status);
return status;
}
@@ -637,7 +624,7 @@ static void ti_close(struct usb_serial_port *port)
port_number = port->number - port->serial->minor;
- dbg("%s - sending TI_CLOSE_PORT", __func__);
+ dev_dbg(&port->dev, "%s - sending TI_CLOSE_PORT\n", __func__);
status = ti_command_out_sync(tdev, TI_CLOSE_PORT,
(__u8)(TI_UART1_PORT + port_number), 0, NULL, 0);
if (status)
@@ -664,7 +651,7 @@ static int ti_write(struct tty_struct *tty, struct usb_serial_port *port,
struct ti_port *tport = usb_get_serial_port_data(port);
if (count == 0) {
- dbg("%s - write request of 0 bytes", __func__);
+ dev_dbg(&port->dev, "%s - write request of 0 bytes\n", __func__);
return 0;
}
@@ -693,7 +680,7 @@ static int ti_write_room(struct tty_struct *tty)
room = kfifo_avail(&tport->write_fifo);
spin_unlock_irqrestore(&tport->tp_lock, flags);
- dbg("%s - returns %d", __func__, room);
+ dev_dbg(&port->dev, "%s - returns %d\n", __func__, room);
return room;
}
@@ -712,7 +699,7 @@ static int ti_chars_in_buffer(struct tty_struct *tty)
chars = kfifo_len(&tport->write_fifo);
spin_unlock_irqrestore(&tport->tp_lock, flags);
- dbg("%s - returns %d", __func__, chars);
+ dev_dbg(&port->dev, "%s - returns %d\n", __func__, chars);
return chars;
}
@@ -755,8 +742,7 @@ static int ti_get_icount(struct tty_struct *tty,
struct ti_port *tport = usb_get_serial_port_data(port);
struct async_icount cnow = tport->tp_icount;
- dbg("%s - (%d) TIOCGICOUNT RX=%d, TX=%d",
- __func__, port->number,
+ dev_dbg(&port->dev, "%s - TIOCGICOUNT RX=%d, TX=%d\n", __func__,
cnow.rx, cnow.tx);
icount->cts = cnow.cts;
@@ -782,22 +768,22 @@ static int ti_ioctl(struct tty_struct *tty,
struct async_icount cnow;
struct async_icount cprev;
- dbg("%s - port %d, cmd = 0x%04X", __func__, port->number, cmd);
+ dev_dbg(&port->dev, "%s - cmd = 0x%04X\n", __func__, cmd);
if (tport == NULL)
return -ENODEV;
switch (cmd) {
case TIOCGSERIAL:
- dbg("%s - (%d) TIOCGSERIAL", __func__, port->number);
+ dev_dbg(&port->dev, "%s - TIOCGSERIAL\n", __func__);
return ti_get_serial_info(tport,
(struct serial_struct __user *)arg);
case TIOCSSERIAL:
- dbg("%s - (%d) TIOCSSERIAL", __func__, port->number);
+ dev_dbg(&port->dev, "%s - TIOCSSERIAL\n", __func__);
return ti_set_serial_info(tty, tport,
(struct serial_struct __user *)arg);
case TIOCMIWAIT:
- dbg("%s - (%d) TIOCMIWAIT", __func__, port->number);
+ dev_dbg(&port->dev, "%s - TIOCMIWAIT\n", __func__);
cprev = tport->tp_icount;
while (1) {
interruptible_sleep_on(&tport->tp_msr_wait);
@@ -834,9 +820,9 @@ static void ti_set_termios(struct tty_struct *tty,
cflag = tty->termios.c_cflag;
iflag = tty->termios.c_iflag;
- dbg("%s - cflag %08x, iflag %08x", __func__, cflag, iflag);
- dbg("%s - old clfag %08x, old iflag %08x", __func__,
- old_termios->c_cflag, old_termios->c_iflag);
+ dev_dbg(&port->dev, "%s - cflag %08x, iflag %08x\n", __func__, cflag, iflag);
+ dev_dbg(&port->dev, "%s - old clfag %08x, old iflag %08x\n", __func__,
+ old_termios->c_cflag, old_termios->c_iflag);
if (tport == NULL)
return;
@@ -926,8 +912,11 @@ static void ti_set_termios(struct tty_struct *tty,
if ((cflag & CBAUD) != B0)
tty_encode_baud_rate(tty, baud, baud);
- dbg("%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d",
- __func__, baud, config->wBaudRate, config->wFlags, config->bDataBits, config->bParity, config->bStopBits, config->cXon, config->cXoff, config->bUartMode);
+ dev_dbg(&port->dev,
+ "%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d",
+ __func__, baud, config->wBaudRate, config->wFlags,
+ config->bDataBits, config->bParity, config->bStopBits,
+ config->cXon, config->cXoff, config->bUartMode);
cpu_to_be16s(&config->wBaudRate);
cpu_to_be16s(&config->wFlags);
@@ -979,7 +968,7 @@ static int ti_tiocmget(struct tty_struct *tty)
| ((msr & TI_MSR_RI) ? TIOCM_RI : 0)
| ((msr & TI_MSR_DSR) ? TIOCM_DSR : 0);
- dbg("%s - 0x%04X", __func__, result);
+ dev_dbg(&port->dev, "%s - 0x%04X\n", __func__, result);
return result;
}
@@ -1024,19 +1013,19 @@ static void ti_break(struct tty_struct *tty, int break_state)
struct ti_port *tport = usb_get_serial_port_data(port);
int status;
- dbg("%s - state = %d", __func__, break_state);
+ dev_dbg(&port->dev, "%s - state = %d\n", __func__, break_state);
if (tport == NULL)
return;
ti_drain(tport, (tport->tp_closing_wait*HZ)/100, 0);
- status = ti_write_byte(tport->tp_tdev,
+ status = ti_write_byte(port, tport->tp_tdev,
tport->tp_uart_base_addr + TI_UART_OFFSET_LCR,
TI_LCR_BREAK, break_state == -1 ? TI_LCR_BREAK : 0);
if (status)
- dbg("%s - error setting break, %d", __func__, status);
+ dev_dbg(&port->dev, "%s - error setting break, %d\n", __func__, status);
}
@@ -1061,18 +1050,17 @@ static void ti_interrupt_callback(struct urb *urb)
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
- dbg("%s - urb shutting down, %d", __func__, status);
+ dev_dbg(dev, "%s - urb shutting down, %d\n", __func__, status);
tdev->td_urb_error = 1;
return;
default:
- dev_err(dev, "%s - nonzero urb status, %d\n",
- __func__, status);
+ dev_err(dev, "%s - nonzero urb status, %d\n", __func__, status);
tdev->td_urb_error = 1;
goto exit;
}
if (length != 2) {
- dbg("%s - bad packet size, %d", __func__, length);
+ dev_dbg(dev, "%s - bad packet size, %d\n", __func__, length);
goto exit;
}
@@ -1084,8 +1072,8 @@ static void ti_interrupt_callback(struct urb *urb)
port_number = TI_GET_PORT_FROM_CODE(data[0]);
function = TI_GET_FUNC_FROM_CODE(data[0]);
- dbg("%s - port_number %d, function %d, data 0x%02X",
- __func__, port_number, function, data[1]);
+ dev_dbg(dev, "%s - port_number %d, function %d, data 0x%02X\n",
+ __func__, port_number, function, data[1]);
if (port_number >= serial->num_ports) {
dev_err(dev, "%s - bad port number, %d\n",
@@ -1102,12 +1090,12 @@ static void ti_interrupt_callback(struct urb *urb)
switch (function) {
case TI_CODE_DATA_ERROR:
dev_err(dev, "%s - DATA ERROR, port %d, data 0x%02X\n",
- __func__, port_number, data[1]);
+ __func__, port_number, data[1]);
break;
case TI_CODE_MODEM_STATUS:
msr = data[1];
- dbg("%s - port %d, msr 0x%02X", __func__, port_number, msr);
+ dev_dbg(dev, "%s - port %d, msr 0x%02X\n", __func__, port_number, msr);
ti_handle_new_msr(tport, msr);
break;
@@ -1140,7 +1128,7 @@ static void ti_bulk_in_callback(struct urb *urb)
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
- dbg("%s - urb shutting down, %d", __func__, status);
+ dev_dbg(dev, "%s - urb shutting down, %d\n", __func__, status);
tport->tp_tdev->td_urb_error = 1;
wake_up_interruptible(&tport->tp_write_wait);
return;
@@ -1162,11 +1150,11 @@ static void ti_bulk_in_callback(struct urb *urb)
tty = tty_port_tty_get(&port->port);
if (tty) {
if (urb->actual_length) {
- usb_serial_debug_data(debug, dev, __func__,
- urb->actual_length, urb->transfer_buffer);
+ usb_serial_debug_data(dev, __func__, urb->actual_length,
+ urb->transfer_buffer);
if (!tport->tp_is_open)
- dbg("%s - port closed, dropping data",
+ dev_dbg(dev, "%s - port closed, dropping data\n",
__func__);
else
ti_recv(&urb->dev->dev, tty,
@@ -1208,7 +1196,7 @@ static void ti_bulk_out_callback(struct urb *urb)
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
- dbg("%s - urb shutting down, %d", __func__, status);
+ dev_dbg(&port->dev, "%s - urb shutting down, %d\n", __func__, status);
tport->tp_tdev->td_urb_error = 1;
wake_up_interruptible(&tport->tp_write_wait);
return;
@@ -1268,8 +1256,8 @@ static void ti_send(struct ti_port *tport)
spin_unlock_irqrestore(&tport->tp_lock, flags);
- usb_serial_debug_data(debug, &port->dev, __func__, count,
- port->write_urb->transfer_buffer);
+ usb_serial_debug_data(&port->dev, __func__, count,
+ port->write_urb->transfer_buffer);
usb_fill_bulk_urb(port->write_urb, port->serial->dev,
usb_sndbulkpipe(port->serial->dev,
@@ -1307,7 +1295,7 @@ static int ti_set_mcr(struct ti_port *tport, unsigned int mcr)
unsigned long flags;
int status;
- status = ti_write_byte(tport->tp_tdev,
+ status = ti_write_byte(tport->tp_port, tport->tp_tdev,
tport->tp_uart_base_addr + TI_UART_OFFSET_MCR,
TI_MCR_RTS | TI_MCR_DTR | TI_MCR_LOOP, mcr);
@@ -1344,7 +1332,7 @@ static int ti_get_lsr(struct ti_port *tport)
goto free_data;
}
- dbg("%s - lsr 0x%02X", __func__, data->bLSR);
+ dev_dbg(&port->dev, "%s - lsr 0x%02X\n", __func__, data->bLSR);
tport->tp_lsr = data->bLSR;
@@ -1401,7 +1389,7 @@ static void ti_handle_new_msr(struct ti_port *tport, __u8 msr)
struct tty_struct *tty;
unsigned long flags;
- dbg("%s - msr 0x%02X", __func__, msr);
+ dev_dbg(&tport->tp_port->dev, "%s - msr 0x%02X\n", __func__, msr);
if (msr & TI_MSR_DELTA_MASK) {
spin_lock_irqsave(&tport->tp_lock, flags);
@@ -1560,21 +1548,21 @@ static int ti_command_in_sync(struct ti_device *tdev, __u8 command,
}
-static int ti_write_byte(struct ti_device *tdev, unsigned long addr,
- __u8 mask, __u8 byte)
+static int ti_write_byte(struct usb_serial_port *port,
+ struct ti_device *tdev, unsigned long addr,
+ __u8 mask, __u8 byte)
{
int status;
unsigned int size;
struct ti_write_data_bytes *data;
- struct device *dev = &tdev->td_serial->dev->dev;
- dbg("%s - addr 0x%08lX, mask 0x%02X, byte 0x%02X",
- __func__, addr, mask, byte);
+ dev_dbg(&port->dev, "%s - addr 0x%08lX, mask 0x%02X, byte 0x%02X\n", __func__,
+ addr, mask, byte);
size = sizeof(struct ti_write_data_bytes) + 2;
data = kmalloc(size, GFP_KERNEL);
if (!data) {
- dev_err(dev, "%s - out of memory\n", __func__);
+ dev_err(&port->dev, "%s - out of memory\n", __func__);
return -ENOMEM;
}
@@ -1590,7 +1578,7 @@ static int ti_write_byte(struct ti_device *tdev, unsigned long addr,
(__u8 *)data, size);
if (status < 0)
- dev_err(dev, "%s - failed, %d\n", __func__, status);
+ dev_err(&port->dev, "%s - failed, %d\n", __func__, status);
kfree(data);
@@ -1615,7 +1603,7 @@ static int ti_do_download(struct usb_device *dev, int pipe,
- sizeof(struct ti_firmware_header)));
header->bCheckSum = cs;
- dbg("%s - downloading firmware", __func__);
+ dev_dbg(&dev->dev, "%s - downloading firmware\n", __func__);
for (pos = 0; pos < size; pos += done) {
len = min(size - pos, TI_DOWNLOAD_MAX_PACKET_SIZE);
status = usb_bulk_msg(dev, pipe, buffer + pos, len,
@@ -1691,7 +1679,7 @@ static int ti_download_firmware(struct ti_device *tdev)
status = ti_do_download(dev, pipe, buffer, fw_p->size);
kfree(buffer);
} else {
- dbg("%s ENOMEM\n", __func__);
+ dev_dbg(&dev->dev, "%s ENOMEM\n", __func__);
status = -ENOMEM;
}
release_firmware(fw_p);
@@ -1701,7 +1689,7 @@ static int ti_download_firmware(struct ti_device *tdev)
return status;
}
- dbg("%s - download successful", __func__);
+ dev_dbg(&dev->dev, "%s - download successful\n", __func__);
return 0;
}
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index aa4b0d775992..73b8e0569164 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -17,6 +17,8 @@
*
*/
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
@@ -37,10 +39,7 @@
#include <linux/kfifo.h>
#include "pl2303.h"
-/*
- * Version Information
- */
-#define DRIVER_AUTHOR "Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux/"
+#define DRIVER_AUTHOR "Greg Kroah-Hartman <gregkh@linuxfoundation.org>"
#define DRIVER_DESC "USB Serial Driver core"
/* There is no MODULE_DEVICE_TABLE for usbserial.c. Instead
@@ -50,7 +49,6 @@
drivers depend on it.
*/
-static bool debug;
/* initially all NULL */
static struct usb_serial *serial_table[SERIAL_TTY_MINORS];
static DEFINE_MUTEX(table_lock);
@@ -87,7 +85,7 @@ static struct usb_serial *get_free_serial(struct usb_serial *serial,
unsigned int i, j;
int good_spot;
- dbg("%s %d", __func__, num_ports);
+ dev_dbg(&serial->interface->dev, "%s %d\n", __func__, num_ports);
*minor = 0;
mutex_lock(&table_lock);
@@ -107,7 +105,7 @@ static struct usb_serial *get_free_serial(struct usb_serial *serial,
*minor = i;
j = 0;
- dbg("%s - minor base = %d", __func__, *minor);
+ dev_dbg(&serial->interface->dev, "%s - minor base = %d\n", __func__, *minor);
for (i = *minor; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) {
serial_table[i] = serial;
serial->port[j++]->number = i;
@@ -123,8 +121,6 @@ static void return_serial(struct usb_serial *serial)
{
int i;
- dbg("%s", __func__);
-
mutex_lock(&table_lock);
for (i = 0; i < serial->num_ports; ++i)
serial_table[serial->minor + i] = NULL;
@@ -139,8 +135,6 @@ static void destroy_serial(struct kref *kref)
serial = to_usb_serial(kref);
- dbg("%s - %s", __func__, serial->type->description);
-
/* return the minor range that this device had */
if (serial->minor != SERIAL_TTY_NO_MINOR)
return_serial(serial);
@@ -191,8 +185,6 @@ static int serial_install(struct tty_driver *driver, struct tty_struct *tty)
struct usb_serial_port *port;
int retval = -ENODEV;
- dbg("%s", __func__);
-
serial = usb_serial_get_by_index(idx);
if (!serial)
return retval;
@@ -256,7 +248,7 @@ static int serial_open(struct tty_struct *tty, struct file *filp)
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
return tty_port_open(&port->port, tty, filp);
}
@@ -287,14 +279,16 @@ static void serial_down(struct tty_port *tport)
static void serial_hangup(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
tty_port_hangup(&port->port);
}
static void serial_close(struct tty_struct *tty, struct file *filp)
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
tty_port_close(&port->port, tty, filp);
}
@@ -319,7 +313,7 @@ static void serial_cleanup(struct tty_struct *tty)
if (port->port.console)
return;
- dbg("%s - port %d", __func__, port->number);
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
tty->driver_data = NULL;
@@ -344,7 +338,8 @@ static int serial_write(struct tty_struct *tty, const unsigned char *buf,
if (port->serial->dev->state == USB_STATE_NOTATTACHED)
goto exit;
- dbg("%s - port %d, %d byte(s)", __func__, port->number, count);
+ dev_dbg(tty->dev, "%s - port %d, %d byte(s)\n", __func__,
+ port->number, count);
/* pass on to the driver specific version of this function */
retval = port->serial->type->write(tty, port, buf, count);
@@ -357,7 +352,8 @@ exit:
static int serial_write_room(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
/* pass on to the driver specific version of this function */
return port->serial->type->write_room(tty);
}
@@ -365,7 +361,8 @@ static int serial_write_room(struct tty_struct *tty)
static int serial_chars_in_buffer(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
/* if the device was unplugged then any remaining characters
fell out of the connector ;) */
@@ -378,7 +375,8 @@ static int serial_chars_in_buffer(struct tty_struct *tty)
static void serial_throttle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
/* pass on to the driver specific version of this function */
if (port->serial->type->throttle)
@@ -388,7 +386,8 @@ static void serial_throttle(struct tty_struct *tty)
static void serial_unthrottle(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
/* pass on to the driver specific version of this function */
if (port->serial->type->unthrottle)
@@ -401,7 +400,8 @@ static int serial_ioctl(struct tty_struct *tty,
struct usb_serial_port *port = tty->driver_data;
int retval = -ENODEV;
- dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd);
+ dev_dbg(tty->dev, "%s - port %d, cmd 0x%.4x\n", __func__,
+ port->number, cmd);
/* pass on to the driver specific version of this function
if it is available */
@@ -415,7 +415,8 @@ static int serial_ioctl(struct tty_struct *tty,
static void serial_set_termios(struct tty_struct *tty, struct ktermios *old)
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
/* pass on to the driver specific version of this function
if it is available */
@@ -429,7 +430,7 @@ static int serial_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
/* pass on to the driver specific version of this function
if it is available */
@@ -444,7 +445,6 @@ static int serial_proc_show(struct seq_file *m, void *v)
int i;
char tmp[40];
- dbg("%s", __func__);
seq_puts(m, "usbserinfo:1.0 driver:2.0\n");
for (i = 0; i < SERIAL_TTY_MINORS; ++i) {
serial = usb_serial_get_by_index(i);
@@ -489,7 +489,7 @@ static int serial_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
if (port->serial->type->tiocmget)
return port->serial->type->tiocmget(tty);
@@ -501,7 +501,7 @@ static int serial_tiocmset(struct tty_struct *tty,
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
if (port->serial->type->tiocmset)
return port->serial->type->tiocmset(tty, set, clear);
@@ -513,7 +513,7 @@ static int serial_get_icount(struct tty_struct *tty,
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s - port %d", __func__, port->number);
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
if (port->serial->type->get_icount)
return port->serial->type->get_icount(tty, icount);
@@ -537,12 +537,12 @@ static void usb_serial_port_work(struct work_struct *work)
container_of(work, struct usb_serial_port, work);
struct tty_struct *tty;
- dbg("%s - port %d", __func__, port->number);
-
tty = tty_port_tty_get(&port->port);
if (!tty)
return;
+ dev_dbg(tty->dev, "%s - port %d\n", __func__, port->number);
+
tty_wakeup(tty);
tty_kref_put(tty);
}
@@ -575,7 +575,7 @@ static void port_release(struct device *dev)
struct usb_serial_port *port = to_usb_serial_port(dev);
int i;
- dbg ("%s - %s", __func__, dev_name(dev));
+ dev_dbg(dev, "%s\n", __func__);
/*
* Stop all the traffic before cancelling the work, so that
@@ -644,12 +644,12 @@ static const struct usb_device_id *get_iface_id(struct usb_serial_driver *drv,
id = usb_match_id(intf, drv->id_table);
if (id) {
- dbg("static descriptor matches");
+ dev_dbg(&intf->dev, "static descriptor matches\n");
goto exit;
}
id = match_dynamic_id(intf, drv);
if (id)
- dbg("dynamic descriptor matches");
+ dev_dbg(&intf->dev, "dynamic descriptor matches\n");
exit:
return id;
}
@@ -703,6 +703,7 @@ static const struct tty_port_operations serial_port_ops = {
static int usb_serial_probe(struct usb_interface *interface,
const struct usb_device_id *id)
{
+ struct device *ddev = &interface->dev;
struct usb_device *dev = interface_to_usbdev(interface);
struct usb_serial *serial = NULL;
struct usb_serial_port *port;
@@ -729,13 +730,13 @@ static int usb_serial_probe(struct usb_interface *interface,
type = search_serial_device(interface);
if (!type) {
mutex_unlock(&table_lock);
- dbg("none matched");
+ dev_dbg(ddev, "none matched\n");
return -ENODEV;
}
if (!try_module_get(type->driver.owner)) {
mutex_unlock(&table_lock);
- dev_err(&interface->dev, "module get failed, exiting\n");
+ dev_err(ddev, "module get failed, exiting\n");
return -EIO;
}
mutex_unlock(&table_lock);
@@ -743,7 +744,7 @@ static int usb_serial_probe(struct usb_interface *interface,
serial = create_serial(dev, interface, type);
if (!serial) {
module_put(type->driver.owner);
- dev_err(&interface->dev, "%s - out of memory\n", __func__);
+ dev_err(ddev, "%s - out of memory\n", __func__);
return -ENOMEM;
}
@@ -755,7 +756,7 @@ static int usb_serial_probe(struct usb_interface *interface,
retval = type->probe(serial, id);
if (retval) {
- dbg("sub driver rejected device");
+ dev_dbg(ddev, "sub driver rejected device\n");
usb_serial_put(serial);
module_put(type->driver.owner);
return retval;
@@ -770,28 +771,28 @@ static int usb_serial_probe(struct usb_interface *interface,
if (usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
- dbg("found bulk in on endpoint %d", i);
+ dev_dbg(ddev, "found bulk in on endpoint %d\n", i);
bulk_in_endpoint[num_bulk_in] = endpoint;
++num_bulk_in;
}
if (usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
- dbg("found bulk out on endpoint %d", i);
+ dev_dbg(ddev, "found bulk out on endpoint %d\n", i);
bulk_out_endpoint[num_bulk_out] = endpoint;
++num_bulk_out;
}
if (usb_endpoint_is_int_in(endpoint)) {
/* we found a interrupt in endpoint */
- dbg("found interrupt in on endpoint %d", i);
+ dev_dbg(ddev, "found interrupt in on endpoint %d\n", i);
interrupt_in_endpoint[num_interrupt_in] = endpoint;
++num_interrupt_in;
}
if (usb_endpoint_is_int_out(endpoint)) {
/* we found an interrupt out endpoint */
- dbg("found interrupt out on endpoint %d", i);
+ dev_dbg(ddev, "found interrupt out on endpoint %d\n", i);
interrupt_out_endpoint[num_interrupt_out] = endpoint;
++num_interrupt_out;
}
@@ -815,7 +816,7 @@ static int usb_serial_probe(struct usb_interface *interface,
endpoint = &iface_desc->endpoint[i].desc;
if (usb_endpoint_is_int_in(endpoint)) {
/* we found a interrupt in endpoint */
- dbg("found interrupt in for Prolific device on separate interface");
+ dev_dbg(ddev, "found interrupt in for Prolific device on separate interface\n");
interrupt_in_endpoint[num_interrupt_in] = endpoint;
++num_interrupt_in;
}
@@ -827,7 +828,7 @@ static int usb_serial_probe(struct usb_interface *interface,
* properly during a later invocation of usb_serial_probe
*/
if (num_bulk_in == 0 || num_bulk_out == 0) {
- dev_info(&interface->dev, "PL-2303 hack: descriptors matched but endpoints did not\n");
+ dev_info(ddev, "PL-2303 hack: descriptors matched but endpoints did not\n");
usb_serial_put(serial);
module_put(type->driver.owner);
return -ENODEV;
@@ -840,14 +841,13 @@ static int usb_serial_probe(struct usb_interface *interface,
if (type == &usb_serial_generic_device) {
num_ports = num_bulk_out;
if (num_ports == 0) {
- dev_err(&interface->dev,
- "Generic device with no bulk out, not allowed.\n");
+ dev_err(ddev, "Generic device with no bulk out, not allowed.\n");
usb_serial_put(serial);
module_put(type->driver.owner);
return -EIO;
}
- dev_info(&interface->dev, "The \"generic\" usb-serial driver is only for testing and one-off prototypes.\n");
- dev_info(&interface->dev, "Tell linux-usb@vger.kernel.org to add your device to a proper driver.\n");
+ dev_info(ddev, "The \"generic\" usb-serial driver is only for testing and one-off prototypes.\n");
+ dev_info(ddev, "Tell linux-usb@vger.kernel.org to add your device to a proper driver.\n");
}
#endif
if (!num_ports) {
@@ -865,8 +865,7 @@ static int usb_serial_probe(struct usb_interface *interface,
serial->num_interrupt_out = num_interrupt_out;
/* found all that we need */
- dev_info(&interface->dev, "%s converter detected\n",
- type->description);
+ dev_info(ddev, "%s converter detected\n", type->description);
/* create our ports, we need as many as the max endpoints */
/* we don't use num_ports here because some devices have more
@@ -877,8 +876,7 @@ static int usb_serial_probe(struct usb_interface *interface,
max_endpoints = max(max_endpoints, (int)serial->num_ports);
serial->num_port_pointers = max_endpoints;
- dbg("%s - setting up %d port structures for this device",
- __func__, max_endpoints);
+ dev_dbg(ddev, "setting up %d port structures for this device", max_endpoints);
for (i = 0; i < max_endpoints; ++i) {
port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
if (!port)
@@ -911,15 +909,13 @@ static int usb_serial_probe(struct usb_interface *interface,
set_bit(j, &port->read_urbs_free);
port->read_urbs[j] = usb_alloc_urb(0, GFP_KERNEL);
if (!port->read_urbs[j]) {
- dev_err(&interface->dev,
- "No free urbs available\n");
+ dev_err(ddev, "No free urbs available\n");
goto probe_error;
}
port->bulk_in_buffers[j] = kmalloc(buffer_size,
GFP_KERNEL);
if (!port->bulk_in_buffers[j]) {
- dev_err(&interface->dev,
- "Couldn't allocate bulk_in_buffer\n");
+ dev_err(ddev, "Couldn't allocate bulk_in_buffer\n");
goto probe_error;
}
usb_fill_bulk_urb(port->read_urbs[j], dev,
@@ -949,15 +945,13 @@ static int usb_serial_probe(struct usb_interface *interface,
set_bit(j, &port->write_urbs_free);
port->write_urbs[j] = usb_alloc_urb(0, GFP_KERNEL);
if (!port->write_urbs[j]) {
- dev_err(&interface->dev,
- "No free urbs available\n");
+ dev_err(ddev, "No free urbs available\n");
goto probe_error;
}
port->bulk_out_buffers[j] = kmalloc(buffer_size,
GFP_KERNEL);
if (!port->bulk_out_buffers[j]) {
- dev_err(&interface->dev,
- "Couldn't allocate bulk_out_buffer\n");
+ dev_err(ddev, "Couldn't allocate bulk_out_buffer\n");
goto probe_error;
}
usb_fill_bulk_urb(port->write_urbs[j], dev,
@@ -978,8 +972,7 @@ static int usb_serial_probe(struct usb_interface *interface,
port = serial->port[i];
port->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!port->interrupt_in_urb) {
- dev_err(&interface->dev,
- "No free urbs available\n");
+ dev_err(ddev, "No free urbs available\n");
goto probe_error;
}
buffer_size = usb_endpoint_maxp(endpoint);
@@ -988,8 +981,7 @@ static int usb_serial_probe(struct usb_interface *interface,
port->interrupt_in_buffer = kmalloc(buffer_size,
GFP_KERNEL);
if (!port->interrupt_in_buffer) {
- dev_err(&interface->dev,
- "Couldn't allocate interrupt_in_buffer\n");
+ dev_err(ddev, "Couldn't allocate interrupt_in_buffer\n");
goto probe_error;
}
usb_fill_int_urb(port->interrupt_in_urb, dev,
@@ -1000,7 +992,7 @@ static int usb_serial_probe(struct usb_interface *interface,
endpoint->bInterval);
}
} else if (num_interrupt_in) {
- dbg("the device claims to support interrupt in transfers, but read_int_callback is not defined");
+ dev_dbg(ddev, "The device claims to support interrupt in transfers, but read_int_callback is not defined\n");
}
if (serial->type->write_int_callback) {
@@ -1009,8 +1001,7 @@ static int usb_serial_probe(struct usb_interface *interface,
port = serial->port[i];
port->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!port->interrupt_out_urb) {
- dev_err(&interface->dev,
- "No free urbs available\n");
+ dev_err(ddev, "No free urbs available\n");
goto probe_error;
}
buffer_size = usb_endpoint_maxp(endpoint);
@@ -1020,8 +1011,7 @@ static int usb_serial_probe(struct usb_interface *interface,
port->interrupt_out_buffer = kmalloc(buffer_size,
GFP_KERNEL);
if (!port->interrupt_out_buffer) {
- dev_err(&interface->dev,
- "Couldn't allocate interrupt_out_buffer\n");
+ dev_err(ddev, "Couldn't allocate interrupt_out_buffer\n");
goto probe_error;
}
usb_fill_int_urb(port->interrupt_out_urb, dev,
@@ -1032,7 +1022,7 @@ static int usb_serial_probe(struct usb_interface *interface,
endpoint->bInterval);
}
} else if (num_interrupt_out) {
- dbg("the device claims to support interrupt out transfers, but write_int_callback is not defined");
+ dev_dbg(ddev, "The device claims to support interrupt out transfers, but write_int_callback is not defined\n");
}
usb_set_intfdata(interface, serial);
@@ -1060,7 +1050,7 @@ static int usb_serial_probe(struct usb_interface *interface,
serial->disconnected = 1;
if (get_free_serial(serial, num_ports, &minor) == NULL) {
- dev_err(&interface->dev, "No more free serial devices\n");
+ dev_err(ddev, "No more free serial devices\n");
goto probe_error;
}
serial->minor = minor;
@@ -1069,18 +1059,17 @@ static int usb_serial_probe(struct usb_interface *interface,
for (i = 0; i < num_ports; ++i) {
port = serial->port[i];
dev_set_name(&port->dev, "ttyUSB%d", port->number);
- dbg ("%s - registering %s", __func__, dev_name(&port->dev));
+ dev_dbg(ddev, "registering %s", dev_name(&port->dev));
device_enable_async_suspend(&port->dev);
retval = device_add(&port->dev);
if (retval)
- dev_err(&port->dev, "Error registering port device, "
- "continuing\n");
+ dev_err(ddev, "Error registering port device, continuing\n");
}
serial->disconnected = 0;
- usb_serial_console_init(debug, minor);
+ usb_serial_console_init(minor);
exit:
module_put(type->driver.owner);
return 0;
@@ -1099,7 +1088,6 @@ static void usb_serial_disconnect(struct usb_interface *interface)
struct usb_serial_port *port;
usb_serial_console_disconnect(serial);
- dbg("%s", __func__);
mutex_lock(&serial->disc_mutex);
/* must set a flag, to signal subdrivers */
@@ -1234,8 +1222,7 @@ static int __init usb_serial_init(void)
result = bus_register(&usb_serial_bus_type);
if (result) {
- printk(KERN_ERR "usb-serial: %s - registering bus driver "
- "failed\n", __func__);
+ pr_err("%s - registering bus driver failed\n", __func__);
goto exit_bus;
}
@@ -1255,29 +1242,24 @@ static int __init usb_serial_init(void)
tty_set_operations(usb_serial_tty_driver, &serial_ops);
result = tty_register_driver(usb_serial_tty_driver);
if (result) {
- printk(KERN_ERR "usb-serial: %s - tty_register_driver failed\n",
- __func__);
+ pr_err("%s - tty_register_driver failed\n", __func__);
goto exit_reg_driver;
}
/* register the USB driver */
result = usb_register(&usb_serial_driver);
if (result < 0) {
- printk(KERN_ERR "usb-serial: %s - usb_register failed\n",
- __func__);
+ pr_err("%s - usb_register failed\n", __func__);
goto exit_tty;
}
/* register the generic driver, if we should */
- result = usb_serial_generic_register(debug);
+ result = usb_serial_generic_register();
if (result < 0) {
- printk(KERN_ERR "usb-serial: %s - registering generic "
- "driver failed\n", __func__);
+ pr_err("%s - registering generic driver failed\n", __func__);
goto exit_generic;
}
- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_DESC "\n");
-
return result;
exit_generic:
@@ -1290,8 +1272,7 @@ exit_reg_driver:
bus_unregister(&usb_serial_bus_type);
exit_bus:
- printk(KERN_ERR "usb-serial: %s - returning with error %d\n",
- __func__, result);
+ pr_err("%s - returning with error %d\n", __func__, result);
put_tty_driver(usb_serial_tty_driver);
return result;
}
@@ -1317,7 +1298,7 @@ module_exit(usb_serial_exit);
do { \
if (!type->function) { \
type->function = usb_serial_generic_##function; \
- dbg("Had to override the " #function \
+ pr_debug("Had to override the " #function \
" usb serial operation with the generic one.");\
} \
} while (0)
@@ -1360,12 +1341,10 @@ static int usb_serial_register(struct usb_serial_driver *driver)
retval = usb_serial_bus_register(driver);
if (retval) {
- printk(KERN_ERR "usb-serial: problem %d when registering "
- "driver %s\n", retval, driver->description);
+ pr_err("problem %d when registering driver %s\n", retval, driver->description);
list_del(&driver->driver_list);
} else
- printk(KERN_INFO "USB Serial support registered for %s\n",
- driver->description);
+ pr_info("USB Serial support registered for %s\n", driver->description);
mutex_unlock(&table_lock);
return retval;
@@ -1373,8 +1352,7 @@ static int usb_serial_register(struct usb_serial_driver *driver)
static void usb_serial_deregister(struct usb_serial_driver *device)
{
- printk(KERN_INFO "USB Serial deregistering driver %s\n",
- device->description);
+ pr_info("USB Serial deregistering driver %s\n", device->description);
mutex_lock(&table_lock);
list_del(&device->driver_list);
usb_serial_bus_deregister(device);
@@ -1425,9 +1403,10 @@ int usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers[]
/* we only set the reset_resume field if the serial_driver has one */
for (sd = serial_drivers; *sd; ++sd) {
- if ((*sd)->reset_resume)
+ if ((*sd)->reset_resume) {
udriver->reset_resume = usb_serial_reset_resume;
break;
+ }
}
rc = usb_register(udriver);
@@ -1477,6 +1456,3 @@ EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index 72b678d90831..e42aa398ed37 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -37,8 +37,6 @@
#include <linux/serial.h>
#include "usb-wwan.h"
-static bool debug;
-
void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
{
struct usb_serial *serial = port->serial;
@@ -178,7 +176,7 @@ int usb_wwan_ioctl(struct tty_struct *tty,
{
struct usb_serial_port *port = tty->driver_data;
- dbg("%s cmd 0x%04x", __func__, cmd);
+ dev_dbg(&port->dev, "%s cmd 0x%04x\n", __func__, cmd);
switch (cmd) {
case TIOCGSERIAL:
@@ -191,7 +189,7 @@ int usb_wwan_ioctl(struct tty_struct *tty,
break;
}
- dbg("%s arg not supported", __func__);
+ dev_dbg(&port->dev, "%s arg not supported\n", __func__);
return -ENOIOCTLCMD;
}
@@ -212,7 +210,7 @@ int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
portdata = usb_get_serial_port_data(port);
intfdata = port->serial->private;
- dbg("%s: write (%d chars)", __func__, count);
+ dev_dbg(&port->dev, "%s: write (%d chars)\n", __func__, count);
i = 0;
left = count;
@@ -229,8 +227,8 @@ int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
usb_unlink_urb(this_urb);
continue;
}
- dbg("%s: endpoint %d buf %d", __func__,
- usb_pipeendpoint(this_urb->pipe), i);
+ dev_dbg(&port->dev, "%s: endpoint %d buf %d\n", __func__,
+ usb_pipeendpoint(this_urb->pipe), i);
err = usb_autopm_get_interface_async(port->serial->interface);
if (err < 0)
@@ -249,8 +247,9 @@ int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
spin_unlock_irqrestore(&intfdata->susp_lock, flags);
err = usb_submit_urb(this_urb, GFP_ATOMIC);
if (err) {
- dbg("usb_submit_urb %p (write bulk) failed "
- "(%d)", this_urb, err);
+ dev_dbg(&port->dev,
+ "usb_submit_urb %p (write bulk) failed (%d)\n",
+ this_urb, err);
clear_bit(i, &portdata->out_busy);
spin_lock_irqsave(&intfdata->susp_lock, flags);
intfdata->in_flight--;
@@ -267,7 +266,7 @@ int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
}
count -= left;
- dbg("%s: wrote (did %d)", __func__, count);
+ dev_dbg(&port->dev, "%s: wrote (did %d)\n", __func__, count);
return count;
}
EXPORT_SYMBOL(usb_wwan_write);
@@ -278,15 +277,17 @@ static void usb_wwan_indat_callback(struct urb *urb)
int endpoint;
struct usb_serial_port *port;
struct tty_struct *tty;
+ struct device *dev;
unsigned char *data = urb->transfer_buffer;
int status = urb->status;
endpoint = usb_pipeendpoint(urb->pipe);
port = urb->context;
+ dev = &port->dev;
if (status) {
- dbg("%s: nonzero status: %d on endpoint %02x.",
- __func__, status, endpoint);
+ dev_dbg(dev, "%s: nonzero status: %d on endpoint %02x.\n",
+ __func__, status, endpoint);
} else {
tty = tty_port_tty_get(&port->port);
if (tty) {
@@ -295,7 +296,7 @@ static void usb_wwan_indat_callback(struct urb *urb)
urb->actual_length);
tty_flip_buffer_push(tty);
} else
- dbg("%s: empty read urb received", __func__);
+ dev_dbg(dev, "%s: empty read urb received\n", __func__);
tty_kref_put(tty);
}
@@ -303,8 +304,7 @@ static void usb_wwan_indat_callback(struct urb *urb)
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
if (err != -EPERM) {
- printk(KERN_ERR "%s: resubmit read urb failed. "
- "(%d)", __func__, err);
+ dev_err(dev, "%s: resubmit read urb failed. (%d)\n", __func__, err);
/* busy also in error unless we are killed */
usb_mark_last_busy(port->serial->dev);
}
@@ -356,7 +356,7 @@ int usb_wwan_write_room(struct tty_struct *tty)
data_len += OUT_BUFLEN;
}
- dbg("%s: %d", __func__, data_len);
+ dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
return data_len;
}
EXPORT_SYMBOL(usb_wwan_write_room);
@@ -378,7 +378,7 @@ int usb_wwan_chars_in_buffer(struct tty_struct *tty)
if (this_urb && test_bit(i, &portdata->out_busy))
data_len += this_urb->transfer_buffer_length;
}
- dbg("%s: %d", __func__, data_len);
+ dev_dbg(&port->dev, "%s: %d\n", __func__, data_len);
return data_len;
}
EXPORT_SYMBOL(usb_wwan_chars_in_buffer);
@@ -401,8 +401,8 @@ int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
continue;
err = usb_submit_urb(urb, GFP_KERNEL);
if (err) {
- dbg("%s: submit urb %d failed (%d) %d",
- __func__, i, err, urb->transfer_buffer_length);
+ dev_dbg(&port->dev, "%s: submit urb %d failed (%d) %d\n",
+ __func__, i, err, urb->transfer_buffer_length);
}
}
@@ -458,7 +458,9 @@ static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint,
urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
if (urb == NULL) {
- dbg("%s: alloc for endpoint %d failed.", __func__, endpoint);
+ dev_dbg(&serial->interface->dev,
+ "%s: alloc for endpoint %d failed.\n", __func__,
+ endpoint);
return NULL;
}
@@ -522,8 +524,8 @@ int usb_wwan_startup(struct usb_serial *serial)
port = serial->port[i];
portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
if (!portdata) {
- dbg("%s: kmalloc for usb_wwan_port_private (%d) failed!.",
- __func__, i);
+ dev_dbg(&port->dev, "%s: kmalloc for usb_wwan_port_private (%d) failed!.\n",
+ __func__, i);
return 1;
}
init_usb_anchor(&portdata->delayed);
@@ -548,7 +550,8 @@ int usb_wwan_startup(struct usb_serial *serial)
continue;
err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (err)
- dbg("%s: submit irq_in urb failed %d", __func__, err);
+ dev_dbg(&port->dev, "%s: submit irq_in urb failed %d\n",
+ __func__, err);
}
usb_wwan_setup_urbs(serial);
return 0;
@@ -683,11 +686,11 @@ int usb_wwan_resume(struct usb_serial *serial)
for (i = 0; i < serial->num_ports; i++) {
port = serial->port[i];
if (!port->interrupt_in_urb) {
- dbg("%s: No interrupt URB for port %d", __func__, i);
+ dev_dbg(&port->dev, "%s: No interrupt URB for port\n", __func__);
continue;
}
err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
- dbg("Submitted interrupt URB for port %d (result %d)", i, err);
+ dev_dbg(&port->dev, "Submitted interrupt URB for port (result %d)\n", err);
if (err < 0) {
dev_err(&port->dev, "%s: Error %d for interrupt URB\n",
__func__, err);
@@ -733,6 +736,3 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_VERSION(DRIVER_VERSION);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug messages");
diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c
index f253c91383da..1129aa73c23e 100644
--- a/drivers/usb/serial/visor.c
+++ b/drivers/usb/serial/visor.c
@@ -51,9 +51,6 @@ static int palm_os_3_probe(struct usb_serial *serial,
static int palm_os_4_probe(struct usb_serial *serial,
const struct usb_device_id *id);
-/* Parameters that may be passed into the module. */
-static bool debug;
-
static struct usb_device_id id_table [] = {
{ USB_DEVICE(HANDSPRING_VENDOR_ID, HANDSPRING_VISOR_ID),
.driver_info = (kernel_ulong_t)&palm_os_3_probe },
@@ -310,8 +307,8 @@ static void visor_read_int_callback(struct urb *urb)
* Rumor has it this endpoint is used to notify when data
* is ready to be read from the bulk ones.
*/
- usb_serial_debug_data(debug, &port->dev, __func__,
- urb->actual_length, urb->transfer_buffer);
+ usb_serial_debug_data(&port->dev, __func__, urb->actual_length,
+ urb->transfer_buffer);
exit:
result = usb_submit_urb(urb, GFP_ATOMIC);
@@ -443,8 +440,7 @@ static int palm_os_4_probe(struct usb_serial *serial,
dev_err(dev, "%s - error %d getting connection info\n",
__func__, retval);
else
- usb_serial_debug_data(debug, &serial->dev->dev, __func__,
- retval, transfer_buffer);
+ usb_serial_debug_data(dev, __func__, retval, transfer_buffer);
kfree(transfer_buffer);
return 0;
@@ -625,6 +621,3 @@ module_usb_serial_driver(serial_drivers, id_table_combined);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index b36077de72b9..346c7efc20b0 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -32,12 +32,9 @@
#include <linux/serial_reg.h>
#include <linux/serial.h>
#include <linux/usb/serial.h>
-#include <linux/firmware.h>
-#include <linux/ihex.h>
+#include <linux/usb/ezusb.h>
#include "whiteheat.h" /* WhiteHEAT specific commands */
-static bool debug;
-
#ifndef CMSPAR
#define CMSPAR 0
#endif
@@ -195,84 +192,15 @@ static int firm_report_tx_done(struct usb_serial_port *port);
static int whiteheat_firmware_download(struct usb_serial *serial,
const struct usb_device_id *id)
{
- int response, ret = -ENOENT;
- const struct firmware *loader_fw = NULL, *firmware_fw = NULL;
- const struct ihex_binrec *record;
+ int response;
- if (request_ihex_firmware(&firmware_fw, "whiteheat.fw",
- &serial->dev->dev)) {
- dev_err(&serial->dev->dev,
- "%s - request \"whiteheat.fw\" failed\n", __func__);
- goto out;
- }
- if (request_ihex_firmware(&loader_fw, "whiteheat_loader.fw",
- &serial->dev->dev)) {
- dev_err(&serial->dev->dev,
- "%s - request \"whiteheat_loader.fw\" failed\n",
- __func__);
- goto out;
+ response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat_loader.fw");
+ if (response >= 0) {
+ response = ezusb_fx1_ihex_firmware_download(serial->dev, "whiteheat.fw");
+ if (response >= 0)
+ return 0;
}
- ret = 0;
- response = ezusb_set_reset (serial, 1);
-
- record = (const struct ihex_binrec *)loader_fw->data;
- while (record) {
- response = ezusb_writememory (serial, be32_to_cpu(record->addr),
- (unsigned char *)record->data,
- be16_to_cpu(record->len), 0xa0);
- if (response < 0) {
- dev_err(&serial->dev->dev, "%s - ezusb_writememory "
- "failed for loader (%d %04X %p %d)\n",
- __func__, response, be32_to_cpu(record->addr),
- record->data, be16_to_cpu(record->len));
- break;
- }
- record = ihex_next_binrec(record);
- }
-
- response = ezusb_set_reset(serial, 0);
-
- record = (const struct ihex_binrec *)firmware_fw->data;
- while (record && be32_to_cpu(record->addr) < 0x1b40)
- record = ihex_next_binrec(record);
- while (record) {
- response = ezusb_writememory (serial, be32_to_cpu(record->addr),
- (unsigned char *)record->data,
- be16_to_cpu(record->len), 0xa3);
- if (response < 0) {
- dev_err(&serial->dev->dev, "%s - ezusb_writememory "
- "failed for first firmware step "
- "(%d %04X %p %d)\n", __func__, response,
- be32_to_cpu(record->addr), record->data,
- be16_to_cpu(record->len));
- break;
- }
- ++record;
- }
-
- response = ezusb_set_reset(serial, 1);
-
- record = (const struct ihex_binrec *)firmware_fw->data;
- while (record && be32_to_cpu(record->addr) < 0x1b40) {
- response = ezusb_writememory (serial, be32_to_cpu(record->addr),
- (unsigned char *)record->data,
- be16_to_cpu(record->len), 0xa0);
- if (response < 0) {
- dev_err(&serial->dev->dev, "%s - ezusb_writememory "
- "failed for second firmware step "
- "(%d %04X %p %d)\n", __func__, response,
- be32_to_cpu(record->addr), record->data,
- be16_to_cpu(record->len));
- break;
- }
- ++record;
- }
- ret = 0;
- response = ezusb_set_reset (serial, 0);
- out:
- release_firmware(loader_fw);
- release_firmware(firmware_fw);
- return ret;
+ return -ENOENT;
}
@@ -533,7 +461,7 @@ static int whiteheat_ioctl(struct tty_struct *tty,
struct serial_struct serstruct;
void __user *user_arg = (void __user *)arg;
- dbg("%s - port %d, cmd 0x%.4x", __func__, port->number, cmd);
+ dev_dbg(&port->dev, "%s - cmd 0x%.4x\n", __func__, cmd);
switch (cmd) {
case TIOCGSERIAL:
@@ -580,7 +508,7 @@ static void command_port_write_callback(struct urb *urb)
int status = urb->status;
if (status) {
- dbg("nonzero urb status: %d", status);
+ dev_dbg(&urb->dev->dev, "nonzero urb status: %d\n", status);
return;
}
}
@@ -596,19 +524,18 @@ static void command_port_read_callback(struct urb *urb)
command_info = usb_get_serial_port_data(command_port);
if (!command_info) {
- dbg("%s - command_info is NULL, exiting.", __func__);
+ dev_dbg(&urb->dev->dev, "%s - command_info is NULL, exiting.\n", __func__);
return;
}
if (status) {
- dbg("%s - nonzero urb status: %d", __func__, status);
+ dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n", __func__, status);
if (status != -ENOENT)
command_info->command_finished = WHITEHEAT_CMD_FAILURE;
wake_up(&command_info->wait_command);
return;
}
- usb_serial_debug_data(debug, &command_port->dev,
- __func__, urb->actual_length, data);
+ usb_serial_debug_data(&command_port->dev, __func__, urb->actual_length, data);
if (data[0] == WHITEHEAT_CMD_COMPLETE) {
command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
@@ -619,19 +546,19 @@ static void command_port_read_callback(struct urb *urb)
} else if (data[0] == WHITEHEAT_EVENT) {
/* These are unsolicited reports from the firmware, hence no
waiting command to wakeup */
- dbg("%s - event received", __func__);
+ dev_dbg(&urb->dev->dev, "%s - event received\n", __func__);
} else if (data[0] == WHITEHEAT_GET_DTR_RTS) {
memcpy(command_info->result_buffer, &data[1],
urb->actual_length - 1);
command_info->command_finished = WHITEHEAT_CMD_COMPLETE;
wake_up(&command_info->wait_command);
} else
- dbg("%s - bad reply from firmware", __func__);
+ dev_dbg(&urb->dev->dev, "%s - bad reply from firmware\n", __func__);
/* Continue trying to always read */
result = usb_submit_urb(command_port->read_urb, GFP_ATOMIC);
if (result)
- dbg("%s - failed resubmitting read urb, error %d",
+ dev_dbg(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n",
__func__, result);
}
@@ -645,11 +572,12 @@ static int firm_send_command(struct usb_serial_port *port, __u8 command,
struct usb_serial_port *command_port;
struct whiteheat_command_private *command_info;
struct whiteheat_private *info;
+ struct device *dev = &port->dev;
__u8 *transfer_buffer;
int retval = 0;
int t;
- dbg("%s - command %d", __func__, command);
+ dev_dbg(dev, "%s - command %d\n", __func__, command);
command_port = port->serial->port[COMMAND_PORT];
command_info = usb_get_serial_port_data(command_port);
@@ -662,7 +590,7 @@ static int firm_send_command(struct usb_serial_port *port, __u8 command,
command_port->write_urb->transfer_buffer_length = datasize + 1;
retval = usb_submit_urb(command_port->write_urb, GFP_NOIO);
if (retval) {
- dbg("%s - submit urb failed", __func__);
+ dev_dbg(dev, "%s - submit urb failed\n", __func__);
goto exit;
}
@@ -673,19 +601,19 @@ static int firm_send_command(struct usb_serial_port *port, __u8 command,
usb_kill_urb(command_port->write_urb);
if (command_info->command_finished == false) {
- dbg("%s - command timed out.", __func__);
+ dev_dbg(dev, "%s - command timed out.\n", __func__);
retval = -ETIMEDOUT;
goto exit;
}
if (command_info->command_finished == WHITEHEAT_CMD_FAILURE) {
- dbg("%s - command failed.", __func__);
+ dev_dbg(dev, "%s - command failed.\n", __func__);
retval = -EIO;
goto exit;
}
if (command_info->command_finished == WHITEHEAT_CMD_COMPLETE) {
- dbg("%s - command completed.", __func__);
+ dev_dbg(dev, "%s - command completed.\n", __func__);
switch (command) {
case WHITEHEAT_GET_DTR_RTS:
info = usb_get_serial_port_data(port);
@@ -723,6 +651,7 @@ static int firm_close(struct usb_serial_port *port)
static void firm_setup_port(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
+ struct device *dev = &port->dev;
struct whiteheat_port_settings port_settings;
unsigned int cflag = tty->termios.c_cflag;
@@ -736,7 +665,7 @@ static void firm_setup_port(struct tty_struct *tty)
default:
case CS8: port_settings.bits = 8; break;
}
- dbg("%s - data bits = %d", __func__, port_settings.bits);
+ dev_dbg(dev, "%s - data bits = %d\n", __func__, port_settings.bits);
/* determine the parity */
if (cflag & PARENB)
@@ -752,14 +681,14 @@ static void firm_setup_port(struct tty_struct *tty)
port_settings.parity = WHITEHEAT_PAR_EVEN;
else
port_settings.parity = WHITEHEAT_PAR_NONE;
- dbg("%s - parity = %c", __func__, port_settings.parity);
+ dev_dbg(dev, "%s - parity = %c\n", __func__, port_settings.parity);
/* figure out the stop bits requested */
if (cflag & CSTOPB)
port_settings.stop = 2;
else
port_settings.stop = 1;
- dbg("%s - stop bits = %d", __func__, port_settings.stop);
+ dev_dbg(dev, "%s - stop bits = %d\n", __func__, port_settings.stop);
/* figure out the flow control settings */
if (cflag & CRTSCTS)
@@ -767,7 +696,7 @@ static void firm_setup_port(struct tty_struct *tty)
WHITEHEAT_HFLOW_RTS);
else
port_settings.hflow = WHITEHEAT_HFLOW_NONE;
- dbg("%s - hardware flow control = %s %s %s %s", __func__,
+ dev_dbg(dev, "%s - hardware flow control = %s %s %s %s\n", __func__,
(port_settings.hflow & WHITEHEAT_HFLOW_CTS) ? "CTS" : "",
(port_settings.hflow & WHITEHEAT_HFLOW_RTS) ? "RTS" : "",
(port_settings.hflow & WHITEHEAT_HFLOW_DSR) ? "DSR" : "",
@@ -778,16 +707,15 @@ static void firm_setup_port(struct tty_struct *tty)
port_settings.sflow = WHITEHEAT_SFLOW_RXTX;
else
port_settings.sflow = WHITEHEAT_SFLOW_NONE;
- dbg("%s - software flow control = %c", __func__, port_settings.sflow);
+ dev_dbg(dev, "%s - software flow control = %c\n", __func__, port_settings.sflow);
port_settings.xon = START_CHAR(tty);
port_settings.xoff = STOP_CHAR(tty);
- dbg("%s - XON = %2x, XOFF = %2x",
- __func__, port_settings.xon, port_settings.xoff);
+ dev_dbg(dev, "%s - XON = %2x, XOFF = %2x\n", __func__, port_settings.xon, port_settings.xoff);
/* get the baud rate wanted */
port_settings.baud = tty_get_baud_rate(tty);
- dbg("%s - baud rate = %d", __func__, port_settings.baud);
+ dev_dbg(dev, "%s - baud rate = %d\n", __func__, port_settings.baud);
/* fixme: should set validated settings */
tty_encode_baud_rate(tty, port_settings.baud, port_settings.baud);
@@ -918,6 +846,3 @@ MODULE_LICENSE("GPL");
MODULE_FIRMWARE("whiteheat.fw");
MODULE_FIRMWARE("whiteheat_loader.fw");
-
-module_param(debug, bool, S_IRUGO | S_IWUSR);
-MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/zte_ev.c b/drivers/usb/serial/zte_ev.c
new file mode 100644
index 000000000000..39ee7373b4ee
--- /dev/null
+++ b/drivers/usb/serial/zte_ev.c
@@ -0,0 +1,307 @@
+/*
+ * ZTE_EV USB serial driver
+ *
+ * Copyright (C) 2012 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+ * Copyright (C) 2012 Linux Foundation
+ *
+ * This 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 driver is based on code found in a ZTE_ENV patch that modified
+ * the usb-serial generic driver. Comments were left in that I think
+ * show the commands used to talk to the device, but I am not sure.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <linux/uaccess.h>
+
+#define MAX_SETUP_DATA_SIZE 32
+
+static void debug_data(struct device *dev, const char *function, int len,
+ const unsigned char *data, int result)
+{
+ dev_dbg(dev, "result = %d\n", result);
+ if (result == len)
+ dev_dbg(dev, "%s - length = %d, data = %*ph\n", function,
+ len, len, data);
+}
+
+static int zte_ev_usb_serial_open(struct tty_struct *tty,
+ struct usb_serial_port *port)
+{
+ struct usb_device *udev = port->serial->dev;
+ struct device *dev = &port->dev;
+ int result = 0;
+ int len;
+ unsigned char *buf;
+
+ if (port->number != 0)
+ return -ENODEV;
+
+ buf = kmalloc(MAX_SETUP_DATA_SIZE, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ /* send 1st ctl cmd(CTL 21 22 01 00 00 00 00 00) */
+ len = 0;
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x22, 0x21,
+ 0x0001, 0x0000, NULL, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ dev_dbg(dev, "result = %d\n", result);
+
+ /* send 2st cmd and recieve data */
+ /*
+ * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 25.1.0(5)
+ * 16.0 DI 00 96 00 00 00 00 08
+ */
+ len = 0x0007;
+ result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x21, 0xa1,
+ 0x0000, 0x0000, buf, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ debug_data(dev, __func__, len, buf, result);
+
+ /* send 3 cmd */
+ /*
+ * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 30.1.0
+ * 16.0 DO 80 25 00 00 00 00 08 .%..... 30.2.0
+ */
+ len = 0x0007;
+ buf[0] = 0x80;
+ buf[1] = 0x25;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ buf[6] = 0x08;
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x20, 0x21,
+ 0x0000, 0x0000, buf, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ debug_data(dev, __func__, len, buf, result);
+
+ /* send 4 cmd */
+ /*
+ * 16.0 CTL 21 22 03 00 00 00 00 00
+ */
+ len = 0;
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x22, 0x21,
+ 0x0003, 0x0000, NULL, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ dev_dbg(dev, "result = %d\n", result);
+
+ /* send 5 cmd */
+ /*
+ * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 33.1.0
+ * 16.0 DI 80 25 00 00 00 00 08
+ */
+ len = 0x0007;
+ result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x21, 0xa1,
+ 0x0000, 0x0000, buf, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ debug_data(dev, __func__, len, buf, result);
+
+ /* send 6 cmd */
+ /*
+ * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 34.1.0
+ * 16.0 DO 80 25 00 00 00 00 08
+ */
+ len = 0x0007;
+ buf[0] = 0x80;
+ buf[1] = 0x25;
+ buf[2] = 0x00;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ buf[6] = 0x08;
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x20, 0x21,
+ 0x0000, 0x0000, buf, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ debug_data(dev, __func__, len, buf, result);
+ kfree(buf);
+
+ return usb_serial_generic_open(tty, port);
+}
+
+/*
+ * CTL 21 22 02 00 00 00 00 00 CLASS 338.1.0
+ *
+ * 16.1 DI a1 20 00 00 00 00 02 00 02 00 . ........ 340.1.0
+ * 16.0 CTL 21 22 03 00 00 00 00 00 CLASS 341.1.0
+ *
+ * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 346.1.0(3)
+ * 16.0 DI 00 08 07 00 00 00 08 ....... 346.2.0
+ *
+ * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 349.1.0
+ * 16.0 DO 00 c2 01 00 00 00 08 ....... 349.2.0
+ *
+ * 16.0 CTL 21 22 03 00 00 00 00 00 CLASS 350.1.0(2)
+ *
+ * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 352.1.0
+ * 16.0 DI 00 c2 01 00 00 00 08 ....... 352.2.0
+ *
+ * 16.1 DI a1 20 00 00 00 00 02 00 02 00 . ........ 353.1.0
+ *
+ * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 354.1.0
+ * 16.0 DO 00 c2 01 00 00 00 08 ....... 354.2.0
+ *
+ * 16.0 CTL 21 22 03 00 00 00 00 00
+*/
+
+static void zte_ev_usb_serial_close(struct usb_serial_port *port)
+{
+ struct usb_device *udev = port->serial->dev;
+ struct device *dev = &port->dev;
+ int result = 0;
+ int len;
+ unsigned char *buf;
+
+ if (port->number != 0)
+ return;
+
+ buf = kmalloc(MAX_SETUP_DATA_SIZE, GFP_KERNEL);
+ if (!buf)
+ return;
+
+ /* send 1st ctl cmd(CTL 21 22 02 00 00 00 00 00) */
+ len = 0;
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x22, 0x21,
+ 0x0002, 0x0000, NULL, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ dev_dbg(dev, "result = %d\n", result);
+
+ /* send 2st ctl cmd(CTL 21 22 03 00 00 00 00 00 ) */
+ len = 0;
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x22, 0x21,
+ 0x0003, 0x0000, NULL, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ dev_dbg(dev, "result = %d\n", result);
+
+ /* send 3st cmd and recieve data */
+ /*
+ * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 25.1.0(5)
+ * 16.0 DI 00 08 07 00 00 00 08
+ */
+ len = 0x0007;
+ result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x21, 0xa1,
+ 0x0000, 0x0000, buf, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ debug_data(dev, __func__, len, buf, result);
+
+ /* send 4 cmd */
+ /*
+ * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 30.1.0
+ * 16.0 DO 00 c2 01 00 00 00 08 .%..... 30.2.0
+ */
+ len = 0x0007;
+ buf[0] = 0x00;
+ buf[1] = 0xc2;
+ buf[2] = 0x01;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ buf[6] = 0x08;
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x20, 0x21,
+ 0x0000, 0x0000, buf, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ debug_data(dev, __func__, len, buf, result);
+
+ /* send 5 cmd */
+ /*
+ * 16.0 CTL 21 22 03 00 00 00 00 00
+ */
+ len = 0;
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x22, 0x21,
+ 0x0003, 0x0000, NULL, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ dev_dbg(dev, "result = %d\n", result);
+
+ /* send 6 cmd */
+ /*
+ * 16.0 CTL a1 21 00 00 00 00 07 00 CLASS 33.1.0
+ * 16.0 DI 00 c2 01 00 00 00 08
+ */
+ len = 0x0007;
+ result = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+ 0x21, 0xa1,
+ 0x0000, 0x0000, buf, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ debug_data(dev, __func__, len, buf, result);
+
+ /* send 7 cmd */
+ /*
+ * 16.0 CTL 21 20 00 00 00 00 07 00 CLASS 354.1.0
+ * 16.0 DO 00 c2 01 00 00 00 08 ....... 354.2.0
+ */
+ len = 0x0007;
+ buf[0] = 0x00;
+ buf[1] = 0xc2;
+ buf[2] = 0x01;
+ buf[3] = 0x00;
+ buf[4] = 0x00;
+ buf[5] = 0x00;
+ buf[6] = 0x08;
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x20, 0x21,
+ 0x0000, 0x0000, buf, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ debug_data(dev, __func__, len, buf, result);
+
+ /* send 8 cmd */
+ /*
+ * 16.0 CTL 21 22 03 00 00 00 00 00
+ */
+ len = 0;
+ result = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+ 0x22, 0x21,
+ 0x0003, 0x0000, NULL, len,
+ HZ * USB_CTRL_GET_TIMEOUT);
+ dev_dbg(dev, "result = %d\n", result);
+
+ kfree(buf);
+
+ usb_serial_generic_close(port);
+}
+
+static const struct usb_device_id id_table[] = {
+ { USB_DEVICE(0x19d2, 0xffff) }, /* AC8700 */
+ { USB_DEVICE(0x19d2, 0xfffe) },
+ { USB_DEVICE(0x19d2, 0xfffd) }, /* MG880 */
+ { USB_DEVICE(0x05C6, 0x3197) },
+ { USB_DEVICE(0x05C6, 0x6000) },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static struct usb_serial_driver zio_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "zte_ev",
+ },
+ .id_table = id_table,
+ .num_ports = 1,
+ .open = zte_ev_usb_serial_open,
+ .close = zte_ev_usb_serial_close,
+};
+
+static struct usb_serial_driver * const serial_drivers[] = {
+ &zio_device, NULL
+};
+
+module_usb_serial_driver(serial_drivers, id_table);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index 7691c866637b..0ae7bb64b5ea 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -213,17 +213,3 @@ config USB_UAS
say 'Y' or 'M' here and the kernel will use the right driver.
If you compile this driver as a module, it will be named uas.
-
-config USB_LIBUSUAL
- bool "The shared table of common (or usual) storage devices"
- depends on USB
- help
- This module contains a table of common (or usual) devices
- for usb-storage and ub drivers, and allows to switch binding
- of these devices without rebuilding modules.
-
- Typical syntax of /etc/modprobe.d/*conf is:
-
- options libusual bias="ub"
-
- If unsure, say N.
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
index 82e6416a2d47..4cd55481b309 100644
--- a/drivers/usb/storage/Makefile
+++ b/drivers/usb/storage/Makefile
@@ -12,16 +12,9 @@ obj-$(CONFIG_USB_STORAGE) += usb-storage.o
usb-storage-y := scsiglue.o protocol.o transport.o usb.o
usb-storage-y += initializers.o sierra_ms.o option_ms.o
-
+usb-storage-y += usual-tables.o
usb-storage-$(CONFIG_USB_STORAGE_DEBUG) += debug.o
-ifeq ($(CONFIG_USB_LIBUSUAL),)
- usb-storage-y += usual-tables.o
-else
- obj-$(CONFIG_USB) += usb-libusual.o
- usb-libusual-y := libusual.o usual-tables.o
-endif
-
obj-$(CONFIG_USB_STORAGE_ALAUDA) += ums-alauda.o
obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += ums-cypress.o
obj-$(CONFIG_USB_STORAGE_DATAFAB) += ums-datafab.o
diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c
index bab8c8fe8290..be5564cc8e01 100644
--- a/drivers/usb/storage/alauda.c
+++ b/drivers/usb/storage/alauda.c
@@ -137,7 +137,7 @@ static int init_alauda(struct us_data *us);
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id alauda_usb_ids[] = {
# include "unusual_alauda.h"
diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c
index 5fe451d16e68..070b5c0ebbf9 100644
--- a/drivers/usb/storage/cypress_atacb.c
+++ b/drivers/usb/storage/cypress_atacb.c
@@ -41,7 +41,7 @@ MODULE_LICENSE("GPL");
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id cypress_usb_ids[] = {
# include "unusual_cypress.h"
diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
index 35e9c51e6696..494fee5af41d 100644
--- a/drivers/usb/storage/datafab.c
+++ b/drivers/usb/storage/datafab.c
@@ -86,7 +86,7 @@ static int datafab_determine_lun(struct us_data *us,
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id datafab_usb_ids[] = {
# include "unusual_datafab.h"
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index b28f2ad127d4..118b134a1dad 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -29,9 +29,21 @@
#include "protocol.h"
#include "debug.h"
+#define SD_INIT1_FIRMWARE "ene-ub6250/sd_init1.bin"
+#define SD_INIT2_FIRMWARE "ene-ub6250/sd_init2.bin"
+#define SD_RW_FIRMWARE "ene-ub6250/sd_rdwr.bin"
+#define MS_INIT_FIRMWARE "ene-ub6250/ms_init.bin"
+#define MSP_RW_FIRMWARE "ene-ub6250/msp_rdwr.bin"
+#define MS_RW_FIRMWARE "ene-ub6250/ms_rdwr.bin"
+
MODULE_DESCRIPTION("Driver for ENE UB6250 reader");
MODULE_LICENSE("GPL");
-
+MODULE_FIRMWARE(SD_INIT1_FIRMWARE);
+MODULE_FIRMWARE(SD_INIT2_FIRMWARE);
+MODULE_FIRMWARE(SD_RW_FIRMWARE);
+MODULE_FIRMWARE(MS_INIT_FIRMWARE);
+MODULE_FIRMWARE(MSP_RW_FIRMWARE);
+MODULE_FIRMWARE(MS_RW_FIRMWARE);
/*
* The table of devices
@@ -40,7 +52,7 @@ MODULE_LICENSE("GPL");
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags)}
static struct usb_device_id ene_ub6250_usb_ids[] = {
# include "unusual_ene_ub6250.h"
@@ -1883,28 +1895,28 @@ static int ene_load_bincode(struct us_data *us, unsigned char flag)
/* For SD */
case SD_INIT1_PATTERN:
US_DEBUGP("SD_INIT1_PATTERN\n");
- fw_name = "ene-ub6250/sd_init1.bin";
+ fw_name = SD_INIT1_FIRMWARE;
break;
case SD_INIT2_PATTERN:
US_DEBUGP("SD_INIT2_PATTERN\n");
- fw_name = "ene-ub6250/sd_init2.bin";
+ fw_name = SD_INIT2_FIRMWARE;
break;
case SD_RW_PATTERN:
- US_DEBUGP("SD_RDWR_PATTERN\n");
- fw_name = "ene-ub6250/sd_rdwr.bin";
+ US_DEBUGP("SD_RW_PATTERN\n");
+ fw_name = SD_RW_FIRMWARE;
break;
/* For MS */
case MS_INIT_PATTERN:
US_DEBUGP("MS_INIT_PATTERN\n");
- fw_name = "ene-ub6250/ms_init.bin";
+ fw_name = MS_INIT_FIRMWARE;
break;
case MSP_RW_PATTERN:
US_DEBUGP("MSP_RW_PATTERN\n");
- fw_name = "ene-ub6250/msp_rdwr.bin";
+ fw_name = MSP_RW_FIRMWARE;
break;
case MS_RW_PATTERN:
US_DEBUGP("MS_RW_PATTERN\n");
- fw_name = "ene-ub6250/ms_rdwr.bin";
+ fw_name = MS_RW_FIRMWARE;
break;
default:
US_DEBUGP("----------- Unknown PATTERN ----------\n");
diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c
index 042cf9ef3153..e6df087dca9d 100644
--- a/drivers/usb/storage/freecom.c
+++ b/drivers/usb/storage/freecom.c
@@ -117,7 +117,7 @@ static int init_freecom(struct us_data *us);
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id freecom_usb_ids[] = {
# include "unusual_freecom.h"
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index 31fa24e7e68a..ecea47877364 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -74,7 +74,7 @@ static int isd200_Initialization(struct us_data *us);
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id isd200_usb_ids[] = {
# include "unusual_isd200.h"
@@ -83,7 +83,6 @@ static struct usb_device_id isd200_usb_ids[] = {
MODULE_DEVICE_TABLE(usb, isd200_usb_ids);
#undef UNUSUAL_DEV
-#undef USUAL_DEV
/*
* The flags table
@@ -105,8 +104,6 @@ static struct us_unusual_dev isd200_unusual_dev_list[] = {
};
#undef UNUSUAL_DEV
-#undef USUAL_DEV
-
/* Timeout defines (in Seconds) */
diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
index e3b97383186a..ddc78780b1ad 100644
--- a/drivers/usb/storage/jumpshot.c
+++ b/drivers/usb/storage/jumpshot.c
@@ -69,7 +69,7 @@ MODULE_LICENSE("GPL");
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id jumpshot_usb_ids[] = {
# include "unusual_jumpshot.h"
diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c
index a8708eae9788..f085ffb606c8 100644
--- a/drivers/usb/storage/karma.c
+++ b/drivers/usb/storage/karma.c
@@ -57,7 +57,7 @@ static int rio_karma_init(struct us_data *us);
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id karma_usb_ids[] = {
# include "unusual_karma.h"
diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c
deleted file mode 100644
index fe3ffe1459b2..000000000000
--- a/drivers/usb/storage/libusual.c
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * libusual
- *
- * The libusual contains the table of devices common for ub and usb-storage.
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/usb.h>
-#include <linux/usb_usual.h>
-#include <linux/vmalloc.h>
-#include <linux/kthread.h>
-#include <linux/mutex.h>
-
-/*
- */
-#define USU_MOD_FL_THREAD 1 /* Thread is running */
-#define USU_MOD_FL_PRESENT 2 /* The module is loaded */
-
-struct mod_status {
- unsigned long fls;
-};
-
-static struct mod_status stat[3];
-static DEFINE_SPINLOCK(usu_lock);
-
-/*
- */
-#define USB_US_DEFAULT_BIAS USB_US_TYPE_STOR
-static atomic_t usu_bias = ATOMIC_INIT(USB_US_DEFAULT_BIAS);
-
-#define BIAS_NAME_SIZE (sizeof("usb-storage"))
-static const char *bias_names[3] = { "none", "usb-storage", "ub" };
-
-static DEFINE_MUTEX(usu_probe_mutex);
-static DECLARE_COMPLETION(usu_end_notify);
-static atomic_t total_threads = ATOMIC_INIT(0);
-
-static int usu_probe_thread(void *arg);
-
-/*
- * @type: the module type as an integer
- */
-void usb_usual_set_present(int type)
-{
- struct mod_status *st;
- unsigned long flags;
-
- if (type <= 0 || type >= 3)
- return;
- st = &stat[type];
- spin_lock_irqsave(&usu_lock, flags);
- st->fls |= USU_MOD_FL_PRESENT;
- spin_unlock_irqrestore(&usu_lock, flags);
-}
-EXPORT_SYMBOL_GPL(usb_usual_set_present);
-
-void usb_usual_clear_present(int type)
-{
- struct mod_status *st;
- unsigned long flags;
-
- if (type <= 0 || type >= 3)
- return;
- st = &stat[type];
- spin_lock_irqsave(&usu_lock, flags);
- st->fls &= ~USU_MOD_FL_PRESENT;
- spin_unlock_irqrestore(&usu_lock, flags);
-}
-EXPORT_SYMBOL_GPL(usb_usual_clear_present);
-
-/*
- * Match the calling driver type against the table.
- * Returns: 0 if the device matches.
- */
-int usb_usual_check_type(const struct usb_device_id *id, int caller_type)
-{
- int id_type = USB_US_TYPE(id->driver_info);
-
- if (caller_type <= 0 || caller_type >= 3)
- return -EINVAL;
-
- /* Drivers grab fixed assignment devices */
- if (id_type == caller_type)
- return 0;
- /* Drivers grab devices biased to them */
- if (id_type == USB_US_TYPE_NONE && caller_type == atomic_read(&usu_bias))
- return 0;
- return -ENODEV;
-}
-EXPORT_SYMBOL_GPL(usb_usual_check_type);
-
-/*
- */
-static int usu_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- int rc;
- unsigned long type;
- struct task_struct* task;
- unsigned long flags;
-
- type = USB_US_TYPE(id->driver_info);
- if (type == 0)
- type = atomic_read(&usu_bias);
-
- spin_lock_irqsave(&usu_lock, flags);
- if ((stat[type].fls & (USU_MOD_FL_THREAD|USU_MOD_FL_PRESENT)) != 0) {
- spin_unlock_irqrestore(&usu_lock, flags);
- return -ENXIO;
- }
- stat[type].fls |= USU_MOD_FL_THREAD;
- spin_unlock_irqrestore(&usu_lock, flags);
-
- task = kthread_run(usu_probe_thread, (void*)type, "libusual_%ld", type);
- if (IS_ERR(task)) {
- rc = PTR_ERR(task);
- printk(KERN_WARNING "libusual: "
- "Unable to start the thread for %s: %d\n",
- bias_names[type], rc);
- spin_lock_irqsave(&usu_lock, flags);
- stat[type].fls &= ~USU_MOD_FL_THREAD;
- spin_unlock_irqrestore(&usu_lock, flags);
- return rc; /* Not being -ENXIO causes a message printed */
- }
- atomic_inc(&total_threads);
-
- return -ENXIO;
-}
-
-static void usu_disconnect(struct usb_interface *intf)
-{
- ; /* We should not be here. */
-}
-
-static struct usb_driver usu_driver = {
- .name = "libusual",
- .probe = usu_probe,
- .disconnect = usu_disconnect,
- .id_table = usb_storage_usb_ids,
-};
-
-/*
- * A whole new thread for a purpose of request_module seems quite stupid.
- * The request_module forks once inside again. However, if we attempt
- * to load a storage module from our own modprobe thread, that module
- * references our symbols, which cannot be resolved until our module is
- * initialized. I wish there was a way to wait for the end of initialization.
- * The module notifier reports MODULE_STATE_COMING only.
- * So, we wait until module->init ends as the next best thing.
- */
-static int usu_probe_thread(void *arg)
-{
- int type = (unsigned long) arg;
- struct mod_status *st = &stat[type];
- int rc;
- unsigned long flags;
-
- mutex_lock(&usu_probe_mutex);
- rc = request_module(bias_names[type]);
- spin_lock_irqsave(&usu_lock, flags);
- if (rc == 0 && (st->fls & USU_MOD_FL_PRESENT) == 0) {
- /*
- * This should not happen, but let us keep tabs on it.
- */
- printk(KERN_NOTICE "libusual: "
- "modprobe for %s succeeded, but module is not present\n",
- bias_names[type]);
- }
- st->fls &= ~USU_MOD_FL_THREAD;
- spin_unlock_irqrestore(&usu_lock, flags);
- mutex_unlock(&usu_probe_mutex);
-
- complete_and_exit(&usu_end_notify, 0);
-}
-
-/*
- */
-static int __init usb_usual_init(void)
-{
- int rc;
-
- mutex_lock(&usu_probe_mutex);
- rc = usb_register(&usu_driver);
- mutex_unlock(&usu_probe_mutex);
- return rc;
-}
-
-static void __exit usb_usual_exit(void)
-{
- /*
- * We do not check for any drivers present, because
- * they keep us pinned with symbol references.
- */
-
- usb_deregister(&usu_driver);
-
- while (atomic_read(&total_threads) > 0) {
- wait_for_completion(&usu_end_notify);
- atomic_dec(&total_threads);
- }
-}
-
-/*
- * Validate and accept the bias parameter.
- */
-static int usu_set_bias(const char *bias_s, struct kernel_param *kp)
-{
- int i;
- int len;
- int bias_n = 0;
-
- len = strlen(bias_s);
- if (len == 0)
- return -EDOM;
- if (bias_s[len-1] == '\n')
- --len;
-
- for (i = 1; i < 3; i++) {
- if (strncmp(bias_s, bias_names[i], len) == 0) {
- bias_n = i;
- break;
- }
- }
- if (bias_n == 0)
- return -EINVAL;
-
- atomic_set(&usu_bias, bias_n);
- return 0;
-}
-
-static int usu_get_bias(char *buffer, struct kernel_param *kp)
-{
- return strlen(strcpy(buffer, bias_names[atomic_read(&usu_bias)]));
-}
-
-module_init(usb_usual_init);
-module_exit(usb_usual_exit);
-
-module_param_call(bias, usu_set_bias, usu_get_bias, NULL, S_IRUGO|S_IWUSR);
-__MODULE_PARM_TYPE(bias, "string");
-MODULE_PARM_DESC(bias, "Bias to usb-storage or ub");
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c
index 886567a3806d..cb79de61f4c8 100644
--- a/drivers/usb/storage/onetouch.c
+++ b/drivers/usb/storage/onetouch.c
@@ -67,7 +67,7 @@ struct usb_onetouch {
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id onetouch_usb_ids[] = {
# include "unusual_onetouch.h"
diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c
index 63cf2822e299..d36446dd7ae8 100644
--- a/drivers/usb/storage/realtek_cr.c
+++ b/drivers/usb/storage/realtek_cr.c
@@ -172,7 +172,7 @@ static int init_realtek_cr(struct us_data *us);
initFunction, flags) \
{\
USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24)\
+ .driver_info = (flags) \
}
static const struct usb_device_id realtek_cr_ids[] = {
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index 3252a62b31bc..7bd54e0d5120 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -69,7 +69,7 @@ static int usb_stor_sddr09_init(struct us_data *us);
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id sddr09_usb_ids[] = {
# include "unusual_sddr09.h"
diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
index c144078065a7..d278c5a99b7a 100644
--- a/drivers/usb/storage/sddr55.c
+++ b/drivers/usb/storage/sddr55.c
@@ -46,7 +46,7 @@ MODULE_LICENSE("GPL");
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id sddr55_usb_ids[] = {
# include "unusual_sddr55.h"
diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c
index fa1ceebc465c..daf2fc58ae02 100644
--- a/drivers/usb/storage/shuttle_usbat.c
+++ b/drivers/usb/storage/shuttle_usbat.c
@@ -168,7 +168,7 @@ static int init_usbat_flash(struct us_data *us);
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id usbat_usb_ids[] = {
# include "unusual_usbat.h"
diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c
index 37539c89e3ba..17e36952bced 100644
--- a/drivers/usb/storage/sierra_ms.c
+++ b/drivers/usb/storage/sierra_ms.c
@@ -130,14 +130,13 @@ int sierra_ms_init(struct us_data *us)
struct swoc_info *swocInfo;
struct usb_device *udev;
struct Scsi_Host *sh;
- struct scsi_device *sd;
retries = 3;
result = 0;
udev = us->pusb_dev;
sh = us_to_host(us);
- sd = scsi_get_host_dev(sh);
+ scsi_get_host_dev(sh);
US_DEBUGP("SWIMS: sierra_ms_init called\n");
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index c70109e5d60b..c0543c83923e 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -1331,7 +1331,7 @@ int usb_stor_port_reset(struct us_data *us)
int result;
/*for these devices we must use the class specific method */
- if (us->pusb_dev->quirks & USB_QUIRK_RESET_MORPHS)
+ if (us->pusb_dev->quirks & USB_QUIRK_RESET)
return -EPERM;
result = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c
index 638cd64f9610..98b98eef7527 100644
--- a/drivers/usb/storage/uas.c
+++ b/drivers/usb/storage/uas.c
@@ -41,6 +41,7 @@ struct sense_iu_old {
struct uas_dev_info {
struct usb_interface *intf;
struct usb_device *udev;
+ struct usb_anchor cmd_urbs;
struct usb_anchor sense_urbs;
struct usb_anchor data_urbs;
int qdepth, resetting;
@@ -49,6 +50,7 @@ struct uas_dev_info {
unsigned use_streams:1;
unsigned uas_sense_old:1;
struct scsi_cmnd *cmnd;
+ spinlock_t lock;
};
enum {
@@ -63,13 +65,13 @@ enum {
DATA_IN_URB_INFLIGHT = (1 << 9),
DATA_OUT_URB_INFLIGHT = (1 << 10),
COMMAND_COMPLETED = (1 << 11),
+ COMMAND_ABORTED = (1 << 12),
};
/* Overrides scsi_pointer */
struct uas_cmd_info {
unsigned int state;
unsigned int stream;
- unsigned int aborted;
struct urb *cmd_urb;
struct urb *data_in_urb;
struct urb *data_out_urb;
@@ -90,6 +92,7 @@ static void uas_do_work(struct work_struct *work)
struct uas_cmd_info *cmdinfo;
struct uas_cmd_info *temp;
struct list_head list;
+ unsigned long flags;
int err;
spin_lock_irq(&uas_work_lock);
@@ -100,7 +103,10 @@ static void uas_do_work(struct work_struct *work)
struct scsi_pointer *scp = (void *)cmdinfo;
struct scsi_cmnd *cmnd = container_of(scp,
struct scsi_cmnd, SCp);
- err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
+ struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
+ spin_lock_irqsave(&devinfo->lock, flags);
+ err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
+ spin_unlock_irqrestore(&devinfo->lock, flags);
if (err) {
list_del(&cmdinfo->list);
spin_lock_irq(&uas_work_lock);
@@ -162,7 +168,7 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller)
struct uas_cmd_info *ci = (void *)&cmnd->SCp;
scmd_printk(KERN_INFO, cmnd, "%s %p tag %d, inflight:"
- "%s%s%s%s%s%s%s%s%s%s%s\n",
+ "%s%s%s%s%s%s%s%s%s%s%s%s\n",
caller, cmnd, cmnd->request->tag,
(ci->state & SUBMIT_STATUS_URB) ? " s-st" : "",
(ci->state & ALLOC_DATA_IN_URB) ? " a-in" : "",
@@ -174,13 +180,16 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller)
(ci->state & COMMAND_INFLIGHT) ? " CMD" : "",
(ci->state & DATA_IN_URB_INFLIGHT) ? " IN" : "",
(ci->state & DATA_OUT_URB_INFLIGHT) ? " OUT" : "",
- (ci->state & COMMAND_COMPLETED) ? " done" : "");
+ (ci->state & COMMAND_COMPLETED) ? " done" : "",
+ (ci->state & COMMAND_ABORTED) ? " abort" : "");
}
static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)
{
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+ struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
+ WARN_ON(!spin_is_locked(&devinfo->lock));
if (cmdinfo->state & (COMMAND_INFLIGHT |
DATA_IN_URB_INFLIGHT |
DATA_OUT_URB_INFLIGHT))
@@ -189,6 +198,10 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)
cmdinfo->state |= COMMAND_COMPLETED;
usb_free_urb(cmdinfo->data_in_urb);
usb_free_urb(cmdinfo->data_out_urb);
+ if (cmdinfo->state & COMMAND_ABORTED) {
+ scmd_printk(KERN_INFO, cmnd, "abort completed\n");
+ cmnd->result = DID_ABORT << 16;
+ }
cmnd->scsi_done(cmnd);
return 0;
}
@@ -216,6 +229,7 @@ static void uas_stat_cmplt(struct urb *urb)
struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
struct scsi_cmnd *cmnd;
struct uas_cmd_info *cmdinfo;
+ unsigned long flags;
u16 tag;
if (urb->status) {
@@ -229,20 +243,24 @@ static void uas_stat_cmplt(struct urb *urb)
return;
}
+ spin_lock_irqsave(&devinfo->lock, flags);
tag = be16_to_cpup(&iu->tag) - 1;
if (tag == 0)
cmnd = devinfo->cmnd;
else
cmnd = scsi_host_find_tag(shost, tag - 1);
+
if (!cmnd) {
- if (iu->iu_id != IU_ID_RESPONSE) {
- usb_free_urb(urb);
- return;
+ if (iu->iu_id == IU_ID_RESPONSE) {
+ /* store results for uas_eh_task_mgmt() */
+ memcpy(&devinfo->response, iu, sizeof(devinfo->response));
}
- } else {
- cmdinfo = (void *)&cmnd->SCp;
+ usb_free_urb(urb);
+ spin_unlock_irqrestore(&devinfo->lock, flags);
+ return;
}
+ cmdinfo = (void *)&cmnd->SCp;
switch (iu->iu_id) {
case IU_ID_STATUS:
if (devinfo->cmnd == cmnd)
@@ -256,10 +274,16 @@ static void uas_stat_cmplt(struct urb *urb)
uas_sense(urb, cmnd);
if (cmnd->result != 0) {
/* cancel data transfers on error */
- if (cmdinfo->state & DATA_IN_URB_INFLIGHT)
+ if (cmdinfo->state & DATA_IN_URB_INFLIGHT) {
+ spin_unlock_irqrestore(&devinfo->lock, flags);
usb_unlink_urb(cmdinfo->data_in_urb);
- if (cmdinfo->state & DATA_OUT_URB_INFLIGHT)
+ spin_lock_irqsave(&devinfo->lock, flags);
+ }
+ if (cmdinfo->state & DATA_OUT_URB_INFLIGHT) {
+ spin_unlock_irqrestore(&devinfo->lock, flags);
usb_unlink_urb(cmdinfo->data_out_urb);
+ spin_lock_irqsave(&devinfo->lock, flags);
+ }
}
cmdinfo->state &= ~COMMAND_INFLIGHT;
uas_try_complete(cmnd, __func__);
@@ -270,23 +294,23 @@ static void uas_stat_cmplt(struct urb *urb)
case IU_ID_WRITE_READY:
uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB);
break;
- case IU_ID_RESPONSE:
- /* store results for uas_eh_task_mgmt() */
- memcpy(&devinfo->response, iu, sizeof(devinfo->response));
- break;
default:
scmd_printk(KERN_ERR, cmnd,
"Bogus IU (%d) received on status pipe\n", iu->iu_id);
}
usb_free_urb(urb);
+ spin_unlock_irqrestore(&devinfo->lock, flags);
}
static void uas_data_cmplt(struct urb *urb)
{
struct scsi_cmnd *cmnd = urb->context;
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+ struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
struct scsi_data_buffer *sdb = NULL;
+ unsigned long flags;
+ spin_lock_irqsave(&devinfo->lock, flags);
if (cmdinfo->data_in_urb == urb) {
sdb = scsi_in(cmnd);
cmdinfo->state &= ~DATA_IN_URB_INFLIGHT;
@@ -301,10 +325,8 @@ static void uas_data_cmplt(struct urb *urb)
} else {
sdb->resid = sdb->length - urb->actual_length;
}
- if (cmdinfo->aborted) {
- return;
- }
uas_try_complete(cmnd, __func__);
+ spin_unlock_irqrestore(&devinfo->lock, flags);
}
static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
@@ -431,6 +453,7 @@ static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp,
err = usb_submit_urb(urb, gfp);
if (err)
goto err;
+ usb_anchor_urb(urb, &devinfo->cmd_urbs);
return 0;
@@ -470,6 +493,7 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
int err;
+ WARN_ON(!spin_is_locked(&devinfo->lock));
if (cmdinfo->state & SUBMIT_STATUS_URB) {
err = uas_submit_sense_urb(cmnd->device->host, gfp,
cmdinfo->stream);
@@ -521,18 +545,22 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
if (cmdinfo->state & ALLOC_CMD_URB) {
cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd,
- cmdinfo->stream);
+ cmdinfo->stream);
if (!cmdinfo->cmd_urb)
return SCSI_MLQUEUE_DEVICE_BUSY;
cmdinfo->state &= ~ALLOC_CMD_URB;
}
if (cmdinfo->state & SUBMIT_CMD_URB) {
+ usb_get_urb(cmdinfo->cmd_urb);
if (usb_submit_urb(cmdinfo->cmd_urb, gfp)) {
scmd_printk(KERN_INFO, cmnd,
"cmd urb submission failure\n");
return SCSI_MLQUEUE_DEVICE_BUSY;
}
+ usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs);
+ usb_put_urb(cmdinfo->cmd_urb);
+ cmdinfo->cmd_urb = NULL;
cmdinfo->state &= ~SUBMIT_CMD_URB;
cmdinfo->state |= COMMAND_INFLIGHT;
}
@@ -546,12 +574,16 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
struct scsi_device *sdev = cmnd->device;
struct uas_dev_info *devinfo = sdev->hostdata;
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+ unsigned long flags;
int err;
BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer));
- if (devinfo->cmnd)
+ spin_lock_irqsave(&devinfo->lock, flags);
+ if (devinfo->cmnd) {
+ spin_unlock_irqrestore(&devinfo->lock, flags);
return SCSI_MLQUEUE_DEVICE_BUSY;
+ }
if (blk_rq_tagged(cmnd->request)) {
cmdinfo->stream = cmnd->request->tag + 2;
@@ -564,7 +596,6 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
cmdinfo->state = SUBMIT_STATUS_URB |
ALLOC_CMD_URB | SUBMIT_CMD_URB;
- cmdinfo->aborted = 0;
switch (cmnd->sc_data_direction) {
case DMA_FROM_DEVICE:
@@ -587,6 +618,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
if (err) {
/* If we did nothing, give up now */
if (cmdinfo->state & SUBMIT_STATUS_URB) {
+ spin_unlock_irqrestore(&devinfo->lock, flags);
return SCSI_MLQUEUE_DEVICE_BUSY;
}
spin_lock(&uas_work_lock);
@@ -595,6 +627,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
schedule_work(&uas_work);
}
+ spin_unlock_irqrestore(&devinfo->lock, flags);
return 0;
}
@@ -605,22 +638,28 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,
{
struct Scsi_Host *shost = cmnd->device->host;
struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
- u16 tag = 9999; /* FIXME */
+ u16 tag = devinfo->qdepth - 1;
+ unsigned long flags;
+ spin_lock_irqsave(&devinfo->lock, flags);
memset(&devinfo->response, 0, sizeof(devinfo->response));
- if (uas_submit_sense_urb(shost, GFP_NOIO, tag)) {
+ if (uas_submit_sense_urb(shost, GFP_ATOMIC, tag)) {
shost_printk(KERN_INFO, shost,
"%s: %s: submit sense urb failed\n",
__func__, fname);
+ spin_unlock_irqrestore(&devinfo->lock, flags);
return FAILED;
}
- if (uas_submit_task_urb(cmnd, GFP_NOIO, function, tag)) {
+ if (uas_submit_task_urb(cmnd, GFP_ATOMIC, function, tag)) {
shost_printk(KERN_INFO, shost,
"%s: %s: submit task mgmt urb failed\n",
__func__, fname);
+ spin_unlock_irqrestore(&devinfo->lock, flags);
return FAILED;
}
- if (0 == usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000)) {
+ spin_unlock_irqrestore(&devinfo->lock, flags);
+
+ if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000) == 0) {
shost_printk(KERN_INFO, shost,
"%s: %s timed out\n", __func__, fname);
return FAILED;
@@ -643,15 +682,15 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,
static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
{
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+ struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
+ unsigned long flags;
int ret;
uas_log_cmd_state(cmnd, __func__);
- cmdinfo->aborted = 1;
+ spin_lock_irqsave(&devinfo->lock, flags);
+ cmdinfo->state |= COMMAND_ABORTED;
+ spin_unlock_irqrestore(&devinfo->lock, flags);
ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK);
- if (cmdinfo->state & DATA_IN_URB_INFLIGHT)
- usb_kill_urb(cmdinfo->data_in_urb);
- if (cmdinfo->state & DATA_OUT_URB_INFLIGHT)
- usb_kill_urb(cmdinfo->data_out_urb);
return ret;
}
@@ -670,6 +709,7 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)
int err;
devinfo->resetting = 1;
+ usb_kill_anchored_urbs(&devinfo->cmd_urbs);
usb_kill_anchored_urbs(&devinfo->sense_urbs);
usb_kill_anchored_urbs(&devinfo->data_urbs);
err = usb_reset_device(udev);
@@ -694,7 +734,7 @@ static int uas_slave_configure(struct scsi_device *sdev)
{
struct uas_dev_info *devinfo = sdev->hostdata;
scsi_set_tag_type(sdev, MSG_ORDERED_TAG);
- scsi_activate_tcq(sdev, devinfo->qdepth - 2);
+ scsi_activate_tcq(sdev, devinfo->qdepth - 3);
return 0;
}
@@ -868,11 +908,13 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
devinfo->intf = intf;
devinfo->udev = udev;
devinfo->resetting = 0;
+ init_usb_anchor(&devinfo->cmd_urbs);
init_usb_anchor(&devinfo->sense_urbs);
init_usb_anchor(&devinfo->data_urbs);
+ spin_lock_init(&devinfo->lock);
uas_configure_endpoints(devinfo);
- result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2);
+ result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3);
if (result)
goto free;
@@ -913,6 +955,7 @@ static void uas_disconnect(struct usb_interface *intf)
struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
scsi_remove_host(shost);
+ usb_kill_anchored_urbs(&devinfo->cmd_urbs);
usb_kill_anchored_urbs(&devinfo->sense_urbs);
usb_kill_anchored_urbs(&devinfo->data_urbs);
uas_free_streams(devinfo);
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 62a31bea0634..779cd954abcb 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2038,25 +2038,25 @@ UNUSUAL_DEV( 0xed10, 0x7636, 0x0001, 0x0001,
USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ),
/* Control/Bulk transport for all SubClass values */
-USUAL_DEV(USB_SC_RBC, USB_PR_CB, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8020, USB_PR_CB, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_QIC, USB_PR_CB, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_UFI, USB_PR_CB, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8070, USB_PR_CB, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_SCSI, USB_PR_CB, USB_US_TYPE_STOR),
+USUAL_DEV(USB_SC_RBC, USB_PR_CB),
+USUAL_DEV(USB_SC_8020, USB_PR_CB),
+USUAL_DEV(USB_SC_QIC, USB_PR_CB),
+USUAL_DEV(USB_SC_UFI, USB_PR_CB),
+USUAL_DEV(USB_SC_8070, USB_PR_CB),
+USUAL_DEV(USB_SC_SCSI, USB_PR_CB),
/* Control/Bulk/Interrupt transport for all SubClass values */
-USUAL_DEV(USB_SC_RBC, USB_PR_CBI, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8020, USB_PR_CBI, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_QIC, USB_PR_CBI, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_UFI, USB_PR_CBI, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8070, USB_PR_CBI, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_SCSI, USB_PR_CBI, USB_US_TYPE_STOR),
+USUAL_DEV(USB_SC_RBC, USB_PR_CBI),
+USUAL_DEV(USB_SC_8020, USB_PR_CBI),
+USUAL_DEV(USB_SC_QIC, USB_PR_CBI),
+USUAL_DEV(USB_SC_UFI, USB_PR_CBI),
+USUAL_DEV(USB_SC_8070, USB_PR_CBI),
+USUAL_DEV(USB_SC_SCSI, USB_PR_CBI),
/* Bulk-only transport for all SubClass values */
-USUAL_DEV(USB_SC_RBC, USB_PR_BULK, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8020, USB_PR_BULK, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_QIC, USB_PR_BULK, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_UFI, USB_PR_BULK, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8070, USB_PR_BULK, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_SCSI, USB_PR_BULK, 0),
+USUAL_DEV(USB_SC_RBC, USB_PR_BULK),
+USUAL_DEV(USB_SC_8020, USB_PR_BULK),
+USUAL_DEV(USB_SC_QIC, USB_PR_BULK),
+USUAL_DEV(USB_SC_UFI, USB_PR_BULK),
+USUAL_DEV(USB_SC_8070, USB_PR_BULK),
+USUAL_DEV(USB_SC_SCSI, USB_PR_BULK),
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index d012fe4329e7..12aa72630aed 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -114,7 +114,7 @@ MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks");
#define COMPLIANT_DEV UNUSUAL_DEV
-#define USUAL_DEV(use_protocol, use_transport, use_type) \
+#define USUAL_DEV(use_protocol, use_transport) \
{ \
.useProtocol = use_protocol, \
.useTransport = use_transport, \
@@ -126,7 +126,7 @@ static struct us_unusual_dev us_unusual_dev_list[] = {
};
static struct us_unusual_dev for_dynamic_ids =
- USUAL_DEV(USB_SC_SCSI, USB_PR_BULK, 0);
+ USUAL_DEV(USB_SC_SCSI, USB_PR_BULK);
#undef UNUSUAL_DEV
#undef COMPLIANT_DEV
@@ -564,7 +564,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id,
us->protocol = (unusual_dev->useTransport == USB_PR_DEVICE) ?
idesc->bInterfaceProtocol :
unusual_dev->useTransport;
- us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
+ us->fflags = id->driver_info;
adjust_quirks(us);
if (us->fflags & US_FL_IGNORE_DEVICE) {
@@ -1041,13 +1041,10 @@ static int storage_probe(struct usb_interface *intf,
int size;
/*
- * If libusual is configured, let it decide whether a standard
- * device should be handled by usb-storage or by ub.
* If the device isn't standard (is handled by a subdriver
* module) then don't accept it.
*/
- if (usb_usual_check_type(id, USB_US_TYPE_STOR) ||
- usb_usual_ignore_device(intf))
+ if (usb_usual_ignore_device(intf))
return -ENXIO;
/*
@@ -1105,10 +1102,8 @@ static int __init usb_stor_init(void)
/* register the driver, return usb_register return code if error */
retval = usb_register(&usb_storage_driver);
- if (retval == 0) {
+ if (retval == 0)
pr_info("USB Mass Storage support registered.\n");
- usb_usual_set_present(USB_US_TYPE_STOR);
- }
return retval;
}
@@ -1122,8 +1117,6 @@ static void __exit usb_stor_exit(void)
*/
US_DEBUGP("-- calling usb_deregister()\n");
usb_deregister(&usb_storage_driver) ;
-
- usb_usual_clear_present(USB_US_TYPE_STOR);
}
module_init(usb_stor_init);
diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c
index b96927914f89..b78a526910fb 100644
--- a/drivers/usb/storage/usual-tables.c
+++ b/drivers/usb/storage/usual-tables.c
@@ -34,31 +34,23 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
-
-#define COMPLIANT_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
- vendorName, productName, useProtocol, useTransport, \
- initFunction, flags) \
-{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-#define USUAL_DEV(useProto, useTrans, useType) \
-{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \
- .driver_info = ((useType)<<24) }
+#define COMPLIANT_DEV UNUSUAL_DEV
+
+#define USUAL_DEV(useProto, useTrans) \
+{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans) }
struct usb_device_id usb_storage_usb_ids[] = {
# include "unusual_devs.h"
{ } /* Terminating entry */
};
-EXPORT_SYMBOL_GPL(usb_storage_usb_ids);
-
MODULE_DEVICE_TABLE(usb, usb_storage_usb_ids);
#undef UNUSUAL_DEV
#undef COMPLIANT_DEV
#undef USUAL_DEV
-
/*
* The table of devices to ignore
*/
@@ -95,7 +87,6 @@ static struct ignore_entry ignore_ids[] = {
#undef UNUSUAL_DEV
-
/* Return an error if a device is in the ignore_ids list */
int usb_usual_ignore_device(struct usb_interface *intf)
{
@@ -115,4 +106,3 @@ int usb_usual_ignore_device(struct usb_interface *intf)
}
return 0;
}
-EXPORT_SYMBOL_GPL(usb_usual_ignore_device);
diff --git a/drivers/usb/wusbcore/Kconfig b/drivers/usb/wusbcore/Kconfig
index f29fdd7f6d75..8bf19760d447 100644
--- a/drivers/usb/wusbcore/Kconfig
+++ b/drivers/usb/wusbcore/Kconfig
@@ -2,8 +2,7 @@
# Wireless USB Core configuration
#
config USB_WUSB
- tristate "Enable Wireless USB extensions (EXPERIMENTAL)"
- depends on EXPERIMENTAL
+ tristate "Enable Wireless USB extensions"
depends on USB
depends on PCI
depends on UWB
diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c
index fa810a83e830..dd88441c8f78 100644
--- a/drivers/usb/wusbcore/security.c
+++ b/drivers/usb/wusbcore/security.c
@@ -202,7 +202,7 @@ int wusb_dev_sec_add(struct wusbhc *wusbhc,
{
int result, bytes, secd_size;
struct device *dev = &usb_dev->dev;
- struct usb_security_descriptor *secd;
+ struct usb_security_descriptor *secd, *new_secd;
const struct usb_encryption_descriptor *etd, *ccm1_etd = NULL;
const void *itr, *top;
char buf[64];
@@ -221,11 +221,12 @@ int wusb_dev_sec_add(struct wusbhc *wusbhc,
goto out;
}
secd_size = le16_to_cpu(secd->wTotalLength);
- secd = krealloc(secd, secd_size, GFP_KERNEL);
- if (secd == NULL) {
+ new_secd = krealloc(secd, secd_size, GFP_KERNEL);
+ if (new_secd == NULL) {
dev_err(dev, "Can't allocate space for security descriptors\n");
goto out;
}
+ secd = new_secd;
result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
0, secd, secd_size);
if (result < secd_size) {
diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c
index 9e4a92461688..a09b65ebd9bb 100644
--- a/drivers/usb/wusbcore/wa-hc.c
+++ b/drivers/usb/wusbcore/wa-hc.c
@@ -46,8 +46,10 @@ int wa_create(struct wahc *wa, struct usb_interface *iface)
wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc;
wa->xfer_result_size = usb_endpoint_maxp(wa->dti_epd);
wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL);
- if (wa->xfer_result == NULL)
+ if (wa->xfer_result == NULL) {
+ result = -ENOMEM;
goto error_xfer_result_alloc;
+ }
result = wa_nep_create(wa, iface);
if (result < 0) {
dev_err(dev, "WA-CDS: can't initialize notif endpoint: %d\n",
diff --git a/drivers/vfio/pci/vfio_pci_intrs.c b/drivers/vfio/pci/vfio_pci_intrs.c
index 211a4920b88a..d8dedc7d3910 100644
--- a/drivers/vfio/pci/vfio_pci_intrs.c
+++ b/drivers/vfio/pci/vfio_pci_intrs.c
@@ -76,9 +76,24 @@ static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key)
schedule_work(&virqfd->inject);
}
- if (flags & POLLHUP)
- /* The eventfd is closing, detach from VFIO */
- virqfd_deactivate(virqfd);
+ if (flags & POLLHUP) {
+ unsigned long flags;
+ spin_lock_irqsave(&virqfd->vdev->irqlock, flags);
+
+ /*
+ * The eventfd is closing, if the virqfd has not yet been
+ * queued for release, as determined by testing whether the
+ * vdev pointer to it is still valid, queue it now. As
+ * with kvm irqfds, we know we won't race against the virqfd
+ * going away because we hold wqh->lock to get here.
+ */
+ if (*(virqfd->pvirqfd) == virqfd) {
+ *(virqfd->pvirqfd) = NULL;
+ virqfd_deactivate(virqfd);
+ }
+
+ spin_unlock_irqrestore(&virqfd->vdev->irqlock, flags);
+ }
return 0;
}
@@ -93,7 +108,6 @@ static void virqfd_ptable_queue_proc(struct file *file,
static void virqfd_shutdown(struct work_struct *work)
{
struct virqfd *virqfd = container_of(work, struct virqfd, shutdown);
- struct virqfd **pvirqfd = virqfd->pvirqfd;
u64 cnt;
eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt);
@@ -101,7 +115,6 @@ static void virqfd_shutdown(struct work_struct *work)
eventfd_ctx_put(virqfd->eventfd);
kfree(virqfd);
- *pvirqfd = NULL;
}
static void virqfd_inject(struct work_struct *work)
@@ -122,15 +135,11 @@ static int virqfd_enable(struct vfio_pci_device *vdev,
int ret = 0;
unsigned int events;
- if (*pvirqfd)
- return -EBUSY;
-
virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL);
if (!virqfd)
return -ENOMEM;
virqfd->pvirqfd = pvirqfd;
- *pvirqfd = virqfd;
virqfd->vdev = vdev;
virqfd->handler = handler;
virqfd->thread = thread;
@@ -154,6 +163,23 @@ static int virqfd_enable(struct vfio_pci_device *vdev,
virqfd->eventfd = ctx;
/*
+ * virqfds can be released by closing the eventfd or directly
+ * through ioctl. These are both done through a workqueue, so
+ * we update the pointer to the virqfd under lock to avoid
+ * pushing multiple jobs to release the same virqfd.
+ */
+ spin_lock_irq(&vdev->irqlock);
+
+ if (*pvirqfd) {
+ spin_unlock_irq(&vdev->irqlock);
+ ret = -EBUSY;
+ goto fail;
+ }
+ *pvirqfd = virqfd;
+
+ spin_unlock_irq(&vdev->irqlock);
+
+ /*
* Install our own custom wake-up handling so we are notified via
* a callback whenever someone signals the underlying eventfd.
*/
@@ -187,19 +213,29 @@ fail:
fput(file);
kfree(virqfd);
- *pvirqfd = NULL;
return ret;
}
-static void virqfd_disable(struct virqfd *virqfd)
+static void virqfd_disable(struct vfio_pci_device *vdev,
+ struct virqfd **pvirqfd)
{
- if (!virqfd)
- return;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vdev->irqlock, flags);
+
+ if (*pvirqfd) {
+ virqfd_deactivate(*pvirqfd);
+ *pvirqfd = NULL;
+ }
- virqfd_deactivate(virqfd);
+ spin_unlock_irqrestore(&vdev->irqlock, flags);
- /* Block until we know all outstanding shutdown jobs have completed. */
+ /*
+ * Block until we know all outstanding shutdown jobs have completed.
+ * Even if we don't queue the job, flush the wq to be sure it's
+ * been released.
+ */
flush_workqueue(vfio_irqfd_cleanup_wq);
}
@@ -392,8 +428,8 @@ static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd)
static void vfio_intx_disable(struct vfio_pci_device *vdev)
{
vfio_intx_set_signal(vdev, -1);
- virqfd_disable(vdev->ctx[0].unmask);
- virqfd_disable(vdev->ctx[0].mask);
+ virqfd_disable(vdev, &vdev->ctx[0].unmask);
+ virqfd_disable(vdev, &vdev->ctx[0].mask);
vdev->irq_type = VFIO_PCI_NUM_IRQS;
vdev->num_ctx = 0;
kfree(vdev->ctx);
@@ -539,8 +575,8 @@ static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix)
vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix);
for (i = 0; i < vdev->num_ctx; i++) {
- virqfd_disable(vdev->ctx[i].unmask);
- virqfd_disable(vdev->ctx[i].mask);
+ virqfd_disable(vdev, &vdev->ctx[i].unmask);
+ virqfd_disable(vdev, &vdev->ctx[i].mask);
}
if (msix) {
@@ -577,7 +613,7 @@ static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev,
vfio_send_intx_eventfd, NULL,
&vdev->ctx[0].unmask, fd);
- virqfd_disable(vdev->ctx[0].unmask);
+ virqfd_disable(vdev, &vdev->ctx[0].unmask);
}
return 0;
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 0217f7415ef5..20c33c42600a 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -979,18 +979,6 @@ config FB_PVR2
(<file:drivers/video/pvr2fb.c>). Please see the file
<file:Documentation/fb/pvr2fb.txt>.
-config FB_EPSON1355
- bool "Epson 1355 framebuffer support"
- depends on (FB = y) && ARCH_CEIVA
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- help
- Build in support for the SED1355 Epson Research Embedded RAMDAC
- LCD/CRT Controller (since redesignated as the S1D13505) as a
- framebuffer. Product specs at
- <http://vdc.epson.com/>.
-
config FB_S1D13XXX
tristate "Epson S1D13XXX framebuffer support"
depends on FB
@@ -1788,7 +1776,7 @@ config FB_AU1200
config FB_VT8500
bool "VT8500 LCD Driver"
- depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_VT8500
+ depends on (FB = y) && ARM && ARCH_VT8500
select FB_WMT_GE_ROPS
select FB_SYS_IMAGEBLIT
help
@@ -1797,11 +1785,11 @@ config FB_VT8500
config FB_WM8505
bool "WM8505 frame buffer support"
- depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_WM8505
+ depends on (FB = y) && ARM && ARCH_VT8500
select FB_WMT_GE_ROPS
select FB_SYS_IMAGEBLIT
help
- This is the framebuffer driver for WonderMedia WM8505
+ This is the framebuffer driver for WonderMedia WM8505/WM8650
integrated LCD controller.
source "drivers/video/geode/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index ee8dafb69e36..194035986af2 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -94,7 +94,6 @@ obj-$(CONFIG_FB_G364) += g364fb.o
obj-$(CONFIG_FB_EP93XX) += ep93xx-fb.o
obj-$(CONFIG_FB_SA1100) += sa1100fb.o
obj-$(CONFIG_FB_HIT) += hitfb.o
-obj-$(CONFIG_FB_EPSON1355) += epson1355fb.o
obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o
obj-$(CONFIG_FB_PVR2) += pvr2fb.o
obj-$(CONFIG_FB_VOODOO1) += sstfb.o
diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c
index f75da8758adc..f49181c73113 100644
--- a/drivers/video/backlight/88pm860x_bl.c
+++ b/drivers/video/backlight/88pm860x_bl.c
@@ -228,7 +228,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
data->port = pdata->flags;
if (data->port < 0) {
dev_err(&pdev->dev, "wrong platform data is assigned");
- kfree(data);
return -EINVAL;
}
diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c
index 7ae9d53f2bf1..113d43a16f54 100644
--- a/drivers/video/da8xx-fb.c
+++ b/drivers/video/da8xx-fb.c
@@ -131,7 +131,7 @@
#define UPPER_MARGIN 32
#define LOWER_MARGIN 32
-static resource_size_t da8xx_fb_reg_base;
+static void __iomem *da8xx_fb_reg_base;
static struct resource *lcdc_regs;
static unsigned int lcd_revision;
static irq_handler_t lcdc_irq_handler;
@@ -951,7 +951,7 @@ static int __devexit fb_remove(struct platform_device *dev)
clk_disable(par->lcdc_clk);
clk_put(par->lcdc_clk);
framebuffer_release(info);
- iounmap((void __iomem *)da8xx_fb_reg_base);
+ iounmap(da8xx_fb_reg_base);
release_mem_region(lcdc_regs->start, resource_size(lcdc_regs));
}
@@ -1171,7 +1171,7 @@ static int __devinit fb_probe(struct platform_device *device)
if (!lcdc_regs)
return -EBUSY;
- da8xx_fb_reg_base = (resource_size_t)ioremap(lcdc_regs->start, len);
+ da8xx_fb_reg_base = ioremap(lcdc_regs->start, len);
if (!da8xx_fb_reg_base) {
ret = -EBUSY;
goto err_request_mem;
@@ -1392,7 +1392,7 @@ err_clk_put:
clk_put(fb_clk);
err_ioremap:
- iounmap((void __iomem *)da8xx_fb_reg_base);
+ iounmap(da8xx_fb_reg_base);
err_request_mem:
release_mem_region(lcdc_regs->start, len);
diff --git a/drivers/video/efifb.c b/drivers/video/efifb.c
index b4a632ada401..932abaa58a89 100644
--- a/drivers/video/efifb.c
+++ b/drivers/video/efifb.c
@@ -553,7 +553,9 @@ static int __init efifb_init(void)
int ret;
char *option = NULL;
- dmi_check_system(dmi_system_table);
+ if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI ||
+ !(screen_info.capabilities & VIDEO_CAPABILITY_SKIP_QUIRKS))
+ dmi_check_system(dmi_system_table);
if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
return -ENODEV;
diff --git a/drivers/video/epson1355fb.c b/drivers/video/epson1355fb.c
deleted file mode 100644
index 68b9b511ce80..000000000000
--- a/drivers/video/epson1355fb.c
+++ /dev/null
@@ -1,749 +0,0 @@
-/*
- * linux/drivers/video/epson1355fb.c -- Epson S1D13505 frame buffer for 2.5.
- *
- * Epson Research S1D13505 Embedded RAMDAC LCD/CRT Controller
- * (previously known as SED1355)
- *
- * Cf. http://vdc.epson.com/
- *
- *
- * Copyright (C) Hewlett-Packard Company. All rights reserved.
- *
- * Written by Christopher Hoover <ch@hpl.hp.com>
- *
- * Adapted from:
- *
- * linux/drivers/video/skeletonfb.c
- * Modified to new api Jan 2001 by James Simmons (jsimmons@infradead.org)
- * Created 28 Dec 1997 by Geert Uytterhoeven
- *
- * linux/drivers/video/epson1355fb.c (2.4 driver)
- * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
- *
- * 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.
- *
- *
- * Noteworthy Issues
- * -----------------
- *
- * This driver is complicated by the fact that this is a 16-bit chip
- * and, on at least one platform (ceiva), we can only do 16-bit reads
- * and writes to the framebuffer. We hide this from user space
- * except in the case of mmap().
- *
- *
- * To Do
- * -----
- *
- * - Test 8-bit pseudocolor mode
- * - Allow setting bpp, virtual resolution
- * - Implement horizontal panning
- * - (maybe) Implement hardware cursor
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/delay.h>
-#include <linux/fb.h>
-#include <linux/init.h>
-#include <linux/ioport.h>
-#include <linux/platform_device.h>
-
-#include <asm/types.h>
-#include <asm/io.h>
-#include <linux/uaccess.h>
-
-#include <video/epson1355.h>
-
-struct epson1355_par {
- unsigned long reg_addr;
- u32 pseudo_palette[16];
-};
-
-/* ------------------------------------------------------------------------- */
-
-#if defined(CONFIG_ARM)
-
-# ifdef CONFIG_ARCH_CEIVA
-# include <mach/hardware.h>
-# define EPSON1355FB_BASE_PHYS (CEIVA_PHYS_SED1355)
-# endif
-
-static inline u8 epson1355_read_reg(struct epson1355_par *par, int index)
-{
- return __raw_readb(par->reg_addr + index);
-}
-
-static inline void epson1355_write_reg(struct epson1355_par *par, u8 data, int index)
-{
- __raw_writeb(data, par->reg_addr + index);
-}
-
-#else
-# error "no architecture-specific epson1355_{read,write}_reg"
-#endif
-
-#ifndef EPSON1355FB_BASE_PHYS
-# error "EPSON1355FB_BASE_PHYS is not defined"
-#endif
-
-#define EPSON1355FB_REGS_OFS (0)
-#define EPSON1355FB_REGS_PHYS (EPSON1355FB_BASE_PHYS + EPSON1355FB_REGS_OFS)
-#define EPSON1355FB_REGS_LEN (64)
-
-#define EPSON1355FB_FB_OFS (0x00200000)
-#define EPSON1355FB_FB_PHYS (EPSON1355FB_BASE_PHYS + EPSON1355FB_FB_OFS)
-#define EPSON1355FB_FB_LEN (2 * 1024 * 1024)
-
-/* ------------------------------------------------------------------------- */
-
-static inline u16 epson1355_read_reg16(struct epson1355_par *par, int index)
-{
- u8 lo = epson1355_read_reg(par, index);
- u8 hi = epson1355_read_reg(par, index + 1);
-
- return (hi << 8) | lo;
-}
-
-static inline void epson1355_write_reg16(struct epson1355_par *par, u16 data, int index)
-{
- u8 lo = data & 0xff;
- u8 hi = (data >> 8) & 0xff;
-
- epson1355_write_reg(par, lo, index);
- epson1355_write_reg(par, hi, index + 1);
-}
-
-static inline u32 epson1355_read_reg20(struct epson1355_par *par, int index)
-{
- u8 b0 = epson1355_read_reg(par, index);
- u8 b1 = epson1355_read_reg(par, index + 1);
- u8 b2 = epson1355_read_reg(par, index + 2);
-
- return (b2 & 0x0f) << 16 | (b1 << 8) | b0;
-}
-
-static inline void epson1355_write_reg20(struct epson1355_par *par, u32 data, int index)
-{
- u8 b0 = data & 0xff;
- u8 b1 = (data >> 8) & 0xff;
- u8 b2 = (data >> 16) & 0x0f;
-
- epson1355_write_reg(par, b0, index);
- epson1355_write_reg(par, b1, index + 1);
- epson1355_write_reg(par, b2, index + 2);
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void set_lut(struct epson1355_par *par, u8 index, u8 r, u8 g, u8 b)
-{
- epson1355_write_reg(par, index, REG_LUT_ADDR);
- epson1355_write_reg(par, r, REG_LUT_DATA);
- epson1355_write_reg(par, g, REG_LUT_DATA);
- epson1355_write_reg(par, b, REG_LUT_DATA);
-}
-
-
-/**
- * epson1355fb_setcolreg - sets a color register.
- * @regno: Which register in the CLUT we are programming
- * @red: The red value which can be up to 16 bits wide
- * @green: The green value which can be up to 16 bits wide
- * @blue: The blue value which can be up to 16 bits wide.
- * @transp: If supported the alpha value which can be up to 16 bits wide.
- * @info: frame buffer info structure
- *
- * Returns negative errno on error, or zero on success.
- */
-static int epson1355fb_setcolreg(unsigned regno, unsigned r, unsigned g,
- unsigned b, unsigned transp,
- struct fb_info *info)
-{
- struct epson1355_par *par = info->par;
-
- if (info->var.grayscale)
- r = g = b = (19595 * r + 38470 * g + 7471 * b) >> 16;
-
- switch (info->fix.visual) {
- case FB_VISUAL_TRUECOLOR:
- if (regno >= 16)
- return -EINVAL;
-
- ((u32 *) info->pseudo_palette)[regno] =
- (r & 0xf800) | (g & 0xfc00) >> 5 | (b & 0xf800) >> 11;
-
- break;
- case FB_VISUAL_PSEUDOCOLOR:
- if (regno >= 256)
- return -EINVAL;
-
- set_lut(par, regno, r >> 8, g >> 8, b >> 8);
-
- break;
- default:
- return -ENOSYS;
- }
- return 0;
-}
-
-/* ------------------------------------------------------------------------- */
-
-/**
- * epson1355fb_pan_display - Pans the display.
- * @var: frame buffer variable screen structure
- * @info: frame buffer structure that represents a single frame buffer
- *
- * Pan (or wrap, depending on the `vmode' field) the display using the
- * `xoffset' and `yoffset' fields of the `var' structure.
- * If the values don't fit, return -EINVAL.
- *
- * Returns negative errno on error, or zero on success.
- */
-static int epson1355fb_pan_display(struct fb_var_screeninfo *var,
- struct fb_info *info)
-{
- struct epson1355_par *par = info->par;
- u32 start;
-
- if (var->xoffset != 0) /* not yet ... */
- return -EINVAL;
-
- if (var->yoffset + info->var.yres > info->var.yres_virtual)
- return -EINVAL;
-
- start = (info->fix.line_length >> 1) * var->yoffset;
-
- epson1355_write_reg20(par, start, REG_SCRN1_DISP_START_ADDR0);
-
- return 0;
-}
-
-/* ------------------------------------------------------------------------- */
-
-static void lcd_enable(struct epson1355_par *par, int enable)
-{
- u8 mode = epson1355_read_reg(par, REG_DISPLAY_MODE);
-
- if (enable)
- mode |= 1;
- else
- mode &= ~1;
-
- epson1355_write_reg(par, mode, REG_DISPLAY_MODE);
-}
-
-#if defined(CONFIG_ARCH_CEIVA)
-static void backlight_enable(int enable)
-{
- /* ### this should be protected by a spinlock ... */
- u8 pddr = clps_readb(PDDR);
- if (enable)
- pddr |= (1 << 5);
- else
- pddr &= ~(1 << 5);
- clps_writeb(pddr, PDDR);
-}
-#else
-static void backlight_enable(int enable)
-{
-}
-#endif
-
-
-/**
- * epson1355fb_blank - blanks the display.
- * @blank_mode: the blank mode we want.
- * @info: frame buffer structure that represents a single frame buffer
- *
- * Blank the screen if blank_mode != 0, else unblank. Return 0 if
- * blanking succeeded, != 0 if un-/blanking failed due to e.g. a
- * video mode which doesn't support it. Implements VESA suspend
- * and powerdown modes on hardware that supports disabling hsync/vsync:
- * blank_mode == 2: suspend vsync
- * blank_mode == 3: suspend hsync
- * blank_mode == 4: powerdown
- *
- * Returns negative errno on error, or zero on success.
- *
- */
-static int epson1355fb_blank(int blank_mode, struct fb_info *info)
-{
- struct epson1355_par *par = info->par;
-
- switch (blank_mode) {
- case FB_BLANK_UNBLANK:
- case FB_BLANK_NORMAL:
- lcd_enable(par, 1);
- backlight_enable(1);
- break;
- case FB_BLANK_VSYNC_SUSPEND:
- case FB_BLANK_HSYNC_SUSPEND:
- backlight_enable(0);
- break;
- case FB_BLANK_POWERDOWN:
- backlight_enable(0);
- lcd_enable(par, 0);
- break;
- default:
- return -EINVAL;
- }
-
- /* let fbcon do a soft blank for us */
- return (blank_mode == FB_BLANK_NORMAL) ? 1 : 0;
-}
-
-/* ------------------------------------------------------------------------- */
-
-/*
- * We can't use the cfb generic routines, as we have to limit
- * ourselves to 16-bit or 8-bit loads and stores to this 16-bit
- * chip.
- */
-
-static inline void epson1355fb_fb_writel(unsigned long v, unsigned long *a)
-{
- u16 *p = (u16 *) a;
- u16 l = v & 0xffff;
- u16 h = v >> 16;
-
- fb_writew(l, p);
- fb_writew(h, p + 1);
-}
-
-static inline unsigned long epson1355fb_fb_readl(const unsigned long *a)
-{
- const u16 *p = (u16 *) a;
- u16 l = fb_readw(p);
- u16 h = fb_readw(p + 1);
-
- return (h << 16) | l;
-}
-
-#define FB_READL epson1355fb_fb_readl
-#define FB_WRITEL epson1355fb_fb_writel
-
-/* ------------------------------------------------------------------------- */
-
-static inline unsigned long copy_from_user16(void *to, const void *from,
- unsigned long n)
-{
- u16 *dst = (u16 *) to;
- u16 *src = (u16 *) from;
-
- if (!access_ok(VERIFY_READ, from, n))
- return n;
-
- while (n > 1) {
- u16 v;
- if (__get_user(v, src))
- return n;
-
- fb_writew(v, dst);
-
- src++, dst++;
- n -= 2;
- }
-
- if (n) {
- u8 v;
-
- if (__get_user(v, ((u8 *) src)))
- return n;
-
- fb_writeb(v, dst);
- }
- return 0;
-}
-
-static inline unsigned long copy_to_user16(void *to, const void *from,
- unsigned long n)
-{
- u16 *dst = (u16 *) to;
- u16 *src = (u16 *) from;
-
- if (!access_ok(VERIFY_WRITE, to, n))
- return n;
-
- while (n > 1) {
- u16 v = fb_readw(src);
-
- if (__put_user(v, dst))
- return n;
-
- src++, dst++;
- n -= 2;
- }
-
- if (n) {
- u8 v = fb_readb(src);
-
- if (__put_user(v, ((u8 *) dst)))
- return n;
- }
- return 0;
-}
-
-
-static ssize_t
-epson1355fb_read(struct fb_info *info, char *buf, size_t count, loff_t * ppos)
-{
- unsigned long p = *ppos;
-
- if (p >= info->fix.smem_len)
- return 0;
- if (count >= info->fix.smem_len)
- count = info->fix.smem_len;
- if (count + p > info->fix.smem_len)
- count = info->fix.smem_len - p;
-
- if (count) {
- char *base_addr;
-
- base_addr = info->screen_base;
- count -= copy_to_user16(buf, base_addr + p, count);
- if (!count)
- return -EFAULT;
- *ppos += count;
- }
- return count;
-}
-
-static ssize_t
-epson1355fb_write(struct fb_info *info, const char *buf,
- size_t count, loff_t * ppos)
-{
- unsigned long p = *ppos;
- int err;
-
- /* from fbmem.c except for our own copy_*_user */
- if (p > info->fix.smem_len)
- return -ENOSPC;
- if (count >= info->fix.smem_len)
- count = info->fix.smem_len;
- err = 0;
- if (count + p > info->fix.smem_len) {
- count = info->fix.smem_len - p;
- err = -ENOSPC;
- }
-
- if (count) {
- char *base_addr;
-
- base_addr = info->screen_base;
- count -= copy_from_user16(base_addr + p, buf, count);
- *ppos += count;
- err = -EFAULT;
- }
- if (count)
- return count;
- return err;
-}
-
-/* ------------------------------------------------------------------------- */
-
-static struct fb_ops epson1355fb_fbops = {
- .owner = THIS_MODULE,
- .fb_setcolreg = epson1355fb_setcolreg,
- .fb_pan_display = epson1355fb_pan_display,
- .fb_blank = epson1355fb_blank,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- .fb_read = epson1355fb_read,
- .fb_write = epson1355fb_write,
-};
-
-/* ------------------------------------------------------------------------- */
-
-static __init unsigned int get_fb_size(struct fb_info *info)
-{
- unsigned int size = 2 * 1024 * 1024;
- char *p = info->screen_base;
-
- /* the 512k framebuffer is aliased at start + 0x80000 * n */
- fb_writeb(1, p);
- fb_writeb(0, p + 0x80000);
- if (!fb_readb(p))
- size = 512 * 1024;
-
- fb_writeb(0, p);
-
- return size;
-}
-
-static int epson1355_width_tab[2][4] __devinitdata =
- { {4, 8, 16, -1}, {9, 12, 16, -1} };
-static int epson1355_bpp_tab[8] __devinitdata = { 1, 2, 4, 8, 15, 16 };
-
-static void __devinit fetch_hw_state(struct fb_info *info, struct epson1355_par *par)
-{
- struct fb_var_screeninfo *var = &info->var;
- struct fb_fix_screeninfo *fix = &info->fix;
- u8 panel, display;
- u16 offset;
- u32 xres, yres;
- u32 xres_virtual, yres_virtual;
- int bpp, lcd_bpp;
- int is_color, is_dual, is_tft;
- int lcd_enabled, crt_enabled;
-
- fix->type = FB_TYPE_PACKED_PIXELS;
-
- display = epson1355_read_reg(par, REG_DISPLAY_MODE);
- bpp = epson1355_bpp_tab[(display >> 2) & 7];
-
- switch (bpp) {
- case 8:
- fix->visual = FB_VISUAL_PSEUDOCOLOR;
- var->bits_per_pixel = 8;
- var->red.offset = var->green.offset = var->blue.offset = 0;
- var->red.length = var->green.length = var->blue.length = 8;
- break;
- case 16:
- /* 5-6-5 RGB */
- fix->visual = FB_VISUAL_TRUECOLOR;
- var->bits_per_pixel = 16;
- var->red.offset = 11;
- var->red.length = 5;
- var->green.offset = 5;
- var->green.length = 6;
- var->blue.offset = 0;
- var->blue.length = 5;
- break;
- default:
- BUG();
- }
- fb_alloc_cmap(&(info->cmap), 256, 0);
-
- panel = epson1355_read_reg(par, REG_PANEL_TYPE);
- is_color = (panel & 0x04) != 0;
- is_dual = (panel & 0x02) != 0;
- is_tft = (panel & 0x01) != 0;
- crt_enabled = (display & 0x02) != 0;
- lcd_enabled = (display & 0x01) != 0;
- lcd_bpp = epson1355_width_tab[is_tft][(panel >> 4) & 3];
-
- xres = (epson1355_read_reg(par, REG_HORZ_DISP_WIDTH) + 1) * 8;
- yres = (epson1355_read_reg16(par, REG_VERT_DISP_HEIGHT0) + 1) *
- ((is_dual && !crt_enabled) ? 2 : 1);
- offset = epson1355_read_reg16(par, REG_MEM_ADDR_OFFSET0) & 0x7ff;
- xres_virtual = offset * 16 / bpp;
- yres_virtual = fix->smem_len / (offset * 2);
-
- var->xres = xres;
- var->yres = yres;
- var->xres_virtual = xres_virtual;
- var->yres_virtual = yres_virtual;
- var->xoffset = var->yoffset = 0;
-
- fix->line_length = offset * 2;
-
- fix->xpanstep = 0; /* no pan yet */
- fix->ypanstep = 1;
- fix->ywrapstep = 0;
- fix->accel = FB_ACCEL_NONE;
-
- var->grayscale = !is_color;
-
-#ifdef DEBUG
- printk(KERN_INFO
- "epson1355fb: xres=%d, yres=%d, "
- "is_color=%d, is_dual=%d, is_tft=%d\n",
- xres, yres, is_color, is_dual, is_tft);
- printk(KERN_INFO
- "epson1355fb: bpp=%d, lcd_bpp=%d, "
- "crt_enabled=%d, lcd_enabled=%d\n",
- bpp, lcd_bpp, crt_enabled, lcd_enabled);
-#endif
-}
-
-
-static void clearfb16(struct fb_info *info)
-{
- u16 *dst = (u16 *) info->screen_base;
- unsigned long n = info->fix.smem_len;
-
- while (n > 1) {
- fb_writew(0, dst);
- dst++, n -= 2;
- }
-
- if (n)
- fb_writeb(0, dst);
-}
-
-static int epson1355fb_remove(struct platform_device *dev)
-{
- struct fb_info *info = platform_get_drvdata(dev);
- struct epson1355_par *par = info->par;
-
- backlight_enable(0);
- if (par) {
- lcd_enable(par, 0);
- if (par && par->reg_addr)
- iounmap((void *) par->reg_addr);
- }
-
- if (info) {
- fb_dealloc_cmap(&info->cmap);
- if (info->screen_base)
- iounmap(info->screen_base);
- framebuffer_release(info);
- }
- release_mem_region(EPSON1355FB_FB_PHYS, EPSON1355FB_FB_LEN);
- release_mem_region(EPSON1355FB_REGS_PHYS, EPSON1355FB_REGS_LEN);
- return 0;
-}
-
-static int __devinit epson1355fb_probe(struct platform_device *dev)
-{
- struct epson1355_par *default_par;
- struct fb_info *info;
- u8 revision;
- int rc = 0;
-
- if (!request_mem_region(EPSON1355FB_REGS_PHYS, EPSON1355FB_REGS_LEN, "S1D13505 registers")) {
- printk(KERN_ERR "epson1355fb: unable to reserve "
- "registers at 0x%0x\n", EPSON1355FB_REGS_PHYS);
- rc = -EBUSY;
- goto bail;
- }
-
- if (!request_mem_region(EPSON1355FB_FB_PHYS, EPSON1355FB_FB_LEN,
- "S1D13505 framebuffer")) {
- printk(KERN_ERR "epson1355fb: unable to reserve "
- "framebuffer at 0x%0x\n", EPSON1355FB_FB_PHYS);
- rc = -EBUSY;
- goto bail;
- }
-
- info = framebuffer_alloc(sizeof(struct epson1355_par), &dev->dev);
- if (!info) {
- rc = -ENOMEM;
- goto bail;
- }
-
- default_par = info->par;
- default_par->reg_addr = (unsigned long) ioremap(EPSON1355FB_REGS_PHYS, EPSON1355FB_REGS_LEN);
- if (!default_par->reg_addr) {
- printk(KERN_ERR "epson1355fb: unable to map registers\n");
- rc = -ENOMEM;
- goto bail;
- }
- info->pseudo_palette = default_par->pseudo_palette;
-
- info->screen_base = ioremap(EPSON1355FB_FB_PHYS, EPSON1355FB_FB_LEN);
- if (!info->screen_base) {
- printk(KERN_ERR "epson1355fb: unable to map framebuffer\n");
- rc = -ENOMEM;
- goto bail;
- }
-
- revision = epson1355_read_reg(default_par, REG_REVISION_CODE);
- if ((revision >> 2) != 3) {
- printk(KERN_INFO "epson1355fb: epson1355 not found\n");
- rc = -ENODEV;
- goto bail;
- }
-
- info->fix.mmio_start = EPSON1355FB_REGS_PHYS;
- info->fix.mmio_len = EPSON1355FB_REGS_LEN;
- info->fix.smem_start = EPSON1355FB_FB_PHYS;
- info->fix.smem_len = get_fb_size(info);
-
- printk(KERN_INFO "epson1355fb: regs mapped at 0x%lx, fb %d KiB mapped at 0x%p\n",
- default_par->reg_addr, info->fix.smem_len / 1024, info->screen_base);
-
- strcpy(info->fix.id, "S1D13505");
- info->par = default_par;
- info->fbops = &epson1355fb_fbops;
- info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
-
- /* we expect the boot loader to have initialized the chip
- with appropriate parameters from which we can determinte
- the flavor of lcd panel attached */
- fetch_hw_state(info, default_par);
-
- /* turn this puppy on ... */
- clearfb16(info);
- backlight_enable(1);
- lcd_enable(default_par, 1);
-
- if (register_framebuffer(info) < 0) {
- rc = -EINVAL;
- goto bail;
- }
- /*
- * Our driver data.
- */
- platform_set_drvdata(dev, info);
-
- printk(KERN_INFO "fb%d: %s frame buffer device\n",
- info->node, info->fix.id);
-
- return 0;
-
- bail:
- epson1355fb_remove(dev);
- return rc;
-}
-
-static struct platform_driver epson1355fb_driver = {
- .probe = epson1355fb_probe,
- .remove = epson1355fb_remove,
- .driver = {
- .name = "epson1355fb",
- },
-};
-
-static struct platform_device *epson1355fb_device;
-
-int __init epson1355fb_init(void)
-{
- int ret = 0;
-
- if (fb_get_options("epson1355fb", NULL))
- return -ENODEV;
-
- ret = platform_driver_register(&epson1355fb_driver);
-
- if (!ret) {
- epson1355fb_device = platform_device_alloc("epson1355fb", 0);
-
- if (epson1355fb_device)
- ret = platform_device_add(epson1355fb_device);
- else
- ret = -ENOMEM;
-
- if (ret) {
- platform_device_put(epson1355fb_device);
- platform_driver_unregister(&epson1355fb_driver);
- }
- }
-
- return ret;
-}
-
-module_init(epson1355fb_init);
-
-#ifdef MODULE
-static void __exit epson1355fb_exit(void)
-{
- platform_device_unregister(epson1355fb_device);
- platform_driver_unregister(&epson1355fb_driver);
-}
-
-/* ------------------------------------------------------------------------- */
-
-module_exit(epson1355fb_exit);
-#endif
-
-MODULE_AUTHOR("Christopher Hoover <ch@hpl.hp.com>");
-MODULE_DESCRIPTION("Framebuffer driver for Epson S1D13505");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/exynos/exynos_mipi_dsi.c b/drivers/video/exynos/exynos_mipi_dsi.c
index 4bc2b8a5dd8b..663c308d0e73 100644
--- a/drivers/video/exynos/exynos_mipi_dsi.c
+++ b/drivers/video/exynos/exynos_mipi_dsi.c
@@ -461,7 +461,7 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
done:
platform_set_drvdata(pdev, dsim);
- dev_dbg(&pdev->dev, "%s() completed sucessfuly (%s mode)\n", __func__,
+ dev_dbg(&pdev->dev, "%s() completed successfully (%s mode)\n", __func__,
dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB");
return 0;
diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c
index 8e4a446b5ed1..b244f060f151 100644
--- a/drivers/video/tmiofb.c
+++ b/drivers/video/tmiofb.c
@@ -694,6 +694,10 @@ static int __devinit tmiofb_probe(struct platform_device *dev)
dev_err(&dev->dev, "NULL platform data!\n");
return -EINVAL;
}
+ if (ccr == NULL || lcr == NULL || vram == NULL || irq < 0) {
+ dev_err(&dev->dev, "missing resources\n");
+ return -EINVAL;
+ }
info = framebuffer_alloc(sizeof(struct tmiofb_par), &dev->dev);
diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
index 66a74f9073fb..9af8da70e781 100644
--- a/drivers/video/vt8500lcdfb.c
+++ b/drivers/video/vt8500lcdfb.c
@@ -35,6 +35,13 @@
#include "vt8500lcdfb.h"
#include "wmt_ge_rops.h"
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/memblock.h>
+#endif
+
+
#define to_vt8500lcd_info(__info) container_of(__info, \
struct vt8500lcd_info, fb)
@@ -270,15 +277,21 @@ static int __devinit vt8500lcd_probe(struct platform_device *pdev)
{
struct vt8500lcd_info *fbi;
struct resource *res;
- struct vt8500fb_platform_data *pdata = pdev->dev.platform_data;
void *addr;
int irq, ret;
+ struct fb_videomode of_mode;
+ struct device_node *np;
+ u32 bpp;
+ dma_addr_t fb_mem_phys;
+ unsigned long fb_mem_len;
+ void *fb_mem_virt;
+
ret = -ENOMEM;
fbi = NULL;
- fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16,
- GFP_KERNEL);
+ fbi = devm_kzalloc(&pdev->dev, sizeof(struct vt8500lcd_info)
+ + sizeof(u32) * 16, GFP_KERNEL);
if (!fbi) {
dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
ret = -ENOMEM;
@@ -333,9 +346,45 @@ static int __devinit vt8500lcd_probe(struct platform_device *pdev)
goto failed_free_res;
}
- fbi->fb.fix.smem_start = pdata->video_mem_phys;
- fbi->fb.fix.smem_len = pdata->video_mem_len;
- fbi->fb.screen_base = pdata->video_mem_virt;
+ np = of_parse_phandle(pdev->dev.of_node, "default-mode", 0);
+ if (!np) {
+ pr_err("%s: No display description in Device Tree\n", __func__);
+ ret = -EINVAL;
+ goto failed_free_res;
+ }
+
+ /*
+ * This code is copied from Sascha Hauer's of_videomode helper
+ * and can be replaced with a call to the helper once mainlined
+ */
+ ret = 0;
+ ret |= of_property_read_u32(np, "hactive", &of_mode.xres);
+ ret |= of_property_read_u32(np, "vactive", &of_mode.yres);
+ ret |= of_property_read_u32(np, "hback-porch", &of_mode.left_margin);
+ ret |= of_property_read_u32(np, "hfront-porch", &of_mode.right_margin);
+ ret |= of_property_read_u32(np, "hsync-len", &of_mode.hsync_len);
+ ret |= of_property_read_u32(np, "vback-porch", &of_mode.upper_margin);
+ ret |= of_property_read_u32(np, "vfront-porch", &of_mode.lower_margin);
+ ret |= of_property_read_u32(np, "vsync-len", &of_mode.vsync_len);
+ ret |= of_property_read_u32(np, "bpp", &bpp);
+ if (ret) {
+ pr_err("%s: Unable to read display properties\n", __func__);
+ goto failed_free_res;
+ }
+ of_mode.vmode = FB_VMODE_NONINTERLACED;
+
+ /* try allocating the framebuffer */
+ fb_mem_len = of_mode.xres * of_mode.yres * 2 * (bpp / 8);
+ fb_mem_virt = dma_alloc_coherent(&pdev->dev, fb_mem_len, &fb_mem_phys,
+ GFP_KERNEL);
+ if (!fb_mem_virt) {
+ pr_err("%s: Failed to allocate framebuffer\n", __func__);
+ return -ENOMEM;
+ };
+
+ fbi->fb.fix.smem_start = fb_mem_phys;
+ fbi->fb.fix.smem_len = fb_mem_len;
+ fbi->fb.screen_base = fb_mem_virt;
fbi->palette_size = PAGE_ALIGN(512);
fbi->palette_cpu = dma_alloc_coherent(&pdev->dev,
@@ -370,10 +419,11 @@ static int __devinit vt8500lcd_probe(struct platform_device *pdev)
goto failed_free_irq;
}
- fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
- fbi->fb.var.bits_per_pixel = pdata->bpp;
- fbi->fb.var.xres_virtual = pdata->xres_virtual;
- fbi->fb.var.yres_virtual = pdata->yres_virtual;
+ fb_videomode_to_var(&fbi->fb.var, &of_mode);
+
+ fbi->fb.var.xres_virtual = of_mode.xres;
+ fbi->fb.var.yres_virtual = of_mode.yres * 2;
+ fbi->fb.var.bits_per_pixel = bpp;
ret = vt8500lcd_set_par(&fbi->fb);
if (ret) {
@@ -448,12 +498,18 @@ static int __devexit vt8500lcd_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id via_dt_ids[] = {
+ { .compatible = "via,vt8500-fb", },
+ {}
+};
+
static struct platform_driver vt8500lcd_driver = {
.probe = vt8500lcd_probe,
.remove = __devexit_p(vt8500lcd_remove),
.driver = {
.owner = THIS_MODULE,
.name = "vt8500-lcd",
+ .of_match_table = of_match_ptr(via_dt_ids),
},
};
@@ -461,4 +517,5 @@ module_platform_driver(vt8500lcd_driver);
MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
MODULE_DESCRIPTION("LCD controller driver for VIA VT8500");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, via_dt_ids);
diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c
index ffeff4838120..77539c1b56a0 100644
--- a/drivers/video/wm8505fb.c
+++ b/drivers/video/wm8505fb.c
@@ -28,6 +28,9 @@
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/wait.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/memblock.h>
#include <linux/platform_data/video-vt8500lcdfb.h>
@@ -59,8 +62,12 @@ static int wm8505fb_init_hw(struct fb_info *info)
writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR);
writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1);
- /* Set in-memory picture format to RGB 32bpp */
- writel(0x1c, fbi->regbase + WMT_GOVR_COLORSPACE);
+ /*
+ * Set in-memory picture format to RGB
+ * 0x31C sets the correct color mode (RGB565) for WM8650
+ * Bit 8+9 (0x300) are ignored on WM8505 as reserved
+ */
+ writel(0x31c, fbi->regbase + WMT_GOVR_COLORSPACE);
writel(1, fbi->regbase + WMT_GOVR_COLORSPACE1);
/* Virtual buffer size */
@@ -127,6 +134,18 @@ static int wm8505fb_set_par(struct fb_info *info)
info->var.blue.msb_right = 0;
info->fix.visual = FB_VISUAL_TRUECOLOR;
info->fix.line_length = info->var.xres_virtual << 2;
+ } else if (info->var.bits_per_pixel == 16) {
+ info->var.red.offset = 11;
+ info->var.red.length = 5;
+ info->var.red.msb_right = 0;
+ info->var.green.offset = 5;
+ info->var.green.length = 6;
+ info->var.green.msb_right = 0;
+ info->var.blue.offset = 0;
+ info->var.blue.length = 5;
+ info->var.blue.msb_right = 0;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.line_length = info->var.xres_virtual << 1;
}
wm8505fb_set_timing(info);
@@ -246,16 +265,20 @@ static int __devinit wm8505fb_probe(struct platform_device *pdev)
struct wm8505fb_info *fbi;
struct resource *res;
void *addr;
- struct vt8500fb_platform_data *pdata;
int ret;
- pdata = pdev->dev.platform_data;
+ struct fb_videomode of_mode;
+ struct device_node *np;
+ u32 bpp;
+ dma_addr_t fb_mem_phys;
+ unsigned long fb_mem_len;
+ void *fb_mem_virt;
ret = -ENOMEM;
fbi = NULL;
- fbi = kzalloc(sizeof(struct wm8505fb_info) + sizeof(u32) * 16,
- GFP_KERNEL);
+ fbi = devm_kzalloc(&pdev->dev, sizeof(struct wm8505fb_info) +
+ sizeof(u32) * 16, GFP_KERNEL);
if (!fbi) {
dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
ret = -ENOMEM;
@@ -305,21 +328,58 @@ static int __devinit wm8505fb_probe(struct platform_device *pdev)
goto failed_free_res;
}
- fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+ np = of_parse_phandle(pdev->dev.of_node, "default-mode", 0);
+ if (!np) {
+ pr_err("%s: No display description in Device Tree\n", __func__);
+ ret = -EINVAL;
+ goto failed_free_res;
+ }
+
+ /*
+ * This code is copied from Sascha Hauer's of_videomode helper
+ * and can be replaced with a call to the helper once mainlined
+ */
+ ret = 0;
+ ret |= of_property_read_u32(np, "hactive", &of_mode.xres);
+ ret |= of_property_read_u32(np, "vactive", &of_mode.yres);
+ ret |= of_property_read_u32(np, "hback-porch", &of_mode.left_margin);
+ ret |= of_property_read_u32(np, "hfront-porch", &of_mode.right_margin);
+ ret |= of_property_read_u32(np, "hsync-len", &of_mode.hsync_len);
+ ret |= of_property_read_u32(np, "vback-porch", &of_mode.upper_margin);
+ ret |= of_property_read_u32(np, "vfront-porch", &of_mode.lower_margin);
+ ret |= of_property_read_u32(np, "vsync-len", &of_mode.vsync_len);
+ ret |= of_property_read_u32(np, "bpp", &bpp);
+ if (ret) {
+ pr_err("%s: Unable to read display properties\n", __func__);
+ goto failed_free_res;
+ }
+
+ of_mode.vmode = FB_VMODE_NONINTERLACED;
+ fb_videomode_to_var(&fbi->fb.var, &of_mode);
fbi->fb.var.nonstd = 0;
fbi->fb.var.activate = FB_ACTIVATE_NOW;
fbi->fb.var.height = -1;
fbi->fb.var.width = -1;
- fbi->fb.var.xres_virtual = pdata->xres_virtual;
- fbi->fb.var.yres_virtual = pdata->yres_virtual;
- fbi->fb.var.bits_per_pixel = pdata->bpp;
- fbi->fb.fix.smem_start = pdata->video_mem_phys;
- fbi->fb.fix.smem_len = pdata->video_mem_len;
- fbi->fb.screen_base = pdata->video_mem_virt;
- fbi->fb.screen_size = pdata->video_mem_len;
+ /* try allocating the framebuffer */
+ fb_mem_len = of_mode.xres * of_mode.yres * 2 * (bpp / 8);
+ fb_mem_virt = dma_alloc_coherent(&pdev->dev, fb_mem_len, &fb_mem_phys,
+ GFP_KERNEL);
+ if (!fb_mem_virt) {
+ pr_err("%s: Failed to allocate framebuffer\n", __func__);
+ return -ENOMEM;
+ };
+
+ fbi->fb.var.xres_virtual = of_mode.xres;
+ fbi->fb.var.yres_virtual = of_mode.yres * 2;
+ fbi->fb.var.bits_per_pixel = bpp;
+
+ fbi->fb.fix.smem_start = fb_mem_phys;
+ fbi->fb.fix.smem_len = fb_mem_len;
+ fbi->fb.screen_base = fb_mem_virt;
+ fbi->fb.screen_size = fb_mem_len;
if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
dev_err(&pdev->dev, "Failed to allocate color map\n");
@@ -395,12 +455,18 @@ static int __devexit wm8505fb_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id wmt_dt_ids[] = {
+ { .compatible = "wm,wm8505-fb", },
+ {}
+};
+
static struct platform_driver wm8505fb_driver = {
.probe = wm8505fb_probe,
.remove = __devexit_p(wm8505fb_remove),
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(wmt_dt_ids),
},
};
@@ -408,4 +474,5 @@ module_platform_driver(wm8505fb_driver);
MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>");
MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, wmt_dt_ids);
diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
index 55be3865015b..ba025b4c7d09 100644
--- a/drivers/video/wmt_ge_rops.c
+++ b/drivers/video/wmt_ge_rops.c
@@ -158,12 +158,18 @@ static int __devexit wmt_ge_rops_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id wmt_dt_ids[] = {
+ { .compatible = "wm,prizm-ge-rops", },
+ { /* sentinel */ }
+};
+
static struct platform_driver wmt_ge_rops_driver = {
.probe = wmt_ge_rops_probe,
.remove = __devexit_p(wmt_ge_rops_remove),
.driver = {
.owner = THIS_MODULE,
.name = "wmt_ge_rops",
+ .of_match_table = of_match_ptr(wmt_dt_ids),
},
};
@@ -172,4 +178,5 @@ module_platform_driver(wmt_ge_rops_driver);
MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com");
MODULE_DESCRIPTION("Accelerators for raster operations using "
"WonderMedia Graphics Engine");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, wmt_dt_ids);
diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c
index e0df92ec44bd..1425d22cf956 100644
--- a/drivers/vme/bridges/vme_ca91cx42.c
+++ b/drivers/vme/bridges/vme_ca91cx42.c
@@ -1603,7 +1603,7 @@ static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int retval, i;
u32 data;
- struct list_head *pos = NULL;
+ struct list_head *pos = NULL, *n;
struct vme_bridge *ca91cx42_bridge;
struct ca91cx42_driver *ca91cx42_device;
struct vme_master_resource *master_image;
@@ -1821,28 +1821,28 @@ err_reg:
ca91cx42_crcsr_exit(ca91cx42_bridge, pdev);
err_lm:
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->lm_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->lm_resources) {
lm = list_entry(pos, struct vme_lm_resource, list);
list_del(pos);
kfree(lm);
}
err_dma:
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->dma_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->dma_resources) {
dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
list_del(pos);
kfree(dma_ctrlr);
}
err_slave:
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->slave_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->slave_resources) {
slave_image = list_entry(pos, struct vme_slave_resource, list);
list_del(pos);
kfree(slave_image);
}
err_master:
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->master_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->master_resources) {
master_image = list_entry(pos, struct vme_master_resource,
list);
list_del(pos);
@@ -1868,7 +1868,7 @@ err_struct:
static void ca91cx42_remove(struct pci_dev *pdev)
{
- struct list_head *pos = NULL;
+ struct list_head *pos = NULL, *n;
struct vme_master_resource *master_image;
struct vme_slave_resource *slave_image;
struct vme_dma_resource *dma_ctrlr;
@@ -1905,28 +1905,28 @@ static void ca91cx42_remove(struct pci_dev *pdev)
ca91cx42_crcsr_exit(ca91cx42_bridge, pdev);
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->lm_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->lm_resources) {
lm = list_entry(pos, struct vme_lm_resource, list);
list_del(pos);
kfree(lm);
}
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->dma_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->dma_resources) {
dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
list_del(pos);
kfree(dma_ctrlr);
}
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->slave_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->slave_resources) {
slave_image = list_entry(pos, struct vme_slave_resource, list);
list_del(pos);
kfree(slave_image);
}
/* resources are stored in link list */
- list_for_each(pos, &ca91cx42_bridge->master_resources) {
+ list_for_each_safe(pos, n, &ca91cx42_bridge->master_resources) {
master_image = list_entry(pos, struct vme_master_resource,
list);
list_del(pos);
diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/vme/bridges/vme_tsi148.c
index 880d9242e349..5fbd08ffb9c2 100644
--- a/drivers/vme/bridges/vme_tsi148.c
+++ b/drivers/vme/bridges/vme_tsi148.c
@@ -2350,7 +2350,7 @@ static int tsi148_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
int retval, i, master_num;
u32 data;
- struct list_head *pos = NULL;
+ struct list_head *pos = NULL, *n;
struct vme_bridge *tsi148_bridge;
struct tsi148_driver *tsi148_device;
struct vme_master_resource *master_image;
@@ -2615,28 +2615,28 @@ err_reg:
err_crcsr:
err_lm:
/* resources are stored in link list */
- list_for_each(pos, &tsi148_bridge->lm_resources) {
+ list_for_each_safe(pos, n, &tsi148_bridge->lm_resources) {
lm = list_entry(pos, struct vme_lm_resource, list);
list_del(pos);
kfree(lm);
}
err_dma:
/* resources are stored in link list */
- list_for_each(pos, &tsi148_bridge->dma_resources) {
+ list_for_each_safe(pos, n, &tsi148_bridge->dma_resources) {
dma_ctrlr = list_entry(pos, struct vme_dma_resource, list);
list_del(pos);
kfree(dma_ctrlr);
}
err_slave:
/* resources are stored in link list */
- list_for_each(pos, &tsi148_bridge->slave_resources) {
+ list_for_each_safe(pos, n, &tsi148_bridge->slave_resources) {
slave_image = list_entry(pos, struct vme_slave_resource, list);
list_del(pos);
kfree(slave_image);
}
err_master:
/* resources are stored in link list */
- list_for_each(pos, &tsi148_bridge->master_resources) {
+ list_for_each_safe(pos, n, &tsi148_bridge->master_resources) {
master_image = list_entry(pos, struct vme_master_resource,
list);
list_del(pos);
diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig
index 5ceb1cd50195..7e984034a11b 100644
--- a/drivers/w1/masters/Kconfig
+++ b/drivers/w1/masters/Kconfig
@@ -60,7 +60,6 @@ config W1_MASTER_GPIO
config HDQ_MASTER_OMAP
tristate "OMAP HDQ driver"
- depends on ARCH_OMAP2PLUS
help
Say Y here if you want support for the 1-wire or HDQ Interface
on an OMAP processor.
diff --git a/drivers/w1/masters/ds1wm.c b/drivers/w1/masters/ds1wm.c
index 530a2d309063..7c294f4dc0ed 100644
--- a/drivers/w1/masters/ds1wm.c
+++ b/drivers/w1/masters/ds1wm.c
@@ -349,7 +349,7 @@ static void ds1wm_search(void *data, struct w1_master *master_dev,
"pass: %d entering ASM\n", pass);
ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA);
dev_dbg(&ds1wm_data->pdev->dev,
- "pass: %d begining nibble loop\n", pass);
+ "pass: %d beginning nibble loop\n", pass);
r_prime = 0;
d = 0;
diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c
index fee195a76941..ca8e60bb2f9c 100644
--- a/drivers/w1/masters/omap_hdq.c
+++ b/drivers/w1/masters/omap_hdq.c
@@ -18,8 +18,6 @@
#include <linux/sched.h>
#include <linux/pm_runtime.h>
-#include <asm/irq.h>
-
#include "../w1.h"
#include "../w1_int.h"
@@ -72,11 +70,11 @@ struct hdq_data {
};
static int __devinit omap_hdq_probe(struct platform_device *pdev);
-static int omap_hdq_remove(struct platform_device *pdev);
+static int __devexit omap_hdq_remove(struct platform_device *pdev);
static struct platform_driver omap_hdq_driver = {
.probe = omap_hdq_probe,
- .remove = omap_hdq_remove,
+ .remove = __devexit_p(omap_hdq_remove),
.driver = {
.name = "omap_hdq",
},
@@ -537,39 +535,35 @@ static void omap_w1_write_byte(void *_hdq, u8 byte)
hdq_data->init_trans = 0;
mutex_unlock(&hdq_data->hdq_mutex);
}
-
- return;
}
static int __devinit omap_hdq_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct hdq_data *hdq_data;
struct resource *res;
int ret, irq;
u8 rev;
- hdq_data = kmalloc(sizeof(*hdq_data), GFP_KERNEL);
+ hdq_data = devm_kzalloc(dev, sizeof(*hdq_data), GFP_KERNEL);
if (!hdq_data) {
dev_dbg(&pdev->dev, "unable to allocate memory\n");
- ret = -ENOMEM;
- goto err_kmalloc;
+ return -ENOMEM;
}
- hdq_data->dev = &pdev->dev;
+ hdq_data->dev = dev;
platform_set_drvdata(pdev, hdq_data);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_dbg(&pdev->dev, "unable to get resource\n");
- ret = -ENXIO;
- goto err_resource;
+ return -ENXIO;
}
- hdq_data->hdq_base = ioremap(res->start, SZ_4K);
+ hdq_data->hdq_base = devm_request_and_ioremap(dev, res);
if (!hdq_data->hdq_base) {
dev_dbg(&pdev->dev, "ioremap failed\n");
- ret = -EINVAL;
- goto err_ioremap;
+ return -ENOMEM;
}
hdq_data->hdq_usecount = 0;
@@ -590,7 +584,8 @@ static int __devinit omap_hdq_probe(struct platform_device *pdev)
goto err_irq;
}
- ret = request_irq(irq, hdq_isr, IRQF_DISABLED, "omap_hdq", hdq_data);
+ ret = devm_request_irq(dev, irq, hdq_isr, IRQF_DISABLED,
+ "omap_hdq", hdq_data);
if (ret < 0) {
dev_dbg(&pdev->dev, "could not request irq\n");
goto err_irq;
@@ -615,19 +610,10 @@ err_irq:
err_w1:
pm_runtime_disable(&pdev->dev);
- iounmap(hdq_data->hdq_base);
-
-err_ioremap:
-err_resource:
- platform_set_drvdata(pdev, NULL);
- kfree(hdq_data);
-
-err_kmalloc:
return ret;
-
}
-static int omap_hdq_remove(struct platform_device *pdev)
+static int __devexit omap_hdq_remove(struct platform_device *pdev)
{
struct hdq_data *hdq_data = platform_get_drvdata(pdev);
@@ -643,27 +629,11 @@ static int omap_hdq_remove(struct platform_device *pdev)
/* remove module dependency */
pm_runtime_disable(&pdev->dev);
- free_irq(platform_get_irq(pdev, 0), hdq_data);
- platform_set_drvdata(pdev, NULL);
- iounmap(hdq_data->hdq_base);
- kfree(hdq_data);
return 0;
}
-static int __init
-omap_hdq_init(void)
-{
- return platform_driver_register(&omap_hdq_driver);
-}
-module_init(omap_hdq_init);
-
-static void __exit
-omap_hdq_exit(void)
-{
- platform_driver_unregister(&omap_hdq_driver);
-}
-module_exit(omap_hdq_exit);
+module_platform_driver(omap_hdq_driver);
module_param(w1_id, int, S_IRUSR);
MODULE_PARM_DESC(w1_id, "1-wire id for the slave detection");
diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c
index df600d14974d..6012c4ea3206 100644
--- a/drivers/w1/masters/w1-gpio.c
+++ b/drivers/w1/masters/w1-gpio.c
@@ -14,6 +14,8 @@
#include <linux/slab.h>
#include <linux/w1-gpio.h>
#include <linux/gpio.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
#include "../w1.h"
#include "../w1_int.h"
@@ -42,12 +44,55 @@ static u8 w1_gpio_read_bit(void *data)
return gpio_get_value(pdata->pin) ? 1 : 0;
}
+#ifdef CONFIG_OF
+static struct of_device_id w1_gpio_dt_ids[] = {
+ { .compatible = "w1-gpio" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, w1_gpio_dt_ids);
+
+static int w1_gpio_probe_dt(struct platform_device *pdev)
+{
+ struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(w1_gpio_dt_ids, &pdev->dev);
+
+ if (!of_id)
+ return 0;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (of_get_property(np, "linux,open-drain", NULL))
+ pdata->is_open_drain = 1;
+
+ pdata->pin = of_get_gpio(np, 0);
+ pdata->ext_pullup_enable_pin = of_get_gpio(np, 1);
+ pdev->dev.platform_data = pdata;
+
+ return 0;
+}
+#else
+static int w1_gpio_probe_dt(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
static int __init w1_gpio_probe(struct platform_device *pdev)
{
struct w1_bus_master *master;
- struct w1_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct w1_gpio_platform_data *pdata;
int err;
+ err = w1_gpio_probe_dt(pdev);
+ if (err < 0)
+ return err;
+
+ pdata = pdev->dev.platform_data;
+
if (!pdata)
return -ENXIO;
@@ -59,6 +104,13 @@ static int __init w1_gpio_probe(struct platform_device *pdev)
if (err)
goto free_master;
+ if (gpio_is_valid(pdata->ext_pullup_enable_pin)) {
+ err = gpio_request_one(pdata->ext_pullup_enable_pin,
+ GPIOF_INIT_LOW, "w1 pullup");
+ if (err < 0)
+ goto free_gpio;
+ }
+
master->data = pdata;
master->read_bit = w1_gpio_read_bit;
@@ -72,15 +124,21 @@ static int __init w1_gpio_probe(struct platform_device *pdev)
err = w1_add_master_device(master);
if (err)
- goto free_gpio;
+ goto free_gpio_ext_pu;
if (pdata->enable_external_pullup)
pdata->enable_external_pullup(1);
+ if (gpio_is_valid(pdata->ext_pullup_enable_pin))
+ gpio_set_value(pdata->ext_pullup_enable_pin, 1);
+
platform_set_drvdata(pdev, master);
return 0;
+ free_gpio_ext_pu:
+ if (gpio_is_valid(pdata->ext_pullup_enable_pin))
+ gpio_free(pdata->ext_pullup_enable_pin);
free_gpio:
gpio_free(pdata->pin);
free_master:
@@ -97,6 +155,9 @@ static int __exit w1_gpio_remove(struct platform_device *pdev)
if (pdata->enable_external_pullup)
pdata->enable_external_pullup(0);
+ if (gpio_is_valid(pdata->ext_pullup_enable_pin))
+ gpio_set_value(pdata->ext_pullup_enable_pin, 0);
+
w1_remove_master_device(master);
gpio_free(pdata->pin);
kfree(master);
@@ -135,6 +196,7 @@ static struct platform_driver w1_gpio_driver = {
.driver = {
.name = "w1-gpio",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(w1_gpio_dt_ids),
},
.remove = __exit_p(w1_gpio_remove),
.suspend = w1_gpio_suspend,
diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c
index 1eff743ec497..ae60406ea8a1 100644
--- a/drivers/watchdog/hpwdt.c
+++ b/drivers/watchdog/hpwdt.c
@@ -814,6 +814,9 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev,
hpwdt_timer_reg = pci_mem_addr + 0x70;
hpwdt_timer_con = pci_mem_addr + 0x72;
+ /* Make sure that timer is disabled until /dev/watchdog is opened */
+ hpwdt_stop();
+
/* Make sure that we have a valid soft_margin */
if (hpwdt_change_timer(soft_margin))
hpwdt_change_timer(DEFAULT_MARGIN);
diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c
index 40f7bf1f8654..e6a038ae8dc2 100644
--- a/drivers/watchdog/mpc8xxx_wdt.c
+++ b/drivers/watchdog/mpc8xxx_wdt.c
@@ -193,7 +193,7 @@ static int __devinit mpc8xxx_wdt_probe(struct platform_device *ofdev)
int ret;
const struct of_device_id *match;
struct device_node *np = ofdev->dev.of_node;
- struct mpc8xxx_wdt_type *wdt_type;
+ const struct mpc8xxx_wdt_type *wdt_type;
u32 freq = fsl_get_sys_freq();
bool enabled;
diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c
index 6aa46a90ff02..3796434991fa 100644
--- a/drivers/watchdog/watchdog_core.c
+++ b/drivers/watchdog/watchdog_core.c
@@ -128,11 +128,12 @@ EXPORT_SYMBOL_GPL(watchdog_register_device);
void watchdog_unregister_device(struct watchdog_device *wdd)
{
int ret;
- int devno = wdd->cdev.dev;
+ int devno;
if (wdd == NULL)
return;
+ devno = wdd->cdev.dev;
ret = watchdog_dev_unregister(wdd);
if (ret)
pr_err("error unregistering /dev/watchdog (err=%d)\n", ret);
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index d80bea5535a2..a4a3cab2f459 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -18,7 +18,7 @@ obj-$(CONFIG_XEN_PVHVM) += platform-pci.o
obj-$(CONFIG_XEN_TMEM) += tmem.o
obj-$(CONFIG_SWIOTLB_XEN) += swiotlb-xen.o
obj-$(CONFIG_XEN_DOM0) += pcpu.o
-obj-$(CONFIG_XEN_DOM0) += pci.o acpi.o
+obj-$(CONFIG_XEN_DOM0) += pci.o dbgp.o acpi.o
obj-$(CONFIG_XEN_MCE_LOG) += mcelog.o
obj-$(CONFIG_XEN_PCIDEV_BACKEND) += xen-pciback/
obj-$(CONFIG_XEN_PRIVCMD) += xen-privcmd.o
diff --git a/drivers/xen/dbgp.c b/drivers/xen/dbgp.c
new file mode 100644
index 000000000000..42569c77ccc8
--- /dev/null
+++ b/drivers/xen/dbgp.c
@@ -0,0 +1,48 @@
+#include <linux/pci.h>
+#include <linux/usb.h>
+#include <linux/usb/ehci_def.h>
+#include <linux/usb/hcd.h>
+#include <asm/xen/hypercall.h>
+#include <xen/interface/physdev.h>
+#include <xen/xen.h>
+
+static int xen_dbgp_op(struct usb_hcd *hcd, int op)
+{
+ const struct device *ctrlr = hcd_to_bus(hcd)->controller;
+ struct physdev_dbgp_op dbgp;
+
+ if (!xen_initial_domain())
+ return 0;
+
+ dbgp.op = op;
+
+#ifdef CONFIG_PCI
+ if (ctrlr->bus == &pci_bus_type) {
+ const struct pci_dev *pdev = to_pci_dev(ctrlr);
+
+ dbgp.u.pci.seg = pci_domain_nr(pdev->bus);
+ dbgp.u.pci.bus = pdev->bus->number;
+ dbgp.u.pci.devfn = pdev->devfn;
+ dbgp.bus = PHYSDEVOP_DBGP_BUS_PCI;
+ } else
+#endif
+ dbgp.bus = PHYSDEVOP_DBGP_BUS_UNKNOWN;
+
+ return HYPERVISOR_physdev_op(PHYSDEVOP_dbgp_op, &dbgp);
+}
+
+int xen_dbgp_reset_prep(struct usb_hcd *hcd)
+{
+ return xen_dbgp_op(hcd, PHYSDEVOP_DBGP_RESET_PREPARE);
+}
+
+int xen_dbgp_external_startup(struct usb_hcd *hcd)
+{
+ return xen_dbgp_op(hcd, PHYSDEVOP_DBGP_RESET_DONE);
+}
+
+#ifndef CONFIG_EARLY_PRINTK_DBGP
+#include <linux/export.h>
+EXPORT_SYMBOL_GPL(xen_dbgp_reset_prep);
+EXPORT_SYMBOL_GPL(xen_dbgp_external_startup);
+#endif
diff --git a/drivers/xen/gntdev.c b/drivers/xen/gntdev.c
index 1ffd03bf8e10..7f1241608489 100644
--- a/drivers/xen/gntdev.c
+++ b/drivers/xen/gntdev.c
@@ -314,8 +314,9 @@ static int __unmap_grant_pages(struct grant_map *map, int offset, int pages)
}
}
- err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages + offset,
- pages, true);
+ err = gnttab_unmap_refs(map->unmap_ops + offset,
+ use_ptemod ? map->kmap_ops + offset : NULL, map->pages + offset,
+ pages);
if (err)
return err;
diff --git a/drivers/xen/grant-table.c b/drivers/xen/grant-table.c
index 0bfc1ef11259..006726688baf 100644
--- a/drivers/xen/grant-table.c
+++ b/drivers/xen/grant-table.c
@@ -870,7 +870,8 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
EXPORT_SYMBOL_GPL(gnttab_map_refs);
int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
- struct page **pages, unsigned int count, bool clear_pte)
+ struct gnttab_map_grant_ref *kmap_ops,
+ struct page **pages, unsigned int count)
{
int i, ret;
bool lazy = false;
@@ -888,7 +889,8 @@ int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
}
for (i = 0; i < count; i++) {
- ret = m2p_remove_override(pages[i], clear_pte);
+ ret = m2p_remove_override(pages[i], kmap_ops ?
+ &kmap_ops[i] : NULL);
if (ret)
return ret;
}
diff --git a/drivers/xen/xen-pciback/pci_stub.c b/drivers/xen/xen-pciback/pci_stub.c
index 03342728bf23..92ff01dbeb10 100644
--- a/drivers/xen/xen-pciback/pci_stub.c
+++ b/drivers/xen/xen-pciback/pci_stub.c
@@ -871,7 +871,7 @@ end:
}
/*add xen_pcibk AER handling*/
-static struct pci_error_handlers xen_pcibk_error_handler = {
+static const struct pci_error_handlers xen_pcibk_error_handler = {
.error_detected = xen_pcibk_error_detected,
.mmio_enabled = xen_pcibk_mmio_enabled,
.slot_reset = xen_pcibk_slot_reset,
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index 0d195b507660..9821b672f5a2 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -116,7 +116,7 @@ struct btrfs_ordered_sum;
#define BTRFS_FREE_SPACE_OBJECTID -11ULL
/*
- * The inode number assigned to the special inode for sotring
+ * The inode number assigned to the special inode for storing
* free ino cache
*/
#define BTRFS_FREE_INO_OBJECTID -12ULL
diff --git a/fs/btrfs/delayed-ref.h b/fs/btrfs/delayed-ref.h
index ab5300595847..c9d703693df0 100644
--- a/fs/btrfs/delayed-ref.h
+++ b/fs/btrfs/delayed-ref.h
@@ -18,7 +18,7 @@
#ifndef __DELAYED_REF__
#define __DELAYED_REF__
-/* these are the possible values of struct btrfs_delayed_ref->action */
+/* these are the possible values of struct btrfs_delayed_ref_node->action */
#define BTRFS_ADD_DELAYED_REF 1 /* add one backref to the tree */
#define BTRFS_DROP_DELAYED_REF 2 /* delete one backref from the tree */
#define BTRFS_ADD_DELAYED_EXTENT 3 /* record a full extent allocation */
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index ec154f954646..316b07a866d2 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -1971,8 +1971,8 @@ out:
ordered_extent->len - 1, NULL, GFP_NOFS);
/*
- * This needs to be dont to make sure anybody waiting knows we are done
- * upating everything for this ordered extent.
+ * This needs to be done to make sure anybody waiting knows we are done
+ * updating everything for this ordered extent.
*/
btrfs_remove_ordered_extent(inode, ordered_extent);
diff --git a/fs/cifs/Kconfig b/fs/cifs/Kconfig
index a08306a8bec9..2075ddfffa73 100644
--- a/fs/cifs/Kconfig
+++ b/fs/cifs/Kconfig
@@ -9,13 +9,14 @@ config CIFS
select CRYPTO_ARC4
select CRYPTO_ECB
select CRYPTO_DES
+ select CRYPTO_SHA256
help
This is the client VFS module for the Common Internet File System
(CIFS) protocol which is the successor to the Server Message Block
(SMB) protocol, the native file sharing mechanism for most early
PC operating systems. The CIFS protocol is fully supported by
- file servers such as Windows 2000 (including Windows 2003, NT 4
- and Windows XP) as well by Samba (which provides excellent CIFS
+ file servers such as Windows 2000 (including Windows 2003, Windows 2008,
+ NT 4 and Windows XP) as well by Samba (which provides excellent CIFS
server support for Linux and many other operating systems). Limited
support for OS/2 and Windows ME and similar servers is provided as
well.
@@ -114,6 +115,13 @@ config CIFS_POSIX
(such as Samba 3.10 and later) which can negotiate
CIFS POSIX ACL support. If unsure, say N.
+config CIFS_ACL
+ bool "Provide CIFS ACL support"
+ depends on CIFS_XATTR && KEYS
+ help
+ Allows fetching CIFS/NTFS ACL from the server. The DACL blob
+ is handed over to the application/caller.
+
config CIFS_DEBUG2
bool "Enable additional CIFS debugging routines"
depends on CIFS
@@ -138,21 +146,6 @@ config CIFS_DFS_UPCALL
IP addresses) which is needed for implicit mounts of DFS junction
points. If unsure, say N.
-config CIFS_FSCACHE
- bool "Provide CIFS client caching support"
- depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y
- help
- Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
- to be cached locally on disk through the general filesystem cache
- manager. If unsure, say N.
-
-config CIFS_ACL
- bool "Provide CIFS ACL support"
- depends on CIFS_XATTR && KEYS
- help
- Allows to fetch CIFS/NTFS ACL from the server. The DACL blob
- is handed over to the application/caller.
-
config CIFS_NFSD_EXPORT
bool "Allow nfsd to export CIFS file system (EXPERIMENTAL)"
depends on CIFS && EXPERIMENTAL && BROKEN
@@ -161,7 +154,7 @@ config CIFS_NFSD_EXPORT
config CIFS_SMB2
bool "SMB2 network file system support (EXPERIMENTAL)"
- depends on EXPERIMENTAL && INET && BROKEN
+ depends on CIFS && EXPERIMENTAL && INET
select NLS
select KEYS
select FSCACHE
@@ -178,3 +171,12 @@ config CIFS_SMB2
(compared to cifs) due to protocol improvements.
Unless you are a developer or tester, say N.
+
+config CIFS_FSCACHE
+ bool "Provide CIFS client caching support"
+ depends on CIFS=m && FSCACHE || CIFS=y && FSCACHE=y
+ help
+ Makes CIFS FS-Cache capable. Say Y here if you want your CIFS data
+ to be cached locally on disk through the general filesystem cache
+ manager. If unsure, say N.
+
diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index feee94309271..aa0d68b086eb 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -17,4 +17,4 @@ cifs-$(CONFIG_CIFS_DFS_UPCALL) += dns_resolve.o cifs_dfs_ref.o
cifs-$(CONFIG_CIFS_FSCACHE) += fscache.o cache.o
cifs-$(CONFIG_CIFS_SMB2) += smb2ops.o smb2maperror.o smb2transport.o \
- smb2misc.o smb2pdu.o smb2inode.o
+ smb2misc.o smb2pdu.o smb2inode.o smb2file.o
diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c
index 7dab9c04ad52..53cf2aabce87 100644
--- a/fs/cifs/cifs_unicode.c
+++ b/fs/cifs/cifs_unicode.c
@@ -328,7 +328,7 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
}
ctoUTF16_out:
- return i;
+ return j;
}
#ifdef CONFIG_CIFS_SMB2
diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c
index 05f4dc263a23..2ee5c54797fa 100644
--- a/fs/cifs/cifsacl.c
+++ b/fs/cifs/cifsacl.c
@@ -1222,7 +1222,7 @@ struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
if (!open_file)
return get_cifs_acl_by_path(cifs_sb, path, pacllen);
- pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->netfid, pacllen);
+ pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->fid.netfid, pacllen);
cifsFileInfo_put(open_file);
return pntsd;
}
diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c
index 6a0d741159f0..652f5051be09 100644
--- a/fs/cifs/cifsencrypt.c
+++ b/fs/cifs/cifsencrypt.c
@@ -29,6 +29,7 @@
#include "ntlmssp.h"
#include <linux/ctype.h>
#include <linux/random.h>
+#include <linux/highmem.h>
/*
* Calculate and return the CIFS signature based on the mac key and SMB PDU.
@@ -37,11 +38,13 @@
* the sequence number before this function is called. Also, this function
* should be called with the server->srv_mutex held.
*/
-static int cifs_calc_signature(const struct kvec *iov, int n_vec,
+static int cifs_calc_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server, char *signature)
{
int i;
int rc;
+ struct kvec *iov = rqst->rq_iov;
+ int n_vec = rqst->rq_nvec;
if (iov == NULL || signature == NULL || server == NULL)
return -EINVAL;
@@ -91,6 +94,16 @@ static int cifs_calc_signature(const struct kvec *iov, int n_vec,
}
}
+ /* now hash over the rq_pages array */
+ for (i = 0; i < rqst->rq_npages; i++) {
+ struct kvec p_iov;
+
+ cifs_rqst_page_to_kvec(rqst, i, &p_iov);
+ crypto_shash_update(&server->secmech.sdescmd5->shash,
+ p_iov.iov_base, p_iov.iov_len);
+ kunmap(rqst->rq_pages[i]);
+ }
+
rc = crypto_shash_final(&server->secmech.sdescmd5->shash, signature);
if (rc)
cERROR(1, "%s: Could not generate md5 hash", __func__);
@@ -99,12 +112,12 @@ static int cifs_calc_signature(const struct kvec *iov, int n_vec,
}
/* must be called with server->srv_mutex held */
-int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
+int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
__u32 *pexpected_response_sequence_number)
{
int rc = 0;
char smb_signature[20];
- struct smb_hdr *cifs_pdu = (struct smb_hdr *)iov[0].iov_base;
+ struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
if ((cifs_pdu == NULL) || (server == NULL))
return -EINVAL;
@@ -125,7 +138,7 @@ int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
*pexpected_response_sequence_number = server->sequence_number++;
server->sequence_number++;
- rc = cifs_calc_signature(iov, n_vec, server, smb_signature);
+ rc = cifs_calc_signature(rqst, server, smb_signature);
if (rc)
memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
else
@@ -134,6 +147,15 @@ int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
return rc;
}
+int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
+ __u32 *pexpected_response_sequence)
+{
+ struct smb_rqst rqst = { .rq_iov = iov,
+ .rq_nvec = n_vec };
+
+ return cifs_sign_rqst(&rqst, server, pexpected_response_sequence);
+}
+
/* must be called with server->srv_mutex held */
int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
__u32 *pexpected_response_sequence_number)
@@ -147,14 +169,14 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
pexpected_response_sequence_number);
}
-int cifs_verify_signature(struct kvec *iov, unsigned int nr_iov,
+int cifs_verify_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server,
__u32 expected_sequence_number)
{
unsigned int rc;
char server_response_sig[8];
char what_we_think_sig_should_be[20];
- struct smb_hdr *cifs_pdu = (struct smb_hdr *)iov[0].iov_base;
+ struct smb_hdr *cifs_pdu = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
if (cifs_pdu == NULL || server == NULL)
return -EINVAL;
@@ -186,8 +208,7 @@ int cifs_verify_signature(struct kvec *iov, unsigned int nr_iov,
cifs_pdu->Signature.Sequence.Reserved = 0;
mutex_lock(&server->srv_mutex);
- rc = cifs_calc_signature(iov, nr_iov, server,
- what_we_think_sig_should_be);
+ rc = cifs_calc_signature(rqst, server, what_we_think_sig_should_be);
mutex_unlock(&server->srv_mutex);
if (rc)
@@ -686,12 +707,17 @@ calc_seckey(struct cifs_ses *ses)
void
cifs_crypto_shash_release(struct TCP_Server_Info *server)
{
+ if (server->secmech.hmacsha256)
+ crypto_free_shash(server->secmech.hmacsha256);
+
if (server->secmech.md5)
crypto_free_shash(server->secmech.md5);
if (server->secmech.hmacmd5)
crypto_free_shash(server->secmech.hmacmd5);
+ kfree(server->secmech.sdeschmacsha256);
+
kfree(server->secmech.sdeschmacmd5);
kfree(server->secmech.sdescmd5);
@@ -716,6 +742,13 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
goto crypto_allocate_md5_fail;
}
+ server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0);
+ if (IS_ERR(server->secmech.hmacsha256)) {
+ cERROR(1, "could not allocate crypto hmacsha256\n");
+ rc = PTR_ERR(server->secmech.hmacsha256);
+ goto crypto_allocate_hmacsha256_fail;
+ }
+
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.hmacmd5);
server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
@@ -727,7 +760,6 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
server->secmech.sdeschmacmd5->shash.flags = 0x0;
-
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.md5);
server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
@@ -739,12 +771,29 @@ cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
server->secmech.sdescmd5->shash.flags = 0x0;
+ size = sizeof(struct shash_desc) +
+ crypto_shash_descsize(server->secmech.hmacsha256);
+ server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL);
+ if (!server->secmech.sdeschmacsha256) {
+ cERROR(1, "%s: Can't alloc hmacsha256\n", __func__);
+ rc = -ENOMEM;
+ goto crypto_allocate_hmacsha256_sdesc_fail;
+ }
+ server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
+ server->secmech.sdeschmacsha256->shash.flags = 0x0;
+
return 0;
+crypto_allocate_hmacsha256_sdesc_fail:
+ kfree(server->secmech.sdescmd5);
+
crypto_allocate_md5_sdesc_fail:
kfree(server->secmech.sdeschmacmd5);
crypto_allocate_hmacmd5_sdesc_fail:
+ crypto_free_shash(server->secmech.hmacsha256);
+
+crypto_allocate_hmacsha256_fail:
crypto_free_shash(server->secmech.md5);
crypto_allocate_md5_fail:
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index db8a404a51dd..a41044a31083 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -36,6 +36,7 @@
#include <linux/kthread.h>
#include <linux/freezer.h>
#include <linux/namei.h>
+#include <linux/random.h>
#include <net/ipv6.h>
#include "cifsfs.h"
#include "cifspdu.h"
@@ -51,7 +52,6 @@
#ifdef CONFIG_CIFS_SMB2
#include "smb2pdu.h"
#endif
-#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
int cifsFYI = 0;
int cifsERROR = 1;
@@ -89,6 +89,10 @@ extern mempool_t *cifs_mid_poolp;
struct workqueue_struct *cifsiod_wq;
+#ifdef CONFIG_CIFS_SMB2
+__u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
+#endif
+
static int
cifs_read_super(struct super_block *sb)
{
@@ -160,13 +164,12 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
struct super_block *sb = dentry->d_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
- int rc = -EOPNOTSUPP;
+ struct TCP_Server_Info *server = tcon->ses->server;
unsigned int xid;
+ int rc = 0;
xid = get_xid();
- buf->f_type = CIFS_MAGIC_NUMBER;
-
/*
* PATH_MAX may be too long - it would presumably be total path,
* but note that some servers (includinng Samba 3) have a shorter
@@ -178,27 +181,8 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_files = 0; /* undefined */
buf->f_ffree = 0; /* unlimited */
- /*
- * We could add a second check for a QFS Unix capability bit
- */
- if ((tcon->ses->capabilities & CAP_UNIX) &&
- (CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability)))
- rc = CIFSSMBQFSPosixInfo(xid, tcon, buf);
-
- /*
- * Only need to call the old QFSInfo if failed on newer one,
- * e.g. by OS/2.
- **/
- if (rc && (tcon->ses->capabilities & CAP_NT_SMBS))
- rc = CIFSSMBQFSInfo(xid, tcon, buf);
-
- /*
- * Some old Windows servers also do not support level 103, retry with
- * older level one if old server failed the previous call or we
- * bypassed it because we detected that this was an older LANMAN sess
- */
- if (rc)
- rc = SMBOldQFSInfo(xid, tcon, buf);
+ if (server->ops->queryfs)
+ rc = server->ops->queryfs(xid, tcon, buf);
free_xid(xid);
return 0;
@@ -239,9 +223,10 @@ cifs_alloc_inode(struct super_block *sb)
return NULL;
cifs_inode->cifsAttrs = 0x20; /* default */
cifs_inode->time = 0;
- /* Until the file is open and we have gotten oplock
- info back from the server, can not assume caching of
- file data or metadata */
+ /*
+ * Until the file is open and we have gotten oplock info back from the
+ * server, can not assume caching of file data or metadata.
+ */
cifs_set_oplock_level(cifs_inode, 0);
cifs_inode->delete_pending = false;
cifs_inode->invalid_mapping = false;
@@ -249,11 +234,16 @@ cifs_alloc_inode(struct super_block *sb)
cifs_inode->server_eof = 0;
cifs_inode->uniqueid = 0;
cifs_inode->createtime = 0;
-
- /* Can not set i_flags here - they get immediately overwritten
- to zero by the VFS */
-/* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME;*/
+#ifdef CONFIG_CIFS_SMB2
+ get_random_bytes(cifs_inode->lease_key, SMB2_LEASE_KEY_SIZE);
+#endif
+ /*
+ * Can not set i_flags here - they get immediately overwritten to zero
+ * by the VFS.
+ */
+ /* cifs_inode->vfs_inode.i_flags = S_NOATIME | S_NOCMTIME; */
INIT_LIST_HEAD(&cifs_inode->openFileList);
+ INIT_LIST_HEAD(&cifs_inode->llist);
return &cifs_inode->vfs_inode;
}
@@ -360,7 +350,8 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
cifs_show_security(s, tcon->ses->server);
cifs_show_cache_flavor(s, cifs_sb);
- seq_printf(s, ",unc=%s", tcon->treeName);
+ seq_printf(s, ",unc=");
+ seq_escape(s, tcon->treeName, " \t\n\\");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)
seq_printf(s, ",multiuser");
@@ -957,7 +948,7 @@ cifs_init_once(void *inode)
struct cifsInodeInfo *cifsi = inode;
inode_init_once(&cifsi->vfs_inode);
- mutex_init(&cifsi->lock_mutex);
+ init_rwsem(&cifsi->lock_sem);
}
static int
@@ -1127,6 +1118,10 @@ init_cifs(void)
spin_lock_init(&cifs_file_list_lock);
spin_lock_init(&GlobalMid_Lock);
+#ifdef CONFIG_CIFS_SMB2
+ get_random_bytes(cifs_client_guid, SMB2_CLIENT_GUID_SIZE);
+#endif
+
if (cifs_max_pending < 2) {
cifs_max_pending = 2;
cFYI(1, "cifs_max_pending set to min of 2");
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index 1c49c5a9b27a..7163419cecd9 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -128,5 +128,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops;
#endif /* CONFIG_CIFS_NFSD_EXPORT */
-#define CIFS_VERSION "1.78"
+#define CIFS_VERSION "2.0"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 977dc0e85ccb..f5af2527fc69 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -32,6 +32,8 @@
#include "smb2pdu.h"
#endif
+#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
+
/*
* The sizes of various internal tables and strings
*/
@@ -128,8 +130,10 @@ struct sdesc {
struct cifs_secmech {
struct crypto_shash *hmacmd5; /* hmac-md5 hash function */
struct crypto_shash *md5; /* md5 hash function */
+ struct crypto_shash *hmacsha256; /* hmac-sha256 hash function */
struct sdesc *sdeschmacmd5; /* ctxt to generate ntlmv2 hash, CR1 */
struct sdesc *sdescmd5; /* ctxt to generate cifs/smb signature */
+ struct sdesc *sdeschmacsha256; /* ctxt to generate smb2 signature */
};
/* per smb session structure/fields */
@@ -158,9 +162,24 @@ struct cifs_cred {
*****************************************************************
*/
+/*
+ * A smb_rqst represents a complete request to be issued to a server. It's
+ * formed by a kvec array, followed by an array of pages. Page data is assumed
+ * to start at the beginning of the first page.
+ */
+struct smb_rqst {
+ struct kvec *rq_iov; /* array of kvecs */
+ unsigned int rq_nvec; /* number of kvecs in array */
+ struct page **rq_pages; /* pointer to array of page ptrs */
+ unsigned int rq_npages; /* number pages in array */
+ unsigned int rq_pagesz; /* page size to use */
+ unsigned int rq_tailsz; /* length of last page */
+};
+
enum smb_version {
Smb_1 = 1,
Smb_21,
+ Smb_30,
};
struct mid_q_entry;
@@ -171,17 +190,23 @@ struct cifs_tcon;
struct dfs_info3_param;
struct cifs_fattr;
struct smb_vol;
+struct cifs_fid;
+struct cifs_readdata;
+struct cifs_writedata;
+struct cifs_io_parms;
+struct cifs_search_info;
+struct cifsInodeInfo;
struct smb_version_operations {
int (*send_cancel)(struct TCP_Server_Info *, void *,
struct mid_q_entry *);
bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
/* setup request: allocate mid, sign message */
- int (*setup_request)(struct cifs_ses *, struct kvec *, unsigned int,
- struct mid_q_entry **);
+ struct mid_q_entry *(*setup_request)(struct cifs_ses *,
+ struct smb_rqst *);
/* setup async request: allocate mid, sign message */
- int (*setup_async_request)(struct TCP_Server_Info *, struct kvec *,
- unsigned int, struct mid_q_entry **);
+ struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *,
+ struct smb_rqst *);
/* check response: verify signature, map error */
int (*check_receive)(struct mid_q_entry *, struct TCP_Server_Info *,
bool);
@@ -212,6 +237,10 @@ struct smb_version_operations {
bool (*need_neg)(struct TCP_Server_Info *);
/* negotiate to the server */
int (*negotiate)(const unsigned int, struct cifs_ses *);
+ /* set negotiated write size */
+ unsigned int (*negotiate_wsize)(struct cifs_tcon *, struct smb_vol *);
+ /* set negotiated read size */
+ unsigned int (*negotiate_rsize)(struct cifs_tcon *, struct smb_vol *);
/* setup smb sessionn */
int (*sess_setup)(const unsigned int, struct cifs_ses *,
const struct nls_table *);
@@ -235,10 +264,22 @@ struct smb_version_operations {
int (*query_path_info)(const unsigned int, struct cifs_tcon *,
struct cifs_sb_info *, const char *,
FILE_ALL_INFO *, bool *);
+ /* query file data from the server */
+ int (*query_file_info)(const unsigned int, struct cifs_tcon *,
+ struct cifs_fid *, FILE_ALL_INFO *);
/* get server index number */
int (*get_srv_inum)(const unsigned int, struct cifs_tcon *,
struct cifs_sb_info *, const char *,
u64 *uniqueid, FILE_ALL_INFO *);
+ /* set size by path */
+ int (*set_path_size)(const unsigned int, struct cifs_tcon *,
+ const char *, __u64, struct cifs_sb_info *, bool);
+ /* set size by file handle */
+ int (*set_file_size)(const unsigned int, struct cifs_tcon *,
+ struct cifsFileInfo *, __u64, bool);
+ /* set attributes */
+ int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *,
+ const unsigned int);
/* build a full path to the root of the mount */
char * (*build_path_to_root)(struct smb_vol *, struct cifs_sb_info *,
struct cifs_tcon *);
@@ -256,10 +297,84 @@ struct smb_version_operations {
/* remove directory */
int (*rmdir)(const unsigned int, struct cifs_tcon *, const char *,
struct cifs_sb_info *);
+ /* unlink file */
+ int (*unlink)(const unsigned int, struct cifs_tcon *, const char *,
+ struct cifs_sb_info *);
+ /* open, rename and delete file */
+ int (*rename_pending_delete)(const char *, struct dentry *,
+ const unsigned int);
+ /* send rename request */
+ int (*rename)(const unsigned int, struct cifs_tcon *, const char *,
+ const char *, struct cifs_sb_info *);
+ /* send create hardlink request */
+ int (*create_hardlink)(const unsigned int, struct cifs_tcon *,
+ const char *, const char *,
+ struct cifs_sb_info *);
+ /* open a file for non-posix mounts */
+ int (*open)(const unsigned int, struct cifs_tcon *, const char *, int,
+ int, int, struct cifs_fid *, __u32 *, FILE_ALL_INFO *,
+ struct cifs_sb_info *);
+ /* set fid protocol-specific info */
+ void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
+ /* close a file */
+ void (*close)(const unsigned int, struct cifs_tcon *,
+ struct cifs_fid *);
+ /* send a flush request to the server */
+ int (*flush)(const unsigned int, struct cifs_tcon *, struct cifs_fid *);
+ /* async read from the server */
+ int (*async_readv)(struct cifs_readdata *);
+ /* async write to the server */
+ int (*async_writev)(struct cifs_writedata *);
+ /* sync read from the server */
+ int (*sync_read)(const unsigned int, struct cifsFileInfo *,
+ struct cifs_io_parms *, unsigned int *, char **,
+ int *);
+ /* sync write to the server */
+ int (*sync_write)(const unsigned int, struct cifsFileInfo *,
+ struct cifs_io_parms *, unsigned int *, struct kvec *,
+ unsigned long);
+ /* open dir, start readdir */
+ int (*query_dir_first)(const unsigned int, struct cifs_tcon *,
+ const char *, struct cifs_sb_info *,
+ struct cifs_fid *, __u16,
+ struct cifs_search_info *);
+ /* continue readdir */
+ int (*query_dir_next)(const unsigned int, struct cifs_tcon *,
+ struct cifs_fid *,
+ __u16, struct cifs_search_info *srch_inf);
+ /* close dir */
+ int (*close_dir)(const unsigned int, struct cifs_tcon *,
+ struct cifs_fid *);
+ /* calculate a size of SMB message */
+ unsigned int (*calc_smb_size)(void *);
+ /* check for STATUS_PENDING and process it in a positive case */
+ bool (*is_status_pending)(char *, struct TCP_Server_Info *, int);
+ /* send oplock break response */
+ int (*oplock_response)(struct cifs_tcon *, struct cifs_fid *,
+ struct cifsInodeInfo *);
+ /* query remote filesystem */
+ int (*queryfs)(const unsigned int, struct cifs_tcon *,
+ struct kstatfs *);
+ /* send mandatory brlock to the server */
+ int (*mand_lock)(const unsigned int, struct cifsFileInfo *, __u64,
+ __u64, __u32, int, int, bool);
+ /* unlock range of mandatory locks */
+ int (*mand_unlock_range)(struct cifsFileInfo *, struct file_lock *,
+ const unsigned int);
+ /* push brlocks from the cache to the server */
+ int (*push_mand_locks)(struct cifsFileInfo *);
+ /* get lease key of the inode */
+ void (*get_lease_key)(struct inode *, struct cifs_fid *fid);
+ /* set lease key of the inode */
+ void (*set_lease_key)(struct inode *, struct cifs_fid *fid);
+ /* generate new lease key */
+ void (*new_lease_key)(struct cifs_fid *fid);
};
struct smb_version_values {
char *version_string;
+ __u16 protocol_id;
+ __u32 req_capabilities;
__u32 large_lock_type;
__u32 exclusive_lock_type;
__u32 shared_lock_type;
@@ -496,6 +611,51 @@ get_next_mid(struct TCP_Server_Info *server)
}
/*
+ * When the server supports very large reads and writes via POSIX extensions,
+ * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not
+ * including the RFC1001 length.
+ *
+ * Note that this might make for "interesting" allocation problems during
+ * writeback however as we have to allocate an array of pointers for the
+ * pages. A 16M write means ~32kb page array with PAGE_CACHE_SIZE == 4096.
+ *
+ * For reads, there is a similar problem as we need to allocate an array
+ * of kvecs to handle the receive, though that should only need to be done
+ * once.
+ */
+#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4)
+#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4)
+
+/*
+ * When the server doesn't allow large posix writes, only allow a rsize/wsize
+ * of 2^17-1 minus the size of the call header. That allows for a read or
+ * write up to the maximum size described by RFC1002.
+ */
+#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4)
+#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4)
+
+/*
+ * The default wsize is 1M. find_get_pages seems to return a maximum of 256
+ * pages in a single call. With PAGE_CACHE_SIZE == 4k, this means we can fill
+ * a single wsize request with a single call.
+ */
+#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
+
+/*
+ * Windows only supports a max of 60kb reads and 65535 byte writes. Default to
+ * those values when posix extensions aren't in force. In actuality here, we
+ * use 65536 to allow for a write that is a multiple of 4k. Most servers seem
+ * to be ok with the extra byte even though Windows doesn't send writes that
+ * are that large.
+ *
+ * Citation:
+ *
+ * http://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx
+ */
+#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024)
+#define CIFS_DEFAULT_NON_POSIX_WSIZE (65536)
+
+/*
* Macros to allow the TCP_Server_Info->net field and related code to drop out
* when CONFIG_NET_NS isn't set.
*/
@@ -559,6 +719,7 @@ struct cifs_ses {
__u16 session_flags;
#endif /* CONFIG_CIFS_SMB2 */
};
+
/* no more than one of the following three session flags may be set */
#define CIFS_SES_NT4 1
#define CIFS_SES_OS2 2
@@ -665,6 +826,7 @@ struct cifs_tcon {
u64 resource_id; /* server resource id */
struct fscache_cookie *fscache; /* cookie for share */
#endif
+ struct list_head pending_opens; /* list of incomplete opens */
/* BB add field for back pointer to sb struct(s)? */
};
@@ -707,6 +869,15 @@ cifs_get_tlink(struct tcon_link *tlink)
/* This function is always expected to succeed */
extern struct cifs_tcon *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb);
+#define CIFS_OPLOCK_NO_CHANGE 0xfe
+
+struct cifs_pending_open {
+ struct list_head olist;
+ struct tcon_link *tlink;
+ __u8 lease_key[16];
+ __u32 oplock;
+};
+
/*
* This info hangs off the cifsFileInfo structure, pointed to by llist.
* This is used to track byte stream locks on the file
@@ -740,16 +911,29 @@ struct cifs_search_info {
bool smallBuf:1; /* so we know which buf_release function to call */
};
+struct cifs_fid {
+ __u16 netfid;
+#ifdef CONFIG_CIFS_SMB2
+ __u64 persistent_fid; /* persist file id for smb2 */
+ __u64 volatile_fid; /* volatile file id for smb2 */
+ __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for smb2 */
+#endif
+ struct cifs_pending_open *pending_open;
+};
+
+struct cifs_fid_locks {
+ struct list_head llist;
+ struct cifsFileInfo *cfile; /* fid that owns locks */
+ struct list_head locks; /* locks held by fid above */
+};
+
struct cifsFileInfo {
struct list_head tlist; /* pointer to next fid owned by tcon */
struct list_head flist; /* next fid (file instance) for this inode */
- struct list_head llist; /*
- * brlocks held by this fid, protected by
- * lock_mutex from cifsInodeInfo structure
- */
+ struct cifs_fid_locks *llist; /* brlocks held by this fid */
unsigned int uid; /* allows finding which FileInfo structure */
__u32 pid; /* process id who opened file */
- __u16 netfid; /* file id from remote */
+ struct cifs_fid fid; /* file id from remote */
/* BB add lock scope info here if needed */ ;
/* lock scope id (0 if none) */
struct dentry *dentry;
@@ -765,12 +949,60 @@ struct cifsFileInfo {
struct cifs_io_parms {
__u16 netfid;
+#ifdef CONFIG_CIFS_SMB2
+ __u64 persistent_fid; /* persist file id for smb2 */
+ __u64 volatile_fid; /* volatile file id for smb2 */
+#endif
__u32 pid;
__u64 offset;
unsigned int length;
struct cifs_tcon *tcon;
};
+struct cifs_readdata;
+
+/* asynchronous read support */
+struct cifs_readdata {
+ struct kref refcount;
+ struct list_head list;
+ struct completion done;
+ struct cifsFileInfo *cfile;
+ struct address_space *mapping;
+ __u64 offset;
+ unsigned int bytes;
+ pid_t pid;
+ int result;
+ struct work_struct work;
+ int (*read_into_pages)(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata,
+ unsigned int len);
+ struct kvec iov;
+ unsigned int pagesz;
+ unsigned int tailsz;
+ unsigned int nr_pages;
+ struct page *pages[];
+};
+
+struct cifs_writedata;
+
+/* asynchronous write support */
+struct cifs_writedata {
+ struct kref refcount;
+ struct list_head list;
+ struct completion done;
+ enum writeback_sync_modes sync_mode;
+ struct work_struct work;
+ struct cifsFileInfo *cfile;
+ __u64 offset;
+ pid_t pid;
+ unsigned int bytes;
+ int result;
+ unsigned int pagesz;
+ unsigned int tailsz;
+ unsigned int nr_pages;
+ struct page *pages[1];
+};
+
/*
* Take a reference on the file private data. Must be called with
* cifs_file_list_lock held.
@@ -790,11 +1022,8 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
struct cifsInodeInfo {
bool can_cache_brlcks;
- struct mutex lock_mutex; /*
- * protect the field above and llist
- * from every cifsFileInfo structure
- * from openFileList
- */
+ struct list_head llist; /* locks helb by this inode */
+ struct rw_semaphore lock_sem; /* protect the fields above */
/* BB add in lists for dirty pages i.e. write caching info for oplock */
struct list_head openFileList;
__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */
@@ -806,6 +1035,9 @@ struct cifsInodeInfo {
u64 server_eof; /* current file size on server -- protected by i_lock */
u64 uniqueid; /* server inode number */
u64 createtime; /* creation time on server */
+#ifdef CONFIG_CIFS_SMB2
+ __u8 lease_key[SMB2_LEASE_KEY_SIZE]; /* lease key for this inode */
+#endif
#ifdef CONFIG_CIFS_FSCACHE
struct fscache_cookie *fscache;
#endif
@@ -1130,7 +1362,7 @@ require use of the stronger protocol */
#define CIFSSEC_MUST_SEAL 0x40040 /* not supported yet */
#define CIFSSEC_MUST_NTLMSSP 0x80080 /* raw ntlmssp with ntlmv2 */
-#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2)
+#define CIFSSEC_DEF (CIFSSEC_MAY_SIGN | CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_NTLMSSP)
#define CIFSSEC_MAX (CIFSSEC_MUST_SIGN | CIFSSEC_MUST_NTLMV2)
#define CIFSSEC_AUTH_MASK (CIFSSEC_MAY_NTLM | CIFSSEC_MAY_NTLMV2 | CIFSSEC_MAY_LANMAN | CIFSSEC_MAY_PLNTXT | CIFSSEC_MAY_KRB5 | CIFSSEC_MAY_NTLMSSP)
/*
@@ -1267,7 +1499,13 @@ extern mempool_t *cifs_mid_poolp;
#define SMB1_VERSION_STRING "1.0"
extern struct smb_version_operations smb1_operations;
extern struct smb_version_values smb1_values;
+#define SMB20_VERSION_STRING "2.0"
+/*extern struct smb_version_operations smb20_operations; */ /* not needed yet */
+extern struct smb_version_values smb20_values;
#define SMB21_VERSION_STRING "2.1"
extern struct smb_version_operations smb21_operations;
extern struct smb_version_values smb21_values;
+#define SMB30_VERSION_STRING "3.0"
+/*extern struct smb_version_operations smb30_operations; */ /* not needed yet */
+extern struct smb_version_values smb30_values;
#endif /* _CIFS_GLOB_H */
diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h
index 3fb03e2c8e86..b9d59a948a2c 100644
--- a/fs/cifs/cifspdu.h
+++ b/fs/cifs/cifspdu.h
@@ -2210,7 +2210,7 @@ typedef struct { /* data block encoding of response to level 263 QPathInfo */
__u8 DeletePending;
__u8 Directory;
__u16 Pad2;
- __u64 IndexNumber;
+ __le64 IndexNumber;
__le32 EASize;
__le32 AccessFlags;
__u64 IndexNumber1;
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index f1bbf8305d3a..5144e9fbeb8c 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -24,6 +24,7 @@
struct statfs;
struct smb_vol;
+struct smb_rqst;
/*
*****************************************************************
@@ -35,6 +36,8 @@ extern struct smb_hdr *cifs_buf_get(void);
extern void cifs_buf_release(void *);
extern struct smb_hdr *cifs_small_buf_get(void);
extern void cifs_small_buf_release(void *);
+extern void cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx,
+ struct kvec *iov);
extern int smb_send(struct TCP_Server_Info *, struct smb_hdr *,
unsigned int /* length */);
extern unsigned int _get_xid(void);
@@ -65,21 +68,22 @@ extern char *cifs_compose_mount_options(const char *sb_mountdata,
extern struct mid_q_entry *AllocMidQEntry(const struct smb_hdr *smb_buffer,
struct TCP_Server_Info *server);
extern void DeleteMidQEntry(struct mid_q_entry *midEntry);
+extern void cifs_delete_mid(struct mid_q_entry *mid);
extern void cifs_wake_up_task(struct mid_q_entry *mid);
-extern int cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
- unsigned int nvec, mid_receive_t *receive,
- mid_callback_t *callback, void *cbdata,
- const int flags);
+extern int cifs_call_async(struct TCP_Server_Info *server,
+ struct smb_rqst *rqst,
+ mid_receive_t *receive, mid_callback_t *callback,
+ void *cbdata, const int flags);
extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
struct smb_hdr * /* input */ ,
struct smb_hdr * /* out */ ,
int * /* bytes returned */ , const int);
extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
char *in_buf, int flags);
-extern int cifs_setup_request(struct cifs_ses *, struct kvec *, unsigned int,
- struct mid_q_entry **);
-extern int cifs_setup_async_request(struct TCP_Server_Info *, struct kvec *,
- unsigned int, struct mid_q_entry **);
+extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *,
+ struct smb_rqst *);
+extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *,
+ struct smb_rqst *);
extern int cifs_check_receive(struct mid_q_entry *mid,
struct TCP_Server_Info *server, bool log_error);
extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
@@ -99,7 +103,7 @@ extern void cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
unsigned int bytes_written);
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
-extern unsigned int smbCalcSize(struct smb_hdr *ptr);
+extern unsigned int smbCalcSize(void *buf);
extern int decode_negTokenInit(unsigned char *security_blob, int length,
struct TCP_Server_Info *server);
extern int cifs_convert_address(struct sockaddr *dst, const char *src, int len);
@@ -120,10 +124,14 @@ extern u64 cifs_UnixTimeToNT(struct timespec);
extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
int offset);
extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
-
-extern struct cifsFileInfo *cifs_new_fileinfo(__u16 fileHandle,
- struct file *file, struct tcon_link *tlink,
- __u32 oplock);
+extern int cifs_unlock_range(struct cifsFileInfo *cfile,
+ struct file_lock *flock, const unsigned int xid);
+extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile);
+
+extern struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid,
+ struct file *file,
+ struct tcon_link *tlink,
+ __u32 oplock);
extern int cifs_posix_open(char *full_path, struct inode **inode,
struct super_block *sb, int mode,
unsigned int f_flags, __u32 *oplock, __u16 *netfid,
@@ -132,18 +140,23 @@ void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr);
extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
FILE_UNIX_BASIC_INFO *info,
struct cifs_sb_info *cifs_sb);
+extern void cifs_dir_info_to_fattr(struct cifs_fattr *, FILE_DIRECTORY_INFO *,
+ struct cifs_sb_info *);
extern void cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr);
extern struct inode *cifs_iget(struct super_block *sb,
struct cifs_fattr *fattr);
-extern int cifs_get_file_info(struct file *filp);
extern int cifs_get_inode_info(struct inode **inode, const char *full_path,
FILE_ALL_INFO *data, struct super_block *sb,
int xid, const __u16 *fid);
-extern int cifs_get_file_info_unix(struct file *filp);
extern int cifs_get_inode_info_unix(struct inode **pinode,
const unsigned char *search_path,
struct super_block *sb, unsigned int xid);
+extern int cifs_set_file_info(struct inode *inode, struct iattr *attrs,
+ unsigned int xid, char *full_path, __u32 dosattr);
+extern int cifs_rename_pending_delete(const char *full_path,
+ struct dentry *dentry,
+ const unsigned int xid);
extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,
struct cifs_fattr *fattr, struct inode *inode,
const char *path, const __u16 *pfid);
@@ -169,6 +182,17 @@ extern struct smb_vol *cifs_get_volume_info(char *mount_data,
extern int cifs_mount(struct cifs_sb_info *, struct smb_vol *);
extern void cifs_umount(struct cifs_sb_info *);
extern void cifs_mark_open_files_invalid(struct cifs_tcon *tcon);
+extern bool cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
+ __u64 length, __u8 type,
+ struct cifsLockInfo **conf_lock,
+ bool rw_check);
+extern void cifs_add_pending_open(struct cifs_fid *fid,
+ struct tcon_link *tlink,
+ struct cifs_pending_open *open);
+extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
+ struct tcon_link *tlink,
+ struct cifs_pending_open *open);
+extern void cifs_del_pending_open(struct cifs_pending_open *open);
#if IS_ENABLED(CONFIG_CIFS_DFS_UPCALL)
extern void cifs_dfs_release_automount_timer(void);
@@ -179,6 +203,10 @@ extern void cifs_dfs_release_automount_timer(void);
void cifs_proc_init(void);
void cifs_proc_clean(void);
+extern void cifs_move_llist(struct list_head *source, struct list_head *dest);
+extern void cifs_free_llist(struct list_head *llist);
+extern void cifs_del_lock_waiters(struct cifsLockInfo *lock);
+
extern int cifs_negotiate_protocol(const unsigned int xid,
struct cifs_ses *ses);
extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
@@ -190,10 +218,10 @@ extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
const struct nls_table *);
extern int CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
- const char *searchName, const struct nls_table *nls_codepage,
+ const char *searchName, struct cifs_sb_info *cifs_sb,
__u16 *searchHandle, __u16 search_flags,
struct cifs_search_info *psrch_inf,
- int map, const char dirsep);
+ bool msearch);
extern int CIFSFindNext(const unsigned int xid, struct cifs_tcon *tcon,
__u16 searchHandle, __u16 search_flags,
@@ -265,13 +293,11 @@ extern int CIFSSMBSetAttrLegacy(unsigned int xid, struct cifs_tcon *tcon,
const struct nls_table *nls_codepage);
#endif /* possibly unneeded function */
extern int CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
- const char *fileName, __u64 size,
- bool setAllocationSizeFlag,
- const struct nls_table *nls_codepage,
- int remap_special_chars);
+ const char *file_name, __u64 size,
+ struct cifs_sb_info *cifs_sb, bool set_allocation);
extern int CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
- __u64 size, __u16 fileHandle, __u32 opener_pid,
- bool AllocSizeFlag);
+ struct cifsFileInfo *cfile, __u64 size,
+ bool set_allocation);
struct cifs_unix_set_info_args {
__u64 ctime;
@@ -303,22 +329,17 @@ extern int CIFSPOSIXDelFile(const unsigned int xid, struct cifs_tcon *tcon,
const struct nls_table *nls_codepage,
int remap_special_chars);
extern int CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon,
- const char *name,
- const struct nls_table *nls_codepage,
- int remap_special_chars);
+ const char *name, struct cifs_sb_info *cifs_sb);
extern int CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
- const char *fromName, const char *toName,
- const struct nls_table *nls_codepage,
- int remap_special_chars);
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb);
extern int CIFSSMBRenameOpenFile(const unsigned int xid, struct cifs_tcon *tcon,
int netfid, const char *target_name,
const struct nls_table *nls_codepage,
int remap_special_chars);
-extern int CIFSCreateHardLink(const unsigned int xid,
- struct cifs_tcon *tcon,
- const char *fromName, const char *toName,
- const struct nls_table *nls_codepage,
- int remap_special_chars);
+extern int CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb);
extern int CIFSUnixCreateHardLink(const unsigned int xid,
struct cifs_tcon *tcon,
const char *fromName, const char *toName,
@@ -367,8 +388,7 @@ extern int CIFSSMBWrite(const unsigned int xid, struct cifs_io_parms *io_parms,
unsigned int *nbytes, const char *buf,
const char __user *ubuf, const int long_op);
extern int CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
- unsigned int *nbytes, struct kvec *iov, const int nvec,
- const int long_op);
+ unsigned int *nbytes, struct kvec *iov, const int nvec);
extern int CIFSGetSrvInodeNumber(const unsigned int xid, struct cifs_tcon *tcon,
const char *search_name, __u64 *inode_number,
const struct nls_table *nls_codepage,
@@ -397,10 +417,12 @@ extern void sesInfoFree(struct cifs_ses *);
extern struct cifs_tcon *tconInfoAlloc(void);
extern void tconInfoFree(struct cifs_tcon *);
-extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *);
+extern int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
+ __u32 *pexpected_response_sequence_number);
extern int cifs_sign_smbv(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
__u32 *);
-extern int cifs_verify_signature(struct kvec *iov, unsigned int nr_iov,
+extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *);
+extern int cifs_verify_signature(struct smb_rqst *rqst,
struct TCP_Server_Info *server,
__u32 expected_sequence_number);
extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *,
@@ -462,45 +484,9 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
extern int SMBencrypt(unsigned char *passwd, const unsigned char *c8,
unsigned char *p24);
-/* asynchronous read support */
-struct cifs_readdata {
- struct kref refcount;
- struct list_head list;
- struct completion done;
- struct cifsFileInfo *cfile;
- struct address_space *mapping;
- __u64 offset;
- unsigned int bytes;
- pid_t pid;
- int result;
- struct list_head pages;
- struct work_struct work;
- int (*marshal_iov) (struct cifs_readdata *rdata,
- unsigned int remaining);
- unsigned int nr_iov;
- struct kvec iov[1];
-};
-
void cifs_readdata_release(struct kref *refcount);
int cifs_async_readv(struct cifs_readdata *rdata);
-
-/* asynchronous write support */
-struct cifs_writedata {
- struct kref refcount;
- struct list_head list;
- struct completion done;
- enum writeback_sync_modes sync_mode;
- struct work_struct work;
- struct cifsFileInfo *cfile;
- __u64 offset;
- pid_t pid;
- unsigned int bytes;
- int result;
- void (*marshal_iov) (struct kvec *iov,
- struct cifs_writedata *wdata);
- unsigned int nr_pages;
- struct page *pages[1];
-};
+int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid);
int cifs_async_writev(struct cifs_writedata *wdata);
void cifs_writev_complete(struct work_struct *work);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index f0cf934ba877..76d0d2998850 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -86,32 +86,6 @@ static struct {
#endif /* CONFIG_CIFS_WEAK_PW_HASH */
#endif /* CIFS_POSIX */
-#ifdef CONFIG_HIGHMEM
-/*
- * On arches that have high memory, kmap address space is limited. By
- * serializing the kmap operations on those arches, we ensure that we don't
- * end up with a bunch of threads in writeback with partially mapped page
- * arrays, stuck waiting for kmap to come back. That situation prevents
- * progress and can deadlock.
- */
-static DEFINE_MUTEX(cifs_kmap_mutex);
-
-static inline void
-cifs_kmap_lock(void)
-{
- mutex_lock(&cifs_kmap_mutex);
-}
-
-static inline void
-cifs_kmap_unlock(void)
-{
- mutex_unlock(&cifs_kmap_mutex);
-}
-#else /* !CONFIG_HIGHMEM */
-#define cifs_kmap_lock() do { ; } while(0)
-#define cifs_kmap_unlock() do { ; } while(0)
-#endif /* CONFIG_HIGHMEM */
-
/*
* Mark as invalid, all open files on tree connections since they
* were closed when session to server was lost.
@@ -751,6 +725,8 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
ECHO_REQ *smb;
int rc = 0;
struct kvec iov;
+ struct smb_rqst rqst = { .rq_iov = &iov,
+ .rq_nvec = 1 };
cFYI(1, "In echo request");
@@ -768,7 +744,7 @@ CIFSSMBEcho(struct TCP_Server_Info *server)
iov.iov_base = smb;
iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
- rc = cifs_call_async(server, &iov, 1, NULL, cifs_echo_callback,
+ rc = cifs_call_async(server, &rqst, NULL, cifs_echo_callback,
server, CIFS_ASYNC_OP | CIFS_ECHO_OP);
if (rc)
cFYI(1, "Echo request failed: %d", rc);
@@ -902,15 +878,15 @@ PsxDelete:
}
int
-CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon,
- const char *fileName, const struct nls_table *nls_codepage,
- int remap)
+CIFSSMBDelFile(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
{
DELETE_FILE_REQ *pSMB = NULL;
DELETE_FILE_RSP *pSMBr = NULL;
int rc = 0;
int bytes_returned;
int name_len;
+ int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
DelFileRetry:
rc = smb_init(SMB_COM_DELETE, 1, tcon, (void **) &pSMB,
@@ -919,15 +895,15 @@ DelFileRetry:
return rc;
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
- name_len =
- cifsConvertToUTF16((__le16 *) pSMB->fileName, fileName,
- PATH_MAX, nls_codepage, remap);
+ name_len = cifsConvertToUTF16((__le16 *) pSMB->fileName, name,
+ PATH_MAX, cifs_sb->local_nls,
+ remap);
name_len++; /* trailing null */
name_len *= 2;
} else { /* BB improve check for buffer overruns BB */
- name_len = strnlen(fileName, PATH_MAX);
+ name_len = strnlen(name, PATH_MAX);
name_len++; /* trailing null */
- strncpy(pSMB->fileName, fileName, name_len);
+ strncpy(pSMB->fileName, name, name_len);
}
pSMB->SearchAttributes =
cpu_to_le16(ATTR_READONLY | ATTR_HIDDEN | ATTR_SYSTEM);
@@ -1440,7 +1416,7 @@ cifs_readv_discard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return 0;
}
-static int
+int
cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
{
int length, len;
@@ -1460,10 +1436,10 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
len = min_t(unsigned int, buflen, server->vals->read_rsp_size) -
HEADER_SIZE(server) + 1;
- rdata->iov[0].iov_base = buf + HEADER_SIZE(server) - 1;
- rdata->iov[0].iov_len = len;
+ rdata->iov.iov_base = buf + HEADER_SIZE(server) - 1;
+ rdata->iov.iov_len = len;
- length = cifs_readv_from_socket(server, rdata->iov, 1, len);
+ length = cifs_readv_from_socket(server, &rdata->iov, 1, len);
if (length < 0)
return length;
server->total_read += length;
@@ -1509,19 +1485,19 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
len = data_offset - server->total_read;
if (len > 0) {
/* read any junk before data into the rest of smallbuf */
- rdata->iov[0].iov_base = buf + server->total_read;
- rdata->iov[0].iov_len = len;
- length = cifs_readv_from_socket(server, rdata->iov, 1, len);
+ rdata->iov.iov_base = buf + server->total_read;
+ rdata->iov.iov_len = len;
+ length = cifs_readv_from_socket(server, &rdata->iov, 1, len);
if (length < 0)
return length;
server->total_read += length;
}
/* set up first iov for signature check */
- rdata->iov[0].iov_base = buf;
- rdata->iov[0].iov_len = server->total_read;
+ rdata->iov.iov_base = buf;
+ rdata->iov.iov_len = server->total_read;
cFYI(1, "0: iov_base=%p iov_len=%zu",
- rdata->iov[0].iov_base, rdata->iov[0].iov_len);
+ rdata->iov.iov_base, rdata->iov.iov_len);
/* how much data is in the response? */
data_len = server->ops->read_data_length(buf);
@@ -1531,23 +1507,11 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
return cifs_readv_discard(server, mid);
}
- /* marshal up the page array */
- cifs_kmap_lock();
- len = rdata->marshal_iov(rdata, data_len);
- cifs_kmap_unlock();
- data_len -= len;
-
- /* issue the read if we have any iovecs left to fill */
- if (rdata->nr_iov > 1) {
- length = cifs_readv_from_socket(server, &rdata->iov[1],
- rdata->nr_iov - 1, len);
- if (length < 0)
- return length;
- server->total_read += length;
- } else {
- length = 0;
- }
+ length = rdata->read_into_pages(server, rdata, data_len);
+ if (length < 0)
+ return length;
+ server->total_read += length;
rdata->bytes = length;
cFYI(1, "total_read=%u buflen=%u remaining=%u", server->total_read,
@@ -1567,6 +1531,12 @@ cifs_readv_callback(struct mid_q_entry *mid)
struct cifs_readdata *rdata = mid->callback_data;
struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
+ struct smb_rqst rqst = { .rq_iov = &rdata->iov,
+ .rq_nvec = 1,
+ .rq_pages = rdata->pages,
+ .rq_npages = rdata->nr_pages,
+ .rq_pagesz = rdata->pagesz,
+ .rq_tailsz = rdata->tailsz };
cFYI(1, "%s: mid=%llu state=%d result=%d bytes=%u", __func__,
mid->mid, mid->mid_state, rdata->result, rdata->bytes);
@@ -1578,9 +1548,8 @@ cifs_readv_callback(struct mid_q_entry *mid)
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
int rc = 0;
- rc = cifs_verify_signature(rdata->iov, rdata->nr_iov,
- server,
- mid->sequence_number + 1);
+ rc = cifs_verify_signature(&rqst, server,
+ mid->sequence_number + 1);
if (rc)
cERROR(1, "SMB signature verification returned "
"error = %d", rc);
@@ -1610,6 +1579,8 @@ cifs_async_readv(struct cifs_readdata *rdata)
READ_REQ *smb = NULL;
int wct;
struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+ struct smb_rqst rqst = { .rq_iov = &rdata->iov,
+ .rq_nvec = 1 };
cFYI(1, "%s: offset=%llu bytes=%u", __func__,
rdata->offset, rdata->bytes);
@@ -1632,7 +1603,7 @@ cifs_async_readv(struct cifs_readdata *rdata)
smb->hdr.PidHigh = cpu_to_le16((__u16)(rdata->pid >> 16));
smb->AndXCommand = 0xFF; /* none */
- smb->Fid = rdata->cfile->netfid;
+ smb->Fid = rdata->cfile->fid.netfid;
smb->OffsetLow = cpu_to_le32(rdata->offset & 0xFFFFFFFF);
if (wct == 12)
smb->OffsetHigh = cpu_to_le32(rdata->offset >> 32);
@@ -1649,13 +1620,12 @@ cifs_async_readv(struct cifs_readdata *rdata)
}
/* 4 for RFC1001 length + 1 for BCC */
- rdata->iov[0].iov_base = smb;
- rdata->iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
+ rdata->iov.iov_base = smb;
+ rdata->iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4;
kref_get(&rdata->refcount);
- rc = cifs_call_async(tcon->ses->server, rdata->iov, 1,
- cifs_readv_receive, cifs_readv_callback,
- rdata, 0);
+ rc = cifs_call_async(tcon->ses->server, &rqst, cifs_readv_receive,
+ cifs_readv_callback, rdata, 0);
if (rc == 0)
cifs_stats_inc(&tcon->stats.cifs_stats.num_reads);
@@ -1926,6 +1896,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
{
int i, rc;
struct inode *inode = wdata->cfile->dentry->d_inode;
+ struct TCP_Server_Info *server;
for (i = 0; i < wdata->nr_pages; i++) {
lock_page(wdata->pages[i]);
@@ -1933,7 +1904,8 @@ cifs_writev_requeue(struct cifs_writedata *wdata)
}
do {
- rc = cifs_async_writev(wdata);
+ server = tlink_tcon(wdata->cfile->tlink)->ses->server;
+ rc = server->ops->async_writev(wdata);
} while (rc == -EAGAIN);
for (i = 0; i < wdata->nr_pages; i++) {
@@ -2053,11 +2025,12 @@ cifs_writev_callback(struct mid_q_entry *mid)
int
cifs_async_writev(struct cifs_writedata *wdata)
{
- int i, rc = -EACCES;
+ int rc = -EACCES;
WRITE_REQ *smb = NULL;
int wct;
struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
- struct kvec *iov = NULL;
+ struct kvec iov;
+ struct smb_rqst rqst = { };
if (tcon->ses->capabilities & CAP_LARGE_FILES) {
wct = 14;
@@ -2073,18 +2046,11 @@ cifs_async_writev(struct cifs_writedata *wdata)
if (rc)
goto async_writev_out;
- /* 1 iov per page + 1 for header */
- iov = kzalloc((wdata->nr_pages + 1) * sizeof(*iov), GFP_NOFS);
- if (iov == NULL) {
- rc = -ENOMEM;
- goto async_writev_out;
- }
-
smb->hdr.Pid = cpu_to_le16((__u16)wdata->pid);
smb->hdr.PidHigh = cpu_to_le16((__u16)(wdata->pid >> 16));
smb->AndXCommand = 0xFF; /* none */
- smb->Fid = wdata->cfile->netfid;
+ smb->Fid = wdata->cfile->fid.netfid;
smb->OffsetLow = cpu_to_le32(wdata->offset & 0xFFFFFFFF);
if (wct == 14)
smb->OffsetHigh = cpu_to_le32(wdata->offset >> 32);
@@ -2096,18 +2062,15 @@ cifs_async_writev(struct cifs_writedata *wdata)
cpu_to_le16(offsetof(struct smb_com_write_req, Data) - 4);
/* 4 for RFC1001 length + 1 for BCC */
- iov[0].iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1;
- iov[0].iov_base = smb;
+ iov.iov_len = be32_to_cpu(smb->hdr.smb_buf_length) + 4 + 1;
+ iov.iov_base = smb;
- /*
- * This function should marshal up the page array into the kvec
- * array, reserving [0] for the header. It should kmap the pages
- * and set the iov_len properly for each one. It may also set
- * wdata->bytes too.
- */
- cifs_kmap_lock();
- wdata->marshal_iov(iov, wdata);
- cifs_kmap_unlock();
+ rqst.rq_iov = &iov;
+ rqst.rq_nvec = 1;
+ rqst.rq_pages = wdata->pages;
+ rqst.rq_npages = wdata->nr_pages;
+ rqst.rq_pagesz = wdata->pagesz;
+ rqst.rq_tailsz = wdata->tailsz;
cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes);
@@ -2123,32 +2086,26 @@ cifs_async_writev(struct cifs_writedata *wdata)
(struct smb_com_writex_req *)smb;
inc_rfc1001_len(&smbw->hdr, wdata->bytes + 5);
put_bcc(wdata->bytes + 5, &smbw->hdr);
- iov[0].iov_len += 4; /* pad bigger by four bytes */
+ iov.iov_len += 4; /* pad bigger by four bytes */
}
kref_get(&wdata->refcount);
- rc = cifs_call_async(tcon->ses->server, iov, wdata->nr_pages + 1,
- NULL, cifs_writev_callback, wdata, 0);
+ rc = cifs_call_async(tcon->ses->server, &rqst, NULL,
+ cifs_writev_callback, wdata, 0);
if (rc == 0)
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
else
kref_put(&wdata->refcount, cifs_writedata_release);
- /* send is done, unmap pages */
- for (i = 0; i < wdata->nr_pages; i++)
- kunmap(wdata->pages[i]);
-
async_writev_out:
cifs_small_buf_release(smb);
- kfree(iov);
return rc;
}
int
CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
- unsigned int *nbytes, struct kvec *iov, int n_vec,
- const int long_op)
+ unsigned int *nbytes, struct kvec *iov, int n_vec)
{
int rc = -EACCES;
WRITE_REQ *pSMB = NULL;
@@ -2219,8 +2176,7 @@ CIFSSMBWrite2(const unsigned int xid, struct cifs_io_parms *io_parms,
iov[0].iov_len = smb_hdr_len + 8;
- rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type,
- long_op);
+ rc = SendReceive2(xid, tcon->ses, iov, n_vec + 1, &resp_buf_type, 0);
cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);
if (rc) {
cFYI(1, "Send error Write2 = %d", rc);
@@ -2557,8 +2513,8 @@ CIFSSMBFlush(const unsigned int xid, struct cifs_tcon *tcon, int smb_file_id)
int
CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
- const char *fromName, const char *toName,
- const struct nls_table *nls_codepage, int remap)
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb)
{
int rc = 0;
RENAME_REQ *pSMB = NULL;
@@ -2566,6 +2522,7 @@ CIFSSMBRename(const unsigned int xid, struct cifs_tcon *tcon,
int bytes_returned;
int name_len, name_len2;
__u16 count;
+ int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
cFYI(1, "In CIFSSMBRename");
renameRetry:
@@ -2580,9 +2537,9 @@ renameRetry:
ATTR_DIRECTORY);
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
- name_len =
- cifsConvertToUTF16((__le16 *) pSMB->OldFileName, fromName,
- PATH_MAX, nls_codepage, remap);
+ name_len = cifsConvertToUTF16((__le16 *) pSMB->OldFileName,
+ from_name, PATH_MAX,
+ cifs_sb->local_nls, remap);
name_len++; /* trailing null */
name_len *= 2;
pSMB->OldFileName[name_len] = 0x04; /* pad */
@@ -2590,17 +2547,18 @@ renameRetry:
pSMB->OldFileName[name_len + 1] = 0x00;
name_len2 =
cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
- toName, PATH_MAX, nls_codepage, remap);
+ to_name, PATH_MAX, cifs_sb->local_nls,
+ remap);
name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
name_len2 *= 2; /* convert to bytes */
} else { /* BB improve the check for buffer overruns BB */
- name_len = strnlen(fromName, PATH_MAX);
+ name_len = strnlen(from_name, PATH_MAX);
name_len++; /* trailing null */
- strncpy(pSMB->OldFileName, fromName, name_len);
- name_len2 = strnlen(toName, PATH_MAX);
+ strncpy(pSMB->OldFileName, from_name, name_len);
+ name_len2 = strnlen(to_name, PATH_MAX);
name_len2++; /* trailing null */
pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
- strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
+ strncpy(&pSMB->OldFileName[name_len + 1], to_name, name_len2);
name_len2++; /* trailing null */
name_len2++; /* signature byte */
}
@@ -2948,8 +2906,8 @@ createHardLinkRetry:
int
CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
- const char *fromName, const char *toName,
- const struct nls_table *nls_codepage, int remap)
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb)
{
int rc = 0;
NT_RENAME_REQ *pSMB = NULL;
@@ -2957,6 +2915,7 @@ CIFSCreateHardLink(const unsigned int xid, struct cifs_tcon *tcon,
int bytes_returned;
int name_len, name_len2;
__u16 count;
+ int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
cFYI(1, "In CIFSCreateHardLink");
winCreateHardLinkRetry:
@@ -2976,8 +2935,8 @@ winCreateHardLinkRetry:
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
- cifsConvertToUTF16((__le16 *) pSMB->OldFileName, fromName,
- PATH_MAX, nls_codepage, remap);
+ cifsConvertToUTF16((__le16 *) pSMB->OldFileName, from_name,
+ PATH_MAX, cifs_sb->local_nls, remap);
name_len++; /* trailing null */
name_len *= 2;
@@ -2986,17 +2945,18 @@ winCreateHardLinkRetry:
pSMB->OldFileName[name_len + 1] = 0x00; /* pad */
name_len2 =
cifsConvertToUTF16((__le16 *)&pSMB->OldFileName[name_len+2],
- toName, PATH_MAX, nls_codepage, remap);
+ to_name, PATH_MAX, cifs_sb->local_nls,
+ remap);
name_len2 += 1 /* trailing null */ + 1 /* Signature word */ ;
name_len2 *= 2; /* convert to bytes */
} else { /* BB improve the check for buffer overruns BB */
- name_len = strnlen(fromName, PATH_MAX);
+ name_len = strnlen(from_name, PATH_MAX);
name_len++; /* trailing null */
- strncpy(pSMB->OldFileName, fromName, name_len);
- name_len2 = strnlen(toName, PATH_MAX);
+ strncpy(pSMB->OldFileName, from_name, name_len);
+ name_len2 = strnlen(to_name, PATH_MAX);
name_len2++; /* trailing null */
pSMB->OldFileName[name_len] = 0x04; /* 2nd buffer format */
- strncpy(&pSMB->OldFileName[name_len + 1], toName, name_len2);
+ strncpy(&pSMB->OldFileName[name_len + 1], to_name, name_len2);
name_len2++; /* trailing null */
name_len2++; /* signature byte */
}
@@ -4254,10 +4214,9 @@ UnixQPathInfoRetry:
/* xid, tcon, searchName and codepage are input parms, rest are returned */
int
CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
- const char *searchName,
- const struct nls_table *nls_codepage,
+ const char *searchName, struct cifs_sb_info *cifs_sb,
__u16 *pnetfid, __u16 search_flags,
- struct cifs_search_info *psrch_inf, int remap, const char dirsep)
+ struct cifs_search_info *psrch_inf, bool msearch)
{
/* level 257 SMB_ */
TRANSACTION2_FFIRST_REQ *pSMB = NULL;
@@ -4265,8 +4224,9 @@ CIFSFindFirst(const unsigned int xid, struct cifs_tcon *tcon,
T2_FFIRST_RSP_PARMS *parms;
int rc = 0;
int bytes_returned = 0;
- int name_len;
+ int name_len, remap;
__u16 params, byte_count;
+ struct nls_table *nls_codepage;
cFYI(1, "In FindFirst for %s", searchName);
@@ -4276,6 +4236,9 @@ findFirstRetry:
if (rc)
return rc;
+ nls_codepage = cifs_sb->local_nls;
+ remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
+
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
cifsConvertToUTF16((__le16 *) pSMB->FileName, searchName,
@@ -4284,24 +4247,29 @@ findFirstRetry:
it got remapped to 0xF03A as if it were part of the
directory name instead of a wildcard */
name_len *= 2;
- pSMB->FileName[name_len] = dirsep;
- pSMB->FileName[name_len+1] = 0;
- pSMB->FileName[name_len+2] = '*';
- pSMB->FileName[name_len+3] = 0;
- name_len += 4; /* now the trailing null */
- pSMB->FileName[name_len] = 0; /* null terminate just in case */
- pSMB->FileName[name_len+1] = 0;
- name_len += 2;
+ if (msearch) {
+ pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb);
+ pSMB->FileName[name_len+1] = 0;
+ pSMB->FileName[name_len+2] = '*';
+ pSMB->FileName[name_len+3] = 0;
+ name_len += 4; /* now the trailing null */
+ /* null terminate just in case */
+ pSMB->FileName[name_len] = 0;
+ pSMB->FileName[name_len+1] = 0;
+ name_len += 2;
+ }
} else { /* BB add check for overrun of SMB buf BB */
name_len = strnlen(searchName, PATH_MAX);
/* BB fix here and in unicode clause above ie
if (name_len > buffersize-header)
free buffer exit; BB */
strncpy(pSMB->FileName, searchName, name_len);
- pSMB->FileName[name_len] = dirsep;
- pSMB->FileName[name_len+1] = '*';
- pSMB->FileName[name_len+2] = 0;
- name_len += 3;
+ if (msearch) {
+ pSMB->FileName[name_len] = CIFS_DIR_SEP(cifs_sb);
+ pSMB->FileName[name_len+1] = '*';
+ pSMB->FileName[name_len+2] = 0;
+ name_len += 3;
+ }
}
params = 12 + name_len /* includes null */ ;
@@ -4389,7 +4357,8 @@ findFirstRetry:
psrch_inf->last_entry = psrch_inf->srch_entries_start +
lnoff;
- *pnetfid = parms->SearchHandle;
+ if (pnetfid)
+ *pnetfid = parms->SearchHandle;
} else {
cifs_buf_release(pSMB);
}
@@ -5417,16 +5386,16 @@ QFSPosixRetry:
}
-/* We can not use write of zero bytes trick to
- set file size due to need for large file support. Also note that
- this SetPathInfo is preferred to SetFileInfo based method in next
- routine which is only needed to work around a sharing violation bug
- in Samba which this routine can run into */
-
+/*
+ * We can not use write of zero bytes trick to set file size due to need for
+ * large file support. Also note that this SetPathInfo is preferred to
+ * SetFileInfo based method in next routine which is only needed to work around
+ * a sharing violation bugin Samba which this routine can run into.
+ */
int
CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
- const char *fileName, __u64 size, bool SetAllocation,
- const struct nls_table *nls_codepage, int remap)
+ const char *file_name, __u64 size, struct cifs_sb_info *cifs_sb,
+ bool set_allocation)
{
struct smb_com_transaction2_spi_req *pSMB = NULL;
struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
@@ -5434,6 +5403,8 @@ CIFSSMBSetEOF(const unsigned int xid, struct cifs_tcon *tcon,
int name_len;
int rc = 0;
int bytes_returned = 0;
+ int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;
+
__u16 params, byte_count, data_count, param_offset, offset;
cFYI(1, "In SetEOF");
@@ -5445,14 +5416,14 @@ SetEOFRetry:
if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) {
name_len =
- cifsConvertToUTF16((__le16 *) pSMB->FileName, fileName,
- PATH_MAX, nls_codepage, remap);
+ cifsConvertToUTF16((__le16 *) pSMB->FileName, file_name,
+ PATH_MAX, cifs_sb->local_nls, remap);
name_len++; /* trailing null */
name_len *= 2;
} else { /* BB improve the check for buffer overruns BB */
- name_len = strnlen(fileName, PATH_MAX);
+ name_len = strnlen(file_name, PATH_MAX);
name_len++; /* trailing null */
- strncpy(pSMB->FileName, fileName, name_len);
+ strncpy(pSMB->FileName, file_name, name_len);
}
params = 6 + name_len;
data_count = sizeof(struct file_end_of_file_info);
@@ -5466,7 +5437,7 @@ SetEOFRetry:
param_offset = offsetof(struct smb_com_transaction2_spi_req,
InformationLevel) - 4;
offset = param_offset + params;
- if (SetAllocation) {
+ if (set_allocation) {
if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
pSMB->InformationLevel =
cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
@@ -5513,8 +5484,8 @@ SetEOFRetry:
}
int
-CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, __u64 size,
- __u16 fid, __u32 pid_of_opener, bool SetAllocation)
+CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile, __u64 size, bool set_allocation)
{
struct smb_com_transaction2_sfi_req *pSMB = NULL;
struct file_end_of_file_info *parm_data;
@@ -5528,8 +5499,8 @@ CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, __u64 size,
if (rc)
return rc;
- pSMB->hdr.Pid = cpu_to_le16((__u16)pid_of_opener);
- pSMB->hdr.PidHigh = cpu_to_le16((__u16)(pid_of_opener >> 16));
+ pSMB->hdr.Pid = cpu_to_le16((__u16)cfile->pid);
+ pSMB->hdr.PidHigh = cpu_to_le16((__u16)(cfile->pid >> 16));
params = 6;
pSMB->MaxSetupCount = 0;
@@ -5558,8 +5529,8 @@ CIFSSMBSetFileSize(const unsigned int xid, struct cifs_tcon *tcon, __u64 size,
+ offset);
pSMB->DataOffset = cpu_to_le16(offset);
parm_data->FileSize = cpu_to_le64(size);
- pSMB->Fid = fid;
- if (SetAllocation) {
+ pSMB->Fid = cfile->fid.netfid;
+ if (set_allocation) {
if (tcon->ses->capabilities & CAP_INFOLEVEL_PASSTHRU)
pSMB->InformationLevel =
cpu_to_le16(SMB_SET_FILE_ALLOCATION_INFO2);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 6df6fa14cba8..2fdbe08a7a23 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -82,8 +82,7 @@ enum {
Opt_serverino, Opt_noserverino,
Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl,
Opt_acl, Opt_noacl, Opt_locallease,
- Opt_sign, Opt_seal, Opt_direct,
- Opt_strictcache, Opt_noac,
+ Opt_sign, Opt_seal, Opt_noac,
Opt_fsc, Opt_mfsymlinks,
Opt_multiuser, Opt_sloppy,
@@ -160,10 +159,6 @@ static const match_table_t cifs_mount_option_tokens = {
{ Opt_locallease, "locallease" },
{ Opt_sign, "sign" },
{ Opt_seal, "seal" },
- { Opt_direct, "direct" },
- { Opt_direct, "directio" },
- { Opt_direct, "forcedirectio" },
- { Opt_strictcache, "strictcache" },
{ Opt_noac, "noac" },
{ Opt_fsc, "fsc" },
{ Opt_mfsymlinks, "mfsymlinks" },
@@ -277,6 +272,7 @@ static const match_table_t cifs_cacheflavor_tokens = {
static const match_table_t cifs_smb_version_tokens = {
{ Smb_1, SMB1_VERSION_STRING },
{ Smb_21, SMB21_VERSION_STRING },
+ { Smb_30, SMB30_VERSION_STRING },
};
static int ip_connect(struct TCP_Server_Info *server);
@@ -819,6 +815,10 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
cifs_dump_mem("Bad SMB: ", buf,
min_t(unsigned int, server->total_read, 48));
+ if (server->ops->is_status_pending &&
+ server->ops->is_status_pending(buf, server, length))
+ return -1;
+
if (!mid)
return length;
@@ -1075,6 +1075,10 @@ cifs_parse_smb_version(char *value, struct smb_vol *vol)
vol->ops = &smb21_operations;
vol->vals = &smb21_values;
break;
+ case Smb_30:
+ vol->ops = &smb21_operations; /* currently identical with 2.1 */
+ vol->vals = &smb30_values;
+ break;
#endif
default:
cERROR(1, "Unknown vers= option specified: %s", value);
@@ -1101,8 +1105,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
char *string = NULL;
char *tmp_end, *value;
char delim;
- bool cache_specified = false;
- static bool cache_warned = false;
separator[0] = ',';
separator[1] = 0;
@@ -1134,6 +1136,9 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
/* default to using server inode numbers where available */
vol->server_ino = 1;
+ /* default is to use strict cifs caching semantics */
+ vol->strict_io = true;
+
vol->actimeo = CIFS_DEF_ACTIMEO;
/* FIXME: add autonegotiation -- for now, SMB1 is default */
@@ -1317,22 +1322,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
*/
vol->seal = 1;
break;
- case Opt_direct:
- cache_specified = true;
- vol->direct_io = true;
- vol->strict_io = false;
- cERROR(1, "The \"directio\" option will be removed in "
- "3.7. Please switch to the \"cache=none\" "
- "option.");
- break;
- case Opt_strictcache:
- cache_specified = true;
- vol->direct_io = false;
- vol->strict_io = true;
- cERROR(1, "The \"strictcache\" option will be removed "
- "in 3.7. Please switch to the \"cache=strict\" "
- "option.");
- break;
case Opt_noac:
printk(KERN_WARNING "CIFS: Mount option noac not "
"supported. Instead set "
@@ -1676,8 +1665,13 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
if (string == NULL)
goto out_nomem;
- if (strnicmp(string, "TCP_NODELAY", 11) == 0)
+ if (strnicmp(string, "TCP_NODELAY", 11) == 0) {
+ printk(KERN_WARNING "CIFS: the "
+ "sockopt=TCP_NODELAY option has been "
+ "deprecated and will be removed "
+ "in 3.9\n");
vol->sockopt_tcp_nodelay = 1;
+ }
break;
case Opt_netbiosname:
string = match_strdup(args);
@@ -1762,7 +1756,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
goto cifs_parse_mount_err;
break;
case Opt_cache:
- cache_specified = true;
string = match_strdup(args);
if (string == NULL)
goto out_nomem;
@@ -1813,14 +1806,6 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
printk(KERN_NOTICE "CIFS: ignoring forcegid mount option "
"specified with no gid= option.\n");
- /* FIXME: remove this block in 3.7 */
- if (!cache_specified && !cache_warned) {
- cache_warned = true;
- printk(KERN_NOTICE "CIFS: no cache= option specified, using "
- "\"cache=loose\". This default will change "
- "to \"cache=strict\" in 3.7.\n");
- }
-
kfree(mountdata_copy);
return 0;
@@ -2636,6 +2621,7 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
tcon->retry = volume_info->retry;
tcon->nocase = volume_info->nocase;
tcon->local_lease = volume_info->local_lease;
+ INIT_LIST_HEAD(&tcon->pending_opens);
spin_lock(&cifs_tcp_ses_lock);
list_add(&tcon->tcon_list, &ses->tcon_list);
@@ -3261,146 +3247,6 @@ void cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
"mount option supported");
}
-/*
- * When the server supports very large reads and writes via POSIX extensions,
- * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not
- * including the RFC1001 length.
- *
- * Note that this might make for "interesting" allocation problems during
- * writeback however as we have to allocate an array of pointers for the
- * pages. A 16M write means ~32kb page array with PAGE_CACHE_SIZE == 4096.
- *
- * For reads, there is a similar problem as we need to allocate an array
- * of kvecs to handle the receive, though that should only need to be done
- * once.
- */
-#define CIFS_MAX_WSIZE ((1<<24) - 1 - sizeof(WRITE_REQ) + 4)
-#define CIFS_MAX_RSIZE ((1<<24) - sizeof(READ_RSP) + 4)
-
-/*
- * When the server doesn't allow large posix writes, only allow a rsize/wsize
- * of 2^17-1 minus the size of the call header. That allows for a read or
- * write up to the maximum size described by RFC1002.
- */
-#define CIFS_MAX_RFC1002_WSIZE ((1<<17) - 1 - sizeof(WRITE_REQ) + 4)
-#define CIFS_MAX_RFC1002_RSIZE ((1<<17) - 1 - sizeof(READ_RSP) + 4)
-
-/*
- * The default wsize is 1M. find_get_pages seems to return a maximum of 256
- * pages in a single call. With PAGE_CACHE_SIZE == 4k, this means we can fill
- * a single wsize request with a single call.
- */
-#define CIFS_DEFAULT_IOSIZE (1024 * 1024)
-
-/*
- * Windows only supports a max of 60kb reads and 65535 byte writes. Default to
- * those values when posix extensions aren't in force. In actuality here, we
- * use 65536 to allow for a write that is a multiple of 4k. Most servers seem
- * to be ok with the extra byte even though Windows doesn't send writes that
- * are that large.
- *
- * Citation:
- *
- * http://blogs.msdn.com/b/openspecification/archive/2009/04/10/smb-maximum-transmit-buffer-size-and-performance-tuning.aspx
- */
-#define CIFS_DEFAULT_NON_POSIX_RSIZE (60 * 1024)
-#define CIFS_DEFAULT_NON_POSIX_WSIZE (65536)
-
-/*
- * On hosts with high memory, we can't currently support wsize/rsize that are
- * larger than we can kmap at once. Cap the rsize/wsize at
- * LAST_PKMAP * PAGE_SIZE. We'll never be able to fill a read or write request
- * larger than that anyway.
- */
-#ifdef CONFIG_HIGHMEM
-#define CIFS_KMAP_SIZE_LIMIT (LAST_PKMAP * PAGE_CACHE_SIZE)
-#else /* CONFIG_HIGHMEM */
-#define CIFS_KMAP_SIZE_LIMIT (1<<24)
-#endif /* CONFIG_HIGHMEM */
-
-static unsigned int
-cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info)
-{
- __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
- struct TCP_Server_Info *server = tcon->ses->server;
- unsigned int wsize;
-
- /* start with specified wsize, or default */
- if (pvolume_info->wsize)
- wsize = pvolume_info->wsize;
- else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
- wsize = CIFS_DEFAULT_IOSIZE;
- else
- wsize = CIFS_DEFAULT_NON_POSIX_WSIZE;
-
- /* can server support 24-bit write sizes? (via UNIX extensions) */
- if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
- wsize = min_t(unsigned int, wsize, CIFS_MAX_RFC1002_WSIZE);
-
- /*
- * no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set?
- * Limit it to max buffer offered by the server, minus the size of the
- * WRITEX header, not including the 4 byte RFC1001 length.
- */
- if (!(server->capabilities & CAP_LARGE_WRITE_X) ||
- (!(server->capabilities & CAP_UNIX) &&
- (server->sec_mode & (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED))))
- wsize = min_t(unsigned int, wsize,
- server->maxBuf - sizeof(WRITE_REQ) + 4);
-
- /* limit to the amount that we can kmap at once */
- wsize = min_t(unsigned int, wsize, CIFS_KMAP_SIZE_LIMIT);
-
- /* hard limit of CIFS_MAX_WSIZE */
- wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE);
-
- return wsize;
-}
-
-static unsigned int
-cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *pvolume_info)
-{
- __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
- struct TCP_Server_Info *server = tcon->ses->server;
- unsigned int rsize, defsize;
-
- /*
- * Set default value...
- *
- * HACK alert! Ancient servers have very small buffers. Even though
- * MS-CIFS indicates that servers are only limited by the client's
- * bufsize for reads, testing against win98se shows that it throws
- * INVALID_PARAMETER errors if you try to request too large a read.
- * OS/2 just sends back short reads.
- *
- * If the server doesn't advertise CAP_LARGE_READ_X, then assume that
- * it can't handle a read request larger than its MaxBufferSize either.
- */
- if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP))
- defsize = CIFS_DEFAULT_IOSIZE;
- else if (server->capabilities & CAP_LARGE_READ_X)
- defsize = CIFS_DEFAULT_NON_POSIX_RSIZE;
- else
- defsize = server->maxBuf - sizeof(READ_RSP);
-
- rsize = pvolume_info->rsize ? pvolume_info->rsize : defsize;
-
- /*
- * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to
- * the client's MaxBufferSize.
- */
- if (!(server->capabilities & CAP_LARGE_READ_X))
- rsize = min_t(unsigned int, CIFSMaxBufSize, rsize);
-
- /* limit to the amount that we can kmap at once */
- rsize = min_t(unsigned int, rsize, CIFS_KMAP_SIZE_LIMIT);
-
- /* hard limit of CIFS_MAX_RSIZE */
- rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE);
-
- return rsize;
-}
-
static void
cleanup_volume_info_contents(struct smb_vol *volume_info)
{
@@ -3651,8 +3497,8 @@ try_mount_again:
if (!tcon->ipc && server->ops->qfs_tcon)
server->ops->qfs_tcon(xid, tcon);
- cifs_sb->wsize = cifs_negotiate_wsize(tcon, volume_info);
- cifs_sb->rsize = cifs_negotiate_rsize(tcon, volume_info);
+ cifs_sb->wsize = server->ops->negotiate_wsize(tcon, volume_info);
+ cifs_sb->rsize = server->ops->negotiate_rsize(tcon, volume_info);
/* tune readahead according to rsize */
cifs_sb->bdi.ra_pages = cifs_sb->rsize / PAGE_CACHE_SIZE;
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 781025be48bc..7c0a81283645 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -160,17 +160,18 @@ check_name(struct dentry *direntry)
static int
cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
struct tcon_link *tlink, unsigned oflags, umode_t mode,
- __u32 *oplock, __u16 *fileHandle, int *created)
+ __u32 *oplock, struct cifs_fid *fid, int *created)
{
int rc = -ENOENT;
int create_options = CREATE_NOT_DIR;
- int desiredAccess;
+ int desired_access;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifs_tcon *tcon = tlink_tcon(tlink);
char *full_path = NULL;
FILE_ALL_INFO *buf = NULL;
struct inode *newinode = NULL;
int disposition;
+ struct TCP_Server_Info *server = tcon->ses->server;
*oplock = 0;
if (tcon->ses->server->oplocks)
@@ -185,8 +186,8 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open &&
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
- rc = cifs_posix_open(full_path, &newinode,
- inode->i_sb, mode, oflags, oplock, fileHandle, xid);
+ rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode,
+ oflags, oplock, &fid->netfid, xid);
switch (rc) {
case 0:
if (newinode == NULL) {
@@ -202,7 +203,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
* close it and proceed as if it were a normal
* lookup.
*/
- CIFSSMBClose(xid, tcon, *fileHandle);
+ CIFSSMBClose(xid, tcon, fid->netfid);
goto cifs_create_get_file_info;
}
/* success, no need to query */
@@ -244,11 +245,11 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
*/
}
- desiredAccess = 0;
+ desired_access = 0;
if (OPEN_FMODE(oflags) & FMODE_READ)
- desiredAccess |= GENERIC_READ; /* is this too little? */
+ desired_access |= GENERIC_READ; /* is this too little? */
if (OPEN_FMODE(oflags) & FMODE_WRITE)
- desiredAccess |= GENERIC_WRITE;
+ desired_access |= GENERIC_WRITE;
disposition = FILE_OVERWRITE_IF;
if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
@@ -260,8 +261,15 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
else
cFYI(1, "Create flag not set in create function");
- /* BB add processing to set equivalent of mode - e.g. via CreateX with
- ACLs */
+ /*
+ * BB add processing to set equivalent of mode - e.g. via CreateX with
+ * ACLs
+ */
+
+ if (!server->ops->open) {
+ rc = -ENOSYS;
+ goto out;
+ }
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
if (buf == NULL) {
@@ -279,28 +287,18 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
- if (tcon->ses->capabilities & CAP_NT_SMBS)
- rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
- desiredAccess, create_options,
- fileHandle, oplock, buf, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
- else
- rc = -EIO; /* no NT SMB support fall into legacy open below */
-
- if (rc == -EIO) {
- /* old server, retry the open legacy style */
- rc = SMBLegacyOpen(xid, tcon, full_path, disposition,
- desiredAccess, create_options,
- fileHandle, oplock, buf, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
- }
+ rc = server->ops->open(xid, tcon, full_path, disposition,
+ desired_access, create_options, fid, oplock,
+ buf, cifs_sb);
if (rc) {
cFYI(1, "cifs_create returned 0x%x", rc);
goto out;
}
- /* If Open reported that we actually created a file
- then we now have to set the mode if possible */
+ /*
+ * If Open reported that we actually created a file then we now have to
+ * set the mode if possible.
+ */
if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) {
struct cifs_unix_set_info_args args = {
.mode = mode,
@@ -321,11 +319,13 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
args.uid = NO_CHANGE_64;
args.gid = NO_CHANGE_64;
}
- CIFSSMBUnixSetFileInfo(xid, tcon, &args, *fileHandle,
- current->tgid);
+ CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid,
+ current->tgid);
} else {
- /* BB implement mode setting via Windows security
- descriptors e.g. */
+ /*
+ * BB implement mode setting via Windows security
+ * descriptors e.g.
+ */
/* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
/* Could set r/o dos attribute if mode & 0222 == 0 */
@@ -334,12 +334,14 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
cifs_create_get_file_info:
/* server might mask mode so we have to query for it */
if (tcon->unix_ext)
- rc = cifs_get_inode_info_unix(&newinode, full_path,
- inode->i_sb, xid);
+ rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb,
+ xid);
else {
- rc = cifs_get_inode_info(&newinode, full_path, buf,
- inode->i_sb, xid, fileHandle);
+ rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb,
+ xid, &fid->netfid);
if (newinode) {
+ if (server->ops->set_lease_key)
+ server->ops->set_lease_key(newinode, fid);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
newinode->i_mode = mode;
if ((*oplock & CIFS_CREATE_ACTION) &&
@@ -356,7 +358,8 @@ cifs_create_get_file_info:
cifs_create_set_dentry:
if (rc != 0) {
cFYI(1, "Create worked, get_inode_info failed rc = %d", rc);
- CIFSSMBClose(xid, tcon, *fileHandle);
+ if (server->ops->close)
+ server->ops->close(xid, tcon, fid);
goto out;
}
d_drop(direntry);
@@ -377,11 +380,14 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
unsigned int xid;
struct tcon_link *tlink;
struct cifs_tcon *tcon;
- __u16 fileHandle;
+ struct TCP_Server_Info *server;
+ struct cifs_fid fid;
+ struct cifs_pending_open open;
__u32 oplock;
- struct cifsFileInfo *pfile_info;
+ struct cifsFileInfo *file_info;
- /* Posix open is only called (at lookup time) for file create now. For
+ /*
+ * Posix open is only called (at lookup time) for file create now. For
* opens (rather than creates), because we do not know if it is a file
* or directory yet, and current Samba no longer allows us to do posix
* open on dirs, we could end up wasting an open call on what turns out
@@ -413,22 +419,34 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,
goto out_free_xid;
tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ if (server->ops->new_lease_key)
+ server->ops->new_lease_key(&fid);
+
+ cifs_add_pending_open(&fid, tlink, &open);
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
- &oplock, &fileHandle, opened);
+ &oplock, &fid, opened);
- if (rc)
+ if (rc) {
+ cifs_del_pending_open(&open);
goto out;
+ }
rc = finish_open(file, direntry, generic_file_open, opened);
if (rc) {
- CIFSSMBClose(xid, tcon, fileHandle);
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &fid);
+ cifs_del_pending_open(&open);
goto out;
}
- pfile_info = cifs_new_fileinfo(fileHandle, file, tlink, oplock);
- if (pfile_info == NULL) {
- CIFSSMBClose(xid, tcon, fileHandle);
+ file_info = cifs_new_fileinfo(&fid, file, tlink, oplock);
+ if (file_info == NULL) {
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &fid);
+ cifs_del_pending_open(&open);
rc = -ENOMEM;
}
@@ -453,7 +471,9 @@ int cifs_create(struct inode *inode, struct dentry *direntry, umode_t mode,
*/
unsigned oflags = O_EXCL | O_CREAT | O_RDWR;
struct tcon_link *tlink;
- __u16 fileHandle;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ struct cifs_fid fid;
__u32 oplock;
int created = FILE_CREATED;
@@ -465,10 +485,16 @@ int cifs_create(struct inode *inode, struct dentry *direntry, umode_t mode,
if (IS_ERR(tlink))
goto out_free_xid;
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ if (server->ops->new_lease_key)
+ server->ops->new_lease_key(&fid);
+
rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode,
- &oplock, &fileHandle, &created);
- if (!rc)
- CIFSSMBClose(xid, tlink_tcon(tlink), fileHandle);
+ &oplock, &fid, &created);
+ if (!rc && server->ops->close)
+ server->ops->close(xid, tcon, &fid);
cifs_put_tlink(tlink);
out_free_xid:
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 71e9ad9f5961..7d7bbdc4c8e7 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -169,16 +169,20 @@ posix_open_ret:
static int
cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
- struct cifs_tcon *tcon, unsigned int f_flags, __u32 *poplock,
- __u16 *pnetfid, unsigned int xid)
+ struct cifs_tcon *tcon, unsigned int f_flags, __u32 *oplock,
+ struct cifs_fid *fid, unsigned int xid)
{
int rc;
- int desiredAccess;
+ int desired_access;
int disposition;
int create_options = CREATE_NOT_DIR;
FILE_ALL_INFO *buf;
+ struct TCP_Server_Info *server = tcon->ses->server;
+
+ if (!server->ops->open)
+ return -ENOSYS;
- desiredAccess = cifs_convert_flags(f_flags);
+ desired_access = cifs_convert_flags(f_flags);
/*********************************************************************
* open flag mapping table:
@@ -215,16 +219,9 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
- if (tcon->ses->capabilities & CAP_NT_SMBS)
- rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
- desiredAccess, create_options, pnetfid, poplock, buf,
- cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
- & CIFS_MOUNT_MAP_SPECIAL_CHR);
- else
- rc = SMBLegacyOpen(xid, tcon, full_path, disposition,
- desiredAccess, CREATE_NOT_DIR, pnetfid, poplock, buf,
- cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
- & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ rc = server->ops->open(xid, tcon, full_path, disposition,
+ desired_access, create_options, fid, oplock, buf,
+ cifs_sb);
if (rc)
goto out;
@@ -234,7 +231,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
xid);
else
rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb,
- xid, pnetfid);
+ xid, &fid->netfid);
out:
kfree(buf);
@@ -242,48 +239,62 @@ out:
}
struct cifsFileInfo *
-cifs_new_fileinfo(__u16 fileHandle, struct file *file,
+cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
struct tcon_link *tlink, __u32 oplock)
{
struct dentry *dentry = file->f_path.dentry;
struct inode *inode = dentry->d_inode;
- struct cifsInodeInfo *pCifsInode = CIFS_I(inode);
- struct cifsFileInfo *pCifsFile;
-
- pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
- if (pCifsFile == NULL)
- return pCifsFile;
-
- pCifsFile->count = 1;
- pCifsFile->netfid = fileHandle;
- pCifsFile->pid = current->tgid;
- pCifsFile->uid = current_fsuid();
- pCifsFile->dentry = dget(dentry);
- pCifsFile->f_flags = file->f_flags;
- pCifsFile->invalidHandle = false;
- pCifsFile->tlink = cifs_get_tlink(tlink);
- mutex_init(&pCifsFile->fh_mutex);
- INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
- INIT_LIST_HEAD(&pCifsFile->llist);
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct cifsFileInfo *cfile;
+ struct cifs_fid_locks *fdlocks;
+ struct cifs_tcon *tcon = tlink_tcon(tlink);
+
+ cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
+ if (cfile == NULL)
+ return cfile;
+
+ fdlocks = kzalloc(sizeof(struct cifs_fid_locks), GFP_KERNEL);
+ if (!fdlocks) {
+ kfree(cfile);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&fdlocks->locks);
+ fdlocks->cfile = cfile;
+ cfile->llist = fdlocks;
+ down_write(&cinode->lock_sem);
+ list_add(&fdlocks->llist, &cinode->llist);
+ up_write(&cinode->lock_sem);
+
+ cfile->count = 1;
+ cfile->pid = current->tgid;
+ cfile->uid = current_fsuid();
+ cfile->dentry = dget(dentry);
+ cfile->f_flags = file->f_flags;
+ cfile->invalidHandle = false;
+ cfile->tlink = cifs_get_tlink(tlink);
+ INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
+ mutex_init(&cfile->fh_mutex);
spin_lock(&cifs_file_list_lock);
- list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList));
+ if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE)
+ oplock = fid->pending_open->oplock;
+ list_del(&fid->pending_open->olist);
+
+ tlink_tcon(tlink)->ses->server->ops->set_fid(cfile, fid, oplock);
+
+ list_add(&cfile->tlist, &tcon->openFileList);
/* if readable file instance put first in list*/
if (file->f_mode & FMODE_READ)
- list_add(&pCifsFile->flist, &pCifsInode->openFileList);
+ list_add(&cfile->flist, &cinode->openFileList);
else
- list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList);
+ list_add_tail(&cfile->flist, &cinode->openFileList);
spin_unlock(&cifs_file_list_lock);
- cifs_set_oplock_level(pCifsInode, oplock);
- pCifsInode->can_cache_brlcks = pCifsInode->clientCanCacheAll;
-
- file->private_data = pCifsFile;
- return pCifsFile;
+ file->private_data = cfile;
+ return cfile;
}
-static void cifs_del_lock_waiters(struct cifsLockInfo *lock);
-
struct cifsFileInfo *
cifsFileInfo_get(struct cifsFileInfo *cifs_file)
{
@@ -302,9 +313,12 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
{
struct inode *inode = cifs_file->dentry->d_inode;
struct cifs_tcon *tcon = tlink_tcon(cifs_file->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsLockInfo *li, *tmp;
+ struct cifs_fid fid;
+ struct cifs_pending_open open;
spin_lock(&cifs_file_list_lock);
if (--cifs_file->count > 0) {
@@ -312,6 +326,12 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
return;
}
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &fid);
+
+ /* store open in pending opens to make sure we don't miss lease break */
+ cifs_add_pending_open_locked(&fid, cifs_file->tlink, &open);
+
/* remove it from the lists */
list_del(&cifs_file->flist);
list_del(&cifs_file->tlist);
@@ -319,13 +339,13 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
if (list_empty(&cifsi->openFileList)) {
cFYI(1, "closing last open instance for inode %p",
cifs_file->dentry->d_inode);
-
- /* in strict cache mode we need invalidate mapping on the last
- close because it may cause a error when we open this file
- again and get at least level II oplock */
+ /*
+ * In strict cache mode we need invalidate mapping on the last
+ * close because it may cause a error when we open this file
+ * again and get at least level II oplock.
+ */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO)
CIFS_I(inode)->invalid_mapping = true;
-
cifs_set_oplock_level(cifsi, 0);
}
spin_unlock(&cifs_file_list_lock);
@@ -333,23 +353,30 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
cancel_work_sync(&cifs_file->oplock_break);
if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
+ struct TCP_Server_Info *server = tcon->ses->server;
unsigned int xid;
- int rc;
+
xid = get_xid();
- rc = CIFSSMBClose(xid, tcon, cifs_file->netfid);
- free_xid(xid);
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &cifs_file->fid);
+ _free_xid(xid);
}
- /* Delete any outstanding lock records. We'll lose them when the file
+ cifs_del_pending_open(&open);
+
+ /*
+ * Delete any outstanding lock records. We'll lose them when the file
* is closed anyway.
*/
- mutex_lock(&cifsi->lock_mutex);
- list_for_each_entry_safe(li, tmp, &cifs_file->llist, llist) {
+ down_write(&cifsi->lock_sem);
+ list_for_each_entry_safe(li, tmp, &cifs_file->llist->locks, llist) {
list_del(&li->llist);
cifs_del_lock_waiters(li);
kfree(li);
}
- mutex_unlock(&cifsi->lock_mutex);
+ list_del(&cifs_file->llist->llist);
+ kfree(cifs_file->llist);
+ up_write(&cifsi->lock_sem);
cifs_put_tlink(cifs_file->tlink);
dput(cifs_file->dentry);
@@ -357,17 +384,20 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
}
int cifs_open(struct inode *inode, struct file *file)
+
{
int rc = -EACCES;
unsigned int xid;
__u32 oplock;
struct cifs_sb_info *cifs_sb;
+ struct TCP_Server_Info *server;
struct cifs_tcon *tcon;
struct tcon_link *tlink;
- struct cifsFileInfo *pCifsFile = NULL;
+ struct cifsFileInfo *cfile = NULL;
char *full_path = NULL;
bool posix_open_ok = false;
- __u16 netfid;
+ struct cifs_fid fid;
+ struct cifs_pending_open open;
xid = get_xid();
@@ -378,6 +408,7 @@ int cifs_open(struct inode *inode, struct file *file)
return PTR_ERR(tlink);
}
tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
full_path = build_path_from_dentry(file->f_path.dentry);
if (full_path == NULL) {
@@ -388,7 +419,7 @@ int cifs_open(struct inode *inode, struct file *file)
cFYI(1, "inode = 0x%p file flags are 0x%x for %s",
inode, file->f_flags, full_path);
- if (tcon->ses->server->oplocks)
+ if (server->oplocks)
oplock = REQ_OPLOCK;
else
oplock = 0;
@@ -399,7 +430,7 @@ int cifs_open(struct inode *inode, struct file *file)
/* can not refresh inode info since size could be stale */
rc = cifs_posix_open(full_path, &inode, inode->i_sb,
cifs_sb->mnt_file_mode /* ignored */,
- file->f_flags, &oplock, &netfid, xid);
+ file->f_flags, &oplock, &fid.netfid, xid);
if (rc == 0) {
cFYI(1, "posix open succeeded");
posix_open_ok = true;
@@ -415,20 +446,34 @@ int cifs_open(struct inode *inode, struct file *file)
} else if ((rc != -EIO) && (rc != -EREMOTE) &&
(rc != -EOPNOTSUPP)) /* path not found or net err */
goto out;
- /* else fallthrough to retry open the old way on network i/o
- or DFS errors */
+ /*
+ * Else fallthrough to retry open the old way on network i/o
+ * or DFS errors.
+ */
}
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &fid);
+
+ cifs_add_pending_open(&fid, tlink, &open);
+
if (!posix_open_ok) {
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &fid);
+
rc = cifs_nt_open(full_path, inode, cifs_sb, tcon,
- file->f_flags, &oplock, &netfid, xid);
- if (rc)
+ file->f_flags, &oplock, &fid, xid);
+ if (rc) {
+ cifs_del_pending_open(&open);
goto out;
+ }
}
- pCifsFile = cifs_new_fileinfo(netfid, file, tlink, oplock);
- if (pCifsFile == NULL) {
- CIFSSMBClose(xid, tcon, netfid);
+ cfile = cifs_new_fileinfo(&fid, file, tlink, oplock);
+ if (cfile == NULL) {
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &fid);
+ cifs_del_pending_open(&open);
rc = -ENOMEM;
goto out;
}
@@ -436,8 +481,10 @@ int cifs_open(struct inode *inode, struct file *file)
cifs_fscache_set_inode_cookie(inode, file);
if ((oplock & CIFS_CREATE_ACTION) && !posix_open_ok && tcon->unix_ext) {
- /* time to set mode which we can not set earlier due to
- problems creating new read-only files */
+ /*
+ * Time to set mode which we can not set earlier due to
+ * problems creating new read-only files.
+ */
struct cifs_unix_set_info_args args = {
.mode = inode->i_mode,
.uid = NO_CHANGE_64,
@@ -447,8 +494,8 @@ int cifs_open(struct inode *inode, struct file *file)
.mtime = NO_CHANGE_64,
.device = 0,
};
- CIFSSMBUnixSetFileInfo(xid, tcon, &args, netfid,
- pCifsFile->pid);
+ CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid.netfid,
+ cfile->pid);
}
out:
@@ -458,59 +505,66 @@ out:
return rc;
}
-/* Try to reacquire byte range locks that were released when session */
-/* to server was lost */
+/*
+ * Try to reacquire byte range locks that were released when session
+ * to server was lost
+ */
static int cifs_relock_file(struct cifsFileInfo *cifsFile)
{
int rc = 0;
-/* BB list all locks open on this file and relock */
+ /* BB list all locks open on this file and relock */
return rc;
}
-static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
+static int
+cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
{
int rc = -EACCES;
unsigned int xid;
__u32 oplock;
struct cifs_sb_info *cifs_sb;
struct cifs_tcon *tcon;
- struct cifsInodeInfo *pCifsInode;
+ struct TCP_Server_Info *server;
+ struct cifsInodeInfo *cinode;
struct inode *inode;
char *full_path = NULL;
- int desiredAccess;
+ int desired_access;
int disposition = FILE_OPEN;
int create_options = CREATE_NOT_DIR;
- __u16 netfid;
+ struct cifs_fid fid;
xid = get_xid();
- mutex_lock(&pCifsFile->fh_mutex);
- if (!pCifsFile->invalidHandle) {
- mutex_unlock(&pCifsFile->fh_mutex);
+ mutex_lock(&cfile->fh_mutex);
+ if (!cfile->invalidHandle) {
+ mutex_unlock(&cfile->fh_mutex);
rc = 0;
free_xid(xid);
return rc;
}
- inode = pCifsFile->dentry->d_inode;
+ inode = cfile->dentry->d_inode;
cifs_sb = CIFS_SB(inode->i_sb);
- tcon = tlink_tcon(pCifsFile->tlink);
+ tcon = tlink_tcon(cfile->tlink);
+ server = tcon->ses->server;
-/* can not grab rename sem here because various ops, including
- those that already have the rename sem can end up causing writepage
- to get called and if the server was down that means we end up here,
- and we can never tell if the caller already has the rename_sem */
- full_path = build_path_from_dentry(pCifsFile->dentry);
+ /*
+ * Can not grab rename sem here because various ops, including those
+ * that already have the rename sem can end up causing writepage to get
+ * called and if the server was down that means we end up here, and we
+ * can never tell if the caller already has the rename_sem.
+ */
+ full_path = build_path_from_dentry(cfile->dentry);
if (full_path == NULL) {
rc = -ENOMEM;
- mutex_unlock(&pCifsFile->fh_mutex);
+ mutex_unlock(&cfile->fh_mutex);
free_xid(xid);
return rc;
}
- cFYI(1, "inode = 0x%p file flags 0x%x for %s",
- inode, pCifsFile->f_flags, full_path);
+ cFYI(1, "inode = 0x%p file flags 0x%x for %s", inode, cfile->f_flags,
+ full_path);
if (tcon->ses->server->oplocks)
oplock = REQ_OPLOCK;
@@ -524,69 +578,72 @@ static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
* O_CREAT, O_EXCL and O_TRUNC already had their effect on the
* original open. Must mask them off for a reopen.
*/
- unsigned int oflags = pCifsFile->f_flags &
+ unsigned int oflags = cfile->f_flags &
~(O_CREAT | O_EXCL | O_TRUNC);
rc = cifs_posix_open(full_path, NULL, inode->i_sb,
- cifs_sb->mnt_file_mode /* ignored */,
- oflags, &oplock, &netfid, xid);
+ cifs_sb->mnt_file_mode /* ignored */,
+ oflags, &oplock, &fid.netfid, xid);
if (rc == 0) {
cFYI(1, "posix reopen succeeded");
goto reopen_success;
}
- /* fallthrough to retry open the old way on errors, especially
- in the reconnect path it is important to retry hard */
+ /*
+ * fallthrough to retry open the old way on errors, especially
+ * in the reconnect path it is important to retry hard
+ */
}
- desiredAccess = cifs_convert_flags(pCifsFile->f_flags);
+ desired_access = cifs_convert_flags(cfile->f_flags);
if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT;
- /* Can not refresh inode by passing in file_info buf to be returned
- by SMBOpen and then calling get_inode_info with returned buf
- since file might have write behind data that needs to be flushed
- and server version of file size can be stale. If we knew for sure
- that inode was not dirty locally we could do this */
+ if (server->ops->get_lease_key)
+ server->ops->get_lease_key(inode, &fid);
- rc = CIFSSMBOpen(xid, tcon, full_path, disposition, desiredAccess,
- create_options, &netfid, &oplock, NULL,
- cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ /*
+ * Can not refresh inode by passing in file_info buf to be returned by
+ * CIFSSMBOpen and then calling get_inode_info with returned buf since
+ * file might have write behind data that needs to be flushed and server
+ * version of file size can be stale. If we knew for sure that inode was
+ * not dirty locally we could do this.
+ */
+ rc = server->ops->open(xid, tcon, full_path, disposition,
+ desired_access, create_options, &fid, &oplock,
+ NULL, cifs_sb);
if (rc) {
- mutex_unlock(&pCifsFile->fh_mutex);
- cFYI(1, "cifs_open returned 0x%x", rc);
+ mutex_unlock(&cfile->fh_mutex);
+ cFYI(1, "cifs_reopen returned 0x%x", rc);
cFYI(1, "oplock: %d", oplock);
goto reopen_error_exit;
}
reopen_success:
- pCifsFile->netfid = netfid;
- pCifsFile->invalidHandle = false;
- mutex_unlock(&pCifsFile->fh_mutex);
- pCifsInode = CIFS_I(inode);
+ cfile->invalidHandle = false;
+ mutex_unlock(&cfile->fh_mutex);
+ cinode = CIFS_I(inode);
if (can_flush) {
rc = filemap_write_and_wait(inode->i_mapping);
mapping_set_error(inode->i_mapping, rc);
if (tcon->unix_ext)
- rc = cifs_get_inode_info_unix(&inode,
- full_path, inode->i_sb, xid);
+ rc = cifs_get_inode_info_unix(&inode, full_path,
+ inode->i_sb, xid);
else
- rc = cifs_get_inode_info(&inode,
- full_path, NULL, inode->i_sb,
- xid, NULL);
- } /* else we are writing out data to server already
- and could deadlock if we tried to flush data, and
- since we do not know if we have data that would
- invalidate the current end of file on the server
- we can not go to the server to get the new inod
- info */
-
- cifs_set_oplock_level(pCifsInode, oplock);
+ rc = cifs_get_inode_info(&inode, full_path, NULL,
+ inode->i_sb, xid, NULL);
+ }
+ /*
+ * Else we are writing out data to server already and could deadlock if
+ * we tried to flush data, and since we do not know if we have data that
+ * would invalidate the current end of file on the server we can not go
+ * to the server to get the new inode info.
+ */
- cifs_relock_file(pCifsFile);
+ server->ops->set_fid(cfile, &fid, oplock);
+ cifs_relock_file(cfile);
reopen_error_exit:
kfree(full_path);
@@ -609,42 +666,48 @@ int cifs_closedir(struct inode *inode, struct file *file)
{
int rc = 0;
unsigned int xid;
- struct cifsFileInfo *pCFileStruct = file->private_data;
- char *ptmp;
+ struct cifsFileInfo *cfile = file->private_data;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
+ char *buf;
cFYI(1, "Closedir inode = 0x%p", inode);
+ if (cfile == NULL)
+ return rc;
+
xid = get_xid();
+ tcon = tlink_tcon(cfile->tlink);
+ server = tcon->ses->server;
- if (pCFileStruct) {
- struct cifs_tcon *pTcon = tlink_tcon(pCFileStruct->tlink);
+ cFYI(1, "Freeing private data in close dir");
+ spin_lock(&cifs_file_list_lock);
+ if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) {
+ cfile->invalidHandle = true;
+ spin_unlock(&cifs_file_list_lock);
+ if (server->ops->close_dir)
+ rc = server->ops->close_dir(xid, tcon, &cfile->fid);
+ else
+ rc = -ENOSYS;
+ cFYI(1, "Closing uncompleted readdir with rc %d", rc);
+ /* not much we can do if it fails anyway, ignore rc */
+ rc = 0;
+ } else
+ spin_unlock(&cifs_file_list_lock);
- cFYI(1, "Freeing private data in close dir");
- spin_lock(&cifs_file_list_lock);
- if (!pCFileStruct->srch_inf.endOfSearch &&
- !pCFileStruct->invalidHandle) {
- pCFileStruct->invalidHandle = true;
- spin_unlock(&cifs_file_list_lock);
- rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid);
- cFYI(1, "Closing uncompleted readdir with rc %d",
- rc);
- /* not much we can do if it fails anyway, ignore rc */
- rc = 0;
- } else
- spin_unlock(&cifs_file_list_lock);
- ptmp = pCFileStruct->srch_inf.ntwrk_buf_start;
- if (ptmp) {
- cFYI(1, "closedir free smb buf in srch struct");
- pCFileStruct->srch_inf.ntwrk_buf_start = NULL;
- if (pCFileStruct->srch_inf.smallBuf)
- cifs_small_buf_release(ptmp);
- else
- cifs_buf_release(ptmp);
- }
- cifs_put_tlink(pCFileStruct->tlink);
- kfree(file->private_data);
- file->private_data = NULL;
+ buf = cfile->srch_inf.ntwrk_buf_start;
+ if (buf) {
+ cFYI(1, "closedir free smb buf in srch struct");
+ cfile->srch_inf.ntwrk_buf_start = NULL;
+ if (cfile->srch_inf.smallBuf)
+ cifs_small_buf_release(buf);
+ else
+ cifs_buf_release(buf);
}
+
+ cifs_put_tlink(cfile->tlink);
+ kfree(file->private_data);
+ file->private_data = NULL;
/* BB can we lock the filestruct while this is going on? */
free_xid(xid);
return rc;
@@ -666,7 +729,7 @@ cifs_lock_init(__u64 offset, __u64 length, __u8 type)
return lock;
}
-static void
+void
cifs_del_lock_waiters(struct cifsLockInfo *lock)
{
struct cifsLockInfo *li, *tmp;
@@ -677,45 +740,47 @@ cifs_del_lock_waiters(struct cifsLockInfo *lock)
}
static bool
-cifs_find_fid_lock_conflict(struct cifsFileInfo *cfile, __u64 offset,
- __u64 length, __u8 type, struct cifsFileInfo *cur,
- struct cifsLockInfo **conf_lock)
+cifs_find_fid_lock_conflict(struct cifs_fid_locks *fdlocks, __u64 offset,
+ __u64 length, __u8 type, struct cifsFileInfo *cfile,
+ struct cifsLockInfo **conf_lock, bool rw_check)
{
struct cifsLockInfo *li;
+ struct cifsFileInfo *cur_cfile = fdlocks->cfile;
struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
- list_for_each_entry(li, &cfile->llist, llist) {
+ list_for_each_entry(li, &fdlocks->locks, llist) {
if (offset + length <= li->offset ||
offset >= li->offset + li->length)
continue;
- else if ((type & server->vals->shared_lock_type) &&
- ((server->ops->compare_fids(cur, cfile) &&
- current->tgid == li->pid) || type == li->type))
+ if (rw_check && server->ops->compare_fids(cfile, cur_cfile) &&
+ current->tgid == li->pid)
continue;
- else {
+ if ((type & server->vals->shared_lock_type) &&
+ ((server->ops->compare_fids(cfile, cur_cfile) &&
+ current->tgid == li->pid) || type == li->type))
+ continue;
+ if (conf_lock)
*conf_lock = li;
- return true;
- }
+ return true;
}
return false;
}
-static bool
+bool
cifs_find_lock_conflict(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
- __u8 type, struct cifsLockInfo **conf_lock)
+ __u8 type, struct cifsLockInfo **conf_lock,
+ bool rw_check)
{
bool rc = false;
- struct cifsFileInfo *fid, *tmp;
+ struct cifs_fid_locks *cur;
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
- spin_lock(&cifs_file_list_lock);
- list_for_each_entry_safe(fid, tmp, &cinode->openFileList, flist) {
- rc = cifs_find_fid_lock_conflict(fid, offset, length, type,
- cfile, conf_lock);
+ list_for_each_entry(cur, &cinode->llist, llist) {
+ rc = cifs_find_fid_lock_conflict(cur, offset, length, type,
+ cfile, conf_lock, rw_check);
if (rc)
break;
}
- spin_unlock(&cifs_file_list_lock);
return rc;
}
@@ -737,10 +802,10 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
bool exist;
- mutex_lock(&cinode->lock_mutex);
+ down_read(&cinode->lock_sem);
exist = cifs_find_lock_conflict(cfile, offset, length, type,
- &conf_lock);
+ &conf_lock, false);
if (exist) {
flock->fl_start = conf_lock->offset;
flock->fl_end = conf_lock->offset + conf_lock->length - 1;
@@ -754,7 +819,7 @@ cifs_lock_test(struct cifsFileInfo *cfile, __u64 offset, __u64 length,
else
flock->fl_type = F_UNLCK;
- mutex_unlock(&cinode->lock_mutex);
+ up_read(&cinode->lock_sem);
return rc;
}
@@ -762,9 +827,9 @@ static void
cifs_lock_add(struct cifsFileInfo *cfile, struct cifsLockInfo *lock)
{
struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
- mutex_lock(&cinode->lock_mutex);
- list_add_tail(&lock->llist, &cfile->llist);
- mutex_unlock(&cinode->lock_mutex);
+ down_write(&cinode->lock_sem);
+ list_add_tail(&lock->llist, &cfile->llist->locks);
+ up_write(&cinode->lock_sem);
}
/*
@@ -784,13 +849,13 @@ cifs_lock_add_if(struct cifsFileInfo *cfile, struct cifsLockInfo *lock,
try_again:
exist = false;
- mutex_lock(&cinode->lock_mutex);
+ down_write(&cinode->lock_sem);
exist = cifs_find_lock_conflict(cfile, lock->offset, lock->length,
- lock->type, &conf_lock);
+ lock->type, &conf_lock, false);
if (!exist && cinode->can_cache_brlcks) {
- list_add_tail(&lock->llist, &cfile->llist);
- mutex_unlock(&cinode->lock_mutex);
+ list_add_tail(&lock->llist, &cfile->llist->locks);
+ up_write(&cinode->lock_sem);
return rc;
}
@@ -800,17 +865,17 @@ try_again:
rc = -EACCES;
else {
list_add_tail(&lock->blist, &conf_lock->blist);
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
rc = wait_event_interruptible(lock->block_q,
(lock->blist.prev == &lock->blist) &&
(lock->blist.next == &lock->blist));
if (!rc)
goto try_again;
- mutex_lock(&cinode->lock_mutex);
+ down_write(&cinode->lock_sem);
list_del_init(&lock->blist);
}
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
return rc;
}
@@ -831,7 +896,7 @@ cifs_posix_lock_test(struct file *file, struct file_lock *flock)
if ((flock->fl_flags & FL_POSIX) == 0)
return 1;
- mutex_lock(&cinode->lock_mutex);
+ down_read(&cinode->lock_sem);
posix_test_lock(file, flock);
if (flock->fl_type == F_UNLCK && !cinode->can_cache_brlcks) {
@@ -839,7 +904,7 @@ cifs_posix_lock_test(struct file *file, struct file_lock *flock)
rc = 1;
}
- mutex_unlock(&cinode->lock_mutex);
+ up_read(&cinode->lock_sem);
return rc;
}
@@ -859,14 +924,14 @@ cifs_posix_lock_set(struct file *file, struct file_lock *flock)
return rc;
try_again:
- mutex_lock(&cinode->lock_mutex);
+ down_write(&cinode->lock_sem);
if (!cinode->can_cache_brlcks) {
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
return rc;
}
rc = posix_lock_file(file, flock, NULL);
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
if (rc == FILE_LOCK_DEFERRED) {
rc = wait_event_interruptible(flock->fl_wait, !flock->fl_next);
if (!rc)
@@ -876,7 +941,7 @@ try_again:
return rc;
}
-static int
+int
cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
{
unsigned int xid;
@@ -893,9 +958,10 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
xid = get_xid();
tcon = tlink_tcon(cfile->tlink);
- mutex_lock(&cinode->lock_mutex);
+ /* we are going to update can_cache_brlcks here - need a write access */
+ down_write(&cinode->lock_sem);
if (!cinode->can_cache_brlcks) {
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
free_xid(xid);
return rc;
}
@@ -906,7 +972,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
*/
max_buf = tcon->ses->server->maxBuf;
if (!max_buf) {
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
free_xid(xid);
return -EINVAL;
}
@@ -915,7 +981,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
sizeof(LOCKING_ANDX_RANGE);
buf = kzalloc(max_num * sizeof(LOCKING_ANDX_RANGE), GFP_KERNEL);
if (!buf) {
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
free_xid(xid);
return -ENOMEM;
}
@@ -923,7 +989,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
for (i = 0; i < 2; i++) {
cur = buf;
num = 0;
- list_for_each_entry_safe(li, tmp, &cfile->llist, llist) {
+ list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
if (li->type != types[i])
continue;
cur->Pid = cpu_to_le16(li->pid);
@@ -932,7 +998,8 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
cur->OffsetLow = cpu_to_le32((u32)li->offset);
cur->OffsetHigh = cpu_to_le32((u32)(li->offset>>32));
if (++num == max_num) {
- stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
+ stored_rc = cifs_lockv(xid, tcon,
+ cfile->fid.netfid,
(__u8)li->type, 0, num,
buf);
if (stored_rc)
@@ -944,7 +1011,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
}
if (num) {
- stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
+ stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid,
(__u8)types[i], 0, num, buf);
if (stored_rc)
rc = stored_rc;
@@ -952,7 +1019,7 @@ cifs_push_mandatory_locks(struct cifsFileInfo *cfile)
}
cinode->can_cache_brlcks = false;
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
kfree(buf);
free_xid(xid);
@@ -987,9 +1054,10 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
xid = get_xid();
- mutex_lock(&cinode->lock_mutex);
+ /* we are going to update can_cache_brlcks here - need a write access */
+ down_write(&cinode->lock_sem);
if (!cinode->can_cache_brlcks) {
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
free_xid(xid);
return rc;
}
@@ -1005,7 +1073,7 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
/*
* Allocating count locks is enough because no FL_POSIX locks can be
- * added to the list while we are holding cinode->lock_mutex that
+ * added to the list while we are holding cinode->lock_sem that
* protects locking operations of this inode.
*/
for (; i < count; i++) {
@@ -1038,7 +1106,7 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
type = CIFS_WRLCK;
lck = list_entry(el, struct lock_to_push, llist);
lck->pid = flock->fl_pid;
- lck->netfid = cfile->netfid;
+ lck->netfid = cfile->fid.netfid;
lck->length = length;
lck->type = type;
lck->offset = flock->fl_start;
@@ -1060,7 +1128,7 @@ cifs_push_posix_locks(struct cifsFileInfo *cfile)
out:
cinode->can_cache_brlcks = false;
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
free_xid(xid);
return rc;
@@ -1083,7 +1151,7 @@ cifs_push_locks(struct cifsFileInfo *cfile)
((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
return cifs_push_posix_locks(cfile);
- return cifs_push_mandatory_locks(cfile);
+ return tcon->ses->server->ops->push_mand_locks(cfile);
}
static void
@@ -1104,7 +1172,8 @@ cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock,
if (flock->fl_flags & FL_LEASE)
cFYI(1, "Lease on file - not implemented yet");
if (flock->fl_flags &
- (~(FL_POSIX | FL_FLOCK | FL_SLEEP | FL_ACCESS | FL_LEASE)))
+ (~(FL_POSIX | FL_FLOCK | FL_SLEEP |
+ FL_ACCESS | FL_LEASE | FL_CLOSE)))
cFYI(1, "Unknown lock flags 0x%x", flock->fl_flags);
*type = server->vals->large_lock_type;
@@ -1134,15 +1203,6 @@ cifs_read_flock(struct file_lock *flock, __u32 *type, int *lock, int *unlock,
}
static int
-cifs_mandatory_lock(unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
- __u64 length, __u32 type, int lock, int unlock, bool wait)
-{
- return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->netfid,
- current->tgid, length, offset, unlock, lock,
- (__u8)type, wait, 0);
-}
-
-static int
cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
bool wait_flag, bool posix_lck, unsigned int xid)
{
@@ -1151,7 +1211,7 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
- __u16 netfid = cfile->netfid;
+ __u16 netfid = cfile->fid.netfid;
if (posix_lck) {
int posix_lock_type;
@@ -1175,11 +1235,11 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
return rc;
/* BB we could chain these into one lock request BB */
- rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length, type,
- 1, 0, false);
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, type,
+ 1, 0, false);
if (rc == 0) {
- rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length,
- type, 0, 1, false);
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
+ type, 0, 1, false);
flock->fl_type = F_UNLCK;
if (rc != 0)
cERROR(1, "Error unlocking previously locked "
@@ -1192,13 +1252,14 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
return 0;
}
- rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length,
- type | server->vals->shared_lock_type, 1, 0,
- false);
+ type &= ~server->vals->exclusive_lock_type;
+
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
+ type | server->vals->shared_lock_type,
+ 1, 0, false);
if (rc == 0) {
- rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length,
- type | server->vals->shared_lock_type,
- 0, 1, false);
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
+ type | server->vals->shared_lock_type, 0, 1, false);
flock->fl_type = F_RDLCK;
if (rc != 0)
cERROR(1, "Error unlocking previously locked "
@@ -1209,7 +1270,7 @@ cifs_getlk(struct file *file, struct file_lock *flock, __u32 type,
return 0;
}
-static void
+void
cifs_move_llist(struct list_head *source, struct list_head *dest)
{
struct list_head *li, *tmp;
@@ -1217,7 +1278,7 @@ cifs_move_llist(struct list_head *source, struct list_head *dest)
list_move(li, dest);
}
-static void
+void
cifs_free_llist(struct list_head *llist)
{
struct cifsLockInfo *li, *tmp;
@@ -1228,7 +1289,7 @@ cifs_free_llist(struct list_head *llist)
}
}
-static int
+int
cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
unsigned int xid)
{
@@ -1260,11 +1321,11 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
if (!buf)
return -ENOMEM;
- mutex_lock(&cinode->lock_mutex);
+ down_write(&cinode->lock_sem);
for (i = 0; i < 2; i++) {
cur = buf;
num = 0;
- list_for_each_entry_safe(li, tmp, &cfile->llist, llist) {
+ list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
if (flock->fl_start > li->offset ||
(flock->fl_start + length) <
(li->offset + li->length))
@@ -1295,7 +1356,8 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
*/
list_move(&li->llist, &tmp_llist);
if (++num == max_num) {
- stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
+ stored_rc = cifs_lockv(xid, tcon,
+ cfile->fid.netfid,
li->type, num, 0, buf);
if (stored_rc) {
/*
@@ -1304,7 +1366,7 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
* list to the head of the file's list.
*/
cifs_move_llist(&tmp_llist,
- &cfile->llist);
+ &cfile->llist->locks);
rc = stored_rc;
} else
/*
@@ -1318,23 +1380,24 @@ cifs_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
cur++;
}
if (num) {
- stored_rc = cifs_lockv(xid, tcon, cfile->netfid,
+ stored_rc = cifs_lockv(xid, tcon, cfile->fid.netfid,
types[i], num, 0, buf);
if (stored_rc) {
- cifs_move_llist(&tmp_llist, &cfile->llist);
+ cifs_move_llist(&tmp_llist,
+ &cfile->llist->locks);
rc = stored_rc;
} else
cifs_free_llist(&tmp_llist);
}
}
- mutex_unlock(&cinode->lock_mutex);
+ up_write(&cinode->lock_sem);
kfree(buf);
return rc;
}
static int
-cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
+cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
bool wait_flag, bool posix_lck, int lock, int unlock,
unsigned int xid)
{
@@ -1343,7 +1406,6 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
struct TCP_Server_Info *server = tcon->ses->server;
- __u16 netfid = cfile->netfid;
if (posix_lck) {
int posix_lock_type;
@@ -1360,9 +1422,9 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
if (unlock == 1)
posix_lock_type = CIFS_UNLCK;
- rc = CIFSSMBPosixLock(xid, tcon, netfid, current->tgid,
- flock->fl_start, length, NULL,
- posix_lock_type, wait_flag);
+ rc = CIFSSMBPosixLock(xid, tcon, cfile->fid.netfid,
+ current->tgid, flock->fl_start, length,
+ NULL, posix_lock_type, wait_flag);
goto out;
}
@@ -1379,8 +1441,8 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
if (rc <= 0)
goto out;
- rc = cifs_mandatory_lock(xid, cfile, flock->fl_start, length,
- type, 1, 0, wait_flag);
+ rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
+ type, 1, 0, wait_flag);
if (rc) {
kfree(lock);
goto out;
@@ -1388,7 +1450,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
cifs_lock_add(cfile, lock);
} else if (unlock)
- rc = cifs_unlock_range(cfile, flock, xid);
+ rc = server->ops->mand_unlock_range(cfile, flock, xid);
out:
if (flock->fl_flags & FL_POSIX)
@@ -1423,7 +1485,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *flock)
tcon->ses->server);
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
- netfid = cfile->netfid;
+ netfid = cfile->fid.netfid;
cinode = CIFS_I(file->f_path.dentry->d_inode);
if (cap_unix(tcon->ses) &&
@@ -1469,15 +1531,16 @@ cifs_update_eof(struct cifsInodeInfo *cifsi, loff_t offset,
cifsi->server_eof = end_of_write;
}
-static ssize_t cifs_write(struct cifsFileInfo *open_file, __u32 pid,
- const char *write_data, size_t write_size,
- loff_t *poffset)
+static ssize_t
+cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data,
+ size_t write_size, loff_t *offset)
{
int rc = 0;
unsigned int bytes_written = 0;
unsigned int total_written;
struct cifs_sb_info *cifs_sb;
- struct cifs_tcon *pTcon;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
unsigned int xid;
struct dentry *dentry = open_file->dentry;
struct cifsInodeInfo *cifsi = CIFS_I(dentry->d_inode);
@@ -1486,9 +1549,13 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, __u32 pid,
cifs_sb = CIFS_SB(dentry->d_sb);
cFYI(1, "write %zd bytes to offset %lld of %s", write_size,
- *poffset, dentry->d_name.name);
+ *offset, dentry->d_name.name);
- pTcon = tlink_tcon(open_file->tlink);
+ tcon = tlink_tcon(open_file->tlink);
+ server = tcon->ses->server;
+
+ if (!server->ops->sync_write)
+ return -ENOSYS;
xid = get_xid();
@@ -1514,13 +1581,12 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, __u32 pid,
/* iov[0] is reserved for smb header */
iov[1].iov_base = (char *)write_data + total_written;
iov[1].iov_len = len;
- io_parms.netfid = open_file->netfid;
io_parms.pid = pid;
- io_parms.tcon = pTcon;
- io_parms.offset = *poffset;
+ io_parms.tcon = tcon;
+ io_parms.offset = *offset;
io_parms.length = len;
- rc = CIFSSMBWrite2(xid, &io_parms, &bytes_written, iov,
- 1, 0);
+ rc = server->ops->sync_write(xid, open_file, &io_parms,
+ &bytes_written, iov, 1);
}
if (rc || (bytes_written == 0)) {
if (total_written)
@@ -1531,18 +1597,18 @@ static ssize_t cifs_write(struct cifsFileInfo *open_file, __u32 pid,
}
} else {
spin_lock(&dentry->d_inode->i_lock);
- cifs_update_eof(cifsi, *poffset, bytes_written);
+ cifs_update_eof(cifsi, *offset, bytes_written);
spin_unlock(&dentry->d_inode->i_lock);
- *poffset += bytes_written;
+ *offset += bytes_written;
}
}
- cifs_stats_bytes_written(pTcon, total_written);
+ cifs_stats_bytes_written(tcon, total_written);
if (total_written > 0) {
spin_lock(&dentry->d_inode->i_lock);
- if (*poffset > dentry->d_inode->i_size)
- i_size_write(dentry->d_inode, *poffset);
+ if (*offset > dentry->d_inode->i_size)
+ i_size_write(dentry->d_inode, *offset);
spin_unlock(&dentry->d_inode->i_lock);
}
mark_inode_dirty_sync(dentry->d_inode);
@@ -1718,27 +1784,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
return rc;
}
-/*
- * Marshal up the iov array, reserving the first one for the header. Also,
- * set wdata->bytes.
- */
-static void
-cifs_writepages_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata)
-{
- int i;
- struct inode *inode = wdata->cfile->dentry->d_inode;
- loff_t size = i_size_read(inode);
-
- /* marshal up the pages into iov array */
- wdata->bytes = 0;
- for (i = 0; i < wdata->nr_pages; i++) {
- iov[i + 1].iov_len = min(size - page_offset(wdata->pages[i]),
- (loff_t)PAGE_CACHE_SIZE);
- iov[i + 1].iov_base = kmap(wdata->pages[i]);
- wdata->bytes += iov[i + 1].iov_len;
- }
-}
-
static int cifs_writepages(struct address_space *mapping,
struct writeback_control *wbc)
{
@@ -1746,8 +1791,10 @@ static int cifs_writepages(struct address_space *mapping,
bool done = false, scanned = false, range_whole = false;
pgoff_t end, index;
struct cifs_writedata *wdata;
+ struct TCP_Server_Info *server;
struct page *page;
int rc = 0;
+ loff_t isize = i_size_read(mapping->host);
/*
* If wsize is smaller than the page cache size, default to writing
@@ -1852,7 +1899,7 @@ retry:
*/
set_page_writeback(page);
- if (page_offset(page) >= mapping->host->i_size) {
+ if (page_offset(page) >= isize) {
done = true;
unlock_page(page);
end_page_writeback(page);
@@ -1883,7 +1930,12 @@ retry:
wdata->sync_mode = wbc->sync_mode;
wdata->nr_pages = nr_pages;
wdata->offset = page_offset(wdata->pages[0]);
- wdata->marshal_iov = cifs_writepages_marshal_iov;
+ wdata->pagesz = PAGE_CACHE_SIZE;
+ wdata->tailsz =
+ min(isize - page_offset(wdata->pages[nr_pages - 1]),
+ (loff_t)PAGE_CACHE_SIZE);
+ wdata->bytes = ((nr_pages - 1) * PAGE_CACHE_SIZE) +
+ wdata->tailsz;
do {
if (wdata->cfile != NULL)
@@ -1896,7 +1948,8 @@ retry:
break;
}
wdata->pid = wdata->cfile->pid;
- rc = cifs_async_writev(wdata);
+ server = tlink_tcon(wdata->cfile->tlink)->ses->server;
+ rc = server->ops->async_writev(wdata);
} while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN);
for (i = 0; i < nr_pages; ++i)
@@ -2054,6 +2107,7 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
unsigned int xid;
int rc = 0;
struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
struct cifsFileInfo *smbfile = file->private_data;
struct inode *inode = file->f_path.dentry->d_inode;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
@@ -2077,8 +2131,13 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
}
tcon = tlink_tcon(smbfile->tlink);
- if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
- rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
+ server = tcon->ses->server;
+ if (server->ops->flush)
+ rc = server->ops->flush(xid, tcon, &smbfile->fid);
+ else
+ rc = -ENOSYS;
+ }
free_xid(xid);
mutex_unlock(&inode->i_mutex);
@@ -2090,6 +2149,7 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
unsigned int xid;
int rc = 0;
struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
struct cifsFileInfo *smbfile = file->private_data;
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
struct inode *inode = file->f_mapping->host;
@@ -2105,8 +2165,13 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
file->f_path.dentry->d_name.name, datasync);
tcon = tlink_tcon(smbfile->tlink);
- if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
- rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
+ if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) {
+ server = tcon->ses->server;
+ if (server->ops->flush)
+ rc = server->ops->flush(xid, tcon, &smbfile->fid);
+ else
+ rc = -ENOSYS;
+ }
free_xid(xid);
mutex_unlock(&inode->i_mutex);
@@ -2172,20 +2237,6 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)
}
static void
-cifs_uncached_marshal_iov(struct kvec *iov, struct cifs_writedata *wdata)
-{
- int i;
- size_t bytes = wdata->bytes;
-
- /* marshal up the pages into iov array */
- for (i = 0; i < wdata->nr_pages; i++) {
- iov[i + 1].iov_len = min_t(size_t, bytes, PAGE_SIZE);
- iov[i + 1].iov_base = kmap(wdata->pages[i]);
- bytes -= iov[i + 1].iov_len;
- }
-}
-
-static void
cifs_uncached_writev_complete(struct work_struct *work)
{
int i;
@@ -2215,6 +2266,9 @@ static int
cifs_uncached_retry_writev(struct cifs_writedata *wdata)
{
int rc;
+ struct TCP_Server_Info *server;
+
+ server = tlink_tcon(wdata->cfile->tlink)->ses->server;
do {
if (wdata->cfile->invalidHandle) {
@@ -2222,7 +2276,7 @@ cifs_uncached_retry_writev(struct cifs_writedata *wdata)
if (rc != 0)
continue;
}
- rc = cifs_async_writev(wdata);
+ rc = server->ops->async_writev(wdata);
} while (rc == -EAGAIN);
return rc;
@@ -2257,6 +2311,10 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
open_file = file->private_data;
tcon = tlink_tcon(open_file->tlink);
+
+ if (!tcon->ses->server->ops->async_writev)
+ return -ENOSYS;
+
offset = *poffset;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
@@ -2298,7 +2356,8 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,
wdata->cfile = cifsFileInfo_get(open_file);
wdata->pid = pid;
wdata->bytes = cur_len;
- wdata->marshal_iov = cifs_uncached_marshal_iov;
+ wdata->pagesz = PAGE_SIZE;
+ wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);
rc = cifs_uncached_retry_writev(wdata);
if (rc) {
kref_put(&wdata->refcount, cifs_writedata_release);
@@ -2376,40 +2435,110 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
return written;
}
-ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
+static ssize_t
+cifs_writev(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
{
- struct inode *inode;
+ struct file *file = iocb->ki_filp;
+ struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
+ struct inode *inode = file->f_mapping->host;
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;
+ ssize_t rc = -EACCES;
- inode = iocb->ki_filp->f_path.dentry->d_inode;
+ BUG_ON(iocb->ki_pos != pos);
- if (CIFS_I(inode)->clientCanCacheAll)
- return generic_file_aio_write(iocb, iov, nr_segs, pos);
+ sb_start_write(inode->i_sb);
+
+ /*
+ * We need to hold the sem to be sure nobody modifies lock list
+ * with a brlock that prevents writing.
+ */
+ down_read(&cinode->lock_sem);
+ if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs),
+ server->vals->exclusive_lock_type, NULL,
+ true)) {
+ mutex_lock(&inode->i_mutex);
+ rc = __generic_file_aio_write(iocb, iov, nr_segs,
+ &iocb->ki_pos);
+ mutex_unlock(&inode->i_mutex);
+ }
+
+ if (rc > 0 || rc == -EIOCBQUEUED) {
+ ssize_t err;
+
+ err = generic_write_sync(file, pos, rc);
+ if (err < 0 && rc > 0)
+ rc = err;
+ }
+
+ up_read(&cinode->lock_sem);
+ sb_end_write(inode->i_sb);
+ return rc;
+}
+
+ssize_t
+cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
+{
+ struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsFileInfo *cfile = (struct cifsFileInfo *)
+ iocb->ki_filp->private_data;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+#ifdef CONFIG_CIFS_SMB2
/*
- * In strict cache mode we need to write the data to the server exactly
- * from the pos to pos+len-1 rather than flush all affected pages
- * because it may cause a error with mandatory locks on these pages but
- * not on the region from pos to ppos+len-1.
+ * If we have an oplock for read and want to write a data to the file
+ * we need to store it in the page cache and then push it to the server
+ * to be sure the next read will get a valid data.
*/
+ if (!cinode->clientCanCacheAll && cinode->clientCanCacheRead) {
+ ssize_t written;
+ int rc;
- return cifs_user_writev(iocb, iov, nr_segs, pos);
+ written = generic_file_aio_write(iocb, iov, nr_segs, pos);
+ rc = filemap_fdatawrite(inode->i_mapping);
+ if (rc)
+ return (ssize_t)rc;
+
+ return written;
+ }
+#endif
+
+ /*
+ * For non-oplocked files in strict cache mode we need to write the data
+ * to the server exactly from the pos to pos+len-1 rather than flush all
+ * affected pages because it may cause a error with mandatory locks on
+ * these pages but not on the region from pos to ppos+len-1.
+ */
+
+ if (!cinode->clientCanCacheAll)
+ return cifs_user_writev(iocb, iov, nr_segs, pos);
+
+ if (cap_unix(tcon->ses) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
+ return generic_file_aio_write(iocb, iov, nr_segs, pos);
+
+ return cifs_writev(iocb, iov, nr_segs, pos);
}
static struct cifs_readdata *
-cifs_readdata_alloc(unsigned int nr_vecs, work_func_t complete)
+cifs_readdata_alloc(unsigned int nr_pages, work_func_t complete)
{
struct cifs_readdata *rdata;
- rdata = kzalloc(sizeof(*rdata) +
- sizeof(struct kvec) * nr_vecs, GFP_KERNEL);
+ rdata = kzalloc(sizeof(*rdata) + (sizeof(struct page *) * nr_pages),
+ GFP_KERNEL);
if (rdata != NULL) {
kref_init(&rdata->refcount);
INIT_LIST_HEAD(&rdata->list);
init_completion(&rdata->done);
INIT_WORK(&rdata->work, complete);
- INIT_LIST_HEAD(&rdata->pages);
}
+
return rdata;
}
@@ -2426,25 +2555,25 @@ cifs_readdata_release(struct kref *refcount)
}
static int
-cifs_read_allocate_pages(struct list_head *list, unsigned int npages)
+cifs_read_allocate_pages(struct cifs_readdata *rdata, unsigned int nr_pages)
{
int rc = 0;
- struct page *page, *tpage;
+ struct page *page;
unsigned int i;
- for (i = 0; i < npages; i++) {
+ for (i = 0; i < nr_pages; i++) {
page = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
if (!page) {
rc = -ENOMEM;
break;
}
- list_add(&page->lru, list);
+ rdata->pages[i] = page;
}
if (rc) {
- list_for_each_entry_safe(page, tpage, list, lru) {
- list_del(&page->lru);
- put_page(page);
+ for (i = 0; i < nr_pages; i++) {
+ put_page(rdata->pages[i]);
+ rdata->pages[i] = NULL;
}
}
return rc;
@@ -2453,13 +2582,13 @@ cifs_read_allocate_pages(struct list_head *list, unsigned int npages)
static void
cifs_uncached_readdata_release(struct kref *refcount)
{
- struct page *page, *tpage;
struct cifs_readdata *rdata = container_of(refcount,
struct cifs_readdata, refcount);
+ unsigned int i;
- list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
- list_del(&page->lru);
- put_page(page);
+ for (i = 0; i < rdata->nr_pages; i++) {
+ put_page(rdata->pages[i]);
+ rdata->pages[i] = NULL;
}
cifs_readdata_release(refcount);
}
@@ -2468,6 +2597,9 @@ static int
cifs_retry_async_readv(struct cifs_readdata *rdata)
{
int rc;
+ struct TCP_Server_Info *server;
+
+ server = tlink_tcon(rdata->cfile->tlink)->ses->server;
do {
if (rdata->cfile->invalidHandle) {
@@ -2475,7 +2607,7 @@ cifs_retry_async_readv(struct cifs_readdata *rdata)
if (rc != 0)
continue;
}
- rc = cifs_async_readv(rdata);
+ rc = server->ops->async_readv(rdata);
} while (rc == -EAGAIN);
return rc;
@@ -2500,17 +2632,18 @@ cifs_readdata_to_iov(struct cifs_readdata *rdata, const struct iovec *iov,
int rc = 0;
struct iov_iter ii;
size_t pos = rdata->offset - offset;
- struct page *page, *tpage;
ssize_t remaining = rdata->bytes;
unsigned char *pdata;
+ unsigned int i;
/* set up iov_iter and advance to the correct offset */
iov_iter_init(&ii, iov, nr_segs, iov_length(iov, nr_segs), 0);
iov_iter_advance(&ii, pos);
*copied = 0;
- list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
+ for (i = 0; i < rdata->nr_pages; i++) {
ssize_t copy;
+ struct page *page = rdata->pages[i];
/* copy a whole page or whatever's left */
copy = min_t(ssize_t, remaining, PAGE_SIZE);
@@ -2530,9 +2663,6 @@ cifs_readdata_to_iov(struct cifs_readdata *rdata, const struct iovec *iov,
iov_iter_advance(&ii, copy);
}
}
-
- list_del(&page->lru);
- put_page(page);
}
return rc;
@@ -2544,59 +2674,56 @@ cifs_uncached_readv_complete(struct work_struct *work)
struct cifs_readdata *rdata = container_of(work,
struct cifs_readdata, work);
- /* if the result is non-zero then the pages weren't kmapped */
- if (rdata->result == 0) {
- struct page *page;
-
- list_for_each_entry(page, &rdata->pages, lru)
- kunmap(page);
- }
-
complete(&rdata->done);
kref_put(&rdata->refcount, cifs_uncached_readdata_release);
}
static int
-cifs_uncached_read_marshal_iov(struct cifs_readdata *rdata,
- unsigned int remaining)
+cifs_uncached_read_into_pages(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata, unsigned int len)
{
- int len = 0;
- struct page *page, *tpage;
+ int total_read = 0, result = 0;
+ unsigned int i;
+ unsigned int nr_pages = rdata->nr_pages;
+ struct kvec iov;
+
+ rdata->tailsz = PAGE_SIZE;
+ for (i = 0; i < nr_pages; i++) {
+ struct page *page = rdata->pages[i];
- rdata->nr_iov = 1;
- list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
- if (remaining >= PAGE_SIZE) {
+ if (len >= PAGE_SIZE) {
/* enough data to fill the page */
- rdata->iov[rdata->nr_iov].iov_base = kmap(page);
- rdata->iov[rdata->nr_iov].iov_len = PAGE_SIZE;
- cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
- rdata->nr_iov, page->index,
- rdata->iov[rdata->nr_iov].iov_base,
- rdata->iov[rdata->nr_iov].iov_len);
- ++rdata->nr_iov;
- len += PAGE_SIZE;
- remaining -= PAGE_SIZE;
- } else if (remaining > 0) {
+ iov.iov_base = kmap(page);
+ iov.iov_len = PAGE_SIZE;
+ cFYI(1, "%u: iov_base=%p iov_len=%zu",
+ i, iov.iov_base, iov.iov_len);
+ len -= PAGE_SIZE;
+ } else if (len > 0) {
/* enough for partial page, fill and zero the rest */
- rdata->iov[rdata->nr_iov].iov_base = kmap(page);
- rdata->iov[rdata->nr_iov].iov_len = remaining;
- cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
- rdata->nr_iov, page->index,
- rdata->iov[rdata->nr_iov].iov_base,
- rdata->iov[rdata->nr_iov].iov_len);
- memset(rdata->iov[rdata->nr_iov].iov_base + remaining,
- '\0', PAGE_SIZE - remaining);
- ++rdata->nr_iov;
- len += remaining;
- remaining = 0;
+ iov.iov_base = kmap(page);
+ iov.iov_len = len;
+ cFYI(1, "%u: iov_base=%p iov_len=%zu",
+ i, iov.iov_base, iov.iov_len);
+ memset(iov.iov_base + len, '\0', PAGE_SIZE - len);
+ rdata->tailsz = len;
+ len = 0;
} else {
/* no need to hold page hostage */
- list_del(&page->lru);
+ rdata->pages[i] = NULL;
+ rdata->nr_pages--;
put_page(page);
+ continue;
}
+
+ result = cifs_readv_from_socket(server, &iov, 1, iov.iov_len);
+ kunmap(page);
+ if (result < 0)
+ break;
+
+ total_read += result;
}
- return len;
+ return total_read > 0 ? total_read : result;
}
static ssize_t
@@ -2627,6 +2754,9 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
open_file = file->private_data;
tcon = tlink_tcon(open_file->tlink);
+ if (!tcon->ses->server->ops->async_readv)
+ return -ENOSYS;
+
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
pid = open_file->pid;
else
@@ -2647,15 +2777,17 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
goto error;
}
- rc = cifs_read_allocate_pages(&rdata->pages, npages);
+ rc = cifs_read_allocate_pages(rdata, npages);
if (rc)
goto error;
rdata->cfile = cifsFileInfo_get(open_file);
+ rdata->nr_pages = npages;
rdata->offset = offset;
rdata->bytes = cur_len;
rdata->pid = pid;
- rdata->marshal_iov = cifs_uncached_read_marshal_iov;
+ rdata->pagesz = PAGE_SIZE;
+ rdata->read_into_pages = cifs_uncached_read_into_pages;
rc = cifs_retry_async_readv(rdata);
error:
@@ -2706,6 +2838,10 @@ restart_loop:
cifs_stats_bytes_read(tcon, total_read);
*poffset += total_read;
+ /* mask nodata case */
+ if (rc == -ENODATA)
+ rc = 0;
+
return total_read ? total_read : rc;
}
@@ -2721,15 +2857,17 @@ ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov,
return read;
}
-ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
- unsigned long nr_segs, loff_t pos)
+ssize_t
+cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t pos)
{
- struct inode *inode;
-
- inode = iocb->ki_filp->f_path.dentry->d_inode;
-
- if (CIFS_I(inode)->clientCanCacheRead)
- return generic_file_aio_read(iocb, iov, nr_segs, pos);
+ struct inode *inode = iocb->ki_filp->f_path.dentry->d_inode;
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct cifsFileInfo *cfile = (struct cifsFileInfo *)
+ iocb->ki_filp->private_data;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ int rc = -EACCES;
/*
* In strict cache mode we need to read from the server all the time
@@ -2739,12 +2877,29 @@ ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,
* on pages affected by this read but not on the region from pos to
* pos+len-1.
*/
+ if (!cinode->clientCanCacheRead)
+ return cifs_user_readv(iocb, iov, nr_segs, pos);
+
+ if (cap_unix(tcon->ses) &&
+ (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&
+ ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0))
+ return generic_file_aio_read(iocb, iov, nr_segs, pos);
- return cifs_user_readv(iocb, iov, nr_segs, pos);
+ /*
+ * We need to hold the sem to be sure nobody modifies lock list
+ * with a brlock that prevents reading.
+ */
+ down_read(&cinode->lock_sem);
+ if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs),
+ tcon->ses->server->vals->shared_lock_type,
+ NULL, true))
+ rc = generic_file_aio_read(iocb, iov, nr_segs, pos);
+ up_read(&cinode->lock_sem);
+ return rc;
}
-static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
- loff_t *poffset)
+static ssize_t
+cifs_read(struct file *file, char *read_data, size_t read_size, loff_t *offset)
{
int rc = -EACCES;
unsigned int bytes_read = 0;
@@ -2753,8 +2908,9 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
unsigned int rsize;
struct cifs_sb_info *cifs_sb;
struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
unsigned int xid;
- char *current_offset;
+ char *cur_offset;
struct cifsFileInfo *open_file;
struct cifs_io_parms io_parms;
int buf_type = CIFS_NO_BUFFER;
@@ -2773,6 +2929,12 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
}
open_file = file->private_data;
tcon = tlink_tcon(open_file->tlink);
+ server = tcon->ses->server;
+
+ if (!server->ops->sync_read) {
+ free_xid(xid);
+ return -ENOSYS;
+ }
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD)
pid = open_file->pid;
@@ -2782,9 +2944,8 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
if ((file->f_flags & O_ACCMODE) == O_WRONLY)
cFYI(1, "attempting read on write only file instance");
- for (total_read = 0, current_offset = read_data;
- read_size > total_read;
- total_read += bytes_read, current_offset += bytes_read) {
+ for (total_read = 0, cur_offset = read_data; read_size > total_read;
+ total_read += bytes_read, cur_offset += bytes_read) {
current_read_size = min_t(uint, read_size - total_read, rsize);
/*
* For windows me and 9x we do not want to request more than it
@@ -2802,13 +2963,13 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
if (rc != 0)
break;
}
- io_parms.netfid = open_file->netfid;
io_parms.pid = pid;
io_parms.tcon = tcon;
- io_parms.offset = *poffset;
+ io_parms.offset = *offset;
io_parms.length = current_read_size;
- rc = CIFSSMBRead(xid, &io_parms, &bytes_read,
- &current_offset, &buf_type);
+ rc = server->ops->sync_read(xid, open_file, &io_parms,
+ &bytes_read, &cur_offset,
+ &buf_type);
}
if (rc || (bytes_read == 0)) {
if (total_read) {
@@ -2819,7 +2980,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
}
} else {
cifs_stats_bytes_read(tcon, total_read);
- *poffset += bytes_read;
+ *offset += bytes_read;
}
}
free_xid(xid);
@@ -2885,16 +3046,16 @@ int cifs_file_mmap(struct file *file, struct vm_area_struct *vma)
static void
cifs_readv_complete(struct work_struct *work)
{
+ unsigned int i;
struct cifs_readdata *rdata = container_of(work,
struct cifs_readdata, work);
- struct page *page, *tpage;
- list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
- list_del(&page->lru);
+ for (i = 0; i < rdata->nr_pages; i++) {
+ struct page *page = rdata->pages[i];
+
lru_cache_add_file(page);
if (rdata->result == 0) {
- kunmap(page);
flush_dcache_page(page);
SetPageUptodate(page);
}
@@ -2905,49 +3066,48 @@ cifs_readv_complete(struct work_struct *work)
cifs_readpage_to_fscache(rdata->mapping->host, page);
page_cache_release(page);
+ rdata->pages[i] = NULL;
}
kref_put(&rdata->refcount, cifs_readdata_release);
}
static int
-cifs_readpages_marshal_iov(struct cifs_readdata *rdata, unsigned int remaining)
+cifs_readpages_read_into_pages(struct TCP_Server_Info *server,
+ struct cifs_readdata *rdata, unsigned int len)
{
- int len = 0;
- struct page *page, *tpage;
+ int total_read = 0, result = 0;
+ unsigned int i;
u64 eof;
pgoff_t eof_index;
+ unsigned int nr_pages = rdata->nr_pages;
+ struct kvec iov;
/* determine the eof that the server (probably) has */
eof = CIFS_I(rdata->mapping->host)->server_eof;
eof_index = eof ? (eof - 1) >> PAGE_CACHE_SHIFT : 0;
cFYI(1, "eof=%llu eof_index=%lu", eof, eof_index);
- rdata->nr_iov = 1;
- list_for_each_entry_safe(page, tpage, &rdata->pages, lru) {
- if (remaining >= PAGE_CACHE_SIZE) {
+ rdata->tailsz = PAGE_CACHE_SIZE;
+ for (i = 0; i < nr_pages; i++) {
+ struct page *page = rdata->pages[i];
+
+ if (len >= PAGE_CACHE_SIZE) {
/* enough data to fill the page */
- rdata->iov[rdata->nr_iov].iov_base = kmap(page);
- rdata->iov[rdata->nr_iov].iov_len = PAGE_CACHE_SIZE;
+ iov.iov_base = kmap(page);
+ iov.iov_len = PAGE_CACHE_SIZE;
cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
- rdata->nr_iov, page->index,
- rdata->iov[rdata->nr_iov].iov_base,
- rdata->iov[rdata->nr_iov].iov_len);
- ++rdata->nr_iov;
- len += PAGE_CACHE_SIZE;
- remaining -= PAGE_CACHE_SIZE;
- } else if (remaining > 0) {
+ i, page->index, iov.iov_base, iov.iov_len);
+ len -= PAGE_CACHE_SIZE;
+ } else if (len > 0) {
/* enough for partial page, fill and zero the rest */
- rdata->iov[rdata->nr_iov].iov_base = kmap(page);
- rdata->iov[rdata->nr_iov].iov_len = remaining;
+ iov.iov_base = kmap(page);
+ iov.iov_len = len;
cFYI(1, "%u: idx=%lu iov_base=%p iov_len=%zu",
- rdata->nr_iov, page->index,
- rdata->iov[rdata->nr_iov].iov_base,
- rdata->iov[rdata->nr_iov].iov_len);
- memset(rdata->iov[rdata->nr_iov].iov_base + remaining,
- '\0', PAGE_CACHE_SIZE - remaining);
- ++rdata->nr_iov;
- len += remaining;
- remaining = 0;
+ i, page->index, iov.iov_base, iov.iov_len);
+ memset(iov.iov_base + len,
+ '\0', PAGE_CACHE_SIZE - len);
+ rdata->tailsz = len;
+ len = 0;
} else if (page->index > eof_index) {
/*
* The VFS will not try to do readahead past the
@@ -2958,22 +3118,33 @@ cifs_readpages_marshal_iov(struct cifs_readdata *rdata, unsigned int remaining)
* fill them until the writes are flushed.
*/
zero_user(page, 0, PAGE_CACHE_SIZE);
- list_del(&page->lru);
lru_cache_add_file(page);
flush_dcache_page(page);
SetPageUptodate(page);
unlock_page(page);
page_cache_release(page);
+ rdata->pages[i] = NULL;
+ rdata->nr_pages--;
+ continue;
} else {
/* no need to hold page hostage */
- list_del(&page->lru);
lru_cache_add_file(page);
unlock_page(page);
page_cache_release(page);
+ rdata->pages[i] = NULL;
+ rdata->nr_pages--;
+ continue;
}
+
+ result = cifs_readv_from_socket(server, &iov, 1, iov.iov_len);
+ kunmap(page);
+ if (result < 0)
+ break;
+
+ total_read += result;
}
- return len;
+ return total_read > 0 ? total_read : result;
}
static int cifs_readpages(struct file *file, struct address_space *mapping,
@@ -3027,6 +3198,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
* the rdata->pages, then we want them in increasing order.
*/
while (!list_empty(page_list)) {
+ unsigned int i;
unsigned int bytes = PAGE_CACHE_SIZE;
unsigned int expected_index;
unsigned int nr_pages = 1;
@@ -3096,14 +3268,18 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
rdata->offset = offset;
rdata->bytes = bytes;
rdata->pid = pid;
- rdata->marshal_iov = cifs_readpages_marshal_iov;
- list_splice_init(&tmplist, &rdata->pages);
+ rdata->pagesz = PAGE_CACHE_SIZE;
+ rdata->read_into_pages = cifs_readpages_read_into_pages;
+
+ list_for_each_entry_safe(page, tpage, &tmplist, lru) {
+ list_del(&page->lru);
+ rdata->pages[rdata->nr_pages++] = page;
+ }
rc = cifs_retry_async_readv(rdata);
if (rc != 0) {
- list_for_each_entry_safe(page, tpage, &rdata->pages,
- lru) {
- list_del(&page->lru);
+ for (i = 0; i < rdata->nr_pages; i++) {
+ page = rdata->pages[i];
lru_cache_add_file(page);
unlock_page(page);
page_cache_release(page);
@@ -3347,6 +3523,7 @@ void cifs_oplock_break(struct work_struct *work)
oplock_break);
struct inode *inode = cfile->dentry->d_inode;
struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
int rc = 0;
if (inode && S_ISREG(inode->i_mode)) {
@@ -3374,10 +3551,8 @@ void cifs_oplock_break(struct work_struct *work)
* disconnected since oplock already released by the server
*/
if (!cfile->oplock_break_cancelled) {
- rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid,
- current->tgid, 0, 0, 0, 0,
- LOCKING_ANDX_OPLOCK_RELEASE, false,
- cinode->clientCanCacheRead ? 1 : 0);
+ rc = tcon->ses->server->ops->oplock_response(tcon, &cfile->fid,
+ cinode);
cFYI(1, "Oplock release rc = %d", rc);
}
}
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index cb79c7edecb0..afdff79651f1 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -282,7 +282,8 @@ cifs_create_dfs_fattr(struct cifs_fattr *fattr, struct super_block *sb)
fattr->cf_flags |= CIFS_FATTR_DFS_REFERRAL;
}
-int cifs_get_file_info_unix(struct file *filp)
+static int
+cifs_get_file_info_unix(struct file *filp)
{
int rc;
unsigned int xid;
@@ -294,7 +295,7 @@ int cifs_get_file_info_unix(struct file *filp)
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
xid = get_xid();
- rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data);
+ rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->fid.netfid, &find_data);
if (!rc) {
cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
} else if (rc == -EREMOTE) {
@@ -550,7 +551,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
fattr->cf_gid = cifs_sb->mnt_gid;
}
-int cifs_get_file_info(struct file *filp)
+static int
+cifs_get_file_info(struct file *filp)
{
int rc;
unsigned int xid;
@@ -560,9 +562,13 @@ int cifs_get_file_info(struct file *filp)
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsFileInfo *cfile = filp->private_data;
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+
+ if (!server->ops->query_file_info)
+ return -ENOSYS;
xid = get_xid();
- rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data);
+ rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data);
switch (rc) {
case 0:
cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false);
@@ -601,7 +607,9 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
FILE_ALL_INFO *data, struct super_block *sb, int xid,
const __u16 *fid)
{
- int rc = 0, tmprc;
+ bool validinum = false;
+ __u16 srchflgs;
+ int rc = 0, tmprc = ENOSYS;
struct cifs_tcon *tcon;
struct TCP_Server_Info *server;
struct tcon_link *tlink;
@@ -609,6 +617,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
char *buf = NULL;
bool adjust_tz = false;
struct cifs_fattr fattr;
+ struct cifs_search_info *srchinf = NULL;
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
@@ -647,9 +656,38 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
} else if (rc == -EREMOTE) {
cifs_create_dfs_fattr(&fattr, sb);
rc = 0;
- } else {
+ } else if (rc == -EACCES && backup_cred(cifs_sb)) {
+ srchinf = kzalloc(sizeof(struct cifs_search_info),
+ GFP_KERNEL);
+ if (srchinf == NULL) {
+ rc = -ENOMEM;
+ goto cgii_exit;
+ }
+
+ srchinf->endOfSearch = false;
+ srchinf->info_level = SMB_FIND_FILE_ID_FULL_DIR_INFO;
+
+ srchflgs = CIFS_SEARCH_CLOSE_ALWAYS |
+ CIFS_SEARCH_CLOSE_AT_END |
+ CIFS_SEARCH_BACKUP_SEARCH;
+
+ rc = CIFSFindFirst(xid, tcon, full_path,
+ cifs_sb, NULL, srchflgs, srchinf, false);
+ if (!rc) {
+ data =
+ (FILE_ALL_INFO *)srchinf->srch_entries_start;
+
+ cifs_dir_info_to_fattr(&fattr,
+ (FILE_DIRECTORY_INFO *)data, cifs_sb);
+ fattr.cf_uniqueid = le64_to_cpu(
+ ((SEARCH_ID_FULL_DIR_INFO *)data)->UniqueId);
+ validinum = true;
+
+ cifs_buf_release(srchinf->ntwrk_buf_start);
+ }
+ kfree(srchinf);
+ } else
goto cgii_exit;
- }
/*
* If an inode wasn't passed in, then get the inode number
@@ -660,23 +698,21 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
*/
if (*inode == NULL) {
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
- if (server->ops->get_srv_inum)
- tmprc = server->ops->get_srv_inum(xid, tcon,
- cifs_sb, full_path, &fattr.cf_uniqueid,
- data);
- else
- tmprc = -ENOSYS;
- if (tmprc || !fattr.cf_uniqueid) {
- cFYI(1, "GetSrvInodeNum rc %d", tmprc);
- fattr.cf_uniqueid = iunique(sb, ROOT_I);
- cifs_autodisable_serverino(cifs_sb);
+ if (validinum == false) {
+ if (server->ops->get_srv_inum)
+ tmprc = server->ops->get_srv_inum(xid,
+ tcon, cifs_sb, full_path,
+ &fattr.cf_uniqueid, data);
+ if (tmprc) {
+ cFYI(1, "GetSrvInodeNum rc %d", tmprc);
+ fattr.cf_uniqueid = iunique(sb, ROOT_I);
+ cifs_autodisable_serverino(cifs_sb);
+ }
}
- } else {
+ } else
fattr.cf_uniqueid = iunique(sb, ROOT_I);
- }
- } else {
+ } else
fattr.cf_uniqueid = CIFS_I(*inode)->uniqueid;
- }
/* query for SFU type info if supported and needed */
if (fattr.cf_cifsattrs & ATTR_SYSTEM &&
@@ -876,25 +912,22 @@ out:
return inode;
}
-static int
+int
cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid,
- char *full_path, __u32 dosattr)
+ char *full_path, __u32 dosattr)
{
- int rc;
- int oplock = 0;
- __u16 netfid;
- __u32 netpid;
bool set_time = false;
- struct cifsFileInfo *open_file;
- struct cifsInodeInfo *cifsInode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
- struct tcon_link *tlink = NULL;
- struct cifs_tcon *pTcon;
+ struct TCP_Server_Info *server;
FILE_BASIC_INFO info_buf;
if (attrs == NULL)
return -EINVAL;
+ server = cifs_sb_master_tcon(cifs_sb)->ses->server;
+ if (!server->ops->set_file_info)
+ return -ENOSYS;
+
if (attrs->ia_valid & ATTR_ATIME) {
set_time = true;
info_buf.LastAccessTime =
@@ -925,81 +958,17 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, unsigned int xid,
info_buf.CreationTime = 0; /* don't change */
info_buf.Attributes = cpu_to_le32(dosattr);
- /*
- * If the file is already open for write, just use that fileid
- */
- open_file = find_writable_file(cifsInode, true);
- if (open_file) {
- netfid = open_file->netfid;
- netpid = open_file->pid;
- pTcon = tlink_tcon(open_file->tlink);
- goto set_via_filehandle;
- }
-
- tlink = cifs_sb_tlink(cifs_sb);
- if (IS_ERR(tlink)) {
- rc = PTR_ERR(tlink);
- tlink = NULL;
- goto out;
- }
- pTcon = tlink_tcon(tlink);
-
- /*
- * NT4 apparently returns success on this call, but it doesn't
- * really work.
- */
- if (!(pTcon->ses->flags & CIFS_SES_NT4)) {
- rc = CIFSSMBSetPathInfo(xid, pTcon, full_path,
- &info_buf, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
- if (rc == 0) {
- cifsInode->cifsAttrs = dosattr;
- goto out;
- } else if (rc != -EOPNOTSUPP && rc != -EINVAL)
- goto out;
- }
-
- cFYI(1, "calling SetFileInfo since SetPathInfo for "
- "times not supported by this server");
- rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_OPEN,
- SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
- CREATE_NOT_DIR, &netfid, &oplock,
- NULL, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
-
- if (rc != 0) {
- if (rc == -EIO)
- rc = -EINVAL;
- goto out;
- }
-
- netpid = current->tgid;
-
-set_via_filehandle:
- rc = CIFSSMBSetFileInfo(xid, pTcon, &info_buf, netfid, netpid);
- if (!rc)
- cifsInode->cifsAttrs = dosattr;
-
- if (open_file == NULL)
- CIFSSMBClose(xid, pTcon, netfid);
- else
- cifsFileInfo_put(open_file);
-out:
- if (tlink != NULL)
- cifs_put_tlink(tlink);
- return rc;
+ return server->ops->set_file_info(inode, full_path, &info_buf, xid);
}
/*
- * open the given file (if it isn't already), set the DELETE_ON_CLOSE bit
+ * Open the given file (if it isn't already), set the DELETE_ON_CLOSE bit
* and rename it to a random name that hopefully won't conflict with
* anything else.
*/
-static int
-cifs_rename_pending_delete(char *full_path, struct dentry *dentry,
- unsigned int xid)
+int
+cifs_rename_pending_delete(const char *full_path, struct dentry *dentry,
+ const unsigned int xid)
{
int oplock = 0;
int rc;
@@ -1136,6 +1105,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct tcon_link *tlink;
struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
struct iattr *attrs = NULL;
__u32 dosattr = 0, origattr = 0;
@@ -1145,6 +1115,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
xid = get_xid();
@@ -1167,8 +1138,12 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
}
retry_std_delete:
- rc = CIFSSMBDelFile(xid, tcon, full_path, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (!server->ops->unlink) {
+ rc = -ENOSYS;
+ goto psx_del_no_retry;
+ }
+
+ rc = server->ops->unlink(xid, tcon, full_path, cifs_sb);
psx_del_no_retry:
if (!rc) {
@@ -1177,9 +1152,14 @@ psx_del_no_retry:
} else if (rc == -ENOENT) {
d_drop(dentry);
} else if (rc == -ETXTBSY) {
- rc = cifs_rename_pending_delete(full_path, dentry, xid);
- if (rc == 0)
- cifs_drop_nlink(inode);
+ if (server->ops->rename_pending_delete) {
+ rc = server->ops->rename_pending_delete(full_path,
+ dentry, xid);
+ if (rc == 0)
+ cifs_drop_nlink(inode);
+ }
+ if (rc == -ETXTBSY)
+ rc = -EBUSY;
} else if ((rc == -EACCES) && (dosattr == 0) && inode) {
attrs = kzalloc(sizeof(*attrs), GFP_KERNEL);
if (attrs == NULL) {
@@ -1227,34 +1207,33 @@ unlink_out:
}
static int
-cifs_mkdir_qinfo(struct inode *inode, struct dentry *dentry, umode_t mode,
+cifs_mkdir_qinfo(struct inode *parent, struct dentry *dentry, umode_t mode,
const char *full_path, struct cifs_sb_info *cifs_sb,
struct cifs_tcon *tcon, const unsigned int xid)
{
int rc = 0;
- struct inode *newinode = NULL;
+ struct inode *inode = NULL;
if (tcon->unix_ext)
- rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb,
+ rc = cifs_get_inode_info_unix(&inode, full_path, parent->i_sb,
xid);
else
- rc = cifs_get_inode_info(&newinode, full_path, NULL,
- inode->i_sb, xid, NULL);
+ rc = cifs_get_inode_info(&inode, full_path, NULL, parent->i_sb,
+ xid, NULL);
+
if (rc)
return rc;
- d_instantiate(dentry, newinode);
/*
* setting nlink not necessary except in cases where we failed to get it
- * from the server or was set bogus
+ * from the server or was set bogus. Also, since this is a brand new
+ * inode, no need to grab the i_lock before setting the i_nlink.
*/
- spin_lock(&dentry->d_inode->i_lock);
- if ((dentry->d_inode) && (dentry->d_inode->i_nlink < 2))
- set_nlink(dentry->d_inode, 2);
- spin_unlock(&dentry->d_inode->i_lock);
+ if (inode->i_nlink < 2)
+ set_nlink(inode, 2);
mode &= ~current_umask();
/* must turn on setgid bit if parent dir has it */
- if (inode->i_mode & S_ISGID)
+ if (parent->i_mode & S_ISGID)
mode |= S_ISGID;
if (tcon->unix_ext) {
@@ -1267,8 +1246,8 @@ cifs_mkdir_qinfo(struct inode *inode, struct dentry *dentry, umode_t mode,
};
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
args.uid = (__u64)current_fsuid();
- if (inode->i_mode & S_ISGID)
- args.gid = (__u64)inode->i_gid;
+ if (parent->i_mode & S_ISGID)
+ args.gid = (__u64)parent->i_gid;
else
args.gid = (__u64)current_fsgid();
} else {
@@ -1283,22 +1262,20 @@ cifs_mkdir_qinfo(struct inode *inode, struct dentry *dentry, umode_t mode,
struct TCP_Server_Info *server = tcon->ses->server;
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) &&
(mode & S_IWUGO) == 0 && server->ops->mkdir_setinfo)
- server->ops->mkdir_setinfo(newinode, full_path, cifs_sb,
+ server->ops->mkdir_setinfo(inode, full_path, cifs_sb,
tcon, xid);
- if (dentry->d_inode) {
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
- dentry->d_inode->i_mode = (mode | S_IFDIR);
-
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
- dentry->d_inode->i_uid = current_fsuid();
- if (inode->i_mode & S_ISGID)
- dentry->d_inode->i_gid = inode->i_gid;
- else
- dentry->d_inode->i_gid =
- current_fsgid();
- }
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
+ inode->i_mode = (mode | S_IFDIR);
+
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
+ inode->i_uid = current_fsuid();
+ if (inode->i_mode & S_ISGID)
+ inode->i_gid = parent->i_gid;
+ else
+ inode->i_gid = current_fsgid();
}
}
+ d_instantiate(dentry, inode);
return rc;
}
@@ -1495,29 +1472,32 @@ rmdir_exit:
}
static int
-cifs_do_rename(unsigned int xid, struct dentry *from_dentry,
- const char *fromPath, struct dentry *to_dentry,
- const char *toPath)
+cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,
+ const char *from_path, struct dentry *to_dentry,
+ const char *to_path)
{
struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb);
struct tcon_link *tlink;
- struct cifs_tcon *pTcon;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
__u16 srcfid;
int oplock, rc;
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
- pTcon = tlink_tcon(tlink);
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+
+ if (!server->ops->rename)
+ return -ENOSYS;
/* try path-based rename first */
- rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ rc = server->ops->rename(xid, tcon, from_path, to_path, cifs_sb);
/*
- * don't bother with rename by filehandle unless file is busy and
- * source Note that cross directory moves do not work with
+ * Don't bother with rename by filehandle unless file is busy and
+ * source. Note that cross directory moves do not work with
* rename by filehandle to various Windows servers.
*/
if (rc == 0 || rc != -ETXTBSY)
@@ -1528,29 +1508,28 @@ cifs_do_rename(unsigned int xid, struct dentry *from_dentry,
goto do_rename_exit;
/* open the file to be renamed -- we need DELETE perms */
- rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE,
+ rc = CIFSSMBOpen(xid, tcon, from_path, FILE_OPEN, DELETE,
CREATE_NOT_DIR, &srcfid, &oplock, NULL,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
-
if (rc == 0) {
- rc = CIFSSMBRenameOpenFile(xid, pTcon, srcfid,
+ rc = CIFSSMBRenameOpenFile(xid, tcon, srcfid,
(const char *) to_dentry->d_name.name,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
-
- CIFSSMBClose(xid, pTcon, srcfid);
+ CIFSSMBClose(xid, tcon, srcfid);
}
do_rename_exit:
cifs_put_tlink(tlink);
return rc;
}
-int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
- struct inode *target_dir, struct dentry *target_dentry)
+int
+cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
+ struct inode *target_dir, struct dentry *target_dentry)
{
- char *fromName = NULL;
- char *toName = NULL;
+ char *from_name = NULL;
+ char *to_name = NULL;
struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifs_tcon *tcon;
@@ -1571,25 +1550,25 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
* we already have the rename sem so we do not need to
* grab it again here to protect the path integrity
*/
- fromName = build_path_from_dentry(source_dentry);
- if (fromName == NULL) {
+ from_name = build_path_from_dentry(source_dentry);
+ if (from_name == NULL) {
rc = -ENOMEM;
goto cifs_rename_exit;
}
- toName = build_path_from_dentry(target_dentry);
- if (toName == NULL) {
+ to_name = build_path_from_dentry(target_dentry);
+ if (to_name == NULL) {
rc = -ENOMEM;
goto cifs_rename_exit;
}
- rc = cifs_do_rename(xid, source_dentry, fromName,
- target_dentry, toName);
+ rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
+ to_name);
if (rc == -EEXIST && tcon->unix_ext) {
/*
- * Are src and dst hardlinks of same inode? We can
- * only tell with unix extensions enabled
+ * Are src and dst hardlinks of same inode? We can only tell
+ * with unix extensions enabled.
*/
info_buf_source =
kmalloc(2 * sizeof(FILE_UNIX_BASIC_INFO),
@@ -1600,19 +1579,19 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
}
info_buf_target = info_buf_source + 1;
- tmprc = CIFSSMBUnixQPathInfo(xid, tcon, fromName,
- info_buf_source,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ tmprc = CIFSSMBUnixQPathInfo(xid, tcon, from_name,
+ info_buf_source,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
if (tmprc != 0)
goto unlink_target;
- tmprc = CIFSSMBUnixQPathInfo(xid, tcon, toName,
- info_buf_target,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ tmprc = CIFSSMBUnixQPathInfo(xid, tcon, to_name,
+ info_buf_target,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
if (tmprc == 0 && (info_buf_source->UniqueId ==
info_buf_target->UniqueId)) {
@@ -1620,8 +1599,11 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
rc = 0;
goto cifs_rename_exit;
}
- } /* else ... BB we could add the same check for Windows by
- checking the UniqueId via FILE_INTERNAL_INFO */
+ }
+ /*
+ * else ... BB we could add the same check for Windows by
+ * checking the UniqueId via FILE_INTERNAL_INFO
+ */
unlink_target:
/* Try unlinking the target dentry if it's not negative */
@@ -1629,15 +1611,14 @@ unlink_target:
tmprc = cifs_unlink(target_dir, target_dentry);
if (tmprc)
goto cifs_rename_exit;
-
- rc = cifs_do_rename(xid, source_dentry, fromName,
- target_dentry, toName);
+ rc = cifs_do_rename(xid, source_dentry, from_name,
+ target_dentry, to_name);
}
cifs_rename_exit:
kfree(info_buf_source);
- kfree(fromName);
- kfree(toName);
+ kfree(from_name);
+ kfree(to_name);
free_xid(xid);
cifs_put_tlink(tlink);
return rc;
@@ -1862,7 +1843,8 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink = NULL;
- struct cifs_tcon *pTcon = NULL;
+ struct cifs_tcon *tcon = NULL;
+ struct TCP_Server_Info *server;
struct cifs_io_parms io_parms;
/*
@@ -1876,19 +1858,21 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
*/
open_file = find_writable_file(cifsInode, true);
if (open_file) {
- __u16 nfid = open_file->netfid;
- __u32 npid = open_file->pid;
- pTcon = tlink_tcon(open_file->tlink);
- rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid,
- npid, false);
+ tcon = tlink_tcon(open_file->tlink);
+ server = tcon->ses->server;
+ if (server->ops->set_file_size)
+ rc = server->ops->set_file_size(xid, tcon, open_file,
+ attrs->ia_size, false);
+ else
+ rc = -ENOSYS;
cifsFileInfo_put(open_file);
cFYI(1, "SetFSize for attrs rc = %d", rc);
if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
unsigned int bytes_written;
- io_parms.netfid = nfid;
- io_parms.pid = npid;
- io_parms.tcon = pTcon;
+ io_parms.netfid = open_file->fid.netfid;
+ io_parms.pid = open_file->pid;
+ io_parms.tcon = tcon;
io_parms.offset = 0;
io_parms.length = attrs->ia_size;
rc = CIFSSMBWrite(xid, &io_parms, &bytes_written,
@@ -1898,52 +1882,55 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
} else
rc = -EINVAL;
- if (rc != 0) {
- if (pTcon == NULL) {
- tlink = cifs_sb_tlink(cifs_sb);
- if (IS_ERR(tlink))
- return PTR_ERR(tlink);
- pTcon = tlink_tcon(tlink);
- }
+ if (!rc)
+ goto set_size_out;
- /* Set file size by pathname rather than by handle
- either because no valid, writeable file handle for
- it was found or because there was an error setting
- it by handle */
- rc = CIFSSMBSetEOF(xid, pTcon, full_path, attrs->ia_size,
- false, cifs_sb->local_nls,
+ if (tcon == NULL) {
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ tcon = tlink_tcon(tlink);
+ server = tcon->ses->server;
+ }
+
+ /*
+ * Set file size by pathname rather than by handle either because no
+ * valid, writeable file handle for it was found or because there was
+ * an error setting it by handle.
+ */
+ if (server->ops->set_path_size)
+ rc = server->ops->set_path_size(xid, tcon, full_path,
+ attrs->ia_size, cifs_sb, false);
+ else
+ rc = -ENOSYS;
+ cFYI(1, "SetEOF by path (setattrs) rc = %d", rc);
+ if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
+ __u16 netfid;
+ int oplock = 0;
+
+ rc = SMBLegacyOpen(xid, tcon, full_path, FILE_OPEN,
+ GENERIC_WRITE, CREATE_NOT_DIR, &netfid,
+ &oplock, NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
- cFYI(1, "SetEOF by path (setattrs) rc = %d", rc);
- if ((rc == -EINVAL) || (rc == -EOPNOTSUPP)) {
- __u16 netfid;
- int oplock = 0;
-
- rc = SMBLegacyOpen(xid, pTcon, full_path,
- FILE_OPEN, GENERIC_WRITE,
- CREATE_NOT_DIR, &netfid, &oplock, NULL,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
- if (rc == 0) {
- unsigned int bytes_written;
-
- io_parms.netfid = netfid;
- io_parms.pid = current->tgid;
- io_parms.tcon = pTcon;
- io_parms.offset = 0;
- io_parms.length = attrs->ia_size;
- rc = CIFSSMBWrite(xid, &io_parms,
- &bytes_written,
- NULL, NULL, 1);
- cFYI(1, "wrt seteof rc %d", rc);
- CIFSSMBClose(xid, pTcon, netfid);
- }
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc == 0) {
+ unsigned int bytes_written;
+
+ io_parms.netfid = netfid;
+ io_parms.pid = current->tgid;
+ io_parms.tcon = tcon;
+ io_parms.offset = 0;
+ io_parms.length = attrs->ia_size;
+ rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, NULL,
+ NULL, 1);
+ cFYI(1, "wrt seteof rc %d", rc);
+ CIFSSMBClose(xid, tcon, netfid);
}
- if (tlink)
- cifs_put_tlink(tlink);
}
+ if (tlink)
+ cifs_put_tlink(tlink);
+set_size_out:
if (rc == 0) {
cifsInode->server_eof = attrs->ia_size;
cifs_setsize(inode, attrs->ia_size);
@@ -2050,7 +2037,7 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
args->device = 0;
open_file = find_writable_file(cifsInode, true);
if (open_file) {
- u16 nfid = open_file->netfid;
+ u16 nfid = open_file->fid.netfid;
u32 npid = open_file->pid;
pTcon = tlink_tcon(open_file->tlink);
rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid);
diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c
index ae082a66de2f..fd5009d56f9f 100644
--- a/fs/cifs/ioctl.c
+++ b/fs/cifs/ioctl.c
@@ -28,8 +28,6 @@
#include "cifs_debug.h"
#include "cifsfs.h"
-#define CIFS_IOC_CHECKUMOUNT _IO(0xCF, 2)
-
long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
{
struct inode *inode = filep->f_dentry->d_inode;
@@ -51,23 +49,6 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
cifs_sb = CIFS_SB(inode->i_sb);
switch (command) {
- static bool warned = false;
- case CIFS_IOC_CHECKUMOUNT:
- if (!warned) {
- warned = true;
- cERROR(1, "the CIFS_IOC_CHECKMOUNT ioctl will "
- "be deprecated in 3.7. Please "
- "migrate away from the use of "
- "umount.cifs");
- }
- cFYI(1, "User unmount attempted");
- if (cifs_sb->mnt_uid == current_uid())
- rc = 0;
- else {
- rc = -EACCES;
- cFYI(1, "uids do not match");
- }
- break;
#ifdef CONFIG_CIFS_POSIX
case FS_IOC_GETFLAGS:
if (pSMBFile == NULL)
@@ -75,8 +56,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
tcon = tlink_tcon(pSMBFile->tlink);
caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
if (CIFS_UNIX_EXTATTR_CAP & caps) {
- rc = CIFSGetExtAttr(xid, tcon, pSMBFile->netfid,
- &ExtAttrBits, &ExtAttrMask);
+ rc = CIFSGetExtAttr(xid, tcon,
+ pSMBFile->fid.netfid,
+ &ExtAttrBits, &ExtAttrMask);
if (rc == 0)
rc = put_user(ExtAttrBits &
FS_FL_USER_VISIBLE,
@@ -94,8 +76,12 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
rc = -EFAULT;
break;
}
- /* rc= CIFSGetExtAttr(xid,tcon,pSMBFile->netfid,
- extAttrBits, &ExtAttrMask);*/
+ /*
+ * rc = CIFSGetExtAttr(xid, tcon,
+ * pSMBFile->fid.netfid,
+ * extAttrBits,
+ * &ExtAttrMask);
+ */
}
cFYI(1, "set flags not implemented yet");
break;
diff --git a/fs/cifs/link.c b/fs/cifs/link.c
index e6ce3b112875..51dc2fb6e854 100644
--- a/fs/cifs/link.c
+++ b/fs/cifs/link.c
@@ -391,72 +391,86 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
{
int rc = -EACCES;
unsigned int xid;
- char *fromName = NULL;
- char *toName = NULL;
+ char *from_name = NULL;
+ char *to_name = NULL;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink;
- struct cifs_tcon *pTcon;
+ struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
struct cifsInodeInfo *cifsInode;
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
- pTcon = tlink_tcon(tlink);
+ tcon = tlink_tcon(tlink);
xid = get_xid();
- fromName = build_path_from_dentry(old_file);
- toName = build_path_from_dentry(direntry);
- if ((fromName == NULL) || (toName == NULL)) {
+ from_name = build_path_from_dentry(old_file);
+ to_name = build_path_from_dentry(direntry);
+ if ((from_name == NULL) || (to_name == NULL)) {
rc = -ENOMEM;
goto cifs_hl_exit;
}
- if (pTcon->unix_ext)
- rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
+ if (tcon->unix_ext)
+ rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
else {
- rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
- cifs_sb->local_nls,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR);
+ server = tcon->ses->server;
+ if (!server->ops->create_hardlink)
+ return -ENOSYS;
+ rc = server->ops->create_hardlink(xid, tcon, from_name, to_name,
+ cifs_sb);
if ((rc == -EIO) || (rc == -EINVAL))
rc = -EOPNOTSUPP;
}
d_drop(direntry); /* force new lookup from server of target */
- /* if source file is cached (oplocked) revalidate will not go to server
- until the file is closed or oplock broken so update nlinks locally */
+ /*
+ * if source file is cached (oplocked) revalidate will not go to server
+ * until the file is closed or oplock broken so update nlinks locally
+ */
if (old_file->d_inode) {
cifsInode = CIFS_I(old_file->d_inode);
if (rc == 0) {
spin_lock(&old_file->d_inode->i_lock);
inc_nlink(old_file->d_inode);
spin_unlock(&old_file->d_inode->i_lock);
-/* BB should we make this contingent on superblock flag NOATIME? */
-/* old_file->d_inode->i_ctime = CURRENT_TIME;*/
- /* parent dir timestamps will update from srv
- within a second, would it really be worth it
- to set the parent dir cifs inode time to zero
- to force revalidate (faster) for it too? */
+ /*
+ * BB should we make this contingent on superblock flag
+ * NOATIME?
+ */
+ /* old_file->d_inode->i_ctime = CURRENT_TIME; */
+ /*
+ * parent dir timestamps will update from srv within a
+ * second, would it really be worth it to set the parent
+ * dir cifs inode time to zero to force revalidate
+ * (faster) for it too?
+ */
}
- /* if not oplocked will force revalidate to get info
- on source file from srv */
+ /*
+ * if not oplocked will force revalidate to get info on source
+ * file from srv
+ */
cifsInode->time = 0;
- /* Will update parent dir timestamps from srv within a second.
- Would it really be worth it to set the parent dir (cifs
- inode) time field to zero to force revalidate on parent
- directory faster ie
- CIFS_I(inode)->time = 0; */
+ /*
+ * Will update parent dir timestamps from srv within a second.
+ * Would it really be worth it to set the parent dir (cifs
+ * inode) time field to zero to force revalidate on parent
+ * directory faster ie
+ *
+ * CIFS_I(inode)->time = 0;
+ */
}
cifs_hl_exit:
- kfree(fromName);
- kfree(toName);
+ kfree(from_name);
+ kfree(to_name);
free_xid(xid);
cifs_put_tlink(tlink);
return rc;
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index ce41fee07e5b..3a00c0d0cead 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -466,7 +466,7 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)
list_for_each(tmp2, &tcon->openFileList) {
netfile = list_entry(tmp2, struct cifsFileInfo,
tlist);
- if (pSMB->Fid != netfile->netfid)
+ if (pSMB->Fid != netfile->fid.netfid)
continue;
cFYI(1, "file id match, oplock break");
@@ -579,3 +579,33 @@ backup_cred(struct cifs_sb_info *cifs_sb)
return false;
}
+
+void
+cifs_del_pending_open(struct cifs_pending_open *open)
+{
+ spin_lock(&cifs_file_list_lock);
+ list_del(&open->olist);
+ spin_unlock(&cifs_file_list_lock);
+}
+
+void
+cifs_add_pending_open_locked(struct cifs_fid *fid, struct tcon_link *tlink,
+ struct cifs_pending_open *open)
+{
+#ifdef CONFIG_CIFS_SMB2
+ memcpy(open->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
+#endif
+ open->oplock = CIFS_OPLOCK_NO_CHANGE;
+ open->tlink = tlink;
+ fid->pending_open = open;
+ list_add_tail(&open->olist, &tlink_tcon(tlink)->pending_opens);
+}
+
+void
+cifs_add_pending_open(struct cifs_fid *fid, struct tcon_link *tlink,
+ struct cifs_pending_open *open)
+{
+ spin_lock(&cifs_file_list_lock);
+ cifs_add_pending_open_locked(fid, tlink, open);
+ spin_unlock(&cifs_file_list_lock);
+}
diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c
index 581c225f7f50..d5ce9e26696c 100644
--- a/fs/cifs/netmisc.c
+++ b/fs/cifs/netmisc.c
@@ -110,7 +110,7 @@ static const struct smb_to_posix_error mapping_table_ERRSRV[] = {
{ERRnoroom, -ENOSPC},
{ERRrmuns, -EUSERS},
{ERRtimeout, -ETIME},
- {ERRnoresource, -ENOBUFS},
+ {ERRnoresource, -EREMOTEIO},
{ERRtoomanyuids, -EUSERS},
{ERRbaduid, -EACCES},
{ERRusempx, -EIO},
@@ -412,7 +412,7 @@ static const struct {
from NT_STATUS_INSUFFICIENT_RESOURCES to
NT_STATUS_INSUFF_SERVER_RESOURCES during the session setup } */
{
- ERRDOS, ERRnomem, NT_STATUS_INSUFFICIENT_RESOURCES}, {
+ ERRDOS, ERRnoresource, NT_STATUS_INSUFFICIENT_RESOURCES}, {
ERRDOS, ERRbadpath, NT_STATUS_DFS_EXIT_PATH_FOUND}, {
ERRDOS, 23, NT_STATUS_DEVICE_DATA_ERROR}, {
ERRHRD, ERRgeneral, NT_STATUS_DEVICE_NOT_CONNECTED}, {
@@ -682,7 +682,7 @@ static const struct {
ERRHRD, ERRgeneral, NT_STATUS_NO_USER_SESSION_KEY}, {
ERRDOS, 59, NT_STATUS_USER_SESSION_DELETED}, {
ERRHRD, ERRgeneral, NT_STATUS_RESOURCE_LANG_NOT_FOUND}, {
- ERRDOS, ERRnomem, NT_STATUS_INSUFF_SERVER_RESOURCES}, {
+ ERRDOS, ERRnoresource, NT_STATUS_INSUFF_SERVER_RESOURCES}, {
ERRHRD, ERRgeneral, NT_STATUS_INVALID_BUFFER_SIZE}, {
ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_COMPONENT}, {
ERRHRD, ERRgeneral, NT_STATUS_INVALID_ADDRESS_WILDCARD}, {
@@ -913,8 +913,9 @@ map_smb_to_linux_error(char *buf, bool logErr)
* portion, the number of word parameters and the data portion of the message
*/
unsigned int
-smbCalcSize(struct smb_hdr *ptr)
+smbCalcSize(void *buf)
{
+ struct smb_hdr *ptr = (struct smb_hdr *)buf;
return (sizeof(struct smb_hdr) + (2 * ptr->WordCount) +
2 /* size of the bcc field */ + get_bcc(ptr));
}
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index d87f82678bc7..f9b5d3d6cf33 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -151,7 +151,7 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
}
}
-static void
+void
cifs_dir_info_to_fattr(struct cifs_fattr *fattr, FILE_DIRECTORY_INFO *info,
struct cifs_sb_info *cifs_sb)
{
@@ -220,7 +220,8 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
}
*/
-static int initiate_cifs_search(const unsigned int xid, struct file *file)
+static int
+initiate_cifs_search(const unsigned int xid, struct file *file)
{
__u16 search_flags;
int rc = 0;
@@ -229,6 +230,7 @@ static int initiate_cifs_search(const unsigned int xid, struct file *file)
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
struct tcon_link *tlink = NULL;
struct cifs_tcon *tcon;
+ struct TCP_Server_Info *server;
if (file->private_data == NULL) {
tlink = cifs_sb_tlink(cifs_sb);
@@ -248,6 +250,13 @@ static int initiate_cifs_search(const unsigned int xid, struct file *file)
tcon = tlink_tcon(cifsFile->tlink);
}
+ server = tcon->ses->server;
+
+ if (!server->ops->query_dir_first) {
+ rc = -ENOSYS;
+ goto error_exit;
+ }
+
cifsFile->invalidHandle = true;
cifsFile->srch_inf.endOfSearch = false;
@@ -278,10 +287,10 @@ ffirst_retry:
if (backup_cred(cifs_sb))
search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
- rc = CIFSFindFirst(xid, tcon, full_path, cifs_sb->local_nls,
- &cifsFile->netfid, search_flags, &cifsFile->srch_inf,
- cifs_sb->mnt_cifs_flags &
- CIFS_MOUNT_MAP_SPECIAL_CHR, CIFS_DIR_SEP(cifs_sb));
+ rc = server->ops->query_dir_first(xid, tcon, full_path, cifs_sb,
+ &cifsFile->fid, search_flags,
+ &cifsFile->srch_inf);
+
if (rc == 0)
cifsFile->invalidHandle = false;
/* BB add following call to handle readdir on new NTFS symlink errors
@@ -501,62 +510,67 @@ static int cifs_save_resume_key(const char *current_entry,
return rc;
}
-/* find the corresponding entry in the search */
-/* Note that the SMB server returns search entries for . and .. which
- complicates logic here if we choose to parse for them and we do not
- assume that they are located in the findfirst return buffer.*/
-/* We start counting in the buffer with entry 2 and increment for every
- entry (do not increment for . or .. entry) */
-static int find_cifs_entry(const unsigned int xid, struct cifs_tcon *pTcon,
- struct file *file, char **ppCurrentEntry, int *num_to_ret)
+/*
+ * Find the corresponding entry in the search. Note that the SMB server returns
+ * search entries for . and .. which complicates logic here if we choose to
+ * parse for them and we do not assume that they are located in the findfirst
+ * return buffer. We start counting in the buffer with entry 2 and increment for
+ * every entry (do not increment for . or .. entry).
+ */
+static int
+find_cifs_entry(const unsigned int xid, struct cifs_tcon *tcon,
+ struct file *file, char **current_entry, int *num_to_ret)
{
__u16 search_flags;
int rc = 0;
int pos_in_buf = 0;
loff_t first_entry_in_buffer;
loff_t index_to_find = file->f_pos;
- struct cifsFileInfo *cifsFile = file->private_data;
+ struct cifsFileInfo *cfile = file->private_data;
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ struct TCP_Server_Info *server = tcon->ses->server;
/* check if index in the buffer */
- if ((cifsFile == NULL) || (ppCurrentEntry == NULL) ||
- (num_to_ret == NULL))
+ if (!server->ops->query_dir_first || !server->ops->query_dir_next)
+ return -ENOSYS;
+
+ if ((cfile == NULL) || (current_entry == NULL) || (num_to_ret == NULL))
return -ENOENT;
- *ppCurrentEntry = NULL;
- first_entry_in_buffer =
- cifsFile->srch_inf.index_of_last_entry -
- cifsFile->srch_inf.entries_in_buffer;
+ *current_entry = NULL;
+ first_entry_in_buffer = cfile->srch_inf.index_of_last_entry -
+ cfile->srch_inf.entries_in_buffer;
- /* if first entry in buf is zero then is first buffer
- in search response data which means it is likely . and ..
- will be in this buffer, although some servers do not return
- . and .. for the root of a drive and for those we need
- to start two entries earlier */
+ /*
+ * If first entry in buf is zero then is first buffer
+ * in search response data which means it is likely . and ..
+ * will be in this buffer, although some servers do not return
+ * . and .. for the root of a drive and for those we need
+ * to start two entries earlier.
+ */
dump_cifs_file_struct(file, "In fce ");
- if (((index_to_find < cifsFile->srch_inf.index_of_last_entry) &&
- is_dir_changed(file)) ||
- (index_to_find < first_entry_in_buffer)) {
+ if (((index_to_find < cfile->srch_inf.index_of_last_entry) &&
+ is_dir_changed(file)) || (index_to_find < first_entry_in_buffer)) {
/* close and restart search */
cFYI(1, "search backing up - close and restart search");
spin_lock(&cifs_file_list_lock);
- if (!cifsFile->srch_inf.endOfSearch &&
- !cifsFile->invalidHandle) {
- cifsFile->invalidHandle = true;
+ if (!cfile->srch_inf.endOfSearch && !cfile->invalidHandle) {
+ cfile->invalidHandle = true;
spin_unlock(&cifs_file_list_lock);
- CIFSFindClose(xid, pTcon, cifsFile->netfid);
+ if (server->ops->close)
+ server->ops->close(xid, tcon, &cfile->fid);
} else
spin_unlock(&cifs_file_list_lock);
- if (cifsFile->srch_inf.ntwrk_buf_start) {
+ if (cfile->srch_inf.ntwrk_buf_start) {
cFYI(1, "freeing SMB ff cache buf on search rewind");
- if (cifsFile->srch_inf.smallBuf)
- cifs_small_buf_release(cifsFile->srch_inf.
+ if (cfile->srch_inf.smallBuf)
+ cifs_small_buf_release(cfile->srch_inf.
ntwrk_buf_start);
else
- cifs_buf_release(cifsFile->srch_inf.
+ cifs_buf_release(cfile->srch_inf.
ntwrk_buf_start);
- cifsFile->srch_inf.ntwrk_buf_start = NULL;
+ cfile->srch_inf.ntwrk_buf_start = NULL;
}
rc = initiate_cifs_search(xid, file);
if (rc) {
@@ -565,65 +579,64 @@ static int find_cifs_entry(const unsigned int xid, struct cifs_tcon *pTcon,
return rc;
}
/* FindFirst/Next set last_entry to NULL on malformed reply */
- if (cifsFile->srch_inf.last_entry)
- cifs_save_resume_key(cifsFile->srch_inf.last_entry,
- cifsFile);
+ if (cfile->srch_inf.last_entry)
+ cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
}
search_flags = CIFS_SEARCH_CLOSE_AT_END | CIFS_SEARCH_RETURN_RESUME;
if (backup_cred(cifs_sb))
search_flags |= CIFS_SEARCH_BACKUP_SEARCH;
- while ((index_to_find >= cifsFile->srch_inf.index_of_last_entry) &&
- (rc == 0) && !cifsFile->srch_inf.endOfSearch) {
+ while ((index_to_find >= cfile->srch_inf.index_of_last_entry) &&
+ (rc == 0) && !cfile->srch_inf.endOfSearch) {
cFYI(1, "calling findnext2");
- rc = CIFSFindNext(xid, pTcon, cifsFile->netfid, search_flags,
- &cifsFile->srch_inf);
+ rc = server->ops->query_dir_next(xid, tcon, &cfile->fid,
+ search_flags,
+ &cfile->srch_inf);
/* FindFirst/Next set last_entry to NULL on malformed reply */
- if (cifsFile->srch_inf.last_entry)
- cifs_save_resume_key(cifsFile->srch_inf.last_entry,
- cifsFile);
+ if (cfile->srch_inf.last_entry)
+ cifs_save_resume_key(cfile->srch_inf.last_entry, cfile);
if (rc)
return -ENOENT;
}
- if (index_to_find < cifsFile->srch_inf.index_of_last_entry) {
+ if (index_to_find < cfile->srch_inf.index_of_last_entry) {
/* we found the buffer that contains the entry */
/* scan and find it */
int i;
- char *current_entry;
- char *end_of_smb = cifsFile->srch_inf.ntwrk_buf_start +
- smbCalcSize((struct smb_hdr *)
- cifsFile->srch_inf.ntwrk_buf_start);
-
- current_entry = cifsFile->srch_inf.srch_entries_start;
- first_entry_in_buffer = cifsFile->srch_inf.index_of_last_entry
- - cifsFile->srch_inf.entries_in_buffer;
+ char *cur_ent;
+ char *end_of_smb = cfile->srch_inf.ntwrk_buf_start +
+ server->ops->calc_smb_size(
+ cfile->srch_inf.ntwrk_buf_start);
+
+ cur_ent = cfile->srch_inf.srch_entries_start;
+ first_entry_in_buffer = cfile->srch_inf.index_of_last_entry
+ - cfile->srch_inf.entries_in_buffer;
pos_in_buf = index_to_find - first_entry_in_buffer;
cFYI(1, "found entry - pos_in_buf %d", pos_in_buf);
- for (i = 0; (i < (pos_in_buf)) && (current_entry != NULL); i++) {
+ for (i = 0; (i < (pos_in_buf)) && (cur_ent != NULL); i++) {
/* go entry by entry figuring out which is first */
- current_entry = nxt_dir_entry(current_entry, end_of_smb,
- cifsFile->srch_inf.info_level);
+ cur_ent = nxt_dir_entry(cur_ent, end_of_smb,
+ cfile->srch_inf.info_level);
}
- if ((current_entry == NULL) && (i < pos_in_buf)) {
+ if ((cur_ent == NULL) && (i < pos_in_buf)) {
/* BB fixme - check if we should flag this error */
cERROR(1, "reached end of buf searching for pos in buf"
- " %d index to find %lld rc %d",
- pos_in_buf, index_to_find, rc);
+ " %d index to find %lld rc %d", pos_in_buf,
+ index_to_find, rc);
}
rc = 0;
- *ppCurrentEntry = current_entry;
+ *current_entry = cur_ent;
} else {
cFYI(1, "index not in buffer - could not findnext into it");
return 0;
}
- if (pos_in_buf >= cifsFile->srch_inf.entries_in_buffer) {
+ if (pos_in_buf >= cfile->srch_inf.entries_in_buffer) {
cFYI(1, "can not return entries pos_in_buf beyond last");
*num_to_ret = 0;
} else
- *num_to_ret = cifsFile->srch_inf.entries_in_buffer - pos_in_buf;
+ *num_to_ret = cfile->srch_inf.entries_in_buffer - pos_in_buf;
return rc;
}
@@ -723,7 +736,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
int rc = 0;
unsigned int xid;
int i;
- struct cifs_tcon *pTcon;
+ struct cifs_tcon *tcon;
struct cifsFileInfo *cifsFile = NULL;
char *current_entry;
int num_to_fill = 0;
@@ -781,12 +794,12 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
}
} /* else {
cifsFile->invalidHandle = true;
- CIFSFindClose(xid, pTcon, cifsFile->netfid);
+ tcon->ses->server->close(xid, tcon, &cifsFile->fid);
} */
- pTcon = tlink_tcon(cifsFile->tlink);
- rc = find_cifs_entry(xid, pTcon, file,
- &current_entry, &num_to_fill);
+ tcon = tlink_tcon(cifsFile->tlink);
+ rc = find_cifs_entry(xid, tcon, file, &current_entry,
+ &num_to_fill);
if (rc) {
cFYI(1, "fce error %d", rc);
goto rddir2_exit;
@@ -798,7 +811,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
}
cFYI(1, "loop through %d times filling dir for net buf %p",
num_to_fill, cifsFile->srch_inf.ntwrk_buf_start);
- max_len = smbCalcSize((struct smb_hdr *)
+ max_len = tcon->ses->server->ops->calc_smb_size(
cifsFile->srch_inf.ntwrk_buf_start);
end_of_smb = cifsFile->srch_inf.ntwrk_buf_start + max_len;
@@ -815,10 +828,12 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
num_to_fill, i);
break;
}
- /* if buggy server returns . and .. late do
- we want to check for that here? */
- rc = cifs_filldir(current_entry, file,
- filldir, direntry, tmp_buf, max_len);
+ /*
+ * if buggy server returns . and .. late do we want to
+ * check for that here?
+ */
+ rc = cifs_filldir(current_entry, file, filldir,
+ direntry, tmp_buf, max_len);
if (rc == -EOVERFLOW) {
rc = 0;
break;
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 382c06d01b38..76809f4d3428 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -876,7 +876,8 @@ ssetup_ntlmssp_authenticate:
pSMB = (SESSION_SETUP_ANDX *)iov[0].iov_base;
smb_buf = (struct smb_hdr *)iov[0].iov_base;
- if ((type == RawNTLMSSP) && (smb_buf->Status.CifsError ==
+ if ((type == RawNTLMSSP) && (resp_buf_type != CIFS_NO_BUFFER) &&
+ (smb_buf->Status.CifsError ==
cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))) {
if (phase != NtLmNegotiate) {
cERROR(1, "Unexpected more processing error");
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c
index 3129ac74b819..56cc4be87807 100644
--- a/fs/cifs/smb1ops.c
+++ b/fs/cifs/smb1ops.c
@@ -17,6 +17,8 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/pagemap.h>
+#include <linux/vfs.h>
#include "cifsglob.h"
#include "cifsproto.h"
#include "cifs_debug.h"
@@ -63,7 +65,7 @@ send_nt_cancel(struct TCP_Server_Info *server, void *buf,
static bool
cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
{
- return ob1->netfid == ob2->netfid;
+ return ob1->fid.netfid == ob2->fid.netfid;
}
static unsigned int
@@ -410,6 +412,83 @@ cifs_negotiate(const unsigned int xid, struct cifs_ses *ses)
return rc;
}
+static unsigned int
+cifs_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
+{
+ __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ unsigned int wsize;
+
+ /* start with specified wsize, or default */
+ if (volume_info->wsize)
+ wsize = volume_info->wsize;
+ else if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
+ wsize = CIFS_DEFAULT_IOSIZE;
+ else
+ wsize = CIFS_DEFAULT_NON_POSIX_WSIZE;
+
+ /* can server support 24-bit write sizes? (via UNIX extensions) */
+ if (!tcon->unix_ext || !(unix_cap & CIFS_UNIX_LARGE_WRITE_CAP))
+ wsize = min_t(unsigned int, wsize, CIFS_MAX_RFC1002_WSIZE);
+
+ /*
+ * no CAP_LARGE_WRITE_X or is signing enabled without CAP_UNIX set?
+ * Limit it to max buffer offered by the server, minus the size of the
+ * WRITEX header, not including the 4 byte RFC1001 length.
+ */
+ if (!(server->capabilities & CAP_LARGE_WRITE_X) ||
+ (!(server->capabilities & CAP_UNIX) &&
+ (server->sec_mode & (SECMODE_SIGN_ENABLED|SECMODE_SIGN_REQUIRED))))
+ wsize = min_t(unsigned int, wsize,
+ server->maxBuf - sizeof(WRITE_REQ) + 4);
+
+ /* hard limit of CIFS_MAX_WSIZE */
+ wsize = min_t(unsigned int, wsize, CIFS_MAX_WSIZE);
+
+ return wsize;
+}
+
+static unsigned int
+cifs_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
+{
+ __u64 unix_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ unsigned int rsize, defsize;
+
+ /*
+ * Set default value...
+ *
+ * HACK alert! Ancient servers have very small buffers. Even though
+ * MS-CIFS indicates that servers are only limited by the client's
+ * bufsize for reads, testing against win98se shows that it throws
+ * INVALID_PARAMETER errors if you try to request too large a read.
+ * OS/2 just sends back short reads.
+ *
+ * If the server doesn't advertise CAP_LARGE_READ_X, then assume that
+ * it can't handle a read request larger than its MaxBufferSize either.
+ */
+ if (tcon->unix_ext && (unix_cap & CIFS_UNIX_LARGE_READ_CAP))
+ defsize = CIFS_DEFAULT_IOSIZE;
+ else if (server->capabilities & CAP_LARGE_READ_X)
+ defsize = CIFS_DEFAULT_NON_POSIX_RSIZE;
+ else
+ defsize = server->maxBuf - sizeof(READ_RSP);
+
+ rsize = volume_info->rsize ? volume_info->rsize : defsize;
+
+ /*
+ * no CAP_LARGE_READ_X? Then MS-CIFS states that we must limit this to
+ * the client's MaxBufferSize.
+ */
+ if (!(server->capabilities & CAP_LARGE_READ_X))
+ rsize = min_t(unsigned int, CIFSMaxBufSize, rsize);
+
+ /* hard limit of CIFS_MAX_RSIZE */
+ rsize = min_t(unsigned int, rsize, CIFS_MAX_RSIZE);
+
+ return rsize;
+}
+
static void
cifs_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon)
{
@@ -489,6 +568,13 @@ cifs_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
CIFS_MOUNT_MAP_SPECIAL_CHR);
}
+static int
+cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid, FILE_ALL_INFO *data)
+{
+ return CIFSSMBQFileInfo(xid, tcon, fid->netfid, data);
+}
+
static char *
cifs_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
struct cifs_tcon *tcon)
@@ -607,6 +693,219 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
cifsInode->cifsAttrs = dosattrs;
}
+static int
+cifs_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path,
+ int disposition, int desired_access, int create_options,
+ struct cifs_fid *fid, __u32 *oplock, FILE_ALL_INFO *buf,
+ struct cifs_sb_info *cifs_sb)
+{
+ if (!(tcon->ses->capabilities & CAP_NT_SMBS))
+ return SMBLegacyOpen(xid, tcon, path, disposition,
+ desired_access, create_options,
+ &fid->netfid, oplock, buf,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
+ & CIFS_MOUNT_MAP_SPECIAL_CHR);
+ return CIFSSMBOpen(xid, tcon, path, disposition, desired_access,
+ create_options, &fid->netfid, oplock, buf,
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+}
+
+static void
+cifs_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
+{
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+ cfile->fid.netfid = fid->netfid;
+ cifs_set_oplock_level(cinode, oplock);
+ cinode->can_cache_brlcks = cinode->clientCanCacheAll;
+}
+
+static void
+cifs_close_file(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ CIFSSMBClose(xid, tcon, fid->netfid);
+}
+
+static int
+cifs_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ return CIFSSMBFlush(xid, tcon, fid->netfid);
+}
+
+static int
+cifs_sync_read(const unsigned int xid, struct cifsFileInfo *cfile,
+ struct cifs_io_parms *parms, unsigned int *bytes_read,
+ char **buf, int *buf_type)
+{
+ parms->netfid = cfile->fid.netfid;
+ return CIFSSMBRead(xid, parms, bytes_read, buf, buf_type);
+}
+
+static int
+cifs_sync_write(const unsigned int xid, struct cifsFileInfo *cfile,
+ struct cifs_io_parms *parms, unsigned int *written,
+ struct kvec *iov, unsigned long nr_segs)
+{
+
+ parms->netfid = cfile->fid.netfid;
+ return CIFSSMBWrite2(xid, parms, written, iov, nr_segs);
+}
+
+static int
+smb_set_file_info(struct inode *inode, const char *full_path,
+ FILE_BASIC_INFO *buf, const unsigned int xid)
+{
+ int oplock = 0;
+ int rc;
+ __u16 netfid;
+ __u32 netpid;
+ struct cifsFileInfo *open_file;
+ struct cifsInodeInfo *cinode = CIFS_I(inode);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink = NULL;
+ struct cifs_tcon *tcon;
+ FILE_BASIC_INFO info_buf;
+
+ /* if the file is already open for write, just use that fileid */
+ open_file = find_writable_file(cinode, true);
+ if (open_file) {
+ netfid = open_file->fid.netfid;
+ netpid = open_file->pid;
+ tcon = tlink_tcon(open_file->tlink);
+ goto set_via_filehandle;
+ }
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink)) {
+ rc = PTR_ERR(tlink);
+ tlink = NULL;
+ goto out;
+ }
+ tcon = tlink_tcon(tlink);
+
+ /*
+ * NT4 apparently returns success on this call, but it doesn't really
+ * work.
+ */
+ if (!(tcon->ses->flags & CIFS_SES_NT4)) {
+ rc = CIFSSMBSetPathInfo(xid, tcon, full_path, buf,
+ cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags &
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
+ if (rc == 0) {
+ cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
+ goto out;
+ } else if (rc != -EOPNOTSUPP && rc != -EINVAL)
+ goto out;
+ }
+
+ cFYI(1, "calling SetFileInfo since SetPathInfo for times not supported "
+ "by this server");
+ rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
+ SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR,
+ &netfid, &oplock, NULL, cifs_sb->local_nls,
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+ if (rc != 0) {
+ if (rc == -EIO)
+ rc = -EINVAL;
+ goto out;
+ }
+
+ netpid = current->tgid;
+
+set_via_filehandle:
+ rc = CIFSSMBSetFileInfo(xid, tcon, &info_buf, netfid, netpid);
+ if (!rc)
+ cinode->cifsAttrs = le32_to_cpu(buf->Attributes);
+
+ if (open_file == NULL)
+ CIFSSMBClose(xid, tcon, netfid);
+ else
+ cifsFileInfo_put(open_file);
+out:
+ if (tlink != NULL)
+ cifs_put_tlink(tlink);
+ return rc;
+}
+
+static int
+cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *path, struct cifs_sb_info *cifs_sb,
+ struct cifs_fid *fid, __u16 search_flags,
+ struct cifs_search_info *srch_inf)
+{
+ return CIFSFindFirst(xid, tcon, path, cifs_sb,
+ &fid->netfid, search_flags, srch_inf, true);
+}
+
+static int
+cifs_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid, __u16 search_flags,
+ struct cifs_search_info *srch_inf)
+{
+ return CIFSFindNext(xid, tcon, fid->netfid, search_flags, srch_inf);
+}
+
+static int
+cifs_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ return CIFSFindClose(xid, tcon, fid->netfid);
+}
+
+static int
+cifs_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
+ struct cifsInodeInfo *cinode)
+{
+ return CIFSSMBLock(0, tcon, fid->netfid, current->tgid, 0, 0, 0, 0,
+ LOCKING_ANDX_OPLOCK_RELEASE, false,
+ cinode->clientCanCacheRead ? 1 : 0);
+}
+
+static int
+cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *buf)
+{
+ int rc = -EOPNOTSUPP;
+
+ buf->f_type = CIFS_MAGIC_NUMBER;
+
+ /*
+ * We could add a second check for a QFS Unix capability bit
+ */
+ if ((tcon->ses->capabilities & CAP_UNIX) &&
+ (CIFS_POSIX_EXTENSIONS & le64_to_cpu(tcon->fsUnixInfo.Capability)))
+ rc = CIFSSMBQFSPosixInfo(xid, tcon, buf);
+
+ /*
+ * Only need to call the old QFSInfo if failed on newer one,
+ * e.g. by OS/2.
+ **/
+ if (rc && (tcon->ses->capabilities & CAP_NT_SMBS))
+ rc = CIFSSMBQFSInfo(xid, tcon, buf);
+
+ /*
+ * Some old Windows servers also do not support level 103, retry with
+ * older level one if old server failed the previous call or we
+ * bypassed it because we detected that this was an older LANMAN sess
+ */
+ if (rc)
+ rc = SMBOldQFSInfo(xid, tcon, buf);
+ return rc;
+}
+
+static int
+cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
+ __u64 length, __u32 type, int lock, int unlock, bool wait)
+{
+ return CIFSSMBLock(xid, tlink_tcon(cfile->tlink), cfile->fid.netfid,
+ current->tgid, length, offset, unlock, lock,
+ (__u8)type, wait, 0);
+}
+
struct smb_version_operations smb1_operations = {
.send_cancel = send_nt_cancel,
.compare_fids = cifs_compare_fids,
@@ -630,6 +929,8 @@ struct smb_version_operations smb1_operations = {
.check_trans2 = cifs_check_trans2,
.need_neg = cifs_need_neg,
.negotiate = cifs_negotiate,
+ .negotiate_wsize = cifs_negotiate_wsize,
+ .negotiate_rsize = cifs_negotiate_rsize,
.sess_setup = CIFS_SessSetup,
.logoff = CIFSSMBLogoff,
.tree_connect = CIFSTCon,
@@ -638,12 +939,37 @@ struct smb_version_operations smb1_operations = {
.qfs_tcon = cifs_qfs_tcon,
.is_path_accessible = cifs_is_path_accessible,
.query_path_info = cifs_query_path_info,
+ .query_file_info = cifs_query_file_info,
.get_srv_inum = cifs_get_srv_inum,
+ .set_path_size = CIFSSMBSetEOF,
+ .set_file_size = CIFSSMBSetFileSize,
+ .set_file_info = smb_set_file_info,
.build_path_to_root = cifs_build_path_to_root,
.echo = CIFSSMBEcho,
.mkdir = CIFSSMBMkDir,
.mkdir_setinfo = cifs_mkdir_setinfo,
.rmdir = CIFSSMBRmDir,
+ .unlink = CIFSSMBDelFile,
+ .rename_pending_delete = cifs_rename_pending_delete,
+ .rename = CIFSSMBRename,
+ .create_hardlink = CIFSCreateHardLink,
+ .open = cifs_open_file,
+ .set_fid = cifs_set_fid,
+ .close = cifs_close_file,
+ .flush = cifs_flush_file,
+ .async_readv = cifs_async_readv,
+ .async_writev = cifs_async_writev,
+ .sync_read = cifs_sync_read,
+ .sync_write = cifs_sync_write,
+ .query_dir_first = cifs_query_dir_first,
+ .query_dir_next = cifs_query_dir_next,
+ .close_dir = cifs_close_dir,
+ .calc_smb_size = smbCalcSize,
+ .oplock_response = cifs_oplock_response,
+ .queryfs = cifs_queryfs,
+ .mand_lock = cifs_mand_lock,
+ .mand_unlock_range = cifs_unlock_range,
+ .push_mand_locks = cifs_push_mandatory_locks,
};
struct smb_version_values smb1_values = {
diff --git a/fs/cifs/smb2file.c b/fs/cifs/smb2file.c
new file mode 100644
index 000000000000..a93eec30a50d
--- /dev/null
+++ b/fs/cifs/smb2file.c
@@ -0,0 +1,302 @@
+/*
+ * fs/cifs/smb2file.c
+ *
+ * Copyright (C) International Business Machines Corp., 2002, 2011
+ * Author(s): Steve French (sfrench@us.ibm.com),
+ * Pavel Shilovsky ((pshilovsky@samba.org) 2012
+ *
+ * This library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
+ * the GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <linux/fs.h>
+#include <linux/stat.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <asm/div64.h>
+#include "cifsfs.h"
+#include "cifspdu.h"
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "cifs_fs_sb.h"
+#include "cifs_unicode.h"
+#include "fscache.h"
+#include "smb2proto.h"
+
+void
+smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
+{
+ oplock &= 0xFF;
+ if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
+ return;
+ if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) {
+ cinode->clientCanCacheAll = true;
+ cinode->clientCanCacheRead = true;
+ cFYI(1, "Exclusive Oplock granted on inode %p",
+ &cinode->vfs_inode);
+ } else if (oplock == SMB2_OPLOCK_LEVEL_II) {
+ cinode->clientCanCacheAll = false;
+ cinode->clientCanCacheRead = true;
+ cFYI(1, "Level II Oplock granted on inode %p",
+ &cinode->vfs_inode);
+ } else {
+ cinode->clientCanCacheAll = false;
+ cinode->clientCanCacheRead = false;
+ }
+}
+
+int
+smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path,
+ int disposition, int desired_access, int create_options,
+ struct cifs_fid *fid, __u32 *oplock, FILE_ALL_INFO *buf,
+ struct cifs_sb_info *cifs_sb)
+{
+ int rc;
+ __le16 *smb2_path;
+ struct smb2_file_all_info *smb2_data = NULL;
+ __u8 smb2_oplock[17];
+
+ smb2_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (smb2_path == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2,
+ GFP_KERNEL);
+ if (smb2_data == NULL) {
+ rc = -ENOMEM;
+ goto out;
+ }
+
+ desired_access |= FILE_READ_ATTRIBUTES;
+ *smb2_oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+
+ if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
+ memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE);
+
+ rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid,
+ &fid->volatile_fid, desired_access, disposition,
+ 0, 0, smb2_oplock, smb2_data);
+ if (rc)
+ goto out;
+
+ if (buf) {
+ /* open response does not have IndexNumber field - get it */
+ rc = SMB2_get_srv_num(xid, tcon, fid->persistent_fid,
+ fid->volatile_fid,
+ &smb2_data->IndexNumber);
+ if (rc) {
+ /* let get_inode_info disable server inode numbers */
+ smb2_data->IndexNumber = 0;
+ rc = 0;
+ }
+ move_smb2_info_to_cifs(buf, smb2_data);
+ }
+
+ *oplock = *smb2_oplock;
+out:
+ kfree(smb2_data);
+ kfree(smb2_path);
+ return rc;
+}
+
+int
+smb2_unlock_range(struct cifsFileInfo *cfile, struct file_lock *flock,
+ const unsigned int xid)
+{
+ int rc = 0, stored_rc;
+ unsigned int max_num, num = 0, max_buf;
+ struct smb2_lock_element *buf, *cur;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+ struct cifsLockInfo *li, *tmp;
+ __u64 length = 1 + flock->fl_end - flock->fl_start;
+ struct list_head tmp_llist;
+
+ INIT_LIST_HEAD(&tmp_llist);
+
+ /*
+ * Accessing maxBuf is racy with cifs_reconnect - need to store value
+ * and check it for zero before using.
+ */
+ max_buf = tcon->ses->server->maxBuf;
+ if (!max_buf)
+ return -EINVAL;
+
+ max_num = max_buf / sizeof(struct smb2_lock_element);
+ buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ cur = buf;
+
+ down_write(&cinode->lock_sem);
+ list_for_each_entry_safe(li, tmp, &cfile->llist->locks, llist) {
+ if (flock->fl_start > li->offset ||
+ (flock->fl_start + length) <
+ (li->offset + li->length))
+ continue;
+ if (current->tgid != li->pid)
+ continue;
+ if (cinode->can_cache_brlcks) {
+ /*
+ * We can cache brlock requests - simply remove a lock
+ * from the file's list.
+ */
+ list_del(&li->llist);
+ cifs_del_lock_waiters(li);
+ kfree(li);
+ continue;
+ }
+ cur->Length = cpu_to_le64(li->length);
+ cur->Offset = cpu_to_le64(li->offset);
+ cur->Flags = cpu_to_le32(SMB2_LOCKFLAG_UNLOCK);
+ /*
+ * We need to save a lock here to let us add it again to the
+ * file's list if the unlock range request fails on the server.
+ */
+ list_move(&li->llist, &tmp_llist);
+ if (++num == max_num) {
+ stored_rc = smb2_lockv(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ current->tgid, num, buf);
+ if (stored_rc) {
+ /*
+ * We failed on the unlock range request - add
+ * all locks from the tmp list to the head of
+ * the file's list.
+ */
+ cifs_move_llist(&tmp_llist,
+ &cfile->llist->locks);
+ rc = stored_rc;
+ } else
+ /*
+ * The unlock range request succeed - free the
+ * tmp list.
+ */
+ cifs_free_llist(&tmp_llist);
+ cur = buf;
+ num = 0;
+ } else
+ cur++;
+ }
+ if (num) {
+ stored_rc = smb2_lockv(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, current->tgid,
+ num, buf);
+ if (stored_rc) {
+ cifs_move_llist(&tmp_llist, &cfile->llist->locks);
+ rc = stored_rc;
+ } else
+ cifs_free_llist(&tmp_llist);
+ }
+ up_write(&cinode->lock_sem);
+
+ kfree(buf);
+ return rc;
+}
+
+static int
+smb2_push_mand_fdlocks(struct cifs_fid_locks *fdlocks, const unsigned int xid,
+ struct smb2_lock_element *buf, unsigned int max_num)
+{
+ int rc = 0, stored_rc;
+ struct cifsFileInfo *cfile = fdlocks->cfile;
+ struct cifsLockInfo *li;
+ unsigned int num = 0;
+ struct smb2_lock_element *cur = buf;
+ struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
+
+ list_for_each_entry(li, &fdlocks->locks, llist) {
+ cur->Length = cpu_to_le64(li->length);
+ cur->Offset = cpu_to_le64(li->offset);
+ cur->Flags = cpu_to_le32(li->type |
+ SMB2_LOCKFLAG_FAIL_IMMEDIATELY);
+ if (++num == max_num) {
+ stored_rc = smb2_lockv(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ current->tgid, num, buf);
+ if (stored_rc)
+ rc = stored_rc;
+ cur = buf;
+ num = 0;
+ } else
+ cur++;
+ }
+ if (num) {
+ stored_rc = smb2_lockv(xid, tcon,
+ cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid,
+ current->tgid, num, buf);
+ if (stored_rc)
+ rc = stored_rc;
+ }
+
+ return rc;
+}
+
+int
+smb2_push_mandatory_locks(struct cifsFileInfo *cfile)
+{
+ int rc = 0, stored_rc;
+ unsigned int xid;
+ unsigned int max_num, max_buf;
+ struct smb2_lock_element *buf;
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+ struct cifs_fid_locks *fdlocks;
+
+ xid = get_xid();
+ /* we are going to update can_cache_brlcks here - need a write access */
+ down_write(&cinode->lock_sem);
+ if (!cinode->can_cache_brlcks) {
+ up_write(&cinode->lock_sem);
+ free_xid(xid);
+ return rc;
+ }
+
+ /*
+ * Accessing maxBuf is racy with cifs_reconnect - need to store value
+ * and check it for zero before using.
+ */
+ max_buf = tlink_tcon(cfile->tlink)->ses->server->maxBuf;
+ if (!max_buf) {
+ up_write(&cinode->lock_sem);
+ free_xid(xid);
+ return -EINVAL;
+ }
+
+ max_num = max_buf / sizeof(struct smb2_lock_element);
+ buf = kzalloc(max_num * sizeof(struct smb2_lock_element), GFP_KERNEL);
+ if (!buf) {
+ up_write(&cinode->lock_sem);
+ free_xid(xid);
+ return -ENOMEM;
+ }
+
+ list_for_each_entry(fdlocks, &cinode->llist, llist) {
+ stored_rc = smb2_push_mand_fdlocks(fdlocks, xid, buf, max_num);
+ if (stored_rc)
+ rc = stored_rc;
+ }
+
+ cinode->can_cache_brlcks = false;
+ kfree(buf);
+
+ up_write(&cinode->lock_sem);
+ free_xid(xid);
+ return rc;
+}
diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h
index 33c1d89090c0..7c0e2143e775 100644
--- a/fs/cifs/smb2glob.h
+++ b/fs/cifs/smb2glob.h
@@ -23,6 +23,8 @@
#ifndef _SMB2_GLOB_H
#define _SMB2_GLOB_H
+#define SMB2_MAGIC_NUMBER 0xFE534D42
+
/*
*****************************************************************
* Constants go here
@@ -40,5 +42,17 @@
#define SMB2_OP_MKDIR 5
#define SMB2_OP_RENAME 6
#define SMB2_OP_DELETE 7
+#define SMB2_OP_HARDLINK 8
+#define SMB2_OP_SET_EOF 9
+
+/* Used when constructing chained read requests. */
+#define CHAINED_REQUEST 1
+#define START_OF_CHAIN 2
+#define END_OF_CHAIN 4
+#define RELATED_REQUEST 8
+
+#define SMB2_SIGNATURE_SIZE (16)
+#define SMB2_NTLMV2_SESSKEY_SIZE (16)
+#define SMB2_HMACSHA256_SIZE (32)
#endif /* _SMB2_GLOB_H */
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 2aa5cb08c526..706482452df4 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -47,6 +47,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
int rc, tmprc = 0;
u64 persistent_fid, volatile_fid;
__le16 *utf16_path;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path)
@@ -54,7 +55,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
desired_access, create_disposition, file_attributes,
- create_options);
+ create_options, &oplock, NULL);
if (rc) {
kfree(utf16_path);
return rc;
@@ -74,6 +75,22 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
* SMB2_open() call.
*/
break;
+ case SMB2_OP_RENAME:
+ tmprc = SMB2_rename(xid, tcon, persistent_fid, volatile_fid,
+ (__le16 *)data);
+ break;
+ case SMB2_OP_HARDLINK:
+ tmprc = SMB2_set_hardlink(xid, tcon, persistent_fid,
+ volatile_fid, (__le16 *)data);
+ break;
+ case SMB2_OP_SET_EOF:
+ tmprc = SMB2_set_eof(xid, tcon, persistent_fid, volatile_fid,
+ current->tgid, (__le64 *)data);
+ break;
+ case SMB2_OP_SET_INFO:
+ tmprc = SMB2_set_info(xid, tcon, persistent_fid, volatile_fid,
+ (FILE_BASIC_INFO *)data);
+ break;
default:
cERROR(1, "Invalid command");
break;
@@ -86,7 +103,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
}
-static void
+void
move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
{
memcpy(dst, src, (size_t)(&src->CurrentByteOffset) - (size_t)src);
@@ -161,3 +178,80 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
0, CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE,
NULL, SMB2_OP_DELETE);
}
+
+int
+smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
+ struct cifs_sb_info *cifs_sb)
+{
+ return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
+ 0, CREATE_DELETE_ON_CLOSE, NULL,
+ SMB2_OP_DELETE);
+}
+
+static int
+smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb, __u32 access, int command)
+{
+ __le16 *smb2_to_name = NULL;
+ int rc;
+
+ smb2_to_name = cifs_convert_path_to_utf16(to_name, cifs_sb);
+ if (smb2_to_name == NULL) {
+ rc = -ENOMEM;
+ goto smb2_rename_path;
+ }
+
+ rc = smb2_open_op_close(xid, tcon, cifs_sb, from_name, access,
+ FILE_OPEN, 0, 0, smb2_to_name, command);
+smb2_rename_path:
+ kfree(smb2_to_name);
+ return rc;
+}
+
+int
+smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb)
+{
+ return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
+ DELETE, SMB2_OP_RENAME);
+}
+
+int
+smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb)
+{
+ return smb2_set_path_attr(xid, tcon, from_name, to_name, cifs_sb,
+ FILE_READ_ATTRIBUTES, SMB2_OP_HARDLINK);
+}
+
+int
+smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *full_path, __u64 size,
+ struct cifs_sb_info *cifs_sb, bool set_alloc)
+{
+ __le64 eof = cpu_to_le64(size);
+ return smb2_open_op_close(xid, tcon, cifs_sb, full_path,
+ FILE_WRITE_DATA, FILE_OPEN, 0, 0, &eof,
+ SMB2_OP_SET_EOF);
+}
+
+int
+smb2_set_file_info(struct inode *inode, const char *full_path,
+ FILE_BASIC_INFO *buf, const unsigned int xid)
+{
+ struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ struct tcon_link *tlink;
+ int rc;
+
+ tlink = cifs_sb_tlink(cifs_sb);
+ if (IS_ERR(tlink))
+ return PTR_ERR(tlink);
+ rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path,
+ FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, 0, buf,
+ SMB2_OP_SET_INFO);
+ cifs_put_tlink(tlink);
+ return rc;
+}
diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c
index be41478acc05..494c912c76fe 100644
--- a/fs/cifs/smb2maperror.c
+++ b/fs/cifs/smb2maperror.c
@@ -453,7 +453,8 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
{STATUS_FILE_INVALID, -EIO, "STATUS_FILE_INVALID"},
{STATUS_ALLOTTED_SPACE_EXCEEDED, -EIO,
"STATUS_ALLOTTED_SPACE_EXCEEDED"},
- {STATUS_INSUFFICIENT_RESOURCES, -EIO, "STATUS_INSUFFICIENT_RESOURCES"},
+ {STATUS_INSUFFICIENT_RESOURCES, -EREMOTEIO,
+ "STATUS_INSUFFICIENT_RESOURCES"},
{STATUS_DFS_EXIT_PATH_FOUND, -EIO, "STATUS_DFS_EXIT_PATH_FOUND"},
{STATUS_DEVICE_DATA_ERROR, -EIO, "STATUS_DEVICE_DATA_ERROR"},
{STATUS_DEVICE_NOT_CONNECTED, -EIO, "STATUS_DEVICE_NOT_CONNECTED"},
@@ -2455,7 +2456,8 @@ map_smb2_to_linux_error(char *buf, bool log_err)
return 0;
/* mask facility */
- if (log_err && (smb2err != (STATUS_MORE_PROCESSING_REQUIRED)))
+ if (log_err && (smb2err != STATUS_MORE_PROCESSING_REQUIRED) &&
+ (smb2err != STATUS_END_OF_FILE))
smb2_print_status(smb2err);
else if (cifsFYI & CIFS_RC)
smb2_print_status(smb2err);
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index e4d3b9964167..7b1c5e3287fb 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -142,12 +142,19 @@ smb2_check_message(char *buf, unsigned int length)
}
if (smb2_rsp_struct_sizes[command] != pdu->StructureSize2) {
- if (hdr->Status == 0 ||
- pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2) {
+ if (command != SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0 ||
+ pdu->StructureSize2 != SMB2_ERROR_STRUCTURE_SIZE2)) {
/* error packets have 9 byte structure size */
cERROR(1, "Illegal response size %u for command %d",
le16_to_cpu(pdu->StructureSize2), command);
return 1;
+ } else if (command == SMB2_OPLOCK_BREAK_HE && (hdr->Status == 0)
+ && (le16_to_cpu(pdu->StructureSize2) != 44)
+ && (le16_to_cpu(pdu->StructureSize2) != 36)) {
+ /* special case for SMB2.1 lease break message */
+ cERROR(1, "Illegal response size %d for oplock break",
+ le16_to_cpu(pdu->StructureSize2));
+ return 1;
}
}
@@ -162,6 +169,9 @@ smb2_check_message(char *buf, unsigned int length)
if (4 + len != clc_len) {
cFYI(1, "Calculated size %u length %u mismatch mid %llu",
clc_len, 4 + len, mid);
+ /* Windows 7 server returns 24 bytes more */
+ if (clc_len + 20 == len && command == SMB2_OPLOCK_BREAK_HE)
+ return 0;
/* server can return one byte more */
if (clc_len == 4 + len + 1)
return 0;
@@ -244,7 +254,15 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
((struct smb2_query_info_rsp *)hdr)->OutputBufferLength);
break;
case SMB2_READ:
+ *off = ((struct smb2_read_rsp *)hdr)->DataOffset;
+ *len = le32_to_cpu(((struct smb2_read_rsp *)hdr)->DataLength);
+ break;
case SMB2_QUERY_DIRECTORY:
+ *off = le16_to_cpu(
+ ((struct smb2_query_directory_rsp *)hdr)->OutputBufferOffset);
+ *len = le32_to_cpu(
+ ((struct smb2_query_directory_rsp *)hdr)->OutputBufferLength);
+ break;
case SMB2_IOCTL:
case SMB2_CHANGE_NOTIFY:
default:
@@ -287,8 +305,9 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
* portion, the number of word parameters and the data portion of the message.
*/
unsigned int
-smb2_calc_size(struct smb2_hdr *hdr)
+smb2_calc_size(void *buf)
{
+ struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
struct smb2_pdu *pdu = (struct smb2_pdu *)hdr;
int offset; /* the offset from the beginning of SMB to data area */
int data_length; /* the length of the variable length data area */
@@ -347,3 +366,218 @@ cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
CIFS_MOUNT_MAP_SPECIAL_CHR);
return to;
}
+
+__le32
+smb2_get_lease_state(struct cifsInodeInfo *cinode)
+{
+ if (cinode->clientCanCacheAll)
+ return SMB2_LEASE_WRITE_CACHING | SMB2_LEASE_READ_CACHING;
+ else if (cinode->clientCanCacheRead)
+ return SMB2_LEASE_READ_CACHING;
+ return 0;
+}
+
+__u8 smb2_map_lease_to_oplock(__le32 lease_state)
+{
+ if (lease_state & SMB2_LEASE_WRITE_CACHING) {
+ if (lease_state & SMB2_LEASE_HANDLE_CACHING)
+ return SMB2_OPLOCK_LEVEL_BATCH;
+ else
+ return SMB2_OPLOCK_LEVEL_EXCLUSIVE;
+ } else if (lease_state & SMB2_LEASE_READ_CACHING)
+ return SMB2_OPLOCK_LEVEL_II;
+ return 0;
+}
+
+struct smb2_lease_break_work {
+ struct work_struct lease_break;
+ struct tcon_link *tlink;
+ __u8 lease_key[16];
+ __le32 lease_state;
+};
+
+static void
+cifs_ses_oplock_break(struct work_struct *work)
+{
+ struct smb2_lease_break_work *lw = container_of(work,
+ struct smb2_lease_break_work, lease_break);
+ int rc;
+
+ rc = SMB2_lease_break(0, tlink_tcon(lw->tlink), lw->lease_key,
+ lw->lease_state);
+ cFYI(1, "Lease release rc %d", rc);
+ cifs_put_tlink(lw->tlink);
+ kfree(lw);
+}
+
+static bool
+smb2_is_valid_lease_break(char *buffer, struct TCP_Server_Info *server)
+{
+ struct smb2_lease_break *rsp = (struct smb2_lease_break *)buffer;
+ struct list_head *tmp, *tmp1, *tmp2;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ struct cifsInodeInfo *cinode;
+ struct cifsFileInfo *cfile;
+ struct cifs_pending_open *open;
+ struct smb2_lease_break_work *lw;
+ bool found;
+ int ack_req = le32_to_cpu(rsp->Flags &
+ SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED);
+
+ lw = kmalloc(sizeof(struct smb2_lease_break_work), GFP_KERNEL);
+ if (!lw) {
+ cERROR(1, "Memory allocation failed during lease break check");
+ return false;
+ }
+
+ INIT_WORK(&lw->lease_break, cifs_ses_oplock_break);
+ lw->lease_state = rsp->NewLeaseState;
+
+ cFYI(1, "Checking for lease break");
+
+ /* look up tcon based on tid & uid */
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &server->smb_ses_list) {
+ ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+
+ spin_lock(&cifs_file_list_lock);
+ list_for_each(tmp1, &ses->tcon_list) {
+ tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
+
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
+ list_for_each(tmp2, &tcon->openFileList) {
+ cfile = list_entry(tmp2, struct cifsFileInfo,
+ tlist);
+ cinode = CIFS_I(cfile->dentry->d_inode);
+
+ if (memcmp(cinode->lease_key, rsp->LeaseKey,
+ SMB2_LEASE_KEY_SIZE))
+ continue;
+
+ cFYI(1, "found in the open list");
+ cFYI(1, "lease key match, lease break 0x%d",
+ le32_to_cpu(rsp->NewLeaseState));
+
+ smb2_set_oplock_level(cinode,
+ smb2_map_lease_to_oplock(rsp->NewLeaseState));
+
+ if (ack_req)
+ cfile->oplock_break_cancelled = false;
+ else
+ cfile->oplock_break_cancelled = true;
+
+ queue_work(cifsiod_wq, &cfile->oplock_break);
+
+ spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
+
+ found = false;
+ list_for_each_entry(open, &tcon->pending_opens, olist) {
+ if (memcmp(open->lease_key, rsp->LeaseKey,
+ SMB2_LEASE_KEY_SIZE))
+ continue;
+
+ if (!found && ack_req) {
+ found = true;
+ memcpy(lw->lease_key, open->lease_key,
+ SMB2_LEASE_KEY_SIZE);
+ lw->tlink = cifs_get_tlink(open->tlink);
+ queue_work(cifsiod_wq,
+ &lw->lease_break);
+ }
+
+ cFYI(1, "found in the pending open list");
+ cFYI(1, "lease key match, lease break 0x%d",
+ le32_to_cpu(rsp->NewLeaseState));
+
+ open->oplock =
+ smb2_map_lease_to_oplock(rsp->NewLeaseState);
+ }
+ if (found) {
+ spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
+ }
+ spin_unlock(&cifs_file_list_lock);
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ kfree(lw);
+ cFYI(1, "Can not process lease break - no lease matched");
+ return false;
+}
+
+bool
+smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)
+{
+ struct smb2_oplock_break *rsp = (struct smb2_oplock_break *)buffer;
+ struct list_head *tmp, *tmp1, *tmp2;
+ struct cifs_ses *ses;
+ struct cifs_tcon *tcon;
+ struct cifsInodeInfo *cinode;
+ struct cifsFileInfo *cfile;
+
+ cFYI(1, "Checking for oplock break");
+
+ if (rsp->hdr.Command != SMB2_OPLOCK_BREAK)
+ return false;
+
+ if (rsp->StructureSize !=
+ smb2_rsp_struct_sizes[SMB2_OPLOCK_BREAK_HE]) {
+ if (le16_to_cpu(rsp->StructureSize) == 44)
+ return smb2_is_valid_lease_break(buffer, server);
+ else
+ return false;
+ }
+
+ cFYI(1, "oplock level 0x%d", rsp->OplockLevel);
+
+ /* look up tcon based on tid & uid */
+ spin_lock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &server->smb_ses_list) {
+ ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+ list_for_each(tmp1, &ses->tcon_list) {
+ tcon = list_entry(tmp1, struct cifs_tcon, tcon_list);
+
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_oplock_brks);
+ spin_lock(&cifs_file_list_lock);
+ list_for_each(tmp2, &tcon->openFileList) {
+ cfile = list_entry(tmp2, struct cifsFileInfo,
+ tlist);
+ if (rsp->PersistentFid !=
+ cfile->fid.persistent_fid ||
+ rsp->VolatileFid !=
+ cfile->fid.volatile_fid)
+ continue;
+
+ cFYI(1, "file id match, oplock break");
+ cinode = CIFS_I(cfile->dentry->d_inode);
+
+ if (!cinode->clientCanCacheAll &&
+ rsp->OplockLevel == SMB2_OPLOCK_LEVEL_NONE)
+ cfile->oplock_break_cancelled = true;
+ else
+ cfile->oplock_break_cancelled = false;
+
+ smb2_set_oplock_level(cinode,
+ rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0);
+
+ queue_work(cifsiod_wq, &cfile->oplock_break);
+
+ spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ return true;
+ }
+ spin_unlock(&cifs_file_list_lock);
+ spin_unlock(&cifs_tcp_ses_lock);
+ cFYI(1, "No matching file for oplock break");
+ return true;
+ }
+ }
+ spin_unlock(&cifs_tcp_ses_lock);
+ cFYI(1, "Can not process oplock break for non-existent connection");
+ return false;
+}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 826209bf3684..4d9dbe0b7385 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -17,11 +17,15 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+#include <linux/pagemap.h>
+#include <linux/vfs.h>
#include "cifsglob.h"
#include "smb2pdu.h"
#include "smb2proto.h"
#include "cifsproto.h"
#include "cifs_debug.h"
+#include "smb2status.h"
+#include "smb2glob.h"
static int
change_conf(struct TCP_Server_Info *server)
@@ -63,6 +67,17 @@ smb2_add_credits(struct TCP_Server_Info *server, const unsigned int add,
server->in_flight--;
if (server->in_flight == 0 && (optype & CIFS_OP_MASK) != CIFS_NEG_OP)
rc = change_conf(server);
+ /*
+ * Sometimes server returns 0 credits on oplock break ack - we need to
+ * rebalance credits in this case.
+ */
+ else if (server->in_flight > 0 && server->oplock_credits == 0 &&
+ server->oplocks) {
+ if (server->credits > 1) {
+ server->credits--;
+ server->oplock_credits++;
+ }
+ }
spin_unlock(&server->req_lock);
wake_up(&server->request_q);
if (rc)
@@ -157,6 +172,42 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
return rc;
}
+static unsigned int
+smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
+{
+ struct TCP_Server_Info *server = tcon->ses->server;
+ unsigned int wsize;
+
+ /* start with specified wsize, or default */
+ wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE;
+ wsize = min_t(unsigned int, wsize, server->max_write);
+ /*
+ * limit write size to 2 ** 16, because we don't support multicredit
+ * requests now.
+ */
+ wsize = min_t(unsigned int, wsize, 2 << 15);
+
+ return wsize;
+}
+
+static unsigned int
+smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)
+{
+ struct TCP_Server_Info *server = tcon->ses->server;
+ unsigned int rsize;
+
+ /* start with specified rsize, or default */
+ rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE;
+ rsize = min_t(unsigned int, rsize, server->max_read);
+ /*
+ * limit write size to 2 ** 16, because we don't support multicredit
+ * requests now.
+ */
+ rsize = min_t(unsigned int, rsize, 2 << 15);
+
+ return rsize;
+}
+
static int
smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path)
@@ -164,13 +215,14 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
int rc;
__u64 persistent_fid, volatile_fid;
__le16 *utf16_path;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path)
return -ENOMEM;
rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
- FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0);
+ FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL);
if (rc) {
kfree(utf16_path);
return rc;
@@ -190,6 +242,26 @@ smb2_get_srv_inum(const unsigned int xid, struct cifs_tcon *tcon,
return 0;
}
+static int
+smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid, FILE_ALL_INFO *data)
+{
+ int rc;
+ struct smb2_file_all_info *smb2_data;
+
+ smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2,
+ GFP_KERNEL);
+ if (smb2_data == NULL)
+ return -ENOMEM;
+
+ rc = SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid,
+ smb2_data);
+ if (!rc)
+ move_smb2_info_to_cifs(data, smb2_data);
+ kfree(smb2_data);
+ return rc;
+}
+
static char *
smb2_build_path_to_root(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
struct cifs_tcon *tcon)
@@ -292,7 +364,221 @@ smb2_print_stats(struct seq_file *m, struct cifs_tcon *tcon)
#endif
}
+static void
+smb2_set_fid(struct cifsFileInfo *cfile, struct cifs_fid *fid, __u32 oplock)
+{
+ struct cifsInodeInfo *cinode = CIFS_I(cfile->dentry->d_inode);
+ cfile->fid.persistent_fid = fid->persistent_fid;
+ cfile->fid.volatile_fid = fid->volatile_fid;
+ smb2_set_oplock_level(cinode, oplock);
+ cinode->can_cache_brlcks = cinode->clientCanCacheAll;
+}
+
+static void
+smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
+}
+
+static int
+smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ return SMB2_flush(xid, tcon, fid->persistent_fid, fid->volatile_fid);
+}
+
+static unsigned int
+smb2_read_data_offset(char *buf)
+{
+ struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf;
+ return rsp->DataOffset;
+}
+
+static unsigned int
+smb2_read_data_length(char *buf)
+{
+ struct smb2_read_rsp *rsp = (struct smb2_read_rsp *)buf;
+ return le32_to_cpu(rsp->DataLength);
+}
+
+
+static int
+smb2_sync_read(const unsigned int xid, struct cifsFileInfo *cfile,
+ struct cifs_io_parms *parms, unsigned int *bytes_read,
+ char **buf, int *buf_type)
+{
+ parms->persistent_fid = cfile->fid.persistent_fid;
+ parms->volatile_fid = cfile->fid.volatile_fid;
+ return SMB2_read(xid, parms, bytes_read, buf, buf_type);
+}
+
+static int
+smb2_sync_write(const unsigned int xid, struct cifsFileInfo *cfile,
+ struct cifs_io_parms *parms, unsigned int *written,
+ struct kvec *iov, unsigned long nr_segs)
+{
+
+ parms->persistent_fid = cfile->fid.persistent_fid;
+ parms->volatile_fid = cfile->fid.volatile_fid;
+ return SMB2_write(xid, parms, written, iov, nr_segs);
+}
+
+static int
+smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifsFileInfo *cfile, __u64 size, bool set_alloc)
+{
+ __le64 eof = cpu_to_le64(size);
+ return SMB2_set_eof(xid, tcon, cfile->fid.persistent_fid,
+ cfile->fid.volatile_fid, cfile->pid, &eof);
+}
+
+static int
+smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *path, struct cifs_sb_info *cifs_sb,
+ struct cifs_fid *fid, __u16 search_flags,
+ struct cifs_search_info *srch_inf)
+{
+ __le16 *utf16_path;
+ int rc;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+ __u64 persistent_fid, volatile_fid;
+
+ utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+ if (!utf16_path)
+ return -ENOMEM;
+
+ rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
+ FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_OPEN, 0, 0,
+ &oplock, NULL);
+ kfree(utf16_path);
+ if (rc) {
+ cERROR(1, "open dir failed");
+ return rc;
+ }
+
+ srch_inf->entries_in_buffer = 0;
+ srch_inf->index_of_last_entry = 0;
+ fid->persistent_fid = persistent_fid;
+ fid->volatile_fid = volatile_fid;
+
+ rc = SMB2_query_directory(xid, tcon, persistent_fid, volatile_fid, 0,
+ srch_inf);
+ if (rc) {
+ cERROR(1, "query directory failed");
+ SMB2_close(xid, tcon, persistent_fid, volatile_fid);
+ }
+ return rc;
+}
+
+static int
+smb2_query_dir_next(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid, __u16 search_flags,
+ struct cifs_search_info *srch_inf)
+{
+ return SMB2_query_directory(xid, tcon, fid->persistent_fid,
+ fid->volatile_fid, 0, srch_inf);
+}
+
+static int
+smb2_close_dir(const unsigned int xid, struct cifs_tcon *tcon,
+ struct cifs_fid *fid)
+{
+ return SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
+}
+
+/*
+* If we negotiate SMB2 protocol and get STATUS_PENDING - update
+* the number of credits and return true. Otherwise - return false.
+*/
+static bool
+smb2_is_status_pending(char *buf, struct TCP_Server_Info *server, int length)
+{
+ struct smb2_hdr *hdr = (struct smb2_hdr *)buf;
+
+ if (hdr->Status != STATUS_PENDING)
+ return false;
+
+ if (!length) {
+ spin_lock(&server->req_lock);
+ server->credits += le16_to_cpu(hdr->CreditRequest);
+ spin_unlock(&server->req_lock);
+ wake_up(&server->request_q);
+ }
+
+ return true;
+}
+
+static int
+smb2_oplock_response(struct cifs_tcon *tcon, struct cifs_fid *fid,
+ struct cifsInodeInfo *cinode)
+{
+ if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
+ return SMB2_lease_break(0, tcon, cinode->lease_key,
+ smb2_get_lease_state(cinode));
+
+ return SMB2_oplock_break(0, tcon, fid->persistent_fid,
+ fid->volatile_fid,
+ cinode->clientCanCacheRead ? 1 : 0);
+}
+
+static int
+smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
+ struct kstatfs *buf)
+{
+ int rc;
+ u64 persistent_fid, volatile_fid;
+ __le16 srch_path = 0; /* Null - open root of share */
+ u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
+
+ rc = SMB2_open(xid, tcon, &srch_path, &persistent_fid, &volatile_fid,
+ FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL);
+ if (rc)
+ return rc;
+ buf->f_type = SMB2_MAGIC_NUMBER;
+ rc = SMB2_QFS_info(xid, tcon, persistent_fid, volatile_fid, buf);
+ SMB2_close(xid, tcon, persistent_fid, volatile_fid);
+ return rc;
+}
+
+static bool
+smb2_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
+{
+ return ob1->fid.persistent_fid == ob2->fid.persistent_fid &&
+ ob1->fid.volatile_fid == ob2->fid.volatile_fid;
+}
+
+static int
+smb2_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,
+ __u64 length, __u32 type, int lock, int unlock, bool wait)
+{
+ if (unlock && !lock)
+ type = SMB2_LOCKFLAG_UNLOCK;
+ return SMB2_lock(xid, tlink_tcon(cfile->tlink),
+ cfile->fid.persistent_fid, cfile->fid.volatile_fid,
+ current->tgid, length, offset, type, wait);
+}
+
+static void
+smb2_get_lease_key(struct inode *inode, struct cifs_fid *fid)
+{
+ memcpy(fid->lease_key, CIFS_I(inode)->lease_key, SMB2_LEASE_KEY_SIZE);
+}
+
+static void
+smb2_set_lease_key(struct inode *inode, struct cifs_fid *fid)
+{
+ memcpy(CIFS_I(inode)->lease_key, fid->lease_key, SMB2_LEASE_KEY_SIZE);
+}
+
+static void
+smb2_new_lease_key(struct cifs_fid *fid)
+{
+ get_random_bytes(fid->lease_key, SMB2_LEASE_KEY_SIZE);
+}
+
struct smb_version_operations smb21_operations = {
+ .compare_fids = smb2_compare_fids,
.setup_request = smb2_setup_request,
.setup_async_request = smb2_setup_async_request,
.check_receive = smb2_check_receive,
@@ -301,13 +587,19 @@ struct smb_version_operations smb21_operations = {
.get_credits_field = smb2_get_credits_field,
.get_credits = smb2_get_credits,
.get_next_mid = smb2_get_next_mid,
+ .read_data_offset = smb2_read_data_offset,
+ .read_data_length = smb2_read_data_length,
+ .map_error = map_smb2_to_linux_error,
.find_mid = smb2_find_mid,
.check_message = smb2_check_message,
.dump_detail = smb2_dump_detail,
.clear_stats = smb2_clear_stats,
.print_stats = smb2_print_stats,
+ .is_oplock_break = smb2_is_valid_oplock_break,
.need_neg = smb2_need_neg,
.negotiate = smb2_negotiate,
+ .negotiate_wsize = smb2_negotiate_wsize,
+ .negotiate_rsize = smb2_negotiate_rsize,
.sess_setup = SMB2_sess_setup,
.logoff = SMB2_logoff,
.tree_connect = SMB2_tcon,
@@ -317,16 +609,68 @@ struct smb_version_operations smb21_operations = {
.echo = SMB2_echo,
.query_path_info = smb2_query_path_info,
.get_srv_inum = smb2_get_srv_inum,
+ .query_file_info = smb2_query_file_info,
+ .set_path_size = smb2_set_path_size,
+ .set_file_size = smb2_set_file_size,
+ .set_file_info = smb2_set_file_info,
.build_path_to_root = smb2_build_path_to_root,
.mkdir = smb2_mkdir,
.mkdir_setinfo = smb2_mkdir_setinfo,
.rmdir = smb2_rmdir,
+ .unlink = smb2_unlink,
+ .rename = smb2_rename_path,
+ .create_hardlink = smb2_create_hardlink,
+ .open = smb2_open_file,
+ .set_fid = smb2_set_fid,
+ .close = smb2_close_file,
+ .flush = smb2_flush_file,
+ .async_readv = smb2_async_readv,
+ .async_writev = smb2_async_writev,
+ .sync_read = smb2_sync_read,
+ .sync_write = smb2_sync_write,
+ .query_dir_first = smb2_query_dir_first,
+ .query_dir_next = smb2_query_dir_next,
+ .close_dir = smb2_close_dir,
+ .calc_smb_size = smb2_calc_size,
+ .is_status_pending = smb2_is_status_pending,
+ .oplock_response = smb2_oplock_response,
+ .queryfs = smb2_queryfs,
+ .mand_lock = smb2_mand_lock,
+ .mand_unlock_range = smb2_unlock_range,
+ .push_mand_locks = smb2_push_mandatory_locks,
+ .get_lease_key = smb2_get_lease_key,
+ .set_lease_key = smb2_set_lease_key,
+ .new_lease_key = smb2_new_lease_key,
};
struct smb_version_values smb21_values = {
.version_string = SMB21_VERSION_STRING,
+ .protocol_id = SMB21_PROT_ID,
+ .req_capabilities = 0, /* MBZ on negotiate req until SMB3 dialect */
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+ .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
+ .header_size = sizeof(struct smb2_hdr),
+ .max_header_size = MAX_SMB2_HDR_SIZE,
+ .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
+ .lock_cmd = SMB2_LOCK,
+ .cap_unix = 0,
+ .cap_nt_find = SMB2_NT_FIND,
+ .cap_large_files = SMB2_LARGE_FILES,
+};
+
+struct smb_version_values smb30_values = {
+ .version_string = SMB30_VERSION_STRING,
+ .protocol_id = SMB30_PROT_ID,
+ .req_capabilities = SMB2_GLOBAL_CAP_DFS | SMB2_GLOBAL_CAP_LEASING | SMB2_GLOBAL_CAP_LARGE_MTU,
+ .large_lock_type = 0,
+ .exclusive_lock_type = SMB2_LOCKFLAG_EXCLUSIVE_LOCK,
+ .shared_lock_type = SMB2_LOCKFLAG_SHARED_LOCK,
+ .unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
.header_size = sizeof(struct smb2_hdr),
.max_header_size = MAX_SMB2_HDR_SIZE,
+ .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
.lock_cmd = SMB2_LOCK,
.cap_unix = 0,
.cap_nt_find = SMB2_NT_FIND,
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 62b3f17d0613..cf33622cdac8 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -1,7 +1,7 @@
/*
* fs/cifs/smb2pdu.c
*
- * Copyright (C) International Business Machines Corp., 2009, 2011
+ * Copyright (C) International Business Machines Corp., 2009, 2012
* Etersoft, 2012
* Author(s): Steve French (sfrench@us.ibm.com)
* Pavel Shilovsky (pshilovsky@samba.org) 2012
@@ -31,7 +31,9 @@
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/vfs.h>
+#include <linux/task_io_accounting_ops.h>
#include <linux/uaccess.h>
+#include <linux/pagemap.h>
#include <linux/xattr.h>
#include "smb2pdu.h"
#include "cifsglob.h"
@@ -42,6 +44,8 @@
#include "cifs_debug.h"
#include "ntlmssp.h"
#include "smb2status.h"
+#include "smb2glob.h"
+#include "cifspdu.h"
/*
* The following table defines the expected "StructureSize" of SMB2 requests
@@ -115,9 +119,9 @@ smb2_hdr_assemble(struct smb2_hdr *hdr, __le16 smb2_cmd /* command */ ,
/* BB how does SMB2 do case sensitive? */
/* if (tcon->nocase)
hdr->Flags |= SMBFLG_CASELESS; */
- /* if (tcon->ses && tcon->ses->server &&
+ if (tcon->ses && tcon->ses->server &&
(tcon->ses->server->sec_mode & SECMODE_SIGN_REQUIRED))
- hdr->Flags |= SMB2_FLAGS_SIGNED; */
+ hdr->Flags |= SMB2_FLAGS_SIGNED;
out:
pdu->StructureSize2 = cpu_to_le16(parmsize);
return;
@@ -300,24 +304,6 @@ free_rsp_buf(int resp_buftype, void *rsp)
cifs_buf_release(rsp);
}
-#define SMB2_NUM_PROT 1
-
-#define SMB2_PROT 0
-#define SMB21_PROT 1
-#define BAD_PROT 0xFFFF
-
-#define SMB2_PROT_ID 0x0202
-#define SMB21_PROT_ID 0x0210
-#define BAD_PROT_ID 0xFFFF
-
-static struct {
- int index;
- __le16 name;
-} smb2protocols[] = {
- {SMB2_PROT, cpu_to_le16(SMB2_PROT_ID)},
- {SMB21_PROT, cpu_to_le16(SMB21_PROT_ID)},
- {BAD_PROT, cpu_to_le16(BAD_PROT_ID)}
-};
/*
*
@@ -344,7 +330,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
int resp_buftype;
struct TCP_Server_Info *server;
unsigned int sec_flags;
- u16 i;
u16 temp = 0;
int blob_offset, blob_length;
char *security_blob;
@@ -373,11 +358,10 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
req->hdr.SessionId = 0;
- for (i = 0; i < SMB2_NUM_PROT; i++)
- req->Dialects[i] = smb2protocols[i].name;
+ req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
- req->DialectCount = cpu_to_le16(i);
- inc_rfc1001_len(req, i * 2);
+ req->DialectCount = cpu_to_le16(1); /* One vers= at a time for now */
+ inc_rfc1001_len(req, 2);
/* only one of SMB2 signing flags may be set in SMB2 request */
if ((sec_flags & CIFSSEC_MUST_SIGN) == CIFSSEC_MUST_SIGN)
@@ -387,7 +371,9 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
req->SecurityMode = cpu_to_le16(temp);
- req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS);
+ req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities);
+
+ memcpy(req->ClientGUID, cifs_client_guid, SMB2_CLIENT_GUID_SIZE);
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
@@ -403,17 +389,16 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
if (rc != 0)
goto neg_exit;
- if (rsp == NULL) {
- rc = -EIO;
- goto neg_exit;
- }
-
cFYI(1, "mode 0x%x", rsp->SecurityMode);
- if (rsp->DialectRevision == smb2protocols[SMB21_PROT].name)
+ /* BB we may eventually want to match the negotiated vs. requested
+ dialect, even though we are only requesting one at a time */
+ if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID))
+ cFYI(1, "negotiated smb2.0 dialect");
+ else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID))
cFYI(1, "negotiated smb2.1 dialect");
- else if (rsp->DialectRevision == smb2protocols[SMB2_PROT].name)
- cFYI(1, "negotiated smb2 dialect");
+ else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID))
+ cFYI(1, "negotiated smb3.0 dialect");
else {
cERROR(1, "Illegal dialect returned by server %d",
le16_to_cpu(rsp->DialectRevision));
@@ -438,6 +423,38 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
rc = -EIO;
goto neg_exit;
}
+
+ cFYI(1, "sec_flags 0x%x", sec_flags);
+ if (sec_flags & CIFSSEC_MUST_SIGN) {
+ cFYI(1, "Signing required");
+ if (!(server->sec_mode & (SMB2_NEGOTIATE_SIGNING_REQUIRED |
+ SMB2_NEGOTIATE_SIGNING_ENABLED))) {
+ cERROR(1, "signing required but server lacks support");
+ rc = -EOPNOTSUPP;
+ goto neg_exit;
+ }
+ server->sec_mode |= SECMODE_SIGN_REQUIRED;
+ } else if (sec_flags & CIFSSEC_MAY_SIGN) {
+ cFYI(1, "Signing optional");
+ if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+ cFYI(1, "Server requires signing");
+ server->sec_mode |= SECMODE_SIGN_REQUIRED;
+ } else {
+ server->sec_mode &=
+ ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
+ }
+ } else {
+ cFYI(1, "Signing disabled");
+ if (server->sec_mode & SMB2_NEGOTIATE_SIGNING_REQUIRED) {
+ cERROR(1, "Server requires packet signing to be enabled"
+ " in /proc/fs/cifs/SecurityFlags.");
+ rc = -EOPNOTSUPP;
+ goto neg_exit;
+ }
+ server->sec_mode &=
+ ~(SECMODE_SIGN_ENABLED | SECMODE_SIGN_REQUIRED);
+ }
+
#ifdef CONFIG_SMB2_ASN1 /* BB REMOVEME when updated asn1.c ready */
rc = decode_neg_token_init(security_blob, blob_length,
&server->sec_type);
@@ -599,13 +616,14 @@ ssetup_ntlmssp_authenticate:
kfree(security_blob);
rsp = (struct smb2_sess_setup_rsp *)iov[0].iov_base;
- if (rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
+ if (resp_buftype != CIFS_NO_BUFFER &&
+ rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED) {
if (phase != NtLmNegotiate) {
cERROR(1, "Unexpected more processing error");
goto ssetup_exit;
}
if (offsetof(struct smb2_sess_setup_rsp, Buffer) - 4 !=
- le16_to_cpu(rsp->SecurityBufferOffset)) {
+ le16_to_cpu(rsp->SecurityBufferOffset)) {
cERROR(1, "Invalid security buffer offset %d",
le16_to_cpu(rsp->SecurityBufferOffset));
rc = -EIO;
@@ -631,11 +649,6 @@ ssetup_ntlmssp_authenticate:
if (rc != 0)
goto ssetup_exit;
- if (rsp == NULL) {
- rc = -EIO;
- goto ssetup_exit;
- }
-
ses->session_flags = le16_to_cpu(rsp->SessionFlags);
ssetup_exit:
free_rsp_buf(resp_buftype, rsp);
@@ -666,6 +679,8 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
/* since no tcon, smb2_init can not do this, so do here */
req->hdr.SessionId = ses->Suid;
+ if (server->sec_mode & SECMODE_SIGN_REQUIRED)
+ req->hdr.Flags |= SMB2_FLAGS_SIGNED;
rc = SendReceiveNoRsp(xid, ses, (char *) &req->hdr, 0);
/*
@@ -753,11 +768,6 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
goto tcon_error_exit;
}
- if (rsp == NULL) {
- rc = -EIO;
- goto tcon_exit;
- }
-
if (tcon == NULL) {
ses->ipc_tid = rsp->hdr.TreeId;
goto tcon_exit;
@@ -830,18 +840,87 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
return rc;
}
+static struct create_lease *
+create_lease_buf(u8 *lease_key, u8 oplock)
+{
+ struct create_lease *buf;
+
+ buf = kmalloc(sizeof(struct create_lease), GFP_KERNEL);
+ if (!buf)
+ return NULL;
+
+ memset(buf, 0, sizeof(struct create_lease));
+
+ buf->lcontext.LeaseKeyLow = cpu_to_le64(*((u64 *)lease_key));
+ buf->lcontext.LeaseKeyHigh = cpu_to_le64(*((u64 *)(lease_key + 8)));
+ if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE)
+ buf->lcontext.LeaseState = SMB2_LEASE_WRITE_CACHING |
+ SMB2_LEASE_READ_CACHING;
+ else if (oplock == SMB2_OPLOCK_LEVEL_II)
+ buf->lcontext.LeaseState = SMB2_LEASE_READ_CACHING;
+ else if (oplock == SMB2_OPLOCK_LEVEL_BATCH)
+ buf->lcontext.LeaseState = SMB2_LEASE_HANDLE_CACHING |
+ SMB2_LEASE_READ_CACHING |
+ SMB2_LEASE_WRITE_CACHING;
+
+ buf->ccontext.DataOffset = cpu_to_le16(offsetof
+ (struct create_lease, lcontext));
+ buf->ccontext.DataLength = cpu_to_le32(sizeof(struct lease_context));
+ buf->ccontext.NameOffset = cpu_to_le16(offsetof
+ (struct create_lease, Name));
+ buf->ccontext.NameLength = cpu_to_le16(4);
+ buf->Name[0] = 'R';
+ buf->Name[1] = 'q';
+ buf->Name[2] = 'L';
+ buf->Name[3] = 's';
+ return buf;
+}
+
+static __u8
+parse_lease_state(struct smb2_create_rsp *rsp)
+{
+ char *data_offset;
+ struct create_lease *lc;
+ bool found = false;
+
+ data_offset = (char *)rsp;
+ data_offset += 4 + le32_to_cpu(rsp->CreateContextsOffset);
+ lc = (struct create_lease *)data_offset;
+ do {
+ char *name = le16_to_cpu(lc->ccontext.NameOffset) + (char *)lc;
+ if (le16_to_cpu(lc->ccontext.NameLength) != 4 ||
+ strncmp(name, "RqLs", 4)) {
+ lc = (struct create_lease *)((char *)lc
+ + le32_to_cpu(lc->ccontext.Next));
+ continue;
+ }
+ if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
+ return SMB2_OPLOCK_LEVEL_NOCHANGE;
+ found = true;
+ break;
+ } while (le32_to_cpu(lc->ccontext.Next) != 0);
+
+ if (!found)
+ return 0;
+
+ return smb2_map_lease_to_oplock(lc->lcontext.LeaseState);
+}
+
int
SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access,
- __u32 create_disposition, __u32 file_attributes, __u32 create_options)
+ __u32 create_disposition, __u32 file_attributes, __u32 create_options,
+ __u8 *oplock, struct smb2_file_all_info *buf)
{
struct smb2_create_req *req;
struct smb2_create_rsp *rsp;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
- struct kvec iov[2];
+ struct kvec iov[3];
int resp_buftype;
int uni_path_len;
+ __le16 *copy_path = NULL;
+ int copy_size;
int rc = 0;
int num_iovecs = 2;
@@ -856,10 +935,6 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
if (rc)
return rc;
- if (enable_oplocks)
- req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
- else
- req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
req->ImpersonationLevel = IL_IMPERSONATION;
req->DesiredAccess = cpu_to_le32(desired_access);
/* File attributes ignored on open (used in create though) */
@@ -869,7 +944,7 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
req->CreateOptions = cpu_to_le32(create_options);
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)
- - 1 /* pad */ - 4 /* do not count rfc1001 len field */);
+ - 8 /* pad */ - 4 /* do not count rfc1001 len field */);
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
@@ -880,6 +955,20 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
req->NameLength = cpu_to_le16(uni_path_len - 2);
/* -1 since last byte is buf[0] which is sent below (path) */
iov[0].iov_len--;
+ if (uni_path_len % 8 != 0) {
+ copy_size = uni_path_len / 8 * 8;
+ if (copy_size < uni_path_len)
+ copy_size += 8;
+
+ copy_path = kzalloc(copy_size, GFP_KERNEL);
+ if (!copy_path)
+ return -ENOMEM;
+ memcpy((char *)copy_path, (const char *)path,
+ uni_path_len);
+ uni_path_len = copy_size;
+ path = copy_path;
+ }
+
iov[1].iov_len = uni_path_len;
iov[1].iov_base = path;
/*
@@ -888,10 +977,37 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
*/
inc_rfc1001_len(req, uni_path_len - 1);
} else {
+ iov[0].iov_len += 7;
+ req->hdr.smb2_buf_length = cpu_to_be32(be32_to_cpu(
+ req->hdr.smb2_buf_length) + 8 - 1);
num_iovecs = 1;
req->NameLength = 0;
}
+ if (!server->oplocks)
+ *oplock = SMB2_OPLOCK_LEVEL_NONE;
+
+ if (!(tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) ||
+ *oplock == SMB2_OPLOCK_LEVEL_NONE)
+ req->RequestedOplockLevel = *oplock;
+ else {
+ iov[num_iovecs].iov_base = create_lease_buf(oplock+1, *oplock);
+ if (iov[num_iovecs].iov_base == NULL) {
+ cifs_small_buf_release(req);
+ kfree(copy_path);
+ return -ENOMEM;
+ }
+ iov[num_iovecs].iov_len = sizeof(struct create_lease);
+ req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
+ req->CreateContextsOffset = cpu_to_le32(
+ sizeof(struct smb2_create_req) - 4 - 8 +
+ iov[num_iovecs-1].iov_len);
+ req->CreateContextsLength = cpu_to_le32(
+ sizeof(struct create_lease));
+ inc_rfc1001_len(&req->hdr, sizeof(struct create_lease));
+ num_iovecs++;
+ }
+
rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
rsp = (struct smb2_create_rsp *)iov[0].iov_base;
@@ -900,13 +1016,24 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
goto creat_exit;
}
- if (rsp == NULL) {
- rc = -EIO;
- goto creat_exit;
- }
*persistent_fid = rsp->PersistentFileId;
*volatile_fid = rsp->VolatileFileId;
+
+ if (buf) {
+ memcpy(buf, &rsp->CreationTime, 32);
+ buf->AllocationSize = rsp->AllocationSize;
+ buf->EndOfFile = rsp->EndofFile;
+ buf->Attributes = rsp->FileAttributes;
+ buf->NumberOfLinks = cpu_to_le32(1);
+ buf->DeletePending = 0;
+ }
+
+ if (rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE)
+ *oplock = parse_lease_state(rsp);
+ else
+ *oplock = rsp->OplockLevel;
creat_exit:
+ kfree(copy_path);
free_rsp_buf(resp_buftype, rsp);
return rc;
}
@@ -950,11 +1077,6 @@ SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
goto close_exit;
}
- if (rsp == NULL) {
- rc = -EIO;
- goto close_exit;
- }
-
/* BB FIXME - decode close response, update inode for caching */
close_exit:
@@ -1019,10 +1141,10 @@ validate_and_copy_buf(unsigned int offset, unsigned int buffer_length,
return 0;
}
-int
-SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
- u64 persistent_fid, u64 volatile_fid,
- struct smb2_file_all_info *data)
+static int
+query_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u8 info_class,
+ size_t output_len, size_t min_len, void *data)
{
struct smb2_query_info_req *req;
struct smb2_query_info_rsp *rsp = NULL;
@@ -1044,37 +1166,56 @@ SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
return rc;
req->InfoType = SMB2_O_INFO_FILE;
- req->FileInfoClass = FILE_ALL_INFORMATION;
+ req->FileInfoClass = info_class;
req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid;
/* 4 for rfc1002 length field and 1 for Buffer */
req->InputBufferOffset =
cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4);
- req->OutputBufferLength =
- cpu_to_le32(sizeof(struct smb2_file_all_info) + MAX_NAME * 2);
+ req->OutputBufferLength = cpu_to_le32(output_len);
iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;
rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0);
+ rsp = (struct smb2_query_info_rsp *)iov[0].iov_base;
+
if (rc) {
cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
goto qinf_exit;
}
- rsp = (struct smb2_query_info_rsp *)iov[0].iov_base;
-
rc = validate_and_copy_buf(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength),
- &rsp->hdr, sizeof(struct smb2_file_all_info),
- (char *)data);
+ &rsp->hdr, min_len, data);
qinf_exit:
free_rsp_buf(resp_buftype, rsp);
return rc;
}
+int
+SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ struct smb2_file_all_info *data)
+{
+ return query_info(xid, tcon, persistent_fid, volatile_fid,
+ FILE_ALL_INFORMATION,
+ sizeof(struct smb2_file_all_info) + MAX_NAME * 2,
+ sizeof(struct smb2_file_all_info), data);
+}
+
+int
+SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid)
+{
+ return query_info(xid, tcon, persistent_fid, volatile_fid,
+ FILE_INTERNAL_INFORMATION,
+ sizeof(struct smb2_file_internal_info),
+ sizeof(struct smb2_file_internal_info), uniqueid);
+}
+
/*
* This is a no-op for now. We're not really interested in the reply, but
* rather in the fact that the server sent one and that server->lstrp
@@ -1102,6 +1243,8 @@ SMB2_echo(struct TCP_Server_Info *server)
struct smb2_echo_req *req;
int rc = 0;
struct kvec iov;
+ struct smb_rqst rqst = { .rq_iov = &iov,
+ .rq_nvec = 1 };
cFYI(1, "In echo request");
@@ -1115,7 +1258,7 @@ SMB2_echo(struct TCP_Server_Info *server)
/* 4 for rfc1002 length field */
iov.iov_len = get_rfc1002_length(req) + 4;
- rc = cifs_call_async(server, &iov, 1, NULL, smb2_echo_callback, server,
+ rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, server,
CIFS_ECHO_OP);
if (rc)
cFYI(1, "Echo request failed: %d", rc);
@@ -1123,3 +1266,945 @@ SMB2_echo(struct TCP_Server_Info *server)
cifs_small_buf_release(req);
return rc;
}
+
+int
+SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ u64 volatile_fid)
+{
+ struct smb2_flush_req *req;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses = tcon->ses;
+ struct kvec iov[1];
+ int resp_buftype;
+ int rc = 0;
+
+ cFYI(1, "Flush");
+
+ if (ses && (ses->server))
+ server = ses->server;
+ else
+ return -EIO;
+
+ rc = small_smb2_init(SMB2_FLUSH, tcon, (void **) &req);
+ if (rc)
+ return rc;
+
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+
+ iov[0].iov_base = (char *)req;
+ /* 4 for rfc1002 length field */
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+ rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0);
+
+ if ((rc != 0) && tcon)
+ cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE);
+
+ free_rsp_buf(resp_buftype, iov[0].iov_base);
+ return rc;
+}
+
+/*
+ * To form a chain of read requests, any read requests after the first should
+ * have the end_of_chain boolean set to true.
+ */
+static int
+smb2_new_read_req(struct kvec *iov, struct cifs_io_parms *io_parms,
+ unsigned int remaining_bytes, int request_type)
+{
+ int rc = -EACCES;
+ struct smb2_read_req *req = NULL;
+
+ rc = small_smb2_init(SMB2_READ, io_parms->tcon, (void **) &req);
+ if (rc)
+ return rc;
+ if (io_parms->tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ req->hdr.ProcessId = cpu_to_le32(io_parms->pid);
+
+ req->PersistentFileId = io_parms->persistent_fid;
+ req->VolatileFileId = io_parms->volatile_fid;
+ req->ReadChannelInfoOffset = 0; /* reserved */
+ req->ReadChannelInfoLength = 0; /* reserved */
+ req->Channel = 0; /* reserved */
+ req->MinimumCount = 0;
+ req->Length = cpu_to_le32(io_parms->length);
+ req->Offset = cpu_to_le64(io_parms->offset);
+
+ if (request_type & CHAINED_REQUEST) {
+ if (!(request_type & END_OF_CHAIN)) {
+ /* 4 for rfc1002 length field */
+ req->hdr.NextCommand =
+ cpu_to_le32(get_rfc1002_length(req) + 4);
+ } else /* END_OF_CHAIN */
+ req->hdr.NextCommand = 0;
+ if (request_type & RELATED_REQUEST) {
+ req->hdr.Flags |= SMB2_FLAGS_RELATED_OPERATIONS;
+ /*
+ * Related requests use info from previous read request
+ * in chain.
+ */
+ req->hdr.SessionId = 0xFFFFFFFF;
+ req->hdr.TreeId = 0xFFFFFFFF;
+ req->PersistentFileId = 0xFFFFFFFF;
+ req->VolatileFileId = 0xFFFFFFFF;
+ }
+ }
+ if (remaining_bytes > io_parms->length)
+ req->RemainingBytes = cpu_to_le32(remaining_bytes);
+ else
+ req->RemainingBytes = 0;
+
+ iov[0].iov_base = (char *)req;
+ /* 4 for rfc1002 length field */
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+ return rc;
+}
+
+static void
+smb2_readv_callback(struct mid_q_entry *mid)
+{
+ struct cifs_readdata *rdata = mid->callback_data;
+ struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
+ struct TCP_Server_Info *server = tcon->ses->server;
+ struct smb2_hdr *buf = (struct smb2_hdr *)rdata->iov.iov_base;
+ unsigned int credits_received = 1;
+ struct smb_rqst rqst = { .rq_iov = &rdata->iov,
+ .rq_nvec = 1,
+ .rq_pages = rdata->pages,
+ .rq_npages = rdata->nr_pages,
+ .rq_pagesz = rdata->pagesz,
+ .rq_tailsz = rdata->tailsz };
+
+ cFYI(1, "%s: mid=%llu state=%d result=%d bytes=%u", __func__,
+ mid->mid, mid->mid_state, rdata->result, rdata->bytes);
+
+ switch (mid->mid_state) {
+ case MID_RESPONSE_RECEIVED:
+ credits_received = le16_to_cpu(buf->CreditRequest);
+ /* result already set, check signature */
+ if (server->sec_mode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
+ int rc;
+
+ rc = smb2_verify_signature(&rqst, server);
+ if (rc)
+ cERROR(1, "SMB signature verification returned "
+ "error = %d", rc);
+ }
+ /* FIXME: should this be counted toward the initiating task? */
+ task_io_account_read(rdata->bytes);
+ cifs_stats_bytes_read(tcon, rdata->bytes);
+ break;
+ case MID_REQUEST_SUBMITTED:
+ case MID_RETRY_NEEDED:
+ rdata->result = -EAGAIN;
+ break;
+ default:
+ if (rdata->result != -ENODATA)
+ rdata->result = -EIO;
+ }
+
+ if (rdata->result)
+ cifs_stats_fail_inc(tcon, SMB2_READ_HE);
+
+ queue_work(cifsiod_wq, &rdata->work);
+ DeleteMidQEntry(mid);
+ add_credits(server, credits_received, 0);
+}
+
+/* smb2_async_readv - send an async write, and set up mid to handle result */
+int
+smb2_async_readv(struct cifs_readdata *rdata)
+{
+ int rc;
+ struct smb2_hdr *buf;
+ struct cifs_io_parms io_parms;
+ struct smb_rqst rqst = { .rq_iov = &rdata->iov,
+ .rq_nvec = 1 };
+
+ cFYI(1, "%s: offset=%llu bytes=%u", __func__,
+ rdata->offset, rdata->bytes);
+
+ io_parms.tcon = tlink_tcon(rdata->cfile->tlink);
+ io_parms.offset = rdata->offset;
+ io_parms.length = rdata->bytes;
+ io_parms.persistent_fid = rdata->cfile->fid.persistent_fid;
+ io_parms.volatile_fid = rdata->cfile->fid.volatile_fid;
+ io_parms.pid = rdata->pid;
+ rc = smb2_new_read_req(&rdata->iov, &io_parms, 0, 0);
+ if (rc)
+ return rc;
+
+ buf = (struct smb2_hdr *)rdata->iov.iov_base;
+ /* 4 for rfc1002 length field */
+ rdata->iov.iov_len = get_rfc1002_length(rdata->iov.iov_base) + 4;
+
+ kref_get(&rdata->refcount);
+ rc = cifs_call_async(io_parms.tcon->ses->server, &rqst,
+ cifs_readv_receive, smb2_readv_callback,
+ rdata, 0);
+ if (rc) {
+ kref_put(&rdata->refcount, cifs_readdata_release);
+ cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE);
+ }
+
+ cifs_small_buf_release(buf);
+ return rc;
+}
+
+int
+SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, char **buf, int *buf_type)
+{
+ int resp_buftype, rc = -EACCES;
+ struct smb2_read_rsp *rsp = NULL;
+ struct kvec iov[1];
+
+ *nbytes = 0;
+ rc = smb2_new_read_req(iov, io_parms, 0, 0);
+ if (rc)
+ return rc;
+
+ rc = SendReceive2(xid, io_parms->tcon->ses, iov, 1,
+ &resp_buftype, CIFS_LOG_ERROR);
+
+ rsp = (struct smb2_read_rsp *)iov[0].iov_base;
+
+ if (rsp->hdr.Status == STATUS_END_OF_FILE) {
+ free_rsp_buf(resp_buftype, iov[0].iov_base);
+ return 0;
+ }
+
+ if (rc) {
+ cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE);
+ cERROR(1, "Send error in read = %d", rc);
+ } else {
+ *nbytes = le32_to_cpu(rsp->DataLength);
+ if ((*nbytes > CIFS_MAX_MSGSIZE) ||
+ (*nbytes > io_parms->length)) {
+ cFYI(1, "bad length %d for count %d", *nbytes,
+ io_parms->length);
+ rc = -EIO;
+ *nbytes = 0;
+ }
+ }
+
+ if (*buf) {
+ memcpy(*buf, (char *)rsp->hdr.ProtocolId + rsp->DataOffset,
+ *nbytes);
+ free_rsp_buf(resp_buftype, iov[0].iov_base);
+ } else if (resp_buftype != CIFS_NO_BUFFER) {
+ *buf = iov[0].iov_base;
+ if (resp_buftype == CIFS_SMALL_BUFFER)
+ *buf_type = CIFS_SMALL_BUFFER;
+ else if (resp_buftype == CIFS_LARGE_BUFFER)
+ *buf_type = CIFS_LARGE_BUFFER;
+ }
+ return rc;
+}
+
+/*
+ * Check the mid_state and signature on received buffer (if any), and queue the
+ * workqueue completion task.
+ */
+static void
+smb2_writev_callback(struct mid_q_entry *mid)
+{
+ struct cifs_writedata *wdata = mid->callback_data;
+ struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
+ unsigned int written;
+ struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf;
+ unsigned int credits_received = 1;
+
+ switch (mid->mid_state) {
+ case MID_RESPONSE_RECEIVED:
+ credits_received = le16_to_cpu(rsp->hdr.CreditRequest);
+ wdata->result = smb2_check_receive(mid, tcon->ses->server, 0);
+ if (wdata->result != 0)
+ break;
+
+ written = le32_to_cpu(rsp->DataLength);
+ /*
+ * Mask off high 16 bits when bytes written as returned
+ * by the server is greater than bytes requested by the
+ * client. OS/2 servers are known to set incorrect
+ * CountHigh values.
+ */
+ if (written > wdata->bytes)
+ written &= 0xFFFF;
+
+ if (written < wdata->bytes)
+ wdata->result = -ENOSPC;
+ else
+ wdata->bytes = written;
+ break;
+ case MID_REQUEST_SUBMITTED:
+ case MID_RETRY_NEEDED:
+ wdata->result = -EAGAIN;
+ break;
+ default:
+ wdata->result = -EIO;
+ break;
+ }
+
+ if (wdata->result)
+ cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
+
+ queue_work(cifsiod_wq, &wdata->work);
+ DeleteMidQEntry(mid);
+ add_credits(tcon->ses->server, credits_received, 0);
+}
+
+/* smb2_async_writev - send an async write, and set up mid to handle result */
+int
+smb2_async_writev(struct cifs_writedata *wdata)
+{
+ int rc = -EACCES;
+ struct smb2_write_req *req = NULL;
+ struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
+ struct kvec iov;
+ struct smb_rqst rqst;
+
+ rc = small_smb2_init(SMB2_WRITE, tcon, (void **) &req);
+ if (rc)
+ goto async_writev_out;
+
+ req->hdr.ProcessId = cpu_to_le32(wdata->cfile->pid);
+
+ req->PersistentFileId = wdata->cfile->fid.persistent_fid;
+ req->VolatileFileId = wdata->cfile->fid.volatile_fid;
+ req->WriteChannelInfoOffset = 0;
+ req->WriteChannelInfoLength = 0;
+ req->Channel = 0;
+ req->Offset = cpu_to_le64(wdata->offset);
+ /* 4 for rfc1002 length field */
+ req->DataOffset = cpu_to_le16(
+ offsetof(struct smb2_write_req, Buffer) - 4);
+ req->RemainingBytes = 0;
+
+ /* 4 for rfc1002 length field and 1 for Buffer */
+ iov.iov_len = get_rfc1002_length(req) + 4 - 1;
+ iov.iov_base = req;
+
+ rqst.rq_iov = &iov;
+ rqst.rq_nvec = 1;
+ rqst.rq_pages = wdata->pages;
+ rqst.rq_npages = wdata->nr_pages;
+ rqst.rq_pagesz = wdata->pagesz;
+ rqst.rq_tailsz = wdata->tailsz;
+
+ cFYI(1, "async write at %llu %u bytes", wdata->offset, wdata->bytes);
+
+ req->Length = cpu_to_le32(wdata->bytes);
+
+ inc_rfc1001_len(&req->hdr, wdata->bytes - 1 /* Buffer */);
+
+ kref_get(&wdata->refcount);
+ rc = cifs_call_async(tcon->ses->server, &rqst, NULL,
+ smb2_writev_callback, wdata, 0);
+
+ if (rc) {
+ kref_put(&wdata->refcount, cifs_writedata_release);
+ cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
+ }
+
+async_writev_out:
+ cifs_small_buf_release(req);
+ return rc;
+}
+
+/*
+ * SMB2_write function gets iov pointer to kvec array with n_vec as a length.
+ * The length field from io_parms must be at least 1 and indicates a number of
+ * elements with data to write that begins with position 1 in iov array. All
+ * data length is specified by count.
+ */
+int
+SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, struct kvec *iov, int n_vec)
+{
+ int rc = 0;
+ struct smb2_write_req *req = NULL;
+ struct smb2_write_rsp *rsp = NULL;
+ int resp_buftype;
+ *nbytes = 0;
+
+ if (n_vec < 1)
+ return rc;
+
+ rc = small_smb2_init(SMB2_WRITE, io_parms->tcon, (void **) &req);
+ if (rc)
+ return rc;
+
+ if (io_parms->tcon->ses->server == NULL)
+ return -ECONNABORTED;
+
+ req->hdr.ProcessId = cpu_to_le32(io_parms->pid);
+
+ req->PersistentFileId = io_parms->persistent_fid;
+ req->VolatileFileId = io_parms->volatile_fid;
+ req->WriteChannelInfoOffset = 0;
+ req->WriteChannelInfoLength = 0;
+ req->Channel = 0;
+ req->Length = cpu_to_le32(io_parms->length);
+ req->Offset = cpu_to_le64(io_parms->offset);
+ /* 4 for rfc1002 length field */
+ req->DataOffset = cpu_to_le16(
+ offsetof(struct smb2_write_req, Buffer) - 4);
+ req->RemainingBytes = 0;
+
+ iov[0].iov_base = (char *)req;
+ /* 4 for rfc1002 length field and 1 for Buffer */
+ iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
+
+ /* length of entire message including data to be written */
+ inc_rfc1001_len(req, io_parms->length - 1 /* Buffer */);
+
+ rc = SendReceive2(xid, io_parms->tcon->ses, iov, n_vec + 1,
+ &resp_buftype, 0);
+ rsp = (struct smb2_write_rsp *)iov[0].iov_base;
+
+ if (rc) {
+ cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE);
+ cERROR(1, "Send error in write = %d", rc);
+ } else
+ *nbytes = le32_to_cpu(rsp->DataLength);
+
+ free_rsp_buf(resp_buftype, rsp);
+ return rc;
+}
+
+static unsigned int
+num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size)
+{
+ int len;
+ unsigned int entrycount = 0;
+ unsigned int next_offset = 0;
+ FILE_DIRECTORY_INFO *entryptr;
+
+ if (bufstart == NULL)
+ return 0;
+
+ entryptr = (FILE_DIRECTORY_INFO *)bufstart;
+
+ while (1) {
+ entryptr = (FILE_DIRECTORY_INFO *)
+ ((char *)entryptr + next_offset);
+
+ if ((char *)entryptr + size > end_of_buf) {
+ cERROR(1, "malformed search entry would overflow");
+ break;
+ }
+
+ len = le32_to_cpu(entryptr->FileNameLength);
+ if ((char *)entryptr + len + size > end_of_buf) {
+ cERROR(1, "directory entry name would overflow frame "
+ "end of buf %p", end_of_buf);
+ break;
+ }
+
+ *lastentry = (char *)entryptr;
+ entrycount++;
+
+ next_offset = le32_to_cpu(entryptr->NextEntryOffset);
+ if (!next_offset)
+ break;
+ }
+
+ return entrycount;
+}
+
+/*
+ * Readdir/FindFirst
+ */
+int
+SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, int index,
+ struct cifs_search_info *srch_inf)
+{
+ struct smb2_query_directory_req *req;
+ struct smb2_query_directory_rsp *rsp = NULL;
+ struct kvec iov[2];
+ int rc = 0;
+ int len;
+ int resp_buftype;
+ unsigned char *bufptr;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses = tcon->ses;
+ __le16 asteriks = cpu_to_le16('*');
+ char *end_of_smb;
+ unsigned int output_size = CIFSMaxBufSize;
+ size_t info_buf_size;
+
+ if (ses && (ses->server))
+ server = ses->server;
+ else
+ return -EIO;
+
+ rc = small_smb2_init(SMB2_QUERY_DIRECTORY, tcon, (void **) &req);
+ if (rc)
+ return rc;
+
+ switch (srch_inf->info_level) {
+ case SMB_FIND_FILE_DIRECTORY_INFO:
+ req->FileInformationClass = FILE_DIRECTORY_INFORMATION;
+ info_buf_size = sizeof(FILE_DIRECTORY_INFO) - 1;
+ break;
+ case SMB_FIND_FILE_ID_FULL_DIR_INFO:
+ req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION;
+ info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1;
+ break;
+ default:
+ cERROR(1, "info level %u isn't supported",
+ srch_inf->info_level);
+ rc = -EINVAL;
+ goto qdir_exit;
+ }
+
+ req->FileIndex = cpu_to_le32(index);
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+
+ len = 0x2;
+ bufptr = req->Buffer;
+ memcpy(bufptr, &asteriks, len);
+
+ req->FileNameOffset =
+ cpu_to_le16(sizeof(struct smb2_query_directory_req) - 1 - 4);
+ req->FileNameLength = cpu_to_le16(len);
+ /*
+ * BB could be 30 bytes or so longer if we used SMB2 specific
+ * buffer lengths, but this is safe and close enough.
+ */
+ output_size = min_t(unsigned int, output_size, server->maxBuf);
+ output_size = min_t(unsigned int, output_size, 2 << 15);
+ req->OutputBufferLength = cpu_to_le32(output_size);
+
+ iov[0].iov_base = (char *)req;
+ /* 4 for RFC1001 length and 1 for Buffer */
+ iov[0].iov_len = get_rfc1002_length(req) + 4 - 1;
+
+ iov[1].iov_base = (char *)(req->Buffer);
+ iov[1].iov_len = len;
+
+ inc_rfc1001_len(req, len - 1 /* Buffer */);
+
+ rc = SendReceive2(xid, ses, iov, 2, &resp_buftype, 0);
+ rsp = (struct smb2_query_directory_rsp *)iov[0].iov_base;
+
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE);
+ goto qdir_exit;
+ }
+
+ rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset),
+ le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr,
+ info_buf_size);
+ if (rc)
+ goto qdir_exit;
+
+ srch_inf->unicode = true;
+
+ if (srch_inf->ntwrk_buf_start) {
+ if (srch_inf->smallBuf)
+ cifs_small_buf_release(srch_inf->ntwrk_buf_start);
+ else
+ cifs_buf_release(srch_inf->ntwrk_buf_start);
+ }
+ srch_inf->ntwrk_buf_start = (char *)rsp;
+ srch_inf->srch_entries_start = srch_inf->last_entry = 4 /* rfclen */ +
+ (char *)&rsp->hdr + le16_to_cpu(rsp->OutputBufferOffset);
+ /* 4 for rfc1002 length field */
+ end_of_smb = get_rfc1002_length(rsp) + 4 + (char *)&rsp->hdr;
+ srch_inf->entries_in_buffer =
+ num_entries(srch_inf->srch_entries_start, end_of_smb,
+ &srch_inf->last_entry, info_buf_size);
+ srch_inf->index_of_last_entry += srch_inf->entries_in_buffer;
+ cFYI(1, "num entries %d last_index %lld srch start %p srch end %p",
+ srch_inf->entries_in_buffer, srch_inf->index_of_last_entry,
+ srch_inf->srch_entries_start, srch_inf->last_entry);
+ if (resp_buftype == CIFS_LARGE_BUFFER)
+ srch_inf->smallBuf = false;
+ else if (resp_buftype == CIFS_SMALL_BUFFER)
+ srch_inf->smallBuf = true;
+ else
+ cERROR(1, "illegal search buffer type");
+
+ if (rsp->hdr.Status == STATUS_NO_MORE_FILES)
+ srch_inf->endOfSearch = 1;
+ else
+ srch_inf->endOfSearch = 0;
+
+ return rc;
+
+qdir_exit:
+ free_rsp_buf(resp_buftype, rsp);
+ return rc;
+}
+
+static int
+send_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 pid, int info_class,
+ unsigned int num, void **data, unsigned int *size)
+{
+ struct smb2_set_info_req *req;
+ struct smb2_set_info_rsp *rsp = NULL;
+ struct kvec *iov;
+ int rc = 0;
+ int resp_buftype;
+ unsigned int i;
+ struct TCP_Server_Info *server;
+ struct cifs_ses *ses = tcon->ses;
+
+ if (ses && (ses->server))
+ server = ses->server;
+ else
+ return -EIO;
+
+ if (!num)
+ return -EINVAL;
+
+ iov = kmalloc(sizeof(struct kvec) * num, GFP_KERNEL);
+ if (!iov)
+ return -ENOMEM;
+
+ rc = small_smb2_init(SMB2_SET_INFO, tcon, (void **) &req);
+ if (rc) {
+ kfree(iov);
+ return rc;
+ }
+
+ req->hdr.ProcessId = cpu_to_le32(pid);
+
+ req->InfoType = SMB2_O_INFO_FILE;
+ req->FileInfoClass = info_class;
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+
+ /* 4 for RFC1001 length and 1 for Buffer */
+ req->BufferOffset =
+ cpu_to_le16(sizeof(struct smb2_set_info_req) - 1 - 4);
+ req->BufferLength = cpu_to_le32(*size);
+
+ inc_rfc1001_len(req, *size - 1 /* Buffer */);
+
+ memcpy(req->Buffer, *data, *size);
+
+ iov[0].iov_base = (char *)req;
+ /* 4 for RFC1001 length */
+ iov[0].iov_len = get_rfc1002_length(req) + 4;
+
+ for (i = 1; i < num; i++) {
+ inc_rfc1001_len(req, size[i]);
+ le32_add_cpu(&req->BufferLength, size[i]);
+ iov[i].iov_base = (char *)data[i];
+ iov[i].iov_len = size[i];
+ }
+
+ rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0);
+ rsp = (struct smb2_set_info_rsp *)iov[0].iov_base;
+
+ if (rc != 0) {
+ cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE);
+ goto out;
+ }
+out:
+ free_rsp_buf(resp_buftype, rsp);
+ kfree(iov);
+ return rc;
+}
+
+int
+SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, __le16 *target_file)
+{
+ struct smb2_file_rename_info info;
+ void **data;
+ unsigned int size[2];
+ int rc;
+ int len = (2 * UniStrnlen((wchar_t *)target_file, PATH_MAX));
+
+ data = kmalloc(sizeof(void *) * 2, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ info.ReplaceIfExists = 1; /* 1 = replace existing target with new */
+ /* 0 = fail if target already exists */
+ info.RootDirectory = 0; /* MBZ for network ops (why does spec say?) */
+ info.FileNameLength = cpu_to_le32(len);
+
+ data[0] = &info;
+ size[0] = sizeof(struct smb2_file_rename_info);
+
+ data[1] = target_file;
+ size[1] = len + 2 /* null */;
+
+ rc = send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ current->tgid, FILE_RENAME_INFORMATION, 2, data,
+ size);
+ kfree(data);
+ return rc;
+}
+
+int
+SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, __le16 *target_file)
+{
+ struct smb2_file_link_info info;
+ void **data;
+ unsigned int size[2];
+ int rc;
+ int len = (2 * UniStrnlen((wchar_t *)target_file, PATH_MAX));
+
+ data = kmalloc(sizeof(void *) * 2, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ info.ReplaceIfExists = 0; /* 1 = replace existing link with new */
+ /* 0 = fail if link already exists */
+ info.RootDirectory = 0; /* MBZ for network ops (why does spec say?) */
+ info.FileNameLength = cpu_to_le32(len);
+
+ data[0] = &info;
+ size[0] = sizeof(struct smb2_file_link_info);
+
+ data[1] = target_file;
+ size[1] = len + 2 /* null */;
+
+ rc = send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ current->tgid, FILE_LINK_INFORMATION, 2, data, size);
+ kfree(data);
+ return rc;
+}
+
+int
+SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
+ u64 volatile_fid, u32 pid, __le64 *eof)
+{
+ struct smb2_file_eof_info info;
+ void *data;
+ unsigned int size;
+
+ info.EndOfFile = *eof;
+
+ data = &info;
+ size = sizeof(struct smb2_file_eof_info);
+
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid, pid,
+ FILE_END_OF_FILE_INFORMATION, 1, &data, &size);
+}
+
+int
+SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, FILE_BASIC_INFO *buf)
+{
+ unsigned int size;
+ size = sizeof(FILE_BASIC_INFO);
+ return send_set_info(xid, tcon, persistent_fid, volatile_fid,
+ current->tgid, FILE_BASIC_INFORMATION, 1,
+ (void **)&buf, &size);
+}
+
+int
+SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
+ const u64 persistent_fid, const u64 volatile_fid,
+ __u8 oplock_level)
+{
+ int rc;
+ struct smb2_oplock_break *req = NULL;
+
+ cFYI(1, "SMB2_oplock_break");
+ rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req);
+
+ if (rc)
+ return rc;
+
+ req->VolatileFid = volatile_fid;
+ req->PersistentFid = persistent_fid;
+ req->OplockLevel = oplock_level;
+ req->hdr.CreditRequest = cpu_to_le16(1);
+
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP);
+ /* SMB2 buffer freed by function above */
+
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
+ cFYI(1, "Send error in Oplock Break = %d", rc);
+ }
+
+ return rc;
+}
+
+static void
+copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
+ struct kstatfs *kst)
+{
+ kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) *
+ le32_to_cpu(pfs_inf->SectorsPerAllocationUnit);
+ kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits);
+ kst->f_bfree = le64_to_cpu(pfs_inf->ActualAvailableAllocationUnits);
+ kst->f_bavail = le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits);
+ return;
+}
+
+static int
+build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon, int level,
+ int outbuf_len, u64 persistent_fid, u64 volatile_fid)
+{
+ int rc;
+ struct smb2_query_info_req *req;
+
+ cFYI(1, "Query FSInfo level %d", level);
+
+ if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
+ return -EIO;
+
+ rc = small_smb2_init(SMB2_QUERY_INFO, tcon, (void **) &req);
+ if (rc)
+ return rc;
+
+ req->InfoType = SMB2_O_INFO_FILESYSTEM;
+ req->FileInfoClass = level;
+ req->PersistentFileId = persistent_fid;
+ req->VolatileFileId = volatile_fid;
+ /* 4 for rfc1002 length field and 1 for pad */
+ req->InputBufferOffset =
+ cpu_to_le16(sizeof(struct smb2_query_info_req) - 1 - 4);
+ req->OutputBufferLength = cpu_to_le32(
+ outbuf_len + sizeof(struct smb2_query_info_rsp) - 1 - 4);
+
+ iov->iov_base = (char *)req;
+ /* 4 for rfc1002 length field */
+ iov->iov_len = get_rfc1002_length(req) + 4;
+ return 0;
+}
+
+int
+SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
+{
+ struct smb2_query_info_rsp *rsp = NULL;
+ struct kvec iov;
+ int rc = 0;
+ int resp_buftype;
+ struct cifs_ses *ses = tcon->ses;
+ struct smb2_fs_full_size_info *info = NULL;
+
+ rc = build_qfs_info_req(&iov, tcon, FS_FULL_SIZE_INFORMATION,
+ sizeof(struct smb2_fs_full_size_info),
+ persistent_fid, volatile_fid);
+ if (rc)
+ return rc;
+
+ rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0);
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
+ goto qinf_exit;
+ }
+ rsp = (struct smb2_query_info_rsp *)iov.iov_base;
+
+ info = (struct smb2_fs_full_size_info *)(4 /* RFC1001 len */ +
+ le16_to_cpu(rsp->OutputBufferOffset) + (char *)&rsp->hdr);
+ rc = validate_buf(le16_to_cpu(rsp->OutputBufferOffset),
+ le32_to_cpu(rsp->OutputBufferLength), &rsp->hdr,
+ sizeof(struct smb2_fs_full_size_info));
+ if (!rc)
+ copy_fs_info_to_kstatfs(info, fsdata);
+
+qinf_exit:
+ free_rsp_buf(resp_buftype, iov.iov_base);
+ return rc;
+}
+
+int
+smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
+ const __u32 num_lock, struct smb2_lock_element *buf)
+{
+ int rc = 0;
+ struct smb2_lock_req *req = NULL;
+ struct kvec iov[2];
+ int resp_buf_type;
+ unsigned int count;
+
+ cFYI(1, "smb2_lockv num lock %d", num_lock);
+
+ rc = small_smb2_init(SMB2_LOCK, tcon, (void **) &req);
+ if (rc)
+ return rc;
+
+ req->hdr.ProcessId = cpu_to_le32(pid);
+ req->LockCount = cpu_to_le16(num_lock);
+
+ req->PersistentFileId = persist_fid;
+ req->VolatileFileId = volatile_fid;
+
+ count = num_lock * sizeof(struct smb2_lock_element);
+ inc_rfc1001_len(req, count - sizeof(struct smb2_lock_element));
+
+ iov[0].iov_base = (char *)req;
+ /* 4 for rfc1002 length field and count for all locks */
+ iov[0].iov_len = get_rfc1002_length(req) + 4 - count;
+ iov[1].iov_base = (char *)buf;
+ iov[1].iov_len = count;
+
+ cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
+ rc = SendReceive2(xid, tcon->ses, iov, 2, &resp_buf_type, CIFS_NO_RESP);
+ if (rc) {
+ cFYI(1, "Send error in smb2_lockv = %d", rc);
+ cifs_stats_fail_inc(tcon, SMB2_LOCK_HE);
+ }
+
+ return rc;
+}
+
+int
+SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
+ const __u64 length, const __u64 offset, const __u32 lock_flags,
+ const bool wait)
+{
+ struct smb2_lock_element lock;
+
+ lock.Offset = cpu_to_le64(offset);
+ lock.Length = cpu_to_le64(length);
+ lock.Flags = cpu_to_le32(lock_flags);
+ if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK)
+ lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY);
+
+ return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock);
+}
+
+int
+SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
+ __u8 *lease_key, const __le32 lease_state)
+{
+ int rc;
+ struct smb2_lease_ack *req = NULL;
+
+ cFYI(1, "SMB2_lease_break");
+ rc = small_smb2_init(SMB2_OPLOCK_BREAK, tcon, (void **) &req);
+
+ if (rc)
+ return rc;
+
+ req->hdr.CreditRequest = cpu_to_le16(1);
+ req->StructureSize = cpu_to_le16(36);
+ inc_rfc1001_len(req, 12);
+
+ memcpy(req->LeaseKey, lease_key, 16);
+ req->LeaseState = lease_state;
+
+ rc = SendReceiveNoRsp(xid, tcon->ses, (char *) req, CIFS_OBREAK_OP);
+ /* SMB2 buffer freed by function above */
+
+ if (rc) {
+ cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
+ cFYI(1, "Send error in Lease Break = %d", rc);
+ }
+
+ return rc;
+}
diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h
index 15dc8eea8273..4cb4ced258cb 100644
--- a/fs/cifs/smb2pdu.h
+++ b/fs/cifs/smb2pdu.h
@@ -150,6 +150,10 @@ struct smb2_err_rsp {
__u8 ErrorData[1]; /* variable length */
} __packed;
+#define SMB2_CLIENT_GUID_SIZE 16
+
+extern __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE];
+
struct smb2_negotiate_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 36 */
@@ -157,11 +161,17 @@ struct smb2_negotiate_req {
__le16 SecurityMode;
__le16 Reserved; /* MBZ */
__le32 Capabilities;
- __u8 ClientGUID[16]; /* MBZ */
+ __u8 ClientGUID[SMB2_CLIENT_GUID_SIZE];
__le64 ClientStartTime; /* MBZ */
- __le16 Dialects[2]; /* variable length */
+ __le16 Dialects[1]; /* One dialect (vers=) at a time for now */
} __packed;
+/* Dialects */
+#define SMB20_PROT_ID 0x0202
+#define SMB21_PROT_ID 0x0210
+#define SMB30_PROT_ID 0x0300
+#define BAD_PROT_ID 0xFFFF
+
/* SecurityMode flags */
#define SMB2_NEGOTIATE_SIGNING_ENABLED 0x0001
#define SMB2_NEGOTIATE_SIGNING_REQUIRED 0x0002
@@ -169,6 +179,10 @@ struct smb2_negotiate_req {
#define SMB2_GLOBAL_CAP_DFS 0x00000001
#define SMB2_GLOBAL_CAP_LEASING 0x00000002 /* Resp only New to SMB2.1 */
#define SMB2_GLOBAL_CAP_LARGE_MTU 0X00000004 /* Resp only New to SMB2.1 */
+#define SMB2_GLOBAL_CAP_MULTI_CHANNEL 0x00000008 /* New to SMB3 */
+#define SMB2_GLOBAL_CAP_PERSISTENT_HANDLES 0x00000010 /* New to SMB3 */
+#define SMB2_GLOBAL_CAP_DIRECTORY_LEASING 0x00000020 /* New to SMB3 */
+#define SMB2_GLOBAL_CAP_ENCRYPTION 0x00000040 /* New to SMB3 */
/* Internal types */
#define SMB2_NT_FIND 0x00100000
#define SMB2_LARGE_FILES 0x00200000
@@ -307,6 +321,8 @@ struct smb2_tree_disconnect_rsp {
#define SMB2_OPLOCK_LEVEL_EXCLUSIVE 0x08
#define SMB2_OPLOCK_LEVEL_BATCH 0x09
#define SMB2_OPLOCK_LEVEL_LEASE 0xFF
+/* Non-spec internal type */
+#define SMB2_OPLOCK_LEVEL_NOCHANGE 0x99
/* Desired Access Flags */
#define FILE_READ_DATA_LE cpu_to_le32(0x00000001)
@@ -404,7 +420,7 @@ struct smb2_create_req {
__le16 NameLength;
__le32 CreateContextsOffset;
__le32 CreateContextsLength;
- __u8 Buffer[1];
+ __u8 Buffer[8];
} __packed;
struct smb2_create_rsp {
@@ -428,6 +444,39 @@ struct smb2_create_rsp {
__u8 Buffer[1];
} __packed;
+struct create_context {
+ __le32 Next;
+ __le16 NameOffset;
+ __le16 NameLength;
+ __le16 Reserved;
+ __le16 DataOffset;
+ __le32 DataLength;
+ __u8 Buffer[0];
+} __packed;
+
+#define SMB2_LEASE_NONE __constant_cpu_to_le32(0x00)
+#define SMB2_LEASE_READ_CACHING __constant_cpu_to_le32(0x01)
+#define SMB2_LEASE_HANDLE_CACHING __constant_cpu_to_le32(0x02)
+#define SMB2_LEASE_WRITE_CACHING __constant_cpu_to_le32(0x04)
+
+#define SMB2_LEASE_FLAG_BREAK_IN_PROGRESS __constant_cpu_to_le32(0x02)
+
+#define SMB2_LEASE_KEY_SIZE 16
+
+struct lease_context {
+ __le64 LeaseKeyLow;
+ __le64 LeaseKeyHigh;
+ __le32 LeaseState;
+ __le32 LeaseFlags;
+ __le64 LeaseDuration;
+} __packed;
+
+struct create_lease {
+ struct create_context ccontext;
+ __u8 Name[8];
+ struct lease_context lcontext;
+} __packed;
+
/* Currently defined values for close flags */
#define SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB cpu_to_le16(0x0001)
struct smb2_close_req {
@@ -453,6 +502,108 @@ struct smb2_close_rsp {
__le32 Attributes;
} __packed;
+struct smb2_flush_req {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 24 */
+ __le16 Reserved1;
+ __le32 Reserved2;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+} __packed;
+
+struct smb2_flush_rsp {
+ struct smb2_hdr hdr;
+ __le16 StructureSize;
+ __le16 Reserved;
+} __packed;
+
+struct smb2_read_req {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 49 */
+ __u8 Padding; /* offset from start of SMB2 header to place read */
+ __u8 Reserved;
+ __le32 Length;
+ __le64 Offset;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __le32 MinimumCount;
+ __le32 Channel; /* Reserved MBZ */
+ __le32 RemainingBytes;
+ __le16 ReadChannelInfoOffset; /* Reserved MBZ */
+ __le16 ReadChannelInfoLength; /* Reserved MBZ */
+ __u8 Buffer[1];
+} __packed;
+
+struct smb2_read_rsp {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 17 */
+ __u8 DataOffset;
+ __u8 Reserved;
+ __le32 DataLength;
+ __le32 DataRemaining;
+ __u32 Reserved2;
+ __u8 Buffer[1];
+} __packed;
+
+/* For write request Flags field below the following flag is defined: */
+#define SMB2_WRITEFLAG_WRITE_THROUGH 0x00000001
+
+struct smb2_write_req {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 49 */
+ __le16 DataOffset; /* offset from start of SMB2 header to write data */
+ __le32 Length;
+ __le64 Offset;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __le32 Channel; /* Reserved MBZ */
+ __le32 RemainingBytes;
+ __le16 WriteChannelInfoOffset; /* Reserved MBZ */
+ __le16 WriteChannelInfoLength; /* Reserved MBZ */
+ __le32 Flags;
+ __u8 Buffer[1];
+} __packed;
+
+struct smb2_write_rsp {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 17 */
+ __u8 DataOffset;
+ __u8 Reserved;
+ __le32 DataLength;
+ __le32 DataRemaining;
+ __u32 Reserved2;
+ __u8 Buffer[1];
+} __packed;
+
+#define SMB2_LOCKFLAG_SHARED_LOCK 0x0001
+#define SMB2_LOCKFLAG_EXCLUSIVE_LOCK 0x0002
+#define SMB2_LOCKFLAG_UNLOCK 0x0004
+#define SMB2_LOCKFLAG_FAIL_IMMEDIATELY 0x0010
+
+struct smb2_lock_element {
+ __le64 Offset;
+ __le64 Length;
+ __le32 Flags;
+ __le32 Reserved;
+} __packed;
+
+struct smb2_lock_req {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 48 */
+ __le16 LockCount;
+ __le32 Reserved;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ /* Followed by at least one */
+ struct smb2_lock_element locks[1];
+} __packed;
+
+struct smb2_lock_rsp {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 4 */
+ __le16 Reserved;
+} __packed;
+
struct smb2_echo_req {
struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 4 */
@@ -465,6 +616,34 @@ struct smb2_echo_rsp {
__u16 Reserved;
} __packed;
+/* search (query_directory) Flags field */
+#define SMB2_RESTART_SCANS 0x01
+#define SMB2_RETURN_SINGLE_ENTRY 0x02
+#define SMB2_INDEX_SPECIFIED 0x04
+#define SMB2_REOPEN 0x10
+
+struct smb2_query_directory_req {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 33 */
+ __u8 FileInformationClass;
+ __u8 Flags;
+ __le32 FileIndex;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __le16 FileNameOffset;
+ __le16 FileNameLength;
+ __le32 OutputBufferLength;
+ __u8 Buffer[1];
+} __packed;
+
+struct smb2_query_directory_rsp {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 9 */
+ __le16 OutputBufferOffset;
+ __le32 OutputBufferLength;
+ __u8 Buffer[1];
+} __packed;
+
/* Possible InfoType values */
#define SMB2_O_INFO_FILE 0x01
#define SMB2_O_INFO_FILESYSTEM 0x02
@@ -495,11 +674,84 @@ struct smb2_query_info_rsp {
__u8 Buffer[1];
} __packed;
+struct smb2_set_info_req {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 33 */
+ __u8 InfoType;
+ __u8 FileInfoClass;
+ __le32 BufferLength;
+ __le16 BufferOffset;
+ __u16 Reserved;
+ __le32 AdditionalInformation;
+ __u64 PersistentFileId; /* opaque endianness */
+ __u64 VolatileFileId; /* opaque endianness */
+ __u8 Buffer[1];
+} __packed;
+
+struct smb2_set_info_rsp {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 2 */
+} __packed;
+
+struct smb2_oplock_break {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 24 */
+ __u8 OplockLevel;
+ __u8 Reserved;
+ __le32 Reserved2;
+ __u64 PersistentFid;
+ __u64 VolatileFid;
+} __packed;
+
+#define SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED cpu_to_le32(0x01)
+
+struct smb2_lease_break {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 44 */
+ __le16 Reserved;
+ __le32 Flags;
+ __u8 LeaseKey[16];
+ __le32 CurrentLeaseState;
+ __le32 NewLeaseState;
+ __le32 BreakReason;
+ __le32 AccessMaskHint;
+ __le32 ShareMaskHint;
+} __packed;
+
+struct smb2_lease_ack {
+ struct smb2_hdr hdr;
+ __le16 StructureSize; /* Must be 36 */
+ __le16 Reserved;
+ __le32 Flags;
+ __u8 LeaseKey[16];
+ __le32 LeaseState;
+ __le64 LeaseDuration;
+} __packed;
+
/*
* PDU infolevel structure definitions
* BB consider moving to a different header
*/
+/* File System Information Classes */
+#define FS_VOLUME_INFORMATION 1 /* Query */
+#define FS_LABEL_INFORMATION 2 /* Set */
+#define FS_SIZE_INFORMATION 3 /* Query */
+#define FS_DEVICE_INFORMATION 4 /* Query */
+#define FS_ATTRIBUTE_INFORMATION 5 /* Query */
+#define FS_CONTROL_INFORMATION 6 /* Query, Set */
+#define FS_FULL_SIZE_INFORMATION 7 /* Query */
+#define FS_OBJECT_ID_INFORMATION 8 /* Query, Set */
+#define FS_DRIVER_PATH_INFORMATION 9 /* Query */
+
+struct smb2_fs_full_size_info {
+ __le64 TotalAllocationUnits;
+ __le64 CallerAvailableAllocationUnits;
+ __le64 ActualAvailableAllocationUnits;
+ __le32 SectorsPerAllocationUnit;
+ __le32 BytesPerSector;
+} __packed;
+
/* partial list of QUERY INFO levels */
#define FILE_DIRECTORY_INFORMATION 1
#define FILE_FULL_DIRECTORY_INFORMATION 2
@@ -548,6 +800,28 @@ struct smb2_query_info_rsp {
#define FILEID_GLOBAL_TX_DIRECTORY_INFORMATION 50
#define FILE_STANDARD_LINK_INFORMATION 54
+struct smb2_file_internal_info {
+ __le64 IndexNumber;
+} __packed; /* level 6 Query */
+
+struct smb2_file_rename_info { /* encoding of request for level 10 */
+ __u8 ReplaceIfExists; /* 1 = replace existing target with new */
+ /* 0 = fail if target already exists */
+ __u8 Reserved[7];
+ __u64 RootDirectory; /* MBZ for network operations (why says spec?) */
+ __le32 FileNameLength;
+ char FileName[0]; /* New name to be assigned */
+} __packed; /* level 10 Set */
+
+struct smb2_file_link_info { /* encoding of request for level 11 */
+ __u8 ReplaceIfExists; /* 1 = replace existing link with new */
+ /* 0 = fail if link already exists */
+ __u8 Reserved[7];
+ __u64 RootDirectory; /* MBZ for network operations (why says spec?) */
+ __le32 FileNameLength;
+ char FileName[0]; /* Name to be assigned to new link */
+} __packed; /* level 11 Set */
+
/*
* This level 18, although with struct with same name is different from cifs
* level 0x107. Level 0x107 has an extra u64 between AccessFlags and
@@ -576,4 +850,8 @@ struct smb2_file_all_info { /* data block encoding of response to level 18 */
char FileName[1];
} __packed; /* level 18 Query */
+struct smb2_file_eof_info { /* encoding of request for level 10 */
+ __le64 EndOfFile; /* new end of file value */
+} __packed; /* level 20 Set */
+
#endif /* _SMB2PDU_H */
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index bfaa7b148afd..7d25f8b14f93 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -26,6 +26,7 @@
#include <linux/key-type.h>
struct statfs;
+struct smb_rqst;
/*
*****************************************************************
@@ -34,24 +35,35 @@ struct statfs;
*/
extern int map_smb2_to_linux_error(char *buf, bool log_err);
extern int smb2_check_message(char *buf, unsigned int length);
-extern unsigned int smb2_calc_size(struct smb2_hdr *hdr);
+extern unsigned int smb2_calc_size(void *buf);
extern char *smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr);
extern __le16 *cifs_convert_path_to_utf16(const char *from,
struct cifs_sb_info *cifs_sb);
+extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *);
extern int smb2_check_receive(struct mid_q_entry *mid,
struct TCP_Server_Info *server, bool log_error);
-extern int smb2_setup_request(struct cifs_ses *ses, struct kvec *iov,
- unsigned int nvec, struct mid_q_entry **ret_mid);
-extern int smb2_setup_async_request(struct TCP_Server_Info *server,
- struct kvec *iov, unsigned int nvec,
- struct mid_q_entry **ret_mid);
+extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
+ struct smb_rqst *rqst);
+extern struct mid_q_entry *smb2_setup_async_request(
+ struct TCP_Server_Info *server, struct smb_rqst *rqst);
extern void smb2_echo_request(struct work_struct *work);
+extern __le32 smb2_get_lease_state(struct cifsInodeInfo *cinode);
+extern __u8 smb2_map_lease_to_oplock(__le32 lease_state);
+extern bool smb2_is_valid_oplock_break(char *buffer,
+ struct TCP_Server_Info *srv);
+extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
+ struct smb2_file_all_info *src);
extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb,
const char *full_path, FILE_ALL_INFO *data,
bool *adjust_tz);
+extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *full_path, __u64 size,
+ struct cifs_sb_info *cifs_sb, bool set_alloc);
+extern int smb2_set_file_info(struct inode *inode, const char *full_path,
+ FILE_BASIC_INFO *buf, const unsigned int xid);
extern int smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon,
const char *name, struct cifs_sb_info *cifs_sb);
extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path,
@@ -59,6 +71,24 @@ extern void smb2_mkdir_setinfo(struct inode *inode, const char *full_path,
struct cifs_tcon *tcon, const unsigned int xid);
extern int smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon,
const char *name, struct cifs_sb_info *cifs_sb);
+extern int smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *name, struct cifs_sb_info *cifs_sb);
+extern int smb2_rename_path(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb);
+extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *from_name, const char *to_name,
+ struct cifs_sb_info *cifs_sb);
+
+extern int smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon,
+ const char *full_path, int disposition,
+ int desired_access, int create_options,
+ struct cifs_fid *fid, __u32 *oplock,
+ FILE_ALL_INFO *buf, struct cifs_sb_info *cifs_sb);
+extern void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
+extern int smb2_unlock_range(struct cifsFileInfo *cfile,
+ struct file_lock *flock, const unsigned int xid);
+extern int smb2_push_mandatory_locks(struct cifsFileInfo *cfile);
/*
* SMB2 Worker functions - most of protocol specific implementation details
@@ -75,12 +105,55 @@ extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon,
__le16 *path, u64 *persistent_fid, u64 *volatile_fid,
__u32 desired_access, __u32 create_disposition,
- __u32 file_attributes, __u32 create_options);
+ __u32 file_attributes, __u32 create_options,
+ __u8 *oplock, struct smb2_file_all_info *buf);
extern int SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id);
+extern int SMB2_flush(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_file_id, u64 volatile_file_id);
extern int SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_file_id, u64 volatile_file_id,
struct smb2_file_all_info *data);
+extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ __le64 *uniqueid);
+extern int smb2_async_readv(struct cifs_readdata *rdata);
+extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, char **buf, int *buf_type);
+extern int smb2_async_writev(struct cifs_writedata *wdata);
+extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
+ unsigned int *nbytes, struct kvec *iov, int n_vec);
extern int SMB2_echo(struct TCP_Server_Info *server);
+extern int SMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, int index,
+ struct cifs_search_info *srch_inf);
+extern int SMB2_rename(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ __le16 *target_file);
+extern int SMB2_set_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ __le16 *target_file);
+extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid, u32 pid,
+ __le64 *eof);
+extern int SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_fid, u64 volatile_fid,
+ FILE_BASIC_INFO *buf);
+extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
+ const u64 persistent_fid, const u64 volatile_fid,
+ const __u8 oplock_level);
+extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
+ u64 persistent_file_id, u64 volatile_file_id,
+ struct kstatfs *FSData);
+extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u64 persist_fid, const __u64 volatile_fid,
+ const __u32 pid, const __u64 length, const __u64 offset,
+ const __u32 lockFlags, const bool wait);
+extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
+ const __u64 persist_fid, const __u64 volatile_fid,
+ const __u32 pid, const __u32 num_lock,
+ struct smb2_lock_element *buf);
+extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
+ __u8 *lease_key, const __le32 lease_state);
#endif /* _SMB2PROTO_H */
diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
index 31f5d420b3ea..2a5fdf26f79f 100644
--- a/fs/cifs/smb2transport.c
+++ b/fs/cifs/smb2transport.c
@@ -30,12 +30,156 @@
#include <linux/uaccess.h>
#include <asm/processor.h>
#include <linux/mempool.h>
+#include <linux/highmem.h>
#include "smb2pdu.h"
#include "cifsglob.h"
#include "cifsproto.h"
#include "smb2proto.h"
#include "cifs_debug.h"
#include "smb2status.h"
+#include "smb2glob.h"
+
+static int
+smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+{
+ int i, rc;
+ unsigned char smb2_signature[SMB2_HMACSHA256_SIZE];
+ unsigned char *sigptr = smb2_signature;
+ struct kvec *iov = rqst->rq_iov;
+ int n_vec = rqst->rq_nvec;
+ struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)iov[0].iov_base;
+
+ memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
+ memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
+
+ rc = crypto_shash_setkey(server->secmech.hmacsha256,
+ server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
+ if (rc) {
+ cERROR(1, "%s: Could not update with response\n", __func__);
+ return rc;
+ }
+
+ rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
+ if (rc) {
+ cERROR(1, "%s: Could not init md5\n", __func__);
+ return rc;
+ }
+
+ for (i = 0; i < n_vec; i++) {
+ if (iov[i].iov_len == 0)
+ continue;
+ if (iov[i].iov_base == NULL) {
+ cERROR(1, "null iovec entry");
+ return -EIO;
+ }
+ /*
+ * The first entry includes a length field (which does not get
+ * signed that occupies the first 4 bytes before the header).
+ */
+ if (i == 0) {
+ if (iov[0].iov_len <= 8) /* cmd field at offset 9 */
+ break; /* nothing to sign or corrupt header */
+ rc =
+ crypto_shash_update(
+ &server->secmech.sdeschmacsha256->shash,
+ iov[i].iov_base + 4, iov[i].iov_len - 4);
+ } else {
+ rc =
+ crypto_shash_update(
+ &server->secmech.sdeschmacsha256->shash,
+ iov[i].iov_base, iov[i].iov_len);
+ }
+ if (rc) {
+ cERROR(1, "%s: Could not update with payload\n",
+ __func__);
+ return rc;
+ }
+ }
+
+ /* now hash over the rq_pages array */
+ for (i = 0; i < rqst->rq_npages; i++) {
+ struct kvec p_iov;
+
+ cifs_rqst_page_to_kvec(rqst, i, &p_iov);
+ crypto_shash_update(&server->secmech.sdeschmacsha256->shash,
+ p_iov.iov_base, p_iov.iov_len);
+ kunmap(rqst->rq_pages[i]);
+ }
+
+ rc = crypto_shash_final(&server->secmech.sdeschmacsha256->shash,
+ sigptr);
+ if (rc)
+ cERROR(1, "%s: Could not generate sha256 hash\n", __func__);
+
+ memcpy(smb2_pdu->Signature, sigptr, SMB2_SIGNATURE_SIZE);
+
+ return rc;
+}
+
+/* must be called with server->srv_mutex held */
+static int
+smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+{
+ int rc = 0;
+ struct smb2_hdr *smb2_pdu = rqst->rq_iov[0].iov_base;
+
+ if (!(smb2_pdu->Flags & SMB2_FLAGS_SIGNED) ||
+ server->tcpStatus == CifsNeedNegotiate)
+ return rc;
+
+ if (!server->session_estab) {
+ strncpy(smb2_pdu->Signature, "BSRSPYL", 8);
+ return rc;
+ }
+
+ rc = smb2_calc_signature(rqst, server);
+
+ return rc;
+}
+
+int
+smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
+{
+ unsigned int rc;
+ char server_response_sig[16];
+ struct smb2_hdr *smb2_pdu = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
+
+ if ((smb2_pdu->Command == SMB2_NEGOTIATE) ||
+ (smb2_pdu->Command == SMB2_OPLOCK_BREAK) ||
+ (!server->session_estab))
+ return 0;
+
+ /*
+ * BB what if signatures are supposed to be on for session but
+ * server does not send one? BB
+ */
+
+ /* Do not need to verify session setups with signature "BSRSPYL " */
+ if (memcmp(smb2_pdu->Signature, "BSRSPYL ", 8) == 0)
+ cFYI(1, "dummy signature received for smb command 0x%x",
+ smb2_pdu->Command);
+
+ /*
+ * Save off the origiginal signature so we can modify the smb and check
+ * our calculated signature against what the server sent.
+ */
+ memcpy(server_response_sig, smb2_pdu->Signature, SMB2_SIGNATURE_SIZE);
+
+ memset(smb2_pdu->Signature, 0, SMB2_SIGNATURE_SIZE);
+
+ mutex_lock(&server->srv_mutex);
+ rc = smb2_calc_signature(rqst, server);
+ mutex_unlock(&server->srv_mutex);
+
+ if (rc)
+ return rc;
+
+ if (memcmp(server_response_sig, smb2_pdu->Signature,
+ SMB2_SIGNATURE_SIZE))
+ return -EACCES;
+ else
+ return 0;
+}
/*
* Set message id for the request. Should be called after wait_for_free_request
@@ -115,58 +259,66 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
bool log_error)
{
unsigned int len = get_rfc1002_length(mid->resp_buf);
+ struct kvec iov;
+ struct smb_rqst rqst = { .rq_iov = &iov,
+ .rq_nvec = 1 };
+
+ iov.iov_base = (char *)mid->resp_buf;
+ iov.iov_len = get_rfc1002_length(mid->resp_buf) + 4;
dump_smb(mid->resp_buf, min_t(u32, 80, len));
/* convert the length into a more usable form */
- /* BB - uncomment with SMB2 signing implementation */
- /* if ((len > 24) &&
+ if ((len > 24) &&
(server->sec_mode & (SECMODE_SIGN_REQUIRED|SECMODE_SIGN_ENABLED))) {
- if (smb2_verify_signature(mid->resp_buf, server))
- cERROR(1, "Unexpected SMB signature");
- } */
+ int rc;
+
+ rc = smb2_verify_signature(&rqst, server);
+ if (rc)
+ cERROR(1, "SMB signature verification returned error = "
+ "%d", rc);
+ }
return map_smb2_to_linux_error(mid->resp_buf, log_error);
}
-int
-smb2_setup_request(struct cifs_ses *ses, struct kvec *iov,
- unsigned int nvec, struct mid_q_entry **ret_mid)
+struct mid_q_entry *
+smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
{
int rc;
- struct smb2_hdr *hdr = (struct smb2_hdr *)iov[0].iov_base;
+ struct smb2_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
smb2_seq_num_into_buf(ses->server, hdr);
rc = smb2_get_mid_entry(ses, hdr, &mid);
if (rc)
- return rc;
- /* rc = smb2_sign_smb2(iov, nvec, ses->server);
- if (rc)
- delete_mid(mid); */
- *ret_mid = mid;
- return rc;
+ return ERR_PTR(rc);
+ rc = smb2_sign_rqst(rqst, ses->server);
+ if (rc) {
+ cifs_delete_mid(mid);
+ return ERR_PTR(rc);
+ }
+ return mid;
}
-int
-smb2_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov,
- unsigned int nvec, struct mid_q_entry **ret_mid)
+struct mid_q_entry *
+smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
{
- int rc = 0;
- struct smb2_hdr *hdr = (struct smb2_hdr *)iov[0].iov_base;
+ int rc;
+ struct smb2_hdr *hdr = (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
smb2_seq_num_into_buf(server, hdr);
mid = smb2_mid_entry_alloc(hdr, server);
if (mid == NULL)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
- /* rc = smb2_sign_smb2(iov, nvec, server);
+ rc = smb2_sign_rqst(rqst, server);
if (rc) {
DeleteMidQEntry(mid);
- return rc;
- }*/
- *ret_mid = mid;
- return rc;
+ return ERR_PTR(rc);
+ }
+
+ return mid;
}
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
index d9b639b95fa8..2126ab185045 100644
--- a/fs/cifs/transport.c
+++ b/fs/cifs/transport.c
@@ -27,6 +27,8 @@
#include <linux/net.h>
#include <linux/delay.h>
#include <linux/freezer.h>
+#include <linux/tcp.h>
+#include <linux/highmem.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <linux/mempool.h>
@@ -109,8 +111,8 @@ DeleteMidQEntry(struct mid_q_entry *midEntry)
mempool_free(midEntry, cifs_mid_poolp);
}
-static void
-delete_mid(struct mid_q_entry *mid)
+void
+cifs_delete_mid(struct mid_q_entry *mid)
{
spin_lock(&GlobalMid_Lock);
list_del(&mid->qhead);
@@ -119,18 +121,29 @@ delete_mid(struct mid_q_entry *mid)
DeleteMidQEntry(mid);
}
+/*
+ * smb_send_kvec - send an array of kvecs to the server
+ * @server: Server to send the data to
+ * @iov: Pointer to array of kvecs
+ * @n_vec: length of kvec array
+ * @sent: amount of data sent on socket is stored here
+ *
+ * Our basic "send data to server" function. Should be called with srv_mutex
+ * held. The caller is responsible for handling the results.
+ */
static int
-smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
+smb_send_kvec(struct TCP_Server_Info *server, struct kvec *iov, size_t n_vec,
+ size_t *sent)
{
int rc = 0;
int i = 0;
struct msghdr smb_msg;
- unsigned int len = iov[0].iov_len;
- unsigned int total_len;
- int first_vec = 0;
- unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base);
+ unsigned int remaining;
+ size_t first_vec = 0;
struct socket *ssocket = server->ssocket;
+ *sent = 0;
+
if (ssocket == NULL)
return -ENOTSOCK; /* BB eventually add reconnect code here */
@@ -143,56 +156,60 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
else
smb_msg.msg_flags = MSG_NOSIGNAL;
- total_len = 0;
+ remaining = 0;
for (i = 0; i < n_vec; i++)
- total_len += iov[i].iov_len;
-
- cFYI(1, "Sending smb: total_len %d", total_len);
- dump_smb(iov[0].iov_base, len);
+ remaining += iov[i].iov_len;
i = 0;
- while (total_len) {
+ while (remaining) {
+ /*
+ * If blocking send, we try 3 times, since each can block
+ * for 5 seconds. For nonblocking we have to try more
+ * but wait increasing amounts of time allowing time for
+ * socket to clear. The overall time we wait in either
+ * case to send on the socket is about 15 seconds.
+ * Similarly we wait for 15 seconds for a response from
+ * the server in SendReceive[2] for the server to send
+ * a response back for most types of requests (except
+ * SMB Write past end of file which can be slow, and
+ * blocking lock operations). NFS waits slightly longer
+ * than CIFS, but this can make it take longer for
+ * nonresponsive servers to be detected and 15 seconds
+ * is more than enough time for modern networks to
+ * send a packet. In most cases if we fail to send
+ * after the retries we will kill the socket and
+ * reconnect which may clear the network problem.
+ */
rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
- n_vec - first_vec, total_len);
- if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
+ n_vec - first_vec, remaining);
+ if (rc == -ENOSPC || rc == -EAGAIN) {
i++;
- /*
- * If blocking send we try 3 times, since each can block
- * for 5 seconds. For nonblocking we have to try more
- * but wait increasing amounts of time allowing time for
- * socket to clear. The overall time we wait in either
- * case to send on the socket is about 15 seconds.
- * Similarly we wait for 15 seconds for a response from
- * the server in SendReceive[2] for the server to send
- * a response back for most types of requests (except
- * SMB Write past end of file which can be slow, and
- * blocking lock operations). NFS waits slightly longer
- * than CIFS, but this can make it take longer for
- * nonresponsive servers to be detected and 15 seconds
- * is more than enough time for modern networks to
- * send a packet. In most cases if we fail to send
- * after the retries we will kill the socket and
- * reconnect which may clear the network problem.
- */
- if ((i >= 14) || (!server->noblocksnd && (i > 2))) {
- cERROR(1, "sends on sock %p stuck for 15 seconds",
- ssocket);
+ if (i >= 14 || (!server->noblocksnd && (i > 2))) {
+ cERROR(1, "sends on sock %p stuck for 15 "
+ "seconds", ssocket);
rc = -EAGAIN;
break;
}
msleep(1 << i);
continue;
}
+
if (rc < 0)
break;
- if (rc == total_len) {
- total_len = 0;
+ /* send was at least partially successful */
+ *sent += rc;
+
+ if (rc == remaining) {
+ remaining = 0;
break;
- } else if (rc > total_len) {
- cERROR(1, "sent %d requested %d", rc, total_len);
+ }
+
+ if (rc > remaining) {
+ cERROR(1, "sent %d requested %d", rc, remaining);
break;
}
+
if (rc == 0) {
/* should never happen, letting socket clear before
retrying is our only obvious option here */
@@ -200,7 +217,9 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
msleep(500);
continue;
}
- total_len -= rc;
+
+ remaining -= rc;
+
/* the line below resets i */
for (i = first_vec; i < n_vec; i++) {
if (iov[i].iov_len) {
@@ -215,16 +234,97 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
}
}
}
+
i = 0; /* in case we get ENOSPC on the next send */
+ rc = 0;
}
+ return rc;
+}
+
+/**
+ * rqst_page_to_kvec - Turn a slot in the smb_rqst page array into a kvec
+ * @rqst: pointer to smb_rqst
+ * @idx: index into the array of the page
+ * @iov: pointer to struct kvec that will hold the result
+ *
+ * Helper function to convert a slot in the rqst->rq_pages array into a kvec.
+ * The page will be kmapped and the address placed into iov_base. The length
+ * will then be adjusted according to the ptailoff.
+ */
+void
+cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx,
+ struct kvec *iov)
+{
+ /*
+ * FIXME: We could avoid this kmap altogether if we used
+ * kernel_sendpage instead of kernel_sendmsg. That will only
+ * work if signing is disabled though as sendpage inlines the
+ * page directly into the fraglist. If userspace modifies the
+ * page after we calculate the signature, then the server will
+ * reject it and may break the connection. kernel_sendmsg does
+ * an extra copy of the data and avoids that issue.
+ */
+ iov->iov_base = kmap(rqst->rq_pages[idx]);
+
+ /* if last page, don't send beyond this offset into page */
+ if (idx == (rqst->rq_npages - 1))
+ iov->iov_len = rqst->rq_tailsz;
+ else
+ iov->iov_len = rqst->rq_pagesz;
+}
+
+static int
+smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)
+{
+ int rc;
+ struct kvec *iov = rqst->rq_iov;
+ int n_vec = rqst->rq_nvec;
+ unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base);
+ unsigned int i;
+ size_t total_len = 0, sent;
+ struct socket *ssocket = server->ssocket;
+ int val = 1;
+
+ cFYI(1, "Sending smb: smb_len=%u", smb_buf_length);
+ dump_smb(iov[0].iov_base, iov[0].iov_len);
+
+ /* cork the socket */
+ kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
+ (char *)&val, sizeof(val));
+
+ rc = smb_send_kvec(server, iov, n_vec, &sent);
+ if (rc < 0)
+ goto uncork;
+
+ total_len += sent;
+
+ /* now walk the page array and send each page in it */
+ for (i = 0; i < rqst->rq_npages; i++) {
+ struct kvec p_iov;
+
+ cifs_rqst_page_to_kvec(rqst, i, &p_iov);
+ rc = smb_send_kvec(server, &p_iov, 1, &sent);
+ kunmap(rqst->rq_pages[i]);
+ if (rc < 0)
+ break;
+
+ total_len += sent;
+ }
+
+uncork:
+ /* uncork it */
+ val = 0;
+ kernel_setsockopt(ssocket, SOL_TCP, TCP_CORK,
+ (char *)&val, sizeof(val));
if ((total_len > 0) && (total_len != smb_buf_length + 4)) {
- cFYI(1, "partial send (%d remaining), terminating session",
- total_len);
- /* If we have only sent part of an SMB then the next SMB
- could be taken as the remainder of this one. We need
- to kill the socket so the server throws away the partial
- SMB */
+ cFYI(1, "partial send (wanted=%u sent=%zu): terminating "
+ "session", smb_buf_length + 4, total_len);
+ /*
+ * If we have only sent part of an SMB then the next SMB could
+ * be taken as the remainder of this one. We need to kill the
+ * socket so the server throws away the partial SMB
+ */
server->tcpStatus = CifsNeedReconnect;
}
@@ -236,6 +336,15 @@ smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
return rc;
}
+static int
+smb_sendv(struct TCP_Server_Info *server, struct kvec *iov, int n_vec)
+{
+ struct smb_rqst rqst = { .rq_iov = iov,
+ .rq_nvec = n_vec };
+
+ return smb_send_rqst(server, &rqst);
+}
+
int
smb_send(struct TCP_Server_Info *server, struct smb_hdr *smb_buffer,
unsigned int smb_buf_length)
@@ -345,12 +454,11 @@ wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ)
return 0;
}
-int
-cifs_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov,
- unsigned int nvec, struct mid_q_entry **ret_mid)
+struct mid_q_entry *
+cifs_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
{
int rc;
- struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base;
+ struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
/* enable signing if server requires it */
@@ -359,16 +467,15 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov,
mid = AllocMidQEntry(hdr, server);
if (mid == NULL)
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
- rc = cifs_sign_smbv(iov, nvec, server, &mid->sequence_number);
+ rc = cifs_sign_rqst(rqst, server, &mid->sequence_number);
if (rc) {
DeleteMidQEntry(mid);
- return rc;
+ return ERR_PTR(rc);
}
- *ret_mid = mid;
- return 0;
+ return mid;
}
/*
@@ -376,9 +483,9 @@ cifs_setup_async_request(struct TCP_Server_Info *server, struct kvec *iov,
* the result. Caller is responsible for dealing with timeouts.
*/
int
-cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
- unsigned int nvec, mid_receive_t *receive,
- mid_callback_t *callback, void *cbdata, const int flags)
+cifs_call_async(struct TCP_Server_Info *server, struct smb_rqst *rqst,
+ mid_receive_t *receive, mid_callback_t *callback,
+ void *cbdata, const int flags)
{
int rc, timeout, optype;
struct mid_q_entry *mid;
@@ -391,12 +498,12 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
return rc;
mutex_lock(&server->srv_mutex);
- rc = server->ops->setup_async_request(server, iov, nvec, &mid);
- if (rc) {
+ mid = server->ops->setup_async_request(server, rqst);
+ if (IS_ERR(mid)) {
mutex_unlock(&server->srv_mutex);
add_credits(server, 1, optype);
wake_up(&server->request_q);
- return rc;
+ return PTR_ERR(mid);
}
mid->receive = receive;
@@ -411,7 +518,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
cifs_in_send_inc(server);
- rc = smb_sendv(server, iov, nvec);
+ rc = smb_send_rqst(server, rqst);
cifs_in_send_dec(server);
cifs_save_when_sent(mid);
mutex_unlock(&server->srv_mutex);
@@ -419,7 +526,7 @@ cifs_call_async(struct TCP_Server_Info *server, struct kvec *iov,
if (rc == 0)
return 0;
- delete_mid(mid);
+ cifs_delete_mid(mid);
add_credits(server, 1, optype);
wake_up(&server->request_q);
return rc;
@@ -504,11 +611,13 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
struct kvec iov;
int rc = 0;
+ struct smb_rqst rqst = { .rq_iov = &iov,
+ .rq_nvec = 1 };
iov.iov_base = mid->resp_buf;
iov.iov_len = len;
/* FIXME: add code to kill session */
- rc = cifs_verify_signature(&iov, 1, server,
+ rc = cifs_verify_signature(&rqst, server,
mid->sequence_number + 1);
if (rc)
cERROR(1, "SMB signature verification returned error = "
@@ -519,22 +628,22 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
return map_smb_to_linux_error(mid->resp_buf, log_error);
}
-int
-cifs_setup_request(struct cifs_ses *ses, struct kvec *iov,
- unsigned int nvec, struct mid_q_entry **ret_mid)
+struct mid_q_entry *
+cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
{
int rc;
- struct smb_hdr *hdr = (struct smb_hdr *)iov[0].iov_base;
+ struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid;
rc = allocate_mid(ses, hdr, &mid);
if (rc)
- return rc;
- rc = cifs_sign_smbv(iov, nvec, ses->server, &mid->sequence_number);
- if (rc)
- delete_mid(mid);
- *ret_mid = mid;
- return rc;
+ return ERR_PTR(rc);
+ rc = cifs_sign_rqst(rqst, ses->server, &mid->sequence_number);
+ if (rc) {
+ cifs_delete_mid(mid);
+ return ERR_PTR(rc);
+ }
+ return mid;
}
int
@@ -547,6 +656,8 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
struct mid_q_entry *midQ;
char *buf = iov[0].iov_base;
unsigned int credits = 1;
+ struct smb_rqst rqst = { .rq_iov = iov,
+ .rq_nvec = n_vec };
timeout = flags & CIFS_TIMEOUT_MASK;
optype = flags & CIFS_OP_MASK;
@@ -584,13 +695,13 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
mutex_lock(&ses->server->srv_mutex);
- rc = ses->server->ops->setup_request(ses, iov, n_vec, &midQ);
- if (rc) {
+ midQ = ses->server->ops->setup_request(ses, &rqst);
+ if (IS_ERR(midQ)) {
mutex_unlock(&ses->server->srv_mutex);
cifs_small_buf_release(buf);
/* Update # of requests on wire to server */
add_credits(ses->server, 1, optype);
- return rc;
+ return PTR_ERR(midQ);
}
midQ->mid_state = MID_REQUEST_SUBMITTED;
@@ -652,11 +763,11 @@ SendReceive2(const unsigned int xid, struct cifs_ses *ses,
rc = ses->server->ops->check_receive(midQ, ses->server,
flags & CIFS_LOG_ERROR);
- /* mark it so buf will not be freed by delete_mid */
+ /* mark it so buf will not be freed by cifs_delete_mid */
if ((flags & CIFS_NO_RESP) == 0)
midQ->resp_buf = NULL;
out:
- delete_mid(midQ);
+ cifs_delete_mid(midQ);
add_credits(ses->server, credits, optype);
return rc;
@@ -762,7 +873,7 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, ses->server, 0);
out:
- delete_mid(midQ);
+ cifs_delete_mid(midQ);
add_credits(ses->server, 1, 0);
return rc;
@@ -846,7 +957,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
if (rc) {
- delete_mid(midQ);
+ cifs_delete_mid(midQ);
mutex_unlock(&ses->server->srv_mutex);
return rc;
}
@@ -859,7 +970,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
mutex_unlock(&ses->server->srv_mutex);
if (rc < 0) {
- delete_mid(midQ);
+ cifs_delete_mid(midQ);
return rc;
}
@@ -880,7 +991,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
blocking lock to return. */
rc = send_cancel(ses->server, in_buf, midQ);
if (rc) {
- delete_mid(midQ);
+ cifs_delete_mid(midQ);
return rc;
}
} else {
@@ -892,7 +1003,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
/* If we get -ENOLCK back the lock may have
already been removed. Don't exit in this case. */
if (rc && rc != -ENOLCK) {
- delete_mid(midQ);
+ cifs_delete_mid(midQ);
return rc;
}
}
@@ -929,7 +1040,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
memcpy(out_buf, midQ->resp_buf, *pbytes_returned + 4);
rc = cifs_check_receive(midQ, ses->server, 0);
out:
- delete_mid(midQ);
+ cifs_delete_mid(midQ);
if (rstart && rc == -EACCES)
return -ERESTARTSYS;
return rc;
diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c
index debdfe0fc809..59f8db4a39a7 100644
--- a/fs/compat_ioctl.c
+++ b/fs/compat_ioctl.c
@@ -866,6 +866,12 @@ COMPATIBLE_IOCTL(TIOCGPTN)
COMPATIBLE_IOCTL(TIOCSPTLCK)
COMPATIBLE_IOCTL(TIOCSERGETLSR)
COMPATIBLE_IOCTL(TIOCSIG)
+#ifdef TIOCSRS485
+COMPATIBLE_IOCTL(TIOCSRS485)
+#endif
+#ifdef TIOCGRS485
+COMPATIBLE_IOCTL(TIOCGRS485)
+#endif
#ifdef TCGETS2
COMPATIBLE_IOCTL(TCGETS2)
COMPATIBLE_IOCTL(TCSETS2)
diff --git a/fs/dcache.c b/fs/dcache.c
index 8086636bf796..693f95bf1cae 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -389,7 +389,7 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent)
* Inform try_to_ascend() that we are no longer attached to the
* dentry tree
*/
- dentry->d_flags |= DCACHE_DISCONNECTED;
+ dentry->d_flags |= DCACHE_DENTRY_KILLED;
if (parent)
spin_unlock(&parent->d_lock);
dentry_iput(dentry);
@@ -1048,7 +1048,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq
* or deletion
*/
if (new != old->d_parent ||
- (old->d_flags & DCACHE_DISCONNECTED) ||
+ (old->d_flags & DCACHE_DENTRY_KILLED) ||
(!locked && read_seqretry(&rename_lock, seq))) {
spin_unlock(&new->d_lock);
new = NULL;
@@ -1134,6 +1134,8 @@ positive:
return 1;
rename_retry:
+ if (locked)
+ goto again;
locked = 1;
write_seqlock(&rename_lock);
goto again;
@@ -1141,7 +1143,7 @@ rename_retry:
EXPORT_SYMBOL(have_submounts);
/*
- * Search the dentry child list for the specified parent,
+ * Search the dentry child list of the specified parent,
* and move any unused dentries to the end of the unused
* list for prune_dcache(). We descend to the next level
* whenever the d_subdirs list is non-empty and continue
@@ -1236,6 +1238,8 @@ out:
rename_retry:
if (found)
return found;
+ if (locked)
+ goto again;
locked = 1;
write_seqlock(&rename_lock);
goto again;
@@ -3035,6 +3039,8 @@ resume:
return;
rename_retry:
+ if (locked)
+ goto again;
locked = 1;
write_seqlock(&rename_lock);
goto again;
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index 2340f6978d6e..c5ca6ae5a30c 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -526,73 +526,51 @@ struct array_data {
u32 elements;
};
-static int u32_array_open(struct inode *inode, struct file *file)
-{
- file->private_data = NULL;
- return nonseekable_open(inode, file);
-}
-
-static size_t format_array(char *buf, size_t bufsize, const char *fmt,
- u32 *array, u32 array_size)
+static size_t u32_format_array(char *buf, size_t bufsize,
+ u32 *array, int array_size)
{
size_t ret = 0;
- u32 i;
- for (i = 0; i < array_size; i++) {
+ while (--array_size >= 0) {
size_t len;
+ char term = array_size ? ' ' : '\n';
- len = snprintf(buf, bufsize, fmt, array[i]);
- len++; /* ' ' or '\n' */
+ len = snprintf(buf, bufsize, "%u%c", *array++, term);
ret += len;
- if (buf) {
- buf += len;
- bufsize -= len;
- buf[-1] = (i == array_size-1) ? '\n' : ' ';
- }
+ buf += len;
+ bufsize -= len;
}
-
- ret++; /* \0 */
- if (buf)
- *buf = '\0';
-
return ret;
}
-static char *format_array_alloc(const char *fmt, u32 *array,
- u32 array_size)
+static int u32_array_open(struct inode *inode, struct file *file)
{
- size_t len = format_array(NULL, 0, fmt, array, array_size);
- char *ret;
-
- ret = kmalloc(len, GFP_KERNEL);
- if (ret == NULL)
- return NULL;
+ struct array_data *data = inode->i_private;
+ int size, elements = data->elements;
+ char *buf;
+
+ /*
+ * Max size:
+ * - 10 digits + ' '/'\n' = 11 bytes per number
+ * - terminating NUL character
+ */
+ size = elements*11;
+ buf = kmalloc(size+1, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+ buf[size] = 0;
+
+ file->private_data = buf;
+ u32_format_array(buf, size, data->array, data->elements);
- format_array(ret, len, fmt, array, array_size);
- return ret;
+ return nonseekable_open(inode, file);
}
static ssize_t u32_array_read(struct file *file, char __user *buf, size_t len,
loff_t *ppos)
{
- struct inode *inode = file->f_path.dentry->d_inode;
- struct array_data *data = inode->i_private;
- size_t size;
-
- if (*ppos == 0) {
- if (file->private_data) {
- kfree(file->private_data);
- file->private_data = NULL;
- }
-
- file->private_data = format_array_alloc("%u", data->array,
- data->elements);
- }
-
- size = 0;
- if (file->private_data)
- size = strlen(file->private_data);
+ size_t size = strlen(file->private_data);
return simple_read_from_buffer(buf, len, ppos,
file->private_data, size);
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index 4733eab34a23..6393fd61d5c4 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -28,7 +28,7 @@
#include <linux/magic.h>
#include <linux/slab.h>
-#define DEBUGFS_DEFAULT_MODE 0755
+#define DEBUGFS_DEFAULT_MODE 0700
static struct vfsmount *debugfs_mount;
static int debugfs_mount_count;
@@ -291,9 +291,9 @@ static struct file_system_type debug_fs_type = {
.kill_sb = kill_litter_super,
};
-struct dentry *__create_file(const char *name, umode_t mode,
- struct dentry *parent, void *data,
- const struct file_operations *fops)
+static struct dentry *__create_file(const char *name, umode_t mode,
+ struct dentry *parent, void *data,
+ const struct file_operations *fops)
{
struct dentry *dentry = NULL;
int error;
diff --git a/fs/dlm/ast.c b/fs/dlm/ast.c
index 63dc19c54d5a..27a6ba9aaeec 100644
--- a/fs/dlm/ast.c
+++ b/fs/dlm/ast.c
@@ -15,8 +15,8 @@
#include "lock.h"
#include "user.h"
-static uint64_t dlm_cb_seq;
-static spinlock_t dlm_cb_seq_spin;
+static uint64_t dlm_cb_seq;
+static DEFINE_SPINLOCK(dlm_cb_seq_spin);
static void dlm_dump_lkb_callbacks(struct dlm_lkb *lkb)
{
diff --git a/fs/dlm/config.c b/fs/dlm/config.c
index 9ccf7346834a..a0387dd8b1f0 100644
--- a/fs/dlm/config.c
+++ b/fs/dlm/config.c
@@ -750,6 +750,7 @@ static ssize_t comm_local_write(struct dlm_comm *cm, const char *buf,
static ssize_t comm_addr_write(struct dlm_comm *cm, const char *buf, size_t len)
{
struct sockaddr_storage *addr;
+ int rv;
if (len != sizeof(struct sockaddr_storage))
return -EINVAL;
@@ -762,6 +763,13 @@ static ssize_t comm_addr_write(struct dlm_comm *cm, const char *buf, size_t len)
return -ENOMEM;
memcpy(addr, buf, len);
+
+ rv = dlm_lowcomms_addr(cm->nodeid, addr, len);
+ if (rv) {
+ kfree(addr);
+ return rv;
+ }
+
cm->addr[cm->addr_count++] = addr;
return len;
}
@@ -878,34 +886,7 @@ static void put_space(struct dlm_space *sp)
config_item_put(&sp->group.cg_item);
}
-static int addr_compare(struct sockaddr_storage *x, struct sockaddr_storage *y)
-{
- switch (x->ss_family) {
- case AF_INET: {
- struct sockaddr_in *sinx = (struct sockaddr_in *)x;
- struct sockaddr_in *siny = (struct sockaddr_in *)y;
- if (sinx->sin_addr.s_addr != siny->sin_addr.s_addr)
- return 0;
- if (sinx->sin_port != siny->sin_port)
- return 0;
- break;
- }
- case AF_INET6: {
- struct sockaddr_in6 *sinx = (struct sockaddr_in6 *)x;
- struct sockaddr_in6 *siny = (struct sockaddr_in6 *)y;
- if (!ipv6_addr_equal(&sinx->sin6_addr, &siny->sin6_addr))
- return 0;
- if (sinx->sin6_port != siny->sin6_port)
- return 0;
- break;
- }
- default:
- return 0;
- }
- return 1;
-}
-
-static struct dlm_comm *get_comm(int nodeid, struct sockaddr_storage *addr)
+static struct dlm_comm *get_comm(int nodeid)
{
struct config_item *i;
struct dlm_comm *cm = NULL;
@@ -919,19 +900,11 @@ static struct dlm_comm *get_comm(int nodeid, struct sockaddr_storage *addr)
list_for_each_entry(i, &comm_list->cg_children, ci_entry) {
cm = config_item_to_comm(i);
- if (nodeid) {
- if (cm->nodeid != nodeid)
- continue;
- found = 1;
- config_item_get(i);
- break;
- } else {
- if (!cm->addr_count || !addr_compare(cm->addr[0], addr))
- continue;
- found = 1;
- config_item_get(i);
- break;
- }
+ if (cm->nodeid != nodeid)
+ continue;
+ found = 1;
+ config_item_get(i);
+ break;
}
mutex_unlock(&clusters_root.subsys.su_mutex);
@@ -995,7 +968,7 @@ int dlm_config_nodes(char *lsname, struct dlm_config_node **nodes_out,
int dlm_comm_seq(int nodeid, uint32_t *seq)
{
- struct dlm_comm *cm = get_comm(nodeid, NULL);
+ struct dlm_comm *cm = get_comm(nodeid);
if (!cm)
return -EEXIST;
*seq = cm->seq;
@@ -1003,28 +976,6 @@ int dlm_comm_seq(int nodeid, uint32_t *seq)
return 0;
}
-int dlm_nodeid_to_addr(int nodeid, struct sockaddr_storage *addr)
-{
- struct dlm_comm *cm = get_comm(nodeid, NULL);
- if (!cm)
- return -EEXIST;
- if (!cm->addr_count)
- return -ENOENT;
- memcpy(addr, cm->addr[0], sizeof(*addr));
- put_comm(cm);
- return 0;
-}
-
-int dlm_addr_to_nodeid(struct sockaddr_storage *addr, int *nodeid)
-{
- struct dlm_comm *cm = get_comm(0, addr);
- if (!cm)
- return -EEXIST;
- *nodeid = cm->nodeid;
- put_comm(cm);
- return 0;
-}
-
int dlm_our_nodeid(void)
{
return local_comm ? local_comm->nodeid : 0;
diff --git a/fs/dlm/config.h b/fs/dlm/config.h
index dbd35a08f3a5..f30697bc2780 100644
--- a/fs/dlm/config.h
+++ b/fs/dlm/config.h
@@ -46,8 +46,6 @@ void dlm_config_exit(void);
int dlm_config_nodes(char *lsname, struct dlm_config_node **nodes_out,
int *count_out);
int dlm_comm_seq(int nodeid, uint32_t *seq);
-int dlm_nodeid_to_addr(int nodeid, struct sockaddr_storage *addr);
-int dlm_addr_to_nodeid(struct sockaddr_storage *addr, int *nodeid);
int dlm_our_nodeid(void);
int dlm_our_addr(struct sockaddr_storage *addr, int num);
diff --git a/fs/dlm/dlm_internal.h b/fs/dlm/dlm_internal.h
index 9d3e485f88c8..871c1abf6029 100644
--- a/fs/dlm/dlm_internal.h
+++ b/fs/dlm/dlm_internal.h
@@ -604,6 +604,7 @@ struct dlm_ls {
struct idr ls_recover_idr;
spinlock_t ls_recover_idr_lock;
wait_queue_head_t ls_wait_general;
+ wait_queue_head_t ls_recover_lock_wait;
struct mutex ls_clear_proc_locks;
struct list_head ls_root_list; /* root resources */
@@ -616,15 +617,40 @@ struct dlm_ls {
char ls_name[1];
};
-#define LSFL_WORK 0
-#define LSFL_RUNNING 1
-#define LSFL_RECOVERY_STOP 2
-#define LSFL_RCOM_READY 3
-#define LSFL_RCOM_WAIT 4
-#define LSFL_UEVENT_WAIT 5
-#define LSFL_TIMEWARN 6
-#define LSFL_CB_DELAY 7
-#define LSFL_NODIR 8
+/*
+ * LSFL_RECOVER_STOP - dlm_ls_stop() sets this to tell dlm recovery routines
+ * that they should abort what they're doing so new recovery can be started.
+ *
+ * LSFL_RECOVER_DOWN - dlm_ls_stop() sets this to tell dlm_recoverd that it
+ * should do down_write() on the in_recovery rw_semaphore. (doing down_write
+ * within dlm_ls_stop causes complaints about the lock acquired/released
+ * in different contexts.)
+ *
+ * LSFL_RECOVER_LOCK - dlm_recoverd holds the in_recovery rw_semaphore.
+ * It sets this after it is done with down_write() on the in_recovery
+ * rw_semaphore and clears it after it has released the rw_semaphore.
+ *
+ * LSFL_RECOVER_WORK - dlm_ls_start() sets this to tell dlm_recoverd that it
+ * should begin recovery of the lockspace.
+ *
+ * LSFL_RUNNING - set when normal locking activity is enabled.
+ * dlm_ls_stop() clears this to tell dlm locking routines that they should
+ * quit what they are doing so recovery can run. dlm_recoverd sets
+ * this after recovery is finished.
+ */
+
+#define LSFL_RECOVER_STOP 0
+#define LSFL_RECOVER_DOWN 1
+#define LSFL_RECOVER_LOCK 2
+#define LSFL_RECOVER_WORK 3
+#define LSFL_RUNNING 4
+
+#define LSFL_RCOM_READY 5
+#define LSFL_RCOM_WAIT 6
+#define LSFL_UEVENT_WAIT 7
+#define LSFL_TIMEWARN 8
+#define LSFL_CB_DELAY 9
+#define LSFL_NODIR 10
/* much of this is just saving user space pointers associated with the
lock that we pass back to the user lib with an ast */
@@ -667,7 +693,7 @@ static inline int dlm_locking_stopped(struct dlm_ls *ls)
static inline int dlm_recovery_stopped(struct dlm_ls *ls)
{
- return test_bit(LSFL_RECOVERY_STOP, &ls->ls_flags);
+ return test_bit(LSFL_RECOVER_STOP, &ls->ls_flags);
}
static inline int dlm_no_directory(struct dlm_ls *ls)
diff --git a/fs/dlm/lockspace.c b/fs/dlm/lockspace.c
index 952557d00ccd..2e99fb0c9737 100644
--- a/fs/dlm/lockspace.c
+++ b/fs/dlm/lockspace.c
@@ -582,8 +582,6 @@ static int new_lockspace(const char *name, const char *cluster,
INIT_LIST_HEAD(&ls->ls_root_list);
init_rwsem(&ls->ls_root_sem);
- down_write(&ls->ls_in_recovery);
-
spin_lock(&lslist_lock);
ls->ls_create_count = 1;
list_add(&ls->ls_list, &lslist);
@@ -597,13 +595,24 @@ static int new_lockspace(const char *name, const char *cluster,
}
}
- /* needs to find ls in lslist */
+ init_waitqueue_head(&ls->ls_recover_lock_wait);
+
+ /*
+ * Once started, dlm_recoverd first looks for ls in lslist, then
+ * initializes ls_in_recovery as locked in "down" mode. We need
+ * to wait for the wakeup from dlm_recoverd because in_recovery
+ * has to start out in down mode.
+ */
+
error = dlm_recoverd_start(ls);
if (error) {
log_error(ls, "can't start dlm_recoverd %d", error);
goto out_callback;
}
+ wait_event(ls->ls_recover_lock_wait,
+ test_bit(LSFL_RECOVER_LOCK, &ls->ls_flags));
+
ls->ls_kobj.kset = dlm_kset;
error = kobject_init_and_add(&ls->ls_kobj, &dlm_ktype, NULL,
"%s", ls->ls_name);
diff --git a/fs/dlm/lowcomms.c b/fs/dlm/lowcomms.c
index 5c1b0e38c7a4..331ea4f94efd 100644
--- a/fs/dlm/lowcomms.c
+++ b/fs/dlm/lowcomms.c
@@ -140,6 +140,16 @@ struct writequeue_entry {
struct connection *con;
};
+struct dlm_node_addr {
+ struct list_head list;
+ int nodeid;
+ int addr_count;
+ struct sockaddr_storage *addr[DLM_MAX_ADDR_COUNT];
+};
+
+static LIST_HEAD(dlm_node_addrs);
+static DEFINE_SPINLOCK(dlm_node_addrs_spin);
+
static struct sockaddr_storage *dlm_local_addr[DLM_MAX_ADDR_COUNT];
static int dlm_local_count;
static int dlm_allow_conn;
@@ -264,31 +274,146 @@ static struct connection *assoc2con(int assoc_id)
return NULL;
}
-static int nodeid_to_addr(int nodeid, struct sockaddr *retaddr)
+static struct dlm_node_addr *find_node_addr(int nodeid)
+{
+ struct dlm_node_addr *na;
+
+ list_for_each_entry(na, &dlm_node_addrs, list) {
+ if (na->nodeid == nodeid)
+ return na;
+ }
+ return NULL;
+}
+
+static int addr_compare(struct sockaddr_storage *x, struct sockaddr_storage *y)
+{
+ switch (x->ss_family) {
+ case AF_INET: {
+ struct sockaddr_in *sinx = (struct sockaddr_in *)x;
+ struct sockaddr_in *siny = (struct sockaddr_in *)y;
+ if (sinx->sin_addr.s_addr != siny->sin_addr.s_addr)
+ return 0;
+ if (sinx->sin_port != siny->sin_port)
+ return 0;
+ break;
+ }
+ case AF_INET6: {
+ struct sockaddr_in6 *sinx = (struct sockaddr_in6 *)x;
+ struct sockaddr_in6 *siny = (struct sockaddr_in6 *)y;
+ if (!ipv6_addr_equal(&sinx->sin6_addr, &siny->sin6_addr))
+ return 0;
+ if (sinx->sin6_port != siny->sin6_port)
+ return 0;
+ break;
+ }
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static int nodeid_to_addr(int nodeid, struct sockaddr_storage *sas_out,
+ struct sockaddr *sa_out)
{
- struct sockaddr_storage addr;
- int error;
+ struct sockaddr_storage sas;
+ struct dlm_node_addr *na;
if (!dlm_local_count)
return -1;
- error = dlm_nodeid_to_addr(nodeid, &addr);
- if (error)
- return error;
+ spin_lock(&dlm_node_addrs_spin);
+ na = find_node_addr(nodeid);
+ if (na && na->addr_count)
+ memcpy(&sas, na->addr[0], sizeof(struct sockaddr_storage));
+ spin_unlock(&dlm_node_addrs_spin);
+
+ if (!na)
+ return -EEXIST;
+
+ if (!na->addr_count)
+ return -ENOENT;
+
+ if (sas_out)
+ memcpy(sas_out, &sas, sizeof(struct sockaddr_storage));
+
+ if (!sa_out)
+ return 0;
if (dlm_local_addr[0]->ss_family == AF_INET) {
- struct sockaddr_in *in4 = (struct sockaddr_in *) &addr;
- struct sockaddr_in *ret4 = (struct sockaddr_in *) retaddr;
+ struct sockaddr_in *in4 = (struct sockaddr_in *) &sas;
+ struct sockaddr_in *ret4 = (struct sockaddr_in *) sa_out;
ret4->sin_addr.s_addr = in4->sin_addr.s_addr;
} else {
- struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) &addr;
- struct sockaddr_in6 *ret6 = (struct sockaddr_in6 *) retaddr;
+ struct sockaddr_in6 *in6 = (struct sockaddr_in6 *) &sas;
+ struct sockaddr_in6 *ret6 = (struct sockaddr_in6 *) sa_out;
ret6->sin6_addr = in6->sin6_addr;
}
return 0;
}
+static int addr_to_nodeid(struct sockaddr_storage *addr, int *nodeid)
+{
+ struct dlm_node_addr *na;
+ int rv = -EEXIST;
+
+ spin_lock(&dlm_node_addrs_spin);
+ list_for_each_entry(na, &dlm_node_addrs, list) {
+ if (!na->addr_count)
+ continue;
+
+ if (!addr_compare(na->addr[0], addr))
+ continue;
+
+ *nodeid = na->nodeid;
+ rv = 0;
+ break;
+ }
+ spin_unlock(&dlm_node_addrs_spin);
+ return rv;
+}
+
+int dlm_lowcomms_addr(int nodeid, struct sockaddr_storage *addr, int len)
+{
+ struct sockaddr_storage *new_addr;
+ struct dlm_node_addr *new_node, *na;
+
+ new_node = kzalloc(sizeof(struct dlm_node_addr), GFP_NOFS);
+ if (!new_node)
+ return -ENOMEM;
+
+ new_addr = kzalloc(sizeof(struct sockaddr_storage), GFP_NOFS);
+ if (!new_addr) {
+ kfree(new_node);
+ return -ENOMEM;
+ }
+
+ memcpy(new_addr, addr, len);
+
+ spin_lock(&dlm_node_addrs_spin);
+ na = find_node_addr(nodeid);
+ if (!na) {
+ new_node->nodeid = nodeid;
+ new_node->addr[0] = new_addr;
+ new_node->addr_count = 1;
+ list_add(&new_node->list, &dlm_node_addrs);
+ spin_unlock(&dlm_node_addrs_spin);
+ return 0;
+ }
+
+ if (na->addr_count >= DLM_MAX_ADDR_COUNT) {
+ spin_unlock(&dlm_node_addrs_spin);
+ kfree(new_addr);
+ kfree(new_node);
+ return -ENOSPC;
+ }
+
+ na->addr[na->addr_count++] = new_addr;
+ spin_unlock(&dlm_node_addrs_spin);
+ kfree(new_node);
+ return 0;
+}
+
/* Data available on socket or listen socket received a connect */
static void lowcomms_data_ready(struct sock *sk, int count_unused)
{
@@ -348,7 +473,7 @@ int dlm_lowcomms_connect_node(int nodeid)
}
/* Make a socket active */
-static int add_sock(struct socket *sock, struct connection *con)
+static void add_sock(struct socket *sock, struct connection *con)
{
con->sock = sock;
@@ -358,7 +483,6 @@ static int add_sock(struct socket *sock, struct connection *con)
con->sock->sk->sk_state_change = lowcomms_state_change;
con->sock->sk->sk_user_data = con;
con->sock->sk->sk_allocation = GFP_NOFS;
- return 0;
}
/* Add the port number to an IPv6 or 4 sockaddr and return the address
@@ -510,7 +634,7 @@ static void process_sctp_notification(struct connection *con,
return;
}
make_sockaddr(&prim.ssp_addr, 0, &addr_len);
- if (dlm_addr_to_nodeid(&prim.ssp_addr, &nodeid)) {
+ if (addr_to_nodeid(&prim.ssp_addr, &nodeid)) {
unsigned char *b=(unsigned char *)&prim.ssp_addr;
log_print("reject connect from unknown addr");
print_hex_dump_bytes("ss: ", DUMP_PREFIX_NONE,
@@ -747,7 +871,7 @@ static int tcp_accept_from_sock(struct connection *con)
/* Get the new node's NODEID */
make_sockaddr(&peeraddr, 0, &len);
- if (dlm_addr_to_nodeid(&peeraddr, &nodeid)) {
+ if (addr_to_nodeid(&peeraddr, &nodeid)) {
unsigned char *b=(unsigned char *)&peeraddr;
log_print("connect from non cluster node");
print_hex_dump_bytes("ss: ", DUMP_PREFIX_NONE,
@@ -862,7 +986,7 @@ static void sctp_init_assoc(struct connection *con)
if (con->retries++ > MAX_CONNECT_RETRIES)
return;
- if (nodeid_to_addr(con->nodeid, (struct sockaddr *)&rem_addr)) {
+ if (nodeid_to_addr(con->nodeid, NULL, (struct sockaddr *)&rem_addr)) {
log_print("no address for nodeid %d", con->nodeid);
return;
}
@@ -928,11 +1052,11 @@ static void sctp_init_assoc(struct connection *con)
/* Connect a new socket to its peer */
static void tcp_connect_to_sock(struct connection *con)
{
- int result = -EHOSTUNREACH;
struct sockaddr_storage saddr, src_addr;
int addr_len;
struct socket *sock = NULL;
int one = 1;
+ int result;
if (con->nodeid == 0) {
log_print("attempt to connect sock 0 foiled");
@@ -944,10 +1068,8 @@ static void tcp_connect_to_sock(struct connection *con)
goto out;
/* Some odd races can cause double-connects, ignore them */
- if (con->sock) {
- result = 0;
+ if (con->sock)
goto out;
- }
/* Create a socket to communicate with */
result = sock_create_kern(dlm_local_addr[0]->ss_family, SOCK_STREAM,
@@ -956,8 +1078,11 @@ static void tcp_connect_to_sock(struct connection *con)
goto out_err;
memset(&saddr, 0, sizeof(saddr));
- if (dlm_nodeid_to_addr(con->nodeid, &saddr))
+ result = nodeid_to_addr(con->nodeid, &saddr, NULL);
+ if (result < 0) {
+ log_print("no address for nodeid %d", con->nodeid);
goto out_err;
+ }
sock->sk->sk_user_data = con;
con->rx_action = receive_from_sock;
@@ -983,8 +1108,7 @@ static void tcp_connect_to_sock(struct connection *con)
kernel_setsockopt(sock, SOL_TCP, TCP_NODELAY, (char *)&one,
sizeof(one));
- result =
- sock->ops->connect(sock, (struct sockaddr *)&saddr, addr_len,
+ result = sock->ops->connect(sock, (struct sockaddr *)&saddr, addr_len,
O_NONBLOCK);
if (result == -EINPROGRESS)
result = 0;
@@ -1002,11 +1126,17 @@ out_err:
* Some errors are fatal and this list might need adjusting. For other
* errors we try again until the max number of retries is reached.
*/
- if (result != -EHOSTUNREACH && result != -ENETUNREACH &&
- result != -ENETDOWN && result != -EINVAL
- && result != -EPROTONOSUPPORT) {
+ if (result != -EHOSTUNREACH &&
+ result != -ENETUNREACH &&
+ result != -ENETDOWN &&
+ result != -EINVAL &&
+ result != -EPROTONOSUPPORT) {
+ log_print("connect %d try %d error %d", con->nodeid,
+ con->retries, result);
+ mutex_unlock(&con->sock_mutex);
+ msleep(1000);
lowcomms_connect_sock(con);
- result = 0;
+ return;
}
out:
mutex_unlock(&con->sock_mutex);
@@ -1044,10 +1174,8 @@ static struct socket *tcp_create_listen_sock(struct connection *con,
if (result < 0) {
log_print("Failed to set SO_REUSEADDR on socket: %d", result);
}
- sock->sk->sk_user_data = con;
con->rx_action = tcp_accept_from_sock;
con->connect_action = tcp_connect_to_sock;
- con->sock = sock;
/* Bind to our port */
make_sockaddr(saddr, dlm_config.ci_tcp_port, &addr_len);
@@ -1358,8 +1486,7 @@ static void send_to_sock(struct connection *con)
}
cond_resched();
goto out;
- }
- if (ret <= 0)
+ } else if (ret < 0)
goto send_error;
}
@@ -1376,7 +1503,6 @@ static void send_to_sock(struct connection *con)
if (e->len == 0 && e->users == 0) {
list_del(&e->list);
free_entry(e);
- continue;
}
}
spin_unlock(&con->writequeue_lock);
@@ -1394,7 +1520,6 @@ out_connect:
mutex_unlock(&con->sock_mutex);
if (!test_bit(CF_INIT_PENDING, &con->flags))
lowcomms_connect_sock(con);
- return;
}
static void clean_one_writequeue(struct connection *con)
@@ -1414,6 +1539,7 @@ static void clean_one_writequeue(struct connection *con)
int dlm_lowcomms_close(int nodeid)
{
struct connection *con;
+ struct dlm_node_addr *na;
log_print("closing connection to node %d", nodeid);
con = nodeid2con(nodeid, 0);
@@ -1428,6 +1554,17 @@ int dlm_lowcomms_close(int nodeid)
clean_one_writequeue(con);
close_connection(con, true);
}
+
+ spin_lock(&dlm_node_addrs_spin);
+ na = find_node_addr(nodeid);
+ if (na) {
+ list_del(&na->list);
+ while (na->addr_count--)
+ kfree(na->addr[na->addr_count]);
+ kfree(na);
+ }
+ spin_unlock(&dlm_node_addrs_spin);
+
return 0;
}
@@ -1577,3 +1714,17 @@ fail_destroy:
fail:
return error;
}
+
+void dlm_lowcomms_exit(void)
+{
+ struct dlm_node_addr *na, *safe;
+
+ spin_lock(&dlm_node_addrs_spin);
+ list_for_each_entry_safe(na, safe, &dlm_node_addrs, list) {
+ list_del(&na->list);
+ while (na->addr_count--)
+ kfree(na->addr[na->addr_count]);
+ kfree(na);
+ }
+ spin_unlock(&dlm_node_addrs_spin);
+}
diff --git a/fs/dlm/lowcomms.h b/fs/dlm/lowcomms.h
index 1311e6426287..67462e54fc2f 100644
--- a/fs/dlm/lowcomms.h
+++ b/fs/dlm/lowcomms.h
@@ -16,10 +16,12 @@
int dlm_lowcomms_start(void);
void dlm_lowcomms_stop(void);
+void dlm_lowcomms_exit(void);
int dlm_lowcomms_close(int nodeid);
void *dlm_lowcomms_get_buffer(int nodeid, int len, gfp_t allocation, char **ppc);
void dlm_lowcomms_commit_buffer(void *mh);
int dlm_lowcomms_connect_node(int nodeid);
+int dlm_lowcomms_addr(int nodeid, struct sockaddr_storage *addr, int len);
#endif /* __LOWCOMMS_DOT_H__ */
diff --git a/fs/dlm/main.c b/fs/dlm/main.c
index 5a59efa0bb46..079c0bd71ab7 100644
--- a/fs/dlm/main.c
+++ b/fs/dlm/main.c
@@ -17,6 +17,7 @@
#include "user.h"
#include "memory.h"
#include "config.h"
+#include "lowcomms.h"
static int __init init_dlm(void)
{
@@ -78,6 +79,7 @@ static void __exit exit_dlm(void)
dlm_config_exit();
dlm_memory_exit();
dlm_lockspace_exit();
+ dlm_lowcomms_exit();
dlm_unregister_debugfs();
}
diff --git a/fs/dlm/member.c b/fs/dlm/member.c
index 862640a36d5c..476557b54921 100644
--- a/fs/dlm/member.c
+++ b/fs/dlm/member.c
@@ -616,13 +616,13 @@ int dlm_ls_stop(struct dlm_ls *ls)
down_write(&ls->ls_recv_active);
/*
- * Abort any recovery that's in progress (see RECOVERY_STOP,
+ * Abort any recovery that's in progress (see RECOVER_STOP,
* dlm_recovery_stopped()) and tell any other threads running in the
* dlm to quit any processing (see RUNNING, dlm_locking_stopped()).
*/
spin_lock(&ls->ls_recover_lock);
- set_bit(LSFL_RECOVERY_STOP, &ls->ls_flags);
+ set_bit(LSFL_RECOVER_STOP, &ls->ls_flags);
new = test_and_clear_bit(LSFL_RUNNING, &ls->ls_flags);
ls->ls_recover_seq++;
spin_unlock(&ls->ls_recover_lock);
@@ -642,12 +642,16 @@ int dlm_ls_stop(struct dlm_ls *ls)
* when recovery is complete.
*/
- if (new)
- down_write(&ls->ls_in_recovery);
+ if (new) {
+ set_bit(LSFL_RECOVER_DOWN, &ls->ls_flags);
+ wake_up_process(ls->ls_recoverd_task);
+ wait_event(ls->ls_recover_lock_wait,
+ test_bit(LSFL_RECOVER_LOCK, &ls->ls_flags));
+ }
/*
* The recoverd suspend/resume makes sure that dlm_recoverd (if
- * running) has noticed RECOVERY_STOP above and quit processing the
+ * running) has noticed RECOVER_STOP above and quit processing the
* previous recovery.
*/
@@ -709,7 +713,8 @@ int dlm_ls_start(struct dlm_ls *ls)
kfree(rv_old);
}
- dlm_recoverd_kick(ls);
+ set_bit(LSFL_RECOVER_WORK, &ls->ls_flags);
+ wake_up_process(ls->ls_recoverd_task);
return 0;
fail:
diff --git a/fs/dlm/rcom.c b/fs/dlm/rcom.c
index 87f1a56eab32..9d61947d473a 100644
--- a/fs/dlm/rcom.c
+++ b/fs/dlm/rcom.c
@@ -581,7 +581,7 @@ void dlm_receive_rcom(struct dlm_ls *ls, struct dlm_rcom *rc, int nodeid)
spin_lock(&ls->ls_recover_lock);
status = ls->ls_recover_status;
- stop = test_bit(LSFL_RECOVERY_STOP, &ls->ls_flags);
+ stop = test_bit(LSFL_RECOVER_STOP, &ls->ls_flags);
seq = ls->ls_recover_seq;
spin_unlock(&ls->ls_recover_lock);
diff --git a/fs/dlm/recoverd.c b/fs/dlm/recoverd.c
index 88ce65ff021e..32f9f8926ec3 100644
--- a/fs/dlm/recoverd.c
+++ b/fs/dlm/recoverd.c
@@ -41,6 +41,7 @@ static int enable_locking(struct dlm_ls *ls, uint64_t seq)
set_bit(LSFL_RUNNING, &ls->ls_flags);
/* unblocks processes waiting to enter the dlm */
up_write(&ls->ls_in_recovery);
+ clear_bit(LSFL_RECOVER_LOCK, &ls->ls_flags);
error = 0;
}
spin_unlock(&ls->ls_recover_lock);
@@ -262,7 +263,7 @@ static void do_ls_recovery(struct dlm_ls *ls)
rv = ls->ls_recover_args;
ls->ls_recover_args = NULL;
if (rv && ls->ls_recover_seq == rv->seq)
- clear_bit(LSFL_RECOVERY_STOP, &ls->ls_flags);
+ clear_bit(LSFL_RECOVER_STOP, &ls->ls_flags);
spin_unlock(&ls->ls_recover_lock);
if (rv) {
@@ -282,26 +283,34 @@ static int dlm_recoverd(void *arg)
return -1;
}
+ down_write(&ls->ls_in_recovery);
+ set_bit(LSFL_RECOVER_LOCK, &ls->ls_flags);
+ wake_up(&ls->ls_recover_lock_wait);
+
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
- if (!test_bit(LSFL_WORK, &ls->ls_flags))
+ if (!test_bit(LSFL_RECOVER_WORK, &ls->ls_flags) &&
+ !test_bit(LSFL_RECOVER_DOWN, &ls->ls_flags))
schedule();
set_current_state(TASK_RUNNING);
- if (test_and_clear_bit(LSFL_WORK, &ls->ls_flags))
+ if (test_and_clear_bit(LSFL_RECOVER_DOWN, &ls->ls_flags)) {
+ down_write(&ls->ls_in_recovery);
+ set_bit(LSFL_RECOVER_LOCK, &ls->ls_flags);
+ wake_up(&ls->ls_recover_lock_wait);
+ }
+
+ if (test_and_clear_bit(LSFL_RECOVER_WORK, &ls->ls_flags))
do_ls_recovery(ls);
}
+ if (test_bit(LSFL_RECOVER_LOCK, &ls->ls_flags))
+ up_write(&ls->ls_in_recovery);
+
dlm_put_lockspace(ls);
return 0;
}
-void dlm_recoverd_kick(struct dlm_ls *ls)
-{
- set_bit(LSFL_WORK, &ls->ls_flags);
- wake_up_process(ls->ls_recoverd_task);
-}
-
int dlm_recoverd_start(struct dlm_ls *ls)
{
struct task_struct *p;
diff --git a/fs/dlm/recoverd.h b/fs/dlm/recoverd.h
index 866657c5d69d..8856079733fa 100644
--- a/fs/dlm/recoverd.h
+++ b/fs/dlm/recoverd.h
@@ -14,7 +14,6 @@
#ifndef __RECOVERD_DOT_H__
#define __RECOVERD_DOT_H__
-void dlm_recoverd_kick(struct dlm_ls *ls);
void dlm_recoverd_stop(struct dlm_ls *ls);
int dlm_recoverd_start(struct dlm_ls *ls);
void dlm_recoverd_suspend(struct dlm_ls *ls);
diff --git a/fs/dlm/user.c b/fs/dlm/user.c
index eb4ed9ba3098..7ff49852b0cb 100644
--- a/fs/dlm/user.c
+++ b/fs/dlm/user.c
@@ -503,6 +503,13 @@ static ssize_t device_write(struct file *file, const char __user *buf,
#endif
return -EINVAL;
+#ifdef CONFIG_COMPAT
+ if (count > sizeof(struct dlm_write_request32) + DLM_RESNAME_MAXLEN)
+#else
+ if (count > sizeof(struct dlm_write_request) + DLM_RESNAME_MAXLEN)
+#endif
+ return -EINVAL;
+
kbuf = kzalloc(count + 1, GFP_NOFS);
if (!kbuf)
return -ENOMEM;
diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c
index 376aa77f3ca7..2616d0ea5c5c 100644
--- a/fs/ext2/balloc.c
+++ b/fs/ext2/balloc.c
@@ -479,7 +479,7 @@ void ext2_discard_reservation(struct inode *inode)
/**
* ext2_free_blocks() -- Free given blocks and update quota and i_blocks
* @inode: inode
- * @block: start physcial block to free
+ * @block: start physical block to free
* @count: number of blocks to free
*/
void ext2_free_blocks (struct inode * inode, unsigned long block,
diff --git a/fs/ext3/balloc.c b/fs/ext3/balloc.c
index 90d901f0486b..7320a66e958f 100644
--- a/fs/ext3/balloc.c
+++ b/fs/ext3/balloc.c
@@ -483,7 +483,7 @@ void ext3_discard_reservation(struct inode *inode)
* ext3_free_blocks_sb() -- Free given blocks and update quota
* @handle: handle to this transaction
* @sb: super block
- * @block: start physcial block to free
+ * @block: start physical block to free
* @count: number of blocks to free
* @pdquot_freed_blocks: pointer to quota
*/
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index ff574b4e345e..7e87e37a372a 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -3207,7 +3207,7 @@ out_brelse:
*
* - Within generic_file_write() for O_SYNC files.
* Here, there will be no transaction running. We wait for any running
- * trasnaction to commit.
+ * transaction to commit.
*
* - Within sys_sync(), kupdate and such.
* We wait on commit, if tol to.
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index dff171c3a123..c862ee5fe79d 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -3313,7 +3313,7 @@ int ext4_discard_partial_page_buffers(handle_t *handle,
* handle: The journal handle
* inode: The files inode
* page: A locked page that contains the offset "from"
- * from: The starting byte offset (from the begining of the file)
+ * from: The starting byte offset (from the beginning of the file)
* to begin discarding
* len: The length of bytes to discard
* flags: Optional flags that may be used:
@@ -3321,11 +3321,11 @@ int ext4_discard_partial_page_buffers(handle_t *handle,
* EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED
* Only zero the regions of the page whose buffer heads
* have already been unmapped. This flag is appropriate
- * for updateing the contents of a page whose blocks may
+ * for updating the contents of a page whose blocks may
* have already been released, and we only want to zero
* out the regions that correspond to those released blocks.
*
- * Returns zero on sucess or negative on failure.
+ * Returns zero on success or negative on failure.
*/
static int ext4_discard_partial_page_buffers_no_lock(handle_t *handle,
struct inode *inode, struct page *page, loff_t from,
@@ -3486,7 +3486,7 @@ int ext4_can_truncate(struct inode *inode)
* @offset: The offset where the hole will begin
* @len: The length of the hole
*
- * Returns: 0 on sucess or negative on failure
+ * Returns: 0 on success or negative on failure
*/
int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
@@ -4008,7 +4008,7 @@ static int ext4_inode_blocks_set(handle_t *handle,
if (i_blocks <= ~0U) {
/*
- * i_blocks can be represnted in a 32 bit variable
+ * i_blocks can be represented in a 32 bit variable
* as multiple of 512 bytes
*/
raw_inode->i_blocks_lo = cpu_to_le32(i_blocks);
@@ -4169,7 +4169,7 @@ out_brelse:
*
* - Within generic_file_write() for O_SYNC files.
* Here, there will be no transaction running. We wait for any running
- * trasnaction to commit.
+ * transaction to commit.
*
* - Within sys_sync(), kupdate and such.
* We wait on commit, if tol to.
@@ -4413,7 +4413,7 @@ static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
* worse case, the indexs blocks spread over different block groups
*
* If datablocks are discontiguous, they are possible to spread over
- * different block groups too. If they are contiuguous, with flexbg,
+ * different block groups too. If they are contiguous, with flexbg,
* they could still across block group boundary.
*
* Also account for superblock, inode, quota and xattr blocks
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 8eae94771c45..08778f6cdfe9 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -4709,7 +4709,7 @@ error_return:
* ext4_group_add_blocks() -- Add given blocks to an existing group
* @handle: handle to this transaction
* @sb: super block
- * @block: start physcial block to add to the block group
+ * @block: start physical block to add to the block group
* @count: number of blocks to free
*
* This marks the blocks as free in the bitmap and buddy.
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c
index be3efc4f64f4..6d46c0d78338 100644
--- a/fs/fs-writeback.c
+++ b/fs/fs-writeback.c
@@ -577,10 +577,6 @@ static long writeback_chunk_size(struct backing_dev_info *bdi,
/*
* Write a portion of b_io inodes which belong to @sb.
*
- * If @only_this_sb is true, then find and write all such
- * inodes. Otherwise write only ones which go sequentially
- * in reverse order.
- *
* Return the number of pages and/or inodes written.
*/
static long writeback_sb_inodes(struct super_block *sb,
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c
index d6526347d386..01c4975da4bc 100644
--- a/fs/gfs2/aops.c
+++ b/fs/gfs2/aops.c
@@ -612,6 +612,7 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping,
struct gfs2_sbd *sdp = GFS2_SB(mapping->host);
struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode);
unsigned int data_blocks = 0, ind_blocks = 0, rblocks;
+ unsigned requested = 0;
int alloc_required;
int error = 0;
pgoff_t index = pos >> PAGE_CACHE_SHIFT;
@@ -641,7 +642,8 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping,
if (error)
goto out_unlock;
- error = gfs2_inplace_reserve(ip, data_blocks + ind_blocks);
+ requested = data_blocks + ind_blocks;
+ error = gfs2_inplace_reserve(ip, requested);
if (error)
goto out_qunlock;
}
@@ -654,7 +656,7 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping,
if (&ip->i_inode == sdp->sd_rindex)
rblocks += 2 * RES_STATFS;
if (alloc_required)
- rblocks += gfs2_rg_blocks(ip);
+ rblocks += gfs2_rg_blocks(ip, requested);
error = gfs2_trans_begin(sdp, rblocks,
PAGE_CACHE_SIZE/sdp->sd_sb.sb_bsize);
@@ -868,8 +870,7 @@ static int gfs2_write_end(struct file *file, struct address_space *mapping,
brelse(dibh);
failed:
gfs2_trans_end(sdp);
- if (gfs2_mb_reserved(ip))
- gfs2_inplace_release(ip);
+ gfs2_inplace_release(ip);
if (ip->i_res->rs_qa_qd_num)
gfs2_quota_unlock(ip);
if (inode == sdp->sd_rindex) {
@@ -1023,7 +1024,7 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb,
offset, nr_segs, gfs2_get_block_direct,
NULL, NULL, 0);
out:
- gfs2_glock_dq_m(1, &gh);
+ gfs2_glock_dq(&gh);
gfs2_holder_uninit(&gh);
return rv;
}
diff --git a/fs/gfs2/bmap.c b/fs/gfs2/bmap.c
index 49cd7dd4a9fa..1fd3ae237bdd 100644
--- a/fs/gfs2/bmap.c
+++ b/fs/gfs2/bmap.c
@@ -786,7 +786,7 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,
goto out_rlist;
if (gfs2_rs_active(ip->i_res)) /* needs to be done with the rgrp glock held */
- gfs2_rs_deltree(ip->i_res);
+ gfs2_rs_deltree(ip, ip->i_res);
error = gfs2_trans_begin(sdp, rg_blocks + RES_DINODE +
RES_INDIRECT + RES_STATFS + RES_QUOTA,
diff --git a/fs/gfs2/file.c b/fs/gfs2/file.c
index 382000ffac1f..30e21997a1a1 100644
--- a/fs/gfs2/file.c
+++ b/fs/gfs2/file.c
@@ -441,7 +441,7 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
rblocks += data_blocks ? data_blocks : 1;
if (ind_blocks || data_blocks) {
rblocks += RES_STATFS + RES_QUOTA;
- rblocks += gfs2_rg_blocks(ip);
+ rblocks += gfs2_rg_blocks(ip, data_blocks + ind_blocks);
}
ret = gfs2_trans_begin(sdp, rblocks, 0);
if (ret)
@@ -845,7 +845,7 @@ retry:
&max_bytes, &data_blocks, &ind_blocks);
rblocks = RES_DINODE + ind_blocks + RES_STATFS + RES_QUOTA +
- RES_RG_HDR + gfs2_rg_blocks(ip);
+ RES_RG_HDR + gfs2_rg_blocks(ip, data_blocks + ind_blocks);
if (gfs2_is_jdata(ip))
rblocks += data_blocks ? data_blocks : 1;
diff --git a/fs/gfs2/glock.c b/fs/gfs2/glock.c
index 1ed81f40da0d..e6c2fd53cab2 100644
--- a/fs/gfs2/glock.c
+++ b/fs/gfs2/glock.c
@@ -186,20 +186,6 @@ static void gfs2_glock_remove_from_lru(struct gfs2_glock *gl)
}
/**
- * __gfs2_glock_schedule_for_reclaim - Add a glock to the reclaim list
- * @gl: the glock
- *
- * If the glock is demotable, then we add it (or move it) to the end
- * of the glock LRU list.
- */
-
-static void __gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl)
-{
- if (demote_ok(gl))
- gfs2_glock_add_to_lru(gl);
-}
-
-/**
* gfs2_glock_put_nolock() - Decrement reference count on glock
* @gl: The glock to put
*
@@ -883,7 +869,14 @@ static int gfs2_glock_demote_wait(void *word)
return 0;
}
-static void wait_on_holder(struct gfs2_holder *gh)
+/**
+ * gfs2_glock_wait - wait on a glock acquisition
+ * @gh: the glock holder
+ *
+ * Returns: 0 on success
+ */
+
+int gfs2_glock_wait(struct gfs2_holder *gh)
{
unsigned long time1 = jiffies;
@@ -894,12 +887,7 @@ static void wait_on_holder(struct gfs2_holder *gh)
gh->gh_gl->gl_hold_time = min(gh->gh_gl->gl_hold_time +
GL_GLOCK_HOLD_INCR,
GL_GLOCK_MAX_HOLD);
-}
-
-static void wait_on_demote(struct gfs2_glock *gl)
-{
- might_sleep();
- wait_on_bit(&gl->gl_flags, GLF_DEMOTE, gfs2_glock_demote_wait, TASK_UNINTERRUPTIBLE);
+ return gh->gh_error;
}
/**
@@ -929,19 +917,6 @@ static void handle_callback(struct gfs2_glock *gl, unsigned int state,
trace_gfs2_demote_rq(gl);
}
-/**
- * gfs2_glock_wait - wait on a glock acquisition
- * @gh: the glock holder
- *
- * Returns: 0 on success
- */
-
-int gfs2_glock_wait(struct gfs2_holder *gh)
-{
- wait_on_holder(gh);
- return gh->gh_error;
-}
-
void gfs2_print_dbg(struct seq_file *seq, const char *fmt, ...)
{
struct va_format vaf;
@@ -979,7 +954,7 @@ __acquires(&gl->gl_spin)
struct gfs2_sbd *sdp = gl->gl_sbd;
struct list_head *insert_pt = NULL;
struct gfs2_holder *gh2;
- int try_lock = 0;
+ int try_futile = 0;
BUG_ON(gh->gh_owner_pid == NULL);
if (test_and_set_bit(HIF_WAIT, &gh->gh_iflags))
@@ -987,7 +962,7 @@ __acquires(&gl->gl_spin)
if (gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) {
if (test_bit(GLF_LOCK, &gl->gl_flags))
- try_lock = 1;
+ try_futile = !may_grant(gl, gh);
if (test_bit(GLF_INVALIDATE_IN_PROGRESS, &gl->gl_flags))
goto fail;
}
@@ -996,9 +971,8 @@ __acquires(&gl->gl_spin)
if (unlikely(gh2->gh_owner_pid == gh->gh_owner_pid &&
(gh->gh_gl->gl_ops->go_type != LM_TYPE_FLOCK)))
goto trap_recursive;
- if (try_lock &&
- !(gh2->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) &&
- !may_grant(gl, gh)) {
+ if (try_futile &&
+ !(gh2->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB))) {
fail:
gh->gh_error = GLR_TRYFAILED;
gfs2_holder_wake(gh);
@@ -1121,8 +1095,9 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
!test_bit(GLF_DEMOTE, &gl->gl_flags))
fast_path = 1;
}
- if (!test_bit(GLF_LFLUSH, &gl->gl_flags))
- __gfs2_glock_schedule_for_reclaim(gl);
+ if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl))
+ gfs2_glock_add_to_lru(gl);
+
trace_gfs2_glock_queue(gh, 0);
spin_unlock(&gl->gl_spin);
if (likely(fast_path))
@@ -1141,7 +1116,8 @@ void gfs2_glock_dq_wait(struct gfs2_holder *gh)
{
struct gfs2_glock *gl = gh->gh_gl;
gfs2_glock_dq(gh);
- wait_on_demote(gl);
+ might_sleep();
+ wait_on_bit(&gl->gl_flags, GLF_DEMOTE, gfs2_glock_demote_wait, TASK_UNINTERRUPTIBLE);
}
/**
diff --git a/fs/gfs2/glops.c b/fs/gfs2/glops.c
index 4bdcf3784187..32cc4fde975c 100644
--- a/fs/gfs2/glops.c
+++ b/fs/gfs2/glops.c
@@ -94,6 +94,7 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
/* A shortened, inline version of gfs2_trans_begin() */
tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes, sizeof(u64));
tr.tr_ip = (unsigned long)__builtin_return_address(0);
+ sb_start_intwrite(sdp->sd_vfs);
gfs2_log_reserve(sdp, tr.tr_reserved);
BUG_ON(current->journal_info);
current->journal_info = &tr;
diff --git a/fs/gfs2/incore.h b/fs/gfs2/incore.h
index aaecc8085fc5..3d469d37345e 100644
--- a/fs/gfs2/incore.h
+++ b/fs/gfs2/incore.h
@@ -99,9 +99,26 @@ struct gfs2_rgrpd {
#define GFS2_RDF_MASK 0xf0000000 /* mask for internal flags */
spinlock_t rd_rsspin; /* protects reservation related vars */
struct rb_root rd_rstree; /* multi-block reservation tree */
- u32 rd_rs_cnt; /* count of current reservations */
};
+struct gfs2_rbm {
+ struct gfs2_rgrpd *rgd;
+ struct gfs2_bitmap *bi; /* Bitmap must belong to the rgd */
+ u32 offset; /* The offset is bitmap relative */
+};
+
+static inline u64 gfs2_rbm_to_block(const struct gfs2_rbm *rbm)
+{
+ return rbm->rgd->rd_data0 + (rbm->bi->bi_start * GFS2_NBBY) + rbm->offset;
+}
+
+static inline bool gfs2_rbm_eq(const struct gfs2_rbm *rbm1,
+ const struct gfs2_rbm *rbm2)
+{
+ return (rbm1->rgd == rbm2->rgd) && (rbm1->bi == rbm2->bi) &&
+ (rbm1->offset == rbm2->offset);
+}
+
enum gfs2_state_bits {
BH_Pinned = BH_PrivateStart,
BH_Escaped = BH_PrivateStart + 1,
@@ -250,18 +267,11 @@ struct gfs2_blkreserv {
/* components used during write (step 1): */
atomic_t rs_sizehint; /* hint of the write size */
- /* components used during inplace_reserve (step 2): */
- u32 rs_requested; /* Filled in by caller of gfs2_inplace_reserve() */
-
- /* components used during get_local_rgrp (step 3): */
- struct gfs2_rgrpd *rs_rgd; /* pointer to the gfs2_rgrpd */
struct gfs2_holder rs_rgd_gh; /* Filled in by get_local_rgrp */
struct rb_node rs_node; /* link to other block reservations */
-
- /* components used during block searches and assignments (step 4): */
- struct gfs2_bitmap *rs_bi; /* bitmap for the current allocation */
- u32 rs_biblk; /* start block relative to the bi */
+ struct gfs2_rbm rs_rbm; /* Start of reservation */
u32 rs_free; /* how many blocks are still free */
+ u64 rs_inum; /* Inode number for reservation */
/* ancillary quota stuff */
struct gfs2_quota_data *rs_qa_qd[2 * MAXQUOTAS];
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 753af3d86bbc..381893ceefa4 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -712,14 +712,9 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
if (error)
goto fail_gunlock2;
- /* The newly created inode needs a reservation so it can allocate
- xattrs. At the same time, we want new blocks allocated to the new
- dinode to be as contiguous as possible. Since we allocated the
- dinode block under the directory's reservation, we transfer
- ownership of that reservation to the new inode. The directory
- doesn't need a reservation unless it needs a new allocation. */
- ip->i_res = dip->i_res;
- dip->i_res = NULL;
+ error = gfs2_rs_alloc(ip);
+ if (error)
+ goto fail_gunlock2;
error = gfs2_acl_create(dip, inode);
if (error)
@@ -737,10 +732,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
brelse(bh);
gfs2_trans_end(sdp);
- /* Check if we reserved space in the rgrp. Function link_dinode may
- not, depending on whether alloc is required. */
- if (gfs2_mb_reserved(dip))
- gfs2_inplace_release(dip);
+ gfs2_inplace_release(dip);
gfs2_quota_unlock(dip);
mark_inode_dirty(inode);
gfs2_glock_dq_uninit_m(2, ghs);
@@ -897,7 +889,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
goto out_gunlock_q;
error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
- gfs2_rg_blocks(dip) +
+ gfs2_rg_blocks(dip, sdp->sd_max_dirres) +
2 * RES_DINODE + RES_STATFS +
RES_QUOTA, 0);
if (error)
@@ -1378,7 +1370,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
goto out_gunlock_q;
error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
- gfs2_rg_blocks(ndip) +
+ gfs2_rg_blocks(ndip, sdp->sd_max_dirres) +
4 * RES_DINODE + 4 * RES_LEAF +
RES_STATFS + RES_QUOTA + 4, 0);
if (error)
diff --git a/fs/gfs2/ops_fstype.c b/fs/gfs2/ops_fstype.c
index e5af9dc420ef..e443966c8106 100644
--- a/fs/gfs2/ops_fstype.c
+++ b/fs/gfs2/ops_fstype.c
@@ -19,6 +19,7 @@
#include <linux/mount.h>
#include <linux/gfs2_ondisk.h>
#include <linux/quotaops.h>
+#include <linux/lockdep.h>
#include "gfs2.h"
#include "incore.h"
@@ -766,6 +767,7 @@ fail:
return error;
}
+static struct lock_class_key gfs2_quota_imutex_key;
static int init_inodes(struct gfs2_sbd *sdp, int undo)
{
@@ -803,6 +805,12 @@ static int init_inodes(struct gfs2_sbd *sdp, int undo)
fs_err(sdp, "can't get quota file inode: %d\n", error);
goto fail_rindex;
}
+ /*
+ * i_mutex on quota files is special. Since this inode is hidden system
+ * file, we are safe to define locking ourselves.
+ */
+ lockdep_set_class(&sdp->sd_quota_inode->i_mutex,
+ &gfs2_quota_imutex_key);
error = gfs2_rindex_update(sdp);
if (error)
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c
index a3bde91645c2..4021deca61ef 100644
--- a/fs/gfs2/quota.c
+++ b/fs/gfs2/quota.c
@@ -765,6 +765,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
struct gfs2_holder *ghs, i_gh;
unsigned int qx, x;
struct gfs2_quota_data *qd;
+ unsigned reserved;
loff_t offset;
unsigned int nalloc = 0, blocks;
int error;
@@ -781,7 +782,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
return -ENOMEM;
sort(qda, num_qd, sizeof(struct gfs2_quota_data *), sort_qd, NULL);
- mutex_lock_nested(&ip->i_inode.i_mutex, I_MUTEX_QUOTA);
+ mutex_lock(&ip->i_inode.i_mutex);
for (qx = 0; qx < num_qd; qx++) {
error = gfs2_glock_nq_init(qda[qx]->qd_gl, LM_ST_EXCLUSIVE,
GL_NOCACHE, &ghs[qx]);
@@ -811,13 +812,13 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
* two blocks need to be updated instead of 1 */
blocks = num_qd * data_blocks + RES_DINODE + num_qd + 3;
- error = gfs2_inplace_reserve(ip, 1 +
- (nalloc * (data_blocks + ind_blocks)));
+ reserved = 1 + (nalloc * (data_blocks + ind_blocks));
+ error = gfs2_inplace_reserve(ip, reserved);
if (error)
goto out_alloc;
if (nalloc)
- blocks += gfs2_rg_blocks(ip) + nalloc * ind_blocks + RES_STATFS;
+ blocks += gfs2_rg_blocks(ip, reserved) + nalloc * ind_blocks + RES_STATFS;
error = gfs2_trans_begin(sdp, blocks, 0);
if (error)
@@ -1598,7 +1599,7 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,
error = gfs2_inplace_reserve(ip, blocks);
if (error)
goto out_i;
- blocks += gfs2_rg_blocks(ip);
+ blocks += gfs2_rg_blocks(ip, blocks);
}
/* Some quotas span block boundaries and can update two blocks,
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c
index c9ed814eeb6f..3cc402ce6fea 100644
--- a/fs/gfs2/rgrp.c
+++ b/fs/gfs2/rgrp.c
@@ -35,9 +35,6 @@
#define BFITNOENT ((u32)~0)
#define NO_BLOCK ((u64)~0)
-#define RSRV_CONTENTION_FACTOR 4
-#define RGRP_RSRV_MAX_CONTENDERS 2
-
#if BITS_PER_LONG == 32
#define LBITMASK (0x55555555UL)
#define LBITSKIP55 (0x55555555UL)
@@ -67,53 +64,48 @@ static const char valid_change[16] = {
1, 0, 0, 0
};
-static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
- unsigned char old_state,
- struct gfs2_bitmap **rbi);
+static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 minext,
+ const struct gfs2_inode *ip, bool nowrap);
+
/**
* gfs2_setbit - Set a bit in the bitmaps
- * @rgd: the resource group descriptor
- * @buf2: the clone buffer that holds the bitmaps
- * @bi: the bitmap structure
- * @block: the block to set
+ * @rbm: The position of the bit to set
+ * @do_clone: Also set the clone bitmap, if it exists
* @new_state: the new state of the block
*
*/
-static inline void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buf2,
- struct gfs2_bitmap *bi, u32 block,
+static inline void gfs2_setbit(const struct gfs2_rbm *rbm, bool do_clone,
unsigned char new_state)
{
unsigned char *byte1, *byte2, *end, cur_state;
- unsigned int buflen = bi->bi_len;
- const unsigned int bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE;
+ unsigned int buflen = rbm->bi->bi_len;
+ const unsigned int bit = (rbm->offset % GFS2_NBBY) * GFS2_BIT_SIZE;
- byte1 = bi->bi_bh->b_data + bi->bi_offset + (block / GFS2_NBBY);
- end = bi->bi_bh->b_data + bi->bi_offset + buflen;
+ byte1 = rbm->bi->bi_bh->b_data + rbm->bi->bi_offset + (rbm->offset / GFS2_NBBY);
+ end = rbm->bi->bi_bh->b_data + rbm->bi->bi_offset + buflen;
BUG_ON(byte1 >= end);
cur_state = (*byte1 >> bit) & GFS2_BIT_MASK;
if (unlikely(!valid_change[new_state * 4 + cur_state])) {
- printk(KERN_WARNING "GFS2: buf_blk = 0x%llx old_state=%d, "
- "new_state=%d\n",
- (unsigned long long)block, cur_state, new_state);
- printk(KERN_WARNING "GFS2: rgrp=0x%llx bi_start=0x%lx\n",
- (unsigned long long)rgd->rd_addr,
- (unsigned long)bi->bi_start);
- printk(KERN_WARNING "GFS2: bi_offset=0x%lx bi_len=0x%lx\n",
- (unsigned long)bi->bi_offset,
- (unsigned long)bi->bi_len);
+ 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,
+ rbm->bi->bi_start);
+ printk(KERN_WARNING "GFS2: bi_offset=0x%x bi_len=0x%x\n",
+ rbm->bi->bi_offset, rbm->bi->bi_len);
dump_stack();
- gfs2_consist_rgrpd(rgd);
+ gfs2_consist_rgrpd(rbm->rgd);
return;
}
*byte1 ^= (cur_state ^ new_state) << bit;
- if (buf2) {
- byte2 = buf2 + bi->bi_offset + (block / GFS2_NBBY);
+ if (do_clone && rbm->bi->bi_clone) {
+ byte2 = rbm->bi->bi_clone + rbm->bi->bi_offset + (rbm->offset / GFS2_NBBY);
cur_state = (*byte2 >> bit) & GFS2_BIT_MASK;
*byte2 ^= (cur_state ^ new_state) << bit;
}
@@ -121,30 +113,21 @@ static inline void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buf2,
/**
* gfs2_testbit - test a bit in the bitmaps
- * @rgd: the resource group descriptor
- * @buffer: the buffer that holds the bitmaps
- * @buflen: the length (in bytes) of the buffer
- * @block: the block to read
+ * @rbm: The bit to test
*
+ * Returns: The two bit block state of the requested bit
*/
-static inline unsigned char gfs2_testbit(struct gfs2_rgrpd *rgd,
- const unsigned char *buffer,
- unsigned int buflen, u32 block)
+static inline u8 gfs2_testbit(const struct gfs2_rbm *rbm)
{
- const unsigned char *byte, *end;
- unsigned char cur_state;
+ const u8 *buffer = rbm->bi->bi_bh->b_data + rbm->bi->bi_offset;
+ const u8 *byte;
unsigned int bit;
- byte = buffer + (block / GFS2_NBBY);
- bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE;
- end = buffer + buflen;
-
- gfs2_assert(rgd->rd_sbd, byte < end);
+ byte = buffer + (rbm->offset / GFS2_NBBY);
+ bit = (rbm->offset % GFS2_NBBY) * GFS2_BIT_SIZE;
- cur_state = (*byte >> bit) & GFS2_BIT_MASK;
-
- return cur_state;
+ return (*byte >> bit) & GFS2_BIT_MASK;
}
/**
@@ -192,7 +175,7 @@ static inline u64 gfs2_bit_search(const __le64 *ptr, u64 mask, u8 state)
*/
static inline int rs_cmp(u64 blk, u32 len, struct gfs2_blkreserv *rs)
{
- u64 startblk = gfs2_rs_startblk(rs);
+ u64 startblk = gfs2_rbm_to_block(&rs->rs_rbm);
if (blk >= startblk + rs->rs_free)
return 1;
@@ -202,36 +185,6 @@ static inline int rs_cmp(u64 blk, u32 len, struct gfs2_blkreserv *rs)
}
/**
- * rs_find - Find a rgrp multi-block reservation that contains a given block
- * @rgd: The rgrp
- * @rgblk: The block we're looking for, relative to the rgrp
- */
-static struct gfs2_blkreserv *rs_find(struct gfs2_rgrpd *rgd, u32 rgblk)
-{
- struct rb_node **newn;
- int rc;
- u64 fsblk = rgblk + rgd->rd_data0;
-
- spin_lock(&rgd->rd_rsspin);
- newn = &rgd->rd_rstree.rb_node;
- while (*newn) {
- struct gfs2_blkreserv *cur =
- rb_entry(*newn, struct gfs2_blkreserv, rs_node);
- rc = rs_cmp(fsblk, 1, cur);
- if (rc < 0)
- newn = &((*newn)->rb_left);
- else if (rc > 0)
- newn = &((*newn)->rb_right);
- else {
- spin_unlock(&rgd->rd_rsspin);
- return cur;
- }
- }
- spin_unlock(&rgd->rd_rsspin);
- return NULL;
-}
-
-/**
* gfs2_bitfit - Search an rgrp's bitmap buffer to find a bit-pair representing
* a block in a given allocation state.
* @buf: the buffer that holds the bitmaps
@@ -262,8 +215,6 @@ static u32 gfs2_bitfit(const u8 *buf, const unsigned int len,
u64 mask = 0x5555555555555555ULL;
u32 bit;
- BUG_ON(state > 3);
-
/* Mask off bits we don't care about at the start of the search */
mask <<= spoint;
tmp = gfs2_bit_search(ptr, mask, state);
@@ -285,6 +236,131 @@ static u32 gfs2_bitfit(const u8 *buf, const unsigned int len,
}
/**
+ * gfs2_rbm_from_block - Set the rbm based upon rgd and block number
+ * @rbm: The rbm with rgd already set correctly
+ * @block: The block number (filesystem relative)
+ *
+ * This sets the bi and offset members of an rbm based on a
+ * resource group and a filesystem relative block number. The
+ * resource group must be set in the rbm on entry, the bi and
+ * offset members will be set by this function.
+ *
+ * Returns: 0 on success, or an error code
+ */
+
+static int gfs2_rbm_from_block(struct gfs2_rbm *rbm, u64 block)
+{
+ u64 rblock = block - rbm->rgd->rd_data0;
+ u32 goal = (u32)rblock;
+ int x;
+
+ if (WARN_ON_ONCE(rblock > UINT_MAX))
+ return -EINVAL;
+ if (block >= rbm->rgd->rd_data0 + rbm->rgd->rd_data)
+ return -E2BIG;
+
+ for (x = 0; x < rbm->rgd->rd_length; x++) {
+ rbm->bi = rbm->rgd->rd_bits + x;
+ if (goal < (rbm->bi->bi_start + rbm->bi->bi_len) * GFS2_NBBY) {
+ rbm->offset = goal - (rbm->bi->bi_start * GFS2_NBBY);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * gfs2_unaligned_extlen - Look for free blocks which are not byte aligned
+ * @rbm: Position to search (value/result)
+ * @n_unaligned: Number of unaligned blocks to check
+ * @len: Decremented for each block found (terminate on zero)
+ *
+ * Returns: true if a non-free block is encountered
+ */
+
+static bool gfs2_unaligned_extlen(struct gfs2_rbm *rbm, u32 n_unaligned, u32 *len)
+{
+ u64 block;
+ u32 n;
+ u8 res;
+
+ for (n = 0; n < n_unaligned; n++) {
+ res = gfs2_testbit(rbm);
+ if (res != GFS2_BLKST_FREE)
+ return true;
+ (*len)--;
+ if (*len == 0)
+ return true;
+ block = gfs2_rbm_to_block(rbm);
+ if (gfs2_rbm_from_block(rbm, block + 1))
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * gfs2_free_extlen - Return extent length of free blocks
+ * @rbm: Starting position
+ * @len: Max length to check
+ *
+ * Starting at the block specified by the rbm, see how many free blocks
+ * there are, not reading more than len blocks ahead. This can be done
+ * using memchr_inv when the blocks are byte aligned, but has to be done
+ * on a block by block basis in case of unaligned blocks. Also this
+ * function can cope with bitmap boundaries (although it must stop on
+ * a resource group boundary)
+ *
+ * Returns: Number of free blocks in the extent
+ */
+
+static u32 gfs2_free_extlen(const struct gfs2_rbm *rrbm, u32 len)
+{
+ struct gfs2_rbm rbm = *rrbm;
+ u32 n_unaligned = rbm.offset & 3;
+ u32 size = len;
+ u32 bytes;
+ u32 chunk_size;
+ u8 *ptr, *start, *end;
+ u64 block;
+
+ if (n_unaligned &&
+ gfs2_unaligned_extlen(&rbm, 4 - n_unaligned, &len))
+ goto out;
+
+ n_unaligned = len & 3;
+ /* Start is now byte aligned */
+ while (len > 3) {
+ start = rbm.bi->bi_bh->b_data;
+ if (rbm.bi->bi_clone)
+ start = rbm.bi->bi_clone;
+ end = start + rbm.bi->bi_bh->b_size;
+ start += rbm.bi->bi_offset;
+ BUG_ON(rbm.offset & 3);
+ start += (rbm.offset / GFS2_NBBY);
+ bytes = min_t(u32, len / GFS2_NBBY, (end - start));
+ ptr = memchr_inv(start, 0, bytes);
+ chunk_size = ((ptr == NULL) ? bytes : (ptr - start));
+ chunk_size *= GFS2_NBBY;
+ BUG_ON(len < chunk_size);
+ len -= chunk_size;
+ block = gfs2_rbm_to_block(&rbm);
+ gfs2_rbm_from_block(&rbm, block + chunk_size);
+ n_unaligned = 3;
+ if (ptr)
+ break;
+ n_unaligned = len & 3;
+ }
+
+ /* Deal with any bits left over at the end */
+ if (n_unaligned)
+ gfs2_unaligned_extlen(&rbm, n_unaligned, &len);
+out:
+ return size - len;
+}
+
+/**
* gfs2_bitcount - count the number of bits in a certain state
* @rgd: the resource group descriptor
* @buffer: the buffer that holds the bitmaps
@@ -487,6 +563,8 @@ int gfs2_rs_alloc(struct gfs2_inode *ip)
if (!res)
error = -ENOMEM;
+ RB_CLEAR_NODE(&res->rs_node);
+
down_write(&ip->i_rw_mutex);
if (ip->i_res)
kmem_cache_free(gfs2_rsrv_cachep, res);
@@ -496,11 +574,12 @@ int gfs2_rs_alloc(struct gfs2_inode *ip)
return error;
}
-static void dump_rs(struct seq_file *seq, struct gfs2_blkreserv *rs)
+static void dump_rs(struct seq_file *seq, const struct gfs2_blkreserv *rs)
{
- gfs2_print_dbg(seq, " r: %llu s:%llu b:%u f:%u\n",
- rs->rs_rgd->rd_addr, gfs2_rs_startblk(rs), rs->rs_biblk,
- rs->rs_free);
+ gfs2_print_dbg(seq, " B: n:%llu s:%llu b:%u f:%u\n",
+ (unsigned long long)rs->rs_inum,
+ (unsigned long long)gfs2_rbm_to_block(&rs->rs_rbm),
+ rs->rs_rbm.offset, rs->rs_free);
}
/**
@@ -508,41 +587,26 @@ static void dump_rs(struct seq_file *seq, struct gfs2_blkreserv *rs)
* @rs: The reservation to remove
*
*/
-static void __rs_deltree(struct gfs2_blkreserv *rs)
+static void __rs_deltree(struct gfs2_inode *ip, struct gfs2_blkreserv *rs)
{
struct gfs2_rgrpd *rgd;
if (!gfs2_rs_active(rs))
return;
- rgd = rs->rs_rgd;
- /* We can't do this: The reason is that when the rgrp is invalidated,
- it's in the "middle" of acquiring the glock, but the HOLDER bit
- isn't set yet:
- BUG_ON(!gfs2_glock_is_locked_by_me(rs->rs_rgd->rd_gl));*/
- trace_gfs2_rs(NULL, rs, TRACE_RS_TREEDEL);
-
- if (!RB_EMPTY_ROOT(&rgd->rd_rstree))
- rb_erase(&rs->rs_node, &rgd->rd_rstree);
- BUG_ON(!rgd->rd_rs_cnt);
- rgd->rd_rs_cnt--;
+ rgd = rs->rs_rbm.rgd;
+ trace_gfs2_rs(rs, TRACE_RS_TREEDEL);
+ rb_erase(&rs->rs_node, &rgd->rd_rstree);
+ RB_CLEAR_NODE(&rs->rs_node);
if (rs->rs_free) {
/* return reserved blocks to the rgrp and the ip */
- BUG_ON(rs->rs_rgd->rd_reserved < rs->rs_free);
- rs->rs_rgd->rd_reserved -= rs->rs_free;
+ BUG_ON(rs->rs_rbm.rgd->rd_reserved < rs->rs_free);
+ rs->rs_rbm.rgd->rd_reserved -= rs->rs_free;
rs->rs_free = 0;
- clear_bit(GBF_FULL, &rs->rs_bi->bi_flags);
+ clear_bit(GBF_FULL, &rs->rs_rbm.bi->bi_flags);
smp_mb__after_clear_bit();
}
- /* We can't change any of the step 1 or step 2 components of the rs.
- E.g. We can't set rs_rgd to NULL because the rgd glock is held and
- dequeued through this pointer.
- Can't: atomic_set(&rs->rs_sizehint, 0);
- Can't: rs->rs_requested = 0;
- Can't: rs->rs_rgd = NULL;*/
- rs->rs_bi = NULL;
- rs->rs_biblk = 0;
}
/**
@@ -550,17 +614,16 @@ static void __rs_deltree(struct gfs2_blkreserv *rs)
* @rs: The reservation to remove
*
*/
-void gfs2_rs_deltree(struct gfs2_blkreserv *rs)
+void gfs2_rs_deltree(struct gfs2_inode *ip, struct gfs2_blkreserv *rs)
{
struct gfs2_rgrpd *rgd;
- if (!gfs2_rs_active(rs))
- return;
-
- rgd = rs->rs_rgd;
- spin_lock(&rgd->rd_rsspin);
- __rs_deltree(rs);
- spin_unlock(&rgd->rd_rsspin);
+ rgd = rs->rs_rbm.rgd;
+ if (rgd) {
+ spin_lock(&rgd->rd_rsspin);
+ __rs_deltree(ip, rs);
+ spin_unlock(&rgd->rd_rsspin);
+ }
}
/**
@@ -572,8 +635,7 @@ void gfs2_rs_delete(struct gfs2_inode *ip)
{
down_write(&ip->i_rw_mutex);
if (ip->i_res) {
- gfs2_rs_deltree(ip->i_res);
- trace_gfs2_rs(ip, ip->i_res, TRACE_RS_DELETE);
+ gfs2_rs_deltree(ip, ip->i_res);
BUG_ON(ip->i_res->rs_free);
kmem_cache_free(gfs2_rsrv_cachep, ip->i_res);
ip->i_res = NULL;
@@ -597,7 +659,7 @@ static void return_all_reservations(struct gfs2_rgrpd *rgd)
spin_lock(&rgd->rd_rsspin);
while ((n = rb_first(&rgd->rd_rstree))) {
rs = rb_entry(n, struct gfs2_blkreserv, rs_node);
- __rs_deltree(rs);
+ __rs_deltree(NULL, rs);
}
spin_unlock(&rgd->rd_rsspin);
}
@@ -1270,211 +1332,276 @@ out:
/**
* rs_insert - insert a new multi-block reservation into the rgrp's rb_tree
- * @bi: the bitmap with the blocks
* @ip: the inode structure
- * @biblk: the 32-bit block number relative to the start of the bitmap
- * @amount: the number of blocks to reserve
*
- * Returns: NULL - reservation was already taken, so not inserted
- * pointer to the inserted reservation
*/
-static struct gfs2_blkreserv *rs_insert(struct gfs2_bitmap *bi,
- struct gfs2_inode *ip, u32 biblk,
- int amount)
+static void rs_insert(struct gfs2_inode *ip)
{
struct rb_node **newn, *parent = NULL;
int rc;
struct gfs2_blkreserv *rs = ip->i_res;
- struct gfs2_rgrpd *rgd = rs->rs_rgd;
- u64 fsblock = gfs2_bi2rgd_blk(bi, biblk) + rgd->rd_data0;
+ struct gfs2_rgrpd *rgd = rs->rs_rbm.rgd;
+ u64 fsblock = gfs2_rbm_to_block(&rs->rs_rbm);
+
+ BUG_ON(gfs2_rs_active(rs));
spin_lock(&rgd->rd_rsspin);
newn = &rgd->rd_rstree.rb_node;
- BUG_ON(!ip->i_res);
- BUG_ON(gfs2_rs_active(rs));
- /* Figure out where to put new node */
- /*BUG_ON(!gfs2_glock_is_locked_by_me(rgd->rd_gl));*/
while (*newn) {
struct gfs2_blkreserv *cur =
rb_entry(*newn, struct gfs2_blkreserv, rs_node);
parent = *newn;
- rc = rs_cmp(fsblock, amount, cur);
+ rc = rs_cmp(fsblock, rs->rs_free, cur);
if (rc > 0)
newn = &((*newn)->rb_right);
else if (rc < 0)
newn = &((*newn)->rb_left);
else {
spin_unlock(&rgd->rd_rsspin);
- return NULL; /* reservation already in use */
+ WARN_ON(1);
+ return;
}
}
- /* Do our reservation work */
- rs = ip->i_res;
- rs->rs_free = amount;
- rs->rs_biblk = biblk;
- rs->rs_bi = bi;
rb_link_node(&rs->rs_node, parent, newn);
rb_insert_color(&rs->rs_node, &rgd->rd_rstree);
- /* Do our inode accounting for the reservation */
- /*BUG_ON(!gfs2_glock_is_locked_by_me(ip->i_gl));*/
-
/* Do our rgrp accounting for the reservation */
- rgd->rd_reserved += amount; /* blocks reserved */
- rgd->rd_rs_cnt++; /* number of in-tree reservations */
+ rgd->rd_reserved += rs->rs_free; /* blocks reserved */
spin_unlock(&rgd->rd_rsspin);
- trace_gfs2_rs(ip, rs, TRACE_RS_INSERT);
- return rs;
-}
-
-/**
- * unclaimed_blocks - return number of blocks that aren't spoken for
- */
-static u32 unclaimed_blocks(struct gfs2_rgrpd *rgd)
-{
- return rgd->rd_free_clone - rgd->rd_reserved;
+ trace_gfs2_rs(rs, TRACE_RS_INSERT);
}
/**
- * rg_mblk_search - find a group of multiple free blocks
+ * rg_mblk_search - find a group of multiple free blocks to form a reservation
* @rgd: the resource group descriptor
- * @rs: the block reservation
* @ip: pointer to the inode for which we're reserving blocks
+ * @requested: number of blocks required for this allocation
*
- * This is very similar to rgblk_search, except we're looking for whole
- * 64-bit words that represent a chunk of 32 free blocks. I'm only focusing
- * on aligned dwords for speed's sake.
- *
- * Returns: 0 if successful or BFITNOENT if there isn't enough free space
*/
-static int rg_mblk_search(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
+static void rg_mblk_search(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip,
+ unsigned requested)
{
- struct gfs2_bitmap *bi = rgd->rd_bits;
- const u32 length = rgd->rd_length;
- u32 blk;
- unsigned int buf, x, search_bytes;
- u8 *buffer = NULL;
- u8 *ptr, *end, *nonzero;
- u32 goal, rsv_bytes;
- struct gfs2_blkreserv *rs;
- u32 best_rs_bytes, unclaimed;
- int best_rs_blocks;
+ struct gfs2_rbm rbm = { .rgd = rgd, };
+ u64 goal;
+ struct gfs2_blkreserv *rs = ip->i_res;
+ u32 extlen;
+ u32 free_blocks = rgd->rd_free_clone - rgd->rd_reserved;
+ int ret;
+
+ extlen = max_t(u32, atomic_read(&rs->rs_sizehint), requested);
+ extlen = clamp(extlen, RGRP_RSRV_MINBLKS, free_blocks);
+ if ((rgd->rd_free_clone < rgd->rd_reserved) || (free_blocks < extlen))
+ return;
/* Find bitmap block that contains bits for goal block */
if (rgrp_contains_block(rgd, ip->i_goal))
- goal = ip->i_goal - rgd->rd_data0;
+ goal = ip->i_goal;
else
- goal = rgd->rd_last_alloc;
- for (buf = 0; buf < length; buf++) {
- bi = rgd->rd_bits + buf;
- /* Convert scope of "goal" from rgrp-wide to within
- found bit block */
- if (goal < (bi->bi_start + bi->bi_len) * GFS2_NBBY) {
- goal -= bi->bi_start * GFS2_NBBY;
- goto do_search;
- }
+ goal = rgd->rd_last_alloc + rgd->rd_data0;
+
+ if (WARN_ON(gfs2_rbm_from_block(&rbm, goal)))
+ return;
+
+ ret = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, extlen, ip, true);
+ if (ret == 0) {
+ rs->rs_rbm = rbm;
+ rs->rs_free = extlen;
+ rs->rs_inum = ip->i_no_addr;
+ rs_insert(ip);
}
- buf = 0;
- goal = 0;
-
-do_search:
- best_rs_blocks = max_t(int, atomic_read(&ip->i_res->rs_sizehint),
- (RGRP_RSRV_MINBLKS * rgd->rd_length));
- best_rs_bytes = (best_rs_blocks *
- (1 + (RSRV_CONTENTION_FACTOR * rgd->rd_rs_cnt))) /
- GFS2_NBBY; /* 1 + is for our not-yet-created reservation */
- best_rs_bytes = ALIGN(best_rs_bytes, sizeof(u64));
- unclaimed = unclaimed_blocks(rgd);
- if (best_rs_bytes * GFS2_NBBY > unclaimed)
- best_rs_bytes = unclaimed >> GFS2_BIT_SIZE;
-
- for (x = 0; x <= length; x++) {
- bi = rgd->rd_bits + buf;
+}
+
+/**
+ * gfs2_next_unreserved_block - Return next block that is not reserved
+ * @rgd: The resource group
+ * @block: The starting block
+ * @length: The required length
+ * @ip: Ignore any reservations for this inode
+ *
+ * If the block does not appear in any reservation, then return the
+ * block number unchanged. If it does appear in the reservation, then
+ * keep looking through the tree of reservations in order to find the
+ * first block number which is not reserved.
+ */
- if (test_bit(GBF_FULL, &bi->bi_flags))
- goto skip;
+static u64 gfs2_next_unreserved_block(struct gfs2_rgrpd *rgd, u64 block,
+ u32 length,
+ const struct gfs2_inode *ip)
+{
+ struct gfs2_blkreserv *rs;
+ struct rb_node *n;
+ int rc;
- WARN_ON(!buffer_uptodate(bi->bi_bh));
- if (bi->bi_clone)
- buffer = bi->bi_clone + bi->bi_offset;
+ spin_lock(&rgd->rd_rsspin);
+ n = rgd->rd_rstree.rb_node;
+ while (n) {
+ rs = rb_entry(n, struct gfs2_blkreserv, rs_node);
+ rc = rs_cmp(block, length, rs);
+ if (rc < 0)
+ n = n->rb_left;
+ else if (rc > 0)
+ n = n->rb_right;
else
- buffer = bi->bi_bh->b_data + bi->bi_offset;
-
- /* We have to keep the reservations aligned on u64 boundaries
- otherwise we could get situations where a byte can't be
- used because it's after a reservation, but a free bit still
- is within the reservation's area. */
- ptr = buffer + ALIGN(goal >> GFS2_BIT_SIZE, sizeof(u64));
- end = (buffer + bi->bi_len);
- while (ptr < end) {
- rsv_bytes = 0;
- if ((ptr + best_rs_bytes) <= end)
- search_bytes = best_rs_bytes;
- else
- search_bytes = end - ptr;
- BUG_ON(!search_bytes);
- nonzero = memchr_inv(ptr, 0, search_bytes);
- /* If the lot is all zeroes, reserve the whole size. If
- there's enough zeroes to satisfy the request, use
- what we can. If there's not enough, keep looking. */
- if (nonzero == NULL)
- rsv_bytes = search_bytes;
- else if ((nonzero - ptr) * GFS2_NBBY >=
- ip->i_res->rs_requested)
- rsv_bytes = (nonzero - ptr);
-
- if (rsv_bytes) {
- blk = ((ptr - buffer) * GFS2_NBBY);
- BUG_ON(blk >= bi->bi_len * GFS2_NBBY);
- rs = rs_insert(bi, ip, blk,
- rsv_bytes * GFS2_NBBY);
- if (IS_ERR(rs))
- return PTR_ERR(rs);
- if (rs)
- return 0;
- }
- ptr += ALIGN(search_bytes, sizeof(u64));
+ break;
+ }
+
+ if (n) {
+ while ((rs_cmp(block, length, rs) == 0) && (ip->i_res != rs)) {
+ block = gfs2_rbm_to_block(&rs->rs_rbm) + rs->rs_free;
+ n = n->rb_right;
+ if (n == NULL)
+ break;
+ rs = rb_entry(n, struct gfs2_blkreserv, rs_node);
}
-skip:
- /* Try next bitmap block (wrap back to rgrp header
- if at end) */
- buf++;
- buf %= length;
- goal = 0;
}
- return BFITNOENT;
+ spin_unlock(&rgd->rd_rsspin);
+ return block;
}
/**
- * try_rgrp_fit - See if a given reservation will fit in a given RG
- * @rgd: the RG data
- * @ip: the inode
+ * gfs2_reservation_check_and_update - Check for reservations during block alloc
+ * @rbm: The current position in the resource group
+ * @ip: The inode for which we are searching for blocks
+ * @minext: The minimum extent length
*
- * If there's room for the requested blocks to be allocated from the RG:
- * This will try to get a multi-block reservation first, and if that doesn't
- * fit, it will take what it can.
+ * This checks the current position in the rgrp to see whether there is
+ * a reservation covering this block. If not then this function is a
+ * no-op. If there is, then the position is moved to the end of the
+ * contiguous reservation(s) so that we are pointing at the first
+ * non-reserved block.
*
- * Returns: 1 on success (it fits), 0 on failure (it doesn't fit)
+ * Returns: 0 if no reservation, 1 if @rbm has changed, otherwise an error
*/
-static int try_rgrp_fit(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
+static int gfs2_reservation_check_and_update(struct gfs2_rbm *rbm,
+ const struct gfs2_inode *ip,
+ u32 minext)
{
- struct gfs2_blkreserv *rs = ip->i_res;
+ u64 block = gfs2_rbm_to_block(rbm);
+ u32 extlen = 1;
+ u64 nblock;
+ int ret;
- if (rgd->rd_flags & (GFS2_RGF_NOALLOC | GFS2_RDF_ERROR))
+ /*
+ * If we have a minimum extent length, then skip over any extent
+ * which is less than the min extent length in size.
+ */
+ if (minext) {
+ extlen = gfs2_free_extlen(rbm, minext);
+ nblock = block + extlen;
+ if (extlen < minext)
+ goto fail;
+ }
+
+ /*
+ * Check the extent which has been found against the reservations
+ * and skip if parts of it are already reserved
+ */
+ nblock = gfs2_next_unreserved_block(rbm->rgd, block, extlen, ip);
+ if (nblock == block)
return 0;
- /* Look for a multi-block reservation. */
- if (unclaimed_blocks(rgd) >= RGRP_RSRV_MINBLKS &&
- rg_mblk_search(rgd, ip) != BFITNOENT)
- return 1;
- if (unclaimed_blocks(rgd) >= rs->rs_requested)
- return 1;
+fail:
+ ret = gfs2_rbm_from_block(rbm, nblock);
+ if (ret < 0)
+ return ret;
+ return 1;
+}
- return 0;
+/**
+ * gfs2_rbm_find - Look for blocks of a particular state
+ * @rbm: Value/result starting position and final position
+ * @state: The state which we want to find
+ * @minext: The requested extent length (0 for a single block)
+ * @ip: If set, check for reservations
+ * @nowrap: Stop looking at the end of the rgrp, rather than wrapping
+ * around until we've reached the starting point.
+ *
+ * Side effects:
+ * - If looking for free blocks, we set GBF_FULL on each bitmap which
+ * has no free blocks in it.
+ *
+ * Returns: 0 on success, -ENOSPC if there is no block of the requested state
+ */
+
+static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 minext,
+ const struct gfs2_inode *ip, bool nowrap)
+{
+ struct buffer_head *bh;
+ struct gfs2_bitmap *initial_bi;
+ u32 initial_offset;
+ u32 offset;
+ u8 *buffer;
+ int index;
+ int n = 0;
+ int iters = rbm->rgd->rd_length;
+ int ret;
+
+ /* If we are not starting at the beginning of a bitmap, then we
+ * need to add one to the bitmap count to ensure that we search
+ * the starting bitmap twice.
+ */
+ if (rbm->offset != 0)
+ iters++;
+
+ while(1) {
+ if (test_bit(GBF_FULL, &rbm->bi->bi_flags) &&
+ (state == GFS2_BLKST_FREE))
+ goto next_bitmap;
+
+ bh = rbm->bi->bi_bh;
+ buffer = bh->b_data + rbm->bi->bi_offset;
+ WARN_ON(!buffer_uptodate(bh));
+ if (state != GFS2_BLKST_UNLINKED && rbm->bi->bi_clone)
+ buffer = rbm->bi->bi_clone + rbm->bi->bi_offset;
+ initial_offset = rbm->offset;
+ offset = gfs2_bitfit(buffer, rbm->bi->bi_len, rbm->offset, state);
+ if (offset == BFITNOENT)
+ goto bitmap_full;
+ rbm->offset = offset;
+ if (ip == NULL)
+ return 0;
+
+ initial_bi = rbm->bi;
+ ret = gfs2_reservation_check_and_update(rbm, ip, minext);
+ if (ret == 0)
+ return 0;
+ if (ret > 0) {
+ n += (rbm->bi - initial_bi);
+ goto next_iter;
+ }
+ if (ret == -E2BIG) {
+ index = 0;
+ rbm->offset = 0;
+ n += (rbm->bi - initial_bi);
+ goto res_covered_end_of_rgrp;
+ }
+ return ret;
+
+bitmap_full: /* Mark bitmap as full and fall through */
+ if ((state == GFS2_BLKST_FREE) && initial_offset == 0)
+ set_bit(GBF_FULL, &rbm->bi->bi_flags);
+
+next_bitmap: /* Find next bitmap in the rgrp */
+ rbm->offset = 0;
+ index = rbm->bi - rbm->rgd->rd_bits;
+ index++;
+ if (index == rbm->rgd->rd_length)
+ index = 0;
+res_covered_end_of_rgrp:
+ rbm->bi = &rbm->rgd->rd_bits[index];
+ if ((index == 0) && nowrap)
+ break;
+ n++;
+next_iter:
+ if (n >= iters)
+ break;
+ }
+
+ return -ENOSPC;
}
/**
@@ -1489,34 +1616,33 @@ static int try_rgrp_fit(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
static void try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, u64 skip)
{
- u32 goal = 0, block;
- u64 no_addr;
+ u64 block;
struct gfs2_sbd *sdp = rgd->rd_sbd;
struct gfs2_glock *gl;
struct gfs2_inode *ip;
int error;
int found = 0;
- struct gfs2_bitmap *bi;
+ struct gfs2_rbm rbm = { .rgd = rgd, .bi = rgd->rd_bits, .offset = 0 };
- while (goal < rgd->rd_data) {
+ while (1) {
down_write(&sdp->sd_log_flush_lock);
- block = rgblk_search(rgd, goal, GFS2_BLKST_UNLINKED, &bi);
+ error = gfs2_rbm_find(&rbm, GFS2_BLKST_UNLINKED, 0, NULL, true);
up_write(&sdp->sd_log_flush_lock);
- if (block == BFITNOENT)
+ if (error == -ENOSPC)
+ break;
+ if (WARN_ON_ONCE(error))
break;
- block = gfs2_bi2rgd_blk(bi, block);
- /* rgblk_search can return a block < goal, so we need to
- keep it marching forward. */
- no_addr = block + rgd->rd_data0;
- goal = max(block + 1, goal + 1);
- if (*last_unlinked != NO_BLOCK && no_addr <= *last_unlinked)
+ block = gfs2_rbm_to_block(&rbm);
+ if (gfs2_rbm_from_block(&rbm, block + 1))
+ break;
+ if (*last_unlinked != NO_BLOCK && block <= *last_unlinked)
continue;
- if (no_addr == skip)
+ if (block == skip)
continue;
- *last_unlinked = no_addr;
+ *last_unlinked = block;
- error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, &gl);
+ error = gfs2_glock_get(sdp, block, &gfs2_inode_glops, CREATE, &gl);
if (error)
continue;
@@ -1543,6 +1669,19 @@ static void try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, u64 skip
return;
}
+static bool gfs2_select_rgrp(struct gfs2_rgrpd **pos, const struct gfs2_rgrpd *begin)
+{
+ struct gfs2_rgrpd *rgd = *pos;
+
+ rgd = gfs2_rgrpd_get_next(rgd);
+ if (rgd == NULL)
+ rgd = gfs2_rgrpd_get_next(NULL);
+ *pos = rgd;
+ if (rgd != begin) /* If we didn't wrap */
+ return true;
+ return false;
+}
+
/**
* gfs2_inplace_reserve - Reserve space in the filesystem
* @ip: the inode to reserve space for
@@ -1562,103 +1701,96 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, u32 requested)
if (sdp->sd_args.ar_rgrplvb)
flags |= GL_SKIP;
- rs->rs_requested = requested;
- if (gfs2_assert_warn(sdp, requested)) {
- error = -EINVAL;
- goto out;
- }
+ if (gfs2_assert_warn(sdp, requested))
+ return -EINVAL;
if (gfs2_rs_active(rs)) {
- begin = rs->rs_rgd;
+ begin = rs->rs_rbm.rgd;
flags = 0; /* Yoda: Do or do not. There is no try */
} else if (ip->i_rgd && rgrp_contains_block(ip->i_rgd, ip->i_goal)) {
- rs->rs_rgd = begin = ip->i_rgd;
+ rs->rs_rbm.rgd = begin = ip->i_rgd;
} else {
- rs->rs_rgd = begin = gfs2_blk2rgrpd(sdp, ip->i_goal, 1);
+ rs->rs_rbm.rgd = begin = gfs2_blk2rgrpd(sdp, ip->i_goal, 1);
}
- if (rs->rs_rgd == NULL)
+ if (rs->rs_rbm.rgd == NULL)
return -EBADSLT;
while (loops < 3) {
- rg_locked = 0;
-
- if (gfs2_glock_is_locked_by_me(rs->rs_rgd->rd_gl)) {
- rg_locked = 1;
- error = 0;
- } else if (!loops && !gfs2_rs_active(rs) &&
- rs->rs_rgd->rd_rs_cnt > RGRP_RSRV_MAX_CONTENDERS) {
- /* If the rgrp already is maxed out for contenders,
- we can eliminate it as a "first pass" without even
- requesting the rgrp glock. */
- error = GLR_TRYFAILED;
- } else {
- error = gfs2_glock_nq_init(rs->rs_rgd->rd_gl,
+ rg_locked = 1;
+
+ if (!gfs2_glock_is_locked_by_me(rs->rs_rbm.rgd->rd_gl)) {
+ rg_locked = 0;
+ error = gfs2_glock_nq_init(rs->rs_rbm.rgd->rd_gl,
LM_ST_EXCLUSIVE, flags,
&rs->rs_rgd_gh);
- if (!error && sdp->sd_args.ar_rgrplvb) {
- error = update_rgrp_lvb(rs->rs_rgd);
- if (error) {
+ if (error == GLR_TRYFAILED)
+ goto next_rgrp;
+ if (unlikely(error))
+ return error;
+ if (sdp->sd_args.ar_rgrplvb) {
+ error = update_rgrp_lvb(rs->rs_rbm.rgd);
+ if (unlikely(error)) {
gfs2_glock_dq_uninit(&rs->rs_rgd_gh);
return error;
}
}
}
- switch (error) {
- case 0:
- if (gfs2_rs_active(rs)) {
- if (unclaimed_blocks(rs->rs_rgd) +
- rs->rs_free >= rs->rs_requested) {
- ip->i_rgd = rs->rs_rgd;
- return 0;
- }
- /* We have a multi-block reservation, but the
- rgrp doesn't have enough free blocks to
- satisfy the request. Free the reservation
- and look for a suitable rgrp. */
- gfs2_rs_deltree(rs);
- }
- if (try_rgrp_fit(rs->rs_rgd, ip)) {
- if (sdp->sd_args.ar_rgrplvb)
- gfs2_rgrp_bh_get(rs->rs_rgd);
- ip->i_rgd = rs->rs_rgd;
- return 0;
- }
- if (rs->rs_rgd->rd_flags & GFS2_RDF_CHECK) {
- if (sdp->sd_args.ar_rgrplvb)
- gfs2_rgrp_bh_get(rs->rs_rgd);
- try_rgrp_unlink(rs->rs_rgd, &last_unlinked,
- ip->i_no_addr);
- }
- if (!rg_locked)
- gfs2_glock_dq_uninit(&rs->rs_rgd_gh);
- /* fall through */
- case GLR_TRYFAILED:
- rs->rs_rgd = gfs2_rgrpd_get_next(rs->rs_rgd);
- rs->rs_rgd = rs->rs_rgd ? : begin; /* if NULL, wrap */
- if (rs->rs_rgd != begin) /* If we didn't wrap */
- break;
- flags &= ~LM_FLAG_TRY;
- loops++;
- /* Check that fs hasn't grown if writing to rindex */
- if (ip == GFS2_I(sdp->sd_rindex) &&
- !sdp->sd_rindex_uptodate) {
- error = gfs2_ri_update(ip);
- if (error)
- goto out;
- } else if (loops == 2)
- /* Flushing the log may release space */
- gfs2_log_flush(sdp, NULL);
- break;
- default:
- goto out;
+ /* Skip unuseable resource groups */
+ if (rs->rs_rbm.rgd->rd_flags & (GFS2_RGF_NOALLOC | GFS2_RDF_ERROR))
+ goto skip_rgrp;
+
+ if (sdp->sd_args.ar_rgrplvb)
+ gfs2_rgrp_bh_get(rs->rs_rbm.rgd);
+
+ /* Get a reservation if we don't already have one */
+ if (!gfs2_rs_active(rs))
+ rg_mblk_search(rs->rs_rbm.rgd, ip, requested);
+
+ /* Skip rgrps when we can't get a reservation on first pass */
+ if (!gfs2_rs_active(rs) && (loops < 1))
+ goto check_rgrp;
+
+ /* If rgrp has enough free space, use it */
+ if (rs->rs_rbm.rgd->rd_free_clone >= requested) {
+ ip->i_rgd = rs->rs_rbm.rgd;
+ return 0;
+ }
+
+ /* Drop reservation, if we couldn't use reserved rgrp */
+ if (gfs2_rs_active(rs))
+ gfs2_rs_deltree(ip, rs);
+check_rgrp:
+ /* Check for unlinked inodes which can be reclaimed */
+ if (rs->rs_rbm.rgd->rd_flags & GFS2_RDF_CHECK)
+ try_rgrp_unlink(rs->rs_rbm.rgd, &last_unlinked,
+ ip->i_no_addr);
+skip_rgrp:
+ /* Unlock rgrp if required */
+ if (!rg_locked)
+ gfs2_glock_dq_uninit(&rs->rs_rgd_gh);
+next_rgrp:
+ /* Find the next rgrp, and continue looking */
+ if (gfs2_select_rgrp(&rs->rs_rbm.rgd, begin))
+ continue;
+
+ /* If we've scanned all the rgrps, but found no free blocks
+ * then this checks for some less likely conditions before
+ * trying again.
+ */
+ flags &= ~LM_FLAG_TRY;
+ loops++;
+ /* Check that fs hasn't grown if writing to rindex */
+ if (ip == GFS2_I(sdp->sd_rindex) && !sdp->sd_rindex_uptodate) {
+ error = gfs2_ri_update(ip);
+ if (error)
+ return error;
}
+ /* Flushing the log may release space */
+ if (loops == 2)
+ gfs2_log_flush(sdp, NULL);
}
- error = -ENOSPC;
-out:
- if (error)
- rs->rs_requested = 0;
- return error;
+ return -ENOSPC;
}
/**
@@ -1672,15 +1804,8 @@ void gfs2_inplace_release(struct gfs2_inode *ip)
{
struct gfs2_blkreserv *rs = ip->i_res;
- if (!rs)
- return;
-
- if (!rs->rs_free)
- gfs2_rs_deltree(rs);
-
if (rs->rs_rgd_gh.gh_gl)
gfs2_glock_dq_uninit(&rs->rs_rgd_gh);
- rs->rs_requested = 0;
}
/**
@@ -1693,173 +1818,47 @@ void gfs2_inplace_release(struct gfs2_inode *ip)
static unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block)
{
- struct gfs2_bitmap *bi = NULL;
- u32 length, rgrp_block, buf_block;
- unsigned int buf;
- unsigned char type;
-
- length = rgd->rd_length;
- rgrp_block = block - rgd->rd_data0;
-
- for (buf = 0; buf < length; buf++) {
- bi = rgd->rd_bits + buf;
- if (rgrp_block < (bi->bi_start + bi->bi_len) * GFS2_NBBY)
- break;
- }
-
- gfs2_assert(rgd->rd_sbd, buf < length);
- buf_block = rgrp_block - bi->bi_start * GFS2_NBBY;
+ struct gfs2_rbm rbm = { .rgd = rgd, };
+ int ret;
- type = gfs2_testbit(rgd, bi->bi_bh->b_data + bi->bi_offset,
- bi->bi_len, buf_block);
+ ret = gfs2_rbm_from_block(&rbm, block);
+ WARN_ON_ONCE(ret != 0);
- return type;
+ return gfs2_testbit(&rbm);
}
-/**
- * rgblk_search - find a block in @state
- * @rgd: the resource group descriptor
- * @goal: the goal block within the RG (start here to search for avail block)
- * @state: GFS2_BLKST_XXX the before-allocation state to find
- * @rbi: address of the pointer to the bitmap containing the block found
- *
- * Walk rgrp's bitmap to find bits that represent a block in @state.
- *
- * This function never fails, because we wouldn't call it unless we
- * know (from reservation results, etc.) that a block is available.
- *
- * Scope of @goal is just within rgrp, not the whole filesystem.
- * Scope of @returned block is just within bitmap, not the whole filesystem.
- *
- * Returns: the block number found relative to the bitmap rbi
- */
-
-static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal, unsigned char state,
- struct gfs2_bitmap **rbi)
-{
- struct gfs2_bitmap *bi = NULL;
- const u32 length = rgd->rd_length;
- u32 biblk = BFITNOENT;
- unsigned int buf, x;
- const u8 *buffer = NULL;
-
- *rbi = NULL;
- /* Find bitmap block that contains bits for goal block */
- for (buf = 0; buf < length; buf++) {
- bi = rgd->rd_bits + buf;
- /* Convert scope of "goal" from rgrp-wide to within found bit block */
- if (goal < (bi->bi_start + bi->bi_len) * GFS2_NBBY) {
- goal -= bi->bi_start * GFS2_NBBY;
- goto do_search;
- }
- }
- buf = 0;
- goal = 0;
-
-do_search:
- /* Search (up to entire) bitmap in this rgrp for allocatable block.
- "x <= length", instead of "x < length", because we typically start
- the search in the middle of a bit block, but if we can't find an
- allocatable block anywhere else, we want to be able wrap around and
- search in the first part of our first-searched bit block. */
- for (x = 0; x <= length; x++) {
- bi = rgd->rd_bits + buf;
-
- if (test_bit(GBF_FULL, &bi->bi_flags) &&
- (state == GFS2_BLKST_FREE))
- goto skip;
-
- /* The GFS2_BLKST_UNLINKED state doesn't apply to the clone
- bitmaps, so we must search the originals for that. */
- buffer = bi->bi_bh->b_data + bi->bi_offset;
- WARN_ON(!buffer_uptodate(bi->bi_bh));
- if (state != GFS2_BLKST_UNLINKED && bi->bi_clone)
- buffer = bi->bi_clone + bi->bi_offset;
-
- while (1) {
- struct gfs2_blkreserv *rs;
- u32 rgblk;
-
- biblk = gfs2_bitfit(buffer, bi->bi_len, goal, state);
- if (biblk == BFITNOENT)
- break;
- /* Check if this block is reserved() */
- rgblk = gfs2_bi2rgd_blk(bi, biblk);
- rs = rs_find(rgd, rgblk);
- if (rs == NULL)
- break;
-
- BUG_ON(rs->rs_bi != bi);
- biblk = BFITNOENT;
- /* This should jump to the first block after the
- reservation. */
- goal = rs->rs_biblk + rs->rs_free;
- if (goal >= bi->bi_len * GFS2_NBBY)
- break;
- }
- if (biblk != BFITNOENT)
- break;
-
- if ((goal == 0) && (state == GFS2_BLKST_FREE))
- set_bit(GBF_FULL, &bi->bi_flags);
-
- /* Try next bitmap block (wrap back to rgrp header if at end) */
-skip:
- buf++;
- buf %= length;
- goal = 0;
- }
-
- if (biblk != BFITNOENT)
- *rbi = bi;
-
- return biblk;
-}
/**
* gfs2_alloc_extent - allocate an extent from a given bitmap
- * @rgd: the resource group descriptor
- * @bi: the bitmap within the rgrp
- * @blk: the block within the bitmap
+ * @rbm: the resource group information
* @dinode: TRUE if the first block we allocate is for a dinode
- * @n: The extent length
+ * @n: The extent length (value/result)
*
- * Add the found bitmap buffer to the transaction.
+ * Add the bitmap buffer to the transaction.
* Set the found bits to @new_state to change block's allocation state.
- * Returns: starting block number of the extent (fs scope)
*/
-static u64 gfs2_alloc_extent(struct gfs2_rgrpd *rgd, struct gfs2_bitmap *bi,
- u32 blk, bool dinode, unsigned int *n)
+static void gfs2_alloc_extent(const struct gfs2_rbm *rbm, bool dinode,
+ unsigned int *n)
{
+ struct gfs2_rbm pos = { .rgd = rbm->rgd, };
const unsigned int elen = *n;
- u32 goal, rgblk;
- const u8 *buffer = NULL;
- struct gfs2_blkreserv *rs;
-
- *n = 0;
- buffer = bi->bi_bh->b_data + bi->bi_offset;
- gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
- gfs2_setbit(rgd, bi->bi_clone, bi, blk,
- dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
- (*n)++;
- goal = blk;
+ u64 block;
+ int ret;
+
+ *n = 1;
+ block = gfs2_rbm_to_block(rbm);
+ gfs2_trans_add_bh(rbm->rgd->rd_gl, rbm->bi->bi_bh, 1);
+ gfs2_setbit(rbm, true, dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
+ block++;
while (*n < elen) {
- goal++;
- if (goal >= (bi->bi_len * GFS2_NBBY))
- break;
- rgblk = gfs2_bi2rgd_blk(bi, goal);
- rs = rs_find(rgd, rgblk);
- if (rs) /* Oops, we bumped into someone's reservation */
- break;
- if (gfs2_testbit(rgd, buffer, bi->bi_len, goal) !=
- GFS2_BLKST_FREE)
+ ret = gfs2_rbm_from_block(&pos, block);
+ if (ret || gfs2_testbit(&pos) != GFS2_BLKST_FREE)
break;
- gfs2_setbit(rgd, bi->bi_clone, bi, goal, GFS2_BLKST_USED);
+ gfs2_trans_add_bh(pos.rgd->rd_gl, pos.bi->bi_bh, 1);
+ gfs2_setbit(&pos, true, GFS2_BLKST_USED);
(*n)++;
+ block++;
}
- blk = gfs2_bi2rgd_blk(bi, blk);
- rgd->rd_last_alloc = blk + *n - 1;
- return rgd->rd_data0 + blk;
}
/**
@@ -1875,46 +1874,30 @@ static u64 gfs2_alloc_extent(struct gfs2_rgrpd *rgd, struct gfs2_bitmap *bi,
static struct gfs2_rgrpd *rgblk_free(struct gfs2_sbd *sdp, u64 bstart,
u32 blen, unsigned char new_state)
{
- struct gfs2_rgrpd *rgd;
- struct gfs2_bitmap *bi = NULL;
- u32 length, rgrp_blk, buf_blk;
- unsigned int buf;
+ struct gfs2_rbm rbm;
- rgd = gfs2_blk2rgrpd(sdp, bstart, 1);
- if (!rgd) {
+ rbm.rgd = gfs2_blk2rgrpd(sdp, bstart, 1);
+ if (!rbm.rgd) {
if (gfs2_consist(sdp))
fs_err(sdp, "block = %llu\n", (unsigned long long)bstart);
return NULL;
}
- length = rgd->rd_length;
-
- rgrp_blk = bstart - rgd->rd_data0;
-
while (blen--) {
- for (buf = 0; buf < length; buf++) {
- bi = rgd->rd_bits + buf;
- if (rgrp_blk < (bi->bi_start + bi->bi_len) * GFS2_NBBY)
- break;
+ gfs2_rbm_from_block(&rbm, bstart);
+ bstart++;
+ if (!rbm.bi->bi_clone) {
+ rbm.bi->bi_clone = kmalloc(rbm.bi->bi_bh->b_size,
+ GFP_NOFS | __GFP_NOFAIL);
+ memcpy(rbm.bi->bi_clone + rbm.bi->bi_offset,
+ rbm.bi->bi_bh->b_data + rbm.bi->bi_offset,
+ rbm.bi->bi_len);
}
-
- gfs2_assert(rgd->rd_sbd, buf < length);
-
- buf_blk = rgrp_blk - bi->bi_start * GFS2_NBBY;
- rgrp_blk++;
-
- if (!bi->bi_clone) {
- bi->bi_clone = kmalloc(bi->bi_bh->b_size,
- GFP_NOFS | __GFP_NOFAIL);
- memcpy(bi->bi_clone + bi->bi_offset,
- bi->bi_bh->b_data + bi->bi_offset,
- bi->bi_len);
- }
- gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
- gfs2_setbit(rgd, NULL, bi, buf_blk, new_state);
+ gfs2_trans_add_bh(rbm.rgd->rd_gl, rbm.bi->bi_bh, 1);
+ gfs2_setbit(&rbm, false, new_state);
}
- return rgd;
+ return rbm.rgd;
}
/**
@@ -1956,46 +1939,41 @@ static void gfs2_rgrp_error(struct gfs2_rgrpd *rgd)
}
/**
- * claim_reserved_blks - Claim previously reserved blocks
- * @ip: the inode that's claiming the reservation
- * @dinode: 1 if this block is a dinode block, otherwise data block
- * @nblocks: desired extent length
+ * gfs2_adjust_reservation - Adjust (or remove) a reservation after allocation
+ * @ip: The inode we have just allocated blocks for
+ * @rbm: The start of the allocated blocks
+ * @len: The extent length
*
- * Lay claim to previously reserved blocks.
- * Returns: Starting block number of the blocks claimed.
- * Sets *nblocks to the actual extent length allocated.
+ * Adjusts a reservation after an allocation has taken place. If the
+ * reservation does not match the allocation, or if it is now empty
+ * then it is removed.
*/
-static u64 claim_reserved_blks(struct gfs2_inode *ip, bool dinode,
- unsigned int *nblocks)
+
+static void gfs2_adjust_reservation(struct gfs2_inode *ip,
+ const struct gfs2_rbm *rbm, unsigned len)
{
struct gfs2_blkreserv *rs = ip->i_res;
- struct gfs2_rgrpd *rgd = rs->rs_rgd;
- struct gfs2_bitmap *bi;
- u64 start_block = gfs2_rs_startblk(rs);
- const unsigned int elen = *nblocks;
+ struct gfs2_rgrpd *rgd = rbm->rgd;
+ unsigned rlen;
+ u64 block;
+ int ret;
- bi = rs->rs_bi;
- gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
-
- for (*nblocks = 0; *nblocks < elen && rs->rs_free; (*nblocks)++) {
- if (gfs2_testbit(rgd, bi->bi_bh->b_data + bi->bi_offset,
- bi->bi_len, rs->rs_biblk) != GFS2_BLKST_FREE)
- break;
- gfs2_setbit(rgd, bi->bi_clone, bi, rs->rs_biblk,
- dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
- rs->rs_biblk++;
- rs->rs_free--;
-
- BUG_ON(!rgd->rd_reserved);
- rgd->rd_reserved--;
- dinode = false;
+ spin_lock(&rgd->rd_rsspin);
+ if (gfs2_rs_active(rs)) {
+ if (gfs2_rbm_eq(&rs->rs_rbm, rbm)) {
+ block = gfs2_rbm_to_block(rbm);
+ ret = gfs2_rbm_from_block(&rs->rs_rbm, block + len);
+ rlen = min(rs->rs_free, len);
+ rs->rs_free -= rlen;
+ rgd->rd_reserved -= rlen;
+ trace_gfs2_rs(rs, TRACE_RS_CLAIM);
+ if (rs->rs_free && !ret)
+ goto out;
+ }
+ __rs_deltree(ip, rs);
}
-
- trace_gfs2_rs(ip, rs, TRACE_RS_CLAIM);
- if (!rs->rs_free || *nblocks != elen)
- gfs2_rs_deltree(rs);
-
- return start_block;
+out:
+ spin_unlock(&rgd->rd_rsspin);
}
/**
@@ -2014,47 +1992,40 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct buffer_head *dibh;
- struct gfs2_rgrpd *rgd;
+ struct gfs2_rbm rbm = { .rgd = ip->i_rgd, };
unsigned int ndata;
- u32 goal, blk; /* block, within the rgrp scope */
+ u64 goal;
u64 block; /* block, within the file system scope */
int error;
- struct gfs2_bitmap *bi;
- /* Only happens if there is a bug in gfs2, return something distinctive
- * to ensure that it is noticed.
- */
- if (ip->i_res->rs_requested == 0)
- return -ECANCELED;
-
- /* If we have a reservation, claim blocks from it. */
- if (gfs2_rs_active(ip->i_res)) {
- BUG_ON(!ip->i_res->rs_free);
- rgd = ip->i_res->rs_rgd;
- block = claim_reserved_blks(ip, dinode, nblocks);
- if (*nblocks)
- goto found_blocks;
- }
-
- rgd = ip->i_rgd;
-
- if (!dinode && rgrp_contains_block(rgd, ip->i_goal))
- goal = ip->i_goal - rgd->rd_data0;
+ if (gfs2_rs_active(ip->i_res))
+ goal = gfs2_rbm_to_block(&ip->i_res->rs_rbm);
+ else if (!dinode && rgrp_contains_block(rbm.rgd, ip->i_goal))
+ goal = ip->i_goal;
else
- goal = rgd->rd_last_alloc;
+ goal = rbm.rgd->rd_last_alloc + rbm.rgd->rd_data0;
- blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, &bi);
+ gfs2_rbm_from_block(&rbm, goal);
+ error = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, 0, ip, false);
+
+ if (error == -ENOSPC) {
+ gfs2_rbm_from_block(&rbm, goal);
+ error = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, 0, NULL, false);
+ }
/* Since all blocks are reserved in advance, this shouldn't happen */
- if (blk == BFITNOENT) {
- printk(KERN_WARNING "BFITNOENT, nblocks=%u\n", *nblocks);
- printk(KERN_WARNING "FULL=%d\n",
- test_bit(GBF_FULL, &rgd->rd_bits->bi_flags));
+ if (error) {
+ fs_warn(sdp, "inum=%llu error=%d, nblocks=%u, full=%d\n",
+ (unsigned long long)ip->i_no_addr, error, *nblocks,
+ test_bit(GBF_FULL, &rbm.rgd->rd_bits->bi_flags));
goto rgrp_error;
}
- block = gfs2_alloc_extent(rgd, bi, blk, dinode, nblocks);
-found_blocks:
+ gfs2_alloc_extent(&rbm, dinode, nblocks);
+ block = gfs2_rbm_to_block(&rbm);
+ rbm.rgd->rd_last_alloc = block - rbm.rgd->rd_data0;
+ if (gfs2_rs_active(ip->i_res))
+ gfs2_adjust_reservation(ip, &rbm, *nblocks);
ndata = *nblocks;
if (dinode)
ndata--;
@@ -2071,22 +2042,22 @@ found_blocks:
brelse(dibh);
}
}
- if (rgd->rd_free < *nblocks) {
+ if (rbm.rgd->rd_free < *nblocks) {
printk(KERN_WARNING "nblocks=%u\n", *nblocks);
goto rgrp_error;
}
- rgd->rd_free -= *nblocks;
+ rbm.rgd->rd_free -= *nblocks;
if (dinode) {
- rgd->rd_dinodes++;
- *generation = rgd->rd_igeneration++;
+ rbm.rgd->rd_dinodes++;
+ *generation = rbm.rgd->rd_igeneration++;
if (*generation == 0)
- *generation = rgd->rd_igeneration++;
+ *generation = rbm.rgd->rd_igeneration++;
}
- gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1);
- gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data);
- gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, rgd->rd_bits[0].bi_bh->b_data);
+ gfs2_trans_add_bh(rbm.rgd->rd_gl, rbm.rgd->rd_bits[0].bi_bh, 1);
+ gfs2_rgrp_out(rbm.rgd, rbm.rgd->rd_bits[0].bi_bh->b_data);
+ gfs2_rgrp_ondisk2lvb(rbm.rgd->rd_rgl, rbm.rgd->rd_bits[0].bi_bh->b_data);
gfs2_statfs_change(sdp, 0, -(s64)*nblocks, dinode ? 1 : 0);
if (dinode)
@@ -2100,14 +2071,14 @@ found_blocks:
gfs2_quota_change(ip, ndata, ip->i_inode.i_uid,
ip->i_inode.i_gid);
- rgd->rd_free_clone -= *nblocks;
- trace_gfs2_block_alloc(ip, rgd, block, *nblocks,
+ rbm.rgd->rd_free_clone -= *nblocks;
+ trace_gfs2_block_alloc(ip, rbm.rgd, block, *nblocks,
dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
*bn = block;
return 0;
rgrp_error:
- gfs2_rgrp_error(rgd);
+ gfs2_rgrp_error(rbm.rgd);
return -EIO;
}
diff --git a/fs/gfs2/rgrp.h b/fs/gfs2/rgrp.h
index ca6e26729b86..24077958dcf6 100644
--- a/fs/gfs2/rgrp.h
+++ b/fs/gfs2/rgrp.h
@@ -46,7 +46,7 @@ extern int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *n,
bool dinode, u64 *generation);
extern int gfs2_rs_alloc(struct gfs2_inode *ip);
-extern void gfs2_rs_deltree(struct gfs2_blkreserv *rs);
+extern void gfs2_rs_deltree(struct gfs2_inode *ip, struct gfs2_blkreserv *rs);
extern void gfs2_rs_delete(struct gfs2_inode *ip);
extern void __gfs2_free_blocks(struct gfs2_inode *ip, u64 bstart, u32 blen, int meta);
extern void gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen);
@@ -73,30 +73,10 @@ extern int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset,
const struct gfs2_bitmap *bi, unsigned minlen, u64 *ptrimmed);
extern int gfs2_fitrim(struct file *filp, void __user *argp);
-/* This is how to tell if a multi-block reservation is "inplace" reserved: */
-static inline int gfs2_mb_reserved(struct gfs2_inode *ip)
+/* This is how to tell if a reservation is in the rgrp tree: */
+static inline bool gfs2_rs_active(struct gfs2_blkreserv *rs)
{
- if (ip->i_res && ip->i_res->rs_requested)
- return 1;
- return 0;
-}
-
-/* This is how to tell if a multi-block reservation is in the rgrp tree: */
-static inline int gfs2_rs_active(struct gfs2_blkreserv *rs)
-{
- if (rs && rs->rs_bi)
- return 1;
- return 0;
-}
-
-static inline u32 gfs2_bi2rgd_blk(const struct gfs2_bitmap *bi, u32 blk)
-{
- return (bi->bi_start * GFS2_NBBY) + blk;
-}
-
-static inline u64 gfs2_rs_startblk(const struct gfs2_blkreserv *rs)
-{
- return gfs2_bi2rgd_blk(rs->rs_bi, rs->rs_biblk) + rs->rs_rgd->rd_data0;
+ return rs && !RB_EMPTY_NODE(&rs->rs_node);
}
#endif /* __RGRP_DOT_H__ */
diff --git a/fs/gfs2/super.c b/fs/gfs2/super.c
index fc3168f47a14..a8d90f2f576c 100644
--- a/fs/gfs2/super.c
+++ b/fs/gfs2/super.c
@@ -1366,6 +1366,8 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root)
val = sdp->sd_tune.gt_statfs_quantum;
if (val != 30)
seq_printf(s, ",statfs_quantum=%d", val);
+ else if (sdp->sd_tune.gt_statfs_slow)
+ seq_puts(s, ",statfs_quantum=0");
val = sdp->sd_tune.gt_quota_quantum;
if (val != 60)
seq_printf(s, ",quota_quantum=%d", val);
@@ -1543,6 +1545,11 @@ static void gfs2_evict_inode(struct inode *inode)
out_truncate:
gfs2_log_flush(sdp, ip->i_gl);
+ if (test_bit(GLF_DIRTY, &ip->i_gl->gl_flags)) {
+ struct address_space *metamapping = gfs2_glock2aspace(ip->i_gl);
+ filemap_fdatawrite(metamapping);
+ filemap_fdatawait(metamapping);
+ }
write_inode_now(inode, 1);
gfs2_ail_flush(ip->i_gl, 0);
@@ -1557,7 +1564,7 @@ out_truncate:
out_unlock:
/* Error path for case 1 */
if (gfs2_rs_active(ip->i_res))
- gfs2_rs_deltree(ip->i_res);
+ gfs2_rs_deltree(ip, ip->i_res);
if (test_bit(HIF_HOLDER, &ip->i_iopen_gh.gh_iflags))
gfs2_glock_dq(&ip->i_iopen_gh);
diff --git a/fs/gfs2/trace_gfs2.h b/fs/gfs2/trace_gfs2.h
index a25c252fe412..bbdc78af60ca 100644
--- a/fs/gfs2/trace_gfs2.h
+++ b/fs/gfs2/trace_gfs2.h
@@ -509,10 +509,9 @@ TRACE_EVENT(gfs2_block_alloc,
/* Keep track of multi-block reservations as they are allocated/freed */
TRACE_EVENT(gfs2_rs,
- TP_PROTO(const struct gfs2_inode *ip, const struct gfs2_blkreserv *rs,
- u8 func),
+ TP_PROTO(const struct gfs2_blkreserv *rs, u8 func),
- TP_ARGS(ip, rs, func),
+ TP_ARGS(rs, func),
TP_STRUCT__entry(
__field( dev_t, dev )
@@ -526,18 +525,17 @@ TRACE_EVENT(gfs2_rs,
),
TP_fast_assign(
- __entry->dev = rs->rs_rgd ? rs->rs_rgd->rd_sbd->sd_vfs->s_dev : 0;
- __entry->rd_addr = rs->rs_rgd ? rs->rs_rgd->rd_addr : 0;
- __entry->rd_free_clone = rs->rs_rgd ? rs->rs_rgd->rd_free_clone : 0;
- __entry->rd_reserved = rs->rs_rgd ? rs->rs_rgd->rd_reserved : 0;
- __entry->inum = ip ? ip->i_no_addr : 0;
- __entry->start = gfs2_rs_startblk(rs);
+ __entry->dev = rs->rs_rbm.rgd->rd_sbd->sd_vfs->s_dev;
+ __entry->rd_addr = rs->rs_rbm.rgd->rd_addr;
+ __entry->rd_free_clone = rs->rs_rbm.rgd->rd_free_clone;
+ __entry->rd_reserved = rs->rs_rbm.rgd->rd_reserved;
+ __entry->inum = rs->rs_inum;
+ __entry->start = gfs2_rbm_to_block(&rs->rs_rbm);
__entry->free = rs->rs_free;
__entry->func = func;
),
- TP_printk("%u,%u bmap %llu resrv %llu rg:%llu rf:%lu rr:%lu %s "
- "f:%lu",
+ TP_printk("%u,%u bmap %llu resrv %llu rg:%llu rf:%lu rr:%lu %s f:%lu",
MAJOR(__entry->dev), MINOR(__entry->dev),
(unsigned long long)__entry->inum,
(unsigned long long)__entry->start,
diff --git a/fs/gfs2/trans.h b/fs/gfs2/trans.h
index 41f42cdccbb8..bf2ae9aeee7a 100644
--- a/fs/gfs2/trans.h
+++ b/fs/gfs2/trans.h
@@ -28,11 +28,10 @@ struct gfs2_glock;
/* reserve either the number of blocks to be allocated plus the rg header
* block, or all of the blocks in the rg, whichever is smaller */
-static inline unsigned int gfs2_rg_blocks(const struct gfs2_inode *ip)
+static inline unsigned int gfs2_rg_blocks(const struct gfs2_inode *ip, unsigned requested)
{
- const struct gfs2_blkreserv *rs = ip->i_res;
- if (rs && rs->rs_requested < ip->i_rgd->rd_length)
- return rs->rs_requested + 1;
+ if (requested < ip->i_rgd->rd_length)
+ return requested + 1;
return ip->i_rgd->rd_length;
}
diff --git a/fs/gfs2/xattr.c b/fs/gfs2/xattr.c
index 27a0b4a901f5..db330e5518cd 100644
--- a/fs/gfs2/xattr.c
+++ b/fs/gfs2/xattr.c
@@ -448,17 +448,18 @@ ssize_t gfs2_listxattr(struct dentry *dentry, char *buffer, size_t size)
}
/**
- * ea_get_unstuffed - actually copies the unstuffed data into the
- * request buffer
+ * ea_iter_unstuffed - copies the unstuffed xattr data to/from the
+ * request buffer
* @ip: The GFS2 inode
* @ea: The extended attribute header structure
- * @data: The data to be copied
+ * @din: The data to be copied in
+ * @dout: The data to be copied out (one of din,dout will be NULL)
*
* Returns: errno
*/
-static int ea_get_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
- char *data)
+static int gfs2_iter_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
+ const char *din, char *dout)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
struct buffer_head **bh;
@@ -467,6 +468,8 @@ static int ea_get_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
__be64 *dataptrs = GFS2_EA2DATAPTRS(ea);
unsigned int x;
int error = 0;
+ unsigned char *pos;
+ unsigned cp_size;
bh = kcalloc(nptrs, sizeof(struct buffer_head *), GFP_NOFS);
if (!bh)
@@ -497,12 +500,21 @@ static int ea_get_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
goto out;
}
- memcpy(data, bh[x]->b_data + sizeof(struct gfs2_meta_header),
- (sdp->sd_jbsize > amount) ? amount : sdp->sd_jbsize);
+ pos = bh[x]->b_data + sizeof(struct gfs2_meta_header);
+ cp_size = (sdp->sd_jbsize > amount) ? amount : sdp->sd_jbsize;
- amount -= sdp->sd_jbsize;
- data += sdp->sd_jbsize;
+ if (dout) {
+ memcpy(dout, pos, cp_size);
+ dout += sdp->sd_jbsize;
+ }
+
+ if (din) {
+ gfs2_trans_add_bh(ip->i_gl, bh[x], 1);
+ memcpy(pos, din, cp_size);
+ din += sdp->sd_jbsize;
+ }
+ amount -= sdp->sd_jbsize;
brelse(bh[x]);
}
@@ -523,7 +535,7 @@ static int gfs2_ea_get_copy(struct gfs2_inode *ip, struct gfs2_ea_location *el,
memcpy(data, GFS2_EA2DATA(el->el_ea), len);
return len;
}
- ret = ea_get_unstuffed(ip, el->el_ea, data);
+ ret = gfs2_iter_unstuffed(ip, el->el_ea, NULL, data);
if (ret < 0)
return ret;
return len;
@@ -727,7 +739,7 @@ static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er,
goto out_gunlock_q;
error = gfs2_trans_begin(GFS2_SB(&ip->i_inode),
- blks + gfs2_rg_blocks(ip) +
+ blks + gfs2_rg_blocks(ip, blks) +
RES_DINODE + RES_STATFS + RES_QUOTA, 0);
if (error)
goto out_ipres;
@@ -1220,69 +1232,23 @@ static int gfs2_xattr_set(struct dentry *dentry, const char *name,
size, flags, type);
}
+
static int ea_acl_chmod_unstuffed(struct gfs2_inode *ip,
struct gfs2_ea_header *ea, char *data)
{
struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
- struct buffer_head **bh;
unsigned int amount = GFS2_EA_DATA_LEN(ea);
unsigned int nptrs = DIV_ROUND_UP(amount, sdp->sd_jbsize);
- __be64 *dataptrs = GFS2_EA2DATAPTRS(ea);
- unsigned int x;
- int error;
-
- bh = kcalloc(nptrs, sizeof(struct buffer_head *), GFP_NOFS);
- if (!bh)
- return -ENOMEM;
-
- error = gfs2_trans_begin(sdp, nptrs + RES_DINODE, 0);
- if (error)
- goto out;
-
- for (x = 0; x < nptrs; x++) {
- error = gfs2_meta_read(ip->i_gl, be64_to_cpu(*dataptrs), 0,
- bh + x);
- if (error) {
- while (x--)
- brelse(bh[x]);
- goto fail;
- }
- dataptrs++;
- }
-
- for (x = 0; x < nptrs; x++) {
- error = gfs2_meta_wait(sdp, bh[x]);
- if (error) {
- for (; x < nptrs; x++)
- brelse(bh[x]);
- goto fail;
- }
- if (gfs2_metatype_check(sdp, bh[x], GFS2_METATYPE_ED)) {
- for (; x < nptrs; x++)
- brelse(bh[x]);
- error = -EIO;
- goto fail;
- }
-
- gfs2_trans_add_bh(ip->i_gl, bh[x], 1);
-
- memcpy(bh[x]->b_data + sizeof(struct gfs2_meta_header), data,
- (sdp->sd_jbsize > amount) ? amount : sdp->sd_jbsize);
-
- amount -= sdp->sd_jbsize;
- data += sdp->sd_jbsize;
-
- brelse(bh[x]);
- }
+ int ret;
-out:
- kfree(bh);
- return error;
+ ret = gfs2_trans_begin(sdp, nptrs + RES_DINODE, 0);
+ if (ret)
+ return ret;
-fail:
+ ret = gfs2_iter_unstuffed(ip, ea, data, NULL);
gfs2_trans_end(sdp);
- kfree(bh);
- return error;
+
+ return ret;
}
int gfs2_xattr_acl_chmod(struct gfs2_inode *ip, struct iattr *attr, char *data)
diff --git a/fs/libfs.c b/fs/libfs.c
index a74cb1725ac6..7cc37ca19cd8 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -874,7 +874,7 @@ struct dentry *generic_fh_to_dentry(struct super_block *sb, struct fid *fid,
EXPORT_SYMBOL_GPL(generic_fh_to_dentry);
/**
- * generic_fh_to_dentry - generic helper for the fh_to_parent export operation
+ * generic_fh_to_parent - generic helper for the fh_to_parent export operation
* @sb: filesystem to do the file handle conversion on
* @fid: file handle to convert
* @fh_len: length of the file handle in bytes
diff --git a/fs/lockd/svclock.c b/fs/lockd/svclock.c
index fb1a2bedbe97..8d80c990dffd 100644
--- a/fs/lockd/svclock.c
+++ b/fs/lockd/svclock.c
@@ -289,7 +289,6 @@ static void nlmsvc_free_block(struct kref *kref)
dprintk("lockd: freeing block %p...\n", block);
/* Remove block from file's list of blocks */
- mutex_lock(&file->f_mutex);
list_del_init(&block->b_flist);
mutex_unlock(&file->f_mutex);
@@ -303,7 +302,7 @@ static void nlmsvc_free_block(struct kref *kref)
static void nlmsvc_release_block(struct nlm_block *block)
{
if (block != NULL)
- kref_put(&block->b_count, nlmsvc_free_block);
+ kref_put_mutex(&block->b_count, nlmsvc_free_block, &block->b_file->f_mutex);
}
/*
diff --git a/fs/namespace.c b/fs/namespace.c
index 4d31f73e2561..7bdf7907413f 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -1886,8 +1886,14 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
return err;
err = -EINVAL;
- if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(real_mount(path->mnt)))
- goto unlock;
+ if (unlikely(!check_mnt(real_mount(path->mnt)))) {
+ /* that's acceptable only for automounts done in private ns */
+ if (!(mnt_flags & MNT_SHRINKABLE))
+ goto unlock;
+ /* ... and for those we'd better have mountpoint still alive */
+ if (!real_mount(path->mnt)->mnt_ns)
+ goto unlock;
+ }
/* Refuse the same filesystem on the same mount point */
err = -EBUSY;
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index b8eda700584b..d2c7f5db0847 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -1537,7 +1537,7 @@ static int nfs_parse_mount_options(char *raw,
/*
* verify that any proto=/mountproto= options match the address
- * familiies in the addr=/mountaddr= options.
+ * families in the addr=/mountaddr= options.
*/
if (protofamily != AF_UNSPEC &&
protofamily != mnt->nfs_server.address.ss_family)
diff --git a/fs/proc/proc_sysctl.c b/fs/proc/proc_sysctl.c
index dfafeb2b05a0..eb7cc91b7258 100644
--- a/fs/proc/proc_sysctl.c
+++ b/fs/proc/proc_sysctl.c
@@ -462,9 +462,6 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
err = ERR_PTR(-ENOMEM);
inode = proc_sys_make_inode(dir->i_sb, h ? h : head, p);
- if (h)
- sysctl_head_finish(h);
-
if (!inode)
goto out;
@@ -473,6 +470,8 @@ static struct dentry *proc_sys_lookup(struct inode *dir, struct dentry *dentry,
d_add(dentry, inode);
out:
+ if (h)
+ sysctl_head_finish(h);
sysctl_head_finish(head);
return err;
}
diff --git a/fs/stat.c b/fs/stat.c
index 40780229a032..208039eec6c7 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -326,7 +326,7 @@ SYSCALL_DEFINE3(readlink, const char __user *, path, char __user *, buf,
/* ---------- LFS-64 ----------- */
-#ifdef __ARCH_WANT_STAT64
+#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64)
#ifndef INIT_STRUCT_STAT64_PADDING
# define INIT_STRUCT_STAT64_PADDING(st) memset(&st, 0, sizeof(st))
@@ -415,7 +415,7 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
return error;
return cp_new_stat64(&stat, statbuf);
}
-#endif /* __ARCH_WANT_STAT64 */
+#endif /* __ARCH_WANT_STAT64 || __ARCH_WANT_COMPAT_STAT64 */
/* Caller is here responsible for sufficient locking (ie. inode->i_lock) */
void __inode_add_bytes(struct inode *inode, loff_t bytes)
diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
index a7ac78f8e67a..3c9eb5624f5e 100644
--- a/fs/sysfs/symlink.c
+++ b/fs/sysfs/symlink.c
@@ -113,7 +113,7 @@ int sysfs_create_link(struct kobject *kobj, struct kobject *target,
* @target: object we're pointing to.
* @name: name of the symlink.
*
- * This function does the same as sysf_create_link(), but it
+ * This function does the same as sysfs_create_link(), but it
* doesn't warn if the link already exists.
*/
int sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target,
diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c
index d7a9dd735e1e..933b7930b863 100644
--- a/fs/xfs/xfs_buf.c
+++ b/fs/xfs/xfs_buf.c
@@ -96,6 +96,7 @@ xfs_buf_lru_add(
atomic_inc(&bp->b_hold);
list_add_tail(&bp->b_lru, &btp->bt_lru);
btp->bt_lru_nr++;
+ bp->b_lru_flags &= ~_XBF_LRU_DISPOSE;
}
spin_unlock(&btp->bt_lru_lock);
}
@@ -154,7 +155,8 @@ xfs_buf_stale(
struct xfs_buftarg *btp = bp->b_target;
spin_lock(&btp->bt_lru_lock);
- if (!list_empty(&bp->b_lru)) {
+ if (!list_empty(&bp->b_lru) &&
+ !(bp->b_lru_flags & _XBF_LRU_DISPOSE)) {
list_del_init(&bp->b_lru);
btp->bt_lru_nr--;
atomic_dec(&bp->b_hold);
@@ -1501,6 +1503,7 @@ xfs_buftarg_shrink(
*/
list_move(&bp->b_lru, &dispose);
btp->bt_lru_nr--;
+ bp->b_lru_flags |= _XBF_LRU_DISPOSE;
}
spin_unlock(&btp->bt_lru_lock);
diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h
index d03b73b9604e..7c0b6a0a1557 100644
--- a/fs/xfs/xfs_buf.h
+++ b/fs/xfs/xfs_buf.h
@@ -38,27 +38,28 @@ typedef enum {
XBRW_ZERO = 3, /* Zero target memory */
} xfs_buf_rw_t;
-#define XBF_READ (1 << 0) /* buffer intended for reading from device */
-#define XBF_WRITE (1 << 1) /* buffer intended for writing to device */
-#define XBF_READ_AHEAD (1 << 2) /* asynchronous read-ahead */
-#define XBF_ASYNC (1 << 4) /* initiator will not wait for completion */
-#define XBF_DONE (1 << 5) /* all pages in the buffer uptodate */
-#define XBF_STALE (1 << 6) /* buffer has been staled, do not find it */
+#define XBF_READ (1 << 0) /* buffer intended for reading from device */
+#define XBF_WRITE (1 << 1) /* buffer intended for writing to device */
+#define XBF_READ_AHEAD (1 << 2) /* asynchronous read-ahead */
+#define XBF_ASYNC (1 << 4) /* initiator will not wait for completion */
+#define XBF_DONE (1 << 5) /* all pages in the buffer uptodate */
+#define XBF_STALE (1 << 6) /* buffer has been staled, do not find it */
/* I/O hints for the BIO layer */
-#define XBF_SYNCIO (1 << 10)/* treat this buffer as synchronous I/O */
-#define XBF_FUA (1 << 11)/* force cache write through mode */
-#define XBF_FLUSH (1 << 12)/* flush the disk cache before a write */
+#define XBF_SYNCIO (1 << 10)/* treat this buffer as synchronous I/O */
+#define XBF_FUA (1 << 11)/* force cache write through mode */
+#define XBF_FLUSH (1 << 12)/* flush the disk cache before a write */
/* flags used only as arguments to access routines */
-#define XBF_TRYLOCK (1 << 16)/* lock requested, but do not wait */
-#define XBF_UNMAPPED (1 << 17)/* do not map the buffer */
+#define XBF_TRYLOCK (1 << 16)/* lock requested, but do not wait */
+#define XBF_UNMAPPED (1 << 17)/* do not map the buffer */
/* flags used only internally */
-#define _XBF_PAGES (1 << 20)/* backed by refcounted pages */
-#define _XBF_KMEM (1 << 21)/* backed by heap memory */
-#define _XBF_DELWRI_Q (1 << 22)/* buffer on a delwri queue */
-#define _XBF_COMPOUND (1 << 23)/* compound buffer */
+#define _XBF_PAGES (1 << 20)/* backed by refcounted pages */
+#define _XBF_KMEM (1 << 21)/* backed by heap memory */
+#define _XBF_DELWRI_Q (1 << 22)/* buffer on a delwri queue */
+#define _XBF_COMPOUND (1 << 23)/* compound buffer */
+#define _XBF_LRU_DISPOSE (1 << 24)/* buffer being discarded */
typedef unsigned int xfs_buf_flags_t;
@@ -72,12 +73,13 @@ typedef unsigned int xfs_buf_flags_t;
{ XBF_SYNCIO, "SYNCIO" }, \
{ XBF_FUA, "FUA" }, \
{ XBF_FLUSH, "FLUSH" }, \
- { XBF_TRYLOCK, "TRYLOCK" }, /* should never be set */\
+ { XBF_TRYLOCK, "TRYLOCK" }, /* should never be set */\
{ XBF_UNMAPPED, "UNMAPPED" }, /* ditto */\
{ _XBF_PAGES, "PAGES" }, \
{ _XBF_KMEM, "KMEM" }, \
{ _XBF_DELWRI_Q, "DELWRI_Q" }, \
- { _XBF_COMPOUND, "COMPOUND" }
+ { _XBF_COMPOUND, "COMPOUND" }, \
+ { _XBF_LRU_DISPOSE, "LRU_DISPOSE" }
typedef struct xfs_buftarg {
dev_t bt_dev;
@@ -124,7 +126,12 @@ typedef struct xfs_buf {
xfs_buf_flags_t b_flags; /* status flags */
struct semaphore b_sema; /* semaphore for lockables */
+ /*
+ * concurrent access to b_lru and b_lru_flags are protected by
+ * bt_lru_lock and not by b_sema
+ */
struct list_head b_lru; /* lru list */
+ xfs_buf_flags_t b_lru_flags; /* internal lru status flags */
wait_queue_head_t b_waiters; /* unpin waiters */
struct list_head b_list;
struct xfs_perag *b_pag; /* contains rbtree root */
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index bdaf4cb9f4a2..19e2380fb867 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -919,6 +919,7 @@ xfs_fs_put_super(
struct xfs_mount *mp = XFS_M(sb);
xfs_filestream_unmount(mp);
+ cancel_delayed_work_sync(&mp->m_sync_work);
xfs_unmountfs(mp);
xfs_syncd_stop(mp);
xfs_freesb(mp);
diff --git a/include/asm-generic/bitops/builtin-__ffs.h b/include/asm-generic/bitops/builtin-__ffs.h
new file mode 100644
index 000000000000..90041e3a41f0
--- /dev/null
+++ b/include/asm-generic/bitops/builtin-__ffs.h
@@ -0,0 +1,15 @@
+#ifndef _ASM_GENERIC_BITOPS_BUILTIN___FFS_H_
+#define _ASM_GENERIC_BITOPS_BUILTIN___FFS_H_
+
+/**
+ * __ffs - find first bit in word.
+ * @word: The word to search
+ *
+ * Undefined if no bit exists, so code should check against 0 first.
+ */
+static __always_inline unsigned long __ffs(unsigned long word)
+{
+ return __builtin_ctzl(word);
+}
+
+#endif
diff --git a/include/asm-generic/bitops/builtin-__fls.h b/include/asm-generic/bitops/builtin-__fls.h
new file mode 100644
index 000000000000..0248f386635f
--- /dev/null
+++ b/include/asm-generic/bitops/builtin-__fls.h
@@ -0,0 +1,15 @@
+#ifndef _ASM_GENERIC_BITOPS_BUILTIN___FLS_H_
+#define _ASM_GENERIC_BITOPS_BUILTIN___FLS_H_
+
+/**
+ * __fls - find last (most-significant) set bit in a long word
+ * @word: the word to search
+ *
+ * Undefined if no set bit exists, so code should check against 0 first.
+ */
+static __always_inline unsigned long __fls(unsigned long word)
+{
+ return (sizeof(word) * 8) - 1 - __builtin_clzl(word);
+}
+
+#endif
diff --git a/include/asm-generic/bitops/builtin-ffs.h b/include/asm-generic/bitops/builtin-ffs.h
new file mode 100644
index 000000000000..064825829e1c
--- /dev/null
+++ b/include/asm-generic/bitops/builtin-ffs.h
@@ -0,0 +1,17 @@
+#ifndef _ASM_GENERIC_BITOPS_BUILTIN_FFS_H_
+#define _ASM_GENERIC_BITOPS_BUILTIN_FFS_H_
+
+/**
+ * ffs - find first bit set
+ * @x: the word to search
+ *
+ * This is defined the same way as
+ * the libc and compiler builtin ffs routines, therefore
+ * differs in spirit from the above ffz (man ffs).
+ */
+static __always_inline int ffs(int x)
+{
+ return __builtin_ffs(x);
+}
+
+#endif
diff --git a/include/asm-generic/bitops/builtin-fls.h b/include/asm-generic/bitops/builtin-fls.h
new file mode 100644
index 000000000000..eda652d0ac7f
--- /dev/null
+++ b/include/asm-generic/bitops/builtin-fls.h
@@ -0,0 +1,16 @@
+#ifndef _ASM_GENERIC_BITOPS_BUILTIN_FLS_H_
+#define _ASM_GENERIC_BITOPS_BUILTIN_FLS_H_
+
+/**
+ * fls - find last (most-significant) bit set
+ * @x: the word to search
+ *
+ * This is defined the same way as ffs.
+ * Note fls(0) = 0, fls(1) = 1, fls(0x80000000) = 32.
+ */
+static __always_inline int fls(int x)
+{
+ return x ? sizeof(x) * 8 - __builtin_clz(x) : 0;
+}
+
+#endif
diff --git a/include/asm-generic/unistd.h b/include/asm-generic/unistd.h
index 991ef01cd77e..3748ec92dcbc 100644
--- a/include/asm-generic/unistd.h
+++ b/include/asm-generic/unistd.h
@@ -691,9 +691,11 @@ __SC_COMP(__NR_process_vm_readv, sys_process_vm_readv, \
#define __NR_process_vm_writev 271
__SC_COMP(__NR_process_vm_writev, sys_process_vm_writev, \
compat_sys_process_vm_writev)
+#define __NR_kcmp 272
+__SYSCALL(__NR_kcmp, sys_kcmp)
#undef __NR_syscalls
-#define __NR_syscalls 272
+#define __NR_syscalls 273
/*
* All syscalls below here should go away really,
diff --git a/include/clocksource/arm_generic.h b/include/clocksource/arm_generic.h
new file mode 100644
index 000000000000..5b41b0d27f0f
--- /dev/null
+++ b/include/clocksource/arm_generic.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef __CLKSOURCE_ARM_GENERIC_H
+#define __CLKSOURCE_ARM_GENERIC_H
+
+extern int arm_generic_timer_init(void);
+
+#endif
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 4f2a76224509..90be98981102 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -138,9 +138,9 @@ void acpi_penalize_isa_irq(int irq, int active);
void acpi_pci_irq_disable (struct pci_dev *dev);
struct acpi_pci_driver {
- struct acpi_pci_driver *next;
- int (*add)(acpi_handle handle);
- void (*remove)(acpi_handle handle);
+ struct list_head node;
+ int (*add)(struct acpi_pci_root *root);
+ void (*remove)(struct acpi_pci_root *root);
};
int acpi_pci_register_driver(struct acpi_pci_driver *driver);
diff --git a/include/linux/atmel_tc.h b/include/linux/atmel_tc.h
index 1d14b1dc1aee..89a931babecf 100644
--- a/include/linux/atmel_tc.h
+++ b/include/linux/atmel_tc.h
@@ -63,7 +63,7 @@ struct atmel_tc {
struct platform_device *pdev;
struct resource *iomem;
void __iomem *regs;
- struct atmel_tcb_config *tcb_config;
+ const struct atmel_tcb_config *tcb_config;
int irq[3];
struct clk *clk[3];
struct list_head node;
diff --git a/include/linux/bcd.h b/include/linux/bcd.h
index 22ea563ba3eb..18fff11fb3ea 100644
--- a/include/linux/bcd.h
+++ b/include/linux/bcd.h
@@ -3,7 +3,20 @@
#include <linux/compiler.h>
-unsigned bcd2bin(unsigned char val) __attribute_const__;
-unsigned char bin2bcd(unsigned val) __attribute_const__;
+#define bcd2bin(x) \
+ (__builtin_constant_p((u8 )(x)) ? \
+ const_bcd2bin(x) : \
+ _bcd2bin(x))
+
+#define bin2bcd(x) \
+ (__builtin_constant_p((u8 )(x)) ? \
+ const_bin2bcd(x) : \
+ _bin2bcd(x))
+
+#define const_bcd2bin(x) (((x) & 0x0f) + ((x) >> 4) * 10)
+#define const_bin2bcd(x) ((((x) / 10) << 4) + (x) % 10)
+
+unsigned _bcd2bin(unsigned char val) __attribute_const__;
+unsigned char _bin2bcd(unsigned val) __attribute_const__;
#endif /* _BCD_H */
diff --git a/include/linux/compiler-gcc4.h b/include/linux/compiler-gcc4.h
index 2f4079175afb..934bc34d5f99 100644
--- a/include/linux/compiler-gcc4.h
+++ b/include/linux/compiler-gcc4.h
@@ -49,6 +49,13 @@
#endif
#endif
+#if __GNUC_MINOR__ >= 6
+/*
+ * Tell the optimizer that something else uses this function or variable.
+ */
+#define __visible __attribute__((externally_visible))
+#endif
+
#if __GNUC_MINOR__ > 0
#define __compiletime_object_size(obj) __builtin_object_size(obj, 0)
#endif
diff --git a/include/linux/compiler.h b/include/linux/compiler.h
index 923d093c9cea..f430e4162f41 100644
--- a/include/linux/compiler.h
+++ b/include/linux/compiler.h
@@ -278,6 +278,10 @@ void ftrace_likely_update(struct ftrace_branch_data *f, int val, int expect);
# define __section(S) __attribute__ ((__section__(#S)))
#endif
+#ifndef __visible
+#define __visible
+#endif
+
/* Are two types/vars the same type (ignoring qualifiers)? */
#ifndef __same_type
# define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index caa34e50537e..59200795482e 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -206,6 +206,8 @@ struct dentry_operations {
#define DCACHE_MANAGED_DENTRY \
(DCACHE_MOUNTED|DCACHE_NEED_AUTOMOUNT|DCACHE_MANAGE_TRANSIT)
+#define DCACHE_DENTRY_KILLED 0x100000
+
extern seqlock_t rename_lock;
static inline int dname_external(struct dentry *dentry)
diff --git a/include/linux/device.h b/include/linux/device.h
index 52a5f15a2223..af92883bb4a6 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -536,6 +536,10 @@ extern void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
#else
extern void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp);
#endif
+extern void devres_for_each_res(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data,
+ void (*fn)(struct device *, void *, void *),
+ void *data);
extern void devres_free(void *res);
extern void devres_add(struct device *dev, void *res);
extern void *devres_find(struct device *dev, dr_release_t release,
@@ -891,12 +895,15 @@ extern const char *dev_driver_string(const struct device *dev);
#ifdef CONFIG_PRINTK
-extern int __dev_printk(const char *level, const struct device *dev,
- struct va_format *vaf);
+extern __printf(3, 0)
+int dev_vprintk_emit(int level, const struct device *dev,
+ const char *fmt, va_list args);
+extern __printf(3, 4)
+int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...);
+
extern __printf(3, 4)
int dev_printk(const char *level, const struct device *dev,
- const char *fmt, ...)
- ;
+ const char *fmt, ...);
extern __printf(2, 3)
int dev_emerg(const struct device *dev, const char *fmt, ...);
extern __printf(2, 3)
@@ -914,6 +921,14 @@ int _dev_info(const struct device *dev, const char *fmt, ...);
#else
+static inline __printf(3, 0)
+int dev_vprintk_emit(int level, const struct device *dev,
+ const char *fmt, va_list args)
+{ return 0; }
+static inline __printf(3, 4)
+int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...)
+{ return 0; }
+
static inline int __dev_printk(const char *level, const struct device *dev,
struct va_format *vaf)
{ return 0; }
@@ -946,6 +961,32 @@ int _dev_info(const struct device *dev, const char *fmt, ...)
#endif
+/*
+ * Stupid hackaround for existing uses of non-printk uses dev_info
+ *
+ * Note that the definition of dev_info below is actually _dev_info
+ * and a macro is used to avoid redefining dev_info
+ */
+
+#define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg)
+
+#if defined(CONFIG_DYNAMIC_DEBUG)
+#define dev_dbg(dev, format, ...) \
+do { \
+ dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \
+} while (0)
+#elif defined(DEBUG)
+#define dev_dbg(dev, format, arg...) \
+ dev_printk(KERN_DEBUG, dev, format, ##arg)
+#else
+#define dev_dbg(dev, format, arg...) \
+({ \
+ if (0) \
+ dev_printk(KERN_DEBUG, dev, format, ##arg); \
+ 0; \
+})
+#endif
+
#define dev_level_ratelimited(dev_level, dev, fmt, ...) \
do { \
static DEFINE_RATELIMIT_STATE(_rs, \
@@ -969,33 +1010,21 @@ do { \
dev_level_ratelimited(dev_notice, dev, fmt, ##__VA_ARGS__)
#define dev_info_ratelimited(dev, fmt, ...) \
dev_level_ratelimited(dev_info, dev, fmt, ##__VA_ARGS__)
+#if defined(CONFIG_DYNAMIC_DEBUG) || defined(DEBUG)
#define dev_dbg_ratelimited(dev, fmt, ...) \
- dev_level_ratelimited(dev_dbg, dev, fmt, ##__VA_ARGS__)
-
-/*
- * Stupid hackaround for existing uses of non-printk uses dev_info
- *
- * Note that the definition of dev_info below is actually _dev_info
- * and a macro is used to avoid redefining dev_info
- */
-
-#define dev_info(dev, fmt, arg...) _dev_info(dev, fmt, ##arg)
-
-#if defined(CONFIG_DYNAMIC_DEBUG)
-#define dev_dbg(dev, format, ...) \
-do { \
- dynamic_dev_dbg(dev, format, ##__VA_ARGS__); \
+do { \
+ static DEFINE_RATELIMIT_STATE(_rs, \
+ DEFAULT_RATELIMIT_INTERVAL, \
+ DEFAULT_RATELIMIT_BURST); \
+ DEFINE_DYNAMIC_DEBUG_METADATA(descriptor, fmt); \
+ if (unlikely(descriptor.flags & _DPRINTK_FLAGS_PRINT) && \
+ __ratelimit(&_rs)) \
+ __dynamic_pr_debug(&descriptor, pr_fmt(fmt), \
+ ##__VA_ARGS__); \
} while (0)
-#elif defined(DEBUG)
-#define dev_dbg(dev, format, arg...) \
- dev_printk(KERN_DEBUG, dev, format, ##arg)
#else
-#define dev_dbg(dev, format, arg...) \
-({ \
- if (0) \
- dev_printk(KERN_DEBUG, dev, format, ##arg); \
- 0; \
-})
+#define dev_dbg_ratelimited(dev, fmt, ...) \
+ no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif
#ifdef VERBOSE_DEBUG
diff --git a/include/linux/efi-bgrt.h b/include/linux/efi-bgrt.h
new file mode 100644
index 000000000000..051b21fedf68
--- /dev/null
+++ b/include/linux/efi-bgrt.h
@@ -0,0 +1,21 @@
+#ifndef _LINUX_EFI_BGRT_H
+#define _LINUX_EFI_BGRT_H
+
+#ifdef CONFIG_ACPI_BGRT
+
+#include <linux/acpi.h>
+
+void efi_bgrt_init(void);
+
+/* The BGRT data itself; only valid if bgrt_image != NULL. */
+extern void *bgrt_image;
+extern size_t bgrt_image_size;
+extern struct acpi_table_bgrt *bgrt_tab;
+
+#else /* !CONFIG_ACPI_BGRT */
+
+static inline void efi_bgrt_init(void) {}
+
+#endif /* !CONFIG_ACPI_BGRT */
+
+#endif /* _LINUX_EFI_BGRT_H */
diff --git a/include/linux/efi.h b/include/linux/efi.h
index ec45ccd8708a..8670eb1eb8cd 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -496,6 +496,14 @@ extern void efi_map_pal_code (void);
extern void efi_memmap_walk (efi_freemem_callback_t callback, void *arg);
extern void efi_gettimeofday (struct timespec *ts);
extern void efi_enter_virtual_mode (void); /* switch EFI to virtual mode, if possible */
+#ifdef CONFIG_X86
+extern void efi_late_init(void);
+extern void efi_free_boot_services(void);
+#else
+static inline void efi_late_init(void) {}
+static inline void efi_free_boot_services(void) {}
+#endif
+extern void __iomem *efi_lookup_mapped_addr(u64 phys_addr);
extern u64 efi_get_iobase (void);
extern u32 efi_mem_type (unsigned long phys_addr);
extern u64 efi_mem_attributes (unsigned long phys_addr);
diff --git a/include/linux/elf.h b/include/linux/elf.h
index 999b4f52e8e5..0a05051a8924 100644
--- a/include/linux/elf.h
+++ b/include/linux/elf.h
@@ -387,7 +387,11 @@ typedef struct elf64_shdr {
#define NT_S390_PREFIX 0x305 /* s390 prefix register */
#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */
#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */
+#define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */
#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */
+#define NT_ARM_TLS 0x401 /* ARM TLS register */
+#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */
+#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */
/* Note header in a PT_NOTE section */
diff --git a/include/linux/extcon.h b/include/linux/extcon.h
index cdd401477656..7443a560c9d0 100644
--- a/include/linux/extcon.h
+++ b/include/linux/extcon.h
@@ -30,19 +30,19 @@
/*
* The standard cable name is to help support general notifier
- * and notifee device drivers to share the common names.
+ * and notifiee device drivers to share the common names.
* Please use standard cable names unless your notifier device has
* a very unique and abnormal cable or
* the cable type is supposed to be used with only one unique
- * pair of notifier/notifee devices.
+ * pair of notifier/notifiee devices.
*
* Please add any other "standard" cables used with extcon dev.
*
* You may add a dot and number to specify version or specification
* of the specific cable if it is required. (e.g., "Fast-charger.18"
* and "Fast-charger.10" for 1.8A and 1.0A chargers)
- * However, the notifee and notifier should be able to handle such
- * string and if the notifee can negotiate the protocol or idenify,
+ * However, the notifiee and notifier should be able to handle such
+ * string and if the notifiee can negotiate the protocol or identify,
* you don't need such convention. This convention is helpful when
* notifier can distinguish but notifiee cannot.
*/
@@ -76,7 +76,7 @@ struct extcon_cable;
* struct extcon_dev - An extcon device represents one external connector.
* @name The name of this extcon device. Parent device name is used
* if NULL.
- * @supported_cable Array of supported cable name ending with NULL.
+ * @supported_cable Array of supported cable names ending with NULL.
* If supported_cable is NULL, cable name related APIs
* are disabled.
* @mutually_exclusive Array of mutually exclusive set of cables that cannot
@@ -95,7 +95,7 @@ struct extcon_cable;
* @state Attach/detach state of this extcon. Do not provide at
* register-time
* @nh Notifier for the state change events from this extcon
- * @entry To support list of extcon devices so that uses can search
+ * @entry To support list of extcon devices so that users can search
* for extcon devices based on the extcon name.
* @lock
* @max_supported Internal value to store the number of cables.
@@ -199,7 +199,7 @@ extern int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state);
/*
* get/set_cable_state access each bit of the 32b encoded state value.
* They are used to access the status of each cable based on the cable_name
- * or cable_index, which is retrived by extcon_find_cable_index
+ * or cable_index, which is retrieved by extcon_find_cable_index
*/
extern int extcon_find_cable_index(struct extcon_dev *sdev,
const char *cable_name);
@@ -226,9 +226,9 @@ extern int extcon_unregister_interest(struct extcon_specific_cable_nb *nb);
/*
* Following APIs are to monitor every action of a notifier.
- * Registerer gets notified for every external port of a connection device.
+ * Registrar gets notified for every external port of a connection device.
* Probably this could be used to debug an action of notifier; however,
- * we do not recommend to use this at normal 'notifiee' device drivers who
+ * we do not recommend to use this for normal 'notifiee' device drivers who
* want to be notified by a specific external port of the notifier.
*/
extern int extcon_register_notifier(struct extcon_dev *edev,
diff --git a/include/linux/extcon/extcon-adc-jack.h b/include/linux/extcon/extcon-adc-jack.h
new file mode 100644
index 000000000000..20e9eef25d4c
--- /dev/null
+++ b/include/linux/extcon/extcon-adc-jack.h
@@ -0,0 +1,71 @@
+/*
+ * include/linux/extcon/extcon-adc-jack.h
+ *
+ * Analog Jack extcon driver with ADC-based detection capability.
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ * MyungJoo Ham <myungjoo.ham@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 _EXTCON_ADC_JACK_H_
+#define _EXTCON_ADC_JACK_H_ __FILE__
+
+#include <linux/module.h>
+#include <linux/extcon.h>
+
+/**
+ * struct adc_jack_cond - condition to use an extcon state
+ * @state - the corresponding extcon state (if 0, this struct denotes
+ * the last adc_jack_cond element among the array)
+ * @min_adc - min adc value for this condition
+ * @max_adc - max adc value for this condition
+ *
+ * For example, if { .state = 0x3, .min_adc = 100, .max_adc = 200}, it means
+ * that if ADC value is between (inclusive) 100 and 200, than the cable 0 and
+ * 1 are attached (1<<0 | 1<<1 == 0x3)
+ *
+ * Note that you don't need to describe condition for "no cable attached"
+ * because when no adc_jack_cond is met, state = 0 is automatically chosen.
+ */
+struct adc_jack_cond {
+ u32 state; /* extcon state value. 0 if invalid */
+ u32 min_adc;
+ u32 max_adc;
+};
+
+/**
+ * struct adc_jack_pdata - platform data for adc jack device.
+ * @name - name of the extcon device. If null, "adc-jack" is used.
+ * @consumer_channel - Unique name to identify the channel on the consumer
+ * side. This typically describes the channels used within
+ * the consumer. E.g. 'battery_voltage'
+ * @cable_names - array of cable names ending with null.
+ * @adc_contitions - array of struct adc_jack_cond conditions ending
+ * with .state = 0 entry. This describes how to decode
+ * adc values into extcon state.
+ * @irq_flags - irq flags used for the @irq
+ * @handling_delay_ms - in some devices, we need to read ADC value some
+ * milli-seconds after the interrupt occurs. You may
+ * describe such delays with @handling_delay_ms, which
+ * is rounded-off by jiffies.
+ */
+struct adc_jack_pdata {
+ const char *name;
+ const char *consumer_channel;
+ /*
+ * The last entry should be NULL
+ */
+ const char **cable_names;
+ /* The last entry's state should be 0 */
+ struct adc_jack_cond *adc_conditions;
+
+ unsigned long irq_flags;
+ unsigned long handling_delay_ms; /* in ms */
+};
+
+#endif /* _EXTCON_ADC_JACK_H */
diff --git a/include/linux/firmware.h b/include/linux/firmware.h
index 1e7c01189fa6..e4279fedb93a 100644
--- a/include/linux/firmware.h
+++ b/include/linux/firmware.h
@@ -12,6 +12,9 @@ struct firmware {
size_t size;
const u8 *data;
struct page **pages;
+
+ /* firmware loader private fields */
+ void *priv;
};
struct module;
@@ -44,6 +47,8 @@ int request_firmware_nowait(
void (*cont)(const struct firmware *fw, void *context));
void release_firmware(const struct firmware *fw);
+int cache_firmware(const char *name);
+int uncache_firmware(const char *name);
#else
static inline int request_firmware(const struct firmware **fw,
const char *name,
@@ -62,6 +67,16 @@ static inline int request_firmware_nowait(
static inline void release_firmware(const struct firmware *fw)
{
}
+
+static inline int cache_firmware(const char *name)
+{
+ return -ENOENT;
+}
+
+static inline int uncache_firmware(const char *name)
+{
+ return -EINVAL;
+}
#endif
#endif
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index 15be561e7397..a82296af413f 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -19,9 +19,11 @@
#define FSL_UTMI_PHY_DLY 10 /*As per P1010RM, delay for UTMI
PHY CLK to become stable - 10ms*/
+#define FSL_USB_PHY_CLK_TIMEOUT 10000 /* uSec */
#define FSL_USB_VER_OLD 0
#define FSL_USB_VER_1_6 1
#define FSL_USB_VER_2_2 2
+#define FSL_USB_VER_2_4 3
#include <linux/types.h>
diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h
index 55e6d63d46d0..a52f2f4fe030 100644
--- a/include/linux/ftrace.h
+++ b/include/linux/ftrace.h
@@ -10,6 +10,7 @@
#include <linux/kallsyms.h>
#include <linux/linkage.h>
#include <linux/bitops.h>
+#include <linux/ptrace.h>
#include <linux/ktime.h>
#include <linux/sched.h>
#include <linux/types.h>
@@ -18,6 +19,28 @@
#include <asm/ftrace.h>
+/*
+ * If the arch supports passing the variable contents of
+ * function_trace_op as the third parameter back from the
+ * mcount call, then the arch should define this as 1.
+ */
+#ifndef ARCH_SUPPORTS_FTRACE_OPS
+#define ARCH_SUPPORTS_FTRACE_OPS 0
+#endif
+
+/*
+ * If the arch's mcount caller does not support all of ftrace's
+ * features, then it must call an indirect function that
+ * does. Or at least does enough to prevent any unwelcomed side effects.
+ */
+#if !defined(CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST) || \
+ !ARCH_SUPPORTS_FTRACE_OPS
+# define FTRACE_FORCE_LIST_FUNC 1
+#else
+# define FTRACE_FORCE_LIST_FUNC 0
+#endif
+
+
struct module;
struct ftrace_hash;
@@ -29,7 +52,10 @@ ftrace_enable_sysctl(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp,
loff_t *ppos);
-typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip);
+struct ftrace_ops;
+
+typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *regs);
/*
* FTRACE_OPS_FL_* bits denote the state of ftrace_ops struct and are
@@ -45,12 +71,33 @@ typedef void (*ftrace_func_t)(unsigned long ip, unsigned long parent_ip);
* could be controled by following calls:
* ftrace_function_local_enable
* ftrace_function_local_disable
+ * SAVE_REGS - The ftrace_ops wants regs saved at each function called
+ * and passed to the callback. If this flag is set, but the
+ * architecture does not support passing regs
+ * (ARCH_SUPPORTS_FTRACE_SAVE_REGS is not defined), then the
+ * ftrace_ops will fail to register, unless the next flag
+ * is set.
+ * SAVE_REGS_IF_SUPPORTED - This is the same as SAVE_REGS, but if the
+ * handler can handle an arch that does not save regs
+ * (the handler tests if regs == NULL), then it can set
+ * this flag instead. It will not fail registering the ftrace_ops
+ * but, the regs field will be NULL if the arch does not support
+ * passing regs to the handler.
+ * Note, if this flag is set, the SAVE_REGS flag will automatically
+ * get set upon registering the ftrace_ops, if the arch supports it.
+ * RECURSION_SAFE - The ftrace_ops can set this to tell the ftrace infrastructure
+ * that the call back has its own recursion protection. If it does
+ * not set this, then the ftrace infrastructure will add recursion
+ * protection for the caller.
*/
enum {
- FTRACE_OPS_FL_ENABLED = 1 << 0,
- FTRACE_OPS_FL_GLOBAL = 1 << 1,
- FTRACE_OPS_FL_DYNAMIC = 1 << 2,
- FTRACE_OPS_FL_CONTROL = 1 << 3,
+ FTRACE_OPS_FL_ENABLED = 1 << 0,
+ FTRACE_OPS_FL_GLOBAL = 1 << 1,
+ FTRACE_OPS_FL_DYNAMIC = 1 << 2,
+ FTRACE_OPS_FL_CONTROL = 1 << 3,
+ FTRACE_OPS_FL_SAVE_REGS = 1 << 4,
+ FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED = 1 << 5,
+ FTRACE_OPS_FL_RECURSION_SAFE = 1 << 6,
};
struct ftrace_ops {
@@ -163,7 +210,8 @@ static inline int ftrace_function_local_disabled(struct ftrace_ops *ops)
return *this_cpu_ptr(ops->disabled);
}
-extern void ftrace_stub(unsigned long a0, unsigned long a1);
+extern void ftrace_stub(unsigned long a0, unsigned long a1,
+ struct ftrace_ops *op, struct pt_regs *regs);
#else /* !CONFIG_FUNCTION_TRACER */
/*
@@ -172,6 +220,10 @@ extern void ftrace_stub(unsigned long a0, unsigned long a1);
*/
#define register_ftrace_function(ops) ({ 0; })
#define unregister_ftrace_function(ops) ({ 0; })
+static inline int ftrace_nr_registered_ops(void)
+{
+ return 0;
+}
static inline void clear_ftrace_function(void) { }
static inline void ftrace_kill(void) { }
static inline void ftrace_stop(void) { }
@@ -227,12 +279,33 @@ extern void unregister_ftrace_function_probe_all(char *glob);
extern int ftrace_text_reserved(void *start, void *end);
+extern int ftrace_nr_registered_ops(void);
+
+/*
+ * The dyn_ftrace record's flags field is split into two parts.
+ * the first part which is '0-FTRACE_REF_MAX' is a counter of
+ * the number of callbacks that have registered the function that
+ * the dyn_ftrace descriptor represents.
+ *
+ * The second part is a mask:
+ * ENABLED - the function is being traced
+ * REGS - the record wants the function to save regs
+ * REGS_EN - the function is set up to save regs.
+ *
+ * When a new ftrace_ops is registered and wants a function to save
+ * pt_regs, the rec->flag REGS is set. When the function has been
+ * set up to save regs, the REG_EN flag is set. Once a function
+ * starts saving regs it will do so until all ftrace_ops are removed
+ * from tracing that function.
+ */
enum {
- FTRACE_FL_ENABLED = (1 << 30),
+ FTRACE_FL_ENABLED = (1UL << 29),
+ FTRACE_FL_REGS = (1UL << 30),
+ FTRACE_FL_REGS_EN = (1UL << 31)
};
-#define FTRACE_FL_MASK (0x3UL << 30)
-#define FTRACE_REF_MAX ((1 << 30) - 1)
+#define FTRACE_FL_MASK (0x7UL << 29)
+#define FTRACE_REF_MAX ((1UL << 29) - 1)
struct dyn_ftrace {
union {
@@ -244,6 +317,8 @@ struct dyn_ftrace {
};
int ftrace_force_update(void);
+int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip,
+ int remove, int reset);
int ftrace_set_filter(struct ftrace_ops *ops, unsigned char *buf,
int len, int reset);
int ftrace_set_notrace(struct ftrace_ops *ops, unsigned char *buf,
@@ -263,9 +338,23 @@ enum {
FTRACE_STOP_FUNC_RET = (1 << 4),
};
+/*
+ * The FTRACE_UPDATE_* enum is used to pass information back
+ * from the ftrace_update_record() and ftrace_test_record()
+ * functions. These are called by the code update routines
+ * to find out what is to be done for a given function.
+ *
+ * IGNORE - The function is already what we want it to be
+ * MAKE_CALL - Start tracing the function
+ * MODIFY_CALL - Stop saving regs for the function
+ * MODIFY_CALL_REGS - Start saving regs for the function
+ * MAKE_NOP - Stop tracing the function
+ */
enum {
FTRACE_UPDATE_IGNORE,
FTRACE_UPDATE_MAKE_CALL,
+ FTRACE_UPDATE_MODIFY_CALL,
+ FTRACE_UPDATE_MODIFY_CALL_REGS,
FTRACE_UPDATE_MAKE_NOP,
};
@@ -317,7 +406,9 @@ extern int ftrace_dyn_arch_init(void *data);
extern void ftrace_replace_code(int enable);
extern int ftrace_update_ftrace_func(ftrace_func_t func);
extern void ftrace_caller(void);
+extern void ftrace_regs_caller(void);
extern void ftrace_call(void);
+extern void ftrace_regs_call(void);
extern void mcount_call(void);
void ftrace_modify_all_code(int command);
@@ -325,6 +416,15 @@ void ftrace_modify_all_code(int command);
#ifndef FTRACE_ADDR
#define FTRACE_ADDR ((unsigned long)ftrace_caller)
#endif
+
+#ifndef FTRACE_REGS_ADDR
+#ifdef ARCH_SUPPORTS_FTRACE_SAVE_REGS
+# define FTRACE_REGS_ADDR ((unsigned long)ftrace_regs_caller)
+#else
+# define FTRACE_REGS_ADDR FTRACE_ADDR
+#endif
+#endif
+
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
extern void ftrace_graph_caller(void);
extern int ftrace_enable_ftrace_graph_caller(void);
@@ -380,6 +480,39 @@ extern int ftrace_make_nop(struct module *mod,
*/
extern int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr);
+#ifdef ARCH_SUPPORTS_FTRACE_SAVE_REGS
+/**
+ * ftrace_modify_call - convert from one addr to another (no nop)
+ * @rec: the mcount call site record
+ * @old_addr: the address expected to be currently called to
+ * @addr: the address to change to
+ *
+ * This is a very sensitive operation and great care needs
+ * to be taken by the arch. The operation should carefully
+ * read the location, check to see if what is read is indeed
+ * what we expect it to be, and then on success of the compare,
+ * it should write to the location.
+ *
+ * The code segment at @rec->ip should be a caller to @old_addr
+ *
+ * Return must be:
+ * 0 on success
+ * -EFAULT on error reading the location
+ * -EINVAL on a failed compare of the contents
+ * -EPERM on error writing to the location
+ * Any other value will be considered a failure.
+ */
+extern int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
+ unsigned long addr);
+#else
+/* Should never be called */
+static inline int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr,
+ unsigned long addr)
+{
+ return -EINVAL;
+}
+#endif
+
/* May be defined in arch */
extern int ftrace_arch_read_dyn_info(char *buf, int size);
@@ -387,7 +520,7 @@ extern int skip_trace(unsigned long ip);
extern void ftrace_disable_daemon(void);
extern void ftrace_enable_daemon(void);
-#else
+#else /* CONFIG_DYNAMIC_FTRACE */
static inline int skip_trace(unsigned long ip) { return 0; }
static inline int ftrace_force_update(void) { return 0; }
static inline void ftrace_disable_daemon(void) { }
@@ -405,6 +538,10 @@ static inline int ftrace_text_reserved(void *start, void *end)
{
return 0;
}
+static inline unsigned long ftrace_location(unsigned long ip)
+{
+ return 0;
+}
/*
* Again users of functions that have ftrace_ops may not
@@ -413,6 +550,7 @@ static inline int ftrace_text_reserved(void *start, void *end)
*/
#define ftrace_regex_open(ops, flag, inod, file) ({ -ENODEV; })
#define ftrace_set_early_filter(ops, buf, enable) do { } while (0)
+#define ftrace_set_filter_ip(ops, ip, remove, reset) ({ -ENODEV; })
#define ftrace_set_filter(ops, buf, len, reset) ({ -ENODEV; })
#define ftrace_set_notrace(ops, buf, len, reset) ({ -ENODEV; })
#define ftrace_free_filter(ops) do { } while (0)
diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h
index 305f23cd7cff..cab3da3d0949 100644
--- a/include/linux/hardirq.h
+++ b/include/linux/hardirq.h
@@ -132,11 +132,11 @@ extern void synchronize_irq(unsigned int irq);
struct task_struct;
#if !defined(CONFIG_VIRT_CPU_ACCOUNTING) && !defined(CONFIG_IRQ_TIME_ACCOUNTING)
-static inline void account_system_vtime(struct task_struct *tsk)
+static inline void vtime_account(struct task_struct *tsk)
{
}
#else
-extern void account_system_vtime(struct task_struct *tsk);
+extern void vtime_account(struct task_struct *tsk);
#endif
#if defined(CONFIG_TINY_RCU) || defined(CONFIG_TINY_PREEMPT_RCU)
@@ -162,7 +162,7 @@ extern void rcu_nmi_exit(void);
*/
#define __irq_enter() \
do { \
- account_system_vtime(current); \
+ vtime_account(current); \
add_preempt_count(HARDIRQ_OFFSET); \
trace_hardirq_enter(); \
} while (0)
@@ -178,7 +178,7 @@ extern void irq_enter(void);
#define __irq_exit() \
do { \
trace_hardirq_exit(); \
- account_system_vtime(current); \
+ vtime_account(current); \
sub_preempt_count(HARDIRQ_OFFSET); \
} while (0)
diff --git a/include/linux/hid-sensor-hub.h b/include/linux/hid-sensor-hub.h
new file mode 100644
index 000000000000..0aa5f4c42ae6
--- /dev/null
+++ b/include/linux/hid-sensor-hub.h
@@ -0,0 +1,160 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ */
+#ifndef _HID_SENSORS_HUB_H
+#define _HID_SENSORS_HUB_H
+
+#include <linux/hid.h>
+#include <linux/hid-sensor-ids.h>
+
+/**
+ * struct hid_sensor_hub_attribute_info - Attribute info
+ * @usage_id: Parent usage id of a physical device.
+ * @attrib_id: Attribute id for this attribute.
+ * @report_id: Report id in which this information resides.
+ * @index: Field index in the report.
+ * @units: Measurment unit for this attribute.
+ * @unit_expo: Exponent used in the data.
+ * @size: Size in bytes for data size.
+ */
+struct hid_sensor_hub_attribute_info {
+ u32 usage_id;
+ u32 attrib_id;
+ s32 report_id;
+ s32 index;
+ s32 units;
+ s32 unit_expo;
+ s32 size;
+};
+
+/**
+ * struct hid_sensor_hub_device - Stores the hub instance data
+ * @hdev: Stores the hid instance.
+ * @vendor_id: Vendor id of hub device.
+ * @product_id: Product id of hub device.
+ */
+struct hid_sensor_hub_device {
+ struct hid_device *hdev;
+ u32 vendor_id;
+ u32 product_id;
+};
+
+/**
+ * struct hid_sensor_hub_callbacks - Client callback functions
+ * @pdev: Platform device instance of the client driver.
+ * @suspend: Suspend callback.
+ * @resume: Resume callback.
+ * @capture_sample: Callback to get a sample.
+ * @send_event: Send notification to indicate all samples are
+ * captured, process and send event
+ */
+struct hid_sensor_hub_callbacks {
+ struct platform_device *pdev;
+ int (*suspend)(struct hid_sensor_hub_device *hsdev, void *priv);
+ int (*resume)(struct hid_sensor_hub_device *hsdev, void *priv);
+ int (*capture_sample)(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id, size_t raw_len, char *raw_data,
+ void *priv);
+ int (*send_event)(struct hid_sensor_hub_device *hsdev, u32 usage_id,
+ void *priv);
+};
+
+/* Registration functions */
+
+/**
+* sensor_hub_register_callback() - Register client callbacks
+* @hsdev: Hub device instance.
+* @usage_id: Usage id of the client (E.g. 0x200076 for Gyro).
+* @usage_callback: Callback function storage
+*
+* Used to register callbacks by client processing drivers. Sensor
+* hub core driver will call these callbacks to offload processing
+* of data streams and notifications.
+*/
+int sensor_hub_register_callback(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ struct hid_sensor_hub_callbacks *usage_callback);
+
+/**
+* sensor_hub_remove_callback() - Remove client callbacks
+* @hsdev: Hub device instance.
+* @usage_id: Usage id of the client (E.g. 0x200076 for Gyro).
+*
+* If there is a callback registred, this call will remove that
+* callbacks, so that it will stop data and event notifications.
+*/
+int sensor_hub_remove_callback(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id);
+
+
+/* Hid sensor hub core interfaces */
+
+/**
+* sensor_hub_input_get_attribute_info() - Get an attribute information
+* @hsdev: Hub device instance.
+* @type: Type of this attribute, input/output/feature
+* @usage_id: Attribute usage id of parent physical device as per spec
+* @attr_usage_id: Attribute usage id as per spec
+* @info: return information about attribute after parsing report
+*
+* Parses report and returns the attribute information such as report id,
+* field index, units and exponet etc.
+*/
+int sensor_hub_input_get_attribute_info(struct hid_sensor_hub_device *hsdev,
+ u8 type,
+ u32 usage_id, u32 attr_usage_id,
+ struct hid_sensor_hub_attribute_info *info);
+
+/**
+* sensor_hub_input_attr_get_raw_value() - Synchronous read request
+* @usage_id: Attribute usage id of parent physical device as per spec
+* @attr_usage_id: Attribute usage id as per spec
+* @report_id: Report id to look for
+*
+* Issues a synchronous read request for an input attribute. Returns
+* data upto 32 bits. Since client can get events, so this call should
+* not be used for data paths, this will impact performance.
+*/
+
+int sensor_hub_input_attr_get_raw_value(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ u32 attr_usage_id, u32 report_id);
+/**
+* sensor_hub_set_feature() - Feature set request
+* @report_id: Report id to look for
+* @field_index: Field index inside a report
+* @value: Value to set
+*
+* Used to set a field in feature report. For example this can set polling
+* interval, sensitivity, activate/deactivate state.
+*/
+int sensor_hub_set_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+ u32 field_index, s32 value);
+
+/**
+* sensor_hub_get_feature() - Feature get request
+* @report_id: Report id to look for
+* @field_index: Field index inside a report
+* @value: Place holder for return value
+*
+* Used to get a field in feature report. For example this can get polling
+* interval, sensitivity, activate/deactivate state.
+*/
+int sensor_hub_get_feature(struct hid_sensor_hub_device *hsdev, u32 report_id,
+ u32 field_index, s32 *value);
+#endif
diff --git a/include/linux/hid-sensor-ids.h b/include/linux/hid-sensor-ids.h
new file mode 100644
index 000000000000..ca8d7e94eb3c
--- /dev/null
+++ b/include/linux/hid-sensor-ids.h
@@ -0,0 +1,112 @@
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2012, 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.
+ *
+ */
+#ifndef _HID_SENSORS_IDS_H
+#define _HID_SENSORS_IDS_H
+
+#define HID_UP_SENSOR 0x00200000
+#define HID_MAX_PHY_DEVICES 0xFF
+
+/* Accel 3D (200073) */
+#define HID_USAGE_SENSOR_ACCEL_3D 0x200073
+#define HID_USAGE_SENSOR_ACCEL_X_AXIS 0x200453
+#define HID_USAGE_SENSOR_ACCEL_Y_AXIS 0x200454
+#define HID_USAGE_SENSOR_ACCEL_Z_AXIS 0x200455
+
+/* ALS (200041) */
+#define HID_USAGE_SENSOR_ALS 0x200041
+#define HID_USAGE_SENSOR_LIGHT_ILLUM 0x2004d1
+
+/* Gyro 3D: (200076) */
+#define HID_USAGE_SENSOR_GYRO_3D 0x200076
+#define HID_USAGE_SENSOR_ANGL_VELOCITY_X_AXIS 0x200457
+#define HID_USAGE_SENSOR_ANGL_VELOCITY_Y_AXIS 0x200458
+#define HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS 0x200459
+
+/*ORIENTATION: Compass 3D: (200083) */
+#define HID_USAGE_SENSOR_COMPASS_3D 0x200083
+#define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING 0x200471
+#define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING_X 0x200472
+#define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING_Y 0x200473
+#define HID_USAGE_SENSOR_ORIENT_MAGN_HEADING_Z 0x200474
+
+#define HID_USAGE_SENSOR_ORIENT_COMP_MAGN_NORTH 0x200475
+#define HID_USAGE_SENSOR_ORIENT_COMP_TRUE_NORTH 0x200476
+#define HID_USAGE_SENSOR_ORIENT_MAGN_NORTH 0x200477
+#define HID_USAGE_SENSOR_ORIENT_TRUE_NORTH 0x200478
+
+#define HID_USAGE_SENSOR_ORIENT_DISTANCE 0x200479
+#define HID_USAGE_SENSOR_ORIENT_DISTANCE_X 0x20047A
+#define HID_USAGE_SENSOR_ORIENT_DISTANCE_Y 0x20047B
+#define HID_USAGE_SENSOR_ORIENT_DISTANCE_Z 0x20047C
+#define HID_USAGE_SENSOR_ORIENT_DISTANCE_OUT_OF_RANGE 0x20047D
+#define HID_USAGE_SENSOR_ORIENT_TILT 0x20047E
+#define HID_USAGE_SENSOR_ORIENT_TILT_X 0x20047F
+#define HID_USAGE_SENSOR_ORIENT_TILT_Y 0x200480
+#define HID_USAGE_SENSOR_ORIENT_TILT_Z 0x200481
+#define HID_USAGE_SENSOR_ORIENT_ROTATION_MATRIX 0x200482
+#define HID_USAGE_SENSOR_ORIENT_QUATERNION 0x200483
+#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX 0x200484
+
+#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_X_AXIS 0x200485
+#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Y_AXIS 0x200486
+#define HID_USAGE_SENSOR_ORIENT_MAGN_FLUX_Z_AXIS 0x200487
+
+/* Units */
+#define HID_USAGE_SENSOR_UNITS_NOT_SPECIFIED 0x00
+#define HID_USAGE_SENSOR_UNITS_LUX 0x01
+#define HID_USAGE_SENSOR_UNITS_KELVIN 0x01000100
+#define HID_USAGE_SENSOR_UNITS_FAHRENHEIT 0x03000100
+#define HID_USAGE_SENSOR_UNITS_PASCAL 0xF1E1
+#define HID_USAGE_SENSOR_UNITS_NEWTON 0x11E1
+#define HID_USAGE_SENSOR_UNITS_METERS_PER_SECOND 0x11F0
+#define HID_USAGE_SENSOR_UNITS_METERS_PER_SEC_SQRD 0x11E0
+#define HID_USAGE_SENSOR_UNITS_FARAD 0xE14F2000
+#define HID_USAGE_SENSOR_UNITS_AMPERE 0x01001000
+#define HID_USAGE_SENSOR_UNITS_WATT 0x21d1
+#define HID_USAGE_SENSOR_UNITS_HENRY 0x21E1E000
+#define HID_USAGE_SENSOR_UNITS_OHM 0x21D1E000
+#define HID_USAGE_SENSOR_UNITS_VOLT 0x21D1F000
+#define HID_USAGE_SENSOR_UNITS_HERTZ 0x01F0
+#define HID_USAGE_SENSOR_UNITS_DEGREES_PER_SEC_SQRD 0x14E0
+#define HID_USAGE_SENSOR_UNITS_RADIANS 0x12
+#define HID_USAGE_SENSOR_UNITS_RADIANS_PER_SECOND 0x12F0
+#define HID_USAGE_SENSOR_UNITS_RADIANS_PER_SEC_SQRD 0x12E0
+#define HID_USAGE_SENSOR_UNITS_SECOND 0x0110
+#define HID_USAGE_SENSOR_UNITS_GAUSS 0x01E1F000
+#define HID_USAGE_SENSOR_UNITS_GRAM 0x0101
+#define HID_USAGE_SENSOR_UNITS_CENTIMETER 0x11
+#define HID_USAGE_SENSOR_UNITS_G 0x1A
+#define HID_USAGE_SENSOR_UNITS_MILLISECOND 0x19
+#define HID_USAGE_SENSOR_UNITS_PERCENT 0x17
+#define HID_USAGE_SENSOR_UNITS_DEGREES 0x14
+#define HID_USAGE_SENSOR_UNITS_DEGREES_PER_SECOND 0x15
+
+/* Common selectors */
+#define HID_USAGE_SENSOR_PROP_REPORT_INTERVAL 0x20030E
+#define HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS 0x20030F
+#define HID_USAGE_SENSOR_PROP_SENSITIVITY_RANGE_PCT 0x200310
+#define HID_USAGE_SENSOR_PROP_SENSITIVITY_REL_PCT 0x200311
+#define HID_USAGE_SENSOR_PROP_ACCURACY 0x200312
+#define HID_USAGE_SENSOR_PROP_RESOLUTION 0x200313
+#define HID_USAGE_SENSOR_PROP_RANGE_MAXIMUM 0x200314
+#define HID_USAGE_SENSOR_PROP_RANGE_MINIMUM 0x200315
+#define HID_USAGE_SENSOR_PROP_REPORT_STATE 0x200316
+#define HID_USAGE_SENSOR_PROY_POWER_STATE 0x200319
+
+#endif
diff --git a/include/linux/hid.h b/include/linux/hid.h
index 42970de1b40c..7e1f37db7582 100644
--- a/include/linux/hid.h
+++ b/include/linux/hid.h
@@ -414,7 +414,7 @@ struct hid_field {
__u16 dpad; /* dpad input code */
};
-#define HID_MAX_FIELDS 128
+#define HID_MAX_FIELDS 256
struct hid_report {
struct list_head list;
@@ -626,6 +626,7 @@ struct hid_usage_id {
* @report_fixup: called before report descriptor parsing (NULL means nop)
* @input_mapping: invoked on input registering before mapping an usage
* @input_mapped: invoked on input registering after mapping an usage
+ * @input_configured: invoked just before the device is registered
* @feature_mapping: invoked on feature registering
* @suspend: invoked on suspend (NULL means nop)
* @resume: invoked on resume if device was not reset (NULL means nop)
@@ -670,6 +671,8 @@ struct hid_driver {
int (*input_mapped)(struct hid_device *hdev,
struct hid_input *hidinput, struct hid_field *field,
struct hid_usage *usage, unsigned long **bit, int *max);
+ void (*input_configured)(struct hid_device *hdev,
+ struct hid_input *hidinput);
void (*feature_mapping)(struct hid_device *hdev,
struct hid_field *field,
struct hid_usage *usage);
diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h
index 68ed7f7e1fc9..e73b852156b1 100644
--- a/include/linux/hyperv.h
+++ b/include/linux/hyperv.h
@@ -122,12 +122,53 @@
#define REG_U32 4
#define REG_U64 8
+/*
+ * As we look at expanding the KVP functionality to include
+ * IP injection functionality, we need to maintain binary
+ * compatibility with older daemons.
+ *
+ * The KVP opcodes are defined by the host and it was unfortunate
+ * that I chose to treat the registration operation as part of the
+ * KVP operations defined by the host.
+ * Here is the level of compatibility
+ * (between the user level daemon and the kernel KVP driver) that we
+ * will implement:
+ *
+ * An older daemon will always be supported on a newer driver.
+ * A given user level daemon will require a minimal version of the
+ * kernel driver.
+ * If we cannot handle the version differences, we will fail gracefully
+ * (this can happen when we have a user level daemon that is more
+ * advanced than the KVP driver.
+ *
+ * We will use values used in this handshake for determining if we have
+ * workable user level daemon and the kernel driver. We begin by taking the
+ * registration opcode out of the KVP opcode namespace. We will however,
+ * maintain compatibility with the existing user-level daemon code.
+ */
+
+/*
+ * Daemon code not supporting IP injection (legacy daemon).
+ */
+
+#define KVP_OP_REGISTER 4
+
+/*
+ * Daemon code supporting IP injection.
+ * The KVP opcode field is used to communicate the
+ * registration information; so define a namespace that
+ * will be distinct from the host defined KVP opcode.
+ */
+
+#define KVP_OP_REGISTER1 100
+
enum hv_kvp_exchg_op {
KVP_OP_GET = 0,
KVP_OP_SET,
KVP_OP_DELETE,
KVP_OP_ENUMERATE,
- KVP_OP_REGISTER,
+ KVP_OP_GET_IP_INFO,
+ KVP_OP_SET_IP_INFO,
KVP_OP_COUNT /* Number of operations, must be last. */
};
@@ -140,6 +181,39 @@ enum hv_kvp_exchg_pool {
KVP_POOL_COUNT /* Number of pools, must be last. */
};
+/*
+ * Some Hyper-V status codes.
+ */
+
+#define HV_S_OK 0x00000000
+#define HV_E_FAIL 0x80004005
+#define HV_S_CONT 0x80070103
+#define HV_ERROR_NOT_SUPPORTED 0x80070032
+#define HV_ERROR_MACHINE_LOCKED 0x800704F7
+#define HV_ERROR_DEVICE_NOT_CONNECTED 0x8007048F
+#define HV_INVALIDARG 0x80070057
+#define HV_GUID_NOTFOUND 0x80041002
+
+#define ADDR_FAMILY_NONE 0x00
+#define ADDR_FAMILY_IPV4 0x01
+#define ADDR_FAMILY_IPV6 0x02
+
+#define MAX_ADAPTER_ID_SIZE 128
+#define MAX_IP_ADDR_SIZE 1024
+#define MAX_GATEWAY_SIZE 512
+
+
+struct hv_kvp_ipaddr_value {
+ __u16 adapter_id[MAX_ADAPTER_ID_SIZE];
+ __u8 addr_family;
+ __u8 dhcp_enabled;
+ __u16 ip_addr[MAX_IP_ADDR_SIZE];
+ __u16 sub_net[MAX_IP_ADDR_SIZE];
+ __u16 gate_way[MAX_GATEWAY_SIZE];
+ __u16 dns_addr[MAX_IP_ADDR_SIZE];
+} __attribute__((packed));
+
+
struct hv_kvp_hdr {
__u8 operation;
__u8 pool;
@@ -181,16 +255,26 @@ struct hv_kvp_register {
};
struct hv_kvp_msg {
- struct hv_kvp_hdr kvp_hdr;
+ union {
+ struct hv_kvp_hdr kvp_hdr;
+ int error;
+ };
union {
struct hv_kvp_msg_get kvp_get;
struct hv_kvp_msg_set kvp_set;
struct hv_kvp_msg_delete kvp_delete;
struct hv_kvp_msg_enumerate kvp_enum_data;
+ struct hv_kvp_ipaddr_value kvp_ip_val;
struct hv_kvp_register kvp_register;
} body;
} __attribute__((packed));
+struct hv_kvp_ip_msg {
+ __u8 operation;
+ __u8 pool;
+ struct hv_kvp_ipaddr_value kvp_ip_val;
+} __attribute__((packed));
+
#ifdef __KERNEL__
#include <linux/scatterlist.h>
#include <linux/list.h>
@@ -405,7 +489,7 @@ struct vmtransfer_page_range {
struct vmtransfer_page_packet_header {
struct vmpacket_descriptor d;
u16 xfer_pageset_id;
- bool sender_owns_set;
+ u8 sender_owns_set;
u8 reserved;
u32 range_cnt;
struct vmtransfer_page_range ranges[1];
@@ -559,7 +643,7 @@ struct vmbus_channel_query_vmbus_version {
/* VMBus Version Supported parameters */
struct vmbus_channel_version_supported {
struct vmbus_channel_message_header header;
- bool version_supported;
+ u8 version_supported;
} __packed;
/* Offer Channel parameters */
@@ -568,7 +652,7 @@ struct vmbus_channel_offer_channel {
struct vmbus_channel_offer offer;
u32 child_relid;
u8 monitorid;
- bool monitor_allocated;
+ u8 monitor_allocated;
} __packed;
/* Rescind Offer parameters */
@@ -704,7 +788,7 @@ struct vmbus_channel_initiate_contact {
struct vmbus_channel_version_response {
struct vmbus_channel_message_header header;
- bool version_supported;
+ u8 version_supported;
} __packed;
enum vmbus_channel_state {
@@ -977,11 +1061,6 @@ void vmbus_driver_unregister(struct hv_driver *hv_driver);
#define ICMSGHDRFLAG_REQUEST 2
#define ICMSGHDRFLAG_RESPONSE 4
-#define HV_S_OK 0x00000000
-#define HV_E_FAIL 0x80004005
-#define HV_S_CONT 0x80070103
-#define HV_ERROR_NOT_SUPPORTED 0x80070032
-#define HV_ERROR_MACHINE_LOCKED 0x800704F7
/*
* While we want to handle util services as regular devices,
diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h
new file mode 100644
index 000000000000..2e4eab9868a3
--- /dev/null
+++ b/include/linux/iio/adc/ad_sigma_delta.h
@@ -0,0 +1,173 @@
+/*
+ * Support code for Analog Devices Sigma-Delta ADCs
+ *
+ * Copyright 2012 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+#ifndef __AD_SIGMA_DELTA_H__
+#define __AD_SIGMA_DELTA_H__
+
+enum ad_sigma_delta_mode {
+ AD_SD_MODE_CONTINUOUS = 0,
+ AD_SD_MODE_SINGLE = 1,
+ AD_SD_MODE_IDLE = 2,
+ AD_SD_MODE_POWERDOWN = 3,
+};
+
+/**
+ * struct ad_sigma_delta_calib_data - Calibration data for Sigma Delta devices
+ * @mode: Calibration mode.
+ * @channel: Calibration channel.
+ */
+struct ad_sd_calib_data {
+ unsigned int mode;
+ unsigned int channel;
+};
+
+struct ad_sigma_delta;
+struct iio_dev;
+
+/**
+ * struct ad_sigma_delta_info - Sigma Delta driver specific callbacks and options
+ * @set_channel: Will be called to select the current channel, may be NULL.
+ * @set_mode: Will be called to select the current mode, may be NULL.
+ * @postprocess_sample: Is called for each sampled data word, can be used to
+ * modify or drop the sample data, it, may be NULL.
+ * @has_registers: true if the device has writable and readable registers, false
+ * if there is just one read-only sample data shift register.
+ * @addr_shift: Shift of the register address in the communications register.
+ * @read_mask: Mask for the communications register having the read bit set.
+ */
+struct ad_sigma_delta_info {
+ int (*set_channel)(struct ad_sigma_delta *, unsigned int channel);
+ int (*set_mode)(struct ad_sigma_delta *, enum ad_sigma_delta_mode mode);
+ int (*postprocess_sample)(struct ad_sigma_delta *, unsigned int raw_sample);
+ bool has_registers;
+ unsigned int addr_shift;
+ unsigned int read_mask;
+};
+
+/**
+ * struct ad_sigma_delta - Sigma Delta device struct
+ * @spi: The spi device associated with the Sigma Delta device.
+ * @trig: The IIO trigger associated with the Sigma Delta device.
+ *
+ * Most of the fields are private to the sigma delta library code and should not
+ * be accessed by individual drivers.
+ */
+struct ad_sigma_delta {
+ struct spi_device *spi;
+ struct iio_trigger *trig;
+
+/* private: */
+ struct completion completion;
+ bool irq_dis;
+
+ bool bus_locked;
+
+ uint8_t comm;
+
+ const struct ad_sigma_delta_info *info;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ uint8_t data[4] ____cacheline_aligned;
+};
+
+static inline int ad_sigma_delta_set_channel(struct ad_sigma_delta *sd,
+ unsigned int channel)
+{
+ if (sd->info->set_channel)
+ return sd->info->set_channel(sd, channel);
+
+ return 0;
+}
+
+static inline int ad_sigma_delta_set_mode(struct ad_sigma_delta *sd,
+ unsigned int mode)
+{
+ if (sd->info->set_mode)
+ return sd->info->set_mode(sd, mode);
+
+ return 0;
+}
+
+static inline int ad_sigma_delta_postprocess_sample(struct ad_sigma_delta *sd,
+ unsigned int raw_sample)
+{
+ if (sd->info->postprocess_sample)
+ return sd->info->postprocess_sample(sd, raw_sample);
+
+ return 0;
+}
+
+void ad_sd_set_comm(struct ad_sigma_delta *sigma_delta, uint8_t comm);
+int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
+ unsigned int size, unsigned int val);
+int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg,
+ unsigned int size, unsigned int *val);
+
+int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val);
+int ad_sd_calibrate_all(struct ad_sigma_delta *sigma_delta,
+ const struct ad_sd_calib_data *cd, unsigned int n);
+int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev,
+ struct spi_device *spi, const struct ad_sigma_delta_info *info);
+
+int ad_sd_setup_buffer_and_trigger(struct iio_dev *indio_dev);
+void ad_sd_cleanup_buffer_and_trigger(struct iio_dev *indio_dev);
+
+int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig);
+
+#define __AD_SD_CHANNEL(_si, _channel1, _channel2, _address, _bits, \
+ _storagebits, _shift, _extend_name, _type) \
+ { \
+ .type = (_type), \
+ .differential = (_channel2 == -1 ? 0 : 1), \
+ .indexed = 1, \
+ .channel = (_channel1), \
+ .channel2 = (_channel2), \
+ .address = (_address), \
+ .extend_name = (_extend_name), \
+ .info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
+ IIO_CHAN_INFO_SCALE_SHARED_BIT | \
+ IIO_CHAN_INFO_OFFSET_SEPARATE_BIT, \
+ .scan_index = (_si), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_bits), \
+ .storagebits = (_storagebits), \
+ .shift = (_shift), \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+#define AD_SD_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \
+ _storagebits, _shift) \
+ __AD_SD_CHANNEL(_si, _channel1, _channel2, _address, _bits, \
+ _storagebits, _shift, NULL, IIO_VOLTAGE)
+
+#define AD_SD_SHORTED_CHANNEL(_si, _channel, _address, _bits, \
+ _storagebits, _shift) \
+ __AD_SD_CHANNEL(_si, _channel, _channel, _address, _bits, \
+ _storagebits, _shift, "shorted", IIO_VOLTAGE)
+
+#define AD_SD_CHANNEL(_si, _channel, _address, _bits, \
+ _storagebits, _shift) \
+ __AD_SD_CHANNEL(_si, _channel, -1, _address, _bits, \
+ _storagebits, _shift, NULL, IIO_VOLTAGE)
+
+#define AD_SD_TEMP_CHANNEL(_si, _address, _bits, _storagebits, _shift) \
+ __AD_SD_CHANNEL(_si, 0, -1, _address, _bits, \
+ _storagebits, _shift, NULL, IIO_TEMP)
+
+#define AD_SD_SUPPLY_CHANNEL(_si, _channel, _address, _bits, _storagebits, \
+ _shift) \
+ __AD_SD_CHANNEL(_si, _channel, -1, _address, _bits, \
+ _storagebits, _shift, "supply", IIO_VOLTAGE)
+
+#endif
diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h
index 8ba516fc2ec6..c629b3a1d9a9 100644
--- a/include/linux/iio/buffer.h
+++ b/include/linux/iio/buffer.h
@@ -36,7 +36,7 @@ struct iio_buffer;
* any of them not existing.
**/
struct iio_buffer_access_funcs {
- int (*store_to)(struct iio_buffer *buffer, u8 *data, s64 timestamp);
+ int (*store_to)(struct iio_buffer *buffer, u8 *data);
int (*read_first_n)(struct iio_buffer *buffer,
size_t n,
char __user *buf);
@@ -118,10 +118,8 @@ int iio_scan_mask_set(struct iio_dev *indio_dev,
* iio_push_to_buffer() - push to a registered buffer.
* @buffer: IIO buffer structure for device
* @data: the data to push to the buffer
- * @timestamp: timestamp to associate with the data
*/
-int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data,
- s64 timestamp);
+int iio_push_to_buffer(struct iio_buffer *buffer, unsigned char *data);
int iio_update_demux(struct iio_dev *indio_dev);
diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
index e2657e6d4d26..e875bcf0478f 100644
--- a/include/linux/iio/consumer.h
+++ b/include/linux/iio/consumer.h
@@ -8,7 +8,7 @@
* the Free Software Foundation.
*/
#ifndef _IIO_INKERN_CONSUMER_H_
-#define _IIO_INKERN_CONSUMER_H
+#define _IIO_INKERN_CONSUMER_H_
#include <linux/iio/types.h>
struct iio_dev;
@@ -61,7 +61,7 @@ void iio_channel_release_all(struct iio_channel *chan);
/**
* iio_read_channel_raw() - read from a given channel
- * @channel: The channel being queried.
+ * @chan: The channel being queried.
* @val: Value read back.
*
* Note raw reads from iio channels are in adc counts and hence
@@ -71,6 +71,21 @@ int iio_read_channel_raw(struct iio_channel *chan,
int *val);
/**
+ * iio_read_channel_processed() - read processed value from a given channel
+ * @chan: The channel being queried.
+ * @val: Value read back.
+ *
+ * Returns an error code or 0.
+ *
+ * This function will read a processed value from a channel. A processed value
+ * means that this value will have the correct unit and not some device internal
+ * representation. If the device does not support reporting a processed value
+ * the function will query the raw value and the channels scale and offset and
+ * do the appropriate transformation.
+ */
+int iio_read_channel_processed(struct iio_channel *chan, int *val);
+
+/**
* iio_get_channel_type() - get the type of a channel
* @channel: The channel being queried.
* @type: The type of the channel.
@@ -82,7 +97,7 @@ int iio_get_channel_type(struct iio_channel *channel,
/**
* iio_read_channel_scale() - read the scale value for a channel
- * @channel: The channel being queried.
+ * @chan: The channel being queried.
* @val: First part of value read back.
* @val2: Second part of value read back.
*
@@ -93,4 +108,27 @@ int iio_get_channel_type(struct iio_channel *channel,
int iio_read_channel_scale(struct iio_channel *chan, int *val,
int *val2);
+/**
+ * iio_convert_raw_to_processed() - Converts a raw value to a processed value
+ * @chan: The channel being queried
+ * @raw: The raw IIO to convert
+ * @processed: The result of the conversion
+ * @scale: Scale factor to apply during the conversion
+ *
+ * Returns an error code or 0.
+ *
+ * This function converts a raw value to processed value for a specific channel.
+ * A raw value is the device internal representation of a sample and the value
+ * returned by iio_read_channel_raw, so the unit of that value is device
+ * depended. A processed value on the other hand is value has a normed unit
+ * according with the IIO specification.
+ *
+ * The scale factor allows to increase the precession of the returned value. For
+ * a scale factor of 1 the function will return the result in the normal IIO
+ * unit for the channel type. E.g. millivolt for voltage channels, if you want
+ * nanovolts instead pass 1000 as the scale factor.
+ */
+int iio_convert_raw_to_processed(struct iio_channel *chan, int raw,
+ int *processed, unsigned int scale);
+
#endif
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index be82936c4089..c0ae76ac4e0b 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -35,10 +35,13 @@ enum iio_chan_info_enum {
IIO_CHAN_INFO_FREQUENCY,
IIO_CHAN_INFO_PHASE,
IIO_CHAN_INFO_HARDWAREGAIN,
+ IIO_CHAN_INFO_HYSTERESIS,
};
#define IIO_CHAN_INFO_SHARED_BIT(type) BIT(type*2)
#define IIO_CHAN_INFO_SEPARATE_BIT(type) BIT(type*2 + 1)
+#define IIO_CHAN_INFO_BITS(type) (IIO_CHAN_INFO_SHARED_BIT(type) | \
+ IIO_CHAN_INFO_SEPARATE_BIT(type))
#define IIO_CHAN_INFO_RAW_SEPARATE_BIT \
IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_RAW)
@@ -100,6 +103,10 @@ enum iio_chan_info_enum {
IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_HARDWAREGAIN)
#define IIO_CHAN_INFO_HARDWAREGAIN_SHARED_BIT \
IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_HARDWAREGAIN)
+#define IIO_CHAN_INFO_HYSTERESIS_SEPARATE_BIT \
+ IIO_CHAN_INFO_SEPARATE_BIT(IIO_CHAN_INFO_HYSTERESIS)
+#define IIO_CHAN_INFO_HYSTERESIS_SHARED_BIT \
+ IIO_CHAN_INFO_SHARED_BIT(IIO_CHAN_INFO_HYSTERESIS)
enum iio_endian {
IIO_CPU,
@@ -164,7 +171,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev,
* IIO_ENUM() - Initialize enum extended channel attribute
* @_name: Attribute name
* @_shared: Whether the attribute is shared between all channels
- * @_e: Pointer to a iio_enum struct
+ * @_e: Pointer to an iio_enum struct
*
* This should usually be used together with IIO_ENUM_AVAILABLE()
*/
@@ -180,9 +187,9 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev,
/**
* IIO_ENUM_AVAILABLE() - Initialize enum available extended channel attribute
* @_name: Attribute name ("_available" will be appended to the name)
- * @_e: Pointer to a iio_enum struct
+ * @_e: Pointer to an iio_enum struct
*
- * Creates a read only attribute which list all the available enum items in a
+ * Creates a read only attribute which lists all the available enum items in a
* space separated list. This should usually be used together with IIO_ENUM()
*/
#define IIO_ENUM_AVAILABLE(_name, _e) \
@@ -229,6 +236,7 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev,
* @indexed: Specify the channel has a numerical index. If not,
* the channel index number will be suppressed for sysfs
* attributes but not for event codes.
+ * @output: Channel is output.
* @differential: Channel is differential.
*/
struct iio_chan_spec {
@@ -255,6 +263,21 @@ struct iio_chan_spec {
unsigned differential:1;
};
+
+/**
+ * iio_channel_has_info() - Checks whether a channel supports a info attribute
+ * @chan: The channel to be queried
+ * @type: Type of the info attribute to be checked
+ *
+ * Returns true if the channels supports reporting values for the given info
+ * attribute type, false otherwise.
+ */
+static inline bool iio_channel_has_info(const struct iio_chan_spec *chan,
+ enum iio_chan_info_enum type)
+{
+ return chan->info_mask & IIO_CHAN_INFO_BITS(type);
+}
+
#define IIO_ST(si, rb, sb, sh) \
{ .sign = si, .realbits = rb, .storagebits = sb, .shift = sh }
@@ -312,6 +335,9 @@ struct iio_dev;
* Meaning is event dependent.
* @validate_trigger: function to validate the trigger when the
* current trigger gets changed.
+ * @update_scan_mode: function to configure device and scan buffer when
+ * channels have changed
+ * @debugfs_reg_access: function to read or write register value of device
**/
struct iio_info {
struct module *driver_module;
@@ -367,10 +393,10 @@ struct iio_info {
* scan mask is valid for the device.
*/
struct iio_buffer_setup_ops {
- int (*preenable)(struct iio_dev *);
- int (*postenable)(struct iio_dev *);
- int (*predisable)(struct iio_dev *);
- int (*postdisable)(struct iio_dev *);
+ int (*preenable)(struct iio_dev *);
+ int (*postenable)(struct iio_dev *);
+ int (*predisable)(struct iio_dev *);
+ int (*postdisable)(struct iio_dev *);
bool (*validate_scan_mask)(struct iio_dev *indio_dev,
const unsigned long *scan_mask);
};
@@ -516,6 +542,31 @@ static inline struct iio_dev *iio_device_get(struct iio_dev *indio_dev)
return indio_dev ? dev_to_iio_dev(get_device(&indio_dev->dev)) : NULL;
}
+
+/**
+ * iio_device_set_drvdata() - Set device driver data
+ * @indio_dev: IIO device structure
+ * @data: Driver specific data
+ *
+ * Allows to attach an arbitrary pointer to an IIO device, which can later be
+ * retrieved by iio_device_get_drvdata().
+ */
+static inline void iio_device_set_drvdata(struct iio_dev *indio_dev, void *data)
+{
+ dev_set_drvdata(&indio_dev->dev, data);
+}
+
+/**
+ * iio_device_get_drvdata() - Get device driver data
+ * @indio_dev: IIO device structure
+ *
+ * Returns the data previously set with iio_device_set_drvdata()
+ */
+static inline void *iio_device_get_drvdata(struct iio_dev *indio_dev)
+{
+ return dev_get_drvdata(&indio_dev->dev);
+}
+
/* Can we make this smaller? */
#define IIO_ALIGN L1_CACHE_BYTES
/**
diff --git a/include/linux/iio/kfifo_buf.h b/include/linux/iio/kfifo_buf.h
index 014d5a13b32b..25eeac762e84 100644
--- a/include/linux/iio/kfifo_buf.h
+++ b/include/linux/iio/kfifo_buf.h
@@ -1,3 +1,5 @@
+#ifndef __LINUX_IIO_KFIFO_BUF_H__
+#define __LINUX_IIO_KFIFO_BUF_H__
#include <linux/kfifo.h>
#include <linux/iio/iio.h>
@@ -6,3 +8,4 @@
struct iio_buffer *iio_kfifo_allocate(struct iio_dev *indio_dev);
void iio_kfifo_free(struct iio_buffer *r);
+#endif
diff --git a/include/linux/iio/machine.h b/include/linux/iio/machine.h
index 400a453ff67b..809a3f08d5a5 100644
--- a/include/linux/iio/machine.h
+++ b/include/linux/iio/machine.h
@@ -8,6 +8,9 @@
* the Free Software Foundation.
*/
+#ifndef __LINUX_IIO_MACHINE_H__
+#define __LINUX_IIO_MACHINE_H__
+
/**
* struct iio_map - description of link between consumer and device channels
* @adc_channel_label: Label used to identify the channel on the provider.
@@ -22,3 +25,5 @@ struct iio_map {
const char *consumer_dev_name;
const char *consumer_channel;
};
+
+#endif
diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h
index a9819940a84c..20239da1d0f7 100644
--- a/include/linux/iio/trigger.h
+++ b/include/linux/iio/trigger.h
@@ -29,7 +29,7 @@ struct iio_subirq {
* instances of a given device.
**/
struct iio_trigger_ops {
- struct module *owner;
+ struct module *owner;
int (*set_trigger_state)(struct iio_trigger *trig, bool state);
int (*try_reenable)(struct iio_trigger *trig);
int (*validate_device)(struct iio_trigger *trig,
@@ -39,7 +39,7 @@ struct iio_trigger_ops {
/**
* struct iio_trigger - industrial I/O trigger device
- *
+ * @ops: [DRIVER] operations structure
* @id: [INTERN] unique id number
* @name: [DRIVER] unique name
* @dev: [DRIVER] associated device (if relevant)
@@ -76,19 +76,19 @@ struct iio_trigger {
static inline struct iio_trigger *to_iio_trigger(struct device *d)
{
return container_of(d, struct iio_trigger, dev);
-};
+}
static inline void iio_trigger_put(struct iio_trigger *trig)
{
module_put(trig->ops->owner);
put_device(&trig->dev);
-};
+}
static inline void iio_trigger_get(struct iio_trigger *trig)
{
get_device(&trig->dev);
__module_get(trig->ops->owner);
-};
+}
/**
* iio_trigger_register() - register a trigger with the IIO core
@@ -104,7 +104,8 @@ void iio_trigger_unregister(struct iio_trigger *trig_info);
/**
* iio_trigger_poll() - called on a trigger occurring
- * @trig: trigger which occurred
+ * @trig: trigger which occurred
+ * @time: timestamp when trigger occurred
*
* Typically called in relevant hardware interrupt handler.
**/
diff --git a/include/linux/iio/trigger_consumer.h b/include/linux/iio/trigger_consumer.h
index 60d64b356945..c4f8c7409666 100644
--- a/include/linux/iio/trigger_consumer.h
+++ b/include/linux/iio/trigger_consumer.h
@@ -7,6 +7,15 @@
* the Free Software Foundation.
*/
+#ifndef __LINUX_IIO_TRIGGER_CONSUMER_H__
+#define __LINUX_IIO_TRIGGER_CONSUMER_H__
+
+#include <linux/interrupt.h>
+#include <linux/types.h>
+
+struct iio_dev;
+struct iio_trigger;
+
/**
* struct iio_poll_func - poll function pair
*
@@ -50,3 +59,5 @@ void iio_trigger_notify_done(struct iio_trigger *trig);
*/
int iio_triggered_buffer_postenable(struct iio_dev *indio_dev);
int iio_triggered_buffer_predisable(struct iio_dev *indio_dev);
+
+#endif
diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h
index 44e397705d7f..5c647ecfd5ba 100644
--- a/include/linux/iio/types.h
+++ b/include/linux/iio/types.h
@@ -57,5 +57,6 @@ enum iio_modifier {
#define IIO_VAL_INT_PLUS_MICRO 2
#define IIO_VAL_INT_PLUS_NANO 3
#define IIO_VAL_INT_PLUS_MICRO_DB 4
+#define IIO_VAL_FRACTIONAL 10
#endif /* _IIO_TYPES_H_ */
diff --git a/include/linux/input.h b/include/linux/input.h
index 725dcd0f63a4..ba4874302939 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -1169,6 +1169,18 @@ struct ff_effect {
#include <linux/mod_devicetable.h>
/**
+ * struct input_value - input value representation
+ * @type: type of value (EV_KEY, EV_ABS, etc)
+ * @code: the value code
+ * @value: the value
+ */
+struct input_value {
+ __u16 type;
+ __u16 code;
+ __s32 value;
+};
+
+/**
* struct input_dev - represents an input device
* @name: name of the device
* @phys: physical path to the device in the system hierarchy
@@ -1203,11 +1215,7 @@ struct ff_effect {
* software autorepeat
* @timer: timer for software autorepeat
* @rep: current values for autorepeat parameters (delay, rate)
- * @mt: pointer to array of struct input_mt_slot holding current values
- * of tracked contacts
- * @mtsize: number of MT slots the device uses
- * @slot: MT slot currently being transmitted
- * @trkid: stores MT tracking ID for the current contact
+ * @mt: pointer to multitouch state
* @absinfo: array of &struct input_absinfo elements holding information
* about absolute axes (current value, min, max, flat, fuzz,
* resolution)
@@ -1244,7 +1252,6 @@ struct ff_effect {
* last user closes the device
* @going_away: marks devices that are in a middle of unregistering and
* causes input_open_device*() fail with -ENODEV.
- * @sync: set to %true when there were no new events since last EV_SYN
* @dev: driver model's view of this device
* @h_list: list of input handles associated with the device. When
* accessing the list dev->mutex must be held
@@ -1287,10 +1294,7 @@ struct input_dev {
int rep[REP_CNT];
- struct input_mt_slot *mt;
- int mtsize;
- int slot;
- int trkid;
+ struct input_mt *mt;
struct input_absinfo *absinfo;
@@ -1312,12 +1316,14 @@ struct input_dev {
unsigned int users;
bool going_away;
- bool sync;
-
struct device dev;
struct list_head h_list;
struct list_head node;
+
+ unsigned int num_vals;
+ unsigned int max_vals;
+ struct input_value *vals;
};
#define to_input_dev(d) container_of(d, struct input_dev, dev)
@@ -1378,6 +1384,9 @@ struct input_handle;
* @event: event handler. This method is being called by input core with
* interrupts disabled and dev->event_lock spinlock held and so
* it may not sleep
+ * @events: event sequence handler. This method is being called by
+ * input core with interrupts disabled and dev->event_lock
+ * spinlock held and so it may not sleep
* @filter: similar to @event; separates normal event handlers from
* "filters".
* @match: called after comparing device's id with handler's id_table
@@ -1414,6 +1423,8 @@ struct input_handler {
void *private;
void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
+ void (*events)(struct input_handle *handle,
+ const struct input_value *vals, unsigned int count);
bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
bool (*match)(struct input_handler *handler, struct input_dev *dev);
int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
diff --git a/include/linux/input/mt.h b/include/linux/input/mt.h
index f86737586e19..cc5cca774bab 100644
--- a/include/linux/input/mt.h
+++ b/include/linux/input/mt.h
@@ -15,12 +15,41 @@
#define TRKID_MAX 0xffff
+#define INPUT_MT_POINTER 0x0001 /* pointer device, e.g. trackpad */
+#define INPUT_MT_DIRECT 0x0002 /* direct device, e.g. touchscreen */
+#define INPUT_MT_DROP_UNUSED 0x0004 /* drop contacts not seen in frame */
+#define INPUT_MT_TRACK 0x0008 /* use in-kernel tracking */
+
/**
* struct input_mt_slot - represents the state of an input MT slot
* @abs: holds current values of ABS_MT axes for this slot
+ * @frame: last frame at which input_mt_report_slot_state() was called
+ * @key: optional driver designation of this slot
*/
struct input_mt_slot {
int abs[ABS_MT_LAST - ABS_MT_FIRST + 1];
+ unsigned int frame;
+ unsigned int key;
+};
+
+/**
+ * struct input_mt - state of tracked contacts
+ * @trkid: stores MT tracking ID for the next contact
+ * @num_slots: number of MT slots the device uses
+ * @slot: MT slot currently being transmitted
+ * @flags: input_mt operation flags
+ * @frame: increases every time input_mt_sync_frame() is called
+ * @red: reduced cost matrix for in-kernel tracking
+ * @slots: array of slots holding current values of tracked contacts
+ */
+struct input_mt {
+ int trkid;
+ int num_slots;
+ int slot;
+ unsigned int flags;
+ unsigned int frame;
+ int *red;
+ struct input_mt_slot slots[];
};
static inline void input_mt_set_value(struct input_mt_slot *slot,
@@ -35,12 +64,18 @@ static inline int input_mt_get_value(const struct input_mt_slot *slot,
return slot->abs[code - ABS_MT_FIRST];
}
-int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots);
+static inline bool input_mt_is_active(const struct input_mt_slot *slot)
+{
+ return input_mt_get_value(slot, ABS_MT_TRACKING_ID) >= 0;
+}
+
+int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
+ unsigned int flags);
void input_mt_destroy_slots(struct input_dev *dev);
-static inline int input_mt_new_trkid(struct input_dev *dev)
+static inline int input_mt_new_trkid(struct input_mt *mt)
{
- return dev->trkid++ & TRKID_MAX;
+ return mt->trkid++ & TRKID_MAX;
}
static inline void input_mt_slot(struct input_dev *dev, int slot)
@@ -64,4 +99,20 @@ void input_mt_report_slot_state(struct input_dev *dev,
void input_mt_report_finger_count(struct input_dev *dev, int count);
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count);
+void input_mt_sync_frame(struct input_dev *dev);
+
+/**
+ * struct input_mt_pos - contact position
+ * @x: horizontal coordinate
+ * @y: vertical coordinate
+ */
+struct input_mt_pos {
+ s16 x, y;
+};
+
+int input_mt_assign_slots(struct input_dev *dev, int *slots,
+ const struct input_mt_pos *pos, int num_pos);
+
+int input_mt_get_slot_by_key(struct input_dev *dev, int key);
+
#endif
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index c5f856a040b9..5e4e6170f43a 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -430,6 +430,8 @@ enum
NR_SOFTIRQS
};
+#define SOFTIRQ_STOP_IDLE_MASK (~(1 << RCU_SOFTIRQ))
+
/* map softirq index to softirq name. update 'softirq_to_name' in
* kernel/softirq.c when adding a new softirq.
*/
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 7e83370e6fd2..f3b99e1c1042 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -256,72 +256,78 @@ static inline void iommu_set_fault_handler(struct iommu_domain *domain,
{
}
-int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group)
+static inline int iommu_attach_group(struct iommu_domain *domain,
+ struct iommu_group *group)
{
return -ENODEV;
}
-void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group)
+static inline void iommu_detach_group(struct iommu_domain *domain,
+ struct iommu_group *group)
{
}
-struct iommu_group *iommu_group_alloc(void)
+static inline struct iommu_group *iommu_group_alloc(void)
{
return ERR_PTR(-ENODEV);
}
-void *iommu_group_get_iommudata(struct iommu_group *group)
+static inline void *iommu_group_get_iommudata(struct iommu_group *group)
{
return NULL;
}
-void iommu_group_set_iommudata(struct iommu_group *group, void *iommu_data,
- void (*release)(void *iommu_data))
+static inline void iommu_group_set_iommudata(struct iommu_group *group,
+ void *iommu_data,
+ void (*release)(void *iommu_data))
{
}
-int iommu_group_set_name(struct iommu_group *group, const char *name)
+static inline int iommu_group_set_name(struct iommu_group *group,
+ const char *name)
{
return -ENODEV;
}
-int iommu_group_add_device(struct iommu_group *group, struct device *dev)
+static inline int iommu_group_add_device(struct iommu_group *group,
+ struct device *dev)
{
return -ENODEV;
}
-void iommu_group_remove_device(struct device *dev)
+static inline void iommu_group_remove_device(struct device *dev)
{
}
-int iommu_group_for_each_dev(struct iommu_group *group, void *data,
- int (*fn)(struct device *, void *))
+static inline int iommu_group_for_each_dev(struct iommu_group *group,
+ void *data,
+ int (*fn)(struct device *, void *))
{
return -ENODEV;
}
-struct iommu_group *iommu_group_get(struct device *dev)
+static inline struct iommu_group *iommu_group_get(struct device *dev)
{
return NULL;
}
-void iommu_group_put(struct iommu_group *group)
+static inline void iommu_group_put(struct iommu_group *group)
{
}
-int iommu_group_register_notifier(struct iommu_group *group,
- struct notifier_block *nb)
+static inline int iommu_group_register_notifier(struct iommu_group *group,
+ struct notifier_block *nb)
{
return -ENODEV;
}
-int iommu_group_unregister_notifier(struct iommu_group *group,
- struct notifier_block *nb)
+static inline int iommu_group_unregister_notifier(struct iommu_group *group,
+ struct notifier_block *nb)
{
return 0;
}
-int iommu_group_id(struct iommu_group *group)
+static inline int iommu_group_id(struct iommu_group *group)
{
return -ENODEV;
}
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index 9a323d12de1c..0ba014c55056 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -10,12 +10,10 @@
struct irq_affinity_notify;
struct proc_dir_entry;
-struct timer_rand_state;
struct module;
/**
* struct irq_desc - interrupt descriptor
* @irq_data: per irq and chip data passed down to chip functions
- * @timer_rand_state: pointer to timer rand state struct
* @kstat_irqs: irq stats per cpu
* @handle_irq: highlevel irq-events handler
* @preflow_handler: handler called before the flow handler (currently used by sparc)
diff --git a/include/linux/kdb.h b/include/linux/kdb.h
index 42d9e863a313..7f6fe6e015bc 100644
--- a/include/linux/kdb.h
+++ b/include/linux/kdb.h
@@ -13,6 +13,14 @@
* Copyright (C) 2009 Jason Wessel <jason.wessel@windriver.com>
*/
+typedef enum {
+ KDB_REPEAT_NONE = 0, /* Do not repeat this command */
+ KDB_REPEAT_NO_ARGS, /* Repeat the command without arguments */
+ KDB_REPEAT_WITH_ARGS, /* Repeat the command including its arguments */
+} kdb_repeat_t;
+
+typedef int (*kdb_func_t)(int, const char **);
+
#ifdef CONFIG_KGDB_KDB
#include <linux/init.h>
#include <linux/sched.h>
@@ -32,14 +40,6 @@ extern atomic_t kdb_event;
#define KDB_MAXARGS 16 /* Maximum number of arguments to a function */
-typedef enum {
- KDB_REPEAT_NONE = 0, /* Do not repeat this command */
- KDB_REPEAT_NO_ARGS, /* Repeat the command without arguments */
- KDB_REPEAT_WITH_ARGS, /* Repeat the command including its arguments */
-} kdb_repeat_t;
-
-typedef int (*kdb_func_t)(int, const char **);
-
/* KDB return codes from a command or internal kdb function */
#define KDB_NOTFOUND (-1)
#define KDB_ARGCOUNT (-2)
@@ -149,11 +149,14 @@ extern int kdb_register_repeat(char *, kdb_func_t, char *, char *,
short, kdb_repeat_t);
extern int kdb_unregister(char *);
#else /* ! CONFIG_KGDB_KDB */
-#define kdb_printf(...)
-#define kdb_init(x)
-#define kdb_register(...)
-#define kdb_register_repeat(...)
-#define kdb_uregister(x)
+static inline __printf(1, 2) int kdb_printf(const char *fmt, ...) { return 0; }
+static inline void kdb_init(int level) {}
+static inline int kdb_register(char *cmd, kdb_func_t func, char *usage,
+ char *help, short minlen) { return 0; }
+static inline int kdb_register_repeat(char *cmd, kdb_func_t func, char *usage,
+ char *help, short minlen,
+ kdb_repeat_t repeat) { return 0; }
+static inline int kdb_unregister(char *cmd) { return 0; }
#endif /* CONFIG_KGDB_KDB */
enum {
KDB_NOT_INITIALIZED,
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 594b419b7d20..2451f1f7a1d9 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -91,7 +91,7 @@
{ \
typeof(x) __x = x; \
typeof(divisor) __d = divisor; \
- (((typeof(x))-1) >= 0 || (__x) >= 0) ? \
+ (((typeof(x))-1) > 0 || (__x) > 0) ? \
(((__x) + ((__d) / 2)) / (__d)) : \
(((__x) - ((__d) / 2)) / (__d)); \
} \
diff --git a/include/linux/kernel_stat.h b/include/linux/kernel_stat.h
index 2fbd9053c2df..36d12f0884c3 100644
--- a/include/linux/kernel_stat.h
+++ b/include/linux/kernel_stat.h
@@ -130,4 +130,12 @@ extern void account_process_tick(struct task_struct *, int user);
extern void account_steal_ticks(unsigned long ticks);
extern void account_idle_ticks(unsigned long ticks);
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING
+extern void vtime_task_switch(struct task_struct *prev);
+extern void vtime_account_system(struct task_struct *tsk);
+extern void vtime_account_idle(struct task_struct *tsk);
+#else
+static inline void vtime_task_switch(struct task_struct *prev) { }
+#endif
+
#endif /* _LINUX_KERNEL_STAT_H */
diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h
index c4d2fc194ede..4dff0c6ed58f 100644
--- a/include/linux/kgdb.h
+++ b/include/linux/kgdb.h
@@ -240,6 +240,7 @@ extern void kgdb_arch_late(void);
* hardware breakpoints.
* @correct_hw_break: Allow an architecture to specify how to correct the
* hardware debug registers.
+ * @enable_nmi: Manage NMI-triggered entry to KGDB
*/
struct kgdb_arch {
unsigned char gdb_bpt_instr[BREAK_INSTR_SIZE];
@@ -252,6 +253,8 @@ struct kgdb_arch {
void (*disable_hw_break)(struct pt_regs *regs);
void (*remove_all_hw_break)(void);
void (*correct_hw_break)(void);
+
+ void (*enable_nmi)(bool on);
};
/**
@@ -283,6 +286,16 @@ extern struct kgdb_arch arch_kgdb_ops;
extern unsigned long __weak kgdb_arch_pc(int exception, struct pt_regs *regs);
+#ifdef CONFIG_SERIAL_KGDB_NMI
+extern int kgdb_register_nmi_console(void);
+extern int kgdb_unregister_nmi_console(void);
+extern bool kgdb_nmi_poll_knock(void);
+#else
+static inline int kgdb_register_nmi_console(void) { return 0; }
+static inline int kgdb_unregister_nmi_console(void) { return 0; }
+static inline bool kgdb_nmi_poll_knock(void) { return 1; }
+#endif
+
extern int kgdb_register_io_module(struct kgdb_io *local_kgdb_io_ops);
extern void kgdb_unregister_io_module(struct kgdb_io *local_kgdb_io_ops);
extern struct kgdb_io *dbg_io_ops;
diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
index b6e1f8c00577..23755ba42abc 100644
--- a/include/linux/kprobes.h
+++ b/include/linux/kprobes.h
@@ -38,6 +38,7 @@
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <linux/mutex.h>
+#include <linux/ftrace.h>
#ifdef CONFIG_KPROBES
#include <asm/kprobes.h>
@@ -48,14 +49,26 @@
#define KPROBE_REENTER 0x00000004
#define KPROBE_HIT_SSDONE 0x00000008
+/*
+ * If function tracer is enabled and the arch supports full
+ * passing of pt_regs to function tracing, then kprobes can
+ * optimize on top of function tracing.
+ */
+#if defined(CONFIG_FUNCTION_TRACER) && defined(ARCH_SUPPORTS_FTRACE_SAVE_REGS) \
+ && defined(ARCH_SUPPORTS_KPROBES_ON_FTRACE)
+# define KPROBES_CAN_USE_FTRACE
+#endif
+
/* Attach to insert probes on any functions which should be ignored*/
#define __kprobes __attribute__((__section__(".kprobes.text")))
+
#else /* CONFIG_KPROBES */
typedef int kprobe_opcode_t;
struct arch_specific_insn {
int dummy;
};
#define __kprobes
+
#endif /* CONFIG_KPROBES */
struct kprobe;
@@ -128,6 +141,7 @@ struct kprobe {
* NOTE:
* this flag is only for optimized_kprobe.
*/
+#define KPROBE_FLAG_FTRACE 8 /* probe is using ftrace */
/* Has this kprobe gone ? */
static inline int kprobe_gone(struct kprobe *p)
@@ -146,6 +160,13 @@ static inline int kprobe_optimized(struct kprobe *p)
{
return p->flags & KPROBE_FLAG_OPTIMIZED;
}
+
+/* Is this kprobe uses ftrace ? */
+static inline int kprobe_ftrace(struct kprobe *p)
+{
+ return p->flags & KPROBE_FLAG_FTRACE;
+}
+
/*
* Special probe type that uses setjmp-longjmp type tricks to resume
* execution at a specified entry with a matching prototype corresponding
@@ -295,6 +316,12 @@ extern int proc_kprobes_optimization_handler(struct ctl_table *table,
#endif
#endif /* CONFIG_OPTPROBES */
+#ifdef KPROBES_CAN_USE_FTRACE
+extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *ops, struct pt_regs *regs);
+extern int arch_prepare_kprobe_ftrace(struct kprobe *p);
+#endif
+
/* Get the kprobe at this addr (if any) - called with preemption disabled */
struct kprobe *get_kprobe(void *addr);
diff --git a/include/linux/kthread.h b/include/linux/kthread.h
index 22ccf9dee177..8d816646f766 100644
--- a/include/linux/kthread.h
+++ b/include/linux/kthread.h
@@ -14,6 +14,11 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)
+struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
+ void *data,
+ unsigned int cpu,
+ const char *namefmt);
+
/**
* kthread_run - create and wake a thread.
* @threadfn: the function to run until signal_pending(current).
@@ -34,9 +39,13 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
void kthread_bind(struct task_struct *k, unsigned int cpu);
int kthread_stop(struct task_struct *k);
-int kthread_should_stop(void);
+bool kthread_should_stop(void);
+bool kthread_should_park(void);
bool kthread_freezable_should_stop(bool *was_frozen);
void *kthread_data(struct task_struct *k);
+int kthread_park(struct task_struct *k);
+void kthread_unpark(struct task_struct *k);
+void kthread_parkme(void);
int kthreadd(void *unused);
extern struct task_struct *kthreadd_task;
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index b70b48b01098..8a59e0abe5fa 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -685,7 +685,7 @@ static inline int kvm_deassign_device(struct kvm *kvm,
static inline void kvm_guest_enter(void)
{
BUG_ON(preemptible());
- account_system_vtime(current);
+ vtime_account(current);
current->flags |= PF_VCPU;
/* KVM does not hold any references to rcu protected data when it
* switches CPU into a guest mode. In fact switching to a guest mode
@@ -699,7 +699,7 @@ static inline void kvm_guest_enter(void)
static inline void kvm_guest_exit(void)
{
- account_system_vtime(current);
+ vtime_account(current);
current->flags &= ~PF_VCPU;
}
diff --git a/include/linux/leds.h b/include/linux/leds.h
index 3aade1d8f410..c6f8dad2ceb0 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -237,4 +237,20 @@ struct gpio_led_platform_data {
struct platform_device *gpio_led_register_device(
int id, const struct gpio_led_platform_data *pdata);
+enum cpu_led_event {
+ CPU_LED_IDLE_START, /* CPU enters idle */
+ CPU_LED_IDLE_END, /* CPU idle ends */
+ CPU_LED_START, /* Machine starts, especially resume */
+ CPU_LED_STOP, /* Machine stops, especially suspend */
+ CPU_LED_HALTED, /* Machine shutdown */
+};
+#ifdef CONFIG_LEDS_TRIGGER_CPU
+extern void ledtrig_cpu(enum cpu_led_event evt);
+#else
+static inline void ledtrig_cpu(enum cpu_led_event evt)
+{
+ return;
+}
+#endif
+
#endif /* __LINUX_LEDS_H_INCLUDED */
diff --git a/include/linux/memory.h b/include/linux/memory.h
index 1ac7f6e405f9..ff9a9f8e0ed9 100644
--- a/include/linux/memory.h
+++ b/include/linux/memory.h
@@ -19,7 +19,7 @@
#include <linux/compiler.h>
#include <linux/mutex.h>
-#define MIN_MEMORY_BLOCK_SIZE (1 << SECTION_SIZE_BITS)
+#define MIN_MEMORY_BLOCK_SIZE (1UL << SECTION_SIZE_BITS)
struct memory_block {
unsigned long start_section_nr;
diff --git a/include/linux/mfd/abx500/ab8500-codec.h b/include/linux/mfd/abx500/ab8500-codec.h
index dc6529202cdd..d7079413def0 100644
--- a/include/linux/mfd/abx500/ab8500-codec.h
+++ b/include/linux/mfd/abx500/ab8500-codec.h
@@ -23,7 +23,8 @@ enum amic_type {
/* Mic-biases */
enum amic_micbias {
AMIC_MICBIAS_VAMIC1,
- AMIC_MICBIAS_VAMIC2
+ AMIC_MICBIAS_VAMIC2,
+ AMIC_MICBIAS_UNKNOWN
};
/* Bias-voltage */
@@ -31,7 +32,8 @@ enum ear_cm_voltage {
EAR_CMV_0_95V,
EAR_CMV_1_10V,
EAR_CMV_1_27V,
- EAR_CMV_1_58V
+ EAR_CMV_1_58V,
+ EAR_CMV_UNKNOWN
};
/* Analog microphone settings */
diff --git a/include/linux/mfd/max77686.h b/include/linux/mfd/max77686.h
index 3d7ae4d7fd36..46c0f320ed76 100644
--- a/include/linux/mfd/max77686.h
+++ b/include/linux/mfd/max77686.h
@@ -74,6 +74,7 @@ enum max77686_regulators {
struct max77686_regulator_data {
int id;
struct regulator_init_data *initdata;
+ struct device_node *of_node;
};
enum max77686_opmode {
diff --git a/include/linux/mfd/max8998.h b/include/linux/mfd/max8998.h
index f4f0dfa4698a..6823548d0c0a 100644
--- a/include/linux/mfd/max8998.h
+++ b/include/linux/mfd/max8998.h
@@ -67,7 +67,7 @@ struct max8998_regulator_data {
/**
* struct max8998_board - packages regulator init data
* @regulators: array of defined regulators
- * @num_regulators: number of regultors used
+ * @num_regulators: number of regulators used
* @irq_base: base IRQ number for max8998, required for IRQs
* @ono: power onoff IRQ number for max8998
* @buck_voltage_lock: Do NOT change the values of the following six
diff --git a/include/linux/micrel_phy.h b/include/linux/micrel_phy.h
index 61f0905bdc48..de201203bc7c 100644
--- a/include/linux/micrel_phy.h
+++ b/include/linux/micrel_phy.h
@@ -1,3 +1,15 @@
+/*
+ * include/linux/micrel_phy.h
+ *
+ * Micrel PHY IDs
+ *
+ * This program is free software; 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 _MICREL_PHY_H
#define _MICREL_PHY_H
@@ -5,10 +17,11 @@
#define PHY_ID_KSZ9021 0x00221610
#define PHY_ID_KS8737 0x00221720
-#define PHY_ID_KS8041 0x00221510
-#define PHY_ID_KS8051 0x00221550
+#define PHY_ID_KSZ8021 0x00221555
+#define PHY_ID_KSZ8041 0x00221510
+#define PHY_ID_KSZ8051 0x00221550
/* both for ks8001 Rev. A/B, and for ks8721 Rev 3. */
-#define PHY_ID_KS8001 0x0022161A
+#define PHY_ID_KSZ8001 0x0022161A
/* struct phy_device dev_flags definitions */
#define MICREL_PHY_50MHZ_CLK 0x00000001
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 6955045199b0..fed3def62818 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -232,7 +232,7 @@ struct of_device_id
char type[32];
char compatible[128];
#ifdef __KERNEL__
- void *data;
+ const void *data;
#else
kernel_ulong_t data;
#endif
@@ -600,4 +600,12 @@ struct x86_cpu_id {
#define X86_MODEL_ANY 0
#define X86_FEATURE_ANY 0 /* Same as FPU, you can't test for that */
+#define IPACK_ANY_FORMAT 0xff
+#define IPACK_ANY_ID (~0)
+struct ipack_device_id {
+ __u8 format; /* Format version or IPACK_ANY_ID */
+ __u32 vendor; /* Vendor ID or IPACK_ANY_ID */
+ __u32 device; /* Device ID or IPACK_ANY_ID */
+};
+
#endif /* LINUX_MOD_DEVICETABLE_H */
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 59dc05f38247..5f49cc0a107e 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -2720,9 +2720,6 @@ static inline const char *netdev_name(const struct net_device *dev)
return dev->name;
}
-extern int __netdev_printk(const char *level, const struct net_device *dev,
- struct va_format *vaf);
-
extern __printf(3, 4)
int netdev_printk(const char *level, const struct net_device *dev,
const char *format, ...);
diff --git a/include/linux/nvme.h b/include/linux/nvme.h
index 9490a00529f4..c25cccaa555a 100644
--- a/include/linux/nvme.h
+++ b/include/linux/nvme.h
@@ -35,8 +35,10 @@ struct nvme_bar {
__u64 acq; /* Admin CQ Base Address */
};
+#define NVME_CAP_MQES(cap) ((cap) & 0xffff)
#define NVME_CAP_TIMEOUT(cap) (((cap) >> 24) & 0xff)
#define NVME_CAP_STRIDE(cap) (((cap) >> 32) & 0xf)
+#define NVME_CAP_MPSMIN(cap) (((cap) >> 48) & 0xf)
enum {
NVME_CC_ENABLE = 1 << 0,
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index 1717cd935e1c..b8e241125201 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -83,6 +83,11 @@ static inline unsigned int irq_of_parse_and_map(struct device_node *dev,
{
return 0;
}
+
+static inline void *of_irq_find_parent(struct device_node *child)
+{
+ return NULL;
+}
#endif /* !CONFIG_OF */
#endif /* __OF_IRQ_H */
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index 248fba2af98a..9a22b5efb384 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -22,19 +22,24 @@ extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle);
static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev)
{
struct pci_bus *pbus = pdev->bus;
+
/* Find a PCI root bus */
while (!pci_is_root_bus(pbus))
pbus = pbus->parent;
- return acpi_get_pci_rootbridge_handle(pci_domain_nr(pbus),
- pbus->number);
+
+ return DEVICE_ACPI_HANDLE(pbus->bridge);
}
static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus)
{
- if (!pci_is_root_bus(pbus))
- return DEVICE_ACPI_HANDLE(&(pbus->self->dev));
- return acpi_get_pci_rootbridge_handle(pci_domain_nr(pbus),
- pbus->number);
+ struct device *dev;
+
+ if (pci_is_root_bus(pbus))
+ dev = pbus->bridge;
+ else
+ dev = &pbus->self->dev;
+
+ return DEVICE_ACPI_HANDLE(dev);
}
#endif
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 5faa8310eec9..be1de01de1c4 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -254,10 +254,10 @@ struct pci_dev {
u8 revision; /* PCI revision, low byte of class word */
u8 hdr_type; /* PCI header type (`multi' flag masked out) */
u8 pcie_cap; /* PCI-E capability offset */
- u8 pcie_type:4; /* PCI-E device/port type */
u8 pcie_mpss:3; /* PCI-E Max Payload Size Supported */
u8 rom_base_reg; /* which config register controls the ROM */
u8 pin; /* which interrupt pin this device uses */
+ u16 pcie_flags_reg; /* cached PCI-E Capabilities Register */
struct pci_driver *driver; /* which driver has allocated this device */
u64 dma_mask; /* Mask of the bits of bus address this
@@ -369,7 +369,6 @@ static inline struct pci_dev *pci_physfn(struct pci_dev *dev)
extern struct pci_dev *alloc_pci_dev(void);
-#define pci_dev_b(n) list_entry(n, struct pci_dev, bus_list)
#define to_pci_dev(n) container_of(n, struct pci_dev, dev)
#define for_each_pci_dev(d) while ((d = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, d)) != NULL)
@@ -596,7 +595,7 @@ struct pci_driver {
int (*resume_early) (struct pci_dev *dev);
int (*resume) (struct pci_dev *dev); /* Device woken up */
void (*shutdown) (struct pci_dev *dev);
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
struct device_driver driver;
struct pci_dynids dynids;
};
@@ -734,9 +733,7 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp);
extern struct pci_dev *pci_dev_get(struct pci_dev *dev);
extern void pci_dev_put(struct pci_dev *dev);
extern void pci_remove_bus(struct pci_bus *b);
-extern void __pci_remove_bus_device(struct pci_dev *dev);
extern void pci_stop_and_remove_bus_device(struct pci_dev *dev);
-extern void pci_stop_bus_device(struct pci_dev *dev);
void pci_setup_cardbus(struct pci_bus *bus);
extern void pci_sort_breadthfirst(void);
#define dev_is_pci(d) ((d)->bus == &pci_bus_type)
@@ -755,6 +752,7 @@ enum pci_lost_interrupt_reason pci_lost_interrupt(struct pci_dev *dev);
int pci_find_capability(struct pci_dev *dev, int cap);
int pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap);
int pci_find_ext_capability(struct pci_dev *dev, int cap);
+int pci_find_next_ext_capability(struct pci_dev *dev, int pos, int cap);
int pci_find_ht_capability(struct pci_dev *dev, int ht_cap);
int pci_find_next_ht_capability(struct pci_dev *dev, int pos, int ht_cap);
struct pci_bus *pci_find_next_bus(const struct pci_bus *from);
@@ -816,6 +814,39 @@ static inline int pci_write_config_dword(const struct pci_dev *dev, int where,
return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val);
}
+int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val);
+int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val);
+int pcie_capability_write_word(struct pci_dev *dev, int pos, u16 val);
+int pcie_capability_write_dword(struct pci_dev *dev, int pos, u32 val);
+int pcie_capability_clear_and_set_word(struct pci_dev *dev, int pos,
+ u16 clear, u16 set);
+int pcie_capability_clear_and_set_dword(struct pci_dev *dev, int pos,
+ u32 clear, u32 set);
+
+static inline int pcie_capability_set_word(struct pci_dev *dev, int pos,
+ u16 set)
+{
+ return pcie_capability_clear_and_set_word(dev, pos, 0, set);
+}
+
+static inline int pcie_capability_set_dword(struct pci_dev *dev, int pos,
+ u32 set)
+{
+ return pcie_capability_clear_and_set_dword(dev, pos, 0, set);
+}
+
+static inline int pcie_capability_clear_word(struct pci_dev *dev, int pos,
+ u16 clear)
+{
+ return pcie_capability_clear_and_set_word(dev, pos, clear, 0);
+}
+
+static inline int pcie_capability_clear_dword(struct pci_dev *dev, int pos,
+ u32 clear)
+{
+ return pcie_capability_clear_and_set_dword(dev, pos, clear, 0);
+}
+
/* user-space driven config access */
int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val);
int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val);
@@ -1013,7 +1044,6 @@ void pci_unregister_driver(struct pci_driver *dev);
module_driver(__pci_driver, pci_register_driver, \
pci_unregister_driver)
-void pci_stop_and_remove_behind_bridge(struct pci_dev *dev);
struct pci_driver *pci_dev_driver(const struct pci_dev *dev);
int pci_add_dynid(struct pci_driver *drv,
unsigned int vendor, unsigned int device,
@@ -1031,6 +1061,8 @@ int pci_cfg_space_size_ext(struct pci_dev *dev);
int pci_cfg_space_size(struct pci_dev *dev);
unsigned char pci_bus_max_busnr(struct pci_bus *bus);
void pci_setup_bridge(struct pci_bus *bus);
+resource_size_t pcibios_window_alignment(struct pci_bus *bus,
+ unsigned long type);
#define PCI_VGA_STATE_CHANGE_BRIDGE (1 << 0)
#define PCI_VGA_STATE_CHANGE_DECODES (1 << 1)
@@ -1472,7 +1504,7 @@ enum pci_fixup_pass {
/* Anonymous variables would be nice... */
#define DECLARE_PCI_FIXUP_SECTION(section, name, vendor, device, class, \
class_shift, hook) \
- static const struct pci_fixup const __pci_fixup_##name __used \
+ static const struct pci_fixup __pci_fixup_##name __used \
__attribute__((__section__(#section), aligned((sizeof(void *))))) \
= { vendor, device, class, class_shift, hook };
@@ -1650,6 +1682,15 @@ static inline bool pci_is_pcie(struct pci_dev *dev)
return !!pci_pcie_cap(dev);
}
+/**
+ * pci_pcie_type - get the PCIe device/port type
+ * @dev: PCI device
+ */
+static inline int pci_pcie_type(const struct pci_dev *dev)
+{
+ return (dev->pcie_flags_reg & PCI_EXP_FLAGS_TYPE) >> 4;
+}
+
void pci_request_acs(void);
bool pci_acs_enabled(struct pci_dev *pdev, u16 acs_flags);
bool pci_acs_path_enabled(struct pci_dev *start,
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 6b4565c440c8..8d3c42719387 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -1847,7 +1847,6 @@
#define PCI_DEVICE_ID_SIIG_8S_20x_650 0x2081
#define PCI_DEVICE_ID_SIIG_8S_20x_850 0x2082
#define PCI_SUBDEVICE_ID_SIIG_QUARTET_SERIAL 0x2050
-#define PCI_SUBDEVICE_ID_SIIG_DUAL_SERIAL 0x2530
#define PCI_VENDOR_ID_RADISYS 0x1331
diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h
index 7fb75b143755..20ae747ddf34 100644
--- a/include/linux/pci_regs.h
+++ b/include/linux/pci_regs.h
@@ -549,6 +549,7 @@
#define PCI_EXP_LNKCAP2_SLS_8_0GB 0x04 /* Current Link Speed 8.0GT/s */
#define PCI_EXP_LNKCAP2_CROSSLINK 0x100 /* Crosslink supported */
#define PCI_EXP_LNKCTL2 48 /* Link Control 2 */
+#define PCI_EXP_LNKSTA2 50 /* Link Status 2 */
#define PCI_EXP_SLTCTL2 56 /* Slot Control 2 */
/* Extended Capabilities (PCI-X 2.0 and Express) */
@@ -677,6 +678,12 @@
#define PCI_PWR_CAP_BUDGET(x) ((x) & 1) /* Included in system budget */
#define PCI_EXT_CAP_PWR_SIZEOF 16
+/* Vendor-Specific (VSEC, PCI_EXT_CAP_ID_VNDR) */
+#define PCI_VNDR_HEADER 4 /* Vendor-Specific Header */
+#define PCI_VNDR_HEADER_ID(x) ((x) & 0xffff)
+#define PCI_VNDR_HEADER_REV(x) (((x) >> 16) & 0xf)
+#define PCI_VNDR_HEADER_LEN(x) (((x) >> 20) & 0xfff)
+
/*
* Hypertransport sub capability types
*
diff --git a/include/linux/pcieport_if.h b/include/linux/pcieport_if.h
index 6775532b92a9..e6f91b1406d8 100644
--- a/include/linux/pcieport_if.h
+++ b/include/linux/pcieport_if.h
@@ -49,7 +49,7 @@ struct pcie_port_service_driver {
int (*resume) (struct pcie_device *dev);
/* Service Error Recovery Handler */
- struct pci_error_handlers *err_handler;
+ const struct pci_error_handlers *err_handler;
/* Link Reset Capability - AER service driver specific */
pci_ers_result_t (*reset_link) (struct pci_dev *dev);
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 33ed9d605f91..599afc4bb67e 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -130,8 +130,10 @@ enum perf_event_sample_format {
PERF_SAMPLE_STREAM_ID = 1U << 9,
PERF_SAMPLE_RAW = 1U << 10,
PERF_SAMPLE_BRANCH_STACK = 1U << 11,
+ PERF_SAMPLE_REGS_USER = 1U << 12,
+ PERF_SAMPLE_STACK_USER = 1U << 13,
- PERF_SAMPLE_MAX = 1U << 12, /* non-ABI */
+ PERF_SAMPLE_MAX = 1U << 14, /* non-ABI */
};
/*
@@ -163,6 +165,15 @@ enum perf_branch_sample_type {
PERF_SAMPLE_BRANCH_HV)
/*
+ * Values to determine ABI of the registers dump.
+ */
+enum perf_sample_regs_abi {
+ PERF_SAMPLE_REGS_ABI_NONE = 0,
+ PERF_SAMPLE_REGS_ABI_32 = 1,
+ PERF_SAMPLE_REGS_ABI_64 = 2,
+};
+
+/*
* The format of the data returned by read() on a perf event fd,
* as specified by attr.read_format:
*
@@ -194,6 +205,8 @@ enum perf_event_read_format {
#define PERF_ATTR_SIZE_VER0 64 /* sizeof first published struct */
#define PERF_ATTR_SIZE_VER1 72 /* add: config2 */
#define PERF_ATTR_SIZE_VER2 80 /* add: branch_sample_type */
+#define PERF_ATTR_SIZE_VER3 96 /* add: sample_regs_user */
+ /* add: sample_stack_user */
/*
* Hardware event_id to monitor via a performance monitoring event:
@@ -255,7 +268,10 @@ struct perf_event_attr {
exclude_host : 1, /* don't count in host */
exclude_guest : 1, /* don't count in guest */
- __reserved_1 : 43;
+ exclude_callchain_kernel : 1, /* exclude kernel callchains */
+ exclude_callchain_user : 1, /* exclude user callchains */
+
+ __reserved_1 : 41;
union {
__u32 wakeup_events; /* wakeup every n events */
@@ -271,9 +287,25 @@ struct perf_event_attr {
__u64 bp_len;
__u64 config2; /* extension of config1 */
};
- __u64 branch_sample_type; /* enum branch_sample_type */
+ __u64 branch_sample_type; /* enum perf_branch_sample_type */
+
+ /*
+ * Defines set of user regs to dump on samples.
+ * See asm/perf_regs.h for details.
+ */
+ __u64 sample_regs_user;
+
+ /*
+ * Defines size of the user stack to dump on samples.
+ */
+ __u32 sample_stack_user;
+
+ /* Align to u64. */
+ __u32 __reserved_2;
};
+#define perf_flags(attr) (*(&(attr)->read_format + 1))
+
/*
* Ioctls that can be done on a perf event fd:
*/
@@ -548,6 +580,13 @@ enum perf_event_type {
* char data[size];}&& PERF_SAMPLE_RAW
*
* { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+ *
+ * { u64 abi; # enum perf_sample_regs_abi
+ * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER
+ *
+ * { u64 size;
+ * char data[size];
+ * u64 dyn_size; } && PERF_SAMPLE_STACK_USER
* };
*/
PERF_RECORD_SAMPLE = 9,
@@ -609,6 +648,7 @@ struct perf_guest_info_callbacks {
#include <linux/static_key.h>
#include <linux/atomic.h>
#include <linux/sysfs.h>
+#include <linux/perf_regs.h>
#include <asm/local.h>
struct perf_callchain_entry {
@@ -654,6 +694,11 @@ struct perf_branch_stack {
struct perf_branch_entry entries[0];
};
+struct perf_regs_user {
+ __u64 abi;
+ struct pt_regs *regs;
+};
+
struct task_struct;
/*
@@ -1133,6 +1178,8 @@ struct perf_sample_data {
struct perf_callchain_entry *callchain;
struct perf_raw_record *raw;
struct perf_branch_stack *br_stack;
+ struct perf_regs_user regs_user;
+ u64 stack_user_size;
};
static inline void perf_sample_data_init(struct perf_sample_data *data,
@@ -1142,7 +1189,10 @@ static inline void perf_sample_data_init(struct perf_sample_data *data,
data->addr = addr;
data->raw = NULL;
data->br_stack = NULL;
- data->period = period;
+ data->period = period;
+ data->regs_user.abi = PERF_SAMPLE_REGS_ABI_NONE;
+ data->regs_user.regs = NULL;
+ data->stack_user_size = 0;
}
extern void perf_output_sample(struct perf_output_handle *handle,
@@ -1290,8 +1340,10 @@ static inline bool has_branch_stack(struct perf_event *event)
extern int perf_output_begin(struct perf_output_handle *handle,
struct perf_event *event, unsigned int size);
extern void perf_output_end(struct perf_output_handle *handle);
-extern void perf_output_copy(struct perf_output_handle *handle,
+extern unsigned int perf_output_copy(struct perf_output_handle *handle,
const void *buf, unsigned int len);
+extern unsigned int perf_output_skip(struct perf_output_handle *handle,
+ unsigned int len);
extern int perf_swevent_get_recursion_context(void);
extern void perf_swevent_put_recursion_context(int rctx);
extern void perf_event_enable(struct perf_event *event);
diff --git a/include/linux/perf_regs.h b/include/linux/perf_regs.h
new file mode 100644
index 000000000000..3c73d5fe18be
--- /dev/null
+++ b/include/linux/perf_regs.h
@@ -0,0 +1,25 @@
+#ifndef _LINUX_PERF_REGS_H
+#define _LINUX_PERF_REGS_H
+
+#ifdef CONFIG_HAVE_PERF_REGS
+#include <asm/perf_regs.h>
+u64 perf_reg_value(struct pt_regs *regs, int idx);
+int perf_reg_validate(u64 mask);
+u64 perf_reg_abi(struct task_struct *task);
+#else
+static inline u64 perf_reg_value(struct pt_regs *regs, int idx)
+{
+ return 0;
+}
+
+static inline int perf_reg_validate(u64 mask)
+{
+ return mask ? -ENOSYS : 0;
+}
+
+static inline u64 perf_reg_abi(struct task_struct *task)
+{
+ return PERF_SAMPLE_REGS_ABI_NONE;
+}
+#endif /* CONFIG_HAVE_PERF_REGS */
+#endif /* _LINUX_PERF_REGS_H */
diff --git a/include/linux/platform_data/ad5755.h b/include/linux/platform_data/ad5755.h
new file mode 100644
index 000000000000..a5a1cb751874
--- /dev/null
+++ b/include/linux/platform_data/ad5755.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2012 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2.
+ */
+#ifndef __LINUX_PLATFORM_DATA_AD5755_H__
+#define __LINUX_PLATFORM_DATA_AD5755_H__
+
+enum ad5755_mode {
+ AD5755_MODE_VOLTAGE_0V_5V = 0,
+ AD5755_MODE_VOLTAGE_0V_10V = 1,
+ AD5755_MODE_VOLTAGE_PLUSMINUS_5V = 2,
+ AD5755_MODE_VOLTAGE_PLUSMINUS_10V = 3,
+ AD5755_MODE_CURRENT_4mA_20mA = 4,
+ AD5755_MODE_CURRENT_0mA_20mA = 5,
+ AD5755_MODE_CURRENT_0mA_24mA = 6,
+};
+
+enum ad5755_dc_dc_phase {
+ AD5755_DC_DC_PHASE_ALL_SAME_EDGE = 0,
+ AD5755_DC_DC_PHASE_A_B_SAME_EDGE_C_D_OPP_EDGE = 1,
+ AD5755_DC_DC_PHASE_A_C_SAME_EDGE_B_D_OPP_EDGE = 2,
+ AD5755_DC_DC_PHASE_90_DEGREE = 3,
+};
+
+enum ad5755_dc_dc_freq {
+ AD5755_DC_DC_FREQ_250kHZ = 0,
+ AD5755_DC_DC_FREQ_410kHZ = 1,
+ AD5755_DC_DC_FREQ_650kHZ = 2,
+};
+
+enum ad5755_dc_dc_maxv {
+ AD5755_DC_DC_MAXV_23V = 0,
+ AD5755_DC_DC_MAXV_24V5 = 1,
+ AD5755_DC_DC_MAXV_27V = 2,
+ AD5755_DC_DC_MAXV_29V5 = 3,
+};
+
+enum ad5755_slew_rate {
+ AD5755_SLEW_RATE_64k = 0,
+ AD5755_SLEW_RATE_32k = 1,
+ AD5755_SLEW_RATE_16k = 2,
+ AD5755_SLEW_RATE_8k = 3,
+ AD5755_SLEW_RATE_4k = 4,
+ AD5755_SLEW_RATE_2k = 5,
+ AD5755_SLEW_RATE_1k = 6,
+ AD5755_SLEW_RATE_500 = 7,
+ AD5755_SLEW_RATE_250 = 8,
+ AD5755_SLEW_RATE_125 = 9,
+ AD5755_SLEW_RATE_64 = 10,
+ AD5755_SLEW_RATE_32 = 11,
+ AD5755_SLEW_RATE_16 = 12,
+ AD5755_SLEW_RATE_8 = 13,
+ AD5755_SLEW_RATE_4 = 14,
+ AD5755_SLEW_RATE_0_5 = 15,
+};
+
+enum ad5755_slew_step_size {
+ AD5755_SLEW_STEP_SIZE_1 = 0,
+ AD5755_SLEW_STEP_SIZE_2 = 1,
+ AD5755_SLEW_STEP_SIZE_4 = 2,
+ AD5755_SLEW_STEP_SIZE_8 = 3,
+ AD5755_SLEW_STEP_SIZE_16 = 4,
+ AD5755_SLEW_STEP_SIZE_32 = 5,
+ AD5755_SLEW_STEP_SIZE_64 = 6,
+ AD5755_SLEW_STEP_SIZE_128 = 7,
+ AD5755_SLEW_STEP_SIZE_256 = 8,
+};
+
+/**
+ * struct ad5755_platform_data - AD5755 DAC driver platform data
+ * @ext_dc_dc_compenstation_resistor: Whether an external DC-DC converter
+ * compensation register is used.
+ * @dc_dc_phase: DC-DC converter phase.
+ * @dc_dc_freq: DC-DC converter frequency.
+ * @dc_dc_maxv: DC-DC maximum allowed boost voltage.
+ * @dac.mode: The mode to be used for the DAC output.
+ * @dac.ext_current_sense_resistor: Whether an external current sense resistor
+ * is used.
+ * @dac.enable_voltage_overrange: Whether to enable 20% voltage output overrange.
+ * @dac.slew.enable: Whether to enable digital slew.
+ * @dac.slew.rate: Slew rate of the digital slew.
+ * @dac.slew.step_size: Slew step size of the digital slew.
+ **/
+struct ad5755_platform_data {
+ bool ext_dc_dc_compenstation_resistor;
+ enum ad5755_dc_dc_phase dc_dc_phase;
+ enum ad5755_dc_dc_freq dc_dc_freq;
+ enum ad5755_dc_dc_maxv dc_dc_maxv;
+
+ struct {
+ enum ad5755_mode mode;
+ bool ext_current_sense_resistor;
+ bool enable_voltage_overrange;
+ struct {
+ bool enable;
+ enum ad5755_slew_rate rate;
+ enum ad5755_slew_step_size step_size;
+ } slew;
+ } dac[4];
+};
+
+#endif
diff --git a/include/linux/platform_data/ad7791.h b/include/linux/platform_data/ad7791.h
new file mode 100644
index 000000000000..f9e4db1b82ae
--- /dev/null
+++ b/include/linux/platform_data/ad7791.h
@@ -0,0 +1,17 @@
+#ifndef __LINUX_PLATFORM_DATA_AD7791__
+#define __LINUX_PLATFORM_DATA_AD7791__
+
+/**
+ * struct ad7791_platform_data - AD7791 device platform data
+ * @buffered: If set to true configure the device for buffered input mode.
+ * @burnout_current: If set to true the 100mA burnout current is enabled.
+ * @unipolar: If set to true sample in unipolar mode, if set to false sample in
+ * bipolar mode.
+ */
+struct ad7791_platform_data {
+ bool buffered;
+ bool burnout_current;
+ bool unipolar;
+};
+
+#endif
diff --git a/include/linux/platform_data/max197.h b/include/linux/platform_data/max197.h
new file mode 100644
index 000000000000..e2a41dd7690c
--- /dev/null
+++ b/include/linux/platform_data/max197.h
@@ -0,0 +1,21 @@
+/*
+ * Maxim MAX197 A/D Converter Driver
+ *
+ * Copyright (c) 2012 Savoir-faire Linux Inc.
+ * Vivien Didelot <vivien.didelot@savoirfairelinux.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.
+ *
+ * For further information, see the Documentation/hwmon/max197 file.
+ */
+
+/**
+ * struct max197_platform_data - MAX197 connectivity info
+ * @convert: Function used to start a conversion with control byte ctrl.
+ * It must return the raw data, or a negative error code.
+ */
+struct max197_platform_data {
+ int (*convert)(u8 ctrl);
+};
diff --git a/include/linux/sht15.h b/include/linux/platform_data/sht15.h
index f85c7c523da0..33e0fd27225e 100644
--- a/include/linux/sht15.h
+++ b/include/linux/platform_data/sht15.h
@@ -31,4 +31,3 @@ struct sht15_platform_data {
bool no_otp_reload;
bool low_resolution;
};
-
diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h
index 60e9994ef405..5711e9525a2a 100644
--- a/include/linux/platform_device.h
+++ b/include/linux/platform_device.h
@@ -14,11 +14,15 @@
#include <linux/device.h>
#include <linux/mod_devicetable.h>
+#define PLATFORM_DEVID_NONE (-1)
+#define PLATFORM_DEVID_AUTO (-2)
+
struct mfd_cell;
struct platform_device {
const char * name;
int id;
+ bool id_auto;
struct device dev;
u32 num_resources;
struct resource * resource;
diff --git a/include/linux/pm.h b/include/linux/pm.h
index f067e60a3832..88f034a23f2c 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -638,6 +638,7 @@ extern void __suspend_report_result(const char *function, void *fn, int ret);
} while (0)
extern int device_pm_wait_for_dev(struct device *sub, struct device *dev);
+extern void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *));
extern int pm_generic_prepare(struct device *dev);
extern int pm_generic_suspend_late(struct device *dev);
@@ -677,6 +678,10 @@ static inline int device_pm_wait_for_dev(struct device *a, struct device *b)
return 0;
}
+static inline void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *))
+{
+}
+
#define pm_generic_prepare NULL
#define pm_generic_suspend NULL
#define pm_generic_resume NULL
diff --git a/include/linux/power/generic-adc-battery.h b/include/linux/power/generic-adc-battery.h
new file mode 100644
index 000000000000..b1ebe08533b6
--- /dev/null
+++ b/include/linux/power/generic-adc-battery.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2012, Anish Kumar <anish198519851985@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 GENERIC_ADC_BATTERY_H
+#define GENERIC_ADC_BATTERY_H
+
+/**
+ * struct gab_platform_data - platform_data for generic adc iio battery driver.
+ * @battery_info: recommended structure to specify static power supply
+ * parameters
+ * @cal_charge: calculate charge level.
+ * @gpio_charge_finished: gpio for the charger.
+ * @gpio_inverted: Should be 1 if the GPIO is active low otherwise 0
+ * @jitter_delay: delay required after the interrupt to check battery
+ * status.Default set is 10ms.
+ */
+struct gab_platform_data {
+ struct power_supply_info battery_info;
+ int (*cal_charge)(long value);
+ int gpio_charge_finished;
+ bool gpio_inverted;
+ int jitter_delay;
+};
+
+#endif /* GENERIC_ADC_BATTERY_H */
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index 115ead2b5155..7c968e4f929e 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -191,6 +191,21 @@ extern void rcu_idle_enter(void);
extern void rcu_idle_exit(void);
extern void rcu_irq_enter(void);
extern void rcu_irq_exit(void);
+
+#ifdef CONFIG_RCU_USER_QS
+extern void rcu_user_enter(void);
+extern void rcu_user_exit(void);
+extern void rcu_user_enter_after_irq(void);
+extern void rcu_user_exit_after_irq(void);
+extern void rcu_user_hooks_switch(struct task_struct *prev,
+ struct task_struct *next);
+#else
+static inline void rcu_user_enter(void) { }
+static inline void rcu_user_exit(void) { }
+static inline void rcu_user_enter_after_irq(void) { }
+static inline void rcu_user_exit_after_irq(void) { }
+#endif /* CONFIG_RCU_USER_QS */
+
extern void exit_rcu(void);
/**
@@ -210,14 +225,12 @@ extern void exit_rcu(void);
* to nest RCU_NONIDLE() wrappers, but the nesting level is currently
* quite limited. If deeper nesting is required, it will be necessary
* to adjust DYNTICK_TASK_NESTING_VALUE accordingly.
- *
- * This macro may be used from process-level code only.
*/
#define RCU_NONIDLE(a) \
do { \
- rcu_idle_exit(); \
+ rcu_irq_enter(); \
do { a; } while (0); \
- rcu_idle_enter(); \
+ rcu_irq_exit(); \
} while (0)
/*
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 7f7e00df3adf..e3bcc3f4dcb8 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -285,6 +285,7 @@ struct regmap_irq {
* @ack_base: Base ack address. If zero then the chip is clear on read.
* @wake_base: Base address for wake enables. If zero unsupported.
* @irq_reg_stride: Stride to use for chips where registers are not contiguous.
+ * @runtime_pm: Hold a runtime PM lock on the device when accessing it.
*
* @num_regs: Number of registers in each control bank.
* @irqs: Descriptors for individual IRQs. Interrupt numbers are
@@ -299,6 +300,8 @@ struct regmap_irq_chip {
unsigned int ack_base;
unsigned int wake_base;
unsigned int irq_reg_stride;
+ unsigned int mask_invert;
+ bool runtime_pm;
int num_regs;
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index da339fd8c755..c43cd3556b1f 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -177,6 +177,8 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode);
unsigned int regulator_get_mode(struct regulator *regulator);
int regulator_set_optimum_mode(struct regulator *regulator, int load_uA);
+int regulator_allow_bypass(struct regulator *regulator, bool allow);
+
/* regulator notifier block */
int regulator_register_notifier(struct regulator *regulator,
struct notifier_block *nb);
@@ -328,6 +330,12 @@ static inline int regulator_set_optimum_mode(struct regulator *regulator,
return REGULATOR_MODE_NORMAL;
}
+static inline int regulator_allow_bypass(struct regulator *regulator,
+ bool allow)
+{
+ return 0;
+}
+
static inline int regulator_register_notifier(struct regulator *regulator,
struct notifier_block *nb)
{
@@ -352,4 +360,11 @@ static inline void regulator_set_drvdata(struct regulator *regulator,
#endif
+static inline int regulator_set_voltage_tol(struct regulator *regulator,
+ int new_uV, int tol_uV)
+{
+ return regulator_set_voltage(regulator,
+ new_uV - tol_uV, new_uV + tol_uV);
+}
+
#endif
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index bac4c871f3bd..7932a3bf21bd 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -32,6 +32,8 @@ enum regulator_status {
REGULATOR_STATUS_NORMAL,
REGULATOR_STATUS_IDLE,
REGULATOR_STATUS_STANDBY,
+ /* The regulator is enabled but not regulating */
+ REGULATOR_STATUS_BYPASS,
/* in case that any other status doesn't apply */
REGULATOR_STATUS_UNDEFINED,
};
@@ -58,6 +60,7 @@ enum regulator_status {
* regulator_desc.n_voltages. Voltages may be reported in any order.
*
* @set_current_limit: Configure a limit for a current-limited regulator.
+ * The driver should select the current closest to max_uA.
* @get_current_limit: Get the configured limit for a current-limited regulator.
*
* @set_mode: Set the configured operating mode for the regulator.
@@ -67,6 +70,9 @@ enum regulator_status {
* @get_optimum_mode: Get the most efficient operating mode for the regulator
* when running with the specified parameters.
*
+ * @set_bypass: Set the regulator in bypass mode.
+ * @get_bypass: Get the regulator bypass mode state.
+ *
* @enable_time: Time taken for the regulator voltage output voltage to
* stabilise after being enabled, in microseconds.
* @set_ramp_delay: Set the ramp delay for the regulator. The driver should
@@ -133,6 +139,10 @@ struct regulator_ops {
unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV,
int output_uV, int load_uA);
+ /* control and report on bypass mode */
+ int (*set_bypass)(struct regulator_dev *dev, bool enable);
+ int (*get_bypass)(struct regulator_dev *dev, bool *enable);
+
/* the operations below are for configuration of regulator state when
* its parent PMIC enters a global STANDBY/HIBERNATE state */
@@ -205,6 +215,8 @@ struct regulator_desc {
unsigned int vsel_mask;
unsigned int enable_reg;
unsigned int enable_mask;
+ unsigned int bypass_reg;
+ unsigned int bypass_mask;
unsigned int enable_time;
};
@@ -221,7 +233,8 @@ struct regulator_desc {
* @driver_data: private regulator data
* @of_node: OpenFirmware node to parse for device tree bindings (may be
* NULL).
- * @regmap: regmap to use for core regmap helpers
+ * @regmap: regmap to use for core regmap helpers if dev_get_regulator() is
+ * insufficient.
* @ena_gpio: GPIO controlling regulator enable.
* @ena_gpio_invert: Sense for GPIO enable control.
* @ena_gpio_flags: Flags to use when calling gpio_request_one()
@@ -253,6 +266,7 @@ struct regulator_dev {
int exclusive;
u32 use_count;
u32 open_count;
+ u32 bypass_count;
/* lists we belong to */
struct list_head list; /* list of all regulators */
@@ -310,6 +324,8 @@ int regulator_disable_regmap(struct regulator_dev *rdev);
int regulator_set_voltage_time_sel(struct regulator_dev *rdev,
unsigned int old_selector,
unsigned int new_selector);
+int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable);
+int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable);
void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data);
diff --git a/include/linux/regulator/fan53555.h b/include/linux/regulator/fan53555.h
new file mode 100644
index 000000000000..5c45c85d52ca
--- /dev/null
+++ b/include/linux/regulator/fan53555.h
@@ -0,0 +1,60 @@
+/*
+ * fan53555.h - Fairchild Regulator FAN53555 Driver
+ *
+ * Copyright (C) 2012 Marvell Technology Ltd.
+ * Yunfan Zhang <yfzhang@marvell.com>
+ *
+ * This package 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 __FAN53555_H__
+
+/* VSEL ID */
+enum {
+ FAN53555_VSEL_ID_0 = 0,
+ FAN53555_VSEL_ID_1,
+};
+
+/* Transition slew rate limiting from a low to high voltage.
+ * -----------------------
+ * Bin |Slew Rate(mV/uS)
+ * ------|----------------
+ * 000 | 64.00
+ * ------|----------------
+ * 001 | 32.00
+ * ------|----------------
+ * 010 | 16.00
+ * ------|----------------
+ * 011 | 8.00
+ * ------|----------------
+ * 100 | 4.00
+ * ------|----------------
+ * 101 | 2.00
+ * ------|----------------
+ * 110 | 1.00
+ * ------|----------------
+ * 111 | 0.50
+ * -----------------------
+ */
+enum {
+ FAN53555_SLEW_RATE_64MV = 0,
+ FAN53555_SLEW_RATE_32MV,
+ FAN53555_SLEW_RATE_16MV,
+ FAN53555_SLEW_RATE_8MV,
+ FAN53555_SLEW_RATE_4MV,
+ FAN53555_SLEW_RATE_2MV,
+ FAN53555_SLEW_RATE_1MV,
+ FAN53555_SLEW_RATE_0_5MV,
+};
+
+struct fan53555_platform_data {
+ struct regulator_init_data *regulator;
+ unsigned int slew_rate;
+ /* Sleep VSEL ID */
+ unsigned int sleep_vsel_id;
+};
+
+#endif /* __FAN53555_H__ */
diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h
index 40dd0a394cfa..36adbc82de6a 100644
--- a/include/linux/regulator/machine.h
+++ b/include/linux/regulator/machine.h
@@ -32,6 +32,7 @@ struct regulator;
* board/machine.
* STATUS: Regulator can be enabled and disabled.
* DRMS: Dynamic Regulator Mode Switching is enabled for this regulator.
+ * BYPASS: Regulator can be put into bypass mode
*/
#define REGULATOR_CHANGE_VOLTAGE 0x1
@@ -39,6 +40,7 @@ struct regulator;
#define REGULATOR_CHANGE_MODE 0x4
#define REGULATOR_CHANGE_STATUS 0x8
#define REGULATOR_CHANGE_DRMS 0x10
+#define REGULATOR_CHANGE_BYPASS 0x20
/**
* struct regulator_state - regulator state during low power system states
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 23bddac4bad8..765dffbb085e 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -273,11 +273,11 @@ extern void init_idle_bootup_task(struct task_struct *idle);
extern int runqueue_is_locked(int cpu);
#if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ)
-extern void select_nohz_load_balancer(int stop_tick);
+extern void nohz_balance_enter_idle(int cpu);
extern void set_cpu_sd_state_idle(void);
extern int get_nohz_timer_target(void);
#else
-static inline void select_nohz_load_balancer(int stop_tick) { }
+static inline void nohz_balance_enter_idle(int cpu) { }
static inline void set_cpu_sd_state_idle(void) { }
#endif
@@ -446,6 +446,9 @@ extern int get_dumpable(struct mm_struct *mm);
#define MMF_VM_HUGEPAGE 17 /* set when VM_HUGEPAGE is set on vma */
#define MMF_EXE_FILE_CHANGED 18 /* see prctl_set_mm_exe_file() */
+#define MMF_HAS_UPROBES 19 /* has uprobes */
+#define MMF_RECALC_UPROBES 20 /* MMF_HAS_UPROBES can be wrong */
+
#define MMF_INIT_MASK (MMF_DUMPABLE_MASK | MMF_DUMP_FILTER_MASK)
struct sighand_struct {
@@ -678,11 +681,6 @@ struct signal_struct {
* (notably. ptrace) */
};
-/* Context switch must be unlocked if interrupts are to be enabled */
-#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
-# define __ARCH_WANT_UNLOCKED_CTXSW
-#endif
-
/*
* Bits in flags field of signal_struct.
*/
@@ -860,7 +858,6 @@ enum cpu_idle_type {
#define SD_BALANCE_FORK 0x0008 /* Balance on fork, clone */
#define SD_BALANCE_WAKE 0x0010 /* Balance on wakeup */
#define SD_WAKE_AFFINE 0x0020 /* Wake task to waking CPU */
-#define SD_PREFER_LOCAL 0x0040 /* Prefer to keep tasks local to this domain */
#define SD_SHARE_CPUPOWER 0x0080 /* Domain members share cpu power */
#define SD_SHARE_PKG_RESOURCES 0x0200 /* Domain members share cpu pkg resources */
#define SD_SERIALIZE 0x0400 /* Only a single load balancing instance */
@@ -1885,6 +1882,14 @@ static inline void rcu_copy_process(struct task_struct *p)
#endif
+static inline void rcu_switch(struct task_struct *prev,
+ struct task_struct *next)
+{
+#ifdef CONFIG_RCU_USER_QS
+ rcu_user_hooks_switch(prev, next);
+#endif
+}
+
static inline void tsk_restore_flags(struct task_struct *task,
unsigned long orig_flags, unsigned long flags)
{
diff --git a/include/linux/screen_info.h b/include/linux/screen_info.h
index 899fbb487c94..fb3c5a8fef3d 100644
--- a/include/linux/screen_info.h
+++ b/include/linux/screen_info.h
@@ -68,6 +68,8 @@ struct screen_info {
#define VIDEO_FLAGS_NOCURSOR (1 << 0) /* The video mode has no cursor set */
+#define VIDEO_CAPABILITY_SKIP_QUIRKS (1 << 0)
+
#ifdef __KERNEL__
extern struct screen_info screen_info;
diff --git a/include/linux/security.h b/include/linux/security.h
index 3dea6a9d568f..d143b8e01954 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -118,6 +118,7 @@ void reset_security_ops(void);
extern unsigned long mmap_min_addr;
extern unsigned long dac_mmap_min_addr;
#else
+#define mmap_min_addr 0UL
#define dac_mmap_min_addr 0UL
#endif
diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
index 7cf0b68bbe9e..f9b22ec7a9f3 100644
--- a/include/linux/serial_core.h
+++ b/include/linux/serial_core.h
@@ -48,7 +48,8 @@
#define PORT_TEGRA 20 /* NVIDIA Tegra internal UART */
#define PORT_XR17D15X 21 /* Exar XR17D15x UART */
#define PORT_LPC3220 22 /* NXP LPC32xx SoC "Standard" UART */
-#define PORT_MAX_8250 22 /* max port ID */
+#define PORT_8250_CIR 23 /* CIR infrared port, has its own driver */
+#define PORT_MAX_8250 23 /* max port ID */
/*
* ARM specific type numbers. These are not currently guaranteed
@@ -274,6 +275,7 @@ struct uart_ops {
int (*verify_port)(struct uart_port *, struct serial_struct *);
int (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLL
+ int (*poll_init)(struct uart_port *);
void (*poll_put_char)(struct uart_port *, unsigned char);
int (*poll_get_char)(struct uart_port *);
#endif
diff --git a/include/linux/smpboot.h b/include/linux/smpboot.h
new file mode 100644
index 000000000000..e0106d8581d3
--- /dev/null
+++ b/include/linux/smpboot.h
@@ -0,0 +1,43 @@
+#ifndef _LINUX_SMPBOOT_H
+#define _LINUX_SMPBOOT_H
+
+#include <linux/types.h>
+
+struct task_struct;
+/* Cookie handed to the thread_fn*/
+struct smpboot_thread_data;
+
+/**
+ * struct smp_hotplug_thread - CPU hotplug related thread descriptor
+ * @store: Pointer to per cpu storage for the task pointers
+ * @list: List head for core management
+ * @thread_should_run: Check whether the thread should run or not. Called with
+ * preemption disabled.
+ * @thread_fn: The associated thread function
+ * @setup: Optional setup function, called when the thread gets
+ * operational the first time
+ * @cleanup: Optional cleanup function, called when the thread
+ * should stop (module exit)
+ * @park: Optional park function, called when the thread is
+ * parked (cpu offline)
+ * @unpark: Optional unpark function, called when the thread is
+ * unparked (cpu online)
+ * @thread_comm: The base name of the thread
+ */
+struct smp_hotplug_thread {
+ struct task_struct __percpu **store;
+ struct list_head list;
+ int (*thread_should_run)(unsigned int cpu);
+ void (*thread_fn)(unsigned int cpu);
+ void (*setup)(unsigned int cpu);
+ void (*cleanup)(unsigned int cpu, bool online);
+ void (*park)(unsigned int cpu);
+ void (*unpark)(unsigned int cpu);
+ const char *thread_comm;
+};
+
+int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread);
+void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread);
+int smpboot_thread_schedule(void);
+
+#endif
diff --git a/include/linux/task_work.h b/include/linux/task_work.h
index fb46b03b1852..ca5a1cf27dae 100644
--- a/include/linux/task_work.h
+++ b/include/linux/task_work.h
@@ -18,8 +18,7 @@ void task_work_run(void);
static inline void exit_task_work(struct task_struct *task)
{
- if (unlikely(task->task_works))
- task_work_run();
+ task_work_run();
}
#endif /* _LINUX_TASK_WORK_H */
diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h
index 3ca0269dd0b5..932b76392248 100644
--- a/include/linux/ti_wilink_st.h
+++ b/include/linux/ti_wilink_st.h
@@ -281,9 +281,10 @@ struct kim_data_s {
long st_kim_start(void *);
long st_kim_stop(void *);
-void st_kim_recv(void *, const unsigned char *, long count);
void st_kim_complete(void *);
void kim_st_list_protocols(struct st_data_s *, void *);
+void st_kim_recv(void *, const unsigned char *, long);
+
/*
* BTS headers
diff --git a/include/linux/timer.h b/include/linux/timer.h
index 6abd9138beda..8c5a197e1587 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -49,147 +49,112 @@ extern struct tvec_base boot_tvec_bases;
#endif
/*
- * Note that all tvec_bases are 2 byte aligned and lower bit of
- * base in timer_list is guaranteed to be zero. Use the LSB to
- * indicate whether the timer is deferrable.
+ * Note that all tvec_bases are at least 4 byte aligned and lower two bits
+ * of base in timer_list is guaranteed to be zero. Use them for flags.
*
* A deferrable timer will work normally when the system is busy, but
* will not cause a CPU to come out of idle just to service it; instead,
* the timer will be serviced when the CPU eventually wakes up with a
* subsequent non-deferrable timer.
+ *
+ * An irqsafe timer is executed with IRQ disabled and it's safe to wait for
+ * the completion of the running instance from IRQ handlers, for example,
+ * by calling del_timer_sync().
+ *
+ * Note: The irq disabled callback execution is a special case for
+ * workqueue locking issues. It's not meant for executing random crap
+ * with interrupts disabled. Abuse is monitored!
*/
-#define TBASE_DEFERRABLE_FLAG (0x1)
+#define TIMER_DEFERRABLE 0x1LU
+#define TIMER_IRQSAFE 0x2LU
-#define TIMER_INITIALIZER(_function, _expires, _data) { \
+#define TIMER_FLAG_MASK 0x3LU
+
+#define __TIMER_INITIALIZER(_function, _expires, _data, _flags) { \
.entry = { .prev = TIMER_ENTRY_STATIC }, \
.function = (_function), \
.expires = (_expires), \
.data = (_data), \
- .base = &boot_tvec_bases, \
+ .base = (void *)((unsigned long)&boot_tvec_bases + (_flags)), \
.slack = -1, \
__TIMER_LOCKDEP_MAP_INITIALIZER( \
__FILE__ ":" __stringify(__LINE__)) \
}
-#define TBASE_MAKE_DEFERRED(ptr) ((struct tvec_base *) \
- ((unsigned char *)(ptr) + TBASE_DEFERRABLE_FLAG))
+#define TIMER_INITIALIZER(_function, _expires, _data) \
+ __TIMER_INITIALIZER((_function), (_expires), (_data), 0)
-#define TIMER_DEFERRED_INITIALIZER(_function, _expires, _data) {\
- .entry = { .prev = TIMER_ENTRY_STATIC }, \
- .function = (_function), \
- .expires = (_expires), \
- .data = (_data), \
- .base = TBASE_MAKE_DEFERRED(&boot_tvec_bases), \
- __TIMER_LOCKDEP_MAP_INITIALIZER( \
- __FILE__ ":" __stringify(__LINE__)) \
- }
+#define TIMER_DEFERRED_INITIALIZER(_function, _expires, _data) \
+ __TIMER_INITIALIZER((_function), (_expires), (_data), TIMER_DEFERRABLE)
#define DEFINE_TIMER(_name, _function, _expires, _data) \
struct timer_list _name = \
TIMER_INITIALIZER(_function, _expires, _data)
-void init_timer_key(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key);
-void init_timer_deferrable_key(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key);
+void init_timer_key(struct timer_list *timer, unsigned int flags,
+ const char *name, struct lock_class_key *key);
+
+#ifdef CONFIG_DEBUG_OBJECTS_TIMERS
+extern void init_timer_on_stack_key(struct timer_list *timer,
+ unsigned int flags, const char *name,
+ struct lock_class_key *key);
+extern void destroy_timer_on_stack(struct timer_list *timer);
+#else
+static inline void destroy_timer_on_stack(struct timer_list *timer) { }
+static inline void init_timer_on_stack_key(struct timer_list *timer,
+ unsigned int flags, const char *name,
+ struct lock_class_key *key)
+{
+ init_timer_key(timer, flags, name, key);
+}
+#endif
#ifdef CONFIG_LOCKDEP
-#define init_timer(timer) \
+#define __init_timer(_timer, _flags) \
do { \
static struct lock_class_key __key; \
- init_timer_key((timer), #timer, &__key); \
+ init_timer_key((_timer), (_flags), #_timer, &__key); \
} while (0)
-#define init_timer_deferrable(timer) \
+#define __init_timer_on_stack(_timer, _flags) \
do { \
static struct lock_class_key __key; \
- init_timer_deferrable_key((timer), #timer, &__key); \
+ init_timer_on_stack_key((_timer), (_flags), #_timer, &__key); \
} while (0)
+#else
+#define __init_timer(_timer, _flags) \
+ init_timer_key((_timer), (_flags), NULL, NULL)
+#define __init_timer_on_stack(_timer, _flags) \
+ init_timer_on_stack_key((_timer), (_flags), NULL, NULL)
+#endif
+#define init_timer(timer) \
+ __init_timer((timer), 0)
+#define init_timer_deferrable(timer) \
+ __init_timer((timer), TIMER_DEFERRABLE)
#define init_timer_on_stack(timer) \
+ __init_timer_on_stack((timer), 0)
+
+#define __setup_timer(_timer, _fn, _data, _flags) \
do { \
- static struct lock_class_key __key; \
- init_timer_on_stack_key((timer), #timer, &__key); \
+ __init_timer((_timer), (_flags)); \
+ (_timer)->function = (_fn); \
+ (_timer)->data = (_data); \
} while (0)
-#define setup_timer(timer, fn, data) \
+#define __setup_timer_on_stack(_timer, _fn, _data, _flags) \
do { \
- static struct lock_class_key __key; \
- setup_timer_key((timer), #timer, &__key, (fn), (data));\
+ __init_timer_on_stack((_timer), (_flags)); \
+ (_timer)->function = (_fn); \
+ (_timer)->data = (_data); \
} while (0)
+#define setup_timer(timer, fn, data) \
+ __setup_timer((timer), (fn), (data), 0)
#define setup_timer_on_stack(timer, fn, data) \
- do { \
- static struct lock_class_key __key; \
- setup_timer_on_stack_key((timer), #timer, &__key, \
- (fn), (data)); \
- } while (0)
+ __setup_timer_on_stack((timer), (fn), (data), 0)
#define setup_deferrable_timer_on_stack(timer, fn, data) \
- do { \
- static struct lock_class_key __key; \
- setup_deferrable_timer_on_stack_key((timer), #timer, \
- &__key, (fn), \
- (data)); \
- } while (0)
-#else
-#define init_timer(timer)\
- init_timer_key((timer), NULL, NULL)
-#define init_timer_deferrable(timer)\
- init_timer_deferrable_key((timer), NULL, NULL)
-#define init_timer_on_stack(timer)\
- init_timer_on_stack_key((timer), NULL, NULL)
-#define setup_timer(timer, fn, data)\
- setup_timer_key((timer), NULL, NULL, (fn), (data))
-#define setup_timer_on_stack(timer, fn, data)\
- setup_timer_on_stack_key((timer), NULL, NULL, (fn), (data))
-#define setup_deferrable_timer_on_stack(timer, fn, data)\
- setup_deferrable_timer_on_stack_key((timer), NULL, NULL, (fn), (data))
-#endif
-
-#ifdef CONFIG_DEBUG_OBJECTS_TIMERS
-extern void init_timer_on_stack_key(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key);
-extern void destroy_timer_on_stack(struct timer_list *timer);
-#else
-static inline void destroy_timer_on_stack(struct timer_list *timer) { }
-static inline void init_timer_on_stack_key(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key)
-{
- init_timer_key(timer, name, key);
-}
-#endif
-
-static inline void setup_timer_key(struct timer_list * timer,
- const char *name,
- struct lock_class_key *key,
- void (*function)(unsigned long),
- unsigned long data)
-{
- timer->function = function;
- timer->data = data;
- init_timer_key(timer, name, key);
-}
-
-static inline void setup_timer_on_stack_key(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key,
- void (*function)(unsigned long),
- unsigned long data)
-{
- timer->function = function;
- timer->data = data;
- init_timer_on_stack_key(timer, name, key);
-}
-
-extern void setup_deferrable_timer_on_stack_key(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key,
- void (*function)(unsigned long),
- unsigned long data);
+ __setup_timer_on_stack((timer), (fn), (data), TIMER_DEFERRABLE)
/**
* timer_pending - is a timer pending?
diff --git a/include/linux/topology.h b/include/linux/topology.h
index fec12d667211..d3cf0d6e7712 100644
--- a/include/linux/topology.h
+++ b/include/linux/topology.h
@@ -129,7 +129,6 @@ int arch_update_cpu_topology(void);
| 1*SD_BALANCE_FORK \
| 0*SD_BALANCE_WAKE \
| 1*SD_WAKE_AFFINE \
- | 0*SD_PREFER_LOCAL \
| 0*SD_SHARE_CPUPOWER \
| 1*SD_SHARE_PKG_RESOURCES \
| 0*SD_SERIALIZE \
@@ -160,7 +159,6 @@ int arch_update_cpu_topology(void);
| 1*SD_BALANCE_FORK \
| 0*SD_BALANCE_WAKE \
| 1*SD_WAKE_AFFINE \
- | 0*SD_PREFER_LOCAL \
| 0*SD_SHARE_CPUPOWER \
| 0*SD_SHARE_PKG_RESOURCES \
| 0*SD_SERIALIZE \
diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h
index 802de56c41e8..2f322c38bd4d 100644
--- a/include/linux/tracepoint.h
+++ b/include/linux/tracepoint.h
@@ -136,6 +136,22 @@ static inline void tracepoint_synchronize_unregister(void)
postrcu; \
} while (0)
+#ifndef MODULE
+#define __DECLARE_TRACE_RCU(name, proto, args, cond, data_proto, data_args) \
+ static inline void trace_##name##_rcuidle(proto) \
+ { \
+ if (static_key_false(&__tracepoint_##name.key)) \
+ __DO_TRACE(&__tracepoint_##name, \
+ TP_PROTO(data_proto), \
+ TP_ARGS(data_args), \
+ TP_CONDITION(cond), \
+ rcu_idle_exit(), \
+ rcu_idle_enter()); \
+ }
+#else
+#define __DECLARE_TRACE_RCU(name, proto, args, cond, data_proto, data_args)
+#endif
+
/*
* Make sure the alignment of the structure in the __tracepoints section will
* not add unwanted padding between the beginning of the section and the
@@ -151,16 +167,8 @@ static inline void tracepoint_synchronize_unregister(void)
TP_ARGS(data_args), \
TP_CONDITION(cond),,); \
} \
- static inline void trace_##name##_rcuidle(proto) \
- { \
- if (static_key_false(&__tracepoint_##name.key)) \
- __DO_TRACE(&__tracepoint_##name, \
- TP_PROTO(data_proto), \
- TP_ARGS(data_args), \
- TP_CONDITION(cond), \
- rcu_idle_exit(), \
- rcu_idle_enter()); \
- } \
+ __DECLARE_TRACE_RCU(name, PARAMS(proto), PARAMS(args), \
+ PARAMS(cond), PARAMS(data_proto), PARAMS(data_args)) \
static inline int \
register_trace_##name(void (*probe)(data_proto), void *data) \
{ \
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index efe4b3308c74..e6f0331e3d45 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -99,25 +99,27 @@ struct xol_area {
struct uprobes_state {
struct xol_area *xol_area;
- atomic_t count;
};
+
extern int __weak set_swbp(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr);
-extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr, bool verify);
+extern int __weak set_orig_insn(struct arch_uprobe *aup, struct mm_struct *mm, unsigned long vaddr);
extern bool __weak is_swbp_insn(uprobe_opcode_t *insn);
extern int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
extern void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumer *uc);
extern int uprobe_mmap(struct vm_area_struct *vma);
extern void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end);
+extern void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm);
extern void uprobe_free_utask(struct task_struct *t);
extern void uprobe_copy_process(struct task_struct *t);
extern unsigned long __weak uprobe_get_swbp_addr(struct pt_regs *regs);
+extern void __weak arch_uprobe_enable_step(struct arch_uprobe *arch);
+extern void __weak arch_uprobe_disable_step(struct arch_uprobe *arch);
extern int uprobe_post_sstep_notifier(struct pt_regs *regs);
extern int uprobe_pre_sstep_notifier(struct pt_regs *regs);
extern void uprobe_notify_resume(struct pt_regs *regs);
extern bool uprobe_deny_signal(void);
extern bool __weak arch_uprobe_skip_sstep(struct arch_uprobe *aup, struct pt_regs *regs);
extern void uprobe_clear_state(struct mm_struct *mm);
-extern void uprobe_reset_state(struct mm_struct *mm);
#else /* !CONFIG_UPROBES */
struct uprobes_state {
};
@@ -138,6 +140,10 @@ static inline void
uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end)
{
}
+static inline void
+uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
+{
+}
static inline void uprobe_notify_resume(struct pt_regs *regs)
{
}
@@ -158,8 +164,5 @@ static inline void uprobe_copy_process(struct task_struct *t)
static inline void uprobe_clear_state(struct mm_struct *mm)
{
}
-static inline void uprobe_reset_state(struct mm_struct *mm)
-{
-}
#endif /* !CONFIG_UPROBES */
#endif /* _LINUX_UPROBES_H */
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 30d1ae38eab1..07915a32fb9d 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -384,6 +384,13 @@ enum usb_device_removable {
USB_DEVICE_FIXED,
};
+enum usb_port_connect_type {
+ USB_PORT_CONNECT_TYPE_UNKNOWN = 0,
+ USB_PORT_CONNECT_TYPE_HOT_PLUG,
+ USB_PORT_CONNECT_TYPE_HARD_WIRED,
+ USB_PORT_NOT_USED,
+};
+
/*
* USB 3.0 Link Power Management (LPM) parameters.
*
@@ -469,7 +476,6 @@ struct usb3_lpm_parameters {
* access from userspace
* @usbfs_dentry: usbfs dentry entry for the device
* @maxchild: number of ports if hub
- * @children: child devices - USB devices that are attached to this hub
* @quirks: quirks of the whole device
* @urbnum: number of URBs submitted for the whole device
* @active_duration: total time device is not suspended
@@ -543,7 +549,6 @@ struct usb_device {
struct list_head filelist;
int maxchild;
- struct usb_device **children;
u32 quirks;
atomic_t urbnum;
@@ -572,6 +577,19 @@ static inline struct usb_device *interface_to_usbdev(struct usb_interface *intf)
extern struct usb_device *usb_get_dev(struct usb_device *dev);
extern void usb_put_dev(struct usb_device *dev);
+extern struct usb_device *usb_hub_find_child(struct usb_device *hdev,
+ int port1);
+
+/**
+ * usb_hub_for_each_child - iterate over all child devices on the hub
+ * @hdev: USB device belonging to the usb hub
+ * @port1: portnum associated with child device
+ * @child: child device pointer
+ */
+#define usb_hub_for_each_child(hdev, port1, child) \
+ for (port1 = 1, child = usb_hub_find_child(hdev, port1); \
+ port1 <= hdev->maxchild; \
+ child = usb_hub_find_child(hdev, ++port1))
/* USB device locking */
#define usb_lock_device(udev) device_lock(&(udev)->dev)
@@ -584,6 +602,16 @@ extern int usb_lock_device_for_reset(struct usb_device *udev,
extern int usb_reset_device(struct usb_device *dev);
extern void usb_queue_reset_device(struct usb_interface *dev);
+#ifdef CONFIG_ACPI
+extern int usb_acpi_set_power_state(struct usb_device *hdev, int index,
+ bool enable);
+extern bool usb_acpi_power_manageable(struct usb_device *hdev, int index);
+#else
+static inline int usb_acpi_set_power_state(struct usb_device *hdev, int index,
+ bool enable) { return 0; }
+static inline bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
+ { return true; }
+#endif
/* USB autosuspend and autoresume */
#ifdef CONFIG_USB_SUSPEND
diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h
index b6c2863b2c94..7692dc69ccf7 100644
--- a/include/linux/usb/ch11.h
+++ b/include/linux/usb/ch11.h
@@ -236,8 +236,8 @@ struct usb_hub_descriptor {
struct {
__u8 bHubHdrDecLat;
- __u16 wHubDelay;
- __u16 DeviceRemovable;
+ __le16 wHubDelay;
+ __le16 DeviceRemovable;
} __attribute__ ((packed)) ss;
} u;
} __attribute__ ((packed));
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 9d8c3b634493..f8dda0621800 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -34,6 +34,8 @@
* the composite model the host can use both functions at the same time.
*/
+#include <linux/bcd.h>
+#include <linux/version.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -46,6 +48,9 @@
*/
#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */
+/* big enough to hold our biggest descriptor */
+#define USB_COMP_EP0_BUFSIZ 1024
+
struct usb_configuration;
/**
@@ -245,24 +250,31 @@ int usb_add_config(struct usb_composite_dev *,
void usb_remove_config(struct usb_composite_dev *,
struct usb_configuration *);
+/* predefined index for usb_composite_driver */
+enum {
+ USB_GADGET_MANUFACTURER_IDX = 0,
+ USB_GADGET_PRODUCT_IDX,
+ USB_GADGET_SERIAL_IDX,
+ USB_GADGET_FIRST_AVAIL_IDX,
+};
+
/**
* struct usb_composite_driver - groups configurations into a gadget
* @name: For diagnostics, identifies the driver.
- * @iProduct: Used as iProduct override if @dev->iProduct is not set.
- * If NULL value of @name is taken.
- * @iManufacturer: Used as iManufacturer override if @dev->iManufacturer is
- * not set. If NULL a default "<system> <release> with <udc>" value
- * will be used.
- * @iSerialNumber: Used as iSerialNumber override if @dev->iSerialNumber is
- * not set.
* @dev: Template descriptor for the device, including default device
* identifiers.
- * @strings: tables of strings, keyed by identifiers assigned during bind()
- * and language IDs provided in control requests
+ * @strings: tables of strings, keyed by identifiers assigned during @bind
+ * and language IDs provided in control requests. Note: The first entries
+ * are predefined. The first entry that may be used is
+ * USB_GADGET_FIRST_AVAIL_IDX
* @max_speed: Highest speed the driver supports.
* @needs_serial: set to 1 if the gadget needs userspace to provide
* a serial number. If one is not provided, warning will be printed.
- * @unbind: Reverses bind; called as a side effect of unregistering
+ * @bind: (REQUIRED) Used to allocate resources that are shared across the
+ * whole device, such as string IDs, and add its configurations using
+ * @usb_add_config(). This may fail by returning a negative errno
+ * value; it should return zero on successful initialization.
+ * @unbind: Reverses @bind; called as a side effect of unregistering
* this driver.
* @disconnect: optional driver disconnect method
* @suspend: Notifies when the host stops sending USB traffic,
@@ -271,9 +283,9 @@ void usb_remove_config(struct usb_composite_dev *,
* before function notifications
*
* Devices default to reporting self powered operation. Devices which rely
- * on bus powered operation should report this in their @bind() method.
+ * on bus powered operation should report this in their @bind method.
*
- * Before returning from bind, various fields in the template descriptor
+ * Before returning from @bind, various fields in the template descriptor
* may be overridden. These include the idVendor/idProduct/bcdDevice values
* normally to bind the appropriate host side driver, and the three strings
* (iManufacturer, iProduct, iSerialNumber) normally used to provide user
@@ -283,14 +295,12 @@ void usb_remove_config(struct usb_composite_dev *,
*/
struct usb_composite_driver {
const char *name;
- const char *iProduct;
- const char *iManufacturer;
- const char *iSerialNumber;
const struct usb_device_descriptor *dev;
struct usb_gadget_strings **strings;
enum usb_device_speed max_speed;
unsigned needs_serial:1;
+ int (*bind)(struct usb_composite_dev *cdev);
int (*unbind)(struct usb_composite_dev *);
void (*disconnect)(struct usb_composite_dev *);
@@ -298,10 +308,10 @@ struct usb_composite_driver {
/* global suspend hooks */
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
+ struct usb_gadget_driver gadget_driver;
};
-extern int usb_composite_probe(struct usb_composite_driver *driver,
- int (*bind)(struct usb_composite_dev *cdev));
+extern int usb_composite_probe(struct usb_composite_driver *driver);
extern void usb_composite_unregister(struct usb_composite_driver *driver);
extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
@@ -310,7 +320,6 @@ extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
* struct usb_composite_device - represents one composite usb gadget
* @gadget: read-only, abstracts the gadget's usb peripheral controller
* @req: used for control responses; buffer is pre-allocated
- * @bufsiz: size of buffer pre-allocated in @req
* @config: the currently active configuration
*
* One of these devices is allocated and initialized before the
@@ -341,7 +350,6 @@ extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
struct usb_composite_dev {
struct usb_gadget *gadget;
struct usb_request *req;
- unsigned bufsiz;
struct usb_configuration *config;
@@ -352,9 +360,7 @@ struct usb_composite_dev {
struct list_head configs;
struct usb_composite_driver *driver;
u8 next_string_id;
- u8 manufacturer_override;
- u8 product_override;
- u8 serial_override;
+ char *def_manufacturer;
/* the gadget driver won't enable the data pullup
* while the deactivation count is nonzero.
@@ -375,6 +381,53 @@ extern int usb_string_ids_tab(struct usb_composite_dev *c,
struct usb_string *str);
extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n);
+/*
+ * Some systems will need runtime overrides for the product identifiers
+ * published in the device descriptor, either numbers or strings or both.
+ * String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+struct usb_composite_overwrite {
+ u16 idVendor;
+ u16 idProduct;
+ u16 bcdDevice;
+ char *serial_number;
+ char *manufacturer;
+ char *product;
+};
+#define USB_GADGET_COMPOSITE_OPTIONS() \
+ static struct usb_composite_overwrite coverwrite; \
+ \
+ module_param_named(idVendor, coverwrite.idVendor, ushort, S_IRUGO); \
+ MODULE_PARM_DESC(idVendor, "USB Vendor ID"); \
+ \
+ module_param_named(idProduct, coverwrite.idProduct, ushort, S_IRUGO); \
+ MODULE_PARM_DESC(idProduct, "USB Product ID"); \
+ \
+ module_param_named(bcdDevice, coverwrite.bcdDevice, ushort, S_IRUGO); \
+ MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); \
+ \
+ module_param_named(iSerialNumber, coverwrite.serial_number, charp, \
+ S_IRUGO); \
+ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); \
+ \
+ module_param_named(iManufacturer, coverwrite.manufacturer, charp, \
+ S_IRUGO); \
+ MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); \
+ \
+ module_param_named(iProduct, coverwrite.product, charp, S_IRUGO); \
+ MODULE_PARM_DESC(iProduct, "USB Product string")
+
+void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
+ struct usb_composite_overwrite *covr);
+
+static inline u16 get_default_bcdDevice(void)
+{
+ u16 bcdDevice;
+
+ bcdDevice = bin2bcd((LINUX_VERSION_CODE >> 16 & 0xff)) << 8;
+ bcdDevice |= bin2bcd((LINUX_VERSION_CODE >> 8 & 0xff));
+ return bcdDevice;
+}
/* messaging utils */
#define DBG(d, fmt, args...) \
diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h
index de4b9ed5d5dd..daec99af5d54 100644
--- a/include/linux/usb/ehci_def.h
+++ b/include/linux/usb/ehci_def.h
@@ -171,18 +171,18 @@ struct ehci_regs {
#define USBMODE_CM_HC (3<<0) /* host controller mode */
#define USBMODE_CM_IDLE (0<<0) /* idle state */
- u32 reserved4[7];
+ u32 reserved4[6];
/* Moorestown has some non-standard registers, partially due to the fact that
* its EHCI controller has both TT and LPM support. HOSTPCx are extensions to
* PORTSCx
*/
/* HOSTPC: offset 0x84 */
- u32 hostpc[0]; /* HOSTPC extension */
+ u32 hostpc[1]; /* HOSTPC extension */
#define HOSTPC_PHCD (1<<22) /* Phy clock disable */
#define HOSTPC_PSPD (3<<25) /* Port speed detection */
- u32 reserved5[17];
+ u32 reserved5[16];
/* USBMODE_EX: offset 0xc8 */
u32 usbmode_ex; /* USB Device mode extension */
@@ -221,18 +221,35 @@ extern int __init early_dbgp_init(char *s);
extern struct console early_dbgp_console;
#endif /* CONFIG_EARLY_PRINTK_DBGP */
+struct usb_hcd;
+
+#ifdef CONFIG_XEN_DOM0
+extern int xen_dbgp_reset_prep(struct usb_hcd *);
+extern int xen_dbgp_external_startup(struct usb_hcd *);
+#else
+static inline int xen_dbgp_reset_prep(struct usb_hcd *hcd)
+{
+ return 1; /* Shouldn't this be 0? */
+}
+
+static inline int xen_dbgp_external_startup(struct usb_hcd *hcd)
+{
+ return -1;
+}
+#endif
+
#ifdef CONFIG_EARLY_PRINTK_DBGP
/* Call backs from ehci host driver to ehci debug driver */
-extern int dbgp_external_startup(void);
-extern int dbgp_reset_prep(void);
+extern int dbgp_external_startup(struct usb_hcd *);
+extern int dbgp_reset_prep(struct usb_hcd *hcd);
#else
-static inline int dbgp_reset_prep(void)
+static inline int dbgp_reset_prep(struct usb_hcd *hcd)
{
- return 1;
+ return xen_dbgp_reset_prep(hcd);
}
-static inline int dbgp_external_startup(void)
+static inline int dbgp_external_startup(struct usb_hcd *hcd)
{
- return -1;
+ return xen_dbgp_external_startup(hcd);
}
#endif
diff --git a/include/linux/usb/ehci_pdriver.h b/include/linux/usb/ehci_pdriver.h
index 1894f42fe3f7..c9d09f8b7ff2 100644
--- a/include/linux/usb/ehci_pdriver.h
+++ b/include/linux/usb/ehci_pdriver.h
@@ -41,6 +41,14 @@ struct usb_ehci_pdata {
unsigned big_endian_mmio:1;
unsigned port_power_on:1;
unsigned port_power_off:1;
+
+ /* Turn on all power and clocks */
+ int (*power_on)(struct platform_device *pdev);
+ /* Turn off all power and clocks */
+ void (*power_off)(struct platform_device *pdev);
+ /* Turn on only VBUS suspend power and hotplug detection,
+ * turn off everything else */
+ void (*power_suspend)(struct platform_device *pdev);
};
#endif /* __USB_CORE_EHCI_PDRIVER_H */
diff --git a/include/linux/usb/ezusb.h b/include/linux/usb/ezusb.h
new file mode 100644
index 000000000000..fc618d8d1e92
--- /dev/null
+++ b/include/linux/usb/ezusb.h
@@ -0,0 +1,16 @@
+#ifndef __EZUSB_H
+#define __EZUSB_H
+
+
+extern int ezusb_writememory(struct usb_device *dev, int address,
+ unsigned char *data, int length, __u8 bRequest);
+
+extern int ezusb_fx1_set_reset(struct usb_device *dev, unsigned char reset_bit);
+extern int ezusb_fx2_set_reset(struct usb_device *dev, unsigned char reset_bit);
+
+extern int ezusb_fx1_ihex_firmware_download(struct usb_device *dev,
+ const char *firmware_path);
+extern int ezusb_fx2_ihex_firmware_download(struct usb_device *dev,
+ const char *firmware_path);
+
+#endif /* __EZUSB_H */
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 9517466ababb..5b6e50888248 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -474,7 +474,8 @@ struct usb_gadget_ops {
/* Those two are deprecated */
int (*start)(struct usb_gadget_driver *,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *,
+ struct usb_gadget_driver *driver));
int (*stop)(struct usb_gadget_driver *);
};
@@ -502,6 +503,8 @@ struct usb_gadget_ops {
* @name: Identifies the controller hardware type. Used in diagnostics
* and sometimes configuration.
* @dev: Driver model state for this abstract device.
+ * @out_epnum: last used out ep number
+ * @in_epnum: last used in ep number
*
* Gadgets have a mostly-portable "gadget driver" implementing device
* functions, handling all usb configurations and interfaces. Gadget
@@ -536,6 +539,8 @@ struct usb_gadget {
unsigned a_alt_hnp_support:1;
const char *name;
struct device dev;
+ unsigned out_epnum;
+ unsigned in_epnum;
};
static inline void set_gadget_data(struct usb_gadget *gadget, void *data)
@@ -558,14 +563,7 @@ static inline struct usb_gadget *dev_to_usb_gadget(struct device *dev)
*/
static inline int gadget_is_dualspeed(struct usb_gadget *g)
{
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- /* runtime test would check "g->max_speed" ... that might be
- * useful to work around hardware bugs, but is mostly pointless
- */
- return 1;
-#else
- return 0;
-#endif
+ return g->max_speed >= USB_SPEED_HIGH;
}
/**
@@ -575,15 +573,7 @@ static inline int gadget_is_dualspeed(struct usb_gadget *g)
*/
static inline int gadget_is_superspeed(struct usb_gadget *g)
{
-#ifdef CONFIG_USB_GADGET_SUPERSPEED
- /*
- * runtime test would check "g->max_speed" ... that might be
- * useful to work around hardware bugs, but is mostly pointless
- */
- return 1;
-#else
- return 0;
-#endif
+ return g->max_speed >= USB_SPEED_SUPER;
}
/**
@@ -781,6 +771,7 @@ static inline int usb_gadget_disconnect(struct usb_gadget *gadget)
* when the host is disconnected. May be called in_interrupt; this
* may not sleep. Some devices can't detect disconnect, so this might
* not be called except as part of controller shutdown.
+ * @bind: the driver's bind callback
* @unbind: Invoked when the driver is unbound from a gadget,
* usually from rmmod (after a disconnect is reported).
* Called in a context that permits sleeping.
@@ -835,6 +826,8 @@ static inline int usb_gadget_disconnect(struct usb_gadget *gadget)
struct usb_gadget_driver {
char *function;
enum usb_device_speed max_speed;
+ int (*bind)(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver);
void (*unbind)(struct usb_gadget *);
int (*setup)(struct usb_gadget *,
const struct usb_ctrlrequest *);
@@ -860,7 +853,6 @@ struct usb_gadget_driver {
/**
* usb_gadget_probe_driver - probe a gadget driver
* @driver: the driver being registered
- * @bind: the driver's bind callback
* Context: can sleep
*
* Call this in your gadget driver's module initialization function,
@@ -869,8 +861,7 @@ struct usb_gadget_driver {
* registration call returns. It's expected that the @bind() function will
* be in init sections.
*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
/**
* usb_gadget_unregister_driver - unregister a gadget driver
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index c5fdb148fc02..608050b2545f 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -135,8 +135,8 @@ struct usb_hcd {
unsigned int irq; /* irq allocated */
void __iomem *regs; /* device memory/io */
- u64 rsrc_start; /* memory/io resource start */
- u64 rsrc_len; /* memory/io resource length */
+ resource_size_t rsrc_start; /* memory/io resource start */
+ resource_size_t rsrc_len; /* memory/io resource length */
unsigned power_budget; /* in mA, 0 = no limit */
/* bandwidth_mutex should be taken before adding or removing
diff --git a/include/linux/usb/nop-usb-xceiv.h b/include/linux/usb/nop-usb-xceiv.h
new file mode 100644
index 000000000000..28884c717411
--- /dev/null
+++ b/include/linux/usb/nop-usb-xceiv.h
@@ -0,0 +1,24 @@
+#ifndef __LINUX_USB_NOP_XCEIV_H
+#define __LINUX_USB_NOP_XCEIV_H
+
+#include <linux/usb/otg.h>
+
+struct nop_usb_xceiv_platform_data {
+ enum usb_phy_type type;
+};
+
+#if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE))
+/* sometimes transceivers are accessed only through e.g. ULPI */
+extern void usb_nop_xceiv_register(void);
+extern void usb_nop_xceiv_unregister(void);
+#else
+static inline void usb_nop_xceiv_register(void)
+{
+}
+
+static inline void usb_nop_xceiv_unregister(void)
+{
+}
+#endif
+
+#endif /* __LINUX_USB_NOP_XCEIV_H */
diff --git a/include/linux/usb/ohci_pdriver.h b/include/linux/usb/ohci_pdriver.h
index 2808f2a9cce8..74e7755168b7 100644
--- a/include/linux/usb/ohci_pdriver.h
+++ b/include/linux/usb/ohci_pdriver.h
@@ -33,6 +33,14 @@ struct usb_ohci_pdata {
unsigned big_endian_desc:1;
unsigned big_endian_mmio:1;
unsigned no_big_frame_no:1;
+
+ /* Turn on all power and clocks */
+ int (*power_on)(struct platform_device *pdev);
+ /* Turn off all power and clocks */
+ void (*power_off)(struct platform_device *pdev);
+ /* Turn on only VBUS suspend power and hotplug detection,
+ * turn off everything else */
+ void (*power_suspend)(struct platform_device *pdev);
};
#endif /* __USB_CORE_OHCI_PDRIVER_H */
diff --git a/include/linux/usb/omap_usb.h b/include/linux/usb/omap_usb.h
new file mode 100644
index 000000000000..0ea17f8ae820
--- /dev/null
+++ b/include/linux/usb/omap_usb.h
@@ -0,0 +1,46 @@
+/*
+ * omap_usb.h -- omap usb2 phy header file
+ *
+ * Copyright (C) 2012 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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 __DRIVERS_OMAP_USB2_H
+#define __DRIVERS_OMAP_USB2_H
+
+#include <linux/usb/otg.h>
+
+struct omap_usb {
+ struct usb_phy phy;
+ struct phy_companion *comparator;
+ struct device *dev;
+ u32 __iomem *control_dev;
+ struct clk *wkupclk;
+ u8 is_suspended:1;
+};
+
+#define PHY_PD 0x1
+
+#define phy_to_omapusb(x) container_of((x), struct omap_usb, phy)
+
+#if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE)
+extern int omap_usb2_set_comparator(struct phy_companion *comparator);
+#else
+static inline int omap_usb2_set_comparator(struct phy_companion *comparator)
+{
+ return -ENODEV;
+}
+#endif
+
+#endif /* __DRIVERS_OMAP_USB_H */
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 45824be0a2f9..e8a5fe87c6bd 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -9,56 +9,7 @@
#ifndef __LINUX_USB_OTG_H
#define __LINUX_USB_OTG_H
-#include <linux/notifier.h>
-
-/* OTG defines lots of enumeration states before device reset */
-enum usb_otg_state {
- OTG_STATE_UNDEFINED = 0,
-
- /* single-role peripheral, and dual-role default-b */
- OTG_STATE_B_IDLE,
- OTG_STATE_B_SRP_INIT,
- OTG_STATE_B_PERIPHERAL,
-
- /* extra dual-role default-b states */
- OTG_STATE_B_WAIT_ACON,
- OTG_STATE_B_HOST,
-
- /* dual-role default-a */
- OTG_STATE_A_IDLE,
- OTG_STATE_A_WAIT_VRISE,
- OTG_STATE_A_WAIT_BCON,
- OTG_STATE_A_HOST,
- OTG_STATE_A_SUSPEND,
- OTG_STATE_A_PERIPHERAL,
- OTG_STATE_A_WAIT_VFALL,
- OTG_STATE_A_VBUS_ERR,
-};
-
-enum usb_phy_events {
- USB_EVENT_NONE, /* no events or cable disconnected */
- USB_EVENT_VBUS, /* vbus valid event */
- USB_EVENT_ID, /* id was grounded */
- USB_EVENT_CHARGER, /* usb dedicated charger */
- USB_EVENT_ENUMERATED, /* gadget driver enumerated */
-};
-
-/* associate a type with PHY */
-enum usb_phy_type {
- USB_PHY_TYPE_UNDEFINED,
- USB_PHY_TYPE_USB2,
- USB_PHY_TYPE_USB3,
-};
-
-struct usb_phy;
-
-/* for transceivers connected thru an ULPI interface, the user must
- * provide access ops
- */
-struct usb_phy_io_ops {
- int (*read)(struct usb_phy *x, u32 reg);
- int (*write)(struct usb_phy *x, u32 val, u32 reg);
-};
+#include <linux/usb/phy.h>
struct usb_otg {
u8 default_a;
@@ -85,134 +36,9 @@ struct usb_otg {
};
-/*
- * the otg driver needs to interact with both device side and host side
- * usb controllers. it decides which controller is active at a given
- * moment, using the transceiver, ID signal, HNP and sometimes static
- * configuration information (including "board isn't wired for otg").
- */
-struct usb_phy {
- struct device *dev;
- const char *label;
- unsigned int flags;
-
- enum usb_phy_type type;
- enum usb_otg_state state;
- enum usb_phy_events last_event;
-
- struct usb_otg *otg;
-
- struct device *io_dev;
- struct usb_phy_io_ops *io_ops;
- void __iomem *io_priv;
-
- /* for notification of usb_phy_events */
- struct atomic_notifier_head notifier;
-
- /* to pass extra port status to the root hub */
- u16 port_status;
- u16 port_change;
-
- /* to support controllers that have multiple transceivers */
- struct list_head head;
-
- /* initialize/shutdown the OTG controller */
- int (*init)(struct usb_phy *x);
- void (*shutdown)(struct usb_phy *x);
-
- /* effective for B devices, ignored for A-peripheral */
- int (*set_power)(struct usb_phy *x,
- unsigned mA);
-
- /* for non-OTG B devices: set transceiver into suspend mode */
- int (*set_suspend)(struct usb_phy *x,
- int suspend);
-
- /* notify phy connect status change */
- int (*notify_connect)(struct usb_phy *x, int port);
- int (*notify_disconnect)(struct usb_phy *x, int port);
-};
-
-
-/* for board-specific init logic */
-extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type);
-extern void usb_remove_phy(struct usb_phy *);
-
-#if defined(CONFIG_NOP_USB_XCEIV) || (defined(CONFIG_NOP_USB_XCEIV_MODULE) && defined(MODULE))
-/* sometimes transceivers are accessed only through e.g. ULPI */
-extern void usb_nop_xceiv_register(void);
-extern void usb_nop_xceiv_unregister(void);
-#else
-static inline void usb_nop_xceiv_register(void)
-{
-}
-
-static inline void usb_nop_xceiv_unregister(void)
-{
-}
-#endif
-
-/* helpers for direct access thru low-level io interface */
-static inline int usb_phy_io_read(struct usb_phy *x, u32 reg)
-{
- if (x->io_ops && x->io_ops->read)
- return x->io_ops->read(x, reg);
-
- return -EINVAL;
-}
-
-static inline int usb_phy_io_write(struct usb_phy *x, u32 val, u32 reg)
-{
- if (x->io_ops && x->io_ops->write)
- return x->io_ops->write(x, val, reg);
-
- return -EINVAL;
-}
-
-static inline int
-usb_phy_init(struct usb_phy *x)
-{
- if (x->init)
- return x->init(x);
-
- return 0;
-}
-
-static inline void
-usb_phy_shutdown(struct usb_phy *x)
-{
- if (x->shutdown)
- x->shutdown(x);
-}
-
-/* for usb host and peripheral controller drivers */
#ifdef CONFIG_USB_OTG_UTILS
-extern struct usb_phy *usb_get_phy(enum usb_phy_type type);
-extern struct usb_phy *devm_usb_get_phy(struct device *dev,
- enum usb_phy_type type);
-extern void usb_put_phy(struct usb_phy *);
-extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x);
extern const char *otg_state_string(enum usb_otg_state state);
#else
-static inline struct usb_phy *usb_get_phy(enum usb_phy_type type)
-{
- return NULL;
-}
-
-static inline struct usb_phy *devm_usb_get_phy(struct device *dev,
- enum usb_phy_type type)
-{
- return NULL;
-}
-
-static inline void usb_put_phy(struct usb_phy *x)
-{
-}
-
-static inline void devm_usb_put_phy(struct device *dev, struct usb_phy *x)
-{
-}
-
static inline const char *otg_state_string(enum usb_otg_state state)
{
return NULL;
@@ -262,42 +88,6 @@ otg_set_peripheral(struct usb_otg *otg, struct usb_gadget *periph)
}
static inline int
-usb_phy_set_power(struct usb_phy *x, unsigned mA)
-{
- if (x && x->set_power)
- return x->set_power(x, mA);
- return 0;
-}
-
-/* Context: can sleep */
-static inline int
-usb_phy_set_suspend(struct usb_phy *x, int suspend)
-{
- if (x->set_suspend != NULL)
- return x->set_suspend(x, suspend);
- else
- return 0;
-}
-
-static inline int
-usb_phy_notify_connect(struct usb_phy *x, int port)
-{
- if (x->notify_connect)
- return x->notify_connect(x, port);
- else
- return 0;
-}
-
-static inline int
-usb_phy_notify_disconnect(struct usb_phy *x, int port)
-{
- if (x->notify_disconnect)
- return x->notify_disconnect(x, port);
- else
- return 0;
-}
-
-static inline int
otg_start_srp(struct usb_otg *otg)
{
if (otg && otg->start_srp)
@@ -306,31 +96,7 @@ otg_start_srp(struct usb_otg *otg)
return -ENOTSUPP;
}
-/* notifiers */
-static inline int
-usb_register_notifier(struct usb_phy *x, struct notifier_block *nb)
-{
- return atomic_notifier_chain_register(&x->notifier, nb);
-}
-
-static inline void
-usb_unregister_notifier(struct usb_phy *x, struct notifier_block *nb)
-{
- atomic_notifier_chain_unregister(&x->notifier, nb);
-}
-
/* for OTG controller drivers (and maybe other stuff) */
extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
-static inline const char *usb_phy_type_string(enum usb_phy_type type)
-{
- switch (type) {
- case USB_PHY_TYPE_USB2:
- return "USB2 PHY";
- case USB_PHY_TYPE_USB3:
- return "USB3 PHY";
- default:
- return "UNKNOWN PHY TYPE";
- }
-}
#endif /* __LINUX_USB_OTG_H */
diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h
new file mode 100644
index 000000000000..06b5bae35b29
--- /dev/null
+++ b/include/linux/usb/phy.h
@@ -0,0 +1,233 @@
+/* USB OTG (On The Go) defines */
+/*
+ *
+ * These APIs may be used between USB controllers. USB device drivers
+ * (for either host or peripheral roles) don't use these calls; they
+ * continue to use just usb_device and usb_gadget.
+ */
+
+#ifndef __LINUX_USB_PHY_H
+#define __LINUX_USB_PHY_H
+
+#include <linux/notifier.h>
+
+enum usb_phy_events {
+ USB_EVENT_NONE, /* no events or cable disconnected */
+ USB_EVENT_VBUS, /* vbus valid event */
+ USB_EVENT_ID, /* id was grounded */
+ USB_EVENT_CHARGER, /* usb dedicated charger */
+ USB_EVENT_ENUMERATED, /* gadget driver enumerated */
+};
+
+/* associate a type with PHY */
+enum usb_phy_type {
+ USB_PHY_TYPE_UNDEFINED,
+ USB_PHY_TYPE_USB2,
+ USB_PHY_TYPE_USB3,
+};
+
+/* OTG defines lots of enumeration states before device reset */
+enum usb_otg_state {
+ OTG_STATE_UNDEFINED = 0,
+
+ /* single-role peripheral, and dual-role default-b */
+ OTG_STATE_B_IDLE,
+ OTG_STATE_B_SRP_INIT,
+ OTG_STATE_B_PERIPHERAL,
+
+ /* extra dual-role default-b states */
+ OTG_STATE_B_WAIT_ACON,
+ OTG_STATE_B_HOST,
+
+ /* dual-role default-a */
+ OTG_STATE_A_IDLE,
+ OTG_STATE_A_WAIT_VRISE,
+ OTG_STATE_A_WAIT_BCON,
+ OTG_STATE_A_HOST,
+ OTG_STATE_A_SUSPEND,
+ OTG_STATE_A_PERIPHERAL,
+ OTG_STATE_A_WAIT_VFALL,
+ OTG_STATE_A_VBUS_ERR,
+};
+
+struct usb_phy;
+struct usb_otg;
+
+/* for transceivers connected thru an ULPI interface, the user must
+ * provide access ops
+ */
+struct usb_phy_io_ops {
+ int (*read)(struct usb_phy *x, u32 reg);
+ int (*write)(struct usb_phy *x, u32 val, u32 reg);
+};
+
+struct usb_phy {
+ struct device *dev;
+ const char *label;
+ unsigned int flags;
+
+ enum usb_phy_type type;
+ enum usb_otg_state state;
+ enum usb_phy_events last_event;
+
+ struct usb_otg *otg;
+
+ struct device *io_dev;
+ struct usb_phy_io_ops *io_ops;
+ void __iomem *io_priv;
+
+ /* for notification of usb_phy_events */
+ struct atomic_notifier_head notifier;
+
+ /* to pass extra port status to the root hub */
+ u16 port_status;
+ u16 port_change;
+
+ /* to support controllers that have multiple transceivers */
+ struct list_head head;
+
+ /* initialize/shutdown the OTG controller */
+ int (*init)(struct usb_phy *x);
+ void (*shutdown)(struct usb_phy *x);
+
+ /* effective for B devices, ignored for A-peripheral */
+ int (*set_power)(struct usb_phy *x,
+ unsigned mA);
+
+ /* for non-OTG B devices: set transceiver into suspend mode */
+ int (*set_suspend)(struct usb_phy *x,
+ int suspend);
+
+ /* notify phy connect status change */
+ int (*notify_connect)(struct usb_phy *x, int port);
+ int (*notify_disconnect)(struct usb_phy *x, int port);
+};
+
+
+/* for board-specific init logic */
+extern int usb_add_phy(struct usb_phy *, enum usb_phy_type type);
+extern void usb_remove_phy(struct usb_phy *);
+
+/* helpers for direct access thru low-level io interface */
+static inline int usb_phy_io_read(struct usb_phy *x, u32 reg)
+{
+ if (x->io_ops && x->io_ops->read)
+ return x->io_ops->read(x, reg);
+
+ return -EINVAL;
+}
+
+static inline int usb_phy_io_write(struct usb_phy *x, u32 val, u32 reg)
+{
+ if (x->io_ops && x->io_ops->write)
+ return x->io_ops->write(x, val, reg);
+
+ return -EINVAL;
+}
+
+static inline int
+usb_phy_init(struct usb_phy *x)
+{
+ if (x->init)
+ return x->init(x);
+
+ return 0;
+}
+
+static inline void
+usb_phy_shutdown(struct usb_phy *x)
+{
+ if (x->shutdown)
+ x->shutdown(x);
+}
+
+/* for usb host and peripheral controller drivers */
+#ifdef CONFIG_USB_OTG_UTILS
+extern struct usb_phy *usb_get_phy(enum usb_phy_type type);
+extern struct usb_phy *devm_usb_get_phy(struct device *dev,
+ enum usb_phy_type type);
+extern void usb_put_phy(struct usb_phy *);
+extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x);
+#else
+static inline struct usb_phy *usb_get_phy(enum usb_phy_type type)
+{
+ return NULL;
+}
+
+static inline struct usb_phy *devm_usb_get_phy(struct device *dev,
+ enum usb_phy_type type)
+{
+ return NULL;
+}
+
+static inline void usb_put_phy(struct usb_phy *x)
+{
+}
+
+static inline void devm_usb_put_phy(struct device *dev, struct usb_phy *x)
+{
+}
+
+#endif
+
+static inline int
+usb_phy_set_power(struct usb_phy *x, unsigned mA)
+{
+ if (x && x->set_power)
+ return x->set_power(x, mA);
+ return 0;
+}
+
+/* Context: can sleep */
+static inline int
+usb_phy_set_suspend(struct usb_phy *x, int suspend)
+{
+ if (x->set_suspend != NULL)
+ return x->set_suspend(x, suspend);
+ else
+ return 0;
+}
+
+static inline int
+usb_phy_notify_connect(struct usb_phy *x, int port)
+{
+ if (x->notify_connect)
+ return x->notify_connect(x, port);
+ else
+ return 0;
+}
+
+static inline int
+usb_phy_notify_disconnect(struct usb_phy *x, int port)
+{
+ if (x->notify_disconnect)
+ return x->notify_disconnect(x, port);
+ else
+ return 0;
+}
+
+/* notifiers */
+static inline int
+usb_register_notifier(struct usb_phy *x, struct notifier_block *nb)
+{
+ return atomic_notifier_chain_register(&x->notifier, nb);
+}
+
+static inline void
+usb_unregister_notifier(struct usb_phy *x, struct notifier_block *nb)
+{
+ atomic_notifier_chain_unregister(&x->notifier, nb);
+}
+
+static inline const char *usb_phy_type_string(enum usb_phy_type type)
+{
+ switch (type) {
+ case USB_PHY_TYPE_USB2:
+ return "USB2 PHY";
+ case USB_PHY_TYPE_USB3:
+ return "USB3 PHY";
+ default:
+ return "UNKNOWN PHY TYPE";
+ }
+}
+#endif /* __LINUX_USB_PHY_H */
diff --git a/include/linux/usb/phy_companion.h b/include/linux/usb/phy_companion.h
new file mode 100644
index 000000000000..edd2ec23d282
--- /dev/null
+++ b/include/linux/usb/phy_companion.h
@@ -0,0 +1,34 @@
+/*
+ * phy-companion.h -- phy companion to indicate the comparator part of PHY
+ *
+ * Copyright (C) 2012 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT 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 __DRIVERS_PHY_COMPANION_H
+#define __DRIVERS_PHY_COMPANION_H
+
+#include <linux/usb/otg.h>
+
+/* phy_companion to take care of VBUS, ID and srp capabilities */
+struct phy_companion {
+
+ /* effective for A-peripheral, ignored for B devices */
+ int (*set_vbus)(struct phy_companion *x, bool enabled);
+
+ /* for B devices only: start session with A-Host */
+ int (*start_srp)(struct phy_companion *x);
+};
+
+#endif /* __DRIVERS_PHY_COMPANION_H */
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 3e93de7ecbc3..52f944dfe2fd 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -19,8 +19,8 @@
/* device can't handle its Configuration or Interface strings */
#define USB_QUIRK_CONFIG_INTF_STRINGS 0x00000008
-/*device will morph if reset, don't use reset for handling errors */
-#define USB_QUIRK_RESET_MORPHS 0x00000010
+/* device can't be reset(e.g morph devices), don't use reset */
+#define USB_QUIRK_RESET 0x00000010
/* device has more interface descriptions than the bNumInterfaces count,
and can't handle talking to these interfaces */
diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h
index 86c0b451745d..ef9be7e1e190 100644
--- a/include/linux/usb/serial.h
+++ b/include/linux/usb/serial.h
@@ -301,17 +301,13 @@ extern void usb_serial_port_softint(struct usb_serial_port *port);
extern int usb_serial_suspend(struct usb_interface *intf, pm_message_t message);
extern int usb_serial_resume(struct usb_interface *intf);
-extern int ezusb_writememory(struct usb_serial *serial, int address,
- unsigned char *data, int length, __u8 bRequest);
-extern int ezusb_set_reset(struct usb_serial *serial, unsigned char reset_bit);
-
/* USB Serial console functions */
#ifdef CONFIG_USB_SERIAL_CONSOLE
-extern void usb_serial_console_init(int debug, int minor);
+extern void usb_serial_console_init(int minor);
extern void usb_serial_console_exit(void);
extern void usb_serial_console_disconnect(struct usb_serial *serial);
#else
-static inline void usb_serial_console_init(int debug, int minor) { }
+static inline void usb_serial_console_init(int minor) { }
static inline void usb_serial_console_exit(void) { }
static inline void usb_serial_console_disconnect(struct usb_serial *serial) {}
#endif
@@ -333,7 +329,7 @@ extern void usb_serial_generic_throttle(struct tty_struct *tty);
extern void usb_serial_generic_unthrottle(struct tty_struct *tty);
extern void usb_serial_generic_disconnect(struct usb_serial *serial);
extern void usb_serial_generic_release(struct usb_serial *serial);
-extern int usb_serial_generic_register(int debug);
+extern int usb_serial_generic_register(void);
extern void usb_serial_generic_deregister(void);
extern int usb_serial_generic_submit_read_urbs(struct usb_serial_port *port,
gfp_t mem_flags);
@@ -355,30 +351,14 @@ extern struct usb_serial_driver usb_serial_generic_device;
extern struct bus_type usb_serial_bus_type;
extern struct tty_driver *usb_serial_tty_driver;
-static inline void usb_serial_debug_data(int debug,
- struct device *dev,
+static inline void usb_serial_debug_data(struct device *dev,
const char *function, int size,
const unsigned char *data)
{
- int i;
-
- if (debug) {
- dev_printk(KERN_DEBUG, dev, "%s - length = %d, data = ",
- function, size);
- for (i = 0; i < size; ++i)
- printk("%.2x ", data[i]);
- printk("\n");
- }
+ dev_dbg(dev, "%s - length = %d, data = %*ph\n",
+ function, size, size, data);
}
-/* Use our own dbg macro */
-#undef dbg
-#define dbg(format, arg...) \
-do { \
- if (debug) \
- printk(KERN_DEBUG "%s: " format "\n", __FILE__, ##arg); \
-} while (0)
-
/*
* Macro for reporting errors in write path to avoid inifinite loop
* when port is used as a console.
diff --git a/arch/arm/mach-tegra/include/mach/usb_phy.h b/include/linux/usb/tegra_usb_phy.h
index 935ce9f65590..176b1ca06ae4 100644
--- a/arch/arm/mach-tegra/include/mach/usb_phy.h
+++ b/include/linux/usb/tegra_usb_phy.h
@@ -1,6 +1,4 @@
/*
- * arch/arm/mach-tegra/include/mach/usb_phy.h
- *
* Copyright (C) 2010 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
@@ -14,8 +12,8 @@
*
*/
-#ifndef __MACH_USB_PHY_H
-#define __MACH_USB_PHY_H
+#ifndef __TEGRA_USB_PHY_H
+#define __TEGRA_USB_PHY_H
#include <linux/clk.h>
#include <linux/usb/otg.h>
@@ -59,19 +57,17 @@ struct tegra_usb_phy {
enum tegra_usb_phy_mode mode;
void *config;
struct usb_phy *ulpi;
+ struct usb_phy u_phy;
+ struct device *dev;
};
struct tegra_usb_phy *tegra_usb_phy_open(struct device *dev, int instance,
void __iomem *regs, void *config, enum tegra_usb_phy_mode phy_mode);
-int tegra_usb_phy_power_on(struct tegra_usb_phy *phy);
-
void tegra_usb_phy_clk_disable(struct tegra_usb_phy *phy);
void tegra_usb_phy_clk_enable(struct tegra_usb_phy *phy);
-void tegra_usb_phy_power_off(struct tegra_usb_phy *phy);
-
void tegra_usb_phy_preresume(struct tegra_usb_phy *phy);
void tegra_usb_phy_postresume(struct tegra_usb_phy *phy);
@@ -81,6 +77,4 @@ void tegra_ehci_phy_restore_start(struct tegra_usb_phy *phy,
void tegra_ehci_phy_restore_end(struct tegra_usb_phy *phy);
-void tegra_usb_phy_close(struct tegra_usb_phy *phy);
-
-#endif /* __MACH_USB_PHY_H */
+#endif /* __TEGRA_USB_PHY_H */
diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
index e84e769aaddc..bf99cd01be20 100644
--- a/include/linux/usb_usual.h
+++ b/include/linux/usb_usual.h
@@ -72,33 +72,9 @@
enum { US_DO_ALL_FLAGS };
#undef US_FLAG
-/*
- * The bias field for libusual and friends.
- */
-#define USB_US_TYPE_NONE 0
-#define USB_US_TYPE_STOR 1 /* usb-storage */
-#define USB_US_TYPE_UB 2 /* ub */
-
-#define USB_US_TYPE(flags) (((flags) >> 24) & 0xFF)
-#define USB_US_ORIG_FLAGS(flags) ((flags) & 0x00FFFFFF)
-
#include <linux/usb/storage.h>
-/*
- */
extern int usb_usual_ignore_device(struct usb_interface *intf);
extern struct usb_device_id usb_storage_usb_ids[];
-#ifdef CONFIG_USB_LIBUSUAL
-
-extern void usb_usual_set_present(int type);
-extern void usb_usual_clear_present(int type);
-extern int usb_usual_check_type(const struct usb_device_id *, int type);
-#else
-
-#define usb_usual_set_present(t) do { } while(0)
-#define usb_usual_clear_present(t) do { } while(0)
-#define usb_usual_check_type(id, t) (0)
-#endif /* CONFIG_USB_LIBUSUAL */
-
#endif /* __LINUX_USB_USUAL_H */
diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h
index 3b74666be027..4abe28e41cbc 100644
--- a/include/linux/usbdevice_fs.h
+++ b/include/linux/usbdevice_fs.h
@@ -131,6 +131,19 @@ struct usbdevfs_hub_portinfo {
#define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04
#define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08
+/* USBDEVFS_DISCONNECT_CLAIM flags & struct */
+
+/* disconnect-and-claim if the driver matches the driver field */
+#define USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER 0x01
+/* disconnect-and-claim except when the driver matches the driver field */
+#define USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 0x02
+
+struct usbdevfs_disconnect_claim {
+ unsigned int interface;
+ unsigned int flags;
+ char driver[USBDEVFS_MAXDRIVERNAME + 1];
+};
+
#ifdef __KERNEL__
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
@@ -211,5 +224,6 @@ struct usbdevfs_ioctl32 {
#define USBDEVFS_CLAIM_PORT _IOR('U', 24, unsigned int)
#define USBDEVFS_RELEASE_PORT _IOR('U', 25, unsigned int)
#define USBDEVFS_GET_CAPABILITIES _IOR('U', 26, __u32)
+#define USBDEVFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbdevfs_disconnect_claim)
#endif /* _LINUX_USBDEVICE_FS_H */
diff --git a/include/linux/w1-gpio.h b/include/linux/w1-gpio.h
index 3adeff82212f..065e3ae79ab0 100644
--- a/include/linux/w1-gpio.h
+++ b/include/linux/w1-gpio.h
@@ -19,6 +19,7 @@ struct w1_gpio_platform_data {
unsigned int pin;
unsigned int is_open_drain:1;
void (*enable_external_pullup)(int enable);
+ unsigned int ext_pullup_enable_pin;
};
#endif /* _LINUX_W1_GPIO_H */
diff --git a/include/linux/xfrm.h b/include/linux/xfrm.h
index 22e61fdf75a2..28e493b5b94c 100644
--- a/include/linux/xfrm.h
+++ b/include/linux/xfrm.h
@@ -84,6 +84,8 @@ struct xfrm_replay_state {
__u32 bitmap;
};
+#define XFRMA_REPLAY_ESN_MAX 4096
+
struct xfrm_replay_state_esn {
unsigned int bmp_len;
__u32 oseq;
diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h
index 0fedbd8d747a..9fc7114159e8 100644
--- a/include/net/ip6_fib.h
+++ b/include/net/ip6_fib.h
@@ -111,9 +111,8 @@ struct rt6_info {
struct inet6_dev *rt6i_idev;
unsigned long _rt6i_peer;
-#ifdef CONFIG_XFRM
- u32 rt6i_flow_cache_genid;
-#endif
+ u32 rt6i_genid;
+
/* more non-fragment space at head required */
unsigned short rt6i_nfheader_len;
diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h
index ae1cd6c9ba52..fd87963a0ea5 100644
--- a/include/net/net_namespace.h
+++ b/include/net/net_namespace.h
@@ -102,6 +102,7 @@ struct net {
#endif
struct netns_ipvs *ipvs;
struct sock *diag_nlsk;
+ atomic_t rt_genid;
};
@@ -300,5 +301,14 @@ static inline void unregister_net_sysctl_table(struct ctl_table_header *header)
}
#endif
+static inline int rt_genid(struct net *net)
+{
+ return atomic_read(&net->rt_genid);
+}
+
+static inline void rt_genid_bump(struct net *net)
+{
+ atomic_inc(&net->rt_genid);
+}
#endif /* __NET_NET_NAMESPACE_H */
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index 1474dd65c66f..eb24dbccd81e 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -65,7 +65,6 @@ struct netns_ipv4 {
unsigned int sysctl_ping_group_range[2];
long sysctl_tcp_mem[3];
- atomic_t rt_genid;
atomic_t dev_addr_genid;
#ifdef CONFIG_IP_MROUTE
diff --git a/include/net/route.h b/include/net/route.h
index 776a27f1ab78..da22243d2760 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -108,7 +108,7 @@ extern struct ip_rt_acct __percpu *ip_rt_acct;
struct in_device;
extern int ip_rt_init(void);
-extern void rt_cache_flush(struct net *net, int how);
+extern void rt_cache_flush(struct net *net);
extern void rt_flush_dev(struct net_device *dev);
extern struct rtable *__ip_route_output_key(struct net *, struct flowi4 *flp);
extern struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp,
diff --git a/include/net/sock.h b/include/net/sock.h
index 72132aef53fc..adb7da20b5a1 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -1332,7 +1332,7 @@ static inline bool sk_wmem_schedule(struct sock *sk, int size)
}
static inline bool
-sk_rmem_schedule(struct sock *sk, struct sk_buff *skb, unsigned int size)
+sk_rmem_schedule(struct sock *sk, struct sk_buff *skb, int size)
{
if (!sk_has_account(sk))
return true;
diff --git a/include/trace/define_trace.h b/include/trace/define_trace.h
index b0b4eb24d592..1905ca8dd399 100644
--- a/include/trace/define_trace.h
+++ b/include/trace/define_trace.h
@@ -1,5 +1,5 @@
/*
- * Trace files that want to automate creationg of all tracepoints defined
+ * Trace files that want to automate creation of all tracepoints defined
* in their file should include this file. The following are macros that the
* trace file may define:
*
diff --git a/include/trace/events/kmem.h b/include/trace/events/kmem.h
index 5f889f16b0c8..08fa27244da7 100644
--- a/include/trace/events/kmem.h
+++ b/include/trace/events/kmem.h
@@ -214,7 +214,7 @@ TRACE_EVENT(mm_page_alloc,
TP_printk("page=%p pfn=%lu order=%d migratetype=%d gfp_flags=%s",
__entry->page,
- page_to_pfn(__entry->page),
+ __entry->page ? page_to_pfn(__entry->page) : 0,
__entry->order,
__entry->migratetype,
show_gfp_flags(__entry->gfp_flags))
@@ -240,7 +240,7 @@ DECLARE_EVENT_CLASS(mm_page,
TP_printk("page=%p pfn=%lu order=%u migratetype=%d percpu_refill=%d",
__entry->page,
- page_to_pfn(__entry->page),
+ __entry->page ? page_to_pfn(__entry->page) : 0,
__entry->order,
__entry->migratetype,
__entry->order == 0)
diff --git a/include/video/epson1355.h b/include/video/epson1355.h
deleted file mode 100644
index 9759f299499e..000000000000
--- a/include/video/epson1355.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * include/video/epson13xx.h -- Epson 13xx frame buffer
- *
- * Copyright (C) Hewlett-Packard Company. All rights reserved.
- *
- * Written by Christopher Hoover <ch@hpl.hp.com>
- *
- */
-
-#ifndef _EPSON13XX_H_
-#define _EPSON13XX_H_
-
-#define REG_REVISION_CODE 0x00
-#define REG_MEMORY_CONFIG 0x01
-#define REG_PANEL_TYPE 0x02
-#define REG_MOD_RATE 0x03
-#define REG_HORZ_DISP_WIDTH 0x04
-#define REG_HORZ_NONDISP_PERIOD 0x05
-#define REG_HRTC_START_POSITION 0x06
-#define REG_HRTC_PULSE_WIDTH 0x07
-#define REG_VERT_DISP_HEIGHT0 0x08
-#define REG_VERT_DISP_HEIGHT1 0x09
-#define REG_VERT_NONDISP_PERIOD 0x0A
-#define REG_VRTC_START_POSITION 0x0B
-#define REG_VRTC_PULSE_WIDTH 0x0C
-#define REG_DISPLAY_MODE 0x0D
-#define REG_SCRN1_LINE_COMPARE0 0x0E
-#define REG_SCRN1_LINE_COMPARE1 0x0F
-#define REG_SCRN1_DISP_START_ADDR0 0x10
-#define REG_SCRN1_DISP_START_ADDR1 0x11
-#define REG_SCRN1_DISP_START_ADDR2 0x12
-#define REG_SCRN2_DISP_START_ADDR0 0x13
-#define REG_SCRN2_DISP_START_ADDR1 0x14
-#define REG_SCRN2_DISP_START_ADDR2 0x15
-#define REG_MEM_ADDR_OFFSET0 0x16
-#define REG_MEM_ADDR_OFFSET1 0x17
-#define REG_PIXEL_PANNING 0x18
-#define REG_CLOCK_CONFIG 0x19
-#define REG_POWER_SAVE_CONFIG 0x1A
-#define REG_MISC 0x1B
-#define REG_MD_CONFIG_READBACK0 0x1C
-#define REG_MD_CONFIG_READBACK1 0x1D
-#define REG_GPIO_CONFIG0 0x1E
-#define REG_GPIO_CONFIG1 0x1F
-#define REG_GPIO_CONTROL0 0x20
-#define REG_GPIO_CONTROL1 0x21
-#define REG_PERF_ENHANCEMENT0 0x22
-#define REG_PERF_ENHANCEMENT1 0x23
-#define REG_LUT_ADDR 0x24
-#define REG_RESERVED_1 0x25
-#define REG_LUT_DATA 0x26
-#define REG_INK_CURSOR_CONTROL 0x27
-#define REG_CURSOR_X_POSITION0 0x28
-#define REG_CURSOR_X_POSITION1 0x29
-#define REG_CURSOR_Y_POSITION0 0x2A
-#define REG_CURSOR_Y_POSITION1 0x2B
-#define REG_INK_CURSOR_COLOR0_0 0x2C
-#define REG_INK_CURSOR_COLOR0_1 0x2D
-#define REG_INK_CURSOR_COLOR1_0 0x2E
-#define REG_INK_CURSOR_COLOR1_1 0x2F
-#define REG_INK_CURSOR_START_ADDR 0x30
-#define REG_ALTERNATE_FRM 0x31
-
-#endif
diff --git a/include/xen/grant_table.h b/include/xen/grant_table.h
index 11e27c3af3cb..f19fff8650e9 100644
--- a/include/xen/grant_table.h
+++ b/include/xen/grant_table.h
@@ -187,6 +187,7 @@ int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
struct gnttab_map_grant_ref *kmap_ops,
struct page **pages, unsigned int count);
int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
- struct page **pages, unsigned int count, bool clear_pte);
+ struct gnttab_map_grant_ref *kunmap_ops,
+ struct page **pages, unsigned int count);
#endif /* __ASM_GNTTAB_H__ */
diff --git a/include/xen/interface/physdev.h b/include/xen/interface/physdev.h
index 9ce788d8cf49..bfa1d50fe15b 100644
--- a/include/xen/interface/physdev.h
+++ b/include/xen/interface/physdev.h
@@ -258,6 +258,22 @@ struct physdev_pci_device {
uint8_t devfn;
};
+#define PHYSDEVOP_DBGP_RESET_PREPARE 1
+#define PHYSDEVOP_DBGP_RESET_DONE 2
+
+#define PHYSDEVOP_DBGP_BUS_UNKNOWN 0
+#define PHYSDEVOP_DBGP_BUS_PCI 1
+
+#define PHYSDEVOP_dbgp_op 29
+struct physdev_dbgp_op {
+ /* IN */
+ uint8_t op;
+ uint8_t bus;
+ union {
+ struct physdev_pci_device pci;
+ } u;
+};
+
/*
* Notify that some PIRQ-bound event channels have been unmasked.
* ** This command is obsolete since interface version 0x00030202 and is **
diff --git a/init/Kconfig b/init/Kconfig
index af6c7f8ba019..73e4adfa91dc 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -267,6 +267,106 @@ config POSIX_MQUEUE_SYSCTL
depends on SYSCTL
default y
+config FHANDLE
+ bool "open by fhandle syscalls"
+ select EXPORTFS
+ help
+ If you say Y here, a user level program will be able to map
+ file names to handle and then later use the handle for
+ different file system operations. This is useful in implementing
+ userspace file servers, which now track files using handles instead
+ of names. The handle would remain the same even if file names
+ get renamed. Enables open_by_handle_at(2) and name_to_handle_at(2)
+ syscalls.
+
+config AUDIT
+ bool "Auditing support"
+ depends on NET
+ help
+ Enable auditing infrastructure that can be used with another
+ kernel subsystem, such as SELinux (which requires this for
+ logging of avc messages output). Does not do system-call
+ auditing without CONFIG_AUDITSYSCALL.
+
+config AUDITSYSCALL
+ bool "Enable system-call auditing support"
+ depends on AUDIT && (X86 || PPC || S390 || IA64 || UML || SPARC64 || SUPERH || (ARM && AEABI && !OABI_COMPAT))
+ default y if SECURITY_SELINUX
+ help
+ Enable low-overhead system-call auditing infrastructure that
+ can be used independently or with another kernel subsystem,
+ such as SELinux.
+
+config AUDIT_WATCH
+ def_bool y
+ depends on AUDITSYSCALL
+ select FSNOTIFY
+
+config AUDIT_TREE
+ def_bool y
+ depends on AUDITSYSCALL
+ select FSNOTIFY
+
+config AUDIT_LOGINUID_IMMUTABLE
+ bool "Make audit loginuid immutable"
+ depends on AUDIT
+ help
+ The config option toggles if a task setting its loginuid requires
+ CAP_SYS_AUDITCONTROL or if that task should require no special permissions
+ but should instead only allow setting its loginuid if it was never
+ previously set. On systems which use systemd or a similar central
+ process to restart login services this should be set to true. On older
+ systems in which an admin would typically have to directly stop and
+ start processes this should be set to false. Setting this to true allows
+ one to drop potentially dangerous capabilites from the login tasks,
+ but may not be backwards compatible with older init systems.
+
+source "kernel/irq/Kconfig"
+source "kernel/time/Kconfig"
+
+menu "CPU/Task time and stats accounting"
+
+choice
+ prompt "Cputime accounting"
+ default TICK_CPU_ACCOUNTING if !PPC64
+ default VIRT_CPU_ACCOUNTING if PPC64
+
+# Kind of a stub config for the pure tick based cputime accounting
+config TICK_CPU_ACCOUNTING
+ bool "Simple tick based cputime accounting"
+ depends on !S390
+ help
+ This is the basic tick based cputime accounting that maintains
+ statistics about user, system and idle time spent on per jiffies
+ granularity.
+
+ If unsure, say Y.
+
+config VIRT_CPU_ACCOUNTING
+ bool "Deterministic task and CPU time accounting"
+ depends on HAVE_VIRT_CPU_ACCOUNTING
+ help
+ Select this option to enable more accurate task and CPU time
+ accounting. This is done by reading a CPU counter on each
+ kernel entry and exit and on transitions within the kernel
+ between system, softirq and hardirq state, so there is a
+ small performance impact. In the case of s390 or IBM POWER > 5,
+ this also enables accounting of stolen time on logically-partitioned
+ systems.
+
+config IRQ_TIME_ACCOUNTING
+ bool "Fine granularity task level IRQ time accounting"
+ depends on HAVE_IRQ_TIME_ACCOUNTING
+ help
+ Select this option to enable fine granularity task irq time
+ accounting. This is done by reading a timestamp on each
+ transitions between softirq and hardirq state, so there can be a
+ small performance impact.
+
+ If in doubt, say N here.
+
+endchoice
+
config BSD_PROCESS_ACCT
bool "BSD Process Accounting"
help
@@ -292,18 +392,6 @@ config BSD_PROCESS_ACCT_V3
for processing it. A preliminary version of these tools is available
at <http://www.gnu.org/software/acct/>.
-config FHANDLE
- bool "open by fhandle syscalls"
- select EXPORTFS
- help
- If you say Y here, a user level program will be able to map
- file names to handle and then later use the handle for
- different file system operations. This is useful in implementing
- userspace file servers, which now track files using handles instead
- of names. The handle would remain the same even if file names
- get renamed. Enables open_by_handle_at(2) and name_to_handle_at(2)
- syscalls.
-
config TASKSTATS
bool "Export task/process statistics through netlink (EXPERIMENTAL)"
depends on NET
@@ -346,50 +434,7 @@ config TASK_IO_ACCOUNTING
Say N if unsure.
-config AUDIT
- bool "Auditing support"
- depends on NET
- help
- Enable auditing infrastructure that can be used with another
- kernel subsystem, such as SELinux (which requires this for
- logging of avc messages output). Does not do system-call
- auditing without CONFIG_AUDITSYSCALL.
-
-config AUDITSYSCALL
- bool "Enable system-call auditing support"
- depends on AUDIT && (X86 || PPC || S390 || IA64 || UML || SPARC64 || SUPERH || (ARM && AEABI && !OABI_COMPAT))
- default y if SECURITY_SELINUX
- help
- Enable low-overhead system-call auditing infrastructure that
- can be used independently or with another kernel subsystem,
- such as SELinux.
-
-config AUDIT_WATCH
- def_bool y
- depends on AUDITSYSCALL
- select FSNOTIFY
-
-config AUDIT_TREE
- def_bool y
- depends on AUDITSYSCALL
- select FSNOTIFY
-
-config AUDIT_LOGINUID_IMMUTABLE
- bool "Make audit loginuid immutable"
- depends on AUDIT
- help
- The config option toggles if a task setting its loginuid requires
- CAP_SYS_AUDITCONTROL or if that task should require no special permissions
- but should instead only allow setting its loginuid if it was never
- previously set. On systems which use systemd or a similar central
- process to restart login services this should be set to true. On older
- systems in which an admin would typically have to directly stop and
- start processes this should be set to false. Setting this to true allows
- one to drop potentially dangerous capabilites from the login tasks,
- but may not be backwards compatible with older init systems.
-
-source "kernel/irq/Kconfig"
-source "kernel/time/Kconfig"
+endmenu # "CPU/Task time and stats accounting"
menu "RCU Subsystem"
@@ -441,6 +486,24 @@ config PREEMPT_RCU
This option enables preemptible-RCU code that is common between
the TREE_PREEMPT_RCU and TINY_PREEMPT_RCU implementations.
+config RCU_USER_QS
+ bool "Consider userspace as in RCU extended quiescent state"
+ depends on HAVE_RCU_USER_QS && SMP
+ help
+ This option sets hooks on kernel / userspace boundaries and
+ puts RCU in extended quiescent state when the CPU runs in
+ userspace. It means that when a CPU runs in userspace, it is
+ excluded from the global RCU state machine and thus doesn't
+ to keep the timer tick on for RCU.
+
+config RCU_USER_QS_FORCE
+ bool "Force userspace extended QS by default"
+ depends on RCU_USER_QS
+ help
+ Set the hooks in user/kernel boundaries by default in order to
+ test this feature that treats userspace as an extended quiescent
+ state until we have a real user like a full adaptive nohz option.
+
config RCU_FANOUT
int "Tree-based hierarchical RCU fanout value"
range 2 64 if 64BIT
@@ -1153,7 +1216,8 @@ menuconfig EXPERT
config UID16
bool "Enable 16-bit UID system calls" if EXPERT
- depends on ARM || BLACKFIN || CRIS || FRV || H8300 || X86_32 || M68K || (S390 && !64BIT) || SUPERH || SPARC32 || (SPARC64 && COMPAT) || UML || (X86_64 && IA32_EMULATION)
+ depends on ARM || BLACKFIN || CRIS || FRV || H8300 || X86_32 || M68K || (S390 && !64BIT) || SUPERH || SPARC32 || (SPARC64 && COMPAT) || UML || (X86_64 && IA32_EMULATION) \
+ || AARCH32_EMULATION
default y
help
This enables the legacy 16-bit UID syscall wrappers.
@@ -1201,13 +1265,7 @@ config KALLSYMS_ALL
Say N unless you really need all symbols.
config HOTPLUG
- bool "Support for hot-pluggable devices" if EXPERT
- default y
- help
- This option is provided for the case where no hotplug or uevent
- capabilities is wanted by the kernel. You should only consider
- disabling this option for embedded systems that do not use modules, a
- dynamic /dev tree, or dynamic device discovery. Just say Y.
+ def_bool y
config PRINTK
default y
diff --git a/init/main.c b/init/main.c
index b28673087ac0..db34c0ec4711 100644
--- a/init/main.c
+++ b/init/main.c
@@ -631,6 +631,11 @@ asmlinkage void __init start_kernel(void)
acpi_early_init(); /* before LAPIC and SMP init */
sfi_init_late();
+ if (efi_enabled) {
+ efi_late_init();
+ efi_free_boot_services();
+ }
+
ftrace_init();
/* Do the rest non-__init'ed, we're now alive */
diff --git a/kernel/Kconfig.locks b/kernel/Kconfig.locks
index 2251882daf53..44511d100eaa 100644
--- a/kernel/Kconfig.locks
+++ b/kernel/Kconfig.locks
@@ -87,6 +87,9 @@ config ARCH_INLINE_WRITE_UNLOCK_IRQ
config ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE
bool
+config UNINLINE_SPIN_UNLOCK
+ bool
+
#
# lock_* functions are inlined when:
# - DEBUG_SPINLOCK=n and GENERIC_LOCKBREAK=n and ARCH_INLINE_*LOCK=y
@@ -103,100 +106,120 @@ config ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE
# - DEBUG_SPINLOCK=n and ARCH_INLINE_*LOCK=y
#
+if !DEBUG_SPINLOCK
+
config INLINE_SPIN_TRYLOCK
- def_bool !DEBUG_SPINLOCK && ARCH_INLINE_SPIN_TRYLOCK
+ def_bool y
+ depends on ARCH_INLINE_SPIN_TRYLOCK
config INLINE_SPIN_TRYLOCK_BH
- def_bool !DEBUG_SPINLOCK && ARCH_INLINE_SPIN_TRYLOCK_BH
+ def_bool y
+ depends on ARCH_INLINE_SPIN_TRYLOCK_BH
config INLINE_SPIN_LOCK
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && ARCH_INLINE_SPIN_LOCK
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_SPIN_LOCK
config INLINE_SPIN_LOCK_BH
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && \
- ARCH_INLINE_SPIN_LOCK_BH
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_SPIN_LOCK_BH
config INLINE_SPIN_LOCK_IRQ
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && \
- ARCH_INLINE_SPIN_LOCK_IRQ
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_SPIN_LOCK_IRQ
config INLINE_SPIN_LOCK_IRQSAVE
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && \
- ARCH_INLINE_SPIN_LOCK_IRQSAVE
-
-config UNINLINE_SPIN_UNLOCK
- bool
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_SPIN_LOCK_IRQSAVE
config INLINE_SPIN_UNLOCK_BH
- def_bool !DEBUG_SPINLOCK && ARCH_INLINE_SPIN_UNLOCK_BH
+ def_bool y
+ depends on ARCH_INLINE_SPIN_UNLOCK_BH
config INLINE_SPIN_UNLOCK_IRQ
- def_bool !DEBUG_SPINLOCK && (!PREEMPT || ARCH_INLINE_SPIN_UNLOCK_BH)
+ def_bool y
+ depends on !PREEMPT || ARCH_INLINE_SPIN_UNLOCK_BH
config INLINE_SPIN_UNLOCK_IRQRESTORE
- def_bool !DEBUG_SPINLOCK && ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE
+ def_bool y
+ depends on ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE
config INLINE_READ_TRYLOCK
- def_bool !DEBUG_SPINLOCK && ARCH_INLINE_READ_TRYLOCK
+ def_bool y
+ depends on ARCH_INLINE_READ_TRYLOCK
config INLINE_READ_LOCK
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && ARCH_INLINE_READ_LOCK
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_READ_LOCK
config INLINE_READ_LOCK_BH
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && \
- ARCH_INLINE_READ_LOCK_BH
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_READ_LOCK_BH
config INLINE_READ_LOCK_IRQ
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && \
- ARCH_INLINE_READ_LOCK_IRQ
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_READ_LOCK_IRQ
config INLINE_READ_LOCK_IRQSAVE
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && \
- ARCH_INLINE_READ_LOCK_IRQSAVE
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_READ_LOCK_IRQSAVE
config INLINE_READ_UNLOCK
- def_bool !DEBUG_SPINLOCK && (!PREEMPT || ARCH_INLINE_READ_UNLOCK)
+ def_bool y
+ depends on !PREEMPT || ARCH_INLINE_READ_UNLOCK
config INLINE_READ_UNLOCK_BH
- def_bool !DEBUG_SPINLOCK && ARCH_INLINE_READ_UNLOCK_BH
+ def_bool y
+ depends on ARCH_INLINE_READ_UNLOCK_BH
config INLINE_READ_UNLOCK_IRQ
- def_bool !DEBUG_SPINLOCK && (!PREEMPT || ARCH_INLINE_READ_UNLOCK_BH)
+ def_bool y
+ depends on !PREEMPT || ARCH_INLINE_READ_UNLOCK_BH
config INLINE_READ_UNLOCK_IRQRESTORE
- def_bool !DEBUG_SPINLOCK && ARCH_INLINE_READ_UNLOCK_IRQRESTORE
+ def_bool y
+ depends on ARCH_INLINE_READ_UNLOCK_IRQRESTORE
config INLINE_WRITE_TRYLOCK
- def_bool !DEBUG_SPINLOCK && ARCH_INLINE_WRITE_TRYLOCK
+ def_bool y
+ depends on ARCH_INLINE_WRITE_TRYLOCK
config INLINE_WRITE_LOCK
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && ARCH_INLINE_WRITE_LOCK
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_WRITE_LOCK
config INLINE_WRITE_LOCK_BH
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && \
- ARCH_INLINE_WRITE_LOCK_BH
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_WRITE_LOCK_BH
config INLINE_WRITE_LOCK_IRQ
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && \
- ARCH_INLINE_WRITE_LOCK_IRQ
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_WRITE_LOCK_IRQ
config INLINE_WRITE_LOCK_IRQSAVE
- def_bool !DEBUG_SPINLOCK && !GENERIC_LOCKBREAK && \
- ARCH_INLINE_WRITE_LOCK_IRQSAVE
+ def_bool y
+ depends on !GENERIC_LOCKBREAK && ARCH_INLINE_WRITE_LOCK_IRQSAVE
config INLINE_WRITE_UNLOCK
- def_bool !DEBUG_SPINLOCK && (!PREEMPT || ARCH_INLINE_WRITE_UNLOCK)
+ def_bool y
+ depends on !PREEMPT || ARCH_INLINE_WRITE_UNLOCK
config INLINE_WRITE_UNLOCK_BH
- def_bool !DEBUG_SPINLOCK && ARCH_INLINE_WRITE_UNLOCK_BH
+ def_bool y
+ depends on ARCH_INLINE_WRITE_UNLOCK_BH
config INLINE_WRITE_UNLOCK_IRQ
- def_bool !DEBUG_SPINLOCK && (!PREEMPT || ARCH_INLINE_WRITE_UNLOCK_BH)
+ def_bool y
+ depends on !PREEMPT || ARCH_INLINE_WRITE_UNLOCK_BH
config INLINE_WRITE_UNLOCK_IRQRESTORE
- def_bool !DEBUG_SPINLOCK && ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE
+ def_bool y
+ depends on ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE
+
+endif
config MUTEX_SPIN_ON_OWNER
- def_bool SMP && !DEBUG_MUTEXES
+ def_bool y
+ depends on SMP && !DEBUG_MUTEXES
diff --git a/kernel/Makefile b/kernel/Makefile
index c0cc67ad764c..5404911eaee9 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -10,7 +10,7 @@ obj-y = fork.o exec_domain.o panic.o printk.o \
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o mutex.o \
hrtimer.o rwsem.o nsproxy.o srcu.o semaphore.o \
notifier.o ksysfs.o cred.o \
- async.o range.o groups.o lglock.o
+ async.o range.o groups.o lglock.o smpboot.o
ifdef CONFIG_FUNCTION_TRACER
# Do not trace debug files and internal ftrace files
@@ -46,7 +46,6 @@ obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o
obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o
obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
obj-$(CONFIG_SMP) += smp.o
-obj-$(CONFIG_SMP) += smpboot.o
ifneq ($(CONFIG_SMP),y)
obj-y += up.o
endif
@@ -98,7 +97,7 @@ obj-$(CONFIG_COMPAT_BINFMT_ELF) += elfcore.o
obj-$(CONFIG_BINFMT_ELF_FDPIC) += elfcore.o
obj-$(CONFIG_FUNCTION_TRACER) += trace/
obj-$(CONFIG_TRACING) += trace/
-obj-$(CONFIG_X86_DS) += trace/
+obj-$(CONFIG_TRACE_CLOCK) += trace/
obj-$(CONFIG_RING_BUFFER) += trace/
obj-$(CONFIG_TRACEPOINTS) += trace/
obj-$(CONFIG_IRQ_WORK) += irq_work.o
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 14d32588cccd..f560598807c1 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -280,12 +280,13 @@ static int __ref _cpu_down(unsigned int cpu, int tasks_frozen)
__func__, cpu);
goto out_release;
}
+ smpboot_park_threads(cpu);
err = __stop_machine(take_cpu_down, &tcd_param, cpumask_of(cpu));
if (err) {
/* CPU didn't die: tell everyone. Can't complain. */
+ smpboot_unpark_threads(cpu);
cpu_notify_nofail(CPU_DOWN_FAILED | mod, hcpu);
-
goto out_release;
}
BUG_ON(cpu_online(cpu));
@@ -354,6 +355,10 @@ static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen)
goto out;
}
+ ret = smpboot_create_threads(cpu);
+ if (ret)
+ goto out;
+
ret = __cpu_notify(CPU_UP_PREPARE | mod, hcpu, -1, &nr_calls);
if (ret) {
nr_calls--;
@@ -368,6 +373,9 @@ static int __cpuinit _cpu_up(unsigned int cpu, int tasks_frozen)
goto out_notify;
BUG_ON(!cpu_online(cpu));
+ /* Wake the per cpu threads */
+ smpboot_unpark_threads(cpu);
+
/* Now call notifier in preparation. */
cpu_notify(CPU_ONLINE | mod, hcpu);
@@ -439,14 +447,6 @@ EXPORT_SYMBOL_GPL(cpu_up);
#ifdef CONFIG_PM_SLEEP_SMP
static cpumask_var_t frozen_cpus;
-void __weak arch_disable_nonboot_cpus_begin(void)
-{
-}
-
-void __weak arch_disable_nonboot_cpus_end(void)
-{
-}
-
int disable_nonboot_cpus(void)
{
int cpu, first_cpu, error = 0;
@@ -458,7 +458,6 @@ int disable_nonboot_cpus(void)
* with the userspace trying to use the CPU hotplug at the same time
*/
cpumask_clear(frozen_cpus);
- arch_disable_nonboot_cpus_begin();
printk("Disabling non-boot CPUs ...\n");
for_each_online_cpu(cpu) {
@@ -474,8 +473,6 @@ int disable_nonboot_cpus(void)
}
}
- arch_disable_nonboot_cpus_end();
-
if (!error) {
BUG_ON(num_online_cpus() > 1);
/* Make sure the CPUs won't be enabled by someone else */
diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c
index 0557f24c6bca..17e073c309e6 100644
--- a/kernel/debug/debug_core.c
+++ b/kernel/debug/debug_core.c
@@ -672,6 +672,10 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)
{
struct kgdb_state kgdb_var;
struct kgdb_state *ks = &kgdb_var;
+ int ret = 0;
+
+ if (arch_kgdb_ops.enable_nmi)
+ arch_kgdb_ops.enable_nmi(0);
ks->cpu = raw_smp_processor_id();
ks->ex_vector = evector;
@@ -681,11 +685,15 @@ kgdb_handle_exception(int evector, int signo, int ecode, struct pt_regs *regs)
ks->linux_regs = regs;
if (kgdb_reenter_check(ks))
- return 0; /* Ouch, double exception ! */
+ goto out; /* Ouch, double exception ! */
if (kgdb_info[ks->cpu].enter_kgdb != 0)
- return 0;
+ goto out;
- return kgdb_cpu_enter(ks, regs, DCPU_WANT_MASTER);
+ ret = kgdb_cpu_enter(ks, regs, DCPU_WANT_MASTER);
+out:
+ if (arch_kgdb_ops.enable_nmi)
+ arch_kgdb_ops.enable_nmi(1);
+ return ret;
}
int kgdb_nmicallback(int cpu, void *regs)
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c
index 31df1706b9a9..1261dc7eaeb9 100644
--- a/kernel/debug/kdb/kdb_main.c
+++ b/kernel/debug/kdb/kdb_main.c
@@ -21,6 +21,7 @@
#include <linux/smp.h>
#include <linux/utsname.h>
#include <linux/vmalloc.h>
+#include <linux/atomic.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/init.h>
@@ -2107,6 +2108,32 @@ static int kdb_dmesg(int argc, const char **argv)
return 0;
}
#endif /* CONFIG_PRINTK */
+
+/* Make sure we balance enable/disable calls, must disable first. */
+static atomic_t kdb_nmi_disabled;
+
+static int kdb_disable_nmi(int argc, const char *argv[])
+{
+ if (atomic_read(&kdb_nmi_disabled))
+ return 0;
+ atomic_set(&kdb_nmi_disabled, 1);
+ arch_kgdb_ops.enable_nmi(0);
+ return 0;
+}
+
+static int kdb_param_enable_nmi(const char *val, const struct kernel_param *kp)
+{
+ if (!atomic_add_unless(&kdb_nmi_disabled, -1, 0))
+ return -EINVAL;
+ arch_kgdb_ops.enable_nmi(1);
+ return 0;
+}
+
+static const struct kernel_param_ops kdb_param_ops_enable_nmi = {
+ .set = kdb_param_enable_nmi,
+};
+module_param_cb(enable_nmi, &kdb_param_ops_enable_nmi, NULL, 0600);
+
/*
* kdb_cpu - This function implements the 'cpu' command.
* cpu [<cpunum>]
@@ -2851,6 +2878,10 @@ static void __init kdb_inittab(void)
kdb_register_repeat("dmesg", kdb_dmesg, "[lines]",
"Display syslog buffer", 0, KDB_REPEAT_NONE);
#endif
+ if (arch_kgdb_ops.enable_nmi) {
+ kdb_register_repeat("disable_nmi", kdb_disable_nmi, "",
+ "Disable NMI entry to KDB", 0, KDB_REPEAT_NONE);
+ }
kdb_register_repeat("defcmd", kdb_defcmd, "name \"usage\" \"help\"",
"Define a set of commands, down to endefcmd", 0, KDB_REPEAT_NONE);
kdb_register_repeat("kill", kdb_kill, "<-signal> <pid>",
diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c
index 98d4597f43d6..c77206184b8b 100644
--- a/kernel/events/callchain.c
+++ b/kernel/events/callchain.c
@@ -159,6 +159,11 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs)
int rctx;
struct perf_callchain_entry *entry;
+ int kernel = !event->attr.exclude_callchain_kernel;
+ int user = !event->attr.exclude_callchain_user;
+
+ if (!kernel && !user)
+ return NULL;
entry = get_callchain_entry(&rctx);
if (rctx == -1)
@@ -169,24 +174,29 @@ perf_callchain(struct perf_event *event, struct pt_regs *regs)
entry->nr = 0;
- if (!user_mode(regs)) {
+ if (kernel && !user_mode(regs)) {
perf_callchain_store(entry, PERF_CONTEXT_KERNEL);
perf_callchain_kernel(entry, regs);
- if (current->mm)
- regs = task_pt_regs(current);
- else
- regs = NULL;
}
- if (regs) {
- /*
- * Disallow cross-task user callchains.
- */
- if (event->ctx->task && event->ctx->task != current)
- goto exit_put;
-
- perf_callchain_store(entry, PERF_CONTEXT_USER);
- perf_callchain_user(entry, regs);
+ if (user) {
+ if (!user_mode(regs)) {
+ if (current->mm)
+ regs = task_pt_regs(current);
+ else
+ regs = NULL;
+ }
+
+ if (regs) {
+ /*
+ * Disallow cross-task user callchains.
+ */
+ if (event->ctx->task && event->ctx->task != current)
+ goto exit_put;
+
+ perf_callchain_store(entry, PERF_CONTEXT_USER);
+ perf_callchain_user(entry, regs);
+ }
}
exit_put:
diff --git a/kernel/events/core.c b/kernel/events/core.c
index 7fee567153f0..7b9df353ba1b 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -36,6 +36,7 @@
#include <linux/perf_event.h>
#include <linux/ftrace_event.h>
#include <linux/hw_breakpoint.h>
+#include <linux/mm_types.h>
#include "internal.h"
@@ -3764,6 +3765,132 @@ int perf_unregister_guest_info_callbacks(struct perf_guest_info_callbacks *cbs)
}
EXPORT_SYMBOL_GPL(perf_unregister_guest_info_callbacks);
+static void
+perf_output_sample_regs(struct perf_output_handle *handle,
+ struct pt_regs *regs, u64 mask)
+{
+ int bit;
+
+ for_each_set_bit(bit, (const unsigned long *) &mask,
+ sizeof(mask) * BITS_PER_BYTE) {
+ u64 val;
+
+ val = perf_reg_value(regs, bit);
+ perf_output_put(handle, val);
+ }
+}
+
+static void perf_sample_regs_user(struct perf_regs_user *regs_user,
+ struct pt_regs *regs)
+{
+ if (!user_mode(regs)) {
+ if (current->mm)
+ regs = task_pt_regs(current);
+ else
+ regs = NULL;
+ }
+
+ if (regs) {
+ regs_user->regs = regs;
+ regs_user->abi = perf_reg_abi(current);
+ }
+}
+
+/*
+ * Get remaining task size from user stack pointer.
+ *
+ * It'd be better to take stack vma map and limit this more
+ * precisly, but there's no way to get it safely under interrupt,
+ * so using TASK_SIZE as limit.
+ */
+static u64 perf_ustack_task_size(struct pt_regs *regs)
+{
+ unsigned long addr = perf_user_stack_pointer(regs);
+
+ if (!addr || addr >= TASK_SIZE)
+ return 0;
+
+ return TASK_SIZE - addr;
+}
+
+static u16
+perf_sample_ustack_size(u16 stack_size, u16 header_size,
+ struct pt_regs *regs)
+{
+ u64 task_size;
+
+ /* No regs, no stack pointer, no dump. */
+ if (!regs)
+ return 0;
+
+ /*
+ * Check if we fit in with the requested stack size into the:
+ * - TASK_SIZE
+ * If we don't, we limit the size to the TASK_SIZE.
+ *
+ * - remaining sample size
+ * If we don't, we customize the stack size to
+ * fit in to the remaining sample size.
+ */
+
+ task_size = min((u64) USHRT_MAX, perf_ustack_task_size(regs));
+ stack_size = min(stack_size, (u16) task_size);
+
+ /* Current header size plus static size and dynamic size. */
+ header_size += 2 * sizeof(u64);
+
+ /* Do we fit in with the current stack dump size? */
+ if ((u16) (header_size + stack_size) < header_size) {
+ /*
+ * If we overflow the maximum size for the sample,
+ * we customize the stack dump size to fit in.
+ */
+ stack_size = USHRT_MAX - header_size - sizeof(u64);
+ stack_size = round_up(stack_size, sizeof(u64));
+ }
+
+ return stack_size;
+}
+
+static void
+perf_output_sample_ustack(struct perf_output_handle *handle, u64 dump_size,
+ struct pt_regs *regs)
+{
+ /* Case of a kernel thread, nothing to dump */
+ if (!regs) {
+ u64 size = 0;
+ perf_output_put(handle, size);
+ } else {
+ unsigned long sp;
+ unsigned int rem;
+ u64 dyn_size;
+
+ /*
+ * We dump:
+ * static size
+ * - the size requested by user or the best one we can fit
+ * in to the sample max size
+ * data
+ * - user stack dump data
+ * dynamic size
+ * - the actual dumped size
+ */
+
+ /* Static size. */
+ perf_output_put(handle, dump_size);
+
+ /* Data. */
+ sp = perf_user_stack_pointer(regs);
+ rem = __output_copy_user(handle, (void *) sp, dump_size);
+ dyn_size = dump_size - rem;
+
+ perf_output_skip(handle, rem);
+
+ /* Dynamic size. */
+ perf_output_put(handle, dyn_size);
+ }
+}
+
static void __perf_event_header__init_id(struct perf_event_header *header,
struct perf_sample_data *data,
struct perf_event *event)
@@ -4024,6 +4151,28 @@ void perf_output_sample(struct perf_output_handle *handle,
perf_output_put(handle, nr);
}
}
+
+ if (sample_type & PERF_SAMPLE_REGS_USER) {
+ u64 abi = data->regs_user.abi;
+
+ /*
+ * If there are no regs to dump, notice it through
+ * first u64 being zero (PERF_SAMPLE_REGS_ABI_NONE).
+ */
+ perf_output_put(handle, abi);
+
+ if (abi) {
+ u64 mask = event->attr.sample_regs_user;
+ perf_output_sample_regs(handle,
+ data->regs_user.regs,
+ mask);
+ }
+ }
+
+ if (sample_type & PERF_SAMPLE_STACK_USER)
+ perf_output_sample_ustack(handle,
+ data->stack_user_size,
+ data->regs_user.regs);
}
void perf_prepare_sample(struct perf_event_header *header,
@@ -4075,6 +4224,49 @@ void perf_prepare_sample(struct perf_event_header *header,
}
header->size += size;
}
+
+ if (sample_type & PERF_SAMPLE_REGS_USER) {
+ /* regs dump ABI info */
+ int size = sizeof(u64);
+
+ perf_sample_regs_user(&data->regs_user, regs);
+
+ if (data->regs_user.regs) {
+ u64 mask = event->attr.sample_regs_user;
+ size += hweight64(mask) * sizeof(u64);
+ }
+
+ header->size += size;
+ }
+
+ if (sample_type & PERF_SAMPLE_STACK_USER) {
+ /*
+ * Either we need PERF_SAMPLE_STACK_USER bit to be allways
+ * processed as the last one or have additional check added
+ * in case new sample type is added, because we could eat
+ * up the rest of the sample size.
+ */
+ struct perf_regs_user *uregs = &data->regs_user;
+ u16 stack_size = event->attr.sample_stack_user;
+ u16 size = sizeof(u64);
+
+ if (!uregs->abi)
+ perf_sample_regs_user(uregs, regs);
+
+ stack_size = perf_sample_ustack_size(stack_size, header->size,
+ uregs->regs);
+
+ /*
+ * If there is something to dump, add space for the dump
+ * itself and for the field that tells the dynamic size,
+ * which is how many have been actually dumped.
+ */
+ if (stack_size)
+ size += sizeof(u64) + stack_size;
+
+ data->stack_user_size = stack_size;
+ header->size += size;
+ }
}
static void perf_event_output(struct perf_event *event,
@@ -6151,6 +6343,28 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,
attr->branch_sample_type = mask;
}
}
+
+ if (attr->sample_type & PERF_SAMPLE_REGS_USER) {
+ ret = perf_reg_validate(attr->sample_regs_user);
+ if (ret)
+ return ret;
+ }
+
+ if (attr->sample_type & PERF_SAMPLE_STACK_USER) {
+ if (!arch_perf_have_user_stack_dump())
+ return -ENOSYS;
+
+ /*
+ * We have __u32 type for the size, but so far
+ * we can only use __u16 as maximum due to the
+ * __u16 sample size limit.
+ */
+ if (attr->sample_stack_user >= USHRT_MAX)
+ ret = -EINVAL;
+ else if (!IS_ALIGNED(attr->sample_stack_user, sizeof(u64)))
+ ret = -EINVAL;
+ }
+
out:
return ret;
diff --git a/kernel/events/internal.h b/kernel/events/internal.h
index a096c19f2c2a..d56a64c99a8b 100644
--- a/kernel/events/internal.h
+++ b/kernel/events/internal.h
@@ -2,6 +2,7 @@
#define _KERNEL_EVENTS_INTERNAL_H
#include <linux/hardirq.h>
+#include <linux/uaccess.h>
/* Buffer handling */
@@ -76,30 +77,53 @@ static inline unsigned long perf_data_size(struct ring_buffer *rb)
return rb->nr_pages << (PAGE_SHIFT + page_order(rb));
}
-static inline void
-__output_copy(struct perf_output_handle *handle,
- const void *buf, unsigned int len)
+#define DEFINE_OUTPUT_COPY(func_name, memcpy_func) \
+static inline unsigned int \
+func_name(struct perf_output_handle *handle, \
+ const void *buf, unsigned int len) \
+{ \
+ unsigned long size, written; \
+ \
+ do { \
+ size = min_t(unsigned long, handle->size, len); \
+ \
+ written = memcpy_func(handle->addr, buf, size); \
+ \
+ len -= written; \
+ handle->addr += written; \
+ buf += written; \
+ handle->size -= written; \
+ if (!handle->size) { \
+ struct ring_buffer *rb = handle->rb; \
+ \
+ handle->page++; \
+ handle->page &= rb->nr_pages - 1; \
+ handle->addr = rb->data_pages[handle->page]; \
+ handle->size = PAGE_SIZE << page_order(rb); \
+ } \
+ } while (len && written == size); \
+ \
+ return len; \
+}
+
+static inline int memcpy_common(void *dst, const void *src, size_t n)
{
- do {
- unsigned long size = min_t(unsigned long, handle->size, len);
-
- memcpy(handle->addr, buf, size);
-
- len -= size;
- handle->addr += size;
- buf += size;
- handle->size -= size;
- if (!handle->size) {
- struct ring_buffer *rb = handle->rb;
-
- handle->page++;
- handle->page &= rb->nr_pages - 1;
- handle->addr = rb->data_pages[handle->page];
- handle->size = PAGE_SIZE << page_order(rb);
- }
- } while (len);
+ memcpy(dst, src, n);
+ return n;
}
+DEFINE_OUTPUT_COPY(__output_copy, memcpy_common)
+
+#define MEMCPY_SKIP(dst, src, n) (n)
+
+DEFINE_OUTPUT_COPY(__output_skip, MEMCPY_SKIP)
+
+#ifndef arch_perf_out_copy_user
+#define arch_perf_out_copy_user __copy_from_user_inatomic
+#endif
+
+DEFINE_OUTPUT_COPY(__output_copy_user, arch_perf_out_copy_user)
+
/* Callchain handling */
extern struct perf_callchain_entry *
perf_callchain(struct perf_event *event, struct pt_regs *regs);
@@ -134,4 +158,20 @@ static inline void put_recursion_context(int *recursion, int rctx)
recursion[rctx]--;
}
+#ifdef CONFIG_HAVE_PERF_USER_STACK_DUMP
+static inline bool arch_perf_have_user_stack_dump(void)
+{
+ return true;
+}
+
+#define perf_user_stack_pointer(regs) user_stack_pointer(regs)
+#else
+static inline bool arch_perf_have_user_stack_dump(void)
+{
+ return false;
+}
+
+#define perf_user_stack_pointer(regs) 0
+#endif /* CONFIG_HAVE_PERF_USER_STACK_DUMP */
+
#endif /* _KERNEL_EVENTS_INTERNAL_H */
diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c
index 6ddaba43fb7a..23cb34ff3973 100644
--- a/kernel/events/ring_buffer.c
+++ b/kernel/events/ring_buffer.c
@@ -182,10 +182,16 @@ out:
return -ENOSPC;
}
-void perf_output_copy(struct perf_output_handle *handle,
+unsigned int perf_output_copy(struct perf_output_handle *handle,
const void *buf, unsigned int len)
{
- __output_copy(handle, buf, len);
+ return __output_copy(handle, buf, len);
+}
+
+unsigned int perf_output_skip(struct perf_output_handle *handle,
+ unsigned int len)
+{
+ return __output_skip(handle, NULL, len);
}
void perf_output_end(struct perf_output_handle *handle)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index c08a22d02f72..912ef48d28ab 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -280,12 +280,10 @@ static int read_opcode(struct mm_struct *mm, unsigned long vaddr, uprobe_opcode_
if (ret <= 0)
return ret;
- lock_page(page);
vaddr_new = kmap_atomic(page);
vaddr &= ~PAGE_MASK;
memcpy(opcode, vaddr_new + vaddr, UPROBE_SWBP_INSN_SIZE);
kunmap_atomic(vaddr_new);
- unlock_page(page);
put_page(page);
@@ -334,7 +332,7 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned
*/
result = is_swbp_at_addr(mm, vaddr);
if (result == 1)
- return -EEXIST;
+ return 0;
if (result)
return result;
@@ -347,24 +345,22 @@ int __weak set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned
* @mm: the probed process address space.
* @auprobe: arch specific probepoint information.
* @vaddr: the virtual address to insert the opcode.
- * @verify: if true, verify existance of breakpoint instruction.
*
* For mm @mm, restore the original opcode (opcode) at @vaddr.
* Return 0 (success) or a negative errno.
*/
int __weak
-set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr, bool verify)
+set_orig_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, unsigned long vaddr)
{
- if (verify) {
- int result;
+ int result;
+
+ result = is_swbp_at_addr(mm, vaddr);
+ if (!result)
+ return -EINVAL;
- result = is_swbp_at_addr(mm, vaddr);
- if (!result)
- return -EINVAL;
+ if (result != 1)
+ return result;
- if (result != 1)
- return result;
- }
return write_opcode(auprobe, mm, vaddr, *(uprobe_opcode_t *)auprobe->insn);
}
@@ -415,11 +411,10 @@ static struct uprobe *__find_uprobe(struct inode *inode, loff_t offset)
static struct uprobe *find_uprobe(struct inode *inode, loff_t offset)
{
struct uprobe *uprobe;
- unsigned long flags;
- spin_lock_irqsave(&uprobes_treelock, flags);
+ spin_lock(&uprobes_treelock);
uprobe = __find_uprobe(inode, offset);
- spin_unlock_irqrestore(&uprobes_treelock, flags);
+ spin_unlock(&uprobes_treelock);
return uprobe;
}
@@ -466,12 +461,11 @@ static struct uprobe *__insert_uprobe(struct uprobe *uprobe)
*/
static struct uprobe *insert_uprobe(struct uprobe *uprobe)
{
- unsigned long flags;
struct uprobe *u;
- spin_lock_irqsave(&uprobes_treelock, flags);
+ spin_lock(&uprobes_treelock);
u = __insert_uprobe(uprobe);
- spin_unlock_irqrestore(&uprobes_treelock, flags);
+ spin_unlock(&uprobes_treelock);
/* For now assume that the instruction need not be single-stepped */
uprobe->flags |= UPROBE_SKIP_SSTEP;
@@ -649,6 +643,7 @@ static int
install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
struct vm_area_struct *vma, unsigned long vaddr)
{
+ bool first_uprobe;
int ret;
/*
@@ -659,7 +654,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
* Hence behave as if probe already existed.
*/
if (!uprobe->consumers)
- return -EEXIST;
+ return 0;
if (!(uprobe->flags & UPROBE_COPY_INSN)) {
ret = copy_insn(uprobe, vma->vm_file);
@@ -681,17 +676,18 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
}
/*
- * Ideally, should be updating the probe count after the breakpoint
- * has been successfully inserted. However a thread could hit the
- * breakpoint we just inserted even before the probe count is
- * incremented. If this is the first breakpoint placed, breakpoint
- * notifier might ignore uprobes and pass the trap to the thread.
- * Hence increment before and decrement on failure.
+ * set MMF_HAS_UPROBES in advance for uprobe_pre_sstep_notifier(),
+ * the task can hit this breakpoint right after __replace_page().
*/
- atomic_inc(&mm->uprobes_state.count);
+ first_uprobe = !test_bit(MMF_HAS_UPROBES, &mm->flags);
+ if (first_uprobe)
+ set_bit(MMF_HAS_UPROBES, &mm->flags);
+
ret = set_swbp(&uprobe->arch, mm, vaddr);
- if (ret)
- atomic_dec(&mm->uprobes_state.count);
+ if (!ret)
+ clear_bit(MMF_RECALC_UPROBES, &mm->flags);
+ else if (first_uprobe)
+ clear_bit(MMF_HAS_UPROBES, &mm->flags);
return ret;
}
@@ -699,8 +695,12 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,
static void
remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vaddr)
{
- if (!set_orig_insn(&uprobe->arch, mm, vaddr, true))
- atomic_dec(&mm->uprobes_state.count);
+ /* can happen if uprobe_register() fails */
+ if (!test_bit(MMF_HAS_UPROBES, &mm->flags))
+ return;
+
+ set_bit(MMF_RECALC_UPROBES, &mm->flags);
+ set_orig_insn(&uprobe->arch, mm, vaddr);
}
/*
@@ -710,11 +710,9 @@ remove_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, unsigned long vad
*/
static void delete_uprobe(struct uprobe *uprobe)
{
- unsigned long flags;
-
- spin_lock_irqsave(&uprobes_treelock, flags);
+ spin_lock(&uprobes_treelock);
rb_erase(&uprobe->rb_node, &uprobes_tree);
- spin_unlock_irqrestore(&uprobes_treelock, flags);
+ spin_unlock(&uprobes_treelock);
iput(uprobe->inode);
put_uprobe(uprobe);
atomic_dec(&uprobe_events);
@@ -831,17 +829,11 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)
vaddr_to_offset(vma, info->vaddr) != uprobe->offset)
goto unlock;
- if (is_register) {
+ if (is_register)
err = install_breakpoint(uprobe, mm, vma, info->vaddr);
- /*
- * We can race against uprobe_mmap(), see the
- * comment near uprobe_hash().
- */
- if (err == -EEXIST)
- err = 0;
- } else {
+ else
remove_breakpoint(uprobe, mm, info->vaddr);
- }
+
unlock:
up_write(&mm->mmap_sem);
free:
@@ -908,7 +900,8 @@ int uprobe_register(struct inode *inode, loff_t offset, struct uprobe_consumer *
}
mutex_unlock(uprobes_hash(inode));
- put_uprobe(uprobe);
+ if (uprobe)
+ put_uprobe(uprobe);
return ret;
}
@@ -978,7 +971,6 @@ static void build_probe_list(struct inode *inode,
struct list_head *head)
{
loff_t min, max;
- unsigned long flags;
struct rb_node *n, *t;
struct uprobe *u;
@@ -986,7 +978,7 @@ static void build_probe_list(struct inode *inode,
min = vaddr_to_offset(vma, start);
max = min + (end - start) - 1;
- spin_lock_irqsave(&uprobes_treelock, flags);
+ spin_lock(&uprobes_treelock);
n = find_node_in_range(inode, min, max);
if (n) {
for (t = n; t; t = rb_prev(t)) {
@@ -1004,27 +996,20 @@ static void build_probe_list(struct inode *inode,
atomic_inc(&u->ref);
}
}
- spin_unlock_irqrestore(&uprobes_treelock, flags);
+ spin_unlock(&uprobes_treelock);
}
/*
- * Called from mmap_region.
- * called with mm->mmap_sem acquired.
+ * Called from mmap_region/vma_adjust with mm->mmap_sem acquired.
*
- * Return -ve no if we fail to insert probes and we cannot
- * bail-out.
- * Return 0 otherwise. i.e:
- *
- * - successful insertion of probes
- * - (or) no possible probes to be inserted.
- * - (or) insertion of probes failed but we can bail-out.
+ * Currently we ignore all errors and always return 0, the callers
+ * can't handle the failure anyway.
*/
int uprobe_mmap(struct vm_area_struct *vma)
{
struct list_head tmp_list;
struct uprobe *uprobe, *u;
struct inode *inode;
- int ret, count;
if (!atomic_read(&uprobe_events) || !valid_vma(vma, true))
return 0;
@@ -1036,44 +1021,35 @@ int uprobe_mmap(struct vm_area_struct *vma)
mutex_lock(uprobes_mmap_hash(inode));
build_probe_list(inode, vma, vma->vm_start, vma->vm_end, &tmp_list);
- ret = 0;
- count = 0;
-
list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
- if (!ret) {
+ if (!fatal_signal_pending(current)) {
unsigned long vaddr = offset_to_vaddr(vma, uprobe->offset);
-
- ret = install_breakpoint(uprobe, vma->vm_mm, vma, vaddr);
- /*
- * We can race against uprobe_register(), see the
- * comment near uprobe_hash().
- */
- if (ret == -EEXIST) {
- ret = 0;
-
- if (!is_swbp_at_addr(vma->vm_mm, vaddr))
- continue;
-
- /*
- * Unable to insert a breakpoint, but
- * breakpoint lies underneath. Increment the
- * probe count.
- */
- atomic_inc(&vma->vm_mm->uprobes_state.count);
- }
-
- if (!ret)
- count++;
+ install_breakpoint(uprobe, vma->vm_mm, vma, vaddr);
}
put_uprobe(uprobe);
}
-
mutex_unlock(uprobes_mmap_hash(inode));
- if (ret)
- atomic_sub(count, &vma->vm_mm->uprobes_state.count);
+ return 0;
+}
- return ret;
+static bool
+vma_has_uprobes(struct vm_area_struct *vma, unsigned long start, unsigned long end)
+{
+ loff_t min, max;
+ struct inode *inode;
+ struct rb_node *n;
+
+ inode = vma->vm_file->f_mapping->host;
+
+ min = vaddr_to_offset(vma, start);
+ max = min + (end - start) - 1;
+
+ spin_lock(&uprobes_treelock);
+ n = find_node_in_range(inode, min, max);
+ spin_unlock(&uprobes_treelock);
+
+ return !!n;
}
/*
@@ -1081,37 +1057,18 @@ int uprobe_mmap(struct vm_area_struct *vma)
*/
void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned long end)
{
- struct list_head tmp_list;
- struct uprobe *uprobe, *u;
- struct inode *inode;
-
if (!atomic_read(&uprobe_events) || !valid_vma(vma, false))
return;
if (!atomic_read(&vma->vm_mm->mm_users)) /* called by mmput() ? */
return;
- if (!atomic_read(&vma->vm_mm->uprobes_state.count))
- return;
-
- inode = vma->vm_file->f_mapping->host;
- if (!inode)
+ if (!test_bit(MMF_HAS_UPROBES, &vma->vm_mm->flags) ||
+ test_bit(MMF_RECALC_UPROBES, &vma->vm_mm->flags))
return;
- mutex_lock(uprobes_mmap_hash(inode));
- build_probe_list(inode, vma, start, end, &tmp_list);
-
- list_for_each_entry_safe(uprobe, u, &tmp_list, pending_list) {
- unsigned long vaddr = offset_to_vaddr(vma, uprobe->offset);
- /*
- * An unregister could have removed the probe before
- * unmap. So check before we decrement the count.
- */
- if (is_swbp_at_addr(vma->vm_mm, vaddr) == 1)
- atomic_dec(&vma->vm_mm->uprobes_state.count);
- put_uprobe(uprobe);
- }
- mutex_unlock(uprobes_mmap_hash(inode));
+ if (vma_has_uprobes(vma, start, end))
+ set_bit(MMF_RECALC_UPROBES, &vma->vm_mm->flags);
}
/* Slot allocation for XOL */
@@ -1213,13 +1170,15 @@ void uprobe_clear_state(struct mm_struct *mm)
kfree(area);
}
-/*
- * uprobe_reset_state - Free the area allocated for slots.
- */
-void uprobe_reset_state(struct mm_struct *mm)
+void uprobe_dup_mmap(struct mm_struct *oldmm, struct mm_struct *newmm)
{
- mm->uprobes_state.xol_area = NULL;
- atomic_set(&mm->uprobes_state.count, 0);
+ newmm->uprobes_state.xol_area = NULL;
+
+ if (test_bit(MMF_HAS_UPROBES, &oldmm->flags)) {
+ set_bit(MMF_HAS_UPROBES, &newmm->flags);
+ /* unconditionally, dup_mmap() skips VM_DONTCOPY vmas */
+ set_bit(MMF_RECALC_UPROBES, &newmm->flags);
+ }
}
/*
@@ -1437,6 +1396,25 @@ static bool can_skip_sstep(struct uprobe *uprobe, struct pt_regs *regs)
return false;
}
+static void mmf_recalc_uprobes(struct mm_struct *mm)
+{
+ struct vm_area_struct *vma;
+
+ for (vma = mm->mmap; vma; vma = vma->vm_next) {
+ if (!valid_vma(vma, false))
+ continue;
+ /*
+ * This is not strictly accurate, we can race with
+ * uprobe_unregister() and see the already removed
+ * uprobe if delete_uprobe() was not yet called.
+ */
+ if (vma_has_uprobes(vma, vma->vm_start, vma->vm_end))
+ return;
+ }
+
+ clear_bit(MMF_HAS_UPROBES, &mm->flags);
+}
+
static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp)
{
struct mm_struct *mm = current->mm;
@@ -1458,11 +1436,24 @@ static struct uprobe *find_active_uprobe(unsigned long bp_vaddr, int *is_swbp)
} else {
*is_swbp = -EFAULT;
}
+
+ if (!uprobe && test_and_clear_bit(MMF_RECALC_UPROBES, &mm->flags))
+ mmf_recalc_uprobes(mm);
up_read(&mm->mmap_sem);
return uprobe;
}
+void __weak arch_uprobe_enable_step(struct arch_uprobe *arch)
+{
+ user_enable_single_step(current);
+}
+
+void __weak arch_uprobe_disable_step(struct arch_uprobe *arch)
+{
+ user_disable_single_step(current);
+}
+
/*
* Run handler and ask thread to singlestep.
* Ensure all non-fatal signals cannot interrupt thread while it singlesteps.
@@ -1509,7 +1500,7 @@ static void handle_swbp(struct pt_regs *regs)
utask->state = UTASK_SSTEP;
if (!pre_ssout(uprobe, regs, bp_vaddr)) {
- user_enable_single_step(current);
+ arch_uprobe_enable_step(&uprobe->arch);
return;
}
@@ -1518,17 +1509,15 @@ cleanup_ret:
utask->active_uprobe = NULL;
utask->state = UTASK_RUNNING;
}
- if (uprobe) {
- if (!(uprobe->flags & UPROBE_SKIP_SSTEP))
+ if (!(uprobe->flags & UPROBE_SKIP_SSTEP))
- /*
- * cannot singlestep; cannot skip instruction;
- * re-execute the instruction.
- */
- instruction_pointer_set(regs, bp_vaddr);
+ /*
+ * cannot singlestep; cannot skip instruction;
+ * re-execute the instruction.
+ */
+ instruction_pointer_set(regs, bp_vaddr);
- put_uprobe(uprobe);
- }
+ put_uprobe(uprobe);
}
/*
@@ -1547,10 +1536,10 @@ static void handle_singlestep(struct uprobe_task *utask, struct pt_regs *regs)
else
WARN_ON_ONCE(1);
+ arch_uprobe_disable_step(&uprobe->arch);
put_uprobe(uprobe);
utask->active_uprobe = NULL;
utask->state = UTASK_RUNNING;
- user_disable_single_step(current);
xol_free_insn_slot(current);
spin_lock_irq(&current->sighand->siglock);
@@ -1589,8 +1578,7 @@ int uprobe_pre_sstep_notifier(struct pt_regs *regs)
{
struct uprobe_task *utask;
- if (!current->mm || !atomic_read(&current->mm->uprobes_state.count))
- /* task is currently not uprobed */
+ if (!current->mm || !test_bit(MMF_HAS_UPROBES, &current->mm->flags))
return 0;
utask = current->utask;
diff --git a/kernel/fork.c b/kernel/fork.c
index 2c8857e12855..5a0e74d89a5a 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -353,6 +353,7 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
down_write(&oldmm->mmap_sem);
flush_cache_dup_mm(oldmm);
+ uprobe_dup_mmap(oldmm, mm);
/*
* Not linked in yet - no deadlock potential:
*/
@@ -454,9 +455,6 @@ static int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
if (retval)
goto out;
-
- if (file)
- uprobe_mmap(tmp);
}
/* a new mm has just been created */
arch_dup_mmap(oldmm, mm);
@@ -839,8 +837,6 @@ struct mm_struct *dup_mm(struct task_struct *tsk)
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
mm->pmd_huge_pte = NULL;
#endif
- uprobe_reset_state(mm);
-
if (!mm_init(mm, tsk))
goto fail_nomem;
@@ -1280,11 +1276,7 @@ static struct task_struct *copy_process(unsigned long clone_flags,
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
p->irq_events = 0;
-#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
- p->hardirqs_enabled = 1;
-#else
p->hardirqs_enabled = 0;
-#endif
p->hardirq_enable_ip = 0;
p->hardirq_enable_event = 0;
p->hardirq_disable_ip = _THIS_IP_;
diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c
index eebd6d5cfb44..57d86d07221e 100644
--- a/kernel/irq/chip.c
+++ b/kernel/irq/chip.c
@@ -671,6 +671,7 @@ irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
irq_set_chip(irq, chip);
__irq_set_handler(irq, handle, 0, name);
}
+EXPORT_SYMBOL_GPL(irq_set_chip_and_handler_name);
void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set)
{
diff --git a/kernel/irq/dummychip.c b/kernel/irq/dummychip.c
index b5fcd96c7102..988dc58e8847 100644
--- a/kernel/irq/dummychip.c
+++ b/kernel/irq/dummychip.c
@@ -6,6 +6,7 @@
*/
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/export.h>
#include "internals.h"
@@ -57,3 +58,4 @@ struct irq_chip dummy_irq_chip = {
.irq_mask = noop,
.irq_unmask = noop,
};
+EXPORT_SYMBOL_GPL(dummy_irq_chip);
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index c62b8546cc90..098f396aa409 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -561,9 +561,9 @@ static __kprobes void kprobe_optimizer(struct work_struct *work)
{
LIST_HEAD(free_list);
+ mutex_lock(&kprobe_mutex);
/* Lock modules while optimizing kprobes */
mutex_lock(&module_mutex);
- mutex_lock(&kprobe_mutex);
/*
* Step 1: Unoptimize kprobes and collect cleaned (unused and disarmed)
@@ -586,8 +586,8 @@ static __kprobes void kprobe_optimizer(struct work_struct *work)
/* Step 4: Free cleaned kprobes after quiesence period */
do_free_cleaned_kprobes(&free_list);
- mutex_unlock(&kprobe_mutex);
mutex_unlock(&module_mutex);
+ mutex_unlock(&kprobe_mutex);
/* Step 5: Kick optimizer again if needed */
if (!list_empty(&optimizing_list) || !list_empty(&unoptimizing_list))
@@ -759,20 +759,32 @@ static __kprobes void try_to_optimize_kprobe(struct kprobe *p)
struct kprobe *ap;
struct optimized_kprobe *op;
+ /* Impossible to optimize ftrace-based kprobe */
+ if (kprobe_ftrace(p))
+ return;
+
+ /* For preparing optimization, jump_label_text_reserved() is called */
+ jump_label_lock();
+ mutex_lock(&text_mutex);
+
ap = alloc_aggr_kprobe(p);
if (!ap)
- return;
+ goto out;
op = container_of(ap, struct optimized_kprobe, kp);
if (!arch_prepared_optinsn(&op->optinsn)) {
/* If failed to setup optimizing, fallback to kprobe */
arch_remove_optimized_kprobe(op);
kfree(op);
- return;
+ goto out;
}
init_aggr_kprobe(ap, p);
- optimize_kprobe(ap);
+ optimize_kprobe(ap); /* This just kicks optimizer thread */
+
+out:
+ mutex_unlock(&text_mutex);
+ jump_label_unlock();
}
#ifdef CONFIG_SYSCTL
@@ -907,9 +919,64 @@ static __kprobes struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
}
#endif /* CONFIG_OPTPROBES */
+#ifdef KPROBES_CAN_USE_FTRACE
+static struct ftrace_ops kprobe_ftrace_ops __read_mostly = {
+ .func = kprobe_ftrace_handler,
+ .flags = FTRACE_OPS_FL_SAVE_REGS,
+};
+static int kprobe_ftrace_enabled;
+
+/* Must ensure p->addr is really on ftrace */
+static int __kprobes prepare_kprobe(struct kprobe *p)
+{
+ if (!kprobe_ftrace(p))
+ return arch_prepare_kprobe(p);
+
+ return arch_prepare_kprobe_ftrace(p);
+}
+
+/* Caller must lock kprobe_mutex */
+static void __kprobes arm_kprobe_ftrace(struct kprobe *p)
+{
+ int ret;
+
+ ret = ftrace_set_filter_ip(&kprobe_ftrace_ops,
+ (unsigned long)p->addr, 0, 0);
+ WARN(ret < 0, "Failed to arm kprobe-ftrace at %p (%d)\n", p->addr, ret);
+ kprobe_ftrace_enabled++;
+ if (kprobe_ftrace_enabled == 1) {
+ ret = register_ftrace_function(&kprobe_ftrace_ops);
+ WARN(ret < 0, "Failed to init kprobe-ftrace (%d)\n", ret);
+ }
+}
+
+/* Caller must lock kprobe_mutex */
+static void __kprobes disarm_kprobe_ftrace(struct kprobe *p)
+{
+ int ret;
+
+ kprobe_ftrace_enabled--;
+ if (kprobe_ftrace_enabled == 0) {
+ ret = unregister_ftrace_function(&kprobe_ftrace_ops);
+ WARN(ret < 0, "Failed to init kprobe-ftrace (%d)\n", ret);
+ }
+ ret = ftrace_set_filter_ip(&kprobe_ftrace_ops,
+ (unsigned long)p->addr, 1, 0);
+ WARN(ret < 0, "Failed to disarm kprobe-ftrace at %p (%d)\n", p->addr, ret);
+}
+#else /* !KPROBES_CAN_USE_FTRACE */
+#define prepare_kprobe(p) arch_prepare_kprobe(p)
+#define arm_kprobe_ftrace(p) do {} while (0)
+#define disarm_kprobe_ftrace(p) do {} while (0)
+#endif
+
/* Arm a kprobe with text_mutex */
static void __kprobes arm_kprobe(struct kprobe *kp)
{
+ if (unlikely(kprobe_ftrace(kp))) {
+ arm_kprobe_ftrace(kp);
+ return;
+ }
/*
* Here, since __arm_kprobe() doesn't use stop_machine(),
* this doesn't cause deadlock on text_mutex. So, we don't
@@ -921,11 +988,15 @@ static void __kprobes arm_kprobe(struct kprobe *kp)
}
/* Disarm a kprobe with text_mutex */
-static void __kprobes disarm_kprobe(struct kprobe *kp)
+static void __kprobes disarm_kprobe(struct kprobe *kp, bool reopt)
{
+ if (unlikely(kprobe_ftrace(kp))) {
+ disarm_kprobe_ftrace(kp);
+ return;
+ }
/* Ditto */
mutex_lock(&text_mutex);
- __disarm_kprobe(kp, true);
+ __disarm_kprobe(kp, reopt);
mutex_unlock(&text_mutex);
}
@@ -1144,12 +1215,6 @@ static int __kprobes add_new_kprobe(struct kprobe *ap, struct kprobe *p)
if (p->post_handler && !ap->post_handler)
ap->post_handler = aggr_post_handler;
- if (kprobe_disabled(ap) && !kprobe_disabled(p)) {
- ap->flags &= ~KPROBE_FLAG_DISABLED;
- if (!kprobes_all_disarmed)
- /* Arm the breakpoint again. */
- __arm_kprobe(ap);
- }
return 0;
}
@@ -1189,11 +1254,22 @@ static int __kprobes register_aggr_kprobe(struct kprobe *orig_p,
int ret = 0;
struct kprobe *ap = orig_p;
+ /* For preparing optimization, jump_label_text_reserved() is called */
+ jump_label_lock();
+ /*
+ * Get online CPUs to avoid text_mutex deadlock.with stop machine,
+ * which is invoked by unoptimize_kprobe() in add_new_kprobe()
+ */
+ get_online_cpus();
+ mutex_lock(&text_mutex);
+
if (!kprobe_aggrprobe(orig_p)) {
/* If orig_p is not an aggr_kprobe, create new aggr_kprobe. */
ap = alloc_aggr_kprobe(orig_p);
- if (!ap)
- return -ENOMEM;
+ if (!ap) {
+ ret = -ENOMEM;
+ goto out;
+ }
init_aggr_kprobe(ap, orig_p);
} else if (kprobe_unused(ap))
/* This probe is going to die. Rescue it */
@@ -1213,7 +1289,7 @@ static int __kprobes register_aggr_kprobe(struct kprobe *orig_p,
* free aggr_probe. It will be used next time, or
* freed by unregister_kprobe.
*/
- return ret;
+ goto out;
/* Prepare optimized instructions if possible. */
prepare_optimized_kprobe(ap);
@@ -1228,7 +1304,20 @@ static int __kprobes register_aggr_kprobe(struct kprobe *orig_p,
/* Copy ap's insn slot to p */
copy_kprobe(ap, p);
- return add_new_kprobe(ap, p);
+ ret = add_new_kprobe(ap, p);
+
+out:
+ mutex_unlock(&text_mutex);
+ put_online_cpus();
+ jump_label_unlock();
+
+ if (ret == 0 && kprobe_disabled(ap) && !kprobe_disabled(p)) {
+ ap->flags &= ~KPROBE_FLAG_DISABLED;
+ if (!kprobes_all_disarmed)
+ /* Arm the breakpoint again. */
+ arm_kprobe(ap);
+ }
+ return ret;
}
static int __kprobes in_kprobes_functions(unsigned long addr)
@@ -1313,71 +1402,96 @@ static inline int check_kprobe_rereg(struct kprobe *p)
return ret;
}
-int __kprobes register_kprobe(struct kprobe *p)
+static __kprobes int check_kprobe_address_safe(struct kprobe *p,
+ struct module **probed_mod)
{
int ret = 0;
- struct kprobe *old_p;
- struct module *probed_mod;
- kprobe_opcode_t *addr;
-
- addr = kprobe_addr(p);
- if (IS_ERR(addr))
- return PTR_ERR(addr);
- p->addr = addr;
+ unsigned long ftrace_addr;
- ret = check_kprobe_rereg(p);
- if (ret)
- return ret;
+ /*
+ * If the address is located on a ftrace nop, set the
+ * breakpoint to the following instruction.
+ */
+ ftrace_addr = ftrace_location((unsigned long)p->addr);
+ if (ftrace_addr) {
+#ifdef KPROBES_CAN_USE_FTRACE
+ /* Given address is not on the instruction boundary */
+ if ((unsigned long)p->addr != ftrace_addr)
+ return -EILSEQ;
+ p->flags |= KPROBE_FLAG_FTRACE;
+#else /* !KPROBES_CAN_USE_FTRACE */
+ return -EINVAL;
+#endif
+ }
jump_label_lock();
preempt_disable();
+
+ /* Ensure it is not in reserved area nor out of text */
if (!kernel_text_address((unsigned long) p->addr) ||
in_kprobes_functions((unsigned long) p->addr) ||
- ftrace_text_reserved(p->addr, p->addr) ||
jump_label_text_reserved(p->addr, p->addr)) {
ret = -EINVAL;
- goto cannot_probe;
+ goto out;
}
- /* User can pass only KPROBE_FLAG_DISABLED to register_kprobe */
- p->flags &= KPROBE_FLAG_DISABLED;
-
- /*
- * Check if are we probing a module.
- */
- probed_mod = __module_text_address((unsigned long) p->addr);
- if (probed_mod) {
- /* Return -ENOENT if fail. */
- ret = -ENOENT;
+ /* Check if are we probing a module */
+ *probed_mod = __module_text_address((unsigned long) p->addr);
+ if (*probed_mod) {
/*
* We must hold a refcount of the probed module while updating
* its code to prohibit unexpected unloading.
*/
- if (unlikely(!try_module_get(probed_mod)))
- goto cannot_probe;
+ if (unlikely(!try_module_get(*probed_mod))) {
+ ret = -ENOENT;
+ goto out;
+ }
/*
* If the module freed .init.text, we couldn't insert
* kprobes in there.
*/
- if (within_module_init((unsigned long)p->addr, probed_mod) &&
- probed_mod->state != MODULE_STATE_COMING) {
- module_put(probed_mod);
- goto cannot_probe;
+ if (within_module_init((unsigned long)p->addr, *probed_mod) &&
+ (*probed_mod)->state != MODULE_STATE_COMING) {
+ module_put(*probed_mod);
+ *probed_mod = NULL;
+ ret = -ENOENT;
}
- /* ret will be updated by following code */
}
+out:
preempt_enable();
jump_label_unlock();
+ return ret;
+}
+
+int __kprobes register_kprobe(struct kprobe *p)
+{
+ int ret;
+ struct kprobe *old_p;
+ struct module *probed_mod;
+ kprobe_opcode_t *addr;
+
+ /* Adjust probe address from symbol */
+ addr = kprobe_addr(p);
+ if (IS_ERR(addr))
+ return PTR_ERR(addr);
+ p->addr = addr;
+
+ ret = check_kprobe_rereg(p);
+ if (ret)
+ return ret;
+
+ /* User can pass only KPROBE_FLAG_DISABLED to register_kprobe */
+ p->flags &= KPROBE_FLAG_DISABLED;
p->nmissed = 0;
INIT_LIST_HEAD(&p->list);
- mutex_lock(&kprobe_mutex);
- jump_label_lock(); /* needed to call jump_label_text_reserved() */
+ ret = check_kprobe_address_safe(p, &probed_mod);
+ if (ret)
+ return ret;
- get_online_cpus(); /* For avoiding text_mutex deadlock. */
- mutex_lock(&text_mutex);
+ mutex_lock(&kprobe_mutex);
old_p = get_kprobe(p->addr);
if (old_p) {
@@ -1386,7 +1500,9 @@ int __kprobes register_kprobe(struct kprobe *p)
goto out;
}
- ret = arch_prepare_kprobe(p);
+ mutex_lock(&text_mutex); /* Avoiding text modification */
+ ret = prepare_kprobe(p);
+ mutex_unlock(&text_mutex);
if (ret)
goto out;
@@ -1395,26 +1511,18 @@ int __kprobes register_kprobe(struct kprobe *p)
&kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
if (!kprobes_all_disarmed && !kprobe_disabled(p))
- __arm_kprobe(p);
+ arm_kprobe(p);
/* Try to optimize kprobe */
try_to_optimize_kprobe(p);
out:
- mutex_unlock(&text_mutex);
- put_online_cpus();
- jump_label_unlock();
mutex_unlock(&kprobe_mutex);
if (probed_mod)
module_put(probed_mod);
return ret;
-
-cannot_probe:
- preempt_enable();
- jump_label_unlock();
- return ret;
}
EXPORT_SYMBOL_GPL(register_kprobe);
@@ -1451,7 +1559,7 @@ static struct kprobe *__kprobes __disable_kprobe(struct kprobe *p)
/* Try to disarm and disable this/parent probe */
if (p == orig_p || aggr_kprobe_disabled(orig_p)) {
- disarm_kprobe(orig_p);
+ disarm_kprobe(orig_p, true);
orig_p->flags |= KPROBE_FLAG_DISABLED;
}
}
@@ -2049,10 +2157,11 @@ static void __kprobes report_probe(struct seq_file *pi, struct kprobe *p,
if (!pp)
pp = p;
- seq_printf(pi, "%s%s%s\n",
+ seq_printf(pi, "%s%s%s%s\n",
(kprobe_gone(p) ? "[GONE]" : ""),
((kprobe_disabled(p) && !kprobe_gone(p)) ? "[DISABLED]" : ""),
- (kprobe_optimized(pp) ? "[OPTIMIZED]" : ""));
+ (kprobe_optimized(pp) ? "[OPTIMIZED]" : ""),
+ (kprobe_ftrace(pp) ? "[FTRACE]" : ""));
}
static void __kprobes *kprobe_seq_start(struct seq_file *f, loff_t *pos)
@@ -2131,14 +2240,12 @@ static void __kprobes arm_all_kprobes(void)
goto already_enabled;
/* Arming kprobes doesn't optimize kprobe itself */
- mutex_lock(&text_mutex);
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i];
hlist_for_each_entry_rcu(p, node, head, hlist)
if (!kprobe_disabled(p))
- __arm_kprobe(p);
+ arm_kprobe(p);
}
- mutex_unlock(&text_mutex);
kprobes_all_disarmed = false;
printk(KERN_INFO "Kprobes globally enabled\n");
@@ -2166,15 +2273,13 @@ static void __kprobes disarm_all_kprobes(void)
kprobes_all_disarmed = true;
printk(KERN_INFO "Kprobes globally disabled\n");
- mutex_lock(&text_mutex);
for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
head = &kprobe_table[i];
hlist_for_each_entry_rcu(p, node, head, hlist) {
if (!arch_trampoline_kprobe(p) && !kprobe_disabled(p))
- __disarm_kprobe(p, false);
+ disarm_kprobe(p, false);
}
}
- mutex_unlock(&text_mutex);
mutex_unlock(&kprobe_mutex);
/* Wait for disarming all kprobes by optimizer */
diff --git a/kernel/kthread.c b/kernel/kthread.c
index b579af57ea10..146a6fa96825 100644
--- a/kernel/kthread.c
+++ b/kernel/kthread.c
@@ -37,11 +37,20 @@ struct kthread_create_info
};
struct kthread {
- int should_stop;
+ unsigned long flags;
+ unsigned int cpu;
void *data;
+ struct completion parked;
struct completion exited;
};
+enum KTHREAD_BITS {
+ KTHREAD_IS_PER_CPU = 0,
+ KTHREAD_SHOULD_STOP,
+ KTHREAD_SHOULD_PARK,
+ KTHREAD_IS_PARKED,
+};
+
#define to_kthread(tsk) \
container_of((tsk)->vfork_done, struct kthread, exited)
@@ -52,13 +61,29 @@ struct kthread {
* and this will return true. You should then return, and your return
* value will be passed through to kthread_stop().
*/
-int kthread_should_stop(void)
+bool kthread_should_stop(void)
{
- return to_kthread(current)->should_stop;
+ return test_bit(KTHREAD_SHOULD_STOP, &to_kthread(current)->flags);
}
EXPORT_SYMBOL(kthread_should_stop);
/**
+ * kthread_should_park - should this kthread park now?
+ *
+ * When someone calls kthread_park() on your kthread, it will be woken
+ * and this will return true. You should then do the necessary
+ * cleanup and call kthread_parkme()
+ *
+ * Similar to kthread_should_stop(), but this keeps the thread alive
+ * and in a park position. kthread_unpark() "restarts" the thread and
+ * calls the thread function again.
+ */
+bool kthread_should_park(void)
+{
+ return test_bit(KTHREAD_SHOULD_PARK, &to_kthread(current)->flags);
+}
+
+/**
* kthread_freezable_should_stop - should this freezable kthread return now?
* @was_frozen: optional out parameter, indicates whether %current was frozen
*
@@ -96,6 +121,24 @@ void *kthread_data(struct task_struct *task)
return to_kthread(task)->data;
}
+static void __kthread_parkme(struct kthread *self)
+{
+ __set_current_state(TASK_INTERRUPTIBLE);
+ while (test_bit(KTHREAD_SHOULD_PARK, &self->flags)) {
+ if (!test_and_set_bit(KTHREAD_IS_PARKED, &self->flags))
+ complete(&self->parked);
+ schedule();
+ __set_current_state(TASK_INTERRUPTIBLE);
+ }
+ clear_bit(KTHREAD_IS_PARKED, &self->flags);
+ __set_current_state(TASK_RUNNING);
+}
+
+void kthread_parkme(void)
+{
+ __kthread_parkme(to_kthread(current));
+}
+
static int kthread(void *_create)
{
/* Copy data: it's on kthread's stack */
@@ -105,9 +148,10 @@ static int kthread(void *_create)
struct kthread self;
int ret;
- self.should_stop = 0;
+ self.flags = 0;
self.data = data;
init_completion(&self.exited);
+ init_completion(&self.parked);
current->vfork_done = &self.exited;
/* OK, tell user we're spawned, wait for stop or wakeup */
@@ -117,9 +161,11 @@ static int kthread(void *_create)
schedule();
ret = -EINTR;
- if (!self.should_stop)
- ret = threadfn(data);
+ if (!test_bit(KTHREAD_SHOULD_STOP, &self.flags)) {
+ __kthread_parkme(&self);
+ ret = threadfn(data);
+ }
/* we can't just return, we must preserve "self" on stack */
do_exit(ret);
}
@@ -172,8 +218,7 @@ static void create_kthread(struct kthread_create_info *create)
* Returns a task_struct or ERR_PTR(-ENOMEM).
*/
struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
- void *data,
- int node,
+ void *data, int node,
const char namefmt[],
...)
{
@@ -210,6 +255,13 @@ struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
}
EXPORT_SYMBOL(kthread_create_on_node);
+static void __kthread_bind(struct task_struct *p, unsigned int cpu)
+{
+ /* It's safe because the task is inactive. */
+ do_set_cpus_allowed(p, cpumask_of(cpu));
+ p->flags |= PF_THREAD_BOUND;
+}
+
/**
* kthread_bind - bind a just-created kthread to a cpu.
* @p: thread created by kthread_create().
@@ -226,14 +278,112 @@ void kthread_bind(struct task_struct *p, unsigned int cpu)
WARN_ON(1);
return;
}
-
- /* It's safe because the task is inactive. */
- do_set_cpus_allowed(p, cpumask_of(cpu));
- p->flags |= PF_THREAD_BOUND;
+ __kthread_bind(p, cpu);
}
EXPORT_SYMBOL(kthread_bind);
/**
+ * kthread_create_on_cpu - Create a cpu bound kthread
+ * @threadfn: the function to run until signal_pending(current).
+ * @data: data ptr for @threadfn.
+ * @cpu: The cpu on which the thread should be bound,
+ * @namefmt: printf-style name for the thread. Format is restricted
+ * to "name.*%u". Code fills in cpu number.
+ *
+ * Description: This helper function creates and names a kernel thread
+ * The thread will be woken and put into park mode.
+ */
+struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
+ void *data, unsigned int cpu,
+ const char *namefmt)
+{
+ struct task_struct *p;
+
+ p = kthread_create_on_node(threadfn, data, cpu_to_node(cpu), namefmt,
+ cpu);
+ if (IS_ERR(p))
+ return p;
+ set_bit(KTHREAD_IS_PER_CPU, &to_kthread(p)->flags);
+ to_kthread(p)->cpu = cpu;
+ /* Park the thread to get it out of TASK_UNINTERRUPTIBLE state */
+ kthread_park(p);
+ return p;
+}
+
+static struct kthread *task_get_live_kthread(struct task_struct *k)
+{
+ struct kthread *kthread;
+
+ get_task_struct(k);
+ kthread = to_kthread(k);
+ /* It might have exited */
+ barrier();
+ if (k->vfork_done != NULL)
+ return kthread;
+ return NULL;
+}
+
+/**
+ * kthread_unpark - unpark a thread created by kthread_create().
+ * @k: thread created by kthread_create().
+ *
+ * Sets kthread_should_park() for @k to return false, wakes it, and
+ * waits for it to return. If the thread is marked percpu then its
+ * bound to the cpu again.
+ */
+void kthread_unpark(struct task_struct *k)
+{
+ struct kthread *kthread = task_get_live_kthread(k);
+
+ if (kthread) {
+ clear_bit(KTHREAD_SHOULD_PARK, &kthread->flags);
+ /*
+ * We clear the IS_PARKED bit here as we don't wait
+ * until the task has left the park code. So if we'd
+ * park before that happens we'd see the IS_PARKED bit
+ * which might be about to be cleared.
+ */
+ if (test_and_clear_bit(KTHREAD_IS_PARKED, &kthread->flags)) {
+ if (test_bit(KTHREAD_IS_PER_CPU, &kthread->flags))
+ __kthread_bind(k, kthread->cpu);
+ wake_up_process(k);
+ }
+ }
+ put_task_struct(k);
+}
+
+/**
+ * kthread_park - park a thread created by kthread_create().
+ * @k: thread created by kthread_create().
+ *
+ * Sets kthread_should_park() for @k to return true, wakes it, and
+ * waits for it to return. This can also be called after kthread_create()
+ * instead of calling wake_up_process(): the thread will park without
+ * calling threadfn().
+ *
+ * Returns 0 if the thread is parked, -ENOSYS if the thread exited.
+ * If called by the kthread itself just the park bit is set.
+ */
+int kthread_park(struct task_struct *k)
+{
+ struct kthread *kthread = task_get_live_kthread(k);
+ int ret = -ENOSYS;
+
+ if (kthread) {
+ if (!test_bit(KTHREAD_IS_PARKED, &kthread->flags)) {
+ set_bit(KTHREAD_SHOULD_PARK, &kthread->flags);
+ if (k != current) {
+ wake_up_process(k);
+ wait_for_completion(&kthread->parked);
+ }
+ }
+ ret = 0;
+ }
+ put_task_struct(k);
+ return ret;
+}
+
+/**
* kthread_stop - stop a thread created by kthread_create().
* @k: thread created by kthread_create().
*
@@ -250,16 +400,13 @@ EXPORT_SYMBOL(kthread_bind);
*/
int kthread_stop(struct task_struct *k)
{
- struct kthread *kthread;
+ struct kthread *kthread = task_get_live_kthread(k);
int ret;
trace_sched_kthread_stop(k);
- get_task_struct(k);
-
- kthread = to_kthread(k);
- barrier(); /* it might have exited */
- if (k->vfork_done != NULL) {
- kthread->should_stop = 1;
+ if (kthread) {
+ set_bit(KTHREAD_SHOULD_STOP, &kthread->flags);
+ clear_bit(KTHREAD_SHOULD_PARK, &kthread->flags);
wake_up_process(k);
wait_for_completion(&kthread->exited);
}
diff --git a/kernel/lockdep.c b/kernel/lockdep.c
index ea9ee4518c35..7981e5b2350d 100644
--- a/kernel/lockdep.c
+++ b/kernel/lockdep.c
@@ -2998,6 +2998,42 @@ EXPORT_SYMBOL_GPL(lockdep_init_map);
struct lock_class_key __lockdep_no_validate__;
+static int
+print_lock_nested_lock_not_held(struct task_struct *curr,
+ struct held_lock *hlock,
+ unsigned long ip)
+{
+ if (!debug_locks_off())
+ return 0;
+ if (debug_locks_silent)
+ return 0;
+
+ printk("\n");
+ printk("==================================\n");
+ printk("[ BUG: Nested lock was not taken ]\n");
+ print_kernel_ident();
+ printk("----------------------------------\n");
+
+ printk("%s/%d is trying to lock:\n", curr->comm, task_pid_nr(curr));
+ print_lock(hlock);
+
+ printk("\nbut this task is not holding:\n");
+ printk("%s\n", hlock->nest_lock->name);
+
+ printk("\nstack backtrace:\n");
+ dump_stack();
+
+ printk("\nother info that might help us debug this:\n");
+ lockdep_print_held_locks(curr);
+
+ printk("\nstack backtrace:\n");
+ dump_stack();
+
+ return 0;
+}
+
+static int __lock_is_held(struct lockdep_map *lock);
+
/*
* This gets called for every mutex_lock*()/spin_lock*() operation.
* We maintain the dependency maps and validate the locking attempt:
@@ -3139,6 +3175,9 @@ static int __lock_acquire(struct lockdep_map *lock, unsigned int subclass,
}
chain_key = iterate_chain_key(chain_key, id);
+ if (nest_lock && !__lock_is_held(nest_lock))
+ return print_lock_nested_lock_not_held(curr, hlock, ip);
+
if (!validate_chain(curr, lock, hlock, chain_head, chain_key))
return 0;
diff --git a/kernel/pid_namespace.c b/kernel/pid_namespace.c
index b3c7fd554250..6144bab8fd8e 100644
--- a/kernel/pid_namespace.c
+++ b/kernel/pid_namespace.c
@@ -232,15 +232,19 @@ static int pid_ns_ctl_handler(struct ctl_table *table, int write,
*/
tmp.data = &current->nsproxy->pid_ns->last_pid;
- return proc_dointvec(&tmp, write, buffer, lenp, ppos);
+ return proc_dointvec_minmax(&tmp, write, buffer, lenp, ppos);
}
+extern int pid_max;
+static int zero = 0;
static struct ctl_table pid_ns_ctl_table[] = {
{
.procname = "ns_last_pid",
.maxlen = sizeof(int),
.mode = 0666, /* permissions are checked in the handler */
.proc_handler = pid_ns_ctl_handler,
+ .extra1 = &zero,
+ .extra2 = &pid_max,
},
{ }
};
diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c
index 4e6a61b15e86..29ca1c6da594 100644
--- a/kernel/rcupdate.c
+++ b/kernel/rcupdate.c
@@ -45,6 +45,7 @@
#include <linux/mutex.h>
#include <linux/export.h>
#include <linux/hardirq.h>
+#include <linux/delay.h>
#define CREATE_TRACE_POINTS
#include <trace/events/rcu.h>
@@ -81,6 +82,9 @@ void __rcu_read_unlock(void)
} else {
barrier(); /* critical section before exit code. */
t->rcu_read_lock_nesting = INT_MIN;
+#ifdef CONFIG_PROVE_RCU_DELAY
+ udelay(10); /* Make preemption more probable. */
+#endif /* #ifdef CONFIG_PROVE_RCU_DELAY */
barrier(); /* assign before ->rcu_read_unlock_special load */
if (unlikely(ACCESS_ONCE(t->rcu_read_unlock_special)))
rcu_read_unlock_special(t);
diff --git a/kernel/rcutiny.c b/kernel/rcutiny.c
index 547b1fe5b052..e4c6a598d6f7 100644
--- a/kernel/rcutiny.c
+++ b/kernel/rcutiny.c
@@ -56,25 +56,28 @@ static void __call_rcu(struct rcu_head *head,
static long long rcu_dynticks_nesting = DYNTICK_TASK_EXIT_IDLE;
/* Common code for rcu_idle_enter() and rcu_irq_exit(), see kernel/rcutree.c. */
-static void rcu_idle_enter_common(long long oldval)
+static void rcu_idle_enter_common(long long newval)
{
- if (rcu_dynticks_nesting) {
+ if (newval) {
RCU_TRACE(trace_rcu_dyntick("--=",
- oldval, rcu_dynticks_nesting));
+ rcu_dynticks_nesting, newval));
+ rcu_dynticks_nesting = newval;
return;
}
- RCU_TRACE(trace_rcu_dyntick("Start", oldval, rcu_dynticks_nesting));
+ RCU_TRACE(trace_rcu_dyntick("Start", rcu_dynticks_nesting, newval));
if (!is_idle_task(current)) {
struct task_struct *idle = idle_task(smp_processor_id());
RCU_TRACE(trace_rcu_dyntick("Error on entry: not idle task",
- oldval, rcu_dynticks_nesting));
+ rcu_dynticks_nesting, newval));
ftrace_dump(DUMP_ALL);
WARN_ONCE(1, "Current pid: %d comm: %s / Idle pid: %d comm: %s",
current->pid, current->comm,
idle->pid, idle->comm); /* must be idle task! */
}
rcu_sched_qs(0); /* implies rcu_bh_qsctr_inc(0) */
+ barrier();
+ rcu_dynticks_nesting = newval;
}
/*
@@ -84,17 +87,16 @@ static void rcu_idle_enter_common(long long oldval)
void rcu_idle_enter(void)
{
unsigned long flags;
- long long oldval;
+ long long newval;
local_irq_save(flags);
- oldval = rcu_dynticks_nesting;
WARN_ON_ONCE((rcu_dynticks_nesting & DYNTICK_TASK_NEST_MASK) == 0);
if ((rcu_dynticks_nesting & DYNTICK_TASK_NEST_MASK) ==
DYNTICK_TASK_NEST_VALUE)
- rcu_dynticks_nesting = 0;
+ newval = 0;
else
- rcu_dynticks_nesting -= DYNTICK_TASK_NEST_VALUE;
- rcu_idle_enter_common(oldval);
+ newval = rcu_dynticks_nesting - DYNTICK_TASK_NEST_VALUE;
+ rcu_idle_enter_common(newval);
local_irq_restore(flags);
}
EXPORT_SYMBOL_GPL(rcu_idle_enter);
@@ -105,15 +107,15 @@ EXPORT_SYMBOL_GPL(rcu_idle_enter);
void rcu_irq_exit(void)
{
unsigned long flags;
- long long oldval;
+ long long newval;
local_irq_save(flags);
- oldval = rcu_dynticks_nesting;
- rcu_dynticks_nesting--;
- WARN_ON_ONCE(rcu_dynticks_nesting < 0);
- rcu_idle_enter_common(oldval);
+ newval = rcu_dynticks_nesting - 1;
+ WARN_ON_ONCE(newval < 0);
+ rcu_idle_enter_common(newval);
local_irq_restore(flags);
}
+EXPORT_SYMBOL_GPL(rcu_irq_exit);
/* Common code for rcu_idle_exit() and rcu_irq_enter(), see kernel/rcutree.c. */
static void rcu_idle_exit_common(long long oldval)
@@ -171,6 +173,7 @@ void rcu_irq_enter(void)
rcu_idle_exit_common(oldval);
local_irq_restore(flags);
}
+EXPORT_SYMBOL_GPL(rcu_irq_enter);
#ifdef CONFIG_DEBUG_LOCK_ALLOC
diff --git a/kernel/rcutiny_plugin.h b/kernel/rcutiny_plugin.h
index 918fd1e8509c..3d0190282204 100644
--- a/kernel/rcutiny_plugin.h
+++ b/kernel/rcutiny_plugin.h
@@ -278,7 +278,7 @@ static int rcu_boost(void)
rcu_preempt_ctrlblk.exp_tasks == NULL)
return 0; /* Nothing to boost. */
- raw_local_irq_save(flags);
+ local_irq_save(flags);
/*
* Recheck with irqs disabled: all tasks in need of boosting
@@ -287,7 +287,7 @@ static int rcu_boost(void)
*/
if (rcu_preempt_ctrlblk.boost_tasks == NULL &&
rcu_preempt_ctrlblk.exp_tasks == NULL) {
- raw_local_irq_restore(flags);
+ local_irq_restore(flags);
return 0;
}
@@ -317,7 +317,7 @@ static int rcu_boost(void)
t = container_of(tb, struct task_struct, rcu_node_entry);
rt_mutex_init_proxy_locked(&mtx, t);
t->rcu_boost_mutex = &mtx;
- raw_local_irq_restore(flags);
+ local_irq_restore(flags);
rt_mutex_lock(&mtx);
rt_mutex_unlock(&mtx); /* Keep lockdep happy. */
@@ -991,9 +991,9 @@ static void rcu_trace_sub_qlen(struct rcu_ctrlblk *rcp, int n)
{
unsigned long flags;
- raw_local_irq_save(flags);
+ local_irq_save(flags);
rcp->qlen -= n;
- raw_local_irq_restore(flags);
+ local_irq_restore(flags);
}
/*
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c
index 25b15033c61f..aaa7b9f3532a 100644
--- a/kernel/rcutorture.c
+++ b/kernel/rcutorture.c
@@ -53,10 +53,11 @@ MODULE_AUTHOR("Paul E. McKenney <paulmck@us.ibm.com> and Josh Triplett <josh@fre
static int nreaders = -1; /* # reader threads, defaults to 2*ncpus */
static int nfakewriters = 4; /* # fake writer threads */
-static int stat_interval; /* Interval between stats, in seconds. */
- /* Defaults to "only at end of test". */
+static int stat_interval = 60; /* Interval between stats, in seconds. */
+ /* Zero means "only at end of test". */
static bool verbose; /* Print more debug info. */
-static bool test_no_idle_hz; /* Test RCU's support for tickless idle CPUs. */
+static bool test_no_idle_hz = true;
+ /* Test RCU support for tickless idle CPUs. */
static int shuffle_interval = 3; /* Interval between shuffles (in sec)*/
static int stutter = 5; /* Start/stop testing interval (in sec) */
static int irqreader = 1; /* RCU readers from irq (timers). */
@@ -119,11 +120,11 @@ MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, rcu_bh, srcu)");
#define TORTURE_FLAG "-torture:"
#define PRINTK_STRING(s) \
- do { printk(KERN_ALERT "%s" TORTURE_FLAG s "\n", torture_type); } while (0)
+ do { pr_alert("%s" TORTURE_FLAG s "\n", torture_type); } while (0)
#define VERBOSE_PRINTK_STRING(s) \
- do { if (verbose) printk(KERN_ALERT "%s" TORTURE_FLAG s "\n", torture_type); } while (0)
+ do { if (verbose) pr_alert("%s" TORTURE_FLAG s "\n", torture_type); } while (0)
#define VERBOSE_PRINTK_ERRSTRING(s) \
- do { if (verbose) printk(KERN_ALERT "%s" TORTURE_FLAG "!!! " s "\n", torture_type); } while (0)
+ do { if (verbose) pr_alert("%s" TORTURE_FLAG "!!! " s "\n", torture_type); } while (0)
static char printk_buf[4096];
@@ -176,8 +177,14 @@ static long n_rcu_torture_boosts;
static long n_rcu_torture_timers;
static long n_offline_attempts;
static long n_offline_successes;
+static unsigned long sum_offline;
+static int min_offline = -1;
+static int max_offline;
static long n_online_attempts;
static long n_online_successes;
+static unsigned long sum_online;
+static int min_online = -1;
+static int max_online;
static long n_barrier_attempts;
static long n_barrier_successes;
static struct list_head rcu_torture_removed;
@@ -235,7 +242,7 @@ rcutorture_shutdown_notify(struct notifier_block *unused1,
if (fullstop == FULLSTOP_DONTSTOP)
fullstop = FULLSTOP_SHUTDOWN;
else
- printk(KERN_WARNING /* but going down anyway, so... */
+ pr_warn(/* but going down anyway, so... */
"Concurrent 'rmmod rcutorture' and shutdown illegal!\n");
mutex_unlock(&fullstop_mutex);
return NOTIFY_DONE;
@@ -248,7 +255,7 @@ rcutorture_shutdown_notify(struct notifier_block *unused1,
static void rcutorture_shutdown_absorb(char *title)
{
if (ACCESS_ONCE(fullstop) == FULLSTOP_SHUTDOWN) {
- printk(KERN_NOTICE
+ pr_notice(
"rcutorture thread %s parking due to system shutdown\n",
title);
schedule_timeout_uninterruptible(MAX_SCHEDULE_TIMEOUT);
@@ -1214,11 +1221,13 @@ rcu_torture_printk(char *page)
n_rcu_torture_boost_failure,
n_rcu_torture_boosts,
n_rcu_torture_timers);
- cnt += sprintf(&page[cnt], "onoff: %ld/%ld:%ld/%ld ",
- n_online_successes,
- n_online_attempts,
- n_offline_successes,
- n_offline_attempts);
+ cnt += sprintf(&page[cnt],
+ "onoff: %ld/%ld:%ld/%ld %d,%d:%d,%d %lu:%lu (HZ=%d) ",
+ n_online_successes, n_online_attempts,
+ n_offline_successes, n_offline_attempts,
+ min_online, max_online,
+ min_offline, max_offline,
+ sum_online, sum_offline, HZ);
cnt += sprintf(&page[cnt], "barrier: %ld/%ld:%ld",
n_barrier_successes,
n_barrier_attempts,
@@ -1267,7 +1276,7 @@ rcu_torture_stats_print(void)
int cnt;
cnt = rcu_torture_printk(printk_buf);
- printk(KERN_ALERT "%s", printk_buf);
+ pr_alert("%s", printk_buf);
}
/*
@@ -1380,20 +1389,20 @@ rcu_torture_stutter(void *arg)
static inline void
rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, char *tag)
{
- printk(KERN_ALERT "%s" TORTURE_FLAG
- "--- %s: nreaders=%d nfakewriters=%d "
- "stat_interval=%d verbose=%d test_no_idle_hz=%d "
- "shuffle_interval=%d stutter=%d irqreader=%d "
- "fqs_duration=%d fqs_holdoff=%d fqs_stutter=%d "
- "test_boost=%d/%d test_boost_interval=%d "
- "test_boost_duration=%d shutdown_secs=%d "
- "onoff_interval=%d onoff_holdoff=%d\n",
- torture_type, tag, nrealreaders, nfakewriters,
- stat_interval, verbose, test_no_idle_hz, shuffle_interval,
- stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter,
- test_boost, cur_ops->can_boost,
- test_boost_interval, test_boost_duration, shutdown_secs,
- onoff_interval, onoff_holdoff);
+ pr_alert("%s" TORTURE_FLAG
+ "--- %s: nreaders=%d nfakewriters=%d "
+ "stat_interval=%d verbose=%d test_no_idle_hz=%d "
+ "shuffle_interval=%d stutter=%d irqreader=%d "
+ "fqs_duration=%d fqs_holdoff=%d fqs_stutter=%d "
+ "test_boost=%d/%d test_boost_interval=%d "
+ "test_boost_duration=%d shutdown_secs=%d "
+ "onoff_interval=%d onoff_holdoff=%d\n",
+ torture_type, tag, nrealreaders, nfakewriters,
+ stat_interval, verbose, test_no_idle_hz, shuffle_interval,
+ stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter,
+ test_boost, cur_ops->can_boost,
+ test_boost_interval, test_boost_duration, shutdown_secs,
+ onoff_interval, onoff_holdoff);
}
static struct notifier_block rcutorture_shutdown_nb = {
@@ -1460,9 +1469,9 @@ rcu_torture_shutdown(void *arg)
!kthread_should_stop()) {
delta = shutdown_time - jiffies_snap;
if (verbose)
- printk(KERN_ALERT "%s" TORTURE_FLAG
- "rcu_torture_shutdown task: %lu jiffies remaining\n",
- torture_type, delta);
+ pr_alert("%s" TORTURE_FLAG
+ "rcu_torture_shutdown task: %lu jiffies remaining\n",
+ torture_type, delta);
schedule_timeout_interruptible(delta);
jiffies_snap = ACCESS_ONCE(jiffies);
}
@@ -1490,8 +1499,10 @@ static int __cpuinit
rcu_torture_onoff(void *arg)
{
int cpu;
+ unsigned long delta;
int maxcpu = -1;
DEFINE_RCU_RANDOM(rand);
+ unsigned long starttime;
VERBOSE_PRINTK_STRING("rcu_torture_onoff task started");
for_each_online_cpu(cpu)
@@ -1506,29 +1517,51 @@ rcu_torture_onoff(void *arg)
cpu = (rcu_random(&rand) >> 4) % (maxcpu + 1);
if (cpu_online(cpu) && cpu_is_hotpluggable(cpu)) {
if (verbose)
- printk(KERN_ALERT "%s" TORTURE_FLAG
- "rcu_torture_onoff task: offlining %d\n",
- torture_type, cpu);
+ pr_alert("%s" TORTURE_FLAG
+ "rcu_torture_onoff task: offlining %d\n",
+ torture_type, cpu);
+ starttime = jiffies;
n_offline_attempts++;
if (cpu_down(cpu) == 0) {
if (verbose)
- printk(KERN_ALERT "%s" TORTURE_FLAG
- "rcu_torture_onoff task: offlined %d\n",
- torture_type, cpu);
+ pr_alert("%s" TORTURE_FLAG
+ "rcu_torture_onoff task: offlined %d\n",
+ torture_type, cpu);
n_offline_successes++;
+ delta = jiffies - starttime;
+ sum_offline += delta;
+ if (min_offline < 0) {
+ min_offline = delta;
+ max_offline = delta;
+ }
+ if (min_offline > delta)
+ min_offline = delta;
+ if (max_offline < delta)
+ max_offline = delta;
}
} else if (cpu_is_hotpluggable(cpu)) {
if (verbose)
- printk(KERN_ALERT "%s" TORTURE_FLAG
- "rcu_torture_onoff task: onlining %d\n",
- torture_type, cpu);
+ pr_alert("%s" TORTURE_FLAG
+ "rcu_torture_onoff task: onlining %d\n",
+ torture_type, cpu);
+ starttime = jiffies;
n_online_attempts++;
if (cpu_up(cpu) == 0) {
if (verbose)
- printk(KERN_ALERT "%s" TORTURE_FLAG
- "rcu_torture_onoff task: onlined %d\n",
- torture_type, cpu);
+ pr_alert("%s" TORTURE_FLAG
+ "rcu_torture_onoff task: onlined %d\n",
+ torture_type, cpu);
n_online_successes++;
+ delta = jiffies - starttime;
+ sum_online += delta;
+ if (min_online < 0) {
+ min_online = delta;
+ max_online = delta;
+ }
+ if (min_online > delta)
+ min_online = delta;
+ if (max_online < delta)
+ max_online = delta;
}
}
schedule_timeout_interruptible(onoff_interval * HZ);
@@ -1593,14 +1626,14 @@ static int __cpuinit rcu_torture_stall(void *args)
if (!kthread_should_stop()) {
stop_at = get_seconds() + stall_cpu;
/* RCU CPU stall is expected behavior in following code. */
- printk(KERN_ALERT "rcu_torture_stall start.\n");
+ pr_alert("rcu_torture_stall start.\n");
rcu_read_lock();
preempt_disable();
while (ULONG_CMP_LT(get_seconds(), stop_at))
continue; /* Induce RCU CPU stall warning. */
preempt_enable();
rcu_read_unlock();
- printk(KERN_ALERT "rcu_torture_stall end.\n");
+ pr_alert("rcu_torture_stall end.\n");
}
rcutorture_shutdown_absorb("rcu_torture_stall");
while (!kthread_should_stop())
@@ -1716,12 +1749,12 @@ static int rcu_torture_barrier_init(void)
if (n_barrier_cbs == 0)
return 0;
if (cur_ops->call == NULL || cur_ops->cb_barrier == NULL) {
- printk(KERN_ALERT "%s" TORTURE_FLAG
- " Call or barrier ops missing for %s,\n",
- torture_type, cur_ops->name);
- printk(KERN_ALERT "%s" TORTURE_FLAG
- " RCU barrier testing omitted from run.\n",
- torture_type);
+ pr_alert("%s" TORTURE_FLAG
+ " Call or barrier ops missing for %s,\n",
+ torture_type, cur_ops->name);
+ pr_alert("%s" TORTURE_FLAG
+ " RCU barrier testing omitted from run.\n",
+ torture_type);
return 0;
}
atomic_set(&barrier_cbs_count, 0);
@@ -1814,7 +1847,7 @@ rcu_torture_cleanup(void)
mutex_lock(&fullstop_mutex);
rcutorture_record_test_transition();
if (fullstop == FULLSTOP_SHUTDOWN) {
- printk(KERN_WARNING /* but going down anyway, so... */
+ pr_warn(/* but going down anyway, so... */
"Concurrent 'rmmod rcutorture' and shutdown illegal!\n");
mutex_unlock(&fullstop_mutex);
schedule_timeout_uninterruptible(10);
@@ -1938,17 +1971,17 @@ rcu_torture_init(void)
break;
}
if (i == ARRAY_SIZE(torture_ops)) {
- printk(KERN_ALERT "rcu-torture: invalid torture type: \"%s\"\n",
- torture_type);
- printk(KERN_ALERT "rcu-torture types:");
+ pr_alert("rcu-torture: invalid torture type: \"%s\"\n",
+ torture_type);
+ pr_alert("rcu-torture types:");
for (i = 0; i < ARRAY_SIZE(torture_ops); i++)
- printk(KERN_ALERT " %s", torture_ops[i]->name);
- printk(KERN_ALERT "\n");
+ pr_alert(" %s", torture_ops[i]->name);
+ pr_alert("\n");
mutex_unlock(&fullstop_mutex);
return -EINVAL;
}
if (cur_ops->fqs == NULL && fqs_duration != 0) {
- printk(KERN_ALERT "rcu-torture: ->fqs NULL and non-zero fqs_duration, fqs disabled.\n");
+ pr_alert("rcu-torture: ->fqs NULL and non-zero fqs_duration, fqs disabled.\n");
fqs_duration = 0;
}
if (cur_ops->init)
@@ -1996,14 +2029,15 @@ rcu_torture_init(void)
/* Start up the kthreads. */
VERBOSE_PRINTK_STRING("Creating rcu_torture_writer task");
- writer_task = kthread_run(rcu_torture_writer, NULL,
- "rcu_torture_writer");
+ writer_task = kthread_create(rcu_torture_writer, NULL,
+ "rcu_torture_writer");
if (IS_ERR(writer_task)) {
firsterr = PTR_ERR(writer_task);
VERBOSE_PRINTK_ERRSTRING("Failed to create writer");
writer_task = NULL;
goto unwind;
}
+ wake_up_process(writer_task);
fakewriter_tasks = kzalloc(nfakewriters * sizeof(fakewriter_tasks[0]),
GFP_KERNEL);
if (fakewriter_tasks == NULL) {
@@ -2118,14 +2152,15 @@ rcu_torture_init(void)
}
if (shutdown_secs > 0) {
shutdown_time = jiffies + shutdown_secs * HZ;
- shutdown_task = kthread_run(rcu_torture_shutdown, NULL,
- "rcu_torture_shutdown");
+ shutdown_task = kthread_create(rcu_torture_shutdown, NULL,
+ "rcu_torture_shutdown");
if (IS_ERR(shutdown_task)) {
firsterr = PTR_ERR(shutdown_task);
VERBOSE_PRINTK_ERRSTRING("Failed to create shutdown");
shutdown_task = NULL;
goto unwind;
}
+ wake_up_process(shutdown_task);
}
i = rcu_torture_onoff_init();
if (i != 0) {
diff --git a/kernel/rcutree.c b/kernel/rcutree.c
index f280e542e3e9..4fb2376ddf06 100644
--- a/kernel/rcutree.c
+++ b/kernel/rcutree.c
@@ -52,6 +52,7 @@
#include <linux/prefetch.h>
#include <linux/delay.h>
#include <linux/stop_machine.h>
+#include <linux/random.h>
#include "rcutree.h"
#include <trace/events/rcu.h>
@@ -61,6 +62,7 @@
/* Data structures. */
static struct lock_class_key rcu_node_class[RCU_NUM_LVLS];
+static struct lock_class_key rcu_fqs_class[RCU_NUM_LVLS];
#define RCU_STATE_INITIALIZER(sname, cr) { \
.level = { &sname##_state.node[0] }, \
@@ -72,7 +74,6 @@ static struct lock_class_key rcu_node_class[RCU_NUM_LVLS];
.orphan_nxttail = &sname##_state.orphan_nxtlist, \
.orphan_donetail = &sname##_state.orphan_donelist, \
.barrier_mutex = __MUTEX_INITIALIZER(sname##_state.barrier_mutex), \
- .fqslock = __RAW_SPIN_LOCK_UNLOCKED(&sname##_state.fqslock), \
.name = #sname, \
}
@@ -88,7 +89,7 @@ LIST_HEAD(rcu_struct_flavors);
/* Increase (but not decrease) the CONFIG_RCU_FANOUT_LEAF at boot time. */
static int rcu_fanout_leaf = CONFIG_RCU_FANOUT_LEAF;
-module_param(rcu_fanout_leaf, int, 0);
+module_param(rcu_fanout_leaf, int, 0444);
int rcu_num_lvls __read_mostly = RCU_NUM_LVLS;
static int num_rcu_lvl[] = { /* Number of rcu_nodes at specified level. */
NUM_RCU_LVL_0,
@@ -133,13 +134,12 @@ static int rcu_scheduler_fully_active __read_mostly;
*/
static DEFINE_PER_CPU(struct task_struct *, rcu_cpu_kthread_task);
DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_status);
-DEFINE_PER_CPU(int, rcu_cpu_kthread_cpu);
DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_loops);
DEFINE_PER_CPU(char, rcu_cpu_has_work);
#endif /* #ifdef CONFIG_RCU_BOOST */
-static void rcu_node_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu);
+static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu);
static void invoke_rcu_core(void);
static void invoke_rcu_callbacks(struct rcu_state *rsp, struct rcu_data *rdp);
@@ -175,8 +175,6 @@ void rcu_sched_qs(int cpu)
{
struct rcu_data *rdp = &per_cpu(rcu_sched_data, cpu);
- rdp->passed_quiesce_gpnum = rdp->gpnum;
- barrier();
if (rdp->passed_quiesce == 0)
trace_rcu_grace_period("rcu_sched", rdp->gpnum, "cpuqs");
rdp->passed_quiesce = 1;
@@ -186,8 +184,6 @@ void rcu_bh_qs(int cpu)
{
struct rcu_data *rdp = &per_cpu(rcu_bh_data, cpu);
- rdp->passed_quiesce_gpnum = rdp->gpnum;
- barrier();
if (rdp->passed_quiesce == 0)
trace_rcu_grace_period("rcu_bh", rdp->gpnum, "cpuqs");
rdp->passed_quiesce = 1;
@@ -210,15 +206,18 @@ EXPORT_SYMBOL_GPL(rcu_note_context_switch);
DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = {
.dynticks_nesting = DYNTICK_TASK_EXIT_IDLE,
.dynticks = ATOMIC_INIT(1),
+#if defined(CONFIG_RCU_USER_QS) && !defined(CONFIG_RCU_USER_QS_FORCE)
+ .ignore_user_qs = true,
+#endif
};
static int blimit = 10; /* Maximum callbacks per rcu_do_batch. */
static int qhimark = 10000; /* If this many pending, ignore blimit. */
static int qlowmark = 100; /* Once only this many pending, use blimit. */
-module_param(blimit, int, 0);
-module_param(qhimark, int, 0);
-module_param(qlowmark, int, 0);
+module_param(blimit, int, 0444);
+module_param(qhimark, int, 0444);
+module_param(qlowmark, int, 0444);
int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */
int rcu_cpu_stall_timeout __read_mostly = CONFIG_RCU_CPU_STALL_TIMEOUT;
@@ -226,7 +225,14 @@ int rcu_cpu_stall_timeout __read_mostly = CONFIG_RCU_CPU_STALL_TIMEOUT;
module_param(rcu_cpu_stall_suppress, int, 0644);
module_param(rcu_cpu_stall_timeout, int, 0644);
-static void force_quiescent_state(struct rcu_state *rsp, int relaxed);
+static ulong jiffies_till_first_fqs = RCU_JIFFIES_TILL_FORCE_QS;
+static ulong jiffies_till_next_fqs = RCU_JIFFIES_TILL_FORCE_QS;
+
+module_param(jiffies_till_first_fqs, ulong, 0644);
+module_param(jiffies_till_next_fqs, ulong, 0644);
+
+static void force_qs_rnp(struct rcu_state *rsp, int (*f)(struct rcu_data *));
+static void force_quiescent_state(struct rcu_state *rsp);
static int rcu_pending(int cpu);
/*
@@ -252,7 +258,7 @@ EXPORT_SYMBOL_GPL(rcu_batches_completed_bh);
*/
void rcu_bh_force_quiescent_state(void)
{
- force_quiescent_state(&rcu_bh_state, 0);
+ force_quiescent_state(&rcu_bh_state);
}
EXPORT_SYMBOL_GPL(rcu_bh_force_quiescent_state);
@@ -286,7 +292,7 @@ EXPORT_SYMBOL_GPL(rcutorture_record_progress);
*/
void rcu_sched_force_quiescent_state(void)
{
- force_quiescent_state(&rcu_sched_state, 0);
+ force_quiescent_state(&rcu_sched_state);
}
EXPORT_SYMBOL_GPL(rcu_sched_force_quiescent_state);
@@ -305,7 +311,9 @@ cpu_has_callbacks_ready_to_invoke(struct rcu_data *rdp)
static int
cpu_needs_another_gp(struct rcu_state *rsp, struct rcu_data *rdp)
{
- return *rdp->nxttail[RCU_DONE_TAIL] && !rcu_gp_in_progress(rsp);
+ return *rdp->nxttail[RCU_DONE_TAIL +
+ ACCESS_ONCE(rsp->completed) != rdp->completed] &&
+ !rcu_gp_in_progress(rsp);
}
/*
@@ -317,45 +325,17 @@ static struct rcu_node *rcu_get_root(struct rcu_state *rsp)
}
/*
- * If the specified CPU is offline, tell the caller that it is in
- * a quiescent state. Otherwise, whack it with a reschedule IPI.
- * Grace periods can end up waiting on an offline CPU when that
- * CPU is in the process of coming online -- it will be added to the
- * rcu_node bitmasks before it actually makes it online. The same thing
- * can happen while a CPU is in the process of coming online. Because this
- * race is quite rare, we check for it after detecting that the grace
- * period has been delayed rather than checking each and every CPU
- * each and every time we start a new grace period.
- */
-static int rcu_implicit_offline_qs(struct rcu_data *rdp)
-{
- /*
- * If the CPU is offline for more than a jiffy, it is in a quiescent
- * state. We can trust its state not to change because interrupts
- * are disabled. The reason for the jiffy's worth of slack is to
- * handle CPUs initializing on the way up and finding their way
- * to the idle loop on the way down.
- */
- if (cpu_is_offline(rdp->cpu) &&
- ULONG_CMP_LT(rdp->rsp->gp_start + 2, jiffies)) {
- trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, "ofl");
- rdp->offline_fqs++;
- return 1;
- }
- return 0;
-}
-
-/*
- * rcu_idle_enter_common - inform RCU that current CPU is moving towards idle
+ * rcu_eqs_enter_common - current CPU is moving towards extended quiescent state
*
* If the new value of the ->dynticks_nesting counter now is zero,
* we really have entered idle, and must do the appropriate accounting.
* The caller must have disabled interrupts.
*/
-static void rcu_idle_enter_common(struct rcu_dynticks *rdtp, long long oldval)
+static void rcu_eqs_enter_common(struct rcu_dynticks *rdtp, long long oldval,
+ bool user)
{
trace_rcu_dyntick("Start", oldval, 0);
- if (!is_idle_task(current)) {
+ if (!user && !is_idle_task(current)) {
struct task_struct *idle = idle_task(smp_processor_id());
trace_rcu_dyntick("Error on entry: not idle task", oldval, 0);
@@ -372,7 +352,7 @@ static void rcu_idle_enter_common(struct rcu_dynticks *rdtp, long long oldval)
WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1);
/*
- * The idle task is not permitted to enter the idle loop while
+ * It is illegal to enter an extended quiescent state while
* in an RCU read-side critical section.
*/
rcu_lockdep_assert(!lock_is_held(&rcu_lock_map),
@@ -383,6 +363,25 @@ static void rcu_idle_enter_common(struct rcu_dynticks *rdtp, long long oldval)
"Illegal idle entry in RCU-sched read-side critical section.");
}
+/*
+ * Enter an RCU extended quiescent state, which can be either the
+ * idle loop or adaptive-tickless usermode execution.
+ */
+static void rcu_eqs_enter(bool user)
+{
+ long long oldval;
+ struct rcu_dynticks *rdtp;
+
+ rdtp = &__get_cpu_var(rcu_dynticks);
+ oldval = rdtp->dynticks_nesting;
+ WARN_ON_ONCE((oldval & DYNTICK_TASK_NEST_MASK) == 0);
+ if ((oldval & DYNTICK_TASK_NEST_MASK) == DYNTICK_TASK_NEST_VALUE)
+ rdtp->dynticks_nesting = 0;
+ else
+ rdtp->dynticks_nesting -= DYNTICK_TASK_NEST_VALUE;
+ rcu_eqs_enter_common(rdtp, oldval, user);
+}
+
/**
* rcu_idle_enter - inform RCU that current CPU is entering idle
*
@@ -398,21 +397,70 @@ static void rcu_idle_enter_common(struct rcu_dynticks *rdtp, long long oldval)
void rcu_idle_enter(void)
{
unsigned long flags;
- long long oldval;
+
+ local_irq_save(flags);
+ rcu_eqs_enter(false);
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL_GPL(rcu_idle_enter);
+
+#ifdef CONFIG_RCU_USER_QS
+/**
+ * rcu_user_enter - inform RCU that we are resuming userspace.
+ *
+ * Enter RCU idle mode right before resuming userspace. No use of RCU
+ * is permitted between this call and rcu_user_exit(). This way the
+ * CPU doesn't need to maintain the tick for RCU maintenance purposes
+ * when the CPU runs in userspace.
+ */
+void rcu_user_enter(void)
+{
+ unsigned long flags;
struct rcu_dynticks *rdtp;
+ /*
+ * Some contexts may involve an exception occuring in an irq,
+ * leading to that nesting:
+ * rcu_irq_enter() rcu_user_exit() rcu_user_exit() rcu_irq_exit()
+ * This would mess up the dyntick_nesting count though. And rcu_irq_*()
+ * helpers are enough to protect RCU uses inside the exception. So
+ * just return immediately if we detect we are in an IRQ.
+ */
+ if (in_interrupt())
+ return;
+
+ WARN_ON_ONCE(!current->mm);
+
local_irq_save(flags);
rdtp = &__get_cpu_var(rcu_dynticks);
- oldval = rdtp->dynticks_nesting;
- WARN_ON_ONCE((oldval & DYNTICK_TASK_NEST_MASK) == 0);
- if ((oldval & DYNTICK_TASK_NEST_MASK) == DYNTICK_TASK_NEST_VALUE)
- rdtp->dynticks_nesting = 0;
- else
- rdtp->dynticks_nesting -= DYNTICK_TASK_NEST_VALUE;
- rcu_idle_enter_common(rdtp, oldval);
+ if (!rdtp->ignore_user_qs && !rdtp->in_user) {
+ rdtp->in_user = true;
+ rcu_eqs_enter(true);
+ }
local_irq_restore(flags);
}
-EXPORT_SYMBOL_GPL(rcu_idle_enter);
+
+/**
+ * rcu_user_enter_after_irq - inform RCU that we are going to resume userspace
+ * after the current irq returns.
+ *
+ * This is similar to rcu_user_enter() but in the context of a non-nesting
+ * irq. After this call, RCU enters into idle mode when the interrupt
+ * returns.
+ */
+void rcu_user_enter_after_irq(void)
+{
+ unsigned long flags;
+ struct rcu_dynticks *rdtp;
+
+ local_irq_save(flags);
+ rdtp = &__get_cpu_var(rcu_dynticks);
+ /* Ensure this irq is interrupting a non-idle RCU state. */
+ WARN_ON_ONCE(!(rdtp->dynticks_nesting & DYNTICK_TASK_MASK));
+ rdtp->dynticks_nesting = 1;
+ local_irq_restore(flags);
+}
+#endif /* CONFIG_RCU_USER_QS */
/**
* rcu_irq_exit - inform RCU that current CPU is exiting irq towards idle
@@ -444,18 +492,19 @@ void rcu_irq_exit(void)
if (rdtp->dynticks_nesting)
trace_rcu_dyntick("--=", oldval, rdtp->dynticks_nesting);
else
- rcu_idle_enter_common(rdtp, oldval);
+ rcu_eqs_enter_common(rdtp, oldval, true);
local_irq_restore(flags);
}
/*
- * rcu_idle_exit_common - inform RCU that current CPU is moving away from idle
+ * rcu_eqs_exit_common - current CPU moving away from extended quiescent state
*
* If the new value of the ->dynticks_nesting counter was previously zero,
* we really have exited idle, and must do the appropriate accounting.
* The caller must have disabled interrupts.
*/
-static void rcu_idle_exit_common(struct rcu_dynticks *rdtp, long long oldval)
+static void rcu_eqs_exit_common(struct rcu_dynticks *rdtp, long long oldval,
+ int user)
{
smp_mb__before_atomic_inc(); /* Force ordering w/previous sojourn. */
atomic_inc(&rdtp->dynticks);
@@ -464,7 +513,7 @@ static void rcu_idle_exit_common(struct rcu_dynticks *rdtp, long long oldval)
WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1));
rcu_cleanup_after_idle(smp_processor_id());
trace_rcu_dyntick("End", oldval, rdtp->dynticks_nesting);
- if (!is_idle_task(current)) {
+ if (!user && !is_idle_task(current)) {
struct task_struct *idle = idle_task(smp_processor_id());
trace_rcu_dyntick("Error on exit: not idle task",
@@ -476,6 +525,25 @@ static void rcu_idle_exit_common(struct rcu_dynticks *rdtp, long long oldval)
}
}
+/*
+ * Exit an RCU extended quiescent state, which can be either the
+ * idle loop or adaptive-tickless usermode execution.
+ */
+static void rcu_eqs_exit(bool user)
+{
+ struct rcu_dynticks *rdtp;
+ long long oldval;
+
+ rdtp = &__get_cpu_var(rcu_dynticks);
+ oldval = rdtp->dynticks_nesting;
+ WARN_ON_ONCE(oldval < 0);
+ if (oldval & DYNTICK_TASK_NEST_MASK)
+ rdtp->dynticks_nesting += DYNTICK_TASK_NEST_VALUE;
+ else
+ rdtp->dynticks_nesting = DYNTICK_TASK_EXIT_IDLE;
+ rcu_eqs_exit_common(rdtp, oldval, user);
+}
+
/**
* rcu_idle_exit - inform RCU that current CPU is leaving idle
*
@@ -490,21 +558,67 @@ static void rcu_idle_exit_common(struct rcu_dynticks *rdtp, long long oldval)
void rcu_idle_exit(void)
{
unsigned long flags;
+
+ local_irq_save(flags);
+ rcu_eqs_exit(false);
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL_GPL(rcu_idle_exit);
+
+#ifdef CONFIG_RCU_USER_QS
+/**
+ * rcu_user_exit - inform RCU that we are exiting userspace.
+ *
+ * Exit RCU idle mode while entering the kernel because it can
+ * run a RCU read side critical section anytime.
+ */
+void rcu_user_exit(void)
+{
+ unsigned long flags;
struct rcu_dynticks *rdtp;
- long long oldval;
+
+ /*
+ * Some contexts may involve an exception occuring in an irq,
+ * leading to that nesting:
+ * rcu_irq_enter() rcu_user_exit() rcu_user_exit() rcu_irq_exit()
+ * This would mess up the dyntick_nesting count though. And rcu_irq_*()
+ * helpers are enough to protect RCU uses inside the exception. So
+ * just return immediately if we detect we are in an IRQ.
+ */
+ if (in_interrupt())
+ return;
local_irq_save(flags);
rdtp = &__get_cpu_var(rcu_dynticks);
- oldval = rdtp->dynticks_nesting;
- WARN_ON_ONCE(oldval < 0);
- if (oldval & DYNTICK_TASK_NEST_MASK)
- rdtp->dynticks_nesting += DYNTICK_TASK_NEST_VALUE;
- else
- rdtp->dynticks_nesting = DYNTICK_TASK_EXIT_IDLE;
- rcu_idle_exit_common(rdtp, oldval);
+ if (rdtp->in_user) {
+ rdtp->in_user = false;
+ rcu_eqs_exit(true);
+ }
local_irq_restore(flags);
}
-EXPORT_SYMBOL_GPL(rcu_idle_exit);
+
+/**
+ * rcu_user_exit_after_irq - inform RCU that we won't resume to userspace
+ * idle mode after the current non-nesting irq returns.
+ *
+ * This is similar to rcu_user_exit() but in the context of an irq.
+ * This is called when the irq has interrupted a userspace RCU idle mode
+ * context. When the current non-nesting interrupt returns after this call,
+ * the CPU won't restore the RCU idle mode.
+ */
+void rcu_user_exit_after_irq(void)
+{
+ unsigned long flags;
+ struct rcu_dynticks *rdtp;
+
+ local_irq_save(flags);
+ rdtp = &__get_cpu_var(rcu_dynticks);
+ /* Ensure we are interrupting an RCU idle mode. */
+ WARN_ON_ONCE(rdtp->dynticks_nesting & DYNTICK_TASK_NEST_MASK);
+ rdtp->dynticks_nesting += DYNTICK_TASK_EXIT_IDLE;
+ local_irq_restore(flags);
+}
+#endif /* CONFIG_RCU_USER_QS */
/**
* rcu_irq_enter - inform RCU that current CPU is entering irq away from idle
@@ -539,7 +653,7 @@ void rcu_irq_enter(void)
if (oldval)
trace_rcu_dyntick("++=", oldval, rdtp->dynticks_nesting);
else
- rcu_idle_exit_common(rdtp, oldval);
+ rcu_eqs_exit_common(rdtp, oldval, true);
local_irq_restore(flags);
}
@@ -603,6 +717,21 @@ int rcu_is_cpu_idle(void)
}
EXPORT_SYMBOL(rcu_is_cpu_idle);
+#ifdef CONFIG_RCU_USER_QS
+void rcu_user_hooks_switch(struct task_struct *prev,
+ struct task_struct *next)
+{
+ struct rcu_dynticks *rdtp;
+
+ /* Interrupts are disabled in context switch */
+ rdtp = &__get_cpu_var(rcu_dynticks);
+ if (!rdtp->ignore_user_qs) {
+ clear_tsk_thread_flag(prev, TIF_NOHZ);
+ set_tsk_thread_flag(next, TIF_NOHZ);
+ }
+}
+#endif /* #ifdef CONFIG_RCU_USER_QS */
+
#if defined(CONFIG_PROVE_RCU) && defined(CONFIG_HOTPLUG_CPU)
/*
@@ -673,7 +802,7 @@ static int dyntick_save_progress_counter(struct rcu_data *rdp)
* Return true if the specified CPU has passed through a quiescent
* state by virtue of being in or having passed through an dynticks
* idle state since the last call to dyntick_save_progress_counter()
- * for this same CPU.
+ * for this same CPU, or by virtue of having been offline.
*/
static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
{
@@ -697,8 +826,26 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
return 1;
}
- /* Go check for the CPU being offline. */
- return rcu_implicit_offline_qs(rdp);
+ /*
+ * Check for the CPU being offline, but only if the grace period
+ * is old enough. We don't need to worry about the CPU changing
+ * state: If we see it offline even once, it has been through a
+ * quiescent state.
+ *
+ * The reason for insisting that the grace period be at least
+ * one jiffy old is that CPUs that are not quite online and that
+ * have just gone offline can still execute RCU read-side critical
+ * sections.
+ */
+ if (ULONG_CMP_GE(rdp->rsp->gp_start + 2, jiffies))
+ return 0; /* Grace period is not old enough. */
+ barrier();
+ if (cpu_is_offline(rdp->cpu)) {
+ trace_rcu_fqs(rdp->rsp->name, rdp->gpnum, rdp->cpu, "ofl");
+ rdp->offline_fqs++;
+ return 1;
+ }
+ return 0;
}
static int jiffies_till_stall_check(void)
@@ -755,14 +902,15 @@ static void print_other_cpu_stall(struct rcu_state *rsp)
rcu_for_each_leaf_node(rsp, rnp) {
raw_spin_lock_irqsave(&rnp->lock, flags);
ndetected += rcu_print_task_stall(rnp);
+ if (rnp->qsmask != 0) {
+ for (cpu = 0; cpu <= rnp->grphi - rnp->grplo; cpu++)
+ if (rnp->qsmask & (1UL << cpu)) {
+ print_cpu_stall_info(rsp,
+ rnp->grplo + cpu);
+ ndetected++;
+ }
+ }
raw_spin_unlock_irqrestore(&rnp->lock, flags);
- if (rnp->qsmask == 0)
- continue;
- for (cpu = 0; cpu <= rnp->grphi - rnp->grplo; cpu++)
- if (rnp->qsmask & (1UL << cpu)) {
- print_cpu_stall_info(rsp, rnp->grplo + cpu);
- ndetected++;
- }
}
/*
@@ -782,11 +930,11 @@ static void print_other_cpu_stall(struct rcu_state *rsp)
else if (!trigger_all_cpu_backtrace())
dump_stack();
- /* If so configured, complain about tasks blocking the grace period. */
+ /* Complain about tasks blocking the grace period. */
rcu_print_detail_task_stall(rsp);
- force_quiescent_state(rsp, 0); /* Kick them all. */
+ force_quiescent_state(rsp); /* Kick them all. */
}
static void print_cpu_stall(struct rcu_state *rsp)
@@ -827,7 +975,8 @@ static void check_cpu_stall(struct rcu_state *rsp, struct rcu_data *rdp)
j = ACCESS_ONCE(jiffies);
js = ACCESS_ONCE(rsp->jiffies_stall);
rnp = rdp->mynode;
- if ((ACCESS_ONCE(rnp->qsmask) & rdp->grpmask) && ULONG_CMP_GE(j, js)) {
+ if (rcu_gp_in_progress(rsp) &&
+ (ACCESS_ONCE(rnp->qsmask) & rdp->grpmask) && ULONG_CMP_GE(j, js)) {
/* We haven't checked in, so go dump stack. */
print_cpu_stall(rsp);
@@ -889,12 +1038,8 @@ static void __note_new_gpnum(struct rcu_state *rsp, struct rcu_node *rnp, struct
*/
rdp->gpnum = rnp->gpnum;
trace_rcu_grace_period(rsp->name, rdp->gpnum, "cpustart");
- if (rnp->qsmask & rdp->grpmask) {
- rdp->qs_pending = 1;
- rdp->passed_quiesce = 0;
- } else {
- rdp->qs_pending = 0;
- }
+ rdp->passed_quiesce = 0;
+ rdp->qs_pending = !!(rnp->qsmask & rdp->grpmask);
zero_cpu_stall_ticks(rdp);
}
}
@@ -974,10 +1119,13 @@ __rcu_process_gp_end(struct rcu_state *rsp, struct rcu_node *rnp, struct rcu_dat
* our behalf. Catch up with this state to avoid noting
* spurious new grace periods. If another grace period
* has started, then rnp->gpnum will have advanced, so
- * we will detect this later on.
+ * we will detect this later on. Of course, any quiescent
+ * states we found for the old GP are now invalid.
*/
- if (ULONG_CMP_LT(rdp->gpnum, rdp->completed))
+ if (ULONG_CMP_LT(rdp->gpnum, rdp->completed)) {
rdp->gpnum = rdp->completed;
+ rdp->passed_quiesce = 0;
+ }
/*
* If RCU does not need a quiescent state from this CPU,
@@ -1021,97 +1169,56 @@ rcu_start_gp_per_cpu(struct rcu_state *rsp, struct rcu_node *rnp, struct rcu_dat
/* Prior grace period ended, so advance callbacks for current CPU. */
__rcu_process_gp_end(rsp, rnp, rdp);
- /*
- * Because this CPU just now started the new grace period, we know
- * that all of its callbacks will be covered by this upcoming grace
- * period, even the ones that were registered arbitrarily recently.
- * Therefore, advance all outstanding callbacks to RCU_WAIT_TAIL.
- *
- * Other CPUs cannot be sure exactly when the grace period started.
- * Therefore, their recently registered callbacks must pass through
- * an additional RCU_NEXT_READY stage, so that they will be handled
- * by the next RCU grace period.
- */
- rdp->nxttail[RCU_NEXT_READY_TAIL] = rdp->nxttail[RCU_NEXT_TAIL];
- rdp->nxttail[RCU_WAIT_TAIL] = rdp->nxttail[RCU_NEXT_TAIL];
-
/* Set state so that this CPU will detect the next quiescent state. */
__note_new_gpnum(rsp, rnp, rdp);
}
/*
- * Start a new RCU grace period if warranted, re-initializing the hierarchy
- * in preparation for detecting the next grace period. The caller must hold
- * the root node's ->lock, which is released before return. Hard irqs must
- * be disabled.
- *
- * Note that it is legal for a dying CPU (which is marked as offline) to
- * invoke this function. This can happen when the dying CPU reports its
- * quiescent state.
+ * Initialize a new grace period.
*/
-static void
-rcu_start_gp(struct rcu_state *rsp, unsigned long flags)
- __releases(rcu_get_root(rsp)->lock)
+static int rcu_gp_init(struct rcu_state *rsp)
{
- struct rcu_data *rdp = this_cpu_ptr(rsp->rda);
+ struct rcu_data *rdp;
struct rcu_node *rnp = rcu_get_root(rsp);
- if (!rcu_scheduler_fully_active ||
- !cpu_needs_another_gp(rsp, rdp)) {
- /*
- * Either the scheduler hasn't yet spawned the first
- * non-idle task or this CPU does not need another
- * grace period. Either way, don't start a new grace
- * period.
- */
- raw_spin_unlock_irqrestore(&rnp->lock, flags);
- return;
- }
+ raw_spin_lock_irq(&rnp->lock);
+ rsp->gp_flags = 0; /* Clear all flags: New grace period. */
- if (rsp->fqs_active) {
- /*
- * This CPU needs a grace period, but force_quiescent_state()
- * is running. Tell it to start one on this CPU's behalf.
- */
- rsp->fqs_need_gp = 1;
- raw_spin_unlock_irqrestore(&rnp->lock, flags);
- return;
+ if (rcu_gp_in_progress(rsp)) {
+ /* Grace period already in progress, don't start another. */
+ raw_spin_unlock_irq(&rnp->lock);
+ return 0;
}
/* Advance to a new grace period and initialize state. */
rsp->gpnum++;
trace_rcu_grace_period(rsp->name, rsp->gpnum, "start");
- WARN_ON_ONCE(rsp->fqs_state == RCU_GP_INIT);
- rsp->fqs_state = RCU_GP_INIT; /* Hold off force_quiescent_state. */
- rsp->jiffies_force_qs = jiffies + RCU_JIFFIES_TILL_FORCE_QS;
record_gp_stall_check_time(rsp);
- raw_spin_unlock(&rnp->lock); /* leave irqs disabled. */
+ raw_spin_unlock_irq(&rnp->lock);
/* Exclude any concurrent CPU-hotplug operations. */
- raw_spin_lock(&rsp->onofflock); /* irqs already disabled. */
+ get_online_cpus();
/*
* Set the quiescent-state-needed bits in all the rcu_node
- * structures for all currently online CPUs in breadth-first
- * order, starting from the root rcu_node structure. This
- * operation relies on the layout of the hierarchy within the
- * rsp->node[] array. Note that other CPUs will access only
- * the leaves of the hierarchy, which still indicate that no
+ * structures for all currently online CPUs in breadth-first order,
+ * starting from the root rcu_node structure, relying on the layout
+ * of the tree within the rsp->node[] array. Note that other CPUs
+ * will access only the leaves of the hierarchy, thus seeing that no
* grace period is in progress, at least until the corresponding
* leaf node has been initialized. In addition, we have excluded
* CPU-hotplug operations.
*
- * Note that the grace period cannot complete until we finish
- * the initialization process, as there will be at least one
- * qsmask bit set in the root node until that time, namely the
- * one corresponding to this CPU, due to the fact that we have
- * irqs disabled.
+ * The grace period cannot complete until the initialization
+ * process finishes, because this kthread handles both.
*/
rcu_for_each_node_breadth_first(rsp, rnp) {
- raw_spin_lock(&rnp->lock); /* irqs already disabled. */
+ raw_spin_lock_irq(&rnp->lock);
+ rdp = this_cpu_ptr(rsp->rda);
rcu_preempt_check_blocked_tasks(rnp);
rnp->qsmask = rnp->qsmaskinit;
rnp->gpnum = rsp->gpnum;
+ WARN_ON_ONCE(rnp->completed != rsp->completed);
rnp->completed = rsp->completed;
if (rnp == rdp->mynode)
rcu_start_gp_per_cpu(rsp, rnp, rdp);
@@ -1119,37 +1226,54 @@ rcu_start_gp(struct rcu_state *rsp, unsigned long flags)
trace_rcu_grace_period_init(rsp->name, rnp->gpnum,
rnp->level, rnp->grplo,
rnp->grphi, rnp->qsmask);
- raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
+ raw_spin_unlock_irq(&rnp->lock);
+#ifdef CONFIG_PROVE_RCU_DELAY
+ if ((random32() % (rcu_num_nodes * 8)) == 0)
+ schedule_timeout_uninterruptible(2);
+#endif /* #ifdef CONFIG_PROVE_RCU_DELAY */
+ cond_resched();
}
- rnp = rcu_get_root(rsp);
- raw_spin_lock(&rnp->lock); /* irqs already disabled. */
- rsp->fqs_state = RCU_SIGNAL_INIT; /* force_quiescent_state now OK. */
- raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
- raw_spin_unlock_irqrestore(&rsp->onofflock, flags);
+ put_online_cpus();
+ return 1;
}
/*
- * Report a full set of quiescent states to the specified rcu_state
- * data structure. This involves cleaning up after the prior grace
- * period and letting rcu_start_gp() start up the next grace period
- * if one is needed. Note that the caller must hold rnp->lock, as
- * required by rcu_start_gp(), which will release it.
+ * Do one round of quiescent-state forcing.
*/
-static void rcu_report_qs_rsp(struct rcu_state *rsp, unsigned long flags)
- __releases(rcu_get_root(rsp)->lock)
+int rcu_gp_fqs(struct rcu_state *rsp, int fqs_state_in)
{
- unsigned long gp_duration;
+ int fqs_state = fqs_state_in;
struct rcu_node *rnp = rcu_get_root(rsp);
- struct rcu_data *rdp = this_cpu_ptr(rsp->rda);
- WARN_ON_ONCE(!rcu_gp_in_progress(rsp));
+ rsp->n_force_qs++;
+ if (fqs_state == RCU_SAVE_DYNTICK) {
+ /* Collect dyntick-idle snapshots. */
+ force_qs_rnp(rsp, dyntick_save_progress_counter);
+ fqs_state = RCU_FORCE_QS;
+ } else {
+ /* Handle dyntick-idle and offline CPUs. */
+ force_qs_rnp(rsp, rcu_implicit_dynticks_qs);
+ }
+ /* Clear flag to prevent immediate re-entry. */
+ if (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) {
+ raw_spin_lock_irq(&rnp->lock);
+ rsp->gp_flags &= ~RCU_GP_FLAG_FQS;
+ raw_spin_unlock_irq(&rnp->lock);
+ }
+ return fqs_state;
+}
- /*
- * Ensure that all grace-period and pre-grace-period activity
- * is seen before the assignment to rsp->completed.
- */
- smp_mb(); /* See above block comment. */
+/*
+ * Clean up after the old grace period.
+ */
+static void rcu_gp_cleanup(struct rcu_state *rsp)
+{
+ unsigned long gp_duration;
+ struct rcu_data *rdp;
+ struct rcu_node *rnp = rcu_get_root(rsp);
+
+ raw_spin_lock_irq(&rnp->lock);
gp_duration = jiffies - rsp->gp_start;
if (gp_duration > rsp->gp_max)
rsp->gp_max = gp_duration;
@@ -1161,35 +1285,149 @@ static void rcu_report_qs_rsp(struct rcu_state *rsp, unsigned long flags)
* they can do to advance the grace period. It is therefore
* safe for us to drop the lock in order to mark the grace
* period as completed in all of the rcu_node structures.
- *
- * But if this CPU needs another grace period, it will take
- * care of this while initializing the next grace period.
- * We use RCU_WAIT_TAIL instead of the usual RCU_DONE_TAIL
- * because the callbacks have not yet been advanced: Those
- * callbacks are waiting on the grace period that just now
- * completed.
*/
- if (*rdp->nxttail[RCU_WAIT_TAIL] == NULL) {
- raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
+ raw_spin_unlock_irq(&rnp->lock);
- /*
- * Propagate new ->completed value to rcu_node structures
- * so that other CPUs don't have to wait until the start
- * of the next grace period to process their callbacks.
- */
- rcu_for_each_node_breadth_first(rsp, rnp) {
- raw_spin_lock(&rnp->lock); /* irqs already disabled. */
- rnp->completed = rsp->gpnum;
- raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
- }
- rnp = rcu_get_root(rsp);
- raw_spin_lock(&rnp->lock); /* irqs already disabled. */
+ /*
+ * Propagate new ->completed value to rcu_node structures so
+ * that other CPUs don't have to wait until the start of the next
+ * grace period to process their callbacks. This also avoids
+ * some nasty RCU grace-period initialization races by forcing
+ * the end of the current grace period to be completely recorded in
+ * all of the rcu_node structures before the beginning of the next
+ * grace period is recorded in any of the rcu_node structures.
+ */
+ rcu_for_each_node_breadth_first(rsp, rnp) {
+ raw_spin_lock_irq(&rnp->lock);
+ rnp->completed = rsp->gpnum;
+ raw_spin_unlock_irq(&rnp->lock);
+ cond_resched();
}
+ rnp = rcu_get_root(rsp);
+ raw_spin_lock_irq(&rnp->lock);
- rsp->completed = rsp->gpnum; /* Declare the grace period complete. */
+ rsp->completed = rsp->gpnum; /* Declare grace period done. */
trace_rcu_grace_period(rsp->name, rsp->completed, "end");
rsp->fqs_state = RCU_GP_IDLE;
- rcu_start_gp(rsp, flags); /* releases root node's rnp->lock. */
+ rdp = this_cpu_ptr(rsp->rda);
+ if (cpu_needs_another_gp(rsp, rdp))
+ rsp->gp_flags = 1;
+ raw_spin_unlock_irq(&rnp->lock);
+}
+
+/*
+ * Body of kthread that handles grace periods.
+ */
+static int __noreturn rcu_gp_kthread(void *arg)
+{
+ int fqs_state;
+ unsigned long j;
+ int ret;
+ struct rcu_state *rsp = arg;
+ struct rcu_node *rnp = rcu_get_root(rsp);
+
+ for (;;) {
+
+ /* Handle grace-period start. */
+ for (;;) {
+ wait_event_interruptible(rsp->gp_wq,
+ rsp->gp_flags &
+ RCU_GP_FLAG_INIT);
+ if ((rsp->gp_flags & RCU_GP_FLAG_INIT) &&
+ rcu_gp_init(rsp))
+ break;
+ cond_resched();
+ flush_signals(current);
+ }
+
+ /* Handle quiescent-state forcing. */
+ fqs_state = RCU_SAVE_DYNTICK;
+ j = jiffies_till_first_fqs;
+ if (j > HZ) {
+ j = HZ;
+ jiffies_till_first_fqs = HZ;
+ }
+ for (;;) {
+ rsp->jiffies_force_qs = jiffies + j;
+ ret = wait_event_interruptible_timeout(rsp->gp_wq,
+ (rsp->gp_flags & RCU_GP_FLAG_FQS) ||
+ (!ACCESS_ONCE(rnp->qsmask) &&
+ !rcu_preempt_blocked_readers_cgp(rnp)),
+ j);
+ /* If grace period done, leave loop. */
+ if (!ACCESS_ONCE(rnp->qsmask) &&
+ !rcu_preempt_blocked_readers_cgp(rnp))
+ break;
+ /* If time for quiescent-state forcing, do it. */
+ if (ret == 0 || (rsp->gp_flags & RCU_GP_FLAG_FQS)) {
+ fqs_state = rcu_gp_fqs(rsp, fqs_state);
+ cond_resched();
+ } else {
+ /* Deal with stray signal. */
+ cond_resched();
+ flush_signals(current);
+ }
+ j = jiffies_till_next_fqs;
+ if (j > HZ) {
+ j = HZ;
+ jiffies_till_next_fqs = HZ;
+ } else if (j < 1) {
+ j = 1;
+ jiffies_till_next_fqs = 1;
+ }
+ }
+
+ /* Handle grace-period end. */
+ rcu_gp_cleanup(rsp);
+ }
+}
+
+/*
+ * Start a new RCU grace period if warranted, re-initializing the hierarchy
+ * in preparation for detecting the next grace period. The caller must hold
+ * the root node's ->lock, which is released before return. Hard irqs must
+ * be disabled.
+ *
+ * Note that it is legal for a dying CPU (which is marked as offline) to
+ * invoke this function. This can happen when the dying CPU reports its
+ * quiescent state.
+ */
+static void
+rcu_start_gp(struct rcu_state *rsp, unsigned long flags)
+ __releases(rcu_get_root(rsp)->lock)
+{
+ struct rcu_data *rdp = this_cpu_ptr(rsp->rda);
+ struct rcu_node *rnp = rcu_get_root(rsp);
+
+ if (!rsp->gp_kthread ||
+ !cpu_needs_another_gp(rsp, rdp)) {
+ /*
+ * Either we have not yet spawned the grace-period
+ * task or this CPU does not need another grace period.
+ * Either way, don't start a new grace period.
+ */
+ raw_spin_unlock_irqrestore(&rnp->lock, flags);
+ return;
+ }
+
+ rsp->gp_flags = RCU_GP_FLAG_INIT;
+ raw_spin_unlock_irqrestore(&rnp->lock, flags);
+ wake_up(&rsp->gp_wq);
+}
+
+/*
+ * Report a full set of quiescent states to the specified rcu_state
+ * data structure. This involves cleaning up after the prior grace
+ * period and letting rcu_start_gp() start up the next grace period
+ * if one is needed. Note that the caller must hold rnp->lock, as
+ * required by rcu_start_gp(), which will release it.
+ */
+static void rcu_report_qs_rsp(struct rcu_state *rsp, unsigned long flags)
+ __releases(rcu_get_root(rsp)->lock)
+{
+ WARN_ON_ONCE(!rcu_gp_in_progress(rsp));
+ raw_spin_unlock_irqrestore(&rcu_get_root(rsp)->lock, flags);
+ wake_up(&rsp->gp_wq); /* Memory barrier implied by wake_up() path. */
}
/*
@@ -1258,7 +1496,7 @@ rcu_report_qs_rnp(unsigned long mask, struct rcu_state *rsp,
* based on quiescent states detected in an earlier grace period!
*/
static void
-rcu_report_qs_rdp(int cpu, struct rcu_state *rsp, struct rcu_data *rdp, long lastgp)
+rcu_report_qs_rdp(int cpu, struct rcu_state *rsp, struct rcu_data *rdp)
{
unsigned long flags;
unsigned long mask;
@@ -1266,7 +1504,8 @@ rcu_report_qs_rdp(int cpu, struct rcu_state *rsp, struct rcu_data *rdp, long las
rnp = rdp->mynode;
raw_spin_lock_irqsave(&rnp->lock, flags);
- if (lastgp != rnp->gpnum || rnp->completed == rnp->gpnum) {
+ if (rdp->passed_quiesce == 0 || rdp->gpnum != rnp->gpnum ||
+ rnp->completed == rnp->gpnum) {
/*
* The grace period in which this quiescent state was
@@ -1325,7 +1564,7 @@ rcu_check_quiescent_state(struct rcu_state *rsp, struct rcu_data *rdp)
* Tell RCU we are done (but rcu_report_qs_rdp() will be the
* judge of that).
*/
- rcu_report_qs_rdp(rdp->cpu, rsp, rdp, rdp->passed_quiesce_gpnum);
+ rcu_report_qs_rdp(rdp->cpu, rsp, rdp);
}
#ifdef CONFIG_HOTPLUG_CPU
@@ -1390,17 +1629,6 @@ static void rcu_adopt_orphan_cbs(struct rcu_state *rsp)
int i;
struct rcu_data *rdp = __this_cpu_ptr(rsp->rda);
- /*
- * If there is an rcu_barrier() operation in progress, then
- * only the task doing that operation is permitted to adopt
- * callbacks. To do otherwise breaks rcu_barrier() and friends
- * by causing them to fail to wait for the callbacks in the
- * orphanage.
- */
- if (rsp->rcu_barrier_in_progress &&
- rsp->rcu_barrier_in_progress != current)
- return;
-
/* Do the accounting first. */
rdp->qlen_lazy += rsp->qlen_lazy;
rdp->qlen += rsp->qlen;
@@ -1455,9 +1683,8 @@ static void rcu_cleanup_dying_cpu(struct rcu_state *rsp)
* The CPU has been completely removed, and some other CPU is reporting
* this fact from process context. Do the remainder of the cleanup,
* including orphaning the outgoing CPU's RCU callbacks, and also
- * adopting them, if there is no _rcu_barrier() instance running.
- * There can only be one CPU hotplug operation at a time, so no other
- * CPU can be attempting to update rcu_cpu_kthread_task.
+ * adopting them. There can only be one CPU hotplug operation at a time,
+ * so no other CPU can be attempting to update rcu_cpu_kthread_task.
*/
static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
{
@@ -1468,8 +1695,7 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */
/* Adjust any no-longer-needed kthreads. */
- rcu_stop_cpu_kthread(cpu);
- rcu_node_kthread_setaffinity(rnp, -1);
+ rcu_boost_kthread_setaffinity(rnp, -1);
/* Remove the dead CPU from the bitmasks in the rcu_node hierarchy. */
@@ -1515,14 +1741,13 @@ static void rcu_cleanup_dead_cpu(int cpu, struct rcu_state *rsp)
WARN_ONCE(rdp->qlen != 0 || rdp->nxtlist != NULL,
"rcu_cleanup_dead_cpu: Callbacks on offline CPU %d: qlen=%lu, nxtlist=%p\n",
cpu, rdp->qlen, rdp->nxtlist);
+ init_callback_list(rdp);
+ /* Disallow further callbacks on this CPU. */
+ rdp->nxttail[RCU_NEXT_TAIL] = NULL;
}
#else /* #ifdef CONFIG_HOTPLUG_CPU */
-static void rcu_adopt_orphan_cbs(struct rcu_state *rsp)
-{
-}
-
static void rcu_cleanup_dying_cpu(struct rcu_state *rsp)
{
}
@@ -1687,6 +1912,7 @@ static void force_qs_rnp(struct rcu_state *rsp, int (*f)(struct rcu_data *))
struct rcu_node *rnp;
rcu_for_each_leaf_node(rsp, rnp) {
+ cond_resched();
mask = 0;
raw_spin_lock_irqsave(&rnp->lock, flags);
if (!rcu_gp_in_progress(rsp)) {
@@ -1723,72 +1949,39 @@ static void force_qs_rnp(struct rcu_state *rsp, int (*f)(struct rcu_data *))
* Force quiescent states on reluctant CPUs, and also detect which
* CPUs are in dyntick-idle mode.
*/
-static void force_quiescent_state(struct rcu_state *rsp, int relaxed)
+static void force_quiescent_state(struct rcu_state *rsp)
{
unsigned long flags;
- struct rcu_node *rnp = rcu_get_root(rsp);
-
- trace_rcu_utilization("Start fqs");
- if (!rcu_gp_in_progress(rsp)) {
- trace_rcu_utilization("End fqs");
- return; /* No grace period in progress, nothing to force. */
- }
- if (!raw_spin_trylock_irqsave(&rsp->fqslock, flags)) {
- rsp->n_force_qs_lh++; /* Inexact, can lose counts. Tough! */
- trace_rcu_utilization("End fqs");
- return; /* Someone else is already on the job. */
- }
- if (relaxed && ULONG_CMP_GE(rsp->jiffies_force_qs, jiffies))
- goto unlock_fqs_ret; /* no emergency and done recently. */
- rsp->n_force_qs++;
- raw_spin_lock(&rnp->lock); /* irqs already disabled */
- rsp->jiffies_force_qs = jiffies + RCU_JIFFIES_TILL_FORCE_QS;
- if(!rcu_gp_in_progress(rsp)) {
- rsp->n_force_qs_ngp++;
- raw_spin_unlock(&rnp->lock); /* irqs remain disabled */
- goto unlock_fqs_ret; /* no GP in progress, time updated. */
- }
- rsp->fqs_active = 1;
- switch (rsp->fqs_state) {
- case RCU_GP_IDLE:
- case RCU_GP_INIT:
-
- break; /* grace period idle or initializing, ignore. */
-
- case RCU_SAVE_DYNTICK:
-
- raw_spin_unlock(&rnp->lock); /* irqs remain disabled */
-
- /* Record dyntick-idle state. */
- force_qs_rnp(rsp, dyntick_save_progress_counter);
- raw_spin_lock(&rnp->lock); /* irqs already disabled */
- if (rcu_gp_in_progress(rsp))
- rsp->fqs_state = RCU_FORCE_QS;
- break;
-
- case RCU_FORCE_QS:
-
- /* Check dyntick-idle state, send IPI to laggarts. */
- raw_spin_unlock(&rnp->lock); /* irqs remain disabled */
- force_qs_rnp(rsp, rcu_implicit_dynticks_qs);
-
- /* Leave state in case more forcing is required. */
-
- raw_spin_lock(&rnp->lock); /* irqs already disabled */
- break;
+ bool ret;
+ struct rcu_node *rnp;
+ struct rcu_node *rnp_old = NULL;
+
+ /* Funnel through hierarchy to reduce memory contention. */
+ rnp = per_cpu_ptr(rsp->rda, raw_smp_processor_id())->mynode;
+ for (; rnp != NULL; rnp = rnp->parent) {
+ ret = (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) ||
+ !raw_spin_trylock(&rnp->fqslock);
+ if (rnp_old != NULL)
+ raw_spin_unlock(&rnp_old->fqslock);
+ if (ret) {
+ rsp->n_force_qs_lh++;
+ return;
+ }
+ rnp_old = rnp;
}
- rsp->fqs_active = 0;
- if (rsp->fqs_need_gp) {
- raw_spin_unlock(&rsp->fqslock); /* irqs remain disabled */
- rsp->fqs_need_gp = 0;
- rcu_start_gp(rsp, flags); /* releases rnp->lock */
- trace_rcu_utilization("End fqs");
- return;
+ /* rnp_old == rcu_get_root(rsp), rnp == NULL. */
+
+ /* Reached the root of the rcu_node tree, acquire lock. */
+ raw_spin_lock_irqsave(&rnp_old->lock, flags);
+ raw_spin_unlock(&rnp_old->fqslock);
+ if (ACCESS_ONCE(rsp->gp_flags) & RCU_GP_FLAG_FQS) {
+ rsp->n_force_qs_lh++;
+ raw_spin_unlock_irqrestore(&rnp_old->lock, flags);
+ return; /* Someone beat us to it. */
}
- raw_spin_unlock(&rnp->lock); /* irqs remain disabled */
-unlock_fqs_ret:
- raw_spin_unlock_irqrestore(&rsp->fqslock, flags);
- trace_rcu_utilization("End fqs");
+ rsp->gp_flags |= RCU_GP_FLAG_FQS;
+ raw_spin_unlock_irqrestore(&rnp_old->lock, flags);
+ wake_up(&rsp->gp_wq); /* Memory barrier implied by wake_up() path. */
}
/*
@@ -1805,13 +1998,6 @@ __rcu_process_callbacks(struct rcu_state *rsp)
WARN_ON_ONCE(rdp->beenonline == 0);
/*
- * If an RCU GP has gone long enough, go check for dyntick
- * idle CPUs and, if needed, send resched IPIs.
- */
- if (ULONG_CMP_LT(ACCESS_ONCE(rsp->jiffies_force_qs), jiffies))
- force_quiescent_state(rsp, 1);
-
- /*
* Advance callbacks in response to end of earlier grace
* period that some other CPU ended.
*/
@@ -1838,6 +2024,8 @@ static void rcu_process_callbacks(struct softirq_action *unused)
{
struct rcu_state *rsp;
+ if (cpu_is_offline(smp_processor_id()))
+ return;
trace_rcu_utilization("Start RCU core");
for_each_rcu_flavor(rsp)
__rcu_process_callbacks(rsp);
@@ -1909,12 +2097,11 @@ static void __call_rcu_core(struct rcu_state *rsp, struct rcu_data *rdp,
rdp->blimit = LONG_MAX;
if (rsp->n_force_qs == rdp->n_force_qs_snap &&
*rdp->nxttail[RCU_DONE_TAIL] != head)
- force_quiescent_state(rsp, 0);
+ force_quiescent_state(rsp);
rdp->n_force_qs_snap = rsp->n_force_qs;
rdp->qlen_last_fqs_check = rdp->qlen;
}
- } else if (ULONG_CMP_LT(ACCESS_ONCE(rsp->jiffies_force_qs), jiffies))
- force_quiescent_state(rsp, 1);
+ }
}
static void
@@ -1929,8 +2116,6 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
head->func = func;
head->next = NULL;
- smp_mb(); /* Ensure RCU update seen before callback registry. */
-
/*
* Opportunistically note grace-period endings and beginnings.
* Note that we might see a beginning right after we see an
@@ -1941,6 +2126,12 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu),
rdp = this_cpu_ptr(rsp->rda);
/* Add the callback to our list. */
+ if (unlikely(rdp->nxttail[RCU_NEXT_TAIL] == NULL)) {
+ /* _call_rcu() is illegal on offline CPU; leak the callback. */
+ WARN_ON_ONCE(1);
+ local_irq_restore(flags);
+ return;
+ }
ACCESS_ONCE(rdp->qlen)++;
if (lazy)
rdp->qlen_lazy++;
@@ -2195,17 +2386,7 @@ static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp)
/* Is the RCU core waiting for a quiescent state from this CPU? */
if (rcu_scheduler_fully_active &&
rdp->qs_pending && !rdp->passed_quiesce) {
-
- /*
- * If force_quiescent_state() coming soon and this CPU
- * needs a quiescent state, and this is either RCU-sched
- * or RCU-bh, force a local reschedule.
- */
rdp->n_rp_qs_pending++;
- if (!rdp->preemptible &&
- ULONG_CMP_LT(ACCESS_ONCE(rsp->jiffies_force_qs) - 1,
- jiffies))
- set_need_resched();
} else if (rdp->qs_pending && rdp->passed_quiesce) {
rdp->n_rp_report_qs++;
return 1;
@@ -2235,13 +2416,6 @@ static int __rcu_pending(struct rcu_state *rsp, struct rcu_data *rdp)
return 1;
}
- /* Has an RCU GP gone long enough to send resched IPIs &c? */
- if (rcu_gp_in_progress(rsp) &&
- ULONG_CMP_LT(ACCESS_ONCE(rsp->jiffies_force_qs), jiffies)) {
- rdp->n_rp_need_fqs++;
- return 1;
- }
-
/* nothing to do */
rdp->n_rp_need_nothing++;
return 0;
@@ -2326,13 +2500,10 @@ static void rcu_barrier_func(void *type)
static void _rcu_barrier(struct rcu_state *rsp)
{
int cpu;
- unsigned long flags;
struct rcu_data *rdp;
- struct rcu_data rd;
unsigned long snap = ACCESS_ONCE(rsp->n_barrier_done);
unsigned long snap_done;
- init_rcu_head_on_stack(&rd.barrier_head);
_rcu_barrier_trace(rsp, "Begin", -1, snap);
/* Take mutex to serialize concurrent rcu_barrier() requests. */
@@ -2372,70 +2543,30 @@ static void _rcu_barrier(struct rcu_state *rsp)
/*
* Initialize the count to one rather than to zero in order to
* avoid a too-soon return to zero in case of a short grace period
- * (or preemption of this task). Also flag this task as doing
- * an rcu_barrier(). This will prevent anyone else from adopting
- * orphaned callbacks, which could cause otherwise failure if a
- * CPU went offline and quickly came back online. To see this,
- * consider the following sequence of events:
- *
- * 1. We cause CPU 0 to post an rcu_barrier_callback() callback.
- * 2. CPU 1 goes offline, orphaning its callbacks.
- * 3. CPU 0 adopts CPU 1's orphaned callbacks.
- * 4. CPU 1 comes back online.
- * 5. We cause CPU 1 to post an rcu_barrier_callback() callback.
- * 6. Both rcu_barrier_callback() callbacks are invoked, awakening
- * us -- but before CPU 1's orphaned callbacks are invoked!!!
+ * (or preemption of this task). Exclude CPU-hotplug operations
+ * to ensure that no offline CPU has callbacks queued.
*/
init_completion(&rsp->barrier_completion);
atomic_set(&rsp->barrier_cpu_count, 1);
- raw_spin_lock_irqsave(&rsp->onofflock, flags);
- rsp->rcu_barrier_in_progress = current;
- raw_spin_unlock_irqrestore(&rsp->onofflock, flags);
+ get_online_cpus();
/*
- * Force every CPU with callbacks to register a new callback
- * that will tell us when all the preceding callbacks have
- * been invoked. If an offline CPU has callbacks, wait for
- * it to either come back online or to finish orphaning those
- * callbacks.
+ * Force each CPU with callbacks to register a new callback.
+ * When that callback is invoked, we will know that all of the
+ * corresponding CPU's preceding callbacks have been invoked.
*/
- for_each_possible_cpu(cpu) {
- preempt_disable();
+ for_each_online_cpu(cpu) {
rdp = per_cpu_ptr(rsp->rda, cpu);
- if (cpu_is_offline(cpu)) {
- _rcu_barrier_trace(rsp, "Offline", cpu,
- rsp->n_barrier_done);
- preempt_enable();
- while (cpu_is_offline(cpu) && ACCESS_ONCE(rdp->qlen))
- schedule_timeout_interruptible(1);
- } else if (ACCESS_ONCE(rdp->qlen)) {
+ if (ACCESS_ONCE(rdp->qlen)) {
_rcu_barrier_trace(rsp, "OnlineQ", cpu,
rsp->n_barrier_done);
smp_call_function_single(cpu, rcu_barrier_func, rsp, 1);
- preempt_enable();
} else {
_rcu_barrier_trace(rsp, "OnlineNQ", cpu,
rsp->n_barrier_done);
- preempt_enable();
}
}
-
- /*
- * Now that all online CPUs have rcu_barrier_callback() callbacks
- * posted, we can adopt all of the orphaned callbacks and place
- * an rcu_barrier_callback() callback after them. When that is done,
- * we are guaranteed to have an rcu_barrier_callback() callback
- * following every callback that could possibly have been
- * registered before _rcu_barrier() was called.
- */
- raw_spin_lock_irqsave(&rsp->onofflock, flags);
- rcu_adopt_orphan_cbs(rsp);
- rsp->rcu_barrier_in_progress = NULL;
- raw_spin_unlock_irqrestore(&rsp->onofflock, flags);
- atomic_inc(&rsp->barrier_cpu_count);
- smp_mb__after_atomic_inc(); /* Ensure atomic_inc() before callback. */
- rd.rsp = rsp;
- rsp->call(&rd.barrier_head, rcu_barrier_callback);
+ put_online_cpus();
/*
* Now that we have an rcu_barrier_callback() callback on each
@@ -2456,8 +2587,6 @@ static void _rcu_barrier(struct rcu_state *rsp)
/* Other rcu_barrier() invocations can now safely proceed. */
mutex_unlock(&rsp->barrier_mutex);
-
- destroy_rcu_head_on_stack(&rd.barrier_head);
}
/**
@@ -2497,6 +2626,9 @@ rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp)
rdp->dynticks = &per_cpu(rcu_dynticks, cpu);
WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != DYNTICK_TASK_EXIT_IDLE);
WARN_ON_ONCE(atomic_read(&rdp->dynticks->dynticks) != 1);
+#ifdef CONFIG_RCU_USER_QS
+ WARN_ON_ONCE(rdp->dynticks->in_user);
+#endif
rdp->cpu = cpu;
rdp->rsp = rsp;
raw_spin_unlock_irqrestore(&rnp->lock, flags);
@@ -2523,6 +2655,7 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp, int preemptible)
rdp->qlen_last_fqs_check = 0;
rdp->n_force_qs_snap = rsp->n_force_qs;
rdp->blimit = blimit;
+ init_callback_list(rdp); /* Re-enable callbacks on this CPU. */
rdp->dynticks->dynticks_nesting = DYNTICK_TASK_EXIT_IDLE;
atomic_set(&rdp->dynticks->dynticks,
(atomic_read(&rdp->dynticks->dynticks) & ~0x1) + 1);
@@ -2555,7 +2688,6 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp, int preemptible)
rdp->completed = rnp->completed;
rdp->passed_quiesce = 0;
rdp->qs_pending = 0;
- rdp->passed_quiesce_gpnum = rnp->gpnum - 1;
trace_rcu_grace_period(rsp->name, rdp->gpnum, "cpuonl");
}
raw_spin_unlock(&rnp->lock); /* irqs already disabled. */
@@ -2594,12 +2726,10 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self,
break;
case CPU_ONLINE:
case CPU_DOWN_FAILED:
- rcu_node_kthread_setaffinity(rnp, -1);
- rcu_cpu_kthread_setrt(cpu, 1);
+ rcu_boost_kthread_setaffinity(rnp, -1);
break;
case CPU_DOWN_PREPARE:
- rcu_node_kthread_setaffinity(rnp, cpu);
- rcu_cpu_kthread_setrt(cpu, 0);
+ rcu_boost_kthread_setaffinity(rnp, cpu);
break;
case CPU_DYING:
case CPU_DYING_FROZEN:
@@ -2627,6 +2757,28 @@ static int __cpuinit rcu_cpu_notify(struct notifier_block *self,
}
/*
+ * Spawn the kthread that handles this RCU flavor's grace periods.
+ */
+static int __init rcu_spawn_gp_kthread(void)
+{
+ unsigned long flags;
+ struct rcu_node *rnp;
+ struct rcu_state *rsp;
+ struct task_struct *t;
+
+ for_each_rcu_flavor(rsp) {
+ t = kthread_run(rcu_gp_kthread, rsp, rsp->name);
+ BUG_ON(IS_ERR(t));
+ rnp = rcu_get_root(rsp);
+ raw_spin_lock_irqsave(&rnp->lock, flags);
+ rsp->gp_kthread = t;
+ raw_spin_unlock_irqrestore(&rnp->lock, flags);
+ }
+ return 0;
+}
+early_initcall(rcu_spawn_gp_kthread);
+
+/*
* This function is invoked towards the end of the scheduler's initialization
* process. Before this is called, the idle task might contain
* RCU read-side critical sections (during which time, this idle
@@ -2661,7 +2813,7 @@ static void __init rcu_init_levelspread(struct rcu_state *rsp)
int cprv;
int i;
- cprv = NR_CPUS;
+ cprv = nr_cpu_ids;
for (i = rcu_num_lvls - 1; i >= 0; i--) {
ccur = rsp->levelcnt[i];
rsp->levelspread[i] = (cprv + ccur - 1) / ccur;
@@ -2676,10 +2828,14 @@ static void __init rcu_init_levelspread(struct rcu_state *rsp)
static void __init rcu_init_one(struct rcu_state *rsp,
struct rcu_data __percpu *rda)
{
- static char *buf[] = { "rcu_node_level_0",
- "rcu_node_level_1",
- "rcu_node_level_2",
- "rcu_node_level_3" }; /* Match MAX_RCU_LVLS */
+ static char *buf[] = { "rcu_node_0",
+ "rcu_node_1",
+ "rcu_node_2",
+ "rcu_node_3" }; /* Match MAX_RCU_LVLS */
+ static char *fqs[] = { "rcu_node_fqs_0",
+ "rcu_node_fqs_1",
+ "rcu_node_fqs_2",
+ "rcu_node_fqs_3" }; /* Match MAX_RCU_LVLS */
int cpustride = 1;
int i;
int j;
@@ -2704,7 +2860,11 @@ static void __init rcu_init_one(struct rcu_state *rsp,
raw_spin_lock_init(&rnp->lock);
lockdep_set_class_and_name(&rnp->lock,
&rcu_node_class[i], buf[i]);
- rnp->gpnum = 0;
+ raw_spin_lock_init(&rnp->fqslock);
+ lockdep_set_class_and_name(&rnp->fqslock,
+ &rcu_fqs_class[i], fqs[i]);
+ rnp->gpnum = rsp->gpnum;
+ rnp->completed = rsp->completed;
rnp->qsmask = 0;
rnp->qsmaskinit = 0;
rnp->grplo = j * cpustride;
@@ -2727,6 +2887,7 @@ static void __init rcu_init_one(struct rcu_state *rsp,
}
rsp->rda = rda;
+ init_waitqueue_head(&rsp->gp_wq);
rnp = rsp->level[rcu_num_lvls - 1];
for_each_possible_cpu(i) {
while (i > rnp->grphi)
@@ -2750,7 +2911,8 @@ static void __init rcu_init_geometry(void)
int rcu_capacity[MAX_RCU_LVLS + 1];
/* If the compile-time values are accurate, just leave. */
- if (rcu_fanout_leaf == CONFIG_RCU_FANOUT_LEAF)
+ if (rcu_fanout_leaf == CONFIG_RCU_FANOUT_LEAF &&
+ nr_cpu_ids == NR_CPUS)
return;
/*
diff --git a/kernel/rcutree.h b/kernel/rcutree.h
index 4d29169f2124..5faf05d68326 100644
--- a/kernel/rcutree.h
+++ b/kernel/rcutree.h
@@ -102,6 +102,10 @@ struct rcu_dynticks {
/* idle-period nonlazy_posted snapshot. */
int tick_nohz_enabled_snap; /* Previously seen value from sysfs. */
#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */
+#ifdef CONFIG_RCU_USER_QS
+ bool ignore_user_qs; /* Treat userspace as extended QS or not */
+ bool in_user; /* Is the CPU in userland from RCU POV? */
+#endif
};
/* RCU's kthread states for tracing. */
@@ -196,12 +200,7 @@ struct rcu_node {
/* Refused to boost: not sure why, though. */
/* This can happen due to race conditions. */
#endif /* #ifdef CONFIG_RCU_BOOST */
- struct task_struct *node_kthread_task;
- /* kthread that takes care of this rcu_node */
- /* structure, for example, awakening the */
- /* per-CPU kthreads as needed. */
- unsigned int node_kthread_status;
- /* State of node_kthread_task for tracing. */
+ raw_spinlock_t fqslock ____cacheline_internodealigned_in_smp;
} ____cacheline_internodealigned_in_smp;
/*
@@ -245,8 +244,6 @@ struct rcu_data {
/* in order to detect GP end. */
unsigned long gpnum; /* Highest gp number that this CPU */
/* is aware of having started. */
- unsigned long passed_quiesce_gpnum;
- /* gpnum at time of quiescent state. */
bool passed_quiesce; /* User-mode/idle loop etc. */
bool qs_pending; /* Core waits for quiesc state. */
bool beenonline; /* CPU online at least once. */
@@ -312,11 +309,13 @@ struct rcu_data {
unsigned long n_rp_cpu_needs_gp;
unsigned long n_rp_gp_completed;
unsigned long n_rp_gp_started;
- unsigned long n_rp_need_fqs;
unsigned long n_rp_need_nothing;
- /* 6) _rcu_barrier() callback. */
+ /* 6) _rcu_barrier() and OOM callbacks. */
struct rcu_head barrier_head;
+#ifdef CONFIG_RCU_FAST_NO_HZ
+ struct rcu_head oom_head;
+#endif /* #ifdef CONFIG_RCU_FAST_NO_HZ */
int cpu;
struct rcu_state *rsp;
@@ -375,20 +374,17 @@ struct rcu_state {
u8 fqs_state ____cacheline_internodealigned_in_smp;
/* Force QS state. */
- u8 fqs_active; /* force_quiescent_state() */
- /* is running. */
- u8 fqs_need_gp; /* A CPU was prevented from */
- /* starting a new grace */
- /* period because */
- /* force_quiescent_state() */
- /* was running. */
u8 boost; /* Subject to priority boost. */
unsigned long gpnum; /* Current gp number. */
unsigned long completed; /* # of last completed gp. */
+ struct task_struct *gp_kthread; /* Task for grace periods. */
+ wait_queue_head_t gp_wq; /* Where GP task waits. */
+ int gp_flags; /* Commands for GP task. */
/* End of fields guarded by root rcu_node's lock. */
- raw_spinlock_t onofflock; /* exclude on/offline and */
+ raw_spinlock_t onofflock ____cacheline_internodealigned_in_smp;
+ /* exclude on/offline and */
/* starting new GP. */
struct rcu_head *orphan_nxtlist; /* Orphaned callbacks that */
/* need a grace period. */
@@ -398,16 +394,11 @@ struct rcu_state {
struct rcu_head **orphan_donetail; /* Tail of above. */
long qlen_lazy; /* Number of lazy callbacks. */
long qlen; /* Total number of callbacks. */
- struct task_struct *rcu_barrier_in_progress;
- /* Task doing rcu_barrier(), */
- /* or NULL if no barrier. */
struct mutex barrier_mutex; /* Guards barrier fields. */
atomic_t barrier_cpu_count; /* # CPUs waiting on. */
struct completion barrier_completion; /* Wake at barrier end. */
unsigned long n_barrier_done; /* ++ at start and end of */
/* _rcu_barrier(). */
- raw_spinlock_t fqslock; /* Only one task forcing */
- /* quiescent states. */
unsigned long jiffies_force_qs; /* Time at which to invoke */
/* force_quiescent_state(). */
unsigned long n_force_qs; /* Number of calls to */
@@ -426,6 +417,10 @@ struct rcu_state {
struct list_head flavors; /* List of RCU flavors. */
};
+/* Values for rcu_state structure's gp_flags field. */
+#define RCU_GP_FLAG_INIT 0x1 /* Need grace-period initialization. */
+#define RCU_GP_FLAG_FQS 0x2 /* Need grace-period quiescent-state forcing. */
+
extern struct list_head rcu_struct_flavors;
#define for_each_rcu_flavor(rsp) \
list_for_each_entry((rsp), &rcu_struct_flavors, flavors)
@@ -468,7 +463,6 @@ static int rcu_preempt_blocked_readers_cgp(struct rcu_node *rnp);
#ifdef CONFIG_HOTPLUG_CPU
static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp,
unsigned long flags);
-static void rcu_stop_cpu_kthread(int cpu);
#endif /* #ifdef CONFIG_HOTPLUG_CPU */
static void rcu_print_detail_task_stall(struct rcu_state *rsp);
static int rcu_print_task_stall(struct rcu_node *rnp);
@@ -491,15 +485,9 @@ static void invoke_rcu_callbacks_kthread(void);
static bool rcu_is_callbacks_kthread(void);
#ifdef CONFIG_RCU_BOOST
static void rcu_preempt_do_callbacks(void);
-static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp,
- cpumask_var_t cm);
static int __cpuinit rcu_spawn_one_boost_kthread(struct rcu_state *rsp,
- struct rcu_node *rnp,
- int rnp_index);
-static void invoke_rcu_node_kthread(struct rcu_node *rnp);
-static void rcu_yield(void (*f)(unsigned long), unsigned long arg);
+ struct rcu_node *rnp);
#endif /* #ifdef CONFIG_RCU_BOOST */
-static void rcu_cpu_kthread_setrt(int cpu, int to_rt);
static void __cpuinit rcu_prepare_kthreads(int cpu);
static void rcu_prepare_for_idle_init(int cpu);
static void rcu_cleanup_after_idle(int cpu);
diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h
index 7f3244c0df01..f92115488187 100644
--- a/kernel/rcutree_plugin.h
+++ b/kernel/rcutree_plugin.h
@@ -25,6 +25,8 @@
*/
#include <linux/delay.h>
+#include <linux/oom.h>
+#include <linux/smpboot.h>
#define RCU_KTHREAD_PRIO 1
@@ -118,7 +120,7 @@ EXPORT_SYMBOL_GPL(rcu_batches_completed);
*/
void rcu_force_quiescent_state(void)
{
- force_quiescent_state(&rcu_preempt_state, 0);
+ force_quiescent_state(&rcu_preempt_state);
}
EXPORT_SYMBOL_GPL(rcu_force_quiescent_state);
@@ -136,8 +138,6 @@ static void rcu_preempt_qs(int cpu)
{
struct rcu_data *rdp = &per_cpu(rcu_preempt_data, cpu);
- rdp->passed_quiesce_gpnum = rdp->gpnum;
- barrier();
if (rdp->passed_quiesce == 0)
trace_rcu_grace_period("rcu_preempt", rdp->gpnum, "cpuqs");
rdp->passed_quiesce = 1;
@@ -422,9 +422,11 @@ static void rcu_print_detail_task_stall_rnp(struct rcu_node *rnp)
unsigned long flags;
struct task_struct *t;
- if (!rcu_preempt_blocked_readers_cgp(rnp))
- return;
raw_spin_lock_irqsave(&rnp->lock, flags);
+ if (!rcu_preempt_blocked_readers_cgp(rnp)) {
+ raw_spin_unlock_irqrestore(&rnp->lock, flags);
+ return;
+ }
t = list_entry(rnp->gp_tasks,
struct task_struct, rcu_node_entry);
list_for_each_entry_continue(t, &rnp->blkd_tasks, rcu_node_entry)
@@ -584,17 +586,23 @@ static int rcu_preempt_offline_tasks(struct rcu_state *rsp,
raw_spin_unlock(&rnp_root->lock); /* irqs still disabled */
}
+ rnp->gp_tasks = NULL;
+ rnp->exp_tasks = NULL;
#ifdef CONFIG_RCU_BOOST
- /* In case root is being boosted and leaf is not. */
+ rnp->boost_tasks = NULL;
+ /*
+ * In case root is being boosted and leaf was not. Make sure
+ * that we boost the tasks blocking the current grace period
+ * in this case.
+ */
raw_spin_lock(&rnp_root->lock); /* irqs already disabled */
if (rnp_root->boost_tasks != NULL &&
- rnp_root->boost_tasks != rnp_root->gp_tasks)
+ rnp_root->boost_tasks != rnp_root->gp_tasks &&
+ rnp_root->boost_tasks != rnp_root->exp_tasks)
rnp_root->boost_tasks = rnp_root->gp_tasks;
raw_spin_unlock(&rnp_root->lock); /* irqs still disabled */
#endif /* #ifdef CONFIG_RCU_BOOST */
- rnp->gp_tasks = NULL;
- rnp->exp_tasks = NULL;
return retval;
}
@@ -676,7 +684,7 @@ void synchronize_rcu(void)
EXPORT_SYMBOL_GPL(synchronize_rcu);
static DECLARE_WAIT_QUEUE_HEAD(sync_rcu_preempt_exp_wq);
-static long sync_rcu_preempt_exp_count;
+static unsigned long sync_rcu_preempt_exp_count;
static DEFINE_MUTEX(sync_rcu_preempt_exp_mutex);
/*
@@ -791,7 +799,7 @@ void synchronize_rcu_expedited(void)
unsigned long flags;
struct rcu_node *rnp;
struct rcu_state *rsp = &rcu_preempt_state;
- long snap;
+ unsigned long snap;
int trycount = 0;
smp_mb(); /* Caller's modifications seen first by other CPUs. */
@@ -799,33 +807,47 @@ void synchronize_rcu_expedited(void)
smp_mb(); /* Above access cannot bleed into critical section. */
/*
+ * Block CPU-hotplug operations. This means that any CPU-hotplug
+ * operation that finds an rcu_node structure with tasks in the
+ * process of being boosted will know that all tasks blocking
+ * this expedited grace period will already be in the process of
+ * being boosted. This simplifies the process of moving tasks
+ * from leaf to root rcu_node structures.
+ */
+ get_online_cpus();
+
+ /*
* Acquire lock, falling back to synchronize_rcu() if too many
* lock-acquisition failures. Of course, if someone does the
* expedited grace period for us, just leave.
*/
while (!mutex_trylock(&sync_rcu_preempt_exp_mutex)) {
+ if (ULONG_CMP_LT(snap,
+ ACCESS_ONCE(sync_rcu_preempt_exp_count))) {
+ put_online_cpus();
+ goto mb_ret; /* Others did our work for us. */
+ }
if (trycount++ < 10) {
udelay(trycount * num_online_cpus());
} else {
+ put_online_cpus();
synchronize_rcu();
return;
}
- if ((ACCESS_ONCE(sync_rcu_preempt_exp_count) - snap) > 0)
- goto mb_ret; /* Others did our work for us. */
}
- if ((ACCESS_ONCE(sync_rcu_preempt_exp_count) - snap) > 0)
+ if (ULONG_CMP_LT(snap, ACCESS_ONCE(sync_rcu_preempt_exp_count))) {
+ put_online_cpus();
goto unlock_mb_ret; /* Others did our work for us. */
+ }
/* force all RCU readers onto ->blkd_tasks lists. */
synchronize_sched_expedited();
- raw_spin_lock_irqsave(&rsp->onofflock, flags);
-
/* Initialize ->expmask for all non-leaf rcu_node structures. */
rcu_for_each_nonleaf_node_breadth_first(rsp, rnp) {
- raw_spin_lock(&rnp->lock); /* irqs already disabled. */
+ raw_spin_lock_irqsave(&rnp->lock, flags);
rnp->expmask = rnp->qsmaskinit;
- raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
+ raw_spin_unlock_irqrestore(&rnp->lock, flags);
}
/* Snapshot current state of ->blkd_tasks lists. */
@@ -834,7 +856,7 @@ void synchronize_rcu_expedited(void)
if (NUM_RCU_NODES > 1)
sync_rcu_preempt_exp_init(rsp, rcu_get_root(rsp));
- raw_spin_unlock_irqrestore(&rsp->onofflock, flags);
+ put_online_cpus();
/* Wait for snapshotted ->blkd_tasks lists to drain. */
rnp = rcu_get_root(rsp);
@@ -1069,6 +1091,16 @@ static void rcu_initiate_boost_trace(struct rcu_node *rnp)
#endif /* #else #ifdef CONFIG_RCU_TRACE */
+static void rcu_wake_cond(struct task_struct *t, int status)
+{
+ /*
+ * If the thread is yielding, only wake it when this
+ * is invoked from idle
+ */
+ if (status != RCU_KTHREAD_YIELDING || is_idle_task(current))
+ wake_up_process(t);
+}
+
/*
* Carry out RCU priority boosting on the task indicated by ->exp_tasks
* or ->boost_tasks, advancing the pointer to the next task in the
@@ -1141,17 +1173,6 @@ static int rcu_boost(struct rcu_node *rnp)
}
/*
- * Timer handler to initiate waking up of boost kthreads that
- * have yielded the CPU due to excessive numbers of tasks to
- * boost. We wake up the per-rcu_node kthread, which in turn
- * will wake up the booster kthread.
- */
-static void rcu_boost_kthread_timer(unsigned long arg)
-{
- invoke_rcu_node_kthread((struct rcu_node *)arg);
-}
-
-/*
* Priority-boosting kthread. One per leaf rcu_node and one for the
* root rcu_node.
*/
@@ -1174,8 +1195,9 @@ static int rcu_boost_kthread(void *arg)
else
spincnt = 0;
if (spincnt > 10) {
+ rnp->boost_kthread_status = RCU_KTHREAD_YIELDING;
trace_rcu_utilization("End boost kthread@rcu_yield");
- rcu_yield(rcu_boost_kthread_timer, (unsigned long)rnp);
+ schedule_timeout_interruptible(2);
trace_rcu_utilization("Start boost kthread@rcu_yield");
spincnt = 0;
}
@@ -1191,9 +1213,9 @@ static int rcu_boost_kthread(void *arg)
* kthread to start boosting them. If there is an expedited grace
* period in progress, it is always time to boost.
*
- * The caller must hold rnp->lock, which this function releases,
- * but irqs remain disabled. The ->boost_kthread_task is immortal,
- * so we don't need to worry about it going away.
+ * The caller must hold rnp->lock, which this function releases.
+ * The ->boost_kthread_task is immortal, so we don't need to worry
+ * about it going away.
*/
static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags)
{
@@ -1213,8 +1235,8 @@ static void rcu_initiate_boost(struct rcu_node *rnp, unsigned long flags)
rnp->boost_tasks = rnp->gp_tasks;
raw_spin_unlock_irqrestore(&rnp->lock, flags);
t = rnp->boost_kthread_task;
- if (t != NULL)
- wake_up_process(t);
+ if (t)
+ rcu_wake_cond(t, rnp->boost_kthread_status);
} else {
rcu_initiate_boost_trace(rnp);
raw_spin_unlock_irqrestore(&rnp->lock, flags);
@@ -1231,8 +1253,10 @@ static void invoke_rcu_callbacks_kthread(void)
local_irq_save(flags);
__this_cpu_write(rcu_cpu_has_work, 1);
if (__this_cpu_read(rcu_cpu_kthread_task) != NULL &&
- current != __this_cpu_read(rcu_cpu_kthread_task))
- wake_up_process(__this_cpu_read(rcu_cpu_kthread_task));
+ current != __this_cpu_read(rcu_cpu_kthread_task)) {
+ rcu_wake_cond(__this_cpu_read(rcu_cpu_kthread_task),
+ __this_cpu_read(rcu_cpu_kthread_status));
+ }
local_irq_restore(flags);
}
@@ -1245,21 +1269,6 @@ static bool rcu_is_callbacks_kthread(void)
return __get_cpu_var(rcu_cpu_kthread_task) == current;
}
-/*
- * Set the affinity of the boost kthread. The CPU-hotplug locks are
- * held, so no one should be messing with the existence of the boost
- * kthread.
- */
-static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp,
- cpumask_var_t cm)
-{
- struct task_struct *t;
-
- t = rnp->boost_kthread_task;
- if (t != NULL)
- set_cpus_allowed_ptr(rnp->boost_kthread_task, cm);
-}
-
#define RCU_BOOST_DELAY_JIFFIES DIV_ROUND_UP(CONFIG_RCU_BOOST_DELAY * HZ, 1000)
/*
@@ -1276,15 +1285,19 @@ static void rcu_preempt_boost_start_gp(struct rcu_node *rnp)
* Returns zero if all is well, a negated errno otherwise.
*/
static int __cpuinit rcu_spawn_one_boost_kthread(struct rcu_state *rsp,
- struct rcu_node *rnp,
- int rnp_index)
+ struct rcu_node *rnp)
{
+ int rnp_index = rnp - &rsp->node[0];
unsigned long flags;
struct sched_param sp;
struct task_struct *t;
if (&rcu_preempt_state != rsp)
return 0;
+
+ if (!rcu_scheduler_fully_active || rnp->qsmaskinit == 0)
+ return 0;
+
rsp->boost = 1;
if (rnp->boost_kthread_task != NULL)
return 0;
@@ -1301,25 +1314,6 @@ static int __cpuinit rcu_spawn_one_boost_kthread(struct rcu_state *rsp,
return 0;
}
-#ifdef CONFIG_HOTPLUG_CPU
-
-/*
- * Stop the RCU's per-CPU kthread when its CPU goes offline,.
- */
-static void rcu_stop_cpu_kthread(int cpu)
-{
- struct task_struct *t;
-
- /* Stop the CPU's kthread. */
- t = per_cpu(rcu_cpu_kthread_task, cpu);
- if (t != NULL) {
- per_cpu(rcu_cpu_kthread_task, cpu) = NULL;
- kthread_stop(t);
- }
-}
-
-#endif /* #ifdef CONFIG_HOTPLUG_CPU */
-
static void rcu_kthread_do_work(void)
{
rcu_do_batch(&rcu_sched_state, &__get_cpu_var(rcu_sched_data));
@@ -1327,112 +1321,22 @@ static void rcu_kthread_do_work(void)
rcu_preempt_do_callbacks();
}
-/*
- * Wake up the specified per-rcu_node-structure kthread.
- * Because the per-rcu_node kthreads are immortal, we don't need
- * to do anything to keep them alive.
- */
-static void invoke_rcu_node_kthread(struct rcu_node *rnp)
-{
- struct task_struct *t;
-
- t = rnp->node_kthread_task;
- if (t != NULL)
- wake_up_process(t);
-}
-
-/*
- * Set the specified CPU's kthread to run RT or not, as specified by
- * the to_rt argument. The CPU-hotplug locks are held, so the task
- * is not going away.
- */
-static void rcu_cpu_kthread_setrt(int cpu, int to_rt)
+static void rcu_cpu_kthread_setup(unsigned int cpu)
{
- int policy;
struct sched_param sp;
- struct task_struct *t;
- t = per_cpu(rcu_cpu_kthread_task, cpu);
- if (t == NULL)
- return;
- if (to_rt) {
- policy = SCHED_FIFO;
- sp.sched_priority = RCU_KTHREAD_PRIO;
- } else {
- policy = SCHED_NORMAL;
- sp.sched_priority = 0;
- }
- sched_setscheduler_nocheck(t, policy, &sp);
+ sp.sched_priority = RCU_KTHREAD_PRIO;
+ sched_setscheduler_nocheck(current, SCHED_FIFO, &sp);
}
-/*
- * Timer handler to initiate the waking up of per-CPU kthreads that
- * have yielded the CPU due to excess numbers of RCU callbacks.
- * We wake up the per-rcu_node kthread, which in turn will wake up
- * the booster kthread.
- */
-static void rcu_cpu_kthread_timer(unsigned long arg)
+static void rcu_cpu_kthread_park(unsigned int cpu)
{
- struct rcu_data *rdp = per_cpu_ptr(rcu_state->rda, arg);
- struct rcu_node *rnp = rdp->mynode;
-
- atomic_or(rdp->grpmask, &rnp->wakemask);
- invoke_rcu_node_kthread(rnp);
+ per_cpu(rcu_cpu_kthread_status, cpu) = RCU_KTHREAD_OFFCPU;
}
-/*
- * Drop to non-real-time priority and yield, but only after posting a
- * timer that will cause us to regain our real-time priority if we
- * remain preempted. Either way, we restore our real-time priority
- * before returning.
- */
-static void rcu_yield(void (*f)(unsigned long), unsigned long arg)
+static int rcu_cpu_kthread_should_run(unsigned int cpu)
{
- struct sched_param sp;
- struct timer_list yield_timer;
- int prio = current->rt_priority;
-
- setup_timer_on_stack(&yield_timer, f, arg);
- mod_timer(&yield_timer, jiffies + 2);
- sp.sched_priority = 0;
- sched_setscheduler_nocheck(current, SCHED_NORMAL, &sp);
- set_user_nice(current, 19);
- schedule();
- set_user_nice(current, 0);
- sp.sched_priority = prio;
- sched_setscheduler_nocheck(current, SCHED_FIFO, &sp);
- del_timer(&yield_timer);
-}
-
-/*
- * Handle cases where the rcu_cpu_kthread() ends up on the wrong CPU.
- * This can happen while the corresponding CPU is either coming online
- * or going offline. We cannot wait until the CPU is fully online
- * before starting the kthread, because the various notifier functions
- * can wait for RCU grace periods. So we park rcu_cpu_kthread() until
- * the corresponding CPU is online.
- *
- * Return 1 if the kthread needs to stop, 0 otherwise.
- *
- * Caller must disable bh. This function can momentarily enable it.
- */
-static int rcu_cpu_kthread_should_stop(int cpu)
-{
- while (cpu_is_offline(cpu) ||
- !cpumask_equal(&current->cpus_allowed, cpumask_of(cpu)) ||
- smp_processor_id() != cpu) {
- if (kthread_should_stop())
- return 1;
- per_cpu(rcu_cpu_kthread_status, cpu) = RCU_KTHREAD_OFFCPU;
- per_cpu(rcu_cpu_kthread_cpu, cpu) = raw_smp_processor_id();
- local_bh_enable();
- schedule_timeout_uninterruptible(1);
- if (!cpumask_equal(&current->cpus_allowed, cpumask_of(cpu)))
- set_cpus_allowed_ptr(current, cpumask_of(cpu));
- local_bh_disable();
- }
- per_cpu(rcu_cpu_kthread_cpu, cpu) = cpu;
- return 0;
+ return __get_cpu_var(rcu_cpu_has_work);
}
/*
@@ -1440,138 +1344,35 @@ static int rcu_cpu_kthread_should_stop(int cpu)
* RCU softirq used in flavors and configurations of RCU that do not
* support RCU priority boosting.
*/
-static int rcu_cpu_kthread(void *arg)
+static void rcu_cpu_kthread(unsigned int cpu)
{
- int cpu = (int)(long)arg;
- unsigned long flags;
- int spincnt = 0;
- unsigned int *statusp = &per_cpu(rcu_cpu_kthread_status, cpu);
- char work;
- char *workp = &per_cpu(rcu_cpu_has_work, cpu);
+ unsigned int *statusp = &__get_cpu_var(rcu_cpu_kthread_status);
+ char work, *workp = &__get_cpu_var(rcu_cpu_has_work);
+ int spincnt;
- trace_rcu_utilization("Start CPU kthread@init");
- for (;;) {
- *statusp = RCU_KTHREAD_WAITING;
- trace_rcu_utilization("End CPU kthread@rcu_wait");
- rcu_wait(*workp != 0 || kthread_should_stop());
+ for (spincnt = 0; spincnt < 10; spincnt++) {
trace_rcu_utilization("Start CPU kthread@rcu_wait");
local_bh_disable();
- if (rcu_cpu_kthread_should_stop(cpu)) {
- local_bh_enable();
- break;
- }
*statusp = RCU_KTHREAD_RUNNING;
- per_cpu(rcu_cpu_kthread_loops, cpu)++;
- local_irq_save(flags);
+ this_cpu_inc(rcu_cpu_kthread_loops);
+ local_irq_disable();
work = *workp;
*workp = 0;
- local_irq_restore(flags);
+ local_irq_enable();
if (work)
rcu_kthread_do_work();
local_bh_enable();
- if (*workp != 0)
- spincnt++;
- else
- spincnt = 0;
- if (spincnt > 10) {
- *statusp = RCU_KTHREAD_YIELDING;
- trace_rcu_utilization("End CPU kthread@rcu_yield");
- rcu_yield(rcu_cpu_kthread_timer, (unsigned long)cpu);
- trace_rcu_utilization("Start CPU kthread@rcu_yield");
- spincnt = 0;
- }
- }
- *statusp = RCU_KTHREAD_STOPPED;
- trace_rcu_utilization("End CPU kthread@term");
- return 0;
-}
-
-/*
- * Spawn a per-CPU kthread, setting up affinity and priority.
- * Because the CPU hotplug lock is held, no other CPU will be attempting
- * to manipulate rcu_cpu_kthread_task. There might be another CPU
- * attempting to access it during boot, but the locking in kthread_bind()
- * will enforce sufficient ordering.
- *
- * Please note that we cannot simply refuse to wake up the per-CPU
- * kthread because kthreads are created in TASK_UNINTERRUPTIBLE state,
- * which can result in softlockup complaints if the task ends up being
- * idle for more than a couple of minutes.
- *
- * However, please note also that we cannot bind the per-CPU kthread to its
- * CPU until that CPU is fully online. We also cannot wait until the
- * CPU is fully online before we create its per-CPU kthread, as this would
- * deadlock the system when CPU notifiers tried waiting for grace
- * periods. So we bind the per-CPU kthread to its CPU only if the CPU
- * is online. If its CPU is not yet fully online, then the code in
- * rcu_cpu_kthread() will wait until it is fully online, and then do
- * the binding.
- */
-static int __cpuinit rcu_spawn_one_cpu_kthread(int cpu)
-{
- struct sched_param sp;
- struct task_struct *t;
-
- if (!rcu_scheduler_fully_active ||
- per_cpu(rcu_cpu_kthread_task, cpu) != NULL)
- return 0;
- t = kthread_create_on_node(rcu_cpu_kthread,
- (void *)(long)cpu,
- cpu_to_node(cpu),
- "rcuc/%d", cpu);
- if (IS_ERR(t))
- return PTR_ERR(t);
- if (cpu_online(cpu))
- kthread_bind(t, cpu);
- per_cpu(rcu_cpu_kthread_cpu, cpu) = cpu;
- WARN_ON_ONCE(per_cpu(rcu_cpu_kthread_task, cpu) != NULL);
- sp.sched_priority = RCU_KTHREAD_PRIO;
- sched_setscheduler_nocheck(t, SCHED_FIFO, &sp);
- per_cpu(rcu_cpu_kthread_task, cpu) = t;
- wake_up_process(t); /* Get to TASK_INTERRUPTIBLE quickly. */
- return 0;
-}
-
-/*
- * Per-rcu_node kthread, which is in charge of waking up the per-CPU
- * kthreads when needed. We ignore requests to wake up kthreads
- * for offline CPUs, which is OK because force_quiescent_state()
- * takes care of this case.
- */
-static int rcu_node_kthread(void *arg)
-{
- int cpu;
- unsigned long flags;
- unsigned long mask;
- struct rcu_node *rnp = (struct rcu_node *)arg;
- struct sched_param sp;
- struct task_struct *t;
-
- for (;;) {
- rnp->node_kthread_status = RCU_KTHREAD_WAITING;
- rcu_wait(atomic_read(&rnp->wakemask) != 0);
- rnp->node_kthread_status = RCU_KTHREAD_RUNNING;
- raw_spin_lock_irqsave(&rnp->lock, flags);
- mask = atomic_xchg(&rnp->wakemask, 0);
- rcu_initiate_boost(rnp, flags); /* releases rnp->lock. */
- for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++, mask >>= 1) {
- if ((mask & 0x1) == 0)
- continue;
- preempt_disable();
- t = per_cpu(rcu_cpu_kthread_task, cpu);
- if (!cpu_online(cpu) || t == NULL) {
- preempt_enable();
- continue;
- }
- per_cpu(rcu_cpu_has_work, cpu) = 1;
- sp.sched_priority = RCU_KTHREAD_PRIO;
- sched_setscheduler_nocheck(t, SCHED_FIFO, &sp);
- preempt_enable();
+ if (*workp == 0) {
+ trace_rcu_utilization("End CPU kthread@rcu_wait");
+ *statusp = RCU_KTHREAD_WAITING;
+ return;
}
}
- /* NOTREACHED */
- rnp->node_kthread_status = RCU_KTHREAD_STOPPED;
- return 0;
+ *statusp = RCU_KTHREAD_YIELDING;
+ trace_rcu_utilization("Start CPU kthread@rcu_yield");
+ schedule_timeout_interruptible(2);
+ trace_rcu_utilization("End CPU kthread@rcu_yield");
+ *statusp = RCU_KTHREAD_WAITING;
}
/*
@@ -1583,17 +1384,17 @@ static int rcu_node_kthread(void *arg)
* no outgoing CPU. If there are no CPUs left in the affinity set,
* this function allows the kthread to execute on any CPU.
*/
-static void rcu_node_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu)
+static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu)
{
+ struct task_struct *t = rnp->boost_kthread_task;
+ unsigned long mask = rnp->qsmaskinit;
cpumask_var_t cm;
int cpu;
- unsigned long mask = rnp->qsmaskinit;
- if (rnp->node_kthread_task == NULL)
+ if (!t)
return;
- if (!alloc_cpumask_var(&cm, GFP_KERNEL))
+ if (!zalloc_cpumask_var(&cm, GFP_KERNEL))
return;
- cpumask_clear(cm);
for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++, mask >>= 1)
if ((mask & 0x1) && cpu != outgoingcpu)
cpumask_set_cpu(cpu, cm);
@@ -1603,62 +1404,36 @@ static void rcu_node_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu)
cpumask_clear_cpu(cpu, cm);
WARN_ON_ONCE(cpumask_weight(cm) == 0);
}
- set_cpus_allowed_ptr(rnp->node_kthread_task, cm);
- rcu_boost_kthread_setaffinity(rnp, cm);
+ set_cpus_allowed_ptr(t, cm);
free_cpumask_var(cm);
}
-/*
- * Spawn a per-rcu_node kthread, setting priority and affinity.
- * Called during boot before online/offline can happen, or, if
- * during runtime, with the main CPU-hotplug locks held. So only
- * one of these can be executing at a time.
- */
-static int __cpuinit rcu_spawn_one_node_kthread(struct rcu_state *rsp,
- struct rcu_node *rnp)
-{
- unsigned long flags;
- int rnp_index = rnp - &rsp->node[0];
- struct sched_param sp;
- struct task_struct *t;
-
- if (!rcu_scheduler_fully_active ||
- rnp->qsmaskinit == 0)
- return 0;
- if (rnp->node_kthread_task == NULL) {
- t = kthread_create(rcu_node_kthread, (void *)rnp,
- "rcun/%d", rnp_index);
- if (IS_ERR(t))
- return PTR_ERR(t);
- raw_spin_lock_irqsave(&rnp->lock, flags);
- rnp->node_kthread_task = t;
- raw_spin_unlock_irqrestore(&rnp->lock, flags);
- sp.sched_priority = 99;
- sched_setscheduler_nocheck(t, SCHED_FIFO, &sp);
- wake_up_process(t); /* get to TASK_INTERRUPTIBLE quickly. */
- }
- return rcu_spawn_one_boost_kthread(rsp, rnp, rnp_index);
-}
+static struct smp_hotplug_thread rcu_cpu_thread_spec = {
+ .store = &rcu_cpu_kthread_task,
+ .thread_should_run = rcu_cpu_kthread_should_run,
+ .thread_fn = rcu_cpu_kthread,
+ .thread_comm = "rcuc/%u",
+ .setup = rcu_cpu_kthread_setup,
+ .park = rcu_cpu_kthread_park,
+};
/*
* Spawn all kthreads -- called as soon as the scheduler is running.
*/
static int __init rcu_spawn_kthreads(void)
{
- int cpu;
struct rcu_node *rnp;
+ int cpu;
rcu_scheduler_fully_active = 1;
- for_each_possible_cpu(cpu) {
+ for_each_possible_cpu(cpu)
per_cpu(rcu_cpu_has_work, cpu) = 0;
- if (cpu_online(cpu))
- (void)rcu_spawn_one_cpu_kthread(cpu);
- }
+ BUG_ON(smpboot_register_percpu_thread(&rcu_cpu_thread_spec));
rnp = rcu_get_root(rcu_state);
- (void)rcu_spawn_one_node_kthread(rcu_state, rnp);
+ (void)rcu_spawn_one_boost_kthread(rcu_state, rnp);
if (NUM_RCU_NODES > 1) {
rcu_for_each_leaf_node(rcu_state, rnp)
- (void)rcu_spawn_one_node_kthread(rcu_state, rnp);
+ (void)rcu_spawn_one_boost_kthread(rcu_state, rnp);
}
return 0;
}
@@ -1670,11 +1445,8 @@ static void __cpuinit rcu_prepare_kthreads(int cpu)
struct rcu_node *rnp = rdp->mynode;
/* Fire up the incoming CPU's kthread and leaf rcu_node kthread. */
- if (rcu_scheduler_fully_active) {
- (void)rcu_spawn_one_cpu_kthread(cpu);
- if (rnp->node_kthread_task == NULL)
- (void)rcu_spawn_one_node_kthread(rcu_state, rnp);
- }
+ if (rcu_scheduler_fully_active)
+ (void)rcu_spawn_one_boost_kthread(rcu_state, rnp);
}
#else /* #ifdef CONFIG_RCU_BOOST */
@@ -1698,19 +1470,7 @@ static void rcu_preempt_boost_start_gp(struct rcu_node *rnp)
{
}
-#ifdef CONFIG_HOTPLUG_CPU
-
-static void rcu_stop_cpu_kthread(int cpu)
-{
-}
-
-#endif /* #ifdef CONFIG_HOTPLUG_CPU */
-
-static void rcu_node_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu)
-{
-}
-
-static void rcu_cpu_kthread_setrt(int cpu, int to_rt)
+static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu)
{
}
@@ -1997,6 +1757,26 @@ static void rcu_prepare_for_idle(int cpu)
if (!tne)
return;
+ /* Adaptive-tick mode, where usermode execution is idle to RCU. */
+ if (!is_idle_task(current)) {
+ rdtp->dyntick_holdoff = jiffies - 1;
+ if (rcu_cpu_has_nonlazy_callbacks(cpu)) {
+ trace_rcu_prep_idle("User dyntick with callbacks");
+ rdtp->idle_gp_timer_expires =
+ round_up(jiffies + RCU_IDLE_GP_DELAY,
+ RCU_IDLE_GP_DELAY);
+ } else if (rcu_cpu_has_callbacks(cpu)) {
+ rdtp->idle_gp_timer_expires =
+ round_jiffies(jiffies + RCU_IDLE_LAZY_GP_DELAY);
+ trace_rcu_prep_idle("User dyntick with lazy callbacks");
+ } else {
+ return;
+ }
+ tp = &rdtp->idle_gp_timer;
+ mod_timer_pinned(tp, rdtp->idle_gp_timer_expires);
+ return;
+ }
+
/*
* If this is an idle re-entry, for example, due to use of
* RCU_NONIDLE() or the new idle-loop tracing API within the idle
@@ -2075,16 +1855,16 @@ static void rcu_prepare_for_idle(int cpu)
#ifdef CONFIG_TREE_PREEMPT_RCU
if (per_cpu(rcu_preempt_data, cpu).nxtlist) {
rcu_preempt_qs(cpu);
- force_quiescent_state(&rcu_preempt_state, 0);
+ force_quiescent_state(&rcu_preempt_state);
}
#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
if (per_cpu(rcu_sched_data, cpu).nxtlist) {
rcu_sched_qs(cpu);
- force_quiescent_state(&rcu_sched_state, 0);
+ force_quiescent_state(&rcu_sched_state);
}
if (per_cpu(rcu_bh_data, cpu).nxtlist) {
rcu_bh_qs(cpu);
- force_quiescent_state(&rcu_bh_state, 0);
+ force_quiescent_state(&rcu_bh_state);
}
/*
@@ -2112,6 +1892,88 @@ static void rcu_idle_count_callbacks_posted(void)
__this_cpu_add(rcu_dynticks.nonlazy_posted, 1);
}
+/*
+ * Data for flushing lazy RCU callbacks at OOM time.
+ */
+static atomic_t oom_callback_count;
+static DECLARE_WAIT_QUEUE_HEAD(oom_callback_wq);
+
+/*
+ * RCU OOM callback -- decrement the outstanding count and deliver the
+ * wake-up if we are the last one.
+ */
+static void rcu_oom_callback(struct rcu_head *rhp)
+{
+ if (atomic_dec_and_test(&oom_callback_count))
+ wake_up(&oom_callback_wq);
+}
+
+/*
+ * Post an rcu_oom_notify callback on the current CPU if it has at
+ * least one lazy callback. This will unnecessarily post callbacks
+ * to CPUs that already have a non-lazy callback at the end of their
+ * callback list, but this is an infrequent operation, so accept some
+ * extra overhead to keep things simple.
+ */
+static void rcu_oom_notify_cpu(void *unused)
+{
+ struct rcu_state *rsp;
+ struct rcu_data *rdp;
+
+ for_each_rcu_flavor(rsp) {
+ rdp = __this_cpu_ptr(rsp->rda);
+ if (rdp->qlen_lazy != 0) {
+ atomic_inc(&oom_callback_count);
+ rsp->call(&rdp->oom_head, rcu_oom_callback);
+ }
+ }
+}
+
+/*
+ * If low on memory, ensure that each CPU has a non-lazy callback.
+ * This will wake up CPUs that have only lazy callbacks, in turn
+ * ensuring that they free up the corresponding memory in a timely manner.
+ * Because an uncertain amount of memory will be freed in some uncertain
+ * timeframe, we do not claim to have freed anything.
+ */
+static int rcu_oom_notify(struct notifier_block *self,
+ unsigned long notused, void *nfreed)
+{
+ int cpu;
+
+ /* Wait for callbacks from earlier instance to complete. */
+ wait_event(oom_callback_wq, atomic_read(&oom_callback_count) == 0);
+
+ /*
+ * Prevent premature wakeup: ensure that all increments happen
+ * before there is a chance of the counter reaching zero.
+ */
+ atomic_set(&oom_callback_count, 1);
+
+ get_online_cpus();
+ for_each_online_cpu(cpu) {
+ smp_call_function_single(cpu, rcu_oom_notify_cpu, NULL, 1);
+ cond_resched();
+ }
+ put_online_cpus();
+
+ /* Unconditionally decrement: no need to wake ourselves up. */
+ atomic_dec(&oom_callback_count);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block rcu_oom_nb = {
+ .notifier_call = rcu_oom_notify
+};
+
+static int __init rcu_register_oom_notifier(void)
+{
+ register_oom_notifier(&rcu_oom_nb);
+ return 0;
+}
+early_initcall(rcu_register_oom_notifier);
+
#endif /* #else #if !defined(CONFIG_RCU_FAST_NO_HZ) */
#ifdef CONFIG_RCU_CPU_STALL_INFO
@@ -2122,11 +1984,15 @@ static void print_cpu_stall_fast_no_hz(char *cp, int cpu)
{
struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
struct timer_list *tltp = &rdtp->idle_gp_timer;
+ char c;
- sprintf(cp, "drain=%d %c timer=%lu",
- rdtp->dyntick_drain,
- rdtp->dyntick_holdoff == jiffies ? 'H' : '.',
- timer_pending(tltp) ? tltp->expires - jiffies : -1);
+ c = rdtp->dyntick_holdoff == jiffies ? 'H' : '.';
+ if (timer_pending(tltp))
+ sprintf(cp, "drain=%d %c timer=%lu",
+ rdtp->dyntick_drain, c, tltp->expires - jiffies);
+ else
+ sprintf(cp, "drain=%d %c timer not pending",
+ rdtp->dyntick_drain, c);
}
#else /* #ifdef CONFIG_RCU_FAST_NO_HZ */
@@ -2194,11 +2060,10 @@ static void zero_cpu_stall_ticks(struct rcu_data *rdp)
/* Increment ->ticks_this_gp for all flavors of RCU. */
static void increment_cpu_stall_ticks(void)
{
- __get_cpu_var(rcu_sched_data).ticks_this_gp++;
- __get_cpu_var(rcu_bh_data).ticks_this_gp++;
-#ifdef CONFIG_TREE_PREEMPT_RCU
- __get_cpu_var(rcu_preempt_data).ticks_this_gp++;
-#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */
+ struct rcu_state *rsp;
+
+ for_each_rcu_flavor(rsp)
+ __this_cpu_ptr(rsp->rda)->ticks_this_gp++;
}
#else /* #ifdef CONFIG_RCU_CPU_STALL_INFO */
diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c
index abffb486e94e..693513bc50e6 100644
--- a/kernel/rcutree_trace.c
+++ b/kernel/rcutree_trace.c
@@ -51,8 +51,8 @@ static int show_rcubarrier(struct seq_file *m, void *unused)
struct rcu_state *rsp;
for_each_rcu_flavor(rsp)
- seq_printf(m, "%s: %c bcc: %d nbd: %lu\n",
- rsp->name, rsp->rcu_barrier_in_progress ? 'B' : '.',
+ seq_printf(m, "%s: bcc: %d nbd: %lu\n",
+ rsp->name,
atomic_read(&rsp->barrier_cpu_count),
rsp->n_barrier_done);
return 0;
@@ -86,12 +86,11 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
{
if (!rdp->beenonline)
return;
- seq_printf(m, "%3d%cc=%lu g=%lu pq=%d pgp=%lu qp=%d",
+ seq_printf(m, "%3d%cc=%lu g=%lu pq=%d qp=%d",
rdp->cpu,
cpu_is_offline(rdp->cpu) ? '!' : ' ',
rdp->completed, rdp->gpnum,
- rdp->passed_quiesce, rdp->passed_quiesce_gpnum,
- rdp->qs_pending);
+ rdp->passed_quiesce, rdp->qs_pending);
seq_printf(m, " dt=%d/%llx/%d df=%lu",
atomic_read(&rdp->dynticks->dynticks),
rdp->dynticks->dynticks_nesting,
@@ -108,11 +107,10 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)
rdp->nxttail[RCU_WAIT_TAIL]],
".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]]);
#ifdef CONFIG_RCU_BOOST
- seq_printf(m, " kt=%d/%c/%d ktl=%x",
+ seq_printf(m, " kt=%d/%c ktl=%x",
per_cpu(rcu_cpu_has_work, rdp->cpu),
convert_kthread_status(per_cpu(rcu_cpu_kthread_status,
rdp->cpu)),
- per_cpu(rcu_cpu_kthread_cpu, rdp->cpu),
per_cpu(rcu_cpu_kthread_loops, rdp->cpu) & 0xffff);
#endif /* #ifdef CONFIG_RCU_BOOST */
seq_printf(m, " b=%ld", rdp->blimit);
@@ -150,12 +148,11 @@ static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp)
{
if (!rdp->beenonline)
return;
- seq_printf(m, "%d,%s,%lu,%lu,%d,%lu,%d",
+ seq_printf(m, "%d,%s,%lu,%lu,%d,%d",
rdp->cpu,
cpu_is_offline(rdp->cpu) ? "\"N\"" : "\"Y\"",
rdp->completed, rdp->gpnum,
- rdp->passed_quiesce, rdp->passed_quiesce_gpnum,
- rdp->qs_pending);
+ rdp->passed_quiesce, rdp->qs_pending);
seq_printf(m, ",%d,%llx,%d,%lu",
atomic_read(&rdp->dynticks->dynticks),
rdp->dynticks->dynticks_nesting,
@@ -186,7 +183,7 @@ static int show_rcudata_csv(struct seq_file *m, void *unused)
int cpu;
struct rcu_state *rsp;
- seq_puts(m, "\"CPU\",\"Online?\",\"c\",\"g\",\"pq\",\"pgp\",\"pq\",");
+ seq_puts(m, "\"CPU\",\"Online?\",\"c\",\"g\",\"pq\",\"pq\",");
seq_puts(m, "\"dt\",\"dt nesting\",\"dt NMI nesting\",\"df\",");
seq_puts(m, "\"of\",\"qll\",\"ql\",\"qs\"");
#ifdef CONFIG_RCU_BOOST
@@ -386,10 +383,9 @@ static void print_one_rcu_pending(struct seq_file *m, struct rcu_data *rdp)
rdp->n_rp_report_qs,
rdp->n_rp_cb_ready,
rdp->n_rp_cpu_needs_gp);
- seq_printf(m, "gpc=%ld gps=%ld nf=%ld nn=%ld\n",
+ seq_printf(m, "gpc=%ld gps=%ld nn=%ld\n",
rdp->n_rp_gp_completed,
rdp->n_rp_gp_started,
- rdp->n_rp_need_fqs,
rdp->n_rp_need_nothing);
}
diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile
index 173ea52f3af0..f06d249e103b 100644
--- a/kernel/sched/Makefile
+++ b/kernel/sched/Makefile
@@ -11,7 +11,7 @@ ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y)
CFLAGS_core.o := $(PROFILING) -fno-omit-frame-pointer
endif
-obj-y += core.o clock.o idle_task.o fair.o rt.o stop_task.o
+obj-y += core.o clock.o cputime.o idle_task.o fair.o rt.o stop_task.o
obj-$(CONFIG_SMP) += cpupri.o
obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o
obj-$(CONFIG_SCHEDSTATS) += stats.o
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index 649c9f876cb1..c17747236438 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -740,126 +740,6 @@ void deactivate_task(struct rq *rq, struct task_struct *p, int flags)
dequeue_task(rq, p, flags);
}
-#ifdef CONFIG_IRQ_TIME_ACCOUNTING
-
-/*
- * There are no locks covering percpu hardirq/softirq time.
- * They are only modified in account_system_vtime, on corresponding CPU
- * with interrupts disabled. So, writes are safe.
- * They are read and saved off onto struct rq in update_rq_clock().
- * This may result in other CPU reading this CPU's irq time and can
- * race with irq/account_system_vtime on this CPU. We would either get old
- * or new value with a side effect of accounting a slice of irq time to wrong
- * task when irq is in progress while we read rq->clock. That is a worthy
- * compromise in place of having locks on each irq in account_system_time.
- */
-static DEFINE_PER_CPU(u64, cpu_hardirq_time);
-static DEFINE_PER_CPU(u64, cpu_softirq_time);
-
-static DEFINE_PER_CPU(u64, irq_start_time);
-static int sched_clock_irqtime;
-
-void enable_sched_clock_irqtime(void)
-{
- sched_clock_irqtime = 1;
-}
-
-void disable_sched_clock_irqtime(void)
-{
- sched_clock_irqtime = 0;
-}
-
-#ifndef CONFIG_64BIT
-static DEFINE_PER_CPU(seqcount_t, irq_time_seq);
-
-static inline void irq_time_write_begin(void)
-{
- __this_cpu_inc(irq_time_seq.sequence);
- smp_wmb();
-}
-
-static inline void irq_time_write_end(void)
-{
- smp_wmb();
- __this_cpu_inc(irq_time_seq.sequence);
-}
-
-static inline u64 irq_time_read(int cpu)
-{
- u64 irq_time;
- unsigned seq;
-
- do {
- seq = read_seqcount_begin(&per_cpu(irq_time_seq, cpu));
- irq_time = per_cpu(cpu_softirq_time, cpu) +
- per_cpu(cpu_hardirq_time, cpu);
- } while (read_seqcount_retry(&per_cpu(irq_time_seq, cpu), seq));
-
- return irq_time;
-}
-#else /* CONFIG_64BIT */
-static inline void irq_time_write_begin(void)
-{
-}
-
-static inline void irq_time_write_end(void)
-{
-}
-
-static inline u64 irq_time_read(int cpu)
-{
- return per_cpu(cpu_softirq_time, cpu) + per_cpu(cpu_hardirq_time, cpu);
-}
-#endif /* CONFIG_64BIT */
-
-/*
- * Called before incrementing preempt_count on {soft,}irq_enter
- * and before decrementing preempt_count on {soft,}irq_exit.
- */
-void account_system_vtime(struct task_struct *curr)
-{
- unsigned long flags;
- s64 delta;
- int cpu;
-
- if (!sched_clock_irqtime)
- return;
-
- local_irq_save(flags);
-
- cpu = smp_processor_id();
- delta = sched_clock_cpu(cpu) - __this_cpu_read(irq_start_time);
- __this_cpu_add(irq_start_time, delta);
-
- irq_time_write_begin();
- /*
- * We do not account for softirq time from ksoftirqd here.
- * We want to continue accounting softirq time to ksoftirqd thread
- * in that case, so as not to confuse scheduler with a special task
- * that do not consume any time, but still wants to run.
- */
- if (hardirq_count())
- __this_cpu_add(cpu_hardirq_time, delta);
- else if (in_serving_softirq() && curr != this_cpu_ksoftirqd())
- __this_cpu_add(cpu_softirq_time, delta);
-
- irq_time_write_end();
- local_irq_restore(flags);
-}
-EXPORT_SYMBOL_GPL(account_system_vtime);
-
-#endif /* CONFIG_IRQ_TIME_ACCOUNTING */
-
-#ifdef CONFIG_PARAVIRT
-static inline u64 steal_ticks(u64 steal)
-{
- if (unlikely(steal > NSEC_PER_SEC))
- return div_u64(steal, TICK_NSEC);
-
- return __iter_div_u64_rem(steal, TICK_NSEC, &steal);
-}
-#endif
-
static void update_rq_clock_task(struct rq *rq, s64 delta)
{
/*
@@ -920,43 +800,6 @@ static void update_rq_clock_task(struct rq *rq, s64 delta)
#endif
}
-#ifdef CONFIG_IRQ_TIME_ACCOUNTING
-static int irqtime_account_hi_update(void)
-{
- u64 *cpustat = kcpustat_this_cpu->cpustat;
- unsigned long flags;
- u64 latest_ns;
- int ret = 0;
-
- local_irq_save(flags);
- latest_ns = this_cpu_read(cpu_hardirq_time);
- if (nsecs_to_cputime64(latest_ns) > cpustat[CPUTIME_IRQ])
- ret = 1;
- local_irq_restore(flags);
- return ret;
-}
-
-static int irqtime_account_si_update(void)
-{
- u64 *cpustat = kcpustat_this_cpu->cpustat;
- unsigned long flags;
- u64 latest_ns;
- int ret = 0;
-
- local_irq_save(flags);
- latest_ns = this_cpu_read(cpu_softirq_time);
- if (nsecs_to_cputime64(latest_ns) > cpustat[CPUTIME_SOFTIRQ])
- ret = 1;
- local_irq_restore(flags);
- return ret;
-}
-
-#else /* CONFIG_IRQ_TIME_ACCOUNTING */
-
-#define sched_clock_irqtime (0)
-
-#endif
-
void sched_set_stop_task(int cpu, struct task_struct *stop)
{
struct sched_param param = { .sched_priority = MAX_RT_PRIO - 1 };
@@ -1518,25 +1361,6 @@ static void ttwu_queue_remote(struct task_struct *p, int cpu)
smp_send_reschedule(cpu);
}
-#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
-static int ttwu_activate_remote(struct task_struct *p, int wake_flags)
-{
- struct rq *rq;
- int ret = 0;
-
- rq = __task_rq_lock(p);
- if (p->on_cpu) {
- ttwu_activate(rq, p, ENQUEUE_WAKEUP);
- ttwu_do_wakeup(rq, p, wake_flags);
- ret = 1;
- }
- __task_rq_unlock(rq);
-
- return ret;
-
-}
-#endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */
-
bool cpus_share_cache(int this_cpu, int that_cpu)
{
return per_cpu(sd_llc_id, this_cpu) == per_cpu(sd_llc_id, that_cpu);
@@ -1597,21 +1421,8 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
* If the owning (remote) cpu is still in the middle of schedule() with
* this task as prev, wait until its done referencing the task.
*/
- while (p->on_cpu) {
-#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
- /*
- * In case the architecture enables interrupts in
- * context_switch(), we cannot busy wait, since that
- * would lead to deadlocks when an interrupt hits and
- * tries to wake up @prev. So bail and do a complete
- * remote wakeup.
- */
- if (ttwu_activate_remote(p, wake_flags))
- goto stat;
-#else
+ while (p->on_cpu)
cpu_relax();
-#endif
- }
/*
* Pairs with the smp_wmb() in finish_lock_switch().
*/
@@ -1953,14 +1764,9 @@ static void finish_task_switch(struct rq *rq, struct task_struct *prev)
* Manfred Spraul <manfred@colorfullife.com>
*/
prev_state = prev->state;
+ vtime_task_switch(prev);
finish_arch_switch(prev);
-#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
- local_irq_disable();
-#endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */
perf_event_task_sched_in(prev, current);
-#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
- local_irq_enable();
-#endif /* __ARCH_WANT_INTERRUPTS_ON_CTXSW */
finish_lock_switch(rq, prev);
finish_arch_post_lock_switch();
@@ -2081,6 +1887,7 @@ context_switch(struct rq *rq, struct task_struct *prev,
#endif
/* Here we just switch the register state and the stack. */
+ rcu_switch(prev, next);
switch_to(prev, next, prev);
barrier();
@@ -2809,404 +2616,6 @@ unsigned long long task_sched_runtime(struct task_struct *p)
return ns;
}
-#ifdef CONFIG_CGROUP_CPUACCT
-struct cgroup_subsys cpuacct_subsys;
-struct cpuacct root_cpuacct;
-#endif
-
-static inline void task_group_account_field(struct task_struct *p, int index,
- u64 tmp)
-{
-#ifdef CONFIG_CGROUP_CPUACCT
- struct kernel_cpustat *kcpustat;
- struct cpuacct *ca;
-#endif
- /*
- * Since all updates are sure to touch the root cgroup, we
- * get ourselves ahead and touch it first. If the root cgroup
- * is the only cgroup, then nothing else should be necessary.
- *
- */
- __get_cpu_var(kernel_cpustat).cpustat[index] += tmp;
-
-#ifdef CONFIG_CGROUP_CPUACCT
- if (unlikely(!cpuacct_subsys.active))
- return;
-
- rcu_read_lock();
- ca = task_ca(p);
- while (ca && (ca != &root_cpuacct)) {
- kcpustat = this_cpu_ptr(ca->cpustat);
- kcpustat->cpustat[index] += tmp;
- ca = parent_ca(ca);
- }
- rcu_read_unlock();
-#endif
-}
-
-
-/*
- * Account user cpu time to a process.
- * @p: the process that the cpu time gets accounted to
- * @cputime: the cpu time spent in user space since the last update
- * @cputime_scaled: cputime scaled by cpu frequency
- */
-void account_user_time(struct task_struct *p, cputime_t cputime,
- cputime_t cputime_scaled)
-{
- int index;
-
- /* Add user time to process. */
- p->utime += cputime;
- p->utimescaled += cputime_scaled;
- account_group_user_time(p, cputime);
-
- index = (TASK_NICE(p) > 0) ? CPUTIME_NICE : CPUTIME_USER;
-
- /* Add user time to cpustat. */
- task_group_account_field(p, index, (__force u64) cputime);
-
- /* Account for user time used */
- acct_update_integrals(p);
-}
-
-/*
- * Account guest cpu time to a process.
- * @p: the process that the cpu time gets accounted to
- * @cputime: the cpu time spent in virtual machine since the last update
- * @cputime_scaled: cputime scaled by cpu frequency
- */
-static void account_guest_time(struct task_struct *p, cputime_t cputime,
- cputime_t cputime_scaled)
-{
- u64 *cpustat = kcpustat_this_cpu->cpustat;
-
- /* Add guest time to process. */
- p->utime += cputime;
- p->utimescaled += cputime_scaled;
- account_group_user_time(p, cputime);
- p->gtime += cputime;
-
- /* Add guest time to cpustat. */
- if (TASK_NICE(p) > 0) {
- cpustat[CPUTIME_NICE] += (__force u64) cputime;
- cpustat[CPUTIME_GUEST_NICE] += (__force u64) cputime;
- } else {
- cpustat[CPUTIME_USER] += (__force u64) cputime;
- cpustat[CPUTIME_GUEST] += (__force u64) cputime;
- }
-}
-
-/*
- * Account system cpu time to a process and desired cpustat field
- * @p: the process that the cpu time gets accounted to
- * @cputime: the cpu time spent in kernel space since the last update
- * @cputime_scaled: cputime scaled by cpu frequency
- * @target_cputime64: pointer to cpustat field that has to be updated
- */
-static inline
-void __account_system_time(struct task_struct *p, cputime_t cputime,
- cputime_t cputime_scaled, int index)
-{
- /* Add system time to process. */
- p->stime += cputime;
- p->stimescaled += cputime_scaled;
- account_group_system_time(p, cputime);
-
- /* Add system time to cpustat. */
- task_group_account_field(p, index, (__force u64) cputime);
-
- /* Account for system time used */
- acct_update_integrals(p);
-}
-
-/*
- * Account system cpu time to a process.
- * @p: the process that the cpu time gets accounted to
- * @hardirq_offset: the offset to subtract from hardirq_count()
- * @cputime: the cpu time spent in kernel space since the last update
- * @cputime_scaled: cputime scaled by cpu frequency
- */
-void account_system_time(struct task_struct *p, int hardirq_offset,
- cputime_t cputime, cputime_t cputime_scaled)
-{
- int index;
-
- if ((p->flags & PF_VCPU) && (irq_count() - hardirq_offset == 0)) {
- account_guest_time(p, cputime, cputime_scaled);
- return;
- }
-
- if (hardirq_count() - hardirq_offset)
- index = CPUTIME_IRQ;
- else if (in_serving_softirq())
- index = CPUTIME_SOFTIRQ;
- else
- index = CPUTIME_SYSTEM;
-
- __account_system_time(p, cputime, cputime_scaled, index);
-}
-
-/*
- * Account for involuntary wait time.
- * @cputime: the cpu time spent in involuntary wait
- */
-void account_steal_time(cputime_t cputime)
-{
- u64 *cpustat = kcpustat_this_cpu->cpustat;
-
- cpustat[CPUTIME_STEAL] += (__force u64) cputime;
-}
-
-/*
- * Account for idle time.
- * @cputime: the cpu time spent in idle wait
- */
-void account_idle_time(cputime_t cputime)
-{
- u64 *cpustat = kcpustat_this_cpu->cpustat;
- struct rq *rq = this_rq();
-
- if (atomic_read(&rq->nr_iowait) > 0)
- cpustat[CPUTIME_IOWAIT] += (__force u64) cputime;
- else
- cpustat[CPUTIME_IDLE] += (__force u64) cputime;
-}
-
-static __always_inline bool steal_account_process_tick(void)
-{
-#ifdef CONFIG_PARAVIRT
- if (static_key_false(&paravirt_steal_enabled)) {
- u64 steal, st = 0;
-
- steal = paravirt_steal_clock(smp_processor_id());
- steal -= this_rq()->prev_steal_time;
-
- st = steal_ticks(steal);
- this_rq()->prev_steal_time += st * TICK_NSEC;
-
- account_steal_time(st);
- return st;
- }
-#endif
- return false;
-}
-
-#ifndef CONFIG_VIRT_CPU_ACCOUNTING
-
-#ifdef CONFIG_IRQ_TIME_ACCOUNTING
-/*
- * Account a tick to a process and cpustat
- * @p: the process that the cpu time gets accounted to
- * @user_tick: is the tick from userspace
- * @rq: the pointer to rq
- *
- * Tick demultiplexing follows the order
- * - pending hardirq update
- * - pending softirq update
- * - user_time
- * - idle_time
- * - system time
- * - check for guest_time
- * - else account as system_time
- *
- * Check for hardirq is done both for system and user time as there is
- * no timer going off while we are on hardirq and hence we may never get an
- * opportunity to update it solely in system time.
- * p->stime and friends are only updated on system time and not on irq
- * softirq as those do not count in task exec_runtime any more.
- */
-static void irqtime_account_process_tick(struct task_struct *p, int user_tick,
- struct rq *rq)
-{
- cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy);
- u64 *cpustat = kcpustat_this_cpu->cpustat;
-
- if (steal_account_process_tick())
- return;
-
- if (irqtime_account_hi_update()) {
- cpustat[CPUTIME_IRQ] += (__force u64) cputime_one_jiffy;
- } else if (irqtime_account_si_update()) {
- cpustat[CPUTIME_SOFTIRQ] += (__force u64) cputime_one_jiffy;
- } else if (this_cpu_ksoftirqd() == p) {
- /*
- * ksoftirqd time do not get accounted in cpu_softirq_time.
- * So, we have to handle it separately here.
- * Also, p->stime needs to be updated for ksoftirqd.
- */
- __account_system_time(p, cputime_one_jiffy, one_jiffy_scaled,
- CPUTIME_SOFTIRQ);
- } else if (user_tick) {
- account_user_time(p, cputime_one_jiffy, one_jiffy_scaled);
- } else if (p == rq->idle) {
- account_idle_time(cputime_one_jiffy);
- } else if (p->flags & PF_VCPU) { /* System time or guest time */
- account_guest_time(p, cputime_one_jiffy, one_jiffy_scaled);
- } else {
- __account_system_time(p, cputime_one_jiffy, one_jiffy_scaled,
- CPUTIME_SYSTEM);
- }
-}
-
-static void irqtime_account_idle_ticks(int ticks)
-{
- int i;
- struct rq *rq = this_rq();
-
- for (i = 0; i < ticks; i++)
- irqtime_account_process_tick(current, 0, rq);
-}
-#else /* CONFIG_IRQ_TIME_ACCOUNTING */
-static void irqtime_account_idle_ticks(int ticks) {}
-static void irqtime_account_process_tick(struct task_struct *p, int user_tick,
- struct rq *rq) {}
-#endif /* CONFIG_IRQ_TIME_ACCOUNTING */
-
-/*
- * Account a single tick of cpu time.
- * @p: the process that the cpu time gets accounted to
- * @user_tick: indicates if the tick is a user or a system tick
- */
-void account_process_tick(struct task_struct *p, int user_tick)
-{
- cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy);
- struct rq *rq = this_rq();
-
- if (sched_clock_irqtime) {
- irqtime_account_process_tick(p, user_tick, rq);
- return;
- }
-
- if (steal_account_process_tick())
- return;
-
- if (user_tick)
- account_user_time(p, cputime_one_jiffy, one_jiffy_scaled);
- else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET))
- account_system_time(p, HARDIRQ_OFFSET, cputime_one_jiffy,
- one_jiffy_scaled);
- else
- account_idle_time(cputime_one_jiffy);
-}
-
-/*
- * Account multiple ticks of steal time.
- * @p: the process from which the cpu time has been stolen
- * @ticks: number of stolen ticks
- */
-void account_steal_ticks(unsigned long ticks)
-{
- account_steal_time(jiffies_to_cputime(ticks));
-}
-
-/*
- * Account multiple ticks of idle time.
- * @ticks: number of stolen ticks
- */
-void account_idle_ticks(unsigned long ticks)
-{
-
- if (sched_clock_irqtime) {
- irqtime_account_idle_ticks(ticks);
- return;
- }
-
- account_idle_time(jiffies_to_cputime(ticks));
-}
-
-#endif
-
-/*
- * Use precise platform statistics if available:
- */
-#ifdef CONFIG_VIRT_CPU_ACCOUNTING
-void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
-{
- *ut = p->utime;
- *st = p->stime;
-}
-
-void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
-{
- struct task_cputime cputime;
-
- thread_group_cputime(p, &cputime);
-
- *ut = cputime.utime;
- *st = cputime.stime;
-}
-#else
-
-#ifndef nsecs_to_cputime
-# define nsecs_to_cputime(__nsecs) nsecs_to_jiffies(__nsecs)
-#endif
-
-static cputime_t scale_utime(cputime_t utime, cputime_t rtime, cputime_t total)
-{
- u64 temp = (__force u64) rtime;
-
- temp *= (__force u64) utime;
-
- if (sizeof(cputime_t) == 4)
- temp = div_u64(temp, (__force u32) total);
- else
- temp = div64_u64(temp, (__force u64) total);
-
- return (__force cputime_t) temp;
-}
-
-void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
-{
- cputime_t rtime, utime = p->utime, total = utime + p->stime;
-
- /*
- * Use CFS's precise accounting:
- */
- rtime = nsecs_to_cputime(p->se.sum_exec_runtime);
-
- if (total)
- utime = scale_utime(utime, rtime, total);
- else
- utime = rtime;
-
- /*
- * Compare with previous values, to keep monotonicity:
- */
- p->prev_utime = max(p->prev_utime, utime);
- p->prev_stime = max(p->prev_stime, rtime - p->prev_utime);
-
- *ut = p->prev_utime;
- *st = p->prev_stime;
-}
-
-/*
- * Must be called with siglock held.
- */
-void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
-{
- struct signal_struct *sig = p->signal;
- struct task_cputime cputime;
- cputime_t rtime, utime, total;
-
- thread_group_cputime(p, &cputime);
-
- total = cputime.utime + cputime.stime;
- rtime = nsecs_to_cputime(cputime.sum_exec_runtime);
-
- if (total)
- utime = scale_utime(cputime.utime, rtime, total);
- else
- utime = rtime;
-
- sig->prev_utime = max(sig->prev_utime, utime);
- sig->prev_stime = max(sig->prev_stime, rtime - sig->prev_utime);
-
- *ut = sig->prev_utime;
- *st = sig->prev_stime;
-}
-#endif
-
/*
* This function gets called by the timer code, with HZ frequency.
* We call it with interrupts disabled.
@@ -3367,6 +2776,40 @@ pick_next_task(struct rq *rq)
/*
* __schedule() is the main scheduler function.
+ *
+ * The main means of driving the scheduler and thus entering this function are:
+ *
+ * 1. Explicit blocking: mutex, semaphore, waitqueue, etc.
+ *
+ * 2. TIF_NEED_RESCHED flag is checked on interrupt and userspace return
+ * paths. For example, see arch/x86/entry_64.S.
+ *
+ * To drive preemption between tasks, the scheduler sets the flag in timer
+ * interrupt handler scheduler_tick().
+ *
+ * 3. Wakeups don't really cause entry into schedule(). They add a
+ * task to the run-queue and that's it.
+ *
+ * Now, if the new task added to the run-queue preempts the current
+ * task, then the wakeup sets TIF_NEED_RESCHED and schedule() gets
+ * called on the nearest possible occasion:
+ *
+ * - If the kernel is preemptible (CONFIG_PREEMPT=y):
+ *
+ * - in syscall or exception context, at the next outmost
+ * preempt_enable(). (this might be as soon as the wake_up()'s
+ * spin_unlock()!)
+ *
+ * - in IRQ context, return from interrupt-handler to
+ * preemptible context
+ *
+ * - If the kernel is not preemptible (CONFIG_PREEMPT is not set)
+ * then at the next:
+ *
+ * - cond_resched() call
+ * - explicit schedule() call
+ * - return from syscall or exception to user-space
+ * - return from interrupt-handler to user-space
*/
static void __sched __schedule(void)
{
@@ -3468,6 +2911,21 @@ asmlinkage void __sched schedule(void)
}
EXPORT_SYMBOL(schedule);
+#ifdef CONFIG_RCU_USER_QS
+asmlinkage void __sched schedule_user(void)
+{
+ /*
+ * If we come here after a random call to set_need_resched(),
+ * or we have been woken up remotely but the IPI has not yet arrived,
+ * we haven't yet exited the RCU idle mode. Do it here manually until
+ * we find a better solution.
+ */
+ rcu_user_exit();
+ schedule();
+ rcu_user_enter();
+}
+#endif
+
/**
* schedule_preempt_disabled - called with preemption disabled
*
@@ -3569,6 +3027,7 @@ asmlinkage void __sched preempt_schedule_irq(void)
/* Catch callers which need to be fixed */
BUG_ON(ti->preempt_count || !irqs_disabled());
+ rcu_user_exit();
do {
add_preempt_count(PREEMPT_ACTIVE);
local_irq_enable();
@@ -4868,13 +4327,6 @@ again:
*/
if (preempt && rq != p_rq)
resched_task(p_rq->curr);
- } else {
- /*
- * We might have set it in task_yield_fair(), but are
- * not going to schedule(), so don't want to skip
- * the next update.
- */
- rq->skip_clock_update = 0;
}
out:
@@ -5416,16 +4868,25 @@ static void sd_free_ctl_entry(struct ctl_table **tablep)
*tablep = NULL;
}
+static int min_load_idx = 0;
+static int max_load_idx = CPU_LOAD_IDX_MAX;
+
static void
set_table_entry(struct ctl_table *entry,
const char *procname, void *data, int maxlen,
- umode_t mode, proc_handler *proc_handler)
+ umode_t mode, proc_handler *proc_handler,
+ bool load_idx)
{
entry->procname = procname;
entry->data = data;
entry->maxlen = maxlen;
entry->mode = mode;
entry->proc_handler = proc_handler;
+
+ if (load_idx) {
+ entry->extra1 = &min_load_idx;
+ entry->extra2 = &max_load_idx;
+ }
}
static struct ctl_table *
@@ -5437,30 +4898,30 @@ sd_alloc_ctl_domain_table(struct sched_domain *sd)
return NULL;
set_table_entry(&table[0], "min_interval", &sd->min_interval,
- sizeof(long), 0644, proc_doulongvec_minmax);
+ sizeof(long), 0644, proc_doulongvec_minmax, false);
set_table_entry(&table[1], "max_interval", &sd->max_interval,
- sizeof(long), 0644, proc_doulongvec_minmax);
+ sizeof(long), 0644, proc_doulongvec_minmax, false);
set_table_entry(&table[2], "busy_idx", &sd->busy_idx,
- sizeof(int), 0644, proc_dointvec_minmax);
+ sizeof(int), 0644, proc_dointvec_minmax, true);
set_table_entry(&table[3], "idle_idx", &sd->idle_idx,
- sizeof(int), 0644, proc_dointvec_minmax);
+ sizeof(int), 0644, proc_dointvec_minmax, true);
set_table_entry(&table[4], "newidle_idx", &sd->newidle_idx,
- sizeof(int), 0644, proc_dointvec_minmax);
+ sizeof(int), 0644, proc_dointvec_minmax, true);
set_table_entry(&table[5], "wake_idx", &sd->wake_idx,
- sizeof(int), 0644, proc_dointvec_minmax);
+ sizeof(int), 0644, proc_dointvec_minmax, true);
set_table_entry(&table[6], "forkexec_idx", &sd->forkexec_idx,
- sizeof(int), 0644, proc_dointvec_minmax);
+ sizeof(int), 0644, proc_dointvec_minmax, true);
set_table_entry(&table[7], "busy_factor", &sd->busy_factor,
- sizeof(int), 0644, proc_dointvec_minmax);
+ sizeof(int), 0644, proc_dointvec_minmax, false);
set_table_entry(&table[8], "imbalance_pct", &sd->imbalance_pct,
- sizeof(int), 0644, proc_dointvec_minmax);
+ sizeof(int), 0644, proc_dointvec_minmax, false);
set_table_entry(&table[9], "cache_nice_tries",
&sd->cache_nice_tries,
- sizeof(int), 0644, proc_dointvec_minmax);
+ sizeof(int), 0644, proc_dointvec_minmax, false);
set_table_entry(&table[10], "flags", &sd->flags,
- sizeof(int), 0644, proc_dointvec_minmax);
+ sizeof(int), 0644, proc_dointvec_minmax, false);
set_table_entry(&table[11], "name", sd->name,
- CORENAME_MAX_SIZE, 0444, proc_dostring);
+ CORENAME_MAX_SIZE, 0444, proc_dostring, false);
/* &table[12] is terminator */
return table;
@@ -5604,7 +5065,9 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu)
migrate_tasks(cpu);
BUG_ON(rq->nr_running != 1); /* the migration thread */
raw_spin_unlock_irqrestore(&rq->lock, flags);
+ break;
+ case CPU_DEAD:
calc_load_migrate(rq);
break;
#endif
@@ -6537,7 +6000,6 @@ sd_numa_init(struct sched_domain_topology_level *tl, int cpu)
| 0*SD_BALANCE_FORK
| 0*SD_BALANCE_WAKE
| 0*SD_WAKE_AFFINE
- | 0*SD_PREFER_LOCAL
| 0*SD_SHARE_CPUPOWER
| 0*SD_SHARE_PKG_RESOURCES
| 1*SD_SERIALIZE
@@ -8335,6 +7797,8 @@ struct cgroup_subsys cpu_cgroup_subsys = {
* (balbir@in.ibm.com).
*/
+struct cpuacct root_cpuacct;
+
/* create a new cpu accounting group */
static struct cgroup_subsys_state *cpuacct_create(struct cgroup *cgrp)
{
diff --git a/kernel/sched/cputime.c b/kernel/sched/cputime.c
new file mode 100644
index 000000000000..81b763ba58a6
--- /dev/null
+++ b/kernel/sched/cputime.c
@@ -0,0 +1,530 @@
+#include <linux/export.h>
+#include <linux/sched.h>
+#include <linux/tsacct_kern.h>
+#include <linux/kernel_stat.h>
+#include <linux/static_key.h>
+#include "sched.h"
+
+
+#ifdef CONFIG_IRQ_TIME_ACCOUNTING
+
+/*
+ * There are no locks covering percpu hardirq/softirq time.
+ * They are only modified in vtime_account, on corresponding CPU
+ * with interrupts disabled. So, writes are safe.
+ * They are read and saved off onto struct rq in update_rq_clock().
+ * This may result in other CPU reading this CPU's irq time and can
+ * race with irq/vtime_account on this CPU. We would either get old
+ * or new value with a side effect of accounting a slice of irq time to wrong
+ * task when irq is in progress while we read rq->clock. That is a worthy
+ * compromise in place of having locks on each irq in account_system_time.
+ */
+DEFINE_PER_CPU(u64, cpu_hardirq_time);
+DEFINE_PER_CPU(u64, cpu_softirq_time);
+
+static DEFINE_PER_CPU(u64, irq_start_time);
+static int sched_clock_irqtime;
+
+void enable_sched_clock_irqtime(void)
+{
+ sched_clock_irqtime = 1;
+}
+
+void disable_sched_clock_irqtime(void)
+{
+ sched_clock_irqtime = 0;
+}
+
+#ifndef CONFIG_64BIT
+DEFINE_PER_CPU(seqcount_t, irq_time_seq);
+#endif /* CONFIG_64BIT */
+
+/*
+ * Called before incrementing preempt_count on {soft,}irq_enter
+ * and before decrementing preempt_count on {soft,}irq_exit.
+ */
+void vtime_account(struct task_struct *curr)
+{
+ unsigned long flags;
+ s64 delta;
+ int cpu;
+
+ if (!sched_clock_irqtime)
+ return;
+
+ local_irq_save(flags);
+
+ cpu = smp_processor_id();
+ delta = sched_clock_cpu(cpu) - __this_cpu_read(irq_start_time);
+ __this_cpu_add(irq_start_time, delta);
+
+ irq_time_write_begin();
+ /*
+ * We do not account for softirq time from ksoftirqd here.
+ * We want to continue accounting softirq time to ksoftirqd thread
+ * in that case, so as not to confuse scheduler with a special task
+ * that do not consume any time, but still wants to run.
+ */
+ if (hardirq_count())
+ __this_cpu_add(cpu_hardirq_time, delta);
+ else if (in_serving_softirq() && curr != this_cpu_ksoftirqd())
+ __this_cpu_add(cpu_softirq_time, delta);
+
+ irq_time_write_end();
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL_GPL(vtime_account);
+
+static int irqtime_account_hi_update(void)
+{
+ u64 *cpustat = kcpustat_this_cpu->cpustat;
+ unsigned long flags;
+ u64 latest_ns;
+ int ret = 0;
+
+ local_irq_save(flags);
+ latest_ns = this_cpu_read(cpu_hardirq_time);
+ if (nsecs_to_cputime64(latest_ns) > cpustat[CPUTIME_IRQ])
+ ret = 1;
+ local_irq_restore(flags);
+ return ret;
+}
+
+static int irqtime_account_si_update(void)
+{
+ u64 *cpustat = kcpustat_this_cpu->cpustat;
+ unsigned long flags;
+ u64 latest_ns;
+ int ret = 0;
+
+ local_irq_save(flags);
+ latest_ns = this_cpu_read(cpu_softirq_time);
+ if (nsecs_to_cputime64(latest_ns) > cpustat[CPUTIME_SOFTIRQ])
+ ret = 1;
+ local_irq_restore(flags);
+ return ret;
+}
+
+#else /* CONFIG_IRQ_TIME_ACCOUNTING */
+
+#define sched_clock_irqtime (0)
+
+#endif /* !CONFIG_IRQ_TIME_ACCOUNTING */
+
+static inline void task_group_account_field(struct task_struct *p, int index,
+ u64 tmp)
+{
+#ifdef CONFIG_CGROUP_CPUACCT
+ struct kernel_cpustat *kcpustat;
+ struct cpuacct *ca;
+#endif
+ /*
+ * Since all updates are sure to touch the root cgroup, we
+ * get ourselves ahead and touch it first. If the root cgroup
+ * is the only cgroup, then nothing else should be necessary.
+ *
+ */
+ __get_cpu_var(kernel_cpustat).cpustat[index] += tmp;
+
+#ifdef CONFIG_CGROUP_CPUACCT
+ if (unlikely(!cpuacct_subsys.active))
+ return;
+
+ rcu_read_lock();
+ ca = task_ca(p);
+ while (ca && (ca != &root_cpuacct)) {
+ kcpustat = this_cpu_ptr(ca->cpustat);
+ kcpustat->cpustat[index] += tmp;
+ ca = parent_ca(ca);
+ }
+ rcu_read_unlock();
+#endif
+}
+
+/*
+ * Account user cpu time to a process.
+ * @p: the process that the cpu time gets accounted to
+ * @cputime: the cpu time spent in user space since the last update
+ * @cputime_scaled: cputime scaled by cpu frequency
+ */
+void account_user_time(struct task_struct *p, cputime_t cputime,
+ cputime_t cputime_scaled)
+{
+ int index;
+
+ /* Add user time to process. */
+ p->utime += cputime;
+ p->utimescaled += cputime_scaled;
+ account_group_user_time(p, cputime);
+
+ index = (TASK_NICE(p) > 0) ? CPUTIME_NICE : CPUTIME_USER;
+
+ /* Add user time to cpustat. */
+ task_group_account_field(p, index, (__force u64) cputime);
+
+ /* Account for user time used */
+ acct_update_integrals(p);
+}
+
+/*
+ * Account guest cpu time to a process.
+ * @p: the process that the cpu time gets accounted to
+ * @cputime: the cpu time spent in virtual machine since the last update
+ * @cputime_scaled: cputime scaled by cpu frequency
+ */
+static void account_guest_time(struct task_struct *p, cputime_t cputime,
+ cputime_t cputime_scaled)
+{
+ u64 *cpustat = kcpustat_this_cpu->cpustat;
+
+ /* Add guest time to process. */
+ p->utime += cputime;
+ p->utimescaled += cputime_scaled;
+ account_group_user_time(p, cputime);
+ p->gtime += cputime;
+
+ /* Add guest time to cpustat. */
+ if (TASK_NICE(p) > 0) {
+ cpustat[CPUTIME_NICE] += (__force u64) cputime;
+ cpustat[CPUTIME_GUEST_NICE] += (__force u64) cputime;
+ } else {
+ cpustat[CPUTIME_USER] += (__force u64) cputime;
+ cpustat[CPUTIME_GUEST] += (__force u64) cputime;
+ }
+}
+
+/*
+ * Account system cpu time to a process and desired cpustat field
+ * @p: the process that the cpu time gets accounted to
+ * @cputime: the cpu time spent in kernel space since the last update
+ * @cputime_scaled: cputime scaled by cpu frequency
+ * @target_cputime64: pointer to cpustat field that has to be updated
+ */
+static inline
+void __account_system_time(struct task_struct *p, cputime_t cputime,
+ cputime_t cputime_scaled, int index)
+{
+ /* Add system time to process. */
+ p->stime += cputime;
+ p->stimescaled += cputime_scaled;
+ account_group_system_time(p, cputime);
+
+ /* Add system time to cpustat. */
+ task_group_account_field(p, index, (__force u64) cputime);
+
+ /* Account for system time used */
+ acct_update_integrals(p);
+}
+
+/*
+ * Account system cpu time to a process.
+ * @p: the process that the cpu time gets accounted to
+ * @hardirq_offset: the offset to subtract from hardirq_count()
+ * @cputime: the cpu time spent in kernel space since the last update
+ * @cputime_scaled: cputime scaled by cpu frequency
+ */
+void account_system_time(struct task_struct *p, int hardirq_offset,
+ cputime_t cputime, cputime_t cputime_scaled)
+{
+ int index;
+
+ if ((p->flags & PF_VCPU) && (irq_count() - hardirq_offset == 0)) {
+ account_guest_time(p, cputime, cputime_scaled);
+ return;
+ }
+
+ if (hardirq_count() - hardirq_offset)
+ index = CPUTIME_IRQ;
+ else if (in_serving_softirq())
+ index = CPUTIME_SOFTIRQ;
+ else
+ index = CPUTIME_SYSTEM;
+
+ __account_system_time(p, cputime, cputime_scaled, index);
+}
+
+/*
+ * Account for involuntary wait time.
+ * @cputime: the cpu time spent in involuntary wait
+ */
+void account_steal_time(cputime_t cputime)
+{
+ u64 *cpustat = kcpustat_this_cpu->cpustat;
+
+ cpustat[CPUTIME_STEAL] += (__force u64) cputime;
+}
+
+/*
+ * Account for idle time.
+ * @cputime: the cpu time spent in idle wait
+ */
+void account_idle_time(cputime_t cputime)
+{
+ u64 *cpustat = kcpustat_this_cpu->cpustat;
+ struct rq *rq = this_rq();
+
+ if (atomic_read(&rq->nr_iowait) > 0)
+ cpustat[CPUTIME_IOWAIT] += (__force u64) cputime;
+ else
+ cpustat[CPUTIME_IDLE] += (__force u64) cputime;
+}
+
+static __always_inline bool steal_account_process_tick(void)
+{
+#ifdef CONFIG_PARAVIRT
+ if (static_key_false(&paravirt_steal_enabled)) {
+ u64 steal, st = 0;
+
+ steal = paravirt_steal_clock(smp_processor_id());
+ steal -= this_rq()->prev_steal_time;
+
+ st = steal_ticks(steal);
+ this_rq()->prev_steal_time += st * TICK_NSEC;
+
+ account_steal_time(st);
+ return st;
+ }
+#endif
+ return false;
+}
+
+#ifndef CONFIG_VIRT_CPU_ACCOUNTING
+
+#ifdef CONFIG_IRQ_TIME_ACCOUNTING
+/*
+ * Account a tick to a process and cpustat
+ * @p: the process that the cpu time gets accounted to
+ * @user_tick: is the tick from userspace
+ * @rq: the pointer to rq
+ *
+ * Tick demultiplexing follows the order
+ * - pending hardirq update
+ * - pending softirq update
+ * - user_time
+ * - idle_time
+ * - system time
+ * - check for guest_time
+ * - else account as system_time
+ *
+ * Check for hardirq is done both for system and user time as there is
+ * no timer going off while we are on hardirq and hence we may never get an
+ * opportunity to update it solely in system time.
+ * p->stime and friends are only updated on system time and not on irq
+ * softirq as those do not count in task exec_runtime any more.
+ */
+static void irqtime_account_process_tick(struct task_struct *p, int user_tick,
+ struct rq *rq)
+{
+ cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy);
+ u64 *cpustat = kcpustat_this_cpu->cpustat;
+
+ if (steal_account_process_tick())
+ return;
+
+ if (irqtime_account_hi_update()) {
+ cpustat[CPUTIME_IRQ] += (__force u64) cputime_one_jiffy;
+ } else if (irqtime_account_si_update()) {
+ cpustat[CPUTIME_SOFTIRQ] += (__force u64) cputime_one_jiffy;
+ } else if (this_cpu_ksoftirqd() == p) {
+ /*
+ * ksoftirqd time do not get accounted in cpu_softirq_time.
+ * So, we have to handle it separately here.
+ * Also, p->stime needs to be updated for ksoftirqd.
+ */
+ __account_system_time(p, cputime_one_jiffy, one_jiffy_scaled,
+ CPUTIME_SOFTIRQ);
+ } else if (user_tick) {
+ account_user_time(p, cputime_one_jiffy, one_jiffy_scaled);
+ } else if (p == rq->idle) {
+ account_idle_time(cputime_one_jiffy);
+ } else if (p->flags & PF_VCPU) { /* System time or guest time */
+ account_guest_time(p, cputime_one_jiffy, one_jiffy_scaled);
+ } else {
+ __account_system_time(p, cputime_one_jiffy, one_jiffy_scaled,
+ CPUTIME_SYSTEM);
+ }
+}
+
+static void irqtime_account_idle_ticks(int ticks)
+{
+ int i;
+ struct rq *rq = this_rq();
+
+ for (i = 0; i < ticks; i++)
+ irqtime_account_process_tick(current, 0, rq);
+}
+#else /* CONFIG_IRQ_TIME_ACCOUNTING */
+static void irqtime_account_idle_ticks(int ticks) {}
+static void irqtime_account_process_tick(struct task_struct *p, int user_tick,
+ struct rq *rq) {}
+#endif /* CONFIG_IRQ_TIME_ACCOUNTING */
+
+/*
+ * Account a single tick of cpu time.
+ * @p: the process that the cpu time gets accounted to
+ * @user_tick: indicates if the tick is a user or a system tick
+ */
+void account_process_tick(struct task_struct *p, int user_tick)
+{
+ cputime_t one_jiffy_scaled = cputime_to_scaled(cputime_one_jiffy);
+ struct rq *rq = this_rq();
+
+ if (sched_clock_irqtime) {
+ irqtime_account_process_tick(p, user_tick, rq);
+ return;
+ }
+
+ if (steal_account_process_tick())
+ return;
+
+ if (user_tick)
+ account_user_time(p, cputime_one_jiffy, one_jiffy_scaled);
+ else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET))
+ account_system_time(p, HARDIRQ_OFFSET, cputime_one_jiffy,
+ one_jiffy_scaled);
+ else
+ account_idle_time(cputime_one_jiffy);
+}
+
+/*
+ * Account multiple ticks of steal time.
+ * @p: the process from which the cpu time has been stolen
+ * @ticks: number of stolen ticks
+ */
+void account_steal_ticks(unsigned long ticks)
+{
+ account_steal_time(jiffies_to_cputime(ticks));
+}
+
+/*
+ * Account multiple ticks of idle time.
+ * @ticks: number of stolen ticks
+ */
+void account_idle_ticks(unsigned long ticks)
+{
+
+ if (sched_clock_irqtime) {
+ irqtime_account_idle_ticks(ticks);
+ return;
+ }
+
+ account_idle_time(jiffies_to_cputime(ticks));
+}
+
+#endif
+
+/*
+ * Use precise platform statistics if available:
+ */
+#ifdef CONFIG_VIRT_CPU_ACCOUNTING
+void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
+{
+ *ut = p->utime;
+ *st = p->stime;
+}
+
+void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
+{
+ struct task_cputime cputime;
+
+ thread_group_cputime(p, &cputime);
+
+ *ut = cputime.utime;
+ *st = cputime.stime;
+}
+
+/*
+ * Archs that account the whole time spent in the idle task
+ * (outside irq) as idle time can rely on this and just implement
+ * vtime_account_system() and vtime_account_idle(). Archs that
+ * have other meaning of the idle time (s390 only includes the
+ * time spent by the CPU when it's in low power mode) must override
+ * vtime_account().
+ */
+#ifndef __ARCH_HAS_VTIME_ACCOUNT
+void vtime_account(struct task_struct *tsk)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ if (in_interrupt() || !is_idle_task(tsk))
+ vtime_account_system(tsk);
+ else
+ vtime_account_idle(tsk);
+
+ local_irq_restore(flags);
+}
+EXPORT_SYMBOL_GPL(vtime_account);
+#endif /* __ARCH_HAS_VTIME_ACCOUNT */
+
+#else
+
+#ifndef nsecs_to_cputime
+# define nsecs_to_cputime(__nsecs) nsecs_to_jiffies(__nsecs)
+#endif
+
+static cputime_t scale_utime(cputime_t utime, cputime_t rtime, cputime_t total)
+{
+ u64 temp = (__force u64) rtime;
+
+ temp *= (__force u64) utime;
+
+ if (sizeof(cputime_t) == 4)
+ temp = div_u64(temp, (__force u32) total);
+ else
+ temp = div64_u64(temp, (__force u64) total);
+
+ return (__force cputime_t) temp;
+}
+
+void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
+{
+ cputime_t rtime, utime = p->utime, total = utime + p->stime;
+
+ /*
+ * Use CFS's precise accounting:
+ */
+ rtime = nsecs_to_cputime(p->se.sum_exec_runtime);
+
+ if (total)
+ utime = scale_utime(utime, rtime, total);
+ else
+ utime = rtime;
+
+ /*
+ * Compare with previous values, to keep monotonicity:
+ */
+ p->prev_utime = max(p->prev_utime, utime);
+ p->prev_stime = max(p->prev_stime, rtime - p->prev_utime);
+
+ *ut = p->prev_utime;
+ *st = p->prev_stime;
+}
+
+/*
+ * Must be called with siglock held.
+ */
+void thread_group_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
+{
+ struct signal_struct *sig = p->signal;
+ struct task_cputime cputime;
+ cputime_t rtime, utime, total;
+
+ thread_group_cputime(p, &cputime);
+
+ total = cputime.utime + cputime.stime;
+ rtime = nsecs_to_cputime(cputime.sum_exec_runtime);
+
+ if (total)
+ utime = scale_utime(cputime.utime, rtime, total);
+ else
+ utime = rtime;
+
+ sig->prev_utime = max(sig->prev_utime, utime);
+ sig->prev_stime = max(sig->prev_stime, rtime - sig->prev_utime);
+
+ *ut = sig->prev_utime;
+ *st = sig->prev_stime;
+}
+#endif
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 96e2b18b6283..6b800a14b990 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -597,7 +597,7 @@ calc_delta_fair(unsigned long delta, struct sched_entity *se)
/*
* The idea is to set a period in which each task runs once.
*
- * When there are too many tasks (sysctl_sched_nr_latency) we have to stretch
+ * When there are too many tasks (sched_nr_latency) we have to stretch
* this period because otherwise the slices get too small.
*
* p = (nr <= nl) ? l : l*nr/nl
@@ -2700,7 +2700,6 @@ select_task_rq_fair(struct task_struct *p, int sd_flag, int wake_flags)
int prev_cpu = task_cpu(p);
int new_cpu = cpu;
int want_affine = 0;
- int want_sd = 1;
int sync = wake_flags & WF_SYNC;
if (p->nr_cpus_allowed == 1)
@@ -2718,48 +2717,21 @@ select_task_rq_fair(struct task_struct *p, int sd_flag, int wake_flags)
continue;
/*
- * If power savings logic is enabled for a domain, see if we
- * are not overloaded, if so, don't balance wider.
- */
- if (tmp->flags & (SD_PREFER_LOCAL)) {
- unsigned long power = 0;
- unsigned long nr_running = 0;
- unsigned long capacity;
- int i;
-
- for_each_cpu(i, sched_domain_span(tmp)) {
- power += power_of(i);
- nr_running += cpu_rq(i)->cfs.nr_running;
- }
-
- capacity = DIV_ROUND_CLOSEST(power, SCHED_POWER_SCALE);
-
- if (nr_running < capacity)
- want_sd = 0;
- }
-
- /*
* If both cpu and prev_cpu are part of this domain,
* cpu is a valid SD_WAKE_AFFINE target.
*/
if (want_affine && (tmp->flags & SD_WAKE_AFFINE) &&
cpumask_test_cpu(prev_cpu, sched_domain_span(tmp))) {
affine_sd = tmp;
- want_affine = 0;
- }
-
- if (!want_sd && !want_affine)
break;
+ }
- if (!(tmp->flags & sd_flag))
- continue;
-
- if (want_sd)
+ if (tmp->flags & sd_flag)
sd = tmp;
}
if (affine_sd) {
- if (cpu == prev_cpu || wake_affine(affine_sd, p, sync))
+ if (cpu != prev_cpu && wake_affine(affine_sd, p, sync))
prev_cpu = cpu;
new_cpu = select_idle_sibling(p, prev_cpu);
@@ -4295,7 +4267,7 @@ redo:
goto out_balanced;
}
- BUG_ON(busiest == this_rq);
+ BUG_ON(busiest == env.dst_rq);
schedstat_add(sd, lb_imbalance[idle], env.imbalance);
@@ -4316,7 +4288,7 @@ redo:
update_h_load(env.src_cpu);
more_balance:
local_irq_save(flags);
- double_rq_lock(this_rq, busiest);
+ double_rq_lock(env.dst_rq, busiest);
/*
* cur_ld_moved - load moved in current iteration
@@ -4324,7 +4296,7 @@ more_balance:
*/
cur_ld_moved = move_tasks(&env);
ld_moved += cur_ld_moved;
- double_rq_unlock(this_rq, busiest);
+ double_rq_unlock(env.dst_rq, busiest);
local_irq_restore(flags);
if (env.flags & LBF_NEED_BREAK) {
@@ -4360,8 +4332,7 @@ more_balance:
if ((env.flags & LBF_SOME_PINNED) && env.imbalance > 0 &&
lb_iterations++ < max_lb_iterations) {
- this_rq = cpu_rq(env.new_dst_cpu);
- env.dst_rq = this_rq;
+ env.dst_rq = cpu_rq(env.new_dst_cpu);
env.dst_cpu = env.new_dst_cpu;
env.flags &= ~LBF_SOME_PINNED;
env.loop = 0;
@@ -4646,7 +4617,7 @@ static void nohz_balancer_kick(int cpu)
return;
}
-static inline void clear_nohz_tick_stopped(int cpu)
+static inline void nohz_balance_exit_idle(int cpu)
{
if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))) {
cpumask_clear_cpu(cpu, nohz.idle_cpus_mask);
@@ -4686,28 +4657,23 @@ void set_cpu_sd_state_idle(void)
}
/*
- * This routine will record that this cpu is going idle with tick stopped.
+ * This routine will record that the cpu is going idle with tick stopped.
* This info will be used in performing idle load balancing in the future.
*/
-void select_nohz_load_balancer(int stop_tick)
+void nohz_balance_enter_idle(int cpu)
{
- int cpu = smp_processor_id();
-
/*
* If this cpu is going down, then nothing needs to be done.
*/
if (!cpu_active(cpu))
return;
- if (stop_tick) {
- if (test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))
- return;
+ if (test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))
+ return;
- cpumask_set_cpu(cpu, nohz.idle_cpus_mask);
- atomic_inc(&nohz.nr_cpus);
- set_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu));
- }
- return;
+ cpumask_set_cpu(cpu, nohz.idle_cpus_mask);
+ atomic_inc(&nohz.nr_cpus);
+ set_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu));
}
static int __cpuinit sched_ilb_notifier(struct notifier_block *nfb,
@@ -4715,7 +4681,7 @@ static int __cpuinit sched_ilb_notifier(struct notifier_block *nfb,
{
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_DYING:
- clear_nohz_tick_stopped(smp_processor_id());
+ nohz_balance_exit_idle(smp_processor_id());
return NOTIFY_OK;
default:
return NOTIFY_DONE;
@@ -4837,14 +4803,15 @@ static void nohz_idle_balance(int this_cpu, enum cpu_idle_type idle)
if (need_resched())
break;
- raw_spin_lock_irq(&this_rq->lock);
- update_rq_clock(this_rq);
- update_idle_cpu_load(this_rq);
- raw_spin_unlock_irq(&this_rq->lock);
+ rq = cpu_rq(balance_cpu);
+
+ raw_spin_lock_irq(&rq->lock);
+ update_rq_clock(rq);
+ update_idle_cpu_load(rq);
+ raw_spin_unlock_irq(&rq->lock);
rebalance_domains(balance_cpu, CPU_IDLE);
- rq = cpu_rq(balance_cpu);
if (time_after(this_rq->next_balance, rq->next_balance))
this_rq->next_balance = rq->next_balance;
}
@@ -4875,7 +4842,7 @@ static inline int nohz_kick_needed(struct rq *rq, int cpu)
* busy tick after returning from idle, we will update the busy stats.
*/
set_cpu_sd_state_busy();
- clear_nohz_tick_stopped(cpu);
+ nohz_balance_exit_idle(cpu);
/*
* None are in tickless mode and hence no need for NOHZ idle load
diff --git a/kernel/sched/features.h b/kernel/sched/features.h
index de00a486c5c6..eebefcad7027 100644
--- a/kernel/sched/features.h
+++ b/kernel/sched/features.h
@@ -12,14 +12,6 @@ SCHED_FEAT(GENTLE_FAIR_SLEEPERS, true)
SCHED_FEAT(START_DEBIT, true)
/*
- * Based on load and program behaviour, see if it makes sense to place
- * a newly woken task on the same cpu as the task that woke it --
- * improve cache locality. Typically used with SYNC wakeups as
- * generated by pipes and the like, see also SYNC_WAKEUPS.
- */
-SCHED_FEAT(AFFINE_WAKEUPS, true)
-
-/*
* Prefer to schedule the task we woke last (assuming it failed
* wakeup-preemption), since its likely going to consume data we
* touched, increases cache locality.
@@ -42,7 +34,7 @@ SCHED_FEAT(CACHE_HOT_BUDDY, true)
/*
* Use arch dependent cpu power functions
*/
-SCHED_FEAT(ARCH_POWER, false)
+SCHED_FEAT(ARCH_POWER, true)
SCHED_FEAT(HRTICK, false)
SCHED_FEAT(DOUBLE_TICK, false)
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index e0b7ba9c040f..418feb01344e 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -1632,11 +1632,6 @@ static int push_rt_task(struct rq *rq)
if (!next_task)
return 0;
-#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
- if (unlikely(task_running(rq, next_task)))
- return 0;
-#endif
-
retry:
if (unlikely(next_task == rq->curr)) {
WARN_ON(1);
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index 0848fa36c383..7a7db09cfabc 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -737,11 +737,7 @@ static inline void prepare_lock_switch(struct rq *rq, struct task_struct *next)
*/
next->on_cpu = 1;
#endif
-#ifdef __ARCH_WANT_INTERRUPTS_ON_CTXSW
- raw_spin_unlock_irq(&rq->lock);
-#else
raw_spin_unlock(&rq->lock);
-#endif
}
static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev)
@@ -755,9 +751,7 @@ static inline void finish_lock_switch(struct rq *rq, struct task_struct *prev)
smp_wmb();
prev->on_cpu = 0;
#endif
-#ifndef __ARCH_WANT_INTERRUPTS_ON_CTXSW
local_irq_enable();
-#endif
}
#endif /* __ARCH_WANT_UNLOCKED_CTXSW */
@@ -891,6 +885,9 @@ struct cpuacct {
struct kernel_cpustat __percpu *cpustat;
};
+extern struct cgroup_subsys cpuacct_subsys;
+extern struct cpuacct root_cpuacct;
+
/* return cpu accounting group corresponding to this container */
static inline struct cpuacct *cgroup_ca(struct cgroup *cgrp)
{
@@ -917,6 +914,16 @@ extern void cpuacct_charge(struct task_struct *tsk, u64 cputime);
static inline void cpuacct_charge(struct task_struct *tsk, u64 cputime) {}
#endif
+#ifdef CONFIG_PARAVIRT
+static inline u64 steal_ticks(u64 steal)
+{
+ if (unlikely(steal > NSEC_PER_SEC))
+ return div_u64(steal, TICK_NSEC);
+
+ return __iter_div_u64_rem(steal, TICK_NSEC, &steal);
+}
+#endif
+
static inline void inc_nr_running(struct rq *rq)
{
rq->nr_running++;
@@ -1156,3 +1163,53 @@ enum rq_nohz_flag_bits {
#define nohz_flags(cpu) (&cpu_rq(cpu)->nohz_flags)
#endif
+
+#ifdef CONFIG_IRQ_TIME_ACCOUNTING
+
+DECLARE_PER_CPU(u64, cpu_hardirq_time);
+DECLARE_PER_CPU(u64, cpu_softirq_time);
+
+#ifndef CONFIG_64BIT
+DECLARE_PER_CPU(seqcount_t, irq_time_seq);
+
+static inline void irq_time_write_begin(void)
+{
+ __this_cpu_inc(irq_time_seq.sequence);
+ smp_wmb();
+}
+
+static inline void irq_time_write_end(void)
+{
+ smp_wmb();
+ __this_cpu_inc(irq_time_seq.sequence);
+}
+
+static inline u64 irq_time_read(int cpu)
+{
+ u64 irq_time;
+ unsigned seq;
+
+ do {
+ seq = read_seqcount_begin(&per_cpu(irq_time_seq, cpu));
+ irq_time = per_cpu(cpu_softirq_time, cpu) +
+ per_cpu(cpu_hardirq_time, cpu);
+ } while (read_seqcount_retry(&per_cpu(irq_time_seq, cpu), seq));
+
+ return irq_time;
+}
+#else /* CONFIG_64BIT */
+static inline void irq_time_write_begin(void)
+{
+}
+
+static inline void irq_time_write_end(void)
+{
+}
+
+static inline u64 irq_time_read(int cpu)
+{
+ return per_cpu(cpu_softirq_time, cpu) + per_cpu(cpu_hardirq_time, cpu);
+}
+#endif /* CONFIG_64BIT */
+#endif /* CONFIG_IRQ_TIME_ACCOUNTING */
+
diff --git a/kernel/signal.c b/kernel/signal.c
index be4f856d52f8..2c681f11b7d2 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1971,13 +1971,8 @@ static void ptrace_do_notify(int signr, int exit_code, int why)
void ptrace_notify(int exit_code)
{
BUG_ON((exit_code & (0x7f | ~0xffff)) != SIGTRAP);
- if (unlikely(current->task_works)) {
- if (test_and_clear_ti_thread_flag(current_thread_info(),
- TIF_NOTIFY_RESUME)) {
- smp_mb__after_clear_bit();
- task_work_run();
- }
- }
+ if (unlikely(current->task_works))
+ task_work_run();
spin_lock_irq(&current->sighand->siglock);
ptrace_do_notify(SIGTRAP, exit_code, CLD_TRAPPED);
@@ -2198,13 +2193,8 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
struct signal_struct *signal = current->signal;
int signr;
- if (unlikely(current->task_works)) {
- if (test_and_clear_ti_thread_flag(current_thread_info(),
- TIF_NOTIFY_RESUME)) {
- smp_mb__after_clear_bit();
- task_work_run();
- }
- }
+ if (unlikely(current->task_works))
+ task_work_run();
if (unlikely(uprobe_deny_signal()))
return 0;
diff --git a/kernel/smpboot.c b/kernel/smpboot.c
index 98f60c5caa1b..d6c5fc054242 100644
--- a/kernel/smpboot.c
+++ b/kernel/smpboot.c
@@ -1,14 +1,22 @@
/*
* Common SMP CPU bringup/teardown functions
*/
+#include <linux/cpu.h>
#include <linux/err.h>
#include <linux/smp.h>
#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/slab.h>
#include <linux/sched.h>
+#include <linux/export.h>
#include <linux/percpu.h>
+#include <linux/kthread.h>
+#include <linux/smpboot.h>
#include "smpboot.h"
+#ifdef CONFIG_SMP
+
#ifdef CONFIG_GENERIC_SMP_IDLE_THREAD
/*
* For the hotplug case we keep the task structs around and reuse
@@ -65,3 +73,228 @@ void __init idle_threads_init(void)
}
}
#endif
+
+#endif /* #ifdef CONFIG_SMP */
+
+static LIST_HEAD(hotplug_threads);
+static DEFINE_MUTEX(smpboot_threads_lock);
+
+struct smpboot_thread_data {
+ unsigned int cpu;
+ unsigned int status;
+ struct smp_hotplug_thread *ht;
+};
+
+enum {
+ HP_THREAD_NONE = 0,
+ HP_THREAD_ACTIVE,
+ HP_THREAD_PARKED,
+};
+
+/**
+ * smpboot_thread_fn - percpu hotplug thread loop function
+ * @data: thread data pointer
+ *
+ * Checks for thread stop and park conditions. Calls the necessary
+ * setup, cleanup, park and unpark functions for the registered
+ * thread.
+ *
+ * Returns 1 when the thread should exit, 0 otherwise.
+ */
+static int smpboot_thread_fn(void *data)
+{
+ struct smpboot_thread_data *td = data;
+ struct smp_hotplug_thread *ht = td->ht;
+
+ while (1) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ preempt_disable();
+ if (kthread_should_stop()) {
+ set_current_state(TASK_RUNNING);
+ preempt_enable();
+ if (ht->cleanup)
+ ht->cleanup(td->cpu, cpu_online(td->cpu));
+ kfree(td);
+ return 0;
+ }
+
+ if (kthread_should_park()) {
+ __set_current_state(TASK_RUNNING);
+ preempt_enable();
+ if (ht->park && td->status == HP_THREAD_ACTIVE) {
+ BUG_ON(td->cpu != smp_processor_id());
+ ht->park(td->cpu);
+ td->status = HP_THREAD_PARKED;
+ }
+ kthread_parkme();
+ /* We might have been woken for stop */
+ continue;
+ }
+
+ BUG_ON(td->cpu != smp_processor_id());
+
+ /* Check for state change setup */
+ switch (td->status) {
+ case HP_THREAD_NONE:
+ preempt_enable();
+ if (ht->setup)
+ ht->setup(td->cpu);
+ td->status = HP_THREAD_ACTIVE;
+ preempt_disable();
+ break;
+ case HP_THREAD_PARKED:
+ preempt_enable();
+ if (ht->unpark)
+ ht->unpark(td->cpu);
+ td->status = HP_THREAD_ACTIVE;
+ preempt_disable();
+ break;
+ }
+
+ if (!ht->thread_should_run(td->cpu)) {
+ preempt_enable();
+ schedule();
+ } else {
+ set_current_state(TASK_RUNNING);
+ preempt_enable();
+ ht->thread_fn(td->cpu);
+ }
+ }
+}
+
+static int
+__smpboot_create_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
+{
+ struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
+ struct smpboot_thread_data *td;
+
+ if (tsk)
+ return 0;
+
+ td = kzalloc_node(sizeof(*td), GFP_KERNEL, cpu_to_node(cpu));
+ if (!td)
+ return -ENOMEM;
+ td->cpu = cpu;
+ td->ht = ht;
+
+ tsk = kthread_create_on_cpu(smpboot_thread_fn, td, cpu,
+ ht->thread_comm);
+ if (IS_ERR(tsk)) {
+ kfree(td);
+ return PTR_ERR(tsk);
+ }
+
+ get_task_struct(tsk);
+ *per_cpu_ptr(ht->store, cpu) = tsk;
+ return 0;
+}
+
+int smpboot_create_threads(unsigned int cpu)
+{
+ struct smp_hotplug_thread *cur;
+ int ret = 0;
+
+ mutex_lock(&smpboot_threads_lock);
+ list_for_each_entry(cur, &hotplug_threads, list) {
+ ret = __smpboot_create_thread(cur, cpu);
+ if (ret)
+ break;
+ }
+ mutex_unlock(&smpboot_threads_lock);
+ return ret;
+}
+
+static void smpboot_unpark_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
+{
+ struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
+
+ kthread_unpark(tsk);
+}
+
+void smpboot_unpark_threads(unsigned int cpu)
+{
+ struct smp_hotplug_thread *cur;
+
+ mutex_lock(&smpboot_threads_lock);
+ list_for_each_entry(cur, &hotplug_threads, list)
+ smpboot_unpark_thread(cur, cpu);
+ mutex_unlock(&smpboot_threads_lock);
+}
+
+static void smpboot_park_thread(struct smp_hotplug_thread *ht, unsigned int cpu)
+{
+ struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
+
+ if (tsk)
+ kthread_park(tsk);
+}
+
+void smpboot_park_threads(unsigned int cpu)
+{
+ struct smp_hotplug_thread *cur;
+
+ mutex_lock(&smpboot_threads_lock);
+ list_for_each_entry_reverse(cur, &hotplug_threads, list)
+ smpboot_park_thread(cur, cpu);
+ mutex_unlock(&smpboot_threads_lock);
+}
+
+static void smpboot_destroy_threads(struct smp_hotplug_thread *ht)
+{
+ unsigned int cpu;
+
+ /* We need to destroy also the parked threads of offline cpus */
+ for_each_possible_cpu(cpu) {
+ struct task_struct *tsk = *per_cpu_ptr(ht->store, cpu);
+
+ if (tsk) {
+ kthread_stop(tsk);
+ put_task_struct(tsk);
+ *per_cpu_ptr(ht->store, cpu) = NULL;
+ }
+ }
+}
+
+/**
+ * smpboot_register_percpu_thread - Register a per_cpu thread related to hotplug
+ * @plug_thread: Hotplug thread descriptor
+ *
+ * Creates and starts the threads on all online cpus.
+ */
+int smpboot_register_percpu_thread(struct smp_hotplug_thread *plug_thread)
+{
+ unsigned int cpu;
+ int ret = 0;
+
+ mutex_lock(&smpboot_threads_lock);
+ for_each_online_cpu(cpu) {
+ ret = __smpboot_create_thread(plug_thread, cpu);
+ if (ret) {
+ smpboot_destroy_threads(plug_thread);
+ goto out;
+ }
+ smpboot_unpark_thread(plug_thread, cpu);
+ }
+ list_add(&plug_thread->list, &hotplug_threads);
+out:
+ mutex_unlock(&smpboot_threads_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(smpboot_register_percpu_thread);
+
+/**
+ * smpboot_unregister_percpu_thread - Unregister a per_cpu thread related to hotplug
+ * @plug_thread: Hotplug thread descriptor
+ *
+ * Stops all threads on all possible cpus.
+ */
+void smpboot_unregister_percpu_thread(struct smp_hotplug_thread *plug_thread)
+{
+ get_online_cpus();
+ mutex_lock(&smpboot_threads_lock);
+ list_del(&plug_thread->list);
+ smpboot_destroy_threads(plug_thread);
+ mutex_unlock(&smpboot_threads_lock);
+ put_online_cpus();
+}
+EXPORT_SYMBOL_GPL(smpboot_unregister_percpu_thread);
diff --git a/kernel/smpboot.h b/kernel/smpboot.h
index 6ef9433e1c70..72415a0eb955 100644
--- a/kernel/smpboot.h
+++ b/kernel/smpboot.h
@@ -13,4 +13,8 @@ static inline void idle_thread_set_boot_cpu(void) { }
static inline void idle_threads_init(void) { }
#endif
+int smpboot_create_threads(unsigned int cpu);
+void smpboot_park_threads(unsigned int cpu);
+void smpboot_unpark_threads(unsigned int cpu);
+
#endif
diff --git a/kernel/softirq.c b/kernel/softirq.c
index b73e681df09e..cc96bdc0c2c9 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -23,6 +23,7 @@
#include <linux/rcupdate.h>
#include <linux/ftrace.h>
#include <linux/smp.h>
+#include <linux/smpboot.h>
#include <linux/tick.h>
#define CREATE_TRACE_POINTS
@@ -220,7 +221,7 @@ asmlinkage void __do_softirq(void)
current->flags &= ~PF_MEMALLOC;
pending = local_softirq_pending();
- account_system_vtime(current);
+ vtime_account(current);
__local_bh_disable((unsigned long)__builtin_return_address(0),
SOFTIRQ_OFFSET);
@@ -271,7 +272,7 @@ restart:
lockdep_softirq_exit();
- account_system_vtime(current);
+ vtime_account(current);
__local_bh_enable(SOFTIRQ_OFFSET);
tsk_restore_flags(current, old_flags, PF_MEMALLOC);
}
@@ -340,7 +341,7 @@ static inline void invoke_softirq(void)
*/
void irq_exit(void)
{
- account_system_vtime(current);
+ vtime_account(current);
trace_hardirq_exit();
sub_preempt_count(IRQ_EXIT_OFFSET);
if (!in_interrupt() && local_softirq_pending())
@@ -742,49 +743,22 @@ void __init softirq_init(void)
open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}
-static int run_ksoftirqd(void * __bind_cpu)
+static int ksoftirqd_should_run(unsigned int cpu)
{
- set_current_state(TASK_INTERRUPTIBLE);
-
- while (!kthread_should_stop()) {
- preempt_disable();
- if (!local_softirq_pending()) {
- schedule_preempt_disabled();
- }
-
- __set_current_state(TASK_RUNNING);
-
- while (local_softirq_pending()) {
- /* Preempt disable stops cpu going offline.
- If already offline, we'll be on wrong CPU:
- don't process */
- if (cpu_is_offline((long)__bind_cpu))
- goto wait_to_die;
- local_irq_disable();
- if (local_softirq_pending())
- __do_softirq();
- local_irq_enable();
- sched_preempt_enable_no_resched();
- cond_resched();
- preempt_disable();
- rcu_note_context_switch((long)__bind_cpu);
- }
- preempt_enable();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- __set_current_state(TASK_RUNNING);
- return 0;
+ return local_softirq_pending();
+}
-wait_to_die:
- preempt_enable();
- /* Wait for kthread_stop */
- set_current_state(TASK_INTERRUPTIBLE);
- while (!kthread_should_stop()) {
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
+static void run_ksoftirqd(unsigned int cpu)
+{
+ local_irq_disable();
+ if (local_softirq_pending()) {
+ __do_softirq();
+ rcu_note_context_switch(cpu);
+ local_irq_enable();
+ cond_resched();
+ return;
}
- __set_current_state(TASK_RUNNING);
- return 0;
+ local_irq_enable();
}
#ifdef CONFIG_HOTPLUG_CPU
@@ -850,50 +824,14 @@ static int __cpuinit cpu_callback(struct notifier_block *nfb,
unsigned long action,
void *hcpu)
{
- int hotcpu = (unsigned long)hcpu;
- struct task_struct *p;
-
switch (action) {
- case CPU_UP_PREPARE:
- case CPU_UP_PREPARE_FROZEN:
- p = kthread_create_on_node(run_ksoftirqd,
- hcpu,
- cpu_to_node(hotcpu),
- "ksoftirqd/%d", hotcpu);
- if (IS_ERR(p)) {
- printk("ksoftirqd for %i failed\n", hotcpu);
- return notifier_from_errno(PTR_ERR(p));
- }
- kthread_bind(p, hotcpu);
- per_cpu(ksoftirqd, hotcpu) = p;
- break;
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- wake_up_process(per_cpu(ksoftirqd, hotcpu));
- break;
#ifdef CONFIG_HOTPLUG_CPU
- case CPU_UP_CANCELED:
- case CPU_UP_CANCELED_FROZEN:
- if (!per_cpu(ksoftirqd, hotcpu))
- break;
- /* Unbind so it can run. Fall thru. */
- kthread_bind(per_cpu(ksoftirqd, hotcpu),
- cpumask_any(cpu_online_mask));
case CPU_DEAD:
- case CPU_DEAD_FROZEN: {
- static const struct sched_param param = {
- .sched_priority = MAX_RT_PRIO-1
- };
-
- p = per_cpu(ksoftirqd, hotcpu);
- per_cpu(ksoftirqd, hotcpu) = NULL;
- sched_setscheduler_nocheck(p, SCHED_FIFO, &param);
- kthread_stop(p);
- takeover_tasklets(hotcpu);
+ case CPU_DEAD_FROZEN:
+ takeover_tasklets((unsigned long)hcpu);
break;
- }
#endif /* CONFIG_HOTPLUG_CPU */
- }
+ }
return NOTIFY_OK;
}
@@ -901,14 +839,19 @@ static struct notifier_block __cpuinitdata cpu_nfb = {
.notifier_call = cpu_callback
};
+static struct smp_hotplug_thread softirq_threads = {
+ .store = &ksoftirqd,
+ .thread_should_run = ksoftirqd_should_run,
+ .thread_fn = run_ksoftirqd,
+ .thread_comm = "ksoftirqd/%u",
+};
+
static __init int spawn_ksoftirqd(void)
{
- void *cpu = (void *)(long)smp_processor_id();
- int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);
-
- BUG_ON(err != NOTIFY_OK);
- cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);
register_cpu_notifier(&cpu_nfb);
+
+ BUG_ON(smpboot_register_percpu_thread(&softirq_threads));
+
return 0;
}
early_initcall(spawn_ksoftirqd);
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 87174ef59161..84c76a34e41c 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -307,7 +307,7 @@ static struct ctl_table kern_table[] = {
.extra2 = &max_sched_tunable_scaling,
},
{
- .procname = "sched_migration_cost",
+ .procname = "sched_migration_cost_ns",
.data = &sysctl_sched_migration_cost,
.maxlen = sizeof(unsigned int),
.mode = 0644,
@@ -321,14 +321,14 @@ static struct ctl_table kern_table[] = {
.proc_handler = proc_dointvec,
},
{
- .procname = "sched_time_avg",
+ .procname = "sched_time_avg_ms",
.data = &sysctl_sched_time_avg,
.maxlen = sizeof(unsigned int),
.mode = 0644,
.proc_handler = proc_dointvec,
},
{
- .procname = "sched_shares_window",
+ .procname = "sched_shares_window_ns",
.data = &sysctl_sched_shares_window,
.maxlen = sizeof(unsigned int),
.mode = 0644,
@@ -1544,7 +1544,7 @@ static struct ctl_table fs_table[] = {
static struct ctl_table debug_table[] = {
#if defined(CONFIG_X86) || defined(CONFIG_PPC) || defined(CONFIG_SPARC) || \
- defined(CONFIG_S390) || defined(CONFIG_TILE)
+ defined(CONFIG_S390) || defined(CONFIG_TILE) || defined(CONFIG_ARM64)
{
.procname = "exception-trace",
.data = &show_unhandled_signals,
diff --git a/kernel/task_work.c b/kernel/task_work.c
index d320d44903bd..65bd3c92d6f3 100644
--- a/kernel/task_work.c
+++ b/kernel/task_work.c
@@ -2,26 +2,20 @@
#include <linux/task_work.h>
#include <linux/tracehook.h>
+static struct callback_head work_exited; /* all we need is ->next == NULL */
+
int
-task_work_add(struct task_struct *task, struct callback_head *twork, bool notify)
+task_work_add(struct task_struct *task, struct callback_head *work, bool notify)
{
- struct callback_head *last, *first;
- unsigned long flags;
+ struct callback_head *head;
- /*
- * Not inserting the new work if the task has already passed
- * exit_task_work() is the responisbility of callers.
- */
- raw_spin_lock_irqsave(&task->pi_lock, flags);
- last = task->task_works;
- first = last ? last->next : twork;
- twork->next = first;
- if (last)
- last->next = twork;
- task->task_works = twork;
- raw_spin_unlock_irqrestore(&task->pi_lock, flags);
+ do {
+ head = ACCESS_ONCE(task->task_works);
+ if (unlikely(head == &work_exited))
+ return -ESRCH;
+ work->next = head;
+ } while (cmpxchg(&task->task_works, head, work) != head);
- /* test_and_set_bit() implies mb(), see tracehook_notify_resume(). */
if (notify)
set_notify_resume(task);
return 0;
@@ -30,52 +24,69 @@ task_work_add(struct task_struct *task, struct callback_head *twork, bool notify
struct callback_head *
task_work_cancel(struct task_struct *task, task_work_func_t func)
{
+ struct callback_head **pprev = &task->task_works;
+ struct callback_head *work = NULL;
unsigned long flags;
- struct callback_head *last, *res = NULL;
-
+ /*
+ * If cmpxchg() fails we continue without updating pprev.
+ * Either we raced with task_work_add() which added the
+ * new entry before this work, we will find it again. Or
+ * we raced with task_work_run(), *pprev == NULL/exited.
+ */
raw_spin_lock_irqsave(&task->pi_lock, flags);
- last = task->task_works;
- if (last) {
- struct callback_head *q = last, *p = q->next;
- while (1) {
- if (p->func == func) {
- q->next = p->next;
- if (p == last)
- task->task_works = q == p ? NULL : q;
- res = p;
- break;
- }
- if (p == last)
- break;
- q = p;
- p = q->next;
- }
+ while ((work = ACCESS_ONCE(*pprev))) {
+ read_barrier_depends();
+ if (work->func != func)
+ pprev = &work->next;
+ else if (cmpxchg(pprev, work, work->next) == work)
+ break;
}
raw_spin_unlock_irqrestore(&task->pi_lock, flags);
- return res;
+
+ return work;
}
void task_work_run(void)
{
struct task_struct *task = current;
- struct callback_head *p, *q;
+ struct callback_head *work, *head, *next;
+
+ for (;;) {
+ /*
+ * work->func() can do task_work_add(), do not set
+ * work_exited unless the list is empty.
+ */
+ do {
+ work = ACCESS_ONCE(task->task_works);
+ head = !work && (task->flags & PF_EXITING) ?
+ &work_exited : NULL;
+ } while (cmpxchg(&task->task_works, work, head) != work);
- while (1) {
- raw_spin_lock_irq(&task->pi_lock);
- p = task->task_works;
- task->task_works = NULL;
- raw_spin_unlock_irq(&task->pi_lock);
+ if (!work)
+ break;
+ /*
+ * Synchronize with task_work_cancel(). It can't remove
+ * the first entry == work, cmpxchg(task_works) should
+ * fail, but it can play with *work and other entries.
+ */
+ raw_spin_unlock_wait(&task->pi_lock);
+ smp_mb();
- if (unlikely(!p))
- return;
+ /* Reverse the list to run the works in fifo order */
+ head = NULL;
+ do {
+ next = work->next;
+ work->next = head;
+ head = work;
+ work = next;
+ } while (work);
- q = p->next; /* head */
- p->next = NULL; /* cut it */
- while (q) {
- p = q->next;
- q->func(q);
- q = p;
+ work = head;
+ do {
+ next = work->next;
+ work->func(work);
+ work = next;
cond_resched();
- }
+ } while (work);
}
}
diff --git a/kernel/time/tick-sched.c b/kernel/time/tick-sched.c
index 3a9e5d5c1091..f423bdd035c2 100644
--- a/kernel/time/tick-sched.c
+++ b/kernel/time/tick-sched.c
@@ -372,7 +372,7 @@ static ktime_t tick_nohz_stop_sched_tick(struct tick_sched *ts,
* the scheduler tick in nohz_restart_sched_tick.
*/
if (!ts->tick_stopped) {
- select_nohz_load_balancer(1);
+ nohz_balance_enter_idle(cpu);
calc_load_enter_idle();
ts->last_tick = hrtimer_get_expires(&ts->sched_timer);
@@ -436,7 +436,8 @@ static bool can_stop_idle_tick(int cpu, struct tick_sched *ts)
if (unlikely(local_softirq_pending() && cpu_online(cpu))) {
static int ratelimit;
- if (ratelimit < 10) {
+ if (ratelimit < 10 &&
+ (local_softirq_pending() & SOFTIRQ_STOP_IDLE_MASK)) {
printk(KERN_ERR "NOHZ: local_softirq_pending %02x\n",
(unsigned int) local_softirq_pending());
ratelimit++;
@@ -569,7 +570,6 @@ static void tick_nohz_restart(struct tick_sched *ts, ktime_t now)
static void tick_nohz_restart_sched_tick(struct tick_sched *ts, ktime_t now)
{
/* Update jiffies first */
- select_nohz_load_balancer(0);
tick_do_update_jiffies64(now);
update_cpu_load_nohz();
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 34e5eac81424..d3b91e75cecd 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -303,10 +303,11 @@ void getnstimeofday(struct timespec *ts)
seq = read_seqbegin(&tk->lock);
ts->tv_sec = tk->xtime_sec;
- ts->tv_nsec = timekeeping_get_ns(tk);
+ nsecs = timekeeping_get_ns(tk);
} while (read_seqretry(&tk->lock, seq));
+ ts->tv_nsec = 0;
timespec_add_ns(ts, nsecs);
}
EXPORT_SYMBOL(getnstimeofday);
@@ -345,6 +346,7 @@ void ktime_get_ts(struct timespec *ts)
{
struct timekeeper *tk = &timekeeper;
struct timespec tomono;
+ s64 nsec;
unsigned int seq;
WARN_ON(timekeeping_suspended);
@@ -352,13 +354,14 @@ void ktime_get_ts(struct timespec *ts)
do {
seq = read_seqbegin(&tk->lock);
ts->tv_sec = tk->xtime_sec;
- ts->tv_nsec = timekeeping_get_ns(tk);
+ nsec = timekeeping_get_ns(tk);
tomono = tk->wall_to_monotonic;
} while (read_seqretry(&tk->lock, seq));
- set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec,
- ts->tv_nsec + tomono.tv_nsec);
+ ts->tv_sec += tomono.tv_sec;
+ ts->tv_nsec = 0;
+ timespec_add_ns(ts, nsec + tomono.tv_nsec);
}
EXPORT_SYMBOL_GPL(ktime_get_ts);
@@ -1244,6 +1247,7 @@ void get_monotonic_boottime(struct timespec *ts)
{
struct timekeeper *tk = &timekeeper;
struct timespec tomono, sleep;
+ s64 nsec;
unsigned int seq;
WARN_ON(timekeeping_suspended);
@@ -1251,14 +1255,15 @@ void get_monotonic_boottime(struct timespec *ts)
do {
seq = read_seqbegin(&tk->lock);
ts->tv_sec = tk->xtime_sec;
- ts->tv_nsec = timekeeping_get_ns(tk);
+ nsec = timekeeping_get_ns(tk);
tomono = tk->wall_to_monotonic;
sleep = tk->total_sleep_time;
} while (read_seqretry(&tk->lock, seq));
- set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec,
- ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec);
+ ts->tv_sec += tomono.tv_sec + sleep.tv_sec;
+ ts->tv_nsec = 0;
+ timespec_add_ns(ts, nsec + tomono.tv_nsec + sleep.tv_nsec);
}
EXPORT_SYMBOL_GPL(get_monotonic_boottime);
diff --git a/kernel/timer.c b/kernel/timer.c
index 8c5e7b908c68..d5de1b2292aa 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -92,24 +92,25 @@ static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases;
/* Functions below help us manage 'deferrable' flag */
static inline unsigned int tbase_get_deferrable(struct tvec_base *base)
{
- return ((unsigned int)(unsigned long)base & TBASE_DEFERRABLE_FLAG);
+ return ((unsigned int)(unsigned long)base & TIMER_DEFERRABLE);
}
-static inline struct tvec_base *tbase_get_base(struct tvec_base *base)
+static inline unsigned int tbase_get_irqsafe(struct tvec_base *base)
{
- return ((struct tvec_base *)((unsigned long)base & ~TBASE_DEFERRABLE_FLAG));
+ return ((unsigned int)(unsigned long)base & TIMER_IRQSAFE);
}
-static inline void timer_set_deferrable(struct timer_list *timer)
+static inline struct tvec_base *tbase_get_base(struct tvec_base *base)
{
- timer->base = TBASE_MAKE_DEFERRED(timer->base);
+ return ((struct tvec_base *)((unsigned long)base & ~TIMER_FLAG_MASK));
}
static inline void
timer_set_base(struct timer_list *timer, struct tvec_base *new_base)
{
- timer->base = (struct tvec_base *)((unsigned long)(new_base) |
- tbase_get_deferrable(timer->base));
+ unsigned long flags = (unsigned long)timer->base & TIMER_FLAG_MASK;
+
+ timer->base = (struct tvec_base *)((unsigned long)(new_base) | flags);
}
static unsigned long round_jiffies_common(unsigned long j, int cpu,
@@ -563,16 +564,14 @@ static inline void debug_timer_assert_init(struct timer_list *timer)
debug_object_assert_init(timer, &timer_debug_descr);
}
-static void __init_timer(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key);
+static void do_init_timer(struct timer_list *timer, unsigned int flags,
+ const char *name, struct lock_class_key *key);
-void init_timer_on_stack_key(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key)
+void init_timer_on_stack_key(struct timer_list *timer, unsigned int flags,
+ const char *name, struct lock_class_key *key)
{
debug_object_init_on_stack(timer, &timer_debug_descr);
- __init_timer(timer, name, key);
+ do_init_timer(timer, flags, name, key);
}
EXPORT_SYMBOL_GPL(init_timer_on_stack_key);
@@ -613,12 +612,13 @@ static inline void debug_assert_init(struct timer_list *timer)
debug_timer_assert_init(timer);
}
-static void __init_timer(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key)
+static void do_init_timer(struct timer_list *timer, unsigned int flags,
+ const char *name, struct lock_class_key *key)
{
+ struct tvec_base *base = __raw_get_cpu_var(tvec_bases);
+
timer->entry.next = NULL;
- timer->base = __raw_get_cpu_var(tvec_bases);
+ timer->base = (void *)((unsigned long)base | flags);
timer->slack = -1;
#ifdef CONFIG_TIMER_STATS
timer->start_site = NULL;
@@ -628,22 +628,10 @@ static void __init_timer(struct timer_list *timer,
lockdep_init_map(&timer->lockdep_map, name, key, 0);
}
-void setup_deferrable_timer_on_stack_key(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key,
- void (*function)(unsigned long),
- unsigned long data)
-{
- timer->function = function;
- timer->data = data;
- init_timer_on_stack_key(timer, name, key);
- timer_set_deferrable(timer);
-}
-EXPORT_SYMBOL_GPL(setup_deferrable_timer_on_stack_key);
-
/**
* init_timer_key - initialize a timer
* @timer: the timer to be initialized
+ * @flags: timer flags
* @name: name of the timer
* @key: lockdep class key of the fake lock used for tracking timer
* sync lock dependencies
@@ -651,24 +639,14 @@ EXPORT_SYMBOL_GPL(setup_deferrable_timer_on_stack_key);
* init_timer_key() must be done to a timer prior calling *any* of the
* other timer functions.
*/
-void init_timer_key(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key)
+void init_timer_key(struct timer_list *timer, unsigned int flags,
+ const char *name, struct lock_class_key *key)
{
debug_init(timer);
- __init_timer(timer, name, key);
+ do_init_timer(timer, flags, name, key);
}
EXPORT_SYMBOL(init_timer_key);
-void init_timer_deferrable_key(struct timer_list *timer,
- const char *name,
- struct lock_class_key *key)
-{
- init_timer_key(timer, name, key);
- timer_set_deferrable(timer);
-}
-EXPORT_SYMBOL(init_timer_deferrable_key);
-
static inline void detach_timer(struct timer_list *timer, bool clear_pending)
{
struct list_head *entry = &timer->entry;
@@ -686,7 +664,7 @@ detach_expired_timer(struct timer_list *timer, struct tvec_base *base)
{
detach_timer(timer, true);
if (!tbase_get_deferrable(timer->base))
- timer->base->active_timers--;
+ base->active_timers--;
}
static int detach_if_pending(struct timer_list *timer, struct tvec_base *base,
@@ -697,7 +675,7 @@ static int detach_if_pending(struct timer_list *timer, struct tvec_base *base,
detach_timer(timer, clear_pending);
if (!tbase_get_deferrable(timer->base)) {
- timer->base->active_timers--;
+ base->active_timers--;
if (timer->expires == base->next_timer)
base->next_timer = base->timer_jiffies;
}
@@ -1029,14 +1007,14 @@ EXPORT_SYMBOL(try_to_del_timer_sync);
*
* Synchronization rules: Callers must prevent restarting of the timer,
* otherwise this function is meaningless. It must not be called from
- * interrupt contexts. The caller must not hold locks which would prevent
- * completion of the timer's handler. The timer's handler must not call
- * add_timer_on(). Upon exit the timer is not queued and the handler is
- * not running on any CPU.
+ * interrupt contexts unless the timer is an irqsafe one. The caller must
+ * not hold locks which would prevent completion of the timer's
+ * handler. The timer's handler must not call add_timer_on(). Upon exit the
+ * timer is not queued and the handler is not running on any CPU.
*
- * Note: You must not hold locks that are held in interrupt context
- * while calling this function. Even if the lock has nothing to do
- * with the timer in question. Here's why:
+ * Note: For !irqsafe timers, you must not hold locks that are held in
+ * interrupt context while calling this function. Even if the lock has
+ * nothing to do with the timer in question. Here's why:
*
* CPU0 CPU1
* ---- ----
@@ -1073,7 +1051,7 @@ int del_timer_sync(struct timer_list *timer)
* don't use it in hardirq context, because it
* could lead to deadlock.
*/
- WARN_ON(in_irq());
+ WARN_ON(in_irq() && !tbase_get_irqsafe(timer->base));
for (;;) {
int ret = try_to_del_timer_sync(timer);
if (ret >= 0)
@@ -1180,19 +1158,27 @@ static inline void __run_timers(struct tvec_base *base)
while (!list_empty(head)) {
void (*fn)(unsigned long);
unsigned long data;
+ bool irqsafe;
timer = list_first_entry(head, struct timer_list,entry);
fn = timer->function;
data = timer->data;
+ irqsafe = tbase_get_irqsafe(timer->base);
timer_stats_account_timer(timer);
base->running_timer = timer;
detach_expired_timer(timer, base);
- spin_unlock_irq(&base->lock);
- call_timer_fn(timer, fn, data);
- spin_lock_irq(&base->lock);
+ if (irqsafe) {
+ spin_unlock(&base->lock);
+ call_timer_fn(timer, fn, data);
+ spin_lock(&base->lock);
+ } else {
+ spin_unlock_irq(&base->lock);
+ call_timer_fn(timer, fn, data);
+ spin_lock_irq(&base->lock);
+ }
}
}
base->running_timer = NULL;
@@ -1791,9 +1777,13 @@ static struct notifier_block __cpuinitdata timers_nb = {
void __init init_timers(void)
{
- int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
- (void *)(long)smp_processor_id());
+ int err;
+
+ /* ensure there are enough low bits for flags in timer->base pointer */
+ BUILD_BUG_ON(__alignof__(struct tvec_base) & TIMER_FLAG_MASK);
+ err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
+ (void *)(long)smp_processor_id());
init_timer_stats();
BUG_ON(err != NOTIFY_OK);
diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 8c4c07071cc5..4cea4f41c1d9 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -49,6 +49,11 @@ config HAVE_SYSCALL_TRACEPOINTS
help
See Documentation/trace/ftrace-design.txt
+config HAVE_FENTRY
+ bool
+ help
+ Arch supports the gcc options -pg with -mfentry
+
config HAVE_C_RECORDMCOUNT
bool
help
@@ -57,8 +62,12 @@ config HAVE_C_RECORDMCOUNT
config TRACER_MAX_TRACE
bool
+config TRACE_CLOCK
+ bool
+
config RING_BUFFER
bool
+ select TRACE_CLOCK
config FTRACE_NMI_ENTER
bool
@@ -109,6 +118,7 @@ config TRACING
select NOP_TRACER
select BINARY_PRINTF
select EVENT_TRACING
+ select TRACE_CLOCK
config GENERIC_TRACER
bool
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile
index b831087c8200..d7e2068e4b71 100644
--- a/kernel/trace/Makefile
+++ b/kernel/trace/Makefile
@@ -5,10 +5,12 @@ ifdef CONFIG_FUNCTION_TRACER
ORIG_CFLAGS := $(KBUILD_CFLAGS)
KBUILD_CFLAGS = $(subst -pg,,$(ORIG_CFLAGS))
+ifdef CONFIG_FTRACE_SELFTEST
# selftest needs instrumentation
CFLAGS_trace_selftest_dynamic.o = -pg
obj-y += trace_selftest_dynamic.o
endif
+endif
# If unlikely tracing is enabled, do not trace these files
ifdef CONFIG_TRACING_BRANCHES
@@ -17,11 +19,7 @@ endif
CFLAGS_trace_events_filter.o := -I$(src)
-#
-# Make the trace clocks available generally: it's infrastructure
-# relied on by ptrace for example:
-#
-obj-y += trace_clock.o
+obj-$(CONFIG_TRACE_CLOCK) += trace_clock.o
obj-$(CONFIG_FUNCTION_TRACER) += libftrace.o
obj-$(CONFIG_RING_BUFFER) += ring_buffer.o
diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c
index b4f20fba09fc..9dcf15d38380 100644
--- a/kernel/trace/ftrace.c
+++ b/kernel/trace/ftrace.c
@@ -64,12 +64,20 @@
#define FL_GLOBAL_CONTROL_MASK (FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_CONTROL)
+static struct ftrace_ops ftrace_list_end __read_mostly = {
+ .func = ftrace_stub,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE,
+};
+
/* ftrace_enabled is a method to turn ftrace on or off */
int ftrace_enabled __read_mostly;
static int last_ftrace_enabled;
/* Quick disabling of function tracer. */
-int function_trace_stop;
+int function_trace_stop __read_mostly;
+
+/* Current function tracing op */
+struct ftrace_ops *function_trace_op __read_mostly = &ftrace_list_end;
/* List for set_ftrace_pid's pids. */
LIST_HEAD(ftrace_pids);
@@ -86,22 +94,43 @@ static int ftrace_disabled __read_mostly;
static DEFINE_MUTEX(ftrace_lock);
-static struct ftrace_ops ftrace_list_end __read_mostly = {
- .func = ftrace_stub,
-};
-
static struct ftrace_ops *ftrace_global_list __read_mostly = &ftrace_list_end;
static struct ftrace_ops *ftrace_control_list __read_mostly = &ftrace_list_end;
static struct ftrace_ops *ftrace_ops_list __read_mostly = &ftrace_list_end;
ftrace_func_t ftrace_trace_function __read_mostly = ftrace_stub;
-static ftrace_func_t __ftrace_trace_function_delay __read_mostly = ftrace_stub;
-ftrace_func_t __ftrace_trace_function __read_mostly = ftrace_stub;
ftrace_func_t ftrace_pid_function __read_mostly = ftrace_stub;
static struct ftrace_ops global_ops;
static struct ftrace_ops control_ops;
-static void
-ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip);
+#if ARCH_SUPPORTS_FTRACE_OPS
+static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *regs);
+#else
+/* See comment below, where ftrace_ops_list_func is defined */
+static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip);
+#define ftrace_ops_list_func ((ftrace_func_t)ftrace_ops_no_ops)
+#endif
+
+/**
+ * ftrace_nr_registered_ops - return number of ops registered
+ *
+ * Returns the number of ftrace_ops registered and tracing functions
+ */
+int ftrace_nr_registered_ops(void)
+{
+ struct ftrace_ops *ops;
+ int cnt = 0;
+
+ mutex_lock(&ftrace_lock);
+
+ for (ops = ftrace_ops_list;
+ ops != &ftrace_list_end; ops = ops->next)
+ cnt++;
+
+ mutex_unlock(&ftrace_lock);
+
+ return cnt;
+}
/*
* Traverse the ftrace_global_list, invoking all entries. The reason that we
@@ -112,29 +141,29 @@ ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip);
*
* Silly Alpha and silly pointer-speculation compiler optimizations!
*/
-static void ftrace_global_list_func(unsigned long ip,
- unsigned long parent_ip)
+static void
+ftrace_global_list_func(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *regs)
{
- struct ftrace_ops *op;
-
if (unlikely(trace_recursion_test(TRACE_GLOBAL_BIT)))
return;
trace_recursion_set(TRACE_GLOBAL_BIT);
op = rcu_dereference_raw(ftrace_global_list); /*see above*/
while (op != &ftrace_list_end) {
- op->func(ip, parent_ip);
+ op->func(ip, parent_ip, op, regs);
op = rcu_dereference_raw(op->next); /*see above*/
};
trace_recursion_clear(TRACE_GLOBAL_BIT);
}
-static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip)
+static void ftrace_pid_func(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *regs)
{
if (!test_tsk_trace_trace(current))
return;
- ftrace_pid_function(ip, parent_ip);
+ ftrace_pid_function(ip, parent_ip, op, regs);
}
static void set_ftrace_pid_function(ftrace_func_t func)
@@ -153,25 +182,9 @@ static void set_ftrace_pid_function(ftrace_func_t func)
void clear_ftrace_function(void)
{
ftrace_trace_function = ftrace_stub;
- __ftrace_trace_function = ftrace_stub;
- __ftrace_trace_function_delay = ftrace_stub;
ftrace_pid_function = ftrace_stub;
}
-#ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
-/*
- * For those archs that do not test ftrace_trace_stop in their
- * mcount call site, we need to do it from C.
- */
-static void ftrace_test_stop_func(unsigned long ip, unsigned long parent_ip)
-{
- if (function_trace_stop)
- return;
-
- __ftrace_trace_function(ip, parent_ip);
-}
-#endif
-
static void control_ops_disable_all(struct ftrace_ops *ops)
{
int cpu;
@@ -230,28 +243,27 @@ static void update_ftrace_function(void)
/*
* If we are at the end of the list and this ops is
- * not dynamic, then have the mcount trampoline call
- * the function directly
+ * recursion safe and not dynamic and the arch supports passing ops,
+ * then have the mcount trampoline call the function directly.
*/
if (ftrace_ops_list == &ftrace_list_end ||
(ftrace_ops_list->next == &ftrace_list_end &&
- !(ftrace_ops_list->flags & FTRACE_OPS_FL_DYNAMIC)))
+ !(ftrace_ops_list->flags & FTRACE_OPS_FL_DYNAMIC) &&
+ (ftrace_ops_list->flags & FTRACE_OPS_FL_RECURSION_SAFE) &&
+ !FTRACE_FORCE_LIST_FUNC)) {
+ /* Set the ftrace_ops that the arch callback uses */
+ if (ftrace_ops_list == &global_ops)
+ function_trace_op = ftrace_global_list;
+ else
+ function_trace_op = ftrace_ops_list;
func = ftrace_ops_list->func;
- else
+ } else {
+ /* Just use the default ftrace_ops */
+ function_trace_op = &ftrace_list_end;
func = ftrace_ops_list_func;
+ }
-#ifdef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
ftrace_trace_function = func;
-#else
-#ifdef CONFIG_DYNAMIC_FTRACE
- /* do not update till all functions have been modified */
- __ftrace_trace_function_delay = func;
-#else
- __ftrace_trace_function = func;
-#endif
- ftrace_trace_function =
- (func == ftrace_stub) ? func : ftrace_test_stop_func;
-#endif
}
static void add_ftrace_ops(struct ftrace_ops **list, struct ftrace_ops *ops)
@@ -325,6 +337,20 @@ static int __register_ftrace_function(struct ftrace_ops *ops)
if ((ops->flags & FL_GLOBAL_CONTROL_MASK) == FL_GLOBAL_CONTROL_MASK)
return -EINVAL;
+#ifndef ARCH_SUPPORTS_FTRACE_SAVE_REGS
+ /*
+ * If the ftrace_ops specifies SAVE_REGS, then it only can be used
+ * if the arch supports it, or SAVE_REGS_IF_SUPPORTED is also set.
+ * Setting SAVE_REGS_IF_SUPPORTED makes SAVE_REGS irrelevant.
+ */
+ if (ops->flags & FTRACE_OPS_FL_SAVE_REGS &&
+ !(ops->flags & FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED))
+ return -EINVAL;
+
+ if (ops->flags & FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED)
+ ops->flags |= FTRACE_OPS_FL_SAVE_REGS;
+#endif
+
if (!core_kernel_data((unsigned long)ops))
ops->flags |= FTRACE_OPS_FL_DYNAMIC;
@@ -773,7 +799,8 @@ ftrace_profile_alloc(struct ftrace_profile_stat *stat, unsigned long ip)
}
static void
-function_profile_call(unsigned long ip, unsigned long parent_ip)
+function_profile_call(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *ops, struct pt_regs *regs)
{
struct ftrace_profile_stat *stat;
struct ftrace_profile *rec;
@@ -803,7 +830,7 @@ function_profile_call(unsigned long ip, unsigned long parent_ip)
#ifdef CONFIG_FUNCTION_GRAPH_TRACER
static int profile_graph_entry(struct ftrace_graph_ent *trace)
{
- function_profile_call(trace->func, 0);
+ function_profile_call(trace->func, 0, NULL, NULL);
return 1;
}
@@ -863,6 +890,7 @@ static void unregister_ftrace_profiler(void)
#else
static struct ftrace_ops ftrace_profile_ops __read_mostly = {
.func = function_profile_call,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE,
};
static int register_ftrace_profiler(void)
@@ -1045,6 +1073,7 @@ static struct ftrace_ops global_ops = {
.func = ftrace_stub,
.notrace_hash = EMPTY_HASH,
.filter_hash = EMPTY_HASH,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE,
};
static DEFINE_MUTEX(ftrace_regex_lock);
@@ -1525,6 +1554,12 @@ static void __ftrace_hash_rec_update(struct ftrace_ops *ops,
rec->flags++;
if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == FTRACE_REF_MAX))
return;
+ /*
+ * If any ops wants regs saved for this function
+ * then all ops will get saved regs.
+ */
+ if (ops->flags & FTRACE_OPS_FL_SAVE_REGS)
+ rec->flags |= FTRACE_FL_REGS;
} else {
if (FTRACE_WARN_ON((rec->flags & ~FTRACE_FL_MASK) == 0))
return;
@@ -1616,18 +1651,59 @@ static int ftrace_check_record(struct dyn_ftrace *rec, int enable, int update)
if (enable && (rec->flags & ~FTRACE_FL_MASK))
flag = FTRACE_FL_ENABLED;
+ /*
+ * If enabling and the REGS flag does not match the REGS_EN, then
+ * do not ignore this record. Set flags to fail the compare against
+ * ENABLED.
+ */
+ if (flag &&
+ (!(rec->flags & FTRACE_FL_REGS) != !(rec->flags & FTRACE_FL_REGS_EN)))
+ flag |= FTRACE_FL_REGS;
+
/* If the state of this record hasn't changed, then do nothing */
if ((rec->flags & FTRACE_FL_ENABLED) == flag)
return FTRACE_UPDATE_IGNORE;
if (flag) {
- if (update)
+ /* Save off if rec is being enabled (for return value) */
+ flag ^= rec->flags & FTRACE_FL_ENABLED;
+
+ if (update) {
rec->flags |= FTRACE_FL_ENABLED;
- return FTRACE_UPDATE_MAKE_CALL;
+ if (flag & FTRACE_FL_REGS) {
+ if (rec->flags & FTRACE_FL_REGS)
+ rec->flags |= FTRACE_FL_REGS_EN;
+ else
+ rec->flags &= ~FTRACE_FL_REGS_EN;
+ }
+ }
+
+ /*
+ * If this record is being updated from a nop, then
+ * return UPDATE_MAKE_CALL.
+ * Otherwise, if the EN flag is set, then return
+ * UPDATE_MODIFY_CALL_REGS to tell the caller to convert
+ * from the non-save regs, to a save regs function.
+ * Otherwise,
+ * return UPDATE_MODIFY_CALL to tell the caller to convert
+ * from the save regs, to a non-save regs function.
+ */
+ if (flag & FTRACE_FL_ENABLED)
+ return FTRACE_UPDATE_MAKE_CALL;
+ else if (rec->flags & FTRACE_FL_REGS_EN)
+ return FTRACE_UPDATE_MODIFY_CALL_REGS;
+ else
+ return FTRACE_UPDATE_MODIFY_CALL;
}
- if (update)
- rec->flags &= ~FTRACE_FL_ENABLED;
+ if (update) {
+ /* If there's no more users, clear all flags */
+ if (!(rec->flags & ~FTRACE_FL_MASK))
+ rec->flags = 0;
+ else
+ /* Just disable the record (keep REGS state) */
+ rec->flags &= ~FTRACE_FL_ENABLED;
+ }
return FTRACE_UPDATE_MAKE_NOP;
}
@@ -1662,13 +1738,17 @@ int ftrace_test_record(struct dyn_ftrace *rec, int enable)
static int
__ftrace_replace_code(struct dyn_ftrace *rec, int enable)
{
+ unsigned long ftrace_old_addr;
unsigned long ftrace_addr;
int ret;
- ftrace_addr = (unsigned long)FTRACE_ADDR;
-
ret = ftrace_update_record(rec, enable);
+ if (rec->flags & FTRACE_FL_REGS)
+ ftrace_addr = (unsigned long)FTRACE_REGS_ADDR;
+ else
+ ftrace_addr = (unsigned long)FTRACE_ADDR;
+
switch (ret) {
case FTRACE_UPDATE_IGNORE:
return 0;
@@ -1678,6 +1758,15 @@ __ftrace_replace_code(struct dyn_ftrace *rec, int enable)
case FTRACE_UPDATE_MAKE_NOP:
return ftrace_make_nop(NULL, rec, ftrace_addr);
+
+ case FTRACE_UPDATE_MODIFY_CALL_REGS:
+ case FTRACE_UPDATE_MODIFY_CALL:
+ if (rec->flags & FTRACE_FL_REGS)
+ ftrace_old_addr = (unsigned long)FTRACE_ADDR;
+ else
+ ftrace_old_addr = (unsigned long)FTRACE_REGS_ADDR;
+
+ return ftrace_modify_call(rec, ftrace_old_addr, ftrace_addr);
}
return -1; /* unknow ftrace bug */
@@ -1882,16 +1971,6 @@ static void ftrace_run_update_code(int command)
*/
arch_ftrace_update_code(command);
-#ifndef CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST
- /*
- * For archs that call ftrace_test_stop_func(), we must
- * wait till after we update all the function callers
- * before we update the callback. This keeps different
- * ops that record different functions from corrupting
- * each other.
- */
- __ftrace_trace_function = __ftrace_trace_function_delay;
-#endif
function_trace_stop--;
ret = ftrace_arch_code_modify_post_process();
@@ -2441,8 +2520,9 @@ static int t_show(struct seq_file *m, void *v)
seq_printf(m, "%ps", (void *)rec->ip);
if (iter->flags & FTRACE_ITER_ENABLED)
- seq_printf(m, " (%ld)",
- rec->flags & ~FTRACE_FL_MASK);
+ seq_printf(m, " (%ld)%s",
+ rec->flags & ~FTRACE_FL_MASK,
+ rec->flags & FTRACE_FL_REGS ? " R" : "");
seq_printf(m, "\n");
return 0;
@@ -2790,8 +2870,8 @@ static int __init ftrace_mod_cmd_init(void)
}
device_initcall(ftrace_mod_cmd_init);
-static void
-function_trace_probe_call(unsigned long ip, unsigned long parent_ip)
+static void function_trace_probe_call(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *pt_regs)
{
struct ftrace_func_probe *entry;
struct hlist_head *hhd;
@@ -3162,8 +3242,27 @@ ftrace_notrace_write(struct file *file, const char __user *ubuf,
}
static int
-ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
- int reset, int enable)
+ftrace_match_addr(struct ftrace_hash *hash, unsigned long ip, int remove)
+{
+ struct ftrace_func_entry *entry;
+
+ if (!ftrace_location(ip))
+ return -EINVAL;
+
+ if (remove) {
+ entry = ftrace_lookup_ip(hash, ip);
+ if (!entry)
+ return -ENOENT;
+ free_hash_entry(hash, entry);
+ return 0;
+ }
+
+ return add_hash_entry(hash, ip);
+}
+
+static int
+ftrace_set_hash(struct ftrace_ops *ops, unsigned char *buf, int len,
+ unsigned long ip, int remove, int reset, int enable)
{
struct ftrace_hash **orig_hash;
struct ftrace_hash *hash;
@@ -3192,6 +3291,11 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
ret = -EINVAL;
goto out_regex_unlock;
}
+ if (ip) {
+ ret = ftrace_match_addr(hash, ip, remove);
+ if (ret < 0)
+ goto out_regex_unlock;
+ }
mutex_lock(&ftrace_lock);
ret = ftrace_hash_move(ops, enable, orig_hash, hash);
@@ -3208,6 +3312,37 @@ ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
return ret;
}
+static int
+ftrace_set_addr(struct ftrace_ops *ops, unsigned long ip, int remove,
+ int reset, int enable)
+{
+ return ftrace_set_hash(ops, 0, 0, ip, remove, reset, enable);
+}
+
+/**
+ * ftrace_set_filter_ip - set a function to filter on in ftrace by address
+ * @ops - the ops to set the filter with
+ * @ip - the address to add to or remove from the filter.
+ * @remove - non zero to remove the ip from the filter
+ * @reset - non zero to reset all filters before applying this filter.
+ *
+ * Filters denote which functions should be enabled when tracing is enabled
+ * If @ip is NULL, it failes to update filter.
+ */
+int ftrace_set_filter_ip(struct ftrace_ops *ops, unsigned long ip,
+ int remove, int reset)
+{
+ return ftrace_set_addr(ops, ip, remove, reset, 1);
+}
+EXPORT_SYMBOL_GPL(ftrace_set_filter_ip);
+
+static int
+ftrace_set_regex(struct ftrace_ops *ops, unsigned char *buf, int len,
+ int reset, int enable)
+{
+ return ftrace_set_hash(ops, buf, len, 0, 0, reset, enable);
+}
+
/**
* ftrace_set_filter - set a function to filter on in ftrace
* @ops - the ops to set the filter with
@@ -3912,6 +4047,7 @@ void __init ftrace_init(void)
static struct ftrace_ops global_ops = {
.func = ftrace_stub,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE,
};
static int __init ftrace_nodyn_init(void)
@@ -3942,10 +4078,9 @@ ftrace_ops_test(struct ftrace_ops *ops, unsigned long ip)
#endif /* CONFIG_DYNAMIC_FTRACE */
static void
-ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip)
+ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *regs)
{
- struct ftrace_ops *op;
-
if (unlikely(trace_recursion_test(TRACE_CONTROL_BIT)))
return;
@@ -3959,7 +4094,7 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip)
while (op != &ftrace_list_end) {
if (!ftrace_function_local_disabled(op) &&
ftrace_ops_test(op, ip))
- op->func(ip, parent_ip);
+ op->func(ip, parent_ip, op, regs);
op = rcu_dereference_raw(op->next);
};
@@ -3969,13 +4104,18 @@ ftrace_ops_control_func(unsigned long ip, unsigned long parent_ip)
static struct ftrace_ops control_ops = {
.func = ftrace_ops_control_func,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE,
};
-static void
-ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip)
+static inline void
+__ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *ignored, struct pt_regs *regs)
{
struct ftrace_ops *op;
+ if (function_trace_stop)
+ return;
+
if (unlikely(trace_recursion_test(TRACE_INTERNAL_BIT)))
return;
@@ -3988,13 +4128,39 @@ ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip)
op = rcu_dereference_raw(ftrace_ops_list);
while (op != &ftrace_list_end) {
if (ftrace_ops_test(op, ip))
- op->func(ip, parent_ip);
+ op->func(ip, parent_ip, op, regs);
op = rcu_dereference_raw(op->next);
};
preempt_enable_notrace();
trace_recursion_clear(TRACE_INTERNAL_BIT);
}
+/*
+ * Some archs only support passing ip and parent_ip. Even though
+ * the list function ignores the op parameter, we do not want any
+ * C side effects, where a function is called without the caller
+ * sending a third parameter.
+ * Archs are to support both the regs and ftrace_ops at the same time.
+ * If they support ftrace_ops, it is assumed they support regs.
+ * If call backs want to use regs, they must either check for regs
+ * being NULL, or ARCH_SUPPORTS_FTRACE_SAVE_REGS.
+ * Note, ARCH_SUPPORT_SAVE_REGS expects a full regs to be saved.
+ * An architecture can pass partial regs with ftrace_ops and still
+ * set the ARCH_SUPPORT_FTARCE_OPS.
+ */
+#if ARCH_SUPPORTS_FTRACE_OPS
+static void ftrace_ops_list_func(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *regs)
+{
+ __ftrace_ops_list_func(ip, parent_ip, NULL, regs);
+}
+#else
+static void ftrace_ops_no_ops(unsigned long ip, unsigned long parent_ip)
+{
+ __ftrace_ops_list_func(ip, parent_ip, NULL, NULL);
+}
+#endif
+
static void clear_ftrace_swapper(void)
{
struct task_struct *p;
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index 49491fa7daa2..b32ed0e385a5 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -2816,7 +2816,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_record_enable);
* to the buffer after this will fail and return NULL.
*
* This is different than ring_buffer_record_disable() as
- * it works like an on/off switch, where as the disable() verison
+ * it works like an on/off switch, where as the disable() version
* must be paired with a enable().
*/
void ring_buffer_record_off(struct ring_buffer *buffer)
@@ -2839,7 +2839,7 @@ EXPORT_SYMBOL_GPL(ring_buffer_record_off);
* ring_buffer_record_off().
*
* This is different than ring_buffer_record_enable() as
- * it works like an on/off switch, where as the enable() verison
+ * it works like an on/off switch, where as the enable() version
* must be paired with a disable().
*/
void ring_buffer_record_on(struct ring_buffer *buffer)
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index 5c38c81496ce..1ec5c1dab629 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -328,7 +328,7 @@ static DECLARE_WAIT_QUEUE_HEAD(trace_wait);
unsigned long trace_flags = TRACE_ITER_PRINT_PARENT | TRACE_ITER_PRINTK |
TRACE_ITER_ANNOTATE | TRACE_ITER_CONTEXT_INFO | TRACE_ITER_SLEEP_TIME |
TRACE_ITER_GRAPH_TIME | TRACE_ITER_RECORD_CMD | TRACE_ITER_OVERWRITE |
- TRACE_ITER_IRQ_INFO;
+ TRACE_ITER_IRQ_INFO | TRACE_ITER_MARKERS;
static int trace_stop_count;
static DEFINE_RAW_SPINLOCK(tracing_start_lock);
@@ -426,15 +426,15 @@ __setup("trace_buf_size=", set_buf_size);
static int __init set_tracing_thresh(char *str)
{
- unsigned long threshhold;
+ unsigned long threshold;
int ret;
if (!str)
return 0;
- ret = strict_strtoul(str, 0, &threshhold);
+ ret = strict_strtoul(str, 0, &threshold);
if (ret < 0)
return 0;
- tracing_thresh = threshhold * 1000;
+ tracing_thresh = threshold * 1000;
return 1;
}
__setup("tracing_thresh=", set_tracing_thresh);
@@ -470,6 +470,7 @@ static const char *trace_options[] = {
"overwrite",
"disable_on_free",
"irq-info",
+ "markers",
NULL
};
@@ -3886,6 +3887,9 @@ tracing_mark_write(struct file *filp, const char __user *ubuf,
if (tracing_disabled)
return -EINVAL;
+ if (!(trace_flags & TRACE_ITER_MARKERS))
+ return -EINVAL;
+
if (cnt > TRACE_BUF_SIZE)
cnt = TRACE_BUF_SIZE;
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h
index 55e1f7f0db12..63a2da0b9a6e 100644
--- a/kernel/trace/trace.h
+++ b/kernel/trace/trace.h
@@ -472,11 +472,11 @@ extern void trace_find_cmdline(int pid, char comm[]);
#ifdef CONFIG_DYNAMIC_FTRACE
extern unsigned long ftrace_update_tot_cnt;
+#endif
#define DYN_FTRACE_TEST_NAME trace_selftest_dynamic_test_func
extern int DYN_FTRACE_TEST_NAME(void);
#define DYN_FTRACE_TEST_NAME2 trace_selftest_dynamic_test_func2
extern int DYN_FTRACE_TEST_NAME2(void);
-#endif
extern int ring_buffer_expanded;
extern bool tracing_selftest_disabled;
@@ -680,6 +680,7 @@ enum trace_iterator_flags {
TRACE_ITER_OVERWRITE = 0x200000,
TRACE_ITER_STOP_ON_FREE = 0x400000,
TRACE_ITER_IRQ_INFO = 0x800000,
+ TRACE_ITER_MARKERS = 0x1000000,
};
/*
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
index 8a6d2ee2086c..84b1e045faba 100644
--- a/kernel/trace/trace_event_perf.c
+++ b/kernel/trace/trace_event_perf.c
@@ -258,7 +258,8 @@ EXPORT_SYMBOL_GPL(perf_trace_buf_prepare);
#ifdef CONFIG_FUNCTION_TRACER
static void
-perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip)
+perf_ftrace_function_call(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *ops, struct pt_regs *pt_regs)
{
struct ftrace_entry *entry;
struct hlist_head *head;
diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c
index 29111da1d100..d608d09d08c0 100644
--- a/kernel/trace/trace_events.c
+++ b/kernel/trace/trace_events.c
@@ -1199,6 +1199,31 @@ event_create_dir(struct ftrace_event_call *call, struct dentry *d_events,
return 0;
}
+static void event_remove(struct ftrace_event_call *call)
+{
+ ftrace_event_enable_disable(call, 0);
+ if (call->event.funcs)
+ __unregister_ftrace_event(&call->event);
+ list_del(&call->list);
+}
+
+static int event_init(struct ftrace_event_call *call)
+{
+ int ret = 0;
+
+ if (WARN_ON(!call->name))
+ return -EINVAL;
+
+ if (call->class->raw_init) {
+ ret = call->class->raw_init(call);
+ if (ret < 0 && ret != -ENOSYS)
+ pr_warn("Could not initialize trace events/%s\n",
+ call->name);
+ }
+
+ return ret;
+}
+
static int
__trace_add_event_call(struct ftrace_event_call *call, struct module *mod,
const struct file_operations *id,
@@ -1209,19 +1234,9 @@ __trace_add_event_call(struct ftrace_event_call *call, struct module *mod,
struct dentry *d_events;
int ret;
- /* The linker may leave blanks */
- if (!call->name)
- return -EINVAL;
-
- if (call->class->raw_init) {
- ret = call->class->raw_init(call);
- if (ret < 0) {
- if (ret != -ENOSYS)
- pr_warning("Could not initialize trace events/%s\n",
- call->name);
- return ret;
- }
- }
+ ret = event_init(call);
+ if (ret < 0)
+ return ret;
d_events = event_trace_events_dir();
if (!d_events)
@@ -1272,13 +1287,10 @@ static void remove_subsystem_dir(const char *name)
*/
static void __trace_remove_event_call(struct ftrace_event_call *call)
{
- ftrace_event_enable_disable(call, 0);
- if (call->event.funcs)
- __unregister_ftrace_event(&call->event);
- debugfs_remove_recursive(call->dir);
- list_del(&call->list);
+ event_remove(call);
trace_destroy_fields(call);
destroy_preds(call);
+ debugfs_remove_recursive(call->dir);
remove_subsystem_dir(call->class->system);
}
@@ -1450,15 +1462,43 @@ static __init int setup_trace_event(char *str)
}
__setup("trace_event=", setup_trace_event);
+static __init int event_trace_enable(void)
+{
+ struct ftrace_event_call **iter, *call;
+ char *buf = bootup_event_buf;
+ char *token;
+ int ret;
+
+ for_each_event(iter, __start_ftrace_events, __stop_ftrace_events) {
+
+ call = *iter;
+ ret = event_init(call);
+ if (!ret)
+ list_add(&call->list, &ftrace_events);
+ }
+
+ while (true) {
+ token = strsep(&buf, ",");
+
+ if (!token)
+ break;
+ if (!*token)
+ continue;
+
+ ret = ftrace_set_clr_event(token, 1);
+ if (ret)
+ pr_warn("Failed to enable trace event: %s\n", token);
+ }
+ return 0;
+}
+
static __init int event_trace_init(void)
{
- struct ftrace_event_call **call;
+ struct ftrace_event_call *call;
struct dentry *d_tracer;
struct dentry *entry;
struct dentry *d_events;
int ret;
- char *buf = bootup_event_buf;
- char *token;
d_tracer = tracing_init_dentry();
if (!d_tracer)
@@ -1497,24 +1537,19 @@ static __init int event_trace_init(void)
if (trace_define_common_fields())
pr_warning("tracing: Failed to allocate common fields");
- for_each_event(call, __start_ftrace_events, __stop_ftrace_events) {
- __trace_add_event_call(*call, NULL, &ftrace_event_id_fops,
+ /*
+ * Early initialization already enabled ftrace event.
+ * Now it's only necessary to create the event directory.
+ */
+ list_for_each_entry(call, &ftrace_events, list) {
+
+ ret = event_create_dir(call, d_events,
+ &ftrace_event_id_fops,
&ftrace_enable_fops,
&ftrace_event_filter_fops,
&ftrace_event_format_fops);
- }
-
- while (true) {
- token = strsep(&buf, ",");
-
- if (!token)
- break;
- if (!*token)
- continue;
-
- ret = ftrace_set_clr_event(token, 1);
- if (ret)
- pr_warning("Failed to enable trace event: %s\n", token);
+ if (ret < 0)
+ event_remove(call);
}
ret = register_module_notifier(&trace_module_nb);
@@ -1523,6 +1558,7 @@ static __init int event_trace_init(void)
return 0;
}
+core_initcall(event_trace_enable);
fs_initcall(event_trace_init);
#ifdef CONFIG_FTRACE_STARTUP_TEST
@@ -1646,9 +1682,11 @@ static __init void event_trace_self_tests(void)
event_test_stuff();
ret = __ftrace_set_clr_event(NULL, system->name, NULL, 0);
- if (WARN_ON_ONCE(ret))
+ if (WARN_ON_ONCE(ret)) {
pr_warning("error disabling system %s\n",
system->name);
+ continue;
+ }
pr_cont("OK\n");
}
@@ -1681,7 +1719,8 @@ static __init void event_trace_self_tests(void)
static DEFINE_PER_CPU(atomic_t, ftrace_test_event_disable);
static void
-function_test_events_call(unsigned long ip, unsigned long parent_ip)
+function_test_events_call(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *pt_regs)
{
struct ring_buffer_event *event;
struct ring_buffer *buffer;
@@ -1720,6 +1759,7 @@ function_test_events_call(unsigned long ip, unsigned long parent_ip)
static struct ftrace_ops trace_ops __initdata =
{
.func = function_test_events_call,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE,
};
static __init void event_trace_self_test_with_function(void)
diff --git a/kernel/trace/trace_events_filter.c b/kernel/trace/trace_events_filter.c
index 431dba8b7542..c154797a7ff7 100644
--- a/kernel/trace/trace_events_filter.c
+++ b/kernel/trace/trace_events_filter.c
@@ -2002,7 +2002,7 @@ static int ftrace_function_set_regexp(struct ftrace_ops *ops, int filter,
static int __ftrace_function_set_filter(int filter, char *buf, int len,
struct function_filter_data *data)
{
- int i, re_cnt, ret;
+ int i, re_cnt, ret = -EINVAL;
int *reset;
char **re;
diff --git a/kernel/trace/trace_functions.c b/kernel/trace/trace_functions.c
index a426f410c060..483162a9f908 100644
--- a/kernel/trace/trace_functions.c
+++ b/kernel/trace/trace_functions.c
@@ -49,7 +49,8 @@ static void function_trace_start(struct trace_array *tr)
}
static void
-function_trace_call_preempt_only(unsigned long ip, unsigned long parent_ip)
+function_trace_call_preempt_only(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_cpu *data;
@@ -84,7 +85,9 @@ enum {
static struct tracer_flags func_flags;
static void
-function_trace_call(unsigned long ip, unsigned long parent_ip)
+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_cpu *data;
@@ -121,7 +124,8 @@ function_trace_call(unsigned long ip, unsigned long parent_ip)
}
static void
-function_stack_trace_call(unsigned long ip, unsigned long parent_ip)
+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_cpu *data;
@@ -164,13 +168,13 @@ function_stack_trace_call(unsigned long ip, unsigned long parent_ip)
static struct ftrace_ops trace_ops __read_mostly =
{
.func = function_trace_call,
- .flags = FTRACE_OPS_FL_GLOBAL,
+ .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE,
};
static struct ftrace_ops trace_stack_ops __read_mostly =
{
.func = function_stack_trace_call,
- .flags = FTRACE_OPS_FL_GLOBAL,
+ .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE,
};
static struct tracer_opt func_opts[] = {
diff --git a/kernel/trace/trace_functions_graph.c b/kernel/trace/trace_functions_graph.c
index ce27c8ba8d31..99b4378393d5 100644
--- a/kernel/trace/trace_functions_graph.c
+++ b/kernel/trace/trace_functions_graph.c
@@ -143,7 +143,7 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret,
return;
}
-#ifdef CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST
+#if defined(CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST) && !defined(CC_USING_FENTRY)
/*
* The arch may choose to record the frame pointer used
* and check it here to make sure that it is what we expect it
@@ -154,6 +154,9 @@ ftrace_pop_return_trace(struct ftrace_graph_ret *trace, unsigned long *ret,
*
* Currently, x86_32 with optimize for size (-Os) makes the latest
* gcc do the above.
+ *
+ * Note, -mfentry does not use frame pointers, and this test
+ * is not needed if CC_USING_FENTRY is set.
*/
if (unlikely(current->ret_stack[index].fp != frame_pointer)) {
ftrace_graph_stop();
diff --git a/kernel/trace/trace_irqsoff.c b/kernel/trace/trace_irqsoff.c
index 99d20e920368..d98ee8283b29 100644
--- a/kernel/trace/trace_irqsoff.c
+++ b/kernel/trace/trace_irqsoff.c
@@ -136,7 +136,8 @@ static int func_prolog_dec(struct trace_array *tr,
* irqsoff uses its own tracer function to keep the overhead down:
*/
static void
-irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip)
+irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *pt_regs)
{
struct trace_array *tr = irqsoff_trace;
struct trace_array_cpu *data;
@@ -153,7 +154,7 @@ irqsoff_tracer_call(unsigned long ip, unsigned long parent_ip)
static struct ftrace_ops trace_ops __read_mostly =
{
.func = irqsoff_tracer_call,
- .flags = FTRACE_OPS_FL_GLOBAL,
+ .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE,
};
#endif /* CONFIG_FUNCTION_TRACER */
diff --git a/kernel/trace/trace_sched_wakeup.c b/kernel/trace/trace_sched_wakeup.c
index ff791ea48b57..02170c00c413 100644
--- a/kernel/trace/trace_sched_wakeup.c
+++ b/kernel/trace/trace_sched_wakeup.c
@@ -108,7 +108,8 @@ out_enable:
* wakeup uses its own tracer function to keep the overhead down:
*/
static void
-wakeup_tracer_call(unsigned long ip, unsigned long parent_ip)
+wakeup_tracer_call(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *pt_regs)
{
struct trace_array *tr = wakeup_trace;
struct trace_array_cpu *data;
@@ -129,7 +130,7 @@ wakeup_tracer_call(unsigned long ip, unsigned long parent_ip)
static struct ftrace_ops trace_ops __read_mostly =
{
.func = wakeup_tracer_call,
- .flags = FTRACE_OPS_FL_GLOBAL,
+ .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE,
};
#endif /* CONFIG_FUNCTION_TRACER */
diff --git a/kernel/trace/trace_selftest.c b/kernel/trace/trace_selftest.c
index 288541f977fb..2c00a691a540 100644
--- a/kernel/trace/trace_selftest.c
+++ b/kernel/trace/trace_selftest.c
@@ -103,54 +103,67 @@ static inline void warn_failed_init_tracer(struct tracer *trace, int init_ret)
static int trace_selftest_test_probe1_cnt;
static void trace_selftest_test_probe1_func(unsigned long ip,
- unsigned long pip)
+ unsigned long pip,
+ struct ftrace_ops *op,
+ struct pt_regs *pt_regs)
{
trace_selftest_test_probe1_cnt++;
}
static int trace_selftest_test_probe2_cnt;
static void trace_selftest_test_probe2_func(unsigned long ip,
- unsigned long pip)
+ unsigned long pip,
+ struct ftrace_ops *op,
+ struct pt_regs *pt_regs)
{
trace_selftest_test_probe2_cnt++;
}
static int trace_selftest_test_probe3_cnt;
static void trace_selftest_test_probe3_func(unsigned long ip,
- unsigned long pip)
+ unsigned long pip,
+ struct ftrace_ops *op,
+ struct pt_regs *pt_regs)
{
trace_selftest_test_probe3_cnt++;
}
static int trace_selftest_test_global_cnt;
static void trace_selftest_test_global_func(unsigned long ip,
- unsigned long pip)
+ unsigned long pip,
+ struct ftrace_ops *op,
+ struct pt_regs *pt_regs)
{
trace_selftest_test_global_cnt++;
}
static int trace_selftest_test_dyn_cnt;
static void trace_selftest_test_dyn_func(unsigned long ip,
- unsigned long pip)
+ unsigned long pip,
+ struct ftrace_ops *op,
+ struct pt_regs *pt_regs)
{
trace_selftest_test_dyn_cnt++;
}
static struct ftrace_ops test_probe1 = {
.func = trace_selftest_test_probe1_func,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE,
};
static struct ftrace_ops test_probe2 = {
.func = trace_selftest_test_probe2_func,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE,
};
static struct ftrace_ops test_probe3 = {
.func = trace_selftest_test_probe3_func,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE,
};
static struct ftrace_ops test_global = {
- .func = trace_selftest_test_global_func,
- .flags = FTRACE_OPS_FL_GLOBAL,
+ .func = trace_selftest_test_global_func,
+ .flags = FTRACE_OPS_FL_GLOBAL | FTRACE_OPS_FL_RECURSION_SAFE,
};
static void print_counts(void)
@@ -393,10 +406,253 @@ int trace_selftest_startup_dynamic_tracing(struct tracer *trace,
return ret;
}
+
+static int trace_selftest_recursion_cnt;
+static void trace_selftest_test_recursion_func(unsigned long ip,
+ unsigned long pip,
+ struct ftrace_ops *op,
+ struct pt_regs *pt_regs)
+{
+ /*
+ * This function is registered without the recursion safe flag.
+ * The ftrace infrastructure should provide the recursion
+ * protection. If not, this will crash the kernel!
+ */
+ trace_selftest_recursion_cnt++;
+ DYN_FTRACE_TEST_NAME();
+}
+
+static void trace_selftest_test_recursion_safe_func(unsigned long ip,
+ unsigned long pip,
+ struct ftrace_ops *op,
+ struct pt_regs *pt_regs)
+{
+ /*
+ * We said we would provide our own recursion. By calling
+ * this function again, we should recurse back into this function
+ * and count again. But this only happens if the arch supports
+ * all of ftrace features and nothing else is using the function
+ * tracing utility.
+ */
+ if (trace_selftest_recursion_cnt++)
+ return;
+ DYN_FTRACE_TEST_NAME();
+}
+
+static struct ftrace_ops test_rec_probe = {
+ .func = trace_selftest_test_recursion_func,
+};
+
+static struct ftrace_ops test_recsafe_probe = {
+ .func = trace_selftest_test_recursion_safe_func,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE,
+};
+
+static int
+trace_selftest_function_recursion(void)
+{
+ int save_ftrace_enabled = ftrace_enabled;
+ int save_tracer_enabled = tracer_enabled;
+ char *func_name;
+ int len;
+ int ret;
+ int cnt;
+
+ /* The previous test PASSED */
+ pr_cont("PASSED\n");
+ pr_info("Testing ftrace recursion: ");
+
+
+ /* enable tracing, and record the filter function */
+ ftrace_enabled = 1;
+ tracer_enabled = 1;
+
+ /* Handle PPC64 '.' name */
+ func_name = "*" __stringify(DYN_FTRACE_TEST_NAME);
+ len = strlen(func_name);
+
+ ret = ftrace_set_filter(&test_rec_probe, func_name, len, 1);
+ if (ret) {
+ pr_cont("*Could not set filter* ");
+ goto out;
+ }
+
+ ret = register_ftrace_function(&test_rec_probe);
+ if (ret) {
+ pr_cont("*could not register callback* ");
+ goto out;
+ }
+
+ DYN_FTRACE_TEST_NAME();
+
+ unregister_ftrace_function(&test_rec_probe);
+
+ ret = -1;
+ if (trace_selftest_recursion_cnt != 1) {
+ pr_cont("*callback not called once (%d)* ",
+ trace_selftest_recursion_cnt);
+ goto out;
+ }
+
+ trace_selftest_recursion_cnt = 1;
+
+ pr_cont("PASSED\n");
+ pr_info("Testing ftrace recursion safe: ");
+
+ ret = ftrace_set_filter(&test_recsafe_probe, func_name, len, 1);
+ if (ret) {
+ pr_cont("*Could not set filter* ");
+ goto out;
+ }
+
+ ret = register_ftrace_function(&test_recsafe_probe);
+ if (ret) {
+ pr_cont("*could not register callback* ");
+ goto out;
+ }
+
+ DYN_FTRACE_TEST_NAME();
+
+ unregister_ftrace_function(&test_recsafe_probe);
+
+ /*
+ * If arch supports all ftrace features, and no other task
+ * was on the list, we should be fine.
+ */
+ if (!ftrace_nr_registered_ops() && !FTRACE_FORCE_LIST_FUNC)
+ cnt = 2; /* Should have recursed */
+ else
+ cnt = 1;
+
+ ret = -1;
+ if (trace_selftest_recursion_cnt != cnt) {
+ pr_cont("*callback not called expected %d times (%d)* ",
+ cnt, trace_selftest_recursion_cnt);
+ goto out;
+ }
+
+ ret = 0;
+out:
+ ftrace_enabled = save_ftrace_enabled;
+ tracer_enabled = save_tracer_enabled;
+
+ return ret;
+}
#else
# define trace_selftest_startup_dynamic_tracing(trace, tr, func) ({ 0; })
+# define trace_selftest_function_recursion() ({ 0; })
#endif /* CONFIG_DYNAMIC_FTRACE */
+static enum {
+ TRACE_SELFTEST_REGS_START,
+ TRACE_SELFTEST_REGS_FOUND,
+ TRACE_SELFTEST_REGS_NOT_FOUND,
+} trace_selftest_regs_stat;
+
+static void trace_selftest_test_regs_func(unsigned long ip,
+ unsigned long pip,
+ struct ftrace_ops *op,
+ struct pt_regs *pt_regs)
+{
+ if (pt_regs)
+ trace_selftest_regs_stat = TRACE_SELFTEST_REGS_FOUND;
+ else
+ trace_selftest_regs_stat = TRACE_SELFTEST_REGS_NOT_FOUND;
+}
+
+static struct ftrace_ops test_regs_probe = {
+ .func = trace_selftest_test_regs_func,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE | FTRACE_OPS_FL_SAVE_REGS,
+};
+
+static int
+trace_selftest_function_regs(void)
+{
+ int save_ftrace_enabled = ftrace_enabled;
+ int save_tracer_enabled = tracer_enabled;
+ char *func_name;
+ int len;
+ int ret;
+ int supported = 0;
+
+#ifdef ARCH_SUPPORTS_FTRACE_SAVE_REGS
+ supported = 1;
+#endif
+
+ /* The previous test PASSED */
+ pr_cont("PASSED\n");
+ pr_info("Testing ftrace regs%s: ",
+ !supported ? "(no arch support)" : "");
+
+ /* enable tracing, and record the filter function */
+ ftrace_enabled = 1;
+ tracer_enabled = 1;
+
+ /* Handle PPC64 '.' name */
+ func_name = "*" __stringify(DYN_FTRACE_TEST_NAME);
+ len = strlen(func_name);
+
+ ret = ftrace_set_filter(&test_regs_probe, func_name, len, 1);
+ /*
+ * If DYNAMIC_FTRACE is not set, then we just trace all functions.
+ * This test really doesn't care.
+ */
+ if (ret && ret != -ENODEV) {
+ pr_cont("*Could not set filter* ");
+ goto out;
+ }
+
+ ret = register_ftrace_function(&test_regs_probe);
+ /*
+ * Now if the arch does not support passing regs, then this should
+ * have failed.
+ */
+ if (!supported) {
+ if (!ret) {
+ pr_cont("*registered save-regs without arch support* ");
+ goto out;
+ }
+ test_regs_probe.flags |= FTRACE_OPS_FL_SAVE_REGS_IF_SUPPORTED;
+ ret = register_ftrace_function(&test_regs_probe);
+ }
+ if (ret) {
+ pr_cont("*could not register callback* ");
+ goto out;
+ }
+
+
+ DYN_FTRACE_TEST_NAME();
+
+ unregister_ftrace_function(&test_regs_probe);
+
+ ret = -1;
+
+ switch (trace_selftest_regs_stat) {
+ case TRACE_SELFTEST_REGS_START:
+ pr_cont("*callback never called* ");
+ goto out;
+
+ case TRACE_SELFTEST_REGS_FOUND:
+ if (supported)
+ break;
+ pr_cont("*callback received regs without arch support* ");
+ goto out;
+
+ case TRACE_SELFTEST_REGS_NOT_FOUND:
+ if (!supported)
+ break;
+ pr_cont("*callback received NULL regs* ");
+ goto out;
+ }
+
+ ret = 0;
+out:
+ ftrace_enabled = save_ftrace_enabled;
+ tracer_enabled = save_tracer_enabled;
+
+ return ret;
+}
+
/*
* Simple verification test of ftrace function tracer.
* Enable ftrace, sleep 1/10 second, and then read the trace
@@ -442,7 +698,14 @@ trace_selftest_startup_function(struct tracer *trace, struct trace_array *tr)
ret = trace_selftest_startup_dynamic_tracing(trace, tr,
DYN_FTRACE_TEST_NAME);
+ if (ret)
+ goto out;
+ ret = trace_selftest_function_recursion();
+ if (ret)
+ goto out;
+
+ ret = trace_selftest_function_regs();
out:
ftrace_enabled = save_ftrace_enabled;
tracer_enabled = save_tracer_enabled;
@@ -778,6 +1041,8 @@ static int trace_wakeup_test_thread(void *data)
set_current_state(TASK_INTERRUPTIBLE);
schedule();
+ complete(x);
+
/* we are awake, now wait to disappear */
while (!kthread_should_stop()) {
/*
@@ -821,24 +1086,21 @@ trace_selftest_startup_wakeup(struct tracer *trace, struct trace_array *tr)
/* reset the max latency */
tracing_max_latency = 0;
- /* sleep to let the RT thread sleep too */
- msleep(100);
+ while (p->on_rq) {
+ /*
+ * Sleep to make sure the RT thread is asleep too.
+ * On virtual machines we can't rely on timings,
+ * but we want to make sure this test still works.
+ */
+ msleep(100);
+ }
- /*
- * Yes this is slightly racy. It is possible that for some
- * strange reason that the RT thread we created, did not
- * call schedule for 100ms after doing the completion,
- * and we do a wakeup on a task that already is awake.
- * But that is extremely unlikely, and the worst thing that
- * happens in such a case, is that we disable tracing.
- * Honestly, if this race does happen something is horrible
- * wrong with the system.
- */
+ init_completion(&isrt);
wake_up_process(p);
- /* give a little time to let the thread wake up */
- msleep(100);
+ /* Wait for the task to wake up */
+ wait_for_completion(&isrt);
/* stop the tracing. */
tracing_stop();
diff --git a/kernel/trace/trace_stack.c b/kernel/trace/trace_stack.c
index d4545f49242e..0c1b165778e5 100644
--- a/kernel/trace/trace_stack.c
+++ b/kernel/trace/trace_stack.c
@@ -111,7 +111,8 @@ static inline void check_stack(void)
}
static void
-stack_trace_call(unsigned long ip, unsigned long parent_ip)
+stack_trace_call(unsigned long ip, unsigned long parent_ip,
+ struct ftrace_ops *op, struct pt_regs *pt_regs)
{
int cpu;
@@ -136,6 +137,7 @@ stack_trace_call(unsigned long ip, unsigned long parent_ip)
static struct ftrace_ops trace_ops __read_mostly =
{
.func = stack_trace_call,
+ .flags = FTRACE_OPS_FL_RECURSION_SAFE,
};
static ssize_t
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c
index 6b245f64c8dd..2485a7d09b11 100644
--- a/kernel/trace/trace_syscalls.c
+++ b/kernel/trace/trace_syscalls.c
@@ -487,7 +487,7 @@ int __init init_ftrace_syscalls(void)
return 0;
}
-core_initcall(init_ftrace_syscalls);
+early_initcall(init_ftrace_syscalls);
#ifdef CONFIG_PERF_EVENTS
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 4b1dfba70f7c..9d4c8d5a1f53 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -22,6 +22,7 @@
#include <linux/notifier.h>
#include <linux/module.h>
#include <linux/sysctl.h>
+#include <linux/smpboot.h>
#include <asm/irq_regs.h>
#include <linux/kvm_para.h>
@@ -29,16 +30,18 @@
int watchdog_enabled = 1;
int __read_mostly watchdog_thresh = 10;
+static int __read_mostly watchdog_disabled;
static DEFINE_PER_CPU(unsigned long, watchdog_touch_ts);
static DEFINE_PER_CPU(struct task_struct *, softlockup_watchdog);
static DEFINE_PER_CPU(struct hrtimer, watchdog_hrtimer);
static DEFINE_PER_CPU(bool, softlockup_touch_sync);
static DEFINE_PER_CPU(bool, soft_watchdog_warn);
+static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts);
+static DEFINE_PER_CPU(unsigned long, soft_lockup_hrtimer_cnt);
#ifdef CONFIG_HARDLOCKUP_DETECTOR
static DEFINE_PER_CPU(bool, hard_watchdog_warn);
static DEFINE_PER_CPU(bool, watchdog_nmi_touch);
-static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts);
static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts_saved);
static DEFINE_PER_CPU(struct perf_event *, watchdog_ev);
#endif
@@ -248,13 +251,15 @@ static void watchdog_overflow_callback(struct perf_event *event,
__this_cpu_write(hard_watchdog_warn, false);
return;
}
+#endif /* CONFIG_HARDLOCKUP_DETECTOR */
+
static void watchdog_interrupt_count(void)
{
__this_cpu_inc(hrtimer_interrupts);
}
-#else
-static inline void watchdog_interrupt_count(void) { return; }
-#endif /* CONFIG_HARDLOCKUP_DETECTOR */
+
+static int watchdog_nmi_enable(unsigned int cpu);
+static void watchdog_nmi_disable(unsigned int cpu);
/* watchdog kicker functions */
static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
@@ -327,49 +332,68 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
return HRTIMER_RESTART;
}
+static void watchdog_set_prio(unsigned int policy, unsigned int prio)
+{
+ struct sched_param param = { .sched_priority = prio };
-/*
- * The watchdog thread - touches the timestamp.
- */
-static int watchdog(void *unused)
+ sched_setscheduler(current, policy, &param);
+}
+
+static void watchdog_enable(unsigned int cpu)
{
- struct sched_param param = { .sched_priority = 0 };
struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer);
- /* initialize timestamp */
- __touch_watchdog();
+ if (!watchdog_enabled) {
+ kthread_park(current);
+ return;
+ }
+
+ /* Enable the perf event */
+ watchdog_nmi_enable(cpu);
/* kick off the timer for the hardlockup detector */
+ hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ hrtimer->function = watchdog_timer_fn;
+
/* done here because hrtimer_start can only pin to smp_processor_id() */
hrtimer_start(hrtimer, ns_to_ktime(get_sample_period()),
HRTIMER_MODE_REL_PINNED);
- set_current_state(TASK_INTERRUPTIBLE);
- /*
- * Run briefly (kicked by the hrtimer callback function) once every
- * get_sample_period() seconds (4 seconds by default) to reset the
- * softlockup timestamp. If this gets delayed for more than
- * 2*watchdog_thresh seconds then the debug-printout triggers in
- * watchdog_timer_fn().
- */
- while (!kthread_should_stop()) {
- __touch_watchdog();
- schedule();
+ /* initialize timestamp */
+ watchdog_set_prio(SCHED_FIFO, MAX_RT_PRIO - 1);
+ __touch_watchdog();
+}
- if (kthread_should_stop())
- break;
+static void watchdog_disable(unsigned int cpu)
+{
+ struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer);
- set_current_state(TASK_INTERRUPTIBLE);
- }
- /*
- * Drop the policy/priority elevation during thread exit to avoid a
- * scheduling latency spike.
- */
- __set_current_state(TASK_RUNNING);
- sched_setscheduler(current, SCHED_NORMAL, &param);
- return 0;
+ watchdog_set_prio(SCHED_NORMAL, 0);
+ hrtimer_cancel(hrtimer);
+ /* disable the perf event */
+ watchdog_nmi_disable(cpu);
}
+static int watchdog_should_run(unsigned int cpu)
+{
+ return __this_cpu_read(hrtimer_interrupts) !=
+ __this_cpu_read(soft_lockup_hrtimer_cnt);
+}
+
+/*
+ * The watchdog thread function - touches the timestamp.
+ *
+ * It only runs once every get_sample_period() seconds (4 seconds by
+ * default) to reset the softlockup timestamp. If this gets delayed
+ * for more than 2*watchdog_thresh seconds then the debug-printout
+ * triggers in watchdog_timer_fn().
+ */
+static void watchdog(unsigned int cpu)
+{
+ __this_cpu_write(soft_lockup_hrtimer_cnt,
+ __this_cpu_read(hrtimer_interrupts));
+ __touch_watchdog();
+}
#ifdef CONFIG_HARDLOCKUP_DETECTOR
/*
@@ -379,7 +403,7 @@ static int watchdog(void *unused)
*/
static unsigned long cpu0_err;
-static int watchdog_nmi_enable(int cpu)
+static int watchdog_nmi_enable(unsigned int cpu)
{
struct perf_event_attr *wd_attr;
struct perf_event *event = per_cpu(watchdog_ev, cpu);
@@ -433,7 +457,7 @@ out:
return 0;
}
-static void watchdog_nmi_disable(int cpu)
+static void watchdog_nmi_disable(unsigned int cpu)
{
struct perf_event *event = per_cpu(watchdog_ev, cpu);
@@ -447,107 +471,35 @@ static void watchdog_nmi_disable(int cpu)
return;
}
#else
-static int watchdog_nmi_enable(int cpu) { return 0; }
-static void watchdog_nmi_disable(int cpu) { return; }
+static int watchdog_nmi_enable(unsigned int cpu) { return 0; }
+static void watchdog_nmi_disable(unsigned int cpu) { return; }
#endif /* CONFIG_HARDLOCKUP_DETECTOR */
/* prepare/enable/disable routines */
-static void watchdog_prepare_cpu(int cpu)
-{
- struct hrtimer *hrtimer = &per_cpu(watchdog_hrtimer, cpu);
-
- WARN_ON(per_cpu(softlockup_watchdog, cpu));
- hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- hrtimer->function = watchdog_timer_fn;
-}
-
-static int watchdog_enable(int cpu)
-{
- struct task_struct *p = per_cpu(softlockup_watchdog, cpu);
- int err = 0;
-
- /* enable the perf event */
- err = watchdog_nmi_enable(cpu);
-
- /* Regardless of err above, fall through and start softlockup */
-
- /* create the watchdog thread */
- if (!p) {
- struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
- p = kthread_create_on_node(watchdog, NULL, cpu_to_node(cpu), "watchdog/%d", cpu);
- if (IS_ERR(p)) {
- pr_err("softlockup watchdog for %i failed\n", cpu);
- if (!err) {
- /* if hardlockup hasn't already set this */
- err = PTR_ERR(p);
- /* and disable the perf event */
- watchdog_nmi_disable(cpu);
- }
- goto out;
- }
- sched_setscheduler(p, SCHED_FIFO, &param);
- kthread_bind(p, cpu);
- per_cpu(watchdog_touch_ts, cpu) = 0;
- per_cpu(softlockup_watchdog, cpu) = p;
- wake_up_process(p);
- }
-
-out:
- return err;
-}
-
-static void watchdog_disable(int cpu)
-{
- struct task_struct *p = per_cpu(softlockup_watchdog, cpu);
- struct hrtimer *hrtimer = &per_cpu(watchdog_hrtimer, cpu);
-
- /*
- * cancel the timer first to stop incrementing the stats
- * and waking up the kthread
- */
- hrtimer_cancel(hrtimer);
-
- /* disable the perf event */
- watchdog_nmi_disable(cpu);
-
- /* stop the watchdog thread */
- if (p) {
- per_cpu(softlockup_watchdog, cpu) = NULL;
- kthread_stop(p);
- }
-}
-
/* sysctl functions */
#ifdef CONFIG_SYSCTL
static void watchdog_enable_all_cpus(void)
{
- int cpu;
-
- watchdog_enabled = 0;
-
- for_each_online_cpu(cpu)
- if (!watchdog_enable(cpu))
- /* if any cpu succeeds, watchdog is considered
- enabled for the system */
- watchdog_enabled = 1;
-
- if (!watchdog_enabled)
- pr_err("failed to be enabled on some cpus\n");
+ unsigned int cpu;
+ if (watchdog_disabled) {
+ watchdog_disabled = 0;
+ for_each_online_cpu(cpu)
+ kthread_unpark(per_cpu(softlockup_watchdog, cpu));
+ }
}
static void watchdog_disable_all_cpus(void)
{
- int cpu;
-
- for_each_online_cpu(cpu)
- watchdog_disable(cpu);
+ unsigned int cpu;
- /* if all watchdogs are disabled, then they are disabled for the system */
- watchdog_enabled = 0;
+ if (!watchdog_disabled) {
+ watchdog_disabled = 1;
+ for_each_online_cpu(cpu)
+ kthread_park(per_cpu(softlockup_watchdog, cpu));
+ }
}
-
/*
* proc handler for /proc/sys/kernel/nmi_watchdog,watchdog_thresh
*/
@@ -557,73 +509,36 @@ int proc_dowatchdog(struct ctl_table *table, int write,
{
int ret;
+ if (watchdog_disabled < 0)
+ return -ENODEV;
+
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
if (ret || !write)
- goto out;
+ return ret;
if (watchdog_enabled && watchdog_thresh)
watchdog_enable_all_cpus();
else
watchdog_disable_all_cpus();
-out:
return ret;
}
#endif /* CONFIG_SYSCTL */
-
-/*
- * Create/destroy watchdog threads as CPUs come and go:
- */
-static int __cpuinit
-cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
-{
- int hotcpu = (unsigned long)hcpu;
-
- switch (action) {
- case CPU_UP_PREPARE:
- case CPU_UP_PREPARE_FROZEN:
- watchdog_prepare_cpu(hotcpu);
- break;
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- if (watchdog_enabled)
- watchdog_enable(hotcpu);
- break;
-#ifdef CONFIG_HOTPLUG_CPU
- case CPU_UP_CANCELED:
- case CPU_UP_CANCELED_FROZEN:
- watchdog_disable(hotcpu);
- break;
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- watchdog_disable(hotcpu);
- break;
-#endif /* CONFIG_HOTPLUG_CPU */
- }
-
- /*
- * hardlockup and softlockup are not important enough
- * to block cpu bring up. Just always succeed and
- * rely on printk output to flag problems.
- */
- return NOTIFY_OK;
-}
-
-static struct notifier_block __cpuinitdata cpu_nfb = {
- .notifier_call = cpu_callback
+static struct smp_hotplug_thread watchdog_threads = {
+ .store = &softlockup_watchdog,
+ .thread_should_run = watchdog_should_run,
+ .thread_fn = watchdog,
+ .thread_comm = "watchdog/%u",
+ .setup = watchdog_enable,
+ .park = watchdog_disable,
+ .unpark = watchdog_enable,
};
void __init lockup_detector_init(void)
{
- void *cpu = (void *)(long)smp_processor_id();
- int err;
-
- err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);
- WARN_ON(notifier_to_errno(err));
-
- cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);
- register_cpu_notifier(&cpu_nfb);
-
- return;
+ if (smpboot_register_percpu_thread(&watchdog_threads)) {
+ pr_err("Failed to create watchdog threads, disabled\n");
+ watchdog_disabled = -ENODEV;
+ }
}
diff --git a/kernel/workqueue.c b/kernel/workqueue.c
index 1e1373bcb3e3..3c5a79e2134c 100644
--- a/kernel/workqueue.c
+++ b/kernel/workqueue.c
@@ -1349,8 +1349,16 @@ static void busy_worker_rebind_fn(struct work_struct *work)
struct worker *worker = container_of(work, struct worker, rebind_work);
struct global_cwq *gcwq = worker->pool->gcwq;
- if (worker_maybe_bind_and_lock(worker))
- worker_clr_flags(worker, WORKER_REBIND);
+ worker_maybe_bind_and_lock(worker);
+
+ /*
+ * %WORKER_REBIND must be cleared even if the above binding failed;
+ * otherwise, we may confuse the next CPU_UP cycle or oops / get
+ * stuck by calling idle_worker_rebind() prematurely. If CPU went
+ * down again inbetween, %WORKER_UNBOUND would be set, so clearing
+ * %WORKER_REBIND is always safe.
+ */
+ worker_clr_flags(worker, WORKER_REBIND);
spin_unlock_irq(&gcwq->lock);
}
@@ -3568,18 +3576,17 @@ static int __devinit workqueue_cpu_down_callback(struct notifier_block *nfb,
#ifdef CONFIG_SMP
struct work_for_cpu {
- struct completion completion;
+ struct work_struct work;
long (*fn)(void *);
void *arg;
long ret;
};
-static int do_work_for_cpu(void *_wfc)
+static void work_for_cpu_fn(struct work_struct *work)
{
- struct work_for_cpu *wfc = _wfc;
+ struct work_for_cpu *wfc = container_of(work, struct work_for_cpu, work);
+
wfc->ret = wfc->fn(wfc->arg);
- complete(&wfc->completion);
- return 0;
}
/**
@@ -3594,19 +3601,11 @@ static int do_work_for_cpu(void *_wfc)
*/
long work_on_cpu(unsigned int cpu, long (*fn)(void *), void *arg)
{
- struct task_struct *sub_thread;
- struct work_for_cpu wfc = {
- .completion = COMPLETION_INITIALIZER_ONSTACK(wfc.completion),
- .fn = fn,
- .arg = arg,
- };
+ struct work_for_cpu wfc = { .fn = fn, .arg = arg };
- sub_thread = kthread_create(do_work_for_cpu, &wfc, "work_for_cpu");
- if (IS_ERR(sub_thread))
- return PTR_ERR(sub_thread);
- kthread_bind(sub_thread, cpu);
- wake_up_process(sub_thread);
- wait_for_completion(&wfc.completion);
+ INIT_WORK_ONSTACK(&wfc.work, work_for_cpu_fn);
+ schedule_work_on(cpu, &wfc.work);
+ flush_work(&wfc.work);
return wfc.ret;
}
EXPORT_SYMBOL_GPL(work_on_cpu);
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 2403a63b5da5..35c4565ee8fa 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -452,7 +452,8 @@ config SLUB_STATS
config DEBUG_KMEMLEAK
bool "Kernel memory leak detector"
depends on DEBUG_KERNEL && EXPERIMENTAL && \
- (X86 || ARM || PPC || MIPS || S390 || SPARC64 || SUPERH || MICROBLAZE || TILE)
+ (X86 || ARM || PPC || MIPS || S390 || SPARC64 || SUPERH || \
+ MICROBLAZE || TILE || ARM64)
select DEBUG_FS
select STACKTRACE if STACKTRACE_SUPPORT
@@ -629,6 +630,20 @@ config PROVE_RCU_REPEATEDLY
Say N if you are unsure.
+config PROVE_RCU_DELAY
+ bool "RCU debugging: preemptible RCU race provocation"
+ depends on DEBUG_KERNEL && PREEMPT_RCU
+ default n
+ help
+ There is a class of races that involve an unlikely preemption
+ of __rcu_read_unlock() just after ->rcu_read_lock_nesting has
+ been set to INT_MIN. This feature inserts a delay at that
+ point to increase the probability of these races.
+
+ Say Y to increase probability of preemption of __rcu_read_unlock().
+
+ Say N if you are unsure.
+
config SPARSE_RCU_POINTER
bool "RCU debugging: sparse-based checks for pointer usage"
default n
@@ -739,7 +754,8 @@ config DEBUG_BUGVERBOSE
bool "Verbose BUG() reporting (adds 70K)" if DEBUG_KERNEL && EXPERT
depends on BUG
depends on ARM || AVR32 || M32R || M68K || SPARC32 || SPARC64 || \
- FRV || SUPERH || GENERIC_BUG || BLACKFIN || MN10300 || TILE
+ FRV || SUPERH || GENERIC_BUG || BLACKFIN || MN10300 || \
+ TILE || ARM64
default y
help
Say Y here to make BUG() panics output the file name and line number
diff --git a/lib/bcd.c b/lib/bcd.c
index 55efaf742346..40d304efe272 100644
--- a/lib/bcd.c
+++ b/lib/bcd.c
@@ -1,14 +1,14 @@
#include <linux/bcd.h>
#include <linux/export.h>
-unsigned bcd2bin(unsigned char val)
+unsigned _bcd2bin(unsigned char val)
{
return (val & 0x0f) + (val >> 4) * 10;
}
-EXPORT_SYMBOL(bcd2bin);
+EXPORT_SYMBOL(_bcd2bin);
-unsigned char bin2bcd(unsigned val)
+unsigned char _bin2bcd(unsigned val)
{
return ((val / 10) << 4) + val % 10;
}
-EXPORT_SYMBOL(bin2bcd);
+EXPORT_SYMBOL(_bin2bcd);
diff --git a/lib/dynamic_debug.c b/lib/dynamic_debug.c
index 7ca29a0a3019..e7f7d993357a 100644
--- a/lib/dynamic_debug.c
+++ b/lib/dynamic_debug.c
@@ -521,25 +521,25 @@ static char *dynamic_emit_prefix(const struct _ddebug *desc, char *buf)
int pos_after_tid;
int pos = 0;
- pos += snprintf(buf + pos, remaining(pos), "%s", KERN_DEBUG);
+ *buf = '\0';
+
if (desc->flags & _DPRINTK_FLAGS_INCL_TID) {
if (in_interrupt())
- pos += snprintf(buf + pos, remaining(pos), "%s ",
- "<intr>");
+ pos += snprintf(buf + pos, remaining(pos), "<intr> ");
else
pos += snprintf(buf + pos, remaining(pos), "[%d] ",
- task_pid_vnr(current));
+ task_pid_vnr(current));
}
pos_after_tid = pos;
if (desc->flags & _DPRINTK_FLAGS_INCL_MODNAME)
pos += snprintf(buf + pos, remaining(pos), "%s:",
- desc->modname);
+ desc->modname);
if (desc->flags & _DPRINTK_FLAGS_INCL_FUNCNAME)
pos += snprintf(buf + pos, remaining(pos), "%s:",
- desc->function);
+ desc->function);
if (desc->flags & _DPRINTK_FLAGS_INCL_LINENO)
pos += snprintf(buf + pos, remaining(pos), "%d:",
- desc->lineno);
+ desc->lineno);
if (pos - pos_after_tid)
pos += snprintf(buf + pos, remaining(pos), " ");
if (pos >= PREFIX_SIZE)
@@ -559,9 +559,13 @@ int __dynamic_pr_debug(struct _ddebug *descriptor, const char *fmt, ...)
BUG_ON(!fmt);
va_start(args, fmt);
+
vaf.fmt = fmt;
vaf.va = &args;
- res = printk("%s%pV", dynamic_emit_prefix(descriptor, buf), &vaf);
+
+ res = printk(KERN_DEBUG "%s%pV",
+ dynamic_emit_prefix(descriptor, buf), &vaf);
+
va_end(args);
return res;
@@ -574,15 +578,26 @@ int __dynamic_dev_dbg(struct _ddebug *descriptor,
struct va_format vaf;
va_list args;
int res;
- char buf[PREFIX_SIZE];
BUG_ON(!descriptor);
BUG_ON(!fmt);
va_start(args, fmt);
+
vaf.fmt = fmt;
vaf.va = &args;
- res = __dev_printk(dynamic_emit_prefix(descriptor, buf), dev, &vaf);
+
+ if (!dev) {
+ res = printk(KERN_DEBUG "(NULL device *): %pV", &vaf);
+ } else {
+ char buf[PREFIX_SIZE];
+
+ res = dev_printk_emit(7, dev, "%s%s %s: %pV",
+ dynamic_emit_prefix(descriptor, buf),
+ dev_driver_string(dev), dev_name(dev),
+ &vaf);
+ }
+
va_end(args);
return res;
@@ -592,20 +607,35 @@ EXPORT_SYMBOL(__dynamic_dev_dbg);
#ifdef CONFIG_NET
int __dynamic_netdev_dbg(struct _ddebug *descriptor,
- const struct net_device *dev, const char *fmt, ...)
+ const struct net_device *dev, const char *fmt, ...)
{
struct va_format vaf;
va_list args;
int res;
- char buf[PREFIX_SIZE];
BUG_ON(!descriptor);
BUG_ON(!fmt);
va_start(args, fmt);
+
vaf.fmt = fmt;
vaf.va = &args;
- res = __netdev_printk(dynamic_emit_prefix(descriptor, buf), dev, &vaf);
+
+ if (dev && dev->dev.parent) {
+ char buf[PREFIX_SIZE];
+
+ res = dev_printk_emit(7, dev->dev.parent,
+ "%s%s %s %s: %pV",
+ dynamic_emit_prefix(descriptor, buf),
+ dev_driver_string(dev->dev.parent),
+ dev_name(dev->dev.parent),
+ netdev_name(dev), &vaf);
+ } else if (dev) {
+ res = printk(KERN_DEBUG "%s: %pV", netdev_name(dev), &vaf);
+ } else {
+ res = printk(KERN_DEBUG "(NULL net_device): %pV", &vaf);
+ }
+
va_end(args);
return res;
diff --git a/lib/flex_proportions.c b/lib/flex_proportions.c
index c785554f9523..ebf3bac460b0 100644
--- a/lib/flex_proportions.c
+++ b/lib/flex_proportions.c
@@ -62,7 +62,7 @@ void fprop_global_destroy(struct fprop_global *p)
*/
bool fprop_new_period(struct fprop_global *p, int periods)
{
- u64 events;
+ s64 events;
unsigned long flags;
local_irq_save(flags);
diff --git a/mm/bootmem.c b/mm/bootmem.c
index bcb63ac48cc5..f468185b3b28 100644
--- a/mm/bootmem.c
+++ b/mm/bootmem.c
@@ -419,7 +419,7 @@ int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
}
/**
- * reserve_bootmem - mark a page range as usable
+ * reserve_bootmem - mark a page range as reserved
* @addr: starting address of the range
* @size: size of the range in bytes
* @flags: reservation flags (see linux/bootmem.h)
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
index 57c4b9309015..141dbb695097 100644
--- a/mm/huge_memory.c
+++ b/mm/huge_memory.c
@@ -1811,7 +1811,6 @@ static void __collapse_huge_page_copy(pte_t *pte, struct page *page,
src_page = pte_page(pteval);
copy_user_highpage(page, src_page, address, vma);
VM_BUG_ON(page_mapcount(src_page) != 1);
- VM_BUG_ON(page_count(src_page) != 2);
release_pte_page(src_page);
/*
* ptl mostly unnecessary, but preempt has to
diff --git a/mm/kmemleak.c b/mm/kmemleak.c
index 45eb6217bf38..0de83b4541e9 100644
--- a/mm/kmemleak.c
+++ b/mm/kmemleak.c
@@ -1483,13 +1483,11 @@ static void *kmemleak_seq_next(struct seq_file *seq, void *v, loff_t *pos)
{
struct kmemleak_object *prev_obj = v;
struct kmemleak_object *next_obj = NULL;
- struct list_head *n = &prev_obj->object_list;
+ struct kmemleak_object *obj = prev_obj;
++(*pos);
- list_for_each_continue_rcu(n, &object_list) {
- struct kmemleak_object *obj =
- list_entry(n, struct kmemleak_object, object_list);
+ list_for_each_entry_continue_rcu(obj, &object_list, object_list) {
if (get_object(obj)) {
next_obj = obj;
break;
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 3ad25f9d1fc1..6a5b90d0cfd7 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -126,9 +126,6 @@ static void register_page_bootmem_info_section(unsigned long start_pfn)
struct mem_section *ms;
struct page *page, *memmap;
- if (!pfn_valid(start_pfn))
- return;
-
section_nr = pfn_to_section_nr(start_pfn);
ms = __nr_to_section(section_nr);
@@ -187,9 +184,16 @@ void register_page_bootmem_info_node(struct pglist_data *pgdat)
end_pfn = pfn + pgdat->node_spanned_pages;
/* register_section info */
- for (; pfn < end_pfn; pfn += PAGES_PER_SECTION)
- register_page_bootmem_info_section(pfn);
-
+ for (; pfn < end_pfn; pfn += PAGES_PER_SECTION) {
+ /*
+ * Some platforms can assign the same pfn to multiple nodes - on
+ * node0 as well as nodeN. To avoid registering a pfn against
+ * multiple nodes we check that this pfn does not already
+ * reside in some other node.
+ */
+ if (pfn_valid(pfn) && (pfn_to_nid(pfn) == node))
+ register_page_bootmem_info_section(pfn);
+ }
}
#endif /* !CONFIG_SPARSEMEM_VMEMMAP */
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index c66fb875104a..c13ea7538891 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -584,7 +584,7 @@ static inline void __free_one_page(struct page *page,
combined_idx = buddy_idx & page_idx;
higher_page = page + (combined_idx - page_idx);
buddy_idx = __find_buddy_index(combined_idx, order + 1);
- higher_buddy = page + (buddy_idx - combined_idx);
+ higher_buddy = higher_page + (buddy_idx - combined_idx);
if (page_is_buddy(higher_page, higher_buddy, order + 1)) {
list_add_tail(&page->lru,
&zone->free_area[order].free_list[migratetype]);
diff --git a/mm/slab.c b/mm/slab.c
index 811af03a14ef..c6854759bcf1 100644
--- a/mm/slab.c
+++ b/mm/slab.c
@@ -983,7 +983,7 @@ static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac,
}
/* The caller cannot use PFMEMALLOC objects, find another one */
- for (i = 1; i < ac->avail; i++) {
+ for (i = 0; i < ac->avail; i++) {
/* If a !PFMEMALLOC object is found, swap them */
if (!is_obj_pfmemalloc(ac->entry[i])) {
objp = ac->entry[i];
@@ -1000,7 +1000,7 @@ static void *__ac_get_obj(struct kmem_cache *cachep, struct array_cache *ac,
l3 = cachep->nodelists[numa_mem_id()];
if (!list_empty(&l3->slabs_free) && force_refill) {
struct slab *slabp = virt_to_slab(objp);
- ClearPageSlabPfmemalloc(virt_to_page(slabp->s_mem));
+ ClearPageSlabPfmemalloc(virt_to_head_page(slabp->s_mem));
clear_obj_pfmemalloc(&objp);
recheck_pfmemalloc_active(cachep, ac);
return objp;
@@ -1032,7 +1032,7 @@ static void *__ac_put_obj(struct kmem_cache *cachep, struct array_cache *ac,
{
if (unlikely(pfmemalloc_active)) {
/* Some pfmemalloc slabs exist, check if this is one */
- struct page *page = virt_to_page(objp);
+ struct page *page = virt_to_head_page(objp);
if (PageSlabPfmemalloc(page))
set_obj_pfmemalloc(&objp);
}
diff --git a/mm/slub.c b/mm/slub.c
index 8f78e2577031..2fdd96f9e998 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -1524,12 +1524,13 @@ static inline void *acquire_slab(struct kmem_cache *s,
}
static int put_cpu_partial(struct kmem_cache *s, struct page *page, int drain);
+static inline bool pfmemalloc_match(struct page *page, gfp_t gfpflags);
/*
* Try to allocate a partial slab from a specific node.
*/
-static void *get_partial_node(struct kmem_cache *s,
- struct kmem_cache_node *n, struct kmem_cache_cpu *c)
+static void *get_partial_node(struct kmem_cache *s, struct kmem_cache_node *n,
+ struct kmem_cache_cpu *c, gfp_t flags)
{
struct page *page, *page2;
void *object = NULL;
@@ -1545,9 +1546,13 @@ static void *get_partial_node(struct kmem_cache *s,
spin_lock(&n->list_lock);
list_for_each_entry_safe(page, page2, &n->partial, lru) {
- void *t = acquire_slab(s, n, page, object == NULL);
+ void *t;
int available;
+ if (!pfmemalloc_match(page, flags))
+ continue;
+
+ t = acquire_slab(s, n, page, object == NULL);
if (!t)
break;
@@ -1614,7 +1619,7 @@ static void *get_any_partial(struct kmem_cache *s, gfp_t flags,
if (n && cpuset_zone_allowed_hardwall(zone, flags) &&
n->nr_partial > s->min_partial) {
- object = get_partial_node(s, n, c);
+ object = get_partial_node(s, n, c, flags);
if (object) {
/*
* Return the object even if
@@ -1643,7 +1648,7 @@ static void *get_partial(struct kmem_cache *s, gfp_t flags, int node,
void *object;
int searchnode = (node == NUMA_NO_NODE) ? numa_node_id() : node;
- object = get_partial_node(s, get_node(s, searchnode), c);
+ object = get_partial_node(s, get_node(s, searchnode), c, flags);
if (object || node != NUMA_NO_NODE)
return object;
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 8d01243d9560..99b434b674c0 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -3102,6 +3102,7 @@ int kswapd_run(int nid)
/* failure at boot is fatal */
BUG_ON(system_state == SYSTEM_BOOTING);
printk("Failed to start kswapd on node %d\n",nid);
+ pgdat->kswapd = NULL;
ret = -1;
}
return ret;
diff --git a/net/8021q/vlanproc.c b/net/8021q/vlanproc.c
index c718fd3664b6..4de77ea5fa37 100644
--- a/net/8021q/vlanproc.c
+++ b/net/8021q/vlanproc.c
@@ -105,7 +105,7 @@ static const struct file_operations vlandev_fops = {
};
/*
- * Proc filesystem derectory entries.
+ * Proc filesystem directory entries.
*/
/* Strings */
diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index e877af8bdd1e..469daabd90c7 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -642,7 +642,8 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
struct batadv_neigh_node *router = NULL;
struct batadv_orig_node *orig_node_tmp;
struct hlist_node *node;
- uint8_t bcast_own_sum_orig, bcast_own_sum_neigh;
+ int if_num;
+ uint8_t sum_orig, sum_neigh;
uint8_t *neigh_addr;
batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
@@ -727,17 +728,17 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
if (router && (neigh_node->tq_avg == router->tq_avg)) {
orig_node_tmp = router->orig_node;
spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
- bcast_own_sum_orig =
- orig_node_tmp->bcast_own_sum[if_incoming->if_num];
+ if_num = router->if_incoming->if_num;
+ sum_orig = orig_node_tmp->bcast_own_sum[if_num];
spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
orig_node_tmp = neigh_node->orig_node;
spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
- bcast_own_sum_neigh =
- orig_node_tmp->bcast_own_sum[if_incoming->if_num];
+ if_num = neigh_node->if_incoming->if_num;
+ sum_neigh = orig_node_tmp->bcast_own_sum[if_num];
spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
- if (bcast_own_sum_orig >= bcast_own_sum_neigh)
+ if (sum_orig >= sum_neigh)
goto update_tt;
}
diff --git a/net/batman-adv/bitarray.h b/net/batman-adv/bitarray.h
index a081ce1c0514..cebaae7e148b 100644
--- a/net/batman-adv/bitarray.h
+++ b/net/batman-adv/bitarray.h
@@ -20,8 +20,8 @@
#ifndef _NET_BATMAN_ADV_BITARRAY_H_
#define _NET_BATMAN_ADV_BITARRAY_H_
-/* returns true if the corresponding bit in the given seq_bits indicates true
- * and curr_seqno is within range of last_seqno
+/* Returns 1 if the corresponding bit in the given seq_bits indicates true
+ * and curr_seqno is within range of last_seqno. Otherwise returns 0.
*/
static inline int batadv_test_bit(const unsigned long *seq_bits,
uint32_t last_seqno, uint32_t curr_seqno)
@@ -32,7 +32,7 @@ static inline int batadv_test_bit(const unsigned long *seq_bits,
if (diff < 0 || diff >= BATADV_TQ_LOCAL_WINDOW_SIZE)
return 0;
else
- return test_bit(diff, seq_bits);
+ return test_bit(diff, seq_bits) != 0;
}
/* turn corresponding bit on, so we can remember that we got the packet */
diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c
index 109ea2aae96c..21c53577c8d6 100644
--- a/net/batman-adv/soft-interface.c
+++ b/net/batman-adv/soft-interface.c
@@ -100,18 +100,21 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p)
{
struct batadv_priv *bat_priv = netdev_priv(dev);
struct sockaddr *addr = p;
+ uint8_t old_addr[ETH_ALEN];
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);
+
/* only modify transtable if it has been initialized before */
if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE) {
- batadv_tt_local_remove(bat_priv, dev->dev_addr,
+ batadv_tt_local_remove(bat_priv, old_addr,
"mac address changed", false);
batadv_tt_local_add(dev, addr->sa_data, BATADV_NULL_IFINDEX);
}
- memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
dev->addr_assign_type &= ~NET_ADDR_RANDOM;
return 0;
}
diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c
index 5e5f5b410e0b..1eaacf10d19d 100644
--- a/net/bluetooth/bnep/sock.c
+++ b/net/bluetooth/bnep/sock.c
@@ -58,7 +58,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
switch (cmd) {
case BNEPCONNADD:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&ca, argp, sizeof(ca)))
return -EFAULT;
@@ -84,7 +84,7 @@ static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
case BNEPCONNDEL:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&cd, argp, sizeof(cd)))
return -EFAULT;
diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c
index 311668d14571..32dc83dcb6b2 100644
--- a/net/bluetooth/cmtp/sock.c
+++ b/net/bluetooth/cmtp/sock.c
@@ -72,7 +72,7 @@ static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
switch (cmd) {
case CMTPCONNADD:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&ca, argp, sizeof(ca)))
return -EFAULT;
@@ -97,7 +97,7 @@ static int cmtp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
case CMTPCONNDEL:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&cd, argp, sizeof(cd)))
return -EFAULT;
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index d4de5db18d5a..0b997c8f9655 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -734,6 +734,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
cancel_work_sync(&hdev->le_scan);
+ cancel_delayed_work(&hdev->power_off);
+
hci_req_cancel(hdev, ENODEV);
hci_req_lock(hdev);
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index 19fdac78e555..d5ace1eda3ed 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -490,7 +490,7 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
switch (cmd) {
case HCISETRAW:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks))
return -EPERM;
@@ -510,12 +510,12 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd,
case HCIBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_sock_blacklist_add(hdev, (void __user *) arg);
case HCIUNBLOCKADDR:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_sock_blacklist_del(hdev, (void __user *) arg);
default:
@@ -546,22 +546,22 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
case HCIDEVUP:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_dev_open(arg);
case HCIDEVDOWN:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_dev_close(arg);
case HCIDEVRESET:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_dev_reset(arg);
case HCIDEVRESTAT:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_dev_reset_stat(arg);
case HCISETSCAN:
@@ -573,7 +573,7 @@ static int hci_sock_ioctl(struct socket *sock, unsigned int cmd,
case HCISETACLMTU:
case HCISETSCOMTU:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
return hci_dev_cmd(cmd, argp);
case HCIINQUIRY:
diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c
index 18b3f6892a36..b24fb3bd8625 100644
--- a/net/bluetooth/hidp/sock.c
+++ b/net/bluetooth/hidp/sock.c
@@ -56,7 +56,7 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
switch (cmd) {
case HIDPCONNADD:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&ca, argp, sizeof(ca)))
return -EFAULT;
@@ -91,7 +91,7 @@ static int hidp_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long
case HIDPCONNDEL:
if (!capable(CAP_NET_ADMIN))
- return -EACCES;
+ return -EPERM;
if (copy_from_user(&cd, argp, sizeof(cd)))
return -EFAULT;
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 4ea1710a4783..38c00f142203 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1008,7 +1008,7 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
if (!conn)
return;
- if (chan->mode == L2CAP_MODE_ERTM) {
+ if (chan->mode == L2CAP_MODE_ERTM && chan->state == BT_CONNECTED) {
__clear_retrans_timer(chan);
__clear_monitor_timer(chan);
__clear_ack_timer(chan);
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index ad6613d17ca6..eba022de3c20 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -2875,6 +2875,22 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
if (scan)
hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
+ if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+ u8 ssp = 1;
+
+ hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
+ }
+
+ if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+ struct hci_cp_write_le_host_supported cp;
+
+ cp.le = 1;
+ cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
+
+ hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
+ sizeof(cp), &cp);
+ }
+
update_class(hdev);
update_name(hdev, hdev->dev_name);
update_eir(hdev);
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index 24c5eea8c45b..159aa8bef9e7 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -1073,16 +1073,13 @@ static int write_partial_msg_pages(struct ceph_connection *con)
BUG_ON(kaddr == NULL);
base = kaddr + con->out_msg_pos.page_pos + bio_offset;
crc = crc32c(crc, base, len);
+ kunmap(page);
msg->footer.data_crc = cpu_to_le32(crc);
con->out_msg_pos.did_page_crc = true;
}
ret = ceph_tcp_sendpage(con->sock, page,
con->out_msg_pos.page_pos + bio_offset,
len, 1);
-
- if (do_datacrc)
- kunmap(page);
-
if (ret <= 0)
goto out;
diff --git a/net/core/dev.c b/net/core/dev.c
index d7fe32c946c1..36c4a0cdb6c1 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -2134,7 +2134,8 @@ static bool can_checksum_protocol(netdev_features_t features, __be16 protocol)
static netdev_features_t harmonize_features(struct sk_buff *skb,
__be16 protocol, netdev_features_t features)
{
- if (!can_checksum_protocol(features, protocol)) {
+ if (skb->ip_summed != CHECKSUM_NONE &&
+ !can_checksum_protocol(features, protocol)) {
features &= ~NETIF_F_ALL_CSUM;
features &= ~NETIF_F_SG;
} else if (illegal_highdma(skb->dev, skb)) {
@@ -3322,7 +3323,7 @@ ncls:
if (pt_prev) {
if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC)))
- ret = -ENOMEM;
+ goto drop;
else
ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
} else {
@@ -6423,22 +6424,26 @@ const char *netdev_drivername(const struct net_device *dev)
return empty;
}
-int __netdev_printk(const char *level, const struct net_device *dev,
+static int __netdev_printk(const char *level, const struct net_device *dev,
struct va_format *vaf)
{
int r;
- if (dev && dev->dev.parent)
- r = dev_printk(level, dev->dev.parent, "%s: %pV",
- netdev_name(dev), vaf);
- else if (dev)
+ if (dev && dev->dev.parent) {
+ r = dev_printk_emit(level[1] - '0',
+ dev->dev.parent,
+ "%s %s %s: %pV",
+ dev_driver_string(dev->dev.parent),
+ dev_name(dev->dev.parent),
+ netdev_name(dev), vaf);
+ } else if (dev) {
r = printk("%s%s: %pV", level, netdev_name(dev), vaf);
- else
+ } else {
r = printk("%s(NULL net_device): %pV", level, vaf);
+ }
return r;
}
-EXPORT_SYMBOL(__netdev_printk);
int netdev_printk(const char *level, const struct net_device *dev,
const char *format, ...)
@@ -6453,6 +6458,7 @@ int netdev_printk(const char *level, const struct net_device *dev,
vaf.va = &args;
r = __netdev_printk(level, dev, &vaf);
+
va_end(args);
return r;
@@ -6472,6 +6478,7 @@ int func(const struct net_device *dev, const char *fmt, ...) \
vaf.va = &args; \
\
r = __netdev_printk(level, dev, &vaf); \
+ \
va_end(args); \
\
return r; \
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
index fe00d1208167..e33ebae519c8 100644
--- a/net/core/skbuff.c
+++ b/net/core/skbuff.c
@@ -3502,7 +3502,9 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from,
if (!skb_cloned(from))
skb_shinfo(from)->nr_frags = 0;
- /* if the skb is cloned this does nothing since we set nr_frags to 0 */
+ /* if the skb is not cloned this does nothing
+ * since we set nr_frags to 0.
+ */
for (i = 0; i < skb_shinfo(from)->nr_frags; i++)
skb_frag_ref(from, i);
diff --git a/net/core/sock.c b/net/core/sock.c
index 305792076121..a6000fbad294 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -691,7 +691,8 @@ set_rcvbuf:
case SO_KEEPALIVE:
#ifdef CONFIG_INET
- if (sk->sk_protocol == IPPROTO_TCP)
+ if (sk->sk_protocol == IPPROTO_TCP &&
+ sk->sk_type == SOCK_STREAM)
tcp_set_keepalive(sk, valbool);
#endif
sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool);
diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c
index 77e87aff419a..47800459e4cb 100644
--- a/net/ipv4/arp.c
+++ b/net/ipv4/arp.c
@@ -1225,7 +1225,7 @@ static int arp_netdev_event(struct notifier_block *this, unsigned long event,
switch (event) {
case NETDEV_CHANGEADDR:
neigh_changeaddr(&arp_tbl, dev);
- rt_cache_flush(dev_net(dev), 0);
+ rt_cache_flush(dev_net(dev));
break;
default:
break;
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c
index 44bf82e3aef7..e12fad773852 100644
--- a/net/ipv4/devinet.c
+++ b/net/ipv4/devinet.c
@@ -725,7 +725,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
break;
case SIOCSIFFLAGS:
- ret = -EACCES;
+ ret = -EPERM;
if (!capable(CAP_NET_ADMIN))
goto out;
break;
@@ -733,7 +733,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg)
case SIOCSIFBRDADDR: /* Set the broadcast address */
case SIOCSIFDSTADDR: /* Set the destination address */
case SIOCSIFNETMASK: /* Set the netmask for the interface */
- ret = -EACCES;
+ ret = -EPERM;
if (!capable(CAP_NET_ADMIN))
goto out;
ret = -EINVAL;
@@ -1503,7 +1503,7 @@ static int devinet_conf_proc(ctl_table *ctl, int write,
if (i == IPV4_DEVCONF_ACCEPT_LOCAL - 1 ||
i == IPV4_DEVCONF_ROUTE_LOCALNET - 1)
if ((new_value == 0) && (old_value != 0))
- rt_cache_flush(net, 0);
+ rt_cache_flush(net);
}
return ret;
@@ -1537,7 +1537,7 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write,
dev_disable_lro(idev->dev);
}
rtnl_unlock();
- rt_cache_flush(net, 0);
+ rt_cache_flush(net);
}
}
@@ -1554,7 +1554,7 @@ static int ipv4_doint_and_flush(ctl_table *ctl, int write,
struct net *net = ctl->extra2;
if (write && *valp != val)
- rt_cache_flush(net, 0);
+ rt_cache_flush(net);
return ret;
}
diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c
index c43ae3fba792..8e2b475da9fa 100644
--- a/net/ipv4/fib_frontend.c
+++ b/net/ipv4/fib_frontend.c
@@ -148,7 +148,7 @@ static void fib_flush(struct net *net)
}
if (flushed)
- rt_cache_flush(net, -1);
+ rt_cache_flush(net);
}
/*
@@ -999,11 +999,11 @@ static void nl_fib_lookup_exit(struct net *net)
net->ipv4.fibnl = NULL;
}
-static void fib_disable_ip(struct net_device *dev, int force, int delay)
+static void fib_disable_ip(struct net_device *dev, int force)
{
if (fib_sync_down_dev(dev, force))
fib_flush(dev_net(dev));
- rt_cache_flush(dev_net(dev), delay);
+ rt_cache_flush(dev_net(dev));
arp_ifdown(dev);
}
@@ -1020,7 +1020,7 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
fib_sync_up(dev);
#endif
atomic_inc(&net->ipv4.dev_addr_genid);
- rt_cache_flush(dev_net(dev), -1);
+ rt_cache_flush(dev_net(dev));
break;
case NETDEV_DOWN:
fib_del_ifaddr(ifa, NULL);
@@ -1029,9 +1029,9 @@ static int fib_inetaddr_event(struct notifier_block *this, unsigned long event,
/* Last address was deleted from this interface.
* Disable IP.
*/
- fib_disable_ip(dev, 1, 0);
+ fib_disable_ip(dev, 1);
} else {
- rt_cache_flush(dev_net(dev), -1);
+ rt_cache_flush(dev_net(dev));
}
break;
}
@@ -1045,7 +1045,7 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
struct net *net = dev_net(dev);
if (event == NETDEV_UNREGISTER) {
- fib_disable_ip(dev, 2, -1);
+ fib_disable_ip(dev, 2);
rt_flush_dev(dev);
return NOTIFY_DONE;
}
@@ -1062,14 +1062,14 @@ static int fib_netdev_event(struct notifier_block *this, unsigned long event, vo
fib_sync_up(dev);
#endif
atomic_inc(&net->ipv4.dev_addr_genid);
- rt_cache_flush(dev_net(dev), -1);
+ rt_cache_flush(dev_net(dev));
break;
case NETDEV_DOWN:
- fib_disable_ip(dev, 0, 0);
+ fib_disable_ip(dev, 0);
break;
case NETDEV_CHANGEMTU:
case NETDEV_CHANGE:
- rt_cache_flush(dev_net(dev), 0);
+ rt_cache_flush(dev_net(dev));
break;
case NETDEV_UNREGISTER_BATCH:
break;
diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c
index a83d74e498d2..274309d3aded 100644
--- a/net/ipv4/fib_rules.c
+++ b/net/ipv4/fib_rules.c
@@ -259,7 +259,7 @@ static size_t fib4_rule_nlmsg_payload(struct fib_rule *rule)
static void fib4_rule_flush_cache(struct fib_rules_ops *ops)
{
- rt_cache_flush(ops->fro_net, -1);
+ rt_cache_flush(ops->fro_net);
}
static const struct fib_rules_ops __net_initdata fib4_rules_ops_template = {
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index 57bd978483e1..d1b93595b4a7 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -1286,7 +1286,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
fib_release_info(fi_drop);
if (state & FA_S_ACCESSED)
- rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
+ rt_cache_flush(cfg->fc_nlinfo.nl_net);
rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen,
tb->tb_id, &cfg->fc_nlinfo, NLM_F_REPLACE);
@@ -1333,7 +1333,7 @@ int fib_table_insert(struct fib_table *tb, struct fib_config *cfg)
list_add_tail_rcu(&new_fa->fa_list,
(fa ? &fa->fa_list : fa_head));
- rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
+ rt_cache_flush(cfg->fc_nlinfo.nl_net);
rtmsg_fib(RTM_NEWROUTE, htonl(key), new_fa, plen, tb->tb_id,
&cfg->fc_nlinfo, 0);
succeeded:
@@ -1708,7 +1708,7 @@ int fib_table_delete(struct fib_table *tb, struct fib_config *cfg)
trie_leaf_remove(t, l);
if (fa->fa_state & FA_S_ACCESSED)
- rt_cache_flush(cfg->fc_nlinfo.nl_net, -1);
+ rt_cache_flush(cfg->fc_nlinfo.nl_net);
fib_release_info(fa->fa_info);
alias_free_mem_rcu(fa);
diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
index e1e0a4e8fd34..c7527f6b9ad9 100644
--- a/net/ipv4/inetpeer.c
+++ b/net/ipv4/inetpeer.c
@@ -510,7 +510,10 @@ relookup:
secure_ipv6_id(daddr->addr.a6));
p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW;
p->rate_tokens = 0;
- p->rate_last = 0;
+ /* 60*HZ is arbitrary, but chosen enough high so that the first
+ * calculation of tokens is at its maximum.
+ */
+ p->rate_last = jiffies - 60*HZ;
INIT_LIST_HEAD(&p->gc_list);
/* Link the node. */
diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c
index ff0f071969ea..d23c6571ba1c 100644
--- a/net/ipv4/raw.c
+++ b/net/ipv4/raw.c
@@ -131,18 +131,20 @@ found:
* 0 - deliver
* 1 - block
*/
-static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb)
+static int icmp_filter(const struct sock *sk, const struct sk_buff *skb)
{
- int type;
+ struct icmphdr _hdr;
+ const struct icmphdr *hdr;
- if (!pskb_may_pull(skb, sizeof(struct icmphdr)))
+ hdr = skb_header_pointer(skb, skb_transport_offset(skb),
+ sizeof(_hdr), &_hdr);
+ if (!hdr)
return 1;
- type = icmp_hdr(skb)->type;
- if (type < 32) {
+ if (hdr->type < 32) {
__u32 data = raw_sk(sk)->filter.data;
- return ((1 << type) & data) != 0;
+ return ((1U << hdr->type) & data) != 0;
}
/* Do not block unknown ICMP types */
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index 82cf2a722b23..fd9af60397b5 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -202,11 +202,6 @@ EXPORT_SYMBOL(ip_tos2prio);
static DEFINE_PER_CPU(struct rt_cache_stat, rt_cache_stat);
#define RT_CACHE_STAT_INC(field) __this_cpu_inc(rt_cache_stat.field)
-static inline int rt_genid(struct net *net)
-{
- return atomic_read(&net->ipv4.rt_genid);
-}
-
#ifdef CONFIG_PROC_FS
static void *rt_cache_seq_start(struct seq_file *seq, loff_t *pos)
{
@@ -447,27 +442,9 @@ static inline bool rt_is_expired(const struct rtable *rth)
return rth->rt_genid != rt_genid(dev_net(rth->dst.dev));
}
-/*
- * Perturbation of rt_genid by a small quantity [1..256]
- * Using 8 bits of shuffling ensure we can call rt_cache_invalidate()
- * many times (2^24) without giving recent rt_genid.
- * Jenkins hash is strong enough that litle changes of rt_genid are OK.
- */
-static void rt_cache_invalidate(struct net *net)
+void rt_cache_flush(struct net *net)
{
- unsigned char shuffle;
-
- get_random_bytes(&shuffle, sizeof(shuffle));
- atomic_add(shuffle + 1U, &net->ipv4.rt_genid);
-}
-
-/*
- * delay < 0 : invalidate cache (fast : entries will be deleted later)
- * delay >= 0 : invalidate & flush cache (can be long)
- */
-void rt_cache_flush(struct net *net, int delay)
-{
- rt_cache_invalidate(net);
+ rt_genid_bump(net);
}
static struct neighbour *ipv4_neigh_lookup(const struct dst_entry *dst,
@@ -2345,7 +2322,7 @@ int ip_rt_dump(struct sk_buff *skb, struct netlink_callback *cb)
void ip_rt_multicast_event(struct in_device *in_dev)
{
- rt_cache_flush(dev_net(in_dev->dev), 0);
+ rt_cache_flush(dev_net(in_dev->dev));
}
#ifdef CONFIG_SYSCTL
@@ -2354,16 +2331,7 @@ static int ipv4_sysctl_rtcache_flush(ctl_table *__ctl, int write,
size_t *lenp, loff_t *ppos)
{
if (write) {
- int flush_delay;
- ctl_table ctl;
- struct net *net;
-
- memcpy(&ctl, __ctl, sizeof(ctl));
- ctl.data = &flush_delay;
- proc_dointvec(&ctl, write, buffer, lenp, ppos);
-
- net = (struct net *)__ctl->extra1;
- rt_cache_flush(net, flush_delay);
+ rt_cache_flush((struct net *)__ctl->extra1);
return 0;
}
@@ -2533,8 +2501,7 @@ static __net_initdata struct pernet_operations sysctl_route_ops = {
static __net_init int rt_genid_init(struct net *net)
{
- get_random_bytes(&net->ipv4.rt_genid,
- sizeof(net->ipv4.rt_genid));
+ atomic_set(&net->rt_genid, 0);
get_random_bytes(&net->ipv4.dev_addr_genid,
sizeof(net->ipv4.dev_addr_genid));
return 0;
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index 2109ff4a1daf..5f6419341821 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -1762,8 +1762,14 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
}
#ifdef CONFIG_NET_DMA
- if (tp->ucopy.dma_chan)
- dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+ if (tp->ucopy.dma_chan) {
+ if (tp->rcv_wnd == 0 &&
+ !skb_queue_empty(&sk->sk_async_wait_queue)) {
+ tcp_service_net_dma(sk, true);
+ tcp_cleanup_rbuf(sk, copied);
+ } else
+ dma_async_memcpy_issue_pending(tp->ucopy.dma_chan);
+ }
#endif
if (copied >= target) {
/* Do not sleep, just process backlog. */
@@ -2325,10 +2331,17 @@ static int tcp_repair_options_est(struct tcp_sock *tp,
tp->rx_opt.mss_clamp = opt.opt_val;
break;
case TCPOPT_WINDOW:
- if (opt.opt_val > 14)
- return -EFBIG;
+ {
+ u16 snd_wscale = opt.opt_val & 0xFFFF;
+ u16 rcv_wscale = opt.opt_val >> 16;
+
+ if (snd_wscale > 14 || rcv_wscale > 14)
+ return -EFBIG;
- tp->rx_opt.snd_wscale = opt.opt_val;
+ tp->rx_opt.snd_wscale = snd_wscale;
+ tp->rx_opt.rcv_wscale = rcv_wscale;
+ tp->rx_opt.wscale_ok = 1;
+ }
break;
case TCPOPT_SACK_PERM:
if (opt.opt_val != 0)
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 6e38c6c23caa..d377f4854cb8 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -4661,7 +4661,7 @@ queue_and_out:
if (eaten > 0)
kfree_skb_partial(skb, fragstolen);
- else if (!sock_flag(sk, SOCK_DEAD))
+ if (!sock_flag(sk, SOCK_DEAD))
sk->sk_data_ready(sk, 0);
return;
}
@@ -5556,8 +5556,7 @@ no_ack:
#endif
if (eaten)
kfree_skb_partial(skb, fragstolen);
- else
- sk->sk_data_ready(sk, 0);
+ sk->sk_data_ready(sk, 0);
return 0;
}
}
diff --git a/net/ipv6/inet6_connection_sock.c b/net/ipv6/inet6_connection_sock.c
index 0251a6005be8..c4f934176cab 100644
--- a/net/ipv6/inet6_connection_sock.c
+++ b/net/ipv6/inet6_connection_sock.c
@@ -175,33 +175,12 @@ void __inet6_csk_dst_store(struct sock *sk, struct dst_entry *dst,
const struct in6_addr *saddr)
{
__ip6_dst_store(sk, dst, daddr, saddr);
-
-#ifdef CONFIG_XFRM
- {
- struct rt6_info *rt = (struct rt6_info *)dst;
- rt->rt6i_flow_cache_genid = atomic_read(&flow_cache_genid);
- }
-#endif
}
static inline
struct dst_entry *__inet6_csk_dst_check(struct sock *sk, u32 cookie)
{
- struct dst_entry *dst;
-
- dst = __sk_dst_check(sk, cookie);
-
-#ifdef CONFIG_XFRM
- if (dst) {
- struct rt6_info *rt = (struct rt6_info *)dst;
- if (rt->rt6i_flow_cache_genid != atomic_read(&flow_cache_genid)) {
- __sk_dst_reset(sk);
- dst = NULL;
- }
- }
-#endif
-
- return dst;
+ return __sk_dst_check(sk, cookie);
}
static struct dst_entry *inet6_csk_route_socket(struct sock *sk,
diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c
index 13690d650c3e..286acfc21250 100644
--- a/net/ipv6/ip6_fib.c
+++ b/net/ipv6/ip6_fib.c
@@ -819,6 +819,10 @@ int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nl_info *info)
offsetof(struct rt6_info, rt6i_src),
allow_create, replace_required);
+ if (IS_ERR(sn)) {
+ err = PTR_ERR(sn);
+ sn = NULL;
+ }
if (!sn) {
/* If it is failed, discard just allocated
root, and then (in st_failure) stale node
diff --git a/net/ipv6/mip6.c b/net/ipv6/mip6.c
index 5b087c31d87b..0f9bdc5ee9f3 100644
--- a/net/ipv6/mip6.c
+++ b/net/ipv6/mip6.c
@@ -86,28 +86,30 @@ static int mip6_mh_len(int type)
static int mip6_mh_filter(struct sock *sk, struct sk_buff *skb)
{
- struct ip6_mh *mh;
+ struct ip6_mh _hdr;
+ const struct ip6_mh *mh;
- if (!pskb_may_pull(skb, (skb_transport_offset(skb)) + 8) ||
- !pskb_may_pull(skb, (skb_transport_offset(skb) +
- ((skb_transport_header(skb)[1] + 1) << 3))))
+ mh = skb_header_pointer(skb, skb_transport_offset(skb),
+ sizeof(_hdr), &_hdr);
+ if (!mh)
return -1;
- mh = (struct ip6_mh *)skb_transport_header(skb);
+ if (((mh->ip6mh_hdrlen + 1) << 3) > skb->len)
+ return -1;
if (mh->ip6mh_hdrlen < mip6_mh_len(mh->ip6mh_type)) {
LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH message too short: %d vs >=%d\n",
mh->ip6mh_hdrlen, mip6_mh_len(mh->ip6mh_type));
- mip6_param_prob(skb, 0, ((&mh->ip6mh_hdrlen) -
- skb_network_header(skb)));
+ mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_hdrlen) +
+ skb_network_header_len(skb));
return -1;
}
if (mh->ip6mh_proto != IPPROTO_NONE) {
LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH invalid payload proto = %d\n",
mh->ip6mh_proto);
- mip6_param_prob(skb, 0, ((&mh->ip6mh_proto) -
- skb_network_header(skb)));
+ mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_proto) +
+ skb_network_header_len(skb));
return -1;
}
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
index ef0579d5bca6..4a5f78b50495 100644
--- a/net/ipv6/raw.c
+++ b/net/ipv6/raw.c
@@ -107,21 +107,20 @@ found:
* 0 - deliver
* 1 - block
*/
-static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
+static int icmpv6_filter(const struct sock *sk, const struct sk_buff *skb)
{
- struct icmp6hdr *icmph;
- struct raw6_sock *rp = raw6_sk(sk);
-
- if (pskb_may_pull(skb, sizeof(struct icmp6hdr))) {
- __u32 *data = &rp->filter.data[0];
- int bit_nr;
+ struct icmp6hdr *_hdr;
+ const struct icmp6hdr *hdr;
- icmph = (struct icmp6hdr *) skb->data;
- bit_nr = icmph->icmp6_type;
+ hdr = skb_header_pointer(skb, skb_transport_offset(skb),
+ sizeof(_hdr), &_hdr);
+ if (hdr) {
+ const __u32 *data = &raw6_sk(sk)->filter.data[0];
+ unsigned int type = hdr->icmp6_type;
- return (data[bit_nr >> 5] & (1 << (bit_nr & 31))) != 0;
+ return (data[type >> 5] & (1U << (type & 31))) != 0;
}
- return 0;
+ return 1;
}
#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index 8e80fd279100..854e4018d205 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -226,7 +226,7 @@ static struct rt6_info ip6_null_entry_template = {
.dst = {
.__refcnt = ATOMIC_INIT(1),
.__use = 1,
- .obsolete = -1,
+ .obsolete = DST_OBSOLETE_FORCE_CHK,
.error = -ENETUNREACH,
.input = ip6_pkt_discard,
.output = ip6_pkt_discard_out,
@@ -246,7 +246,7 @@ static struct rt6_info ip6_prohibit_entry_template = {
.dst = {
.__refcnt = ATOMIC_INIT(1),
.__use = 1,
- .obsolete = -1,
+ .obsolete = DST_OBSOLETE_FORCE_CHK,
.error = -EACCES,
.input = ip6_pkt_prohibit,
.output = ip6_pkt_prohibit_out,
@@ -261,7 +261,7 @@ static struct rt6_info ip6_blk_hole_entry_template = {
.dst = {
.__refcnt = ATOMIC_INIT(1),
.__use = 1,
- .obsolete = -1,
+ .obsolete = DST_OBSOLETE_FORCE_CHK,
.error = -EINVAL,
.input = dst_discard,
.output = dst_discard,
@@ -281,13 +281,14 @@ static inline struct rt6_info *ip6_dst_alloc(struct net *net,
struct fib6_table *table)
{
struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev,
- 0, DST_OBSOLETE_NONE, flags);
+ 0, DST_OBSOLETE_FORCE_CHK, flags);
if (rt) {
struct dst_entry *dst = &rt->dst;
memset(dst + 1, 0, sizeof(*rt) - sizeof(*dst));
rt6_init_peer(rt, table ? &table->tb6_peers : net->ipv6.peers);
+ rt->rt6i_genid = rt_genid(net);
}
return rt;
}
@@ -1031,6 +1032,13 @@ static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie)
rt = (struct rt6_info *) dst;
+ /* All IPV6 dsts are created with ->obsolete set to the value
+ * DST_OBSOLETE_FORCE_CHK which forces validation calls down
+ * into this function always.
+ */
+ if (rt->rt6i_genid != rt_genid(dev_net(rt->dst.dev)))
+ return NULL;
+
if (rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) {
if (rt->rt6i_peer_genid != rt6_peer_genid()) {
if (!rt6_has_peer(rt))
@@ -1397,8 +1405,6 @@ int ip6_route_add(struct fib6_config *cfg)
goto out;
}
- rt->dst.obsolete = -1;
-
if (cfg->fc_flags & RTF_EXPIRES)
rt6_set_expires(rt, jiffies +
clock_t_to_jiffies(cfg->fc_expires));
@@ -2080,7 +2086,6 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev,
rt->dst.input = ip6_input;
rt->dst.output = ip6_output;
rt->rt6i_idev = idev;
- rt->dst.obsolete = -1;
rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP;
if (anycast)
diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c
index d71cd9229a47..6f936358d664 100644
--- a/net/l2tp/l2tp_netlink.c
+++ b/net/l2tp/l2tp_netlink.c
@@ -80,8 +80,8 @@ static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
&l2tp_nl_family, 0, L2TP_CMD_NOOP);
- if (IS_ERR(hdr)) {
- ret = PTR_ERR(hdr);
+ if (!hdr) {
+ ret = -EMSGSIZE;
goto err_out;
}
@@ -250,8 +250,8 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags,
L2TP_CMD_TUNNEL_GET);
- if (IS_ERR(hdr))
- return PTR_ERR(hdr);
+ if (!hdr)
+ return -EMSGSIZE;
if (nla_put_u8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version) ||
nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
@@ -617,8 +617,8 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 pid, u32 seq, int flags
sk = tunnel->sock;
hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags, L2TP_CMD_SESSION_GET);
- if (IS_ERR(hdr))
- return PTR_ERR(hdr);
+ if (!hdr)
+ return -EMSGSIZE;
if (nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
nla_put_u32(skb, L2TP_ATTR_SESSION_ID, session->session_id) ||
diff --git a/net/netfilter/xt_limit.c b/net/netfilter/xt_limit.c
index 5c22ce8ab309..a4c1e4528cac 100644
--- a/net/netfilter/xt_limit.c
+++ b/net/netfilter/xt_limit.c
@@ -117,11 +117,11 @@ static int limit_mt_check(const struct xt_mtchk_param *par)
/* For SMP, we only want to use one set of state. */
r->master = priv;
+ /* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
+ 128. */
+ priv->prev = jiffies;
+ priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
if (r->cost == 0) {
- /* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
- 128. */
- priv->prev = jiffies;
- priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
r->credit_cap = priv->credit; /* Credits full. */
r->cost = user2credits(r->avg);
}
diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
index 1b9024ee963c..7261eb81974f 100644
--- a/net/netrom/af_netrom.c
+++ b/net/netrom/af_netrom.c
@@ -601,7 +601,7 @@ static int nr_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
if (!capable(CAP_NET_BIND_SERVICE)) {
dev_put(dev);
release_sock(sk);
- return -EACCES;
+ return -EPERM;
}
nr->user_addr = addr->fsa_digipeater[0];
nr->source_addr = addr->fsa_ax25.sax25_call;
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index e4723d31fdd5..211a21217045 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -865,7 +865,10 @@ static void qfq_update_start(struct qfq_sched *q, struct qfq_class *cl)
if (mask) {
struct qfq_group *next = qfq_ffs(q, mask);
if (qfq_gt(roundedF, next->F)) {
- cl->S = next->F;
+ if (qfq_gt(limit, next->F))
+ cl->S = next->F;
+ else /* preserve timestamp correctness */
+ cl->S = limit;
return;
}
}
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 2ded3c7fad06..72d170ca3406 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -350,6 +350,9 @@ static void reg_regdb_search(struct work_struct *work)
struct reg_regdb_search_request *request;
const struct ieee80211_regdomain *curdom, *regdom;
int i, r;
+ bool set_reg = false;
+
+ mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_regdb_search_mutex);
while (!list_empty(&reg_regdb_search_list)) {
@@ -365,9 +368,7 @@ static void reg_regdb_search(struct work_struct *work)
r = reg_copy_regd(&regdom, curdom);
if (r)
break;
- mutex_lock(&cfg80211_mutex);
- set_regdom(regdom);
- mutex_unlock(&cfg80211_mutex);
+ set_reg = true;
break;
}
}
@@ -375,6 +376,11 @@ static void reg_regdb_search(struct work_struct *work)
kfree(request);
}
mutex_unlock(&reg_regdb_search_mutex);
+
+ if (set_reg)
+ set_regdom(regdom);
+
+ mutex_unlock(&cfg80211_mutex);
}
static DECLARE_WORK(reg_regdb_work, reg_regdb_search);
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index 5a2aa17e4d3c..387848e90078 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -585,6 +585,7 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl)
xfrm_pol_hold(policy);
net->xfrm.policy_count[dir]++;
atomic_inc(&flow_cache_genid);
+ rt_genid_bump(net);
if (delpol)
__xfrm_policy_unlink(delpol, dir);
policy->index = delpol ? delpol->index : xfrm_gen_index(net, dir);
@@ -1763,7 +1764,7 @@ static struct dst_entry *make_blackhole(struct net *net, u16 family,
if (!afinfo) {
dst_release(dst_orig);
- ret = ERR_PTR(-EINVAL);
+ return ERR_PTR(-EINVAL);
} else {
ret = afinfo->blackhole_route(net, dst_orig);
}
diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c
index e75d8e47f35c..289f4bf18ff0 100644
--- a/net/xfrm/xfrm_user.c
+++ b/net/xfrm/xfrm_user.c
@@ -123,9 +123,21 @@ static inline int verify_replay(struct xfrm_usersa_info *p,
struct nlattr **attrs)
{
struct nlattr *rt = attrs[XFRMA_REPLAY_ESN_VAL];
+ struct xfrm_replay_state_esn *rs;
- if ((p->flags & XFRM_STATE_ESN) && !rt)
- return -EINVAL;
+ if (p->flags & XFRM_STATE_ESN) {
+ if (!rt)
+ return -EINVAL;
+
+ rs = nla_data(rt);
+
+ if (rs->bmp_len > XFRMA_REPLAY_ESN_MAX / sizeof(rs->bmp[0]) / 8)
+ return -EINVAL;
+
+ if (nla_len(rt) < xfrm_replay_state_esn_len(rs) &&
+ nla_len(rt) != sizeof(*rs))
+ return -EINVAL;
+ }
if (!rt)
return 0;
@@ -370,14 +382,15 @@ static inline int xfrm_replay_verify_len(struct xfrm_replay_state_esn *replay_es
struct nlattr *rp)
{
struct xfrm_replay_state_esn *up;
+ int ulen;
if (!replay_esn || !rp)
return 0;
up = nla_data(rp);
+ ulen = xfrm_replay_state_esn_len(up);
- if (xfrm_replay_state_esn_len(replay_esn) !=
- xfrm_replay_state_esn_len(up))
+ if (nla_len(rp) < ulen || xfrm_replay_state_esn_len(replay_esn) != ulen)
return -EINVAL;
return 0;
@@ -388,22 +401,28 @@ static int xfrm_alloc_replay_state_esn(struct xfrm_replay_state_esn **replay_esn
struct nlattr *rta)
{
struct xfrm_replay_state_esn *p, *pp, *up;
+ int klen, ulen;
if (!rta)
return 0;
up = nla_data(rta);
+ klen = xfrm_replay_state_esn_len(up);
+ ulen = nla_len(rta) >= klen ? klen : sizeof(*up);
- p = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
+ p = kzalloc(klen, GFP_KERNEL);
if (!p)
return -ENOMEM;
- pp = kmemdup(up, xfrm_replay_state_esn_len(up), GFP_KERNEL);
+ pp = kzalloc(klen, GFP_KERNEL);
if (!pp) {
kfree(p);
return -ENOMEM;
}
+ memcpy(p, up, ulen);
+ memcpy(pp, up, ulen);
+
*replay_esn = p;
*preplay_esn = pp;
@@ -442,10 +461,11 @@ static void copy_from_user_state(struct xfrm_state *x, struct xfrm_usersa_info *
* somehow made shareable and move it to xfrm_state.c - JHS
*
*/
-static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs)
+static void xfrm_update_ae_params(struct xfrm_state *x, struct nlattr **attrs,
+ int update_esn)
{
struct nlattr *rp = attrs[XFRMA_REPLAY_VAL];
- struct nlattr *re = attrs[XFRMA_REPLAY_ESN_VAL];
+ struct nlattr *re = update_esn ? attrs[XFRMA_REPLAY_ESN_VAL] : NULL;
struct nlattr *lt = attrs[XFRMA_LTIME_VAL];
struct nlattr *et = attrs[XFRMA_ETIMER_THRESH];
struct nlattr *rt = attrs[XFRMA_REPLAY_THRESH];
@@ -555,7 +575,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net,
goto error;
/* override default values from above */
- xfrm_update_ae_params(x, attrs);
+ xfrm_update_ae_params(x, attrs, 0);
return x;
@@ -689,6 +709,7 @@ out:
static void copy_to_user_state(struct xfrm_state *x, struct xfrm_usersa_info *p)
{
+ memset(p, 0, sizeof(*p));
memcpy(&p->id, &x->id, sizeof(p->id));
memcpy(&p->sel, &x->sel, sizeof(p->sel));
memcpy(&p->lft, &x->lft, sizeof(p->lft));
@@ -742,7 +763,7 @@ static int copy_to_user_auth(struct xfrm_algo_auth *auth, struct sk_buff *skb)
return -EMSGSIZE;
algo = nla_data(nla);
- strcpy(algo->alg_name, auth->alg_name);
+ strncpy(algo->alg_name, auth->alg_name, sizeof(algo->alg_name));
memcpy(algo->alg_key, auth->alg_key, (auth->alg_key_len + 7) / 8);
algo->alg_key_len = auth->alg_key_len;
@@ -878,6 +899,7 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
{
struct xfrm_dump_info info;
struct sk_buff *skb;
+ int err;
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
if (!skb)
@@ -888,9 +910,10 @@ static struct sk_buff *xfrm_state_netlink(struct sk_buff *in_skb,
info.nlmsg_seq = seq;
info.nlmsg_flags = 0;
- if (dump_one_state(x, 0, &info)) {
+ err = dump_one_state(x, 0, &info);
+ if (err) {
kfree_skb(skb);
- return NULL;
+ return ERR_PTR(err);
}
return skb;
@@ -1317,6 +1340,7 @@ static void copy_from_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy
static void copy_to_user_policy(struct xfrm_policy *xp, struct xfrm_userpolicy_info *p, int dir)
{
+ memset(p, 0, sizeof(*p));
memcpy(&p->sel, &xp->selector, sizeof(p->sel));
memcpy(&p->lft, &xp->lft, sizeof(p->lft));
memcpy(&p->curlft, &xp->curlft, sizeof(p->curlft));
@@ -1421,6 +1445,7 @@ static int copy_to_user_tmpl(struct xfrm_policy *xp, struct sk_buff *skb)
struct xfrm_user_tmpl *up = &vec[i];
struct xfrm_tmpl *kp = &xp->xfrm_vec[i];
+ memset(up, 0, sizeof(*up));
memcpy(&up->id, &kp->id, sizeof(up->id));
up->family = kp->encap_family;
memcpy(&up->saddr, &kp->saddr, sizeof(up->saddr));
@@ -1546,6 +1571,7 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
{
struct xfrm_dump_info info;
struct sk_buff *skb;
+ int err;
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb)
@@ -1556,9 +1582,10 @@ static struct sk_buff *xfrm_policy_netlink(struct sk_buff *in_skb,
info.nlmsg_seq = seq;
info.nlmsg_flags = 0;
- if (dump_one_policy(xp, dir, 0, &info) < 0) {
+ err = dump_one_policy(xp, dir, 0, &info);
+ if (err) {
kfree_skb(skb);
- return NULL;
+ return ERR_PTR(err);
}
return skb;
@@ -1822,7 +1849,7 @@ static int xfrm_new_ae(struct sk_buff *skb, struct nlmsghdr *nlh,
goto out;
spin_lock_bh(&x->lock);
- xfrm_update_ae_params(x, attrs);
+ xfrm_update_ae_params(x, attrs, 1);
spin_unlock_bh(&x->lock);
c.event = nlh->nlmsg_type;
diff --git a/scripts/Makefile.fwinst b/scripts/Makefile.fwinst
index c3f69ae275d1..4d908d16c035 100644
--- a/scripts/Makefile.fwinst
+++ b/scripts/Makefile.fwinst
@@ -27,7 +27,7 @@ endif
installed-mod-fw := $(addprefix $(INSTALL_FW_PATH)/,$(mod-fw))
installed-fw := $(addprefix $(INSTALL_FW_PATH)/,$(fw-shipped-all))
-installed-fw-dirs := $(sort $(dir $(installed-fw))) $(INSTALL_FW_PATH)/.
+installed-fw-dirs := $(sort $(dir $(installed-fw))) $(INSTALL_FW_PATH)/./
# Workaround for make < 3.81, where .SECONDEXPANSION doesn't work.
PHONY += $(INSTALL_FW_PATH)/$$(%) install-all-dirs
@@ -42,7 +42,7 @@ quiet_cmd_install = INSTALL $(subst $(srctree)/,,$@)
$(installed-fw-dirs):
$(call cmd,mkdir)
-$(installed-fw): $(INSTALL_FW_PATH)/%: $(obj)/% | $$(dir $(INSTALL_FW_PATH)/%)
+$(installed-fw): $(INSTALL_FW_PATH)/%: $(obj)/% | $(INSTALL_FW_PATH)/$$(dir %)
$(call cmd,install)
PHONY += __fw_install __fw_modinst FORCE
diff --git a/scripts/checksyscalls.sh b/scripts/checksyscalls.sh
index d24810fc6af6..fd8fa9aa7c4e 100755
--- a/scripts/checksyscalls.sh
+++ b/scripts/checksyscalls.sh
@@ -200,7 +200,7 @@ EOF
syscall_list() {
grep '^[0-9]' "$1" | sort -n | (
while read nr abi name entry ; do
- echo <<EOF
+ cat <<EOF
#if !defined(__NR_${name}) && !defined(__IGNORE_${name})
#warning syscall ${name} not implemented
#endif
diff --git a/scripts/coccinelle/api/memdup_user.cocci b/scripts/coccinelle/api/memdup_user.cocci
index 2efac289fd59..2b131a8a1306 100644
--- a/scripts/coccinelle/api/memdup_user.cocci
+++ b/scripts/coccinelle/api/memdup_user.cocci
@@ -51,10 +51,10 @@ statement S1,S2;
p << r.p;
@@
-coccilib.org.print_todo(p[0], "WARNING opportunity for memdep_user")
+coccilib.org.print_todo(p[0], "WARNING opportunity for memdup_user")
@script:python depends on report@
p << r.p;
@@
-coccilib.report.print_report(p[0], "WARNING opportunity for memdep_user")
+coccilib.report.print_report(p[0], "WARNING opportunity for memdup_user")
diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl
index 2fbbbc1ddea0..33689396953a 100644
--- a/scripts/kconfig/streamline_config.pl
+++ b/scripts/kconfig/streamline_config.pl
@@ -100,7 +100,7 @@ my @searchconfigs = (
},
);
-sub find_config {
+sub read_config {
foreach my $conf (@searchconfigs) {
my $file = $conf->{"file"};
@@ -115,17 +115,15 @@ sub find_config {
print STDERR "using config: '$file'\n";
- open(CIN, "$exec $file |") || die "Failed to run $exec $file";
- return;
+ open(my $infile, '-|', "$exec $file") || die "Failed to run $exec $file";
+ my @x = <$infile>;
+ close $infile;
+ return @x;
}
die "No config file found";
}
-find_config;
-
-# Read in the entire config file into config_file
-my @config_file = <CIN>;
-close CIN;
+my @config_file = read_config;
# Parse options
my $localmodconfig = 0;
@@ -135,7 +133,7 @@ GetOptions("localmodconfig" => \$localmodconfig,
"localyesconfig" => \$localyesconfig);
# Get the build source and top level Kconfig file (passed in)
-my $ksource = $ARGV[0];
+my $ksource = ($ARGV[0] ? $ARGV[0] : '.');
my $kconfig = $ARGV[1];
my $lsmod_file = $ENV{'LSMOD'};
@@ -173,8 +171,8 @@ sub read_kconfig {
$source =~ s/\$$env/$ENV{$env}/;
}
- open(KIN, "$source") || die "Can't open $kconfig";
- while (<KIN>) {
+ open(my $kinfile, '<', $source) || die "Can't open $kconfig";
+ while (<$kinfile>) {
chomp;
# Make sure that lines ending with \ continue
@@ -251,10 +249,10 @@ sub read_kconfig {
$state = "NONE";
}
}
- close(KIN);
+ close($kinfile);
# read in any configs that were found.
- foreach $kconfig (@kconfigs) {
+ foreach my $kconfig (@kconfigs) {
if (!defined($read_kconfigs{$kconfig})) {
$read_kconfigs{$kconfig} = 1;
read_kconfig($kconfig);
@@ -295,8 +293,8 @@ foreach my $makefile (@makefiles) {
my $line = "";
my %make_vars;
- open(MIN,$makefile) || die "Can't open $makefile";
- while (<MIN>) {
+ open(my $infile, '<', $makefile) || die "Can't open $makefile";
+ while (<$infile>) {
# if this line ends with a backslash, continue
chomp;
if (/^(.*)\\$/) {
@@ -343,10 +341,11 @@ foreach my $makefile (@makefiles) {
}
}
}
- close(MIN);
+ close($infile);
}
my %modules;
+my $linfile;
if (defined($lsmod_file)) {
if ( ! -f $lsmod_file) {
@@ -356,13 +355,10 @@ if (defined($lsmod_file)) {
die "$lsmod_file not found";
}
}
- if ( -x $lsmod_file) {
- # the file is executable, run it
- open(LIN, "$lsmod_file|");
- } else {
- # Just read the contents
- open(LIN, "$lsmod_file");
- }
+
+ my $otype = ( -x $lsmod_file) ? '-|' : '<';
+ open($linfile, $otype, $lsmod_file);
+
} else {
# see what modules are loaded on this system
@@ -379,16 +375,16 @@ if (defined($lsmod_file)) {
$lsmod = "lsmod";
}
- open(LIN,"$lsmod|") || die "Can not call lsmod with $lsmod";
+ open($linfile, '-|', $lsmod) || die "Can not call lsmod with $lsmod";
}
-while (<LIN>) {
+while (<$linfile>) {
next if (/^Module/); # Skip the first line.
if (/^(\S+)/) {
$modules{$1} = 1;
}
}
-close (LIN);
+close ($linfile);
# add to the configs hash all configs that are needed to enable
# a loaded module. This is a direct obj-${CONFIG_FOO} += bar.o
@@ -605,6 +601,8 @@ foreach my $line (@config_file) {
if (defined($configs{$1})) {
if ($localyesconfig) {
$setconfigs{$1} = 'y';
+ print "$1=y\n";
+ next;
} else {
$setconfigs{$1} = $2;
}
diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
index 7ed6864ef65b..df4fc23dd836 100644
--- a/scripts/mod/file2alias.c
+++ b/scripts/mod/file2alias.c
@@ -966,6 +966,21 @@ static int do_isapnp_entry(const char *filename,
}
ADD_TO_DEVTABLE("isapnp", struct isapnp_device_id, do_isapnp_entry);
+/* Looks like: "ipack:fNvNdN". */
+static int do_ipack_entry(const char *filename,
+ struct ipack_device_id *id, char *alias)
+{
+ id->vendor = TO_NATIVE(id->vendor);
+ id->device = TO_NATIVE(id->device);
+ strcpy(alias, "ipack:");
+ ADD(alias, "f", id->format != IPACK_ANY_FORMAT, id->format);
+ ADD(alias, "v", id->vendor != IPACK_ANY_ID, id->vendor);
+ ADD(alias, "d", id->device != IPACK_ANY_ID, id->device);
+ add_wildcard(alias);
+ return 1;
+}
+ADD_TO_DEVTABLE("ipack", struct ipack_device_id, do_ipack_entry);
+
/*
* Append a match expression for a single masked hex digit.
* outp points to a pointer to the character at which to append.
diff --git a/scripts/recordmcount.h b/scripts/recordmcount.h
index 54e35c1e5948..9d1421e63ff8 100644
--- a/scripts/recordmcount.h
+++ b/scripts/recordmcount.h
@@ -261,11 +261,13 @@ static unsigned get_mcountsym(Elf_Sym const *const sym0,
&sym0[Elf_r_sym(relp)];
char const *symname = &str0[w(symp->st_name)];
char const *mcount = gpfx == '_' ? "_mcount" : "mcount";
+ char const *fentry = "__fentry__";
if (symname[0] == '.')
++symname; /* ppc64 hack */
if (strcmp(mcount, symname) == 0 ||
- (altmcount && strcmp(altmcount, symname) == 0))
+ (altmcount && strcmp(altmcount, symname) == 0) ||
+ (strcmp(fentry, symname) == 0))
mcountsym = Elf_r_sym(relp);
return mcountsym;
diff --git a/scripts/sortextable.c b/scripts/sortextable.c
index 6acf83449105..f19ddc47304c 100644
--- a/scripts/sortextable.c
+++ b/scripts/sortextable.c
@@ -161,7 +161,7 @@ typedef void (*table_sort_t)(char *, int);
#define SORTEXTABLE_64
#include "sortextable.h"
-static int compare_x86_table(const void *a, const void *b)
+static int compare_relative_table(const void *a, const void *b)
{
int32_t av = (int32_t)r(a);
int32_t bv = (int32_t)r(b);
@@ -173,7 +173,7 @@ static int compare_x86_table(const void *a, const void *b)
return 0;
}
-static void sort_x86_table(char *extab_image, int image_size)
+static void sort_relative_table(char *extab_image, int image_size)
{
int i;
@@ -188,7 +188,7 @@ static void sort_x86_table(char *extab_image, int image_size)
i += 4;
}
- qsort(extab_image, image_size / 8, 8, compare_x86_table);
+ qsort(extab_image, image_size / 8, 8, compare_relative_table);
/* Now denormalize. */
i = 0;
@@ -245,9 +245,9 @@ do_file(char const *const fname)
break;
case EM_386:
case EM_X86_64:
- custom_sort = sort_x86_table;
- break;
case EM_S390:
+ custom_sort = sort_relative_table;
+ break;
case EM_MIPS:
break;
} /* end switch */
diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore
index 4d995aeaebc0..9cdec70d72b8 100644
--- a/security/apparmor/.gitignore
+++ b/security/apparmor/.gitignore
@@ -1,6 +1,5 @@
#
# Generated include files
#
-af_names.h
capability_names.h
rlim_names.h
diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c
index 3364fbf46807..6cfc6478863e 100644
--- a/security/keys/keyctl.c
+++ b/security/keys/keyctl.c
@@ -1486,7 +1486,6 @@ long keyctl_session_to_parent(void)
oldwork = NULL;
parent = me->real_parent;
- task_lock(parent);
/* the parent mustn't be init and mustn't be a kernel thread */
if (parent->pid <= 1 || !parent->mm)
goto unlock;
@@ -1530,7 +1529,6 @@ long keyctl_session_to_parent(void)
if (!ret)
newwork = NULL;
unlock:
- task_unlock(parent);
write_unlock_irq(&tasklist_lock);
rcu_read_unlock();
if (oldwork)
diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h
index c220f314709c..65f67cb0aefb 100644
--- a/security/selinux/include/xfrm.h
+++ b/security/selinux/include/xfrm.h
@@ -51,6 +51,7 @@ int selinux_xfrm_decode_session(struct sk_buff *skb, u32 *sid, int ckall);
static inline void selinux_xfrm_notify_policyload(void)
{
atomic_inc(&flow_cache_genid);
+ rt_genid_bump(&init_net);
}
#else
static inline int selinux_xfrm_enabled(void)
diff --git a/sound/oss/.gitignore b/sound/oss/.gitignore
index 7efb12b45502..12a3920d6fb6 100644
--- a/sound/oss/.gitignore
+++ b/sound/oss/.gitignore
@@ -1,4 +1,3 @@
#Ignore generated files
-maui_boot.h
pss_boot.h
trix_boot.h
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index 23b40186f9b8..07abd09e0b1d 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -34,6 +34,7 @@
#include <linux/mfd/abx500/ab8500-sysctrl.h>
#include <linux/mfd/abx500/ab8500-codec.h>
#include <linux/regulator/consumer.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -2394,9 +2395,65 @@ struct snd_soc_dai_driver ab8500_codec_dai[] = {
}
};
+static void ab8500_codec_of_probe(struct device *dev, struct device_node *np,
+ struct ab8500_codec_platform_data *codec)
+{
+ u32 value;
+
+ if (of_get_property(np, "stericsson,amic1-type-single-ended", NULL))
+ codec->amics.mic1_type = AMIC_TYPE_SINGLE_ENDED;
+ else
+ codec->amics.mic1_type = AMIC_TYPE_DIFFERENTIAL;
+
+ if (of_get_property(np, "stericsson,amic2-type-single-ended", NULL))
+ codec->amics.mic2_type = AMIC_TYPE_SINGLE_ENDED;
+ else
+ codec->amics.mic2_type = AMIC_TYPE_DIFFERENTIAL;
+
+ /* Has a non-standard Vamic been requested? */
+ if (of_get_property(np, "stericsson,amic1a-bias-vamic2", NULL))
+ codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC2;
+ else
+ codec->amics.mic1a_micbias = AMIC_MICBIAS_VAMIC1;
+
+ if (of_get_property(np, "stericsson,amic1b-bias-vamic2", NULL))
+ codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC2;
+ else
+ codec->amics.mic1b_micbias = AMIC_MICBIAS_VAMIC1;
+
+ if (of_get_property(np, "stericsson,amic2-bias-vamic1", NULL))
+ codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC1;
+ else
+ codec->amics.mic2_micbias = AMIC_MICBIAS_VAMIC2;
+
+ if (!of_property_read_u32(np, "stericsson,earpeice-cmv", &value)) {
+ switch (value) {
+ case 950 :
+ codec->ear_cmv = EAR_CMV_0_95V;
+ break;
+ case 1100 :
+ codec->ear_cmv = EAR_CMV_1_10V;
+ break;
+ case 1270 :
+ codec->ear_cmv = EAR_CMV_1_27V;
+ break;
+ case 1580 :
+ codec->ear_cmv = EAR_CMV_1_58V;
+ break;
+ default :
+ codec->ear_cmv = EAR_CMV_UNKNOWN;
+ dev_err(dev, "Unsuitable earpiece voltage found in DT\n");
+ }
+ } else {
+ dev_warn(dev, "No earpiece voltage found in DT - using default\n");
+ codec->ear_cmv = EAR_CMV_0_95V;
+ }
+}
+
static int ab8500_codec_probe(struct snd_soc_codec *codec)
{
struct device *dev = codec->dev;
+ struct device_node *np = dev->of_node;
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev);
struct ab8500_platform_data *pdata;
struct filter_control *fc;
@@ -2410,6 +2467,30 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
/* Inform SoC Core that we have our own I/O arrangements. */
codec->control_data = (void *)true;
+ if (np) {
+ if (!pdata)
+ pdata = devm_kzalloc(dev,
+ sizeof(struct ab8500_platform_data),
+ GFP_KERNEL);
+
+ if (pdata && !pdata->codec)
+ pdata->codec
+ = devm_kzalloc(dev,
+ sizeof(struct ab8500_codec_platform_data),
+ GFP_KERNEL);
+
+ if (!(pdata && pdata->codec))
+ return -ENOMEM;
+
+ ab8500_codec_of_probe(dev, np, pdata->codec);
+
+ } else {
+ if (!(pdata && pdata->codec)) {
+ dev_err(dev, "No codec platform data or DT found\n");
+ return -EINVAL;
+ }
+ }
+
status = ab8500_audio_setup_mics(codec, &pdata->codec->amics);
if (status < 0) {
pr_err("%s: Failed to setup mics (%d)!\n", __func__, status);
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
index 3fd5b29dc933..a3acb7a85f6a 100644
--- a/sound/soc/codecs/wm2000.c
+++ b/sound/soc/codecs/wm2000.c
@@ -702,7 +702,7 @@ static bool wm2000_readable_reg(struct device *dev, unsigned int reg)
}
static const struct regmap_config wm2000_regmap = {
- .reg_bits = 8,
+ .reg_bits = 16,
.val_bits = 8,
.max_register = WM2000_REG_IF_CTL,
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index 31c4d26d0359..356611d9654d 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -16,6 +16,7 @@
#include <linux/module.h>
#include <linux/io.h>
#include <linux/spi/spi.h>
+#include <linux/of.h>
#include <sound/soc.h>
#include <sound/initval.h>
@@ -56,16 +57,47 @@ static struct snd_soc_card mop500_card = {
.num_links = ARRAY_SIZE(mop500_dai_links),
};
+static int __devinit mop500_of_probe(struct platform_device *pdev,
+ struct device_node *np)
+{
+ struct device_node *codec_np, *msp_np[2];
+ int i;
+
+ msp_np[0] = of_parse_phandle(np, "stericsson,cpu-dai", 0);
+ msp_np[1] = of_parse_phandle(np, "stericsson,cpu-dai", 1);
+ codec_np = of_parse_phandle(np, "stericsson,audio-codec", 0);
+
+ if (!(msp_np[0] && msp_np[1] && codec_np)) {
+ dev_err(&pdev->dev, "Phandle missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ mop500_dai_links[i].cpu_of_node = msp_np[i];
+ mop500_dai_links[i].cpu_dai_name = NULL;
+ mop500_dai_links[i].codec_of_node = codec_np;
+ mop500_dai_links[i].codec_name = NULL;
+ }
+
+ snd_soc_of_parse_card_name(&mop500_card, "stericsson,card-name");
+
+ return 0;
+}
static int __devinit mop500_probe(struct platform_device *pdev)
{
+ struct device_node *np = pdev->dev.of_node;
int ret;
- pr_debug("%s: Enter.\n", __func__);
-
dev_dbg(&pdev->dev, "%s: Enter.\n", __func__);
mop500_card.dev = &pdev->dev;
+ if (np) {
+ ret = mop500_of_probe(pdev, np);
+ if (ret)
+ return ret;
+ }
+
dev_dbg(&pdev->dev, "%s: Card %s: Set platform drvdata.\n",
__func__, mop500_card.name);
platform_set_drvdata(pdev, &mop500_card);
@@ -83,8 +115,7 @@ static int __devinit mop500_probe(struct platform_device *pdev)
ret = snd_soc_register_card(&mop500_card);
if (ret)
dev_err(&pdev->dev,
- "Error: snd_soc_register_card failed (%d)!\n",
- ret);
+ "Error: snd_soc_register_card failed (%d)!\n", ret);
return ret;
}
@@ -97,14 +128,20 @@ static int __devexit mop500_remove(struct platform_device *pdev)
snd_soc_unregister_card(mop500_card);
mop500_ab8500_remove(mop500_card);
-
+
return 0;
}
+static const struct of_device_id snd_soc_mop500_match[] = {
+ { .compatible = "stericsson,snd-soc-mop500", },
+ {},
+};
+
static struct platform_driver snd_soc_mop500_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "snd-soc-mop500",
+ .of_match_table = snd_soc_mop500_match,
},
.probe = mop500_probe,
.remove = __devexit_p(mop500_remove),
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index 057e28ef770e..45e43b4057b0 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -830,10 +830,16 @@ static int __devexit ux500_msp_drv_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id ux500_msp_i2s_match[] = {
+ { .compatible = "stericsson,ux500-msp-i2s", },
+ {},
+};
+
static struct platform_driver msp_i2s_driver = {
.driver = {
.name = "ux500-msp-i2s",
.owner = THIS_MODULE,
+ .of_match_table = ux500_msp_i2s_match,
},
.probe = ux500_msp_drv_probe,
.remove = ux500_msp_drv_remove,
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
index eb85113d472a..e5c79ca42518 100644
--- a/sound/soc/ux500/ux500_msp_i2s.c
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -15,8 +15,10 @@
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/of.h>
#include <mach/hardware.h>
#include <mach/msp.h>
@@ -25,6 +27,9 @@
#include "ux500_msp_i2s.h"
+/* MSP1/3 Tx/Rx usage protection */
+static DEFINE_SPINLOCK(msp_rxtx_lock);
+
/* Protocol desciptors */
static const struct msp_protdesc prot_descs[] = {
{ /* I2S */
@@ -352,17 +357,23 @@ static int configure_multichannel(struct ux500_msp *msp,
static int enable_msp(struct ux500_msp *msp, struct ux500_msp_config *config)
{
- int status = 0;
+ int status = 0, retval = 0;
u32 reg_val_DMACR, reg_val_GCR;
+ unsigned long flags;
/* Check msp state whether in RUN or CONFIGURED Mode */
- if ((msp->msp_state == MSP_STATE_IDLE) && (msp->plat_init)) {
- status = msp->plat_init();
- if (status) {
- dev_err(msp->dev, "%s: ERROR: Failed to init MSP (%d)!\n",
- __func__, status);
- return status;
+ if (msp->msp_state == MSP_STATE_IDLE) {
+ spin_lock_irqsave(&msp_rxtx_lock, flags);
+ if (msp->pinctrl_rxtx_ref == 0 &&
+ !(IS_ERR(msp->pinctrl_p) || IS_ERR(msp->pinctrl_def))) {
+ retval = pinctrl_select_state(msp->pinctrl_p,
+ msp->pinctrl_def);
+ if (retval)
+ pr_err("could not set MSP defstate\n");
}
+ if (!retval)
+ msp->pinctrl_rxtx_ref++;
+ spin_unlock_irqrestore(&msp_rxtx_lock, flags);
}
/* Configure msp with protocol dependent settings */
@@ -620,7 +631,8 @@ int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
{
- int status = 0;
+ int status = 0, retval = 0;
+ unsigned long flags;
dev_dbg(msp->dev, "%s: Enter (dir = 0x%01x).\n", __func__, dir);
@@ -631,12 +643,19 @@ int ux500_msp_i2s_close(struct ux500_msp *msp, unsigned int dir)
writel((readl(msp->registers + MSP_GCR) &
(~(FRAME_GEN_ENABLE | SRG_ENABLE))),
msp->registers + MSP_GCR);
- if (msp->plat_exit)
- status = msp->plat_exit();
- if (status)
- dev_warn(msp->dev,
- "%s: WARN: ux500_msp_i2s_exit failed (%d)!\n",
- __func__, status);
+
+ spin_lock_irqsave(&msp_rxtx_lock, flags);
+ WARN_ON(!msp->pinctrl_rxtx_ref);
+ msp->pinctrl_rxtx_ref--;
+ if (msp->pinctrl_rxtx_ref == 0 &&
+ !(IS_ERR(msp->pinctrl_p) || IS_ERR(msp->pinctrl_sleep))) {
+ retval = pinctrl_select_state(msp->pinctrl_p,
+ msp->pinctrl_sleep);
+ if (retval)
+ pr_err("could not set MSP sleepstate\n");
+ }
+ spin_unlock_irqrestore(&msp_rxtx_lock, flags);
+
writel(0, msp->registers + MSP_GCR);
writel(0, msp->registers + MSP_TCF);
writel(0, msp->registers + MSP_RCF);
@@ -665,18 +684,31 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
{
struct resource *res = NULL;
struct i2s_controller *i2s_cont;
+ struct device_node *np = pdev->dev.of_node;
struct ux500_msp *msp;
- dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__,
- pdev->name, platform_data->id);
-
*msp_p = devm_kzalloc(&pdev->dev, sizeof(struct ux500_msp), GFP_KERNEL);
msp = *msp_p;
+ if (np) {
+ if (!platform_data) {
+ platform_data = devm_kzalloc(&pdev->dev,
+ sizeof(struct msp_i2s_platform_data), GFP_KERNEL);
+ if (!platform_data)
+ ret = -ENOMEM;
+ }
+ } else
+ if (!platform_data)
+ ret = -EINVAL;
+
+ if (ret)
+ goto err_res;
+
+ dev_dbg(&pdev->dev, "%s: Enter (name: %s, id: %d).\n", __func__,
+ pdev->name, platform_data->id);
+
msp->id = platform_data->id;
msp->dev = &pdev->dev;
- msp->plat_init = platform_data->msp_i2s_init;
- msp->plat_exit = platform_data->msp_i2s_exit;
msp->dma_cfg_rx = platform_data->msp_i2s_dma_rx;
msp->dma_cfg_tx = platform_data->msp_i2s_dma_tx;
@@ -713,6 +745,25 @@ int ux500_msp_i2s_init_msp(struct platform_device *pdev,
dev_dbg(&pdev->dev, "I2S device-name: '%s'\n", i2s_cont->name);
msp->i2s_cont = i2s_cont;
+ msp->pinctrl_p = pinctrl_get(msp->dev);
+ if (IS_ERR(msp->pinctrl_p))
+ dev_err(&pdev->dev, "could not get MSP pinctrl\n");
+ else {
+ msp->pinctrl_def = pinctrl_lookup_state(msp->pinctrl_p,
+ PINCTRL_STATE_DEFAULT);
+ if (IS_ERR(msp->pinctrl_def)) {
+ dev_err(&pdev->dev,
+ "could not get MSP defstate (%li)\n",
+ PTR_ERR(msp->pinctrl_def));
+ }
+ msp->pinctrl_sleep = pinctrl_lookup_state(msp->pinctrl_p,
+ PINCTRL_STATE_SLEEP);
+ if (IS_ERR(msp->pinctrl_sleep))
+ dev_err(&pdev->dev,
+ "could not get MSP idlestate (%li)\n",
+ PTR_ERR(msp->pinctrl_def));
+ }
+
return 0;
}
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h
index 2d9136da9865..1311c0df7628 100644
--- a/sound/soc/ux500/ux500_msp_i2s.h
+++ b/sound/soc/ux500/ux500_msp_i2s.h
@@ -524,14 +524,18 @@ struct ux500_msp {
struct dma_chan *rx_pipeid;
enum msp_state msp_state;
int (*transfer) (struct ux500_msp *msp, struct i2s_message *message);
- int (*plat_init) (void);
- int (*plat_exit) (void);
struct timer_list notify_timer;
int def_elem_len;
unsigned int dir_busy;
int loopback_enable;
u32 backup_regs[MAX_MSP_BACKUP_REGS];
unsigned int f_bitclk;
+ /* Pin modes */
+ struct pinctrl *pinctrl_p;
+ struct pinctrl_state *pinctrl_def;
+ struct pinctrl_state *pinctrl_sleep;
+ /* Reference Count */
+ int pinctrl_rxtx_ref;
};
struct ux500_msp_dma_params {
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index d6e2bb49c59c..060dccb9ec75 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -197,7 +197,13 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
/* no data provider, so send silence */
unsigned int offs = 0;
for (i = 0; i < ctx->packets; ++i) {
- int counts = ctx->packet_size[i];
+ int counts;
+
+ if (ctx->packet_size[i])
+ counts = ctx->packet_size[i];
+ else
+ counts = snd_usb_endpoint_next_packet_size(ep);
+
urb->iso_frame_desc[i].offset = offs * ep->stride;
urb->iso_frame_desc[i].length = counts * ep->stride;
offs += counts;
diff --git a/tools/hv/hv_get_dhcp_info.sh b/tools/hv/hv_get_dhcp_info.sh
new file mode 100755
index 000000000000..ccd3e9532764
--- /dev/null
+++ b/tools/hv/hv_get_dhcp_info.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# This example script retrieves the DHCP state of a given interface.
+# In the interest of keeping the KVP daemon code free of distro specific
+# information; the kvp daemon code invokes this external script to gather
+# DHCP setting for the specific interface.
+#
+# Input: Name of the interface
+#
+# Output: The script prints the string "Enabled" to stdout to indicate
+# that DHCP is enabled on the interface. If DHCP is not enabled,
+# the script prints the string "Disabled" to stdout.
+#
+# Each Distro is expected to implement this script in a distro specific
+# fashion. For instance on Distros that ship with Network Manager enabled,
+# this script can be based on the Network Manager APIs for retrieving DHCP
+# information.
+
+if_file="/etc/sysconfig/network-scripts/ifcfg-"$1
+
+dhcp=$(grep "dhcp" $if_file 2>/dev/null)
+
+if [ "$dhcp" != "" ];
+then
+echo "Enabled"
+else
+echo "Disabled"
+fi
diff --git a/tools/hv/hv_get_dns_info.sh b/tools/hv/hv_get_dns_info.sh
new file mode 100755
index 000000000000..058c17b46ffc
--- /dev/null
+++ b/tools/hv/hv_get_dns_info.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+# This example script parses /etc/resolv.conf to retrive DNS information.
+# In the interest of keeping the KVP daemon code free of distro specific
+# information; the kvp daemon code invokes this external script to gather
+# DNS information.
+# This script is expected to print the nameserver values to stdout.
+# Each Distro is expected to implement this script in a distro specific
+# fashion. For instance on Distros that ship with Network Manager enabled,
+# this script can be based on the Network Manager APIs for retrieving DNS
+# entries.
+
+cat /etc/resolv.conf 2>/dev/null | awk '/^nameserver/ { print $2 }'
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index d9834b362943..5959affd8820 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <ctype.h>
#include <errno.h>
#include <arpa/inet.h>
#include <linux/connector.h>
@@ -41,6 +42,7 @@
#include <syslog.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <dirent.h>
/*
* KVP protocol: The user mode component first registers with the
@@ -68,25 +70,39 @@ enum key_index {
ProcessorArchitecture
};
+
+enum {
+ IPADDR = 0,
+ NETMASK,
+ GATEWAY,
+ DNS
+};
+
static char kvp_send_buffer[4096];
-static char kvp_recv_buffer[4096];
+static char kvp_recv_buffer[4096 * 2];
static struct sockaddr_nl addr;
+static int in_hand_shake = 1;
static char *os_name = "";
static char *os_major = "";
static char *os_minor = "";
static char *processor_arch;
static char *os_build;
-static char *lic_version;
+static char *lic_version = "Unknown version";
static struct utsname uts_buf;
+/*
+ * The location of the interface configuration file.
+ */
+
+#define KVP_CONFIG_LOC "/var/opt/"
#define MAX_FILE_NAME 100
#define ENTRIES_PER_BLOCK 50
struct kvp_record {
- __u8 key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
- __u8 value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
+ char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
+ char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
};
struct kvp_file_state {
@@ -94,7 +110,7 @@ struct kvp_file_state {
int num_blocks;
struct kvp_record *records;
int num_records;
- __u8 fname[MAX_FILE_NAME];
+ char fname[MAX_FILE_NAME];
};
static struct kvp_file_state kvp_file_info[KVP_POOL_COUNT];
@@ -106,7 +122,7 @@ static void kvp_acquire_lock(int pool)
if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) {
syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool);
- exit(-1);
+ exit(EXIT_FAILURE);
}
}
@@ -118,7 +134,7 @@ static void kvp_release_lock(int pool)
if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) {
perror("fcntl");
syslog(LOG_ERR, "Failed to release the lock pool: %d", pool);
- exit(-1);
+ exit(EXIT_FAILURE);
}
}
@@ -137,14 +153,19 @@ static void kvp_update_file(int pool)
if (!filep) {
kvp_release_lock(pool);
syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
- exit(-1);
+ exit(EXIT_FAILURE);
}
bytes_written = fwrite(kvp_file_info[pool].records,
sizeof(struct kvp_record),
kvp_file_info[pool].num_records, filep);
- fflush(filep);
+ if (ferror(filep) || fclose(filep)) {
+ kvp_release_lock(pool);
+ syslog(LOG_ERR, "Failed to write file, pool: %d", pool);
+ exit(EXIT_FAILURE);
+ }
+
kvp_release_lock(pool);
}
@@ -163,14 +184,19 @@ static void kvp_update_mem_state(int pool)
if (!filep) {
kvp_release_lock(pool);
syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
- exit(-1);
+ exit(EXIT_FAILURE);
}
- while (!feof(filep)) {
+ for (;;) {
readp = &record[records_read];
records_read += fread(readp, sizeof(struct kvp_record),
ENTRIES_PER_BLOCK * num_blocks,
filep);
+ if (ferror(filep)) {
+ syslog(LOG_ERR, "Failed to read file, pool: %d", pool);
+ exit(EXIT_FAILURE);
+ }
+
if (!feof(filep)) {
/*
* We have more data to read.
@@ -180,7 +206,7 @@ static void kvp_update_mem_state(int pool)
if (record == NULL) {
syslog(LOG_ERR, "malloc failed");
- exit(-1);
+ exit(EXIT_FAILURE);
}
continue;
}
@@ -191,14 +217,15 @@ static void kvp_update_mem_state(int pool)
kvp_file_info[pool].records = record;
kvp_file_info[pool].num_records = records_read;
+ fclose(filep);
kvp_release_lock(pool);
}
static int kvp_file_init(void)
{
- int ret, fd;
+ int fd;
FILE *filep;
size_t records_read;
- __u8 *fname;
+ char *fname;
struct kvp_record *record;
struct kvp_record *readp;
int num_blocks;
@@ -208,7 +235,7 @@ static int kvp_file_init(void)
if (access("/var/opt/hyperv", F_OK)) {
if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) {
syslog(LOG_ERR, " Failed to create /var/opt/hyperv");
- exit(-1);
+ exit(EXIT_FAILURE);
}
}
@@ -232,12 +259,18 @@ static int kvp_file_init(void)
fclose(filep);
return 1;
}
- while (!feof(filep)) {
+ for (;;) {
readp = &record[records_read];
records_read += fread(readp, sizeof(struct kvp_record),
ENTRIES_PER_BLOCK,
filep);
+ if (ferror(filep)) {
+ syslog(LOG_ERR, "Failed to read file, pool: %d",
+ i);
+ exit(EXIT_FAILURE);
+ }
+
if (!feof(filep)) {
/*
* We have more data to read.
@@ -311,7 +344,6 @@ static int kvp_key_add_or_modify(int pool, __u8 *key, int key_size, __u8 *value,
int value_size)
{
int i;
- int j, k;
int num_records;
struct kvp_record *record;
int num_blocks;
@@ -394,7 +426,7 @@ static int kvp_get_value(int pool, __u8 *key, int key_size, __u8 *value,
return 1;
}
-static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
+static int kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
__u8 *value, int value_size)
{
struct kvp_record *record;
@@ -406,16 +438,12 @@ static void kvp_pool_enumerate(int pool, int index, __u8 *key, int key_size,
record = kvp_file_info[pool].records;
if (index >= kvp_file_info[pool].num_records) {
- /*
- * This is an invalid index; terminate enumeration;
- * - a NULL value will do the trick.
- */
- strcpy(value, "");
- return;
+ return 1;
}
memcpy(key, record[index].key, key_size);
memcpy(value, record[index].value, value_size);
+ return 0;
}
@@ -426,6 +454,7 @@ void kvp_get_os_info(void)
uname(&uts_buf);
os_build = uts_buf.release;
+ os_name = uts_buf.sysname;
processor_arch = uts_buf.machine;
/*
@@ -437,20 +466,70 @@ void kvp_get_os_info(void)
if (p)
*p = '\0';
+ /*
+ * Parse the /etc/os-release file if present:
+ * http://www.freedesktop.org/software/systemd/man/os-release.html
+ */
+ file = fopen("/etc/os-release", "r");
+ if (file != NULL) {
+ while (fgets(buf, sizeof(buf), file)) {
+ char *value, *q;
+
+ /* Ignore comments */
+ if (buf[0] == '#')
+ continue;
+
+ /* Split into name=value */
+ p = strchr(buf, '=');
+ if (!p)
+ continue;
+ *p++ = 0;
+
+ /* Remove quotes and newline; un-escape */
+ value = p;
+ q = p;
+ while (*p) {
+ if (*p == '\\') {
+ ++p;
+ if (!*p)
+ break;
+ *q++ = *p++;
+ } else if (*p == '\'' || *p == '"' ||
+ *p == '\n') {
+ ++p;
+ } else {
+ *q++ = *p++;
+ }
+ }
+ *q = 0;
+
+ if (!strcmp(buf, "NAME")) {
+ p = strdup(value);
+ if (!p)
+ break;
+ os_name = p;
+ } else if (!strcmp(buf, "VERSION_ID")) {
+ p = strdup(value);
+ if (!p)
+ break;
+ os_major = p;
+ }
+ }
+ fclose(file);
+ return;
+ }
+
+ /* Fallback for older RH/SUSE releases */
file = fopen("/etc/SuSE-release", "r");
if (file != NULL)
goto kvp_osinfo_found;
file = fopen("/etc/redhat-release", "r");
if (file != NULL)
goto kvp_osinfo_found;
- /*
- * Add code for other supported platforms.
- */
/*
* We don't have information about the os.
*/
- os_name = uts_buf.sysname;
return;
kvp_osinfo_found:
@@ -494,82 +573,458 @@ done:
return;
}
+
+
+/*
+ * Retrieve an interface name corresponding to the specified guid.
+ * If there is a match, the function returns a pointer
+ * to the interface name and if not, a NULL is returned.
+ * If a match is found, the caller is responsible for
+ * freeing the memory.
+ */
+
+static char *kvp_get_if_name(char *guid)
+{
+ DIR *dir;
+ struct dirent *entry;
+ FILE *file;
+ char *p, *q, *x;
+ char *if_name = NULL;
+ char buf[256];
+ char *kvp_net_dir = "/sys/class/net/";
+ char dev_id[256];
+
+ dir = opendir(kvp_net_dir);
+ if (dir == NULL)
+ return NULL;
+
+ snprintf(dev_id, sizeof(dev_id), "%s", kvp_net_dir);
+ q = dev_id + strlen(kvp_net_dir);
+
+ while ((entry = readdir(dir)) != NULL) {
+ /*
+ * Set the state for the next pass.
+ */
+ *q = '\0';
+ strcat(dev_id, entry->d_name);
+ strcat(dev_id, "/device/device_id");
+
+ file = fopen(dev_id, "r");
+ if (file == NULL)
+ continue;
+
+ p = fgets(buf, sizeof(buf), file);
+ if (p) {
+ x = strchr(p, '\n');
+ if (x)
+ *x = '\0';
+
+ if (!strcmp(p, guid)) {
+ /*
+ * Found the guid match; return the interface
+ * name. The caller will free the memory.
+ */
+ if_name = strdup(entry->d_name);
+ fclose(file);
+ break;
+ }
+ }
+ fclose(file);
+ }
+
+ closedir(dir);
+ return if_name;
+}
+
+/*
+ * Retrieve the MAC address given the interface name.
+ */
+
+static char *kvp_if_name_to_mac(char *if_name)
+{
+ FILE *file;
+ char *p, *x;
+ char buf[256];
+ char addr_file[256];
+ int i;
+ char *mac_addr = NULL;
+
+ snprintf(addr_file, sizeof(addr_file), "%s%s%s", "/sys/class/net/",
+ if_name, "/address");
+
+ file = fopen(addr_file, "r");
+ if (file == NULL)
+ return NULL;
+
+ p = fgets(buf, sizeof(buf), file);
+ if (p) {
+ x = strchr(p, '\n');
+ if (x)
+ *x = '\0';
+ for (i = 0; i < strlen(p); i++)
+ p[i] = toupper(p[i]);
+ mac_addr = strdup(p);
+ }
+
+ fclose(file);
+ return mac_addr;
+}
+
+
+/*
+ * Retrieve the interface name given tha MAC address.
+ */
+
+static char *kvp_mac_to_if_name(char *mac)
+{
+ DIR *dir;
+ struct dirent *entry;
+ FILE *file;
+ char *p, *q, *x;
+ char *if_name = NULL;
+ char buf[256];
+ char *kvp_net_dir = "/sys/class/net/";
+ char dev_id[256];
+ int i;
+
+ dir = opendir(kvp_net_dir);
+ if (dir == NULL)
+ return NULL;
+
+ snprintf(dev_id, sizeof(dev_id), kvp_net_dir);
+ q = dev_id + strlen(kvp_net_dir);
+
+ while ((entry = readdir(dir)) != NULL) {
+ /*
+ * Set the state for the next pass.
+ */
+ *q = '\0';
+
+ strcat(dev_id, entry->d_name);
+ strcat(dev_id, "/address");
+
+ file = fopen(dev_id, "r");
+ if (file == NULL)
+ continue;
+
+ p = fgets(buf, sizeof(buf), file);
+ if (p) {
+ x = strchr(p, '\n');
+ if (x)
+ *x = '\0';
+
+ for (i = 0; i < strlen(p); i++)
+ p[i] = toupper(p[i]);
+
+ if (!strcmp(p, mac)) {
+ /*
+ * Found the MAC match; return the interface
+ * name. The caller will free the memory.
+ */
+ if_name = strdup(entry->d_name);
+ fclose(file);
+ break;
+ }
+ }
+ fclose(file);
+ }
+
+ closedir(dir);
+ return if_name;
+}
+
+
+static void kvp_process_ipconfig_file(char *cmd,
+ char *config_buf, int len,
+ int element_size, int offset)
+{
+ char buf[256];
+ char *p;
+ char *x;
+ FILE *file;
+
+ /*
+ * First execute the command.
+ */
+ file = popen(cmd, "r");
+ if (file == NULL)
+ return;
+
+ if (offset == 0)
+ memset(config_buf, 0, len);
+ while ((p = fgets(buf, sizeof(buf), file)) != NULL) {
+ if ((len - strlen(config_buf)) < (element_size + 1))
+ break;
+
+ x = strchr(p, '\n');
+ *x = '\0';
+ strcat(config_buf, p);
+ strcat(config_buf, ";");
+ }
+ pclose(file);
+}
+
+static void kvp_get_ipconfig_info(char *if_name,
+ struct hv_kvp_ipaddr_value *buffer)
+{
+ char cmd[512];
+ char dhcp_info[128];
+ char *p;
+ FILE *file;
+
+ /*
+ * Get the address of default gateway (ipv4).
+ */
+ sprintf(cmd, "%s %s", "ip route show dev", if_name);
+ strcat(cmd, " | awk '/default/ {print $3 }'");
+
+ /*
+ * Execute the command to gather gateway info.
+ */
+ kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,
+ (MAX_GATEWAY_SIZE * 2), INET_ADDRSTRLEN, 0);
+
+ /*
+ * Get the address of default gateway (ipv6).
+ */
+ sprintf(cmd, "%s %s", "ip -f inet6 route show dev", if_name);
+ strcat(cmd, " | awk '/default/ {print $3 }'");
+
+ /*
+ * Execute the command to gather gateway info (ipv6).
+ */
+ kvp_process_ipconfig_file(cmd, (char *)buffer->gate_way,
+ (MAX_GATEWAY_SIZE * 2), INET6_ADDRSTRLEN, 1);
+
+
+ /*
+ * Gather the DNS state.
+ * Since there is no standard way to get this information
+ * across various distributions of interest; we just invoke
+ * an external script that needs to be ported across distros
+ * of interest.
+ *
+ * Following is the expected format of the information from the script:
+ *
+ * ipaddr1 (nameserver1)
+ * ipaddr2 (nameserver2)
+ * .
+ * .
+ */
+
+ sprintf(cmd, "%s", "hv_get_dns_info");
+
+ /*
+ * Execute the command to gather DNS info.
+ */
+ kvp_process_ipconfig_file(cmd, (char *)buffer->dns_addr,
+ (MAX_IP_ADDR_SIZE * 2), INET_ADDRSTRLEN, 0);
+
+ /*
+ * Gather the DHCP state.
+ * We will gather this state by invoking an external script.
+ * The parameter to the script is the interface name.
+ * Here is the expected output:
+ *
+ * Enabled: DHCP enabled.
+ */
+
+ sprintf(cmd, "%s %s", "hv_get_dhcp_info", if_name);
+
+ file = popen(cmd, "r");
+ if (file == NULL)
+ return;
+
+ p = fgets(dhcp_info, sizeof(dhcp_info), file);
+ if (p == NULL) {
+ pclose(file);
+ return;
+ }
+
+ if (!strncmp(p, "Enabled", 7))
+ buffer->dhcp_enabled = 1;
+ else
+ buffer->dhcp_enabled = 0;
+
+ pclose(file);
+}
+
+
+static unsigned int hweight32(unsigned int *w)
+{
+ unsigned int res = *w - ((*w >> 1) & 0x55555555);
+ res = (res & 0x33333333) + ((res >> 2) & 0x33333333);
+ res = (res + (res >> 4)) & 0x0F0F0F0F;
+ res = res + (res >> 8);
+ return (res + (res >> 16)) & 0x000000FF;
+}
+
+static int kvp_process_ip_address(void *addrp,
+ int family, char *buffer,
+ int length, int *offset)
+{
+ struct sockaddr_in *addr;
+ struct sockaddr_in6 *addr6;
+ int addr_length;
+ char tmp[50];
+ const char *str;
+
+ if (family == AF_INET) {
+ addr = (struct sockaddr_in *)addrp;
+ str = inet_ntop(family, &addr->sin_addr, tmp, 50);
+ addr_length = INET_ADDRSTRLEN;
+ } else {
+ addr6 = (struct sockaddr_in6 *)addrp;
+ str = inet_ntop(family, &addr6->sin6_addr.s6_addr, tmp, 50);
+ addr_length = INET6_ADDRSTRLEN;
+ }
+
+ if ((length - *offset) < addr_length + 1)
+ return HV_E_FAIL;
+ if (str == NULL) {
+ strcpy(buffer, "inet_ntop failed\n");
+ return HV_E_FAIL;
+ }
+ if (*offset == 0)
+ strcpy(buffer, tmp);
+ else
+ strcat(buffer, tmp);
+ strcat(buffer, ";");
+
+ *offset += strlen(str) + 1;
+ return 0;
+}
+
static int
-kvp_get_ip_address(int family, char *buffer, int length)
+kvp_get_ip_info(int family, char *if_name, int op,
+ void *out_buffer, int length)
{
struct ifaddrs *ifap;
struct ifaddrs *curp;
- int ipv4_len = strlen("255.255.255.255") + 1;
- int ipv6_len = strlen("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")+1;
int offset = 0;
- const char *str;
- char tmp[50];
+ int sn_offset = 0;
int error = 0;
-
+ char *buffer;
+ struct hv_kvp_ipaddr_value *ip_buffer;
+ char cidr_mask[5]; /* /xyz */
+ int weight;
+ int i;
+ unsigned int *w;
+ char *sn_str;
+ struct sockaddr_in6 *addr6;
+
+ if (op == KVP_OP_ENUMERATE) {
+ buffer = out_buffer;
+ } else {
+ ip_buffer = out_buffer;
+ buffer = (char *)ip_buffer->ip_addr;
+ ip_buffer->addr_family = 0;
+ }
/*
* On entry into this function, the buffer is capable of holding the
- * maximum key value (2048 bytes).
+ * maximum key value.
*/
if (getifaddrs(&ifap)) {
strcpy(buffer, "getifaddrs failed\n");
- return 1;
+ return HV_E_FAIL;
}
curp = ifap;
while (curp != NULL) {
- if ((curp->ifa_addr != NULL) &&
- (curp->ifa_addr->sa_family == family)) {
- if (family == AF_INET) {
- struct sockaddr_in *addr =
- (struct sockaddr_in *) curp->ifa_addr;
-
- str = inet_ntop(family, &addr->sin_addr,
- tmp, 50);
- if (str == NULL) {
- strcpy(buffer, "inet_ntop failed\n");
- error = 1;
- goto getaddr_done;
- }
- if (offset == 0)
- strcpy(buffer, tmp);
- else
- strcat(buffer, tmp);
- strcat(buffer, ";");
+ if (curp->ifa_addr == NULL) {
+ curp = curp->ifa_next;
+ continue;
+ }
- offset += strlen(str) + 1;
- if ((length - offset) < (ipv4_len + 1))
- goto getaddr_done;
+ if ((if_name != NULL) &&
+ (strncmp(curp->ifa_name, if_name, strlen(if_name)))) {
+ /*
+ * We want info about a specific interface;
+ * just continue.
+ */
+ curp = curp->ifa_next;
+ continue;
+ }
- } else {
+ /*
+ * We only support two address families: AF_INET and AF_INET6.
+ * If a family value of 0 is specified, we collect both
+ * supported address families; if not we gather info on
+ * the specified address family.
+ */
+ if ((family != 0) && (curp->ifa_addr->sa_family != family)) {
+ curp = curp->ifa_next;
+ continue;
+ }
+ if ((curp->ifa_addr->sa_family != AF_INET) &&
+ (curp->ifa_addr->sa_family != AF_INET6)) {
+ curp = curp->ifa_next;
+ continue;
+ }
+ if (op == KVP_OP_GET_IP_INFO) {
/*
- * We only support AF_INET and AF_INET6
- * and the list of addresses is separated by a ";".
+ * Gather info other than the IP address.
+ * IP address info will be gathered later.
*/
- struct sockaddr_in6 *addr =
- (struct sockaddr_in6 *) curp->ifa_addr;
-
- str = inet_ntop(family,
- &addr->sin6_addr.s6_addr,
- tmp, 50);
- if (str == NULL) {
- strcpy(buffer, "inet_ntop failed\n");
- error = 1;
- goto getaddr_done;
- }
- if (offset == 0)
- strcpy(buffer, tmp);
- else
- strcat(buffer, tmp);
- strcat(buffer, ";");
- offset += strlen(str) + 1;
- if ((length - offset) < (ipv6_len + 1))
- goto getaddr_done;
+ if (curp->ifa_addr->sa_family == AF_INET) {
+ ip_buffer->addr_family |= ADDR_FAMILY_IPV4;
+ /*
+ * Get subnet info.
+ */
+ error = kvp_process_ip_address(
+ curp->ifa_netmask,
+ AF_INET,
+ (char *)
+ ip_buffer->sub_net,
+ length,
+ &sn_offset);
+ if (error)
+ goto gather_ipaddr;
+ } else {
+ ip_buffer->addr_family |= ADDR_FAMILY_IPV6;
+ /*
+ * Get subnet info in CIDR format.
+ */
+ weight = 0;
+ sn_str = (char *)ip_buffer->sub_net;
+ addr6 = (struct sockaddr_in6 *)
+ curp->ifa_netmask;
+ w = addr6->sin6_addr.s6_addr32;
+
+ for (i = 0; i < 4; i++)
+ weight += hweight32(&w[i]);
+
+ sprintf(cidr_mask, "/%d", weight);
+ if ((length - sn_offset) <
+ (strlen(cidr_mask) + 1))
+ goto gather_ipaddr;
+
+ if (sn_offset == 0)
+ strcpy(sn_str, cidr_mask);
+ else
+ strcat(sn_str, cidr_mask);
+ strcat((char *)ip_buffer->sub_net, ";");
+ sn_offset += strlen(sn_str) + 1;
}
+ /*
+ * Collect other ip related configuration info.
+ */
+
+ kvp_get_ipconfig_info(if_name, ip_buffer);
}
+
+gather_ipaddr:
+ error = kvp_process_ip_address(curp->ifa_addr,
+ curp->ifa_addr->sa_family,
+ buffer,
+ length, &offset);
+ if (error)
+ goto getaddr_done;
+
curp = curp->ifa_next;
}
@@ -579,6 +1034,315 @@ getaddr_done:
}
+static int expand_ipv6(char *addr, int type)
+{
+ int ret;
+ struct in6_addr v6_addr;
+
+ ret = inet_pton(AF_INET6, addr, &v6_addr);
+
+ if (ret != 1) {
+ if (type == NETMASK)
+ return 1;
+ return 0;
+ }
+
+ sprintf(addr, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:"
+ "%02x%02x:%02x%02x:%02x%02x",
+ (int)v6_addr.s6_addr[0], (int)v6_addr.s6_addr[1],
+ (int)v6_addr.s6_addr[2], (int)v6_addr.s6_addr[3],
+ (int)v6_addr.s6_addr[4], (int)v6_addr.s6_addr[5],
+ (int)v6_addr.s6_addr[6], (int)v6_addr.s6_addr[7],
+ (int)v6_addr.s6_addr[8], (int)v6_addr.s6_addr[9],
+ (int)v6_addr.s6_addr[10], (int)v6_addr.s6_addr[11],
+ (int)v6_addr.s6_addr[12], (int)v6_addr.s6_addr[13],
+ (int)v6_addr.s6_addr[14], (int)v6_addr.s6_addr[15]);
+
+ return 1;
+
+}
+
+static int is_ipv4(char *addr)
+{
+ int ret;
+ struct in_addr ipv4_addr;
+
+ ret = inet_pton(AF_INET, addr, &ipv4_addr);
+
+ if (ret == 1)
+ return 1;
+ return 0;
+}
+
+static int parse_ip_val_buffer(char *in_buf, int *offset,
+ char *out_buf, int out_len)
+{
+ char *x;
+ char *start;
+
+ /*
+ * in_buf has sequence of characters that are seperated by
+ * the character ';'. The last sequence does not have the
+ * terminating ";" character.
+ */
+ start = in_buf + *offset;
+
+ x = strchr(start, ';');
+ if (x)
+ *x = 0;
+ else
+ x = start + strlen(start);
+
+ if (strlen(start) != 0) {
+ int i = 0;
+ /*
+ * Get rid of leading spaces.
+ */
+ while (start[i] == ' ')
+ i++;
+
+ if ((x - start) <= out_len) {
+ strcpy(out_buf, (start + i));
+ *offset += (x - start) + 1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int kvp_write_file(FILE *f, char *s1, char *s2, char *s3)
+{
+ int ret;
+
+ ret = fprintf(f, "%s%s%s%s\n", s1, s2, "=", s3);
+
+ if (ret < 0)
+ return HV_E_FAIL;
+
+ return 0;
+}
+
+
+static int process_ip_string(FILE *f, char *ip_string, int type)
+{
+ int error = 0;
+ char addr[INET6_ADDRSTRLEN];
+ int i = 0;
+ int j = 0;
+ char str[256];
+ char sub_str[10];
+ int offset = 0;
+
+ memset(addr, 0, sizeof(addr));
+
+ while (parse_ip_val_buffer(ip_string, &offset, addr,
+ (MAX_IP_ADDR_SIZE * 2))) {
+
+ sub_str[0] = 0;
+ if (is_ipv4(addr)) {
+ switch (type) {
+ case IPADDR:
+ snprintf(str, sizeof(str), "%s", "IPADDR");
+ break;
+ case NETMASK:
+ snprintf(str, sizeof(str), "%s", "NETMASK");
+ break;
+ case GATEWAY:
+ snprintf(str, sizeof(str), "%s", "GATEWAY");
+ break;
+ case DNS:
+ snprintf(str, sizeof(str), "%s", "DNS");
+ break;
+ }
+ if (i != 0) {
+ if (type != DNS) {
+ snprintf(sub_str, sizeof(sub_str),
+ "_%d", i++);
+ } else {
+ snprintf(sub_str, sizeof(sub_str),
+ "%d", ++i);
+ }
+ } else if (type == DNS) {
+ snprintf(sub_str, sizeof(sub_str), "%d", ++i);
+ }
+
+
+ } else if (expand_ipv6(addr, type)) {
+ switch (type) {
+ case IPADDR:
+ snprintf(str, sizeof(str), "%s", "IPV6ADDR");
+ break;
+ case NETMASK:
+ snprintf(str, sizeof(str), "%s", "IPV6NETMASK");
+ break;
+ case GATEWAY:
+ snprintf(str, sizeof(str), "%s",
+ "IPV6_DEFAULTGW");
+ break;
+ case DNS:
+ snprintf(str, sizeof(str), "%s", "DNS");
+ break;
+ }
+ if ((j != 0) || (type == DNS)) {
+ if (type != DNS) {
+ snprintf(sub_str, sizeof(sub_str),
+ "_%d", j++);
+ } else {
+ snprintf(sub_str, sizeof(sub_str),
+ "%d", ++i);
+ }
+ } else if (type == DNS) {
+ snprintf(sub_str, sizeof(sub_str),
+ "%d", ++i);
+ }
+ } else {
+ return HV_INVALIDARG;
+ }
+
+ error = kvp_write_file(f, str, sub_str, addr);
+ if (error)
+ return error;
+ memset(addr, 0, sizeof(addr));
+ }
+
+ return 0;
+}
+
+static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
+{
+ int error = 0;
+ char if_file[128];
+ FILE *file;
+ char cmd[512];
+ char *mac_addr;
+
+ /*
+ * Set the configuration for the specified interface with
+ * the information provided. Since there is no standard
+ * way to configure an interface, we will have an external
+ * script that does the job of configuring the interface and
+ * flushing the configuration.
+ *
+ * The parameters passed to this external script are:
+ * 1. A configuration file that has the specified configuration.
+ *
+ * We will embed the name of the interface in the configuration
+ * file: ifcfg-ethx (where ethx is the interface name).
+ *
+ * The information provided here may be more than what is needed
+ * in a given distro to configure the interface and so are free
+ * ignore information that may not be relevant.
+ *
+ * Here is the format of the ip configuration file:
+ *
+ * HWADDR=macaddr
+ * IF_NAME=interface name
+ * DHCP=yes (This is optional; if yes, DHCP is configured)
+ *
+ * IPADDR=ipaddr1
+ * IPADDR_1=ipaddr2
+ * IPADDR_x=ipaddry (where y = x + 1)
+ *
+ * NETMASK=netmask1
+ * NETMASK_x=netmasky (where y = x + 1)
+ *
+ * GATEWAY=ipaddr1
+ * GATEWAY_x=ipaddry (where y = x + 1)
+ *
+ * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
+ *
+ * IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
+ * tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
+ * IPV6NETMASK.
+ *
+ * The host can specify multiple ipv4 and ipv6 addresses to be
+ * configured for the interface. Furthermore, the configuration
+ * needs to be persistent. A subsequent GET call on the interface
+ * is expected to return the configuration that is set via the SET
+ * call.
+ */
+
+ snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC,
+ "hyperv/ifcfg-", if_name);
+
+ file = fopen(if_file, "w");
+
+ if (file == NULL) {
+ syslog(LOG_ERR, "Failed to open config file");
+ return HV_E_FAIL;
+ }
+
+ /*
+ * First write out the MAC address.
+ */
+
+ mac_addr = kvp_if_name_to_mac(if_name);
+ if (mac_addr == NULL) {
+ error = HV_E_FAIL;
+ goto setval_error;
+ }
+
+ error = kvp_write_file(file, "HWADDR", "", mac_addr);
+ if (error)
+ goto setval_error;
+
+ error = kvp_write_file(file, "IF_NAME", "", if_name);
+ if (error)
+ goto setval_error;
+
+ if (new_val->dhcp_enabled) {
+ error = kvp_write_file(file, "DHCP", "", "yes");
+ if (error)
+ goto setval_error;
+
+ /*
+ * We are done!.
+ */
+ goto setval_done;
+ }
+
+ /*
+ * Write the configuration for ipaddress, netmask, gateway and
+ * name servers.
+ */
+
+ error = process_ip_string(file, (char *)new_val->ip_addr, IPADDR);
+ if (error)
+ goto setval_error;
+
+ error = process_ip_string(file, (char *)new_val->sub_net, NETMASK);
+ if (error)
+ goto setval_error;
+
+ error = process_ip_string(file, (char *)new_val->gate_way, GATEWAY);
+ if (error)
+ goto setval_error;
+
+ error = process_ip_string(file, (char *)new_val->dns_addr, DNS);
+ if (error)
+ goto setval_error;
+
+setval_done:
+ free(mac_addr);
+ fclose(file);
+
+ /*
+ * Now that we have populated the configuration file,
+ * invoke the external script to do its magic.
+ */
+
+ snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file);
+ system(cmd);
+ return 0;
+
+setval_error:
+ syslog(LOG_ERR, "Failed to write config file");
+ free(mac_addr);
+ fclose(file);
+ return error;
+}
+
+
static int
kvp_get_domain_name(char *buffer, int length)
{
@@ -646,6 +1410,10 @@ int main(void)
char *p;
char *key_value;
char *key_name;
+ int op;
+ int pool;
+ char *if_name;
+ struct hv_kvp_ipaddr_value *kvp_ip_val;
daemon(1, 0);
openlog("KVP", 0, LOG_USER);
@@ -657,13 +1425,13 @@ int main(void)
if (kvp_file_init()) {
syslog(LOG_ERR, "Failed to initialize the pools");
- exit(-1);
+ exit(EXIT_FAILURE);
}
fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (fd < 0) {
syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
- exit(-1);
+ exit(EXIT_FAILURE);
}
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
@@ -675,7 +1443,7 @@ int main(void)
if (error < 0) {
syslog(LOG_ERR, "bind failed; error:%d", error);
close(fd);
- exit(-1);
+ exit(EXIT_FAILURE);
}
sock_opt = addr.nl_groups;
setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt));
@@ -687,7 +1455,7 @@ int main(void)
message->id.val = CN_KVP_VAL;
hv_msg = (struct hv_kvp_msg *)message->data;
- hv_msg->kvp_hdr.operation = KVP_OP_REGISTER;
+ hv_msg->kvp_hdr.operation = KVP_OP_REGISTER1;
message->ack = 0;
message->len = sizeof(struct hv_kvp_msg);
@@ -695,7 +1463,7 @@ int main(void)
if (len < 0) {
syslog(LOG_ERR, "netlink_send failed; error:%d", len);
close(fd);
- exit(-1);
+ exit(EXIT_FAILURE);
}
pfd.fd = fd;
@@ -721,12 +1489,21 @@ int main(void)
incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
- switch (hv_msg->kvp_hdr.operation) {
- case KVP_OP_REGISTER:
+ /*
+ * We will use the KVP header information to pass back
+ * the error from this daemon. So, first copy the state
+ * and set the error code to success.
+ */
+ op = hv_msg->kvp_hdr.operation;
+ pool = hv_msg->kvp_hdr.pool;
+ hv_msg->error = HV_S_OK;
+
+ if ((in_hand_shake) && (op == KVP_OP_REGISTER1)) {
/*
* Driver is registering with us; stash away the version
* information.
*/
+ in_hand_shake = 0;
p = (char *)hv_msg->body.kvp_register.version;
lic_version = malloc(strlen(p) + 1);
if (lic_version) {
@@ -737,44 +1514,82 @@ int main(void)
syslog(LOG_ERR, "malloc failed");
}
continue;
+ }
- /*
- * The current protocol with the kernel component uses a
- * NULL key name to pass an error condition.
- * For the SET, GET and DELETE operations,
- * use the existing protocol to pass back error.
- */
+ switch (op) {
+ case KVP_OP_GET_IP_INFO:
+ kvp_ip_val = &hv_msg->body.kvp_ip_val;
+ if_name =
+ kvp_mac_to_if_name((char *)kvp_ip_val->adapter_id);
+
+ if (if_name == NULL) {
+ /*
+ * We could not map the mac address to an
+ * interface name; return error.
+ */
+ hv_msg->error = HV_E_FAIL;
+ break;
+ }
+ error = kvp_get_ip_info(
+ 0, if_name, KVP_OP_GET_IP_INFO,
+ kvp_ip_val,
+ (MAX_IP_ADDR_SIZE * 2));
+
+ if (error)
+ hv_msg->error = error;
+
+ free(if_name);
+ break;
+
+ case KVP_OP_SET_IP_INFO:
+ kvp_ip_val = &hv_msg->body.kvp_ip_val;
+ if_name = kvp_get_if_name(
+ (char *)kvp_ip_val->adapter_id);
+ if (if_name == NULL) {
+ /*
+ * We could not map the guid to an
+ * interface name; return error.
+ */
+ hv_msg->error = HV_GUID_NOTFOUND;
+ break;
+ }
+ error = kvp_set_ip_info(if_name, kvp_ip_val);
+ if (error)
+ hv_msg->error = error;
+
+ free(if_name);
+ break;
case KVP_OP_SET:
- if (kvp_key_add_or_modify(hv_msg->kvp_hdr.pool,
+ if (kvp_key_add_or_modify(pool,
hv_msg->body.kvp_set.data.key,
hv_msg->body.kvp_set.data.key_size,
hv_msg->body.kvp_set.data.value,
hv_msg->body.kvp_set.data.value_size))
- strcpy(hv_msg->body.kvp_set.data.key, "");
+ hv_msg->error = HV_S_CONT;
break;
case KVP_OP_GET:
- if (kvp_get_value(hv_msg->kvp_hdr.pool,
+ if (kvp_get_value(pool,
hv_msg->body.kvp_set.data.key,
hv_msg->body.kvp_set.data.key_size,
hv_msg->body.kvp_set.data.value,
hv_msg->body.kvp_set.data.value_size))
- strcpy(hv_msg->body.kvp_set.data.key, "");
+ hv_msg->error = HV_S_CONT;
break;
case KVP_OP_DELETE:
- if (kvp_key_delete(hv_msg->kvp_hdr.pool,
+ if (kvp_key_delete(pool,
hv_msg->body.kvp_delete.key,
hv_msg->body.kvp_delete.key_size))
- strcpy(hv_msg->body.kvp_delete.key, "");
+ hv_msg->error = HV_S_CONT;
break;
default:
break;
}
- if (hv_msg->kvp_hdr.operation != KVP_OP_ENUMERATE)
+ if (op != KVP_OP_ENUMERATE)
goto kvp_done;
/*
@@ -782,13 +1597,14 @@ int main(void)
* both the key and the value; if not read from the
* appropriate pool.
*/
- if (hv_msg->kvp_hdr.pool != KVP_POOL_AUTO) {
- kvp_pool_enumerate(hv_msg->kvp_hdr.pool,
+ if (pool != KVP_POOL_AUTO) {
+ if (kvp_pool_enumerate(pool,
hv_msg->body.kvp_enum_data.index,
hv_msg->body.kvp_enum_data.data.key,
HV_KVP_EXCHANGE_MAX_KEY_SIZE,
hv_msg->body.kvp_enum_data.data.value,
- HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
+ HV_KVP_EXCHANGE_MAX_VALUE_SIZE))
+ hv_msg->error = HV_S_CONT;
goto kvp_done;
}
@@ -807,13 +1623,13 @@ int main(void)
strcpy(key_value, lic_version);
break;
case NetworkAddressIPv4:
- kvp_get_ip_address(AF_INET, key_value,
- HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
+ kvp_get_ip_info(AF_INET, NULL, KVP_OP_ENUMERATE,
+ key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
strcpy(key_name, "NetworkAddressIPv4");
break;
case NetworkAddressIPv6:
- kvp_get_ip_address(AF_INET6, key_value,
- HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
+ kvp_get_ip_info(AF_INET6, NULL, KVP_OP_ENUMERATE,
+ key_value, HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
strcpy(key_name, "NetworkAddressIPv6");
break;
case OSBuildNumber:
@@ -841,11 +1657,7 @@ int main(void)
strcpy(key_name, "ProcessorArchitecture");
break;
default:
- strcpy(key_value, "Unknown Key");
- /*
- * We use a null key name to terminate enumeration.
- */
- strcpy(key_name, "");
+ hv_msg->error = HV_S_CONT;
break;
}
/*
@@ -863,7 +1675,7 @@ kvp_done:
len = netlink_send(fd, incoming_cn_msg);
if (len < 0) {
syslog(LOG_ERR, "net_link send failed; error:%d", len);
- exit(-1);
+ exit(EXIT_FAILURE);
}
}
diff --git a/tools/hv/hv_set_ifconfig.sh b/tools/hv/hv_set_ifconfig.sh
new file mode 100755
index 000000000000..3e9427e08d80
--- /dev/null
+++ b/tools/hv/hv_set_ifconfig.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+
+# This example script activates an interface based on the specified
+# configuration.
+#
+# In the interest of keeping the KVP daemon code free of distro specific
+# information; the kvp daemon code invokes this external script to configure
+# the interface.
+#
+# The only argument to this script is the configuration file that is to
+# be used to configure the interface.
+#
+# Each Distro is expected to implement this script in a distro specific
+# fashion. For instance on Distros that ship with Network Manager enabled,
+# this script can be based on the Network Manager APIs for configuring the
+# interface.
+#
+# This example script is based on a RHEL environment.
+#
+# Here is the format of the ip configuration file:
+#
+# HWADDR=macaddr
+# IF_NAME=interface name
+# DHCP=yes (This is optional; if yes, DHCP is configured)
+#
+# IPADDR=ipaddr1
+# IPADDR_1=ipaddr2
+# IPADDR_x=ipaddry (where y = x + 1)
+#
+# NETMASK=netmask1
+# NETMASK_x=netmasky (where y = x + 1)
+#
+# GATEWAY=ipaddr1
+# GATEWAY_x=ipaddry (where y = x + 1)
+#
+# DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc)
+#
+# IPV6 addresses will be tagged as IPV6ADDR, IPV6 gateway will be
+# tagged as IPV6_DEFAULTGW and IPV6 NETMASK will be tagged as
+# IPV6NETMASK.
+#
+# The host can specify multiple ipv4 and ipv6 addresses to be
+# configured for the interface. Furthermore, the configuration
+# needs to be persistent. A subsequent GET call on the interface
+# is expected to return the configuration that is set via the SET
+# call.
+#
+
+
+
+echo "IPV6INIT=yes" >> $1
+echo "NM_CONTROLLED=no" >> $1
+echo "PEERDNS=yes" >> $1
+echo "ONBOOT=yes" >> $1
+
+dhcp=$(grep "DHCP" $1 2>/dev/null)
+if [ "$dhcp" != "" ];
+then
+echo "BOOTPROTO=dhcp" >> $1;
+fi
+
+cp $1 /etc/sysconfig/network-scripts/
+
+
+interface=$(echo $1 | awk -F - '{ print $2 }')
+
+/sbin/ifdown $interface 2>/dev/null
+/sbin/ifup $interfac 2>/dev/null
diff --git a/tools/lib/traceevent/Makefile b/tools/lib/traceevent/Makefile
index 14131cb0522d..04d959fa0226 100644
--- a/tools/lib/traceevent/Makefile
+++ b/tools/lib/traceevent/Makefile
@@ -129,7 +129,7 @@ CFLAGS ?= -g -Wall
# Append required CFLAGS
override CFLAGS += $(CONFIG_FLAGS) $(INCLUDES) $(PLUGIN_DIR_SQ)
-override CFLAGS += $(udis86-flags)
+override CFLAGS += $(udis86-flags) -D_GNU_SOURCE
ifeq ($(VERBOSE),1)
Q =
diff --git a/tools/lib/traceevent/event-parse.c b/tools/lib/traceevent/event-parse.c
index 5f34aa371b56..47264b4652b9 100644
--- a/tools/lib/traceevent/event-parse.c
+++ b/tools/lib/traceevent/event-parse.c
@@ -24,13 +24,14 @@
* Frederic Weisbecker gave his permission to relicense the code to
* the Lesser General Public License.
*/
-#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <errno.h>
+#include <stdint.h>
+#include <limits.h>
#include "event-parse.h"
#include "event-utils.h"
@@ -117,14 +118,7 @@ void breakpoint(void)
struct print_arg *alloc_arg(void)
{
- struct print_arg *arg;
-
- arg = malloc_or_die(sizeof(*arg));
- if (!arg)
- return NULL;
- memset(arg, 0, sizeof(*arg));
-
- return arg;
+ return calloc(1, sizeof(struct print_arg));
}
struct cmdline {
@@ -158,7 +152,9 @@ static int cmdline_init(struct pevent *pevent)
struct cmdline *cmdlines;
int i;
- cmdlines = malloc_or_die(sizeof(*cmdlines) * pevent->cmdline_count);
+ cmdlines = malloc(sizeof(*cmdlines) * pevent->cmdline_count);
+ if (!cmdlines)
+ return -1;
i = 0;
while (cmdlist) {
@@ -186,8 +182,8 @@ static char *find_cmdline(struct pevent *pevent, int pid)
if (!pid)
return "<idle>";
- if (!pevent->cmdlines)
- cmdline_init(pevent);
+ if (!pevent->cmdlines && cmdline_init(pevent))
+ return "<not enough memory for cmdlines!>";
key.pid = pid;
@@ -215,8 +211,8 @@ int pevent_pid_is_registered(struct pevent *pevent, int pid)
if (!pid)
return 1;
- if (!pevent->cmdlines)
- cmdline_init(pevent);
+ if (!pevent->cmdlines && cmdline_init(pevent))
+ return 0;
key.pid = pid;
@@ -258,10 +254,14 @@ static int add_new_comm(struct pevent *pevent, const char *comm, int pid)
return -1;
}
- cmdlines[pevent->cmdline_count].pid = pid;
cmdlines[pevent->cmdline_count].comm = strdup(comm);
- if (!cmdlines[pevent->cmdline_count].comm)
- die("malloc comm");
+ if (!cmdlines[pevent->cmdline_count].comm) {
+ free(cmdlines);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ cmdlines[pevent->cmdline_count].pid = pid;
if (cmdlines[pevent->cmdline_count].comm)
pevent->cmdline_count++;
@@ -288,10 +288,15 @@ int pevent_register_comm(struct pevent *pevent, const char *comm, int pid)
if (pevent->cmdlines)
return add_new_comm(pevent, comm, pid);
- item = malloc_or_die(sizeof(*item));
+ item = malloc(sizeof(*item));
+ if (!item)
+ return -1;
+
item->comm = strdup(comm);
- if (!item->comm)
- die("malloc comm");
+ if (!item->comm) {
+ free(item);
+ return -1;
+ }
item->pid = pid;
item->next = pevent->cmdlist;
@@ -355,7 +360,10 @@ static int func_map_init(struct pevent *pevent)
struct func_map *func_map;
int i;
- func_map = malloc_or_die(sizeof(*func_map) * (pevent->func_count + 1));
+ func_map = malloc(sizeof(*func_map) * (pevent->func_count + 1));
+ if (!func_map)
+ return -1;
+
funclist = pevent->funclist;
i = 0;
@@ -455,25 +463,36 @@ pevent_find_function_address(struct pevent *pevent, unsigned long long addr)
int pevent_register_function(struct pevent *pevent, char *func,
unsigned long long addr, char *mod)
{
- struct func_list *item;
+ struct func_list *item = malloc(sizeof(*item));
- item = malloc_or_die(sizeof(*item));
+ if (!item)
+ return -1;
item->next = pevent->funclist;
item->func = strdup(func);
- if (mod)
+ if (!item->func)
+ goto out_free;
+
+ if (mod) {
item->mod = strdup(mod);
- else
+ if (!item->mod)
+ goto out_free_func;
+ } else
item->mod = NULL;
item->addr = addr;
- if (!item->func || (mod && !item->mod))
- die("malloc func");
-
pevent->funclist = item;
pevent->func_count++;
return 0;
+
+out_free_func:
+ free(item->func);
+ item->func = NULL;
+out_free:
+ free(item);
+ errno = ENOMEM;
+ return -1;
}
/**
@@ -524,14 +543,16 @@ static int printk_cmp(const void *a, const void *b)
return 0;
}
-static void printk_map_init(struct pevent *pevent)
+static int printk_map_init(struct pevent *pevent)
{
struct printk_list *printklist;
struct printk_list *item;
struct printk_map *printk_map;
int i;
- printk_map = malloc_or_die(sizeof(*printk_map) * (pevent->printk_count + 1));
+ printk_map = malloc(sizeof(*printk_map) * (pevent->printk_count + 1));
+ if (!printk_map)
+ return -1;
printklist = pevent->printklist;
@@ -549,6 +570,8 @@ static void printk_map_init(struct pevent *pevent)
pevent->printk_map = printk_map;
pevent->printklist = NULL;
+
+ return 0;
}
static struct printk_map *
@@ -557,8 +580,8 @@ find_printk(struct pevent *pevent, unsigned long long addr)
struct printk_map *printk;
struct printk_map key;
- if (!pevent->printk_map)
- printk_map_init(pevent);
+ if (!pevent->printk_map && printk_map_init(pevent))
+ return NULL;
key.addr = addr;
@@ -580,21 +603,27 @@ find_printk(struct pevent *pevent, unsigned long long addr)
int pevent_register_print_string(struct pevent *pevent, char *fmt,
unsigned long long addr)
{
- struct printk_list *item;
+ struct printk_list *item = malloc(sizeof(*item));
- item = malloc_or_die(sizeof(*item));
+ if (!item)
+ return -1;
item->next = pevent->printklist;
- item->printk = strdup(fmt);
item->addr = addr;
+ item->printk = strdup(fmt);
if (!item->printk)
- die("malloc fmt");
+ goto out_free;
pevent->printklist = item;
pevent->printk_count++;
return 0;
+
+out_free:
+ free(item);
+ errno = ENOMEM;
+ return -1;
}
/**
@@ -619,24 +648,18 @@ void pevent_print_printk(struct pevent *pevent)
static struct event_format *alloc_event(void)
{
- struct event_format *event;
-
- event = malloc(sizeof(*event));
- if (!event)
- return NULL;
- memset(event, 0, sizeof(*event));
-
- return event;
+ return calloc(1, sizeof(struct event_format));
}
-static void add_event(struct pevent *pevent, struct event_format *event)
+static int add_event(struct pevent *pevent, struct event_format *event)
{
int i;
+ struct event_format **events = realloc(pevent->events, sizeof(event) *
+ (pevent->nr_events + 1));
+ if (!events)
+ return -1;
- pevent->events = realloc(pevent->events, sizeof(event) *
- (pevent->nr_events + 1));
- if (!pevent->events)
- die("Can not allocate events");
+ pevent->events = events;
for (i = 0; i < pevent->nr_events; i++) {
if (pevent->events[i]->id > event->id)
@@ -651,6 +674,8 @@ static void add_event(struct pevent *pevent, struct event_format *event)
pevent->nr_events++;
event->pevent = pevent;
+
+ return 0;
}
static int event_item_type(enum event_type type)
@@ -827,9 +852,9 @@ static enum event_type __read_token(char **tok)
switch (type) {
case EVENT_NEWLINE:
case EVENT_DELIM:
- *tok = malloc_or_die(2);
- (*tok)[0] = ch;
- (*tok)[1] = 0;
+ if (asprintf(tok, "%c", ch) < 0)
+ return EVENT_ERROR;
+
return type;
case EVENT_OP:
@@ -1240,8 +1265,10 @@ static int event_read_fields(struct event_format *event, struct format_field **f
last_token = token;
- field = malloc_or_die(sizeof(*field));
- memset(field, 0, sizeof(*field));
+ field = calloc(1, sizeof(*field));
+ if (!field)
+ goto fail;
+
field->event = event;
/* read the rest of the type */
@@ -1282,7 +1309,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f
}
if (!field->type) {
- die("no type found");
+ do_warning("%s: no type found", __func__);
goto fail;
}
field->name = last_token;
@@ -1329,7 +1356,7 @@ static int event_read_fields(struct event_format *event, struct format_field **f
free_token(token);
type = read_token(&token);
if (type == EVENT_NONE) {
- die("failed to find token");
+ do_warning("failed to find token");
goto fail;
}
}
@@ -1538,6 +1565,14 @@ process_cond(struct event_format *event, struct print_arg *top, char **tok)
left = alloc_arg();
right = alloc_arg();
+ if (!arg || !left || !right) {
+ do_warning("%s: not enough memory!", __func__);
+ /* arg will be freed at out_free */
+ free_arg(left);
+ free_arg(right);
+ goto out_free;
+ }
+
arg->type = PRINT_OP;
arg->op.left = left;
arg->op.right = right;
@@ -1580,6 +1615,12 @@ process_array(struct event_format *event, struct print_arg *top, char **tok)
char *token = NULL;
arg = alloc_arg();
+ if (!arg) {
+ do_warning("%s: not enough memory!", __func__);
+ /* '*tok' is set to top->op.op. No need to free. */
+ *tok = NULL;
+ return EVENT_ERROR;
+ }
*tok = NULL;
type = process_arg(event, arg, &token);
@@ -1595,8 +1636,7 @@ process_array(struct event_format *event, struct print_arg *top, char **tok)
return type;
out_free:
- free_token(*tok);
- *tok = NULL;
+ free_token(token);
free_arg(arg);
return EVENT_ERROR;
}
@@ -1682,7 +1722,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
if (arg->type == PRINT_OP && !arg->op.left) {
/* handle single op */
if (token[1]) {
- die("bad op token %s", token);
+ do_warning("bad op token %s", token);
goto out_free;
}
switch (token[0]) {
@@ -1699,10 +1739,16 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
/* make an empty left */
left = alloc_arg();
+ if (!left)
+ goto out_warn_free;
+
left->type = PRINT_NULL;
arg->op.left = left;
right = alloc_arg();
+ if (!right)
+ goto out_warn_free;
+
arg->op.right = right;
/* do not free the token, it belongs to an op */
@@ -1712,6 +1758,9 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
} else if (strcmp(token, "?") == 0) {
left = alloc_arg();
+ if (!left)
+ goto out_warn_free;
+
/* copy the top arg to the left */
*left = *arg;
@@ -1720,6 +1769,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
arg->op.left = left;
arg->op.prio = 0;
+ /* it will set arg->op.right */
type = process_cond(event, arg, tok);
} else if (strcmp(token, ">>") == 0 ||
@@ -1739,6 +1789,8 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
strcmp(token, "!=") == 0) {
left = alloc_arg();
+ if (!left)
+ goto out_warn_free;
/* copy the top arg to the left */
*left = *arg;
@@ -1746,6 +1798,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
arg->type = PRINT_OP;
arg->op.op = token;
arg->op.left = left;
+ arg->op.right = NULL;
if (set_op_prio(arg) == -1) {
event->flags |= EVENT_FL_FAILED;
@@ -1762,12 +1815,14 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
type == EVENT_DELIM && (strcmp(token, ")") == 0)) {
char *new_atom;
- if (left->type != PRINT_ATOM)
- die("bad pointer type");
+ if (left->type != PRINT_ATOM) {
+ do_warning("bad pointer type");
+ goto out_free;
+ }
new_atom = realloc(left->atom.atom,
strlen(left->atom.atom) + 3);
if (!new_atom)
- goto out_free;
+ goto out_warn_free;
left->atom.atom = new_atom;
strcat(left->atom.atom, " *");
@@ -1779,12 +1834,18 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
}
right = alloc_arg();
+ if (!right)
+ goto out_warn_free;
+
type = process_arg_token(event, right, tok, type);
arg->op.right = right;
} else if (strcmp(token, "[") == 0) {
left = alloc_arg();
+ if (!left)
+ goto out_warn_free;
+
*left = *arg;
arg->type = PRINT_OP;
@@ -1793,6 +1854,7 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
arg->op.prio = 0;
+ /* it will set arg->op.right */
type = process_array(event, arg, tok);
} else {
@@ -1816,14 +1878,16 @@ process_op(struct event_format *event, struct print_arg *arg, char **tok)
return type;
- out_free:
+out_warn_free:
+ do_warning("%s: not enough memory!", __func__);
+out_free:
free_token(token);
*tok = NULL;
return EVENT_ERROR;
}
static enum event_type
-process_entry(struct event_format *event __unused, struct print_arg *arg,
+process_entry(struct event_format *event __maybe_unused, struct print_arg *arg,
char **tok)
{
enum event_type type;
@@ -1880,7 +1944,11 @@ eval_type_str(unsigned long long val, const char *type, int pointer)
return val;
}
- ref = malloc_or_die(len);
+ ref = malloc(len);
+ if (!ref) {
+ do_warning("%s: not enough memory!", __func__);
+ return val;
+ }
memcpy(ref, type, len);
/* chop off the " *" */
@@ -1957,8 +2025,10 @@ eval_type_str(unsigned long long val, const char *type, int pointer)
static unsigned long long
eval_type(unsigned long long val, struct print_arg *arg, int pointer)
{
- if (arg->type != PRINT_TYPE)
- die("expected type argument");
+ if (arg->type != PRINT_TYPE) {
+ do_warning("expected type argument");
+ return 0;
+ }
return eval_type_str(val, arg->typecast.type, pointer);
}
@@ -2143,7 +2213,7 @@ static char *arg_eval (struct print_arg *arg)
case PRINT_STRING:
case PRINT_BSTRING:
default:
- die("invalid eval type %d", arg->type);
+ do_warning("invalid eval type %d", arg->type);
break;
}
@@ -2166,6 +2236,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char **
break;
arg = alloc_arg();
+ if (!arg)
+ goto out_free;
free_token(token);
type = process_arg(event, arg, &token);
@@ -2179,30 +2251,33 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char **
if (test_type_token(type, token, EVENT_DELIM, ","))
goto out_free;
- field = malloc_or_die(sizeof(*field));
- memset(field, 0, sizeof(*field));
+ field = calloc(1, sizeof(*field));
+ if (!field)
+ goto out_free;
value = arg_eval(arg);
if (value == NULL)
- goto out_free;
+ goto out_free_field;
field->value = strdup(value);
if (field->value == NULL)
- goto out_free;
+ goto out_free_field;
free_arg(arg);
arg = alloc_arg();
+ if (!arg)
+ goto out_free;
free_token(token);
type = process_arg(event, arg, &token);
if (test_type_token(type, token, EVENT_OP, "}"))
- goto out_free;
+ goto out_free_field;
value = arg_eval(arg);
if (value == NULL)
- goto out_free;
+ goto out_free_field;
field->str = strdup(value);
if (field->str == NULL)
- goto out_free;
+ goto out_free_field;
free_arg(arg);
arg = NULL;
@@ -2216,6 +2291,8 @@ process_fields(struct event_format *event, struct print_flag_sym **list, char **
*tok = token;
return type;
+out_free_field:
+ free_flag_sym(field);
out_free:
free_arg(arg);
free_token(token);
@@ -2235,6 +2312,10 @@ process_flags(struct event_format *event, struct print_arg *arg, char **tok)
arg->type = PRINT_FLAGS;
field = alloc_arg();
+ if (!field) {
+ do_warning("%s: not enough memory!", __func__);
+ goto out_free;
+ }
type = process_arg(event, field, &token);
@@ -2243,7 +2324,7 @@ process_flags(struct event_format *event, struct print_arg *arg, char **tok)
type = process_op(event, field, &token);
if (test_type_token(type, token, EVENT_DELIM, ","))
- goto out_free;
+ goto out_free_field;
free_token(token);
arg->flags.field = field;
@@ -2265,7 +2346,9 @@ process_flags(struct event_format *event, struct print_arg *arg, char **tok)
type = read_token_item(tok);
return type;
- out_free:
+out_free_field:
+ free_arg(field);
+out_free:
free_token(token);
*tok = NULL;
return EVENT_ERROR;
@@ -2282,10 +2365,14 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok)
arg->type = PRINT_SYMBOL;
field = alloc_arg();
+ if (!field) {
+ do_warning("%s: not enough memory!", __func__);
+ goto out_free;
+ }
type = process_arg(event, field, &token);
if (test_type_token(type, token, EVENT_DELIM, ","))
- goto out_free;
+ goto out_free_field;
arg->symbol.field = field;
@@ -2297,7 +2384,9 @@ process_symbols(struct event_format *event, struct print_arg *arg, char **tok)
type = read_token_item(tok);
return type;
- out_free:
+out_free_field:
+ free_arg(field);
+out_free:
free_token(token);
*tok = NULL;
return EVENT_ERROR;
@@ -2314,6 +2403,11 @@ process_hex(struct event_format *event, struct print_arg *arg, char **tok)
arg->type = PRINT_HEX;
field = alloc_arg();
+ if (!field) {
+ do_warning("%s: not enough memory!", __func__);
+ goto out_free;
+ }
+
type = process_arg(event, field, &token);
if (test_type_token(type, token, EVENT_DELIM, ","))
@@ -2324,6 +2418,12 @@ process_hex(struct event_format *event, struct print_arg *arg, char **tok)
free_token(token);
field = alloc_arg();
+ if (!field) {
+ do_warning("%s: not enough memory!", __func__);
+ *tok = NULL;
+ return EVENT_ERROR;
+ }
+
type = process_arg(event, field, &token);
if (test_type_token(type, token, EVENT_DELIM, ")"))
@@ -2381,6 +2481,12 @@ process_dynamic_array(struct event_format *event, struct print_arg *arg, char **
free_token(token);
arg = alloc_arg();
+ if (!field) {
+ do_warning("%s: not enough memory!", __func__);
+ *tok = NULL;
+ return EVENT_ERROR;
+ }
+
type = process_arg(event, arg, &token);
if (type == EVENT_ERROR)
goto out_free_arg;
@@ -2434,10 +2540,16 @@ process_paren(struct event_format *event, struct print_arg *arg, char **tok)
/* make this a typecast and contine */
/* prevous must be an atom */
- if (arg->type != PRINT_ATOM)
- die("previous needed to be PRINT_ATOM");
+ if (arg->type != PRINT_ATOM) {
+ do_warning("previous needed to be PRINT_ATOM");
+ goto out_free;
+ }
item_arg = alloc_arg();
+ if (!item_arg) {
+ do_warning("%s: not enough memory!", __func__);
+ goto out_free;
+ }
arg->type = PRINT_TYPE;
arg->typecast.type = arg->atom.atom;
@@ -2457,7 +2569,8 @@ process_paren(struct event_format *event, struct print_arg *arg, char **tok)
static enum event_type
-process_str(struct event_format *event __unused, struct print_arg *arg, char **tok)
+process_str(struct event_format *event __maybe_unused, struct print_arg *arg,
+ char **tok)
{
enum event_type type;
char *token;
@@ -2532,6 +2645,11 @@ process_func_handler(struct event_format *event, struct pevent_function_handler
next_arg = &(arg->func.args);
for (i = 0; i < func->nr_args; i++) {
farg = alloc_arg();
+ if (!farg) {
+ do_warning("%s: not enough memory!", __func__);
+ return EVENT_ERROR;
+ }
+
type = process_arg(event, farg, &token);
if (i < (func->nr_args - 1))
test = ",";
@@ -2676,7 +2794,8 @@ process_arg_token(struct event_format *event, struct print_arg *arg,
case EVENT_ERROR ... EVENT_NEWLINE:
default:
- die("unexpected type %d", type);
+ do_warning("unexpected type %d", type);
+ return EVENT_ERROR;
}
*tok = token;
@@ -2697,6 +2816,10 @@ static int event_read_print_args(struct event_format *event, struct print_arg **
}
arg = alloc_arg();
+ if (!arg) {
+ do_warning("%s: not enough memory!", __func__);
+ return -1;
+ }
type = process_arg(event, arg, &token);
@@ -2768,10 +2891,8 @@ static int event_read_print(struct event_format *event)
if (type == EVENT_DQUOTE) {
char *cat;
- cat = malloc_or_die(strlen(event->print_fmt.format) +
- strlen(token) + 1);
- strcpy(cat, event->print_fmt.format);
- strcat(cat, token);
+ if (asprintf(&cat, "%s%s", event->print_fmt.format, token) < 0)
+ goto fail;
free_token(token);
free_token(event->print_fmt.format);
event->print_fmt.format = NULL;
@@ -2925,8 +3046,10 @@ static int get_common_info(struct pevent *pevent,
* All events should have the same common elements.
* Pick any event to find where the type is;
*/
- if (!pevent->events)
- die("no event_list!");
+ if (!pevent->events) {
+ do_warning("no event_list!");
+ return -1;
+ }
event = pevent->events[0];
field = pevent_find_common_field(event, type);
@@ -3084,7 +3207,8 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
if (!arg->field.field) {
arg->field.field = pevent_find_any_field(event, arg->field.name);
if (!arg->field.field)
- die("field %s not found", arg->field.name);
+ goto out_warning_field;
+
}
/* must be a number */
val = pevent_read_number(pevent, data + arg->field.field->offset,
@@ -3145,8 +3269,10 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
if (!larg->field.field) {
larg->field.field =
pevent_find_any_field(event, larg->field.name);
- if (!larg->field.field)
- die("field %s not found", larg->field.name);
+ if (!larg->field.field) {
+ arg = larg;
+ goto out_warning_field;
+ }
}
field_size = larg->field.field->elementsize;
offset = larg->field.field->offset +
@@ -3182,7 +3308,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
val = left != right;
break;
default:
- die("unknown op '%s'", arg->op.op);
+ goto out_warning_op;
}
break;
case '~':
@@ -3212,7 +3338,7 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
val = left <= right;
break;
default:
- die("unknown op '%s'", arg->op.op);
+ goto out_warning_op;
}
break;
case '>':
@@ -3227,12 +3353,13 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
val = left >= right;
break;
default:
- die("unknown op '%s'", arg->op.op);
+ goto out_warning_op;
}
break;
case '=':
if (arg->op.op[1] != '=')
- die("unknown op '%s'", arg->op.op);
+ goto out_warning_op;
+
val = left == right;
break;
case '-':
@@ -3248,13 +3375,21 @@ eval_num_arg(void *data, int size, struct event_format *event, struct print_arg
val = left * right;
break;
default:
- die("unknown op '%s'", arg->op.op);
+ goto out_warning_op;
}
break;
default: /* not sure what to do there */
return 0;
}
return val;
+
+out_warning_op:
+ do_warning("%s: unknown op '%s'", __func__, arg->op.op);
+ return 0;
+
+out_warning_field:
+ do_warning("%s: field %s not found", __func__, arg->field.name);
+ return 0;
}
struct flag {
@@ -3331,8 +3466,10 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
field = arg->field.field;
if (!field) {
field = pevent_find_any_field(event, arg->field.name);
- if (!field)
- die("field %s not found", arg->field.name);
+ if (!field) {
+ str = arg->field.name;
+ goto out_warning_field;
+ }
arg->field.field = field;
}
/* Zero sized fields, mean the rest of the data */
@@ -3349,7 +3486,11 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
trace_seq_printf(s, "%lx", addr);
break;
}
- str = malloc_or_die(len + 1);
+ str = malloc(len + 1);
+ if (!str) {
+ do_warning("%s: not enough memory!", __func__);
+ return;
+ }
memcpy(str, data + field->offset, len);
str[len] = 0;
print_str_to_seq(s, format, len_arg, str);
@@ -3389,7 +3530,7 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
str = arg->hex.field->field.name;
field = pevent_find_any_field(event, str);
if (!field)
- die("field %s not found", str);
+ goto out_warning_field;
arg->hex.field->field.field = field;
}
hex = data + field->offset;
@@ -3441,6 +3582,11 @@ static void print_str_arg(struct trace_seq *s, void *data, int size,
/* well... */
break;
}
+
+ return;
+
+out_warning_field:
+ do_warning("%s: field %s not found", __func__, arg->field.name);
}
static unsigned long long
@@ -3467,7 +3613,11 @@ process_defined_func(struct trace_seq *s, void *data, int size,
farg = arg->func.args;
param = func_handle->params;
- args = malloc_or_die(sizeof(*args) * func_handle->nr_args);
+ ret = ULLONG_MAX;
+ args = malloc(sizeof(*args) * func_handle->nr_args);
+ if (!args)
+ goto out;
+
for (i = 0; i < func_handle->nr_args; i++) {
switch (param->type) {
case PEVENT_FUNC_ARG_INT:
@@ -3479,13 +3629,19 @@ process_defined_func(struct trace_seq *s, void *data, int size,
trace_seq_init(&str);
print_str_arg(&str, data, size, event, "%s", -1, farg);
trace_seq_terminate(&str);
- string = malloc_or_die(sizeof(*string));
+ string = malloc(sizeof(*string));
+ if (!string) {
+ do_warning("%s(%d): malloc str", __func__, __LINE__);
+ goto out_free;
+ }
string->next = strings;
string->str = strdup(str.buffer);
- if (!string->str)
- die("malloc str");
-
- args[i] = (unsigned long long)string->str;
+ if (!string->str) {
+ free(string);
+ do_warning("%s(%d): malloc str", __func__, __LINE__);
+ goto out_free;
+ }
+ args[i] = (uintptr_t)string->str;
strings = string;
trace_seq_destroy(&str);
break;
@@ -3494,14 +3650,15 @@ process_defined_func(struct trace_seq *s, void *data, int size,
* Something went totally wrong, this is not
* an input error, something in this code broke.
*/
- die("Unexpected end of arguments\n");
- break;
+ do_warning("Unexpected end of arguments\n");
+ goto out_free;
}
farg = farg->next;
param = param->next;
}
ret = (*func_handle->func)(s, args);
+out_free:
free(args);
while (strings) {
string = strings;
@@ -3515,6 +3672,18 @@ process_defined_func(struct trace_seq *s, void *data, int size,
return ret;
}
+static void free_args(struct print_arg *args)
+{
+ struct print_arg *next;
+
+ while (args) {
+ next = args->next;
+
+ free_arg(args);
+ args = next;
+ }
+}
+
static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struct event_format *event)
{
struct pevent *pevent = event->pevent;
@@ -3530,11 +3699,15 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
if (!field) {
field = pevent_find_field(event, "buf");
- if (!field)
- die("can't find buffer field for binary printk");
+ if (!field) {
+ do_warning("can't find buffer field for binary printk");
+ return NULL;
+ }
ip_field = pevent_find_field(event, "ip");
- if (!ip_field)
- die("can't find ip field for binary printk");
+ if (!ip_field) {
+ do_warning("can't find ip field for binary printk");
+ return NULL;
+ }
pevent->bprint_buf_field = field;
pevent->bprint_ip_field = ip_field;
}
@@ -3545,13 +3718,18 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
* The first arg is the IP pointer.
*/
args = alloc_arg();
+ if (!args) {
+ do_warning("%s(%d): not enough memory!", __func__, __LINE__);
+ return NULL;
+ }
arg = args;
arg->next = NULL;
next = &arg->next;
arg->type = PRINT_ATOM;
- arg->atom.atom = malloc_or_die(32);
- sprintf(arg->atom.atom, "%lld", ip);
+
+ if (asprintf(&arg->atom.atom, "%lld", ip) < 0)
+ goto out_free;
/* skip the first "%pf : " */
for (ptr = fmt + 6, bptr = data + field->offset;
@@ -3606,10 +3784,17 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
val = pevent_read_number(pevent, bptr, vsize);
bptr += vsize;
arg = alloc_arg();
+ if (!arg) {
+ do_warning("%s(%d): not enough memory!",
+ __func__, __LINE__);
+ goto out_free;
+ }
arg->next = NULL;
arg->type = PRINT_ATOM;
- arg->atom.atom = malloc_or_die(32);
- sprintf(arg->atom.atom, "%lld", val);
+ if (asprintf(&arg->atom.atom, "%lld", val) < 0) {
+ free(arg);
+ goto out_free;
+ }
*next = arg;
next = &arg->next;
/*
@@ -3622,11 +3807,16 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
break;
case 's':
arg = alloc_arg();
+ if (!arg) {
+ do_warning("%s(%d): not enough memory!",
+ __func__, __LINE__);
+ goto out_free;
+ }
arg->next = NULL;
arg->type = PRINT_BSTRING;
arg->string.string = strdup(bptr);
if (!arg->string.string)
- break;
+ goto out_free;
bptr += strlen(bptr) + 1;
*next = arg;
next = &arg->next;
@@ -3637,22 +3827,15 @@ static struct print_arg *make_bprint_args(char *fmt, void *data, int size, struc
}
return args;
-}
-static void free_args(struct print_arg *args)
-{
- struct print_arg *next;
-
- while (args) {
- next = args->next;
-
- free_arg(args);
- args = next;
- }
+out_free:
+ free_args(args);
+ return NULL;
}
static char *
-get_bprint_format(void *data, int size __unused, struct event_format *event)
+get_bprint_format(void *data, int size __maybe_unused,
+ struct event_format *event)
{
struct pevent *pevent = event->pevent;
unsigned long long addr;
@@ -3665,8 +3848,10 @@ get_bprint_format(void *data, int size __unused, struct event_format *event)
if (!field) {
field = pevent_find_field(event, "fmt");
- if (!field)
- die("can't find format field for binary printk");
+ if (!field) {
+ do_warning("can't find format field for binary printk");
+ return NULL;
+ }
pevent->bprint_fmt_field = field;
}
@@ -3674,9 +3859,8 @@ get_bprint_format(void *data, int size __unused, struct event_format *event)
printk = find_printk(pevent, addr);
if (!printk) {
- format = malloc_or_die(45);
- sprintf(format, "%%pf : (NO FORMAT FOUND at %llx)\n",
- addr);
+ if (asprintf(&format, "%%pf : (NO FORMAT FOUND at %llx)\n", addr) < 0)
+ return NULL;
return format;
}
@@ -3684,8 +3868,8 @@ get_bprint_format(void *data, int size __unused, struct event_format *event)
/* Remove any quotes. */
if (*p == '"')
p++;
- format = malloc_or_die(strlen(p) + 10);
- sprintf(format, "%s : %s", "%pf", p);
+ if (asprintf(&format, "%s : %s", "%pf", p) < 0)
+ return NULL;
/* remove ending quotes and new line since we will add one too */
p = format + strlen(format) - 1;
if (*p == '"')
@@ -3720,8 +3904,11 @@ static void print_mac_arg(struct trace_seq *s, int mac, void *data, int size,
if (!arg->field.field) {
arg->field.field =
pevent_find_any_field(event, arg->field.name);
- if (!arg->field.field)
- die("field %s not found", arg->field.name);
+ if (!arg->field.field) {
+ do_warning("%s: field %s not found",
+ __func__, arg->field.name);
+ return;
+ }
}
if (arg->field.field->size != 6) {
trace_seq_printf(s, "INVALIDMAC");
@@ -3888,8 +4075,11 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
goto cont_process;
case '*':
/* The argument is the length. */
- if (!arg)
- die("no argument match");
+ if (!arg) {
+ do_warning("no argument match");
+ event->flags |= EVENT_FL_FAILED;
+ goto out_failed;
+ }
len_arg = eval_num_arg(data, size, event, arg);
len_as_arg = 1;
arg = arg->next;
@@ -3922,15 +4112,21 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
case 'x':
case 'X':
case 'u':
- if (!arg)
- die("no argument match");
+ if (!arg) {
+ do_warning("no argument match");
+ event->flags |= EVENT_FL_FAILED;
+ goto out_failed;
+ }
len = ((unsigned long)ptr + 1) -
(unsigned long)saveptr;
/* should never happen */
- if (len > 31)
- die("bad format!");
+ if (len > 31) {
+ do_warning("bad format!");
+ event->flags |= EVENT_FL_FAILED;
+ len = 31;
+ }
memcpy(format, saveptr, len);
format[len] = 0;
@@ -3994,19 +4190,26 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
trace_seq_printf(s, format, (long long)val);
break;
default:
- die("bad count (%d)", ls);
+ do_warning("bad count (%d)", ls);
+ event->flags |= EVENT_FL_FAILED;
}
break;
case 's':
- if (!arg)
- die("no matching argument");
+ if (!arg) {
+ do_warning("no matching argument");
+ event->flags |= EVENT_FL_FAILED;
+ goto out_failed;
+ }
len = ((unsigned long)ptr + 1) -
(unsigned long)saveptr;
/* should never happen */
- if (len > 31)
- die("bad format!");
+ if (len > 31) {
+ do_warning("bad format!");
+ event->flags |= EVENT_FL_FAILED;
+ len = 31;
+ }
memcpy(format, saveptr, len);
format[len] = 0;
@@ -4024,6 +4227,11 @@ static void pretty_print(struct trace_seq *s, void *data, int size, struct event
trace_seq_putc(s, *ptr);
}
+ if (event->flags & EVENT_FL_FAILED) {
+out_failed:
+ trace_seq_printf(s, "[FAILED TO PARSE]");
+ }
+
if (args) {
free_args(args);
free(bprint_fmt);
@@ -4356,7 +4564,10 @@ get_event_fields(const char *type, const char *name,
struct format_field *field;
int i = 0;
- fields = malloc_or_die(sizeof(*fields) * (count + 1));
+ fields = malloc(sizeof(*fields) * (count + 1));
+ if (!fields)
+ return NULL;
+
for (field = list; field; field = field->next) {
fields[i++] = field;
if (i == count + 1) {
@@ -4672,8 +4883,7 @@ static int find_event_handle(struct pevent *pevent, struct event_format *event)
}
/**
- * pevent_parse_event - parse the event format
- * @pevent: the handle to the pevent
+ * __pevent_parse_format - parse the event format
* @buf: the buffer storing the event format string
* @size: the size of @buf
* @sys: the system the event belongs to
@@ -4685,28 +4895,27 @@ static int find_event_handle(struct pevent *pevent, struct event_format *event)
*
* /sys/kernel/debug/tracing/events/.../.../format
*/
-int pevent_parse_event(struct pevent *pevent,
- const char *buf, unsigned long size,
- const char *sys)
+enum pevent_errno __pevent_parse_format(struct event_format **eventp,
+ struct pevent *pevent, const char *buf,
+ unsigned long size, const char *sys)
{
struct event_format *event;
int ret;
init_input_buf(buf, size);
- event = alloc_event();
+ *eventp = event = alloc_event();
if (!event)
- return -ENOMEM;
+ return PEVENT_ERRNO__MEM_ALLOC_FAILED;
event->name = event_read_name();
if (!event->name) {
/* Bad event? */
- free(event);
- return -1;
+ ret = PEVENT_ERRNO__MEM_ALLOC_FAILED;
+ goto event_alloc_failed;
}
if (strcmp(sys, "ftrace") == 0) {
-
event->flags |= EVENT_FL_ISFTRACE;
if (strcmp(event->name, "bprint") == 0)
@@ -4714,74 +4923,189 @@ int pevent_parse_event(struct pevent *pevent,
}
event->id = event_read_id();
- if (event->id < 0)
- die("failed to read event id");
+ if (event->id < 0) {
+ ret = PEVENT_ERRNO__READ_ID_FAILED;
+ /*
+ * This isn't an allocation error actually.
+ * But as the ID is critical, just bail out.
+ */
+ goto event_alloc_failed;
+ }
event->system = strdup(sys);
- if (!event->system)
- die("failed to allocate system");
-
- /* Add pevent to event so that it can be referenced */
- event->pevent = pevent;
+ if (!event->system) {
+ ret = PEVENT_ERRNO__MEM_ALLOC_FAILED;
+ goto event_alloc_failed;
+ }
ret = event_read_format(event);
if (ret < 0) {
- do_warning("failed to read event format for %s", event->name);
- goto event_failed;
+ ret = PEVENT_ERRNO__READ_FORMAT_FAILED;
+ goto event_parse_failed;
}
/*
* If the event has an override, don't print warnings if the event
* print format fails to parse.
*/
- if (find_event_handle(pevent, event))
+ if (pevent && find_event_handle(pevent, event))
show_warning = 0;
ret = event_read_print(event);
- if (ret < 0) {
- do_warning("failed to read event print fmt for %s",
- event->name);
- show_warning = 1;
- goto event_failed;
- }
show_warning = 1;
- add_event(pevent, event);
+ if (ret < 0) {
+ ret = PEVENT_ERRNO__READ_PRINT_FAILED;
+ goto event_parse_failed;
+ }
if (!ret && (event->flags & EVENT_FL_ISFTRACE)) {
struct format_field *field;
struct print_arg *arg, **list;
/* old ftrace had no args */
-
list = &event->print_fmt.args;
for (field = event->format.fields; field; field = field->next) {
arg = alloc_arg();
- *list = arg;
- list = &arg->next;
+ if (!arg) {
+ event->flags |= EVENT_FL_FAILED;
+ return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED;
+ }
arg->type = PRINT_FIELD;
arg->field.name = strdup(field->name);
if (!arg->field.name) {
- do_warning("failed to allocate field name");
event->flags |= EVENT_FL_FAILED;
- return -1;
+ free_arg(arg);
+ return PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED;
}
arg->field.field = field;
+ *list = arg;
+ list = &arg->next;
}
return 0;
}
+ return 0;
+
+ event_parse_failed:
+ event->flags |= EVENT_FL_FAILED;
+ return ret;
+
+ event_alloc_failed:
+ free(event->system);
+ free(event->name);
+ free(event);
+ *eventp = NULL;
+ return ret;
+}
+
+/**
+ * pevent_parse_format - parse the event format
+ * @buf: the buffer storing the event format string
+ * @size: the size of @buf
+ * @sys: the system the event belongs to
+ *
+ * This parses the event format and creates an event structure
+ * to quickly parse raw data for a given event.
+ *
+ * These files currently come from:
+ *
+ * /sys/kernel/debug/tracing/events/.../.../format
+ */
+enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf,
+ unsigned long size, const char *sys)
+{
+ return __pevent_parse_format(eventp, NULL, buf, size, sys);
+}
+
+/**
+ * pevent_parse_event - parse the event format
+ * @pevent: the handle to the pevent
+ * @buf: the buffer storing the event format string
+ * @size: the size of @buf
+ * @sys: the system the event belongs to
+ *
+ * This parses the event format and creates an event structure
+ * to quickly parse raw data for a given event.
+ *
+ * These files currently come from:
+ *
+ * /sys/kernel/debug/tracing/events/.../.../format
+ */
+enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf,
+ unsigned long size, const char *sys)
+{
+ struct event_format *event = NULL;
+ int ret = __pevent_parse_format(&event, pevent, buf, size, sys);
+
+ if (event == NULL)
+ return ret;
+
+ /* Add pevent to event so that it can be referenced */
+ event->pevent = pevent;
+
+ if (add_event(pevent, event)) {
+ ret = PEVENT_ERRNO__MEM_ALLOC_FAILED;
+ goto event_add_failed;
+ }
+
#define PRINT_ARGS 0
if (PRINT_ARGS && event->print_fmt.args)
print_args(event->print_fmt.args);
return 0;
- event_failed:
- event->flags |= EVENT_FL_FAILED;
- /* still add it even if it failed */
- add_event(pevent, event);
- return -1;
+event_add_failed:
+ pevent_free_format(event);
+ return ret;
+}
+
+#undef _PE
+#define _PE(code, str) str
+static const char * const pevent_error_str[] = {
+ PEVENT_ERRORS
+};
+#undef _PE
+
+int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
+ char *buf, size_t buflen)
+{
+ int idx;
+ const char *msg;
+
+ if (errnum >= 0) {
+ msg = strerror_r(errnum, buf, buflen);
+ if (msg != buf) {
+ size_t len = strlen(msg);
+ memcpy(buf, msg, min(buflen - 1, len));
+ *(buf + min(buflen - 1, len)) = '\0';
+ }
+ return 0;
+ }
+
+ if (errnum <= __PEVENT_ERRNO__START ||
+ errnum >= __PEVENT_ERRNO__END)
+ return -1;
+
+ idx = errnum - __PEVENT_ERRNO__START - 1;
+ msg = pevent_error_str[idx];
+
+ switch (errnum) {
+ case PEVENT_ERRNO__MEM_ALLOC_FAILED:
+ case PEVENT_ERRNO__PARSE_EVENT_FAILED:
+ case PEVENT_ERRNO__READ_ID_FAILED:
+ case PEVENT_ERRNO__READ_FORMAT_FAILED:
+ case PEVENT_ERRNO__READ_PRINT_FAILED:
+ case PEVENT_ERRNO__OLD_FTRACE_ARG_FAILED:
+ snprintf(buf, buflen, "%s", msg);
+ break;
+
+ default:
+ /* cannot reach here */
+ break;
+ }
+
+ return 0;
}
int get_field_val(struct trace_seq *s, struct format_field *field,
@@ -5000,6 +5324,7 @@ int pevent_register_print_function(struct pevent *pevent,
struct pevent_func_params *param;
enum pevent_func_arg_type type;
va_list ap;
+ int ret;
func_handle = find_func_handler(pevent, name);
if (func_handle) {
@@ -5012,14 +5337,20 @@ int pevent_register_print_function(struct pevent *pevent,
remove_func_handler(pevent, name);
}
- func_handle = malloc_or_die(sizeof(*func_handle));
- memset(func_handle, 0, sizeof(*func_handle));
+ func_handle = calloc(1, sizeof(*func_handle));
+ if (!func_handle) {
+ do_warning("Failed to allocate function handler");
+ return PEVENT_ERRNO__MEM_ALLOC_FAILED;
+ }
func_handle->ret_type = ret_type;
func_handle->name = strdup(name);
func_handle->func = func;
- if (!func_handle->name)
- die("Failed to allocate function name");
+ if (!func_handle->name) {
+ do_warning("Failed to allocate function name");
+ free(func_handle);
+ return PEVENT_ERRNO__MEM_ALLOC_FAILED;
+ }
next_param = &(func_handle->params);
va_start(ap, name);
@@ -5029,11 +5360,17 @@ int pevent_register_print_function(struct pevent *pevent,
break;
if (type < 0 || type >= PEVENT_FUNC_ARG_MAX_TYPES) {
- warning("Invalid argument type %d", type);
+ do_warning("Invalid argument type %d", type);
+ ret = PEVENT_ERRNO__INVALID_ARG_TYPE;
goto out_free;
}
- param = malloc_or_die(sizeof(*param));
+ param = malloc(sizeof(*param));
+ if (!param) {
+ do_warning("Failed to allocate function param");
+ ret = PEVENT_ERRNO__MEM_ALLOC_FAILED;
+ goto out_free;
+ }
param->type = type;
param->next = NULL;
@@ -5051,7 +5388,7 @@ int pevent_register_print_function(struct pevent *pevent,
out_free:
va_end(ap);
free_func_handle(func_handle);
- return -1;
+ return ret;
}
/**
@@ -5103,8 +5440,12 @@ int pevent_register_event_handler(struct pevent *pevent,
not_found:
/* Save for later use. */
- handle = malloc_or_die(sizeof(*handle));
- memset(handle, 0, sizeof(*handle));
+ handle = calloc(1, sizeof(*handle));
+ if (!handle) {
+ do_warning("Failed to allocate event handler");
+ return PEVENT_ERRNO__MEM_ALLOC_FAILED;
+ }
+
handle->id = id;
if (event_name)
handle->event_name = strdup(event_name);
@@ -5113,7 +5454,11 @@ int pevent_register_event_handler(struct pevent *pevent,
if ((event_name && !handle->event_name) ||
(sys_name && !handle->sys_name)) {
- die("Failed to allocate event/sys name");
+ do_warning("Failed to allocate event/sys name");
+ free((void *)handle->event_name);
+ free((void *)handle->sys_name);
+ free(handle);
+ return PEVENT_ERRNO__MEM_ALLOC_FAILED;
}
handle->func = func;
@@ -5129,13 +5474,10 @@ int pevent_register_event_handler(struct pevent *pevent,
*/
struct pevent *pevent_alloc(void)
{
- struct pevent *pevent;
+ struct pevent *pevent = calloc(1, sizeof(*pevent));
- pevent = malloc(sizeof(*pevent));
- if (!pevent)
- return NULL;
- memset(pevent, 0, sizeof(*pevent));
- pevent->ref_count = 1;
+ if (pevent)
+ pevent->ref_count = 1;
return pevent;
}
@@ -5164,7 +5506,7 @@ static void free_formats(struct format *format)
free_format_fields(format->fields);
}
-static void free_event(struct event_format *event)
+void pevent_free_format(struct event_format *event)
{
free(event->name);
free(event->system);
@@ -5250,7 +5592,7 @@ void pevent_free(struct pevent *pevent)
}
for (i = 0; i < pevent->nr_events; i++)
- free_event(pevent->events[i]);
+ pevent_free_format(pevent->events[i]);
while (pevent->handlers) {
handle = pevent->handlers;
diff --git a/tools/lib/traceevent/event-parse.h b/tools/lib/traceevent/event-parse.h
index 5772ad8cb386..24a4bbabc5d5 100644
--- a/tools/lib/traceevent/event-parse.h
+++ b/tools/lib/traceevent/event-parse.h
@@ -24,8 +24,8 @@
#include <stdarg.h>
#include <regex.h>
-#ifndef __unused
-#define __unused __attribute__ ((unused))
+#ifndef __maybe_unused
+#define __maybe_unused __attribute__((unused))
#endif
/* ----------------------- trace_seq ----------------------- */
@@ -49,7 +49,7 @@ struct pevent_record {
int cpu;
int ref_count;
int locked; /* Do not free, even if ref_count is zero */
- void *private;
+ void *priv;
#if DEBUG_RECORD
struct pevent_record *prev;
struct pevent_record *next;
@@ -106,7 +106,7 @@ struct plugin_option {
char *plugin_alias;
char *description;
char *value;
- void *private;
+ void *priv;
int set;
};
@@ -345,6 +345,35 @@ enum pevent_flag {
PEVENT_NSEC_OUTPUT = 1, /* output in NSECS */
};
+#define PEVENT_ERRORS \
+ _PE(MEM_ALLOC_FAILED, "failed to allocate memory"), \
+ _PE(PARSE_EVENT_FAILED, "failed to parse event"), \
+ _PE(READ_ID_FAILED, "failed to read event id"), \
+ _PE(READ_FORMAT_FAILED, "failed to read event format"), \
+ _PE(READ_PRINT_FAILED, "failed to read event print fmt"), \
+ _PE(OLD_FTRACE_ARG_FAILED,"failed to allocate field name for ftrace"),\
+ _PE(INVALID_ARG_TYPE, "invalid argument type")
+
+#undef _PE
+#define _PE(__code, __str) PEVENT_ERRNO__ ## __code
+enum pevent_errno {
+ PEVENT_ERRNO__SUCCESS = 0,
+
+ /*
+ * Choose an arbitrary negative big number not to clash with standard
+ * errno since SUS requires the errno has distinct positive values.
+ * See 'Issue 6' in the link below.
+ *
+ * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
+ */
+ __PEVENT_ERRNO__START = -100000,
+
+ PEVENT_ERRORS,
+
+ __PEVENT_ERRNO__END,
+};
+#undef _PE
+
struct cmdline;
struct cmdline_list;
struct func_map;
@@ -509,8 +538,11 @@ void pevent_print_event(struct pevent *pevent, struct trace_seq *s,
int pevent_parse_header_page(struct pevent *pevent, char *buf, unsigned long size,
int long_size);
-int pevent_parse_event(struct pevent *pevent, const char *buf,
- unsigned long size, const char *sys);
+enum pevent_errno pevent_parse_event(struct pevent *pevent, const char *buf,
+ unsigned long size, const char *sys);
+enum pevent_errno pevent_parse_format(struct event_format **eventp, const char *buf,
+ unsigned long size, const char *sys);
+void pevent_free_format(struct event_format *event);
void *pevent_get_field_raw(struct trace_seq *s, struct event_format *event,
const char *name, struct pevent_record *record,
@@ -561,6 +593,8 @@ int pevent_data_pid(struct pevent *pevent, struct pevent_record *rec);
const char *pevent_data_comm_from_pid(struct pevent *pevent, int pid);
void pevent_event_info(struct trace_seq *s, struct event_format *event,
struct pevent_record *record);
+int pevent_strerror(struct pevent *pevent, enum pevent_errno errnum,
+ char *buf, size_t buflen);
struct event_format **pevent_list_events(struct pevent *pevent, enum event_sort_type);
struct format_field **pevent_event_common_fields(struct event_format *event);
diff --git a/tools/lib/traceevent/event-utils.h b/tools/lib/traceevent/event-utils.h
index 08296383d1e6..bc075006966e 100644
--- a/tools/lib/traceevent/event-utils.h
+++ b/tools/lib/traceevent/event-utils.h
@@ -39,6 +39,12 @@ void __vdie(const char *fmt, ...);
void __vwarning(const char *fmt, ...);
void __vpr_stat(const char *fmt, ...);
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+
static inline char *strim(char *string)
{
char *ret;
diff --git a/tools/perf/.gitignore b/tools/perf/.gitignore
index 26b823b61aa1..8f8fbc227a46 100644
--- a/tools/perf/.gitignore
+++ b/tools/perf/.gitignore
@@ -21,3 +21,5 @@ config.mak
config.mak.autogen
*-bison.*
*-flex.*
+*.pyc
+*.pyo
diff --git a/tools/perf/Documentation/Makefile b/tools/perf/Documentation/Makefile
index ca600e09c8d4..9f2e44f2b17a 100644
--- a/tools/perf/Documentation/Makefile
+++ b/tools/perf/Documentation/Makefile
@@ -195,10 +195,10 @@ install-pdf: pdf
#install-html: html
# '$(SHELL_PATH_SQ)' ./install-webdoc.sh $(DESTDIR)$(htmldir)
-../PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
- $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) PERF-VERSION-FILE
+$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
+ $(QUIET_SUBDIR0)../ $(QUIET_SUBDIR1) $(OUTPUT)PERF-VERSION-FILE
--include ../PERF-VERSION-FILE
+-include $(OUTPUT)PERF-VERSION-FILE
#
# Determine "include::" file references in asciidoc files.
diff --git a/tools/perf/Documentation/jit-interface.txt b/tools/perf/Documentation/jit-interface.txt
new file mode 100644
index 000000000000..a8656f564915
--- /dev/null
+++ b/tools/perf/Documentation/jit-interface.txt
@@ -0,0 +1,15 @@
+perf supports a simple JIT interface to resolve symbols for dynamic code generated
+by a JIT.
+
+The JIT has to write a /tmp/perf-%d.map (%d = pid of process) file
+
+This is a text file.
+
+Each line has the following format, fields separated with spaces:
+
+START SIZE symbolname
+
+START and SIZE are hex numbers without 0x.
+symbolname is the rest of the line, so it could contain special characters.
+
+The ownership of the file has to match the process.
diff --git a/tools/perf/Documentation/perf-annotate.txt b/tools/perf/Documentation/perf-annotate.txt
index c89f9e1453f7..c8ffd9fd5c6a 100644
--- a/tools/perf/Documentation/perf-annotate.txt
+++ b/tools/perf/Documentation/perf-annotate.txt
@@ -85,6 +85,9 @@ OPTIONS
-M::
--disassembler-style=:: Set disassembler style for objdump.
+--objdump=<path>::
+ Path to objdump binary.
+
SEE ALSO
--------
linkperf:perf-record[1], linkperf:perf-report[1]
diff --git a/tools/perf/Documentation/perf-diff.txt b/tools/perf/Documentation/perf-diff.txt
index 74d7481ed7a6..ab7f667de1b1 100644
--- a/tools/perf/Documentation/perf-diff.txt
+++ b/tools/perf/Documentation/perf-diff.txt
@@ -17,6 +17,9 @@ captured via perf record.
If no parameters are passed it will assume perf.data.old and perf.data.
+The differential profile is displayed only for events matching both
+specified perf.data files.
+
OPTIONS
-------
-M::
diff --git a/tools/perf/Documentation/perf-kvm.txt b/tools/perf/Documentation/perf-kvm.txt
index dd84cb2f0a88..326f2cb333cb 100644
--- a/tools/perf/Documentation/perf-kvm.txt
+++ b/tools/perf/Documentation/perf-kvm.txt
@@ -12,7 +12,7 @@ SYNOPSIS
[--guestkallsyms=<path> --guestmodules=<path> | --guestvmlinux=<path>]]
{top|record|report|diff|buildid-list}
'perf kvm' [--host] [--guest] [--guestkallsyms=<path> --guestmodules=<path>
- | --guestvmlinux=<path>] {top|record|report|diff|buildid-list}
+ | --guestvmlinux=<path>] {top|record|report|diff|buildid-list|stat}
DESCRIPTION
-----------
@@ -38,6 +38,18 @@ There are a couple of variants of perf kvm:
so that other tools can be used to fetch packages with matching symbol tables
for use by perf report.
+ 'perf kvm stat <command>' to run a command and gather performance counter
+ statistics.
+ Especially, perf 'kvm stat record/report' generates a statistical analysis
+ of KVM events. Currently, vmexit, mmio and ioport events are supported.
+ 'perf kvm stat record <command>' records kvm events and the events between
+ start and end <command>.
+ And this command produces a file which contains tracing results of kvm
+ events.
+
+ 'perf kvm stat report' reports statistical data which includes events
+ handled time, samples, and so on.
+
OPTIONS
-------
-i::
@@ -68,7 +80,21 @@ OPTIONS
--guestvmlinux=<path>::
Guest os kernel vmlinux.
+STAT REPORT OPTIONS
+-------------------
+--vcpu=<value>::
+ analyze events which occures on this vcpu. (default: all vcpus)
+
+--events=<value>::
+ events to be analyzed. Possible values: vmexit, mmio, ioport.
+ (default: vmexit)
+-k::
+--key=<value>::
+ Sorting key. Possible values: sample (default, sort by samples
+ number), time (sort by average time).
+
SEE ALSO
--------
linkperf:perf-top[1], linkperf:perf-record[1], linkperf:perf-report[1],
-linkperf:perf-diff[1], linkperf:perf-buildid-list[1]
+linkperf:perf-diff[1], linkperf:perf-buildid-list[1],
+linkperf:perf-stat[1]
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt
index ddc22525228d..d1e39dc8c810 100644
--- a/tools/perf/Documentation/perf-list.txt
+++ b/tools/perf/Documentation/perf-list.txt
@@ -15,24 +15,43 @@ DESCRIPTION
This command displays the symbolic event types which can be selected in the
various perf commands with the -e option.
+[[EVENT_MODIFIERS]]
EVENT MODIFIERS
---------------
Events can optionally have a modifer by appending a colon and one or
-more modifiers. Modifiers allow the user to restrict when events are
-counted with 'u' for user-space, 'k' for kernel, 'h' for hypervisor.
-Additional modifiers are 'G' for guest counting (in KVM guests) and 'H'
-for host counting (not in KVM guests).
+more modifiers. Modifiers allow the user to restrict the events to be
+counted. The following modifiers exist:
+
+ u - user-space counting
+ k - kernel counting
+ h - hypervisor counting
+ G - guest counting (in KVM guests)
+ H - host counting (not in KVM guests)
+ p - precise level
The 'p' modifier can be used for specifying how precise the instruction
-address should be. The 'p' modifier is currently only implemented for
-Intel PEBS and can be specified multiple times:
- 0 - SAMPLE_IP can have arbitrary skid
- 1 - SAMPLE_IP must have constant skid
- 2 - SAMPLE_IP requested to have 0 skid
- 3 - SAMPLE_IP must have 0 skid
+address should be. The 'p' modifier can be specified multiple times:
+
+ 0 - SAMPLE_IP can have arbitrary skid
+ 1 - SAMPLE_IP must have constant skid
+ 2 - SAMPLE_IP requested to have 0 skid
+ 3 - SAMPLE_IP must have 0 skid
+
+For Intel systems precise event sampling is implemented with PEBS
+which supports up to precise-level 2.
-The PEBS implementation now supports up to 2.
+On AMD systems it is implemented using IBS (up to precise-level 2).
+The precise modifier works with event types 0x76 (cpu-cycles, CPU
+clocks not halted) and 0xC1 (micro-ops retired). Both events map to
+IBS execution sampling (IBS op) with the IBS Op Counter Control bit
+(IbsOpCntCtl) set respectively (see AMD64 Architecture Programmer’s
+Manual Volume 2: System Programming, 13.3 Instruction-Based
+Sampling). Examples to use IBS:
+
+ perf record -a -e cpu-cycles:p ... # use ibs op counting cycles
+ perf record -a -e r076:p ... # same as -e cpu-cycles:p
+ perf record -a -e r0C1:p ... # use ibs op counting micro-ops
RAW HARDWARE EVENT DESCRIPTOR
-----------------------------
@@ -44,6 +63,11 @@ layout of IA32_PERFEVTSELx MSRs (see [Intel® 64 and IA-32 Architectures Softwar
of IA32_PERFEVTSELx MSRs) or AMD's PerfEvtSeln (see [AMD64 Architecture Programmer’s Manual Volume 2: System Programming], Page 344,
Figure 13-7 Performance Event-Select Register (PerfEvtSeln)).
+Note: Only the following bit fields can be set in x86 counter
+registers: event, umask, edge, inv, cmask. Esp. guest/host only and
+OS/user mode flags must be setup using <<EVENT_MODIFIERS, EVENT
+MODIFIERS>>.
+
Example:
If the Intel docs for a QM720 Core i7 describe an event as:
@@ -91,4 +115,4 @@ SEE ALSO
linkperf:perf-stat[1], linkperf:perf-top[1],
linkperf:perf-record[1],
http://www.intel.com/Assets/PDF/manual/253669.pdf[Intel® 64 and IA-32 Architectures Software Developer's Manual Volume 3B: System Programming Guide],
-http://support.amd.com/us/Processor_TechDocs/24593.pdf[AMD64 Architecture Programmer’s Manual Volume 2: System Programming]
+http://support.amd.com/us/Processor_TechDocs/24593_APM_v2.pdf[AMD64 Architecture Programmer’s Manual Volume 2: System Programming]
diff --git a/tools/perf/Documentation/perf-report.txt b/tools/perf/Documentation/perf-report.txt
index 495210a612c4..f4d91bebd59d 100644
--- a/tools/perf/Documentation/perf-report.txt
+++ b/tools/perf/Documentation/perf-report.txt
@@ -168,6 +168,9 @@ OPTIONS
branch stacks and it will automatically switch to the branch view mode,
unless --no-branch-stack is used.
+--objdump=<path>::
+ Path to objdump binary.
+
SEE ALSO
--------
linkperf:perf-stat[1], linkperf:perf-annotate[1]
diff --git a/tools/perf/Documentation/perf-script-perl.txt b/tools/perf/Documentation/perf-script-perl.txt
index 3152cca15501..d00bef231340 100644
--- a/tools/perf/Documentation/perf-script-perl.txt
+++ b/tools/perf/Documentation/perf-script-perl.txt
@@ -116,8 +116,8 @@ search path and 'use'ing a few support modules (see module
descriptions below):
----
- use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/perf-script-Util/lib";
- use lib "./perf-script-Util/lib";
+ use lib "$ENV{'PERF_EXEC_PATH'}/scripts/perl/Perf-Trace-Util/lib";
+ use lib "./Perf-Trace-Util/lib";
use Perf::Trace::Core;
use Perf::Trace::Context;
use Perf::Trace::Util;
diff --git a/tools/perf/Documentation/perf-script-python.txt b/tools/perf/Documentation/perf-script-python.txt
index 471022069119..a4027f221a53 100644
--- a/tools/perf/Documentation/perf-script-python.txt
+++ b/tools/perf/Documentation/perf-script-python.txt
@@ -129,7 +129,7 @@ import os
import sys
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
- '/scripts/python/perf-script-Util/lib/Perf/Trace')
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
from perf_trace_context import *
from Core import *
@@ -216,7 +216,7 @@ import os
import sys
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
- '/scripts/python/perf-script-Util/lib/Perf/Trace')
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
from perf_trace_context import *
from Core import *
@@ -279,7 +279,7 @@ import os
import sys
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
- '/scripts/python/perf-script-Util/lib/Perf/Trace')
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
from perf_trace_context import *
from Core import *
@@ -391,7 +391,7 @@ drwxr-xr-x 4 trz trz 4096 2010-01-26 22:30 .
drwxr-xr-x 4 trz trz 4096 2010-01-26 22:29 ..
drwxr-xr-x 2 trz trz 4096 2010-01-26 22:29 bin
-rw-r--r-- 1 trz trz 2548 2010-01-26 22:29 check-perf-script.py
-drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 perf-script-Util
+drwxr-xr-x 3 trz trz 4096 2010-01-26 22:49 Perf-Trace-Util
-rw-r--r-- 1 trz trz 1462 2010-01-26 22:30 syscall-counts.py
----
@@ -518,7 +518,7 @@ descriptions below):
import sys
sys.path.append(os.environ['PERF_EXEC_PATH'] + \
- '/scripts/python/perf-script-Util/lib/Perf/Trace')
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
from perf_trace_context import *
from Core import *
diff --git a/tools/perf/Documentation/perf-trace.txt b/tools/perf/Documentation/perf-trace.txt
new file mode 100644
index 000000000000..3a2ae37310a9
--- /dev/null
+++ b/tools/perf/Documentation/perf-trace.txt
@@ -0,0 +1,53 @@
+perf-trace(1)
+=============
+
+NAME
+----
+perf-trace - strace inspired tool
+
+SYNOPSIS
+--------
+[verse]
+'perf trace'
+
+DESCRIPTION
+-----------
+This command will show the events associated with the target, initially
+syscalls, but other system events like pagefaults, task lifetime events,
+scheduling events, etc.
+
+Initially this is a live mode only tool, but eventually will work with
+perf.data files like the other tools, allowing a detached 'record' from
+analysis phases.
+
+OPTIONS
+-------
+
+--all-cpus::
+ System-wide collection from all CPUs.
+
+-p::
+--pid=::
+ Record events on existing process ID (comma separated list).
+
+--tid=::
+ Record events on existing thread ID (comma separated list).
+
+--uid=::
+ Record events in threads owned by uid. Name or number.
+
+--no-inherit::
+ Child tasks do not inherit counters.
+
+--mmap-pages=::
+ Number of mmap data pages. Must be a power of two.
+
+--cpu::
+Collect samples only on the list of CPUs provided. Multiple CPUs can be provided as a
+comma-separated list with no space: 0,1. Ranges of CPUs are specified with -: 0-2.
+In per-thread mode with inheritance mode on (default), Events are captured only when
+the thread executes on the designated CPUs. Default is to monitor all CPUs.
+
+SEE ALSO
+--------
+linkperf:perf-record[1], linkperf:perf-script[1]
diff --git a/tools/perf/MANIFEST b/tools/perf/MANIFEST
index b4b572e8c100..80db3f4bcf7a 100644
--- a/tools/perf/MANIFEST
+++ b/tools/perf/MANIFEST
@@ -10,8 +10,12 @@ include/linux/stringify.h
lib/rbtree.c
include/linux/swab.h
arch/*/include/asm/unistd*.h
+arch/*/include/asm/perf_regs.h
arch/*/lib/memcpy*.S
arch/*/lib/memset*.S
include/linux/poison.h
include/linux/magic.h
include/linux/hw_breakpoint.h
+arch/x86/include/asm/svm.h
+arch/x86/include/asm/vmx.h
+arch/x86/include/asm/kvm_host.h
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 35655c3a7b7a..e5e71e7d95a0 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -37,7 +37,14 @@ include config/utilities.mak
#
# Define NO_NEWT if you do not want TUI support.
#
+# Define NO_GTK2 if you do not want GTK+ GUI support.
+#
# Define NO_DEMANGLE if you do not want C++ symbol demangling.
+#
+# Define NO_LIBELF if you do not want libelf dependency (e.g. cross-builds)
+#
+# Define NO_LIBUNWIND if you do not want libunwind dependency for dwarf
+# backtrace post unwind.
$(OUTPUT)PERF-VERSION-FILE: .FORCE-PERF-VERSION-FILE
@$(SHELL_PATH) util/PERF-VERSION-GEN $(OUTPUT)
@@ -50,16 +57,19 @@ ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \
-e s/s390x/s390/ -e s/parisc64/parisc/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ )
+NO_PERF_REGS := 1
CC = $(CROSS_COMPILE)gcc
AR = $(CROSS_COMPILE)ar
# Additional ARCH settings for x86
ifeq ($(ARCH),i386)
- ARCH := x86
+ override ARCH := x86
+ NO_PERF_REGS := 0
+ LIBUNWIND_LIBS = -lunwind -lunwind-x86
endif
ifeq ($(ARCH),x86_64)
- ARCH := x86
+ override ARCH := x86
IS_X86_64 := 0
ifeq (, $(findstring m32,$(EXTRA_CFLAGS)))
IS_X86_64 := $(shell echo __x86_64__ | ${CC} -E -xc - | tail -n 1)
@@ -69,6 +79,8 @@ ifeq ($(ARCH),x86_64)
ARCH_CFLAGS := -DARCH_X86_64
ARCH_INCLUDE = ../../arch/x86/lib/memcpy_64.S ../../arch/x86/lib/memset_64.S
endif
+ NO_PERF_REGS := 0
+ LIBUNWIND_LIBS = -lunwind -lunwind-x86_64
endif
# Treat warnings as errors unless directed not to
@@ -89,7 +101,7 @@ ifdef PARSER_DEBUG
PARSER_DEBUG_CFLAGS := -DPARSER_DEBUG
endif
-CFLAGS = -fno-omit-frame-pointer -ggdb3 -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS)
+CFLAGS = -fno-omit-frame-pointer -ggdb3 -funwind-tables -Wall -Wextra -std=gnu99 $(CFLAGS_WERROR) $(CFLAGS_OPTIMIZE) $(EXTRA_WARNINGS) $(EXTRA_CFLAGS) $(PARSER_DEBUG_CFLAGS)
EXTLIBS = -lpthread -lrt -lelf -lm
ALL_CFLAGS = $(CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE
ALL_LDFLAGS = $(LDFLAGS)
@@ -186,10 +198,10 @@ SCRIPTS = $(patsubst %.sh,%,$(SCRIPT_SH))
TRACE_EVENT_DIR = ../lib/traceevent/
-ifeq ("$(origin O)", "command line")
- TE_PATH=$(OUTPUT)/
+ifneq ($(OUTPUT),)
+ TE_PATH=$(OUTPUT)
else
- TE_PATH=$(TRACE_EVENT_DIR)/
+ TE_PATH=$(TRACE_EVENT_DIR)
endif
LIBTRACEEVENT = $(TE_PATH)libtraceevent.a
@@ -221,13 +233,13 @@ export PERL_PATH
FLEX = flex
BISON= bison
-$(OUTPUT)util/parse-events-flex.c: util/parse-events.l
+$(OUTPUT)util/parse-events-flex.c: util/parse-events.l $(OUTPUT)util/parse-events-bison.c
$(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/parse-events-flex.h $(PARSER_DEBUG_FLEX) -t util/parse-events.l > $(OUTPUT)util/parse-events-flex.c
$(OUTPUT)util/parse-events-bison.c: util/parse-events.y
$(QUIET_BISON)$(BISON) -v util/parse-events.y -d $(PARSER_DEBUG_BISON) -o $(OUTPUT)util/parse-events-bison.c
-$(OUTPUT)util/pmu-flex.c: util/pmu.l
+$(OUTPUT)util/pmu-flex.c: util/pmu.l $(OUTPUT)util/pmu-bison.c
$(QUIET_FLEX)$(FLEX) --header-file=$(OUTPUT)util/pmu-flex.h -t util/pmu.l > $(OUTPUT)util/pmu-flex.c
$(OUTPUT)util/pmu-bison.c: util/pmu.y
@@ -252,6 +264,7 @@ LIB_H += util/include/linux/ctype.h
LIB_H += util/include/linux/kernel.h
LIB_H += util/include/linux/list.h
LIB_H += util/include/linux/export.h
+LIB_H += util/include/linux/magic.h
LIB_H += util/include/linux/poison.h
LIB_H += util/include/linux/prefetch.h
LIB_H += util/include/linux/rbtree.h
@@ -321,6 +334,10 @@ LIB_H += $(TRACE_EVENT_DIR)event-parse.h
LIB_H += util/target.h
LIB_H += util/rblist.h
LIB_H += util/intlist.h
+LIB_H += util/perf_regs.h
+LIB_H += util/unwind.h
+LIB_H += ui/helpline.h
+LIB_H += util/vdso.h
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
@@ -356,6 +373,7 @@ LIB_OBJS += $(OUTPUT)util/usage.o
LIB_OBJS += $(OUTPUT)util/wrapper.o
LIB_OBJS += $(OUTPUT)util/sigchain.o
LIB_OBJS += $(OUTPUT)util/symbol.o
+LIB_OBJS += $(OUTPUT)util/symbol-elf.o
LIB_OBJS += $(OUTPUT)util/dso-test-data.o
LIB_OBJS += $(OUTPUT)util/color.o
LIB_OBJS += $(OUTPUT)util/pager.o
@@ -387,11 +405,15 @@ LIB_OBJS += $(OUTPUT)util/cgroup.o
LIB_OBJS += $(OUTPUT)util/target.o
LIB_OBJS += $(OUTPUT)util/rblist.o
LIB_OBJS += $(OUTPUT)util/intlist.o
+LIB_OBJS += $(OUTPUT)util/vdso.o
+LIB_OBJS += $(OUTPUT)util/stat.o
-BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
+LIB_OBJS += $(OUTPUT)ui/helpline.o
+LIB_OBJS += $(OUTPUT)ui/hist.o
+LIB_OBJS += $(OUTPUT)ui/stdio/hist.o
+BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
BUILTIN_OBJS += $(OUTPUT)builtin-bench.o
-
# Benchmark modules
BUILTIN_OBJS += $(OUTPUT)bench/sched-messaging.o
BUILTIN_OBJS += $(OUTPUT)bench/sched-pipe.o
@@ -449,34 +471,73 @@ PYRF_OBJS += $(OUTPUT)util/xyarray.o
-include config.mak.autogen
-include config.mak
-ifndef NO_DWARF
-FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS)
-ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y)
- msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
+ifdef NO_LIBELF
NO_DWARF := 1
-endif # Dwarf support
-endif # NO_DWARF
-
--include arch/$(ARCH)/Makefile
-
-ifneq ($(OUTPUT),)
- BASIC_CFLAGS += -I$(OUTPUT)
-endif
-
+ NO_DEMANGLE := 1
+ NO_LIBUNWIND := 1
+else
FLAGS_LIBELF=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS)
ifneq ($(call try-cc,$(SOURCE_LIBELF),$(FLAGS_LIBELF)),y)
FLAGS_GLIBC=$(ALL_CFLAGS) $(ALL_LDFLAGS)
ifneq ($(call try-cc,$(SOURCE_GLIBC),$(FLAGS_GLIBC)),y)
msg := $(error No gnu/libc-version.h found, please install glibc-dev[el]/glibc-static);
else
- msg := $(error No libelf.h/libelf found, please install libelf-dev/elfutils-libelf-devel);
+ NO_LIBELF := 1
+ NO_DWARF := 1
+ NO_DEMANGLE := 1
endif
endif
+endif # NO_LIBELF
+
+ifndef NO_LIBUNWIND
+# for linking with debug library, run like:
+# make DEBUG=1 LIBUNWIND_DIR=/opt/libunwind/
+ifdef LIBUNWIND_DIR
+ LIBUNWIND_CFLAGS := -I$(LIBUNWIND_DIR)/include
+ LIBUNWIND_LDFLAGS := -L$(LIBUNWIND_DIR)/lib
+endif
+
+FLAGS_UNWIND=$(LIBUNWIND_CFLAGS) $(ALL_CFLAGS) $(LIBUNWIND_LDFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(LIBUNWIND_LIBS)
+ifneq ($(call try-cc,$(SOURCE_LIBUNWIND),$(FLAGS_UNWIND)),y)
+ msg := $(warning No libunwind found, disabling post unwind support. Please install libunwind-dev[el] >= 0.99);
+ NO_LIBUNWIND := 1
+endif # Libunwind support
+endif # NO_LIBUNWIND
+
+-include arch/$(ARCH)/Makefile
+
+ifneq ($(OUTPUT),)
+ BASIC_CFLAGS += -I$(OUTPUT)
+endif
+
+ifdef NO_LIBELF
+BASIC_CFLAGS += -DNO_LIBELF_SUPPORT
+
+EXTLIBS := $(filter-out -lelf,$(EXTLIBS))
+
+# Remove ELF/DWARF dependent codes
+LIB_OBJS := $(filter-out $(OUTPUT)util/symbol-elf.o,$(LIB_OBJS))
+LIB_OBJS := $(filter-out $(OUTPUT)util/dwarf-aux.o,$(LIB_OBJS))
+LIB_OBJS := $(filter-out $(OUTPUT)util/probe-event.o,$(LIB_OBJS))
+LIB_OBJS := $(filter-out $(OUTPUT)util/probe-finder.o,$(LIB_OBJS))
+
+BUILTIN_OBJS := $(filter-out $(OUTPUT)builtin-probe.o,$(BUILTIN_OBJS))
+
+# Use minimal symbol handling
+LIB_OBJS += $(OUTPUT)util/symbol-minimal.o
+
+else # NO_LIBELF
ifneq ($(call try-cc,$(SOURCE_ELF_MMAP),$(FLAGS_COMMON)),y)
BASIC_CFLAGS += -DLIBELF_NO_MMAP
endif
+FLAGS_DWARF=$(ALL_CFLAGS) -ldw -lelf $(ALL_LDFLAGS) $(EXTLIBS)
+ifneq ($(call try-cc,$(SOURCE_DWARF),$(FLAGS_DWARF)),y)
+ msg := $(warning No libdw.h found or old libdw.h found or elfutils is older than 0.138, disables dwarf support. Please install new elfutils-devel/libdw-dev);
+ NO_DWARF := 1
+endif # Dwarf support
+
ifndef NO_DWARF
ifeq ($(origin PERF_HAVE_DWARF_REGS), undefined)
msg := $(warning DWARF register mappings have not been defined for architecture $(ARCH), DWARF support disabled);
@@ -487,6 +548,29 @@ else
LIB_OBJS += $(OUTPUT)util/dwarf-aux.o
endif # PERF_HAVE_DWARF_REGS
endif # NO_DWARF
+endif # NO_LIBELF
+
+ifdef NO_LIBUNWIND
+ BASIC_CFLAGS += -DNO_LIBUNWIND_SUPPORT
+else
+ EXTLIBS += $(LIBUNWIND_LIBS)
+ BASIC_CFLAGS := $(LIBUNWIND_CFLAGS) $(BASIC_CFLAGS)
+ BASIC_LDFLAGS := $(LIBUNWIND_LDFLAGS) $(BASIC_LDFLAGS)
+ LIB_OBJS += $(OUTPUT)util/unwind.o
+endif
+
+ifdef NO_LIBAUDIT
+ BASIC_CFLAGS += -DNO_LIBAUDIT_SUPPORT
+else
+ FLAGS_LIBAUDIT = $(ALL_CFLAGS) $(ALL_LDFLAGS) -laudit
+ ifneq ($(call try-cc,$(SOURCE_LIBAUDIT),$(FLAGS_LIBAUDIT)),y)
+ msg := $(warning No libaudit.h found, disables 'trace' tool, please install audit-libs-devel or libaudit-dev);
+ BASIC_CFLAGS += -DNO_LIBAUDIT_SUPPORT
+ else
+ BUILTIN_OBJS += $(OUTPUT)builtin-trace.o
+ EXTLIBS += -laudit
+ endif
+endif
ifdef NO_NEWT
BASIC_CFLAGS += -DNO_NEWT_SUPPORT
@@ -504,14 +588,13 @@ else
LIB_OBJS += $(OUTPUT)ui/browsers/annotate.o
LIB_OBJS += $(OUTPUT)ui/browsers/hists.o
LIB_OBJS += $(OUTPUT)ui/browsers/map.o
- LIB_OBJS += $(OUTPUT)ui/helpline.o
LIB_OBJS += $(OUTPUT)ui/progress.o
LIB_OBJS += $(OUTPUT)ui/util.o
LIB_OBJS += $(OUTPUT)ui/tui/setup.o
LIB_OBJS += $(OUTPUT)ui/tui/util.o
+ LIB_OBJS += $(OUTPUT)ui/tui/helpline.o
LIB_H += ui/browser.h
LIB_H += ui/browsers/map.h
- LIB_H += ui/helpline.h
LIB_H += ui/keysyms.h
LIB_H += ui/libslang.h
LIB_H += ui/progress.h
@@ -523,7 +606,7 @@ endif
ifdef NO_GTK2
BASIC_CFLAGS += -DNO_GTK2_SUPPORT
else
- FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0)
+ FLAGS_GTK2=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) $(shell pkg-config --libs --cflags gtk+-2.0 2>/dev/null)
ifneq ($(call try-cc,$(SOURCE_GTK2),$(FLAGS_GTK2)),y)
msg := $(warning GTK2 not found, disables GTK2 support. Please install gtk2-devel or libgtk2.0-dev);
BASIC_CFLAGS += -DNO_GTK2_SUPPORT
@@ -531,11 +614,12 @@ else
ifeq ($(call try-cc,$(SOURCE_GTK2_INFOBAR),$(FLAGS_GTK2)),y)
BASIC_CFLAGS += -DHAVE_GTK_INFO_BAR
endif
- BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0)
- EXTLIBS += $(shell pkg-config --libs gtk+-2.0)
+ BASIC_CFLAGS += $(shell pkg-config --cflags gtk+-2.0 2>/dev/null)
+ EXTLIBS += $(shell pkg-config --libs gtk+-2.0 2>/dev/null)
LIB_OBJS += $(OUTPUT)ui/gtk/browser.o
LIB_OBJS += $(OUTPUT)ui/gtk/setup.o
LIB_OBJS += $(OUTPUT)ui/gtk/util.o
+ LIB_OBJS += $(OUTPUT)ui/gtk/helpline.o
# Make sure that it'd be included only once.
ifneq ($(findstring -DNO_NEWT_SUPPORT,$(BASIC_CFLAGS)),)
LIB_OBJS += $(OUTPUT)ui/setup.o
@@ -644,7 +728,7 @@ else
EXTLIBS += -liberty
BASIC_CFLAGS += -DHAVE_CPLUS_DEMANGLE
else
- FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -lbfd
+ FLAGS_BFD=$(ALL_CFLAGS) $(ALL_LDFLAGS) $(EXTLIBS) -DPACKAGE='perf' -lbfd
has_bfd := $(call try-cc,$(SOURCE_BFD),$(FLAGS_BFD))
ifeq ($(has_bfd),y)
EXTLIBS += -lbfd
@@ -674,6 +758,13 @@ else
endif
endif
+ifeq ($(NO_PERF_REGS),0)
+ ifeq ($(ARCH),x86)
+ LIB_H += arch/x86/include/perf_regs.h
+ endif
+else
+ BASIC_CFLAGS += -DNO_PERF_REGS
+endif
ifdef NO_STRLCPY
BASIC_CFLAGS += -DNO_STRLCPY
@@ -683,6 +774,14 @@ else
endif
endif
+ifdef NO_BACKTRACE
+ BASIC_CFLAGS += -DNO_BACKTRACE
+else
+ ifneq ($(call try-cc,$(SOURCE_BACKTRACE),),y)
+ BASIC_CFLAGS += -DNO_BACKTRACE
+ endif
+endif
+
ifdef ASCIIDOC8
export ASCIIDOC8
endif
@@ -700,6 +799,7 @@ perfexecdir_SQ = $(subst ','\'',$(perfexecdir))
template_dir_SQ = $(subst ','\'',$(template_dir))
htmldir_SQ = $(subst ','\'',$(htmldir))
prefix_SQ = $(subst ','\'',$(prefix))
+sysconfdir_SQ = $(subst ','\'',$(sysconfdir))
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
@@ -767,10 +867,10 @@ $(OUTPUT)perf.o perf.spec \
# over the general rule for .o
$(OUTPUT)util/%-flex.o: $(OUTPUT)util/%-flex.c $(OUTPUT)PERF-CFLAGS
- $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -Iutil/ -w $<
+ $(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(ALL_CFLAGS) -w $<
$(OUTPUT)util/%-bison.o: $(OUTPUT)util/%-bison.c $(OUTPUT)PERF-CFLAGS
- $(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -Iutil/ -w $<
+ $(QUIET_CC)$(CC) -o $@ -c -Iutil/ $(ALL_CFLAGS) -DYYENABLE_NLS=0 -DYYLTYPE_IS_TRIVIAL=0 -w $<
$(OUTPUT)%.o: %.c $(OUTPUT)PERF-CFLAGS
$(QUIET_CC)$(CC) -o $@ -c $(ALL_CFLAGS) $<
@@ -842,7 +942,10 @@ $(LIB_FILE): $(LIB_OBJS)
# libtraceevent.a
$(LIBTRACEEVENT):
- $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) $(COMMAND_O) libtraceevent.a
+ $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) libtraceevent.a
+
+$(LIBTRACEEVENT)-clean:
+ $(QUIET_SUBDIR0)$(TRACE_EVENT_DIR) $(QUIET_SUBDIR1) O=$(OUTPUT) clean
help:
@echo 'Perf make targets:'
@@ -951,6 +1054,8 @@ install: all
$(INSTALL) scripts/python/Perf-Trace-Util/lib/Perf/Trace/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/Perf-Trace-Util/lib/Perf/Trace'
$(INSTALL) scripts/python/*.py -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python'
$(INSTALL) scripts/python/bin/* -t '$(DESTDIR_SQ)$(perfexec_instdir_SQ)/scripts/python/bin'
+ $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d'
+ $(INSTALL) bash_completion '$(DESTDIR_SQ)$(sysconfdir_SQ)/bash_completion.d/perf'
install-python_ext:
$(PYTHON_WORD) util/setup.py --quiet install --root='/$(DESTDIR_SQ)'
@@ -981,7 +1086,7 @@ quick-install-html:
### Cleaning rules
-clean:
+clean: $(LIBTRACEEVENT)-clean
$(RM) $(LIB_OBJS) $(BUILTIN_OBJS) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf.o $(LANG_BINDINGS)
$(RM) $(ALL_PROGRAMS) perf
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
diff --git a/tools/perf/arch/x86/Makefile b/tools/perf/arch/x86/Makefile
index 744e629797be..815841c04eb2 100644
--- a/tools/perf/arch/x86/Makefile
+++ b/tools/perf/arch/x86/Makefile
@@ -2,4 +2,7 @@ ifndef NO_DWARF
PERF_HAVE_DWARF_REGS := 1
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/dwarf-regs.o
endif
+ifndef NO_LIBUNWIND
+LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/unwind.o
+endif
LIB_OBJS += $(OUTPUT)arch/$(ARCH)/util/header.o
diff --git a/tools/perf/arch/x86/include/perf_regs.h b/tools/perf/arch/x86/include/perf_regs.h
new file mode 100644
index 000000000000..46fc9f15c6b3
--- /dev/null
+++ b/tools/perf/arch/x86/include/perf_regs.h
@@ -0,0 +1,80 @@
+#ifndef ARCH_PERF_REGS_H
+#define ARCH_PERF_REGS_H
+
+#include <stdlib.h>
+#include "../../util/types.h"
+#include "../../../../../arch/x86/include/asm/perf_regs.h"
+
+#ifndef ARCH_X86_64
+#define PERF_REGS_MASK ((1ULL << PERF_REG_X86_32_MAX) - 1)
+#else
+#define REG_NOSUPPORT ((1ULL << PERF_REG_X86_DS) | \
+ (1ULL << PERF_REG_X86_ES) | \
+ (1ULL << PERF_REG_X86_FS) | \
+ (1ULL << PERF_REG_X86_GS))
+#define PERF_REGS_MASK (((1ULL << PERF_REG_X86_64_MAX) - 1) & ~REG_NOSUPPORT)
+#endif
+#define PERF_REG_IP PERF_REG_X86_IP
+#define PERF_REG_SP PERF_REG_X86_SP
+
+static inline const char *perf_reg_name(int id)
+{
+ switch (id) {
+ case PERF_REG_X86_AX:
+ return "AX";
+ case PERF_REG_X86_BX:
+ return "BX";
+ case PERF_REG_X86_CX:
+ return "CX";
+ case PERF_REG_X86_DX:
+ return "DX";
+ case PERF_REG_X86_SI:
+ return "SI";
+ case PERF_REG_X86_DI:
+ return "DI";
+ case PERF_REG_X86_BP:
+ return "BP";
+ case PERF_REG_X86_SP:
+ return "SP";
+ case PERF_REG_X86_IP:
+ return "IP";
+ case PERF_REG_X86_FLAGS:
+ return "FLAGS";
+ case PERF_REG_X86_CS:
+ return "CS";
+ case PERF_REG_X86_SS:
+ return "SS";
+ case PERF_REG_X86_DS:
+ return "DS";
+ case PERF_REG_X86_ES:
+ return "ES";
+ case PERF_REG_X86_FS:
+ return "FS";
+ case PERF_REG_X86_GS:
+ return "GS";
+#ifdef ARCH_X86_64
+ case PERF_REG_X86_R8:
+ return "R8";
+ case PERF_REG_X86_R9:
+ return "R9";
+ case PERF_REG_X86_R10:
+ return "R10";
+ case PERF_REG_X86_R11:
+ return "R11";
+ case PERF_REG_X86_R12:
+ return "R12";
+ case PERF_REG_X86_R13:
+ return "R13";
+ case PERF_REG_X86_R14:
+ return "R14";
+ case PERF_REG_X86_R15:
+ return "R15";
+#endif /* ARCH_X86_64 */
+ default:
+ return NULL;
+ }
+
+ return NULL;
+}
+
+#endif /* ARCH_PERF_REGS_H */
diff --git a/tools/perf/arch/x86/util/unwind.c b/tools/perf/arch/x86/util/unwind.c
new file mode 100644
index 000000000000..78d956eff96f
--- /dev/null
+++ b/tools/perf/arch/x86/util/unwind.c
@@ -0,0 +1,111 @@
+
+#include <errno.h>
+#include <libunwind.h>
+#include "perf_regs.h"
+#include "../../util/unwind.h"
+
+#ifdef ARCH_X86_64
+int unwind__arch_reg_id(int regnum)
+{
+ int id;
+
+ switch (regnum) {
+ case UNW_X86_64_RAX:
+ id = PERF_REG_X86_AX;
+ break;
+ case UNW_X86_64_RDX:
+ id = PERF_REG_X86_DX;
+ break;
+ case UNW_X86_64_RCX:
+ id = PERF_REG_X86_CX;
+ break;
+ case UNW_X86_64_RBX:
+ id = PERF_REG_X86_BX;
+ break;
+ case UNW_X86_64_RSI:
+ id = PERF_REG_X86_SI;
+ break;
+ case UNW_X86_64_RDI:
+ id = PERF_REG_X86_DI;
+ break;
+ case UNW_X86_64_RBP:
+ id = PERF_REG_X86_BP;
+ break;
+ case UNW_X86_64_RSP:
+ id = PERF_REG_X86_SP;
+ break;
+ case UNW_X86_64_R8:
+ id = PERF_REG_X86_R8;
+ break;
+ case UNW_X86_64_R9:
+ id = PERF_REG_X86_R9;
+ break;
+ case UNW_X86_64_R10:
+ id = PERF_REG_X86_R10;
+ break;
+ case UNW_X86_64_R11:
+ id = PERF_REG_X86_R11;
+ break;
+ case UNW_X86_64_R12:
+ id = PERF_REG_X86_R12;
+ break;
+ case UNW_X86_64_R13:
+ id = PERF_REG_X86_R13;
+ break;
+ case UNW_X86_64_R14:
+ id = PERF_REG_X86_R14;
+ break;
+ case UNW_X86_64_R15:
+ id = PERF_REG_X86_R15;
+ break;
+ case UNW_X86_64_RIP:
+ id = PERF_REG_X86_IP;
+ break;
+ default:
+ pr_err("unwind: invalid reg id %d\n", regnum);
+ return -EINVAL;
+ }
+
+ return id;
+}
+#else
+int unwind__arch_reg_id(int regnum)
+{
+ int id;
+
+ switch (regnum) {
+ case UNW_X86_EAX:
+ id = PERF_REG_X86_AX;
+ break;
+ case UNW_X86_EDX:
+ id = PERF_REG_X86_DX;
+ break;
+ case UNW_X86_ECX:
+ id = PERF_REG_X86_CX;
+ break;
+ case UNW_X86_EBX:
+ id = PERF_REG_X86_BX;
+ break;
+ case UNW_X86_ESI:
+ id = PERF_REG_X86_SI;
+ break;
+ case UNW_X86_EDI:
+ id = PERF_REG_X86_DI;
+ break;
+ case UNW_X86_EBP:
+ id = PERF_REG_X86_BP;
+ break;
+ case UNW_X86_ESP:
+ id = PERF_REG_X86_SP;
+ break;
+ case UNW_X86_EIP:
+ id = PERF_REG_X86_IP;
+ break;
+ default:
+ pr_err("unwind: invalid reg id %d\n", regnum);
+ return -EINVAL;
+ }
+
+ return id;
+}
+#endif /* ARCH_X86_64 */
diff --git a/tools/perf/bash_completion b/tools/perf/bash_completion
new file mode 100644
index 000000000000..1958fa539d0f
--- /dev/null
+++ b/tools/perf/bash_completion
@@ -0,0 +1,26 @@
+# perf completion
+
+have perf &&
+_perf()
+{
+ local cur cmd
+
+ COMPREPLY=()
+ _get_comp_words_by_ref cur prev
+
+ cmd=${COMP_WORDS[0]}
+
+ # List perf subcommands
+ if [ $COMP_CWORD -eq 1 ]; then
+ cmds=$($cmd --list-cmds)
+ COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) )
+ # List possible events for -e option
+ elif [[ $prev == "-e" && "${COMP_WORDS[1]}" == @(record|stat|top) ]]; then
+ cmds=$($cmd list --raw-dump)
+ COMPREPLY=( $( compgen -W '$cmds' -- "$cur" ) )
+ # Fall down to list regular files
+ else
+ _filedir
+ fi
+} &&
+complete -F _perf perf
diff --git a/tools/perf/bench/bench.h b/tools/perf/bench/bench.h
index a09bece6dad2..8f89998eeaf4 100644
--- a/tools/perf/bench/bench.h
+++ b/tools/perf/bench/bench.h
@@ -3,7 +3,8 @@
extern int bench_sched_messaging(int argc, const char **argv, const char *prefix);
extern int bench_sched_pipe(int argc, const char **argv, const char *prefix);
-extern int bench_mem_memcpy(int argc, const char **argv, const char *prefix __used);
+extern int bench_mem_memcpy(int argc, const char **argv,
+ const char *prefix __maybe_unused);
extern int bench_mem_memset(int argc, const char **argv, const char *prefix);
#define BENCH_FORMAT_DEFAULT_STR "default"
diff --git a/tools/perf/bench/mem-memcpy.c b/tools/perf/bench/mem-memcpy.c
index 02dad5d3359b..93c83e3cb4a7 100644
--- a/tools/perf/bench/mem-memcpy.c
+++ b/tools/perf/bench/mem-memcpy.c
@@ -177,7 +177,7 @@ static double do_memcpy_gettimeofday(memcpy_t fn, size_t len, bool prefault)
} while (0)
int bench_mem_memcpy(int argc, const char **argv,
- const char *prefix __used)
+ const char *prefix __maybe_unused)
{
int i;
size_t len;
diff --git a/tools/perf/bench/mem-memset.c b/tools/perf/bench/mem-memset.c
index 350cc9557265..c6e4bc523492 100644
--- a/tools/perf/bench/mem-memset.c
+++ b/tools/perf/bench/mem-memset.c
@@ -171,7 +171,7 @@ static double do_memset_gettimeofday(memset_t fn, size_t len, bool prefault)
} while (0)
int bench_mem_memset(int argc, const char **argv,
- const char *prefix __used)
+ const char *prefix __maybe_unused)
{
int i;
size_t len;
diff --git a/tools/perf/bench/sched-messaging.c b/tools/perf/bench/sched-messaging.c
index d1d1b30f99c1..cc1190a0849b 100644
--- a/tools/perf/bench/sched-messaging.c
+++ b/tools/perf/bench/sched-messaging.c
@@ -267,7 +267,7 @@ static const char * const bench_sched_message_usage[] = {
};
int bench_sched_messaging(int argc, const char **argv,
- const char *prefix __used)
+ const char *prefix __maybe_unused)
{
unsigned int i, total_children;
struct timeval start, stop, diff;
diff --git a/tools/perf/bench/sched-pipe.c b/tools/perf/bench/sched-pipe.c
index 0c7454f8b8a9..69cfba8d4c6c 100644
--- a/tools/perf/bench/sched-pipe.c
+++ b/tools/perf/bench/sched-pipe.c
@@ -43,7 +43,7 @@ static const char * const bench_sched_pipe_usage[] = {
};
int bench_sched_pipe(int argc, const char **argv,
- const char *prefix __used)
+ const char *prefix __maybe_unused)
{
int pipe_1[2], pipe_2[2];
int m = 0, i;
@@ -55,14 +55,14 @@ int bench_sched_pipe(int argc, const char **argv,
* discarding returned value of read(), write()
* causes error in building environment for perf
*/
- int __used ret, wait_stat;
- pid_t pid, retpid;
+ int __maybe_unused ret, wait_stat;
+ pid_t pid, retpid __maybe_unused;
argc = parse_options(argc, argv, options,
bench_sched_pipe_usage, 0);
- assert(!pipe(pipe_1));
- assert(!pipe(pipe_2));
+ BUG_ON(pipe(pipe_1));
+ BUG_ON(pipe(pipe_2));
pid = fork();
assert(pid >= 0);
diff --git a/tools/perf/builtin-annotate.c b/tools/perf/builtin-annotate.c
index 67522cf87405..9ea38540b873 100644
--- a/tools/perf/builtin-annotate.c
+++ b/tools/perf/builtin-annotate.c
@@ -239,7 +239,7 @@ static const char * const annotate_usage[] = {
NULL
};
-int cmd_annotate(int argc, const char **argv, const char *prefix __used)
+int cmd_annotate(int argc, const char **argv, const char *prefix __maybe_unused)
{
struct perf_annotate annotate = {
.tool = {
@@ -282,6 +282,8 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)
"Display raw encoding of assembly instructions (default)"),
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
"Specify disassembler style (e.g. -M intel for intel syntax)"),
+ OPT_STRING(0, "objdump", &objdump_path, "path",
+ "objdump binary to use for disassembly and annotations"),
OPT_END()
};
diff --git a/tools/perf/builtin-bench.c b/tools/perf/builtin-bench.c
index 1f3100216448..cae9a5fd2ecf 100644
--- a/tools/perf/builtin-bench.c
+++ b/tools/perf/builtin-bench.c
@@ -173,7 +173,7 @@ static void all_subsystem(void)
all_suite(&subsystems[i]);
}
-int cmd_bench(int argc, const char **argv, const char *prefix __used)
+int cmd_bench(int argc, const char **argv, const char *prefix __maybe_unused)
{
int i, j, status = 0;
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 29ad20e67919..83654557e108 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -43,15 +43,16 @@ static int build_id_cache__add_file(const char *filename, const char *debugdir)
}
build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
- err = build_id_cache__add_s(sbuild_id, debugdir, filename, false);
+ err = build_id_cache__add_s(sbuild_id, debugdir, filename,
+ false, false);
if (verbose)
pr_info("Adding %s %s: %s\n", sbuild_id, filename,
err ? "FAIL" : "Ok");
return err;
}
-static int build_id_cache__remove_file(const char *filename __used,
- const char *debugdir __used)
+static int build_id_cache__remove_file(const char *filename __maybe_unused,
+ const char *debugdir __maybe_unused)
{
u8 build_id[BUILD_ID_SIZE];
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
@@ -119,7 +120,8 @@ static int __cmd_buildid_cache(void)
return 0;
}
-int cmd_buildid_cache(int argc, const char **argv, const char *prefix __used)
+int cmd_buildid_cache(int argc, const char **argv,
+ const char *prefix __maybe_unused)
{
argc = parse_options(argc, argv, buildid_cache_options,
buildid_cache_usage, 0);
diff --git a/tools/perf/builtin-buildid-list.c b/tools/perf/builtin-buildid-list.c
index 6b2bcfbde150..1159feeebb19 100644
--- a/tools/perf/builtin-buildid-list.c
+++ b/tools/perf/builtin-buildid-list.c
@@ -16,8 +16,6 @@
#include "util/session.h"
#include "util/symbol.h"
-#include <libelf.h>
-
static const char *input_name;
static bool force;
static bool show_kernel;
@@ -71,7 +69,7 @@ static int perf_session__list_build_ids(void)
{
struct perf_session *session;
- elf_version(EV_CURRENT);
+ symbol__elf_init();
session = perf_session__new(input_name, O_RDONLY, force, false,
&build_id__mark_dso_hit_ops);
@@ -105,7 +103,8 @@ static int __cmd_buildid_list(void)
return perf_session__list_build_ids();
}
-int cmd_buildid_list(int argc, const char **argv, const char *prefix __used)
+int cmd_buildid_list(int argc, const char **argv,
+ const char *prefix __maybe_unused)
{
argc = parse_options(argc, argv, options, buildid_list_usage, 0);
setup_pager();
diff --git a/tools/perf/builtin-diff.c b/tools/perf/builtin-diff.c
index d29d350fb2b7..761f4197a9e2 100644
--- a/tools/perf/builtin-diff.c
+++ b/tools/perf/builtin-diff.c
@@ -10,6 +10,7 @@
#include "util/event.h"
#include "util/hist.h"
#include "util/evsel.h"
+#include "util/evlist.h"
#include "util/session.h"
#include "util/tool.h"
#include "util/sort.h"
@@ -24,11 +25,6 @@ static char diff__default_sort_order[] = "dso,symbol";
static bool force;
static bool show_displacement;
-struct perf_diff {
- struct perf_tool tool;
- struct perf_session *session;
-};
-
static int hists__add_entry(struct hists *self,
struct addr_location *al, u64 period)
{
@@ -37,14 +33,12 @@ static int hists__add_entry(struct hists *self,
return -ENOMEM;
}
-static int diff__process_sample_event(struct perf_tool *tool,
+static int diff__process_sample_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel __used,
+ struct perf_evsel *evsel,
struct machine *machine)
{
- struct perf_diff *_diff = container_of(tool, struct perf_diff, tool);
- struct perf_session *session = _diff->session;
struct addr_location al;
if (perf_event__preprocess_sample(event, machine, &al, sample, NULL) < 0) {
@@ -56,26 +50,24 @@ static int diff__process_sample_event(struct perf_tool *tool,
if (al.filtered || al.sym == NULL)
return 0;
- if (hists__add_entry(&session->hists, &al, sample->period)) {
+ if (hists__add_entry(&evsel->hists, &al, sample->period)) {
pr_warning("problem incrementing symbol period, skipping event\n");
return -1;
}
- session->hists.stats.total_period += sample->period;
+ evsel->hists.stats.total_period += sample->period;
return 0;
}
-static struct perf_diff diff = {
- .tool = {
- .sample = diff__process_sample_event,
- .mmap = perf_event__process_mmap,
- .comm = perf_event__process_comm,
- .exit = perf_event__process_task,
- .fork = perf_event__process_task,
- .lost = perf_event__process_lost,
- .ordered_samples = true,
- .ordering_requires_timestamps = true,
- },
+static struct perf_tool tool = {
+ .sample = diff__process_sample_event,
+ .mmap = perf_event__process_mmap,
+ .comm = perf_event__process_comm,
+ .exit = perf_event__process_task,
+ .fork = perf_event__process_task,
+ .lost = perf_event__process_lost,
+ .ordered_samples = true,
+ .ordering_requires_timestamps = true,
};
static void perf_session__insert_hist_entry_by_name(struct rb_root *root,
@@ -146,34 +138,71 @@ static void hists__match(struct hists *older, struct hists *newer)
}
}
+static struct perf_evsel *evsel_match(struct perf_evsel *evsel,
+ struct perf_evlist *evlist)
+{
+ struct perf_evsel *e;
+
+ list_for_each_entry(e, &evlist->entries, node)
+ if (perf_evsel__match2(evsel, e))
+ return e;
+
+ return NULL;
+}
+
static int __cmd_diff(void)
{
int ret, i;
#define older (session[0])
#define newer (session[1])
struct perf_session *session[2];
+ struct perf_evlist *evlist_new, *evlist_old;
+ struct perf_evsel *evsel;
+ bool first = true;
older = perf_session__new(input_old, O_RDONLY, force, false,
- &diff.tool);
+ &tool);
newer = perf_session__new(input_new, O_RDONLY, force, false,
- &diff.tool);
+ &tool);
if (session[0] == NULL || session[1] == NULL)
return -ENOMEM;
for (i = 0; i < 2; ++i) {
- diff.session = session[i];
- ret = perf_session__process_events(session[i], &diff.tool);
+ ret = perf_session__process_events(session[i], &tool);
if (ret)
goto out_delete;
- hists__output_resort(&session[i]->hists);
}
- if (show_displacement)
- hists__resort_entries(&older->hists);
+ evlist_old = older->evlist;
+ evlist_new = newer->evlist;
+
+ list_for_each_entry(evsel, &evlist_new->entries, node)
+ hists__output_resort(&evsel->hists);
+
+ list_for_each_entry(evsel, &evlist_old->entries, node) {
+ hists__output_resort(&evsel->hists);
+
+ if (show_displacement)
+ hists__resort_entries(&evsel->hists);
+ }
+
+ list_for_each_entry(evsel, &evlist_new->entries, node) {
+ struct perf_evsel *evsel_old;
+
+ evsel_old = evsel_match(evsel, evlist_old);
+ if (!evsel_old)
+ continue;
+
+ fprintf(stdout, "%s# Event '%s'\n#\n", first ? "" : "\n",
+ perf_evsel__name(evsel));
+
+ first = false;
+
+ hists__match(&evsel_old->hists, &evsel->hists);
+ hists__fprintf(&evsel->hists, &evsel_old->hists,
+ show_displacement, true, 0, 0, stdout);
+ }
- hists__match(&older->hists, &newer->hists);
- hists__fprintf(&newer->hists, &older->hists,
- show_displacement, true, 0, 0, stdout);
out_delete:
for (i = 0; i < 2; ++i)
perf_session__delete(session[i]);
@@ -213,7 +242,7 @@ static const struct option options[] = {
OPT_END()
};
-int cmd_diff(int argc, const char **argv, const char *prefix __used)
+int cmd_diff(int argc, const char **argv, const char *prefix __maybe_unused)
{
sort_order = diff__default_sort_order;
argc = parse_options(argc, argv, options, diff_usage, 0);
@@ -235,6 +264,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix __used)
if (symbol__init() < 0)
return -1;
+ perf_hpp__init(true, show_displacement);
setup_sorting(diff_usage, options);
setup_pager();
diff --git a/tools/perf/builtin-evlist.c b/tools/perf/builtin-evlist.c
index 0dd5a058f766..1fb164164fd0 100644
--- a/tools/perf/builtin-evlist.c
+++ b/tools/perf/builtin-evlist.c
@@ -113,7 +113,7 @@ static const char * const evlist_usage[] = {
NULL
};
-int cmd_evlist(int argc, const char **argv, const char *prefix __used)
+int cmd_evlist(int argc, const char **argv, const char *prefix __maybe_unused)
{
struct perf_attr_details details = { .verbose = false, };
const char *input_name = NULL;
diff --git a/tools/perf/builtin-help.c b/tools/perf/builtin-help.c
index 6d5a8a7faf48..25c8b942ff85 100644
--- a/tools/perf/builtin-help.c
+++ b/tools/perf/builtin-help.c
@@ -24,13 +24,14 @@ static struct man_viewer_info_list {
} *man_viewer_info_list;
enum help_format {
+ HELP_FORMAT_NONE,
HELP_FORMAT_MAN,
HELP_FORMAT_INFO,
HELP_FORMAT_WEB,
};
static bool show_all = false;
-static enum help_format help_format = HELP_FORMAT_MAN;
+static enum help_format help_format = HELP_FORMAT_NONE;
static struct option builtin_help_options[] = {
OPT_BOOLEAN('a', "all", &show_all, "print all available commands"),
OPT_SET_UINT('m', "man", &help_format, "show man page", HELP_FORMAT_MAN),
@@ -54,7 +55,9 @@ static enum help_format parse_help_format(const char *format)
return HELP_FORMAT_INFO;
if (!strcmp(format, "web") || !strcmp(format, "html"))
return HELP_FORMAT_WEB;
- die("unrecognized help format '%s'", format);
+
+ pr_err("unrecognized help format '%s'", format);
+ return HELP_FORMAT_NONE;
}
static const char *get_man_viewer_info(const char *name)
@@ -259,6 +262,8 @@ static int perf_help_config(const char *var, const char *value, void *cb)
if (!value)
return config_error_nonbool(var);
help_format = parse_help_format(value);
+ if (help_format == HELP_FORMAT_NONE)
+ return -1;
return 0;
}
if (!strcmp(var, "man.viewer")) {
@@ -352,7 +357,7 @@ static void exec_viewer(const char *name, const char *page)
warning("'%s': unknown man viewer.", name);
}
-static void show_man_page(const char *perf_cmd)
+static int show_man_page(const char *perf_cmd)
{
struct man_viewer_list *viewer;
const char *page = cmd_to_page(perf_cmd);
@@ -365,28 +370,35 @@ static void show_man_page(const char *perf_cmd)
if (fallback)
exec_viewer(fallback, page);
exec_viewer("man", page);
- die("no man viewer handled the request");
+
+ pr_err("no man viewer handled the request");
+ return -1;
}
-static void show_info_page(const char *perf_cmd)
+static int show_info_page(const char *perf_cmd)
{
const char *page = cmd_to_page(perf_cmd);
setenv("INFOPATH", system_path(PERF_INFO_PATH), 1);
execlp("info", "info", "perfman", page, NULL);
+ return -1;
}
-static void get_html_page_path(struct strbuf *page_path, const char *page)
+static int get_html_page_path(struct strbuf *page_path, const char *page)
{
struct stat st;
const char *html_path = system_path(PERF_HTML_PATH);
/* Check that we have a perf documentation directory. */
if (stat(mkpath("%s/perf.html", html_path), &st)
- || !S_ISREG(st.st_mode))
- die("'%s': not a documentation directory.", html_path);
+ || !S_ISREG(st.st_mode)) {
+ pr_err("'%s': not a documentation directory.", html_path);
+ return -1;
+ }
strbuf_init(page_path, 0);
strbuf_addf(page_path, "%s/%s.html", html_path, page);
+
+ return 0;
}
/*
@@ -401,19 +413,23 @@ static void open_html(const char *path)
}
#endif
-static void show_html_page(const char *perf_cmd)
+static int show_html_page(const char *perf_cmd)
{
const char *page = cmd_to_page(perf_cmd);
struct strbuf page_path; /* it leaks but we exec bellow */
- get_html_page_path(&page_path, page);
+ if (get_html_page_path(&page_path, page) != 0)
+ return -1;
open_html(page_path.buf);
+
+ return 0;
}
-int cmd_help(int argc, const char **argv, const char *prefix __used)
+int cmd_help(int argc, const char **argv, const char *prefix __maybe_unused)
{
const char *alias;
+ int rc = 0;
load_command_list("perf-", &main_cmds, &other_cmds);
@@ -444,16 +460,20 @@ int cmd_help(int argc, const char **argv, const char *prefix __used)
switch (help_format) {
case HELP_FORMAT_MAN:
- show_man_page(argv[0]);
+ rc = show_man_page(argv[0]);
break;
case HELP_FORMAT_INFO:
- show_info_page(argv[0]);
+ rc = show_info_page(argv[0]);
break;
case HELP_FORMAT_WEB:
- show_html_page(argv[0]);
+ rc = show_html_page(argv[0]);
+ break;
+ case HELP_FORMAT_NONE:
+ /* fall-through */
default:
+ rc = -1;
break;
}
- return 0;
+ return rc;
}
diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 3beab489afc5..1eaa6617c814 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -17,9 +17,9 @@
static char const *input_name = "-";
static bool inject_build_ids;
-static int perf_event__repipe_synth(struct perf_tool *tool __used,
+static int perf_event__repipe_synth(struct perf_tool *tool __maybe_unused,
union perf_event *event,
- struct machine *machine __used)
+ struct machine *machine __maybe_unused)
{
uint32_t size;
void *buf = event;
@@ -40,7 +40,8 @@ static int perf_event__repipe_synth(struct perf_tool *tool __used,
static int perf_event__repipe_op2_synth(struct perf_tool *tool,
union perf_event *event,
- struct perf_session *session __used)
+ struct perf_session *session
+ __maybe_unused)
{
return perf_event__repipe_synth(tool, event, NULL);
}
@@ -52,13 +53,14 @@ static int perf_event__repipe_event_type_synth(struct perf_tool *tool,
}
static int perf_event__repipe_tracing_data_synth(union perf_event *event,
- struct perf_session *session __used)
+ struct perf_session *session
+ __maybe_unused)
{
return perf_event__repipe_synth(NULL, event, NULL);
}
static int perf_event__repipe_attr(union perf_event *event,
- struct perf_evlist **pevlist __used)
+ struct perf_evlist **pevlist __maybe_unused)
{
int ret;
ret = perf_event__process_attr(event, pevlist);
@@ -70,7 +72,7 @@ static int perf_event__repipe_attr(union perf_event *event,
static int perf_event__repipe(struct perf_tool *tool,
union perf_event *event,
- struct perf_sample *sample __used,
+ struct perf_sample *sample __maybe_unused,
struct machine *machine)
{
return perf_event__repipe_synth(tool, event, machine);
@@ -78,8 +80,8 @@ static int perf_event__repipe(struct perf_tool *tool,
static int perf_event__repipe_sample(struct perf_tool *tool,
union perf_event *event,
- struct perf_sample *sample __used,
- struct perf_evsel *evsel __used,
+ struct perf_sample *sample __maybe_unused,
+ struct perf_evsel *evsel __maybe_unused,
struct machine *machine)
{
return perf_event__repipe_synth(tool, event, machine);
@@ -163,7 +165,7 @@ static int dso__inject_build_id(struct dso *self, struct perf_tool *tool,
static int perf_event__inject_buildid(struct perf_tool *tool,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel __used,
+ struct perf_evsel *evsel __maybe_unused,
struct machine *machine)
{
struct addr_location al;
@@ -191,10 +193,13 @@ static int perf_event__inject_buildid(struct perf_tool *tool,
* If this fails, too bad, let the other side
* account this as unresolved.
*/
- } else
+ } else {
+#ifndef NO_LIBELF_SUPPORT
pr_warning("no symbols found in %s, maybe "
"install a debug package?\n",
al.map->dso->long_name);
+#endif
+ }
}
}
@@ -221,7 +226,7 @@ struct perf_tool perf_inject = {
extern volatile int session_done;
-static void sig_handler(int sig __attribute__((__unused__)))
+static void sig_handler(int sig __maybe_unused)
{
session_done = 1;
}
@@ -264,7 +269,7 @@ static const struct option options[] = {
OPT_END()
};
-int cmd_inject(int argc, const char **argv, const char *prefix __used)
+int cmd_inject(int argc, const char **argv, const char *prefix __maybe_unused)
{
argc = parse_options(argc, argv, options, report_usage, 0);
diff --git a/tools/perf/builtin-kmem.c b/tools/perf/builtin-kmem.c
index ce35015f2dc6..bc912c68f49a 100644
--- a/tools/perf/builtin-kmem.c
+++ b/tools/perf/builtin-kmem.c
@@ -1,6 +1,8 @@
#include "builtin.h"
#include "perf.h"
+#include "util/evlist.h"
+#include "util/evsel.h"
#include "util/util.h"
#include "util/cache.h"
#include "util/symbol.h"
@@ -57,46 +59,52 @@ static unsigned long nr_allocs, nr_cross_allocs;
#define PATH_SYS_NODE "/sys/devices/system/node"
-struct perf_kmem {
- struct perf_tool tool;
- struct perf_session *session;
-};
-
-static void init_cpunode_map(void)
+static int init_cpunode_map(void)
{
FILE *fp;
- int i;
+ int i, err = -1;
fp = fopen("/sys/devices/system/cpu/kernel_max", "r");
if (!fp) {
max_cpu_num = 4096;
- return;
+ return 0;
+ }
+
+ if (fscanf(fp, "%d", &max_cpu_num) < 1) {
+ pr_err("Failed to read 'kernel_max' from sysfs");
+ goto out_close;
}
- if (fscanf(fp, "%d", &max_cpu_num) < 1)
- die("Failed to read 'kernel_max' from sysfs");
max_cpu_num++;
cpunode_map = calloc(max_cpu_num, sizeof(int));
- if (!cpunode_map)
- die("calloc");
+ if (!cpunode_map) {
+ pr_err("%s: calloc failed\n", __func__);
+ goto out_close;
+ }
+
for (i = 0; i < max_cpu_num; i++)
cpunode_map[i] = -1;
+
+ err = 0;
+out_close:
fclose(fp);
+ return err;
}
-static void setup_cpunode_map(void)
+static int setup_cpunode_map(void)
{
struct dirent *dent1, *dent2;
DIR *dir1, *dir2;
unsigned int cpu, mem;
char buf[PATH_MAX];
- init_cpunode_map();
+ if (init_cpunode_map())
+ return -1;
dir1 = opendir(PATH_SYS_NODE);
if (!dir1)
- return;
+ return -1;
while ((dent1 = readdir(dir1)) != NULL) {
if (dent1->d_type != DT_DIR ||
@@ -116,10 +124,11 @@ static void setup_cpunode_map(void)
closedir(dir2);
}
closedir(dir1);
+ return 0;
}
-static void insert_alloc_stat(unsigned long call_site, unsigned long ptr,
- int bytes_req, int bytes_alloc, int cpu)
+static int insert_alloc_stat(unsigned long call_site, unsigned long ptr,
+ int bytes_req, int bytes_alloc, int cpu)
{
struct rb_node **node = &root_alloc_stat.rb_node;
struct rb_node *parent = NULL;
@@ -143,8 +152,10 @@ static void insert_alloc_stat(unsigned long call_site, unsigned long ptr,
data->bytes_alloc += bytes_alloc;
} else {
data = malloc(sizeof(*data));
- if (!data)
- die("malloc");
+ if (!data) {
+ pr_err("%s: malloc failed\n", __func__);
+ return -1;
+ }
data->ptr = ptr;
data->pingpong = 0;
data->hit = 1;
@@ -156,9 +167,10 @@ static void insert_alloc_stat(unsigned long call_site, unsigned long ptr,
}
data->call_site = call_site;
data->alloc_cpu = cpu;
+ return 0;
}
-static void insert_caller_stat(unsigned long call_site,
+static int insert_caller_stat(unsigned long call_site,
int bytes_req, int bytes_alloc)
{
struct rb_node **node = &root_caller_stat.rb_node;
@@ -183,8 +195,10 @@ static void insert_caller_stat(unsigned long call_site,
data->bytes_alloc += bytes_alloc;
} else {
data = malloc(sizeof(*data));
- if (!data)
- die("malloc");
+ if (!data) {
+ pr_err("%s: malloc failed\n", __func__);
+ return -1;
+ }
data->call_site = call_site;
data->pingpong = 0;
data->hit = 1;
@@ -194,39 +208,43 @@ static void insert_caller_stat(unsigned long call_site,
rb_link_node(&data->node, parent, node);
rb_insert_color(&data->node, &root_caller_stat);
}
+
+ return 0;
}
-static void process_alloc_event(void *data,
- struct event_format *event,
- int cpu,
- u64 timestamp __used,
- struct thread *thread __used,
- int node)
+static int perf_evsel__process_alloc_event(struct perf_evsel *evsel,
+ struct perf_sample *sample)
{
- unsigned long call_site;
- unsigned long ptr;
- int bytes_req;
- int bytes_alloc;
- int node1, node2;
-
- ptr = raw_field_value(event, "ptr", data);
- call_site = raw_field_value(event, "call_site", data);
- bytes_req = raw_field_value(event, "bytes_req", data);
- bytes_alloc = raw_field_value(event, "bytes_alloc", data);
+ unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr"),
+ call_site = perf_evsel__intval(evsel, sample, "call_site");
+ int bytes_req = perf_evsel__intval(evsel, sample, "bytes_req"),
+ bytes_alloc = perf_evsel__intval(evsel, sample, "bytes_alloc");
- insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, cpu);
- insert_caller_stat(call_site, bytes_req, bytes_alloc);
+ if (insert_alloc_stat(call_site, ptr, bytes_req, bytes_alloc, sample->cpu) ||
+ insert_caller_stat(call_site, bytes_req, bytes_alloc))
+ return -1;
total_requested += bytes_req;
total_allocated += bytes_alloc;
- if (node) {
- node1 = cpunode_map[cpu];
- node2 = raw_field_value(event, "node", data);
+ nr_allocs++;
+ return 0;
+}
+
+static int perf_evsel__process_alloc_node_event(struct perf_evsel *evsel,
+ struct perf_sample *sample)
+{
+ int ret = perf_evsel__process_alloc_event(evsel, sample);
+
+ if (!ret) {
+ int node1 = cpunode_map[sample->cpu],
+ node2 = perf_evsel__intval(evsel, sample, "node");
+
if (node1 != node2)
nr_cross_allocs++;
}
- nr_allocs++;
+
+ return ret;
}
static int ptr_cmp(struct alloc_stat *, struct alloc_stat *);
@@ -257,66 +275,37 @@ static struct alloc_stat *search_alloc_stat(unsigned long ptr,
return NULL;
}
-static void process_free_event(void *data,
- struct event_format *event,
- int cpu,
- u64 timestamp __used,
- struct thread *thread __used)
+static int perf_evsel__process_free_event(struct perf_evsel *evsel,
+ struct perf_sample *sample)
{
- unsigned long ptr;
+ unsigned long ptr = perf_evsel__intval(evsel, sample, "ptr");
struct alloc_stat *s_alloc, *s_caller;
- ptr = raw_field_value(event, "ptr", data);
-
s_alloc = search_alloc_stat(ptr, 0, &root_alloc_stat, ptr_cmp);
if (!s_alloc)
- return;
+ return 0;
- if (cpu != s_alloc->alloc_cpu) {
+ if ((short)sample->cpu != s_alloc->alloc_cpu) {
s_alloc->pingpong++;
s_caller = search_alloc_stat(0, s_alloc->call_site,
&root_caller_stat, callsite_cmp);
- assert(s_caller);
+ if (!s_caller)
+ return -1;
s_caller->pingpong++;
}
s_alloc->alloc_cpu = -1;
-}
-static void process_raw_event(struct perf_tool *tool,
- union perf_event *raw_event __used, void *data,
- int cpu, u64 timestamp, struct thread *thread)
-{
- struct perf_kmem *kmem = container_of(tool, struct perf_kmem, tool);
- struct event_format *event;
- int type;
-
- type = trace_parse_common_type(kmem->session->pevent, data);
- event = pevent_find_event(kmem->session->pevent, type);
-
- if (!strcmp(event->name, "kmalloc") ||
- !strcmp(event->name, "kmem_cache_alloc")) {
- process_alloc_event(data, event, cpu, timestamp, thread, 0);
- return;
- }
-
- if (!strcmp(event->name, "kmalloc_node") ||
- !strcmp(event->name, "kmem_cache_alloc_node")) {
- process_alloc_event(data, event, cpu, timestamp, thread, 1);
- return;
- }
-
- if (!strcmp(event->name, "kfree") ||
- !strcmp(event->name, "kmem_cache_free")) {
- process_free_event(data, event, cpu, timestamp, thread);
- return;
- }
+ return 0;
}
-static int process_sample_event(struct perf_tool *tool,
+typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
+ struct perf_sample *sample);
+
+static int process_sample_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel __used,
+ struct perf_evsel *evsel,
struct machine *machine)
{
struct thread *thread = machine__findnew_thread(machine, event->ip.pid);
@@ -329,18 +318,18 @@ static int process_sample_event(struct perf_tool *tool,
dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
- process_raw_event(tool, event, sample->raw_data, sample->cpu,
- sample->time, thread);
+ if (evsel->handler.func != NULL) {
+ tracepoint_handler f = evsel->handler.func;
+ return f(evsel, sample);
+ }
return 0;
}
-static struct perf_kmem perf_kmem = {
- .tool = {
- .sample = process_sample_event,
- .comm = perf_event__process_comm,
- .ordered_samples = true,
- },
+static struct perf_tool perf_kmem = {
+ .sample = process_sample_event,
+ .comm = perf_event__process_comm,
+ .ordered_samples = true,
};
static double fragmentation(unsigned long n_req, unsigned long n_alloc)
@@ -496,22 +485,32 @@ static int __cmd_kmem(void)
{
int err = -EINVAL;
struct perf_session *session;
-
- session = perf_session__new(input_name, O_RDONLY, 0, false,
- &perf_kmem.tool);
+ const struct perf_evsel_str_handler kmem_tracepoints[] = {
+ { "kmem:kmalloc", perf_evsel__process_alloc_event, },
+ { "kmem:kmem_cache_alloc", perf_evsel__process_alloc_event, },
+ { "kmem:kmalloc_node", perf_evsel__process_alloc_node_event, },
+ { "kmem:kmem_cache_alloc_node", perf_evsel__process_alloc_node_event, },
+ { "kmem:kfree", perf_evsel__process_free_event, },
+ { "kmem:kmem_cache_free", perf_evsel__process_free_event, },
+ };
+
+ session = perf_session__new(input_name, O_RDONLY, 0, false, &perf_kmem);
if (session == NULL)
return -ENOMEM;
- perf_kmem.session = session;
-
if (perf_session__create_kernel_maps(session) < 0)
goto out_delete;
if (!perf_session__has_traces(session, "kmem record"))
goto out_delete;
+ if (perf_session__set_tracepoints_handlers(session, kmem_tracepoints)) {
+ pr_err("Initializing perf session tracepoint handlers failed\n");
+ return -1;
+ }
+
setup_pager();
- err = perf_session__process_events(session, &perf_kmem.tool);
+ err = perf_session__process_events(session, &perf_kmem);
if (err != 0)
goto out_delete;
sort_result();
@@ -635,8 +634,10 @@ static int sort_dimension__add(const char *tok, struct list_head *list)
for (i = 0; i < NUM_AVAIL_SORTS; i++) {
if (!strcmp(avail_sorts[i]->name, tok)) {
sort = malloc(sizeof(*sort));
- if (!sort)
- die("malloc");
+ if (!sort) {
+ pr_err("%s: malloc failed\n", __func__);
+ return -1;
+ }
memcpy(sort, avail_sorts[i], sizeof(*sort));
list_add_tail(&sort->list, list);
return 0;
@@ -651,8 +652,10 @@ static int setup_sorting(struct list_head *sort_list, const char *arg)
char *tok;
char *str = strdup(arg);
- if (!str)
- die("strdup");
+ if (!str) {
+ pr_err("%s: strdup failed\n", __func__);
+ return -1;
+ }
while (true) {
tok = strsep(&str, ",");
@@ -669,8 +672,8 @@ static int setup_sorting(struct list_head *sort_list, const char *arg)
return 0;
}
-static int parse_sort_opt(const struct option *opt __used,
- const char *arg, int unset __used)
+static int parse_sort_opt(const struct option *opt __maybe_unused,
+ const char *arg, int unset __maybe_unused)
{
if (!arg)
return -1;
@@ -683,22 +686,24 @@ static int parse_sort_opt(const struct option *opt __used,
return 0;
}
-static int parse_caller_opt(const struct option *opt __used,
- const char *arg __used, int unset __used)
+static int parse_caller_opt(const struct option *opt __maybe_unused,
+ const char *arg __maybe_unused,
+ int unset __maybe_unused)
{
caller_flag = (alloc_flag + 1);
return 0;
}
-static int parse_alloc_opt(const struct option *opt __used,
- const char *arg __used, int unset __used)
+static int parse_alloc_opt(const struct option *opt __maybe_unused,
+ const char *arg __maybe_unused,
+ int unset __maybe_unused)
{
alloc_flag = (caller_flag + 1);
return 0;
}
-static int parse_line_opt(const struct option *opt __used,
- const char *arg, int unset __used)
+static int parse_line_opt(const struct option *opt __maybe_unused,
+ const char *arg, int unset __maybe_unused)
{
int lines;
@@ -768,7 +773,7 @@ static int __cmd_record(int argc, const char **argv)
return cmd_record(i, rec_argv, NULL);
}
-int cmd_kmem(int argc, const char **argv, const char *prefix __used)
+int cmd_kmem(int argc, const char **argv, const char *prefix __maybe_unused)
{
argc = parse_options(argc, argv, kmem_options, kmem_usage, 0);
@@ -780,7 +785,8 @@ int cmd_kmem(int argc, const char **argv, const char *prefix __used)
if (!strncmp(argv[0], "rec", 3)) {
return __cmd_record(argc, argv);
} else if (!strcmp(argv[0], "stat")) {
- setup_cpunode_map();
+ if (setup_cpunode_map())
+ return -1;
if (list_empty(&caller_sort))
setup_sorting(&caller_sort, default_sort_order);
diff --git a/tools/perf/builtin-kvm.c b/tools/perf/builtin-kvm.c
index 9fc6e0fa3dce..a28c9cad9048 100644
--- a/tools/perf/builtin-kvm.c
+++ b/tools/perf/builtin-kvm.c
@@ -1,6 +1,7 @@
#include "builtin.h"
#include "perf.h"
+#include "util/evsel.h"
#include "util/util.h"
#include "util/cache.h"
#include "util/symbol.h"
@@ -10,8 +11,10 @@
#include "util/parse-options.h"
#include "util/trace-event.h"
-
#include "util/debug.h"
+#include "util/debugfs.h"
+#include "util/tool.h"
+#include "util/stat.h"
#include <sys/prctl.h>
@@ -19,11 +22,836 @@
#include <pthread.h>
#include <math.h>
-static const char *file_name;
+#include "../../arch/x86/include/asm/svm.h"
+#include "../../arch/x86/include/asm/vmx.h"
+#include "../../arch/x86/include/asm/kvm.h"
+
+struct event_key {
+ #define INVALID_KEY (~0ULL)
+ u64 key;
+ int info;
+};
+
+struct kvm_events_ops {
+ bool (*is_begin_event)(struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct event_key *key);
+ bool (*is_end_event)(struct perf_evsel *evsel,
+ struct perf_sample *sample, struct event_key *key);
+ void (*decode_key)(struct event_key *key, char decode[20]);
+ const char *name;
+};
+
+static void exit_event_get_key(struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct event_key *key)
+{
+ key->info = 0;
+ key->key = perf_evsel__intval(evsel, sample, "exit_reason");
+}
+
+static bool kvm_exit_event(struct perf_evsel *evsel)
+{
+ return !strcmp(evsel->name, "kvm:kvm_exit");
+}
+
+static bool exit_event_begin(struct perf_evsel *evsel,
+ struct perf_sample *sample, struct event_key *key)
+{
+ if (kvm_exit_event(evsel)) {
+ exit_event_get_key(evsel, sample, key);
+ return true;
+ }
+
+ return false;
+}
+
+static bool kvm_entry_event(struct perf_evsel *evsel)
+{
+ return !strcmp(evsel->name, "kvm:kvm_entry");
+}
+
+static bool exit_event_end(struct perf_evsel *evsel,
+ struct perf_sample *sample __maybe_unused,
+ struct event_key *key __maybe_unused)
+{
+ return kvm_entry_event(evsel);
+}
+
+struct exit_reasons_table {
+ unsigned long exit_code;
+ const char *reason;
+};
+
+struct exit_reasons_table vmx_exit_reasons[] = {
+ VMX_EXIT_REASONS
+};
+
+struct exit_reasons_table svm_exit_reasons[] = {
+ SVM_EXIT_REASONS
+};
+
+static int cpu_isa;
+
+static const char *get_exit_reason(u64 exit_code)
+{
+ int table_size = ARRAY_SIZE(svm_exit_reasons);
+ struct exit_reasons_table *table = svm_exit_reasons;
+
+ if (cpu_isa == 1) {
+ table = vmx_exit_reasons;
+ table_size = ARRAY_SIZE(vmx_exit_reasons);
+ }
+
+ while (table_size--) {
+ if (table->exit_code == exit_code)
+ return table->reason;
+ table++;
+ }
+
+ pr_err("unknown kvm exit code:%lld on %s\n",
+ (unsigned long long)exit_code, cpu_isa ? "VMX" : "SVM");
+ return "UNKNOWN";
+}
+
+static void exit_event_decode_key(struct event_key *key, char decode[20])
+{
+ const char *exit_reason = get_exit_reason(key->key);
+
+ scnprintf(decode, 20, "%s", exit_reason);
+}
+
+static struct kvm_events_ops exit_events = {
+ .is_begin_event = exit_event_begin,
+ .is_end_event = exit_event_end,
+ .decode_key = exit_event_decode_key,
+ .name = "VM-EXIT"
+};
+
+ /*
+ * For the mmio events, we treat:
+ * the time of MMIO write: kvm_mmio(KVM_TRACE_MMIO_WRITE...) -> kvm_entry
+ * the time of MMIO read: kvm_exit -> kvm_mmio(KVM_TRACE_MMIO_READ...).
+ */
+static void mmio_event_get_key(struct perf_evsel *evsel, struct perf_sample *sample,
+ struct event_key *key)
+{
+ key->key = perf_evsel__intval(evsel, sample, "gpa");
+ key->info = perf_evsel__intval(evsel, sample, "type");
+}
+
+#define KVM_TRACE_MMIO_READ_UNSATISFIED 0
+#define KVM_TRACE_MMIO_READ 1
+#define KVM_TRACE_MMIO_WRITE 2
+
+static bool mmio_event_begin(struct perf_evsel *evsel,
+ struct perf_sample *sample, struct event_key *key)
+{
+ /* MMIO read begin event in kernel. */
+ if (kvm_exit_event(evsel))
+ return true;
+
+ /* MMIO write begin event in kernel. */
+ if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
+ perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_WRITE) {
+ mmio_event_get_key(evsel, sample, key);
+ return true;
+ }
+
+ return false;
+}
+
+static bool mmio_event_end(struct perf_evsel *evsel, struct perf_sample *sample,
+ struct event_key *key)
+{
+ /* MMIO write end event in kernel. */
+ if (kvm_entry_event(evsel))
+ return true;
+
+ /* MMIO read end event in kernel.*/
+ if (!strcmp(evsel->name, "kvm:kvm_mmio") &&
+ perf_evsel__intval(evsel, sample, "type") == KVM_TRACE_MMIO_READ) {
+ mmio_event_get_key(evsel, sample, key);
+ return true;
+ }
+
+ return false;
+}
+
+static void mmio_event_decode_key(struct event_key *key, char decode[20])
+{
+ scnprintf(decode, 20, "%#lx:%s", (unsigned long)key->key,
+ key->info == KVM_TRACE_MMIO_WRITE ? "W" : "R");
+}
+
+static struct kvm_events_ops mmio_events = {
+ .is_begin_event = mmio_event_begin,
+ .is_end_event = mmio_event_end,
+ .decode_key = mmio_event_decode_key,
+ .name = "MMIO Access"
+};
+
+ /* The time of emulation pio access is from kvm_pio to kvm_entry. */
+static void ioport_event_get_key(struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct event_key *key)
+{
+ key->key = perf_evsel__intval(evsel, sample, "port");
+ key->info = perf_evsel__intval(evsel, sample, "rw");
+}
+
+static bool ioport_event_begin(struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct event_key *key)
+{
+ if (!strcmp(evsel->name, "kvm:kvm_pio")) {
+ ioport_event_get_key(evsel, sample, key);
+ return true;
+ }
+
+ return false;
+}
+
+static bool ioport_event_end(struct perf_evsel *evsel,
+ struct perf_sample *sample __maybe_unused,
+ struct event_key *key __maybe_unused)
+{
+ return kvm_entry_event(evsel);
+}
+
+static void ioport_event_decode_key(struct event_key *key, char decode[20])
+{
+ scnprintf(decode, 20, "%#llx:%s", (unsigned long long)key->key,
+ key->info ? "POUT" : "PIN");
+}
+
+static struct kvm_events_ops ioport_events = {
+ .is_begin_event = ioport_event_begin,
+ .is_end_event = ioport_event_end,
+ .decode_key = ioport_event_decode_key,
+ .name = "IO Port Access"
+};
+
+static const char *report_event = "vmexit";
+struct kvm_events_ops *events_ops;
+
+static bool register_kvm_events_ops(void)
+{
+ bool ret = true;
+
+ if (!strcmp(report_event, "vmexit"))
+ events_ops = &exit_events;
+ else if (!strcmp(report_event, "mmio"))
+ events_ops = &mmio_events;
+ else if (!strcmp(report_event, "ioport"))
+ events_ops = &ioport_events;
+ else {
+ pr_err("Unknown report event:%s\n", report_event);
+ ret = false;
+ }
+
+ return ret;
+}
+
+struct kvm_event_stats {
+ u64 time;
+ struct stats stats;
+};
+
+struct kvm_event {
+ struct list_head hash_entry;
+ struct rb_node rb;
+
+ struct event_key key;
+
+ struct kvm_event_stats total;
+
+ #define DEFAULT_VCPU_NUM 8
+ int max_vcpu;
+ struct kvm_event_stats *vcpu;
+};
+
+struct vcpu_event_record {
+ int vcpu_id;
+ u64 start_time;
+ struct kvm_event *last_event;
+};
+
+#define EVENTS_BITS 12
+#define EVENTS_CACHE_SIZE (1UL << EVENTS_BITS)
+
+static u64 total_time;
+static u64 total_count;
+static struct list_head kvm_events_cache[EVENTS_CACHE_SIZE];
+
+static void init_kvm_event_record(void)
+{
+ int i;
+
+ for (i = 0; i < (int)EVENTS_CACHE_SIZE; i++)
+ INIT_LIST_HEAD(&kvm_events_cache[i]);
+}
+
+static int kvm_events_hash_fn(u64 key)
+{
+ return key & (EVENTS_CACHE_SIZE - 1);
+}
+
+static bool kvm_event_expand(struct kvm_event *event, int vcpu_id)
+{
+ int old_max_vcpu = event->max_vcpu;
+
+ if (vcpu_id < event->max_vcpu)
+ return true;
+
+ while (event->max_vcpu <= vcpu_id)
+ event->max_vcpu += DEFAULT_VCPU_NUM;
+
+ event->vcpu = realloc(event->vcpu,
+ event->max_vcpu * sizeof(*event->vcpu));
+ if (!event->vcpu) {
+ pr_err("Not enough memory\n");
+ return false;
+ }
+
+ memset(event->vcpu + old_max_vcpu, 0,
+ (event->max_vcpu - old_max_vcpu) * sizeof(*event->vcpu));
+ return true;
+}
+
+static struct kvm_event *kvm_alloc_init_event(struct event_key *key)
+{
+ struct kvm_event *event;
+
+ event = zalloc(sizeof(*event));
+ if (!event) {
+ pr_err("Not enough memory\n");
+ return NULL;
+ }
+
+ event->key = *key;
+ return event;
+}
+
+static struct kvm_event *find_create_kvm_event(struct event_key *key)
+{
+ struct kvm_event *event;
+ struct list_head *head;
+
+ BUG_ON(key->key == INVALID_KEY);
+
+ head = &kvm_events_cache[kvm_events_hash_fn(key->key)];
+ list_for_each_entry(event, head, hash_entry)
+ if (event->key.key == key->key && event->key.info == key->info)
+ return event;
+
+ event = kvm_alloc_init_event(key);
+ if (!event)
+ return NULL;
+
+ list_add(&event->hash_entry, head);
+ return event;
+}
+
+static bool handle_begin_event(struct vcpu_event_record *vcpu_record,
+ struct event_key *key, u64 timestamp)
+{
+ struct kvm_event *event = NULL;
+
+ if (key->key != INVALID_KEY)
+ event = find_create_kvm_event(key);
+
+ vcpu_record->last_event = event;
+ vcpu_record->start_time = timestamp;
+ return true;
+}
+
+static void
+kvm_update_event_stats(struct kvm_event_stats *kvm_stats, u64 time_diff)
+{
+ kvm_stats->time += time_diff;
+ update_stats(&kvm_stats->stats, time_diff);
+}
+
+static double kvm_event_rel_stddev(int vcpu_id, struct kvm_event *event)
+{
+ struct kvm_event_stats *kvm_stats = &event->total;
+
+ if (vcpu_id != -1)
+ kvm_stats = &event->vcpu[vcpu_id];
+
+ return rel_stddev_stats(stddev_stats(&kvm_stats->stats),
+ avg_stats(&kvm_stats->stats));
+}
+
+static bool update_kvm_event(struct kvm_event *event, int vcpu_id,
+ u64 time_diff)
+{
+ kvm_update_event_stats(&event->total, time_diff);
+
+ if (!kvm_event_expand(event, vcpu_id))
+ return false;
+
+ kvm_update_event_stats(&event->vcpu[vcpu_id], time_diff);
+ return true;
+}
+
+static bool handle_end_event(struct vcpu_event_record *vcpu_record,
+ struct event_key *key, u64 timestamp)
+{
+ struct kvm_event *event;
+ u64 time_begin, time_diff;
+
+ event = vcpu_record->last_event;
+ time_begin = vcpu_record->start_time;
+
+ /* The begin event is not caught. */
+ if (!time_begin)
+ return true;
+
+ /*
+ * In some case, the 'begin event' only records the start timestamp,
+ * the actual event is recognized in the 'end event' (e.g. mmio-event).
+ */
+
+ /* Both begin and end events did not get the key. */
+ if (!event && key->key == INVALID_KEY)
+ return true;
+
+ if (!event)
+ event = find_create_kvm_event(key);
+
+ if (!event)
+ return false;
+
+ vcpu_record->last_event = NULL;
+ vcpu_record->start_time = 0;
+
+ BUG_ON(timestamp < time_begin);
+
+ time_diff = timestamp - time_begin;
+ return update_kvm_event(event, vcpu_record->vcpu_id, time_diff);
+}
+
+static
+struct vcpu_event_record *per_vcpu_record(struct thread *thread,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample)
+{
+ /* Only kvm_entry records vcpu id. */
+ if (!thread->priv && kvm_entry_event(evsel)) {
+ struct vcpu_event_record *vcpu_record;
+
+ vcpu_record = zalloc(sizeof(*vcpu_record));
+ if (!vcpu_record) {
+ pr_err("%s: Not enough memory\n", __func__);
+ return NULL;
+ }
+
+ vcpu_record->vcpu_id = perf_evsel__intval(evsel, sample, "vcpu_id");
+ thread->priv = vcpu_record;
+ }
+
+ return thread->priv;
+}
+
+static bool handle_kvm_event(struct thread *thread, struct perf_evsel *evsel,
+ struct perf_sample *sample)
+{
+ struct vcpu_event_record *vcpu_record;
+ struct event_key key = {.key = INVALID_KEY};
+
+ vcpu_record = per_vcpu_record(thread, evsel, sample);
+ if (!vcpu_record)
+ return true;
+
+ if (events_ops->is_begin_event(evsel, sample, &key))
+ return handle_begin_event(vcpu_record, &key, sample->time);
+
+ if (events_ops->is_end_event(evsel, sample, &key))
+ return handle_end_event(vcpu_record, &key, sample->time);
+
+ return true;
+}
+
+typedef int (*key_cmp_fun)(struct kvm_event*, struct kvm_event*, int);
+struct kvm_event_key {
+ const char *name;
+ key_cmp_fun key;
+};
+
+static int trace_vcpu = -1;
+#define GET_EVENT_KEY(func, field) \
+static u64 get_event_ ##func(struct kvm_event *event, int vcpu) \
+{ \
+ if (vcpu == -1) \
+ return event->total.field; \
+ \
+ if (vcpu >= event->max_vcpu) \
+ return 0; \
+ \
+ return event->vcpu[vcpu].field; \
+}
+
+#define COMPARE_EVENT_KEY(func, field) \
+GET_EVENT_KEY(func, field) \
+static int compare_kvm_event_ ## func(struct kvm_event *one, \
+ struct kvm_event *two, int vcpu)\
+{ \
+ return get_event_ ##func(one, vcpu) > \
+ get_event_ ##func(two, vcpu); \
+}
+
+GET_EVENT_KEY(time, time);
+COMPARE_EVENT_KEY(count, stats.n);
+COMPARE_EVENT_KEY(mean, stats.mean);
+
+#define DEF_SORT_NAME_KEY(name, compare_key) \
+ { #name, compare_kvm_event_ ## compare_key }
+
+static struct kvm_event_key keys[] = {
+ DEF_SORT_NAME_KEY(sample, count),
+ DEF_SORT_NAME_KEY(time, mean),
+ { NULL, NULL }
+};
+
+static const char *sort_key = "sample";
+static key_cmp_fun compare;
+
+static bool select_key(void)
+{
+ int i;
+
+ for (i = 0; keys[i].name; i++) {
+ if (!strcmp(keys[i].name, sort_key)) {
+ compare = keys[i].key;
+ return true;
+ }
+ }
+
+ pr_err("Unknown compare key:%s\n", sort_key);
+ return false;
+}
+
+static struct rb_root result;
+static void insert_to_result(struct kvm_event *event, key_cmp_fun bigger,
+ int vcpu)
+{
+ struct rb_node **rb = &result.rb_node;
+ struct rb_node *parent = NULL;
+ struct kvm_event *p;
+
+ while (*rb) {
+ p = container_of(*rb, struct kvm_event, rb);
+ parent = *rb;
+
+ if (bigger(event, p, vcpu))
+ rb = &(*rb)->rb_left;
+ else
+ rb = &(*rb)->rb_right;
+ }
+
+ rb_link_node(&event->rb, parent, rb);
+ rb_insert_color(&event->rb, &result);
+}
+
+static void update_total_count(struct kvm_event *event, int vcpu)
+{
+ total_count += get_event_count(event, vcpu);
+ total_time += get_event_time(event, vcpu);
+}
+
+static bool event_is_valid(struct kvm_event *event, int vcpu)
+{
+ return !!get_event_count(event, vcpu);
+}
+
+static void sort_result(int vcpu)
+{
+ unsigned int i;
+ struct kvm_event *event;
+
+ for (i = 0; i < EVENTS_CACHE_SIZE; i++)
+ list_for_each_entry(event, &kvm_events_cache[i], hash_entry)
+ if (event_is_valid(event, vcpu)) {
+ update_total_count(event, vcpu);
+ insert_to_result(event, compare, vcpu);
+ }
+}
+
+/* returns left most element of result, and erase it */
+static struct kvm_event *pop_from_result(void)
+{
+ struct rb_node *node = rb_first(&result);
+
+ if (!node)
+ return NULL;
+
+ rb_erase(node, &result);
+ return container_of(node, struct kvm_event, rb);
+}
+
+static void print_vcpu_info(int vcpu)
+{
+ pr_info("Analyze events for ");
+
+ if (vcpu == -1)
+ pr_info("all VCPUs:\n\n");
+ else
+ pr_info("VCPU %d:\n\n", vcpu);
+}
+
+static void print_result(int vcpu)
+{
+ char decode[20];
+ struct kvm_event *event;
+
+ pr_info("\n\n");
+ print_vcpu_info(vcpu);
+ pr_info("%20s ", events_ops->name);
+ pr_info("%10s ", "Samples");
+ pr_info("%9s ", "Samples%");
+
+ pr_info("%9s ", "Time%");
+ pr_info("%16s ", "Avg time");
+ pr_info("\n\n");
+
+ while ((event = pop_from_result())) {
+ u64 ecount, etime;
+
+ ecount = get_event_count(event, vcpu);
+ etime = get_event_time(event, vcpu);
+
+ events_ops->decode_key(&event->key, decode);
+ pr_info("%20s ", decode);
+ pr_info("%10llu ", (unsigned long long)ecount);
+ pr_info("%8.2f%% ", (double)ecount / total_count * 100);
+ pr_info("%8.2f%% ", (double)etime / total_time * 100);
+ pr_info("%9.2fus ( +-%7.2f%% )", (double)etime / ecount/1e3,
+ kvm_event_rel_stddev(vcpu, event));
+ pr_info("\n");
+ }
+
+ pr_info("\nTotal Samples:%lld, Total events handled time:%.2fus.\n\n",
+ (unsigned long long)total_count, total_time / 1e3);
+}
+
+static int process_sample_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct perf_evsel *evsel,
+ struct machine *machine)
+{
+ struct thread *thread = machine__findnew_thread(machine, sample->tid);
+
+ if (thread == NULL) {
+ pr_debug("problem processing %d event, skipping it.\n",
+ event->header.type);
+ return -1;
+ }
+
+ if (!handle_kvm_event(thread, evsel, sample))
+ return -1;
+
+ return 0;
+}
+
+static struct perf_tool eops = {
+ .sample = process_sample_event,
+ .comm = perf_event__process_comm,
+ .ordered_samples = true,
+};
+
+static int get_cpu_isa(struct perf_session *session)
+{
+ char *cpuid = session->header.env.cpuid;
+ int isa;
+
+ if (strstr(cpuid, "Intel"))
+ isa = 1;
+ else if (strstr(cpuid, "AMD"))
+ isa = 0;
+ else {
+ pr_err("CPU %s is not supported.\n", cpuid);
+ isa = -ENOTSUP;
+ }
+
+ return isa;
+}
+
+static const char *file_name;
+
+static int read_events(void)
+{
+ struct perf_session *kvm_session;
+ int ret;
+
+ kvm_session = perf_session__new(file_name, O_RDONLY, 0, false, &eops);
+ if (!kvm_session) {
+ pr_err("Initializing perf session failed\n");
+ return -EINVAL;
+ }
+
+ if (!perf_session__has_traces(kvm_session, "kvm record"))
+ return -EINVAL;
+
+ /*
+ * Do not use 'isa' recorded in kvm_exit tracepoint since it is not
+ * traced in the old kernel.
+ */
+ ret = get_cpu_isa(kvm_session);
+
+ if (ret < 0)
+ return ret;
+
+ cpu_isa = ret;
+
+ return perf_session__process_events(kvm_session, &eops);
+}
+
+static bool verify_vcpu(int vcpu)
+{
+ if (vcpu != -1 && vcpu < 0) {
+ pr_err("Invalid vcpu:%d.\n", vcpu);
+ return false;
+ }
+
+ return true;
+}
+
+static int kvm_events_report_vcpu(int vcpu)
+{
+ int ret = -EINVAL;
+
+ if (!verify_vcpu(vcpu))
+ goto exit;
+
+ if (!select_key())
+ goto exit;
+
+ if (!register_kvm_events_ops())
+ goto exit;
+
+ init_kvm_event_record();
+ setup_pager();
+
+ ret = read_events();
+ if (ret)
+ goto exit;
+
+ sort_result(vcpu);
+ print_result(vcpu);
+exit:
+ return ret;
+}
+
+static const char * const record_args[] = {
+ "record",
+ "-R",
+ "-f",
+ "-m", "1024",
+ "-c", "1",
+ "-e", "kvm:kvm_entry",
+ "-e", "kvm:kvm_exit",
+ "-e", "kvm:kvm_mmio",
+ "-e", "kvm:kvm_pio",
+};
+
+#define STRDUP_FAIL_EXIT(s) \
+ ({ char *_p; \
+ _p = strdup(s); \
+ if (!_p) \
+ return -ENOMEM; \
+ _p; \
+ })
+
+static int kvm_events_record(int argc, const char **argv)
+{
+ unsigned int rec_argc, i, j;
+ const char **rec_argv;
+
+ rec_argc = ARRAY_SIZE(record_args) + argc + 2;
+ rec_argv = calloc(rec_argc + 1, sizeof(char *));
+
+ if (rec_argv == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(record_args); i++)
+ rec_argv[i] = STRDUP_FAIL_EXIT(record_args[i]);
+
+ rec_argv[i++] = STRDUP_FAIL_EXIT("-o");
+ rec_argv[i++] = STRDUP_FAIL_EXIT(file_name);
+
+ for (j = 1; j < (unsigned int)argc; j++, i++)
+ rec_argv[i] = argv[j];
+
+ return cmd_record(i, rec_argv, NULL);
+}
+
+static const char * const kvm_events_report_usage[] = {
+ "perf kvm stat report [<options>]",
+ NULL
+};
+
+static const struct option kvm_events_report_options[] = {
+ OPT_STRING(0, "event", &report_event, "report event",
+ "event for reporting: vmexit, mmio, ioport"),
+ OPT_INTEGER(0, "vcpu", &trace_vcpu,
+ "vcpu id to report"),
+ OPT_STRING('k', "key", &sort_key, "sort-key",
+ "key for sorting: sample(sort by samples number)"
+ " time (sort by avg time)"),
+ OPT_END()
+};
+
+static int kvm_events_report(int argc, const char **argv)
+{
+ symbol__init();
+
+ if (argc) {
+ argc = parse_options(argc, argv,
+ kvm_events_report_options,
+ kvm_events_report_usage, 0);
+ if (argc)
+ usage_with_options(kvm_events_report_usage,
+ kvm_events_report_options);
+ }
+
+ return kvm_events_report_vcpu(trace_vcpu);
+}
+
+static void print_kvm_stat_usage(void)
+{
+ printf("Usage: perf kvm stat <command>\n\n");
+
+ printf("# Available commands:\n");
+ printf("\trecord: record kvm events\n");
+ printf("\treport: report statistical data of kvm events\n");
+
+ printf("\nOtherwise, it is the alias of 'perf stat':\n");
+}
+
+static int kvm_cmd_stat(int argc, const char **argv)
+{
+ if (argc == 1) {
+ print_kvm_stat_usage();
+ goto perf_stat;
+ }
+
+ if (!strncmp(argv[1], "rec", 3))
+ return kvm_events_record(argc - 1, argv + 1);
+
+ if (!strncmp(argv[1], "rep", 3))
+ return kvm_events_report(argc - 1 , argv + 1);
+
+perf_stat:
+ return cmd_stat(argc, argv, NULL);
+}
+
static char name_buffer[256];
static const char * const kvm_usage[] = {
- "perf kvm [<options>] {top|record|report|diff|buildid-list}",
+ "perf kvm [<options>] {top|record|report|diff|buildid-list|stat}",
NULL
};
@@ -102,7 +930,7 @@ static int __cmd_buildid_list(int argc, const char **argv)
return cmd_buildid_list(i, rec_argv, NULL);
}
-int cmd_kvm(int argc, const char **argv, const char *prefix __used)
+int cmd_kvm(int argc, const char **argv, const char *prefix __maybe_unused)
{
perf_host = 0;
perf_guest = 1;
@@ -135,6 +963,8 @@ int cmd_kvm(int argc, const char **argv, const char *prefix __used)
return cmd_top(argc, argv, NULL);
else if (!strncmp(argv[0], "buildid-list", 12))
return __cmd_buildid_list(argc, argv);
+ else if (!strncmp(argv[0], "stat", 4))
+ return kvm_cmd_stat(argc, argv);
else
usage_with_options(kvm_usage, kvm_options);
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c
index 6313b6eb3ebb..1948eceb517a 100644
--- a/tools/perf/builtin-list.c
+++ b/tools/perf/builtin-list.c
@@ -14,20 +14,20 @@
#include "util/parse-events.h"
#include "util/cache.h"
-int cmd_list(int argc, const char **argv, const char *prefix __used)
+int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused)
{
setup_pager();
if (argc == 1)
- print_events(NULL);
+ print_events(NULL, false);
else {
int i;
for (i = 1; i < argc; ++i) {
- if (i > 1)
+ if (i > 2)
putchar('\n');
if (strncmp(argv[i], "tracepoint", 10) == 0)
- print_tracepoint_events(NULL, NULL);
+ print_tracepoint_events(NULL, NULL, false);
else if (strcmp(argv[i], "hw") == 0 ||
strcmp(argv[i], "hardware") == 0)
print_events_type(PERF_TYPE_HARDWARE);
@@ -36,13 +36,15 @@ int cmd_list(int argc, const char **argv, const char *prefix __used)
print_events_type(PERF_TYPE_SOFTWARE);
else if (strcmp(argv[i], "cache") == 0 ||
strcmp(argv[i], "hwcache") == 0)
- print_hwcache_events(NULL);
+ print_hwcache_events(NULL, false);
+ else if (strcmp(argv[i], "--raw-dump") == 0)
+ print_events(NULL, true);
else {
char *sep = strchr(argv[i], ':'), *s;
int sep_idx;
if (sep == NULL) {
- print_events(argv[i]);
+ print_events(argv[i], false);
continue;
}
sep_idx = sep - argv[i];
@@ -51,7 +53,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __used)
return -1;
s[sep_idx] = '\0';
- print_tracepoint_events(s, s + sep_idx + 1);
+ print_tracepoint_events(s, s + sep_idx + 1, false);
free(s);
}
}
diff --git a/tools/perf/builtin-lock.c b/tools/perf/builtin-lock.c
index b3c428548868..7d6e09949880 100644
--- a/tools/perf/builtin-lock.c
+++ b/tools/perf/builtin-lock.c
@@ -1,6 +1,8 @@
#include "builtin.h"
#include "perf.h"
+#include "util/evlist.h"
+#include "util/evsel.h"
#include "util/util.h"
#include "util/cache.h"
#include "util/symbol.h"
@@ -40,7 +42,7 @@ struct lock_stat {
struct rb_node rb; /* used for sorting */
/*
- * FIXME: raw_field_value() returns unsigned long long,
+ * FIXME: perf_evsel__intval() returns u64,
* so address of lockdep_map should be dealed as 64bit.
* Is there more better solution?
*/
@@ -160,8 +162,10 @@ static struct thread_stat *thread_stat_findnew_after_first(u32 tid)
return st;
st = zalloc(sizeof(struct thread_stat));
- if (!st)
- die("memory allocation failed\n");
+ if (!st) {
+ pr_err("memory allocation failed\n");
+ return NULL;
+ }
st->tid = tid;
INIT_LIST_HEAD(&st->seq_list);
@@ -180,8 +184,10 @@ static struct thread_stat *thread_stat_findnew_first(u32 tid)
struct thread_stat *st;
st = zalloc(sizeof(struct thread_stat));
- if (!st)
- die("memory allocation failed\n");
+ if (!st) {
+ pr_err("memory allocation failed\n");
+ return NULL;
+ }
st->tid = tid;
INIT_LIST_HEAD(&st->seq_list);
@@ -247,18 +253,20 @@ struct lock_key keys[] = {
{ NULL, NULL }
};
-static void select_key(void)
+static int select_key(void)
{
int i;
for (i = 0; keys[i].name; i++) {
if (!strcmp(keys[i].name, sort_key)) {
compare = keys[i].key;
- return;
+ return 0;
}
}
- die("Unknown compare key:%s\n", sort_key);
+ pr_err("Unknown compare key: %s\n", sort_key);
+
+ return -1;
}
static void insert_to_result(struct lock_stat *st,
@@ -323,61 +331,24 @@ static struct lock_stat *lock_stat_findnew(void *addr, const char *name)
return new;
alloc_failed:
- die("memory allocation failed\n");
+ pr_err("memory allocation failed\n");
+ return NULL;
}
static const char *input_name;
-struct raw_event_sample {
- u32 size;
- char data[0];
-};
-
-struct trace_acquire_event {
- void *addr;
- const char *name;
- int flag;
-};
-
-struct trace_acquired_event {
- void *addr;
- const char *name;
-};
+struct trace_lock_handler {
+ int (*acquire_event)(struct perf_evsel *evsel,
+ struct perf_sample *sample);
-struct trace_contended_event {
- void *addr;
- const char *name;
-};
+ int (*acquired_event)(struct perf_evsel *evsel,
+ struct perf_sample *sample);
-struct trace_release_event {
- void *addr;
- const char *name;
-};
+ int (*contended_event)(struct perf_evsel *evsel,
+ struct perf_sample *sample);
-struct trace_lock_handler {
- void (*acquire_event)(struct trace_acquire_event *,
- struct event_format *,
- int cpu,
- u64 timestamp,
- struct thread *thread);
-
- void (*acquired_event)(struct trace_acquired_event *,
- struct event_format *,
- int cpu,
- u64 timestamp,
- struct thread *thread);
-
- void (*contended_event)(struct trace_contended_event *,
- struct event_format *,
- int cpu,
- u64 timestamp,
- struct thread *thread);
-
- void (*release_event)(struct trace_release_event *,
- struct event_format *,
- int cpu,
- u64 timestamp,
- struct thread *thread);
+ int (*release_event)(struct perf_evsel *evsel,
+ struct perf_sample *sample);
};
static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr)
@@ -390,8 +361,10 @@ static struct lock_seq_stat *get_seq(struct thread_stat *ts, void *addr)
}
seq = zalloc(sizeof(struct lock_seq_stat));
- if (!seq)
- die("Not enough memory\n");
+ if (!seq) {
+ pr_err("memory allocation failed\n");
+ return NULL;
+ }
seq->state = SEQ_STATE_UNINITIALIZED;
seq->addr = addr;
@@ -414,33 +387,42 @@ enum acquire_flags {
READ_LOCK = 2,
};
-static void
-report_lock_acquire_event(struct trace_acquire_event *acquire_event,
- struct event_format *__event __used,
- int cpu __used,
- u64 timestamp __used,
- struct thread *thread __used)
+static int report_lock_acquire_event(struct perf_evsel *evsel,
+ struct perf_sample *sample)
{
+ void *addr;
struct lock_stat *ls;
struct thread_stat *ts;
struct lock_seq_stat *seq;
+ const char *name = perf_evsel__strval(evsel, sample, "name");
+ u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
+ int flag = perf_evsel__intval(evsel, sample, "flag");
+
+ memcpy(&addr, &tmp, sizeof(void *));
- ls = lock_stat_findnew(acquire_event->addr, acquire_event->name);
+ ls = lock_stat_findnew(addr, name);
+ if (!ls)
+ return -1;
if (ls->discard)
- return;
+ return 0;
- ts = thread_stat_findnew(thread->pid);
- seq = get_seq(ts, acquire_event->addr);
+ ts = thread_stat_findnew(sample->tid);
+ if (!ts)
+ return -1;
+
+ seq = get_seq(ts, addr);
+ if (!seq)
+ return -1;
switch (seq->state) {
case SEQ_STATE_UNINITIALIZED:
case SEQ_STATE_RELEASED:
- if (!acquire_event->flag) {
+ if (!flag) {
seq->state = SEQ_STATE_ACQUIRING;
} else {
- if (acquire_event->flag & TRY_LOCK)
+ if (flag & TRY_LOCK)
ls->nr_trylock++;
- if (acquire_event->flag & READ_LOCK)
+ if (flag & READ_LOCK)
ls->nr_readlock++;
seq->state = SEQ_STATE_READ_ACQUIRED;
seq->read_count = 1;
@@ -448,7 +430,7 @@ report_lock_acquire_event(struct trace_acquire_event *acquire_event,
}
break;
case SEQ_STATE_READ_ACQUIRED:
- if (acquire_event->flag & READ_LOCK) {
+ if (flag & READ_LOCK) {
seq->read_count++;
ls->nr_acquired++;
goto end;
@@ -473,38 +455,46 @@ broken:
}
ls->nr_acquire++;
- seq->prev_event_time = timestamp;
+ seq->prev_event_time = sample->time;
end:
- return;
+ return 0;
}
-static void
-report_lock_acquired_event(struct trace_acquired_event *acquired_event,
- struct event_format *__event __used,
- int cpu __used,
- u64 timestamp __used,
- struct thread *thread __used)
+static int report_lock_acquired_event(struct perf_evsel *evsel,
+ struct perf_sample *sample)
{
+ void *addr;
struct lock_stat *ls;
struct thread_stat *ts;
struct lock_seq_stat *seq;
u64 contended_term;
+ const char *name = perf_evsel__strval(evsel, sample, "name");
+ u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
+
+ memcpy(&addr, &tmp, sizeof(void *));
- ls = lock_stat_findnew(acquired_event->addr, acquired_event->name);
+ ls = lock_stat_findnew(addr, name);
+ if (!ls)
+ return -1;
if (ls->discard)
- return;
+ return 0;
+
+ ts = thread_stat_findnew(sample->tid);
+ if (!ts)
+ return -1;
- ts = thread_stat_findnew(thread->pid);
- seq = get_seq(ts, acquired_event->addr);
+ seq = get_seq(ts, addr);
+ if (!seq)
+ return -1;
switch (seq->state) {
case SEQ_STATE_UNINITIALIZED:
/* orphan event, do nothing */
- return;
+ return 0;
case SEQ_STATE_ACQUIRING:
break;
case SEQ_STATE_CONTENDED:
- contended_term = timestamp - seq->prev_event_time;
+ contended_term = sample->time - seq->prev_event_time;
ls->wait_time_total += contended_term;
if (contended_term < ls->wait_time_min)
ls->wait_time_min = contended_term;
@@ -529,33 +519,41 @@ report_lock_acquired_event(struct trace_acquired_event *acquired_event,
seq->state = SEQ_STATE_ACQUIRED;
ls->nr_acquired++;
- seq->prev_event_time = timestamp;
+ seq->prev_event_time = sample->time;
end:
- return;
+ return 0;
}
-static void
-report_lock_contended_event(struct trace_contended_event *contended_event,
- struct event_format *__event __used,
- int cpu __used,
- u64 timestamp __used,
- struct thread *thread __used)
+static int report_lock_contended_event(struct perf_evsel *evsel,
+ struct perf_sample *sample)
{
+ void *addr;
struct lock_stat *ls;
struct thread_stat *ts;
struct lock_seq_stat *seq;
+ const char *name = perf_evsel__strval(evsel, sample, "name");
+ u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
- ls = lock_stat_findnew(contended_event->addr, contended_event->name);
+ memcpy(&addr, &tmp, sizeof(void *));
+
+ ls = lock_stat_findnew(addr, name);
+ if (!ls)
+ return -1;
if (ls->discard)
- return;
+ return 0;
+
+ ts = thread_stat_findnew(sample->tid);
+ if (!ts)
+ return -1;
- ts = thread_stat_findnew(thread->pid);
- seq = get_seq(ts, contended_event->addr);
+ seq = get_seq(ts, addr);
+ if (!seq)
+ return -1;
switch (seq->state) {
case SEQ_STATE_UNINITIALIZED:
/* orphan event, do nothing */
- return;
+ return 0;
case SEQ_STATE_ACQUIRING:
break;
case SEQ_STATE_RELEASED:
@@ -576,28 +574,36 @@ report_lock_contended_event(struct trace_contended_event *contended_event,
seq->state = SEQ_STATE_CONTENDED;
ls->nr_contended++;
- seq->prev_event_time = timestamp;
+ seq->prev_event_time = sample->time;
end:
- return;
+ return 0;
}
-static void
-report_lock_release_event(struct trace_release_event *release_event,
- struct event_format *__event __used,
- int cpu __used,
- u64 timestamp __used,
- struct thread *thread __used)
+static int report_lock_release_event(struct perf_evsel *evsel,
+ struct perf_sample *sample)
{
+ void *addr;
struct lock_stat *ls;
struct thread_stat *ts;
struct lock_seq_stat *seq;
+ const char *name = perf_evsel__strval(evsel, sample, "name");
+ u64 tmp = perf_evsel__intval(evsel, sample, "lockdep_addr");
+
+ memcpy(&addr, &tmp, sizeof(void *));
- ls = lock_stat_findnew(release_event->addr, release_event->name);
+ ls = lock_stat_findnew(addr, name);
+ if (!ls)
+ return -1;
if (ls->discard)
- return;
+ return 0;
+
+ ts = thread_stat_findnew(sample->tid);
+ if (!ts)
+ return -1;
- ts = thread_stat_findnew(thread->pid);
- seq = get_seq(ts, release_event->addr);
+ seq = get_seq(ts, addr);
+ if (!seq)
+ return -1;
switch (seq->state) {
case SEQ_STATE_UNINITIALIZED:
@@ -631,7 +637,7 @@ free_seq:
list_del(&seq->list);
free(seq);
end:
- return;
+ return 0;
}
/* lock oriented handlers */
@@ -645,96 +651,36 @@ static struct trace_lock_handler report_lock_ops = {
static struct trace_lock_handler *trace_handler;
-static void
-process_lock_acquire_event(void *data,
- struct event_format *event __used,
- int cpu __used,
- u64 timestamp __used,
- struct thread *thread __used)
-{
- struct trace_acquire_event acquire_event;
- u64 tmp; /* this is required for casting... */
-
- tmp = raw_field_value(event, "lockdep_addr", data);
- memcpy(&acquire_event.addr, &tmp, sizeof(void *));
- acquire_event.name = (char *)raw_field_ptr(event, "name", data);
- acquire_event.flag = (int)raw_field_value(event, "flag", data);
-
- if (trace_handler->acquire_event)
- trace_handler->acquire_event(&acquire_event, event, cpu, timestamp, thread);
-}
-
-static void
-process_lock_acquired_event(void *data,
- struct event_format *event __used,
- int cpu __used,
- u64 timestamp __used,
- struct thread *thread __used)
+static int perf_evsel__process_lock_acquire(struct perf_evsel *evsel,
+ struct perf_sample *sample)
{
- struct trace_acquired_event acquired_event;
- u64 tmp; /* this is required for casting... */
-
- tmp = raw_field_value(event, "lockdep_addr", data);
- memcpy(&acquired_event.addr, &tmp, sizeof(void *));
- acquired_event.name = (char *)raw_field_ptr(event, "name", data);
-
if (trace_handler->acquire_event)
- trace_handler->acquired_event(&acquired_event, event, cpu, timestamp, thread);
+ return trace_handler->acquire_event(evsel, sample);
+ return 0;
}
-static void
-process_lock_contended_event(void *data,
- struct event_format *event __used,
- int cpu __used,
- u64 timestamp __used,
- struct thread *thread __used)
+static int perf_evsel__process_lock_acquired(struct perf_evsel *evsel,
+ struct perf_sample *sample)
{
- struct trace_contended_event contended_event;
- u64 tmp; /* this is required for casting... */
-
- tmp = raw_field_value(event, "lockdep_addr", data);
- memcpy(&contended_event.addr, &tmp, sizeof(void *));
- contended_event.name = (char *)raw_field_ptr(event, "name", data);
-
- if (trace_handler->acquire_event)
- trace_handler->contended_event(&contended_event, event, cpu, timestamp, thread);
+ if (trace_handler->acquired_event)
+ return trace_handler->acquired_event(evsel, sample);
+ return 0;
}
-static void
-process_lock_release_event(void *data,
- struct event_format *event __used,
- int cpu __used,
- u64 timestamp __used,
- struct thread *thread __used)
+static int perf_evsel__process_lock_contended(struct perf_evsel *evsel,
+ struct perf_sample *sample)
{
- struct trace_release_event release_event;
- u64 tmp; /* this is required for casting... */
-
- tmp = raw_field_value(event, "lockdep_addr", data);
- memcpy(&release_event.addr, &tmp, sizeof(void *));
- release_event.name = (char *)raw_field_ptr(event, "name", data);
-
- if (trace_handler->acquire_event)
- trace_handler->release_event(&release_event, event, cpu, timestamp, thread);
+ if (trace_handler->contended_event)
+ return trace_handler->contended_event(evsel, sample);
+ return 0;
}
-static void
-process_raw_event(void *data, int cpu, u64 timestamp, struct thread *thread)
+static int perf_evsel__process_lock_release(struct perf_evsel *evsel,
+ struct perf_sample *sample)
{
- struct event_format *event;
- int type;
-
- type = trace_parse_common_type(session->pevent, data);
- event = pevent_find_event(session->pevent, type);
-
- if (!strcmp(event->name, "lock_acquire"))
- process_lock_acquire_event(data, event, cpu, timestamp, thread);
- if (!strcmp(event->name, "lock_acquired"))
- process_lock_acquired_event(data, event, cpu, timestamp, thread);
- if (!strcmp(event->name, "lock_contended"))
- process_lock_contended_event(data, event, cpu, timestamp, thread);
- if (!strcmp(event->name, "lock_release"))
- process_lock_release_event(data, event, cpu, timestamp, thread);
+ if (trace_handler->release_event)
+ return trace_handler->release_event(evsel, sample);
+ return 0;
}
static void print_bad_events(int bad, int total)
@@ -836,20 +782,29 @@ static void dump_map(void)
}
}
-static void dump_info(void)
+static int dump_info(void)
{
+ int rc = 0;
+
if (info_threads)
dump_threads();
else if (info_map)
dump_map();
- else
- die("Unknown type of information\n");
+ else {
+ rc = -1;
+ pr_err("Unknown type of information\n");
+ }
+
+ return rc;
}
-static int process_sample_event(struct perf_tool *tool __used,
+typedef int (*tracepoint_handler)(struct perf_evsel *evsel,
+ struct perf_sample *sample);
+
+static int process_sample_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel __used,
+ struct perf_evsel *evsel,
struct machine *machine)
{
struct thread *thread = machine__findnew_thread(machine, sample->tid);
@@ -860,7 +815,10 @@ static int process_sample_event(struct perf_tool *tool __used,
return -1;
}
- process_raw_event(sample->raw_data, sample->cpu, sample->time, thread);
+ if (evsel->handler.func != NULL) {
+ tracepoint_handler f = evsel->handler.func;
+ return f(evsel, sample);
+ }
return 0;
}
@@ -871,11 +829,25 @@ static struct perf_tool eops = {
.ordered_samples = true,
};
+static const struct perf_evsel_str_handler lock_tracepoints[] = {
+ { "lock:lock_acquire", perf_evsel__process_lock_acquire, }, /* CONFIG_LOCKDEP */
+ { "lock:lock_acquired", perf_evsel__process_lock_acquired, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
+ { "lock:lock_contended", perf_evsel__process_lock_contended, }, /* CONFIG_LOCKDEP, CONFIG_LOCK_STAT */
+ { "lock:lock_release", perf_evsel__process_lock_release, }, /* CONFIG_LOCKDEP */
+};
+
static int read_events(void)
{
session = perf_session__new(input_name, O_RDONLY, 0, false, &eops);
- if (!session)
- die("Initializing perf session failed\n");
+ if (!session) {
+ pr_err("Initializing perf session failed\n");
+ return -1;
+ }
+
+ if (perf_session__set_tracepoints_handlers(session, lock_tracepoints)) {
+ pr_err("Initializing perf session tracepoint handlers failed\n");
+ return -1;
+ }
return perf_session__process_events(session, &eops);
}
@@ -892,13 +864,18 @@ static void sort_result(void)
}
}
-static void __cmd_report(void)
+static int __cmd_report(void)
{
setup_pager();
- select_key();
- read_events();
+
+ if ((select_key() != 0) ||
+ (read_events() != 0))
+ return -1;
+
sort_result();
print_result();
+
+ return 0;
}
static const char * const report_usage[] = {
@@ -944,10 +921,6 @@ static const char *record_args[] = {
"-f",
"-m", "1024",
"-c", "1",
- "-e", "lock:lock_acquire",
- "-e", "lock:lock_acquired",
- "-e", "lock:lock_contended",
- "-e", "lock:lock_release",
};
static int __cmd_record(int argc, const char **argv)
@@ -955,15 +928,31 @@ static int __cmd_record(int argc, const char **argv)
unsigned int rec_argc, i, j;
const char **rec_argv;
+ for (i = 0; i < ARRAY_SIZE(lock_tracepoints); i++) {
+ if (!is_valid_tracepoint(lock_tracepoints[i].name)) {
+ pr_err("tracepoint %s is not enabled. "
+ "Are CONFIG_LOCKDEP and CONFIG_LOCK_STAT enabled?\n",
+ lock_tracepoints[i].name);
+ return 1;
+ }
+ }
+
rec_argc = ARRAY_SIZE(record_args) + argc - 1;
- rec_argv = calloc(rec_argc + 1, sizeof(char *));
+ /* factor of 2 is for -e in front of each tracepoint */
+ rec_argc += 2 * ARRAY_SIZE(lock_tracepoints);
+ rec_argv = calloc(rec_argc + 1, sizeof(char *));
if (rec_argv == NULL)
return -ENOMEM;
for (i = 0; i < ARRAY_SIZE(record_args); i++)
rec_argv[i] = strdup(record_args[i]);
+ for (j = 0; j < ARRAY_SIZE(lock_tracepoints); j++) {
+ rec_argv[i++] = "-e";
+ rec_argv[i++] = strdup(lock_tracepoints[j].name);
+ }
+
for (j = 1; j < (unsigned int)argc; j++, i++)
rec_argv[i] = argv[j];
@@ -972,9 +961,10 @@ static int __cmd_record(int argc, const char **argv)
return cmd_record(i, rec_argv, NULL);
}
-int cmd_lock(int argc, const char **argv, const char *prefix __used)
+int cmd_lock(int argc, const char **argv, const char *prefix __maybe_unused)
{
unsigned int i;
+ int rc = 0;
symbol__init();
for (i = 0; i < LOCKHASH_SIZE; i++)
@@ -1009,11 +999,13 @@ int cmd_lock(int argc, const char **argv, const char *prefix __used)
/* recycling report_lock_ops */
trace_handler = &report_lock_ops;
setup_pager();
- read_events();
- dump_info();
+ if (read_events() != 0)
+ rc = -1;
+ else
+ rc = dump_info();
} else {
usage_with_options(lock_usage, lock_options);
}
- return 0;
+ return rc;
}
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index e215ae61b2ae..118aa8946573 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -143,8 +143,8 @@ static int parse_probe_event_argv(int argc, const char **argv)
return ret;
}
-static int opt_add_probe_event(const struct option *opt __used,
- const char *str, int unset __used)
+static int opt_add_probe_event(const struct option *opt __maybe_unused,
+ const char *str, int unset __maybe_unused)
{
if (str) {
params.mod_events = true;
@@ -153,8 +153,8 @@ static int opt_add_probe_event(const struct option *opt __used,
return 0;
}
-static int opt_del_probe_event(const struct option *opt __used,
- const char *str, int unset __used)
+static int opt_del_probe_event(const struct option *opt __maybe_unused,
+ const char *str, int unset __maybe_unused)
{
if (str) {
params.mod_events = true;
@@ -166,7 +166,7 @@ static int opt_del_probe_event(const struct option *opt __used,
}
static int opt_set_target(const struct option *opt, const char *str,
- int unset __used)
+ int unset __maybe_unused)
{
int ret = -ENOENT;
@@ -188,8 +188,8 @@ static int opt_set_target(const struct option *opt, const char *str,
}
#ifdef DWARF_SUPPORT
-static int opt_show_lines(const struct option *opt __used,
- const char *str, int unset __used)
+static int opt_show_lines(const struct option *opt __maybe_unused,
+ const char *str, int unset __maybe_unused)
{
int ret = 0;
@@ -209,8 +209,8 @@ static int opt_show_lines(const struct option *opt __used,
return ret;
}
-static int opt_show_vars(const struct option *opt __used,
- const char *str, int unset __used)
+static int opt_show_vars(const struct option *opt __maybe_unused,
+ const char *str, int unset __maybe_unused)
{
struct perf_probe_event *pev = &params.events[params.nevents];
int ret;
@@ -229,8 +229,8 @@ static int opt_show_vars(const struct option *opt __used,
}
#endif
-static int opt_set_filter(const struct option *opt __used,
- const char *str, int unset __used)
+static int opt_set_filter(const struct option *opt __maybe_unused,
+ const char *str, int unset __maybe_unused)
{
const char *err;
@@ -327,7 +327,7 @@ static const struct option options[] = {
OPT_END()
};
-int cmd_probe(int argc, const char **argv, const char *prefix __used)
+int cmd_probe(int argc, const char **argv, const char *prefix __maybe_unused)
{
int ret;
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 4db6e1ba54e3..f14cb5fdb91f 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -31,6 +31,15 @@
#include <sched.h>
#include <sys/mman.h>
+#define CALLCHAIN_HELP "do call-graph (stack chain/backtrace) recording: "
+
+#ifdef NO_LIBUNWIND_SUPPORT
+static char callchain_help[] = CALLCHAIN_HELP "[fp]";
+#else
+static unsigned long default_stack_dump_size = 8192;
+static char callchain_help[] = CALLCHAIN_HELP "[fp] dwarf";
+#endif
+
enum write_mode_t {
WRITE_FORCE,
WRITE_APPEND
@@ -62,32 +71,38 @@ static void advance_output(struct perf_record *rec, size_t size)
rec->bytes_written += size;
}
-static void write_output(struct perf_record *rec, void *buf, size_t size)
+static int write_output(struct perf_record *rec, void *buf, size_t size)
{
while (size) {
int ret = write(rec->output, buf, size);
- if (ret < 0)
- die("failed to write");
+ if (ret < 0) {
+ pr_err("failed to write\n");
+ return -1;
+ }
size -= ret;
buf += ret;
rec->bytes_written += ret;
}
+
+ return 0;
}
static int process_synthesized_event(struct perf_tool *tool,
union perf_event *event,
- struct perf_sample *sample __used,
- struct machine *machine __used)
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
{
struct perf_record *rec = container_of(tool, struct perf_record, tool);
- write_output(rec, event, event->header.size);
+ if (write_output(rec, event, event->header.size) < 0)
+ return -1;
+
return 0;
}
-static void perf_record__mmap_read(struct perf_record *rec,
+static int perf_record__mmap_read(struct perf_record *rec,
struct perf_mmap *md)
{
unsigned int head = perf_mmap__read_head(md);
@@ -95,9 +110,10 @@ static void perf_record__mmap_read(struct perf_record *rec,
unsigned char *data = md->base + rec->page_size;
unsigned long size;
void *buf;
+ int rc = 0;
if (old == head)
- return;
+ return 0;
rec->samples++;
@@ -108,17 +124,26 @@ static void perf_record__mmap_read(struct perf_record *rec,
size = md->mask + 1 - (old & md->mask);
old += size;
- write_output(rec, buf, size);
+ if (write_output(rec, buf, size) < 0) {
+ rc = -1;
+ goto out;
+ }
}
buf = &data[old & md->mask];
size = head - old;
old += size;
- write_output(rec, buf, size);
+ if (write_output(rec, buf, size) < 0) {
+ rc = -1;
+ goto out;
+ }
md->prev = old;
perf_mmap__write_tail(md, old);
+
+out:
+ return rc;
}
static volatile int done = 0;
@@ -134,7 +159,7 @@ static void sig_handler(int sig)
signr = sig;
}
-static void perf_record__sig_exit(int exit_status __used, void *arg)
+static void perf_record__sig_exit(int exit_status __maybe_unused, void *arg)
{
struct perf_record *rec = arg;
int status;
@@ -163,31 +188,32 @@ static bool perf_evlist__equal(struct perf_evlist *evlist,
if (evlist->nr_entries != other->nr_entries)
return false;
- pair = list_entry(other->entries.next, struct perf_evsel, node);
+ pair = perf_evlist__first(other);
list_for_each_entry(pos, &evlist->entries, node) {
if (memcmp(&pos->attr, &pair->attr, sizeof(pos->attr) != 0))
return false;
- pair = list_entry(pair->node.next, struct perf_evsel, node);
+ pair = perf_evsel__next(pair);
}
return true;
}
-static void perf_record__open(struct perf_record *rec)
+static int perf_record__open(struct perf_record *rec)
{
- struct perf_evsel *pos, *first;
+ struct perf_evsel *pos;
struct perf_evlist *evlist = rec->evlist;
struct perf_session *session = rec->session;
struct perf_record_opts *opts = &rec->opts;
-
- first = list_entry(evlist->entries.next, struct perf_evsel, node);
+ int rc = 0;
perf_evlist__config_attrs(evlist, opts);
+ if (opts->group)
+ perf_evlist__set_leader(evlist);
+
list_for_each_entry(pos, &evlist->entries, node) {
struct perf_event_attr *attr = &pos->attr;
- struct xyarray *group_fd = NULL;
/*
* Check if parse_single_tracepoint_event has already asked for
* PERF_SAMPLE_TIME.
@@ -202,24 +228,24 @@ static void perf_record__open(struct perf_record *rec)
*/
bool time_needed = attr->sample_type & PERF_SAMPLE_TIME;
- if (opts->group && pos != first)
- group_fd = first->fd;
fallback_missing_features:
if (opts->exclude_guest_missing)
attr->exclude_guest = attr->exclude_host = 0;
retry_sample_id:
attr->sample_id_all = opts->sample_id_all_missing ? 0 : 1;
try_again:
- if (perf_evsel__open(pos, evlist->cpus, evlist->threads,
- opts->group, group_fd) < 0) {
+ if (perf_evsel__open(pos, evlist->cpus, evlist->threads) < 0) {
int err = errno;
if (err == EPERM || err == EACCES) {
ui__error_paranoid();
- exit(EXIT_FAILURE);
+ rc = -err;
+ goto out;
} else if (err == ENODEV && opts->target.cpu_list) {
- die("No such device - did you specify"
- " an out-of-range profile CPU?\n");
+ pr_err("No such device - did you specify"
+ " an out-of-range profile CPU?\n");
+ rc = -err;
+ goto out;
} else if (err == EINVAL) {
if (!opts->exclude_guest_missing &&
(attr->exclude_guest || attr->exclude_host)) {
@@ -266,42 +292,57 @@ try_again:
if (err == ENOENT) {
ui__error("The %s event is not supported.\n",
perf_evsel__name(pos));
- exit(EXIT_FAILURE);
+ rc = -err;
+ goto out;
}
printf("\n");
- error("sys_perf_event_open() syscall returned with %d (%s). /bin/dmesg may provide additional information.\n",
- err, strerror(err));
+ error("sys_perf_event_open() syscall returned with %d "
+ "(%s) for event %s. /bin/dmesg may provide "
+ "additional information.\n",
+ err, strerror(err), perf_evsel__name(pos));
#if defined(__i386__) || defined(__x86_64__)
- if (attr->type == PERF_TYPE_HARDWARE && err == EOPNOTSUPP)
- die("No hardware sampling interrupt available."
- " No APIC? If so then you can boot the kernel"
- " with the \"lapic\" boot parameter to"
- " force-enable it.\n");
+ if (attr->type == PERF_TYPE_HARDWARE &&
+ err == EOPNOTSUPP) {
+ pr_err("No hardware sampling interrupt available."
+ " No APIC? If so then you can boot the kernel"
+ " with the \"lapic\" boot parameter to"
+ " force-enable it.\n");
+ rc = -err;
+ goto out;
+ }
#endif
- die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
+ pr_err("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
+ rc = -err;
+ goto out;
}
}
- if (perf_evlist__set_filters(evlist)) {
+ if (perf_evlist__apply_filters(evlist)) {
error("failed to set filter with %d (%s)\n", errno,
strerror(errno));
- exit(-1);
+ rc = -1;
+ goto out;
}
if (perf_evlist__mmap(evlist, opts->mmap_pages, false) < 0) {
- if (errno == EPERM)
- die("Permission error mapping pages.\n"
- "Consider increasing "
- "/proc/sys/kernel/perf_event_mlock_kb,\n"
- "or try again with a smaller value of -m/--mmap_pages.\n"
- "(current value: %d)\n", opts->mmap_pages);
- else if (!is_power_of_2(opts->mmap_pages))
- die("--mmap_pages/-m value must be a power of two.");
-
- die("failed to mmap with %d (%s)\n", errno, strerror(errno));
+ if (errno == EPERM) {
+ pr_err("Permission error mapping pages.\n"
+ "Consider increasing "
+ "/proc/sys/kernel/perf_event_mlock_kb,\n"
+ "or try again with a smaller value of -m/--mmap_pages.\n"
+ "(current value: %d)\n", opts->mmap_pages);
+ rc = -errno;
+ } else if (!is_power_of_2(opts->mmap_pages)) {
+ pr_err("--mmap_pages/-m value must be a power of two.");
+ rc = -EINVAL;
+ } else {
+ pr_err("failed to mmap with %d (%s)\n", errno, strerror(errno));
+ rc = -errno;
+ }
+ goto out;
}
if (rec->file_new)
@@ -309,11 +350,14 @@ try_again:
else {
if (!perf_evlist__equal(session->evlist, evlist)) {
fprintf(stderr, "incompatible append\n");
- exit(-1);
+ rc = -1;
+ goto out;
}
}
perf_session__set_id_hdr_size(session);
+out:
+ return rc;
}
static int process_buildids(struct perf_record *rec)
@@ -329,10 +373,13 @@ static int process_buildids(struct perf_record *rec)
size, &build_id__mark_dso_hit_ops);
}
-static void perf_record__exit(int status __used, void *arg)
+static void perf_record__exit(int status, void *arg)
{
struct perf_record *rec = arg;
+ if (status != 0)
+ return;
+
if (!rec->opts.pipe_output) {
rec->session->header.data_size += rec->bytes_written;
@@ -387,17 +434,26 @@ static struct perf_event_header finished_round_event = {
.type = PERF_RECORD_FINISHED_ROUND,
};
-static void perf_record__mmap_read_all(struct perf_record *rec)
+static int perf_record__mmap_read_all(struct perf_record *rec)
{
int i;
+ int rc = 0;
for (i = 0; i < rec->evlist->nr_mmaps; i++) {
- if (rec->evlist->mmap[i].base)
- perf_record__mmap_read(rec, &rec->evlist->mmap[i]);
+ if (rec->evlist->mmap[i].base) {
+ if (perf_record__mmap_read(rec, &rec->evlist->mmap[i]) != 0) {
+ rc = -1;
+ goto out;
+ }
+ }
}
if (perf_header__has_feat(&rec->session->header, HEADER_TRACING_DATA))
- write_output(rec, &finished_round_event, sizeof(finished_round_event));
+ rc = write_output(rec, &finished_round_event,
+ sizeof(finished_round_event));
+
+out:
+ return rc;
}
static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
@@ -457,7 +513,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
output = open(output_name, flags, S_IRUSR | S_IWUSR);
if (output < 0) {
perror("failed to create output file");
- exit(-1);
+ return -1;
}
rec->output = output;
@@ -497,7 +553,10 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
}
}
- perf_record__open(rec);
+ if (perf_record__open(rec) != 0) {
+ err = -1;
+ goto out_delete_session;
+ }
/*
* perf_session__delete(session) will be called at perf_record__exit()
@@ -507,19 +566,20 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
if (opts->pipe_output) {
err = perf_header__write_pipe(output);
if (err < 0)
- return err;
+ goto out_delete_session;
} else if (rec->file_new) {
err = perf_session__write_header(session, evsel_list,
output, false);
if (err < 0)
- return err;
+ goto out_delete_session;
}
if (!rec->no_buildid
&& !perf_header__has_feat(&session->header, HEADER_BUILD_ID)) {
pr_err("Couldn't generate buildids. "
"Use --no-buildid to profile anyway.\n");
- return -1;
+ err = -1;
+ goto out_delete_session;
}
rec->post_processing_offset = lseek(output, 0, SEEK_CUR);
@@ -527,7 +587,8 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
machine = perf_session__find_host_machine(session);
if (!machine) {
pr_err("Couldn't find native kernel information.\n");
- return -1;
+ err = -1;
+ goto out_delete_session;
}
if (opts->pipe_output) {
@@ -535,14 +596,14 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
process_synthesized_event);
if (err < 0) {
pr_err("Couldn't synthesize attrs.\n");
- return err;
+ goto out_delete_session;
}
err = perf_event__synthesize_event_types(tool, process_synthesized_event,
machine);
if (err < 0) {
pr_err("Couldn't synthesize event_types.\n");
- return err;
+ goto out_delete_session;
}
if (have_tracepoints(&evsel_list->entries)) {
@@ -558,7 +619,7 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
process_synthesized_event);
if (err <= 0) {
pr_err("Couldn't record tracing data.\n");
- return err;
+ goto out_delete_session;
}
advance_output(rec, err);
}
@@ -586,20 +647,24 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
perf_event__synthesize_guest_os);
if (!opts->target.system_wide)
- perf_event__synthesize_thread_map(tool, evsel_list->threads,
+ err = perf_event__synthesize_thread_map(tool, evsel_list->threads,
process_synthesized_event,
machine);
else
- perf_event__synthesize_threads(tool, process_synthesized_event,
+ err = perf_event__synthesize_threads(tool, process_synthesized_event,
machine);
+ if (err != 0)
+ goto out_delete_session;
+
if (rec->realtime_prio) {
struct sched_param param;
param.sched_priority = rec->realtime_prio;
if (sched_setscheduler(0, SCHED_FIFO, &param)) {
pr_err("Could not set realtime priority.\n");
- exit(-1);
+ err = -1;
+ goto out_delete_session;
}
}
@@ -614,7 +679,10 @@ static int __cmd_record(struct perf_record *rec, int argc, const char **argv)
for (;;) {
int hits = rec->samples;
- perf_record__mmap_read_all(rec);
+ if (perf_record__mmap_read_all(rec) < 0) {
+ err = -1;
+ goto out_delete_session;
+ }
if (hits == rec->samples) {
if (done)
@@ -732,6 +800,106 @@ error:
return ret;
}
+#ifndef NO_LIBUNWIND_SUPPORT
+static int get_stack_size(char *str, unsigned long *_size)
+{
+ char *endptr;
+ unsigned long size;
+ unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
+
+ size = strtoul(str, &endptr, 0);
+
+ do {
+ if (*endptr)
+ break;
+
+ size = round_up(size, sizeof(u64));
+ if (!size || size > max_size)
+ break;
+
+ *_size = size;
+ return 0;
+
+ } while (0);
+
+ pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
+ max_size, str);
+ return -1;
+}
+#endif /* !NO_LIBUNWIND_SUPPORT */
+
+static int
+parse_callchain_opt(const struct option *opt __maybe_unused, const char *arg,
+ int unset)
+{
+ struct perf_record *rec = (struct perf_record *)opt->value;
+ char *tok, *name, *saveptr = NULL;
+ char *buf;
+ int ret = -1;
+
+ /* --no-call-graph */
+ if (unset)
+ return 0;
+
+ /* We specified default option if none is provided. */
+ BUG_ON(!arg);
+
+ /* We need buffer that we know we can write to. */
+ buf = malloc(strlen(arg) + 1);
+ if (!buf)
+ return -ENOMEM;
+
+ strcpy(buf, arg);
+
+ tok = strtok_r((char *)buf, ",", &saveptr);
+ name = tok ? : (char *)buf;
+
+ do {
+ /* Framepointer style */
+ if (!strncmp(name, "fp", sizeof("fp"))) {
+ if (!strtok_r(NULL, ",", &saveptr)) {
+ rec->opts.call_graph = CALLCHAIN_FP;
+ ret = 0;
+ } else
+ pr_err("callchain: No more arguments "
+ "needed for -g fp\n");
+ break;
+
+#ifndef NO_LIBUNWIND_SUPPORT
+ /* Dwarf style */
+ } else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
+ ret = 0;
+ rec->opts.call_graph = CALLCHAIN_DWARF;
+ rec->opts.stack_dump_size = default_stack_dump_size;
+
+ tok = strtok_r(NULL, ",", &saveptr);
+ if (tok) {
+ unsigned long size = 0;
+
+ ret = get_stack_size(tok, &size);
+ rec->opts.stack_dump_size = size;
+ }
+
+ if (!ret)
+ pr_debug("callchain: stack dump size %d\n",
+ rec->opts.stack_dump_size);
+#endif /* !NO_LIBUNWIND_SUPPORT */
+ } else {
+ pr_err("callchain: Unknown -g option "
+ "value: %s\n", arg);
+ break;
+ }
+
+ } while (0);
+
+ free(buf);
+
+ if (!ret)
+ pr_debug("callchain: type %d\n", rec->opts.call_graph);
+
+ return ret;
+}
+
static const char * const record_usage[] = {
"perf record [<options>] [<command>]",
"perf record [<options>] -- <command> [<options>]",
@@ -803,8 +971,9 @@ const struct option record_options[] = {
"number of mmap data pages"),
OPT_BOOLEAN(0, "group", &record.opts.group,
"put the counters into a counter group"),
- OPT_BOOLEAN('g', "call-graph", &record.opts.call_graph,
- "do call-graph (stack chain/backtrace) recording"),
+ OPT_CALLBACK_DEFAULT('g', "call-graph", &record, "mode[,dump_size]",
+ callchain_help, &parse_callchain_opt,
+ "fp"),
OPT_INCR('v', "verbose", &verbose,
"be more verbose (show counter open errors, etc)"),
OPT_BOOLEAN('q', "quiet", &quiet, "don't print any message"),
@@ -836,7 +1005,7 @@ const struct option record_options[] = {
OPT_END()
};
-int cmd_record(int argc, const char **argv, const char *prefix __used)
+int cmd_record(int argc, const char **argv, const char *prefix __maybe_unused)
{
int err = -ENOMEM;
struct perf_evsel *pos;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 7c88a243b5db..1da243dfbc3e 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -69,8 +69,8 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain)
&& sample->callchain) {
- err = machine__resolve_callchain(machine, al->thread,
- sample->callchain, &parent);
+ err = machine__resolve_callchain(machine, evsel, al->thread,
+ sample, &parent);
if (err)
return err;
}
@@ -93,7 +93,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
struct annotation *notes;
err = -ENOMEM;
bx = he->branch_info;
- if (bx->from.sym && use_browser > 0) {
+ if (bx->from.sym && use_browser == 1 && sort__has_sym) {
notes = symbol__annotation(bx->from.sym);
if (!notes->src
&& symbol__alloc_hist(bx->from.sym) < 0)
@@ -107,7 +107,7 @@ static int perf_report__add_branch_hist_entry(struct perf_tool *tool,
goto out;
}
- if (bx->to.sym && use_browser > 0) {
+ if (bx->to.sym && use_browser == 1 && sort__has_sym) {
notes = symbol__annotation(bx->to.sym);
if (!notes->src
&& symbol__alloc_hist(bx->to.sym) < 0)
@@ -140,8 +140,8 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
struct hist_entry *he;
if ((sort__has_parent || symbol_conf.use_callchain) && sample->callchain) {
- err = machine__resolve_callchain(machine, al->thread,
- sample->callchain, &parent);
+ err = machine__resolve_callchain(machine, evsel, al->thread,
+ sample, &parent);
if (err)
return err;
}
@@ -162,7 +162,7 @@ static int perf_evsel__add_hist_entry(struct perf_evsel *evsel,
* so we don't allocated the extra space needed because the stdio
* code will not use it.
*/
- if (he->ms.sym != NULL && use_browser > 0) {
+ if (he->ms.sym != NULL && use_browser == 1 && sort__has_sym) {
struct annotation *notes = symbol__annotation(he->ms.sym);
assert(evsel != NULL);
@@ -223,9 +223,9 @@ static int process_sample_event(struct perf_tool *tool,
static int process_read_event(struct perf_tool *tool,
union perf_event *event,
- struct perf_sample *sample __used,
+ struct perf_sample *sample __maybe_unused,
struct perf_evsel *evsel,
- struct machine *machine __used)
+ struct machine *machine __maybe_unused)
{
struct perf_report *rep = container_of(tool, struct perf_report, tool);
@@ -287,7 +287,7 @@ static int perf_report__setup_sample_type(struct perf_report *rep)
extern volatile int session_done;
-static void sig_handler(int sig __used)
+static void sig_handler(int sig __maybe_unused)
{
session_done = 1;
}
@@ -397,17 +397,17 @@ static int __cmd_report(struct perf_report *rep)
desc);
}
- if (dump_trace) {
- perf_session__fprintf_nr_events(session, stdout);
- goto out_delete;
- }
-
if (verbose > 3)
perf_session__fprintf(session, stdout);
if (verbose > 2)
perf_session__fprintf_dsos(session, stdout);
+ if (dump_trace) {
+ perf_session__fprintf_nr_events(session, stdout);
+ goto out_delete;
+ }
+
nr_samples = 0;
list_for_each_entry(pos, &session->evlist->entries, node) {
struct hists *hists = &pos->hists;
@@ -533,13 +533,14 @@ setup:
}
static int
-parse_branch_mode(const struct option *opt __used, const char *str __used, int unset)
+parse_branch_mode(const struct option *opt __maybe_unused,
+ const char *str __maybe_unused, int unset)
{
sort__branch_mode = !unset;
return 0;
}
-int cmd_report(int argc, const char **argv, const char *prefix __used)
+int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
{
struct perf_session *session;
struct stat st;
@@ -638,6 +639,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
"Show a column with the sum of periods"),
OPT_CALLBACK_NOOPT('b', "branch-stack", &sort__branch_mode, "",
"use branch records for histogram filling", parse_branch_mode),
+ OPT_STRING(0, "objdump", &objdump_path, "path",
+ "objdump binary to use for disassembly and annotations"),
OPT_END()
};
@@ -686,15 +689,19 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
if (strcmp(report.input_name, "-") != 0)
setup_browser(true);
- else
+ else {
use_browser = 0;
+ perf_hpp__init(false, false);
+ }
+
+ setup_sorting(report_usage, options);
/*
* Only in the newt browser we are doing integrated annotation,
* so don't allocate extra space that won't be used in the stdio
* implementation.
*/
- if (use_browser > 0) {
+ if (use_browser == 1 && sort__has_sym) {
symbol_conf.priv_size = sizeof(struct annotation);
report.annotate_init = symbol__annotate_init;
/*
@@ -717,8 +724,6 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
if (symbol__init() < 0)
goto error;
- setup_sorting(report_usage, options);
-
if (parent_pattern != default_parent_pattern) {
if (sort_dimension__add("parent") < 0)
goto error;
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c
index 7a9ad2b1ee76..9b9e32eaa805 100644
--- a/tools/perf/builtin-sched.c
+++ b/tools/perf/builtin-sched.c
@@ -23,31 +23,12 @@
#include <pthread.h>
#include <math.h>
-static const char *input_name;
-
-static char default_sort_order[] = "avg, max, switch, runtime";
-static const char *sort_order = default_sort_order;
-
-static int profile_cpu = -1;
-
#define PR_SET_NAME 15 /* Set process name */
#define MAX_CPUS 4096
-
-static u64 run_measurement_overhead;
-static u64 sleep_measurement_overhead;
-
#define COMM_LEN 20
#define SYM_LEN 129
-
#define MAX_PID 65536
-static unsigned long nr_tasks;
-
-struct perf_sched {
- struct perf_tool tool;
- struct perf_session *session;
-};
-
struct sched_atom;
struct task_desc {
@@ -85,44 +66,6 @@ struct sched_atom {
struct task_desc *wakee;
};
-static struct task_desc *pid_to_task[MAX_PID];
-
-static struct task_desc **tasks;
-
-static pthread_mutex_t start_work_mutex = PTHREAD_MUTEX_INITIALIZER;
-static u64 start_time;
-
-static pthread_mutex_t work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static unsigned long nr_run_events;
-static unsigned long nr_sleep_events;
-static unsigned long nr_wakeup_events;
-
-static unsigned long nr_sleep_corrections;
-static unsigned long nr_run_events_optimized;
-
-static unsigned long targetless_wakeups;
-static unsigned long multitarget_wakeups;
-
-static u64 cpu_usage;
-static u64 runavg_cpu_usage;
-static u64 parent_cpu_usage;
-static u64 runavg_parent_cpu_usage;
-
-static unsigned long nr_runs;
-static u64 sum_runtime;
-static u64 sum_fluct;
-static u64 run_avg;
-
-static unsigned int replay_repeat = 10;
-static unsigned long nr_timestamps;
-static unsigned long nr_unordered_timestamps;
-static unsigned long nr_state_machine_bugs;
-static unsigned long nr_context_switch_bugs;
-static unsigned long nr_events;
-static unsigned long nr_lost_chunks;
-static unsigned long nr_lost_events;
-
#define TASK_STATE_TO_CHAR_STR "RSDTtZX"
enum thread_state {
@@ -154,11 +97,79 @@ struct work_atoms {
typedef int (*sort_fn_t)(struct work_atoms *, struct work_atoms *);
-static struct rb_root atom_root, sorted_atom_root;
+struct perf_sched;
+
+struct trace_sched_handler {
+ int (*switch_event)(struct perf_sched *sched, struct perf_evsel *evsel,
+ struct perf_sample *sample, struct machine *machine);
-static u64 all_runtime;
-static u64 all_count;
+ int (*runtime_event)(struct perf_sched *sched, struct perf_evsel *evsel,
+ struct perf_sample *sample, struct machine *machine);
+ int (*wakeup_event)(struct perf_sched *sched, struct perf_evsel *evsel,
+ struct perf_sample *sample, struct machine *machine);
+
+ int (*fork_event)(struct perf_sched *sched, struct perf_evsel *evsel,
+ struct perf_sample *sample);
+
+ int (*migrate_task_event)(struct perf_sched *sched,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine);
+};
+
+struct perf_sched {
+ struct perf_tool tool;
+ const char *input_name;
+ const char *sort_order;
+ unsigned long nr_tasks;
+ struct task_desc *pid_to_task[MAX_PID];
+ struct task_desc **tasks;
+ const struct trace_sched_handler *tp_handler;
+ pthread_mutex_t start_work_mutex;
+ pthread_mutex_t work_done_wait_mutex;
+ int profile_cpu;
+/*
+ * Track the current task - that way we can know whether there's any
+ * weird events, such as a task being switched away that is not current.
+ */
+ int max_cpu;
+ u32 curr_pid[MAX_CPUS];
+ struct thread *curr_thread[MAX_CPUS];
+ char next_shortname1;
+ char next_shortname2;
+ unsigned int replay_repeat;
+ unsigned long nr_run_events;
+ unsigned long nr_sleep_events;
+ unsigned long nr_wakeup_events;
+ unsigned long nr_sleep_corrections;
+ unsigned long nr_run_events_optimized;
+ unsigned long targetless_wakeups;
+ unsigned long multitarget_wakeups;
+ unsigned long nr_runs;
+ unsigned long nr_timestamps;
+ unsigned long nr_unordered_timestamps;
+ unsigned long nr_state_machine_bugs;
+ unsigned long nr_context_switch_bugs;
+ unsigned long nr_events;
+ unsigned long nr_lost_chunks;
+ unsigned long nr_lost_events;
+ u64 run_measurement_overhead;
+ u64 sleep_measurement_overhead;
+ u64 start_time;
+ u64 cpu_usage;
+ u64 runavg_cpu_usage;
+ u64 parent_cpu_usage;
+ u64 runavg_parent_cpu_usage;
+ u64 sum_runtime;
+ u64 sum_fluct;
+ u64 run_avg;
+ u64 all_runtime;
+ u64 all_count;
+ u64 cpu_last_switched[MAX_CPUS];
+ struct rb_root atom_root, sorted_atom_root;
+ struct list_head sort_list, cmp_pid;
+};
static u64 get_nsecs(void)
{
@@ -169,13 +180,13 @@ static u64 get_nsecs(void)
return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
}
-static void burn_nsecs(u64 nsecs)
+static void burn_nsecs(struct perf_sched *sched, u64 nsecs)
{
u64 T0 = get_nsecs(), T1;
do {
T1 = get_nsecs();
- } while (T1 + run_measurement_overhead < T0 + nsecs);
+ } while (T1 + sched->run_measurement_overhead < T0 + nsecs);
}
static void sleep_nsecs(u64 nsecs)
@@ -188,24 +199,24 @@ static void sleep_nsecs(u64 nsecs)
nanosleep(&ts, NULL);
}
-static void calibrate_run_measurement_overhead(void)
+static void calibrate_run_measurement_overhead(struct perf_sched *sched)
{
u64 T0, T1, delta, min_delta = 1000000000ULL;
int i;
for (i = 0; i < 10; i++) {
T0 = get_nsecs();
- burn_nsecs(0);
+ burn_nsecs(sched, 0);
T1 = get_nsecs();
delta = T1-T0;
min_delta = min(min_delta, delta);
}
- run_measurement_overhead = min_delta;
+ sched->run_measurement_overhead = min_delta;
printf("run measurement overhead: %" PRIu64 " nsecs\n", min_delta);
}
-static void calibrate_sleep_measurement_overhead(void)
+static void calibrate_sleep_measurement_overhead(struct perf_sched *sched)
{
u64 T0, T1, delta, min_delta = 1000000000ULL;
int i;
@@ -218,7 +229,7 @@ static void calibrate_sleep_measurement_overhead(void)
min_delta = min(min_delta, delta);
}
min_delta -= 10000;
- sleep_measurement_overhead = min_delta;
+ sched->sleep_measurement_overhead = min_delta;
printf("sleep measurement overhead: %" PRIu64 " nsecs\n", min_delta);
}
@@ -251,8 +262,8 @@ static struct sched_atom *last_event(struct task_desc *task)
return task->atoms[task->nr_events - 1];
}
-static void
-add_sched_event_run(struct task_desc *task, u64 timestamp, u64 duration)
+static void add_sched_event_run(struct perf_sched *sched, struct task_desc *task,
+ u64 timestamp, u64 duration)
{
struct sched_atom *event, *curr_event = last_event(task);
@@ -261,7 +272,7 @@ add_sched_event_run(struct task_desc *task, u64 timestamp, u64 duration)
* to it:
*/
if (curr_event && curr_event->type == SCHED_EVENT_RUN) {
- nr_run_events_optimized++;
+ sched->nr_run_events_optimized++;
curr_event->duration += duration;
return;
}
@@ -271,12 +282,11 @@ add_sched_event_run(struct task_desc *task, u64 timestamp, u64 duration)
event->type = SCHED_EVENT_RUN;
event->duration = duration;
- nr_run_events++;
+ sched->nr_run_events++;
}
-static void
-add_sched_event_wakeup(struct task_desc *task, u64 timestamp,
- struct task_desc *wakee)
+static void add_sched_event_wakeup(struct perf_sched *sched, struct task_desc *task,
+ u64 timestamp, struct task_desc *wakee)
{
struct sched_atom *event, *wakee_event;
@@ -286,11 +296,11 @@ add_sched_event_wakeup(struct task_desc *task, u64 timestamp,
wakee_event = last_event(wakee);
if (!wakee_event || wakee_event->type != SCHED_EVENT_SLEEP) {
- targetless_wakeups++;
+ sched->targetless_wakeups++;
return;
}
if (wakee_event->wait_sem) {
- multitarget_wakeups++;
+ sched->multitarget_wakeups++;
return;
}
@@ -299,89 +309,89 @@ add_sched_event_wakeup(struct task_desc *task, u64 timestamp,
wakee_event->specific_wait = 1;
event->wait_sem = wakee_event->wait_sem;
- nr_wakeup_events++;
+ sched->nr_wakeup_events++;
}
-static void
-add_sched_event_sleep(struct task_desc *task, u64 timestamp,
- u64 task_state __used)
+static void add_sched_event_sleep(struct perf_sched *sched, struct task_desc *task,
+ u64 timestamp, u64 task_state __maybe_unused)
{
struct sched_atom *event = get_new_event(task, timestamp);
event->type = SCHED_EVENT_SLEEP;
- nr_sleep_events++;
+ sched->nr_sleep_events++;
}
-static struct task_desc *register_pid(unsigned long pid, const char *comm)
+static struct task_desc *register_pid(struct perf_sched *sched,
+ unsigned long pid, const char *comm)
{
struct task_desc *task;
BUG_ON(pid >= MAX_PID);
- task = pid_to_task[pid];
+ task = sched->pid_to_task[pid];
if (task)
return task;
task = zalloc(sizeof(*task));
task->pid = pid;
- task->nr = nr_tasks;
+ task->nr = sched->nr_tasks;
strcpy(task->comm, comm);
/*
* every task starts in sleeping state - this gets ignored
* if there's no wakeup pointing to this sleep state:
*/
- add_sched_event_sleep(task, 0, 0);
+ add_sched_event_sleep(sched, task, 0, 0);
- pid_to_task[pid] = task;
- nr_tasks++;
- tasks = realloc(tasks, nr_tasks*sizeof(struct task_task *));
- BUG_ON(!tasks);
- tasks[task->nr] = task;
+ sched->pid_to_task[pid] = task;
+ sched->nr_tasks++;
+ sched->tasks = realloc(sched->tasks, sched->nr_tasks * sizeof(struct task_task *));
+ BUG_ON(!sched->tasks);
+ sched->tasks[task->nr] = task;
if (verbose)
- printf("registered task #%ld, PID %ld (%s)\n", nr_tasks, pid, comm);
+ printf("registered task #%ld, PID %ld (%s)\n", sched->nr_tasks, pid, comm);
return task;
}
-static void print_task_traces(void)
+static void print_task_traces(struct perf_sched *sched)
{
struct task_desc *task;
unsigned long i;
- for (i = 0; i < nr_tasks; i++) {
- task = tasks[i];
+ for (i = 0; i < sched->nr_tasks; i++) {
+ task = sched->tasks[i];
printf("task %6ld (%20s:%10ld), nr_events: %ld\n",
task->nr, task->comm, task->pid, task->nr_events);
}
}
-static void add_cross_task_wakeups(void)
+static void add_cross_task_wakeups(struct perf_sched *sched)
{
struct task_desc *task1, *task2;
unsigned long i, j;
- for (i = 0; i < nr_tasks; i++) {
- task1 = tasks[i];
+ for (i = 0; i < sched->nr_tasks; i++) {
+ task1 = sched->tasks[i];
j = i + 1;
- if (j == nr_tasks)
+ if (j == sched->nr_tasks)
j = 0;
- task2 = tasks[j];
- add_sched_event_wakeup(task1, 0, task2);
+ task2 = sched->tasks[j];
+ add_sched_event_wakeup(sched, task1, 0, task2);
}
}
-static void
-process_sched_event(struct task_desc *this_task __used, struct sched_atom *atom)
+static void perf_sched__process_event(struct perf_sched *sched,
+ struct sched_atom *atom)
{
int ret = 0;
switch (atom->type) {
case SCHED_EVENT_RUN:
- burn_nsecs(atom->duration);
+ burn_nsecs(sched, atom->duration);
break;
case SCHED_EVENT_SLEEP:
if (atom->wait_sem)
@@ -428,8 +438,8 @@ static int self_open_counters(void)
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
if (fd < 0)
- die("Error: sys_perf_event_open() syscall returned"
- "with %d (%s)\n", fd, strerror(errno));
+ pr_err("Error: sys_perf_event_open() syscall returned "
+ "with %d (%s)\n", fd, strerror(errno));
return fd;
}
@@ -444,31 +454,41 @@ static u64 get_cpu_usage_nsec_self(int fd)
return runtime;
}
+struct sched_thread_parms {
+ struct task_desc *task;
+ struct perf_sched *sched;
+};
+
static void *thread_func(void *ctx)
{
- struct task_desc *this_task = ctx;
+ struct sched_thread_parms *parms = ctx;
+ struct task_desc *this_task = parms->task;
+ struct perf_sched *sched = parms->sched;
u64 cpu_usage_0, cpu_usage_1;
unsigned long i, ret;
char comm2[22];
int fd;
+ free(parms);
+
sprintf(comm2, ":%s", this_task->comm);
prctl(PR_SET_NAME, comm2);
fd = self_open_counters();
-
+ if (fd < 0)
+ return NULL;
again:
ret = sem_post(&this_task->ready_for_work);
BUG_ON(ret);
- ret = pthread_mutex_lock(&start_work_mutex);
+ ret = pthread_mutex_lock(&sched->start_work_mutex);
BUG_ON(ret);
- ret = pthread_mutex_unlock(&start_work_mutex);
+ ret = pthread_mutex_unlock(&sched->start_work_mutex);
BUG_ON(ret);
cpu_usage_0 = get_cpu_usage_nsec_self(fd);
for (i = 0; i < this_task->nr_events; i++) {
this_task->curr_event = i;
- process_sched_event(this_task, this_task->atoms[i]);
+ perf_sched__process_event(sched, this_task->atoms[i]);
}
cpu_usage_1 = get_cpu_usage_nsec_self(fd);
@@ -476,15 +496,15 @@ again:
ret = sem_post(&this_task->work_done_sem);
BUG_ON(ret);
- ret = pthread_mutex_lock(&work_done_wait_mutex);
+ ret = pthread_mutex_lock(&sched->work_done_wait_mutex);
BUG_ON(ret);
- ret = pthread_mutex_unlock(&work_done_wait_mutex);
+ ret = pthread_mutex_unlock(&sched->work_done_wait_mutex);
BUG_ON(ret);
goto again;
}
-static void create_tasks(void)
+static void create_tasks(struct perf_sched *sched)
{
struct task_desc *task;
pthread_attr_t attr;
@@ -496,128 +516,129 @@ static void create_tasks(void)
err = pthread_attr_setstacksize(&attr,
(size_t) max(16 * 1024, PTHREAD_STACK_MIN));
BUG_ON(err);
- err = pthread_mutex_lock(&start_work_mutex);
+ err = pthread_mutex_lock(&sched->start_work_mutex);
BUG_ON(err);
- err = pthread_mutex_lock(&work_done_wait_mutex);
+ err = pthread_mutex_lock(&sched->work_done_wait_mutex);
BUG_ON(err);
- for (i = 0; i < nr_tasks; i++) {
- task = tasks[i];
+ for (i = 0; i < sched->nr_tasks; i++) {
+ struct sched_thread_parms *parms = malloc(sizeof(*parms));
+ BUG_ON(parms == NULL);
+ parms->task = task = sched->tasks[i];
+ parms->sched = sched;
sem_init(&task->sleep_sem, 0, 0);
sem_init(&task->ready_for_work, 0, 0);
sem_init(&task->work_done_sem, 0, 0);
task->curr_event = 0;
- err = pthread_create(&task->thread, &attr, thread_func, task);
+ err = pthread_create(&task->thread, &attr, thread_func, parms);
BUG_ON(err);
}
}
-static void wait_for_tasks(void)
+static void wait_for_tasks(struct perf_sched *sched)
{
u64 cpu_usage_0, cpu_usage_1;
struct task_desc *task;
unsigned long i, ret;
- start_time = get_nsecs();
- cpu_usage = 0;
- pthread_mutex_unlock(&work_done_wait_mutex);
+ sched->start_time = get_nsecs();
+ sched->cpu_usage = 0;
+ pthread_mutex_unlock(&sched->work_done_wait_mutex);
- for (i = 0; i < nr_tasks; i++) {
- task = tasks[i];
+ for (i = 0; i < sched->nr_tasks; i++) {
+ task = sched->tasks[i];
ret = sem_wait(&task->ready_for_work);
BUG_ON(ret);
sem_init(&task->ready_for_work, 0, 0);
}
- ret = pthread_mutex_lock(&work_done_wait_mutex);
+ ret = pthread_mutex_lock(&sched->work_done_wait_mutex);
BUG_ON(ret);
cpu_usage_0 = get_cpu_usage_nsec_parent();
- pthread_mutex_unlock(&start_work_mutex);
+ pthread_mutex_unlock(&sched->start_work_mutex);
- for (i = 0; i < nr_tasks; i++) {
- task = tasks[i];
+ for (i = 0; i < sched->nr_tasks; i++) {
+ task = sched->tasks[i];
ret = sem_wait(&task->work_done_sem);
BUG_ON(ret);
sem_init(&task->work_done_sem, 0, 0);
- cpu_usage += task->cpu_usage;
+ sched->cpu_usage += task->cpu_usage;
task->cpu_usage = 0;
}
cpu_usage_1 = get_cpu_usage_nsec_parent();
- if (!runavg_cpu_usage)
- runavg_cpu_usage = cpu_usage;
- runavg_cpu_usage = (runavg_cpu_usage*9 + cpu_usage)/10;
+ if (!sched->runavg_cpu_usage)
+ sched->runavg_cpu_usage = sched->cpu_usage;
+ sched->runavg_cpu_usage = (sched->runavg_cpu_usage * 9 + sched->cpu_usage) / 10;
- parent_cpu_usage = cpu_usage_1 - cpu_usage_0;
- if (!runavg_parent_cpu_usage)
- runavg_parent_cpu_usage = parent_cpu_usage;
- runavg_parent_cpu_usage = (runavg_parent_cpu_usage*9 +
- parent_cpu_usage)/10;
+ sched->parent_cpu_usage = cpu_usage_1 - cpu_usage_0;
+ if (!sched->runavg_parent_cpu_usage)
+ sched->runavg_parent_cpu_usage = sched->parent_cpu_usage;
+ sched->runavg_parent_cpu_usage = (sched->runavg_parent_cpu_usage * 9 +
+ sched->parent_cpu_usage)/10;
- ret = pthread_mutex_lock(&start_work_mutex);
+ ret = pthread_mutex_lock(&sched->start_work_mutex);
BUG_ON(ret);
- for (i = 0; i < nr_tasks; i++) {
- task = tasks[i];
+ for (i = 0; i < sched->nr_tasks; i++) {
+ task = sched->tasks[i];
sem_init(&task->sleep_sem, 0, 0);
task->curr_event = 0;
}
}
-static void run_one_test(void)
+static void run_one_test(struct perf_sched *sched)
{
u64 T0, T1, delta, avg_delta, fluct;
T0 = get_nsecs();
- wait_for_tasks();
+ wait_for_tasks(sched);
T1 = get_nsecs();
delta = T1 - T0;
- sum_runtime += delta;
- nr_runs++;
+ sched->sum_runtime += delta;
+ sched->nr_runs++;
- avg_delta = sum_runtime / nr_runs;
+ avg_delta = sched->sum_runtime / sched->nr_runs;
if (delta < avg_delta)
fluct = avg_delta - delta;
else
fluct = delta - avg_delta;
- sum_fluct += fluct;
- if (!run_avg)
- run_avg = delta;
- run_avg = (run_avg*9 + delta)/10;
+ sched->sum_fluct += fluct;
+ if (!sched->run_avg)
+ sched->run_avg = delta;
+ sched->run_avg = (sched->run_avg * 9 + delta) / 10;
- printf("#%-3ld: %0.3f, ",
- nr_runs, (double)delta/1000000.0);
+ printf("#%-3ld: %0.3f, ", sched->nr_runs, (double)delta / 1000000.0);
- printf("ravg: %0.2f, ",
- (double)run_avg/1e6);
+ printf("ravg: %0.2f, ", (double)sched->run_avg / 1e6);
printf("cpu: %0.2f / %0.2f",
- (double)cpu_usage/1e6, (double)runavg_cpu_usage/1e6);
+ (double)sched->cpu_usage / 1e6, (double)sched->runavg_cpu_usage / 1e6);
#if 0
/*
* rusage statistics done by the parent, these are less
- * accurate than the sum_exec_runtime based statistics:
+ * accurate than the sched->sum_exec_runtime based statistics:
*/
printf(" [%0.2f / %0.2f]",
- (double)parent_cpu_usage/1e6,
- (double)runavg_parent_cpu_usage/1e6);
+ (double)sched->parent_cpu_usage/1e6,
+ (double)sched->runavg_parent_cpu_usage/1e6);
#endif
printf("\n");
- if (nr_sleep_corrections)
- printf(" (%ld sleep corrections)\n", nr_sleep_corrections);
- nr_sleep_corrections = 0;
+ if (sched->nr_sleep_corrections)
+ printf(" (%ld sleep corrections)\n", sched->nr_sleep_corrections);
+ sched->nr_sleep_corrections = 0;
}
-static void test_calibrations(void)
+static void test_calibrations(struct perf_sched *sched)
{
u64 T0, T1;
T0 = get_nsecs();
- burn_nsecs(1e6);
+ burn_nsecs(sched, 1e6);
T1 = get_nsecs();
printf("the run test took %" PRIu64 " nsecs\n", T1 - T0);
@@ -629,236 +650,92 @@ static void test_calibrations(void)
printf("the sleep test took %" PRIu64 " nsecs\n", T1 - T0);
}
-#define FILL_FIELD(ptr, field, event, data) \
- ptr.field = (typeof(ptr.field)) raw_field_value(event, #field, data)
-
-#define FILL_ARRAY(ptr, array, event, data) \
-do { \
- void *__array = raw_field_ptr(event, #array, data); \
- memcpy(ptr.array, __array, sizeof(ptr.array)); \
-} while(0)
-
-#define FILL_COMMON_FIELDS(ptr, event, data) \
-do { \
- FILL_FIELD(ptr, common_type, event, data); \
- FILL_FIELD(ptr, common_flags, event, data); \
- FILL_FIELD(ptr, common_preempt_count, event, data); \
- FILL_FIELD(ptr, common_pid, event, data); \
- FILL_FIELD(ptr, common_tgid, event, data); \
-} while (0)
-
-
-
-struct trace_switch_event {
- u32 size;
-
- u16 common_type;
- u8 common_flags;
- u8 common_preempt_count;
- u32 common_pid;
- u32 common_tgid;
-
- char prev_comm[16];
- u32 prev_pid;
- u32 prev_prio;
- u64 prev_state;
- char next_comm[16];
- u32 next_pid;
- u32 next_prio;
-};
-
-struct trace_runtime_event {
- u32 size;
-
- u16 common_type;
- u8 common_flags;
- u8 common_preempt_count;
- u32 common_pid;
- u32 common_tgid;
-
- char comm[16];
- u32 pid;
- u64 runtime;
- u64 vruntime;
-};
-
-struct trace_wakeup_event {
- u32 size;
-
- u16 common_type;
- u8 common_flags;
- u8 common_preempt_count;
- u32 common_pid;
- u32 common_tgid;
-
- char comm[16];
- u32 pid;
-
- u32 prio;
- u32 success;
- u32 cpu;
-};
-
-struct trace_fork_event {
- u32 size;
-
- u16 common_type;
- u8 common_flags;
- u8 common_preempt_count;
- u32 common_pid;
- u32 common_tgid;
-
- char parent_comm[16];
- u32 parent_pid;
- char child_comm[16];
- u32 child_pid;
-};
-
-struct trace_migrate_task_event {
- u32 size;
-
- u16 common_type;
- u8 common_flags;
- u8 common_preempt_count;
- u32 common_pid;
- u32 common_tgid;
-
- char comm[16];
- u32 pid;
-
- u32 prio;
- u32 cpu;
-};
-
-struct trace_sched_handler {
- void (*switch_event)(struct trace_switch_event *,
- struct machine *,
- struct event_format *,
- int cpu,
- u64 timestamp,
- struct thread *thread);
-
- void (*runtime_event)(struct trace_runtime_event *,
- struct machine *,
- struct event_format *,
- int cpu,
- u64 timestamp,
- struct thread *thread);
-
- void (*wakeup_event)(struct trace_wakeup_event *,
- struct machine *,
- struct event_format *,
- int cpu,
- u64 timestamp,
- struct thread *thread);
-
- void (*fork_event)(struct trace_fork_event *,
- struct event_format *,
- int cpu,
- u64 timestamp,
- struct thread *thread);
-
- void (*migrate_task_event)(struct trace_migrate_task_event *,
- struct machine *machine,
- struct event_format *,
- int cpu,
- u64 timestamp,
- struct thread *thread);
-};
-
-
-static void
-replay_wakeup_event(struct trace_wakeup_event *wakeup_event,
- struct machine *machine __used,
- struct event_format *event,
- int cpu __used,
- u64 timestamp __used,
- struct thread *thread __used)
+static int
+replay_wakeup_event(struct perf_sched *sched,
+ struct perf_evsel *evsel, struct perf_sample *sample,
+ struct machine *machine __maybe_unused)
{
+ const char *comm = perf_evsel__strval(evsel, sample, "comm");
+ const u32 pid = perf_evsel__intval(evsel, sample, "pid");
struct task_desc *waker, *wakee;
if (verbose) {
- printf("sched_wakeup event %p\n", event);
+ printf("sched_wakeup event %p\n", evsel);
- printf(" ... pid %d woke up %s/%d\n",
- wakeup_event->common_pid,
- wakeup_event->comm,
- wakeup_event->pid);
+ printf(" ... pid %d woke up %s/%d\n", sample->tid, comm, pid);
}
- waker = register_pid(wakeup_event->common_pid, "<unknown>");
- wakee = register_pid(wakeup_event->pid, wakeup_event->comm);
+ waker = register_pid(sched, sample->tid, "<unknown>");
+ wakee = register_pid(sched, pid, comm);
- add_sched_event_wakeup(waker, timestamp, wakee);
+ add_sched_event_wakeup(sched, waker, sample->time, wakee);
+ return 0;
}
-static u64 cpu_last_switched[MAX_CPUS];
-
-static void
-replay_switch_event(struct trace_switch_event *switch_event,
- struct machine *machine __used,
- struct event_format *event,
- int cpu,
- u64 timestamp,
- struct thread *thread __used)
+static int replay_switch_event(struct perf_sched *sched,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine __maybe_unused)
{
- struct task_desc *prev, __used *next;
- u64 timestamp0;
+ const char *prev_comm = perf_evsel__strval(evsel, sample, "prev_comm"),
+ *next_comm = perf_evsel__strval(evsel, sample, "next_comm");
+ const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"),
+ next_pid = perf_evsel__intval(evsel, sample, "next_pid");
+ const u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
+ struct task_desc *prev, __maybe_unused *next;
+ u64 timestamp0, timestamp = sample->time;
+ int cpu = sample->cpu;
s64 delta;
if (verbose)
- printf("sched_switch event %p\n", event);
+ printf("sched_switch event %p\n", evsel);
if (cpu >= MAX_CPUS || cpu < 0)
- return;
+ return 0;
- timestamp0 = cpu_last_switched[cpu];
+ timestamp0 = sched->cpu_last_switched[cpu];
if (timestamp0)
delta = timestamp - timestamp0;
else
delta = 0;
- if (delta < 0)
- die("hm, delta: %" PRIu64 " < 0 ?\n", delta);
-
- if (verbose) {
- printf(" ... switch from %s/%d to %s/%d [ran %" PRIu64 " nsecs]\n",
- switch_event->prev_comm, switch_event->prev_pid,
- switch_event->next_comm, switch_event->next_pid,
- delta);
+ if (delta < 0) {
+ pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta);
+ return -1;
}
- prev = register_pid(switch_event->prev_pid, switch_event->prev_comm);
- next = register_pid(switch_event->next_pid, switch_event->next_comm);
+ pr_debug(" ... switch from %s/%d to %s/%d [ran %" PRIu64 " nsecs]\n",
+ prev_comm, prev_pid, next_comm, next_pid, delta);
- cpu_last_switched[cpu] = timestamp;
+ prev = register_pid(sched, prev_pid, prev_comm);
+ next = register_pid(sched, next_pid, next_comm);
- add_sched_event_run(prev, timestamp, delta);
- add_sched_event_sleep(prev, timestamp, switch_event->prev_state);
-}
+ sched->cpu_last_switched[cpu] = timestamp;
+ add_sched_event_run(sched, prev, timestamp, delta);
+ add_sched_event_sleep(sched, prev, timestamp, prev_state);
-static void
-replay_fork_event(struct trace_fork_event *fork_event,
- struct event_format *event,
- int cpu __used,
- u64 timestamp __used,
- struct thread *thread __used)
+ return 0;
+}
+
+static int replay_fork_event(struct perf_sched *sched, struct perf_evsel *evsel,
+ struct perf_sample *sample)
{
+ const char *parent_comm = perf_evsel__strval(evsel, sample, "parent_comm"),
+ *child_comm = perf_evsel__strval(evsel, sample, "child_comm");
+ const u32 parent_pid = perf_evsel__intval(evsel, sample, "parent_pid"),
+ child_pid = perf_evsel__intval(evsel, sample, "child_pid");
+
if (verbose) {
- printf("sched_fork event %p\n", event);
- printf("... parent: %s/%d\n", fork_event->parent_comm, fork_event->parent_pid);
- printf("... child: %s/%d\n", fork_event->child_comm, fork_event->child_pid);
+ printf("sched_fork event %p\n", evsel);
+ printf("... parent: %s/%d\n", parent_comm, parent_pid);
+ printf("... child: %s/%d\n", child_comm, child_pid);
}
- register_pid(fork_event->parent_pid, fork_event->parent_comm);
- register_pid(fork_event->child_pid, fork_event->child_comm);
-}
-static struct trace_sched_handler replay_ops = {
- .wakeup_event = replay_wakeup_event,
- .switch_event = replay_switch_event,
- .fork_event = replay_fork_event,
-};
+ register_pid(sched, parent_pid, parent_comm);
+ register_pid(sched, child_pid, child_comm);
+ return 0;
+}
struct sort_dimension {
const char *name;
@@ -866,8 +743,6 @@ struct sort_dimension {
struct list_head list;
};
-static LIST_HEAD(cmp_pid);
-
static int
thread_lat_cmp(struct list_head *list, struct work_atoms *l, struct work_atoms *r)
{
@@ -936,43 +811,45 @@ __thread_latency_insert(struct rb_root *root, struct work_atoms *data,
rb_insert_color(&data->node, root);
}
-static void thread_atoms_insert(struct thread *thread)
+static int thread_atoms_insert(struct perf_sched *sched, struct thread *thread)
{
struct work_atoms *atoms = zalloc(sizeof(*atoms));
- if (!atoms)
- die("No memory");
+ if (!atoms) {
+ pr_err("No memory at %s\n", __func__);
+ return -1;
+ }
atoms->thread = thread;
INIT_LIST_HEAD(&atoms->work_list);
- __thread_latency_insert(&atom_root, atoms, &cmp_pid);
+ __thread_latency_insert(&sched->atom_root, atoms, &sched->cmp_pid);
+ return 0;
}
-static void
-latency_fork_event(struct trace_fork_event *fork_event __used,
- struct event_format *event __used,
- int cpu __used,
- u64 timestamp __used,
- struct thread *thread __used)
+static int latency_fork_event(struct perf_sched *sched __maybe_unused,
+ struct perf_evsel *evsel __maybe_unused,
+ struct perf_sample *sample __maybe_unused)
{
/* should insert the newcomer */
+ return 0;
}
-__used
-static char sched_out_state(struct trace_switch_event *switch_event)
+static char sched_out_state(u64 prev_state)
{
const char *str = TASK_STATE_TO_CHAR_STR;
- return str[switch_event->prev_state];
+ return str[prev_state];
}
-static void
+static int
add_sched_out_event(struct work_atoms *atoms,
char run_state,
u64 timestamp)
{
struct work_atom *atom = zalloc(sizeof(*atom));
- if (!atom)
- die("Non memory");
+ if (!atom) {
+ pr_err("Non memory at %s", __func__);
+ return -1;
+ }
atom->sched_out_time = timestamp;
@@ -982,10 +859,12 @@ add_sched_out_event(struct work_atoms *atoms,
}
list_add_tail(&atom->list, &atoms->work_list);
+ return 0;
}
static void
-add_runtime_event(struct work_atoms *atoms, u64 delta, u64 timestamp __used)
+add_runtime_event(struct work_atoms *atoms, u64 delta,
+ u64 timestamp __maybe_unused)
{
struct work_atom *atom;
@@ -1028,106 +907,128 @@ add_sched_in_event(struct work_atoms *atoms, u64 timestamp)
atoms->nb_atoms++;
}
-static void
-latency_switch_event(struct trace_switch_event *switch_event,
- struct machine *machine,
- struct event_format *event __used,
- int cpu,
- u64 timestamp,
- struct thread *thread __used)
+static int latency_switch_event(struct perf_sched *sched,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine)
{
+ const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"),
+ next_pid = perf_evsel__intval(evsel, sample, "next_pid");
+ const u64 prev_state = perf_evsel__intval(evsel, sample, "prev_state");
struct work_atoms *out_events, *in_events;
struct thread *sched_out, *sched_in;
- u64 timestamp0;
+ u64 timestamp0, timestamp = sample->time;
+ int cpu = sample->cpu;
s64 delta;
BUG_ON(cpu >= MAX_CPUS || cpu < 0);
- timestamp0 = cpu_last_switched[cpu];
- cpu_last_switched[cpu] = timestamp;
+ timestamp0 = sched->cpu_last_switched[cpu];
+ sched->cpu_last_switched[cpu] = timestamp;
if (timestamp0)
delta = timestamp - timestamp0;
else
delta = 0;
- if (delta < 0)
- die("hm, delta: %" PRIu64 " < 0 ?\n", delta);
-
+ if (delta < 0) {
+ pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta);
+ return -1;
+ }
- sched_out = machine__findnew_thread(machine, switch_event->prev_pid);
- sched_in = machine__findnew_thread(machine, switch_event->next_pid);
+ sched_out = machine__findnew_thread(machine, prev_pid);
+ sched_in = machine__findnew_thread(machine, next_pid);
- out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid);
+ out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid);
if (!out_events) {
- thread_atoms_insert(sched_out);
- out_events = thread_atoms_search(&atom_root, sched_out, &cmp_pid);
- if (!out_events)
- die("out-event: Internal tree error");
+ if (thread_atoms_insert(sched, sched_out))
+ return -1;
+ out_events = thread_atoms_search(&sched->atom_root, sched_out, &sched->cmp_pid);
+ if (!out_events) {
+ pr_err("out-event: Internal tree error");
+ return -1;
+ }
}
- add_sched_out_event(out_events, sched_out_state(switch_event), timestamp);
+ if (add_sched_out_event(out_events, sched_out_state(prev_state), timestamp))
+ return -1;
- in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid);
+ in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid);
if (!in_events) {
- thread_atoms_insert(sched_in);
- in_events = thread_atoms_search(&atom_root, sched_in, &cmp_pid);
- if (!in_events)
- die("in-event: Internal tree error");
+ if (thread_atoms_insert(sched, sched_in))
+ return -1;
+ in_events = thread_atoms_search(&sched->atom_root, sched_in, &sched->cmp_pid);
+ if (!in_events) {
+ pr_err("in-event: Internal tree error");
+ return -1;
+ }
/*
* Take came in we have not heard about yet,
* add in an initial atom in runnable state:
*/
- add_sched_out_event(in_events, 'R', timestamp);
+ if (add_sched_out_event(in_events, 'R', timestamp))
+ return -1;
}
add_sched_in_event(in_events, timestamp);
+
+ return 0;
}
-static void
-latency_runtime_event(struct trace_runtime_event *runtime_event,
- struct machine *machine,
- struct event_format *event __used,
- int cpu,
- u64 timestamp,
- struct thread *this_thread __used)
+static int latency_runtime_event(struct perf_sched *sched,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine)
{
- struct thread *thread = machine__findnew_thread(machine, runtime_event->pid);
- struct work_atoms *atoms = thread_atoms_search(&atom_root, thread, &cmp_pid);
+ const u32 pid = perf_evsel__intval(evsel, sample, "pid");
+ const u64 runtime = perf_evsel__intval(evsel, sample, "runtime");
+ struct thread *thread = machine__findnew_thread(machine, pid);
+ struct work_atoms *atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid);
+ u64 timestamp = sample->time;
+ int cpu = sample->cpu;
BUG_ON(cpu >= MAX_CPUS || cpu < 0);
if (!atoms) {
- thread_atoms_insert(thread);
- atoms = thread_atoms_search(&atom_root, thread, &cmp_pid);
- if (!atoms)
- die("in-event: Internal tree error");
- add_sched_out_event(atoms, 'R', timestamp);
+ if (thread_atoms_insert(sched, thread))
+ return -1;
+ atoms = thread_atoms_search(&sched->atom_root, thread, &sched->cmp_pid);
+ if (!atoms) {
+ pr_err("in-event: Internal tree error");
+ return -1;
+ }
+ if (add_sched_out_event(atoms, 'R', timestamp))
+ return -1;
}
- add_runtime_event(atoms, runtime_event->runtime, timestamp);
+ add_runtime_event(atoms, runtime, timestamp);
+ return 0;
}
-static void
-latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
- struct machine *machine,
- struct event_format *__event __used,
- int cpu __used,
- u64 timestamp,
- struct thread *thread __used)
+static int latency_wakeup_event(struct perf_sched *sched,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine)
{
+ const u32 pid = perf_evsel__intval(evsel, sample, "pid"),
+ success = perf_evsel__intval(evsel, sample, "success");
struct work_atoms *atoms;
struct work_atom *atom;
struct thread *wakee;
+ u64 timestamp = sample->time;
/* Note for later, it may be interesting to observe the failing cases */
- if (!wakeup_event->success)
- return;
+ if (!success)
+ return 0;
- wakee = machine__findnew_thread(machine, wakeup_event->pid);
- atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid);
+ wakee = machine__findnew_thread(machine, pid);
+ atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);
if (!atoms) {
- thread_atoms_insert(wakee);
- atoms = thread_atoms_search(&atom_root, wakee, &cmp_pid);
- if (!atoms)
- die("wakeup-event: Internal tree error");
- add_sched_out_event(atoms, 'S', timestamp);
+ if (thread_atoms_insert(sched, wakee))
+ return -1;
+ atoms = thread_atoms_search(&sched->atom_root, wakee, &sched->cmp_pid);
+ if (!atoms) {
+ pr_err("wakeup-event: Internal tree error");
+ return -1;
+ }
+ if (add_sched_out_event(atoms, 'S', timestamp))
+ return -1;
}
BUG_ON(list_empty(&atoms->work_list));
@@ -1139,27 +1040,27 @@ latency_wakeup_event(struct trace_wakeup_event *wakeup_event,
* one CPU, or are only looking at only one, so don't
* make useless noise.
*/
- if (profile_cpu == -1 && atom->state != THREAD_SLEEPING)
- nr_state_machine_bugs++;
+ if (sched->profile_cpu == -1 && atom->state != THREAD_SLEEPING)
+ sched->nr_state_machine_bugs++;
- nr_timestamps++;
+ sched->nr_timestamps++;
if (atom->sched_out_time > timestamp) {
- nr_unordered_timestamps++;
- return;
+ sched->nr_unordered_timestamps++;
+ return 0;
}
atom->state = THREAD_WAIT_CPU;
atom->wake_up_time = timestamp;
+ return 0;
}
-static void
-latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event,
- struct machine *machine,
- struct event_format *__event __used,
- int cpu __used,
- u64 timestamp,
- struct thread *thread __used)
+static int latency_migrate_task_event(struct perf_sched *sched,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine)
{
+ const u32 pid = perf_evsel__intval(evsel, sample, "pid");
+ u64 timestamp = sample->time;
struct work_atoms *atoms;
struct work_atom *atom;
struct thread *migrant;
@@ -1167,18 +1068,22 @@ latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event,
/*
* Only need to worry about migration when profiling one CPU.
*/
- if (profile_cpu == -1)
- return;
+ if (sched->profile_cpu == -1)
+ return 0;
- migrant = machine__findnew_thread(machine, migrate_task_event->pid);
- atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid);
+ migrant = machine__findnew_thread(machine, pid);
+ atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
if (!atoms) {
- thread_atoms_insert(migrant);
- register_pid(migrant->pid, migrant->comm);
- atoms = thread_atoms_search(&atom_root, migrant, &cmp_pid);
- if (!atoms)
- die("migration-event: Internal tree error");
- add_sched_out_event(atoms, 'R', timestamp);
+ if (thread_atoms_insert(sched, migrant))
+ return -1;
+ register_pid(sched, migrant->pid, migrant->comm);
+ atoms = thread_atoms_search(&sched->atom_root, migrant, &sched->cmp_pid);
+ if (!atoms) {
+ pr_err("migration-event: Internal tree error");
+ return -1;
+ }
+ if (add_sched_out_event(atoms, 'R', timestamp))
+ return -1;
}
BUG_ON(list_empty(&atoms->work_list));
@@ -1186,21 +1091,15 @@ latency_migrate_task_event(struct trace_migrate_task_event *migrate_task_event,
atom = list_entry(atoms->work_list.prev, struct work_atom, list);
atom->sched_in_time = atom->sched_out_time = atom->wake_up_time = timestamp;
- nr_timestamps++;
+ sched->nr_timestamps++;
if (atom->sched_out_time > timestamp)
- nr_unordered_timestamps++;
-}
+ sched->nr_unordered_timestamps++;
-static struct trace_sched_handler lat_ops = {
- .wakeup_event = latency_wakeup_event,
- .switch_event = latency_switch_event,
- .runtime_event = latency_runtime_event,
- .fork_event = latency_fork_event,
- .migrate_task_event = latency_migrate_task_event,
-};
+ return 0;
+}
-static void output_lat_thread(struct work_atoms *work_list)
+static void output_lat_thread(struct perf_sched *sched, struct work_atoms *work_list)
{
int i;
int ret;
@@ -1214,8 +1113,8 @@ static void output_lat_thread(struct work_atoms *work_list)
if (!strcmp(work_list->thread->comm, "swapper"))
return;
- all_runtime += work_list->total_runtime;
- all_count += work_list->nb_atoms;
+ sched->all_runtime += work_list->total_runtime;
+ sched->all_count += work_list->nb_atoms;
ret = printf(" %s:%d ", work_list->thread->comm, work_list->thread->pid);
@@ -1241,11 +1140,6 @@ static int pid_cmp(struct work_atoms *l, struct work_atoms *r)
return 0;
}
-static struct sort_dimension pid_sort_dimension = {
- .name = "pid",
- .cmp = pid_cmp,
-};
-
static int avg_cmp(struct work_atoms *l, struct work_atoms *r)
{
u64 avgl, avgr;
@@ -1267,11 +1161,6 @@ static int avg_cmp(struct work_atoms *l, struct work_atoms *r)
return 0;
}
-static struct sort_dimension avg_sort_dimension = {
- .name = "avg",
- .cmp = avg_cmp,
-};
-
static int max_cmp(struct work_atoms *l, struct work_atoms *r)
{
if (l->max_lat < r->max_lat)
@@ -1282,11 +1171,6 @@ static int max_cmp(struct work_atoms *l, struct work_atoms *r)
return 0;
}
-static struct sort_dimension max_sort_dimension = {
- .name = "max",
- .cmp = max_cmp,
-};
-
static int switch_cmp(struct work_atoms *l, struct work_atoms *r)
{
if (l->nb_atoms < r->nb_atoms)
@@ -1297,11 +1181,6 @@ static int switch_cmp(struct work_atoms *l, struct work_atoms *r)
return 0;
}
-static struct sort_dimension switch_sort_dimension = {
- .name = "switch",
- .cmp = switch_cmp,
-};
-
static int runtime_cmp(struct work_atoms *l, struct work_atoms *r)
{
if (l->total_runtime < r->total_runtime)
@@ -1312,28 +1191,38 @@ static int runtime_cmp(struct work_atoms *l, struct work_atoms *r)
return 0;
}
-static struct sort_dimension runtime_sort_dimension = {
- .name = "runtime",
- .cmp = runtime_cmp,
-};
-
-static struct sort_dimension *available_sorts[] = {
- &pid_sort_dimension,
- &avg_sort_dimension,
- &max_sort_dimension,
- &switch_sort_dimension,
- &runtime_sort_dimension,
-};
-
-#define NB_AVAILABLE_SORTS (int)(sizeof(available_sorts) / sizeof(struct sort_dimension *))
-
-static LIST_HEAD(sort_list);
-
static int sort_dimension__add(const char *tok, struct list_head *list)
{
- int i;
+ size_t i;
+ static struct sort_dimension avg_sort_dimension = {
+ .name = "avg",
+ .cmp = avg_cmp,
+ };
+ static struct sort_dimension max_sort_dimension = {
+ .name = "max",
+ .cmp = max_cmp,
+ };
+ static struct sort_dimension pid_sort_dimension = {
+ .name = "pid",
+ .cmp = pid_cmp,
+ };
+ static struct sort_dimension runtime_sort_dimension = {
+ .name = "runtime",
+ .cmp = runtime_cmp,
+ };
+ static struct sort_dimension switch_sort_dimension = {
+ .name = "switch",
+ .cmp = switch_cmp,
+ };
+ struct sort_dimension *available_sorts[] = {
+ &pid_sort_dimension,
+ &avg_sort_dimension,
+ &max_sort_dimension,
+ &switch_sort_dimension,
+ &runtime_sort_dimension,
+ };
- for (i = 0; i < NB_AVAILABLE_SORTS; i++) {
+ for (i = 0; i < ARRAY_SIZE(available_sorts); i++) {
if (!strcmp(available_sorts[i]->name, tok)) {
list_add_tail(&available_sorts[i]->list, list);
@@ -1344,126 +1233,97 @@ static int sort_dimension__add(const char *tok, struct list_head *list)
return -1;
}
-static void setup_sorting(void);
-
-static void sort_lat(void)
+static void perf_sched__sort_lat(struct perf_sched *sched)
{
struct rb_node *node;
for (;;) {
struct work_atoms *data;
- node = rb_first(&atom_root);
+ node = rb_first(&sched->atom_root);
if (!node)
break;
- rb_erase(node, &atom_root);
+ rb_erase(node, &sched->atom_root);
data = rb_entry(node, struct work_atoms, node);
- __thread_latency_insert(&sorted_atom_root, data, &sort_list);
+ __thread_latency_insert(&sched->sorted_atom_root, data, &sched->sort_list);
}
}
-static struct trace_sched_handler *trace_handler;
-
-static void
-process_sched_wakeup_event(struct perf_tool *tool __used,
- struct event_format *event,
- struct perf_sample *sample,
- struct machine *machine,
- struct thread *thread)
+static int process_sched_wakeup_event(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine)
{
- void *data = sample->raw_data;
- struct trace_wakeup_event wakeup_event;
-
- FILL_COMMON_FIELDS(wakeup_event, event, data);
+ struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
- FILL_ARRAY(wakeup_event, comm, event, data);
- FILL_FIELD(wakeup_event, pid, event, data);
- FILL_FIELD(wakeup_event, prio, event, data);
- FILL_FIELD(wakeup_event, success, event, data);
- FILL_FIELD(wakeup_event, cpu, event, data);
+ if (sched->tp_handler->wakeup_event)
+ return sched->tp_handler->wakeup_event(sched, evsel, sample, machine);
- if (trace_handler->wakeup_event)
- trace_handler->wakeup_event(&wakeup_event, machine, event,
- sample->cpu, sample->time, thread);
+ return 0;
}
-/*
- * Track the current task - that way we can know whether there's any
- * weird events, such as a task being switched away that is not current.
- */
-static int max_cpu;
-
-static u32 curr_pid[MAX_CPUS] = { [0 ... MAX_CPUS-1] = -1 };
-
-static struct thread *curr_thread[MAX_CPUS];
-
-static char next_shortname1 = 'A';
-static char next_shortname2 = '0';
-
-static void
-map_switch_event(struct trace_switch_event *switch_event,
- struct machine *machine,
- struct event_format *event __used,
- int this_cpu,
- u64 timestamp,
- struct thread *thread __used)
+static int map_switch_event(struct perf_sched *sched, struct perf_evsel *evsel,
+ struct perf_sample *sample, struct machine *machine)
{
- struct thread *sched_out __used, *sched_in;
+ const u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"),
+ next_pid = perf_evsel__intval(evsel, sample, "next_pid");
+ struct thread *sched_out __maybe_unused, *sched_in;
int new_shortname;
- u64 timestamp0;
+ u64 timestamp0, timestamp = sample->time;
s64 delta;
- int cpu;
+ int cpu, this_cpu = sample->cpu;
BUG_ON(this_cpu >= MAX_CPUS || this_cpu < 0);
- if (this_cpu > max_cpu)
- max_cpu = this_cpu;
+ if (this_cpu > sched->max_cpu)
+ sched->max_cpu = this_cpu;
- timestamp0 = cpu_last_switched[this_cpu];
- cpu_last_switched[this_cpu] = timestamp;
+ timestamp0 = sched->cpu_last_switched[this_cpu];
+ sched->cpu_last_switched[this_cpu] = timestamp;
if (timestamp0)
delta = timestamp - timestamp0;
else
delta = 0;
- if (delta < 0)
- die("hm, delta: %" PRIu64 " < 0 ?\n", delta);
-
+ if (delta < 0) {
+ pr_err("hm, delta: %" PRIu64 " < 0 ?\n", delta);
+ return -1;
+ }
- sched_out = machine__findnew_thread(machine, switch_event->prev_pid);
- sched_in = machine__findnew_thread(machine, switch_event->next_pid);
+ sched_out = machine__findnew_thread(machine, prev_pid);
+ sched_in = machine__findnew_thread(machine, next_pid);
- curr_thread[this_cpu] = sched_in;
+ sched->curr_thread[this_cpu] = sched_in;
printf(" ");
new_shortname = 0;
if (!sched_in->shortname[0]) {
- sched_in->shortname[0] = next_shortname1;
- sched_in->shortname[1] = next_shortname2;
+ sched_in->shortname[0] = sched->next_shortname1;
+ sched_in->shortname[1] = sched->next_shortname2;
- if (next_shortname1 < 'Z') {
- next_shortname1++;
+ if (sched->next_shortname1 < 'Z') {
+ sched->next_shortname1++;
} else {
- next_shortname1='A';
- if (next_shortname2 < '9') {
- next_shortname2++;
+ sched->next_shortname1='A';
+ if (sched->next_shortname2 < '9') {
+ sched->next_shortname2++;
} else {
- next_shortname2='0';
+ sched->next_shortname2='0';
}
}
new_shortname = 1;
}
- for (cpu = 0; cpu <= max_cpu; cpu++) {
+ for (cpu = 0; cpu <= sched->max_cpu; cpu++) {
if (cpu != this_cpu)
printf(" ");
else
printf("*");
- if (curr_thread[cpu]) {
- if (curr_thread[cpu]->pid)
- printf("%2s ", curr_thread[cpu]->shortname);
+ if (sched->curr_thread[cpu]) {
+ if (sched->curr_thread[cpu]->pid)
+ printf("%2s ", sched->curr_thread[cpu]->shortname);
else
printf(". ");
} else
@@ -1477,134 +1337,97 @@ map_switch_event(struct trace_switch_event *switch_event,
} else {
printf("\n");
}
+
+ return 0;
}
-static void
-process_sched_switch_event(struct perf_tool *tool __used,
- struct event_format *event,
- struct perf_sample *sample,
- struct machine *machine,
- struct thread *thread)
+static int process_sched_switch_event(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine)
{
- int this_cpu = sample->cpu;
- void *data = sample->raw_data;
- struct trace_switch_event switch_event;
-
- FILL_COMMON_FIELDS(switch_event, event, data);
-
- FILL_ARRAY(switch_event, prev_comm, event, data);
- FILL_FIELD(switch_event, prev_pid, event, data);
- FILL_FIELD(switch_event, prev_prio, event, data);
- FILL_FIELD(switch_event, prev_state, event, data);
- FILL_ARRAY(switch_event, next_comm, event, data);
- FILL_FIELD(switch_event, next_pid, event, data);
- FILL_FIELD(switch_event, next_prio, event, data);
+ struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
+ int this_cpu = sample->cpu, err = 0;
+ u32 prev_pid = perf_evsel__intval(evsel, sample, "prev_pid"),
+ next_pid = perf_evsel__intval(evsel, sample, "next_pid");
- if (curr_pid[this_cpu] != (u32)-1) {
+ if (sched->curr_pid[this_cpu] != (u32)-1) {
/*
* Are we trying to switch away a PID that is
* not current?
*/
- if (curr_pid[this_cpu] != switch_event.prev_pid)
- nr_context_switch_bugs++;
+ if (sched->curr_pid[this_cpu] != prev_pid)
+ sched->nr_context_switch_bugs++;
}
- if (trace_handler->switch_event)
- trace_handler->switch_event(&switch_event, machine, event,
- this_cpu, sample->time, thread);
- curr_pid[this_cpu] = switch_event.next_pid;
+ if (sched->tp_handler->switch_event)
+ err = sched->tp_handler->switch_event(sched, evsel, sample, machine);
+
+ sched->curr_pid[this_cpu] = next_pid;
+ return err;
}
-static void
-process_sched_runtime_event(struct perf_tool *tool __used,
- struct event_format *event,
- struct perf_sample *sample,
- struct machine *machine,
- struct thread *thread)
+static int process_sched_runtime_event(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine)
{
- void *data = sample->raw_data;
- struct trace_runtime_event runtime_event;
+ struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
- FILL_ARRAY(runtime_event, comm, event, data);
- FILL_FIELD(runtime_event, pid, event, data);
- FILL_FIELD(runtime_event, runtime, event, data);
- FILL_FIELD(runtime_event, vruntime, event, data);
+ if (sched->tp_handler->runtime_event)
+ return sched->tp_handler->runtime_event(sched, evsel, sample, machine);
- if (trace_handler->runtime_event)
- trace_handler->runtime_event(&runtime_event, machine, event,
- sample->cpu, sample->time, thread);
+ return 0;
}
-static void
-process_sched_fork_event(struct perf_tool *tool __used,
- struct event_format *event,
- struct perf_sample *sample,
- struct machine *machine __used,
- struct thread *thread)
+static int process_sched_fork_event(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine __maybe_unused)
{
- void *data = sample->raw_data;
- struct trace_fork_event fork_event;
-
- FILL_COMMON_FIELDS(fork_event, event, data);
+ struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
- FILL_ARRAY(fork_event, parent_comm, event, data);
- FILL_FIELD(fork_event, parent_pid, event, data);
- FILL_ARRAY(fork_event, child_comm, event, data);
- FILL_FIELD(fork_event, child_pid, event, data);
+ if (sched->tp_handler->fork_event)
+ return sched->tp_handler->fork_event(sched, evsel, sample);
- if (trace_handler->fork_event)
- trace_handler->fork_event(&fork_event, event,
- sample->cpu, sample->time, thread);
+ return 0;
}
-static void
-process_sched_exit_event(struct perf_tool *tool __used,
- struct event_format *event,
- struct perf_sample *sample __used,
- struct machine *machine __used,
- struct thread *thread __used)
+static int process_sched_exit_event(struct perf_tool *tool __maybe_unused,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
{
- if (verbose)
- printf("sched_exit event %p\n", event);
+ pr_debug("sched_exit event %p\n", evsel);
+ return 0;
}
-static void
-process_sched_migrate_task_event(struct perf_tool *tool __used,
- struct event_format *event,
- struct perf_sample *sample,
- struct machine *machine,
- struct thread *thread)
+static int process_sched_migrate_task_event(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine)
{
- void *data = sample->raw_data;
- struct trace_migrate_task_event migrate_task_event;
-
- FILL_COMMON_FIELDS(migrate_task_event, event, data);
+ struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
- FILL_ARRAY(migrate_task_event, comm, event, data);
- FILL_FIELD(migrate_task_event, pid, event, data);
- FILL_FIELD(migrate_task_event, prio, event, data);
- FILL_FIELD(migrate_task_event, cpu, event, data);
+ if (sched->tp_handler->migrate_task_event)
+ return sched->tp_handler->migrate_task_event(sched, evsel, sample, machine);
- if (trace_handler->migrate_task_event)
- trace_handler->migrate_task_event(&migrate_task_event, machine,
- event, sample->cpu,
- sample->time, thread);
+ return 0;
}
-typedef void (*tracepoint_handler)(struct perf_tool *tool, struct event_format *event,
- struct perf_sample *sample,
- struct machine *machine,
- struct thread *thread);
+typedef int (*tracepoint_handler)(struct perf_tool *tool,
+ struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ struct machine *machine);
-static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
- union perf_event *event __used,
+static int perf_sched__process_tracepoint_sample(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct machine *machine)
{
- struct perf_sched *sched = container_of(tool, struct perf_sched, tool);
- struct pevent *pevent = sched->session->pevent;
struct thread *thread = machine__findnew_thread(machine, sample->pid);
+ int err = 0;
if (thread == NULL) {
pr_debug("problem processing %s event, skipping it.\n",
@@ -1617,30 +1440,15 @@ static int perf_sched__process_tracepoint_sample(struct perf_tool *tool,
if (evsel->handler.func != NULL) {
tracepoint_handler f = evsel->handler.func;
-
- if (evsel->handler.data == NULL)
- evsel->handler.data = pevent_find_event(pevent,
- evsel->attr.config);
-
- f(tool, evsel->handler.data, sample, machine, thread);
+ err = f(tool, evsel, sample, machine);
}
- return 0;
+ return err;
}
-static struct perf_sched perf_sched = {
- .tool = {
- .sample = perf_sched__process_tracepoint_sample,
- .comm = perf_event__process_comm,
- .lost = perf_event__process_lost,
- .fork = perf_event__process_task,
- .ordered_samples = true,
- },
-};
-
-static void read_events(bool destroy, struct perf_session **psession)
+static int perf_sched__read_events(struct perf_sched *sched, bool destroy,
+ struct perf_session **psession)
{
- int err = -EINVAL;
const struct perf_evsel_str_handler handlers[] = {
{ "sched:sched_switch", process_sched_switch_event, },
{ "sched:sched_stat_runtime", process_sched_runtime_event, },
@@ -1652,24 +1460,25 @@ static void read_events(bool destroy, struct perf_session **psession)
};
struct perf_session *session;
- session = perf_session__new(input_name, O_RDONLY, 0, false,
- &perf_sched.tool);
- if (session == NULL)
- die("No Memory");
-
- perf_sched.session = session;
+ session = perf_session__new(sched->input_name, O_RDONLY, 0, false, &sched->tool);
+ if (session == NULL) {
+ pr_debug("No Memory for session\n");
+ return -1;
+ }
- err = perf_session__set_tracepoints_handlers(session, handlers);
- assert(err == 0);
+ if (perf_session__set_tracepoints_handlers(session, handlers))
+ goto out_delete;
if (perf_session__has_traces(session, "record -R")) {
- err = perf_session__process_events(session, &perf_sched.tool);
- if (err)
- die("Failed to process events, error %d", err);
+ int err = perf_session__process_events(session, &sched->tool);
+ if (err) {
+ pr_err("Failed to process events, error %d", err);
+ goto out_delete;
+ }
- nr_events = session->hists.stats.nr_events[0];
- nr_lost_events = session->hists.stats.total_lost;
- nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST];
+ sched->nr_events = session->hists.stats.nr_events[0];
+ sched->nr_lost_events = session->hists.stats.total_lost;
+ sched->nr_lost_chunks = session->hists.stats.nr_events[PERF_RECORD_LOST];
}
if (destroy)
@@ -1677,208 +1486,166 @@ static void read_events(bool destroy, struct perf_session **psession)
if (psession)
*psession = session;
+
+ return 0;
+
+out_delete:
+ perf_session__delete(session);
+ return -1;
}
-static void print_bad_events(void)
+static void print_bad_events(struct perf_sched *sched)
{
- if (nr_unordered_timestamps && nr_timestamps) {
+ if (sched->nr_unordered_timestamps && sched->nr_timestamps) {
printf(" INFO: %.3f%% unordered timestamps (%ld out of %ld)\n",
- (double)nr_unordered_timestamps/(double)nr_timestamps*100.0,
- nr_unordered_timestamps, nr_timestamps);
+ (double)sched->nr_unordered_timestamps/(double)sched->nr_timestamps*100.0,
+ sched->nr_unordered_timestamps, sched->nr_timestamps);
}
- if (nr_lost_events && nr_events) {
+ if (sched->nr_lost_events && sched->nr_events) {
printf(" INFO: %.3f%% lost events (%ld out of %ld, in %ld chunks)\n",
- (double)nr_lost_events/(double)nr_events*100.0,
- nr_lost_events, nr_events, nr_lost_chunks);
+ (double)sched->nr_lost_events/(double)sched->nr_events * 100.0,
+ sched->nr_lost_events, sched->nr_events, sched->nr_lost_chunks);
}
- if (nr_state_machine_bugs && nr_timestamps) {
+ if (sched->nr_state_machine_bugs && sched->nr_timestamps) {
printf(" INFO: %.3f%% state machine bugs (%ld out of %ld)",
- (double)nr_state_machine_bugs/(double)nr_timestamps*100.0,
- nr_state_machine_bugs, nr_timestamps);
- if (nr_lost_events)
+ (double)sched->nr_state_machine_bugs/(double)sched->nr_timestamps*100.0,
+ sched->nr_state_machine_bugs, sched->nr_timestamps);
+ if (sched->nr_lost_events)
printf(" (due to lost events?)");
printf("\n");
}
- if (nr_context_switch_bugs && nr_timestamps) {
+ if (sched->nr_context_switch_bugs && sched->nr_timestamps) {
printf(" INFO: %.3f%% context switch bugs (%ld out of %ld)",
- (double)nr_context_switch_bugs/(double)nr_timestamps*100.0,
- nr_context_switch_bugs, nr_timestamps);
- if (nr_lost_events)
+ (double)sched->nr_context_switch_bugs/(double)sched->nr_timestamps*100.0,
+ sched->nr_context_switch_bugs, sched->nr_timestamps);
+ if (sched->nr_lost_events)
printf(" (due to lost events?)");
printf("\n");
}
}
-static void __cmd_lat(void)
+static int perf_sched__lat(struct perf_sched *sched)
{
struct rb_node *next;
struct perf_session *session;
setup_pager();
- read_events(false, &session);
- sort_lat();
+ if (perf_sched__read_events(sched, false, &session))
+ return -1;
+ perf_sched__sort_lat(sched);
printf("\n ---------------------------------------------------------------------------------------------------------------\n");
printf(" Task | Runtime ms | Switches | Average delay ms | Maximum delay ms | Maximum delay at |\n");
printf(" ---------------------------------------------------------------------------------------------------------------\n");
- next = rb_first(&sorted_atom_root);
+ next = rb_first(&sched->sorted_atom_root);
while (next) {
struct work_atoms *work_list;
work_list = rb_entry(next, struct work_atoms, node);
- output_lat_thread(work_list);
+ output_lat_thread(sched, work_list);
next = rb_next(next);
}
printf(" -----------------------------------------------------------------------------------------\n");
printf(" TOTAL: |%11.3f ms |%9" PRIu64 " |\n",
- (double)all_runtime/1e6, all_count);
+ (double)sched->all_runtime / 1e6, sched->all_count);
printf(" ---------------------------------------------------\n");
- print_bad_events();
+ print_bad_events(sched);
printf("\n");
perf_session__delete(session);
+ return 0;
}
-static struct trace_sched_handler map_ops = {
- .wakeup_event = NULL,
- .switch_event = map_switch_event,
- .runtime_event = NULL,
- .fork_event = NULL,
-};
-
-static void __cmd_map(void)
+static int perf_sched__map(struct perf_sched *sched)
{
- max_cpu = sysconf(_SC_NPROCESSORS_CONF);
+ sched->max_cpu = sysconf(_SC_NPROCESSORS_CONF);
setup_pager();
- read_events(true, NULL);
- print_bad_events();
+ if (perf_sched__read_events(sched, true, NULL))
+ return -1;
+ print_bad_events(sched);
+ return 0;
}
-static void __cmd_replay(void)
+static int perf_sched__replay(struct perf_sched *sched)
{
unsigned long i;
- calibrate_run_measurement_overhead();
- calibrate_sleep_measurement_overhead();
+ calibrate_run_measurement_overhead(sched);
+ calibrate_sleep_measurement_overhead(sched);
- test_calibrations();
+ test_calibrations(sched);
- read_events(true, NULL);
+ if (perf_sched__read_events(sched, true, NULL))
+ return -1;
- printf("nr_run_events: %ld\n", nr_run_events);
- printf("nr_sleep_events: %ld\n", nr_sleep_events);
- printf("nr_wakeup_events: %ld\n", nr_wakeup_events);
+ printf("nr_run_events: %ld\n", sched->nr_run_events);
+ printf("nr_sleep_events: %ld\n", sched->nr_sleep_events);
+ printf("nr_wakeup_events: %ld\n", sched->nr_wakeup_events);
- if (targetless_wakeups)
- printf("target-less wakeups: %ld\n", targetless_wakeups);
- if (multitarget_wakeups)
- printf("multi-target wakeups: %ld\n", multitarget_wakeups);
- if (nr_run_events_optimized)
+ if (sched->targetless_wakeups)
+ printf("target-less wakeups: %ld\n", sched->targetless_wakeups);
+ if (sched->multitarget_wakeups)
+ printf("multi-target wakeups: %ld\n", sched->multitarget_wakeups);
+ if (sched->nr_run_events_optimized)
printf("run atoms optimized: %ld\n",
- nr_run_events_optimized);
+ sched->nr_run_events_optimized);
- print_task_traces();
- add_cross_task_wakeups();
+ print_task_traces(sched);
+ add_cross_task_wakeups(sched);
- create_tasks();
+ create_tasks(sched);
printf("------------------------------------------------------------\n");
- for (i = 0; i < replay_repeat; i++)
- run_one_test();
-}
-
-
-static const char * const sched_usage[] = {
- "perf sched [<options>] {record|latency|map|replay|script}",
- NULL
-};
-
-static const struct option sched_options[] = {
- OPT_STRING('i', "input", &input_name, "file",
- "input file name"),
- OPT_INCR('v', "verbose", &verbose,
- "be more verbose (show symbol address, etc)"),
- OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
- "dump raw trace in ASCII"),
- OPT_END()
-};
-
-static const char * const latency_usage[] = {
- "perf sched latency [<options>]",
- NULL
-};
-
-static const struct option latency_options[] = {
- OPT_STRING('s', "sort", &sort_order, "key[,key2...]",
- "sort by key(s): runtime, switch, avg, max"),
- OPT_INCR('v', "verbose", &verbose,
- "be more verbose (show symbol address, etc)"),
- OPT_INTEGER('C', "CPU", &profile_cpu,
- "CPU to profile on"),
- OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
- "dump raw trace in ASCII"),
- OPT_END()
-};
-
-static const char * const replay_usage[] = {
- "perf sched replay [<options>]",
- NULL
-};
+ for (i = 0; i < sched->replay_repeat; i++)
+ run_one_test(sched);
-static const struct option replay_options[] = {
- OPT_UINTEGER('r', "repeat", &replay_repeat,
- "repeat the workload replay N times (-1: infinite)"),
- OPT_INCR('v', "verbose", &verbose,
- "be more verbose (show symbol address, etc)"),
- OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
- "dump raw trace in ASCII"),
- OPT_END()
-};
+ return 0;
+}
-static void setup_sorting(void)
+static void setup_sorting(struct perf_sched *sched, const struct option *options,
+ const char * const usage_msg[])
{
- char *tmp, *tok, *str = strdup(sort_order);
+ char *tmp, *tok, *str = strdup(sched->sort_order);
for (tok = strtok_r(str, ", ", &tmp);
tok; tok = strtok_r(NULL, ", ", &tmp)) {
- if (sort_dimension__add(tok, &sort_list) < 0) {
+ if (sort_dimension__add(tok, &sched->sort_list) < 0) {
error("Unknown --sort key: `%s'", tok);
- usage_with_options(latency_usage, latency_options);
+ usage_with_options(usage_msg, options);
}
}
free(str);
- sort_dimension__add("pid", &cmp_pid);
+ sort_dimension__add("pid", &sched->cmp_pid);
}
-static const char *record_args[] = {
- "record",
- "-a",
- "-R",
- "-f",
- "-m", "1024",
- "-c", "1",
- "-e", "sched:sched_switch",
- "-e", "sched:sched_stat_wait",
- "-e", "sched:sched_stat_sleep",
- "-e", "sched:sched_stat_iowait",
- "-e", "sched:sched_stat_runtime",
- "-e", "sched:sched_process_exit",
- "-e", "sched:sched_process_fork",
- "-e", "sched:sched_wakeup",
- "-e", "sched:sched_migrate_task",
-};
-
static int __cmd_record(int argc, const char **argv)
{
unsigned int rec_argc, i, j;
const char **rec_argv;
+ const char * const record_args[] = {
+ "record",
+ "-a",
+ "-R",
+ "-f",
+ "-m", "1024",
+ "-c", "1",
+ "-e", "sched:sched_switch",
+ "-e", "sched:sched_stat_wait",
+ "-e", "sched:sched_stat_sleep",
+ "-e", "sched:sched_stat_iowait",
+ "-e", "sched:sched_stat_runtime",
+ "-e", "sched:sched_process_exit",
+ "-e", "sched:sched_process_fork",
+ "-e", "sched:sched_wakeup",
+ "-e", "sched:sched_migrate_task",
+ };
rec_argc = ARRAY_SIZE(record_args) + argc - 1;
rec_argv = calloc(rec_argc + 1, sizeof(char *));
@@ -1897,8 +1664,85 @@ static int __cmd_record(int argc, const char **argv)
return cmd_record(i, rec_argv, NULL);
}
-int cmd_sched(int argc, const char **argv, const char *prefix __used)
+int cmd_sched(int argc, const char **argv, const char *prefix __maybe_unused)
{
+ const char default_sort_order[] = "avg, max, switch, runtime";
+ struct perf_sched sched = {
+ .tool = {
+ .sample = perf_sched__process_tracepoint_sample,
+ .comm = perf_event__process_comm,
+ .lost = perf_event__process_lost,
+ .fork = perf_event__process_task,
+ .ordered_samples = true,
+ },
+ .cmp_pid = LIST_HEAD_INIT(sched.cmp_pid),
+ .sort_list = LIST_HEAD_INIT(sched.sort_list),
+ .start_work_mutex = PTHREAD_MUTEX_INITIALIZER,
+ .work_done_wait_mutex = PTHREAD_MUTEX_INITIALIZER,
+ .curr_pid = { [0 ... MAX_CPUS - 1] = -1 },
+ .sort_order = default_sort_order,
+ .replay_repeat = 10,
+ .profile_cpu = -1,
+ .next_shortname1 = 'A',
+ .next_shortname2 = '0',
+ };
+ const struct option latency_options[] = {
+ OPT_STRING('s', "sort", &sched.sort_order, "key[,key2...]",
+ "sort by key(s): runtime, switch, avg, max"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_INTEGER('C', "CPU", &sched.profile_cpu,
+ "CPU to profile on"),
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+ "dump raw trace in ASCII"),
+ OPT_END()
+ };
+ const struct option replay_options[] = {
+ OPT_UINTEGER('r', "repeat", &sched.replay_repeat,
+ "repeat the workload replay N times (-1: infinite)"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+ "dump raw trace in ASCII"),
+ OPT_END()
+ };
+ const struct option sched_options[] = {
+ OPT_STRING('i', "input", &sched.input_name, "file",
+ "input file name"),
+ OPT_INCR('v', "verbose", &verbose,
+ "be more verbose (show symbol address, etc)"),
+ OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
+ "dump raw trace in ASCII"),
+ OPT_END()
+ };
+ const char * const latency_usage[] = {
+ "perf sched latency [<options>]",
+ NULL
+ };
+ const char * const replay_usage[] = {
+ "perf sched replay [<options>]",
+ NULL
+ };
+ const char * const sched_usage[] = {
+ "perf sched [<options>] {record|latency|map|replay|script}",
+ NULL
+ };
+ struct trace_sched_handler lat_ops = {
+ .wakeup_event = latency_wakeup_event,
+ .switch_event = latency_switch_event,
+ .runtime_event = latency_runtime_event,
+ .fork_event = latency_fork_event,
+ .migrate_task_event = latency_migrate_task_event,
+ };
+ struct trace_sched_handler map_ops = {
+ .switch_event = map_switch_event,
+ };
+ struct trace_sched_handler replay_ops = {
+ .wakeup_event = replay_wakeup_event,
+ .switch_event = replay_switch_event,
+ .fork_event = replay_fork_event,
+ };
+
argc = parse_options(argc, argv, sched_options, sched_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc)
@@ -1914,26 +1758,26 @@ int cmd_sched(int argc, const char **argv, const char *prefix __used)
if (!strncmp(argv[0], "rec", 3)) {
return __cmd_record(argc, argv);
} else if (!strncmp(argv[0], "lat", 3)) {
- trace_handler = &lat_ops;
+ sched.tp_handler = &lat_ops;
if (argc > 1) {
argc = parse_options(argc, argv, latency_options, latency_usage, 0);
if (argc)
usage_with_options(latency_usage, latency_options);
}
- setup_sorting();
- __cmd_lat();
+ setup_sorting(&sched, latency_options, latency_usage);
+ return perf_sched__lat(&sched);
} else if (!strcmp(argv[0], "map")) {
- trace_handler = &map_ops;
- setup_sorting();
- __cmd_map();
+ sched.tp_handler = &map_ops;
+ setup_sorting(&sched, latency_options, latency_usage);
+ return perf_sched__map(&sched);
} else if (!strncmp(argv[0], "rep", 3)) {
- trace_handler = &replay_ops;
+ sched.tp_handler = &replay_ops;
if (argc) {
argc = parse_options(argc, argv, replay_options, replay_usage, 0);
if (argc)
usage_with_options(replay_usage, replay_options);
}
- __cmd_replay();
+ return perf_sched__replay(&sched);
} else {
usage_with_options(sched_usage, sched_options);
}
diff --git a/tools/perf/builtin-script.c b/tools/perf/builtin-script.c
index 1e60ab70b2b1..1be843aa1546 100644
--- a/tools/perf/builtin-script.c
+++ b/tools/perf/builtin-script.c
@@ -14,6 +14,7 @@
#include "util/util.h"
#include "util/evlist.h"
#include "util/evsel.h"
+#include "util/sort.h"
#include <linux/bitmap.h>
static char const *script_name;
@@ -28,11 +29,6 @@ static bool system_wide;
static const char *cpu_list;
static DECLARE_BITMAP(cpu_bitmap, MAX_NR_CPUS);
-struct perf_script {
- struct perf_tool tool;
- struct perf_session *session;
-};
-
enum perf_output_field {
PERF_OUTPUT_COMM = 1U << 0,
PERF_OUTPUT_TID = 1U << 1,
@@ -262,14 +258,11 @@ static int perf_session__check_output_opt(struct perf_session *session)
return 0;
}
-static void print_sample_start(struct pevent *pevent,
- struct perf_sample *sample,
+static void print_sample_start(struct perf_sample *sample,
struct thread *thread,
struct perf_evsel *evsel)
{
- int type;
struct perf_event_attr *attr = &evsel->attr;
- struct event_format *event;
const char *evname = NULL;
unsigned long secs;
unsigned long usecs;
@@ -307,20 +300,7 @@ static void print_sample_start(struct pevent *pevent,
}
if (PRINT_FIELD(EVNAME)) {
- if (attr->type == PERF_TYPE_TRACEPOINT) {
- /*
- * XXX Do we really need this here?
- * perf_evlist__set_tracepoint_names should have done
- * this already
- */
- type = trace_parse_common_type(pevent,
- sample->raw_data);
- event = pevent_find_event(pevent, type);
- if (event)
- evname = event->name;
- } else
- evname = perf_evsel__name(evsel);
-
+ evname = perf_evsel__name(evsel);
printf("%s: ", evname ? evname : "[unknown]");
}
}
@@ -401,7 +381,7 @@ static void print_sample_bts(union perf_event *event,
printf(" ");
else
printf("\n");
- perf_event__print_ip(event, sample, machine,
+ perf_evsel__print_ip(evsel, event, sample, machine,
PRINT_FIELD(SYM), PRINT_FIELD(DSO),
PRINT_FIELD(SYMOFFSET));
}
@@ -415,19 +395,17 @@ static void print_sample_bts(union perf_event *event,
printf("\n");
}
-static void process_event(union perf_event *event __unused,
- struct pevent *pevent,
- struct perf_sample *sample,
- struct perf_evsel *evsel,
- struct machine *machine,
- struct thread *thread)
+static void process_event(union perf_event *event, struct perf_sample *sample,
+ struct perf_evsel *evsel, struct machine *machine,
+ struct addr_location *al)
{
struct perf_event_attr *attr = &evsel->attr;
+ struct thread *thread = al->thread;
if (output[attr->type].fields == 0)
return;
- print_sample_start(pevent, sample, thread, evsel);
+ print_sample_start(sample, thread, evsel);
if (is_bts_event(attr)) {
print_sample_bts(event, sample, evsel, machine, thread);
@@ -435,9 +413,8 @@ static void process_event(union perf_event *event __unused,
}
if (PRINT_FIELD(TRACE))
- print_trace_event(pevent, sample->cpu, sample->raw_data,
- sample->raw_size);
-
+ event_format__print(evsel->tp_format, sample->cpu,
+ sample->raw_data, sample->raw_size);
if (PRINT_FIELD(ADDR))
print_sample_addr(event, sample, machine, thread, attr);
@@ -446,7 +423,7 @@ static void process_event(union perf_event *event __unused,
printf(" ");
else
printf("\n");
- perf_event__print_ip(event, sample, machine,
+ perf_evsel__print_ip(evsel, event, sample, machine,
PRINT_FIELD(SYM), PRINT_FIELD(DSO),
PRINT_FIELD(SYMOFFSET));
}
@@ -454,9 +431,9 @@ static void process_event(union perf_event *event __unused,
printf("\n");
}
-static int default_start_script(const char *script __unused,
- int argc __unused,
- const char **argv __unused)
+static int default_start_script(const char *script __maybe_unused,
+ int argc __maybe_unused,
+ const char **argv __maybe_unused)
{
return 0;
}
@@ -466,8 +443,8 @@ static int default_stop_script(void)
return 0;
}
-static int default_generate_script(struct pevent *pevent __unused,
- const char *outfile __unused)
+static int default_generate_script(struct pevent *pevent __maybe_unused,
+ const char *outfile __maybe_unused)
{
return 0;
}
@@ -498,14 +475,13 @@ static int cleanup_scripting(void)
static const char *input_name;
-static int process_sample_event(struct perf_tool *tool __used,
+static int process_sample_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct machine *machine)
{
struct addr_location al;
- struct perf_script *scr = container_of(tool, struct perf_script, tool);
struct thread *thread = machine__findnew_thread(machine, event->ip.tid);
if (thread == NULL) {
@@ -537,32 +513,29 @@ static int process_sample_event(struct perf_tool *tool __used,
if (cpu_list && !test_bit(sample->cpu, cpu_bitmap))
return 0;
- scripting_ops->process_event(event, scr->session->pevent,
- sample, evsel, machine, thread);
+ scripting_ops->process_event(event, sample, evsel, machine, &al);
evsel->hists.stats.total_period += sample->period;
return 0;
}
-static struct perf_script perf_script = {
- .tool = {
- .sample = process_sample_event,
- .mmap = perf_event__process_mmap,
- .comm = perf_event__process_comm,
- .exit = perf_event__process_task,
- .fork = perf_event__process_task,
- .attr = perf_event__process_attr,
- .event_type = perf_event__process_event_type,
- .tracing_data = perf_event__process_tracing_data,
- .build_id = perf_event__process_build_id,
- .ordered_samples = true,
- .ordering_requires_timestamps = true,
- },
+static struct perf_tool perf_script = {
+ .sample = process_sample_event,
+ .mmap = perf_event__process_mmap,
+ .comm = perf_event__process_comm,
+ .exit = perf_event__process_task,
+ .fork = perf_event__process_task,
+ .attr = perf_event__process_attr,
+ .event_type = perf_event__process_event_type,
+ .tracing_data = perf_event__process_tracing_data,
+ .build_id = perf_event__process_build_id,
+ .ordered_samples = true,
+ .ordering_requires_timestamps = true,
};
extern volatile int session_done;
-static void sig_handler(int sig __unused)
+static void sig_handler(int sig __maybe_unused)
{
session_done = 1;
}
@@ -573,7 +546,7 @@ static int __cmd_script(struct perf_session *session)
signal(SIGINT, sig_handler);
- ret = perf_session__process_events(session, &perf_script.tool);
+ ret = perf_session__process_events(session, &perf_script);
if (debug_mode)
pr_err("Misordered timestamps: %" PRIu64 "\n", nr_unordered);
@@ -672,8 +645,8 @@ static void list_available_languages(void)
fprintf(stderr, "\n");
}
-static int parse_scriptname(const struct option *opt __used,
- const char *str, int unset __used)
+static int parse_scriptname(const struct option *opt __maybe_unused,
+ const char *str, int unset __maybe_unused)
{
char spec[PATH_MAX];
const char *script, *ext;
@@ -718,8 +691,8 @@ static int parse_scriptname(const struct option *opt __used,
return 0;
}
-static int parse_output_fields(const struct option *opt __used,
- const char *arg, int unset __used)
+static int parse_output_fields(const struct option *opt __maybe_unused,
+ const char *arg, int unset __maybe_unused)
{
char *tok;
int i, imax = sizeof(all_output_options) / sizeof(struct output_option);
@@ -1010,8 +983,9 @@ static char *get_script_root(struct dirent *script_dirent, const char *suffix)
return script_root;
}
-static int list_available_scripts(const struct option *opt __used,
- const char *s __used, int unset __used)
+static int list_available_scripts(const struct option *opt __maybe_unused,
+ const char *s __maybe_unused,
+ int unset __maybe_unused)
{
struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
char scripts_path[MAXPATHLEN];
@@ -1058,6 +1032,61 @@ static int list_available_scripts(const struct option *opt __used,
exit(0);
}
+/*
+ * Return -1 if none is found, otherwise the actual scripts number.
+ *
+ * Currently the only user of this function is the script browser, which
+ * will list all statically runnable scripts, select one, execute it and
+ * show the output in a perf browser.
+ */
+int find_scripts(char **scripts_array, char **scripts_path_array)
+{
+ struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
+ char scripts_path[MAXPATHLEN];
+ DIR *scripts_dir, *lang_dir;
+ char lang_path[MAXPATHLEN];
+ char *temp;
+ int i = 0;
+
+ snprintf(scripts_path, MAXPATHLEN, "%s/scripts", perf_exec_path());
+
+ scripts_dir = opendir(scripts_path);
+ if (!scripts_dir)
+ return -1;
+
+ for_each_lang(scripts_path, scripts_dir, lang_dirent, lang_next) {
+ snprintf(lang_path, MAXPATHLEN, "%s/%s", scripts_path,
+ lang_dirent.d_name);
+#ifdef NO_LIBPERL
+ if (strstr(lang_path, "perl"))
+ continue;
+#endif
+#ifdef NO_LIBPYTHON
+ if (strstr(lang_path, "python"))
+ continue;
+#endif
+
+ lang_dir = opendir(lang_path);
+ if (!lang_dir)
+ continue;
+
+ for_each_script(lang_path, lang_dir, script_dirent, script_next) {
+ /* Skip those real time scripts: xxxtop.p[yl] */
+ if (strstr(script_dirent.d_name, "top."))
+ continue;
+ sprintf(scripts_path_array[i], "%s/%s", lang_path,
+ script_dirent.d_name);
+ temp = strchr(script_dirent.d_name, '.');
+ snprintf(scripts_array[i],
+ (temp - script_dirent.d_name) + 1,
+ "%s", script_dirent.d_name);
+ i++;
+ }
+ }
+
+ return i;
+}
+
static char *get_script_path(const char *script_root, const char *suffix)
{
struct dirent *script_next, *lang_next, script_dirent, lang_dirent;
@@ -1170,6 +1199,8 @@ static const struct option options[] = {
parse_output_fields),
OPT_BOOLEAN('a', "all-cpus", &system_wide,
"system-wide collection from all CPUs"),
+ OPT_STRING('S', "symbols", &symbol_conf.sym_list_str, "symbol[,symbol...]",
+ "only consider these symbols"),
OPT_STRING('C', "cpu", &cpu_list, "cpu", "list of cpus to profile"),
OPT_STRING('c', "comms", &symbol_conf.comm_list_str, "comm[,comm...]",
"only display events for these comms"),
@@ -1181,21 +1212,26 @@ static const struct option options[] = {
OPT_END()
};
-static bool have_cmd(int argc, const char **argv)
+static int have_cmd(int argc, const char **argv)
{
char **__argv = malloc(sizeof(const char *) * argc);
- if (!__argv)
- die("malloc");
+ if (!__argv) {
+ pr_err("malloc failed\n");
+ return -1;
+ }
+
memcpy(__argv, argv, sizeof(const char *) * argc);
argc = parse_options(argc, (const char **)__argv, record_options,
NULL, PARSE_OPT_STOP_AT_NON_OPTION);
free(__argv);
- return argc != 0;
+ system_wide = (argc == 0);
+
+ return 0;
}
-int cmd_script(int argc, const char **argv, const char *prefix __used)
+int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
{
char *rec_script_path = NULL;
char *rep_script_path = NULL;
@@ -1259,13 +1295,13 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
if (pipe(live_pipe) < 0) {
perror("failed to create pipe");
- exit(-1);
+ return -1;
}
pid = fork();
if (pid < 0) {
perror("failed to fork");
- exit(-1);
+ return -1;
}
if (!pid) {
@@ -1277,13 +1313,18 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
if (is_top_script(argv[0])) {
system_wide = true;
} else if (!system_wide) {
- system_wide = !have_cmd(argc - rep_args,
- &argv[rep_args]);
+ if (have_cmd(argc - rep_args, &argv[rep_args]) != 0) {
+ err = -1;
+ goto out;
+ }
}
__argv = malloc((argc + 6) * sizeof(const char *));
- if (!__argv)
- die("malloc");
+ if (!__argv) {
+ pr_err("malloc failed\n");
+ err = -ENOMEM;
+ goto out;
+ }
__argv[j++] = "/bin/sh";
__argv[j++] = rec_script_path;
@@ -1305,8 +1346,12 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
close(live_pipe[1]);
__argv = malloc((argc + 4) * sizeof(const char *));
- if (!__argv)
- die("malloc");
+ if (!__argv) {
+ pr_err("malloc failed\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
j = 0;
__argv[j++] = "/bin/sh";
__argv[j++] = rep_script_path;
@@ -1331,12 +1376,20 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
if (!rec_script_path)
system_wide = false;
- else if (!system_wide)
- system_wide = !have_cmd(argc - 1, &argv[1]);
+ else if (!system_wide) {
+ if (have_cmd(argc - 1, &argv[1]) != 0) {
+ err = -1;
+ goto out;
+ }
+ }
__argv = malloc((argc + 2) * sizeof(const char *));
- if (!__argv)
- die("malloc");
+ if (!__argv) {
+ pr_err("malloc failed\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
__argv[j++] = "/bin/sh";
__argv[j++] = script_path;
if (system_wide)
@@ -1356,12 +1409,10 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
setup_pager();
session = perf_session__new(input_name, O_RDONLY, 0, false,
- &perf_script.tool);
+ &perf_script);
if (session == NULL)
return -ENOMEM;
- perf_script.session = session;
-
if (cpu_list) {
if (perf_session__cpu_bitmap(session, cpu_list, cpu_bitmap))
return -1;
@@ -1387,18 +1438,18 @@ int cmd_script(int argc, const char **argv, const char *prefix __used)
input = open(session->filename, O_RDONLY); /* input_name */
if (input < 0) {
perror("failed to open file");
- exit(-1);
+ return -1;
}
err = fstat(input, &perf_stat);
if (err < 0) {
perror("failed to stat file");
- exit(-1);
+ return -1;
}
if (!perf_stat.st_size) {
fprintf(stderr, "zero-sized file, nothing to do!\n");
- exit(0);
+ return 0;
}
scripting_ops = script_spec__lookup(generate_script_lang);
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index 861f0aec77ae..e8cd4d81b06e 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -51,13 +51,13 @@
#include "util/evsel.h"
#include "util/debug.h"
#include "util/color.h"
+#include "util/stat.h"
#include "util/header.h"
#include "util/cpumap.h"
#include "util/thread.h"
#include "util/thread_map.h"
#include <sys/prctl.h>
-#include <math.h>
#include <locale.h>
#define DEFAULT_SEPARATOR " "
@@ -199,11 +199,6 @@ static int output_fd;
static volatile int done = 0;
-struct stats
-{
- double n, mean, M2;
-};
-
struct perf_stat {
struct stats res_stats[3];
};
@@ -220,48 +215,14 @@ static void perf_evsel__free_stat_priv(struct perf_evsel *evsel)
evsel->priv = NULL;
}
-static void update_stats(struct stats *stats, u64 val)
-{
- double delta;
-
- stats->n++;
- delta = val - stats->mean;
- stats->mean += delta / stats->n;
- stats->M2 += delta*(val - stats->mean);
-}
-
-static double avg_stats(struct stats *stats)
+static inline struct cpu_map *perf_evsel__cpus(struct perf_evsel *evsel)
{
- return stats->mean;
+ return (evsel->cpus && !target.cpu_list) ? evsel->cpus : evsel_list->cpus;
}
-/*
- * http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
- *
- * (\Sum n_i^2) - ((\Sum n_i)^2)/n
- * s^2 = -------------------------------
- * n - 1
- *
- * http://en.wikipedia.org/wiki/Stddev
- *
- * The std dev of the mean is related to the std dev by:
- *
- * s
- * s_mean = -------
- * sqrt(n)
- *
- */
-static double stddev_stats(struct stats *stats)
+static inline int perf_evsel__nr_cpus(struct perf_evsel *evsel)
{
- double variance, variance_mean;
-
- if (!stats->n)
- return 0.0;
-
- variance = stats->M2 / (stats->n - 1);
- variance_mean = variance / stats->n;
-
- return sqrt(variance_mean);
+ return perf_evsel__cpus(evsel)->nr;
}
static struct stats runtime_nsecs_stats[MAX_NR_CPUS];
@@ -281,13 +242,9 @@ static int create_perf_stat_counter(struct perf_evsel *evsel,
struct perf_evsel *first)
{
struct perf_event_attr *attr = &evsel->attr;
- struct xyarray *group_fd = NULL;
bool exclude_guest_missing = false;
int ret;
- if (group && evsel != first)
- group_fd = first->fd;
-
if (scale)
attr->read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
PERF_FORMAT_TOTAL_TIME_RUNNING;
@@ -299,8 +256,7 @@ retry:
evsel->attr.exclude_guest = evsel->attr.exclude_host = 0;
if (perf_target__has_cpu(&target)) {
- ret = perf_evsel__open_per_cpu(evsel, evsel_list->cpus,
- group, group_fd);
+ ret = perf_evsel__open_per_cpu(evsel, perf_evsel__cpus(evsel));
if (ret)
goto check_ret;
return 0;
@@ -311,8 +267,7 @@ retry:
attr->enable_on_exec = 1;
}
- ret = perf_evsel__open_per_thread(evsel, evsel_list->threads,
- group, group_fd);
+ ret = perf_evsel__open_per_thread(evsel, evsel_list->threads);
if (!ret)
return 0;
/* fall through */
@@ -382,7 +337,7 @@ static int read_counter_aggr(struct perf_evsel *counter)
u64 *count = counter->counts->aggr.values;
int i;
- if (__perf_evsel__read(counter, evsel_list->cpus->nr,
+ if (__perf_evsel__read(counter, perf_evsel__nr_cpus(counter),
evsel_list->threads->nr, scale) < 0)
return -1;
@@ -411,7 +366,7 @@ static int read_counter(struct perf_evsel *counter)
u64 *count;
int cpu;
- for (cpu = 0; cpu < evsel_list->cpus->nr; cpu++) {
+ for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
if (__perf_evsel__read_on_cpu(counter, cpu, 0, scale) < 0)
return -1;
@@ -423,7 +378,7 @@ static int read_counter(struct perf_evsel *counter)
return 0;
}
-static int run_perf_stat(int argc __used, const char **argv)
+static int run_perf_stat(int argc __maybe_unused, const char **argv)
{
unsigned long long t0, t1;
struct perf_evsel *counter, *first;
@@ -434,7 +389,7 @@ static int run_perf_stat(int argc __used, const char **argv)
if (forks && (pipe(child_ready_pipe) < 0 || pipe(go_pipe) < 0)) {
perror("failed to create pipes");
- exit(1);
+ return -1;
}
if (forks) {
@@ -483,7 +438,10 @@ static int run_perf_stat(int argc __used, const char **argv)
close(child_ready_pipe[0]);
}
- first = list_entry(evsel_list->entries.next, struct perf_evsel, node);
+ if (group)
+ perf_evlist__set_leader(evsel_list);
+
+ first = perf_evlist__first(evsel_list);
list_for_each_entry(counter, &evsel_list->entries, node) {
if (create_perf_stat_counter(counter, first) < 0) {
@@ -513,13 +471,14 @@ static int run_perf_stat(int argc __used, const char **argv)
}
if (child_pid != -1)
kill(child_pid, SIGTERM);
- die("Not all events could be opened.\n");
+
+ pr_err("Not all events could be opened.\n");
return -1;
}
counter->supported = true;
}
- if (perf_evlist__set_filters(evsel_list)) {
+ if (perf_evlist__apply_filters(evsel_list)) {
error("failed to set filter with %d (%s)\n", errno,
strerror(errno));
return -1;
@@ -546,12 +505,12 @@ static int run_perf_stat(int argc __used, const char **argv)
if (no_aggr) {
list_for_each_entry(counter, &evsel_list->entries, node) {
read_counter(counter);
- perf_evsel__close_fd(counter, evsel_list->cpus->nr, 1);
+ perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter), 1);
}
} else {
list_for_each_entry(counter, &evsel_list->entries, node) {
read_counter_aggr(counter);
- perf_evsel__close_fd(counter, evsel_list->cpus->nr,
+ perf_evsel__close_fd(counter, perf_evsel__nr_cpus(counter),
evsel_list->threads->nr);
}
}
@@ -561,10 +520,7 @@ static int run_perf_stat(int argc __used, const char **argv)
static void print_noise_pct(double total, double avg)
{
- double pct = 0.0;
-
- if (avg)
- pct = 100.0*total/avg;
+ double pct = rel_stddev_stats(total, avg);
if (csv_output)
fprintf(output, "%s%.2f%%", csv_sep, pct);
@@ -592,7 +548,7 @@ static void nsec_printout(int cpu, struct perf_evsel *evsel, double avg)
if (no_aggr)
sprintf(cpustr, "CPU%*d%s",
csv_output ? 0 : -4,
- evsel_list->cpus->map[cpu], csv_sep);
+ perf_evsel__cpus(evsel)->map[cpu], csv_sep);
fprintf(output, fmt, cpustr, msecs, csv_sep, perf_evsel__name(evsel));
@@ -636,7 +592,9 @@ static const char *get_ratio_color(enum grc_type type, double ratio)
return color;
}
-static void print_stalled_cycles_frontend(int cpu, struct perf_evsel *evsel __used, double avg)
+static void print_stalled_cycles_frontend(int cpu,
+ struct perf_evsel *evsel
+ __maybe_unused, double avg)
{
double total, ratio = 0.0;
const char *color;
@@ -653,7 +611,9 @@ static void print_stalled_cycles_frontend(int cpu, struct perf_evsel *evsel __us
fprintf(output, " frontend cycles idle ");
}
-static void print_stalled_cycles_backend(int cpu, struct perf_evsel *evsel __used, double avg)
+static void print_stalled_cycles_backend(int cpu,
+ struct perf_evsel *evsel
+ __maybe_unused, double avg)
{
double total, ratio = 0.0;
const char *color;
@@ -670,7 +630,9 @@ static void print_stalled_cycles_backend(int cpu, struct perf_evsel *evsel __use
fprintf(output, " backend cycles idle ");
}
-static void print_branch_misses(int cpu, struct perf_evsel *evsel __used, double avg)
+static void print_branch_misses(int cpu,
+ struct perf_evsel *evsel __maybe_unused,
+ double avg)
{
double total, ratio = 0.0;
const char *color;
@@ -687,7 +649,9 @@ static void print_branch_misses(int cpu, struct perf_evsel *evsel __used, double
fprintf(output, " of all branches ");
}
-static void print_l1_dcache_misses(int cpu, struct perf_evsel *evsel __used, double avg)
+static void print_l1_dcache_misses(int cpu,
+ struct perf_evsel *evsel __maybe_unused,
+ double avg)
{
double total, ratio = 0.0;
const char *color;
@@ -704,7 +668,9 @@ static void print_l1_dcache_misses(int cpu, struct perf_evsel *evsel __used, dou
fprintf(output, " of all L1-dcache hits ");
}
-static void print_l1_icache_misses(int cpu, struct perf_evsel *evsel __used, double avg)
+static void print_l1_icache_misses(int cpu,
+ struct perf_evsel *evsel __maybe_unused,
+ double avg)
{
double total, ratio = 0.0;
const char *color;
@@ -721,7 +687,9 @@ static void print_l1_icache_misses(int cpu, struct perf_evsel *evsel __used, dou
fprintf(output, " of all L1-icache hits ");
}
-static void print_dtlb_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg)
+static void print_dtlb_cache_misses(int cpu,
+ struct perf_evsel *evsel __maybe_unused,
+ double avg)
{
double total, ratio = 0.0;
const char *color;
@@ -738,7 +706,9 @@ static void print_dtlb_cache_misses(int cpu, struct perf_evsel *evsel __used, do
fprintf(output, " of all dTLB cache hits ");
}
-static void print_itlb_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg)
+static void print_itlb_cache_misses(int cpu,
+ struct perf_evsel *evsel __maybe_unused,
+ double avg)
{
double total, ratio = 0.0;
const char *color;
@@ -755,7 +725,9 @@ static void print_itlb_cache_misses(int cpu, struct perf_evsel *evsel __used, do
fprintf(output, " of all iTLB cache hits ");
}
-static void print_ll_cache_misses(int cpu, struct perf_evsel *evsel __used, double avg)
+static void print_ll_cache_misses(int cpu,
+ struct perf_evsel *evsel __maybe_unused,
+ double avg)
{
double total, ratio = 0.0;
const char *color;
@@ -788,7 +760,7 @@ static void abs_printout(int cpu, struct perf_evsel *evsel, double avg)
if (no_aggr)
sprintf(cpustr, "CPU%*d%s",
csv_output ? 0 : -4,
- evsel_list->cpus->map[cpu], csv_sep);
+ perf_evsel__cpus(evsel)->map[cpu], csv_sep);
else
cpu = 0;
@@ -949,14 +921,14 @@ static void print_counter(struct perf_evsel *counter)
u64 ena, run, val;
int cpu;
- for (cpu = 0; cpu < evsel_list->cpus->nr; cpu++) {
+ for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
val = counter->counts->cpu[cpu].val;
ena = counter->counts->cpu[cpu].ena;
run = counter->counts->cpu[cpu].run;
if (run == 0 || ena == 0) {
fprintf(output, "CPU%*d%s%*s%s%*s",
csv_output ? 0 : -4,
- evsel_list->cpus->map[cpu], csv_sep,
+ perf_evsel__cpus(counter)->map[cpu], csv_sep,
csv_output ? 0 : 18,
counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
csv_sep,
@@ -1061,8 +1033,8 @@ static const char * const stat_usage[] = {
NULL
};
-static int stat__set_big_num(const struct option *opt __used,
- const char *s __used, int unset)
+static int stat__set_big_num(const struct option *opt __maybe_unused,
+ const char *s __maybe_unused, int unset)
{
big_num_opt = unset ? 0 : 1;
return 0;
@@ -1156,7 +1128,7 @@ static int add_default_attributes(void)
return perf_evlist__add_default_attrs(evsel_list, very_very_detailed_attrs);
}
-int cmd_stat(int argc, const char **argv, const char *prefix __used)
+int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
{
struct perf_evsel *pos;
int status = -ENOMEM;
@@ -1192,7 +1164,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
output = fopen(output_name, mode);
if (!output) {
perror("failed to create output file");
- exit(-1);
+ return -1;
}
clock_gettime(CLOCK_REALTIME, &tm);
fprintf(output, "# started on %s\n", ctime(&tm.tv_sec));
@@ -1255,7 +1227,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
list_for_each_entry(pos, &evsel_list->entries, node) {
if (perf_evsel__alloc_stat_priv(pos) < 0 ||
- perf_evsel__alloc_counts(pos, evsel_list->cpus->nr) < 0)
+ perf_evsel__alloc_counts(pos, perf_evsel__nr_cpus(pos)) < 0)
goto out_free_fd;
}
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
index 1d592f5cbea9..484f26cc0c00 100644
--- a/tools/perf/builtin-test.c
+++ b/tools/perf/builtin-test.c
@@ -14,11 +14,13 @@
#include "util/symbol.h"
#include "util/thread_map.h"
#include "util/pmu.h"
+#include "event-parse.h"
#include "../../include/linux/hw_breakpoint.h"
#include <sys/mman.h>
-static int vmlinux_matches_kallsyms_filter(struct map *map __used, struct symbol *sym)
+static int vmlinux_matches_kallsyms_filter(struct map *map __maybe_unused,
+ struct symbol *sym)
{
bool *visited = symbol__priv(sym);
*visited = true;
@@ -294,7 +296,7 @@ static int test__open_syscall_event(void)
goto out_thread_map_delete;
}
- if (perf_evsel__open_per_thread(evsel, threads, false, NULL) < 0) {
+ if (perf_evsel__open_per_thread(evsel, threads) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno));
@@ -369,7 +371,7 @@ static int test__open_syscall_event_on_all_cpus(void)
goto out_thread_map_delete;
}
- if (perf_evsel__open(evsel, cpus, threads, false, NULL) < 0) {
+ if (perf_evsel__open(evsel, cpus, threads) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno));
@@ -533,7 +535,7 @@ static int test__basic_mmap(void)
perf_evlist__add(evlist, evsels[i]);
- if (perf_evsel__open(evsels[i], cpus, threads, false, NULL) < 0) {
+ if (perf_evsel__open(evsels[i], cpus, threads) < 0) {
pr_debug("failed to open counter: %s, "
"tweak /proc/sys/kernel/perf_event_paranoid?\n",
strerror(errno));
@@ -562,7 +564,7 @@ static int test__basic_mmap(void)
goto out_munmap;
}
- err = perf_evlist__parse_sample(evlist, event, &sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &sample);
if (err) {
pr_err("Can't parse sample, err = %d\n", err);
goto out_munmap;
@@ -710,7 +712,7 @@ static int test__PERF_RECORD(void)
/*
* Config the evsels, setting attr->comm on the first one, etc.
*/
- evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
+ evsel = perf_evlist__first(evlist);
evsel->attr.sample_type |= PERF_SAMPLE_CPU;
evsel->attr.sample_type |= PERF_SAMPLE_TID;
evsel->attr.sample_type |= PERF_SAMPLE_TIME;
@@ -737,7 +739,7 @@ static int test__PERF_RECORD(void)
* Call sys_perf_event_open on all the fds on all the evsels,
* grouping them if asked to.
*/
- err = perf_evlist__open(evlist, opts.group);
+ err = perf_evlist__open(evlist);
if (err < 0) {
pr_debug("perf_evlist__open: %s\n", strerror(errno));
goto out_delete_evlist;
@@ -779,7 +781,7 @@ static int test__PERF_RECORD(void)
if (type < PERF_RECORD_MAX)
nr_events[type]++;
- err = perf_evlist__parse_sample(evlist, event, &sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &sample);
if (err < 0) {
if (verbose)
perf_event__fprintf(event, stderr);
@@ -996,7 +998,9 @@ static u64 mmap_read_self(void *addr)
/*
* If the RDPMC instruction faults then signal this back to the test parent task:
*/
-static void segfault_handler(int sig __used, siginfo_t *info __used, void *uc __used)
+static void segfault_handler(int sig __maybe_unused,
+ siginfo_t *info __maybe_unused,
+ void *uc __maybe_unused)
{
exit(-1);
}
@@ -1023,14 +1027,16 @@ static int __test__rdpmc(void)
fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
if (fd < 0) {
- die("Error: sys_perf_event_open() syscall returned "
- "with %d (%s)\n", fd, strerror(errno));
+ pr_err("Error: sys_perf_event_open() syscall returned "
+ "with %d (%s)\n", fd, strerror(errno));
+ return -1;
}
addr = mmap(NULL, page_size, PROT_READ, MAP_SHARED, fd, 0);
if (addr == (void *)(-1)) {
- die("Error: mmap() syscall returned "
- "with (%s)\n", strerror(errno));
+ pr_err("Error: mmap() syscall returned with (%s)\n",
+ strerror(errno));
+ goto out_close;
}
for (n = 0; n < 6; n++) {
@@ -1051,9 +1057,9 @@ static int __test__rdpmc(void)
}
munmap(addr, page_size);
- close(fd);
-
pr_debug(" ");
+out_close:
+ close(fd);
if (!delta_sum)
return -1;
@@ -1092,6 +1098,309 @@ static int test__perf_pmu(void)
return perf_pmu__test();
}
+static int perf_evsel__roundtrip_cache_name_test(void)
+{
+ char name[128];
+ int type, op, err = 0, ret = 0, i, idx;
+ struct perf_evsel *evsel;
+ struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
+
+ if (evlist == NULL)
+ return -ENOMEM;
+
+ for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
+ for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
+ /* skip invalid cache type */
+ if (!perf_evsel__is_cache_op_valid(type, op))
+ continue;
+
+ for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
+ __perf_evsel__hw_cache_type_op_res_name(type, op, i,
+ name, sizeof(name));
+ err = parse_events(evlist, name, 0);
+ if (err)
+ ret = err;
+ }
+ }
+ }
+
+ idx = 0;
+ evsel = perf_evlist__first(evlist);
+
+ for (type = 0; type < PERF_COUNT_HW_CACHE_MAX; type++) {
+ for (op = 0; op < PERF_COUNT_HW_CACHE_OP_MAX; op++) {
+ /* skip invalid cache type */
+ if (!perf_evsel__is_cache_op_valid(type, op))
+ continue;
+
+ for (i = 0; i < PERF_COUNT_HW_CACHE_RESULT_MAX; i++) {
+ __perf_evsel__hw_cache_type_op_res_name(type, op, i,
+ name, sizeof(name));
+ if (evsel->idx != idx)
+ continue;
+
+ ++idx;
+
+ if (strcmp(perf_evsel__name(evsel), name)) {
+ pr_debug("%s != %s\n", perf_evsel__name(evsel), name);
+ ret = -1;
+ }
+
+ evsel = perf_evsel__next(evsel);
+ }
+ }
+ }
+
+ perf_evlist__delete(evlist);
+ return ret;
+}
+
+static int __perf_evsel__name_array_test(const char *names[], int nr_names)
+{
+ int i, err;
+ struct perf_evsel *evsel;
+ struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
+
+ if (evlist == NULL)
+ return -ENOMEM;
+
+ for (i = 0; i < nr_names; ++i) {
+ err = parse_events(evlist, names[i], 0);
+ if (err) {
+ pr_debug("failed to parse event '%s', err %d\n",
+ names[i], err);
+ goto out_delete_evlist;
+ }
+ }
+
+ err = 0;
+ list_for_each_entry(evsel, &evlist->entries, node) {
+ if (strcmp(perf_evsel__name(evsel), names[evsel->idx])) {
+ --err;
+ pr_debug("%s != %s\n", perf_evsel__name(evsel), names[evsel->idx]);
+ }
+ }
+
+out_delete_evlist:
+ perf_evlist__delete(evlist);
+ return err;
+}
+
+#define perf_evsel__name_array_test(names) \
+ __perf_evsel__name_array_test(names, ARRAY_SIZE(names))
+
+static int perf_evsel__roundtrip_name_test(void)
+{
+ int err = 0, ret = 0;
+
+ err = perf_evsel__name_array_test(perf_evsel__hw_names);
+ if (err)
+ ret = err;
+
+ err = perf_evsel__name_array_test(perf_evsel__sw_names);
+ if (err)
+ ret = err;
+
+ err = perf_evsel__roundtrip_cache_name_test();
+ if (err)
+ ret = err;
+
+ return ret;
+}
+
+static int perf_evsel__test_field(struct perf_evsel *evsel, const char *name,
+ int size, bool should_be_signed)
+{
+ struct format_field *field = perf_evsel__field(evsel, name);
+ int is_signed;
+ int ret = 0;
+
+ if (field == NULL) {
+ pr_debug("%s: \"%s\" field not found!\n", evsel->name, name);
+ return -1;
+ }
+
+ is_signed = !!(field->flags | FIELD_IS_SIGNED);
+ if (should_be_signed && !is_signed) {
+ pr_debug("%s: \"%s\" signedness(%d) is wrong, should be %d\n",
+ evsel->name, name, is_signed, should_be_signed);
+ ret = -1;
+ }
+
+ if (field->size != size) {
+ pr_debug("%s: \"%s\" size (%d) should be %d!\n",
+ evsel->name, name, field->size, size);
+ ret = -1;
+ }
+
+ return ret;
+}
+
+static int perf_evsel__tp_sched_test(void)
+{
+ struct perf_evsel *evsel = perf_evsel__newtp("sched", "sched_switch", 0);
+ int ret = 0;
+
+ if (evsel == NULL) {
+ pr_debug("perf_evsel__new\n");
+ return -1;
+ }
+
+ if (perf_evsel__test_field(evsel, "prev_comm", 16, true))
+ ret = -1;
+
+ if (perf_evsel__test_field(evsel, "prev_pid", 4, true))
+ ret = -1;
+
+ if (perf_evsel__test_field(evsel, "prev_prio", 4, true))
+ ret = -1;
+
+ if (perf_evsel__test_field(evsel, "prev_state", 8, true))
+ ret = -1;
+
+ if (perf_evsel__test_field(evsel, "next_comm", 16, true))
+ ret = -1;
+
+ if (perf_evsel__test_field(evsel, "next_pid", 4, true))
+ ret = -1;
+
+ if (perf_evsel__test_field(evsel, "next_prio", 4, true))
+ ret = -1;
+
+ perf_evsel__delete(evsel);
+
+ evsel = perf_evsel__newtp("sched", "sched_wakeup", 0);
+
+ if (perf_evsel__test_field(evsel, "comm", 16, true))
+ ret = -1;
+
+ if (perf_evsel__test_field(evsel, "pid", 4, true))
+ ret = -1;
+
+ if (perf_evsel__test_field(evsel, "prio", 4, true))
+ ret = -1;
+
+ if (perf_evsel__test_field(evsel, "success", 4, true))
+ ret = -1;
+
+ if (perf_evsel__test_field(evsel, "target_cpu", 4, true))
+ ret = -1;
+
+ return ret;
+}
+
+static int test__syscall_open_tp_fields(void)
+{
+ struct perf_record_opts opts = {
+ .target = {
+ .uid = UINT_MAX,
+ .uses_mmap = true,
+ },
+ .no_delay = true,
+ .freq = 1,
+ .mmap_pages = 256,
+ .raw_samples = true,
+ };
+ const char *filename = "/etc/passwd";
+ int flags = O_RDONLY | O_DIRECTORY;
+ struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
+ struct perf_evsel *evsel;
+ int err = -1, i, nr_events = 0, nr_polls = 0;
+
+ if (evlist == NULL) {
+ pr_debug("%s: perf_evlist__new\n", __func__);
+ goto out;
+ }
+
+ evsel = perf_evsel__newtp("syscalls", "sys_enter_open", 0);
+ if (evsel == NULL) {
+ pr_debug("%s: perf_evsel__newtp\n", __func__);
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__add(evlist, evsel);
+
+ err = perf_evlist__create_maps(evlist, &opts.target);
+ if (err < 0) {
+ pr_debug("%s: perf_evlist__create_maps\n", __func__);
+ goto out_delete_evlist;
+ }
+
+ perf_evsel__config(evsel, &opts, evsel);
+
+ evlist->threads->map[0] = getpid();
+
+ err = perf_evlist__open(evlist);
+ if (err < 0) {
+ pr_debug("perf_evlist__open: %s\n", strerror(errno));
+ goto out_delete_evlist;
+ }
+
+ err = perf_evlist__mmap(evlist, UINT_MAX, false);
+ if (err < 0) {
+ pr_debug("perf_evlist__mmap: %s\n", strerror(errno));
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__enable(evlist);
+
+ /*
+ * Generate the event:
+ */
+ open(filename, flags);
+
+ while (1) {
+ int before = nr_events;
+
+ for (i = 0; i < evlist->nr_mmaps; i++) {
+ union perf_event *event;
+
+ while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+ const u32 type = event->header.type;
+ int tp_flags;
+ struct perf_sample sample;
+
+ ++nr_events;
+
+ if (type != PERF_RECORD_SAMPLE)
+ continue;
+
+ err = perf_evsel__parse_sample(evsel, event, &sample);
+ if (err) {
+ pr_err("Can't parse sample, err = %d\n", err);
+ goto out_munmap;
+ }
+
+ tp_flags = perf_evsel__intval(evsel, &sample, "flags");
+
+ if (flags != tp_flags) {
+ pr_debug("%s: Expected flags=%#x, got %#x\n",
+ __func__, flags, tp_flags);
+ goto out_munmap;
+ }
+
+ goto out_ok;
+ }
+ }
+
+ if (nr_events == before)
+ poll(evlist->pollfd, evlist->nr_fds, 10);
+
+ if (++nr_polls > 5) {
+ pr_debug("%s: no events!\n", __func__);
+ goto out_munmap;
+ }
+ }
+out_ok:
+ err = 0;
+out_munmap:
+ perf_evlist__munmap(evlist);
+out_delete_evlist:
+ perf_evlist__delete(evlist);
+out:
+ return err;
+}
+
static struct test {
const char *desc;
int (*func)(void);
@@ -1135,6 +1444,18 @@ static struct test {
.func = dso__test_data,
},
{
+ .desc = "roundtrip evsel->name check",
+ .func = perf_evsel__roundtrip_name_test,
+ },
+ {
+ .desc = "Check parsing of sched tracepoints fields",
+ .func = perf_evsel__tp_sched_test,
+ },
+ {
+ .desc = "Generate and check syscalls:sys_enter_open event fields",
+ .func = test__syscall_open_tp_fields,
+ },
+ {
.func = NULL,
},
};
@@ -1199,7 +1520,7 @@ static int perf_test__list(int argc, const char **argv)
return 0;
}
-int cmd_test(int argc, const char **argv, const char *prefix __used)
+int cmd_test(int argc, const char **argv, const char *prefix __maybe_unused)
{
const char * const test_usage[] = {
"perf test [<options>] [{list <test-name-fragment>|[<test-name-fragments>|<test-numbers>]}]",
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c
index 3b75b2e21ea5..b1a8a3b841cc 100644
--- a/tools/perf/builtin-timechart.c
+++ b/tools/perf/builtin-timechart.c
@@ -168,9 +168,8 @@ static struct per_pid *find_create_pid(int pid)
return cursor;
cursor = cursor->next;
}
- cursor = malloc(sizeof(struct per_pid));
+ cursor = zalloc(sizeof(*cursor));
assert(cursor != NULL);
- memset(cursor, 0, sizeof(struct per_pid));
cursor->pid = pid;
cursor->next = all_data;
all_data = cursor;
@@ -195,9 +194,8 @@ static void pid_set_comm(int pid, char *comm)
}
c = c->next;
}
- c = malloc(sizeof(struct per_pidcomm));
+ c = zalloc(sizeof(*c));
assert(c != NULL);
- memset(c, 0, sizeof(struct per_pidcomm));
c->comm = strdup(comm);
p->current = c;
c->next = p->all;
@@ -239,17 +237,15 @@ pid_put_sample(int pid, int type, unsigned int cpu, u64 start, u64 end)
p = find_create_pid(pid);
c = p->current;
if (!c) {
- c = malloc(sizeof(struct per_pidcomm));
+ c = zalloc(sizeof(*c));
assert(c != NULL);
- memset(c, 0, sizeof(struct per_pidcomm));
p->current = c;
c->next = p->all;
p->all = c;
}
- sample = malloc(sizeof(struct cpu_sample));
+ sample = zalloc(sizeof(*sample));
assert(sample != NULL);
- memset(sample, 0, sizeof(struct cpu_sample));
sample->start_time = start;
sample->end_time = end;
sample->type = type;
@@ -275,28 +271,28 @@ static int cpus_cstate_state[MAX_CPUS];
static u64 cpus_pstate_start_times[MAX_CPUS];
static u64 cpus_pstate_state[MAX_CPUS];
-static int process_comm_event(struct perf_tool *tool __used,
+static int process_comm_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
- struct perf_sample *sample __used,
- struct machine *machine __used)
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
{
pid_set_comm(event->comm.tid, event->comm.comm);
return 0;
}
-static int process_fork_event(struct perf_tool *tool __used,
+static int process_fork_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
- struct perf_sample *sample __used,
- struct machine *machine __used)
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
{
pid_fork(event->fork.pid, event->fork.ppid, event->fork.time);
return 0;
}
-static int process_exit_event(struct perf_tool *tool __used,
+static int process_exit_event(struct perf_tool *tool __maybe_unused,
union perf_event *event,
- struct perf_sample *sample __used,
- struct machine *machine __used)
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
{
pid_exit(event->fork.pid, event->fork.time);
return 0;
@@ -373,11 +369,10 @@ static void c_state_start(int cpu, u64 timestamp, int state)
static void c_state_end(int cpu, u64 timestamp)
{
- struct power_event *pwr;
- pwr = malloc(sizeof(struct power_event));
+ struct power_event *pwr = zalloc(sizeof(*pwr));
+
if (!pwr)
return;
- memset(pwr, 0, sizeof(struct power_event));
pwr->state = cpus_cstate_state[cpu];
pwr->start_time = cpus_cstate_start_times[cpu];
@@ -392,14 +387,13 @@ static void c_state_end(int cpu, u64 timestamp)
static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
{
struct power_event *pwr;
- pwr = malloc(sizeof(struct power_event));
if (new_freq > 8000000) /* detect invalid data */
return;
+ pwr = zalloc(sizeof(*pwr));
if (!pwr)
return;
- memset(pwr, 0, sizeof(struct power_event));
pwr->state = cpus_pstate_state[cpu];
pwr->start_time = cpus_pstate_start_times[cpu];
@@ -429,15 +423,13 @@ static void p_state_change(int cpu, u64 timestamp, u64 new_freq)
static void
sched_wakeup(int cpu, u64 timestamp, int pid, struct trace_entry *te)
{
- struct wake_event *we;
struct per_pid *p;
struct wakeup_entry *wake = (void *)te;
+ struct wake_event *we = zalloc(sizeof(*we));
- we = malloc(sizeof(struct wake_event));
if (!we)
return;
- memset(we, 0, sizeof(struct wake_event));
we->time = timestamp;
we->waker = pid;
@@ -491,11 +483,11 @@ static void sched_switch(int cpu, u64 timestamp, struct trace_entry *te)
}
-static int process_sample_event(struct perf_tool *tool __used,
- union perf_event *event __used,
+static int process_sample_event(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
struct perf_sample *sample,
struct perf_evsel *evsel,
- struct machine *machine __used)
+ struct machine *machine __maybe_unused)
{
struct trace_entry *te;
@@ -579,13 +571,12 @@ static void end_sample_processing(void)
struct power_event *pwr;
for (cpu = 0; cpu <= numcpus; cpu++) {
- pwr = malloc(sizeof(struct power_event));
+ /* C state */
+#if 0
+ pwr = zalloc(sizeof(*pwr));
if (!pwr)
return;
- memset(pwr, 0, sizeof(struct power_event));
- /* C state */
-#if 0
pwr->state = cpus_cstate_state[cpu];
pwr->start_time = cpus_cstate_start_times[cpu];
pwr->end_time = last_time;
@@ -597,10 +588,9 @@ static void end_sample_processing(void)
#endif
/* P state */
- pwr = malloc(sizeof(struct power_event));
+ pwr = zalloc(sizeof(*pwr));
if (!pwr)
return;
- memset(pwr, 0, sizeof(struct power_event));
pwr->state = cpus_pstate_state[cpu];
pwr->start_time = cpus_pstate_start_times[cpu];
@@ -830,11 +820,9 @@ static void draw_process_bars(void)
static void add_process_filter(const char *string)
{
- struct process_filter *filt;
- int pid;
+ int pid = strtoull(string, NULL, 10);
+ struct process_filter *filt = malloc(sizeof(*filt));
- pid = strtoull(string, NULL, 10);
- filt = malloc(sizeof(struct process_filter));
if (!filt)
return;
@@ -1081,7 +1069,8 @@ static int __cmd_record(int argc, const char **argv)
}
static int
-parse_process(const struct option *opt __used, const char *arg, int __used unset)
+parse_process(const struct option *opt __maybe_unused, const char *arg,
+ int __maybe_unused unset)
{
if (arg)
add_process_filter(arg);
@@ -1106,7 +1095,8 @@ static const struct option options[] = {
};
-int cmd_timechart(int argc, const char **argv, const char *prefix __used)
+int cmd_timechart(int argc, const char **argv,
+ const char *prefix __maybe_unused)
{
argc = parse_options(argc, argv, options, timechart_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 68cd61ef6ac5..e434a16bb5ac 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -95,7 +95,8 @@ static void perf_top__update_print_entries(struct perf_top *top)
top->print_entries -= 9;
}
-static void perf_top__sig_winch(int sig __used, siginfo_t *info __used, void *arg)
+static void perf_top__sig_winch(int sig __maybe_unused,
+ siginfo_t *info __maybe_unused, void *arg)
{
struct perf_top *top = arg;
@@ -509,7 +510,7 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
prompt_integer(&counter, "Enter details event counter");
if (counter >= top->evlist->nr_entries) {
- top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node);
+ top->sym_evsel = perf_evlist__first(top->evlist);
fprintf(stderr, "Sorry, no such event, using %s.\n", perf_evsel__name(top->sym_evsel));
sleep(1);
break;
@@ -518,7 +519,7 @@ static void perf_top__handle_keypress(struct perf_top *top, int c)
if (top->sym_evsel->idx == counter)
break;
} else
- top->sym_evsel = list_entry(top->evlist->entries.next, struct perf_evsel, node);
+ top->sym_evsel = perf_evlist__first(top->evlist);
break;
case 'f':
prompt_integer(&top->count_filter, "Enter display event count filter");
@@ -663,7 +664,7 @@ static const char *skip_symbols[] = {
NULL
};
-static int symbol_filter(struct map *map __used, struct symbol *sym)
+static int symbol_filter(struct map *map __maybe_unused, struct symbol *sym)
{
const char *name = sym->name;
int i;
@@ -783,8 +784,10 @@ static void perf_event__process_sample(struct perf_tool *tool,
if ((sort__has_parent || symbol_conf.use_callchain) &&
sample->callchain) {
- err = machine__resolve_callchain(machine, al.thread,
- sample->callchain, &parent);
+ err = machine__resolve_callchain(machine, evsel,
+ al.thread, sample,
+ &parent);
+
if (err)
return;
}
@@ -820,7 +823,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, int idx)
int ret;
while ((event = perf_evlist__mmap_read(top->evlist, idx)) != NULL) {
- ret = perf_evlist__parse_sample(top->evlist, event, &sample, false);
+ ret = perf_evlist__parse_sample(top->evlist, event, &sample);
if (ret) {
pr_err("Can't parse sample, err = %d\n", ret);
continue;
@@ -884,17 +887,14 @@ static void perf_top__mmap_read(struct perf_top *top)
static void perf_top__start_counters(struct perf_top *top)
{
- struct perf_evsel *counter, *first;
+ struct perf_evsel *counter;
struct perf_evlist *evlist = top->evlist;
- first = list_entry(evlist->entries.next, struct perf_evsel, node);
+ if (top->group)
+ perf_evlist__set_leader(evlist);
list_for_each_entry(counter, &evlist->entries, node) {
struct perf_event_attr *attr = &counter->attr;
- struct xyarray *group_fd = NULL;
-
- if (top->group && counter != first)
- group_fd = first->fd;
attr->sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID;
@@ -925,8 +925,7 @@ retry_sample_id:
attr->sample_id_all = top->sample_id_all_missing ? 0 : 1;
try_again:
if (perf_evsel__open(counter, top->evlist->cpus,
- top->evlist->threads, top->group,
- group_fd) < 0) {
+ top->evlist->threads) < 0) {
int err = errno;
if (err == EPERM || err == EACCES) {
@@ -1165,7 +1164,7 @@ static const char * const top_usage[] = {
NULL
};
-int cmd_top(int argc, const char **argv, const char *prefix __used)
+int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
{
struct perf_evsel *pos;
int status;
@@ -1328,7 +1327,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
pos->attr.sample_period = top.default_interval;
}
- top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node);
+ top.sym_evsel = perf_evlist__first(top.evlist);
symbol_conf.priv_size = sizeof(struct annotation);
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
new file mode 100644
index 000000000000..8f113dab8bf1
--- /dev/null
+++ b/tools/perf/builtin-trace.c
@@ -0,0 +1,310 @@
+#include "builtin.h"
+#include "util/evlist.h"
+#include "util/parse-options.h"
+#include "util/thread_map.h"
+#include "event-parse.h"
+
+#include <libaudit.h>
+#include <stdlib.h>
+
+static struct syscall_fmt {
+ const char *name;
+ const char *alias;
+ bool errmsg;
+ bool timeout;
+} syscall_fmts[] = {
+ { .name = "arch_prctl", .errmsg = true, .alias = "prctl", },
+ { .name = "fstat", .errmsg = true, .alias = "newfstat", },
+ { .name = "fstatat", .errmsg = true, .alias = "newfstatat", },
+ { .name = "futex", .errmsg = true, },
+ { .name = "poll", .errmsg = true, .timeout = true, },
+ { .name = "ppoll", .errmsg = true, .timeout = true, },
+ { .name = "read", .errmsg = true, },
+ { .name = "recvfrom", .errmsg = true, },
+ { .name = "select", .errmsg = true, .timeout = true, },
+ { .name = "stat", .errmsg = true, .alias = "newstat", },
+};
+
+static int syscall_fmt__cmp(const void *name, const void *fmtp)
+{
+ const struct syscall_fmt *fmt = fmtp;
+ return strcmp(name, fmt->name);
+}
+
+static struct syscall_fmt *syscall_fmt__find(const char *name)
+{
+ const int nmemb = ARRAY_SIZE(syscall_fmts);
+ return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp);
+}
+
+struct syscall {
+ struct event_format *tp_format;
+ const char *name;
+ struct syscall_fmt *fmt;
+};
+
+struct trace {
+ int audit_machine;
+ struct {
+ int max;
+ struct syscall *table;
+ } syscalls;
+ struct perf_record_opts opts;
+};
+
+static int trace__read_syscall_info(struct trace *trace, int id)
+{
+ char tp_name[128];
+ struct syscall *sc;
+
+ if (id > trace->syscalls.max) {
+ struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc));
+
+ if (nsyscalls == NULL)
+ return -1;
+
+ if (trace->syscalls.max != -1) {
+ memset(nsyscalls + trace->syscalls.max + 1, 0,
+ (id - trace->syscalls.max) * sizeof(*sc));
+ } else {
+ memset(nsyscalls, 0, (id + 1) * sizeof(*sc));
+ }
+
+ trace->syscalls.table = nsyscalls;
+ trace->syscalls.max = id;
+ }
+
+ sc = trace->syscalls.table + id;
+ sc->name = audit_syscall_to_name(id, trace->audit_machine);
+ if (sc->name == NULL)
+ return -1;
+
+ sc->fmt = syscall_fmt__find(sc->name);
+
+ snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
+ sc->tp_format = event_format__new("syscalls", tp_name);
+
+ if (sc->tp_format == NULL && sc->fmt && sc->fmt->alias) {
+ snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->fmt->alias);
+ sc->tp_format = event_format__new("syscalls", tp_name);
+ }
+
+ return sc->tp_format != NULL ? 0 : -1;
+}
+
+static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FILE *fp)
+{
+ int i = 0;
+ size_t printed = 0;
+
+ if (sc->tp_format != NULL) {
+ struct format_field *field;
+
+ for (field = sc->tp_format->format.fields->next; field; field = field->next) {
+ printed += fprintf(fp, "%s%s: %ld", printed ? ", " : "",
+ field->name, args[i++]);
+ }
+ } else {
+ while (i < 6) {
+ printed += fprintf(fp, "%sarg%d: %ld", printed ? ", " : "", i, args[i]);
+ ++i;
+ }
+ }
+
+ return printed;
+}
+
+static int trace__run(struct trace *trace)
+{
+ struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
+ struct perf_evsel *evsel, *evsel_enter, *evsel_exit;
+ int err = -1, i, nr_events = 0, before;
+
+ if (evlist == NULL) {
+ printf("Not enough memory to run!\n");
+ goto out;
+ }
+
+ evsel_enter = perf_evsel__newtp("raw_syscalls", "sys_enter", 0);
+ if (evsel_enter == NULL) {
+ printf("Couldn't read the raw_syscalls:sys_enter tracepoint information!\n");
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__add(evlist, evsel_enter);
+
+ evsel_exit = perf_evsel__newtp("raw_syscalls", "sys_exit", 1);
+ if (evsel_exit == NULL) {
+ printf("Couldn't read the raw_syscalls:sys_exit tracepoint information!\n");
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__add(evlist, evsel_exit);
+
+ err = perf_evlist__create_maps(evlist, &trace->opts.target);
+ if (err < 0) {
+ printf("Problems parsing the target to trace, check your options!\n");
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__config_attrs(evlist, &trace->opts);
+
+ err = perf_evlist__open(evlist);
+ if (err < 0) {
+ printf("Couldn't create the events: %s\n", strerror(errno));
+ goto out_delete_evlist;
+ }
+
+ err = perf_evlist__mmap(evlist, UINT_MAX, false);
+ if (err < 0) {
+ printf("Couldn't mmap the events: %s\n", strerror(errno));
+ goto out_delete_evlist;
+ }
+
+ perf_evlist__enable(evlist);
+again:
+ before = nr_events;
+
+ for (i = 0; i < evlist->nr_mmaps; i++) {
+ union perf_event *event;
+
+ while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
+ const u32 type = event->header.type;
+ struct syscall *sc;
+ struct perf_sample sample;
+ int id;
+
+ ++nr_events;
+
+ switch (type) {
+ case PERF_RECORD_SAMPLE:
+ break;
+ case PERF_RECORD_LOST:
+ printf("LOST %" PRIu64 " events!\n", event->lost.lost);
+ continue;
+ default:
+ printf("Unexpected %s event, skipping...\n",
+ perf_event__name(type));
+ continue;
+ }
+
+ err = perf_evlist__parse_sample(evlist, event, &sample);
+ if (err) {
+ printf("Can't parse sample, err = %d, skipping...\n", err);
+ continue;
+ }
+
+ evsel = perf_evlist__id2evsel(evlist, sample.id);
+ if (evsel == NULL) {
+ printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id);
+ continue;
+ }
+
+ id = perf_evsel__intval(evsel, &sample, "id");
+ if (id < 0) {
+ printf("Invalid syscall %d id, skipping...\n", id);
+ continue;
+ }
+
+ if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) &&
+ trace__read_syscall_info(trace, id))
+ continue;
+
+ if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL))
+ continue;
+
+ sc = &trace->syscalls.table[id];
+
+ if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1)
+ printf("%d ", sample.tid);
+
+ if (evsel == evsel_enter) {
+ void *args = perf_evsel__rawptr(evsel, &sample, "args");
+
+ printf("%s(", sc->name);
+ syscall__fprintf_args(sc, args, stdout);
+ } else if (evsel == evsel_exit) {
+ int ret = perf_evsel__intval(evsel, &sample, "ret");
+
+ if (ret < 0 && sc->fmt && sc->fmt->errmsg) {
+ char bf[256];
+ const char *emsg = strerror_r(-ret, bf, sizeof(bf)),
+ *e = audit_errno_to_name(-ret);
+
+ printf(") = -1 %s %s", e, emsg);
+ } else if (ret == 0 && sc->fmt && sc->fmt->timeout)
+ printf(") = 0 Timeout");
+ else
+ printf(") = %d", ret);
+
+ putchar('\n');
+ }
+ }
+ }
+
+ if (nr_events == before)
+ poll(evlist->pollfd, evlist->nr_fds, -1);
+
+ goto again;
+
+out_delete_evlist:
+ perf_evlist__delete(evlist);
+out:
+ return err;
+}
+
+int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
+{
+ const char * const trace_usage[] = {
+ "perf trace [<options>]",
+ NULL
+ };
+ struct trace trace = {
+ .audit_machine = audit_detect_machine(),
+ .syscalls = {
+ . max = -1,
+ },
+ .opts = {
+ .target = {
+ .uid = UINT_MAX,
+ .uses_mmap = true,
+ },
+ .user_freq = UINT_MAX,
+ .user_interval = ULLONG_MAX,
+ .no_delay = true,
+ .mmap_pages = 1024,
+ },
+ };
+ const struct option trace_options[] = {
+ OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
+ "trace events on existing process id"),
+ OPT_STRING(0, "tid", &trace.opts.target.tid, "tid",
+ "trace events on existing thread id"),
+ OPT_BOOLEAN(0, "all-cpus", &trace.opts.target.system_wide,
+ "system-wide collection from all CPUs"),
+ OPT_STRING(0, "cpu", &trace.opts.target.cpu_list, "cpu",
+ "list of cpus to monitor"),
+ OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit,
+ "child tasks do not inherit counters"),
+ OPT_UINTEGER(0, "mmap-pages", &trace.opts.mmap_pages,
+ "number of mmap data pages"),
+ OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user",
+ "user to profile"),
+ OPT_END()
+ };
+ int err;
+
+ argc = parse_options(argc, argv, trace_options, trace_usage, 0);
+ if (argc)
+ usage_with_options(trace_usage, trace_options);
+
+ err = perf_target__parse_uid(&trace.opts.target);
+ if (err) {
+ char bf[BUFSIZ];
+ perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
+ printf("%s", bf);
+ return err;
+ }
+
+ return trace__run(&trace);
+}
diff --git a/tools/perf/builtin.h b/tools/perf/builtin.h
index b382bd551aac..08143bd854c7 100644
--- a/tools/perf/builtin.h
+++ b/tools/perf/builtin.h
@@ -34,6 +34,8 @@ extern int cmd_kmem(int argc, const char **argv, const char *prefix);
extern int cmd_lock(int argc, const char **argv, const char *prefix);
extern int cmd_kvm(int argc, const char **argv, const char *prefix);
extern int cmd_test(int argc, const char **argv, const char *prefix);
+extern int cmd_trace(int argc, const char **argv, const char *prefix);
extern int cmd_inject(int argc, const char **argv, const char *prefix);
+extern int find_scripts(char **scripts_array, char **scripts_path_array);
#endif
diff --git a/tools/perf/command-list.txt b/tools/perf/command-list.txt
index d695fe40fbff..3e86bbd8c2d5 100644
--- a/tools/perf/command-list.txt
+++ b/tools/perf/command-list.txt
@@ -17,8 +17,9 @@ perf-report mainporcelain common
perf-stat mainporcelain common
perf-timechart mainporcelain common
perf-top mainporcelain common
+perf-trace mainporcelain common
perf-script mainporcelain common
-perf-probe mainporcelain common
+perf-probe mainporcelain full
perf-kmem mainporcelain common
perf-lock mainporcelain common
perf-kvm mainporcelain common
diff --git a/tools/perf/config/feature-tests.mak b/tools/perf/config/feature-tests.mak
index 6c18785a6417..4add41bb0c7e 100644
--- a/tools/perf/config/feature-tests.mak
+++ b/tools/perf/config/feature-tests.mak
@@ -154,3 +154,53 @@ int main(void)
return 0;
}
endef
+
+ifndef NO_LIBUNWIND
+define SOURCE_LIBUNWIND
+#include <libunwind.h>
+#include <stdlib.h>
+
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+
+
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+
+int main(void)
+{
+ unw_addr_space_t addr_space;
+ addr_space = unw_create_addr_space(NULL, 0);
+ unw_init_remote(NULL, addr_space, NULL);
+ dwarf_search_unwind_table(addr_space, 0, NULL, NULL, 0, NULL);
+ return 0;
+}
+endef
+endif
+
+ifndef NO_BACKTRACE
+define SOURCE_BACKTRACE
+#include <execinfo.h>
+#include <stdio.h>
+
+int main(void)
+{
+ backtrace(NULL, 0);
+ backtrace_symbols(NULL, 0);
+ return 0;
+}
+endef
+endif
+
+ifndef NO_LIBAUDIT
+define SOURCE_LIBAUDIT
+#include <libaudit.h>
+
+int main(void)
+{
+ return audit_open();
+}
+endef
+endif \ No newline at end of file
diff --git a/tools/perf/perf-archive.sh b/tools/perf/perf-archive.sh
index 95b6f8b6177a..e91930620269 100644
--- a/tools/perf/perf-archive.sh
+++ b/tools/perf/perf-archive.sh
@@ -24,7 +24,7 @@ NOBUILDID=0000000000000000000000000000000000000000
perf buildid-list -i $PERF_DATA --with-hits | grep -v "^$NOBUILDID " > $BUILDIDS
if [ ! -s $BUILDIDS ] ; then
echo "perf archive: no build-ids found"
- rm -f $BUILDIDS
+ rm $BUILDIDS || true
exit 1
fi
@@ -39,8 +39,8 @@ while read build_id ; do
echo ${filename#$PERF_BUILDID_LINKDIR} >> $MANIFEST
done
-tar cfj $PERF_DATA.tar.bz2 -C $PERF_BUILDID_DIR -T $MANIFEST
-rm -f $MANIFEST $BUILDIDS
+tar cjf $PERF_DATA.tar.bz2 -C $PERF_BUILDID_DIR -T $MANIFEST
+rm $MANIFEST $BUILDIDS || true
echo -e "Now please run:\n"
echo -e "$ tar xvf $PERF_DATA.tar.bz2 -C ~/.debug\n"
echo "wherever you need to run 'perf report' on."
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 2b2e225a4d4c..fc2f770e3027 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -14,6 +14,7 @@
#include "util/run-command.h"
#include "util/parse-events.h"
#include "util/debugfs.h"
+#include <pthread.h>
const char perf_usage_string[] =
"perf [--version] [--help] COMMAND [ARGS]";
@@ -24,6 +25,42 @@ const char perf_more_info_string[] =
int use_browser = -1;
static int use_pager = -1;
+struct cmd_struct {
+ const char *cmd;
+ int (*fn)(int, const char **, const char *);
+ int option;
+};
+
+static struct cmd_struct commands[] = {
+ { "buildid-cache", cmd_buildid_cache, 0 },
+ { "buildid-list", cmd_buildid_list, 0 },
+ { "diff", cmd_diff, 0 },
+ { "evlist", cmd_evlist, 0 },
+ { "help", cmd_help, 0 },
+ { "list", cmd_list, 0 },
+ { "record", cmd_record, 0 },
+ { "report", cmd_report, 0 },
+ { "bench", cmd_bench, 0 },
+ { "stat", cmd_stat, 0 },
+ { "timechart", cmd_timechart, 0 },
+ { "top", cmd_top, 0 },
+ { "annotate", cmd_annotate, 0 },
+ { "version", cmd_version, 0 },
+ { "script", cmd_script, 0 },
+ { "sched", cmd_sched, 0 },
+#ifndef NO_LIBELF_SUPPORT
+ { "probe", cmd_probe, 0 },
+#endif
+ { "kmem", cmd_kmem, 0 },
+ { "lock", cmd_lock, 0 },
+ { "kvm", cmd_kvm, 0 },
+ { "test", cmd_test, 0 },
+#ifndef NO_LIBAUDIT_SUPPORT
+ { "trace", cmd_trace, 0 },
+#endif
+ { "inject", cmd_inject, 0 },
+};
+
struct pager_config {
const char *cmd;
int val;
@@ -160,6 +197,14 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)
fprintf(stderr, "dir: %s\n", debugfs_mountpoint);
if (envchanged)
*envchanged = 1;
+ } else if (!strcmp(cmd, "--list-cmds")) {
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(commands); i++) {
+ struct cmd_struct *p = commands+i;
+ printf("%s ", p->cmd);
+ }
+ exit(0);
} else {
fprintf(stderr, "Unknown option: %s\n", cmd);
usage(perf_usage_string);
@@ -245,12 +290,6 @@ const char perf_version_string[] = PERF_VERSION;
*/
#define NEED_WORK_TREE (1<<2)
-struct cmd_struct {
- const char *cmd;
- int (*fn)(int, const char **, const char *);
- int option;
-};
-
static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
{
int status;
@@ -296,30 +335,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
static void handle_internal_command(int argc, const char **argv)
{
const char *cmd = argv[0];
- static struct cmd_struct commands[] = {
- { "buildid-cache", cmd_buildid_cache, 0 },
- { "buildid-list", cmd_buildid_list, 0 },
- { "diff", cmd_diff, 0 },
- { "evlist", cmd_evlist, 0 },
- { "help", cmd_help, 0 },
- { "list", cmd_list, 0 },
- { "record", cmd_record, 0 },
- { "report", cmd_report, 0 },
- { "bench", cmd_bench, 0 },
- { "stat", cmd_stat, 0 },
- { "timechart", cmd_timechart, 0 },
- { "top", cmd_top, 0 },
- { "annotate", cmd_annotate, 0 },
- { "version", cmd_version, 0 },
- { "script", cmd_script, 0 },
- { "sched", cmd_sched, 0 },
- { "probe", cmd_probe, 0 },
- { "kmem", cmd_kmem, 0 },
- { "lock", cmd_lock, 0 },
- { "kvm", cmd_kvm, 0 },
- { "test", cmd_test, 0 },
- { "inject", cmd_inject, 0 },
- };
unsigned int i;
static const char ext[] = STRIP_EXTENSION;
diff --git a/tools/perf/perf.h b/tools/perf/perf.h
index f960ccb2edc6..a89cbbb61801 100644
--- a/tools/perf/perf.h
+++ b/tools/perf/perf.h
@@ -88,6 +88,12 @@ void get_term_dimensions(struct winsize *ws);
#define CPUINFO_PROC "Processor"
#endif
+#ifdef __aarch64__
+#include "../../arch/arm64/include/asm/unistd.h"
+#define rmb() asm volatile("dmb ld" ::: "memory")
+#define cpu_relax() asm volatile("yield" ::: "memory")
+#endif
+
#ifdef __mips__
#include "../../arch/mips/include/asm/unistd.h"
#define rmb() asm volatile( \
@@ -209,9 +215,15 @@ void pthread__unblock_sigwinch(void);
#include "util/target.h"
+enum perf_call_graph_mode {
+ CALLCHAIN_NONE,
+ CALLCHAIN_FP,
+ CALLCHAIN_DWARF
+};
+
struct perf_record_opts {
struct perf_target target;
- bool call_graph;
+ int call_graph;
bool group;
bool inherit_stat;
bool no_delay;
@@ -230,6 +242,7 @@ struct perf_record_opts {
u64 branch_stack;
u64 default_interval;
u64 user_interval;
+ u16 stack_dump_size;
};
#endif
diff --git a/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py
new file mode 100755
index 000000000000..9e0985794e20
--- /dev/null
+++ b/tools/perf/scripts/python/Perf-Trace-Util/lib/Perf/Trace/EventClass.py
@@ -0,0 +1,94 @@
+# EventClass.py
+#
+# This is a library defining some events types classes, which could
+# be used by other scripts to analyzing the perf samples.
+#
+# Currently there are just a few classes defined for examples,
+# PerfEvent is the base class for all perf event sample, PebsEvent
+# is a HW base Intel x86 PEBS event, and user could add more SW/HW
+# event classes based on requirements.
+
+import struct
+
+# Event types, user could add more here
+EVTYPE_GENERIC = 0
+EVTYPE_PEBS = 1 # Basic PEBS event
+EVTYPE_PEBS_LL = 2 # PEBS event with load latency info
+EVTYPE_IBS = 3
+
+#
+# Currently we don't have good way to tell the event type, but by
+# the size of raw buffer, raw PEBS event with load latency data's
+# size is 176 bytes, while the pure PEBS event's size is 144 bytes.
+#
+def create_event(name, comm, dso, symbol, raw_buf):
+ if (len(raw_buf) == 144):
+ event = PebsEvent(name, comm, dso, symbol, raw_buf)
+ elif (len(raw_buf) == 176):
+ event = PebsNHM(name, comm, dso, symbol, raw_buf)
+ else:
+ event = PerfEvent(name, comm, dso, symbol, raw_buf)
+
+ return event
+
+class PerfEvent(object):
+ event_num = 0
+ def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=EVTYPE_GENERIC):
+ self.name = name
+ self.comm = comm
+ self.dso = dso
+ self.symbol = symbol
+ self.raw_buf = raw_buf
+ self.ev_type = ev_type
+ PerfEvent.event_num += 1
+
+ def show(self):
+ print "PMU event: name=%12s, symbol=%24s, comm=%8s, dso=%12s" % (self.name, self.symbol, self.comm, self.dso)
+
+#
+# Basic Intel PEBS (Precise Event-based Sampling) event, whose raw buffer
+# contains the context info when that event happened: the EFLAGS and
+# linear IP info, as well as all the registers.
+#
+class PebsEvent(PerfEvent):
+ pebs_num = 0
+ def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=EVTYPE_PEBS):
+ tmp_buf=raw_buf[0:80]
+ flags, ip, ax, bx, cx, dx, si, di, bp, sp = struct.unpack('QQQQQQQQQQ', tmp_buf)
+ self.flags = flags
+ self.ip = ip
+ self.ax = ax
+ self.bx = bx
+ self.cx = cx
+ self.dx = dx
+ self.si = si
+ self.di = di
+ self.bp = bp
+ self.sp = sp
+
+ PerfEvent.__init__(self, name, comm, dso, symbol, raw_buf, ev_type)
+ PebsEvent.pebs_num += 1
+ del tmp_buf
+
+#
+# Intel Nehalem and Westmere support PEBS plus Load Latency info which lie
+# in the four 64 bit words write after the PEBS data:
+# Status: records the IA32_PERF_GLOBAL_STATUS register value
+# DLA: Data Linear Address (EIP)
+# DSE: Data Source Encoding, where the latency happens, hit or miss
+# in L1/L2/L3 or IO operations
+# LAT: the actual latency in cycles
+#
+class PebsNHM(PebsEvent):
+ pebs_nhm_num = 0
+ def __init__(self, name, comm, dso, symbol, raw_buf, ev_type=EVTYPE_PEBS_LL):
+ tmp_buf=raw_buf[144:176]
+ status, dla, dse, lat = struct.unpack('QQQQ', tmp_buf)
+ self.status = status
+ self.dla = dla
+ self.dse = dse
+ self.lat = lat
+
+ PebsEvent.__init__(self, name, comm, dso, symbol, raw_buf, ev_type)
+ PebsNHM.pebs_nhm_num += 1
+ del tmp_buf
diff --git a/tools/perf/scripts/python/bin/event_analyzing_sample-record b/tools/perf/scripts/python/bin/event_analyzing_sample-record
new file mode 100644
index 000000000000..5ce652dabd02
--- /dev/null
+++ b/tools/perf/scripts/python/bin/event_analyzing_sample-record
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+#
+# event_analyzing_sample.py can cover all type of perf samples including
+# the tracepoints, so no special record requirements, just record what
+# you want to analyze.
+#
+perf record $@
diff --git a/tools/perf/scripts/python/bin/event_analyzing_sample-report b/tools/perf/scripts/python/bin/event_analyzing_sample-report
new file mode 100644
index 000000000000..0941fc94e158
--- /dev/null
+++ b/tools/perf/scripts/python/bin/event_analyzing_sample-report
@@ -0,0 +1,3 @@
+#!/bin/bash
+# description: analyze all perf samples
+perf script $@ -s "$PERF_EXEC_PATH"/scripts/python/event_analyzing_sample.py
diff --git a/tools/perf/scripts/python/event_analyzing_sample.py b/tools/perf/scripts/python/event_analyzing_sample.py
new file mode 100644
index 000000000000..163c39fa12d9
--- /dev/null
+++ b/tools/perf/scripts/python/event_analyzing_sample.py
@@ -0,0 +1,189 @@
+# event_analyzing_sample.py: general event handler in python
+#
+# Current perf report is already very powerful with the annotation integrated,
+# and this script is not trying to be as powerful as perf report, but
+# providing end user/developer a flexible way to analyze the events other
+# than trace points.
+#
+# The 2 database related functions in this script just show how to gather
+# the basic information, and users can modify and write their own functions
+# according to their specific requirement.
+#
+# The first function "show_general_events" just does a basic grouping for all
+# generic events with the help of sqlite, and the 2nd one "show_pebs_ll" is
+# for a x86 HW PMU event: PEBS with load latency data.
+#
+
+import os
+import sys
+import math
+import struct
+import sqlite3
+
+sys.path.append(os.environ['PERF_EXEC_PATH'] + \
+ '/scripts/python/Perf-Trace-Util/lib/Perf/Trace')
+
+from perf_trace_context import *
+from EventClass import *
+
+#
+# If the perf.data has a big number of samples, then the insert operation
+# will be very time consuming (about 10+ minutes for 10000 samples) if the
+# .db database is on disk. Move the .db file to RAM based FS to speedup
+# the handling, which will cut the time down to several seconds.
+#
+con = sqlite3.connect("/dev/shm/perf.db")
+con.isolation_level = None
+
+def trace_begin():
+ print "In trace_begin:\n"
+
+ #
+ # Will create several tables at the start, pebs_ll is for PEBS data with
+ # load latency info, while gen_events is for general event.
+ #
+ con.execute("""
+ create table if not exists gen_events (
+ name text,
+ symbol text,
+ comm text,
+ dso text
+ );""")
+ con.execute("""
+ create table if not exists pebs_ll (
+ name text,
+ symbol text,
+ comm text,
+ dso text,
+ flags integer,
+ ip integer,
+ status integer,
+ dse integer,
+ dla integer,
+ lat integer
+ );""")
+
+#
+# Create and insert event object to a database so that user could
+# do more analysis with simple database commands.
+#
+def process_event(param_dict):
+ event_attr = param_dict["attr"]
+ sample = param_dict["sample"]
+ raw_buf = param_dict["raw_buf"]
+ comm = param_dict["comm"]
+ name = param_dict["ev_name"]
+
+ # Symbol and dso info are not always resolved
+ if (param_dict.has_key("dso")):
+ dso = param_dict["dso"]
+ else:
+ dso = "Unknown_dso"
+
+ if (param_dict.has_key("symbol")):
+ symbol = param_dict["symbol"]
+ else:
+ symbol = "Unknown_symbol"
+
+ # Create the event object and insert it to the right table in database
+ event = create_event(name, comm, dso, symbol, raw_buf)
+ insert_db(event)
+
+def insert_db(event):
+ if event.ev_type == EVTYPE_GENERIC:
+ con.execute("insert into gen_events values(?, ?, ?, ?)",
+ (event.name, event.symbol, event.comm, event.dso))
+ elif event.ev_type == EVTYPE_PEBS_LL:
+ event.ip &= 0x7fffffffffffffff
+ event.dla &= 0x7fffffffffffffff
+ con.execute("insert into pebs_ll values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
+ (event.name, event.symbol, event.comm, event.dso, event.flags,
+ event.ip, event.status, event.dse, event.dla, event.lat))
+
+def trace_end():
+ print "In trace_end:\n"
+ # We show the basic info for the 2 type of event classes
+ show_general_events()
+ show_pebs_ll()
+ con.close()
+
+#
+# As the event number may be very big, so we can't use linear way
+# to show the histogram in real number, but use a log2 algorithm.
+#
+
+def num2sym(num):
+ # Each number will have at least one '#'
+ snum = '#' * (int)(math.log(num, 2) + 1)
+ return snum
+
+def show_general_events():
+
+ # Check the total record number in the table
+ count = con.execute("select count(*) from gen_events")
+ for t in count:
+ print "There is %d records in gen_events table" % t[0]
+ if t[0] == 0:
+ return
+
+ print "Statistics about the general events grouped by thread/symbol/dso: \n"
+
+ # Group by thread
+ commq = con.execute("select comm, count(comm) from gen_events group by comm order by -count(comm)")
+ print "\n%16s %8s %16s\n%s" % ("comm", "number", "histogram", "="*42)
+ for row in commq:
+ print "%16s %8d %s" % (row[0], row[1], num2sym(row[1]))
+
+ # Group by symbol
+ print "\n%32s %8s %16s\n%s" % ("symbol", "number", "histogram", "="*58)
+ symbolq = con.execute("select symbol, count(symbol) from gen_events group by symbol order by -count(symbol)")
+ for row in symbolq:
+ print "%32s %8d %s" % (row[0], row[1], num2sym(row[1]))
+
+ # Group by dso
+ print "\n%40s %8s %16s\n%s" % ("dso", "number", "histogram", "="*74)
+ dsoq = con.execute("select dso, count(dso) from gen_events group by dso order by -count(dso)")
+ for row in dsoq:
+ print "%40s %8d %s" % (row[0], row[1], num2sym(row[1]))
+
+#
+# This function just shows the basic info, and we could do more with the
+# data in the tables, like checking the function parameters when some
+# big latency events happen.
+#
+def show_pebs_ll():
+
+ count = con.execute("select count(*) from pebs_ll")
+ for t in count:
+ print "There is %d records in pebs_ll table" % t[0]
+ if t[0] == 0:
+ return
+
+ print "Statistics about the PEBS Load Latency events grouped by thread/symbol/dse/latency: \n"
+
+ # Group by thread
+ commq = con.execute("select comm, count(comm) from pebs_ll group by comm order by -count(comm)")
+ print "\n%16s %8s %16s\n%s" % ("comm", "number", "histogram", "="*42)
+ for row in commq:
+ print "%16s %8d %s" % (row[0], row[1], num2sym(row[1]))
+
+ # Group by symbol
+ print "\n%32s %8s %16s\n%s" % ("symbol", "number", "histogram", "="*58)
+ symbolq = con.execute("select symbol, count(symbol) from pebs_ll group by symbol order by -count(symbol)")
+ for row in symbolq:
+ print "%32s %8d %s" % (row[0], row[1], num2sym(row[1]))
+
+ # Group by dse
+ dseq = con.execute("select dse, count(dse) from pebs_ll group by dse order by -count(dse)")
+ print "\n%32s %8s %16s\n%s" % ("dse", "number", "histogram", "="*58)
+ for row in dseq:
+ print "%32s %8d %s" % (row[0], row[1], num2sym(row[1]))
+
+ # Group by latency
+ latq = con.execute("select lat, count(lat) from pebs_ll group by lat order by lat")
+ print "\n%32s %8s %16s\n%s" % ("latency", "number", "histogram", "="*58)
+ for row in latq:
+ print "%32s %8d %s" % (row[0], row[1], num2sym(row[1]))
+
+def trace_unhandled(event_name, context, event_fields_dict):
+ print ' '.join(['%s=%s'%(k,str(v))for k,v in sorted(event_fields_dict.items())])
diff --git a/tools/perf/ui/browser.c b/tools/perf/ui/browser.c
index 1818a531f1d3..4aeb7d5df939 100644
--- a/tools/perf/ui/browser.c
+++ b/tools/perf/ui/browser.c
@@ -269,7 +269,7 @@ int ui_browser__show(struct ui_browser *browser, const char *title,
return err ? 0 : -1;
}
-void ui_browser__hide(struct ui_browser *browser __used)
+void ui_browser__hide(struct ui_browser *browser __maybe_unused)
{
pthread_mutex_lock(&ui__lock);
ui_helpline__pop();
@@ -518,7 +518,7 @@ static struct ui_browser__colorset {
static int ui_browser__color_config(const char *var, const char *value,
- void *data __used)
+ void *data __maybe_unused)
{
char *fg = NULL, *bg;
int i;
@@ -602,7 +602,8 @@ void __ui_browser__vline(struct ui_browser *browser, unsigned int column,
SLsmg_set_char_set(0);
}
-void ui_browser__write_graph(struct ui_browser *browser __used, int graph)
+void ui_browser__write_graph(struct ui_browser *browser __maybe_unused,
+ int graph)
{
SLsmg_set_char_set(1);
SLsmg_write_char(graph);
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 67a2703e666a..8f8cd2d73b3b 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -54,7 +54,8 @@ static inline struct browser_disasm_line *disasm_line__browser(struct disasm_lin
return (struct browser_disasm_line *)(dl + 1);
}
-static bool disasm_line__filter(struct ui_browser *browser __used, void *entry)
+static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
+ void *entry)
{
if (annotate_browser__opts.hide_src_code) {
struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
@@ -928,7 +929,8 @@ static int annotate_config__cmp(const void *name, const void *cfgp)
return strcmp(name, cfg->name);
}
-static int annotate__config(const char *var, const char *value, void *data __used)
+static int annotate__config(const char *var, const char *value,
+ void *data __maybe_unused)
{
struct annotate__config *cfg;
const char *name;
diff --git a/tools/perf/ui/browsers/hists.c b/tools/perf/ui/browsers/hists.c
index 413bd62eedb1..a21f40bebbac 100644
--- a/tools/perf/ui/browsers/hists.c
+++ b/tools/perf/ui/browsers/hists.c
@@ -24,9 +24,12 @@ struct hist_browser {
struct hist_entry *he_selection;
struct map_symbol *selection;
int print_seq;
+ bool show_dso;
bool has_symbols;
};
+extern void hist_browser__init_hpp(void);
+
static int hists__browser_title(struct hists *hists, char *bf, size_t size,
const char *ev_name);
@@ -376,12 +379,19 @@ out:
}
static char *callchain_list__sym_name(struct callchain_list *cl,
- char *bf, size_t bfsize)
+ char *bf, size_t bfsize, bool show_dso)
{
+ int printed;
+
if (cl->ms.sym)
- return cl->ms.sym->name;
+ printed = scnprintf(bf, bfsize, "%s", cl->ms.sym->name);
+ else
+ printed = scnprintf(bf, bfsize, "%#" PRIx64, cl->ip);
+
+ if (show_dso)
+ scnprintf(bf + printed, bfsize - printed, " %s",
+ cl->ms.map ? cl->ms.map->dso->short_name : "unknown");
- snprintf(bf, bfsize, "%#" PRIx64, cl->ip);
return bf;
}
@@ -417,7 +427,7 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
remaining -= cumul;
list_for_each_entry(chain, &child->val, list) {
- char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
+ char bf[1024], *alloc_str;
const char *str;
int color;
bool was_first = first;
@@ -434,7 +444,8 @@ static int hist_browser__show_callchain_node_rb_tree(struct hist_browser *browse
}
alloc_str = NULL;
- str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
+ str = callchain_list__sym_name(chain, bf, sizeof(bf),
+ browser->show_dso);
if (was_first) {
double percent = cumul * 100.0 / new_total;
@@ -493,7 +504,7 @@ static int hist_browser__show_callchain_node(struct hist_browser *browser,
char folded_sign = ' ';
list_for_each_entry(chain, &node->val, list) {
- char ipstr[BITS_PER_LONG / 4 + 1], *s;
+ char bf[1024], *s;
int color;
folded_sign = callchain_list__folded(chain);
@@ -510,7 +521,8 @@ static int hist_browser__show_callchain_node(struct hist_browser *browser,
*is_current_entry = true;
}
- s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
+ s = callchain_list__sym_name(chain, bf, sizeof(bf),
+ browser->show_dso);
ui_browser__gotorc(&browser->b, row, 0);
ui_browser__set_color(&browser->b, color);
slsmg_write_nstring(" ", offset);
@@ -553,14 +565,47 @@ static int hist_browser__show_callchain(struct hist_browser *browser,
return row - first_row;
}
+#define HPP__COLOR_FN(_name, _field) \
+static int hist_browser__hpp_color_ ## _name(struct perf_hpp *hpp, \
+ struct hist_entry *he) \
+{ \
+ double percent = 100.0 * he->_field / hpp->total_period; \
+ *(double *)hpp->ptr = percent; \
+ return scnprintf(hpp->buf, hpp->size, "%6.2f%%", percent); \
+}
+
+HPP__COLOR_FN(overhead, period)
+HPP__COLOR_FN(overhead_sys, period_sys)
+HPP__COLOR_FN(overhead_us, period_us)
+HPP__COLOR_FN(overhead_guest_sys, period_guest_sys)
+HPP__COLOR_FN(overhead_guest_us, period_guest_us)
+
+#undef HPP__COLOR_FN
+
+void hist_browser__init_hpp(void)
+{
+ perf_hpp__init(false, false);
+
+ perf_hpp__format[PERF_HPP__OVERHEAD].color =
+ hist_browser__hpp_color_overhead;
+ perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
+ hist_browser__hpp_color_overhead_sys;
+ perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
+ hist_browser__hpp_color_overhead_us;
+ perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
+ hist_browser__hpp_color_overhead_guest_sys;
+ perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
+ hist_browser__hpp_color_overhead_guest_us;
+}
+
static int hist_browser__show_entry(struct hist_browser *browser,
struct hist_entry *entry,
unsigned short row)
{
char s[256];
double percent;
- int printed = 0;
- int width = browser->b.width - 6; /* The percentage */
+ int i, printed = 0;
+ int width = browser->b.width;
char folded_sign = ' ';
bool current_entry = ui_browser__is_current_entry(&browser->b, row);
off_t row_offset = entry->row_offset;
@@ -576,35 +621,50 @@ static int hist_browser__show_entry(struct hist_browser *browser,
}
if (row_offset == 0) {
- hist_entry__snprintf(entry, s, sizeof(s), browser->hists);
- percent = (entry->period * 100.0) / browser->hists->stats.total_period;
+ struct perf_hpp hpp = {
+ .buf = s,
+ .size = sizeof(s),
+ .total_period = browser->hists->stats.total_period,
+ };
- ui_browser__set_percent_color(&browser->b, percent, current_entry);
ui_browser__gotorc(&browser->b, row, 0);
- if (symbol_conf.use_callchain) {
- slsmg_printf("%c ", folded_sign);
- width -= 2;
- }
- slsmg_printf(" %5.2f%%", percent);
+ for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
+ if (!perf_hpp__format[i].cond)
+ continue;
- /* The scroll bar isn't being used */
- if (!browser->b.navkeypressed)
- width += 1;
+ if (i) {
+ slsmg_printf(" ");
+ width -= 2;
+ }
- if (!current_entry || !browser->b.navkeypressed)
- ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
+ if (perf_hpp__format[i].color) {
+ hpp.ptr = &percent;
+ /* It will set percent for us. See HPP__COLOR_FN above. */
+ width -= perf_hpp__format[i].color(&hpp, entry);
- if (symbol_conf.show_nr_samples) {
- slsmg_printf(" %11u", entry->nr_events);
- width -= 12;
- }
+ ui_browser__set_percent_color(&browser->b, percent, current_entry);
+
+ if (i == 0 && symbol_conf.use_callchain) {
+ slsmg_printf("%c ", folded_sign);
+ width -= 2;
+ }
+
+ slsmg_printf("%s", s);
- if (symbol_conf.show_total_period) {
- slsmg_printf(" %12" PRIu64, entry->period);
- width -= 13;
+ if (!current_entry || !browser->b.navkeypressed)
+ ui_browser__set_color(&browser->b, HE_COLORSET_NORMAL);
+ } else {
+ width -= perf_hpp__format[i].entry(&hpp, entry);
+ slsmg_printf("%s", s);
+ }
}
+ /* The scroll bar isn't being used */
+ if (!browser->b.navkeypressed)
+ width += 1;
+
+ hist_entry__sort_snprintf(entry, s, sizeof(s), browser->hists);
slsmg_write_nstring(s, width);
++row;
++printed;
@@ -830,7 +890,7 @@ static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *bro
remaining -= cumul;
list_for_each_entry(chain, &child->val, list) {
- char ipstr[BITS_PER_LONG / 4 + 1], *alloc_str;
+ char bf[1024], *alloc_str;
const char *str;
bool was_first = first;
@@ -842,7 +902,8 @@ static int hist_browser__fprintf_callchain_node_rb_tree(struct hist_browser *bro
folded_sign = callchain_list__folded(chain);
alloc_str = NULL;
- str = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
+ str = callchain_list__sym_name(chain, bf, sizeof(bf),
+ browser->show_dso);
if (was_first) {
double percent = cumul * 100.0 / new_total;
@@ -880,10 +941,10 @@ static int hist_browser__fprintf_callchain_node(struct hist_browser *browser,
int printed = 0;
list_for_each_entry(chain, &node->val, list) {
- char ipstr[BITS_PER_LONG / 4 + 1], *s;
+ char bf[1024], *s;
folded_sign = callchain_list__folded(chain);
- s = callchain_list__sym_name(chain, ipstr, sizeof(ipstr));
+ s = callchain_list__sym_name(chain, bf, sizeof(bf), browser->show_dso);
printed += fprintf(fp, "%*s%c %s\n", offset, " ", folded_sign, s);
}
@@ -920,7 +981,7 @@ static int hist_browser__fprintf_entry(struct hist_browser *browser,
if (symbol_conf.use_callchain)
folded_sign = hist_entry__folded(he);
- hist_entry__snprintf(he, s, sizeof(s), browser->hists);
+ hist_entry__sort_snprintf(he, s, sizeof(s), browser->hists);
percent = (he->period * 100.0) / browser->hists->stats.total_period;
if (symbol_conf.use_callchain)
@@ -1133,6 +1194,9 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
continue;
case 'd':
goto zoom_dso;
+ case 'V':
+ browser->show_dso = !browser->show_dso;
+ continue;
case 't':
goto zoom_thread;
case '/':
@@ -1164,6 +1228,7 @@ static int perf_evsel__hists_browse(struct perf_evsel *evsel, int nr_events,
"d Zoom into current DSO\n"
"t Zoom into current Thread\n"
"P Print histograms to perf.hist.N\n"
+ "V Verbose (DSO names in callchains, etc)\n"
"/ Filter symbol by name");
continue;
case K_ENTER:
diff --git a/tools/perf/ui/gtk/browser.c b/tools/perf/ui/gtk/browser.c
index ec12e0b4ded6..7ff99ec1d95e 100644
--- a/tools/perf/ui/gtk/browser.c
+++ b/tools/perf/ui/gtk/browser.c
@@ -3,6 +3,7 @@
#include "../evsel.h"
#include "../sort.h"
#include "../hist.h"
+#include "../helpline.h"
#include "gtk.h"
#include <signal.h>
@@ -35,6 +36,57 @@ static void perf_gtk__resize_window(GtkWidget *window)
gtk_window_resize(GTK_WINDOW(window), width, height);
}
+static const char *perf_gtk__get_percent_color(double percent)
+{
+ if (percent >= MIN_RED)
+ return "<span fgcolor='red'>";
+ if (percent >= MIN_GREEN)
+ return "<span fgcolor='dark green'>";
+ return NULL;
+}
+
+#define HPP__COLOR_FN(_name, _field) \
+static int perf_gtk__hpp_color_ ## _name(struct perf_hpp *hpp, \
+ struct hist_entry *he) \
+{ \
+ double percent = 100.0 * he->_field / hpp->total_period; \
+ const char *markup; \
+ int ret = 0; \
+ \
+ markup = perf_gtk__get_percent_color(percent); \
+ if (markup) \
+ ret += scnprintf(hpp->buf, hpp->size, "%s", markup); \
+ ret += scnprintf(hpp->buf + ret, hpp->size - ret, "%6.2f%%", percent); \
+ if (markup) \
+ ret += scnprintf(hpp->buf + ret, hpp->size - ret, "</span>"); \
+ \
+ return ret; \
+}
+
+HPP__COLOR_FN(overhead, period)
+HPP__COLOR_FN(overhead_sys, period_sys)
+HPP__COLOR_FN(overhead_us, period_us)
+HPP__COLOR_FN(overhead_guest_sys, period_guest_sys)
+HPP__COLOR_FN(overhead_guest_us, period_guest_us)
+
+#undef HPP__COLOR_FN
+
+void perf_gtk__init_hpp(void)
+{
+ perf_hpp__init(false, false);
+
+ perf_hpp__format[PERF_HPP__OVERHEAD].color =
+ perf_gtk__hpp_color_overhead;
+ perf_hpp__format[PERF_HPP__OVERHEAD_SYS].color =
+ perf_gtk__hpp_color_overhead_sys;
+ perf_hpp__format[PERF_HPP__OVERHEAD_US].color =
+ perf_gtk__hpp_color_overhead_us;
+ perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].color =
+ perf_gtk__hpp_color_overhead_guest_sys;
+ perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].color =
+ perf_gtk__hpp_color_overhead_guest_us;
+}
+
static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
{
GType col_types[MAX_COLUMNS];
@@ -42,15 +94,25 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
struct sort_entry *se;
GtkListStore *store;
struct rb_node *nd;
- u64 total_period;
GtkWidget *view;
- int col_idx;
+ int i, col_idx;
int nr_cols;
+ char s[512];
+
+ struct perf_hpp hpp = {
+ .buf = s,
+ .size = sizeof(s),
+ .total_period = hists->stats.total_period,
+ };
nr_cols = 0;
- /* The percentage column */
- col_types[nr_cols++] = G_TYPE_STRING;
+ for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
+ if (!perf_hpp__format[i].cond)
+ continue;
+
+ col_types[nr_cols++] = G_TYPE_STRING;
+ }
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
@@ -67,11 +129,17 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
col_idx = 0;
- /* The percentage column */
- gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
- -1, "Overhead (%)",
- renderer, "text",
- col_idx++, NULL);
+ for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
+ if (!perf_hpp__format[i].cond)
+ continue;
+
+ perf_hpp__format[i].header(&hpp);
+
+ gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
+ -1, s,
+ renderer, "markup",
+ col_idx++, NULL);
+ }
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
@@ -87,13 +155,9 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
g_object_unref(GTK_TREE_MODEL(store));
- total_period = hists->stats.total_period;
-
for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
GtkTreeIter iter;
- double percent;
- char s[512];
if (h->filtered)
continue;
@@ -102,11 +166,17 @@ static void perf_gtk__show_hists(GtkWidget *window, struct hists *hists)
col_idx = 0;
- percent = (h->period * 100.0) / total_period;
+ for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
+ if (!perf_hpp__format[i].cond)
+ continue;
- snprintf(s, ARRAY_SIZE(s), "%.2f", percent);
+ if (perf_hpp__format[i].color)
+ perf_hpp__format[i].color(&hpp, h);
+ else
+ perf_hpp__format[i].entry(&hpp, h);
- gtk_list_store_set(store, &iter, col_idx++, s, -1);
+ gtk_list_store_set(store, &iter, col_idx++, s, -1);
+ }
list_for_each_entry(se, &hist_entry__sort_list, list) {
if (se->elide)
@@ -166,9 +236,10 @@ static GtkWidget *perf_gtk__setup_statusbar(void)
}
int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
- const char *help __used,
- void (*timer) (void *arg)__used,
- void *arg __used, int delay_secs __used)
+ const char *help,
+ void (*timer) (void *arg)__maybe_unused,
+ void *arg __maybe_unused,
+ int delay_secs __maybe_unused)
{
struct perf_evsel *pos;
GtkWidget *vbox;
@@ -233,6 +304,8 @@ int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist,
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
+ ui_helpline__push(help);
+
gtk_main();
perf_gtk__deactivate_context(&pgctx);
diff --git a/tools/perf/ui/gtk/gtk.h b/tools/perf/ui/gtk/gtk.h
index a4d0f2b4a2dc..687af0bba187 100644
--- a/tools/perf/ui/gtk/gtk.h
+++ b/tools/perf/ui/gtk/gtk.h
@@ -29,6 +29,9 @@ static inline bool perf_gtk__is_active_context(struct perf_gtk_context *ctx)
struct perf_gtk_context *perf_gtk__activate_context(GtkWidget *window);
int perf_gtk__deactivate_context(struct perf_gtk_context **ctx);
+void perf_gtk__init_helpline(void);
+void perf_gtk__init_hpp(void);
+
#ifndef HAVE_GTK_INFO_BAR
static inline GtkWidget *perf_gtk__setup_info_bar(void)
{
diff --git a/tools/perf/ui/gtk/helpline.c b/tools/perf/ui/gtk/helpline.c
new file mode 100644
index 000000000000..5db4432ff12a
--- /dev/null
+++ b/tools/perf/ui/gtk/helpline.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "gtk.h"
+#include "../ui.h"
+#include "../helpline.h"
+#include "../../util/debug.h"
+
+static void gtk_helpline_pop(void)
+{
+ if (!perf_gtk__is_active_context(pgctx))
+ return;
+
+ gtk_statusbar_pop(GTK_STATUSBAR(pgctx->statbar),
+ pgctx->statbar_ctx_id);
+}
+
+static void gtk_helpline_push(const char *msg)
+{
+ if (!perf_gtk__is_active_context(pgctx))
+ return;
+
+ gtk_statusbar_push(GTK_STATUSBAR(pgctx->statbar),
+ pgctx->statbar_ctx_id, msg);
+}
+
+static struct ui_helpline gtk_helpline_fns = {
+ .pop = gtk_helpline_pop,
+ .push = gtk_helpline_push,
+};
+
+void perf_gtk__init_helpline(void)
+{
+ helpline_fns = &gtk_helpline_fns;
+}
+
+int perf_gtk__show_helpline(const char *fmt, va_list ap)
+{
+ int ret;
+ char *ptr;
+ static int backlog;
+
+ ret = vscnprintf(ui_helpline__current + backlog,
+ sizeof(ui_helpline__current) - backlog, fmt, ap);
+ backlog += ret;
+
+ /* only first line can be displayed */
+ ptr = strchr(ui_helpline__current, '\n');
+ if (ptr && (ptr - ui_helpline__current) <= backlog) {
+ *ptr = '\0';
+ ui_helpline__puts(ui_helpline__current);
+ backlog = 0;
+ }
+
+ return ret;
+}
diff --git a/tools/perf/ui/gtk/setup.c b/tools/perf/ui/gtk/setup.c
index 92879ce61e2f..3c4c6ef78283 100644
--- a/tools/perf/ui/gtk/setup.c
+++ b/tools/perf/ui/gtk/setup.c
@@ -7,11 +7,15 @@ extern struct perf_error_ops perf_gtk_eops;
int perf_gtk__init(void)
{
perf_error__register(&perf_gtk_eops);
+ perf_gtk__init_helpline();
+ perf_gtk__init_hpp();
return gtk_init_check(NULL, NULL) ? 0 : -1;
}
-void perf_gtk__exit(bool wait_for_ok __used)
+void perf_gtk__exit(bool wait_for_ok __maybe_unused)
{
+ if (!perf_gtk__is_active_context(pgctx))
+ return;
perf_error__unregister(&perf_gtk_eops);
gtk_main_quit();
}
diff --git a/tools/perf/ui/gtk/util.c b/tools/perf/ui/gtk/util.c
index 0ead373c0dfb..8aada5b3c04c 100644
--- a/tools/perf/ui/gtk/util.c
+++ b/tools/perf/ui/gtk/util.c
@@ -117,13 +117,8 @@ struct perf_error_ops perf_gtk_eops = {
* For now, just add stubs for NO_NEWT=1 build.
*/
#ifdef NO_NEWT_SUPPORT
-int ui_helpline__show_help(const char *format __used, va_list ap __used)
-{
- return 0;
-}
-
-void ui_progress__update(u64 curr __used, u64 total __used,
- const char *title __used)
+void ui_progress__update(u64 curr __maybe_unused, u64 total __maybe_unused,
+ const char *title __maybe_unused)
{
}
#endif
diff --git a/tools/perf/ui/helpline.c b/tools/perf/ui/helpline.c
index 2f950c2641c8..a49bcf3c190b 100644
--- a/tools/perf/ui/helpline.c
+++ b/tools/perf/ui/helpline.c
@@ -5,23 +5,32 @@
#include "../debug.h"
#include "helpline.h"
#include "ui.h"
-#include "libslang.h"
-void ui_helpline__pop(void)
+char ui_helpline__current[512];
+
+static void nop_helpline__pop(void)
{
}
-char ui_helpline__current[512];
+static void nop_helpline__push(const char *msg __maybe_unused)
+{
+}
-void ui_helpline__push(const char *msg)
+static struct ui_helpline default_helpline_fns = {
+ .pop = nop_helpline__pop,
+ .push = nop_helpline__push,
+};
+
+struct ui_helpline *helpline_fns = &default_helpline_fns;
+
+void ui_helpline__pop(void)
{
- const size_t sz = sizeof(ui_helpline__current);
+ helpline_fns->pop();
+}
- SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
- SLsmg_set_color(0);
- SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols);
- SLsmg_refresh();
- strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0';
+void ui_helpline__push(const char *msg)
+{
+ helpline_fns->push(msg);
}
void ui_helpline__vpush(const char *fmt, va_list ap)
@@ -50,30 +59,3 @@ void ui_helpline__puts(const char *msg)
ui_helpline__pop();
ui_helpline__push(msg);
}
-
-void ui_helpline__init(void)
-{
- ui_helpline__puts(" ");
-}
-
-char ui_helpline__last_msg[1024];
-
-int ui_helpline__show_help(const char *format, va_list ap)
-{
- int ret;
- static int backlog;
-
- pthread_mutex_lock(&ui__lock);
- ret = vscnprintf(ui_helpline__last_msg + backlog,
- sizeof(ui_helpline__last_msg) - backlog, format, ap);
- backlog += ret;
-
- if (ui_helpline__last_msg[backlog - 1] == '\n') {
- ui_helpline__puts(ui_helpline__last_msg);
- SLsmg_refresh();
- backlog = 0;
- }
- pthread_mutex_unlock(&ui__lock);
-
- return ret;
-}
diff --git a/tools/perf/ui/helpline.h b/tools/perf/ui/helpline.h
index 7bab6b34e35e..2b667ee454c3 100644
--- a/tools/perf/ui/helpline.h
+++ b/tools/perf/ui/helpline.h
@@ -4,13 +4,44 @@
#include <stdio.h>
#include <stdarg.h>
+#include "../util/cache.h"
+
+struct ui_helpline {
+ void (*pop)(void);
+ void (*push)(const char *msg);
+};
+
+extern struct ui_helpline *helpline_fns;
+
void ui_helpline__init(void);
+
void ui_helpline__pop(void);
void ui_helpline__push(const char *msg);
void ui_helpline__vpush(const char *fmt, va_list ap);
void ui_helpline__fpush(const char *fmt, ...);
void ui_helpline__puts(const char *msg);
-extern char ui_helpline__current[];
+extern char ui_helpline__current[512];
+
+#ifdef NO_NEWT_SUPPORT
+static inline int ui_helpline__show_help(const char *format __maybe_unused,
+ va_list ap __maybe_unused)
+{
+ return 0;
+}
+#else
+extern char ui_helpline__last_msg[];
+int ui_helpline__show_help(const char *format, va_list ap);
+#endif /* NO_NEWT_SUPPORT */
+
+#ifdef NO_GTK2_SUPPORT
+static inline int perf_gtk__show_helpline(const char *format __maybe_unused,
+ va_list ap __maybe_unused)
+{
+ return 0;
+}
+#else
+int perf_gtk__show_helpline(const char *format, va_list ap);
+#endif /* NO_GTK2_SUPPORT */
#endif /* _PERF_UI_HELPLINE_H_ */
diff --git a/tools/perf/ui/hist.c b/tools/perf/ui/hist.c
new file mode 100644
index 000000000000..e3f8cd46e7d7
--- /dev/null
+++ b/tools/perf/ui/hist.c
@@ -0,0 +1,390 @@
+#include <math.h>
+
+#include "../util/hist.h"
+#include "../util/util.h"
+#include "../util/sort.h"
+
+
+/* hist period print (hpp) functions */
+static int hpp__header_overhead(struct perf_hpp *hpp)
+{
+ const char *fmt = hpp->ptr ? "Baseline" : "Overhead";
+
+ return scnprintf(hpp->buf, hpp->size, fmt);
+}
+
+static int hpp__width_overhead(struct perf_hpp *hpp __maybe_unused)
+{
+ return 8;
+}
+
+static int hpp__color_overhead(struct perf_hpp *hpp, struct hist_entry *he)
+{
+ double percent = 100.0 * he->period / hpp->total_period;
+
+ if (hpp->ptr) {
+ struct hists *old_hists = hpp->ptr;
+ u64 total_period = old_hists->stats.total_period;
+ u64 base_period = he->pair ? he->pair->period : 0;
+
+ if (total_period)
+ percent = 100.0 * base_period / total_period;
+ else
+ percent = 0.0;
+ }
+
+ return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%%", percent);
+}
+
+static int hpp__entry_overhead(struct perf_hpp *hpp, struct hist_entry *he)
+{
+ double percent = 100.0 * he->period / hpp->total_period;
+ const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%%";
+
+ if (hpp->ptr) {
+ struct hists *old_hists = hpp->ptr;
+ u64 total_period = old_hists->stats.total_period;
+ u64 base_period = he->pair ? he->pair->period : 0;
+
+ if (total_period)
+ percent = 100.0 * base_period / total_period;
+ else
+ percent = 0.0;
+ }
+
+ return scnprintf(hpp->buf, hpp->size, fmt, percent);
+}
+
+static int hpp__header_overhead_sys(struct perf_hpp *hpp)
+{
+ const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
+
+ return scnprintf(hpp->buf, hpp->size, fmt, "sys");
+}
+
+static int hpp__width_overhead_sys(struct perf_hpp *hpp __maybe_unused)
+{
+ return 7;
+}
+
+static int hpp__color_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he)
+{
+ double percent = 100.0 * he->period_sys / hpp->total_period;
+ return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
+}
+
+static int hpp__entry_overhead_sys(struct perf_hpp *hpp, struct hist_entry *he)
+{
+ double percent = 100.0 * he->period_sys / hpp->total_period;
+ const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
+
+ return scnprintf(hpp->buf, hpp->size, fmt, percent);
+}
+
+static int hpp__header_overhead_us(struct perf_hpp *hpp)
+{
+ const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
+
+ return scnprintf(hpp->buf, hpp->size, fmt, "user");
+}
+
+static int hpp__width_overhead_us(struct perf_hpp *hpp __maybe_unused)
+{
+ return 7;
+}
+
+static int hpp__color_overhead_us(struct perf_hpp *hpp, struct hist_entry *he)
+{
+ double percent = 100.0 * he->period_us / hpp->total_period;
+ return percent_color_snprintf(hpp->buf, hpp->size, "%6.2f%%", percent);
+}
+
+static int hpp__entry_overhead_us(struct perf_hpp *hpp, struct hist_entry *he)
+{
+ double percent = 100.0 * he->period_us / hpp->total_period;
+ const char *fmt = symbol_conf.field_sep ? "%.2f" : "%6.2f%%";
+
+ return scnprintf(hpp->buf, hpp->size, fmt, percent);
+}
+
+static int hpp__header_overhead_guest_sys(struct perf_hpp *hpp)
+{
+ return scnprintf(hpp->buf, hpp->size, "guest sys");
+}
+
+static int hpp__width_overhead_guest_sys(struct perf_hpp *hpp __maybe_unused)
+{
+ return 9;
+}
+
+static int hpp__color_overhead_guest_sys(struct perf_hpp *hpp,
+ struct hist_entry *he)
+{
+ double percent = 100.0 * he->period_guest_sys / hpp->total_period;
+ return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent);
+}
+
+static int hpp__entry_overhead_guest_sys(struct perf_hpp *hpp,
+ struct hist_entry *he)
+{
+ double percent = 100.0 * he->period_guest_sys / hpp->total_period;
+ const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% ";
+
+ return scnprintf(hpp->buf, hpp->size, fmt, percent);
+}
+
+static int hpp__header_overhead_guest_us(struct perf_hpp *hpp)
+{
+ return scnprintf(hpp->buf, hpp->size, "guest usr");
+}
+
+static int hpp__width_overhead_guest_us(struct perf_hpp *hpp __maybe_unused)
+{
+ return 9;
+}
+
+static int hpp__color_overhead_guest_us(struct perf_hpp *hpp,
+ struct hist_entry *he)
+{
+ double percent = 100.0 * he->period_guest_us / hpp->total_period;
+ return percent_color_snprintf(hpp->buf, hpp->size, " %6.2f%% ", percent);
+}
+
+static int hpp__entry_overhead_guest_us(struct perf_hpp *hpp,
+ struct hist_entry *he)
+{
+ double percent = 100.0 * he->period_guest_us / hpp->total_period;
+ const char *fmt = symbol_conf.field_sep ? "%.2f" : " %6.2f%% ";
+
+ return scnprintf(hpp->buf, hpp->size, fmt, percent);
+}
+
+static int hpp__header_samples(struct perf_hpp *hpp)
+{
+ const char *fmt = symbol_conf.field_sep ? "%s" : "%11s";
+
+ return scnprintf(hpp->buf, hpp->size, fmt, "Samples");
+}
+
+static int hpp__width_samples(struct perf_hpp *hpp __maybe_unused)
+{
+ return 11;
+}
+
+static int hpp__entry_samples(struct perf_hpp *hpp, struct hist_entry *he)
+{
+ const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%11" PRIu64;
+
+ return scnprintf(hpp->buf, hpp->size, fmt, he->nr_events);
+}
+
+static int hpp__header_period(struct perf_hpp *hpp)
+{
+ const char *fmt = symbol_conf.field_sep ? "%s" : "%12s";
+
+ return scnprintf(hpp->buf, hpp->size, fmt, "Period");
+}
+
+static int hpp__width_period(struct perf_hpp *hpp __maybe_unused)
+{
+ return 12;
+}
+
+static int hpp__entry_period(struct perf_hpp *hpp, struct hist_entry *he)
+{
+ const char *fmt = symbol_conf.field_sep ? "%" PRIu64 : "%12" PRIu64;
+
+ return scnprintf(hpp->buf, hpp->size, fmt, he->period);
+}
+
+static int hpp__header_delta(struct perf_hpp *hpp)
+{
+ const char *fmt = symbol_conf.field_sep ? "%s" : "%7s";
+
+ return scnprintf(hpp->buf, hpp->size, fmt, "Delta");
+}
+
+static int hpp__width_delta(struct perf_hpp *hpp __maybe_unused)
+{
+ return 7;
+}
+
+static int hpp__entry_delta(struct perf_hpp *hpp, struct hist_entry *he)
+{
+ struct hists *pair_hists = hpp->ptr;
+ u64 old_total, new_total;
+ double old_percent = 0, new_percent = 0;
+ double diff;
+ const char *fmt = symbol_conf.field_sep ? "%s" : "%7.7s";
+ char buf[32] = " ";
+
+ old_total = pair_hists->stats.total_period;
+ if (old_total > 0 && he->pair)
+ old_percent = 100.0 * he->pair->period / old_total;
+
+ new_total = hpp->total_period;
+ if (new_total > 0)
+ new_percent = 100.0 * he->period / new_total;
+
+ diff = new_percent - old_percent;
+ if (fabs(diff) >= 0.01)
+ scnprintf(buf, sizeof(buf), "%+4.2F%%", diff);
+
+ return scnprintf(hpp->buf, hpp->size, fmt, buf);
+}
+
+static int hpp__header_displ(struct perf_hpp *hpp)
+{
+ return scnprintf(hpp->buf, hpp->size, "Displ.");
+}
+
+static int hpp__width_displ(struct perf_hpp *hpp __maybe_unused)
+{
+ return 6;
+}
+
+static int hpp__entry_displ(struct perf_hpp *hpp,
+ struct hist_entry *he __maybe_unused)
+{
+ const char *fmt = symbol_conf.field_sep ? "%s" : "%6.6s";
+ char buf[32] = " ";
+
+ if (hpp->displacement)
+ scnprintf(buf, sizeof(buf), "%+4ld", hpp->displacement);
+
+ return scnprintf(hpp->buf, hpp->size, fmt, buf);
+}
+
+#define HPP__COLOR_PRINT_FNS(_name) \
+ .header = hpp__header_ ## _name, \
+ .width = hpp__width_ ## _name, \
+ .color = hpp__color_ ## _name, \
+ .entry = hpp__entry_ ## _name
+
+#define HPP__PRINT_FNS(_name) \
+ .header = hpp__header_ ## _name, \
+ .width = hpp__width_ ## _name, \
+ .entry = hpp__entry_ ## _name
+
+struct perf_hpp_fmt perf_hpp__format[] = {
+ { .cond = true, HPP__COLOR_PRINT_FNS(overhead) },
+ { .cond = false, HPP__COLOR_PRINT_FNS(overhead_sys) },
+ { .cond = false, HPP__COLOR_PRINT_FNS(overhead_us) },
+ { .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_sys) },
+ { .cond = false, HPP__COLOR_PRINT_FNS(overhead_guest_us) },
+ { .cond = false, HPP__PRINT_FNS(samples) },
+ { .cond = false, HPP__PRINT_FNS(period) },
+ { .cond = false, HPP__PRINT_FNS(delta) },
+ { .cond = false, HPP__PRINT_FNS(displ) }
+};
+
+#undef HPP__COLOR_PRINT_FNS
+#undef HPP__PRINT_FNS
+
+void perf_hpp__init(bool need_pair, bool show_displacement)
+{
+ if (symbol_conf.show_cpu_utilization) {
+ perf_hpp__format[PERF_HPP__OVERHEAD_SYS].cond = true;
+ perf_hpp__format[PERF_HPP__OVERHEAD_US].cond = true;
+
+ if (perf_guest) {
+ perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_SYS].cond = true;
+ perf_hpp__format[PERF_HPP__OVERHEAD_GUEST_US].cond = true;
+ }
+ }
+
+ if (symbol_conf.show_nr_samples)
+ perf_hpp__format[PERF_HPP__SAMPLES].cond = true;
+
+ if (symbol_conf.show_total_period)
+ perf_hpp__format[PERF_HPP__PERIOD].cond = true;
+
+ if (need_pair) {
+ perf_hpp__format[PERF_HPP__DELTA].cond = true;
+
+ if (show_displacement)
+ perf_hpp__format[PERF_HPP__DISPL].cond = true;
+ }
+}
+
+static inline void advance_hpp(struct perf_hpp *hpp, int inc)
+{
+ hpp->buf += inc;
+ hpp->size -= inc;
+}
+
+int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,
+ bool color)
+{
+ const char *sep = symbol_conf.field_sep;
+ char *start = hpp->buf;
+ int i, ret;
+
+ if (symbol_conf.exclude_other && !he->parent)
+ return 0;
+
+ for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
+ if (!perf_hpp__format[i].cond)
+ continue;
+
+ if (!sep || i > 0) {
+ ret = scnprintf(hpp->buf, hpp->size, "%s", sep ?: " ");
+ advance_hpp(hpp, ret);
+ }
+
+ if (color && perf_hpp__format[i].color)
+ ret = perf_hpp__format[i].color(hpp, he);
+ else
+ ret = perf_hpp__format[i].entry(hpp, he);
+
+ advance_hpp(hpp, ret);
+ }
+
+ return hpp->buf - start;
+}
+
+int hist_entry__sort_snprintf(struct hist_entry *he, char *s, size_t size,
+ struct hists *hists)
+{
+ const char *sep = symbol_conf.field_sep;
+ struct sort_entry *se;
+ int ret = 0;
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+
+ ret += scnprintf(s + ret, size - ret, "%s", sep ?: " ");
+ ret += se->se_snprintf(he, s + ret, size - ret,
+ hists__col_len(hists, se->se_width_idx));
+ }
+
+ return ret;
+}
+
+/*
+ * See hists__fprintf to match the column widths
+ */
+unsigned int hists__sort_list_width(struct hists *hists)
+{
+ struct sort_entry *se;
+ int i, ret = 0;
+
+ for (i = 0; i < PERF_HPP__MAX_INDEX; i++) {
+ if (!perf_hpp__format[i].cond)
+ continue;
+ if (i)
+ ret += 2;
+
+ ret += perf_hpp__format[i].width(NULL);
+ }
+
+ list_for_each_entry(se, &hist_entry__sort_list, list)
+ if (!se->elide)
+ ret += 2 + hists__col_len(hists, se->se_width_idx);
+
+ if (verbose) /* Addr + origin */
+ ret += 3 + BITS_PER_LONG / 4;
+
+ return ret;
+}
diff --git a/tools/perf/ui/setup.c b/tools/perf/ui/setup.c
index 791fb15ce350..bd7d460f844c 100644
--- a/tools/perf/ui/setup.c
+++ b/tools/perf/ui/setup.c
@@ -1,6 +1,10 @@
-#include "../cache.h"
-#include "../debug.h"
+#include <pthread.h>
+#include "../util/cache.h"
+#include "../util/debug.h"
+#include "../util/hist.h"
+
+pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
void setup_browser(bool fallback_to_pager)
{
@@ -25,6 +29,8 @@ void setup_browser(bool fallback_to_pager)
use_browser = 0;
if (fallback_to_pager)
setup_pager();
+
+ perf_hpp__init(false, false);
break;
}
}
diff --git a/tools/perf/ui/stdio/hist.c b/tools/perf/ui/stdio/hist.c
new file mode 100644
index 000000000000..882461a42830
--- /dev/null
+++ b/tools/perf/ui/stdio/hist.c
@@ -0,0 +1,498 @@
+#include <stdio.h>
+
+#include "../../util/util.h"
+#include "../../util/hist.h"
+#include "../../util/sort.h"
+
+
+static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
+{
+ int i;
+ int ret = fprintf(fp, " ");
+
+ for (i = 0; i < left_margin; i++)
+ ret += fprintf(fp, " ");
+
+ return ret;
+}
+
+static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
+ int left_margin)
+{
+ int i;
+ size_t ret = callchain__fprintf_left_margin(fp, left_margin);
+
+ for (i = 0; i < depth; i++)
+ if (depth_mask & (1 << i))
+ ret += fprintf(fp, "| ");
+ else
+ ret += fprintf(fp, " ");
+
+ ret += fprintf(fp, "\n");
+
+ return ret;
+}
+
+static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
+ int depth, int depth_mask, int period,
+ u64 total_samples, u64 hits,
+ int left_margin)
+{
+ int i;
+ size_t ret = 0;
+
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ for (i = 0; i < depth; i++) {
+ if (depth_mask & (1 << i))
+ ret += fprintf(fp, "|");
+ else
+ ret += fprintf(fp, " ");
+ if (!period && i == depth - 1) {
+ double percent;
+
+ percent = hits * 100.0 / total_samples;
+ ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
+ } else
+ ret += fprintf(fp, "%s", " ");
+ }
+ if (chain->ms.sym)
+ ret += fprintf(fp, "%s\n", chain->ms.sym->name);
+ else
+ ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip);
+
+ return ret;
+}
+
+static struct symbol *rem_sq_bracket;
+static struct callchain_list rem_hits;
+
+static void init_rem_hits(void)
+{
+ rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
+ if (!rem_sq_bracket) {
+ fprintf(stderr, "Not enough memory to display remaining hits\n");
+ return;
+ }
+
+ strcpy(rem_sq_bracket->name, "[...]");
+ rem_hits.ms.sym = rem_sq_bracket;
+}
+
+static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
+ u64 total_samples, int depth,
+ int depth_mask, int left_margin)
+{
+ struct rb_node *node, *next;
+ struct callchain_node *child;
+ struct callchain_list *chain;
+ int new_depth_mask = depth_mask;
+ u64 remaining;
+ size_t ret = 0;
+ int i;
+ uint entries_printed = 0;
+
+ remaining = total_samples;
+
+ node = rb_first(root);
+ while (node) {
+ u64 new_total;
+ u64 cumul;
+
+ child = rb_entry(node, struct callchain_node, rb_node);
+ cumul = callchain_cumul_hits(child);
+ remaining -= cumul;
+
+ /*
+ * The depth mask manages the output of pipes that show
+ * the depth. We don't want to keep the pipes of the current
+ * level for the last child of this depth.
+ * Except if we have remaining filtered hits. They will
+ * supersede the last child
+ */
+ next = rb_next(node);
+ if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
+ new_depth_mask &= ~(1 << (depth - 1));
+
+ /*
+ * But we keep the older depth mask for the line separator
+ * to keep the level link until we reach the last child
+ */
+ ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
+ left_margin);
+ i = 0;
+ list_for_each_entry(chain, &child->val, list) {
+ ret += ipchain__fprintf_graph(fp, chain, depth,
+ new_depth_mask, i++,
+ total_samples,
+ cumul,
+ left_margin);
+ }
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL)
+ new_total = child->children_hit;
+ else
+ new_total = total_samples;
+
+ ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
+ depth + 1,
+ new_depth_mask | (1 << depth),
+ left_margin);
+ node = next;
+ if (++entries_printed == callchain_param.print_limit)
+ break;
+ }
+
+ if (callchain_param.mode == CHAIN_GRAPH_REL &&
+ remaining && remaining != total_samples) {
+
+ if (!rem_sq_bracket)
+ return ret;
+
+ new_depth_mask &= ~(1 << (depth - 1));
+ ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
+ new_depth_mask, 0, total_samples,
+ remaining, left_margin);
+ }
+
+ return ret;
+}
+
+static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
+ u64 total_samples, int left_margin)
+{
+ struct callchain_node *cnode;
+ struct callchain_list *chain;
+ u32 entries_printed = 0;
+ bool printed = false;
+ struct rb_node *node;
+ int i = 0;
+ int ret = 0;
+
+ /*
+ * If have one single callchain root, don't bother printing
+ * its percentage (100 % in fractal mode and the same percentage
+ * than the hist in graph mode). This also avoid one level of column.
+ */
+ node = rb_first(root);
+ if (node && !rb_next(node)) {
+ cnode = rb_entry(node, struct callchain_node, rb_node);
+ list_for_each_entry(chain, &cnode->val, list) {
+ /*
+ * If we sort by symbol, the first entry is the same than
+ * the symbol. No need to print it otherwise it appears as
+ * displayed twice.
+ */
+ if (!i++ && sort__first_dimension == SORT_SYM)
+ continue;
+ if (!printed) {
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ ret += fprintf(fp, "|\n");
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+ ret += fprintf(fp, "---");
+ left_margin += 3;
+ printed = true;
+ } else
+ ret += callchain__fprintf_left_margin(fp, left_margin);
+
+ if (chain->ms.sym)
+ ret += fprintf(fp, " %s\n", chain->ms.sym->name);
+ else
+ ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
+
+ if (++entries_printed == callchain_param.print_limit)
+ break;
+ }
+ root = &cnode->rb_root;
+ }
+
+ ret += __callchain__fprintf_graph(fp, root, total_samples,
+ 1, 1, left_margin);
+ ret += fprintf(fp, "\n");
+
+ return ret;
+}
+
+static size_t __callchain__fprintf_flat(FILE *fp,
+ struct callchain_node *self,
+ u64 total_samples)
+{
+ struct callchain_list *chain;
+ size_t ret = 0;
+
+ if (!self)
+ return 0;
+
+ ret += __callchain__fprintf_flat(fp, self->parent, total_samples);
+
+
+ list_for_each_entry(chain, &self->val, list) {
+ if (chain->ip >= PERF_CONTEXT_MAX)
+ continue;
+ if (chain->ms.sym)
+ ret += fprintf(fp, " %s\n", chain->ms.sym->name);
+ else
+ ret += fprintf(fp, " %p\n",
+ (void *)(long)chain->ip);
+ }
+
+ return ret;
+}
+
+static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
+ u64 total_samples)
+{
+ size_t ret = 0;
+ u32 entries_printed = 0;
+ struct rb_node *rb_node;
+ struct callchain_node *chain;
+
+ rb_node = rb_first(self);
+ while (rb_node) {
+ double percent;
+
+ chain = rb_entry(rb_node, struct callchain_node, rb_node);
+ percent = chain->hit * 100.0 / total_samples;
+
+ ret = percent_color_fprintf(fp, " %6.2f%%\n", percent);
+ ret += __callchain__fprintf_flat(fp, chain, total_samples);
+ ret += fprintf(fp, "\n");
+ if (++entries_printed == callchain_param.print_limit)
+ break;
+
+ rb_node = rb_next(rb_node);
+ }
+
+ return ret;
+}
+
+static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
+ u64 total_samples, int left_margin,
+ FILE *fp)
+{
+ switch (callchain_param.mode) {
+ case CHAIN_GRAPH_REL:
+ return callchain__fprintf_graph(fp, &he->sorted_chain, he->period,
+ left_margin);
+ break;
+ case CHAIN_GRAPH_ABS:
+ return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
+ left_margin);
+ break;
+ case CHAIN_FLAT:
+ return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
+ break;
+ case CHAIN_NONE:
+ break;
+ default:
+ pr_err("Bad callchain mode\n");
+ }
+
+ return 0;
+}
+
+static size_t hist_entry__callchain_fprintf(struct hist_entry *he,
+ struct hists *hists,
+ u64 total_period, FILE *fp)
+{
+ int left_margin = 0;
+
+ if (sort__first_dimension == SORT_COMM) {
+ struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
+ typeof(*se), list);
+ left_margin = hists__col_len(hists, se->se_width_idx);
+ left_margin -= thread__comm_len(he->thread);
+ }
+
+ return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
+}
+
+static int hist_entry__fprintf(struct hist_entry *he, size_t size,
+ struct hists *hists, struct hists *pair_hists,
+ long displacement, u64 total_period, FILE *fp)
+{
+ char bf[512];
+ int ret;
+ struct perf_hpp hpp = {
+ .buf = bf,
+ .size = size,
+ .total_period = total_period,
+ .displacement = displacement,
+ .ptr = pair_hists,
+ };
+ bool color = !symbol_conf.field_sep;
+
+ if (size == 0 || size > sizeof(bf))
+ size = hpp.size = sizeof(bf);
+
+ ret = hist_entry__period_snprintf(&hpp, he, color);
+ hist_entry__sort_snprintf(he, bf + ret, size - ret, hists);
+
+ ret = fprintf(fp, "%s\n", bf);
+
+ if (symbol_conf.use_callchain)
+ ret += hist_entry__callchain_fprintf(he, hists,
+ total_period, fp);
+
+ return ret;
+}
+
+size_t hists__fprintf(struct hists *hists, struct hists *pair,
+ bool show_displacement, bool show_header, int max_rows,
+ int max_cols, FILE *fp)
+{
+ struct sort_entry *se;
+ struct rb_node *nd;
+ size_t ret = 0;
+ u64 total_period;
+ unsigned long position = 1;
+ long displacement = 0;
+ unsigned int width;
+ const char *sep = symbol_conf.field_sep;
+ const char *col_width = symbol_conf.col_width_list_str;
+ int idx, nr_rows = 0;
+ char bf[64];
+ struct perf_hpp dummy_hpp = {
+ .buf = bf,
+ .size = sizeof(bf),
+ .ptr = pair,
+ };
+
+ init_rem_hits();
+
+ if (!show_header)
+ goto print_entries;
+
+ fprintf(fp, "# ");
+ for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
+ if (!perf_hpp__format[idx].cond)
+ continue;
+
+ if (idx)
+ fprintf(fp, "%s", sep ?: " ");
+
+ perf_hpp__format[idx].header(&dummy_hpp);
+ fprintf(fp, "%s", bf);
+ }
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ if (se->elide)
+ continue;
+ if (sep) {
+ fprintf(fp, "%c%s", *sep, se->se_header);
+ continue;
+ }
+ width = strlen(se->se_header);
+ if (symbol_conf.col_width_list_str) {
+ if (col_width) {
+ hists__set_col_len(hists, se->se_width_idx,
+ atoi(col_width));
+ col_width = strchr(col_width, ',');
+ if (col_width)
+ ++col_width;
+ }
+ }
+ if (!hists__new_col_len(hists, se->se_width_idx, width))
+ width = hists__col_len(hists, se->se_width_idx);
+ fprintf(fp, " %*s", width, se->se_header);
+ }
+
+ fprintf(fp, "\n");
+ if (max_rows && ++nr_rows >= max_rows)
+ goto out;
+
+ if (sep)
+ goto print_entries;
+
+ fprintf(fp, "# ");
+ for (idx = 0; idx < PERF_HPP__MAX_INDEX; idx++) {
+ unsigned int i;
+
+ if (!perf_hpp__format[idx].cond)
+ continue;
+
+ if (idx)
+ fprintf(fp, "%s", sep ?: " ");
+
+ width = perf_hpp__format[idx].width(&dummy_hpp);
+ for (i = 0; i < width; i++)
+ fprintf(fp, ".");
+ }
+
+ list_for_each_entry(se, &hist_entry__sort_list, list) {
+ unsigned int i;
+
+ if (se->elide)
+ continue;
+
+ fprintf(fp, " ");
+ width = hists__col_len(hists, se->se_width_idx);
+ if (width == 0)
+ width = strlen(se->se_header);
+ for (i = 0; i < width; i++)
+ fprintf(fp, ".");
+ }
+
+ fprintf(fp, "\n");
+ if (max_rows && ++nr_rows >= max_rows)
+ goto out;
+
+ fprintf(fp, "#\n");
+ if (max_rows && ++nr_rows >= max_rows)
+ goto out;
+
+print_entries:
+ total_period = hists->stats.total_period;
+
+ for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
+ struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
+
+ if (h->filtered)
+ continue;
+
+ if (show_displacement) {
+ if (h->pair != NULL)
+ displacement = ((long)h->pair->position -
+ (long)position);
+ else
+ displacement = 0;
+ ++position;
+ }
+ ret += hist_entry__fprintf(h, max_cols, hists, pair, displacement,
+ total_period, fp);
+
+ if (max_rows && ++nr_rows >= max_rows)
+ goto out;
+
+ if (h->ms.map == NULL && verbose > 1) {
+ __map_groups__fprintf_maps(&h->thread->mg,
+ MAP__FUNCTION, verbose, fp);
+ fprintf(fp, "%.10s end\n", graph_dotted_line);
+ }
+ }
+out:
+ free(rem_sq_bracket);
+
+ return ret;
+}
+
+size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
+{
+ int i;
+ size_t ret = 0;
+
+ for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
+ const char *name;
+
+ if (hists->stats.nr_events[i] == 0)
+ continue;
+
+ name = perf_event__name(i);
+ if (!strcmp(name, "UNKNOWN"))
+ continue;
+
+ ret += fprintf(fp, "%16s events: %10d\n", name,
+ hists->stats.nr_events[i]);
+ }
+
+ return ret;
+}
diff --git a/tools/perf/ui/tui/helpline.c b/tools/perf/ui/tui/helpline.c
new file mode 100644
index 000000000000..2884d2f41e33
--- /dev/null
+++ b/tools/perf/ui/tui/helpline.c
@@ -0,0 +1,57 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+
+#include "../../util/debug.h"
+#include "../helpline.h"
+#include "../ui.h"
+#include "../libslang.h"
+
+static void tui_helpline__pop(void)
+{
+}
+
+static void tui_helpline__push(const char *msg)
+{
+ const size_t sz = sizeof(ui_helpline__current);
+
+ SLsmg_gotorc(SLtt_Screen_Rows - 1, 0);
+ SLsmg_set_color(0);
+ SLsmg_write_nstring((char *)msg, SLtt_Screen_Cols);
+ SLsmg_refresh();
+ strncpy(ui_helpline__current, msg, sz)[sz - 1] = '\0';
+}
+
+struct ui_helpline tui_helpline_fns = {
+ .pop = tui_helpline__pop,
+ .push = tui_helpline__push,
+};
+
+void ui_helpline__init(void)
+{
+ helpline_fns = &tui_helpline_fns;
+ ui_helpline__puts(" ");
+}
+
+char ui_helpline__last_msg[1024];
+
+int ui_helpline__show_help(const char *format, va_list ap)
+{
+ int ret;
+ static int backlog;
+
+ pthread_mutex_lock(&ui__lock);
+ ret = vscnprintf(ui_helpline__last_msg + backlog,
+ sizeof(ui_helpline__last_msg) - backlog, format, ap);
+ backlog += ret;
+
+ if (ui_helpline__last_msg[backlog - 1] == '\n') {
+ ui_helpline__puts(ui_helpline__last_msg);
+ SLsmg_refresh();
+ backlog = 0;
+ }
+ pthread_mutex_unlock(&ui__lock);
+
+ return ret;
+}
diff --git a/tools/perf/ui/tui/setup.c b/tools/perf/ui/tui/setup.c
index e813c1d17346..60debb81537a 100644
--- a/tools/perf/ui/tui/setup.c
+++ b/tools/perf/ui/tui/setup.c
@@ -11,12 +11,12 @@
#include "../libslang.h"
#include "../keysyms.h"
-pthread_mutex_t ui__lock = PTHREAD_MUTEX_INITIALIZER;
-
static volatile int ui__need_resize;
extern struct perf_error_ops perf_tui_eops;
+extern void hist_browser__init_hpp(void);
+
void ui__refresh_dimensions(bool force)
{
if (force || ui__need_resize) {
@@ -28,7 +28,7 @@ void ui__refresh_dimensions(bool force)
}
}
-static void ui__sigwinch(int sig __used)
+static void ui__sigwinch(int sig __maybe_unused)
{
ui__need_resize = 1;
}
@@ -88,7 +88,7 @@ int ui__getch(int delay_secs)
return SLkp_getkey();
}
-static void newt_suspend(void *d __used)
+static void newt_suspend(void *d __maybe_unused)
{
newtSuspend();
raise(SIGTSTP);
@@ -126,6 +126,8 @@ int ui__init(void)
signal(SIGTERM, ui__signal);
perf_error__register(&perf_tui_eops);
+
+ hist_browser__init_hpp();
out:
return err;
}
diff --git a/tools/perf/util/alias.c b/tools/perf/util/alias.c
index b8144e80bb1e..e6d134773d0a 100644
--- a/tools/perf/util/alias.c
+++ b/tools/perf/util/alias.c
@@ -3,7 +3,8 @@
static const char *alias_key;
static char *alias_val;
-static int alias_lookup_cb(const char *k, const char *v, void *cb __used)
+static int alias_lookup_cb(const char *k, const char *v,
+ void *cb __maybe_unused)
{
if (!prefixcmp(k, "alias.") && !strcmp(k+6, alias_key)) {
if (!v)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 3a282c0057d2..f0a910371377 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -17,6 +17,7 @@
#include <pthread.h>
const char *disassembler_style;
+const char *objdump_path;
static struct ins *ins__find(const char *name);
static int disasm_line__parse(char *line, char **namep, char **rawp);
@@ -312,8 +313,8 @@ static struct ins_ops dec_ops = {
.scnprintf = dec__scnprintf,
};
-static int nop__scnprintf(struct ins *ins __used, char *bf, size_t size,
- struct ins_operands *ops __used)
+static int nop__scnprintf(struct ins *ins __maybe_unused, char *bf, size_t size,
+ struct ins_operands *ops __maybe_unused)
{
return scnprintf(bf, size, "%-6.6s", "nop");
}
@@ -415,7 +416,7 @@ static struct ins *ins__find(const char *name)
return bsearch(name, instructions, nmemb, sizeof(struct ins), ins__cmp);
}
-int symbol__annotate_init(struct map *map __used, struct symbol *sym)
+int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym)
{
struct annotation *notes = symbol__annotation(sym);
pthread_mutex_init(&notes->lock, NULL);
@@ -820,9 +821,10 @@ fallback:
dso, dso->long_name, sym, sym->name);
snprintf(command, sizeof(command),
- "objdump %s%s --start-address=0x%016" PRIx64
+ "%s %s%s --start-address=0x%016" PRIx64
" --stop-address=0x%016" PRIx64
" -d %s %s -C %s|grep -v %s|expand",
+ objdump_path ? objdump_path : "objdump",
disassembler_style ? "-M " : "",
disassembler_style ? disassembler_style : "",
map__rip_2objdump(map, sym->start),
@@ -982,7 +984,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
int context)
{
struct dso *dso = map->dso;
- const char *filename = dso->long_name, *d_filename;
+ char *filename;
+ const char *d_filename;
struct annotation *notes = symbol__annotation(sym);
struct disasm_line *pos, *queue = NULL;
u64 start = map__rip_2objdump(map, sym->start);
@@ -990,6 +993,10 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
int more = 0;
u64 len;
+ filename = strdup(dso->long_name);
+ if (!filename)
+ return -ENOMEM;
+
if (full_paths)
d_filename = filename;
else
@@ -1040,6 +1047,8 @@ int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
}
}
+ free(filename);
+
return more;
}
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 78a5692dd718..9b5b21e7b032 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -7,6 +7,7 @@
#include "symbol.h"
#include <linux/list.h>
#include <linux/rbtree.h>
+#include <pthread.h>
struct ins;
@@ -125,7 +126,7 @@ int symbol__alloc_hist(struct symbol *sym);
void symbol__annotate_zero_histograms(struct symbol *sym);
int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize);
-int symbol__annotate_init(struct map *map __used, struct symbol *sym);
+int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym);
int symbol__annotate_printf(struct symbol *sym, struct map *map, int evidx,
bool full_paths, int min_pcnt, int max_lines,
int context);
@@ -138,11 +139,12 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, int evidx,
int max_lines);
#ifdef NO_NEWT_SUPPORT
-static inline int symbol__tui_annotate(struct symbol *sym __used,
- struct map *map __used,
- int evidx __used,
- void(*timer)(void *arg) __used,
- void *arg __used, int delay_secs __used)
+static inline int symbol__tui_annotate(struct symbol *sym __maybe_unused,
+ struct map *map __maybe_unused,
+ int evidx __maybe_unused,
+ void(*timer)(void *arg) __maybe_unused,
+ void *arg __maybe_unused,
+ int delay_secs __maybe_unused)
{
return 0;
}
@@ -152,5 +154,6 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
#endif
extern const char *disassembler_style;
+extern const char *objdump_path;
#endif /* __PERF_ANNOTATE_H */
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index fd9a5944b627..8e3a740ddbd4 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -16,10 +16,10 @@
#include "session.h"
#include "tool.h"
-static int build_id__mark_dso_hit(struct perf_tool *tool __used,
+static int build_id__mark_dso_hit(struct perf_tool *tool __maybe_unused,
union perf_event *event,
- struct perf_sample *sample __used,
- struct perf_evsel *evsel __used,
+ struct perf_sample *sample __maybe_unused,
+ struct perf_evsel *evsel __maybe_unused,
struct machine *machine)
{
struct addr_location al;
@@ -41,9 +41,10 @@ static int build_id__mark_dso_hit(struct perf_tool *tool __used,
return 0;
}
-static int perf_event__exit_del_thread(struct perf_tool *tool __used,
+static int perf_event__exit_del_thread(struct perf_tool *tool __maybe_unused,
union perf_event *event,
- struct perf_sample *sample __used,
+ struct perf_sample *sample
+ __maybe_unused,
struct machine *machine)
{
struct thread *thread = machine__findnew_thread(machine, event->fork.tid);
diff --git a/tools/perf/util/cache.h b/tools/perf/util/cache.h
index cff18c617d13..ab1769426541 100644
--- a/tools/perf/util/cache.h
+++ b/tools/perf/util/cache.h
@@ -39,7 +39,7 @@ static inline void setup_browser(bool fallback_to_pager)
if (fallback_to_pager)
setup_pager();
}
-static inline void exit_browser(bool wait_for_ok __used) {}
+static inline void exit_browser(bool wait_for_ok __maybe_unused) {}
#else
void setup_browser(bool fallback_to_pager);
void exit_browser(bool wait_for_ok);
@@ -49,7 +49,7 @@ static inline int ui__init(void)
{
return -1;
}
-static inline void ui__exit(bool wait_for_ok __used) {}
+static inline void ui__exit(bool wait_for_ok __maybe_unused) {}
#else
int ui__init(void);
void ui__exit(bool wait_for_ok);
@@ -60,7 +60,7 @@ static inline int perf_gtk__init(void)
{
return -1;
}
-static inline void perf_gtk__exit(bool wait_for_ok __used) {}
+static inline void perf_gtk__exit(bool wait_for_ok __maybe_unused) {}
#else
int perf_gtk__init(void);
void perf_gtk__exit(bool wait_for_ok);
diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c
index 3a6bff47614f..d3b3f5d82137 100644
--- a/tools/perf/util/callchain.c
+++ b/tools/perf/util/callchain.c
@@ -93,7 +93,7 @@ __sort_chain_flat(struct rb_root *rb_root, struct callchain_node *node,
*/
static void
sort_chain_flat(struct rb_root *rb_root, struct callchain_root *root,
- u64 min_hit, struct callchain_param *param __used)
+ u64 min_hit, struct callchain_param *param __maybe_unused)
{
__sort_chain_flat(rb_root, &root->node, min_hit);
}
@@ -115,7 +115,7 @@ static void __sort_chain_graph_abs(struct callchain_node *node,
static void
sort_chain_graph_abs(struct rb_root *rb_root, struct callchain_root *chain_root,
- u64 min_hit, struct callchain_param *param __used)
+ u64 min_hit, struct callchain_param *param __maybe_unused)
{
__sort_chain_graph_abs(&chain_root->node, min_hit);
rb_root->rb_node = chain_root->node.rb_root.rb_node;
@@ -140,7 +140,7 @@ static void __sort_chain_graph_rel(struct callchain_node *node,
static void
sort_chain_graph_rel(struct rb_root *rb_root, struct callchain_root *chain_root,
- u64 min_hit __used, struct callchain_param *param)
+ u64 min_hit __maybe_unused, struct callchain_param *param)
{
__sort_chain_graph_rel(&chain_root->node, param->min_percent / 100.0);
rb_root->rb_node = chain_root->node.rb_root.rb_node;
diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h
index 3bdb407f9cd9..eb340571e7d6 100644
--- a/tools/perf/util/callchain.h
+++ b/tools/perf/util/callchain.h
@@ -58,7 +58,7 @@ struct callchain_list {
/*
* A callchain cursor is a single linked list that
* let one feed a callchain progressively.
- * It keeps persitent allocated entries to minimize
+ * It keeps persistent allocated entries to minimize
* allocations.
*/
struct callchain_cursor_node {
diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c
index dbe2f16b1a1a..96bbda1ddb83 100644
--- a/tools/perf/util/cgroup.c
+++ b/tools/perf/util/cgroup.c
@@ -138,8 +138,8 @@ void close_cgroup(struct cgroup_sel *cgrp)
}
}
-int parse_cgroups(const struct option *opt __used, const char *str,
- int unset __used)
+int parse_cgroups(const struct option *opt __maybe_unused, const char *str,
+ int unset __maybe_unused)
{
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
const char *p, *e, *eos = str + strlen(str);
diff --git a/tools/perf/util/config.c b/tools/perf/util/config.c
index 6faa3a18bfbd..3e0fdd369ccb 100644
--- a/tools/perf/util/config.c
+++ b/tools/perf/util/config.c
@@ -342,13 +342,15 @@ const char *perf_config_dirname(const char *name, const char *value)
return value;
}
-static int perf_default_core_config(const char *var __used, const char *value __used)
+static int perf_default_core_config(const char *var __maybe_unused,
+ const char *value __maybe_unused)
{
/* Add other config variables here. */
return 0;
}
-int perf_default_config(const char *var, const char *value, void *dummy __used)
+int perf_default_config(const char *var, const char *value,
+ void *dummy __maybe_unused)
{
if (!prefixcmp(var, "core."))
return perf_default_core_config(var, value);
diff --git a/tools/perf/util/cpumap.c b/tools/perf/util/cpumap.c
index adc72f09914d..2b32ffa9ebdb 100644
--- a/tools/perf/util/cpumap.c
+++ b/tools/perf/util/cpumap.c
@@ -38,24 +38,19 @@ static struct cpu_map *cpu_map__trim_new(int nr_cpus, int *tmp_cpus)
return cpus;
}
-static struct cpu_map *cpu_map__read_all_cpu_map(void)
+struct cpu_map *cpu_map__read(FILE *file)
{
struct cpu_map *cpus = NULL;
- FILE *onlnf;
int nr_cpus = 0;
int *tmp_cpus = NULL, *tmp;
int max_entries = 0;
int n, cpu, prev;
char sep;
- onlnf = fopen("/sys/devices/system/cpu/online", "r");
- if (!onlnf)
- return cpu_map__default_new();
-
sep = 0;
prev = -1;
for (;;) {
- n = fscanf(onlnf, "%u%c", &cpu, &sep);
+ n = fscanf(file, "%u%c", &cpu, &sep);
if (n <= 0)
break;
if (prev >= 0) {
@@ -95,6 +90,19 @@ static struct cpu_map *cpu_map__read_all_cpu_map(void)
cpus = cpu_map__default_new();
out_free_tmp:
free(tmp_cpus);
+ return cpus;
+}
+
+static struct cpu_map *cpu_map__read_all_cpu_map(void)
+{
+ struct cpu_map *cpus = NULL;
+ FILE *onlnf;
+
+ onlnf = fopen("/sys/devices/system/cpu/online", "r");
+ if (!onlnf)
+ return cpu_map__default_new();
+
+ cpus = cpu_map__read(onlnf);
fclose(onlnf);
return cpus;
}
diff --git a/tools/perf/util/cpumap.h b/tools/perf/util/cpumap.h
index c41518573c6a..2f68a3b8c285 100644
--- a/tools/perf/util/cpumap.h
+++ b/tools/perf/util/cpumap.h
@@ -2,6 +2,7 @@
#define __PERF_CPUMAP_H
#include <stdio.h>
+#include <stdbool.h>
struct cpu_map {
int nr;
@@ -11,7 +12,17 @@ struct cpu_map {
struct cpu_map *cpu_map__new(const char *cpu_list);
struct cpu_map *cpu_map__dummy_new(void);
void cpu_map__delete(struct cpu_map *map);
-
+struct cpu_map *cpu_map__read(FILE *file);
size_t cpu_map__fprintf(struct cpu_map *map, FILE *fp);
+static inline int cpu_map__nr(const struct cpu_map *map)
+{
+ return map ? map->nr : 1;
+}
+
+static inline bool cpu_map__all(const struct cpu_map *map)
+{
+ return map ? map->map[0] == -1 : true;
+}
+
#endif /* __PERF_CPUMAP_H */
diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c
index 4dfe0bb3c322..66eb3828ceb5 100644
--- a/tools/perf/util/debug.c
+++ b/tools/perf/util/debug.c
@@ -23,8 +23,10 @@ int eprintf(int level, const char *fmt, ...)
if (verbose >= level) {
va_start(args, fmt);
- if (use_browser > 0)
+ if (use_browser == 1)
ret = ui_helpline__show_help(fmt, args);
+ else if (use_browser == 2)
+ ret = perf_gtk__show_helpline(fmt, args);
else
ret = vfprintf(stderr, fmt, args);
va_end(args);
diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h
index 015c91dbc096..bb2e7d1007ab 100644
--- a/tools/perf/util/debug.h
+++ b/tools/perf/util/debug.h
@@ -4,6 +4,7 @@
#include <stdbool.h>
#include "event.h"
+#include "../ui/helpline.h"
extern int verbose;
extern bool quiet, dump_trace;
@@ -15,32 +16,26 @@ struct ui_progress;
struct perf_error_ops;
#if defined(NO_NEWT_SUPPORT) && defined(NO_GTK2_SUPPORT)
-static inline int ui_helpline__show_help(const char *format __used, va_list ap __used)
-{
- return 0;
-}
-
-static inline void ui_progress__update(u64 curr __used, u64 total __used,
- const char *title __used) {}
+static inline void ui_progress__update(u64 curr __maybe_unused,
+ u64 total __maybe_unused,
+ const char *title __maybe_unused) {}
#define ui__error(format, arg...) ui__warning(format, ##arg)
static inline int
-perf_error__register(struct perf_error_ops *eops __used)
+perf_error__register(struct perf_error_ops *eops __maybe_unused)
{
return 0;
}
static inline int
-perf_error__unregister(struct perf_error_ops *eops __used)
+perf_error__unregister(struct perf_error_ops *eops __maybe_unused)
{
return 0;
}
#else /* NO_NEWT_SUPPORT && NO_GTK2_SUPPORT */
-extern char ui_helpline__last_msg[];
-int ui_helpline__show_help(const char *format, va_list ap);
#include "../ui/progress.h"
int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2)));
#include "../ui/util.h"
diff --git a/tools/perf/util/dso-test-data.c b/tools/perf/util/dso-test-data.c
index 541cdc72c7df..c6caedeb1d6b 100644
--- a/tools/perf/util/dso-test-data.c
+++ b/tools/perf/util/dso-test-data.c
@@ -23,7 +23,7 @@ static char *test_file(int size)
int fd, i;
unsigned char *buf;
- fd = mkostemp(templ, O_CREAT|O_WRONLY|O_TRUNC);
+ fd = mkstemp(templ);
buf = malloc(size);
if (!buf) {
diff --git a/tools/perf/util/dwarf-aux.c b/tools/perf/util/dwarf-aux.c
index ee51e9b4dc09..3e5f5430a28a 100644
--- a/tools/perf/util/dwarf-aux.c
+++ b/tools/perf/util/dwarf-aux.c
@@ -804,6 +804,8 @@ int die_get_typename(Dwarf_Die *vr_die, char *buf, int len)
tmp = "union ";
else if (tag == DW_TAG_structure_type)
tmp = "struct ";
+ else if (tag == DW_TAG_enumeration_type)
+ tmp = "enum ";
/* Write a base name */
ret = snprintf(buf, len, "%s%s", tmp, dwarf_diename(&type));
return (ret >= len) ? -E2BIG : ret;
diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c
index 2a6f33cd888c..6715b1938725 100644
--- a/tools/perf/util/event.c
+++ b/tools/perf/util/event.c
@@ -112,7 +112,7 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool,
event->comm.header.type = PERF_RECORD_COMM;
size = strlen(event->comm.comm) + 1;
- size = ALIGN(size, sizeof(u64));
+ size = PERF_ALIGN(size, sizeof(u64));
memset(event->comm.comm + size, 0, machine->id_hdr_size);
event->comm.header.size = (sizeof(event->comm) -
(sizeof(event->comm.comm) - size) +
@@ -120,7 +120,9 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool,
if (!full) {
event->comm.tid = pid;
- process(tool, event, &synth_sample, machine);
+ if (process(tool, event, &synth_sample, machine) != 0)
+ return -1;
+
goto out;
}
@@ -143,7 +145,7 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool,
sizeof(event->comm.comm));
size = strlen(event->comm.comm) + 1;
- size = ALIGN(size, sizeof(u64));
+ size = PERF_ALIGN(size, sizeof(u64));
memset(event->comm.comm + size, 0, machine->id_hdr_size);
event->comm.header.size = (sizeof(event->comm) -
(sizeof(event->comm.comm) - size) +
@@ -151,7 +153,10 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool,
event->comm.tid = pid;
- process(tool, event, &synth_sample, machine);
+ if (process(tool, event, &synth_sample, machine) != 0) {
+ tgid = -1;
+ break;
+ }
}
closedir(tasks);
@@ -167,6 +172,7 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
{
char filename[PATH_MAX];
FILE *fp;
+ int rc = 0;
snprintf(filename, sizeof(filename), "/proc/%d/maps", pid);
@@ -222,7 +228,7 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
size = strlen(execname);
execname[size - 1] = '\0'; /* Remove \n */
memcpy(event->mmap.filename, execname, size);
- size = ALIGN(size, sizeof(u64));
+ size = PERF_ALIGN(size, sizeof(u64));
event->mmap.len -= event->mmap.start;
event->mmap.header.size = (sizeof(event->mmap) -
(sizeof(event->mmap.filename) - size));
@@ -231,18 +237,22 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool,
event->mmap.pid = tgid;
event->mmap.tid = pid;
- process(tool, event, &synth_sample, machine);
+ if (process(tool, event, &synth_sample, machine) != 0) {
+ rc = -1;
+ break;
+ }
}
}
fclose(fp);
- return 0;
+ return rc;
}
int perf_event__synthesize_modules(struct perf_tool *tool,
perf_event__handler_t process,
struct machine *machine)
{
+ int rc = 0;
struct rb_node *nd;
struct map_groups *kmaps = &machine->kmaps;
union perf_event *event = zalloc((sizeof(event->mmap) +
@@ -272,7 +282,7 @@ int perf_event__synthesize_modules(struct perf_tool *tool,
if (pos->dso->kernel)
continue;
- size = ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
+ size = PERF_ALIGN(pos->dso->long_name_len + 1, sizeof(u64));
event->mmap.header.type = PERF_RECORD_MMAP;
event->mmap.header.size = (sizeof(event->mmap) -
(sizeof(event->mmap.filename) - size));
@@ -284,11 +294,14 @@ int perf_event__synthesize_modules(struct perf_tool *tool,
memcpy(event->mmap.filename, pos->dso->long_name,
pos->dso->long_name_len + 1);
- process(tool, event, &synth_sample, machine);
+ if (process(tool, event, &synth_sample, machine) != 0) {
+ rc = -1;
+ break;
+ }
}
free(event);
- return 0;
+ return rc;
}
static int __event__synthesize_thread(union perf_event *comm_event,
@@ -392,12 +405,16 @@ int perf_event__synthesize_threads(struct perf_tool *tool,
if (*end) /* only interested in proper numerical dirents */
continue;
- __event__synthesize_thread(comm_event, mmap_event, pid, 1,
- process, tool, machine);
+ if (__event__synthesize_thread(comm_event, mmap_event, pid, 1,
+ process, tool, machine) != 0) {
+ err = -1;
+ goto out_closedir;
+ }
}
- closedir(proc);
err = 0;
+out_closedir:
+ closedir(proc);
out_free_mmap:
free(mmap_event);
out_free_comm:
@@ -412,7 +429,7 @@ struct process_symbol_args {
};
static int find_symbol_cb(void *arg, const char *name, char type,
- u64 start, u64 end __used)
+ u64 start)
{
struct process_symbol_args *args = arg;
@@ -477,7 +494,7 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
map = machine->vmlinux_maps[MAP__FUNCTION];
size = snprintf(event->mmap.filename, sizeof(event->mmap.filename),
"%s%s", mmap_name, symbol_name) + 1;
- size = ALIGN(size, sizeof(u64));
+ size = PERF_ALIGN(size, sizeof(u64));
event->mmap.header.type = PERF_RECORD_MMAP;
event->mmap.header.size = (sizeof(event->mmap) -
(sizeof(event->mmap.filename) - size) + machine->id_hdr_size);
@@ -497,9 +514,9 @@ size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp)
return fprintf(fp, ": %s:%d\n", event->comm.comm, event->comm.tid);
}
-int perf_event__process_comm(struct perf_tool *tool __used,
+int perf_event__process_comm(struct perf_tool *tool __maybe_unused,
union perf_event *event,
- struct perf_sample *sample __used,
+ struct perf_sample *sample __maybe_unused,
struct machine *machine)
{
struct thread *thread = machine__findnew_thread(machine, event->comm.tid);
@@ -515,10 +532,10 @@ int perf_event__process_comm(struct perf_tool *tool __used,
return 0;
}
-int perf_event__process_lost(struct perf_tool *tool __used,
+int perf_event__process_lost(struct perf_tool *tool __maybe_unused,
union perf_event *event,
- struct perf_sample *sample __used,
- struct machine *machine __used)
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
{
dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",
event->lost.id, event->lost.lost);
@@ -538,7 +555,8 @@ static void perf_event__set_kernel_mmap_len(union perf_event *event,
maps[MAP__FUNCTION]->end = ~0ULL;
}
-static int perf_event__process_kernel_mmap(struct perf_tool *tool __used,
+static int perf_event__process_kernel_mmap(struct perf_tool *tool
+ __maybe_unused,
union perf_event *event,
struct machine *machine)
{
@@ -640,7 +658,7 @@ size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp)
int perf_event__process_mmap(struct perf_tool *tool,
union perf_event *event,
- struct perf_sample *sample __used,
+ struct perf_sample *sample __maybe_unused,
struct machine *machine)
{
struct thread *thread;
@@ -684,9 +702,9 @@ size_t perf_event__fprintf_task(union perf_event *event, FILE *fp)
event->fork.ppid, event->fork.ptid);
}
-int perf_event__process_task(struct perf_tool *tool __used,
+int perf_event__process_task(struct perf_tool *tool __maybe_unused,
union perf_event *event,
- struct perf_sample *sample __used,
+ struct perf_sample *sample __maybe_unused,
struct machine *machine)
{
struct thread *thread = machine__findnew_thread(machine, event->fork.tid);
@@ -886,8 +904,9 @@ int perf_event__preprocess_sample(const union perf_event *event,
al->sym = map__find_symbol(al->map, al->addr, filter);
}
- if (symbol_conf.sym_list && al->sym &&
- !strlist__has_entry(symbol_conf.sym_list, al->sym->name))
+ if (symbol_conf.sym_list &&
+ (!al->sym || !strlist__has_entry(symbol_conf.sym_list,
+ al->sym->name)))
goto out_filtered;
return 0;
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index d84870b06426..21b99e741a87 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -69,6 +69,16 @@ struct sample_event {
u64 array[];
};
+struct regs_dump {
+ u64 *regs;
+};
+
+struct stack_dump {
+ u16 offset;
+ u64 size;
+ char *data;
+};
+
struct perf_sample {
u64 ip;
u32 pid, tid;
@@ -82,6 +92,8 @@ struct perf_sample {
void *raw_data;
struct ip_callchain *callchain;
struct branch_stack *branch_stack;
+ struct regs_dump user_regs;
+ struct stack_dump user_stack;
};
#define BUILD_ID_SIZE 20
@@ -89,7 +101,7 @@ struct perf_sample {
struct build_id_event {
struct perf_event_header header;
pid_t pid;
- u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
+ u8 build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))];
char filename[];
};
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 9b38681add9e..ae89686102f4 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -57,7 +57,7 @@ void perf_evlist__config_attrs(struct perf_evlist *evlist,
if (evlist->cpus->map[0] < 0)
opts->no_inherit = true;
- first = list_entry(evlist->entries.next, struct perf_evsel, node);
+ first = perf_evlist__first(evlist);
list_for_each_entry(evsel, &evlist->entries, node) {
perf_evsel__config(evsel, opts, first);
@@ -108,6 +108,25 @@ void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
evlist->nr_entries += nr_entries;
}
+void __perf_evlist__set_leader(struct list_head *list)
+{
+ struct perf_evsel *evsel, *leader;
+
+ leader = list_entry(list->next, struct perf_evsel, node);
+ leader->leader = NULL;
+
+ list_for_each_entry(evsel, list, node) {
+ if (evsel != leader)
+ evsel->leader = leader;
+ }
+}
+
+void perf_evlist__set_leader(struct perf_evlist *evlist)
+{
+ if (evlist->nr_entries)
+ __perf_evlist__set_leader(&evlist->entries);
+}
+
int perf_evlist__add_default(struct perf_evlist *evlist)
{
struct perf_event_attr attr = {
@@ -285,7 +304,7 @@ void perf_evlist__enable(struct perf_evlist *evlist)
int cpu, thread;
struct perf_evsel *pos;
- for (cpu = 0; cpu < evlist->cpus->nr; cpu++) {
+ for (cpu = 0; cpu < cpu_map__nr(evlist->cpus); cpu++) {
list_for_each_entry(pos, &evlist->entries, node) {
for (thread = 0; thread < evlist->threads->nr; thread++)
ioctl(FD(pos, cpu, thread),
@@ -296,7 +315,7 @@ void perf_evlist__enable(struct perf_evlist *evlist)
static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
{
- int nfds = evlist->cpus->nr * evlist->threads->nr * evlist->nr_entries;
+ int nfds = cpu_map__nr(evlist->cpus) * evlist->threads->nr * evlist->nr_entries;
evlist->pollfd = malloc(sizeof(struct pollfd) * nfds);
return evlist->pollfd != NULL ? 0 : -ENOMEM;
}
@@ -357,7 +376,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
int hash;
if (evlist->nr_entries == 1)
- return list_entry(evlist->entries.next, struct perf_evsel, node);
+ return perf_evlist__first(evlist);
hash = hash_64(id, PERF_EVLIST__HLIST_BITS);
head = &evlist->heads[hash];
@@ -367,7 +386,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id)
return sid->evsel;
if (!perf_evlist__sample_id_all(evlist))
- return list_entry(evlist->entries.next, struct perf_evsel, node);
+ return perf_evlist__first(evlist);
return NULL;
}
@@ -456,8 +475,8 @@ void perf_evlist__munmap(struct perf_evlist *evlist)
static int perf_evlist__alloc_mmap(struct perf_evlist *evlist)
{
- evlist->nr_mmaps = evlist->cpus->nr;
- if (evlist->cpus->map[0] == -1)
+ evlist->nr_mmaps = cpu_map__nr(evlist->cpus);
+ if (cpu_map__all(evlist->cpus))
evlist->nr_mmaps = evlist->threads->nr;
evlist->mmap = zalloc(evlist->nr_mmaps * sizeof(struct perf_mmap));
return evlist->mmap != NULL ? 0 : -ENOMEM;
@@ -603,11 +622,11 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
list_for_each_entry(evsel, &evlist->entries, node) {
if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
evsel->sample_id == NULL &&
- perf_evsel__alloc_id(evsel, cpus->nr, threads->nr) < 0)
+ perf_evsel__alloc_id(evsel, cpu_map__nr(cpus), threads->nr) < 0)
return -ENOMEM;
}
- if (evlist->cpus->map[0] == -1)
+ if (cpu_map__all(cpus))
return perf_evlist__mmap_per_thread(evlist, prot, mask);
return perf_evlist__mmap_per_cpu(evlist, prot, mask);
@@ -647,39 +666,44 @@ void perf_evlist__delete_maps(struct perf_evlist *evlist)
evlist->threads = NULL;
}
-int perf_evlist__set_filters(struct perf_evlist *evlist)
+int perf_evlist__apply_filters(struct perf_evlist *evlist)
{
- const struct thread_map *threads = evlist->threads;
- const struct cpu_map *cpus = evlist->cpus;
struct perf_evsel *evsel;
- char *filter;
- int thread;
- int cpu;
- int err;
- int fd;
+ int err = 0;
+ const int ncpus = cpu_map__nr(evlist->cpus),
+ nthreads = evlist->threads->nr;
list_for_each_entry(evsel, &evlist->entries, node) {
- filter = evsel->filter;
- if (!filter)
+ if (evsel->filter == NULL)
continue;
- for (cpu = 0; cpu < cpus->nr; cpu++) {
- for (thread = 0; thread < threads->nr; thread++) {
- fd = FD(evsel, cpu, thread);
- err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter);
- if (err)
- return err;
- }
- }
+
+ err = perf_evsel__set_filter(evsel, ncpus, nthreads, evsel->filter);
+ if (err)
+ break;
}
- return 0;
+ return err;
}
-bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist)
+int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter)
{
- struct perf_evsel *pos, *first;
+ struct perf_evsel *evsel;
+ int err = 0;
+ const int ncpus = cpu_map__nr(evlist->cpus),
+ nthreads = evlist->threads->nr;
+
+ list_for_each_entry(evsel, &evlist->entries, node) {
+ err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter);
+ if (err)
+ break;
+ }
+
+ return err;
+}
- pos = first = list_entry(evlist->entries.next, struct perf_evsel, node);
+bool perf_evlist__valid_sample_type(struct perf_evlist *evlist)
+{
+ struct perf_evsel *first = perf_evlist__first(evlist), *pos = first;
list_for_each_entry_continue(pos, &evlist->entries, node) {
if (first->attr.sample_type != pos->attr.sample_type)
@@ -689,23 +713,19 @@ bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist)
return true;
}
-u64 perf_evlist__sample_type(const struct perf_evlist *evlist)
+u64 perf_evlist__sample_type(struct perf_evlist *evlist)
{
- struct perf_evsel *first;
-
- first = list_entry(evlist->entries.next, struct perf_evsel, node);
+ struct perf_evsel *first = perf_evlist__first(evlist);
return first->attr.sample_type;
}
-u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist)
+u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist)
{
- struct perf_evsel *first;
+ struct perf_evsel *first = perf_evlist__first(evlist);
struct perf_sample *data;
u64 sample_type;
u16 size = 0;
- first = list_entry(evlist->entries.next, struct perf_evsel, node);
-
if (!first->attr.sample_id_all)
goto out;
@@ -729,11 +749,9 @@ out:
return size;
}
-bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist)
+bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist)
{
- struct perf_evsel *pos, *first;
-
- pos = first = list_entry(evlist->entries.next, struct perf_evsel, node);
+ struct perf_evsel *first = perf_evlist__first(evlist), *pos = first;
list_for_each_entry_continue(pos, &evlist->entries, node) {
if (first->attr.sample_id_all != pos->attr.sample_id_all)
@@ -743,11 +761,9 @@ bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist)
return true;
}
-bool perf_evlist__sample_id_all(const struct perf_evlist *evlist)
+bool perf_evlist__sample_id_all(struct perf_evlist *evlist)
{
- struct perf_evsel *first;
-
- first = list_entry(evlist->entries.next, struct perf_evsel, node);
+ struct perf_evsel *first = perf_evlist__first(evlist);
return first->attr.sample_id_all;
}
@@ -757,21 +773,13 @@ void perf_evlist__set_selected(struct perf_evlist *evlist,
evlist->selected = evsel;
}
-int perf_evlist__open(struct perf_evlist *evlist, bool group)
+int perf_evlist__open(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel, *first;
+ struct perf_evsel *evsel;
int err, ncpus, nthreads;
- first = list_entry(evlist->entries.next, struct perf_evsel, node);
-
list_for_each_entry(evsel, &evlist->entries, node) {
- struct xyarray *group_fd = NULL;
-
- if (group && evsel != first)
- group_fd = first->fd;
-
- err = perf_evsel__open(evsel, evlist->cpus, evlist->threads,
- group, group_fd);
+ err = perf_evsel__open(evsel, evlist->cpus, evlist->threads);
if (err < 0)
goto out_err;
}
@@ -883,8 +891,21 @@ int perf_evlist__start_workload(struct perf_evlist *evlist)
}
int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
- struct perf_sample *sample, bool swapped)
+ struct perf_sample *sample)
+{
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
+ return perf_evsel__parse_sample(evsel, event, sample);
+}
+
+size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp)
{
- struct perf_evsel *e = list_entry(evlist->entries.next, struct perf_evsel, node);
- return perf_evsel__parse_sample(e, event, sample, swapped);
+ struct perf_evsel *evsel;
+ size_t printed = 0;
+
+ list_for_each_entry(evsel, &evlist->entries, node) {
+ printed += fprintf(fp, "%s%s", evsel->idx ? ", " : "",
+ perf_evsel__name(evsel));
+ }
+
+ return printed + fprintf(fp, "\n");;
}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 528c1acd9298..3f1fb66be022 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -5,6 +5,7 @@
#include <stdio.h>
#include "../perf.h"
#include "event.h"
+#include "evsel.h"
#include "util.h"
#include <unistd.h>
@@ -41,8 +42,6 @@ struct perf_evsel_str_handler {
void *handler;
};
-struct perf_evsel;
-
struct perf_evlist *perf_evlist__new(struct cpu_map *cpus,
struct thread_map *threads);
void perf_evlist__init(struct perf_evlist *evlist, struct cpu_map *cpus,
@@ -73,6 +72,8 @@ int perf_evlist__set_tracepoints_handlers(struct perf_evlist *evlist,
#define perf_evlist__set_tracepoints_handlers_array(evlist, array) \
perf_evlist__set_tracepoints_handlers(evlist, array, ARRAY_SIZE(array))
+int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter);
+
struct perf_evsel *
perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id);
@@ -85,7 +86,7 @@ struct perf_evsel *perf_evlist__id2evsel(struct perf_evlist *evlist, u64 id);
union perf_event *perf_evlist__mmap_read(struct perf_evlist *self, int idx);
-int perf_evlist__open(struct perf_evlist *evlist, bool group);
+int perf_evlist__open(struct perf_evlist *evlist);
void perf_evlist__config_attrs(struct perf_evlist *evlist,
struct perf_record_opts *opts);
@@ -116,20 +117,34 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
int perf_evlist__create_maps(struct perf_evlist *evlist,
struct perf_target *target);
void perf_evlist__delete_maps(struct perf_evlist *evlist);
-int perf_evlist__set_filters(struct perf_evlist *evlist);
+int perf_evlist__apply_filters(struct perf_evlist *evlist);
+
+void __perf_evlist__set_leader(struct list_head *list);
+void perf_evlist__set_leader(struct perf_evlist *evlist);
-u64 perf_evlist__sample_type(const struct perf_evlist *evlist);
-bool perf_evlist__sample_id_all(const const struct perf_evlist *evlist);
-u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist);
+u64 perf_evlist__sample_type(struct perf_evlist *evlist);
+bool perf_evlist__sample_id_all(struct perf_evlist *evlist);
+u16 perf_evlist__id_hdr_size(struct perf_evlist *evlist);
int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
- struct perf_sample *sample, bool swapped);
+ struct perf_sample *sample);
-bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist);
-bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist);
+bool perf_evlist__valid_sample_type(struct perf_evlist *evlist);
+bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist);
void perf_evlist__splice_list_tail(struct perf_evlist *evlist,
struct list_head *list,
int nr_entries);
+static inline struct perf_evsel *perf_evlist__first(struct perf_evlist *evlist)
+{
+ return list_entry(evlist->entries.next, struct perf_evsel, node);
+}
+
+static inline struct perf_evsel *perf_evlist__last(struct perf_evlist *evlist)
+{
+ return list_entry(evlist->entries.prev, struct perf_evsel, node);
+}
+
+size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp);
#endif /* __PERF_EVLIST_H */
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 2eaae140def2..ffdd94e9c9c3 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -8,7 +8,10 @@
*/
#include <byteswap.h>
+#include <linux/bitops.h>
#include "asm/bug.h"
+#include "debugfs.h"
+#include "event-parse.h"
#include "evsel.h"
#include "evlist.h"
#include "util.h"
@@ -16,9 +19,10 @@
#include "thread_map.h"
#include "target.h"
#include "../../../include/linux/hw_breakpoint.h"
+#include "../../include/linux/perf_event.h"
+#include "perf_regs.h"
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
-#define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0))
static int __perf_evsel__sample_size(u64 sample_type)
{
@@ -66,7 +70,80 @@ struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
return evsel;
}
-static const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
+struct event_format *event_format__new(const char *sys, const char *name)
+{
+ int fd, n;
+ char *filename;
+ void *bf = NULL, *nbf;
+ size_t size = 0, alloc_size = 0;
+ struct event_format *format = NULL;
+
+ if (asprintf(&filename, "%s/%s/%s/format", tracing_events_path, sys, name) < 0)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out_free_filename;
+
+ do {
+ if (size == alloc_size) {
+ alloc_size += BUFSIZ;
+ nbf = realloc(bf, alloc_size);
+ if (nbf == NULL)
+ goto out_free_bf;
+ bf = nbf;
+ }
+
+ n = read(fd, bf + size, BUFSIZ);
+ if (n < 0)
+ goto out_free_bf;
+ size += n;
+ } while (n > 0);
+
+ pevent_parse_format(&format, bf, size, sys);
+
+out_free_bf:
+ free(bf);
+ close(fd);
+out_free_filename:
+ free(filename);
+out:
+ return format;
+}
+
+struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx)
+{
+ struct perf_evsel *evsel = zalloc(sizeof(*evsel));
+
+ if (evsel != NULL) {
+ struct perf_event_attr attr = {
+ .type = PERF_TYPE_TRACEPOINT,
+ .sample_type = (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME |
+ PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD),
+ };
+
+ if (asprintf(&evsel->name, "%s:%s", sys, name) < 0)
+ goto out_free;
+
+ evsel->tp_format = event_format__new(sys, name);
+ if (evsel->tp_format == NULL)
+ goto out_free;
+
+ event_attr_init(&attr);
+ attr.config = evsel->tp_format->id;
+ attr.sample_period = 1;
+ perf_evsel__init(evsel, &attr, idx);
+ }
+
+ return evsel;
+
+out_free:
+ free(evsel->name);
+ free(evsel);
+ return NULL;
+}
+
+const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX] = {
"cycles",
"instructions",
"cache-references",
@@ -129,12 +206,12 @@ static int perf_evsel__hw_name(struct perf_evsel *evsel, char *bf, size_t size)
return r + perf_evsel__add_modifiers(evsel, bf + r, size - r);
}
-static const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = {
+const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX] = {
"cpu-clock",
"task-clock",
"page-faults",
"context-switches",
- "CPU-migrations",
+ "cpu-migrations",
"minor-faults",
"major-faults",
"alignment-faults",
@@ -317,7 +394,8 @@ const char *perf_evsel__name(struct perf_evsel *evsel)
break;
default:
- scnprintf(bf, sizeof(bf), "%s", "unknown attr type");
+ scnprintf(bf, sizeof(bf), "unknown attr type: %d",
+ evsel->attr.type);
break;
}
@@ -367,9 +445,18 @@ void perf_evsel__config(struct perf_evsel *evsel, struct perf_record_opts *opts,
attr->mmap_data = track;
}
- if (opts->call_graph)
+ if (opts->call_graph) {
attr->sample_type |= PERF_SAMPLE_CALLCHAIN;
+ if (opts->call_graph == CALLCHAIN_DWARF) {
+ attr->sample_type |= PERF_SAMPLE_REGS_USER |
+ PERF_SAMPLE_STACK_USER;
+ attr->sample_regs_user = PERF_REGS_MASK;
+ attr->sample_stack_user = opts->stack_dump_size;
+ attr->exclude_callchain_user = 1;
+ }
+ }
+
if (perf_target__has_cpu(&opts->target))
attr->sample_type |= PERF_SAMPLE_CPU;
@@ -421,6 +508,24 @@ int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads)
return evsel->fd != NULL ? 0 : -ENOMEM;
}
+int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
+ const char *filter)
+{
+ int cpu, thread;
+
+ for (cpu = 0; cpu < ncpus; cpu++) {
+ for (thread = 0; thread < nthreads; thread++) {
+ int fd = FD(evsel, cpu, thread),
+ err = ioctl(fd, PERF_EVENT_IOC_SET_FILTER, filter);
+
+ if (err)
+ return err;
+ }
+ }
+
+ return 0;
+}
+
int perf_evsel__alloc_id(struct perf_evsel *evsel, int ncpus, int nthreads)
{
evsel->sample_id = xyarray__new(ncpus, nthreads, sizeof(struct perf_sample_id));
@@ -481,6 +586,9 @@ void perf_evsel__delete(struct perf_evsel *evsel)
{
perf_evsel__exit(evsel);
close_cgroup(evsel->cgrp);
+ free(evsel->group_name);
+ if (evsel->tp_format)
+ pevent_free_format(evsel->tp_format);
free(evsel->name);
free(evsel);
}
@@ -556,9 +664,28 @@ int __perf_evsel__read(struct perf_evsel *evsel,
return 0;
}
+static int get_group_fd(struct perf_evsel *evsel, int cpu, int thread)
+{
+ struct perf_evsel *leader = evsel->leader;
+ int fd;
+
+ if (!leader)
+ return -1;
+
+ /*
+ * Leader must be already processed/open,
+ * if not it's a bug.
+ */
+ BUG_ON(!leader->fd);
+
+ fd = FD(leader, cpu, thread);
+ BUG_ON(fd == -1);
+
+ return fd;
+}
+
static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
- struct thread_map *threads, bool group,
- struct xyarray *group_fds)
+ struct thread_map *threads)
{
int cpu, thread;
unsigned long flags = 0;
@@ -574,13 +701,15 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
}
for (cpu = 0; cpu < cpus->nr; cpu++) {
- int group_fd = group_fds ? GROUP_FD(group_fds, cpu) : -1;
for (thread = 0; thread < threads->nr; thread++) {
+ int group_fd;
if (!evsel->cgrp)
pid = threads->map[thread];
+ group_fd = get_group_fd(evsel, cpu, thread);
+
FD(evsel, cpu, thread) = sys_perf_event_open(&evsel->attr,
pid,
cpus->map[cpu],
@@ -589,9 +718,6 @@ static int __perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
err = -errno;
goto out_close;
}
-
- if (group && group_fd == -1)
- group_fd = FD(evsel, cpu, thread);
}
}
@@ -635,8 +761,7 @@ static struct {
};
int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
- struct thread_map *threads, bool group,
- struct xyarray *group_fd)
+ struct thread_map *threads)
{
if (cpus == NULL) {
/* Work around old compiler warnings about strict aliasing */
@@ -646,30 +771,28 @@ int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
if (threads == NULL)
threads = &empty_thread_map.map;
- return __perf_evsel__open(evsel, cpus, threads, group, group_fd);
+ return __perf_evsel__open(evsel, cpus, threads);
}
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
- struct cpu_map *cpus, bool group,
- struct xyarray *group_fd)
+ struct cpu_map *cpus)
{
- return __perf_evsel__open(evsel, cpus, &empty_thread_map.map, group,
- group_fd);
+ return __perf_evsel__open(evsel, cpus, &empty_thread_map.map);
}
int perf_evsel__open_per_thread(struct perf_evsel *evsel,
- struct thread_map *threads, bool group,
- struct xyarray *group_fd)
+ struct thread_map *threads)
{
- return __perf_evsel__open(evsel, &empty_cpu_map.map, threads, group,
- group_fd);
+ return __perf_evsel__open(evsel, &empty_cpu_map.map, threads);
}
-static int perf_event__parse_id_sample(const union perf_event *event, u64 type,
- struct perf_sample *sample,
- bool swapped)
+static int perf_evsel__parse_id_sample(const struct perf_evsel *evsel,
+ const union perf_event *event,
+ struct perf_sample *sample)
{
+ u64 type = evsel->attr.sample_type;
const u64 *array = event->sample.array;
+ bool swapped = evsel->needs_swap;
union u64_swap u;
array += ((event->header.size -
@@ -730,9 +853,11 @@ static bool sample_overlap(const union perf_event *event,
}
int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
- struct perf_sample *data, bool swapped)
+ struct perf_sample *data)
{
u64 type = evsel->attr.sample_type;
+ u64 regs_user = evsel->attr.sample_regs_user;
+ bool swapped = evsel->needs_swap;
const u64 *array;
/*
@@ -749,7 +874,7 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
if (event->header.type != PERF_RECORD_SAMPLE) {
if (!evsel->attr.sample_id_all)
return 0;
- return perf_event__parse_id_sample(event, type, data, swapped);
+ return perf_evsel__parse_id_sample(evsel, event, data);
}
array = event->sample.array;
@@ -869,6 +994,32 @@ int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
sz /= sizeof(u64);
array += sz;
}
+
+ if (type & PERF_SAMPLE_REGS_USER) {
+ /* First u64 tells us if we have any regs in sample. */
+ u64 avail = *array++;
+
+ if (avail) {
+ data->user_regs.regs = (u64 *)array;
+ array += hweight_long(regs_user);
+ }
+ }
+
+ if (type & PERF_SAMPLE_STACK_USER) {
+ u64 size = *array++;
+
+ data->user_stack.offset = ((char *)(array - 1)
+ - (char *) event);
+
+ if (!size) {
+ data->user_stack.size = 0;
+ } else {
+ data->user_stack.data = (char *)array;
+ array += size / sizeof(*array);
+ data->user_stack.size = *array;
+ }
+ }
+
return 0;
}
@@ -947,3 +1098,72 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type,
return 0;
}
+
+struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name)
+{
+ return pevent_find_field(evsel->tp_format, name);
+}
+
+void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample,
+ const char *name)
+{
+ struct format_field *field = perf_evsel__field(evsel, name);
+ int offset;
+
+ if (!field)
+ return NULL;
+
+ offset = field->offset;
+
+ if (field->flags & FIELD_IS_DYNAMIC) {
+ offset = *(int *)(sample->raw_data + field->offset);
+ offset &= 0xffff;
+ }
+
+ return sample->raw_data + offset;
+}
+
+u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
+ const char *name)
+{
+ struct format_field *field = perf_evsel__field(evsel, name);
+ void *ptr;
+ u64 value;
+
+ if (!field)
+ return 0;
+
+ ptr = sample->raw_data + field->offset;
+
+ switch (field->size) {
+ case 1:
+ return *(u8 *)ptr;
+ case 2:
+ value = *(u16 *)ptr;
+ break;
+ case 4:
+ value = *(u32 *)ptr;
+ break;
+ case 8:
+ value = *(u64 *)ptr;
+ break;
+ default:
+ return 0;
+ }
+
+ if (!evsel->needs_swap)
+ return value;
+
+ switch (field->size) {
+ case 2:
+ return bswap_16(value);
+ case 4:
+ return bswap_32(value);
+ case 8:
+ return bswap_64(value);
+ default:
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index b559929983bb..3ead0d59c03d 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -53,9 +53,10 @@ struct perf_evsel {
u64 *id;
struct perf_counts *counts;
int idx;
- int ids;
+ u32 ids;
struct hists hists;
char *name;
+ struct event_format *tp_format;
union {
void *priv;
off_t id_offset;
@@ -65,8 +66,14 @@ struct perf_evsel {
void *func;
void *data;
} handler;
+ struct cpu_map *cpus;
unsigned int sample_size;
bool supported;
+ bool needs_swap;
+ /* parse modifier helper */
+ int exclude_GH;
+ struct perf_evsel *leader;
+ char *group_name;
};
struct cpu_map;
@@ -75,6 +82,10 @@ struct perf_evlist;
struct perf_record_opts;
struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx);
+struct perf_evsel *perf_evsel__newtp(const char *sys, const char *name, int idx);
+
+struct event_format *event_format__new(const char *sys, const char *name);
+
void perf_evsel__init(struct perf_evsel *evsel,
struct perf_event_attr *attr, int idx);
void perf_evsel__exit(struct perf_evsel *evsel);
@@ -92,8 +103,10 @@ extern const char *perf_evsel__hw_cache[PERF_COUNT_HW_CACHE_MAX]
[PERF_EVSEL__MAX_ALIASES];
extern const char *perf_evsel__hw_cache_op[PERF_COUNT_HW_CACHE_OP_MAX]
[PERF_EVSEL__MAX_ALIASES];
-const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
- [PERF_EVSEL__MAX_ALIASES];
+extern const char *perf_evsel__hw_cache_result[PERF_COUNT_HW_CACHE_RESULT_MAX]
+ [PERF_EVSEL__MAX_ALIASES];
+extern const char *perf_evsel__hw_names[PERF_COUNT_HW_MAX];
+extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];
int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
char *bf, size_t size);
const char *perf_evsel__name(struct perf_evsel *evsel);
@@ -105,21 +118,46 @@ void perf_evsel__free_fd(struct perf_evsel *evsel);
void perf_evsel__free_id(struct perf_evsel *evsel);
void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads);
+int perf_evsel__set_filter(struct perf_evsel *evsel, int ncpus, int nthreads,
+ const char *filter);
+
int perf_evsel__open_per_cpu(struct perf_evsel *evsel,
- struct cpu_map *cpus, bool group,
- struct xyarray *group_fds);
+ struct cpu_map *cpus);
int perf_evsel__open_per_thread(struct perf_evsel *evsel,
- struct thread_map *threads, bool group,
- struct xyarray *group_fds);
+ struct thread_map *threads);
int perf_evsel__open(struct perf_evsel *evsel, struct cpu_map *cpus,
- struct thread_map *threads, bool group,
- struct xyarray *group_fds);
+ struct thread_map *threads);
void perf_evsel__close(struct perf_evsel *evsel, int ncpus, int nthreads);
+struct perf_sample;
+
+void *perf_evsel__rawptr(struct perf_evsel *evsel, struct perf_sample *sample,
+ const char *name);
+u64 perf_evsel__intval(struct perf_evsel *evsel, struct perf_sample *sample,
+ const char *name);
+
+static inline char *perf_evsel__strval(struct perf_evsel *evsel,
+ struct perf_sample *sample,
+ const char *name)
+{
+ return perf_evsel__rawptr(evsel, sample, name);
+}
+
+struct format_field;
+
+struct format_field *perf_evsel__field(struct perf_evsel *evsel, const char *name);
+
#define perf_evsel__match(evsel, t, c) \
(evsel->attr.type == PERF_TYPE_##t && \
evsel->attr.config == PERF_COUNT_##c)
+static inline bool perf_evsel__match2(struct perf_evsel *e1,
+ struct perf_evsel *e2)
+{
+ return (e1->attr.type == e2->attr.type) &&
+ (e1->attr.config == e2->attr.config);
+}
+
int __perf_evsel__read_on_cpu(struct perf_evsel *evsel,
int cpu, int thread, bool scale);
@@ -181,5 +219,10 @@ static inline int perf_evsel__read_scaled(struct perf_evsel *evsel,
void hists__init(struct hists *hists);
int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
- struct perf_sample *sample, bool swapped);
+ struct perf_sample *sample);
+
+static inline struct perf_evsel *perf_evsel__next(struct perf_evsel *evsel)
+{
+ return list_entry(evsel->node.next, struct perf_evsel, node);
+}
#endif /* __PERF_EVSEL_H */
diff --git a/tools/perf/util/generate-cmdlist.sh b/tools/perf/util/generate-cmdlist.sh
index f06f6fd148f8..389590c1ad21 100755
--- a/tools/perf/util/generate-cmdlist.sh
+++ b/tools/perf/util/generate-cmdlist.sh
@@ -21,4 +21,19 @@ do
p
}' "Documentation/perf-$cmd.txt"
done
+
+echo "#ifndef NO_LIBELF_SUPPORT"
+sed -n -e 's/^perf-\([^ ]*\)[ ].* full.*/\1/p' command-list.txt |
+sort |
+while read cmd
+do
+ sed -n '
+ /^NAME/,/perf-'"$cmd"'/H
+ ${
+ x
+ s/.*perf-'"$cmd"' - \(.*\)/ {"'"$cmd"'", "\1"},/
+ p
+ }' "Documentation/perf-$cmd.txt"
+done
+echo "#endif /* NO_LIBELF_SUPPORT */"
echo "};"
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 74ea3c2f8138..7daad237dea5 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -20,11 +20,14 @@
#include "symbol.h"
#include "debug.h"
#include "cpumap.h"
+#include "pmu.h"
+#include "vdso.h"
+#include "strbuf.h"
static bool no_buildid_cache = false;
-static int event_count;
-static struct perf_trace_event_type *events;
+static int trace_event_count;
+static struct perf_trace_event_type *trace_events;
static u32 header_argc;
static const char **header_argv;
@@ -36,24 +39,24 @@ int perf_header__push_event(u64 id, const char *name)
if (strlen(name) > MAX_EVENT_NAME)
pr_warning("Event %s will be truncated\n", name);
- nevents = realloc(events, (event_count + 1) * sizeof(*events));
+ nevents = realloc(trace_events, (trace_event_count + 1) * sizeof(*trace_events));
if (nevents == NULL)
return -ENOMEM;
- events = nevents;
+ trace_events = nevents;
- memset(&events[event_count], 0, sizeof(struct perf_trace_event_type));
- events[event_count].event_id = id;
- strncpy(events[event_count].name, name, MAX_EVENT_NAME - 1);
- event_count++;
+ memset(&trace_events[trace_event_count], 0, sizeof(struct perf_trace_event_type));
+ trace_events[trace_event_count].event_id = id;
+ strncpy(trace_events[trace_event_count].name, name, MAX_EVENT_NAME - 1);
+ trace_event_count++;
return 0;
}
char *perf_header__find_event(u64 id)
{
int i;
- for (i = 0 ; i < event_count; i++) {
- if (events[i].event_id == id)
- return events[i].name;
+ for (i = 0 ; i < trace_event_count; i++) {
+ if (trace_events[i].event_id == id)
+ return trace_events[i].name;
}
return NULL;
}
@@ -128,7 +131,7 @@ static int do_write_string(int fd, const char *str)
int ret;
olen = strlen(str) + 1;
- len = ALIGN(olen, NAME_ALIGN);
+ len = PERF_ALIGN(olen, NAME_ALIGN);
/* write len, incl. \0 */
ret = do_write(fd, &len, sizeof(len));
@@ -206,6 +209,29 @@ perf_header__set_cmdline(int argc, const char **argv)
continue; \
else
+static int write_buildid(char *name, size_t name_len, u8 *build_id,
+ pid_t pid, u16 misc, int fd)
+{
+ int err;
+ struct build_id_event b;
+ size_t len;
+
+ len = name_len + 1;
+ len = PERF_ALIGN(len, NAME_ALIGN);
+
+ memset(&b, 0, sizeof(b));
+ memcpy(&b.build_id, build_id, BUILD_ID_SIZE);
+ b.pid = pid;
+ b.header.misc = misc;
+ b.header.size = sizeof(b) + len;
+
+ err = do_write(fd, &b, sizeof(b));
+ if (err < 0)
+ return err;
+
+ return write_padded(fd, name, name_len + 1, len);
+}
+
static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
u16 misc, int fd)
{
@@ -213,24 +239,23 @@ static int __dsos__write_buildid_table(struct list_head *head, pid_t pid,
dsos__for_each_with_build_id(pos, head) {
int err;
- struct build_id_event b;
- size_t len;
+ char *name;
+ size_t name_len;
if (!pos->hit)
continue;
- len = pos->long_name_len + 1;
- len = ALIGN(len, NAME_ALIGN);
- memset(&b, 0, sizeof(b));
- memcpy(&b.build_id, pos->build_id, sizeof(pos->build_id));
- b.pid = pid;
- b.header.misc = misc;
- b.header.size = sizeof(b) + len;
- err = do_write(fd, &b, sizeof(b));
- if (err < 0)
- return err;
- err = write_padded(fd, pos->long_name,
- pos->long_name_len + 1, len);
- if (err < 0)
+
+ if (is_vdso_map(pos->short_name)) {
+ name = (char *) VDSO__MAP_NAME;
+ name_len = sizeof(VDSO__MAP_NAME) + 1;
+ } else {
+ name = pos->long_name;
+ name_len = pos->long_name_len + 1;
+ }
+
+ err = write_buildid(name, name_len, pos->build_id,
+ pid, misc, fd);
+ if (err)
return err;
}
@@ -276,19 +301,20 @@ static int dsos__write_buildid_table(struct perf_header *header, int fd)
}
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
- const char *name, bool is_kallsyms)
+ const char *name, bool is_kallsyms, bool is_vdso)
{
const size_t size = PATH_MAX;
char *realname, *filename = zalloc(size),
*linkname = zalloc(size), *targetname;
int len, err = -1;
+ bool slash = is_kallsyms || is_vdso;
if (is_kallsyms) {
if (symbol_conf.kptr_restrict) {
pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");
return 0;
}
- realname = (char *)name;
+ realname = (char *) name;
} else
realname = realpath(name, NULL);
@@ -296,7 +322,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
goto out_free;
len = scnprintf(filename, size, "%s%s%s",
- debugdir, is_kallsyms ? "/" : "", realname);
+ debugdir, slash ? "/" : "",
+ is_vdso ? VDSO__MAP_NAME : realname);
if (mkdir_p(filename, 0755))
goto out_free;
@@ -332,13 +359,14 @@ out_free:
static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
const char *name, const char *debugdir,
- bool is_kallsyms)
+ bool is_kallsyms, bool is_vdso)
{
char sbuild_id[BUILD_ID_SIZE * 2 + 1];
build_id__sprintf(build_id, build_id_size, sbuild_id);
- return build_id_cache__add_s(sbuild_id, debugdir, name, is_kallsyms);
+ return build_id_cache__add_s(sbuild_id, debugdir, name,
+ is_kallsyms, is_vdso);
}
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir)
@@ -382,9 +410,11 @@ out_free:
static int dso__cache_build_id(struct dso *dso, const char *debugdir)
{
bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';
+ bool is_vdso = is_vdso_map(dso->short_name);
return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id),
- dso->long_name, debugdir, is_kallsyms);
+ dso->long_name, debugdir,
+ is_kallsyms, is_vdso);
}
static int __dsos__cache_build_ids(struct list_head *head, const char *debugdir)
@@ -446,7 +476,7 @@ static bool perf_session__read_build_ids(struct perf_session *session, bool with
return ret;
}
-static int write_tracing_data(int fd, struct perf_header *h __used,
+static int write_tracing_data(int fd, struct perf_header *h __maybe_unused,
struct perf_evlist *evlist)
{
return read_tracing_data(fd, &evlist->entries);
@@ -454,7 +484,7 @@ static int write_tracing_data(int fd, struct perf_header *h __used,
static int write_build_id(int fd, struct perf_header *h,
- struct perf_evlist *evlist __used)
+ struct perf_evlist *evlist __maybe_unused)
{
struct perf_session *session;
int err;
@@ -475,8 +505,8 @@ static int write_build_id(int fd, struct perf_header *h,
return 0;
}
-static int write_hostname(int fd, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_hostname(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
struct utsname uts;
int ret;
@@ -488,8 +518,8 @@ static int write_hostname(int fd, struct perf_header *h __used,
return do_write_string(fd, uts.nodename);
}
-static int write_osrelease(int fd, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_osrelease(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
struct utsname uts;
int ret;
@@ -501,8 +531,8 @@ static int write_osrelease(int fd, struct perf_header *h __used,
return do_write_string(fd, uts.release);
}
-static int write_arch(int fd, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_arch(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
struct utsname uts;
int ret;
@@ -514,14 +544,14 @@ static int write_arch(int fd, struct perf_header *h __used,
return do_write_string(fd, uts.machine);
}
-static int write_version(int fd, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_version(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
return do_write_string(fd, perf_version_string);
}
-static int write_cpudesc(int fd, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_cpudesc(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
#ifndef CPUINFO_PROC
#define CPUINFO_PROC NULL
@@ -579,8 +609,8 @@ done:
return ret;
}
-static int write_nrcpus(int fd, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_nrcpus(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
long nr;
u32 nrc, nra;
@@ -605,15 +635,14 @@ static int write_nrcpus(int fd, struct perf_header *h __used,
return do_write(fd, &nra, sizeof(nra));
}
-static int write_event_desc(int fd, struct perf_header *h __used,
+static int write_event_desc(int fd, struct perf_header *h __maybe_unused,
struct perf_evlist *evlist)
{
- struct perf_evsel *attr;
- u32 nre = 0, nri, sz;
+ struct perf_evsel *evsel;
+ u32 nre, nri, sz;
int ret;
- list_for_each_entry(attr, &evlist->entries, node)
- nre++;
+ nre = evlist->nr_entries;
/*
* write number of events
@@ -625,14 +654,14 @@ static int write_event_desc(int fd, struct perf_header *h __used,
/*
* size of perf_event_attr struct
*/
- sz = (u32)sizeof(attr->attr);
+ sz = (u32)sizeof(evsel->attr);
ret = do_write(fd, &sz, sizeof(sz));
if (ret < 0)
return ret;
- list_for_each_entry(attr, &evlist->entries, node) {
+ list_for_each_entry(evsel, &evlist->entries, node) {
- ret = do_write(fd, &attr->attr, sz);
+ ret = do_write(fd, &evsel->attr, sz);
if (ret < 0)
return ret;
/*
@@ -642,7 +671,7 @@ static int write_event_desc(int fd, struct perf_header *h __used,
* copy into an nri to be independent of the
* type of ids,
*/
- nri = attr->ids;
+ nri = evsel->ids;
ret = do_write(fd, &nri, sizeof(nri));
if (ret < 0)
return ret;
@@ -650,21 +679,21 @@ static int write_event_desc(int fd, struct perf_header *h __used,
/*
* write event string as passed on cmdline
*/
- ret = do_write_string(fd, perf_evsel__name(attr));
+ ret = do_write_string(fd, perf_evsel__name(evsel));
if (ret < 0)
return ret;
/*
* write unique ids for this event
*/
- ret = do_write(fd, attr->id, attr->ids * sizeof(u64));
+ ret = do_write(fd, evsel->id, evsel->ids * sizeof(u64));
if (ret < 0)
return ret;
}
return 0;
}
-static int write_cmdline(int fd, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_cmdline(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
char buf[MAXPATHLEN];
char proc[32];
@@ -832,8 +861,8 @@ static struct cpu_topo *build_cpu_topology(void)
return tp;
}
-static int write_cpu_topology(int fd, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_cpu_topology(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
struct cpu_topo *tp;
u32 i;
@@ -868,8 +897,8 @@ done:
-static int write_total_mem(int fd, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_total_mem(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
char *buf = NULL;
FILE *fp;
@@ -954,8 +983,8 @@ done:
return ret;
}
-static int write_numa_topology(int fd, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_numa_topology(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
char *buf = NULL;
size_t len = 0;
@@ -1004,16 +1033,56 @@ done:
}
/*
+ * File format:
+ *
+ * struct pmu_mappings {
+ * u32 pmu_num;
+ * struct pmu_map {
+ * u32 type;
+ * char name[];
+ * }[pmu_num];
+ * };
+ */
+
+static int write_pmu_mappings(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
+{
+ struct perf_pmu *pmu = NULL;
+ off_t offset = lseek(fd, 0, SEEK_CUR);
+ __u32 pmu_num = 0;
+
+ /* write real pmu_num later */
+ do_write(fd, &pmu_num, sizeof(pmu_num));
+
+ while ((pmu = perf_pmu__scan(pmu))) {
+ if (!pmu->name)
+ continue;
+ pmu_num++;
+ do_write(fd, &pmu->type, sizeof(pmu->type));
+ do_write_string(fd, pmu->name);
+ }
+
+ if (pwrite(fd, &pmu_num, sizeof(pmu_num), offset) != sizeof(pmu_num)) {
+ /* discard all */
+ lseek(fd, offset, SEEK_SET);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
* default get_cpuid(): nothing gets recorded
* actual implementation must be in arch/$(ARCH)/util/header.c
*/
-int __attribute__((weak)) get_cpuid(char *buffer __used, size_t sz __used)
+int __attribute__ ((weak)) get_cpuid(char *buffer __maybe_unused,
+ size_t sz __maybe_unused)
{
return -1;
}
-static int write_cpuid(int fd, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_cpuid(int fd, struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
char buffer[64];
int ret;
@@ -1027,133 +1096,113 @@ write_it:
return do_write_string(fd, buffer);
}
-static int write_branch_stack(int fd __used, struct perf_header *h __used,
- struct perf_evlist *evlist __used)
+static int write_branch_stack(int fd __maybe_unused,
+ struct perf_header *h __maybe_unused,
+ struct perf_evlist *evlist __maybe_unused)
{
return 0;
}
-static void print_hostname(struct perf_header *ph, int fd, FILE *fp)
+static void print_hostname(struct perf_header *ph, int fd __maybe_unused,
+ FILE *fp)
{
- char *str = do_read_string(fd, ph);
- fprintf(fp, "# hostname : %s\n", str);
- free(str);
+ fprintf(fp, "# hostname : %s\n", ph->env.hostname);
}
-static void print_osrelease(struct perf_header *ph, int fd, FILE *fp)
+static void print_osrelease(struct perf_header *ph, int fd __maybe_unused,
+ FILE *fp)
{
- char *str = do_read_string(fd, ph);
- fprintf(fp, "# os release : %s\n", str);
- free(str);
+ fprintf(fp, "# os release : %s\n", ph->env.os_release);
}
-static void print_arch(struct perf_header *ph, int fd, FILE *fp)
+static void print_arch(struct perf_header *ph, int fd __maybe_unused, FILE *fp)
{
- char *str = do_read_string(fd, ph);
- fprintf(fp, "# arch : %s\n", str);
- free(str);
+ fprintf(fp, "# arch : %s\n", ph->env.arch);
}
-static void print_cpudesc(struct perf_header *ph, int fd, FILE *fp)
+static void print_cpudesc(struct perf_header *ph, int fd __maybe_unused,
+ FILE *fp)
{
- char *str = do_read_string(fd, ph);
- fprintf(fp, "# cpudesc : %s\n", str);
- free(str);
+ fprintf(fp, "# cpudesc : %s\n", ph->env.cpu_desc);
}
-static void print_nrcpus(struct perf_header *ph, int fd, FILE *fp)
+static void print_nrcpus(struct perf_header *ph, int fd __maybe_unused,
+ FILE *fp)
{
- ssize_t ret;
- u32 nr;
-
- ret = read(fd, &nr, sizeof(nr));
- if (ret != (ssize_t)sizeof(nr))
- nr = -1; /* interpreted as error */
-
- if (ph->needs_swap)
- nr = bswap_32(nr);
-
- fprintf(fp, "# nrcpus online : %u\n", nr);
-
- ret = read(fd, &nr, sizeof(nr));
- if (ret != (ssize_t)sizeof(nr))
- nr = -1; /* interpreted as error */
-
- if (ph->needs_swap)
- nr = bswap_32(nr);
-
- fprintf(fp, "# nrcpus avail : %u\n", nr);
+ fprintf(fp, "# nrcpus online : %u\n", ph->env.nr_cpus_online);
+ fprintf(fp, "# nrcpus avail : %u\n", ph->env.nr_cpus_avail);
}
-static void print_version(struct perf_header *ph, int fd, FILE *fp)
+static void print_version(struct perf_header *ph, int fd __maybe_unused,
+ FILE *fp)
{
- char *str = do_read_string(fd, ph);
- fprintf(fp, "# perf version : %s\n", str);
- free(str);
+ fprintf(fp, "# perf version : %s\n", ph->env.version);
}
-static void print_cmdline(struct perf_header *ph, int fd, FILE *fp)
+static void print_cmdline(struct perf_header *ph, int fd __maybe_unused,
+ FILE *fp)
{
- ssize_t ret;
+ int nr, i;
char *str;
- u32 nr, i;
-
- ret = read(fd, &nr, sizeof(nr));
- if (ret != (ssize_t)sizeof(nr))
- return;
- if (ph->needs_swap)
- nr = bswap_32(nr);
+ nr = ph->env.nr_cmdline;
+ str = ph->env.cmdline;
fprintf(fp, "# cmdline : ");
for (i = 0; i < nr; i++) {
- str = do_read_string(fd, ph);
fprintf(fp, "%s ", str);
- free(str);
+ str += strlen(str) + 1;
}
fputc('\n', fp);
}
-static void print_cpu_topology(struct perf_header *ph, int fd, FILE *fp)
+static void print_cpu_topology(struct perf_header *ph, int fd __maybe_unused,
+ FILE *fp)
{
- ssize_t ret;
- u32 nr, i;
+ int nr, i;
char *str;
- ret = read(fd, &nr, sizeof(nr));
- if (ret != (ssize_t)sizeof(nr))
- return;
-
- if (ph->needs_swap)
- nr = bswap_32(nr);
+ nr = ph->env.nr_sibling_cores;
+ str = ph->env.sibling_cores;
for (i = 0; i < nr; i++) {
- str = do_read_string(fd, ph);
fprintf(fp, "# sibling cores : %s\n", str);
- free(str);
+ str += strlen(str) + 1;
}
- ret = read(fd, &nr, sizeof(nr));
- if (ret != (ssize_t)sizeof(nr))
- return;
-
- if (ph->needs_swap)
- nr = bswap_32(nr);
+ nr = ph->env.nr_sibling_threads;
+ str = ph->env.sibling_threads;
for (i = 0; i < nr; i++) {
- str = do_read_string(fd, ph);
fprintf(fp, "# sibling threads : %s\n", str);
- free(str);
+ str += strlen(str) + 1;
}
}
-static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
+static void free_event_desc(struct perf_evsel *events)
+{
+ struct perf_evsel *evsel;
+
+ if (!events)
+ return;
+
+ for (evsel = events; evsel->attr.size; evsel++) {
+ if (evsel->name)
+ free(evsel->name);
+ if (evsel->id)
+ free(evsel->id);
+ }
+
+ free(events);
+}
+
+static struct perf_evsel *
+read_event_desc(struct perf_header *ph, int fd)
{
- struct perf_event_attr attr;
- uint64_t id;
+ struct perf_evsel *evsel, *events = NULL;
+ u64 *id;
void *buf = NULL;
- char *str;
u32 nre, sz, nr, i, j;
ssize_t ret;
size_t msz;
@@ -1173,18 +1222,22 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
if (ph->needs_swap)
sz = bswap_32(sz);
- memset(&attr, 0, sizeof(attr));
-
/* buffer to hold on file attr struct */
buf = malloc(sz);
if (!buf)
goto error;
- msz = sizeof(attr);
+ /* the last event terminates with evsel->attr.size == 0: */
+ events = calloc(nre + 1, sizeof(*events));
+ if (!events)
+ goto error;
+
+ msz = sizeof(evsel->attr);
if (sz < msz)
msz = sz;
- for (i = 0 ; i < nre; i++) {
+ for (i = 0, evsel = events; i < nre; evsel++, i++) {
+ evsel->idx = i;
/*
* must read entire on-file attr struct to
@@ -1197,146 +1250,188 @@ static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
if (ph->needs_swap)
perf_event__attr_swap(buf);
- memcpy(&attr, buf, msz);
+ memcpy(&evsel->attr, buf, msz);
ret = read(fd, &nr, sizeof(nr));
if (ret != (ssize_t)sizeof(nr))
goto error;
- if (ph->needs_swap)
+ if (ph->needs_swap) {
nr = bswap_32(nr);
+ evsel->needs_swap = true;
+ }
- str = do_read_string(fd, ph);
- fprintf(fp, "# event : name = %s, ", str);
- free(str);
+ evsel->name = do_read_string(fd, ph);
+
+ if (!nr)
+ continue;
+
+ id = calloc(nr, sizeof(*id));
+ if (!id)
+ goto error;
+ evsel->ids = nr;
+ evsel->id = id;
+
+ for (j = 0 ; j < nr; j++) {
+ ret = read(fd, id, sizeof(*id));
+ if (ret != (ssize_t)sizeof(*id))
+ goto error;
+ if (ph->needs_swap)
+ *id = bswap_64(*id);
+ id++;
+ }
+ }
+out:
+ if (buf)
+ free(buf);
+ return events;
+error:
+ if (events)
+ free_event_desc(events);
+ events = NULL;
+ goto out;
+}
+
+static void print_event_desc(struct perf_header *ph, int fd, FILE *fp)
+{
+ struct perf_evsel *evsel, *events = read_event_desc(ph, fd);
+ u32 j;
+ u64 *id;
+
+ if (!events) {
+ fprintf(fp, "# event desc: not available or unable to read\n");
+ return;
+ }
+
+ for (evsel = events; evsel->attr.size; evsel++) {
+ fprintf(fp, "# event : name = %s, ", evsel->name);
fprintf(fp, "type = %d, config = 0x%"PRIx64
", config1 = 0x%"PRIx64", config2 = 0x%"PRIx64,
- attr.type,
- (u64)attr.config,
- (u64)attr.config1,
- (u64)attr.config2);
+ evsel->attr.type,
+ (u64)evsel->attr.config,
+ (u64)evsel->attr.config1,
+ (u64)evsel->attr.config2);
fprintf(fp, ", excl_usr = %d, excl_kern = %d",
- attr.exclude_user,
- attr.exclude_kernel);
+ evsel->attr.exclude_user,
+ evsel->attr.exclude_kernel);
fprintf(fp, ", excl_host = %d, excl_guest = %d",
- attr.exclude_host,
- attr.exclude_guest);
+ evsel->attr.exclude_host,
+ evsel->attr.exclude_guest);
- fprintf(fp, ", precise_ip = %d", attr.precise_ip);
+ fprintf(fp, ", precise_ip = %d", evsel->attr.precise_ip);
- if (nr)
+ if (evsel->ids) {
fprintf(fp, ", id = {");
-
- for (j = 0 ; j < nr; j++) {
- ret = read(fd, &id, sizeof(id));
- if (ret != (ssize_t)sizeof(id))
- goto error;
-
- if (ph->needs_swap)
- id = bswap_64(id);
-
- if (j)
- fputc(',', fp);
-
- fprintf(fp, " %"PRIu64, id);
- }
- if (nr && j == nr)
+ for (j = 0, id = evsel->id; j < evsel->ids; j++, id++) {
+ if (j)
+ fputc(',', fp);
+ fprintf(fp, " %"PRIu64, *id);
+ }
fprintf(fp, " }");
+ }
+
fputc('\n', fp);
}
- free(buf);
- return;
-error:
- fprintf(fp, "# event desc: not available or unable to read\n");
+
+ free_event_desc(events);
}
-static void print_total_mem(struct perf_header *h __used, int fd, FILE *fp)
+static void print_total_mem(struct perf_header *ph, int fd __maybe_unused,
+ FILE *fp)
{
- uint64_t mem;
- ssize_t ret;
-
- ret = read(fd, &mem, sizeof(mem));
- if (ret != sizeof(mem))
- goto error;
-
- if (h->needs_swap)
- mem = bswap_64(mem);
-
- fprintf(fp, "# total memory : %"PRIu64" kB\n", mem);
- return;
-error:
- fprintf(fp, "# total memory : unknown\n");
+ fprintf(fp, "# total memory : %Lu kB\n", ph->env.total_mem);
}
-static void print_numa_topology(struct perf_header *h __used, int fd, FILE *fp)
+static void print_numa_topology(struct perf_header *ph, int fd __maybe_unused,
+ FILE *fp)
{
- ssize_t ret;
u32 nr, c, i;
- char *str;
+ char *str, *tmp;
uint64_t mem_total, mem_free;
/* nr nodes */
- ret = read(fd, &nr, sizeof(nr));
- if (ret != (ssize_t)sizeof(nr))
- goto error;
-
- if (h->needs_swap)
- nr = bswap_32(nr);
+ nr = ph->env.nr_numa_nodes;
+ str = ph->env.numa_nodes;
for (i = 0; i < nr; i++) {
-
/* node number */
- ret = read(fd, &c, sizeof(c));
- if (ret != (ssize_t)sizeof(c))
+ c = strtoul(str, &tmp, 0);
+ if (*tmp != ':')
goto error;
- if (h->needs_swap)
- c = bswap_32(c);
-
- ret = read(fd, &mem_total, sizeof(u64));
- if (ret != sizeof(u64))
+ str = tmp + 1;
+ mem_total = strtoull(str, &tmp, 0);
+ if (*tmp != ':')
goto error;
- ret = read(fd, &mem_free, sizeof(u64));
- if (ret != sizeof(u64))
+ str = tmp + 1;
+ mem_free = strtoull(str, &tmp, 0);
+ if (*tmp != ':')
goto error;
- if (h->needs_swap) {
- mem_total = bswap_64(mem_total);
- mem_free = bswap_64(mem_free);
- }
-
fprintf(fp, "# node%u meminfo : total = %"PRIu64" kB,"
" free = %"PRIu64" kB\n",
- c,
- mem_total,
- mem_free);
+ c, mem_total, mem_free);
- str = do_read_string(fd, h);
+ str = tmp + 1;
fprintf(fp, "# node%u cpu list : %s\n", c, str);
- free(str);
}
return;
error:
fprintf(fp, "# numa topology : not available\n");
}
-static void print_cpuid(struct perf_header *ph, int fd, FILE *fp)
+static void print_cpuid(struct perf_header *ph, int fd __maybe_unused, FILE *fp)
{
- char *str = do_read_string(fd, ph);
- fprintf(fp, "# cpuid : %s\n", str);
- free(str);
+ fprintf(fp, "# cpuid : %s\n", ph->env.cpuid);
}
-static void print_branch_stack(struct perf_header *ph __used, int fd __used,
- FILE *fp)
+static void print_branch_stack(struct perf_header *ph __maybe_unused,
+ int fd __maybe_unused, FILE *fp)
{
fprintf(fp, "# contains samples with branch stack\n");
}
+static void print_pmu_mappings(struct perf_header *ph, int fd __maybe_unused,
+ FILE *fp)
+{
+ const char *delimiter = "# pmu mappings: ";
+ char *str, *tmp;
+ u32 pmu_num;
+ u32 type;
+
+ pmu_num = ph->env.nr_pmu_mappings;
+ if (!pmu_num) {
+ fprintf(fp, "# pmu mappings: not available\n");
+ return;
+ }
+
+ str = ph->env.pmu_mappings;
+
+ while (pmu_num) {
+ type = strtoul(str, &tmp, 0);
+ if (*tmp != ':')
+ goto error;
+
+ str = tmp + 1;
+ fprintf(fp, "%s%s = %" PRIu32, delimiter, str, type);
+
+ delimiter = ", ";
+ str += strlen(str) + 1;
+ pmu_num--;
+ }
+
+ fprintf(fp, "\n");
+
+ if (!pmu_num)
+ return;
+error:
+ fprintf(fp, "# pmu mappings: unable to read\n");
+}
+
static int __event_process_build_id(struct build_id_event *bev,
char *filename,
struct perf_session *session)
@@ -1398,7 +1493,7 @@ static int perf_header__read_build_ids_abi_quirk(struct perf_header *header,
struct perf_session *session = container_of(header, struct perf_session, header);
struct {
struct perf_event_header header;
- u8 build_id[ALIGN(BUILD_ID_SIZE, sizeof(u64))];
+ u8 build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))];
char filename[0];
} old_bev;
struct build_id_event bev;
@@ -1487,28 +1582,375 @@ out:
return err;
}
-static int process_tracing_data(struct perf_file_section *section __unused,
- struct perf_header *ph __unused,
- int feat __unused, int fd, void *data)
+static int process_tracing_data(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph __maybe_unused,
+ int fd, void *data)
{
trace_report(fd, data, false);
return 0;
}
static int process_build_id(struct perf_file_section *section,
- struct perf_header *ph,
- int feat __unused, int fd, void *data __used)
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
{
if (perf_header__read_build_ids(ph, fd, section->offset, section->size))
pr_debug("Failed to read buildids, continuing...\n");
return 0;
}
+static int process_hostname(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ ph->env.hostname = do_read_string(fd, ph);
+ return ph->env.hostname ? 0 : -ENOMEM;
+}
+
+static int process_osrelease(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ ph->env.os_release = do_read_string(fd, ph);
+ return ph->env.os_release ? 0 : -ENOMEM;
+}
+
+static int process_version(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ ph->env.version = do_read_string(fd, ph);
+ return ph->env.version ? 0 : -ENOMEM;
+}
+
+static int process_arch(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ ph->env.arch = do_read_string(fd, ph);
+ return ph->env.arch ? 0 : -ENOMEM;
+}
+
+static int process_nrcpus(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ size_t ret;
+ u32 nr;
+
+ ret = read(fd, &nr, sizeof(nr));
+ if (ret != sizeof(nr))
+ return -1;
+
+ if (ph->needs_swap)
+ nr = bswap_32(nr);
+
+ ph->env.nr_cpus_online = nr;
+
+ ret = read(fd, &nr, sizeof(nr));
+ if (ret != sizeof(nr))
+ return -1;
+
+ if (ph->needs_swap)
+ nr = bswap_32(nr);
+
+ ph->env.nr_cpus_avail = nr;
+ return 0;
+}
+
+static int process_cpudesc(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ ph->env.cpu_desc = do_read_string(fd, ph);
+ return ph->env.cpu_desc ? 0 : -ENOMEM;
+}
+
+static int process_cpuid(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ ph->env.cpuid = do_read_string(fd, ph);
+ return ph->env.cpuid ? 0 : -ENOMEM;
+}
+
+static int process_total_mem(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ uint64_t mem;
+ size_t ret;
+
+ ret = read(fd, &mem, sizeof(mem));
+ if (ret != sizeof(mem))
+ return -1;
+
+ if (ph->needs_swap)
+ mem = bswap_64(mem);
+
+ ph->env.total_mem = mem;
+ return 0;
+}
+
+static struct perf_evsel *
+perf_evlist__find_by_index(struct perf_evlist *evlist, int idx)
+{
+ struct perf_evsel *evsel;
+
+ list_for_each_entry(evsel, &evlist->entries, node) {
+ if (evsel->idx == idx)
+ return evsel;
+ }
+
+ return NULL;
+}
+
+static void
+perf_evlist__set_event_name(struct perf_evlist *evlist,
+ struct perf_evsel *event)
+{
+ struct perf_evsel *evsel;
+
+ if (!event->name)
+ return;
+
+ evsel = perf_evlist__find_by_index(evlist, event->idx);
+ if (!evsel)
+ return;
+
+ if (evsel->name)
+ return;
+
+ evsel->name = strdup(event->name);
+}
+
+static int
+process_event_desc(struct perf_file_section *section __maybe_unused,
+ struct perf_header *header, int fd,
+ void *data __maybe_unused)
+{
+ struct perf_session *session;
+ struct perf_evsel *evsel, *events = read_event_desc(header, fd);
+
+ if (!events)
+ return 0;
+
+ session = container_of(header, struct perf_session, header);
+ for (evsel = events; evsel->attr.size; evsel++)
+ perf_evlist__set_event_name(session->evlist, evsel);
+
+ free_event_desc(events);
+
+ return 0;
+}
+
+static int process_cmdline(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ size_t ret;
+ char *str;
+ u32 nr, i;
+ struct strbuf sb;
+
+ ret = read(fd, &nr, sizeof(nr));
+ if (ret != sizeof(nr))
+ return -1;
+
+ if (ph->needs_swap)
+ nr = bswap_32(nr);
+
+ ph->env.nr_cmdline = nr;
+ strbuf_init(&sb, 128);
+
+ for (i = 0; i < nr; i++) {
+ str = do_read_string(fd, ph);
+ if (!str)
+ goto error;
+
+ /* include a NULL character at the end */
+ strbuf_add(&sb, str, strlen(str) + 1);
+ free(str);
+ }
+ ph->env.cmdline = strbuf_detach(&sb, NULL);
+ return 0;
+
+error:
+ strbuf_release(&sb);
+ return -1;
+}
+
+static int process_cpu_topology(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ size_t ret;
+ u32 nr, i;
+ char *str;
+ struct strbuf sb;
+
+ ret = read(fd, &nr, sizeof(nr));
+ if (ret != sizeof(nr))
+ return -1;
+
+ if (ph->needs_swap)
+ nr = bswap_32(nr);
+
+ ph->env.nr_sibling_cores = nr;
+ strbuf_init(&sb, 128);
+
+ for (i = 0; i < nr; i++) {
+ str = do_read_string(fd, ph);
+ if (!str)
+ goto error;
+
+ /* include a NULL character at the end */
+ strbuf_add(&sb, str, strlen(str) + 1);
+ free(str);
+ }
+ ph->env.sibling_cores = strbuf_detach(&sb, NULL);
+
+ ret = read(fd, &nr, sizeof(nr));
+ if (ret != sizeof(nr))
+ return -1;
+
+ if (ph->needs_swap)
+ nr = bswap_32(nr);
+
+ ph->env.nr_sibling_threads = nr;
+
+ for (i = 0; i < nr; i++) {
+ str = do_read_string(fd, ph);
+ if (!str)
+ goto error;
+
+ /* include a NULL character at the end */
+ strbuf_add(&sb, str, strlen(str) + 1);
+ free(str);
+ }
+ ph->env.sibling_threads = strbuf_detach(&sb, NULL);
+ return 0;
+
+error:
+ strbuf_release(&sb);
+ return -1;
+}
+
+static int process_numa_topology(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ size_t ret;
+ u32 nr, node, i;
+ char *str;
+ uint64_t mem_total, mem_free;
+ struct strbuf sb;
+
+ /* nr nodes */
+ ret = read(fd, &nr, sizeof(nr));
+ if (ret != sizeof(nr))
+ goto error;
+
+ if (ph->needs_swap)
+ nr = bswap_32(nr);
+
+ ph->env.nr_numa_nodes = nr;
+ strbuf_init(&sb, 256);
+
+ for (i = 0; i < nr; i++) {
+ /* node number */
+ ret = read(fd, &node, sizeof(node));
+ if (ret != sizeof(node))
+ goto error;
+
+ ret = read(fd, &mem_total, sizeof(u64));
+ if (ret != sizeof(u64))
+ goto error;
+
+ ret = read(fd, &mem_free, sizeof(u64));
+ if (ret != sizeof(u64))
+ goto error;
+
+ if (ph->needs_swap) {
+ node = bswap_32(node);
+ mem_total = bswap_64(mem_total);
+ mem_free = bswap_64(mem_free);
+ }
+
+ strbuf_addf(&sb, "%u:%"PRIu64":%"PRIu64":",
+ node, mem_total, mem_free);
+
+ str = do_read_string(fd, ph);
+ if (!str)
+ goto error;
+
+ /* include a NULL character at the end */
+ strbuf_add(&sb, str, strlen(str) + 1);
+ free(str);
+ }
+ ph->env.numa_nodes = strbuf_detach(&sb, NULL);
+ return 0;
+
+error:
+ strbuf_release(&sb);
+ return -1;
+}
+
+static int process_pmu_mappings(struct perf_file_section *section __maybe_unused,
+ struct perf_header *ph, int fd,
+ void *data __maybe_unused)
+{
+ size_t ret;
+ char *name;
+ u32 pmu_num;
+ u32 type;
+ struct strbuf sb;
+
+ ret = read(fd, &pmu_num, sizeof(pmu_num));
+ if (ret != sizeof(pmu_num))
+ return -1;
+
+ if (ph->needs_swap)
+ pmu_num = bswap_32(pmu_num);
+
+ if (!pmu_num) {
+ pr_debug("pmu mappings not available\n");
+ return 0;
+ }
+
+ ph->env.nr_pmu_mappings = pmu_num;
+ strbuf_init(&sb, 128);
+
+ while (pmu_num) {
+ if (read(fd, &type, sizeof(type)) != sizeof(type))
+ goto error;
+ if (ph->needs_swap)
+ type = bswap_32(type);
+
+ name = do_read_string(fd, ph);
+ if (!name)
+ goto error;
+
+ strbuf_addf(&sb, "%u:%s", type, name);
+ /* include a NULL character at the end */
+ strbuf_add(&sb, "", 1);
+
+ free(name);
+ pmu_num--;
+ }
+ ph->env.pmu_mappings = strbuf_detach(&sb, NULL);
+ return 0;
+
+error:
+ strbuf_release(&sb);
+ return -1;
+}
+
struct feature_ops {
int (*write)(int fd, struct perf_header *h, struct perf_evlist *evlist);
void (*print)(struct perf_header *h, int fd, FILE *fp);
int (*process)(struct perf_file_section *section,
- struct perf_header *h, int feat, int fd, void *data);
+ struct perf_header *h, int fd, void *data);
const char *name;
bool full_only;
};
@@ -1520,7 +1962,7 @@ struct feature_ops {
.process = process_##func }
#define FEAT_OPF(n, func) \
[n] = { .name = #n, .write = write_##func, .print = print_##func, \
- .full_only = true }
+ .process = process_##func, .full_only = true }
/* feature_ops not implemented: */
#define print_tracing_data NULL
@@ -1529,19 +1971,20 @@ struct feature_ops {
static const struct feature_ops feat_ops[HEADER_LAST_FEATURE] = {
FEAT_OPP(HEADER_TRACING_DATA, tracing_data),
FEAT_OPP(HEADER_BUILD_ID, build_id),
- FEAT_OPA(HEADER_HOSTNAME, hostname),
- FEAT_OPA(HEADER_OSRELEASE, osrelease),
- FEAT_OPA(HEADER_VERSION, version),
- FEAT_OPA(HEADER_ARCH, arch),
- FEAT_OPA(HEADER_NRCPUS, nrcpus),
- FEAT_OPA(HEADER_CPUDESC, cpudesc),
- FEAT_OPA(HEADER_CPUID, cpuid),
- FEAT_OPA(HEADER_TOTAL_MEM, total_mem),
- FEAT_OPA(HEADER_EVENT_DESC, event_desc),
- FEAT_OPA(HEADER_CMDLINE, cmdline),
+ FEAT_OPP(HEADER_HOSTNAME, hostname),
+ FEAT_OPP(HEADER_OSRELEASE, osrelease),
+ FEAT_OPP(HEADER_VERSION, version),
+ FEAT_OPP(HEADER_ARCH, arch),
+ FEAT_OPP(HEADER_NRCPUS, nrcpus),
+ FEAT_OPP(HEADER_CPUDESC, cpudesc),
+ FEAT_OPP(HEADER_CPUID, cpuid),
+ FEAT_OPP(HEADER_TOTAL_MEM, total_mem),
+ FEAT_OPP(HEADER_EVENT_DESC, event_desc),
+ FEAT_OPP(HEADER_CMDLINE, cmdline),
FEAT_OPF(HEADER_CPU_TOPOLOGY, cpu_topology),
FEAT_OPF(HEADER_NUMA_TOPOLOGY, numa_topology),
FEAT_OPA(HEADER_BRANCH_STACK, branch_stack),
+ FEAT_OPP(HEADER_PMU_MAPPINGS, pmu_mappings),
};
struct header_print_data {
@@ -1683,17 +2126,17 @@ int perf_session__write_header(struct perf_session *session,
struct perf_file_header f_header;
struct perf_file_attr f_attr;
struct perf_header *header = &session->header;
- struct perf_evsel *attr, *pair = NULL;
+ struct perf_evsel *evsel, *pair = NULL;
int err;
lseek(fd, sizeof(f_header), SEEK_SET);
if (session->evlist != evlist)
- pair = list_entry(session->evlist->entries.next, struct perf_evsel, node);
+ pair = perf_evlist__first(session->evlist);
- list_for_each_entry(attr, &evlist->entries, node) {
- attr->id_offset = lseek(fd, 0, SEEK_CUR);
- err = do_write(fd, attr->id, attr->ids * sizeof(u64));
+ list_for_each_entry(evsel, &evlist->entries, node) {
+ evsel->id_offset = lseek(fd, 0, SEEK_CUR);
+ err = do_write(fd, evsel->id, evsel->ids * sizeof(u64));
if (err < 0) {
out_err_write:
pr_debug("failed to write perf header\n");
@@ -1703,19 +2146,19 @@ out_err_write:
err = do_write(fd, pair->id, pair->ids * sizeof(u64));
if (err < 0)
goto out_err_write;
- attr->ids += pair->ids;
- pair = list_entry(pair->node.next, struct perf_evsel, node);
+ evsel->ids += pair->ids;
+ pair = perf_evsel__next(pair);
}
}
header->attr_offset = lseek(fd, 0, SEEK_CUR);
- list_for_each_entry(attr, &evlist->entries, node) {
+ list_for_each_entry(evsel, &evlist->entries, node) {
f_attr = (struct perf_file_attr){
- .attr = attr->attr,
+ .attr = evsel->attr,
.ids = {
- .offset = attr->id_offset,
- .size = attr->ids * sizeof(u64),
+ .offset = evsel->id_offset,
+ .size = evsel->ids * sizeof(u64),
}
};
err = do_write(fd, &f_attr, sizeof(f_attr));
@@ -1726,9 +2169,9 @@ out_err_write:
}
header->event_offset = lseek(fd, 0, SEEK_CUR);
- header->event_size = event_count * sizeof(struct perf_trace_event_type);
- if (events) {
- err = do_write(fd, events, header->event_size);
+ header->event_size = trace_event_count * sizeof(struct perf_trace_event_type);
+ if (trace_events) {
+ err = do_write(fd, trace_events, header->event_size);
if (err < 0) {
pr_debug("failed to write perf header events\n");
return err;
@@ -1829,6 +2272,8 @@ out_free:
static const int attr_file_abi_sizes[] = {
[0] = PERF_ATTR_SIZE_VER0,
[1] = PERF_ATTR_SIZE_VER1,
+ [2] = PERF_ATTR_SIZE_VER2,
+ [3] = PERF_ATTR_SIZE_VER3,
0,
};
@@ -2019,7 +2464,7 @@ static int perf_file_section__process(struct perf_file_section *section,
if (!feat_ops[feat].process)
return 0;
- return feat_ops[feat].process(section, ph, feat, fd, data);
+ return feat_ops[feat].process(section, ph, fd, data);
}
static int perf_file_header__read_pipe(struct perf_pipe_file_header *header,
@@ -2108,32 +2553,39 @@ static int read_attr(int fd, struct perf_header *ph,
return ret <= 0 ? -1 : 0;
}
-static int perf_evsel__set_tracepoint_name(struct perf_evsel *evsel,
- struct pevent *pevent)
+static int perf_evsel__prepare_tracepoint_event(struct perf_evsel *evsel,
+ struct pevent *pevent)
{
- struct event_format *event = pevent_find_event(pevent,
- evsel->attr.config);
+ struct event_format *event;
char bf[128];
+ /* already prepared */
+ if (evsel->tp_format)
+ return 0;
+
+ event = pevent_find_event(pevent, evsel->attr.config);
if (event == NULL)
return -1;
- snprintf(bf, sizeof(bf), "%s:%s", event->system, event->name);
- evsel->name = strdup(bf);
- if (event->name == NULL)
- return -1;
+ if (!evsel->name) {
+ snprintf(bf, sizeof(bf), "%s:%s", event->system, event->name);
+ evsel->name = strdup(bf);
+ if (evsel->name == NULL)
+ return -1;
+ }
+ evsel->tp_format = event;
return 0;
}
-static int perf_evlist__set_tracepoint_names(struct perf_evlist *evlist,
- struct pevent *pevent)
+static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist,
+ struct pevent *pevent)
{
struct perf_evsel *pos;
list_for_each_entry(pos, &evlist->entries, node) {
if (pos->attr.type == PERF_TYPE_TRACEPOINT &&
- perf_evsel__set_tracepoint_name(pos, pevent))
+ perf_evsel__prepare_tracepoint_event(pos, pevent))
return -1;
}
@@ -2176,6 +2628,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
if (evsel == NULL)
goto out_delete_evlist;
+
+ evsel->needs_swap = header->needs_swap;
/*
* Do it before so that if perf_evsel__alloc_id fails, this
* entry gets purged too at perf_evlist__delete().
@@ -2207,13 +2661,13 @@ int perf_session__read_header(struct perf_session *session, int fd)
if (f_header.event_types.size) {
lseek(fd, f_header.event_types.offset, SEEK_SET);
- events = malloc(f_header.event_types.size);
- if (events == NULL)
+ trace_events = malloc(f_header.event_types.size);
+ if (trace_events == NULL)
return -ENOMEM;
- if (perf_header__getbuffer64(header, fd, events,
+ if (perf_header__getbuffer64(header, fd, trace_events,
f_header.event_types.size))
goto out_errno;
- event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
+ trace_event_count = f_header.event_types.size / sizeof(struct perf_trace_event_type);
}
perf_header__process_sections(header, fd, &session->pevent,
@@ -2221,7 +2675,8 @@ int perf_session__read_header(struct perf_session *session, int fd)
lseek(fd, header->data_offset, SEEK_SET);
- if (perf_evlist__set_tracepoint_names(session->evlist, session->pevent))
+ if (perf_evlist__prepare_tracepoint_events(session->evlist,
+ session->pevent))
goto out_delete_evlist;
header->frozen = 1;
@@ -2236,7 +2691,7 @@ out_delete_evlist:
}
int perf_event__synthesize_attr(struct perf_tool *tool,
- struct perf_event_attr *attr, u16 ids, u64 *id,
+ struct perf_event_attr *attr, u32 ids, u64 *id,
perf_event__handler_t process)
{
union perf_event *ev;
@@ -2244,7 +2699,7 @@ int perf_event__synthesize_attr(struct perf_tool *tool,
int err;
size = sizeof(struct perf_event_attr);
- size = ALIGN(size, sizeof(u64));
+ size = PERF_ALIGN(size, sizeof(u64));
size += sizeof(struct perf_event_header);
size += ids * sizeof(u64);
@@ -2257,9 +2712,12 @@ int perf_event__synthesize_attr(struct perf_tool *tool,
memcpy(ev->attr.id, id, ids * sizeof(u64));
ev->attr.header.type = PERF_RECORD_HEADER_ATTR;
- ev->attr.header.size = size;
+ ev->attr.header.size = (u16)size;
- err = process(tool, ev, NULL, NULL);
+ if (ev->attr.header.size == size)
+ err = process(tool, ev, NULL, NULL);
+ else
+ err = -E2BIG;
free(ev);
@@ -2270,12 +2728,12 @@ int perf_event__synthesize_attrs(struct perf_tool *tool,
struct perf_session *session,
perf_event__handler_t process)
{
- struct perf_evsel *attr;
+ struct perf_evsel *evsel;
int err = 0;
- list_for_each_entry(attr, &session->evlist->entries, node) {
- err = perf_event__synthesize_attr(tool, &attr->attr, attr->ids,
- attr->id, process);
+ list_for_each_entry(evsel, &session->evlist->entries, node) {
+ err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids,
+ evsel->id, process);
if (err) {
pr_debug("failed to create perf header attribute\n");
return err;
@@ -2288,7 +2746,7 @@ int perf_event__synthesize_attrs(struct perf_tool *tool,
int perf_event__process_attr(union perf_event *event,
struct perf_evlist **pevlist)
{
- unsigned int i, ids, n_ids;
+ u32 i, ids, n_ids;
struct perf_evsel *evsel;
struct perf_evlist *evlist = *pevlist;
@@ -2339,7 +2797,7 @@ int perf_event__synthesize_event_type(struct perf_tool *tool,
ev.event_type.header.type = PERF_RECORD_HEADER_EVENT_TYPE;
size = strlen(ev.event_type.event_type.name);
- size = ALIGN(size, sizeof(u64));
+ size = PERF_ALIGN(size, sizeof(u64));
ev.event_type.header.size = sizeof(ev.event_type) -
(sizeof(ev.event_type.event_type.name) - size);
@@ -2355,8 +2813,8 @@ int perf_event__synthesize_event_types(struct perf_tool *tool,
struct perf_trace_event_type *type;
int i, err = 0;
- for (i = 0; i < event_count; i++) {
- type = &events[i];
+ for (i = 0; i < trace_event_count; i++) {
+ type = &trace_events[i];
err = perf_event__synthesize_event_type(tool, type->event_id,
type->name, process,
@@ -2370,7 +2828,7 @@ int perf_event__synthesize_event_types(struct perf_tool *tool,
return err;
}
-int perf_event__process_event_type(struct perf_tool *tool __unused,
+int perf_event__process_event_type(struct perf_tool *tool __maybe_unused,
union perf_event *event)
{
if (perf_header__push_event(event->event_type.event_type.event_id,
@@ -2387,7 +2845,7 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
union perf_event ev;
struct tracing_data *tdata;
ssize_t size = 0, aligned_size = 0, padding;
- int err __used = 0;
+ int err __maybe_unused = 0;
/*
* We are going to store the size of the data followed
@@ -2408,7 +2866,7 @@ int perf_event__synthesize_tracing_data(struct perf_tool *tool, int fd,
ev.tracing_data.header.type = PERF_RECORD_HEADER_TRACING_DATA;
size = tdata->size;
- aligned_size = ALIGN(size, sizeof(u64));
+ aligned_size = PERF_ALIGN(size, sizeof(u64));
padding = aligned_size - size;
ev.tracing_data.header.size = sizeof(ev.tracing_data);
ev.tracing_data.size = aligned_size;
@@ -2439,7 +2897,7 @@ int perf_event__process_tracing_data(union perf_event *event,
size_read = trace_report(session->fd, &session->pevent,
session->repipe);
- padding = ALIGN(size_read, sizeof(u64)) - size_read;
+ padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read;
if (read(session->fd, buf, padding) < 0)
die("reading input file");
@@ -2452,6 +2910,9 @@ int perf_event__process_tracing_data(union perf_event *event,
if (size_read + padding != size)
die("tracing data size mismatch");
+ perf_evlist__prepare_tracepoint_events(session->evlist,
+ session->pevent);
+
return size_read + padding;
}
@@ -2470,7 +2931,7 @@ int perf_event__synthesize_build_id(struct perf_tool *tool,
memset(&ev, 0, sizeof(ev));
len = pos->long_name_len + 1;
- len = ALIGN(len, NAME_ALIGN);
+ len = PERF_ALIGN(len, NAME_ALIGN);
memcpy(&ev.build_id.build_id, pos->build_id, sizeof(pos->build_id));
ev.build_id.header.type = PERF_RECORD_HEADER_BUILD_ID;
ev.build_id.header.misc = misc;
@@ -2483,7 +2944,7 @@ int perf_event__synthesize_build_id(struct perf_tool *tool,
return err;
}
-int perf_event__process_build_id(struct perf_tool *tool __used,
+int perf_event__process_build_id(struct perf_tool *tool __maybe_unused,
union perf_event *event,
struct perf_session *session)
{
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h
index 2d42b3e1826f..99bdd3abce59 100644
--- a/tools/perf/util/header.h
+++ b/tools/perf/util/header.h
@@ -28,6 +28,7 @@ enum {
HEADER_CPU_TOPOLOGY,
HEADER_NUMA_TOPOLOGY,
HEADER_BRANCH_STACK,
+ HEADER_PMU_MAPPINGS,
HEADER_LAST_FEATURE,
HEADER_FEAT_BITS = 256,
};
@@ -57,6 +58,29 @@ struct perf_header;
int perf_file_header__read(struct perf_file_header *header,
struct perf_header *ph, int fd);
+struct perf_session_env {
+ char *hostname;
+ char *os_release;
+ char *version;
+ char *arch;
+ int nr_cpus_online;
+ int nr_cpus_avail;
+ char *cpu_desc;
+ char *cpuid;
+ unsigned long long total_mem;
+
+ int nr_cmdline;
+ char *cmdline;
+ int nr_sibling_cores;
+ char *sibling_cores;
+ int nr_sibling_threads;
+ char *sibling_threads;
+ int nr_numa_nodes;
+ char *numa_nodes;
+ int nr_pmu_mappings;
+ char *pmu_mappings;
+};
+
struct perf_header {
int frozen;
bool needs_swap;
@@ -66,6 +90,7 @@ struct perf_header {
u64 event_offset;
u64 event_size;
DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+ struct perf_session_env env;
};
struct perf_evlist;
@@ -95,11 +120,11 @@ int perf_header__process_sections(struct perf_header *header, int fd,
int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full);
int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
- const char *name, bool is_kallsyms);
+ const char *name, bool is_kallsyms, bool is_vdso);
int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
int perf_event__synthesize_attr(struct perf_tool *tool,
- struct perf_event_attr *attr, u16 ids, u64 *id,
+ struct perf_event_attr *attr, u32 ids, u64 *id,
perf_event__handler_t process);
int perf_event__synthesize_attrs(struct perf_tool *tool,
struct perf_session *session,
diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c
index 6f2975a00358..8b1f6e891b8a 100644
--- a/tools/perf/util/help.c
+++ b/tools/perf/util/help.c
@@ -3,6 +3,7 @@
#include "exec_cmd.h"
#include "levenshtein.h"
#include "help.h"
+#include <termios.h>
void add_cmdname(struct cmdnames *cmds, const char *name, size_t len)
{
@@ -331,7 +332,8 @@ const char *help_unknown_cmd(const char *cmd)
exit(1);
}
-int cmd_version(int argc __used, const char **argv __used, const char *prefix __used)
+int cmd_version(int argc __maybe_unused, const char **argv __maybe_unused,
+ const char *prefix __maybe_unused)
{
printf("perf version %s\n", perf_version_string);
return 0;
diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c
index f247ef2789a4..236bc9d98ff2 100644
--- a/tools/perf/util/hist.c
+++ b/tools/perf/util/hist.c
@@ -45,7 +45,7 @@ bool hists__new_col_len(struct hists *hists, enum hist_column col, u16 len)
return false;
}
-static void hists__reset_col_len(struct hists *hists)
+void hists__reset_col_len(struct hists *hists)
{
enum hist_column col;
@@ -63,7 +63,7 @@ static void hists__set_unres_dso_col_len(struct hists *hists, int dso)
hists__set_col_len(hists, dso, unresolved_col_width);
}
-static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
+void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
{
const unsigned int unresolved_col_width = BITS_PER_LONG / 4;
u16 len;
@@ -114,6 +114,22 @@ static void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
}
}
+void hists__output_recalc_col_len(struct hists *hists, int max_rows)
+{
+ struct rb_node *next = rb_first(&hists->entries);
+ struct hist_entry *n;
+ int row = 0;
+
+ hists__reset_col_len(hists);
+
+ while (next && row++ < max_rows) {
+ n = rb_entry(next, struct hist_entry, rb_node);
+ if (!n->filtered)
+ hists__calc_col_len(hists, n);
+ next = rb_next(&n->rb_node);
+ }
+}
+
static void hist_entry__add_cpumode_period(struct hist_entry *he,
unsigned int cpumode, u64 period)
{
@@ -378,7 +394,7 @@ void hist_entry__free(struct hist_entry *he)
* collapse the histogram
*/
-static bool hists__collapse_insert_entry(struct hists *hists __used,
+static bool hists__collapse_insert_entry(struct hists *hists __maybe_unused,
struct rb_root *root,
struct hist_entry *he)
{
@@ -394,8 +410,13 @@ static bool hists__collapse_insert_entry(struct hists *hists __used,
cmp = hist_entry__collapse(iter, he);
if (!cmp) {
- iter->period += he->period;
- iter->nr_events += he->nr_events;
+ iter->period += he->period;
+ iter->period_sys += he->period_sys;
+ iter->period_us += he->period_us;
+ iter->period_guest_sys += he->period_guest_sys;
+ iter->period_guest_us += he->period_guest_us;
+ iter->nr_events += he->nr_events;
+
if (symbol_conf.use_callchain) {
callchain_cursor_reset(&callchain_cursor);
callchain_merge(&callchain_cursor,
@@ -547,674 +568,6 @@ void hists__output_resort_threaded(struct hists *hists)
return __hists__output_resort(hists, true);
}
-static size_t callchain__fprintf_left_margin(FILE *fp, int left_margin)
-{
- int i;
- int ret = fprintf(fp, " ");
-
- for (i = 0; i < left_margin; i++)
- ret += fprintf(fp, " ");
-
- return ret;
-}
-
-static size_t ipchain__fprintf_graph_line(FILE *fp, int depth, int depth_mask,
- int left_margin)
-{
- int i;
- size_t ret = callchain__fprintf_left_margin(fp, left_margin);
-
- for (i = 0; i < depth; i++)
- if (depth_mask & (1 << i))
- ret += fprintf(fp, "| ");
- else
- ret += fprintf(fp, " ");
-
- ret += fprintf(fp, "\n");
-
- return ret;
-}
-
-static size_t ipchain__fprintf_graph(FILE *fp, struct callchain_list *chain,
- int depth, int depth_mask, int period,
- u64 total_samples, u64 hits,
- int left_margin)
-{
- int i;
- size_t ret = 0;
-
- ret += callchain__fprintf_left_margin(fp, left_margin);
- for (i = 0; i < depth; i++) {
- if (depth_mask & (1 << i))
- ret += fprintf(fp, "|");
- else
- ret += fprintf(fp, " ");
- if (!period && i == depth - 1) {
- double percent;
-
- percent = hits * 100.0 / total_samples;
- ret += percent_color_fprintf(fp, "--%2.2f%%-- ", percent);
- } else
- ret += fprintf(fp, "%s", " ");
- }
- if (chain->ms.sym)
- ret += fprintf(fp, "%s\n", chain->ms.sym->name);
- else
- ret += fprintf(fp, "0x%0" PRIx64 "\n", chain->ip);
-
- return ret;
-}
-
-static struct symbol *rem_sq_bracket;
-static struct callchain_list rem_hits;
-
-static void init_rem_hits(void)
-{
- rem_sq_bracket = malloc(sizeof(*rem_sq_bracket) + 6);
- if (!rem_sq_bracket) {
- fprintf(stderr, "Not enough memory to display remaining hits\n");
- return;
- }
-
- strcpy(rem_sq_bracket->name, "[...]");
- rem_hits.ms.sym = rem_sq_bracket;
-}
-
-static size_t __callchain__fprintf_graph(FILE *fp, struct rb_root *root,
- u64 total_samples, int depth,
- int depth_mask, int left_margin)
-{
- struct rb_node *node, *next;
- struct callchain_node *child;
- struct callchain_list *chain;
- int new_depth_mask = depth_mask;
- u64 remaining;
- size_t ret = 0;
- int i;
- uint entries_printed = 0;
-
- remaining = total_samples;
-
- node = rb_first(root);
- while (node) {
- u64 new_total;
- u64 cumul;
-
- child = rb_entry(node, struct callchain_node, rb_node);
- cumul = callchain_cumul_hits(child);
- remaining -= cumul;
-
- /*
- * The depth mask manages the output of pipes that show
- * the depth. We don't want to keep the pipes of the current
- * level for the last child of this depth.
- * Except if we have remaining filtered hits. They will
- * supersede the last child
- */
- next = rb_next(node);
- if (!next && (callchain_param.mode != CHAIN_GRAPH_REL || !remaining))
- new_depth_mask &= ~(1 << (depth - 1));
-
- /*
- * But we keep the older depth mask for the line separator
- * to keep the level link until we reach the last child
- */
- ret += ipchain__fprintf_graph_line(fp, depth, depth_mask,
- left_margin);
- i = 0;
- list_for_each_entry(chain, &child->val, list) {
- ret += ipchain__fprintf_graph(fp, chain, depth,
- new_depth_mask, i++,
- total_samples,
- cumul,
- left_margin);
- }
-
- if (callchain_param.mode == CHAIN_GRAPH_REL)
- new_total = child->children_hit;
- else
- new_total = total_samples;
-
- ret += __callchain__fprintf_graph(fp, &child->rb_root, new_total,
- depth + 1,
- new_depth_mask | (1 << depth),
- left_margin);
- node = next;
- if (++entries_printed == callchain_param.print_limit)
- break;
- }
-
- if (callchain_param.mode == CHAIN_GRAPH_REL &&
- remaining && remaining != total_samples) {
-
- if (!rem_sq_bracket)
- return ret;
-
- new_depth_mask &= ~(1 << (depth - 1));
- ret += ipchain__fprintf_graph(fp, &rem_hits, depth,
- new_depth_mask, 0, total_samples,
- remaining, left_margin);
- }
-
- return ret;
-}
-
-static size_t callchain__fprintf_graph(FILE *fp, struct rb_root *root,
- u64 total_samples, int left_margin)
-{
- struct callchain_node *cnode;
- struct callchain_list *chain;
- u32 entries_printed = 0;
- bool printed = false;
- struct rb_node *node;
- int i = 0;
- int ret = 0;
-
- /*
- * If have one single callchain root, don't bother printing
- * its percentage (100 % in fractal mode and the same percentage
- * than the hist in graph mode). This also avoid one level of column.
- */
- node = rb_first(root);
- if (node && !rb_next(node)) {
- cnode = rb_entry(node, struct callchain_node, rb_node);
- list_for_each_entry(chain, &cnode->val, list) {
- /*
- * If we sort by symbol, the first entry is the same than
- * the symbol. No need to print it otherwise it appears as
- * displayed twice.
- */
- if (!i++ && sort__first_dimension == SORT_SYM)
- continue;
- if (!printed) {
- ret += callchain__fprintf_left_margin(fp, left_margin);
- ret += fprintf(fp, "|\n");
- ret += callchain__fprintf_left_margin(fp, left_margin);
- ret += fprintf(fp, "---");
- left_margin += 3;
- printed = true;
- } else
- ret += callchain__fprintf_left_margin(fp, left_margin);
-
- if (chain->ms.sym)
- ret += fprintf(fp, " %s\n", chain->ms.sym->name);
- else
- ret += fprintf(fp, " %p\n", (void *)(long)chain->ip);
-
- if (++entries_printed == callchain_param.print_limit)
- break;
- }
- root = &cnode->rb_root;
- }
-
- ret += __callchain__fprintf_graph(fp, root, total_samples,
- 1, 1, left_margin);
- ret += fprintf(fp, "\n");
-
- return ret;
-}
-
-static size_t __callchain__fprintf_flat(FILE *fp,
- struct callchain_node *self,
- u64 total_samples)
-{
- struct callchain_list *chain;
- size_t ret = 0;
-
- if (!self)
- return 0;
-
- ret += __callchain__fprintf_flat(fp, self->parent, total_samples);
-
-
- list_for_each_entry(chain, &self->val, list) {
- if (chain->ip >= PERF_CONTEXT_MAX)
- continue;
- if (chain->ms.sym)
- ret += fprintf(fp, " %s\n", chain->ms.sym->name);
- else
- ret += fprintf(fp, " %p\n",
- (void *)(long)chain->ip);
- }
-
- return ret;
-}
-
-static size_t callchain__fprintf_flat(FILE *fp, struct rb_root *self,
- u64 total_samples)
-{
- size_t ret = 0;
- u32 entries_printed = 0;
- struct rb_node *rb_node;
- struct callchain_node *chain;
-
- rb_node = rb_first(self);
- while (rb_node) {
- double percent;
-
- chain = rb_entry(rb_node, struct callchain_node, rb_node);
- percent = chain->hit * 100.0 / total_samples;
-
- ret = percent_color_fprintf(fp, " %6.2f%%\n", percent);
- ret += __callchain__fprintf_flat(fp, chain, total_samples);
- ret += fprintf(fp, "\n");
- if (++entries_printed == callchain_param.print_limit)
- break;
-
- rb_node = rb_next(rb_node);
- }
-
- return ret;
-}
-
-static size_t hist_entry_callchain__fprintf(struct hist_entry *he,
- u64 total_samples, int left_margin,
- FILE *fp)
-{
- switch (callchain_param.mode) {
- case CHAIN_GRAPH_REL:
- return callchain__fprintf_graph(fp, &he->sorted_chain, he->period,
- left_margin);
- break;
- case CHAIN_GRAPH_ABS:
- return callchain__fprintf_graph(fp, &he->sorted_chain, total_samples,
- left_margin);
- break;
- case CHAIN_FLAT:
- return callchain__fprintf_flat(fp, &he->sorted_chain, total_samples);
- break;
- case CHAIN_NONE:
- break;
- default:
- pr_err("Bad callchain mode\n");
- }
-
- return 0;
-}
-
-void hists__output_recalc_col_len(struct hists *hists, int max_rows)
-{
- struct rb_node *next = rb_first(&hists->entries);
- struct hist_entry *n;
- int row = 0;
-
- hists__reset_col_len(hists);
-
- while (next && row++ < max_rows) {
- n = rb_entry(next, struct hist_entry, rb_node);
- if (!n->filtered)
- hists__calc_col_len(hists, n);
- next = rb_next(&n->rb_node);
- }
-}
-
-static int hist_entry__pcnt_snprintf(struct hist_entry *he, char *s,
- size_t size, struct hists *pair_hists,
- bool show_displacement, long displacement,
- bool color, u64 total_period)
-{
- u64 period, total, period_sys, period_us, period_guest_sys, period_guest_us;
- u64 nr_events;
- const char *sep = symbol_conf.field_sep;
- int ret;
-
- if (symbol_conf.exclude_other && !he->parent)
- return 0;
-
- if (pair_hists) {
- period = he->pair ? he->pair->period : 0;
- nr_events = he->pair ? he->pair->nr_events : 0;
- total = pair_hists->stats.total_period;
- period_sys = he->pair ? he->pair->period_sys : 0;
- period_us = he->pair ? he->pair->period_us : 0;
- period_guest_sys = he->pair ? he->pair->period_guest_sys : 0;
- period_guest_us = he->pair ? he->pair->period_guest_us : 0;
- } else {
- period = he->period;
- nr_events = he->nr_events;
- total = total_period;
- period_sys = he->period_sys;
- period_us = he->period_us;
- period_guest_sys = he->period_guest_sys;
- period_guest_us = he->period_guest_us;
- }
-
- if (total) {
- if (color)
- ret = percent_color_snprintf(s, size,
- sep ? "%.2f" : " %6.2f%%",
- (period * 100.0) / total);
- else
- ret = scnprintf(s, size, sep ? "%.2f" : " %6.2f%%",
- (period * 100.0) / total);
- if (symbol_conf.show_cpu_utilization) {
- ret += percent_color_snprintf(s + ret, size - ret,
- sep ? "%.2f" : " %6.2f%%",
- (period_sys * 100.0) / total);
- ret += percent_color_snprintf(s + ret, size - ret,
- sep ? "%.2f" : " %6.2f%%",
- (period_us * 100.0) / total);
- if (perf_guest) {
- ret += percent_color_snprintf(s + ret,
- size - ret,
- sep ? "%.2f" : " %6.2f%%",
- (period_guest_sys * 100.0) /
- total);
- ret += percent_color_snprintf(s + ret,
- size - ret,
- sep ? "%.2f" : " %6.2f%%",
- (period_guest_us * 100.0) /
- total);
- }
- }
- } else
- ret = scnprintf(s, size, sep ? "%" PRIu64 : "%12" PRIu64 " ", period);
-
- if (symbol_conf.show_nr_samples) {
- if (sep)
- ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, nr_events);
- else
- ret += scnprintf(s + ret, size - ret, "%11" PRIu64, nr_events);
- }
-
- if (symbol_conf.show_total_period) {
- if (sep)
- ret += scnprintf(s + ret, size - ret, "%c%" PRIu64, *sep, period);
- else
- ret += scnprintf(s + ret, size - ret, " %12" PRIu64, period);
- }
-
- if (pair_hists) {
- char bf[32];
- double old_percent = 0, new_percent = 0, diff;
-
- if (total > 0)
- old_percent = (period * 100.0) / total;
- if (total_period > 0)
- new_percent = (he->period * 100.0) / total_period;
-
- diff = new_percent - old_percent;
-
- if (fabs(diff) >= 0.01)
- scnprintf(bf, sizeof(bf), "%+4.2F%%", diff);
- else
- scnprintf(bf, sizeof(bf), " ");
-
- if (sep)
- ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
- else
- ret += scnprintf(s + ret, size - ret, "%11.11s", bf);
-
- if (show_displacement) {
- if (displacement)
- scnprintf(bf, sizeof(bf), "%+4ld", displacement);
- else
- scnprintf(bf, sizeof(bf), " ");
-
- if (sep)
- ret += scnprintf(s + ret, size - ret, "%c%s", *sep, bf);
- else
- ret += scnprintf(s + ret, size - ret, "%6.6s", bf);
- }
- }
-
- return ret;
-}
-
-int hist_entry__snprintf(struct hist_entry *he, char *s, size_t size,
- struct hists *hists)
-{
- const char *sep = symbol_conf.field_sep;
- struct sort_entry *se;
- int ret = 0;
-
- list_for_each_entry(se, &hist_entry__sort_list, list) {
- if (se->elide)
- continue;
-
- ret += scnprintf(s + ret, size - ret, "%s", sep ?: " ");
- ret += se->se_snprintf(he, s + ret, size - ret,
- hists__col_len(hists, se->se_width_idx));
- }
-
- return ret;
-}
-
-static int hist_entry__fprintf(struct hist_entry *he, size_t size,
- struct hists *hists, struct hists *pair_hists,
- bool show_displacement, long displacement,
- u64 total_period, FILE *fp)
-{
- char bf[512];
- int ret;
-
- if (size == 0 || size > sizeof(bf))
- size = sizeof(bf);
-
- ret = hist_entry__pcnt_snprintf(he, bf, size, pair_hists,
- show_displacement, displacement,
- true, total_period);
- hist_entry__snprintf(he, bf + ret, size - ret, hists);
- return fprintf(fp, "%s\n", bf);
-}
-
-static size_t hist_entry__fprintf_callchain(struct hist_entry *he,
- struct hists *hists,
- u64 total_period, FILE *fp)
-{
- int left_margin = 0;
-
- if (sort__first_dimension == SORT_COMM) {
- struct sort_entry *se = list_first_entry(&hist_entry__sort_list,
- typeof(*se), list);
- left_margin = hists__col_len(hists, se->se_width_idx);
- left_margin -= thread__comm_len(he->thread);
- }
-
- return hist_entry_callchain__fprintf(he, total_period, left_margin, fp);
-}
-
-size_t hists__fprintf(struct hists *hists, struct hists *pair,
- bool show_displacement, bool show_header, int max_rows,
- int max_cols, FILE *fp)
-{
- struct sort_entry *se;
- struct rb_node *nd;
- size_t ret = 0;
- u64 total_period;
- unsigned long position = 1;
- long displacement = 0;
- unsigned int width;
- const char *sep = symbol_conf.field_sep;
- const char *col_width = symbol_conf.col_width_list_str;
- int nr_rows = 0;
-
- init_rem_hits();
-
- if (!show_header)
- goto print_entries;
-
- fprintf(fp, "# %s", pair ? "Baseline" : "Overhead");
-
- if (symbol_conf.show_cpu_utilization) {
- if (sep) {
- ret += fprintf(fp, "%csys", *sep);
- ret += fprintf(fp, "%cus", *sep);
- if (perf_guest) {
- ret += fprintf(fp, "%cguest sys", *sep);
- ret += fprintf(fp, "%cguest us", *sep);
- }
- } else {
- ret += fprintf(fp, " sys ");
- ret += fprintf(fp, " us ");
- if (perf_guest) {
- ret += fprintf(fp, " guest sys ");
- ret += fprintf(fp, " guest us ");
- }
- }
- }
-
- if (symbol_conf.show_nr_samples) {
- if (sep)
- fprintf(fp, "%cSamples", *sep);
- else
- fputs(" Samples ", fp);
- }
-
- if (symbol_conf.show_total_period) {
- if (sep)
- ret += fprintf(fp, "%cPeriod", *sep);
- else
- ret += fprintf(fp, " Period ");
- }
-
- if (pair) {
- if (sep)
- ret += fprintf(fp, "%cDelta", *sep);
- else
- ret += fprintf(fp, " Delta ");
-
- if (show_displacement) {
- if (sep)
- ret += fprintf(fp, "%cDisplacement", *sep);
- else
- ret += fprintf(fp, " Displ");
- }
- }
-
- list_for_each_entry(se, &hist_entry__sort_list, list) {
- if (se->elide)
- continue;
- if (sep) {
- fprintf(fp, "%c%s", *sep, se->se_header);
- continue;
- }
- width = strlen(se->se_header);
- if (symbol_conf.col_width_list_str) {
- if (col_width) {
- hists__set_col_len(hists, se->se_width_idx,
- atoi(col_width));
- col_width = strchr(col_width, ',');
- if (col_width)
- ++col_width;
- }
- }
- if (!hists__new_col_len(hists, se->se_width_idx, width))
- width = hists__col_len(hists, se->se_width_idx);
- fprintf(fp, " %*s", width, se->se_header);
- }
-
- fprintf(fp, "\n");
- if (max_rows && ++nr_rows >= max_rows)
- goto out;
-
- if (sep)
- goto print_entries;
-
- fprintf(fp, "# ........");
- if (symbol_conf.show_cpu_utilization)
- fprintf(fp, " ....... .......");
- if (symbol_conf.show_nr_samples)
- fprintf(fp, " ..........");
- if (symbol_conf.show_total_period)
- fprintf(fp, " ............");
- if (pair) {
- fprintf(fp, " ..........");
- if (show_displacement)
- fprintf(fp, " .....");
- }
- list_for_each_entry(se, &hist_entry__sort_list, list) {
- unsigned int i;
-
- if (se->elide)
- continue;
-
- fprintf(fp, " ");
- width = hists__col_len(hists, se->se_width_idx);
- if (width == 0)
- width = strlen(se->se_header);
- for (i = 0; i < width; i++)
- fprintf(fp, ".");
- }
-
- fprintf(fp, "\n");
- if (max_rows && ++nr_rows >= max_rows)
- goto out;
-
- fprintf(fp, "#\n");
- if (max_rows && ++nr_rows >= max_rows)
- goto out;
-
-print_entries:
- total_period = hists->stats.total_period;
-
- for (nd = rb_first(&hists->entries); nd; nd = rb_next(nd)) {
- struct hist_entry *h = rb_entry(nd, struct hist_entry, rb_node);
-
- if (h->filtered)
- continue;
-
- if (show_displacement) {
- if (h->pair != NULL)
- displacement = ((long)h->pair->position -
- (long)position);
- else
- displacement = 0;
- ++position;
- }
- ret += hist_entry__fprintf(h, max_cols, hists, pair, show_displacement,
- displacement, total_period, fp);
-
- if (symbol_conf.use_callchain)
- ret += hist_entry__fprintf_callchain(h, hists, total_period, fp);
- if (max_rows && ++nr_rows >= max_rows)
- goto out;
-
- if (h->ms.map == NULL && verbose > 1) {
- __map_groups__fprintf_maps(&h->thread->mg,
- MAP__FUNCTION, verbose, fp);
- fprintf(fp, "%.10s end\n", graph_dotted_line);
- }
- }
-out:
- free(rem_sq_bracket);
-
- return ret;
-}
-
-/*
- * See hists__fprintf to match the column widths
- */
-unsigned int hists__sort_list_width(struct hists *hists)
-{
- struct sort_entry *se;
- int ret = 9; /* total % */
-
- if (symbol_conf.show_cpu_utilization) {
- ret += 7; /* count_sys % */
- ret += 6; /* count_us % */
- if (perf_guest) {
- ret += 13; /* count_guest_sys % */
- ret += 12; /* count_guest_us % */
- }
- }
-
- if (symbol_conf.show_nr_samples)
- ret += 11;
-
- if (symbol_conf.show_total_period)
- ret += 13;
-
- list_for_each_entry(se, &hist_entry__sort_list, list)
- if (!se->elide)
- ret += 2 + hists__col_len(hists, se->se_width_idx);
-
- if (verbose) /* Addr + origin */
- ret += 3 + BITS_PER_LONG / 4;
-
- return ret;
-}
-
static void hists__remove_entry_filter(struct hists *hists, struct hist_entry *h,
enum hist_filter filter)
{
@@ -1342,25 +695,3 @@ void hists__inc_nr_events(struct hists *hists, u32 type)
++hists->stats.nr_events[0];
++hists->stats.nr_events[type];
}
-
-size_t hists__fprintf_nr_events(struct hists *hists, FILE *fp)
-{
- int i;
- size_t ret = 0;
-
- for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
- const char *name;
-
- if (hists->stats.nr_events[i] == 0)
- continue;
-
- name = perf_event__name(i);
- if (!strcmp(name, "UNKNOWN"))
- continue;
-
- ret += fprintf(fp, "%16s events: %10d\n", name,
- hists->stats.nr_events[i]);
- }
-
- return ret;
-}
diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h
index 0b096c27a419..f011ad4756e8 100644
--- a/tools/perf/util/hist.h
+++ b/tools/perf/util/hist.h
@@ -75,8 +75,8 @@ struct hist_entry *__hists__add_entry(struct hists *self,
struct symbol *parent, u64 period);
int64_t hist_entry__cmp(struct hist_entry *left, struct hist_entry *right);
int64_t hist_entry__collapse(struct hist_entry *left, struct hist_entry *right);
-int hist_entry__snprintf(struct hist_entry *self, char *bf, size_t size,
- struct hists *hists);
+int hist_entry__sort_snprintf(struct hist_entry *self, char *bf, size_t size,
+ struct hists *hists);
void hist_entry__free(struct hist_entry *);
struct hist_entry *__hists__add_branch_entry(struct hists *self,
@@ -112,25 +112,66 @@ void hists__filter_by_symbol(struct hists *hists);
u16 hists__col_len(struct hists *self, enum hist_column col);
void hists__set_col_len(struct hists *self, enum hist_column col, u16 len);
bool hists__new_col_len(struct hists *self, enum hist_column col, u16 len);
+void hists__reset_col_len(struct hists *hists);
+void hists__calc_col_len(struct hists *hists, struct hist_entry *he);
+
+struct perf_hpp {
+ char *buf;
+ size_t size;
+ u64 total_period;
+ const char *sep;
+ long displacement;
+ void *ptr;
+};
+
+struct perf_hpp_fmt {
+ bool cond;
+ int (*header)(struct perf_hpp *hpp);
+ int (*width)(struct perf_hpp *hpp);
+ int (*color)(struct perf_hpp *hpp, struct hist_entry *he);
+ int (*entry)(struct perf_hpp *hpp, struct hist_entry *he);
+};
+
+extern struct perf_hpp_fmt perf_hpp__format[];
+
+enum {
+ PERF_HPP__OVERHEAD,
+ PERF_HPP__OVERHEAD_SYS,
+ PERF_HPP__OVERHEAD_US,
+ PERF_HPP__OVERHEAD_GUEST_SYS,
+ PERF_HPP__OVERHEAD_GUEST_US,
+ PERF_HPP__SAMPLES,
+ PERF_HPP__PERIOD,
+ PERF_HPP__DELTA,
+ PERF_HPP__DISPL,
+
+ PERF_HPP__MAX_INDEX
+};
+
+void perf_hpp__init(bool need_pair, bool show_displacement);
+int hist_entry__period_snprintf(struct perf_hpp *hpp, struct hist_entry *he,
+ bool color);
struct perf_evlist;
#ifdef NO_NEWT_SUPPORT
static inline
-int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __used,
- const char *help __used,
- void(*timer)(void *arg) __used,
- void *arg __used,
- int refresh __used)
+int perf_evlist__tui_browse_hists(struct perf_evlist *evlist __maybe_unused,
+ const char *help __maybe_unused,
+ void(*timer)(void *arg) __maybe_unused,
+ void *arg __maybe_unused,
+ int refresh __maybe_unused)
{
return 0;
}
-static inline int hist_entry__tui_annotate(struct hist_entry *self __used,
- int evidx __used,
- void(*timer)(void *arg) __used,
- void *arg __used,
- int delay_secs __used)
+static inline int hist_entry__tui_annotate(struct hist_entry *self
+ __maybe_unused,
+ int evidx __maybe_unused,
+ void(*timer)(void *arg)
+ __maybe_unused,
+ void *arg __maybe_unused,
+ int delay_secs __maybe_unused)
{
return 0;
}
@@ -148,11 +189,11 @@ int perf_evlist__tui_browse_hists(struct perf_evlist *evlist, const char *help,
#ifdef NO_GTK2_SUPPORT
static inline
-int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __used,
- const char *help __used,
- void(*timer)(void *arg) __used,
- void *arg __used,
- int refresh __used)
+int perf_evlist__gtk_browse_hists(struct perf_evlist *evlist __maybe_unused,
+ const char *help __maybe_unused,
+ void(*timer)(void *arg) __maybe_unused,
+ void *arg __maybe_unused,
+ int refresh __maybe_unused)
{
return 0;
}
diff --git a/tools/perf/util/include/linux/bitops.h b/tools/perf/util/include/linux/bitops.h
index 587a230d2075..a55d8cf083c9 100644
--- a/tools/perf/util/include/linux/bitops.h
+++ b/tools/perf/util/include/linux/bitops.h
@@ -5,6 +5,10 @@
#include <linux/compiler.h>
#include <asm/hweight.h>
+#ifndef __WORDSIZE
+#define __WORDSIZE (__SIZEOF_LONG__ * 8)
+#endif
+
#define BITS_PER_LONG __WORDSIZE
#define BITS_PER_BYTE 8
#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h
index 547628e97f3d..96b919dae11c 100644
--- a/tools/perf/util/include/linux/compiler.h
+++ b/tools/perf/util/include/linux/compiler.h
@@ -9,6 +9,13 @@
#define __attribute_const__
#endif
-#define __used __attribute__((__unused__))
+#ifndef __maybe_unused
+#define __maybe_unused __attribute__((unused))
+#endif
+#define __packed __attribute__((__packed__))
+
+#ifndef __force
+#define __force
+#endif
#endif
diff --git a/tools/perf/util/include/linux/kernel.h b/tools/perf/util/include/linux/kernel.h
index b6842c1d02a8..d8c927c868ee 100644
--- a/tools/perf/util/include/linux/kernel.h
+++ b/tools/perf/util/include/linux/kernel.h
@@ -8,8 +8,8 @@
#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
-#define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1)
-#define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask))
+#define PERF_ALIGN(x, a) __PERF_ALIGN_MASK(x, (typeof(x))(a)-1)
+#define __PERF_ALIGN_MASK(x, mask) (((x)+(mask))&~(mask))
#ifndef offsetof
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
@@ -46,9 +46,22 @@
_min1 < _min2 ? _min1 : _min2; })
#endif
+#ifndef roundup
+#define roundup(x, y) ( \
+{ \
+ const typeof(y) __y = y; \
+ (((x) + (__y - 1)) / __y) * __y; \
+} \
+)
+#endif
+
#ifndef BUG_ON
+#ifdef NDEBUG
+#define BUG_ON(cond) do { if (cond) {} } while (0)
+#else
#define BUG_ON(cond) assert(!(cond))
#endif
+#endif
/*
* Both need more care to handle endianness
diff --git a/tools/perf/util/include/linux/magic.h b/tools/perf/util/include/linux/magic.h
new file mode 100644
index 000000000000..58b64ed4da12
--- /dev/null
+++ b/tools/perf/util/include/linux/magic.h
@@ -0,0 +1,12 @@
+#ifndef _PERF_LINUX_MAGIC_H_
+#define _PERF_LINUX_MAGIC_H_
+
+#ifndef DEBUGFS_MAGIC
+#define DEBUGFS_MAGIC 0x64626720
+#endif
+
+#ifndef SYSFS_MAGIC
+#define SYSFS_MAGIC 0x62656572
+#endif
+
+#endif
diff --git a/tools/perf/util/include/linux/rbtree.h b/tools/perf/util/include/linux/rbtree.h
index 7a243a143037..2a030c5af3aa 100644
--- a/tools/perf/util/include/linux/rbtree.h
+++ b/tools/perf/util/include/linux/rbtree.h
@@ -1 +1,2 @@
+#include <stdbool.h>
#include "../../../../include/linux/rbtree.h"
diff --git a/tools/perf/util/include/linux/string.h b/tools/perf/util/include/linux/string.h
index 3b2f5900276f..6f19c548ecc0 100644
--- a/tools/perf/util/include/linux/string.h
+++ b/tools/perf/util/include/linux/string.h
@@ -1 +1,3 @@
#include <string.h>
+
+void *memdup(const void *src, size_t len);
diff --git a/tools/perf/util/include/linux/types.h b/tools/perf/util/include/linux/types.h
index 12de3b8112f9..eb464786c084 100644
--- a/tools/perf/util/include/linux/types.h
+++ b/tools/perf/util/include/linux/types.h
@@ -3,6 +3,14 @@
#include <asm/types.h>
+#ifndef __bitwise
+#define __bitwise
+#endif
+
+#ifndef __le32
+typedef __u32 __bitwise __le32;
+#endif
+
#define DECLARE_BITMAP(name,bits) \
unsigned long name[BITS_TO_LONGS(bits)]
diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c
index fd530dced9cb..9d0740024ba8 100644
--- a/tools/perf/util/intlist.c
+++ b/tools/perf/util/intlist.c
@@ -11,7 +11,7 @@
#include "intlist.h"
-static struct rb_node *intlist__node_new(struct rblist *rblist __used,
+static struct rb_node *intlist__node_new(struct rblist *rblist __maybe_unused,
const void *entry)
{
int i = (int)((long)entry);
@@ -31,7 +31,7 @@ static void int_node__delete(struct int_node *ilist)
free(ilist);
}
-static void intlist__node_delete(struct rblist *rblist __used,
+static void intlist__node_delete(struct rblist *rblist __maybe_unused,
struct rb_node *rb_node)
{
struct int_node *node = container_of(rb_node, struct int_node, rb_node);
@@ -52,9 +52,9 @@ int intlist__add(struct intlist *ilist, int i)
return rblist__add_node(&ilist->rblist, (void *)((long)i));
}
-void intlist__remove(struct intlist *ilist __used, struct int_node *node)
+void intlist__remove(struct intlist *ilist, struct int_node *node)
{
- int_node__delete(node);
+ rblist__remove_node(&ilist->rblist, &node->rb_node);
}
struct int_node *intlist__find(struct intlist *ilist, int i)
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index cc33486ad9e2..ead5316b3f89 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -9,6 +9,7 @@
#include "map.h"
#include "thread.h"
#include "strlist.h"
+#include "vdso.h"
const char *map_type__name[MAP__NR_TYPES] = {
[MAP__FUNCTION] = "Functions",
@@ -23,7 +24,6 @@ static inline int is_anon_memory(const char *filename)
static inline int is_no_dso_memory(const char *filename)
{
return !strcmp(filename, "[stack]") ||
- !strcmp(filename, "[vdso]") ||
!strcmp(filename, "[heap]");
}
@@ -52,9 +52,10 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
if (self != NULL) {
char newfilename[PATH_MAX];
struct dso *dso;
- int anon, no_dso;
+ int anon, no_dso, vdso;
anon = is_anon_memory(filename);
+ vdso = is_vdso_map(filename);
no_dso = is_no_dso_memory(filename);
if (anon) {
@@ -62,7 +63,12 @@ struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
filename = newfilename;
}
- dso = __dsos__findnew(dsos__list, filename);
+ if (vdso) {
+ pgoff = 0;
+ dso = vdso__dso_findnew(dsos__list);
+ } else
+ dso = __dsos__findnew(dsos__list, filename);
+
if (dso == NULL)
goto out_delete;
@@ -86,6 +92,25 @@ out_delete:
return NULL;
}
+/*
+ * Constructor variant for modules (where we know from /proc/modules where
+ * they are loaded) and for vmlinux, where only after we load all the
+ * symbols we'll know where it starts and ends.
+ */
+struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
+{
+ struct map *map = calloc(1, (sizeof(*map) +
+ (dso->kernel ? sizeof(struct kmap) : 0)));
+ if (map != NULL) {
+ /*
+ * ->end will be filled after we load all the symbols
+ */
+ map__init(map, type, start, 0, 0, dso);
+ }
+
+ return map;
+}
+
void map__delete(struct map *self)
{
free(self);
@@ -137,6 +162,7 @@ int map__load(struct map *self, symbol_filter_t filter)
pr_warning(", continuing without symbols\n");
return -1;
} else if (nr == 0) {
+#ifndef NO_LIBELF_SUPPORT
const size_t len = strlen(name);
const size_t real_len = len - sizeof(DSO__DELETED);
@@ -149,7 +175,7 @@ int map__load(struct map *self, symbol_filter_t filter)
pr_warning("no symbols found in %s, maybe install "
"a debug package?\n", name);
}
-
+#endif
return -1;
}
/*
@@ -217,15 +243,14 @@ size_t map__fprintf(struct map *self, FILE *fp)
size_t map__fprintf_dsoname(struct map *map, FILE *fp)
{
- const char *dsoname;
+ const char *dsoname = "[unknown]";
if (map && map->dso && (map->dso->name || map->dso->long_name)) {
if (symbol_conf.show_kernel_path && map->dso->long_name)
dsoname = map->dso->long_name;
else if (map->dso->name)
dsoname = map->dso->name;
- } else
- dsoname = "[unknown]";
+ }
return fprintf(fp, "%s", dsoname);
}
@@ -242,14 +267,6 @@ u64 map__rip_2objdump(struct map *map, u64 rip)
return addr;
}
-u64 map__objdump_2ip(struct map *map, u64 addr)
-{
- u64 ip = map->dso->adjust_symbols ?
- addr :
- map->unmap_ip(map, addr); /* RIP -> IP */
- return ip;
-}
-
void map_groups__init(struct map_groups *mg)
{
int i;
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index 03a1e9b08b21..d2250fc97e25 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -96,7 +96,7 @@ static inline u64 map__unmap_ip(struct map *map, u64 ip)
return ip + map->start - map->pgoff;
}
-static inline u64 identity__map_ip(struct map *map __used, u64 ip)
+static inline u64 identity__map_ip(struct map *map __maybe_unused, u64 ip)
{
return ip;
}
@@ -104,7 +104,6 @@ static inline u64 identity__map_ip(struct map *map __used, u64 ip)
/* rip/ip <-> addr suitable for passing to `objdump --start-address=` */
u64 map__rip_2objdump(struct map *map, u64 rip);
-u64 map__objdump_2ip(struct map *map, u64 addr);
struct symbol;
@@ -115,6 +114,7 @@ void map__init(struct map *self, enum map_type type,
struct map *map__new(struct list_head *dsos__list, u64 start, u64 len,
u64 pgoff, u32 pid, char *filename,
enum map_type type);
+struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
void map__delete(struct map *self);
struct map *map__clone(struct map *self);
int map__overlap(struct map *l, struct map *r);
@@ -157,9 +157,12 @@ int machine__init(struct machine *self, const char *root_dir, pid_t pid);
void machine__exit(struct machine *self);
void machine__delete(struct machine *self);
+struct perf_evsel;
+struct perf_sample;
int machine__resolve_callchain(struct machine *machine,
+ struct perf_evsel *evsel,
struct thread *thread,
- struct ip_callchain *chain,
+ struct perf_sample *sample,
struct symbol **parent);
int maps__set_kallsyms_ref_reloc_sym(struct map **maps, const char *symbol_name,
u64 addr);
diff --git a/tools/perf/util/parse-events-test.c b/tools/perf/util/parse-events-test.c
index 127d648cc548..28c18d1d52c3 100644
--- a/tools/perf/util/parse-events-test.c
+++ b/tools/perf/util/parse-events-test.c
@@ -18,8 +18,7 @@ do { \
static int test__checkevent_tracepoint(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
@@ -48,8 +47,7 @@ static int test__checkevent_tracepoint_multi(struct perf_evlist *evlist)
static int test__checkevent_raw(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
@@ -59,8 +57,7 @@ static int test__checkevent_raw(struct perf_evlist *evlist)
static int test__checkevent_numeric(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
@@ -70,8 +67,7 @@ static int test__checkevent_numeric(struct perf_evlist *evlist)
static int test__checkevent_symbolic_name(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
@@ -82,8 +78,7 @@ static int test__checkevent_symbolic_name(struct perf_evlist *evlist)
static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
@@ -100,8 +95,7 @@ static int test__checkevent_symbolic_name_config(struct perf_evlist *evlist)
static int test__checkevent_symbolic_alias(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type);
@@ -112,8 +106,7 @@ static int test__checkevent_symbolic_alias(struct perf_evlist *evlist)
static int test__checkevent_genhw(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_HW_CACHE == evsel->attr.type);
@@ -123,8 +116,7 @@ static int test__checkevent_genhw(struct perf_evlist *evlist)
static int test__checkevent_breakpoint(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
@@ -138,8 +130,7 @@ static int test__checkevent_breakpoint(struct perf_evlist *evlist)
static int test__checkevent_breakpoint_x(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_BREAKPOINT == evsel->attr.type);
@@ -152,8 +143,7 @@ static int test__checkevent_breakpoint_x(struct perf_evlist *evlist)
static int test__checkevent_breakpoint_r(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type",
@@ -168,8 +158,7 @@ static int test__checkevent_breakpoint_r(struct perf_evlist *evlist)
static int test__checkevent_breakpoint_w(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type",
@@ -184,8 +173,7 @@ static int test__checkevent_breakpoint_w(struct perf_evlist *evlist)
static int test__checkevent_breakpoint_rw(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type",
@@ -200,8 +188,7 @@ static int test__checkevent_breakpoint_rw(struct perf_evlist *evlist)
static int test__checkevent_tracepoint_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
@@ -232,8 +219,7 @@ test__checkevent_tracepoint_multi_modifier(struct perf_evlist *evlist)
static int test__checkevent_raw_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
@@ -245,8 +231,7 @@ static int test__checkevent_raw_modifier(struct perf_evlist *evlist)
static int test__checkevent_numeric_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
@@ -258,8 +243,7 @@ static int test__checkevent_numeric_modifier(struct perf_evlist *evlist)
static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
@@ -271,8 +255,7 @@ static int test__checkevent_symbolic_name_modifier(struct perf_evlist *evlist)
static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
@@ -282,8 +265,7 @@ static int test__checkevent_exclude_host_modifier(struct perf_evlist *evlist)
static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
@@ -293,8 +275,7 @@ static int test__checkevent_exclude_guest_modifier(struct perf_evlist *evlist)
static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
@@ -306,8 +287,7 @@ static int test__checkevent_symbolic_alias_modifier(struct perf_evlist *evlist)
static int test__checkevent_genhw_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
@@ -319,75 +299,71 @@ static int test__checkevent_genhw_modifier(struct perf_evlist *evlist)
static int test__checkevent_breakpoint_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
+
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
- !strcmp(perf_evsel__name(evsel), "mem:0x0:rw:u"));
+ !strcmp(perf_evsel__name(evsel), "mem:0:u"));
return test__checkevent_breakpoint(evlist);
}
static int test__checkevent_breakpoint_x_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
- !strcmp(perf_evsel__name(evsel), "mem:0x0:x:k"));
+ !strcmp(perf_evsel__name(evsel), "mem:0:x:k"));
return test__checkevent_breakpoint_x(evlist);
}
static int test__checkevent_breakpoint_r_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
- !strcmp(perf_evsel__name(evsel), "mem:0x0:r:hp"));
+ !strcmp(perf_evsel__name(evsel), "mem:0:r:hp"));
return test__checkevent_breakpoint_r(evlist);
}
static int test__checkevent_breakpoint_w_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
- !strcmp(perf_evsel__name(evsel), "mem:0x0:w:up"));
+ !strcmp(perf_evsel__name(evsel), "mem:0:w:up"));
return test__checkevent_breakpoint_w(evlist);
}
static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip);
TEST_ASSERT_VAL("wrong name",
- !strcmp(perf_evsel__name(evsel), "mem:0x0:rw:kp"));
+ !strcmp(perf_evsel__name(evsel), "mem:0:rw:kp"));
return test__checkevent_breakpoint_rw(evlist);
}
@@ -395,8 +371,7 @@ static int test__checkevent_breakpoint_rw_modifier(struct perf_evlist *evlist)
static int test__checkevent_pmu(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel = list_entry(evlist->entries.next,
- struct perf_evsel, node);
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
@@ -410,12 +385,11 @@ static int test__checkevent_pmu(struct perf_evlist *evlist)
static int test__checkevent_list(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
/* r1 */
- evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
TEST_ASSERT_VAL("wrong config1", 0 == evsel->attr.config1);
@@ -426,7 +400,7 @@ static int test__checkevent_list(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
/* syscalls:sys_enter_open:k */
- evsel = list_entry(evsel->node.next, struct perf_evsel, node);
+ evsel = perf_evsel__next(evsel);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
TEST_ASSERT_VAL("wrong sample_type",
PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
@@ -437,7 +411,7 @@ static int test__checkevent_list(struct perf_evlist *evlist)
TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
/* 1:1:hp */
- evsel = list_entry(evsel->node.next, struct perf_evsel, node);
+ evsel = perf_evsel__next(evsel);
TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
@@ -450,22 +424,21 @@ static int test__checkevent_list(struct perf_evlist *evlist)
static int test__checkevent_pmu_name(struct perf_evlist *evlist)
{
- struct perf_evsel *evsel;
+ struct perf_evsel *evsel = perf_evlist__first(evlist);
/* cpu/config=1,name=krava/u */
- evsel = list_entry(evlist->entries.next, struct perf_evsel, node);
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 1 == evsel->attr.config);
TEST_ASSERT_VAL("wrong name", !strcmp(perf_evsel__name(evsel), "krava"));
/* cpu/config=2/u" */
- evsel = list_entry(evsel->node.next, struct perf_evsel, node);
+ evsel = perf_evsel__next(evsel);
TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_RAW == evsel->attr.type);
TEST_ASSERT_VAL("wrong config", 2 == evsel->attr.config);
TEST_ASSERT_VAL("wrong name",
- !strcmp(perf_evsel__name(evsel), "raw 0x2:u"));
+ !strcmp(perf_evsel__name(evsel), "cpu/config=2/u"));
return 0;
}
@@ -513,6 +486,280 @@ static int test__checkterms_simple(struct list_head *terms)
return 0;
}
+static int test__group1(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel, *leader;
+
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+
+ /* instructions:k */
+ evsel = leader = perf_evlist__first(evlist);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
+
+ /* cycles:upp */
+ evsel = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+
+ return 0;
+}
+
+static int test__group2(struct perf_evlist *evlist)
+{
+ struct perf_evsel *evsel, *leader;
+
+ TEST_ASSERT_VAL("wrong number of entries", 3 == evlist->nr_entries);
+
+ /* faults + :ku modifier */
+ evsel = leader = perf_evlist__first(evlist);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_SOFTWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_SW_PAGE_FAULTS == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
+
+ /* cache-references + :u modifier */
+ evsel = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_CACHE_REFERENCES == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+
+ /* cycles:k */
+ evsel = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
+
+ return 0;
+}
+
+static int test__group3(struct perf_evlist *evlist __maybe_unused)
+{
+ struct perf_evsel *evsel, *leader;
+
+ TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries);
+
+ /* group1 syscalls:sys_enter_open:H */
+ evsel = leader = perf_evlist__first(evlist);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong sample_type",
+ PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
+ TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
+ TEST_ASSERT_VAL("wrong group name",
+ !strcmp(leader->group_name, "group1"));
+
+ /* group1 cycles:kppp */
+ evsel = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 3);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+ TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
+
+ /* group2 cycles + G modifier */
+ evsel = leader = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
+ TEST_ASSERT_VAL("wrong group name",
+ !strcmp(leader->group_name, "group2"));
+
+ /* group2 1:3 + G modifier */
+ evsel = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", 1 == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config", 3 == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+
+ /* instructions:u */
+ evsel = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
+
+ return 0;
+}
+
+static int test__group4(struct perf_evlist *evlist __maybe_unused)
+{
+ struct perf_evsel *evsel, *leader;
+
+ TEST_ASSERT_VAL("wrong number of entries", 2 == evlist->nr_entries);
+
+ /* cycles:u + p */
+ evsel = leader = perf_evlist__first(evlist);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 1);
+ TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
+
+ /* instructions:kp + p */
+ evsel = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", evsel->attr.precise_ip == 2);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+
+ return 0;
+}
+
+static int test__group5(struct perf_evlist *evlist __maybe_unused)
+{
+ struct perf_evsel *evsel, *leader;
+
+ TEST_ASSERT_VAL("wrong number of entries", 5 == evlist->nr_entries);
+
+ /* cycles + G */
+ evsel = leader = perf_evlist__first(evlist);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
+
+ /* instructions + G */
+ evsel = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+
+ /* cycles:G */
+ evsel = leader = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong group name", !evsel->group_name);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
+
+ /* instructions:G */
+ evsel = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_INSTRUCTIONS == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", !evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == leader);
+
+ /* cycles */
+ evsel = perf_evsel__next(evsel);
+ TEST_ASSERT_VAL("wrong type", PERF_TYPE_HARDWARE == evsel->attr.type);
+ TEST_ASSERT_VAL("wrong config",
+ PERF_COUNT_HW_CPU_CYCLES == evsel->attr.config);
+ TEST_ASSERT_VAL("wrong exclude_user", !evsel->attr.exclude_user);
+ TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
+ TEST_ASSERT_VAL("wrong exclude_hv", !evsel->attr.exclude_hv);
+ TEST_ASSERT_VAL("wrong exclude guest", evsel->attr.exclude_guest);
+ TEST_ASSERT_VAL("wrong exclude host", !evsel->attr.exclude_host);
+ TEST_ASSERT_VAL("wrong precise_ip", !evsel->attr.precise_ip);
+ TEST_ASSERT_VAL("wrong leader", evsel->leader == NULL);
+
+ return 0;
+}
+
struct test__event_st {
const char *name;
__u32 type;
@@ -632,6 +879,26 @@ static struct test__event_st test__events[] = {
.name = "mem:0:rw:kp",
.check = test__checkevent_breakpoint_rw_modifier,
},
+ [28] = {
+ .name = "{instructions:k,cycles:upp}",
+ .check = test__group1,
+ },
+ [29] = {
+ .name = "{faults:k,cache-references}:u,cycles:k",
+ .check = test__group2,
+ },
+ [30] = {
+ .name = "group1{syscalls:sys_enter_open:H,cycles:kppp},group2{cycles,1:3}:G,instructions:u",
+ .check = test__group3,
+ },
+ [31] = {
+ .name = "{cycles:u,instructions:kp}:p",
+ .check = test__group4,
+ },
+ [32] = {
+ .name = "{cycles,instructions}:G,{cycles:G,instructions:G},cycles",
+ .check = test__group5,
+ },
};
static struct test__event_st test__events_pmu[] = {
@@ -658,9 +925,6 @@ static struct test__term test__terms[] = {
},
};
-#define TEST__TERMS_CNT (sizeof(test__terms) / \
- sizeof(struct test__term))
-
static int test_event(struct test__event_st *e)
{
struct perf_evlist *evlist;
@@ -685,19 +949,19 @@ static int test_event(struct test__event_st *e)
static int test_events(struct test__event_st *events, unsigned cnt)
{
- int ret = 0;
+ int ret1, ret2 = 0;
unsigned i;
for (i = 0; i < cnt; i++) {
struct test__event_st *e = &events[i];
pr_debug("running test %d '%s'\n", i, e->name);
- ret = test_event(e);
- if (ret)
- break;
+ ret1 = test_event(e);
+ if (ret1)
+ ret2 = ret1;
}
- return ret;
+ return ret2;
}
static int test_term(struct test__term *t)
@@ -752,19 +1016,19 @@ static int test_pmu(void)
ret = stat(path, &st);
if (ret)
- pr_debug("ommiting PMU cpu tests\n");
+ pr_debug("omitting PMU cpu tests\n");
return !ret;
}
int parse_events__test(void)
{
- int ret;
+ int ret1, ret2 = 0;
#define TEST_EVENTS(tests) \
do { \
- ret = test_events(tests, ARRAY_SIZE(tests)); \
- if (ret) \
- return ret; \
+ ret1 = test_events(tests, ARRAY_SIZE(tests)); \
+ if (!ret2) \
+ ret2 = ret1; \
} while (0)
TEST_EVENTS(test__events);
@@ -772,5 +1036,9 @@ do { \
if (test_pmu())
TEST_EVENTS(test__events_pmu);
- return test_terms(test__terms, ARRAY_SIZE(test__terms));
+ ret1 = test_terms(test__terms, ARRAY_SIZE(test__terms));
+ if (!ret2)
+ ret2 = ret1;
+
+ return ret2;
}
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 74a5af4d33ec..aed38e4b9dfa 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -239,8 +239,11 @@ const char *event_type(int type)
return "unknown";
}
-static int add_event(struct list_head **_list, int *idx,
- struct perf_event_attr *attr, char *name)
+
+
+static int __add_event(struct list_head **_list, int *idx,
+ struct perf_event_attr *attr,
+ char *name, struct cpu_map *cpus)
{
struct perf_evsel *evsel;
struct list_head *list = *_list;
@@ -260,6 +263,7 @@ static int add_event(struct list_head **_list, int *idx,
return -ENOMEM;
}
+ evsel->cpus = cpus;
if (name)
evsel->name = strdup(name);
list_add_tail(&evsel->node, list);
@@ -267,6 +271,12 @@ static int add_event(struct list_head **_list, int *idx,
return 0;
}
+static int add_event(struct list_head **_list, int *idx,
+ struct perf_event_attr *attr, char *name)
+{
+ return __add_event(_list, idx, attr, name, NULL);
+}
+
static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
{
int i, j;
@@ -308,7 +318,7 @@ int parse_events_add_cache(struct list_head **list, int *idx,
for (i = 0; (i < 2) && (op_result[i]); i++) {
char *str = op_result[i];
- snprintf(name + n, MAX_NAME_LEN - n, "-%s\n", str);
+ n += snprintf(name + n, MAX_NAME_LEN - n, "-%s", str);
if (cache_op == -1) {
cache_op = parse_aliases(str, perf_evsel__hw_cache_op,
@@ -346,42 +356,28 @@ int parse_events_add_cache(struct list_head **list, int *idx,
return add_event(list, idx, &attr, name);
}
-static int add_tracepoint(struct list_head **list, int *idx,
+static int add_tracepoint(struct list_head **listp, int *idx,
char *sys_name, char *evt_name)
{
- struct perf_event_attr attr;
- char name[MAX_NAME_LEN];
- char evt_path[MAXPATHLEN];
- char id_buf[4];
- u64 id;
- int fd;
-
- snprintf(evt_path, MAXPATHLEN, "%s/%s/%s/id", tracing_events_path,
- sys_name, evt_name);
-
- fd = open(evt_path, O_RDONLY);
- if (fd < 0)
- return -1;
+ struct perf_evsel *evsel;
+ struct list_head *list = *listp;
- if (read(fd, id_buf, sizeof(id_buf)) < 0) {
- close(fd);
- return -1;
+ if (!list) {
+ list = malloc(sizeof(*list));
+ if (!list)
+ return -ENOMEM;
+ INIT_LIST_HEAD(list);
}
- close(fd);
- id = atoll(id_buf);
-
- memset(&attr, 0, sizeof(attr));
- attr.config = id;
- attr.type = PERF_TYPE_TRACEPOINT;
- attr.sample_type |= PERF_SAMPLE_RAW;
- attr.sample_type |= PERF_SAMPLE_TIME;
- attr.sample_type |= PERF_SAMPLE_CPU;
- attr.sample_type |= PERF_SAMPLE_PERIOD;
- attr.sample_period = 1;
+ evsel = perf_evsel__newtp(sys_name, evt_name, (*idx)++);
+ if (!evsel) {
+ free(list);
+ return -ENOMEM;
+ }
- snprintf(name, MAX_NAME_LEN, "%s:%s", sys_name, evt_name);
- return add_event(list, idx, &attr, name);
+ list_add_tail(&evsel->node, list);
+ *listp = list;
+ return 0;
}
static int add_tracepoint_multi(struct list_head **list, int *idx,
@@ -551,7 +547,7 @@ static int config_attr(struct perf_event_attr *attr,
}
int parse_events_add_numeric(struct list_head **list, int *idx,
- unsigned long type, unsigned long config,
+ u32 type, u64 config,
struct list_head *head_config)
{
struct perf_event_attr attr;
@@ -607,8 +603,23 @@ int parse_events_add_pmu(struct list_head **list, int *idx,
if (perf_pmu__config(pmu, &attr, head_config))
return -EINVAL;
- return add_event(list, idx, &attr,
- pmu_event_name(head_config));
+ return __add_event(list, idx, &attr, pmu_event_name(head_config),
+ pmu->cpus);
+}
+
+int parse_events__modifier_group(struct list_head *list,
+ char *event_mod)
+{
+ return parse_events__modifier_event(list, event_mod, true);
+}
+
+void parse_events__set_leader(char *name, struct list_head *list)
+{
+ struct perf_evsel *leader;
+
+ __perf_evlist__set_leader(list);
+ leader = list_entry(list->next, struct perf_evsel, node);
+ leader->group_name = name ? strdup(name) : NULL;
}
void parse_events_update_lists(struct list_head *list_event,
@@ -616,21 +627,45 @@ void parse_events_update_lists(struct list_head *list_event,
{
/*
* Called for single event definition. Update the
- * 'all event' list, and reinit the 'signle event'
+ * 'all event' list, and reinit the 'single event'
* list, for next event definition.
*/
list_splice_tail(list_event, list_all);
free(list_event);
}
-int parse_events_modifier(struct list_head *list, char *str)
+struct event_modifier {
+ int eu;
+ int ek;
+ int eh;
+ int eH;
+ int eG;
+ int precise;
+ int exclude_GH;
+};
+
+static int get_event_modifier(struct event_modifier *mod, char *str,
+ struct perf_evsel *evsel)
{
- struct perf_evsel *evsel;
- int exclude = 0, exclude_GH = 0;
- int eu = 0, ek = 0, eh = 0, eH = 0, eG = 0, precise = 0;
+ int eu = evsel ? evsel->attr.exclude_user : 0;
+ int ek = evsel ? evsel->attr.exclude_kernel : 0;
+ int eh = evsel ? evsel->attr.exclude_hv : 0;
+ int eH = evsel ? evsel->attr.exclude_host : 0;
+ int eG = evsel ? evsel->attr.exclude_guest : 0;
+ int precise = evsel ? evsel->attr.precise_ip : 0;
- if (str == NULL)
- return 0;
+ int exclude = eu | ek | eh;
+ int exclude_GH = evsel ? evsel->exclude_GH : 0;
+
+ /*
+ * We are here for group and 'GH' was not set as event
+ * modifier and whatever event/group modifier override
+ * default 'GH' setup.
+ */
+ if (evsel && !exclude_GH)
+ eH = eG = 0;
+
+ memset(mod, 0, sizeof(*mod));
while (*str) {
if (*str == 'u') {
@@ -674,13 +709,51 @@ int parse_events_modifier(struct list_head *list, char *str)
if (precise > 3)
return -EINVAL;
+ mod->eu = eu;
+ mod->ek = ek;
+ mod->eh = eh;
+ mod->eH = eH;
+ mod->eG = eG;
+ mod->precise = precise;
+ mod->exclude_GH = exclude_GH;
+ return 0;
+}
+
+int parse_events__modifier_event(struct list_head *list, char *str, bool add)
+{
+ struct perf_evsel *evsel;
+ struct event_modifier mod;
+
+ if (str == NULL)
+ return 0;
+
+ if (!add && get_event_modifier(&mod, str, NULL))
+ return -EINVAL;
+
list_for_each_entry(evsel, list, node) {
- evsel->attr.exclude_user = eu;
- evsel->attr.exclude_kernel = ek;
- evsel->attr.exclude_hv = eh;
- evsel->attr.precise_ip = precise;
- evsel->attr.exclude_host = eH;
- evsel->attr.exclude_guest = eG;
+
+ if (add && get_event_modifier(&mod, str, evsel))
+ return -EINVAL;
+
+ evsel->attr.exclude_user = mod.eu;
+ evsel->attr.exclude_kernel = mod.ek;
+ evsel->attr.exclude_hv = mod.eh;
+ evsel->attr.precise_ip = mod.precise;
+ evsel->attr.exclude_host = mod.eH;
+ evsel->attr.exclude_guest = mod.eG;
+ evsel->exclude_GH = mod.exclude_GH;
+ }
+
+ return 0;
+}
+
+int parse_events_name(struct list_head *list, char *name)
+{
+ struct perf_evsel *evsel;
+
+ list_for_each_entry(evsel, list, node) {
+ if (!evsel->name)
+ evsel->name = strdup(name);
}
return 0;
@@ -730,7 +803,8 @@ int parse_events_terms(struct list_head *terms, const char *str)
return ret;
}
-int parse_events(struct perf_evlist *evlist, const char *str, int unset __used)
+int parse_events(struct perf_evlist *evlist, const char *str,
+ int unset __maybe_unused)
{
struct parse_events_data__events data = {
.list = LIST_HEAD_INIT(data.list),
@@ -756,20 +830,20 @@ int parse_events(struct perf_evlist *evlist, const char *str, int unset __used)
}
int parse_events_option(const struct option *opt, const char *str,
- int unset __used)
+ int unset __maybe_unused)
{
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
return parse_events(evlist, str, unset);
}
int parse_filter(const struct option *opt, const char *str,
- int unset __used)
+ int unset __maybe_unused)
{
struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
struct perf_evsel *last = NULL;
if (evlist->nr_entries > 0)
- last = list_entry(evlist->entries.prev, struct perf_evsel, node);
+ last = perf_evlist__last(evlist);
if (last == NULL || last->attr.type != PERF_TYPE_TRACEPOINT) {
fprintf(stderr,
@@ -799,7 +873,8 @@ static const char * const event_type_descriptors[] = {
* Print the events from <debugfs_mount_point>/tracing/events
*/
-void print_tracepoint_events(const char *subsys_glob, const char *event_glob)
+void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
+ bool name_only)
{
DIR *sys_dir, *evt_dir;
struct dirent *sys_next, *evt_next, sys_dirent, evt_dirent;
@@ -829,6 +904,11 @@ void print_tracepoint_events(const char *subsys_glob, const char *event_glob)
!strglobmatch(evt_dirent.d_name, event_glob))
continue;
+ if (name_only) {
+ printf("%s:%s ", sys_dirent.d_name, evt_dirent.d_name);
+ continue;
+ }
+
snprintf(evt_path, MAXPATHLEN, "%s:%s",
sys_dirent.d_name, evt_dirent.d_name);
printf(" %-50s [%s]\n", evt_path,
@@ -906,7 +986,7 @@ void print_events_type(u8 type)
__print_events_type(type, event_symbols_hw, PERF_COUNT_HW_MAX);
}
-int print_hwcache_events(const char *event_glob)
+int print_hwcache_events(const char *event_glob, bool name_only)
{
unsigned int type, op, i, printed = 0;
char name[64];
@@ -923,8 +1003,11 @@ int print_hwcache_events(const char *event_glob)
if (event_glob != NULL && !strglobmatch(name, event_glob))
continue;
- printf(" %-50s [%s]\n", name,
- event_type_descriptors[PERF_TYPE_HW_CACHE]);
+ if (name_only)
+ printf("%s ", name);
+ else
+ printf(" %-50s [%s]\n", name,
+ event_type_descriptors[PERF_TYPE_HW_CACHE]);
++printed;
}
}
@@ -934,7 +1017,8 @@ int print_hwcache_events(const char *event_glob)
}
static void print_symbol_events(const char *event_glob, unsigned type,
- struct event_symbol *syms, unsigned max)
+ struct event_symbol *syms, unsigned max,
+ bool name_only)
{
unsigned i, printed = 0;
char name[MAX_NAME_LEN];
@@ -946,6 +1030,11 @@ static void print_symbol_events(const char *event_glob, unsigned type,
(syms->alias && strglobmatch(syms->alias, event_glob))))
continue;
+ if (name_only) {
+ printf("%s ", syms->symbol);
+ continue;
+ }
+
if (strlen(syms->alias))
snprintf(name, MAX_NAME_LEN, "%s OR %s", syms->symbol, syms->alias);
else
@@ -963,39 +1052,42 @@ static void print_symbol_events(const char *event_glob, unsigned type,
/*
* Print the help text for the event symbols:
*/
-void print_events(const char *event_glob)
+void print_events(const char *event_glob, bool name_only)
{
-
- printf("\n");
- printf("List of pre-defined events (to be used in -e):\n");
+ if (!name_only) {
+ printf("\n");
+ printf("List of pre-defined events (to be used in -e):\n");
+ }
print_symbol_events(event_glob, PERF_TYPE_HARDWARE,
- event_symbols_hw, PERF_COUNT_HW_MAX);
+ event_symbols_hw, PERF_COUNT_HW_MAX, name_only);
print_symbol_events(event_glob, PERF_TYPE_SOFTWARE,
- event_symbols_sw, PERF_COUNT_SW_MAX);
+ event_symbols_sw, PERF_COUNT_SW_MAX, name_only);
- print_hwcache_events(event_glob);
+ print_hwcache_events(event_glob, name_only);
if (event_glob != NULL)
return;
- printf("\n");
- printf(" %-50s [%s]\n",
- "rNNN",
- event_type_descriptors[PERF_TYPE_RAW]);
- printf(" %-50s [%s]\n",
- "cpu/t1=v1[,t2=v2,t3 ...]/modifier",
- event_type_descriptors[PERF_TYPE_RAW]);
- printf(" (see 'perf list --help' on how to encode it)\n");
- printf("\n");
-
- printf(" %-50s [%s]\n",
- "mem:<addr>[:access]",
+ if (!name_only) {
+ printf("\n");
+ printf(" %-50s [%s]\n",
+ "rNNN",
+ event_type_descriptors[PERF_TYPE_RAW]);
+ printf(" %-50s [%s]\n",
+ "cpu/t1=v1[,t2=v2,t3 ...]/modifier",
+ event_type_descriptors[PERF_TYPE_RAW]);
+ printf(" (see 'perf list --help' on how to encode it)\n");
+ printf("\n");
+
+ printf(" %-50s [%s]\n",
+ "mem:<addr>[:access]",
event_type_descriptors[PERF_TYPE_BREAKPOINT]);
- printf("\n");
+ printf("\n");
+ }
- print_tracepoint_events(NULL, NULL);
+ print_tracepoint_events(NULL, NULL, name_only);
}
int parse_events__is_hardcoded_term(struct parse_events__term *term)
@@ -1005,7 +1097,7 @@ int parse_events__is_hardcoded_term(struct parse_events__term *term)
static int new_term(struct parse_events__term **_term, int type_val,
int type_term, char *config,
- char *str, long num)
+ char *str, u64 num)
{
struct parse_events__term *term;
@@ -1034,7 +1126,7 @@ static int new_term(struct parse_events__term **_term, int type_val,
}
int parse_events__term_num(struct parse_events__term **term,
- int type_term, char *config, long num)
+ int type_term, char *config, u64 num)
{
return new_term(term, PARSE_EVENTS__TERM_TYPE_NUM, type_term,
config, NULL, num);
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h
index ee9c218a193c..c356e443448d 100644
--- a/tools/perf/util/parse-events.h
+++ b/tools/perf/util/parse-events.h
@@ -55,7 +55,7 @@ struct parse_events__term {
char *config;
union {
char *str;
- long num;
+ u64 num;
} val;
int type_val;
int type_term;
@@ -73,17 +73,19 @@ struct parse_events_data__terms {
int parse_events__is_hardcoded_term(struct parse_events__term *term);
int parse_events__term_num(struct parse_events__term **_term,
- int type_term, char *config, long num);
+ int type_term, char *config, u64 num);
int parse_events__term_str(struct parse_events__term **_term,
int type_term, char *config, char *str);
int parse_events__term_clone(struct parse_events__term **new,
struct parse_events__term *term);
void parse_events__free_terms(struct list_head *terms);
-int parse_events_modifier(struct list_head *list, char *str);
+int parse_events__modifier_event(struct list_head *list, char *str, bool add);
+int parse_events__modifier_group(struct list_head *list, char *event_mod);
+int parse_events_name(struct list_head *list, char *name);
int parse_events_add_tracepoint(struct list_head **list, int *idx,
char *sys, char *event);
int parse_events_add_numeric(struct list_head **list, int *idx,
- unsigned long type, unsigned long config,
+ u32 type, u64 config,
struct list_head *head_config);
int parse_events_add_cache(struct list_head **list, int *idx,
char *type, char *op_result1, char *op_result2);
@@ -91,15 +93,17 @@ int parse_events_add_breakpoint(struct list_head **list, int *idx,
void *ptr, char *type);
int parse_events_add_pmu(struct list_head **list, int *idx,
char *pmu , struct list_head *head_config);
+void parse_events__set_leader(char *name, struct list_head *list);
void parse_events_update_lists(struct list_head *list_event,
struct list_head *list_all);
void parse_events_error(void *data, void *scanner, char const *msg);
int parse_events__test(void);
-void print_events(const char *event_glob);
+void print_events(const char *event_glob, bool name_only);
void print_events_type(u8 type);
-void print_tracepoint_events(const char *subsys_glob, const char *event_glob);
-int print_hwcache_events(const char *event_glob);
+void print_tracepoint_events(const char *subsys_glob, const char *event_glob,
+ bool name_only);
+int print_hwcache_events(const char *event_glob, bool name_only);
extern int is_valid_tracepoint(const char *event_string);
extern int valid_debugfs_mount(const char *debugfs);
diff --git a/tools/perf/util/parse-events.l b/tools/perf/util/parse-events.l
index 384ca74c6b22..c87efc12579d 100644
--- a/tools/perf/util/parse-events.l
+++ b/tools/perf/util/parse-events.l
@@ -15,10 +15,10 @@ YYSTYPE *parse_events_get_lval(yyscan_t yyscanner);
static int __value(YYSTYPE *yylval, char *str, int base, int token)
{
- long num;
+ u64 num;
errno = 0;
- num = strtoul(str, NULL, base);
+ num = strtoull(str, NULL, base);
if (errno)
return PE_ERROR;
@@ -70,6 +70,12 @@ static int term(yyscan_t scanner, int type)
%}
%x mem
+%s config
+%x event
+
+group [^,{}/]*[{][^}]*[}][^,{}/]*
+event_pmu [^,{}/]+[/][^/]*[/][^,{}/]*
+event [^,{}/]+
num_dec [0-9]+
num_hex 0x[a-fA-F0-9]+
@@ -84,7 +90,13 @@ modifier_bp [rwx]{1,3}
{
int start_token;
- start_token = (int) parse_events_get_extra(yyscanner);
+ start_token = parse_events_get_extra(yyscanner);
+
+ if (start_token == PE_START_TERMS)
+ BEGIN(config);
+ else if (start_token == PE_START_EVENTS)
+ BEGIN(event);
+
if (start_token) {
parse_events_set_extra(NULL, yyscanner);
return start_token;
@@ -92,6 +104,26 @@ modifier_bp [rwx]{1,3}
}
%}
+<event>{
+
+{group} {
+ BEGIN(INITIAL); yyless(0);
+ }
+
+{event_pmu} |
+{event} {
+ str(yyscanner, PE_EVENT_NAME);
+ BEGIN(INITIAL); yyless(0);
+ return PE_EVENT_NAME;
+ }
+
+. |
+<<EOF>> {
+ BEGIN(INITIAL); yyless(0);
+ }
+
+}
+
cpu-cycles|cycles { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES); }
stalled-cycles-frontend|idle-cycles-frontend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND); }
stalled-cycles-backend|idle-cycles-backend { return sym(yyscanner, PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND); }
@@ -127,18 +159,16 @@ speculative-read|speculative-load |
refs|Reference|ops|access |
misses|miss { return str(yyscanner, PE_NAME_CACHE_OP_RESULT); }
- /*
- * These are event config hardcoded term names to be specified
- * within xxx/.../ syntax. So far we dont clash with other names,
- * so we can put them here directly. In case the we have a conflict
- * in future, this needs to go into '//' condition block.
- */
+<config>{
config { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG); }
config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); }
config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); }
name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); }
period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
+, { return ','; }
+"/" { BEGIN(INITIAL); return '/'; }
+}
mem: { BEGIN(mem); return PE_PREFIX_MEM; }
r{num_raw_hex} { return raw(yyscanner); }
@@ -147,10 +177,12 @@ r{num_raw_hex} { return raw(yyscanner); }
{modifier_event} { return str(yyscanner, PE_MODIFIER_EVENT); }
{name} { return str(yyscanner, PE_NAME); }
-"/" { return '/'; }
+"/" { BEGIN(config); return '/'; }
- { return '-'; }
-, { return ','; }
+, { BEGIN(event); return ','; }
: { return ':'; }
+"{" { BEGIN(event); return '{'; }
+"}" { return '}'; }
= { return '='; }
\n { }
@@ -175,7 +207,7 @@ r{num_raw_hex} { return raw(yyscanner); }
%%
-int parse_events_wrap(void *scanner __used)
+int parse_events_wrap(void *scanner __maybe_unused)
{
return 1;
}
diff --git a/tools/perf/util/parse-events.y b/tools/perf/util/parse-events.y
index 2bc5fbff2b5d..cd88209e3c58 100644
--- a/tools/perf/util/parse-events.y
+++ b/tools/perf/util/parse-events.y
@@ -27,10 +27,11 @@ do { \
%token PE_START_EVENTS PE_START_TERMS
%token PE_VALUE PE_VALUE_SYM_HW PE_VALUE_SYM_SW PE_RAW PE_TERM
+%token PE_EVENT_NAME
%token PE_NAME
%token PE_MODIFIER_EVENT PE_MODIFIER_BP
%token PE_NAME_CACHE_TYPE PE_NAME_CACHE_OP_RESULT
-%token PE_PREFIX_MEM PE_PREFIX_RAW
+%token PE_PREFIX_MEM PE_PREFIX_RAW PE_PREFIX_GROUP
%token PE_ERROR
%type <num> PE_VALUE
%type <num> PE_VALUE_SYM_HW
@@ -42,6 +43,7 @@ do { \
%type <str> PE_NAME_CACHE_OP_RESULT
%type <str> PE_MODIFIER_EVENT
%type <str> PE_MODIFIER_BP
+%type <str> PE_EVENT_NAME
%type <num> value_sym
%type <head> event_config
%type <term> event_term
@@ -53,44 +55,125 @@ do { \
%type <head> event_legacy_numeric
%type <head> event_legacy_raw
%type <head> event_def
+%type <head> event_mod
+%type <head> event_name
+%type <head> event
+%type <head> events
+%type <head> group_def
+%type <head> group
+%type <head> groups
%union
{
char *str;
- unsigned long num;
+ u64 num;
struct list_head *head;
struct parse_events__term *term;
}
%%
start:
-PE_START_EVENTS events
+PE_START_EVENTS start_events
|
-PE_START_TERMS terms
+PE_START_TERMS start_terms
+
+start_events: groups
+{
+ struct parse_events_data__events *data = _data;
+
+ parse_events_update_lists($1, &data->list);
+}
+
+groups:
+groups ',' group
+{
+ struct list_head *list = $1;
+ struct list_head *group = $3;
+
+ parse_events_update_lists(group, list);
+ $$ = list;
+}
+|
+groups ',' event
+{
+ struct list_head *list = $1;
+ struct list_head *event = $3;
+
+ parse_events_update_lists(event, list);
+ $$ = list;
+}
+|
+group
+|
+event
+
+group:
+group_def ':' PE_MODIFIER_EVENT
+{
+ struct list_head *list = $1;
+
+ ABORT_ON(parse_events__modifier_group(list, $3));
+ $$ = list;
+}
+|
+group_def
+
+group_def:
+PE_NAME '{' events '}'
+{
+ struct list_head *list = $3;
+
+ parse_events__set_leader($1, list);
+ $$ = list;
+}
+|
+'{' events '}'
+{
+ struct list_head *list = $2;
+
+ parse_events__set_leader(NULL, list);
+ $$ = list;
+}
events:
-events ',' event | event
+events ',' event
+{
+ struct list_head *event = $3;
+ struct list_head *list = $1;
-event:
-event_def PE_MODIFIER_EVENT
+ parse_events_update_lists(event, list);
+ $$ = list;
+}
+|
+event
+
+event: event_mod
+
+event_mod:
+event_name PE_MODIFIER_EVENT
{
- struct parse_events_data__events *data = _data;
+ struct list_head *list = $1;
/*
* Apply modifier on all events added by single event definition
* (there could be more events added for multiple tracepoint
* definitions via '*?'.
*/
- ABORT_ON(parse_events_modifier($1, $2));
- parse_events_update_lists($1, &data->list);
+ ABORT_ON(parse_events__modifier_event(list, $2, false));
+ $$ = list;
}
|
-event_def
-{
- struct parse_events_data__events *data = _data;
+event_name
- parse_events_update_lists($1, &data->list);
+event_name:
+PE_EVENT_NAME event_def
+{
+ ABORT_ON(parse_events_name($2, $1));
+ free($1);
+ $$ = $2;
}
+|
+event_def
event_def: event_pmu |
event_legacy_symbol |
@@ -207,7 +290,7 @@ PE_VALUE ':' PE_VALUE
struct parse_events_data__events *data = _data;
struct list_head *list = NULL;
- ABORT_ON(parse_events_add_numeric(&list, &data->idx, $1, $3, NULL));
+ ABORT_ON(parse_events_add_numeric(&list, &data->idx, (u32)$1, $3, NULL));
$$ = list;
}
@@ -222,7 +305,7 @@ PE_RAW
$$ = list;
}
-terms: event_config
+start_terms: event_config
{
struct parse_events_data__terms *data = _data;
data->terms = $1;
@@ -282,7 +365,7 @@ PE_TERM '=' PE_NAME
{
struct parse_events__term *term;
- ABORT_ON(parse_events__term_str(&term, $1, NULL, $3));
+ ABORT_ON(parse_events__term_str(&term, (int)$1, NULL, $3));
$$ = term;
}
|
@@ -290,7 +373,7 @@ PE_TERM '=' PE_VALUE
{
struct parse_events__term *term;
- ABORT_ON(parse_events__term_num(&term, $1, NULL, $3));
+ ABORT_ON(parse_events__term_num(&term, (int)$1, NULL, $3));
$$ = term;
}
|
@@ -298,7 +381,7 @@ PE_TERM
{
struct parse_events__term *term;
- ABORT_ON(parse_events__term_num(&term, $1, NULL, 1));
+ ABORT_ON(parse_events__term_num(&term, (int)$1, NULL, 1));
$$ = term;
}
@@ -308,7 +391,7 @@ sep_slash_dc: '/' | ':' |
%%
-void parse_events_error(void *data __used, void *scanner __used,
- char const *msg __used)
+void parse_events_error(void *data __maybe_unused, void *scanner __maybe_unused,
+ char const *msg __maybe_unused)
{
}
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index 594f8fad5ecd..443fc116512b 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -557,7 +557,8 @@ int parse_options_usage(const char * const *usagestr,
}
-int parse_opt_verbosity_cb(const struct option *opt, const char *arg __used,
+int parse_opt_verbosity_cb(const struct option *opt,
+ const char *arg __maybe_unused,
int unset)
{
int *target = opt->value;
diff --git a/tools/perf/util/perf_regs.h b/tools/perf/util/perf_regs.h
new file mode 100644
index 000000000000..316dbe7f86ed
--- /dev/null
+++ b/tools/perf/util/perf_regs.h
@@ -0,0 +1,14 @@
+#ifndef __PERF_REGS_H
+#define __PERF_REGS_H
+
+#ifndef NO_PERF_REGS
+#include <perf_regs.h>
+#else
+#define PERF_REGS_MASK 0
+
+static inline const char *perf_reg_name(int id __maybe_unused)
+{
+ return NULL;
+}
+#endif /* NO_PERF_REGS */
+#endif /* __PERF_REGS_H */
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 67715a42cd6d..8a2229da594f 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -9,6 +9,9 @@
#include "util.h"
#include "pmu.h"
#include "parse-events.h"
+#include "cpumap.h"
+
+#define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/"
int perf_pmu_parse(struct list_head *list, char *name);
extern FILE *perf_pmu_in;
@@ -69,7 +72,7 @@ static int pmu_format(char *name, struct list_head *format)
return -1;
snprintf(path, PATH_MAX,
- "%s/bus/event_source/devices/%s/format", sysfs, name);
+ "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name);
if (stat(path, &st) < 0)
return 0; /* no error if format does not exist */
@@ -206,7 +209,7 @@ static int pmu_type(char *name, __u32 *type)
return -1;
snprintf(path, PATH_MAX,
- "%s/bus/event_source/devices/%s/type", sysfs, name);
+ "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name);
if (stat(path, &st) < 0)
return -1;
@@ -222,6 +225,62 @@ static int pmu_type(char *name, __u32 *type)
return ret;
}
+/* Add all pmus in sysfs to pmu list: */
+static void pmu_read_sysfs(void)
+{
+ char path[PATH_MAX];
+ const char *sysfs;
+ DIR *dir;
+ struct dirent *dent;
+
+ sysfs = sysfs_find_mountpoint();
+ if (!sysfs)
+ return;
+
+ snprintf(path, PATH_MAX,
+ "%s" EVENT_SOURCE_DEVICE_PATH, sysfs);
+
+ dir = opendir(path);
+ if (!dir)
+ return;
+
+ while ((dent = readdir(dir))) {
+ if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))
+ continue;
+ /* add to static LIST_HEAD(pmus): */
+ perf_pmu__find(dent->d_name);
+ }
+
+ closedir(dir);
+}
+
+static struct cpu_map *pmu_cpumask(char *name)
+{
+ struct stat st;
+ char path[PATH_MAX];
+ const char *sysfs;
+ FILE *file;
+ struct cpu_map *cpus;
+
+ sysfs = sysfs_find_mountpoint();
+ if (!sysfs)
+ return NULL;
+
+ snprintf(path, PATH_MAX,
+ "%s/bus/event_source/devices/%s/cpumask", sysfs, name);
+
+ if (stat(path, &st) < 0)
+ return NULL;
+
+ file = fopen(path, "r");
+ if (!file)
+ return NULL;
+
+ cpus = cpu_map__read(file);
+ fclose(file);
+ return cpus;
+}
+
static struct perf_pmu *pmu_lookup(char *name)
{
struct perf_pmu *pmu;
@@ -244,6 +303,8 @@ static struct perf_pmu *pmu_lookup(char *name)
if (!pmu)
return NULL;
+ pmu->cpus = pmu_cpumask(name);
+
pmu_aliases(name, &aliases);
INIT_LIST_HEAD(&pmu->format);
@@ -267,6 +328,21 @@ static struct perf_pmu *pmu_find(char *name)
return NULL;
}
+struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu)
+{
+ /*
+ * pmu iterator: If pmu is NULL, we start at the begin,
+ * otherwise return the next pmu. Returns NULL on end.
+ */
+ if (!pmu) {
+ pmu_read_sysfs();
+ pmu = list_prepare_entry(pmu, &pmus, list);
+ }
+ list_for_each_entry_continue(pmu, &pmus, list)
+ return pmu;
+ return NULL;
+}
+
struct perf_pmu *perf_pmu__find(char *name)
{
struct perf_pmu *pmu;
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 535f2c5258ab..53c7794fc4be 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -28,6 +28,7 @@ struct perf_pmu__alias {
struct perf_pmu {
char *name;
__u32 type;
+ struct cpu_map *cpus;
struct list_head format;
struct list_head aliases;
struct list_head list;
@@ -46,5 +47,7 @@ int perf_pmu__new_format(struct list_head *list, char *name,
int config, unsigned long *bits);
void perf_pmu__set_format(unsigned long *bits, long from, long to);
+struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu);
+
int perf_pmu__test(void);
#endif /* __PMU_H */
diff --git a/tools/perf/util/pmu.y b/tools/perf/util/pmu.y
index 20ea77e93169..ec898047ebb9 100644
--- a/tools/perf/util/pmu.y
+++ b/tools/perf/util/pmu.y
@@ -86,8 +86,8 @@ PP_VALUE
%%
-void perf_pmu_error(struct list_head *list __used,
- char *name __used,
- char const *msg __used)
+void perf_pmu_error(struct list_head *list __maybe_unused,
+ char *name __maybe_unused,
+ char const *msg __maybe_unused)
{
}
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 0dda25d82d06..49a256e6e0a2 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -41,7 +41,7 @@
#include "symbol.h"
#include "thread.h"
#include "debugfs.h"
-#include "trace-event.h" /* For __unused */
+#include "trace-event.h" /* For __maybe_unused */
#include "probe-event.h"
#include "probe-finder.h"
#include "session.h"
@@ -647,8 +647,8 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp,
}
static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
- struct probe_trace_event **tevs __unused,
- int max_tevs __unused, const char *target)
+ struct probe_trace_event **tevs __maybe_unused,
+ int max_tevs __maybe_unused, const char *target)
{
if (perf_probe_event_need_dwarf(pev)) {
pr_warning("Debuginfo-analysis is not supported.\n");
@@ -661,17 +661,18 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
return 0;
}
-int show_line_range(struct line_range *lr __unused, const char *module __unused)
+int show_line_range(struct line_range *lr __maybe_unused,
+ const char *module __maybe_unused)
{
pr_warning("Debuginfo-analysis is not supported.\n");
return -ENOSYS;
}
-int show_available_vars(struct perf_probe_event *pevs __unused,
- int npevs __unused, int max_vls __unused,
- const char *module __unused,
- struct strfilter *filter __unused,
- bool externs __unused)
+int show_available_vars(struct perf_probe_event *pevs __maybe_unused,
+ int npevs __maybe_unused, int max_vls __maybe_unused,
+ const char *module __maybe_unused,
+ struct strfilter *filter __maybe_unused,
+ bool externs __maybe_unused)
{
pr_warning("Debuginfo-analysis is not supported.\n");
return -ENOSYS;
@@ -1099,6 +1100,7 @@ static int parse_probe_trace_command(const char *cmd,
struct probe_trace_point *tp = &tev->point;
char pr;
char *p;
+ char *argv0_str = NULL, *fmt, *fmt1_str, *fmt2_str, *fmt3_str;
int ret, i, argc;
char **argv;
@@ -1115,14 +1117,27 @@ static int parse_probe_trace_command(const char *cmd,
}
/* Scan event and group name. */
- ret = sscanf(argv[0], "%c:%a[^/ \t]/%a[^ \t]",
- &pr, (float *)(void *)&tev->group,
- (float *)(void *)&tev->event);
- if (ret != 3) {
+ argv0_str = strdup(argv[0]);
+ if (argv0_str == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ fmt1_str = strtok_r(argv0_str, ":", &fmt);
+ fmt2_str = strtok_r(NULL, "/", &fmt);
+ fmt3_str = strtok_r(NULL, " \t", &fmt);
+ if (fmt1_str == NULL || strlen(fmt1_str) != 1 || fmt2_str == NULL
+ || fmt3_str == NULL) {
semantic_error("Failed to parse event name: %s\n", argv[0]);
ret = -EINVAL;
goto out;
}
+ pr = fmt1_str[0];
+ tev->group = strdup(fmt2_str);
+ tev->event = strdup(fmt3_str);
+ if (tev->group == NULL || tev->event == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
pr_debug("Group:%s Event:%s probe:%c\n", tev->group, tev->event, pr);
tp->retprobe = (pr == 'r');
@@ -1134,10 +1149,17 @@ static int parse_probe_trace_command(const char *cmd,
p++;
} else
p = argv[1];
- ret = sscanf(p, "%a[^+]+%lu", (float *)(void *)&tp->symbol,
- &tp->offset);
- if (ret == 1)
+ fmt1_str = strtok_r(p, "+", &fmt);
+ tp->symbol = strdup(fmt1_str);
+ if (tp->symbol == NULL) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ fmt2_str = strtok_r(NULL, "", &fmt);
+ if (fmt2_str == NULL)
tp->offset = 0;
+ else
+ tp->offset = strtoul(fmt2_str, NULL, 10);
tev->nargs = argc - 2;
tev->args = zalloc(sizeof(struct probe_trace_arg) * tev->nargs);
@@ -1161,6 +1183,7 @@ static int parse_probe_trace_command(const char *cmd,
}
ret = 0;
out:
+ free(argv0_str);
argv_free(argv);
return ret;
}
@@ -2183,7 +2206,7 @@ static struct strfilter *available_func_filter;
* If a symbol corresponds to a function with global binding and
* matches filter return 0. For all others return 1.
*/
-static int filter_available_functions(struct map *map __unused,
+static int filter_available_functions(struct map *map __maybe_unused,
struct symbol *sym)
{
if (sym->binding == STB_GLOBAL &&
@@ -2307,10 +2330,17 @@ static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec)
function = NULL;
}
if (!pev->group) {
- char *ptr1, *ptr2;
+ char *ptr1, *ptr2, *exec_copy;
pev->group = zalloc(sizeof(char *) * 64);
- ptr1 = strdup(basename(exec));
+ exec_copy = strdup(exec);
+ if (!exec_copy) {
+ ret = -ENOMEM;
+ pr_warning("Failed to copy exec string.\n");
+ goto out;
+ }
+
+ ptr1 = strdup(basename(exec_copy));
if (ptr1) {
ptr2 = strpbrk(ptr1, "-._");
if (ptr2)
@@ -2319,6 +2349,7 @@ static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec)
ptr1);
free(ptr1);
}
+ free(exec_copy);
}
free(pp->function);
pp->function = zalloc(sizeof(char *) * MAX_PROBE_ARGS);
diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c
index d448984ed789..1daf5c14e751 100644
--- a/tools/perf/util/probe-finder.c
+++ b/tools/perf/util/probe-finder.c
@@ -207,7 +207,7 @@ static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self,
#else
/* With older elfutils, this just support kernel module... */
static int debuginfo__init_online_kernel_dwarf(struct debuginfo *self,
- Dwarf_Addr addr __used)
+ Dwarf_Addr addr __maybe_unused)
{
const char *path = kernel_get_module_path("kernel");
@@ -525,8 +525,10 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
return -ENOENT;
}
/* Verify it is a data structure */
- if (dwarf_tag(&type) != DW_TAG_structure_type) {
- pr_warning("%s is not a data structure.\n", varname);
+ tag = dwarf_tag(&type);
+ if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) {
+ pr_warning("%s is not a data structure nor an union.\n",
+ varname);
return -EINVAL;
}
@@ -539,8 +541,9 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
*ref_ptr = ref;
} else {
/* Verify it is a data structure */
- if (tag != DW_TAG_structure_type) {
- pr_warning("%s is not a data structure.\n", varname);
+ if (tag != DW_TAG_structure_type && tag != DW_TAG_union_type) {
+ pr_warning("%s is not a data structure nor an union.\n",
+ varname);
return -EINVAL;
}
if (field->name[0] == '[') {
@@ -567,10 +570,15 @@ static int convert_variable_fields(Dwarf_Die *vr_die, const char *varname,
}
/* Get the offset of the field */
- ret = die_get_data_member_location(die_mem, &offs);
- if (ret < 0) {
- pr_warning("Failed to get the offset of %s.\n", field->name);
- return ret;
+ if (tag == DW_TAG_union_type) {
+ offs = 0;
+ } else {
+ ret = die_get_data_member_location(die_mem, &offs);
+ if (ret < 0) {
+ pr_warning("Failed to get the offset of %s.\n",
+ field->name);
+ return ret;
+ }
}
ref->offset += (long)offs;
@@ -1419,7 +1427,7 @@ static int line_range_add_line(const char *src, unsigned int lineno,
}
static int line_range_walk_cb(const char *fname, int lineno,
- Dwarf_Addr addr __used,
+ Dwarf_Addr addr __maybe_unused,
void *data)
{
struct line_finder *lf = data;
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources
index 213362850abd..c40c2d33199e 100644
--- a/tools/perf/util/python-ext-sources
+++ b/tools/perf/util/python-ext-sources
@@ -1,5 +1,5 @@
#
-# List of files needed by perf python extention
+# List of files needed by perf python extension
#
# Each source file must be placed on its own line so that it can be
# processed by Makefile and util/setup.py accordingly.
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index 0688bfb6d280..9181bf212fb9 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -627,7 +627,7 @@ static PyObject *pyrf_evsel__open(struct pyrf_evsel *pevsel,
* This will group just the fds for this single evsel, to group
* multiple events, use evlist.open().
*/
- if (perf_evsel__open(evsel, cpus, threads, group, NULL) < 0) {
+ if (perf_evsel__open(evsel, cpus, threads) < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
@@ -672,7 +672,7 @@ struct pyrf_evlist {
};
static int pyrf_evlist__init(struct pyrf_evlist *pevlist,
- PyObject *args, PyObject *kwargs __used)
+ PyObject *args, PyObject *kwargs __maybe_unused)
{
PyObject *pcpus = NULL, *pthreads = NULL;
struct cpu_map *cpus;
@@ -733,7 +733,8 @@ static PyObject *pyrf_evlist__poll(struct pyrf_evlist *pevlist,
}
static PyObject *pyrf_evlist__get_pollfd(struct pyrf_evlist *pevlist,
- PyObject *args __used, PyObject *kwargs __used)
+ PyObject *args __maybe_unused,
+ PyObject *kwargs __maybe_unused)
{
struct perf_evlist *evlist = &pevlist->evlist;
PyObject *list = PyList_New(0);
@@ -765,7 +766,8 @@ free_list:
static PyObject *pyrf_evlist__add(struct pyrf_evlist *pevlist,
- PyObject *args, PyObject *kwargs __used)
+ PyObject *args,
+ PyObject *kwargs __maybe_unused)
{
struct perf_evlist *evlist = &pevlist->evlist;
PyObject *pevsel;
@@ -803,7 +805,7 @@ static PyObject *pyrf_evlist__read_on_cpu(struct pyrf_evlist *pevlist,
if (pyevent == NULL)
return PyErr_NoMemory();
- err = perf_evlist__parse_sample(evlist, event, &pevent->sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &pevent->sample);
if (err)
return PyErr_Format(PyExc_OSError,
"perf: can't parse sample, err=%d", err);
@@ -824,7 +826,10 @@ static PyObject *pyrf_evlist__open(struct pyrf_evlist *pevlist,
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOii", kwlist, &group))
return NULL;
- if (perf_evlist__open(evlist, group) < 0) {
+ if (group)
+ perf_evlist__set_leader(evlist);
+
+ if (perf_evlist__open(evlist) < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c
index 02dfa19a467f..f80605eb1855 100644
--- a/tools/perf/util/scripting-engines/trace-event-perl.c
+++ b/tools/perf/util/scripting-engines/trace-event-perl.c
@@ -25,16 +25,16 @@
#include <ctype.h>
#include <errno.h>
-#include "../../perf.h"
#include "../util.h"
+#include <EXTERN.h>
+#include <perl.h>
+
+#include "../../perf.h"
#include "../thread.h"
#include "../event.h"
#include "../trace-event.h"
#include "../evsel.h"
-#include <EXTERN.h>
-#include <perl.h>
-
void boot_Perf__Trace__Context(pTHX_ CV *cv);
void boot_DynaLoader(pTHX_ CV *cv);
typedef PerlInterpreter * INTERP;
@@ -237,16 +237,16 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->next);
}
-static inline
-struct event_format *find_cache_event(struct pevent *pevent, int type)
+static inline struct event_format *find_cache_event(struct perf_evsel *evsel)
{
static char ev_name[256];
struct event_format *event;
+ int type = evsel->attr.config;
if (events[type])
return events[type];
- events[type] = event = pevent_find_event(pevent, type);
+ events[type] = event = evsel->tp_format;
if (!event)
return NULL;
@@ -257,23 +257,22 @@ struct event_format *find_cache_event(struct pevent *pevent, int type)
return event;
}
-static void perl_process_tracepoint(union perf_event *perf_event __unused,
- struct pevent *pevent,
+static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused,
struct perf_sample *sample,
struct perf_evsel *evsel,
- struct machine *machine __unused,
- struct thread *thread)
+ struct machine *machine __maybe_unused,
+ struct addr_location *al)
{
struct format_field *field;
static char handler[256];
unsigned long long val;
unsigned long s, ns;
struct event_format *event;
- int type;
int pid;
int cpu = sample->cpu;
void *data = sample->raw_data;
unsigned long long nsecs = sample->time;
+ struct thread *thread = al->thread;
char *comm = thread->comm;
dSP;
@@ -281,13 +280,11 @@ static void perl_process_tracepoint(union perf_event *perf_event __unused,
if (evsel->attr.type != PERF_TYPE_TRACEPOINT)
return;
- type = trace_parse_common_type(pevent, data);
-
- event = find_cache_event(pevent, type);
+ event = find_cache_event(evsel);
if (!event)
- die("ug! no event found for type %d", type);
+ die("ug! no event found for type %" PRIu64, evsel->attr.config);
- pid = trace_parse_common_pid(pevent, data);
+ pid = raw_field_value(event, "common_pid", data);
sprintf(handler, "%s::%s", event->system, event->name);
@@ -320,7 +317,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __unused,
offset = field->offset;
XPUSHs(sv_2mortal(newSVpv((char *)data + offset, 0)));
} else { /* FIELD_IS_NUMERIC */
- val = read_size(pevent, data + field->offset,
+ val = read_size(event, data + field->offset,
field->size);
if (field->flags & FIELD_IS_SIGNED) {
XPUSHs(sv_2mortal(newSViv(val)));
@@ -349,11 +346,11 @@ static void perl_process_tracepoint(union perf_event *perf_event __unused,
LEAVE;
}
-static void perl_process_event_generic(union perf_event *pevent __unused,
+static void perl_process_event_generic(union perf_event *event,
struct perf_sample *sample,
- struct perf_evsel *evsel __unused,
- struct machine *machine __unused,
- struct thread *thread __unused)
+ struct perf_evsel *evsel,
+ struct machine *machine __maybe_unused,
+ struct addr_location *al __maybe_unused)
{
dSP;
@@ -363,7 +360,7 @@ static void perl_process_event_generic(union perf_event *pevent __unused,
ENTER;
SAVETMPS;
PUSHMARK(SP);
- XPUSHs(sv_2mortal(newSVpvn((const char *)pevent, pevent->header.size)));
+ XPUSHs(sv_2mortal(newSVpvn((const char *)event, event->header.size)));
XPUSHs(sv_2mortal(newSVpvn((const char *)&evsel->attr, sizeof(evsel->attr))));
XPUSHs(sv_2mortal(newSVpvn((const char *)sample, sizeof(*sample))));
XPUSHs(sv_2mortal(newSVpvn((const char *)sample->raw_data, sample->raw_size)));
@@ -376,14 +373,13 @@ static void perl_process_event_generic(union perf_event *pevent __unused,
}
static void perl_process_event(union perf_event *event,
- struct pevent *pevent,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct machine *machine,
- struct thread *thread)
+ struct addr_location *al)
{
- perl_process_tracepoint(event, pevent, sample, evsel, machine, thread);
- perl_process_event_generic(event, sample, evsel, machine, thread);
+ perl_process_tracepoint(event, sample, evsel, machine, al);
+ perl_process_event_generic(event, sample, evsel, machine, al);
}
static void run_start_sub(void)
diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c
index ce4d1b0c3862..730c6630cba5 100644
--- a/tools/perf/util/scripting-engines/trace-event-python.c
+++ b/tools/perf/util/scripting-engines/trace-event-python.c
@@ -27,10 +27,12 @@
#include <errno.h>
#include "../../perf.h"
+#include "../evsel.h"
#include "../util.h"
#include "../event.h"
#include "../thread.h"
#include "../trace-event.h"
+#include "../evsel.h"
PyMODINIT_FUNC initperf_trace_context(void);
@@ -194,16 +196,21 @@ static void define_event_symbols(struct event_format *event,
define_event_symbols(event, ev_name, args->next);
}
-static inline
-struct event_format *find_cache_event(struct pevent *pevent, int type)
+static inline struct event_format *find_cache_event(struct perf_evsel *evsel)
{
static char ev_name[256];
struct event_format *event;
+ int type = evsel->attr.config;
+ /*
+ * XXX: Do we really need to cache this since now we have evsel->tp_format
+ * cached already? Need to re-read this "cache" routine that as well calls
+ * define_event_symbols() :-\
+ */
if (events[type])
return events[type];
- events[type] = event = pevent_find_event(pevent, type);
+ events[type] = event = evsel->tp_format;
if (!event)
return NULL;
@@ -214,12 +221,12 @@ struct event_format *find_cache_event(struct pevent *pevent, int type)
return event;
}
-static void python_process_event(union perf_event *perf_event __unused,
- struct pevent *pevent,
+static void python_process_tracepoint(union perf_event *perf_event
+ __maybe_unused,
struct perf_sample *sample,
- struct perf_evsel *evsel __unused,
- struct machine *machine __unused,
- struct thread *thread)
+ struct perf_evsel *evsel,
+ struct machine *machine __maybe_unused,
+ struct addr_location *al)
{
PyObject *handler, *retval, *context, *t, *obj, *dict = NULL;
static char handler_name[256];
@@ -228,24 +235,22 @@ static void python_process_event(union perf_event *perf_event __unused,
unsigned long s, ns;
struct event_format *event;
unsigned n = 0;
- int type;
int pid;
int cpu = sample->cpu;
void *data = sample->raw_data;
unsigned long long nsecs = sample->time;
+ struct thread *thread = al->thread;
char *comm = thread->comm;
t = PyTuple_New(MAX_FIELDS);
if (!t)
Py_FatalError("couldn't create Python tuple");
- type = trace_parse_common_type(pevent, data);
-
- event = find_cache_event(pevent, type);
+ event = find_cache_event(evsel);
if (!event)
- die("ug! no event found for type %d", type);
+ die("ug! no event found for type %d", (int)evsel->attr.config);
- pid = trace_parse_common_pid(pevent, data);
+ pid = raw_field_value(event, "common_pid", data);
sprintf(handler_name, "%s__%s", event->system, event->name);
@@ -290,7 +295,7 @@ static void python_process_event(union perf_event *perf_event __unused,
offset = field->offset;
obj = PyString_FromString((char *)data + offset);
} else { /* FIELD_IS_NUMERIC */
- val = read_size(pevent, data + field->offset,
+ val = read_size(event, data + field->offset,
field->size);
if (field->flags & FIELD_IS_SIGNED) {
if ((long long)val >= LONG_MIN &&
@@ -335,6 +340,84 @@ static void python_process_event(union perf_event *perf_event __unused,
Py_DECREF(t);
}
+static void python_process_general_event(union perf_event *perf_event
+ __maybe_unused,
+ struct perf_sample *sample,
+ struct perf_evsel *evsel,
+ struct machine *machine __maybe_unused,
+ struct addr_location *al)
+{
+ PyObject *handler, *retval, *t, *dict;
+ static char handler_name[64];
+ unsigned n = 0;
+ struct thread *thread = al->thread;
+
+ /*
+ * Use the MAX_FIELDS to make the function expandable, though
+ * currently there is only one item for the tuple.
+ */
+ t = PyTuple_New(MAX_FIELDS);
+ if (!t)
+ Py_FatalError("couldn't create Python tuple");
+
+ dict = PyDict_New();
+ if (!dict)
+ Py_FatalError("couldn't create Python dictionary");
+
+ snprintf(handler_name, sizeof(handler_name), "%s", "process_event");
+
+ handler = PyDict_GetItemString(main_dict, handler_name);
+ if (!handler || !PyCallable_Check(handler))
+ goto exit;
+
+ PyDict_SetItemString(dict, "ev_name", PyString_FromString(perf_evsel__name(evsel)));
+ PyDict_SetItemString(dict, "attr", PyString_FromStringAndSize(
+ (const char *)&evsel->attr, sizeof(evsel->attr)));
+ PyDict_SetItemString(dict, "sample", PyString_FromStringAndSize(
+ (const char *)sample, sizeof(*sample)));
+ PyDict_SetItemString(dict, "raw_buf", PyString_FromStringAndSize(
+ (const char *)sample->raw_data, sample->raw_size));
+ PyDict_SetItemString(dict, "comm",
+ PyString_FromString(thread->comm));
+ if (al->map) {
+ PyDict_SetItemString(dict, "dso",
+ PyString_FromString(al->map->dso->name));
+ }
+ if (al->sym) {
+ PyDict_SetItemString(dict, "symbol",
+ PyString_FromString(al->sym->name));
+ }
+
+ PyTuple_SetItem(t, n++, dict);
+ if (_PyTuple_Resize(&t, n) == -1)
+ Py_FatalError("error resizing Python tuple");
+
+ retval = PyObject_CallObject(handler, t);
+ if (retval == NULL)
+ handler_call_die(handler_name);
+exit:
+ Py_DECREF(dict);
+ Py_DECREF(t);
+}
+
+static void python_process_event(union perf_event *perf_event,
+ struct perf_sample *sample,
+ struct perf_evsel *evsel,
+ struct machine *machine,
+ struct addr_location *al)
+{
+ switch (evsel->attr.type) {
+ case PERF_TYPE_TRACEPOINT:
+ python_process_tracepoint(perf_event, sample, evsel,
+ machine, al);
+ break;
+ /* Reserve for future process_hw/sw/raw APIs */
+ default:
+ python_process_general_event(perf_event, sample, evsel,
+ machine, al);
+ }
+}
+
static int run_start_sub(void)
{
PyObject *handler, *retval;
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 2437fb0b463a..8cdd23239c90 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -15,6 +15,9 @@
#include "util.h"
#include "cpumap.h"
#include "event-parse.h"
+#include "perf_regs.h"
+#include "unwind.h"
+#include "vdso.h"
static int perf_session__open(struct perf_session *self, bool force)
{
@@ -209,6 +212,7 @@ void perf_session__delete(struct perf_session *self)
machine__exit(&self->host_machine);
close(self->fd);
free(self);
+ vdso__exit();
}
void machine__remove_thread(struct machine *self, struct thread *th)
@@ -288,10 +292,11 @@ struct branch_info *machine__resolve_bstack(struct machine *self,
return bi;
}
-int machine__resolve_callchain(struct machine *self,
- struct thread *thread,
- struct ip_callchain *chain,
- struct symbol **parent)
+static int machine__resolve_callchain_sample(struct machine *machine,
+ struct thread *thread,
+ struct ip_callchain *chain,
+ struct symbol **parent)
+
{
u8 cpumode = PERF_RECORD_MISC_USER;
unsigned int i;
@@ -316,11 +321,14 @@ int machine__resolve_callchain(struct machine *self,
if (ip >= PERF_CONTEXT_MAX) {
switch (ip) {
case PERF_CONTEXT_HV:
- cpumode = PERF_RECORD_MISC_HYPERVISOR; break;
+ cpumode = PERF_RECORD_MISC_HYPERVISOR;
+ break;
case PERF_CONTEXT_KERNEL:
- cpumode = PERF_RECORD_MISC_KERNEL; break;
+ cpumode = PERF_RECORD_MISC_KERNEL;
+ break;
case PERF_CONTEXT_USER:
- cpumode = PERF_RECORD_MISC_USER; break;
+ cpumode = PERF_RECORD_MISC_USER;
+ break;
default:
pr_debug("invalid callchain context: "
"%"PRId64"\n", (s64) ip);
@@ -335,7 +343,7 @@ int machine__resolve_callchain(struct machine *self,
}
al.filtered = false;
- thread__find_addr_location(thread, self, cpumode,
+ thread__find_addr_location(thread, machine, cpumode,
MAP__FUNCTION, ip, &al, NULL);
if (al.sym != NULL) {
if (sort__has_parent && !*parent &&
@@ -354,49 +362,92 @@ int machine__resolve_callchain(struct machine *self,
return 0;
}
-static int process_event_synth_tracing_data_stub(union perf_event *event __used,
- struct perf_session *session __used)
+static int unwind_entry(struct unwind_entry *entry, void *arg)
+{
+ struct callchain_cursor *cursor = arg;
+ return callchain_cursor_append(cursor, entry->ip,
+ entry->map, entry->sym);
+}
+
+int machine__resolve_callchain(struct machine *machine,
+ struct perf_evsel *evsel,
+ struct thread *thread,
+ struct perf_sample *sample,
+ struct symbol **parent)
+
+{
+ int ret;
+
+ callchain_cursor_reset(&callchain_cursor);
+
+ ret = machine__resolve_callchain_sample(machine, thread,
+ sample->callchain, parent);
+ if (ret)
+ return ret;
+
+ /* Can we do dwarf post unwind? */
+ if (!((evsel->attr.sample_type & PERF_SAMPLE_REGS_USER) &&
+ (evsel->attr.sample_type & PERF_SAMPLE_STACK_USER)))
+ return 0;
+
+ /* Bail out if nothing was captured. */
+ if ((!sample->user_regs.regs) ||
+ (!sample->user_stack.size))
+ return 0;
+
+ return unwind__get_entries(unwind_entry, &callchain_cursor, machine,
+ thread, evsel->attr.sample_regs_user,
+ sample);
+
+}
+
+static int process_event_synth_tracing_data_stub(union perf_event *event
+ __maybe_unused,
+ struct perf_session *session
+ __maybe_unused)
{
dump_printf(": unhandled!\n");
return 0;
}
-static int process_event_synth_attr_stub(union perf_event *event __used,
- struct perf_evlist **pevlist __used)
+static int process_event_synth_attr_stub(union perf_event *event __maybe_unused,
+ struct perf_evlist **pevlist
+ __maybe_unused)
{
dump_printf(": unhandled!\n");
return 0;
}
-static int process_event_sample_stub(struct perf_tool *tool __used,
- union perf_event *event __used,
- struct perf_sample *sample __used,
- struct perf_evsel *evsel __used,
- struct machine *machine __used)
+static int process_event_sample_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ struct perf_evsel *evsel __maybe_unused,
+ struct machine *machine __maybe_unused)
{
dump_printf(": unhandled!\n");
return 0;
}
-static int process_event_stub(struct perf_tool *tool __used,
- union perf_event *event __used,
- struct perf_sample *sample __used,
- struct machine *machine __used)
+static int process_event_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ struct machine *machine __maybe_unused)
{
dump_printf(": unhandled!\n");
return 0;
}
-static int process_finished_round_stub(struct perf_tool *tool __used,
- union perf_event *event __used,
- struct perf_session *perf_session __used)
+static int process_finished_round_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused,
+ struct perf_session *perf_session
+ __maybe_unused)
{
dump_printf(": unhandled!\n");
return 0;
}
-static int process_event_type_stub(struct perf_tool *tool __used,
- union perf_event *event __used)
+static int process_event_type_stub(struct perf_tool *tool __maybe_unused,
+ union perf_event *event __maybe_unused)
{
dump_printf(": unhandled!\n");
return 0;
@@ -473,7 +524,7 @@ static void swap_sample_id_all(union perf_event *event, void *data)
}
static void perf_event__all64_swap(union perf_event *event,
- bool sample_id_all __used)
+ bool sample_id_all __maybe_unused)
{
struct perf_event_header *hdr = &event->header;
mem_bswap_64(hdr + 1, event->header.size - sizeof(*hdr));
@@ -487,7 +538,7 @@ static void perf_event__comm_swap(union perf_event *event, bool sample_id_all)
if (sample_id_all) {
void *data = &event->comm.comm;
- data += ALIGN(strlen(data) + 1, sizeof(u64));
+ data += PERF_ALIGN(strlen(data) + 1, sizeof(u64));
swap_sample_id_all(event, data);
}
}
@@ -504,7 +555,7 @@ static void perf_event__mmap_swap(union perf_event *event,
if (sample_id_all) {
void *data = &event->mmap.filename;
- data += ALIGN(strlen(data) + 1, sizeof(u64));
+ data += PERF_ALIGN(strlen(data) + 1, sizeof(u64));
swap_sample_id_all(event, data);
}
}
@@ -584,7 +635,7 @@ void perf_event__attr_swap(struct perf_event_attr *attr)
}
static void perf_event__hdr_attr_swap(union perf_event *event,
- bool sample_id_all __used)
+ bool sample_id_all __maybe_unused)
{
size_t size;
@@ -596,14 +647,14 @@ static void perf_event__hdr_attr_swap(union perf_event *event,
}
static void perf_event__event_type_swap(union perf_event *event,
- bool sample_id_all __used)
+ bool sample_id_all __maybe_unused)
{
event->event_type.event_type.event_id =
bswap_64(event->event_type.event_type.event_id);
}
static void perf_event__tracing_data_swap(union perf_event *event,
- bool sample_id_all __used)
+ bool sample_id_all __maybe_unused)
{
event->tracing_data.size = bswap_32(event->tracing_data.size);
}
@@ -652,7 +703,7 @@ static int perf_session_deliver_event(struct perf_session *session,
struct perf_tool *tool,
u64 file_offset);
-static void flush_sample_queue(struct perf_session *s,
+static int flush_sample_queue(struct perf_session *s,
struct perf_tool *tool)
{
struct ordered_samples *os = &s->ordered_samples;
@@ -665,19 +716,21 @@ static void flush_sample_queue(struct perf_session *s,
int ret;
if (!tool->ordered_samples || !limit)
- return;
+ return 0;
list_for_each_entry_safe(iter, tmp, head, list) {
if (iter->timestamp > limit)
break;
- ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample,
- s->header.needs_swap);
+ ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample);
if (ret)
pr_err("Can't parse sample, err = %d\n", ret);
- else
- perf_session_deliver_event(s, iter->event, &sample, tool,
- iter->file_offset);
+ else {
+ ret = perf_session_deliver_event(s, iter->event, &sample, tool,
+ iter->file_offset);
+ if (ret)
+ return ret;
+ }
os->last_flush = iter->timestamp;
list_del(&iter->list);
@@ -697,6 +750,8 @@ static void flush_sample_queue(struct perf_session *s,
}
os->nr_samples = 0;
+
+ return 0;
}
/*
@@ -739,13 +794,14 @@ static void flush_sample_queue(struct perf_session *s,
* etc...
*/
static int process_finished_round(struct perf_tool *tool,
- union perf_event *event __used,
+ union perf_event *event __maybe_unused,
struct perf_session *session)
{
- flush_sample_queue(session, tool);
- session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
+ int ret = flush_sample_queue(session, tool);
+ if (!ret)
+ session->ordered_samples.next_flush = session->ordered_samples.max_timestamp;
- return 0;
+ return ret;
}
/* The queue is ordered by time */
@@ -860,6 +916,34 @@ static void branch_stack__printf(struct perf_sample *sample)
sample->branch_stack->entries[i].to);
}
+static void regs_dump__printf(u64 mask, u64 *regs)
+{
+ unsigned rid, i = 0;
+
+ for_each_set_bit(rid, (unsigned long *) &mask, sizeof(mask) * 8) {
+ u64 val = regs[i++];
+
+ printf(".... %-5s 0x%" PRIx64 "\n",
+ perf_reg_name(rid), val);
+ }
+}
+
+static void regs_user__printf(struct perf_sample *sample, u64 mask)
+{
+ struct regs_dump *user_regs = &sample->user_regs;
+
+ if (user_regs->regs) {
+ printf("... user regs: mask 0x%" PRIx64 "\n", mask);
+ regs_dump__printf(mask, user_regs->regs);
+ }
+}
+
+static void stack_user__printf(struct stack_dump *dump)
+{
+ printf("... ustack: size %" PRIu64 ", offset 0x%x\n",
+ dump->size, dump->offset);
+}
+
static void perf_session__print_tstamp(struct perf_session *session,
union perf_event *event,
struct perf_sample *sample)
@@ -897,7 +981,7 @@ static void dump_event(struct perf_session *session, union perf_event *event,
event->header.size, perf_event__name(event->header.type));
}
-static void dump_sample(struct perf_session *session, union perf_event *event,
+static void dump_sample(struct perf_evsel *evsel, union perf_event *event,
struct perf_sample *sample)
{
u64 sample_type;
@@ -909,13 +993,19 @@ static void dump_sample(struct perf_session *session, union perf_event *event,
event->header.misc, sample->pid, sample->tid, sample->ip,
sample->period, sample->addr);
- sample_type = perf_evlist__sample_type(session->evlist);
+ sample_type = evsel->attr.sample_type;
if (sample_type & PERF_SAMPLE_CALLCHAIN)
callchain__printf(sample);
if (sample_type & PERF_SAMPLE_BRANCH_STACK)
branch_stack__printf(sample);
+
+ if (sample_type & PERF_SAMPLE_REGS_USER)
+ regs_user__printf(sample, evsel->attr.sample_regs_user);
+
+ if (sample_type & PERF_SAMPLE_STACK_USER)
+ stack_user__printf(&sample->user_stack);
}
static struct machine *
@@ -973,7 +1063,7 @@ static int perf_session_deliver_event(struct perf_session *session,
switch (event->header.type) {
case PERF_RECORD_SAMPLE:
- dump_sample(session, event, sample);
+ dump_sample(evsel, event, sample);
if (evsel == NULL) {
++session->hists.stats.nr_unknown_id;
return 0;
@@ -1083,8 +1173,7 @@ static int perf_session__process_event(struct perf_session *session,
/*
* For all kernel events we get the sample data
*/
- ret = perf_evlist__parse_sample(session->evlist, event, &sample,
- session->header.needs_swap);
+ ret = perf_evlist__parse_sample(session->evlist, event, &sample);
if (ret)
return ret;
@@ -1369,7 +1458,7 @@ more:
err = 0;
/* do the final flush for ordered samples */
session->ordered_samples.next_flush = ULLONG_MAX;
- flush_sample_queue(session, tool);
+ err = flush_sample_queue(session, tool);
out_err:
perf_session__warn_about_errors(session, tool);
perf_session_free_sample_buffers(session);
@@ -1498,9 +1587,9 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
return NULL;
}
-void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
- struct machine *machine, int print_sym,
- int print_dso, int print_symoffset)
+void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
+ struct perf_sample *sample, struct machine *machine,
+ int print_sym, int print_dso, int print_symoffset)
{
struct addr_location al;
struct callchain_cursor_node *node;
@@ -1514,8 +1603,9 @@ void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
if (symbol_conf.use_callchain && sample->callchain) {
- if (machine__resolve_callchain(machine, al.thread,
- sample->callchain, NULL) != 0) {
+
+ if (machine__resolve_callchain(machine, evsel, al.thread,
+ sample, NULL) != 0) {
if (verbose)
error("Failed to resolve callchain. Skipping\n");
return;
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 1f7ec87db7d7..aab414fbb64b 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -36,9 +36,7 @@ struct perf_session {
struct pevent *pevent;
/*
* FIXME: Need to split this up further, we need global
- * stats + per event stats. 'perf diff' also needs
- * to properly support multiple events in a single
- * perf.data file.
+ * stats + per event stats.
*/
struct hists hists;
int fd;
@@ -129,9 +127,9 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp);
struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
unsigned int type);
-void perf_event__print_ip(union perf_event *event, struct perf_sample *sample,
- struct machine *machine, int print_sym,
- int print_dso, int print_symoffset);
+void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event,
+ struct perf_sample *sample, struct machine *machine,
+ int print_sym, int print_dso, int print_symoffset);
int perf_session__cpu_bitmap(struct perf_session *session,
const char *cpu_list, unsigned long *cpu_bitmap);
diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c
index 0f5a0a496bc4..b5b1b9211960 100644
--- a/tools/perf/util/sort.c
+++ b/tools/perf/util/sort.c
@@ -8,12 +8,11 @@ const char default_sort_order[] = "comm,dso,symbol";
const char *sort_order = default_sort_order;
int sort__need_collapse = 0;
int sort__has_parent = 0;
+int sort__has_sym = 0;
int sort__branch_mode = -1; /* -1 = means not set */
enum sort_type sort__first_dimension;
-char * field_sep;
-
LIST_HEAD(hist_entry__sort_list);
static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
@@ -23,11 +22,11 @@ static int repsep_snprintf(char *bf, size_t size, const char *fmt, ...)
va_start(ap, fmt);
n = vsnprintf(bf, size, fmt, ap);
- if (field_sep && n > 0) {
+ if (symbol_conf.field_sep && n > 0) {
char *sep = bf;
while (1) {
- sep = strchr(sep, *field_sep);
+ sep = strchr(sep, *symbol_conf.field_sep);
if (sep == NULL)
break;
*sep = '.';
@@ -172,7 +171,7 @@ static int hist_entry__dso_snprintf(struct hist_entry *self, char *bf,
static int _hist_entry__sym_snprintf(struct map *map, struct symbol *sym,
u64 ip, char level, char *bf, size_t size,
- unsigned int width __used)
+ unsigned int width __maybe_unused)
{
size_t ret = 0;
@@ -207,7 +206,8 @@ struct sort_entry sort_dso = {
};
static int hist_entry__sym_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width __used)
+ size_t size,
+ unsigned int width __maybe_unused)
{
return _hist_entry__sym_snprintf(self->ms.map, self->ms.sym, self->ip,
self->level, bf, size, width);
@@ -250,7 +250,8 @@ sort__srcline_cmp(struct hist_entry *left, struct hist_entry *right)
}
static int hist_entry__srcline_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width __used)
+ size_t size,
+ unsigned int width __maybe_unused)
{
FILE *fp;
char cmd[PATH_MAX + 2], *path = self->srcline, *nl;
@@ -399,7 +400,8 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right)
}
static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width __used)
+ size_t size,
+ unsigned int width __maybe_unused)
{
struct addr_map_symbol *from = &self->branch_info->from;
return _hist_entry__sym_snprintf(from->map, from->sym, from->addr,
@@ -408,7 +410,8 @@ static int hist_entry__sym_from_snprintf(struct hist_entry *self, char *bf,
}
static int hist_entry__sym_to_snprintf(struct hist_entry *self, char *bf,
- size_t size, unsigned int width __used)
+ size_t size,
+ unsigned int width __maybe_unused)
{
struct addr_map_symbol *to = &self->branch_info->to;
return _hist_entry__sym_snprintf(to->map, to->sym, to->addr,
@@ -509,6 +512,10 @@ int sort_dimension__add(const char *tok)
return -EINVAL;
}
sort__has_parent = 1;
+ } else if (sd->entry == &sort_sym ||
+ sd->entry == &sort_sym_from ||
+ sd->entry == &sort_sym_to) {
+ sort__has_sym = 1;
}
if (sd->taken)
diff --git a/tools/perf/util/sort.h b/tools/perf/util/sort.h
index e724b26acd51..12d634792de5 100644
--- a/tools/perf/util/sort.h
+++ b/tools/perf/util/sort.h
@@ -31,8 +31,8 @@ extern const char *parent_pattern;
extern const char default_sort_order[];
extern int sort__need_collapse;
extern int sort__has_parent;
+extern int sort__has_sym;
extern int sort__branch_mode;
-extern char *field_sep;
extern struct sort_entry sort_comm;
extern struct sort_entry sort_dso;
extern struct sort_entry sort_sym;
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
new file mode 100644
index 000000000000..23742126f47c
--- /dev/null
+++ b/tools/perf/util/stat.c
@@ -0,0 +1,57 @@
+#include <math.h>
+
+#include "stat.h"
+
+void update_stats(struct stats *stats, u64 val)
+{
+ double delta;
+
+ stats->n++;
+ delta = val - stats->mean;
+ stats->mean += delta / stats->n;
+ stats->M2 += delta*(val - stats->mean);
+}
+
+double avg_stats(struct stats *stats)
+{
+ return stats->mean;
+}
+
+/*
+ * http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance
+ *
+ * (\Sum n_i^2) - ((\Sum n_i)^2)/n
+ * s^2 = -------------------------------
+ * n - 1
+ *
+ * http://en.wikipedia.org/wiki/Stddev
+ *
+ * The std dev of the mean is related to the std dev by:
+ *
+ * s
+ * s_mean = -------
+ * sqrt(n)
+ *
+ */
+double stddev_stats(struct stats *stats)
+{
+ double variance, variance_mean;
+
+ if (!stats->n)
+ return 0.0;
+
+ variance = stats->M2 / (stats->n - 1);
+ variance_mean = variance / stats->n;
+
+ return sqrt(variance_mean);
+}
+
+double rel_stddev_stats(double stddev, double avg)
+{
+ double pct = 0.0;
+
+ if (avg)
+ pct = 100.0 * stddev/avg;
+
+ return pct;
+}
diff --git a/tools/perf/util/stat.h b/tools/perf/util/stat.h
new file mode 100644
index 000000000000..588367c3c767
--- /dev/null
+++ b/tools/perf/util/stat.h
@@ -0,0 +1,16 @@
+#ifndef __PERF_STATS_H
+#define __PERF_STATS_H
+
+#include "types.h"
+
+struct stats
+{
+ double n, mean, M2;
+};
+
+void update_stats(struct stats *stats, u64 val);
+double avg_stats(struct stats *stats);
+double stddev_stats(struct stats *stats);
+double rel_stddev_stats(double stddev, double avg);
+
+#endif
diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c
index 199bc4d8905d..32170590892d 100644
--- a/tools/perf/util/string.c
+++ b/tools/perf/util/string.c
@@ -1,5 +1,5 @@
#include "util.h"
-#include "string.h"
+#include "linux/string.h"
#define K 1024LL
/*
@@ -335,3 +335,19 @@ char *rtrim(char *s)
return s;
}
+
+/**
+ * memdup - duplicate region of memory
+ * @src: memory region to duplicate
+ * @len: memory region length
+ */
+void *memdup(const void *src, size_t len)
+{
+ void *p;
+
+ p = malloc(len);
+ if (p)
+ memcpy(p, src, len);
+
+ return p;
+}
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c
index 95856ff3dda4..155d8b7078a7 100644
--- a/tools/perf/util/strlist.c
+++ b/tools/perf/util/strlist.c
@@ -93,7 +93,7 @@ out:
void strlist__remove(struct strlist *slist, struct str_node *snode)
{
- str_node__delete(snode, slist->dupstr);
+ rblist__remove_node(&slist->rblist, &snode->rb_node);
}
struct str_node *strlist__find(struct strlist *slist, const char *entry)
diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c
new file mode 100644
index 000000000000..db0cc92cf2ea
--- /dev/null
+++ b/tools/perf/util/symbol-elf.c
@@ -0,0 +1,841 @@
+#include <libelf.h>
+#include <gelf.h>
+#include <elf.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <inttypes.h>
+
+#include "symbol.h"
+#include "debug.h"
+
+#ifndef NT_GNU_BUILD_ID
+#define NT_GNU_BUILD_ID 3
+#endif
+
+/**
+ * elf_symtab__for_each_symbol - iterate thru all the symbols
+ *
+ * @syms: struct elf_symtab instance to iterate
+ * @idx: uint32_t idx
+ * @sym: GElf_Sym iterator
+ */
+#define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
+ for (idx = 0, gelf_getsym(syms, idx, &sym);\
+ idx < nr_syms; \
+ idx++, gelf_getsym(syms, idx, &sym))
+
+static inline uint8_t elf_sym__type(const GElf_Sym *sym)
+{
+ return GELF_ST_TYPE(sym->st_info);
+}
+
+static inline int elf_sym__is_function(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_FUNC &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF;
+}
+
+static inline bool elf_sym__is_object(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_OBJECT &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF;
+}
+
+static inline int elf_sym__is_label(const GElf_Sym *sym)
+{
+ return elf_sym__type(sym) == STT_NOTYPE &&
+ sym->st_name != 0 &&
+ sym->st_shndx != SHN_UNDEF &&
+ sym->st_shndx != SHN_ABS;
+}
+
+static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type)
+{
+ switch (type) {
+ case MAP__FUNCTION:
+ return elf_sym__is_function(sym);
+ case MAP__VARIABLE:
+ return elf_sym__is_object(sym);
+ default:
+ return false;
+ }
+}
+
+static inline const char *elf_sym__name(const GElf_Sym *sym,
+ const Elf_Data *symstrs)
+{
+ return symstrs->d_buf + sym->st_name;
+}
+
+static inline const char *elf_sec__name(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return secstrs->d_buf + shdr->sh_name;
+}
+
+static inline int elf_sec__is_text(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
+}
+
+static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
+ const Elf_Data *secstrs)
+{
+ return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
+}
+
+static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs,
+ enum map_type type)
+{
+ switch (type) {
+ case MAP__FUNCTION:
+ return elf_sec__is_text(shdr, secstrs);
+ case MAP__VARIABLE:
+ return elf_sec__is_data(shdr, secstrs);
+ default:
+ return false;
+ }
+}
+
+static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
+{
+ Elf_Scn *sec = NULL;
+ GElf_Shdr shdr;
+ size_t cnt = 1;
+
+ while ((sec = elf_nextscn(elf, sec)) != NULL) {
+ gelf_getshdr(sec, &shdr);
+
+ if ((addr >= shdr.sh_addr) &&
+ (addr < (shdr.sh_addr + shdr.sh_size)))
+ return cnt;
+
+ ++cnt;
+ }
+
+ return -1;
+}
+
+static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
+ GElf_Shdr *shp, const char *name,
+ size_t *idx)
+{
+ Elf_Scn *sec = NULL;
+ size_t cnt = 1;
+
+ /* Elf is corrupted/truncated, avoid calling elf_strptr. */
+ if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL))
+ return NULL;
+
+ while ((sec = elf_nextscn(elf, sec)) != NULL) {
+ char *str;
+
+ gelf_getshdr(sec, shp);
+ str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
+ if (!strcmp(name, str)) {
+ if (idx)
+ *idx = cnt;
+ break;
+ }
+ ++cnt;
+ }
+
+ return sec;
+}
+
+#define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
+ for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
+ idx < nr_entries; \
+ ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
+
+#define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
+ for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
+ idx < nr_entries; \
+ ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
+
+/*
+ * We need to check if we have a .dynsym, so that we can handle the
+ * .plt, synthesizing its symbols, that aren't on the symtabs (be it
+ * .dynsym or .symtab).
+ * And always look at the original dso, not at debuginfo packages, that
+ * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
+ */
+int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss, struct map *map,
+ symbol_filter_t filter)
+{
+ uint32_t nr_rel_entries, idx;
+ GElf_Sym sym;
+ u64 plt_offset;
+ GElf_Shdr shdr_plt;
+ struct symbol *f;
+ GElf_Shdr shdr_rel_plt, shdr_dynsym;
+ Elf_Data *reldata, *syms, *symstrs;
+ Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
+ size_t dynsym_idx;
+ GElf_Ehdr ehdr;
+ char sympltname[1024];
+ Elf *elf;
+ int nr = 0, symidx, err = 0;
+
+ if (!ss->dynsym)
+ return 0;
+
+ elf = ss->elf;
+ ehdr = ss->ehdr;
+
+ scn_dynsym = ss->dynsym;
+ shdr_dynsym = ss->dynshdr;
+ dynsym_idx = ss->dynsym_idx;
+
+ if (scn_dynsym == NULL)
+ goto out_elf_end;
+
+ scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
+ ".rela.plt", NULL);
+ if (scn_plt_rel == NULL) {
+ scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
+ ".rel.plt", NULL);
+ if (scn_plt_rel == NULL)
+ goto out_elf_end;
+ }
+
+ err = -1;
+
+ if (shdr_rel_plt.sh_link != dynsym_idx)
+ goto out_elf_end;
+
+ if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL)
+ goto out_elf_end;
+
+ /*
+ * Fetch the relocation section to find the idxes to the GOT
+ * and the symbols in the .dynsym they refer to.
+ */
+ reldata = elf_getdata(scn_plt_rel, NULL);
+ if (reldata == NULL)
+ goto out_elf_end;
+
+ syms = elf_getdata(scn_dynsym, NULL);
+ if (syms == NULL)
+ goto out_elf_end;
+
+ scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);
+ if (scn_symstrs == NULL)
+ goto out_elf_end;
+
+ symstrs = elf_getdata(scn_symstrs, NULL);
+ if (symstrs == NULL)
+ goto out_elf_end;
+
+ if (symstrs->d_size == 0)
+ goto out_elf_end;
+
+ nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
+ plt_offset = shdr_plt.sh_offset;
+
+ if (shdr_rel_plt.sh_type == SHT_RELA) {
+ GElf_Rela pos_mem, *pos;
+
+ elf_section__for_each_rela(reldata, pos, pos_mem, idx,
+ nr_rel_entries) {
+ symidx = GELF_R_SYM(pos->r_info);
+ plt_offset += shdr_plt.sh_entsize;
+ gelf_getsym(syms, symidx, &sym);
+ snprintf(sympltname, sizeof(sympltname),
+ "%s@plt", elf_sym__name(&sym, symstrs));
+
+ f = symbol__new(plt_offset, shdr_plt.sh_entsize,
+ STB_GLOBAL, sympltname);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&dso->symbols[map->type], f);
+ ++nr;
+ }
+ }
+ } else if (shdr_rel_plt.sh_type == SHT_REL) {
+ GElf_Rel pos_mem, *pos;
+ elf_section__for_each_rel(reldata, pos, pos_mem, idx,
+ nr_rel_entries) {
+ symidx = GELF_R_SYM(pos->r_info);
+ plt_offset += shdr_plt.sh_entsize;
+ gelf_getsym(syms, symidx, &sym);
+ snprintf(sympltname, sizeof(sympltname),
+ "%s@plt", elf_sym__name(&sym, symstrs));
+
+ f = symbol__new(plt_offset, shdr_plt.sh_entsize,
+ STB_GLOBAL, sympltname);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&dso->symbols[map->type], f);
+ ++nr;
+ }
+ }
+ }
+
+ err = 0;
+out_elf_end:
+ if (err == 0)
+ return nr;
+ pr_debug("%s: problems reading %s PLT info.\n",
+ __func__, dso->long_name);
+ return 0;
+}
+
+/*
+ * Align offset to 4 bytes as needed for note name and descriptor data.
+ */
+#define NOTE_ALIGN(n) (((n) + 3) & -4U)
+
+static int elf_read_build_id(Elf *elf, void *bf, size_t size)
+{
+ int err = -1;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ Elf_Scn *sec;
+ Elf_Kind ek;
+ void *ptr;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ ek = elf_kind(elf);
+ if (ek != ELF_K_ELF)
+ goto out;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ pr_err("%s: cannot get elf header.\n", __func__);
+ goto out;
+ }
+
+ /*
+ * Check following sections for notes:
+ * '.note.gnu.build-id'
+ * '.notes'
+ * '.note' (VDSO specific)
+ */
+ do {
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".note.gnu.build-id", NULL);
+ if (sec)
+ break;
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".notes", NULL);
+ if (sec)
+ break;
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".note", NULL);
+ if (sec)
+ break;
+
+ return err;
+
+ } while (0);
+
+ data = elf_getdata(sec, NULL);
+ if (data == NULL)
+ goto out;
+
+ ptr = data->d_buf;
+ while (ptr < (data->d_buf + data->d_size)) {
+ GElf_Nhdr *nhdr = ptr;
+ size_t namesz = NOTE_ALIGN(nhdr->n_namesz),
+ descsz = NOTE_ALIGN(nhdr->n_descsz);
+ const char *name;
+
+ ptr += sizeof(*nhdr);
+ name = ptr;
+ ptr += namesz;
+ if (nhdr->n_type == NT_GNU_BUILD_ID &&
+ nhdr->n_namesz == sizeof("GNU")) {
+ if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
+ size_t sz = min(size, descsz);
+ memcpy(bf, ptr, sz);
+ memset(bf + sz, 0, size - sz);
+ err = descsz;
+ break;
+ }
+ }
+ ptr += descsz;
+ }
+
+out:
+ return err;
+}
+
+int filename__read_build_id(const char *filename, void *bf, size_t size)
+{
+ int fd, err = -1;
+ Elf *elf;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
+ goto out_close;
+ }
+
+ err = elf_read_build_id(elf, bf, size);
+
+ elf_end(elf);
+out_close:
+ close(fd);
+out:
+ return err;
+}
+
+int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
+{
+ int fd, err = -1;
+
+ if (size < BUILD_ID_SIZE)
+ goto out;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ while (1) {
+ char bf[BUFSIZ];
+ GElf_Nhdr nhdr;
+ size_t namesz, descsz;
+
+ if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
+ break;
+
+ namesz = NOTE_ALIGN(nhdr.n_namesz);
+ descsz = NOTE_ALIGN(nhdr.n_descsz);
+ if (nhdr.n_type == NT_GNU_BUILD_ID &&
+ nhdr.n_namesz == sizeof("GNU")) {
+ if (read(fd, bf, namesz) != (ssize_t)namesz)
+ break;
+ if (memcmp(bf, "GNU", sizeof("GNU")) == 0) {
+ size_t sz = min(descsz, size);
+ if (read(fd, build_id, sz) == (ssize_t)sz) {
+ memset(build_id + sz, 0, size - sz);
+ err = 0;
+ break;
+ }
+ } else if (read(fd, bf, descsz) != (ssize_t)descsz)
+ break;
+ } else {
+ int n = namesz + descsz;
+ if (read(fd, bf, n) != n)
+ break;
+ }
+ }
+ close(fd);
+out:
+ return err;
+}
+
+int filename__read_debuglink(const char *filename, char *debuglink,
+ size_t size)
+{
+ int fd, err = -1;
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ Elf_Scn *sec;
+ Elf_Kind ek;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ goto out;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
+ goto out_close;
+ }
+
+ ek = elf_kind(elf);
+ if (ek != ELF_K_ELF)
+ goto out_close;
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ pr_err("%s: cannot get elf header.\n", __func__);
+ goto out_close;
+ }
+
+ sec = elf_section_by_name(elf, &ehdr, &shdr,
+ ".gnu_debuglink", NULL);
+ if (sec == NULL)
+ goto out_close;
+
+ data = elf_getdata(sec, NULL);
+ if (data == NULL)
+ goto out_close;
+
+ /* the start of this section is a zero-terminated string */
+ strncpy(debuglink, data->d_buf, size);
+
+ elf_end(elf);
+
+out_close:
+ close(fd);
+out:
+ return err;
+}
+
+static int dso__swap_init(struct dso *dso, unsigned char eidata)
+{
+ static unsigned int const endian = 1;
+
+ dso->needs_swap = DSO_SWAP__NO;
+
+ switch (eidata) {
+ case ELFDATA2LSB:
+ /* We are big endian, DSO is little endian. */
+ if (*(unsigned char const *)&endian != 1)
+ dso->needs_swap = DSO_SWAP__YES;
+ break;
+
+ case ELFDATA2MSB:
+ /* We are little endian, DSO is big endian. */
+ if (*(unsigned char const *)&endian != 0)
+ dso->needs_swap = DSO_SWAP__YES;
+ break;
+
+ default:
+ pr_err("unrecognized DSO data encoding %d\n", eidata);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+bool symsrc__possibly_runtime(struct symsrc *ss)
+{
+ return ss->dynsym || ss->opdsec;
+}
+
+bool symsrc__has_symtab(struct symsrc *ss)
+{
+ return ss->symtab != NULL;
+}
+
+void symsrc__destroy(struct symsrc *ss)
+{
+ free(ss->name);
+ elf_end(ss->elf);
+ close(ss->fd);
+}
+
+int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
+ enum dso_binary_type type)
+{
+ int err = -1;
+ GElf_Ehdr ehdr;
+ Elf *elf;
+ int fd;
+
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL) {
+ pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
+ goto out_close;
+ }
+
+ if (gelf_getehdr(elf, &ehdr) == NULL) {
+ pr_debug("%s: cannot get elf header.\n", __func__);
+ goto out_elf_end;
+ }
+
+ if (dso__swap_init(dso, ehdr.e_ident[EI_DATA]))
+ goto out_elf_end;
+
+ /* Always reject images with a mismatched build-id: */
+ if (dso->has_build_id) {
+ u8 build_id[BUILD_ID_SIZE];
+
+ if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0)
+ goto out_elf_end;
+
+ if (!dso__build_id_equal(dso, build_id))
+ goto out_elf_end;
+ }
+
+ ss->symtab = elf_section_by_name(elf, &ehdr, &ss->symshdr, ".symtab",
+ NULL);
+ if (ss->symshdr.sh_type != SHT_SYMTAB)
+ ss->symtab = NULL;
+
+ ss->dynsym_idx = 0;
+ ss->dynsym = elf_section_by_name(elf, &ehdr, &ss->dynshdr, ".dynsym",
+ &ss->dynsym_idx);
+ if (ss->dynshdr.sh_type != SHT_DYNSYM)
+ ss->dynsym = NULL;
+
+ ss->opdidx = 0;
+ ss->opdsec = elf_section_by_name(elf, &ehdr, &ss->opdshdr, ".opd",
+ &ss->opdidx);
+ if (ss->opdshdr.sh_type != SHT_PROGBITS)
+ ss->opdsec = NULL;
+
+ if (dso->kernel == DSO_TYPE_USER) {
+ GElf_Shdr shdr;
+ ss->adjust_symbols = (ehdr.e_type == ET_EXEC ||
+ elf_section_by_name(elf, &ehdr, &shdr,
+ ".gnu.prelink_undo",
+ NULL) != NULL);
+ } else {
+ ss->adjust_symbols = 0;
+ }
+
+ ss->name = strdup(name);
+ if (!ss->name)
+ goto out_elf_end;
+
+ ss->elf = elf;
+ ss->fd = fd;
+ ss->ehdr = ehdr;
+ ss->type = type;
+
+ return 0;
+
+out_elf_end:
+ elf_end(elf);
+out_close:
+ close(fd);
+ return err;
+}
+
+int dso__load_sym(struct dso *dso, struct map *map,
+ struct symsrc *syms_ss, struct symsrc *runtime_ss,
+ symbol_filter_t filter, int kmodule)
+{
+ struct kmap *kmap = dso->kernel ? map__kmap(map) : NULL;
+ struct map *curr_map = map;
+ struct dso *curr_dso = dso;
+ Elf_Data *symstrs, *secstrs;
+ uint32_t nr_syms;
+ int err = -1;
+ uint32_t idx;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ Elf_Data *syms, *opddata = NULL;
+ GElf_Sym sym;
+ Elf_Scn *sec, *sec_strndx;
+ Elf *elf;
+ int nr = 0;
+
+ dso->symtab_type = syms_ss->type;
+
+ if (!syms_ss->symtab) {
+ syms_ss->symtab = syms_ss->dynsym;
+ syms_ss->symshdr = syms_ss->dynshdr;
+ }
+
+ elf = syms_ss->elf;
+ ehdr = syms_ss->ehdr;
+ sec = syms_ss->symtab;
+ shdr = syms_ss->symshdr;
+
+ if (runtime_ss->opdsec)
+ opddata = elf_rawdata(runtime_ss->opdsec, NULL);
+
+ syms = elf_getdata(sec, NULL);
+ if (syms == NULL)
+ goto out_elf_end;
+
+ sec = elf_getscn(elf, shdr.sh_link);
+ if (sec == NULL)
+ goto out_elf_end;
+
+ symstrs = elf_getdata(sec, NULL);
+ if (symstrs == NULL)
+ goto out_elf_end;
+
+ sec_strndx = elf_getscn(elf, ehdr.e_shstrndx);
+ if (sec_strndx == NULL)
+ goto out_elf_end;
+
+ secstrs = elf_getdata(sec_strndx, NULL);
+ if (secstrs == NULL)
+ goto out_elf_end;
+
+ nr_syms = shdr.sh_size / shdr.sh_entsize;
+
+ memset(&sym, 0, sizeof(sym));
+ dso->adjust_symbols = runtime_ss->adjust_symbols;
+ elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
+ struct symbol *f;
+ const char *elf_name = elf_sym__name(&sym, symstrs);
+ char *demangled = NULL;
+ int is_label = elf_sym__is_label(&sym);
+ const char *section_name;
+ bool used_opd = false;
+
+ if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name &&
+ strcmp(elf_name, kmap->ref_reloc_sym->name) == 0)
+ kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
+
+ if (!is_label && !elf_sym__is_a(&sym, map->type))
+ continue;
+
+ /* Reject ARM ELF "mapping symbols": these aren't unique and
+ * don't identify functions, so will confuse the profile
+ * output: */
+ if (ehdr.e_machine == EM_ARM) {
+ if (!strcmp(elf_name, "$a") ||
+ !strcmp(elf_name, "$d") ||
+ !strcmp(elf_name, "$t"))
+ continue;
+ }
+
+ if (runtime_ss->opdsec && sym.st_shndx == runtime_ss->opdidx) {
+ u32 offset = sym.st_value - syms_ss->opdshdr.sh_addr;
+ u64 *opd = opddata->d_buf + offset;
+ sym.st_value = DSO__SWAP(dso, u64, *opd);
+ sym.st_shndx = elf_addr_to_index(runtime_ss->elf,
+ sym.st_value);
+ used_opd = true;
+ }
+
+ sec = elf_getscn(runtime_ss->elf, sym.st_shndx);
+ if (!sec)
+ goto out_elf_end;
+
+ gelf_getshdr(sec, &shdr);
+
+ if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))
+ continue;
+
+ section_name = elf_sec__name(&shdr, secstrs);
+
+ /* On ARM, symbols for thumb functions have 1 added to
+ * the symbol address as a flag - remove it */
+ if ((ehdr.e_machine == EM_ARM) &&
+ (map->type == MAP__FUNCTION) &&
+ (sym.st_value & 1))
+ --sym.st_value;
+
+ if (dso->kernel != DSO_TYPE_USER || kmodule) {
+ char dso_name[PATH_MAX];
+
+ if (strcmp(section_name,
+ (curr_dso->short_name +
+ dso->short_name_len)) == 0)
+ goto new_symbol;
+
+ if (strcmp(section_name, ".text") == 0) {
+ curr_map = map;
+ curr_dso = dso;
+ goto new_symbol;
+ }
+
+ snprintf(dso_name, sizeof(dso_name),
+ "%s%s", dso->short_name, section_name);
+
+ curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name);
+ if (curr_map == NULL) {
+ u64 start = sym.st_value;
+
+ if (kmodule)
+ start += map->start + shdr.sh_offset;
+
+ curr_dso = dso__new(dso_name);
+ if (curr_dso == NULL)
+ goto out_elf_end;
+ curr_dso->kernel = dso->kernel;
+ curr_dso->long_name = dso->long_name;
+ curr_dso->long_name_len = dso->long_name_len;
+ curr_map = map__new2(start, curr_dso,
+ map->type);
+ if (curr_map == NULL) {
+ dso__delete(curr_dso);
+ goto out_elf_end;
+ }
+ curr_map->map_ip = identity__map_ip;
+ curr_map->unmap_ip = identity__map_ip;
+ curr_dso->symtab_type = dso->symtab_type;
+ map_groups__insert(kmap->kmaps, curr_map);
+ dsos__add(&dso->node, curr_dso);
+ dso__set_loaded(curr_dso, map->type);
+ } else
+ curr_dso = curr_map->dso;
+
+ goto new_symbol;
+ }
+
+ if ((used_opd && runtime_ss->adjust_symbols)
+ || (!used_opd && syms_ss->adjust_symbols)) {
+ pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
+ "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__,
+ (u64)sym.st_value, (u64)shdr.sh_addr,
+ (u64)shdr.sh_offset);
+ sym.st_value -= shdr.sh_addr - shdr.sh_offset;
+ }
+ /*
+ * We need to figure out if the object was created from C++ sources
+ * DWARF DW_compile_unit has this, but we don't always have access
+ * to it...
+ */
+ demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI);
+ if (demangled != NULL)
+ elf_name = demangled;
+new_symbol:
+ f = symbol__new(sym.st_value, sym.st_size,
+ GELF_ST_BIND(sym.st_info), elf_name);
+ free(demangled);
+ if (!f)
+ goto out_elf_end;
+
+ if (filter && filter(curr_map, f))
+ symbol__delete(f);
+ else {
+ symbols__insert(&curr_dso->symbols[curr_map->type], f);
+ nr++;
+ }
+ }
+
+ /*
+ * For misannotated, zeroed, ASM function sizes.
+ */
+ if (nr > 0) {
+ symbols__fixup_duplicate(&dso->symbols[map->type]);
+ symbols__fixup_end(&dso->symbols[map->type]);
+ if (kmap) {
+ /*
+ * We need to fixup this here too because we create new
+ * maps here, for things like vsyscall sections.
+ */
+ __map_groups__fixup_end(kmap->kmaps, map->type);
+ }
+ }
+ err = nr;
+out_elf_end:
+ return err;
+}
+
+void symbol__elf_init(void)
+{
+ elf_version(EV_CURRENT);
+}
diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c
new file mode 100644
index 000000000000..259f8f2ea9c9
--- /dev/null
+++ b/tools/perf/util/symbol-minimal.c
@@ -0,0 +1,307 @@
+#include "symbol.h"
+
+#include <elf.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <byteswap.h>
+#include <sys/stat.h>
+
+
+static bool check_need_swap(int file_endian)
+{
+ const int data = 1;
+ u8 *check = (u8 *)&data;
+ int host_endian;
+
+ if (check[0] == 1)
+ host_endian = ELFDATA2LSB;
+ else
+ host_endian = ELFDATA2MSB;
+
+ return host_endian != file_endian;
+}
+
+#define NOTE_ALIGN(sz) (((sz) + 3) & ~3)
+
+#define NT_GNU_BUILD_ID 3
+
+static int read_build_id(void *note_data, size_t note_len, void *bf,
+ size_t size, bool need_swap)
+{
+ struct {
+ u32 n_namesz;
+ u32 n_descsz;
+ u32 n_type;
+ } *nhdr;
+ void *ptr;
+
+ ptr = note_data;
+ while (ptr < (note_data + note_len)) {
+ const char *name;
+ size_t namesz, descsz;
+
+ nhdr = ptr;
+ if (need_swap) {
+ nhdr->n_namesz = bswap_32(nhdr->n_namesz);
+ nhdr->n_descsz = bswap_32(nhdr->n_descsz);
+ nhdr->n_type = bswap_32(nhdr->n_type);
+ }
+
+ namesz = NOTE_ALIGN(nhdr->n_namesz);
+ descsz = NOTE_ALIGN(nhdr->n_descsz);
+
+ ptr += sizeof(*nhdr);
+ name = ptr;
+ ptr += namesz;
+ if (nhdr->n_type == NT_GNU_BUILD_ID &&
+ nhdr->n_namesz == sizeof("GNU")) {
+ if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
+ size_t sz = min(size, descsz);
+ memcpy(bf, ptr, sz);
+ memset(bf + sz, 0, size - sz);
+ return 0;
+ }
+ }
+ ptr += descsz;
+ }
+
+ return -1;
+}
+
+int filename__read_debuglink(const char *filename __maybe_unused,
+ char *debuglink __maybe_unused,
+ size_t size __maybe_unused)
+{
+ return -1;
+}
+
+/*
+ * Just try PT_NOTE header otherwise fails
+ */
+int filename__read_build_id(const char *filename, void *bf, size_t size)
+{
+ FILE *fp;
+ int ret = -1;
+ bool need_swap = false;
+ u8 e_ident[EI_NIDENT];
+ size_t buf_size;
+ void *buf;
+ int i;
+
+ fp = fopen(filename, "r");
+ if (fp == NULL)
+ return -1;
+
+ if (fread(e_ident, sizeof(e_ident), 1, fp) != 1)
+ goto out;
+
+ if (memcmp(e_ident, ELFMAG, SELFMAG) ||
+ e_ident[EI_VERSION] != EV_CURRENT)
+ goto out;
+
+ need_swap = check_need_swap(e_ident[EI_DATA]);
+
+ /* for simplicity */
+ fseek(fp, 0, SEEK_SET);
+
+ if (e_ident[EI_CLASS] == ELFCLASS32) {
+ Elf32_Ehdr ehdr;
+ Elf32_Phdr *phdr;
+
+ if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1)
+ goto out;
+
+ if (need_swap) {
+ ehdr.e_phoff = bswap_32(ehdr.e_phoff);
+ ehdr.e_phentsize = bswap_16(ehdr.e_phentsize);
+ ehdr.e_phnum = bswap_16(ehdr.e_phnum);
+ }
+
+ buf_size = ehdr.e_phentsize * ehdr.e_phnum;
+ buf = malloc(buf_size);
+ if (buf == NULL)
+ goto out;
+
+ fseek(fp, ehdr.e_phoff, SEEK_SET);
+ if (fread(buf, buf_size, 1, fp) != 1)
+ goto out_free;
+
+ for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) {
+ void *tmp;
+
+ if (need_swap) {
+ phdr->p_type = bswap_32(phdr->p_type);
+ phdr->p_offset = bswap_32(phdr->p_offset);
+ phdr->p_filesz = bswap_32(phdr->p_filesz);
+ }
+
+ if (phdr->p_type != PT_NOTE)
+ continue;
+
+ buf_size = phdr->p_filesz;
+ tmp = realloc(buf, buf_size);
+ if (tmp == NULL)
+ goto out_free;
+
+ buf = tmp;
+ fseek(fp, phdr->p_offset, SEEK_SET);
+ if (fread(buf, buf_size, 1, fp) != 1)
+ goto out_free;
+
+ ret = read_build_id(buf, buf_size, bf, size, need_swap);
+ if (ret == 0)
+ ret = size;
+ break;
+ }
+ } else {
+ Elf64_Ehdr ehdr;
+ Elf64_Phdr *phdr;
+
+ if (fread(&ehdr, sizeof(ehdr), 1, fp) != 1)
+ goto out;
+
+ if (need_swap) {
+ ehdr.e_phoff = bswap_64(ehdr.e_phoff);
+ ehdr.e_phentsize = bswap_16(ehdr.e_phentsize);
+ ehdr.e_phnum = bswap_16(ehdr.e_phnum);
+ }
+
+ buf_size = ehdr.e_phentsize * ehdr.e_phnum;
+ buf = malloc(buf_size);
+ if (buf == NULL)
+ goto out;
+
+ fseek(fp, ehdr.e_phoff, SEEK_SET);
+ if (fread(buf, buf_size, 1, fp) != 1)
+ goto out_free;
+
+ for (i = 0, phdr = buf; i < ehdr.e_phnum; i++, phdr++) {
+ void *tmp;
+
+ if (need_swap) {
+ phdr->p_type = bswap_32(phdr->p_type);
+ phdr->p_offset = bswap_64(phdr->p_offset);
+ phdr->p_filesz = bswap_64(phdr->p_filesz);
+ }
+
+ if (phdr->p_type != PT_NOTE)
+ continue;
+
+ buf_size = phdr->p_filesz;
+ tmp = realloc(buf, buf_size);
+ if (tmp == NULL)
+ goto out_free;
+
+ buf = tmp;
+ fseek(fp, phdr->p_offset, SEEK_SET);
+ if (fread(buf, buf_size, 1, fp) != 1)
+ goto out_free;
+
+ ret = read_build_id(buf, buf_size, bf, size, need_swap);
+ if (ret == 0)
+ ret = size;
+ break;
+ }
+ }
+out_free:
+ free(buf);
+out:
+ fclose(fp);
+ return ret;
+}
+
+int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
+{
+ int fd;
+ int ret = -1;
+ struct stat stbuf;
+ size_t buf_size;
+ void *buf;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ if (fstat(fd, &stbuf) < 0)
+ goto out;
+
+ buf_size = stbuf.st_size;
+ buf = malloc(buf_size);
+ if (buf == NULL)
+ goto out;
+
+ if (read(fd, buf, buf_size) != (ssize_t) buf_size)
+ goto out_free;
+
+ ret = read_build_id(buf, buf_size, build_id, size, false);
+out_free:
+ free(buf);
+out:
+ close(fd);
+ return ret;
+}
+
+int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused,
+ const char *name,
+ enum dso_binary_type type)
+{
+ int fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ ss->name = strdup(name);
+ if (!ss->name)
+ goto out_close;
+
+ ss->type = type;
+
+ return 0;
+out_close:
+ close(fd);
+ return -1;
+}
+
+bool symsrc__possibly_runtime(struct symsrc *ss __maybe_unused)
+{
+ /* Assume all sym sources could be a runtime image. */
+ return true;
+}
+
+bool symsrc__has_symtab(struct symsrc *ss __maybe_unused)
+{
+ return false;
+}
+
+void symsrc__destroy(struct symsrc *ss)
+{
+ free(ss->name);
+ close(ss->fd);
+}
+
+int dso__synthesize_plt_symbols(struct dso *dso __maybe_unused,
+ struct symsrc *ss __maybe_unused,
+ struct map *map __maybe_unused,
+ symbol_filter_t filter __maybe_unused)
+{
+ return 0;
+}
+
+int dso__load_sym(struct dso *dso, struct map *map __maybe_unused,
+ struct symsrc *ss,
+ struct symsrc *runtime_ss __maybe_unused,
+ symbol_filter_t filter __maybe_unused,
+ int kmodule __maybe_unused)
+{
+ unsigned char *build_id[BUILD_ID_SIZE];
+
+ if (filename__read_build_id(ss->name, build_id, BUILD_ID_SIZE) > 0) {
+ dso__set_build_id(dso, build_id);
+ return 1;
+ }
+ return 0;
+}
+
+void symbol__elf_init(void)
+{
+}
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 8b63b678e127..e2e8c697cffe 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -15,8 +15,6 @@
#include "symbol.h"
#include "strlist.h"
-#include <libelf.h>
-#include <gelf.h>
#include <elf.h>
#include <limits.h>
#include <sys/utsname.h>
@@ -25,15 +23,7 @@
#define KSYM_NAME_LEN 256
#endif
-#ifndef NT_GNU_BUILD_ID
-#define NT_GNU_BUILD_ID 3
-#endif
-
static void dso_cache__free(struct rb_root *root);
-static bool dso__build_id_equal(const struct dso *dso, u8 *build_id);
-static int elf_read_build_id(Elf *elf, void *bf, size_t size);
-static void dsos__add(struct list_head *head, struct dso *dso);
-static struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
static int dso__load_kernel_sym(struct dso *dso, struct map *map,
symbol_filter_t filter);
static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map,
@@ -170,7 +160,7 @@ static int choose_best_symbol(struct symbol *syma, struct symbol *symb)
return SYMBOL_B;
}
-static void symbols__fixup_duplicate(struct rb_root *symbols)
+void symbols__fixup_duplicate(struct rb_root *symbols)
{
struct rb_node *nd;
struct symbol *curr, *next;
@@ -199,7 +189,7 @@ again:
}
}
-static void symbols__fixup_end(struct rb_root *symbols)
+void symbols__fixup_end(struct rb_root *symbols)
{
struct rb_node *nd, *prevnd = rb_first(symbols);
struct symbol *curr, *prev;
@@ -222,7 +212,7 @@ static void symbols__fixup_end(struct rb_root *symbols)
curr->end = roundup(curr->start, 4096);
}
-static void __map_groups__fixup_end(struct map_groups *mg, enum map_type type)
+void __map_groups__fixup_end(struct map_groups *mg, enum map_type type)
{
struct map *prev, *curr;
struct rb_node *nd, *prevnd = rb_first(&mg->maps[type]);
@@ -252,8 +242,7 @@ static void map_groups__fixup_end(struct map_groups *mg)
__map_groups__fixup_end(mg, i);
}
-static struct symbol *symbol__new(u64 start, u64 len, u8 binding,
- const char *name)
+struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name)
{
size_t namelen = strlen(name) + 1;
struct symbol *sym = calloc(1, (symbol_conf.priv_size +
@@ -390,7 +379,7 @@ void dso__set_build_id(struct dso *dso, void *build_id)
dso->has_build_id = 1;
}
-static void symbols__insert(struct rb_root *symbols, struct symbol *sym)
+void symbols__insert(struct rb_root *symbols, struct symbol *sym)
{
struct rb_node **p = &symbols->rb_node;
struct rb_node *parent = NULL;
@@ -574,7 +563,7 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp)
int kallsyms__parse(const char *filename, void *arg,
int (*process_symbol)(void *arg, const char *name,
- char type, u64 start, u64 end))
+ char type, u64 start))
{
char *line = NULL;
size_t n;
@@ -614,13 +603,8 @@ int kallsyms__parse(const char *filename, void *arg,
break;
}
- /*
- * module symbols are not sorted so we add all
- * symbols with zero length and rely on
- * symbols__fixup_end() to fix it up.
- */
err = process_symbol(arg, symbol_name,
- symbol_type, start, start);
+ symbol_type, start);
if (err)
break;
}
@@ -647,7 +631,7 @@ static u8 kallsyms2elf_type(char type)
}
static int map__process_kallsym_symbol(void *arg, const char *name,
- char type, u64 start, u64 end)
+ char type, u64 start)
{
struct symbol *sym;
struct process_kallsyms_args *a = arg;
@@ -656,8 +640,12 @@ static int map__process_kallsym_symbol(void *arg, const char *name,
if (!symbol_type__is_a(type, a->map->type))
return 0;
- sym = symbol__new(start, end - start + 1,
- kallsyms2elf_type(type), name);
+ /*
+ * module symbols are not sorted so we add all
+ * symbols, setting length to 0, and rely on
+ * symbols__fixup_end() to fix it up.
+ */
+ sym = symbol__new(start, 0, kallsyms2elf_type(type), name);
if (sym == NULL)
return -ENOMEM;
/*
@@ -904,556 +892,7 @@ out_failure:
return -1;
}
-/**
- * elf_symtab__for_each_symbol - iterate thru all the symbols
- *
- * @syms: struct elf_symtab instance to iterate
- * @idx: uint32_t idx
- * @sym: GElf_Sym iterator
- */
-#define elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) \
- for (idx = 0, gelf_getsym(syms, idx, &sym);\
- idx < nr_syms; \
- idx++, gelf_getsym(syms, idx, &sym))
-
-static inline uint8_t elf_sym__type(const GElf_Sym *sym)
-{
- return GELF_ST_TYPE(sym->st_info);
-}
-
-static inline int elf_sym__is_function(const GElf_Sym *sym)
-{
- return elf_sym__type(sym) == STT_FUNC &&
- sym->st_name != 0 &&
- sym->st_shndx != SHN_UNDEF;
-}
-
-static inline bool elf_sym__is_object(const GElf_Sym *sym)
-{
- return elf_sym__type(sym) == STT_OBJECT &&
- sym->st_name != 0 &&
- sym->st_shndx != SHN_UNDEF;
-}
-
-static inline int elf_sym__is_label(const GElf_Sym *sym)
-{
- return elf_sym__type(sym) == STT_NOTYPE &&
- sym->st_name != 0 &&
- sym->st_shndx != SHN_UNDEF &&
- sym->st_shndx != SHN_ABS;
-}
-
-static inline const char *elf_sec__name(const GElf_Shdr *shdr,
- const Elf_Data *secstrs)
-{
- return secstrs->d_buf + shdr->sh_name;
-}
-
-static inline int elf_sec__is_text(const GElf_Shdr *shdr,
- const Elf_Data *secstrs)
-{
- return strstr(elf_sec__name(shdr, secstrs), "text") != NULL;
-}
-
-static inline bool elf_sec__is_data(const GElf_Shdr *shdr,
- const Elf_Data *secstrs)
-{
- return strstr(elf_sec__name(shdr, secstrs), "data") != NULL;
-}
-
-static inline const char *elf_sym__name(const GElf_Sym *sym,
- const Elf_Data *symstrs)
-{
- return symstrs->d_buf + sym->st_name;
-}
-
-static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
- GElf_Shdr *shp, const char *name,
- size_t *idx)
-{
- Elf_Scn *sec = NULL;
- size_t cnt = 1;
-
- while ((sec = elf_nextscn(elf, sec)) != NULL) {
- char *str;
-
- gelf_getshdr(sec, shp);
- str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
- if (!strcmp(name, str)) {
- if (idx)
- *idx = cnt;
- break;
- }
- ++cnt;
- }
-
- return sec;
-}
-
-#define elf_section__for_each_rel(reldata, pos, pos_mem, idx, nr_entries) \
- for (idx = 0, pos = gelf_getrel(reldata, 0, &pos_mem); \
- idx < nr_entries; \
- ++idx, pos = gelf_getrel(reldata, idx, &pos_mem))
-
-#define elf_section__for_each_rela(reldata, pos, pos_mem, idx, nr_entries) \
- for (idx = 0, pos = gelf_getrela(reldata, 0, &pos_mem); \
- idx < nr_entries; \
- ++idx, pos = gelf_getrela(reldata, idx, &pos_mem))
-
-/*
- * We need to check if we have a .dynsym, so that we can handle the
- * .plt, synthesizing its symbols, that aren't on the symtabs (be it
- * .dynsym or .symtab).
- * And always look at the original dso, not at debuginfo packages, that
- * have the PLT data stripped out (shdr_rel_plt.sh_type == SHT_NOBITS).
- */
-static int
-dso__synthesize_plt_symbols(struct dso *dso, char *name, struct map *map,
- symbol_filter_t filter)
-{
- uint32_t nr_rel_entries, idx;
- GElf_Sym sym;
- u64 plt_offset;
- GElf_Shdr shdr_plt;
- struct symbol *f;
- GElf_Shdr shdr_rel_plt, shdr_dynsym;
- Elf_Data *reldata, *syms, *symstrs;
- Elf_Scn *scn_plt_rel, *scn_symstrs, *scn_dynsym;
- size_t dynsym_idx;
- GElf_Ehdr ehdr;
- char sympltname[1024];
- Elf *elf;
- int nr = 0, symidx, fd, err = 0;
-
- fd = open(name, O_RDONLY);
- if (fd < 0)
- goto out;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL)
- goto out_close;
-
- if (gelf_getehdr(elf, &ehdr) == NULL)
- goto out_elf_end;
-
- scn_dynsym = elf_section_by_name(elf, &ehdr, &shdr_dynsym,
- ".dynsym", &dynsym_idx);
- if (scn_dynsym == NULL)
- goto out_elf_end;
-
- scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
- ".rela.plt", NULL);
- if (scn_plt_rel == NULL) {
- scn_plt_rel = elf_section_by_name(elf, &ehdr, &shdr_rel_plt,
- ".rel.plt", NULL);
- if (scn_plt_rel == NULL)
- goto out_elf_end;
- }
-
- err = -1;
-
- if (shdr_rel_plt.sh_link != dynsym_idx)
- goto out_elf_end;
-
- if (elf_section_by_name(elf, &ehdr, &shdr_plt, ".plt", NULL) == NULL)
- goto out_elf_end;
-
- /*
- * Fetch the relocation section to find the idxes to the GOT
- * and the symbols in the .dynsym they refer to.
- */
- reldata = elf_getdata(scn_plt_rel, NULL);
- if (reldata == NULL)
- goto out_elf_end;
-
- syms = elf_getdata(scn_dynsym, NULL);
- if (syms == NULL)
- goto out_elf_end;
-
- scn_symstrs = elf_getscn(elf, shdr_dynsym.sh_link);
- if (scn_symstrs == NULL)
- goto out_elf_end;
-
- symstrs = elf_getdata(scn_symstrs, NULL);
- if (symstrs == NULL)
- goto out_elf_end;
-
- nr_rel_entries = shdr_rel_plt.sh_size / shdr_rel_plt.sh_entsize;
- plt_offset = shdr_plt.sh_offset;
-
- if (shdr_rel_plt.sh_type == SHT_RELA) {
- GElf_Rela pos_mem, *pos;
-
- elf_section__for_each_rela(reldata, pos, pos_mem, idx,
- nr_rel_entries) {
- symidx = GELF_R_SYM(pos->r_info);
- plt_offset += shdr_plt.sh_entsize;
- gelf_getsym(syms, symidx, &sym);
- snprintf(sympltname, sizeof(sympltname),
- "%s@plt", elf_sym__name(&sym, symstrs));
-
- f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- STB_GLOBAL, sympltname);
- if (!f)
- goto out_elf_end;
-
- if (filter && filter(map, f))
- symbol__delete(f);
- else {
- symbols__insert(&dso->symbols[map->type], f);
- ++nr;
- }
- }
- } else if (shdr_rel_plt.sh_type == SHT_REL) {
- GElf_Rel pos_mem, *pos;
- elf_section__for_each_rel(reldata, pos, pos_mem, idx,
- nr_rel_entries) {
- symidx = GELF_R_SYM(pos->r_info);
- plt_offset += shdr_plt.sh_entsize;
- gelf_getsym(syms, symidx, &sym);
- snprintf(sympltname, sizeof(sympltname),
- "%s@plt", elf_sym__name(&sym, symstrs));
-
- f = symbol__new(plt_offset, shdr_plt.sh_entsize,
- STB_GLOBAL, sympltname);
- if (!f)
- goto out_elf_end;
-
- if (filter && filter(map, f))
- symbol__delete(f);
- else {
- symbols__insert(&dso->symbols[map->type], f);
- ++nr;
- }
- }
- }
-
- err = 0;
-out_elf_end:
- elf_end(elf);
-out_close:
- close(fd);
-
- if (err == 0)
- return nr;
-out:
- pr_debug("%s: problems reading %s PLT info.\n",
- __func__, dso->long_name);
- return 0;
-}
-
-static bool elf_sym__is_a(GElf_Sym *sym, enum map_type type)
-{
- switch (type) {
- case MAP__FUNCTION:
- return elf_sym__is_function(sym);
- case MAP__VARIABLE:
- return elf_sym__is_object(sym);
- default:
- return false;
- }
-}
-
-static bool elf_sec__is_a(GElf_Shdr *shdr, Elf_Data *secstrs,
- enum map_type type)
-{
- switch (type) {
- case MAP__FUNCTION:
- return elf_sec__is_text(shdr, secstrs);
- case MAP__VARIABLE:
- return elf_sec__is_data(shdr, secstrs);
- default:
- return false;
- }
-}
-
-static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr)
-{
- Elf_Scn *sec = NULL;
- GElf_Shdr shdr;
- size_t cnt = 1;
-
- while ((sec = elf_nextscn(elf, sec)) != NULL) {
- gelf_getshdr(sec, &shdr);
-
- if ((addr >= shdr.sh_addr) &&
- (addr < (shdr.sh_addr + shdr.sh_size)))
- return cnt;
-
- ++cnt;
- }
-
- return -1;
-}
-
-static int dso__swap_init(struct dso *dso, unsigned char eidata)
-{
- static unsigned int const endian = 1;
-
- dso->needs_swap = DSO_SWAP__NO;
-
- switch (eidata) {
- case ELFDATA2LSB:
- /* We are big endian, DSO is little endian. */
- if (*(unsigned char const *)&endian != 1)
- dso->needs_swap = DSO_SWAP__YES;
- break;
-
- case ELFDATA2MSB:
- /* We are little endian, DSO is big endian. */
- if (*(unsigned char const *)&endian != 0)
- dso->needs_swap = DSO_SWAP__YES;
- break;
-
- default:
- pr_err("unrecognized DSO data encoding %d\n", eidata);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int dso__load_sym(struct dso *dso, struct map *map, const char *name,
- int fd, symbol_filter_t filter, int kmodule,
- int want_symtab)
-{
- struct kmap *kmap = dso->kernel ? map__kmap(map) : NULL;
- struct map *curr_map = map;
- struct dso *curr_dso = dso;
- Elf_Data *symstrs, *secstrs;
- uint32_t nr_syms;
- int err = -1;
- uint32_t idx;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr, opdshdr;
- Elf_Data *syms, *opddata = NULL;
- GElf_Sym sym;
- Elf_Scn *sec, *sec_strndx, *opdsec;
- Elf *elf;
- int nr = 0;
- size_t opdidx = 0;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL) {
- pr_debug("%s: cannot read %s ELF file.\n", __func__, name);
- goto out_close;
- }
-
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- pr_debug("%s: cannot get elf header.\n", __func__);
- goto out_elf_end;
- }
-
- if (dso__swap_init(dso, ehdr.e_ident[EI_DATA]))
- goto out_elf_end;
-
- /* Always reject images with a mismatched build-id: */
- if (dso->has_build_id) {
- u8 build_id[BUILD_ID_SIZE];
-
- if (elf_read_build_id(elf, build_id, BUILD_ID_SIZE) < 0)
- goto out_elf_end;
-
- if (!dso__build_id_equal(dso, build_id))
- goto out_elf_end;
- }
-
- sec = elf_section_by_name(elf, &ehdr, &shdr, ".symtab", NULL);
- if (sec == NULL) {
- if (want_symtab)
- goto out_elf_end;
-
- sec = elf_section_by_name(elf, &ehdr, &shdr, ".dynsym", NULL);
- if (sec == NULL)
- goto out_elf_end;
- }
-
- opdsec = elf_section_by_name(elf, &ehdr, &opdshdr, ".opd", &opdidx);
- if (opdshdr.sh_type != SHT_PROGBITS)
- opdsec = NULL;
- if (opdsec)
- opddata = elf_rawdata(opdsec, NULL);
-
- syms = elf_getdata(sec, NULL);
- if (syms == NULL)
- goto out_elf_end;
-
- sec = elf_getscn(elf, shdr.sh_link);
- if (sec == NULL)
- goto out_elf_end;
-
- symstrs = elf_getdata(sec, NULL);
- if (symstrs == NULL)
- goto out_elf_end;
-
- sec_strndx = elf_getscn(elf, ehdr.e_shstrndx);
- if (sec_strndx == NULL)
- goto out_elf_end;
-
- secstrs = elf_getdata(sec_strndx, NULL);
- if (secstrs == NULL)
- goto out_elf_end;
-
- nr_syms = shdr.sh_size / shdr.sh_entsize;
-
- memset(&sym, 0, sizeof(sym));
- if (dso->kernel == DSO_TYPE_USER) {
- dso->adjust_symbols = (ehdr.e_type == ET_EXEC ||
- elf_section_by_name(elf, &ehdr, &shdr,
- ".gnu.prelink_undo",
- NULL) != NULL);
- } else {
- dso->adjust_symbols = 0;
- }
- elf_symtab__for_each_symbol(syms, nr_syms, idx, sym) {
- struct symbol *f;
- const char *elf_name = elf_sym__name(&sym, symstrs);
- char *demangled = NULL;
- int is_label = elf_sym__is_label(&sym);
- const char *section_name;
-
- if (kmap && kmap->ref_reloc_sym && kmap->ref_reloc_sym->name &&
- strcmp(elf_name, kmap->ref_reloc_sym->name) == 0)
- kmap->ref_reloc_sym->unrelocated_addr = sym.st_value;
-
- if (!is_label && !elf_sym__is_a(&sym, map->type))
- continue;
-
- /* Reject ARM ELF "mapping symbols": these aren't unique and
- * don't identify functions, so will confuse the profile
- * output: */
- if (ehdr.e_machine == EM_ARM) {
- if (!strcmp(elf_name, "$a") ||
- !strcmp(elf_name, "$d") ||
- !strcmp(elf_name, "$t"))
- continue;
- }
-
- if (opdsec && sym.st_shndx == opdidx) {
- u32 offset = sym.st_value - opdshdr.sh_addr;
- u64 *opd = opddata->d_buf + offset;
- sym.st_value = DSO__SWAP(dso, u64, *opd);
- sym.st_shndx = elf_addr_to_index(elf, sym.st_value);
- }
-
- sec = elf_getscn(elf, sym.st_shndx);
- if (!sec)
- goto out_elf_end;
-
- gelf_getshdr(sec, &shdr);
-
- if (is_label && !elf_sec__is_a(&shdr, secstrs, map->type))
- continue;
-
- section_name = elf_sec__name(&shdr, secstrs);
-
- /* On ARM, symbols for thumb functions have 1 added to
- * the symbol address as a flag - remove it */
- if ((ehdr.e_machine == EM_ARM) &&
- (map->type == MAP__FUNCTION) &&
- (sym.st_value & 1))
- --sym.st_value;
-
- if (dso->kernel != DSO_TYPE_USER || kmodule) {
- char dso_name[PATH_MAX];
-
- if (strcmp(section_name,
- (curr_dso->short_name +
- dso->short_name_len)) == 0)
- goto new_symbol;
-
- if (strcmp(section_name, ".text") == 0) {
- curr_map = map;
- curr_dso = dso;
- goto new_symbol;
- }
-
- snprintf(dso_name, sizeof(dso_name),
- "%s%s", dso->short_name, section_name);
-
- curr_map = map_groups__find_by_name(kmap->kmaps, map->type, dso_name);
- if (curr_map == NULL) {
- u64 start = sym.st_value;
-
- if (kmodule)
- start += map->start + shdr.sh_offset;
-
- curr_dso = dso__new(dso_name);
- if (curr_dso == NULL)
- goto out_elf_end;
- curr_dso->kernel = dso->kernel;
- curr_dso->long_name = dso->long_name;
- curr_dso->long_name_len = dso->long_name_len;
- curr_map = map__new2(start, curr_dso,
- map->type);
- if (curr_map == NULL) {
- dso__delete(curr_dso);
- goto out_elf_end;
- }
- curr_map->map_ip = identity__map_ip;
- curr_map->unmap_ip = identity__map_ip;
- curr_dso->symtab_type = dso->symtab_type;
- map_groups__insert(kmap->kmaps, curr_map);
- dsos__add(&dso->node, curr_dso);
- dso__set_loaded(curr_dso, map->type);
- } else
- curr_dso = curr_map->dso;
-
- goto new_symbol;
- }
-
- if (curr_dso->adjust_symbols) {
- pr_debug4("%s: adjusting symbol: st_value: %#" PRIx64 " "
- "sh_addr: %#" PRIx64 " sh_offset: %#" PRIx64 "\n", __func__,
- (u64)sym.st_value, (u64)shdr.sh_addr,
- (u64)shdr.sh_offset);
- sym.st_value -= shdr.sh_addr - shdr.sh_offset;
- }
- /*
- * We need to figure out if the object was created from C++ sources
- * DWARF DW_compile_unit has this, but we don't always have access
- * to it...
- */
- demangled = bfd_demangle(NULL, elf_name, DMGL_PARAMS | DMGL_ANSI);
- if (demangled != NULL)
- elf_name = demangled;
-new_symbol:
- f = symbol__new(sym.st_value, sym.st_size,
- GELF_ST_BIND(sym.st_info), elf_name);
- free(demangled);
- if (!f)
- goto out_elf_end;
-
- if (filter && filter(curr_map, f))
- symbol__delete(f);
- else {
- symbols__insert(&curr_dso->symbols[curr_map->type], f);
- nr++;
- }
- }
-
- /*
- * For misannotated, zeroed, ASM function sizes.
- */
- if (nr > 0) {
- symbols__fixup_duplicate(&dso->symbols[map->type]);
- symbols__fixup_end(&dso->symbols[map->type]);
- if (kmap) {
- /*
- * We need to fixup this here too because we create new
- * maps here, for things like vsyscall sections.
- */
- __map_groups__fixup_end(kmap->kmaps, map->type);
- }
- }
- err = nr;
-out_elf_end:
- elf_end(elf);
-out_close:
- return err;
-}
-
-static bool dso__build_id_equal(const struct dso *dso, u8 *build_id)
+bool dso__build_id_equal(const struct dso *dso, u8 *build_id)
{
return memcmp(dso->build_id, build_id, sizeof(dso->build_id)) == 0;
}
@@ -1480,216 +919,11 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
return have_build_id;
}
-/*
- * Align offset to 4 bytes as needed for note name and descriptor data.
- */
-#define NOTE_ALIGN(n) (((n) + 3) & -4U)
-
-static int elf_read_build_id(Elf *elf, void *bf, size_t size)
-{
- int err = -1;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr;
- Elf_Data *data;
- Elf_Scn *sec;
- Elf_Kind ek;
- void *ptr;
-
- if (size < BUILD_ID_SIZE)
- goto out;
-
- ek = elf_kind(elf);
- if (ek != ELF_K_ELF)
- goto out;
-
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- pr_err("%s: cannot get elf header.\n", __func__);
- goto out;
- }
-
- /*
- * Check following sections for notes:
- * '.note.gnu.build-id'
- * '.notes'
- * '.note' (VDSO specific)
- */
- do {
- sec = elf_section_by_name(elf, &ehdr, &shdr,
- ".note.gnu.build-id", NULL);
- if (sec)
- break;
-
- sec = elf_section_by_name(elf, &ehdr, &shdr,
- ".notes", NULL);
- if (sec)
- break;
-
- sec = elf_section_by_name(elf, &ehdr, &shdr,
- ".note", NULL);
- if (sec)
- break;
-
- return err;
-
- } while (0);
-
- data = elf_getdata(sec, NULL);
- if (data == NULL)
- goto out;
-
- ptr = data->d_buf;
- while (ptr < (data->d_buf + data->d_size)) {
- GElf_Nhdr *nhdr = ptr;
- size_t namesz = NOTE_ALIGN(nhdr->n_namesz),
- descsz = NOTE_ALIGN(nhdr->n_descsz);
- const char *name;
-
- ptr += sizeof(*nhdr);
- name = ptr;
- ptr += namesz;
- if (nhdr->n_type == NT_GNU_BUILD_ID &&
- nhdr->n_namesz == sizeof("GNU")) {
- if (memcmp(name, "GNU", sizeof("GNU")) == 0) {
- size_t sz = min(size, descsz);
- memcpy(bf, ptr, sz);
- memset(bf + sz, 0, size - sz);
- err = descsz;
- break;
- }
- }
- ptr += descsz;
- }
-
-out:
- return err;
-}
-
-int filename__read_build_id(const char *filename, void *bf, size_t size)
-{
- int fd, err = -1;
- Elf *elf;
-
- if (size < BUILD_ID_SIZE)
- goto out;
-
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- goto out;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL) {
- pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
- goto out_close;
- }
-
- err = elf_read_build_id(elf, bf, size);
-
- elf_end(elf);
-out_close:
- close(fd);
-out:
- return err;
-}
-
-int sysfs__read_build_id(const char *filename, void *build_id, size_t size)
-{
- int fd, err = -1;
-
- if (size < BUILD_ID_SIZE)
- goto out;
-
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- goto out;
-
- while (1) {
- char bf[BUFSIZ];
- GElf_Nhdr nhdr;
- size_t namesz, descsz;
-
- if (read(fd, &nhdr, sizeof(nhdr)) != sizeof(nhdr))
- break;
-
- namesz = NOTE_ALIGN(nhdr.n_namesz);
- descsz = NOTE_ALIGN(nhdr.n_descsz);
- if (nhdr.n_type == NT_GNU_BUILD_ID &&
- nhdr.n_namesz == sizeof("GNU")) {
- if (read(fd, bf, namesz) != (ssize_t)namesz)
- break;
- if (memcmp(bf, "GNU", sizeof("GNU")) == 0) {
- size_t sz = min(descsz, size);
- if (read(fd, build_id, sz) == (ssize_t)sz) {
- memset(build_id + sz, 0, size - sz);
- err = 0;
- break;
- }
- } else if (read(fd, bf, descsz) != (ssize_t)descsz)
- break;
- } else {
- int n = namesz + descsz;
- if (read(fd, bf, n) != n)
- break;
- }
- }
- close(fd);
-out:
- return err;
-}
-
-static int filename__read_debuglink(const char *filename,
- char *debuglink, size_t size)
-{
- int fd, err = -1;
- Elf *elf;
- GElf_Ehdr ehdr;
- GElf_Shdr shdr;
- Elf_Data *data;
- Elf_Scn *sec;
- Elf_Kind ek;
-
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- goto out;
-
- elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
- if (elf == NULL) {
- pr_debug2("%s: cannot read %s ELF file.\n", __func__, filename);
- goto out_close;
- }
-
- ek = elf_kind(elf);
- if (ek != ELF_K_ELF)
- goto out_close;
-
- if (gelf_getehdr(elf, &ehdr) == NULL) {
- pr_err("%s: cannot get elf header.\n", __func__);
- goto out_close;
- }
-
- sec = elf_section_by_name(elf, &ehdr, &shdr,
- ".gnu_debuglink", NULL);
- if (sec == NULL)
- goto out_close;
-
- data = elf_getdata(sec, NULL);
- if (data == NULL)
- goto out_close;
-
- /* the start of this section is a zero-terminated string */
- strncpy(debuglink, data->d_buf, size);
-
- elf_end(elf);
-
-out_close:
- close(fd);
-out:
- return err;
-}
-
char dso__symtab_origin(const struct dso *dso)
{
static const char origin[] = {
[DSO_BINARY_TYPE__KALLSYMS] = 'k',
+ [DSO_BINARY_TYPE__VMLINUX] = 'v',
[DSO_BINARY_TYPE__JAVA_JIT] = 'j',
[DSO_BINARY_TYPE__DEBUGLINK] = 'l',
[DSO_BINARY_TYPE__BUILD_ID_CACHE] = 'B',
@@ -1700,6 +934,7 @@ char dso__symtab_origin(const struct dso *dso)
[DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE] = 'K',
[DSO_BINARY_TYPE__GUEST_KALLSYMS] = 'g',
[DSO_BINARY_TYPE__GUEST_KMODULE] = 'G',
+ [DSO_BINARY_TYPE__GUEST_VMLINUX] = 'V',
};
if (dso == NULL || dso->symtab_type == DSO_BINARY_TYPE__NOT_FOUND)
@@ -1775,7 +1010,9 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type,
default:
case DSO_BINARY_TYPE__KALLSYMS:
+ case DSO_BINARY_TYPE__VMLINUX:
case DSO_BINARY_TYPE__GUEST_KALLSYMS:
+ case DSO_BINARY_TYPE__GUEST_VMLINUX:
case DSO_BINARY_TYPE__JAVA_JIT:
case DSO_BINARY_TYPE__NOT_FOUND:
ret = -1;
@@ -1789,11 +1026,12 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
{
char *name;
int ret = -1;
- int fd;
u_int i;
struct machine *machine;
char *root_dir = (char *) "";
- int want_symtab;
+ int ss_pos = 0;
+ struct symsrc ss_[2];
+ struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
dso__set_loaded(dso, map->type);
@@ -1835,54 +1073,69 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter)
root_dir = machine->root_dir;
/* Iterate over candidate debug images.
- * On the first pass, only load images if they have a full symtab.
- * Failing that, do a second pass where we accept .dynsym also
+ * Keep track of "interesting" ones (those which have a symtab, dynsym,
+ * and/or opd section) for processing.
*/
- want_symtab = 1;
-restart:
for (i = 0; i < DSO_BINARY_TYPE__SYMTAB_CNT; i++) {
+ struct symsrc *ss = &ss_[ss_pos];
+ bool next_slot = false;
- dso->symtab_type = binary_type_symtab[i];
+ enum dso_binary_type symtab_type = binary_type_symtab[i];
- if (dso__binary_type_file(dso, dso->symtab_type,
+ if (dso__binary_type_file(dso, symtab_type,
root_dir, name, PATH_MAX))
continue;
/* Name is now the name of the next image to try */
- fd = open(name, O_RDONLY);
- if (fd < 0)
+ if (symsrc__init(ss, dso, name, symtab_type) < 0)
continue;
- ret = dso__load_sym(dso, map, name, fd, filter, 0,
- want_symtab);
- close(fd);
+ if (!syms_ss && symsrc__has_symtab(ss)) {
+ syms_ss = ss;
+ next_slot = true;
+ }
- /*
- * Some people seem to have debuginfo files _WITHOUT_ debug
- * info!?!?
- */
- if (!ret)
- continue;
+ if (!runtime_ss && symsrc__possibly_runtime(ss)) {
+ runtime_ss = ss;
+ next_slot = true;
+ }
- if (ret > 0) {
- int nr_plt;
+ if (next_slot) {
+ ss_pos++;
- nr_plt = dso__synthesize_plt_symbols(dso, name, map, filter);
- if (nr_plt > 0)
- ret += nr_plt;
- break;
+ if (syms_ss && runtime_ss)
+ break;
}
+
}
- /*
- * If we wanted a full symtab but no image had one,
- * relax our requirements and repeat the search.
- */
- if (ret <= 0 && want_symtab) {
- want_symtab = 0;
- goto restart;
+ if (!runtime_ss && !syms_ss)
+ goto out_free;
+
+ if (runtime_ss && !syms_ss) {
+ syms_ss = runtime_ss;
+ }
+
+ /* We'll have to hope for the best */
+ if (!runtime_ss && syms_ss)
+ runtime_ss = syms_ss;
+
+ if (syms_ss)
+ ret = dso__load_sym(dso, map, syms_ss, runtime_ss, filter, 0);
+ else
+ ret = -1;
+
+ if (ret > 0) {
+ int nr_plt;
+
+ nr_plt = dso__synthesize_plt_symbols(dso, runtime_ss, map, filter);
+ if (nr_plt > 0)
+ ret += nr_plt;
}
+ for (; ss_pos > 0; ss_pos--)
+ symsrc__destroy(&ss_[ss_pos - 1]);
+out_free:
free(name);
if (ret < 0 && strstr(dso->name, " (deleted)") != NULL)
return 0;
@@ -2030,25 +1283,6 @@ static int machine__set_modules_path(struct machine *machine)
return map_groups__set_modules_path_dir(&machine->kmaps, modules_path);
}
-/*
- * Constructor variant for modules (where we know from /proc/modules where
- * they are loaded) and for vmlinux, where only after we load all the
- * symbols we'll know where it starts and ends.
- */
-static struct map *map__new2(u64 start, struct dso *dso, enum map_type type)
-{
- struct map *map = calloc(1, (sizeof(*map) +
- (dso->kernel ? sizeof(struct kmap) : 0)));
- if (map != NULL) {
- /*
- * ->end will be filled after we load all the symbols
- */
- map__init(map, type, start, 0, 0, dso);
- }
-
- return map;
-}
-
struct map *machine__new_module(struct machine *machine, u64 start,
const char *filename)
{
@@ -2141,22 +1375,30 @@ out_failure:
int dso__load_vmlinux(struct dso *dso, struct map *map,
const char *vmlinux, symbol_filter_t filter)
{
- int err = -1, fd;
+ int err = -1;
+ struct symsrc ss;
char symfs_vmlinux[PATH_MAX];
+ enum dso_binary_type symtab_type;
snprintf(symfs_vmlinux, sizeof(symfs_vmlinux), "%s%s",
symbol_conf.symfs, vmlinux);
- fd = open(symfs_vmlinux, O_RDONLY);
- if (fd < 0)
+
+ if (dso->kernel == DSO_TYPE_GUEST_KERNEL)
+ symtab_type = DSO_BINARY_TYPE__GUEST_VMLINUX;
+ else
+ symtab_type = DSO_BINARY_TYPE__VMLINUX;
+
+ if (symsrc__init(&ss, dso, symfs_vmlinux, symtab_type))
return -1;
- dso__set_long_name(dso, (char *)vmlinux);
- dso__set_loaded(dso, map->type);
- err = dso__load_sym(dso, map, symfs_vmlinux, fd, filter, 0, 0);
- close(fd);
+ err = dso__load_sym(dso, map, &ss, &ss, filter, 0);
+ symsrc__destroy(&ss);
- if (err > 0)
+ if (err > 0) {
+ dso__set_long_name(dso, (char *)vmlinux);
+ dso__set_loaded(dso, map->type);
pr_debug("Using %s for symbols\n", symfs_vmlinux);
+ }
return err;
}
@@ -2173,10 +1415,8 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map,
filename = dso__build_id_filename(dso, NULL, 0);
if (filename != NULL) {
err = dso__load_vmlinux(dso, map, filename, filter);
- if (err > 0) {
- dso__set_long_name(dso, filename);
+ if (err > 0)
goto out;
- }
free(filename);
}
@@ -2291,9 +1531,8 @@ do_kallsyms:
free(kallsyms_allocated_filename);
if (err > 0) {
+ dso__set_long_name(dso, strdup("[kernel.kallsyms]"));
out_fixup:
- if (kallsyms_filename != NULL)
- dso__set_long_name(dso, strdup("[kernel.kallsyms]"));
map__fixup_start(map);
map__fixup_end(map);
}
@@ -2352,12 +1591,12 @@ out_try_fixup:
return err;
}
-static void dsos__add(struct list_head *head, struct dso *dso)
+void dsos__add(struct list_head *head, struct dso *dso)
{
list_add_tail(&dso->node, head);
}
-static struct dso *dsos__find(struct list_head *head, const char *name)
+struct dso *dsos__find(struct list_head *head, const char *name)
{
struct dso *pos;
@@ -2516,7 +1755,7 @@ struct process_args {
};
static int symbol__in_kernel(void *arg, const char *name,
- char type __used, u64 start, u64 end __used)
+ char type __maybe_unused, u64 start)
{
struct process_args *args = arg;
@@ -2752,9 +1991,10 @@ int symbol__init(void)
if (symbol_conf.initialized)
return 0;
- symbol_conf.priv_size = ALIGN(symbol_conf.priv_size, sizeof(u64));
+ symbol_conf.priv_size = PERF_ALIGN(symbol_conf.priv_size, sizeof(u64));
+
+ symbol__elf_init();
- elf_version(EV_CURRENT);
if (symbol_conf.sort_by_name)
symbol_conf.priv_size += (sizeof(struct symbol_name_rb_node) -
sizeof(struct symbol));
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h
index 1fe733a1e21f..b441b07172b7 100644
--- a/tools/perf/util/symbol.h
+++ b/tools/perf/util/symbol.h
@@ -10,22 +10,31 @@
#include <linux/rbtree.h>
#include <stdio.h>
#include <byteswap.h>
+#include <libgen.h>
+
+#ifndef NO_LIBELF_SUPPORT
+#include <libelf.h>
+#include <gelf.h>
+#include <elf.h>
+#endif
#ifdef HAVE_CPLUS_DEMANGLE
extern char *cplus_demangle(const char *, int);
-static inline char *bfd_demangle(void __used *v, const char *c, int i)
+static inline char *bfd_demangle(void __maybe_unused *v, const char *c, int i)
{
return cplus_demangle(c, i);
}
#else
#ifdef NO_DEMANGLE
-static inline char *bfd_demangle(void __used *v, const char __used *c,
- int __used i)
+static inline char *bfd_demangle(void __maybe_unused *v,
+ const char __maybe_unused *c,
+ int __maybe_unused i)
{
return NULL;
}
#else
+#define PACKAGE 'perf'
#include <bfd.h>
#endif
#endif
@@ -158,6 +167,8 @@ struct addr_location {
enum dso_binary_type {
DSO_BINARY_TYPE__KALLSYMS = 0,
DSO_BINARY_TYPE__GUEST_KALLSYMS,
+ DSO_BINARY_TYPE__VMLINUX,
+ DSO_BINARY_TYPE__GUEST_VMLINUX,
DSO_BINARY_TYPE__JAVA_JIT,
DSO_BINARY_TYPE__DEBUGLINK,
DSO_BINARY_TYPE__BUILD_ID_CACHE,
@@ -217,6 +228,36 @@ struct dso {
char name[0];
};
+struct symsrc {
+ char *name;
+ int fd;
+ enum dso_binary_type type;
+
+#ifndef NO_LIBELF_SUPPORT
+ Elf *elf;
+ GElf_Ehdr ehdr;
+
+ Elf_Scn *opdsec;
+ size_t opdidx;
+ GElf_Shdr opdshdr;
+
+ Elf_Scn *symtab;
+ GElf_Shdr symshdr;
+
+ Elf_Scn *dynsym;
+ size_t dynsym_idx;
+ GElf_Shdr dynshdr;
+
+ bool adjust_symbols;
+#endif
+};
+
+void symsrc__destroy(struct symsrc *ss);
+int symsrc__init(struct symsrc *ss, struct dso *dso, const char *name,
+ enum dso_binary_type type);
+bool symsrc__has_symtab(struct symsrc *ss);
+bool symsrc__possibly_runtime(struct symsrc *ss);
+
#define DSO__SWAP(dso, type, val) \
({ \
type ____r = val; \
@@ -254,6 +295,8 @@ static inline void dso__set_loaded(struct dso *dso, enum map_type type)
void dso__sort_by_name(struct dso *dso, enum map_type type);
+void dsos__add(struct list_head *head, struct dso *dso);
+struct dso *dsos__find(struct list_head *head, const char *name);
struct dso *__dsos__findnew(struct list_head *head, const char *name);
int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter);
@@ -283,6 +326,7 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp);
char dso__symtab_origin(const struct dso *dso);
void dso__set_long_name(struct dso *dso, char *name);
void dso__set_build_id(struct dso *dso, void *build_id);
+bool dso__build_id_equal(const struct dso *dso, u8 *build_id);
void dso__read_running_kernel_build_id(struct dso *dso,
struct machine *machine);
struct map *dso__new_map(const char *name);
@@ -297,7 +341,9 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits);
int build_id__sprintf(const u8 *build_id, int len, char *bf);
int kallsyms__parse(const char *filename, void *arg,
int (*process_symbol)(void *arg, const char *name,
- char type, u64 start, u64 end));
+ char type, u64 start));
+int filename__read_debuglink(const char *filename, char *debuglink,
+ size_t size);
void machine__destroy_kernel_maps(struct machine *machine);
int __machine__create_kernel_maps(struct machine *machine, struct dso *kernel);
@@ -309,6 +355,8 @@ void machines__destroy_guest_kernel_maps(struct rb_root *machines);
int symbol__init(void);
void symbol__exit(void);
+void symbol__elf_init(void);
+struct symbol *symbol__new(u64 start, u64 len, u8 binding, const char *name);
size_t symbol__fprintf_symname_offs(const struct symbol *sym,
const struct addr_location *al, FILE *fp);
size_t symbol__fprintf_symname(const struct symbol *sym, FILE *fp);
@@ -326,4 +374,15 @@ ssize_t dso__data_read_addr(struct dso *dso, struct map *map,
struct machine *machine, u64 addr,
u8 *data, ssize_t size);
int dso__test_data(void);
+int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss,
+ struct symsrc *runtime_ss, symbol_filter_t filter,
+ int kmodule);
+int dso__synthesize_plt_symbols(struct dso *dso, struct symsrc *ss,
+ struct map *map, symbol_filter_t filter);
+
+void symbols__insert(struct rb_root *symbols, struct symbol *sym);
+void symbols__fixup_duplicate(struct rb_root *symbols);
+void symbols__fixup_end(struct rb_root *symbols);
+void __map_groups__fixup_end(struct map_groups *mg, enum map_type type);
+
#endif /* __PERF_SYMBOL */
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c
index 051eaa68095e..065528b7563e 100644
--- a/tools/perf/util/target.c
+++ b/tools/perf/util/target.c
@@ -117,8 +117,8 @@ int perf_target__strerror(struct perf_target *target, int errnum,
if (err != buf) {
size_t len = strlen(err);
- char *c = mempcpy(buf, err, min(buflen - 1, len));
- *c = '\0';
+ memcpy(buf, err, min(buflen - 1, len));
+ *(buf + min(buflen - 1, len)) = '\0';
}
return 0;
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 70c2c13ff679..f66610b7bacf 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -16,6 +16,8 @@ struct thread {
bool comm_set;
char *comm;
int comm_len;
+
+ void *priv;
};
struct machine;
diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c
index 7eeebcee291c..884dde9b9bc1 100644
--- a/tools/perf/util/top.c
+++ b/tools/perf/util/top.c
@@ -58,8 +58,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
}
if (top->evlist->nr_entries == 1) {
- struct perf_evsel *first;
- first = list_entry(top->evlist->entries.next, struct perf_evsel, node);
+ struct perf_evsel *first = perf_evlist__first(top->evlist);
ret += SNPRINTF(bf + ret, size - ret, "%" PRIu64 "%s ",
(uint64_t)first->attr.sample_period,
top->freq ? "Hz" : "");
diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h
index 33347ca89ee4..86ff1b15059b 100644
--- a/tools/perf/util/top.h
+++ b/tools/perf/util/top.h
@@ -5,6 +5,7 @@
#include "types.h"
#include <stddef.h>
#include <stdbool.h>
+#include <termios.h>
struct perf_evlist;
struct perf_evsel;
diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c
index 0715c843c2e7..3aabcd687cd5 100644
--- a/tools/perf/util/trace-event-parse.c
+++ b/tools/perf/util/trace-event-parse.c
@@ -162,25 +162,16 @@ int trace_parse_common_pid(struct pevent *pevent, void *data)
return pevent_data_pid(pevent, &record);
}
-unsigned long long read_size(struct pevent *pevent, void *ptr, int size)
+unsigned long long read_size(struct event_format *event, void *ptr, int size)
{
- return pevent_read_number(pevent, ptr, size);
+ return pevent_read_number(event->pevent, ptr, size);
}
-void print_trace_event(struct pevent *pevent, int cpu, void *data, int size)
+void event_format__print(struct event_format *event,
+ int cpu, void *data, int size)
{
- struct event_format *event;
struct pevent_record record;
struct trace_seq s;
- int type;
-
- type = trace_parse_common_type(pevent, data);
-
- event = pevent_find_event(pevent, type);
- if (!event) {
- warning("ug! no event found for type %d", type);
- return;
- }
memset(&record, 0, sizeof(record));
record.cpu = cpu;
@@ -192,6 +183,19 @@ void print_trace_event(struct pevent *pevent, int cpu, void *data, int size)
trace_seq_do_printf(&s);
}
+void print_trace_event(struct pevent *pevent, int cpu, void *data, int size)
+{
+ int type = trace_parse_common_type(pevent, data);
+ struct event_format *event = pevent_find_event(pevent, type);
+
+ if (!event) {
+ warning("ug! no event found for type %d", type);
+ return;
+ }
+
+ event_format__print(event, cpu, data, size);
+}
+
void print_event(struct pevent *pevent, int cpu, void *data, int size,
unsigned long long nsecs, char *comm)
{
@@ -217,7 +221,7 @@ void print_event(struct pevent *pevent, int cpu, void *data, int size,
}
void parse_proc_kallsyms(struct pevent *pevent,
- char *file, unsigned int size __unused)
+ char *file, unsigned int size __maybe_unused)
{
unsigned long long addr;
char *func;
@@ -225,31 +229,29 @@ void parse_proc_kallsyms(struct pevent *pevent,
char *next = NULL;
char *addr_str;
char *mod;
- char ch;
+ char *fmt;
line = strtok_r(file, "\n", &next);
while (line) {
mod = NULL;
- sscanf(line, "%as %c %as\t[%as",
- (float *)(void *)&addr_str, /* workaround gcc warning */
- &ch, (float *)(void *)&func, (float *)(void *)&mod);
+ addr_str = strtok_r(line, " ", &fmt);
addr = strtoull(addr_str, NULL, 16);
- free(addr_str);
-
- /* truncate the extra ']' */
+ /* skip character */
+ strtok_r(NULL, " ", &fmt);
+ func = strtok_r(NULL, "\t", &fmt);
+ mod = strtok_r(NULL, "]", &fmt);
+ /* truncate the extra '[' */
if (mod)
- mod[strlen(mod) - 1] = 0;
+ mod = mod + 1;
pevent_register_function(pevent, func, addr, mod);
- free(func);
- free(mod);
line = strtok_r(NULL, "\n", &next);
}
}
void parse_ftrace_printk(struct pevent *pevent,
- char *file, unsigned int size __unused)
+ char *file, unsigned int size __maybe_unused)
{
unsigned long long addr;
char *printk;
@@ -289,7 +291,7 @@ struct event_format *trace_find_next_event(struct pevent *pevent,
{
static int idx;
- if (!pevent->events)
+ if (!pevent || !pevent->events)
return NULL;
if (!event) {
diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c
index 474aa7a7df43..8715a1006d00 100644
--- a/tools/perf/util/trace-event-scripting.c
+++ b/tools/perf/util/trace-event-scripting.c
@@ -35,12 +35,11 @@ static int stop_script_unsupported(void)
return 0;
}
-static void process_event_unsupported(union perf_event *event __unused,
- struct pevent *pevent __unused,
- struct perf_sample *sample __unused,
- struct perf_evsel *evsel __unused,
- struct machine *machine __unused,
- struct thread *thread __unused)
+static void process_event_unsupported(union perf_event *event __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ struct perf_evsel *evsel __maybe_unused,
+ struct machine *machine __maybe_unused,
+ struct addr_location *al __maybe_unused)
{
}
@@ -53,17 +52,19 @@ static void print_python_unsupported_msg(void)
"\n etc.\n");
}
-static int python_start_script_unsupported(const char *script __unused,
- int argc __unused,
- const char **argv __unused)
+static int python_start_script_unsupported(const char *script __maybe_unused,
+ int argc __maybe_unused,
+ const char **argv __maybe_unused)
{
print_python_unsupported_msg();
return -1;
}
-static int python_generate_script_unsupported(struct pevent *pevent __unused,
- const char *outfile __unused)
+static int python_generate_script_unsupported(struct pevent *pevent
+ __maybe_unused,
+ const char *outfile
+ __maybe_unused)
{
print_python_unsupported_msg();
@@ -115,17 +116,18 @@ static void print_perl_unsupported_msg(void)
"\n etc.\n");
}
-static int perl_start_script_unsupported(const char *script __unused,
- int argc __unused,
- const char **argv __unused)
+static int perl_start_script_unsupported(const char *script __maybe_unused,
+ int argc __maybe_unused,
+ const char **argv __maybe_unused)
{
print_perl_unsupported_msg();
return -1;
}
-static int perl_generate_script_unsupported(struct pevent *pevent __unused,
- const char *outfile __unused)
+static int perl_generate_script_unsupported(struct pevent *pevent
+ __maybe_unused,
+ const char *outfile __maybe_unused)
{
print_perl_unsupported_msg();
diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h
index 8fef1d6687b7..a55fd37ffea1 100644
--- a/tools/perf/util/trace-event.h
+++ b/tools/perf/util/trace-event.h
@@ -9,7 +9,6 @@ struct machine;
struct perf_sample;
union perf_event;
struct perf_tool;
-struct thread;
extern int header_page_size_size;
extern int header_page_ts_size;
@@ -32,6 +31,8 @@ int bigendian(void);
struct pevent *read_trace_init(int file_bigendian, int host_bigendian);
void print_trace_event(struct pevent *pevent, int cpu, void *data, int size);
+void event_format__print(struct event_format *event,
+ int cpu, void *data, int size);
void print_event(struct pevent *pevent, int cpu, void *data, int size,
unsigned long long nsecs, char *comm);
@@ -56,7 +57,7 @@ int trace_parse_common_pid(struct pevent *pevent, void *data);
struct event_format *trace_find_next_event(struct pevent *pevent,
struct event_format *event);
-unsigned long long read_size(struct pevent *pevent, void *ptr, int size);
+unsigned long long read_size(struct event_format *event, void *ptr, int size);
unsigned long long eval_flag(const char *flag);
struct pevent_record *trace_read_data(struct pevent *pevent, int cpu);
@@ -74,16 +75,19 @@ struct tracing_data *tracing_data_get(struct list_head *pattrs,
void tracing_data_put(struct tracing_data *tdata);
+struct addr_location;
+
+struct perf_session;
+
struct scripting_ops {
const char *name;
int (*start_script) (const char *script, int argc, const char **argv);
int (*stop_script) (void);
void (*process_event) (union perf_event *event,
- struct pevent *pevent,
struct perf_sample *sample,
struct perf_evsel *evsel,
struct machine *machine,
- struct thread *thread);
+ struct addr_location *al);
int (*generate_script) (struct pevent *pevent, const char *outfile);
};
diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c
new file mode 100644
index 000000000000..958723ba3d2e
--- /dev/null
+++ b/tools/perf/util/unwind.c
@@ -0,0 +1,571 @@
+/*
+ * Post mortem Dwarf CFI based unwinding on top of regs and stack dumps.
+ *
+ * Lots of this code have been borrowed or heavily inspired from parts of
+ * the libunwind 0.99 code which are (amongst other contributors I may have
+ * forgotten):
+ *
+ * Copyright (C) 2002-2007 Hewlett-Packard Co
+ * Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ * And the bugs have been added by:
+ *
+ * Copyright (C) 2010, Frederic Weisbecker <fweisbec@gmail.com>
+ * Copyright (C) 2012, Jiri Olsa <jolsa@redhat.com>
+ *
+ */
+
+#include <elf.h>
+#include <gelf.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <linux/list.h>
+#include <libunwind.h>
+#include <libunwind-ptrace.h>
+#include "thread.h"
+#include "session.h"
+#include "perf_regs.h"
+#include "unwind.h"
+#include "util.h"
+
+extern int
+UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+
+#define DW_EH_PE_FORMAT_MASK 0x0f /* format of the encoded value */
+#define DW_EH_PE_APPL_MASK 0x70 /* how the value is to be applied */
+
+/* Pointer-encoding formats: */
+#define DW_EH_PE_omit 0xff
+#define DW_EH_PE_ptr 0x00 /* pointer-sized unsigned value */
+#define DW_EH_PE_udata4 0x03 /* unsigned 32-bit value */
+#define DW_EH_PE_udata8 0x04 /* unsigned 64-bit value */
+#define DW_EH_PE_sdata4 0x0b /* signed 32-bit value */
+#define DW_EH_PE_sdata8 0x0c /* signed 64-bit value */
+
+/* Pointer-encoding application: */
+#define DW_EH_PE_absptr 0x00 /* absolute value */
+#define DW_EH_PE_pcrel 0x10 /* rel. to addr. of encoded value */
+
+/*
+ * The following are not documented by LSB v1.3, yet they are used by
+ * GCC, presumably they aren't documented by LSB since they aren't
+ * used on Linux:
+ */
+#define DW_EH_PE_funcrel 0x40 /* start-of-procedure-relative */
+#define DW_EH_PE_aligned 0x50 /* aligned pointer */
+
+/* Flags intentionaly not handled, since they're not needed:
+ * #define DW_EH_PE_indirect 0x80
+ * #define DW_EH_PE_uleb128 0x01
+ * #define DW_EH_PE_udata2 0x02
+ * #define DW_EH_PE_sleb128 0x09
+ * #define DW_EH_PE_sdata2 0x0a
+ * #define DW_EH_PE_textrel 0x20
+ * #define DW_EH_PE_datarel 0x30
+ */
+
+struct unwind_info {
+ struct perf_sample *sample;
+ struct machine *machine;
+ struct thread *thread;
+ u64 sample_uregs;
+};
+
+#define dw_read(ptr, type, end) ({ \
+ type *__p = (type *) ptr; \
+ type __v; \
+ if ((__p + 1) > (type *) end) \
+ return -EINVAL; \
+ __v = *__p++; \
+ ptr = (typeof(ptr)) __p; \
+ __v; \
+ })
+
+static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val,
+ u8 encoding)
+{
+ u8 *cur = *p;
+ *val = 0;
+
+ switch (encoding) {
+ case DW_EH_PE_omit:
+ *val = 0;
+ goto out;
+ case DW_EH_PE_ptr:
+ *val = dw_read(cur, unsigned long, end);
+ goto out;
+ default:
+ break;
+ }
+
+ switch (encoding & DW_EH_PE_APPL_MASK) {
+ case DW_EH_PE_absptr:
+ break;
+ case DW_EH_PE_pcrel:
+ *val = (unsigned long) cur;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if ((encoding & 0x07) == 0x00)
+ encoding |= DW_EH_PE_udata4;
+
+ switch (encoding & DW_EH_PE_FORMAT_MASK) {
+ case DW_EH_PE_sdata4:
+ *val += dw_read(cur, s32, end);
+ break;
+ case DW_EH_PE_udata4:
+ *val += dw_read(cur, u32, end);
+ break;
+ case DW_EH_PE_sdata8:
+ *val += dw_read(cur, s64, end);
+ break;
+ case DW_EH_PE_udata8:
+ *val += dw_read(cur, u64, end);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ out:
+ *p = cur;
+ return 0;
+}
+
+#define dw_read_encoded_value(ptr, end, enc) ({ \
+ u64 __v; \
+ if (__dw_read_encoded_value(&ptr, end, &__v, enc)) { \
+ return -EINVAL; \
+ } \
+ __v; \
+ })
+
+static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep,
+ GElf_Shdr *shp, const char *name)
+{
+ Elf_Scn *sec = NULL;
+
+ while ((sec = elf_nextscn(elf, sec)) != NULL) {
+ char *str;
+
+ gelf_getshdr(sec, shp);
+ str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name);
+ if (!strcmp(name, str))
+ break;
+ }
+
+ return sec;
+}
+
+static u64 elf_section_offset(int fd, const char *name)
+{
+ Elf *elf;
+ GElf_Ehdr ehdr;
+ GElf_Shdr shdr;
+ u64 offset = 0;
+
+ elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL);
+ if (elf == NULL)
+ return 0;
+
+ do {
+ if (gelf_getehdr(elf, &ehdr) == NULL)
+ break;
+
+ if (!elf_section_by_name(elf, &ehdr, &shdr, name))
+ break;
+
+ offset = shdr.sh_offset;
+ } while (0);
+
+ elf_end(elf);
+ return offset;
+}
+
+struct table_entry {
+ u32 start_ip_offset;
+ u32 fde_offset;
+};
+
+struct eh_frame_hdr {
+ unsigned char version;
+ unsigned char eh_frame_ptr_enc;
+ unsigned char fde_count_enc;
+ unsigned char table_enc;
+
+ /*
+ * The rest of the header is variable-length and consists of the
+ * following members:
+ *
+ * encoded_t eh_frame_ptr;
+ * encoded_t fde_count;
+ */
+
+ /* A single encoded pointer should not be more than 8 bytes. */
+ u64 enc[2];
+
+ /*
+ * struct {
+ * encoded_t start_ip;
+ * encoded_t fde_addr;
+ * } binary_search_table[fde_count];
+ */
+ char data[0];
+} __packed;
+
+static int unwind_spec_ehframe(struct dso *dso, struct machine *machine,
+ u64 offset, u64 *table_data, u64 *segbase,
+ u64 *fde_count)
+{
+ struct eh_frame_hdr hdr;
+ u8 *enc = (u8 *) &hdr.enc;
+ u8 *end = (u8 *) &hdr.data;
+ ssize_t r;
+
+ r = dso__data_read_offset(dso, machine, offset,
+ (u8 *) &hdr, sizeof(hdr));
+ if (r != sizeof(hdr))
+ return -EINVAL;
+
+ /* We dont need eh_frame_ptr, just skip it. */
+ dw_read_encoded_value(enc, end, hdr.eh_frame_ptr_enc);
+
+ *fde_count = dw_read_encoded_value(enc, end, hdr.fde_count_enc);
+ *segbase = offset;
+ *table_data = (enc - (u8 *) &hdr) + offset;
+ return 0;
+}
+
+static int read_unwind_spec(struct dso *dso, struct machine *machine,
+ u64 *table_data, u64 *segbase, u64 *fde_count)
+{
+ int ret = -EINVAL, fd;
+ u64 offset;
+
+ fd = dso__data_fd(dso, machine);
+ if (fd < 0)
+ return -EINVAL;
+
+ offset = elf_section_offset(fd, ".eh_frame_hdr");
+ close(fd);
+
+ if (offset)
+ ret = unwind_spec_ehframe(dso, machine, offset,
+ table_data, segbase,
+ fde_count);
+
+ /* TODO .debug_frame check if eh_frame_hdr fails */
+ return ret;
+}
+
+static struct map *find_map(unw_word_t ip, struct unwind_info *ui)
+{
+ struct addr_location al;
+
+ thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER,
+ MAP__FUNCTION, ip, &al);
+ return al.map;
+}
+
+static int
+find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ struct unwind_info *ui = arg;
+ struct map *map;
+ unw_dyn_info_t di;
+ u64 table_data, segbase, fde_count;
+
+ map = find_map(ip, ui);
+ if (!map || !map->dso)
+ return -EINVAL;
+
+ pr_debug("unwind: find_proc_info dso %s\n", map->dso->name);
+
+ if (read_unwind_spec(map->dso, ui->machine,
+ &table_data, &segbase, &fde_count))
+ return -EINVAL;
+
+ memset(&di, 0, sizeof(di));
+ di.format = UNW_INFO_FORMAT_REMOTE_TABLE;
+ di.start_ip = map->start;
+ di.end_ip = map->end;
+ di.u.rti.segbase = map->start + segbase;
+ di.u.rti.table_data = map->start + table_data;
+ di.u.rti.table_len = fde_count * sizeof(struct table_entry)
+ / sizeof(unw_word_t);
+ return dwarf_search_unwind_table(as, ip, &di, pi,
+ need_unwind_info, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int
+get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int access_dso_mem(struct unwind_info *ui, unw_word_t addr,
+ unw_word_t *data)
+{
+ struct addr_location al;
+ ssize_t size;
+
+ thread__find_addr_map(ui->thread, ui->machine, PERF_RECORD_MISC_USER,
+ MAP__FUNCTION, addr, &al);
+ if (!al.map) {
+ pr_debug("unwind: no map for %lx\n", (unsigned long)addr);
+ return -1;
+ }
+
+ if (!al.map->dso)
+ return -1;
+
+ size = dso__data_read_addr(al.map->dso, al.map, ui->machine,
+ addr, (u8 *) data, sizeof(*data));
+
+ return !(size == sizeof(*data));
+}
+
+static int reg_value(unw_word_t *valp, struct regs_dump *regs, int id,
+ u64 sample_regs)
+{
+ int i, idx = 0;
+
+ if (!(sample_regs & (1 << id)))
+ return -EINVAL;
+
+ for (i = 0; i < id; i++) {
+ if (sample_regs & (1 << i))
+ idx++;
+ }
+
+ *valp = regs->regs[idx];
+ return 0;
+}
+
+static int access_mem(unw_addr_space_t __maybe_unused as,
+ unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ struct unwind_info *ui = arg;
+ struct stack_dump *stack = &ui->sample->user_stack;
+ unw_word_t start, end;
+ int offset;
+ int ret;
+
+ /* Don't support write, probably not needed. */
+ if (__write || !stack || !ui->sample->user_regs.regs) {
+ *valp = 0;
+ return 0;
+ }
+
+ ret = reg_value(&start, &ui->sample->user_regs, PERF_REG_SP,
+ ui->sample_uregs);
+ if (ret)
+ return ret;
+
+ end = start + stack->size;
+
+ /* Check overflow. */
+ if (addr + sizeof(unw_word_t) < addr)
+ return -EINVAL;
+
+ if (addr < start || addr + sizeof(unw_word_t) >= end) {
+ ret = access_dso_mem(ui, addr, valp);
+ if (ret) {
+ pr_debug("unwind: access_mem %p not inside range %p-%p\n",
+ (void *)addr, (void *)start, (void *)end);
+ *valp = 0;
+ return ret;
+ }
+ return 0;
+ }
+
+ offset = addr - start;
+ *valp = *(unw_word_t *)&stack->data[offset];
+ pr_debug("unwind: access_mem addr %p, val %lx, offset %d\n",
+ (void *)addr, (unsigned long)*valp, offset);
+ return 0;
+}
+
+static int access_reg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ struct unwind_info *ui = arg;
+ int id, ret;
+
+ /* Don't support write, I suspect we don't need it. */
+ if (__write) {
+ pr_err("unwind: access_reg w %d\n", regnum);
+ return 0;
+ }
+
+ if (!ui->sample->user_regs.regs) {
+ *valp = 0;
+ return 0;
+ }
+
+ id = unwind__arch_reg_id(regnum);
+ if (id < 0)
+ return -EINVAL;
+
+ ret = reg_value(valp, &ui->sample->user_regs, id, ui->sample_uregs);
+ if (ret) {
+ pr_err("unwind: can't read reg %d\n", regnum);
+ return ret;
+ }
+
+ pr_debug("unwind: reg %d, val %lx\n", regnum, (unsigned long)*valp);
+ return 0;
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int entry(u64 ip, struct thread *thread, struct machine *machine,
+ unwind_entry_cb_t cb, void *arg)
+{
+ struct unwind_entry e;
+ struct addr_location al;
+
+ thread__find_addr_location(thread, machine,
+ PERF_RECORD_MISC_USER,
+ MAP__FUNCTION, ip, &al, NULL);
+
+ e.ip = ip;
+ e.map = al.map;
+ e.sym = al.sym;
+
+ pr_debug("unwind: %s:ip = 0x%" PRIx64 " (0x%" PRIx64 ")\n",
+ al.sym ? al.sym->name : "''",
+ ip,
+ al.map ? al.map->map_ip(al.map, ip) : (u64) 0);
+
+ return cb(&e, arg);
+}
+
+static void display_error(int err)
+{
+ switch (err) {
+ case UNW_EINVAL:
+ pr_err("unwind: Only supports local.\n");
+ break;
+ case UNW_EUNSPEC:
+ pr_err("unwind: Unspecified error.\n");
+ break;
+ case UNW_EBADREG:
+ pr_err("unwind: Register unavailable.\n");
+ break;
+ default:
+ break;
+ }
+}
+
+static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+};
+
+static int get_entries(struct unwind_info *ui, unwind_entry_cb_t cb,
+ void *arg)
+{
+ unw_addr_space_t addr_space;
+ unw_cursor_t c;
+ int ret;
+
+ addr_space = unw_create_addr_space(&accessors, 0);
+ if (!addr_space) {
+ pr_err("unwind: Can't create unwind address space.\n");
+ return -ENOMEM;
+ }
+
+ ret = unw_init_remote(&c, addr_space, ui);
+ if (ret)
+ display_error(ret);
+
+ while (!ret && (unw_step(&c) > 0)) {
+ unw_word_t ip;
+
+ unw_get_reg(&c, UNW_REG_IP, &ip);
+ ret = entry(ip, ui->thread, ui->machine, cb, arg);
+ }
+
+ unw_destroy_addr_space(addr_space);
+ return ret;
+}
+
+int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
+ struct machine *machine, struct thread *thread,
+ u64 sample_uregs, struct perf_sample *data)
+{
+ unw_word_t ip;
+ struct unwind_info ui = {
+ .sample = data,
+ .sample_uregs = sample_uregs,
+ .thread = thread,
+ .machine = machine,
+ };
+ int ret;
+
+ if (!data->user_regs.regs)
+ return -EINVAL;
+
+ ret = reg_value(&ip, &data->user_regs, PERF_REG_IP, sample_uregs);
+ if (ret)
+ return ret;
+
+ ret = entry(ip, thread, machine, cb, arg);
+ if (ret)
+ return -ENOMEM;
+
+ return get_entries(&ui, cb, arg);
+}
diff --git a/tools/perf/util/unwind.h b/tools/perf/util/unwind.h
new file mode 100644
index 000000000000..a78c8b303bb5
--- /dev/null
+++ b/tools/perf/util/unwind.h
@@ -0,0 +1,35 @@
+#ifndef __UNWIND_H
+#define __UNWIND_H
+
+#include "types.h"
+#include "event.h"
+#include "symbol.h"
+
+struct unwind_entry {
+ struct map *map;
+ struct symbol *sym;
+ u64 ip;
+};
+
+typedef int (*unwind_entry_cb_t)(struct unwind_entry *entry, void *arg);
+
+#ifndef NO_LIBUNWIND_SUPPORT
+int unwind__get_entries(unwind_entry_cb_t cb, void *arg,
+ struct machine *machine,
+ struct thread *thread,
+ u64 sample_uregs,
+ struct perf_sample *data);
+int unwind__arch_reg_id(int regnum);
+#else
+static inline int
+unwind__get_entries(unwind_entry_cb_t cb __maybe_unused,
+ void *arg __maybe_unused,
+ struct machine *machine __maybe_unused,
+ struct thread *thread __maybe_unused,
+ u64 sample_uregs __maybe_unused,
+ struct perf_sample *data __maybe_unused)
+{
+ return 0;
+}
+#endif /* NO_LIBUNWIND_SUPPORT */
+#endif /* __UNWIND_H */
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index d03599fbe78b..2055cf38041c 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -1,6 +1,11 @@
#include "../perf.h"
#include "util.h"
#include <sys/mman.h>
+#ifndef NO_BACKTRACE
+#include <execinfo.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
/*
* XXX We need to find a better place for these things...
@@ -158,3 +163,23 @@ size_t hex_width(u64 v)
return n;
}
+
+/* Obtain a backtrace and print it to stdout. */
+#ifndef NO_BACKTRACE
+void dump_stack(void)
+{
+ void *array[16];
+ size_t size = backtrace(array, ARRAY_SIZE(array));
+ char **strings = backtrace_symbols(array, size);
+ size_t i;
+
+ printf("Obtained %zd stack frames.\n", size);
+
+ for (i = 0; i < size; i++)
+ printf("%s\n", strings[i]);
+
+ free(strings);
+}
+#else
+void dump_stack(void) {}
+#endif
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index b13c7331eaf8..70fa70b535b2 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -69,13 +69,8 @@
#include <sys/poll.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
-#include <sys/select.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <netdb.h>
#include <inttypes.h>
-#include "../../../include/linux/magic.h"
+#include <linux/magic.h>
#include "types.h"
#include <sys/ttydefaults.h>
@@ -266,4 +261,6 @@ size_t hex_width(u64 v);
char *rtrim(char *s);
+void dump_stack(void);
+
#endif
diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c
new file mode 100644
index 000000000000..e60951fcdb12
--- /dev/null
+++ b/tools/perf/util/vdso.c
@@ -0,0 +1,111 @@
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <linux/kernel.h>
+
+#include "vdso.h"
+#include "util.h"
+#include "symbol.h"
+#include "linux/string.h"
+
+static bool vdso_found;
+static char vdso_file[] = "/tmp/perf-vdso.so-XXXXXX";
+
+static int find_vdso_map(void **start, void **end)
+{
+ FILE *maps;
+ char line[128];
+ int found = 0;
+
+ maps = fopen("/proc/self/maps", "r");
+ if (!maps) {
+ pr_err("vdso: cannot open maps\n");
+ return -1;
+ }
+
+ while (!found && fgets(line, sizeof(line), maps)) {
+ int m = -1;
+
+ /* We care only about private r-x mappings. */
+ if (2 != sscanf(line, "%p-%p r-xp %*x %*x:%*x %*u %n",
+ start, end, &m))
+ continue;
+ if (m < 0)
+ continue;
+
+ if (!strncmp(&line[m], VDSO__MAP_NAME,
+ sizeof(VDSO__MAP_NAME) - 1))
+ found = 1;
+ }
+
+ fclose(maps);
+ return !found;
+}
+
+static char *get_file(void)
+{
+ char *vdso = NULL;
+ char *buf = NULL;
+ void *start, *end;
+ size_t size;
+ int fd;
+
+ if (vdso_found)
+ return vdso_file;
+
+ if (find_vdso_map(&start, &end))
+ return NULL;
+
+ size = end - start;
+
+ buf = memdup(start, size);
+ if (!buf)
+ return NULL;
+
+ fd = mkstemp(vdso_file);
+ if (fd < 0)
+ goto out;
+
+ if (size == (size_t) write(fd, buf, size))
+ vdso = vdso_file;
+
+ close(fd);
+
+ out:
+ free(buf);
+
+ vdso_found = (vdso != NULL);
+ return vdso;
+}
+
+void vdso__exit(void)
+{
+ if (vdso_found)
+ unlink(vdso_file);
+}
+
+struct dso *vdso__dso_findnew(struct list_head *head)
+{
+ struct dso *dso = dsos__find(head, VDSO__MAP_NAME);
+
+ if (!dso) {
+ char *file;
+
+ file = get_file();
+ if (!file)
+ return NULL;
+
+ dso = dso__new(VDSO__MAP_NAME);
+ if (dso != NULL) {
+ dsos__add(head, dso);
+ dso__set_long_name(dso, file);
+ }
+ }
+
+ return dso;
+}
diff --git a/tools/perf/util/vdso.h b/tools/perf/util/vdso.h
new file mode 100644
index 000000000000..0f76e7caf6f8
--- /dev/null
+++ b/tools/perf/util/vdso.h
@@ -0,0 +1,18 @@
+#ifndef __PERF_VDSO__
+#define __PERF_VDSO__
+
+#include <linux/types.h>
+#include <string.h>
+#include <stdbool.h>
+
+#define VDSO__MAP_NAME "[vdso]"
+
+static inline bool is_vdso_map(const char *filename)
+{
+ return !strcmp(filename, VDSO__MAP_NAME);
+}
+
+struct dso *vdso__dso_findnew(struct list_head *head);
+void vdso__exit(void);
+
+#endif /* __PERF_VDSO__ */
diff --git a/tools/perf/util/wrapper.c b/tools/perf/util/wrapper.c
index 73e900edb5a2..19f15b650703 100644
--- a/tools/perf/util/wrapper.c
+++ b/tools/perf/util/wrapper.c
@@ -7,7 +7,8 @@
* There's no pack memory to release - but stay close to the Git
* version so wrap this away:
*/
-static inline void release_pack_memory(size_t size __used, int flag __used)
+static inline void release_pack_memory(size_t size __maybe_unused,
+ int flag __maybe_unused)
{
}
diff --git a/tools/scripts/Makefile.include b/tools/scripts/Makefile.include
index bde8521d56bb..96ce80a3743b 100644
--- a/tools/scripts/Makefile.include
+++ b/tools/scripts/Makefile.include
@@ -1,6 +1,8 @@
ifeq ("$(origin O)", "command line")
- OUTPUT := $(O)/
- COMMAND_O := O=$(O)
+ dummy := $(if $(shell test -d $(O) || echo $(O)),$(error O=$(O) does not exist),)
+ ABSOLUTE_O := $(shell cd $(O) ; pwd)
+ OUTPUT := $(ABSOLUTE_O)/
+ COMMAND_O := O=$(ABSOLUTE_O)
endif
ifneq ($(OUTPUT),)
diff --git a/tools/testing/ktest/examples/include/defaults.conf b/tools/testing/ktest/examples/include/defaults.conf
index 323a552ce642..63a1a83f4f0b 100644
--- a/tools/testing/ktest/examples/include/defaults.conf
+++ b/tools/testing/ktest/examples/include/defaults.conf
@@ -33,7 +33,7 @@ DEFAULTS
THIS_DIR := ${PWD}
-# to orginize your configs, having each machine save their configs
+# to organize your configs, having each machine save their configs
# into a separate directly is useful.
CONFIG_DIR := ${THIS_DIR}/configs/${MACHINE}
diff --git a/tools/testing/ktest/examples/include/tests.conf b/tools/testing/ktest/examples/include/tests.conf
index 4fdb811bd810..60cedb1a1154 100644
--- a/tools/testing/ktest/examples/include/tests.conf
+++ b/tools/testing/ktest/examples/include/tests.conf
@@ -47,7 +47,7 @@ BUILD_NOCLEAN = 1
# Build, install, boot and test with a randconfg 10 times.
# It is important that you have set MIN_CONFIG in the config
# that includes this file otherwise it is likely that the
-# randconfig will not have the neccessary configs needed to
+# randconfig will not have the necessary configs needed to
# boot your box. This version of the test requires a min
# config that has enough to make sure the target has network
# working.
diff --git a/tools/testing/ktest/ktest.pl b/tools/testing/ktest/ktest.pl
index 52b7959cd513..c05bcd293d8c 100755
--- a/tools/testing/ktest/ktest.pl
+++ b/tools/testing/ktest/ktest.pl
@@ -840,7 +840,9 @@ sub __read_config {
if ($rest =~ /\sIF\s+(.*)/) {
# May be a ELSE IF section.
- if (!process_if($name, $1)) {
+ if (process_if($name, $1)) {
+ $if_set = 1;
+ } else {
$skip = 1;
}
$rest = "";
diff --git a/tools/testing/selftests/vm/run_vmtests b/tools/testing/selftests/vm/run_vmtests
index 8b40bd5e5cc2..4c53cae6c273 100644
--- a/tools/testing/selftests/vm/run_vmtests
+++ b/tools/testing/selftests/vm/run_vmtests
@@ -36,7 +36,7 @@ mkdir $mnt
mount -t hugetlbfs none $mnt
echo "--------------------"
-echo "runing hugepage-mmap"
+echo "running hugepage-mmap"
echo "--------------------"
./hugepage-mmap
if [ $? -ne 0 ]; then
@@ -50,7 +50,7 @@ shmall=`cat /proc/sys/kernel/shmall`
echo 268435456 > /proc/sys/kernel/shmmax
echo 4194304 > /proc/sys/kernel/shmall
echo "--------------------"
-echo "runing hugepage-shm"
+echo "running hugepage-shm"
echo "--------------------"
./hugepage-shm
if [ $? -ne 0 ]; then
@@ -62,7 +62,7 @@ echo $shmmax > /proc/sys/kernel/shmmax
echo $shmall > /proc/sys/kernel/shmall
echo "--------------------"
-echo "runing map_hugetlb"
+echo "running map_hugetlb"
echo "--------------------"
./map_hugetlb
if [ $? -ne 0 ]; then
diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
index b0adb2710c02..68d0734b2081 100644
--- a/tools/usb/testusb.c
+++ b/tools/usb/testusb.c
@@ -253,9 +253,6 @@ static int find_testdev(const char *name, const struct stat *sb, int flag)
if (flag != FTW_F)
return 0;
- /* ignore /proc/bus/usb/{devices,drivers} */
- if (strrchr(name, '/')[1] == 'd')
- return 0;
fd = fopen(name, "rb");
if (!fd) {
@@ -356,28 +353,8 @@ restart:
static const char *usbfs_dir_find(void)
{
- static char usbfs_path_0[] = "/dev/usb/devices";
- static char usbfs_path_1[] = "/proc/bus/usb/devices";
static char udev_usb_path[] = "/dev/bus/usb";
- static char *const usbfs_paths[] = {
- usbfs_path_0, usbfs_path_1
- };
-
- static char *const *
- end = usbfs_paths + sizeof usbfs_paths / sizeof *usbfs_paths;
-
- char *const *it = usbfs_paths;
- do {
- int fd = open(*it, O_RDONLY);
- close(fd);
- if (fd >= 0) {
- strrchr(*it, '/')[0] = '\0';
- return *it;
- }
- } while (++it != end);
-
- /* real device-nodes managed by udev */
if (access(udev_usb_path, F_OK) == 0)
return udev_usb_path;
@@ -489,7 +466,7 @@ usage:
goto usage;
if (!all && !device) {
fprintf (stderr, "must specify '-a' or '-D dev', "
- "or DEVICE=/proc/bus/usb/BBB/DDD in env\n");
+ "or DEVICE=/dev/bus/usb/BBB/DDD in env\n");
goto usage;
}